summaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend')
-rw-r--r--backend/Makefile.am1126
-rw-r--r--backend/Makefile.in5420
-rw-r--r--backend/abaton.c1491
-rw-r--r--backend/abaton.conf.in2
-rw-r--r--backend/abaton.h133
-rw-r--r--backend/agfafocus.c2083
-rw-r--r--backend/agfafocus.conf.in2
-rw-r--r--backend/agfafocus.h132
-rw-r--r--backend/apple.c2690
-rw-r--r--backend/apple.conf.in2
-rw-r--r--backend/apple.h270
-rw-r--r--backend/artec.c3754
-rw-r--r--backend/artec.conf.in3
-rw-r--r--backend/artec.h272
-rw-r--r--backend/artec_eplus48u.c4568
-rw-r--r--backend/artec_eplus48u.conf.in119
-rw-r--r--backend/artec_eplus48u.h596
-rw-r--r--backend/as6e.c942
-rw-r--r--backend/as6e.h124
-rw-r--r--backend/avision.c8626
-rw-r--r--backend/avision.conf.in23
-rw-r--r--backend/avision.h803
-rw-r--r--backend/bh.c3883
-rw-r--r--backend/bh.conf.in2
-rw-r--r--backend/bh.h1036
-rw-r--r--backend/canon-sane.c2229
-rw-r--r--backend/canon-scsi.c733
-rw-r--r--backend/canon.c1881
-rw-r--r--backend/canon.conf.in2
-rw-r--r--backend/canon.h408
-rw-r--r--backend/canon630u-common.c1656
-rw-r--r--backend/canon630u.c1044
-rw-r--r--backend/canon630u.conf.in8
-rw-r--r--backend/canon_dr-cmd.h545
-rw-r--r--backend/canon_dr.c8133
-rw-r--r--backend/canon_dr.conf.in146
-rw-r--r--backend/canon_dr.h587
-rw-r--r--backend/canon_pp-dev.c1368
-rw-r--r--backend/canon_pp-dev.h185
-rw-r--r--backend/canon_pp-io.c618
-rw-r--r--backend/canon_pp-io.h65
-rw-r--r--backend/canon_pp.c2039
-rw-r--r--backend/canon_pp.conf.in36
-rw-r--r--backend/canon_pp.h125
-rw-r--r--backend/cardscan.c1686
-rw-r--r--backend/cardscan.conf.in17
-rw-r--r--backend/cardscan.h197
-rw-r--r--backend/coolscan-scsidef.h839
-rw-r--r--backend/coolscan.c4195
-rw-r--r--backend/coolscan.conf.in2
-rw-r--r--backend/coolscan.h324
-rw-r--r--backend/coolscan2.c3092
-rw-r--r--backend/coolscan2.conf.in20
-rw-r--r--backend/coolscan3.c3188
-rw-r--r--backend/coolscan3.conf.in20
-rw-r--r--backend/dc210.c1531
-rw-r--r--backend/dc210.conf.in29
-rw-r--r--backend/dc210.h279
-rw-r--r--backend/dc240.c2150
-rw-r--r--backend/dc240.conf.in29
-rw-r--r--backend/dc240.h278
-rw-r--r--backend/dc25.c2760
-rw-r--r--backend/dc25.conf.in23
-rw-r--r--backend/dc25.h276
-rw-r--r--backend/dell1600n_net.c2065
-rw-r--r--backend/dell1600n_net.conf.in14
-rw-r--r--backend/dll.aliases6
-rw-r--r--backend/dll.c1305
-rw-r--r--backend/dll.conf.in87
-rw-r--r--backend/dmc.c1404
-rw-r--r--backend/dmc.conf.in1
-rw-r--r--backend/dmc.h125
-rw-r--r--backend/epjitsu-cmd.h459
-rw-r--r--backend/epjitsu.c4330
-rw-r--r--backend/epjitsu.conf.in37
-rw-r--r--backend/epjitsu.h384
-rw-r--r--backend/epson.c6403
-rw-r--r--backend/epson.conf.in27
-rw-r--r--backend/epson.h262
-rw-r--r--backend/epson2-cct.c613
-rw-r--r--backend/epson2-commands.c1088
-rw-r--r--backend/epson2-commands.h63
-rw-r--r--backend/epson2-io.c392
-rw-r--r--backend/epson2-io.h50
-rw-r--r--backend/epson2-ops.c2211
-rw-r--r--backend/epson2-ops.h52
-rw-r--r--backend/epson2.c2351
-rw-r--r--backend/epson2.conf.in27
-rw-r--r--backend/epson2.h425
-rw-r--r--backend/epson2_net.c229
-rw-r--r--backend/epson2_net.h18
-rw-r--r--backend/epson2_scsi.c103
-rw-r--r--backend/epson2_scsi.h24
-rw-r--r--backend/epson_scsi.c114
-rw-r--r--backend/epson_scsi.h24
-rw-r--r--backend/epson_usb.c93
-rw-r--r--backend/epson_usb.h10
-rw-r--r--backend/fujitsu-scsi.h1132
-rw-r--r--backend/fujitsu.c9757
-rw-r--r--backend/fujitsu.conf.in149
-rw-r--r--backend/fujitsu.h837
-rw-r--r--backend/genesys.c7897
-rw-r--r--backend/genesys.conf.in125
-rw-r--r--backend/genesys.h149
-rw-r--r--backend/genesys_conv.c478
-rw-r--r--backend/genesys_conv_hlp.c345
-rw-r--r--backend/genesys_devices.c3415
-rw-r--r--backend/genesys_gl124.c3891
-rw-r--r--backend/genesys_gl124.h711
-rw-r--r--backend/genesys_gl646.c5811
-rw-r--r--backend/genesys_gl646.h680
-rw-r--r--backend/genesys_gl841.c5733
-rw-r--r--backend/genesys_gl841.h390
-rw-r--r--backend/genesys_gl843.c4459
-rw-r--r--backend/genesys_gl843.h700
-rw-r--r--backend/genesys_gl846.c3704
-rw-r--r--backend/genesys_gl846.h705
-rw-r--r--backend/genesys_gl847.c3765
-rw-r--r--backend/genesys_gl847.h695
-rw-r--r--backend/genesys_low.c1981
-rw-r--r--backend/genesys_low.h1206
-rw-r--r--backend/gphoto2.c1995
-rw-r--r--backend/gphoto2.conf.in32
-rw-r--r--backend/gphoto2.h200
-rw-r--r--backend/gt68xx.c2389
-rw-r--r--backend/gt68xx.conf.in255
-rw-r--r--backend/gt68xx.h56
-rw-r--r--backend/gt68xx_devices.c1947
-rw-r--r--backend/gt68xx_generic.c679
-rw-r--r--backend/gt68xx_generic.h75
-rw-r--r--backend/gt68xx_gt6801.c278
-rw-r--r--backend/gt68xx_gt6801.h71
-rw-r--r--backend/gt68xx_gt6816.c263
-rw-r--r--backend/gt68xx_gt6816.h72
-rw-r--r--backend/gt68xx_high.c2744
-rw-r--r--backend/gt68xx_high.h374
-rw-r--r--backend/gt68xx_low.c1034
-rw-r--r--backend/gt68xx_low.h1099
-rw-r--r--backend/gt68xx_mid.c1185
-rw-r--r--backend/gt68xx_mid.h154
-rw-r--r--backend/gt68xx_shm_channel.c673
-rw-r--r--backend/gt68xx_shm_channel.h106
-rw-r--r--backend/hp-accessor.c902
-rw-r--r--backend/hp-accessor.h88
-rw-r--r--backend/hp-device.c476
-rw-r--r--backend/hp-device.h94
-rw-r--r--backend/hp-handle.c790
-rw-r--r--backend/hp-handle.h65
-rw-r--r--backend/hp-hpmem.c151
-rw-r--r--backend/hp-option.c4087
-rw-r--r--backend/hp-option.h294
-rw-r--r--backend/hp-scl.c2118
-rw-r--r--backend/hp-scl.h166
-rw-r--r--backend/hp-scsi.h78
-rw-r--r--backend/hp.README70
-rw-r--r--backend/hp.TODO52
-rw-r--r--backend/hp.c1013
-rw-r--r--backend/hp.conf.in21
-rw-r--r--backend/hp.h226
-rw-r--r--backend/hp3500.c3506
-rw-r--r--backend/hp3900.c61
-rw-r--r--backend/hp3900.conf.in31
-rw-r--r--backend/hp3900_config.c6055
-rw-r--r--backend/hp3900_debug.c1581
-rw-r--r--backend/hp3900_rts8822.c15123
-rw-r--r--backend/hp3900_sane.c2734
-rw-r--r--backend/hp3900_types.c783
-rw-r--r--backend/hp3900_usb.c509
-rw-r--r--backend/hp4200.c2979
-rw-r--r--backend/hp4200.conf.in6
-rw-r--r--backend/hp4200.h266
-rw-r--r--backend/hp4200_lm9830.c219
-rw-r--r--backend/hp4200_lm9830.h184
-rw-r--r--backend/hp5400.c82
-rw-r--r--backend/hp5400.conf.in14
-rw-r--r--backend/hp5400.h143
-rw-r--r--backend/hp5400_debug.c73
-rw-r--r--backend/hp5400_debug.h81
-rw-r--r--backend/hp5400_internal.c1455
-rw-r--r--backend/hp5400_internal.h276
-rw-r--r--backend/hp5400_sane.c1022
-rw-r--r--backend/hp5400_sanei.c387
-rw-r--r--backend/hp5400_sanei.h108
-rw-r--r--backend/hp5400_xfer.h77
-rw-r--r--backend/hp5590.c1381
-rw-r--r--backend/hp5590_cmds.c2274
-rw-r--r--backend/hp5590_cmds.h202
-rw-r--r--backend/hp5590_low.c965
-rw-r--r--backend/hp5590_low.h89
-rw-r--r--backend/hpljm1005.c1104
-rw-r--r--backend/hpsj5s.c1568
-rw-r--r--backend/hpsj5s.conf.in2
-rw-r--r--backend/hpsj5s.h102
-rw-r--r--backend/hs2p-saneopts.h365
-rw-r--r--backend/hs2p-scsi.c2198
-rw-r--r--backend/hs2p-scsi.h1290
-rw-r--r--backend/hs2p.c3332
-rw-r--r--backend/hs2p.conf.in2
-rw-r--r--backend/hs2p.h516
-rw-r--r--backend/ibm-scsi.c439
-rw-r--r--backend/ibm.c1183
-rw-r--r--backend/ibm.conf.in3
-rw-r--r--backend/ibm.h386
-rw-r--r--backend/kodak-cmd.h645
-rw-r--r--backend/kodak.c2913
-rw-r--r--backend/kodak.conf.in14
-rw-r--r--backend/kodak.h272
-rw-r--r--backend/kodakaio.c3346
-rw-r--r--backend/kodakaio.conf.in80
-rw-r--r--backend/kodakaio.h198
-rw-r--r--backend/kvs1025.c456
-rw-r--r--backend/kvs1025.h127
-rw-r--r--backend/kvs1025_cmds.h74
-rw-r--r--backend/kvs1025_low.c1178
-rw-r--r--backend/kvs1025_low.h290
-rw-r--r--backend/kvs1025_opt.c1581
-rw-r--r--backend/kvs1025_usb.c370
-rw-r--r--backend/kvs1025_usb.h28
-rw-r--r--backend/kvs20xx.c535
-rw-r--r--backend/kvs20xx.h211
-rw-r--r--backend/kvs20xx_cmd.c379
-rw-r--r--backend/kvs20xx_cmd.h128
-rw-r--r--backend/kvs20xx_opt.c801
-rw-r--r--backend/kvs40xx.c748
-rw-r--r--backend/kvs40xx.h275
-rw-r--r--backend/kvs40xx_cmd.c601
-rw-r--r--backend/kvs40xx_opt.c1414
-rw-r--r--backend/leo.c2014
-rw-r--r--backend/leo.conf.in8
-rw-r--r--backend/leo.h554
-rw-r--r--backend/lexmark.c1348
-rw-r--r--backend/lexmark.conf.in8
-rw-r--r--backend/lexmark.h281
-rw-r--r--backend/lexmark_low.c6106
-rw-r--r--backend/lexmark_models.c116
-rw-r--r--backend/lexmark_sensors.c134
-rw-r--r--backend/lm9830.h58
-rw-r--r--backend/ma1509.c2020
-rw-r--r--backend/ma1509.conf.in10
-rw-r--r--backend/ma1509.h177
-rw-r--r--backend/magicolor.c3006
-rw-r--r--backend/magicolor.conf.in42
-rw-r--r--backend/magicolor.h233
-rw-r--r--backend/matsushita.c2512
-rw-r--r--backend/matsushita.conf.in23
-rw-r--r--backend/matsushita.h369
-rw-r--r--backend/microtek.c4187
-rw-r--r--backend/microtek.conf.in7
-rw-r--r--backend/microtek.h379
-rw-r--r--backend/microtek2.c8409
-rw-r--r--backend/microtek2.conf.in11
-rw-r--r--backend/microtek2.h1384
-rw-r--r--backend/mustek.c6776
-rw-r--r--backend/mustek.conf.in41
-rw-r--r--backend/mustek.h303
-rw-r--r--backend/mustek_pp.c1958
-rw-r--r--backend/mustek_pp.conf.in103
-rw-r--r--backend/mustek_pp.h286
-rw-r--r--backend/mustek_pp_ccd300.c2061
-rw-r--r--backend/mustek_pp_ccd300.h102
-rw-r--r--backend/mustek_pp_cis.c2854
-rw-r--r--backend/mustek_pp_cis.h273
-rw-r--r--backend/mustek_pp_decl.h83
-rw-r--r--backend/mustek_pp_drivers.h72
-rw-r--r--backend/mustek_pp_null.c153
-rw-r--r--backend/mustek_scsi_pp.c1086
-rw-r--r--backend/mustek_scsi_pp.h123
-rw-r--r--backend/mustek_usb.c1610
-rw-r--r--backend/mustek_usb.conf.in39
-rw-r--r--backend/mustek_usb.h62
-rw-r--r--backend/mustek_usb2.c2696
-rw-r--r--backend/mustek_usb2.h158
-rw-r--r--backend/mustek_usb2_asic.c5264
-rw-r--r--backend/mustek_usb2_asic.h1421
-rw-r--r--backend/mustek_usb2_high.c3240
-rw-r--r--backend/mustek_usb2_high.h263
-rw-r--r--backend/mustek_usb2_reflective.c1943
-rw-r--r--backend/mustek_usb2_transparent.c1745
-rw-r--r--backend/mustek_usb_high.c2751
-rw-r--r--backend/mustek_usb_high.h591
-rw-r--r--backend/mustek_usb_low.c2914
-rw-r--r--backend/mustek_usb_low.h450
-rw-r--r--backend/mustek_usb_mid.c2688
-rw-r--r--backend/mustek_usb_mid.h390
-rw-r--r--backend/nec.c3717
-rw-r--r--backend/nec.conf.in1
-rw-r--r--backend/nec.h447
-rw-r--r--backend/net.c2378
-rw-r--r--backend/net.conf.in14
-rw-r--r--backend/net.h88
-rw-r--r--backend/niash.c1486
-rw-r--r--backend/niash_core.c1362
-rw-r--r--backend/niash_core.h136
-rw-r--r--backend/niash_xfer.c330
-rw-r--r--backend/niash_xfer.h95
-rw-r--r--backend/p5.c2048
-rw-r--r--backend/p5.conf.in10
-rw-r--r--backend/p5.h205
-rw-r--r--backend/p5_device.c1611
-rw-r--r--backend/p5_device.h304
-rw-r--r--backend/pie-scsidef.h433
-rw-r--r--backend/pie.c3828
-rw-r--r--backend/pie.conf.in4
-rw-r--r--backend/pint.c987
-rw-r--r--backend/pint.h105
-rw-r--r--backend/pixma.c1825
-rw-r--r--backend/pixma.conf.in14
-rw-r--r--backend/pixma.h490
-rw-r--r--backend/pixma_bjnp.c2356
-rw-r--r--backend/pixma_bjnp.h202
-rw-r--r--backend/pixma_bjnp_private.h349
-rw-r--r--backend/pixma_common.c1172
-rw-r--r--backend/pixma_common.h231
-rw-r--r--backend/pixma_imageclass.c799
-rw-r--r--backend/pixma_io.h186
-rw-r--r--backend/pixma_io_sanei.c592
-rw-r--r--backend/pixma_mp150.c1759
-rw-r--r--backend/pixma_mp730.c828
-rw-r--r--backend/pixma_mp750.c970
-rw-r--r--backend/pixma_mp810.c2365
-rw-r--r--backend/pixma_rename.h105
-rw-r--r--backend/pixma_sane_options.c346
-rw-r--r--backend/pixma_sane_options.h51
-rw-r--r--backend/plustek-pp.h640
-rw-r--r--backend/plustek-pp_dac.c2658
-rw-r--r--backend/plustek-pp_dbg.h101
-rw-r--r--backend/plustek-pp_detect.c526
-rw-r--r--backend/plustek-pp_genericio.c1463
-rw-r--r--backend/plustek-pp_hwdefs.h1034
-rw-r--r--backend/plustek-pp_image.c1652
-rw-r--r--backend/plustek-pp_io.c999
-rw-r--r--backend/plustek-pp_map.c304
-rw-r--r--backend/plustek-pp_misc.c823
-rw-r--r--backend/plustek-pp_models.c614
-rw-r--r--backend/plustek-pp_motor.c3348
-rw-r--r--backend/plustek-pp_p12.c778
-rw-r--r--backend/plustek-pp_p12ccd.c1149
-rw-r--r--backend/plustek-pp_p48xx.c877
-rw-r--r--backend/plustek-pp_p9636.c1088
-rw-r--r--backend/plustek-pp_procfs.c474
-rw-r--r--backend/plustek-pp_procs.h255
-rw-r--r--backend/plustek-pp_ptdrv.c1987
-rw-r--r--backend/plustek-pp_scale.c149
-rw-r--r--backend/plustek-pp_scan.h202
-rw-r--r--backend/plustek-pp_scandata.h642
-rw-r--r--backend/plustek-pp_sysdep.h362
-rw-r--r--backend/plustek-pp_tpa.c1174
-rw-r--r--backend/plustek-pp_types.h191
-rw-r--r--backend/plustek-pp_wrapper.c361
-rw-r--r--backend/plustek-usb.c1526
-rw-r--r--backend/plustek-usb.h725
-rw-r--r--backend/plustek-usbcal.c1388
-rw-r--r--backend/plustek-usbcalfile.c845
-rw-r--r--backend/plustek-usbdevs.c3095
-rw-r--r--backend/plustek-usbhw.c1848
-rw-r--r--backend/plustek-usbimg.c1984
-rw-r--r--backend/plustek-usbio.c351
-rw-r--r--backend/plustek-usbmap.c210
-rw-r--r--backend/plustek-usbscan.c1666
-rw-r--r--backend/plustek-usbshading.c3215
-rw-r--r--backend/plustek.c2806
-rw-r--r--backend/plustek.conf.in184
-rw-r--r--backend/plustek.h431
-rw-r--r--backend/plustek_pp.c2180
-rw-r--r--backend/plustek_pp.conf.in40
-rw-r--r--backend/pnm.c1395
-rw-r--r--backend/qcam.c2264
-rw-r--r--backend/qcam.conf.in12
-rw-r--r--backend/qcam.h190
-rw-r--r--backend/ricoh-scsi.c413
-rw-r--r--backend/ricoh.c1025
-rw-r--r--backend/ricoh.conf.in2
-rw-r--r--backend/ricoh.h330
-rw-r--r--backend/rts8891.c7817
-rw-r--r--backend/rts8891.conf.in9
-rw-r--r--backend/rts8891.h155
-rw-r--r--backend/rts8891_devices.c248
-rw-r--r--backend/rts8891_low.c869
-rw-r--r--backend/rts8891_low.h301
-rw-r--r--backend/rts88xx_lib.c870
-rw-r--r--backend/rts88xx_lib.h213
-rw-r--r--backend/s9036.c1341
-rw-r--r--backend/s9036.conf.in1
-rw-r--r--backend/s9036.h83
-rw-r--r--backend/sane_strstatus.c109
-rw-r--r--backend/saned.conf.in31
-rw-r--r--backend/sceptre.c2094
-rw-r--r--backend/sceptre.conf.in2
-rw-r--r--backend/sceptre.h414
-rw-r--r--backend/sharp.c4213
-rw-r--r--backend/sharp.conf.in42
-rw-r--r--backend/sharp.h462
-rw-r--r--backend/sm3600-color.c313
-rw-r--r--backend/sm3600-gray.c389
-rw-r--r--backend/sm3600-homerun.c499
-rw-r--r--backend/sm3600-scanmtek.c310
-rw-r--r--backend/sm3600-scantool.h134
-rw-r--r--backend/sm3600-scanusb.c443
-rw-r--r--backend/sm3600-scanutil.c440
-rw-r--r--backend/sm3600.c804
-rw-r--r--backend/sm3600.h315
-rwxr-xr-xbackend/sm3840.c846
-rw-r--r--backend/sm3840.conf.in5
-rwxr-xr-xbackend/sm3840.h125
-rwxr-xr-xbackend/sm3840_lib.c1004
-rwxr-xr-xbackend/sm3840_lib.h152
-rw-r--r--backend/sm3840_params.h74
-rwxr-xr-xbackend/sm3840_scan.c946
-rw-r--r--backend/snapscan-data.c4150
-rw-r--r--backend/snapscan-mutex.c183
-rw-r--r--backend/snapscan-options.c1922
-rw-r--r--backend/snapscan-scsi.c1948
-rw-r--r--backend/snapscan-sources.c1385
-rw-r--r--backend/snapscan-sources.h109
-rw-r--r--backend/snapscan-usb.c656
-rw-r--r--backend/snapscan-usb.h128
-rw-r--r--backend/snapscan.c2646
-rw-r--r--backend/snapscan.conf.in115
-rw-r--r--backend/snapscan.h751
-rw-r--r--backend/sp15c-scsi.h566
-rw-r--r--backend/sp15c.c2232
-rw-r--r--backend/sp15c.conf.in1
-rw-r--r--backend/sp15c.h336
-rw-r--r--backend/st400.c1309
-rw-r--r--backend/st400.conf.in53
-rw-r--r--backend/st400.h79
-rw-r--r--backend/stubs.c85
-rw-r--r--backend/stv680.c2180
-rwxr-xr-xbackend/stv680.conf.in10
-rwxr-xr-xbackend/stv680.h302
-rw-r--r--backend/tamarack.c1474
-rw-r--r--backend/tamarack.conf.in3
-rw-r--r--backend/tamarack.h267
-rw-r--r--backend/teco1.c2263
-rw-r--r--backend/teco1.conf.in17
-rw-r--r--backend/teco1.h399
-rw-r--r--backend/teco2.c3455
-rw-r--r--backend/teco2.conf.in29
-rw-r--r--backend/teco2.h465
-rw-r--r--backend/teco3.c2255
-rw-r--r--backend/teco3.conf.in11
-rw-r--r--backend/teco3.h427
-rw-r--r--backend/test-picture.c849
-rw-r--r--backend/test.c2841
-rw-r--r--backend/test.conf.in87
-rw-r--r--backend/test.h146
-rw-r--r--backend/u12-ccd.c1121
-rw-r--r--backend/u12-hw.c978
-rw-r--r--backend/u12-hwdef.h473
-rw-r--r--backend/u12-if.c602
-rw-r--r--backend/u12-image.c908
-rw-r--r--backend/u12-io.c851
-rw-r--r--backend/u12-map.c192
-rw-r--r--backend/u12-motor.c519
-rw-r--r--backend/u12-scanner.h306
-rw-r--r--backend/u12-shading.c877
-rw-r--r--backend/u12-tpa.c441
-rw-r--r--backend/u12.c1869
-rw-r--r--backend/u12.conf.in67
-rw-r--r--backend/u12.h336
-rw-r--r--backend/umax-scanner.c136
-rw-r--r--backend/umax-scanner.h61
-rw-r--r--backend/umax-scsidef.h1091
-rw-r--r--backend/umax-uc1200s.c204
-rw-r--r--backend/umax-uc1200se.c204
-rw-r--r--backend/umax-uc1260.c204
-rw-r--r--backend/umax-uc630.c195
-rw-r--r--backend/umax-uc840.c195
-rw-r--r--backend/umax-ug630.c195
-rw-r--r--backend/umax-ug80.c198
-rw-r--r--backend/umax-usb.c324
-rw-r--r--backend/umax.c8131
-rw-r--r--backend/umax.conf.in117
-rw-r--r--backend/umax.h499
-rw-r--r--backend/umax1220u-common.c2386
-rw-r--r--backend/umax1220u.c960
-rw-r--r--backend/umax1220u.conf.in14
-rw-r--r--backend/umax_pp.c2404
-rw-r--r--backend/umax_pp.conf.in64
-rw-r--r--backend/umax_pp.h207
-rw-r--r--backend/umax_pp_low.c13195
-rw-r--r--backend/umax_pp_low.h123
-rw-r--r--backend/umax_pp_mid.c475
-rw-r--r--backend/umax_pp_mid.h191
-rw-r--r--backend/v4l-frequencies.h113
-rw-r--r--backend/v4l.c1137
-rw-r--r--backend/v4l.conf.in10
-rw-r--r--backend/v4l.h242
-rw-r--r--backend/xerox_mfp-tcp.c182
-rw-r--r--backend/xerox_mfp-usb.c112
-rw-r--r--backend/xerox_mfp.c1343
-rw-r--r--backend/xerox_mfp.conf.in212
-rw-r--r--backend/xerox_mfp.h216
493 files changed, 533735 insertions, 0 deletions
diff --git a/backend/Makefile.am b/backend/Makefile.am
new file mode 100644
index 0000000..f84d23e
--- /dev/null
+++ b/backend/Makefile.am
@@ -0,0 +1,1126 @@
+## Makefile.am -- an automake template for Makefile.in file
+## Copyright (C) 2009 Chris Bagwell, Olaf Meeuwissen, and Sane Developers.
+##
+## This file is part of the "Sane" build infra-structure. See
+## included LICENSE file for license information.
+
+INSTALL_LOCKPATH = @INSTALL_LOCKPATH@
+LOCKPATH_GROUP = @LOCKPATH_GROUP@
+
+BACKEND_LIBS_ENABLED=@BACKEND_LIBS_ENABLED@
+BACKEND_CONFS_ENABLED=@BACKEND_CONFS_ENABLED@
+
+DL_LIBS = @DL_LIBS@
+LIBV4L_LIBS = @LIBV4L_LIBS@
+MATH_LIB = @MATH_LIB@
+IEEE1284_LIBS = @IEEE1284_LIBS@
+TIFF_LIBS = @TIFF_LIBS@
+JPEG_LIBS = @JPEG_LIBS@
+GPHOTO2_LIBS = @GPHOTO2_LIBS@
+GPHOTO2_LDFLAGS = @GPHOTO2_LDFLAGS@
+SOCKET_LIBS = @SOCKET_LIBS@
+AVAHI_LIBS = @AVAHI_LIBS@
+USB_LIBS = @USB_LIBS@
+SCSI_LIBS = @SCSI_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+
+AM_CPPFLAGS = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include -DLIBDIR="$(libdir)/sane"
+
+V_MAJOR = @V_MAJOR@
+V_MINOR = @V_MINOR@
+V_REV = @V_REV@
+
+AM_LDFLAGS = @STRICT_LDFLAGS@
+# The -rpath option is added because we are creating _LTLIBRARIES based
+# on configure substitution. This causes automake to not know the
+# correct $libdir and must be added here.
+DIST_SANELIBS_LDFLAGS = $(AM_LDFLAGS) -rpath '$(libdir)/sane' -version-number $(V_MAJOR):$(V_MINOR):$(V_REV) $(DYNAMIC_FLAG)
+DIST_LIBS_LDFLAGS = $(AM_LDFLAGS) -rpath '$(libdir)' -version-number $(V_MAJOR):$(V_MINOR):$(V_REV)
+
+# LIBTOOL install is a little to noisy for my liking.
+LIBTOOL = @LIBTOOL@ --silent
+FIRMWARE_DIRS = artec_eplus48u gt68xx snapscan epjitsu
+
+EXTRA_DIST = sane_strstatus.c
+
+all: becfg
+
+EXTRA_DIST += stubs.c
+
+# FIXME: % is a GNU extension... This is only thing left requiring
+# use to use GNU make.
+%-s.c: $(srcdir)/stubs.c
+ rm -f $@
+ $(LN_S) $(srcdir)/stubs.c $@
+
+dll-preload.h:
+ rm -f $@
+ list="$(PRELOADABLE_BACKENDS)"; for be in $$list; do \
+ echo "PRELOAD_DECL($$be)" >> $@; \
+ done
+ echo "static struct backend preloaded_backends[] = {" >> $@
+ sep=""; \
+ list="$(PRELOADABLE_BACKENDS)"; \
+ if test -z "$${list}"; then \
+ echo { 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} >> $@; \
+ else \
+ for be in $$list; do \
+ echo "$${sep}PRELOAD_DEFN($$be)" >> $@; \
+ sep=","; \
+ done; \
+ fi
+ echo "};" >> $@
+
+# TODO: This really belongs together with the saned sources and
+# should be installed there as well.
+EXTRA_DIST += saned.conf.in
+
+# Backends are not required to have a config file. Any backend
+# that wants to install a config file should list it here.
+BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \
+ artec_eplus48u.conf avision.conf bh.conf \
+ canon630u.conf canon.conf canon_dr.conf \
+ canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \
+ coolscan.conf dc210.conf dc240.conf dc25.conf \
+ dell1600n_net.conf dmc.conf epjitsu.conf epson2.conf \
+ epson.conf fujitsu.conf genesys.conf gphoto2.conf \
+ gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
+ hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\
+ leo.conf lexmark.conf ma1509.conf magicolor.conf \
+ matsushita.conf microtek2.conf microtek.conf mustek.conf \
+ mustek_pp.conf mustek_usb.conf nec.conf net.conf \
+ p5.conf \
+ pie.conf pixma.conf plustek.conf plustek_pp.conf \
+ qcam.conf ricoh.conf rts8891.conf s9036.conf sceptre.conf \
+ sharp.conf sm3840.conf snapscan.conf sp15c.conf \
+ st400.conf stv680.conf tamarack.conf \
+ teco1.conf teco2.conf teco3.conf test.conf \
+ u12.conf umax1220u.conf umax.conf umax_pp.conf v4l.conf \
+ xerox_mfp.conf dll.conf saned.conf
+becfg: $(BACKEND_CONFS)
+
+SUFFIXES = .conf.in .conf
+.conf.in.conf:
+ @echo Generating $@ from $^
+ @sed -e 's|@DATADIR@|$(datadir)|g' \
+ -e 's|@CONFIGDIR@|$(configdir)|g' \
+ -e 's|@DOCDIR@|$(docdir)|g' \
+ -e 's|@LIBDIR@|$(libdir)/sane|g' \
+ -e 's|@BINDIR@|$(bindir)|g' \
+ -e 's|@SBINDIR@|$(sbindir)|g' \
+ -e 's|@PACKAGEVERSION@|$(PACKAGE_VERSION)|g' $? > $@
+
+install-data-hook: install-becfg install-firmware-path $(INSTALL_LOCKPATH)
+
+# Custom install target to install config files. Do not overwrite
+# files that have been previously installed so that user modifications
+# are not lost.
+install-becfg:
+ @# Libtool has a bug where it will sometimes symlink the last
+ @# installed library in $(sanelibdir) to $(sanelibdir)/libsane.*.
+ @# Having two libsane's can cause issues so get rid of it.
+ -rm -f $(DESTDIR)$(sanelibdir)/libsane.*
+ test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)"
+ test -z "$(configdir)/dll.d" || $(MKDIR_P) "$(DESTDIR)$(configdir)/dll.d"
+ @list="$(BACKEND_CONFS_ENABLED) saned.conf dll.conf"; for cfg in $$list; do \
+ if test ! -r $${cfg}; then continue; fi; \
+ if test -f $(DESTDIR)$(configdir)/$${cfg}; then \
+ echo NOT overwriting $${cfg} in $(configdir)...; \
+ else \
+ echo installing $${cfg} in $(configdir)/$${cfg}...; \
+ $(INSTALL_DATA) $${cfg} $(DESTDIR)$(configdir)/$${cfg} \
+ || exit 1; \
+ fi; \
+ done
+
+install-firmware-path:
+ for dir in $(FIRMWARE_DIRS) ; do \
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/sane/$${dir} ; \
+ done
+
+install-lockpath:
+ $(mkinstalldirs) -m 775 $(DESTDIR)$(locksanedir)
+
+uninstall-hook:
+ rm -rf $(DESTDIR)$(libdir)/sane $(DESTDIR)$(configdir) $(DESTDIR)$(locksanedir)
+ rm -f $(DESTDIR)$(libdir)/libsane.*
+ -for dir in $(FIRMWARE_DIRS) ; do \
+ rmdir $(DESTDIR)$(datadir)/sane/$${dir} ; \
+ done
+
+CLEANFILES = $(BACKEND_CONFS) $(be_convenience_libs)
+clean-local:
+ find . -type l -name \*-s.c | xargs rm -f
+
+
+# Backends
+#
+# All possible backends should be listed here. As a first step, we create
+# a convenience library containing all files needed to link a backend
+# directly into libsane.la. Convenience library should have the
+# form of lib${backend}.la to match what configure will list to
+# build.
+# Occasionally, this approach will have name conflicts with external
+# libraries that need to be linked in. See libgphoto2_i.la for
+# example of working around that issue.
+be_convenience_libs = libabaton.la libagfafocus.la \
+ libapple.la libartec.la libartec_eplus48u.la \
+ libas6e.la libavision.la libbh.la \
+ libcanon.la libcanon630u.la libcanon_dr.la \
+ libcanon_pp.la libcardscan.la libcoolscan.la \
+ libcoolscan2.la libcoolscan3.la libdc25.la \
+ libdc210.la libdc240.la libdell1600n_net.la \
+ libdmc.la libdll.la libdll_preload.la libepjitsu.la libepson.la \
+ libepson2.la libfujitsu.la libgenesys.la \
+ libgphoto2_i.la libgt68xx.la libhp.la \
+ libhp3500.la libhp3900.la libhp4200.la \
+ libhp5400.la libhp5590.la libhpljm1005.la \
+ libhpsj5s.la libhs2p.la libibm.la libkodak.la libkodakaio.la\
+ libkvs1025.la libkvs20xx.la libkvs40xx.la \
+ libleo.la liblexmark.la libma1509.la libmagicolor.la \
+ libmatsushita.la libmicrotek.la libmicrotek2.la \
+ libmustek.la libmustek_pp.la libmustek_usb.la \
+ libmustek_usb2.la libnec.la libnet.la \
+ libniash.la libp5.la \
+ libpie.la libpint.la libpixma.la \
+ libplustek.la libplustek_pp.la libpnm.la \
+ libqcam.la libricoh.la librts8891.la \
+ libs9036.la libsceptre.la libsharp.la \
+ libsm3600.la libsm3840.la libsnapscan.la \
+ libsp15c.la libst400.la libstv680.la \
+ libtamarack.la libtest.la libteco1.la \
+ libteco2.la libteco3.la libu12.la libumax.la \
+ libumax1220u.la libumax_pp.la libv4l.la \
+ libxerox_mfp.la
+
+# Each stand alone backend thats possible to be built should be listed
+# here. There are the libraries that are installed under $(libdir)/sane.
+# Format is libsane-${backend}.la.
+be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
+ libsane-apple.la libsane-artec.la libsane-artec_eplus48u.la \
+ libsane-as6e.la libsane-avision.la libsane-bh.la \
+ libsane-canon.la libsane-canon630u.la libsane-canon_dr.la \
+ libsane-canon_pp.la libsane-cardscan.la libsane-coolscan.la \
+ libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \
+ libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \
+ libsane-dmc.la libsane-epjitsu.la libsane-epson.la \
+ libsane-epson2.la libsane-fujitsu.la libsane-genesys.la \
+ libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
+ libsane-hp3500.la libsane-hp3900.la libsane-hp4200.la \
+ libsane-hp5400.la libsane-hp5590.la libsane-hpljm1005.la \
+ libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\
+ libsane-kvs1025.la libsane-kvs20xx.la libsane-kvs40xx.la \
+ libsane-leo.la \
+ libsane-lexmark.la libsane-ma1509.la libsane-magicolor.la \
+ libsane-matsushita.la libsane-microtek.la libsane-microtek2.la \
+ libsane-mustek.la libsane-mustek_pp.la libsane-mustek_usb.la \
+ libsane-mustek_usb2.la libsane-nec.la libsane-net.la \
+ libsane-niash.la libsane-p5.la \
+ libsane-pie.la libsane-pint.la libsane-pixma.la \
+ libsane-plustek.la libsane-plustek_pp.la libsane-pnm.la \
+ libsane-qcam.la libsane-ricoh.la libsane-rts8891.la \
+ libsane-s9036.la libsane-sceptre.la libsane-sharp.la \
+ libsane-sm3600.la libsane-sm3840.la libsane-snapscan.la \
+ libsane-sp15c.la libsane-st400.la libsane-stv680.la \
+ libsane-tamarack.la libsane-test.la libsane-teco1.la \
+ libsane-teco2.la libsane-teco3.la libsane-u12.la libsane-umax.la \
+ libsane-umax1220u.la libsane-umax_pp.la libsane-v4l.la \
+ libsane-xerox_mfp.la
+
+EXTRA_LTLIBRARIES = $(be_convenience_libs) $(be_dlopen_libs)
+
+lib_LTLIBRARIES = libsane.la
+
+sanelibdir = $(libdir)/sane
+sanelib_LTLIBRARIES = $(BACKEND_LIBS_ENABLED) libsane-dll.la
+
+COMMON_LIBS = ../lib/liblib.la
+
+# Each backend should define a convenience library that compiles
+# all related files within backend directory. General guideline
+# is to have a ${backend}.c and ${backend}.h. Some backends also
+# add a few support source files to convience library.
+# Note: automake doesn't really use header files listed here.
+# They are indications that they need to be distributed only.
+libabaton_la_SOURCES = abaton.c abaton.h
+libabaton_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=abaton
+
+# Each backend should define a stand alone library that gets installed.
+# This will need to link in a special file ${backend}-s.c that allows
+# the backend to be stand alone and contain all SANE API functions.
+# Also, it will need to link in related convenience library as well as
+# any external libraries required to resolve symbols.
+#
+# All backends should include $(DIST_SANELIBS_LDFLAGS) so that
+# library is correctly versioned.
+#
+# If a backend has a config file, it must be listed here to get distributed.
+nodist_libsane_abaton_la_SOURCES = abaton-s.c
+libsane_abaton_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=abaton
+libsane_abaton_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_abaton_la_LIBADD = $(COMMON_LIBS) libabaton.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += abaton.conf.in
+
+libagfafocus_la_SOURCES = agfafocus.c agfafocus.h
+libagfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus
+
+nodist_libsane_agfafocus_la_SOURCES = agfafocus-s.c
+libsane_agfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus
+libsane_agfafocus_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_agfafocus_la_LIBADD = $(COMMON_LIBS) libagfafocus.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += agfafocus.conf.in
+
+libapple_la_SOURCES = apple.c apple.h
+libapple_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=apple
+
+nodist_libsane_apple_la_SOURCES = apple-s.c
+libsane_apple_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=apple
+libsane_apple_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_apple_la_LIBADD = $(COMMON_LIBS) libapple.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += apple.conf.in
+
+libartec_la_SOURCES = artec.c artec.h
+libartec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec
+
+nodist_libsane_artec_la_SOURCES = artec-s.c
+libsane_artec_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_artec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec
+libsane_artec_la_LIBADD = $(COMMON_LIBS) libartec.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += artec.conf.in
+
+libartec_eplus48u_la_SOURCES = artec_eplus48u.c artec_eplus48u.h
+libartec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u
+
+nodist_libsane_artec_eplus48u_la_SOURCES = artec_eplus48u-s.c
+libsane_artec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u
+libsane_artec_eplus48u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_artec_eplus48u_la_LIBADD = $(COMMON_LIBS) libartec_eplus48u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMEG_LIBS)
+EXTRA_DIST += artec_eplus48u.conf.in
+
+libas6e_la_SOURCES = as6e.c as6e.h
+libas6e_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=as6e
+
+nodist_libsane_as6e_la_SOURCES = as6e-s.c
+libsane_as6e_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=as6e
+libsane_as6e_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_as6e_la_LIBADD = $(COMMON_LIBS) libas6e.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+
+libavision_la_SOURCES = avision.c avision.h
+libavision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision
+
+nodist_libsane_avision_la_SOURCES = avision-s.c
+libsane_avision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision
+libsane_avision_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_avision_la_LIBADD = $(COMMON_LIBS) libavision.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += avision.conf.in
+
+libbh_la_SOURCES = bh.c bh.h
+libbh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=bh
+
+nodist_libsane_bh_la_SOURCES = bh-s.c
+libsane_bh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=bh
+libsane_bh_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_bh_la_LIBADD = $(COMMON_LIBS) libbh.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += bh.conf.in
+
+libcanon_la_SOURCES = canon.c canon.h
+libcanon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon
+
+nodist_libsane_canon_la_SOURCES = canon-s.c
+libsane_canon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon
+libsane_canon_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon_la_LIBADD = $(COMMON_LIBS) libcanon.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += canon.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += canon-sane.c canon-scsi.c
+
+libcanon630u_la_SOURCES = canon630u.c
+libcanon630u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon630u
+
+nodist_libsane_canon630u_la_SOURCES = canon630u-s.c
+libsane_canon630u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon630u
+libsane_canon630u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon630u_la_LIBADD = $(COMMON_LIBS) libcanon630u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += canon630u.conf.in
+# TODO: Why are this distributed but not compiled?
+EXTRA_DIST += canon630u-common.c lm9830.h
+
+libcanon_dr_la_SOURCES = canon_dr.c canon_dr.h canon_dr-cmd.h
+libcanon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr
+
+nodist_libsane_canon_dr_la_SOURCES = canon_dr-s.c
+libsane_canon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr
+libsane_canon_dr_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += canon_dr.conf.in
+
+libcanon_pp_la_SOURCES = canon_pp.c canon_pp.h canon_pp-io.c canon_pp-io.h canon_pp-dev.c canon_pp-dev.h
+libcanon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp
+
+nodist_libsane_canon_pp_la_SOURCES = canon_pp-s.c
+libsane_canon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp
+libsane_canon_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon_pp_la_LIBADD = $(COMMON_LIBS) libcanon_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(IEEE1284_LIBS)
+EXTRA_DIST += canon_pp.conf.in
+
+libcardscan_la_SOURCES = cardscan.c cardscan.h
+libcardscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=cardscan
+
+nodist_libsane_cardscan_la_SOURCES = cardscan-s.c
+libsane_cardscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=cardscan
+libsane_cardscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_cardscan_la_LIBADD = $(COMMON_LIBS) libcardscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += cardscan.conf.in
+
+libcoolscan_la_SOURCES = coolscan.c coolscan.h coolscan-scsidef.h
+libcoolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan
+
+nodist_libsane_coolscan_la_SOURCES = coolscan-s.c
+libsane_coolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan
+libsane_coolscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_coolscan_la_LIBADD = $(COMMON_LIBS) libcoolscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += coolscan.conf.in
+
+libcoolscan2_la_SOURCES = coolscan2.c
+libcoolscan2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan2
+
+nodist_libsane_coolscan2_la_SOURCES = coolscan2-s.c
+libsane_coolscan2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan2
+libsane_coolscan2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_coolscan2_la_LIBADD = $(COMMON_LIBS) libcoolscan2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += coolscan2.conf.in
+
+libcoolscan3_la_SOURCES = coolscan3.c
+libcoolscan3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan3
+
+nodist_libsane_coolscan3_la_SOURCES = coolscan3-s.c
+libsane_coolscan3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan3
+libsane_coolscan3_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_coolscan3_la_LIBADD = $(COMMON_LIBS) libcoolscan3.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += coolscan3.conf.in
+
+libdc25_la_SOURCES = dc25.c dc25.h
+libdc25_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc25
+
+nodist_libsane_dc25_la_SOURCES = dc25-s.c
+libsane_dc25_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc25
+libsane_dc25_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dc25_la_LIBADD = $(COMMON_LIBS) libdc25.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(MATH_LIB)
+EXTRA_DIST += dc25.conf.in
+
+libdc210_la_SOURCES = dc210.c dc210.h
+libdc210_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc210
+
+nodist_libsane_dc210_la_SOURCES = dc210-s.c
+libsane_dc210_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc210
+libsane_dc210_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dc210_la_LIBADD = $(COMMON_LIBS) libdc210.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo @SANEI_SANEI_JPEG_LO@ $(JPEG_LIBS)
+EXTRA_DIST += dc210.conf.in
+
+libdc240_la_SOURCES = dc240.c dc240.h
+libdc240_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc240
+
+nodist_libsane_dc240_la_SOURCES = dc240-s.c
+libsane_dc240_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc240
+libsane_dc240_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dc240_la_LIBADD = $(COMMON_LIBS) libdc240.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo @SANEI_SANEI_JPEG_LO@ $(JPEG_LIBS)
+EXTRA_DIST += dc240.conf.in
+
+libdell1600n_net_la_SOURCES = dell1600n_net.c
+libdell1600n_net_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dell1600n_net
+
+nodist_libsane_dell1600n_net_la_SOURCES = dell1600n_net-s.c
+libsane_dell1600n_net_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dell1600n_net
+libsane_dell1600n_net_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dell1600n_net_la_LIBADD = $(COMMON_LIBS) libdell1600n_net.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(TIFF_LIBS) $(JPEG_LIBS) $(SOCKET_LIBS)
+EXTRA_DIST += dell1600n_net.conf.in
+
+libdmc_la_SOURCES = dmc.c dmc.h
+libdmc_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dmc
+
+nodist_libsane_dmc_la_SOURCES = dmc-s.c
+libsane_dmc_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dmc
+libsane_dmc_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dmc_la_LIBADD = $(COMMON_LIBS) libdmc.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += dmc.conf.in
+
+libepjitsu_la_SOURCES = epjitsu.c epjitsu.h epjitsu-cmd.h
+libepjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
+
+nodist_libsane_epjitsu_la_SOURCES = epjitsu-s.c
+libsane_epjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
+libsane_epjitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_epjitsu_la_LIBADD = $(COMMON_LIBS) libepjitsu.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += epjitsu.conf.in
+
+libepson_la_SOURCES = epson.c epson.h epson_scsi.c epson_scsi.h epson_usb.c epson_usb.h
+libepson_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson
+
+nodist_libsane_epson_la_SOURCES = epson-s.c
+libsane_epson_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson
+libsane_epson_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_epson_la_LIBADD = $(COMMON_LIBS) libepson.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += epson.conf.in
+
+libepson2_la_SOURCES = epson2.c epson2.h epson2_scsi.c epson2_scsi.h epson_usb.c epson2_net.c epson2_net.h epson2-io.c epson2-io.h epson2-commands.c epson2-commands.h epson2-ops.c epson2-ops.h epson2-cct.c
+libepson2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson2
+
+nodist_libsane_epson2_la_SOURCES = epson2-s.c
+libsane_epson2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson2
+libsane_epson2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_epson2_la_LIBADD = $(COMMON_LIBS) libepson2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo $(SCSI_LIBS) $(USB_LIBS) $(SOCKET_LIBS) $(MATH_LIB) $(RESMGR_LIBS)
+EXTRA_DIST += epson2.conf.in
+
+libfujitsu_la_SOURCES = fujitsu.c fujitsu.h fujitsu-scsi.h
+libfujitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=fujitsu
+
+nodist_libsane_fujitsu_la_SOURCES = fujitsu-s.c
+libsane_fujitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=fujitsu
+libsane_fujitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_fujitsu_la_LIBADD = $(COMMON_LIBS) libfujitsu.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += fujitsu.conf.in
+
+libgenesys_la_SOURCES = genesys.c genesys.h genesys_gl646.c genesys_gl646.h genesys_gl841.c genesys_gl841.h genesys_gl843.c genesys_gl843.h genesys_gl846.c genesys_gl846.h genesys_gl847.c genesys_gl847.h genesys_gl124.c genesys_gl124.h genesys_low.c genesys_low.h
+libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
+
+nodist_libsane_genesys_la_SOURCES = genesys-s.c
+libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
+libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += genesys.conf.in
+# TODO: Why are this distributed but not compiled?
+EXTRA_DIST += genesys_conv.c genesys_conv_hlp.c genesys_devices.c
+
+libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h
+libgphoto2_i_la_CPPFLAGS = $(AM_CPPFLAGS) @GPHOTO2_CPPFLAGS@ -DBACKEND_NAME=gphoto2
+
+nodist_libsane_gphoto2_la_SOURCES = gphoto2-s.c
+libsane_gphoto2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gphoto2
+libsane_gphoto2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_gphoto2_la_LIBADD = $(GPHOTO2_LDFLAGS) $(COMMON_LIBS) libgphoto2_i.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo @SANEI_SANEI_JPEG_LO@ $(GPHOTO2_LIBS) $(JPEG_LIBS)
+EXTRA_DIST += gphoto2.conf.in
+
+libgt68xx_la_SOURCES = gt68xx.c gt68xx.h
+libgt68xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gt68xx
+
+nodist_libsane_gt68xx_la_SOURCES = gt68xx-s.c
+libsane_gt68xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gt68xx
+libsane_gt68xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_gt68xx_la_LIBADD = $(COMMON_LIBS) libgt68xx.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += gt68xx.conf.in
+# TODO: Why are this distributed but not compiled?
+EXTRA_DIST += gt68xx_devices.c gt68xx_generic.c gt68xx_generic.h gt68xx_gt6801.c gt68xx_gt6801.h gt68xx_gt6816.c gt68xx_gt6816.h gt68xx_high.c gt68xx_high.h gt68xx_low.c gt68xx_low.h gt68xx_mid.c gt68xx_mid.h gt68xx_shm_channel.c gt68xx_shm_channel.h
+
+libhp_la_SOURCES = hp.c hp.h hp-accessor.c hp-accessor.h hp-device.c hp-device.h hp-handle.c hp-handle.h hp-hpmem.c hp-option.c hp-option.h hp-scl.c hp-scl.h hp-scsi.h
+libhp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp
+
+nodist_libsane_hp_la_SOURCES = hp-s.c
+libsane_hp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp
+libsane_hp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp_la_LIBADD = $(COMMON_LIBS) libhp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += hp.conf.in
+# TODO: These should be moved to ../docs/hp; don't belong here.
+EXTRA_DIST += hp.README hp.TODO
+
+libhp3500_la_SOURCES = hp3500.c
+libhp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500
+
+nodist_libsane_hp3500_la_SOURCES = hp3500-s.c
+libsane_hp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500
+libsane_hp3500_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp3500_la_LIBADD = $(COMMON_LIBS) libhp3500.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+
+libhp3900_la_SOURCES = hp3900.c
+libhp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900
+
+nodist_libsane_hp3900_la_SOURCES = hp3900-s.c
+libsane_hp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900
+libsane_hp3900_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp3900_la_LIBADD = $(COMMON_LIBS) libhp3900.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(TIFF_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += hp3900.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += hp3900_config.c hp3900_debug.c hp3900_rts8822.c hp3900_sane.c hp3900_types.c hp3900_usb.c
+
+libhp4200_la_SOURCES = hp4200.c hp4200.h
+libhp4200_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp4200
+
+nodist_libsane_hp4200_la_SOURCES = hp4200-s.c
+libsane_hp4200_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp4200
+libsane_hp4200_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp4200_la_LIBADD = $(COMMON_LIBS) libhp4200.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += hp4200.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += hp4200_lm9830.c hp4200_lm9830.h
+
+libhp5400_la_SOURCES = hp5400.c hp5400.h
+libhp5400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5400
+
+nodist_libsane_hp5400_la_SOURCES = hp5400-s.c
+libsane_hp5400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5400
+libsane_hp5400_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp5400_la_LIBADD = $(COMMON_LIBS) libhp5400.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += hp5400.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += hp5400_debug.c hp5400_debug.h hp5400_internal.c hp5400_internal.h hp5400_sane.c hp5400_sanei.c hp5400_sanei.h hp5400_xfer.h
+
+libhp5590_la_SOURCES = hp5590.c
+libhp5590_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5590
+
+nodist_libsane_hp5590_la_SOURCES = hp5590-s.c
+libsane_hp5590_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5590
+libsane_hp5590_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp5590_la_LIBADD = $(COMMON_LIBS) libhp5590.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += hp5590_cmds.c hp5590_cmds.h hp5590_low.c hp5590_low.h
+
+libhpljm1005_la_SOURCES = hpljm1005.c
+libhpljm1005_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpljm1005
+
+nodist_libsane_hpljm1005_la_SOURCES = hpljm1005-s.c
+libsane_hpljm1005_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpljm1005
+libsane_hpljm1005_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hpljm1005_la_LIBADD = $(COMMON_LIBS) libhpljm1005.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+
+libhpsj5s_la_SOURCES = hpsj5s.c hpsj5s.h
+libhpsj5s_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpsj5s
+
+nodist_libsane_hpsj5s_la_SOURCES = hpsj5s-s.c
+libsane_hpsj5s_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpsj5s
+libsane_hpsj5s_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hpsj5s_la_LIBADD = $(COMMON_LIBS) libhpsj5s.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(IEEE1284_LIBS)
+EXTRA_DIST += hpsj5s.conf.in
+
+libhs2p_la_SOURCES = hs2p.c hs2p.h hs2p-saneopts.h
+libhs2p_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hs2p
+
+nodist_libsane_hs2p_la_SOURCES = hs2p-s.c
+libsane_hs2p_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hs2p
+libsane_hs2p_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hs2p_la_LIBADD = $(COMMON_LIBS) libhs2p.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += hs2p.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += hs2p-scsi.c hs2p-scsi.h
+
+libibm_la_SOURCES = ibm.c ibm.h
+libibm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ibm
+
+nodist_libsane_ibm_la_SOURCES = ibm-s.c
+libsane_ibm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ibm
+libsane_ibm_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_ibm_la_LIBADD = $(COMMON_LIBS) libibm.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += ibm.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += ibm-scsi.c
+
+libkodak_la_SOURCES = kodak.c kodak.h kodak-cmd.h
+libkodak_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodak
+
+nodist_libsane_kodak_la_SOURCES = kodak-s.c
+libsane_kodak_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodak
+libsane_kodak_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kodak_la_LIBADD = $(COMMON_LIBS) libkodak.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += kodak.conf.in
+
+libkodakaio_la_SOURCES = kodakaio.c kodakaio.h
+libkodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodakaio
+
+nodist_libsane_kodakaio_la_SOURCES = kodakaio-s.c
+libsane_kodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodakaio
+libsane_kodakaio_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kodakaio_la_LIBADD = $(COMMON_LIBS) libkodakaio.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo $(USB_LIBS) $(SOCKET_LIBS) $(AVAHI_LIBS) $(MATH_LIB) $(RESMGR_LIBS)
+EXTRA_DIST += kodakaio.conf.in
+
+libkvs1025_la_SOURCES = kvs1025.c kvs1025_low.c kvs1025_opt.c kvs1025_usb.c \
+ kvs1025.h kvs1025_low.h kvs1025_usb.h kvs1025_cmds.h
+libkvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
+
+nodist_libsane_kvs1025_la_SOURCES = kvs1025-s.c
+libsane_kvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
+libsane_kvs1025_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) libkvs1025.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+
+libkvs20xx_la_SOURCES = kvs20xx.c kvs20xx_cmd.c kvs20xx_opt.c \
+ kvs20xx_cmd.h kvs20xx.h
+libkvs20xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs20xx
+
+nodist_libsane_kvs20xx_la_SOURCES = kvs20xx-s.c
+libsane_kvs20xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs20xx
+libsane_kvs20xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kvs20xx_la_LIBADD = $(COMMON_LIBS) libkvs20xx.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+
+libkvs40xx_la_SOURCES = kvs40xx.c kvs40xx_cmd.c kvs40xx_opt.c \
+ kvs40xx.h
+libkvs40xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs40xx
+
+nodist_libsane_kvs40xx_la_SOURCES = kvs40xx-s.c
+libsane_kvs40xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs40xx
+libsane_kvs40xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kvs40xx_la_LIBADD = $(COMMON_LIBS) libkvs40xx.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+
+libleo_la_SOURCES = leo.c leo.h
+libleo_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=leo
+
+nodist_libsane_leo_la_SOURCES = leo-s.c
+libsane_leo_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=leo
+libsane_leo_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_leo_la_LIBADD = $(COMMON_LIBS) libleo.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += leo.conf.in
+
+liblexmark_la_SOURCES = lexmark.c lexmark.h lexmark_low.c
+liblexmark_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark
+
+nodist_libsane_lexmark_la_SOURCES = lexmark-s.c
+libsane_lexmark_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark
+libsane_lexmark_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_lexmark_la_LIBADD = $(COMMON_LIBS) liblexmark.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += lexmark.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += lexmark_models.c lexmark_sensors.c
+
+libma1509_la_SOURCES = ma1509.c ma1509.h
+libma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509
+
+nodist_libsane_ma1509_la_SOURCES = ma1509-s.c
+libsane_ma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509
+libsane_ma1509_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_ma1509_la_LIBADD = $(COMMON_LIBS) libma1509.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += ma1509.conf.in
+
+libmagicolor_la_SOURCES = magicolor.c magicolor.h
+libmagicolor_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=magicolor
+
+nodist_libsane_magicolor_la_SOURCES = magicolor-s.c
+libsane_magicolor_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=magicolor
+libsane_magicolor_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_magicolor_la_LIBADD = $(COMMON_LIBS) libmagicolor.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo $(USB_LIBS) $(SOCKET_LIBS) $(MATH_LIB) $(RESMGR_LIBS)
+EXTRA_DIST += magicolor.conf.in
+
+libmatsushita_la_SOURCES = matsushita.c matsushita.h
+libmatsushita_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=matsushita
+
+nodist_libsane_matsushita_la_SOURCES = matsushita-s.c
+libsane_matsushita_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=matsushita
+libsane_matsushita_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_matsushita_la_LIBADD = $(COMMON_LIBS) libmatsushita.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += matsushita.conf.in
+
+libmicrotek_la_SOURCES = microtek.c microtek.h
+libmicrotek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek
+
+nodist_libsane_microtek_la_SOURCES = microtek-s.c
+libsane_microtek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek
+libsane_microtek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_microtek_la_LIBADD = $(COMMON_LIBS) libmicrotek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += microtek.conf.in
+
+libmicrotek2_la_SOURCES = microtek2.c microtek2.h
+libmicrotek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2
+
+nodist_libsane_microtek2_la_SOURCES = microtek2-s.c
+libsane_microtek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2
+libsane_microtek2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_microtek2_la_LIBADD = $(COMMON_LIBS) libmicrotek2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += microtek2.conf.in
+
+libmustek_la_SOURCES = mustek.c mustek.h
+libmustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek
+
+nodist_libsane_mustek_la_SOURCES = mustek-s.c
+libsane_mustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek
+libsane_mustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_la_LIBADD = $(COMMON_LIBS) libmustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pa4s2.lo $(IEEE1284_LIBS) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += mustek.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += mustek_scsi_pp.c mustek_scsi_pp.h
+
+libmustek_pp_la_SOURCES = mustek_pp.c mustek_pp.h mustek_pp_decl.h mustek_pp_drivers.h
+libmustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_pp
+
+nodist_libsane_mustek_pp_la_SOURCES = mustek_pp-s.c
+libsane_mustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_pp
+libsane_mustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_pp_la_LIBADD = $(COMMON_LIBS) libmustek_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pa4s2.lo $(MATH_LIB) $(IEEE1284_LIBS)
+EXTRA_DIST += mustek_pp.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += mustek_pp_ccd300.c mustek_pp_ccd300.h mustek_pp_cis.c mustek_pp_cis.h mustek_pp_null.c
+
+libmustek_usb_la_SOURCES = mustek_usb.c mustek_usb.h
+libmustek_usb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb
+
+nodist_libsane_mustek_usb_la_SOURCES = mustek_usb-s.c
+libsane_mustek_usb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb
+libsane_mustek_usb_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_usb_la_LIBADD = $(COMMON_LIBS) libmustek_usb.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += mustek_usb.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += mustek_usb_high.c mustek_usb_high.h mustek_usb_low.c mustek_usb_low.h mustek_usb_mid.c mustek_usb_mid.h
+
+libmustek_usb2_la_SOURCES = mustek_usb2.c mustek_usb2.h
+libmustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2
+
+nodist_libsane_mustek_usb2_la_SOURCES = mustek_usb2-s.c
+libsane_mustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2
+libsane_mustek_usb2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_usb2_la_LIBADD = $(COMMON_LIBS) libmustek_usb2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(PTHREAD_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += mustek_usb2_asic.c mustek_usb2_asic.h mustek_usb2_high.c mustek_usb2_high.h mustek_usb2_reflective.c mustek_usb2_transparent.c
+
+libnec_la_SOURCES = nec.c nec.h
+libnec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=nec
+
+nodist_libsane_nec_la_SOURCES = nec-s.c
+libsane_nec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=nec
+libsane_nec_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_nec_la_LIBADD = $(COMMON_LIBS) libnec.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += nec.conf.in
+
+libnet_la_SOURCES = net.c net.h
+libnet_la_CPPFLAGS = $(AM_CPPFLAGS) @AVAHI_CFLAGS@ -DBACKEND_NAME=net
+
+nodist_libsane_net_la_SOURCES = net-s.c
+libsane_net_la_CPPFLAGS = $(AM_CPPFLAGS) @AVAHI_CFLAGS@ -DBACKEND_NAME=net
+libsane_net_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_net_la_LIBADD = $(COMMON_LIBS) libnet.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo $(AVAHI_LIBS) $(SOCKET_LIBS)
+EXTRA_DIST += net.conf.in
+
+libniash_la_SOURCES = niash.c
+libniash_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=niash
+
+nodist_libsane_niash_la_SOURCES = niash-s.c
+libsane_niash_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=niash
+libsane_niash_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_niash_la_LIBADD = $(COMMON_LIBS) libniash.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += niash_core.c niash_core.h niash_xfer.c niash_xfer.h
+
+libpie_la_SOURCES = pie.c pie-scsidef.h
+libpie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie
+
+nodist_libsane_pie_la_SOURCES = pie-s.c
+libsane_pie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie
+libsane_pie_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pie_la_LIBADD = $(COMMON_LIBS) libpie.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += pie.conf.in
+
+libp5_la_SOURCES = p5.c p5.h p5_device.h
+libp5_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=p5
+
+nodist_libsane_p5_la_SOURCES = p5-s.c
+libsane_p5_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=p5
+libsane_p5_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_p5_la_LIBADD = $(COMMON_LIBS) libp5.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+EXTRA_DIST += p5.conf.in p5_device.c
+
+libpint_la_SOURCES = pint.c pint.h
+libpint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint
+
+nodist_libsane_pint_la_SOURCES = pint-s.c
+libsane_pint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint
+libsane_pint_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pint_la_LIBADD = $(COMMON_LIBS) libpint.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+
+libpixma_la_SOURCES = pixma.c pixma.h pixma_io_sanei.c pixma_io.h pixma_common.c pixma_common.h pixma_mp150.c pixma_mp730.c pixma_mp750.c pixma_mp810.c pixma_imageclass.c pixma_bjnp.c pixma_bjnp.h pixma_bjnp_private.h pixma_rename.h
+libpixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma
+
+nodist_libsane_pixma_la_SOURCES = pixma-s.c
+libsane_pixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma
+libsane_pixma_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pixma_la_LIBADD = $(COMMON_LIBS) libpixma.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += pixma.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += pixma_sane_options.c pixma_sane_options.h
+
+libplustek_la_SOURCES = plustek.c plustek.h
+libplustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
+
+nodist_libsane_plustek_la_SOURCES = plustek-s.c
+libsane_plustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
+libsane_plustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_plustek_la_LIBADD = $(COMMON_LIBS) libplustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += plustek.conf.in
+EXTRA_DIST += plustek-usb.c plustek-usb.h plustek-usbcal.c plustek-usbcalfile.c plustek-usbdevs.c plustek-usbhw.c plustek-usbimg.c plustek-usbio.c plustek-usbmap.c plustek-usbscan.c plustek-usbshading.c
+
+libplustek_pp_la_SOURCES = plustek_pp.c plustek-pp.h
+libplustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp
+
+nodist_libsane_plustek_pp_la_SOURCES = plustek_pp-s.c
+libsane_plustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp
+libsane_plustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_plustek_pp_la_LIBADD = $(COMMON_LIBS) libplustek_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(IEEE1284_LIBS) $(PTHREAD_LIBS)
+EXTRA_DIST += plustek_pp.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += plustek-pp_dac.c plustek-pp_dbg.h plustek-pp_detect.c plustek-pp_genericio.c plustek-pp_hwdefs.h plustek-pp_image.c plustek-pp_io.c plustek-pp_map.c plustek-pp_misc.c plustek-pp_models.c plustek-pp_motor.c plustek-pp_p12.c plustek-pp_p12ccd.c plustek-pp_p48xx.c plustek-pp_p9636.c plustek-pp_procfs.c plustek-pp_procs.h plustek-pp_ptdrv.c plustek-pp_scale.c plustek-pp_scan.h plustek-pp_scandata.h plustek-pp_sysdep.h plustek-pp_tpa.c plustek-pp_types.h plustek-pp_wrapper.c
+
+libpnm_la_SOURCES = pnm.c
+libpnm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pnm
+
+nodist_libsane_pnm_la_SOURCES = pnm-s.c
+libsane_pnm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pnm
+libsane_pnm_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pnm_la_LIBADD = $(COMMON_LIBS) libpnm.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+
+libqcam_la_SOURCES = qcam.c qcam.h
+libqcam_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=qcam
+
+nodist_libsane_qcam_la_SOURCES = qcam-s.c
+libsane_qcam_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=qcam
+libsane_qcam_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_qcam_la_LIBADD = $(COMMON_LIBS) libqcam.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pio.lo
+EXTRA_DIST += qcam.conf.in
+
+libricoh_la_SOURCES = ricoh.c ricoh.h
+libricoh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh
+
+nodist_libsane_ricoh_la_SOURCES = ricoh-s.c
+libsane_ricoh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh
+libsane_ricoh_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_ricoh_la_LIBADD = $(COMMON_LIBS) libricoh.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += ricoh.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += ricoh-scsi.c
+
+librts8891_la_SOURCES = rts8891.c rts8891.h rts88xx_lib.c rts88xx_lib.h
+librts8891_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=rts8891
+
+nodist_libsane_rts8891_la_SOURCES = rts8891-s.c
+libsane_rts8891_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=rts8891
+libsane_rts8891_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_rts8891_la_LIBADD = $(COMMON_LIBS) librts8891.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_usb.lo $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += rts8891.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += rts8891_devices.c rts8891_low.c rts8891_low.h
+
+libs9036_la_SOURCES = s9036.c s9036.h
+libs9036_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=s9036
+
+nodist_libsane_s9036_la_SOURCES = s9036-s.c
+libsane_s9036_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=s9036
+libsane_s9036_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_s9036_la_LIBADD = $(COMMON_LIBS) libs9036.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += s9036.conf.in
+
+libsceptre_la_SOURCES = sceptre.c sceptre.h
+libsceptre_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sceptre
+
+nodist_libsane_sceptre_la_SOURCES = sceptre-s.c
+libsane_sceptre_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sceptre
+libsane_sceptre_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sceptre_la_LIBADD = $(COMMON_LIBS) libsceptre.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += sceptre.conf.in
+
+libsharp_la_SOURCES = sharp.c sharp.h
+libsharp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sharp
+
+nodist_libsane_sharp_la_SOURCES = sharp-s.c
+libsane_sharp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sharp
+libsane_sharp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sharp_la_LIBADD = $(COMMON_LIBS) libsharp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += sharp.conf.in
+
+libsm3600_la_SOURCES = sm3600.c sm3600.h
+libsm3600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3600
+
+nodist_libsane_sm3600_la_SOURCES = sm3600-s.c
+libsane_sm3600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3600
+libsane_sm3600_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sm3600_la_LIBADD = $(COMMON_LIBS) libsm3600.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += sm3600-color.c sm3600-gray.c sm3600-homerun.c sm3600-scanmtek.c sm3600-scantool.h sm3600-scanusb.c sm3600-scanutil.c
+
+libsm3840_la_SOURCES = sm3840.c sm3840.h sm3840_params.h
+libsm3840_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3840
+
+nodist_libsane_sm3840_la_SOURCES = sm3840-s.c
+libsane_sm3840_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3840
+libsane_sm3840_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sm3840_la_LIBADD = $(COMMON_LIBS) libsm3840.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += sm3840.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += sm3840_lib.c sm3840_lib.h sm3840_scan.c
+
+libsnapscan_la_SOURCES = snapscan.c snapscan.h
+libsnapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan
+
+nodist_libsane_snapscan_la_SOURCES = snapscan-s.c
+libsane_snapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan
+libsane_snapscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_snapscan_la_LIBADD = $(COMMON_LIBS) libsnapscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += snapscan.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += snapscan-data.c snapscan-mutex.c snapscan-options.c snapscan-scsi.c snapscan-sources.c snapscan-sources.h snapscan-usb.c snapscan-usb.h
+
+libsp15c_la_SOURCES = sp15c.c sp15c.h sp15c-scsi.h
+libsp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c
+
+nodist_libsane_sp15c_la_SOURCES = sp15c-s.c
+libsane_sp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c
+libsane_sp15c_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sp15c_la_LIBADD = $(COMMON_LIBS) libsp15c.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += sp15c.conf.in
+
+libst400_la_SOURCES = st400.c st400.h
+libst400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=st400
+
+nodist_libsane_st400_la_SOURCES = st400-s.c ../sanei/sanei_scsi.lo
+libsane_st400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=st400
+libsane_st400_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_st400_la_LIBADD = $(COMMON_LIBS) libst400.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += st400.conf.in
+
+libstv680_la_SOURCES = stv680.c stv680.h
+libstv680_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=stv680
+
+nodist_libsane_stv680_la_SOURCES = stv680-s.c
+libsane_stv680_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=stv680
+libsane_stv680_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_stv680_la_LIBADD = $(COMMON_LIBS) libstv680.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += stv680.conf.in
+
+libtamarack_la_SOURCES = tamarack.c tamarack.h
+libtamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack
+
+nodist_libsane_tamarack_la_SOURCES = tamarack-s.c
+libsane_tamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack
+libsane_tamarack_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_tamarack_la_LIBADD = $(COMMON_LIBS) libtamarack.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += tamarack.conf.in
+
+libtest_la_SOURCES = test.c test.h
+libtest_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test
+
+nodist_libsane_test_la_SOURCES = test-s.c
+libsane_test_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test
+libsane_test_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_test_la_LIBADD = $(COMMON_LIBS) libtest.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_thread.lo $(PTHREAD_LIBS)
+EXTRA_DIST += test.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += test-picture.c
+
+libteco1_la_SOURCES = teco1.c teco1.h
+libteco1_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco1
+
+nodist_libsane_teco1_la_SOURCES = teco1-s.c
+libsane_teco1_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco1
+libsane_teco1_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_teco1_la_LIBADD = $(COMMON_LIBS) libteco1.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += teco1.conf.in
+
+libteco2_la_SOURCES = teco2.c teco2.h
+libteco2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco2
+
+nodist_libsane_teco2_la_SOURCES = teco2-s.c
+libsane_teco2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco2
+libsane_teco2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_teco2_la_LIBADD = $(COMMON_LIBS) libteco2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += teco2.conf.in
+
+libteco3_la_SOURCES = teco3.c teco3.h
+libteco3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco3
+
+nodist_libsane_teco3_la_SOURCES = teco3-s.c
+libsane_teco3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco3
+libsane_teco3_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_teco3_la_LIBADD = $(COMMON_LIBS) libteco3.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += teco3.conf.in
+
+libu12_la_SOURCES = u12.c u12.h
+libu12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12
+
+nodist_libsane_u12_la_SOURCES = u12-s.c
+libsane_u12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12
+libsane_u12_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_u12_la_LIBADD = $(COMMON_LIBS) libu12.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += u12.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += u12-ccd.c u12-hw.c u12-hwdef.h u12-if.c u12-image.c u12-io.c u12-map.c u12-motor.c u12-scanner.h u12-shading.c u12-tpa.c
+
+libumax_la_SOURCES = umax.c umax.h
+libumax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax
+
+nodist_libsane_umax_la_SOURCES = umax-s.c
+libsane_umax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax
+libsane_umax_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_umax_la_LIBADD = $(COMMON_LIBS) libumax.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += umax.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += umax-scanner.c umax-scanner.h umax-scsidef.h umax-uc1200s.c umax-uc1200se.c umax-uc1260.c umax-uc630.c umax-uc840.c umax-ug630.c umax-ug80.c umax-usb.c
+
+libumax1220u_la_SOURCES = umax1220u.c
+libumax1220u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax1220u
+
+nodist_libsane_umax1220u_la_SOURCES = umax1220u-s.c
+libsane_umax1220u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax1220u
+libsane_umax1220u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_umax1220u_la_LIBADD = $(COMMON_LIBS) libumax1220u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += umax1220u.conf.in
+# TODO: Why are these distributed but not compiled?
+EXTRA_DIST += umax1220u-common.c
+
+libumax_pp_la_SOURCES = umax_pp.c umax_pp.h umax_pp_low.c umax_pp_low.h umax_pp_mid.c umax_pp_mid.h
+libumax_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax_pp
+
+nodist_libsane_umax_pp_la_SOURCES = umax_pp-s.c
+libsane_umax_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax_pp
+libsane_umax_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_umax_pp_la_LIBADD = $(COMMON_LIBS) libumax_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(MATH_LIB)
+EXTRA_DIST += umax_pp.conf.in
+
+libv4l_la_SOURCES = v4l.c v4l.h v4l-frequencies.h
+libv4l_la_CPPFLAGS = $(AM_CPPFLAGS) @LIBV4L_CFLAGS@ -DBACKEND_NAME=v4l
+
+nodist_libsane_v4l_la_SOURCES = v4l-s.c
+libsane_v4l_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=v4l
+libsane_v4l_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_v4l_la_LIBADD = $(COMMON_LIBS) libv4l.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(LIBV4L_LIBS)
+EXTRA_DIST += v4l.conf.in
+
+libxerox_mfp_la_SOURCES = xerox_mfp.c xerox_mfp-usb.c xerox_mfp-tcp.c xerox_mfp.h
+libxerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp
+
+nodist_libsane_xerox_mfp_la_SOURCES = xerox_mfp-s.c
+libsane_xerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp
+libsane_xerox_mfp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_xerox_mfp_la_LIBADD = $(COMMON_LIBS) libxerox_mfp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_tcp.lo $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += xerox_mfp.conf.in
+
+libdll_preload_la_SOURCES = dll.c
+libdll_preload_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll -DENABLE_PRELOAD
+libdll_la_SOURCES = dll.c
+libdll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+BUILT_SOURCES = dll-preload.h
+CLEANFILES += dll-preload.h
+
+nodist_libsane_dll_la_SOURCES = dll-s.c
+libsane_dll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+libsane_dll_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dll_la_LIBADD = $(COMMON_LIBS) libdll.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(DL_LIBS)
+EXTRA_DIST += dll.conf.in
+# TODO: Why is this distributed but not installed?
+EXTRA_DIST += dll.aliases
+
+# libsane.la and libsane-dll.la are the same thing except for
+# the addition of backends listed by PRELOADABLE_BACKENDS that are
+# statically linked in.
+# Also, libsane.la goes into $(libdir) where as all libsane-*
+# (including libsane-dll.la) go into $(libdir)/sane
+
+# FIXME: Since we are throwing in the kitchen sink, might as
+# well link in ../sanei/libsanei.la instead. But currently,
+# libsanei.la is linking in sanei_auth which requires md5.
+# Shipping md5 could cause symbol conflicts with commonly used
+# md5 external libraries. Either need to prefix md5 with sanei_
+# (see liblib.la and snprintf), or move sanei_auth outside
+# of libsanei.
+#
+# FIXME: This is linking in every possible external library because there
+# is the off chance user is using PRELOADABLE_BACKENDS that may need
+# them. Since standard mode is to only have the dll backend, its a waste.
+# Need to update configure to build a list of only what needs to go into
+# LIBADD based on whats being preloaded.
+nodist_libsane_la_SOURCES = dll-s.c
+libsane_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+libsane_la_LDFLAGS = $(DIST_LIBS_LDFLAGS)
+libsane_la_LIBADD = $(COMMON_LIBS) @PRELOADABLE_BACKENDS_ENABLED@ libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo $(DL_LIBS) $(LIBV4L_LIBS) $(MATH_LIB) $(IEEE1284_LIBS) $(TIFF_LIBS) $(JPEG_LIBS) $(GPHOTO2_LIBS) $(SOCKET_LIBS) $(USB_LIBS) $(AVAHI_LIBS) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+
+# WARNING: Automake is getting this wrong so have to do it ourselves.
+libsane_la_DEPENDENCIES = $(COMMON_LIBS) @PRELOADABLE_BACKENDS_ENABLED@ libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo @SANEI_SANEI_JPEG_LO@
diff --git a/backend/Makefile.in b/backend/Makefile.in
new file mode 100644
index 0000000..3f7fa12
--- /dev/null
+++ b/backend/Makefile.in
@@ -0,0 +1,5420 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = backend
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/byteorder.m4 \
+ $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/sane/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(sanelibdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(sanelib_LTLIBRARIES)
+libabaton_la_LIBADD =
+am_libabaton_la_OBJECTS = libabaton_la-abaton.lo
+libabaton_la_OBJECTS = $(am_libabaton_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libagfafocus_la_LIBADD =
+am_libagfafocus_la_OBJECTS = libagfafocus_la-agfafocus.lo
+libagfafocus_la_OBJECTS = $(am_libagfafocus_la_OBJECTS)
+libapple_la_LIBADD =
+am_libapple_la_OBJECTS = libapple_la-apple.lo
+libapple_la_OBJECTS = $(am_libapple_la_OBJECTS)
+libartec_la_LIBADD =
+am_libartec_la_OBJECTS = libartec_la-artec.lo
+libartec_la_OBJECTS = $(am_libartec_la_OBJECTS)
+libartec_eplus48u_la_LIBADD =
+am_libartec_eplus48u_la_OBJECTS = \
+ libartec_eplus48u_la-artec_eplus48u.lo
+libartec_eplus48u_la_OBJECTS = $(am_libartec_eplus48u_la_OBJECTS)
+libas6e_la_LIBADD =
+am_libas6e_la_OBJECTS = libas6e_la-as6e.lo
+libas6e_la_OBJECTS = $(am_libas6e_la_OBJECTS)
+libavision_la_LIBADD =
+am_libavision_la_OBJECTS = libavision_la-avision.lo
+libavision_la_OBJECTS = $(am_libavision_la_OBJECTS)
+libbh_la_LIBADD =
+am_libbh_la_OBJECTS = libbh_la-bh.lo
+libbh_la_OBJECTS = $(am_libbh_la_OBJECTS)
+libcanon_la_LIBADD =
+am_libcanon_la_OBJECTS = libcanon_la-canon.lo
+libcanon_la_OBJECTS = $(am_libcanon_la_OBJECTS)
+libcanon630u_la_LIBADD =
+am_libcanon630u_la_OBJECTS = libcanon630u_la-canon630u.lo
+libcanon630u_la_OBJECTS = $(am_libcanon630u_la_OBJECTS)
+libcanon_dr_la_LIBADD =
+am_libcanon_dr_la_OBJECTS = libcanon_dr_la-canon_dr.lo
+libcanon_dr_la_OBJECTS = $(am_libcanon_dr_la_OBJECTS)
+libcanon_pp_la_LIBADD =
+am_libcanon_pp_la_OBJECTS = libcanon_pp_la-canon_pp.lo \
+ libcanon_pp_la-canon_pp-io.lo libcanon_pp_la-canon_pp-dev.lo
+libcanon_pp_la_OBJECTS = $(am_libcanon_pp_la_OBJECTS)
+libcardscan_la_LIBADD =
+am_libcardscan_la_OBJECTS = libcardscan_la-cardscan.lo
+libcardscan_la_OBJECTS = $(am_libcardscan_la_OBJECTS)
+libcoolscan_la_LIBADD =
+am_libcoolscan_la_OBJECTS = libcoolscan_la-coolscan.lo
+libcoolscan_la_OBJECTS = $(am_libcoolscan_la_OBJECTS)
+libcoolscan2_la_LIBADD =
+am_libcoolscan2_la_OBJECTS = libcoolscan2_la-coolscan2.lo
+libcoolscan2_la_OBJECTS = $(am_libcoolscan2_la_OBJECTS)
+libcoolscan3_la_LIBADD =
+am_libcoolscan3_la_OBJECTS = libcoolscan3_la-coolscan3.lo
+libcoolscan3_la_OBJECTS = $(am_libcoolscan3_la_OBJECTS)
+libdc210_la_LIBADD =
+am_libdc210_la_OBJECTS = libdc210_la-dc210.lo
+libdc210_la_OBJECTS = $(am_libdc210_la_OBJECTS)
+libdc240_la_LIBADD =
+am_libdc240_la_OBJECTS = libdc240_la-dc240.lo
+libdc240_la_OBJECTS = $(am_libdc240_la_OBJECTS)
+libdc25_la_LIBADD =
+am_libdc25_la_OBJECTS = libdc25_la-dc25.lo
+libdc25_la_OBJECTS = $(am_libdc25_la_OBJECTS)
+libdell1600n_net_la_LIBADD =
+am_libdell1600n_net_la_OBJECTS = libdell1600n_net_la-dell1600n_net.lo
+libdell1600n_net_la_OBJECTS = $(am_libdell1600n_net_la_OBJECTS)
+libdll_la_LIBADD =
+am_libdll_la_OBJECTS = libdll_la-dll.lo
+libdll_la_OBJECTS = $(am_libdll_la_OBJECTS)
+libdll_preload_la_LIBADD =
+am_libdll_preload_la_OBJECTS = libdll_preload_la-dll.lo
+libdll_preload_la_OBJECTS = $(am_libdll_preload_la_OBJECTS)
+libdmc_la_LIBADD =
+am_libdmc_la_OBJECTS = libdmc_la-dmc.lo
+libdmc_la_OBJECTS = $(am_libdmc_la_OBJECTS)
+libepjitsu_la_LIBADD =
+am_libepjitsu_la_OBJECTS = libepjitsu_la-epjitsu.lo
+libepjitsu_la_OBJECTS = $(am_libepjitsu_la_OBJECTS)
+libepson_la_LIBADD =
+am_libepson_la_OBJECTS = libepson_la-epson.lo \
+ libepson_la-epson_scsi.lo libepson_la-epson_usb.lo
+libepson_la_OBJECTS = $(am_libepson_la_OBJECTS)
+libepson2_la_LIBADD =
+am_libepson2_la_OBJECTS = libepson2_la-epson2.lo \
+ libepson2_la-epson2_scsi.lo libepson2_la-epson_usb.lo \
+ libepson2_la-epson2_net.lo libepson2_la-epson2-io.lo \
+ libepson2_la-epson2-commands.lo libepson2_la-epson2-ops.lo \
+ libepson2_la-epson2-cct.lo
+libepson2_la_OBJECTS = $(am_libepson2_la_OBJECTS)
+libfujitsu_la_LIBADD =
+am_libfujitsu_la_OBJECTS = libfujitsu_la-fujitsu.lo
+libfujitsu_la_OBJECTS = $(am_libfujitsu_la_OBJECTS)
+libgenesys_la_LIBADD =
+am_libgenesys_la_OBJECTS = libgenesys_la-genesys.lo \
+ libgenesys_la-genesys_gl646.lo libgenesys_la-genesys_gl841.lo \
+ libgenesys_la-genesys_gl843.lo libgenesys_la-genesys_gl846.lo \
+ libgenesys_la-genesys_gl847.lo libgenesys_la-genesys_gl124.lo \
+ libgenesys_la-genesys_low.lo
+libgenesys_la_OBJECTS = $(am_libgenesys_la_OBJECTS)
+libgphoto2_i_la_LIBADD =
+am_libgphoto2_i_la_OBJECTS = libgphoto2_i_la-gphoto2.lo
+libgphoto2_i_la_OBJECTS = $(am_libgphoto2_i_la_OBJECTS)
+libgt68xx_la_LIBADD =
+am_libgt68xx_la_OBJECTS = libgt68xx_la-gt68xx.lo
+libgt68xx_la_OBJECTS = $(am_libgt68xx_la_OBJECTS)
+libhp_la_LIBADD =
+am_libhp_la_OBJECTS = libhp_la-hp.lo libhp_la-hp-accessor.lo \
+ libhp_la-hp-device.lo libhp_la-hp-handle.lo \
+ libhp_la-hp-hpmem.lo libhp_la-hp-option.lo libhp_la-hp-scl.lo
+libhp_la_OBJECTS = $(am_libhp_la_OBJECTS)
+libhp3500_la_LIBADD =
+am_libhp3500_la_OBJECTS = libhp3500_la-hp3500.lo
+libhp3500_la_OBJECTS = $(am_libhp3500_la_OBJECTS)
+libhp3900_la_LIBADD =
+am_libhp3900_la_OBJECTS = libhp3900_la-hp3900.lo
+libhp3900_la_OBJECTS = $(am_libhp3900_la_OBJECTS)
+libhp4200_la_LIBADD =
+am_libhp4200_la_OBJECTS = libhp4200_la-hp4200.lo
+libhp4200_la_OBJECTS = $(am_libhp4200_la_OBJECTS)
+libhp5400_la_LIBADD =
+am_libhp5400_la_OBJECTS = libhp5400_la-hp5400.lo
+libhp5400_la_OBJECTS = $(am_libhp5400_la_OBJECTS)
+libhp5590_la_LIBADD =
+am_libhp5590_la_OBJECTS = libhp5590_la-hp5590.lo
+libhp5590_la_OBJECTS = $(am_libhp5590_la_OBJECTS)
+libhpljm1005_la_LIBADD =
+am_libhpljm1005_la_OBJECTS = libhpljm1005_la-hpljm1005.lo
+libhpljm1005_la_OBJECTS = $(am_libhpljm1005_la_OBJECTS)
+libhpsj5s_la_LIBADD =
+am_libhpsj5s_la_OBJECTS = libhpsj5s_la-hpsj5s.lo
+libhpsj5s_la_OBJECTS = $(am_libhpsj5s_la_OBJECTS)
+libhs2p_la_LIBADD =
+am_libhs2p_la_OBJECTS = libhs2p_la-hs2p.lo
+libhs2p_la_OBJECTS = $(am_libhs2p_la_OBJECTS)
+libibm_la_LIBADD =
+am_libibm_la_OBJECTS = libibm_la-ibm.lo
+libibm_la_OBJECTS = $(am_libibm_la_OBJECTS)
+libkodak_la_LIBADD =
+am_libkodak_la_OBJECTS = libkodak_la-kodak.lo
+libkodak_la_OBJECTS = $(am_libkodak_la_OBJECTS)
+libkodakaio_la_LIBADD =
+am_libkodakaio_la_OBJECTS = libkodakaio_la-kodakaio.lo
+libkodakaio_la_OBJECTS = $(am_libkodakaio_la_OBJECTS)
+libkvs1025_la_LIBADD =
+am_libkvs1025_la_OBJECTS = libkvs1025_la-kvs1025.lo \
+ libkvs1025_la-kvs1025_low.lo libkvs1025_la-kvs1025_opt.lo \
+ libkvs1025_la-kvs1025_usb.lo
+libkvs1025_la_OBJECTS = $(am_libkvs1025_la_OBJECTS)
+libkvs20xx_la_LIBADD =
+am_libkvs20xx_la_OBJECTS = libkvs20xx_la-kvs20xx.lo \
+ libkvs20xx_la-kvs20xx_cmd.lo libkvs20xx_la-kvs20xx_opt.lo
+libkvs20xx_la_OBJECTS = $(am_libkvs20xx_la_OBJECTS)
+libkvs40xx_la_LIBADD =
+am_libkvs40xx_la_OBJECTS = libkvs40xx_la-kvs40xx.lo \
+ libkvs40xx_la-kvs40xx_cmd.lo libkvs40xx_la-kvs40xx_opt.lo
+libkvs40xx_la_OBJECTS = $(am_libkvs40xx_la_OBJECTS)
+libleo_la_LIBADD =
+am_libleo_la_OBJECTS = libleo_la-leo.lo
+libleo_la_OBJECTS = $(am_libleo_la_OBJECTS)
+liblexmark_la_LIBADD =
+am_liblexmark_la_OBJECTS = liblexmark_la-lexmark.lo \
+ liblexmark_la-lexmark_low.lo
+liblexmark_la_OBJECTS = $(am_liblexmark_la_OBJECTS)
+libma1509_la_LIBADD =
+am_libma1509_la_OBJECTS = libma1509_la-ma1509.lo
+libma1509_la_OBJECTS = $(am_libma1509_la_OBJECTS)
+libmagicolor_la_LIBADD =
+am_libmagicolor_la_OBJECTS = libmagicolor_la-magicolor.lo
+libmagicolor_la_OBJECTS = $(am_libmagicolor_la_OBJECTS)
+libmatsushita_la_LIBADD =
+am_libmatsushita_la_OBJECTS = libmatsushita_la-matsushita.lo
+libmatsushita_la_OBJECTS = $(am_libmatsushita_la_OBJECTS)
+libmicrotek_la_LIBADD =
+am_libmicrotek_la_OBJECTS = libmicrotek_la-microtek.lo
+libmicrotek_la_OBJECTS = $(am_libmicrotek_la_OBJECTS)
+libmicrotek2_la_LIBADD =
+am_libmicrotek2_la_OBJECTS = libmicrotek2_la-microtek2.lo
+libmicrotek2_la_OBJECTS = $(am_libmicrotek2_la_OBJECTS)
+libmustek_la_LIBADD =
+am_libmustek_la_OBJECTS = libmustek_la-mustek.lo
+libmustek_la_OBJECTS = $(am_libmustek_la_OBJECTS)
+libmustek_pp_la_LIBADD =
+am_libmustek_pp_la_OBJECTS = libmustek_pp_la-mustek_pp.lo
+libmustek_pp_la_OBJECTS = $(am_libmustek_pp_la_OBJECTS)
+libmustek_usb_la_LIBADD =
+am_libmustek_usb_la_OBJECTS = libmustek_usb_la-mustek_usb.lo
+libmustek_usb_la_OBJECTS = $(am_libmustek_usb_la_OBJECTS)
+libmustek_usb2_la_LIBADD =
+am_libmustek_usb2_la_OBJECTS = libmustek_usb2_la-mustek_usb2.lo
+libmustek_usb2_la_OBJECTS = $(am_libmustek_usb2_la_OBJECTS)
+libnec_la_LIBADD =
+am_libnec_la_OBJECTS = libnec_la-nec.lo
+libnec_la_OBJECTS = $(am_libnec_la_OBJECTS)
+libnet_la_LIBADD =
+am_libnet_la_OBJECTS = libnet_la-net.lo
+libnet_la_OBJECTS = $(am_libnet_la_OBJECTS)
+libniash_la_LIBADD =
+am_libniash_la_OBJECTS = libniash_la-niash.lo
+libniash_la_OBJECTS = $(am_libniash_la_OBJECTS)
+libp5_la_LIBADD =
+am_libp5_la_OBJECTS = libp5_la-p5.lo
+libp5_la_OBJECTS = $(am_libp5_la_OBJECTS)
+libpie_la_LIBADD =
+am_libpie_la_OBJECTS = libpie_la-pie.lo
+libpie_la_OBJECTS = $(am_libpie_la_OBJECTS)
+libpint_la_LIBADD =
+am_libpint_la_OBJECTS = libpint_la-pint.lo
+libpint_la_OBJECTS = $(am_libpint_la_OBJECTS)
+libpixma_la_LIBADD =
+am_libpixma_la_OBJECTS = libpixma_la-pixma.lo \
+ libpixma_la-pixma_io_sanei.lo libpixma_la-pixma_common.lo \
+ libpixma_la-pixma_mp150.lo libpixma_la-pixma_mp730.lo \
+ libpixma_la-pixma_mp750.lo libpixma_la-pixma_mp810.lo \
+ libpixma_la-pixma_imageclass.lo libpixma_la-pixma_bjnp.lo
+libpixma_la_OBJECTS = $(am_libpixma_la_OBJECTS)
+libplustek_la_LIBADD =
+am_libplustek_la_OBJECTS = libplustek_la-plustek.lo
+libplustek_la_OBJECTS = $(am_libplustek_la_OBJECTS)
+libplustek_pp_la_LIBADD =
+am_libplustek_pp_la_OBJECTS = libplustek_pp_la-plustek_pp.lo
+libplustek_pp_la_OBJECTS = $(am_libplustek_pp_la_OBJECTS)
+libpnm_la_LIBADD =
+am_libpnm_la_OBJECTS = libpnm_la-pnm.lo
+libpnm_la_OBJECTS = $(am_libpnm_la_OBJECTS)
+libqcam_la_LIBADD =
+am_libqcam_la_OBJECTS = libqcam_la-qcam.lo
+libqcam_la_OBJECTS = $(am_libqcam_la_OBJECTS)
+libricoh_la_LIBADD =
+am_libricoh_la_OBJECTS = libricoh_la-ricoh.lo
+libricoh_la_OBJECTS = $(am_libricoh_la_OBJECTS)
+librts8891_la_LIBADD =
+am_librts8891_la_OBJECTS = librts8891_la-rts8891.lo \
+ librts8891_la-rts88xx_lib.lo
+librts8891_la_OBJECTS = $(am_librts8891_la_OBJECTS)
+libs9036_la_LIBADD =
+am_libs9036_la_OBJECTS = libs9036_la-s9036.lo
+libs9036_la_OBJECTS = $(am_libs9036_la_OBJECTS)
+am__DEPENDENCIES_1 =
+libsane_abaton_la_DEPENDENCIES = $(COMMON_LIBS) libabaton.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_abaton_la_OBJECTS = libsane_abaton_la-abaton-s.lo
+libsane_abaton_la_OBJECTS = $(nodist_libsane_abaton_la_OBJECTS)
+libsane_abaton_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_abaton_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_agfafocus_la_DEPENDENCIES = $(COMMON_LIBS) libagfafocus.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_thread.lo \
+ ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_agfafocus_la_OBJECTS = \
+ libsane_agfafocus_la-agfafocus-s.lo
+libsane_agfafocus_la_OBJECTS = $(nodist_libsane_agfafocus_la_OBJECTS)
+libsane_agfafocus_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_agfafocus_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_apple_la_DEPENDENCIES = $(COMMON_LIBS) libapple.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_apple_la_OBJECTS = libsane_apple_la-apple-s.lo
+libsane_apple_la_OBJECTS = $(nodist_libsane_apple_la_OBJECTS)
+libsane_apple_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_apple_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_artec_la_DEPENDENCIES = $(COMMON_LIBS) libartec.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_artec_la_OBJECTS = libsane_artec_la-artec-s.lo
+libsane_artec_la_OBJECTS = $(nodist_libsane_artec_la_OBJECTS)
+libsane_artec_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_artec_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_artec_eplus48u_la_DEPENDENCIES = $(COMMON_LIBS) \
+ libartec_eplus48u.la ../sanei/sanei_init_debug.lo \
+ ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo \
+ ../sanei/sanei_thread.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_artec_eplus48u_la_OBJECTS = \
+ libsane_artec_eplus48u_la-artec_eplus48u-s.lo
+libsane_artec_eplus48u_la_OBJECTS = \
+ $(nodist_libsane_artec_eplus48u_la_OBJECTS)
+libsane_artec_eplus48u_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_artec_eplus48u_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_as6e_la_DEPENDENCIES = $(COMMON_LIBS) libas6e.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo
+nodist_libsane_as6e_la_OBJECTS = libsane_as6e_la-as6e-s.lo
+libsane_as6e_la_OBJECTS = $(nodist_libsane_as6e_la_OBJECTS)
+libsane_as6e_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_as6e_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_avision_la_DEPENDENCIES = $(COMMON_LIBS) libavision.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo \
+ ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_avision_la_OBJECTS = libsane_avision_la-avision-s.lo
+libsane_avision_la_OBJECTS = $(nodist_libsane_avision_la_OBJECTS)
+libsane_avision_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_avision_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_bh_la_DEPENDENCIES = $(COMMON_LIBS) libbh.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_bh_la_OBJECTS = libsane_bh_la-bh-s.lo
+libsane_bh_la_OBJECTS = $(nodist_libsane_bh_la_OBJECTS)
+libsane_bh_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libsane_bh_la_LDFLAGS) $(LDFLAGS) -o $@
+libsane_canon_la_DEPENDENCIES = $(COMMON_LIBS) libcanon.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_canon_la_OBJECTS = libsane_canon_la-canon-s.lo
+libsane_canon_la_OBJECTS = $(nodist_libsane_canon_la_OBJECTS)
+libsane_canon_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_canon_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_canon630u_la_DEPENDENCIES = $(COMMON_LIBS) libcanon630u.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_canon630u_la_OBJECTS = \
+ libsane_canon630u_la-canon630u-s.lo
+libsane_canon630u_la_OBJECTS = $(nodist_libsane_canon630u_la_OBJECTS)
+libsane_canon630u_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_canon630u_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_canon_dr_la_DEPENDENCIES = $(COMMON_LIBS) libcanon_dr.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_canon_dr_la_OBJECTS = \
+ libsane_canon_dr_la-canon_dr-s.lo
+libsane_canon_dr_la_OBJECTS = $(nodist_libsane_canon_dr_la_OBJECTS)
+libsane_canon_dr_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_canon_dr_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_canon_pp_la_DEPENDENCIES = $(COMMON_LIBS) libcanon_pp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_canon_pp_la_OBJECTS = \
+ libsane_canon_pp_la-canon_pp-s.lo
+libsane_canon_pp_la_OBJECTS = $(nodist_libsane_canon_pp_la_OBJECTS)
+libsane_canon_pp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_canon_pp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_cardscan_la_DEPENDENCIES = $(COMMON_LIBS) libcardscan.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_cardscan_la_OBJECTS = \
+ libsane_cardscan_la-cardscan-s.lo
+libsane_cardscan_la_OBJECTS = $(nodist_libsane_cardscan_la_OBJECTS)
+libsane_cardscan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_cardscan_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_coolscan_la_DEPENDENCIES = $(COMMON_LIBS) libcoolscan.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_thread.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_coolscan_la_OBJECTS = \
+ libsane_coolscan_la-coolscan-s.lo
+libsane_coolscan_la_OBJECTS = $(nodist_libsane_coolscan_la_OBJECTS)
+libsane_coolscan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_coolscan_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_coolscan2_la_DEPENDENCIES = $(COMMON_LIBS) libcoolscan2.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_coolscan2_la_OBJECTS = \
+ libsane_coolscan2_la-coolscan2-s.lo
+libsane_coolscan2_la_OBJECTS = $(nodist_libsane_coolscan2_la_OBJECTS)
+libsane_coolscan2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_coolscan2_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_coolscan3_la_DEPENDENCIES = $(COMMON_LIBS) libcoolscan3.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_coolscan3_la_OBJECTS = \
+ libsane_coolscan3_la-coolscan3-s.lo
+libsane_coolscan3_la_OBJECTS = $(nodist_libsane_coolscan3_la_OBJECTS)
+libsane_coolscan3_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_coolscan3_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_dc210_la_DEPENDENCIES = $(COMMON_LIBS) libdc210.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_dc210_la_OBJECTS = libsane_dc210_la-dc210-s.lo
+libsane_dc210_la_OBJECTS = $(nodist_libsane_dc210_la_OBJECTS)
+libsane_dc210_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_dc210_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_dc240_la_DEPENDENCIES = $(COMMON_LIBS) libdc240.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_dc240_la_OBJECTS = libsane_dc240_la-dc240-s.lo
+libsane_dc240_la_OBJECTS = $(nodist_libsane_dc240_la_OBJECTS)
+libsane_dc240_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_dc240_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_dc25_la_DEPENDENCIES = $(COMMON_LIBS) libdc25.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_dc25_la_OBJECTS = libsane_dc25_la-dc25-s.lo
+libsane_dc25_la_OBJECTS = $(nodist_libsane_dc25_la_OBJECTS)
+libsane_dc25_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_dc25_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_dell1600n_net_la_DEPENDENCIES = $(COMMON_LIBS) \
+ libdell1600n_net.la ../sanei/sanei_init_debug.lo \
+ ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo \
+ sane_strstatus.lo $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_dell1600n_net_la_OBJECTS = \
+ libsane_dell1600n_net_la-dell1600n_net-s.lo
+libsane_dell1600n_net_la_OBJECTS = \
+ $(nodist_libsane_dell1600n_net_la_OBJECTS)
+libsane_dell1600n_net_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_dell1600n_net_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_dll_la_DEPENDENCIES = $(COMMON_LIBS) libdll.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_dll_la_OBJECTS = libsane_dll_la-dll-s.lo
+libsane_dll_la_OBJECTS = $(nodist_libsane_dll_la_OBJECTS)
+libsane_dll_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_dll_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_dmc_la_DEPENDENCIES = $(COMMON_LIBS) libdmc.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_dmc_la_OBJECTS = libsane_dmc_la-dmc-s.lo
+libsane_dmc_la_OBJECTS = $(nodist_libsane_dmc_la_OBJECTS)
+libsane_dmc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_dmc_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_epjitsu_la_DEPENDENCIES = $(COMMON_LIBS) libepjitsu.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_epjitsu_la_OBJECTS = libsane_epjitsu_la-epjitsu-s.lo
+libsane_epjitsu_la_OBJECTS = $(nodist_libsane_epjitsu_la_OBJECTS)
+libsane_epjitsu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_epjitsu_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_epson_la_DEPENDENCIES = $(COMMON_LIBS) libepson.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_pio.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_epson_la_OBJECTS = libsane_epson_la-epson-s.lo
+libsane_epson_la_OBJECTS = $(nodist_libsane_epson_la_OBJECTS)
+libsane_epson_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_epson_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_epson2_la_DEPENDENCIES = $(COMMON_LIBS) libepson2.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo \
+ ../sanei/sanei_udp.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_epson2_la_OBJECTS = libsane_epson2_la-epson2-s.lo
+libsane_epson2_la_OBJECTS = $(nodist_libsane_epson2_la_OBJECTS)
+libsane_epson2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_epson2_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_fujitsu_la_DEPENDENCIES = $(COMMON_LIBS) libfujitsu.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_magic.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_fujitsu_la_OBJECTS = libsane_fujitsu_la-fujitsu-s.lo
+libsane_fujitsu_la_OBJECTS = $(nodist_libsane_fujitsu_la_OBJECTS)
+libsane_fujitsu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_fujitsu_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_genesys_la_DEPENDENCIES = $(COMMON_LIBS) libgenesys.la \
+ ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo \
+ ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_genesys_la_OBJECTS = libsane_genesys_la-genesys-s.lo
+libsane_genesys_la_OBJECTS = $(nodist_libsane_genesys_la_OBJECTS)
+libsane_genesys_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_genesys_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_gphoto2_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(COMMON_LIBS) \
+ libgphoto2_i.la ../sanei/sanei_init_debug.lo \
+ ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo \
+ sane_strstatus.lo $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_gphoto2_la_OBJECTS = libsane_gphoto2_la-gphoto2-s.lo
+libsane_gphoto2_la_OBJECTS = $(nodist_libsane_gphoto2_la_OBJECTS)
+libsane_gphoto2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_gphoto2_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_gt68xx_la_DEPENDENCIES = $(COMMON_LIBS) libgt68xx.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_gt68xx_la_OBJECTS = libsane_gt68xx_la-gt68xx-s.lo
+libsane_gt68xx_la_OBJECTS = $(nodist_libsane_gt68xx_la_OBJECTS)
+libsane_gt68xx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_gt68xx_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hp_la_DEPENDENCIES = $(COMMON_LIBS) libhp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_pio.lo ../sanei/sanei_thread.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_hp_la_OBJECTS = libsane_hp_la-hp-s.lo
+libsane_hp_la_OBJECTS = $(nodist_libsane_hp_la_OBJECTS)
+libsane_hp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libsane_hp_la_LDFLAGS) $(LDFLAGS) -o $@
+libsane_hp3500_la_DEPENDENCIES = $(COMMON_LIBS) libhp3500.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hp3500_la_OBJECTS = libsane_hp3500_la-hp3500-s.lo
+libsane_hp3500_la_OBJECTS = $(nodist_libsane_hp3500_la_OBJECTS)
+libsane_hp3500_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hp3500_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hp3900_la_DEPENDENCIES = $(COMMON_LIBS) libhp3900.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hp3900_la_OBJECTS = libsane_hp3900_la-hp3900-s.lo
+libsane_hp3900_la_OBJECTS = $(nodist_libsane_hp3900_la_OBJECTS)
+libsane_hp3900_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hp3900_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hp4200_la_DEPENDENCIES = $(COMMON_LIBS) libhp4200.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_pv8630.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hp4200_la_OBJECTS = libsane_hp4200_la-hp4200-s.lo
+libsane_hp4200_la_OBJECTS = $(nodist_libsane_hp4200_la_OBJECTS)
+libsane_hp4200_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hp4200_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hp5400_la_DEPENDENCIES = $(COMMON_LIBS) libhp5400.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hp5400_la_OBJECTS = libsane_hp5400_la-hp5400-s.lo
+libsane_hp5400_la_OBJECTS = $(nodist_libsane_hp5400_la_OBJECTS)
+libsane_hp5400_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hp5400_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hp5590_la_DEPENDENCIES = $(COMMON_LIBS) libhp5590.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hp5590_la_OBJECTS = libsane_hp5590_la-hp5590-s.lo
+libsane_hp5590_la_OBJECTS = $(nodist_libsane_hp5590_la_OBJECTS)
+libsane_hp5590_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hp5590_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hpljm1005_la_DEPENDENCIES = $(COMMON_LIBS) libhpljm1005.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_hpljm1005_la_OBJECTS = \
+ libsane_hpljm1005_la-hpljm1005-s.lo
+libsane_hpljm1005_la_OBJECTS = $(nodist_libsane_hpljm1005_la_OBJECTS)
+libsane_hpljm1005_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hpljm1005_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_hpsj5s_la_DEPENDENCIES = $(COMMON_LIBS) libhpsj5s.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hpsj5s_la_OBJECTS = libsane_hpsj5s_la-hpsj5s-s.lo
+libsane_hpsj5s_la_OBJECTS = $(nodist_libsane_hpsj5s_la_OBJECTS)
+libsane_hpsj5s_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hpsj5s_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_hs2p_la_DEPENDENCIES = $(COMMON_LIBS) libhs2p.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_hs2p_la_OBJECTS = libsane_hs2p_la-hs2p-s.lo
+libsane_hs2p_la_OBJECTS = $(nodist_libsane_hs2p_la_OBJECTS)
+libsane_hs2p_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_hs2p_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_ibm_la_DEPENDENCIES = $(COMMON_LIBS) libibm.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_ibm_la_OBJECTS = libsane_ibm_la-ibm-s.lo
+libsane_ibm_la_OBJECTS = $(nodist_libsane_ibm_la_OBJECTS)
+libsane_ibm_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_ibm_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_kodak_la_DEPENDENCIES = $(COMMON_LIBS) libkodak.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_kodak_la_OBJECTS = libsane_kodak_la-kodak-s.lo
+libsane_kodak_la_OBJECTS = $(nodist_libsane_kodak_la_OBJECTS)
+libsane_kodak_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_kodak_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_kodakaio_la_DEPENDENCIES = $(COMMON_LIBS) libkodakaio.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_kodakaio_la_OBJECTS = \
+ libsane_kodakaio_la-kodakaio-s.lo
+libsane_kodakaio_la_OBJECTS = $(nodist_libsane_kodakaio_la_OBJECTS)
+libsane_kodakaio_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_kodakaio_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_kvs1025_la_DEPENDENCIES = $(COMMON_LIBS) libkvs1025.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_magic.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_kvs1025_la_OBJECTS = libsane_kvs1025_la-kvs1025-s.lo
+libsane_kvs1025_la_OBJECTS = $(nodist_libsane_kvs1025_la_OBJECTS)
+libsane_kvs1025_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_kvs1025_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_kvs20xx_la_DEPENDENCIES = $(COMMON_LIBS) libkvs20xx.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_kvs20xx_la_OBJECTS = libsane_kvs20xx_la-kvs20xx-s.lo
+libsane_kvs20xx_la_OBJECTS = $(nodist_libsane_kvs20xx_la_OBJECTS)
+libsane_kvs20xx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_kvs20xx_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_kvs40xx_la_DEPENDENCIES = $(COMMON_LIBS) libkvs40xx.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_kvs40xx_la_OBJECTS = libsane_kvs40xx_la-kvs40xx-s.lo
+libsane_kvs40xx_la_OBJECTS = $(nodist_libsane_kvs40xx_la_OBJECTS)
+libsane_kvs40xx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_kvs40xx_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_leo_la_DEPENDENCIES = $(COMMON_LIBS) libleo.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_leo_la_OBJECTS = libsane_leo_la-leo-s.lo
+libsane_leo_la_OBJECTS = $(nodist_libsane_leo_la_OBJECTS)
+libsane_leo_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_leo_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_lexmark_la_DEPENDENCIES = $(COMMON_LIBS) liblexmark.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_lexmark_la_OBJECTS = libsane_lexmark_la-lexmark-s.lo
+libsane_lexmark_la_OBJECTS = $(nodist_libsane_lexmark_la_OBJECTS)
+libsane_lexmark_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_lexmark_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_ma1509_la_DEPENDENCIES = $(COMMON_LIBS) libma1509.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_ma1509_la_OBJECTS = libsane_ma1509_la-ma1509-s.lo
+libsane_ma1509_la_OBJECTS = $(nodist_libsane_ma1509_la_OBJECTS)
+libsane_ma1509_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_ma1509_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_magicolor_la_DEPENDENCIES = $(COMMON_LIBS) libmagicolor.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_magicolor_la_OBJECTS = \
+ libsane_magicolor_la-magicolor-s.lo
+libsane_magicolor_la_OBJECTS = $(nodist_libsane_magicolor_la_OBJECTS)
+libsane_magicolor_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_magicolor_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_matsushita_la_DEPENDENCIES = $(COMMON_LIBS) libmatsushita.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_matsushita_la_OBJECTS = \
+ libsane_matsushita_la-matsushita-s.lo
+libsane_matsushita_la_OBJECTS = \
+ $(nodist_libsane_matsushita_la_OBJECTS)
+libsane_matsushita_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_matsushita_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_microtek_la_DEPENDENCIES = $(COMMON_LIBS) libmicrotek.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_microtek_la_OBJECTS = \
+ libsane_microtek_la-microtek-s.lo
+libsane_microtek_la_OBJECTS = $(nodist_libsane_microtek_la_OBJECTS)
+libsane_microtek_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_microtek_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_microtek2_la_DEPENDENCIES = $(COMMON_LIBS) libmicrotek2.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_thread.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_microtek2_la_OBJECTS = \
+ libsane_microtek2_la-microtek2-s.lo
+libsane_microtek2_la_OBJECTS = $(nodist_libsane_microtek2_la_OBJECTS)
+libsane_microtek2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_microtek2_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_mustek_la_DEPENDENCIES = $(COMMON_LIBS) libmustek.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_thread.lo ../sanei/sanei_ab306.lo \
+ ../sanei/sanei_pa4s2.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_mustek_la_OBJECTS = libsane_mustek_la-mustek-s.lo
+libsane_mustek_la_OBJECTS = $(nodist_libsane_mustek_la_OBJECTS)
+libsane_mustek_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_mustek_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_mustek_pp_la_DEPENDENCIES = $(COMMON_LIBS) libmustek_pp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_pa4s2.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_mustek_pp_la_OBJECTS = \
+ libsane_mustek_pp_la-mustek_pp-s.lo
+libsane_mustek_pp_la_OBJECTS = $(nodist_libsane_mustek_pp_la_OBJECTS)
+libsane_mustek_pp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_mustek_pp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_mustek_usb_la_DEPENDENCIES = $(COMMON_LIBS) libmustek_usb.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_mustek_usb_la_OBJECTS = \
+ libsane_mustek_usb_la-mustek_usb-s.lo
+libsane_mustek_usb_la_OBJECTS = \
+ $(nodist_libsane_mustek_usb_la_OBJECTS)
+libsane_mustek_usb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_mustek_usb_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_mustek_usb2_la_DEPENDENCIES = $(COMMON_LIBS) libmustek_usb2.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_mustek_usb2_la_OBJECTS = \
+ libsane_mustek_usb2_la-mustek_usb2-s.lo
+libsane_mustek_usb2_la_OBJECTS = \
+ $(nodist_libsane_mustek_usb2_la_OBJECTS)
+libsane_mustek_usb2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_mustek_usb2_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_nec_la_DEPENDENCIES = $(COMMON_LIBS) libnec.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_nec_la_OBJECTS = libsane_nec_la-nec-s.lo
+libsane_nec_la_OBJECTS = $(nodist_libsane_nec_la_OBJECTS)
+libsane_nec_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_nec_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_net_la_DEPENDENCIES = $(COMMON_LIBS) libnet.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_net.lo ../sanei/sanei_wire.lo \
+ ../sanei/sanei_codec_bin.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_net_la_OBJECTS = libsane_net_la-net-s.lo
+libsane_net_la_OBJECTS = $(nodist_libsane_net_la_OBJECTS)
+libsane_net_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_net_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_niash_la_DEPENDENCIES = $(COMMON_LIBS) libniash.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_niash_la_OBJECTS = libsane_niash_la-niash-s.lo
+libsane_niash_la_OBJECTS = $(nodist_libsane_niash_la_OBJECTS)
+libsane_niash_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_niash_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_p5_la_DEPENDENCIES = $(COMMON_LIBS) libp5.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo
+nodist_libsane_p5_la_OBJECTS = libsane_p5_la-p5-s.lo
+libsane_p5_la_OBJECTS = $(nodist_libsane_p5_la_OBJECTS)
+libsane_p5_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libsane_p5_la_LDFLAGS) $(LDFLAGS) -o $@
+libsane_pie_la_DEPENDENCIES = $(COMMON_LIBS) libpie.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_thread.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_pie_la_OBJECTS = libsane_pie_la-pie-s.lo
+libsane_pie_la_OBJECTS = $(nodist_libsane_pie_la_OBJECTS)
+libsane_pie_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_pie_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_pint_la_DEPENDENCIES = $(COMMON_LIBS) libpint.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo
+nodist_libsane_pint_la_OBJECTS = libsane_pint_la-pint-s.lo
+libsane_pint_la_OBJECTS = $(nodist_libsane_pint_la_OBJECTS)
+libsane_pint_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_pint_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_pixma_la_DEPENDENCIES = $(COMMON_LIBS) libpixma.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_pixma_la_OBJECTS = libsane_pixma_la-pixma-s.lo
+libsane_pixma_la_OBJECTS = $(nodist_libsane_pixma_la_OBJECTS)
+libsane_pixma_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_pixma_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_plustek_la_DEPENDENCIES = $(COMMON_LIBS) libplustek.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo \
+ ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_plustek_la_OBJECTS = libsane_plustek_la-plustek-s.lo
+libsane_plustek_la_OBJECTS = $(nodist_libsane_plustek_la_OBJECTS)
+libsane_plustek_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_plustek_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_plustek_pp_la_DEPENDENCIES = $(COMMON_LIBS) libplustek_pp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_plustek_pp_la_OBJECTS = \
+ libsane_plustek_pp_la-plustek_pp-s.lo
+libsane_plustek_pp_la_OBJECTS = \
+ $(nodist_libsane_plustek_pp_la_OBJECTS)
+libsane_plustek_pp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_plustek_pp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_pnm_la_DEPENDENCIES = $(COMMON_LIBS) libpnm.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo
+nodist_libsane_pnm_la_OBJECTS = libsane_pnm_la-pnm-s.lo
+libsane_pnm_la_OBJECTS = $(nodist_libsane_pnm_la_OBJECTS)
+libsane_pnm_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_pnm_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_qcam_la_DEPENDENCIES = $(COMMON_LIBS) libqcam.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_pio.lo
+nodist_libsane_qcam_la_OBJECTS = libsane_qcam_la-qcam-s.lo
+libsane_qcam_la_OBJECTS = $(nodist_libsane_qcam_la_OBJECTS)
+libsane_qcam_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_qcam_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_ricoh_la_DEPENDENCIES = $(COMMON_LIBS) libricoh.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_ricoh_la_OBJECTS = libsane_ricoh_la-ricoh-s.lo
+libsane_ricoh_la_OBJECTS = $(nodist_libsane_ricoh_la_OBJECTS)
+libsane_ricoh_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_ricoh_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_rts8891_la_DEPENDENCIES = $(COMMON_LIBS) librts8891.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_scsi.lo ../sanei/sanei_usb.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_rts8891_la_OBJECTS = libsane_rts8891_la-rts8891-s.lo
+libsane_rts8891_la_OBJECTS = $(nodist_libsane_rts8891_la_OBJECTS)
+libsane_rts8891_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_rts8891_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_s9036_la_DEPENDENCIES = $(COMMON_LIBS) libs9036.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_s9036_la_OBJECTS = libsane_s9036_la-s9036-s.lo
+libsane_s9036_la_OBJECTS = $(nodist_libsane_s9036_la_OBJECTS)
+libsane_s9036_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_s9036_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_sceptre_la_DEPENDENCIES = $(COMMON_LIBS) libsceptre.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_sceptre_la_OBJECTS = libsane_sceptre_la-sceptre-s.lo
+libsane_sceptre_la_OBJECTS = $(nodist_libsane_sceptre_la_OBJECTS)
+libsane_sceptre_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_sceptre_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_sharp_la_DEPENDENCIES = $(COMMON_LIBS) libsharp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_sharp_la_OBJECTS = libsane_sharp_la-sharp-s.lo
+libsane_sharp_la_OBJECTS = $(nodist_libsane_sharp_la_OBJECTS)
+libsane_sharp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_sharp_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_sm3600_la_DEPENDENCIES = $(COMMON_LIBS) libsm3600.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_sm3600_la_OBJECTS = libsane_sm3600_la-sm3600-s.lo
+libsane_sm3600_la_OBJECTS = $(nodist_libsane_sm3600_la_OBJECTS)
+libsane_sm3600_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_sm3600_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_sm3840_la_DEPENDENCIES = $(COMMON_LIBS) libsm3840.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_sm3840_la_OBJECTS = libsane_sm3840_la-sm3840-s.lo
+libsane_sm3840_la_OBJECTS = $(nodist_libsane_sm3840_la_OBJECTS)
+libsane_sm3840_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_sm3840_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_snapscan_la_DEPENDENCIES = $(COMMON_LIBS) libsnapscan.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo \
+ ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_snapscan_la_OBJECTS = \
+ libsane_snapscan_la-snapscan-s.lo
+libsane_snapscan_la_OBJECTS = $(nodist_libsane_snapscan_la_OBJECTS)
+libsane_snapscan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_snapscan_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_sp15c_la_DEPENDENCIES = $(COMMON_LIBS) libsp15c.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_thread.lo \
+ ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_sp15c_la_OBJECTS = libsane_sp15c_la-sp15c-s.lo
+libsane_sp15c_la_OBJECTS = $(nodist_libsane_sp15c_la_OBJECTS)
+libsane_sp15c_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_sp15c_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_st400_la_DEPENDENCIES = $(COMMON_LIBS) libst400.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_st400_la_OBJECTS = libsane_st400_la-st400-s.lo
+libsane_st400_la_OBJECTS = $(nodist_libsane_st400_la_OBJECTS)
+libsane_st400_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_st400_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_stv680_la_DEPENDENCIES = $(COMMON_LIBS) libstv680.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_stv680_la_OBJECTS = libsane_stv680_la-stv680-s.lo
+libsane_stv680_la_OBJECTS = $(nodist_libsane_stv680_la_OBJECTS)
+libsane_stv680_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_stv680_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_tamarack_la_DEPENDENCIES = $(COMMON_LIBS) libtamarack.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_thread.lo \
+ ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_tamarack_la_OBJECTS = \
+ libsane_tamarack_la-tamarack-s.lo
+libsane_tamarack_la_OBJECTS = $(nodist_libsane_tamarack_la_OBJECTS)
+libsane_tamarack_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_tamarack_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_teco1_la_DEPENDENCIES = $(COMMON_LIBS) libteco1.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_teco1_la_OBJECTS = libsane_teco1_la-teco1-s.lo
+libsane_teco1_la_OBJECTS = $(nodist_libsane_teco1_la_OBJECTS)
+libsane_teco1_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_teco1_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_teco2_la_DEPENDENCIES = $(COMMON_LIBS) libteco2.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_teco2_la_OBJECTS = libsane_teco2_la-teco2-s.lo
+libsane_teco2_la_OBJECTS = $(nodist_libsane_teco2_la_OBJECTS)
+libsane_teco2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_teco2_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_teco3_la_DEPENDENCIES = $(COMMON_LIBS) libteco3.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_scsi.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_teco3_la_OBJECTS = libsane_teco3_la-teco3-s.lo
+libsane_teco3_la_OBJECTS = $(nodist_libsane_teco3_la_OBJECTS)
+libsane_teco3_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_teco3_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_test_la_DEPENDENCIES = $(COMMON_LIBS) libtest.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_thread.lo $(am__DEPENDENCIES_1)
+nodist_libsane_test_la_OBJECTS = libsane_test_la-test-s.lo
+libsane_test_la_OBJECTS = $(nodist_libsane_test_la_OBJECTS)
+libsane_test_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_test_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_u12_la_DEPENDENCIES = $(COMMON_LIBS) libu12.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_u12_la_OBJECTS = libsane_u12_la-u12-s.lo
+libsane_u12_la_OBJECTS = $(nodist_libsane_u12_la_OBJECTS)
+libsane_u12_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_u12_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_umax_la_DEPENDENCIES = $(COMMON_LIBS) libumax.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo \
+ sane_strstatus.lo ../sanei/sanei_usb.lo \
+ ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo \
+ ../sanei/sanei_pv8630.lo $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_umax_la_OBJECTS = libsane_umax_la-umax-s.lo
+libsane_umax_la_OBJECTS = $(nodist_libsane_umax_la_OBJECTS)
+libsane_umax_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_umax_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libsane_umax1220u_la_DEPENDENCIES = $(COMMON_LIBS) libumax1220u.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_pv8630.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_umax1220u_la_OBJECTS = \
+ libsane_umax1220u_la-umax1220u-s.lo
+libsane_umax1220u_la_OBJECTS = $(nodist_libsane_umax1220u_la_OBJECTS)
+libsane_umax1220u_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_umax1220u_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_umax_pp_la_DEPENDENCIES = $(COMMON_LIBS) libumax_pp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_umax_pp_la_OBJECTS = libsane_umax_pp_la-umax_pp-s.lo
+libsane_umax_pp_la_OBJECTS = $(nodist_libsane_umax_pp_la_OBJECTS)
+libsane_umax_pp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_umax_pp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libsane_v4l_la_DEPENDENCIES = $(COMMON_LIBS) libv4l.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ $(am__DEPENDENCIES_1)
+nodist_libsane_v4l_la_OBJECTS = libsane_v4l_la-v4l-s.lo
+libsane_v4l_la_OBJECTS = $(nodist_libsane_v4l_la_OBJECTS)
+libsane_v4l_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_v4l_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+libsane_xerox_mfp_la_DEPENDENCIES = $(COMMON_LIBS) libxerox_mfp.la \
+ ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
+ ../sanei/sanei_config.lo sane_strstatus.lo \
+ ../sanei/sanei_usb.lo ../sanei/sanei_tcp.lo \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+nodist_libsane_xerox_mfp_la_OBJECTS = \
+ libsane_xerox_mfp_la-xerox_mfp-s.lo
+libsane_xerox_mfp_la_OBJECTS = $(nodist_libsane_xerox_mfp_la_OBJECTS)
+libsane_xerox_mfp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsane_xerox_mfp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+nodist_libsane_la_OBJECTS = libsane_la-dll-s.lo
+libsane_la_OBJECTS = $(nodist_libsane_la_OBJECTS)
+libsane_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libsane_la_LDFLAGS) $(LDFLAGS) -o $@
+libsceptre_la_LIBADD =
+am_libsceptre_la_OBJECTS = libsceptre_la-sceptre.lo
+libsceptre_la_OBJECTS = $(am_libsceptre_la_OBJECTS)
+libsharp_la_LIBADD =
+am_libsharp_la_OBJECTS = libsharp_la-sharp.lo
+libsharp_la_OBJECTS = $(am_libsharp_la_OBJECTS)
+libsm3600_la_LIBADD =
+am_libsm3600_la_OBJECTS = libsm3600_la-sm3600.lo
+libsm3600_la_OBJECTS = $(am_libsm3600_la_OBJECTS)
+libsm3840_la_LIBADD =
+am_libsm3840_la_OBJECTS = libsm3840_la-sm3840.lo
+libsm3840_la_OBJECTS = $(am_libsm3840_la_OBJECTS)
+libsnapscan_la_LIBADD =
+am_libsnapscan_la_OBJECTS = libsnapscan_la-snapscan.lo
+libsnapscan_la_OBJECTS = $(am_libsnapscan_la_OBJECTS)
+libsp15c_la_LIBADD =
+am_libsp15c_la_OBJECTS = libsp15c_la-sp15c.lo
+libsp15c_la_OBJECTS = $(am_libsp15c_la_OBJECTS)
+libst400_la_LIBADD =
+am_libst400_la_OBJECTS = libst400_la-st400.lo
+libst400_la_OBJECTS = $(am_libst400_la_OBJECTS)
+libstv680_la_LIBADD =
+am_libstv680_la_OBJECTS = libstv680_la-stv680.lo
+libstv680_la_OBJECTS = $(am_libstv680_la_OBJECTS)
+libtamarack_la_LIBADD =
+am_libtamarack_la_OBJECTS = libtamarack_la-tamarack.lo
+libtamarack_la_OBJECTS = $(am_libtamarack_la_OBJECTS)
+libteco1_la_LIBADD =
+am_libteco1_la_OBJECTS = libteco1_la-teco1.lo
+libteco1_la_OBJECTS = $(am_libteco1_la_OBJECTS)
+libteco2_la_LIBADD =
+am_libteco2_la_OBJECTS = libteco2_la-teco2.lo
+libteco2_la_OBJECTS = $(am_libteco2_la_OBJECTS)
+libteco3_la_LIBADD =
+am_libteco3_la_OBJECTS = libteco3_la-teco3.lo
+libteco3_la_OBJECTS = $(am_libteco3_la_OBJECTS)
+libtest_la_LIBADD =
+am_libtest_la_OBJECTS = libtest_la-test.lo
+libtest_la_OBJECTS = $(am_libtest_la_OBJECTS)
+libu12_la_LIBADD =
+am_libu12_la_OBJECTS = libu12_la-u12.lo
+libu12_la_OBJECTS = $(am_libu12_la_OBJECTS)
+libumax_la_LIBADD =
+am_libumax_la_OBJECTS = libumax_la-umax.lo
+libumax_la_OBJECTS = $(am_libumax_la_OBJECTS)
+libumax1220u_la_LIBADD =
+am_libumax1220u_la_OBJECTS = libumax1220u_la-umax1220u.lo
+libumax1220u_la_OBJECTS = $(am_libumax1220u_la_OBJECTS)
+libumax_pp_la_LIBADD =
+am_libumax_pp_la_OBJECTS = libumax_pp_la-umax_pp.lo \
+ libumax_pp_la-umax_pp_low.lo libumax_pp_la-umax_pp_mid.lo
+libumax_pp_la_OBJECTS = $(am_libumax_pp_la_OBJECTS)
+libv4l_la_LIBADD =
+am_libv4l_la_OBJECTS = libv4l_la-v4l.lo
+libv4l_la_OBJECTS = $(am_libv4l_la_OBJECTS)
+libxerox_mfp_la_LIBADD =
+am_libxerox_mfp_la_OBJECTS = libxerox_mfp_la-xerox_mfp.lo \
+ libxerox_mfp_la-xerox_mfp-usb.lo \
+ libxerox_mfp_la-xerox_mfp-tcp.lo
+libxerox_mfp_la_OBJECTS = $(am_libxerox_mfp_la_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include/sane
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libabaton_la_SOURCES) $(libagfafocus_la_SOURCES) \
+ $(libapple_la_SOURCES) $(libartec_la_SOURCES) \
+ $(libartec_eplus48u_la_SOURCES) $(libas6e_la_SOURCES) \
+ $(libavision_la_SOURCES) $(libbh_la_SOURCES) \
+ $(libcanon_la_SOURCES) $(libcanon630u_la_SOURCES) \
+ $(libcanon_dr_la_SOURCES) $(libcanon_pp_la_SOURCES) \
+ $(libcardscan_la_SOURCES) $(libcoolscan_la_SOURCES) \
+ $(libcoolscan2_la_SOURCES) $(libcoolscan3_la_SOURCES) \
+ $(libdc210_la_SOURCES) $(libdc240_la_SOURCES) \
+ $(libdc25_la_SOURCES) $(libdell1600n_net_la_SOURCES) \
+ $(libdll_la_SOURCES) $(libdll_preload_la_SOURCES) \
+ $(libdmc_la_SOURCES) $(libepjitsu_la_SOURCES) \
+ $(libepson_la_SOURCES) $(libepson2_la_SOURCES) \
+ $(libfujitsu_la_SOURCES) $(libgenesys_la_SOURCES) \
+ $(libgphoto2_i_la_SOURCES) $(libgt68xx_la_SOURCES) \
+ $(libhp_la_SOURCES) $(libhp3500_la_SOURCES) \
+ $(libhp3900_la_SOURCES) $(libhp4200_la_SOURCES) \
+ $(libhp5400_la_SOURCES) $(libhp5590_la_SOURCES) \
+ $(libhpljm1005_la_SOURCES) $(libhpsj5s_la_SOURCES) \
+ $(libhs2p_la_SOURCES) $(libibm_la_SOURCES) \
+ $(libkodak_la_SOURCES) $(libkodakaio_la_SOURCES) \
+ $(libkvs1025_la_SOURCES) $(libkvs20xx_la_SOURCES) \
+ $(libkvs40xx_la_SOURCES) $(libleo_la_SOURCES) \
+ $(liblexmark_la_SOURCES) $(libma1509_la_SOURCES) \
+ $(libmagicolor_la_SOURCES) $(libmatsushita_la_SOURCES) \
+ $(libmicrotek_la_SOURCES) $(libmicrotek2_la_SOURCES) \
+ $(libmustek_la_SOURCES) $(libmustek_pp_la_SOURCES) \
+ $(libmustek_usb_la_SOURCES) $(libmustek_usb2_la_SOURCES) \
+ $(libnec_la_SOURCES) $(libnet_la_SOURCES) \
+ $(libniash_la_SOURCES) $(libp5_la_SOURCES) \
+ $(libpie_la_SOURCES) $(libpint_la_SOURCES) \
+ $(libpixma_la_SOURCES) $(libplustek_la_SOURCES) \
+ $(libplustek_pp_la_SOURCES) $(libpnm_la_SOURCES) \
+ $(libqcam_la_SOURCES) $(libricoh_la_SOURCES) \
+ $(librts8891_la_SOURCES) $(libs9036_la_SOURCES) \
+ $(nodist_libsane_abaton_la_SOURCES) \
+ $(nodist_libsane_agfafocus_la_SOURCES) \
+ $(nodist_libsane_apple_la_SOURCES) \
+ $(nodist_libsane_artec_la_SOURCES) \
+ $(nodist_libsane_artec_eplus48u_la_SOURCES) \
+ $(nodist_libsane_as6e_la_SOURCES) \
+ $(nodist_libsane_avision_la_SOURCES) \
+ $(nodist_libsane_bh_la_SOURCES) \
+ $(nodist_libsane_canon_la_SOURCES) \
+ $(nodist_libsane_canon630u_la_SOURCES) \
+ $(nodist_libsane_canon_dr_la_SOURCES) \
+ $(nodist_libsane_canon_pp_la_SOURCES) \
+ $(nodist_libsane_cardscan_la_SOURCES) \
+ $(nodist_libsane_coolscan_la_SOURCES) \
+ $(nodist_libsane_coolscan2_la_SOURCES) \
+ $(nodist_libsane_coolscan3_la_SOURCES) \
+ $(nodist_libsane_dc210_la_SOURCES) \
+ $(nodist_libsane_dc240_la_SOURCES) \
+ $(nodist_libsane_dc25_la_SOURCES) \
+ $(nodist_libsane_dell1600n_net_la_SOURCES) \
+ $(nodist_libsane_dll_la_SOURCES) \
+ $(nodist_libsane_dmc_la_SOURCES) \
+ $(nodist_libsane_epjitsu_la_SOURCES) \
+ $(nodist_libsane_epson_la_SOURCES) \
+ $(nodist_libsane_epson2_la_SOURCES) \
+ $(nodist_libsane_fujitsu_la_SOURCES) \
+ $(nodist_libsane_genesys_la_SOURCES) \
+ $(nodist_libsane_gphoto2_la_SOURCES) \
+ $(nodist_libsane_gt68xx_la_SOURCES) \
+ $(nodist_libsane_hp_la_SOURCES) \
+ $(nodist_libsane_hp3500_la_SOURCES) \
+ $(nodist_libsane_hp3900_la_SOURCES) \
+ $(nodist_libsane_hp4200_la_SOURCES) \
+ $(nodist_libsane_hp5400_la_SOURCES) \
+ $(nodist_libsane_hp5590_la_SOURCES) \
+ $(nodist_libsane_hpljm1005_la_SOURCES) \
+ $(nodist_libsane_hpsj5s_la_SOURCES) \
+ $(nodist_libsane_hs2p_la_SOURCES) \
+ $(nodist_libsane_ibm_la_SOURCES) \
+ $(nodist_libsane_kodak_la_SOURCES) \
+ $(nodist_libsane_kodakaio_la_SOURCES) \
+ $(nodist_libsane_kvs1025_la_SOURCES) \
+ $(nodist_libsane_kvs20xx_la_SOURCES) \
+ $(nodist_libsane_kvs40xx_la_SOURCES) \
+ $(nodist_libsane_leo_la_SOURCES) \
+ $(nodist_libsane_lexmark_la_SOURCES) \
+ $(nodist_libsane_ma1509_la_SOURCES) \
+ $(nodist_libsane_magicolor_la_SOURCES) \
+ $(nodist_libsane_matsushita_la_SOURCES) \
+ $(nodist_libsane_microtek_la_SOURCES) \
+ $(nodist_libsane_microtek2_la_SOURCES) \
+ $(nodist_libsane_mustek_la_SOURCES) \
+ $(nodist_libsane_mustek_pp_la_SOURCES) \
+ $(nodist_libsane_mustek_usb_la_SOURCES) \
+ $(nodist_libsane_mustek_usb2_la_SOURCES) \
+ $(nodist_libsane_nec_la_SOURCES) \
+ $(nodist_libsane_net_la_SOURCES) \
+ $(nodist_libsane_niash_la_SOURCES) \
+ $(nodist_libsane_p5_la_SOURCES) \
+ $(nodist_libsane_pie_la_SOURCES) \
+ $(nodist_libsane_pint_la_SOURCES) \
+ $(nodist_libsane_pixma_la_SOURCES) \
+ $(nodist_libsane_plustek_la_SOURCES) \
+ $(nodist_libsane_plustek_pp_la_SOURCES) \
+ $(nodist_libsane_pnm_la_SOURCES) \
+ $(nodist_libsane_qcam_la_SOURCES) \
+ $(nodist_libsane_ricoh_la_SOURCES) \
+ $(nodist_libsane_rts8891_la_SOURCES) \
+ $(nodist_libsane_s9036_la_SOURCES) \
+ $(nodist_libsane_sceptre_la_SOURCES) \
+ $(nodist_libsane_sharp_la_SOURCES) \
+ $(nodist_libsane_sm3600_la_SOURCES) \
+ $(nodist_libsane_sm3840_la_SOURCES) \
+ $(nodist_libsane_snapscan_la_SOURCES) \
+ $(nodist_libsane_sp15c_la_SOURCES) \
+ $(nodist_libsane_st400_la_SOURCES) \
+ $(nodist_libsane_stv680_la_SOURCES) \
+ $(nodist_libsane_tamarack_la_SOURCES) \
+ $(nodist_libsane_teco1_la_SOURCES) \
+ $(nodist_libsane_teco2_la_SOURCES) \
+ $(nodist_libsane_teco3_la_SOURCES) \
+ $(nodist_libsane_test_la_SOURCES) \
+ $(nodist_libsane_u12_la_SOURCES) \
+ $(nodist_libsane_umax_la_SOURCES) \
+ $(nodist_libsane_umax1220u_la_SOURCES) \
+ $(nodist_libsane_umax_pp_la_SOURCES) \
+ $(nodist_libsane_v4l_la_SOURCES) \
+ $(nodist_libsane_xerox_mfp_la_SOURCES) \
+ $(nodist_libsane_la_SOURCES) $(libsceptre_la_SOURCES) \
+ $(libsharp_la_SOURCES) $(libsm3600_la_SOURCES) \
+ $(libsm3840_la_SOURCES) $(libsnapscan_la_SOURCES) \
+ $(libsp15c_la_SOURCES) $(libst400_la_SOURCES) \
+ $(libstv680_la_SOURCES) $(libtamarack_la_SOURCES) \
+ $(libteco1_la_SOURCES) $(libteco2_la_SOURCES) \
+ $(libteco3_la_SOURCES) $(libtest_la_SOURCES) \
+ $(libu12_la_SOURCES) $(libumax_la_SOURCES) \
+ $(libumax1220u_la_SOURCES) $(libumax_pp_la_SOURCES) \
+ $(libv4l_la_SOURCES) $(libxerox_mfp_la_SOURCES)
+DIST_SOURCES = $(libabaton_la_SOURCES) $(libagfafocus_la_SOURCES) \
+ $(libapple_la_SOURCES) $(libartec_la_SOURCES) \
+ $(libartec_eplus48u_la_SOURCES) $(libas6e_la_SOURCES) \
+ $(libavision_la_SOURCES) $(libbh_la_SOURCES) \
+ $(libcanon_la_SOURCES) $(libcanon630u_la_SOURCES) \
+ $(libcanon_dr_la_SOURCES) $(libcanon_pp_la_SOURCES) \
+ $(libcardscan_la_SOURCES) $(libcoolscan_la_SOURCES) \
+ $(libcoolscan2_la_SOURCES) $(libcoolscan3_la_SOURCES) \
+ $(libdc210_la_SOURCES) $(libdc240_la_SOURCES) \
+ $(libdc25_la_SOURCES) $(libdell1600n_net_la_SOURCES) \
+ $(libdll_la_SOURCES) $(libdll_preload_la_SOURCES) \
+ $(libdmc_la_SOURCES) $(libepjitsu_la_SOURCES) \
+ $(libepson_la_SOURCES) $(libepson2_la_SOURCES) \
+ $(libfujitsu_la_SOURCES) $(libgenesys_la_SOURCES) \
+ $(libgphoto2_i_la_SOURCES) $(libgt68xx_la_SOURCES) \
+ $(libhp_la_SOURCES) $(libhp3500_la_SOURCES) \
+ $(libhp3900_la_SOURCES) $(libhp4200_la_SOURCES) \
+ $(libhp5400_la_SOURCES) $(libhp5590_la_SOURCES) \
+ $(libhpljm1005_la_SOURCES) $(libhpsj5s_la_SOURCES) \
+ $(libhs2p_la_SOURCES) $(libibm_la_SOURCES) \
+ $(libkodak_la_SOURCES) $(libkodakaio_la_SOURCES) \
+ $(libkvs1025_la_SOURCES) $(libkvs20xx_la_SOURCES) \
+ $(libkvs40xx_la_SOURCES) $(libleo_la_SOURCES) \
+ $(liblexmark_la_SOURCES) $(libma1509_la_SOURCES) \
+ $(libmagicolor_la_SOURCES) $(libmatsushita_la_SOURCES) \
+ $(libmicrotek_la_SOURCES) $(libmicrotek2_la_SOURCES) \
+ $(libmustek_la_SOURCES) $(libmustek_pp_la_SOURCES) \
+ $(libmustek_usb_la_SOURCES) $(libmustek_usb2_la_SOURCES) \
+ $(libnec_la_SOURCES) $(libnet_la_SOURCES) \
+ $(libniash_la_SOURCES) $(libp5_la_SOURCES) \
+ $(libpie_la_SOURCES) $(libpint_la_SOURCES) \
+ $(libpixma_la_SOURCES) $(libplustek_la_SOURCES) \
+ $(libplustek_pp_la_SOURCES) $(libpnm_la_SOURCES) \
+ $(libqcam_la_SOURCES) $(libricoh_la_SOURCES) \
+ $(librts8891_la_SOURCES) $(libs9036_la_SOURCES) \
+ $(libsceptre_la_SOURCES) $(libsharp_la_SOURCES) \
+ $(libsm3600_la_SOURCES) $(libsm3840_la_SOURCES) \
+ $(libsnapscan_la_SOURCES) $(libsp15c_la_SOURCES) \
+ $(libst400_la_SOURCES) $(libstv680_la_SOURCES) \
+ $(libtamarack_la_SOURCES) $(libteco1_la_SOURCES) \
+ $(libteco2_la_SOURCES) $(libteco3_la_SOURCES) \
+ $(libtest_la_SOURCES) $(libu12_la_SOURCES) \
+ $(libumax_la_SOURCES) $(libumax1220u_la_SOURCES) \
+ $(libumax_pp_la_SOURCES) $(libv4l_la_SOURCES) \
+ $(libxerox_mfp_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AVAHI_CFLAGS = @AVAHI_CFLAGS@
+AVAHI_LIBS = @AVAHI_LIBS@
+AWK = @AWK@
+BACKENDS = @BACKENDS@
+BACKEND_CONFS_ENABLED = @BACKEND_CONFS_ENABLED@
+BACKEND_LIBS_ENABLED = @BACKEND_LIBS_ENABLED@
+BACKEND_MANS_ENABLED = @BACKEND_MANS_ENABLED@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCLEAN_FILES = @DISTCLEAN_FILES@
+DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DVIPS = @DVIPS@
+DYNAMIC_FLAG = @DYNAMIC_FLAG@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GPHOTO2_CPPFLAGS = @GPHOTO2_CPPFLAGS@
+GPHOTO2_LDFLAGS = @GPHOTO2_LDFLAGS@
+GPHOTO2_LIBS = @GPHOTO2_LIBS@
+GREP = @GREP@
+HAVE_GPHOTO2 = @HAVE_GPHOTO2@
+IEEE1284_LIBS = @IEEE1284_LIBS@
+INCLUDES = @INCLUDES@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_LOCKPATH = @INSTALL_LOCKPATH@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
+LATEX = @LATEX@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+
+# LIBTOOL install is a little to noisy for my liking.
+LIBTOOL = @LIBTOOL@ --silent
+LIBUSB_1_0_CFLAGS = @LIBUSB_1_0_CFLAGS@
+LIBUSB_1_0_LIBS = @LIBUSB_1_0_LIBS@
+LIBV4L_CFLAGS = @LIBV4L_CFLAGS@
+LIBV4L_LIBS = @LIBV4L_LIBS@
+LINKER_RPATH = @LINKER_RPATH@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCKPATH_GROUP = @LOCKPATH_GROUP@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINDEX = @MAKEINDEX@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIB = @MATH_LIB@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NUMBER_VERSION = @NUMBER_VERSION@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PRELOADABLE_BACKENDS = @PRELOADABLE_BACKENDS@
+PRELOADABLE_BACKENDS_ENABLED = @PRELOADABLE_BACKENDS_ENABLED@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RESMGR_LIBS = @RESMGR_LIBS@
+SANEI_SANEI_JPEG_LO = @SANEI_SANEI_JPEG_LO@
+SANE_CONFIG_PATH = @SANE_CONFIG_PATH@
+SCSI_LIBS = @SCSI_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNMP_CONFIG_PATH = @SNMP_CONFIG_PATH@
+SOCKET_LIBS = @SOCKET_LIBS@
+STRICT_LDFLAGS = @STRICT_LDFLAGS@
+STRIP = @STRIP@
+SYSLOG_LIBS = @SYSLOG_LIBS@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+TIFF_LIBS = @TIFF_LIBS@
+USB_LIBS = @USB_LIBS@
+VERSION = @VERSION@
+V_MAJOR = @V_MAJOR@
+V_MINOR = @V_MINOR@
+V_REV = @V_REV@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+configdir = @configdir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+effective_target = @effective_target@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+locksanedir = @locksanedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include -DLIBDIR="$(libdir)/sane"
+AM_LDFLAGS = @STRICT_LDFLAGS@
+# The -rpath option is added because we are creating _LTLIBRARIES based
+# on configure substitution. This causes automake to not know the
+# correct $libdir and must be added here.
+DIST_SANELIBS_LDFLAGS = $(AM_LDFLAGS) -rpath '$(libdir)/sane' -version-number $(V_MAJOR):$(V_MINOR):$(V_REV) $(DYNAMIC_FLAG)
+DIST_LIBS_LDFLAGS = $(AM_LDFLAGS) -rpath '$(libdir)' -version-number $(V_MAJOR):$(V_MINOR):$(V_REV)
+FIRMWARE_DIRS = artec_eplus48u gt68xx snapscan epjitsu
+
+# TODO: This really belongs together with the saned sources and
+# should be installed there as well.
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are this distributed but not compiled?
+# TODO: Why are this distributed but not compiled?
+# TODO: Why are this distributed but not compiled?
+# TODO: These should be moved to ../docs/hp; don't belong here.
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why are these distributed but not compiled?
+# TODO: Why is this distributed but not installed?
+EXTRA_DIST = sane_strstatus.c stubs.c saned.conf.in abaton.conf.in \
+ agfafocus.conf.in apple.conf.in artec.conf.in \
+ artec_eplus48u.conf.in avision.conf.in bh.conf.in \
+ canon.conf.in canon-sane.c canon-scsi.c canon630u.conf.in \
+ canon630u-common.c lm9830.h canon_dr.conf.in canon_pp.conf.in \
+ cardscan.conf.in coolscan.conf.in coolscan2.conf.in \
+ coolscan3.conf.in dc25.conf.in dc210.conf.in dc240.conf.in \
+ dell1600n_net.conf.in dmc.conf.in epjitsu.conf.in \
+ epson.conf.in epson2.conf.in fujitsu.conf.in genesys.conf.in \
+ genesys_conv.c genesys_conv_hlp.c genesys_devices.c \
+ gphoto2.conf.in gt68xx.conf.in gt68xx_devices.c \
+ gt68xx_generic.c gt68xx_generic.h gt68xx_gt6801.c \
+ gt68xx_gt6801.h gt68xx_gt6816.c gt68xx_gt6816.h gt68xx_high.c \
+ gt68xx_high.h gt68xx_low.c gt68xx_low.h gt68xx_mid.c \
+ gt68xx_mid.h gt68xx_shm_channel.c gt68xx_shm_channel.h \
+ hp.conf.in hp.README hp.TODO hp3900.conf.in hp3900_config.c \
+ hp3900_debug.c hp3900_rts8822.c hp3900_sane.c hp3900_types.c \
+ hp3900_usb.c hp4200.conf.in hp4200_lm9830.c hp4200_lm9830.h \
+ hp5400.conf.in hp5400_debug.c hp5400_debug.h hp5400_internal.c \
+ hp5400_internal.h hp5400_sane.c hp5400_sanei.c hp5400_sanei.h \
+ hp5400_xfer.h hp5590_cmds.c hp5590_cmds.h hp5590_low.c \
+ hp5590_low.h hpsj5s.conf.in hs2p.conf.in hs2p-scsi.c \
+ hs2p-scsi.h ibm.conf.in ibm-scsi.c kodak.conf.in \
+ kodakaio.conf.in leo.conf.in lexmark.conf.in lexmark_models.c \
+ lexmark_sensors.c ma1509.conf.in magicolor.conf.in \
+ matsushita.conf.in microtek.conf.in microtek2.conf.in \
+ mustek.conf.in mustek_scsi_pp.c mustek_scsi_pp.h \
+ mustek_pp.conf.in mustek_pp_ccd300.c mustek_pp_ccd300.h \
+ mustek_pp_cis.c mustek_pp_cis.h mustek_pp_null.c \
+ mustek_usb.conf.in mustek_usb_high.c mustek_usb_high.h \
+ mustek_usb_low.c mustek_usb_low.h mustek_usb_mid.c \
+ mustek_usb_mid.h mustek_usb2_asic.c mustek_usb2_asic.h \
+ mustek_usb2_high.c mustek_usb2_high.h mustek_usb2_reflective.c \
+ mustek_usb2_transparent.c nec.conf.in net.conf.in niash_core.c \
+ niash_core.h niash_xfer.c niash_xfer.h pie.conf.in p5.conf.in \
+ p5_device.c pixma.conf.in pixma_sane_options.c \
+ pixma_sane_options.h plustek.conf.in plustek-usb.c \
+ plustek-usb.h plustek-usbcal.c plustek-usbcalfile.c \
+ plustek-usbdevs.c plustek-usbhw.c plustek-usbimg.c \
+ plustek-usbio.c plustek-usbmap.c plustek-usbscan.c \
+ plustek-usbshading.c plustek_pp.conf.in plustek-pp_dac.c \
+ plustek-pp_dbg.h plustek-pp_detect.c plustek-pp_genericio.c \
+ plustek-pp_hwdefs.h plustek-pp_image.c plustek-pp_io.c \
+ plustek-pp_map.c plustek-pp_misc.c plustek-pp_models.c \
+ plustek-pp_motor.c plustek-pp_p12.c plustek-pp_p12ccd.c \
+ plustek-pp_p48xx.c plustek-pp_p9636.c plustek-pp_procfs.c \
+ plustek-pp_procs.h plustek-pp_ptdrv.c plustek-pp_scale.c \
+ plustek-pp_scan.h plustek-pp_scandata.h plustek-pp_sysdep.h \
+ plustek-pp_tpa.c plustek-pp_types.h plustek-pp_wrapper.c \
+ qcam.conf.in ricoh.conf.in ricoh-scsi.c rts8891.conf.in \
+ rts8891_devices.c rts8891_low.c rts8891_low.h s9036.conf.in \
+ sceptre.conf.in sharp.conf.in sm3600-color.c sm3600-gray.c \
+ sm3600-homerun.c sm3600-scanmtek.c sm3600-scantool.h \
+ sm3600-scanusb.c sm3600-scanutil.c sm3840.conf.in sm3840_lib.c \
+ sm3840_lib.h sm3840_scan.c snapscan.conf.in snapscan-data.c \
+ snapscan-mutex.c snapscan-options.c snapscan-scsi.c \
+ snapscan-sources.c snapscan-sources.h snapscan-usb.c \
+ snapscan-usb.h sp15c.conf.in st400.conf.in stv680.conf.in \
+ tamarack.conf.in test.conf.in test-picture.c teco1.conf.in \
+ teco2.conf.in teco3.conf.in u12.conf.in u12-ccd.c u12-hw.c \
+ u12-hwdef.h u12-if.c u12-image.c u12-io.c u12-map.c \
+ u12-motor.c u12-scanner.h u12-shading.c u12-tpa.c umax.conf.in \
+ umax-scanner.c umax-scanner.h umax-scsidef.h umax-uc1200s.c \
+ umax-uc1200se.c umax-uc1260.c umax-uc630.c umax-uc840.c \
+ umax-ug630.c umax-ug80.c umax-usb.c umax1220u.conf.in \
+ umax1220u-common.c umax_pp.conf.in v4l.conf.in \
+ xerox_mfp.conf.in dll.conf.in dll.aliases
+
+# Backends are not required to have a config file. Any backend
+# that wants to install a config file should list it here.
+BACKEND_CONFS = abaton.conf agfafocus.conf apple.conf artec.conf \
+ artec_eplus48u.conf avision.conf bh.conf \
+ canon630u.conf canon.conf canon_dr.conf \
+ canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \
+ coolscan.conf dc210.conf dc240.conf dc25.conf \
+ dell1600n_net.conf dmc.conf epjitsu.conf epson2.conf \
+ epson.conf fujitsu.conf genesys.conf gphoto2.conf \
+ gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
+ hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\
+ leo.conf lexmark.conf ma1509.conf magicolor.conf \
+ matsushita.conf microtek2.conf microtek.conf mustek.conf \
+ mustek_pp.conf mustek_usb.conf nec.conf net.conf \
+ p5.conf \
+ pie.conf pixma.conf plustek.conf plustek_pp.conf \
+ qcam.conf ricoh.conf rts8891.conf s9036.conf sceptre.conf \
+ sharp.conf sm3840.conf snapscan.conf sp15c.conf \
+ st400.conf stv680.conf tamarack.conf \
+ teco1.conf teco2.conf teco3.conf test.conf \
+ u12.conf umax1220u.conf umax.conf umax_pp.conf v4l.conf \
+ xerox_mfp.conf dll.conf saned.conf
+
+SUFFIXES = .conf.in .conf
+CLEANFILES = $(BACKEND_CONFS) $(be_convenience_libs) dll-preload.h
+
+# Backends
+#
+# All possible backends should be listed here. As a first step, we create
+# a convenience library containing all files needed to link a backend
+# directly into libsane.la. Convenience library should have the
+# form of lib${backend}.la to match what configure will list to
+# build.
+# Occasionally, this approach will have name conflicts with external
+# libraries that need to be linked in. See libgphoto2_i.la for
+# example of working around that issue.
+be_convenience_libs = libabaton.la libagfafocus.la \
+ libapple.la libartec.la libartec_eplus48u.la \
+ libas6e.la libavision.la libbh.la \
+ libcanon.la libcanon630u.la libcanon_dr.la \
+ libcanon_pp.la libcardscan.la libcoolscan.la \
+ libcoolscan2.la libcoolscan3.la libdc25.la \
+ libdc210.la libdc240.la libdell1600n_net.la \
+ libdmc.la libdll.la libdll_preload.la libepjitsu.la libepson.la \
+ libepson2.la libfujitsu.la libgenesys.la \
+ libgphoto2_i.la libgt68xx.la libhp.la \
+ libhp3500.la libhp3900.la libhp4200.la \
+ libhp5400.la libhp5590.la libhpljm1005.la \
+ libhpsj5s.la libhs2p.la libibm.la libkodak.la libkodakaio.la\
+ libkvs1025.la libkvs20xx.la libkvs40xx.la \
+ libleo.la liblexmark.la libma1509.la libmagicolor.la \
+ libmatsushita.la libmicrotek.la libmicrotek2.la \
+ libmustek.la libmustek_pp.la libmustek_usb.la \
+ libmustek_usb2.la libnec.la libnet.la \
+ libniash.la libp5.la \
+ libpie.la libpint.la libpixma.la \
+ libplustek.la libplustek_pp.la libpnm.la \
+ libqcam.la libricoh.la librts8891.la \
+ libs9036.la libsceptre.la libsharp.la \
+ libsm3600.la libsm3840.la libsnapscan.la \
+ libsp15c.la libst400.la libstv680.la \
+ libtamarack.la libtest.la libteco1.la \
+ libteco2.la libteco3.la libu12.la libumax.la \
+ libumax1220u.la libumax_pp.la libv4l.la \
+ libxerox_mfp.la
+
+
+# Each stand alone backend thats possible to be built should be listed
+# here. There are the libraries that are installed under $(libdir)/sane.
+# Format is libsane-${backend}.la.
+be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
+ libsane-apple.la libsane-artec.la libsane-artec_eplus48u.la \
+ libsane-as6e.la libsane-avision.la libsane-bh.la \
+ libsane-canon.la libsane-canon630u.la libsane-canon_dr.la \
+ libsane-canon_pp.la libsane-cardscan.la libsane-coolscan.la \
+ libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \
+ libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \
+ libsane-dmc.la libsane-epjitsu.la libsane-epson.la \
+ libsane-epson2.la libsane-fujitsu.la libsane-genesys.la \
+ libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
+ libsane-hp3500.la libsane-hp3900.la libsane-hp4200.la \
+ libsane-hp5400.la libsane-hp5590.la libsane-hpljm1005.la \
+ libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\
+ libsane-kvs1025.la libsane-kvs20xx.la libsane-kvs40xx.la \
+ libsane-leo.la \
+ libsane-lexmark.la libsane-ma1509.la libsane-magicolor.la \
+ libsane-matsushita.la libsane-microtek.la libsane-microtek2.la \
+ libsane-mustek.la libsane-mustek_pp.la libsane-mustek_usb.la \
+ libsane-mustek_usb2.la libsane-nec.la libsane-net.la \
+ libsane-niash.la libsane-p5.la \
+ libsane-pie.la libsane-pint.la libsane-pixma.la \
+ libsane-plustek.la libsane-plustek_pp.la libsane-pnm.la \
+ libsane-qcam.la libsane-ricoh.la libsane-rts8891.la \
+ libsane-s9036.la libsane-sceptre.la libsane-sharp.la \
+ libsane-sm3600.la libsane-sm3840.la libsane-snapscan.la \
+ libsane-sp15c.la libsane-st400.la libsane-stv680.la \
+ libsane-tamarack.la libsane-test.la libsane-teco1.la \
+ libsane-teco2.la libsane-teco3.la libsane-u12.la libsane-umax.la \
+ libsane-umax1220u.la libsane-umax_pp.la libsane-v4l.la \
+ libsane-xerox_mfp.la
+
+EXTRA_LTLIBRARIES = $(be_convenience_libs) $(be_dlopen_libs)
+lib_LTLIBRARIES = libsane.la
+sanelibdir = $(libdir)/sane
+sanelib_LTLIBRARIES = $(BACKEND_LIBS_ENABLED) libsane-dll.la
+COMMON_LIBS = ../lib/liblib.la
+
+# Each backend should define a convenience library that compiles
+# all related files within backend directory. General guideline
+# is to have a ${backend}.c and ${backend}.h. Some backends also
+# add a few support source files to convience library.
+# Note: automake doesn't really use header files listed here.
+# They are indications that they need to be distributed only.
+libabaton_la_SOURCES = abaton.c abaton.h
+libabaton_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=abaton
+
+# Each backend should define a stand alone library that gets installed.
+# This will need to link in a special file ${backend}-s.c that allows
+# the backend to be stand alone and contain all SANE API functions.
+# Also, it will need to link in related convenience library as well as
+# any external libraries required to resolve symbols.
+#
+# All backends should include $(DIST_SANELIBS_LDFLAGS) so that
+# library is correctly versioned.
+#
+# If a backend has a config file, it must be listed here to get distributed.
+nodist_libsane_abaton_la_SOURCES = abaton-s.c
+libsane_abaton_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=abaton
+libsane_abaton_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_abaton_la_LIBADD = $(COMMON_LIBS) libabaton.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libagfafocus_la_SOURCES = agfafocus.c agfafocus.h
+libagfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus
+nodist_libsane_agfafocus_la_SOURCES = agfafocus-s.c
+libsane_agfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus
+libsane_agfafocus_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_agfafocus_la_LIBADD = $(COMMON_LIBS) libagfafocus.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libapple_la_SOURCES = apple.c apple.h
+libapple_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=apple
+nodist_libsane_apple_la_SOURCES = apple-s.c
+libsane_apple_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=apple
+libsane_apple_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_apple_la_LIBADD = $(COMMON_LIBS) libapple.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libartec_la_SOURCES = artec.c artec.h
+libartec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec
+nodist_libsane_artec_la_SOURCES = artec-s.c
+libsane_artec_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_artec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec
+libsane_artec_la_LIBADD = $(COMMON_LIBS) libartec.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libartec_eplus48u_la_SOURCES = artec_eplus48u.c artec_eplus48u.h
+libartec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u
+nodist_libsane_artec_eplus48u_la_SOURCES = artec_eplus48u-s.c
+libsane_artec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u
+libsane_artec_eplus48u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_artec_eplus48u_la_LIBADD = $(COMMON_LIBS) libartec_eplus48u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMEG_LIBS)
+libas6e_la_SOURCES = as6e.c as6e.h
+libas6e_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=as6e
+nodist_libsane_as6e_la_SOURCES = as6e-s.c
+libsane_as6e_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=as6e
+libsane_as6e_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_as6e_la_LIBADD = $(COMMON_LIBS) libas6e.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+libavision_la_SOURCES = avision.c avision.h
+libavision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision
+nodist_libsane_avision_la_SOURCES = avision-s.c
+libsane_avision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision
+libsane_avision_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_avision_la_LIBADD = $(COMMON_LIBS) libavision.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libbh_la_SOURCES = bh.c bh.h
+libbh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=bh
+nodist_libsane_bh_la_SOURCES = bh-s.c
+libsane_bh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=bh
+libsane_bh_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_bh_la_LIBADD = $(COMMON_LIBS) libbh.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libcanon_la_SOURCES = canon.c canon.h
+libcanon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon
+nodist_libsane_canon_la_SOURCES = canon-s.c
+libsane_canon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon
+libsane_canon_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon_la_LIBADD = $(COMMON_LIBS) libcanon.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+libcanon630u_la_SOURCES = canon630u.c
+libcanon630u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon630u
+nodist_libsane_canon630u_la_SOURCES = canon630u-s.c
+libsane_canon630u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon630u
+libsane_canon630u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon630u_la_LIBADD = $(COMMON_LIBS) libcanon630u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libcanon_dr_la_SOURCES = canon_dr.c canon_dr.h canon_dr-cmd.h
+libcanon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr
+nodist_libsane_canon_dr_la_SOURCES = canon_dr-s.c
+libsane_canon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr
+libsane_canon_dr_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libcanon_pp_la_SOURCES = canon_pp.c canon_pp.h canon_pp-io.c canon_pp-io.h canon_pp-dev.c canon_pp-dev.h
+libcanon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp
+nodist_libsane_canon_pp_la_SOURCES = canon_pp-s.c
+libsane_canon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp
+libsane_canon_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_canon_pp_la_LIBADD = $(COMMON_LIBS) libcanon_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(IEEE1284_LIBS)
+libcardscan_la_SOURCES = cardscan.c cardscan.h
+libcardscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=cardscan
+nodist_libsane_cardscan_la_SOURCES = cardscan-s.c
+libsane_cardscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=cardscan
+libsane_cardscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_cardscan_la_LIBADD = $(COMMON_LIBS) libcardscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libcoolscan_la_SOURCES = coolscan.c coolscan.h coolscan-scsidef.h
+libcoolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan
+nodist_libsane_coolscan_la_SOURCES = coolscan-s.c
+libsane_coolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan
+libsane_coolscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_coolscan_la_LIBADD = $(COMMON_LIBS) libcoolscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libcoolscan2_la_SOURCES = coolscan2.c
+libcoolscan2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan2
+nodist_libsane_coolscan2_la_SOURCES = coolscan2-s.c
+libsane_coolscan2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan2
+libsane_coolscan2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_coolscan2_la_LIBADD = $(COMMON_LIBS) libcoolscan2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libcoolscan3_la_SOURCES = coolscan3.c
+libcoolscan3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan3
+nodist_libsane_coolscan3_la_SOURCES = coolscan3-s.c
+libsane_coolscan3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan3
+libsane_coolscan3_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_coolscan3_la_LIBADD = $(COMMON_LIBS) libcoolscan3.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libdc25_la_SOURCES = dc25.c dc25.h
+libdc25_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc25
+nodist_libsane_dc25_la_SOURCES = dc25-s.c
+libsane_dc25_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc25
+libsane_dc25_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dc25_la_LIBADD = $(COMMON_LIBS) libdc25.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(MATH_LIB)
+libdc210_la_SOURCES = dc210.c dc210.h
+libdc210_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc210
+nodist_libsane_dc210_la_SOURCES = dc210-s.c
+libsane_dc210_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc210
+libsane_dc210_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dc210_la_LIBADD = $(COMMON_LIBS) libdc210.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo @SANEI_SANEI_JPEG_LO@ $(JPEG_LIBS)
+libdc240_la_SOURCES = dc240.c dc240.h
+libdc240_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc240
+nodist_libsane_dc240_la_SOURCES = dc240-s.c
+libsane_dc240_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc240
+libsane_dc240_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dc240_la_LIBADD = $(COMMON_LIBS) libdc240.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo @SANEI_SANEI_JPEG_LO@ $(JPEG_LIBS)
+libdell1600n_net_la_SOURCES = dell1600n_net.c
+libdell1600n_net_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dell1600n_net
+nodist_libsane_dell1600n_net_la_SOURCES = dell1600n_net-s.c
+libsane_dell1600n_net_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dell1600n_net
+libsane_dell1600n_net_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dell1600n_net_la_LIBADD = $(COMMON_LIBS) libdell1600n_net.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(TIFF_LIBS) $(JPEG_LIBS) $(SOCKET_LIBS)
+libdmc_la_SOURCES = dmc.c dmc.h
+libdmc_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dmc
+nodist_libsane_dmc_la_SOURCES = dmc-s.c
+libsane_dmc_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dmc
+libsane_dmc_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dmc_la_LIBADD = $(COMMON_LIBS) libdmc.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libepjitsu_la_SOURCES = epjitsu.c epjitsu.h epjitsu-cmd.h
+libepjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
+nodist_libsane_epjitsu_la_SOURCES = epjitsu-s.c
+libsane_epjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
+libsane_epjitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_epjitsu_la_LIBADD = $(COMMON_LIBS) libepjitsu.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libepson_la_SOURCES = epson.c epson.h epson_scsi.c epson_scsi.h epson_usb.c epson_usb.h
+libepson_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson
+nodist_libsane_epson_la_SOURCES = epson-s.c
+libsane_epson_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson
+libsane_epson_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_epson_la_LIBADD = $(COMMON_LIBS) libepson.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libepson2_la_SOURCES = epson2.c epson2.h epson2_scsi.c epson2_scsi.h epson_usb.c epson2_net.c epson2_net.h epson2-io.c epson2-io.h epson2-commands.c epson2-commands.h epson2-ops.c epson2-ops.h epson2-cct.c
+libepson2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson2
+nodist_libsane_epson2_la_SOURCES = epson2-s.c
+libsane_epson2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson2
+libsane_epson2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_epson2_la_LIBADD = $(COMMON_LIBS) libepson2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo $(SCSI_LIBS) $(USB_LIBS) $(SOCKET_LIBS) $(MATH_LIB) $(RESMGR_LIBS)
+libfujitsu_la_SOURCES = fujitsu.c fujitsu.h fujitsu-scsi.h
+libfujitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=fujitsu
+nodist_libsane_fujitsu_la_SOURCES = fujitsu-s.c
+libsane_fujitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=fujitsu
+libsane_fujitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_fujitsu_la_LIBADD = $(COMMON_LIBS) libfujitsu.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libgenesys_la_SOURCES = genesys.c genesys.h genesys_gl646.c genesys_gl646.h genesys_gl841.c genesys_gl841.h genesys_gl843.c genesys_gl843.h genesys_gl846.c genesys_gl846.h genesys_gl847.c genesys_gl847.h genesys_gl124.c genesys_gl124.h genesys_low.c genesys_low.h
+libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
+nodist_libsane_genesys_la_SOURCES = genesys-s.c
+libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
+libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h
+libgphoto2_i_la_CPPFLAGS = $(AM_CPPFLAGS) @GPHOTO2_CPPFLAGS@ -DBACKEND_NAME=gphoto2
+nodist_libsane_gphoto2_la_SOURCES = gphoto2-s.c
+libsane_gphoto2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gphoto2
+libsane_gphoto2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_gphoto2_la_LIBADD = $(GPHOTO2_LDFLAGS) $(COMMON_LIBS) libgphoto2_i.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo @SANEI_SANEI_JPEG_LO@ $(GPHOTO2_LIBS) $(JPEG_LIBS)
+libgt68xx_la_SOURCES = gt68xx.c gt68xx.h
+libgt68xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gt68xx
+nodist_libsane_gt68xx_la_SOURCES = gt68xx-s.c
+libsane_gt68xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gt68xx
+libsane_gt68xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_gt68xx_la_LIBADD = $(COMMON_LIBS) libgt68xx.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libhp_la_SOURCES = hp.c hp.h hp-accessor.c hp-accessor.h hp-device.c hp-device.h hp-handle.c hp-handle.h hp-hpmem.c hp-option.c hp-option.h hp-scl.c hp-scl.h hp-scsi.h
+libhp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp
+nodist_libsane_hp_la_SOURCES = hp-s.c
+libsane_hp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp
+libsane_hp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp_la_LIBADD = $(COMMON_LIBS) libhp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libhp3500_la_SOURCES = hp3500.c
+libhp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500
+nodist_libsane_hp3500_la_SOURCES = hp3500-s.c
+libsane_hp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500
+libsane_hp3500_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp3500_la_LIBADD = $(COMMON_LIBS) libhp3500.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libhp3900_la_SOURCES = hp3900.c
+libhp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900
+nodist_libsane_hp3900_la_SOURCES = hp3900-s.c
+libsane_hp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900
+libsane_hp3900_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp3900_la_LIBADD = $(COMMON_LIBS) libhp3900.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(TIFF_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libhp4200_la_SOURCES = hp4200.c hp4200.h
+libhp4200_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp4200
+nodist_libsane_hp4200_la_SOURCES = hp4200-s.c
+libsane_hp4200_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp4200
+libsane_hp4200_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp4200_la_LIBADD = $(COMMON_LIBS) libhp4200.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libhp5400_la_SOURCES = hp5400.c hp5400.h
+libhp5400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5400
+nodist_libsane_hp5400_la_SOURCES = hp5400-s.c
+libsane_hp5400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5400
+libsane_hp5400_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp5400_la_LIBADD = $(COMMON_LIBS) libhp5400.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libhp5590_la_SOURCES = hp5590.c
+libhp5590_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5590
+nodist_libsane_hp5590_la_SOURCES = hp5590-s.c
+libsane_hp5590_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5590
+libsane_hp5590_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hp5590_la_LIBADD = $(COMMON_LIBS) libhp5590.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libhpljm1005_la_SOURCES = hpljm1005.c
+libhpljm1005_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpljm1005
+nodist_libsane_hpljm1005_la_SOURCES = hpljm1005-s.c
+libsane_hpljm1005_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpljm1005
+libsane_hpljm1005_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hpljm1005_la_LIBADD = $(COMMON_LIBS) libhpljm1005.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libhpsj5s_la_SOURCES = hpsj5s.c hpsj5s.h
+libhpsj5s_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpsj5s
+nodist_libsane_hpsj5s_la_SOURCES = hpsj5s-s.c
+libsane_hpsj5s_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpsj5s
+libsane_hpsj5s_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hpsj5s_la_LIBADD = $(COMMON_LIBS) libhpsj5s.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(IEEE1284_LIBS)
+libhs2p_la_SOURCES = hs2p.c hs2p.h hs2p-saneopts.h
+libhs2p_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hs2p
+nodist_libsane_hs2p_la_SOURCES = hs2p-s.c
+libsane_hs2p_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hs2p
+libsane_hs2p_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_hs2p_la_LIBADD = $(COMMON_LIBS) libhs2p.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libibm_la_SOURCES = ibm.c ibm.h
+libibm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ibm
+nodist_libsane_ibm_la_SOURCES = ibm-s.c
+libsane_ibm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ibm
+libsane_ibm_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_ibm_la_LIBADD = $(COMMON_LIBS) libibm.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libkodak_la_SOURCES = kodak.c kodak.h kodak-cmd.h
+libkodak_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodak
+nodist_libsane_kodak_la_SOURCES = kodak-s.c
+libsane_kodak_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodak
+libsane_kodak_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kodak_la_LIBADD = $(COMMON_LIBS) libkodak.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libkodakaio_la_SOURCES = kodakaio.c kodakaio.h
+libkodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodakaio
+nodist_libsane_kodakaio_la_SOURCES = kodakaio-s.c
+libsane_kodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodakaio
+libsane_kodakaio_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kodakaio_la_LIBADD = $(COMMON_LIBS) libkodakaio.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo $(USB_LIBS) $(SOCKET_LIBS) $(AVAHI_LIBS) $(MATH_LIB) $(RESMGR_LIBS)
+libkvs1025_la_SOURCES = kvs1025.c kvs1025_low.c kvs1025_opt.c kvs1025_usb.c \
+ kvs1025.h kvs1025_low.h kvs1025_usb.h kvs1025_cmds.h
+
+libkvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
+nodist_libsane_kvs1025_la_SOURCES = kvs1025-s.c
+libsane_kvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
+libsane_kvs1025_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) libkvs1025.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libkvs20xx_la_SOURCES = kvs20xx.c kvs20xx_cmd.c kvs20xx_opt.c \
+ kvs20xx_cmd.h kvs20xx.h
+
+libkvs20xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs20xx
+nodist_libsane_kvs20xx_la_SOURCES = kvs20xx-s.c
+libsane_kvs20xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs20xx
+libsane_kvs20xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kvs20xx_la_LIBADD = $(COMMON_LIBS) libkvs20xx.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libkvs40xx_la_SOURCES = kvs40xx.c kvs40xx_cmd.c kvs40xx_opt.c \
+ kvs40xx.h
+
+libkvs40xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs40xx
+nodist_libsane_kvs40xx_la_SOURCES = kvs40xx-s.c
+libsane_kvs40xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs40xx
+libsane_kvs40xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_kvs40xx_la_LIBADD = $(COMMON_LIBS) libkvs40xx.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libleo_la_SOURCES = leo.c leo.h
+libleo_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=leo
+nodist_libsane_leo_la_SOURCES = leo-s.c
+libsane_leo_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=leo
+libsane_leo_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_leo_la_LIBADD = $(COMMON_LIBS) libleo.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+liblexmark_la_SOURCES = lexmark.c lexmark.h lexmark_low.c
+liblexmark_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark
+nodist_libsane_lexmark_la_SOURCES = lexmark-s.c
+libsane_lexmark_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark
+libsane_lexmark_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_lexmark_la_LIBADD = $(COMMON_LIBS) liblexmark.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libma1509_la_SOURCES = ma1509.c ma1509.h
+libma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509
+nodist_libsane_ma1509_la_SOURCES = ma1509-s.c
+libsane_ma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509
+libsane_ma1509_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_ma1509_la_LIBADD = $(COMMON_LIBS) libma1509.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libmagicolor_la_SOURCES = magicolor.c magicolor.h
+libmagicolor_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=magicolor
+nodist_libsane_magicolor_la_SOURCES = magicolor-s.c
+libsane_magicolor_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=magicolor
+libsane_magicolor_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_magicolor_la_LIBADD = $(COMMON_LIBS) libmagicolor.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo $(USB_LIBS) $(SOCKET_LIBS) $(MATH_LIB) $(RESMGR_LIBS)
+libmatsushita_la_SOURCES = matsushita.c matsushita.h
+libmatsushita_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=matsushita
+nodist_libsane_matsushita_la_SOURCES = matsushita-s.c
+libsane_matsushita_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=matsushita
+libsane_matsushita_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_matsushita_la_LIBADD = $(COMMON_LIBS) libmatsushita.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libmicrotek_la_SOURCES = microtek.c microtek.h
+libmicrotek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek
+nodist_libsane_microtek_la_SOURCES = microtek-s.c
+libsane_microtek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek
+libsane_microtek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_microtek_la_LIBADD = $(COMMON_LIBS) libmicrotek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+libmicrotek2_la_SOURCES = microtek2.c microtek2.h
+libmicrotek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2
+nodist_libsane_microtek2_la_SOURCES = microtek2-s.c
+libsane_microtek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2
+libsane_microtek2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_microtek2_la_LIBADD = $(COMMON_LIBS) libmicrotek2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libmustek_la_SOURCES = mustek.c mustek.h
+libmustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek
+nodist_libsane_mustek_la_SOURCES = mustek-s.c
+libsane_mustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek
+libsane_mustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_la_LIBADD = $(COMMON_LIBS) libmustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pa4s2.lo $(IEEE1284_LIBS) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libmustek_pp_la_SOURCES = mustek_pp.c mustek_pp.h mustek_pp_decl.h mustek_pp_drivers.h
+libmustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_pp
+nodist_libsane_mustek_pp_la_SOURCES = mustek_pp-s.c
+libsane_mustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_pp
+libsane_mustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_pp_la_LIBADD = $(COMMON_LIBS) libmustek_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pa4s2.lo $(MATH_LIB) $(IEEE1284_LIBS)
+libmustek_usb_la_SOURCES = mustek_usb.c mustek_usb.h
+libmustek_usb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb
+nodist_libsane_mustek_usb_la_SOURCES = mustek_usb-s.c
+libsane_mustek_usb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb
+libsane_mustek_usb_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_usb_la_LIBADD = $(COMMON_LIBS) libmustek_usb.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libmustek_usb2_la_SOURCES = mustek_usb2.c mustek_usb2.h
+libmustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2
+nodist_libsane_mustek_usb2_la_SOURCES = mustek_usb2-s.c
+libsane_mustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2
+libsane_mustek_usb2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_mustek_usb2_la_LIBADD = $(COMMON_LIBS) libmustek_usb2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(PTHREAD_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libnec_la_SOURCES = nec.c nec.h
+libnec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=nec
+nodist_libsane_nec_la_SOURCES = nec-s.c
+libsane_nec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=nec
+libsane_nec_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_nec_la_LIBADD = $(COMMON_LIBS) libnec.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+libnet_la_SOURCES = net.c net.h
+libnet_la_CPPFLAGS = $(AM_CPPFLAGS) @AVAHI_CFLAGS@ -DBACKEND_NAME=net
+nodist_libsane_net_la_SOURCES = net-s.c
+libsane_net_la_CPPFLAGS = $(AM_CPPFLAGS) @AVAHI_CFLAGS@ -DBACKEND_NAME=net
+libsane_net_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_net_la_LIBADD = $(COMMON_LIBS) libnet.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo $(AVAHI_LIBS) $(SOCKET_LIBS)
+libniash_la_SOURCES = niash.c
+libniash_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=niash
+nodist_libsane_niash_la_SOURCES = niash-s.c
+libsane_niash_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=niash
+libsane_niash_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_niash_la_LIBADD = $(COMMON_LIBS) libniash.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libpie_la_SOURCES = pie.c pie-scsidef.h
+libpie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie
+nodist_libsane_pie_la_SOURCES = pie-s.c
+libsane_pie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie
+libsane_pie_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pie_la_LIBADD = $(COMMON_LIBS) libpie.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libp5_la_SOURCES = p5.c p5.h p5_device.h
+libp5_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=p5
+nodist_libsane_p5_la_SOURCES = p5-s.c
+libsane_p5_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=p5
+libsane_p5_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_p5_la_LIBADD = $(COMMON_LIBS) libp5.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+libpint_la_SOURCES = pint.c pint.h
+libpint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint
+nodist_libsane_pint_la_SOURCES = pint-s.c
+libsane_pint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint
+libsane_pint_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pint_la_LIBADD = $(COMMON_LIBS) libpint.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+libpixma_la_SOURCES = pixma.c pixma.h pixma_io_sanei.c pixma_io.h pixma_common.c pixma_common.h pixma_mp150.c pixma_mp730.c pixma_mp750.c pixma_mp810.c pixma_imageclass.c pixma_bjnp.c pixma_bjnp.h pixma_bjnp_private.h pixma_rename.h
+libpixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma
+nodist_libsane_pixma_la_SOURCES = pixma-s.c
+libsane_pixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma
+libsane_pixma_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pixma_la_LIBADD = $(COMMON_LIBS) libpixma.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libplustek_la_SOURCES = plustek.c plustek.h
+libplustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
+nodist_libsane_plustek_la_SOURCES = plustek-s.c
+libsane_plustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
+libsane_plustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_plustek_la_LIBADD = $(COMMON_LIBS) libplustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libplustek_pp_la_SOURCES = plustek_pp.c plustek-pp.h
+libplustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp
+nodist_libsane_plustek_pp_la_SOURCES = plustek_pp-s.c
+libsane_plustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp
+libsane_plustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_plustek_pp_la_LIBADD = $(COMMON_LIBS) libplustek_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(IEEE1284_LIBS) $(PTHREAD_LIBS)
+libpnm_la_SOURCES = pnm.c
+libpnm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pnm
+nodist_libsane_pnm_la_SOURCES = pnm-s.c
+libsane_pnm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pnm
+libsane_pnm_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_pnm_la_LIBADD = $(COMMON_LIBS) libpnm.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
+libqcam_la_SOURCES = qcam.c qcam.h
+libqcam_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=qcam
+nodist_libsane_qcam_la_SOURCES = qcam-s.c
+libsane_qcam_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=qcam
+libsane_qcam_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_qcam_la_LIBADD = $(COMMON_LIBS) libqcam.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pio.lo
+libricoh_la_SOURCES = ricoh.c ricoh.h
+libricoh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh
+nodist_libsane_ricoh_la_SOURCES = ricoh-s.c
+libsane_ricoh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh
+libsane_ricoh_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_ricoh_la_LIBADD = $(COMMON_LIBS) libricoh.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+librts8891_la_SOURCES = rts8891.c rts8891.h rts88xx_lib.c rts88xx_lib.h
+librts8891_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=rts8891
+nodist_libsane_rts8891_la_SOURCES = rts8891-s.c
+libsane_rts8891_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=rts8891
+libsane_rts8891_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_rts8891_la_LIBADD = $(COMMON_LIBS) librts8891.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_usb.lo $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) $(RESMGR_LIBS)
+libs9036_la_SOURCES = s9036.c s9036.h
+libs9036_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=s9036
+nodist_libsane_s9036_la_SOURCES = s9036-s.c
+libsane_s9036_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=s9036
+libsane_s9036_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_s9036_la_LIBADD = $(COMMON_LIBS) libs9036.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libsceptre_la_SOURCES = sceptre.c sceptre.h
+libsceptre_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sceptre
+nodist_libsane_sceptre_la_SOURCES = sceptre-s.c
+libsane_sceptre_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sceptre
+libsane_sceptre_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sceptre_la_LIBADD = $(COMMON_LIBS) libsceptre.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libsharp_la_SOURCES = sharp.c sharp.h
+libsharp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sharp
+nodist_libsane_sharp_la_SOURCES = sharp-s.c
+libsane_sharp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sharp
+libsane_sharp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sharp_la_LIBADD = $(COMMON_LIBS) libsharp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS)
+libsm3600_la_SOURCES = sm3600.c sm3600.h
+libsm3600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3600
+nodist_libsane_sm3600_la_SOURCES = sm3600-s.c
+libsane_sm3600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3600
+libsane_sm3600_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sm3600_la_LIBADD = $(COMMON_LIBS) libsm3600.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libsm3840_la_SOURCES = sm3840.c sm3840.h sm3840_params.h
+libsm3840_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3840
+nodist_libsane_sm3840_la_SOURCES = sm3840-s.c
+libsane_sm3840_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3840
+libsane_sm3840_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sm3840_la_LIBADD = $(COMMON_LIBS) libsm3840.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libsnapscan_la_SOURCES = snapscan.c snapscan.h
+libsnapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan
+nodist_libsane_snapscan_la_SOURCES = snapscan-s.c
+libsane_snapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan
+libsane_snapscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_snapscan_la_LIBADD = $(COMMON_LIBS) libsnapscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsp15c_la_SOURCES = sp15c.c sp15c.h sp15c-scsi.h
+libsp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c
+nodist_libsane_sp15c_la_SOURCES = sp15c-s.c
+libsane_sp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c
+libsane_sp15c_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_sp15c_la_LIBADD = $(COMMON_LIBS) libsp15c.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libst400_la_SOURCES = st400.c st400.h
+libst400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=st400
+nodist_libsane_st400_la_SOURCES = st400-s.c ../sanei/sanei_scsi.lo
+libsane_st400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=st400
+libsane_st400_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_st400_la_LIBADD = $(COMMON_LIBS) libst400.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libstv680_la_SOURCES = stv680.c stv680.h
+libstv680_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=stv680
+nodist_libsane_stv680_la_SOURCES = stv680-s.c
+libsane_stv680_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=stv680
+libsane_stv680_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_stv680_la_LIBADD = $(COMMON_LIBS) libstv680.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
+libtamarack_la_SOURCES = tamarack.c tamarack.h
+libtamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack
+nodist_libsane_tamarack_la_SOURCES = tamarack-s.c
+libsane_tamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack
+libsane_tamarack_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_tamarack_la_LIBADD = $(COMMON_LIBS) libtamarack.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libtest_la_SOURCES = test.c test.h
+libtest_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test
+nodist_libsane_test_la_SOURCES = test-s.c
+libsane_test_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test
+libsane_test_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_test_la_LIBADD = $(COMMON_LIBS) libtest.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_thread.lo $(PTHREAD_LIBS)
+libteco1_la_SOURCES = teco1.c teco1.h
+libteco1_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco1
+nodist_libsane_teco1_la_SOURCES = teco1-s.c
+libsane_teco1_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco1
+libsane_teco1_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_teco1_la_LIBADD = $(COMMON_LIBS) libteco1.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libteco2_la_SOURCES = teco2.c teco2.h
+libteco2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco2
+nodist_libsane_teco2_la_SOURCES = teco2-s.c
+libsane_teco2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco2
+libsane_teco2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_teco2_la_LIBADD = $(COMMON_LIBS) libteco2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libteco3_la_SOURCES = teco3.c teco3.h
+libteco3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco3
+nodist_libsane_teco3_la_SOURCES = teco3-s.c
+libsane_teco3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco3
+libsane_teco3_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_teco3_la_LIBADD = $(COMMON_LIBS) libteco3.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
+libu12_la_SOURCES = u12.c u12.h
+libu12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12
+nodist_libsane_u12_la_SOURCES = u12-s.c
+libsane_u12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12
+libsane_u12_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_u12_la_LIBADD = $(COMMON_LIBS) libu12.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libumax_la_SOURCES = umax.c umax.h
+libumax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax
+nodist_libsane_umax_la_SOURCES = umax-s.c
+libsane_umax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax
+libsane_umax_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_umax_la_LIBADD = $(COMMON_LIBS) libumax.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libumax1220u_la_SOURCES = umax1220u.c
+libumax1220u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax1220u
+nodist_libsane_umax1220u_la_SOURCES = umax1220u-s.c
+libsane_umax1220u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax1220u
+libsane_umax1220u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_umax1220u_la_LIBADD = $(COMMON_LIBS) libumax1220u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
+libumax_pp_la_SOURCES = umax_pp.c umax_pp.h umax_pp_low.c umax_pp_low.h umax_pp_mid.c umax_pp_mid.h
+libumax_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax_pp
+nodist_libsane_umax_pp_la_SOURCES = umax_pp-s.c
+libsane_umax_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax_pp
+libsane_umax_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_umax_pp_la_LIBADD = $(COMMON_LIBS) libumax_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(MATH_LIB)
+libv4l_la_SOURCES = v4l.c v4l.h v4l-frequencies.h
+libv4l_la_CPPFLAGS = $(AM_CPPFLAGS) @LIBV4L_CFLAGS@ -DBACKEND_NAME=v4l
+nodist_libsane_v4l_la_SOURCES = v4l-s.c
+libsane_v4l_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=v4l
+libsane_v4l_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_v4l_la_LIBADD = $(COMMON_LIBS) libv4l.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(LIBV4L_LIBS)
+libxerox_mfp_la_SOURCES = xerox_mfp.c xerox_mfp-usb.c xerox_mfp-tcp.c xerox_mfp.h
+libxerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp
+nodist_libsane_xerox_mfp_la_SOURCES = xerox_mfp-s.c
+libsane_xerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp
+libsane_xerox_mfp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_xerox_mfp_la_LIBADD = $(COMMON_LIBS) libxerox_mfp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_tcp.lo $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
+libdll_preload_la_SOURCES = dll.c
+libdll_preload_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll -DENABLE_PRELOAD
+libdll_la_SOURCES = dll.c
+libdll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+BUILT_SOURCES = dll-preload.h
+nodist_libsane_dll_la_SOURCES = dll-s.c
+libsane_dll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+libsane_dll_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_dll_la_LIBADD = $(COMMON_LIBS) libdll.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(DL_LIBS)
+
+# libsane.la and libsane-dll.la are the same thing except for
+# the addition of backends listed by PRELOADABLE_BACKENDS that are
+# statically linked in.
+# Also, libsane.la goes into $(libdir) where as all libsane-*
+# (including libsane-dll.la) go into $(libdir)/sane
+
+# FIXME: Since we are throwing in the kitchen sink, might as
+# well link in ../sanei/libsanei.la instead. But currently,
+# libsanei.la is linking in sanei_auth which requires md5.
+# Shipping md5 could cause symbol conflicts with commonly used
+# md5 external libraries. Either need to prefix md5 with sanei_
+# (see liblib.la and snprintf), or move sanei_auth outside
+# of libsanei.
+#
+# FIXME: This is linking in every possible external library because there
+# is the off chance user is using PRELOADABLE_BACKENDS that may need
+# them. Since standard mode is to only have the dll backend, its a waste.
+# Need to update configure to build a list of only what needs to go into
+# LIBADD based on whats being preloaded.
+nodist_libsane_la_SOURCES = dll-s.c
+libsane_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+libsane_la_LDFLAGS = $(DIST_LIBS_LDFLAGS)
+libsane_la_LIBADD = $(COMMON_LIBS) @PRELOADABLE_BACKENDS_ENABLED@ libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo $(DL_LIBS) $(LIBV4L_LIBS) $(MATH_LIB) $(IEEE1284_LIBS) $(TIFF_LIBS) $(JPEG_LIBS) $(GPHOTO2_LIBS) $(SOCKET_LIBS) $(USB_LIBS) $(AVAHI_LIBS) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+
+# WARNING: Automake is getting this wrong so have to do it ourselves.
+libsane_la_DEPENDENCIES = $(COMMON_LIBS) @PRELOADABLE_BACKENDS_ENABLED@ libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo @SANEI_SANEI_JPEG_LO@
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .conf.in .conf .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu backend/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu backend/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+install-sanelibLTLIBRARIES: $(sanelib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(sanelib_LTLIBRARIES)'; test -n "$(sanelibdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sanelibdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sanelibdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(sanelibdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(sanelibdir)"; \
+ }
+
+uninstall-sanelibLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sanelib_LTLIBRARIES)'; test -n "$(sanelibdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(sanelibdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(sanelibdir)/$$f"; \
+ done
+
+clean-sanelibLTLIBRARIES:
+ -test -z "$(sanelib_LTLIBRARIES)" || rm -f $(sanelib_LTLIBRARIES)
+ @list='$(sanelib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libabaton.la: $(libabaton_la_OBJECTS) $(libabaton_la_DEPENDENCIES) $(EXTRA_libabaton_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libabaton_la_OBJECTS) $(libabaton_la_LIBADD) $(LIBS)
+
+libagfafocus.la: $(libagfafocus_la_OBJECTS) $(libagfafocus_la_DEPENDENCIES) $(EXTRA_libagfafocus_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libagfafocus_la_OBJECTS) $(libagfafocus_la_LIBADD) $(LIBS)
+
+libapple.la: $(libapple_la_OBJECTS) $(libapple_la_DEPENDENCIES) $(EXTRA_libapple_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libapple_la_OBJECTS) $(libapple_la_LIBADD) $(LIBS)
+
+libartec.la: $(libartec_la_OBJECTS) $(libartec_la_DEPENDENCIES) $(EXTRA_libartec_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libartec_la_OBJECTS) $(libartec_la_LIBADD) $(LIBS)
+
+libartec_eplus48u.la: $(libartec_eplus48u_la_OBJECTS) $(libartec_eplus48u_la_DEPENDENCIES) $(EXTRA_libartec_eplus48u_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libartec_eplus48u_la_OBJECTS) $(libartec_eplus48u_la_LIBADD) $(LIBS)
+
+libas6e.la: $(libas6e_la_OBJECTS) $(libas6e_la_DEPENDENCIES) $(EXTRA_libas6e_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libas6e_la_OBJECTS) $(libas6e_la_LIBADD) $(LIBS)
+
+libavision.la: $(libavision_la_OBJECTS) $(libavision_la_DEPENDENCIES) $(EXTRA_libavision_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libavision_la_OBJECTS) $(libavision_la_LIBADD) $(LIBS)
+
+libbh.la: $(libbh_la_OBJECTS) $(libbh_la_DEPENDENCIES) $(EXTRA_libbh_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libbh_la_OBJECTS) $(libbh_la_LIBADD) $(LIBS)
+
+libcanon.la: $(libcanon_la_OBJECTS) $(libcanon_la_DEPENDENCIES) $(EXTRA_libcanon_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcanon_la_OBJECTS) $(libcanon_la_LIBADD) $(LIBS)
+
+libcanon630u.la: $(libcanon630u_la_OBJECTS) $(libcanon630u_la_DEPENDENCIES) $(EXTRA_libcanon630u_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcanon630u_la_OBJECTS) $(libcanon630u_la_LIBADD) $(LIBS)
+
+libcanon_dr.la: $(libcanon_dr_la_OBJECTS) $(libcanon_dr_la_DEPENDENCIES) $(EXTRA_libcanon_dr_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcanon_dr_la_OBJECTS) $(libcanon_dr_la_LIBADD) $(LIBS)
+
+libcanon_pp.la: $(libcanon_pp_la_OBJECTS) $(libcanon_pp_la_DEPENDENCIES) $(EXTRA_libcanon_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcanon_pp_la_OBJECTS) $(libcanon_pp_la_LIBADD) $(LIBS)
+
+libcardscan.la: $(libcardscan_la_OBJECTS) $(libcardscan_la_DEPENDENCIES) $(EXTRA_libcardscan_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcardscan_la_OBJECTS) $(libcardscan_la_LIBADD) $(LIBS)
+
+libcoolscan.la: $(libcoolscan_la_OBJECTS) $(libcoolscan_la_DEPENDENCIES) $(EXTRA_libcoolscan_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcoolscan_la_OBJECTS) $(libcoolscan_la_LIBADD) $(LIBS)
+
+libcoolscan2.la: $(libcoolscan2_la_OBJECTS) $(libcoolscan2_la_DEPENDENCIES) $(EXTRA_libcoolscan2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcoolscan2_la_OBJECTS) $(libcoolscan2_la_LIBADD) $(LIBS)
+
+libcoolscan3.la: $(libcoolscan3_la_OBJECTS) $(libcoolscan3_la_DEPENDENCIES) $(EXTRA_libcoolscan3_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libcoolscan3_la_OBJECTS) $(libcoolscan3_la_LIBADD) $(LIBS)
+
+libdc210.la: $(libdc210_la_OBJECTS) $(libdc210_la_DEPENDENCIES) $(EXTRA_libdc210_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdc210_la_OBJECTS) $(libdc210_la_LIBADD) $(LIBS)
+
+libdc240.la: $(libdc240_la_OBJECTS) $(libdc240_la_DEPENDENCIES) $(EXTRA_libdc240_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdc240_la_OBJECTS) $(libdc240_la_LIBADD) $(LIBS)
+
+libdc25.la: $(libdc25_la_OBJECTS) $(libdc25_la_DEPENDENCIES) $(EXTRA_libdc25_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdc25_la_OBJECTS) $(libdc25_la_LIBADD) $(LIBS)
+
+libdell1600n_net.la: $(libdell1600n_net_la_OBJECTS) $(libdell1600n_net_la_DEPENDENCIES) $(EXTRA_libdell1600n_net_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdell1600n_net_la_OBJECTS) $(libdell1600n_net_la_LIBADD) $(LIBS)
+
+libdll.la: $(libdll_la_OBJECTS) $(libdll_la_DEPENDENCIES) $(EXTRA_libdll_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdll_la_OBJECTS) $(libdll_la_LIBADD) $(LIBS)
+
+libdll_preload.la: $(libdll_preload_la_OBJECTS) $(libdll_preload_la_DEPENDENCIES) $(EXTRA_libdll_preload_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdll_preload_la_OBJECTS) $(libdll_preload_la_LIBADD) $(LIBS)
+
+libdmc.la: $(libdmc_la_OBJECTS) $(libdmc_la_DEPENDENCIES) $(EXTRA_libdmc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdmc_la_OBJECTS) $(libdmc_la_LIBADD) $(LIBS)
+
+libepjitsu.la: $(libepjitsu_la_OBJECTS) $(libepjitsu_la_DEPENDENCIES) $(EXTRA_libepjitsu_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libepjitsu_la_OBJECTS) $(libepjitsu_la_LIBADD) $(LIBS)
+
+libepson.la: $(libepson_la_OBJECTS) $(libepson_la_DEPENDENCIES) $(EXTRA_libepson_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libepson_la_OBJECTS) $(libepson_la_LIBADD) $(LIBS)
+
+libepson2.la: $(libepson2_la_OBJECTS) $(libepson2_la_DEPENDENCIES) $(EXTRA_libepson2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libepson2_la_OBJECTS) $(libepson2_la_LIBADD) $(LIBS)
+
+libfujitsu.la: $(libfujitsu_la_OBJECTS) $(libfujitsu_la_DEPENDENCIES) $(EXTRA_libfujitsu_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libfujitsu_la_OBJECTS) $(libfujitsu_la_LIBADD) $(LIBS)
+
+libgenesys.la: $(libgenesys_la_OBJECTS) $(libgenesys_la_DEPENDENCIES) $(EXTRA_libgenesys_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libgenesys_la_OBJECTS) $(libgenesys_la_LIBADD) $(LIBS)
+
+libgphoto2_i.la: $(libgphoto2_i_la_OBJECTS) $(libgphoto2_i_la_DEPENDENCIES) $(EXTRA_libgphoto2_i_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libgphoto2_i_la_OBJECTS) $(libgphoto2_i_la_LIBADD) $(LIBS)
+
+libgt68xx.la: $(libgt68xx_la_OBJECTS) $(libgt68xx_la_DEPENDENCIES) $(EXTRA_libgt68xx_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libgt68xx_la_OBJECTS) $(libgt68xx_la_LIBADD) $(LIBS)
+
+libhp.la: $(libhp_la_OBJECTS) $(libhp_la_DEPENDENCIES) $(EXTRA_libhp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhp_la_OBJECTS) $(libhp_la_LIBADD) $(LIBS)
+
+libhp3500.la: $(libhp3500_la_OBJECTS) $(libhp3500_la_DEPENDENCIES) $(EXTRA_libhp3500_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhp3500_la_OBJECTS) $(libhp3500_la_LIBADD) $(LIBS)
+
+libhp3900.la: $(libhp3900_la_OBJECTS) $(libhp3900_la_DEPENDENCIES) $(EXTRA_libhp3900_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhp3900_la_OBJECTS) $(libhp3900_la_LIBADD) $(LIBS)
+
+libhp4200.la: $(libhp4200_la_OBJECTS) $(libhp4200_la_DEPENDENCIES) $(EXTRA_libhp4200_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhp4200_la_OBJECTS) $(libhp4200_la_LIBADD) $(LIBS)
+
+libhp5400.la: $(libhp5400_la_OBJECTS) $(libhp5400_la_DEPENDENCIES) $(EXTRA_libhp5400_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhp5400_la_OBJECTS) $(libhp5400_la_LIBADD) $(LIBS)
+
+libhp5590.la: $(libhp5590_la_OBJECTS) $(libhp5590_la_DEPENDENCIES) $(EXTRA_libhp5590_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhp5590_la_OBJECTS) $(libhp5590_la_LIBADD) $(LIBS)
+
+libhpljm1005.la: $(libhpljm1005_la_OBJECTS) $(libhpljm1005_la_DEPENDENCIES) $(EXTRA_libhpljm1005_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhpljm1005_la_OBJECTS) $(libhpljm1005_la_LIBADD) $(LIBS)
+
+libhpsj5s.la: $(libhpsj5s_la_OBJECTS) $(libhpsj5s_la_DEPENDENCIES) $(EXTRA_libhpsj5s_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhpsj5s_la_OBJECTS) $(libhpsj5s_la_LIBADD) $(LIBS)
+
+libhs2p.la: $(libhs2p_la_OBJECTS) $(libhs2p_la_DEPENDENCIES) $(EXTRA_libhs2p_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhs2p_la_OBJECTS) $(libhs2p_la_LIBADD) $(LIBS)
+
+libibm.la: $(libibm_la_OBJECTS) $(libibm_la_DEPENDENCIES) $(EXTRA_libibm_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libibm_la_OBJECTS) $(libibm_la_LIBADD) $(LIBS)
+
+libkodak.la: $(libkodak_la_OBJECTS) $(libkodak_la_DEPENDENCIES) $(EXTRA_libkodak_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libkodak_la_OBJECTS) $(libkodak_la_LIBADD) $(LIBS)
+
+libkodakaio.la: $(libkodakaio_la_OBJECTS) $(libkodakaio_la_DEPENDENCIES) $(EXTRA_libkodakaio_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libkodakaio_la_OBJECTS) $(libkodakaio_la_LIBADD) $(LIBS)
+
+libkvs1025.la: $(libkvs1025_la_OBJECTS) $(libkvs1025_la_DEPENDENCIES) $(EXTRA_libkvs1025_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libkvs1025_la_OBJECTS) $(libkvs1025_la_LIBADD) $(LIBS)
+
+libkvs20xx.la: $(libkvs20xx_la_OBJECTS) $(libkvs20xx_la_DEPENDENCIES) $(EXTRA_libkvs20xx_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libkvs20xx_la_OBJECTS) $(libkvs20xx_la_LIBADD) $(LIBS)
+
+libkvs40xx.la: $(libkvs40xx_la_OBJECTS) $(libkvs40xx_la_DEPENDENCIES) $(EXTRA_libkvs40xx_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libkvs40xx_la_OBJECTS) $(libkvs40xx_la_LIBADD) $(LIBS)
+
+libleo.la: $(libleo_la_OBJECTS) $(libleo_la_DEPENDENCIES) $(EXTRA_libleo_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libleo_la_OBJECTS) $(libleo_la_LIBADD) $(LIBS)
+
+liblexmark.la: $(liblexmark_la_OBJECTS) $(liblexmark_la_DEPENDENCIES) $(EXTRA_liblexmark_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(liblexmark_la_OBJECTS) $(liblexmark_la_LIBADD) $(LIBS)
+
+libma1509.la: $(libma1509_la_OBJECTS) $(libma1509_la_DEPENDENCIES) $(EXTRA_libma1509_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libma1509_la_OBJECTS) $(libma1509_la_LIBADD) $(LIBS)
+
+libmagicolor.la: $(libmagicolor_la_OBJECTS) $(libmagicolor_la_DEPENDENCIES) $(EXTRA_libmagicolor_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmagicolor_la_OBJECTS) $(libmagicolor_la_LIBADD) $(LIBS)
+
+libmatsushita.la: $(libmatsushita_la_OBJECTS) $(libmatsushita_la_DEPENDENCIES) $(EXTRA_libmatsushita_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmatsushita_la_OBJECTS) $(libmatsushita_la_LIBADD) $(LIBS)
+
+libmicrotek.la: $(libmicrotek_la_OBJECTS) $(libmicrotek_la_DEPENDENCIES) $(EXTRA_libmicrotek_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmicrotek_la_OBJECTS) $(libmicrotek_la_LIBADD) $(LIBS)
+
+libmicrotek2.la: $(libmicrotek2_la_OBJECTS) $(libmicrotek2_la_DEPENDENCIES) $(EXTRA_libmicrotek2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmicrotek2_la_OBJECTS) $(libmicrotek2_la_LIBADD) $(LIBS)
+
+libmustek.la: $(libmustek_la_OBJECTS) $(libmustek_la_DEPENDENCIES) $(EXTRA_libmustek_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmustek_la_OBJECTS) $(libmustek_la_LIBADD) $(LIBS)
+
+libmustek_pp.la: $(libmustek_pp_la_OBJECTS) $(libmustek_pp_la_DEPENDENCIES) $(EXTRA_libmustek_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmustek_pp_la_OBJECTS) $(libmustek_pp_la_LIBADD) $(LIBS)
+
+libmustek_usb.la: $(libmustek_usb_la_OBJECTS) $(libmustek_usb_la_DEPENDENCIES) $(EXTRA_libmustek_usb_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmustek_usb_la_OBJECTS) $(libmustek_usb_la_LIBADD) $(LIBS)
+
+libmustek_usb2.la: $(libmustek_usb2_la_OBJECTS) $(libmustek_usb2_la_DEPENDENCIES) $(EXTRA_libmustek_usb2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libmustek_usb2_la_OBJECTS) $(libmustek_usb2_la_LIBADD) $(LIBS)
+
+libnec.la: $(libnec_la_OBJECTS) $(libnec_la_DEPENDENCIES) $(EXTRA_libnec_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libnec_la_OBJECTS) $(libnec_la_LIBADD) $(LIBS)
+
+libnet.la: $(libnet_la_OBJECTS) $(libnet_la_DEPENDENCIES) $(EXTRA_libnet_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libnet_la_OBJECTS) $(libnet_la_LIBADD) $(LIBS)
+
+libniash.la: $(libniash_la_OBJECTS) $(libniash_la_DEPENDENCIES) $(EXTRA_libniash_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libniash_la_OBJECTS) $(libniash_la_LIBADD) $(LIBS)
+
+libp5.la: $(libp5_la_OBJECTS) $(libp5_la_DEPENDENCIES) $(EXTRA_libp5_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libp5_la_OBJECTS) $(libp5_la_LIBADD) $(LIBS)
+
+libpie.la: $(libpie_la_OBJECTS) $(libpie_la_DEPENDENCIES) $(EXTRA_libpie_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libpie_la_OBJECTS) $(libpie_la_LIBADD) $(LIBS)
+
+libpint.la: $(libpint_la_OBJECTS) $(libpint_la_DEPENDENCIES) $(EXTRA_libpint_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libpint_la_OBJECTS) $(libpint_la_LIBADD) $(LIBS)
+
+libpixma.la: $(libpixma_la_OBJECTS) $(libpixma_la_DEPENDENCIES) $(EXTRA_libpixma_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libpixma_la_OBJECTS) $(libpixma_la_LIBADD) $(LIBS)
+
+libplustek.la: $(libplustek_la_OBJECTS) $(libplustek_la_DEPENDENCIES) $(EXTRA_libplustek_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libplustek_la_OBJECTS) $(libplustek_la_LIBADD) $(LIBS)
+
+libplustek_pp.la: $(libplustek_pp_la_OBJECTS) $(libplustek_pp_la_DEPENDENCIES) $(EXTRA_libplustek_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libplustek_pp_la_OBJECTS) $(libplustek_pp_la_LIBADD) $(LIBS)
+
+libpnm.la: $(libpnm_la_OBJECTS) $(libpnm_la_DEPENDENCIES) $(EXTRA_libpnm_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libpnm_la_OBJECTS) $(libpnm_la_LIBADD) $(LIBS)
+
+libqcam.la: $(libqcam_la_OBJECTS) $(libqcam_la_DEPENDENCIES) $(EXTRA_libqcam_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libqcam_la_OBJECTS) $(libqcam_la_LIBADD) $(LIBS)
+
+libricoh.la: $(libricoh_la_OBJECTS) $(libricoh_la_DEPENDENCIES) $(EXTRA_libricoh_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libricoh_la_OBJECTS) $(libricoh_la_LIBADD) $(LIBS)
+
+librts8891.la: $(librts8891_la_OBJECTS) $(librts8891_la_DEPENDENCIES) $(EXTRA_librts8891_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(librts8891_la_OBJECTS) $(librts8891_la_LIBADD) $(LIBS)
+
+libs9036.la: $(libs9036_la_OBJECTS) $(libs9036_la_DEPENDENCIES) $(EXTRA_libs9036_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libs9036_la_OBJECTS) $(libs9036_la_LIBADD) $(LIBS)
+
+libsane-abaton.la: $(libsane_abaton_la_OBJECTS) $(libsane_abaton_la_DEPENDENCIES) $(EXTRA_libsane_abaton_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_abaton_la_LINK) $(libsane_abaton_la_OBJECTS) $(libsane_abaton_la_LIBADD) $(LIBS)
+
+libsane-agfafocus.la: $(libsane_agfafocus_la_OBJECTS) $(libsane_agfafocus_la_DEPENDENCIES) $(EXTRA_libsane_agfafocus_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_agfafocus_la_LINK) $(libsane_agfafocus_la_OBJECTS) $(libsane_agfafocus_la_LIBADD) $(LIBS)
+
+libsane-apple.la: $(libsane_apple_la_OBJECTS) $(libsane_apple_la_DEPENDENCIES) $(EXTRA_libsane_apple_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_apple_la_LINK) $(libsane_apple_la_OBJECTS) $(libsane_apple_la_LIBADD) $(LIBS)
+
+libsane-artec.la: $(libsane_artec_la_OBJECTS) $(libsane_artec_la_DEPENDENCIES) $(EXTRA_libsane_artec_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_artec_la_LINK) $(libsane_artec_la_OBJECTS) $(libsane_artec_la_LIBADD) $(LIBS)
+
+libsane-artec_eplus48u.la: $(libsane_artec_eplus48u_la_OBJECTS) $(libsane_artec_eplus48u_la_DEPENDENCIES) $(EXTRA_libsane_artec_eplus48u_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_artec_eplus48u_la_LINK) $(libsane_artec_eplus48u_la_OBJECTS) $(libsane_artec_eplus48u_la_LIBADD) $(LIBS)
+
+libsane-as6e.la: $(libsane_as6e_la_OBJECTS) $(libsane_as6e_la_DEPENDENCIES) $(EXTRA_libsane_as6e_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_as6e_la_LINK) $(libsane_as6e_la_OBJECTS) $(libsane_as6e_la_LIBADD) $(LIBS)
+
+libsane-avision.la: $(libsane_avision_la_OBJECTS) $(libsane_avision_la_DEPENDENCIES) $(EXTRA_libsane_avision_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_avision_la_LINK) $(libsane_avision_la_OBJECTS) $(libsane_avision_la_LIBADD) $(LIBS)
+
+libsane-bh.la: $(libsane_bh_la_OBJECTS) $(libsane_bh_la_DEPENDENCIES) $(EXTRA_libsane_bh_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_bh_la_LINK) $(libsane_bh_la_OBJECTS) $(libsane_bh_la_LIBADD) $(LIBS)
+
+libsane-canon.la: $(libsane_canon_la_OBJECTS) $(libsane_canon_la_DEPENDENCIES) $(EXTRA_libsane_canon_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_canon_la_LINK) $(libsane_canon_la_OBJECTS) $(libsane_canon_la_LIBADD) $(LIBS)
+
+libsane-canon630u.la: $(libsane_canon630u_la_OBJECTS) $(libsane_canon630u_la_DEPENDENCIES) $(EXTRA_libsane_canon630u_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_canon630u_la_LINK) $(libsane_canon630u_la_OBJECTS) $(libsane_canon630u_la_LIBADD) $(LIBS)
+
+libsane-canon_dr.la: $(libsane_canon_dr_la_OBJECTS) $(libsane_canon_dr_la_DEPENDENCIES) $(EXTRA_libsane_canon_dr_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_canon_dr_la_LINK) $(libsane_canon_dr_la_OBJECTS) $(libsane_canon_dr_la_LIBADD) $(LIBS)
+
+libsane-canon_pp.la: $(libsane_canon_pp_la_OBJECTS) $(libsane_canon_pp_la_DEPENDENCIES) $(EXTRA_libsane_canon_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_canon_pp_la_LINK) $(libsane_canon_pp_la_OBJECTS) $(libsane_canon_pp_la_LIBADD) $(LIBS)
+
+libsane-cardscan.la: $(libsane_cardscan_la_OBJECTS) $(libsane_cardscan_la_DEPENDENCIES) $(EXTRA_libsane_cardscan_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_cardscan_la_LINK) $(libsane_cardscan_la_OBJECTS) $(libsane_cardscan_la_LIBADD) $(LIBS)
+
+libsane-coolscan.la: $(libsane_coolscan_la_OBJECTS) $(libsane_coolscan_la_DEPENDENCIES) $(EXTRA_libsane_coolscan_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_coolscan_la_LINK) $(libsane_coolscan_la_OBJECTS) $(libsane_coolscan_la_LIBADD) $(LIBS)
+
+libsane-coolscan2.la: $(libsane_coolscan2_la_OBJECTS) $(libsane_coolscan2_la_DEPENDENCIES) $(EXTRA_libsane_coolscan2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_coolscan2_la_LINK) $(libsane_coolscan2_la_OBJECTS) $(libsane_coolscan2_la_LIBADD) $(LIBS)
+
+libsane-coolscan3.la: $(libsane_coolscan3_la_OBJECTS) $(libsane_coolscan3_la_DEPENDENCIES) $(EXTRA_libsane_coolscan3_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_coolscan3_la_LINK) $(libsane_coolscan3_la_OBJECTS) $(libsane_coolscan3_la_LIBADD) $(LIBS)
+
+libsane-dc210.la: $(libsane_dc210_la_OBJECTS) $(libsane_dc210_la_DEPENDENCIES) $(EXTRA_libsane_dc210_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_dc210_la_LINK) $(libsane_dc210_la_OBJECTS) $(libsane_dc210_la_LIBADD) $(LIBS)
+
+libsane-dc240.la: $(libsane_dc240_la_OBJECTS) $(libsane_dc240_la_DEPENDENCIES) $(EXTRA_libsane_dc240_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_dc240_la_LINK) $(libsane_dc240_la_OBJECTS) $(libsane_dc240_la_LIBADD) $(LIBS)
+
+libsane-dc25.la: $(libsane_dc25_la_OBJECTS) $(libsane_dc25_la_DEPENDENCIES) $(EXTRA_libsane_dc25_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_dc25_la_LINK) $(libsane_dc25_la_OBJECTS) $(libsane_dc25_la_LIBADD) $(LIBS)
+
+libsane-dell1600n_net.la: $(libsane_dell1600n_net_la_OBJECTS) $(libsane_dell1600n_net_la_DEPENDENCIES) $(EXTRA_libsane_dell1600n_net_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_dell1600n_net_la_LINK) $(libsane_dell1600n_net_la_OBJECTS) $(libsane_dell1600n_net_la_LIBADD) $(LIBS)
+
+libsane-dll.la: $(libsane_dll_la_OBJECTS) $(libsane_dll_la_DEPENDENCIES) $(EXTRA_libsane_dll_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_dll_la_LINK) -rpath $(sanelibdir) $(libsane_dll_la_OBJECTS) $(libsane_dll_la_LIBADD) $(LIBS)
+
+libsane-dmc.la: $(libsane_dmc_la_OBJECTS) $(libsane_dmc_la_DEPENDENCIES) $(EXTRA_libsane_dmc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_dmc_la_LINK) $(libsane_dmc_la_OBJECTS) $(libsane_dmc_la_LIBADD) $(LIBS)
+
+libsane-epjitsu.la: $(libsane_epjitsu_la_OBJECTS) $(libsane_epjitsu_la_DEPENDENCIES) $(EXTRA_libsane_epjitsu_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_epjitsu_la_LINK) $(libsane_epjitsu_la_OBJECTS) $(libsane_epjitsu_la_LIBADD) $(LIBS)
+
+libsane-epson.la: $(libsane_epson_la_OBJECTS) $(libsane_epson_la_DEPENDENCIES) $(EXTRA_libsane_epson_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_epson_la_LINK) $(libsane_epson_la_OBJECTS) $(libsane_epson_la_LIBADD) $(LIBS)
+
+libsane-epson2.la: $(libsane_epson2_la_OBJECTS) $(libsane_epson2_la_DEPENDENCIES) $(EXTRA_libsane_epson2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_epson2_la_LINK) $(libsane_epson2_la_OBJECTS) $(libsane_epson2_la_LIBADD) $(LIBS)
+
+libsane-fujitsu.la: $(libsane_fujitsu_la_OBJECTS) $(libsane_fujitsu_la_DEPENDENCIES) $(EXTRA_libsane_fujitsu_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_fujitsu_la_LINK) $(libsane_fujitsu_la_OBJECTS) $(libsane_fujitsu_la_LIBADD) $(LIBS)
+
+libsane-genesys.la: $(libsane_genesys_la_OBJECTS) $(libsane_genesys_la_DEPENDENCIES) $(EXTRA_libsane_genesys_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_genesys_la_LINK) $(libsane_genesys_la_OBJECTS) $(libsane_genesys_la_LIBADD) $(LIBS)
+
+libsane-gphoto2.la: $(libsane_gphoto2_la_OBJECTS) $(libsane_gphoto2_la_DEPENDENCIES) $(EXTRA_libsane_gphoto2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_gphoto2_la_LINK) $(libsane_gphoto2_la_OBJECTS) $(libsane_gphoto2_la_LIBADD) $(LIBS)
+
+libsane-gt68xx.la: $(libsane_gt68xx_la_OBJECTS) $(libsane_gt68xx_la_DEPENDENCIES) $(EXTRA_libsane_gt68xx_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_gt68xx_la_LINK) $(libsane_gt68xx_la_OBJECTS) $(libsane_gt68xx_la_LIBADD) $(LIBS)
+
+libsane-hp.la: $(libsane_hp_la_OBJECTS) $(libsane_hp_la_DEPENDENCIES) $(EXTRA_libsane_hp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hp_la_LINK) $(libsane_hp_la_OBJECTS) $(libsane_hp_la_LIBADD) $(LIBS)
+
+libsane-hp3500.la: $(libsane_hp3500_la_OBJECTS) $(libsane_hp3500_la_DEPENDENCIES) $(EXTRA_libsane_hp3500_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hp3500_la_LINK) $(libsane_hp3500_la_OBJECTS) $(libsane_hp3500_la_LIBADD) $(LIBS)
+
+libsane-hp3900.la: $(libsane_hp3900_la_OBJECTS) $(libsane_hp3900_la_DEPENDENCIES) $(EXTRA_libsane_hp3900_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hp3900_la_LINK) $(libsane_hp3900_la_OBJECTS) $(libsane_hp3900_la_LIBADD) $(LIBS)
+
+libsane-hp4200.la: $(libsane_hp4200_la_OBJECTS) $(libsane_hp4200_la_DEPENDENCIES) $(EXTRA_libsane_hp4200_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hp4200_la_LINK) $(libsane_hp4200_la_OBJECTS) $(libsane_hp4200_la_LIBADD) $(LIBS)
+
+libsane-hp5400.la: $(libsane_hp5400_la_OBJECTS) $(libsane_hp5400_la_DEPENDENCIES) $(EXTRA_libsane_hp5400_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hp5400_la_LINK) $(libsane_hp5400_la_OBJECTS) $(libsane_hp5400_la_LIBADD) $(LIBS)
+
+libsane-hp5590.la: $(libsane_hp5590_la_OBJECTS) $(libsane_hp5590_la_DEPENDENCIES) $(EXTRA_libsane_hp5590_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hp5590_la_LINK) $(libsane_hp5590_la_OBJECTS) $(libsane_hp5590_la_LIBADD) $(LIBS)
+
+libsane-hpljm1005.la: $(libsane_hpljm1005_la_OBJECTS) $(libsane_hpljm1005_la_DEPENDENCIES) $(EXTRA_libsane_hpljm1005_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hpljm1005_la_LINK) $(libsane_hpljm1005_la_OBJECTS) $(libsane_hpljm1005_la_LIBADD) $(LIBS)
+
+libsane-hpsj5s.la: $(libsane_hpsj5s_la_OBJECTS) $(libsane_hpsj5s_la_DEPENDENCIES) $(EXTRA_libsane_hpsj5s_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hpsj5s_la_LINK) $(libsane_hpsj5s_la_OBJECTS) $(libsane_hpsj5s_la_LIBADD) $(LIBS)
+
+libsane-hs2p.la: $(libsane_hs2p_la_OBJECTS) $(libsane_hs2p_la_DEPENDENCIES) $(EXTRA_libsane_hs2p_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_hs2p_la_LINK) $(libsane_hs2p_la_OBJECTS) $(libsane_hs2p_la_LIBADD) $(LIBS)
+
+libsane-ibm.la: $(libsane_ibm_la_OBJECTS) $(libsane_ibm_la_DEPENDENCIES) $(EXTRA_libsane_ibm_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_ibm_la_LINK) $(libsane_ibm_la_OBJECTS) $(libsane_ibm_la_LIBADD) $(LIBS)
+
+libsane-kodak.la: $(libsane_kodak_la_OBJECTS) $(libsane_kodak_la_DEPENDENCIES) $(EXTRA_libsane_kodak_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_kodak_la_LINK) $(libsane_kodak_la_OBJECTS) $(libsane_kodak_la_LIBADD) $(LIBS)
+
+libsane-kodakaio.la: $(libsane_kodakaio_la_OBJECTS) $(libsane_kodakaio_la_DEPENDENCIES) $(EXTRA_libsane_kodakaio_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_kodakaio_la_LINK) $(libsane_kodakaio_la_OBJECTS) $(libsane_kodakaio_la_LIBADD) $(LIBS)
+
+libsane-kvs1025.la: $(libsane_kvs1025_la_OBJECTS) $(libsane_kvs1025_la_DEPENDENCIES) $(EXTRA_libsane_kvs1025_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_kvs1025_la_LINK) $(libsane_kvs1025_la_OBJECTS) $(libsane_kvs1025_la_LIBADD) $(LIBS)
+
+libsane-kvs20xx.la: $(libsane_kvs20xx_la_OBJECTS) $(libsane_kvs20xx_la_DEPENDENCIES) $(EXTRA_libsane_kvs20xx_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_kvs20xx_la_LINK) $(libsane_kvs20xx_la_OBJECTS) $(libsane_kvs20xx_la_LIBADD) $(LIBS)
+
+libsane-kvs40xx.la: $(libsane_kvs40xx_la_OBJECTS) $(libsane_kvs40xx_la_DEPENDENCIES) $(EXTRA_libsane_kvs40xx_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_kvs40xx_la_LINK) $(libsane_kvs40xx_la_OBJECTS) $(libsane_kvs40xx_la_LIBADD) $(LIBS)
+
+libsane-leo.la: $(libsane_leo_la_OBJECTS) $(libsane_leo_la_DEPENDENCIES) $(EXTRA_libsane_leo_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_leo_la_LINK) $(libsane_leo_la_OBJECTS) $(libsane_leo_la_LIBADD) $(LIBS)
+
+libsane-lexmark.la: $(libsane_lexmark_la_OBJECTS) $(libsane_lexmark_la_DEPENDENCIES) $(EXTRA_libsane_lexmark_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_lexmark_la_LINK) $(libsane_lexmark_la_OBJECTS) $(libsane_lexmark_la_LIBADD) $(LIBS)
+
+libsane-ma1509.la: $(libsane_ma1509_la_OBJECTS) $(libsane_ma1509_la_DEPENDENCIES) $(EXTRA_libsane_ma1509_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_ma1509_la_LINK) $(libsane_ma1509_la_OBJECTS) $(libsane_ma1509_la_LIBADD) $(LIBS)
+
+libsane-magicolor.la: $(libsane_magicolor_la_OBJECTS) $(libsane_magicolor_la_DEPENDENCIES) $(EXTRA_libsane_magicolor_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_magicolor_la_LINK) $(libsane_magicolor_la_OBJECTS) $(libsane_magicolor_la_LIBADD) $(LIBS)
+
+libsane-matsushita.la: $(libsane_matsushita_la_OBJECTS) $(libsane_matsushita_la_DEPENDENCIES) $(EXTRA_libsane_matsushita_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_matsushita_la_LINK) $(libsane_matsushita_la_OBJECTS) $(libsane_matsushita_la_LIBADD) $(LIBS)
+
+libsane-microtek.la: $(libsane_microtek_la_OBJECTS) $(libsane_microtek_la_DEPENDENCIES) $(EXTRA_libsane_microtek_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_microtek_la_LINK) $(libsane_microtek_la_OBJECTS) $(libsane_microtek_la_LIBADD) $(LIBS)
+
+libsane-microtek2.la: $(libsane_microtek2_la_OBJECTS) $(libsane_microtek2_la_DEPENDENCIES) $(EXTRA_libsane_microtek2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_microtek2_la_LINK) $(libsane_microtek2_la_OBJECTS) $(libsane_microtek2_la_LIBADD) $(LIBS)
+
+libsane-mustek.la: $(libsane_mustek_la_OBJECTS) $(libsane_mustek_la_DEPENDENCIES) $(EXTRA_libsane_mustek_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_mustek_la_LINK) $(libsane_mustek_la_OBJECTS) $(libsane_mustek_la_LIBADD) $(LIBS)
+
+libsane-mustek_pp.la: $(libsane_mustek_pp_la_OBJECTS) $(libsane_mustek_pp_la_DEPENDENCIES) $(EXTRA_libsane_mustek_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_mustek_pp_la_LINK) $(libsane_mustek_pp_la_OBJECTS) $(libsane_mustek_pp_la_LIBADD) $(LIBS)
+
+libsane-mustek_usb.la: $(libsane_mustek_usb_la_OBJECTS) $(libsane_mustek_usb_la_DEPENDENCIES) $(EXTRA_libsane_mustek_usb_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_mustek_usb_la_LINK) $(libsane_mustek_usb_la_OBJECTS) $(libsane_mustek_usb_la_LIBADD) $(LIBS)
+
+libsane-mustek_usb2.la: $(libsane_mustek_usb2_la_OBJECTS) $(libsane_mustek_usb2_la_DEPENDENCIES) $(EXTRA_libsane_mustek_usb2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_mustek_usb2_la_LINK) $(libsane_mustek_usb2_la_OBJECTS) $(libsane_mustek_usb2_la_LIBADD) $(LIBS)
+
+libsane-nec.la: $(libsane_nec_la_OBJECTS) $(libsane_nec_la_DEPENDENCIES) $(EXTRA_libsane_nec_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_nec_la_LINK) $(libsane_nec_la_OBJECTS) $(libsane_nec_la_LIBADD) $(LIBS)
+
+libsane-net.la: $(libsane_net_la_OBJECTS) $(libsane_net_la_DEPENDENCIES) $(EXTRA_libsane_net_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_net_la_LINK) $(libsane_net_la_OBJECTS) $(libsane_net_la_LIBADD) $(LIBS)
+
+libsane-niash.la: $(libsane_niash_la_OBJECTS) $(libsane_niash_la_DEPENDENCIES) $(EXTRA_libsane_niash_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_niash_la_LINK) $(libsane_niash_la_OBJECTS) $(libsane_niash_la_LIBADD) $(LIBS)
+
+libsane-p5.la: $(libsane_p5_la_OBJECTS) $(libsane_p5_la_DEPENDENCIES) $(EXTRA_libsane_p5_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_p5_la_LINK) $(libsane_p5_la_OBJECTS) $(libsane_p5_la_LIBADD) $(LIBS)
+
+libsane-pie.la: $(libsane_pie_la_OBJECTS) $(libsane_pie_la_DEPENDENCIES) $(EXTRA_libsane_pie_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_pie_la_LINK) $(libsane_pie_la_OBJECTS) $(libsane_pie_la_LIBADD) $(LIBS)
+
+libsane-pint.la: $(libsane_pint_la_OBJECTS) $(libsane_pint_la_DEPENDENCIES) $(EXTRA_libsane_pint_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_pint_la_LINK) $(libsane_pint_la_OBJECTS) $(libsane_pint_la_LIBADD) $(LIBS)
+
+libsane-pixma.la: $(libsane_pixma_la_OBJECTS) $(libsane_pixma_la_DEPENDENCIES) $(EXTRA_libsane_pixma_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_pixma_la_LINK) $(libsane_pixma_la_OBJECTS) $(libsane_pixma_la_LIBADD) $(LIBS)
+
+libsane-plustek.la: $(libsane_plustek_la_OBJECTS) $(libsane_plustek_la_DEPENDENCIES) $(EXTRA_libsane_plustek_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_plustek_la_LINK) $(libsane_plustek_la_OBJECTS) $(libsane_plustek_la_LIBADD) $(LIBS)
+
+libsane-plustek_pp.la: $(libsane_plustek_pp_la_OBJECTS) $(libsane_plustek_pp_la_DEPENDENCIES) $(EXTRA_libsane_plustek_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_plustek_pp_la_LINK) $(libsane_plustek_pp_la_OBJECTS) $(libsane_plustek_pp_la_LIBADD) $(LIBS)
+
+libsane-pnm.la: $(libsane_pnm_la_OBJECTS) $(libsane_pnm_la_DEPENDENCIES) $(EXTRA_libsane_pnm_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_pnm_la_LINK) $(libsane_pnm_la_OBJECTS) $(libsane_pnm_la_LIBADD) $(LIBS)
+
+libsane-qcam.la: $(libsane_qcam_la_OBJECTS) $(libsane_qcam_la_DEPENDENCIES) $(EXTRA_libsane_qcam_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_qcam_la_LINK) $(libsane_qcam_la_OBJECTS) $(libsane_qcam_la_LIBADD) $(LIBS)
+
+libsane-ricoh.la: $(libsane_ricoh_la_OBJECTS) $(libsane_ricoh_la_DEPENDENCIES) $(EXTRA_libsane_ricoh_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_ricoh_la_LINK) $(libsane_ricoh_la_OBJECTS) $(libsane_ricoh_la_LIBADD) $(LIBS)
+
+libsane-rts8891.la: $(libsane_rts8891_la_OBJECTS) $(libsane_rts8891_la_DEPENDENCIES) $(EXTRA_libsane_rts8891_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_rts8891_la_LINK) $(libsane_rts8891_la_OBJECTS) $(libsane_rts8891_la_LIBADD) $(LIBS)
+
+libsane-s9036.la: $(libsane_s9036_la_OBJECTS) $(libsane_s9036_la_DEPENDENCIES) $(EXTRA_libsane_s9036_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_s9036_la_LINK) $(libsane_s9036_la_OBJECTS) $(libsane_s9036_la_LIBADD) $(LIBS)
+
+libsane-sceptre.la: $(libsane_sceptre_la_OBJECTS) $(libsane_sceptre_la_DEPENDENCIES) $(EXTRA_libsane_sceptre_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_sceptre_la_LINK) $(libsane_sceptre_la_OBJECTS) $(libsane_sceptre_la_LIBADD) $(LIBS)
+
+libsane-sharp.la: $(libsane_sharp_la_OBJECTS) $(libsane_sharp_la_DEPENDENCIES) $(EXTRA_libsane_sharp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_sharp_la_LINK) $(libsane_sharp_la_OBJECTS) $(libsane_sharp_la_LIBADD) $(LIBS)
+
+libsane-sm3600.la: $(libsane_sm3600_la_OBJECTS) $(libsane_sm3600_la_DEPENDENCIES) $(EXTRA_libsane_sm3600_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_sm3600_la_LINK) $(libsane_sm3600_la_OBJECTS) $(libsane_sm3600_la_LIBADD) $(LIBS)
+
+libsane-sm3840.la: $(libsane_sm3840_la_OBJECTS) $(libsane_sm3840_la_DEPENDENCIES) $(EXTRA_libsane_sm3840_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_sm3840_la_LINK) $(libsane_sm3840_la_OBJECTS) $(libsane_sm3840_la_LIBADD) $(LIBS)
+
+libsane-snapscan.la: $(libsane_snapscan_la_OBJECTS) $(libsane_snapscan_la_DEPENDENCIES) $(EXTRA_libsane_snapscan_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_snapscan_la_LINK) $(libsane_snapscan_la_OBJECTS) $(libsane_snapscan_la_LIBADD) $(LIBS)
+
+libsane-sp15c.la: $(libsane_sp15c_la_OBJECTS) $(libsane_sp15c_la_DEPENDENCIES) $(EXTRA_libsane_sp15c_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_sp15c_la_LINK) $(libsane_sp15c_la_OBJECTS) $(libsane_sp15c_la_LIBADD) $(LIBS)
+
+libsane-st400.la: $(libsane_st400_la_OBJECTS) $(libsane_st400_la_DEPENDENCIES) $(EXTRA_libsane_st400_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_st400_la_LINK) $(libsane_st400_la_OBJECTS) $(libsane_st400_la_LIBADD) $(LIBS)
+
+libsane-stv680.la: $(libsane_stv680_la_OBJECTS) $(libsane_stv680_la_DEPENDENCIES) $(EXTRA_libsane_stv680_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_stv680_la_LINK) $(libsane_stv680_la_OBJECTS) $(libsane_stv680_la_LIBADD) $(LIBS)
+
+libsane-tamarack.la: $(libsane_tamarack_la_OBJECTS) $(libsane_tamarack_la_DEPENDENCIES) $(EXTRA_libsane_tamarack_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_tamarack_la_LINK) $(libsane_tamarack_la_OBJECTS) $(libsane_tamarack_la_LIBADD) $(LIBS)
+
+libsane-teco1.la: $(libsane_teco1_la_OBJECTS) $(libsane_teco1_la_DEPENDENCIES) $(EXTRA_libsane_teco1_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_teco1_la_LINK) $(libsane_teco1_la_OBJECTS) $(libsane_teco1_la_LIBADD) $(LIBS)
+
+libsane-teco2.la: $(libsane_teco2_la_OBJECTS) $(libsane_teco2_la_DEPENDENCIES) $(EXTRA_libsane_teco2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_teco2_la_LINK) $(libsane_teco2_la_OBJECTS) $(libsane_teco2_la_LIBADD) $(LIBS)
+
+libsane-teco3.la: $(libsane_teco3_la_OBJECTS) $(libsane_teco3_la_DEPENDENCIES) $(EXTRA_libsane_teco3_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_teco3_la_LINK) $(libsane_teco3_la_OBJECTS) $(libsane_teco3_la_LIBADD) $(LIBS)
+
+libsane-test.la: $(libsane_test_la_OBJECTS) $(libsane_test_la_DEPENDENCIES) $(EXTRA_libsane_test_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_test_la_LINK) $(libsane_test_la_OBJECTS) $(libsane_test_la_LIBADD) $(LIBS)
+
+libsane-u12.la: $(libsane_u12_la_OBJECTS) $(libsane_u12_la_DEPENDENCIES) $(EXTRA_libsane_u12_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_u12_la_LINK) $(libsane_u12_la_OBJECTS) $(libsane_u12_la_LIBADD) $(LIBS)
+
+libsane-umax.la: $(libsane_umax_la_OBJECTS) $(libsane_umax_la_DEPENDENCIES) $(EXTRA_libsane_umax_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_umax_la_LINK) $(libsane_umax_la_OBJECTS) $(libsane_umax_la_LIBADD) $(LIBS)
+
+libsane-umax1220u.la: $(libsane_umax1220u_la_OBJECTS) $(libsane_umax1220u_la_DEPENDENCIES) $(EXTRA_libsane_umax1220u_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_umax1220u_la_LINK) $(libsane_umax1220u_la_OBJECTS) $(libsane_umax1220u_la_LIBADD) $(LIBS)
+
+libsane-umax_pp.la: $(libsane_umax_pp_la_OBJECTS) $(libsane_umax_pp_la_DEPENDENCIES) $(EXTRA_libsane_umax_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_umax_pp_la_LINK) $(libsane_umax_pp_la_OBJECTS) $(libsane_umax_pp_la_LIBADD) $(LIBS)
+
+libsane-v4l.la: $(libsane_v4l_la_OBJECTS) $(libsane_v4l_la_DEPENDENCIES) $(EXTRA_libsane_v4l_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_v4l_la_LINK) $(libsane_v4l_la_OBJECTS) $(libsane_v4l_la_LIBADD) $(LIBS)
+
+libsane-xerox_mfp.la: $(libsane_xerox_mfp_la_OBJECTS) $(libsane_xerox_mfp_la_DEPENDENCIES) $(EXTRA_libsane_xerox_mfp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_xerox_mfp_la_LINK) $(libsane_xerox_mfp_la_OBJECTS) $(libsane_xerox_mfp_la_LIBADD) $(LIBS)
+
+libsane.la: $(libsane_la_OBJECTS) $(libsane_la_DEPENDENCIES) $(EXTRA_libsane_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsane_la_LINK) -rpath $(libdir) $(libsane_la_OBJECTS) $(libsane_la_LIBADD) $(LIBS)
+
+libsceptre.la: $(libsceptre_la_OBJECTS) $(libsceptre_la_DEPENDENCIES) $(EXTRA_libsceptre_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsceptre_la_OBJECTS) $(libsceptre_la_LIBADD) $(LIBS)
+
+libsharp.la: $(libsharp_la_OBJECTS) $(libsharp_la_DEPENDENCIES) $(EXTRA_libsharp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsharp_la_OBJECTS) $(libsharp_la_LIBADD) $(LIBS)
+
+libsm3600.la: $(libsm3600_la_OBJECTS) $(libsm3600_la_DEPENDENCIES) $(EXTRA_libsm3600_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsm3600_la_OBJECTS) $(libsm3600_la_LIBADD) $(LIBS)
+
+libsm3840.la: $(libsm3840_la_OBJECTS) $(libsm3840_la_DEPENDENCIES) $(EXTRA_libsm3840_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsm3840_la_OBJECTS) $(libsm3840_la_LIBADD) $(LIBS)
+
+libsnapscan.la: $(libsnapscan_la_OBJECTS) $(libsnapscan_la_DEPENDENCIES) $(EXTRA_libsnapscan_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsnapscan_la_OBJECTS) $(libsnapscan_la_LIBADD) $(LIBS)
+
+libsp15c.la: $(libsp15c_la_OBJECTS) $(libsp15c_la_DEPENDENCIES) $(EXTRA_libsp15c_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsp15c_la_OBJECTS) $(libsp15c_la_LIBADD) $(LIBS)
+
+libst400.la: $(libst400_la_OBJECTS) $(libst400_la_DEPENDENCIES) $(EXTRA_libst400_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libst400_la_OBJECTS) $(libst400_la_LIBADD) $(LIBS)
+
+libstv680.la: $(libstv680_la_OBJECTS) $(libstv680_la_DEPENDENCIES) $(EXTRA_libstv680_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libstv680_la_OBJECTS) $(libstv680_la_LIBADD) $(LIBS)
+
+libtamarack.la: $(libtamarack_la_OBJECTS) $(libtamarack_la_DEPENDENCIES) $(EXTRA_libtamarack_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libtamarack_la_OBJECTS) $(libtamarack_la_LIBADD) $(LIBS)
+
+libteco1.la: $(libteco1_la_OBJECTS) $(libteco1_la_DEPENDENCIES) $(EXTRA_libteco1_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libteco1_la_OBJECTS) $(libteco1_la_LIBADD) $(LIBS)
+
+libteco2.la: $(libteco2_la_OBJECTS) $(libteco2_la_DEPENDENCIES) $(EXTRA_libteco2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libteco2_la_OBJECTS) $(libteco2_la_LIBADD) $(LIBS)
+
+libteco3.la: $(libteco3_la_OBJECTS) $(libteco3_la_DEPENDENCIES) $(EXTRA_libteco3_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libteco3_la_OBJECTS) $(libteco3_la_LIBADD) $(LIBS)
+
+libtest.la: $(libtest_la_OBJECTS) $(libtest_la_DEPENDENCIES) $(EXTRA_libtest_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libtest_la_OBJECTS) $(libtest_la_LIBADD) $(LIBS)
+
+libu12.la: $(libu12_la_OBJECTS) $(libu12_la_DEPENDENCIES) $(EXTRA_libu12_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libu12_la_OBJECTS) $(libu12_la_LIBADD) $(LIBS)
+
+libumax.la: $(libumax_la_OBJECTS) $(libumax_la_DEPENDENCIES) $(EXTRA_libumax_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libumax_la_OBJECTS) $(libumax_la_LIBADD) $(LIBS)
+
+libumax1220u.la: $(libumax1220u_la_OBJECTS) $(libumax1220u_la_DEPENDENCIES) $(EXTRA_libumax1220u_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libumax1220u_la_OBJECTS) $(libumax1220u_la_LIBADD) $(LIBS)
+
+libumax_pp.la: $(libumax_pp_la_OBJECTS) $(libumax_pp_la_DEPENDENCIES) $(EXTRA_libumax_pp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libumax_pp_la_OBJECTS) $(libumax_pp_la_LIBADD) $(LIBS)
+
+libv4l.la: $(libv4l_la_OBJECTS) $(libv4l_la_DEPENDENCIES) $(EXTRA_libv4l_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libv4l_la_OBJECTS) $(libv4l_la_LIBADD) $(LIBS)
+
+libxerox_mfp.la: $(libxerox_mfp_la_OBJECTS) $(libxerox_mfp_la_DEPENDENCIES) $(EXTRA_libxerox_mfp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libxerox_mfp_la_OBJECTS) $(libxerox_mfp_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libabaton_la-abaton.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libagfafocus_la-agfafocus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapple_la-apple.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libartec_eplus48u_la-artec_eplus48u.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libartec_la-artec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libas6e_la-as6e.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libavision_la-avision.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbh_la-bh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcanon630u_la-canon630u.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcanon_dr_la-canon_dr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcanon_la-canon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcanon_pp_la-canon_pp-dev.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcanon_pp_la-canon_pp-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcanon_pp_la-canon_pp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcardscan_la-cardscan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcoolscan2_la-coolscan2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcoolscan3_la-coolscan3.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcoolscan_la-coolscan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdc210_la-dc210.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdc240_la-dc240.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdc25_la-dc25.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdell1600n_net_la-dell1600n_net.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdll_la-dll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdll_preload_la-dll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdmc_la-dmc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepjitsu_la-epjitsu.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2-cct.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2-commands.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2-ops.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2_net.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson2_scsi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson2_la-epson_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson_la-epson.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson_la-epson_scsi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libepson_la-epson_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfujitsu_la-fujitsu.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_gl124.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_gl646.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_gl841.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_gl843.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_gl846.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_gl847.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgenesys_la-genesys_low.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgphoto2_i_la-gphoto2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgt68xx_la-gt68xx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp3500_la-hp3500.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp3900_la-hp3900.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp4200_la-hp4200.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp5400_la-hp5400.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp5590_la-hp5590.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp-accessor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp-device.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp-handle.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp-hpmem.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp-option.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp-scl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhp_la-hp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhpljm1005_la-hpljm1005.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhpsj5s_la-hpsj5s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhs2p_la-hs2p.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libibm_la-ibm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkodak_la-kodak.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkodakaio_la-kodakaio.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs1025_la-kvs1025.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs1025_la-kvs1025_low.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs1025_la-kvs1025_opt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs1025_la-kvs1025_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs20xx_la-kvs20xx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs20xx_la-kvs20xx_cmd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs20xx_la-kvs20xx_opt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs40xx_la-kvs40xx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs40xx_la-kvs40xx_cmd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkvs40xx_la-kvs40xx_opt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libleo_la-leo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblexmark_la-lexmark.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblexmark_la-lexmark_low.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libma1509_la-ma1509.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmagicolor_la-magicolor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmatsushita_la-matsushita.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrotek2_la-microtek2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrotek_la-microtek.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmustek_la-mustek.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmustek_pp_la-mustek_pp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmustek_usb2_la-mustek_usb2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmustek_usb_la-mustek_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnec_la-nec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnet_la-net.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libniash_la-niash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libp5_la-p5.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpie_la-pie.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpint_la-pint.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_bjnp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_common.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_imageclass.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_io_sanei.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_mp150.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_mp730.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_mp750.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpixma_la-pixma_mp810.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libplustek_la-plustek.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libplustek_pp_la-plustek_pp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpnm_la-pnm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqcam_la-qcam.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libricoh_la-ricoh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librts8891_la-rts8891.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librts8891_la-rts88xx_lib.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libs9036_la-s9036.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_abaton_la-abaton-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_agfafocus_la-agfafocus-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_apple_la-apple-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_artec_eplus48u_la-artec_eplus48u-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_artec_la-artec-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_as6e_la-as6e-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_avision_la-avision-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_bh_la-bh-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_canon630u_la-canon630u-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_canon_dr_la-canon_dr-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_canon_la-canon-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_canon_pp_la-canon_pp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_cardscan_la-cardscan-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_coolscan2_la-coolscan2-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_coolscan3_la-coolscan3-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_coolscan_la-coolscan-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_dc210_la-dc210-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_dc240_la-dc240-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_dc25_la-dc25-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_dell1600n_net_la-dell1600n_net-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_dll_la-dll-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_dmc_la-dmc-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_epjitsu_la-epjitsu-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_epson2_la-epson2-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_epson_la-epson-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_fujitsu_la-fujitsu-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_genesys_la-genesys-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_gphoto2_la-gphoto2-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_gt68xx_la-gt68xx-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hp3500_la-hp3500-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hp3900_la-hp3900-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hp4200_la-hp4200-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hp5400_la-hp5400-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hp5590_la-hp5590-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hp_la-hp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hpljm1005_la-hpljm1005-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hpsj5s_la-hpsj5s-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_hs2p_la-hs2p-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_ibm_la-ibm-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_kodak_la-kodak-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_kodakaio_la-kodakaio-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_kvs1025_la-kvs1025-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_kvs20xx_la-kvs20xx-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_kvs40xx_la-kvs40xx-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_la-dll-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_leo_la-leo-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_lexmark_la-lexmark-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_ma1509_la-ma1509-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_magicolor_la-magicolor-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_matsushita_la-matsushita-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_microtek2_la-microtek2-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_microtek_la-microtek-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_mustek_la-mustek-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_mustek_pp_la-mustek_pp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_mustek_usb2_la-mustek_usb2-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_mustek_usb_la-mustek_usb-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_nec_la-nec-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_net_la-net-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_niash_la-niash-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_p5_la-p5-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_pie_la-pie-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_pint_la-pint-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_pixma_la-pixma-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_plustek_la-plustek-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_plustek_pp_la-plustek_pp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_pnm_la-pnm-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_qcam_la-qcam-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_ricoh_la-ricoh-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_rts8891_la-rts8891-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_s9036_la-s9036-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_sceptre_la-sceptre-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_sharp_la-sharp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_sm3600_la-sm3600-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_sm3840_la-sm3840-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_snapscan_la-snapscan-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_sp15c_la-sp15c-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_st400_la-st400-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_stv680_la-stv680-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_tamarack_la-tamarack-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_teco1_la-teco1-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_teco2_la-teco2-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_teco3_la-teco3-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_test_la-test-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_u12_la-u12-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_umax1220u_la-umax1220u-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_umax_la-umax-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_umax_pp_la-umax_pp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_v4l_la-v4l-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsane_xerox_mfp_la-xerox_mfp-s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsceptre_la-sceptre.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsharp_la-sharp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsm3600_la-sm3600.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsm3840_la-sm3840.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsnapscan_la-snapscan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsp15c_la-sp15c.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libst400_la-st400.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libstv680_la-stv680.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtamarack_la-tamarack.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libteco1_la-teco1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libteco2_la-teco2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libteco3_la-teco3.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_la-test.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libu12_la-u12.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libumax1220u_la-umax1220u.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libumax_la-umax.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libumax_pp_la-umax_pp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libumax_pp_la-umax_pp_low.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libumax_pp_la-umax_pp_mid.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libv4l_la-v4l.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libxerox_mfp_la-xerox_mfp-tcp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libxerox_mfp_la-xerox_mfp-usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libxerox_mfp_la-xerox_mfp.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libabaton_la-abaton.lo: abaton.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libabaton_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libabaton_la-abaton.lo -MD -MP -MF $(DEPDIR)/libabaton_la-abaton.Tpo -c -o libabaton_la-abaton.lo `test -f 'abaton.c' || echo '$(srcdir)/'`abaton.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libabaton_la-abaton.Tpo $(DEPDIR)/libabaton_la-abaton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='abaton.c' object='libabaton_la-abaton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libabaton_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libabaton_la-abaton.lo `test -f 'abaton.c' || echo '$(srcdir)/'`abaton.c
+
+libagfafocus_la-agfafocus.lo: agfafocus.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libagfafocus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libagfafocus_la-agfafocus.lo -MD -MP -MF $(DEPDIR)/libagfafocus_la-agfafocus.Tpo -c -o libagfafocus_la-agfafocus.lo `test -f 'agfafocus.c' || echo '$(srcdir)/'`agfafocus.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libagfafocus_la-agfafocus.Tpo $(DEPDIR)/libagfafocus_la-agfafocus.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agfafocus.c' object='libagfafocus_la-agfafocus.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libagfafocus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libagfafocus_la-agfafocus.lo `test -f 'agfafocus.c' || echo '$(srcdir)/'`agfafocus.c
+
+libapple_la-apple.lo: apple.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libapple_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libapple_la-apple.lo -MD -MP -MF $(DEPDIR)/libapple_la-apple.Tpo -c -o libapple_la-apple.lo `test -f 'apple.c' || echo '$(srcdir)/'`apple.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapple_la-apple.Tpo $(DEPDIR)/libapple_la-apple.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apple.c' object='libapple_la-apple.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libapple_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libapple_la-apple.lo `test -f 'apple.c' || echo '$(srcdir)/'`apple.c
+
+libartec_la-artec.lo: artec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libartec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libartec_la-artec.lo -MD -MP -MF $(DEPDIR)/libartec_la-artec.Tpo -c -o libartec_la-artec.lo `test -f 'artec.c' || echo '$(srcdir)/'`artec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libartec_la-artec.Tpo $(DEPDIR)/libartec_la-artec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='artec.c' object='libartec_la-artec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libartec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libartec_la-artec.lo `test -f 'artec.c' || echo '$(srcdir)/'`artec.c
+
+libartec_eplus48u_la-artec_eplus48u.lo: artec_eplus48u.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libartec_eplus48u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libartec_eplus48u_la-artec_eplus48u.lo -MD -MP -MF $(DEPDIR)/libartec_eplus48u_la-artec_eplus48u.Tpo -c -o libartec_eplus48u_la-artec_eplus48u.lo `test -f 'artec_eplus48u.c' || echo '$(srcdir)/'`artec_eplus48u.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libartec_eplus48u_la-artec_eplus48u.Tpo $(DEPDIR)/libartec_eplus48u_la-artec_eplus48u.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='artec_eplus48u.c' object='libartec_eplus48u_la-artec_eplus48u.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libartec_eplus48u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libartec_eplus48u_la-artec_eplus48u.lo `test -f 'artec_eplus48u.c' || echo '$(srcdir)/'`artec_eplus48u.c
+
+libas6e_la-as6e.lo: as6e.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libas6e_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libas6e_la-as6e.lo -MD -MP -MF $(DEPDIR)/libas6e_la-as6e.Tpo -c -o libas6e_la-as6e.lo `test -f 'as6e.c' || echo '$(srcdir)/'`as6e.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libas6e_la-as6e.Tpo $(DEPDIR)/libas6e_la-as6e.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as6e.c' object='libas6e_la-as6e.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libas6e_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libas6e_la-as6e.lo `test -f 'as6e.c' || echo '$(srcdir)/'`as6e.c
+
+libavision_la-avision.lo: avision.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libavision_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libavision_la-avision.lo -MD -MP -MF $(DEPDIR)/libavision_la-avision.Tpo -c -o libavision_la-avision.lo `test -f 'avision.c' || echo '$(srcdir)/'`avision.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libavision_la-avision.Tpo $(DEPDIR)/libavision_la-avision.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='avision.c' object='libavision_la-avision.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libavision_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libavision_la-avision.lo `test -f 'avision.c' || echo '$(srcdir)/'`avision.c
+
+libbh_la-bh.lo: bh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbh_la-bh.lo -MD -MP -MF $(DEPDIR)/libbh_la-bh.Tpo -c -o libbh_la-bh.lo `test -f 'bh.c' || echo '$(srcdir)/'`bh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libbh_la-bh.Tpo $(DEPDIR)/libbh_la-bh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bh.c' object='libbh_la-bh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbh_la-bh.lo `test -f 'bh.c' || echo '$(srcdir)/'`bh.c
+
+libcanon_la-canon.lo: canon.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcanon_la-canon.lo -MD -MP -MF $(DEPDIR)/libcanon_la-canon.Tpo -c -o libcanon_la-canon.lo `test -f 'canon.c' || echo '$(srcdir)/'`canon.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcanon_la-canon.Tpo $(DEPDIR)/libcanon_la-canon.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon.c' object='libcanon_la-canon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcanon_la-canon.lo `test -f 'canon.c' || echo '$(srcdir)/'`canon.c
+
+libcanon630u_la-canon630u.lo: canon630u.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon630u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcanon630u_la-canon630u.lo -MD -MP -MF $(DEPDIR)/libcanon630u_la-canon630u.Tpo -c -o libcanon630u_la-canon630u.lo `test -f 'canon630u.c' || echo '$(srcdir)/'`canon630u.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcanon630u_la-canon630u.Tpo $(DEPDIR)/libcanon630u_la-canon630u.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon630u.c' object='libcanon630u_la-canon630u.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon630u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcanon630u_la-canon630u.lo `test -f 'canon630u.c' || echo '$(srcdir)/'`canon630u.c
+
+libcanon_dr_la-canon_dr.lo: canon_dr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_dr_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcanon_dr_la-canon_dr.lo -MD -MP -MF $(DEPDIR)/libcanon_dr_la-canon_dr.Tpo -c -o libcanon_dr_la-canon_dr.lo `test -f 'canon_dr.c' || echo '$(srcdir)/'`canon_dr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcanon_dr_la-canon_dr.Tpo $(DEPDIR)/libcanon_dr_la-canon_dr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon_dr.c' object='libcanon_dr_la-canon_dr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_dr_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcanon_dr_la-canon_dr.lo `test -f 'canon_dr.c' || echo '$(srcdir)/'`canon_dr.c
+
+libcanon_pp_la-canon_pp.lo: canon_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcanon_pp_la-canon_pp.lo -MD -MP -MF $(DEPDIR)/libcanon_pp_la-canon_pp.Tpo -c -o libcanon_pp_la-canon_pp.lo `test -f 'canon_pp.c' || echo '$(srcdir)/'`canon_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcanon_pp_la-canon_pp.Tpo $(DEPDIR)/libcanon_pp_la-canon_pp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon_pp.c' object='libcanon_pp_la-canon_pp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcanon_pp_la-canon_pp.lo `test -f 'canon_pp.c' || echo '$(srcdir)/'`canon_pp.c
+
+libcanon_pp_la-canon_pp-io.lo: canon_pp-io.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcanon_pp_la-canon_pp-io.lo -MD -MP -MF $(DEPDIR)/libcanon_pp_la-canon_pp-io.Tpo -c -o libcanon_pp_la-canon_pp-io.lo `test -f 'canon_pp-io.c' || echo '$(srcdir)/'`canon_pp-io.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcanon_pp_la-canon_pp-io.Tpo $(DEPDIR)/libcanon_pp_la-canon_pp-io.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon_pp-io.c' object='libcanon_pp_la-canon_pp-io.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcanon_pp_la-canon_pp-io.lo `test -f 'canon_pp-io.c' || echo '$(srcdir)/'`canon_pp-io.c
+
+libcanon_pp_la-canon_pp-dev.lo: canon_pp-dev.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcanon_pp_la-canon_pp-dev.lo -MD -MP -MF $(DEPDIR)/libcanon_pp_la-canon_pp-dev.Tpo -c -o libcanon_pp_la-canon_pp-dev.lo `test -f 'canon_pp-dev.c' || echo '$(srcdir)/'`canon_pp-dev.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcanon_pp_la-canon_pp-dev.Tpo $(DEPDIR)/libcanon_pp_la-canon_pp-dev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon_pp-dev.c' object='libcanon_pp_la-canon_pp-dev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcanon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcanon_pp_la-canon_pp-dev.lo `test -f 'canon_pp-dev.c' || echo '$(srcdir)/'`canon_pp-dev.c
+
+libcardscan_la-cardscan.lo: cardscan.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcardscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcardscan_la-cardscan.lo -MD -MP -MF $(DEPDIR)/libcardscan_la-cardscan.Tpo -c -o libcardscan_la-cardscan.lo `test -f 'cardscan.c' || echo '$(srcdir)/'`cardscan.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcardscan_la-cardscan.Tpo $(DEPDIR)/libcardscan_la-cardscan.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cardscan.c' object='libcardscan_la-cardscan.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcardscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcardscan_la-cardscan.lo `test -f 'cardscan.c' || echo '$(srcdir)/'`cardscan.c
+
+libcoolscan_la-coolscan.lo: coolscan.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcoolscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcoolscan_la-coolscan.lo -MD -MP -MF $(DEPDIR)/libcoolscan_la-coolscan.Tpo -c -o libcoolscan_la-coolscan.lo `test -f 'coolscan.c' || echo '$(srcdir)/'`coolscan.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcoolscan_la-coolscan.Tpo $(DEPDIR)/libcoolscan_la-coolscan.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='coolscan.c' object='libcoolscan_la-coolscan.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcoolscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcoolscan_la-coolscan.lo `test -f 'coolscan.c' || echo '$(srcdir)/'`coolscan.c
+
+libcoolscan2_la-coolscan2.lo: coolscan2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcoolscan2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcoolscan2_la-coolscan2.lo -MD -MP -MF $(DEPDIR)/libcoolscan2_la-coolscan2.Tpo -c -o libcoolscan2_la-coolscan2.lo `test -f 'coolscan2.c' || echo '$(srcdir)/'`coolscan2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcoolscan2_la-coolscan2.Tpo $(DEPDIR)/libcoolscan2_la-coolscan2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='coolscan2.c' object='libcoolscan2_la-coolscan2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcoolscan2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcoolscan2_la-coolscan2.lo `test -f 'coolscan2.c' || echo '$(srcdir)/'`coolscan2.c
+
+libcoolscan3_la-coolscan3.lo: coolscan3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcoolscan3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcoolscan3_la-coolscan3.lo -MD -MP -MF $(DEPDIR)/libcoolscan3_la-coolscan3.Tpo -c -o libcoolscan3_la-coolscan3.lo `test -f 'coolscan3.c' || echo '$(srcdir)/'`coolscan3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcoolscan3_la-coolscan3.Tpo $(DEPDIR)/libcoolscan3_la-coolscan3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='coolscan3.c' object='libcoolscan3_la-coolscan3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcoolscan3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcoolscan3_la-coolscan3.lo `test -f 'coolscan3.c' || echo '$(srcdir)/'`coolscan3.c
+
+libdc210_la-dc210.lo: dc210.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdc210_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdc210_la-dc210.lo -MD -MP -MF $(DEPDIR)/libdc210_la-dc210.Tpo -c -o libdc210_la-dc210.lo `test -f 'dc210.c' || echo '$(srcdir)/'`dc210.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdc210_la-dc210.Tpo $(DEPDIR)/libdc210_la-dc210.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dc210.c' object='libdc210_la-dc210.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdc210_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdc210_la-dc210.lo `test -f 'dc210.c' || echo '$(srcdir)/'`dc210.c
+
+libdc240_la-dc240.lo: dc240.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdc240_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdc240_la-dc240.lo -MD -MP -MF $(DEPDIR)/libdc240_la-dc240.Tpo -c -o libdc240_la-dc240.lo `test -f 'dc240.c' || echo '$(srcdir)/'`dc240.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdc240_la-dc240.Tpo $(DEPDIR)/libdc240_la-dc240.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dc240.c' object='libdc240_la-dc240.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdc240_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdc240_la-dc240.lo `test -f 'dc240.c' || echo '$(srcdir)/'`dc240.c
+
+libdc25_la-dc25.lo: dc25.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdc25_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdc25_la-dc25.lo -MD -MP -MF $(DEPDIR)/libdc25_la-dc25.Tpo -c -o libdc25_la-dc25.lo `test -f 'dc25.c' || echo '$(srcdir)/'`dc25.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdc25_la-dc25.Tpo $(DEPDIR)/libdc25_la-dc25.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dc25.c' object='libdc25_la-dc25.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdc25_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdc25_la-dc25.lo `test -f 'dc25.c' || echo '$(srcdir)/'`dc25.c
+
+libdell1600n_net_la-dell1600n_net.lo: dell1600n_net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdell1600n_net_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdell1600n_net_la-dell1600n_net.lo -MD -MP -MF $(DEPDIR)/libdell1600n_net_la-dell1600n_net.Tpo -c -o libdell1600n_net_la-dell1600n_net.lo `test -f 'dell1600n_net.c' || echo '$(srcdir)/'`dell1600n_net.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdell1600n_net_la-dell1600n_net.Tpo $(DEPDIR)/libdell1600n_net_la-dell1600n_net.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dell1600n_net.c' object='libdell1600n_net_la-dell1600n_net.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdell1600n_net_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdell1600n_net_la-dell1600n_net.lo `test -f 'dell1600n_net.c' || echo '$(srcdir)/'`dell1600n_net.c
+
+libdll_la-dll.lo: dll.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdll_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdll_la-dll.lo -MD -MP -MF $(DEPDIR)/libdll_la-dll.Tpo -c -o libdll_la-dll.lo `test -f 'dll.c' || echo '$(srcdir)/'`dll.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdll_la-dll.Tpo $(DEPDIR)/libdll_la-dll.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dll.c' object='libdll_la-dll.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdll_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdll_la-dll.lo `test -f 'dll.c' || echo '$(srcdir)/'`dll.c
+
+libdll_preload_la-dll.lo: dll.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdll_preload_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdll_preload_la-dll.lo -MD -MP -MF $(DEPDIR)/libdll_preload_la-dll.Tpo -c -o libdll_preload_la-dll.lo `test -f 'dll.c' || echo '$(srcdir)/'`dll.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdll_preload_la-dll.Tpo $(DEPDIR)/libdll_preload_la-dll.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dll.c' object='libdll_preload_la-dll.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdll_preload_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdll_preload_la-dll.lo `test -f 'dll.c' || echo '$(srcdir)/'`dll.c
+
+libdmc_la-dmc.lo: dmc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdmc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdmc_la-dmc.lo -MD -MP -MF $(DEPDIR)/libdmc_la-dmc.Tpo -c -o libdmc_la-dmc.lo `test -f 'dmc.c' || echo '$(srcdir)/'`dmc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdmc_la-dmc.Tpo $(DEPDIR)/libdmc_la-dmc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmc.c' object='libdmc_la-dmc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdmc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdmc_la-dmc.lo `test -f 'dmc.c' || echo '$(srcdir)/'`dmc.c
+
+libepjitsu_la-epjitsu.lo: epjitsu.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepjitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepjitsu_la-epjitsu.lo -MD -MP -MF $(DEPDIR)/libepjitsu_la-epjitsu.Tpo -c -o libepjitsu_la-epjitsu.lo `test -f 'epjitsu.c' || echo '$(srcdir)/'`epjitsu.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepjitsu_la-epjitsu.Tpo $(DEPDIR)/libepjitsu_la-epjitsu.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epjitsu.c' object='libepjitsu_la-epjitsu.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepjitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepjitsu_la-epjitsu.lo `test -f 'epjitsu.c' || echo '$(srcdir)/'`epjitsu.c
+
+libepson_la-epson.lo: epson.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson_la-epson.lo -MD -MP -MF $(DEPDIR)/libepson_la-epson.Tpo -c -o libepson_la-epson.lo `test -f 'epson.c' || echo '$(srcdir)/'`epson.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson_la-epson.Tpo $(DEPDIR)/libepson_la-epson.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson.c' object='libepson_la-epson.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson_la-epson.lo `test -f 'epson.c' || echo '$(srcdir)/'`epson.c
+
+libepson_la-epson_scsi.lo: epson_scsi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson_la-epson_scsi.lo -MD -MP -MF $(DEPDIR)/libepson_la-epson_scsi.Tpo -c -o libepson_la-epson_scsi.lo `test -f 'epson_scsi.c' || echo '$(srcdir)/'`epson_scsi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson_la-epson_scsi.Tpo $(DEPDIR)/libepson_la-epson_scsi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson_scsi.c' object='libepson_la-epson_scsi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson_la-epson_scsi.lo `test -f 'epson_scsi.c' || echo '$(srcdir)/'`epson_scsi.c
+
+libepson_la-epson_usb.lo: epson_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson_la-epson_usb.lo -MD -MP -MF $(DEPDIR)/libepson_la-epson_usb.Tpo -c -o libepson_la-epson_usb.lo `test -f 'epson_usb.c' || echo '$(srcdir)/'`epson_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson_la-epson_usb.Tpo $(DEPDIR)/libepson_la-epson_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson_usb.c' object='libepson_la-epson_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson_la-epson_usb.lo `test -f 'epson_usb.c' || echo '$(srcdir)/'`epson_usb.c
+
+libepson2_la-epson2.lo: epson2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2.Tpo -c -o libepson2_la-epson2.lo `test -f 'epson2.c' || echo '$(srcdir)/'`epson2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2.Tpo $(DEPDIR)/libepson2_la-epson2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2.c' object='libepson2_la-epson2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2.lo `test -f 'epson2.c' || echo '$(srcdir)/'`epson2.c
+
+libepson2_la-epson2_scsi.lo: epson2_scsi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2_scsi.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2_scsi.Tpo -c -o libepson2_la-epson2_scsi.lo `test -f 'epson2_scsi.c' || echo '$(srcdir)/'`epson2_scsi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2_scsi.Tpo $(DEPDIR)/libepson2_la-epson2_scsi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2_scsi.c' object='libepson2_la-epson2_scsi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2_scsi.lo `test -f 'epson2_scsi.c' || echo '$(srcdir)/'`epson2_scsi.c
+
+libepson2_la-epson_usb.lo: epson_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson_usb.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson_usb.Tpo -c -o libepson2_la-epson_usb.lo `test -f 'epson_usb.c' || echo '$(srcdir)/'`epson_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson_usb.Tpo $(DEPDIR)/libepson2_la-epson_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson_usb.c' object='libepson2_la-epson_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson_usb.lo `test -f 'epson_usb.c' || echo '$(srcdir)/'`epson_usb.c
+
+libepson2_la-epson2_net.lo: epson2_net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2_net.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2_net.Tpo -c -o libepson2_la-epson2_net.lo `test -f 'epson2_net.c' || echo '$(srcdir)/'`epson2_net.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2_net.Tpo $(DEPDIR)/libepson2_la-epson2_net.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2_net.c' object='libepson2_la-epson2_net.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2_net.lo `test -f 'epson2_net.c' || echo '$(srcdir)/'`epson2_net.c
+
+libepson2_la-epson2-io.lo: epson2-io.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2-io.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2-io.Tpo -c -o libepson2_la-epson2-io.lo `test -f 'epson2-io.c' || echo '$(srcdir)/'`epson2-io.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2-io.Tpo $(DEPDIR)/libepson2_la-epson2-io.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2-io.c' object='libepson2_la-epson2-io.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2-io.lo `test -f 'epson2-io.c' || echo '$(srcdir)/'`epson2-io.c
+
+libepson2_la-epson2-commands.lo: epson2-commands.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2-commands.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2-commands.Tpo -c -o libepson2_la-epson2-commands.lo `test -f 'epson2-commands.c' || echo '$(srcdir)/'`epson2-commands.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2-commands.Tpo $(DEPDIR)/libepson2_la-epson2-commands.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2-commands.c' object='libepson2_la-epson2-commands.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2-commands.lo `test -f 'epson2-commands.c' || echo '$(srcdir)/'`epson2-commands.c
+
+libepson2_la-epson2-ops.lo: epson2-ops.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2-ops.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2-ops.Tpo -c -o libepson2_la-epson2-ops.lo `test -f 'epson2-ops.c' || echo '$(srcdir)/'`epson2-ops.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2-ops.Tpo $(DEPDIR)/libepson2_la-epson2-ops.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2-ops.c' object='libepson2_la-epson2-ops.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2-ops.lo `test -f 'epson2-ops.c' || echo '$(srcdir)/'`epson2-ops.c
+
+libepson2_la-epson2-cct.lo: epson2-cct.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libepson2_la-epson2-cct.lo -MD -MP -MF $(DEPDIR)/libepson2_la-epson2-cct.Tpo -c -o libepson2_la-epson2-cct.lo `test -f 'epson2-cct.c' || echo '$(srcdir)/'`epson2-cct.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libepson2_la-epson2-cct.Tpo $(DEPDIR)/libepson2_la-epson2-cct.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2-cct.c' object='libepson2_la-epson2-cct.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libepson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libepson2_la-epson2-cct.lo `test -f 'epson2-cct.c' || echo '$(srcdir)/'`epson2-cct.c
+
+libfujitsu_la-fujitsu.lo: fujitsu.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfujitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libfujitsu_la-fujitsu.lo -MD -MP -MF $(DEPDIR)/libfujitsu_la-fujitsu.Tpo -c -o libfujitsu_la-fujitsu.lo `test -f 'fujitsu.c' || echo '$(srcdir)/'`fujitsu.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfujitsu_la-fujitsu.Tpo $(DEPDIR)/libfujitsu_la-fujitsu.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fujitsu.c' object='libfujitsu_la-fujitsu.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfujitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libfujitsu_la-fujitsu.lo `test -f 'fujitsu.c' || echo '$(srcdir)/'`fujitsu.c
+
+libgenesys_la-genesys.lo: genesys.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys.Tpo -c -o libgenesys_la-genesys.lo `test -f 'genesys.c' || echo '$(srcdir)/'`genesys.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys.Tpo $(DEPDIR)/libgenesys_la-genesys.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys.c' object='libgenesys_la-genesys.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys.lo `test -f 'genesys.c' || echo '$(srcdir)/'`genesys.c
+
+libgenesys_la-genesys_gl646.lo: genesys_gl646.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_gl646.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_gl646.Tpo -c -o libgenesys_la-genesys_gl646.lo `test -f 'genesys_gl646.c' || echo '$(srcdir)/'`genesys_gl646.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_gl646.Tpo $(DEPDIR)/libgenesys_la-genesys_gl646.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_gl646.c' object='libgenesys_la-genesys_gl646.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_gl646.lo `test -f 'genesys_gl646.c' || echo '$(srcdir)/'`genesys_gl646.c
+
+libgenesys_la-genesys_gl841.lo: genesys_gl841.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_gl841.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_gl841.Tpo -c -o libgenesys_la-genesys_gl841.lo `test -f 'genesys_gl841.c' || echo '$(srcdir)/'`genesys_gl841.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_gl841.Tpo $(DEPDIR)/libgenesys_la-genesys_gl841.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_gl841.c' object='libgenesys_la-genesys_gl841.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_gl841.lo `test -f 'genesys_gl841.c' || echo '$(srcdir)/'`genesys_gl841.c
+
+libgenesys_la-genesys_gl843.lo: genesys_gl843.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_gl843.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_gl843.Tpo -c -o libgenesys_la-genesys_gl843.lo `test -f 'genesys_gl843.c' || echo '$(srcdir)/'`genesys_gl843.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_gl843.Tpo $(DEPDIR)/libgenesys_la-genesys_gl843.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_gl843.c' object='libgenesys_la-genesys_gl843.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_gl843.lo `test -f 'genesys_gl843.c' || echo '$(srcdir)/'`genesys_gl843.c
+
+libgenesys_la-genesys_gl846.lo: genesys_gl846.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_gl846.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_gl846.Tpo -c -o libgenesys_la-genesys_gl846.lo `test -f 'genesys_gl846.c' || echo '$(srcdir)/'`genesys_gl846.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_gl846.Tpo $(DEPDIR)/libgenesys_la-genesys_gl846.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_gl846.c' object='libgenesys_la-genesys_gl846.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_gl846.lo `test -f 'genesys_gl846.c' || echo '$(srcdir)/'`genesys_gl846.c
+
+libgenesys_la-genesys_gl847.lo: genesys_gl847.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_gl847.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_gl847.Tpo -c -o libgenesys_la-genesys_gl847.lo `test -f 'genesys_gl847.c' || echo '$(srcdir)/'`genesys_gl847.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_gl847.Tpo $(DEPDIR)/libgenesys_la-genesys_gl847.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_gl847.c' object='libgenesys_la-genesys_gl847.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_gl847.lo `test -f 'genesys_gl847.c' || echo '$(srcdir)/'`genesys_gl847.c
+
+libgenesys_la-genesys_gl124.lo: genesys_gl124.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_gl124.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_gl124.Tpo -c -o libgenesys_la-genesys_gl124.lo `test -f 'genesys_gl124.c' || echo '$(srcdir)/'`genesys_gl124.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_gl124.Tpo $(DEPDIR)/libgenesys_la-genesys_gl124.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_gl124.c' object='libgenesys_la-genesys_gl124.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_gl124.lo `test -f 'genesys_gl124.c' || echo '$(srcdir)/'`genesys_gl124.c
+
+libgenesys_la-genesys_low.lo: genesys_low.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgenesys_la-genesys_low.lo -MD -MP -MF $(DEPDIR)/libgenesys_la-genesys_low.Tpo -c -o libgenesys_la-genesys_low.lo `test -f 'genesys_low.c' || echo '$(srcdir)/'`genesys_low.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgenesys_la-genesys_low.Tpo $(DEPDIR)/libgenesys_la-genesys_low.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys_low.c' object='libgenesys_la-genesys_low.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgenesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgenesys_la-genesys_low.lo `test -f 'genesys_low.c' || echo '$(srcdir)/'`genesys_low.c
+
+libgphoto2_i_la-gphoto2.lo: gphoto2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgphoto2_i_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgphoto2_i_la-gphoto2.lo -MD -MP -MF $(DEPDIR)/libgphoto2_i_la-gphoto2.Tpo -c -o libgphoto2_i_la-gphoto2.lo `test -f 'gphoto2.c' || echo '$(srcdir)/'`gphoto2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgphoto2_i_la-gphoto2.Tpo $(DEPDIR)/libgphoto2_i_la-gphoto2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gphoto2.c' object='libgphoto2_i_la-gphoto2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgphoto2_i_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgphoto2_i_la-gphoto2.lo `test -f 'gphoto2.c' || echo '$(srcdir)/'`gphoto2.c
+
+libgt68xx_la-gt68xx.lo: gt68xx.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgt68xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libgt68xx_la-gt68xx.lo -MD -MP -MF $(DEPDIR)/libgt68xx_la-gt68xx.Tpo -c -o libgt68xx_la-gt68xx.lo `test -f 'gt68xx.c' || echo '$(srcdir)/'`gt68xx.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgt68xx_la-gt68xx.Tpo $(DEPDIR)/libgt68xx_la-gt68xx.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gt68xx.c' object='libgt68xx_la-gt68xx.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgt68xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgt68xx_la-gt68xx.lo `test -f 'gt68xx.c' || echo '$(srcdir)/'`gt68xx.c
+
+libhp_la-hp.lo: hp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp.Tpo -c -o libhp_la-hp.lo `test -f 'hp.c' || echo '$(srcdir)/'`hp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp.Tpo $(DEPDIR)/libhp_la-hp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp.c' object='libhp_la-hp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp.lo `test -f 'hp.c' || echo '$(srcdir)/'`hp.c
+
+libhp_la-hp-accessor.lo: hp-accessor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp-accessor.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp-accessor.Tpo -c -o libhp_la-hp-accessor.lo `test -f 'hp-accessor.c' || echo '$(srcdir)/'`hp-accessor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp-accessor.Tpo $(DEPDIR)/libhp_la-hp-accessor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-accessor.c' object='libhp_la-hp-accessor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp-accessor.lo `test -f 'hp-accessor.c' || echo '$(srcdir)/'`hp-accessor.c
+
+libhp_la-hp-device.lo: hp-device.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp-device.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp-device.Tpo -c -o libhp_la-hp-device.lo `test -f 'hp-device.c' || echo '$(srcdir)/'`hp-device.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp-device.Tpo $(DEPDIR)/libhp_la-hp-device.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-device.c' object='libhp_la-hp-device.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp-device.lo `test -f 'hp-device.c' || echo '$(srcdir)/'`hp-device.c
+
+libhp_la-hp-handle.lo: hp-handle.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp-handle.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp-handle.Tpo -c -o libhp_la-hp-handle.lo `test -f 'hp-handle.c' || echo '$(srcdir)/'`hp-handle.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp-handle.Tpo $(DEPDIR)/libhp_la-hp-handle.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-handle.c' object='libhp_la-hp-handle.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp-handle.lo `test -f 'hp-handle.c' || echo '$(srcdir)/'`hp-handle.c
+
+libhp_la-hp-hpmem.lo: hp-hpmem.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp-hpmem.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp-hpmem.Tpo -c -o libhp_la-hp-hpmem.lo `test -f 'hp-hpmem.c' || echo '$(srcdir)/'`hp-hpmem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp-hpmem.Tpo $(DEPDIR)/libhp_la-hp-hpmem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-hpmem.c' object='libhp_la-hp-hpmem.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp-hpmem.lo `test -f 'hp-hpmem.c' || echo '$(srcdir)/'`hp-hpmem.c
+
+libhp_la-hp-option.lo: hp-option.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp-option.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp-option.Tpo -c -o libhp_la-hp-option.lo `test -f 'hp-option.c' || echo '$(srcdir)/'`hp-option.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp-option.Tpo $(DEPDIR)/libhp_la-hp-option.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-option.c' object='libhp_la-hp-option.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp-option.lo `test -f 'hp-option.c' || echo '$(srcdir)/'`hp-option.c
+
+libhp_la-hp-scl.lo: hp-scl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp_la-hp-scl.lo -MD -MP -MF $(DEPDIR)/libhp_la-hp-scl.Tpo -c -o libhp_la-hp-scl.lo `test -f 'hp-scl.c' || echo '$(srcdir)/'`hp-scl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp_la-hp-scl.Tpo $(DEPDIR)/libhp_la-hp-scl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-scl.c' object='libhp_la-hp-scl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp_la-hp-scl.lo `test -f 'hp-scl.c' || echo '$(srcdir)/'`hp-scl.c
+
+libhp3500_la-hp3500.lo: hp3500.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp3500_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp3500_la-hp3500.lo -MD -MP -MF $(DEPDIR)/libhp3500_la-hp3500.Tpo -c -o libhp3500_la-hp3500.lo `test -f 'hp3500.c' || echo '$(srcdir)/'`hp3500.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp3500_la-hp3500.Tpo $(DEPDIR)/libhp3500_la-hp3500.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp3500.c' object='libhp3500_la-hp3500.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp3500_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp3500_la-hp3500.lo `test -f 'hp3500.c' || echo '$(srcdir)/'`hp3500.c
+
+libhp3900_la-hp3900.lo: hp3900.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp3900_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp3900_la-hp3900.lo -MD -MP -MF $(DEPDIR)/libhp3900_la-hp3900.Tpo -c -o libhp3900_la-hp3900.lo `test -f 'hp3900.c' || echo '$(srcdir)/'`hp3900.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp3900_la-hp3900.Tpo $(DEPDIR)/libhp3900_la-hp3900.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp3900.c' object='libhp3900_la-hp3900.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp3900_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp3900_la-hp3900.lo `test -f 'hp3900.c' || echo '$(srcdir)/'`hp3900.c
+
+libhp4200_la-hp4200.lo: hp4200.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp4200_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp4200_la-hp4200.lo -MD -MP -MF $(DEPDIR)/libhp4200_la-hp4200.Tpo -c -o libhp4200_la-hp4200.lo `test -f 'hp4200.c' || echo '$(srcdir)/'`hp4200.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp4200_la-hp4200.Tpo $(DEPDIR)/libhp4200_la-hp4200.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp4200.c' object='libhp4200_la-hp4200.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp4200_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp4200_la-hp4200.lo `test -f 'hp4200.c' || echo '$(srcdir)/'`hp4200.c
+
+libhp5400_la-hp5400.lo: hp5400.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp5400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp5400_la-hp5400.lo -MD -MP -MF $(DEPDIR)/libhp5400_la-hp5400.Tpo -c -o libhp5400_la-hp5400.lo `test -f 'hp5400.c' || echo '$(srcdir)/'`hp5400.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp5400_la-hp5400.Tpo $(DEPDIR)/libhp5400_la-hp5400.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp5400.c' object='libhp5400_la-hp5400.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp5400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp5400_la-hp5400.lo `test -f 'hp5400.c' || echo '$(srcdir)/'`hp5400.c
+
+libhp5590_la-hp5590.lo: hp5590.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp5590_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhp5590_la-hp5590.lo -MD -MP -MF $(DEPDIR)/libhp5590_la-hp5590.Tpo -c -o libhp5590_la-hp5590.lo `test -f 'hp5590.c' || echo '$(srcdir)/'`hp5590.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhp5590_la-hp5590.Tpo $(DEPDIR)/libhp5590_la-hp5590.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp5590.c' object='libhp5590_la-hp5590.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhp5590_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhp5590_la-hp5590.lo `test -f 'hp5590.c' || echo '$(srcdir)/'`hp5590.c
+
+libhpljm1005_la-hpljm1005.lo: hpljm1005.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhpljm1005_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhpljm1005_la-hpljm1005.lo -MD -MP -MF $(DEPDIR)/libhpljm1005_la-hpljm1005.Tpo -c -o libhpljm1005_la-hpljm1005.lo `test -f 'hpljm1005.c' || echo '$(srcdir)/'`hpljm1005.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhpljm1005_la-hpljm1005.Tpo $(DEPDIR)/libhpljm1005_la-hpljm1005.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hpljm1005.c' object='libhpljm1005_la-hpljm1005.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhpljm1005_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhpljm1005_la-hpljm1005.lo `test -f 'hpljm1005.c' || echo '$(srcdir)/'`hpljm1005.c
+
+libhpsj5s_la-hpsj5s.lo: hpsj5s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhpsj5s_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhpsj5s_la-hpsj5s.lo -MD -MP -MF $(DEPDIR)/libhpsj5s_la-hpsj5s.Tpo -c -o libhpsj5s_la-hpsj5s.lo `test -f 'hpsj5s.c' || echo '$(srcdir)/'`hpsj5s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhpsj5s_la-hpsj5s.Tpo $(DEPDIR)/libhpsj5s_la-hpsj5s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hpsj5s.c' object='libhpsj5s_la-hpsj5s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhpsj5s_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhpsj5s_la-hpsj5s.lo `test -f 'hpsj5s.c' || echo '$(srcdir)/'`hpsj5s.c
+
+libhs2p_la-hs2p.lo: hs2p.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhs2p_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhs2p_la-hs2p.lo -MD -MP -MF $(DEPDIR)/libhs2p_la-hs2p.Tpo -c -o libhs2p_la-hs2p.lo `test -f 'hs2p.c' || echo '$(srcdir)/'`hs2p.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhs2p_la-hs2p.Tpo $(DEPDIR)/libhs2p_la-hs2p.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hs2p.c' object='libhs2p_la-hs2p.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhs2p_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhs2p_la-hs2p.lo `test -f 'hs2p.c' || echo '$(srcdir)/'`hs2p.c
+
+libibm_la-ibm.lo: ibm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libibm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libibm_la-ibm.lo -MD -MP -MF $(DEPDIR)/libibm_la-ibm.Tpo -c -o libibm_la-ibm.lo `test -f 'ibm.c' || echo '$(srcdir)/'`ibm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libibm_la-ibm.Tpo $(DEPDIR)/libibm_la-ibm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ibm.c' object='libibm_la-ibm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libibm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libibm_la-ibm.lo `test -f 'ibm.c' || echo '$(srcdir)/'`ibm.c
+
+libkodak_la-kodak.lo: kodak.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkodak_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkodak_la-kodak.lo -MD -MP -MF $(DEPDIR)/libkodak_la-kodak.Tpo -c -o libkodak_la-kodak.lo `test -f 'kodak.c' || echo '$(srcdir)/'`kodak.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkodak_la-kodak.Tpo $(DEPDIR)/libkodak_la-kodak.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kodak.c' object='libkodak_la-kodak.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkodak_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkodak_la-kodak.lo `test -f 'kodak.c' || echo '$(srcdir)/'`kodak.c
+
+libkodakaio_la-kodakaio.lo: kodakaio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkodakaio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkodakaio_la-kodakaio.lo -MD -MP -MF $(DEPDIR)/libkodakaio_la-kodakaio.Tpo -c -o libkodakaio_la-kodakaio.lo `test -f 'kodakaio.c' || echo '$(srcdir)/'`kodakaio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkodakaio_la-kodakaio.Tpo $(DEPDIR)/libkodakaio_la-kodakaio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kodakaio.c' object='libkodakaio_la-kodakaio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkodakaio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkodakaio_la-kodakaio.lo `test -f 'kodakaio.c' || echo '$(srcdir)/'`kodakaio.c
+
+libkvs1025_la-kvs1025.lo: kvs1025.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs1025_la-kvs1025.lo -MD -MP -MF $(DEPDIR)/libkvs1025_la-kvs1025.Tpo -c -o libkvs1025_la-kvs1025.lo `test -f 'kvs1025.c' || echo '$(srcdir)/'`kvs1025.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs1025_la-kvs1025.Tpo $(DEPDIR)/libkvs1025_la-kvs1025.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs1025.c' object='libkvs1025_la-kvs1025.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs1025_la-kvs1025.lo `test -f 'kvs1025.c' || echo '$(srcdir)/'`kvs1025.c
+
+libkvs1025_la-kvs1025_low.lo: kvs1025_low.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs1025_la-kvs1025_low.lo -MD -MP -MF $(DEPDIR)/libkvs1025_la-kvs1025_low.Tpo -c -o libkvs1025_la-kvs1025_low.lo `test -f 'kvs1025_low.c' || echo '$(srcdir)/'`kvs1025_low.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs1025_la-kvs1025_low.Tpo $(DEPDIR)/libkvs1025_la-kvs1025_low.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs1025_low.c' object='libkvs1025_la-kvs1025_low.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs1025_la-kvs1025_low.lo `test -f 'kvs1025_low.c' || echo '$(srcdir)/'`kvs1025_low.c
+
+libkvs1025_la-kvs1025_opt.lo: kvs1025_opt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs1025_la-kvs1025_opt.lo -MD -MP -MF $(DEPDIR)/libkvs1025_la-kvs1025_opt.Tpo -c -o libkvs1025_la-kvs1025_opt.lo `test -f 'kvs1025_opt.c' || echo '$(srcdir)/'`kvs1025_opt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs1025_la-kvs1025_opt.Tpo $(DEPDIR)/libkvs1025_la-kvs1025_opt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs1025_opt.c' object='libkvs1025_la-kvs1025_opt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs1025_la-kvs1025_opt.lo `test -f 'kvs1025_opt.c' || echo '$(srcdir)/'`kvs1025_opt.c
+
+libkvs1025_la-kvs1025_usb.lo: kvs1025_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs1025_la-kvs1025_usb.lo -MD -MP -MF $(DEPDIR)/libkvs1025_la-kvs1025_usb.Tpo -c -o libkvs1025_la-kvs1025_usb.lo `test -f 'kvs1025_usb.c' || echo '$(srcdir)/'`kvs1025_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs1025_la-kvs1025_usb.Tpo $(DEPDIR)/libkvs1025_la-kvs1025_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs1025_usb.c' object='libkvs1025_la-kvs1025_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs1025_la-kvs1025_usb.lo `test -f 'kvs1025_usb.c' || echo '$(srcdir)/'`kvs1025_usb.c
+
+libkvs20xx_la-kvs20xx.lo: kvs20xx.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs20xx_la-kvs20xx.lo -MD -MP -MF $(DEPDIR)/libkvs20xx_la-kvs20xx.Tpo -c -o libkvs20xx_la-kvs20xx.lo `test -f 'kvs20xx.c' || echo '$(srcdir)/'`kvs20xx.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs20xx_la-kvs20xx.Tpo $(DEPDIR)/libkvs20xx_la-kvs20xx.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs20xx.c' object='libkvs20xx_la-kvs20xx.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs20xx_la-kvs20xx.lo `test -f 'kvs20xx.c' || echo '$(srcdir)/'`kvs20xx.c
+
+libkvs20xx_la-kvs20xx_cmd.lo: kvs20xx_cmd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs20xx_la-kvs20xx_cmd.lo -MD -MP -MF $(DEPDIR)/libkvs20xx_la-kvs20xx_cmd.Tpo -c -o libkvs20xx_la-kvs20xx_cmd.lo `test -f 'kvs20xx_cmd.c' || echo '$(srcdir)/'`kvs20xx_cmd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs20xx_la-kvs20xx_cmd.Tpo $(DEPDIR)/libkvs20xx_la-kvs20xx_cmd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs20xx_cmd.c' object='libkvs20xx_la-kvs20xx_cmd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs20xx_la-kvs20xx_cmd.lo `test -f 'kvs20xx_cmd.c' || echo '$(srcdir)/'`kvs20xx_cmd.c
+
+libkvs20xx_la-kvs20xx_opt.lo: kvs20xx_opt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs20xx_la-kvs20xx_opt.lo -MD -MP -MF $(DEPDIR)/libkvs20xx_la-kvs20xx_opt.Tpo -c -o libkvs20xx_la-kvs20xx_opt.lo `test -f 'kvs20xx_opt.c' || echo '$(srcdir)/'`kvs20xx_opt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs20xx_la-kvs20xx_opt.Tpo $(DEPDIR)/libkvs20xx_la-kvs20xx_opt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs20xx_opt.c' object='libkvs20xx_la-kvs20xx_opt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs20xx_la-kvs20xx_opt.lo `test -f 'kvs20xx_opt.c' || echo '$(srcdir)/'`kvs20xx_opt.c
+
+libkvs40xx_la-kvs40xx.lo: kvs40xx.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs40xx_la-kvs40xx.lo -MD -MP -MF $(DEPDIR)/libkvs40xx_la-kvs40xx.Tpo -c -o libkvs40xx_la-kvs40xx.lo `test -f 'kvs40xx.c' || echo '$(srcdir)/'`kvs40xx.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs40xx_la-kvs40xx.Tpo $(DEPDIR)/libkvs40xx_la-kvs40xx.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs40xx.c' object='libkvs40xx_la-kvs40xx.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs40xx_la-kvs40xx.lo `test -f 'kvs40xx.c' || echo '$(srcdir)/'`kvs40xx.c
+
+libkvs40xx_la-kvs40xx_cmd.lo: kvs40xx_cmd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs40xx_la-kvs40xx_cmd.lo -MD -MP -MF $(DEPDIR)/libkvs40xx_la-kvs40xx_cmd.Tpo -c -o libkvs40xx_la-kvs40xx_cmd.lo `test -f 'kvs40xx_cmd.c' || echo '$(srcdir)/'`kvs40xx_cmd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs40xx_la-kvs40xx_cmd.Tpo $(DEPDIR)/libkvs40xx_la-kvs40xx_cmd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs40xx_cmd.c' object='libkvs40xx_la-kvs40xx_cmd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs40xx_la-kvs40xx_cmd.lo `test -f 'kvs40xx_cmd.c' || echo '$(srcdir)/'`kvs40xx_cmd.c
+
+libkvs40xx_la-kvs40xx_opt.lo: kvs40xx_opt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkvs40xx_la-kvs40xx_opt.lo -MD -MP -MF $(DEPDIR)/libkvs40xx_la-kvs40xx_opt.Tpo -c -o libkvs40xx_la-kvs40xx_opt.lo `test -f 'kvs40xx_opt.c' || echo '$(srcdir)/'`kvs40xx_opt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkvs40xx_la-kvs40xx_opt.Tpo $(DEPDIR)/libkvs40xx_la-kvs40xx_opt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs40xx_opt.c' object='libkvs40xx_la-kvs40xx_opt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkvs40xx_la-kvs40xx_opt.lo `test -f 'kvs40xx_opt.c' || echo '$(srcdir)/'`kvs40xx_opt.c
+
+libleo_la-leo.lo: leo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libleo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libleo_la-leo.lo -MD -MP -MF $(DEPDIR)/libleo_la-leo.Tpo -c -o libleo_la-leo.lo `test -f 'leo.c' || echo '$(srcdir)/'`leo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libleo_la-leo.Tpo $(DEPDIR)/libleo_la-leo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='leo.c' object='libleo_la-leo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libleo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libleo_la-leo.lo `test -f 'leo.c' || echo '$(srcdir)/'`leo.c
+
+liblexmark_la-lexmark.lo: lexmark.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblexmark_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liblexmark_la-lexmark.lo -MD -MP -MF $(DEPDIR)/liblexmark_la-lexmark.Tpo -c -o liblexmark_la-lexmark.lo `test -f 'lexmark.c' || echo '$(srcdir)/'`lexmark.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblexmark_la-lexmark.Tpo $(DEPDIR)/liblexmark_la-lexmark.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lexmark.c' object='liblexmark_la-lexmark.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblexmark_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liblexmark_la-lexmark.lo `test -f 'lexmark.c' || echo '$(srcdir)/'`lexmark.c
+
+liblexmark_la-lexmark_low.lo: lexmark_low.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblexmark_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liblexmark_la-lexmark_low.lo -MD -MP -MF $(DEPDIR)/liblexmark_la-lexmark_low.Tpo -c -o liblexmark_la-lexmark_low.lo `test -f 'lexmark_low.c' || echo '$(srcdir)/'`lexmark_low.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblexmark_la-lexmark_low.Tpo $(DEPDIR)/liblexmark_la-lexmark_low.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lexmark_low.c' object='liblexmark_la-lexmark_low.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblexmark_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liblexmark_la-lexmark_low.lo `test -f 'lexmark_low.c' || echo '$(srcdir)/'`lexmark_low.c
+
+libma1509_la-ma1509.lo: ma1509.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libma1509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libma1509_la-ma1509.lo -MD -MP -MF $(DEPDIR)/libma1509_la-ma1509.Tpo -c -o libma1509_la-ma1509.lo `test -f 'ma1509.c' || echo '$(srcdir)/'`ma1509.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libma1509_la-ma1509.Tpo $(DEPDIR)/libma1509_la-ma1509.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ma1509.c' object='libma1509_la-ma1509.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libma1509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libma1509_la-ma1509.lo `test -f 'ma1509.c' || echo '$(srcdir)/'`ma1509.c
+
+libmagicolor_la-magicolor.lo: magicolor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmagicolor_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmagicolor_la-magicolor.lo -MD -MP -MF $(DEPDIR)/libmagicolor_la-magicolor.Tpo -c -o libmagicolor_la-magicolor.lo `test -f 'magicolor.c' || echo '$(srcdir)/'`magicolor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmagicolor_la-magicolor.Tpo $(DEPDIR)/libmagicolor_la-magicolor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='magicolor.c' object='libmagicolor_la-magicolor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmagicolor_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmagicolor_la-magicolor.lo `test -f 'magicolor.c' || echo '$(srcdir)/'`magicolor.c
+
+libmatsushita_la-matsushita.lo: matsushita.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmatsushita_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmatsushita_la-matsushita.lo -MD -MP -MF $(DEPDIR)/libmatsushita_la-matsushita.Tpo -c -o libmatsushita_la-matsushita.lo `test -f 'matsushita.c' || echo '$(srcdir)/'`matsushita.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmatsushita_la-matsushita.Tpo $(DEPDIR)/libmatsushita_la-matsushita.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='matsushita.c' object='libmatsushita_la-matsushita.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmatsushita_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmatsushita_la-matsushita.lo `test -f 'matsushita.c' || echo '$(srcdir)/'`matsushita.c
+
+libmicrotek_la-microtek.lo: microtek.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrotek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmicrotek_la-microtek.lo -MD -MP -MF $(DEPDIR)/libmicrotek_la-microtek.Tpo -c -o libmicrotek_la-microtek.lo `test -f 'microtek.c' || echo '$(srcdir)/'`microtek.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmicrotek_la-microtek.Tpo $(DEPDIR)/libmicrotek_la-microtek.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='microtek.c' object='libmicrotek_la-microtek.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrotek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmicrotek_la-microtek.lo `test -f 'microtek.c' || echo '$(srcdir)/'`microtek.c
+
+libmicrotek2_la-microtek2.lo: microtek2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrotek2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmicrotek2_la-microtek2.lo -MD -MP -MF $(DEPDIR)/libmicrotek2_la-microtek2.Tpo -c -o libmicrotek2_la-microtek2.lo `test -f 'microtek2.c' || echo '$(srcdir)/'`microtek2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmicrotek2_la-microtek2.Tpo $(DEPDIR)/libmicrotek2_la-microtek2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='microtek2.c' object='libmicrotek2_la-microtek2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrotek2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmicrotek2_la-microtek2.lo `test -f 'microtek2.c' || echo '$(srcdir)/'`microtek2.c
+
+libmustek_la-mustek.lo: mustek.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmustek_la-mustek.lo -MD -MP -MF $(DEPDIR)/libmustek_la-mustek.Tpo -c -o libmustek_la-mustek.lo `test -f 'mustek.c' || echo '$(srcdir)/'`mustek.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmustek_la-mustek.Tpo $(DEPDIR)/libmustek_la-mustek.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek.c' object='libmustek_la-mustek.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmustek_la-mustek.lo `test -f 'mustek.c' || echo '$(srcdir)/'`mustek.c
+
+libmustek_pp_la-mustek_pp.lo: mustek_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmustek_pp_la-mustek_pp.lo -MD -MP -MF $(DEPDIR)/libmustek_pp_la-mustek_pp.Tpo -c -o libmustek_pp_la-mustek_pp.lo `test -f 'mustek_pp.c' || echo '$(srcdir)/'`mustek_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmustek_pp_la-mustek_pp.Tpo $(DEPDIR)/libmustek_pp_la-mustek_pp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek_pp.c' object='libmustek_pp_la-mustek_pp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmustek_pp_la-mustek_pp.lo `test -f 'mustek_pp.c' || echo '$(srcdir)/'`mustek_pp.c
+
+libmustek_usb_la-mustek_usb.lo: mustek_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_usb_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmustek_usb_la-mustek_usb.lo -MD -MP -MF $(DEPDIR)/libmustek_usb_la-mustek_usb.Tpo -c -o libmustek_usb_la-mustek_usb.lo `test -f 'mustek_usb.c' || echo '$(srcdir)/'`mustek_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmustek_usb_la-mustek_usb.Tpo $(DEPDIR)/libmustek_usb_la-mustek_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek_usb.c' object='libmustek_usb_la-mustek_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_usb_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmustek_usb_la-mustek_usb.lo `test -f 'mustek_usb.c' || echo '$(srcdir)/'`mustek_usb.c
+
+libmustek_usb2_la-mustek_usb2.lo: mustek_usb2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_usb2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmustek_usb2_la-mustek_usb2.lo -MD -MP -MF $(DEPDIR)/libmustek_usb2_la-mustek_usb2.Tpo -c -o libmustek_usb2_la-mustek_usb2.lo `test -f 'mustek_usb2.c' || echo '$(srcdir)/'`mustek_usb2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmustek_usb2_la-mustek_usb2.Tpo $(DEPDIR)/libmustek_usb2_la-mustek_usb2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek_usb2.c' object='libmustek_usb2_la-mustek_usb2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmustek_usb2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmustek_usb2_la-mustek_usb2.lo `test -f 'mustek_usb2.c' || echo '$(srcdir)/'`mustek_usb2.c
+
+libnec_la-nec.lo: nec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnec_la-nec.lo -MD -MP -MF $(DEPDIR)/libnec_la-nec.Tpo -c -o libnec_la-nec.lo `test -f 'nec.c' || echo '$(srcdir)/'`nec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnec_la-nec.Tpo $(DEPDIR)/libnec_la-nec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nec.c' object='libnec_la-nec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnec_la-nec.lo `test -f 'nec.c' || echo '$(srcdir)/'`nec.c
+
+libnet_la-net.lo: net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnet_la-net.lo -MD -MP -MF $(DEPDIR)/libnet_la-net.Tpo -c -o libnet_la-net.lo `test -f 'net.c' || echo '$(srcdir)/'`net.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnet_la-net.Tpo $(DEPDIR)/libnet_la-net.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net.c' object='libnet_la-net.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnet_la-net.lo `test -f 'net.c' || echo '$(srcdir)/'`net.c
+
+libniash_la-niash.lo: niash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libniash_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libniash_la-niash.lo -MD -MP -MF $(DEPDIR)/libniash_la-niash.Tpo -c -o libniash_la-niash.lo `test -f 'niash.c' || echo '$(srcdir)/'`niash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libniash_la-niash.Tpo $(DEPDIR)/libniash_la-niash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='niash.c' object='libniash_la-niash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libniash_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libniash_la-niash.lo `test -f 'niash.c' || echo '$(srcdir)/'`niash.c
+
+libp5_la-p5.lo: p5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libp5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libp5_la-p5.lo -MD -MP -MF $(DEPDIR)/libp5_la-p5.Tpo -c -o libp5_la-p5.lo `test -f 'p5.c' || echo '$(srcdir)/'`p5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libp5_la-p5.Tpo $(DEPDIR)/libp5_la-p5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='p5.c' object='libp5_la-p5.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libp5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libp5_la-p5.lo `test -f 'p5.c' || echo '$(srcdir)/'`p5.c
+
+libpie_la-pie.lo: pie.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpie_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpie_la-pie.lo -MD -MP -MF $(DEPDIR)/libpie_la-pie.Tpo -c -o libpie_la-pie.lo `test -f 'pie.c' || echo '$(srcdir)/'`pie.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpie_la-pie.Tpo $(DEPDIR)/libpie_la-pie.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pie.c' object='libpie_la-pie.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpie_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpie_la-pie.lo `test -f 'pie.c' || echo '$(srcdir)/'`pie.c
+
+libpint_la-pint.lo: pint.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpint_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpint_la-pint.lo -MD -MP -MF $(DEPDIR)/libpint_la-pint.Tpo -c -o libpint_la-pint.lo `test -f 'pint.c' || echo '$(srcdir)/'`pint.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpint_la-pint.Tpo $(DEPDIR)/libpint_la-pint.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pint.c' object='libpint_la-pint.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpint_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpint_la-pint.lo `test -f 'pint.c' || echo '$(srcdir)/'`pint.c
+
+libpixma_la-pixma.lo: pixma.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma.Tpo -c -o libpixma_la-pixma.lo `test -f 'pixma.c' || echo '$(srcdir)/'`pixma.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma.Tpo $(DEPDIR)/libpixma_la-pixma.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma.c' object='libpixma_la-pixma.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma.lo `test -f 'pixma.c' || echo '$(srcdir)/'`pixma.c
+
+libpixma_la-pixma_io_sanei.lo: pixma_io_sanei.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_io_sanei.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_io_sanei.Tpo -c -o libpixma_la-pixma_io_sanei.lo `test -f 'pixma_io_sanei.c' || echo '$(srcdir)/'`pixma_io_sanei.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_io_sanei.Tpo $(DEPDIR)/libpixma_la-pixma_io_sanei.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_io_sanei.c' object='libpixma_la-pixma_io_sanei.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_io_sanei.lo `test -f 'pixma_io_sanei.c' || echo '$(srcdir)/'`pixma_io_sanei.c
+
+libpixma_la-pixma_common.lo: pixma_common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_common.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_common.Tpo -c -o libpixma_la-pixma_common.lo `test -f 'pixma_common.c' || echo '$(srcdir)/'`pixma_common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_common.Tpo $(DEPDIR)/libpixma_la-pixma_common.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_common.c' object='libpixma_la-pixma_common.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_common.lo `test -f 'pixma_common.c' || echo '$(srcdir)/'`pixma_common.c
+
+libpixma_la-pixma_mp150.lo: pixma_mp150.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_mp150.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_mp150.Tpo -c -o libpixma_la-pixma_mp150.lo `test -f 'pixma_mp150.c' || echo '$(srcdir)/'`pixma_mp150.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_mp150.Tpo $(DEPDIR)/libpixma_la-pixma_mp150.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_mp150.c' object='libpixma_la-pixma_mp150.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_mp150.lo `test -f 'pixma_mp150.c' || echo '$(srcdir)/'`pixma_mp150.c
+
+libpixma_la-pixma_mp730.lo: pixma_mp730.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_mp730.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_mp730.Tpo -c -o libpixma_la-pixma_mp730.lo `test -f 'pixma_mp730.c' || echo '$(srcdir)/'`pixma_mp730.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_mp730.Tpo $(DEPDIR)/libpixma_la-pixma_mp730.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_mp730.c' object='libpixma_la-pixma_mp730.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_mp730.lo `test -f 'pixma_mp730.c' || echo '$(srcdir)/'`pixma_mp730.c
+
+libpixma_la-pixma_mp750.lo: pixma_mp750.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_mp750.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_mp750.Tpo -c -o libpixma_la-pixma_mp750.lo `test -f 'pixma_mp750.c' || echo '$(srcdir)/'`pixma_mp750.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_mp750.Tpo $(DEPDIR)/libpixma_la-pixma_mp750.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_mp750.c' object='libpixma_la-pixma_mp750.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_mp750.lo `test -f 'pixma_mp750.c' || echo '$(srcdir)/'`pixma_mp750.c
+
+libpixma_la-pixma_mp810.lo: pixma_mp810.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_mp810.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_mp810.Tpo -c -o libpixma_la-pixma_mp810.lo `test -f 'pixma_mp810.c' || echo '$(srcdir)/'`pixma_mp810.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_mp810.Tpo $(DEPDIR)/libpixma_la-pixma_mp810.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_mp810.c' object='libpixma_la-pixma_mp810.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_mp810.lo `test -f 'pixma_mp810.c' || echo '$(srcdir)/'`pixma_mp810.c
+
+libpixma_la-pixma_imageclass.lo: pixma_imageclass.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_imageclass.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_imageclass.Tpo -c -o libpixma_la-pixma_imageclass.lo `test -f 'pixma_imageclass.c' || echo '$(srcdir)/'`pixma_imageclass.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_imageclass.Tpo $(DEPDIR)/libpixma_la-pixma_imageclass.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_imageclass.c' object='libpixma_la-pixma_imageclass.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_imageclass.lo `test -f 'pixma_imageclass.c' || echo '$(srcdir)/'`pixma_imageclass.c
+
+libpixma_la-pixma_bjnp.lo: pixma_bjnp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpixma_la-pixma_bjnp.lo -MD -MP -MF $(DEPDIR)/libpixma_la-pixma_bjnp.Tpo -c -o libpixma_la-pixma_bjnp.lo `test -f 'pixma_bjnp.c' || echo '$(srcdir)/'`pixma_bjnp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpixma_la-pixma_bjnp.Tpo $(DEPDIR)/libpixma_la-pixma_bjnp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma_bjnp.c' object='libpixma_la-pixma_bjnp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpixma_la-pixma_bjnp.lo `test -f 'pixma_bjnp.c' || echo '$(srcdir)/'`pixma_bjnp.c
+
+libplustek_la-plustek.lo: plustek.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libplustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libplustek_la-plustek.lo -MD -MP -MF $(DEPDIR)/libplustek_la-plustek.Tpo -c -o libplustek_la-plustek.lo `test -f 'plustek.c' || echo '$(srcdir)/'`plustek.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libplustek_la-plustek.Tpo $(DEPDIR)/libplustek_la-plustek.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plustek.c' object='libplustek_la-plustek.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libplustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libplustek_la-plustek.lo `test -f 'plustek.c' || echo '$(srcdir)/'`plustek.c
+
+libplustek_pp_la-plustek_pp.lo: plustek_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libplustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libplustek_pp_la-plustek_pp.lo -MD -MP -MF $(DEPDIR)/libplustek_pp_la-plustek_pp.Tpo -c -o libplustek_pp_la-plustek_pp.lo `test -f 'plustek_pp.c' || echo '$(srcdir)/'`plustek_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libplustek_pp_la-plustek_pp.Tpo $(DEPDIR)/libplustek_pp_la-plustek_pp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plustek_pp.c' object='libplustek_pp_la-plustek_pp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libplustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libplustek_pp_la-plustek_pp.lo `test -f 'plustek_pp.c' || echo '$(srcdir)/'`plustek_pp.c
+
+libpnm_la-pnm.lo: pnm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpnm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpnm_la-pnm.lo -MD -MP -MF $(DEPDIR)/libpnm_la-pnm.Tpo -c -o libpnm_la-pnm.lo `test -f 'pnm.c' || echo '$(srcdir)/'`pnm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpnm_la-pnm.Tpo $(DEPDIR)/libpnm_la-pnm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pnm.c' object='libpnm_la-pnm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpnm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libpnm_la-pnm.lo `test -f 'pnm.c' || echo '$(srcdir)/'`pnm.c
+
+libqcam_la-qcam.lo: qcam.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqcam_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqcam_la-qcam.lo -MD -MP -MF $(DEPDIR)/libqcam_la-qcam.Tpo -c -o libqcam_la-qcam.lo `test -f 'qcam.c' || echo '$(srcdir)/'`qcam.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqcam_la-qcam.Tpo $(DEPDIR)/libqcam_la-qcam.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='qcam.c' object='libqcam_la-qcam.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqcam_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqcam_la-qcam.lo `test -f 'qcam.c' || echo '$(srcdir)/'`qcam.c
+
+libricoh_la-ricoh.lo: ricoh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libricoh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libricoh_la-ricoh.lo -MD -MP -MF $(DEPDIR)/libricoh_la-ricoh.Tpo -c -o libricoh_la-ricoh.lo `test -f 'ricoh.c' || echo '$(srcdir)/'`ricoh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libricoh_la-ricoh.Tpo $(DEPDIR)/libricoh_la-ricoh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ricoh.c' object='libricoh_la-ricoh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libricoh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libricoh_la-ricoh.lo `test -f 'ricoh.c' || echo '$(srcdir)/'`ricoh.c
+
+librts8891_la-rts8891.lo: rts8891.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librts8891_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librts8891_la-rts8891.lo -MD -MP -MF $(DEPDIR)/librts8891_la-rts8891.Tpo -c -o librts8891_la-rts8891.lo `test -f 'rts8891.c' || echo '$(srcdir)/'`rts8891.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librts8891_la-rts8891.Tpo $(DEPDIR)/librts8891_la-rts8891.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rts8891.c' object='librts8891_la-rts8891.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librts8891_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librts8891_la-rts8891.lo `test -f 'rts8891.c' || echo '$(srcdir)/'`rts8891.c
+
+librts8891_la-rts88xx_lib.lo: rts88xx_lib.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librts8891_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librts8891_la-rts88xx_lib.lo -MD -MP -MF $(DEPDIR)/librts8891_la-rts88xx_lib.Tpo -c -o librts8891_la-rts88xx_lib.lo `test -f 'rts88xx_lib.c' || echo '$(srcdir)/'`rts88xx_lib.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librts8891_la-rts88xx_lib.Tpo $(DEPDIR)/librts8891_la-rts88xx_lib.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rts88xx_lib.c' object='librts8891_la-rts88xx_lib.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librts8891_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librts8891_la-rts88xx_lib.lo `test -f 'rts88xx_lib.c' || echo '$(srcdir)/'`rts88xx_lib.c
+
+libs9036_la-s9036.lo: s9036.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libs9036_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libs9036_la-s9036.lo -MD -MP -MF $(DEPDIR)/libs9036_la-s9036.Tpo -c -o libs9036_la-s9036.lo `test -f 's9036.c' || echo '$(srcdir)/'`s9036.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libs9036_la-s9036.Tpo $(DEPDIR)/libs9036_la-s9036.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='s9036.c' object='libs9036_la-s9036.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libs9036_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libs9036_la-s9036.lo `test -f 's9036.c' || echo '$(srcdir)/'`s9036.c
+
+libsane_abaton_la-abaton-s.lo: abaton-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_abaton_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_abaton_la-abaton-s.lo -MD -MP -MF $(DEPDIR)/libsane_abaton_la-abaton-s.Tpo -c -o libsane_abaton_la-abaton-s.lo `test -f 'abaton-s.c' || echo '$(srcdir)/'`abaton-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_abaton_la-abaton-s.Tpo $(DEPDIR)/libsane_abaton_la-abaton-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='abaton-s.c' object='libsane_abaton_la-abaton-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_abaton_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_abaton_la-abaton-s.lo `test -f 'abaton-s.c' || echo '$(srcdir)/'`abaton-s.c
+
+libsane_agfafocus_la-agfafocus-s.lo: agfafocus-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_agfafocus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_agfafocus_la-agfafocus-s.lo -MD -MP -MF $(DEPDIR)/libsane_agfafocus_la-agfafocus-s.Tpo -c -o libsane_agfafocus_la-agfafocus-s.lo `test -f 'agfafocus-s.c' || echo '$(srcdir)/'`agfafocus-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_agfafocus_la-agfafocus-s.Tpo $(DEPDIR)/libsane_agfafocus_la-agfafocus-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agfafocus-s.c' object='libsane_agfafocus_la-agfafocus-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_agfafocus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_agfafocus_la-agfafocus-s.lo `test -f 'agfafocus-s.c' || echo '$(srcdir)/'`agfafocus-s.c
+
+libsane_apple_la-apple-s.lo: apple-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_apple_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_apple_la-apple-s.lo -MD -MP -MF $(DEPDIR)/libsane_apple_la-apple-s.Tpo -c -o libsane_apple_la-apple-s.lo `test -f 'apple-s.c' || echo '$(srcdir)/'`apple-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_apple_la-apple-s.Tpo $(DEPDIR)/libsane_apple_la-apple-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apple-s.c' object='libsane_apple_la-apple-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_apple_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_apple_la-apple-s.lo `test -f 'apple-s.c' || echo '$(srcdir)/'`apple-s.c
+
+libsane_artec_la-artec-s.lo: artec-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_artec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_artec_la-artec-s.lo -MD -MP -MF $(DEPDIR)/libsane_artec_la-artec-s.Tpo -c -o libsane_artec_la-artec-s.lo `test -f 'artec-s.c' || echo '$(srcdir)/'`artec-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_artec_la-artec-s.Tpo $(DEPDIR)/libsane_artec_la-artec-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='artec-s.c' object='libsane_artec_la-artec-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_artec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_artec_la-artec-s.lo `test -f 'artec-s.c' || echo '$(srcdir)/'`artec-s.c
+
+libsane_artec_eplus48u_la-artec_eplus48u-s.lo: artec_eplus48u-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_artec_eplus48u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_artec_eplus48u_la-artec_eplus48u-s.lo -MD -MP -MF $(DEPDIR)/libsane_artec_eplus48u_la-artec_eplus48u-s.Tpo -c -o libsane_artec_eplus48u_la-artec_eplus48u-s.lo `test -f 'artec_eplus48u-s.c' || echo '$(srcdir)/'`artec_eplus48u-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_artec_eplus48u_la-artec_eplus48u-s.Tpo $(DEPDIR)/libsane_artec_eplus48u_la-artec_eplus48u-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='artec_eplus48u-s.c' object='libsane_artec_eplus48u_la-artec_eplus48u-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_artec_eplus48u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_artec_eplus48u_la-artec_eplus48u-s.lo `test -f 'artec_eplus48u-s.c' || echo '$(srcdir)/'`artec_eplus48u-s.c
+
+libsane_as6e_la-as6e-s.lo: as6e-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_as6e_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_as6e_la-as6e-s.lo -MD -MP -MF $(DEPDIR)/libsane_as6e_la-as6e-s.Tpo -c -o libsane_as6e_la-as6e-s.lo `test -f 'as6e-s.c' || echo '$(srcdir)/'`as6e-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_as6e_la-as6e-s.Tpo $(DEPDIR)/libsane_as6e_la-as6e-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as6e-s.c' object='libsane_as6e_la-as6e-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_as6e_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_as6e_la-as6e-s.lo `test -f 'as6e-s.c' || echo '$(srcdir)/'`as6e-s.c
+
+libsane_avision_la-avision-s.lo: avision-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_avision_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_avision_la-avision-s.lo -MD -MP -MF $(DEPDIR)/libsane_avision_la-avision-s.Tpo -c -o libsane_avision_la-avision-s.lo `test -f 'avision-s.c' || echo '$(srcdir)/'`avision-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_avision_la-avision-s.Tpo $(DEPDIR)/libsane_avision_la-avision-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='avision-s.c' object='libsane_avision_la-avision-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_avision_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_avision_la-avision-s.lo `test -f 'avision-s.c' || echo '$(srcdir)/'`avision-s.c
+
+libsane_bh_la-bh-s.lo: bh-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_bh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_bh_la-bh-s.lo -MD -MP -MF $(DEPDIR)/libsane_bh_la-bh-s.Tpo -c -o libsane_bh_la-bh-s.lo `test -f 'bh-s.c' || echo '$(srcdir)/'`bh-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_bh_la-bh-s.Tpo $(DEPDIR)/libsane_bh_la-bh-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bh-s.c' object='libsane_bh_la-bh-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_bh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_bh_la-bh-s.lo `test -f 'bh-s.c' || echo '$(srcdir)/'`bh-s.c
+
+libsane_canon_la-canon-s.lo: canon-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_canon_la-canon-s.lo -MD -MP -MF $(DEPDIR)/libsane_canon_la-canon-s.Tpo -c -o libsane_canon_la-canon-s.lo `test -f 'canon-s.c' || echo '$(srcdir)/'`canon-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_canon_la-canon-s.Tpo $(DEPDIR)/libsane_canon_la-canon-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon-s.c' object='libsane_canon_la-canon-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_canon_la-canon-s.lo `test -f 'canon-s.c' || echo '$(srcdir)/'`canon-s.c
+
+libsane_canon630u_la-canon630u-s.lo: canon630u-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon630u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_canon630u_la-canon630u-s.lo -MD -MP -MF $(DEPDIR)/libsane_canon630u_la-canon630u-s.Tpo -c -o libsane_canon630u_la-canon630u-s.lo `test -f 'canon630u-s.c' || echo '$(srcdir)/'`canon630u-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_canon630u_la-canon630u-s.Tpo $(DEPDIR)/libsane_canon630u_la-canon630u-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon630u-s.c' object='libsane_canon630u_la-canon630u-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon630u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_canon630u_la-canon630u-s.lo `test -f 'canon630u-s.c' || echo '$(srcdir)/'`canon630u-s.c
+
+libsane_canon_dr_la-canon_dr-s.lo: canon_dr-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon_dr_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_canon_dr_la-canon_dr-s.lo -MD -MP -MF $(DEPDIR)/libsane_canon_dr_la-canon_dr-s.Tpo -c -o libsane_canon_dr_la-canon_dr-s.lo `test -f 'canon_dr-s.c' || echo '$(srcdir)/'`canon_dr-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_canon_dr_la-canon_dr-s.Tpo $(DEPDIR)/libsane_canon_dr_la-canon_dr-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon_dr-s.c' object='libsane_canon_dr_la-canon_dr-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon_dr_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_canon_dr_la-canon_dr-s.lo `test -f 'canon_dr-s.c' || echo '$(srcdir)/'`canon_dr-s.c
+
+libsane_canon_pp_la-canon_pp-s.lo: canon_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_canon_pp_la-canon_pp-s.lo -MD -MP -MF $(DEPDIR)/libsane_canon_pp_la-canon_pp-s.Tpo -c -o libsane_canon_pp_la-canon_pp-s.lo `test -f 'canon_pp-s.c' || echo '$(srcdir)/'`canon_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_canon_pp_la-canon_pp-s.Tpo $(DEPDIR)/libsane_canon_pp_la-canon_pp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='canon_pp-s.c' object='libsane_canon_pp_la-canon_pp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_canon_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_canon_pp_la-canon_pp-s.lo `test -f 'canon_pp-s.c' || echo '$(srcdir)/'`canon_pp-s.c
+
+libsane_cardscan_la-cardscan-s.lo: cardscan-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_cardscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_cardscan_la-cardscan-s.lo -MD -MP -MF $(DEPDIR)/libsane_cardscan_la-cardscan-s.Tpo -c -o libsane_cardscan_la-cardscan-s.lo `test -f 'cardscan-s.c' || echo '$(srcdir)/'`cardscan-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_cardscan_la-cardscan-s.Tpo $(DEPDIR)/libsane_cardscan_la-cardscan-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cardscan-s.c' object='libsane_cardscan_la-cardscan-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_cardscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_cardscan_la-cardscan-s.lo `test -f 'cardscan-s.c' || echo '$(srcdir)/'`cardscan-s.c
+
+libsane_coolscan_la-coolscan-s.lo: coolscan-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_coolscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_coolscan_la-coolscan-s.lo -MD -MP -MF $(DEPDIR)/libsane_coolscan_la-coolscan-s.Tpo -c -o libsane_coolscan_la-coolscan-s.lo `test -f 'coolscan-s.c' || echo '$(srcdir)/'`coolscan-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_coolscan_la-coolscan-s.Tpo $(DEPDIR)/libsane_coolscan_la-coolscan-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='coolscan-s.c' object='libsane_coolscan_la-coolscan-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_coolscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_coolscan_la-coolscan-s.lo `test -f 'coolscan-s.c' || echo '$(srcdir)/'`coolscan-s.c
+
+libsane_coolscan2_la-coolscan2-s.lo: coolscan2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_coolscan2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_coolscan2_la-coolscan2-s.lo -MD -MP -MF $(DEPDIR)/libsane_coolscan2_la-coolscan2-s.Tpo -c -o libsane_coolscan2_la-coolscan2-s.lo `test -f 'coolscan2-s.c' || echo '$(srcdir)/'`coolscan2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_coolscan2_la-coolscan2-s.Tpo $(DEPDIR)/libsane_coolscan2_la-coolscan2-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='coolscan2-s.c' object='libsane_coolscan2_la-coolscan2-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_coolscan2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_coolscan2_la-coolscan2-s.lo `test -f 'coolscan2-s.c' || echo '$(srcdir)/'`coolscan2-s.c
+
+libsane_coolscan3_la-coolscan3-s.lo: coolscan3-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_coolscan3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_coolscan3_la-coolscan3-s.lo -MD -MP -MF $(DEPDIR)/libsane_coolscan3_la-coolscan3-s.Tpo -c -o libsane_coolscan3_la-coolscan3-s.lo `test -f 'coolscan3-s.c' || echo '$(srcdir)/'`coolscan3-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_coolscan3_la-coolscan3-s.Tpo $(DEPDIR)/libsane_coolscan3_la-coolscan3-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='coolscan3-s.c' object='libsane_coolscan3_la-coolscan3-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_coolscan3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_coolscan3_la-coolscan3-s.lo `test -f 'coolscan3-s.c' || echo '$(srcdir)/'`coolscan3-s.c
+
+libsane_dc210_la-dc210-s.lo: dc210-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dc210_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_dc210_la-dc210-s.lo -MD -MP -MF $(DEPDIR)/libsane_dc210_la-dc210-s.Tpo -c -o libsane_dc210_la-dc210-s.lo `test -f 'dc210-s.c' || echo '$(srcdir)/'`dc210-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_dc210_la-dc210-s.Tpo $(DEPDIR)/libsane_dc210_la-dc210-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dc210-s.c' object='libsane_dc210_la-dc210-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dc210_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_dc210_la-dc210-s.lo `test -f 'dc210-s.c' || echo '$(srcdir)/'`dc210-s.c
+
+libsane_dc240_la-dc240-s.lo: dc240-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dc240_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_dc240_la-dc240-s.lo -MD -MP -MF $(DEPDIR)/libsane_dc240_la-dc240-s.Tpo -c -o libsane_dc240_la-dc240-s.lo `test -f 'dc240-s.c' || echo '$(srcdir)/'`dc240-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_dc240_la-dc240-s.Tpo $(DEPDIR)/libsane_dc240_la-dc240-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dc240-s.c' object='libsane_dc240_la-dc240-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dc240_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_dc240_la-dc240-s.lo `test -f 'dc240-s.c' || echo '$(srcdir)/'`dc240-s.c
+
+libsane_dc25_la-dc25-s.lo: dc25-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dc25_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_dc25_la-dc25-s.lo -MD -MP -MF $(DEPDIR)/libsane_dc25_la-dc25-s.Tpo -c -o libsane_dc25_la-dc25-s.lo `test -f 'dc25-s.c' || echo '$(srcdir)/'`dc25-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_dc25_la-dc25-s.Tpo $(DEPDIR)/libsane_dc25_la-dc25-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dc25-s.c' object='libsane_dc25_la-dc25-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dc25_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_dc25_la-dc25-s.lo `test -f 'dc25-s.c' || echo '$(srcdir)/'`dc25-s.c
+
+libsane_dell1600n_net_la-dell1600n_net-s.lo: dell1600n_net-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dell1600n_net_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_dell1600n_net_la-dell1600n_net-s.lo -MD -MP -MF $(DEPDIR)/libsane_dell1600n_net_la-dell1600n_net-s.Tpo -c -o libsane_dell1600n_net_la-dell1600n_net-s.lo `test -f 'dell1600n_net-s.c' || echo '$(srcdir)/'`dell1600n_net-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_dell1600n_net_la-dell1600n_net-s.Tpo $(DEPDIR)/libsane_dell1600n_net_la-dell1600n_net-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dell1600n_net-s.c' object='libsane_dell1600n_net_la-dell1600n_net-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dell1600n_net_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_dell1600n_net_la-dell1600n_net-s.lo `test -f 'dell1600n_net-s.c' || echo '$(srcdir)/'`dell1600n_net-s.c
+
+libsane_dll_la-dll-s.lo: dll-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dll_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_dll_la-dll-s.lo -MD -MP -MF $(DEPDIR)/libsane_dll_la-dll-s.Tpo -c -o libsane_dll_la-dll-s.lo `test -f 'dll-s.c' || echo '$(srcdir)/'`dll-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_dll_la-dll-s.Tpo $(DEPDIR)/libsane_dll_la-dll-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dll-s.c' object='libsane_dll_la-dll-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dll_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_dll_la-dll-s.lo `test -f 'dll-s.c' || echo '$(srcdir)/'`dll-s.c
+
+libsane_dmc_la-dmc-s.lo: dmc-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dmc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_dmc_la-dmc-s.lo -MD -MP -MF $(DEPDIR)/libsane_dmc_la-dmc-s.Tpo -c -o libsane_dmc_la-dmc-s.lo `test -f 'dmc-s.c' || echo '$(srcdir)/'`dmc-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_dmc_la-dmc-s.Tpo $(DEPDIR)/libsane_dmc_la-dmc-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dmc-s.c' object='libsane_dmc_la-dmc-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_dmc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_dmc_la-dmc-s.lo `test -f 'dmc-s.c' || echo '$(srcdir)/'`dmc-s.c
+
+libsane_epjitsu_la-epjitsu-s.lo: epjitsu-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_epjitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_epjitsu_la-epjitsu-s.lo -MD -MP -MF $(DEPDIR)/libsane_epjitsu_la-epjitsu-s.Tpo -c -o libsane_epjitsu_la-epjitsu-s.lo `test -f 'epjitsu-s.c' || echo '$(srcdir)/'`epjitsu-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_epjitsu_la-epjitsu-s.Tpo $(DEPDIR)/libsane_epjitsu_la-epjitsu-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epjitsu-s.c' object='libsane_epjitsu_la-epjitsu-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_epjitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_epjitsu_la-epjitsu-s.lo `test -f 'epjitsu-s.c' || echo '$(srcdir)/'`epjitsu-s.c
+
+libsane_epson_la-epson-s.lo: epson-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_epson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_epson_la-epson-s.lo -MD -MP -MF $(DEPDIR)/libsane_epson_la-epson-s.Tpo -c -o libsane_epson_la-epson-s.lo `test -f 'epson-s.c' || echo '$(srcdir)/'`epson-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_epson_la-epson-s.Tpo $(DEPDIR)/libsane_epson_la-epson-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson-s.c' object='libsane_epson_la-epson-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_epson_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_epson_la-epson-s.lo `test -f 'epson-s.c' || echo '$(srcdir)/'`epson-s.c
+
+libsane_epson2_la-epson2-s.lo: epson2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_epson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_epson2_la-epson2-s.lo -MD -MP -MF $(DEPDIR)/libsane_epson2_la-epson2-s.Tpo -c -o libsane_epson2_la-epson2-s.lo `test -f 'epson2-s.c' || echo '$(srcdir)/'`epson2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_epson2_la-epson2-s.Tpo $(DEPDIR)/libsane_epson2_la-epson2-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='epson2-s.c' object='libsane_epson2_la-epson2-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_epson2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_epson2_la-epson2-s.lo `test -f 'epson2-s.c' || echo '$(srcdir)/'`epson2-s.c
+
+libsane_fujitsu_la-fujitsu-s.lo: fujitsu-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_fujitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_fujitsu_la-fujitsu-s.lo -MD -MP -MF $(DEPDIR)/libsane_fujitsu_la-fujitsu-s.Tpo -c -o libsane_fujitsu_la-fujitsu-s.lo `test -f 'fujitsu-s.c' || echo '$(srcdir)/'`fujitsu-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_fujitsu_la-fujitsu-s.Tpo $(DEPDIR)/libsane_fujitsu_la-fujitsu-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fujitsu-s.c' object='libsane_fujitsu_la-fujitsu-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_fujitsu_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_fujitsu_la-fujitsu-s.lo `test -f 'fujitsu-s.c' || echo '$(srcdir)/'`fujitsu-s.c
+
+libsane_genesys_la-genesys-s.lo: genesys-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_genesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_genesys_la-genesys-s.lo -MD -MP -MF $(DEPDIR)/libsane_genesys_la-genesys-s.Tpo -c -o libsane_genesys_la-genesys-s.lo `test -f 'genesys-s.c' || echo '$(srcdir)/'`genesys-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_genesys_la-genesys-s.Tpo $(DEPDIR)/libsane_genesys_la-genesys-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genesys-s.c' object='libsane_genesys_la-genesys-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_genesys_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_genesys_la-genesys-s.lo `test -f 'genesys-s.c' || echo '$(srcdir)/'`genesys-s.c
+
+libsane_gphoto2_la-gphoto2-s.lo: gphoto2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_gphoto2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_gphoto2_la-gphoto2-s.lo -MD -MP -MF $(DEPDIR)/libsane_gphoto2_la-gphoto2-s.Tpo -c -o libsane_gphoto2_la-gphoto2-s.lo `test -f 'gphoto2-s.c' || echo '$(srcdir)/'`gphoto2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_gphoto2_la-gphoto2-s.Tpo $(DEPDIR)/libsane_gphoto2_la-gphoto2-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gphoto2-s.c' object='libsane_gphoto2_la-gphoto2-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_gphoto2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_gphoto2_la-gphoto2-s.lo `test -f 'gphoto2-s.c' || echo '$(srcdir)/'`gphoto2-s.c
+
+libsane_gt68xx_la-gt68xx-s.lo: gt68xx-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_gt68xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_gt68xx_la-gt68xx-s.lo -MD -MP -MF $(DEPDIR)/libsane_gt68xx_la-gt68xx-s.Tpo -c -o libsane_gt68xx_la-gt68xx-s.lo `test -f 'gt68xx-s.c' || echo '$(srcdir)/'`gt68xx-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_gt68xx_la-gt68xx-s.Tpo $(DEPDIR)/libsane_gt68xx_la-gt68xx-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gt68xx-s.c' object='libsane_gt68xx_la-gt68xx-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_gt68xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_gt68xx_la-gt68xx-s.lo `test -f 'gt68xx-s.c' || echo '$(srcdir)/'`gt68xx-s.c
+
+libsane_hp_la-hp-s.lo: hp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hp_la-hp-s.lo -MD -MP -MF $(DEPDIR)/libsane_hp_la-hp-s.Tpo -c -o libsane_hp_la-hp-s.lo `test -f 'hp-s.c' || echo '$(srcdir)/'`hp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hp_la-hp-s.Tpo $(DEPDIR)/libsane_hp_la-hp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp-s.c' object='libsane_hp_la-hp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hp_la-hp-s.lo `test -f 'hp-s.c' || echo '$(srcdir)/'`hp-s.c
+
+libsane_hp3500_la-hp3500-s.lo: hp3500-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp3500_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hp3500_la-hp3500-s.lo -MD -MP -MF $(DEPDIR)/libsane_hp3500_la-hp3500-s.Tpo -c -o libsane_hp3500_la-hp3500-s.lo `test -f 'hp3500-s.c' || echo '$(srcdir)/'`hp3500-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hp3500_la-hp3500-s.Tpo $(DEPDIR)/libsane_hp3500_la-hp3500-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp3500-s.c' object='libsane_hp3500_la-hp3500-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp3500_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hp3500_la-hp3500-s.lo `test -f 'hp3500-s.c' || echo '$(srcdir)/'`hp3500-s.c
+
+libsane_hp3900_la-hp3900-s.lo: hp3900-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp3900_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hp3900_la-hp3900-s.lo -MD -MP -MF $(DEPDIR)/libsane_hp3900_la-hp3900-s.Tpo -c -o libsane_hp3900_la-hp3900-s.lo `test -f 'hp3900-s.c' || echo '$(srcdir)/'`hp3900-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hp3900_la-hp3900-s.Tpo $(DEPDIR)/libsane_hp3900_la-hp3900-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp3900-s.c' object='libsane_hp3900_la-hp3900-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp3900_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hp3900_la-hp3900-s.lo `test -f 'hp3900-s.c' || echo '$(srcdir)/'`hp3900-s.c
+
+libsane_hp4200_la-hp4200-s.lo: hp4200-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp4200_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hp4200_la-hp4200-s.lo -MD -MP -MF $(DEPDIR)/libsane_hp4200_la-hp4200-s.Tpo -c -o libsane_hp4200_la-hp4200-s.lo `test -f 'hp4200-s.c' || echo '$(srcdir)/'`hp4200-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hp4200_la-hp4200-s.Tpo $(DEPDIR)/libsane_hp4200_la-hp4200-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp4200-s.c' object='libsane_hp4200_la-hp4200-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp4200_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hp4200_la-hp4200-s.lo `test -f 'hp4200-s.c' || echo '$(srcdir)/'`hp4200-s.c
+
+libsane_hp5400_la-hp5400-s.lo: hp5400-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp5400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hp5400_la-hp5400-s.lo -MD -MP -MF $(DEPDIR)/libsane_hp5400_la-hp5400-s.Tpo -c -o libsane_hp5400_la-hp5400-s.lo `test -f 'hp5400-s.c' || echo '$(srcdir)/'`hp5400-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hp5400_la-hp5400-s.Tpo $(DEPDIR)/libsane_hp5400_la-hp5400-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp5400-s.c' object='libsane_hp5400_la-hp5400-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp5400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hp5400_la-hp5400-s.lo `test -f 'hp5400-s.c' || echo '$(srcdir)/'`hp5400-s.c
+
+libsane_hp5590_la-hp5590-s.lo: hp5590-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp5590_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hp5590_la-hp5590-s.lo -MD -MP -MF $(DEPDIR)/libsane_hp5590_la-hp5590-s.Tpo -c -o libsane_hp5590_la-hp5590-s.lo `test -f 'hp5590-s.c' || echo '$(srcdir)/'`hp5590-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hp5590_la-hp5590-s.Tpo $(DEPDIR)/libsane_hp5590_la-hp5590-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hp5590-s.c' object='libsane_hp5590_la-hp5590-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hp5590_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hp5590_la-hp5590-s.lo `test -f 'hp5590-s.c' || echo '$(srcdir)/'`hp5590-s.c
+
+libsane_hpljm1005_la-hpljm1005-s.lo: hpljm1005-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hpljm1005_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hpljm1005_la-hpljm1005-s.lo -MD -MP -MF $(DEPDIR)/libsane_hpljm1005_la-hpljm1005-s.Tpo -c -o libsane_hpljm1005_la-hpljm1005-s.lo `test -f 'hpljm1005-s.c' || echo '$(srcdir)/'`hpljm1005-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hpljm1005_la-hpljm1005-s.Tpo $(DEPDIR)/libsane_hpljm1005_la-hpljm1005-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hpljm1005-s.c' object='libsane_hpljm1005_la-hpljm1005-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hpljm1005_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hpljm1005_la-hpljm1005-s.lo `test -f 'hpljm1005-s.c' || echo '$(srcdir)/'`hpljm1005-s.c
+
+libsane_hpsj5s_la-hpsj5s-s.lo: hpsj5s-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hpsj5s_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hpsj5s_la-hpsj5s-s.lo -MD -MP -MF $(DEPDIR)/libsane_hpsj5s_la-hpsj5s-s.Tpo -c -o libsane_hpsj5s_la-hpsj5s-s.lo `test -f 'hpsj5s-s.c' || echo '$(srcdir)/'`hpsj5s-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hpsj5s_la-hpsj5s-s.Tpo $(DEPDIR)/libsane_hpsj5s_la-hpsj5s-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hpsj5s-s.c' object='libsane_hpsj5s_la-hpsj5s-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hpsj5s_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hpsj5s_la-hpsj5s-s.lo `test -f 'hpsj5s-s.c' || echo '$(srcdir)/'`hpsj5s-s.c
+
+libsane_hs2p_la-hs2p-s.lo: hs2p-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hs2p_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_hs2p_la-hs2p-s.lo -MD -MP -MF $(DEPDIR)/libsane_hs2p_la-hs2p-s.Tpo -c -o libsane_hs2p_la-hs2p-s.lo `test -f 'hs2p-s.c' || echo '$(srcdir)/'`hs2p-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_hs2p_la-hs2p-s.Tpo $(DEPDIR)/libsane_hs2p_la-hs2p-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hs2p-s.c' object='libsane_hs2p_la-hs2p-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_hs2p_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_hs2p_la-hs2p-s.lo `test -f 'hs2p-s.c' || echo '$(srcdir)/'`hs2p-s.c
+
+libsane_ibm_la-ibm-s.lo: ibm-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_ibm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_ibm_la-ibm-s.lo -MD -MP -MF $(DEPDIR)/libsane_ibm_la-ibm-s.Tpo -c -o libsane_ibm_la-ibm-s.lo `test -f 'ibm-s.c' || echo '$(srcdir)/'`ibm-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_ibm_la-ibm-s.Tpo $(DEPDIR)/libsane_ibm_la-ibm-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ibm-s.c' object='libsane_ibm_la-ibm-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_ibm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_ibm_la-ibm-s.lo `test -f 'ibm-s.c' || echo '$(srcdir)/'`ibm-s.c
+
+libsane_kodak_la-kodak-s.lo: kodak-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kodak_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_kodak_la-kodak-s.lo -MD -MP -MF $(DEPDIR)/libsane_kodak_la-kodak-s.Tpo -c -o libsane_kodak_la-kodak-s.lo `test -f 'kodak-s.c' || echo '$(srcdir)/'`kodak-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_kodak_la-kodak-s.Tpo $(DEPDIR)/libsane_kodak_la-kodak-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kodak-s.c' object='libsane_kodak_la-kodak-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kodak_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_kodak_la-kodak-s.lo `test -f 'kodak-s.c' || echo '$(srcdir)/'`kodak-s.c
+
+libsane_kodakaio_la-kodakaio-s.lo: kodakaio-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kodakaio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_kodakaio_la-kodakaio-s.lo -MD -MP -MF $(DEPDIR)/libsane_kodakaio_la-kodakaio-s.Tpo -c -o libsane_kodakaio_la-kodakaio-s.lo `test -f 'kodakaio-s.c' || echo '$(srcdir)/'`kodakaio-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_kodakaio_la-kodakaio-s.Tpo $(DEPDIR)/libsane_kodakaio_la-kodakaio-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kodakaio-s.c' object='libsane_kodakaio_la-kodakaio-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kodakaio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_kodakaio_la-kodakaio-s.lo `test -f 'kodakaio-s.c' || echo '$(srcdir)/'`kodakaio-s.c
+
+libsane_kvs1025_la-kvs1025-s.lo: kvs1025-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_kvs1025_la-kvs1025-s.lo -MD -MP -MF $(DEPDIR)/libsane_kvs1025_la-kvs1025-s.Tpo -c -o libsane_kvs1025_la-kvs1025-s.lo `test -f 'kvs1025-s.c' || echo '$(srcdir)/'`kvs1025-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_kvs1025_la-kvs1025-s.Tpo $(DEPDIR)/libsane_kvs1025_la-kvs1025-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs1025-s.c' object='libsane_kvs1025_la-kvs1025-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kvs1025_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_kvs1025_la-kvs1025-s.lo `test -f 'kvs1025-s.c' || echo '$(srcdir)/'`kvs1025-s.c
+
+libsane_kvs20xx_la-kvs20xx-s.lo: kvs20xx-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_kvs20xx_la-kvs20xx-s.lo -MD -MP -MF $(DEPDIR)/libsane_kvs20xx_la-kvs20xx-s.Tpo -c -o libsane_kvs20xx_la-kvs20xx-s.lo `test -f 'kvs20xx-s.c' || echo '$(srcdir)/'`kvs20xx-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_kvs20xx_la-kvs20xx-s.Tpo $(DEPDIR)/libsane_kvs20xx_la-kvs20xx-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs20xx-s.c' object='libsane_kvs20xx_la-kvs20xx-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kvs20xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_kvs20xx_la-kvs20xx-s.lo `test -f 'kvs20xx-s.c' || echo '$(srcdir)/'`kvs20xx-s.c
+
+libsane_kvs40xx_la-kvs40xx-s.lo: kvs40xx-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_kvs40xx_la-kvs40xx-s.lo -MD -MP -MF $(DEPDIR)/libsane_kvs40xx_la-kvs40xx-s.Tpo -c -o libsane_kvs40xx_la-kvs40xx-s.lo `test -f 'kvs40xx-s.c' || echo '$(srcdir)/'`kvs40xx-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_kvs40xx_la-kvs40xx-s.Tpo $(DEPDIR)/libsane_kvs40xx_la-kvs40xx-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kvs40xx-s.c' object='libsane_kvs40xx_la-kvs40xx-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_kvs40xx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_kvs40xx_la-kvs40xx-s.lo `test -f 'kvs40xx-s.c' || echo '$(srcdir)/'`kvs40xx-s.c
+
+libsane_leo_la-leo-s.lo: leo-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_leo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_leo_la-leo-s.lo -MD -MP -MF $(DEPDIR)/libsane_leo_la-leo-s.Tpo -c -o libsane_leo_la-leo-s.lo `test -f 'leo-s.c' || echo '$(srcdir)/'`leo-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_leo_la-leo-s.Tpo $(DEPDIR)/libsane_leo_la-leo-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='leo-s.c' object='libsane_leo_la-leo-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_leo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_leo_la-leo-s.lo `test -f 'leo-s.c' || echo '$(srcdir)/'`leo-s.c
+
+libsane_lexmark_la-lexmark-s.lo: lexmark-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_lexmark_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_lexmark_la-lexmark-s.lo -MD -MP -MF $(DEPDIR)/libsane_lexmark_la-lexmark-s.Tpo -c -o libsane_lexmark_la-lexmark-s.lo `test -f 'lexmark-s.c' || echo '$(srcdir)/'`lexmark-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_lexmark_la-lexmark-s.Tpo $(DEPDIR)/libsane_lexmark_la-lexmark-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lexmark-s.c' object='libsane_lexmark_la-lexmark-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_lexmark_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_lexmark_la-lexmark-s.lo `test -f 'lexmark-s.c' || echo '$(srcdir)/'`lexmark-s.c
+
+libsane_ma1509_la-ma1509-s.lo: ma1509-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_ma1509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_ma1509_la-ma1509-s.lo -MD -MP -MF $(DEPDIR)/libsane_ma1509_la-ma1509-s.Tpo -c -o libsane_ma1509_la-ma1509-s.lo `test -f 'ma1509-s.c' || echo '$(srcdir)/'`ma1509-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_ma1509_la-ma1509-s.Tpo $(DEPDIR)/libsane_ma1509_la-ma1509-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ma1509-s.c' object='libsane_ma1509_la-ma1509-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_ma1509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_ma1509_la-ma1509-s.lo `test -f 'ma1509-s.c' || echo '$(srcdir)/'`ma1509-s.c
+
+libsane_magicolor_la-magicolor-s.lo: magicolor-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_magicolor_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_magicolor_la-magicolor-s.lo -MD -MP -MF $(DEPDIR)/libsane_magicolor_la-magicolor-s.Tpo -c -o libsane_magicolor_la-magicolor-s.lo `test -f 'magicolor-s.c' || echo '$(srcdir)/'`magicolor-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_magicolor_la-magicolor-s.Tpo $(DEPDIR)/libsane_magicolor_la-magicolor-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='magicolor-s.c' object='libsane_magicolor_la-magicolor-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_magicolor_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_magicolor_la-magicolor-s.lo `test -f 'magicolor-s.c' || echo '$(srcdir)/'`magicolor-s.c
+
+libsane_matsushita_la-matsushita-s.lo: matsushita-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_matsushita_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_matsushita_la-matsushita-s.lo -MD -MP -MF $(DEPDIR)/libsane_matsushita_la-matsushita-s.Tpo -c -o libsane_matsushita_la-matsushita-s.lo `test -f 'matsushita-s.c' || echo '$(srcdir)/'`matsushita-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_matsushita_la-matsushita-s.Tpo $(DEPDIR)/libsane_matsushita_la-matsushita-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='matsushita-s.c' object='libsane_matsushita_la-matsushita-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_matsushita_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_matsushita_la-matsushita-s.lo `test -f 'matsushita-s.c' || echo '$(srcdir)/'`matsushita-s.c
+
+libsane_microtek_la-microtek-s.lo: microtek-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_microtek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_microtek_la-microtek-s.lo -MD -MP -MF $(DEPDIR)/libsane_microtek_la-microtek-s.Tpo -c -o libsane_microtek_la-microtek-s.lo `test -f 'microtek-s.c' || echo '$(srcdir)/'`microtek-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_microtek_la-microtek-s.Tpo $(DEPDIR)/libsane_microtek_la-microtek-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='microtek-s.c' object='libsane_microtek_la-microtek-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_microtek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_microtek_la-microtek-s.lo `test -f 'microtek-s.c' || echo '$(srcdir)/'`microtek-s.c
+
+libsane_microtek2_la-microtek2-s.lo: microtek2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_microtek2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_microtek2_la-microtek2-s.lo -MD -MP -MF $(DEPDIR)/libsane_microtek2_la-microtek2-s.Tpo -c -o libsane_microtek2_la-microtek2-s.lo `test -f 'microtek2-s.c' || echo '$(srcdir)/'`microtek2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_microtek2_la-microtek2-s.Tpo $(DEPDIR)/libsane_microtek2_la-microtek2-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='microtek2-s.c' object='libsane_microtek2_la-microtek2-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_microtek2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_microtek2_la-microtek2-s.lo `test -f 'microtek2-s.c' || echo '$(srcdir)/'`microtek2-s.c
+
+libsane_mustek_la-mustek-s.lo: mustek-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_mustek_la-mustek-s.lo -MD -MP -MF $(DEPDIR)/libsane_mustek_la-mustek-s.Tpo -c -o libsane_mustek_la-mustek-s.lo `test -f 'mustek-s.c' || echo '$(srcdir)/'`mustek-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_mustek_la-mustek-s.Tpo $(DEPDIR)/libsane_mustek_la-mustek-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek-s.c' object='libsane_mustek_la-mustek-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_mustek_la-mustek-s.lo `test -f 'mustek-s.c' || echo '$(srcdir)/'`mustek-s.c
+
+libsane_mustek_pp_la-mustek_pp-s.lo: mustek_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_mustek_pp_la-mustek_pp-s.lo -MD -MP -MF $(DEPDIR)/libsane_mustek_pp_la-mustek_pp-s.Tpo -c -o libsane_mustek_pp_la-mustek_pp-s.lo `test -f 'mustek_pp-s.c' || echo '$(srcdir)/'`mustek_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_mustek_pp_la-mustek_pp-s.Tpo $(DEPDIR)/libsane_mustek_pp_la-mustek_pp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek_pp-s.c' object='libsane_mustek_pp_la-mustek_pp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_mustek_pp_la-mustek_pp-s.lo `test -f 'mustek_pp-s.c' || echo '$(srcdir)/'`mustek_pp-s.c
+
+libsane_mustek_usb_la-mustek_usb-s.lo: mustek_usb-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_usb_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_mustek_usb_la-mustek_usb-s.lo -MD -MP -MF $(DEPDIR)/libsane_mustek_usb_la-mustek_usb-s.Tpo -c -o libsane_mustek_usb_la-mustek_usb-s.lo `test -f 'mustek_usb-s.c' || echo '$(srcdir)/'`mustek_usb-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_mustek_usb_la-mustek_usb-s.Tpo $(DEPDIR)/libsane_mustek_usb_la-mustek_usb-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek_usb-s.c' object='libsane_mustek_usb_la-mustek_usb-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_usb_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_mustek_usb_la-mustek_usb-s.lo `test -f 'mustek_usb-s.c' || echo '$(srcdir)/'`mustek_usb-s.c
+
+libsane_mustek_usb2_la-mustek_usb2-s.lo: mustek_usb2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_usb2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_mustek_usb2_la-mustek_usb2-s.lo -MD -MP -MF $(DEPDIR)/libsane_mustek_usb2_la-mustek_usb2-s.Tpo -c -o libsane_mustek_usb2_la-mustek_usb2-s.lo `test -f 'mustek_usb2-s.c' || echo '$(srcdir)/'`mustek_usb2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_mustek_usb2_la-mustek_usb2-s.Tpo $(DEPDIR)/libsane_mustek_usb2_la-mustek_usb2-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mustek_usb2-s.c' object='libsane_mustek_usb2_la-mustek_usb2-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_mustek_usb2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_mustek_usb2_la-mustek_usb2-s.lo `test -f 'mustek_usb2-s.c' || echo '$(srcdir)/'`mustek_usb2-s.c
+
+libsane_nec_la-nec-s.lo: nec-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_nec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_nec_la-nec-s.lo -MD -MP -MF $(DEPDIR)/libsane_nec_la-nec-s.Tpo -c -o libsane_nec_la-nec-s.lo `test -f 'nec-s.c' || echo '$(srcdir)/'`nec-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_nec_la-nec-s.Tpo $(DEPDIR)/libsane_nec_la-nec-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nec-s.c' object='libsane_nec_la-nec-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_nec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_nec_la-nec-s.lo `test -f 'nec-s.c' || echo '$(srcdir)/'`nec-s.c
+
+libsane_net_la-net-s.lo: net-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_net_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_net_la-net-s.lo -MD -MP -MF $(DEPDIR)/libsane_net_la-net-s.Tpo -c -o libsane_net_la-net-s.lo `test -f 'net-s.c' || echo '$(srcdir)/'`net-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_net_la-net-s.Tpo $(DEPDIR)/libsane_net_la-net-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net-s.c' object='libsane_net_la-net-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_net_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_net_la-net-s.lo `test -f 'net-s.c' || echo '$(srcdir)/'`net-s.c
+
+libsane_niash_la-niash-s.lo: niash-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_niash_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_niash_la-niash-s.lo -MD -MP -MF $(DEPDIR)/libsane_niash_la-niash-s.Tpo -c -o libsane_niash_la-niash-s.lo `test -f 'niash-s.c' || echo '$(srcdir)/'`niash-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_niash_la-niash-s.Tpo $(DEPDIR)/libsane_niash_la-niash-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='niash-s.c' object='libsane_niash_la-niash-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_niash_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_niash_la-niash-s.lo `test -f 'niash-s.c' || echo '$(srcdir)/'`niash-s.c
+
+libsane_p5_la-p5-s.lo: p5-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_p5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_p5_la-p5-s.lo -MD -MP -MF $(DEPDIR)/libsane_p5_la-p5-s.Tpo -c -o libsane_p5_la-p5-s.lo `test -f 'p5-s.c' || echo '$(srcdir)/'`p5-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_p5_la-p5-s.Tpo $(DEPDIR)/libsane_p5_la-p5-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='p5-s.c' object='libsane_p5_la-p5-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_p5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_p5_la-p5-s.lo `test -f 'p5-s.c' || echo '$(srcdir)/'`p5-s.c
+
+libsane_pie_la-pie-s.lo: pie-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pie_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_pie_la-pie-s.lo -MD -MP -MF $(DEPDIR)/libsane_pie_la-pie-s.Tpo -c -o libsane_pie_la-pie-s.lo `test -f 'pie-s.c' || echo '$(srcdir)/'`pie-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_pie_la-pie-s.Tpo $(DEPDIR)/libsane_pie_la-pie-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pie-s.c' object='libsane_pie_la-pie-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pie_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_pie_la-pie-s.lo `test -f 'pie-s.c' || echo '$(srcdir)/'`pie-s.c
+
+libsane_pint_la-pint-s.lo: pint-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pint_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_pint_la-pint-s.lo -MD -MP -MF $(DEPDIR)/libsane_pint_la-pint-s.Tpo -c -o libsane_pint_la-pint-s.lo `test -f 'pint-s.c' || echo '$(srcdir)/'`pint-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_pint_la-pint-s.Tpo $(DEPDIR)/libsane_pint_la-pint-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pint-s.c' object='libsane_pint_la-pint-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pint_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_pint_la-pint-s.lo `test -f 'pint-s.c' || echo '$(srcdir)/'`pint-s.c
+
+libsane_pixma_la-pixma-s.lo: pixma-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_pixma_la-pixma-s.lo -MD -MP -MF $(DEPDIR)/libsane_pixma_la-pixma-s.Tpo -c -o libsane_pixma_la-pixma-s.lo `test -f 'pixma-s.c' || echo '$(srcdir)/'`pixma-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_pixma_la-pixma-s.Tpo $(DEPDIR)/libsane_pixma_la-pixma-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pixma-s.c' object='libsane_pixma_la-pixma-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pixma_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_pixma_la-pixma-s.lo `test -f 'pixma-s.c' || echo '$(srcdir)/'`pixma-s.c
+
+libsane_plustek_la-plustek-s.lo: plustek-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_plustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_plustek_la-plustek-s.lo -MD -MP -MF $(DEPDIR)/libsane_plustek_la-plustek-s.Tpo -c -o libsane_plustek_la-plustek-s.lo `test -f 'plustek-s.c' || echo '$(srcdir)/'`plustek-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_plustek_la-plustek-s.Tpo $(DEPDIR)/libsane_plustek_la-plustek-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plustek-s.c' object='libsane_plustek_la-plustek-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_plustek_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_plustek_la-plustek-s.lo `test -f 'plustek-s.c' || echo '$(srcdir)/'`plustek-s.c
+
+libsane_plustek_pp_la-plustek_pp-s.lo: plustek_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_plustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_plustek_pp_la-plustek_pp-s.lo -MD -MP -MF $(DEPDIR)/libsane_plustek_pp_la-plustek_pp-s.Tpo -c -o libsane_plustek_pp_la-plustek_pp-s.lo `test -f 'plustek_pp-s.c' || echo '$(srcdir)/'`plustek_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_plustek_pp_la-plustek_pp-s.Tpo $(DEPDIR)/libsane_plustek_pp_la-plustek_pp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plustek_pp-s.c' object='libsane_plustek_pp_la-plustek_pp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_plustek_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_plustek_pp_la-plustek_pp-s.lo `test -f 'plustek_pp-s.c' || echo '$(srcdir)/'`plustek_pp-s.c
+
+libsane_pnm_la-pnm-s.lo: pnm-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pnm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_pnm_la-pnm-s.lo -MD -MP -MF $(DEPDIR)/libsane_pnm_la-pnm-s.Tpo -c -o libsane_pnm_la-pnm-s.lo `test -f 'pnm-s.c' || echo '$(srcdir)/'`pnm-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_pnm_la-pnm-s.Tpo $(DEPDIR)/libsane_pnm_la-pnm-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pnm-s.c' object='libsane_pnm_la-pnm-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_pnm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_pnm_la-pnm-s.lo `test -f 'pnm-s.c' || echo '$(srcdir)/'`pnm-s.c
+
+libsane_qcam_la-qcam-s.lo: qcam-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_qcam_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_qcam_la-qcam-s.lo -MD -MP -MF $(DEPDIR)/libsane_qcam_la-qcam-s.Tpo -c -o libsane_qcam_la-qcam-s.lo `test -f 'qcam-s.c' || echo '$(srcdir)/'`qcam-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_qcam_la-qcam-s.Tpo $(DEPDIR)/libsane_qcam_la-qcam-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='qcam-s.c' object='libsane_qcam_la-qcam-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_qcam_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_qcam_la-qcam-s.lo `test -f 'qcam-s.c' || echo '$(srcdir)/'`qcam-s.c
+
+libsane_ricoh_la-ricoh-s.lo: ricoh-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_ricoh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_ricoh_la-ricoh-s.lo -MD -MP -MF $(DEPDIR)/libsane_ricoh_la-ricoh-s.Tpo -c -o libsane_ricoh_la-ricoh-s.lo `test -f 'ricoh-s.c' || echo '$(srcdir)/'`ricoh-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_ricoh_la-ricoh-s.Tpo $(DEPDIR)/libsane_ricoh_la-ricoh-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ricoh-s.c' object='libsane_ricoh_la-ricoh-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_ricoh_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_ricoh_la-ricoh-s.lo `test -f 'ricoh-s.c' || echo '$(srcdir)/'`ricoh-s.c
+
+libsane_rts8891_la-rts8891-s.lo: rts8891-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_rts8891_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_rts8891_la-rts8891-s.lo -MD -MP -MF $(DEPDIR)/libsane_rts8891_la-rts8891-s.Tpo -c -o libsane_rts8891_la-rts8891-s.lo `test -f 'rts8891-s.c' || echo '$(srcdir)/'`rts8891-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_rts8891_la-rts8891-s.Tpo $(DEPDIR)/libsane_rts8891_la-rts8891-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rts8891-s.c' object='libsane_rts8891_la-rts8891-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_rts8891_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_rts8891_la-rts8891-s.lo `test -f 'rts8891-s.c' || echo '$(srcdir)/'`rts8891-s.c
+
+libsane_s9036_la-s9036-s.lo: s9036-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_s9036_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_s9036_la-s9036-s.lo -MD -MP -MF $(DEPDIR)/libsane_s9036_la-s9036-s.Tpo -c -o libsane_s9036_la-s9036-s.lo `test -f 's9036-s.c' || echo '$(srcdir)/'`s9036-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_s9036_la-s9036-s.Tpo $(DEPDIR)/libsane_s9036_la-s9036-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='s9036-s.c' object='libsane_s9036_la-s9036-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_s9036_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_s9036_la-s9036-s.lo `test -f 's9036-s.c' || echo '$(srcdir)/'`s9036-s.c
+
+libsane_sceptre_la-sceptre-s.lo: sceptre-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sceptre_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_sceptre_la-sceptre-s.lo -MD -MP -MF $(DEPDIR)/libsane_sceptre_la-sceptre-s.Tpo -c -o libsane_sceptre_la-sceptre-s.lo `test -f 'sceptre-s.c' || echo '$(srcdir)/'`sceptre-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_sceptre_la-sceptre-s.Tpo $(DEPDIR)/libsane_sceptre_la-sceptre-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sceptre-s.c' object='libsane_sceptre_la-sceptre-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sceptre_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_sceptre_la-sceptre-s.lo `test -f 'sceptre-s.c' || echo '$(srcdir)/'`sceptre-s.c
+
+libsane_sharp_la-sharp-s.lo: sharp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sharp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_sharp_la-sharp-s.lo -MD -MP -MF $(DEPDIR)/libsane_sharp_la-sharp-s.Tpo -c -o libsane_sharp_la-sharp-s.lo `test -f 'sharp-s.c' || echo '$(srcdir)/'`sharp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_sharp_la-sharp-s.Tpo $(DEPDIR)/libsane_sharp_la-sharp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sharp-s.c' object='libsane_sharp_la-sharp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sharp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_sharp_la-sharp-s.lo `test -f 'sharp-s.c' || echo '$(srcdir)/'`sharp-s.c
+
+libsane_sm3600_la-sm3600-s.lo: sm3600-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sm3600_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_sm3600_la-sm3600-s.lo -MD -MP -MF $(DEPDIR)/libsane_sm3600_la-sm3600-s.Tpo -c -o libsane_sm3600_la-sm3600-s.lo `test -f 'sm3600-s.c' || echo '$(srcdir)/'`sm3600-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_sm3600_la-sm3600-s.Tpo $(DEPDIR)/libsane_sm3600_la-sm3600-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sm3600-s.c' object='libsane_sm3600_la-sm3600-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sm3600_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_sm3600_la-sm3600-s.lo `test -f 'sm3600-s.c' || echo '$(srcdir)/'`sm3600-s.c
+
+libsane_sm3840_la-sm3840-s.lo: sm3840-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sm3840_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_sm3840_la-sm3840-s.lo -MD -MP -MF $(DEPDIR)/libsane_sm3840_la-sm3840-s.Tpo -c -o libsane_sm3840_la-sm3840-s.lo `test -f 'sm3840-s.c' || echo '$(srcdir)/'`sm3840-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_sm3840_la-sm3840-s.Tpo $(DEPDIR)/libsane_sm3840_la-sm3840-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sm3840-s.c' object='libsane_sm3840_la-sm3840-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sm3840_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_sm3840_la-sm3840-s.lo `test -f 'sm3840-s.c' || echo '$(srcdir)/'`sm3840-s.c
+
+libsane_snapscan_la-snapscan-s.lo: snapscan-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_snapscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_snapscan_la-snapscan-s.lo -MD -MP -MF $(DEPDIR)/libsane_snapscan_la-snapscan-s.Tpo -c -o libsane_snapscan_la-snapscan-s.lo `test -f 'snapscan-s.c' || echo '$(srcdir)/'`snapscan-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_snapscan_la-snapscan-s.Tpo $(DEPDIR)/libsane_snapscan_la-snapscan-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snapscan-s.c' object='libsane_snapscan_la-snapscan-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_snapscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_snapscan_la-snapscan-s.lo `test -f 'snapscan-s.c' || echo '$(srcdir)/'`snapscan-s.c
+
+libsane_sp15c_la-sp15c-s.lo: sp15c-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sp15c_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_sp15c_la-sp15c-s.lo -MD -MP -MF $(DEPDIR)/libsane_sp15c_la-sp15c-s.Tpo -c -o libsane_sp15c_la-sp15c-s.lo `test -f 'sp15c-s.c' || echo '$(srcdir)/'`sp15c-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_sp15c_la-sp15c-s.Tpo $(DEPDIR)/libsane_sp15c_la-sp15c-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sp15c-s.c' object='libsane_sp15c_la-sp15c-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_sp15c_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_sp15c_la-sp15c-s.lo `test -f 'sp15c-s.c' || echo '$(srcdir)/'`sp15c-s.c
+
+libsane_st400_la-st400-s.lo: st400-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_st400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_st400_la-st400-s.lo -MD -MP -MF $(DEPDIR)/libsane_st400_la-st400-s.Tpo -c -o libsane_st400_la-st400-s.lo `test -f 'st400-s.c' || echo '$(srcdir)/'`st400-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_st400_la-st400-s.Tpo $(DEPDIR)/libsane_st400_la-st400-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='st400-s.c' object='libsane_st400_la-st400-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_st400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_st400_la-st400-s.lo `test -f 'st400-s.c' || echo '$(srcdir)/'`st400-s.c
+
+libsane_stv680_la-stv680-s.lo: stv680-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_stv680_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_stv680_la-stv680-s.lo -MD -MP -MF $(DEPDIR)/libsane_stv680_la-stv680-s.Tpo -c -o libsane_stv680_la-stv680-s.lo `test -f 'stv680-s.c' || echo '$(srcdir)/'`stv680-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_stv680_la-stv680-s.Tpo $(DEPDIR)/libsane_stv680_la-stv680-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stv680-s.c' object='libsane_stv680_la-stv680-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_stv680_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_stv680_la-stv680-s.lo `test -f 'stv680-s.c' || echo '$(srcdir)/'`stv680-s.c
+
+libsane_tamarack_la-tamarack-s.lo: tamarack-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_tamarack_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_tamarack_la-tamarack-s.lo -MD -MP -MF $(DEPDIR)/libsane_tamarack_la-tamarack-s.Tpo -c -o libsane_tamarack_la-tamarack-s.lo `test -f 'tamarack-s.c' || echo '$(srcdir)/'`tamarack-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_tamarack_la-tamarack-s.Tpo $(DEPDIR)/libsane_tamarack_la-tamarack-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tamarack-s.c' object='libsane_tamarack_la-tamarack-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_tamarack_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_tamarack_la-tamarack-s.lo `test -f 'tamarack-s.c' || echo '$(srcdir)/'`tamarack-s.c
+
+libsane_teco1_la-teco1-s.lo: teco1-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_teco1_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_teco1_la-teco1-s.lo -MD -MP -MF $(DEPDIR)/libsane_teco1_la-teco1-s.Tpo -c -o libsane_teco1_la-teco1-s.lo `test -f 'teco1-s.c' || echo '$(srcdir)/'`teco1-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_teco1_la-teco1-s.Tpo $(DEPDIR)/libsane_teco1_la-teco1-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='teco1-s.c' object='libsane_teco1_la-teco1-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_teco1_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_teco1_la-teco1-s.lo `test -f 'teco1-s.c' || echo '$(srcdir)/'`teco1-s.c
+
+libsane_teco2_la-teco2-s.lo: teco2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_teco2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_teco2_la-teco2-s.lo -MD -MP -MF $(DEPDIR)/libsane_teco2_la-teco2-s.Tpo -c -o libsane_teco2_la-teco2-s.lo `test -f 'teco2-s.c' || echo '$(srcdir)/'`teco2-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_teco2_la-teco2-s.Tpo $(DEPDIR)/libsane_teco2_la-teco2-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='teco2-s.c' object='libsane_teco2_la-teco2-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_teco2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_teco2_la-teco2-s.lo `test -f 'teco2-s.c' || echo '$(srcdir)/'`teco2-s.c
+
+libsane_teco3_la-teco3-s.lo: teco3-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_teco3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_teco3_la-teco3-s.lo -MD -MP -MF $(DEPDIR)/libsane_teco3_la-teco3-s.Tpo -c -o libsane_teco3_la-teco3-s.lo `test -f 'teco3-s.c' || echo '$(srcdir)/'`teco3-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_teco3_la-teco3-s.Tpo $(DEPDIR)/libsane_teco3_la-teco3-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='teco3-s.c' object='libsane_teco3_la-teco3-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_teco3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_teco3_la-teco3-s.lo `test -f 'teco3-s.c' || echo '$(srcdir)/'`teco3-s.c
+
+libsane_test_la-test-s.lo: test-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_test_la-test-s.lo -MD -MP -MF $(DEPDIR)/libsane_test_la-test-s.Tpo -c -o libsane_test_la-test-s.lo `test -f 'test-s.c' || echo '$(srcdir)/'`test-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_test_la-test-s.Tpo $(DEPDIR)/libsane_test_la-test-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-s.c' object='libsane_test_la-test-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_test_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_test_la-test-s.lo `test -f 'test-s.c' || echo '$(srcdir)/'`test-s.c
+
+libsane_u12_la-u12-s.lo: u12-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_u12_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_u12_la-u12-s.lo -MD -MP -MF $(DEPDIR)/libsane_u12_la-u12-s.Tpo -c -o libsane_u12_la-u12-s.lo `test -f 'u12-s.c' || echo '$(srcdir)/'`u12-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_u12_la-u12-s.Tpo $(DEPDIR)/libsane_u12_la-u12-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='u12-s.c' object='libsane_u12_la-u12-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_u12_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_u12_la-u12-s.lo `test -f 'u12-s.c' || echo '$(srcdir)/'`u12-s.c
+
+libsane_umax_la-umax-s.lo: umax-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_umax_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_umax_la-umax-s.lo -MD -MP -MF $(DEPDIR)/libsane_umax_la-umax-s.Tpo -c -o libsane_umax_la-umax-s.lo `test -f 'umax-s.c' || echo '$(srcdir)/'`umax-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_umax_la-umax-s.Tpo $(DEPDIR)/libsane_umax_la-umax-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax-s.c' object='libsane_umax_la-umax-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_umax_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_umax_la-umax-s.lo `test -f 'umax-s.c' || echo '$(srcdir)/'`umax-s.c
+
+libsane_umax1220u_la-umax1220u-s.lo: umax1220u-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_umax1220u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_umax1220u_la-umax1220u-s.lo -MD -MP -MF $(DEPDIR)/libsane_umax1220u_la-umax1220u-s.Tpo -c -o libsane_umax1220u_la-umax1220u-s.lo `test -f 'umax1220u-s.c' || echo '$(srcdir)/'`umax1220u-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_umax1220u_la-umax1220u-s.Tpo $(DEPDIR)/libsane_umax1220u_la-umax1220u-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax1220u-s.c' object='libsane_umax1220u_la-umax1220u-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_umax1220u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_umax1220u_la-umax1220u-s.lo `test -f 'umax1220u-s.c' || echo '$(srcdir)/'`umax1220u-s.c
+
+libsane_umax_pp_la-umax_pp-s.lo: umax_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_umax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_umax_pp_la-umax_pp-s.lo -MD -MP -MF $(DEPDIR)/libsane_umax_pp_la-umax_pp-s.Tpo -c -o libsane_umax_pp_la-umax_pp-s.lo `test -f 'umax_pp-s.c' || echo '$(srcdir)/'`umax_pp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_umax_pp_la-umax_pp-s.Tpo $(DEPDIR)/libsane_umax_pp_la-umax_pp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax_pp-s.c' object='libsane_umax_pp_la-umax_pp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_umax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_umax_pp_la-umax_pp-s.lo `test -f 'umax_pp-s.c' || echo '$(srcdir)/'`umax_pp-s.c
+
+libsane_v4l_la-v4l-s.lo: v4l-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_v4l_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_v4l_la-v4l-s.lo -MD -MP -MF $(DEPDIR)/libsane_v4l_la-v4l-s.Tpo -c -o libsane_v4l_la-v4l-s.lo `test -f 'v4l-s.c' || echo '$(srcdir)/'`v4l-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_v4l_la-v4l-s.Tpo $(DEPDIR)/libsane_v4l_la-v4l-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='v4l-s.c' object='libsane_v4l_la-v4l-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_v4l_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_v4l_la-v4l-s.lo `test -f 'v4l-s.c' || echo '$(srcdir)/'`v4l-s.c
+
+libsane_xerox_mfp_la-xerox_mfp-s.lo: xerox_mfp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_xerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_xerox_mfp_la-xerox_mfp-s.lo -MD -MP -MF $(DEPDIR)/libsane_xerox_mfp_la-xerox_mfp-s.Tpo -c -o libsane_xerox_mfp_la-xerox_mfp-s.lo `test -f 'xerox_mfp-s.c' || echo '$(srcdir)/'`xerox_mfp-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_xerox_mfp_la-xerox_mfp-s.Tpo $(DEPDIR)/libsane_xerox_mfp_la-xerox_mfp-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xerox_mfp-s.c' object='libsane_xerox_mfp_la-xerox_mfp-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_xerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_xerox_mfp_la-xerox_mfp-s.lo `test -f 'xerox_mfp-s.c' || echo '$(srcdir)/'`xerox_mfp-s.c
+
+libsane_la-dll-s.lo: dll-s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsane_la-dll-s.lo -MD -MP -MF $(DEPDIR)/libsane_la-dll-s.Tpo -c -o libsane_la-dll-s.lo `test -f 'dll-s.c' || echo '$(srcdir)/'`dll-s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsane_la-dll-s.Tpo $(DEPDIR)/libsane_la-dll-s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dll-s.c' object='libsane_la-dll-s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsane_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsane_la-dll-s.lo `test -f 'dll-s.c' || echo '$(srcdir)/'`dll-s.c
+
+libsceptre_la-sceptre.lo: sceptre.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsceptre_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsceptre_la-sceptre.lo -MD -MP -MF $(DEPDIR)/libsceptre_la-sceptre.Tpo -c -o libsceptre_la-sceptre.lo `test -f 'sceptre.c' || echo '$(srcdir)/'`sceptre.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsceptre_la-sceptre.Tpo $(DEPDIR)/libsceptre_la-sceptre.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sceptre.c' object='libsceptre_la-sceptre.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsceptre_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsceptre_la-sceptre.lo `test -f 'sceptre.c' || echo '$(srcdir)/'`sceptre.c
+
+libsharp_la-sharp.lo: sharp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsharp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsharp_la-sharp.lo -MD -MP -MF $(DEPDIR)/libsharp_la-sharp.Tpo -c -o libsharp_la-sharp.lo `test -f 'sharp.c' || echo '$(srcdir)/'`sharp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsharp_la-sharp.Tpo $(DEPDIR)/libsharp_la-sharp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sharp.c' object='libsharp_la-sharp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsharp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsharp_la-sharp.lo `test -f 'sharp.c' || echo '$(srcdir)/'`sharp.c
+
+libsm3600_la-sm3600.lo: sm3600.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsm3600_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsm3600_la-sm3600.lo -MD -MP -MF $(DEPDIR)/libsm3600_la-sm3600.Tpo -c -o libsm3600_la-sm3600.lo `test -f 'sm3600.c' || echo '$(srcdir)/'`sm3600.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsm3600_la-sm3600.Tpo $(DEPDIR)/libsm3600_la-sm3600.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sm3600.c' object='libsm3600_la-sm3600.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsm3600_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsm3600_la-sm3600.lo `test -f 'sm3600.c' || echo '$(srcdir)/'`sm3600.c
+
+libsm3840_la-sm3840.lo: sm3840.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsm3840_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsm3840_la-sm3840.lo -MD -MP -MF $(DEPDIR)/libsm3840_la-sm3840.Tpo -c -o libsm3840_la-sm3840.lo `test -f 'sm3840.c' || echo '$(srcdir)/'`sm3840.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsm3840_la-sm3840.Tpo $(DEPDIR)/libsm3840_la-sm3840.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sm3840.c' object='libsm3840_la-sm3840.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsm3840_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsm3840_la-sm3840.lo `test -f 'sm3840.c' || echo '$(srcdir)/'`sm3840.c
+
+libsnapscan_la-snapscan.lo: snapscan.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsnapscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsnapscan_la-snapscan.lo -MD -MP -MF $(DEPDIR)/libsnapscan_la-snapscan.Tpo -c -o libsnapscan_la-snapscan.lo `test -f 'snapscan.c' || echo '$(srcdir)/'`snapscan.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsnapscan_la-snapscan.Tpo $(DEPDIR)/libsnapscan_la-snapscan.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snapscan.c' object='libsnapscan_la-snapscan.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsnapscan_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsnapscan_la-snapscan.lo `test -f 'snapscan.c' || echo '$(srcdir)/'`snapscan.c
+
+libsp15c_la-sp15c.lo: sp15c.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsp15c_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsp15c_la-sp15c.lo -MD -MP -MF $(DEPDIR)/libsp15c_la-sp15c.Tpo -c -o libsp15c_la-sp15c.lo `test -f 'sp15c.c' || echo '$(srcdir)/'`sp15c.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsp15c_la-sp15c.Tpo $(DEPDIR)/libsp15c_la-sp15c.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sp15c.c' object='libsp15c_la-sp15c.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsp15c_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsp15c_la-sp15c.lo `test -f 'sp15c.c' || echo '$(srcdir)/'`sp15c.c
+
+libst400_la-st400.lo: st400.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libst400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libst400_la-st400.lo -MD -MP -MF $(DEPDIR)/libst400_la-st400.Tpo -c -o libst400_la-st400.lo `test -f 'st400.c' || echo '$(srcdir)/'`st400.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libst400_la-st400.Tpo $(DEPDIR)/libst400_la-st400.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='st400.c' object='libst400_la-st400.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libst400_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libst400_la-st400.lo `test -f 'st400.c' || echo '$(srcdir)/'`st400.c
+
+libstv680_la-stv680.lo: stv680.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libstv680_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libstv680_la-stv680.lo -MD -MP -MF $(DEPDIR)/libstv680_la-stv680.Tpo -c -o libstv680_la-stv680.lo `test -f 'stv680.c' || echo '$(srcdir)/'`stv680.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libstv680_la-stv680.Tpo $(DEPDIR)/libstv680_la-stv680.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stv680.c' object='libstv680_la-stv680.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libstv680_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libstv680_la-stv680.lo `test -f 'stv680.c' || echo '$(srcdir)/'`stv680.c
+
+libtamarack_la-tamarack.lo: tamarack.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtamarack_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtamarack_la-tamarack.lo -MD -MP -MF $(DEPDIR)/libtamarack_la-tamarack.Tpo -c -o libtamarack_la-tamarack.lo `test -f 'tamarack.c' || echo '$(srcdir)/'`tamarack.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtamarack_la-tamarack.Tpo $(DEPDIR)/libtamarack_la-tamarack.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tamarack.c' object='libtamarack_la-tamarack.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtamarack_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtamarack_la-tamarack.lo `test -f 'tamarack.c' || echo '$(srcdir)/'`tamarack.c
+
+libteco1_la-teco1.lo: teco1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libteco1_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libteco1_la-teco1.lo -MD -MP -MF $(DEPDIR)/libteco1_la-teco1.Tpo -c -o libteco1_la-teco1.lo `test -f 'teco1.c' || echo '$(srcdir)/'`teco1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libteco1_la-teco1.Tpo $(DEPDIR)/libteco1_la-teco1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='teco1.c' object='libteco1_la-teco1.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libteco1_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libteco1_la-teco1.lo `test -f 'teco1.c' || echo '$(srcdir)/'`teco1.c
+
+libteco2_la-teco2.lo: teco2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libteco2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libteco2_la-teco2.lo -MD -MP -MF $(DEPDIR)/libteco2_la-teco2.Tpo -c -o libteco2_la-teco2.lo `test -f 'teco2.c' || echo '$(srcdir)/'`teco2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libteco2_la-teco2.Tpo $(DEPDIR)/libteco2_la-teco2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='teco2.c' object='libteco2_la-teco2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libteco2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libteco2_la-teco2.lo `test -f 'teco2.c' || echo '$(srcdir)/'`teco2.c
+
+libteco3_la-teco3.lo: teco3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libteco3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libteco3_la-teco3.lo -MD -MP -MF $(DEPDIR)/libteco3_la-teco3.Tpo -c -o libteco3_la-teco3.lo `test -f 'teco3.c' || echo '$(srcdir)/'`teco3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libteco3_la-teco3.Tpo $(DEPDIR)/libteco3_la-teco3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='teco3.c' object='libteco3_la-teco3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libteco3_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libteco3_la-teco3.lo `test -f 'teco3.c' || echo '$(srcdir)/'`teco3.c
+
+libtest_la-test.lo: test.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_la-test.lo -MD -MP -MF $(DEPDIR)/libtest_la-test.Tpo -c -o libtest_la-test.lo `test -f 'test.c' || echo '$(srcdir)/'`test.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtest_la-test.Tpo $(DEPDIR)/libtest_la-test.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test.c' object='libtest_la-test.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_la-test.lo `test -f 'test.c' || echo '$(srcdir)/'`test.c
+
+libu12_la-u12.lo: u12.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libu12_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libu12_la-u12.lo -MD -MP -MF $(DEPDIR)/libu12_la-u12.Tpo -c -o libu12_la-u12.lo `test -f 'u12.c' || echo '$(srcdir)/'`u12.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libu12_la-u12.Tpo $(DEPDIR)/libu12_la-u12.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='u12.c' object='libu12_la-u12.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libu12_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libu12_la-u12.lo `test -f 'u12.c' || echo '$(srcdir)/'`u12.c
+
+libumax_la-umax.lo: umax.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libumax_la-umax.lo -MD -MP -MF $(DEPDIR)/libumax_la-umax.Tpo -c -o libumax_la-umax.lo `test -f 'umax.c' || echo '$(srcdir)/'`umax.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libumax_la-umax.Tpo $(DEPDIR)/libumax_la-umax.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax.c' object='libumax_la-umax.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libumax_la-umax.lo `test -f 'umax.c' || echo '$(srcdir)/'`umax.c
+
+libumax1220u_la-umax1220u.lo: umax1220u.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax1220u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libumax1220u_la-umax1220u.lo -MD -MP -MF $(DEPDIR)/libumax1220u_la-umax1220u.Tpo -c -o libumax1220u_la-umax1220u.lo `test -f 'umax1220u.c' || echo '$(srcdir)/'`umax1220u.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libumax1220u_la-umax1220u.Tpo $(DEPDIR)/libumax1220u_la-umax1220u.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax1220u.c' object='libumax1220u_la-umax1220u.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax1220u_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libumax1220u_la-umax1220u.lo `test -f 'umax1220u.c' || echo '$(srcdir)/'`umax1220u.c
+
+libumax_pp_la-umax_pp.lo: umax_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libumax_pp_la-umax_pp.lo -MD -MP -MF $(DEPDIR)/libumax_pp_la-umax_pp.Tpo -c -o libumax_pp_la-umax_pp.lo `test -f 'umax_pp.c' || echo '$(srcdir)/'`umax_pp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libumax_pp_la-umax_pp.Tpo $(DEPDIR)/libumax_pp_la-umax_pp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax_pp.c' object='libumax_pp_la-umax_pp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libumax_pp_la-umax_pp.lo `test -f 'umax_pp.c' || echo '$(srcdir)/'`umax_pp.c
+
+libumax_pp_la-umax_pp_low.lo: umax_pp_low.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libumax_pp_la-umax_pp_low.lo -MD -MP -MF $(DEPDIR)/libumax_pp_la-umax_pp_low.Tpo -c -o libumax_pp_la-umax_pp_low.lo `test -f 'umax_pp_low.c' || echo '$(srcdir)/'`umax_pp_low.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libumax_pp_la-umax_pp_low.Tpo $(DEPDIR)/libumax_pp_la-umax_pp_low.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax_pp_low.c' object='libumax_pp_la-umax_pp_low.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libumax_pp_la-umax_pp_low.lo `test -f 'umax_pp_low.c' || echo '$(srcdir)/'`umax_pp_low.c
+
+libumax_pp_la-umax_pp_mid.lo: umax_pp_mid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libumax_pp_la-umax_pp_mid.lo -MD -MP -MF $(DEPDIR)/libumax_pp_la-umax_pp_mid.Tpo -c -o libumax_pp_la-umax_pp_mid.lo `test -f 'umax_pp_mid.c' || echo '$(srcdir)/'`umax_pp_mid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libumax_pp_la-umax_pp_mid.Tpo $(DEPDIR)/libumax_pp_la-umax_pp_mid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='umax_pp_mid.c' object='libumax_pp_la-umax_pp_mid.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libumax_pp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libumax_pp_la-umax_pp_mid.lo `test -f 'umax_pp_mid.c' || echo '$(srcdir)/'`umax_pp_mid.c
+
+libv4l_la-v4l.lo: v4l.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libv4l_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libv4l_la-v4l.lo -MD -MP -MF $(DEPDIR)/libv4l_la-v4l.Tpo -c -o libv4l_la-v4l.lo `test -f 'v4l.c' || echo '$(srcdir)/'`v4l.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libv4l_la-v4l.Tpo $(DEPDIR)/libv4l_la-v4l.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='v4l.c' object='libv4l_la-v4l.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libv4l_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libv4l_la-v4l.lo `test -f 'v4l.c' || echo '$(srcdir)/'`v4l.c
+
+libxerox_mfp_la-xerox_mfp.lo: xerox_mfp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libxerox_mfp_la-xerox_mfp.lo -MD -MP -MF $(DEPDIR)/libxerox_mfp_la-xerox_mfp.Tpo -c -o libxerox_mfp_la-xerox_mfp.lo `test -f 'xerox_mfp.c' || echo '$(srcdir)/'`xerox_mfp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libxerox_mfp_la-xerox_mfp.Tpo $(DEPDIR)/libxerox_mfp_la-xerox_mfp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xerox_mfp.c' object='libxerox_mfp_la-xerox_mfp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libxerox_mfp_la-xerox_mfp.lo `test -f 'xerox_mfp.c' || echo '$(srcdir)/'`xerox_mfp.c
+
+libxerox_mfp_la-xerox_mfp-usb.lo: xerox_mfp-usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libxerox_mfp_la-xerox_mfp-usb.lo -MD -MP -MF $(DEPDIR)/libxerox_mfp_la-xerox_mfp-usb.Tpo -c -o libxerox_mfp_la-xerox_mfp-usb.lo `test -f 'xerox_mfp-usb.c' || echo '$(srcdir)/'`xerox_mfp-usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libxerox_mfp_la-xerox_mfp-usb.Tpo $(DEPDIR)/libxerox_mfp_la-xerox_mfp-usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xerox_mfp-usb.c' object='libxerox_mfp_la-xerox_mfp-usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libxerox_mfp_la-xerox_mfp-usb.lo `test -f 'xerox_mfp-usb.c' || echo '$(srcdir)/'`xerox_mfp-usb.c
+
+libxerox_mfp_la-xerox_mfp-tcp.lo: xerox_mfp-tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libxerox_mfp_la-xerox_mfp-tcp.lo -MD -MP -MF $(DEPDIR)/libxerox_mfp_la-xerox_mfp-tcp.Tpo -c -o libxerox_mfp_la-xerox_mfp-tcp.lo `test -f 'xerox_mfp-tcp.c' || echo '$(srcdir)/'`xerox_mfp-tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libxerox_mfp_la-xerox_mfp-tcp.Tpo $(DEPDIR)/libxerox_mfp_la-xerox_mfp-tcp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xerox_mfp-tcp.c' object='libxerox_mfp_la-xerox_mfp-tcp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxerox_mfp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libxerox_mfp_la-xerox_mfp-tcp.lo `test -f 'xerox_mfp-tcp.c' || echo '$(srcdir)/'`xerox_mfp-tcp.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(sanelibdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+ clean-sanelibLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-sanelibLTLIBRARIES
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES uninstall-sanelibLTLIBRARIES
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: all check install install-am install-data-am install-strip \
+ uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-local \
+ clean-sanelibLTLIBRARIES cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-data-hook install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-sanelibLTLIBRARIES install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-hook \
+ uninstall-libLTLIBRARIES uninstall-sanelibLTLIBRARIES
+
+
+all: becfg
+
+# FIXME: % is a GNU extension... This is only thing left requiring
+# use to use GNU make.
+%-s.c: $(srcdir)/stubs.c
+ rm -f $@
+ $(LN_S) $(srcdir)/stubs.c $@
+
+dll-preload.h:
+ rm -f $@
+ list="$(PRELOADABLE_BACKENDS)"; for be in $$list; do \
+ echo "PRELOAD_DECL($$be)" >> $@; \
+ done
+ echo "static struct backend preloaded_backends[] = {" >> $@
+ sep=""; \
+ list="$(PRELOADABLE_BACKENDS)"; \
+ if test -z "$${list}"; then \
+ echo { 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} >> $@; \
+ else \
+ for be in $$list; do \
+ echo "$${sep}PRELOAD_DEFN($$be)" >> $@; \
+ sep=","; \
+ done; \
+ fi
+ echo "};" >> $@
+becfg: $(BACKEND_CONFS)
+.conf.in.conf:
+ @echo Generating $@ from $^
+ @sed -e 's|@DATADIR@|$(datadir)|g' \
+ -e 's|@CONFIGDIR@|$(configdir)|g' \
+ -e 's|@DOCDIR@|$(docdir)|g' \
+ -e 's|@LIBDIR@|$(libdir)/sane|g' \
+ -e 's|@BINDIR@|$(bindir)|g' \
+ -e 's|@SBINDIR@|$(sbindir)|g' \
+ -e 's|@PACKAGEVERSION@|$(PACKAGE_VERSION)|g' $? > $@
+
+install-data-hook: install-becfg install-firmware-path $(INSTALL_LOCKPATH)
+
+# Custom install target to install config files. Do not overwrite
+# files that have been previously installed so that user modifications
+# are not lost.
+install-becfg:
+ @# Libtool has a bug where it will sometimes symlink the last
+ @# installed library in $(sanelibdir) to $(sanelibdir)/libsane.*.
+ @# Having two libsane's can cause issues so get rid of it.
+ -rm -f $(DESTDIR)$(sanelibdir)/libsane.*
+ test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)"
+ test -z "$(configdir)/dll.d" || $(MKDIR_P) "$(DESTDIR)$(configdir)/dll.d"
+ @list="$(BACKEND_CONFS_ENABLED) saned.conf dll.conf"; for cfg in $$list; do \
+ if test ! -r $${cfg}; then continue; fi; \
+ if test -f $(DESTDIR)$(configdir)/$${cfg}; then \
+ echo NOT overwriting $${cfg} in $(configdir)...; \
+ else \
+ echo installing $${cfg} in $(configdir)/$${cfg}...; \
+ $(INSTALL_DATA) $${cfg} $(DESTDIR)$(configdir)/$${cfg} \
+ || exit 1; \
+ fi; \
+ done
+
+install-firmware-path:
+ for dir in $(FIRMWARE_DIRS) ; do \
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/sane/$${dir} ; \
+ done
+
+install-lockpath:
+ $(mkinstalldirs) -m 775 $(DESTDIR)$(locksanedir)
+
+uninstall-hook:
+ rm -rf $(DESTDIR)$(libdir)/sane $(DESTDIR)$(configdir) $(DESTDIR)$(locksanedir)
+ rm -f $(DESTDIR)$(libdir)/libsane.*
+ -for dir in $(FIRMWARE_DIRS) ; do \
+ rmdir $(DESTDIR)$(datadir)/sane/$${dir} ; \
+ done
+clean-local:
+ find . -type l -name \*-s.c | xargs rm -f
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/backend/abaton.c b/backend/abaton.c
new file mode 100644
index 0000000..10f5c21
--- /dev/null
+++ b/backend/abaton.c
@@ -0,0 +1,1491 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998 David Huggins-Daines, heavily based on the Apple
+ scanner driver (since Abaton scanners are very similar to old Apple
+ scanners), which is (C) 1998 Milon Firikis, which is, in turn, based
+ on the Mustek driver, (C) 1996-7 David Mosberger-Tang.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Abaton flatbed scanners. */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+#define BACKEND_NAME abaton
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define ABATON_CONFIG_FILE "abaton.conf"
+
+#include "abaton.h"
+
+
+
+static int num_devices;
+static Abaton_Device *first_dev;
+static Abaton_Scanner *first_handle;
+
+static SANE_String_Const mode_list[5];
+
+static const SANE_String_Const halftone_pattern_list[] =
+{
+ "spiral", "bayer",
+ 0
+};
+
+static const SANE_Range dpi_range =
+{
+ /* min, max, quant */
+ 72,
+ 300,
+ 1
+};
+
+static const SANE_Range enhance_range =
+{
+ 1,
+ 255,
+ 1
+};
+
+static const SANE_Range x_range =
+{
+ 0,
+ 8.5 * MM_PER_INCH,
+ 1
+};
+
+static const SANE_Range y_range =
+{
+ 0,
+ 14.0 * MM_PER_INCH,
+ 1
+};
+
+#define ERROR_MESSAGE 1
+#define USER_MESSAGE 5
+#define FLOW_CONTROL 50
+#define VARIABLE_CONTROL 70
+#define DEBUG_SPECIAL 100
+#define IO_MESSAGE 110
+#define INNER_LOOP 120
+
+
+/* SCSI commands that the Abaton scanners understand: */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define START_STOP 0x1b
+#define SET_WINDOW 0x24
+#define READ_10 0x28
+#define WRITE_10 0x2b /* not used, AFAIK */
+#define GET_DATA_STATUS 0x34
+
+
+#define INQ_LEN 0x60
+static const uint8_t inquiry[] =
+{
+ INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00
+};
+
+static const uint8_t test_unit_ready[] =
+{
+ TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* convenience macros */
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+/* store an 8-bit-wide value at the location specified by ptr */
+#define STORE8(ptr, val) (*((uint8_t *) ptr) = val)
+
+/* store a 16-bit-wide value in network (big-endian) byte order */
+#define STORE16(ptr, val) \
+ { \
+ *((uint8_t *) ptr) = (val >> 8) & 0xff; \
+ *((uint8_t *) ptr+1) = val & 0xff; \
+ }
+
+/* store a 24-bit-wide value in network (big-endian) byte order */
+#define STORE24(ptr, val) \
+ { \
+ *((uint8_t *) ptr) = (val >> 16) & 0xff; \
+ *((uint8_t *) ptr+1) = (val >> 8) & 0xff; \
+ *((uint8_t *) ptr+2) = val & 0xff; \
+ }
+
+/* store a 32-bit-wide value in network (big-endian) byte order */
+#define STORE32(ptr, val) \
+ { \
+ *((uint8_t *) ptr) = (val >> 24) & 0xff; \
+ *((uint8_t *) ptr+1) = (val >> 16) & 0xff; \
+ *((uint8_t *) ptr+2) = (val >> 8) & 0xff; \
+ *((uint8_t *) ptr+3) = val & 0xff; \
+ }
+
+/* retrieve a 24-bit-wide big-endian value at ptr */
+#define GET24(ptr) \
+ (*((uint8_t *) ptr) << 16) + \
+ (*((uint8_t *) ptr+1) << 8) + \
+ (*((uint8_t *) ptr+2))
+
+static SANE_Status
+wait_ready (int fd)
+{
+#define MAX_WAITING_TIME 60 /* one minute, at most */
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG (USER_MESSAGE, "wait_ready: sending TEST_UNIT_READY\n");
+
+ status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0);
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (ERROR_MESSAGE, "wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (ERROR_MESSAGE, "wait_ready: timed out after %ld seconds\n",
+ (long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ scsi_fd = scsi_fd; /* silence gcc */
+ arg = arg; /* silence gcc */
+
+ switch (result[2] & 0x0F)
+ {
+ case 0:
+ DBG (USER_MESSAGE, "Sense: No sense Error\n");
+ return SANE_STATUS_GOOD;
+ case 2:
+ DBG (ERROR_MESSAGE, "Sense: Scanner not ready\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ case 4:
+ DBG (ERROR_MESSAGE, "Sense: Hardware Error. Read more...\n");
+ return SANE_STATUS_IO_ERROR;
+ case 5:
+ DBG (ERROR_MESSAGE, "Sense: Illegal request\n");
+ return SANE_STATUS_UNSUPPORTED;
+ case 6:
+ DBG (ERROR_MESSAGE, "Sense: Unit Attention (Wait until scanner "
+ "boots)\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ case 9:
+ DBG (ERROR_MESSAGE, "Sense: Vendor Unique. Read more...\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG (ERROR_MESSAGE, "Sense: Unknown Sense Key. Read more...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+request_sense (Abaton_Scanner * s)
+{
+ uint8_t cmd[6];
+ uint8_t result[22];
+ size_t size = sizeof (result);
+ SANE_Status status;
+
+ memset (cmd, 0, sizeof (cmd));
+ memset (result, 0, sizeof (result));
+
+ cmd[0] = REQUEST_SENSE;
+ STORE8 (cmd + 4, sizeof (result));
+ sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), result, &size);
+
+ if (result[7] != 14)
+ {
+ DBG (ERROR_MESSAGE, "Additional Length %u\n", (unsigned int) result[7]);
+ status = SANE_STATUS_IO_ERROR;
+ }
+
+
+ status = sense_handler (s->fd, result, NULL);
+ if (status == SANE_STATUS_IO_ERROR)
+ {
+
+ /* Since I haven't figured out the vendor unique error codes on
+ this thing, I'll just handle the normal ones for now */
+
+ if (result[18] & 0x80)
+ DBG (ERROR_MESSAGE, "Sense: Dim Light (output of lamp below 70%%).\n");
+
+ if (result[18] & 0x40)
+ DBG (ERROR_MESSAGE, "Sense: No Light at all.\n");
+
+ if (result[18] & 0x20)
+ DBG (ERROR_MESSAGE, "Sense: No Home.\n");
+
+ if (result[18] & 0x10)
+ DBG (ERROR_MESSAGE, "Sense: No Limit. Tried to scan out of range.\n");
+ }
+
+ DBG (USER_MESSAGE, "Sense: Optical gain %u.\n", (unsigned int) result[20]);
+ return status;
+}
+
+static SANE_Status
+set_window (Abaton_Scanner * s)
+{
+ uint8_t cmd[10 + 40];
+ uint8_t *window = cmd + 10 + 8;
+ int invert;
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = SET_WINDOW;
+ cmd[8] = 40;
+
+ /* Just like the Apple scanners, we put the resolution here */
+ STORE16 (window + 2, s->val[OPT_X_RESOLUTION].w);
+ STORE16 (window + 4, s->val[OPT_Y_RESOLUTION].w);
+
+ /* Unlike Apple scanners, these are pixel values */
+ STORE16 (window + 6, s->ULx);
+ STORE16 (window + 8, s->ULy);
+ STORE16 (window + 10, s->Width);
+ STORE16 (window + 12, s->Height);
+
+ STORE8 (window + 14, s->val[OPT_BRIGHTNESS].w);
+ STORE8 (window + 15, s->val[OPT_THRESHOLD].w);
+ STORE8 (window + 16, s->val[OPT_CONTRAST].w);
+
+ invert = s->val[OPT_NEGATIVE].w;
+
+ if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ STORE8 (window + 17, 0);
+ }
+ else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ STORE8 (window + 17, 1);
+ }
+ else if (!strcmp (s->val[OPT_MODE].s, "Gray256")
+ || !strcmp (s->val[OPT_MODE].s, "Gray16"))
+ {
+ STORE8 (window + 17, 2);
+ invert = !s->val[OPT_NEGATIVE].w;
+ }
+ else
+ {
+ DBG (ERROR_MESSAGE, "Can't match mode %s\n", s->val[OPT_MODE].s);
+ return SANE_STATUS_INVAL;
+ }
+
+ STORE8 (window + 18, s->bpp);
+
+ if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "spiral"))
+ {
+ STORE8 (window + 20, 0);
+ }
+ else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "bayer"))
+ {
+ STORE8 (window + 20, 1);
+ }
+ else
+ {
+ DBG (ERROR_MESSAGE, "Can't match haftone pattern %s\n",
+ s->val[OPT_HALFTONE_PATTERN].s);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* We have to invert these ones for some reason, so why not
+ let the scanner do it for us... */
+ STORE8 (window + 21, invert ? 0x80 : 0);
+
+ STORE16 (window + 22, (s->val[OPT_MIRROR].w != 0));
+
+ return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
+}
+
+static SANE_Status
+start_scan (Abaton_Scanner * s)
+{
+ SANE_Status status;
+ uint8_t start[7];
+
+
+ memset (start, 0, sizeof (start));
+ start[0] = START_STOP;
+ start[4] = 1;
+
+ status = sanei_scsi_cmd (s->fd, start, sizeof (start), 0, 0);
+ return status;
+}
+
+static SANE_Status
+attach (const char *devname, Abaton_Device ** devp, int may_wait)
+{
+ char result[INQ_LEN];
+ const char *model_name = result + 44;
+ int fd, abaton_scanner;
+ Abaton_Device *dev;
+ SANE_Status status;
+ size_t size;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (USER_MESSAGE, "attach: opening %s\n", devname);
+ status = sanei_scsi_open (devname, &fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "attach: open failed (%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (may_wait)
+ wait_ready (fd);
+
+ DBG (USER_MESSAGE, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "attach: inquiry failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ status = wait_ready (fd);
+ sanei_scsi_close (fd);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* check that we've got an Abaton */
+ abaton_scanner = (strncmp (result + 8, "ABATON ", 8) == 0);
+ model_name = result + 16;
+
+ /* make sure it's a scanner ;-) */
+ abaton_scanner = abaton_scanner && (result[0] == 0x06);
+
+ if (!abaton_scanner)
+ {
+ DBG (ERROR_MESSAGE, "attach: device doesn't look like an Abaton scanner "
+ "(result[0]=%#02x)\n", result[0]);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor = "Abaton";
+ dev->sane.model = strndup (model_name, 16);
+ dev->sane.type = "flatbed scanner";
+
+ if (!strncmp (model_name, "SCAN 300/GS", 11))
+ {
+ dev->ScannerModel = ABATON_300GS;
+ }
+ else if (!strncmp (model_name, "SCAN 300/S", 10))
+ {
+ dev->ScannerModel = ABATON_300S;
+ }
+
+ DBG (USER_MESSAGE, "attach: found Abaton scanner model %s (%s)\n",
+ dev->sane.model, dev->sane.type);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *devname)
+{
+ return attach (devname, 0, SANE_FALSE);
+}
+
+static SANE_Status
+calc_parameters (Abaton_Scanner * s)
+{
+ SANE_String val = s->val[OPT_MODE].s;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int dpix = s->val[OPT_X_RESOLUTION].w;
+ SANE_Int dpiy = s->val[OPT_Y_RESOLUTION].w;
+ double ulx, uly, width, height;
+
+ DBG (FLOW_CONTROL, "Entering calc_parameters\n");
+
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) || !strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ s->params.depth = 1;
+ s->bpp = 1;
+ }
+ else if (!strcmp (val, "Gray16"))
+ {
+ s->params.depth = 8;
+ s->bpp = 4;
+ }
+ else if (!strcmp (val, "Gray256"))
+ {
+ s->params.depth = 8;
+ s->bpp = 8;
+ }
+ else
+ {
+ DBG (ERROR_MESSAGE, "calc_parameters: Invalid mode %s\n", (char *) val);
+ status = SANE_STATUS_INVAL;
+ }
+
+ /* in inches */
+ ulx = (double) s->val[OPT_TL_X].w / MM_PER_INCH;
+ uly = (double) s->val[OPT_TL_Y].w / MM_PER_INCH;
+ width = (double) s->val[OPT_BR_X].w / MM_PER_INCH - ulx;
+ height = (double) s->val[OPT_BR_Y].w / MM_PER_INCH - uly;
+
+ DBG (VARIABLE_CONTROL, "(inches) ulx: %f, uly: %f, width: %f, height: %f\n",
+ ulx, uly, width, height);
+
+ /* turn 'em into pixel quantities */
+ s->ULx = ulx * dpix;
+ s->ULy = uly * dpiy;
+ s->Width = width * dpix;
+ s->Height = height * dpiy;
+
+ DBG (VARIABLE_CONTROL, "(pixels) ulx: %d, uly: %d, width: %d, height: %d\n",
+ s->ULx, s->ULy, s->Width, s->Height);
+
+ /* floor width to a byte multiple */
+ if ((s->Width * s->bpp) % 8)
+ {
+ s->Width /= 8;
+ s->Width *= 8;
+ DBG (VARIABLE_CONTROL, "Adapting to width %d\n", s->Width);
+ }
+
+ s->params.pixels_per_line = s->Width;
+ s->params.lines = s->Height;
+ s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8;
+
+
+ DBG (VARIABLE_CONTROL, "format=%d\n", s->params.format);
+ DBG (VARIABLE_CONTROL, "last_frame=%d\n", s->params.last_frame);
+ DBG (VARIABLE_CONTROL, "lines=%d\n", s->params.lines);
+ DBG (VARIABLE_CONTROL, "depth=%d (%d)\n", s->params.depth, s->bpp);
+ DBG (VARIABLE_CONTROL, "pixels_per_line=%d\n", s->params.pixels_per_line);
+ DBG (VARIABLE_CONTROL, "bytes_per_line=%d\n", s->params.bytes_per_line);
+ DBG (VARIABLE_CONTROL, "Pixels %dx%dx%d\n",
+ s->params.pixels_per_line, s->params.lines, 1 << s->params.depth);
+
+ DBG (FLOW_CONTROL, "Leaving calc_parameters\n");
+ return status;
+}
+
+static SANE_Status
+mode_update (SANE_Handle handle, char *val)
+{
+ Abaton_Scanner *s = handle;
+
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ DISABLE (OPT_BRIGHTNESS);
+ DISABLE (OPT_CONTRAST);
+ ENABLE (OPT_THRESHOLD);
+ DISABLE (OPT_HALFTONE_PATTERN);
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ ENABLE (OPT_BRIGHTNESS);
+ ENABLE (OPT_CONTRAST);
+ DISABLE (OPT_THRESHOLD);
+ ENABLE (OPT_HALFTONE_PATTERN);
+ }
+ else if (!strcmp (val, "Gray16") || !strcmp (val, "Gray256"))
+ {
+ ENABLE (OPT_BRIGHTNESS);
+ ENABLE (OPT_CONTRAST);
+ DISABLE (OPT_THRESHOLD);
+ DISABLE (OPT_HALFTONE_PATTERN);
+ } /* End of Gray */
+ else
+ {
+ DBG (ERROR_MESSAGE, "Invalid mode %s\n", (char *) val);
+ return SANE_STATUS_INVAL;
+ }
+
+ calc_parameters (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* find the longest of a list of strings */
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+static SANE_Status
+init_options (Abaton_Scanner * s)
+{
+ int i;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART;
+
+ switch (s->hw->ScannerModel)
+ {
+ case ABATON_300GS:
+ mode_list[1]=SANE_VALUE_SCAN_MODE_HALFTONE;
+ mode_list[2]="Gray16";
+ mode_list[3]="Gray256";
+ mode_list[4]=NULL;
+ break;
+ case ABATON_300S:
+ default:
+ mode_list[1]=NULL;
+ break;
+ }
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[0]);
+
+ /* resolution - horizontal */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &dpi_range;
+ s->val[OPT_X_RESOLUTION].w = 150;
+
+ /* resolution - vertical */
+ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &dpi_range;
+ s->val[OPT_Y_RESOLUTION].w = 150;
+
+ /* constrain resolutions */
+ s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ s->opt[OPT_RESOLUTION_BIND].unit = SANE_UNIT_NONE;
+ s->opt[OPT_RESOLUTION_BIND].constraint_type = SANE_CONSTRAINT_NONE;
+ /* until I fix it */
+ s->val[OPT_RESOLUTION_BIND].w = SANE_FALSE;
+
+ /* preview mode */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* halftone pattern */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list);
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list;
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]);
+
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y].w = y_range.max;
+
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &enhance_range;
+ s->val[OPT_BRIGHTNESS].w = 150;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &enhance_range;
+ s->val[OPT_CONTRAST].w = 150;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &enhance_range;
+ s->val[OPT_THRESHOLD].w = 150;
+
+ /* negative */
+ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEGATIVE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ /* mirror-image */
+ s->opt[OPT_MIRROR].name = "mirror";
+ s->opt[OPT_MIRROR].title = "Mirror Image";
+ s->opt[OPT_MIRROR].desc = "Scan in mirror-image";
+ s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
+ s->opt[OPT_MIRROR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_MIRROR].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_MIRROR].w = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* silence gcc */
+
+ DBG_INIT ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (ABATON_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0, SANE_FALSE);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ if (strncmp (dev_name, "option", 6) == 0
+ && isspace (dev_name[6]))
+ {
+ const char *str = dev_name + 7;
+
+ while (isspace (*str))
+ ++str;
+
+ continue;
+ }
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Abaton_Device *dev, *next;
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Abaton_Device *dev;
+ int i;
+
+ local_only = local_only; /* silence gcc */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Abaton_Device *dev;
+ SANE_Status status;
+ Abaton_Scanner *s;
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach (devicename, &dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ /* empty devicname -> use first device */
+ dev = first_dev;
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->fd = -1;
+ s->hw = dev;
+
+ init_options (s);
+
+ /* set up some universal parameters */
+ s->params.last_frame = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Abaton_Scanner *prev, *s;
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == (Abaton_Scanner *) handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (ERROR_MESSAGE, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Abaton_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Abaton_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ if (info != NULL)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_RESOLUTION_BIND:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_NEGATIVE:
+ case OPT_MIRROR:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+
+ case OPT_MODE:
+ case OPT_HALFTONE_PATTERN:
+ status = sanei_constrain_value (s->opt + option, s->val[option].s,
+ info);
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ switch (option)
+ {
+ /* resolution should be uniform for previews, or when the
+ user says so. */
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ if (*(SANE_Word *) val) {
+ s->val[OPT_Y_RESOLUTION].w = s->val[OPT_X_RESOLUTION].w;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ /* always recalculate! */
+ calc_parameters (s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION_BIND:
+ s->val[option].w = *(SANE_Word *) val;
+ if (*(SANE_Word *) val) {
+ s->val[OPT_Y_RESOLUTION].w = s->val[OPT_X_RESOLUTION].w;
+ calc_parameters (s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS |
+ SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_X_RESOLUTION:
+ if (s->val[OPT_PREVIEW].w || s->val[OPT_RESOLUTION_BIND].w) {
+ s->val[OPT_Y_RESOLUTION].w = *(SANE_Word *)val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ s->val[option].w = *(SANE_Word *) val;
+ calc_parameters (s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_Y_RESOLUTION:
+ if (s->val[OPT_PREVIEW].w || s->val[OPT_RESOLUTION_BIND].w) {
+ s->val[OPT_X_RESOLUTION].w = *(SANE_Word *)val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ s->val[option].w = *(SANE_Word *) val;
+ calc_parameters (s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ /* these ones don't have crazy side effects */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ calc_parameters (s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ /* this one is somewhat imprecise */
+ case OPT_BR_X:
+ s->val[option].w = *(SANE_Word *) val;
+ calc_parameters (s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS
+ | SANE_INFO_INEXACT;
+ return SANE_STATUS_GOOD;
+
+ /* no side-effects whatsoever */
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_NEGATIVE:
+ case OPT_MIRROR:
+
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_HALFTONE_PATTERN:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ status = mode_update (s, val);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ } /* End of switch */
+ } /* End of SET_VALUE */
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Abaton_Scanner *s = handle;
+
+ DBG (FLOW_CONTROL, "Entering sane_get_parameters\n");
+ calc_parameters (s);
+
+
+ if (params)
+ *params = s->params;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Abaton_Scanner *s = handle;
+ SANE_Status status;
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+
+ calc_parameters (s);
+
+ if (s->fd < 0)
+ {
+ /* this is the first (and maybe only) pass... */
+
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "open: wait_ready() failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = request_sense (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "sane_start: request_sense revealed error: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "open: set scan area command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ s->scanning = SANE_TRUE;
+ s->AbortedByUser = SANE_FALSE;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ return SANE_STATUS_GOOD;
+
+stop_scanner_and_return:
+ s->scanning = SANE_FALSE;
+ s->AbortedByUser = SANE_FALSE;
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Abaton_Scanner *s = handle;
+ SANE_Status status;
+
+ uint8_t get_data_status[10];
+ uint8_t read[10];
+
+ uint8_t result[12];
+ size_t size;
+ SANE_Int data_av = 0;
+ SANE_Int data_length = 0;
+ SANE_Int offset = 0;
+ SANE_Int rread = 0;
+ SANE_Bool Pseudo8bit = SANE_FALSE;
+
+
+ *len = 0;
+
+ /* don't let bogus read requests reach the scanner */
+ /* this is a sub-optimal way of doing this, I'm sure */
+ if (!s->scanning)
+ return SANE_STATUS_EOF;
+
+ if (!strcmp (s->val[OPT_MODE].s, "Gray16"))
+ Pseudo8bit = SANE_TRUE;
+
+ memset (get_data_status, 0, sizeof (get_data_status));
+ get_data_status[0] = GET_DATA_STATUS;
+ /* This means "block" for Apple scanners, it seems to be the same
+ for Abaton. The scanner will do non-blocking I/O, but I don't
+ want to go there right now. */
+ get_data_status[1] = 1;
+ STORE8 (get_data_status + 8, sizeof (result));
+
+ memset (read, 0, sizeof (read));
+ read[0] = READ_10;
+
+ do
+ {
+ size = sizeof (result);
+ /* this isn't necessary */
+ /* memset (result, 0, size); */
+ status = sanei_scsi_cmd (s->fd, get_data_status,
+ sizeof (get_data_status), result, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (!size)
+ {
+ DBG (ERROR_MESSAGE, "sane_read: cannot get_data_status.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* this is not an accurate name, but oh well... */
+ data_length = GET24 (result);
+ data_av = GET24 (result + 9);
+
+ /* don't check result[3] here, because that screws things up
+ somewhat */
+ if (data_length) {
+ DBG (IO_MESSAGE,
+ "sane_read: (status) Available in scanner buffer %u.\n",
+ data_av);
+
+ if (Pseudo8bit)
+ {
+ if ((data_av * 2) + offset > max_len)
+ rread = (max_len - offset) / 2;
+ else
+ rread = data_av;
+ }
+ else if (data_av + offset > max_len)
+ {
+ rread = max_len - offset;
+ }
+ else
+ {
+ rread = data_av;
+ }
+
+ DBG (IO_MESSAGE,
+ "sane_read: (action) Actual read request for %u bytes.\n",
+ rread);
+
+ size = rread;
+
+ STORE24 (read + 6, rread);
+
+ status = sanei_scsi_cmd (s->fd, read, sizeof (read),
+ buf + offset, &size);
+
+ if (Pseudo8bit)
+ {
+ SANE_Int byte;
+ SANE_Int pos = offset + (rread << 1) - 1;
+ SANE_Byte B;
+ for (byte = offset + rread - 1; byte >= offset; byte--)
+ {
+ B = buf[byte];
+ /* don't invert these! */
+ buf[pos--] = B << 4; /* low (right) nibble */
+ buf[pos--] = B & 0xF0; /* high (left) nibble */
+ }
+ /* putting an end to bitop abuse here */
+ offset += size * 2;
+ }
+ else
+ offset += size;
+
+ DBG (IO_MESSAGE, "sane_read: Buffer %u of %u full %g%%\n",
+ offset, max_len, (double) (offset * 100. / max_len));
+ }
+ }
+ while (offset < max_len && data_length != 0 && !s->AbortedByUser);
+
+ if (s->AbortedByUser)
+ {
+ s->scanning = SANE_FALSE;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "sane_read: request_sense revealed error: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_scsi_cmd (s->fd, test_unit_ready,
+ sizeof (test_unit_ready), 0, 0);
+ if (status != SANE_STATUS_GOOD || status != SANE_STATUS_INVAL)
+ return status;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (!data_length)
+ {
+ s->scanning = SANE_FALSE;
+ DBG (IO_MESSAGE, "sane_read: (status) No more data...");
+ if (!offset)
+ {
+ /* this shouldn't happen */
+ *len = 0;
+ DBG (IO_MESSAGE, "EOF\n");
+ return SANE_STATUS_EOF;
+ }
+ else
+ {
+ *len = offset;
+ DBG (IO_MESSAGE, "GOOD\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+
+ DBG (FLOW_CONTROL,
+ "sane_read: Normal Exiting, Aborted=%u, data_length=%u\n",
+ s->AbortedByUser, data_av);
+ *len = offset;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Abaton_Scanner *s = handle;
+
+ if (s->scanning)
+ {
+ if (s->AbortedByUser)
+ {
+ DBG (FLOW_CONTROL,
+ "sane_cancel: Already Aborted. Please Wait...\n");
+ }
+ else
+ {
+ s->scanning = SANE_FALSE;
+ s->AbortedByUser = SANE_TRUE;
+ DBG (FLOW_CONTROL, "sane_cancel: Signal Caught! Aborting...\n");
+ }
+ }
+ else
+ {
+ if (s->AbortedByUser)
+ {
+ DBG (FLOW_CONTROL, "sane_cancel: Scan has not been initiated yet."
+ "we probably recieved a signal while writing data.\n");
+ s->AbortedByUser = SANE_FALSE;
+ }
+ else
+ {
+ DBG (FLOW_CONTROL, "sane_cancel: Scan has not been initiated "
+ "yet (or it's over).\n");
+ }
+ }
+
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ handle = handle; /* silence gcc */
+ non_blocking = non_blocking; /* silence gcc */
+
+ DBG (FLOW_CONTROL, "sane_set_io_mode: Don't call me please. "
+ "Unimplemented function\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle; /* silence gcc */
+ fd = fd; /* silence gcc */
+
+ DBG (FLOW_CONTROL, "sane_get_select_fd: Don't call me please. "
+ "Unimplemented function\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/abaton.conf.in b/backend/abaton.conf.in
new file mode 100644
index 0000000..dcadbaf
--- /dev/null
+++ b/backend/abaton.conf.in
@@ -0,0 +1,2 @@
+scsi ABATON
+/dev/scanner
diff --git a/backend/abaton.h b/backend/abaton.h
new file mode 100644
index 0000000..1f46ded
--- /dev/null
+++ b/backend/abaton.h
@@ -0,0 +1,133 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998 David Huggins-Daines, heavily based on the Apple
+ scanner driver (since Abaton scanners are very similar to old Apple
+ scanners), which is (C) 1998 Milon Firikis, which is, in turn, based
+ on the Mustek driver, (C) 1996-7 David Mosberger-Tang.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef abaton_h
+#define abaton_h
+
+#include <sys/types.h>
+
+enum Abaton_Modes
+ {
+ ABATON_MODE_LINEART=0,
+ ABATON_MODE_HALFTONE,
+ ABATON_MODE_GRAY
+ };
+
+enum Abaton_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+ OPT_RESOLUTION_BIND,
+ OPT_PREVIEW,
+ OPT_HALFTONE_PATTERN,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+ OPT_NEGATIVE,
+ OPT_MIRROR,
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+enum ScannerModels
+{
+ ABATON_300GS,
+ ABATON_300S
+};
+
+typedef struct Abaton_Device
+ {
+ struct Abaton_Device *next;
+ SANE_Int ScannerModel;
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ unsigned flags;
+ }
+Abaton_Device;
+
+typedef struct Abaton_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct Abaton_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Bool scanning;
+ SANE_Bool AbortedByUser;
+
+ SANE_Parameters params;
+
+ /* The actual bpp, before "Pseudo-8-bit" fiddling */
+ SANE_Int bpp;
+
+ /* window, in pixels */
+ SANE_Int ULx;
+ SANE_Int ULy;
+ SANE_Int Width;
+ SANE_Int Height;
+
+ int fd; /* SCSI filedescriptor */
+
+ /* scanner dependent/low-level state: */
+ Abaton_Device *hw;
+
+ }
+Abaton_Scanner;
+
+#endif /* abaton_h */
diff --git a/backend/agfafocus.c b/backend/agfafocus.c
new file mode 100644
index 0000000..bc21558
--- /dev/null
+++ b/backend/agfafocus.c
@@ -0,0 +1,2083 @@
+/* sane - Scanner Access Now Easy.
+
+ This file (C) 1997 Ingo Schneider
+ (C) 1998 Karl Anders Øygard
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for AGFA Focus flatbed scanners. */
+
+#include "../include/sane/config.h"
+
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_thread.h"
+
+#define BACKEND_NAME agfafocus
+#include "../include/sane/sanei_backend.h"
+
+#include "agfafocus.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+
+#undef Byte
+#define Byte SANE_Byte
+
+static int num_devices;
+static AgfaFocus_Device *agfafocus_devices;
+
+static const SANE_String_Const focus_mode_list[] =
+{
+ "Lineart", "Gray (6 bit)",
+ 0
+};
+
+static const SANE_String_Const focusii_mode_list[] =
+{
+ "Lineart", "Gray (6 bit)", "Gray (8 bit)",
+ 0
+};
+
+static const SANE_String_Const focuscolor_mode_list[] =
+{
+ "Lineart", "Gray (6 bit)", "Gray (8 bit)", "Color (18 bit)", "Color (24 bit)",
+ 0
+};
+
+static const SANE_String_Const halftone_list[] =
+{
+ "None", "Dispersed dot 4x4", "Round (Clustered dot 4x4)", "Diamond (Clustered dot 4x4)",
+ 0
+};
+
+static const SANE_String_Const halftone_upload_list[] =
+{
+ "None", "Dispersed dot 4x4", "Round (Clustered dot 4x4)", "Diamond (Clustered dot 4x4)",
+ 0
+};
+
+static const SANE_String_Const source_list[] =
+{
+ "Opaque/Normal", "Transparency",
+ 0
+};
+
+static const SANE_String_Const quality_list[] =
+{
+ "Low", "Normal", "High",
+ 0
+};
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (11, ">> max_string_size\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (11, "<< max_string_size\n");
+ return max_size;
+}
+
+/* sets loc_s bytes long value at offset loc in scsi command to value size */
+static void
+set_size (Byte * loc, int loc_s, size_t size)
+{
+ int i;
+
+ for (i = 0; i < loc_s; i++)
+ {
+ loc[loc_s - i - 1] = (size >> (i * 8)) & 0xff;
+ }
+}
+
+/* gets loc_s bytes long value from loc in scsi command */
+static int
+get_size (Byte * loc, int loc_s)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < loc_s; i++)
+ {
+ j = (j << 8) + (loc[i] & 0xff);
+ }
+
+ return j;
+}
+
+static long
+reserve_unit (int fd)
+{
+ struct
+ {
+ /* Command */
+ Byte cmd;
+ Byte lun;
+ Byte res[2];
+ Byte tr_len;
+ Byte ctrl;
+ }
+ scsi_reserve;
+
+ memset (&scsi_reserve, 0, sizeof (scsi_reserve));
+
+ scsi_reserve.cmd = 0x16; /* RELEASE */
+
+ DBG (3, "reserve_unit()\n");
+ return sanei_scsi_cmd (fd, &scsi_reserve, sizeof (scsi_reserve), 0, 0);
+}
+
+static long
+release_unit (int fd)
+{
+ struct
+ {
+ /* Command */
+ Byte cmd;
+ Byte lun;
+ Byte res[2];
+ Byte tr_len;
+ Byte ctrl;
+ }
+ scsi_release;
+
+ memset (&scsi_release, 0, sizeof (scsi_release));
+
+ scsi_release.cmd = 0x17; /* RELEASE */
+
+ DBG (3, "release_unit()\n");
+ return sanei_scsi_cmd (fd, &scsi_release, sizeof (scsi_release), 0, 0);
+}
+
+static SANE_Status
+test_ready (int fd)
+{
+ SANE_Status status;
+ int try;
+
+ struct
+ {
+ /* Command */
+ Byte cmd;
+ Byte lun;
+ Byte res[2];
+ Byte tr_len;
+ Byte ctrl;
+ }
+ scsi_test_ready;
+
+ memset (&scsi_test_ready, 0, sizeof (scsi_test_ready));
+
+ scsi_test_ready.cmd = 0x00; /* TEST UNIT READY */
+
+ for (try = 0; try < 1000; ++try)
+ {
+ DBG (3, "test_ready: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd (fd, &scsi_test_ready, sizeof (scsi_test_ready),
+ 0, 0);
+
+ switch (status)
+ {
+ case SANE_STATUS_DEVICE_BUSY:
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+
+ default:
+ DBG (1, "test_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (1, "test_ready: timed out after %d attempts\n", try);
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char *result, void *arg)
+{
+ scsi_fd = scsi_fd; /* silence gcc */
+ arg = arg; /* silence gcc */
+
+ if (result[0])
+ {
+ DBG (0, "sense_handler() : sense code = %02x\n", result[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+}
+
+static SANE_Status
+stop_scan (int fd)
+{
+ fd = fd; /* silence gcc */
+
+ /* XXX don't know how to stop the scanner. To be tested ! */
+#if 0
+ const Byte scsi_rewind[] =
+ {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ DBG (1, "Trying to stop scanner...\n");
+ return sanei_scsi_cmd (fd, scsi_rewind, sizeof (scsi_rewind), 0, 0);
+#else
+ return SANE_STATUS_GOOD;
+#endif
+}
+
+
+static SANE_Status
+start_scan (int fd, SANE_Bool cont)
+{
+ struct
+ {
+ /* Command */
+ Byte cmd;
+ Byte lun;
+ Byte res[2];
+ Byte tr_len;
+ Byte ctrl;
+
+ /* Data */
+ Byte wid;
+ }
+ scsi_start_scan;
+
+ memset (&scsi_start_scan, 0, sizeof (scsi_start_scan));
+
+ scsi_start_scan.cmd = 0x1b; /* SCAN */
+ scsi_start_scan.tr_len = 1;
+ scsi_start_scan.wid = 0;
+ scsi_start_scan.ctrl = (cont == SANE_TRUE) ? 0x80 : 0x00;
+
+ DBG (1, "Starting scanner ...\n");
+ return sanei_scsi_cmd (fd, &scsi_start_scan, sizeof (scsi_start_scan), 0, 0);
+}
+
+static void
+wait_ready (int fd)
+{
+ struct
+ {
+ Byte bytes[2]; /* Total # of bytes */
+ Byte scan[2]; /* ms to complete - driver sleep time */
+ } result;
+
+ size_t size = sizeof (result);
+ SANE_Status status;
+
+ struct {
+ Byte cmd;
+ Byte lun;
+ Byte data_type;
+ Byte re1[3];
+ Byte tr_len[3];
+ Byte ctrl;
+ } cmd;
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ cmd.cmd = 0x28; /* READ */
+ cmd.data_type = 0x80; /* get scan time */
+
+ set_size (cmd.tr_len, 3, sizeof (result));
+
+ while (1)
+ {
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd),
+ &result, &size);
+
+ if (status != SANE_STATUS_GOOD || size != sizeof (result))
+ {
+ /*
+ Command failed, the assembler code of the windows scan library
+ ignores this condition, and so do I
+ */
+ break;
+ }
+ else
+ {
+ /* left is the amount of seconds left till the scanner is
+ ready * 100 */
+ int left = get_size (result.scan, 2);
+
+ DBG (1, "wait_ready() : %d left...\n", left);
+
+ if (!left)
+ break;
+ /* We delay only for half the given time */
+ else if (left < 200)
+ usleep (left * 5000);
+ else
+ sleep (left / 200);
+ }
+ }
+
+ return;
+}
+
+static SANE_Status
+get_read_sizes (int fd, int *lines_available, int *bpl, int *total_lines)
+{
+ struct {
+ Byte reserved1[8];
+ Byte line_width[2];
+ Byte total_lines[2];
+ Byte cur_line[2];
+ Byte lines_this_block[2];
+ Byte reserved[8];
+ } read_sizes;
+
+ const Byte scsi_read[] =
+ {
+ 0x28, 0x00, /* opcode, lun */
+ 0x81, /* data type 81 == read time left */
+ 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, sizeof (read_sizes), /* transfer length */
+ 0x00, /* control byte */
+ };
+
+ size_t size = sizeof (read_sizes);
+ SANE_Status status;
+
+ status = sanei_scsi_cmd (fd, scsi_read, sizeof (scsi_read), &read_sizes, &size);
+
+ if (status != SANE_STATUS_GOOD || size != sizeof (read_sizes))
+ {
+ /* Command failed */
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ *lines_available = get_size (read_sizes.lines_this_block, 2);
+ *bpl = get_size (read_sizes.cur_line, 2);
+ if (total_lines)
+ *total_lines = get_size (read_sizes.total_lines, 2);
+ }
+
+ DBG (1, "get_read_sizes() : %d of %d, %d\n",
+ *lines_available, total_lines ? *total_lines : -1, *bpl);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+set_window (AgfaFocus_Scanner * s)
+/* This function sets and sends the window for scanning */
+{
+ double pixels_per_mm = (double) s->val[OPT_RESOLUTION].w / MM_PER_INCH;
+
+ SANE_Bool auto_bright = s->val[OPT_AUTO_BRIGHTNESS].b;
+ SANE_Bool auto_contr = s->val[OPT_AUTO_CONTRAST].b;
+
+ /* ranges down 255 (dark) down to 1(bright) */
+ int brightness = auto_bright ? 0 : (SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)
+ * -1.27 + 128.5);
+ /* ranges from 1 (little contrast) up to 255 (much contrast) */
+ int contrast = auto_contr ? 0 : (SANE_UNFIX (s->val[OPT_CONTRAST].w)
+ * 1.27 + 128.5);
+
+ int width;
+
+ /* ranges from 40 (dark) down to 0 (bright) */
+ int bright_adjust = (SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) * -20.0) / 100.0 + 20.0;
+
+ /* ranges from 20 (little contrast) down to -20 = 235 (much contrast) */
+ int contr_adjust = (SANE_UNFIX (s->val[OPT_CONTRAST].w) * -20.0) / 100.0;
+
+ /* Warning ! The following structur SEEMS to be a valid SCSI-2 SET_WINDOW
+ command. But e.g. the limits for the window are only 2 Bytes instead
+ of 4. The scanner was built at about 1990, so SCSI-2 wasn't available
+ for development... */
+
+ struct
+ {
+ Byte cmd;
+ Byte lun;
+ Byte re1[4];
+ Byte tr_len[3];
+ Byte ctrl;
+
+ Byte re2[6];
+ Byte wd_len[2];
+
+ struct
+ {
+ Byte wid; /* Window ID */
+ Byte autobit; /* Window creation */
+
+ Byte x_axis_res[2]; /* X resolution */
+ Byte y_axis_res[2]; /* X resolution */
+
+ Byte x_axis_ul[2]; /* X upper left */
+ Byte y_axis_ul[2]; /* Y upper left */
+
+ Byte wwidth[2]; /* Width */
+ Byte wlength[2]; /* Length */
+
+ Byte contrast; /* Contrast */
+ Byte dummy1;
+ Byte intensity; /* Intensity */
+
+ Byte image_comp; /* Image composition (0, 2, 5) */
+ Byte bpp; /* Bits per pixel */
+
+ Byte tonecurve; /* Tone curve (0 - 8) */
+ Byte ht_pattern; /* Halftone pattern */
+ Byte paddingtype; /* Padding type */
+
+ Byte bitordering[2]; /* Bit ordering (0 = left to right) */
+ Byte comprtype; /* Compression type */
+ Byte comprarg; /* Compression argument */
+
+ Byte dummy2[6];
+ Byte edge; /* Sharpening (0 - 7) */
+ Byte dummy3;
+
+ Byte bright_adjust; /* */
+ Byte contr_adjust; /* */
+
+ Byte imagewidthtruncation; /* */
+
+ Byte dummy4;
+ Byte quality_type; /* 0 normal, 1 high, 255 low */
+ Byte red_att;
+ Byte green_att;
+ Byte blue_att;
+
+ Byte dummy5[5];
+ Byte color_planes;
+ Byte orig_type;
+ Byte fixturetype;
+ Byte exposure[2];
+ Byte defocus[2];
+ Byte dummy6[4];
+ Byte descreen_factor;
+
+ Byte packing_word_length;
+ Byte packing_number_of_pixels;
+ Byte packing_color_mode;
+ Byte strokenab;
+ Byte rotatenab;
+ Byte autostrokenab;
+ Byte dummy7;
+ }
+ wd;
+
+ }
+ cmd;
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ cmd.cmd = 0x24; /* SET WINDOW PARAMETERS */
+
+ switch (s->hw->type)
+ {
+ case AGFAGRAY64:
+ case AGFALINEART:
+ case AGFAGRAY256:
+ set_size (cmd.tr_len, 3, 36 + 8);
+ set_size (cmd.wd_len, 2, 36);
+ break;
+
+ case AGFACOLOR:
+ set_size (cmd.tr_len, 3, 65 + 8);
+ set_size (cmd.wd_len, 2, 65);
+ break;
+ }
+
+ /* Resolution. Original comment in German: Aufloesung */
+ set_size (cmd.wd.x_axis_res, 2, s->val[OPT_RESOLUTION].w);
+ set_size (cmd.wd.y_axis_res, 2, s->val[OPT_RESOLUTION].w);
+
+ /* Scan window position/size. Original comment in German:
+ Fensterposition / Groesse */
+ set_size (cmd.wd.x_axis_ul, 2,
+ SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5);
+ set_size (cmd.wd.y_axis_ul, 2,
+ SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5);
+
+ width = (SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) * pixels_per_mm) + 0.5;
+
+ if (s->bpp == 1 && width % 8)
+ width += 8 - width % 8;
+
+ set_size (cmd.wd.wwidth, 2, width);
+ set_size (cmd.wd.wlength, 2, SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
+ * pixels_per_mm + 0.5);
+
+ cmd.wd.bpp = s->bpp;
+
+ if (s->mode == COLOR18BIT ||
+ s->mode == COLOR24BIT)
+ {
+ cmd.wd.paddingtype = 3;
+ cmd.wd.ht_pattern = 3;
+
+ cmd.wd.red_att = s->r_att;
+ cmd.wd.blue_att = s->g_att;
+ cmd.wd.green_att = s->b_att;
+ cmd.wd.color_planes = 0x0e;
+
+ set_size (cmd.wd.exposure, 2, s->exposure);
+
+ cmd.wd.packing_word_length = 1;
+ cmd.wd.packing_number_of_pixels = 1;
+ cmd.wd.packing_color_mode = 2;
+
+ if (s->bpp == 6)
+ cmd.wd.edge = s->edge;
+
+ DBG (3,
+ "Setting parameters: imc %d, bpp %d, res %d, exp %d, attenuation [%d, %d, %d], edge %d\n",
+ s->image_composition, s->bpp, s->val[OPT_RESOLUTION].w,
+ s->exposure, cmd.wd.red_att, cmd.wd.blue_att, cmd.wd.green_att, s->edge);
+ }
+ else
+ {
+ if (s->bpp == 1)
+ cmd.wd.ht_pattern = s->halftone;
+ else
+ cmd.wd.ht_pattern = 3;
+
+ cmd.wd.intensity = brightness;
+ cmd.wd.contrast = contrast;
+
+ cmd.wd.contr_adjust = contr_adjust;
+ cmd.wd.bright_adjust = bright_adjust;
+
+ cmd.wd.tonecurve = s->tonecurve;
+ cmd.wd.paddingtype = 3;
+ cmd.wd.edge = s->edge;
+
+ if (s->lin_log)
+ cmd.wd.dummy3 = 0x02;
+
+ DBG (3,
+ "Setting parameters: imc %d, bpp %d, res %d, bri %d, con %d, bad %d, cad %d, ht %d, edge %d\n",
+ s->image_composition, s->bpp, s->val[OPT_RESOLUTION].w,
+ brightness, contrast, bright_adjust, contr_adjust, s->halftone, s->edge);
+ }
+
+ cmd.wd.image_comp = s->image_composition;
+ cmd.wd.quality_type = s->quality;
+ cmd.wd.orig_type = s->original;
+
+ return sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
+}
+
+/* Tell scanner to scan more data. */
+
+static SANE_Status
+request_more_data (AgfaFocus_Scanner * s)
+{
+ SANE_Status status;
+ int lines_available;
+ int bytes_per_line;
+
+ status = start_scan (s->fd, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (!s->hw->disconnect)
+ wait_ready (s->fd);
+
+ status = get_read_sizes (s->fd, &lines_available, &bytes_per_line, 0);
+
+ if (!lines_available)
+ return SANE_STATUS_INVAL;
+
+ s->lines_available = lines_available;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+upload_dither_matrix (AgfaFocus_Scanner * s, int rows, int cols, int *dither_matrix)
+{
+ struct {
+ Byte cmd;
+ Byte lun;
+ Byte data_type;
+ Byte re1[3];
+ Byte tr_len[3];
+ Byte ctrl;
+
+ struct {
+ Byte nrrows[2];
+ Byte nrcols[2];
+
+ struct {
+ Byte data[2];
+ } element[256];
+ } wd;
+ } cmd;
+
+ SANE_Status status;
+ int i;
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ cmd.cmd = 0x2a; /* WRITE */
+ cmd.data_type = 0x81; /* upload dither matrix */
+
+ set_size (cmd.tr_len, 3, 4 + (2 * rows * cols));
+ set_size (cmd.wd.nrrows, 2, rows);
+ set_size (cmd.wd.nrcols, 2, cols);
+
+ for (i = 0; i < cols * rows; ++i)
+ set_size (cmd.wd.element[i].data, 2, dither_matrix[i]);
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ /* Command failed */
+ return SANE_STATUS_IO_ERROR;
+
+ DBG (1, "upload_dither_matrix(): uploaded dither matrix: %d, %d\n", rows, cols);
+
+ return SANE_STATUS_GOOD;
+}
+
+#if 0
+static SANE_Status
+upload_tonecurve (AgfaFocus_Scanner * s, int color_type, int input, int output, int dither_matrix[256])
+{
+ struct {
+ Byte cmd;
+ Byte lun;
+ Byte re1[4];
+ Byte tr_len[3];
+ Byte ctrl;
+
+ Byte re2[6];
+ Byte wd_len[2];
+
+ struct {
+ Byte color_type[2];
+ Byte nrinput[2];
+ Byte nroutput[2];
+
+ struct {
+ Byte data[2];
+ } outputval[256];
+ } wd;
+ } cmd;
+
+ SANE_Status status;
+ int i, j;
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ cmd.cmd = 0x80;
+
+ set_size (cmd.tr_len, 3, sizeof (cmd.wd));
+ set_size (cmd.wd.nrrows, 2, rows);
+ set_size (cmd.wd.nrrows, 2, cols);
+
+ for (i = 0; i < cols; ++i)
+ for (j = 0; j < rows; ++j)
+ set_size (cmd.wd.element[j + i * rows].data, 2, dither_matrix[j + i * rows]);
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ /* * Command failed * */
+ return SANE_STATUS_IO_ERROR;
+
+ DBG (1, "upload_dither_matrix(): uploaded dither matrix\n");
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+/* May only be called when there is at least one row of data to
+ be read.
+
+ Original comment in German: Darf nur aufgerufen werden, wenn
+ wirklich noch Zeilen zu scannen/lesen sind ! */
+static SANE_Status
+read_data (AgfaFocus_Scanner * s, SANE_Byte *buf, int lines, int bpl)
+{
+ struct {
+ Byte cmd;
+ Byte lun;
+ Byte re1[4];
+ Byte tr_len[3];
+ Byte ctrl;
+ } cmd;
+
+ SANE_Status status;
+ size_t size;
+ unsigned int i;
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ cmd.cmd = 0x28; /* READ */
+
+ set_size (cmd.tr_len, 3, lines);
+ size = lines * bpl;
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sanei_scsi_cmd() = %d\n", status);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (size != ((unsigned int) lines * bpl))
+ {
+ DBG (1, "sanei_scsi_cmd(): got %lu bytes, expected %d\n",
+ (u_long) size, lines * bpl);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (1, "Got %lu bytes\n", (u_long) size);
+
+ /* Reverse: */
+ if (s->bpp != 1)
+ {
+ if (s->bpp != 6)
+ for (i = 0; i < size; i++)
+ buf[i] = 255 - buf[i];
+ else
+ for (i = 0; i < size; i++)
+ buf[i] = 255 - ((buf[i] * 256.0f) / 64.0f);
+ }
+
+ s->lines_available -= lines;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+attach (const char *devname, AgfaFocus_Device ** devp)
+{
+#define ATTACH_SCSI_INQ_LEN 55
+ const Byte scsi_inquiry[] =
+ {
+ 0x12, 0x00, 0x00, 0x00, ATTACH_SCSI_INQ_LEN, 0x00
+ };
+ Byte result[ATTACH_SCSI_INQ_LEN];
+
+ int fd;
+ AgfaFocus_Device *dev;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ for (dev = agfafocus_devices; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "attach: opening %s\n", devname);
+ status = sanei_scsi_open (devname, &fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed (%s)\n", sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ status = sanei_scsi_cmd (fd, scsi_inquiry, sizeof (scsi_inquiry),
+ result, &size);
+ if (status != SANE_STATUS_GOOD || size != ATTACH_SCSI_INQ_LEN)
+ {
+ DBG (1, "attach: inquiry failed (%s)\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ status = test_ready (fd);
+ sanei_scsi_close (fd);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* The structure send by the scanner after inquiry is not SCSI-2
+ compatible. The standard manufacturer/model fields are no ASCII
+ strings, but ? At offset 36 my SIEMENS scanner identifies as an
+ AGFA one ?! */
+
+ if (result[0] != 6 || strncmp ((char *)result + 36, "AGFA0", 5))
+ {
+ DBG (1, "attach: device doesn't look like a Siemens 9036 scanner\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "Inquiry data:\n");
+ DBG (4, "-----------\n");
+ for (i = 5; i < 55; i += 10)
+ DBG (4, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ result[i], result[i + 1], result[i + 2], result[i + 3], result[i + 4],
+ result[i + 5], result[i + 6], result[i + 7], result[i + 8],
+ result[i + 9]);
+
+ dev = malloc (sizeof (*dev));
+
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devname);
+ if (!strncmp ((char *)result + 36, "AGFA01", 6)) {
+ dev->sane.vendor = "AGFA";
+ dev->sane.model = "Focus GS Scanner (6 bit)";
+ dev->upload_user_defines = SANE_TRUE;
+ dev->type = AGFAGRAY64;
+ } else if (!strncmp ((char *)result + 36, "AGFA02", 6)) {
+ dev->sane.vendor = "AGFA";
+ dev->sane.model = "Focus Lineart Scanner";
+ dev->upload_user_defines = SANE_FALSE;
+ dev->type = AGFALINEART;
+ } else if (!strncmp ((char *)result + 36, "AGFA03", 6)) {
+ dev->sane.vendor = "AGFA";
+ dev->sane.model = "Focus II";
+ dev->upload_user_defines = SANE_TRUE;
+ dev->type = AGFAGRAY256;
+ } else if (!strncmp ((char *)result + 36, "AGFA04", 6)) {
+ dev->sane.vendor = "AGFA";
+ dev->sane.model = "Focus Color";
+ dev->upload_user_defines = SANE_TRUE;
+ dev->type = AGFACOLOR;
+ } else {
+ free (dev);
+ DBG (1, "attach: device looks like an AGFA scanner, but wasn't recognised\n");
+ return SANE_STATUS_INVAL;
+ }
+ dev->sane.type = "flatbed scanner";
+
+ dev->transparent = result[45] & 0x80 ? SANE_TRUE : SANE_FALSE;
+ dev->analoglog = result[46] & 0x80 ? SANE_TRUE : SANE_FALSE;
+ dev->tos5 = result[46] & 0x05 ? SANE_TRUE : SANE_FALSE;
+ dev->quality = result[47] & 0x40 ? SANE_TRUE : SANE_FALSE;
+ dev->disconnect = result[47] & 0x80 ? SANE_TRUE : SANE_FALSE;
+
+ DBG (4, "\n");
+ DBG (4, "scan modes:\n");
+ DBG (4, "-----------\n");
+ DBG (4, "three pass color mode: %s\n", dev->type >= AGFACOLOR ? "yes" : "no");
+ DBG (4, "8 bit gray mode: %s\n", dev->type >= AGFAGRAY64 ? "yes" : "no");
+ DBG (4, "uploadable matrices: %s\n", dev->upload_user_defines ? "yes" : "no");
+ DBG (4, "transparency: %s\n", dev->transparent ? "yes" : "no");
+ DBG (4, "disconnect: %s\n", dev->disconnect ? "yes" : "no");
+ DBG (4, "quality calibration: %s\n", dev->quality ? "yes" : "no");
+
+ dev->handle = 0;
+
+ DBG (3, "attach: found AgfaFocus scanner model\n");
+
+ ++num_devices;
+ dev->next = agfafocus_devices;
+ agfafocus_devices = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_eof (AgfaFocus_Scanner *s)
+{
+ if (s->pipe >= 0)
+ {
+ close (s->pipe);
+ s->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+
+
+static SANE_Status
+do_cancel (AgfaFocus_Scanner * s)
+{
+ s->scanning = SANE_FALSE;
+ s->pass = 0;
+
+ do_eof (s);
+
+ if (s->reader_pid != -1)
+ {
+ int exit_status;
+
+ /* ensure child knows it's time to stop: */
+ sanei_thread_kill (s->reader_pid);
+ sanei_thread_waitpid (s->reader_pid, &exit_status);
+ s->reader_pid = -1;
+ }
+
+ if (s->fd >= 0)
+ {
+ stop_scan (s->fd);
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+
+static SANE_Status
+init_options (AgfaFocus_Scanner * s)
+{
+ int i;
+
+ /* Hardware Limitations: must be static ! */
+ static const SANE_Int dpi_list[] =
+ {8, 100, 200, 300, 400, 500, 600, 700, 800};
+
+ static const SANE_Range percentage_range =
+ {
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+ };
+
+ static const SANE_Range sharpen_range =
+ {0, 7, 1};
+ static const SANE_Range exposure_range =
+ {0, 100, 0};
+ static const SANE_Range attenuation_range =
+ {
+ 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+ };
+
+
+ static const SANE_Range x_range =
+ {0, SANE_FIX (8.27 * MM_PER_INCH), 0};
+ static const SANE_Range y_range =
+ {0, SANE_FIX (12.72 * MM_PER_INCH), 0};
+
+ /* ------ */
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+
+ switch (s->hw->type)
+ {
+ case AGFACOLOR:
+ s->opt[OPT_MODE].size = max_string_size (focuscolor_mode_list);
+ s->opt[OPT_MODE].constraint.string_list = focuscolor_mode_list;
+ s->val[OPT_MODE].s = strdup (focuscolor_mode_list[0]);
+ break;
+ case AGFAGRAY256:
+ s->opt[OPT_MODE].size = max_string_size (focusii_mode_list);
+ s->opt[OPT_MODE].constraint.string_list = focusii_mode_list;
+ s->val[OPT_MODE].s = strdup (focusii_mode_list[0]);
+ break;
+ default:
+ s->opt[OPT_MODE].size = max_string_size (focus_mode_list);
+ s->opt[OPT_MODE].constraint.string_list = focus_mode_list;
+ s->val[OPT_MODE].s = strdup (focus_mode_list[0]);
+ break;
+ }
+
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+ s->val[OPT_RESOLUTION].w = 100;
+
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].unit = SANE_UNIT_NONE;
+ if (!s->hw->transparent)
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ else
+ s->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+ s->val[OPT_SOURCE].s = strdup (source_list[0]);
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y].w = y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* exposure */
+ s->opt[OPT_EXPOSURE].name = "exposure";
+ s->opt[OPT_EXPOSURE].title = "Exposure";
+ s->opt[OPT_EXPOSURE].desc = "Analog exposure control.";
+ s->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_EXPOSURE].constraint.range = &exposure_range;
+ s->val[OPT_EXPOSURE].w = 23;
+
+ /* brightness automatic correct */
+ s->opt[OPT_AUTO_BRIGHTNESS].name = "adjust-bright";
+ s->opt[OPT_AUTO_BRIGHTNESS].title = "Automatic brightness correction";
+ s->opt[OPT_AUTO_BRIGHTNESS].desc = "Turns on automatic brightness correction of "
+ "the acquired image. This makes the scanner do a two pass scan to analyse the "
+ "brightness of the image before it's scanned.";
+ s->opt[OPT_AUTO_BRIGHTNESS].type = SANE_TYPE_BOOL;
+ s->val[OPT_AUTO_BRIGHTNESS].b = SANE_FALSE;
+
+ /* contrast automatic correct */
+ s->opt[OPT_AUTO_CONTRAST].name = "adjust-contr";
+ s->opt[OPT_AUTO_CONTRAST].title = "Automatic contrast correction";
+ s->opt[OPT_AUTO_CONTRAST].desc = "Turns on automatic contrast correction of "
+ "the acquired image. This makes the scanner do a two pass scan to analyse "
+ "the contrast of the image to be scanned.";
+ s->opt[OPT_AUTO_CONTRAST].type = SANE_TYPE_BOOL;
+ s->val[OPT_AUTO_CONTRAST].b = SANE_FALSE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = "Controls the brightness of the acquired image. "
+ "When automatic brightness is enabled, this can be used to adjust the selected brightness.";
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = "Controls the contrast of the acquired image. "
+ "When automatic contrast is enabled, this can be used to adjust the selected contrast.";
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* halftone patterns */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_PATTERN].size = 32;
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ if (s->hw->upload_user_defines)
+ {
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_upload_list;
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_upload_list[0]);
+ }
+ else
+ {
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_list;
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_list[0]);
+ }
+
+ /* red-attenuation */
+ s->opt[OPT_ATTENUATION_RED].name = "red-attenuation";
+ s->opt[OPT_ATTENUATION_RED].title = "Red attenuation";
+ s->opt[OPT_ATTENUATION_RED].desc = "Controls the red attenuation of the acquired image. "
+ "Higher values mean less impact on scanned image.";
+ s->opt[OPT_ATTENUATION_RED].type = SANE_TYPE_FIXED;
+ s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_RED].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_ATTENUATION_RED].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ATTENUATION_RED].constraint.range = &attenuation_range;
+ s->val[OPT_ATTENUATION_RED].w = SANE_FIX (50.0);
+
+ /* green-attenuation */
+ s->opt[OPT_ATTENUATION_GREEN].name = "green-attenuation";
+ s->opt[OPT_ATTENUATION_GREEN].title = "Green attenuation";
+ s->opt[OPT_ATTENUATION_GREEN].desc = "Controls the green attenuation of the acquired image. "
+ "Higher values mean less impact on scanned image.";
+ s->opt[OPT_ATTENUATION_GREEN].type = SANE_TYPE_FIXED;
+ s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_GREEN].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_ATTENUATION_GREEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ATTENUATION_GREEN].constraint.range = &attenuation_range;
+ s->val[OPT_ATTENUATION_GREEN].w = SANE_FIX (50.0);
+
+ /* blue-attenuation */
+ s->opt[OPT_ATTENUATION_BLUE].name = "blue-attenuation";
+ s->opt[OPT_ATTENUATION_BLUE].title = "Blue attenuation";
+ s->opt[OPT_ATTENUATION_BLUE].desc = "Controls the blue attenuation of the acquired image. "
+ "Higher values mean less impact on scanned image.";
+ s->opt[OPT_ATTENUATION_BLUE].type = SANE_TYPE_FIXED;
+ s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_BLUE].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_ATTENUATION_BLUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ATTENUATION_BLUE].constraint.range = &attenuation_range;
+ s->val[OPT_ATTENUATION_BLUE].w = SANE_FIX (50.0);
+
+ /* quality-calibration */
+ s->opt[OPT_QUALITY].name = SANE_NAME_QUALITY_CAL;
+ s->opt[OPT_QUALITY].title = SANE_TITLE_QUALITY_CAL;
+ s->opt[OPT_QUALITY].desc = "Controls the calibration that will be done in the "
+ "scanner. Less calibration result in faster scanner times.";
+ s->opt[OPT_QUALITY].type = SANE_TYPE_STRING;
+ s->opt[OPT_QUALITY].size = 32;
+ if (!s->hw->quality)
+ s->opt[OPT_QUALITY].cap |= SANE_CAP_INACTIVE;
+ else
+ s->opt[OPT_QUALITY].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_QUALITY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_QUALITY].constraint.string_list = quality_list;
+ s->val[OPT_QUALITY].s = strdup (quality_list[1]);
+
+ /* sharpening */
+ s->opt[OPT_SHARPEN].name = "sharpen";
+ s->opt[OPT_SHARPEN].title = "Sharpening";
+ s->opt[OPT_SHARPEN].desc = "Controls the sharpening that will be "
+ "done by the video processor in the scanner.";
+ s->opt[OPT_SHARPEN].type = SANE_TYPE_INT;
+ s->opt[OPT_SHARPEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHARPEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHARPEN].constraint.range = &sharpen_range;
+ s->val[OPT_SHARPEN].w = 1;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach (dev, 0);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* silence gcc */
+
+ DBG_INIT ();
+
+ sanei_thread_init ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open ("agfafocus.conf");
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ AgfaFocus_Device *dev, *next;
+
+ for (dev = agfafocus_devices; dev; dev = next)
+ {
+ next = dev->next;
+ if (dev->handle)
+ sane_close (dev->handle);
+ free (dev);
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ AgfaFocus_Device *dev;
+ int i;
+
+ local_only = local_only; /* silence gcc */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = agfafocus_devices, i = 0; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ AgfaFocus_Device *dev;
+ SANE_Status status;
+ AgfaFocus_Scanner *s;
+
+ if (devicename[0])
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = agfafocus_devices;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (dev->handle)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+
+ memset (s, 0, sizeof (*s));
+ s->scanning = SANE_FALSE;
+
+ s->fd = -1;
+ s->hw = dev;
+ s->hw->handle = s;
+
+ init_options (s);
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ AgfaFocus_Scanner *s = handle;
+
+ if (s->scanning)
+ do_cancel (handle);
+
+ s->hw->handle = 0;
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ AgfaFocus_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ AgfaFocus_Scanner *s = handle;
+ SANE_Status status;
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS || !SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_SHARPEN:
+ case OPT_EXPOSURE:
+ case OPT_ATTENUATION_RED:
+ case OPT_ATTENUATION_GREEN:
+ case OPT_ATTENUATION_BLUE:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ case OPT_AUTO_BRIGHTNESS:
+ case OPT_AUTO_CONTRAST:
+ *(SANE_Bool *) val = s->val[option].b;
+ break;
+ case OPT_MODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_QUALITY:
+ case OPT_SOURCE:
+ strcpy (val, s->val[option].s);
+ return (SANE_STATUS_GOOD);
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_SHARPEN:
+ case OPT_EXPOSURE:
+ case OPT_ATTENUATION_RED:
+ case OPT_ATTENUATION_GREEN:
+ case OPT_ATTENUATION_BLUE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_AUTO_BRIGHTNESS:
+ case OPT_AUTO_CONTRAST:
+ s->val[option].b = *(SANE_Bool *) val;
+ break;
+ case OPT_MODE:
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ if (s->val[option].s)
+ free (s->val[option].s);
+
+ s->val[option].s = strdup (val);
+
+ if (strcmp (s->val[option].s, "Gray (6 bit)") == 0)
+ s->mode = GRAY6BIT;
+ else if (strcmp (s->val[option].s, "Gray (8 bit)") == 0)
+ s->mode = GRAY8BIT;
+ else if (strcmp (s->val[option].s, "Color (18 bit)") == 0)
+ s->mode = COLOR18BIT;
+ else if (strcmp (s->val[option].s, "Color (24 bit)") == 0)
+ s->mode = COLOR24BIT;
+ else
+ s->mode = LINEART;
+
+ switch (s->mode)
+ {
+ case LINEART:
+ s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ break;
+
+ case GRAY6BIT:
+ s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ break;
+
+ case GRAY8BIT:
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE;
+ break;
+
+ case COLOR18BIT:
+ s->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_RED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_GREEN].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_BLUE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ break;
+
+ case COLOR24BIT:
+ s->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_RED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_GREEN].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ATTENUATION_BLUE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_AUTO_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ break;
+ }
+ }
+ break;
+ case OPT_SOURCE:
+ case OPT_QUALITY:
+ case OPT_HALFTONE_PATTERN:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ AgfaFocus_Scanner *s = handle;
+
+ if (!s->scanning)
+ {
+ double width, height, dpi;
+ const char *quality;
+ const char *original;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
+ height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
+ dpi = s->val[OPT_RESOLUTION].w;
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (dpi > 0.0 && width > 0.0 && height > 0.0)
+ {
+ double dots_per_mm = dpi / MM_PER_INCH;
+
+ s->params.pixels_per_line = width * dots_per_mm + 0.5;
+ s->params.lines = height * dots_per_mm + 0.5;
+ }
+
+ /* Should we specify calibration quality? */
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_QUALITY].cap))
+ {
+ DBG(3, " -------------- setting quality\n");
+ quality = s->val[OPT_QUALITY].s;
+ if (strcmp (quality, "Low") == 0 )
+ s->quality = 255;
+ else if (strcmp (quality, "High") == 0)
+ s->quality = 1;
+ else
+ s->quality = 0;
+ }
+ else
+ s->quality = 0;
+
+ /* Should we select source type? */
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SOURCE].cap))
+ {
+ DBG(3, " -------------- setting source\n");
+ original = s->val[OPT_SOURCE].s;
+ if (strcmp (original, "Transparency") == 0)
+ s->original = 0;
+ else
+ s->original = 1;
+ }
+ else
+ s->original = 0;
+
+ s->exposure = ((s->val[OPT_EXPOSURE].w * (255.0f - 80.0f)) / 100.0f) + 80.0f;
+ s->r_att = (SANE_UNFIX (s->val[OPT_ATTENUATION_RED].w) * 20.0f) / 100.0f;
+ s->g_att = (SANE_UNFIX (s->val[OPT_ATTENUATION_GREEN].w) * 20.0f) / 100.0f;
+ s->b_att = (SANE_UNFIX (s->val[OPT_ATTENUATION_BLUE].w) * 20.0f) / 100.0f;
+ s->tonecurve = 0;
+
+ switch (s->mode)
+ {
+ case LINEART:
+ {
+ const char *halftone;
+
+ s->image_composition = 0;
+
+ /* in 1 bpp mode, lines need to be 8 pixel length */
+
+ if (s->params.pixels_per_line % 8)
+ s->params.pixels_per_line += 8 - (s->params.pixels_per_line % 8);
+
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ s->bpp = s->params.depth = 1;
+
+ halftone = s->val[OPT_HALFTONE_PATTERN].s;
+ if (strcmp (halftone, "1") == 0 )
+ s->halftone = 1;
+ else if (strcmp (halftone, "Dispersed dot 4x4") == 0)
+ s->halftone = 2;
+ else if (strcmp (halftone, "Round (Clustered dot 4x4)") == 0)
+ s->halftone = 3;
+ else if (strcmp (halftone, "Diamond (Clustered dot 4x4)") == 0)
+ s->halftone = 4;
+ else if (strcmp (halftone, "User defined") == 0)
+ s->halftone = 5;
+ else
+ s->halftone = 0;
+
+ s->edge = s->val[OPT_SHARPEN].w;
+ }
+ break;
+
+ case GRAY6BIT:
+ s->image_composition = 2;
+
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->bpp = 6;
+ s->params.depth = 8;
+ s->edge = s->val[OPT_SHARPEN].w;
+
+ break;
+
+ case GRAY8BIT:
+ s->image_composition = 2;
+
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->bpp = s->params.depth = 8;
+
+ break;
+
+ case COLOR18BIT:
+ s->image_composition = 5;
+
+ s->params.format = SANE_FRAME_RED;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->bpp = 6;
+ s->params.depth = 8;
+ s->edge = s->val[OPT_SHARPEN].w;
+
+ break;
+
+ case COLOR24BIT:
+ s->image_composition = 5;
+
+ s->params.format = SANE_FRAME_RED;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->bpp = s->params.depth = 8;
+
+ break;
+ }
+
+ s->pass = 0;
+
+ /*s->params.bytes_per_line =
+ (s->params.pixels_per_line + (8 - s->params.depth))
+ / (8 / s->params.depth);*/
+ }
+ else
+ if (s->mode == COLOR18BIT ||
+ s->mode == COLOR24BIT)
+ s->params.format = SANE_FRAME_RED + s->pass;
+
+ s->params.last_frame = (s->params.format != SANE_FRAME_RED && s->params.format != SANE_FRAME_GREEN);
+
+ if (params)
+ *params = s->params;
+ return SANE_STATUS_GOOD;
+}
+
+/* This function is executed as a child process. The reason this is
+ executed as a subprocess is because some (most?) generic SCSI
+ interfaces block a SCSI request until it has completed. With a
+ subprocess, we can let it block waiting for the request to finish
+ while the main process can go about to do more important things
+ (such as recognizing when the user presses a cancel button).
+
+
+ WARNING: Since this is executed as a subprocess, it's NOT possible
+ to update any of the variables in the main process (in particular
+ the scanner state cannot be updated). */
+static int
+reader_process (void *scanner)
+{
+ AgfaFocus_Scanner *s = (AgfaFocus_Scanner *) scanner;
+ int fd = s->reader_pipe;
+
+ SANE_Status status;
+ SANE_Byte *data;
+ int lines_read = 0;
+ int lines_per_buffer;
+ int bytes_per_line = 0, total_lines = 0;
+ int i;
+ sigset_t sigterm_set;
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ if (sanei_thread_is_forked()) close (s->pipe);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ if (!s->hw->disconnect)
+ wait_ready (s->fd);
+
+ status = get_read_sizes (s->fd, &s->lines_available, &bytes_per_line, &total_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: get_read_sizes() failed: %s\n",
+ sane_strstatus (status));
+ do_cancel (s);
+ close (fd);
+ return 1;
+ }
+
+ if (!s->lines_available || !bytes_per_line || !total_lines || bytes_per_line < s->params.bytes_per_line)
+ {
+ DBG (1, "open: invalid sizes: %d, %d, %d\n",
+ s->lines_available, bytes_per_line, total_lines);
+ do_cancel (s);
+ close (fd);
+ return 1;
+ }
+
+ lines_per_buffer = sanei_scsi_max_request_size / bytes_per_line;
+ if (!lines_per_buffer)
+ {
+ close (fd);
+ return 2; /* resolution is too high */
+ }
+
+ data = malloc (lines_per_buffer * bytes_per_line);
+ if (!data)
+ {
+ DBG (1, "open malloc(%lu) failed.\n", (u_long) lines_per_buffer * bytes_per_line);
+ do_cancel (s);
+ close (fd);
+ return 1;
+ }
+
+ while (lines_read < s->params.lines)
+ {
+ int lines = lines_per_buffer;
+
+ if (s->lines_available == 0)
+ {
+ /* No lines in scanner? Scan some more */
+ status = request_more_data (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ close (fd);
+ return 1;
+ }
+ }
+
+ /* We only request as many lines as there are already scanned */
+ if (lines > s->lines_available)
+ lines = s->lines_available;
+
+ DBG (1, "Requesting %d lines, in scanner: %d, total: %d\n", lines,
+ s->lines_available, s->params.lines);
+
+ status = read_data (s, data, lines, bytes_per_line);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_read: read_data() failed (%s)\n",
+ sane_strstatus (status));
+ do_cancel (s);
+ close (fd);
+ return 1;
+ }
+
+ /* Sometimes the scanner will return more bytes per line than
+ requested, so we copy only what we wanted. */
+
+ for (i = 0; i < lines; i++)
+ if (write (fd, data + i * bytes_per_line, s->params.bytes_per_line) != s->params.bytes_per_line)
+ {
+ do_cancel (s);
+ close (fd);
+ return 1;
+ }
+
+ lines_read += lines;
+ }
+
+ close (fd);
+ return 0;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ AgfaFocus_Scanner *s = handle;
+ SANE_Status status;
+ int fds[2];
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* don't initialise scanner if we're doing a three-pass scan */
+
+ if (s->pass == 0)
+ {
+ if (s->fd < 0)
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ s->fd = -1;
+ return status;
+ }
+ }
+
+ status = test_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: test_ready() failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ status = reserve_unit (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: reserve_unit() failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: set_window() failed: %s\n", sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ {
+ int matrix[256] = {
+ 2, 60, 16, 56, 3, 57, 13, 53,
+ 34, 18, 48, 32, 35, 19, 45, 29,
+ 10, 50, 6, 63, 11, 51, 7, 61,
+ 42, 26, 38, 22, 43, 27, 39, 23,
+ 4, 58, 14, 54, 1, 59, 15, 55,
+ 36, 20, 46, 30, 33, 17, 47, 31,
+ 12, 52, 8, 62, 9, 49, 5, 63,
+ 44, 28, 40, 24, 41, 25, 37, 21
+ };
+
+ status = upload_dither_matrix (s, 8, 8, matrix);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: upload_dither_matrix() failed: %s\n", sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+ }
+
+ s->scanning = SANE_TRUE;
+
+ status = start_scan (s->fd, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: start_scan() failed: %s\n", sane_strstatus (status));
+ do_cancel (s);
+ return status;
+ }
+ }
+ else
+ {
+ /* continue three-pass scan */
+
+ status = start_scan (s->fd, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: start_scan() failed: %s\n", sane_strstatus (status));
+ do_cancel (s);
+ return status;
+ }
+ }
+
+ if (pipe (fds) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ s->pipe = fds[0];
+ s->reader_pipe = fds[1];
+ s->reader_pid = sanei_thread_begin (reader_process, (void *) s);
+
+ if (sanei_thread_is_forked()) close (s->reader_pipe);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ AgfaFocus_Scanner *s = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read (s->pipe, buf, max_len);
+ DBG (3, "read %ld bytes\n", (long) nread);
+
+ if (!s->scanning)
+ return do_cancel (s);
+
+ if (nread < 0) {
+ if (errno == EAGAIN) {
+ return SANE_STATUS_GOOD;
+ } else {
+ do_cancel (s);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0) {
+ s->pass++;
+ return do_eof (s);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ AgfaFocus_Scanner *s = handle;
+
+ if (s->reader_pid != -1)
+ sanei_thread_kill (s->reader_pid);
+ s->scanning = SANE_FALSE;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ AgfaFocus_Scanner *s = handle;
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ AgfaFocus_Scanner *s = handle;
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ *fd = s->pipe;
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/agfafocus.conf.in b/backend/agfafocus.conf.in
new file mode 100644
index 0000000..2648863
--- /dev/null
+++ b/backend/agfafocus.conf.in
@@ -0,0 +1,2 @@
+/dev/scanner
+
diff --git a/backend/agfafocus.h b/backend/agfafocus.h
new file mode 100644
index 0000000..078b68e
--- /dev/null
+++ b/backend/agfafocus.h
@@ -0,0 +1,132 @@
+/* sane - Scanner Access Now Easy.
+
+ This file (C) 1997 Ingo Schneider
+ (C) 1998 Karl Anders Øygard
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef agfafocus_h
+#define agfafocus_h
+
+enum AgfaFocus_Scanner_Type
+ {
+ AGFAGRAY64,
+ AGFALINEART,
+ AGFAGRAY256,
+ AGFACOLOR
+ };
+
+typedef enum
+ {
+ LINEART,
+ GRAY6BIT,
+ GRAY8BIT,
+ COLOR18BIT,
+ COLOR24BIT
+ }
+AgfaFocus_Scanner_Mode;
+
+enum AgfaFocus_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_HALFTONE_PATTERN, /* halftone matrix */
+ OPT_RESOLUTION,
+ OPT_SOURCE,
+ OPT_QUALITY, /* quality calibration */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_EXPOSURE,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_AUTO_BRIGHTNESS,
+ OPT_AUTO_CONTRAST,
+ OPT_ATTENUATION_RED,
+ OPT_ATTENUATION_BLUE,
+ OPT_ATTENUATION_GREEN,
+ OPT_SHARPEN, /* sharpening */
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+
+typedef struct AgfaFocus_Device
+ {
+ struct AgfaFocus_Device *next;
+ SANE_Device sane;
+ SANE_Handle handle;
+
+ SANE_Word type;
+ SANE_Bool transparent;
+ SANE_Bool analoglog;
+ SANE_Bool tos5;
+ SANE_Bool quality;
+ SANE_Bool disconnect;
+ SANE_Bool upload_user_defines;
+ }
+AgfaFocus_Device;
+
+typedef struct AgfaFocus_Scanner
+ {
+ /* all the state needed to define a scan request: */
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ /* Parsed option values and variables that are valid only during
+ actual scanning: */
+ SANE_Bool scanning;
+ SANE_Int pass;
+ SANE_Parameters params;
+
+ AgfaFocus_Scanner_Mode mode;
+
+ SANE_Int image_composition;
+ SANE_Int bpp;
+ SANE_Int halftone;
+ SANE_Int original;
+ SANE_Int exposure;
+ SANE_Int r_att;
+ SANE_Int g_att;
+ SANE_Int b_att;
+ SANE_Int tonecurve;
+ SANE_Int quality;
+ SANE_Bool edge;
+ SANE_Bool lin_log;
+
+ int lines_available; /* Lines in scanner memory */
+
+ int fd; /* SCSI filedescriptor */
+ SANE_Pid reader_pid; /* process id of reader */
+ int pipe; /* pipe to reader process */
+ int reader_pipe; /* pipe from reader process */
+
+ /* scanner dependent/low-level state: */
+ AgfaFocus_Device *hw;
+ }
+AgfaFocus_Scanner;
+
+#endif /* agfafocus_h */
diff --git a/backend/apple.c b/backend/apple.c
new file mode 100644
index 0000000..c806983
--- /dev/null
+++ b/backend/apple.c
@@ -0,0 +1,2690 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998 Milon Firikis based on David Mosberger-Tang previous
+ Work on mustek.c file from the SANE package.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Apple flatbed scanners. */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+
+/* SCSI commands that the Apple scanners understand: */
+#define APPLE_SCSI_TEST_UNIT_READY 0x00
+#define APPLE_SCSI_REQUEST_SENSE 0x03
+#define APPLE_SCSI_INQUIRY 0x12
+#define APPLE_SCSI_MODE_SELECT 0x15
+#define APPLE_SCSI_RESERVE 0x16
+#define APPLE_SCSI_RELEASE 0x17
+#define APPLE_SCSI_START 0x1b
+#define APPLE_SCSI_AREA_AND_WINDOWS 0x24
+#define APPLE_SCSI_READ_SCANNED_DATA 0x28
+#define APPLE_SCSI_GET_DATA_STATUS 0x34
+
+
+#define INQ_LEN 0x60
+
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+#define XQSTEP(XRES,BPP) (SANE_Int) (((double) (8*1200)) / ((double) (XRES*BPP)))
+#define YQSTEP(YRES) (SANE_Int) (((double) (1200)) / ((double) (YRES)))
+
+
+/* Very low info, Apple Scanners only */
+
+/* TODO: Ok I admit it. I am not so clever to do this operations with bitwised
+ operators. Sorry. */
+
+#define STORE8(p,v) \
+ { \
+ *(p)=(v); \
+ }
+
+#define STORE16(p,v) \
+ { \
+ *(p)=(v)/256; \
+ *(p+1)=(v-*(p)*256); \
+ }
+
+#define STORE24(p,v) \
+ { \
+ *(p)=(v)/65536; \
+ *(p+1)=(v-*(p)*65536)/256; \
+ *(p+2)=(v-*(p)*65536-*(p+1)*256); \
+ }
+
+
+#define STORE32(p,v) \
+ { \
+ *(p)=(v)/16777216; \
+ *(p+1)=(v-*(p)*16777216)/65536; \
+ *(p+2)=(v-*(p)*16777216-*(p+1)*65536)/256; \
+ *(p+3)=(v-*(p)*16777216-*(p+1)*65536-*(p+2)*256);\
+ }
+
+#define READ24(p) *(p)*65536 + *(p+1)*256 + *(p+2)
+
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define APPLE_CONFIG_FILE "apple.conf"
+
+#include "apple.h"
+
+
+static int num_devices;
+static Apple_Device *first_dev;
+static Apple_Scanner *first_handle;
+
+
+static SANE_String_Const mode_list[6];
+
+static SANE_String_Const SupportedModel[] =
+{
+"3",
+"AppleScanner 4bit, 16 Shades of Gray",
+"OneScanner 8bit, 256 Shades of Gray",
+"ColorOneScanner, RGB color 8bit per band",
+NULL
+};
+
+static const SANE_String_Const graymap_list[] =
+{
+ "dark", "normal", "light",
+ 0
+};
+
+#if 0
+static const SANE_Int resbit4_list[] =
+{
+ 5,
+ 75, 100, 150, 200, 300
+};
+
+static const SANE_Int resbit1_list[] =
+{
+ 17,
+ 75, 90, 100, 120, 135, 150, 165, 180, 195,
+ 200, 210, 225, 240, 255, 270, 285, 300
+};
+#endif
+
+static const SANE_Int resbit_list[] =
+{
+ 5,
+ 75, 100, 150, 200, 300
+};
+
+static const SANE_String_Const speed_list[] =
+{
+ "normal", "high", "high wo H/S",
+ 0
+};
+
+static SANE_String_Const halftone_pattern_list[6];
+
+static const SANE_String_Const color_sensor_list[] =
+{
+ "All", "Red", "Green", "Blue",
+ 0
+};
+
+/* NOTE: This is used for Brightness, Contrast, Threshold, AutoBackAdj
+ and 0 is the default value */
+static const SANE_Range byte_range =
+{
+ 1, 255, 1
+};
+
+static const SANE_Range u8_range =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+
+/* NOTE: However I can select from different lists during the hardware
+ probing time. */
+
+
+
+
+static const uint8_t inquiry[] =
+{
+ APPLE_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00
+};
+
+static const uint8_t test_unit_ready[] =
+{
+ APPLE_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+#if 0
+SANE_Int
+xqstep (unsigned int Xres, unsigned int bpp)
+{
+ return (SANE_Int) ((double) (8 * 1200)) / ((double) (Xres * bpp));
+}
+
+
+SANE_Int
+yqstep (unsigned int Yres, unsigned int bpp)
+{
+ return (SANE_Int) ((double) (1200)) / ((double) (Yres));
+}
+#endif
+
+
+
+/* The functions below return the quantized value of x,y in scanners dots
+ aka 1/1200 of an inch */
+
+static SANE_Int
+xquant (double x, unsigned int Xres, unsigned int bpp, int dir)
+{
+ double tmp;
+ unsigned int t;
+
+ tmp = (double) x *Xres * bpp / (double) 8;
+ t = (unsigned int) tmp;
+
+ if (tmp - ((double) t) >= 0.1)
+ if (dir)
+ t++;;
+
+ return t * 8 * 1200 / (Xres * bpp);
+}
+
+
+
+static SANE_Int
+yquant (double y, unsigned int Yres, int dir)
+{
+ double tmp;
+ unsigned int t;
+
+ tmp = (double) y *Yres;
+ t = (unsigned int) tmp;
+
+ if (tmp - ((double) t) >= 0.1)
+ if (dir)
+ t++;;
+
+ return t * 1200 / Yres;
+}
+
+static SANE_Status
+wait_ready (int fd)
+{
+#define MAX_WAITING_TIME 60 /* one minute, at most */
+ struct timeval now, start;
+ SANE_Status status;
+
+#ifdef NEUTRALIZE_BACKEND
+return SANE_STATUS_GOOD;
+#else
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG (USER_MESSAGE, "wait_ready: sending TEST_UNIT_READY\n");
+
+ status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0);
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (ERROR_MESSAGE, "wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (ERROR_MESSAGE, "wait_ready: timed out after %lu seconds\n",
+ (u_long) now.tv_sec - start.tv_sec);
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+#endif /* NEUTRALIZE_BACKEND */
+}
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ scsi_fd = scsi_fd; /* silence gcc */
+ arg = arg; /* silence gcc */
+
+ switch (result[2] & 0x0F)
+ {
+ case 0:
+ DBG (USER_MESSAGE, "Sense: No sense Error\n");
+ return SANE_STATUS_GOOD;
+ case 2:
+ DBG (ERROR_MESSAGE, "Sense: Scanner not ready\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ case 4:
+ DBG (ERROR_MESSAGE, "Sense: Hardware Error. Read more...\n");
+ return SANE_STATUS_IO_ERROR;
+ case 5:
+ DBG (ERROR_MESSAGE, "Sense: Illegall request\n");
+ return SANE_STATUS_UNSUPPORTED;
+ case 6:
+ DBG (ERROR_MESSAGE, "Sense: Unit Attention (Wait until scanner "
+ "boots)\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ case 9:
+ DBG (ERROR_MESSAGE, "Sense: Vendor Unique. Read more...\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG (ERROR_MESSAGE, "Sense: Unknown Sense Key. Read more...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+request_sense (Apple_Scanner * s)
+{
+ uint8_t cmd[6];
+ uint8_t result[22];
+ size_t size = sizeof (result);
+ SANE_Status status;
+
+ memset (cmd, 0, sizeof (cmd));
+ memset (result, 0, sizeof (result));
+
+#ifdef NEUTRALIZE_BACKEND
+return SANE_STATUS_GOOD;
+#else
+
+ cmd[0] = APPLE_SCSI_REQUEST_SENSE;
+ STORE8 (cmd + 4, sizeof (result));
+ sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), result, &size);
+
+ if (result[7] != 14)
+ {
+ DBG (ERROR_MESSAGE, "Additional Length %u\n", (unsigned int) result[7]);
+ status = SANE_STATUS_IO_ERROR;
+ }
+
+
+ status = sense_handler (s->fd, result, NULL);
+ if (status == SANE_STATUS_IO_ERROR)
+ {
+
+/* Now we are checking for Harware and Vendor Unique Errors for all models */
+/* First check the common Error conditions */
+
+ if (result[18] & 0x80)
+ DBG (ERROR_MESSAGE, "Sense: Dim Light (output of lamp below 70%%).\n");
+
+ if (result[18] & 0x40)
+ DBG (ERROR_MESSAGE, "Sense: No Light at all.\n");
+
+ if (result[18] & 0x20)
+ DBG (ERROR_MESSAGE, "Sense: No Home.\n");
+
+ if (result[18] & 0x10)
+ DBG (ERROR_MESSAGE, "Sense: No Limit. Tried to scan out of range.\n");
+
+
+ switch (s->hw->ScannerModel)
+ {
+ case APPLESCANNER:
+ if (result[18] & 0x08)
+ DBG (ERROR_MESSAGE, "Sense: Shade Error. Failed Calibration.\n");
+ if (result[18] & 0x04)
+ DBG (ERROR_MESSAGE, "Sense: ROM Error.\n");
+ if (result[18] & 0x02)
+ DBG (ERROR_MESSAGE, "Sense: RAM Error.\n");
+ if (result[18] & 0x01)
+ DBG (ERROR_MESSAGE, "Sense: CPU Error.\n");
+ if (result[19] & 0x80)
+ DBG (ERROR_MESSAGE, "Sense: DIPP Error.\n");
+ if (result[19] & 0x40)
+ DBG (ERROR_MESSAGE, "Sense: DMA Error.\n");
+ if (result[19] & 0x20)
+ DBG (ERROR_MESSAGE, "Sense: GA1 Error.\n");
+ break;
+ case ONESCANNER:
+ if (result[18] & 0x08)
+ DBG (ERROR_MESSAGE, "Sense: CCD clock generator failed.\n");
+ if (result[18] & 0x04)
+ DBG (ERROR_MESSAGE, "Sense: LRAM (Line RAM) Error.\n");
+ if (result[18] & 0x02)
+ DBG (ERROR_MESSAGE, "Sense: CRAM (Correction RAM) Error.\n");
+ if (result[18] & 0x01)
+ DBG (ERROR_MESSAGE, "Sense: ROM Error.\n");
+ if (result[19] & 0x08)
+ DBG (ERROR_MESSAGE, "Sense: SRAM Error.\n");
+ if (result[19] & 0x04)
+ DBG (ERROR_MESSAGE, "Sense: CPU Error.\n");
+ break;
+ case COLORONESCANNER:
+ if (result[18] & 0x08)
+ DBG (ERROR_MESSAGE, "Sense: Calibration cirquit cannot "
+ "support normal shading.\n");
+ if (result[18] & 0x04)
+ DBG (ERROR_MESSAGE, "Sense: PSRAM (Correction RAM) Error.\n");
+ if (result[18] & 0x02)
+ DBG (ERROR_MESSAGE, "Sense: SRAM Error.\n");
+ if (result[18] & 0x01)
+ DBG (ERROR_MESSAGE, "Sense: ROM Error.\n");
+ if (result[19] & 0x10)
+ DBG (ERROR_MESSAGE, "Sense: ICP (CPU) Error.\n");
+ if (result[19] & 0x02)
+ DBG (ERROR_MESSAGE, "Sense: Over light. (Too bright lamp ?).\n");
+ break;
+ default:
+ DBG (ERROR_MESSAGE,
+ "Sense: Unselected Scanner model. Please report this.\n");
+ break;
+ }
+ }
+
+ DBG (USER_MESSAGE, "Sense: Optical gain %u.\n", (unsigned int) result[20]);
+ return status;
+#endif /* NEUTRALIZE_BACKEND */
+}
+
+
+
+
+
+static SANE_Status
+attach (const char *devname, Apple_Device ** devp, int may_wait)
+{
+ char result[INQ_LEN];
+ const char *model_name = result + 44;
+ int fd, apple_scanner, fw_revision;
+ Apple_Device *dev;
+ SANE_Status status;
+ size_t size;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (USER_MESSAGE, "attach: opening %s\n", devname);
+
+#ifdef NEUTRALIZE_BACKEND
+result[0]=0x06;
+strcpy(result + 8, "APPLE ");
+
+if (APPLE_MODEL_SELECT==APPLESCANNER)
+ strcpy(result + 16, "SCANNER A9M0337 ");
+if (APPLE_MODEL_SELECT==ONESCANNER)
+ strcpy(result + 16, "SCANNER II ");
+if (APPLE_MODEL_SELECT==COLORONESCANNER)
+ strcpy(result + 16, "SCANNER III ");
+
+#else
+ status = sanei_scsi_open (devname, &fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "attach: open failed (%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (may_wait)
+ wait_ready (fd);
+
+ DBG (USER_MESSAGE, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "attach: inquiry failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ status = wait_ready (fd);
+ sanei_scsi_close (fd);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+#endif /* NEUTRALIZE_BACKEND */
+
+ /* check for old format: */
+ apple_scanner = (strncmp (result + 8, "APPLE ", 8) == 0);
+ model_name = result + 16;
+
+ apple_scanner = apple_scanner && (result[0] == 0x06);
+
+ if (!apple_scanner)
+ {
+ DBG (ERROR_MESSAGE, "attach: device doesn't look like an Apple scanner"
+ "(result[0]=%#02x)\n", result[0]);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* get firmware revision as BCD number: */
+ fw_revision =
+ (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - '0');
+ DBG (USER_MESSAGE, "attach: firmware revision %d.%02x\n",
+ fw_revision >> 8, fw_revision & 0xff);
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor = "Apple";
+ dev->sane.model = strndup (model_name, 16);
+ dev->sane.type = "flatbed scanner";
+
+ dev->x_range.min = 0;
+ dev->x_range.max = SANE_FIX (8.51 * MM_PER_INCH);
+ dev->x_range.quant = 0;
+
+ dev->y_range.min = 0;
+ dev->y_range.max = SANE_FIX (14.0 * MM_PER_INCH);
+ dev->y_range.quant = 0;
+
+ dev->MaxHeight = 16800;
+
+ if (strncmp (model_name, "SCANNER A9M0337 ", 16) == 0)
+ {
+ dev->ScannerModel = APPLESCANNER;
+ dev->dpi_range.min = SANE_FIX (75);
+ dev->dpi_range.max = SANE_FIX (300);
+ dev->dpi_range.quant = SANE_FIX (1);
+ dev->MaxWidth = 10208;
+ }
+ else if (strncmp (model_name, "SCANNER II ", 16) == 0)
+ {
+ dev->ScannerModel = ONESCANNER;
+ dev->dpi_range.min = SANE_FIX (72);
+ dev->dpi_range.max = SANE_FIX (300);
+ dev->dpi_range.quant = SANE_FIX (1);
+ dev->MaxWidth = 10200;
+ }
+ else if (strncmp (model_name, "SCANNER III ", 16) == 0)
+ {
+ dev->ScannerModel = COLORONESCANNER;
+ dev->dpi_range.min = SANE_FIX (72);
+ dev->dpi_range.max = SANE_FIX (300);
+ dev->dpi_range.quant = SANE_FIX (1);
+ dev->MaxWidth = 10200;
+ }
+ else
+ {
+ DBG (ERROR_MESSAGE,
+ "attach: Cannot found Apple scanner in the neighborhood\n");
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (USER_MESSAGE, "attach: found Apple scanner model %s (%s)\n",
+ dev->sane.model, dev->sane.type);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static SANE_Status
+scan_area_and_windows (Apple_Scanner * s)
+{
+ uint8_t cmd[10 + 8 + 42];
+#define CMD cmd + 0
+#define WH cmd + 10
+#define WP WH + 8
+
+#ifdef NEUTRALIZE_BACKEND
+return SANE_STATUS_GOOD;
+#else
+
+ /* setup SCSI command (except length): */
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = APPLE_SCSI_AREA_AND_WINDOWS;
+
+
+ if (s->hw->ScannerModel == COLORONESCANNER)
+ {
+ STORE24 (CMD + 6, 50);
+ STORE16 (WH + 6, 42);
+ }
+ else
+ {
+ STORE24 (CMD + 6, 48);
+ STORE16 (WH + 6, 40);
+ }
+
+/* Store resolution. First X, the Y */
+
+ STORE16 (WP + 2, s->val[OPT_RESOLUTION].w);
+ STORE16 (WP + 4, s->val[OPT_RESOLUTION].w);
+
+/* Now the Scanner Window in Scanner Parameters */
+
+ STORE32 (WP + 6, s->ULx);
+ STORE32 (WP + 10, s->ULy);
+ STORE32 (WP + 14, s->Width);
+ STORE32 (WP + 18, s->Height);
+
+/* Now The Enhansment Group */
+
+ STORE8 (WP + 22, s->val[OPT_BRIGHTNESS].w);
+ STORE8 (WP + 23, s->val[OPT_THRESHOLD].w);
+ STORE8 (WP + 24, s->val[OPT_CONTRAST].w);
+
+/* The Mode */
+
+ if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART))
+ STORE8 (WP + 25, 0)
+ else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_HALFTONE))
+ STORE8 (WP + 25, 1)
+ else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) ||
+ !strcmp (s->val[OPT_MODE].s, "Gray16"))
+ STORE8 (WP + 25, 2)
+ else if (!strcmp (s->val[OPT_MODE].s, "BiColor"))
+ STORE8 (WP + 25, 3)
+ else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR))
+ STORE8 (WP + 25, 5)
+ else
+ {
+ DBG (ERROR_MESSAGE, "Cannot much mode %s\n", s->val[OPT_MODE].s);
+ return SANE_STATUS_INVAL;
+ }
+
+ STORE8 (WP + 26, s->bpp)
+
+/* HalfTone */
+if (s->hw->ScannerModel != COLORONESCANNER)
+ {
+ if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "spiral4x4"))
+ STORE16 (WP + 27, 0)
+ else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "bayer4x4"))
+ STORE16 (WP + 27, 1)
+ else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "download"))
+ STORE16 (WP + 27, 1)
+ else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "spiral8x8"))
+ STORE16 (WP + 27, 3)
+ else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "bayer8x8"))
+ STORE16 (WP + 27, 4)
+ else
+ {
+ DBG (ERROR_MESSAGE, "Cannot much haftone pattern %s\n",
+ s->val[OPT_HALFTONE_PATTERN].s);
+ return SANE_STATUS_INVAL;
+ }
+ }
+/* Padding Type */
+ STORE8 (WP + 29, 3);
+
+ if (s->hw->ScannerModel == COLORONESCANNER)
+ {
+ if (s->val[OPT_VOLT_REF].w)
+ {
+ STORE8(WP+40,s->val[OPT_VOLT_REF_TOP].w);
+ STORE8(WP+41,s->val[OPT_VOLT_REF_BOTTOM].w);
+ }
+ else
+ {
+ STORE8(WP+40,0);
+ STORE8(WP+41,0);
+ }
+ return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
+ }
+ else
+ return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd) - 2, 0, 0);
+
+#endif /* NEUTRALIZE_BACKEND */
+}
+
+static SANE_Status
+mode_select (Apple_Scanner * s)
+{
+ uint8_t cmd[6 + 12];
+#define CMD cmd + 0
+#define PP cmd + 6
+
+ /* setup SCSI command (except length): */
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = APPLE_SCSI_MODE_SELECT;
+
+/* Apple Hardware Magic */
+ STORE8 (CMD + 1, 0x10);
+
+/* Parameter list length */
+ STORE8 (CMD + 4, 12);
+
+ STORE8 (PP + 5, 6);
+
+ if (s->val[OPT_LAMP].w) *(PP+8) |= 1;
+
+ switch (s->hw->ScannerModel)
+ {
+ case APPLESCANNER:
+ if (!strcmp (s->val[OPT_GRAYMAP].s, "dark"))
+ STORE8 (PP + 6, 0)
+ else if (!strcmp (s->val[OPT_GRAYMAP].s, "normal"))
+ STORE8 (PP + 6, 1)
+ else if (!strcmp (s->val[OPT_GRAYMAP].s, "light"))
+ STORE8 (PP + 6, 2)
+ else
+ {
+ DBG (ERROR_MESSAGE, "Cannot mach GrayMap Function %s\n",
+ s->val[OPT_GRAYMAP].s);
+ return SANE_STATUS_INVAL;
+ }
+ /* And the auto background threshold */
+ STORE8 (PP + 7, s->val[OPT_AUTOBACKGROUND_THRESHOLD].w)
+ break;
+ case ONESCANNER:
+ if (s->val[OPT_LED].w) *(PP+7) |= 4;
+ if (s->val[OPT_CCD].w) *(PP+8) |= 2;
+ if (!strcmp (s->val[OPT_SPEED].s, "high"))
+ *(PP+8) |= 4;
+ else if (!strcmp (s->val[OPT_SPEED].s, "high wo H/S"))
+ *(PP+8) |= 8;
+ else if (!strcmp (s->val[OPT_SPEED].s, "normal"))
+ { /* Do nothing. Zeros are great */}
+ else
+ {
+ DBG (ERROR_MESSAGE, "Cannot mach speed selection %s\n",
+ s->val[OPT_SPEED].s);
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case COLORONESCANNER:
+ if (s->val[OPT_LED].w) *(PP+7) |= 4;
+ if (!s->val[OPT_CUSTOM_GAMMA].w) *(PP+7) |= 2;
+ if (!s->val[OPT_CUSTOM_CCT].w) *(PP+7) |= 1;
+ if (s->val[OPT_MTF_CIRCUIT].w) *(PP+8) |= 16;
+ if (s->val[OPT_ICP].w) *(PP+8) |= 8;
+ if (s->val[OPT_POLARITY].w) *(PP+8) |= 4;
+ if (s->val[OPT_CCD].w) *(PP+8) |= 2;
+
+ if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "All"))
+ STORE8 (PP + 9, 0)
+ else if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "Red"))
+ STORE8 (PP + 9, 1)
+ else if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "Green"))
+ STORE8 (PP + 9, 2)
+ else if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "Blue"))
+ STORE8 (PP + 9, 3)
+ else
+ {
+ DBG (ERROR_MESSAGE, "Cannot mach Color Sensor for gray scans %s\n",
+ s->val[OPT_COLOR_SENSOR].s);
+ return SANE_STATUS_INVAL;
+ }
+
+ break;
+ default:
+ DBG(ERROR_MESSAGE,"Bad Scanner.\n");
+ break;
+ }
+
+#ifdef NEUTRALIZE_BACKEND
+ return SANE_STATUS_GOOD;
+#else
+ return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
+#endif /* NEUTRALIZE_BACKEND */
+
+}
+
+static SANE_Status
+start_scan (Apple_Scanner * s)
+{
+ SANE_Status status;
+ uint8_t start[7];
+
+
+ memset (start, 0, sizeof (start));
+ start[0] = APPLE_SCSI_START;
+ start[4] = 1;
+
+ switch (s->hw->ScannerModel)
+ {
+ case APPLESCANNER:
+ if (s->val[OPT_WAIT].w) start[5]=0x80;
+ /* NOT TODO NoHome */
+ break;
+ case ONESCANNER:
+ if (!s->val[OPT_CALIBRATE].w) start[5]=0x20;
+ break;
+ case COLORONESCANNER:
+ break;
+ default:
+ DBG(ERROR_MESSAGE,"Bad Scanner.\n");
+ break;
+ }
+
+
+#ifdef NEUTRALIZE_BACKEND
+ return SANE_STATUS_GOOD;
+#else
+ status = sanei_scsi_cmd (s->fd, start, sizeof (start), 0, 0);
+ return status;
+#endif /* NEUTRALIZE_BACKEND */
+}
+
+static SANE_Status
+calc_parameters (Apple_Scanner * s)
+{
+ SANE_String val = s->val[OPT_MODE].s;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool OutOfRangeX, OutOfRangeY, Protect = SANE_TRUE;
+ SANE_Int xqstep, yqstep;
+
+ DBG (FLOW_CONTROL, "Entering calc_parameters\n");
+
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ s->params.last_frame = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->bpp = 1;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ s->params.last_frame = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->bpp = 1;
+ }
+ else if (!strcmp (val, "Gray16"))
+ {
+ s->params.last_frame = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 8;
+ s->bpp = 4;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ s->params.last_frame = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 8;
+ s->bpp = 8;
+ }
+ else if (!strcmp (val, "BiColor"))
+ {
+ s->params.last_frame = SANE_TRUE;
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 24;
+ s->bpp = 3;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR))
+ {
+ s->params.last_frame = SANE_FALSE;
+ s->params.format = SANE_FRAME_RED;
+ s->params.depth = 24;
+ s->bpp = 24;
+ }
+ else
+ {
+ DBG (ERROR_MESSAGE, "calc_parameters: Invalid mode %s\n", (char *) val);
+ status = SANE_STATUS_INVAL;
+ }
+
+ s->ulx = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH;
+ s->uly = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH;
+ s->wx = SANE_UNFIX (s->val[OPT_BR_X].w) / MM_PER_INCH - s->ulx;
+ s->wy = SANE_UNFIX (s->val[OPT_BR_Y].w) / MM_PER_INCH - s->uly;
+
+ DBG (VARIABLE_CONTROL, "Desired [%g,%g] to +[%g,%g]\n",
+ s->ulx, s->uly, s->wx, s->wy);
+
+ xqstep = XQSTEP (s->val[OPT_RESOLUTION].w, s->bpp);
+ yqstep = YQSTEP (s->val[OPT_RESOLUTION].w);
+
+ DBG (VARIABLE_CONTROL, "Quantization steps of [%u,%u].\n", xqstep, yqstep);
+
+ s->ULx = xquant (s->ulx, s->val[OPT_RESOLUTION].w, s->bpp, 0);
+ s->Width = xquant (s->wx, s->val[OPT_RESOLUTION].w, s->bpp, 1);
+ s->ULy = yquant (s->uly, s->val[OPT_RESOLUTION].w, 0);
+ s->Height = yquant (s->wy, s->val[OPT_RESOLUTION].w, 1);
+
+ DBG (VARIABLE_CONTROL, "Scanner [%u,%u] to +[%u,%u]\n",
+ s->ULx, s->ULy, s->Width, s->Height);
+
+ do
+ {
+
+ OutOfRangeX = SANE_FALSE;
+ OutOfRangeY = SANE_FALSE;
+
+ if (s->ULx + s->Width > s->hw->MaxWidth)
+ {
+ OutOfRangeX = SANE_TRUE;
+ Protect = SANE_FALSE;
+ s->Width -= xqstep;
+ }
+
+ if (s->ULy + s->Height > s->hw->MaxHeight)
+ {
+ OutOfRangeY = SANE_TRUE;
+ Protect = SANE_FALSE;
+ s->Height -= yqstep;
+ }
+
+ DBG (VARIABLE_CONTROL, "Adapting to [%u,%u] to +[%u,%u]\n",
+ s->ULx, s->ULy, s->Width, s->Height);
+
+ }
+ while (OutOfRangeX || OutOfRangeY);
+
+ s->ulx = (double) s->ULx / 1200;
+ s->uly = (double) s->ULy / 1200;
+ s->wx = (double) s->Width / 1200;
+ s->wy = (double) s->Height / 1200;
+
+
+ DBG (VARIABLE_CONTROL, "Real [%g,%g] to +[%g,%g]\n",
+ s->ulx, s->uly, s->wx, s->wy);
+
+
+/*
+
+ TODO: Remove this ugly hack (Protect). Read to learn why!
+
+ NOTE: I hate the Fixed Sane type. This type gave me a terrible
+ headache and a difficult bug to find out. The xscanimage frontend
+ was looping and segfaulting all the time with random order. The
+ problem was the following:
+
+ * You select new let's say BR_X
+ * sane_control_option returns info inexact (always for BR_X) but
+ does not modify val because it fits under the constrained
+ quantization.
+
+ Hm... Well sane_control doesn't change the (double) value of val
+ but the Fixed interpatation may have been change (by 1 or something
+ small).
+
+ So now we should protect the val if the change is smaller than the
+ quantization step or better under the SANE_[UN]FIX accuracy.
+
+ Looks like for two distinct val (Fixed) values we get the same
+ double. How come ?
+
+ This hack fixed the looping situtation. Unfortunately SIGSEGV
+ remains when you touch the slice bars (thouhg not all the
+ time). But it's OK if you select scan_area from the preview window
+ (cool).
+
+ */
+
+ if (!Protect)
+ {
+ s->val[OPT_TL_X].w = SANE_FIX (s->ulx * MM_PER_INCH);
+ s->val[OPT_TL_Y].w = SANE_FIX (s->uly * MM_PER_INCH);
+ s->val[OPT_BR_X].w = SANE_FIX ((s->ulx + s->wx) * MM_PER_INCH);
+ s->val[OPT_BR_Y].w = SANE_FIX ((s->uly + s->wy) * MM_PER_INCH);
+ }
+ else
+ DBG (VARIABLE_CONTROL, "Not adapted. Protecting\n");
+
+
+ DBG (VARIABLE_CONTROL, "GUI [%g,%g] to [%g,%g]\n",
+ SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_TL_Y].w),
+ SANE_UNFIX (s->val[OPT_BR_X].w),
+ SANE_UNFIX (s->val[OPT_BR_Y].w));
+
+ /* NOTE: remember that AppleScanners quantize the scan area to be a
+ byte multiple */
+
+
+ s->params.pixels_per_line = s->Width * s->val[OPT_RESOLUTION].w / 1200;
+ s->params.lines = s->Height * s->val[OPT_RESOLUTION].w / 1200;
+ s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8;
+
+
+ DBG (VARIABLE_CONTROL, "format=%d\n", s->params.format);
+ DBG (VARIABLE_CONTROL, "last_frame=%d\n", s->params.last_frame);
+ DBG (VARIABLE_CONTROL, "lines=%d\n", s->params.lines);
+ DBG (VARIABLE_CONTROL, "depth=%d (%d)\n", s->params.depth, s->bpp);
+ DBG (VARIABLE_CONTROL, "pixels_per_line=%d\n", s->params.pixels_per_line);
+ DBG (VARIABLE_CONTROL, "bytes_per_line=%d\n", s->params.bytes_per_line);
+ DBG (VARIABLE_CONTROL, "Pixels %dx%dx%d\n",
+ s->params.pixels_per_line, s->params.lines, 1 << s->params.depth);
+
+ DBG (FLOW_CONTROL, "Leaving calc_parameters\n");
+ return status;
+}
+
+
+
+static SANE_Status
+gamma_update(SANE_Handle handle)
+{
+Apple_Scanner *s = handle;
+
+
+if (s->hw->ScannerModel == COLORONESCANNER)
+ {
+ if ( !strcmp(s->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_GRAY) ||
+ !strcmp(s->val[OPT_MODE].s,"Gray16") )
+ {
+ ENABLE (OPT_CUSTOM_GAMMA);
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ ENABLE (OPT_DOWNLOAD_GAMMA);
+ if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"All"))
+ {
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"Red"))
+ {
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE(OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"Green"))
+ {
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"Blue"))
+ {
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ }
+ else /* Not custom gamma */
+ {
+ goto discustom;
+ }
+ }
+ else if (!strcmp(s->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_COLOR))
+ {
+ ENABLE (OPT_CUSTOM_GAMMA);
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ ENABLE (OPT_DOWNLOAD_GAMMA);
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ else /* Not custom gamma */
+ {
+ goto discustom;
+ }
+ }
+ else /* Not Gamma capable mode */
+ {
+ goto disall;
+ }
+ } /* Not Gamma capable Scanner */
+else
+ {
+disall:
+ DISABLE (OPT_CUSTOM_GAMMA);
+discustom:
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ DISABLE (OPT_DOWNLOAD_GAMMA);
+ }
+
+return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+mode_update (SANE_Handle handle, char *val)
+{
+ Apple_Scanner *s = handle;
+ SANE_Bool cct=SANE_FALSE;
+ SANE_Bool UseThreshold=SANE_FALSE;
+
+ DISABLE(OPT_COLOR_SENSOR);
+
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ if (s->hw->ScannerModel == APPLESCANNER)
+ ENABLE (OPT_AUTOBACKGROUND);
+ else
+ DISABLE (OPT_AUTOBACKGROUND);
+ DISABLE (OPT_HALFTONE_PATTERN);
+
+ UseThreshold=SANE_TRUE;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ DISABLE (OPT_AUTOBACKGROUND);
+ ENABLE (OPT_HALFTONE_PATTERN);
+ }
+ else if (!strcmp (val, "Gray16") || !strcmp (val, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ DISABLE (OPT_AUTOBACKGROUND);
+ DISABLE (OPT_HALFTONE_PATTERN);
+ if (s->hw->ScannerModel == COLORONESCANNER)
+ ENABLE(OPT_COLOR_SENSOR);
+
+ } /* End of Gray */
+ else if (!strcmp (val, "BiColor"))
+ {
+ DISABLE (OPT_AUTOBACKGROUND);
+ DISABLE (OPT_HALFTONE_PATTERN);
+ UseThreshold=SANE_TRUE;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR))
+ {
+ DISABLE (OPT_AUTOBACKGROUND);
+ DISABLE (OPT_HALFTONE_PATTERN);
+ cct=SANE_TRUE;
+ }
+ else
+ {
+ DBG (ERROR_MESSAGE, "Invalid mode %s\n", (char *) val);
+ return SANE_STATUS_INVAL;
+ }
+
+/* Second hand dependancies of mode option */
+/* Looks like code doubling */
+
+
+ if (UseThreshold)
+ {
+ DISABLE (OPT_BRIGHTNESS);
+ DISABLE (OPT_CONTRAST);
+ DISABLE (OPT_VOLT_REF);
+ DISABLE (OPT_VOLT_REF_TOP);
+ DISABLE (OPT_VOLT_REF_BOTTOM);
+
+ if (IS_ACTIVE (OPT_AUTOBACKGROUND) && s->val[OPT_AUTOBACKGROUND].w)
+ {
+ DISABLE (OPT_THRESHOLD);
+ ENABLE (OPT_AUTOBACKGROUND_THRESHOLD);
+ }
+ else
+ {
+ ENABLE (OPT_THRESHOLD);
+ DISABLE (OPT_AUTOBACKGROUND_THRESHOLD);
+ }
+ }
+ else
+ {
+ DISABLE (OPT_THRESHOLD);
+ DISABLE (OPT_AUTOBACKGROUND_THRESHOLD);
+
+ if (s->hw->ScannerModel == COLORONESCANNER)
+ {
+ ENABLE (OPT_VOLT_REF);
+ if (s->val[OPT_VOLT_REF].w)
+ {
+ ENABLE (OPT_VOLT_REF_TOP);
+ ENABLE (OPT_VOLT_REF_BOTTOM);
+ DISABLE (OPT_BRIGHTNESS);
+ DISABLE (OPT_CONTRAST);
+ }
+ else
+ {
+ DISABLE (OPT_VOLT_REF_TOP);
+ DISABLE (OPT_VOLT_REF_BOTTOM);
+ ENABLE (OPT_BRIGHTNESS);
+ ENABLE (OPT_CONTRAST);
+ }
+ }
+ else
+ {
+ ENABLE (OPT_BRIGHTNESS);
+ ENABLE (OPT_CONTRAST);
+ }
+ }
+
+
+ if (IS_ACTIVE (OPT_HALFTONE_PATTERN) &&
+ !strcmp (s->val[OPT_HALFTONE_PATTERN].s, "download"))
+ ENABLE (OPT_HALFTONE_FILE);
+ else
+ DISABLE (OPT_HALFTONE_FILE);
+
+ if (cct)
+ ENABLE (OPT_CUSTOM_CCT);
+ else
+ DISABLE (OPT_CUSTOM_CCT);
+
+ if (cct && s->val[OPT_CUSTOM_CCT].w)
+ {
+ ENABLE(OPT_CCT);
+ ENABLE(OPT_DOWNLOAD_CCT);
+ }
+ else
+ {
+ DISABLE(OPT_CCT);
+ DISABLE(OPT_DOWNLOAD_CCT);
+ }
+
+
+ gamma_update (s);
+ calc_parameters (s);
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+
+
+static SANE_Status
+init_options (Apple_Scanner * s)
+{
+ int i;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* Hardware detect Information group: */
+
+ s->opt[OPT_HWDETECT_GROUP].title = "Hardware";
+ s->opt[OPT_HWDETECT_GROUP].desc = "Detected during hardware probing";
+ s->opt[OPT_HWDETECT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_HWDETECT_GROUP].cap = 0;
+ s->opt[OPT_HWDETECT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_MODEL].name = "model";
+ s->opt[OPT_MODEL].title = "Model";
+ s->opt[OPT_MODEL].desc = "Model and capabilities";
+ s->opt[OPT_MODEL].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODEL].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_MODEL].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_MODEL].size = max_string_size (SupportedModel);
+ s->val[OPT_MODEL].s = strdup (SupportedModel[s->hw->ScannerModel]);
+
+
+ /* "Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ halftone_pattern_list[0]="spiral4x4";
+ halftone_pattern_list[1]="bayer4x4";
+ halftone_pattern_list[2]="download";
+ halftone_pattern_list[3]=NULL;
+
+
+ switch (s->hw->ScannerModel)
+ {
+ case APPLESCANNER:
+ mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART;
+ mode_list[1]=SANE_VALUE_SCAN_MODE_HALFTONE;
+ mode_list[2]="Gray16";
+ mode_list[3]=NULL;
+ break;
+ case ONESCANNER:
+ mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART;
+ mode_list[1]=SANE_VALUE_SCAN_MODE_HALFTONE;
+ mode_list[2]="Gray16";
+ mode_list[3]=SANE_VALUE_SCAN_MODE_GRAY;
+ mode_list[4]=NULL;
+ halftone_pattern_list[3]="spiral8x8";
+ halftone_pattern_list[4]="bayer8x8";
+ halftone_pattern_list[5]=NULL;
+ break;
+ case COLORONESCANNER:
+ mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART;
+ mode_list[1]="Gray16";
+ mode_list[2]=SANE_VALUE_SCAN_MODE_GRAY;
+ mode_list[3]="BiColor";
+ mode_list[4]=SANE_VALUE_SCAN_MODE_COLOR;
+ mode_list[5]=NULL;
+ break;
+ default:
+ break;
+ }
+
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[0]);
+
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+/* TODO: Build the constraints on resolution in a smart way */
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = resbit_list;
+ s->val[OPT_RESOLUTION].w = resbit_list[1];
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range.max;
+
+
+ /* "Enhancement" group: */
+
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &byte_range;
+ s->val[OPT_BRIGHTNESS].w = 128;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST
+ " This option is active for halftone/Grayscale modes only.";
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &byte_range;
+ s->val[OPT_CONTRAST].w = 1;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &byte_range;
+ s->val[OPT_THRESHOLD].w = 128;
+
+ /* AppleScanner Only options */
+
+ /* GrayMap Enhance */
+ s->opt[OPT_GRAYMAP].name = "graymap";
+ s->opt[OPT_GRAYMAP].title = "GrayMap";
+ s->opt[OPT_GRAYMAP].desc = "Fixed Gamma Enhancing";
+ s->opt[OPT_GRAYMAP].type = SANE_TYPE_STRING;
+ s->opt[OPT_GRAYMAP].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ if (s->hw->ScannerModel != APPLESCANNER)
+ s->opt[OPT_GRAYMAP].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GRAYMAP].constraint.string_list = graymap_list;
+ s->opt[OPT_GRAYMAP].size = max_string_size (graymap_list);
+ s->val[OPT_GRAYMAP].s = strdup (graymap_list[1]);
+
+ /* Enable auto background adjustment */
+ s->opt[OPT_AUTOBACKGROUND].name = "abj";
+ s->opt[OPT_AUTOBACKGROUND].title = "Use Auto Background Adjustment";
+ s->opt[OPT_AUTOBACKGROUND].desc =
+ "Enables/Disables the Auto Background Adjustment feature";
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)
+ || (s->hw->ScannerModel != APPLESCANNER))
+ DISABLE (OPT_AUTOBACKGROUND);
+ s->opt[OPT_AUTOBACKGROUND].type = SANE_TYPE_BOOL;
+ s->val[OPT_AUTOBACKGROUND].w = SANE_FALSE;
+
+ /* auto background adjustment threshold */
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].name = "abjthreshold";
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].title = "Auto Background Adjustment Threshold";
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].desc = "Selects the automatically adjustable threshold";
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ if (!IS_ACTIVE (OPT_AUTOBACKGROUND) ||
+ s->val[OPT_AUTOBACKGROUND].w == SANE_FALSE)
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].constraint.range = &byte_range;
+ s->val[OPT_AUTOBACKGROUND_THRESHOLD].w = 64;
+
+
+ /* AppleScanner & OneScanner options */
+
+ /* Select HalfTone Pattern */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list);
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_AUTOMATIC;
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list;
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]);
+
+ if (s->hw->ScannerModel!=APPLESCANNER && s->hw->ScannerModel!=ONESCANNER)
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+
+ /* halftone pattern file */
+ s->opt[OPT_HALFTONE_FILE].name = "halftone-pattern-file";
+ s->opt[OPT_HALFTONE_FILE].title = "Halftone Pattern File";
+ s->opt[OPT_HALFTONE_FILE].desc =
+ "Download and use the specified file as halftone pattern";
+ s->opt[OPT_HALFTONE_FILE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_FILE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_FILE].size = 256;
+ s->val[OPT_HALFTONE_FILE].s = "halftone.pgm";
+
+ /* Use volt_ref */
+ s->opt[OPT_VOLT_REF].name = "volt-ref";
+ s->opt[OPT_VOLT_REF].title = "Volt Reference";
+ s->opt[OPT_VOLT_REF].desc ="It's brightness equivalant.";
+ s->opt[OPT_VOLT_REF].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_VOLT_REF].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_VOLT_REF].w = SANE_FALSE;
+
+ s->opt[OPT_VOLT_REF_TOP].name = "volt-ref-top";
+ s->opt[OPT_VOLT_REF_TOP].title = "Top Voltage Reference";
+ s->opt[OPT_VOLT_REF_TOP].desc = "I really do not know.";
+ s->opt[OPT_VOLT_REF_TOP].type = SANE_TYPE_INT;
+ s->opt[OPT_VOLT_REF_TOP].unit = SANE_UNIT_NONE;
+ if (s->hw->ScannerModel!=COLORONESCANNER || s->val[OPT_VOLT_REF].w==SANE_FALSE)
+ s->opt[OPT_VOLT_REF_TOP].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_VOLT_REF_TOP].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_VOLT_REF_TOP].constraint.range = &byte_range;
+ s->val[OPT_VOLT_REF_TOP].w = 255;
+
+ s->opt[OPT_VOLT_REF_BOTTOM].name = "volt-ref-bottom";
+ s->opt[OPT_VOLT_REF_BOTTOM].title = "Bottom Voltage Reference";
+ s->opt[OPT_VOLT_REF_BOTTOM].desc = "I really do not know.";
+ s->opt[OPT_VOLT_REF_BOTTOM].type = SANE_TYPE_INT;
+ s->opt[OPT_VOLT_REF_BOTTOM].unit = SANE_UNIT_NONE;
+ if (s->hw->ScannerModel!=COLORONESCANNER || s->val[OPT_VOLT_REF].w==SANE_FALSE)
+ s->opt[OPT_VOLT_REF_BOTTOM].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_VOLT_REF_BOTTOM].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_VOLT_REF_BOTTOM].constraint.range = &byte_range;
+ s->val[OPT_VOLT_REF_BOTTOM].w = 1;
+
+/* Misc Functions: Advanced */
+
+ s->opt[OPT_MISC_GROUP].title = "Miscallaneous";
+ s->opt[OPT_MISC_GROUP].desc = "";
+ s->opt[OPT_MISC_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MISC_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_MISC_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ /* Turn On lamp during scan: All scanners */
+ s->opt[OPT_LAMP].name = "lamp";
+ s->opt[OPT_LAMP].title = "Lamp";
+ s->opt[OPT_LAMP].desc = "Hold the lamp on during scans.";
+ s->opt[OPT_LAMP].type = SANE_TYPE_BOOL;
+ s->val[OPT_LAMP].w = SANE_FALSE;
+
+ /* AppleScanner Only options */
+
+ /* Wait for button to be pressed before scanning */
+ s->opt[OPT_WAIT].name = "wait";
+ s->opt[OPT_WAIT].title = "Wait";
+ s->opt[OPT_WAIT].desc = "You may issue the scan command but the actual "
+ "scan will not start unless you press the button in the front of the "
+ "scanner. It is usefull feature when you want to make a network scan (?) "
+ "In the mean time you may halt your computer waiting for the SCSI bus "
+ "to be free. If this happens just press the scanner button.";
+ s->opt[OPT_WAIT].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel != APPLESCANNER)
+ s->opt[OPT_WAIT].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_WAIT].w = SANE_FALSE;
+
+
+ /* OneScanner Only options */
+
+ /* Calibrate before scanning ? */
+ s->opt[OPT_CALIBRATE].name = "calibrate";
+ s->opt[OPT_CALIBRATE].title = "Calibrate";
+ s->opt[OPT_CALIBRATE].desc = "You may avoid the calibration before "
+ "scanning but this will lead you to lower image quality.";
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel != ONESCANNER)
+ s->opt[OPT_CALIBRATE].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CALIBRATE].w = SANE_TRUE;
+
+ /* speed */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ s->opt[OPT_SPEED].type = SANE_TYPE_STRING;
+ if (s->hw->ScannerModel != ONESCANNER)
+ s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SPEED].size = max_string_size (speed_list);
+ s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SPEED].constraint.string_list = speed_list;
+ s->val[OPT_SPEED].s = strdup (speed_list[0]);
+
+ /* OneScanner & ColorOneScanner (LED && CCD) */
+
+ /* LED ? */
+ s->opt[OPT_LED].name = "led";
+ s->opt[OPT_LED].title = "LED";
+ s->opt[OPT_LED].desc ="This option controls the setting of the ambler LED.";
+ s->opt[OPT_LED].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel!=ONESCANNER && s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_LED].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_LED].w = SANE_TRUE;
+
+ /* CCD Power ? */
+ s->opt[OPT_CCD].name = "ccd";
+ s->opt[OPT_CCD].title = "CCD Power";
+ s->opt[OPT_CCD].desc ="This option controls the power to the CCD array.";
+ s->opt[OPT_CCD].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel!=ONESCANNER && s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_CCD].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CCD].w = SANE_TRUE;
+
+ /* Use MTF Circuit */
+ s->opt[OPT_MTF_CIRCUIT].name = "mtf";
+ s->opt[OPT_MTF_CIRCUIT].title = "MTF Circuit";
+ s->opt[OPT_MTF_CIRCUIT].desc ="Turns the MTF (Modulation Transfer Function) "
+ "peaking circuit on or off.";
+ s->opt[OPT_MTF_CIRCUIT].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_MTF_CIRCUIT].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_MTF_CIRCUIT].w = SANE_TRUE;
+
+
+ /* Use ICP */
+ s->opt[OPT_ICP].name = "icp";
+ s->opt[OPT_ICP].title = "ICP";
+ s->opt[OPT_ICP].desc ="What is an ICP anyway?";
+ s->opt[OPT_ICP].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_ICP].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_ICP].w = SANE_TRUE;
+
+
+ /* Data Polarity */
+ s->opt[OPT_POLARITY].name = "polarity";
+ s->opt[OPT_POLARITY].title = "Data Polarity";
+ s->opt[OPT_POLARITY].desc = "Reverse black and white.";
+ s->opt[OPT_POLARITY].type = SANE_TYPE_BOOL;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_POLARITY].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_POLARITY].w = SANE_FALSE;
+
+
+/* Color Functions: Advanced */
+
+ s->opt[OPT_COLOR_GROUP].title = SANE_VALUE_SCAN_MODE_COLOR;
+ s->opt[OPT_COLOR_GROUP].desc = "";
+ s->opt[OPT_COLOR_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_COLOR_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_COLOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+#ifdef CALIBRATION_FUNCTIONALITY
+ /* OneScanner calibration vector */
+ s->opt[OPT_CALIBRATION_VECTOR].name = "calibration-vector";
+ s->opt[OPT_CALIBRATION_VECTOR].title = "Calibration Vector";
+ s->opt[OPT_CALIBRATION_VECTOR].desc = "Calibration vector for the CCD array.";
+ s->opt[OPT_CALIBRATION_VECTOR].type = SANE_TYPE_INT;
+ if (s->hw->ScannerModel!=ONESCANNER)
+ s->opt[OPT_CALIBRATION_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CALIBRATION_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATION_VECTOR].size = 2550 * sizeof (SANE_Word);
+ s->opt[OPT_CALIBRATION_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CALIBRATION_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_CALIBRATION_VECTOR].wa = s->calibration_vector;
+
+ /* ColorOneScanner calibration vector per band */
+ s->opt[OPT_CALIBRATION_VECTOR_RED].name = "calibration-vector-red";
+ s->opt[OPT_CALIBRATION_VECTOR_RED].title = "Calibration Vector for Red";
+ s->opt[OPT_CALIBRATION_VECTOR_RED].desc = "Calibration vector for the CCD array.";
+ s->opt[OPT_CALIBRATION_VECTOR_RED].type = SANE_TYPE_INT;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_CALIBRATION_VECTOR_RED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CALIBRATION_VECTOR_RED].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATION_VECTOR_RED].size = 2700 * sizeof (SANE_Word);
+ s->opt[OPT_CALIBRATION_VECTOR_RED].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CALIBRATION_VECTOR_RED].constraint.range = &u8_range;
+ s->val[OPT_CALIBRATION_VECTOR_RED].wa = s->calibration_vector_red;
+
+ /* ColorOneScanner calibration vector per band */
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].name = "calibration-vector-green";
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].title = "Calibration Vector for Green";
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].desc = "Calibration vector for the CCD array.";
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].type = SANE_TYPE_INT;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_CALIBRATION_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].size = 2700 * sizeof (SANE_Word);
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CALIBRATION_VECTOR_GREEN].constraint.range = &u8_range;
+ s->val[OPT_CALIBRATION_VECTOR_GREEN].wa = s->calibration_vector_green;
+
+ /* ColorOneScanner calibration vector per band */
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].name = "calibration-vector-blue";
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].title = "Calibration Vector for Blue";
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].desc = "Calibration vector for the CCD array.";
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].type = SANE_TYPE_INT;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].size = 2700 * sizeof (SANE_Word);
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CALIBRATION_VECTOR_BLUE].constraint.range = &u8_range;
+ s->val[OPT_CALIBRATION_VECTOR_BLUE].wa = s->calibration_vector_blue;
+#endif /* CALIBRATION_FUNCTIONALITY */
+
+ /* Action: Download calibration vector */
+ s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].name = "download-calibration";
+ s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].title = "Download Calibration Vector";
+ s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].desc = "Download calibration vector to scanner";
+ s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].type = SANE_TYPE_BUTTON;
+ if (s->hw->ScannerModel!=ONESCANNER && s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].cap |= SANE_CAP_INACTIVE;
+
+ /* custom-cct table */
+ s->opt[OPT_CUSTOM_CCT].name = "custom-cct";
+ s->opt[OPT_CUSTOM_CCT].title = "Use Custom CCT";
+ s->opt[OPT_CUSTOM_CCT].desc ="Determines whether a builtin "
+ "or a custom 3x3 Color Correction Table (CCT) should be used.";
+ s->opt[OPT_CUSTOM_CCT].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_CCT].cap |= SANE_CAP_INACTIVE;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_CUSTOM_CCT].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CUSTOM_CCT].w = SANE_FALSE;
+
+
+ /* CCT */
+ s->opt[OPT_CCT].name = "cct";
+ s->opt[OPT_CCT].title = "3x3 Color Correction Table";
+ s->opt[OPT_CCT].desc = "TODO: Color Correction is currently unsupported";
+ s->opt[OPT_CCT].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT].size = 9 * sizeof (SANE_Word);
+ s->opt[OPT_CCT].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT].constraint.range = &u8_range;
+ s->val[OPT_CCT].wa = s->cct3x3;
+
+
+ /* Action: custom 3x3 color correction table */
+ s->opt[OPT_DOWNLOAD_CCT].name = "download-3x3";
+ s->opt[OPT_DOWNLOAD_CCT].title = "Download 3x3 CCT";
+ s->opt[OPT_DOWNLOAD_CCT].desc = "Download 3x3 color correction table";
+ s->opt[OPT_DOWNLOAD_CCT].type = SANE_TYPE_BUTTON;
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_DOWNLOAD_CCT].cap |= SANE_CAP_INACTIVE;
+
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0];
+
+ /* Action: download gamma vectors table */
+ s->opt[OPT_DOWNLOAD_GAMMA].name = "download-gamma";
+ s->opt[OPT_DOWNLOAD_GAMMA].title = "Download Gamma Vector(s)";
+ s->opt[OPT_DOWNLOAD_GAMMA].desc = "Download Gamma Vector(s).";
+ s->opt[OPT_DOWNLOAD_GAMMA].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_DOWNLOAD_GAMMA].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_COLOR_SENSOR].name = "color-sensor";
+ s->opt[OPT_COLOR_SENSOR].title = "Gray scan with";
+ s->opt[OPT_COLOR_SENSOR].desc = "Select the color sensor to scan in gray mode.";
+ s->opt[OPT_COLOR_SENSOR].type = SANE_TYPE_STRING;
+ s->opt[OPT_COLOR_SENSOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_COLOR_SENSOR].size = max_string_size (color_sensor_list);
+ if (s->hw->ScannerModel!=COLORONESCANNER)
+ s->opt[OPT_COLOR_SENSOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR_SENSOR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_COLOR_SENSOR].constraint.string_list = color_sensor_list;
+ s->val[OPT_COLOR_SENSOR].s = strdup(color_sensor_list[2]);
+
+
+ mode_update (s, s->val[OPT_MODE].s);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach (dev, 0, SANE_FALSE);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* silence gcc */
+
+ DBG_INIT ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (APPLE_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0, SANE_FALSE);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ if (strncmp (dev_name, "option", 6) == 0
+ && isspace (dev_name[6]))
+ {
+ const char *str = dev_name + 7;
+
+ while (isspace (*str))
+ ++str;
+
+ continue;
+ }
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Apple_Device *dev, *next;
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Apple_Device *dev;
+ int i;
+
+ local_only = local_only; /* silence gcc */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Apple_Device *dev;
+ SANE_Status status;
+ Apple_Scanner *s;
+ int i, j;
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach (devicename, &dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ /* empty devicname -> use first device */
+ dev = first_dev;
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->fd = -1;
+ s->hw = dev;
+ for (i = 0; i < 3; ++i)
+ for (j = 0; j < 256; ++j)
+ s->gamma_table[i][j] = j;
+
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Apple_Scanner *prev, *s;
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (ERROR_MESSAGE, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Apple_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Apple_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+
+ DBG (FLOW_CONTROL, "(%s): Entering on control_option for option %s (%d).\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" : "set",
+ s->opt[option].name, option);
+
+ if (val || action == SANE_ACTION_GET_VALUE)
+ switch (s->opt[option].type)
+ {
+ case SANE_TYPE_STRING:
+ DBG (FLOW_CONTROL, "Value %s\n", (action == SANE_ACTION_GET_VALUE) ?
+ s->val[option].s : (char *) val);
+ break;
+ case SANE_TYPE_FIXED:
+ {
+ double v1, v2;
+ SANE_Fixed f;
+ v1 = SANE_UNFIX (s->val[option].w);
+ f = *(SANE_Fixed *) val;
+ v2 = SANE_UNFIX (f);
+ DBG (FLOW_CONTROL, "Value %g (Fixed)\n",
+ (action == SANE_ACTION_GET_VALUE) ? v1 : v2);
+ }
+ default:
+ DBG (FLOW_CONTROL, "Value %u (Int).\n",
+ (action == SANE_ACTION_GET_VALUE)
+ ? s->val[option].w : *(SANE_Int *) val);
+ break;
+ }
+
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_AUTOBACKGROUND:
+ case OPT_AUTOBACKGROUND_THRESHOLD:
+ case OPT_VOLT_REF:
+ case OPT_VOLT_REF_TOP:
+ case OPT_VOLT_REF_BOTTOM:
+
+ case OPT_LAMP:
+ case OPT_WAIT:
+ case OPT_CALIBRATE:
+ case OPT_LED:
+ case OPT_CCD:
+ case OPT_MTF_CIRCUIT:
+ case OPT_ICP:
+ case OPT_POLARITY:
+
+ case OPT_CUSTOM_CCT:
+ case OPT_CUSTOM_GAMMA:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+
+ case OPT_CCT:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+
+ case OPT_MODE:
+/*
+TODO: This is to protect the mode string to be ruined from the dll?
+backend. I do not know why. It's definitely an overkill and should be
+eliminated.
+ status = sanei_constrain_value (s->opt + option, s->val[option].s,
+ info);
+*/
+ case OPT_MODEL:
+ case OPT_GRAYMAP:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_HALFTONE_FILE:
+ case OPT_SPEED:
+ case OPT_COLOR_SENSOR:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+
+/* Some Buttons */
+ case OPT_DOWNLOAD_CALIBRATION_VECTOR:
+ case OPT_DOWNLOAD_CCT:
+ case OPT_DOWNLOAD_GAMMA:
+ return SANE_STATUS_INVAL;
+
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+
+ s->val[option].w = *(SANE_Word *) val;
+ calc_parameters (s);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS
+ | SANE_INFO_RELOAD_OPTIONS
+ | SANE_INFO_INEXACT;
+
+ return SANE_STATUS_GOOD;
+
+ /* fall through */
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_AUTOBACKGROUND_THRESHOLD:
+ case OPT_VOLT_REF_TOP:
+ case OPT_VOLT_REF_BOTTOM:
+ case OPT_LAMP:
+ case OPT_WAIT:
+ case OPT_CALIBRATE:
+ case OPT_LED:
+ case OPT_CCD:
+ case OPT_MTF_CIRCUIT:
+ case OPT_ICP:
+ case OPT_POLARITY:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Simple Strings */
+ case OPT_GRAYMAP:
+ case OPT_HALFTONE_FILE:
+ case OPT_SPEED:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* Boolean */
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Bool *) val;
+ return SANE_STATUS_GOOD;
+
+
+ /* side-effect-free word-array options: */
+ case OPT_CCT:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+
+ /* options with light side-effects: */
+
+ case OPT_HALFTONE_PATTERN:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!strcmp (val, "download"))
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ /* TODO: ENABLE(OPT_HALFTONE_FILE); */
+ }
+ else
+ DISABLE (OPT_HALFTONE_FILE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOBACKGROUND:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ s->val[option].w = *(SANE_Bool *) val;
+ if (*(SANE_Bool *) val)
+ {
+ DISABLE (OPT_THRESHOLD);
+ ENABLE (OPT_AUTOBACKGROUND_THRESHOLD);
+ }
+ else
+ {
+ ENABLE (OPT_THRESHOLD);
+ DISABLE (OPT_AUTOBACKGROUND_THRESHOLD);
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_VOLT_REF:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ s->val[option].w = *(SANE_Bool *) val;
+ if (*(SANE_Bool *) val)
+ {
+ DISABLE(OPT_BRIGHTNESS);
+ DISABLE(OPT_CONTRAST);
+ ENABLE(OPT_VOLT_REF_TOP);
+ ENABLE(OPT_VOLT_REF_BOTTOM);
+ }
+ else
+ {
+ ENABLE(OPT_BRIGHTNESS);
+ ENABLE(OPT_CONTRAST);
+ DISABLE(OPT_VOLT_REF_TOP);
+ DISABLE(OPT_VOLT_REF_BOTTOM);
+ }
+ return SANE_STATUS_GOOD;
+
+/* Actions: Buttons */
+
+ case OPT_DOWNLOAD_CALIBRATION_VECTOR:
+ case OPT_DOWNLOAD_CCT:
+ case OPT_DOWNLOAD_GAMMA:
+ /* TODO: fix {down/up}loads */
+ return SANE_STATUS_UNSUPPORTED;
+
+ case OPT_CUSTOM_CCT:
+ s->val[OPT_CUSTOM_CCT].w=*(SANE_Word *) val;
+ if (s->val[OPT_CUSTOM_CCT].w)
+ {
+ ENABLE(OPT_CCT);
+ ENABLE(OPT_DOWNLOAD_CCT);
+ }
+ else
+ {
+ DISABLE(OPT_CCT);
+ DISABLE(OPT_DOWNLOAD_CCT);
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ gamma_update(s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_COLOR_SENSOR:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ gamma_update(s);
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ /* HEAVY (RADIOACTIVE) SIDE EFFECTS: CHECKME */
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+
+ status = mode_update (s, val);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ } /* End of switch */
+ } /* End of SET_VALUE */
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Apple_Scanner *s = handle;
+
+ DBG (FLOW_CONTROL, "Entering sane_get_parameters\n");
+ calc_parameters (s);
+
+
+ if (params)
+ *params = s->params;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Apple_Scanner *s = handle;
+ SANE_Status status;
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+
+ calc_parameters (s);
+
+ if (s->fd < 0)
+ {
+ /* this is the first (and maybe only) pass... */
+
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "open: wait_ready() failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = mode_select (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "sane_start: mode_select command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = scan_area_and_windows (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "open: set scan area command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = request_sense (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (ERROR_MESSAGE, "sane_start: request_sense revealed error: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ s->scanning = SANE_TRUE;
+ s->AbortedByUser = SANE_FALSE;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ return SANE_STATUS_GOOD;
+
+stop_scanner_and_return:
+ s->scanning = SANE_FALSE;
+ s->AbortedByUser = SANE_FALSE;
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Apple_Scanner *s = handle;
+ SANE_Status status;
+
+ uint8_t get_data_status[10];
+ uint8_t read[10];
+
+#ifdef RESERVE_RELEASE_HACK
+ uint8_t reserve[6];
+ uint8_t release[6];
+#endif
+
+ uint8_t result[12];
+ size_t size;
+ SANE_Int data_length = 0;
+ SANE_Int data_av = 0;
+ SANE_Int offset = 0;
+ SANE_Int rread = 0;
+ SANE_Bool Pseudo8bit = SANE_FALSE;
+
+#ifdef NEUTRALIZE_BACKEND
+ *len=max_len;
+ return SANE_STATUS_GOOD;
+
+#else
+ *len = 0;
+ if (!s->scanning) return SANE_STATUS_EOF;
+
+
+ if (!strcmp (s->val[OPT_MODE].s, "Gray16"))
+ Pseudo8bit = SANE_TRUE;
+
+ /* TODO: The current function only implements for APPLESCANNER In
+ order to use the COLORONESCANNER you have to study the docs to
+ see how it the parameters get modified before scan. From this
+ starting point it should be trivial to use a ONESCANNER int the
+ gray256 mode but I don't have one from these pets in home. MF */
+
+
+ memset (get_data_status, 0, sizeof (get_data_status));
+ get_data_status[0] = APPLE_SCSI_GET_DATA_STATUS;
+ get_data_status[1] = 1; /* Wait */
+ STORE24 (get_data_status + 6, sizeof (result));
+
+ memset (read, 0, sizeof (read));
+ read[0] = APPLE_SCSI_READ_SCANNED_DATA;
+
+
+#ifdef RESERVE_RELEASE_HACK
+ memset (reserve, 0, sizeof (reserve));
+ reserve[0] = APPLE_SCSI_RESERVE;
+
+ reserve[1]=CONTROLLER_SCSI_ID;
+ reserve[1]=reserve[1] << 1;
+ reserve[1]|=SETTHIRDPARTY;
+
+ memset (release, 0, sizeof (release));
+ release[0] = APPLE_SCSI_RELEASE;
+ release[1]=CONTROLLER_SCSI_ID;
+ release[1]=reserve[1] << 1;
+ release[1]|=SETTHIRDPARTY;
+
+#endif
+
+ do
+ {
+ size = sizeof (result);
+ status = sanei_scsi_cmd (s->fd, get_data_status,
+ sizeof (get_data_status), result, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (!size)
+ {
+ DBG (ERROR_MESSAGE, "sane_read: cannot get_data_status.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ data_length = READ24 (result);
+ data_av = READ24 (result + 9);
+
+ if (data_length)
+ {
+ /* if (result[3] & 1) Scanner Blocked: Retrieve data */
+ if ((result[3] & 1) || data_av)
+ {
+ DBG (IO_MESSAGE,
+ "sane_read: (status) Available in scanner buffer %u.\n",
+ data_av);
+
+ if (Pseudo8bit)
+ if ((data_av << 1) + offset > max_len)
+ rread = (max_len - offset) >> 1;
+ else
+ rread = data_av;
+ else if (data_av + offset > max_len)
+ rread = max_len - offset;
+ else
+ rread = data_av;
+
+ DBG (IO_MESSAGE,
+ "sane_read: (action) Actual read request for %u bytes.\n",
+ rread);
+
+ size = rread;
+
+ STORE24 (read + 6, rread);
+
+#ifdef RESERVE_RELEASE_HACK
+ {
+ SANE_Status status;
+ DBG(IO_MESSAGE,"Reserving the SCSI bus.\n");
+ status=sanei_scsi_cmd (s->fd,reserve,sizeof(reserve),0,0);
+ DBG(IO_MESSAGE,"Reserving... status:= %d\n",status);
+ }
+#endif /* RESERVE_RELEASE_HACK */
+
+ status = sanei_scsi_cmd (s->fd, read, sizeof (read),
+ buf + offset, &size);
+
+#ifdef RESERVE_RELEASE_HACK
+ {
+ SANE_Status status;
+ DBG(IO_MESSAGE,"Releasing the SCSI bus.\n");
+ status=sanei_scsi_cmd (s->fd,release,sizeof(release),0,0);
+ DBG(IO_MESSAGE,"Releasing... status:= %d\n",status);
+ }
+#endif /* RESERVE_RELEASE_HACK */
+
+
+ if (Pseudo8bit)
+ {
+ SANE_Int byte;
+ SANE_Int pos = offset + (rread << 1) - 1;
+ SANE_Byte B;
+ for (byte = offset + rread - 1; byte >= offset; byte--)
+ {
+ B = buf[byte];
+ buf[pos--] = 255 - (B << 4); /* low (right) nibble */
+ buf[pos--] = 255 - (B & 0xF0); /* high (left) nibble */
+ }
+ offset += size << 1;
+ }
+ else
+ offset += size;
+
+ DBG (IO_MESSAGE, "sane_read: Buffer %u of %u full %g%%\n",
+ offset, max_len, (double) (offset * 100. / max_len));
+ }
+ }
+ }
+ while (offset < max_len && data_length != 0 && !s->AbortedByUser);
+
+
+ if (s->AbortedByUser)
+ {
+ s->scanning = SANE_FALSE;
+ status = sanei_scsi_cmd (s->fd, test_unit_ready,
+ sizeof (test_unit_ready), 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (!data_length) /* If not blocked */
+ {
+ s->scanning = SANE_FALSE;
+
+ DBG (IO_MESSAGE, "sane_read: (status) Oups! No more data...");
+ if (!offset)
+ {
+ *len = 0;
+ DBG (IO_MESSAGE, "EOF\n");
+ return SANE_STATUS_EOF;
+ }
+ else
+ {
+ *len = offset;
+ DBG (IO_MESSAGE, "GOOD\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+
+ DBG (FLOW_CONTROL,
+ "sane_read: Normal Exiting (?), Aborted=%u, data_length=%u\n",
+ s->AbortedByUser, data_length);
+ *len = offset;
+
+ return SANE_STATUS_GOOD;
+
+#endif /* NEUTRALIZE_BACKEND */
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Apple_Scanner *s = handle;
+
+ if (s->scanning)
+ {
+ if (s->AbortedByUser)
+ {
+ DBG (FLOW_CONTROL,
+ "sane_cancel: Allready Aborted. Please Wait...\n");
+ }
+ else
+ {
+ s->scanning=SANE_FALSE;
+ s->AbortedByUser = SANE_TRUE;
+ DBG (FLOW_CONTROL, "sane_cancel: Signal Caught! Aborting...\n");
+ }
+ }
+ else
+ {
+ if (s->AbortedByUser)
+ {
+ DBG (FLOW_CONTROL, "sane_cancel: Scan has not been Initiated yet, "
+ "or it is allready aborted.\n");
+ s->AbortedByUser = SANE_FALSE;
+ sanei_scsi_cmd (s->fd, test_unit_ready,
+ sizeof (test_unit_ready), 0, 0);
+ }
+ else
+ {
+ DBG (FLOW_CONTROL, "sane_cancel: Scan has not been Initiated "
+ "yet (or it's over).\n");
+ }
+ }
+
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+DBG (FLOW_CONTROL,"sane_set_io_mode: Entering.\n");
+
+ handle = handle; /* silence gcc */
+
+if (non_blocking)
+ {
+ DBG (FLOW_CONTROL, "sane_set_io_mode: Don't call me please. "
+ "Unimplemented function\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle; /* silence gcc */
+ fd = fd; /* silence gcc */
+
+ DBG (FLOW_CONTROL, "sane_get_select_fd: Don't call me please. "
+ "Unimplemented function\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/apple.conf.in b/backend/apple.conf.in
new file mode 100644
index 0000000..4a1fc7f
--- /dev/null
+++ b/backend/apple.conf.in
@@ -0,0 +1,2 @@
+scsi APPLE
+/dev/scanner
diff --git a/backend/apple.h b/backend/apple.h
new file mode 100644
index 0000000..9e14434
--- /dev/null
+++ b/backend/apple.h
@@ -0,0 +1,270 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998 Milon Firikis based on David Mosberger-Tang previous
+ Work on mustek.c file from the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+#ifndef apple_h
+#define apple_h
+
+#include <sys/types.h>
+
+
+/*
+Warning: if you uncomment the next line you 'll get
+zero functionality. All the scanner specific function
+such as sane_read, attach and the others will return
+without doing anything. This way you can run the backend
+without an attached scanner just to see if it gets
+its control variables in a proper way.
+
+TODO: This could be a nice thing to do as a sane config
+option at runtime. This way one can debug the gui-ipc
+part of the backend without actually has the scanner.
+
+*/
+
+#if 0
+#define NEUTRALIZE_BACKEND
+#define APPLE_MODEL_SELECT APPLESCANNER
+#endif
+#undef CALIBRATION_FUNCTIONALITY
+#undef RESERVE_RELEASE_HACK
+
+#ifdef RESERVE_RELEASE_HACK
+/* Also Try these with zero */
+#define CONTROLLER_SCSI_ID 7
+#define SETTHIRDPARTY 0x10
+#endif
+
+
+#define ERROR_MESSAGE 1
+#define USER_MESSAGE 5
+#define FLOW_CONTROL 50
+#define VARIABLE_CONTROL 70
+#define DEBUG_SPECIAL 100
+#define IO_MESSAGE 110
+#define INNER_LOOP 120
+
+
+/* mode values: */
+enum Apple_Modes
+ {
+ APPLE_MODE_LINEART=0,
+ APPLE_MODE_HALFTONE,
+ APPLE_MODE_GRAY,
+ APPLE_MODE_BICOLOR,
+ EMPTY_DONT_USE_IT,
+ APPLE_MODE_COLOR
+ };
+
+enum Apple_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_HWDETECT_GROUP,
+ OPT_MODEL,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+
+ OPT_ENHANCEMENT_GROUP,
+ /* COMMON */
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+
+ /* AppleScanner only */
+ OPT_GRAYMAP,
+ OPT_AUTOBACKGROUND,
+ OPT_AUTOBACKGROUND_THRESHOLD,
+
+ /* AppleScanner & OneScanner */
+ OPT_HALFTONE_PATTERN,
+ OPT_HALFTONE_FILE,
+
+ /* ColorOneScanner Only */
+ OPT_VOLT_REF,
+ OPT_VOLT_REF_TOP,
+ OPT_VOLT_REF_BOTTOM,
+
+ /* misc : advanced */
+ OPT_MISC_GROUP,
+
+ /* all */
+ OPT_LAMP,
+
+ /* AppleScanner Only */
+ OPT_WAIT,
+
+ /* OneScanner only */
+ OPT_CALIBRATE,
+ OPT_SPEED,
+
+ /* OneScanner && ColorOneScanner */
+ OPT_LED,
+ OPT_CCD,
+
+ /* ColorOneScanner only */
+
+ OPT_MTF_CIRCUIT,
+ OPT_ICP,
+ OPT_POLARITY,
+
+ /* color group : advanced */
+
+ OPT_COLOR_GROUP,
+
+
+#ifdef CALIBRATION_FUNCTIONALITY
+
+ /* OneScanner */
+ OPT_CALIBRATION_VECTOR,
+
+ /* ColorOneScanner */
+
+ OPT_CALIBRATION_VECTOR_RED,
+ OPT_CALIBRATION_VECTOR_GREEN,
+ OPT_CALIBRATION_VECTOR_BLUE,
+#endif
+
+
+ /* OneScanner && ColorOneScanner */
+ OPT_DOWNLOAD_CALIBRATION_VECTOR,
+
+ /* ColorOneScanner */
+
+ OPT_CUSTOM_CCT,
+ OPT_CCT,
+ OPT_DOWNLOAD_CCT,
+
+ OPT_CUSTOM_GAMMA,
+
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_DOWNLOAD_GAMMA,
+ OPT_COLOR_SENSOR,
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+
+/* This is a hack to get fast the model of the Attached Scanner */
+/* But it Works well and I am not considering in "fix" it */
+enum SCANNERMODEL
+ {
+ OPT_NUM_SCANNERS = 0,
+
+ APPLESCANNER, ONESCANNER, COLORONESCANNER,
+ NUM_SCANNERS
+ };
+
+typedef struct Apple_Device
+ {
+ struct Apple_Device *next;
+ SANE_Int ScannerModel;
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Int MaxWidth;
+ SANE_Int MaxHeight;
+ unsigned flags;
+ }
+Apple_Device;
+
+typedef struct Apple_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct Apple_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ /* First we put here all the scan variables */
+
+ /* These are needed for converting back and forth the scan area */
+
+ SANE_Int bpp; /* The actual bpp, before scaling */
+
+ double ulx;
+ double uly;
+ double wx;
+ double wy;
+ SANE_Int ULx;
+ SANE_Int ULy;
+ SANE_Int Width;
+ SANE_Int Height;
+
+/*
+TODO: Initialize this beasts with malloc instead of statically allocation.
+*/
+ SANE_Int calibration_vector[2550];
+ SANE_Int calibration_vector_red[2700];
+ SANE_Int calibration_vector_green[2700];
+ SANE_Int calibration_vector_blue[2700];
+ SANE_Fixed cct3x3[9];
+ SANE_Int gamma_table[3][256];
+ SANE_Int halftone_pattern[64];
+
+ SANE_Bool scanning;
+ SANE_Bool AbortedByUser;
+
+ int pass; /* pass number */
+ SANE_Parameters params;
+
+ int fd; /* SCSI filedescriptor */
+
+ /* scanner dependent/low-level state: */
+ Apple_Device *hw;
+
+ }
+Apple_Scanner;
+
+#endif /* apple_h */
diff --git a/backend/artec.c b/backend/artec.c
new file mode 100644
index 0000000..212de34
--- /dev/null
+++ b/backend/artec.c
@@ -0,0 +1,3754 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Artec/Ultima scanners.
+
+ Copyright (C) 1998-2000 Chris Pinkham
+ Released under the terms of the GPL.
+ *NO WARRANTY*
+
+ Portions contributed by:
+ David Leadbetter - A6000C (3-pass)
+ Dick Bruijn - AT12
+
+ *********************************************************************
+ For feedback/information:
+
+ cpinkham@corp.infi.net
+ http://www4.infi.net/~cpinkham/sane/sane-artec-doc.html
+ *********************************************************************
+ */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include <artec.h>
+
+#define BACKEND_NAME artec
+
+#define ARTEC_MAJOR 0
+#define ARTEC_MINOR 5
+#define ARTEC_SUB 16
+#define ARTEC_LAST_MOD "05/26/2001 17:28 EST"
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define ARTEC_CONFIG_FILE "artec.conf"
+#define ARTEC_MAX_READ_SIZE 32768
+
+static int num_devices;
+static const SANE_Device **devlist = 0;
+static ARTEC_Device *first_dev;
+static ARTEC_Scanner *first_handle;
+
+static const SANE_String_Const mode_list[] =
+{
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static const SANE_String_Const filter_type_list[] =
+{
+ "Mono", "Red", "Green", "Blue",
+ 0
+};
+
+static const SANE_String_Const halftone_pattern_list[] =
+{
+ "User defined (unsupported)", "4x4 Spiral", "4x4 Bayer", "8x8 Spiral",
+ "8x8 Bayer",
+ 0
+};
+
+static const SANE_Range u8_range =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+#define INQ_LEN 0x60
+static const uint8_t inquiry[] =
+{
+ 0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00
+};
+
+static const uint8_t test_unit_ready[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static struct
+ {
+ SANE_String model; /* product model */
+ SANE_String type; /* type of scanner */
+ double width; /* width in inches */
+ double height; /* height in inches */
+ SANE_Word adc_bits; /* Analog-to-Digital Converter Bits */
+ SANE_Word setwindow_cmd_size; /* Set-Window command size */
+ SANE_Word max_read_size; /* Max Read size in bytes */
+ long flags; /* flags */
+ SANE_String horz_resolution_str; /* Horizontal resolution list */
+ SANE_String vert_resolution_str; /* Vertical resolution list */
+ }
+cap_data[] =
+{
+ {
+ "AT3", "flatbed",
+ 8.3, 11, 8, 55, 32768,
+ ARTEC_FLAG_CALIBRATE_RGB |
+ ARTEC_FLAG_RGB_LINE_OFFSET |
+ ARTEC_FLAG_RGB_CHAR_SHIFT |
+ ARTEC_FLAG_OPT_CONTRAST |
+ ARTEC_FLAG_GAMMA_SINGLE |
+ ARTEC_FLAG_SEPARATE_RES |
+ ARTEC_FLAG_SENSE_HANDLER |
+ ARTEC_FLAG_SENSE_BYTE_19 |
+ ARTEC_FLAG_ADF |
+ ARTEC_FLAG_HALFTONE_PATTERN |
+ ARTEC_FLAG_MBPP_NEGATIVE |
+ ARTEC_FLAG_ONE_PASS_SCANNER,
+ "50,100,200,300", "50,100,200,300,600"
+ }
+ ,
+ {
+ "A6000C", "flatbed",
+ 8.3, 14, 8, 55, 8192,
+/* some have reported that Calibration does not work the same as AT3 & A6000C+
+ ARTEC_FLAG_CALIBRATE_RGB |
+ */
+ ARTEC_FLAG_OPT_CONTRAST |
+ ARTEC_FLAG_OPT_BRIGHTNESS |
+ ARTEC_FLAG_SEPARATE_RES |
+ ARTEC_FLAG_SENSE_HANDLER |
+ ARTEC_FLAG_ADF |
+ ARTEC_FLAG_HALFTONE_PATTERN,
+ "50,100,200,300", "50,100,200,300,600"
+ }
+ ,
+ {
+ "A6000C PLUS", "flatbed",
+ 8.3, 14, 8, 55, 8192,
+ ARTEC_FLAG_CALIBRATE_RGB |
+ ARTEC_FLAG_RGB_LINE_OFFSET |
+ ARTEC_FLAG_RGB_CHAR_SHIFT |
+ ARTEC_FLAG_OPT_CONTRAST |
+ ARTEC_FLAG_GAMMA_SINGLE |
+ ARTEC_FLAG_SEPARATE_RES |
+ ARTEC_FLAG_SENSE_HANDLER |
+ ARTEC_FLAG_SENSE_BYTE_19 |
+ ARTEC_FLAG_ADF |
+ ARTEC_FLAG_HALFTONE_PATTERN |
+ ARTEC_FLAG_MBPP_NEGATIVE |
+ ARTEC_FLAG_ONE_PASS_SCANNER,
+ "50,100,200,300", "50,100,200,300,600"
+ }
+ ,
+ {
+ "AT6", "flatbed",
+ 8.3, 11, 10, 55, 32768,
+ ARTEC_FLAG_CALIBRATE_RGB |
+ ARTEC_FLAG_RGB_LINE_OFFSET |
+ ARTEC_FLAG_RGB_CHAR_SHIFT |
+ ARTEC_FLAG_OPT_CONTRAST |
+/* gamma not working totally correct yet.
+ ARTEC_FLAG_GAMMA_SINGLE |
+ */
+ ARTEC_FLAG_SEPARATE_RES |
+ ARTEC_FLAG_SENSE_HANDLER |
+ ARTEC_FLAG_ADF |
+ ARTEC_FLAG_HALFTONE_PATTERN |
+ ARTEC_FLAG_MBPP_NEGATIVE |
+ ARTEC_FLAG_ONE_PASS_SCANNER,
+ "50,100,200,300", "50,100,200,300,600"
+ }
+ ,
+ {
+ "AT12", "flatbed",
+ 8.5, 11, 12, 67, 32768,
+/* calibration works slower so disabled
+ ARTEC_CALIBRATE_DARK_WHITE |
+ */
+/* gamma not working totally correct yet.
+ ARTEC_FLAG_GAMMA |
+ */
+ ARTEC_FLAG_OPT_CONTRAST |
+ ARTEC_FLAG_SEPARATE_RES |
+ ARTEC_FLAG_SENSE_HANDLER |
+ ARTEC_FLAG_SENSE_ENH_18 |
+ ARTEC_FLAG_SENSE_BYTE_22 |
+ ARTEC_FLAG_SC_BUFFERS_LINES |
+ ARTEC_FLAG_SC_HANDLES_OFFSET |
+ ARTEC_FLAG_PIXEL_AVERAGING |
+ ARTEC_FLAG_ENHANCE_LINE_EDGE |
+ ARTEC_FLAG_ADF |
+ ARTEC_FLAG_HALFTONE_PATTERN |
+ ARTEC_FLAG_MBPP_NEGATIVE |
+ ARTEC_FLAG_ONE_PASS_SCANNER,
+ "25,50,100,200,300,400,500,600",
+ "25,50,100,200,300,400,500,600,700,800,900,1000,1100,1200"
+ }
+ ,
+ {
+ "AM12S", "flatbed",
+ 8.26, 11.7, 12, 67, ARTEC_MAX_READ_SIZE,
+/* calibration works slower so disabled
+ ARTEC_CALIBRATE_DARK_WHITE |
+ */
+/* gamma not working totally correct yet.
+ ARTEC_FLAG_GAMMA |
+ */
+ ARTEC_FLAG_RGB_LINE_OFFSET |
+ ARTEC_FLAG_SEPARATE_RES |
+ ARTEC_FLAG_IMAGE_REV_LR |
+ ARTEC_FLAG_REVERSE_WINDOW |
+ ARTEC_FLAG_SENSE_HANDLER |
+ ARTEC_FLAG_SENSE_ENH_18 |
+ ARTEC_FLAG_MBPP_NEGATIVE |
+ ARTEC_FLAG_ONE_PASS_SCANNER,
+ "50,100,300,600",
+ "50,100,300,600,1200"
+ }
+ ,
+};
+
+/* store vendor and model if hardcoded in artec.conf */
+static char artec_vendor[9] = "";
+static char artec_model[17] = "";
+
+/* file descriptor for debug data output */
+static int debug_fd = -1;
+
+static char *artec_skip_whitespace (char *str)
+{
+ while (isspace (*str))
+ ++str;
+ return str;
+}
+
+static SANE_Status
+artec_str_list_to_word_list (SANE_Word ** word_list_ptr, SANE_String str)
+{
+ SANE_Word *word_list;
+ char *start;
+ char *end;
+ char temp_str[1024];
+ int comma_count = 1;
+
+ if ((str == NULL) ||
+ (strlen (str) == 0))
+ {
+ /* alloc space for word which stores length (0 in this case) */
+ word_list = (SANE_Word *) malloc (sizeof (SANE_Word));
+ if (word_list == NULL)
+ return (SANE_STATUS_NO_MEM);
+
+ word_list[0] = 0;
+ *word_list_ptr = word_list;
+ return (SANE_STATUS_GOOD);
+ }
+
+ /* make temp copy of input string (only hold 1024 for now) */
+ strncpy (temp_str, str, 1023);
+ temp_str[1023] = '\0';
+
+ end = strchr (temp_str, ',');
+ while (end != NULL)
+ {
+ comma_count++;
+ start = end + 1;
+ end = strchr (start, ',');
+ }
+
+ word_list = (SANE_Word *) calloc (comma_count + 1,
+ sizeof (SANE_Word));
+
+ if (word_list == NULL)
+ return (SANE_STATUS_NO_MEM);
+
+ word_list[0] = comma_count;
+
+ comma_count = 1;
+ start = temp_str;
+ end = strchr (temp_str, ',');
+ while (end != NULL)
+ {
+ *end = '\0';
+ word_list[comma_count] = atol (start);
+
+ start = end + 1;
+ comma_count++;
+ end = strchr (start, ',');
+ }
+
+ word_list[comma_count] = atol (start);
+
+ *word_list_ptr = word_list;
+ return (SANE_STATUS_GOOD);
+}
+
+static size_t
+artec_get_str_index (const SANE_String_Const strings[], char *str)
+{
+ size_t index;
+
+ index = 0;
+ while ((strings[index]) && strcmp (strings[index], str))
+ {
+ index++;
+ }
+
+ if (!strings[index])
+ {
+ index = 0;
+ }
+
+ return (index);
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return (max_size);
+}
+
+/* DB added a sense handler */
+/* last argument is expected to be a pointer to a Artec_Scanner structure */
+static SANE_Status
+sense_handler (int fd, u_char * sense, void *arg)
+{
+ ARTEC_Scanner *s = (ARTEC_Scanner *)arg;
+ int err;
+
+ err = 0;
+
+ DBG(2, "sense fd: %d, data: %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", fd,
+ sense[0], sense[1], sense[2], sense[3],
+ sense[4], sense[5], sense[6], sense[7],
+ sense[8], sense[9], sense[10], sense[11],
+ sense[12], sense[13], sense[14], sense[15]);
+
+ /* byte 18 info pertaining to ADF */
+ if ((s) && (s->hw->flags & ARTEC_FLAG_ADF))
+ {
+ if (sense[18] & 0x01)
+ {
+ DBG (2, "sense: ADF PAPER JAM\n");
+ err++;
+ }
+ if (sense[18] & 0x02)
+ {
+ DBG (2, "sense: ADF NO DOCUMENT IN BIN\n");
+ err++;
+ }
+ if (sense[18] & 0x04)
+ {
+ DBG (2, "sense: ADF SWITCH COVER OPEN\n");
+ err++;
+ }
+ /* DB : next is, i think no failure, so no incrementing s */
+ if (sense[18] & 0x08)
+ {
+ DBG (2, "sense: ADF SET CORRECTLY ON TARGET\n");
+ }
+ /* The following only for AT12, its reserved (zero?) on other models, */
+ if (sense[18] & 0x10)
+ {
+ DBG (2, "sense: ADF LENGTH TOO SHORT\n");
+ err++;
+ }
+ }
+
+ /* enhanced byte 18 sense data */
+ if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_ENH_18))
+ {
+ if (sense[18] & 0x20)
+ {
+ DBG (2, "sense: LAMP FAIL : NOT WARM \n");
+ err++;
+ }
+ if (sense[18] & 0x40)
+ {
+ DBG (2, "sense: NOT READY STATE\n");
+ err++;
+ }
+ }
+
+ if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_BYTE_19))
+ {
+ if (sense[19] & 0x01)
+ {
+ DBG (2, "sense: 8031 program ROM checksum Error\n");
+ err++;
+ }
+ if (sense[19] & 0x02)
+ {
+ DBG (2, "sense: 8031 data RAM R/W Error\n");
+ err++;
+ }
+ if (sense[19] & 0x04)
+ {
+ DBG (2, "sense: Shadow Correction RAM R/W Error\n");
+ err++;
+ }
+ if (sense[19] & 0x08)
+ {
+ DBG (2, "sense: Line RAM R/W Error\n");
+ err++;
+ }
+ if (sense[19] & 0x10)
+ {
+ /* docs say "reserved to '0'" */
+ DBG (2, "sense: CCD control circuit Error\n");
+ err++;
+ }
+ if (sense[19] & 0x20)
+ {
+ DBG (2, "sense: Motor End Switch Error\n");
+ err++;
+ }
+ if (sense[19] & 0x40)
+ {
+ /* docs say "reserved to '0'" */
+ DBG (2, "sense: Lamp Error\n");
+ err++;
+ }
+ if (sense[19] & 0x80)
+ {
+ DBG (2, "sense: Optical Calibration/Shading Error\n");
+ err++;
+ }
+ }
+
+ /* These are the self test results for tests 0-15 */
+ if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_BYTE_22))
+ {
+ if (sense[22] & 0x01)
+ {
+ DBG (2, "sense: 8031 Internal Memory R/W Error\n");
+ err++;
+ }
+ if (sense[22] & 0x02)
+ {
+ DBG (2, "sense: EEPROM test pattern R/W Error\n");
+ err++;
+ }
+ if (sense[22] & 0x04)
+ {
+ DBG (2, "sense: ASIC Test Error\n");
+ err++;
+ }
+ if (sense[22] & 0x08)
+ {
+ DBG (2, "sense: Line RAM R/W Error\n");
+ err++;
+ }
+ if (sense[22] & 0x10)
+ {
+ DBG (2, "sense: PSRAM R/W Test Error\n");
+ err++;
+ }
+ if (sense[22] & 0x20)
+ {
+ DBG (2, "sense: Positioning Error\n");
+ err++;
+ }
+ if (sense[22] & 0x40)
+ {
+ DBG (2, "sense: Test 6 Error\n");
+ err++;
+ }
+ if (sense[22] & 0x80)
+ {
+ DBG (2, "sense: Test 7 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x01)
+ {
+ DBG (2, "sense: Test 8 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x02)
+ {
+ DBG (2, "sense: Test 9 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x04)
+ {
+ DBG (2, "sense: Test 10 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x08)
+ {
+ DBG (2, "sense: Test 11 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x10)
+ {
+ DBG (2, "sense: Test 12 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x20)
+ {
+ DBG (2, "sense: Test 13 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x40)
+ {
+ DBG (2, "sense: Test 14 Error\n");
+ err++;
+ }
+ if (sense[23] & 0x80)
+ {
+ DBG (2, "sense: Test 15 Error\n");
+ err++;
+ }
+ }
+
+ if (err)
+ return SANE_STATUS_IO_ERROR;
+
+ switch (sense[0])
+ {
+ case 0x70: /* ALWAYS */
+ switch (sense[2])
+ {
+ case 0x00:
+ DBG (2, "sense: Successful command\n");
+ return SANE_STATUS_GOOD;
+ case 0x02:
+ DBG (2, "sense: Not Ready, target can not be accessed\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x03:
+ DBG (2, "sense: Medium Error, paper jam or misfeed during ADF\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x04:
+ DBG (2, "sense: Hardware Error, non-recoverable\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x05:
+ DBG (2, "sense: Illegal Request, bad parameter in command block\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x06:
+ DBG (2, "sense: Unit Attention\n");
+ return SANE_STATUS_GOOD;
+ default:
+ DBG (2, "sense: SENSE KEY UNKNOWN (%02x)\n", sense[2]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ default:
+ DBG (2, "sense: Unkown Error Code Qualifier (%02x)\n", sense[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (2, "sense: Should not come here!\n");
+ return SANE_STATUS_IO_ERROR;
+}
+
+
+/* DB added a wait routine for the scanner to come ready */
+static SANE_Status
+wait_ready (int fd)
+{
+ SANE_Status status;
+ int retry = 30; /* make this tuneable? */
+
+ DBG (7, "wait_ready()\n");
+ while (retry-- > 0)
+ {
+ status = sanei_scsi_cmd (fd, test_unit_ready,
+ sizeof (test_unit_ready), 0, 0);
+ if (status == SANE_STATUS_GOOD)
+ return status;
+
+ if (status == SANE_STATUS_DEVICE_BUSY)
+ {
+ sleep (1);
+ continue;
+ }
+
+ /* status != GOOD && != BUSY */
+ DBG (9, "wait_ready: '%s'\n", sane_strstatus (status));
+ return status;
+ }
+
+ /* BUSY after n retries */
+ DBG (9, "wait_ready: '%s'\n", sane_strstatus (status));
+ return status;
+}
+
+/* DB added a abort routine, executed via mode select */
+static SANE_Status
+abort_scan (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ uint8_t *data, comm[22];
+
+ DBG (7, "abort_scan()\n");
+ memset (comm, 0, sizeof (comm));
+
+ comm[0] = 0x15;
+ comm[1] = 0x10;
+ comm[2] = 0x00;
+ comm[3] = 0x00;
+ comm[4] = 0x10;
+ comm[5] = 0x00;
+
+ data = comm + 6;
+ data[0] = 0x00; /* mode data length */
+ data[1] = 0x00; /* medium type */
+ data[2] = 0x00; /* device specific parameter */
+ data[3] = 0x00; /* block descriptor length */
+
+ data = comm + 10;
+ data[0] = 0x00; /* control page parameters */
+ data[1] = 0x0a; /* parameter length */
+ data[2] = 0x02 | ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) |
+ ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01);
+ data[3] = 0x00; /* reserved */
+ data[4] = 0x00; /* reserved */
+
+ DBG (9, "abort: sending abort command\n");
+ sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0);
+
+ DBG (9, "abort: wait for scanner to come ready...\n");
+ wait_ready (s->fd);
+
+ DBG (9, "abort: resetting abort status\n");
+ data[2] = ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) |
+ ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01);
+ sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0);
+
+ DBG (9, "abort: wait for scanner to come ready...\n");
+ return wait_ready (s->fd);
+}
+
+/* DAL - mode_select: used for transparency and ADF scanning */
+/* Based on abort_scan */
+static SANE_Status
+artec_mode_select (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ uint8_t *data, comm[22];
+
+ DBG (7, "artec_mode_select()\n");
+ memset (comm, 0, sizeof (comm));
+
+ comm[0] = 0x15;
+ comm[1] = 0x10;
+ comm[2] = 0x00;
+ comm[3] = 0x00;
+ comm[4] = 0x10;
+ comm[5] = 0x00;
+
+ data = comm + 6;
+ data[0] = 0x00; /* mode data length */
+ data[1] = 0x00; /* medium type */
+ data[2] = 0x00; /* device specific parameter */
+ data[3] = 0x00; /* block descriptor length */
+
+ data = comm + 10;
+ data[0] = 0x00; /* control page parameters */
+ data[1] = 0x0a; /* parameter length */
+ data[2] = ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) |
+ ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01);
+ data[3] = 0x00; /* reserved */
+ data[4] = 0x00; /* reserved */
+
+ DBG (9, "artec_mode_select: mode %d\n", data[2]);
+ DBG (9, "artec_mode_select: sending mode command\n");
+ sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0);
+
+ DBG (9, "artec_mode_select: wait for scanner to come ready...\n");
+ return wait_ready (s->fd);
+}
+
+
+static SANE_Status
+read_data (int fd, int data_type_code, u_char * dest, size_t * len)
+{
+ static u_char read_6[10];
+
+ DBG (7, "read_data()\n");
+
+ memset (read_6, 0, sizeof (read_6));
+ read_6[0] = 0x28;
+ read_6[2] = data_type_code;
+ read_6[6] = *len >> 16;
+ read_6[7] = *len >> 8;
+ read_6[8] = *len;
+
+ return (sanei_scsi_cmd (fd, read_6, sizeof (read_6), dest, len));
+}
+
+static int
+artec_get_status (int fd)
+{
+ u_char write_10[10];
+ u_char read_12[12];
+ size_t nread;
+
+ DBG (7, "artec_get_status()\n");
+
+ nread = 12;
+
+ memset (write_10, 0, 10);
+ write_10[0] = 0x34;
+ write_10[8] = 0x0c;
+
+ sanei_scsi_cmd (fd, write_10, 10, read_12, &nread);
+
+ nread = (read_12[9] << 16) + (read_12[10] << 8) + read_12[11];
+ DBG (9, "artec_status: %lu\n", (u_long) nread);
+
+ return (nread);
+}
+
+static SANE_Status
+artec_reverse_line (SANE_Handle handle, SANE_Byte * data)
+{
+ ARTEC_Scanner *s = handle;
+ SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */
+ SANE_Byte *to, *from;
+ int len;
+
+ DBG (8, "artec_reverse_line()\n");
+
+ len = s->params.bytes_per_line;
+ memcpy (tmp_buf, data, len);
+
+ if (s->params.format == SANE_FRAME_RGB) /* RGB format */
+ {
+ for (from = tmp_buf, to = data + len - 3;
+ to >= data;
+ to -= 3, from += 3)
+ {
+ *(to + 0) = *(from + 0); /* copy the R byte */
+ *(to + 1) = *(from + 1); /* copy the G byte */
+ *(to + 2) = *(from + 2); /* copy the B byte */
+ }
+ }
+ else if (s->params.format == SANE_FRAME_GRAY)
+ {
+ if (s->params.depth == 8) /* 256 color gray-scale */
+ {
+ for (from = tmp_buf, to = data + len; to >= data; to--, from++)
+ {
+ *to = *from;
+ }
+ }
+ else if (s->params.depth == 1) /* line art or halftone */
+ {
+ for (from = tmp_buf, to = data + len; to >= data; to--, from++)
+ {
+ *to = (((*from & 0x01) << 7) |
+ ((*from & 0x02) << 5) |
+ ((*from & 0x04) << 3) |
+ ((*from & 0x08) << 1) |
+ ((*from & 0x10) >> 1) |
+ ((*from & 0x20) >> 3) |
+ ((*from & 0x40) >> 5) |
+ ((*from & 0x80) >> 7));
+ }
+ }
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+#if 0
+static SANE_Status
+artec_byte_rgb_to_line_rgb (SANE_Byte * data, SANE_Int len)
+{
+ SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */
+ int count, from;
+
+ DBG (8, "artec_byte_rgb_to_line_rgb()\n");
+
+ /* copy the RGBRGBRGBRGBRGB... formated data to our temp buffer */
+ memcpy (tmp_buf, data, len * 3);
+
+ /* now copy back to *data in RRRRRRRGGGGGGGBBBBBBB format */
+ for (count = 0, from = 0; count < len; count++, from += 3)
+ {
+ data[count] = tmp_buf[from]; /* R byte */
+ data[count + len] = tmp_buf[from + 1]; /* G byte */
+ data[count + (len * 2)] = tmp_buf[from + 2]; /* B byte */
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+#endif
+
+static SANE_Status
+artec_line_rgb_to_byte_rgb (SANE_Byte * data, SANE_Int len)
+{
+ SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */
+ int count, to;
+
+ DBG (8, "artec_line_rgb_to_byte_rgb()\n");
+
+ /* copy the rgb data to our temp buffer */
+ memcpy (tmp_buf, data, len * 3);
+
+ /* now copy back to *data in RGB format */
+ for (count = 0, to = 0; count < len; count++, to += 3)
+ {
+ data[to] = tmp_buf[count]; /* R byte */
+ data[to + 1] = tmp_buf[count + len]; /* G byte */
+ data[to + 2] = tmp_buf[count + (len * 2)]; /* B byte */
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Byte **line_buffer = NULL;
+static SANE_Byte *tmp_line_buf = NULL;
+static SANE_Int r_buf_lines;
+static SANE_Int g_buf_lines;
+
+static SANE_Status
+artec_buffer_line_offset (SANE_Handle handle, SANE_Int line_offset,
+ SANE_Byte * data, size_t * len)
+{
+ ARTEC_Scanner *s = handle;
+ static SANE_Int width;
+ static SANE_Int cur_line;
+ SANE_Byte *tmp_buf_ptr;
+ SANE_Byte *grn_ptr;
+ SANE_Byte *blu_ptr;
+ SANE_Byte *out_ptr;
+ int count;
+
+ DBG (8, "artec_buffer_line_offset()\n");
+
+ if (*len == 0)
+ return (SANE_STATUS_GOOD);
+
+ if (tmp_line_buf == NULL)
+ {
+ width = *len / 3;
+ cur_line = 0;
+
+ DBG (9, "buffer_line_offset: offset = %d, len = %lu\n",
+ line_offset, (u_long) * len);
+
+ tmp_line_buf = malloc (*len);
+ if (tmp_line_buf == NULL)
+ {
+ DBG (1, "couldn't allocate memory for temp line buffer\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+
+ r_buf_lines = line_offset * 2;
+ g_buf_lines = line_offset;
+
+ line_buffer = malloc (r_buf_lines * sizeof (SANE_Byte *));
+ if (line_buffer == NULL)
+ {
+ DBG (1, "couldn't allocate memory for line buffer pointers\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+
+ for (count = 0; count < r_buf_lines; count++)
+ {
+ line_buffer[count] = malloc ((*len) * sizeof (SANE_Byte));
+ if (line_buffer[count] == NULL)
+ {
+ DBG (1, "couldn't allocate memory for line buffer %d\n",
+ count);
+ return (SANE_STATUS_NO_MEM);
+ }
+ }
+
+ DBG (9, "buffer_line_offset: r lines = %d, g lines = %d\n",
+ r_buf_lines, g_buf_lines);
+ }
+
+ cur_line++;
+
+ if (r_buf_lines > 0)
+ {
+ if (cur_line > r_buf_lines)
+ {
+ /* copy the Red and Green portions out of the buffer */
+ /* if scanner returns RRRRRRRRGGGGGGGGGBBBBBBBB format it's easier */
+ if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)
+ {
+ /* get the red line info from r_buf_lines ago */
+ memcpy (tmp_line_buf, line_buffer[0], width);
+
+ /* get the green line info from g_buf_lines ago */
+ memcpy (tmp_line_buf + width, &line_buffer[line_offset][width],
+ width);
+ }
+ else
+ {
+ /* get the red line info from r_buf_lines ago as a whole line */
+ memcpy (tmp_line_buf, line_buffer[0], *len);
+
+ /* scanner returns RGBRGBRGB format so we do a loop for green */
+ grn_ptr = &line_buffer[line_offset][1];
+ out_ptr = tmp_line_buf + 1;
+ for (count = 0; count < width; count++)
+ {
+ *out_ptr = *grn_ptr; /* copy green pixel */
+
+ grn_ptr += 3;
+ out_ptr += 3;
+ }
+ }
+ }
+
+ /* move all the buffered lines down (just move the ptrs for speed) */
+ tmp_buf_ptr = line_buffer[0];
+ for (count = 0; count < (r_buf_lines - 1); count++)
+ {
+ line_buffer[count] = line_buffer[count + 1];
+ }
+ line_buffer[r_buf_lines - 1] = tmp_buf_ptr;
+
+ /* insert the new line data at the end of our FIFO */
+ memcpy (line_buffer[r_buf_lines - 1], data, *len);
+
+ if (cur_line > r_buf_lines)
+ {
+ /* copy the Red and Green portions out of the buffer */
+ /* if scanner returns RRRRRRRRGGGGGGGGGBBBBBBBB format it's easier */
+ if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)
+ {
+ /* copy the red and green data in with the original blue */
+ memcpy (data, tmp_line_buf, width * 2);
+ }
+ else
+ {
+ /* scanner returns RGBRGBRGB format so we have to do a loop */
+ /* copy the blue data into our temp buffer then copy full */
+ /* temp buffer overtop of input data */
+ if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR)
+ {
+ blu_ptr = data;
+ out_ptr = tmp_line_buf;
+ }
+ else
+ {
+ blu_ptr = data + 2;
+ out_ptr = tmp_line_buf + 2;
+ }
+
+ for (count = 0; count < width; count++)
+ {
+ *out_ptr = *blu_ptr; /* copy blue pixel */
+
+ blu_ptr += 3;
+ out_ptr += 3;
+ }
+
+ /* now just copy tmp_line_buf back over original data */
+ memcpy (data, tmp_line_buf, *len);
+ }
+ }
+ else
+ {
+ /* if in the first r_buf_lines, then don't return anything */
+ *len = 0;
+ }
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+artec_buffer_line_offset_free (void)
+{
+ int count;
+
+ DBG (7, "artec_buffer_line_offset_free()\n");
+
+ free (tmp_line_buf);
+ tmp_line_buf = NULL;
+
+ for (count = 0; count < r_buf_lines; count++)
+ {
+ free (line_buffer[count]);
+ }
+ free (line_buffer);
+ line_buffer = NULL;
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+#if 0
+static SANE_Status
+artec_read_gamma_table (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ char write_6[4096 + 20]; /* max gamma table is 4096 + 20 for command data */
+ char *data;
+ char prt_buf[128];
+ char tmp_buf[128];
+ int i;
+
+ DBG (7, "artec_read_gamma_table()\n");
+
+ memset (write_6, 0, sizeof (*write_6));
+
+ write_6[0] = 0x28; /* read data code */
+
+ /* FIXME: AT12 and AM12S use 0x0E for reading all channels of data */
+ write_6[2] = 0x03; /* data type code "gamma data" */
+
+ write_6[6] = (s->gamma_length + 9) >> 16;
+ write_6[7] = (s->gamma_length + 9) >> 8;
+ write_6[8] = (s->gamma_length + 9);
+
+ /* FIXME: AT12 and AM12S have one less byte so use 18 */
+ if ((!strcmp (s->hw->sane.model, "AT12")) ||
+ (!strcmp (s->hw->sane.model, "AM12S")))
+ {
+ data = write_6 + 18;
+ }
+ else
+ {
+ data = write_6 + 19;
+ }
+
+ /* FIXME: AT12 & AM12S ignore this, it's a reserved field */
+ write_6[10] = 0x08; /* bitmask, bit 3 means mono type */
+
+ if (!s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ write_6[11] = 1; /* internal gamma table #1 (hope this is default) */
+ }
+
+ DBG( 9, "Gamma Table\n" );
+ DBG( 9, "==================================\n" );
+
+ prt_buf[0] = '\0';
+ for (i = 0; i < s->gamma_length; i++)
+ {
+ if (DBG_LEVEL >= 9)
+ {
+ if (!(i % 16))
+ {
+ if ( prt_buf[0] )
+ {
+ strcat( prt_buf, "\n" );
+ DBG( 9, "%s", prt_buf );
+ }
+ sprintf (prt_buf, "%02x: ", i);
+ }
+ sprintf (tmp_buf, "%02x ", (int) s->gamma_table[0][i]);
+ strcat (prt_buf, tmp_buf );
+ }
+
+ data[i] = s->gamma_table[0][i];
+ }
+
+ if ( prt_buf[0] )
+ {
+ strcat( prt_buf, "\n" );
+ DBG( 9, "%s", prt_buf );
+ }
+
+ if ((!strcmp (s->hw->sane.model, "AT12")) ||
+ (!strcmp (s->hw->sane.model, "AM12S")))
+ {
+ return (sanei_scsi_cmd (s->fd, write_6, 10 + 8 + s->gamma_length, 0, 0));
+ }
+ else
+ {
+ return (sanei_scsi_cmd (s->fd, write_6, 10 + 9 + s->gamma_length, 0, 0));
+ }
+}
+#endif
+
+static SANE_Status
+artec_send_gamma_table (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ char write_6[4096 + 20]; /* max gamma table is 4096 + 20 for command data */
+ char *data;
+ char prt_buf[128];
+ char tmp_buf[128];
+ int i;
+
+ DBG (7, "artec_send_gamma_table()\n");
+
+ memset (write_6, 0, sizeof (*write_6));
+
+ write_6[0] = 0x2a; /* send data code */
+
+ if (s->hw->setwindow_cmd_size > 55)
+ {
+ /* newer scanners support sending 3 channels of gamma, or populating all */
+ /* 3 channels with same data by using code 0x0e */
+ write_6[2] = 0x0e;
+ }
+ else
+ {
+ /* older scanners only support 1 channel of gamma data using code 0x3 */
+ write_6[2] = 0x03;
+ }
+
+ /* FIXME: AT12 & AM!2S ignore this, it's a reserved field */
+ write_6[10] = 0x08; /* bitmask, bit 3 means mono type */
+
+ if (!s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ write_6[6] = 9 >> 16;
+ write_6[7] = 9 >> 8;
+ write_6[8] = 9;
+ write_6[11] = 1; /* internal gamma table #1 (hope this is default) */
+
+ return (sanei_scsi_cmd (s->fd, write_6, 10 + 9, 0, 0));
+ }
+ else
+ {
+ write_6[6] = (s->gamma_length + 9) >> 16;
+ write_6[7] = (s->gamma_length + 9) >> 8;
+ write_6[8] = (s->gamma_length + 9);
+
+ DBG( 9, "Gamma Table\n" );
+ DBG( 9, "==================================\n" );
+
+ /* FIXME: AT12 and AM12S have one less byte so use 18 */
+ if ((!strcmp (s->hw->sane.model, "AT12")) ||
+ (!strcmp (s->hw->sane.model, "AM12S")))
+ {
+ data = write_6 + 18;
+ }
+ else
+ {
+ data = write_6 + 19;
+ }
+
+ prt_buf[0] = '\0';
+ for (i = 0; i < s->gamma_length; i++)
+ {
+ if (DBG_LEVEL >= 9)
+ {
+ if (!(i % 16))
+ {
+ if ( prt_buf[0] )
+ {
+ strcat( prt_buf, "\n" );
+ DBG( 9, "%s", prt_buf );
+ }
+ sprintf (prt_buf, "%02x: ", i);
+ }
+ sprintf (tmp_buf, "%02x ", (int) s->gamma_table[0][i]);
+ strcat (prt_buf, tmp_buf );
+ }
+
+ data[i] = s->gamma_table[0][i];
+ }
+
+ data[s->gamma_length - 1] = 0;
+
+ if ( prt_buf[0] )
+ {
+ strcat( prt_buf, "\n" );
+ DBG( 9, "%s", prt_buf );
+ }
+
+ /* FIXME: AT12 and AM12S have one less byte so use 18 */
+ if ((!strcmp (s->hw->sane.model, "AT12")) ||
+ (!strcmp (s->hw->sane.model, "AM12S")))
+ {
+ return (sanei_scsi_cmd (s->fd, write_6, 10 + 8 + s->gamma_length, 0, 0));
+ }
+ else
+ {
+ return (sanei_scsi_cmd (s->fd, write_6, 10 + 9 + s->gamma_length, 0, 0));
+ }
+ }
+}
+
+static SANE_Status
+artec_set_scan_window (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ char write_6[4096];
+ unsigned char *data;
+ int counter;
+ int reversed_x;
+ int max_x;
+
+ DBG (7, "artec_set_scan_window()\n");
+
+ /*
+ * if we can, start before the desired window since we have to throw away
+ * s->line_offset number of rows because of the RGB fixup.
+ */
+ if ((s->line_offset) &&
+ (s->tl_y) &&
+ (s->tl_y >= (s->line_offset * 2)))
+ {
+ s->tl_y -= (s->line_offset * 2);
+ }
+
+ data = (unsigned char *)write_6 + 10;
+
+ DBG (5, "Scan window info:\n");
+ DBG (5, " X resolution: %5d (%d-%d)\n",
+ s->x_resolution, ARTEC_MIN_X (s->hw), ARTEC_MAX_X (s->hw));
+ DBG (5, " Y resolution: %5d (%d-%d)\n",
+ s->y_resolution, ARTEC_MIN_Y (s->hw), ARTEC_MAX_Y (s->hw));
+ DBG (5, " TL_X (pixel): %5d\n",
+ s->tl_x);
+ DBG (5, " TL_Y (pixel): %5d\n",
+ s->tl_y);
+ DBG (5, " Width : %5d (%d-%d)\n",
+ s->params.pixels_per_line,
+ s->hw->x_range.min,
+ (int) ((SANE_UNFIX (s->hw->x_range.max) / MM_PER_INCH) *
+ s->x_resolution));
+ DBG (5, " Height : %5d (%d-%d)\n",
+ s->params.lines,
+ s->hw->y_range.min,
+ (int) ((SANE_UNFIX (s->hw->y_range.max) / MM_PER_INCH) *
+ s->y_resolution));
+
+ DBG (5, " Image Comp. : %s\n", s->mode);
+ DBG (5, " Line Offset : %lu\n", (u_long) s->line_offset);
+
+ memset (write_6, 0, 4096);
+ write_6[0] = 0x24;
+ write_6[8] = s->hw->setwindow_cmd_size; /* total size of command */
+
+ /* beginning of set window data header */
+ /* actual SCSI command data byte count */
+ data[7] = s->hw->setwindow_cmd_size - 8;
+
+ /* x resolution */
+ data[10] = s->x_resolution >> 8;
+ data[11] = s->x_resolution;
+
+ /* y resolution */
+ data[12] = s->y_resolution >> 8;
+ data[13] = s->y_resolution;
+
+ if ( s->hw->flags & ARTEC_FLAG_REVERSE_WINDOW )
+ {
+ /* top left X value */
+ /* the select area is flipped across the page, so we have to do some */
+ /* calculation here to get the the real starting X value */
+ max_x = (int) ((SANE_UNFIX (s->hw->x_range.max) / MM_PER_INCH) *
+ s->x_resolution);
+ reversed_x = max_x - s->tl_x - s->params.pixels_per_line;
+
+ data[14] = reversed_x >> 24;
+ data[15] = reversed_x >> 16;
+ data[16] = reversed_x >> 8;
+ data[17] = reversed_x;
+ }
+ else
+ {
+ /* top left X value */
+ data[14] = s->tl_x >> 24;
+ data[15] = s->tl_x >> 16;
+ data[16] = s->tl_x >> 8;
+ data[17] = s->tl_x;
+ }
+
+ /* top left Y value */
+ data[18] = s->tl_y >> 24;
+ data[19] = s->tl_y >> 16;
+ data[20] = s->tl_y >> 8;
+ data[21] = s->tl_y;
+
+
+ /* width */
+ data[22] = s->params.pixels_per_line >> 24;
+ data[23] = s->params.pixels_per_line >> 16;
+ data[24] = s->params.pixels_per_line >> 8;
+ data[25] = s->params.pixels_per_line;
+
+ /* height */
+ data[26] = (s->params.lines + (s->line_offset * 2)) >> 24;
+ data[27] = (s->params.lines + (s->line_offset * 2)) >> 16;
+ data[28] = (s->params.lines + (s->line_offset * 2)) >> 8;
+ data[29] = (s->params.lines + (s->line_offset * 2));
+
+ /* misc. single-byte settings */
+ /* brightness */
+ if (s->hw->flags & ARTEC_FLAG_OPT_BRIGHTNESS)
+ data[30] = s->val[OPT_BRIGHTNESS].w;
+
+ data[31] = s->val[OPT_THRESHOLD].w; /* threshold */
+
+ /* contrast */
+ if (s->hw->flags & ARTEC_FLAG_OPT_CONTRAST)
+ data[32] = s->val[OPT_CONTRAST].w;
+
+ /*
+ * byte 33 is mode
+ * byte 37 bit 7 is "negative" setting
+ */
+ if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ data[33] = ARTEC_COMP_LINEART;
+ data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80;
+ }
+ else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ {
+ data[33] = ARTEC_COMP_HALFTONE;
+ data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80;
+ }
+ else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ data[33] = ARTEC_COMP_GRAY;
+ data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0;
+ }
+ else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ data[33] = ARTEC_COMP_COLOR;
+ data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0;
+ }
+
+ data[34] = s->params.depth; /* bits per pixel */
+
+ if (s->hw->flags & ARTEC_FLAG_HALFTONE_PATTERN)
+ {
+ data[35] = artec_get_str_index (halftone_pattern_list,
+ s->val[OPT_HALFTONE_PATTERN].s); /* halftone pattern */
+ }
+
+ /* user supplied halftone pattern not supported for now so override with */
+ /* 8x8 Bayer */
+ if (data[35] == 0)
+ {
+ data[35] = 4;
+ }
+
+ /* NOTE: AT12 doesn't support mono according to docs. */
+ data[48] = artec_get_str_index (filter_type_list,
+ s->val[OPT_FILTER_TYPE].s); /* filter mode */
+
+ if (s->hw->setwindow_cmd_size > 55)
+ {
+ data[48] = 0x2; /* DB filter type green for AT12,see above */
+
+ if (s->hw->flags & ARTEC_FLAG_SC_BUFFERS_LINES)
+ {
+ /* FIXME: guessing at this value, use formula instead */
+ data[55] = 0x00; /* buffer full line count */
+ data[56] = 0x00; /* buffer full line count */
+ data[57] = 0x00; /* buffer full line count */
+ data[58] = 0x0a; /* buffer full line count */
+
+ /* FIXME: guessing at this value, use formula instead */
+ data[59] = 0x00; /* access line count */
+ data[60] = 0x00; /* access line count */
+ data[61] = 0x00; /* access line count */
+ data[62] = 0x0a; /* access line count */
+ }
+
+ if (s->hw->flags & ARTEC_FLAG_SC_HANDLES_OFFSET)
+ {
+ /* DB : following fields : high order bit (0x80) is enable */
+ /* scanner handles line offset fixup, 0 = driver handles */
+ data[63] = 0x80;
+ }
+
+ if ((s->hw->flags & ARTEC_FLAG_PIXEL_AVERAGING) &&
+ (s->val[OPT_PIXEL_AVG].w))
+ {
+ /* enable pixel average function */
+ data[64] = 0x80;
+ }
+ else
+ {
+ /* disable pixel average function */
+ data[64] = 0;
+ }
+
+ if ((s->hw->flags & ARTEC_FLAG_ENHANCE_LINE_EDGE) &&
+ (s->val[OPT_EDGE_ENH].w))
+ {
+ /* enable lineart edge enhancement function */
+ data[65] = 0x80;
+ }
+ else
+ {
+ /* disable lineart edge enhancement function */
+ data[65] = 0;
+ }
+
+ /* data is R-G-B format, 0x80 = G-B-R format (reversed) */
+ data[66] = 0;
+ }
+
+ DBG (50, "Set Window data : \n");
+ for (counter = 0; counter < s->hw->setwindow_cmd_size; counter++)
+ {
+ DBG (50, " byte %2d = %02x \n", counter, data[counter] & 0xff); /* DB */
+ }
+ DBG (50, "\n");
+
+ /* set the scan window */
+ return (sanei_scsi_cmd (s->fd, write_6, 10 +
+ s->hw->setwindow_cmd_size, 0, 0));
+}
+
+static SANE_Status
+artec_start_scan (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ char write_7[7];
+
+ DBG (7, "artec_start_scan()\n");
+
+ /* setup cmd to start scanning */
+ memset (write_7, 0, 7);
+ write_7[0] = 0x1b; /* code to start scan */
+
+ /* FIXME: need to make this a flag */
+ if (!strcmp (s->hw->sane.model, "AM12S"))
+ {
+ /* start the scan */
+ return (sanei_scsi_cmd (s->fd, write_7, 6, 0, 0));
+ }
+ else
+ {
+ write_7[4] = 0x01; /* need to send 1 data byte */
+
+ /* start the scan */
+ return (sanei_scsi_cmd (s->fd, write_7, 7, 0, 0));
+ }
+}
+
+static SANE_Status
+artec_software_rgb_calibrate (SANE_Handle handle, SANE_Byte * buf, int lines)
+{
+ ARTEC_Scanner *s = handle;
+ int line, i, loop, offset;
+
+ DBG (7, "artec_software_rgb_calibrate()\n");
+
+ for (line = 0; line < lines; line++)
+ {
+ i = 0;
+ offset = 0;
+
+ if (s->x_resolution == 200)
+ {
+ /* skip ever 3rd byte, -= causes us to go down in count */
+ if ((s->tl_x % 3) == 0)
+ offset -= 1;
+ }
+ else
+ {
+ /* round down to the previous pixel */
+ offset += ((s->tl_x / (300 / s->x_resolution)) *
+ (300 / s->x_resolution));
+ }
+
+ for (loop = 0; loop < s->params.pixels_per_line; loop++)
+ {
+ if ((DBG_LEVEL == 100) &&
+ (loop < 100))
+ {
+ DBG (100, " %2d-%4d R (%4d,%4d): %d * %5.2f = %d\n",
+ line, loop, i, offset, buf[i],
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset],
+ (int) (buf[i] *
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset]));
+ }
+ buf[i] = buf[i] *
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset];
+ i++;
+
+ if ((DBG_LEVEL == 100) &&
+ (loop < 100))
+ {
+ DBG (100, " G (%4d,%4d): %d * %5.2f = %d\n",
+ i, offset, buf[i],
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset],
+ (int) (buf[i] *
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset]));
+ }
+ buf[i] = buf[i] *
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset];
+ i++;
+
+ if ((DBG_LEVEL == 100) &&
+ (loop < 100))
+ {
+ DBG (100, " B (%4d,%4d): %d * %5.2f = %d\n",
+ i, offset, buf[i],
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset],
+ (int) (buf[i] *
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset]));
+ }
+ buf[i] = buf[i] *
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset];
+ i++;
+
+ if (s->x_resolution == 200)
+ {
+ offset += 1;
+
+ /* skip every 3rd byte */
+ if (((offset + 1) % 3) == 0)
+ offset += 1;
+ }
+ else
+ {
+ offset += (300 / s->x_resolution);
+ }
+ }
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+artec_calibrate_shading (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ SANE_Status status; /* DB added */
+ u_char buf[76800]; /* should be big enough */
+ size_t len;
+ SANE_Word save_x_resolution;
+ SANE_Word save_pixels_per_line;
+ int i;
+
+ DBG (7, "artec_calibrate_shading()\n");
+
+ if (s->hw->flags & ARTEC_FLAG_CALIBRATE_RGB)
+ {
+ /* this method scans in 4 lines each of Red, Green, and Blue */
+ /* after reading line of shading data, generate data for software */
+ /* calibration so we have it if user requests */
+ len = 4 * 2592; /* 4 lines of data, 2592 pixels wide */
+
+ if ( DBG_LEVEL == 100 )
+ DBG (100, "RED Software Calibration data\n");
+
+ read_data (s->fd, ARTEC_DATA_RED_SHADING, buf, &len);
+ for (i = 0; i < 2592; i++)
+ {
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][i] =
+ 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4);
+ if (DBG_LEVEL == 100)
+ {
+ DBG (100,
+ " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n",
+ i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776],
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][i]);
+ }
+ }
+
+ if (DBG_LEVEL == 100)
+ {
+ DBG (100, "GREEN Software Calibration data\n");
+ }
+
+ read_data (s->fd, ARTEC_DATA_GREEN_SHADING, buf, &len);
+ for (i = 0; i < 2592; i++)
+ {
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][i] =
+ 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4);
+ if (DBG_LEVEL == 100)
+ {
+ DBG (100,
+ " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n",
+ i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776],
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][i]);
+ }
+ }
+
+ if (DBG_LEVEL == 100)
+ {
+ DBG (100, "BLUE Software Calibration data\n");
+ }
+
+ read_data (s->fd, ARTEC_DATA_BLUE_SHADING, buf, &len);
+ for (i = 0; i < 2592; i++)
+ {
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][i] =
+ 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4);
+ if (DBG_LEVEL == 100)
+ {
+ DBG (100,
+ " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n",
+ i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776],
+ s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][i]);
+ }
+ }
+ }
+ else if (s->hw->flags & ARTEC_FLAG_CALIBRATE_DARK_WHITE)
+ {
+ /* this method scans black, then white data */
+ len = 3 * 5100; /* 1 line of data, 5100 pixels wide, RGB data */
+ read_data (s->fd, ARTEC_DATA_DARK_SHADING, buf, &len);
+ save_x_resolution = s->x_resolution;
+ s->x_resolution = 600;
+ save_pixels_per_line = s->params.pixels_per_line;
+ s->params.pixels_per_line = ARTEC_MAX_X (s->hw);
+ s->params.pixels_per_line = 600 * 8.5; /* ?this? or ?above line? */
+ /* DB added wait_ready */
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "wait for scanner ready failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ /* next line should use ARTEC_DATA_WHITE_SHADING_TRANS if using ADF */
+ read_data (s->fd, ARTEC_DATA_WHITE_SHADING_OPT, buf, &len);
+ s->x_resolution = save_x_resolution;
+ s->params.pixels_per_line = save_pixels_per_line;
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+static SANE_Status
+end_scan (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ /* DB
+ uint8_t write_6[6] =
+ {0x1B, 0, 0, 0, 0, 0};
+ */
+
+ DBG (7, "end_scan()\n");
+
+ s->scanning = SANE_FALSE;
+
+/* if (s->this_pass == 3) */
+ s->this_pass = 0;
+
+ if ((s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) &&
+ (tmp_line_buf != NULL))
+ {
+ artec_buffer_line_offset_free ();
+ }
+
+ /* DB
+ return (sanei_scsi_cmd (s->fd, write_6, 6, 0, 0));
+ */
+ return abort_scan (s);
+}
+
+
+static SANE_Status
+artec_get_cap_data (ARTEC_Device * dev, int fd)
+{
+ int cap_model, loop;
+ SANE_Status status;
+ u_char cap_buf[256]; /* buffer for cap data */
+
+ DBG (7, "artec_get_cap_data()\n");
+
+ /* DB always use the hard-coded capability info first
+ * if we get cap data from the scanner, we override */
+ cap_model = -1;
+ for (loop = 0; loop < NELEMS (cap_data); loop++)
+ {
+ if (strcmp (cap_data[loop].model, dev->sane.model) == 0)
+ {
+ cap_model = loop;
+ }
+ }
+
+ if (cap_model == -1)
+ {
+ DBG (1, "unable to identify Artec model '%s', check artec.c\n",
+ dev->sane.model);
+ return (SANE_STATUS_UNSUPPORTED);
+ }
+
+ dev->x_range.min = 0;
+ dev->x_range.max = SANE_FIX (cap_data[cap_model].width) * MM_PER_INCH;
+ dev->x_range.quant = 1;
+
+ dev->width = cap_data[cap_model].width;
+
+ dev->y_range.min = 0;
+ dev->y_range.max = SANE_FIX (cap_data[cap_model].height) * MM_PER_INCH;
+ dev->y_range.quant = 1;
+
+ dev->height = cap_data[cap_model].height;
+
+ status = artec_str_list_to_word_list (&dev->horz_resolution_list,
+ cap_data[cap_model].horz_resolution_str);
+
+ status = artec_str_list_to_word_list (&dev->vert_resolution_list,
+ cap_data[cap_model].vert_resolution_str);
+
+ dev->contrast_range.min = 0;
+ dev->contrast_range.max = 255;
+ dev->contrast_range.quant = 1;
+
+ dev->brightness_range.min = 0;
+ dev->brightness_range.max = 255;
+ dev->brightness_range.quant = 1;
+
+ dev->threshold_range.min = 0;
+ dev->threshold_range.max = 255;
+ dev->threshold_range.quant = 1;
+
+ dev->sane.type = cap_data[cap_model].type;
+
+ dev->max_read_size = cap_data[cap_model].max_read_size;
+
+ dev->flags = cap_data[cap_model].flags;
+
+ switch (cap_data[cap_model].adc_bits)
+ {
+ case 8:
+ dev->gamma_length = 256;
+ break;
+
+ case 10:
+ dev->gamma_length = 1024;
+ break;
+
+ case 12:
+ dev->gamma_length = 4096;
+ break;
+ }
+
+ dev->setwindow_cmd_size = cap_data[cap_model].setwindow_cmd_size;
+
+ if (dev->support_cap_data_retrieve) /* DB */
+ {
+ /* DB added reading capability data from scanner */
+ char info[80]; /* for printing debugging info */
+ size_t len = sizeof (cap_buf);
+
+ /* read the capability data from the scanner */
+ DBG (9, "reading capability data from scanner...\n");
+
+ wait_ready (fd);
+
+ read_data (fd, ARTEC_DATA_CAPABILITY_DATA, cap_buf, &len);
+
+ DBG (50, "scanner capability data : \n");
+ strncpy (info, (const char *) &cap_buf[0], 8);
+ info[8] = '\0';
+ DBG (50, " Vendor : %s\n", info);
+ strncpy (info, (const char *) &cap_buf[8], 16);
+ info[16] = '\0';
+ DBG (50, " Device Name : %s\n", info);
+ strncpy (info, (const char *) &cap_buf[24], 4);
+ info[4] = '\0';
+ DBG (50, " Version Number : %s\n", info);
+ sprintf (info, "%d ", cap_buf[29]);
+ DBG (50, " CCD Type : %s\n", info);
+ sprintf (info, "%d ", cap_buf[30]);
+ DBG (50, " AD Converter Type : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[31] << 8) | cap_buf[32]);
+ DBG (50, " Buffer size : %s\n", info);
+ sprintf (info, "%d ", cap_buf[33]);
+ DBG (50, " Channels of RGB Gamma : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[34] << 8) | cap_buf[35]);
+ DBG (50, " Opt. res. of R channel : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[36] << 8) | cap_buf[37]);
+ DBG (50, " Opt. res. of G channel : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[38] << 8) | cap_buf[39]);
+ DBG (50, " Opt. res. of B channel : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[40] << 8) | cap_buf[41]);
+ DBG (50, " Min. Hor. Resolution : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[42] << 8) | cap_buf[43]);
+ DBG (50, " Max. Vert. Resolution : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[44] << 8) | cap_buf[45]);
+ DBG (50, " Min. Vert. Resolution : %s\n", info);
+ sprintf (info, "%s ", cap_buf[46] == 0x80 ? "yes" : "no");
+ DBG (50, " Chunky Data Format : %s\n", info);
+ sprintf (info, "%s ", cap_buf[47] == 0x80 ? "yes" : "no");
+ DBG (50, " RGB Data Format : %s\n", info);
+ sprintf (info, "%s ", cap_buf[48] == 0x80 ? "yes" : "no");
+ DBG (50, " BGR Data Format : %s\n", info);
+ sprintf (info, "%d ", cap_buf[49]);
+ DBG (50, " Line Offset : %s\n", info);
+ sprintf (info, "%s ", cap_buf[50] == 0x80 ? "yes" : "no");
+ DBG (50, " Channel Valid Sequence : %s\n", info);
+ sprintf (info, "%s ", cap_buf[51] == 0x80 ? "yes" : "no");
+ DBG (50, " True Gray : %s\n", info);
+ sprintf (info, "%s ", cap_buf[52] == 0x80 ? "yes" : "no");
+ DBG (50, " Force Host Not Do Shading : %s\n", info);
+ sprintf (info, "%s ", cap_buf[53] == 0x00 ? "AT006" : "AT010");
+ DBG (50, " ASIC : %s\n", info);
+ sprintf (info, "%s ", cap_buf[54] == 0x82 ? "SCSI2" :
+ cap_buf[54] == 0x81 ? "SCSI1" : "Parallel");
+ DBG (50, " Interface : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[55] << 8) | cap_buf[56]);
+ DBG (50, " Phys. Area Width : %s\n", info);
+ sprintf (info, "%d ", (cap_buf[57] << 8) | cap_buf[58]);
+ DBG (50, " Phys. Area Length : %s\n", info);
+
+ /* fill in the information we've got from the scanner */
+
+ dev->width = ((float) ((cap_buf[55] << 8) | cap_buf[56])) / 1000;
+ dev->height = ((float) ((cap_buf[57] << 8) | cap_buf[58])) / 1000;
+
+ /* DB ----- */
+ }
+
+ DBG (9, "Scanner capability info.\n");
+ DBG (9, " Vendor : %s\n", dev->sane.vendor);
+ DBG (9, " Model : %s\n", dev->sane.model);
+ DBG (9, " Type : %s\n", dev->sane.type);
+ DBG (5, " Width : %.2f inches\n", dev->width);
+ DBG (9, " Height : %.2f inches\n", dev->height);
+ DBG (9, " X Range(mm) : %d-%d\n",
+ dev->x_range.min,
+ (int) (SANE_UNFIX (dev->x_range.max)));
+ DBG (9, " Y Range(mm) : %d-%d\n",
+ dev->y_range.min,
+ (int) (SANE_UNFIX (dev->y_range.max)));
+
+ DBG (9, " Horz. DPI : %d-%d\n", ARTEC_MIN_X (dev), ARTEC_MAX_X (dev));
+ DBG (9, " Vert. DPI : %d-%d\n", ARTEC_MIN_Y (dev), ARTEC_MAX_Y (dev));
+ DBG (9, " Contrast : %d-%d\n",
+ dev->contrast_range.min, dev->contrast_range.max);
+ DBG (9, " REQ Sh. Cal.: %d\n",
+ dev->flags & ARTEC_FLAG_CALIBRATE ? 1 : 0);
+ DBG (9, " REQ Ln. Offs: %d\n",
+ dev->flags & ARTEC_FLAG_RGB_LINE_OFFSET ? 1 : 0);
+ DBG (9, " REQ Ch. Shft: %d\n",
+ dev->flags & ARTEC_FLAG_RGB_CHAR_SHIFT ? 1 : 0);
+ DBG (9, " SetWind Size: %d\n",
+ dev->setwindow_cmd_size);
+ DBG (9, " Calib Method: %s\n",
+ dev->flags & ARTEC_FLAG_CALIBRATE_RGB ? "RGB" :
+ dev->flags & ARTEC_FLAG_CALIBRATE_DARK_WHITE ? "white/black" : "N/A");
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+dump_inquiry (unsigned char *result)
+{
+ int i;
+ int j;
+ char prt_buf[129] = "";
+ char tmp_buf[129];
+
+ DBG (4, "dump_inquiry()\n");
+
+ DBG (4, " === SANE/Artec backend v%d.%d.%d ===\n",
+ ARTEC_MAJOR, ARTEC_MINOR, ARTEC_SUB);
+ DBG (4, " ===== Scanner Inquiry Block =====\n");
+ for (i = 0; i < 96; i += 16)
+ {
+ sprintf (prt_buf, "0x%02x: ", i);
+ for (j = 0; j < 16; j++)
+ {
+ sprintf (tmp_buf, "%02x ", (int) result[i + j]);
+ strcat( prt_buf, tmp_buf );
+ }
+ strcat( prt_buf, " ");
+ for (j = 0; j < 16; j++)
+ {
+ sprintf (tmp_buf, "%c",
+ isprint (result[i + j]) ? result[i + j] : '.');
+ strcat( prt_buf, tmp_buf );
+ }
+ strcat( prt_buf, "\n" );
+ DBG(4, "%s", prt_buf );
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+attach (const char *devname, ARTEC_Device ** devp)
+{
+ char result[INQ_LEN];
+ char product_revision[5];
+ char temp_result[33];
+ char *str, *t;
+ int fd;
+ SANE_Status status;
+ ARTEC_Device *dev;
+ size_t size;
+
+ DBG (7, "attach()\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ DBG (6, "attach: opening %s\n", devname);
+
+ status = sanei_scsi_open (devname, &fd, sense_handler, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed (%s)\n", sane_strstatus (status));
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (6, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD || size < 16)
+ {
+ DBG (1, "attach: inquiry failed (%s)\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ /*
+ * Check to see if this device is a scanner.
+ */
+ if (result[0] != 0x6)
+ {
+ DBG (1, "attach: device doesn't look like a scanner at all.\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ /*
+ * The BlackWidow BW4800SP is actually a rebadged AT3, with the vendor
+ * string set to 8 spaces and the product to "Flatbed Scanner ". So,
+ * if we have one of these, we'll make it look like an AT3.
+ *
+ * For now, to be on the safe side, we'll also check the version number
+ * since BlackWidow seems to have left that intact as "1.90".
+ *
+ * Check that result[36] == 0x00 so we don't mistake a microtek scanner.
+ */
+ if ((result[36] == 0x00) &&
+ (strncmp (result + 32, "1.90", 4) == 0) &&
+ (strncmp (result + 8, " ", 8) == 0) &&
+ (strncmp (result + 16, "Flatbed Scanner ", 16) == 0))
+ {
+ DBG (6, "Found BlackWidow BW4800SP scanner, setting up like AT3\n");
+
+ /* setup the vendor and product to mimic the Artec/Ultima AT3 */
+ strncpy (result + 8, "ULTIMA", 6);
+ strncpy (result + 16, "AT3 ", 16);
+ }
+
+ /*
+ * The Plustek 19200S is actually a rebadged AM12S, with the vendor string
+ * set to 8 spaces.
+ */
+ if ((strncmp (result + 8, " ", 8) == 0) &&
+ (strncmp (result + 16, "SCAN19200 ", 16) == 0))
+ {
+ DBG (6, "Found Plustek 19200S scanner, setting up like AM12S\n");
+
+ /* setup the vendor and product to mimic the Artec/Ultima AM12S */
+ strncpy (result + 8, "ULTIMA", 6);
+ strncpy (result + 16, "AM12S ", 16);
+ }
+
+ /*
+ * Check to see if they have forced a vendor and/or model string and
+ * if so, fudge the inquiry results with that info. We do this right
+ * before we check the inquiry results, otherwise we might not be forcing
+ * anything.
+ */
+ if (artec_vendor[0] != 0x0)
+ {
+ /*
+ * 1) copy the vendor string to our temp variable
+ * 2) append 8 spaces to make sure we have at least 8 characters
+ * 3) copy our fudged vendor string into the inquiry result.
+ */
+ strcpy (temp_result, artec_vendor);
+ strcat (temp_result, " ");
+ strncpy (result + 8, temp_result, 8);
+ }
+
+ if (artec_model[0] != 0x0)
+ {
+ /*
+ * 1) copy the model string to our temp variable
+ * 2) append 16 spaces to make sure we have at least 16 characters
+ * 3) copy our fudged model string into the inquiry result.
+ */
+ strcpy (temp_result, artec_model);
+ strcat (temp_result, " ");
+ strncpy (result + 16, temp_result, 16);
+ }
+
+ /* are we really dealing with a scanner by ULTIMA/ARTEC? */
+ if ((strncmp (result + 8, "ULTIMA", 6) != 0) &&
+ (strncmp (result + 8, "ARTEC", 5) != 0))
+ {
+ DBG (1, "attach: device doesn't look like a Artec/ULTIMA scanner\n");
+
+ strncpy (temp_result, result + 8, 8);
+ temp_result[8] = 0x0;
+ DBG (1, "attach: FOUND vendor = '%s'\n", temp_result);
+ strncpy (temp_result, result + 16, 16);
+ temp_result[16] = 0x0;
+ DBG (1, "attach: FOUND model = '%s'\n", temp_result);
+
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* turn this wait OFF for now since it appears to cause problems with */
+ /* AT12 models */
+ /* turned off by creating an "if" that can never be true */
+ if ( 1 == 2 ) {
+ DBG (6, "attach: wait for scanner to come ready\n");
+ status = wait_ready (fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+ /* This is the end of the "if" that can never be true that in effect */
+ /* comments out this wait_ready() call */
+ }
+ /* end of "if( 1 == 2 )" */
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+
+ memset (dev, 0, sizeof (*dev));
+
+ if (DBG_LEVEL >= 4)
+ dump_inquiry ((unsigned char *) result);
+
+ dev->sane.name = strdup (devname);
+
+ /* get the model info */
+ str = malloc (17);
+ memcpy (str, result + 16, 16);
+ str[16] = ' ';
+ t = str + 16;
+ while ((*t == ' ') && (t > str))
+ {
+ *t = '\0';
+ t--;
+ }
+ dev->sane.model = str;
+
+ /* for some reason, the firmware revision is in the model info string on */
+ /* the A6000C PLUS scanners instead of in it's proper place */
+ if (strstr (str, "A6000C PLUS") == str)
+ {
+ str[11] = '\0';
+ strncpy (product_revision, str + 12, 4);
+ }
+ else if (strstr (str, "AT3") == str)
+ {
+ str[3] = '\0';
+ strncpy (product_revision, str + 8, 4);
+ }
+ else
+ {
+ /* get the product revision from it's normal place */
+ strncpy (product_revision, result + 32, 4);
+ }
+ product_revision[4] = ' ';
+ t = strchr (product_revision, ' ');
+ if (t)
+ *t = '\0';
+ else
+ t = "unknown revision";
+
+ /* get the vendor info */
+ str = malloc (9);
+ memcpy (str, result + 8, 8);
+ str[8] = ' ';
+ t = strchr (str, ' ');
+ *t = '\0';
+ dev->sane.vendor = str;
+
+ DBG (5, "scanner vendor: '%s', model: '%s', revision: '%s'\n",
+ dev->sane.vendor, dev->sane.model, product_revision);
+
+ /* Artec docs say if bytes 36-43 = "ULTIMA ", then supports read cap. data */
+ if (strncmp (result + 36, "ULTIMA ", 8) == 0)
+ {
+ DBG (5, "scanner supports read capability data function\n");
+ dev->support_cap_data_retrieve = SANE_TRUE;
+ }
+ else
+ {
+ DBG (5, "scanner does NOT support read capability data function\n");
+ dev->support_cap_data_retrieve = SANE_FALSE;
+ }
+
+ DBG (6, "attach: getting scanner capability data\n");
+ status = artec_get_cap_data (dev, fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: artec_get_cap_data failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ sanei_scsi_close (fd);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+init_options (ARTEC_Scanner * s)
+{
+ int i;
+
+ DBG (7, "init_options()\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[3]);
+
+ /* horizontal resolution */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->horz_resolution_list;
+ s->val[OPT_X_RESOLUTION].w = 100;
+
+ /* vertical resolution */
+ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->vert_resolution_list;
+ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_Y_RESOLUTION].w = 100;
+
+ /* bind resolution */
+ s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ s->val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
+
+ if (!(s->hw->flags & ARTEC_FLAG_SEPARATE_RES))
+ s->opt[OPT_RESOLUTION_BIND].cap |= SANE_CAP_INACTIVE;
+
+ /* Preview Mode */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_PREVIEW].size = sizeof (SANE_Word);
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Grayscale Preview Mode */
+ s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_GRAY_PREVIEW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GRAY_PREVIEW].size = sizeof (SANE_Word);
+ s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
+
+ /* negative */
+ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ s->opt[OPT_NEGATIVE].desc = "Negative Image";
+ s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ s->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE))
+ {
+ s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = s->hw->x_range.min;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = s->hw->y_range.min;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range.max;
+
+ /* Enhancement group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* filter mode */
+ s->opt[OPT_FILTER_TYPE].name = "filter-type";
+ s->opt[OPT_FILTER_TYPE].title = "Filter Type";
+ s->opt[OPT_FILTER_TYPE].desc = "Filter Type for mono scans";
+ s->opt[OPT_FILTER_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_FILTER_TYPE].size = max_string_size (filter_type_list);
+ s->opt[OPT_FILTER_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FILTER_TYPE].constraint.string_list = filter_type_list;
+ s->val[OPT_FILTER_TYPE].s = strdup (filter_type_list[0]);
+ s->opt[OPT_FILTER_TYPE].cap |= SANE_CAP_INACTIVE;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &s->hw->brightness_range;
+ s->val[OPT_CONTRAST].w = 0x80;
+
+ if (!(s->hw->flags & ARTEC_FLAG_OPT_CONTRAST))
+ {
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->contrast_range;
+ s->val[OPT_BRIGHTNESS].w = 0x80;
+
+ if (!(s->hw->flags & ARTEC_FLAG_OPT_BRIGHTNESS))
+ {
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &s->hw->threshold_range;
+ s->val[OPT_THRESHOLD].w = 0x80;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ /* halftone pattern */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list);
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list;
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[1]);
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ /* pixel averaging */
+ s->opt[OPT_PIXEL_AVG].name = "pixel-avg";
+ s->opt[OPT_PIXEL_AVG].title = "Pixel Averaging";
+ s->opt[OPT_PIXEL_AVG].desc = "Enable HardWare Pixel Averaging function";
+ s->opt[OPT_PIXEL_AVG].type = SANE_TYPE_BOOL;
+ s->val[OPT_PIXEL_AVG].w = SANE_FALSE;
+
+ if (!(s->hw->flags & ARTEC_FLAG_PIXEL_AVERAGING))
+ {
+ s->opt[OPT_PIXEL_AVG].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* lineart line edge enhancement */
+ s->opt[OPT_EDGE_ENH].name = "edge-enh";
+ s->opt[OPT_EDGE_ENH].title = "Line Edge Enhancement";
+ s->opt[OPT_EDGE_ENH].desc = "Enable HardWare Lineart Line Edge Enhancement";
+ s->opt[OPT_EDGE_ENH].type = SANE_TYPE_BOOL;
+ s->val[OPT_EDGE_ENH].w = SANE_FALSE;
+ s->opt[OPT_EDGE_ENH].cap |= SANE_CAP_INACTIVE;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR].wa = &(s->gamma_table[0][0]);
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->opt[OPT_GAMMA_VECTOR].size = s->gamma_length * sizeof (SANE_Word);
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &(s->gamma_table[1][0]);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_R].size = s->gamma_length * sizeof (SANE_Word);
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &(s->gamma_table[2][0]);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_G].size = s->gamma_length * sizeof (SANE_Word);
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &(s->gamma_table[3][0]);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof (SANE_Word);
+
+ if (s->hw->flags & ARTEC_FLAG_GAMMA_SINGLE)
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (!(s->hw->flags & ARTEC_FLAG_GAMMA))
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* transparency */
+ s->opt[OPT_TRANSPARENCY].name = "transparency";
+ s->opt[OPT_TRANSPARENCY].title = "Transparency";
+ s->opt[OPT_TRANSPARENCY].desc = "Use transparency adaptor";
+ s->opt[OPT_TRANSPARENCY].type = SANE_TYPE_BOOL;
+ s->val[OPT_TRANSPARENCY].w = SANE_FALSE;
+
+ /* ADF */
+ s->opt[OPT_ADF].name = "adf";
+ s->opt[OPT_ADF].title = "ADF";
+ s->opt[OPT_ADF].desc = "Use ADF";
+ s->opt[OPT_ADF].type = SANE_TYPE_BOOL;
+ s->val[OPT_ADF].w = SANE_FALSE;
+
+ /* Calibration group: */
+ s->opt[OPT_CALIBRATION_GROUP].title = "Calibration";
+ s->opt[OPT_CALIBRATION_GROUP].desc = "";
+ s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CALIBRATION_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Calibrate Every Scan? */
+ s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].title = "Hardware Calibrate Every Scan";
+ s->opt[OPT_QUALITY_CAL].desc = "Perform hardware calibration on every scan";
+ s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL;
+ s->val[OPT_QUALITY_CAL].w = SANE_FALSE;
+
+ if (!(s->hw->flags & ARTEC_FLAG_CALIBRATE))
+ {
+ s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* Perform Software Quality Calibration */
+ s->opt[OPT_SOFTWARE_CAL].name = "software-cal";
+ s->opt[OPT_SOFTWARE_CAL].title = "Software Color Calibration";
+ s->opt[OPT_SOFTWARE_CAL].desc = "Perform software quality calibration in "
+ "addition to hardware calibration";
+ s->opt[OPT_SOFTWARE_CAL].type = SANE_TYPE_BOOL;
+ s->val[OPT_SOFTWARE_CAL].w = SANE_FALSE;
+
+ /* check for RGB calibration now because we have only implemented software */
+ /* calibration in conjunction with hardware RGB calibration */
+ if ((!(s->hw->flags & ARTEC_FLAG_CALIBRATE)) ||
+ (!(s->hw->flags & ARTEC_FLAG_CALIBRATE_RGB)))
+ {
+ s->opt[OPT_SOFTWARE_CAL].cap |= SANE_CAP_INACTIVE;
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+do_cancel (ARTEC_Scanner * s)
+{
+ DBG (7, "do_cancel()\n");
+
+ s->scanning = SANE_FALSE;
+
+ /* DAL: Terminate a three pass scan properly */
+/* if (s->this_pass == 3) */
+ s->this_pass = 0;
+
+ if ((s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) &&
+ (tmp_line_buf != NULL))
+ {
+ artec_buffer_line_offset_free ();
+ }
+
+ if (s->fd >= 0)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ return (SANE_STATUS_CANCELLED);
+}
+
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (7, "attach_one()\n");
+
+ attach (dev, 0);
+ return (SANE_STATUS_GOOD);
+}
+
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX], *cp;
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT ();
+
+ DBG (1, "Artec/Ultima backend version %d.%d.%d, last mod: %s\n",
+ ARTEC_MAJOR, ARTEC_MINOR, ARTEC_SUB, ARTEC_LAST_MOD);
+ DBG (1, "http://www4.infi.net/~cpinkham/sane-artec-doc.html\n");
+
+ DBG (7, "sane_init()\n" );
+
+ devlist = 0;
+ /* make sure these 2 are empty */
+ strcpy (artec_vendor, "");
+ strcpy (artec_model, "");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ if (authorize)
+ DBG (7, "sane_init(), authorize %s null\n", (authorize) ? "!=" : "==");
+
+ fp = sanei_config_open (ARTEC_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ cp = artec_skip_whitespace (dev_name);
+
+ /* ignore line comments and blank lines */
+ if ((!*cp) || (*cp == '#'))
+ continue;
+
+ len = strlen (cp);
+
+ /* ignore empty lines */
+ if (!len)
+ continue;
+
+ DBG (50, "%s line: '%s', len = %lu\n", ARTEC_CONFIG_FILE, cp,
+ (u_long) len);
+
+ /* check to see if they forced a vendor string in artec.conf */
+ if ((strncmp (cp, "vendor", 6) == 0) && isspace (cp[6]))
+ {
+ cp += 7;
+ cp = artec_skip_whitespace (cp);
+
+ strcpy (artec_vendor, cp);
+ DBG (5, "sane_init: Forced vendor string '%s' in %s.\n",
+ cp, ARTEC_CONFIG_FILE);
+ }
+ /* OK, maybe they forced the model string in artec.conf */
+ else if ((strncmp (cp, "model", 5) == 0) && isspace (cp[5]))
+ {
+ cp += 6;
+ cp = artec_skip_whitespace (cp);
+
+ strcpy (artec_model, cp);
+ DBG (5, "sane_init: Forced model string '%s' in %s.\n",
+ cp, ARTEC_CONFIG_FILE);
+ }
+ /* well, nothing else to do but attempt the attach */
+ else
+ {
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ strcpy (artec_vendor, "");
+ strcpy (artec_model, "");
+ }
+ }
+ fclose (fp);
+
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_exit (void)
+{
+ ARTEC_Device *dev, *next;
+
+ DBG (7, "sane_exit()\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+
+ if (devlist)
+ free (devlist);
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ ARTEC_Device *dev;
+ int i;
+
+ DBG (7, "sane_get_devices( device_list, local_only = %d )\n", local_only );
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status;
+ ARTEC_Device *dev;
+ ARTEC_Scanner *s;
+ int i, j;
+
+ DBG (7, "sane_open()\n");
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->fd = -1;
+ s->hw = dev;
+ s->this_pass = 0;
+
+ s->gamma_length = s->hw->gamma_length;
+ s->gamma_range.min = 0;
+ s->gamma_range.max = s->gamma_length - 1;
+ s->gamma_range.quant = 0;
+
+ /* not sure if I need this or not, it was in the umax backend though. :-) */
+ for (j = 0; j < s->gamma_length; ++j)
+ {
+ s->gamma_table[0][j] = j * (s->gamma_length - 1) / s->gamma_length;
+ }
+
+ for (i = 1; i < 4; i++)
+ {
+ for (j = 0; j < s->gamma_length; ++j)
+ {
+ s->gamma_table[i][j] = j;
+ }
+ }
+
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ if (s->hw->flags & ARTEC_FLAG_CALIBRATE)
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "error opening scanner for initial calibration: %s\n",
+ sane_strstatus (status));
+ s->fd = -1;
+ return status;
+ }
+
+ status = artec_calibrate_shading (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "initial shading calibration failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ sanei_scsi_close (s->fd);
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ ARTEC_Scanner *prev, *s;
+
+ DBG (7, "sane_close()\n");
+
+ if ((DBG_LEVEL == 101) &&
+ (debug_fd > -1))
+ {
+ close (debug_fd);
+ DBG (101, "closed artec.data.raw output file\n");
+ }
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ do_cancel (handle);
+
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ ARTEC_Scanner *s = handle;
+
+ DBG (7, "sane_get_option_descriptor()\n");
+
+ if (((unsigned) option >= NUM_OPTIONS) ||
+ (option < 0 ))
+ return (0);
+
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ ARTEC_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word w, cap;
+
+ DBG (7, "sane_control_option()\n");
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (s->this_pass)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (13, "sane_control_option %d, get value\n", option);
+
+ switch (option)
+ {
+ /* word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_RESOLUTION_BIND:
+ case OPT_NEGATIVE:
+ case OPT_TRANSPARENCY:
+ case OPT_ADF:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_QUALITY_CAL:
+ case OPT_SOFTWARE_CAL:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_PIXEL_AVG:
+ case OPT_EDGE_ENH:
+ *(SANE_Word *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_FILTER_TYPE:
+ case OPT_HALFTONE_PATTERN:
+ strcpy (val, s->val[option].s);
+ return (SANE_STATUS_GOOD);
+
+ /* word array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (13, "sane_control_option %d, set value\n", option);
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ /* fall through */
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_QUALITY_CAL:
+ case OPT_SOFTWARE_CAL:
+ case OPT_NUM_OPTS:
+ case OPT_NEGATIVE:
+ case OPT_TRANSPARENCY:
+ case OPT_ADF:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_PIXEL_AVG:
+ case OPT_EDGE_ENH:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_MODE:
+ {
+ if (s->val[option].s)
+ free (s->val[option].s);
+
+ s->val[option].s = (SANE_Char *) strdup (val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* options INvisible by default */
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SOFTWARE_CAL].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_EDGE_ENH].cap |= SANE_CAP_INACTIVE;
+
+ /* options VISIBLE by default */
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_FILTER_TYPE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE;
+
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ /* Lineart mode */
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* OFF */
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+
+ if (s->hw->flags & ARTEC_FLAG_ENHANCE_LINE_EDGE)
+ s->opt[OPT_EDGE_ENH].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ {
+ /* Halftone mode */
+ if (s->hw->flags & ARTEC_FLAG_HALFTONE_PATTERN)
+ s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ /* Grayscale mode */
+ if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE))
+ {
+ s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ /* Color mode */
+ s->opt[OPT_FILTER_TYPE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SOFTWARE_CAL].cap &= ~SANE_CAP_INACTIVE;
+ if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE))
+ {
+ s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ return (SANE_STATUS_GOOD);
+
+ case OPT_FILTER_TYPE:
+ case OPT_HALFTONE_PATTERN:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_RESOLUTION_BIND:
+ if (s->val[option].w != *(SANE_Word *) val)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ if (s->val[option].w == SANE_FALSE)
+ { /* don't bind */
+ s->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_X_RESOLUTION].title =
+ SANE_TITLE_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].name =
+ SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc =
+ SANE_DESC_SCAN_X_RESOLUTION;
+ }
+ else
+ { /* bind */
+ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_X_RESOLUTION].title =
+ SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].name =
+ SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc =
+ SANE_DESC_SCAN_RESOLUTION;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return (SANE_STATUS_GOOD);
+
+ /* options with side effects: */
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return (SANE_STATUS_GOOD);
+
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ if (w) /* use custom_gamma_table */
+ {
+ const char *mode = s->val[OPT_MODE].s;
+
+ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) ||
+ (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) ||
+ (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0))
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+
+ if (!(s->hw->flags & ARTEC_FLAG_GAMMA_SINGLE))
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ else
+ /* don't use custom_gamma_table */
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ return (SANE_STATUS_INVAL);
+}
+
+static void
+set_pass_parameters (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+
+ DBG (7, "set_pass_parameters()\n");
+
+ if (s->threepasscolor)
+ {
+ s->this_pass += 1;
+ DBG (9, "set_pass_parameters: three-pass, on %d\n", s->this_pass);
+ switch (s->this_pass)
+ {
+ case 1:
+ s->params.format = SANE_FRAME_RED;
+ s->params.last_frame = SANE_FALSE;
+ break;
+ case 2:
+ s->params.format = SANE_FRAME_GREEN;
+ s->params.last_frame = SANE_FALSE;
+ break;
+ case 3:
+ s->params.format = SANE_FRAME_BLUE;
+ s->params.last_frame = SANE_TRUE;
+ break;
+ default:
+ DBG (9, "set_pass_parameters: What?!? pass %d = filter?\n",
+ s->this_pass);
+ break;
+ }
+ }
+ else
+ s->this_pass = 0;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ ARTEC_Scanner *s = handle;
+
+ DBG (7, "sane_get_parameters()\n");
+
+ if (!s->scanning)
+ {
+ double width, height;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ s->x_resolution = s->val[OPT_X_RESOLUTION].w;
+ s->y_resolution = s->val[OPT_Y_RESOLUTION].w;
+
+ if ((s->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) ||
+ (s->val[OPT_PREVIEW].w == SANE_TRUE))
+ {
+ s->y_resolution = s->x_resolution;
+ }
+
+ s->tl_x = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH
+ * s->x_resolution;
+ s->tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH
+ * s->y_resolution;
+ width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
+ height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
+
+ if ((s->x_resolution > 0.0) &&
+ (s->y_resolution > 0.0) &&
+ (width > 0.0) &&
+ (height > 0.0))
+ {
+ s->params.pixels_per_line = width * s->x_resolution / MM_PER_INCH + 1;
+ s->params.lines = height * s->y_resolution / MM_PER_INCH + 1;
+ }
+
+ s->onepasscolor = SANE_FALSE;
+ s->threepasscolor = SANE_FALSE;
+ s->params.last_frame = SANE_TRUE;
+
+ if ((s->val[OPT_PREVIEW].w == SANE_TRUE) &&
+ (s->val[OPT_GRAY_PREVIEW].w == SANE_TRUE))
+ {
+ s->mode = SANE_VALUE_SCAN_MODE_GRAY;
+ }
+ else
+ {
+ s->mode = s->val[OPT_MODE].s;
+ }
+
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) ||
+ (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ s->line_offset = 0;
+
+ /* round pixels_per_line up to the next full byte of pixels */
+ /* this way we don't have to do bit buffering, pixels_per_line is */
+ /* what is used in the set window command. */
+ /* SANE expects the last byte in a line to be padded if it's not */
+ /* full, so this should not affect scans in a negative way */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ }
+ else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->line_offset = 0;
+ }
+ else
+ {
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+
+ if (s->hw->flags & ARTEC_FLAG_ONE_PASS_SCANNER)
+ {
+ s->onepasscolor = SANE_TRUE;
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line *= 3;
+
+ /*
+ * line offsets from documentation.
+ * (I don't yet see a common formula I can easily use)
+ */
+ /* FIXME: figure out a cleaner way to do this... */
+ s->line_offset = 0; /* default */
+ if ((!strcmp (s->hw->sane.model, "AT3")) ||
+ (!strcmp (s->hw->sane.model, "A6000C")) ||
+ (!strcmp (s->hw->sane.model, "A6000C PLUS")) ||
+ (!strcmp (s->hw->sane.model, "AT6")))
+ {
+ /* formula #1 */
+ /* ranges from 1 at 50dpi to 16 at 600dpi */
+ s->line_offset = 8 * (s->y_resolution / 300.0);
+ }
+ else if (!strcmp (s->hw->sane.model, "AT12"))
+ {
+ /* formula #2 */
+ /* ranges from 0 at 25dpi to 16 at 1200dpi */
+ /***********************************************************/
+ /* this should be handled in hardware for now, so leave it */
+ /* sitting at zero for now. */
+ /***********************************************************/
+ /*
+ s->line_offset = 16 * ( s->y_resolution / 1200.0 );
+ */
+ }
+ else if (!strcmp (s->hw->sane.model, "AM12S"))
+ {
+ /* formula #3 */
+ /* ranges from 0 at 50dpi to 8 at 1200dpi */
+ s->line_offset = 8 * (s->y_resolution / 1200.0);
+ }
+ }
+ else
+ {
+ s->params.last_frame = SANE_FALSE;
+ s->threepasscolor = SANE_TRUE;
+ s->line_offset = 0;
+ }
+ }
+ }
+
+ if (params)
+ *params = s->params;
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (7, "sane_start()\n");
+
+ if (debug_fd != -1)
+ {
+ close (debug_fd);
+ debug_fd = -1;
+ }
+
+ if (DBG_LEVEL == 101)
+ {
+ debug_fd = open ("artec.data.raw",
+ O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (debug_fd > -1)
+ DBG (101, "opened artec.data.raw output file\n");
+ }
+
+ /* First make sure we have a current parameter set. Some of the */
+ /* parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* DAL: For 3 pass colour set the current pass parameters */
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && s->threepasscolor)
+ set_pass_parameters (s);
+
+ /* DAL: For single pass scans and the first pass of a 3 pass scan */
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) ||
+ (!s->threepasscolor) ||
+ ((s->threepasscolor) &&
+ (s->this_pass == 1)))
+ {
+
+ if (s->hw->flags & ARTEC_FLAG_SENSE_HANDLER)
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler,
+ (void *)s);
+ }
+ else
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0);
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return status;
+ }
+
+ /* DB added wait_ready */
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "wait for scanner ready failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+ DBG (9, "%d pixels per line, %d bytes, %d lines high, xdpi = %d, "
+ "ydpi = %d, btr = %lu\n",
+ s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines,
+ s->x_resolution, s->y_resolution, (u_long) s->bytes_to_read);
+
+ /* DAL: For single pass scans and the first pass of a 3 pass scan */
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || !s->threepasscolor ||
+ (s->threepasscolor && s->this_pass == 1))
+ {
+
+ /* do a calibrate if scanner requires/recommends it */
+ if ((s->hw->flags & ARTEC_FLAG_CALIBRATE) &&
+ (s->val[OPT_QUALITY_CAL].w == SANE_TRUE))
+ {
+ status = artec_calibrate_shading (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "shading calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* DB added wait_ready */
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "wait for scanner ready failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send the custom gamma table if we have one */
+ if (s->hw->flags & ARTEC_FLAG_GAMMA)
+ artec_send_gamma_table (s);
+
+ /* now set our scan window */
+ status = artec_set_scan_window (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "set scan window failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* DB added wait_ready */
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "wait for scanner ready failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* now we can start the actual scan */
+ /* DAL: For single pass scans and the first pass of a 3 pass scan */
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) ||
+ (!s->threepasscolor) ||
+ (s->this_pass == 1))
+ {
+ /* DAL - do mode select before each scan */
+ /* The mode is NOT turned off at the end of the scan */
+ artec_mode_select (s);
+
+ status = artec_start_scan (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start scan: %s\n", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ s->scanning = SANE_TRUE;
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+#if 0
+static void
+binout (SANE_Byte byte)
+{
+ SANE_Byte b = byte;
+ int bit;
+
+ for (bit = 0; bit < 8; bit++)
+ {
+ DBG (9, "%d", b & 128 ? 1 : 0);
+ b = b << 1;
+ }
+}
+#endif
+
+static SANE_Status
+artec_sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ ARTEC_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ size_t lread;
+ size_t bytes_read;
+ size_t rows_read;
+ size_t max_read_rows;
+ size_t max_ret_rows;
+ size_t remaining_rows;
+ size_t rows_available;
+ size_t line;
+ SANE_Byte temp_buf[ARTEC_MAX_READ_SIZE];
+ SANE_Byte line_buf[ARTEC_MAX_READ_SIZE];
+
+
+ DBG (7, "artec_sane_read( %p, %p, %d, %d )\n", handle, buf, max_len, *len);
+
+ *len = 0;
+
+ if (s->bytes_to_read == 0)
+ {
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || !s->threepasscolor ||
+ (s->threepasscolor && s->this_pass == 3))
+ {
+ do_cancel (s);
+ /* without this a 4th pass is attempted, yet do_cancel does this */
+ s->scanning = SANE_FALSE;
+ }
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ return do_cancel (s);
+
+ remaining_rows = (s->bytes_to_read + s->params.bytes_per_line - 1) / s->params.bytes_per_line;
+ max_read_rows = s->hw->max_read_size / s->params.bytes_per_line;
+ max_ret_rows = max_len / s->params.bytes_per_line;
+
+ while (artec_get_status (s->fd) == 0)
+ {
+ DBG (120, "hokey loop till data available\n");
+ usleep (50000); /* sleep for .05 second */
+ }
+
+ rows_read = 0;
+ bytes_read = 0;
+ while ((rows_read < max_ret_rows) && (rows_read < remaining_rows))
+ {
+ DBG (50, "top of while loop, rr = %lu, mrr = %lu, rem = %lu\n",
+ (u_long) rows_read, (u_long) max_ret_rows, (u_long) remaining_rows);
+
+ if (s->bytes_to_read - bytes_read <= s->params.bytes_per_line * max_read_rows)
+ {
+ nread = s->bytes_to_read - bytes_read;
+ }
+ else
+ {
+ nread = s->params.bytes_per_line * max_read_rows;
+ }
+ lread = nread / s->params.bytes_per_line;
+
+ if ((max_read_rows - rows_read) < lread)
+ {
+ lread = max_read_rows - rows_read;
+ nread = lread * s->params.bytes_per_line;
+ }
+
+ if ((max_ret_rows - rows_read) < lread)
+ {
+ lread = max_ret_rows - rows_read;
+ nread = lread * s->params.bytes_per_line;
+ }
+
+ while ((rows_available = artec_get_status (s->fd)) == 0)
+ {
+ DBG (120, "hokey loop till data available\n");
+ usleep (50000); /* sleep for .05 second */
+ }
+
+ if (rows_available < lread)
+ {
+ lread = rows_available;
+ nread = lread * s->params.bytes_per_line;
+ }
+
+ /* This should never happen, but just in case... */
+ if (nread > (s->bytes_to_read - bytes_read))
+ {
+ nread = s->bytes_to_read - bytes_read;
+ lread = 1;
+ }
+
+ DBG (50, "rows_available = %lu, params.lines = %d, bytes_per_line = %d\n",
+ (u_long) rows_available, s->params.lines, s->params.bytes_per_line);
+ DBG (50, "bytes_to_read = %lu, max_len = %d, max_rows = %lu\n",
+ (u_long) s->bytes_to_read, max_len, (u_long) max_ret_rows);
+ DBG (50, "nread = %lu, lread = %lu, bytes_read = %lu, rows_read = %lu\n",
+ (u_long) nread, (u_long) lread, (u_long) bytes_read, (u_long) rows_read);
+
+ status = read_data (s->fd, ARTEC_DATA_IMAGE, temp_buf, &nread);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ end_scan (s);
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if ((DBG_LEVEL == 101) &&
+ (debug_fd > -1))
+ {
+ write (debug_fd, temp_buf, nread);
+ }
+
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) &&
+ (s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET))
+ {
+ for (line = 0; line < lread; line++)
+ {
+ memcpy (line_buf,
+ temp_buf + (line * s->params.bytes_per_line),
+ s->params.bytes_per_line);
+
+ nread = s->params.bytes_per_line;
+
+ artec_buffer_line_offset (s, s->line_offset, line_buf, &nread);
+
+ if (nread > 0)
+ {
+ if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)
+ {
+ artec_line_rgb_to_byte_rgb (line_buf,
+ s->params.pixels_per_line);
+ }
+ if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR)
+ {
+ artec_reverse_line (s, line_buf);
+ }
+
+ /* do software calibration if necessary */
+ if (s->val[OPT_SOFTWARE_CAL].w)
+ {
+ artec_software_rgb_calibrate (s, line_buf, 1);
+ }
+
+ memcpy (buf + bytes_read, line_buf,
+ s->params.bytes_per_line);
+ bytes_read += nread;
+ rows_read++;
+ }
+ }
+ }
+ else
+ {
+ if ((s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) ||
+ ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) &&
+ (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)))
+ {
+ for (line = 0; line < lread; line++)
+ {
+ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) &&
+ (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT))
+ {
+ artec_line_rgb_to_byte_rgb (temp_buf +
+ (line * s->params.bytes_per_line),
+ s->params.pixels_per_line);
+ }
+ if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR)
+ {
+ artec_reverse_line (s, temp_buf +
+ (line * s->params.bytes_per_line));
+ }
+ }
+ }
+
+ /* do software calibration if necessary */
+ if ((s->val[OPT_SOFTWARE_CAL].w) &&
+ (strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0))
+ {
+ artec_software_rgb_calibrate (s, temp_buf, lread);
+ }
+
+ memcpy (buf + bytes_read, temp_buf, nread);
+ bytes_read += nread;
+ rows_read += lread;
+ }
+ }
+
+ *len = bytes_read;
+ s->bytes_to_read -= bytes_read;
+
+ DBG (9, "artec_sane_read() returning, we read %lu bytes, %lu left\n",
+ (u_long) * len, (u_long) s->bytes_to_read);
+
+ if ((s->bytes_to_read == 0) &&
+ (s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) &&
+ (tmp_line_buf != NULL))
+ {
+ artec_buffer_line_offset_free ();
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ ARTEC_Scanner *s = handle;
+ SANE_Status status;
+ int bytes_to_copy;
+ int loop;
+
+ static SANE_Byte temp_buf[ARTEC_MAX_READ_SIZE];
+ static int bytes_in_buf = 0;
+
+ DBG (7, "sane_read( %p, %p, %d, %d )\n", handle, buf, max_len, *len);
+ DBG (9, "sane_read: bib = %d, ml = %d\n", bytes_in_buf, max_len);
+
+ if (bytes_in_buf != 0)
+ {
+ bytes_to_copy = max_len < bytes_in_buf ? max_len : bytes_in_buf;
+ }
+ else
+ {
+ status = artec_sane_read (s, temp_buf, s->hw->max_read_size, len);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+
+ bytes_in_buf = *len;
+
+ if (*len == 0)
+ {
+ return (SANE_STATUS_GOOD);
+ }
+
+ bytes_to_copy = max_len < s->hw->max_read_size ?
+ max_len : s->hw->max_read_size;
+ bytes_to_copy = *len < bytes_to_copy ? *len : bytes_to_copy;
+ }
+
+ memcpy (buf, temp_buf, bytes_to_copy);
+ bytes_in_buf -= bytes_to_copy;
+ *len = bytes_to_copy;
+
+ DBG (9, "sane_read: btc = %d, bib now = %d\n",
+ bytes_to_copy, bytes_in_buf);
+
+ for (loop = 0; loop < bytes_in_buf; loop++)
+ {
+ temp_buf[loop] = temp_buf[loop + bytes_to_copy];
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ ARTEC_Scanner *s = handle;
+
+ DBG (7, "sane_cancel()\n");
+
+ if (s->scanning)
+ {
+ s->scanning = SANE_FALSE;
+
+ abort_scan (s);
+
+ do_cancel (s);
+ }
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (7, "sane_set_io_mode( %p, %d )\n", handle, non_blocking);
+
+ return (SANE_STATUS_UNSUPPORTED);
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (7, "sane_get_select_fd( %p, %d )\n", handle, *fd );
+
+ return (SANE_STATUS_UNSUPPORTED);
+}
diff --git a/backend/artec.conf.in b/backend/artec.conf.in
new file mode 100644
index 0000000..371a174
--- /dev/null
+++ b/backend/artec.conf.in
@@ -0,0 +1,3 @@
+scsi ULTIMA
+/dev/scanner
+
diff --git a/backend/artec.h b/backend/artec.h
new file mode 100644
index 0000000..efb4bb4
--- /dev/null
+++ b/backend/artec.h
@@ -0,0 +1,272 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Artec/Ultima scanners.
+
+ Copyright (C) 1998,1999 Chris Pinkham
+ Released under the terms of the GPL.
+ *NO WARRANTY*
+
+ *********************************************************************
+ For feedback/information:
+
+ cpinkham@corp.infi.net
+ http://www4.infi.net/~cpinkham/sane/sane-artec-doc.html
+ *********************************************************************
+ */
+
+#ifndef artec_h
+#define artec_h
+
+#include <sys/types.h>
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define ARTEC_MIN_X( hw ) ( hw->horz_resolution_list[ 0 ] ? \
+ hw->horz_resolution_list[ 1 ] : 0 )
+#define ARTEC_MAX_X( hw ) ( hw->horz_resolution_list[ 0 ] ? \
+ hw->horz_resolution_list[ \
+ hw->horz_resolution_list[ 0 ] ] : 0 )
+#define ARTEC_MIN_Y( hw ) ( hw->vert_resolution_list[ 0 ] ? \
+ hw->vert_resolution_list[ 1 ] : 0 )
+#define ARTEC_MAX_Y( hw ) ( hw->vert_resolution_list[ 0 ] ? \
+ hw->vert_resolution_list[ \
+ hw->vert_resolution_list[ 0 ] ] : 0 )
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+ OPT_RESOLUTION_BIND,
+ OPT_PREVIEW,
+ OPT_GRAY_PREVIEW,
+ OPT_NEGATIVE,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CONTRAST,
+ OPT_BRIGHTNESS,
+ OPT_THRESHOLD,
+ OPT_HALFTONE_PATTERN,
+ OPT_FILTER_TYPE,
+ OPT_PIXEL_AVG,
+ OPT_EDGE_ENH,
+
+ OPT_CUSTOM_GAMMA, /* use custom gamma table */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_TRANSPARENCY,
+ OPT_ADF,
+
+ OPT_CALIBRATION_GROUP,
+ OPT_QUALITY_CAL,
+ OPT_SOFTWARE_CAL,
+
+ /* must come last */
+ NUM_OPTIONS
+ }
+ARTEC_Option;
+
+/* Some FLAGS */
+#define ARTEC_FLAG_CALIBRATE 0x00000001 /* supports hardware calib */
+#define ARTEC_FLAG_CALIBRATE_RGB 0x00000003 /* yes 3, set CALIB. also */
+#define ARTEC_FLAG_CALIBRATE_DARK_WHITE 0x00000005 /* yes 5, set CALIB. also */
+#define ARTEC_FLAG_RGB_LINE_OFFSET 0x00000008 /* need line offset buffer */
+#define ARTEC_FLAG_RGB_CHAR_SHIFT 0x00000010 /* RRRRGGGGBBBB line fmt */
+#define ARTEC_FLAG_OPT_CONTRAST 0x00000020 /* supports set contrast */
+#define ARTEC_FLAG_ONE_PASS_SCANNER 0x00000040 /* single pass scanner */
+#define ARTEC_FLAG_GAMMA 0x00000080 /* supports set gamma */
+#define ARTEC_FLAG_GAMMA_SINGLE 0x00000180 /* yes 180, implies GAMMA */
+#define ARTEC_FLAG_SEPARATE_RES 0x00000200 /* separate x & y scan res */
+#define ARTEC_FLAG_IMAGE_REV_LR 0x00000400 /* reversed left-right */
+#define ARTEC_FLAG_ENHANCE_LINE_EDGE 0x00000800 /* line edge enhancement */
+#define ARTEC_FLAG_HALFTONE_PATTERN 0x00001000 /* > 1 halftone pattern */
+#define ARTEC_FLAG_REVERSE_WINDOW 0x00002000 /* reverse selected area */
+#define ARTEC_FLAG_SC_BUFFERS_LINES 0x00004000 /* scanner has line buffer */
+#define ARTEC_FLAG_SC_HANDLES_OFFSET 0x00008000 /* sc. handles line offset */
+#define ARTEC_FLAG_SENSE_HANDLER 0x00010000 /* supports sense handler */
+#define ARTEC_FLAG_SENSE_ENH_18 0x00020000 /* supports enh. byte 18 */
+#define ARTEC_FLAG_SENSE_BYTE_19 0x00040000 /* supports sense byte 19 */
+#define ARTEC_FLAG_SENSE_BYTE_22 0x00080000 /* supports sense byte 22 */
+#define ARTEC_FLAG_PIXEL_AVERAGING 0x00100000 /* supports pixel avg-ing */
+#define ARTEC_FLAG_ADF 0x00200000 /* auto document feeder */
+#define ARTEC_FLAG_OPT_BRIGHTNESS 0x00400000 /* supports set brightness */
+#define ARTEC_FLAG_MBPP_NEGATIVE 0x00800000 /* can negate > 1bpp modes */
+
+typedef enum
+ {
+ ARTEC_COMP_LINEART = 0,
+ ARTEC_COMP_HALFTONE,
+ ARTEC_COMP_GRAY,
+ ARTEC_COMP_UNSUPP1,
+ ARTEC_COMP_UNSUPP2,
+ ARTEC_COMP_COLOR
+ }
+ARTEC_Image_Composition;
+
+typedef enum
+ {
+ ARTEC_DATA_IMAGE = 0,
+ ARTEC_DATA_UNSUPP1,
+ ARTEC_DATA_HALFTONE_PATTERN, /* 2 */
+ ARTEC_DATA_UNSUPP3,
+ ARTEC_DATA_RED_SHADING, /* 4 */
+ ARTEC_DATA_GREEN_SHADING, /* 5 */
+ ARTEC_DATA_BLUE_SHADING, /* 6 */
+ ARTEC_DATA_WHITE_SHADING_OPT, /* 7 */
+ ARTEC_DATA_WHITE_SHADING_TRANS, /* 8 */
+ ARTEC_DATA_CAPABILITY_DATA, /* 9 */
+ ARTEC_DATA_DARK_SHADING, /* 10, 0xA */
+ ARTEC_DATA_RED_GAMMA_CURVE, /* 11, 0xB */
+ ARTEC_DATA_GREEN_GAMMA_CURVE, /* 12, 0xC */
+ ARTEC_DATA_BLUE_GAMMA_CURVE, /* 13, 0xD */
+ ARTEC_DATA_ALL_GAMMA_CURVE /* 14, 0xE */
+ }
+ARTEC_Read_Data_Type;
+
+typedef enum
+ {
+ ARTEC_CALIB_RGB = 0,
+ ARTEC_CALIB_DARK_WHITE
+ }
+ARTEC_Calibrate_Method;
+
+typedef enum
+ {
+ ARTEC_FILTER_MONO = 0,
+ ARTEC_FILTER_RED,
+ ARTEC_FILTER_GREEN,
+ ARTEC_FILTER_BLUE
+ }
+ARTEC_Filter_Type;
+
+typedef enum
+ {
+ ARTEC_SOFT_CALIB_RED = 0,
+ ARTEC_SOFT_CALIB_GREEN,
+ ARTEC_SOFT_CALIB_BLUE
+ }
+ARTEC_Software_Calibrate;
+
+
+typedef struct ARTEC_Device
+ {
+ struct ARTEC_Device *next;
+ SANE_Device sane;
+ double width;
+ SANE_Range x_range;
+ SANE_Word *horz_resolution_list;
+ double height;
+ SANE_Range y_range;
+ SANE_Word *vert_resolution_list;
+ SANE_Range threshold_range;
+ SANE_Range contrast_range;
+ SANE_Range brightness_range;
+ SANE_Word setwindow_cmd_size;
+ SANE_Word calibrate_method;
+ SANE_Word max_read_size;
+
+ long flags;
+ SANE_Bool support_cap_data_retrieve;
+ SANE_Bool req_shading_calibrate;
+ SANE_Bool req_rgb_line_offset;
+ SANE_Bool req_rgb_char_shift;
+
+ /* info for 1-pass vs. 3-pass */
+ SANE_Bool onepass;
+
+ SANE_Bool support_gamma;
+ SANE_Bool single_gamma;
+ SANE_Int gamma_length;
+ }
+ARTEC_Device;
+
+typedef struct ARTEC_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct ARTEC_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Int gamma_table[4][4096];
+ double soft_calibrate_data[3][2592];
+ SANE_Int halftone_pattern[64];
+ SANE_Range gamma_range;
+ int gamma_length;
+
+ int scanning;
+ SANE_Parameters params;
+ size_t bytes_to_read;
+ SANE_Int line_offset;
+
+ /* scan parameters */
+ char *mode;
+ SANE_Int x_resolution;
+ SANE_Int y_resolution;
+ SANE_Int tl_x;
+ SANE_Int tl_y;
+
+ /* info for 1-pass vs. 3-pass */
+ int this_pass;
+ SANE_Bool onepasscolor;
+ SANE_Bool threepasscolor;
+
+ int fd; /* SCSI filedescriptor */
+
+ /* scanner dependent/low-level state: */
+ ARTEC_Device *hw;
+ }
+ARTEC_Scanner;
+
+#endif /* artec_h */
diff --git a/backend/artec_eplus48u.c b/backend/artec_eplus48u.c
new file mode 100644
index 0000000..c26fbb0
--- /dev/null
+++ b/backend/artec_eplus48u.c
@@ -0,0 +1,4568 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2002 Michael Herder <crapsite@gmx.net>
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+This backend is based on the "gt68xxtest" program written by the following
+persons:
+ Sergey Vlasov <vsu@mivlgu.murom.ru>
+ - Main backend code.
+
+ Andreas Nowack <nowack.andreas@gmx.de>
+ - Support for GT6801 (Mustek ScanExpress 1200 UB Plus).
+
+ David Stevenson <david.stevenson@zoom.co.uk>
+ - Automatic AFE gain and offset setting.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+Please note:
+The calibration code from the gt68xxtest program isn't used here, since I
+couldn't get it working. I'm using my own calibration code, which is based
+on wild assumptions based on the USB logs from the windoze driver.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+It also contains code from the plustek backend
+
+Copyright (C) 2000-2002 Gerhard Jaeger <g.jaeger@earthling.net>
+
+and from the mustek_usb backend
+
+Copyright (C) 2000 Mustek.
+Maintained by Tom Wang <tom.wang@mustek.com.tw>
+Updates (C) 2001 by Henning Meier-Geinitz.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#define BUILD 11
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME artec_eplus48u
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include "artec_eplus48u.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define _DEFAULT_DEVICE "/dev/usbscanner"
+#define ARTEC48U_CONFIG_FILE "artec_eplus48u.conf"
+
+#define _SHADING_FILE_BLACK "artec48ushading_black"
+#define _SHADING_FILE_WHITE "artec48ushading_white"
+#define _EXPOSURE_FILE "artec48uexposure"
+#define _OFFSET_FILE "artec48uoffset"
+
+#define _BYTE 3
+#define _STRING 2
+#define _FLOAT 1
+#define _INT 0
+
+/*for calibration*/
+#define WHITE_MIN 243*257
+#define WHITE_MAX 253*257
+#define BLACK_MIN 8*257
+#define BLACK_MAX 18*257
+#define EXPOSURE_STEP 280
+
+static Artec48U_Device *first_dev = 0;
+static Artec48U_Scanner *first_handle = 0;
+static SANE_Int num_devices = 0;
+static char devName[PATH_MAX];
+static char firmwarePath[PATH_MAX];
+static char vendor_string[PATH_MAX];
+static char model_string[PATH_MAX];
+
+static SANE_Bool cancelRead;
+static int isEPro;
+static int eProMult;
+static SANE_Auth_Callback auth = NULL;
+static double gamma_master_default = 1.7;
+static double gamma_r_default = 1.0;
+static double gamma_g_default = 1.0;
+static double gamma_b_default = 1.0;
+
+static SANE_Word memory_read_value = 0x200c; /**< Memory read - wValue */
+static SANE_Word memory_write_value = 0x200b; /**< Memory write - wValue */
+static SANE_Word send_cmd_value = 0x2010; /**< Send normal command - wValue */
+static SANE_Word send_cmd_index = 0x3f40; /**< Send normal command - wIndex */
+static SANE_Word recv_res_value = 0x2011; /**< Receive normal result - wValue */
+static SANE_Word recv_res_index = 0x3f00; /**< Receive normal result - wIndex */
+static SANE_Word send_small_cmd_value = 0x2012; /**< Send small command - wValue */
+static SANE_Word send_small_cmd_index = 0x3f40; /**< Send small command - wIndex */
+static SANE_Word recv_small_res_value = 0x2013; /**< Receive small result - wValue */
+static SANE_Word recv_small_res_index = 0x3f00; /**< Receive small result - wIndex */
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static SANE_Word resbit_list[] = {
+ 6,
+ 50, 100, 200, 300, 600, 1200
+};
+
+static SANE_Range brightness_contrast_range = {
+ -127,
+ 127,
+ 0
+};
+
+static SANE_Range blacklevel_range = {
+ 20,
+ 240,
+ 1
+};
+
+static SANE_Range gamma_range = {
+ 0, /* minimum */
+ SANE_FIX (4.0), /* maximum */
+ 0 /* quantization */
+};
+
+static SANE_Range scan_range_x = {
+ 0, /* minimum */
+ SANE_FIX (216.0), /* maximum */
+ 0 /* quantization */
+};
+
+static SANE_Range scan_range_y = {
+ 0, /* minimum */
+ SANE_FIX (297.0), /* maximum */
+ 0 /* quantization */
+};
+
+
+static SANE_Word bitdepth_list[] = {
+ 2, 8, 16
+};
+
+static SANE_Word bitdepth_list2[] = {
+ 1, 8
+};
+
+static Artec48U_Exposure_Parameters exp_params;
+static Artec48U_Exposure_Parameters default_exp_params =
+ { 0x009f, 0x0109, 0x00cb };
+static Artec48U_AFE_Parameters afe_params;
+static Artec48U_AFE_Parameters default_afe_params =
+ { 0x28, 0x0a, 0x2e, 0x03, 0x2e, 0x03 };
+
+static SANE_Status
+download_firmware_file (Artec48U_Device * chip)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte *buf = NULL;
+ int size = -1;
+ FILE *f;
+
+ XDBG ((2, "Try to open firmware file: \"%s\"\n", chip->firmware_path));
+ f = fopen (chip->firmware_path, "rb");
+ if (!f)
+ {
+ XDBG ((2, "Cannot open firmware file \"%s\"\n", firmwarePath));
+ status = SANE_STATUS_INVAL;
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ fseek (f, 0, SEEK_END);
+ size = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ if (size == -1)
+ {
+ XDBG ((2, "Error getting size of firmware file \"%s\"\n",
+ chip->firmware_path));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "firmware size: %d\n", size));
+ buf = (SANE_Byte *) malloc (size);
+ if (!buf)
+ {
+ XDBG ((2, "Cannot allocate %d bytes for firmware\n", size));
+ status = SANE_STATUS_NO_MEM;
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ int bytes_read = fread (buf, 1, size, f);
+ if (bytes_read != size)
+ {
+ XDBG ((2, "Problem reading firmware file \"%s\"\n",
+ chip->firmware_path));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (f)
+ fclose (f);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ status = artec48u_download_firmware (chip, buf, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "Firmware download failed\n"));
+ }
+ }
+
+ if (buf)
+ free (buf);
+ return status;
+}
+
+static SANE_Status
+init_calibrator (Artec48U_Scanner * s)
+{
+ XDBG ((2, "Init calibrator size %d\n",30720 * s->dev->epro_mult));
+ s->shading_buffer_w = (unsigned char *) malloc (30720 * s->dev->epro_mult); /*epro*/
+ s->shading_buffer_b = (unsigned char *) malloc (30720 * s->dev->epro_mult); /*epro*/
+ s->shading_buffer_white[0] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof(unsigned int));/*epro*/
+ s->shading_buffer_black[0] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_white[1] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_black[1] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_white[2] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_black[2] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+
+ if (!s->shading_buffer_w || !s->shading_buffer_b
+ || !s->shading_buffer_white[0] || !s->shading_buffer_black[0]
+ || !s->shading_buffer_white[1] || !s->shading_buffer_black[1]
+ || !s->shading_buffer_white[2] || !s->shading_buffer_black[2])
+ {
+ if (s->shading_buffer_w)
+ free (s->shading_buffer_w);
+ if (s->shading_buffer_b)
+ free (s->shading_buffer_b);
+ if (s->shading_buffer_white[0])
+ free (s->shading_buffer_white[0]);
+ if (s->shading_buffer_black[0])
+ free (s->shading_buffer_black[0]);
+ if (s->shading_buffer_white[1])
+ free (s->shading_buffer_white[1]);
+ if (s->shading_buffer_black[1])
+ free (s->shading_buffer_black[1]);
+ if (s->shading_buffer_white[2])
+ free (s->shading_buffer_white[2]);
+ if (s->shading_buffer_black[2])
+ free (s->shading_buffer_black[2]);
+ return SANE_STATUS_NO_MEM;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static void
+init_shading_buffer (Artec48U_Scanner * s)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ s->temp_shading_buffer[j][i] = 0;
+ }
+ }
+}
+
+static void
+add_to_shading_buffer (Artec48U_Scanner * s, unsigned int **buffer_pointers)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ s->temp_shading_buffer[j][i] += buffer_pointers[j][i];
+ }
+ }
+}
+
+static void
+finish_shading_buffer (Artec48U_Scanner * s, SANE_Bool white)
+{
+ unsigned int i, j, cnt, c, div;
+ unsigned long max_r;
+ unsigned long max_g;
+ unsigned long max_b;
+ unsigned char *shading_buffer;
+ cnt = 0;
+
+ if (white)
+ {
+ shading_buffer = s->shading_buffer_w;
+ div = s->dev->shading_lines_w;
+ }
+ else
+ {
+ shading_buffer = s->shading_buffer_b;
+ div = s->dev->shading_lines_b;
+ }
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int value = s->temp_shading_buffer[j][i] / (div);
+ shading_buffer[cnt] = (SANE_Byte) (value & 0xff);
+ ++cnt;
+ shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff);
+ ++cnt;
+ }
+ }
+ max_r = 0;
+ max_g = 0;
+ max_b = 0;
+
+ for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/
+ {
+ i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8);
+ max_r += i;
+ i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8);
+ max_g += i;
+ i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8);
+ max_b += i;
+ }
+}
+
+static void
+finish_exposure_buffer (Artec48U_Scanner * s, int *avg_r, int *avg_g,
+ int *avg_b)
+{
+ unsigned int i, j, cnt, c, div;
+ unsigned int max_r;
+ unsigned int max_g;
+ unsigned int max_b;
+ unsigned char *shading_buffer;
+ cnt = 0;
+
+ shading_buffer = s->shading_buffer_w;
+ div = s->dev->shading_lines_w;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int value = s->temp_shading_buffer[j][i] / (div);
+ shading_buffer[cnt] = (SANE_Byte) (value & 0xff);
+ ++cnt;
+ shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff);
+ ++cnt;
+ }
+ }
+ max_r = 0;
+ max_g = 0;
+ max_b = 0;
+ for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/
+ {
+ i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8);
+ if (i > max_r)
+ max_r = i;
+ i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8);
+ if (i > max_g)
+ max_g = i;
+ i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8);
+ if (i > max_b)
+ max_b = i;
+ }
+ *avg_r = max_r;
+ *avg_g = max_g;
+ *avg_b = max_b;
+}
+
+static void
+finish_offset_buffer (Artec48U_Scanner * s, int *avg_r, int *avg_g,
+ int *avg_b)
+{
+ unsigned int i, j, cnt, c, div;
+ unsigned int min_r;
+ unsigned int min_g;
+ unsigned int min_b;
+ unsigned char *shading_buffer;
+ cnt = 0;
+
+ shading_buffer = s->shading_buffer_b;
+ div = s->dev->shading_lines_b;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int value = s->temp_shading_buffer[j][i] / (div);
+ shading_buffer[cnt] = (SANE_Byte) (value & 0xff);
+ ++cnt;
+ shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff);
+ ++cnt;
+ }
+ }
+ min_r = 65535;
+ min_g = 65535;
+ min_b = 65535;
+ for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/
+ {
+ i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8);
+ if (i < min_r)
+ min_r = i;
+ i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8);
+ if (i < min_g)
+ min_g = i;
+ i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8);
+ if (i < min_b)
+ min_b = i;
+ }
+ *avg_r = min_r;
+ *avg_g = min_g;
+ *avg_b = min_b;
+}
+
+static SANE_Status
+artec48u_wait_for_positioning (Artec48U_Device * chip)
+{
+ SANE_Status status;
+ SANE_Bool moving;
+
+ while (SANE_TRUE)
+ {
+ status = artec48u_is_moving (chip, &moving);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (!moving)
+ break;
+ usleep (100000);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+copy_scan_line (Artec48U_Scanner * s)
+{
+ /*For resolution of 1200 dpi we have to interpolate
+ horizontally, because the optical horizontal resolution is
+ limited to 600 dpi. We simply use the avarage value of two pixels. */
+ int cnt, i, j;
+ int xs = s->params.pixel_xs;
+ int interpolate = 0;
+ int value;
+ int value1;
+ int value2;
+ if ((s->reader->params.ydpi == 1200) && (s->dev->is_epro == 0)) /*epro*/
+ interpolate = 1;
+ cnt = 0;
+ if (s->params.color)
+ {
+ if (s->params.depth > 8)
+ {
+ for (i = xs - 1; i >= 0; i--)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ value = s->buffer_pointers[j][i];
+ s->line_buffer[cnt] = LOBYTE (value);
+ ++cnt;
+ s->line_buffer[cnt] = HIBYTE (value);
+ ++cnt;
+ }
+ if (interpolate == 1) /*1200 dpi */
+ cnt += 6;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 12) - 12; i += 12)
+ {
+ value1 = (int) s->line_buffer[i];
+ value1 += (int) (s->line_buffer[i + 1] << 8);
+ value2 = (int) s->line_buffer[i + 12];
+ value2 += (int) (s->line_buffer[i + 13] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 6] = LOBYTE (value);
+ s->line_buffer[i + 7] = HIBYTE (value);
+
+ value1 = (int) s->line_buffer[i + 2];
+ value1 += (int) (s->line_buffer[i + 3] << 8);
+ value2 = (int) s->line_buffer[i + 14];
+ value2 += (int) (s->line_buffer[i + 15] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 8] = LOBYTE (value);
+ s->line_buffer[i + 9] = HIBYTE (value);
+
+ value1 = (int) s->line_buffer[i + 4];
+ value1 += (int) (s->line_buffer[i + 5] << 8);
+ value2 = (int) s->line_buffer[i + 16];
+ value2 += (int) (s->line_buffer[i + 17] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 10] = LOBYTE (value);
+ s->line_buffer[i + 11] = HIBYTE (value);
+ }
+ }
+ }
+ else
+ {
+ for (i = xs - 1; i >= 0; i--)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ value = s->buffer_pointers[j][i];
+ s->line_buffer[cnt] = (SANE_Byte) (value / 257);
+ cnt += 1;
+ }
+ if (interpolate == 1) /*1200 dpi */
+ cnt += 3;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 6) - 6; i += 6)
+ {
+ value1 = (int) s->line_buffer[i];
+ value2 = (int) s->line_buffer[i + 6];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 3] = (SANE_Byte) (value);
+
+ value1 = (int) s->line_buffer[i + 1];
+ value2 = (int) s->line_buffer[i + 7];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 4] = (SANE_Byte) (value);
+
+ value1 = (int) s->line_buffer[i + 2];
+ value2 = (int) s->line_buffer[i + 8];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 5] = (SANE_Byte) (value);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (s->params.depth > 8)
+ {
+ for (i = xs - 1; i >= 0; --i)
+ {
+ value = s->buffer_pointers[0][i];
+ s->line_buffer[cnt] = LOBYTE (value);
+ ++cnt;
+ s->line_buffer[cnt] = HIBYTE (value);
+ ++cnt;
+ if (interpolate == 1) /*1200 dpi */
+ cnt += 2;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 4) - 4; i += 4)
+ {
+ value1 = (int) s->line_buffer[i];
+ value1 += (int) (s->line_buffer[i + 1] << 8);
+ value2 = (int) s->line_buffer[i + 4];
+ value2 += (int) (s->line_buffer[i + 5] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 2] = LOBYTE (value);
+ s->line_buffer[i + 3] = HIBYTE (value);
+ }
+ }
+ }
+ else
+ {
+ if (s->params.lineart == SANE_FALSE)
+ {
+ for (i = xs - 1; i >= 0; --i)
+ {
+ value = s->buffer_pointers[0][i];
+ s->line_buffer[cnt] = (SANE_Byte) (value / 257);
+ ++cnt;
+ if (interpolate == 1) /*1200 dpi */
+ ++cnt;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 2) - 2; i += 2)
+ {
+ value1 = (int) s->line_buffer[i];
+ value2 = (int) s->line_buffer[i + 2];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 1] = (SANE_Byte) (value);
+ }
+ }
+ }
+ else
+ {
+ int cnt2;
+ int bit_cnt = 0;
+ int black_level = s->val[OPT_BLACK_LEVEL].w;
+ /*copy to lineart_buffer */
+ for (i = xs - 1; i >= 0; --i)
+ {
+ s->lineart_buffer[cnt] =
+ (SANE_Byte) (s->buffer_pointers[0][i] / 257);
+ ++cnt;
+ if (interpolate == 1) /*1200 dpi */
+ ++cnt;
+ }
+ cnt2 = cnt - 1;
+ cnt = 0;
+ if (interpolate == 1)
+ {
+ for (i = 0; i < cnt2 - 2; i += 2)
+ {
+ value1 = (int) s->lineart_buffer[i];
+ value2 = (int) s->lineart_buffer[i + 2];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->lineart_buffer[i + 1] = (SANE_Byte) (value);
+ }
+ }
+ /* in this case, every value in buffer_pointers represents a bit */
+ for (i = 0; i < cnt2; i++)
+ {
+ SANE_Byte temp;
+ if (bit_cnt == 0)
+ s->line_buffer[cnt] = 0; /*clear */
+ temp = s->lineart_buffer[i];
+ if (temp <= black_level)
+ s->line_buffer[cnt] |= 1 << (7 - bit_cnt);
+ ++bit_cnt;
+ if (bit_cnt > 7)
+ {
+ bit_cnt = 0;
+ ++cnt;
+ }
+ }
+
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ * attach a device to the backend
+ */
+static SANE_Status
+attach (const char *dev_name, Artec48U_Device ** devp)
+{
+ SANE_Status status;
+ Artec48U_Device *dev;
+
+ XDBG ((1, "attach (%s, %p)\n", dev_name, (void *) devp));
+
+ if (!dev_name)
+ {
+ XDBG ((1, "attach: devname == NULL\n"));
+ return SANE_STATUS_INVAL;
+ }
+ /* already attached ? */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (0 == strcmp (dev->name, dev_name))
+ {
+ if (devp)
+ *devp = dev;
+ XDBG ((3, "attach: device %s already attached\n", dev_name));
+ return SANE_STATUS_GOOD;
+ }
+ }
+ XDBG ((3, "attach: device %s NOT attached\n", dev_name));
+ /* allocate some memory for the device */
+ artec48u_device_new (&dev);
+ if (NULL == dev)
+ return SANE_STATUS_NO_MEM;
+
+ dev->fd = -1;
+ dev->name = strdup (dev_name);
+ dev->sane.name = strdup (dev_name);
+/*
+ * go ahead and open the scanner device
+ */
+ status = artec48u_device_open (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "Could not open device!!\n"));
+ artec48u_device_free (dev);
+ return status;
+ }
+ /*limit the size of vendor and model string to 40 */
+ vendor_string[40] = 0;
+ model_string[40] = 0;
+
+ /* assign all the stuff we need fo this device... */
+ dev->sane.vendor = strdup (vendor_string);
+ XDBG ((3, "attach: setting vendor string: %s\n", vendor_string));
+ dev->sane.model = strdup (model_string);
+ XDBG ((3, "attach: setting model string: %s\n", model_string));
+ dev->sane.type = "flatbed scanner";
+ dev->firmware_path = strdup (firmwarePath);
+
+ dev->epro_mult = eProMult;
+ dev->is_epro = isEPro;
+ XDBG ((1, "attach eProMult %d\n", eProMult));
+ XDBG ((1, "attach isEPro %d\n", isEPro));
+ dev->optical_xdpi = 600 * dev->epro_mult; /*epro*/
+ dev->optical_ydpi = 1200 * dev->epro_mult; /*epro*/
+ dev->base_ydpi = 600 * dev->epro_mult; /*epro*/
+ dev->xdpi_offset = 0; /* in optical_xdpi units */
+ dev->ydpi_offset = 280 * dev->epro_mult; /* in optical_ydpi units */
+ dev->x_size = 5120 * dev->epro_mult; /*epro*/ /* in optical_xdpi units */
+ dev->y_size = 14100 * dev->epro_mult; /*epro*/ /* in optical_ydpi units */
+ dev->shading_offset = 10 * dev->epro_mult;
+ dev->shading_lines_b = 70 * dev->epro_mult;
+ dev->shading_lines_w = 70 * dev->epro_mult;
+
+ dev->gamma_master = gamma_master_default;
+ dev->gamma_r = gamma_r_default;
+ dev->gamma_g = gamma_g_default;
+ dev->gamma_b = gamma_b_default;
+
+ dev->afe_params.r_offset = afe_params.r_offset;
+ dev->afe_params.g_offset = afe_params.g_offset;
+ dev->afe_params.b_offset = afe_params.b_offset;
+
+ dev->afe_params.r_pga = default_afe_params.r_pga;
+ dev->afe_params.g_pga = default_afe_params.g_pga;
+ dev->afe_params.b_pga = default_afe_params.b_pga;
+
+ dev->exp_params.r_time = exp_params.r_time;
+ dev->exp_params.g_time = exp_params.g_time;
+ dev->exp_params.b_time = exp_params.b_time;
+
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = first_dev;
+ status = artec48u_device_close (dev);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ Artec48U_Device *dev;
+ SANE_Status status;
+
+ status = attach (devname, &dev);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * function to decode an value and give it back to the caller.
+ * @param src - pointer to the source string to check
+ * @param opt - string that keeps the option name to check src for
+ * @param what - _FLOAT or _INT
+ * @param result - pointer to the var that should receive our result
+ * @param def - default value that result should be in case of any error
+ * @return The function returns SANE_TRUE if the option has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+decodeVal (char *src, char *opt, int what, void *result, void *def)
+{
+ char *tmp, *tmp2;
+ const char *name;
+
+/* skip the option string */
+ name = (const char *) &src[strlen ("option")];
+
+/* get the name of the option */
+ name = sanei_config_get_string (name, &tmp);
+
+ if (tmp)
+ {
+ /* on success, compare wiht the given one */
+ if (0 == strcmp (tmp, opt))
+ {
+ XDBG ((1, "Decoding option >%s<\n", opt));
+ if (_INT == what)
+ {
+ /* assign the default value for this option... */
+ *((int *) result) = *((int *) def);
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ *((int *) result) = strtol (tmp2, 0, 0);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ else if (_FLOAT == what)
+ {
+ /* assign the default value for this option... */
+ *((double *) result) = *((double *) def);
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ *((double *) result) = strtod (tmp2, 0);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ else if (_BYTE == what)
+ {
+ /* assign the default value for this option... */
+ *((SANE_Byte *) result) = *((SANE_Byte *) def);
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ *((SANE_Byte *) result) =
+ (SANE_Byte) strtol (tmp2, 0, 0);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ else if (_STRING == what)
+ {
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ strcpy ((char *) result, (char *) tmp2);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ }
+ free (tmp);
+ }
+ return SANE_FALSE;
+}
+
+/**
+ * function to retrive the device name of a given string
+ * @param src - string that keeps the option name to check src for
+ * @param dest - pointer to the string, that should receive the detected
+ * devicename
+ * @return The function returns SANE_TRUE if the devicename has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+decodeDevName (char *src, char *dest)
+{
+ char *tmp;
+ const char *name;
+
+ if (0 == strncmp ("device", src, 6))
+ {
+ name = (const char *) &src[strlen ("device")];
+ name = sanei_config_skip_whitespace (name);
+
+ XDBG ((1, "Decoding device name >%s<\n", name));
+
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &tmp);
+ if (tmp)
+ {
+ strcpy (dest, tmp);
+ free (tmp);
+ return SANE_TRUE;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+#ifdef ARTEC48U_USE_BUTTONS
+static SANE_Status
+artec48u_check_buttons (Artec48U_Device * dev, SANE_Int * value)
+{
+ SANE_Status status;
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x74;
+ req[1] = 0x01;
+
+ status = artec48u_device_small_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ *value = (SANE_Int) req[2];
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+#define MAX_DOWNLOAD_BLOCK_SIZE 64
+static SANE_Status
+artec48u_generic_start_scan (Artec48U_Device * dev)
+{
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x43;
+ req[1] = 0x01;
+
+ return artec48u_device_req (dev, req, req);
+
+}
+
+static SANE_Status
+artec48u_generic_read_scanned_data (Artec48U_Device * dev, SANE_Bool * ready)
+{
+ SANE_Status status;
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x35;
+ req[1] = 0x01;
+
+ status = artec48u_device_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (req[1] == 0x35)
+ {
+ if (req[0] == 0)
+ *ready = SANE_TRUE;
+ else
+ *ready = SANE_FALSE;
+ }
+ else
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_download_firmware (Artec48U_Device * dev,
+ SANE_Byte * data, SANE_Word size)
+{
+ SANE_Status status;
+ SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte *block;
+ SANE_Word addr, bytes_left;
+ Artec48U_Packet boot_req;
+ SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE;
+
+ CHECK_DEV_ACTIVE ((Artec48U_Device *) dev,
+ (char *) "artec48u_device_download_firmware");
+
+ for (addr = 0; addr < size; addr += block_size)
+ {
+ bytes_left = size - addr;
+ if (bytes_left > block_size)
+ block = data + addr;
+ else
+ {
+ memset (download_buf, 0, block_size);
+ memcpy (download_buf, data + addr, bytes_left);
+ block = download_buf;
+ }
+ status = artec48u_device_memory_write (dev, addr, block_size, block);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ status = artec48u_device_memory_read (dev, addr, block_size, check_buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (memcmp (block, check_buf, block_size) != 0)
+ {
+ XDBG ((3,
+ "artec48u_device_download_firmware: mismatch at block 0x%0x\n",
+ addr));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ memset (boot_req, 0, sizeof (boot_req));
+ boot_req[0] = 0x69;
+ boot_req[1] = 0x01;
+ boot_req[2] = LOBYTE (addr);
+ boot_req[3] = HIBYTE (addr);
+ status = artec48u_device_req (dev, boot_req, boot_req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_is_moving (Artec48U_Device * dev, SANE_Bool * moving)
+{
+ SANE_Status status;
+ Artec48U_Packet req;
+ memset (req, 0, sizeof (req));
+ req[0] = 0x17;
+ req[1] = 0x01;
+
+ status = artec48u_device_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (req[0] == 0x00 && req[1] == 0x17)
+ {
+ if (req[2] == 0 && (req[3] == 0 || req[3] == 2))
+ *moving = SANE_FALSE;
+ else
+ *moving = SANE_TRUE;
+ }
+ else
+ return SANE_STATUS_IO_ERROR;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_carriage_home (Artec48U_Device * dev)
+{
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x24;
+ req[1] = 0x01;
+
+ return artec48u_device_req (dev, req, req);
+}
+
+
+static SANE_Status
+artec48u_stop_scan (Artec48U_Device * dev)
+{
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x41;
+ req[1] = 0x01;
+ return artec48u_device_small_req (dev, req, req);
+}
+
+
+static SANE_Status
+artec48u_setup_scan (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Action action,
+ SANE_Bool calculate_only,
+ Artec48U_Scan_Parameters * params)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_setup_scan") SANE_Status status;
+ SANE_Int xdpi, ydpi;
+ SANE_Bool color;
+ SANE_Int depth;
+ SANE_Int pixel_x0, pixel_y0, pixel_xs, pixel_ys;
+ SANE_Int pixel_align;
+
+ SANE_Int abs_x0, abs_y0, abs_xs, abs_ys, base_xdpi, base_ydpi;
+ SANE_Int scan_xs, scan_ys, scan_bpl;
+ SANE_Int bits_per_line;
+ SANE_Byte color_mode_code;
+
+ /*If we scan a black line, we use these exposure values */
+ Artec48U_Exposure_Parameters exp_params_black = { 4, 4, 4 };
+
+ XDBG ((6, "%s: enter\n", function_name));
+ XDBG ((1,"setup scan is_epro %d\n",s->dev->is_epro));
+ XDBG ((1,"setup scan epro_mult %d\n",s->dev->epro_mult));
+
+ xdpi = request->xdpi;
+ ydpi = request->ydpi;
+ color = request->color;
+ depth = request->depth;
+
+ switch (action)
+ {
+ case SA_CALIBRATE_SCAN_WHITE:
+ {
+ /*move a bit inside scan mark -
+ the value for the offset was found by trial and error */
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_w;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE_SCAN_OFFSET_1:
+ case SA_CALIBRATE_SCAN_OFFSET_2:
+ {
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_b;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE_SCAN_EXPOSURE_1:
+ case SA_CALIBRATE_SCAN_EXPOSURE_2:
+ {
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_w;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE_SCAN_BLACK:
+ {
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_w;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_SCAN:
+ {
+ SANE_Fixed x0 = request->x0 + s->dev->xdpi_offset;
+ SANE_Fixed y0;
+ /*epro*/
+ if ((ydpi == 1200) && (s->dev->is_epro == 0))
+ xdpi = 600;
+ y0 = request->y0 + s->dev->ydpi_offset;
+ pixel_ys = SANE_UNFIX (request->ys) * ydpi / MM_PER_INCH + 0.5;
+ pixel_x0 = SANE_UNFIX (x0) * xdpi / MM_PER_INCH + 0.5;
+ pixel_y0 = SANE_UNFIX (y0) * ydpi / MM_PER_INCH + 0.5;
+ pixel_xs = SANE_UNFIX (request->xs) * xdpi / MM_PER_INCH + 0.5;
+ break;
+ }
+
+ default:
+ XDBG ((6, "%s: invalid action=%d\n", function_name, (int) action));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((6, "%s: xdpi=%d, ydpi=%d\n", function_name, xdpi, ydpi));
+ XDBG ((6, "%s: color=%s, depth=%d\n", function_name,
+ color ? "TRUE" : "FALSE", depth));
+ XDBG ((6, "%s: pixel_x0=%d, pixel_y0=%d\n", function_name,
+ pixel_x0, pixel_y0));
+ XDBG ((6, "%s: pixel_xs=%d, pixel_ys=%d\n", function_name,
+ pixel_xs, pixel_ys));
+
+ switch (depth)
+ {
+ case 8:
+ color_mode_code = color ? 0x84 : 0x82;
+ break;
+
+ case 16:
+ color_mode_code = color ? 0xa4 : 0xa2;
+ break;
+
+ default:
+ XDBG ((6, "%s: unsupported depth=%d\n", function_name, depth));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ base_xdpi = s->dev->optical_xdpi;
+ base_ydpi = s->dev->base_ydpi;
+
+ XDBG ((6, "%s: base_xdpi=%d, base_ydpi=%d\n", function_name,
+ base_xdpi, base_ydpi));
+
+ abs_x0 = pixel_x0 * base_xdpi / xdpi;
+ abs_y0 = pixel_y0 * base_ydpi / ydpi;
+
+ /* Calculate minimum number of pixels which span an integral multiple of 64
+ * bytes. */
+ pixel_align = 32; /* best case for depth = 16 */
+ while ((depth * pixel_align) % (64 * 8) != 0)
+ pixel_align *= 2;
+ XDBG ((6, "%s: pixel_align=%d\n", function_name, pixel_align));
+
+ if (pixel_xs % pixel_align == 0)
+ scan_xs = pixel_xs;
+ else
+ scan_xs = (pixel_xs / pixel_align + 1) * pixel_align;
+ scan_ys = pixel_ys;
+ XDBG ((6, "%s: scan_xs=%d, scan_ys=%d\n", function_name, scan_xs, scan_ys));
+
+ abs_xs = scan_xs * base_xdpi / xdpi;
+ abs_ys = scan_ys * base_ydpi / ydpi;
+ XDBG ((6, "%s: abs_xs=%d, abs_ys=%d\n", function_name, abs_xs, abs_ys));
+
+ bits_per_line = depth * scan_xs;
+ if (bits_per_line % 8) /* impossible */
+ {
+ XDBG ((1, "%s: BUG: unaligned bits_per_line=%d\n", function_name,
+ bits_per_line));
+ return SANE_STATUS_INVAL;
+ }
+ scan_bpl = bits_per_line / 8;
+
+ if (scan_bpl % 64) /* impossible */
+ {
+ XDBG ((1, "%s: BUG: unaligned scan_bpl=%d\n", function_name, scan_bpl));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scan_bpl > 15600)
+ {
+ XDBG ((6, "%s: scan_bpl=%d, too large\n", function_name, scan_bpl));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((6, "%s: scan_bpl=%d\n", function_name, scan_bpl));
+
+ if (!calculate_only)
+ {
+ Artec48U_Packet req;
+ char motor_mode_1, motor_mode_2;
+ switch (action)
+ {
+ case SA_CALIBRATE_SCAN_WHITE:
+ motor_mode_1 = 0x01;
+ motor_mode_2 = 0x00;
+ break;
+
+ case SA_CALIBRATE_SCAN_BLACK:
+ motor_mode_1 = 0x04;
+ motor_mode_2 = 0x00;
+ break;
+
+ case SA_SCAN:
+ motor_mode_1 = 0x01;
+ motor_mode_2 = 0x00;
+ break;
+
+ default:
+ XDBG ((6, "%s: invalid action=%d\n", function_name, (int) action));
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill in the setup command */
+ memset (req, 0, sizeof (req));
+ req[0x00] = 0x20;
+ req[0x01] = 0x01;
+ req[0x02] = LOBYTE (abs_y0);
+ req[0x03] = HIBYTE (abs_y0);
+ req[0x04] = LOBYTE (abs_ys);
+ req[0x05] = HIBYTE (abs_ys);
+ req[0x06] = LOBYTE (abs_x0);
+ req[0x07] = HIBYTE (abs_x0);
+ req[0x08] = LOBYTE (abs_xs);
+ req[0x09] = HIBYTE (abs_xs);
+ req[0x0a] = color_mode_code;
+ req[0x0b] = 0x60;
+ req[0x0c] = LOBYTE (xdpi);
+ req[0x0d] = HIBYTE (xdpi);
+ req[0x0e] = 0x12;
+ req[0x0f] = 0x00;
+ req[0x10] = LOBYTE (scan_bpl);
+ req[0x11] = HIBYTE (scan_bpl);
+ req[0x12] = LOBYTE (scan_ys);
+ req[0x13] = HIBYTE (scan_ys);
+ req[0x14] = motor_mode_1;
+ req[0x15] = motor_mode_2;
+ req[0x16] = LOBYTE (ydpi);
+ req[0x17] = HIBYTE (ydpi);
+ req[0x18] = 0x00;
+
+ status = artec48u_device_req (s->dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: setup request failed: %s\n", function_name,
+ sane_strstatus (status)));
+ return status;
+ }
+
+ if (action == SA_SCAN)
+ {
+ artec48u_calculate_shading_buffer (s, pixel_x0, pixel_xs + pixel_x0,
+ xdpi, color);
+ artec48u_generic_set_exposure_time (s->dev,
+ &(s->dev->
+ artec_48u_exposure_params));
+ artec48u_generic_set_afe (s->dev, &(s->dev->artec_48u_afe_params));
+ }
+ else if (action == SA_CALIBRATE_SCAN_BLACK)
+ {
+ artec48u_generic_set_exposure_time (s->dev, &exp_params_black);
+ artec48u_generic_set_afe (s->dev, &(s->dev->afe_params));
+ }
+ else if (action == SA_CALIBRATE_SCAN_WHITE)
+ {
+ artec48u_generic_set_exposure_time (s->dev, &(s->dev->exp_params));
+ artec48u_generic_set_afe (s->dev, &(s->dev->afe_params));
+ }
+ }
+ /* Fill in calculated values */
+ params->xdpi = xdpi;
+ params->ydpi = ydpi;
+ params->depth = depth;
+ params->color = color;
+ params->pixel_xs = pixel_xs;
+ params->pixel_ys = pixel_ys;
+ params->scan_xs = scan_xs;
+ params->scan_ys = scan_ys;
+ params->scan_bpl = scan_bpl;
+
+ XDBG ((6, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_generic_set_afe (Artec48U_Device * dev,
+ Artec48U_AFE_Parameters * params)
+{
+ Artec48U_Packet req;
+ memset (req, 0, sizeof (req));
+ req[0] = 0x22;
+ req[1] = 0x01;
+ req[2] = params->r_offset;
+ req[3] = params->r_pga;
+ req[4] = params->g_offset;
+ req[5] = params->g_pga;
+ req[6] = params->b_offset;
+ req[7] = params->b_pga;
+
+ return artec48u_device_req (dev, req, req);
+}
+
+
+static SANE_Status
+artec48u_generic_set_exposure_time (Artec48U_Device * dev,
+ Artec48U_Exposure_Parameters * params)
+{
+ Artec48U_Packet req;
+ memset (req, 0, sizeof (req));
+ req[0] = 0x76;
+ req[1] = 0x01;
+ req[2] = req[6] = req[10] = 0x04;
+ req[4] = LOBYTE (params->r_time);
+ req[5] = HIBYTE (params->r_time);
+ req[8] = LOBYTE (params->g_time);
+ req[9] = HIBYTE (params->g_time);
+ req[12] = LOBYTE (params->b_time);
+ req[13] = HIBYTE (params->b_time);
+ return artec48u_device_req (dev, req, req);
+}
+
+static SANE_Status
+artec48u_device_new (Artec48U_Device ** dev_return)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_new") Artec48U_Device *dev;
+
+ XDBG ((7, "%s: enter\n", function_name));
+ if (!dev_return)
+ return SANE_STATUS_INVAL;
+
+ dev = (Artec48U_Device *) malloc (sizeof (Artec48U_Device));
+
+ if (!dev)
+ {
+ XDBG ((3, "%s: couldn't malloc %lu bytes for device\n",
+ function_name, (u_long) sizeof (Artec48U_Device)));
+ *dev_return = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ *dev_return = dev;
+
+ memset (dev, 0, sizeof (Artec48U_Device));
+
+ dev->fd = -1;
+ dev->active = SANE_FALSE;
+
+ dev->read_buffer = NULL;
+ dev->requested_buffer_size = 32768;
+
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_free (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_free")
+ XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev));
+ if (dev)
+ {
+ if (dev->active)
+ artec48u_device_deactivate (dev);
+
+ if (dev->fd != -1)
+ artec48u_device_close (dev);
+
+ XDBG ((7, "%s: freeing dev\n", function_name));
+ free (dev);
+ }
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_open (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_open")
+ SANE_Status status;
+ SANE_Int fd;
+
+ XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev));
+
+ CHECK_DEV_NOT_NULL (dev, function_name);
+
+ if (dev->fd != -1)
+ {
+ XDBG ((3, "%s: device already open\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_usb_open (dev->sane.name, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: sanei_usb_open failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ dev->fd = fd;
+
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_close (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_close")
+ XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev));
+
+ CHECK_DEV_OPEN (dev, function_name);
+
+ if (dev->active)
+ artec48u_device_deactivate (dev);
+
+ sanei_usb_close (dev->fd);
+ dev->fd = -1;
+
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_activate (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_activate")
+ CHECK_DEV_OPEN (dev, function_name);
+
+ if (dev->active)
+ {
+ XDBG ((3, "%s: device already active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((7, "%s: model \"%s\"\n", function_name, dev->sane.model));
+
+ dev->xdpi_offset = SANE_FIX (dev->xdpi_offset *
+ MM_PER_INCH / dev->optical_xdpi);
+ dev->ydpi_offset = SANE_FIX (dev->ydpi_offset *
+ MM_PER_INCH / dev->optical_ydpi);
+
+ dev->active = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_deactivate (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_deactivate")
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (dev->read_active)
+ artec48u_device_read_finish (dev);
+
+ dev->active = SANE_FALSE;
+
+ return status;
+}
+
+static SANE_Status
+artec48u_device_memory_write (Artec48U_Device * dev,
+ SANE_Word addr,
+ SANE_Word size, SANE_Byte * data)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_memory_write")
+ SANE_Status status;
+
+ XDBG ((8, "%s: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ function_name, (void *) dev, addr, size, (void *) data));
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ status = sanei_usb_control_msg (dev->fd, 0x40, 0x01,
+ memory_write_value, addr, size, data);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: sanei_usb_control_msg failed: %s\n",
+ function_name, sane_strstatus (status)));
+ }
+
+ return status;
+}
+
+static SANE_Status
+artec48u_device_memory_read (Artec48U_Device * dev,
+ SANE_Word addr, SANE_Word size, SANE_Byte * data)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_memory_read")
+ SANE_Status status;
+
+ XDBG ((8, "%s: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ function_name, (void *) dev, addr, size, data));
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ status = sanei_usb_control_msg (dev->fd, 0xc0, 0x01,
+ memory_read_value, addr, size, data);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: sanei_usb_control_msg failed: %s\n",
+ function_name, sane_strstatus (status)));
+ }
+
+ return status;
+}
+
+static SANE_Status
+artec48u_device_generic_req (Artec48U_Device * dev,
+ SANE_Word cmd_value, SANE_Word cmd_index,
+ SANE_Word res_value, SANE_Word res_index,
+ Artec48U_Packet cmd, Artec48U_Packet res)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_generic_req")
+ SANE_Status status;
+
+ XDBG ((7, "%s: command=0x%02x\n", function_name, cmd[0]));
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ status = sanei_usb_control_msg (dev->fd,
+ 0x40, 0x01, cmd_value, cmd_index,
+ ARTEC48U_PACKET_SIZE, cmd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: writing command failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ memset (res, 0, sizeof (Artec48U_Packet));
+
+ status = sanei_usb_control_msg (dev->fd,
+ 0xc0, 0x01, res_value, res_index,
+ ARTEC48U_PACKET_SIZE, res);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: reading response failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+ return status;
+}
+
+static SANE_Status
+artec48u_device_req (Artec48U_Device * dev, Artec48U_Packet cmd,
+ Artec48U_Packet res)
+{
+ return artec48u_device_generic_req (dev,
+ send_cmd_value,
+ send_cmd_index,
+ recv_res_value,
+ recv_res_index, cmd, res);
+}
+
+static SANE_Status
+artec48u_device_small_req (Artec48U_Device * dev, Artec48U_Packet cmd,
+ Artec48U_Packet res)
+{
+ Artec48U_Packet fixed_cmd;
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ memcpy (fixed_cmd + i * 8, cmd, 8);
+
+ return artec48u_device_generic_req (dev,
+ send_small_cmd_value,
+ send_small_cmd_index,
+ recv_small_res_value,
+ recv_small_res_index, fixed_cmd, res);
+}
+
+static SANE_Status
+artec48u_device_read_raw (Artec48U_Device * dev, SANE_Byte * buffer,
+ size_t * size)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read_raw")
+ SANE_Status status;
+
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ XDBG ((7, "%s: enter: size=0x%lx\n", function_name, (unsigned long) *size));
+
+ status = sanei_usb_read_bulk (dev->fd, buffer, size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: bulk read failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ XDBG ((7, "%s: leave: size=0x%lx\n", function_name, (unsigned long) *size));
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_set_read_buffer_size (Artec48U_Device * dev,
+ size_t buffer_size)
+{
+ DECLARE_FUNCTION_NAME ("gt68xx_device_set_read_buffer_size")
+ CHECK_DEV_NOT_NULL (dev, function_name);
+
+ if (dev->read_active)
+ {
+ XDBG ((3, "%s: BUG: read already active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ buffer_size = (buffer_size + 63UL) & ~63UL;
+ if (buffer_size > 0)
+ {
+ dev->requested_buffer_size = buffer_size;
+ return SANE_STATUS_GOOD;
+ }
+
+ XDBG ((3, "%s: bad buffer size\n", function_name));
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+artec48u_device_read_prepare (Artec48U_Device * dev, size_t expected_count)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read_prepare")
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (dev->read_active)
+ {
+ XDBG ((3, "%s: read already active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ dev->read_buffer = (SANE_Byte *) malloc (dev->requested_buffer_size);
+ if (!dev->read_buffer)
+ {
+ XDBG ((3, "%s: not enough memory for the read buffer (%lu bytes)\n",
+ function_name, (unsigned long) dev->requested_buffer_size));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->read_active = SANE_TRUE;
+ dev->read_pos = dev->read_bytes_in_buffer = 0;
+ dev->read_bytes_left = expected_count;
+
+ return SANE_STATUS_GOOD;
+}
+
+static RETSIGTYPE
+reader_process_sigterm_handler (int signal)
+{
+ XDBG ((1, "reader_process: terminated by signal %d\n", signal));
+ _exit (SANE_STATUS_GOOD);
+}
+
+static RETSIGTYPE
+usb_reader_process_sigterm_handler (int signal)
+{
+ XDBG ((1, "reader_process (usb): terminated by signal %d\n", signal));
+ cancelRead = SANE_TRUE;
+}
+
+static SANE_Status
+artec48u_device_read_start (Artec48U_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "artec48u_device_read_start");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_read (Artec48U_Device * dev, SANE_Byte * buffer,
+ size_t * size)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read") SANE_Status status;
+ size_t byte_count = 0;
+ size_t left_to_read = *size;
+ size_t transfer_size, block_size, raw_block_size;
+
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (!dev->read_active)
+ {
+ XDBG ((3, "%s: read not active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ while (left_to_read > 0)
+ {
+ if (dev->read_bytes_in_buffer == 0)
+ {
+ block_size = dev->requested_buffer_size;
+ if (block_size > dev->read_bytes_left)
+ block_size = dev->read_bytes_left;
+ if (block_size == 0)
+ break;
+ raw_block_size = (block_size + 63UL) & ~63UL;
+ status = artec48u_device_read_raw (dev, dev->read_buffer,
+ &raw_block_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: read failed\n", function_name));
+ return status;
+ }
+ dev->read_pos = 0;
+ dev->read_bytes_in_buffer = block_size;
+ dev->read_bytes_left -= block_size;
+ }
+
+ transfer_size = left_to_read;
+ if (transfer_size > dev->read_bytes_in_buffer)
+ transfer_size = dev->read_bytes_in_buffer;
+ if (transfer_size > 0)
+ {
+ memcpy (buffer, dev->read_buffer + dev->read_pos, transfer_size);
+ dev->read_pos += transfer_size;
+ dev->read_bytes_in_buffer -= transfer_size;
+ byte_count += transfer_size;
+ left_to_read -= transfer_size;
+ buffer += transfer_size;
+ }
+ }
+
+ *size = byte_count;
+
+ if (byte_count == 0)
+ return SANE_STATUS_EOF;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_read_finish (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read_finish")
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (!dev->read_active)
+ {
+ XDBG ((3, "%s: read not active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((7, "%s: read_bytes_left = %ld\n",
+ function_name, (long) dev->read_bytes_left));
+
+ free (dev->read_buffer);
+ dev->read_buffer = NULL;
+
+ dev->read_active = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_delay_buffer_init (Artec48U_Delay_Buffer * delay,
+ SANE_Int pixels_per_line)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_delay_buffer_init")
+ SANE_Int bytes_per_line;
+ SANE_Int line_count, i;
+
+ if (pixels_per_line <= 0)
+ {
+ XDBG ((3, "%s: BUG: pixels_per_line=%d\n",
+ function_name, pixels_per_line));
+ return SANE_STATUS_INVAL;
+ }
+
+ bytes_per_line = pixels_per_line * sizeof (unsigned int);
+
+ delay->line_count = line_count = 1;
+ delay->read_index = 0;
+ delay->write_index = 0;
+
+ delay->mem_block = (SANE_Byte *) malloc (bytes_per_line * line_count);
+ if (!delay->mem_block)
+ {
+ XDBG ((3, "%s: no memory for delay block\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ delay->lines =
+ (unsigned int **) malloc (sizeof (unsigned int *) * line_count);
+ if (!delay->lines)
+ {
+ free (delay->mem_block);
+ XDBG ((3, "%s: no memory for delay line pointers\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < line_count; ++i)
+ delay->lines[i] =
+ (unsigned int *) (delay->mem_block + i * bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_delay_buffer_done (Artec48U_Delay_Buffer * delay)
+{
+ if (delay->lines)
+ {
+ free (delay->lines);
+ delay->lines = NULL;
+ }
+
+ if (delay->mem_block)
+ {
+ free (delay->mem_block);
+ delay->mem_block = NULL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#define DELAY_BUFFER_WRITE_PTR(delay) ( (delay)->lines[(delay)->write_index] )
+
+#define DELAY_BUFFER_READ_PTR(delay) ( (delay)->lines[(delay)->read_index ] )
+
+#define DELAY_BUFFER_STEP(delay) \
+ do { \
+ (delay)->read_index = ((delay)->read_index + 1) % (delay)->line_count; \
+ (delay)->write_index = ((delay)->write_index + 1) % (delay)->line_count; \
+ } while (SANE_FALSE)
+
+
+static inline void
+unpack_8_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line)
+{
+ XDBG ((3, "unpack_8_mono\n"));
+ for (; pixels_per_line > 0; ++src, ++dst, --pixels_per_line)
+ {
+ *dst = (((unsigned int) *src) << 8) | *src;
+ }
+}
+
+static inline void
+unpack_16_le_mono (SANE_Byte * src, unsigned int *dst,
+ SANE_Int pixels_per_line)
+{
+ XDBG ((3, "unpack_16_le_mono\n"));
+ for (; pixels_per_line > 0; src += 2, dst++, --pixels_per_line)
+ {
+ *dst = (((unsigned int) src[1]) << 8) | src[0];
+ }
+}
+
+static SANE_Status
+line_read_gray_8 (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+ XDBG ((3, "line_read_gray_8\n"));
+
+ size = reader->params.scan_bpl;
+ status = artec48u_device_read (reader->dev, reader->pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_8_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_16 (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+
+ XDBG ((3, "line_read_gray_16\n"));
+ size = reader->params.scan_bpl;
+ status = artec48u_device_read (reader->dev, reader->pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_16_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_8_line_mode (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+ XDBG ((3, "line_read_bgr_8_line_mode\n"));
+
+ size = reader->params.scan_bpl * 3;
+ status = artec48u_device_read (reader->dev, pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_16_line_mode (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ XDBG ((3, "line_read_bgr_16_line_mode\n"));
+ size = reader->params.scan_bpl * 3;
+ status = artec48u_device_read (reader->dev, pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_line_reader_init_delays (Artec48U_Line_Reader * reader)
+{
+ SANE_Status status;
+
+ if (reader->params.color)
+ {
+ status = artec48u_delay_buffer_init (&reader->r_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = artec48u_delay_buffer_init (&reader->g_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ artec48u_delay_buffer_done (&reader->r_delay);
+ return status;
+ }
+
+ status = artec48u_delay_buffer_init (&reader->b_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ artec48u_delay_buffer_done (&reader->g_delay);
+ artec48u_delay_buffer_done (&reader->r_delay);
+ return status;
+ }
+ }
+ else
+ {
+ status = artec48u_delay_buffer_init (&reader->g_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ reader->delays_initialized = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+artec48u_line_reader_free_delays (Artec48U_Line_Reader * reader)
+{
+ if (!reader)
+ {
+ return;
+ }
+ if (reader->delays_initialized)
+ {
+ if (reader->params.color)
+ {
+ artec48u_delay_buffer_done (&reader->b_delay);
+ artec48u_delay_buffer_done (&reader->g_delay);
+ artec48u_delay_buffer_done (&reader->r_delay);
+ }
+ else
+ {
+ artec48u_delay_buffer_done (&reader->g_delay);
+ }
+ reader->delays_initialized = SANE_FALSE;
+ }
+}
+
+static SANE_Status
+artec48u_line_reader_new (Artec48U_Device * dev,
+ Artec48U_Scan_Parameters * params,
+ Artec48U_Line_Reader ** reader_return)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_line_reader_new") SANE_Status status;
+ Artec48U_Line_Reader *reader;
+ SANE_Int image_size;
+ SANE_Int scan_bpl_full;
+
+ XDBG ((6, "%s: enter\n", function_name));
+ XDBG ((6, "%s: enter params xdpi: %i\n", function_name, params->xdpi));
+ XDBG ((6, "%s: enter params ydpi: %i\n", function_name, params->ydpi));
+ XDBG ((6, "%s: enter params depth: %i\n", function_name, params->depth));
+ XDBG ((6, "%s: enter params color: %i\n", function_name, params->color));
+ XDBG ((6, "%s: enter params pixel_xs: %i\n", function_name, params->pixel_xs));
+ XDBG ((6, "%s: enter params pixel_ys: %i\n", function_name, params->pixel_ys));
+ XDBG ((6, "%s: enter params scan_xs: %i\n", function_name, params->scan_xs));
+ XDBG ((6, "%s: enter params scan_ys: %i\n", function_name, params->scan_ys));
+ XDBG ((6, "%s: enter params scan_bpl: %i\n", function_name, params->scan_bpl));
+ *reader_return = NULL;
+
+ reader = (Artec48U_Line_Reader *) malloc (sizeof (Artec48U_Line_Reader));
+ if (!reader)
+ {
+ XDBG ((3, "%s: cannot allocate Artec48U_Line_Reader\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (reader, 0, sizeof (Artec48U_Line_Reader));
+
+ reader->dev = dev;
+ memcpy (&reader->params, params, sizeof (Artec48U_Scan_Parameters));
+ reader->pixel_buffer = 0;
+ reader->delays_initialized = SANE_FALSE;
+
+ reader->read = NULL;
+
+ status = artec48u_line_reader_init_delays (reader);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: cannot allocate line buffers: %s\n",
+ function_name, sane_strstatus (status)));
+ free (reader);
+ return status;
+ }
+
+ reader->pixels_per_line = reader->params.pixel_xs;
+
+ if (!reader->params.color)
+ {
+ XDBG ((2, "!reader->params.color\n"));
+ if (reader->params.depth == 8)
+ reader->read = line_read_gray_8;
+ else if (reader->params.depth == 16)
+ reader->read = line_read_gray_16;
+ }
+ else
+ {
+ XDBG ((2, "reader line mode\n"));
+ if (reader->params.depth == 8)
+ {
+ XDBG ((2, "depth 8\n"));
+ reader->read = line_read_bgr_8_line_mode;
+ }
+ else if (reader->params.depth == 16)
+ {
+ XDBG ((2, "depth 16\n"));
+ reader->read = line_read_bgr_16_line_mode;
+ }
+ }
+
+ if (reader->read == NULL)
+ {
+ XDBG ((3, "%s: unsupported bit depth (%d)\n",
+ function_name, reader->params.depth));
+ artec48u_line_reader_free_delays (reader);
+ free (reader);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ scan_bpl_full = reader->params.scan_bpl;
+ if (reader->params.color)
+ scan_bpl_full *= 3;
+
+ reader->pixel_buffer = malloc (scan_bpl_full);
+ if (!reader->pixel_buffer)
+ {
+ XDBG ((3, "%s: cannot allocate pixel buffer\n", function_name));
+ artec48u_line_reader_free_delays (reader);
+ free (reader);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ artec48u_device_set_read_buffer_size (reader->dev,
+ scan_bpl_full /* 200 */ );
+
+ image_size = scan_bpl_full * reader->params.scan_ys;
+ status = artec48u_device_read_prepare (reader->dev, image_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: artec48u_device_read_prepare failed: %s\n",
+ function_name, sane_strstatus (status)));
+ free (reader->pixel_buffer);
+ artec48u_line_reader_free_delays (reader);
+ free (reader);
+ return status;
+ }
+
+ XDBG ((6, "%s: leave: ok\n", function_name));
+ *reader_return = reader;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_line_reader_free (Artec48U_Line_Reader * reader)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_line_reader_free") SANE_Status status;
+
+ XDBG ((6, "%s: enter\n", function_name));
+
+ if (!reader)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ artec48u_line_reader_free_delays (reader);
+
+ if (reader->pixel_buffer)
+ {
+ free (reader->pixel_buffer);
+ reader->pixel_buffer = NULL;
+ }
+
+ status = artec48u_device_read_finish (reader->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: artec48u_device_read_finish failed: %s\n",
+ function_name, sane_strstatus (status)));
+ }
+
+ if (reader)
+ free (reader);
+
+ XDBG ((6, "%s: leave\n", function_name));
+ return status;
+}
+
+static SANE_Status
+artec48u_line_reader_read (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ return (*reader->read) (reader, buffer_pointers_return);
+}
+
+static SANE_Status
+artec48u_scanner_new (Artec48U_Device * dev,
+ Artec48U_Scanner ** scanner_return)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_new") Artec48U_Scanner *s;
+
+ *scanner_return = NULL;
+
+ s = (Artec48U_Scanner *) malloc (sizeof (Artec48U_Scanner));
+ if (!s)
+ {
+ XDBG ((5, "%s: no memory for Artec48U_Scanner\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+ s->dev = dev;
+ s->reader = NULL;
+ s->scanning = SANE_FALSE;
+ s->line_buffer = NULL;
+ s->lineart_buffer = NULL;
+ s->next = NULL;
+ s->pipe_handle = NULL;
+ s->buffer_pointers[0] = NULL;
+ s->buffer_pointers[1] = NULL;
+ s->buffer_pointers[2] = NULL;
+ s->shading_buffer_w = NULL;
+ s->shading_buffer_b = NULL;
+ s->shading_buffer_white[0] = NULL;
+ s->shading_buffer_white[1] = NULL;
+ s->shading_buffer_white[2] = NULL;
+ s->shading_buffer_black[0] = NULL;
+ s->shading_buffer_black[1] = NULL;
+ s->shading_buffer_black[2] = NULL;
+ *scanner_return = s;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_read_line (Artec48U_Scanner * s,
+ unsigned int **buffer_pointers, SANE_Bool shading)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_read_line") SANE_Status status;
+ int i, j, c;
+
+ status = artec48u_line_reader_read (s->reader, buffer_pointers);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((5, "%s: artec48u_line_reader_read failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+ if (shading != SANE_TRUE)
+ return status;
+
+ c = s->reader->pixels_per_line;
+ if (s->reader->params.color == SANE_TRUE)
+ {
+ for (i = c - 1; i >= 0; i--)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int new_value;
+ unsigned int value = buffer_pointers[j][i];
+ if (value < s->shading_buffer_black[j][i])
+ value = s->shading_buffer_black[j][i];
+ if (value > s->shading_buffer_white[j][i])
+ value = s->shading_buffer_white[j][i];
+ new_value =
+ (double) (value -
+ s->shading_buffer_black[j][i]) * 65535.0 /
+ (double) (s->shading_buffer_white[j][i] -
+ s->shading_buffer_black[j][i]);
+ if (new_value < 0)
+ new_value = 0;
+ if (new_value > 65535)
+ new_value = 65535;
+ new_value =
+ s->gamma_array[j +
+ 1][s->contrast_array[s->
+ brightness_array
+ [new_value]]];
+ new_value = s->gamma_array[0][new_value];
+ buffer_pointers[j][i] = new_value;
+ }
+ }
+ }
+ else
+ {
+ for (i = c - 1; i >= 0; i--)
+ {
+ int new_value;
+ unsigned int value = buffer_pointers[0][i];
+ new_value =
+ (double) (value -
+ s->shading_buffer_black[1][i]) * 65535.0 /
+ (double) (s->shading_buffer_white[1][i] -
+ s->shading_buffer_black[1][i]);
+ if (new_value < 0)
+ new_value = 0;
+ if (new_value > 65535)
+ new_value = 65535;
+ new_value =
+ s->gamma_array[0][s->
+ contrast_array[s->brightness_array[new_value]]];
+ buffer_pointers[0][i] = new_value;
+ }
+ }
+ return status;
+}
+
+static SANE_Status
+artec48u_scanner_free (Artec48U_Scanner * s)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_free") if (!s)
+ {
+ XDBG ((5, "%s: scanner==NULL\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->reader)
+ {
+ artec48u_line_reader_free (s->reader);
+ s->reader = NULL;
+ }
+
+ free (s->shading_buffer_w);
+ free (s->shading_buffer_b);
+ free (s->shading_buffer_white[0]);
+ free (s->shading_buffer_black[0]);
+ free (s->shading_buffer_white[1]);
+ free (s->shading_buffer_black[1]);
+ free (s->shading_buffer_white[2]);
+ free (s->shading_buffer_black[2]);
+
+ if (s->line_buffer)
+ free (s->line_buffer);
+ if (s->lineart_buffer)
+ free (s->lineart_buffer);
+
+ free (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_internal_start_scan (Artec48U_Scanner * s)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_internal_start_scan")
+ SANE_Status status;
+ SANE_Bool ready;
+ SANE_Int repeat_count;
+
+ status = artec48u_wait_for_positioning (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_scanner_wait_for_positioning error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ status = artec48u_generic_start_scan (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_start_scan error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ for (repeat_count = 0; repeat_count < 30 * 10; ++repeat_count)
+ {
+ status = artec48u_generic_read_scanned_data (s->dev, &ready);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_read_scanned_data error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+ if (ready)
+ break;
+ usleep (100000);
+ }
+
+ if (!ready)
+ {
+ XDBG ((2, "%s: scanner still not ready - giving up\n", function_name));
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = artec48u_device_read_start (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_read_start error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_start_scan_extended (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Action action,
+ Artec48U_Scan_Parameters * params)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_start_scan_extended")
+ SANE_Status status;
+
+ status = artec48u_wait_for_positioning (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_scanner_wait_for_positioning error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ if (action == SA_SCAN)
+ status = artec48u_setup_scan (s, request, action, SANE_FALSE, params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_setup_scan failed: %s\n", function_name,
+ sane_strstatus (status)));
+ return status;
+ }
+ status = artec48u_line_reader_new (s->dev, params, &s->reader);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_line_reader_new failed: %s\n", function_name,
+ sane_strstatus (status)));
+ return status;
+ }
+
+ status = artec48u_scanner_internal_start_scan (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_scanner_internal_start_scan failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_start_scan (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Parameters * params)
+{
+ return artec48u_scanner_start_scan_extended (s, request, SA_SCAN, params);
+}
+
+
+static SANE_Status
+artec48u_scanner_stop_scan (Artec48U_Scanner * s)
+{
+ XDBG ((1, "artec48u_scanner_stop_scan begin: \n"));
+ artec48u_line_reader_free (s->reader);
+ s->reader = NULL;
+
+ return artec48u_stop_scan (s->dev);
+}
+
+static void
+calculateGamma (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[0][i] = gval;
+ }
+}
+
+static void
+calculateGammaRed (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA_R].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[1][i] = gval;
+ }
+}
+
+static void
+calculateGammaGreen (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA_G].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[2][i] = gval;
+ }
+}
+
+static void
+calculateGammaBlue (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA_B].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[3][i] = gval;
+ }
+}
+
+static SANE_Status
+artec48u_calculate_shading_buffer (Artec48U_Scanner * s, int start, int end,
+ int resolution, SANE_Bool color)
+{
+ int i;
+ int c;
+ int bpp;
+ c = 0;
+ bpp = 6;
+ switch (resolution)
+ {
+ case 50:
+ bpp = 72;
+ break;
+ case 100:
+ bpp = 36;
+ break;
+ case 200:
+ bpp = 18;
+ break;
+ case 300:
+ bpp = 12;
+ break;
+ case 600:
+ bpp = 6;
+ break;
+ case 1200:
+ if(s->dev->is_epro == 0)
+ bpp = 6;
+ else
+ bpp = 3;
+ }
+
+ for (i = start * bpp; i < end * bpp; i += bpp)
+ {
+ if (color)
+ {
+ s->shading_buffer_white[0][c] =
+ (unsigned int) s->shading_buffer_w[i] +
+ ((((unsigned int) s->shading_buffer_w[i + 1]) << 8));
+ s->shading_buffer_white[2][c] =
+ (unsigned int) s->shading_buffer_w[i + 4] +
+ ((((unsigned int) s->shading_buffer_w[i + 5]) << 8));
+ s->shading_buffer_black[0][c] =
+ (unsigned int) s->shading_buffer_b[i] +
+ ((((unsigned int) s->shading_buffer_b[i + 1]) << 8));
+ s->shading_buffer_black[2][c] =
+ (unsigned int) s->shading_buffer_b[i + 4] +
+ ((((unsigned int) s->shading_buffer_b[i + 5]) << 8));
+ }
+ s->shading_buffer_white[1][c] =
+ (unsigned int) s->shading_buffer_w[i + 2] +
+ ((((unsigned int) s->shading_buffer_w[i + 3]) << 8));
+ s->shading_buffer_black[1][c] =
+ (unsigned int) s->shading_buffer_b[i + 2] +
+ ((((unsigned int) s->shading_buffer_b[i + 3]) << 8));
+ ++c;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+init_options (Artec48U_Scanner * s)
+{
+ int i;
+
+ XDBG ((5, "init_options: scanner %p\n", (void *) s));
+ XDBG ((5, "init_options: start\n"));
+ XDBG ((5, "init_options: num options %i\n", NUM_OPTIONS));
+
+ memset (s->val, 0, sizeof (s->val));
+ memset (s->opt, 0, sizeof (s->opt));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ s->opt[OPT_MODE_GROUP].name = "scanmode-group";
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_MODE].constraint.string_list = mode_list;
+ s->val[OPT_SCAN_MODE].s = strdup (mode_list[1]);
+
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list;
+ s->val[OPT_BIT_DEPTH].w = bitdepth_list[1];
+
+ /* black level (lineart only) */
+ s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BLACK_LEVEL].constraint.range = &blacklevel_range;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BLACK_LEVEL].w = 127;
+
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = resbit_list;
+ s->val[OPT_RESOLUTION].w = resbit_list[1];
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].name = "enhancement-group";
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_contrast_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &brightness_contrast_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* master analog gamma */
+ s->opt[OPT_GAMMA].name = SANE_NAME_ANALOG_GAMMA;
+ s->opt[OPT_GAMMA].title = SANE_TITLE_ANALOG_GAMMA;
+ s->opt[OPT_GAMMA].desc = SANE_DESC_ANALOG_GAMMA;
+ s->opt[OPT_GAMMA].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA].w = SANE_FIX (s->dev->gamma_master);
+ s->opt[OPT_GAMMA].size = sizeof (SANE_Word);
+
+ /* red analog gamma */
+ s->opt[OPT_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ s->opt[OPT_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ s->opt[OPT_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ s->opt[OPT_GAMMA_R].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_R].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA_R].w = SANE_FIX (s->dev->gamma_r);
+
+ /* green analog gamma */
+ s->opt[OPT_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ s->opt[OPT_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ s->opt[OPT_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ s->opt[OPT_GAMMA_G].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_G].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA_G].w = SANE_FIX (s->dev->gamma_g);
+
+ /* blue analog gamma */
+ s->opt[OPT_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ s->opt[OPT_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ s->opt[OPT_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ s->opt[OPT_GAMMA_B].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_B].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA_B].w = SANE_FIX (s->dev->gamma_b);
+
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].name = "default-enhancements";
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].title = SANE_I18N ("Defaults");
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].desc =
+ SANE_I18N ("Set default values for enhancement controls.");
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].size = 0;
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = "geometry-group";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = 0;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &scan_range_x;
+ s->val[OPT_TL_X].w = SANE_FIX (0.0);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &scan_range_y;
+ s->val[OPT_TL_Y].w = SANE_FIX (0.0);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &scan_range_x;
+ s->val[OPT_BR_X].w = SANE_FIX (50.0);
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &scan_range_y;
+ s->val[OPT_BR_Y].w = SANE_FIX (50.0);
+
+ /* "Calibration" group: */
+ s->opt[OPT_CALIBRATION_GROUP].name = "calibration-group";
+ s->opt[OPT_CALIBRATION_GROUP].title = SANE_I18N ("Calibration");
+ s->opt[OPT_CALIBRATION_GROUP].desc = "";
+ s->opt[OPT_CALIBRATION_GROUP].size = 0;
+ s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_CALIBRATION_GROUP].cap = 0;
+
+ /* calibrate */
+ s->opt[OPT_CALIBRATE].name = "calibration";
+ s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate before next scan");
+ s->opt[OPT_CALIBRATE].desc =
+ SANE_I18N ("If enabled, the device will be calibrated before the "
+ "next scan. Otherwise, calibration is performed "
+ "only before the first start.");
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CALIBRATE].w = SANE_FALSE;
+
+ /* calibrate */
+ s->opt[OPT_CALIBRATE_SHADING].name = "calibration-shading";
+ s->opt[OPT_CALIBRATE_SHADING].title =
+ SANE_I18N ("Only perform shading-correction");
+ s->opt[OPT_CALIBRATE_SHADING].desc =
+ SANE_I18N ("If enabled, only the shading correction is "
+ "performed during calibration. The default values "
+ "for gain, offset and exposure time, "
+ "either build-in or from the configuration file, "
+ "are used.");
+ s->opt[OPT_CALIBRATE_SHADING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CALIBRATE_SHADING].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATE_SHADING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CALIBRATE_SHADING].w = SANE_FALSE;
+#ifdef ARTEC48U_USE_BUTTONS
+ s->opt[OPT_BUTTON_STATE].name = "button-state";
+ s->opt[OPT_BUTTON_STATE].title = SANE_I18N ("Button state");
+ s->opt[OPT_BUTTON_STATE].type = SANE_TYPE_INT;
+ s->opt[OPT_BUTTON_STATE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BUTTON_STATE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_BUTTON_STATE].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_BUTTON_STATE].w = 0;
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+static void
+calculate_brightness (Artec48U_Scanner * s)
+{
+ long cnt;
+ double bright;
+
+ bright = (double) s->val[OPT_BRIGHTNESS].w;
+
+ bright *= 257.0;
+ for (cnt = 0; cnt < 65536; cnt++)
+ {
+ if (bright < 0.0)
+ s->brightness_array[cnt] =
+ (int) (((double) cnt * (65535.0 + bright)) / 65535.0);
+ else
+ s->brightness_array[cnt] =
+ (int) ((double) cnt +
+ ((65535.0 - (double) cnt) * bright) / 65535.0);
+ if (s->brightness_array[cnt] > 65535)
+ s->brightness_array[cnt] = 65535;
+ if (s->brightness_array[cnt] < 0)
+ s->brightness_array[cnt] = 0;
+ }
+}
+
+static void
+calculate_contrast (Artec48U_Scanner * s)
+{
+ int val;
+ double p;
+ int cnt;
+ double contr;
+
+ contr = (double) s->val[OPT_CONTRAST].w;
+
+ contr *= 257.0;
+
+ for (cnt = 0; cnt < 65536; cnt++)
+ {
+ if (contr < 0.0)
+ {
+ val = (int) (cnt > 32769) ? (65535 - cnt) : cnt;
+ val = (int) (32769.0 * pow ((double) (val ? val : 1) / 32769.0,
+ (32769.0 + contr) / 32769.0));
+ s->contrast_array[cnt] = (cnt > 32769) ? (65535 - val) : val;
+ if (s->contrast_array[cnt] > 65535)
+ s->contrast_array[cnt] = 65535;
+ if (s->contrast_array[cnt] < 0)
+ s->contrast_array[cnt] = 0;
+ }
+ else
+ {
+ val = (cnt > 32769) ? (65535 - cnt) : cnt;
+ p = ((int) contr == 32769) ? 32769.0 : 32769.0 / (32769.0 - contr);
+ val = (int) (32769.0 * pow ((double) val / 32769.0, p));
+ s->contrast_array[cnt] = (cnt > 32639) ? (65535 - val) : val;
+ if (s->contrast_array[cnt] > 65535)
+ s->contrast_array[cnt] = 65535;
+ if (s->contrast_array[cnt] < 0)
+ s->contrast_array[cnt] = 0;
+ }
+ }
+}
+
+/*
+ The calibration function
+ Disclaimer: the following might be complete crap :-)
+ -Gain, offset, exposure time
+ It seems, that the gain values are actually constants. The windows driver always
+ uses the values 0x0a,0x03,0x03, during calibration as well as during a normal
+ scan. The exposure values are set to 0x04 for black calibration. It's not necessary to
+ move the scan head during this stage.
+ Calibration starts with default values for offset/exposure. These values are
+ increased/decreased until the white and black values are within a specific range, defined
+ by WHITE_MIN, WHITE_MAX, BLACK_MIN and BLACK_MAX.
+
+ -White shading correction
+ The scanning head is moved some lines over the calibration strip. Some lines
+ are scanned at 600dpi/16bit over the full width. The average values are used for the
+ shading buffer. The normal exposure values are used.
+ -Black shading correction
+ Works like the white shading correction, with the difference, that the red-, green-
+ and blue exposure time is set to 0x04 (the value is taken from the windoze driver).
+ -Since we do this over the whole width of the image with the maximal optical resolution,
+ we can use the shading data for every scan, independend of the size, position or resolution,
+ because we have the shading values for every sensor/LED.
+
+ Note:
+ For a CIS device, it's sufficient to determine those values once. It's not necessary, to
+ repeat the calibration sequence before every new scan. The windoze driver even saves the values
+ to various files to avoid the quite lengthy calibration sequence. This backend can also save
+ the values to files. For this purpose, the user has to create a hidden directory called
+ .artec-eplus48u in his/her home directory. If the user insists on calibration
+ before every new scan, he/she can enable a specific option in the backend.
+*/
+static SANE_Status
+calibrate_scanner (SANE_Handle handle)
+{
+ Artec48U_Scanner *s = handle;
+ unsigned int *buffer_pointers[3];
+ int avg_black[3];
+ int avg_white[3];
+ int exp_off;
+ int c;
+ int finish = 0;
+ int noloop = 0;
+
+
+ if ((s->val[OPT_CALIBRATE].w == SANE_TRUE) &&
+ (s->val[OPT_CALIBRATE_SHADING].w == SANE_FALSE))
+ {
+ while (finish == 0)
+ {
+ finish = 1;
+ /*get black values */
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_BLACK,
+ SANE_FALSE, &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_OFFSET_1,
+ &(s->params));
+
+ for (c = 0; c < s->dev->shading_lines_b; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ /* we abuse the shading buffer for the offset calculation */
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_offset_buffer (s, &avg_black[0], &avg_black[1],
+ &avg_black[2]);
+ s->scanning = SANE_FALSE;
+ XDBG ((1, "avg_r: %i, avg_g: %i, avg_b: %i\n", avg_black[0],
+ avg_black[1], avg_black[2]));
+ /*adjust offset */
+ for (c = 0; c < 3; c++)
+ {
+ if (c == 0)
+ {
+ if (avg_black[c] < BLACK_MIN)
+ {
+ s->dev->afe_params.r_offset -= 1;
+ finish = 0;
+ XDBG ((1, "adjust offset r: -1\n"));
+ }
+ else if (avg_black[c] > BLACK_MAX)
+ {
+ s->dev->afe_params.r_offset += 1;
+ finish = 0;
+ XDBG ((1, "adjust offset r: +1\n"));
+ }
+ }
+ if (c == 1)
+ {
+ if (avg_black[c] < BLACK_MIN)
+ {
+ s->dev->afe_params.g_offset -= 1;
+ finish = 0;
+ XDBG ((1, "adjust offset g: -1\n"));
+ }
+ else if (avg_black[c] > BLACK_MAX)
+ {
+ s->dev->afe_params.g_offset += 1;
+ finish = 0;
+ XDBG ((1, "adjust offset g: +1\n"));
+ }
+ }
+ if (c == 2)
+ {
+ if (avg_black[c] < BLACK_MIN)
+ {
+ s->dev->afe_params.b_offset -= 1;
+ finish = 0;
+ XDBG ((1, "adjust offset b: -1\n"));
+ }
+ else if (avg_black[c] > BLACK_MAX)
+ {
+ s->dev->afe_params.b_offset += 1;
+ finish = 0;
+ XDBG ((1, "adjust offset b: +1\n"));
+ }
+ }
+ }
+
+ /*adjust exposure */
+ /*get white values */
+
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_WHITE,
+ SANE_FALSE, &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_EXPOSURE_1,
+ &(s->params));
+
+ for (c = 0; c < s->dev->shading_lines_w; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ /* we abuse the shading buffer for the exposure calculation */
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_exposure_buffer (s, &avg_white[0], &avg_white[1],
+ &avg_white[2]);
+ s->scanning = SANE_FALSE;
+ XDBG ((1, "avg_r: %i, avg_g: %i, avg_b: %i\n", avg_white[0],
+ avg_white[1], avg_white[2]));
+ for (c = 0; c < 3; c++)
+ {
+ if (c == 0)
+ {
+ if (avg_white[c] < WHITE_MIN)
+ {
+ exp_off =
+ ((WHITE_MAX + WHITE_MIN) / 2 -
+ avg_white[c]) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.r_time += exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure r: ++\n"));
+ }
+ else if (avg_white[c] > WHITE_MAX)
+ {
+ exp_off =
+ (avg_white[c] -
+ (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.r_time -= exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure r: --\n"));
+ }
+ }
+ else if (c == 1)
+ {
+ if (avg_white[c] < WHITE_MIN)
+ {
+ exp_off =
+ ((WHITE_MAX + WHITE_MIN) / 2 -
+ avg_white[c]) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.g_time += exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure g: ++\n"));
+ }
+ else if (avg_white[c] > WHITE_MAX)
+ {
+ exp_off =
+ (avg_white[c] -
+ (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.g_time -= exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure g: --\n"));
+ }
+ }
+ else if (c == 2)
+ {
+ if (avg_white[c] < WHITE_MIN)
+ {
+ exp_off =
+ ((WHITE_MAX + WHITE_MIN) / 2 -
+ avg_white[c]) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.b_time += exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure b: ++\n"));
+ }
+ else if (avg_white[c] > WHITE_MAX)
+ {
+ exp_off =
+ (avg_white[c] -
+ (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.b_time -= exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure b: --\n"));
+ }
+ }
+ }
+
+ XDBG ((1, "time_r: %x, time_g: %x, time_b: %x\n",
+ s->dev->exp_params.r_time, s->dev->exp_params.g_time,
+ s->dev->exp_params.b_time));
+ XDBG ((1, "offset_r: %x, offset_g: %x, offset_b: %x\n",
+ s->dev->afe_params.r_offset, s->dev->afe_params.g_offset,
+ s->dev->afe_params.b_offset));
+ ++noloop;
+ if (noloop > 10)
+ break;
+ }
+ }
+
+ XDBG ((1, "option redOffset 0x%x\n", s->dev->afe_params.r_offset));
+ XDBG ((1, "option greenOffset 0x%x\n", s->dev->afe_params.g_offset));
+ XDBG ((1, "option blueOffset 0x%x\n", s->dev->afe_params.b_offset));
+ XDBG ((1, "option redExposure 0x%x\n", s->dev->exp_params.r_time));
+ XDBG ((1, "option greenExposure 0x%x\n", s->dev->exp_params.g_time));
+ XDBG ((1, "option blueExposure 0x%x\n", s->dev->exp_params.b_time));
+
+ s->dev->artec_48u_afe_params.r_offset = s->dev->afe_params.r_offset;
+ s->dev->artec_48u_afe_params.g_offset = s->dev->afe_params.g_offset;
+ s->dev->artec_48u_afe_params.b_offset = s->dev->afe_params.b_offset;
+ /*don't forget the gain */
+ s->dev->artec_48u_afe_params.r_pga = s->dev->afe_params.r_pga;
+ s->dev->artec_48u_afe_params.g_pga = s->dev->afe_params.g_pga;
+ s->dev->artec_48u_afe_params.b_pga = s->dev->afe_params.b_pga;
+
+ s->dev->artec_48u_exposure_params.r_time = s->dev->exp_params.r_time;
+ s->dev->artec_48u_exposure_params.g_time = s->dev->exp_params.g_time;
+ s->dev->artec_48u_exposure_params.b_time = s->dev->exp_params.b_time;
+
+ /*******************************
+ *get the black shading values *
+ *******************************/
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_BLACK, SANE_FALSE,
+ &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_BLACK,
+ &(s->params));
+
+ for (c = 0; c < s->dev->shading_lines_b; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_shading_buffer (s, SANE_FALSE);
+ s->scanning = SANE_FALSE;
+
+ /*******************************
+ *get the white shading values *
+ *******************************/
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_WHITE, SANE_FALSE,
+ &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_WHITE,
+ &(s->params));
+ for (c = 0; c < s->dev->shading_lines_w; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_shading_buffer (s, SANE_TRUE);
+ s->scanning = SANE_FALSE;
+ save_calibration_data (s);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+close_pipe (Artec48U_Scanner * s)
+{
+ if (s->pipe >= 0)
+ {
+ XDBG ((1, "close_pipe\n"));
+ close (s->pipe);
+ s->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+static RETSIGTYPE
+sigalarm_handler (int signal)
+{
+ int dummy; /*Henning doesn't like warnings :-) */
+ XDBG ((1, "ALARM!!!\n"));
+ dummy = signal;
+ cancelRead = SANE_TRUE;
+}
+
+static void
+sig_chldhandler (int signo)
+{
+ XDBG ((1, "Child is down (signal=%d)\n", signo));
+}
+
+static int
+reader_process (void * data)
+{
+ Artec48U_Scanner * s = (Artec48U_Scanner *) data;
+ int fd = s->reader_pipe;
+
+ SANE_Status status;
+ struct SIGACTION act;
+ sigset_t ignore_set;
+ ssize_t bytes_written = 0;
+
+ XDBG ((1, "reader process...\n"));
+
+ if (sanei_thread_is_forked()) close (s->pipe);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+ sigdelset (&ignore_set, SIGUSR1);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+ sigaction (SIGUSR1, &act, 0);
+
+ cancelRead = SANE_FALSE;
+ if (sigemptyset (&(act.sa_mask)) < 0)
+ XDBG ((2, "(child) reader_process: sigemptyset() failed\n"));
+ act.sa_flags = 0;
+
+ act.sa_handler = reader_process_sigterm_handler;
+ if (sigaction (SIGTERM, &act, 0) < 0)
+ XDBG ((2, "(child) reader_process: sigaction(SIGTERM,...) failed\n"));
+
+ act.sa_handler = usb_reader_process_sigterm_handler;
+ if (sigaction (SIGUSR1, &act, 0) < 0)
+ XDBG ((2, "(child) reader_process: sigaction(SIGUSR1,...) failed\n"));
+
+
+ XDBG ((2, "(child) reader_process: s=%p, fd=%d\n", (void *) s, fd));
+
+ /*read line by line into buffer */
+ /*copy buffer pointers to line_buffer */
+ XDBG ((2, "(child) reader_process: byte_cnt %d\n", (int) s->byte_cnt));
+ s->eof = SANE_FALSE;
+ while (s->lines_to_read > 0)
+ {
+ if (cancelRead == SANE_TRUE)
+ {
+ XDBG ((2, "(child) reader_process: cancelRead == SANE_TRUE\n"));
+ s->scanning = SANE_FALSE;
+ s->eof = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+ if (s->scanning != SANE_TRUE)
+ {
+ XDBG ((2, "(child) reader_process: scanning != SANE_TRUE\n"));
+ return SANE_STATUS_CANCELLED;
+ }
+ status = artec48u_scanner_read_line (s, s->buffer_pointers, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "(child) reader_process: scanner_read_line failed\n"));
+ return SANE_STATUS_IO_ERROR;
+ }
+ copy_scan_line (s);
+ s->lines_to_read -= 1;
+ bytes_written =
+ write (fd, s->line_buffer, s->sane_params.bytes_per_line);
+
+ if (bytes_written < 0)
+ {
+ XDBG ((2, "(child) reader_process: write returned %s\n",
+ strerror (errno)));
+ s->eof = SANE_FALSE;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ XDBG ((2, "(child) reader_process: lines to read %i\n", s->lines_to_read));
+ }
+ s->eof = SANE_TRUE;
+ close (fd);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (Artec48U_Scanner * s, SANE_Bool closepipe)
+{
+ struct SIGACTION act;
+ SANE_Pid res;
+ XDBG ((1, "do_cancel\n"));
+
+ s->scanning = SANE_FALSE;
+
+ if (s->reader_pid != -1)
+ {
+ /*parent */
+ XDBG ((1, "killing reader_process\n"));
+ /* tell the driver to stop scanning */
+ sigemptyset (&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+
+ if (sigaction (SIGALRM, &act, 0) == -1)
+ XDBG ((1, "sigaction() failed !\n"));
+
+ /* kill our child process and wait until done */
+ alarm (10);
+ if (sanei_thread_kill (s->reader_pid) < 0)
+ XDBG ((1, "sanei_thread_kill() failed !\n"));
+ res = sanei_thread_waitpid (s->reader_pid, 0);
+ alarm (0);
+
+ if (res != s->reader_pid)
+ {
+ XDBG ((1, "sanei_thread_waitpid() failed !\n"));
+ }
+ s->reader_pid = -1;
+ XDBG ((1, "reader_process killed\n"));
+ }
+ if (SANE_TRUE == closepipe)
+ {
+ close_pipe (s);
+ XDBG ((1, "pipe closed\n"));
+ }
+ artec48u_scanner_stop_scan (s);
+ artec48u_carriage_home (s->dev);
+ if (s->line_buffer)
+ {
+ XDBG ((2, "freeing line_buffer\n"));
+ free (s->line_buffer);
+ s->line_buffer = NULL;
+ }
+ if (s->lineart_buffer)
+ {
+ XDBG ((2, "freeing lineart_buffer\n"));
+ free (s->lineart_buffer);
+ s->lineart_buffer = NULL;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Artec48U_Device *dev;
+ SANE_Int dev_num;
+
+ XDBG ((5, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false"));
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ dev_num = 0;
+ for (dev = first_dev; dev_num < num_devices; dev = dev->next)
+ {
+ devlist[dev_num] = &dev->sane;
+ XDBG ((3, "sane_get_devices: name %s\n", dev->sane.name));
+ XDBG ((3, "sane_get_devices: vendor %s\n", dev->sane.vendor));
+ XDBG ((3, "sane_get_devices: model %s\n", dev->sane.model));
+ ++dev_num;
+ }
+ devlist[dev_num] = 0;
+ ++dev_num;
+
+ *device_list = devlist;
+
+ XDBG ((5, "sane_get_devices: exit\n"));
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+load_calibration_data (Artec48U_Scanner * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ FILE *f = 0;
+ size_t cnt;
+ char path[PATH_MAX];
+ char filename[PATH_MAX];
+
+ s->calibrated = SANE_FALSE;
+ path[0] = 0;
+ if (strlen (getenv ("HOME")) < (PATH_MAX - 1))
+ strcat (path, getenv ("HOME"));
+ else
+ return SANE_STATUS_INVAL;
+
+ if (strlen (path) < (PATH_MAX - 1 - strlen ("/.artec_eplus48u/")))
+ strcat (path, "/.artec_eplus48u/");
+ else
+ return SANE_STATUS_INVAL;
+
+ /*try to load black shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_black")))
+ strcat (filename, "artec48ushading_black");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read black shading file: \"%s\"\n", filename));
+
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+
+ /*read values */
+ cnt = fread (s->shading_buffer_b, sizeof (unsigned char), 30720*s->dev->epro_mult, f); /*epro*/
+ if (cnt != (30720*s->dev->epro_mult)) /*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not load black shading file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to load white shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_white")))
+ strcat (filename, "artec48ushading_white");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read white shading file: \"%s\"\n", filename));
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt = fread (s->shading_buffer_w, sizeof (unsigned char), 30720*s->dev->epro_mult, f);/*epro*/
+ if (cnt != (30720*s->dev->epro_mult)) /*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not load white shading file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to load offset file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uoffset")))
+ strcat (filename, "artec48uoffset");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read offset file: \"%s\"\n", filename));
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fread (&s->dev->artec_48u_afe_params, sizeof (Artec48U_AFE_Parameters), 1,
+ f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not load offset file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*load exposure file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uexposure")))
+ strcat (filename, "artec48uexposure");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read exposure file: \"%s\"\n", filename));
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fread (&s->dev->artec_48u_exposure_params,
+ sizeof (Artec48U_Exposure_Parameters), 1, f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not load exposure file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+ s->calibrated = SANE_TRUE;
+ return status;
+}
+
+static SANE_Status
+save_calibration_data (Artec48U_Scanner * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ FILE *f = 0;
+ size_t cnt;
+ char path[PATH_MAX];
+ char filename[PATH_MAX];
+ mode_t mode = S_IRUSR | S_IWUSR;
+
+ path[0] = 0;
+ if (strlen (getenv ("HOME")) < (PATH_MAX - 1))
+ strcat (path, getenv ("HOME"));
+ else
+ return SANE_STATUS_INVAL;
+
+ if (strlen (path) < (PATH_MAX - 1 - strlen ("/.artec_eplus48u/")))
+ strcat (path, "/.artec_eplus48u/");
+ else
+ return SANE_STATUS_INVAL;
+
+ /*try to save black shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_black")))
+ strcat (filename, "artec48ushading_black");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to save black shading file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ {
+ XDBG ((1, "Could not save artec48ushading_black\n"));
+ return SANE_STATUS_INVAL;
+ }
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+
+ /*read values */
+ cnt = fwrite (s->shading_buffer_b, sizeof (unsigned char), 30720*s->dev->epro_mult, f); /*epro*/
+ XDBG ((1, "Wrote %li bytes to black shading buffer \n", (u_long) cnt));
+ if (cnt != (30720*s->dev->epro_mult))/*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not write black shading buffer\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to save white shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_white")))
+ strcat (filename, "artec48ushading_white");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to save white shading file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt = fwrite (s->shading_buffer_w, sizeof (unsigned char), 30720*s->dev->epro_mult, f);/*epro*/
+ if (cnt != (30720*s->dev->epro_mult)) /*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not write white shading buffer\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to save offset file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uoffset")))
+ strcat (filename, "artec48uoffset");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to write offset file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fwrite (&s->dev->artec_48u_afe_params, sizeof (Artec48U_AFE_Parameters),
+ 1, f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not write afe values\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to write exposure file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uexposure")))
+ strcat (filename, "artec48uexposure");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to write exposure file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fwrite (&s->dev->artec_48u_exposure_params,
+ sizeof (Artec48U_Exposure_Parameters), 1, f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not write exposure values\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+ return status;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status = SANE_STATUS_INVAL;
+ Artec48U_Device *dev = 0;
+ Artec48U_Scanner *s = 0;
+
+ if (!devicename)
+ return SANE_STATUS_INVAL;
+ XDBG ((2, "sane_open: devicename = \"%s\"\n", devicename));
+
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ XDBG ((2, "sane_open: found matching device %s\n",
+ dev->sane.name));
+ break;
+ }
+ }
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ XDBG ((2, "sane_open: attach failed %s\n", devicename));
+ }
+ }
+ else
+ {
+ /* empty devicename -> use first device */
+ XDBG ((2, "sane_open: empty devicename\n"));
+ dev = first_dev;
+ }
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ status = artec48u_device_open (dev);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "could not open device\n"));
+ return status;
+ }
+ XDBG ((2, "sane_open: opening device `%s', handle = %p\n", dev->sane.name,
+ (void *) dev));
+
+ XDBG ((1, "sane_open - %s\n", dev->sane.name));
+ XDBG ((2, "sane_open: try to open %s\n", dev->sane.name));
+
+ status = artec48u_device_activate (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "could not activate device\n"));
+ return status;
+ }
+ /* We do not check anymore, whether the firmware is already loaded */
+ /* because that caused problems after rebooting; furthermore, loading */
+ /* of the firmware is fast, therefore the test doesn't make much sense */
+ status = download_firmware_file (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "download_firmware_file failed\n"));
+ return status;
+ }
+ /* If a scan is interrupted without sending stop_scan, bad things happen.
+ * Send the stop scan command now just in case. */
+ artec48u_stop_scan (dev);
+
+ artec48u_wait_for_positioning (dev);
+
+ artec48u_scanner_new (dev, &s);
+ init_calibrator (s);
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+
+ status = init_options (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ /*Try to load the calibration values */
+ status = load_calibration_data (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Artec48U_Scanner *prev, *s;
+
+ XDBG ((5, "sane_close: start\n"));
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ XDBG ((5, "close: invalid handle %p\n", handle));
+ return;
+ }
+ artec48u_device_close (s->dev);
+ artec48u_scanner_free (s);
+ XDBG ((5, "sane_close: exit\n"));
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Artec48U_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ XDBG ((5, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option));
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ Artec48U_Scanner *s = handle;
+#ifdef ARTEC48U_USE_BUTTONS
+ SANE_Int button_state;
+#endif
+ SANE_Status status;
+ XDBG ((8, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ (void *) handle, option, action, (void *) value, (void *) info));
+
+ if (info)
+ *info = 0;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ return SANE_STATUS_INVAL;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_VALUE:
+ if (s->scanning == SANE_TRUE)
+ return SANE_STATUS_INVAL;
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, value, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ if(s->dev->is_epro != 0)
+ {
+ if((s->val[option].w == 1200) && (*(SANE_Word *) value < 1200))
+ {
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ else if((s->val[option].w < 1200) && (*(SANE_Word *) value == 1200))
+ {
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list2;
+ if(s->val[OPT_BIT_DEPTH].w > 8)
+ s->val[OPT_BIT_DEPTH].w = 8;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ }
+ s->val[option].w = *(SANE_Word *) value;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+ /* fall through */
+ case OPT_BIT_DEPTH:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) value;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ /* fall through */
+ case OPT_BLACK_LEVEL:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_GAMMA:
+ case OPT_GAMMA_R:
+ case OPT_GAMMA_G:
+ case OPT_GAMMA_B:
+ case OPT_CALIBRATE:
+ case OPT_CALIBRATE_SHADING:
+ s->val[option].w = *(SANE_Word *) value;
+ return SANE_STATUS_GOOD;
+ case OPT_DEFAULT_ENHANCEMENTS:
+ s->val[OPT_GAMMA].w = SANE_FIX (s->dev->gamma_master);
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[2]) == 0)
+ {
+ s->val[OPT_GAMMA_R].w = SANE_FIX (s->dev->gamma_r);
+ s->val[OPT_GAMMA_G].w = SANE_FIX (s->dev->gamma_g);
+ s->val[OPT_GAMMA_B].w = SANE_FIX (s->dev->gamma_b);
+ }
+ s->val[OPT_BRIGHTNESS].w = 0;
+ s->val[OPT_CONTRAST].w = 0;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_SCAN_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (value);
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0)
+ {
+ s->opt[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[1]) == 0)
+ {
+ s->opt[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ }
+ break;
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_BLACK_LEVEL:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_GAMMA:
+ case OPT_GAMMA_R:
+ case OPT_GAMMA_G:
+ case OPT_GAMMA_B:
+ case OPT_CALIBRATE:
+ case OPT_CALIBRATE_SHADING:
+ *(SANE_Word *) value = (SANE_Word) s->val[option].w;
+ return SANE_STATUS_GOOD;
+ /* string options: */
+ case OPT_SCAN_MODE:
+ strcpy (value, s->val[option].s);
+ return SANE_STATUS_GOOD;
+#ifdef ARTEC48U_USE_BUTTONS
+ case OPT_BUTTON_STATE:
+ status = artec48u_check_buttons (s->dev, &button_state);
+ if (status == SANE_STATUS_GOOD)
+ {
+ s->val[option].w = button_state;
+ *(SANE_Int *) value = (SANE_Int) s->val[option].w;
+ }
+ else
+ {
+ s->val[option].w = 0;
+ *(SANE_Int *) value = 0;
+ }
+ return SANE_STATUS_GOOD;
+#endif
+ }
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Artec48U_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word resx;
+/* int scan_mode;*/
+ SANE_String str = s->val[OPT_SCAN_MODE].s;
+ int tlx;
+ int tly;
+ int brx;
+ int bry;
+ int tmp;
+ XDBG ((2, "sane_get_params: string %s\n", str));
+ XDBG ((2, "sane_get_params: enter\n"));
+
+ tlx = s->val[OPT_TL_X].w;
+ tly = s->val[OPT_TL_Y].w;
+ brx = s->val[OPT_BR_X].w;
+ bry = s->val[OPT_BR_Y].w;
+
+ /*make sure, that tlx < brx and tly < bry
+ this will NOT change the options */
+ if (tlx > brx)
+ {
+ tmp = tlx;
+ tlx = brx;
+ brx = tmp;
+ }
+ if (tly > bry)
+ {
+ tmp = tly;
+ tly = bry;
+ bry = tmp;
+ }
+ resx = s->val[OPT_RESOLUTION].w;
+ str = s->val[OPT_SCAN_MODE].s;
+
+ s->request.color = SANE_TRUE;
+ if ((strcmp (str, mode_list[0]) == 0) || (strcmp (str, mode_list[1]) == 0))
+ s->request.color = SANE_FALSE;
+ else
+ s->request.color = SANE_TRUE;
+ s->request.depth = s->val[OPT_BIT_DEPTH].w;
+ if (strcmp (str, mode_list[0]) == 0)
+ s->request.depth = 8;
+ s->request.y0 = tly; /**< Top boundary */
+ s->request.x0 = SANE_FIX (216.0) - brx; /**< left boundary */
+ s->request.xs = brx - tlx; /**< Width */
+ s->request.ys = bry - tly; /**< Height */
+ s->request.xdpi = resx; /**< Horizontal resolution */
+ s->request.ydpi = resx; /**< Vertical resolution */
+ /*epro*/
+ if ((resx == 1200) && (s->dev->is_epro == 0))
+ s->request.xdpi = 600;/**< Vertical resolution */
+
+ status = artec48u_setup_scan (s, &(s->request), SA_SCAN,
+ SANE_TRUE, &(s->params));
+ if (status != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+/*DBG(1, "sane_get_params: scan_mode %i\n",scan_mode);*/
+
+ params->depth = s->params.depth;
+ s->params.lineart = SANE_FALSE;
+ if (s->params.color == SANE_TRUE)
+ {
+ params->format = SANE_FRAME_RGB;
+ params->bytes_per_line = s->params.pixel_xs * 3;
+ }
+ else
+ {
+ params->format = SANE_FRAME_GRAY;
+ params->bytes_per_line = s->params.pixel_xs;
+ if (strcmp (str, mode_list[0]) == 0)
+ {
+ params->depth = 1;
+ params->bytes_per_line = (s->params.pixel_xs + 7) / 8;
+ s->params.lineart = SANE_TRUE;
+ }
+ }
+ if ((resx == 1200) && (s->dev->is_epro == 0))
+ {
+ if (params->depth == 1)
+ params->bytes_per_line = (s->params.pixel_xs * 2 + 7) / 8;
+ else
+ params->bytes_per_line *= 2;
+ }
+ if (params->depth == 16)
+ params->bytes_per_line *= 2;
+ params->last_frame = SANE_TRUE;
+ params->pixels_per_line = s->params.pixel_xs;
+ if ((resx == 1200) && (s->dev->is_epro == 0))
+ params->pixels_per_line *= 2;
+ params->lines = s->params.pixel_ys;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Artec48U_Scanner *s = handle;
+ SANE_Status status;
+ int fds[2];
+
+ if (s->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (sane_get_parameters (handle, &s->sane_params) != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+ if ((s->calibrated != SANE_TRUE) || (s->val[OPT_CALIBRATE].w == SANE_TRUE))
+ {
+ XDBG ((1, "Must calibrate scanner\n"));
+ status = calibrate_scanner (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ s->calibrated = SANE_TRUE;
+ }
+ if (sane_get_parameters (handle, &s->sane_params) != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+ calculate_brightness (s);
+ calculate_contrast (s);
+ calculateGamma (s);
+ calculateGammaRed (s);
+ calculateGammaGreen (s);
+ calculateGammaBlue (s);
+
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+ s->byte_cnt = 0;
+ s->lines_to_read = s->params.pixel_ys;
+ /*allocate a buffer, that can hold a complete scan line */
+ /*If resolution is 1200 dpi and we are scanning in lineart mode,
+ then we also allocate a lineart_buffer, which can hold a complete scan line
+ in 8 bit/gray. This makes interpolation easier. */
+ if ((s->params.ydpi == 1200) && (s->dev->is_epro == 0))
+ {
+ if (s->request.color == SANE_TRUE)
+ {
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 8);
+ }
+ else
+ {
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 4);
+ /*lineart ? */
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0)
+ s->lineart_buffer = (SANE_Byte *) malloc (s->params.pixel_xs * 2);
+ }
+ }
+ else
+ {
+ if (s->request.color == SANE_TRUE)
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 4);
+ else
+ {
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 2);
+ /*lineart ? */
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0)
+ s->lineart_buffer = (SANE_Byte *) malloc (s->params.pixel_xs * 2);
+ }
+ }
+ if (pipe (fds) < 0)
+ {
+ s->scanning = SANE_FALSE;
+ XDBG ((2, "sane_start: pipe failed (%s)\n", strerror (errno)));
+ return SANE_STATUS_IO_ERROR;
+ }
+ status = artec48u_scanner_start_scan (s, &s->request, &s->params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "sane_start: could not start scan\n"));
+ return status;
+ }
+ s->pipe = fds[0];
+ s->reader_pipe = fds[1];
+ s->reader_pid = sanei_thread_begin (reader_process, s);
+ cancelRead = SANE_FALSE;
+ if (s->reader_pid == -1)
+ {
+ s->scanning = SANE_FALSE;
+ XDBG ((2, "sane_start: sanei_thread_begin failed (%s)\n", strerror (errno)));
+ return SANE_STATUS_NO_MEM;
+ }
+ signal (SIGCHLD, sig_chldhandler);
+
+ if (sanei_thread_is_forked()) close (s->reader_pipe);
+
+ XDBG ((1, "sane_start done\n"));
+
+ return SANE_STATUS_GOOD; /* parent */
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Artec48U_Scanner *s = handle;
+ ssize_t nread;
+
+ *length = 0;
+
+ /* here we read all data from the driver... */
+ nread = read (s->pipe, data, max_length);
+ XDBG ((3, "sane_read - read %ld bytes\n", (long) nread));
+ if (cancelRead == SANE_TRUE)
+ {
+ return do_cancel (s, SANE_TRUE);
+ }
+
+ if (nread < 0)
+ {
+ if (EAGAIN == errno)
+ {
+ /* if we already had read the picture, so it's okay and stop */
+ if (s->eof == SANE_TRUE)
+ {
+ sanei_thread_waitpid (s->reader_pid, 0);
+ s->reader_pid = -1;
+ artec48u_scanner_stop_scan (s);
+ artec48u_carriage_home (s->dev);
+ return close_pipe (s);
+ }
+ /* else force the frontend to try again */
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ XDBG ((4, "ERROR: errno=%d\n", errno));
+ do_cancel (s, SANE_TRUE);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *length = nread;
+ s->byte_cnt += nread;
+
+ /* nothing read means that we're finished OR we had a problem... */
+ if (0 == nread)
+ {
+ if (0 == s->byte_cnt)
+ {
+ s->exit_code = sanei_thread_get_status (s->reader_pid);
+
+ if (SANE_STATUS_GOOD != s->exit_code)
+ {
+ close_pipe (s);
+ return s->exit_code;
+ }
+ }
+ return close_pipe (s);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Artec48U_Scanner *s = handle;
+ XDBG ((2, "sane_cancel: handle = %p\n", handle));
+ if (s->scanning)
+ do_cancel (s, SANE_FALSE);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Artec48U_Scanner *s = (Artec48U_Scanner *) handle;
+
+ XDBG ((1, "sane_set_io_mode: non_blocking=%d\n", non_blocking));
+
+ if (!s->scanning)
+ {
+ XDBG ((4, "ERROR: not scanning !\n"));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (-1 == s->pipe)
+ {
+ XDBG ((4, "ERROR: not supported !\n"));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ XDBG ((4, "ERROR: can?t set to non-blocking mode !\n"));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ XDBG ((1, "sane_set_io_mode done\n"));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Artec48U_Scanner *s = (Artec48U_Scanner *) handle;
+
+ XDBG ((1, "sane_get_select_fd\n"));
+
+ if (!s->scanning)
+ {
+ XDBG ((4, "ERROR: not scanning !\n"));
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->pipe;
+
+ XDBG ((1, "sane_get_select_fd done\n"));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ Artec48U_Device *device = 0;
+ SANE_Status status;
+ char str[PATH_MAX] = _DEFAULT_DEVICE;
+ char temp[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ double gamma_m = 1.9;
+ double gamma_r = 1.0;
+ double gamma_g = 1.0;
+ double gamma_b = 1.0;
+ int epro_default = 0;
+
+ DBG_INIT ();
+ eProMult = 1;
+ isEPro = 0;
+ temp[0] = 0;
+ strcpy (vendor_string, "Artec");
+ strcpy (model_string, "E+ 48U");
+
+ sanei_usb_init ();
+ sanei_thread_init ();
+
+ /* do some presettings... */
+ auth = authorize;
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (ARTEC48U_CONFIG_FILE);
+
+ /* default to _DEFAULT_DEVICE instead of insisting on config file */
+ if (NULL == fp)
+ {
+ status = attach (_DEFAULT_DEVICE, &device);
+ return status;
+ }
+
+ while (sanei_config_read (str, sizeof (str), fp))
+ {
+ XDBG ((1, "sane_init, >%s<\n", str));
+ /* ignore line comments */
+ if (str[0] == '#')
+ continue;
+ len = strlen (str);
+ /* ignore empty lines */
+ if (0 == len)
+ continue;
+ /* check for options */
+ if (0 == strncmp (str, "option", 6))
+ {
+ if(decodeVal (str,"ePlusPro",_INT, &isEPro,&epro_default) == SANE_TRUE)
+ {
+ eProMult = 1;
+ if(isEPro != 0)
+ {
+ eProMult = 2;
+ XDBG ((3, "Is Artec E Pro\n"));
+ }
+ else
+ XDBG ((3, "Is Artec E+ 48U\n"));
+ }
+ decodeVal (str, "masterGamma", _FLOAT, &gamma_master_default,
+ &gamma_m);
+ decodeVal (str, "redGamma", _FLOAT, &gamma_r_default, &gamma_r);
+ decodeVal (str, "greenGamma", _FLOAT, &gamma_g_default, &gamma_g);
+ decodeVal (str, "blueGamma", _FLOAT, &gamma_b_default, &gamma_b);
+ decodeVal (str, "redOffset", _BYTE, &afe_params.r_offset,
+ &default_afe_params.r_offset);
+ decodeVal (str, "greenOffset", _BYTE, &afe_params.g_offset,
+ &default_afe_params.g_offset);
+ decodeVal (str, "blueOffset", _BYTE, &afe_params.b_offset,
+ &default_afe_params.b_offset);
+
+ decodeVal (str, "redExposure", _INT, &exp_params.r_time,
+ &default_exp_params.r_time);
+ decodeVal (str, "greenExposure", _INT, &exp_params.g_time,
+ &default_exp_params.g_time);
+ decodeVal (str, "blueExposure", _INT, &exp_params.b_time,
+ &default_exp_params.b_time);
+
+ decodeVal (str, "modelString", _STRING, model_string, model_string);
+ decodeVal (str, "vendorString", _STRING, vendor_string,
+ vendor_string);
+
+ decodeVal (str, "artecFirmwareFile", _STRING, firmwarePath,
+ firmwarePath);
+ }
+ else if (0 == strncmp (str, "usb", 3))
+ {
+ if (temp[0] != 0)
+ {
+ XDBG ((3, "trying to attach: %s\n", temp));
+ XDBG ((3, " vendor: %s\n", vendor_string));
+ XDBG ((3, " model: %s\n", model_string));
+ sanei_usb_attach_matching_devices (temp, attach_one_device);
+ }
+ /*save config line in temp */
+ strcpy (temp, str);
+ }
+ else if (0 == strncmp (str, "device", 6))
+ {
+ if (SANE_TRUE == decodeDevName (str, devName))
+ {
+ if (devName[0] != 0)
+ sanei_usb_attach_matching_devices (devName,
+ attach_one_device);
+ temp[0] = 0;
+ }
+ }
+ else
+ {
+ /* ignore other stuff... */
+ XDBG ((1, "ignoring >%s<\n", str));
+ }
+ }
+ if (temp[0] != 0)
+ {
+ XDBG ((3, "trying to attach: %s\n", temp));
+ XDBG ((3, " vendor: %s\n", vendor_string));
+ XDBG ((3, " model: %s\n", model_string));
+ sanei_usb_attach_matching_devices (temp, attach_one_device);
+ temp[0] = 0;
+ }
+
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Artec48U_Device *dev, *next;
+
+ XDBG ((5, "sane_exit: start\n"));
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ /*function will check, whether device is really open */
+ artec48u_device_close (dev);
+ artec48u_device_free (dev);
+ }
+ XDBG ((5, "sane_exit: exit\n"));
+ return;
+}
diff --git a/backend/artec_eplus48u.conf.in b/backend/artec_eplus48u.conf.in
new file mode 100644
index 0000000..0b20c6e
--- /dev/null
+++ b/backend/artec_eplus48u.conf.in
@@ -0,0 +1,119 @@
+# artec_eplus48u - SANE Backend configuration file
+
+# This section is for use with the Artec E+ 48U scanner
+# This scanner is also sold as
+# Tevion MD 9693, Medion MD 9705, Medion MD 9693, Medion MD4394
+# This sections contains verbose description of each option.
+# For other scanners, see below.
+#
+# The USB section:
+# each device needs at least the following line:
+# usb vendor-ID and product-ID
+# Every device configuration in this file must begin with an usb entry.
+usb 0x05d8 0x4003
+
+# Path to the firmware file
+# This file comes with the Windows driver
+# The scanner won't work without it
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb
+
+# Is the scanner an Artec E Plus Pro (or compatible)? 0 = no, 1 = yes
+option ePlusPro 0
+
+# for adjusting the default gamma values
+option redGamma 1.0
+option greenGamma 1.0
+option blueGamma 1.0
+option masterGamma 1.9
+
+#Use this options to set the default offset and exposure time values.
+option redOffset 0x28
+option greenOffset 0x2f
+option blueOffset 0x2f
+option redExposure 0xa7
+option greenExposure 0x116
+option blueExposure 0xdc
+
+# The vendor and model string
+# This string is displayed by the frontends. If you do not want to get your
+# scanner reported as "Artec E+ 48U", then change the option accordingly.
+option vendorString "Artec"
+option modelString "E+ 48U"
+
+# device-name
+#
+# If autodetection does not work, then you can specify the device here
+# The device entry must be the last one in this config file (or the last one before a
+# new usb entry).
+#If you are using libusb, a device looks like this:
+#device libusb:001:002
+#If you are using the scanner module (kernel driver), a device looks like this:
+#device /dev/usbscanner
+
+# ----------------------------------------------------------------------------
+
+# Since the Trust Easy Webscan 19200 uses a different product id, we add
+# another usb section here.
+usb 0x05d8 0x4006
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb
+option vendorString "Trust"
+option modelString "Easy Webscan 19200"
+
+# ----------------------------------------------------------------------------
+
+# Since the Memorex Mem48U uses a different product id, we add
+# another usb section here.
+usb 0x05d8 0x4005
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb
+option vendorString "Memorex"
+option modelString "MEM 48U"
+
+# ----------------------------------------------------------------------------
+
+# Since the Trust 240H Easy Webscan Gold uses a different product id, we add
+# another usb section here.
+usb 0x05d8 0x4007
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb
+option ePlusPro 1
+option vendorString "Trust"
+option modelString "240H Easy Webscan Gold"
+
+# ----------------------------------------------------------------------------
+
+# Since the UMAX AstraSlim SE uses a different product id, we add
+# another usb section here.
+usb 0x05d8 0x4009
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb
+option vendorString "UMAX"
+option modelString "AstraSlim SE"
+
+# ----------------------------------------------------------------------------
+
+# This section is for the Artec E+ Pro
+# Note, that the name of the firmware file is called 1200.usb for
+# this device
+usb 0x05d8 0x4004
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/1200.usb
+option ePlusPro 1
+option vendorString "Artec"
+option modelString "E+ Pro"
+
+# ----------------------------------------------------------------------------
+
+# This section is for the UMAX AstraSlim 1200 SE
+# Note, that the name of the firmware file is called 1200.usb for
+# this device
+usb 0x05d8 0x4010
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/1200.usb
+option vendorString "UMAX"
+option modelString "AstraSlim 1200 SE"
+
+# ----------------------------------------------------------------------------
+
+# Since the Yakumo Scan50 uses a different product id, we add
+# another usb section here.
+usb 0x05d8 0x4011
+option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb
+option vendorString "Yakumo"
+option modelString "Scan50"
+
diff --git a/backend/artec_eplus48u.h b/backend/artec_eplus48u.h
new file mode 100644
index 0000000..cc30214
--- /dev/null
+++ b/backend/artec_eplus48u.h
@@ -0,0 +1,596 @@
+#ifndef ARTEC48U_H
+#define ARTEC48U_H
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include <sys/types.h>
+#ifdef HAVE_SYS_IPC_H
+#include <sys/ipc.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_thread.h"
+
+#define _MAX_ID_LEN 20
+
+/*Uncomment next line for button support. This
+ actually isn't supported by the frontends. */
+/*#define ARTEC48U_USE_BUTTONS 1*/
+
+#define ARTEC48U_PACKET_SIZE 64
+#define DECLARE_FUNCTION_NAME(name) \
+ IF_DBG ( static const char function_name[] = name; )
+
+typedef SANE_Byte Artec48U_Packet[ARTEC48U_PACKET_SIZE];
+#define XDBG(args) do { IF_DBG ( DBG args ); } while (0)
+
+/* calculate the minimum/maximum values */
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+/* return the lower/upper 8 bits of a 16 bit word */
+#define HIBYTE(w) ((SANE_Byte)(((SANE_Word)(w) >> 8) & 0xFF))
+#define LOBYTE(w) ((SANE_Byte)(w))
+
+
+#define CHECK_DEV_NOT_NULL(dev, func_name) \
+ do { \
+ if (!(dev)) \
+ { \
+ XDBG ((3, "%s: BUG: NULL device\n", (func_name))); \
+ return SANE_STATUS_INVAL; \
+ } \
+ } while (SANE_FALSE)
+
+/** Check that the device is open.
+ *
+ * @param dev Pointer to the device object (Artec48U_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_OPEN(dev, func_name) \
+ do { \
+ CHECK_DEV_NOT_NULL ((dev), (func_name)); \
+ if ((dev)->fd == -1) \
+ { \
+ XDBG ((3, "%s: BUG: device %p not open\n", (func_name), (void*)(dev)));\
+ return SANE_STATUS_INVAL; \
+ } \
+ } while (SANE_FALSE)
+
+#define CHECK_DEV_ACTIVE(dev,func_name) \
+ do { \
+ CHECK_DEV_OPEN ((dev), (func_name)); \
+ if (!(dev)->active) \
+ { \
+ XDBG ((3, "%s: BUG: device %p not active\n", \
+ (func_name), (void*)(dev))); \
+ return SANE_STATUS_INVAL; \
+ } \
+ } while (SANE_FALSE)
+
+typedef struct Artec48U_Device Artec48U_Device;
+typedef struct Artec48U_Scan_Request Artec48U_Scan_Request;
+typedef struct Artec48U_Scanner Artec48U_Scanner;
+typedef struct Artec48U_Scan_Parameters Artec48U_Scan_Parameters;
+typedef struct Artec48U_AFE_Parameters Artec48U_AFE_Parameters;
+typedef struct Artec48U_Exposure_Parameters Artec48U_Exposure_Parameters;
+typedef struct Artec48U_Line_Reader Artec48U_Line_Reader;
+typedef struct Artec48U_Delay_Buffer Artec48U_Delay_Buffer;
+
+enum artec_options
+{
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_SCAN_MODE,
+ OPT_BIT_DEPTH,
+ OPT_BLACK_LEVEL,
+ OPT_RESOLUTION,
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_GAMMA,
+ OPT_GAMMA_R,
+ OPT_GAMMA_G,
+ OPT_GAMMA_B,
+ OPT_DEFAULT_ENHANCEMENTS,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_CALIBRATION_GROUP,
+ OPT_CALIBRATE,
+ OPT_CALIBRATE_SHADING,
+#ifdef ARTEC48U_USE_BUTTONS
+ OPT_BUTTON_STATE,
+#endif
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+/** Artec48U analog front-end (AFE) parameters.
+ */
+struct Artec48U_AFE_Parameters
+{
+ SANE_Byte r_offset; /**< Red channel offset */
+ SANE_Byte r_pga; /**< Red channel PGA gain */
+ SANE_Byte g_offset; /**< Green channel offset (also used for mono) */
+ SANE_Byte g_pga; /**< Green channel PGA gain (also used for mono) */
+ SANE_Byte b_offset; /**< Blue channel offset */
+ SANE_Byte b_pga; /**< Blue channel PGA gain */
+};
+
+/** TV9693 exposure time parameters.
+ */
+struct Artec48U_Exposure_Parameters
+{
+ SANE_Int r_time; /**< Red exposure time */
+ SANE_Int g_time; /**< Red exposure time */
+ SANE_Int b_time; /**< Red exposure time */
+};
+
+struct Artec48U_Device
+{
+ Artec48U_Device *next;
+ /** Device file descriptor. */
+ int fd;
+ /** Device activation flag. */
+ SANE_Bool active;
+ SANE_String_Const name;
+ SANE_Device sane; /** Scanner model data. */
+ SANE_String_Const firmware_path;
+ double gamma_master;
+ double gamma_r;
+ double gamma_g;
+ double gamma_b;
+ Artec48U_Exposure_Parameters exp_params;
+ Artec48U_AFE_Parameters afe_params;
+ Artec48U_AFE_Parameters artec_48u_afe_params;
+ Artec48U_Exposure_Parameters artec_48u_exposure_params;
+
+ SANE_Int optical_xdpi;
+ SANE_Int optical_ydpi;
+ SANE_Int base_ydpi;
+ SANE_Int xdpi_offset; /* in optical_xdpi units */
+ SANE_Int ydpi_offset; /* in optical_ydpi units */
+ SANE_Int x_size; /* in optical_xdpi units */
+ SANE_Int y_size; /* in optical_ydpi units */
+/* the number of lines, that we move forward before we start reading the
+ shading lines */
+ int shading_offset;
+/* the number of lines we read for the black shading buffer */
+ int shading_lines_b;
+/* the number of lines we read for the white shading buffer */
+ int shading_lines_w;
+
+ SANE_Fixed x_offset, y_offset;
+ SANE_Bool read_active;
+ SANE_Byte *read_buffer;
+ size_t requested_buffer_size;
+ size_t read_pos;
+ size_t read_bytes_in_buffer;
+ size_t read_bytes_left;
+ unsigned int is_epro;
+ unsigned int epro_mult;
+};
+
+/** Scan parameters for artec48u_device_setup_scan().
+ *
+ * These parameters describe a low-level scan request; many such requests are
+ * executed during calibration, and they need to have parameters separate from
+ * the main request (Artec48U_Scan_Request). E.g., on the BearPaw 2400 TA the
+ * scan to find the home position is always done at 300dpi 8-bit mono with
+ * fixed width and height, regardless of the high-level scan parameters.
+ */
+struct Artec48U_Scan_Parameters
+{
+ SANE_Int xdpi; /**< Horizontal resolution */
+ SANE_Int ydpi; /**< Vertical resolution */
+ SANE_Int depth; /**< Number of bits per channel */
+ SANE_Bool color; /**< Color mode flag */
+
+ SANE_Int pixel_xs; /**< Logical width in pixels */
+ SANE_Int pixel_ys; /**< Logical height in pixels */
+ SANE_Int scan_xs; /**< Physical width in pixels */
+ SANE_Int scan_ys; /**< Physical height in pixels */
+ SANE_Int scan_bpl; /**< Number of bytes per scan line */
+ SANE_Bool lineart; /**<Lineart is not really supported by device*/
+};
+
+
+/** Parameters for the high-level scan request.
+ *
+ * These parameters describe the scan request sent by the SANE frontend.
+ */
+struct Artec48U_Scan_Request
+{
+ SANE_Fixed x0; /**< Left boundary */
+ SANE_Fixed y0; /**< Top boundary */
+ SANE_Fixed xs; /**< Width */
+ SANE_Fixed ys; /**< Height */
+ SANE_Int xdpi; /**< Horizontal resolution */
+ SANE_Int ydpi; /**< Vertical resolution */
+ SANE_Int depth; /**< Number of bits per channel */
+ SANE_Bool color; /**< Color mode flag */
+};
+/** Scan action code (purpose of the scan).
+ *
+ * The scan action code affects various scanning mode fields in the setup
+ * command.
+ */
+typedef enum Artec48U_Scan_Action
+{
+ SA_CALIBRATE_SCAN_WHITE, /**< Scan white shading buffer */
+ SA_CALIBRATE_SCAN_BLACK, /**< Scan black shading buffer */
+ SA_CALIBRATE_SCAN_OFFSET_1, /**< First scan to determin offset */
+ SA_CALIBRATE_SCAN_OFFSET_2, /**< Second scan to determine offset */
+ SA_CALIBRATE_SCAN_EXPOSURE_1, /**< First scan to determin offset */
+ SA_CALIBRATE_SCAN_EXPOSURE_2, /**< Second scan to determine offset */
+ SA_SCAN /**< Normal scan */
+}
+Artec48U_Scan_Action;
+
+
+struct Artec48U_Delay_Buffer
+{
+ SANE_Int line_count;
+ SANE_Int read_index;
+ SANE_Int write_index;
+ unsigned int **lines;
+ SANE_Byte *mem_block;
+};
+
+struct Artec48U_Line_Reader
+{
+ Artec48U_Device *dev; /**< Low-level interface object */
+ Artec48U_Scan_Parameters params; /**< Scan parameters */
+
+ /** Number of pixels in the returned scanlines */
+ SANE_Int pixels_per_line;
+
+ SANE_Byte *pixel_buffer;
+
+ Artec48U_Delay_Buffer r_delay;
+ Artec48U_Delay_Buffer g_delay;
+ Artec48U_Delay_Buffer b_delay;
+ SANE_Bool delays_initialized;
+
+ SANE_Status (*read) (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return);
+};
+
+#ifndef SANE_OPTION
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+}
+Option_Value;
+#endif
+
+struct Artec48U_Scanner
+{
+ Artec48U_Scanner *next;
+ Artec48U_Scan_Parameters params;
+ Artec48U_Scan_Request request;
+ Artec48U_Device *dev;
+ Artec48U_Line_Reader *reader;
+ FILE *pipe_handle;
+ SANE_Pid reader_pid;
+ int pipe;
+ int reader_pipe;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Status exit_code;
+ SANE_Parameters sane_params;
+ SANE_Bool scanning;
+ SANE_Bool eof;
+ SANE_Bool calibrated;
+ SANE_Word gamma_array[4][65536];
+ SANE_Word contrast_array[65536];
+ SANE_Word brightness_array[65536];
+ SANE_Byte *line_buffer;
+ SANE_Byte *lineart_buffer;
+ SANE_Word lines_to_read;
+ unsigned int temp_shading_buffer[3][10240]; /*epro*/
+ unsigned int *buffer_pointers[3];
+ unsigned char *shading_buffer_w;
+ unsigned char *shading_buffer_b;
+ unsigned int *shading_buffer_white[3];
+ unsigned int *shading_buffer_black[3];
+ unsigned long byte_cnt;
+};
+
+
+/** Create a new Artec48U_Device object.
+ *
+ * The newly created device object is in the closed state.
+ *
+ * @param dev_return Returned pointer to the created device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the device object was created.
+ * - #SANE_STATUS_NO_MEM - not enough system resources to create the object.
+ */
+static SANE_Status artec48u_device_new (Artec48U_Device ** dev_return);
+
+/** Destroy the device object and release all associated resources.
+ *
+ * If the device was active, it will be deactivated; if the device was open, it
+ * will be closed.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ */
+static SANE_Status artec48u_device_free (Artec48U_Device * dev);
+
+/** Open the scanner device.
+ *
+ * This function opens the device special file @a dev_name and tries to detect
+ * the device model by its USB ID.
+ *
+ * If the device is detected successfully (its USB ID is found in the supported
+ * device list), this function sets the appropriate model parameters.
+ *
+ * If the USB ID is not recognized, the device remains unconfigured; an attempt
+ * to activate it will fail unless artec48u_device_set_model() is used to force
+ * the parameter set. Note that the open is considered to be successful in
+ * this case.
+ *
+ * @param dev Device object.
+ * @param dev_name Scanner device name.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the device was opened successfully (it still may be
+ * unconfigured).
+ */
+static SANE_Status artec48u_device_open (Artec48U_Device * dev);
+
+/** Close the scanner device.
+ *
+ * @param dev Device object.
+ */
+static SANE_Status artec48u_device_close (Artec48U_Device * dev);
+
+/** Activate the device.
+ *
+ * The device must be activated before performing any I/O operations with it.
+ * All device model parameters must be configured before activation; it is
+ * impossible to change them after the device is active.
+ *
+ * This function might need to acquire resources (it calls
+ * Artec48U_Command_Set::activate). These resources will be released when
+ * artec48u_device_deactivate() is called.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - device activated successfully.
+ * - #SANE_STATUS_INVAL - invalid request (attempt to activate a closed or
+ * unconfigured device).
+ */
+static SANE_Status artec48u_device_activate (Artec48U_Device * dev);
+
+/** Deactivate the device.
+ *
+ * This function reverses the action of artec48u_device_activate().
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - device deactivated successfully.
+ * - #SANE_STATUS_INVAL - invalid request (the device was not activated).
+ */
+static SANE_Status artec48u_device_deactivate (Artec48U_Device * dev);
+
+/** Write a data block to the TV9693 memory.
+ *
+ * @param dev Device object.
+ * @param addr Start address in the TV9693 memory.
+ * @param size Size of the data block in bytes.
+ * @param data Data block to write.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ *
+ * @warning
+ * @a size must be a multiple of 64 (at least with TV9693), otherwise the
+ * scanner (and possibly the entire USB bus) will lock up.
+ */
+static SANE_Status
+artec48u_device_memory_write (Artec48U_Device * dev, SANE_Word addr,
+ SANE_Word size, SANE_Byte * data);
+
+/** Read a data block from the TV9693 memory.
+ *
+ * @param dev Device object.
+ * @param addr Start address in the TV9693 memory.
+ * @param size Size of the data block in bytes.
+ * @param data Buffer for the read data.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ *
+ * @warning
+ * @a size must be a multiple of 64 (at least with TV9693), otherwise the
+ * scanner (and possibly the entire USB bus) will lock up.
+ */
+static SANE_Status
+artec48u_device_memory_read (Artec48U_Device * dev, SANE_Word addr,
+ SANE_Word size, SANE_Byte * data);
+
+/** Execute a control command.
+ *
+ * @param dev Device object.
+ * @param cmd Command packet.
+ * @param res Result packet (may point to the same buffer as @a cmd).
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+artec48u_device_req (Artec48U_Device * dev, Artec48U_Packet cmd,
+ Artec48U_Packet res);
+
+/** Execute a "small" control command.
+ *
+ * @param dev Device object.
+ * @param cmd Command packet; only first 8 bytes are used.
+ * @param res Result packet (may point to the same buffer as @a cmd).
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+artec48u_device_small_req (Artec48U_Device * dev, Artec48U_Packet cmd,
+ Artec48U_Packet res);
+
+/** Read raw data from the bulk-in scanner pipe.
+ *
+ * @param dev Device object.
+ * @param buffer Buffer for the read data.
+ * @param size Pointer to the variable which must be set to the requested data
+ * size before call. After completion this variable will hold the number of
+ * bytes actually read.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+artec48u_device_read_raw (Artec48U_Device * dev, SANE_Byte * buffer,
+ size_t * size);
+
+static SANE_Status
+artec48u_device_set_read_buffer_size (Artec48U_Device * dev,
+ size_t buffer_size);
+
+static SANE_Status
+artec48u_device_read_prepare (Artec48U_Device * dev, size_t expected_count);
+
+static SANE_Status
+artec48u_device_read (Artec48U_Device * dev, SANE_Byte * buffer,
+ size_t * size);
+
+static SANE_Status artec48u_device_read_finish (Artec48U_Device * dev);
+
+
+/**
+ * Create a new Artec48U_Line_Reader object.
+ *
+ * @param dev The low-level scanner interface object.
+ * @param params Scan parameters prepared by artec48u_device_setup_scan().
+ * @param reader_return Location for the returned object.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_NO_MEM - cannot allocate memory for object or buffers
+ * - other error values - failure of some internal functions
+ */
+static SANE_Status
+artec48u_line_reader_new (Artec48U_Device * dev,
+ Artec48U_Scan_Parameters * params,
+ Artec48U_Line_Reader ** reader_return);
+
+/**
+ * Destroy the Artec48U_Line_Reader object.
+ *
+ * @param reader The Artec48U_Line_Reader object to destroy.
+ */
+static SANE_Status artec48u_line_reader_free (Artec48U_Line_Reader * reader);
+
+/**
+ * Read a scanline from the Artec48U_Line_Reader object.
+ *
+ * @param reader The Artec48U_Line_Reader object.
+ * @param buffer_pointers_return Array of pointers to image lines (1 or 3
+ * elements)
+ *
+ * This function reads a full scanline from the device, unpacks it to internal
+ * buffers and returns pointer to these buffers in @a
+ * buffer_pointers_return[i]. For monochrome scan, only @a
+ * buffer_pointers_return[0] is filled; for color scan, elements 0, 1, 2 are
+ * filled with pointers to red, green, and blue data. The returned pointers
+ * are valid until the next call to artec48u_line_reader_read(), or until @a
+ * reader is destroyed.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - read completed successfully
+ * - other error value - an error occured
+ */
+static SANE_Status
+artec48u_line_reader_read (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return);
+
+static SANE_Status
+artec48u_download_firmware (Artec48U_Device * dev,
+ SANE_Byte * data, SANE_Word size);
+
+static SANE_Status
+artec48u_is_moving (Artec48U_Device * dev, SANE_Bool * moving);
+
+static SANE_Status artec48u_carriage_home (Artec48U_Device * dev);
+
+static SANE_Status artec48u_stop_scan (Artec48U_Device * dev);
+
+static SANE_Status
+artec48u_setup_scan (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Action action,
+ SANE_Bool calculate_only,
+ Artec48U_Scan_Parameters * params);
+
+static SANE_Status
+artec48u_scanner_new (Artec48U_Device * dev,
+ Artec48U_Scanner ** scanner_return);
+
+static SANE_Status artec48u_scanner_free (Artec48U_Scanner * scanner);
+
+static SANE_Status
+artec48u_scanner_start_scan (Artec48U_Scanner * scanner,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Parameters * params);
+
+static SANE_Status
+artec48u_scanner_read_line (Artec48U_Scanner * scanner,
+ unsigned int **buffer_pointers,
+ SANE_Bool shading);
+
+static SANE_Status artec48u_scanner_stop_scan (Artec48U_Scanner * scanner);
+
+static SANE_Status
+artec48u_calculate_shading_buffer (Artec48U_Scanner * s, int start, int end,
+ int resolution, SANE_Bool color);
+
+static SANE_Status download_firmware_file (Artec48U_Device * chip);
+
+static SANE_Status
+artec48u_generic_set_exposure_time (Artec48U_Device * dev,
+ Artec48U_Exposure_Parameters * params);
+
+static SANE_Status
+artec48u_generic_set_afe (Artec48U_Device * dev,
+ Artec48U_AFE_Parameters * params);
+
+static SANE_Status artec48u_generic_start_scan (Artec48U_Device * dev);
+
+static SANE_Status
+artec48u_generic_read_scanned_data (Artec48U_Device * dev, SANE_Bool * ready);
+
+static SANE_Status init_options (Artec48U_Scanner * s);
+
+static SANE_Status load_calibration_data (Artec48U_Scanner * s);
+
+static SANE_Status save_calibration_data (Artec48U_Scanner * s);
+#endif
diff --git a/backend/as6e.c b/backend/as6e.c
new file mode 100644
index 0000000..0fb9e31
--- /dev/null
+++ b/backend/as6e.c
@@ -0,0 +1,942 @@
+/* sane - Scanner Access Now Easy.
+ Artec AS6E backend.
+ Copyright (C) 2000 Eugene S. Weiss
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a backend for the Artec AS6E by making a bridge
+ to the as6edriver program. The as6edriver program can be found at
+ http://as6edriver.sourceforge.net . */
+
+
+
+
+#include "../include/sane/config.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKENDNAME as6e
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include "as6e.h"
+
+static int num_devices;
+static AS6E_Device *first_dev;
+static AS6E_Scan *first_handle;
+static const SANE_Device **devlist = 0;
+
+static SANE_Status attach (const char *devname, AS6E_Device ** devp);
+/* static SANE_Status attach_one (const char *dev); */
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static const SANE_Word resolution_list[] = {
+ 4, 300, 200, 100, 50
+};
+
+static const SANE_Range x_range = {
+ SANE_FIX (0),
+ SANE_FIX (215.91),
+ SANE_FIX (0)
+};
+
+static const SANE_Range y_range = {
+ SANE_FIX (0),
+ SANE_FIX (297.19),
+ SANE_FIX (0)
+};
+
+
+static const SANE_Range brightness_range = {
+ -100,
+ 100,
+ 1
+};
+
+static const SANE_Range contrast_range = {
+ -100,
+ 100,
+ 1
+};
+
+/*--------------------------------------------------------------------------*/
+static SANE_Int
+as6e_unit_convert (SANE_Fixed value)
+{
+
+ double precise;
+ SANE_Int return_value;
+
+ precise = SANE_UNFIX (value);
+ precise = (precise * 300) / MM_PER_INCH;
+ return_value = precise;
+ return return_value;
+}
+
+/*--------------------------------------------------------------------------*/
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ AS6E_Scan *s = handle;
+ SANE_Word buffer_offset = 0;
+ int written = 0, bytes_read = 0, maxbytes;
+ SANE_Word bytecounter, linebufcounter, ctlbytes;
+ SANE_Byte *linebuffer;
+
+ DBG (3, "reading %d bytes, %d bytes in carryover buffer\n", max_len,
+ s->scan_buffer_count);
+
+ if ((unsigned int) s->image_counter >= s->bytes_to_read)
+ {
+ *len = 0;
+ if (s->scanning)
+ {
+ read (s->as6e_params.ctlinpipe, &written, sizeof (written));
+ if (written != -1)
+ DBG (3, "pipe error\n");
+ DBG (3, "trying to read -1 ...written = %d\n", written);
+ }
+ s->scanning = SANE_FALSE;
+ DBG (1, "image data complete, sending EOF...\n");
+ return SANE_STATUS_EOF;
+ } /*image complete */
+
+ linebuffer = s->line_buffer;
+ if (s->scan_buffer_count > 0)
+ { /*there are leftover bytes from the last call */
+ if (s->scan_buffer_count <= max_len)
+ {
+ for (*len = 0; *len < s->scan_buffer_count; (*len)++)
+ {
+ buf[*len] = s->scan_buffer[*len];
+ buffer_offset++;
+ }
+ s->scan_buffer_count = 0;
+ if (s->scan_buffer_count == max_len)
+ {
+ s->scan_buffer_count = 0;
+ s->image_counter += max_len;
+ DBG (3, "returning %d bytes from the carryover buffer\n", *len);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ for (*len = 0; *len < max_len; (*len)++)
+ buf[*len] = s->scan_buffer[*len];
+
+ for (bytecounter = max_len;
+ bytecounter < s->scan_buffer_count; bytecounter++)
+ s->scan_buffer[bytecounter - max_len]
+ = s->scan_buffer[bytecounter];
+
+ s->scan_buffer_count -= max_len;
+ s->image_counter += max_len;
+ DBG (3, "returning %d bytes from the carryover buffer\n", *len);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ *len = 0; /*no bytes in the buffer */
+ if (!s->scanning)
+ {
+ DBG (1, "scan over returning %d\n", *len);
+ if (s->scan_buffer_count)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_EOF;
+ }
+ }
+ while (*len < max_len)
+ {
+ DBG (3, "trying to read number of bytes...\n");
+ ctlbytes = read (s->as6e_params.ctlinpipe, &written, sizeof (written));
+ DBG (3, "bytes written = %d, ctlbytes =%d\n", written, ctlbytes);
+ fflush (stdout);
+ if ((s->cancelled) && (written == 0))
+ { /*first clear -1 from pipe */
+ DBG (1, "sending SANE_STATUS_CANCELLED\n");
+ read (s->as6e_params.ctlinpipe, &written, sizeof (written));
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+ if (written == -1)
+ {
+ DBG (1, "-1READ Scanner through. returning %d bytes\n", *len);
+ s->image_counter += *len;
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+ }
+ linebufcounter = 0;
+ DBG (3,
+ "linebufctr reset, len =%d written =%d bytes_read =%d, max = %d\n",
+ *len, written, bytes_read, max_len);
+ maxbytes = written;
+ while (linebufcounter < written)
+ {
+ DBG (4, "trying to read data pipe\n");
+ bytes_read =
+ read (s->as6e_params.datapipe, linebuffer + linebufcounter,
+ maxbytes);
+ linebufcounter += bytes_read;
+ maxbytes -= bytes_read;
+ DBG (3, "bytes_read = %d linebufcounter = %d\n", bytes_read,
+ linebufcounter);
+ }
+ DBG (3, "written =%d max_len =%d len =%d\n", written, max_len, *len);
+ if (written <= (max_len - *len))
+ {
+ for (bytecounter = 0; bytecounter < written; bytecounter++)
+ {
+ buf[bytecounter + buffer_offset] = linebuffer[bytecounter];
+ (*len)++;
+ }
+ buffer_offset += written;
+ DBG (3, "buffer offset = %d\n", buffer_offset);
+ }
+ else if (max_len > *len)
+ { /*there's still room to send data */
+ for (bytecounter = 0; bytecounter < (max_len - *len); bytecounter++)
+ buf[bytecounter + buffer_offset] = linebuffer[bytecounter];
+ DBG (3, "topping off buffer\n");
+ for (bytecounter = (max_len - *len); bytecounter < written;
+ bytecounter++)
+ {
+
+ s->scan_buffer[s->scan_buffer_count + bytecounter -
+ (max_len - *len)] = linebuffer[bytecounter];
+ }
+ s->scan_buffer_count += (written - (max_len - *len));
+ *len = max_len;
+ }
+ else
+ { /*everything goes into the carryover buffer */
+ for (bytecounter = 0; bytecounter < written; bytecounter++)
+ s->scan_buffer[s->scan_buffer_count + bytecounter]
+ = linebuffer[bytecounter];
+ s->scan_buffer_count += written;
+ }
+ } /*while there's space in the buffer */
+ s->image_counter += *len;
+ DBG (3, "image ctr = %d bytes_to_read = %lu returning %d\n",
+ s->image_counter, (u_long) s->bytes_to_read, *len);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*--------------------------------------------------------------------------*/
+void
+sane_cancel (SANE_Handle h)
+{
+ AS6E_Scan *s = h;
+ SANE_Word test;
+ DBG (2, "trying to cancel...\n");
+ if (s->scanning)
+ {
+ test = kill (s->child_pid, SIGUSR1);
+ if (test == 0)
+ s->cancelled = SANE_TRUE;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ AS6E_Scan *s = handle;
+ SANE_Status status;
+ int repeat = 1;
+ SANE_Word numbytes;
+ int scan_params[8];
+ /* First make sure we have a current parameter set. Some of the
+ * parameters will be overwritten below, but that's OK. */
+ DBG (2, "sane_start\n");
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ DBG (1, "Got params again...\n");
+ numbytes = write (s->as6e_params.ctloutpipe, &repeat, sizeof (repeat));
+ if (numbytes != sizeof (repeat))
+ return (SANE_STATUS_IO_ERROR);
+ DBG (1, "sending start_scan signal\n");
+ scan_params[0] = s->as6e_params.resolution;
+ if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ scan_params[1] = 0;
+ else if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ scan_params[1] = 1;
+ else if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ scan_params[1] = 2;
+ else
+ return (SANE_STATUS_JAMMED); /*this should never happen */
+ scan_params[2] = s->as6e_params.startpos;
+ scan_params[3] = s->as6e_params.stoppos;
+ scan_params[4] = s->as6e_params.startline;
+ scan_params[5] = s->as6e_params.stopline;
+ scan_params[6] = s->value[OPT_BRIGHTNESS].w;
+ scan_params[7] = s->value[OPT_CONTRAST].w;
+ DBG (1, "scan params = %d %d %d %d %d %d %d %d\n", scan_params[0],
+ scan_params[1], scan_params[2], scan_params[3],
+ scan_params[4], scan_params[5], scan_params[6], scan_params[7]);
+ numbytes =
+ write (s->as6e_params.ctloutpipe, scan_params, sizeof (scan_params));
+ if (numbytes != sizeof (scan_params))
+ return (SANE_STATUS_IO_ERROR);
+ s->scanning = SANE_TRUE;
+ s->scan_buffer_count = 0;
+ s->image_counter = 0;
+ s->cancelled = 0;
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ AS6E_Scan *s = handle;
+ SANE_String mode;
+ SANE_Word divisor = 1;
+ DBG (2, "sane_get_parameters\n");
+ if (!s->scanning)
+ {
+ memset (&s->sane_params, 0, sizeof (s->sane_params));
+ s->as6e_params.resolution = s->value[OPT_RESOLUTION].w;
+ s->as6e_params.startpos = as6e_unit_convert (s->value[OPT_TL_X].w);
+ s->as6e_params.stoppos = as6e_unit_convert (s->value[OPT_BR_X].w);
+ s->as6e_params.startline = as6e_unit_convert (s->value[OPT_TL_Y].w);
+ s->as6e_params.stopline = as6e_unit_convert (s->value[OPT_BR_Y].w);
+ if ((s->as6e_params.resolution == 200)
+ || (s->as6e_params.resolution == 100))
+ divisor = 3;
+ else if (s->as6e_params.resolution == 50)
+ divisor = 6; /*get legal values for 200 dpi */
+ s->as6e_params.startpos = (s->as6e_params.startpos / divisor) * divisor;
+ s->as6e_params.stoppos = (s->as6e_params.stoppos / divisor) * divisor;
+ s->as6e_params.startline =
+ (s->as6e_params.startline / divisor) * divisor;
+ s->as6e_params.stopline = (s->as6e_params.stopline / divisor) * divisor;
+ s->sane_params.pixels_per_line =
+ (s->as6e_params.stoppos -
+ s->as6e_params.startpos) * s->as6e_params.resolution / 300;
+ s->sane_params.lines =
+ (s->as6e_params.stopline -
+ s->as6e_params.startline) * s->as6e_params.resolution / 300;
+ mode = s->value[OPT_MODE].s;
+/* if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) ||
+ (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0))
+ {
+ s->sane_params.format = SANE_FRAME_GRAY;
+ s->sane_params.bytes_per_line = (s->sane_params.pixels_per_line + 7) / 8;
+ s->sane_params.depth = 1;
+ } */
+/*else*/ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ {
+ s->sane_params.format = SANE_FRAME_GRAY;
+ s->sane_params.bytes_per_line = s->sane_params.pixels_per_line;
+ s->sane_params.depth = 8;
+ } /*grey frame */
+ else
+ {
+ s->sane_params.format = SANE_FRAME_RGB;
+ s->sane_params.bytes_per_line = 3 * s->sane_params.pixels_per_line;
+ s->sane_params.depth = 8;
+ } /*color frame */
+ s->bytes_to_read = s->sane_params.lines * s->sane_params.bytes_per_line;
+ s->sane_params.last_frame = SANE_TRUE;
+ } /*!scanning */
+
+ if (params)
+ *params = s->sane_params;
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ AS6E_Scan *s = handle;
+ SANE_Status status = 0;
+ SANE_Word cap;
+ DBG (2, "sane_control_option\n");
+ if (info)
+ *info = 0;
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+ cap = s->options_list[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (1, "sane_control_option %d, get value\n", option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = s->value[option].w;
+ return (SANE_STATUS_GOOD);
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->value[option].s);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (1, "sane_control_option %d, set value\n", option);
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+/* status = sanei_constrain_value (s->options_list[option], val, info);*/
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ if (info && s->value[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ s->value[option].w = *(SANE_Word *) val;
+ DBG (1, "set brightness to\n");
+ return (SANE_STATUS_GOOD);
+ case OPT_MODE:
+ if (s->value[option].s)
+ free (s->value[option].s);
+ s->value[option].s = strdup (val);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ return (SANE_STATUS_INVAL);
+}
+
+/*--------------------------------------------------------------------------*/
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ AS6E_Scan *s = handle;
+ DBG (2, "sane_get_option_descriptor\n");
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+ return (&s->options_list[option]);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void
+sane_close (SANE_Handle handle)
+{
+ AS6E_Scan *prev, *s;
+ SANE_Word repeat = 0;
+ DBG (2, "sane_close\n");
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ sane_cancel (handle);
+ write (s->as6e_params.ctloutpipe, &repeat, sizeof (repeat));
+ close (s->as6e_params.ctloutpipe);
+ free (s->scan_buffer);
+ free (s->line_buffer);
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s;
+ free (handle);
+}
+
+/*--------------------------------------------------------------------------*/
+void
+sane_exit (void)
+{
+ AS6E_Device *next;
+ DBG (2, "sane_exit\n");
+ while (first_dev != NULL)
+ {
+ next = first_dev->next;
+ free (first_dev);
+ first_dev = next;
+ }
+ if (devlist)
+ free (devlist);
+}
+
+/*--------------------------------------------------------------------------*/
+static SANE_Status
+as6e_open (AS6E_Scan * s)
+{
+
+ int data_processed, exec_result, as6e_status;
+ int ctloutpipe[2], ctlinpipe[2], datapipe[2];
+ char inpipe_desc[32], outpipe_desc[32], datapipe_desc[32];
+ pid_t fork_result;
+ DBG (1, "as6e_open\n");
+ memset (inpipe_desc, '\0', sizeof (inpipe_desc));
+ memset (outpipe_desc, '\0', sizeof (outpipe_desc));
+ memset (datapipe_desc, '\0', sizeof (datapipe_desc));
+ if ((pipe (ctloutpipe) == 0) && (pipe (ctlinpipe) == 0)
+ && (pipe (datapipe) == 0))
+ {
+ fork_result = fork ();
+ if (fork_result == (pid_t) - 1)
+ {
+ DBG (1, "Fork failure");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (fork_result == 0)
+ { /*in child */
+ sprintf (inpipe_desc, "%d", ctlinpipe[WRITEPIPE]);
+ sprintf (outpipe_desc, "%d", ctloutpipe[READPIPE]);
+ sprintf (datapipe_desc, "%d", datapipe[WRITEPIPE]);
+ exec_result =
+ execlp ("as6edriver", "as6edriver", "-s", inpipe_desc,
+ outpipe_desc, datapipe_desc, (char *) 0);
+ DBG (1, "The SANE backend was unable to start \"as6edriver\".\n");
+ DBG (1, "This must be installed in a driectory in your PATH.\n");
+ DBG (1, "To aquire the as6edriver program,\n");
+ DBG (1, "go to http://as6edriver.sourceforge.net.\n");
+ write (ctlinpipe[WRITEPIPE], &exec_result, sizeof (exec_result));
+ exit (-1);
+ }
+ else
+ { /*parent process */
+ data_processed =
+ read (ctlinpipe[READPIPE], &as6e_status, sizeof (as6e_status));
+ DBG (1, "%d - read %d status = %d\n", getpid (), data_processed,
+ as6e_status);
+ if (as6e_status == -2)
+ {
+ DBG (1, "Port access denied.\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+ if (as6e_status == -1)
+ {
+ DBG (1, "Could not contact scanner.\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (as6e_status == 1)
+ DBG (1, "Using nibble mode.\n");
+ if (as6e_status == 2)
+ DBG (1, "Using byte mode.\n");
+ if (as6e_status == 3)
+ DBG (1, "Using EPP mode.\n");
+ s->as6e_params.ctlinpipe = ctlinpipe[READPIPE];
+ s->as6e_params.ctloutpipe = ctloutpipe[WRITEPIPE];
+ s->as6e_params.datapipe = datapipe[READPIPE];
+ s->child_pid = fork_result;
+ return (SANE_STATUS_GOOD);
+ } /*else */
+ }
+ else
+ return (SANE_STATUS_IO_ERROR);
+}
+
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp = NULL;
+
+ DBG_INIT ();
+ DBG (2, "sane_init (authorize %s null)\n", (authorize) ? "!=" : "==");
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+/* fp = sanei_config_open (AS6E_CONFIG_FILE);*/
+ if (!fp)
+ {
+ return (attach ("as6edriver", 0));
+ }
+
+ while (fgets (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+ if (dev_name[len - 1] == '\n')
+ dev_name[--len] = '\0';
+ if (!len)
+ continue; /* ignore empty lines */
+/* sanei_config_attach_matching_devices (dev_name, attach_one);*/
+ }
+ fclose (fp);
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+/*
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (2, "attach_one\n");
+ attach (dev, 0);
+ return (SANE_STATUS_GOOD);
+}
+ */
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ AS6E_Device *dev;
+ int i;
+ DBG (3, "sane_get_devices (local_only = %d)\n", local_only);
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+ *device_list = devlist;
+ return (SANE_STATUS_GOOD);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return (max_size);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void
+initialize_options_list (AS6E_Scan * s)
+{
+
+ SANE_Int option;
+ DBG (2, "initialize_options_list\n");
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->options_list[option].size = sizeof (SANE_Word);
+ s->options_list[option].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->options_list[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->options_list[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_NUM_OPTS].size = sizeof (SANE_Word);
+ s->options_list[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->options_list[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->value[OPT_NUM_OPTS].w = NUM_OPTIONS;
+ s->options_list[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->options_list[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->options_list[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->options_list[OPT_MODE].type = SANE_TYPE_STRING;
+ s->options_list[OPT_MODE].size = max_string_size (mode_list);
+ s->options_list[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->options_list[OPT_MODE].constraint.string_list = mode_list;
+ s->value[OPT_MODE].s = strdup (mode_list[2]);
+ s->options_list[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->options_list[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->options_list[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->options_list[OPT_RESOLUTION].constraint.word_list = resolution_list;
+ s->value[OPT_RESOLUTION].w = 200;
+ s->options_list[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->options_list[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->options_list[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->options_list[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->options_list[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_TL_X].constraint.range = &x_range;
+ s->value[OPT_TL_X].w = s->options_list[OPT_TL_X].constraint.range->min;
+ s->options_list[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->options_list[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_TL_Y].constraint.range = &y_range;
+ s->value[OPT_TL_Y].w = s->options_list[OPT_TL_Y].constraint.range->min;
+ s->options_list[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->options_list[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->options_list[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->options_list[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->options_list[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BR_X].constraint.range = &x_range;
+ s->value[OPT_BR_X].w = s->options_list[OPT_BR_X].constraint.range->max;
+ s->options_list[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->options_list[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BR_Y].constraint.range = &y_range;
+ s->value[OPT_BR_Y].w = s->options_list[OPT_BR_Y].constraint.range->max;
+ s->options_list[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->options_list[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->options_list[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->options_list[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->options_list[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_CONTRAST].constraint.range = &brightness_range;
+ s->value[OPT_BRIGHTNESS].w = 10;
+ s->options_list[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->options_list[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BRIGHTNESS].constraint.range = &contrast_range;
+ s->value[OPT_CONTRAST].w = -32;
+}
+
+/*--------------------------------------------------------------------------*/
+static int
+check_for_driver (const char *devname)
+{
+#define NAMESIZE 128
+ struct stat statbuf;
+ mode_t modes;
+ char *path;
+ char fullname[NAMESIZE];
+ char dir[NAMESIZE];
+ int count = 0, offset = 0;
+
+ path = getenv ("PATH");
+ if (!path)
+ return 0;
+ while (path[count] != '\0')
+ {
+ memset (fullname, '\0', sizeof (fullname));
+ memset (dir, '\0', sizeof (dir));
+ while ((path[count] != ':') && (path[count] != '\0'))
+ {
+ dir[count - offset] = path[count];
+ count++;
+ }
+ /* use sizeof(fullname)-1 to make sure there is at least one padded null byte */
+ strncpy (fullname, dir, sizeof(fullname)-1);
+ /* take into account that fullname already contains non-null bytes */
+ strncat (fullname, "/", sizeof(fullname)-strlen(fullname)-1);
+ strncat (fullname, devname, sizeof(fullname)-strlen(fullname)-1);
+ if (!stat (fullname, &statbuf))
+ {
+ modes = statbuf.st_mode;
+ if (S_ISREG (modes))
+ return (1); /* found as6edriver */
+ }
+ if (path[count] == '\0')
+ return (0); /* end of path --no driver found */
+ count++;
+ offset = count;
+ }
+ return (0);
+}
+
+
+/*--------------------------------------------------------------------------*/
+static SANE_Status
+attach (const char *devname, AS6E_Device ** devp)
+{
+
+ AS6E_Device *dev;
+
+/* SANE_Status status; */
+ DBG (2, "attach\n");
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+ dev->sane.name = strdup (devname);
+ if (!check_for_driver (devname))
+ {
+ free (dev);
+ return (SANE_STATUS_INVAL);
+ }
+
+ dev->sane.model = "AS6E";
+ dev->sane.vendor = "Artec";
+ dev->sane.type = "flatbed scanner";
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+}
+
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status;
+ AS6E_Device *dev;
+ AS6E_Scan *s;
+ DBG (2, "sane_open\n");
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = first_dev;
+ }
+ if (!dev)
+ return SANE_STATUS_INVAL;
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->scan_buffer = malloc (SCAN_BUF_SIZE);
+ if (!s->scan_buffer)
+ return SANE_STATUS_NO_MEM;
+ memset (s->scan_buffer, 0, SCAN_BUF_SIZE);
+ s->line_buffer = malloc (SCAN_BUF_SIZE);
+ if (!s->line_buffer)
+ return SANE_STATUS_NO_MEM;
+ memset (s->line_buffer, 0, SCAN_BUF_SIZE);
+ status = as6e_open (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ initialize_options_list (s);
+ s->scanning = 0;
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+ return (status);
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (2, "sane_set_io_mode( %p, %d )\n", handle, non_blocking);
+ return (SANE_STATUS_UNSUPPORTED);
+}
+
+/*---------------------------------------------------------------------------*/
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd( %p, %p )\n",(void *) handle, (void *) fd);
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/as6e.h b/backend/as6e.h
new file mode 100644
index 0000000..7132d6b
--- /dev/null
+++ b/backend/as6e.h
@@ -0,0 +1,124 @@
+/* sane - Scanner Access Now Easy.
+ Artec AS6E backend.
+ Copyright (C) 2000 Eugene S. Weiss
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a backend for the Artec AS6E by making a bridge
+ to the as6edriver program. The as6edriver program can be found at
+ http://as6edriver.sourceforge.net . */
+
+#ifndef as6e_h
+#define as6e_h
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+
+
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+ OPT_MODE,
+ OPT_RESOLUTION,
+
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ /* must come last */
+ NUM_OPTIONS
+ } AS6E_Option;
+
+typedef struct
+{
+ int color;
+ int resolution;
+ int startpos;
+ int stoppos;
+ int startline;
+ int stopline;
+ int ctloutpipe;
+ int ctlinpipe;
+ int datapipe;
+} AS6E_Params;
+
+
+typedef struct AS6E_Device
+{
+ struct AS6E_Device *next;
+ SANE_Device sane;
+} AS6E_Device;
+
+
+
+typedef struct AS6E_Scan
+{
+ struct AS6E_Scan *next;
+ SANE_Option_Descriptor options_list[NUM_OPTIONS];
+ Option_Value value[NUM_OPTIONS];
+ SANE_Bool scanning;
+ SANE_Bool cancelled;
+ SANE_Parameters sane_params;
+ AS6E_Params as6e_params;
+ pid_t child_pid;
+ size_t bytes_to_read;
+ SANE_Byte *scan_buffer;
+ SANE_Byte *line_buffer;
+ SANE_Word scan_buffer_count;
+ SANE_Word image_counter;
+} AS6E_Scan;
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define AS6E_CONFIG_FILE "as6e.conf"
+
+#define READPIPE 0
+#define WRITEPIPE 1
+
+
+#define SCAN_BUF_SIZE 32768
+
+#endif /* as6e_h */
diff --git a/backend/avision.c b/backend/avision.c
new file mode 100644
index 0000000..31b0a58
--- /dev/null
+++ b/backend/avision.c
@@ -0,0 +1,8626 @@
+/*******************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ avision.c
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ *****************************************************************************
+
+ This backend is based upon the Tamarack backend and adapted to the Avision
+ scanners by René Rebe and Meino Cramer.
+
+ This file implements a SANE backend for the Avision SCSI Scanners (like the
+ AV 630 / 620 (CS) ...) and some Avision (OEM) USB scanners (like the HP 53xx,
+ 74xx, Minolta FS-V1 ...) or Fujitsu ScanPartner with the AVISION SCSI-2/3
+ or USB command set.
+
+ Copyright 1999, 2000, 2001 by
+ "René Rebe" <rene@exactcode.de>
+ "Meino Christian Cramer" <mccramer@s.netic.de>
+
+ Copyright 2002 by
+ "René Rebe" <rene@exactcode.de>
+ "Jose Paulo Moitinho de Almeida" <moitinho@civil.ist.utl.pt>
+
+ Copyright 2003, 2004, 2005, 2006, 2007 by
+ "René Rebe" <rene@exactcode.de>
+
+ Copyright 2010, 2011 by
+ "Mike Kelly" <mike@piratehaven.org>
+
+ Additional Contributers:
+ "Gunter Wagner"
+ (some fixes and the transparency option)
+ "Martin Jelínek" <mates@sirrah.troja.mff.cuni.cz>
+ nice attach debug output
+ "Marcin Siennicki" <m.siennicki@cloos.pl>
+ found some typos and contributed fixes for the HP 7400
+ "Frank Zago" <fzago@greshamstorage.com>
+ Mitsubishi IDs and report
+ Avision INC
+ example code to handle calibration and C5 ASIC specifics
+ "Franz Bakan" <fbakan@gmx.net>
+ OS/2 threading support
+ "Falk Rohsiepe"
+ Spelling and whitespace as well as HP5370 quirks
+
+ Many additional special thanks to:
+ Avision INC for providing protocol documentation.
+ Avision INC for sponsoring an AV 8000S with ADF.
+ Avision Europe and BHS Binkert for sponsoring several more scanners.
+ Archivista GmbH, Switzerland, for sponsoring several features
+ Roberto Di Cosmo who sponsored a HP 5370 scanner.
+ Oliver Neukum who sponsored a HP 5300 USB scanner.
+ Matthias Wiedemann for lending his HP 7450C for some weeks.
+ Compusoft, C.A. Caracas / Venezuela for sponsoring a
+ HP 7450 scanner and so enhanced ADF support.
+ Chris Komatsu for the nice ADF scanning observation.
+
+ All the many other beta-tester and debug-log sender!
+
+ Thanks to all the people and companies above. Without you
+ the Avision backend would not be in the shape it is today! ;-)
+
+********************************************************************************/
+
+/* SANE-FLOW-DIAGRAMM (from umax.c)
+ *
+ * - sane_init() : initialize backend, attach scanners(devicename,0)
+ * . - sane_get_devices() : query list of scanner-devices
+ * . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev)
+ * . . - sane_set_io_mode : set blocking-mode
+ * . . - sane_get_select_fd : get scanner-fd
+ * . . - sane_get_option_descriptor() : get option information
+ * . . - sane_control_option() : change option values
+ * . .
+ * . . - sane_start() : start image acquisition
+ * . . - sane_get_parameters() : returns actual scan-parameters
+ * . . - sane_read() : read image-data (from pipe)
+ *
+ * in ADF mode this is done often:
+ * . . - sane_start() : start image acquisition
+ * . . - sane_get_parameters() : returns actual scan-parameters
+ * . . - sane_read() : read image-data (from pipe)
+ *
+ * . . - sane_cancel() : cancel operation, kill reader_process
+ *
+ * . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
+ * - sane_exit() : terminate use of backend, free devicename and device-structure
+ */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <math.h>
+
+#define BACKEND_NAME avision
+#define BACKEND_BUILD 296 /* avision backend BUILD version */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_thread.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+#include <avision.h>
+
+/* For timeval... */
+#ifdef DEBUG
+#include <sys/time.h>
+#endif
+
+/* Attention: The comments must stay as they are - they are automatically parsed
+ to generate the SANE avision.desc file, as well as HTML online content! */
+
+/* Attention2: This device table is part of the source code and as such
+ licensed under the terms of the license as listed above (GPL2+). By
+ using this data you obviously create derived work! -ReneR */
+
+static Avision_HWEntry Avision_Device_List [] =
+ {
+ { "AVISION", "AV100CS",
+ 0, 0,
+ "Avision", "AV100CS",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV100IIICS",
+ 0, 0,
+ "Avision", "AV100IIICS",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV100S",
+ 0, 0,
+ "Avision", "AV100S",
+ 0,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A27,
+ "Avision", "AV120",
+ AV_INT_STATUS,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A3C,
+ "Avision", "AV121",
+ AV_INT_BUTTON | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA,0},
+ /* comment="sheetfed scanner" */
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A33,
+ "Avision", "AV122",
+ AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET,0},
+ /* comment="sheetfed duplex scanner" */
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A93,
+ "Avision", "AV122 C2",
+ AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_NOT_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET,0},
+ /* comment="sheetfed duplex scanner" */
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A24,
+ "Avision", "AV210",
+ AV_INT_BUTTON | AV_ACCEL_TABLE,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A25,
+ "Avision", "AV210",
+ AV_INT_BUTTON | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A3A,
+ "Avision", "AV210C2",
+ AV_INT_BUTTON | AV_GRAY_MODES,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A2F,
+ "Avision", "AV210C2-G",
+ AV_INT_BUTTON | AV_GRAY_MODES,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x1A35,
+ "Avision", "AV210D2+",
+ AV_INT_BUTTON, AV_USE_GRAY_FILTER},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A23,
+ "Avision", "AV220",
+ AV_INT_BUTTON | AV_GRAY_MODES,0},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A2A,
+ "Avision", "AV220C2",
+ AV_INT_BUTTON | AV_CANCEL_BUTTON,0},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A2B,
+ "Avision", "AV220D2",
+ AV_INT_BUTTON | AV_CANCEL_BUTTON,0},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A2C,
+ "Avision", "AV220+",
+ AV_INT_BUTTON | AV_CANCEL_BUTTON,0},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A2D,
+ "Avision", "AV220C2-G",
+ AV_INT_BUTTON | AV_CANCEL_BUTTON,0},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A2E,
+ "Avision", "AV220C2-B",
+ AV_INT_BUTTON | AV_CANCEL_BUTTON,0},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A94,
+ "Avision", "AV220-G",
+ AV_INT_BUTTON | AV_2ND_LINE_INTERLACED, AV_FIRMWARE},
+ /* comment="duplex! sheetfed scanner" */
+ /* status="complete" */
+
+ { "AVISION", "AV240SC",
+ 0, 0,
+ "Avision", "AV240SC",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV260CS",
+ 0, 0,
+ "Avision", "AV260CS",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV360CS",
+ 0, 0,
+ "Avision", "AV360CS",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV363CS",
+ 0, 0,
+ "Avision", "AV363CS",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV420CS",
+ 0, 0,
+ "Avision", "AV420CS",
+ 0,0},
+ /* status="untested" */
+
+ { "AVISION", "AV6120",
+ 0, 0,
+ "Avision", "AV6120",
+ 0,0},
+ /* status="untested" */
+
+ { NULL, "AV610",
+ 0x0638, 0x0a18,
+ "Avision", "AV610",
+ AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | AV_INT_BUTTON, 0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a18,
+ "Avision", "AV600U Plus",
+ /* If this unit requires the AV_INT_STATUS flag, then we'll need to alter the code to deal with two different devices with the same USB id (AV610 above) */
+ AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | /* AV_INT_STATUS | */ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a5e,
+ "Avision", "AV610C2",
+ AV_NO_BACKGROUND | AV_INT_BUTTON,0}, /* cancel button -> sense abort! */
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a41,
+ "Avision", "AM3000 Series",
+ 0,0},
+ /* comment="MFD" */
+ /* status="basic" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a16,
+ "Avision", "DS610CU Scancopier",
+ AV_INT_STATUS,0},
+ /* comment="1 pass, 600 dpi, A4" */
+ /* status="good" */
+
+ { "AVISION", "AV620CS",
+ 0, 0,
+ "Avision", "AV620CS",
+ 0,0},
+ /* comment="1 pass, 600 dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV620CS Plus",
+ 0, 0,
+ "Avision", "AV620CS Plus",
+ 0,0},
+ /* comment="1 pass, 1200 dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV630CS",
+ 0, 0,
+ "Avision", "AV630CS",
+ 0,0},
+ /* comment="1 pass, 1200 dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV630CSL",
+ 0, 0,
+ "Avision", "AV630CSL",
+ 0,0},
+ /* comment="1 pass, 1200 dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV6240",
+ 0, 0,
+ "Avision", "AV6240",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A13,
+ "Avision", "AV600U",
+ AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_SOFT_SCALE | AV_INT_STATUS | AV_NO_BUTTON,0},
+ /* comment="1 pass, 600 dpi" */
+ /* status="good" */
+
+ { "AVISION", "AV660S",
+ 0, 0,
+ "Avision", "AV660S",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV680S",
+ 0, 0,
+ "Avision", "AV680S",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV690U",
+ 0, 0,
+ "Avision", "AV690U",
+ 0,0},
+ /* comment="1 pass, 2400 dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV800S",
+ 0, 0,
+ "Avision", "AV800S",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV810C",
+ 0, 0,
+ "Avision", "AV810C",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV820",
+ 0, 0,
+ "Avision", "AV820",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV820C",
+ 0, 0,
+ "Avision", "AV820C",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV820C Plus",
+ 0, 0,
+ "Avision", "AV820C Plus",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV830C",
+ 0, 0,
+ "Avision", "AV830C",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV830C Plus",
+ 0, 0,
+ "Avision", "AV830C Plus",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV880",
+ 0, 0,
+ "Avision", "AV880",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV880C",
+ 0, 0,
+ "Avision", "AV880C",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="untested" */
+
+ { "AVISION", "AV3200C",
+ 0, 0,
+ "Avision", "AV3200C",
+ AV_NON_INTERLACED_DUPLEX_300 | AV_FASTER_WITH_FILTER,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV3200SU",
+ 0x0638, 0x0A4E,
+ "Avision", "AV3200SU",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV3730SU",
+ 0x0638, 0x0A4F,
+ "Avision", "AV3730SU",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV3750SU",
+ 0x0638, 0x0A65,
+ "Avision", "AV3750SU",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV3800C",
+ 0, 0,
+ "Avision", "AV3800C",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "AV3850SU",
+ 0x0638, 0x0a66,
+ "Avision", "AV3850SU",
+ 0,0},
+ /* comment="1 pass, ??? dpi" */
+ /* status="complete" */
+
+ { "AVISION", "FB6000E",
+ 0, 0,
+ "Avision", "FB6000E",
+ AV_NON_INTERLACED_DUPLEX_300,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex! - zero edge!" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a82,
+ "Avision", "FB6080E",
+ AV_NON_INTERLACED_DUPLEX_300,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex! - zero edge!" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a84,
+ "Avision", "FB2080E",
+ 0,0},
+ /* comment="1 pass, 600 dpi, zero-edge" ASIC 7 */
+ /* status="basic" */
+
+ { "AVISION", "AV8000S",
+ 0, 0,
+ "Avision", "AV8000S",
+ AV_DOES_NOT_KEEP_WINDOW,0},
+ /* comment="1 pass, 1200 dpi, A3" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0a4d,
+ "Avision", "AV8050U",
+ AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex!" */
+ /* status="complete" */
+
+ { "AVISION", "AV8300",
+ 0x0638, 0x0A40,
+ "Avision", "AV8300",
+ AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex!" */
+ /* status="complete" */
+
+ { "AVISION", "AV8350",
+ 0x0638, 0x0A68,
+ "Avision", "AV8350",
+ AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex!" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A61,
+ "Avision", "IT8300",
+ AV_NON_INTERLACED_DUPLEX_300 | AV_ACCEL_TABLE,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x0638, 0x0AA1,
+ "Avision", "@V2500",
+ 0,0},
+ /* comment="" */
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x0638, 0x0A45,
+ "Avision", "@V5100",
+ 0,0},
+ /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */
+ /* status="good" */
+
+ { "AVISION", "AVA3",
+ 0, 0,
+ "Avision", "AVA3",
+ AV_FORCE_A3,0},
+ /* comment="1 pass, 600 dpi, A3" */
+ /* status="basic" */
+
+ /* and possibly more avisions ;-) */
+
+ { "HP", "ScanJet 5300C",
+ 0x03f0, 0x0701,
+ "Hewlett-Packard", "ScanJet 5300C",
+ AV_INT_STATUS,0},
+ /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */
+ /* status="complete" */
+
+ { "HP", "ScanJet 5370C",
+ 0x03f0, 0x0701,
+ "Hewlett-Packard", "ScanJet 5370C",
+ AV_MULTI_CALIB_CMD | AV_INT_STATUS, AV_FIRMWARE },
+ /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */
+ /* status="good" */
+
+ { "hp", "scanjet 7400c",
+ 0x03f0, 0x0801,
+ "Hewlett-Packard", "ScanJet 7400c",
+ AV_LIGHT_CHECK_BOGUS | AV_NO_64BYTE_ALIGN | AV_INT_STATUS,0},
+ /* comment="1 pass, 2400 dpi - dual USB/SCSI interface" */
+ /* status="good" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { "hp", "scanjet 7450c",
+ 0x03f0, 0x0801,
+ "Hewlett-Packard", "ScanJet 7450c",
+ AV_NO_64BYTE_ALIGN | AV_INT_STATUS,0},
+ /* comment="1 pass, 2400 dpi - dual USB/SCSI interface" */
+ /* status="good" */
+
+ { "hp", "scanjet 7490c",
+ 0x03f0, 0x0801,
+ "Hewlett-Packard", "ScanJet 7490c",
+ AV_NO_64BYTE_ALIGN | AV_INT_STATUS,0},
+ /* comment="1 pass, 1200 dpi - dual USB/SCSI interface" */
+ /* status="good" */
+
+#endif
+ { "HP", "C9930A",
+ 0x03f0, 0x0b01,
+ "Hewlett-Packard", "ScanJet 8200",
+ 0, AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE },
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0" */
+ /* status="good" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { "HP", "C9930A",
+ 0x03f0, 0x0b01,
+ "Hewlett-Packard", "ScanJet 8250",
+ 0, AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE },
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0" */
+ /* status="good" */
+#endif
+
+ { "HP", "C9930A",
+ 0x03f0, 0x3905,
+ "Hewlett-Packard", "ScanJet 8270",
+ 0, AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE },
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0" */
+ /* status="good" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { "HP", "C9930A",
+ 0x03f0, 0x0b01,
+ "Hewlett-Packard", "ScanJet 8290",
+ 0, AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE },
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0 and SCSI - only SCSI tested so far" */
+ /* status="good" */
+
+#endif
+ { "HP", "C9930A",
+ 0x03f0, 0x3805,
+ "Hewlett-Packard", "ScanJet 8300",
+ 0,0},
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0" */
+ /* status="good" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { "HP", "C9930A",
+ 0x03f0, 0x3805,
+ "Hewlett-Packard", "ScanJet 8350",
+ 0,0},
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0" */
+ /* status="good" */
+
+ { "HP", "C9930A",
+ 0x03f0, 0x3805,
+ "Hewlett-Packard", "ScanJet 8390",
+ 0,0},
+ /* comment="1 pass, 4800 (?) dpi - USB 2.0" */
+ /* status="good" */
+
+#endif
+ { "Minolta", "#2882",
+ 0, 0,
+ "Minolta", "Dimage Scan Dual I",
+ AV_FORCE_FILM | AV_NO_START_SCAN,0}, /* not AV_FILMSCANNER (no frame control) */
+ /* status="basic" */
+
+ { "Minolta", "#2887",
+ 0, 0,
+ "Minolta", "Scan Multi Pro",
+ AV_FORCE_FILM | AV_NO_START_SCAN,0}, /* AV_FILMSCANNER (frame control)? */
+ /* status="untested" */
+
+ { "MINOLTA", "FS-V1",
+ 0x0638, 0x026a,
+ "Minolta", "Dimage Scan Dual II",
+ AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_12_BIT_MODE,0},
+ /* comment="1 pass, film-scanner" */
+ /* status="good" */
+
+ { "MINOLTA", "Elite II",
+ 0x0686, 0x4004,
+ "Minolta", "Elite II",
+ AV_FILMSCANNER | AV_ONE_CALIB_CMD,0},
+ /* comment="1 pass, film-scanner" */
+ /* status="untested" */
+
+ { "MINOLTA", "FS-V3",
+ 0x0686, 0x400d,
+ "Minolta", "Dimage Scan Dual III",
+ AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_ACCEL_TABLE ,0},
+ /* comment="1 pass, film-scanner" */
+ /* status="good" */
+
+ { "MINOLTA", "FS-V4",
+ 0x0686, 0x400e,
+ "Minolta", "Dimage Scan Elite 5400",
+ AV_FILMSCANNER | AV_ONE_CALIB_CMD | /*AV_ACCEL_TABLE |*/ AV_NO_START_SCAN,0},
+ /* comment="1 pass, film-scanner" */
+ /* status="good" */
+
+ { "QMS", "SC-110",
+ 0x0638, 0x0a15,
+ "Minolta-QMS", "SC-110",
+ 0,0},
+ /* comment="" */
+ /* status="untested" */
+
+ { "QMS", "SC-215",
+ 0x0638, 0x0a16,
+ "Minolta-QMS", "SC-215",
+ 0,0},
+ /* comment="" */
+ /* status="good" */
+
+ { "MITSBISH", "MCA-ADFC",
+ 0, 0,
+ "Mitsubishi", "MCA-ADFC",
+ 0,0},
+ /* status="untested" */
+
+ { "MITSBISH", "MCA-S1200C",
+ 0, 0,
+ "Mitsubishi", "S1200C",
+ 0,0},
+ /* status="untested" */
+
+ { "MITSBISH", "MCA-S600C",
+ 0, 0,
+ "Mitsubishi", "S600C",
+ 0,0},
+ /* status="untested" */
+
+ { "MITSBISH", "SS600",
+ 0, 0,
+ "Mitsubishi", "SS600",
+ 0,0},
+ /* status="good" */
+
+ /* The next are all untested ... */
+
+ { "FCPA", "ScanPartner",
+ 0, 0,
+ "Fujitsu", "ScanPartner",
+ AV_FUJITSU,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanPartner 10",
+ 0, 0,
+ "Fujitsu", "ScanPartner 10",
+ AV_FUJITSU,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanPartner 10C",
+ 0, 0,
+ "Fujitsu", "ScanPartner 10C",
+ AV_FUJITSU,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanPartner 15C",
+ 0, 0,
+ "Fujitsu", "ScanPartner 15C",
+ AV_FUJITSU,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanPartner 300C",
+ 0, 0,
+ "Fujitsu", "ScanPartner 300C",
+ 0,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanPartner 600C",
+ 0, 0,
+ "Fujitsu", "ScanPartner 600C",
+ 0,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanPartner 620C",
+ 0, 0,
+ "Fujitsu", "ScanPartner 620C",
+ AV_LIGHT_CHECK_BOGUS,0},
+ /* status="good" */
+
+ { "FCPA", "ScanPartner Jr",
+ 0, 0,
+ "Fujitsu", "ScanPartner Jr",
+ 0,0},
+ /* status="untested" */
+
+ { "FCPA", "ScanStation",
+ 0, 0,
+ "Fujitsu", "ScanStation",
+ 0,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04c5, 0x1029,
+ "Fujitsu", "fi-4010CU",
+ 0,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04c5, 0x10ef,
+ "Fujitsu", "fi-5015C",
+ 0,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x040a, 0x6001,
+ "Kodak", "i30",
+ AV_INT_BUTTON | AV_GRAY_MODES,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x040a, 0x6002,
+ "Kodak", "i40",
+ AV_INT_BUTTON | AV_GRAY_MODES,0},
+ /* status="basic" */
+
+ { NULL, NULL,
+ 0x040a, 0x6003,
+ "Kodak", "i50",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { NULL, NULL,
+ 0x040a, 0x6003,
+ "Kodak", "i55",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+#endif
+
+ { NULL, NULL,
+ 0x040a, 0x6004,
+ "Kodak", "i60",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { NULL, NULL,
+ 0x040a, 0x6004,
+ "Kodak", "i65",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+#endif
+
+ { NULL, NULL,
+ 0x040a, 0x6005,
+ "Kodak", "i80",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { "iVina", "1200U",
+ 0x0638, 0x0268,
+ "iVina", "1200U",
+ 0,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0424,
+ "Visioneer", "Strobe XP 450",
+ AV_INT_BUTTON | AV_ACCEL_TABLE,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0491,
+ "Visioneer", "Strobe XP 450-G",
+ AV_INT_BUTTON | AV_ACCEL_TABLE,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0479,
+ "Visioneer", "Strobe XP 470",
+ AV_INT_BUTTON | AV_ACCEL_TABLE,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x048F,
+ "Visioneer", "Strobe XP 470-G",
+ AV_INT_BUTTON | AV_ACCEL_TABLE,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0420,
+ "Visioneer", "9320",
+ 0,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0421,
+ "Visioneer", "9450",
+ AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_NO_BUTTON, AV_NO_TUNE_SCAN_LENGTH},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x047A,
+ "Visioneer", "9450-G",
+ 0,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0422,
+ "Visioneer", "9550",
+ 0,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0390,
+ "Visioneer", "9650",
+ 0,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x047B,
+ "Visioneer", "9650-G",
+ 0,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0423,
+ "Visioneer", "9750",
+ AV_INT_BUTTON,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0493,
+ "Visioneer", "9750-G",
+ AV_INT_BUTTON,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0497,
+ "Visioneer", "Patriot 430",
+ AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { NULL, NULL,
+ 0x04a7, 0x048F,
+ "Visioneer", "Patriot 470",
+ AV_INT_BUTTON,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+#endif
+
+ { NULL, NULL,
+ 0x04a7, 0x0498,
+ "Visioneer", "Patriot 680",
+ AV_INT_BUTTON,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0499,
+ "Visioneer", "Patriot 780",
+ AV_INT_BUTTON,0},
+ /* comment="sheetfed scanner" */
+ /* status="complete" */
+
+ { NULL, NULL,
+ 0x04a7, 0x049C,
+ "Xerox", "DocuMate150",
+ AV_INT_BUTTON | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0477,
+ "Xerox", "DocuMate152",
+ AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET | AV_BACKGROUND_QUIRK,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x049D,
+ "Xerox", "DocuMate162",
+ AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET | AV_BACKGROUND_QUIRK,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0448,
+ "Xerox", "DocuMate250",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0490,
+ "Xerox", "DocuMate250-G",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0449,
+ "Xerox", "DocuMate252",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x048C,
+ "Xerox", "DocuMate252-G",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0476,
+ "Xerox", "DocuMate232",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x044c,
+ "Xerox", "DocuMate262",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x048D,
+ "Xerox", "DocuMate262-G",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x04a7,
+ "Xerox", "DocuMate262i",
+ AV_INT_BUTTON,0},
+ /* status="good" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0475,
+ "Xerox", "DocuMate272",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x048E,
+ "Xerox", "DocuMate272-G",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0446,
+ "Xerox", "DocuMate510",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0495,
+ "Xerox", "DocuMate512",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x047c,
+ "Xerox", "DocuMate510-G",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0447,
+ "Xerox", "DocuMate520",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x0492,
+ "Xerox", "DocuMate520-G",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { NULL, NULL,
+ 0x04a7, 0x0498,
+ "Xerox", "DocuMate632",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+#endif
+
+ { NULL, NULL,
+ 0x04a7, 0x0478,
+ "Xerox", "DocuMate752",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+ { NULL, NULL,
+ 0x04a7, 0x049A,
+ "Xerox", "DocuMate752",
+ AV_INT_BUTTON,0},
+ /* status="untested" */
+
+#ifdef FAKE_ENTRIES_FOR_DESC_GENERATION
+ { NULL, NULL,
+ 0x0638, 0x0a16,
+ "OKI", "S700 Scancopier",
+ 0,0},
+ /* comment="1 pass, 600 dpi, A4" */
+ /* status="good" */
+#endif
+
+ { "B+H", "2000F",
+ 0, 0,
+ "Bell+Howell", "2000F",
+ 0,0},
+ /* comment="1 pass, ??? dpi, A4" */
+ /* status="basic" */
+
+ { NULL, NULL,
+ 0x0482, 0x0335,
+ "Kyocera", "FS-1016MFP",
+ 0,0},
+ /* comment="1 pass, ??? dpi, A4" */
+ /* status="untested" */
+
+ /* More IDs from the Avision dll:
+ ArtiScan ProA3
+ FB1065
+ FB1265
+ PHI860S
+ PSDC SCSI
+ SCSI Scan 19200
+ V6240 */
+
+ /* Possibly:
+Lexmark 4600 MFP Option MFP Options
+Lexmark 4600 MFP Option (C772n) MFP Options
+Lexmark X215
+Lexmark Optra Image X242
+Lexmark X443
+Lexmark 3100
+Lexmark 3200
+Lexmark X340 MFP Multifunction
+Lexmark X342n MFP Multifunction
+Lexmark X522
+Lexmark X630
+Lexmark X632E
+Lexmark X642e MFP Multifunction
+Lexmark X644e MFP Multifunction
+Lexmark X646dte MFP Multifunction
+Lexmark X646e MFP Multifunction
+Lexmark X646ef MFP Multifunction
+Lexmark X772e Multifunction
+Lexmark X850e MFP Multifunction
+Lexmark X852e MFP Multifunction
+Lexmark X854e MFP Multifunction
+Lexmark X4500 MFP
+ */
+
+ /* last entry detection */
+ { NULL, NULL,
+ 0, 0,
+ NULL, NULL,
+ 0,0}
+ };
+
+#if 0
+ struct timeval tv;
+ #define TIMING(txt) gettimeofday (&tv, NULL); \
+ DBG (4, "%lu: " txt "\n", tv.tv_sec * 1000000 + tv.tv_usec)
+#else
+ #define TIMING(txt)
+#endif
+
+/* used when scanner returns invalid range fields ... */
+#define A4_X_RANGE 8.5 /* or 8.25 ? */
+#define A4_Y_RANGE 11.8
+#define A3_X_RANGE 11.8
+#define A3_Y_RANGE 16.5 /* or 17 ? */
+#define FILM_X_RANGE 1.0 /* really ? */
+#define FILM_Y_RANGE 1.0
+#define SHEETFEED_Y_RANGE 14.0
+
+#define AVISION_CONFIG_FILE "avision.conf"
+
+#define STD_INQUIRY_SIZE 0x24
+#define AVISION_INQUIRY_SIZE_V1 0x60
+#define AVISION_INQUIRY_SIZE_V2 0x88
+#define AVISION_INQUIRY_SIZE_MAX AVISION_INQUIRY_SIZE_V2
+
+
+#define AVISION_BASE_RES 300
+
+/* calibration (shading) defines */
+
+#define INVALID_WHITE_SHADING 0x0000
+#define DEFAULT_WHITE_SHADING 0xFFF0
+
+#define MAX_WHITE_SHADING 0xFFFF
+/* originally the WHITE_MAP_RANGE was 0x4000 - but this always
+ * resulted in slightly too dark images - thus I have chosen
+ * 0x4FFF ... */
+#define WHITE_MAP_RANGE 0x4FFF
+
+#define INVALID_DARK_SHADING 0xFFFF
+#define DEFAULT_DARK_SHADING 0x0000
+
+#define read_constrains(s,var) {\
+ if (s->hw->hw->feature_type & AV_NO_64BYTE_ALIGN) {\
+ if (var % 64 == 0) var /= 2;\
+ if (var % 64 == 0) var += 2;\
+ }\
+}\
+
+static int num_devices;
+static Avision_Device* first_dev;
+static Avision_Scanner* first_handle;
+static const SANE_Device** devlist = 0;
+
+/* this is a bit hacky to get extra information in the attach callback */
+static Avision_HWEntry* attaching_hw = 0;
+
+/* disable the usage of a custom gamma-table */
+static SANE_Bool disable_gamma_table = SANE_FALSE;
+
+/* disable the calibration */
+static SANE_Bool disable_calibration = SANE_FALSE;
+static SANE_Bool force_calibration = SANE_FALSE;
+
+/* force scanable areas to ISO(DIN) A4/A3 */
+static SANE_Bool force_a4 = SANE_FALSE;
+static SANE_Bool force_a3 = SANE_FALSE;
+
+/* hardware resolutions to interpolate from */
+static const int hw_res_list_c5[] =
+ {
+ /* tested on AV600U */
+ 75, 150, 300, 600, 1200, 2400, 4800, /* ... */ 0
+ };
+static const int hw_res_list_generic[] =
+ {
+ 50, /* slower than 150 on the AV122/DM152, left for USB 1 host's preview */
+ 75, /* slower than 150 on the AV122/DM152, left for USB 1 host's */
+ 150, 200, 300,
+ /* 400,*/ /* AV122 simplex y-scaling and duplex interlacing corrupt */
+ 600, 1200, 2400, 4800,
+ /* ... */
+ 0
+ };
+
+static SANE_Bool static_calib_list[3] =
+ {
+ SANE_FALSE, SANE_FALSE, SANE_FALSE
+ };
+
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+
+static const SANE_Range percentage_range =
+ {
+ SANE_FIX (-100), /* minimum */
+ SANE_FIX (100), /* maximum */
+ SANE_FIX (1) /* quantization */
+ };
+
+static const SANE_Range abs_percentage_range =
+ {
+ SANE_FIX (0), /* minimum */
+ SANE_FIX (100), /* maximum */
+ SANE_FIX (1) /* quantization */
+ };
+
+static const SANE_Range exposure_range =
+ {
+ 0, /* minimum */
+ 1000, /* maximum */
+ 1 /* quantization */
+ };
+
+static const SANE_Range overscan_range =
+ {
+ SANE_FIX (0), /* minimum */
+ SANE_FIX (4), /* maximum */ /* 4mm, measured on AV122, AV220C2, i40 */
+ 0 /* quantization */
+ };
+
+/* The 0x32 is a random guess based on USB logs. Might need a
+ per-device value in the future - 0x32 was tested on the AV122,
+ DM152, AV220. */
+static const SANE_Range background_range =
+ {
+ 0, /* minimum */
+ 0x32, /* maximum */
+ 0 /* quantization */
+ };
+
+static const uint8_t test_unit_ready[] =
+ {
+ AVISION_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+static const uint8_t get_status[] =
+ {
+ AVISION_SCSI_GET_DATA_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x00
+ };
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ DBG (3, "max_string_size:\n");
+
+ for (i = 0; strings[i]; ++ i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+constrain_value (Avision_Scanner* s, SANE_Int option, void* value,
+ SANE_Int* info)
+{
+ DBG (3, "constrain_value:\n");
+ return sanei_constrain_value (s->opt + option, value, info);
+}
+
+static void debug_print_raw (int dbg_level, char* info, const uint8_t* data,
+ size_t count)
+{
+ size_t i;
+
+ DBG (dbg_level, "%s", info);
+ for (i = 0; i < count; ++ i) {
+ DBG (dbg_level, " [%lu] %1d%1d%1d%1d%1d%1d%1d%1db %3oo %3dd %2xx\n",
+ (u_long) i,
+ BIT(data[i],7), BIT(data[i],6), BIT(data[i],5), BIT(data[i],4),
+ BIT(data[i],3), BIT(data[i],2), BIT(data[i],1), BIT(data[i],0),
+ data[i], data[i], data[i]);
+ }
+}
+
+static void debug_print_hex_raw (int dbg_level, char* info, const uint8_t* data,
+ size_t count)
+{
+ int address = 0;
+ char text [16*3+1];
+
+ DBG (dbg_level, "%s", info);
+ while (count) {
+ char* t = text;
+ int i = 0;
+ while (i < 16 && count) {
+ t += sprintf (t, "%02x ", *data++);
+ count--; i++;
+ }
+ *--t = 0;
+
+ DBG (dbg_level, " [%08x] %s\n", address, text);
+ address += 16;
+ }
+}
+
+static void debug_print_nvram_data (int dbg_level, char* func,
+ nvram_data* nvram)
+{
+ DBG (dbg_level, "%s: pad scans: %d\n",
+ func, get_quad(nvram->pad_scans));
+ DBG (dbg_level, "%s: ADF simplex scans: %d\n",
+ func, get_quad(nvram->adf_simplex_scans));
+ DBG (dbg_level, "%s: ADF duplex scans: %d\n",
+ func, get_quad(nvram->adf_duplex_scans));
+ DBG (dbg_level, "%s: flatbed scans: %d\n",
+ func, get_quad(nvram->flatbed_scans));
+
+ DBG (dbg_level, "%s: flatbed leading edge: %d\n",
+ func, (int16_t)get_double(nvram->flatbed_leading_edge));
+ DBG (dbg_level, "%s: flatbed side edge: %d\n",
+ func, (int16_t)get_double(nvram->flatbed_side_edge));
+ DBG (dbg_level, "%s: ADF leading edge: %d\n",
+ func, (int16_t)get_double(nvram->adf_leading_edge));
+ DBG (dbg_level, "%s: ADF side edge: %d\n",
+ func, (int16_t)get_double(nvram->adf_side_edge));
+ DBG (dbg_level, "%s: ADF rear leading edge: %d\n",
+ func, (int16_t)get_double(nvram->adf_rear_leading_edge));
+ DBG (dbg_level, "%s: ADF rear side edge: %d\n",
+ func, (int16_t)get_double(nvram->adf_rear_side_edge));
+
+ DBG (dbg_level, "%s: born month: %d\n",
+ func, get_double(nvram->born_month));
+ DBG (dbg_level, "%s: born day: %d\n",
+ func, get_double(nvram->born_day));
+ DBG (dbg_level, "%s: born year: %d\n",
+ func, get_double(nvram->born_year));
+
+ DBG (dbg_level, "%s: first scan month: %d\n",
+ func, get_double(nvram->first_scan_month));
+ DBG (dbg_level, "%s: first scan day: %d\n",
+ func, get_double(nvram->first_scan_day));
+ DBG (dbg_level, "%s: first scan year: %d\n",
+ func, get_double(nvram->first_scan_year));
+
+
+ DBG (dbg_level, "%s: vert. magnification: %d\n",
+ func, get_double(nvram->vertical_magnification));
+ DBG (dbg_level, "%s: horiz. magnification: %d\n",
+ func, get_double(nvram->horizontal_magnification));
+
+ DBG (dbg_level, "%s: CCD type: %d\n",
+ func, nvram->ccd_type);
+ DBG (dbg_level, "%s: scan speed: %d\n",
+ func, nvram->scan_speed);
+
+ DBG (dbg_level, "%s: serial: '%.24s'\n", /* 24 chars max */
+ func, nvram->serial);
+
+ DBG (dbg_level, "%s: power saving time: %d\n",
+ func, get_double(nvram->power_saving_time));
+
+ DBG (dbg_level, "%s: auto feed: %d\n",
+ func, nvram->auto_feed);
+
+ DBG (dbg_level, "%s: roller count: %d\n",
+ func, get_quad(nvram->roller_count));
+ DBG (dbg_level, "%s: multifeed count: %d\n",
+ func, get_quad(nvram->multifeed_count));
+ DBG (dbg_level, "%s: jam count: %d\n",
+ func, get_quad(nvram->jam_count));
+
+ DBG (dbg_level, "%s: identify info: '%.16s'\n", /* 16 chars max */
+ func, nvram->identify_info);
+ DBG (dbg_level, "%s: formal_name: '%.16s'\n", /* 16 chars max */
+ func, nvram->formal_name);
+}
+
+static void debug_print_avdimen (int dbg_level, char* func,
+ Avision_Dimensions* avdimen)
+{
+ DBG (dbg_level, "%s: hw_xres: %d, hw_yres: %d, line_difference: %d\n",
+ func, avdimen->hw_xres, avdimen->hw_yres, avdimen->line_difference);
+
+ DBG (dbg_level, "%s: tlx: %ld, tly: %ld, brx: %ld, bry: %ld\n",
+ func, avdimen->tlx, avdimen->tly,
+ avdimen->brx, avdimen->bry);
+
+ DBG (dbg_level, "%s: hw_pixel_per_line: %d, hw_lines: %d, hw_bytes_per_line: %d\n",
+ func, avdimen->hw_pixels_per_line, avdimen->hw_lines, avdimen->hw_bytes_per_line);
+
+ DBG (dbg_level, "%s: xres: %d, yres: %d\n",
+ func, avdimen->xres, avdimen->yres);
+}
+
+static void debug_print_params (int dbg_level, char* func, SANE_Parameters* params)
+{
+ DBG (dbg_level, "%s: pixel_per_line: %d, lines: %d\n",
+ func, params->pixels_per_line, params->lines);
+
+ DBG (dbg_level, "%s: depth: %d, bytes_per_line: %d\n",
+ func, params->depth, params->bytes_per_line);
+}
+
+static void debug_print_calib_format (int dbg_level, char* func,
+ uint8_t* result)
+{
+ debug_print_raw (dbg_level + 2, "debug_print_calib_format:\n", result, 32);
+
+ DBG (dbg_level, "%s: [0-1] pixels per line: %d\n",
+ func, get_double ( &(result[0]) ));
+ DBG (dbg_level, "%s: [2] bytes per channel: %d\n", func, result[2]);
+ DBG (dbg_level, "%s: [3] line count: %d\n", func, result[3]);
+
+ DBG (dbg_level, "%s: [4] FLAG:%s%s%s\n",
+ func,
+ result[4] == 1?" MUST_DO_CALIBRATION":"",
+ result[4] == 2?" SCAN_IMAGE_DOES_CALIBRATION":"",
+ result[4] == 3?" NEEDS_NO_CALIBRATION":"");
+
+ DBG (dbg_level, "%s: [5] Ability1:%s%s%s%s%s%s%s%s\n",
+ func,
+ BIT(result[5],7)?" NONE_PACKED":" PACKED",
+ BIT(result[5],6)?" INTERPOLATED":"",
+ BIT(result[5],5)?" SEND_REVERSED":"",
+ BIT(result[5],4)?" PACKED_DATA":"",
+ BIT(result[5],3)?" COLOR_CALIB":"",
+ BIT(result[5],2)?" DARK_CALIB":"",
+ BIT(result[5],1)?" NEEDS_WHITE_BLACK_SHADING_DATA":"",
+ BIT(result[5],0)?" NEEDS_CALIB_TABLE_CHANNEL_BY_CHANNEL":"");
+
+ DBG (dbg_level, "%s: [6] R gain: %d\n", func, result[6]);
+ DBG (dbg_level, "%s: [7] G gain: %d\n", func, result[7]);
+ DBG (dbg_level, "%s: [8] B gain: %d\n", func, result[8]);
+
+ DBG (dbg_level, "%s: [9-10] R shading target: %x\n",
+ func, get_double ( &(result[9]) ) );
+ DBG (dbg_level, "%s: [11-12] G shading target: %x\n",
+ func, get_double ( &(result[11]) ) );
+ DBG (dbg_level, "%s: [13-14] B shading target: %x\n",
+ func, get_double ( &(result[13]) ) );
+
+ DBG (dbg_level, "%s: [15-16] R dark shading target: %x\n",
+ func, get_double ( &(result[15]) ) );
+ DBG (dbg_level, "%s: [17-18] G dark shading target: %x\n",
+ func, get_double ( &(result[17]) ) );
+ DBG (dbg_level, "%s: [19-20] B dark shading target: %x\n",
+ func, get_double ( &(result[19]) ) );
+
+ DBG (dbg_level, "%s: [21] true-gray gain: %d\n", func, result[21]);
+ DBG (dbg_level, "%s: [22-23] true-gray shading target: %x\n",
+ func, get_double ( &(result[22]) ) );
+
+ DBG (dbg_level, "%s: [24-25] true-gray dark shading target: %x\n",
+ func, get_double ( &(result[24]) ) );
+}
+
+static void debug_print_accel_info (int dbg_level, char* func,
+ uint8_t* result)
+{
+ debug_print_raw (dbg_level + 2, "debug_print_accel_info:\n", result, 24);
+
+ DBG (dbg_level, "%s: [0-1] acceleration step count: %d\n",
+ func, get_double ( &(result[0]) ));
+ DBG (dbg_level, "%s: [2-3] stable step count: %d\n",
+ func, get_double ( &(result[2]) ));
+ DBG (dbg_level, "%s: [4-7] table units: %d\n",
+ func, get_quad ( &(result[4]) ));
+ DBG (dbg_level, "%s: [8-11] base units: %d\n",
+ func, get_quad ( &(result[8]) ));
+ DBG (dbg_level, "%s: [12-13] start speed: %d\n",
+ func, get_double ( &(result[12]) ));
+ DBG (dbg_level, "%s: [14-15] target speed: %d\n",
+ func, get_double ( &(result[14]) ));
+ DBG (dbg_level, "%s: [16] ability:%s%s\n",
+ func,
+ BIT(result[16],0)?" TWO_BYTES_PER_ELEM":" SINGLE_BYTE_PER_ELEM",
+ BIT(result[16],1)?" LOW_HIGH_ORDER":" HIGH_LOW_ORDER");
+ DBG (dbg_level, "%s: [17] table count: %d\n", func, result[17]);
+
+}
+
+static void debug_print_window_descriptor (int dbg_level, char* func,
+ command_set_window_window* window)
+{
+ debug_print_raw (dbg_level + 1, "window_data_header: \n",
+ (uint8_t*)(&window->header),
+ sizeof(window->header));
+
+ debug_print_raw (dbg_level + 1, "window_descriptor: \n",
+ (uint8_t*)(&window->descriptor),
+ sizeof(*window) -
+ sizeof(window->header));
+
+ DBG (dbg_level, "%s: [0] window_id: %d\n", func,
+ window->descriptor.winid);
+ DBG (dbg_level, "%s: [2-3] x-axis res: %d\n", func,
+ get_double (window->descriptor.xres));
+ DBG (dbg_level, "%s: [4-5] y-axis res: %d\n", func,
+ get_double (window->descriptor.yres));
+ DBG (dbg_level, "%s: [6-9] x-axis upper left: %d\n",
+ func, get_quad (window->descriptor.ulx));
+ DBG (dbg_level, "%s: [10-13] y-axis upper left: %d\n",
+ func, get_quad (window->descriptor.uly));
+ DBG (dbg_level, "%s: [14-17] window width: %d\n", func,
+ get_quad (window->descriptor.width));
+ DBG (dbg_level, "%s: [18-21] window length: %d\n", func,
+ get_quad (window->descriptor.length));
+ DBG (dbg_level, "%s: [22] brightness: %d\n", func,
+ window->descriptor.brightness);
+ DBG (dbg_level, "%s: [23] threshold: %d\n", func,
+ window->descriptor.threshold);
+ DBG (dbg_level, "%s: [24] contrast: %d\n", func,
+ window->descriptor.contrast);
+ DBG (dbg_level, "%s: [25] image composition: %x\n", func,
+ window->descriptor.image_comp);
+ DBG (dbg_level, "%s: [26] bits per channel: %d\n", func,
+ window->descriptor.bpc);
+ DBG (dbg_level, "%s: [27-28] halftone pattern: %x\n", func,
+ get_double (window->descriptor.halftone));
+ DBG (dbg_level, "%s: [29] padding_and_bitset: %x\n", func,
+ window->descriptor.padding_and_bitset);
+ DBG (dbg_level, "%s: [30-31] bit ordering: %x\n", func,
+ get_double (window->descriptor.bitordering));
+ DBG (dbg_level, "%s: [32] compression type: %x\n", func,
+ window->descriptor.compr_type);
+ DBG (dbg_level, "%s: [33] compression argument: %x\n", func,
+ window->descriptor.compr_arg);
+ DBG (dbg_level, "%s: [34-35] paper length: %x\n", func,
+ get_double (window->descriptor.paper_length) );
+ DBG (dbg_level, "%s: [40] vendor id: %x\n", func,
+ window->descriptor.vendor_specific);
+ DBG (dbg_level, "%s: [41] param length: %d\n", func,
+ window->descriptor.paralen);
+ DBG (dbg_level, "%s: [42] bitset1: %x\n", func,
+ window->avision.bitset1);
+ DBG (dbg_level, "%s: [43] highlight: %d\n", func,
+ window->avision.highlight);
+ DBG (dbg_level, "%s: [44] shadow: %d\n", func,
+ window->avision.shadow);
+ DBG (dbg_level, "%s: [45-46] line-width: %d\n", func,
+ get_double (window->avision.line_width));
+ DBG (dbg_level, "%s: [47-48] line-count: %d\n", func,
+ get_double (window->avision.line_count));
+ DBG (dbg_level, "%s: [49] bitset2: %x\n", func,
+ window->avision.type.normal.bitset2);
+ DBG (dbg_level, "%s: [50] ir exposure time: %x\n",
+ func, window->avision.type.normal.ir_exposure_time);
+
+ DBG (dbg_level, "%s: [51-52] r exposure: %x\n", func,
+ get_double (window->avision.type.normal.r_exposure_time));
+ DBG (dbg_level, "%s: [53-54] g exposure: %x\n", func,
+ get_double (window->avision.type.normal.g_exposure_time));
+ DBG (dbg_level, "%s: [55-56] b exposure: %x\n", func,
+ get_double (window->avision.type.normal.b_exposure_time));
+
+ DBG (dbg_level, "%s: [57] bitset3: %x\n", func,
+ window->avision.type.normal.bitset3);
+ DBG (dbg_level, "%s: [58] auto focus: %d\n", func,
+ window->avision.type.normal.auto_focus);
+ DBG (dbg_level, "%s: [59] line-width (MSB): %d\n",
+ func, window->avision.type.normal.line_width_msb);
+ DBG (dbg_level, "%s: [60] line-count (MSB): %d\n",
+ func, window->avision.type.normal.line_count_msb);
+ DBG (dbg_level, "%s: [61] background lines: %d\n",
+ func, window->avision.type.normal.background_lines);
+}
+
+static int write_pnm_header (FILE* f, color_mode m, int depth, int width, int height)
+{
+ int maxval = (1 << depth) - 1;
+ const char* hdr_str = NULL;
+ /* construct PNM header */
+
+ switch (m) {
+ case AV_THRESHOLDED:
+ case AV_DITHERED:
+ hdr_str = "P4\n%d %d\n";
+ break;
+ case AV_GRAYSCALE:
+ case AV_GRAYSCALE12:
+ case AV_GRAYSCALE16:
+ hdr_str = "P5\n%d %d\n%d\n";
+ break;
+ case AV_TRUECOLOR:
+ case AV_TRUECOLOR12:
+ case AV_TRUECOLOR16:
+ hdr_str = "P6\n%d %d\n%d\n";
+ break;
+ case AV_COLOR_MODE_LAST:
+ ; /* silence compiler warning */
+ }
+
+ return fprintf (f, hdr_str, width, height, maxval);
+}
+
+static SANE_Status
+sense_handler (int fd, u_char* sense, void* arg)
+{
+ SANE_Status status = SANE_STATUS_IO_ERROR; /* default case */
+
+ char* text;
+ char textbuf[64];
+
+ uint8_t error_code = sense[0] & 0x7f;
+ uint8_t sense_key = sense[2] & 0xf;
+ uint8_t additional_sense = sense[7];
+
+ fd = fd; /* silence gcc */
+ arg = arg; /* silence gcc */
+
+ DBG (3, "sense_handler:\n");
+
+ switch (error_code)
+ {
+ case 0x70:
+ text = "standard sense";
+ break;
+ case 0x7f:
+ text = "Avision-specific sense";
+ break;
+ default:
+ text = "unknown sense";
+ }
+
+ debug_print_raw (1, "sense_handler: data:\n", sense, 8 + additional_sense);
+
+ /* request valid? */
+ if (! (sense[0] & (1<<7))) {
+ DBG (1, "sense_handler: sense not valid ...\n");
+ return status;
+ }
+
+ switch (sense_key)
+ {
+ case 0x00:
+ status = SANE_STATUS_GOOD;
+ text = "ok ?!?";
+ break;
+ case 0x02:
+ text = "NOT READY";
+ break;
+ case 0x03:
+ text = "MEDIUM ERROR (mostly ADF)";
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x04:
+ text = "HARDWARE ERROR";
+ break;
+ case 0x05:
+ text = "ILLEGAL REQUEST";
+ break;
+ case 0x06:
+ text = "UNIT ATTENTION";
+ break;
+ case 0x09:
+ text = "VENDOR SPECIFIC";
+ break;
+ case 0x0b:
+ text = "ABORTED COMMAND";
+ status = SANE_STATUS_CANCELLED; /* AV610C2 cancel button */
+ break;
+ default:
+ sprintf (textbuf, "got unknown sense code 0x%02x", (int)sense_key);
+ text = textbuf;
+ }
+
+ DBG (1, "sense_handler: sense code: %s\n", text);
+
+ if (sense[2] & (1<<6))
+ DBG (1, "sense_handler: end of scan\n");
+ else
+ DBG (1, "sense_handler: scan has not yet been completed\n");
+
+ if (sense[2] & (1<<5))
+ DBG (1, "sense_handler: incorrect logical length\n");
+ else
+ DBG (1, "sense_handler: correct logical length\n");
+
+ {
+ uint8_t asc = sense[12];
+ uint8_t ascq = sense[13];
+
+#define ADDITIONAL_SENSE(asc,ascq,txt) \
+ case ( (asc << 8) + ascq): text = txt; break
+
+ switch ( (asc << 8) + ascq )
+ {
+ /* normal */
+ ADDITIONAL_SENSE (0x00,0x00, "No additional sense information");
+ ADDITIONAL_SENSE (0x00,0x06, "I/O process terminated");
+ ADDITIONAL_SENSE (0x15,0x01, "Mechanical positioning error");
+
+ ADDITIONAL_SENSE (0x15,0x02, "Flatbed Home Sensor Error (OKI only");
+ ADDITIONAL_SENSE (0x15,0x03, "ADF Home Sensor Error (OKI only)");
+ ADDITIONAL_SENSE (0x15,0x04, "Lock Error (OKI only)");
+
+ ADDITIONAL_SENSE (0x1a,0x00, "parameter list length error");
+
+ ADDITIONAL_SENSE (0x20,0x00, "Invalid command");
+ ADDITIONAL_SENSE (0x24,0x00, "Invalid field in CDB");
+ ADDITIONAL_SENSE (0x25,0x00, "Logical unit not supported");
+ ADDITIONAL_SENSE (0x26,0x00, "Invalid field in parameter list");
+ ADDITIONAL_SENSE (0x26,0x01, "parameter not supported");
+ ADDITIONAL_SENSE (0x26,0x02, "parameter value invalid");
+ ADDITIONAL_SENSE (0x29,0x00, "Power-on, reset or bus device reset occurred");
+ ADDITIONAL_SENSE (0x2c,0x02, "Invalid combination of window specified");
+ ADDITIONAL_SENSE (0x2f,0x00, "Command cleared by another initiator");
+
+ ADDITIONAL_SENSE (0x3D,0x00, "Invalid Bit in Identify Message");
+
+ ADDITIONAL_SENSE (0x43,0x00, "Message error");
+ ADDITIONAL_SENSE (0x44,0x00, "Internal target failure");
+ ADDITIONAL_SENSE (0x44,0x01, "Flatbed DRAM Error(OKI only)");
+ ADDITIONAL_SENSE (0x44,0x02, "ADF DRAM Error(OKI only)");
+ ADDITIONAL_SENSE (0x44,0x03, "Write NVRAM Error");
+ ADDITIONAL_SENSE (0x47,0x00, "SCSI parity error");
+ ADDITIONAL_SENSE (0x49,0x00, "Invalid message error");
+
+ ADDITIONAL_SENSE (0x60,0x00, "Lamp failure");
+ ADDITIONAL_SENSE (0x60,0x01, "Flatbed Lamp error (Oki only)");
+ ADDITIONAL_SENSE (0x60,0x02, "ADF lamp error (Oki only)");
+ ADDITIONAL_SENSE (0x62,0x00, "Scan head positioning error");
+
+ ADDITIONAL_SENSE (0x80,0x01, "ADF paper jam"; status = SANE_STATUS_JAMMED);
+ ADDITIONAL_SENSE (0x80,0x02, "ADF cover open"; status = SANE_STATUS_COVER_OPEN);
+ ADDITIONAL_SENSE (0x80,0x03, "ADF chute empty"; status = SANE_STATUS_NO_DOCS);
+ ADDITIONAL_SENSE (0x80,0x04, "ADF paper end"; status = SANE_STATUS_EOF);
+ ADDITIONAL_SENSE (0x80,0x05, "Multi-feed (AV220,Kodak)");
+ ADDITIONAL_SENSE (0x80,0x06, "ADF prefeeding (OKI only)");
+ ADDITIONAL_SENSE (0x80,0x07, "Flatbed cover open (OKI only)"; status = SANE_STATUS_COVER_OPEN);
+ ADDITIONAL_SENSE (0x80,0x08, "FW module doesn't match with scanner");
+ ADDITIONAL_SENSE (0x80,0x09, "Papers fed from multiple trays (DM272)");
+ ADDITIONAL_SENSE (0x80,0x0A, "ADF Paper Start");
+ ADDITIONAL_SENSE (0x80,0x0B, "Multiple ADF paper End and Start");
+ ADDITIONAL_SENSE (0x80,0x0C, "Multiple ADF paper End");
+
+ /* film scanner */
+ ADDITIONAL_SENSE (0x81,0x00, "ADF/MFP front door open"; status = SANE_STATUS_COVER_OPEN);
+ ADDITIONAL_SENSE (0x81,0x01, "ADF holder cartridge open"; status = SANE_STATUS_COVER_OPEN);
+ ADDITIONAL_SENSE (0x81,0x02, "ADF no film inside"; status = SANE_STATUS_NO_DOCS);
+ ADDITIONAL_SENSE (0x81,0x03, "ADF initial load fail");
+ ADDITIONAL_SENSE (0x81,0x04, "ADF film end"; status = SANE_STATUS_NO_DOCS);
+ ADDITIONAL_SENSE (0x81,0x05, "ADF forward feed error");
+ ADDITIONAL_SENSE (0x81,0x06, "ADF rewind error");
+ ADDITIONAL_SENSE (0x81,0x07, "ADF set unload");
+ ADDITIONAL_SENSE (0x81,0x08, "ADF adapter error");
+
+ ADDITIONAL_SENSE (0xA0,0x01, "Filter Positioning Error");
+
+ ADDITIONAL_SENSE (0x90,0x00, "Scanner busy (FW busy)");
+
+ default:
+ sprintf (textbuf, "Unknown sense code asc: 0x%02x, ascq: 0x%02x",
+ (int)asc, (int)ascq);
+ text = textbuf;
+ }
+
+#undef ADDITIONAL_SENSE
+
+ DBG (1, "sense_handler: sense code: %s\n", text);
+
+ /* sense code specific for invalid request
+ * it is possible to get a detailed error location here ;-)*/
+ if (sense_key == 0x05) {
+ if (sense[15] & (1<<7) )
+ {
+ if (sense[15] & (1<<6) )
+ DBG (1, "sense_handler: error in command parameter\n");
+ else
+ DBG (1, "sense_handler: error in data parameter\n");
+
+ DBG (1, "sense_handler: error in parameter byte: %d, %x\n",
+ get_double(&(sense[16])), get_double(&(sense[16])));
+
+ /* bit pointer valid ?*/
+ if (sense[15] & (1<<3) )
+ DBG (1, "sense_handler: error in command parameter\n");
+ else
+ DBG (1, "sense_handler: bit pointer invalid\n");
+ }
+ }
+ }
+
+ return status;
+}
+
+/*
+ * Avision scsi/usb multiplexers - to keep the code clean:
+ */
+
+static SANE_Status
+avision_usb_status (Avision_Connection* av_con, int retry, int timeout)
+{
+ SANE_Status status = 0;
+ uint8_t usb_status[1] = {0};
+ size_t count = 0;
+ int t_retry = retry;
+
+#define valid_status(status,a) (status == SANE_STATUS_GOOD ? a : 0)
+
+ DBG (4, "avision_usb_status: timeout %d, %d retries\n", timeout, retry);
+#ifndef HAVE_SANEI_USB_SET_TIMEOUT
+#error "You must update include/sane/sanei_usb.h and sanei/sanei_usb.c accordingly!"
+#endif
+ sanei_usb_set_timeout (timeout);
+
+ /* 1st try bulk transfers - they are more lightweight ... */
+ for (;
+ count == 0 &&
+ (av_con->usb_status == AVISION_USB_BULK_STATUS ||
+ av_con->usb_status == AVISION_USB_UNTESTED_STATUS) &&
+ retry > 0;
+ --retry)
+ {
+ count = sizeof (usb_status);
+
+ DBG (5, "==> (bulk read) going down ...\n");
+ status = sanei_usb_read_bulk (av_con->usb_dn, usb_status,
+ &count);
+ DBG (5, "<== (bulk read) got: %ld, status: %d\n",
+ (u_long)count, valid_status(status, usb_status[0]));
+
+ if (count > 0) {
+ av_con->usb_status = AVISION_USB_BULK_STATUS;
+ }
+ }
+
+ /* reset retry count ... */
+ retry = t_retry;
+
+ /* 2nd try interrupt status read - if not yet disabled */
+ for (;
+ count == 0 &&
+ (av_con->usb_status == AVISION_USB_INT_STATUS ||
+ av_con->usb_status == AVISION_USB_UNTESTED_STATUS) &&
+ retry > 0;
+ --retry)
+ {
+ count = sizeof (usb_status);
+
+ DBG (5, "==> (interrupt read) going down ...\n");
+ status = sanei_usb_read_int (av_con->usb_dn, usb_status,
+ &count);
+ DBG (5, "<== (interrupt read) got: %ld, status: %d\n",
+ (u_long)count, valid_status(status, usb_status[0]));
+
+ if (count > 0)
+ av_con->usb_status = AVISION_USB_INT_STATUS;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (count == 0)
+ return SANE_STATUS_IO_ERROR;
+
+ /* 0 = ok, 2 => request sense, 8 ==> busy, else error */
+ switch (usb_status[0])
+ {
+ case AVISION_USB_GOOD:
+ return SANE_STATUS_GOOD;
+ case AVISION_USB_REQUEST_SENSE:
+ DBG (2, "avision_usb_status: Needs to request sense!\n");
+ return SANE_STATUS_INVAL;
+ case AVISION_USB_BUSY:
+ DBG (2, "avision_usb_status: Busy!\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ default:
+ DBG (1, "avision_usb_status: Unknown!\n");
+ return SANE_STATUS_INVAL;
+ }
+}
+
+static SANE_Status avision_open (const char* device_name,
+ Avision_Connection* av_con,
+ SANEI_SCSI_Sense_Handler sense_handler,
+ void *sense_arg)
+{
+ if (av_con->connection_type == AV_SCSI) {
+ return sanei_scsi_open (device_name, &(av_con->scsi_fd),
+ sense_handler, sense_arg);
+ }
+ else {
+ SANE_Status status;
+ status = sanei_usb_open (device_name, &(av_con->usb_dn));
+ return status;
+ }
+}
+
+static SANE_Status avision_open_extended (const char* device_name,
+ Avision_Connection* av_con,
+ SANEI_SCSI_Sense_Handler sense_handler,
+ void *sense_arg, int *buffersize)
+{
+ if (av_con->connection_type == AV_SCSI) {
+ return sanei_scsi_open_extended (device_name, &(av_con->scsi_fd),
+ sense_handler, sense_arg, buffersize);
+ }
+ else {
+ SANE_Status status;
+ status = sanei_usb_open (device_name, &(av_con->usb_dn));
+ return status;
+ }
+}
+
+static void avision_close (Avision_Connection* av_con)
+{
+ if (av_con->connection_type == AV_SCSI) {
+ sanei_scsi_close (av_con->scsi_fd);
+ av_con->scsi_fd = -1;
+ }
+ else {
+ sanei_usb_close (av_con->usb_dn);
+ av_con->usb_dn = -1;
+ }
+}
+
+static SANE_Bool avision_is_open (Avision_Connection* av_con)
+{
+ if (av_con->connection_type == AV_SCSI) {
+ return av_con->scsi_fd >= 0;
+ }
+ else {
+ return av_con->usb_dn >= 0;
+ }
+}
+
+static SANE_Status avision_cmd (Avision_Connection* av_con,
+ const void* cmd, size_t cmd_size,
+ const void* src, size_t src_size,
+ void* dst, size_t* dst_size)
+{
+ if (av_con->connection_type == AV_SCSI) {
+ return sanei_scsi_cmd2 (av_con->scsi_fd, cmd, cmd_size,
+ src, src_size, dst, dst_size);
+ }
+ else {
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ size_t i, count, out_count;
+ /* some commands on some devices need a rather long time to respond */
+#define STD_TIMEOUT 30000
+#define STD_STATUS_TIMEOUT 10000
+ int retry = 4;
+ int write_timeout = STD_TIMEOUT;
+ int read_timeout = STD_TIMEOUT;
+ int status_timeout = STD_STATUS_TIMEOUT;
+
+ /* simply to allow nicer code below */
+ const uint8_t* m_cmd = (const uint8_t*)cmd;
+ const uint8_t* m_src = (const uint8_t*)src;
+ uint8_t* m_dst = (uint8_t*)dst;
+
+ /* may I vote for the possibility to use C99 ... */
+#define min_usb_size 10
+#define max_usb_size 256 * 1024 /* or 0x10000, used by AV Windows driver during background raster read, ... ? */
+
+ /* 1st send command data - at least 10 Bytes for USB scanners */
+ uint8_t enlarged_cmd [min_usb_size];
+ if (cmd_size < min_usb_size) {
+ DBG (1, "filling command to have a length of 10, was: %lu\n", (u_long) cmd_size);
+ memcpy (enlarged_cmd, m_cmd, cmd_size);
+ memset (enlarged_cmd + cmd_size, 0, min_usb_size - cmd_size);
+ m_cmd = enlarged_cmd;
+ cmd_size = min_usb_size;
+ }
+
+ /* per command class timeout tweaks */
+ switch (m_cmd[0]) {
+ case AVISION_SCSI_INQUIRY:
+ read_timeout = 1000; /* quickly timeout on initial detection */
+ status_timeout = 1000;
+ break;
+ case AVISION_SCSI_TEST_UNIT_READY:
+ read_timeout = 15000; /* quickly timeout on initial detection */
+ status_timeout = 15000;
+ break;
+ }
+
+ DBG (7, "Timeouts: write: %d, read: %d, status: %d\n",
+ write_timeout, read_timeout, status_timeout);
+
+write_usb_cmd:
+ if (--retry == 0) {
+ DBG (1, "Max retry count reached: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ count = cmd_size;
+
+ sanei_usb_set_timeout (write_timeout);
+ DBG (8, "try to write cmd, count: %lu.\n", (u_long) count);
+ status = sanei_usb_write_bulk (av_con->usb_dn, m_cmd, &count);
+
+ DBG (8, "wrote %lu bytes\n", (u_long) count);
+ if (status != SANE_STATUS_GOOD || count != cmd_size) {
+ DBG (3, "=== Got error %d trying to write, wrote: %ld. ===\n",
+ status, (long)count);
+
+ if (status != SANE_STATUS_GOOD) /* == SANE_STATUS_EOF) */ {
+ DBG (3, "try to read status to clear the FIFO\n");
+ status = avision_usb_status (av_con, 1, 500);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (3, "=== Got error %d trying to read status. ===\n", status);
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ goto write_usb_cmd;
+ } else {
+ DBG (3, "Retrying to send command\n");
+ goto write_usb_cmd;
+ }
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* 2nd send command data (if any) */
+ for (i = 0; i < src_size; ) {
+
+ count = src_size - i;
+ /* if (count > max_usb_size)
+ count = max_usb_size; */
+
+ DBG (8, "try to write src, count: %lu.\n", (u_long) count);
+ sanei_usb_set_timeout (write_timeout);
+ status = sanei_usb_write_bulk (av_con->usb_dn, &(m_src[i]), &count);
+
+ DBG (8, "wrote %lu bytes\n", (u_long) count);
+ if (status == SANE_STATUS_GOOD) {
+ i += count;
+ }
+ else {
+ goto write_usb_cmd;
+ }
+ }
+
+ /* 3rd: read the resulting data (payload) (if any) */
+ if (status == SANE_STATUS_GOOD && dst != NULL && *dst_size > 0) {
+ out_count = 0;
+ sanei_usb_set_timeout (read_timeout);
+ while (out_count < *dst_size) {
+ count = (*dst_size - out_count);
+
+ DBG (8, "try to read %lu bytes\n", (u_long) count);
+ status = sanei_usb_read_bulk(av_con->usb_dn, &(m_dst[out_count]),
+ &count);
+ DBG (8, "read %lu bytes\n", (u_long) count);
+
+ if (count == 1 && (*dst_size - out_count > 1)) {
+ DBG (1, "Got 1 byte - status? (%d) Resending.\n", m_dst[out_count]);
+ goto write_usb_cmd;
+ }
+ else if (count > 0) {
+ out_count += count;
+ }
+ else {
+ DBG (1, "No data arrived.\n");
+ goto write_usb_cmd;
+ }
+ }
+ }
+
+ /* last: read the device status via a pseudo interrupt transfer
+ * this is needed - otherwise the scanner will hang ... */
+ sanei_usb_set_timeout (status_timeout);
+ status = avision_usb_status (av_con, /*retry*/ 1, status_timeout);
+ /* next i/o hardening attempt - and yes this gets ugly ... */
+ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_INVAL)
+ goto write_usb_cmd;
+
+ if (status == SANE_STATUS_INVAL) {
+ struct {
+ command_header header;
+ uint8_t pad[4];
+ } sense_cmd;
+
+ uint8_t sense_buffer[22];
+
+ DBG (3, "Error during status read!\n");
+ DBG (3, "=== Try to request sense ===\n");
+
+ /* we can not call avision_cmd recursively - we might ending in
+ an endless recursion requesting sense for failing request
+ sense transfers ...*/
+
+ memset (&sense_cmd, 0, sizeof (sense_cmd) );
+ memset (&sense_buffer, 0, sizeof (sense_buffer) );
+ sense_cmd.header.opc = AVISION_SCSI_REQUEST_SENSE;
+ sense_cmd.header.len = sizeof (sense_buffer);
+
+ count = sizeof(sense_cmd);
+
+ DBG (8, "try to write %lu bytes\n", (u_long) count);
+ sanei_usb_set_timeout (write_timeout);
+ status = sanei_usb_write_bulk (av_con->usb_dn,
+ (uint8_t*) &sense_cmd, &count);
+ DBG (8, "wrote %lu bytes\n", (u_long) count);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (3, "=== Got error %d trying to request sense! ===\n", status);
+ }
+ else {
+ count = sizeof (sense_buffer);
+
+ DBG (8, "try to read %lu bytes sense data\n", (u_long) count);
+ sanei_usb_set_timeout (read_timeout);
+ status = sanei_usb_read_bulk(av_con->usb_dn, sense_buffer, &count);
+ DBG (8, "read %lu bytes sense data\n", (u_long) count);
+
+ /* we need to read out the status from the scanner i/o buffer */
+ status = avision_usb_status (av_con, 1, status_timeout);
+
+ /* some scanner return NEED_SENSE even after reading it */
+ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_INVAL)
+ DBG (3, "=== Got error %d trying to read sense! ===\n", status);
+ else {
+ /* read complete -> call our sense handler */
+ status = sense_handler (-1, sense_buffer, 0);
+ }
+ } /* end read sense data */
+ } /* end request sense */
+ return status;
+ } /* end cmd usb */
+}
+
+/* A bubble sort for the calibration. It only sorts the first third
+ * and returns an average of the top 2/3 values. The input data is
+ * 16bit big endian and the count is the count of the words - not
+ * bytes! */
+
+static uint16_t
+bubble_sort (uint8_t* sort_data, size_t count)
+{
+ size_t i, j, limit, k;
+ double sum = 0.0;
+
+ limit = count / 3;
+
+ for (i = 0; i < limit; ++i)
+ {
+ uint16_t ti = 0;
+ uint16_t tj = 0;
+
+ for (j = (i + 1); j < count; ++j)
+ {
+ ti = get_double ((sort_data + i*2));
+ tj = get_double ((sort_data + j*2));
+
+ if (ti > tj) {
+ set_double ((sort_data + i*2), tj);
+ set_double ((sort_data + j*2), ti);
+ }
+ }
+ }
+
+ for (k = 0, i = limit; i < count; ++i) {
+ sum += get_double ((sort_data + i*2));
+ ++ k;
+ }
+
+ /* DBG (7, "bubble_sort: %d values for average\n", k); */
+
+ if (k > 0) /* if avg to compute */
+ return (uint16_t) (sum / k);
+ else
+ return (uint16_t) (sum); /* always zero? */
+}
+
+static SANE_Status
+add_color_mode (Avision_Device* dev, color_mode mode, SANE_String name)
+{
+ int i;
+ DBG (3, "add_color_mode: %d %s\n", mode, name);
+
+ for (i = 0; i < AV_COLOR_MODE_LAST; ++i)
+ {
+ if (dev->color_list [i] == 0) {
+ dev->color_list [i] = strdup (name);
+ dev->color_list_num [i] = mode;
+ return SANE_STATUS_GOOD;
+ } else if (strcmp (dev->color_list [i], name) == 0) {
+ /* already in list */
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (3, "add_color_mode: failed\n");
+ return SANE_STATUS_NO_MEM;
+}
+
+static int
+last_color_mode (Avision_Device* dev)
+{
+ int i = 1;
+
+ while (dev->color_list [i] != 0 && i < AV_COLOR_MODE_LAST)
+ ++i;
+
+ /* we are off by one */
+ --i;
+
+ return i;
+}
+
+static color_mode
+match_color_mode (Avision_Device* dev, SANE_String name)
+{
+ int i;
+ DBG (3, "match_color_mode:\n");
+
+ for (i = 0; i < AV_COLOR_MODE_LAST; ++i)
+ {
+ if (dev->color_list [i] != 0 && strcmp (dev->color_list [i], name) == 0) {
+ DBG (3, "match_color_mode: found at %d mode: %d\n",
+ i, dev->color_list_num [i]);
+ return dev->color_list_num [i];
+ }
+ }
+
+ DBG (3, "match_color_mode: source mode invalid\n");
+ return AV_GRAYSCALE;
+}
+
+static SANE_Bool
+color_mode_is_shaded (color_mode mode)
+{
+ return mode >= AV_GRAYSCALE;
+}
+
+static SANE_Bool
+color_mode_is_color (color_mode mode)
+{
+ return mode >= AV_TRUECOLOR;
+}
+
+static SANE_Bool
+is_adf_scan (Avision_Scanner* s)
+{
+ return s->hw->scanner_type == AV_SHEETFEED || (s->hw->scanner_type == AV_FLATBED && s->source_mode_dim == AV_ADF_DIM);
+
+}
+
+static SANE_Status
+add_source_mode (Avision_Device* dev, source_mode mode, SANE_String name)
+{
+ int i;
+
+ for (i = 0; i < AV_SOURCE_MODE_LAST; ++i)
+ {
+ if (dev->source_list [i] == 0) {
+ dev->source_list [i] = strdup (name);
+ dev->source_list_num [i] = mode;
+ return SANE_STATUS_GOOD;
+ } else if (strcmp (dev->source_list [i], name) == 0) {
+ /* already in list */
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ return SANE_STATUS_NO_MEM;
+}
+
+static source_mode
+match_source_mode (Avision_Device* dev, SANE_String name)
+{
+ int i;
+
+ DBG (3, "match_source_mode: \"%s\"\n", name);
+
+ for (i = 0; i < AV_SOURCE_MODE_LAST; ++i)
+ {
+ if (dev->source_list [i] != 0 && strcmp (dev->source_list [i], name) == 0) {
+ DBG (3, "match_source_mode: found at %d mode: %d\n",
+ i, dev->source_list_num [i]);
+ return dev->source_list_num [i];
+ }
+ }
+
+ DBG (3, "match_source_mode: source mode invalid\n");
+ return AV_NORMAL;
+}
+
+static source_mode_dim
+match_source_mode_dim (source_mode sm)
+{
+ DBG (3, "match_source_mode_dim: %d\n", sm);
+
+ switch (sm) {
+ case AV_NORMAL:
+ return AV_NORMAL_DIM;
+ case AV_TRANSPARENT:
+ return AV_TRANSPARENT_DIM;
+ case AV_ADF:
+ case AV_ADF_REAR:
+ case AV_ADF_DUPLEX:
+ return AV_ADF_DIM;
+ default:
+ DBG (3, "match_source_mode_dim: source mode invalid\n");
+ return AV_NORMAL_DIM;
+ }
+}
+
+static int
+get_pixel_boundary (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+ int boundary;
+
+ switch (s->c_mode) {
+ case AV_TRUECOLOR:
+ case AV_TRUECOLOR12:
+ case AV_TRUECOLOR16:
+ boundary = dev->inquiry_color_boundary;
+ break;
+ case AV_GRAYSCALE:
+ case AV_GRAYSCALE12:
+ case AV_GRAYSCALE16:
+ boundary = dev->inquiry_gray_boundary;
+ break;
+ case AV_DITHERED:
+ if (dev->inquiry_asic_type != AV_ASIC_C5)
+ boundary = 32;
+ else
+ boundary = dev->inquiry_dithered_boundary;
+ break;
+ case AV_THRESHOLDED:
+ if (dev->inquiry_asic_type != AV_ASIC_C5)
+ boundary = 32;
+ else
+ boundary = dev->inquiry_thresholded_boundary;
+ break;
+ default:
+ boundary = 8;
+ }
+
+ return boundary;
+}
+
+static SANE_Status
+compute_parameters (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+
+ int boundary = get_pixel_boundary (s);
+ SANE_Bool gray_mode = color_mode_is_shaded (s->c_mode);
+
+ /* interlaced duplex (higher end) or flipping paper (HP8xxx)? */
+ s->avdimen.interlaced_duplex = s->source_mode == AV_ADF_DUPLEX &&
+ dev->inquiry_duplex_interlaced;
+
+ /* for infra-red we use the same code path es for interlaced
+ duplex */
+ if (s->val[OPT_IR].w)
+ s->avdimen.interlaced_duplex = 1;
+
+#ifdef AVISION_ENHANCED_SANE
+ /* quick fix for Microsoft Office Products ... */
+ switch (s->c_mode)
+ {
+ case AV_THRESHOLDED:
+ case AV_DITHERED:
+ /* our backend already has this restriction - so this line is for
+ documentation purposes only */
+ boundary = boundary > 32 ? boundary : 32;
+ break;
+ case AV_GRAYSCALE:
+ case AV_GRAYSCALE12:
+ case AV_GRAYSCALE16:
+ boundary = boundary > 4 ? boundary : 4;
+ break;
+ case AV_TRUECOLOR:
+ case AV_TRUECOLOR12:
+ case AV_TRUECOLOR16:
+ /* 12 bytes for 24bit color - 48bit is untested w/ Office */
+ boundary = boundary > 4 ? boundary : 4;
+ break;
+ }
+#endif
+
+ DBG (3, "sane_compute_parameters:\n");
+
+ DBG (3, "sane_compute_parameters: boundary %d, gray_mode: %d, \n",
+ boundary, gray_mode);
+
+ /* TODO: Implement different x/y resolutions support */
+ s->avdimen.xres = s->val[OPT_RESOLUTION].w;
+ s->avdimen.yres = s->val[OPT_RESOLUTION].w;
+
+ /* soft scale ? */
+ if (dev->hw->feature_type & AV_SOFT_SCALE) {
+ /* find supported hardware resolution */
+ const int* hw_res;
+ const int* hw_res_list =
+ dev->inquiry_asic_type == AV_ASIC_C5 ? hw_res_list_c5 : hw_res_list_generic;
+
+ for (hw_res = hw_res_list; *hw_res && *hw_res < s->avdimen.xres; ++hw_res)
+ /* just iterate */;
+ s->avdimen.hw_xres = *hw_res;
+
+ for (hw_res = hw_res_list; *hw_res && *hw_res < s->avdimen.yres; ++hw_res)
+ /* just iterate */;
+ s->avdimen.hw_yres = *hw_res;
+
+ DBG (3, "sane_compute_parameters: soft scale, hw res: %dx%d\n",
+ s->avdimen.hw_xres,
+ s->avdimen.hw_yres);
+
+ if (!s->avdimen.hw_xres || ! s->avdimen.hw_yres) {
+ DBG (1, "sane_compute_parameters: no matching HW res for: %dx%d\n",
+ s->avdimen.xres,
+ s->avdimen.yres);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else {
+ s->avdimen.hw_xres = s->val[OPT_RESOLUTION].w;
+ s->avdimen.hw_yres = s->val[OPT_RESOLUTION].w;
+ }
+
+ DBG (3, "sane_compute_parameters: tlx: %f, tly: %f, brx: %f, bry: %f\n",
+ SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w),
+ SANE_UNFIX (s->val[OPT_BR_X].w), SANE_UNFIX (s->val[OPT_BR_Y].w));
+
+ /* window parameter in pixel */
+ s->avdimen.tlx = s->avdimen.hw_xres * SANE_UNFIX (s->val[OPT_TL_X].w)
+ / MM_PER_INCH;
+ s->avdimen.tly = s->avdimen.hw_yres * SANE_UNFIX (s->val[OPT_TL_Y].w)
+ / MM_PER_INCH;
+ s->avdimen.brx = s->avdimen.hw_xres * SANE_UNFIX (s->val[OPT_BR_X].w)
+ / MM_PER_INCH;
+ s->avdimen.bry = s->avdimen.hw_yres * SANE_UNFIX (s->val[OPT_BR_Y].w)
+ / MM_PER_INCH;
+
+ /* line difference */
+ if (color_mode_is_color (s->c_mode) &&
+ dev->inquiry_needs_software_colorpack &&
+ dev->inquiry_line_difference)
+ {
+ s->avdimen.line_difference =
+ (dev->inquiry_line_difference * s->avdimen.hw_yres) / dev->inquiry_optical_res;
+
+ s->avdimen.bry += 2 * s->avdimen.line_difference;
+
+ /* limit bry + line_difference to real scan boundary */
+ {
+ long y_max = dev->inquiry_y_ranges[s->source_mode_dim] *
+ s->avdimen.hw_yres / MM_PER_INCH;
+ DBG (3, "sane_compute_parameters: y_max: %ld, bry: %ld, line_difference: %d\n",
+ y_max, s->avdimen.bry, s->avdimen.line_difference);
+
+ if (s->avdimen.bry + 2 * s->avdimen.line_difference > y_max) {
+ DBG (1, "sane_compute_parameters: bry limited!\n");
+ s->avdimen.bry = y_max - 2 * s->avdimen.line_difference;
+ }
+ }
+
+ } /* end if needs software colorpack */
+ else {
+ s->avdimen.line_difference = 0;
+ }
+
+ /* add overscan */
+ if (dev->inquiry_tune_scan_length && is_adf_scan (s)) {
+ /* some extra effort for precise rounding ... */
+ int overscan = (s->avdimen.hw_yres *
+ (SANE_UNFIX (s->val[OPT_OVERSCAN_TOP].w) +
+ SANE_UNFIX (s->val[OPT_OVERSCAN_BOTTOM].w)) + (MM_PER_INCH - 1)
+ ) / MM_PER_INCH;
+ DBG (3, "sane_compute_parameters: overscan lines: %d\n", overscan);
+ s->avdimen.bry += overscan;
+ }
+
+ /* rear offset compensation */
+ if (s->avdimen.interlaced_duplex && dev->hw->feature_type & AV_REAR_OFFSET) {
+ const double offset = 0.5; /* in current affected models 1/2 inch */
+ s->avdimen.rear_offset = (int) (offset * s->avdimen.hw_yres);
+ DBG (1, "sane_compute_parameters: rear_offset: %d!\n", s->avdimen.rear_offset);
+ /* we do not limit against the bottom-y here, as rear offset always
+ applies to ADF scans, only */
+ }
+ else {
+ s->avdimen.rear_offset = 0;
+ }
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ s->avdimen.hw_pixels_per_line = (s->avdimen.brx - s->avdimen.tlx);
+ s->avdimen.hw_pixels_per_line -= s->avdimen.hw_pixels_per_line % boundary;
+
+ s->avdimen.hw_lines = (s->avdimen.bry - s->avdimen.tly -
+ 2 * s->avdimen.line_difference);
+
+ if (s->avdimen.interlaced_duplex && dev->scanner_type != AV_FILM)
+ s->avdimen.hw_lines -= s->avdimen.hw_lines % dev->read_stripe_size;
+
+ s->params.pixels_per_line = s->avdimen.hw_pixels_per_line * s->avdimen.xres / s->avdimen.hw_xres;
+ s->params.lines = s->avdimen.hw_lines * s->avdimen.xres / s->avdimen.hw_xres;
+ if (is_adf_scan (s))
+ /* we can't know how many lines we'll see with an ADF because that depends on the paper length */
+ s->params.lines = -1;
+ if (s->c_mode == AV_THRESHOLDED || s->c_mode == AV_DITHERED)
+ s->params.pixels_per_line -= s->params.pixels_per_line % 8;
+
+ debug_print_avdimen (1, "sane_compute_parameters", &s->avdimen);
+
+ switch (s->c_mode)
+ {
+ case AV_THRESHOLDED:
+ s->params.format = SANE_FRAME_GRAY;
+ s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line / 8;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ s->params.depth = 1;
+ break;
+ case AV_DITHERED:
+ s->params.format = SANE_FRAME_GRAY;
+ s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line / 8;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ s->params.depth = 1;
+ break;
+ case AV_GRAYSCALE:
+ s->params.format = SANE_FRAME_GRAY;
+ s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ break;
+ case AV_GRAYSCALE12:
+ case AV_GRAYSCALE16:
+ s->params.format = SANE_FRAME_GRAY;
+ s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line * 2;
+ s->params.bytes_per_line = s->params.pixels_per_line * 2;
+ s->params.depth = 16;
+ break;
+ case AV_TRUECOLOR:
+ s->params.format = SANE_FRAME_RGB;
+ s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line * 3;
+ s->params.bytes_per_line = s->params.pixels_per_line * 3;
+ s->params.depth = 8;
+ break;
+ case AV_TRUECOLOR12:
+ case AV_TRUECOLOR16:
+ s->params.format = SANE_FRAME_RGB;
+ s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line * 3 * 2;
+ s->params.bytes_per_line = s->params.pixels_per_line * 3 * 2;
+ s->params.depth = 16;
+ break;
+ default:
+ DBG (1, "Invalid mode. %d\n", s->c_mode);
+ return SANE_STATUS_INVAL;
+ } /* end switch */
+
+ s->params.last_frame = SANE_TRUE;
+
+ debug_print_params (1, "sane_compute_parameters", &s->params);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+inquiry (Avision_Connection av_con, uint8_t* data, size_t len)
+{
+ SANE_Status status;
+ command_header inquiry;
+ int try = 2;
+
+ DBG (3, "inquiry: length: %ld\n", (long)len);
+
+ memset (&inquiry, 0, sizeof(inquiry));
+ inquiry.opc = AVISION_SCSI_INQUIRY;
+ inquiry.len = len;
+
+ do {
+ size_t size = inquiry.len;
+
+ DBG (3, "inquiry: inquiring ...\n");
+ status = avision_cmd (&av_con, &inquiry, sizeof (inquiry), 0, 0,
+ data, &size);
+ if (status == SANE_STATUS_GOOD && size == inquiry.len)
+ break;
+
+ DBG (1, "inquiry: inquiry failed (%s)\n", sane_strstatus (status));
+ --try;
+ } while (try > 0);
+
+ return status;
+}
+
+static SANE_Status
+wait_ready (Avision_Connection* av_con, int delay)
+{
+ SANE_Status status;
+ int try;
+
+ for (try = 0; try < 10; ++ try)
+ {
+ DBG (3, "wait_ready: sending TEST_UNIT_READY\n");
+ status = avision_cmd (av_con, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0, 0, 0);
+ sleep (delay);
+
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (1, "wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ break;
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ DBG (1, "wait_ready: timed out after %d attempts\n", try);
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+wait_4_light (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+
+ /* read stuff */
+ struct command_read rcmd;
+ char* light_status[] =
+ { "off", "on", "warming up", "needs warm up test",
+ "light check error", "RESERVED" };
+
+ SANE_Status status;
+ uint8_t result;
+ int try;
+ size_t size = 1;
+
+ DBG (3, "wait_4_light: getting light status.\n");
+
+ memset (&rcmd, 0, sizeof (rcmd));
+
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0xa0; /* get light status */
+ set_double (rcmd.datatypequal, dev->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ for (try = 0; try < 90; ++ try) {
+
+ DBG (5, "wait_4_light: read bytes %lu\n", (u_long) size);
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, &result, &size);
+
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "wait_4_light: read failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ DBG (3, "wait_4_light: command is %d. Result is %s\n",
+ status, light_status[(result>4)?5:result]);
+
+ if (result == 1) {
+ return SANE_STATUS_GOOD;
+ }
+ else if (dev->hw->feature_type & AV_LIGHT_CHECK_BOGUS) {
+ DBG (3, "wait_4_light: scanner marked as returning bogus values in device-list!!\n");
+ return SANE_STATUS_GOOD;
+ }
+ else {
+ struct command_send scmd;
+ uint8_t light_on = 1;
+
+ /* turn on the light */
+ DBG (3, "wait_4_light: setting light status.\n");
+
+ memset (&scmd, 0, sizeof (scmd));
+
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = 0xa0; /* send light status */
+ set_double (scmd.datatypequal, dev->data_dq);
+ set_triple (scmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ &light_on, sizeof (light_on), 0, 0);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "wait_4_light: send failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+ }
+ sleep (1);
+ }
+
+ DBG (1, "wait_4_light: timed out after %d attempts\n", try);
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+set_power_save_time (Avision_Scanner* s, int time)
+{
+ struct {
+ struct command_send cmd;
+ uint8_t time[2];
+ } scmd;
+
+ Avision_Device* dev = s->hw;
+ SANE_Status status;
+
+ DBG (3, "set_power_save_time: time %d\n", time);
+
+ memset (&scmd, 0, sizeof (scmd));
+ scmd.cmd.opc = AVISION_SCSI_SEND;
+ scmd.cmd.datatypecode = 0xA2; /* power-saving timer */
+ set_double (scmd.cmd.datatypequal, dev->data_dq);
+ set_triple (scmd.cmd.transferlen, sizeof (scmd.time) );
+
+ set_double (scmd.time, time);
+
+ status = avision_cmd (&s->av_con, &scmd.cmd, sizeof (scmd.cmd),
+ &scmd.time, sizeof (scmd.time), 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "set_power_save_time: send_data (%s)\n", sane_strstatus (status));
+ return status;
+}
+
+static SANE_Status
+get_firmware_status (Avision_Connection* av_con)
+{
+ /* read stuff */
+ struct command_read rcmd;
+ size_t size;
+ SANE_Status status;
+
+ firmware_status result;
+
+ DBG (3, "get_firmware_status\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0x90; /* firmware status */
+ set_double (rcmd.datatypequal, 0); /* dev->data_dq not available */
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (av_con, &rcmd, sizeof (rcmd), 0, 0, &result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "get_firmware_status: read failed (%s)\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ debug_print_raw (6, "get_firmware_status: raw data:\n", (uint8_t*)&result, size);
+
+ DBG (3, "get_firmware_status: [0] needs firmware %x\n", result.download_firmware);
+ DBG (3, "get_firmware_status: [1] side edge: %d\n", get_double ( result.first_effective_pixel_flatbed ));
+ DBG (3, "get_firmware_status: [3] side edge: %d\n", get_double ( result.first_effective_pixel_adf_front ));
+ DBG (3, "get_firmware_status: [5] side edge: %d\n", get_double ( result.first_effective_pixel_adf_rear ));
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_flash_ram_info (Avision_Connection* av_con)
+{
+ /* read stuff */
+ struct command_read rcmd;
+ size_t size;
+ SANE_Status status;
+ uint8_t result[40];
+
+ DBG (3, "get_flash_ram_info\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0x6a; /* flash ram information */
+ set_double (rcmd.datatypequal, 0); /* dev->data_dq not available */
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "get_flash_ram_info: read failed (%s)\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ debug_print_raw (6, "get_flash_ram_info: raw data:\n", result, size);
+
+ DBG (3, "get_flash_ram_info: [0] data type %x\n", result [0]);
+ DBG (3, "get_flash_ram_info: [1] Ability1:%s%s%s%s%s%s%s%s\n",
+ BIT(result[1],7)?" RESERVED_BIT7":"",
+ BIT(result[1],6)?" RESERVED_BIT6":"",
+ BIT(result[1],5)?" FONT(r/w)":"",
+ BIT(result[1],4)?" FPGA(w)":"",
+ BIT(result[1],3)?" FMDBG(r)":"",
+ BIT(result[1],2)?" RAWLINE(r)":"",
+ BIT(result[1],1)?" FIRMWARE(r/w)":"",
+ BIT(result[1],0)?" CTAB(r/w)":"");
+
+ DBG (3, "get_flash_ram_info: [2-5] size CTAB: %d\n",
+ get_quad ( &(result[2]) ) );
+
+ DBG (3, "get_flash_ram_info: [6-9] size FIRMWARE: %d\n",
+ get_quad ( &(result[6]) ) );
+
+ DBG (3, "get_flash_ram_info: [10-13] size RAWLINE: %d\n",
+ get_quad ( &(result[10]) ) );
+
+ DBG (3, "get_flash_ram_info: [14-17] size FMDBG: %d\n",
+ get_quad ( &(result[14]) ) );
+
+ DBG (3, "get_flash_ram_info: [18-21] size FPGA: %d\n",
+ get_quad ( &(result[18]) ) );
+
+ DBG (3, "get_flash_ram_info: [22-25] size FONT: %d\n",
+ get_quad ( &(result[22]) ) );
+
+ DBG (3, "get_flash_ram_info: [26-29] size RESERVED: %d\n",
+ get_quad ( &(result[26]) ) );
+
+ DBG (3, "get_flash_ram_info: [30-33] size RESERVED: %d\n",
+ get_quad ( &(result[30]) ) );
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_nvram_data (Avision_Scanner* s, nvram_data* nvram)
+{
+ /* read stuff */
+ struct command_send rcmd;
+
+ size_t size;
+ SANE_Status status;
+
+ DBG (3, "get_nvram_data\n");
+
+ size = sizeof (*nvram);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ memset (nvram, 0, size);
+
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0x69; /* Read NVM RAM data */
+ set_double (rcmd.datatypequal, 0); /* dev->data_dq not available */
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0,
+ nvram, &size);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_nvram_data: read failed (%s)\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ debug_print_nvram_data (5, "get_nvram_data", nvram);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_and_parse_nvram (Avision_Scanner* s, char* str, int n)
+{
+ SANE_Status status;
+ int i = 0;
+ int x;
+ nvram_data nvram;
+ uint8_t inquiry_result [AVISION_INQUIRY_SIZE_V1];
+
+ status = inquiry (s->av_con, inquiry_result, sizeof(inquiry_result));
+ if (status == SANE_STATUS_GOOD) {
+ i += snprintf (str+i, n-i, "Vendor: %.8s",
+ inquiry_result+8);
+ i += snprintf (str+i, n-i, "\nModel: %.16s",
+ inquiry_result+16);
+ i += snprintf (str+i, n-i, "\nFirmware: %.4s",
+ inquiry_result+32);
+ }
+
+ if (!s->hw->inquiry_nvram_read)
+ return SANE_STATUS_GOOD;
+
+ status = get_nvram_data (s, &nvram);
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (nvram.serial[0])
+ i += snprintf (str+i, n-i, "\nSerial: %.24s",
+ nvram.serial);
+
+ if (nvram.born_year)
+ i += snprintf (str+i, n-i, "\nManufacturing date: %d-%d-%d",
+ get_double(nvram.born_year),
+ get_double(nvram.born_month),
+ get_double(nvram.born_day));
+ if (nvram.first_scan_year)
+ i += snprintf (str+i, n-i, "\nFirst scan date: %d-%d-%d",
+ get_double(nvram.first_scan_year),
+ get_double(nvram.first_scan_month),
+ get_double(nvram.first_scan_day));
+
+ x = get_quad (nvram.flatbed_scans);
+ if (x)
+ i += snprintf (str+i, n-i, "\nFlatbed scans: %d", x);
+ x = get_quad (nvram.pad_scans);
+ if (x)
+ i += snprintf (str+i, n-i, "\nPad scans: %d", x);
+ x = get_quad (nvram.adf_simplex_scans);
+ if (x)
+ i += snprintf (str+i, n-i, "\nADF simplex scans: %d", x);
+ x = get_quad (nvram.adf_duplex_scans);
+ if (x)
+ i += snprintf (str+i, n-i, "\nADF duplex scans: %d", x);
+ }
+
+ return status;
+}
+
+static SANE_Status
+get_power_save_time (Avision_Scanner* s, SANE_Word* time)
+{
+ SANE_Status status;
+ nvram_data nvram;
+
+ DBG (3, "get_power_save_time\n");
+
+ if (!s->hw->inquiry_nvram_read)
+ return SANE_STATUS_INVAL;
+
+ status = get_nvram_data (s, &nvram);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_power_save_time: read nvram failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ *time = get_double (nvram.power_saving_time);
+
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef NEEDED
+
+static SANE_Status
+send_nvram_data (Avision_Connection* av_con)
+{
+ /* read stuff */
+ struct command_send scmd;
+ size_t size;
+ SANE_Status status;
+
+ DBG (3, "send_nvram_data\n");
+
+ size = sizeof (c7_nvram);
+
+ memset (&scmd, 0, sizeof (scmd));
+ scmd.opc = AVISION_SCSI_SEND;
+
+ scmd.datatypecode = 0x85; /* nvram data */
+ set_double (scmd.datatypequal, 0); /* dev->data_dq not available */
+ set_triple (scmd.transferlen, size);
+
+ status = avision_cmd (av_con, &scmd, sizeof (scmd), &c7_nvram, size,
+ 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "send_nvram_data: send failed (%s)\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+send_flash_ram_data (Avision_Connection* av_con)
+{
+ /* read stuff */
+ struct command_send scmd;
+ size_t size;
+ SANE_Status status;
+
+ DBG (3, "send_flash_ram_data\n");
+
+ size = sizeof (c7_flash_ram);
+
+ memset (&scmd, 0, sizeof (scmd));
+ scmd.opc = AVISION_SCSI_SEND;
+
+ scmd.datatypecode = 0x86; /* flash data */
+ set_double (scmd.datatypequal, 0);
+ set_triple (scmd.transferlen, size);
+
+ status = avision_cmd (av_con, &scmd, sizeof (scmd), &c7_flash_ram, size,
+ 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "send_flash_ram_data: send failed (%s)\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+
+static SANE_Status
+adf_reset (Avision_Scanner* s)
+{
+ SANE_Status status;
+ Avision_Device* dev = s->hw;
+ struct command_send scmd;
+ struct command_read rcmd;
+ uint8_t payload[4];
+ size_t size;
+ size_t n;
+ int i;
+ DBG (3, "adf_reset\n");
+
+ /* loop twice */
+ for (i=1; i >= 0; i--) {
+ n=i;
+ memset (&scmd, 0, sizeof (scmd));
+ memset (&payload, 0, sizeof (payload));
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = 0xD0; /* unknown */
+ set_double (scmd.datatypequal, 0);
+ size = 2;
+ set_triple (scmd.transferlen, size);
+ payload[1] = 0x10 * i; /* write 0x10 the first time, 0x00 the second */
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), payload, size, 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "adf_reset: write %d failed (%s)\n", (2-i),
+ sane_strstatus (status));
+ return (status);
+ }
+ DBG (3, "adf_reset: write %d complete.\n", (2-i));
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ memset (&payload, 0, sizeof (payload));
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0x69; /* Read NVRAM data */
+ set_double (rcmd.datatypequal, dev->data_dq);
+ size = 4 - i; /* read 3 bytes the first time, 4 the second */
+ set_triple (rcmd.transferlen, size);
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, payload, &size);
+ if (status != SANE_STATUS_GOOD || size != (4-n)) {
+ DBG (1, "adf_reset: read %lu failed (%s)\n", (2-n),
+ sane_strstatus (status));
+ return (status);
+ }
+ debug_print_raw (3, "adf_reset: raw data:\n", payload, size);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+get_accessories_info (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+ int try = 3;
+
+ /* read stuff */
+ struct command_read rcmd;
+ size_t size;
+ SANE_Status status;
+ uint8_t result[8];
+
+ char* adf_model[] =
+ { "Origami", "Oodles", "HP9930", "unknown" };
+ const int adf_models = sizeof (adf_model) / sizeof(char*) - 1;
+
+ DBG (3, "get_accessories_info\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0x64; /* detect accessories */
+ set_double (rcmd.datatypequal, dev->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ /* after resetting the ADF unit, try reprobing it again */
+ RETRY:
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "get_accessories_info: read failed (%s)\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ debug_print_raw (6, "get_accessories_info: raw data:\n", result, size);
+
+ DBG (3, "get_accessories_info: [0] ADF: %x\n", result[0]);
+ DBG (3, "get_accessories_info: [1] Light Box: %x\n", result[1]);
+
+ DBG (3, "get_accessories_info: [2] ADF model: %d (%s)\n",
+ result [2],
+ adf_model[ (result[2] < adf_models) ? result[2] : adf_models ]);
+
+ dev->inquiry_adf |= result [0];
+
+ if (dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX)
+ {
+ if (result[0] == 1)
+ {
+ dev->inquiry_duplex = 1;
+ dev->inquiry_duplex_interlaced = 0;
+ } else if (result[0] == 0 && result[2] != 0) {
+ /* Sometimes the scanner will report that there is no ADF attached, yet
+ * an ADF model number will still be reported. This happens on the
+ * HP8200 series and possibly others. In this case we need to reset the
+ * the adf and try reading it again.
+ */
+ DBG (3, "get_accessories_info: Found ADF model number but the ADF-present flag is not set. Trying to recover...\n");
+ status = adf_reset (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (3, "get_accessories_info: Failed to reset ADF: %s\n", sane_strstatus (status));
+ return status;
+ }
+ DBG (1, "get_accessories_info: Waiting while ADF firmware resets...\n");
+ sleep(3);
+ status = wait_ready (&s->av_con, 1);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_accessories_info: wait_ready() failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ if (try) {
+ try--;
+ goto RETRY;
+ }
+ DBG (1, "get_accessories_info: Maximum retries attempted, ADF unresponsive.\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* only honor a 1, some scanner without adapter set 0xff */
+ if (result[1] == 1)
+ dev->inquiry_light_box = 1;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Returns a pointer to static char* strings or NULL for cancel (we do
+ not want to start memcmp'ing for the cancel case). */
+static const char*
+string_for_button (Avision_Scanner* s, int button)
+{
+ static char buffer [16];
+ Avision_Device* dev = s->hw;
+
+ /* dev->sane.model
+ dev->inquiry_asic_type */
+
+ if (dev->inquiry_buttons == 1)
+ goto return_scan;
+
+ /* simplex / duplex buttons */
+ if (strcmp (dev->sane.vendor, "Xerox") == 0 ||
+ strcmp (dev->sane.vendor, "Visioneer") == 0 ||
+ strcmp (dev->sane.model, "AV121") == 0 ||
+ strcmp (dev->sane.model, "AV122") == 0
+ )
+ {
+ switch (button)
+ {
+ case 1: return "simplex";
+ case 2: return "duplex";
+ }
+ }
+
+ if (strcmp (dev->sane.model, "AV210C2") == 0 ||
+ strcmp (dev->sane.model, "AV210D2+") == 0 ||
+ strcmp (dev->sane.model, "AV220C2") == 0 ||
+ strcmp (dev->sane.model, "AV610C2") == 0
+ )
+ {
+ if (button == 1)
+ return NULL; /* cancel */
+ else
+ goto return_scan;
+ }
+
+ /* those are unique, right now */
+ if (strcmp (dev->sane.model, "AV610") == 0)
+ {
+ switch (button)
+ {
+ case 0: return "email";
+ case 1: return "copy";
+ case 2: return "scan";
+ }
+ }
+
+ /* last resort */
+ snprintf (buffer, sizeof (buffer), "button%d", button);
+ return buffer;
+
+ return_scan:
+ return "scan";
+}
+
+static SANE_Status
+get_button_status (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+
+ /* read stuff */
+ struct command_read rcmd;
+ size_t size;
+ SANE_Status status;
+ /* was only 6 in an old SPEC - maybe we need a feature override :-( -ReneR */
+ struct {
+ uint8_t press_state;
+ uint8_t buttons[5];
+ uint8_t display; /* AV220 et.al. 7 segment LED display */
+ uint8_t reserved[9];
+ } result;
+
+ unsigned int i;
+
+ DBG (3, "get_button_status:\n");
+
+ size = sizeof (result);
+
+ /* AV220 et.al. */
+ if (! (dev->hw->feature_type & AV_INT_BUTTON))
+ {
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0xA1; /* button status */
+ set_double (rcmd.datatypequal, dev->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0,
+ (uint8_t*)&result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "get_button_status: read failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+ }
+ else
+ {
+ /* only try to read the first 8 bytes ...*/
+ size = 8;
+
+ /* no SCSI equivalent */
+ /* either there was a button press and this completes quickly
+ or there is no point waiting for a future press */
+ sanei_usb_set_timeout (100); /* 10th of a second */
+ DBG (5, "==> (interrupt read) going down ...\n");
+ status = sanei_usb_read_int (s->av_con.usb_dn, (uint8_t*)&result,
+ &size);
+ DBG (5, "==> (interrupt read) got: %ld\n", (long)size);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_button_status: interrupt read failed (%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_GOOD;
+ }
+
+ if (size < sizeof (result))
+ memset ((char*)result.buttons + size, 0, sizeof (result) - size);
+
+ /* hack to fill in meaningful values for the AV 210 / 610 and
+ under some conditions the AV 220 */
+ if (size == 1) { /* AV 210, AV 610 */
+ DBG (1, "get_button_status: just one byte, filling the rest\n");
+
+ if (result.press_state > 0) {
+ debug_print_raw (6, "get_button_status: raw data\n",
+ (uint8_t*)&result, size);
+ result.buttons[0] = result.press_state;
+ result.press_state = 0x80 | 1;
+ size = 2;
+ }
+ else /* nothing pressed */
+ return SANE_STATUS_GOOD;
+ }
+ else if (size >= 8 && result.press_state == 0) { /* AV 220 */
+
+ debug_print_raw (6, "get_button_status: raw data\n",
+ (uint8_t*)&result, size);
+
+ DBG (1, "get_button_status: zero buttons - filling values ...\n");
+
+ /* simulate button press of the last button ... */
+ result.press_state = 0x80 | 1;
+ result.buttons[0] = dev->inquiry_buttons; /* 1 based */
+ }
+ }
+
+ debug_print_raw (6, "get_button_status: raw data\n",
+ (uint8_t*)&result, size);
+
+ DBG (3, "get_button_status: [0] Button status: %x\n", result.press_state);
+ for (i = 0; i < 5; ++i)
+ DBG (3, "get_button_status: [%d] Button number %d: %x\n", i+1, i,
+ result.buttons[i]);
+ DBG (3, "get_button_status: [7] Display: %d\n", result.display);
+
+ {
+ char* message_begin = s->val[OPT_MESSAGE].s;
+ char* message_end = s->val[OPT_MESSAGE].s + s->opt[OPT_MESSAGE].size;
+ char* message = message_begin;
+
+#define add_token(format,value) do { \
+ int n = snprintf (message, message_end - message, "%s" format, \
+ message == message_begin ? "" : ":", value); \
+ message += n > 0 ? n : 0; \
+ } while (0)
+
+ if (result.display > 0)
+ add_token ("%d", result.display);
+
+ if (result.press_state >> 7) /* AV220 et.al. bit 6 is long/short press? */
+ {
+
+ const unsigned int buttons_pressed = result.press_state & 0x7F;
+ DBG (3, "get_button_status: %d button(s) pressed\n", buttons_pressed);
+
+ /* reset the hardware button status */
+ if (! (dev->hw->feature_type & AV_INT_BUTTON))
+ {
+ struct command_send scmd;
+ uint8_t button_reset = 1;
+
+ DBG (3, "get_button_status: resetting status\n");
+
+ memset (&scmd, 0, sizeof (scmd));
+
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = 0xA1; /* button control */
+ set_double (scmd.datatypequal, dev->data_dq);
+ set_triple (scmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ &button_reset, sizeof (button_reset), 0, 0);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_button_status: send failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ for (i = 0; i < buttons_pressed; ++i) {
+ const unsigned int button = result.buttons[i] - 1; /* 1 based ... */
+ DBG (3, "get_button_status: button %d pressed\n", button);
+ if (button >= dev->inquiry_buttons) {
+ DBG (1, "get_button_status: button %d not allocated as not indicated in inquiry\n",
+ button);
+ }
+ else {
+ const char* label = string_for_button (s, button);
+ if (label)
+ add_token ("%s", label);
+ else
+ return SANE_STATUS_CANCELLED;
+ }
+ }
+ }
+ else
+ DBG (3, "get_button_status: no button pressed\n");
+ }
+
+ return SANE_STATUS_GOOD;
+#undef add_token
+}
+
+static SANE_Status
+get_frame_info (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+
+ /* read stuff */
+ struct command_read rcmd;
+ size_t size;
+ SANE_Status status;
+ uint8_t result[8];
+ size_t i;
+
+ DBG (3, "get_frame_info:\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0x87; /* film holder sense */
+ set_double (rcmd.datatypequal, dev->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "get_frame_info: read failed (%s)\n", sane_strstatus (status));
+ return (status);
+ }
+
+ debug_print_raw (6, "get_frame_info: raw data\n", result, size);
+
+ DBG (3, "get_frame_info: [0] Holder type: %s\n",
+ (result[0]==1)?"APS":
+ (result[0]==2)?"Film holder (35mm)":
+ (result[0]==3)?"Slide holder":
+ (result[0]==0xff)?"Empty":"unknown");
+ DBG (3, "get_frame_info: [1] Current frame number: %d\n", result[1]);
+ DBG (3, "get_frame_info: [2] Frame amount: %d\n", result[2]);
+ DBG (3, "get_frame_info: [3] Mode: %s\n", BIT(result[3],4)?"APS":"Not APS");
+ DBG (3, "get_frame_info: [3] Exposures (if APS): %s\n",
+ ((i=(BIT(result[3],3)<<1)+BIT(result[2],2))==0)?"Unknown":
+ (i==1)?"15":(i==2)?"25":"40");
+ DBG (3, "get_frame_info: [3] Film Type (if APS): %s\n",
+ ((i=(BIT(result[1],3)<<1)+BIT(result[0],2))==0)?"Unknown":
+ (i==1)?"B&W Negative":(i==2)?"Color slide":"Color Negative");
+
+ dev->holder_type = result[0];
+ dev->current_frame = result[1];
+
+ dev->frame_range.min = 1;
+ dev->frame_range.quant = 1;
+
+ if (result[0] != 0xff)
+ dev->frame_range.max = result[2];
+ else
+ dev->frame_range.max = 1;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_duplex_info (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+
+ /* read stuff */
+ struct command_read rcmd;
+
+ struct {
+ uint8_t mode;
+ uint8_t color_line_difference[2];
+ uint8_t gray_line_difference[2];
+ uint8_t lineart_line_difference[2];
+ uint8_t image_info;
+ } result;
+
+ size_t size;
+ SANE_Status status;
+
+ DBG (3, "get_duplex_info:\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+
+ rcmd.datatypecode = 0xB1; /* read duplex info */
+ set_double (rcmd.datatypequal, dev->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0,
+ &result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result)) {
+ DBG (1, "get_duplex_info: read failed (%s)\n", sane_strstatus (status));
+ return (status);
+ }
+
+ debug_print_raw (6, "get_duplex_info: raw data\n", (uint8_t*)&result, size);
+
+ DBG (3, "get_duplex_info: [0] Mode: %s%s\n",
+ BIT(result.mode,0)?"MERGED_PAGES":"",
+ BIT(result.mode,1)?"2ND_PAGE_FOLLOWS":"");
+ DBG (3, "get_duplex_info: [1-2] Color line difference: %d\n",
+ get_double(result.color_line_difference));
+ DBG (3, "get_duplex_info: [3-4] Gray line difference: %d\n",
+ get_double(result.gray_line_difference));
+ DBG (3, "get_duplex_info: [5-6] Lineart line difference: %d\n",
+ get_double(result.lineart_line_difference));
+ /* isn't this supposed to be result.info ?!? */
+ DBG (3, "get_duplex_info: [7] Mode: %s%s%s%s\n",
+ BIT(result.image_info,0)?" FLATBED_BGR":" FLATBED_RGB",
+ BIT(result.image_info,1)?" ADF_BGR":" ADF_RGB",
+ BIT(result.image_info,2)?" FLATBED_NEEDS_MIRROR_IMAGE":"",
+ BIT(result.image_info,3)?" ADF_NEEDS_MIRROR_IMAGE":"");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+set_frame (Avision_Scanner* s, SANE_Word frame)
+{
+ struct {
+ struct command_send cmd;
+ uint8_t data[8];
+ } scmd;
+
+ Avision_Device* dev = s->hw;
+ SANE_Status status;
+
+ DBG (3, "set_frame: request frame %d\n", frame);
+
+ /* Better check the current status of the film holder, because it
+ can be changed between scans. */
+ status = get_frame_info (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* No film holder? */
+ if (dev->holder_type == 0xff) {
+ DBG (1, "set_frame: No film holder!!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Requesting frame 0xff indicates eject/rewind */
+ if (frame != 0xff && (frame < 1 || frame > dev->frame_range.max) ) {
+ DBG (1, "set_frame: Illegal frame (%d) requested (min=1, max=%d)\n",
+ frame, dev->frame_range.max);
+ return SANE_STATUS_INVAL;
+ }
+
+ memset (&scmd, 0, sizeof (scmd));
+ scmd.cmd.opc = AVISION_SCSI_SEND;
+ scmd.cmd.datatypecode = 0x87; /* send film holder "sense" */
+ set_double (scmd.cmd.datatypequal, dev->data_dq);
+ set_triple (scmd.cmd.transferlen, sizeof (scmd.data) );
+
+ scmd.data[0] = dev->holder_type;
+ scmd.data[1] = frame;
+
+ status = avision_cmd (&s->av_con, &scmd.cmd, sizeof (scmd.cmd),
+ &scmd.data, sizeof (scmd.data), 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "set_frame: send_data (%s)\n", sane_strstatus (status));
+ }
+
+ return status;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, Avision_ConnectionType con_type,
+ Avision_Device** devp)
+{
+ uint8_t result [AVISION_INQUIRY_SIZE_MAX];
+ int model_num;
+
+ Avision_Device* dev;
+ SANE_Status status;
+
+ Avision_Connection av_con;
+
+ char mfg [9];
+ char model [17];
+ char rev [5];
+
+ unsigned int i;
+ char* s;
+ SANE_Bool found;
+
+ DBG (3, "attach:\n");
+ memset (result, 0, sizeof(result));
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0) {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ av_con.connection_type = con_type;
+ if (av_con.connection_type == AV_USB)
+ av_con.usb_status = AVISION_USB_UNTESTED_STATUS;
+
+ /* set known USB status type */
+ if (attaching_hw && attaching_hw->feature_type & AV_INT_STATUS)
+ av_con.usb_status = AVISION_USB_INT_STATUS;
+
+ DBG (3, "attach: opening %s\n", devname);
+ status = avision_open (devname, &av_con, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "attach: open failed (%s)\n", sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ /* first: get the standard inquiry? */
+ status = inquiry (av_con, result, AVISION_INQUIRY_SIZE_V1);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "attach: 1st inquiry failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ /* copy string information - and zero terminate them c-style */
+ memcpy (&mfg, result + 8, 8);
+ mfg [8] = 0;
+ memcpy (&model, result + 16, 16);
+ model [16] = 0;
+ memcpy (&rev, result + 32, 4);
+ rev [4] = 0;
+
+ /* shorten strings (-1 for last index
+ -1 for last 0; >0 because one char at least) */
+ for (i = sizeof (mfg) - 2; i > 0; i--) {
+ if (mfg[i] == 0x20)
+ mfg[i] = 0;
+ else
+ break;
+ }
+ for (i = sizeof (model) - 2; i > 0; i--) {
+ if (model[i] == 0x20)
+ model[i] = 0;
+ else
+ break;
+ }
+
+ DBG (1, "attach: Inquiry gives mfg=%s, model=%s, product revision=%s.\n",
+ mfg, model, rev);
+
+ model_num = 0;
+ found = 0;
+ /* while not at at end of list NULL terminator */
+ while (Avision_Device_List[model_num].real_mfg != NULL ||
+ Avision_Device_List[model_num].scsi_mfg != NULL)
+ {
+ int matches = 0, match_count = 0; /* count number of matches */
+ DBG (1, "attach: Checking model: %d\n", model_num);
+
+ if (Avision_Device_List[model_num].scsi_mfg) {
+ ++match_count;
+ if (strcmp(mfg, Avision_Device_List[model_num].scsi_mfg) == 0)
+ ++matches;
+ }
+ if (Avision_Device_List[model_num].scsi_model) {
+ ++match_count;
+ if (strcmp(model, Avision_Device_List[model_num].scsi_model) == 0)
+ ++matches;
+ }
+
+ /* we need 2 matches (mfg, model) for SCSI entries, or the ones available
+ for "we know what we are looking for" USB entries */
+ if ((attaching_hw == &(Avision_Device_List [model_num]) &&
+ matches == match_count) ||
+ matches == 2)
+ {
+ DBG (1, "attach: Scanner matched entry: %d: \"%s\", \"%s\", 0x%.4x, 0x%.4x\n",
+ model_num,
+ Avision_Device_List[model_num].scsi_mfg,
+ Avision_Device_List[model_num].scsi_model,
+ Avision_Device_List[model_num].usb_vendor,
+ Avision_Device_List[model_num].usb_product);
+ found = 1;
+ break;
+ }
+ ++model_num;
+ }
+
+ if (!found) {
+ DBG (0, "attach: \"%s\" - \"%s\" not yet in whitelist!\n", mfg, model);
+ DBG (0, "attach: You might want to report this output.\n");
+ DBG (0, "attach: To: mike@piratehaven.org (the Avision backend maintainer)\n");
+
+ status = SANE_STATUS_INVAL;
+ goto close_scanner_and_return;
+ }
+
+ /* second: maybe ask for the firmware status and flash ram info */
+ if (Avision_Device_List [model_num].feature_type2 & AV_FIRMWARE)
+ {
+ DBG (3, "attach: reading firmware status\n");
+ status = get_firmware_status (&av_con);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "attach: get firmware status failed (%s)\n",
+ sane_strstatus (status));
+ goto close_scanner_and_return;
+ }
+
+ DBG (3, "attach: reading flash ram info\n");
+ status = get_flash_ram_info (&av_con);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "attach: get flash ram info failed (%s)\n",
+ sane_strstatus (status));
+ goto close_scanner_and_return;
+ }
+
+#ifdef FIRMWARE_DATABASE_INCLUDED
+ /* Send new NV-RAM (firmware) data */
+ status = send_nvram_data (&av_con);
+ if (status != SANE_STATUS_GOOD)
+ goto close_scanner_and_return;
+#endif
+ }
+
+ /* third: get the extended Avision inquiry */
+ status = inquiry (av_con, result, AVISION_INQUIRY_SIZE_V1);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "attach: avision v1 inquiry failed (%s)\n", sane_strstatus (status));
+ goto close_scanner_and_return;
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev) {
+ status = SANE_STATUS_NO_MEM;
+ goto close_scanner_and_return;
+ }
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->hw = &Avision_Device_List[model_num];
+
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor = dev->hw->real_mfg ? dev->hw->real_mfg : strdup (mfg);
+ dev->sane.model = dev->hw->real_model ? dev->hw->real_model : strdup (model);
+ dev->connection.connection_type = av_con.connection_type;
+ dev->connection.usb_status = av_con.usb_status;
+
+ /* and finally Avision even extended this one later on
+ the AV220C2 does not grok this */
+ dev->inquiry_asic_type = (int) result[91];
+ if (dev->inquiry_asic_type == AV_ASIC_C6)
+ {
+ status = inquiry (av_con, result, AVISION_INQUIRY_SIZE_V2);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "attach: avision v2 inquiry failed (%s)\n", sane_strstatus (status));
+ goto close_scanner_and_return;
+ }
+ }
+
+ debug_print_raw (6, "attach: raw data:\n", result, sizeof (result) );
+
+ DBG (3, "attach: [8-15] Vendor id.: '%8.8s'\n", result+8);
+ DBG (3, "attach: [16-31] Product id.: '%16.16s'\n", result+16);
+ DBG (3, "attach: [32-35] Product rev.: '%4.4s'\n", result+32);
+
+ i = (result[36] >> 4) & 0x7;
+ switch (result[36] & 0x07) {
+ case 0:
+ s = " RGB"; break;
+ case 1:
+ s = " BGR"; break;
+ default:
+ s = " unknown (RESERVED)";
+ }
+ DBG (3, "attach: [36] Bitfield:%s%s%s%s%s%s%s color plane\n",
+ BIT(result[36],7)?" ADF":"",
+ (i==0)?" B&W only":"",
+ BIT(i, 1)?" 3-pass color":"",
+ BIT(i, 2)?" 1-pass color":"",
+ BIT(i, 2) && BIT(i, 0) ?" 1-pass color (ScanPartner only)":"",
+ BIT(result[36],3)?" IS_NOT_FLATBED":"",
+ s);
+
+ DBG (3, "attach: [37] Optical res.: %d00 dpi\n", result[37]);
+ DBG (3, "attach: [38] Maximum res.: %d00 dpi\n", result[38]);
+
+ DBG (3, "attach: [39] Bitfield1:%s%s%s%s%s%s\n",
+ BIT(result[39],7)?" TRANS":"",
+ BIT(result[39],6)?" Q_SCAN":"",
+ BIT(result[39],5)?" EXTENDED_RES":"",
+ BIT(result[39],4)?" SUPPORTS_CALIB":"",
+ BIT(result[39],2)?" NEW_PROTOCOL":"",
+ (result[39] & 0x03) == 0x03 ? " AVISION":" OEM");
+
+ DBG (3, "attach: [40-41] X res. in gray: %d dpi\n",
+ get_double ( &(result[40]) ));
+ DBG (3, "attach: [42-43] Y res. in gray: %d dpi\n",
+ get_double ( &(result[42]) ));
+ DBG (3, "attach: [44-45] X res. in color: %d dpi\n",
+ get_double ( &(result[44]) ));
+ DBG (3, "attach: [46-47] Y res. in color: %d dpi\n",
+ get_double ( &(result[46]) ));
+ DBG (3, "attach: [48-49] USB max read: %d\n",
+get_double ( &(result[48] ) ));
+
+ DBG (3, "attach: [50] ESA1:%s%s%s%s%s%s%s%s\n",
+ BIT(result[50],7)?" LIGHT_CONTROL":"",
+ BIT(result[50],6)?" BUTTON_CONTROL":"",
+ BIT(result[50],5)?" NEED_SW_COLORPACK":"",
+ BIT(result[50],4)?" SW_CALIB":"",
+ BIT(result[50],3)?" NEED_SW_GAMMA":"",
+ BIT(result[50],2)?" KEEPS_GAMMA":"",
+ BIT(result[50],1)?" KEEPS_WINDOW_CMD":"",
+ BIT(result[50],0)?" XYRES_DIFFERENT":"");
+ DBG (3, "attach: [51] ESA2:%s%s%s%s%s%s%s%s\n",
+ BIT(result[51],7)?" EXPOSURE_CTRL":"",
+ BIT(result[51],6)?" NEED_SW_TRIGGER_CAL":"",
+ BIT(result[51],5)?" NEED_WHITE_PAPER_CALIB":"",
+ BIT(result[51],4)?" SUPPORTS_QUALITY_SPEED_CAL":"",
+ BIT(result[51],3)?" NEED_TRANSP_CAL":"",
+ BIT(result[51],2)?" HAS_PUSH_BUTTON":"",
+ BIT(result[51],1)?" NEW_CAL_METHOD_3x3_MATRIX_(NO_GAMMA_TABLE)":"",
+ BIT(result[51],0)?" ADF_MIRRORS_IMAGE":"");
+ DBG (3, "attach: [52] ESA3:%s%s%s%s%s%s%s%s\n",
+ BIT(result[52],7)?" GRAY_WHITE":"",
+ BIT(result[52],6)?" SUPPORTS_GAIN_CONTROL":"",
+ BIT(result[52],5)?" SUPPORTS_TET":"", /* "Text Enhanced Technology" */
+ BIT(result[52],4)?" 3x3COL_TABLE":"",
+ BIT(result[52],3)?" 1x3FILTER":"",
+ BIT(result[52],2)?" INDEX_COLOR":"",
+ BIT(result[52],1)?" POWER_SAVING_TIMER":"",
+ BIT(result[52],0)?" NVM_DATA_REC":"");
+
+ /* print some more scanner features/params */
+ DBG (3, "attach: [53] line difference (software color pack): %d\n", result[53]);
+ DBG (3, "attach: [54] color mode pixel boundary: %d\n", result[54]);
+ DBG (3, "attach: [55] gray mode pixel boundary: %d\n", result[55]);
+ DBG (3, "attach: [56] 4bit gray mode pixel boundary: %d\n", result[56]);
+ DBG (3, "attach: [57] lineart mode pixel boundary: %d\n", result[57]);
+ DBG (3, "attach: [58] halftone mode pixel boundary: %d\n", result[58]);
+ DBG (3, "attach: [59] error-diffusion mode pixel boundary: %d\n", result[59]);
+
+ DBG (3, "attach: [60] channels per pixel:%s%s%s\n",
+ BIT(result[60],7)?" 1":"",
+ BIT(result[60],6)?" 3":"",
+ (result[60] & 0x3F) != 0 ? " RESERVED":"");
+
+ DBG (3, "attach: [61] bits per channel:%s%s%s%s%s%s%s\n",
+ BIT(result[61],7)?" 1":"",
+ BIT(result[61],6)?" 4":"",
+ BIT(result[61],5)?" 6":"",
+ BIT(result[61],4)?" 8":"",
+ BIT(result[61],3)?" 10":"",
+ BIT(result[61],2)?" 12":"",
+ BIT(result[61],1)?" 16":"");
+
+ DBG (3, "attach: [62] scanner type:%s%s%s%s%s%s\n",
+ BIT(result[62],7)?" Flatbed":"",
+ BIT(result[62],6)?" Roller (ADF)":"",
+ BIT(result[62],5)?" Flatbed (ADF)":"",
+ BIT(result[62],4)?" Roller":"", /* does not feed multiple pages, AV25 */
+ BIT(result[62],3)?" Film scanner":"",
+ BIT(result[62],2)?" Duplex":"");
+
+ DBG (3, "attach: [75-76] Max shading target : %x\n",
+ get_double ( &(result[75]) ));
+
+ DBG (3, "attach: [77-78] Max X of transparency: %d dots * base_dpi\n",
+ get_double ( &(result[77]) ));
+ DBG (3, "attach: [79-80] Max Y of transparency: %d dots * base_dpi\n",
+ get_double ( &(result[79]) ));
+
+ DBG (3, "attach: [81-82] Max X of flatbed: %d dots * base_dpi\n",
+ get_double ( &(result[81]) ));
+ DBG (3, "attach: [83-84] Max Y of flatbed: %d dots * base_dpi\n",
+ get_double ( &(result[83]) ));
+
+ DBG (3, "attach: [85-86] Max X of ADF: %d dots * base_dpi\n",
+ get_double ( &(result[85]) ));
+ DBG (3, "attach: [87-88] Max Y of ADF: %d dots * base_dpi\n",
+ get_double ( &(result[87]) )); /* 0xFFFF means unlimited length */
+
+ DBG (3, "attach: [89-90] Res. in Ex. mode: %d dpi\n",
+ get_double ( &(result[89]) ));
+
+ DBG (3, "attach: [91] ASIC: %d\n", result[91]);
+
+ DBG (3, "attach: [92] Buttons: %d\n", result[92]);
+
+ DBG (3, "attach: [93] ESA4:%s%s%s%s%s%s%s%s\n",
+ BIT(result[93],7)?" SUPPORTS_ACCESSORIES_DETECT":"",
+ BIT(result[93],6)?" ADF_IS_BGR_ORDERED":"",
+ BIT(result[93],5)?" NO_SINGLE_CHANNEL_GRAY_MODE":"",
+ BIT(result[93],4)?" SUPPORTS_FLASH_UPDATE":"",
+ BIT(result[93],3)?" SUPPORTS_ASIC_UPDATE":"",
+ BIT(result[93],2)?" SUPPORTS_LIGHT_DETECT":"",
+ BIT(result[93],1)?" SUPPORTS_READ_PRNU_DATA":"",
+ BIT(result[93],0)?" FLATBED_MIRRORS_IMAGE":"");
+
+ DBG (3, "attach: [94] ESA5:%s%s%s%s%s%s%s%s\n",
+ BIT(result[94],7)?" IGNORE_LINE_DIFFERENCE_FOR_ADF":"",
+ BIT(result[94],6)?" NEEDS_SW_LINE_COLOR_PACK":"",
+ BIT(result[94],5)?" SUPPORTS_DUPLEX_SCAN":"",
+ BIT(result[94],4)?" INTERLACED_DUPLEX_SCAN":"",
+ BIT(result[94],3)?" SUPPORTS_TWO_MODE_ADF_SCANS":"",
+ BIT(result[94],2)?" SUPPORTS_TUNE_SCAN_LENGTH":"",
+ BIT(result[94],1)?" SUPPORTS_SWITCH_STRIP_FOR_DESKEW":"", /* Kodak i80 only */
+ BIT(result[94],0)?" SEARCHES_LEADING_SIDE_EDGE_BY_FIRMWARE":"");
+
+ DBG (3, "attach: [95] ESA6:%s%s%s%s%s%s%s%s\n",
+ BIT(result[95],7)?" SUPPORTS_PAPER_SIZE_AUTO_DETECTION":"",
+ BIT(result[95],6)?" SUPPORTS_DO_HOUSEKEEPING":"", /* Kodak i80 only */
+ BIT(result[95],5)?" SUPPORTS_PAPER_LENGTH_SETTING":"", /* AV220, Kodak */
+ BIT(result[95],4)?" SUPPORTS_PRE_GAMMA_LINEAR_CORRECTION":"",
+ BIT(result[95],3)?" SUPPORTS_PREFEEDING":"", /* OKI S9800 */
+ BIT(result[95],2)?" SUPPORTS_GET_BACKGROUND_RASTER":"", /* AV220 et.al. */
+ BIT(result[95],1)?" SUPPORTS_NVRAM_RESET":"",
+ BIT(result[95],0)?" SUPPORTS_BATCH_SCAN":"");
+
+ DBG (3, "attach: [128] ESA7:%s%s%s%s%s%s%s%s\n",
+ BIT(result[128],7)?" SUPPORTS_ADF_CONTINUOUS":"",
+ BIT(result[128],6)?" SUPPORTS_YCbCr_COLOR":"",
+ BIT(result[128],5)?" SUPPORTS_ADF_3PASS":"",
+ BIT(result[128],4)?" SUPPORTS_TUNE_SCAN_LENGTH_HORIZ":"",
+ BIT(result[128],3)?" SUPPORTS_READ_WRITE_ABILITY_PARAMETER":"",
+ BIT(result[128],2)?" SUPPORTS_JOB_CONTROL":"",
+ BIT(result[128],1)?" SUPPORTS_INF_LENGTH":"",
+ BIT(result[128],0)?" ULTRA_SONIC_DOUBLE_FEED_DETECTION":"");
+
+ DBG (3, "attach: [129] YCbCr:%s%s%s%s%s%s%s%s\n",
+ BIT(result[129],7)?" YCC4:2:0":"",
+ BIT(result[129],6)?" YCC(profile2)":"",
+ BIT(result[129],5)?" YCC(profile3)":"",
+ BIT(result[129],4)?" YCC(profile4)":"",
+ BIT(result[129],3)?" JPEG(profile1)":"",
+ BIT(result[129],2)?" JPEG(profile2)":"",
+ BIT(result[129],1)?" JPEG(profile3)":"",
+ BIT(result[129],0)?" JPEG(profile4)":"");
+
+ /* I have no idea how film scanner could reliably be detected -ReneR */
+ if (dev->hw->feature_type & AV_FILMSCANNER) {
+ dev->scanner_type = AV_FILM;
+ dev->sane.type = "film scanner";
+ }
+ else if ( BIT(result[62],6) || BIT(result[62],4) ) {
+ dev->scanner_type = AV_SHEETFEED;
+ dev->sane.type = "sheetfed scanner";
+ }
+ else {
+ dev->scanner_type = AV_FLATBED;
+ dev->sane.type = "flatbed scanner";
+ }
+
+ dev->inquiry_new_protocol = BIT (result[39],2);
+ dev->inquiry_asic_type = (int) result[91];
+
+ dev->inquiry_nvram_read = BIT(result[52],0);
+ dev->inquiry_power_save_time = BIT(result[52],1);
+
+ dev->inquiry_adf = BIT (result[62], 5);
+ dev->inquiry_duplex = BIT (result[62], 2) || BIT (result[94], 5);
+ dev->inquiry_duplex_interlaced = BIT(result[62],2) || BIT (result[94], 4);
+ /* the first avision scanners (AV3200) do not set the interlaced bit */
+ if (dev->inquiry_duplex && dev->inquiry_asic_type < AV_ASIC_C6)
+ dev->inquiry_duplex_interlaced = 1;
+
+ dev->inquiry_paper_length = BIT (result[95], 5);
+ dev->inquiry_batch_scan = BIT (result[95], 0); /* AV122, DM152 */
+
+ dev->inquiry_detect_accessories = BIT (result[93], 7);
+
+ dev->inquiry_needs_calibration = BIT (result[50], 4);
+
+ dev->inquiry_keeps_window = BIT (result[50], 1);
+ if (Avision_Device_List [model_num].feature_type & AV_DOES_NOT_KEEP_WINDOW)
+ dev->inquiry_keeps_window = 0;
+ if (Avision_Device_List [model_num].feature_type & AV_DOES_KEEP_WINDOW)
+ dev->inquiry_keeps_window = 1;
+
+ dev->inquiry_needs_gamma = BIT (result[50], 3);
+ dev->inquiry_keeps_gamma = BIT (result[50], 2);
+ if (Avision_Device_List [model_num].feature_type & AV_DOES_NOT_KEEP_GAMMA)
+ dev->inquiry_keeps_gamma = 0;
+ if (Avision_Device_List [model_num].feature_type & AV_DOES_KEEP_GAMMA)
+ dev->inquiry_keeps_gamma = 1;
+
+ dev->inquiry_3x3_matrix = BIT (result[51], 1);
+ dev->inquiry_needs_software_colorpack = BIT (result[50],5);
+
+ dev->inquiry_needs_line_pack = BIT (result[94], 6);
+
+ dev->inquiry_adf_need_mirror = BIT (result[51], 0);
+ dev->inquiry_adf_bgr_order = BIT (result[93], 6);
+ if (Avision_Device_List [model_num].feature_type & AV_ADF_BGR_ORDER_INVERT)
+ dev->inquiry_adf_bgr_order = ! dev->inquiry_adf_bgr_order;
+
+ dev->inquiry_light_detect = BIT (result[93], 2);
+ dev->inquiry_light_control = BIT (result[50], 7);
+ dev->inquiry_button_control = BIT (result[50], 6) | BIT (result[51],2);
+
+ dev->inquiry_exposure_control = BIT(result[51],7);
+ dev->inquiry_max_shading_target = get_double ( &(result[75]) );
+
+ dev->inquiry_color_boundary = result[54];
+ if (dev->inquiry_color_boundary == 0)
+ dev->inquiry_color_boundary = 8;
+
+ dev->inquiry_gray_boundary = result[55];
+ if (dev->inquiry_gray_boundary == 0)
+ dev->inquiry_gray_boundary = 8;
+
+ dev->inquiry_dithered_boundary = result[59];
+ if (dev->inquiry_dithered_boundary == 0)
+ dev->inquiry_dithered_boundary = 8;
+
+ dev->inquiry_thresholded_boundary = result[57];
+ if (dev->inquiry_thresholded_boundary == 0)
+ dev->inquiry_thresholded_boundary = 8;
+
+ dev->inquiry_line_difference = result[53];
+ /* compensation according to real world hardware */
+ switch (dev->inquiry_asic_type)
+ {
+ case AV_ASIC_C2: /* HP 5300 */
+ case AV_ASIC_C5: /* HP 53xx R2 */
+ dev->inquiry_line_difference /= 2; /* HP 5300 */
+ break;
+ case AV_ASIC_C7:
+ dev->inquiry_line_difference *= 2; /* AV610C2 */
+ break;
+ default:
+ ;
+ }
+
+ if (dev->inquiry_new_protocol) {
+ dev->inquiry_optical_res = get_double ( &(result[89]) );
+ dev->inquiry_max_res = get_double ( &(result[44]) );
+ }
+ else {
+ dev->inquiry_optical_res = result[37] * 100;
+ dev->inquiry_max_res = result[38] * 100;
+ }
+
+ /* fixup max res */
+ if (dev->inquiry_optical_res > dev->inquiry_max_res) {
+ DBG (1, "Inquiry optical resolution > max_resolution, adjusting!\n");
+ dev->inquiry_max_res = dev->inquiry_optical_res;
+ }
+
+ if (dev->inquiry_optical_res == 0)
+ {
+ DBG (1, "Inquiry optical resolution is invalid!\n");
+ if (dev->hw->feature_type & AV_FORCE_FILM)
+ dev->inquiry_optical_res = 2438; /* verify */
+ if (dev->scanner_type == AV_SHEETFEED)
+ dev->inquiry_optical_res = 300;
+ else
+ dev->inquiry_optical_res = 600;
+ }
+ if (dev->inquiry_max_res == 0) {
+ DBG (1, "Inquiry max resolution is invalid, using 1200 dpi!\n");
+ dev->inquiry_max_res = 1200;
+ }
+
+ DBG (1, "attach: optical resolution set to: %d dpi\n", dev->inquiry_optical_res);
+ DBG (1, "attach: max resolution set to: %d dpi\n", dev->inquiry_max_res);
+
+ if (BIT(result[60],6))
+ dev->inquiry_channels_per_pixel = 3;
+ else if (BIT(result[60],7))
+ dev->inquiry_channels_per_pixel = 1;
+ else if ( ((result[36] >> 4) & 0x7) > 0)
+ dev->inquiry_channels_per_pixel = 3;
+ else
+ dev->inquiry_channels_per_pixel = 1;
+
+ if (BIT(result[61],1))
+ dev->inquiry_bits_per_channel = 16;
+ else if (BIT(result[61],2))
+ dev->inquiry_bits_per_channel = 12;
+ else if (BIT(result[61],3))
+ dev->inquiry_bits_per_channel = 10;
+ else if (BIT(result[61],4))
+ dev->inquiry_bits_per_channel = 8;
+ else if (BIT(result[61],5))
+ dev->inquiry_bits_per_channel = 6;
+ else if (BIT(result[61],6))
+ dev->inquiry_bits_per_channel = 4;
+ else if (BIT(result[61],7))
+ dev->inquiry_bits_per_channel = 1;
+ else
+ dev->inquiry_bits_per_channel = 8; /* default for old scanners */
+
+ if (dev->hw->feature_type & AV_12_BIT_MODE)
+ dev->inquiry_bits_per_channel = 12;
+
+ if (! (dev->hw->feature_type & AV_GRAY_MODES))
+ dev->inquiry_no_gray_modes = BIT(result[93],5);
+
+ DBG (1, "attach: max channels per pixel: %d, max bits per channel: %d\n",
+ dev->inquiry_channels_per_pixel, dev->inquiry_bits_per_channel);
+
+ if (! (dev->hw->feature_type & AV_NO_BUTTON))
+ dev->inquiry_buttons = result[92];
+
+ /* get max x/y ranges for the different modes */
+ {
+ double base_dpi; /* TODO: make int */
+ if (dev->scanner_type != AV_FILM) {
+ base_dpi = AVISION_BASE_RES;
+ } else {
+ /* ZP: The right number is 2820, whether it is 40-41, 42-43, 44-45,
+ * 46-47 or 89-90 I don't know but I would bet for the last !
+ * ReneR: OK. We use it via the optical_res which we need anyway ...
+ */
+ base_dpi = dev->inquiry_optical_res;
+ }
+
+ /* .1 to slightly increase the size to match the one of American standard paper
+ formats that would otherwise be .1 mm too large to scan ... */
+ dev->inquiry_x_ranges [AV_NORMAL_DIM] =
+ (double)get_double (&(result[81])) * MM_PER_INCH / base_dpi + .1;
+ dev->inquiry_y_ranges [AV_NORMAL_DIM] =
+ (double)get_double (&(result[83])) * MM_PER_INCH / base_dpi;
+
+ dev->inquiry_x_ranges [AV_TRANSPARENT_DIM] =
+ (double)get_double (&(result[77])) * MM_PER_INCH / base_dpi + .1;
+ dev->inquiry_y_ranges [AV_TRANSPARENT_DIM] =
+ (double)get_double (&(result[79])) * MM_PER_INCH / base_dpi;
+
+ dev->inquiry_x_ranges [AV_ADF_DIM] =
+ (double)get_double (&(result[85])) * MM_PER_INCH / base_dpi + .1;
+ dev->inquiry_y_ranges [AV_ADF_DIM] =
+ (double)get_double (&(result[87])) * MM_PER_INCH / base_dpi;
+ }
+
+ dev->inquiry_tune_scan_length = BIT(result[94],2);
+ if (Avision_Device_List [model_num].feature_type2 & AV_NO_TUNE_SCAN_LENGTH)
+ dev->inquiry_tune_scan_length = 0;
+
+ dev->inquiry_background_raster = BIT(result[95],2);
+
+ if (dev->hw->feature_type & AV_NO_BACKGROUND)
+ dev->inquiry_background_raster = 0;
+
+ if (dev->inquiry_background_raster) {
+ dev->inquiry_background_raster_pixel =
+ get_double(&(result[85])) * dev->inquiry_optical_res / AVISION_BASE_RES;
+ }
+
+ /* check if x/y ranges are valid :-((( */
+ {
+ source_mode_dim mode;
+
+ for (mode = AV_NORMAL_DIM; mode < AV_SOURCE_MODE_DIM_LAST; ++ mode)
+ {
+ if (dev->inquiry_x_ranges [mode] != 0 &&
+ dev->inquiry_y_ranges [mode] != 0)
+ {
+ DBG (3, "attach: x/y-range for mode %d is valid!\n", mode);
+ if (force_a4) {
+ DBG (1, "attach: \"force_a4\" found! Using default (ISO A4).\n");
+ dev->inquiry_x_ranges [mode] = A4_X_RANGE * MM_PER_INCH;
+ dev->inquiry_y_ranges [mode] = A4_Y_RANGE * MM_PER_INCH;
+ } else if (force_a3) {
+ DBG (1, "attach: \"force_a3\" found! Using default (ISO A3).\n");
+ dev->inquiry_x_ranges [mode] = A3_X_RANGE * MM_PER_INCH;
+ dev->inquiry_y_ranges [mode] = A3_Y_RANGE * MM_PER_INCH;
+ }
+ }
+ else /* mode is invalid */
+ {
+ DBG (1, "attach: x/y-range for mode %d is invalid! Using a default.\n", mode);
+ if (dev->hw->feature_type & AV_FORCE_A3) {
+ dev->inquiry_x_ranges [mode] = A3_X_RANGE * MM_PER_INCH;
+ dev->inquiry_y_ranges [mode] = A3_Y_RANGE * MM_PER_INCH;
+ }
+ else if (dev->hw->feature_type & AV_FORCE_FILM) {
+ dev->inquiry_x_ranges [mode] = FILM_X_RANGE * MM_PER_INCH;
+ dev->inquiry_y_ranges [mode] = FILM_Y_RANGE * MM_PER_INCH;
+ }
+ else {
+ dev->inquiry_x_ranges [mode] = A4_X_RANGE * MM_PER_INCH;
+
+ if (dev->scanner_type == AV_SHEETFEED)
+ dev->inquiry_y_ranges [mode] = SHEETFEED_Y_RANGE * MM_PER_INCH;
+ else
+ dev->inquiry_y_ranges [mode] = A4_Y_RANGE * MM_PER_INCH;
+ }
+ }
+ DBG (1, "attach: Mode %d range is now: %f x %f mm.\n",
+ mode,
+ dev->inquiry_x_ranges [mode], dev->inquiry_y_ranges [mode]);
+ } /* end for all modes */
+ }
+
+ /* We need a bigger buffer for USB devices, since they seem to have
+ a firmware bug and do not support reading the calibration data in
+ tiny chunks */
+ if (av_con.connection_type == AV_USB)
+ dev->scsi_buffer_size = 1024 * 1024; /* or 0x10000, used by AV Windows driver during background raster read, ... ? */
+ else
+ dev->scsi_buffer_size = sanei_scsi_max_request_size;
+
+ if (dev->inquiry_asic_type >= AV_ASIC_C5)
+ dev->read_stripe_size = 32;
+ else /* tested on AV3200 with it's max of 300dpi @color */
+ dev->read_stripe_size = 8; /* maybe made dynamic on scan res ... */
+
+ /* normally the data_dq is 0x0a0d - but some newer scanner hang with it ... */
+ if (dev->inquiry_new_protocol) /* TODO: match on ASIC? which model hung? */
+ dev->data_dq = 0x0a0d;
+ else
+ dev->data_dq = 0;
+
+ avision_close (&av_con);
+
+ ++ num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+
+ close_scanner_and_return:
+ avision_close (&av_con);
+
+ return status;
+}
+
+
+static SANE_Status
+get_tune_scan_length (Avision_Scanner* s)
+{
+ SANE_Status status;
+ int i;
+
+ struct command_read rcmd;
+ size_t size;
+
+ struct max_value {
+ uint8_t max [2];
+ } payload;
+
+ /* turn on the light */
+ DBG (3, "get_tune_scan_length:\n");
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ size = sizeof (payload);
+
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0xD2; /* Read General Ability/Parameter */
+
+ for (i = 1; i <= 8; ++i) {
+ memset (&payload, 0, sizeof (payload));
+
+ set_double (rcmd.datatypequal, i); /* type */
+ set_triple (rcmd.transferlen, size);
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd),
+ 0, 0, &payload, &size);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_tune_scan_length: read %d failed (%s)\n", i, sane_strstatus (status));
+ return status;
+ }
+ DBG (1, "get_tune_scan_length: %d: %d\n", i, get_double (payload.max));
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+send_tune_scan_length (Avision_Scanner* s)
+{
+ int top, bottom;
+
+ SANE_Status status;
+ size_t size;
+ struct command_send scmd;
+ struct truncate_attach {
+ uint8_t vertical [2];
+ /* uint8_t horizontal [2]; not send by the Windows driver, yet */
+ } payload;
+
+ DBG (3, "send_tune_scan_length:\n");
+
+ memset (&scmd, 0, sizeof (scmd));
+
+ size = sizeof (payload);
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = 0x96; /* Attach/Truncate head(left) of scan length */
+ set_triple (scmd.transferlen, size);
+
+ /* the SPEC says optical DPI, but real world measuring suggests it is 1200
+ as in the window descriptor */
+ top = 1200 * SANE_UNFIX (s->val[OPT_OVERSCAN_TOP].w) / MM_PER_INCH;
+ DBG (3, "send_tune_scan_length: top: %d\n", top);
+
+ set_double (scmd.datatypequal, 0x0001); /* attach, 0x000 is shorten */
+ set_double (payload.vertical, top);
+ /* set_double (payload.horizontal, 0); */
+
+ /* we alway send it, even for 0 as the scanner keeps it in RAM and
+ previous runs could already have set something */
+
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ &payload, sizeof (payload), 0, 0);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "send_tune_scan_length: send top/left failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ scmd.datatypecode = 0x95; /* Attach/Truncate tail(right) of scan length */
+ bottom = 1200 * SANE_UNFIX (s->val[OPT_OVERSCAN_BOTTOM].w) / MM_PER_INCH;
+ DBG (3, "send_tune_scan_length: bottom: %d\n", bottom);
+
+ set_double (payload.vertical, bottom);
+ /*set_double (payload.horizontal, 0); */
+
+ size = sizeof (payload);
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ &payload, sizeof (payload), 0, 0);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "send_tune_scan_length: send bottom/right failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+additional_probe (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+
+ /* we should wait until the scanner is ready before we
+ perform further actions */
+
+ SANE_Status status;
+ /* try to retrieve additional accessory information */
+ if (dev->inquiry_detect_accessories) {
+ status = get_accessories_info (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* for a film scanner try to retrieve additional frame information */
+ if (dev->scanner_type == AV_FILM) {
+ status = get_frame_info (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* no scanner did support this so far: tried on AV220, DM152 */
+ if (0 && dev->inquiry_duplex) {
+ status = get_duplex_info (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* get overscan ("head/tail tune") information: hangs AV220, zeros on AV122 */
+ if (0 && dev->inquiry_tune_scan_length) {
+ status = get_tune_scan_length (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* create dynamic *-mode entries */
+ if (!dev->inquiry_no_gray_modes)
+ {
+ if (dev->inquiry_bits_per_channel > 0) {
+ add_color_mode (dev, AV_THRESHOLDED, SANE_VALUE_SCAN_MODE_LINEART);
+ add_color_mode (dev, AV_DITHERED, "Dithered");
+ }
+
+ if (dev->inquiry_bits_per_channel >= 8)
+ add_color_mode (dev, AV_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY);
+
+ if (dev->inquiry_bits_per_channel == 12)
+ add_color_mode (dev, AV_GRAYSCALE12, "12bit Gray");
+
+ if (dev->inquiry_bits_per_channel >= 16)
+ add_color_mode (dev, AV_GRAYSCALE16, "16bit Gray");
+ }
+
+ if (dev->inquiry_channels_per_pixel > 1) {
+ add_color_mode (dev, AV_TRUECOLOR, SANE_VALUE_SCAN_MODE_COLOR);
+
+ if (dev->inquiry_bits_per_channel == 12)
+ add_color_mode (dev, AV_TRUECOLOR12, "12bit Color");
+
+ if (dev->inquiry_bits_per_channel >= 16)
+ add_color_mode (dev, AV_TRUECOLOR16, "16bit Color");
+ }
+
+ /* now choose the default mode - avoiding the 12/16 bit modes */
+ dev->color_list_default = last_color_mode (dev);
+ if (dev->inquiry_bits_per_channel > 8 && dev->color_list_default > 0) {
+ dev->color_list_default--;
+ }
+
+ if (dev->scanner_type == AV_SHEETFEED)
+ {
+ add_source_mode (dev, AV_ADF, "ADF Front");
+ }
+ else
+ {
+ add_source_mode (dev, AV_NORMAL, "Normal");
+
+ if (dev->inquiry_light_box)
+ add_source_mode (dev, AV_TRANSPARENT, "Transparency");
+
+ if (dev->inquiry_adf)
+ add_source_mode (dev, AV_ADF, "ADF Front");
+ }
+
+ if (dev->inquiry_duplex) {
+ if (dev->inquiry_duplex_interlaced && !(dev->hw->feature_type & AV_NO_REAR))
+ add_source_mode (dev, AV_ADF_REAR, "ADF Back");
+ add_source_mode (dev, AV_ADF_DUPLEX, "ADF Duplex");
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_calib_format (Avision_Scanner* s, struct calibration_format* format)
+{
+ SANE_Status status;
+
+ struct command_read rcmd;
+ uint8_t result [32];
+ size_t size;
+
+ DBG (3, "get_calib_format:\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0x60; /* get calibration format */
+ set_double (rcmd.datatypequal, s->hw->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ DBG (3, "get_calib_format: read_data: %lu bytes\n", (u_long) size);
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result) ) {
+ DBG (1, "get_calib_format: read calib. info failed (%s)\n",
+ sane_strstatus (status) );
+ return status;
+ }
+
+ debug_print_calib_format (3, "get_calib_format", result);
+
+ format->pixel_per_line = get_double (&(result[0]));
+ format->bytes_per_channel = result[2];
+ format->lines = result[3];
+ format->flags = result[4];
+ format->ability1 = result[5];
+ format->r_gain = result[6];
+ format->g_gain = result[7];
+ format->b_gain = result[8];
+ format->r_shading_target = get_double (&(result[9]));
+ format->g_shading_target = get_double (&(result[11]));
+ format->b_shading_target = get_double (&(result[13]));
+ format->r_dark_shading_target = get_double (&(result[15]));
+ format->g_dark_shading_target = get_double (&(result[17]));
+ format->b_dark_shading_target = get_double (&(result[19]));
+
+ /* now translate to normal! */
+ /* firmware return R--RG--GB--B with 3 line count */
+ /* software format it as 1 line if true color scan */
+ /* only line interleave format to be supported */
+
+ if (color_mode_is_color (s->c_mode) || BIT(format->ability1, 3)) {
+ format->channels = 3;
+ format->lines /= 3; /* line interleave */
+ }
+ else
+ format->channels = 1;
+
+ DBG (3, "get_calib_format: channels: %d\n", format->channels);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_calib_data (Avision_Scanner* s, uint8_t data_type,
+ uint8_t* calib_data,
+ size_t calib_size)
+{
+ SANE_Status status;
+ uint8_t *calib_ptr;
+
+ size_t get_size, data_size, chunk_size;
+
+ struct command_read rcmd;
+
+ chunk_size = calib_size;
+
+ DBG (3, "get_calib_data: type %x, size %lu, chunk_size: %lu\n",
+ data_type, (u_long) calib_size, (u_long) chunk_size);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = data_type;
+ set_double (rcmd.datatypequal, s->hw->data_dq);
+
+ calib_ptr = calib_data;
+ get_size = chunk_size;
+ data_size = calib_size;
+
+ while (data_size) {
+ if (get_size > data_size)
+ get_size = data_size;
+
+ read_constrains(s, get_size);
+
+ set_triple (rcmd.transferlen, get_size);
+
+ DBG (3, "get_calib_data: Reading %ld bytes calibration data\n",
+ (long)get_size);
+
+ status = avision_cmd (&s->av_con, &rcmd,
+ sizeof (rcmd), 0, 0, calib_ptr, &get_size);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "get_calib_data: read data failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (3, "get_calib_data: Got %ld bytes calibration data\n", (long)get_size);
+
+ data_size -= get_size;
+ calib_ptr += get_size;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+set_calib_data (Avision_Scanner* s, struct calibration_format* format,
+ uint8_t* dark_data, uint8_t* white_data)
+{
+ Avision_Device* dev = s->hw;
+
+ const int elements_per_line = format->pixel_per_line * format->channels;
+
+ SANE_Status status;
+
+ uint8_t send_type;
+ uint16_t send_type_q;
+
+ struct command_send scmd;
+
+ int i;
+ size_t out_size;
+
+ DBG (3, "set_calib_data:\n");
+
+ send_type = 0x82; /* download calibration data */
+
+ /* do we use a color mode? */
+ if (format->channels > 1) {
+ send_type_q = 0x12; /* color calib data */
+ }
+ else {
+ if (dev->hw->feature_type & AV_GRAY_CALIB_BLUE)
+ send_type_q = 0x2; /* gray/bw calib data on the blue channel (AV610) */
+ else
+ send_type_q = 0x11; /* gray/bw calib data */
+ }
+
+ memset (&scmd, 0x00, sizeof (scmd));
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = send_type;
+
+ /* data corrections due to dark calibration data merge */
+ if (BIT (format->ability1, 2) ) {
+ DBG (3, "set_calib_data: merging dark calibration data\n");
+ for (i = 0; i < elements_per_line; ++i) {
+ uint16_t value_orig = get_double_le (white_data + i*2);
+ uint16_t value_new = value_orig;
+
+ value_new &= 0xffc0;
+ value_new |= (get_double_le (dark_data + i*2) >> 10) & 0x3f;
+
+ DBG (9, "set_calib_data: element %d, dark difference %d\n",
+ i, value_orig - value_new);
+
+ set_double_le ((white_data + i*2), value_new);
+ }
+ }
+
+ out_size = format->pixel_per_line * 2;
+
+ /* send data in one command? */
+ /* FR: HP5370 reports one-pass, but needs multi (or other format in single) */
+ if (format->channels == 1 ||
+ ( ( (dev->hw->feature_type & AV_ONE_CALIB_CMD) ||
+ ! BIT(format->ability1, 0) ) &&
+ ! (dev->hw->feature_type & AV_MULTI_CALIB_CMD) ) )
+ /* one command (most scanners) */
+ {
+ size_t send_size = elements_per_line * 2;
+ DBG (3, "set_calib_data: all channels in one command\n");
+ DBG (3, "set_calib_data: send_size: %lu\n", (u_long) send_size);
+
+ memset (&scmd, 0, sizeof (scmd) );
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = send_type;
+ set_double (scmd.datatypequal, send_type_q);
+ set_triple (scmd.transferlen, send_size);
+
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ (char*) white_data, send_size, 0, 0);
+ /* not return immediately to free mem at the end */
+ }
+ else /* send data channel by channel (some USB ones) */
+ {
+ int conv_out_size = format->pixel_per_line * 2;
+ uint16_t* conv_out_data; /* here it is save to use 16bit data
+ since we only move whole words around */
+
+ DBG (3, "set_calib_data: channels in single commands\n");
+
+ conv_out_data = (uint16_t*) malloc (conv_out_size);
+ if (!conv_out_data) {
+ status = SANE_STATUS_NO_MEM;
+ }
+ else {
+ int channel;
+ for (channel = 0; channel < 3; ++ channel)
+ {
+ int i;
+
+ /* no need for endianness handling since whole word copy */
+ uint16_t* casted_avg_data = (uint16_t*) white_data;
+
+ DBG (3, "set_calib_data_calibration: channel: %i\n", channel);
+
+ for (i = 0; i < format->pixel_per_line; ++ i)
+ conv_out_data [i] = casted_avg_data [i * 3 + channel];
+
+ DBG (3, "set_calib_data: sending %i bytes now\n",
+ conv_out_size);
+
+ memset (&scmd, 0, sizeof (scmd));
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = send_type; /* send calibration data */
+ set_double (scmd.datatypequal, channel);
+ set_triple (scmd.transferlen, conv_out_size);
+
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ conv_out_data, conv_out_size, 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (3, "set_calib_data: send_data failed (%s)\n",
+ sane_strstatus (status));
+ /* not return immediately to free mem at the end */
+ }
+ } /* end for each channel */
+ free (conv_out_data);
+ } /* end else send calib data*/
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Sort data pixel by pixel and average first 2/3 of the data.
+ The caller has to free return pointer. R,G,B pixels
+ interleave to R,G,B line interleave.
+
+ The input data data is in 16 bits little endian, always.
+ That is a = b[1] << 8 + b[0] in all system.
+
+ We convert it to SCSI high-endian (big-endian) since we use it all
+ over the place anyway .... - Sorry for this mess. */
+
+static uint8_t*
+sort_and_average (struct calibration_format* format, uint8_t* data)
+{
+ const int elements_per_line = format->pixel_per_line * format->channels;
+ const int stride = format->bytes_per_channel * elements_per_line;
+ int i, line;
+
+ uint8_t *sort_data, *avg_data;
+
+ DBG (1, "sort_and_average:\n");
+
+ if (!format || !data)
+ return NULL;
+
+ sort_data = malloc (format->lines * 2);
+ if (!sort_data)
+ return NULL;
+
+ avg_data = malloc (elements_per_line * 2);
+ if (!avg_data) {
+ free (sort_data);
+ return NULL;
+ }
+
+ /* for each pixel */
+ for (i = 0; i < elements_per_line; ++ i)
+ {
+ uint8_t* ptr1 = data + i * format->bytes_per_channel;
+ uint16_t temp;
+
+ /* copy all lines for pixel i into the linear array sort_data */
+ for (line = 0; line < format->lines; ++ line) {
+ uint8_t* ptr2 = ptr1 + line * stride; /* pixel */
+
+ if (format->bytes_per_channel == 1)
+ temp = 0xffff * *ptr2 / 255;
+ else
+ temp = get_double_le (ptr2); /* little-endian! */
+ set_double ((sort_data + line*2), temp); /* store big-endian */
+ /* DBG (7, "ReneR to sort: %x\n", temp); */
+ }
+
+ temp = bubble_sort (sort_data, format->lines);
+ /* DBG (7, "ReneR averaged: %x\n", temp); */
+ set_double ((avg_data + i*2), temp); /* store big-endian */
+ }
+
+ free ((void *) sort_data);
+ return avg_data;
+}
+
+/* shading data is 16bits little endian format when send/read from firmware */
+static void
+compute_dark_shading_data (Avision_Scanner* s,
+ struct calibration_format* format, uint8_t* data)
+{
+ uint16_t map_value = DEFAULT_DARK_SHADING;
+ uint16_t rgb_map_value[3];
+
+ int elements_per_line, i;
+
+ DBG (3, "compute_dark_shading_data:\n");
+
+ if (s->hw->inquiry_max_shading_target != INVALID_DARK_SHADING)
+ map_value = s->hw->inquiry_max_shading_target << 8;
+
+ rgb_map_value[0] = format->r_dark_shading_target;
+ rgb_map_value[1] = format->g_dark_shading_target;
+ rgb_map_value[2] = format->b_dark_shading_target;
+
+ for (i = 0; i < format->channels; ++i) {
+ if (rgb_map_value[i] == INVALID_DARK_SHADING)
+ rgb_map_value[i] = map_value;
+ }
+
+ if (format->channels == 1) {
+ /* set to green, TODO: should depend on color drop-out and true-gray -ReneR */
+ rgb_map_value[0] = rgb_map_value[1] = rgb_map_value[2] = rgb_map_value[1];
+ }
+
+ elements_per_line = format->pixel_per_line * format->channels;
+
+ /* Check line interleave or pixel interleave. */
+ /* It seems no ASIC use line interleave right now. */
+ /* Avision SCSI protocol document has bad description. */
+ for (i = 0; i < elements_per_line; ++i)
+ {
+ uint16_t tmp_data = get_double_le((data + i*2));
+ if (tmp_data > rgb_map_value[i % 3]) {
+ set_double ((data + i*2), tmp_data - rgb_map_value[i % 3]);
+ }
+ else {
+ set_double ((data + i*2), 0);
+ }
+ }
+}
+
+static void
+compute_white_shading_data (Avision_Scanner* s,
+ struct calibration_format* format, uint8_t* data)
+{
+ int i;
+ uint16_t inquiry_mst = DEFAULT_WHITE_SHADING;
+ uint16_t mst[3];
+
+ int elements_per_line = format->pixel_per_line * format->channels;
+
+ /* debug counter */
+ int values_invalid = 0;
+ int values_limitted = 0;
+
+ DBG (3, "compute_white_shading_data:\n");
+
+ if (s->hw->inquiry_max_shading_target != INVALID_WHITE_SHADING)
+ inquiry_mst = s->hw->inquiry_max_shading_target << 4;
+
+ mst[0] = format->r_shading_target;
+ mst[1] = format->g_shading_target;
+ mst[2] = format->b_shading_target;
+
+ for (i = 0; i < 3; ++i) {
+ if (mst[i] == INVALID_WHITE_SHADING) /* mst[i] > MAX_WHITE_SHADING) */ {
+ DBG (3, "compute_white_shading_data: target %d invalid (%x) using inquiry (%x)\n",
+ i, mst[i], inquiry_mst);
+ mst[i] = inquiry_mst;
+ }
+ /* some firmware versions seems to return the bytes swapped? */
+ else if (mst[i] < 0x110) {
+ uint8_t* swap_mst = (uint8_t*) &mst[i];
+ uint8_t low_nibble_mst = swap_mst [0];
+ swap_mst [0] = swap_mst[1];
+ swap_mst [1] = low_nibble_mst;
+
+ DBG (3, "compute_white_shading_data: target %d: bytes swapped.\n", i);
+ }
+ if (mst[i] < DEFAULT_WHITE_SHADING / 2) {
+ DBG (3, "compute_white_shading_data: target %d: too low (%d) using default (%d).\n",
+ i, mst[i], DEFAULT_WHITE_SHADING);
+ mst[i] = DEFAULT_WHITE_SHADING;
+ }
+ else
+ DBG (3, "compute_white_shading_data: target %d: %x\n", i, mst[0]);
+ }
+
+ /* some Avision example code was present here until SANE/Avision
+ * BUILD 57. */
+
+ if (format->channels == 1) {
+ /* set to green, TODO: should depend on color drop-out and true-gray -ReneR */
+ mst[0] = mst[1] = mst[2] = mst[1];
+ }
+
+ /* calculate calibration data */
+ for (i = 0; i < elements_per_line; ++ i)
+ {
+ int result;
+ /* calculate calibration value for pixel i */
+ uint16_t tmp_data = get_double((data + i*2));
+
+ if (tmp_data == INVALID_WHITE_SHADING) {
+ tmp_data = DEFAULT_WHITE_SHADING;
+ ++ values_invalid;
+ }
+
+ result = ( (int)mst[i % 3] * WHITE_MAP_RANGE / (tmp_data + 0.5));
+
+ /* sanity check for over-amplification, clipping */
+ if (result > MAX_WHITE_SHADING) {
+ result = WHITE_MAP_RANGE;
+ ++ values_limitted;
+ }
+
+ /* for visual debugging ... */
+ if (static_calib_list [i % 3] == SANE_TRUE)
+ result = 0xA000;
+
+ /* the output to the scanner will be 16 bit little endian again */
+ set_double_le ((data + i*2), result);
+ }
+ DBG (3, "compute_white_shading_data: %d invalid, %d limited\n",
+ values_invalid, values_limitted);
+}
+
+/* old_r_calibration was here until SANE/Avision BUILD 90 */
+
+static SANE_Status
+normal_calibration (Avision_Scanner* s)
+{
+ SANE_Status status;
+
+ struct calibration_format calib_format;
+
+ int calib_data_size, calib_bytes_per_line;
+ uint8_t read_type;
+ uint8_t *calib_tmp_data;
+
+ DBG (1, "normal_calibration:\n");
+
+ /* get calibration format and data */
+ status = get_calib_format (s, &calib_format);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* check if need do calibration */
+ if (calib_format.flags != 1) {
+ DBG (1, "normal_calibration: Scanner claims no calibration needed -> skipped!\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* calculate calibration data size for read from scanner */
+ /* size = lines * bytes_per_channel * pixels_per_line * channel */
+ calib_bytes_per_line = calib_format.bytes_per_channel *
+ calib_format.pixel_per_line * calib_format.channels;
+
+ calib_data_size = calib_format.lines * calib_bytes_per_line;
+
+ calib_tmp_data = malloc (calib_data_size);
+ if (!calib_tmp_data)
+ return SANE_STATUS_NO_MEM;
+
+ /* check if we need to do dark calibration (shading) */
+ if (BIT(calib_format.ability1, 3))
+ {
+ DBG (1, "normal_calibration: reading dark data\n");
+ /* read dark calib data */
+ status = get_calib_data (s, 0x66, calib_tmp_data, calib_data_size);
+
+ if (status != SANE_STATUS_GOOD) {
+ free (calib_tmp_data);
+ return status;
+ }
+
+ /* process dark data: sort and average. */
+
+ if (s->dark_avg_data) {
+ free (s->dark_avg_data);
+ s->dark_avg_data = 0;
+ }
+ s->dark_avg_data = sort_and_average (&calib_format, calib_tmp_data);
+ if (!s->dark_avg_data) {
+ free (calib_tmp_data);
+ return SANE_STATUS_NO_MEM;
+ }
+ compute_dark_shading_data (s, &calib_format, s->dark_avg_data);
+ }
+
+ /* do we use a color mode? */
+ if (calib_format.channels > 1) {
+ DBG (3, "normal_calibration: using color calibration\n");
+ read_type = 0x62; /* read color calib data */
+ }
+ else {
+ DBG (3, "normal_calibration: using gray calibration\n");
+ read_type = 0x61; /* gray calib data */
+ }
+
+ /* do white calibration: read gray or color data */
+ status = get_calib_data (s, read_type, calib_tmp_data, calib_data_size);
+
+ if (status != SANE_STATUS_GOOD) {
+ free (calib_tmp_data);
+ return status;
+ }
+
+ if (0) /* debug */
+ {
+ FILE* f = NULL;
+ f = fopen ("calibration-white.pnm", "w");
+ write_pnm_header (f, AV_GRAYSCALE, calib_format.bytes_per_channel * 8,
+ calib_format.pixel_per_line,
+ calib_format.lines * calib_format.channels);
+
+ fwrite (calib_tmp_data, 1, calib_data_size, f);
+ fclose (f);
+ }
+
+ if (s->white_avg_data) {
+ free (s->white_avg_data);
+ s->white_avg_data = 0;
+ }
+ s->white_avg_data = sort_and_average (&calib_format, calib_tmp_data);
+ if (!s->white_avg_data) {
+ free (calib_tmp_data);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* decrease white average data (if dark average data is present) */
+ if (s->dark_avg_data) {
+ int elements_per_line = calib_format.pixel_per_line * calib_format.channels;
+ int i;
+
+ DBG (1, "normal_calibration: dark data present - decreasing white average data\n");
+
+ for (i = 0; i < elements_per_line; ++ i) {
+ s->white_avg_data[i] -= s->dark_avg_data[i];
+ }
+ }
+
+ compute_white_shading_data (s, &calib_format, s->white_avg_data);
+
+ status = set_calib_data (s, &calib_format,
+ s->dark_avg_data, s->white_avg_data);
+
+ free (calib_tmp_data);
+ return status;
+}
+
+/* next was taken from the GIMP and is a bit modified ... ;-)
+ * original Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ */
+static double
+brightness_contrast_func (double brightness, double contrast, double value)
+{
+ double nvalue;
+ double power;
+
+ /* apply brightness */
+ if (brightness < 0.0)
+ value = value * (1.0 + brightness);
+ else
+ value = value + ((1.0 - value) * brightness);
+
+ /* apply contrast */
+ if (contrast < 0.0)
+ {
+ if (value > 0.5)
+ nvalue = 1.0 - value;
+ else
+ nvalue = value;
+ if (nvalue < 0.0)
+ nvalue = 0.0;
+ nvalue = 0.5 * pow (nvalue * 2.0 , (double) (1.0 + contrast));
+ if (value > 0.5)
+ value = 1.0 - nvalue;
+ else
+ value = nvalue;
+ }
+ else
+ {
+ if (value > 0.5)
+ nvalue = 1.0 - value;
+ else
+ nvalue = value;
+ if (nvalue < 0.0)
+ nvalue = 0.0;
+ power = (contrast == 1.0) ? 127 : 1.0 / (1.0 - contrast);
+ nvalue = 0.5 * pow (2.0 * nvalue, power);
+ if (value > 0.5)
+ value = 1.0 - nvalue;
+ else
+ value = nvalue;
+ }
+ return value;
+}
+
+static SANE_Status
+send_gamma (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ int invert_table = 0;
+
+ size_t gamma_table_raw_size;
+ size_t gamma_table_size;
+ size_t gamma_values;
+
+ struct command_send scmd;
+ uint8_t *gamma_data;
+
+ int color; /* current color */
+ size_t i; /* big table index */
+ size_t j; /* little table index */
+ size_t k; /* big table sub index */
+ double v1, v2;
+
+ double brightness;
+ double contrast;
+
+ if (dev->inquiry_asic_type != AV_ASIC_OA980)
+ invert_table = (s->c_mode == AV_THRESHOLDED) || (s->c_mode == AV_DITHERED);
+
+ switch (dev->inquiry_asic_type)
+ {
+ case AV_ASIC_Cx:
+ case AV_ASIC_C1: /* from avision code */
+ gamma_table_raw_size = 4096;
+ gamma_table_size = 2048;
+ break;
+ case AV_ASIC_C5:
+ gamma_table_raw_size = 256;
+ gamma_table_size = 256;
+ break;
+ case AV_ASIC_C6: /* SPEC claims: 256 ... ? */
+ case AV_ASIC_C7:
+ gamma_table_raw_size = 512;
+ gamma_table_size = 512;
+ break;
+ case AV_ASIC_OA980:
+ gamma_table_raw_size = 4096;
+ gamma_table_size = 4096;
+ break;
+ case AV_ASIC_OA982:
+ gamma_table_raw_size = 256;
+ gamma_table_size = 256;
+ break;
+ default:
+ gamma_table_raw_size = gamma_table_size = 4096;
+ }
+
+ gamma_values = gamma_table_size / 256;
+
+ DBG (3, "send_gamma: table_raw_size: %lu, table_size: %lu\n",
+ (u_long) gamma_table_raw_size, (u_long) gamma_table_size);
+ DBG (3, "send_gamma: values: %lu, invert_table: %d\n",
+ (u_long) gamma_values, invert_table);
+
+ /* prepare for emulating contrast, brightness ... via the gamma-table */
+ brightness = SANE_UNFIX (s->val[OPT_BRIGHTNESS].w);
+ brightness /= 100;
+ contrast = SANE_UNFIX (s->val[OPT_CONTRAST].w);
+ contrast /= 100;
+
+ DBG (3, "send_gamma: brightness: %f, contrast: %f\n", brightness, contrast);
+
+ gamma_data = malloc (gamma_table_raw_size);
+ if (!gamma_data)
+ return SANE_STATUS_NO_MEM;
+
+ memset (&scmd, 0, sizeof (scmd) );
+
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = 0x81; /* 0x81 for download gamma table */
+ set_triple (scmd.transferlen, gamma_table_raw_size);
+
+ for (color = 0; color < 3 && status == SANE_STATUS_GOOD; ++ color)
+ {
+ /* color: 0=red; 1=green; 2=blue */
+ set_double (scmd.datatypequal, color);
+
+ i = 0; /* big table index */
+ for (j = 0; j < 256; ++ j) /* little table index */
+ {
+ /* calculate mode dependent values v1 and v2
+ * v1 <- current value for table
+ * v2 <- next value for table (for interpolation)
+ */
+ switch (s->c_mode)
+ {
+ case AV_TRUECOLOR:
+ case AV_TRUECOLOR12:
+ case AV_TRUECOLOR16:
+ {
+ v1 = (double) s->gamma_table [1 + color][j];
+ if (j == 255)
+ v2 = (double) v1;
+ else
+ v2 = (double) s->gamma_table [1 + color][j + 1];
+ }
+ break;
+ default:
+ /* for all other modes: */
+ {
+ v1 = (double) s->gamma_table [0][j];
+ if (j == 255)
+ v2 = (double) v1;
+ else
+ v2 = (double) s->gamma_table [0][j + 1];
+ }
+ } /*end switch */
+
+ /* Emulate brightness and contrast (at least the Avision AV6[2,3]0
+ * as well as many others do not have a hardware implementation,
+ * --$. The function was taken from the GIMP source - maybe I'll
+ * optimize it in the future (when I have spare time). */
+
+ v1 /= 255;
+ v2 /= 255;
+
+ v1 = (brightness_contrast_func (brightness, contrast, v1) );
+ v2 = (brightness_contrast_func (brightness, contrast, v2) );
+
+ v1 *= 255;
+ v2 *= 255;
+
+ if (invert_table) {
+ v1 = 255 - v1;
+ v2 = 255 - v2;
+ if (v1 <= 0)
+ v1 = 0;
+ if (v2 <= 0)
+ v2 = 0;
+ }
+
+ for (k = 0; k < gamma_values; ++ k, ++ i) {
+ gamma_data [i] = (uint8_t)
+ (((v1 * (gamma_values - k)) + (v2 * k) ) / (double) gamma_values);
+ }
+ }
+ /* fill the gamma table - (e.g.) if 11bit (old protocol) table */
+ {
+ size_t t_i = i-1;
+ if (i < gamma_table_raw_size) {
+ DBG (4, "send_gamma: (old protocol) - filling the table.\n");
+ for ( ; i < gamma_table_raw_size; ++ i)
+ gamma_data [i] = gamma_data [t_i];
+ }
+ }
+
+ DBG (4, "send_gamma: sending %lu bytes gamma table.\n",
+ (u_long) gamma_table_raw_size);
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ gamma_data, gamma_table_raw_size, 0, 0);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "send_gamma: gamma table upload failed: %s\n",
+ sane_strstatus (status));
+ }
+ }
+ free (gamma_data);
+ return status;
+}
+
+static SANE_Status
+send_3x3_matrix (Avision_Scanner* s)
+{
+ SANE_Status status;
+
+#define SIGN_BIT 0x1000
+#define INT_PART 10
+
+ struct matrix_cmd
+ {
+ struct command_send scmd;
+ struct matrix_3x3 matrix;
+ } cmd;
+
+ /* 04 00 00 00 00 00
+ 00 00 04 00 00 00
+ 00 00 00 00 04 00 */
+
+ int i, a_i;
+ static const double c5_matrix[] =
+ { 1.000, 0.000, 0.000,
+ 0.000, 1.000, 0.000,
+ 0.000, 0.000, 1.000 };
+
+ double a_f, b_f;
+ uint16_t m;
+
+ DBG (3, "send_3x3_matrix:\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ for (i = 0; i < 9; i++)
+ {
+ m = 0;
+ a_f = c5_matrix[i];
+ if (a_f < 0) {
+ m |= SIGN_BIT;
+ a_f = -a_f;
+ }
+
+ a_i = (int) a_f; /* integer */
+ b_f = a_f - (double) a_i; /* float */
+ m |= ((a_i & 0x3) << INT_PART);
+ m |= (uint16_t) (b_f * 1024);
+ set_double (((uint8_t*)(&cmd.matrix.v[i])), m);
+ }
+
+ cmd.scmd.opc = AVISION_SCSI_SEND;
+ cmd.scmd.datatypecode = 0x83; /* 0x83 for 3x3 color matrix */
+ set_triple (cmd.scmd.transferlen, sizeof (struct matrix_3x3));
+
+ if (1) {
+ DBG (3, "send_3x3_matrix: sending matrix split into two commands\n");
+ status = avision_cmd (&s->av_con, &cmd.scmd, sizeof (cmd.scmd),
+ &cmd.matrix, sizeof(cmd.matrix), 0, 0);
+ }
+ else {
+ DBG (3, "send_3x3_matrix: sending matrix in one command\n");
+ status = avision_cmd (&s->av_con, &cmd, sizeof (cmd), 0, 0, 0, 0);
+ }
+
+ return status;
+}
+
+static SANE_Status
+get_acceleration_info (Avision_Scanner* s, struct acceleration_info* info)
+{
+ SANE_Status status;
+
+ struct command_read rcmd;
+ uint8_t result [24];
+ size_t size;
+
+ DBG (3, "get_acceleration_info:\n");
+
+ size = sizeof (result);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0x6c; /* get acceleration information */
+ set_double (rcmd.datatypequal, s->hw->data_dq);
+ set_triple (rcmd.transferlen, size);
+
+ DBG (3, "get_acceleration_info: read_data: %lu bytes\n", (u_long) size);
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size);
+ if (status != SANE_STATUS_GOOD || size != sizeof (result) ) {
+ DBG (1, "get_acceleration_info: read accel. info failed (%s)\n",
+ sane_strstatus (status) );
+ return status;
+ }
+
+ debug_print_accel_info (3, "get_acceleration_info", result);
+
+ info->total_steps = get_double (&(result[0]));
+ info->stable_steps = get_double (&(result[2]));
+ info->table_units = get_quad (&(result[4]));
+ info->base_units = get_quad (&(result[8]));
+ info->start_speed = get_double (&(result[12]));
+ info->target_speed = get_double (&(result[14]));
+ info->ability = result[16];
+ info->table_count = result[17];
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+send_acceleration_table (Avision_Scanner* s)
+{
+ SANE_Status status;
+
+ struct command_send scmd;
+ int table = 0;
+ int i;
+ struct acceleration_info accel_info = accel_info;
+ uint8_t* table_data;
+
+ DBG (3, "send_acceleration_table:\n");
+
+ do {
+ status = get_acceleration_info (s, &accel_info);
+
+ if (accel_info.table_count == 0) {
+ DBG (3, "send_acceleration_table: device does not need tables\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ if (accel_info.target_speed > accel_info.start_speed ||
+ accel_info.target_speed == 0 ||
+ accel_info.total_steps <= accel_info.stable_steps) {
+ DBG (1, "send_acceleration_table: table does not look right.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (accel_info.ability != 0) {
+ DBG (1, "send_acceleration_table: ability non-zero - insert code\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* so far I assume we have one byte tables as used in the C6 ASIC ... */
+ table_data = malloc (accel_info.total_steps + 1000);
+
+ memset (&scmd, 0x00, sizeof (scmd));
+ scmd.opc = AVISION_SCSI_SEND;
+ scmd.datatypecode = 0x6c; /* send acceleration table */
+
+ set_double (scmd.datatypequal, table);
+ set_triple (scmd.transferlen, accel_info.total_steps);
+
+ /* construct the table - Warning: This code is derived from Avision
+ sample code and is a bit scary! I have no idea why the scanner
+ needs such a dumb table and also do not know /why/ it has to be
+ constructed this way. "Works for me" -ReneR */
+ {
+ float low_lim = 0.001;
+ float up_lim = 1.0;
+
+ uint16_t accel_steps = accel_info.total_steps - accel_info.stable_steps + 1;
+
+ /* acceleration ramp */
+ while ((up_lim - low_lim) > 0.0001)
+ {
+ float mid = (up_lim + low_lim) / 2; /* accel rate */
+
+ uint16_t now_count = accel_info.start_speed;
+
+ uint16_t i = 0;
+
+ float now_count_f = now_count;
+ table_data [i++] = (uint8_t) accel_info.start_speed;
+
+ while (now_count != accel_info.target_speed)
+ {
+ now_count_f = now_count_f - (now_count_f -
+ accel_info.target_speed) * mid;
+ now_count = (uint16_t)(now_count_f + 0.5);
+ table_data[i++] = (uint8_t) now_count;
+ }
+
+
+ if (i == accel_steps)
+ break;
+ if (i > accel_steps)
+ low_lim = mid;
+ else
+ up_lim = mid;
+ }
+
+ /* fill stable steps */
+ for (i = accel_steps; i < accel_info.total_steps; i++)
+ table_data [i] = table_data [i-1];
+
+ debug_print_hex_raw (5, "send_acceleration_table: first pass:\n",
+ table_data, accel_info.total_steps);
+
+ /* maybe post fix-up */
+ {
+ int add_count;
+
+ /* count total steps in table */
+ int table_total = 0;
+ for (i = 0; i < accel_info.total_steps; i++)
+ table_total += table_data [i];
+
+ i = 0;
+ if (((table_total * accel_info.table_units) % accel_info.base_units) == 0)
+ add_count = 0;
+ else
+ add_count = (accel_info.base_units -
+ ((table_total*accel_info.table_units) % accel_info.base_units))
+ / accel_info.table_units;
+
+ /* add_count should not be bigger than 255 */
+ if (add_count > 255) {
+ DBG (1, "send_acceleration_table: add_count limited, was: %d\n", add_count);
+ add_count = 255;
+ }
+ for (i = 0; i < accel_info.total_steps - 1 && add_count > 0; i++)
+ {
+ uint16_t temp_count = 255 - table_data [i];
+ temp_count = temp_count > add_count ? add_count : temp_count;
+
+ table_data [i] += (uint8_t) temp_count;
+ add_count -= temp_count;
+ }
+ }
+ }
+
+ debug_print_hex_raw (5, "send_acceleration_table: fixed up:\n",
+ table_data, accel_info.total_steps);
+
+ /* decrease all by one ... */
+ for (i = 0; i < accel_info.total_steps; i++) {
+ table_data[i]--;
+ }
+
+ DBG (1, "send_acceleration_table: sending table %d\n", table);
+
+ debug_print_hex_raw (5, "send_acceleration_table: final:\n",
+ table_data, accel_info.total_steps);
+
+ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd),
+ (char*) table_data, accel_info.total_steps,
+ 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (3, "send_acceleration_table: send_data failed (%s)\n",
+ sane_strstatus (status));
+ }
+
+ free (table_data); table_data = 0;
+
+ table++;
+ } while (table < accel_info.table_count);
+
+
+ return status;
+}
+
+static SANE_Status
+set_window (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+ SANE_Status status;
+ int base_dpi_abs, base_dpi_rel;
+ int transferlen;
+ int paralen;
+
+ int bytes_per_line;
+ int line_count;
+
+ struct {
+ struct command_set_window cmd;
+ struct command_set_window_window window;
+ } cmd;
+
+ DBG (1, "set_window:\n");
+
+ /* plain old scanners, the C3 ASIC HP 53xx and the C6 ASIC HP 74xx
+ and up do use 1200 as base - only the C5 differs */
+ switch (dev->inquiry_asic_type) {
+ case AV_ASIC_C5:
+ base_dpi_abs = 1200;
+ /* round down to the next multiple of 300 */
+ base_dpi_rel = s->avdimen.hw_xres - s->avdimen.hw_xres % 300;
+ if (base_dpi_rel > dev->inquiry_optical_res)
+ base_dpi_rel = dev->inquiry_optical_res;
+ else if (s->avdimen.hw_xres <= 150)
+ base_dpi_rel = 150;
+ break;
+ default:
+ base_dpi_abs = 1200;
+ base_dpi_rel = 1200;
+ }
+
+ DBG (2, "set_window: base_dpi_abs: %d, base_dpi_rel: %d\n", base_dpi_abs, base_dpi_rel);
+
+ /* wipe out anything */
+ memset (&cmd, 0, sizeof (cmd) );
+ cmd.window.descriptor.winid = AV_WINID; /* normally defined to be zero */
+
+ /* optional parameter length to use */
+ paralen = sizeof (cmd.window.avision) - sizeof (cmd.window.avision.type);
+
+ DBG (2, "set_window: base paralen: %d\n", paralen);
+
+ if (dev->hw->feature_type & AV_FUJITSU)
+ paralen += sizeof (cmd.window.avision.type.fujitsu);
+ else if (!dev->inquiry_new_protocol)
+ paralen += sizeof (cmd.window.avision.type.old);
+ else
+ paralen += sizeof (cmd.window.avision.type.normal);
+
+ DBG (2, "set_window: final paralen: %d\n", paralen);
+
+ transferlen = sizeof (cmd.window)
+ - sizeof (cmd.window.avision) + paralen;
+
+ DBG (2, "set_window: transferlen: %d\n", transferlen);
+
+ /* command setup */
+ cmd.cmd.opc = AVISION_SCSI_SET_WINDOW;
+ set_triple (cmd.cmd.transferlen, transferlen);
+ set_double (cmd.window.header.desclen,
+ sizeof (cmd.window.descriptor) + paralen);
+
+ /* resolution parameters */
+ set_double (cmd.window.descriptor.xres, s->avdimen.hw_xres);
+ set_double (cmd.window.descriptor.yres, s->avdimen.hw_yres);
+
+ /* upper left corner x/y as well as width/length in inch * base_dpi
+ - avdimen are world pixels */
+ set_quad (cmd.window.descriptor.ulx, s->avdimen.tlx * base_dpi_abs / s->avdimen.hw_xres);
+ set_quad (cmd.window.descriptor.uly, s->avdimen.tly * base_dpi_abs / s->avdimen.hw_yres);
+
+ set_quad (cmd.window.descriptor.width,
+ s->avdimen.hw_pixels_per_line * base_dpi_rel / s->avdimen.hw_xres + 1);
+ line_count = s->avdimen.hw_lines + 2 * s->avdimen.line_difference + s->avdimen.rear_offset;
+ set_quad (cmd.window.descriptor.length,
+ line_count * base_dpi_rel / s->avdimen.hw_yres + 1);
+
+ /* interlaced duplex scans are twice as long */
+ if (s->avdimen.interlaced_duplex && dev->scanner_type != AV_FILM) {
+ DBG (2, "set_window: interlaced duplex scan, doubled line count\n");
+ line_count *= 2;
+ }
+
+ bytes_per_line = s->avdimen.hw_bytes_per_line;
+
+ set_double (cmd.window.avision.line_width, bytes_per_line);
+ set_double (cmd.window.avision.line_count, line_count);
+
+ /* here go the most significant bits if bigger than 16 bit */
+ if (dev->inquiry_new_protocol && !(dev->hw->feature_type & AV_FUJITSU) ) {
+ DBG (2, "set_window: large data-transfer support (>16bit)!\n");
+ cmd.window.avision.type.normal.line_width_msb =
+ bytes_per_line >> 16;
+ cmd.window.avision.type.normal.line_count_msb =
+ line_count >> 16;
+ }
+
+ if (dev->inquiry_background_raster)
+ cmd.window.avision.type.normal.background_lines = s->val[OPT_BACKGROUND].w;
+
+ /* scanner should use our line-width and count */
+ SET_BIT (cmd.window.avision.bitset1, 6);
+
+ /* set speed */
+ cmd.window.avision.bitset1 |= s->val[OPT_SPEED].w & 0x07; /* only 3 bit */
+
+ /* ADF scan? */
+ DBG (3, "set_window: source mode %d source mode dim %d\n",
+ s->source_mode, s->source_mode_dim);
+
+ if (s->source_mode == AV_ADF ||
+ s->source_mode == AV_ADF_REAR ||
+ s->source_mode == AV_ADF_DUPLEX) {
+ DBG (3, "set_window: filling ADF bits\n");
+ SET_BIT (cmd.window.avision.bitset1, 7);
+
+ /* normal, interlaced duplex scanners */
+ if (dev->inquiry_duplex_interlaced) {
+ DBG (3, "set_window: interlaced duplex type\n");
+ if (s->source_mode == AV_ADF_REAR) {
+ SET_BIT(cmd.window.avision.type.normal.bitset3, 3); /* 0x08 */
+ }
+ if (s->source_mode == AV_ADF_DUPLEX) {
+ SET_BIT(cmd.window.avision.type.normal.bitset3, 4); /* 0x10 */
+ }
+ }
+ else if (s->source_mode == AV_ADF_DUPLEX) /* HP 2-pass duplex */
+ {
+ DBG (3, "set_window: non-interlaced duplex type (HP)\n");
+ SET_BIT(cmd.window.avision.type.normal.bitset3, 0); /* DPLX 0x01 */
+ if (s->val[OPT_ADF_FLIP].w)
+ SET_BIT(cmd.window.avision.type.normal.bitset3, 1); /* FLIP 0x02 */
+ SET_BIT(cmd.window.avision.type.normal.bitset3, 2); /* MIRR 0x04 */
+ }
+ }
+
+ if (s->val[OPT_PAPERLEN].w)
+ set_double (cmd.window.descriptor.paper_length, (int)((double)30.0*1200));
+
+ if ( !(dev->hw->feature_type & AV_FUJITSU) )
+ {
+ /* quality scan option switch */
+ if (s->val[OPT_QSCAN].w == SANE_TRUE) {
+ SET_BIT (cmd.window.avision.type.normal.bitset2, 4);
+ }
+
+ /* quality calibration option switch (inverted! if set == speed) */
+ if (s->val[OPT_QCALIB].w == SANE_FALSE) {
+ SET_BIT (cmd.window.avision.type.normal.bitset2, 3);
+ }
+
+ /* transparency option switch */
+ if (s->source_mode_dim == AV_TRANSPARENT_DIM) {
+ SET_BIT (cmd.window.avision.type.normal.bitset2, 7);
+ }
+
+ if (dev->scanner_type == AV_FILM) {
+ /* TODO: wire to IR exposure option? */
+ cmd.window.avision.type.normal.ir_exposure_time = 100;
+ set_double (cmd.window.avision.type.normal.r_exposure_time, s->val[OPT_EXPOSURE].w);
+ set_double (cmd.window.avision.type.normal.g_exposure_time, s->val[OPT_EXPOSURE].w);
+ set_double (cmd.window.avision.type.normal.b_exposure_time, s->val[OPT_EXPOSURE].w);
+
+ if (s->val[OPT_IR].w)
+ cmd.window.avision.type.normal.bitset3 |= (1 << 0);
+
+ if (s->val[OPT_MULTISAMPLE].w)
+ cmd.window.avision.type.normal.bitset3 |= (1 << 1);
+ }
+ }
+
+ /* fixed values */
+ cmd.window.descriptor.padding_and_bitset = 3;
+ cmd.window.descriptor.vendor_specific = 0xFF;
+ cmd.window.descriptor.paralen = paralen; /* R² was: 9, later 14 */
+
+ /* This is normally unsupported by Avision scanners, and we do this
+ via the gamma table - which works for all devices ... */
+ cmd.window.descriptor.threshold = 128;
+ cmd.window.descriptor.brightness = 128;
+ cmd.window.descriptor.contrast = 128;
+ cmd.window.avision.highlight = 0xFF;
+ cmd.window.avision.shadow = 0x00;
+
+ /* mode dependant settings */
+ switch (s->c_mode)
+ {
+ case AV_THRESHOLDED:
+ cmd.window.descriptor.bpc = 1;
+ cmd.window.descriptor.image_comp = 0;
+ break;
+
+ case AV_DITHERED:
+ cmd.window.descriptor.bpc = 1;
+ cmd.window.descriptor.image_comp = 1;
+ break;
+
+ case AV_GRAYSCALE:
+ cmd.window.descriptor.bpc = 8;
+ cmd.window.descriptor.image_comp = 2;
+ break;
+
+ case AV_GRAYSCALE12:
+ cmd.window.descriptor.bpc = 12;
+ cmd.window.descriptor.image_comp = 2;
+ break;
+
+ case AV_GRAYSCALE16:
+ cmd.window.descriptor.bpc = 16;
+ cmd.window.descriptor.image_comp = 2;
+ break;
+
+ case AV_TRUECOLOR:
+ cmd.window.descriptor.bpc = 8;
+ cmd.window.descriptor.image_comp = 5;
+ break;
+
+ case AV_TRUECOLOR12:
+ cmd.window.descriptor.bpc = 12;
+ cmd.window.descriptor.image_comp = 5;
+ break;
+
+ case AV_TRUECOLOR16:
+ cmd.window.descriptor.bpc = 16;
+ cmd.window.descriptor.image_comp = 5;
+ break;
+
+ default:
+ DBG (1, "Invalid mode. %d\n", s->c_mode);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (color_mode_is_color (s->c_mode)) {
+ cmd.window.avision.bitset1 |= AVISION_FILTER_RGB;
+ }
+ else {
+ if (dev->hw->feature_type & AV_FASTER_WITH_FILTER)
+ cmd.window.avision.bitset1 |= AVISION_FILTER_GREEN;
+ else if (dev->hw->feature_type2 & AV_USE_GRAY_FILTER)
+ cmd.window.avision.bitset1 |= AVISION_FILTER_GRAY;
+ else
+ cmd.window.avision.bitset1 |= AVISION_FILTER_NONE;
+ }
+
+ debug_print_window_descriptor (5, "set_window", &(cmd.window));
+
+ DBG (3, "set_window: sending command. Bytes: %d\n", transferlen);
+ status = avision_cmd (&s->av_con, &cmd, sizeof (cmd.cmd),
+ &(cmd.window), transferlen, 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+get_background_raster (Avision_Scanner* s)
+{
+ const int debug = 0;
+
+ Avision_Device* dev = s->hw;
+ SANE_Status status;
+
+ struct command_read rcmd;
+ size_t size;
+ int bytes_per_line, i;
+ const int bpp = color_mode_is_color (s->c_mode) ? 3 : 1;
+ const int lines = s->val[OPT_BACKGROUND].w * (s->avdimen.interlaced_duplex ? 2 : 1);
+
+ uint8_t* background = NULL;
+
+ DBG (1, "get_background_raster:\n");
+
+ if (lines == 0) {
+ DBG (1, "get_background_raster: no background requested\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* full width, always :-(, duplex *2 for front and rear */
+ bytes_per_line = dev->inquiry_background_raster_pixel *
+ s->avdimen.hw_xres / dev->inquiry_optical_res;
+ bytes_per_line *= bpp;
+
+ DBG (3, "get_background_raster: native raster pixels: %d, raster bytes_per_line: %d\n",
+ dev->inquiry_background_raster_pixel, bytes_per_line);
+
+ /* according to spec only 8-bit gray or color, TODO: test for bi-level scans */
+ size = bytes_per_line * lines;
+
+ DBG (3, "get_background_raster: buffer size: %ld\n", (long)size);
+
+ background = s->background_raster = realloc (s->background_raster, size);
+ if (!background)
+ return SANE_STATUS_NO_MEM;
+
+ memset (&rcmd, 0, sizeof (rcmd));
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0x9b; /* get background raster */
+ set_double (rcmd.datatypequal, s->hw->data_dq);
+
+ /* Ok, well - this part is very messy. The AV122 and DM152 appear to
+ contain differently buggy ASICs. The only combination I found to
+ at least get a correct front raster out of them is to read it
+ line by line and then every second line appears to be valid front
+ data, ... */
+
+ /* read the raster data */
+ for (i = 0; i < lines;)
+ {
+ uint8_t* dst_raster = background + bytes_per_line * i;
+ /* read stripe by stripe, or all in one chunk */
+ size_t this_read, read_size;
+ int this_lines;
+
+ if (dev->hw->feature_type & AV_2ND_LINE_INTERLACED) {
+ if (dev->hw->feature_type & AV_BACKGROUND_QUIRK)
+ this_lines = 1;
+ else
+ this_lines = lines;
+ }
+ else {
+ this_lines = s->val[OPT_BACKGROUND].w;
+ }
+ this_read = bytes_per_line * this_lines;
+
+ DBG (3, "get_background_raster: line: %d, lines: %d, %lu bytes\n",
+ i, this_lines, (u_long) this_read);
+
+ set_triple (rcmd.transferlen, this_read);
+
+ read_size = this_read;
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, dst_raster, &read_size);
+ if (status != SANE_STATUS_GOOD || read_size != this_read) {
+ DBG (1, "get_background_raster: read raster failed (%s)\n",
+ sane_strstatus (status) );
+ return status;
+ }
+
+ i += this_lines;
+ }
+
+ /* dump raw result while debugging */
+ if (debug)
+ {
+ FILE* f = NULL;
+ f = fopen ("background-raw.pnm", "w");
+
+ write_pnm_header (f, (color_mode_is_color (s->c_mode) ? AV_TRUECOLOR : AV_GRAYSCALE), 8,
+ bytes_per_line / bpp, lines);
+
+ fwrite (background, 1, bytes_per_line * lines, f);
+ fclose (f);
+ }
+
+ /* line-pack - move to unified processing flow, later */
+ if (dev->inquiry_needs_line_pack)
+ {
+ /* TODO: add 16bit per sample code? */
+ int l, p;
+
+ uint8_t* tmp_data = malloc (bytes_per_line);
+ for (l = 0; l < lines; ++l)
+ {
+ uint8_t* out_data = tmp_data;
+ uint8_t* r_ptr = background + (bytes_per_line * l);
+ uint8_t* g_ptr = r_ptr + bytes_per_line / bpp;
+ uint8_t* b_ptr = g_ptr + bytes_per_line / bpp;
+
+ for (p = 0; p < bytes_per_line;) {
+ out_data [p++] = *(r_ptr++);
+ out_data [p++] = *(g_ptr++);
+ out_data [p++] = *(b_ptr++);
+ }
+
+ memcpy (background + (bytes_per_line * l), tmp_data, bytes_per_line);
+ }
+
+ free (tmp_data);
+ } /* end line pack */
+
+ /* deinterlace? */
+ if (s->avdimen.interlaced_duplex && dev->hw->feature_type & AV_2ND_LINE_INTERLACED)
+ {
+ uint8_t* deinterlaced = malloc (size * 2);
+ if (!deinterlaced)
+ return SANE_STATUS_NO_MEM;
+
+ for (i = 0; i < lines; ++i)
+ {
+ int dst_i = i / 2 + (i % 2) * (lines / 2);
+ uint8_t* dst_raster; /* just no C99 in SANE :-( */
+ uint8_t* src_raster;
+
+ /* for the quirky devices and some resolutions the interlacing differs */
+ if (dev->hw->feature_type & AV_BACKGROUND_QUIRK && s->avdimen.hw_xres >= 150)
+ dst_i = i / 2 + ((i+1) % 2) * (lines / 2);
+
+ dst_raster = deinterlaced + bytes_per_line * dst_i;
+ src_raster = background + bytes_per_line * i;
+
+ DBG(3, "get_background_raster: deinterlaced %d -> %d\n", i, dst_i);
+ memcpy(dst_raster, src_raster, bytes_per_line);
+ }
+
+ free (background);
+ background = s->background_raster = deinterlaced;
+ }
+
+ /* dump raw result while debugging */
+ for (i = 0; debug && i < (s->avdimen.interlaced_duplex ? 2 : 1); ++i)
+ {
+ FILE* f = NULL;
+ uint8_t* raster = background;
+ if (i == 0) {
+ f = fopen ("background.pnm", "w");
+ }
+ else {
+ f = fopen ("background-rear.pnm", "w");
+ raster += bytes_per_line * s->val[OPT_BACKGROUND].w;
+ }
+
+ write_pnm_header (f, (color_mode_is_color (s->c_mode) ? AV_TRUECOLOR : AV_GRAYSCALE), 8,
+ bytes_per_line / bpp, s->val[OPT_BACKGROUND].w);
+
+ fwrite (raster, 1, bytes_per_line * s->val[OPT_BACKGROUND].w, f);
+ fclose (f);
+ }
+
+ /* crop from full-width scanlines to scan window */
+ {
+ uint8_t *dst_ptr, *src_ptr;
+ dst_ptr = background;
+ src_ptr = background + s->avdimen.tlx * bpp;
+ for (i = 0; i < lines; ++i)
+ {
+ memmove (dst_ptr, src_ptr, s->avdimen.hw_bytes_per_line);
+ dst_ptr += s->avdimen.hw_bytes_per_line;
+ src_ptr += bytes_per_line;
+ }
+ }
+
+ /* soft-scale - move to unified processing flow, later */
+ if (s->avdimen.hw_xres != s->avdimen.xres)
+ {
+ const uint8_t* out_data = background;
+ uint8_t* dst = background;
+
+ int l;
+ for (l = 0; l < lines; ++l)
+ {
+ const int hwbpl = s->avdimen.hw_bytes_per_line;
+ const int sy = l;
+
+ int x;
+ for (x = 0; x < s->params.pixels_per_line; ++x)
+ {
+ const double bx = (-1.0 + s->avdimen.hw_pixels_per_line) * x / s->params.pixels_per_line;
+ const int sx = (int)floor(bx);
+ const int xdist = (int) ((bx - sx) * 256);
+ const int sxx = sx + 1;
+
+ switch (bpp) {
+ case 1:
+ {
+ uint8_t v =
+ ( out_data [sy*hwbpl + sx ] * (256-xdist) +
+ out_data [sy*hwbpl + sxx] * xdist
+ ) / (256);
+ *dst++ = v;
+ }
+ break;
+
+ case 3:
+ {
+ int c;
+ for (c = 0; c < 3; ++c)
+ {
+ uint8_t v =
+ ( out_data [sy*hwbpl + sx*3 + c] * (256-xdist) +
+ out_data [sy*hwbpl + sxx*3 + c] * xdist
+ ) / (256);
+ *dst++ = v;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /* dump final result while debugging */
+ if (debug) {
+ for (i = 0; i < (s->avdimen.interlaced_duplex ? 2 : 1); ++i)
+ {
+ FILE* f = NULL;
+ uint8_t* raster = background;
+ if (i == 0) {
+ f = fopen ("background-final.pnm", "w");
+ }
+ else {
+ f = fopen ("background-final-rear.pnm", "w");
+ raster += s->params.bytes_per_line * s->val[OPT_BACKGROUND].w;
+ }
+
+ write_pnm_header (f, (color_mode_is_color (s->c_mode) ? AV_TRUECOLOR : AV_GRAYSCALE), 8,
+ s->params.bytes_per_line / bpp, s->val[OPT_BACKGROUND].w);
+
+ fwrite (raster, 1, s->params.bytes_per_line * s->val[OPT_BACKGROUND].w, f);
+ fclose (f);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+reserve_unit (Avision_Scanner* s)
+{
+ char cmd[] =
+ {AVISION_SCSI_RESERVE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (1, "reserve_unit:\n");
+
+ status = avision_cmd (&s->av_con, cmd, sizeof (cmd), 0, 0, 0, 0);
+ return status;
+}
+
+static SANE_Status
+release_unit (Avision_Scanner* s, int type)
+{
+ char cmd[] =
+ {AVISION_SCSI_RELEASE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (1, "release unit: type: %d\n", type);
+ cmd[5] = type; /* latest scanners also allow 1: release paper and 2: end job */
+ status = avision_cmd (&s->av_con, cmd, sizeof (cmd), 0, 0, 0, 0);
+ return status;
+}
+
+/* Check if a sheet is present. */
+static SANE_Status
+media_check (Avision_Scanner* s)
+{
+ char cmd[] = {AVISION_SCSI_MEDIA_CHECK, 0, 0, 0, 1, 0}; /* 1, 4 */
+ SANE_Status status;
+ uint8_t result[1]; /* 4 */
+ size_t size = sizeof(result);
+
+ status = avision_cmd (&s->av_con, cmd, sizeof (cmd),
+ 0, 0, result, &size);
+
+ debug_print_raw (5, "media_check: result\n", result, size);
+
+ if (status == SANE_STATUS_GOOD) {
+ if (!(result[0] & 0x1))
+ status = SANE_STATUS_NO_DOCS;
+ }
+
+ return status;
+}
+
+#if 0 /* unused */
+static SANE_Status
+flush_media (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+ SANE_Status status;
+
+ if (s->source_mode_dim == AV_ADF_DIM && dev->inquiry_batch_scan)
+ {
+ DBG (1, "flush_media: flushing pages out of batch scanner\n");
+ do {
+ status = media_check (s);
+ if (status == SANE_STATUS_GOOD) {
+ SANE_Status status2 = reserve_unit (s);
+ DBG (1, "flush_media: reserve status: %d\n", status2);
+ status2 = release_unit (s, 0);
+ DBG (1, "flush_media: release status: %d\n", status2);
+ }
+ } while (status == SANE_STATUS_GOOD);
+ }
+ return SANE_STATUS_GOOD;
+}
+#endif /* 0 - unused */
+
+static SANE_Status
+object_position (Avision_Scanner* s, uint8_t position)
+{
+ SANE_Status status;
+
+ uint8_t cmd [10];
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = AVISION_SCSI_OBJECT_POSITION;
+ cmd[1] = position;
+
+ DBG (1, "object_position: %d\n", position);
+
+ status = avision_cmd (&s->av_con, cmd, sizeof(cmd), 0, 0, 0, 0);
+ return status;
+}
+
+static SANE_Status
+start_scan (Avision_Scanner* s)
+{
+ struct command_scan cmd;
+
+ size_t size = sizeof (cmd);
+
+ DBG (3, "start_scan:\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opc = AVISION_SCSI_SCAN;
+ cmd.transferlen = 1;
+
+ /* AV610C2 in ADF preview mode does not detect the page end (...) */
+ if (s->val[OPT_PREVIEW].w == SANE_TRUE && s->hw->inquiry_asic_type != AV_ASIC_C7) {
+ SET_BIT(cmd.bitset1,6);
+ }
+
+ if (s->val[OPT_QSCAN].w == SANE_TRUE) {
+ SET_BIT(cmd.bitset1,7);
+ }
+
+ DBG (3, "start_scan: sending command. Bytes: %lu\n", (u_long) size);
+ return avision_cmd (&s->av_con, &cmd, size, 0, 0, 0, 0);
+}
+
+static SANE_Status
+do_eof (Avision_Scanner *s)
+{
+ int exit_status;
+
+ DBG (3, "do_eof:\n");
+
+ /* we do not scan anymore */
+ s->prepared = s->scanning = SANE_FALSE;
+
+ /* we can now mark the rear data as valid */
+ if (s->avdimen.interlaced_duplex ||
+ (s->hw->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX)) {
+ DBG (3, "do_eof: toggling duplex rear data valid\n");
+ s->duplex_rear_valid = !s->duplex_rear_valid;
+ DBG (3, "do_eof: duplex rear data valid: %x\n",
+ s->duplex_rear_valid);
+ }
+
+ if (s->read_fds >= 0) {
+ close (s->read_fds);
+ s->read_fds = -1;
+ }
+
+ /* join our processes - without a wait() you will produce zombies
+ (defunct children) */
+ sanei_thread_waitpid (s->reader_pid, &exit_status);
+ s->reader_pid = -1;
+
+ DBG (3, "do_eof: returning %d\n", exit_status);
+ return (SANE_Status)exit_status;
+}
+
+static SANE_Status
+do_cancel (Avision_Scanner* s)
+{
+ DBG (3, "do_cancel:\n");
+
+ s->prepared = s->scanning = SANE_FALSE;
+ s->duplex_rear_valid = SANE_FALSE;
+ s->page = 0;
+
+ if (s->reader_pid != -1) {
+ int exit_status;
+
+ /* ensure child knows it's time to stop: */
+ sanei_thread_kill (s->reader_pid);
+ sanei_thread_waitpid (s->reader_pid, &exit_status);
+ s->reader_pid = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+static SANE_Status
+read_data (Avision_Scanner* s, SANE_Byte* buf, size_t* count)
+{
+ struct command_read rcmd;
+ SANE_Status status;
+
+ DBG (9, "read_data: %lu\n", (u_long) *count);
+
+ memset (&rcmd, 0, sizeof (rcmd));
+
+ rcmd.opc = AVISION_SCSI_READ;
+ rcmd.datatypecode = 0x00; /* read image data */
+ set_double (rcmd.datatypequal, s->hw->data_dq);
+ set_triple (rcmd.transferlen, *count);
+
+ status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, buf, count);
+
+ return status;
+}
+
+static SANE_Status
+init_options (Avision_Scanner* s)
+{
+ Avision_Device* dev = s->hw;
+ int i;
+
+ DBG (3, "init_options:\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++ i) {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Init the SANE option from the scanner inquiry data */
+
+ dev->x_range.max = SANE_FIX ( (int)dev->inquiry_x_ranges[s->source_mode_dim]);
+ dev->x_range.quant = 0;
+ dev->y_range.max = SANE_FIX ( (int)dev->inquiry_y_ranges[s->source_mode_dim]);
+ dev->y_range.quant = 0;
+
+ switch (dev->inquiry_asic_type) {
+ case AV_ASIC_C2:
+ dev->dpi_range.min = 100;
+ break;
+ case AV_ASIC_C5:
+ dev->dpi_range.min = 80;
+ break;
+ case AV_ASIC_C6: /* TODO: AV610 in ADF mode does not scan less than 180 or so */
+ dev->dpi_range.min = 50;
+ break;
+ case AV_ASIC_C7: /* AV610C2 empirically tested out */
+ dev->dpi_range.min = 75;
+ break;
+ default:
+ dev->dpi_range.min = 50;
+ }
+ DBG (1, "init_options: dpi_range.min set to %d\n", dev->dpi_range.min);
+
+ dev->dpi_range.quant = 1; /* any, including 72, 144, etc. */
+ dev->dpi_range.max = dev->inquiry_max_res;
+
+ dev->speed_range.min = (SANE_Int)0;
+ dev->speed_range.max = (SANE_Int)4;
+ dev->speed_range.quant = (SANE_Int)1;
+
+ s->opt[OPT_NUM_OPTS].name = "";
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = "";
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].size = sizeof(SANE_TYPE_INT);
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE_GROUP].desc = ""; /* for groups only title and type are valid */
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* color mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (dev->color_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = dev->color_list;
+ s->val[OPT_MODE].s = strdup (dev->color_list[dev->color_list_default]);
+ s->c_mode = match_color_mode (dev, s->val[OPT_MODE].s);
+
+ /* source mode */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size(dev->source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = &dev->source_list[0];
+ s->val[OPT_SOURCE].s = strdup(dev->source_list[0]);
+ s->source_mode = match_source_mode (dev, s->val[OPT_SOURCE].s);
+ s->source_mode_dim = match_source_mode_dim (s->source_mode);
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &dev->dpi_range;
+ s->val[OPT_RESOLUTION].w = OPT_RESOLUTION_DEFAULT;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* speed option */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ s->opt[OPT_SPEED].type = SANE_TYPE_INT;
+ s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SPEED].constraint.range = &dev->speed_range;
+ s->val[OPT_SPEED].w = 0;
+ if (dev->scanner_type == AV_SHEETFEED)
+ s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = ""; /* for groups only title and type are valid */
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &dev->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &dev->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &dev->x_range;
+ s->val[OPT_BR_X].w = dev->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &dev->y_range;
+ s->val[OPT_BR_Y].w = dev->y_range.max;
+
+ /* overscan top */
+ s->opt[OPT_OVERSCAN_TOP].name = "overscan-top";
+ s->opt[OPT_OVERSCAN_TOP].title = "Overscan top";
+ s->opt[OPT_OVERSCAN_TOP].desc = "The top overscan controls the additional area to scan before the paper is detected.";
+ s->opt[OPT_OVERSCAN_TOP].type = SANE_TYPE_FIXED;
+ s->opt[OPT_OVERSCAN_TOP].unit = SANE_UNIT_MM;
+ s->opt[OPT_OVERSCAN_TOP].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVERSCAN_TOP].constraint.range = &overscan_range;
+ s->val[OPT_OVERSCAN_TOP].w = SANE_FIX(0);
+
+ /* overscan bottom */
+ s->opt[OPT_OVERSCAN_BOTTOM].name = "overscan-bottom";
+ s->opt[OPT_OVERSCAN_BOTTOM].title = "Overscan bottom";
+ s->opt[OPT_OVERSCAN_BOTTOM].desc = "The bottom overscan controls the additional area to scan after the paper end is detected.";
+ s->opt[OPT_OVERSCAN_BOTTOM].type = SANE_TYPE_FIXED;
+ s->opt[OPT_OVERSCAN_BOTTOM].unit = SANE_UNIT_MM;
+ s->opt[OPT_OVERSCAN_BOTTOM].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVERSCAN_BOTTOM].constraint.range = &overscan_range;
+ s->val[OPT_OVERSCAN_BOTTOM].w = SANE_FIX(0);
+
+ if (!dev->inquiry_tune_scan_length)
+ s->opt[OPT_OVERSCAN_TOP].cap |= SANE_CAP_INACTIVE;
+ if (!dev->inquiry_tune_scan_length)
+ s->opt[OPT_OVERSCAN_BOTTOM].cap |= SANE_CAP_INACTIVE;
+
+ /* background raster */
+ s->opt[OPT_BACKGROUND].name = "background-lines";
+ s->opt[OPT_BACKGROUND].title = "Background raster lines";
+ s->opt[OPT_BACKGROUND].desc = "The background raster controls the additional background lines to scan before the paper is feed through the scanner.";
+ s->opt[OPT_BACKGROUND].type = SANE_TYPE_INT;
+ s->opt[OPT_BACKGROUND].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BACKGROUND].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BACKGROUND].constraint.range = &background_range;
+ s->val[OPT_BACKGROUND].w = 0;
+
+ if (!dev->inquiry_background_raster) {
+ s->opt[OPT_BACKGROUND].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* for groups only title and type are valid */
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ if (disable_gamma_table)
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS].w = SANE_FIX(0);
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ if (disable_gamma_table)
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST].w = SANE_FIX(0);
+
+ /* Quality Scan */
+ s->opt[OPT_QSCAN].name = "quality-scan";
+ s->opt[OPT_QSCAN].title = "Quality scan";
+ s->opt[OPT_QSCAN].desc = "Turn on quality scanning (slower but better).";
+ s->opt[OPT_QSCAN].type = SANE_TYPE_BOOL;
+ s->opt[OPT_QSCAN].unit = SANE_UNIT_NONE;
+ s->val[OPT_QSCAN].w = SANE_TRUE;
+
+ /* Quality Calibration */
+ s->opt[OPT_QCALIB].name = SANE_NAME_QUALITY_CAL;
+ s->opt[OPT_QCALIB].title = SANE_TITLE_QUALITY_CAL;
+ s->opt[OPT_QCALIB].desc = SANE_DESC_QUALITY_CAL;
+ s->opt[OPT_QCALIB].type = SANE_TYPE_BOOL;
+ s->opt[OPT_QCALIB].unit = SANE_UNIT_NONE;
+ s->val[OPT_QCALIB].w = SANE_TRUE;
+
+ /* gray scale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+
+ if (!disable_gamma_table)
+ {
+ if (color_mode_is_color (s->c_mode)) {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* exposure */
+ s->opt[OPT_EXPOSURE].name = "exposure";
+ s->opt[OPT_EXPOSURE].title = "Exposure";
+ s->opt[OPT_EXPOSURE].desc = "Manual exposure adjustment.";
+ s->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ s->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_EXPOSURE].constraint.range = &exposure_range;
+ s->val[OPT_EXPOSURE].w = 100;
+
+ if (!dev->inquiry_exposure_control) {
+ s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* Multi sample */
+ s->opt[OPT_MULTISAMPLE].name = "multi-sample";
+ s->opt[OPT_MULTISAMPLE].title = "Multi-sample";
+ s->opt[OPT_MULTISAMPLE].desc = "Enable multi-sample scan mode.";
+ s->opt[OPT_MULTISAMPLE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_MULTISAMPLE].unit = SANE_UNIT_NONE;
+ s->val[OPT_MULTISAMPLE].w = SANE_FALSE;
+
+ /* TODO: No idea how to detect, assume exposure control devices are
+ new enough to support this, for now. -ReneR */
+ if (!dev->inquiry_exposure_control) {
+ s->opt[OPT_MULTISAMPLE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* Infra-red */
+ s->opt[OPT_IR].name = "infra-red";
+ s->opt[OPT_IR].title = "Infra-red";
+ s->opt[OPT_IR].desc = "Enable infra-red scan mode.";
+ s->opt[OPT_IR].type = SANE_TYPE_BOOL;
+ s->opt[OPT_IR].unit = SANE_UNIT_NONE;
+ s->val[OPT_IR].w = SANE_FALSE;
+
+ /* TODO: No idea how to detect, assume exposure control devices are
+ new enough to support this, for now. -ReneR */
+ if (!dev->inquiry_exposure_control) {
+ s->opt[OPT_IR].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "MISC" group: */
+ s->opt[OPT_MISC_GROUP].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MISC_GROUP].desc = ""; /* for groups only title and type are valid */
+ s->opt[OPT_MISC_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MISC_GROUP].cap = 0;
+ s->opt[OPT_MISC_GROUP].size = 0;
+ s->opt[OPT_MISC_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* film holder control */
+ if (dev->scanner_type != AV_FILM)
+ s->opt[OPT_FRAME].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_FRAME].name = SANE_NAME_FRAME;
+ s->opt[OPT_FRAME].title = SANE_TITLE_FRAME;
+ s->opt[OPT_FRAME].desc = SANE_DESC_FRAME;
+ s->opt[OPT_FRAME].type = SANE_TYPE_INT;
+ s->opt[OPT_FRAME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FRAME].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_FRAME].constraint.range = &dev->frame_range;
+ s->val[OPT_FRAME].w = dev->current_frame;
+
+ /* power save time */
+ if (!dev->inquiry_power_save_time)
+ s->opt[OPT_POWER_SAVE_TIME].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_POWER_SAVE_TIME].name = "power-save-time";
+ s->opt[OPT_POWER_SAVE_TIME].title = "Power save timer control";
+ s->opt[OPT_POWER_SAVE_TIME].desc = "Allows control of the scanner's power save timer, dimming or turning off the light.";
+ s->opt[OPT_POWER_SAVE_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_POWER_SAVE_TIME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_POWER_SAVE_TIME].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_POWER_SAVE_TIME].w = 0;
+
+ /* message, like options set on the scanner, LED no. & co */
+ s->opt[OPT_MESSAGE].name = "message";
+ s->opt[OPT_MESSAGE].title = "message text from the scanner";
+ s->opt[OPT_MESSAGE].desc = "This text contains device specific options controlled by the user on the scanner hardware.";
+ s->opt[OPT_MESSAGE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MESSAGE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->opt[OPT_MESSAGE].size = 129;
+ s->opt[OPT_MESSAGE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_MESSAGE].s = malloc(s->opt[OPT_MESSAGE].size);
+ s->val[OPT_MESSAGE].s[0] = 0;
+
+ /* NVRAM */
+ s->opt[OPT_NVRAM].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if (!dev->inquiry_nvram_read)
+ s->opt[OPT_NVRAM].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_NVRAM].name = "nvram-values";
+ s->opt[OPT_NVRAM].title = "Obtain NVRAM values";
+ s->opt[OPT_NVRAM].desc = "Allows access obtaining the scanner's NVRAM values as pretty printed text.";
+ s->opt[OPT_NVRAM].type = SANE_TYPE_STRING;
+ s->opt[OPT_NVRAM].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NVRAM].size = 1024;
+ s->opt[OPT_NVRAM].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NVRAM].s = malloc(s->opt[OPT_NVRAM].size);
+ s->val[OPT_NVRAM].s[0] = 0;
+
+ /* paper_length */
+ s->opt[OPT_PAPERLEN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if (!dev->inquiry_paper_length)
+ s->opt[OPT_PAPERLEN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_PAPERLEN].name = "paper-length";
+ s->opt[OPT_PAPERLEN].title = "Use paper length";
+ s->opt[OPT_PAPERLEN].desc = "Newer scanners can utilize this paper length to detect double feeds. However some others (DM152) can get confused during media flush if it is set.";
+ s->opt[OPT_PAPERLEN].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PAPERLEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_PAPERLEN].size = sizeof(SANE_Word);
+ s->opt[OPT_PAPERLEN].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PAPERLEN].w = SANE_FALSE;
+
+ /* ADF page flipping */
+ s->opt[OPT_ADF_FLIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
+ if (!(s->hw->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX))
+ s->opt[OPT_ADF_FLIP].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ADF_FLIP].name = "flip-page";
+ s->opt[OPT_ADF_FLIP].title = "Flip document after duplex scanning";
+ s->opt[OPT_ADF_FLIP].desc = "Tells page-flipping document scanners to flip the paper back to its original orientation before dropping it in the output tray. Turning this off might make scanning a little faster if you don't care about manually flipping the pages afterwards.";
+ s->opt[OPT_ADF_FLIP].type = SANE_TYPE_BOOL;
+ s->opt[OPT_ADF_FLIP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ADF_FLIP].size = sizeof(SANE_Word);
+ s->opt[OPT_ADF_FLIP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_ADF_FLIP].w = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* This function is executed as a child process. The reason this is
+ executed as a subprocess is because some (most?) generic SCSI
+ interfaces block a SCSI request until it has completed. With a
+ subprocess, we can let it block waiting for the request to finish
+ while the main process can go about to do more important things
+ (such as recognizing when the user presses a cancel button).
+
+ WARNING: Since this is executed as a subprocess, it's NOT possible
+ to update any of the variables in the main process (in particular
+ the scanner state cannot be updated). */
+
+static int
+reader_process (void *data)
+{
+ struct Avision_Scanner *s = (struct Avision_Scanner *) data;
+ int fd = s->write_fds;
+
+ Avision_Device* dev = s->hw;
+
+ SANE_Status status;
+ SANE_Status exit_status = SANE_STATUS_GOOD;
+ sigset_t sigterm_set;
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ FILE* fp;
+ FILE* rear_fp = 0; /* used to store the deinterlaced rear data */
+ FILE* raw_fp = 0; /* used to write the RAW image data for debugging */
+
+ /* the complex params */
+ unsigned int lines_per_stripe;
+ unsigned int lines_per_output;
+ unsigned int max_bytes_per_read;
+
+ SANE_Bool gray_mode;
+
+ /* the simple params for the data reader */
+ int hw_line = 0;
+ int line = 0;
+
+ unsigned int stripe_size;
+ unsigned int stripe_fill;
+ unsigned int out_size;
+
+ size_t total_size;
+ size_t processed_bytes;
+
+ enum {
+ NONE, /* do not de-interlace at all */
+ STRIPE, /* every 2nd stripe */
+ HALF, /* the 2nd half */
+ LINE /* every 2nd line */
+ } deinterlace = NONE;
+
+ /* the fat strip we currently puzzle together to perform software-colorpack
+ and more */
+ uint8_t* stripe_data;
+ /* the corrected output data */
+ uint8_t* out_data;
+ /* interpolation output data, one line */
+ uint8_t* ip_history = 0;
+ uint8_t* ip_data = 0;
+
+ DBG (3, "reader_process:\n");
+
+ if (sanei_thread_is_forked())
+ close (s->read_fds);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ gray_mode = color_mode_is_shaded (s->c_mode);
+
+ if (s->avdimen.interlaced_duplex) {
+ deinterlace = STRIPE;
+
+ if ( (dev->hw->feature_type & AV_NON_INTERLACED_DUPLEX_300) &&
+ (s->avdimen.hw_xres <= 300 && s->avdimen.hw_yres <= 300) )
+ deinterlace = HALF;
+ if (dev->hw->feature_type & AV_2ND_LINE_INTERLACED)
+ deinterlace = LINE;
+
+ if (dev->scanner_type == AV_FILM)
+ deinterlace = LINE;
+ }
+
+ fp = fdopen (fd, "w");
+ if (!fp)
+ return SANE_STATUS_NO_MEM;
+
+ /* start scan ? */
+ if ((deinterlace == NONE && !((dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && s->duplex_rear_valid)) ||
+ (deinterlace != NONE && !s->duplex_rear_valid))
+ {
+ /* reserve unit - in the past we did this in open - but the
+ windows driver does reserves for each scan and some ADF
+ devices need a release for each sheet anyway ... */
+ status = reserve_unit (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "reader_process: reserve_unit failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (dev->hw->feature_type & AV_NO_START_SCAN) {
+ DBG (1, "reader_process: start_scan skipped due to device-list!\n");
+ }
+ else {
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "reader_process: start_scan failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (dev->hw->feature_type & AV_ACCEL_TABLE)
+ /* (s->hw->inquiry_asic_type == AV_ASIC_C6) */ {
+ status = send_acceleration_table (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "reader_process: send_acceleration_table failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ }
+
+ /* setup file i/o for deinterlacing scans or if we are the back page with a flipping duplexer */
+ if (deinterlace != NONE ||
+ (dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX && !(s->page % 2)))
+ {
+ if (!s->duplex_rear_valid) { /* create new file for writing */
+ DBG (3, "reader_process: opening duplex rear file for writing.\n");
+ rear_fp = fopen (s->duplex_rear_fname, "w");
+ if (! rear_fp) {
+ fclose (fp);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ else { /* open saved rear data */
+ DBG (3, "reader_process: opening duplex rear file for reading.\n");
+ rear_fp = fopen (s->duplex_rear_fname, "r");
+ if (! rear_fp) {
+ fclose (fp);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ /* it takes quite a few lines to saturate the (USB) bus */
+ lines_per_stripe = dev->read_stripe_size;
+ if (s->avdimen.line_difference)
+ lines_per_stripe += 2 * s->avdimen.line_difference;
+
+ stripe_size = s->avdimen.hw_bytes_per_line * lines_per_stripe;
+ lines_per_output = lines_per_stripe - 2 * s->avdimen.line_difference;
+
+ if (s->av_con.connection_type == AV_SCSI)
+ /* maybe better not /2 ... */
+ max_bytes_per_read = dev->scsi_buffer_size / 2;
+ else
+ /* vast buffer size to saturate the bus */
+ max_bytes_per_read = 0x100000;
+
+ out_size = s->avdimen.hw_bytes_per_line * lines_per_output;
+
+ DBG (3, "dev->scsi_buffer_size / 2: %d\n",
+ dev->scsi_buffer_size / 2);
+
+ DBG (3, "bytes_per_line: %d, pixels_per_line: %d\n",
+ s->avdimen.hw_bytes_per_line, s->avdimen.hw_pixels_per_line);
+
+ DBG (3, "lines_per_stripe: %d, lines_per_output: %d\n",
+ lines_per_stripe, lines_per_output);
+
+ DBG (3, "max_bytes_per_read: %d, stripe_size: %d, out_size: %d\n",
+ max_bytes_per_read, stripe_size, out_size);
+
+ stripe_data = malloc (stripe_size);
+
+ /* for software scaling we need an additional interpolation line buffer */
+ if (s->avdimen.hw_xres != s->avdimen.xres ||
+ s->avdimen.hw_yres != s->avdimen.yres)
+ {
+ /* layout out_data so that the interpolation history is exactly in front */
+ ip_history = malloc (s->avdimen.hw_bytes_per_line + out_size);
+ out_data = ip_history + s->avdimen.hw_bytes_per_line;
+
+ ip_data = malloc (s->params.bytes_per_line);
+ }
+ else {
+ out_data = malloc (out_size);
+ }
+
+ /* calculate params for the reading loop */
+ total_size = s->avdimen.hw_bytes_per_line * (s->avdimen.hw_lines +
+ 2 * s->avdimen.line_difference +
+ s->avdimen.rear_offset);
+ if (deinterlace != NONE && !s->duplex_rear_valid)
+ total_size *= 2;
+ DBG (3, "reader_process: total_size: %lu\n", (u_long) total_size);
+
+ /* write a RAW PNM file for debugging -ReneR */
+ if (0 /* DEBUG */ &&
+ (deinterlace == NONE || (deinterlace != NONE && !s->duplex_rear_valid)) )
+ {
+ raw_fp = fopen ("/tmp/sane-avision.raw", "w");
+ write_pnm_header (fp, s->c_mode, s->params.depth,
+ s->avdimen.hw_pixels_per_line, total_size / s->avdimen.hw_bytes_per_line);
+ }
+
+ processed_bytes = 0;
+ stripe_fill = 0;
+
+ /* First, dump background raster, bypassing all the other processing. */
+ if (dev->inquiry_background_raster && s->val[OPT_BACKGROUND].w)
+ {
+ uint8_t* background = s->background_raster;
+ if (s->duplex_rear_valid)
+ background += s->params.bytes_per_line * s->val[OPT_BACKGROUND].w;
+
+ DBG (5, "reader_process: dumping background raster\n");
+ fwrite (background, s->params.bytes_per_line, s->val[OPT_BACKGROUND].w, fp);
+ }
+
+ /* Data read; loop until all data has been processed. Might exit
+ before all lines are transferred for ADF paper end. */
+ while (exit_status == SANE_STATUS_GOOD && processed_bytes < total_size)
+ {
+ unsigned int useful_bytes;
+
+ DBG (5, "reader_process: stripe filled: %d\n", stripe_fill);
+
+ /* fill the stripe buffer with real data */
+ while (!s->duplex_rear_valid &&
+ processed_bytes < total_size && stripe_fill < stripe_size &&
+ exit_status == SANE_STATUS_GOOD)
+ {
+ size_t this_read = stripe_size - stripe_fill;
+
+ /* Limit reads to max_bytes_per_read and global data
+ boundaries. Rounded to the next lower multiple of
+ byte_per_lines, otherwise some scanners freeze. */
+ if (this_read > max_bytes_per_read)
+ this_read = (max_bytes_per_read -
+ max_bytes_per_read % s->avdimen.hw_bytes_per_line);
+
+ if (processed_bytes + this_read > total_size)
+ this_read = total_size - processed_bytes;
+
+ read_constrains(s, this_read);
+
+ DBG (5, "reader_process: processed_bytes: %lu, total_size: %lu\n",
+ (u_long) processed_bytes, (u_long) total_size);
+ DBG (5, "reader_process: this_read: %lu\n", (u_long) this_read);
+
+ sigprocmask (SIG_BLOCK, &sigterm_set, 0);
+ status = read_data (s, stripe_data + stripe_fill, &this_read);
+ sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
+
+ /* only EOF on the second stripe, as otherwise the rear page
+ is shorter */
+ if (status == SANE_STATUS_EOF && deinterlace == STRIPE) {
+ static int already_eof = 0;
+ if (!already_eof) {
+ DBG (5, "reader_process: first EOF on stripe interlace: hiding.\n");
+ status = SANE_STATUS_GOOD;
+ already_eof = 1;
+ }
+ }
+
+ /* write RAW data to file for debugging */
+ if (raw_fp && this_read > 0)
+ fwrite (stripe_data + stripe_fill, this_read, 1, raw_fp);
+
+ if (status == SANE_STATUS_EOF || this_read == 0) {
+ DBG (1, "reader_process: read_data failed due to EOF\n");
+ exit_status = SANE_STATUS_EOF;
+ }
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "reader_process: read_data failed with status: %d\n",
+ status);
+ exit_status = status;
+ }
+
+ stripe_fill += this_read;
+ processed_bytes += this_read;
+ }
+
+ /* fill the stripe buffer with stored, virtual data */
+ if (s->duplex_rear_valid)
+ {
+ size_t this_read = stripe_size - stripe_fill;
+ size_t got;
+
+ /* limit reads to max_read and global data boundaries */
+ if (this_read > max_bytes_per_read)
+ this_read = max_bytes_per_read;
+
+ if (processed_bytes + this_read > total_size)
+ this_read = total_size - processed_bytes;
+
+ DBG (5, "reader_process: virtual processed_bytes: %lu, total_size: %lu\n",
+ (u_long) processed_bytes, (u_long) total_size);
+ DBG (5, "reader_process: virtual this_read: %lu\n", (u_long) this_read);
+
+ got = fread (stripe_data + stripe_fill, 1, this_read, rear_fp);
+ stripe_fill += got;
+ processed_bytes += got;
+ if (got != this_read)
+ exit_status = SANE_STATUS_EOF;
+ }
+
+ DBG (5, "reader_process: stripe filled: %d\n", stripe_fill);
+
+ useful_bytes = stripe_fill;
+
+ if (color_mode_is_color (s->c_mode))
+ useful_bytes -= 2 * s->avdimen.line_difference * s->avdimen.hw_bytes_per_line;
+
+ DBG (3, "reader_process: useful_bytes %i\n", useful_bytes);
+
+ /* Deinterlace, save the rear stripes. For some scanners (AV220)
+ that is every 2nd stripe, the 2nd half of the transferred
+ data ((AV83xx), or every 2nd line (AV122)). */
+ if (deinterlace != NONE && !s->duplex_rear_valid)
+ {
+ /* for all lines we have in the buffer: */
+ unsigned int absline = (processed_bytes - stripe_fill) / s->avdimen.hw_bytes_per_line;
+ unsigned int abslines = absline + useful_bytes / s->avdimen.hw_bytes_per_line;
+ uint8_t* ptr = stripe_data;
+ for ( ; absline < abslines; ++absline)
+ {
+ DBG (9, "reader_process: deinterlacing line %d\n", absline);
+ /* interlaced? save the back data to the rear buffer */
+ if ( (deinterlace == STRIPE && absline % (lines_per_stripe*2) >= lines_per_stripe) ||
+ (deinterlace == HALF && absline >= total_size / s->avdimen.hw_bytes_per_line / 2) ||
+ (deinterlace == LINE && absline & 0x1) ) /* last bit equals % 2 */
+ {
+ DBG (9, "reader_process: saving rear line %d to temporary file.\n", absline);
+ fwrite (ptr, s->avdimen.hw_bytes_per_line, 1, rear_fp);
+ if (deinterlace == LINE)
+ memmove (ptr, ptr+s->avdimen.hw_bytes_per_line,
+ stripe_data + stripe_fill - ptr - s->avdimen.hw_bytes_per_line);
+ else
+ ptr += s->avdimen.hw_bytes_per_line;
+ useful_bytes -= s->avdimen.hw_bytes_per_line;
+ stripe_fill -= s->avdimen.hw_bytes_per_line;
+ }
+ else
+ ptr += s->avdimen.hw_bytes_per_line;
+ }
+ DBG (9, "reader_process: after deinterlacing: useful_bytes: %d, stripe_fill: %d\n",
+ useful_bytes, stripe_fill);
+ }
+ if (dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX && !(s->page % 2) && !s->duplex_rear_valid) {
+ /* Here we flip the image by writing the lines from the end of the file to the beginning. */
+ unsigned int absline = (processed_bytes - stripe_fill) / s->avdimen.hw_bytes_per_line;
+ unsigned int abslines = absline + useful_bytes / s->avdimen.hw_bytes_per_line;
+ uint8_t* ptr = stripe_data;
+ for ( ; absline < abslines; ++absline) {
+ fseek (rear_fp, ((0 - s->params.lines) - absline - 2) * s->avdimen.hw_bytes_per_line, SEEK_SET);
+ fwrite (ptr, s->avdimen.hw_bytes_per_line, 1, rear_fp);
+ useful_bytes -= s->avdimen.hw_bytes_per_line;
+ stripe_fill -= s->avdimen.hw_bytes_per_line;
+ ptr += s->avdimen.hw_bytes_per_line;
+ }
+ DBG (9, "reader_process: after page flip: useful_bytes: %d, stripe_fill: %d\n",
+ useful_bytes, stripe_fill);
+ } else {
+
+ /*
+ * Perform needed data conversions (packing, ...) and/or copy the
+ * image data.
+ */
+
+ if (s->c_mode != AV_TRUECOLOR && s->c_mode != AV_TRUECOLOR16)
+ /* simple copy */
+ {
+ memcpy (out_data, stripe_data, useful_bytes);
+ }
+ else /* AV_TRUECOLOR* */
+ {
+ /* WARNING: DO NOT MODIFY MY (HOPEFULLY WELL) OPTIMIZED
+ ALGORITHMS BELOW, WITHOUT UNDERSTANDING THEM FULLY ! */
+ if (s->avdimen.line_difference > 0) /* color-pack */
+ {
+ /* TODO: add 16bit per sample code? */
+ unsigned int i;
+ int c_offset = s->avdimen.line_difference * s->avdimen.hw_bytes_per_line;
+
+ uint8_t* r_ptr = stripe_data;
+ uint8_t* g_ptr = stripe_data + c_offset + 1;
+ uint8_t* b_ptr = stripe_data + 2 * c_offset + 2;
+
+ for (i = 0; i < useful_bytes;) {
+ out_data [i++] = *r_ptr; r_ptr += 3;
+ out_data [i++] = *g_ptr; g_ptr += 3;
+ out_data [i++] = *b_ptr; b_ptr += 3;
+ }
+ } /* end color pack */
+ else if (dev->inquiry_needs_line_pack) /* line-pack */
+ {
+ /* TODO: add 16bit per sample code? */
+ int i = 0, l, p;
+ const int lines = useful_bytes / s->avdimen.hw_bytes_per_line;
+
+ for (l = 0; l < lines; ++l)
+ {
+ uint8_t* r_ptr = stripe_data + (s->avdimen.hw_bytes_per_line * l);
+ uint8_t* g_ptr = r_ptr + s->avdimen.hw_pixels_per_line;
+ uint8_t* b_ptr = g_ptr + s->avdimen.hw_pixels_per_line;
+
+ for (p = 0; p < s->avdimen.hw_pixels_per_line; ++p) {
+ out_data [i++] = *(r_ptr++);
+ out_data [i++] = *(g_ptr++);
+ out_data [i++] = *(b_ptr++);
+ }
+ }
+ } /* end line pack */
+ else /* else no packing was required -> simple copy */
+ {
+ memcpy (out_data, stripe_data, useful_bytes);
+ }
+ } /* end if AV_TRUECOLOR* */
+
+ /* FURTHER POST-PROCESSING ON THE FINAL OUTPUT DATA */
+
+ /* maybe mirroring in ADF mode */
+ if (s->source_mode_dim == AV_ADF_DIM && dev->inquiry_adf_need_mirror)
+ {
+ if ( (s->c_mode != AV_TRUECOLOR) ||
+ (s->c_mode == AV_TRUECOLOR && dev->inquiry_adf_bgr_order) )
+ {
+ /* Mirroring with bgr -> rgb conversion: Just mirror the
+ * whole line */
+
+ int l;
+ int lines = useful_bytes / s->avdimen.hw_bytes_per_line;
+
+ for (l = 0; l < lines; ++l)
+ {
+ uint8_t* begin_ptr = out_data + (l * s->avdimen.hw_bytes_per_line);
+ uint8_t* end_ptr = begin_ptr + s->avdimen.hw_bytes_per_line;
+
+ while (begin_ptr < end_ptr) {
+ uint8_t tmp;
+ tmp = *begin_ptr;
+ *begin_ptr++ = *end_ptr;
+ *end_ptr-- = tmp;
+ }
+ }
+ }
+ else /* non trivial mirroring */
+ {
+ /* Non-trivial Mirroring with element swapping */
+
+ int l;
+ int lines = useful_bytes / s->avdimen.hw_bytes_per_line;
+
+ for (l = 0; l < lines; ++l)
+ {
+ uint8_t* begin_ptr = out_data + (l * s->avdimen.hw_bytes_per_line);
+ uint8_t* end_ptr = begin_ptr + s->avdimen.hw_bytes_per_line - 3;
+
+ while (begin_ptr < end_ptr) {
+ uint8_t tmp;
+
+ /* R */
+ tmp = *begin_ptr;
+ *begin_ptr++ = *end_ptr;
+ *end_ptr++ = tmp;
+
+ /* G */
+ tmp = *begin_ptr;
+ *begin_ptr++ = *end_ptr;
+ *end_ptr++ = tmp;
+
+ /* B */
+ tmp = *begin_ptr;
+ *begin_ptr++ = *end_ptr;
+ *end_ptr = tmp;
+
+ end_ptr -= 5;
+ }
+ }
+ }
+ } /* end if mirroring needed */
+
+ /* byte swapping and software calibration 16bit mode */
+ if (s->c_mode == AV_GRAYSCALE12 ||
+ s->c_mode == AV_GRAYSCALE16 ||
+ s->c_mode == AV_TRUECOLOR12 ||
+ s->c_mode == AV_TRUECOLOR16) {
+
+ int l;
+ int lines = useful_bytes / s->avdimen.hw_bytes_per_line;
+
+ uint8_t* dark_avg_data = s->dark_avg_data;
+ uint8_t* white_avg_data = s->white_avg_data;
+
+ uint8_t* begin_ptr = out_data;
+ uint8_t* end_ptr = begin_ptr + s->avdimen.hw_bytes_per_line;
+ uint8_t* line_ptr;
+
+ double scale = 1.0;
+ if (s->c_mode == AV_GRAYSCALE12 || s->c_mode == AV_TRUECOLOR12)
+ scale = (double) (1<<4);
+
+ while (begin_ptr < end_ptr) {
+ uint16_t dark_avg = 0;
+ uint16_t white_avg = WHITE_MAP_RANGE;
+
+ if (dark_avg_data)
+ dark_avg = get_double_le (dark_avg_data);
+ if (white_avg_data)
+ white_avg = get_double_le (white_avg_data);
+
+ line_ptr = begin_ptr;
+ for (l = 0; l < lines; ++ l)
+ {
+ double v = (double) get_double_le (line_ptr) * scale;
+ uint16_t v2;
+ if (0)
+ v = (v - dark_avg) * white_avg / WHITE_MAP_RANGE;
+
+ v2 = v < 0xFFFF ? v : 0xFFFF;
+
+ /* SANE Standard 3.2.1 "... bytes of each sample value are
+ transmitted in the machine's native byte order." */
+ *line_ptr = v2;
+
+ line_ptr += s->avdimen.hw_bytes_per_line;
+ }
+
+ begin_ptr += 2;
+ if (dark_avg_data)
+ dark_avg_data += 2;
+ if (white_avg_data)
+ white_avg_data += 2;
+ }
+ }
+
+ /* SOFTWARE SCALING WITH INTERPOLATION (IF NECESSARY) */
+
+ if (s->avdimen.hw_xres == s->avdimen.xres &&
+ s->avdimen.hw_yres == s->avdimen.yres) /* No scaling */
+ {
+ int lines, _hw_line = hw_line;
+ uint8_t* src = out_data;
+ /* we support cropping at the beginning and end due to rear offset */
+ for (lines = useful_bytes / s->avdimen.hw_bytes_per_line;
+ lines > 0; --lines, ++_hw_line, src += s->avdimen.hw_bytes_per_line)
+ {
+ if (deinterlace != NONE) {
+ /* crop rear offset :-( */
+ if ( (!s->duplex_rear_valid && _hw_line >= s->avdimen.hw_lines) ||
+ (s->duplex_rear_valid && _hw_line < s->avdimen.rear_offset) )
+ {
+ DBG (7, "reader_process: skip due read offset line: %d\n", line);
+ continue;
+ }
+ }
+ fwrite (src, s->avdimen.hw_bytes_per_line, 1, fp);
+ ++line;
+ }
+ }
+ else /* Software scaling - watch out - this code bites back! */
+ {
+ int x;
+ /* for convenience in the 16bit code path */
+ uint16_t* out_data16 = (uint16_t*) out_data;
+
+ const int hw_line_end = hw_line + useful_bytes / s->avdimen.hw_bytes_per_line;
+
+ /* on-the-fly bi-linear interpolation */
+ while (1) {
+ double by = (-1.0 + s->avdimen.hw_lines) * line / (s->avdimen.hw_lines * s->avdimen.xres / s->avdimen.hw_xres + s->val[OPT_BACKGROUND].w);
+ int sy = (int)floor(by);
+ int ydist = (int) ((by - sy) * 256);
+ int syy = sy + 1;
+
+ const int hwbpl = s->avdimen.hw_bytes_per_line;
+
+ uint8_t* dst = ip_data;
+ uint16_t* dst16 = (uint16_t*) ip_data;
+ unsigned int v; /* accumulator */
+
+ /* Break out if we do not have the hw source line - yet,
+ or when we are past the end of wanted data (e.g. on the
+ front page due to rear_offset). Also take the read_offset
+ into account on the rear side */
+ if (deinterlace != NONE) {
+ if (!s->duplex_rear_valid && syy >= s->avdimen.hw_lines) {
+ DBG (7, "reader_process: skip due past intended front page lines: %d\n", sy);
+ break;
+ }
+ else if (s->duplex_rear_valid) {
+ /* the beginning is to be skipped, accessed thru offset */
+ DBG (7, "reader_process: rear_offset adjusting source: %d\n", sy);
+ sy += s->avdimen.rear_offset;
+ syy += s->avdimen.rear_offset;
+ }
+ }
+
+ if (sy >= hw_line_end || syy >= hw_line_end) {
+ DBG (3, "reader_process: source line %d-%d not yet avail\n",
+ sy, syy);
+ break;
+ }
+
+ /* convert to offset in current stripe */
+ sy -= hw_line;
+ syy -= hw_line;
+
+ if (sy < -1) {
+ DBG (1, "reader_process: need more history: %d???\n", sy);
+ sy = -1;
+ }
+
+ DBG (8, "reader_process: out line: %d <- from: %d-%d\n",
+ line, sy, syy);
+
+ for (x = 0; x < s->params.pixels_per_line; ++x) {
+ const double bx = (-1.0 + s->avdimen.hw_pixels_per_line) * x / s->params.pixels_per_line;
+ const int sx = (int)floor(bx);
+ const int xdist = (int) ((bx - sx) * 256);
+ const int sxx = sx + 1;
+
+ if (x == 0 || x == s->params.pixels_per_line - 1)
+ DBG (8, "reader_process: x: %d <- from: %d-%d\n",
+ x, sx, sxx);
+
+ switch (s->c_mode) {
+ case AV_THRESHOLDED:
+ case AV_DITHERED:
+ {
+ /* Repeating this over and over again is not fast, but
+ as a seldom used code-path we want it readable.
+ x/8 is the byte, and x%8 the bit position. */
+ v =
+ ( ((out_data [sy*hwbpl + sx/8 ] >> (7-sx%8 )) & 1) * (256-xdist) * (256-ydist) +
+ ((out_data [sy*hwbpl + sxx/8] >> (7-sxx%8)) & 1) * xdist * (256-ydist) +
+ ((out_data [syy*hwbpl + sx/8 ] >> (7-sx%8 )) & 1) * (256-xdist) * ydist +
+ ((out_data [syy*hwbpl + sxx/8] >> (7-sxx%8)) & 1) * xdist * ydist
+ ) / (1 + 1 * 256);
+
+ /* Shift and or the result together and eventually
+ jump to the next byte. */
+ *dst = (*dst << 1) | ((v>>7)&1);
+ if (x % 8 == 7)
+ ++dst;
+ }
+ break;
+
+ case AV_GRAYSCALE:
+ {
+ v =
+ ( out_data [sy*hwbpl + sx ] * (256-xdist) * (256-ydist) +
+ out_data [sy*hwbpl + sxx] * xdist * (256-ydist) +
+ out_data [syy*hwbpl + sx ] * (256-xdist) * ydist +
+ out_data [syy*hwbpl + sxx] * xdist * ydist
+ ) / (256 * 256);
+ *dst++ = v;
+ }
+ break;
+
+ case AV_GRAYSCALE12:
+ case AV_GRAYSCALE16:
+ {
+ /* TODO: test! */
+ v =
+ ( out_data16 [sy*hwbpl + sx ] * (256-xdist) * (256-ydist) +
+ out_data16 [sy*hwbpl + sxx] * xdist * (256-ydist) +
+ out_data16 [syy*hwbpl + sx ] * (256-xdist) * ydist +
+ out_data16 [syy*hwbpl + sxx] * xdist * ydist
+ ) / (256 * 256);
+ *dst16++ = v;
+ }
+ break;
+
+ case AV_TRUECOLOR:
+ {
+ int c;
+ for (c = 0; c < 3; ++c)
+ {
+ v =
+ ( out_data [sy*hwbpl + sx*3 + c] * (256-xdist) * (256-ydist) +
+ out_data [sy*hwbpl + sxx*3 + c] * xdist * (256-ydist) +
+ out_data [syy*hwbpl + sx*3 + c] * (256-xdist) * ydist +
+ out_data [syy*hwbpl + sxx*3 + c] * xdist * ydist
+ ) / (256 * 256);
+ *dst++ = v;
+ }
+ }
+ break;
+
+ case AV_TRUECOLOR12:
+ case AV_TRUECOLOR16:
+ {
+ /* TODO: test! */
+ int c;
+ for (c = 0; c < 3; ++c)
+ {
+ v =
+ ( out_data16 [sy*hwbpl + sx*3 + c] * (256-xdist) * (256-ydist) +
+ out_data16 [sy*hwbpl + sxx*3 + c] * xdist * (256-ydist) +
+ out_data16 [syy*hwbpl + sx*3 + c] * (256-xdist) * ydist +
+ out_data16 [syy*hwbpl + sxx*3 + c] * xdist * ydist
+ ) / (256 * 256);
+ *dst16++ = v;
+ }
+ }
+ break;
+
+ case AV_COLOR_MODE_LAST:
+ ; /* silence compiler warning */
+ }
+ }
+ fwrite (ip_data, s->params.bytes_per_line, 1, fp);
+ ++line;
+ }
+ /* copy one line of history for the next pass */
+ memcpy (ip_history,
+ out_data + useful_bytes - s->avdimen.hw_bytes_per_line,
+ s->avdimen.hw_bytes_per_line);
+ }
+ }
+
+ /* save image date in stripe buffer for next next stripe */
+ stripe_fill -= useful_bytes;
+ if (stripe_fill > 0)
+ memcpy (stripe_data, stripe_data + useful_bytes, stripe_fill);
+
+ hw_line += useful_bytes / s->avdimen.hw_bytes_per_line;
+
+ DBG (3, "reader_process: end of iteration\n");
+ } /* end while not all lines or inf. mode */
+
+ DBG (3, "reader_process: i/o loop finished\n");
+ if (exit_status == SANE_STATUS_GOOD)
+ exit_status = SANE_STATUS_EOF;
+
+ if (raw_fp)
+ fclose (raw_fp);
+
+ /* maybe we need to fill in some white data */
+ if (exit_status == SANE_STATUS_EOF && line < s->params.lines) {
+ DBG (3, "reader_process: padding with white data\n");
+ memset (out_data, gray_mode ? 0xff : 0x00, s->params.bytes_per_line);
+
+ DBG (6, "reader_process: padding line %d - %d\n",
+ line, s->params.lines);
+ while (line < s->params.lines) {
+ fwrite (out_data, s->params.bytes_per_line, 1, fp);
+ ++line;
+ }
+ }
+
+ /* Eject film holder and/or release_unit - but only for
+ non-duplex-rear / non-virtual scans. */
+ if ((deinterlace != NONE && s->duplex_rear_valid) ||
+ ((dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && !(s->page % 2) && s->duplex_rear_valid))
+ {
+ DBG (1, "reader_process: virtual duplex scan - no device cleanup!\n");
+ }
+ else
+ {
+ /* poll the cancel button if the scanner is marked as having one */
+ if (dev->hw->feature_type & AV_CANCEL_BUTTON) {
+ if (get_button_status (s) == SANE_STATUS_CANCELLED)
+ exit_status = SANE_STATUS_CANCELLED;
+ }
+
+ status = release_unit (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "reader_process: release_unit failed\n");
+
+ if (dev->inquiry_new_protocol && dev->scanner_type == AV_FILM) {
+ status = object_position (s, AVISION_SCSI_OP_GO_HOME);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "reader_process: object position go-home failed!\n");
+ }
+ }
+
+ if ((dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && s->page % 2) {
+ /* front page of flipping duplex */
+ if (exit_status == SANE_STATUS_EOF) {
+ if (s->val[OPT_ADF_FLIP].w) {
+ /* The page flip bit must be reset after every scan, but if the
+ * user doesn't care, there's no reason to reset.
+ */
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "reader_process: set scan window command failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ /* we can set anything here without fear because the process will terminate soon and take our changes with it */
+ s->page += 1;
+ s->params.lines = -line;
+ exit_status = reader_process (s);
+ }
+ /* TODO:
+ * else {
+ * spit out the page if an error was encountered...
+ * assuming the error won't prevent it.
+ * } */
+ } else {
+ fclose (fp);
+ }
+ if (rear_fp)
+ fclose (rear_fp);
+
+ if (ip_data) free (ip_data);
+ if (ip_history)
+ free (ip_history);
+ else
+ free (out_data); /* if we have ip_history out_data is included there */
+
+ free (stripe_data);
+
+ DBG (3, "reader_process: returning success\n");
+ return exit_status;
+}
+
+/* SANE callback to attach a SCSI device */
+static SANE_Status
+attach_one_scsi (const char* dev)
+{
+ attach (dev, AV_SCSI, 0);
+ return SANE_STATUS_GOOD;
+}
+
+/* SANE callback to attach a USB device */
+static SANE_Status
+attach_one_usb (const char* dev)
+{
+ attach (dev, AV_USB, 0);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+sane_reload_devices (void)
+{
+ FILE* fp;
+
+ char line[PATH_MAX];
+ const char* cp = 0;
+ char* word;
+ int linenumber = 0;
+ int model_num = 0;
+
+ sanei_usb_init ();
+ fp = sanei_config_open (AVISION_CONFIG_FILE);
+ if (fp <= (FILE*)0)
+ {
+ DBG (1, "sane_reload_devices: No config file present!\n");
+ }
+ else
+ {
+ /* first parse the config file */
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ attaching_hw = 0;
+
+ word = NULL;
+ ++ linenumber;
+
+ DBG (5, "sane_reload_devices: parsing config line \"%s\"\n",
+ line);
+
+ cp = sanei_config_get_string (line, &word);
+
+ if (!word || cp == line) {
+ DBG (5, "sane_reload_devices: config file line %d: ignoring empty line\n",
+ linenumber);
+ if (word) {
+ free (word);
+ word = NULL;
+ }
+ continue;
+ }
+
+ if (!word) {
+ DBG (1, "sane_reload_devices: config file line %d: could not be parsed\n",
+ linenumber);
+ continue;
+ }
+
+ if (word[0] == '#') {
+ DBG (5, "sane_reload_devices: config file line %d: ignoring comment line\n",
+ linenumber);
+ free (word);
+ word = NULL;
+ continue;
+ }
+
+ if (strcmp (word, "option") == 0)
+ {
+ free (word);
+ word = NULL;
+ cp = sanei_config_get_string (cp, &word);
+
+ if (strcmp (word, "disable-gamma-table") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: disable-gamma-table\n",
+ linenumber);
+ disable_gamma_table = SANE_TRUE;
+ }
+ else if (strcmp (word, "disable-calibration") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: disable-calibration\n",
+ linenumber);
+ disable_calibration = SANE_TRUE;
+ }
+ else if (strcmp (word, "force-calibration") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: force-calibration\n",
+ linenumber);
+ force_calibration = SANE_TRUE;
+ }
+ else if (strcmp (word, "force-a4") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: enabling force-a4\n",
+ linenumber);
+ force_a4 = SANE_TRUE;
+ }
+ else if (strcmp (word, "force-a3") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: enabling force-a3\n",
+ linenumber);
+ force_a3 = SANE_TRUE;
+ }
+ else if (strcmp (word, "static-red-calib") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: static red calibration\n",
+ linenumber);
+ static_calib_list [0] = SANE_TRUE;
+ }
+ else if (strcmp (word, "static-green-calib") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: static green calibration\n",
+ linenumber);
+ static_calib_list [1] = SANE_TRUE;
+ }
+ else if (strcmp (word, "static-blue-calib") == 0) {
+ DBG (3, "sane_reload_devices: config file line %d: static blue calibration\n",
+ linenumber);
+ static_calib_list [2] = SANE_TRUE;
+ }
+ else
+ DBG (1, "sane_reload_devices: config file line %d: options unknown!\n",
+ linenumber);
+ }
+ else if (strcmp (word, "usb") == 0) {
+ DBG (2, "sane_reload_devices: config file line %d: trying to attach USB:`%s'\n",
+ linenumber, line);
+ /* try to attach USB device */
+ sanei_usb_attach_matching_devices (line, attach_one_usb);
+ }
+ else if (strcmp (word, "scsi") == 0) {
+ DBG (2, "sane_reload_devices: config file line %d: trying to attach SCSI: %s'\n",
+ linenumber, line);
+
+ /* the last time I verified (2003-03-18) this function
+ only matches SCSI devices ... */
+ sanei_config_attach_matching_devices (line, attach_one_scsi);
+ }
+ else {
+ DBG (1, "sane_reload_devices: config file line %d: OBSOLETE !! use the scsi keyword!\n",
+ linenumber);
+ DBG (1, "sane_reload_devices: (see man sane-avision for details): trying to attach SCSI: %s'\n",
+ line);
+
+ /* the last time I verified (2003-03-18) this function
+ only matched SCSI devices ... */
+ sanei_config_attach_matching_devices (line, attach_one_scsi);
+ }
+ free (word);
+ word = NULL;
+ } /* end while read */
+
+ fclose (fp);
+
+ if (word)
+ free (word);
+ } /* end if fp */
+
+ /* search for all supported SCSI/USB devices */
+ while (Avision_Device_List [model_num].scsi_mfg != NULL ||
+ Avision_Device_List [model_num].real_mfg != NULL)
+ {
+ /* also potentially accessed from the attach_* callbacks */
+ attaching_hw = &(Avision_Device_List [model_num]);
+ if (attaching_hw->scsi_mfg != NULL)
+ sanei_scsi_find_devices (attaching_hw->scsi_mfg,
+ attaching_hw->scsi_model, NULL,
+ -1, -1, -1, -1,
+ attach_one_scsi);
+
+ if (attaching_hw->usb_vendor != 0 && attaching_hw->usb_product != 0 )
+ {
+ DBG (1, "sane_reload_devices: Trying to find USB device %.4x %.4x ...\n",
+ attaching_hw->usb_vendor,
+ attaching_hw->usb_product);
+
+ /* TODO: check return value */
+ if (sanei_usb_find_devices (attaching_hw->usb_vendor,
+ attaching_hw->usb_product,
+ attach_one_usb) != SANE_STATUS_GOOD) {
+ DBG (1, "sane_reload_devices: error during USB device detection!\n");
+ }
+ }
+ ++ model_num;
+ } /* end for all devices in supported list */
+
+ attaching_hw = 0;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int* version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* silence gcc */
+
+ DBG_INIT();
+
+#ifdef AVISION_STATIC_DEBUG_LEVEL
+ DBG_LEVEL = AVISION_STATIC_DEBUG_LEVEL;
+#endif
+
+ DBG (3, "sane_init:(Version: %i.%i Build: %i)\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BACKEND_BUILD);
+
+ /* must come first */
+ sanei_thread_init ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BACKEND_BUILD);
+
+ sane_reload_devices ();
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Avision_Device* dev;
+ Avision_Device* next;
+
+ DBG (3, "sane_exit:\n");
+
+ for (dev = first_dev; dev; dev = next) {
+ next = dev->next;
+ /* no warning for stripping const - C lacks a const_cast<> */
+ free ((void*)(size_t) dev->sane.name);
+
+ free (dev);
+ }
+ first_dev = NULL;
+
+ free(devlist);
+ devlist = NULL;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device*** device_list, SANE_Bool local_only)
+{
+ Avision_Device* dev;
+ int i;
+
+ local_only = local_only; /* silence gcc */
+
+ DBG (3, "sane_get_devices:\n");
+
+ sane_reload_devices ();
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle *handle)
+{
+ Avision_Device* dev;
+ SANE_Status status;
+ Avision_Scanner* s;
+ int i, j;
+ uint8_t inquiry_result[AVISION_INQUIRY_SIZE_V1];
+
+ DBG (3, "sane_open:\n");
+
+ if (devicename[0]) {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (dev) {
+ status = attach (devicename, dev->connection.connection_type, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ } else {
+ /* empty devicename -> use first device */
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+
+ /* initialize ... */
+ /* the other states (scanning, ...) rely on this memset (0) */
+ memset (s, 0, sizeof (*s));
+
+ /* initialize connection state */
+ s->av_con.connection_type = dev->connection.connection_type;
+ s->av_con.usb_status = dev->connection.usb_status;
+ s->av_con.scsi_fd = -1;
+ s->av_con.usb_dn = -1;
+
+ s->reader_pid = -1;
+ s->read_fds = -1;
+
+ s->hw = dev;
+
+ /* We initialize the table to a gamma value of 2.22, since this is what
+ papers about Colorimetry suggest.
+
+ http://www.poynton.com/GammaFAQ.html
+
+ Avision's driver defaults to 2.2 though. */
+ {
+ const double gamma = 2.22;
+ const double one_over_gamma = 1. / gamma;
+
+ for (i = 0; i < 4; ++ i)
+ for (j = 0; j < 256; ++ j)
+ s->gamma_table[i][j] = pow( (double) j / 255, one_over_gamma) * 255;
+ }
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+
+ /* open the device */
+ if (! avision_is_open (&s->av_con) ) {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ DBG (1, "sane_open: using open_extended\n");
+ status = avision_open_extended (s->hw->sane.name, &s->av_con, sense_handler, 0,
+ &(dev->scsi_buffer_size));
+#else
+ status = avision_open (s->hw->sane.name, &s->av_con, sense_handler, 0);
+#endif
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return status;
+ }
+ DBG (1, "sane_open: got %d scsi_max_request_size\n", dev->scsi_buffer_size);
+ }
+
+ /* first: re-awake the device with an inquiry, some devices are flunk while initializing
+ the usb connection and like a inquiry to come first ... (AV610 et.al.) */
+ status = inquiry (s->av_con, inquiry_result, sizeof(inquiry_result));
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_open: awakening inquiry failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ status = wait_ready (&s->av_con, 1);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_open: wait_ready() failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ /* update settings based on additional accessory information */
+ status = additional_probe (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_open: additional probe failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ /* initialize the options */
+ init_options (s);
+
+ if (dev->inquiry_duplex_interlaced || dev->scanner_type == AV_FILM ||
+ dev->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX) {
+ /* Might need at least *DOS (Windows flavour and OS/2) portability fix
+ However, I was told Cygwin (et al.) takes care of it. */
+ strncpy(s->duplex_rear_fname, "/tmp/avision-rear-XXXXXX", PATH_MAX);
+
+ if (! mktemp(s->duplex_rear_fname) ) {
+ DBG (1, "sane_open: failed to generate temporary fname for duplex scans\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ else {
+ DBG (1, "sane_open: temporary fname for duplex scans: %s\n",
+ s->duplex_rear_fname);
+ }
+ }
+
+ /* calibrate film scanners, as this must be done without the
+ film holder and at the full resolution */
+ if (dev->scanner_type == AV_FILM)
+ {
+ int default_res = s->val[OPT_RESOLUTION].w;
+ s->val[OPT_RESOLUTION].w = dev->inquiry_optical_res;
+
+ DBG (1, "sane_open: early calibration for film scanner.\n");
+
+ compute_parameters (s);
+
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_open: set scan window command failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (!(dev->hw->feature_type & AV_NO_CALIB))
+ {
+ status = normal_calibration (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_open: perform calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (dev->scanner_type == AV_FILM) {
+ status = object_position (s, AVISION_SCSI_OP_GO_HOME);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "reader_open: object position go-home failed!\n");
+ }
+
+ s->val[OPT_RESOLUTION].w = default_res;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Avision_Scanner* prev;
+ Avision_Scanner* s = handle;
+ int i;
+
+ DBG (3, "sane_close:\n");
+
+ /* close the device */
+ if (avision_is_open (&s->av_con) ) {
+ avision_close (&s->av_con);
+ }
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next) {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+
+ /* a handle we know about ? */
+ if (!s) {
+ DBG (1, "sane_close: invalid handle %p\n", handle);
+ return;
+ }
+
+ if (s->scanning)
+ do_cancel (handle);
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ for (i = 1; i < NUM_OPTIONS; ++ i) {
+ if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s) {
+ free (s->val[i].s);
+ }
+ }
+
+ if (s->white_avg_data)
+ free (s->white_avg_data);
+ if (s->dark_avg_data)
+ free (s->dark_avg_data);
+
+ if (s->background_raster)
+ free (s->background_raster);
+
+ if (*(s->duplex_rear_fname)) {
+ unlink (s->duplex_rear_fname);
+ *(s->duplex_rear_fname) = 0;
+ }
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor*
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Avision_Scanner* s = handle;
+
+ DBG (3, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void* val, SANE_Int* info)
+{
+ Avision_Scanner* s = handle;
+ Avision_Device* dev = s->hw;
+ SANE_Status status;
+ SANE_Word cap;
+
+ DBG (3, "sane_control_option: option=%d, action=%d\n",
+ (int)option, (int)action);
+
+ DBG (5, "sane_control_option: option=%s, action=%s\n",
+ s->opt[option].name,
+ action == SANE_ACTION_GET_VALUE ? "GET" :
+ (action == SANE_ACTION_SET_VALUE ? "SET" :
+ (action == SANE_ACTION_SET_AUTO ? "AUTO" : "UNKNOWN") ) );
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_PREVIEW:
+
+ case OPT_RESOLUTION:
+ case OPT_SPEED:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_OVERSCAN_TOP:
+ case OPT_OVERSCAN_BOTTOM:
+ case OPT_BACKGROUND:
+ case OPT_NUM_OPTS:
+
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_EXPOSURE:
+ case OPT_IR:
+ case OPT_MULTISAMPLE:
+ case OPT_QSCAN:
+ case OPT_QCALIB:
+ case OPT_PAPERLEN:
+ case OPT_ADF_FLIP:
+ *(SANE_Word*) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* specially treated word options */
+
+ case OPT_FRAME:
+ status = get_frame_info (s);
+ *(SANE_Word*) val = s->val[option].w;
+ return status;
+
+ case OPT_POWER_SAVE_TIME:
+ get_power_save_time (s, &(s->val[option].w));
+ *(SANE_Word*) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_SOURCE:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* specially treated string options */
+ case OPT_MESSAGE:
+ if (dev->inquiry_button_control || dev->inquiry_buttons)
+ status = get_button_status (s);
+
+ strcpy (val, s->val[option].s);
+ s->val[option].s[0] = 0;
+ return SANE_STATUS_GOOD;
+
+ case OPT_NVRAM:
+ get_and_parse_nvram (s, s->val[option].s, 1024);
+
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ } /* end switch option */
+ } /* end if GET_ACTION_GET_VALUE */
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = constrain_value (s, option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* side-effect-free word options: */
+ case OPT_SPEED:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_EXPOSURE:
+ case OPT_IR:
+ case OPT_MULTISAMPLE:
+ case OPT_QSCAN:
+ case OPT_QCALIB:
+ case OPT_OVERSCAN_TOP:
+ case OPT_OVERSCAN_BOTTOM:
+ case OPT_BACKGROUND:
+ case OPT_PAPERLEN:
+ case OPT_ADF_FLIP:
+ s->val[option].w = *(SANE_Word*) val;
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* options with side-effects: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+
+ s->val[option].w = *(SANE_Word*) val;
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ /* string options with side-effects: */
+ case OPT_SOURCE:
+
+ if (s->val[option].s) {
+ free(s->val[option].s);
+ }
+ s->val[option].s = strdup(val);
+ s->source_mode = match_source_mode (dev, s->val[option].s);
+ s->source_mode_dim = match_source_mode_dim (s->source_mode);
+
+ /* set side-effects */
+ dev->x_range.max =
+ SANE_FIX ( dev->inquiry_x_ranges[s->source_mode_dim]);
+ dev->y_range.max =
+ SANE_FIX ( dev->inquiry_y_ranges[s->source_mode_dim]);
+
+ if (s->hw->hw->feature_type2 & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX) {
+ s->opt[OPT_ADF_FLIP].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_ADF_FLIP].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ {
+ if (s->val[option].s)
+ free (s->val[option].s);
+
+ s->val[option].s = strdup (val);
+ s->c_mode = match_color_mode (dev, s->val[OPT_MODE].s);
+
+ /* set to mode specific values */
+
+ /* the gamma table related */
+ if (!disable_gamma_table)
+ {
+ if (color_mode_is_color (s->c_mode) ) {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* gray or mono */
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ }
+ case OPT_FRAME:
+ {
+ SANE_Word frame = *((SANE_Word *) val);
+
+ status = set_frame (s, frame);
+ if (status == SANE_STATUS_GOOD) {
+ s->val[OPT_FRAME].w = frame;
+ dev->current_frame = frame;
+ }
+ return status;
+ }
+ case OPT_POWER_SAVE_TIME:
+ {
+ SANE_Word time = *((SANE_Word *) val);
+
+ status = set_power_save_time (s, time);
+ if (status == SANE_STATUS_GOOD)
+ s->val[OPT_POWER_SAVE_TIME].w = time;
+ return status;
+ }
+ } /* end switch option */
+ }
+ else if (action == SANE_ACTION_SET_AUTO)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ switch (option)
+ {
+ case OPT_ADF_FLIP:
+ s->val[option].w = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+ } /* end switch option */
+ } /* end else SET_VALUE */
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters* params)
+{
+ Avision_Scanner* s = handle;
+
+ DBG (3, "sane_get_parameters:\n");
+
+ /* During an actual scan these parameters will have been computed in
+ sane_start(). Otherwise, the values must be computed on demand. The
+ values cannot be changed during a scan to avoid inconsistency. */
+ if (!s->scanning)
+ {
+ DBG (3, "sane_get_parameters: computing parameters\n");
+ compute_parameters (s);
+ }
+
+ if (params) {
+ /* add background raster lines */
+ s->params.lines += s->val[OPT_BACKGROUND].w;
+
+ /* copy structure members */
+ params->format = s->params.format;
+ params->last_frame = s->params.last_frame;
+ params->bytes_per_line = s->params.bytes_per_line;
+ params->pixels_per_line = s->params.pixels_per_line;
+ params->lines = s->params.lines;
+ params->depth = s->params.depth;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Avision_Scanner* s = handle;
+ Avision_Device* dev = s->hw;
+
+ SANE_Status status;
+ int fds [2];
+ DBG (1, "sane_start:\n");
+
+ /* Make sure there is no scan running!!! */
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ /* Make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, &s->params);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ /* for non ADF scans (e.g. scanimage --batch-prompt on a Flatbed
+ scanner) make sure we do not assume it's an ADF scan and
+ optimize something away*/
+ if (!is_adf_scan (s))
+ s->page = 0;
+
+ if (s->page > 0 && s->duplex_rear_valid) {
+ DBG (1, "sane_start: virtual duplex rear data valid.\n");
+ goto start_scan_end;
+ }
+
+ /* Check for paper during ADF scans and for sheetfed scanners. */
+ if (is_adf_scan (s)) {
+ status = media_check (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: media_check failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ else
+ DBG (1, "sane_start: media_check ok\n");
+ }
+
+ /* Check the light early, to return to the GUI and notify the user. */
+ if (s->prepared == SANE_FALSE) {
+ if (dev->inquiry_light_control) {
+ status = wait_4_light (s);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ }
+ }
+
+ if (s->page > 0 && dev->inquiry_keeps_window) {
+ DBG (1, "sane_start: Optimized set_window away.\n");
+ }
+ else
+ {
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: set scan window command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ }
+
+#ifdef DEBUG_TEST
+ /* debug window size test ... */
+ if (dev->inquiry_new_protocol)
+ {
+ size_t size = 16;
+ uint8_t result[16];
+
+ DBG (5, "sane_start: reading scanner window size\n");
+
+ status = simple_read (s, 0x80, 0, &size, result);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: get pixel size command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ debug_print_raw (5, "sane_start: pixel_size:", result, size);
+ DBG (5, "sane_start: x-pixels: %d, y-pixels: %d\n",
+ get_quad (&(result[0])), get_quad (&(result[4])));
+ }
+#endif
+
+ /* no calibration for ADF pages */
+ if (s->page > 0) {
+ DBG (1, "sane_start: optimized calibration away.\n");
+ goto calib_end;
+ }
+
+ /* check whether the user enforces calibration */
+ if (force_calibration) {
+ DBG (1, "sane_start: calibration enforced in config!\n");
+ goto calib;
+ }
+
+ /* Only perform the calibration for newer scanners - it is not needed
+ for my Avision AV 630 - and also does not even work ... */
+ if (!dev->inquiry_new_protocol) {
+ DBG (1, "sane_start: old protocol no calibration needed!\n");
+ goto calib_end;
+ }
+
+ if (!dev->inquiry_needs_calibration) {
+ DBG (1, "sane_start: due to inquiry no calibration needed!\n");
+ goto calib_end;
+ }
+
+ /* calibration allowed for this scanner? */
+ if (dev->hw->feature_type & AV_NO_CALIB) {
+ DBG (1, "sane_start: calibration disabled in device list!!\n");
+ goto calib_end;
+ }
+
+ /* Not for film scanners, ... */
+ if (dev->scanner_type == AV_FILM) {
+ DBG (1, "sane_start: no calibration for film scanner!\n");
+ goto calib_end;
+ }
+
+ /* check whether calibration is disabled by the user */
+ if (disable_calibration) {
+ DBG (1, "sane_start: calibration disabled in config - skipped!\n");
+ goto calib_end;
+ }
+
+ /* R² reminder: We must not skip the calibration for ADF scans, some
+ scanner (HP 53xx/74xx ASIC series) rely on a calibration data
+ read (and will hang otherwise) */
+
+ calib:
+ status = normal_calibration (s);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: perform calibration failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ calib_end:
+
+ if (dev->inquiry_3x3_matrix && dev->inquiry_asic_type >= AV_ASIC_C6 &&
+ s->page == 0)
+ {
+ status = send_3x3_matrix (s);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ }
+
+ /* check whether gamma-table is disabled by the user? */
+ if (disable_gamma_table) {
+ DBG (1, "sane_start: gamma-table disabled in config - skipped!\n");
+ goto gamma_end;
+ }
+
+ if (dev->hw->feature_type & AV_NO_GAMMA) {
+ DBG (1, "sane_start: gamma table skipped due to device-list!!\n");
+ goto gamma_end;
+ }
+
+ if (s->page > 0 && dev->inquiry_keeps_gamma)
+ DBG (1, "sane_start: Optimized send_gamma away.\n");
+ else
+ {
+ status = send_gamma (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: send gamma failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ }
+
+ gamma_end:
+
+ if (dev->inquiry_tune_scan_length && is_adf_scan (s)) {
+ status = send_tune_scan_length (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: tune_scan_length command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ }
+
+ /* if the device supports retrieving background raster data
+ inquire the data no matter if the user/applications asks for
+ it in order to use it for bottom padding */
+ if (s->page == 0 && dev->inquiry_background_raster) {
+ status = get_background_raster (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sane_start: get background raster command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ }
+
+ /* check film holder */
+ if (dev->scanner_type == AV_FILM && dev->holder_type == 0xff) {
+ DBG (1, "sane_start: no film holder or APS cassette!\n");
+
+ /* Normally "go_home" is executed from the reader process,
+ but as it will not start we have to reset things here */
+ if (dev->inquiry_new_protocol) {
+ status = object_position (s, AVISION_SCSI_OP_GO_HOME);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "sane_start: go home failed: %s\n",
+ sane_strstatus (status));
+ }
+ goto stop_scanner_and_return;
+ }
+
+ start_scan_end:
+
+ s->scanning = SANE_TRUE;
+ s->page += 1; /* processing next page */
+
+ if (pipe (fds) < 0) {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->read_fds = fds[0];
+ s->write_fds = fds[1];
+
+ /* create reader routine as new process or thread */
+ DBG (3, "sane_start: starting thread\n");
+ s->reader_pid = sanei_thread_begin (reader_process, (void *) s);
+
+ if (sanei_thread_is_forked())
+ close (s->write_fds);
+
+ return SANE_STATUS_GOOD;
+
+ stop_scanner_and_return:
+
+ /* cancel the scan nicely */
+ do_cancel (s);
+
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte* buf, SANE_Int max_len, SANE_Int* len)
+{
+ Avision_Scanner* s = handle;
+ ssize_t nread;
+ *len = 0;
+
+ DBG (8, "sane_read: max_len: %d\n", max_len);
+
+ nread = read (s->read_fds, buf, max_len);
+ if (nread > 0) {
+ DBG (8, "sane_read: got %ld bytes\n", (long) nread);
+ }
+ else {
+ DBG (3, "sane_read: got %ld bytes, err: %d %s\n", (long) nread, errno, strerror(errno));
+ }
+
+ if (!s->scanning)
+ return SANE_STATUS_CANCELLED;
+
+ if (nread < 0) {
+ if (errno == EAGAIN) {
+ return SANE_STATUS_GOOD;
+ } else {
+ do_cancel (s);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ /* if all data was passed through */
+ if (nread == 0)
+ return do_eof (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Avision_Scanner* s = handle;
+ DBG (3, "sane_cancel:\n");
+
+ /* always do the housekeeping, e.g. flush batch scanner pages */
+ do_cancel (s);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Avision_Scanner* s = handle;
+
+ DBG (3, "sane_set_io_mode:\n");
+ if (!s->scanning) {
+ DBG (3, "sane_set_io_mode: not yet scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (fcntl (s->read_fds, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int* fd)
+{
+ Avision_Scanner* s = handle;
+
+ DBG (3, "sane_get_select_fd:\n");
+
+ if (!s->scanning) {
+ DBG (3, "sane_get_select_fd: not yet scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->read_fds;
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/avision.conf.in b/backend/avision.conf.in
new file mode 100644
index 0000000..0862d5c
--- /dev/null
+++ b/backend/avision.conf.in
@@ -0,0 +1,23 @@
+
+# These are the possible options. Normally any scanner
+# should work just fine without them - and they are only
+# needed for test and debugging. So if you experience problems
+# and you solve them with enabling options here, please notify
+# the SANE/Avision maintainer: Mike Kelly <mike@piratehaven.org>
+
+#option disable-gamma-table
+#option disable-calibration
+#option force-a4
+
+#scsi AVISION
+#scsi FCPA
+#scsi MINOLTA
+#scsi MITSBISH MCA-S600C
+#scsi MITSBISH MCA-SS600
+#scsi HP
+#scsi hp
+
+#scsi /dev/scanner
+# usb libusb:002:003
+# usb 0x03f0 0x0701
+
diff --git a/backend/avision.h b/backend/avision.h
new file mode 100644
index 0000000..14134e1
--- /dev/null
+++ b/backend/avision.h
@@ -0,0 +1,803 @@
+/*******************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ avision.h
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ *****************************************************************************
+
+ This backend is based upon the Tamarack backend and adapted to the Avision
+ scanners by René Rebe and Meino Cramer.
+
+ Check the avision.c file for detailed copyright and change-log
+ information.
+
+********************************************************************************/
+
+#ifndef avision_h
+#define avision_h
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h> /* available in ISO C99 */
+#else
+# include <sys/types.h>
+typedef uint8_t uint8_t;
+typedef uint16_t uint16_t;
+typedef uint32_t uint32_t;
+#endif /* HAVE_STDINT_H */
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+typedef enum Avision_ConnectionType {
+ AV_SCSI,
+ AV_USB
+} Avision_ConnectionType;
+
+/* information needed for device access */
+typedef struct Avision_Connection {
+ Avision_ConnectionType connection_type;
+ int scsi_fd; /* SCSI filedescriptor */
+ SANE_Int usb_dn; /* USB (libusb or scanner.c) device number */
+ enum {
+ AVISION_USB_UNTESTED_STATUS, /* status type untested */
+ AVISION_USB_INT_STATUS, /* interrupt endp. (USB 1.x device) status */
+ AVISION_USB_BULK_STATUS /* bulk endp. (USB 2.0 device) status */
+ } usb_status;
+
+} Avision_Connection;
+
+typedef struct Avision_HWEntry {
+ const char* scsi_mfg;
+ const char* scsi_model;
+
+ int usb_vendor;
+ int usb_product;
+
+ const char* real_mfg;
+ const char* real_model;
+
+ /* feature overwrites - as embedded CPUs have 16bit enums - this
+ would need a change ... */
+ enum {
+ /* force no calibration */
+ AV_NO_CALIB = (1<<0),
+
+ /* force all in one command calibration */
+ AV_ONE_CALIB_CMD = (1<<1),
+
+ /* no gamma table */
+ AV_NO_GAMMA = (1<<2),
+
+ /* light check is bogus */
+ AV_LIGHT_CHECK_BOGUS = (1<<3),
+
+ /* no button though the device advertise it */
+ AV_NO_BUTTON = (1<<4),
+
+ /* if the scan area needs to be forced to A3 */
+ AV_FORCE_A3 = (1<<5),
+
+ /* if the scan area and resolution needs to be forced for films */
+ AV_FORCE_FILM = (1<<6),
+
+ /* does not suport, or very broken background (added for AV610C2) */
+ AV_NO_BACKGROUND = (1<<7),
+
+ /* is film scanner - no detection yet */
+ AV_FILMSCANNER = (1<<8),
+
+ /* fujitsu adaption */
+ AV_FUJITSU = (1<<9),
+
+ /* gray calibration data has to be uploaded on the blue channel ... ? */
+ AV_GRAY_CALIB_BLUE = (1<<10),
+
+ /* Interrupt endpoint button readout (so far AV220) */
+ AV_INT_BUTTON = (1<<11),
+
+ /* send acceleration table ... */
+ AV_ACCEL_TABLE = (1<<12),
+
+ /* non-interlaced scanns up to 300 dpi (AV32xx / AV83xx) */
+ AV_NON_INTERLACED_DUPLEX_300 = (1<<13),
+
+ /* do not read multiples of 64 bytes - stalls the USB chip */
+ AV_NO_64BYTE_ALIGN = (1<<14),
+
+ /* force channel-by-channel calibration */
+ AV_MULTI_CALIB_CMD = (1<<15),
+
+ /* non color scans are faster with a filter applied (AV32xx) */
+ AV_FASTER_WITH_FILTER = (1<<16),
+
+ /* interlaced data with 1 line distance */
+ AV_2ND_LINE_INTERLACED = (1<<17),
+
+ /* does not keep the window though it advertices so */
+ AV_DOES_NOT_KEEP_WINDOW = (1<<18),
+
+ /* does not keep the gamma though it advertices so */
+ AV_DOES_NOT_KEEP_GAMMA = (1<<19),
+
+ /* advertises ADF is BGR order, but isn't (or vice versa) */
+ AV_ADF_BGR_ORDER_INVERT = (1<<20),
+
+ /* allows 12bit mode, though not flagged */
+ AV_12_BIT_MODE = (1<<21),
+
+ /* very broken background raster */
+ AV_BACKGROUND_QUIRK = (1<<22),
+
+ /* though marked as GRAY only the scanner can do GRAY modes */
+ AV_GRAY_MODES = (1<<23),
+
+ /* no seperate, single REAR scan (AV122, DM152, ...) */
+ AV_NO_REAR = (1<<24),
+
+ /* only scan with some known good hardware resolutions, as the
+ scanner fails to properly interpoloate in between (e.g. AV121,
+ DM152 on duplex scans - but also the AV600), software scale and
+ interpolate to all the others */
+ AV_SOFT_SCALE = (1<<25),
+
+ /* does keep window though it does not advertice it - the AV122/DM152
+ mess up image data if window is resend between ADF pages */
+ AV_DOES_KEEP_WINDOW = (1<<26),
+
+ /* does keep gamma though it does not advertice it */
+ AV_DOES_KEEP_GAMMA = (1<<27),
+
+ /* does the scanner contain a Cancel button? */
+ AV_CANCEL_BUTTON = (1<<28),
+
+ /* is the rear image offset? */
+ AV_REAR_OFFSET = (1<<29),
+
+ /* some devices do not need a START_SCAN, even hang with it */
+ AV_NO_START_SCAN = (1<<30),
+
+ AV_INT_STATUS = (1<<31)
+
+ /* maybe more ...*/
+ } feature_type;
+
+ /*second enum cause 32 bit int above is full*/
+ enum {
+ /* force no calibration */
+ AV_NO_TUNE_SCAN_LENGTH = (1<<0),
+
+ /* for gray scans, set grey filter */
+ AV_USE_GRAY_FILTER = (1<<1),
+
+ /* For (HP) scanners with flipping duplexers */
+ AV_ADF_FLIPPING_DUPLEX = (1<<2),
+
+ /* For scanners which need to have their firmware read to properly function. */
+ AV_FIRMWARE = (1<<3)
+ } feature_type2;
+
+} Avision_HWEntry;
+
+typedef enum {
+ AV_ASIC_Cx = 0,
+ AV_ASIC_C1 = 1,
+ AV_ASIC_W1 = 2,
+ AV_ASIC_C2 = 3,
+ AV_ASIC_C5 = 5,
+ AV_ASIC_C6 = 6,
+ AV_ASIC_C7 = 7,
+ AV_ASIC_OA980 = 128,
+ AV_ASIC_OA982 = 129
+} asic_type;
+
+typedef enum {
+ AV_THRESHOLDED,
+ AV_DITHERED,
+ AV_GRAYSCALE, /* all gray needs to be before color for is_color() */
+ AV_GRAYSCALE12,
+ AV_GRAYSCALE16,
+ AV_TRUECOLOR,
+ AV_TRUECOLOR12,
+ AV_TRUECOLOR16,
+ AV_COLOR_MODE_LAST
+} color_mode;
+
+typedef enum {
+ AV_NORMAL,
+ AV_TRANSPARENT,
+ AV_ADF,
+ AV_ADF_REAR,
+ AV_ADF_DUPLEX,
+ AV_SOURCE_MODE_LAST
+} source_mode;
+
+typedef enum {
+ AV_NORMAL_DIM,
+ AV_TRANSPARENT_DIM,
+ AV_ADF_DIM,
+ AV_SOURCE_MODE_DIM_LAST
+} source_mode_dim;
+
+enum Avision_Option
+{
+ OPT_NUM_OPTS = 0, /* must come first */
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+#define OPT_RESOLUTION_DEFAULT 150
+ OPT_SPEED,
+ OPT_PREVIEW,
+
+ OPT_SOURCE, /* scan source normal, transparency, ADF */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_OVERSCAN_TOP, /* overscan for auto-crop/deskew, if supported */
+ OPT_OVERSCAN_BOTTOM,
+ OPT_BACKGROUND, /* background raster lines to read out */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_QSCAN,
+ OPT_QCALIB,
+
+ OPT_GAMMA_VECTOR, /* first must be gray */
+ OPT_GAMMA_VECTOR_R, /* then r g b vector */
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_EXPOSURE, /* film exposure adjustment */
+ OPT_IR, /* infra-red */
+ OPT_MULTISAMPLE, /* multi-sample */
+
+ OPT_MISC_GROUP,
+ OPT_FRAME, /* Film holder control */
+
+ OPT_POWER_SAVE_TIME, /* set power save time to the scanner */
+
+ OPT_MESSAGE, /* optional message from the scanner display */
+ OPT_NVRAM, /* retrieve NVRAM values as pretty printed text */
+
+ OPT_PAPERLEN, /* Use paper_length field to detect double feeds */
+ OPT_ADF_FLIP, /* For flipping duplex, reflip the document */
+
+ NUM_OPTIONS /* must come last */
+};
+
+typedef struct Avision_Dimensions
+{
+ /* in dpi */
+ int xres;
+ int yres;
+
+ /* in pixels */
+ long tlx;
+ long tly;
+ long brx;
+ long bry;
+
+ /* in pixels */
+ int line_difference;
+ int rear_offset; /* in pixels of HW res */
+
+ /* interlaced duplex scan */
+ SANE_Bool interlaced_duplex;
+
+ /* in dpi, likewise - different if software scaling required */
+ int hw_xres;
+ int hw_yres;
+
+ int hw_pixels_per_line;
+ int hw_bytes_per_line;
+ int hw_lines;
+
+} Avision_Dimensions;
+
+/* this contains our low-level info - not relevant for the SANE interface */
+typedef struct Avision_Device
+{
+ struct Avision_Device* next;
+ SANE_Device sane;
+ Avision_Connection connection;
+
+ /* structs used to store config options */
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range speed_range;
+
+ asic_type inquiry_asic_type;
+ SANE_Bool inquiry_new_protocol;
+
+ SANE_Bool inquiry_nvram_read;
+ SANE_Bool inquiry_power_save_time;
+
+ SANE_Bool inquiry_light_box;
+ SANE_Bool inquiry_adf;
+ SANE_Bool inquiry_duplex;
+ SANE_Bool inquiry_duplex_interlaced;
+ SANE_Bool inquiry_paper_length;
+ SANE_Bool inquiry_batch_scan;
+ SANE_Bool inquiry_detect_accessories;
+ SANE_Bool inquiry_needs_calibration;
+ SANE_Bool inquiry_needs_gamma;
+ SANE_Bool inquiry_keeps_gamma;
+ SANE_Bool inquiry_keeps_window;
+ SANE_Bool inquiry_calibration;
+ SANE_Bool inquiry_3x3_matrix;
+ SANE_Bool inquiry_needs_software_colorpack;
+ SANE_Bool inquiry_needs_line_pack;
+ SANE_Bool inquiry_adf_need_mirror;
+ SANE_Bool inquiry_adf_bgr_order;
+ SANE_Bool inquiry_light_detect;
+ SANE_Bool inquiry_light_control;
+ SANE_Bool inquiry_exposure_control;
+
+ int inquiry_max_shading_target;
+ SANE_Bool inquiry_button_control;
+ unsigned int inquiry_buttons;
+ SANE_Bool inquiry_tune_scan_length;
+ SANE_Bool inquiry_background_raster;
+ int inquiry_background_raster_pixel;
+
+ enum {AV_FLATBED,
+ AV_FILM,
+ AV_SHEETFEED
+ } scanner_type;
+
+ /* the list of available color modes */
+ SANE_String_Const color_list[AV_COLOR_MODE_LAST + 1];
+ color_mode color_list_num[AV_COLOR_MODE_LAST];
+ color_mode color_list_default;
+
+ /* the list of available source modes */
+ SANE_String_Const source_list[AV_SOURCE_MODE_LAST + 1];
+ source_mode source_list_num[AV_SOURCE_MODE_LAST];
+
+ int inquiry_optical_res; /* in dpi */
+ int inquiry_max_res; /* in dpi */
+
+ double inquiry_x_ranges [AV_SOURCE_MODE_DIM_LAST]; /* in mm */
+ double inquiry_y_ranges [AV_SOURCE_MODE_DIM_LAST]; /* in mm */
+
+ int inquiry_color_boundary;
+ int inquiry_gray_boundary;
+ int inquiry_dithered_boundary;
+ int inquiry_thresholded_boundary;
+ int inquiry_line_difference; /* software color pack */
+
+ int inquiry_channels_per_pixel;
+ int inquiry_bits_per_channel;
+ int inquiry_no_gray_modes;
+
+ int scsi_buffer_size; /* nice to have SCSI buffer size */
+ int read_stripe_size; /* stripes to be read at-a-time */
+
+ /* film scanner atributes - maybe these should be in the scanner struct? */
+ SANE_Range frame_range;
+ SANE_Word current_frame;
+ SANE_Word holder_type;
+
+ /* some versin corrections */
+ uint16_t data_dq; /* was ox0A0D - but hangs some new scanners */
+
+ Avision_HWEntry* hw;
+} Avision_Device;
+
+/* all the state relevant for the SANE interface */
+typedef struct Avision_Scanner
+{
+ struct Avision_Scanner* next;
+ Avision_Device* hw;
+
+ SANE_Option_Descriptor opt [NUM_OPTIONS];
+ Option_Value val [NUM_OPTIONS];
+ SANE_Int gamma_table [4][256];
+
+ /* we now save the calib data because we might need it for 16bit software
+ calibration :-( */
+ uint8_t* dark_avg_data;
+ uint8_t* white_avg_data;
+
+ /* background raster data, if duplex first front, then rear */
+ uint8_t* background_raster;
+
+ /* Parsed option values and variables that are valid only during
+ the actual scan: */
+ SANE_Bool prepared; /* first page marker */
+ SANE_Bool scanning; /* scan in progress */
+ unsigned int page; /* page counter, 0: uninitialized, 1: scanning 1st page, ... */
+
+ SANE_Parameters params; /* scan window */
+ Avision_Dimensions avdimen; /* scan window - detailed internals */
+
+ /* Internal data for duplex scans */
+ char duplex_rear_fname [PATH_MAX];
+ SANE_Bool duplex_rear_valid;
+
+ color_mode c_mode;
+ source_mode source_mode;
+ source_mode_dim source_mode_dim;
+
+ /* Avision HW Access Connection (SCSI/USB abstraction) */
+ Avision_Connection av_con;
+
+ SANE_Pid reader_pid; /* process id of reader */
+ int read_fds; /* pipe reading end */
+ int write_fds; /* pipe writing end */
+
+} Avision_Scanner;
+
+/* Some Avision driver internal defines */
+#define AV_WINID 0
+
+/* Avision SCSI over USB error codes */
+#define AVISION_USB_GOOD 0x00
+#define AVISION_USB_REQUEST_SENSE 0x02
+#define AVISION_USB_BUSY 0x08
+
+/* SCSI commands that the Avision scanners understand: */
+
+#define AVISION_SCSI_TEST_UNIT_READY 0x00
+#define AVISION_SCSI_REQUEST_SENSE 0x03
+#define AVISION_SCSI_MEDIA_CHECK 0x08
+#define AVISION_SCSI_INQUIRY 0x12
+#define AVISION_SCSI_MODE_SELECT 0x15
+#define AVISION_SCSI_RESERVE_UNIT 0x16
+#define AVISION_SCSI_RELEASE_UNIT 0x17
+#define AVISION_SCSI_SCAN 0x1b
+#define AVISION_SCSI_SET_WINDOW 0x24
+#define AVISION_SCSI_READ 0x28
+#define AVISION_SCSI_SEND 0x2a
+#define AVISION_SCSI_OBJECT_POSITION 0x31
+#define AVISION_SCSI_GET_DATA_STATUS 0x34
+
+#define AVISION_SCSI_OP_REJECT_PAPER 0x00
+#define AVISION_SCSI_OP_LOAD_PAPER 0x01
+#define AVISION_SCSI_OP_GO_HOME 0x02
+#define AVISION_SCSI_OP_TRANS_CALIB_GRAY 0x04
+#define AVISION_SCSI_OP_TRANS_CALIB_COLOR 0x05
+
+/* These apply to bitset1. The values are 0 to 6, shifted 3 bits to the left */
+#define AVISION_FILTER_NONE 0x00
+#define AVISION_FILTER_RED 0x08
+#define AVISION_FILTER_GREEN 0x10
+#define AVISION_FILTER_BLUE 0x18
+#define AVISION_FILTER_RGB 0x20
+#define AVISION_FILTER_CMYK 0x28
+#define AVISION_FILTER_GRAY 0x30
+
+/* The SCSI structures that we have to send to an avision to get it to
+ do various stuff... */
+
+typedef struct command_header
+{
+ uint8_t opc;
+ uint8_t pad0 [3];
+ uint8_t len;
+ uint8_t pad1;
+} command_header;
+
+typedef struct command_set_window
+{
+ uint8_t opc;
+ uint8_t reserved0 [5];
+ uint8_t transferlen [3];
+ uint8_t control;
+} command_set_window;
+
+typedef struct command_read
+{
+ uint8_t opc;
+ uint8_t bitset1;
+ uint8_t datatypecode;
+ uint8_t readtype;
+ uint8_t datatypequal [2];
+ uint8_t transferlen [3];
+ uint8_t control;
+} command_read;
+
+typedef struct command_scan
+{
+ uint8_t opc;
+ uint8_t bitset0;
+ uint8_t reserved0 [2];
+ uint8_t transferlen;
+ uint8_t bitset1;
+} command_scan;
+
+typedef struct command_send
+{
+ uint8_t opc;
+ uint8_t bitset1;
+ uint8_t datatypecode;
+ uint8_t reserved0;
+ uint8_t datatypequal [2];
+ uint8_t transferlen [3];
+ uint8_t reserved1;
+} command_send;
+
+typedef struct firmware_status
+{
+ uint8_t download_firmware;
+ uint8_t first_effective_pixel_flatbed [2];
+ uint8_t first_effective_pixel_adf_front [2];
+ uint8_t first_effective_pixel_adf_rear [2];
+ uint8_t reserved;
+} firmware_status;
+
+typedef struct nvram_data
+{
+ uint8_t pad_scans [4];
+ uint8_t adf_simplex_scans [4];
+ uint8_t adf_duplex_scans [4];
+ uint8_t flatbed_scans [4];
+
+ uint8_t flatbed_leading_edge [2];
+ uint8_t flatbed_side_edge [2];
+ uint8_t adf_leading_edge [2];
+ uint8_t adf_side_edge [2];
+ uint8_t adf_rear_leading_edge [2];
+ uint8_t adf_rear_side_edge [2];
+
+ uint8_t born_month [2];
+ uint8_t born_day [2];
+ uint8_t born_year [2];
+
+ uint8_t first_scan_month [2];
+ uint8_t first_scan_day [2];
+ uint8_t first_scan_year [2];
+
+ uint8_t vertical_magnification [2];
+ uint8_t horizontal_magnification [2];
+
+ uint8_t ccd_type;
+ uint8_t scan_speed;
+
+ char serial [24];
+
+ uint8_t power_saving_time [2];
+
+ uint8_t auto_feed;
+ uint8_t roller_count [4];
+ uint8_t multifeed_count [4];
+ uint8_t jam_count [4];
+
+ uint8_t reserved;
+ char identify_info[16];
+ char formal_name[16];
+
+ uint8_t reserved2 [10];
+} nvram_data;
+
+typedef struct command_set_window_window
+{
+ struct {
+ uint8_t reserved0 [6];
+ uint8_t desclen [2];
+ } header;
+
+ struct {
+ uint8_t winid;
+ uint8_t reserved0;
+ uint8_t xres [2];
+ uint8_t yres [2];
+ uint8_t ulx [4];
+ uint8_t uly [4];
+ uint8_t width [4];
+ uint8_t length [4];
+ uint8_t brightness;
+ uint8_t threshold;
+ uint8_t contrast;
+ uint8_t image_comp;
+ uint8_t bpc;
+ uint8_t halftone [2];
+ uint8_t padding_and_bitset;
+ uint8_t bitordering [2];
+ uint8_t compr_type;
+ uint8_t compr_arg;
+ uint8_t paper_length[2];
+ uint8_t reserved1 [4];
+
+ /* Avision specific parameters */
+ uint8_t vendor_specific;
+ uint8_t paralen; /* bytes following after this byte */
+ } descriptor;
+
+ struct {
+ uint8_t bitset1;
+ uint8_t highlight;
+ uint8_t shadow;
+ uint8_t line_width [2];
+ uint8_t line_count [2];
+
+ /* the tail is quite version and model specific */
+ union {
+ struct {
+ uint8_t bitset2;
+ uint8_t reserved;
+ } old;
+
+ struct {
+ uint8_t bitset2;
+ uint8_t ir_exposure_time;
+
+ /* optional */
+ uint8_t r_exposure_time [2];
+ uint8_t g_exposure_time [2];
+ uint8_t b_exposure_time [2];
+
+ uint8_t bitset3; /* reserved in the v2 */
+ uint8_t auto_focus;
+ uint8_t line_width_msb;
+ uint8_t line_count_msb;
+ uint8_t background_lines;
+ } normal;
+
+ struct {
+ uint8_t reserved0 [4];
+ uint8_t paper_size;
+ uint8_t paperx [4];
+ uint8_t papery [4];
+ uint8_t reserved1 [2];
+ } fujitsu;
+ } type;
+ } avision;
+} command_set_window_window;
+
+typedef struct page_header
+{
+ uint8_t pad0 [4];
+ uint8_t code;
+ uint8_t length;
+} page_header;
+
+typedef struct avision_page
+{
+ uint8_t gamma;
+ uint8_t thresh;
+ uint8_t masks;
+ uint8_t delay;
+ uint8_t features;
+ uint8_t pad0;
+} avision_page;
+
+typedef struct calibration_format
+{
+ uint16_t pixel_per_line;
+ uint8_t bytes_per_channel;
+ uint8_t lines;
+ uint8_t flags;
+ uint8_t ability1;
+ uint8_t r_gain;
+ uint8_t g_gain;
+ uint8_t b_gain;
+ uint16_t r_shading_target;
+ uint16_t g_shading_target;
+ uint16_t b_shading_target;
+ uint16_t r_dark_shading_target;
+ uint16_t g_dark_shading_target;
+ uint16_t b_dark_shading_target;
+
+ /* not returned but usefull in some places */
+ uint8_t channels;
+} calibration_format;
+
+typedef struct matrix_3x3
+{
+ uint16_t v[9];
+} matrix_3x3;
+
+typedef struct acceleration_info
+{
+ uint16_t total_steps;
+ uint16_t stable_steps;
+ uint32_t table_units;
+ uint32_t base_units;
+ uint16_t start_speed;
+ uint16_t target_speed;
+ uint8_t ability;
+ uint8_t table_count;
+ uint8_t reserved[6];
+} acceleration_info;
+
+/* set/get SCSI highended (big-endian) variables. Declare them as an array
+ * of chars endianness-safe, int-size safe ... */
+#define set_double(var,val) var[0] = ((val) >> 8) & 0xff; \
+ var[1] = ((val) ) & 0xff
+
+#define set_triple(var,val) var[0] = ((val) >> 16) & 0xff; \
+ var[1] = ((val) >> 8 ) & 0xff; \
+ var[2] = ((val) ) & 0xff
+
+#define set_quad(var,val) var[0] = ((val) >> 24) & 0xff; \
+ var[1] = ((val) >> 16) & 0xff; \
+ var[2] = ((val) >> 8 ) & 0xff; \
+ var[3] = ((val) ) & 0xff
+
+#define get_double(var) ((*var << 8) + *(var + 1))
+
+#define get_triple(var) ((*var << 16) + \
+ (*(var + 1) << 8) + *(var + 2))
+
+#define get_quad(var) ((*var << 24) + \
+ (*(var + 1) << 16) + \
+ (*(var + 2) << 8) + *(var + 3))
+
+/* set/get Avision lowended (little-endian) shading data */
+#define set_double_le(var,val) var[0] = ((val) ) & 0xff; \
+ var[1] = ((val) >> 8) & 0xff
+
+#define get_double_le(var) ((*(var + 1) << 8) + *var)
+
+#define BIT(n, p) ((n & (1 << p)) ? 1 : 0)
+
+#define SET_BIT(n, p) (n |= (1 << p))
+#define CLEAR_BIT(n, p) (n &= ~(1 << p))
+
+/* These should be in saneopts.h */
+#define SANE_NAME_FRAME "frame"
+#define SANE_TITLE_FRAME SANE_I18N("Number of the frame to scan")
+#define SANE_DESC_FRAME SANE_I18N("Selects the number of the frame to scan")
+
+#define SANE_NAME_DUPLEX "duplex"
+#define SANE_TITLE_DUPLEX SANE_I18N("Duplex scan")
+#define SANE_DESC_DUPLEX SANE_I18N("Duplex scan provide a scan of the front and back side of the document")
+
+#ifdef AVISION_ENHANCED_SANE
+#warning "Compiled Avision backend will violate the SANE standard"
+/* Some Avision SANE extensions */
+typedef enum
+{
+ SANE_STATUS_LAMP_WARMING = SANE_STATUS_ACCESS_DENIED + 1 /* lamp is warming up */
+}
+SANE_Avision_Status;
+
+/* public API extension */
+
+extern SANE_Status ENTRY(media_check) (SANE_Handle handle);
+
+#endif
+
+#endif /* avision_h */
diff --git a/backend/bh.c b/backend/bh.c
new file mode 100644
index 0000000..58c8caa
--- /dev/null
+++ b/backend/bh.c
@@ -0,0 +1,3883 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1999,2000 Tom Martone
+ This file is part of a SANE backend for Bell and Howell Copiscan II
+ Scanners using the Remote SCSI Controller(RSC).
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+#include "../include/sane/config.h"
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_config.h"
+
+#define BACKEND_NAME bh
+#include "../include/sane/sanei_backend.h"
+#define BUILD 4
+
+#include "bh.h"
+
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+
+static int num_devices = 0;
+static BH_Device *first_dev = NULL;
+static BH_Scanner *first_handle = NULL;
+static SANE_Char inquiry_data[255] = "Bell+Howell scanner";
+static SANE_Int disable_optional_frames = 0;
+static SANE_Int fake_inquiry = 0;
+
+static int allblank(const char *s)
+{
+ while (s && *s)
+ if (!isspace(*s++))
+ return 0;
+
+ return 1;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+static void
+trim_spaces(char *s, size_t n)
+{
+ for (s += (n-1); n > 0; n--, s--)
+ {
+ if (*s && !isspace(*s))
+ break;
+ *s = '\0';
+ }
+}
+
+static SANE_String_Const
+print_devtype (SANE_Byte devtype)
+{
+ static SANE_String devtypes[] =
+ {
+ "disk",
+ "tape",
+ "printer",
+ "processor",
+ "CD-writer",
+ "CD-drive",
+ "scanner",
+ "optical-drive",
+ "jukebox",
+ "communicator"
+ };
+
+ return (devtype > 0 && devtype < NELEMS(devtypes)) ?
+ devtypes[devtype] :
+ "unknown-device";
+}
+
+static SANE_String_Const
+print_barcodetype (SANE_Int i)
+{
+ return (i > 0 && i < NELEMS(barcode_search_bar_list)) ?
+ barcode_search_bar_list[i] :
+ (SANE_String_Const) "unknown";
+}
+
+static SANE_String_Const
+print_orientation (SANE_Int i)
+{
+ switch(i)
+ {
+ case 0:
+ case 7:
+ return "vertical upwards";
+ case 1:
+ case 2:
+ return "horizontal right";
+ case 3:
+ case 4:
+ return "vertical downwards";
+ case 5:
+ case 6:
+ return "horizontal left";
+ default:
+ return "unknown";
+ }
+}
+
+static SANE_String_Const
+print_read_type (SANE_Int i)
+{
+ static char buf[32];
+ SANE_Int n;
+
+ /* translate BH_SCSI_READ_TYPE_ codes to a human-readable string */
+ if (i == BH_SCSI_READ_TYPE_FRONT)
+ {
+ strcpy(buf, "front page");
+ }
+ else if (i == BH_SCSI_READ_TYPE_BACK)
+ {
+ strcpy(buf, "back page");
+ }
+ else if (i > BH_SCSI_READ_TYPE_FRONT &&
+ i <= BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS)
+ {
+ n = i - BH_SCSI_READ_TYPE_FRONT;
+ sprintf(buf, "front section %d", n);
+ }
+ else if (i > BH_SCSI_READ_TYPE_BACK &&
+ i <= BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS)
+ {
+ n = i - BH_SCSI_READ_TYPE_BACK;
+ sprintf(buf, "back section %d", n);
+ }
+ else if (i == BH_SCSI_READ_TYPE_FRONT_BARCODE)
+ {
+ strcpy(buf, "front page barcode");
+ }
+ else if (i == BH_SCSI_READ_TYPE_BACK_BARCODE)
+ {
+ strcpy(buf, "back page barcode");
+ }
+ else if (i > BH_SCSI_READ_TYPE_FRONT_BARCODE &&
+ i <= BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)
+ {
+ n = i - BH_SCSI_READ_TYPE_FRONT_BARCODE;
+ sprintf(buf, "front barcode section %d", n);
+ }
+ else if (i > BH_SCSI_READ_TYPE_BACK_BARCODE &&
+ i <= BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)
+ {
+ n = i - BH_SCSI_READ_TYPE_BACK_BARCODE;
+ sprintf(buf, "back barcode section %d", n);
+ }
+ else if (i == BH_SCSI_READ_TYPE_FRONT_PATCHCODE)
+ {
+ strcpy(buf, "front page patchcode");
+ }
+ else if (i == BH_SCSI_READ_TYPE_BACK_PATCHCODE)
+ {
+ strcpy(buf, "back page patchcode");
+ }
+ else if (i > BH_SCSI_READ_TYPE_FRONT_PATCHCODE &&
+ i <= BH_SCSI_READ_TYPE_FRONT_PATCHCODE + NUM_SECTIONS)
+ {
+ n = i - BH_SCSI_READ_TYPE_FRONT_PATCHCODE;
+ sprintf(buf, "front patchcode section %d", n);
+ }
+ else if (i > BH_SCSI_READ_TYPE_BACK_PATCHCODE &&
+ i <= BH_SCSI_READ_TYPE_BACK_PATCHCODE + NUM_SECTIONS)
+ {
+ n = i - BH_SCSI_READ_TYPE_BACK_PATCHCODE;
+ sprintf(buf, "back patchcode section %d", n);
+ }
+ else if (i == BH_SCSI_READ_TYPE_FRONT_ICON)
+ {
+ strcpy(buf, "front page icon");
+ }
+ else if (i == BH_SCSI_READ_TYPE_BACK_ICON)
+ {
+ strcpy(buf, "back page icon");
+ }
+ else if (i == BH_SCSI_READ_TYPE_SENDBARFILE)
+ {
+ strcpy(buf, "transmit bar/patch codes");
+ }
+ else
+ {
+ strcpy(buf, "unknown");
+ }
+
+ return buf;
+}
+
+static SANE_Int
+get_rotation_id(char *s)
+{
+ SANE_Int i;
+
+ for (i = 0; rotation_list[i]; i++)
+ if (strcmp(s, rotation_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as '0' */
+ return rotation_list[i] ? i : 0;
+}
+
+static SANE_Int
+get_compression_id(char *s)
+{
+ SANE_Int i;
+
+ for (i = 0; compression_list[i]; i++)
+ if (strcmp(s, compression_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as 'none' */
+ return compression_list[i] ? i : 0;
+}
+
+static SANE_Int
+get_barcode_id(char *s)
+{
+ SANE_Int i;
+
+ for (i = 0; barcode_search_bar_list[i]; i++)
+ if (strcmp(s, barcode_search_bar_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as 'none' */
+ return barcode_search_bar_list[i] ? i : 0;
+}
+
+static SANE_Int
+get_scan_mode_id(char *s)
+{
+ SANE_Int i;
+
+ for (i = 0; scan_mode_list[i]; i++)
+ if (strcmp(s, scan_mode_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as 'lineart' */
+ return scan_mode_list[i] ? i : 0;
+}
+
+static SANE_Int
+get_paper_id(char *s)
+{
+ SANE_Int i;
+
+ for (i = 0; paper_list[i]; i++)
+ if (strcmp(s, paper_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as 'custom' */
+ return paper_list[i] ? i : 0;
+}
+
+static SANE_Int
+get_barcode_search_mode(char *s)
+{
+ SANE_Int i;
+
+ if (strcmp(s, "horizontal") == 0)
+ {
+ i = 1;
+ }
+ else if (strcmp(s, "vertical") == 0)
+ {
+ i = 2;
+ }
+ else if (strcmp(s, "vert-horiz") == 0)
+ {
+ i = 6;
+ }
+ else if (strcmp(s, "horiz-vert") == 0)
+ {
+ i = 9;
+ }
+ else
+ {
+ /* unknown strings are treated as 'horiz-vert' */
+ DBG(1, "get_barcode_search_mode: unrecognized string `%s'\n", s);
+ i = 9;
+ }
+
+ return i;
+}
+
+static void
+appendStdList(BH_Info *sc, SANE_Int res)
+{
+ /* append entry to resolution list - a SANE_WORD_LIST */
+ sc->resStdList[sc->resStdList[0]+1] = res;
+ sc->resStdList[0]++;
+}
+
+static void
+ScannerDump(BH_Scanner *s)
+{
+ int i;
+ BH_Info *info;
+ SANE_Device *sdev;
+
+ info = &s->hw->info;
+ sdev = &s->hw->sane;
+
+ DBG (1, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n",
+ sdev->name,
+ sdev->vendor,
+ sdev->model,
+ sdev->type);
+
+ DBG (1, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n",
+ print_devtype(info->devtype),
+ info->vendor,
+ info->product,
+ info->revision);
+
+ DBG (1, "Automatic Document Feeder:%s\n",
+ info->canADF ? " <Installed>" : " <Not Installed>");
+
+ DBG (1, "Colors:%s%s\n", info->colorBandW ? " <Black and White>" : "",
+ info->colorHalftone ? " <Halftone>" : "");
+
+ DBG (1, "Data processing:%s%s%s%s%s%s\n",
+ info->canWhiteFrame ? " <White Frame>" : "",
+ info->canBlackFrame ? " <Black Frame>" : "",
+ info->canEdgeExtract ? " <Edge Extraction>" : "",
+ info->canNoiseFilter ? " <Noise Filter>" : "",
+ info->canSmooth ? " <Smooth>" : "",
+ info->canLineBold ? " <Line Bolding>" : "");
+
+ DBG (1, "Compression:%s%s%s\n",
+ info->comprG3_1D ? " <Group 3, 1D>" : "",
+ info->comprG3_2D ? " <Group 3, 2D>" : "",
+ info->comprG4 ? " <Group 4>" : "");
+
+ DBG (1, "Optional Features:%s%s%s%s\n",
+ info->canBorderRecog ? " <Border Recognition>" : "",
+ info->canBarCode ? " <BarCode Decoding>" : "",
+ info->canIcon ? " <Icon Generation>" : "",
+ info->canSection ? " <Section Support>" : "");
+
+ DBG (1, "Max bytes per scan-line: %d (%d pixels)\n",
+ info->lineMaxBytes,
+ info->lineMaxBytes * 8);
+
+ DBG (1, "Basic resolution (X/Y): %d/%d\n",
+ info->resBasicX,
+ info->resBasicY);
+
+ DBG (1, "Maximum resolution (X/Y): %d/%d\n",
+ info->resMaxX,
+ info->resMaxY);
+
+ DBG (1, "Minimum resolution (X/Y): %d/%d\n",
+ info->resMinX,
+ info->resMinY);
+
+ DBG (1, "Standard Resolutions:\n");
+ for (i = 0; i < info->resStdList[0]; i++)
+ DBG (1, " %d\n", info->resStdList[i+1]);
+
+ DBG (1, "Window Width/Height (in basic res) %d/%d (%.2f/%.2f inches)\n",
+ info->winWidth,
+ info->winHeight,
+ (info->resBasicX != 0) ? ((float) info->winWidth) / info->resBasicX : 0.0,
+ (info->resBasicY) ? ((float) info->winHeight) / info->resBasicY : 0.0);
+
+ DBG (1, "Summary:%s%s%s\n",
+ info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
+ info->canACE ? " (ACE capable)" : "",
+ info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
+
+ sprintf(inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s%s\n",
+ info->vendor,
+ info->product,
+ info->revision,
+ info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
+ info->canACE ? " (ACE capable)" : "",
+ info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
+
+ DBG (5, "autoborder_default=%d\n", info->autoborder_default);
+ DBG (5, "batch_default=%d\n", info->batch_default);
+ DBG (5, "deskew_default=%d\n", info->deskew_default);
+ DBG (5, "check_adf_default=%d\n", info->check_adf_default);
+ DBG (5, "duplex_default=%d\n", info->duplex_default);
+ DBG (5, "timeout_adf_default=%d\n", info->timeout_adf_default);
+ DBG (5, "timeout_manual_default=%d\n", info->timeout_manual_default);
+ DBG (5, "control_panel_default=%d\n", info->control_panel_default);
+
+}
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (3, "test_unit_ready called\n");
+
+ cmd[0] = BH_SCSI_TEST_UNIT_READY;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+object_position (BH_Scanner *s)
+{
+ static SANE_Byte cmd[10];
+ SANE_Status status;
+ DBG (3, "object_position called\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = BH_SCSI_OBJECT_POSITION;
+ cmd[1] = 0x01;
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+read_barcode_data (BH_Scanner *s, FILE *fp)
+{
+ static SANE_Byte cmd[10];
+ SANE_Status status;
+ SANE_Int num_found = 0;
+ double w, l, x, y, res;
+ struct barcode_data buf;
+ size_t buf_size = sizeof(buf);
+ DBG (3, "read_barcode_data called\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd[0] = BH_SCSI_READ_SCANNED_DATA;
+ cmd[2] = s->readlist[s->readptr];
+ _lto3b(buf_size, &cmd[6]); /* transfer length */
+
+ s->barcode_not_found = SANE_FALSE;
+ do {
+ memset (&buf, 0, sizeof(buf));
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ if (s->barcode_not_found == SANE_TRUE)
+ break;
+
+ num_found++;
+
+ buf.barcodedata[sizeof(buf.barcodedata)-1] = '\0';
+
+ /* calculate the bounding rectangle */
+ x = MIN((int) _2btol(buf.posxb), (int) _2btol(buf.posxa));
+ y = MIN((int) _2btol(buf.posyb), (int) _2btol(buf.posyd));
+ w = MAX((int) _2btol(buf.posxd), (int) _2btol(buf.posxd)) - x;
+ l = MAX((int) _2btol(buf.posya), (int) _2btol(buf.posyc)) - y;
+ /* convert from pixels to mm */
+ res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
+ if (res <= 0.0)
+ {
+ /* avoid divide by zero */
+ DBG(1, "read_barcode_data: warning: "
+ "encountered bad resolution value '%f', replacing with '%f'\n",
+ res, 200.0);
+ res = 200.0;
+ }
+ x = x * MM_PER_INCH / res;
+ y = y * MM_PER_INCH / res;
+ w = w * MM_PER_INCH / res;
+ l = l * MM_PER_INCH / res;
+ /* add a bit of a border around the edges */
+ x = MAX(0.0, x - BH_DECODE_FUDGE);
+ y = MAX(0.0, y - BH_DECODE_FUDGE);
+ w += (BH_DECODE_FUDGE * 4);
+ l += (BH_DECODE_FUDGE * 4);
+
+ /* write the decoded barcode data into the file */
+ fprintf(fp, "<barcode>\n <section>%s</section>\n",
+ print_read_type((int) s->readlist[s->readptr]));
+ fprintf(fp, " <type>%s</type>\n <status-flag>%d</status-flag>\n",
+ print_barcodetype((int) _2btol(buf.barcodetype)),
+ (int) _2btol(buf.statusflag));
+ fprintf(fp, " <orientation>%s</orientation>\n",
+ print_orientation((int) _2btol(buf.barcodeorientation)));
+ fprintf(fp, " <location>\n <tl><x>%d</x><y>%d</y></tl>\n",
+ (int) _2btol(buf.posxb), (int) _2btol(buf.posyb));
+ fprintf(fp, " <tr><x>%d</x><y>%d</y></tr>\n",
+ (int) _2btol(buf.posxd), (int) _2btol(buf.posyd));
+ fprintf(fp, " <bl><x>%d</x><y>%d</y></bl>\n",
+ (int) _2btol(buf.posxa), (int) _2btol(buf.posya));
+ fprintf(fp, " <br><x>%d</x><y>%d</y></br>\n </location>\n",
+ (int) _2btol(buf.posxc), (int) _2btol(buf.posyc));
+ fprintf(fp, " <rectangle>%.2fx%.2f+%.2f+%.2f</rectangle>\n",
+ w, l, x, y);
+ fprintf(fp, " <search-time>%d</search-time>\n <length>%d</length>\n",
+ (int) _2btol(buf.barcodesearchtime),
+ (int) buf.barcodelen);
+ fprintf(fp, " <data>%s</data>\n</barcode>\n",
+ buf.barcodedata);
+ } while (num_found <= BH_DECODE_TRIES);
+
+ DBG (3, "read_barcode_data: found %d barcodes, returning %s\n",
+ num_found, sane_strstatus(status));
+
+ return status;
+}
+
+static SANE_Status
+read_icon_data (BH_Scanner *s)
+{
+ static SANE_Byte cmd[10];
+ SANE_Status status;
+ struct icon_data buf;
+ size_t buf_size = sizeof(buf);
+ DBG (3, "read_icon_data called\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd[0] = BH_SCSI_READ_SCANNED_DATA;
+ cmd[2] = s->readlist[s->readptr];
+ _lto3b(buf_size, &cmd[6]); /* transfer length */
+
+ memset (&buf, 0, sizeof(buf));
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size);
+
+ /* set the fields in the scanner handle for later reference */
+ s->iconwidth = _4btol(buf.iconwidth);
+ s->iconlength = _4btol(buf.iconlength);
+
+ DBG(3, "read_icon_data: windowwidth:%lu, windowlength:%lu\n",
+ _4btol(buf.windowwidth),
+ _4btol(buf.windowlength));
+ DBG(3, "read_icon_data: iconwidth:%lu, iconlength:%lu, iconwidth(bytes):%lu\n",
+ _4btol(buf.iconwidth),
+ _4btol(buf.iconlength),
+ _4btol(buf.iconwidthbytes));
+ DBG(3, "read_icon_data: bitordering:%02x, icondatalen:%lu\n",
+ buf.bitordering,
+ _4btol(buf.icondatalen));
+
+ DBG (3, "read_icon_data returning %d\n", status);
+
+ return status;
+}
+
+static SANE_Status
+read_barfile (BH_Scanner *s, void *buf, size_t *buf_size)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t nread;
+ DBG (3, "read_barfile called (%lu bytes)\n", (u_long) *buf_size);
+
+ if (s->barf != NULL)
+ {
+ /* this function needs to set InvalidBytes so it looks
+ * like a B&H scsi EOF
+ */
+ if ((nread = fread(buf, 1, *buf_size, s->barf)) < *buf_size)
+ {
+ /* set InvalidBytes */
+ s->InvalidBytes = *buf_size - nread;
+
+ if (ferror(s->barf))
+ {
+ status = SANE_STATUS_IO_ERROR;
+ fclose(s->barf);
+ s->barf = NULL;
+ unlink(s->barfname);
+ }
+ else if (feof(s->barf))
+ {
+ /* it also needs to close the file and delete it when EOF is
+ * reached.
+ */
+ fclose(s->barf);
+ s->barf = NULL;
+ unlink(s->barfname);
+ }
+ }
+ }
+ else
+ {
+ /* set InvalidBytes */
+ s->InvalidBytes = *buf_size;
+ }
+
+ return status;
+}
+
+static SANE_Status
+read_data (BH_Scanner *s, void *buf, size_t *buf_size)
+{
+ static SANE_Byte cmd[10];
+ SANE_Status status;
+ DBG (3, "read_data called (%lu bytes)\n", (u_long) *buf_size);
+
+ if (s->readlist[s->readptr] == BH_SCSI_READ_TYPE_SENDBARFILE)
+ {
+ /* call special barcode data read function. */
+ status = read_barfile(s, buf, buf_size);
+ }
+ else
+ {
+ memset (&cmd, 0, sizeof (cmd));
+ cmd[0] = BH_SCSI_READ_SCANNED_DATA;
+ cmd[2] = s->readlist[s->readptr];
+ _lto3b(*buf_size, &cmd[6]); /* transfer length */
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, buf_size);
+ }
+
+ return status;
+}
+
+static SANE_Status
+mode_select_measurement (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_03 mp;
+ } select_cmd;
+ SANE_Status status;
+
+ DBG (3, "mode_select_measurement called (bmu:%d mud:%d)\n",
+ s->bmu, s->mud);
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.pagecode = BH_MODE_MEASUREMENT_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+ select_cmd.mp.bmu = s->bmu;
+ _lto2b(s->mud, select_cmd.mp.mud);
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+mode_select_timeout (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_20 mp;
+ } select_cmd;
+ SANE_Status status;
+
+ DBG (3, "mode_select_timeout called\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.pagecode = BH_MODE_TIMEOUT_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+ select_cmd.mp.timeoutmanual = _OPT_VAL_WORD(s, OPT_TIMEOUT_MANUAL);
+ select_cmd.mp.timeoutadf = _OPT_VAL_WORD(s, OPT_TIMEOUT_ADF);
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+mode_select_icon (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_21 mp;
+ } select_cmd;
+ SANE_Status status;
+
+ DBG (3, "mode_select_icon called\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.pagecode = BH_MODE_ICON_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+ _lto2b(_OPT_VAL_WORD(s, OPT_ICON_WIDTH), select_cmd.mp.iconwidth);
+ _lto2b(_OPT_VAL_WORD(s, OPT_ICON_LENGTH), select_cmd.mp.iconlength);
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+mode_select_barcode_priority (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_30 mp;
+ } select_cmd;
+ SANE_Status status;
+ int i;
+
+ DBG (3, "mode_select_barcode_priority called\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.pagecode = BH_MODE_BARCODE_PRIORITY_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+
+ for (i = 0; i < NUM_SEARCH_BARS; i++)
+ {
+ /* anything after a 'none' is ignored */
+ if ((select_cmd.mp.priority[i] = s->search_bars[i]) == 0) break;
+ }
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+mode_select_barcode_param1 (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_31 mp;
+ } select_cmd;
+ SANE_Status status;
+
+ DBG (3, "mode_select_barcode_param1 called\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM1_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+
+ _lto2b((SANE_Int)_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BARCODE_HMIN), select_cmd.mp.minbarheight);
+ select_cmd.mp.searchcount = _OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_COUNT);
+ select_cmd.mp.searchmode =
+ get_barcode_search_mode(_OPT_VAL_STRING(s, OPT_BARCODE_SEARCH_MODE));
+ _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_TIMEOUT), select_cmd.mp.searchtimeout);
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ return status;
+}
+
+static SANE_Status
+mode_select_barcode_param2 (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_32 mp;
+ } select_cmd;
+ SANE_Status status;
+ size_t len;
+
+ DBG (3, "mode_select_barcode_param2 called\n");
+
+ /* first we'll do a mode sense, then we'll overwrite with
+ * our new values, and then do a mode select
+ */
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SENSE;
+ select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM2_PAGE_CODE;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ len = sizeof(select_cmd.mp);
+ status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd),
+ &select_cmd.mp, &len);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG(8, "mode_select_barcode_param2: sensed values: relmax:%d barmin:%d barmax:%d\n",
+ (int) _2btol(select_cmd.mp.relmax),
+ (int) _2btol(select_cmd.mp.barmin),
+ (int) _2btol(select_cmd.mp.barmax));
+
+ memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.modedatalen = 0x00;
+ select_cmd.mp.mediumtype = 0x00;
+ select_cmd.mp.devicespecificparam = 0x00;
+ select_cmd.mp.blockdescriptorlen = 0x00;
+
+ select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM2_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+
+ /* only overwrite the default values if the option is non-zero */
+ if (_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX) != 0)
+ {
+ _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX), select_cmd.mp.relmax);
+ }
+ if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN) != 0)
+ {
+ _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN), select_cmd.mp.barmin);
+ }
+ if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX) != 0)
+ {
+ _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX), select_cmd.mp.barmax);
+ }
+
+ DBG(8, "mode_select_barcode_param2: param values: relmax:%d barmin:%d barmax:%d\n",
+ (int) _OPT_VAL_WORD(s, OPT_BARCODE_RELMAX),
+ (int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMIN),
+ (int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMAX));
+
+ DBG(8, "mode_select_barcode_param2: select values: relmax:%d barmin:%d barmax:%d\n",
+ (int) _2btol(select_cmd.mp.relmax),
+ (int) _2btol(select_cmd.mp.barmin),
+ (int) _2btol(select_cmd.mp.barmax));
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+ }
+
+ return status;
+}
+
+static SANE_Status
+mode_select_barcode_param3 (BH_Scanner *s)
+{
+ static struct {
+ SANE_Byte cmd[6];
+ struct mode_page_33 mp;
+ } select_cmd;
+ SANE_Status status;
+ size_t len;
+
+ DBG (3, "mode_select_barcode_param3 called\n");
+
+ /* first we'll do a mode sense, then we'll overwrite with
+ * our new values, and then do a mode select
+ */
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SENSE;
+ select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM3_PAGE_CODE;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ len = sizeof(select_cmd.mp);
+ status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd),
+ &select_cmd.mp, &len);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG(8, "mode_select_barcode_param3: sensed values: contrast:%d patchmode:%d\n",
+ (int) _2btol(select_cmd.mp.barcodecontrast),
+ (int) _2btol(select_cmd.mp.patchmode));
+
+ memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd));
+ select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
+ select_cmd.cmd[1] = 0x10;
+ select_cmd.cmd[4] = sizeof(select_cmd.mp);
+
+ select_cmd.mp.modedatalen = 0x00;
+ select_cmd.mp.mediumtype = 0x00;
+ select_cmd.mp.devicespecificparam = 0x00;
+ select_cmd.mp.blockdescriptorlen = 0x00;
+
+ select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM3_PAGE_CODE;
+ select_cmd.mp.paramlen = 0x06;
+
+ /* only overwrite the default values if the option is non-zero */
+ if (_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST) != 0)
+ {
+ _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST), select_cmd.mp.barcodecontrast);
+ }
+ if (_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE) != 0)
+ {
+ _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE), select_cmd.mp.patchmode);
+ }
+
+ DBG(8, "mode_select_barcode_param3: param values: contrast:%d patchmode:%d\n",
+ (int) _OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST),
+ (int) _OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE));
+
+ DBG(8, "mode_select_barcode_param3: select values: contrast:%d patchmode:%d\n",
+ (int) _2btol(select_cmd.mp.barcodecontrast),
+ (int) _2btol(select_cmd.mp.patchmode));
+
+ status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
+ }
+
+ return status;
+}
+
+static SANE_Status
+inquiry (int fd, void *buf, size_t *buf_size, SANE_Byte evpd, SANE_Byte page_code)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (3, "inquiry called\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = BH_SCSI_INQUIRY;
+ cmd[1] = evpd;
+ cmd[2] = page_code;
+ cmd[4] = *buf_size;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ return status;
+}
+
+static SANE_Status
+set_window (BH_Scanner *s, SANE_Byte batchmode)
+{
+ static struct {
+ SANE_Byte cmd[10];
+ SANE_Byte hdr[8];
+ struct window_data window;
+ } set_window_cmd;
+ SANE_Status status;
+ SANE_Int width, length, i, format, rotation, deskew ;
+
+ DBG (3, "set_window called\n");
+
+ /* set to thousandths for set_window */
+ s->bmu = BH_UNIT_INCH;
+ s->mud = 1000;
+ status = mode_select_measurement(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ memset (&set_window_cmd, 0, sizeof (set_window_cmd));
+ set_window_cmd.cmd[0] = BH_SCSI_SET_WINDOW;
+ DBG(3, "set_window: sizeof(hdr) %d, sizeof(window): %d\n",
+ (int)sizeof(set_window_cmd.hdr), (int)sizeof(set_window_cmd.window));
+
+ _lto3b(sizeof(set_window_cmd.hdr) + sizeof(set_window_cmd.window),
+ &set_window_cmd.cmd[6]);
+
+ _lto2b(256, &set_window_cmd.hdr[6]);
+
+ set_window_cmd.window.windowid = 0;
+ set_window_cmd.window.autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER);
+ DBG (5, "autoborder set to=%d\n", set_window_cmd.window.autoborder);
+ _lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.xres);
+ _lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.yres);
+ _lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X), set_window_cmd.window.ulx);
+ _lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y), set_window_cmd.window.uly);
+
+ width = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X) -
+ _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X));
+ length = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y) -
+ _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y));
+
+ _lto4b(width, set_window_cmd.window.windowwidth);
+ _lto4b(length, set_window_cmd.window.windowlength);
+
+ /* brightness (1-255) 0 is default, aka 128. Ignored with ACE scanners */
+ set_window_cmd.window.brightness = _OPT_VAL_WORD(s, OPT_BRIGHTNESS);
+ /* threshold (1-255) 0 is default, aka 128. Ignored with ACE scanners */
+ set_window_cmd.window.threshold = _OPT_VAL_WORD(s, OPT_THRESHOLD);
+ /*!!! contrast (not used) */
+ /*!!! set_window_cmd.window.contrast = _OPT_VAL_WORD(s, OPT_CONTRAST); */
+ /* imagecomposition 0x00 lineart, 0x01 dithered/halftone, 0x02 grayscale*/
+ set_window_cmd.window.imagecomposition =
+ get_scan_mode_id(_OPT_VAL_STRING(s, OPT_SCAN_MODE));
+
+ set_window_cmd.window.bitsperpixel = 0x01;
+ /*!!! halftone code (not used) */
+ /*!!! halftone id (not used) */
+
+ set_window_cmd.window.paddingtype = 0x03; /* truncate byte */
+ if (_OPT_VAL_WORD(s, OPT_NEGATIVE) == SANE_TRUE) {
+ /* reverse image format (valid when bitsperpixel=1)
+ * 0x00 normal, 0x01 reversed. This is bit 7 of paddingtype.
+ */
+ set_window_cmd.window.paddingtype |= 0x80;
+ }
+
+ set_window_cmd.window.bitordering[0] = 0x00;
+
+ /* we must always sent plain gray data in preview mode */
+ format = (_OPT_VAL_WORD(s, OPT_PREVIEW)) ?
+ BH_COMP_NONE :
+ get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
+
+ switch (format)
+ {
+ case BH_COMP_G31D:
+ set_window_cmd.window.compressiontype = 0x01;
+ set_window_cmd.window.compressionarg = 0x00;
+ set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
+ break;
+ case BH_COMP_G32D:
+ set_window_cmd.window.compressiontype = 0x02;
+ set_window_cmd.window.compressionarg = 0x04;
+ set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
+ break;
+ case BH_COMP_G42D:
+ set_window_cmd.window.compressiontype = 0x03;
+ set_window_cmd.window.compressionarg = 0x00;
+ set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
+ break;
+ case BH_COMP_NONE:
+ default:
+ set_window_cmd.window.compressiontype = 0x00;
+ set_window_cmd.window.compressionarg = 0x00;
+ set_window_cmd.window.bitordering[1] = 0x00; /* n/a */
+ break;
+ }
+
+ /* rotation and deskew settings, if autoborder is turned on */
+ if(set_window_cmd.window.autoborder){ /*--- setting byte 46 of the window descriptor block only works with autoborder */
+ rotation = get_rotation_id(_OPT_VAL_STRING(s, OPT_ROTATION));
+ if (_OPT_VAL_WORD(s, OPT_DESKEW) == SANE_TRUE) deskew = BH_DESKEW_ENABLE;
+ else deskew = BH_DESKEW_DISABLE;
+ set_window_cmd.window.border_rotation = ( rotation | deskew ); /*--- deskew assumes autoborder */
+ }
+
+ /* remote - 0x00 ACE set in window; 0x01 ACE set by control panel */
+ set_window_cmd.window.remote = _OPT_VAL_WORD(s, OPT_CONTROL_PANEL);
+ if (set_window_cmd.window.remote == 0x00) {
+ /* acefunction (ignored on non-ACE scanners) */
+ set_window_cmd.window.acefunction = _OPT_VAL_WORD(s, OPT_ACE_FUNCTION);
+ /* acesensitivity (ignored on non-ACE scanners) */
+ set_window_cmd.window.acesensitivity = _OPT_VAL_WORD(s, OPT_ACE_SENSITIVITY);
+ }
+
+ set_window_cmd.window.batchmode = batchmode;
+
+ /* fill in the section descriptor blocks */
+ for (i = 0; i < s->num_sections; i++)
+ {
+ BH_SectionBlock *b;
+
+ b = &set_window_cmd.window.sectionblock[i];
+
+ _lto4b(s->sections[i].left, b->ul_x);
+ _lto4b(s->sections[i].top, b->ul_y);
+ _lto4b(s->sections[i].width, b->width);
+ _lto4b(s->sections[i].length, b->length);
+ b->compressiontype = s->sections[i].compressiontype;
+ b->compressionarg = s->sections[i].compressionarg;
+ }
+
+ status = sanei_scsi_cmd (s->fd, &set_window_cmd, sizeof (set_window_cmd), 0, 0);
+ DBG (5, "sanei_scsi_cmd executed, status=%d\n", status );
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* set to points for reading */
+ s->bmu = BH_UNIT_POINT;
+ s->mud = 1;
+ status = mode_select_measurement(s);
+
+ return status;
+}
+
+static SANE_Status
+get_window (BH_Scanner *s, SANE_Int *w, SANE_Int *h, SANE_Bool backpage)
+{
+ SANE_Byte cmd[10];
+ static struct {
+ SANE_Byte hdr[8];
+ struct window_data window;
+ } get_window_data;
+ SANE_Status status;
+ SANE_Int x, y, i = 0, get_window_delay = 1;
+ SANE_Bool autoborder;
+ size_t len;
+
+ DBG (3, "get_window called\n");
+
+ autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER) == 1;
+
+ while (1)
+ {
+ i++;
+ memset (&cmd, 0, sizeof (cmd));
+ memset (&get_window_data, 0, sizeof (get_window_data));
+
+ cmd[0] = BH_SCSI_GET_WINDOW;
+ _lto3b(sizeof(get_window_data), &cmd[6]);
+
+ _lto2b(256, &get_window_data.hdr[6]);
+
+ get_window_data.window.windowid = (backpage == SANE_TRUE) ? 1 : 0;
+
+ len = sizeof(get_window_data);
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd),
+ &get_window_data, &len);
+ if (status == SANE_STATUS_GOOD)
+ {
+ x =_4btol(get_window_data.window.ulx);
+ y =_4btol(get_window_data.window.uly);
+ *w =_4btol(get_window_data.window.windowwidth);
+ *h =_4btol(get_window_data.window.windowlength);
+
+ if (autoborder)
+ {
+ /* we try repeatedly until we get the autoborder bit set */
+ if (get_window_data.window.autoborder != 1 &&
+ i < BH_AUTOBORDER_TRIES)
+ {
+ DBG (5, "waiting %d second[s], try: %d\n",get_window_delay,i);
+ sleep(get_window_delay); /*--- page 4-5 of B&H Copiscan 8000 ESC OEM Tech Manual */
+ /*--- requires at least 50ms wait between each GET WINDOW command */
+ /*--- experience shows that this can take 3 to 4 seconds */
+ continue;
+ }
+ if (get_window_data.window.autoborder != 1)
+ {
+ DBG(1, "Automatic Border Detection not done within %d tries\n",
+ BH_AUTOBORDER_TRIES);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ DBG (0, "page dimension: wide:%d high:%d \n",*w,*h);
+ }
+ DBG (3, "*** Window size: %dx%d+%d+%d\n", *w, *h, x, y);
+ DBG (5, "*** get_window found autoborder=%02xh\n", get_window_data.window.autoborder);
+ DBG (5, "*** get_window found border_rotation=%02xh\n", get_window_data.window.border_rotation);
+ }
+
+ /* we are 'outta here' */
+ break;
+ }
+
+ return status;
+}
+
+static SANE_Status
+get_parameters (SANE_Handle handle, SANE_Parameters *params)
+{
+ BH_Scanner *s = handle;
+ SANE_Int width, length, res, comp;
+ double br_x, tl_x, br_y, tl_y;
+ SANE_Frame format;
+
+ DBG(3, "get_parameters called\n");
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
+
+ /* make best-effort guess at what parameters will look like once
+ the scan starts. */
+
+ br_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X);
+ br_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y);
+ tl_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X);
+ tl_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y);
+
+ width = (br_x - tl_x + 1) * res / 1000.0;
+ length = (br_y - tl_y + 1) * res / 1000.0;
+
+ /* figure out the default image format for front/back pages */
+ comp = get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
+ switch (comp)
+ {
+ case BH_COMP_G31D:
+ format = SANE_FRAME_G31D;
+ break;
+ case BH_COMP_G32D:
+ format = SANE_FRAME_G32D;
+ break;
+ case BH_COMP_G42D:
+ format = SANE_FRAME_G42D;
+ break;
+ case BH_COMP_NONE:
+ default:
+ format = SANE_FRAME_GRAY;
+ break;
+ }
+
+ if (s->scanning)
+ {
+ SANE_Int w, l, status;
+ SANE_Byte itemtype;
+
+ itemtype = s->readlist[s->readptr];
+ /* update parameters based on the current item */
+
+ status = SANE_STATUS_GOOD;
+ if (itemtype == BH_SCSI_READ_TYPE_FRONT)
+ {
+ DBG (3, "get_parameters: sending GET WINDOW (front)\n");
+ status = get_window (s, &w, &l, SANE_FALSE);
+ if (status == SANE_STATUS_GOOD)
+ {
+ width = w;
+ length = l;
+ }
+ }
+ else if (itemtype == BH_SCSI_READ_TYPE_BACK)
+ {
+ DBG (3, "get_parameters: sending GET WINDOW (back)\n");
+ status = get_window (s, &w, &l, SANE_TRUE);
+ if (status == SANE_STATUS_GOOD)
+ {
+ width = w;
+ length = l;
+ }
+ }
+ else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON ||
+ itemtype == BH_SCSI_READ_TYPE_BACK_ICON)
+ {
+ /* the icon is never compressed */
+ format = SANE_FRAME_GRAY;
+ width = s->iconwidth;
+ length = s->iconlength;
+ }
+ else if (itemtype > BH_SCSI_READ_TYPE_FRONT &&
+ itemtype <= (BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS))
+ {
+ /* a front section */
+ SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_FRONT;
+
+ format = s->sections[sectnum - 1].format;
+ /* convert from thousandths to pixels */
+ width = s->sections[sectnum - 1].width * res / 1000.0;
+ length = s->sections[sectnum - 1].length * res / 1000.0;
+ }
+ else if (itemtype > BH_SCSI_READ_TYPE_BACK &&
+ itemtype <= (BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS))
+ {
+ /* a back section */
+ SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_BACK;
+
+ format = s->sections[sectnum - 1].format;
+ /* convert from thousandths to pixels */
+ width = s->sections[sectnum - 1].width * res / 1000.0;
+ length = s->sections[sectnum - 1].length * res / 1000.0;
+ }
+ else if ( (itemtype >= BH_SCSI_READ_TYPE_BACK_BARCODE &&
+ itemtype <= (BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)) ||
+ (itemtype >= BH_SCSI_READ_TYPE_FRONT_BARCODE &&
+ itemtype <= (BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)) )
+ {
+ /* decoded barcode data */
+ format = SANE_FRAME_TEXT;
+ width = 8;
+ length = -1;
+ }
+ else if (itemtype == BH_SCSI_READ_TYPE_SENDBARFILE)
+ {
+ /* decoded barcode data file */
+ format = SANE_FRAME_TEXT;
+ width = 8;
+ length = -1;
+ }
+ else
+ {
+ format = SANE_FRAME_GRAY;
+ width = 8;
+ length = -1;
+ DBG(1, "get_parameters: unrecognized read itemtype: %d\n",
+ itemtype);
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "get_parameters: failed\n");
+ return status;
+ }
+ }
+
+ if (res <= 0 || width <= 0)
+ {
+ DBG(1, "get_parameters:illegal parameters res=%d, width=%d, length=%d\n",
+ res, width, length);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* we disable our compression/barcode formats in preview as well
+ * as with the disable_optional_frames configuration option. NOTE:
+ * we may still be delivering 'wierd' data and lying about it being _GRAY!
+ */
+ if (format != SANE_FRAME_GRAY &&
+ (_OPT_VAL_WORD(s, OPT_PREVIEW) || disable_optional_frames))
+ {
+ DBG(1, "get_parameters: warning: delivering %s data as gray",
+ sane_strframe(format));
+ format = SANE_FRAME_GRAY;
+ }
+
+ s->params.format = format;
+ s->params.depth = 1;
+ s->params.last_frame = SANE_TRUE;
+ s->params.pixels_per_line = width;
+ s->params.lines = length;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ /* The Bell and Howell truncates to the byte */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+
+ if (params)
+ *params = s->params;
+
+ DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, "
+ "lines=%d, dpi=%d\n",
+ (int) s->params.format,
+ s->params.pixels_per_line,
+ s->params.bytes_per_line,
+ s->params.lines,
+ res);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+section_parse(const char *val, BH_Section *sect, SANE_Int res, SANE_Int comp)
+{
+ SANE_Status status = SANE_STATUS_INVAL;
+ char buf[255+1], *x, *y, *w, *l, *f, *ep;
+ const char *seps = "x+:";
+ double mm, fpixels;
+ u_long pixels;
+
+ DBG(3, "section_parse called\n");
+
+ /* a section option looks something like this:
+ * <width>x<length>+<tl-x>+<tl-y>:<functioncodes>
+ * Example:
+ * 76.2x25.4+50.8+0:frontbar:back:front
+ * the width, length, tl-x, and tl-y are in mm.
+ * the function codes are one or more of:
+ * front, back, frontbar, backbar, frontpatch, backpatch
+ */
+ if (strlen(val) > sizeof(buf) - 1)
+ {
+ DBG(1, "section_parse: option string too long\n");
+ status = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ do {
+ strcpy(buf, val);
+
+ x = y = w = l = f = NULL;
+ w = strtok(buf, seps);
+ if (w) l = strtok(NULL, seps);
+ if (l) x = strtok(NULL, seps);
+ if (x) y = strtok(NULL, seps);
+ if (y) f = strtok(NULL, seps);
+ if (!x || !y || !w || !l) break;
+
+ mm = strtod(x, &ep);
+ if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
+ sect->left = mm * 1000.0 / MM_PER_INCH;
+
+ mm = strtod(y, &ep);
+ if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
+ sect->top = mm * 1000.0 / MM_PER_INCH;
+
+ mm = strtod(w, &ep);
+ if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
+ sect->width = mm * 1000.0 / MM_PER_INCH;
+ /* the window width must be truncated to 16 bit points */
+ fpixels = sect->width * res / 1000.0;
+ pixels = fpixels / 16;
+ sect->width = pixels * 16 * 1000 / res;
+
+ mm = strtod(l, &ep);
+ if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
+ sect->length = mm * 1000.0 / MM_PER_INCH;
+
+ status = SANE_STATUS_GOOD;
+ while (f)
+ {
+ /* parse the function modifiers and set flags */
+ if (strcmp(f, "front") == 0)
+ sect->flags |= BH_SECTION_FRONT_IMAGE;
+ else if (strcmp(f, "frontbar") == 0)
+ sect->flags |= BH_SECTION_FRONT_BAR;
+ else if (strcmp(f, "frontpatch") == 0)
+ sect->flags |= BH_SECTION_FRONT_PATCH;
+ else if (strcmp(f, "back") == 0)
+ sect->flags |= BH_SECTION_BACK_IMAGE;
+ else if (strcmp(f, "backbar") == 0)
+ sect->flags |= BH_SECTION_BACK_BAR;
+ else if (strcmp(f, "backpatch") == 0)
+ sect->flags |= BH_SECTION_BACK_PATCH;
+ else if (strcmp(f, "g42d") == 0)
+ comp = BH_COMP_G42D;
+ else if (strcmp(f, "g32d") == 0)
+ comp = BH_COMP_G32D;
+ else if (strcmp(f, "g31d") == 0)
+ comp = BH_COMP_G31D;
+ else if (strcmp(f, "none") == 0)
+ comp = BH_COMP_NONE;
+ else
+ DBG(1, "section_parse: ignoring unrecognized function "
+ "code '%s'\n", f);
+
+ f = strtok(NULL, seps);
+ }
+
+ switch (comp)
+ {
+ case BH_COMP_G31D:
+ sect->compressiontype = 0x01;
+ sect->compressionarg = 0x00;
+ sect->format = SANE_FRAME_G31D;
+ break;
+ case BH_COMP_G32D:
+ sect->compressiontype = 0x02;
+ sect->compressionarg = 0x04;
+ sect->format = SANE_FRAME_G32D;
+ break;
+ case BH_COMP_G42D:
+ sect->compressiontype = 0x03;
+ sect->compressionarg = 0x00;
+ sect->format = SANE_FRAME_G42D;
+ break;
+ case BH_COMP_NONE:
+ default:
+ sect->compressiontype = 0x00;
+ sect->compressionarg = 0x00;
+ sect->format = SANE_FRAME_GRAY;
+ break;
+ }
+
+ DBG(3, "section_parse: converted '%s' (mm) to "
+ "%ldx%ld+%ld+%ld (thousandths) "
+ "flags=%02x compression=[%d,%d] frame=%s\n",
+ val,
+ sect->width, sect->length, sect->left, sect->top,
+ sect->flags,
+ sect->compressiontype, sect->compressionarg,
+ sane_strframe(sect->format));
+
+ } while (0); /* perform 'loop' once */
+ }
+
+ return status;
+}
+
+static SANE_Status
+setup_sections (BH_Scanner *s, const char *val)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int sectnum = 0;
+ char buf[255+1], *section;
+
+ DBG(3, "setup_sections called\n");
+
+ memset(s->sections, '\0', sizeof(s->sections));
+ if (strlen(val) > sizeof(buf) - 1)
+ {
+ DBG(1, "setup_sections: option string too long\n");
+ status = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ strcpy(buf, val);
+
+ section = strtok(buf, ",");
+ while (section != NULL && sectnum < NUM_SECTIONS)
+ {
+ if (!allblank(section))
+ {
+ SANE_Int res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
+ SANE_Int format =
+ get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
+
+ status = section_parse(section, &s->sections[sectnum],
+ res, format);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1,
+ "setup_sections: error parsing section `%s'\n",
+ section);
+ break;
+ }
+
+ sectnum++;
+ }
+ section += strlen(section) + 1;
+ if (section > buf + strlen(val)) break;
+
+ section = strtok(section, ",");
+ }
+ }
+ s->num_sections = sectnum;
+
+ return status;
+}
+
+static SANE_Status
+start_setup (BH_Scanner *s)
+{
+ SANE_Status status;
+ SANE_Bool duplex;
+ SANE_Int i, imagecnt;
+ SANE_Byte batchmode;
+
+ DBG(3, "start_setup called\n");
+
+ duplex = _OPT_VAL_WORD(s, OPT_DUPLEX);
+
+ /* get the _SECTION option, parse it and fill in the sections */
+ status = setup_sections(s, _OPT_VAL_STRING(s, OPT_SECTION));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: setup_sections failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ /* see whether we'll be decoding barcodes and
+ * set the barcodes flag appropriately
+ */
+ if (s->search_bars[0] == 0)
+ {
+ s->barcodes = SANE_FALSE;
+ }
+ else
+ {
+ s->barcodes = SANE_TRUE;
+ }
+
+ /* see whether we'll be handling icons (thumbnails)
+ * set the icons flag appropriately
+ */
+ if (_OPT_VAL_WORD(s, OPT_ICON_WIDTH) >= 8 &&
+ _OPT_VAL_WORD(s, OPT_ICON_LENGTH) >= 8)
+ {
+ s->icons = SANE_TRUE;
+ }
+ else
+ {
+ s->icons = SANE_FALSE;
+ }
+
+
+ /* calculate a new readlist for this 'batch' */
+ s->readptr = s->readcnt = 0;
+
+ /* always read the front image */
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT;
+
+ /* read back page only if duplex is true */
+ if (duplex == SANE_TRUE)
+ {
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK;
+ }
+
+ /* add image section reads to the readlist */
+ for (i = 0; i < s->num_sections; i++)
+ {
+ SANE_Word flags = s->sections[i].flags;
+
+ if (flags & BH_SECTION_FRONT_IMAGE)
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT + i + 1;
+ if (flags & BH_SECTION_BACK_IMAGE)
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK + i + 1;
+ }
+
+
+ /* icons (thumbnails) */
+ if (s->icons)
+ {
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_ICON;
+ /* read back icon only if duplex is true */
+ if (duplex == SANE_TRUE)
+ {
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_ICON;
+ }
+ }
+
+ /* NOTE: It is important that all of the image data comes before
+ * the barcode/patchcode data.
+ */
+ /* barcodes */
+ imagecnt = s->readcnt;
+ if (s->barcodes)
+ {
+ if (s->num_sections == 0)
+ {
+ /* we only decode the entire page(s) if there are no
+ * sections defined
+ */
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_BARCODE;
+ /* read back barcode only if duplex is true */
+ if (duplex == SANE_TRUE)
+ {
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_BARCODE;
+ }
+ }
+ else
+ {
+ /* add barcode section reads to the readlist */
+ for (i = 0; i < s->num_sections; i++)
+ {
+ SANE_Word flags = s->sections[i].flags;
+
+ if (flags & BH_SECTION_FRONT_BAR)
+ s->readlist[s->readcnt++] =
+ BH_SCSI_READ_TYPE_FRONT_BARCODE + i + 1;
+ if (flags & BH_SECTION_BACK_BAR)
+ s->readlist[s->readcnt++] =
+ BH_SCSI_READ_TYPE_BACK_BARCODE + i + 1;
+ }
+ }
+ }
+
+ /* patchcodes */
+ if (s->patchcodes)
+ {
+ if (s->num_sections == 0)
+ {
+ /* we only decode the entire page(s) if there are no
+ * sections defined
+ */
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_PATCHCODE;
+ /* read back patchcode only if duplex is true */
+ if (duplex == SANE_TRUE)
+ {
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_PATCHCODE;
+ }
+ }
+ else
+ {
+ /* add patchcode section reads to the readlist */
+ for (i = 0; i < s->num_sections; i++)
+ {
+ SANE_Word flags = s->sections[i].flags;
+
+ if (flags & BH_SECTION_FRONT_PATCH)
+ s->readlist[s->readcnt++] =
+ BH_SCSI_READ_TYPE_FRONT_PATCHCODE + i + 1;
+ if (flags & BH_SECTION_BACK_PATCH)
+ s->readlist[s->readcnt++] =
+ BH_SCSI_READ_TYPE_BACK_PATCHCODE + i + 1;
+ }
+ }
+ }
+
+ /* add the special item to the read list which transfers the barcode
+ * file that's built as a result of processing barcode and patchcode
+ * readitems. NOTE: this one must be last!
+ */
+ if (s->readcnt > imagecnt)
+ {
+ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_SENDBARFILE;
+ }
+
+ if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE)
+ {
+ /* if batchmode is enabled, then call set_window to
+ * abort the batch (even though there might not (and probably
+ * isn't) a batch in progress). This avoids a batch start error
+ * in the case where a previous batch was not aborted.
+ */
+ DBG(5, "start_setup: calling set_window to abort batch\n");
+ set_window(s, BH_BATCH_ABORT);
+
+ batchmode = BH_BATCH_ENABLE;
+ }
+ else
+ {
+ batchmode = BH_BATCH_DISABLE;
+ }
+
+ DBG(5, "start_setup: duplex=%s, barcodes=%s, patchcodes=%s, "
+ "icons=%s, batch=%s\n",
+ (duplex == SANE_TRUE) ? "yes" : "no",
+ (s->barcodes == SANE_TRUE) ? "yes" : "no",
+ (s->patchcodes == SANE_TRUE) ? "yes" : "no",
+ (s->icons == SANE_TRUE) ? "yes" : "no",
+ (batchmode == BH_BATCH_ENABLE) ? "yes" : "no");
+ DBG(5, "start_setup: sections=%d\n", s->num_sections);
+ for (i = 0; i < s->num_sections; i++)
+ {
+ DBG(5, "start_setup: "
+ "[%d] %lux%lu+%lu+%lu flags=%02x compression=[%d,%d]\n",
+ i+1,
+ s->sections[i].width, s->sections[i].length,
+ s->sections[i].left, s->sections[i].top,
+ s->sections[i].flags,
+ s->sections[i].compressiontype, s->sections[i].compressionarg);
+ }
+ DBG(5, "start_setup: read list length=%d\n", s->readcnt);
+ for (i = 0; i < s->readcnt; i++)
+ {
+ DBG(5, "start_setup: [%d] %s\n", i+1, print_read_type(s->readlist[i]));
+ }
+
+ DBG(5, "start_setup: sending SET WINDOW\n");
+ status = set_window(s, batchmode);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: SET WINDOW failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ DBG(5, "start_setup: sending mode_select_timeout\n");
+ status = mode_select_timeout(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: mode_select_timeout failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ if (s->icons == SANE_TRUE)
+ {
+ DBG(5, "start_setup: sending mode_select_icon\n");
+ status = mode_select_icon(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: mode_select_icon failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+ }
+
+ if (s->barcodes == SANE_TRUE)
+ {
+ DBG(5, "start_setup: sending mode_select_barcode_priority\n");
+ status = mode_select_barcode_priority(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: mode_select_barcode_priority failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ DBG(5, "start_setup: sending mode_select_barcode_param1\n");
+ status = mode_select_barcode_param1(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: mode_select_barcode_param1 failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ DBG(5, "start_setup: sending mode_select_barcode_param2\n");
+ status = mode_select_barcode_param2(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: mode_select_barcode_param2 failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ DBG(5, "start_setup: sending mode_select_barcode_param3\n");
+ status = mode_select_barcode_param3(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "start_setup: mode_select_barcode_param3 failed: %s\n",
+ sane_strstatus(status));
+ return status;
+ }
+ }
+
+ return status;
+}
+
+static SANE_Status
+start_scan (BH_Scanner *s)
+{
+ static SANE_Byte cmd[8];
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool check_adf, duplex;
+ DBG (3, "start_scan called\n");
+
+ /* SANE front ends will call this function between 'FRAMES'.
+ * A single scan on the B&H may result in up to 56 different
+ * things to read (20 are SANE image frames, 36 are non-SANE
+ * data - decoded bar/patch codes).
+ */
+
+ if (s->readcnt > 1 && s->scanning == SANE_TRUE)
+ {
+ DBG(3, "start_scan: any more items in the readlist?\n");
+ /* we've been reading data from this scan, so we just
+ * move on to the next item in the readlist without
+ * starting a new scan.
+ */
+ s->readptr++;
+ if (s->readptr < s->readcnt)
+ {
+ SANE_Byte itemtype;
+
+ for (; s->readptr < s->readcnt; s->readptr++)
+ {
+
+ itemtype = s->readlist[s->readptr];
+
+ DBG(3, "start_scan: advance readlist(%d, %d)\n",
+ s->readptr,
+ (int) itemtype);
+
+ /* 'dance' by the non-SANE data streams
+ * like bar/patch code data
+ */
+ if (!BH_HAS_IMAGE_DATA(itemtype))
+ {
+ int fd;
+ FILE *fp;
+
+ strncpy(s->barfname, "/tmp/bhXXXXXX", sizeof(s->barfname));
+ s->barfname[sizeof(s->barfname)-1] = '\0';
+
+ if ((mktemp(s->barfname) == NULL) &&
+ ((fd = open(s->barfname, O_CREAT | O_EXCL | O_WRONLY, 0600)) != -1) &&
+ ((fp = fdopen(fd, "w")) != NULL))
+ {
+ fprintf(fp, "<xml-stream>\n");
+
+ for (;
+ s->readptr < s->readcnt &&
+ status == SANE_STATUS_GOOD;
+ s->readptr++)
+ {
+ if (s->readlist[s->readptr] ==
+ BH_SCSI_READ_TYPE_SENDBARFILE) {
+ break;
+ }
+ status = read_barcode_data(s, fp);
+ if (status != SANE_STATUS_GOOD) break;
+ }
+
+ fprintf(fp, "</xml-stream>\n");
+
+ /* close file; re-open for read(setting s->barfd) */
+ fclose(fp);
+ if ((s->barf = fopen(s->barfname, "r")) == NULL)
+ {
+ DBG(1, "sane_start: error opening barfile `%s'\n",
+ s->barfname);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ {
+ DBG(1, "sane_start: error opening barfile `%s'\n",
+ s->barfname);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ }
+ else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON ||
+ itemtype == BH_SCSI_READ_TYPE_BACK_ICON)
+ {
+ /* read the icon header setting the iconwidth and iconlength
+ * to the actual values so get_parameters will have them.
+ * Subsequent calls to sane_read will get pure image data
+ * since the icon header has been consumed.
+ */
+
+ status = read_icon_data(s);
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ /* update our parameters to reflect the new item */
+ status = get_parameters (s, 0);
+ }
+
+ if (status != SANE_STATUS_GOOD) s->scanning = SANE_FALSE;
+
+ return status;
+ }
+ /* if we reach here, we're finished with the readlist and
+ * will drop through to start a new scan
+ */
+ }
+ }
+
+ s->readptr = 0;
+
+ check_adf = _OPT_VAL_WORD(s, OPT_CHECK_ADF);
+ duplex = _OPT_VAL_WORD(s, OPT_DUPLEX);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd[0] = BH_SCSI_START_SCAN;
+ cmd[4] = (duplex == SANE_TRUE) ? 2 : 1;
+
+ cmd[6] = 0;
+ cmd[7] = 1;
+
+ if (check_adf)
+ {
+ status = object_position(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(3, "object_position: returned %d\n", status);
+ return status;
+ }
+ }
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
+ if (status == SANE_STATUS_GOOD)
+ {
+ s->scanning = SANE_TRUE;
+
+ /* update our parameters,
+ * now that we're scanning we'll do a GET_WINDOW
+ */
+ status = get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ s->scanning = SANE_FALSE;
+ }
+ }
+
+ return status;
+}
+
+/* a sensible sense handler, courtesy of Franck;
+ arg is a pointer to the associated BH_Scanner structure */
+static SANE_Status
+sense_handler (int scsi_fd, u_char *result, void *arg)
+{
+ BH_Scanner *s = (BH_Scanner *) arg;
+ u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData;
+ u_long InvalidBytes;
+ char *sense_str = "", *as_str = "";
+ SANE_Int i;
+ SANE_Status status = SANE_STATUS_INVAL;
+ SANE_Char print_sense[(16 * 3) + 1];
+
+ scsi_fd = scsi_fd; /* get rid of compiler warning */
+ ErrorCode = result[0] & 0x7F;
+ ValidData = (result[0] & 0x80) != 0;
+ sense = result[2] & 0x0f; /* Key */
+ asc = result[12]; /* Code */
+ ascq = result[13]; /* Qual */
+ EOM = (result[2] & 0x40) != 0; /* End Of Media */
+ ILI = (result[2] & 0x20) != 0; /* Invalid Length Indicator */
+ InvalidBytes = ValidData ? _4btol(&result[3]) : 0;
+
+ DBG(3, "sense_handler: result=%x, sense=%x, asc=%x, ascq=%x\n",
+ result[0], sense, asc, ascq);
+ DBG(3, "sense_handler: ErrorCode %02x ValidData: %d "
+ "EOM: %d ILI: %d InvalidBytes: %lu\n",
+ ErrorCode, ValidData, EOM, ILI, InvalidBytes);
+
+ memset(print_sense, '\0', sizeof(print_sense));
+ for (i = 0; i < 16; i++)
+ {
+ sprintf(print_sense + strlen(print_sense), "%02x ", result[i]);
+ }
+ DBG(5, "sense_handler: sense=%s\n", print_sense);
+
+ if (ErrorCode != 0x70 && ErrorCode != 0x71)
+ {
+ DBG (3, "sense_handler: error code is invalid.\n");
+ return SANE_STATUS_IO_ERROR; /* error code is invalid */
+ }
+
+ /* handle each sense key;
+ * RSC supports 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B
+ */
+ switch (sense)
+ {
+ case 0x00:
+ /* no sense */
+ sense_str = "No sense.";
+ status = SANE_STATUS_GOOD;
+ if (ILI && asc == 0x00 && ascq == 0x05)
+ {
+ /* from read_data function */
+ as_str = "ILI bit is set.";
+ if (s != NULL)
+ {
+ s->InvalidBytes = InvalidBytes;
+ }
+ status = SANE_STATUS_GOOD;
+ }
+ else if (EOM && asc == 0x00 && ascq == 0x02)
+ {
+ /* from adfStatus or startScan function */
+ as_str = "Out of paper in the hopper.";
+ status = SANE_STATUS_NO_DOCS;
+ }
+ else if (EOM)
+ {
+ /* from adfStatus or startScan function */
+ as_str = "Out of paper in the hopper.";
+ status = SANE_STATUS_NO_DOCS;
+ }
+ break;
+ case 0x01:
+ /* recovered error */
+ sense_str = "Recovered error.";
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x02:
+ /* not ready */
+ sense_str = "Not ready.";
+ status = SANE_STATUS_DEVICE_BUSY;
+ if (asc == 0x40 && ascq == 0x01)
+ {
+ as_str = "P.O.D. error: Scanner not found.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x40 && ascq == 0x02)
+ {
+ as_str = "P.O.D. error: Scanner not ready(paper in transport).";
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (asc == 0x40 && ascq == 0x03)
+ {
+ as_str = "P.O.D. error: Unknown scanner.";
+ status = SANE_STATUS_INVAL;
+ }
+ break;
+ case 0x03:
+ /* medium error */
+ sense_str = "Medium error.";
+ status = SANE_STATUS_IO_ERROR;
+ if (asc == 0x00 && ascq == 0x00)
+ {
+ as_str = "Scanner error: paper jam detected.";
+ status = SANE_STATUS_JAMMED;
+ }
+ break;
+ case 0x04:
+ /* hardware error */
+ sense_str = "Hardware error.";
+ status = SANE_STATUS_IO_ERROR;
+ if (asc == 0x60 && ascq == 0x00)
+ {
+ as_str = "Scanner error: illumination lamps failure.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x03)
+ {
+ as_str = "Communication error between RSC and scanner.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x06)
+ {
+ as_str = "Scanner error: page detected but lamps are off.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x07)
+ {
+ as_str = "Scanner error: camera white level problem.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x08)
+ {
+ /* could be caught from start_scan or read_data */
+ /* stop button pressed */
+ as_str = "Scanner error: operator pressed the Stop key.";
+ status = SANE_STATUS_NO_DOCS;
+ }
+ else if (asc == 0x80 && ascq == 0x12)
+ {
+ as_str = "Scanner error: transport motor failure.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x15)
+ {
+ as_str = "Scanner error: device / page sensor(s) bouncing.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x16)
+ {
+ as_str = "Scanner error: feeder is not attached.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x18)
+ {
+ as_str = "Scanner error: logic system general failure.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x34)
+ {
+ as_str = "Scanner error: no dual logic communication.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ break;
+ case 0x05:
+ /* illegal request */
+ sense_str = "Illegal request.";
+ status = SANE_STATUS_INVAL;
+ if (asc == 0x1a && ascq == 0x00)
+ {
+ as_str = "Parameter list length error.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x20 && ascq == 0x00)
+ {
+ as_str = "Invalid command operation code.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x24 && ascq == 0x00)
+ {
+ /* caught from object_position (via reverse engineering) */
+ /* Not supported? */
+ as_str = "Invalid field in CDB.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x25 && ascq == 0x00)
+ {
+ as_str = "Unsupported LUN.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x26 && ascq == 0x00)
+ {
+ /* caught from mode_select (as well as others) */
+ /* Bar/Patch code detection support not installed */
+ /* See Appendix A, Section A.5 */
+ as_str = "Invalid field in parameter list.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x2c && ascq == 0x00)
+ {
+ /* we were getting this in read_data during the time
+ that the ADF was misbehaving. Hopefully we will
+ not see it anymore.
+ */
+ as_str = "Command out of sequence.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x2c && ascq == 0x01)
+ {
+ as_str = "Too many windows defined.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x2c && ascq == 0x02)
+ {
+ as_str = "Batch start error.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x2c && ascq == 0x03)
+ {
+ as_str = "Batch abort error.";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x3d && ascq == 0x00)
+ {
+ as_str = "Invalid bits in IDENTIFY message.";
+ status = SANE_STATUS_INVAL;
+ }
+ break;
+ case 0x06:
+ /* unit attention */
+ sense_str = "Unit attention.";
+ status = SANE_STATUS_IO_ERROR;
+ if (asc == 0x04 && ascq == 0x01)
+ {
+ as_str = "Reset detected, LUN is becoming ready.";
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ break;
+ case 0x07:
+ /* data protect */
+ sense_str = "Data protect.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x08:
+ /* blank check */
+ sense_str = "Blank check.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x09:
+ /* vendor specific */
+ sense_str = "Vendor specific.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0A:
+ /* copy aborted */
+ sense_str = "Copy aborted.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0B:
+ /* aborted command */
+ sense_str = "Aborted command.";
+ status = SANE_STATUS_IO_ERROR;
+ if (asc == 0x00 && ascq == 0x00)
+ {
+ as_str = "Aborted command (unspecified error).";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x08 && ascq == 0x01)
+ {
+ /* caught from start_scan */
+ /* manual feed timeout */
+ as_str = "SCSI Time-out, paper Time-out (SCAN command).";
+ status = SANE_STATUS_NO_DOCS;
+ }
+ else if (asc == 0x47 && ascq == 0x00)
+ {
+ as_str = "SCSI parity error.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x00)
+ {
+ as_str = "Aborted command due to memory error.";
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x80 && ascq == 0x01)
+ {
+ /* caught from read_data */
+ /* section border error; border is outside the main window */
+ /* See Appendix A, Section A.4 */
+ as_str = "Section Read error (out of border).";
+ status = SANE_STATUS_INVAL;
+ }
+ else if (asc == 0x80 && ascq == 0x02)
+ {
+ /* caught from read_data */
+ /* No code found; no barcode data is found */
+ /* See Appendix A, Section A.5 */
+ s->barcode_not_found = SANE_TRUE;
+ as_str = "No Bar/Patch Code found.";
+ status = SANE_STATUS_GOOD;
+ }
+ else if (asc == 0x80 && ascq == 0x03)
+ {
+ as_str = "Icon Read error (out of border).";
+ status = SANE_STATUS_INVAL;
+ }
+ break;
+ case 0x0C:
+ /* equal */
+ sense_str = "Equal.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0D:
+ /* volume overflow */
+ sense_str = "Volume overflow.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0E:
+ /* miscompare */
+ sense_str = "Miscompare.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0F:
+ /* reserved */
+ sense_str = "Reserved.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default:
+ sense_str = "Unhandled case.";
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+
+ DBG(3, "sense_handler: '%s' '%s' return:%d\n",
+ sense_str, as_str, status);
+
+ return status;
+}
+
+static SANE_Status
+init_options (BH_Scanner * s)
+{
+ int i;
+ DBG (3, "init_options called\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+ s->opt[OPT_MODE_GROUP].name = "";
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP;
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Preview: */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* Inquiry */
+ s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY;
+ s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY;
+ s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY;
+ s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING;
+ s->opt[OPT_INQUIRY].size = sizeof(inquiry_data);
+ s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_INQUIRY].s = strdup(inquiry_data);
+ s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT;
+
+ /* scan mode */
+ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_list);
+ s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_list;
+ s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]);
+
+ /* Standard resolutions */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList;
+ s->val[OPT_RESOLUTION].w = s->hw->info.res_default;
+
+ /* compression */
+ s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION;
+ s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION;
+ s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION;
+ s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING;
+ s->opt[OPT_COMPRESSION].size = max_string_size (compression_list);
+ s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_COMPRESSION].constraint.string_list = compression_list;
+ s->val[OPT_COMPRESSION].s = strdup (compression_list[0]);
+
+ if (s->hw->info.colorHalftone == SANE_FALSE)
+ {
+ s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_min_list);
+ s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_min_list;
+ }
+
+ if (s->hw->info.comprG3_1D == SANE_FALSE ||
+ s->hw->info.comprG3_2D == SANE_FALSE ||
+ s->hw->info.comprG4 == SANE_FALSE)
+ {
+ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = "";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Autoborder: */
+ s->opt[OPT_AUTOBORDER].name = SANE_NAME_AUTOBORDER;
+ s->opt[OPT_AUTOBORDER].title = SANE_TITLE_AUTOBORDER;
+ s->opt[OPT_AUTOBORDER].desc = SANE_DESC_AUTOBORDER;
+ s->opt[OPT_AUTOBORDER].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTOBORDER].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTOBORDER].w = s->hw->info.autoborder_default;
+
+ /* Paper Size */
+ s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
+ s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list);
+ s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER_SIZE].constraint.string_list = paper_list;
+ s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]);
+
+ /* rotation */
+ s->opt[OPT_ROTATION].name = SANE_NAME_ROTATION;
+ s->opt[OPT_ROTATION].title = SANE_TITLE_ROTATION;
+ s->opt[OPT_ROTATION].desc = SANE_DESC_ROTATION;
+ s->opt[OPT_ROTATION].type = SANE_TYPE_STRING;
+ s->opt[OPT_ROTATION].size = max_string_size (rotation_list);
+ s->opt[OPT_ROTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ROTATION].constraint.string_list = rotation_list;
+ s->val[OPT_ROTATION].s = strdup (rotation_list[0]);
+
+ /* Deskew: */
+ s->opt[OPT_DESKEW].name = SANE_NAME_DESKEW;
+ s->opt[OPT_DESKEW].title = SANE_TITLE_DESKEW;
+ s->opt[OPT_DESKEW].desc = SANE_DESC_DESKEW;
+ s->opt[OPT_DESKEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DESKEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DESKEW].w = s->hw->info.deskew_default;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &(s->hw->info.x_range);
+ s->val[OPT_TL_X].w = SANE_FIX(0.0);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &(s->hw->info.y_range);
+ s->val[OPT_TL_Y].w = SANE_FIX(0.0);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &(s->hw->info.x_range);
+ s->val[OPT_BR_X].w = s->hw->info.x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &(s->hw->info.y_range);
+ s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
+
+ if (s->hw->info.canBorderRecog == SANE_FALSE)
+ {
+ s->opt[OPT_AUTOBORDER].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Feeder" group: */
+ s->opt[OPT_FEEDER_GROUP].name = "";
+ s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP;
+ s->opt[OPT_FEEDER_GROUP].desc = "";
+ s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan source */
+ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list);
+ s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_SOURCE].constraint.string_list = scan_source_list;
+ s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]);
+
+ /* Batch: */
+ s->opt[OPT_BATCH].name = SANE_NAME_BATCH;
+ s->opt[OPT_BATCH].title = SANE_TITLE_BATCH;
+ s->opt[OPT_BATCH].desc = SANE_DESC_BATCH;
+ s->opt[OPT_BATCH].type = SANE_TYPE_BOOL;
+ s->opt[OPT_BATCH].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_BATCH].w = s->hw->info.batch_default;
+
+ /* Check ADF: */
+ s->opt[OPT_CHECK_ADF].name = SANE_NAME_CHECK_ADF;
+ s->opt[OPT_CHECK_ADF].title = SANE_TITLE_CHECK_ADF;
+ s->opt[OPT_CHECK_ADF].desc = SANE_DESC_CHECK_ADF;
+ s->opt[OPT_CHECK_ADF].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CHECK_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CHECK_ADF].w = s->hw->info.check_adf_default;
+
+ /* Duplex: */
+ s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
+ s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
+ s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
+ s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DUPLEX].w = s->hw->info.duplex_default;
+
+ /* timeout adf */
+ s->opt[OPT_TIMEOUT_ADF].name = SANE_NAME_TIMEOUT_ADF;
+ s->opt[OPT_TIMEOUT_ADF].title = SANE_TITLE_TIMEOUT_ADF;
+ s->opt[OPT_TIMEOUT_ADF].desc = SANE_DESC_TIMEOUT_ADF;
+ s->opt[OPT_TIMEOUT_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_TIMEOUT_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_TIMEOUT_ADF].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TIMEOUT_ADF].constraint.range = &u8_range;
+ s->val[OPT_TIMEOUT_ADF].w = s->hw->info.timeout_adf_default;
+
+ /* timeout manual */
+ s->opt[OPT_TIMEOUT_MANUAL].name = SANE_NAME_TIMEOUT_MANUAL;
+ s->opt[OPT_TIMEOUT_MANUAL].title = SANE_TITLE_TIMEOUT_MANUAL;
+ s->opt[OPT_TIMEOUT_MANUAL].desc = SANE_DESC_TIMEOUT_MANUAL;
+ s->opt[OPT_TIMEOUT_MANUAL].type = SANE_TYPE_INT;
+ s->opt[OPT_TIMEOUT_MANUAL].unit = SANE_UNIT_NONE;
+ s->opt[OPT_TIMEOUT_MANUAL].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TIMEOUT_MANUAL].constraint.range = &u8_range;
+ s->val[OPT_TIMEOUT_MANUAL].w = s->hw->info.timeout_manual_default;
+
+ if (s->hw->info.canCheckADF == SANE_FALSE)
+ {
+ s->opt[OPT_CHECK_ADF].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (s->hw->info.canDuplex == SANE_FALSE)
+ {
+ s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (s->hw->info.canADF == SANE_FALSE)
+ {
+ s->opt[OPT_TIMEOUT_ADF].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].name = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Control Panel: */
+ s->opt[OPT_CONTROL_PANEL].name = SANE_NAME_CONTROL_PANEL;
+ s->opt[OPT_CONTROL_PANEL].title = SANE_TITLE_CONTROL_PANEL;
+ s->opt[OPT_CONTROL_PANEL].desc = SANE_DESC_CONTROL_PANEL;
+ s->opt[OPT_CONTROL_PANEL].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CONTROL_PANEL].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CONTROL_PANEL].w = s->hw->info.control_panel_default;
+
+ /* Ace_Function */
+ s->opt[OPT_ACE_FUNCTION].name = SANE_NAME_ACE_FUNCTION;
+ s->opt[OPT_ACE_FUNCTION].title = SANE_TITLE_ACE_FUNCTION;
+ s->opt[OPT_ACE_FUNCTION].desc = SANE_DESC_ACE_FUNCTION;
+ s->opt[OPT_ACE_FUNCTION].type = SANE_TYPE_INT;
+ s->opt[OPT_ACE_FUNCTION].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ACE_FUNCTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ACE_FUNCTION].constraint.range = &ace_function_range;
+ s->val[OPT_ACE_FUNCTION].w = 0;
+
+ /* Ace_Sensitivity */
+ s->opt[OPT_ACE_SENSITIVITY].name = SANE_NAME_ACE_SENSITIVITY;
+ s->opt[OPT_ACE_SENSITIVITY].title = SANE_TITLE_ACE_SENSITIVITY;
+ s->opt[OPT_ACE_SENSITIVITY].desc = SANE_DESC_ACE_SENSITIVITY;
+ s->opt[OPT_ACE_SENSITIVITY].type = SANE_TYPE_INT;
+ s->opt[OPT_ACE_SENSITIVITY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ACE_SENSITIVITY].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ACE_SENSITIVITY].constraint.range = &ace_sensitivity_range;
+ s->val[OPT_ACE_SENSITIVITY].w = 4;
+
+ /* Brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* Threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->val[OPT_THRESHOLD].w = 0;
+
+ /* Contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &u8_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* Negative: */
+ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ /* Contrast is not used in any case; why did we add it? */
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ if (s->hw->info.control_panel_default == SANE_TRUE)
+ {
+ s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (s->hw->info.canACE == SANE_FALSE)
+ {
+ s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "ICON" group: */
+ s->opt[OPT_ICON_GROUP].name = "";
+ s->opt[OPT_ICON_GROUP].title = SANE_TITLE_ICON_GROUP;
+ s->opt[OPT_ICON_GROUP].desc = "";
+ s->opt[OPT_ICON_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ICON_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_ICON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Icon_Width */
+ s->opt[OPT_ICON_WIDTH].name = SANE_NAME_ICON_WIDTH;
+ s->opt[OPT_ICON_WIDTH].title = SANE_TITLE_ICON_WIDTH;
+ s->opt[OPT_ICON_WIDTH].desc = SANE_DESC_ICON_WIDTH;
+ s->opt[OPT_ICON_WIDTH].type = SANE_TYPE_INT;
+ s->opt[OPT_ICON_WIDTH].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_ICON_WIDTH].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ICON_WIDTH].constraint.range = &icon_range;
+ s->val[OPT_ICON_WIDTH].w = 0;
+
+ /* Icon_Length */
+ s->opt[OPT_ICON_LENGTH].name = SANE_NAME_ICON_LENGTH;
+ s->opt[OPT_ICON_LENGTH].title = SANE_TITLE_ICON_LENGTH;
+ s->opt[OPT_ICON_LENGTH].desc = SANE_DESC_ICON_LENGTH;
+ s->opt[OPT_ICON_LENGTH].type = SANE_TYPE_INT;
+ s->opt[OPT_ICON_LENGTH].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_ICON_LENGTH].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ICON_LENGTH].constraint.range = &icon_range;
+ s->val[OPT_ICON_LENGTH].w = 0;
+
+ if (s->hw->info.canIcon == SANE_FALSE)
+ {
+ s->opt[OPT_ICON_GROUP].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ICON_WIDTH].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ICON_LENGTH].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Barcode" group: */
+ s->opt[OPT_BARCODE_GROUP].name = "";
+ s->opt[OPT_BARCODE_GROUP].title = SANE_TITLE_BARCODE_GROUP;
+ s->opt[OPT_BARCODE_GROUP].desc = "";
+ s->opt[OPT_BARCODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_BARCODE_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_BARCODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Add <name> to barcode search priority. */
+ s->opt[OPT_BARCODE_SEARCH_BAR].name = SANE_NAME_BARCODE_SEARCH_BAR;
+ s->opt[OPT_BARCODE_SEARCH_BAR].title = SANE_TITLE_BARCODE_SEARCH_BAR;
+ s->opt[OPT_BARCODE_SEARCH_BAR].desc = SANE_DESC_BARCODE_SEARCH_BAR;
+ s->opt[OPT_BARCODE_SEARCH_BAR].type = SANE_TYPE_STRING;
+ s->opt[OPT_BARCODE_SEARCH_BAR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_SEARCH_BAR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BARCODE_SEARCH_BAR].constraint.string_list = barcode_search_bar_list;
+ s->opt[OPT_BARCODE_SEARCH_BAR].size = max_string_size (barcode_search_bar_list);
+ s->val[OPT_BARCODE_SEARCH_BAR].s = strdup (barcode_search_bar_list[0]);
+
+ /* Barcode search count (1-7, default 1). */
+ s->opt[OPT_BARCODE_SEARCH_COUNT].name = SANE_NAME_BARCODE_SEARCH_COUNT;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].title = SANE_TITLE_BARCODE_SEARCH_COUNT;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].desc = SANE_DESC_BARCODE_SEARCH_COUNT;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].constraint.range = &barcode_search_count_range;
+ s->val[OPT_BARCODE_SEARCH_COUNT].w = 3;
+
+ /* Barcode search mode. horiz-vert, horizontal, vertical, vert-horiz */
+ s->opt[OPT_BARCODE_SEARCH_MODE].name = SANE_NAME_BARCODE_SEARCH_MODE;
+ s->opt[OPT_BARCODE_SEARCH_MODE].title = SANE_TITLE_BARCODE_SEARCH_MODE;
+ s->opt[OPT_BARCODE_SEARCH_MODE].desc = SANE_DESC_BARCODE_SEARCH_MODE;
+ s->opt[OPT_BARCODE_SEARCH_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_BARCODE_SEARCH_MODE].size = max_string_size (barcode_search_mode_list);
+ s->opt[OPT_BARCODE_SEARCH_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BARCODE_SEARCH_MODE].constraint.string_list = barcode_search_mode_list;
+ s->val[OPT_BARCODE_SEARCH_MODE].s = strdup(barcode_search_mode_list[0]);
+
+ /* Patch code min height (def=5mm) */
+ s->opt[OPT_BARCODE_HMIN].name = SANE_NAME_BARCODE_HMIN;
+ s->opt[OPT_BARCODE_HMIN].title = SANE_TITLE_BARCODE_HMIN;
+ s->opt[OPT_BARCODE_HMIN].desc = SANE_DESC_BARCODE_HMIN;
+ s->opt[OPT_BARCODE_HMIN].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_HMIN].unit = SANE_UNIT_MM;
+ s->opt[OPT_BARCODE_HMIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_HMIN].constraint.range = &barcode_hmin_range;
+ s->val[OPT_BARCODE_HMIN].w = 5;
+
+ /* Barcode search timeout in ms (20-65535,default is 10000). */
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].name = SANE_NAME_BARCODE_SEARCH_TIMEOUT;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].title = SANE_TITLE_BARCODE_SEARCH_TIMEOUT;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].desc = SANE_DESC_BARCODE_SEARCH_TIMEOUT;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].unit = SANE_UNIT_MICROSECOND;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint.range = &barcode_search_timeout_range;
+ s->val[OPT_BARCODE_SEARCH_TIMEOUT].w = 10000;
+
+ /* Specify image sections and functions */
+ s->opt[OPT_SECTION].name = SANE_NAME_SECTION;
+ s->opt[OPT_SECTION].title = SANE_TITLE_SECTION;
+ s->opt[OPT_SECTION].desc = SANE_DESC_SECTION;
+ s->opt[OPT_SECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_SECTION].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SECTION].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_SECTION].size = 255;
+ s->val[OPT_SECTION].s = strdup ("");
+
+ /* Barcode_Relmax */
+ s->opt[OPT_BARCODE_RELMAX].name = SANE_NAME_BARCODE_RELMAX;
+ s->opt[OPT_BARCODE_RELMAX].title = SANE_TITLE_BARCODE_RELMAX;
+ s->opt[OPT_BARCODE_RELMAX].desc = SANE_DESC_BARCODE_RELMAX;
+ s->opt[OPT_BARCODE_RELMAX].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_RELMAX].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_RELMAX].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_RELMAX].constraint.range = &u8_range;
+ s->val[OPT_BARCODE_RELMAX].w = 0;
+
+ /* Barcode_Barmin */
+ s->opt[OPT_BARCODE_BARMIN].name = SANE_NAME_BARCODE_BARMIN;
+ s->opt[OPT_BARCODE_BARMIN].title = SANE_TITLE_BARCODE_BARMIN;
+ s->opt[OPT_BARCODE_BARMIN].desc = SANE_DESC_BARCODE_BARMIN;
+ s->opt[OPT_BARCODE_BARMIN].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_BARMIN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_BARMIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_BARMIN].constraint.range = &u8_range;
+ s->val[OPT_BARCODE_BARMIN].w = 0;
+
+ /* Barcode_Barmax */
+ s->opt[OPT_BARCODE_BARMAX].name = SANE_NAME_BARCODE_BARMAX;
+ s->opt[OPT_BARCODE_BARMAX].title = SANE_TITLE_BARCODE_BARMAX;
+ s->opt[OPT_BARCODE_BARMAX].desc = SANE_DESC_BARCODE_BARMAX;
+ s->opt[OPT_BARCODE_BARMAX].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_BARMAX].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_BARMAX].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_BARMAX].constraint.range = &u8_range;
+ s->val[OPT_BARCODE_BARMAX].w = 0;
+
+ /* Barcode_Contrast */
+ s->opt[OPT_BARCODE_CONTRAST].name = SANE_NAME_BARCODE_CONTRAST;
+ s->opt[OPT_BARCODE_CONTRAST].title = SANE_TITLE_BARCODE_CONTRAST;
+ s->opt[OPT_BARCODE_CONTRAST].desc = SANE_DESC_BARCODE_CONTRAST;
+ s->opt[OPT_BARCODE_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_CONTRAST].constraint.range = &barcode_contrast_range;
+ s->val[OPT_BARCODE_CONTRAST].w = 3;
+
+ /* Barcode_Patchmode */
+ s->opt[OPT_BARCODE_PATCHMODE].name = SANE_NAME_BARCODE_PATCHMODE;
+ s->opt[OPT_BARCODE_PATCHMODE].title = SANE_TITLE_BARCODE_PATCHMODE;
+ s->opt[OPT_BARCODE_PATCHMODE].desc = SANE_DESC_BARCODE_PATCHMODE;
+ s->opt[OPT_BARCODE_PATCHMODE].type = SANE_TYPE_INT;
+ s->opt[OPT_BARCODE_PATCHMODE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BARCODE_PATCHMODE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BARCODE_PATCHMODE].constraint.range = &barcode_patchmode_range;
+ s->val[OPT_BARCODE_PATCHMODE].w = 0;
+
+ if (s->hw->info.canSection == SANE_FALSE)
+ {
+ s->opt[OPT_SECTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (s->hw->info.canBarCode == SANE_FALSE)
+ {
+ s->opt[OPT_BARCODE_GROUP].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_SEARCH_BAR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_SEARCH_COUNT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_SEARCH_MODE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_HMIN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_RELMAX].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_BARMIN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_BARMAX].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BARCODE_PATCHMODE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (const char *devnam, BH_Device ** devp)
+{
+ SANE_Status status;
+ BH_Device *dev;
+ struct inquiry_standard_data ibuf;
+ struct inquiry_vpd_data vbuf;
+ struct inquiry_jis_data jbuf;
+ size_t buf_size;
+ int fd = -1;
+ double mm;
+
+ DBG (3, "attach called\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+#ifdef FAKE_INQUIRY
+ if (fake_inquiry)
+ {
+ DBG (3, "attach: faking inquiry of %s\n", devnam);
+
+ memset (&ibuf, 0, sizeof (ibuf));
+ ibuf.devtype = 6;
+ memcpy(ibuf.vendor, "**FAKE**", 8);
+ memcpy(ibuf.product, "COPISCAN II 6338", 16);
+ memcpy(ibuf.revision, "0016", 4);
+
+ DBG (1, "attach: reported devtype='%d', vendor='%.8s', "
+ "product='%.16s', revision='%.4s'\n",
+ ibuf.devtype, ibuf.vendor,
+ ibuf.product, ibuf.revision);
+
+ memset (&vbuf, 0, sizeof (vbuf));
+ memset (&jbuf, 0, sizeof (jbuf));
+ }
+ else
+#endif
+ {
+ DBG (3, "attach: opening %s\n", devnam);
+ status = sanei_scsi_open (devnam, &fd, sense_handler, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ DBG (3, "attach: sending INQUIRY (standard data)\n");
+ memset (&ibuf, 0, sizeof (ibuf));
+ buf_size = sizeof(ibuf);
+ status = inquiry (fd, &ibuf, &buf_size, 0,
+ BH_INQUIRY_STANDARD_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry (standard data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ DBG (1, "attach: reported devtype='%d', vendor='%.8s', "
+ "product='%.16s', revision='%.4s'\n",
+ ibuf.devtype, ibuf.vendor,
+ ibuf.product, ibuf.revision);
+
+ if (ibuf.devtype != 6
+ || strncmp ((char *)ibuf.vendor, "B&H SCSI", 8) != 0
+ || strncmp ((char *)ibuf.product, "COPISCAN ", 9) != 0)
+ {
+ DBG (1,
+ "attach: device is not a recognized Bell and Howell scanner\n");
+ sanei_scsi_close (fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "attach: sending INQUIRY (vpd data)\n");
+ memset (&vbuf, 0, sizeof (vbuf));
+ buf_size = sizeof(vbuf);
+ status = inquiry (fd, &vbuf, &buf_size, 1,
+ BH_INQUIRY_VPD_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry (vpd data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ DBG (3, "attach: sending INQUIRY (jis data)\n");
+ memset (&jbuf, 0, sizeof (jbuf));
+ buf_size = sizeof(jbuf);
+ status = inquiry (fd, &jbuf, &buf_size, 1,
+ BH_INQUIRY_JIS_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry (jis data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ sanei_scsi_close (fd);
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, 0, sizeof (*dev));
+
+
+ dev->info.devtype = ibuf.devtype;
+ sprintf(dev->info.vendor, "%.8s", ibuf.vendor);
+ trim_spaces(dev->info.vendor, sizeof(dev->info.vendor));
+ sprintf(dev->info.product, "%.16s", ibuf.product);
+ trim_spaces(dev->info.product, sizeof(dev->info.product));
+ sprintf(dev->info.revision, "%.4s", ibuf.revision);
+ trim_spaces(dev->info.revision, sizeof(dev->info.revision));
+
+ dev->sane.name = strdup (devnam);
+ dev->sane.vendor = strdup(dev->info.vendor);
+ dev->sane.model = strdup(dev->info.product);;
+ dev->sane.type = strdup(print_devtype(dev->info.devtype));
+
+ /* set capabilities from vpd */
+ dev->info.canADF = vbuf.adf & 0x01;
+ dev->info.colorBandW = vbuf.imagecomposition & 0x01;
+ dev->info.colorHalftone = vbuf.imagecomposition & 0x02;
+ dev->info.canWhiteFrame = vbuf.imagedataprocessing[1] & 0x01;
+ dev->info.canBlackFrame = vbuf.imagedataprocessing[1] & 0x02;
+ dev->info.canEdgeExtract = vbuf.imagedataprocessing[1] & 0x04;
+ dev->info.canNoiseFilter = vbuf.imagedataprocessing[1] & 0x08;
+ dev->info.canSmooth = vbuf.imagedataprocessing[1] & 0x10;
+ dev->info.canLineBold = vbuf.imagedataprocessing[1] & 0x20;
+ dev->info.comprG3_1D = vbuf.compression & 0x01;
+ dev->info.comprG3_2D = vbuf.compression & 0x02;
+ dev->info.comprG4 = vbuf.compression & 0x04;
+ dev->info.canBorderRecog = vbuf.sizerecognition & 0x01;
+ dev->info.canBarCode = vbuf.optionalfeatures & 0x01;
+ dev->info.canIcon = vbuf.optionalfeatures & 0x02;
+ dev->info.canSection = vbuf.optionalfeatures & 0x04;
+ dev->info.lineMaxBytes = _2btol(vbuf.xmaxoutputbytes);
+
+#ifdef FAKE_INQUIRY
+ if (fake_inquiry)
+ {
+ dev->info.canADF = SANE_FALSE;
+ dev->info.colorBandW = SANE_TRUE;
+ dev->info.colorHalftone = SANE_TRUE;
+ dev->info.canWhiteFrame = SANE_TRUE;
+ dev->info.canBlackFrame = SANE_TRUE;
+ dev->info.canEdgeExtract = SANE_TRUE;
+ dev->info.canNoiseFilter = SANE_TRUE;
+ dev->info.canSmooth = SANE_TRUE;
+ dev->info.canLineBold = SANE_TRUE;
+ dev->info.comprG3_1D = SANE_TRUE;
+ dev->info.comprG3_2D = SANE_TRUE;
+ dev->info.comprG4 = SANE_TRUE;
+ dev->info.canBorderRecog = SANE_TRUE;
+ dev->info.canBarCode = SANE_TRUE;
+ dev->info.canIcon = SANE_TRUE;
+ dev->info.canSection = SANE_TRUE;
+ dev->info.lineMaxBytes = 450;
+ }
+#endif
+
+ /* set capabilities from jis */
+ dev->info.resBasicX = _2btol(jbuf.basicxres);
+ dev->info.resBasicY = _2btol(jbuf.basicyres);
+ dev->info.resMaxX = _2btol(jbuf.maxxres);
+ dev->info.resMaxY = _2btol(jbuf.maxyres);
+ dev->info.resMinX = _2btol(jbuf.minxres);
+ dev->info.resMinY = _2btol(jbuf.minyres);
+
+ /* set the length of the list to zero first, then append standard resolutions */
+ dev->info.resStdList[0] = 0;
+ if (jbuf.standardres[0] & 0x80) appendStdList(&dev->info, 60);
+ if (jbuf.standardres[0] & 0x40) appendStdList(&dev->info, 75);
+ if (jbuf.standardres[0] & 0x20) appendStdList(&dev->info, 100);
+ if (jbuf.standardres[0] & 0x10) appendStdList(&dev->info, 120);
+ if (jbuf.standardres[0] & 0x08) appendStdList(&dev->info, 150);
+ if (jbuf.standardres[0] & 0x04) appendStdList(&dev->info, 160);
+ if (jbuf.standardres[0] & 0x02) appendStdList(&dev->info, 180);
+ if (jbuf.standardres[0] & 0x01) appendStdList(&dev->info, 200);
+ if (jbuf.standardres[1] & 0x80) appendStdList(&dev->info, 240);
+ if (jbuf.standardres[1] & 0x40) appendStdList(&dev->info, 300);
+ if (jbuf.standardres[1] & 0x20) appendStdList(&dev->info, 320);
+ if (jbuf.standardres[1] & 0x10) appendStdList(&dev->info, 400);
+ if (jbuf.standardres[1] & 0x08) appendStdList(&dev->info, 480);
+ if (jbuf.standardres[1] & 0x04) appendStdList(&dev->info, 600);
+ if (jbuf.standardres[1] & 0x02) appendStdList(&dev->info, 800);
+ if (jbuf.standardres[1] & 0x01) appendStdList(&dev->info, 1200);
+ if (dev->info.resStdList[0] == 0)
+ {
+ /* make a default standard resolutions for 200 and 300dpi */
+ DBG(1, "attach: no standard resolutions reported\n");
+ dev->info.resStdList[0] = 2;
+ dev->info.resStdList[1] = 200;
+ dev->info.resStdList[2] = 300;
+ dev->info.resBasicX = dev->info.resBasicY = 300;
+ }
+
+ dev->info.winWidth = _4btol(jbuf.windowwidth);
+ dev->info.winHeight = _4btol(jbuf.windowlength);
+
+ if (dev->info.winWidth <= 0)
+ {
+ dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5);
+ DBG(1, "attach: invalid window width reported, using %d\n", dev->info.winWidth);
+ }
+ if (dev->info.winHeight <= 0)
+ {
+ dev->info.winHeight = dev->info.resBasicY * 14;
+ DBG(1, "attach: invalid window height reported, using %d\n", dev->info.winHeight);
+ }
+
+ mm = (dev->info.resBasicX > 0) ?
+ ((double) dev->info.winWidth / (double) dev->info.resBasicX * MM_PER_INCH) :
+ 0.0;
+ dev->info.x_range.min = SANE_FIX(0.0);
+ dev->info.x_range.max = SANE_FIX(mm);
+ dev->info.x_range.quant = SANE_FIX(0.0);
+
+ mm = (dev->info.resBasicY > 0) ?
+ ((double) dev->info.winHeight / (double) dev->info.resBasicY * MM_PER_INCH) :
+ 0.0;
+ dev->info.y_range.min = SANE_FIX(0.0);
+ dev->info.y_range.max = SANE_FIX(mm);
+ dev->info.y_range.quant = SANE_FIX(0.0);
+
+ /* set additional discovered/guessed capabilities */
+
+ /* if all of the ACE capabilities are present, declare it ACE capable */
+ dev->info.canACE = dev->info.canEdgeExtract &&
+ dev->info.canNoiseFilter &&
+ dev->info.canSmooth &&
+ dev->info.canLineBold;
+
+ /* if the model is known to be a duplex, declare it duplex capable */
+ if (strcmp(dev->info.product, "COPISCAN II 6338") == 0)
+ {
+ dev->info.canDuplex = SANE_TRUE;
+ }
+ else
+ {
+ dev->info.canDuplex = SANE_FALSE;
+ }
+
+ /* the paper sensor requires RSC revision 1.4 or higher and an
+ * installed feeder. NOTE: It also requires SW-4 on and the
+ * AccufeedPlus feeder, but we cannot discover that.
+ */
+ if (strcmp(dev->info.revision, "0014") >= 0)
+ {
+ dev->info.canCheckADF = dev->info.canADF;
+ }
+ else
+ {
+ dev->info.canCheckADF = SANE_FALSE;
+ }
+
+ /* set option defaults based on inquiry information */
+ dev->info.res_default = dev->info.resBasicX;
+ dev->info.autoborder_default = dev->info.canBorderRecog;
+ dev->info.batch_default = SANE_FALSE;
+ dev->info.deskew_default = SANE_FALSE;
+ dev->info.check_adf_default = SANE_FALSE;
+ dev->info.duplex_default = SANE_FALSE;
+ dev->info.timeout_adf_default = 0;
+ dev->info.timeout_manual_default = 0;
+ dev->info.control_panel_default = dev->info.canACE;
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one(const char *devnam)
+{
+ attach (devnam, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ FILE *fp;
+
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT();
+ DBG(3, "sane_init called\n");
+ DBG(1, "Bell+Howell SANE backend %d.%d build %d %s endian\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD,
+ _is_host_little_endian() ? "little" : "big");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ fp = sanei_config_open(BH_CONFIG_FILE);
+ if (fp)
+ {
+ char line[PATH_MAX];
+ const char *lp;
+ size_t len;
+
+ /* read config file */
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ lp = sanei_config_skip_whitespace (line);
+
+ DBG(16,
+ "sane_init: processing config file line '%s'\n",
+ line);
+ if (strncmp(lp, "option", 6) == 0 &&
+ (isspace (lp[6]) || lp[6] == '\0'))
+ {
+ lp += 6;
+ lp = sanei_config_skip_whitespace (lp);
+
+ if (strncmp(lp, "disable-optional-frames", 23) == 0)
+ {
+ DBG(1, "sane_init: configuration option "
+ "'disable-optional-frames' set\n");
+ disable_optional_frames = 1;
+ }
+ else if (strncmp(lp, "fake-inquiry", 12) == 0)
+ {
+ DBG(1, "sane_init: configuration option "
+ "'fake-inquiry' set\n");
+ fake_inquiry = 1;
+ }
+ else
+ {
+ DBG(1, "sane_init: ignoring unknown "
+ "configuration option '%s'\n",
+ lp);
+ }
+ }
+ else
+ {
+ DBG(16,
+ "sane_init: found a device: line '%s'\n",
+ lp);
+ strncpy (devnam, lp, sizeof(devnam));
+ devnam[sizeof(devnam)-1] = '\0';
+
+ sanei_config_attach_matching_devices(devnam,
+ attach_one);
+ }
+ }
+ fclose (fp);
+ }
+ else
+ {
+ /* configure the /dev/scanner device in the absence of config file */
+ sanei_config_attach_matching_devices ("/dev/scanner", attach_one);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local)
+{
+ static const SANE_Device **devlist = 0;
+ BH_Device *dev;
+ int i;
+ DBG(3, "sane_get_devices called\n");
+
+ local = local; /* get rid of compiler warning */
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle *handle)
+{
+ SANE_Status status;
+ BH_Device *dev;
+ BH_Scanner *s;
+ DBG(3, "sane_open called\n");
+
+ if (devnam[0] != '\0')
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ s->bmu = BH_UNIT_POINT;
+ s->mud = 1;
+
+ ScannerDump(s);
+
+ init_options (s);
+
+ s->next = first_handle;
+ first_handle = s;
+
+ /* initialize our parameters */
+ get_parameters(s, 0);
+
+ *handle = s;
+
+#ifdef FAKE_INQUIRY
+ if (fake_inquiry)
+ {
+ DBG (1, "sane_open: faking open of %s\n",
+ s->hw->sane.name);
+ }
+ else
+#endif
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ BH_Scanner *s = handle;
+ DBG(3, "sane_get_option_descriptor called (option:%d)\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *val, SANE_Word *info)
+{
+ BH_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+
+ DBG(3, "sane_control_option called\n");
+
+ name = s->opt[option].name ? s->opt[option].name : "(nil)";
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning && action == SANE_ACTION_SET_VALUE)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG(16, "sane_control_option: get_value %s [#%d]\n", name, option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_TIMEOUT_ADF:
+ case OPT_TIMEOUT_MANUAL:
+ case OPT_ACE_FUNCTION:
+ case OPT_ACE_SENSITIVITY:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_ICON_WIDTH:
+ case OPT_ICON_LENGTH:
+ case OPT_BARCODE_SEARCH_COUNT:
+ case OPT_BARCODE_HMIN:
+ case OPT_BARCODE_SEARCH_TIMEOUT:
+ case OPT_BARCODE_RELMAX:
+ case OPT_BARCODE_BARMIN:
+ case OPT_BARCODE_BARMAX:
+ case OPT_BARCODE_CONTRAST:
+ case OPT_BARCODE_PATCHMODE:
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_INQUIRY:
+ case OPT_SCAN_SOURCE:
+ case OPT_SCAN_MODE:
+ case OPT_COMPRESSION:
+ case OPT_PAPER_SIZE:
+ case OPT_ROTATION:
+ case OPT_BARCODE_SEARCH_BAR:
+ case OPT_BARCODE_SEARCH_MODE:
+ case OPT_SECTION:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* boolean options: */
+ case OPT_PREVIEW:
+ case OPT_AUTOBORDER:
+ case OPT_DESKEW:
+ case OPT_BATCH:
+ case OPT_CHECK_ADF:
+ case OPT_DUPLEX:
+ case OPT_CONTROL_PANEL:
+ case OPT_NEGATIVE:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ default:
+ DBG(1, "sane_control_option:invalid option number %d\n", option);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ switch (s->opt[option].type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ DBG(16, "sane_control_option: set_value %s [#%d] to %d\n",
+ name, option, *(SANE_Word *) val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ DBG(16, "sane_control_option: set_value %s [#%d] to %f\n",
+ name, option, SANE_UNFIX(*(SANE_Word *) val));
+ break;
+
+ case SANE_TYPE_STRING:
+ DBG(16, "sane_control_option: set_value %s [#%d] to %s\n",
+ name, option, (char *) val);
+ break;
+
+ default:
+ DBG(16, "sane_control_option: set_value %s [#%d]\n",
+ name, option);
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ /* make sure that paper-size is set to custom */
+ if (s->val[option].w != *(SANE_Word *) val)
+ {
+ if (info) *info |= SANE_INFO_RELOAD_PARAMS;
+
+ if (get_paper_id(_OPT_VAL_STRING(s, OPT_PAPER_SIZE)) != 0)
+ {
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ /* set paper size to 'custom' */
+ free (s->val[OPT_PAPER_SIZE].s);
+ s->val[OPT_PAPER_SIZE].s = strdup(paper_list[0]);
+ }
+ }
+ /* fall through */
+ case OPT_RESOLUTION:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_TIMEOUT_ADF:
+ case OPT_TIMEOUT_MANUAL:
+ case OPT_ACE_FUNCTION:
+ case OPT_ACE_SENSITIVITY:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_ICON_WIDTH:
+ case OPT_ICON_LENGTH:
+ case OPT_BARCODE_SEARCH_COUNT:
+ case OPT_BARCODE_HMIN:
+ case OPT_BARCODE_SEARCH_TIMEOUT:
+ case OPT_BARCODE_RELMAX:
+ case OPT_BARCODE_BARMIN:
+ case OPT_BARCODE_BARMAX:
+ case OPT_BARCODE_CONTRAST:
+ case OPT_BARCODE_PATCHMODE:
+ case OPT_NUM_OPTS:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_BARCODE_SEARCH_BAR:
+ /*!!! we're supporting only a single barcode type via the option */
+ s->search_bars[0] = get_barcode_id(val);
+ /* fall through */
+ case OPT_SCAN_SOURCE:
+ case OPT_COMPRESSION:
+ case OPT_ROTATION:
+ case OPT_BARCODE_SEARCH_MODE:
+ case OPT_SECTION:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* boolean options: */
+ case OPT_AUTOBORDER:
+ /*!!! autoborder true disables geometry controls
+ * and sets them to defaults?
+ */
+ /* fall through */
+ case OPT_PREVIEW:
+ case OPT_BATCH:
+ case OPT_DESKEW:
+ case OPT_CHECK_ADF:
+ case OPT_DUPLEX:
+ case OPT_NEGATIVE:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* options with side effects */
+ case OPT_CONTROL_PANEL:
+ /* a boolean option */
+ /* control-panel true enables/disables some enhancement controls */
+ if (s->val[option].w != *(SANE_Word *) val)
+ {
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->val[option].w = *(SANE_Word *) val;
+
+ if (*(SANE_Word *) val == SANE_TRUE)
+ {
+ if (s->hw->info.canACE == SANE_TRUE)
+ {
+ s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ if (s->hw->info.canACE == SANE_TRUE)
+ {
+ s->opt[OPT_ACE_FUNCTION].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ACE_SENSITIVITY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_SCAN_MODE:
+ /* a string option */
+ /* scan mode != lineart disables compression, setting it to
+ * 'none'
+ */
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (get_scan_mode_id((SANE_String) val) != 0)
+ {
+ /* scan mode is not lineart, disable compression
+ * and set compression to 'none'
+ */
+ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
+ if (s->val[OPT_COMPRESSION].s &&
+ get_compression_id(s->val[OPT_COMPRESSION].s) != 0)
+ {
+ free (s->val[OPT_COMPRESSION].s);
+ s->val[OPT_COMPRESSION].s = strdup(compression_list[0]);
+ }
+ }
+ else
+ {
+ /* scan mode is lineart, enable compression */
+ s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE;
+ }
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAPER_SIZE:
+ /* a string option */
+ /* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ SANE_Int paper_id = get_paper_id((SANE_String) val);
+
+ /* paper_id 0 is a special case (custom) that
+ * disables the paper size control of geometry
+ */
+ if (paper_id != 0)
+ {
+ double left, x_max, y_max, x, y;
+
+ x_max = SANE_UNFIX(s->hw->info.x_range.max);
+ y_max = SANE_UNFIX(s->hw->info.y_range.max);
+ /* a dimension of 0.0 (or less) is replaced with the max value */
+ x = (paper_sizes[paper_id].width <= 0.0) ? x_max :
+ paper_sizes[paper_id].width;
+ y = (paper_sizes[paper_id].length <= 0.0) ? y_max :
+ paper_sizes[paper_id].length;
+
+ if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ /* set geometry options based on paper size */
+ /* set geometry options based on paper size */
+ if (s->hw->info.canADF)
+ {
+ /* when the feeder is used the paper is centered in the
+ * hopper; with the manual feed it is aligned left.
+ */
+ left = (x_max - x) / 2.0;
+ if (left < 0.0) left = 0.0;
+ }
+ else
+ {
+ left = 0.0;
+ }
+
+ s->val[OPT_TL_X].w = SANE_FIX(left);
+ s->val[OPT_TL_Y].w = SANE_FIX(0.0);
+ s->val[OPT_BR_X].w = SANE_FIX(MIN(x + left, x_max));
+ s->val[OPT_BR_Y].w = SANE_FIX(MIN(y, y_max));
+ }
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ DBG(1, "sane_control_option:invalid option number %d\n", option);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
+{
+ BH_Scanner *s = handle;
+ SANE_Int status = SANE_STATUS_GOOD;
+
+ DBG(3, "sane_get_parameters called\n");
+
+ if (params)
+ {
+ SANE_Int res;
+
+ if (!s->scanning)
+ {
+ /* update our parameters ONLY if we're not scanning */
+ status = get_parameters(s, 0);
+ }
+
+ *params = s->params;
+
+ res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
+
+ DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, "
+ "lines=%d, dpi=%d\n",
+ (int) s->params.format,
+ s->params.pixels_per_line,
+ s->params.bytes_per_line,
+ s->params.lines,
+ res);
+ }
+
+ return status;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ BH_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG(3, "sane_start called\n");
+ s->cancelled = SANE_FALSE;
+
+ if (s->scanning == SANE_FALSE)
+ {
+ /* get preliminary parameters */
+ status = get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: get_parameters failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Do the setup once per 'batch'. The SANE standard requires the
+ * frontend to call sane_cancel once all desired frames have been
+ * acquired. That is when scanning is set back to SANE_FALSE and
+ * the 'batch' is considered done.
+ */
+ status = start_setup (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: start_setup failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: start_scan failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
+{
+ BH_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+
+ DBG(3, "sane_read called\n");
+
+ *len = 0;
+
+ if (s->cancelled) {
+ DBG (3, "sane_read: cancelled!\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (!s->scanning) {
+ DBG (3, "sane_read: scanning is false!\n");
+ sane_cancel(s);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ nread = maxlen;
+
+ DBG (3, "sane_read: request %lu bytes\n", (u_long) nread);
+ /* set InvalidBytes to 0 before read; sense_handler will set it
+ * to non-zero if we do the last partial read.
+ */
+ s->InvalidBytes = 0;
+ status = read_data (s, buf, &nread);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_read: read_data failed %s\n",
+ sane_strstatus(status));
+ sane_cancel (s);
+ return status;
+ }
+ nread = maxlen - s->InvalidBytes;
+ DBG (3, "sane_read: got %lu bytes\n", (u_long) nread);
+ *len = nread;
+
+ return (maxlen != 0 && nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+#ifdef NONBLOCKSUPPORTED
+ BH_Scanner *s = handle;
+#endif
+ DBG(3, "sane_set_io_mode called: non_blocking=%d\n", non_blocking);
+
+#ifdef NONBLOCKSUPPORTED
+ if (s->fd < 0)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (fcntl (s->fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ DBG(1, "sane_set_io_mode: error setting io mode\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+#else
+ handle = handle; /* get rid of compiler warning */
+ return (non_blocking == 1) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
+#endif
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+#ifdef NONBLOCKSUPPORTED
+ BH_Scanner *s = handle;
+#endif
+ DBG(3, "sane_get_select_fd called\n");
+
+#ifdef NONBLOCKSUPPORTED
+ if (s->fd < 0)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ *fd = s->fd;
+
+ return SANE_STATUS_GOOD;
+#else
+ handle = handle; fd = fd; /* get rid of compiler warning */
+ return SANE_STATUS_UNSUPPORTED;
+#endif
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ BH_Scanner *s = (BH_Scanner *) handle;
+ DBG(3, "sane_cancel called\n");
+ if (s->scanning)
+ {
+ /* if batchmode is enabled, then call set_window to
+ * abort the batch
+ */
+ if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE)
+ {
+ DBG(5, "sane_cancel: calling set_window to abort batch\n");
+ set_window(s, BH_BATCH_ABORT);
+ }
+ }
+ s->scanning = SANE_FALSE;
+ s->cancelled = SANE_TRUE;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ BH_Scanner *s = (BH_Scanner *) handle;
+ DBG(3, "sane_close called\n");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ free (s);
+}
+
+void
+sane_exit (void)
+{
+ BH_Device *dev, *next;
+ DBG(3, "sane_exit called\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev);
+ }
+}
+
diff --git a/backend/bh.conf.in b/backend/bh.conf.in
new file mode 100644
index 0000000..bca80ec
--- /dev/null
+++ b/backend/bh.conf.in
@@ -0,0 +1,2 @@
+scsi "B&H SCSI"
+/dev/scanner
diff --git a/backend/bh.h b/backend/bh.h
new file mode 100644
index 0000000..62115c6
--- /dev/null
+++ b/backend/bh.h
@@ -0,0 +1,1036 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1999,2000 Tom Martone
+ This file is part of a SANE backend for Bell and Howell Copiscan II
+ Scanners using the Remote SCSI Controller(RSC).
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef BH_H
+#define BH_H 1
+
+#ifndef PATH_MAX
+#define PATH_MAX (1024)
+#endif
+
+#define BH_CONFIG_FILE "bh.conf"
+
+/* number of barcode types that can be searched at one time */
+#define NUM_SEARCH_BARS 6
+/* number of additional scanning/decoding sections supported */
+#define NUM_SECTIONS 8
+/* number of possible reads per scan plus the extra one for
+ * the barcode file
+ */
+#define NUM_READS 56 + 1
+
+/* specify sanity limits for autoborder detection and barcode decoding */
+#define BH_AUTOBORDER_TRIES 100
+#define BH_DECODE_TRIES 100
+/* specify a fudge factor in mm for border around decoded barcodes */
+#define BH_DECODE_FUDGE 1.0
+
+/* section flags - what operation(s) to perform on section */
+#define BH_SECTION_FRONT_IMAGE (1 << 0)
+#define BH_SECTION_BACK_IMAGE (1 << 1)
+#define BH_SECTION_FRONT_BAR (1 << 2)
+#define BH_SECTION_BACK_BAR (1 << 3)
+#define BH_SECTION_FRONT_PATCH (1 << 4)
+#define BH_SECTION_BACK_PATCH (1 << 5)
+
+typedef enum
+{
+ BH_UNIT_INCH,
+ BH_UNIT_MM,
+ BH_UNIT_POINT
+} bh_measureUnit;
+
+typedef enum
+{
+ BH_COMP_NONE,
+ BH_COMP_G31D,
+ BH_COMP_G32D,
+ BH_COMP_G42D
+} bh_compress;
+
+typedef enum
+{
+ BH_ROTATION_0,
+ BH_ROTATION_90,
+ BH_ROTATION_180,
+ BH_ROTATION_270
+} bh_rotation;
+
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ /* inquiry string */
+ OPT_INQUIRY,
+ /* preview mode */
+ OPT_PREVIEW,
+ /* scan mode */
+ OPT_SCAN_MODE,
+ /* resolution */
+ OPT_RESOLUTION,
+ /* hardware compression */
+ OPT_COMPRESSION,
+
+ OPT_GEOMETRY_GROUP,
+ /* automatic border detection */
+ OPT_AUTOBORDER,
+ /* hardware rotation */
+ OPT_ROTATION,
+ /* hardware deskew */
+ OPT_DESKEW,
+ /* paper size */
+ OPT_PAPER_SIZE,
+ /* top-left x */
+ OPT_TL_X,
+ /* top-left y */
+ OPT_TL_Y,
+ /* bottom-right x */
+ OPT_BR_X,
+ /* bottom-right y */
+ OPT_BR_Y,
+
+ OPT_FEEDER_GROUP,
+ /* scan source (eg. ADF) */
+ OPT_SCAN_SOURCE,
+ /* scan in batch mode */
+ OPT_BATCH,
+ /* scan both sides of the page */
+ OPT_DUPLEX,
+ /* timeout in seconds with manual feed */
+ OPT_TIMEOUT_MANUAL,
+ /* timeout in seconds with ADF */
+ OPT_TIMEOUT_ADF,
+ /* check for page in ADF before scanning */
+ OPT_CHECK_ADF,
+
+ OPT_ENHANCEMENT_GROUP,
+ /* Enables the scanner's control panel */
+ OPT_CONTROL_PANEL,
+ /* ACE Function */
+ OPT_ACE_FUNCTION,
+ /* ACE Sensitivity */
+ OPT_ACE_SENSITIVITY,
+ /* Brightness */
+ OPT_BRIGHTNESS,
+ /* Threshold */
+ OPT_THRESHOLD,
+ /* Contrast */
+ OPT_CONTRAST,
+ /* Negative (reverse image) */
+ OPT_NEGATIVE,
+
+ OPT_ICON_GROUP,
+ /* Width of icon (thumbnail) image in pixels */
+ OPT_ICON_WIDTH,
+ /* Length of icon (thumbnail) image in pixels */
+ OPT_ICON_LENGTH,
+
+ OPT_BARCODE_GROUP,
+ /* Add <name> to barcode search priority. */
+ OPT_BARCODE_SEARCH_BAR,
+ /* Barcode search count (1-7, default 3). */
+ OPT_BARCODE_SEARCH_COUNT,
+ /* Barcode search mode.
+ * (1 = horizontal,2 = vertical, 6 = v then h, 9 = h then v).
+ */
+ OPT_BARCODE_SEARCH_MODE,
+ /* Patch code min height (def=127 (5mm)) */
+ OPT_BARCODE_HMIN,
+ /* Barcode search timeout in ms
+ * (20-65535,default is disabled).
+ */
+ OPT_BARCODE_SEARCH_TIMEOUT,
+ /* Specify image sections and functions
+ */
+ OPT_SECTION,
+ /* Specifies the maximum relation from the widest to
+ * the smallest bar
+ */
+ OPT_BARCODE_RELMAX,
+ /* Specifies the minimum number of bars in Bar/Patch code */
+ OPT_BARCODE_BARMIN,
+ /* Specifies the maximum number of bars in a Bar/Patch code */
+ OPT_BARCODE_BARMAX,
+ /* Specifies the image contrast used in decoding.
+ * Use higher values when there are more white pixels
+ * in the code
+ */
+ OPT_BARCODE_CONTRAST,
+ /* Controls Patch Code detection. */
+ OPT_BARCODE_PATCHMODE,
+
+ /* must come last: */
+ NUM_OPTIONS
+
+} BH_Option;
+
+/* macros for accessing the value for an option within a scanning context */
+#define _OPT_VAL_WORD(s, o) ((s)->val[(o)].w)
+#define _OPT_VAL_WORD_THOUSANDTHS(s, o) \
+ (SANE_UNFIX(_OPT_VAL_WORD((s), (o))) * 1000.0 / MM_PER_INCH)
+#define _OPT_VAL_STRING(s, o) ((s)->val[(o)].s)
+#define _OPT_VAL_WORD_ARRAY(s, o) ((s)->val[(o)].wa)
+
+
+typedef struct _BH_Paper
+{
+ SANE_String name;
+
+ /* paper dimensions in mm */
+ double width, length;
+} BH_Paper;
+
+typedef struct _BH_Section
+{
+ /* section dimensions - in millimeters */
+ u_long top, left, width, length;
+
+ /* compression type/arg/frameformat */
+ SANE_Byte compressiontype;
+ SANE_Byte compressionarg;
+ SANE_Frame format;
+
+ /* Flags (see BH_SECTION_...) indicating operation(s) to perform
+ * on the section. If zero, the section is completely disabled
+ * and will not even be defined in set_window.
+ */
+ SANE_Word flags;
+
+} BH_Section;
+
+typedef struct _BH_Info
+{
+ SANE_Range x_range;
+ SANE_Range y_range;
+
+ SANE_Int res_default;
+ SANE_Bool autoborder_default;
+ SANE_Bool batch_default;
+ SANE_Bool deskew_default;
+ SANE_Bool check_adf_default;
+ SANE_Bool duplex_default;
+ SANE_Int timeout_adf_default;
+ SANE_Int timeout_manual_default;
+ SANE_Bool control_panel_default;
+
+ /* additional discovered/guessed capabilities */
+ SANE_Bool canACE;
+ SANE_Bool canDuplex;
+ SANE_Bool canCheckADF;
+
+ /* standard information */
+ SANE_Byte devtype;
+ SANE_Char vendor[9]; /* model name */
+ SANE_Char product[17]; /* product name */
+ SANE_Char revision[5]; /* revision */
+
+ /* VPD information */
+ SANE_Bool canADF; /* is there an ADF available */
+ SANE_Bool colorBandW; /* can scanner do black and white */
+ SANE_Bool colorHalftone; /* can scanner do Halftone */
+ SANE_Bool canWhiteFrame; /* data processing: White Framing */
+ SANE_Bool canBlackFrame; /* data processing: Black Framing */
+ SANE_Bool canEdgeExtract; /* data processing: ACE: Edge Extraction */
+ SANE_Bool canNoiseFilter; /* data processing: ACE: Noise Filtering */
+ SANE_Bool canSmooth; /* data processing: ACE: Smoothing */
+ SANE_Bool canLineBold; /* data processing: ACE: LineBolding */
+ SANE_Bool comprG3_1D; /* compression: Group 3, 1 dimensional */
+ SANE_Bool comprG3_2D; /* compression: Group 3, 2 dimensional */
+ SANE_Bool comprG4; /* compression: Group 4 */
+ SANE_Bool canBorderRecog; /* can do border recognition */
+ SANE_Bool canBarCode; /* bar code support available */
+ SANE_Bool canIcon; /* icon support available */
+ SANE_Bool canSection; /* section support available */
+ SANE_Int lineMaxBytes; /* maximum bytes per scan-line */
+
+ /* jis information */
+ SANE_Int resBasicX; /* basic X resolution */
+ SANE_Int resBasicY; /* basic Y resolution */
+ SANE_Int resMaxX; /* maximum X resolution */
+ SANE_Int resMaxY; /* maximum Y resolution */
+ SANE_Int resMinX; /* minimum X resolution */
+ SANE_Int resMinY; /* minimum Y resolution */
+ SANE_Int resStdList[16+1]; /* list of available standard resolutions
+ * (first slot is the length)
+ */
+ SANE_Int winWidth; /* length of window (in BasicX res DPI) */
+ SANE_Int winHeight; /* height of window (in BasicY res DPI) */
+} BH_Info;
+
+typedef struct _BH_Device BH_Device;
+
+struct _BH_Device
+{
+ BH_Device *next;
+ SANE_Device sane;
+ BH_Info info;
+};
+
+
+typedef struct _BH_Scanner BH_Scanner;
+struct _BH_Scanner
+{
+ /* all the state needed to define a scan request: */
+
+ /* linked list for housekeeping */
+ BH_Scanner *next;
+
+ /* scanner dependent/low-level state: */
+ BH_Device *hw;
+
+ /* SCSI filedescriptor */
+ int fd;
+
+ /* tempfile which is used to send decoded barcode data */
+ FILE *barf;
+ char barfname[PATH_MAX+1];
+
+ /* SANE option descriptors and values */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ /* additional values that don't fit into Option_Value representation */
+ SANE_Byte search_bars[NUM_SEARCH_BARS];
+ BH_Section sections[NUM_SECTIONS];
+ SANE_Int num_sections;
+
+ /* SANE image parameters */
+ SANE_Parameters params;
+
+ /* state information - not options */
+
+ /* Basic Measurement Unit */
+ SANE_Int bmu;
+ /* Measurement Unit Divisor */
+ SANE_Int mud;
+
+ /* track data to be read. ReadList contains the codes of the read types
+ * (see BH_READ_TYPE...) to perform, readcnt is the total number of reads
+ * for this scan and readptr points to the current read operation.
+ */
+ SANE_Byte readlist[NUM_READS];
+ SANE_Int readcnt, readptr;
+
+ u_long InvalidBytes;
+ SANE_Bool scanning;
+ SANE_Bool cancelled;
+ SANE_Bool backpage;
+ SANE_Bool barcodes;
+ SANE_Bool patchcodes;
+ SANE_Bool icons;
+ u_long iconwidth, iconlength;
+ SANE_Bool barcode_not_found;
+};
+
+static const SANE_Range u8_range =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range u16_range =
+{
+ 0, /* minimum */
+ 65535, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range icon_range =
+{
+ 0, /* minimum */
+ 3600, /* maximum */
+ 8 /* quantization */
+};
+
+static const SANE_Range barcode_search_timeout_range =
+{
+ 20, /* minimum */
+ 65535, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range barcode_hmin_range =
+{
+ 1, /* minimum */
+ 1660, /* maximum (when converted from mm
+ * to thousandths will still be less
+ * than 65536)
+ */
+ 0 /* quantization */
+};
+
+static const SANE_Range barcode_search_count_range =
+{
+ 1, /* minimum */
+ 7, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range barcode_relmax_range =
+{
+ 0, /* minimum */
+ 6, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range barcode_contrast_range =
+{
+ 0, /* minimum */
+ 6, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range barcode_patchmode_range =
+{
+ 0, /* minimum */
+ 1, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range ace_function_range =
+{
+ -4, /* minimum */
+ 4, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range ace_sensitivity_range =
+{
+ 0, /* minimum */
+ 9, /* maximum */
+ 0 /* quantization */
+};
+
+static SANE_String_Const scan_mode_list[] =
+{
+ "lineart",
+ "halftone",
+ 0
+};
+
+static SANE_String_Const scan_mode_min_list[] =
+{
+ "lineart",
+ 0
+};
+
+static SANE_String_Const barcode_search_mode_list[] =
+{
+ "horiz-vert", /* 9 */
+ "horizontal", /* 1 */
+ "vertical", /* 2 */
+ "vert-horiz", /* 6 */
+ 0
+};
+
+static SANE_String_Const scan_source_list[] =
+{
+ "Automatic Document Feeder",
+ "Manual Feed Tray",
+ 0
+};
+
+static SANE_String_Const compression_list[] =
+{
+ "none",
+ "g31d",
+ "g32d",
+ "g42d",
+ 0
+};
+
+/* list of supported bar/patch codes */
+static SANE_String_Const barcode_search_bar_list[] =
+{
+ "none",
+ "ean-8",
+ "ean-13",
+ "reserved-ean-add",
+ "code39",
+ "code2-5-interleaved",
+ "code2-5-3lines-matrix",
+ "code2-5-3lines-datalogic",
+ "code2-5-5lines-industrial",
+ "patchcode",
+ "codabar",
+ "codabar-with-start-stop",
+ "code39ascii",
+ "code128",
+ "code2-5-5lines-iata",
+ 0
+};
+
+/* list of supported rotation angles */
+static SANE_String_Const rotation_list[] =
+{
+ "0",
+ "90",
+ "180",
+ "270",
+ 0
+};
+
+/* list of support paper sizes */
+/* 'custom' MUST be item 0; otherwise a width or length of 0 indicates
+ * the maximum value supported by the scanner
+ */
+static const BH_Paper paper_sizes[] =
+{
+ {"Custom", 0.0, 0.0},
+ {"Letter", 215.9, 279.4},
+ {"Legal", 215.9, 355.6},
+ {"A3", 297, 420},
+ {"A4", 210, 297},
+ {"A5", 148.5, 210},
+ {"A6", 105, 148.5},
+ {"B4", 250, 353},
+ {"B5", 182, 257},
+ {"Full", 0.0, 0.0},
+};
+
+/* MUST be kept in sync with paper_sizes */
+static SANE_String_Const paper_list[] =
+{
+ "Custom",
+ "Letter",
+ "Legal",
+ "A3",
+ "A4",
+ "A5",
+ "A6",
+ "B4",
+ "B5",
+ "Full",
+ 0
+};
+
+static /* inline */ int _is_host_little_endian(void);
+static /* inline */ int
+_is_host_little_endian()
+{
+ SANE_Int val = 255;
+ unsigned char *firstbyte = (unsigned char *) &val;
+
+ return (*firstbyte == 255) ? SANE_TRUE : SANE_FALSE;
+}
+
+static /* inline */ void
+_lto2b(u_long val, SANE_Byte *bytes)
+{
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static /* inline */ void
+_lto3b(u_long val, SANE_Byte *bytes)
+{
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static /* inline */ void
+_lto4b(u_long val, SANE_Byte *bytes)
+{
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static /* inline */ u_long
+_2btol(SANE_Byte *bytes)
+{
+ u_long rv;
+
+ rv = (bytes[0] << 8) | bytes[1];
+
+ return rv;
+}
+
+static /* inline */ u_long
+_4btol(SANE_Byte *bytes)
+{
+ u_long rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+
+ return rv;
+}
+
+#define SANE_TITLE_SCAN_MODE_GROUP "Scan Mode"
+#define SANE_TITLE_GEOMETRY_GROUP "Geometry"
+#define SANE_TITLE_FEEDER_GROUP "Feeder"
+#define SANE_TITLE_ENHANCEMENT_GROUP "Enhancement"
+#define SANE_TITLE_ICON_GROUP "Icon"
+#define SANE_TITLE_BARCODE_GROUP "Barcode"
+
+#define SANE_NAME_AUTOBORDER "autoborder"
+#define SANE_TITLE_AUTOBORDER "Autoborder"
+#define SANE_DESC_AUTOBORDER "Enable Automatic Border Detection"
+
+#define SANE_NAME_COMPRESSION "compression"
+#define SANE_TITLE_COMPRESSION "Data Compression"
+#define SANE_DESC_COMPRESSION "Sets the compression mode of the scanner"
+
+#define SANE_NAME_ROTATION "rotation"
+#define SANE_TITLE_ROTATION "Page Rotation"
+#define SANE_DESC_ROTATION "Sets the page rotation mode of the scanner"
+
+#define SANE_NAME_DESKEW "deskew"
+#define SANE_TITLE_DESKEW "Page Deskew"
+#define SANE_DESC_DESKEW "Enable Deskew Mode"
+
+#define SANE_NAME_TIMEOUT_ADF "timeout-adf"
+#define SANE_TITLE_TIMEOUT_ADF "ADF Timeout"
+#define SANE_DESC_TIMEOUT_ADF "Sets the timeout in seconds for the ADF"
+
+#define SANE_NAME_TIMEOUT_MANUAL "timeout-manual"
+#define SANE_TITLE_TIMEOUT_MANUAL "Manual Timeout"
+#define SANE_DESC_TIMEOUT_MANUAL "Sets the timeout in seconds for manual feeder"
+
+#define SANE_NAME_BATCH "batch"
+#define SANE_TITLE_BATCH "Batch"
+#define SANE_DESC_BATCH "Enable Batch Mode"
+
+#define SANE_NAME_CHECK_ADF "check-adf"
+#define SANE_TITLE_CHECK_ADF "Check ADF"
+#define SANE_DESC_CHECK_ADF "Check ADF Status prior to starting scan"
+
+#define SANE_NAME_DUPLEX "duplex"
+#define SANE_TITLE_DUPLEX "Duplex"
+#define SANE_DESC_DUPLEX "Enable Duplex (Dual-Sided) Scanning"
+
+#define SANE_NAME_BARCODE_SEARCH_COUNT "barcode-search-count"
+#define SANE_TITLE_BARCODE_SEARCH_COUNT "Barcode Search Count"
+#define SANE_DESC_BARCODE_SEARCH_COUNT "Number of barcodes to search for in the scanned image"
+
+#define SANE_NAME_BARCODE_HMIN "barcode-hmin"
+#define SANE_TITLE_BARCODE_HMIN "Barcode Minimum Height"
+#define SANE_DESC_BARCODE_HMIN "Sets the Barcode Minimun Height (larger values increase recognition speed)"
+
+#define SANE_NAME_BARCODE_SEARCH_MODE "barcode-search-mode"
+#define SANE_TITLE_BARCODE_SEARCH_MODE "Barcode Search Mode"
+#define SANE_DESC_BARCODE_SEARCH_MODE "Chooses the orientation of barcodes to be searched"
+
+#define SANE_NAME_BARCODE_SEARCH_TIMEOUT "barcode-search-timeout"
+#define SANE_TITLE_BARCODE_SEARCH_TIMEOUT "Barcode Search Timeout"
+#define SANE_DESC_BARCODE_SEARCH_TIMEOUT "Sets the timeout for barcode searching"
+
+#define SANE_NAME_BARCODE_SEARCH_BAR "barcode-search-bar"
+#define SANE_TITLE_BARCODE_SEARCH_BAR "Barcode Search Bar"
+#define SANE_DESC_BARCODE_SEARCH_BAR "Specifies the barcode type to search for"
+
+#define SANE_NAME_SECTION "section"
+#define SANE_TITLE_SECTION "Image/Barcode Search Sections"
+#define SANE_DESC_SECTION "Specifies an image section and/or a barcode search region"
+
+#define SANE_NAME_BARCODE_RELMAX "barcode-relmax"
+#define SANE_TITLE_BARCODE_RELMAX "Barcode RelMax"
+#define SANE_DESC_BARCODE_RELMAX "Specifies the maximum relation from the widest to the smallest bar"
+
+#define SANE_NAME_BARCODE_BARMIN "barcode-barmin"
+#define SANE_TITLE_BARCODE_BARMIN "Barcode Bar Minimum"
+#define SANE_DESC_BARCODE_BARMIN "Specifies the minimum number of bars in Bar/Patch code"
+
+#define SANE_NAME_BARCODE_BARMAX "barcode-barmax"
+#define SANE_TITLE_BARCODE_BARMAX "Barcode Bar Maximum"
+#define SANE_DESC_BARCODE_BARMAX "Specifies the maximum number of bars in a Bar/Patch code"
+
+#define SANE_NAME_BARCODE_CONTRAST "barcode-contrast"
+#define SANE_TITLE_BARCODE_CONTRAST "Barcode Contrast"
+#define SANE_DESC_BARCODE_CONTRAST "Specifies the image contrast used in decoding. Use higher values when " \
+"there are more white pixels in the code"
+
+#define SANE_NAME_BARCODE_PATCHMODE "barcode-patchmode"
+#define SANE_TITLE_BARCODE_PATCHMODE "Barcode Patch Mode"
+#define SANE_DESC_BARCODE_PATCHMODE "Controls Patch Code detection."
+
+#define SANE_NAME_CONTROL_PANEL "control-panel"
+#define SANE_TITLE_CONTROL_PANEL "Control Panel "
+#define SANE_DESC_CONTROL_PANEL "Enables the scanner's control panel"
+
+#define SANE_NAME_ACE_FUNCTION "ace-function"
+#define SANE_TITLE_ACE_FUNCTION "ACE Function"
+#define SANE_DESC_ACE_FUNCTION "ACE Function"
+
+#define SANE_NAME_ACE_SENSITIVITY "ace-sensitivity"
+#define SANE_TITLE_ACE_SENSITIVITY "ACE Sensitivity"
+#define SANE_DESC_ACE_SENSITIVITY "ACE Sensitivity"
+
+#define SANE_NAME_ICON_WIDTH "icon-width"
+#define SANE_TITLE_ICON_WIDTH "Icon Width"
+#define SANE_DESC_ICON_WIDTH "Width of icon (thumbnail) image in pixels"
+
+#define SANE_NAME_ICON_LENGTH "icon-length"
+#define SANE_TITLE_ICON_LENGTH "Icon Length"
+#define SANE_DESC_ICON_LENGTH "Length of icon (thumbnail) image in pixels"
+
+#define SANE_NAME_PAPER_SIZE "paper-size"
+#define SANE_TITLE_PAPER_SIZE "Paper Size"
+#define SANE_DESC_PAPER_SIZE "Specify the scan window geometry by specifying the paper size " \
+"of the documents to be scanned"
+
+#define SANE_NAME_INQUIRY "inquiry"
+#define SANE_TITLE_INQUIRY "Inquiry Data"
+#define SANE_DESC_INQUIRY "Displays scanner inquiry data"
+
+/* low level SCSI commands and buffers */
+
+/* SCSI commands */
+#define BH_SCSI_TEST_UNIT_READY 0x00
+#define BH_SCSI_SET_WINDOW 0x24
+#define BH_SCSI_GET_WINDOW 0x25
+#define BH_SCSI_READ_SCANNED_DATA 0x28
+#define BH_SCSI_INQUIRY 0x12
+#define BH_SCSI_MODE_SELECT 0x15
+#define BH_SCSI_START_SCAN 0x1b
+#define BH_SCSI_MODE_SENSE 0x1a
+#define BH_SCSI_GET_BUFFER_STATUS 0x34
+#define BH_SCSI_OBJECT_POSITION 0x31
+
+/* page codes used with BH_SCSI_INQUIRY */
+#define BH_INQUIRY_STANDARD_PAGE_CODE 0x00
+#define BH_INQUIRY_VPD_PAGE_CODE 0xC0
+#define BH_INQUIRY_JIS_PAGE_CODE 0xF0
+
+/* page codes used with BH_SCSI_MODE_SELECT and BH_SCSI_MODE_SENSE */
+#define BH_MODE_MEASUREMENT_PAGE_CODE 0x03
+#define BH_MODE_TIMEOUT_PAGE_CODE 0x20
+#define BH_MODE_ICON_PAGE_CODE 0x21
+#define BH_MODE_BARCODE_PRIORITY_PAGE_CODE 0x30
+#define BH_MODE_BARCODE_PARAM1_PAGE_CODE 0x31
+#define BH_MODE_BARCODE_PARAM2_PAGE_CODE 0x32
+#define BH_MODE_BARCODE_PARAM3_PAGE_CODE 0x32
+
+/* data type codes used with BH_SCSI_READ_SCANNED_DATA */
+#define BH_SCSI_READ_TYPE_FRONT 0x80
+/* 0x81 thru 0x88 read front page sections 1 thru 8 respectively */
+#define BH_SCSI_READ_TYPE_BACK 0x90
+/* 0x91 thru 0x98 read back page sections 1 thru 8 respectively */
+#define BH_SCSI_READ_TYPE_FRONT_BARCODE 0xA0
+/* 0xA1 thru 0xA8 read front page barcodes in sections 1 thru 8 respectively */
+#define BH_SCSI_READ_TYPE_BACK_BARCODE 0xB0
+/* 0xB1 thru 0xB8 read back page barcodes in sections 1 thru 8 respectively */
+#define BH_SCSI_READ_TYPE_FRONT_PATCHCODE 0xC0
+/* 0xC1 thru 0xC8 read front page patchcodes in sections 1 thru 8 respectively */
+#define BH_SCSI_READ_TYPE_BACK_PATCHCODE 0xD0
+/* 0xD1 thru 0xD8 read back page patchcodes in sections 1 thru 8 respectively */
+#define BH_SCSI_READ_TYPE_FRONT_ICON 0x89
+#define BH_SCSI_READ_TYPE_BACK_ICON 0x99
+
+/* this one is not a real readtype; it's used to help transfer the barcode file */
+#define BH_SCSI_READ_TYPE_SENDBARFILE 0xBB
+
+#define BH_HAS_IMAGE_DATA(i) ((i) >= BH_SCSI_READ_TYPE_FRONT && \
+ (i) <= BH_SCSI_READ_TYPE_BACK_ICON)
+
+/* batchmode codes used with BH_SCSI_SET_WINDOW */
+#define BH_BATCH_DISABLE 0x00
+#define BH_BATCH_ENABLE 0x01
+#define BH_BATCH_TERMINATE 0x02
+#define BH_BATCH_ABORT 0x03
+
+/* deskew mode codes used with BH_SCSI_SET_WINDOW */
+#define BH_DESKEW_DISABLE 0x00 /* border detection is assumed, see page 3-37 of 8000 manual */
+#define BH_DESKEW_ENABLE 0x04 /* deskew and border detection */
+
+/* used with BH_SCSI_SET_WINDOW, BH_SCSI_GET_WINDOW */
+typedef struct _BH_SectionBlock {
+ SANE_Byte ul_x[4];
+ SANE_Byte ul_y[4];
+ SANE_Byte width[4];
+ SANE_Byte length[4];
+ SANE_Byte compressiontype;
+ SANE_Byte compressionarg;
+ SANE_Byte reserved[6];
+} BH_SectionBlock;
+
+/* used with BH_SCSI_SET_WINDOW, BH_SCSI_GET_WINDOW */
+struct window_data { /* window descriptor block byte layout */
+ SANE_Byte windowid; /* 0 */
+ SANE_Byte autoborder; /* 1 */
+ SANE_Byte xres[2]; /* 2,3 */
+ SANE_Byte yres[2]; /* 4,5 */
+ SANE_Byte ulx[4]; /* 6-9 */
+ SANE_Byte uly[4]; /* 10-13 */
+ SANE_Byte windowwidth[4]; /* 14-17 */
+ SANE_Byte windowlength[4]; /* 18-21 */
+ SANE_Byte brightness; /* 22 */
+ SANE_Byte threshold; /* 23 */
+ SANE_Byte contrast; /* 24 */
+ SANE_Byte imagecomposition; /* 25 */
+ SANE_Byte bitsperpixel; /* 26 */
+ SANE_Byte halftonecode; /* 27 */
+ SANE_Byte halftoneid; /* 28 */
+ SANE_Byte paddingtype; /* 29 */
+ SANE_Byte bitordering[2]; /* 30,31 */
+ SANE_Byte compressiontype; /* 32 */
+ SANE_Byte compressionarg; /* 33 */
+ SANE_Byte reserved2[6]; /* 34-39 */
+ SANE_Byte remote; /* 40 */
+ SANE_Byte acefunction; /* 41 */
+ SANE_Byte acesensitivity; /* 42 */
+ SANE_Byte batchmode; /* 43 */
+ SANE_Byte reserved3[2]; /* 44,45 */
+ SANE_Byte border_rotation; /* 46 added this for copiscan 8080 */
+ SANE_Byte reserved4[17]; /* 47-63 added this for copiscan 8080 */
+ BH_SectionBlock sectionblock[NUM_SECTIONS];
+};
+
+/* used with BH_SCSI_READ_SCANNED_DATA */
+/* structure for returned decoded barcode data */
+struct barcode_data {
+ SANE_Byte reserved1[2];
+ SANE_Byte barcodetype[2];
+ SANE_Byte statusflag[2];
+ SANE_Byte barcodeorientation[2];
+ SANE_Byte posxa[2];
+ SANE_Byte posya[2];
+ SANE_Byte posxb[2];
+ SANE_Byte posyb[2];
+ SANE_Byte posxc[2];
+ SANE_Byte posyc[2];
+ SANE_Byte posxd[2];
+ SANE_Byte posyd[2];
+ SANE_Byte barcodesearchtime[2];
+ SANE_Byte reserved2[13];
+ SANE_Byte barcodelen;
+ SANE_Byte barcodedata[160];
+};
+
+/* structure for returned icon data block */
+struct icon_data {
+ SANE_Byte windowwidth[4];
+ SANE_Byte windowlength[4];
+ SANE_Byte iconwidth[4];
+ SANE_Byte iconwidthbytes[4];
+ SANE_Byte iconlength[4];
+ SANE_Byte bitordering;
+ SANE_Byte reserved[7];
+ SANE_Byte icondatalen[4];
+};
+
+
+/* used with BH_SCSI_INQUIRY */
+
+/* Standard Data [EVPD=0] */
+struct inquiry_standard_data {
+ SANE_Byte devtype;
+ SANE_Byte reserved[7];
+ SANE_Byte vendor[8];
+ SANE_Byte product[16];
+ SANE_Byte revision[4];
+};
+
+/* VPD Information [EVPD=1, PageCode=C0H] */
+struct inquiry_vpd_data {
+ SANE_Byte devtype;
+ SANE_Byte pagecode;
+ SANE_Byte reserved1;
+ SANE_Byte alloclen;
+ SANE_Byte adf;
+ SANE_Byte reserved2[2];
+ SANE_Byte imagecomposition;
+ SANE_Byte imagedataprocessing[2];
+ SANE_Byte compression;
+ SANE_Byte reserved3;
+ SANE_Byte sizerecognition;
+ SANE_Byte optionalfeatures;
+ SANE_Byte xmaxoutputbytes[2];
+};
+
+
+/* JIS Information [EVPD=1, PageCode=F0H] */
+struct inquiry_jis_data {
+ SANE_Byte devtype;
+ SANE_Byte pagecode;
+ SANE_Byte jisversion;
+ SANE_Byte reserved1;
+ SANE_Byte alloclen;
+ SANE_Byte basicxres[2];
+ SANE_Byte basicyres[2];
+ SANE_Byte resolutionstep;
+ SANE_Byte maxxres[2];
+ SANE_Byte maxyres[2];
+ SANE_Byte minxres[2];
+ SANE_Byte minyres[2];
+ SANE_Byte standardres[2];
+ SANE_Byte windowwidth[4];
+ SANE_Byte windowlength[4];
+ SANE_Byte functions;
+ SANE_Byte reserved2;
+};
+
+/* used with BH_SCSI_MODE_SELECT and BH_SCSI_MODE_SENSE */
+
+/* Scanning Measurement Parameters PageCode=03H */
+struct mode_page_03 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte bmu;
+ SANE_Byte reserved1;
+ SANE_Byte mud[2];
+ SANE_Byte reserved2[2];
+};
+
+/* Scan Command Timeout PageCode=20H */
+struct mode_page_20 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte timeoutmanual;
+ SANE_Byte timeoutadf;
+ SANE_Byte reserved[4];
+};
+
+/* Icon Definition PageCode=21H */
+struct mode_page_21 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte iconwidth[2];
+ SANE_Byte iconlength[2];
+ SANE_Byte reserved[2];
+};
+
+/* Bar/Patch Code search priority order PageCode=30H */
+struct mode_page_30 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte priority[6];
+};
+
+/* Bar/Patch Code search parameters 1 of 3 PageCode=31H */
+struct mode_page_31 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte minbarheight[2];
+ SANE_Byte searchcount;
+ SANE_Byte searchmode;
+ SANE_Byte searchtimeout[2];
+};
+
+/* Bar/Patch Code search parameters 2 of 3 PageCode=32H */
+struct mode_page_32 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte relmax[2];
+ SANE_Byte barmin[2];
+ SANE_Byte barmax[2];
+};
+
+/* Bar/Patch Code search parameters 3 of 3 PageCode=33H */
+struct mode_page_33 {
+ SANE_Byte modedatalen;
+ SANE_Byte mediumtype;
+ SANE_Byte devicespecificparam;
+ SANE_Byte blockdescriptorlen;
+
+ SANE_Byte pagecode;
+ SANE_Byte paramlen;
+ SANE_Byte barcodecontrast[2];
+ SANE_Byte patchmode[2];
+ SANE_Byte reserved[2];
+};
+
+#ifndef sane_isbasicframe
+#define SANE_FRAME_TEXT 10
+#define SANE_FRAME_JPEG 11
+#define SANE_FRAME_G31D 12
+#define SANE_FRAME_G32D 13
+#define SANE_FRAME_G42D 14
+#define sane_strframe(f) ( (f) == SANE_FRAME_GRAY ? "gray" : \
+ (f) == SANE_FRAME_RGB ? "RGB" : \
+ (f) == SANE_FRAME_RED ? "red" : \
+ (f) == SANE_FRAME_GREEN ? "green" : \
+ (f) == SANE_FRAME_BLUE ? "blue" : \
+ (f) == SANE_FRAME_TEXT ? "text" : \
+ (f) == SANE_FRAME_JPEG ? "jpeg" : \
+ (f) == SANE_FRAME_G31D ? "g31d" : \
+ (f) == SANE_FRAME_G32D ? "g32d" : \
+ (f) == SANE_FRAME_G42D ? "g42d" : \
+ "unknown" )
+
+#define sane_isbasicframe(f) ( (f) == SANE_FRAME_GRAY || \
+ (f) == SANE_FRAME_RGB || \
+ (f) == SANE_FRAME_RED || \
+ (f) == SANE_FRAME_GREEN || \
+ (f) == SANE_FRAME_BLUE )
+
+#endif
+
+#endif /* BH_H */
diff --git a/backend/canon-sane.c b/backend/canon-sane.c
new file mode 100644
index 0000000..79ce0ba
--- /dev/null
+++ b/backend/canon-sane.c
@@ -0,0 +1,2229 @@
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ FILE *fp;
+
+ int i, j;
+ SANE_Byte primary, secondary, inmask, priMask, secMask;
+
+ DBG_INIT ();
+ DBG (1, ">> sane_init\n");
+
+/****** for lineart mode of FB1200S ******/
+ for (i = 0; i < 256; i++)
+ {
+ primary = secondary = 0;
+ inmask = 0x80;
+ priMask = 0x40;
+ secMask = 0x80;
+ for (j = 0; j < 4; j++)
+ {
+ if (i & inmask)
+ {
+ primary |= priMask;
+ secondary |= secMask;
+ }
+ priMask = priMask >> 2;
+ secMask = secMask >> 2;
+ inmask = inmask >> 1;
+ }
+ primaryHigh[i] = primary;
+ secondaryHigh[i] = secondary;
+
+ primary = secondary = 0;
+ priMask = 0x40;
+ secMask = 0x80;
+ for (j = 0; j < 4; j++)
+ {
+ if (i & inmask)
+ {
+ primary |= priMask;
+ secondary |= secMask;
+ }
+ priMask = priMask >> 2;
+ secMask = secMask >> 2;
+ inmask = inmask >> 1;
+ }
+ primaryLow[i] = primary;
+ secondaryLow[i] = secondary;
+ }
+/******************************************/
+
+#if defined PACKAGE && defined VERSION
+ DBG (2, "sane_init: " PACKAGE " " VERSION "\n");
+#endif
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (CANON_CONFIG_FILE);
+ if (fp)
+ {
+ char line[PATH_MAX];
+ size_t len;
+
+ /* read config file */
+ /* while (fgets (line, sizeof (line), fp)) */
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+ /*if (line[len - 1] == '\n')
+ line[--len] = '\0'; */
+
+ if (!len)
+ continue; /* ignore empty lines */
+ strcpy (devnam, line);
+ }
+ fclose (fp);
+ }
+ sanei_config_attach_matching_devices (devnam, attach_one);
+ DBG (1, "<< sane_init\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**************************************************************************/
+
+void
+sane_exit (void)
+{
+ CANON_Device *dev, *next;
+ DBG (1, ">> sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free ((void *) dev);
+ }
+
+ DBG (1, "<< sane_exit\n");
+}
+
+/**************************************************************************/
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+SANE_Bool UNUSEDARG local_only)
+{
+ static const SANE_Device **devlist = 0;
+ CANON_Device *dev;
+ int i;
+ DBG (1, ">> sane_get_devices\n");
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (1, "<< sane_get_devices\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**************************************************************************/
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ CANON_Device *dev;
+ CANON_Scanner *s;
+ int i, j, c;
+
+ DBG (1, ">> sane_open\n");
+
+ if (devnam[0] == '\0')
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+ }
+ else
+ dev = first_dev;
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+
+
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ if (s->hw->info.model == FS2710)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ s->gamma_map[i][0] = '\0';
+ s->gamma_table[i][0] = 0;
+ }
+ for (j = 1; j < 4096; ++j)
+ { /* FS2710 needs inital gamma 2.0 */
+ c = (int) (256.0 * pow (((double) j) / 4096.0, 0.5));
+ for (i = 0; i < 4; i++)
+ {
+ s->gamma_map[i][j] = (u_char) c;
+ if ((j & 0xf) == 0)
+ s->gamma_table[i][j >> 4] = c;
+ }
+ }
+ s->colour = 1;
+ s->auxbuf_len = 0;
+ }
+ else
+ {
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 256; ++j)
+ s->gamma_table[i][j] = j;
+ }
+ }
+
+ init_options (s);
+
+ if (s->hw->info.model == FB1200)
+ s->inbuffer = malloc (30894); /* modification for FB1200S */
+ else
+ s->inbuffer = malloc (15312); /* modification for FB620S */
+
+ if (!s->inbuffer)
+ return SANE_STATUS_NO_MEM;
+
+ if (s->hw->info.model == FB1200)
+ s->outbuffer = malloc (30894); /* modification for FB1200S */
+ else
+ s->outbuffer = malloc (15312); /* modification for FB620S */
+
+ if (!s->outbuffer)
+ {
+ free (s->inbuffer);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (1, "<< sane_open\n");
+ return SANE_STATUS_GOOD;
+
+}
+
+/**************************************************************************/
+
+void
+sane_close (SANE_Handle handle)
+{
+ CANON_Scanner *s = (CANON_Scanner *) handle;
+ SANE_Status status;
+
+ DBG (1, ">> sane_close\n");
+
+ if (s->val[OPT_EJECT_BEFOREEXIT].w)
+ {
+ if (s->fd == -1)
+ sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ status = medium_position (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_close: MEDIUM POSITION failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+ s->AF_NOW = SANE_TRUE;
+ DBG (1, "sane_close AF_NOW = '%d'\n", s->AF_NOW);
+ }
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+
+ if (s->inbuffer)
+ free (s->inbuffer); /* modification for FB620S */
+ if (s->outbuffer)
+ free (s->outbuffer); /* modification for FB620S */
+ if (s->auxbuf_len > 0)
+ free (s->auxbuf); /* modification for FS2710S */
+
+ free (s);
+
+ DBG (1, ">> sane_close\n");
+}
+
+/**************************************************************************/
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ CANON_Scanner *s = handle;
+ DBG (21, ">> sane_get_option_descriptor option number %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (21, " sane_get_option_descriptor option name %s\n",
+ option_name[option]);
+
+ DBG (21, "<< sane_get_option_descriptor option number %d\n", option);
+ return (s->opt + option);
+}
+
+/**************************************************************************/
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word w, cap;
+ SANE_Byte gbuf[4096];
+ size_t buf_size;
+ int i, neg, gamma_component, int_t, transfer_data_type;
+ time_t dtime, rt;
+
+ DBG (21, ">> sane_control_option %s\n", option_name[option]);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning == SANE_TRUE)
+ {
+ DBG (21, ">> sane_control_option: device is busy scanning\n");
+ return (SANE_STATUS_DEVICE_BUSY);
+ }
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (21, "sane_control_option get value of %s\n", option_name[option]);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_FLATBED_ONLY:
+ case OPT_TPU_ON:
+ case OPT_TPU_PN:
+ case OPT_TPU_TRANSPARENCY:
+ case OPT_RESOLUTION_BIND:
+ case OPT_HW_RESOLUTION_ONLY: /* 990320, ss */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_CUSTOM_GAMMA_BIND:
+ case OPT_HNEGATIVE:
+ /* case OPT_GRC: */
+ case OPT_MIRROR:
+ case OPT_AE:
+ case OPT_PREVIEW:
+ case OPT_BIND_HILO:
+ case OPT_HILITE_R:
+ case OPT_SHADOW_R:
+ case OPT_HILITE_G:
+ case OPT_SHADOW_G:
+ case OPT_HILITE_B:
+ case OPT_SHADOW_B:
+ case OPT_EJECT_AFTERSCAN:
+ case OPT_EJECT_BEFOREEXIT:
+ case OPT_THRESHOLD:
+ case OPT_AF:
+ case OPT_AF_ONCE:
+ case OPT_FOCUS:
+ if ((option >= OPT_NEGATIVE) && (option <= OPT_SHADOW_B))
+ {
+ DBG (21, "GET_VALUE for %s: s->val[%s].w = %d\n",
+ option_name[option], option_name[option],
+ s->val[option].w);
+ }
+ *(SANE_Word *) val = s->val[option].w;
+ DBG (21, "value for option %s: %d\n", option_name[option],
+ s->val[option].w);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+
+ memset (gbuf, 0, sizeof (gbuf));
+ buf_size = 256;
+ transfer_data_type = 0x03;
+
+ DBG (21, "sending GET_DENSITY_CURVE\n");
+ if (s->val[OPT_CUSTOM_GAMMA_BIND].w == SANE_TRUE)
+ /* If using bind analog gamma, option will be OPT_GAMMA_VECTOR.
+ In this case, use the curve for green */
+ gamma_component = 2;
+ else
+ /* Else use a different index for each curve */
+ gamma_component = option - OPT_GAMMA_VECTOR;
+
+ /* Now get the values from the scanner */
+ if (s->hw->info.model != FS2710)
+ {
+ sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ status =
+ get_density_curve (s->fd, gamma_component, gbuf, &buf_size,
+ transfer_data_type);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (21, "GET_DENSITY_CURVE\n");
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else
+ status =
+ get_density_curve_fs2710 (s, gamma_component, gbuf, &buf_size);
+
+ neg = (s->hw->info.is_filmscanner) ?
+ strcmp (filmtype_list[1], s->val[OPT_NEGATIVE].s)
+ : s->val[OPT_HNEGATIVE].w;
+
+ for (i = 0; i < 256; i++)
+ {
+ if (!neg)
+ s->gamma_table[option - OPT_GAMMA_VECTOR][i] =
+ (SANE_Int) gbuf[i];
+ else
+ s->gamma_table[option - OPT_GAMMA_VECTOR][i] =
+ 255 - (SANE_Int) gbuf[255 - i];
+ }
+
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ DBG (21, "value for option %s: %d\n", option_name[option],
+ s->val[option].w);
+ return (SANE_STATUS_GOOD);
+
+ /* string options: */
+ case OPT_TPU_DCM:
+ case OPT_TPU_FILMTYPE:
+ case OPT_MODE:
+ case OPT_NEGATIVE:
+ case OPT_NEGATIVE_TYPE:
+ case OPT_SCANNING_SPEED:
+ strcpy (val, s->val[option].s);
+ DBG (21, "value for option %s: %s\n", option_name[option],
+ s->val[option].s);
+ return (SANE_STATUS_GOOD);
+
+ default:
+ val = 0;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (21, "sane_control_option set value for %s\n", option_name[option]);
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_TPU_PN:
+ case OPT_TPU_TRANSPARENCY:
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_HNEGATIVE:
+ /* case OPT_GRC: */
+ case OPT_MIRROR:
+ case OPT_AE:
+ case OPT_PREVIEW:
+ case OPT_HILITE_R:
+ case OPT_SHADOW_R:
+ case OPT_HILITE_G:
+ case OPT_SHADOW_G:
+ case OPT_HILITE_B:
+ case OPT_SHADOW_B:
+ case OPT_AF_ONCE:
+ case OPT_FOCUS:
+ case OPT_EJECT_AFTERSCAN:
+ case OPT_EJECT_BEFOREEXIT:
+ s->val[option].w = *(SANE_Word *) val;
+ DBG (21, "SET_VALUE for %s: s->val[%s].w = %d\n",
+ option_name[option], option_name[option], s->val[option].w);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_RESOLUTION_BIND:
+ if (s->val[option].w != *(SANE_Word *) val)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (!s->val[option].w)
+ { /* don't bind */
+ s->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_X_RESOLUTION].title =
+ SANE_TITLE_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ }
+ else
+ { /* bind */
+ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ /* 990320, ss: switch between slider and option menue for resolution */
+ case OPT_HW_RESOLUTION_ONLY:
+ if (s->val[option].w != *(SANE_Word *) val)
+ {
+ int iPos, xres, yres;
+
+ s->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (!s->val[option].w) /* use complete range */
+ {
+ s->opt[OPT_X_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range =
+ &s->hw->info.xres_range;
+ s->opt[OPT_Y_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range =
+ &s->hw->info.yres_range;
+ }
+ else /* use only hardware resolutions */
+ {
+ s->opt[OPT_X_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_X_RESOLUTION].constraint.word_list =
+ s->xres_word_list;
+ s->opt[OPT_Y_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_Y_RESOLUTION].constraint.word_list =
+ s->yres_word_list;
+
+ /* adjust resolutions */
+ xres = s->xres_word_list[1];
+ for (iPos = 0; iPos < s->xres_word_list[0]; iPos++)
+ {
+ if (s->val[OPT_X_RESOLUTION].w >=
+ s->xres_word_list[iPos + 1])
+ xres = s->xres_word_list[iPos + 1];
+ }
+ s->val[OPT_X_RESOLUTION].w = xres;
+
+ yres = s->yres_word_list[1];
+ for (iPos = 0; iPos < s->yres_word_list[0]; iPos++)
+ {
+ if (s->val[OPT_Y_RESOLUTION].w >=
+ s->yres_word_list[iPos + 1])
+ yres = s->yres_word_list[iPos + 1];
+ }
+ s->val[OPT_Y_RESOLUTION].w = yres;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+
+ case OPT_BIND_HILO:
+ if (s->val[option].w != *(SANE_Word *) val)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (!s->val[option].w)
+ { /* don't bind */
+ s->opt[OPT_HILITE_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_HILITE_B].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE;
+
+ s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT_G;
+ s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT_G;
+ s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT_G;
+
+ s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G;
+ s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G;
+ s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G;
+ }
+ else
+ { /* bind */
+ s->opt[OPT_HILITE_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HILITE_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT;
+ s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT;
+ s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT;
+
+ s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW;
+ s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW;
+ s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_AF:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ s->val[option].w = *(SANE_Word *) val;
+ w = *(SANE_Word *) val;
+ if (w)
+ {
+ s->opt[OPT_AF_ONCE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_AF_ONCE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ return (SANE_STATUS_GOOD);
+
+ case OPT_FLATBED_ONLY:
+ s->val[option].w = *(SANE_Word *) val;
+ if (s->hw->adf.Status != ADF_STAT_NONE && s->val[option].w)
+ { /* switch on */
+ s->hw->adf.Priority |= 0x03; /* flatbed mode */
+ s->hw->adf.Feeder &= 0x00; /* autofeed off (default) */
+ s->hw->adf.Status = ADF_STAT_DISABLED;
+ } /* if it isn't connected, don't bother fixing */
+ return SANE_STATUS_GOOD;
+
+ case OPT_TPU_ON:
+ s->val[option].w = *(SANE_Word *) val;
+ if (s->val[option].w) /* switch on */
+ {
+ s->hw->tpu.Status = TPU_STAT_ACTIVE;
+ s->opt[OPT_TPU_TRANSPARENCY].cap &=
+ (s->hw->tpu.ControlMode == 3) ? ~SANE_CAP_INACTIVE : ~0;
+ s->opt[OPT_TPU_FILMTYPE].cap &=
+ (s->hw->tpu.ControlMode == 1) ? ~SANE_CAP_INACTIVE : ~0;
+ }
+ else /* switch off */
+ {
+ s->hw->tpu.Status = TPU_STAT_INACTIVE;
+ s->opt[OPT_TPU_TRANSPARENCY].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TPU_FILMTYPE].cap |= SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_TPU_PN].cap ^= SANE_CAP_INACTIVE;
+ s->opt[OPT_TPU_DCM].cap ^= SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TPU_DCM:
+ if (s->val[OPT_TPU_DCM].s)
+ free (s->val[OPT_TPU_DCM].s);
+ s->val[OPT_TPU_DCM].s = strdup (val);
+
+ s->opt[OPT_TPU_TRANSPARENCY].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TPU_FILMTYPE].cap |= SANE_CAP_INACTIVE;
+ if (!strcmp (s->val[OPT_TPU_DCM].s,
+ SANE_I18N("Correction according to transparency ratio")))
+ {
+ s->hw->tpu.ControlMode = 3;
+ s->opt[OPT_TPU_TRANSPARENCY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (!strcmp (s->val[OPT_TPU_DCM].s,
+ SANE_I18N("Correction according to film type")))
+ {
+ s->hw->tpu.ControlMode = 1;
+ s->opt[OPT_TPU_FILMTYPE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ s->hw->tpu.ControlMode = 0;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TPU_FILMTYPE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)
+ || !strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ /* For Lineart and Halftone: */
+ /* Enable "threshold" */
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+
+ /* Disable "custom gamma" and "brightness & contrast" */
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* For Gray and Color modes: */
+ /* Disable "threshold" */
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ /* Enable "custom gamma" and "brightness & contrast" */
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR)
+ || !strcmp (val, SANE_I18N("Fine color")))
+ {
+ s->opt[OPT_CUSTOM_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA_BIND].w == SANE_TRUE)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &=
+ ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &=
+ ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &=
+ ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+
+ case OPT_NEGATIVE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!strcmp (val, SANE_I18N("Negatives")))
+ {
+ s->RIF = 0;
+ s->opt[OPT_NEGATIVE_TYPE].cap &= ~SANE_CAP_INACTIVE;
+ if (SANE_OPTION_IS_SETTABLE(s->opt[OPT_SCANNING_SPEED].cap))
+ s->opt[OPT_SCANNING_SPEED].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->RIF = 1;
+ s->opt[OPT_NEGATIVE_TYPE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SCANNING_SPEED].cap |= SANE_CAP_INACTIVE;
+ }
+ return (SANE_STATUS_GOOD);
+
+ case OPT_NEGATIVE_TYPE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ for (i = 0; strcmp (val, negative_filmtype_list[i]); i++);
+ s->negative_filmtype = i;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_SCANNING_SPEED:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ for (i = 0; strcmp (val, scanning_speed_list[i]); i++);
+ s->scanning_speed = i;
+ return (SANE_STATUS_GOOD);
+
+ /* modification for FB620S */
+ case OPT_CALIBRATION_NOW:
+ sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ if (status == SANE_STATUS_GOOD)
+ {
+ status = execute_calibration (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (21, "EXECUTE CALIBRATION failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ DBG (21, "EXECUTE CALIBRATION\n");
+ sanei_scsi_close (s->fd);
+ }
+ else
+ DBG (1, "calibration: cannot open device file\n");
+ s->fd = -1;
+ return status;
+
+ case OPT_SCANNER_SELF_DIAGNOSTIC:
+ sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ if (status == SANE_STATUS_GOOD)
+ {
+ status = send_diagnostic (s->fd);
+ {
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (21, "SEND DIAGNOSTIC error: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (21, "SEND DIAGNOSTIC result: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ }
+ }
+ else
+ DBG (1, "send diagnostic: cannot open device file\n");
+ s->fd = -1;
+ return status;
+
+ case OPT_RESET_SCANNER:
+ sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ if (status == SANE_STATUS_GOOD)
+ {
+ time (&(s->time1));
+ DBG (11, "time0 = %ld\n", s->time0);
+ DBG (11, "time1 = %ld\n", s->time1);
+ dtime = (s->time1) - (s->time0);
+ DBG (11, "dtime = %ld\n", dtime);
+
+ DBG (11, "switch_preview = %d\n", s->switch_preview);
+ if (s->switch_preview == 0)
+ {
+ rt = sqrt (15 * 15 * (SANE_UNFIX (s->val[OPT_BR_Y].w))
+ / 297) + 0.5;
+ rt = rt + 2;
+ }
+ else
+ rt = 17;
+
+ DBG (11, "SANE_UNFIX(s->val[OPT_BR_Y].w) = %f\n",
+ SANE_UNFIX (s->val[OPT_BR_Y].w));
+ DBG (11, "rt = %ld\n", rt);
+
+ if (dtime < rt)
+ {
+ int_t = (int) (rt - dtime);
+ DBG (11, "int_t = %d\n", int_t);
+
+ sleep (int_t);
+ }
+ status = reset_scanner (s->fd);
+ {
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (21, "RESET SCANNER failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ DBG (21, "RESET SCANNER\n");
+ sanei_scsi_close (s->fd);
+ }
+ }
+ else
+ DBG (1, "reset scanner: cannot open device file\n");
+ s->fd = -1;
+ return status;
+
+ case OPT_EJECT_NOW:
+ sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ status = medium_position (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (21, "MEDIUM POSITION failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ DBG (21, "AF_NOW before = '%d'\n", s->AF_NOW);
+ s->AF_NOW = SANE_TRUE;
+ DBG (21, "AF_NOW after = '%d'\n", s->AF_NOW);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ if (w)
+ {
+ const char *mode = s->val[OPT_MODE].s;
+
+ if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY))
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR)
+ || !strcmp (mode, SANE_I18N("Fine color")))
+ {
+ s->opt[OPT_CUSTOM_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA_BIND].w)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA_BIND:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA_BIND].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->val[OPT_CUSTOM_GAMMA_BIND].w = w;
+ if (w)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ DBG (21, "setting gamma vector\n");
+/* if (info) */
+/* *info |= SANE_INFO_RELOAD_OPTIONS; */
+ return (SANE_STATUS_GOOD);
+
+ }
+ }
+
+ DBG (1, "<< sane_control_option %s\n", option_name[option]);
+ return (SANE_STATUS_INVAL);
+
+}
+
+/**************************************************************************/
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
+{
+ CANON_Scanner *s = handle;
+ DBG (1, ">> sane_get_parameters\n");
+
+ if (!s->scanning)
+ {
+ int width, length, xres, yres;
+ const char *mode;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
+ * s->hw->info.mud / MM_PER_INCH;
+ length = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
+ * s->hw->info.mud / MM_PER_INCH;
+
+ xres = s->val[OPT_X_RESOLUTION].w;
+ yres = s->val[OPT_Y_RESOLUTION].w;
+ if (s->val[OPT_RESOLUTION_BIND].w || s->val[OPT_PREVIEW].w)
+ yres = xres;
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (xres > 0 && yres > 0 && width > 0 && length > 0)
+ {
+ DBG (11, "sane_get_parameters: width='%d', xres='%d', mud='%d'\n",
+ width, xres, s->hw->info.mud);
+ s->params.pixels_per_line = width * xres / s->hw->info.mud;
+ DBG (11, "sane_get_parameters: length='%d', yres='%d', mud='%d'\n",
+ length, yres, s->hw->info.mud);
+ s->params.lines = length * yres / s->hw->info.mud;
+ DBG (11, "sane_get_parameters: pixels_per_line='%d', lines='%d'\n",
+ s->params.pixels_per_line, s->params.lines);
+ }
+
+ mode = s->val[OPT_MODE].s;
+ if (!strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART)
+ || !strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ /* workaround rounding problems */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ s->params.depth = 1;
+ }
+ else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR)
+ || !strcmp (mode, SANE_I18N("Fine color")))
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ else
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 6 * s->params.pixels_per_line;
+ s->params.depth = 16;
+ }
+ s->params.last_frame = SANE_TRUE;
+ }
+
+ DBG (11, "sane_get_parameters: xres='%d', yres='%d', pixels_per_line='%d', "
+ "bytes_per_line='%d', lines='%d'\n", s->xres, s->yres,
+ s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines);
+
+ if (params)
+ *params = s->params;
+
+ DBG (1, "<< sane_get_parameters\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int mode;
+ char *mode_str;
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ u_char wbuf[72], dbuf[28], ebuf[72];
+ u_char cbuf[2]; /* modification for FB620S */
+ size_t buf_size, i;
+
+ char tmpfilename[] = "/tmp/canon.XXXXXX"; /* for FB1200S */
+ char *thistmpfile; /* for FB1200S */
+
+ DBG (1, ">> sane_start\n");
+
+ s->tmpfile = -1; /* for FB1200S */
+
+/******* making a tempfile for 1200 dpi scanning of FB1200S ******/
+ if (s->hw->info.model == FB1200)
+ {
+ thistmpfile = strdup(tmpfilename);
+
+ if (thistmpfile != NULL)
+ {
+ if (mktemp(thistmpfile) == 0)
+ {
+ DBG(1, "mktemp(thistmpfile) is failed\n");
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else
+ {
+ DBG(1, "strdup(thistmpfile) is failed\n");
+ return (SANE_STATUS_INVAL);
+ }
+
+ s->tmpfile = open(thistmpfile, O_RDWR | O_CREAT | O_EXCL, 0600);
+
+ if (s->tmpfile == -1)
+ {
+ DBG(1, "error opening temp file %s\n", thistmpfile);
+ DBG(1, "errno: %i; %s\n", errno, strerror(errno));
+ errno = 0;
+ return (SANE_STATUS_INVAL);
+ }
+ DBG(1, " ****** tmpfile is opened ****** \n");
+
+ unlink(thistmpfile);
+ free (thistmpfile);
+ DBG(1, "free thistmpfile\n");
+ }
+/******************************************************************/
+
+ s->scanning = SANE_FALSE;
+
+ if ((s->hw->adf.Status != ADF_STAT_NONE)
+ && (s->val[OPT_FLATBED_ONLY].w != SANE_TRUE)
+ && (s->hw->adf.Problem != 0))
+ {
+ DBG (3, "SCANNER ADF HAS A PROBLEM\n");
+ if (s->hw->adf.Problem & 0x08)
+ {
+ status = SANE_STATUS_COVER_OPEN;
+ DBG (3, "ADF Cover Open\n");
+ }
+ else if (s->hw->adf.Problem & 0x04)
+ {
+ status = SANE_STATUS_JAMMED;
+ DBG (3, "ADF Paper Jam\n");
+ }
+ else /* adf.Problem = 0x02 */
+ {
+ status = SANE_STATUS_NO_DOCS;
+ DBG (3, "ADF No More Documents\n");
+ }
+ return status;
+ }
+ else if ((s->hw->adf.Status != ADF_STAT_NONE)
+ && (s->val[OPT_FLATBED_ONLY].w == SANE_TRUE))
+ {
+ set_adf_mode (s->fd, s->hw->adf.Priority);
+ /* 2.23 define ADF Mode */
+ }
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n", s->hw->sane.name,
+ sane_strstatus (status));
+ return (status);
+ }
+
+#if 0 /* code moved after define_scan() calls */
+ /* Do focus, but not for the preview */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FOCUS_GROUP].cap)
+ && !s->val[OPT_PREVIEW].w && s->AF_NOW)
+ {
+ if ((status = do_focus (s)) != SANE_STATUS_GOOD) return (status);
+ if (s->val[OPT_AF_ONCE].w) s->AF_NOW = SANE_FALSE;
+ }
+#endif
+
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ if ((status = do_gamma (s)) != SANE_STATUS_GOOD) return (status);
+ }
+
+ DBG (3, "attach: sending GET SCAN MODE for scan control conditions\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = 20;
+ status = get_scan_mode (s->fd, (u_char) SCAN_CONTROL_CONDITIONS,
+ ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n");
+ sanei_scsi_close (s->fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ DBG (3, "scan mode control byte[%d] = %d\n", (int) i, ebuf[i]);
+
+ if (s->hw->adf.Status != ADF_STAT_NONE)
+ {
+ DBG (3, "attach: sending GET SCAN MODE for transparency unit\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = 12;
+ status = get_scan_mode (s->fd, (u_char) TRANSPARENCY_UNIT,
+ ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET SCAN MODE for transparency unit failed\n");
+ sanei_scsi_close (s->fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ DBG (3, "scan mode control byte[%d] = %d\n", (int) i,
+ ebuf[i]);
+ }
+
+ mode_str = s->val[OPT_MODE].s;
+ s->xres = s->val[OPT_X_RESOLUTION].w;
+ s->yres = s->val[OPT_Y_RESOLUTION].w;
+
+ if (s->val[OPT_RESOLUTION_BIND].w || s->val[OPT_PREVIEW].w)
+ s->yres = s->xres;
+
+ s->ulx = SANE_UNFIX (s->val[OPT_TL_X].w) * s->hw->info.mud / MM_PER_INCH;
+ s->uly = SANE_UNFIX (s->val[OPT_TL_Y].w) * s->hw->info.mud / MM_PER_INCH;
+
+ s->width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
+ * s->hw->info.mud / MM_PER_INCH;
+ s->length = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
+ * s->hw->info.mud / MM_PER_INCH;
+
+ DBG (11, "s->width='%d', s->length='%d'\n", s->width, s->length);
+
+ if (s->hw->info.model != CS2700 && s->hw->info.model != FS2710)
+ {
+ if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART)
+ || !strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE))
+ s->RIF = s->val[OPT_HNEGATIVE].w;
+ else
+ s->RIF = !s->val[OPT_HNEGATIVE].w;
+ }
+
+ s->brightness = s->val[OPT_BRIGHTNESS].w;
+ s->contrast = s->val[OPT_CONTRAST].w;
+ s->threshold = s->val[OPT_THRESHOLD].w;
+ s->bpp = s->params.depth;
+
+ s->GRC = s->val[OPT_CUSTOM_GAMMA].w;
+ s->Mirror = s->val[OPT_MIRROR].w;
+ s->AE = s->val[OPT_AE].w;
+
+ s->HiliteG = s->val[OPT_HILITE_G].w;
+ s->ShadowG = s->val[OPT_SHADOW_G].w;
+ if (s->val[OPT_BIND_HILO].w)
+ {
+ s->HiliteR = s->val[OPT_HILITE_G].w;
+ s->ShadowR = s->val[OPT_SHADOW_G].w;
+ s->HiliteB = s->val[OPT_HILITE_G].w;
+ s->ShadowB = s->val[OPT_SHADOW_G].w;
+ }
+ else
+ {
+ s->HiliteR = s->val[OPT_HILITE_R].w;
+ s->ShadowR = s->val[OPT_SHADOW_R].w;
+ s->HiliteB = s->val[OPT_HILITE_B].w;
+ s->ShadowB = s->val[OPT_SHADOW_B].w;
+ }
+
+ if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ mode = 4;
+ s->image_composition = 0;
+ }
+ else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ mode = 4;
+ s->image_composition = 1;
+ }
+ else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ mode = 5;
+ s->image_composition = 2;
+ }
+ else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_COLOR)
+ || !strcmp (mode_str, SANE_I18N("Fine color")))
+ {
+ mode = 6;
+ s->image_composition = 5;
+ }
+ else if (!strcmp (mode_str, SANE_I18N("Raw")))
+ {
+ mode = 6;
+ s->image_composition = 5;
+ }
+ else
+ {
+ mode = 6;
+ s->image_composition = 5;
+ }
+
+ memset (wbuf, 0, sizeof (wbuf));
+ wbuf[7] = 64;
+ wbuf[10] = s->xres >> 8;
+ wbuf[11] = s->xres;
+ wbuf[12] = s->yres >> 8;
+ wbuf[13] = s->yres;
+ wbuf[14] = s->ulx >> 24;
+ wbuf[15] = s->ulx >> 16;
+ wbuf[16] = s->ulx >> 8;
+ wbuf[17] = s->ulx;
+ wbuf[18] = s->uly >> 24;
+ wbuf[19] = s->uly >> 16;
+ wbuf[20] = s->uly >> 8;
+ wbuf[21] = s->uly;
+ wbuf[22] = s->width >> 24;
+ wbuf[23] = s->width >> 16;
+ wbuf[24] = s->width >> 8;
+ wbuf[25] = s->width;
+ wbuf[26] = s->length >> 24;
+ wbuf[27] = s->length >> 16;
+ wbuf[28] = s->length >> 8;
+ wbuf[29] = s->length;
+ wbuf[30] = s->brightness;
+ wbuf[31] = s->threshold;
+ wbuf[32] = s->contrast;
+ wbuf[33] = s->image_composition;
+ wbuf[34] = (s->hw->info.model == FS2710) ? 12 : s->bpp;
+ wbuf[36] = 1;
+ wbuf[37] = (1 << 7) + 0x03;
+ wbuf[50] = (s->GRC << 3) | (s->Mirror << 2);
+#if 1
+ wbuf[50] |= s->AE; /* AE also for preview; needed by frontend controls */
+#else
+ if (!s->val[OPT_PREVIEW].w) wbuf[50] |= s->AE; /* AE not during preview */
+#endif
+ wbuf[54] = 2;
+ wbuf[57] = 1;
+ wbuf[58] = 1;
+ wbuf[59] = s->HiliteR;
+ wbuf[60] = s->ShadowR;
+ wbuf[62] = s->HiliteG;
+ wbuf[64] = s->ShadowG;
+ wbuf[70] = s->HiliteB;
+ wbuf[71] = s->ShadowB;
+
+ DBG (7, "RIF=%d, GRC=%d, Mirror=%d, AE=%d, Speed=%d\n", s->RIF, s->GRC,
+ s->Mirror, s->AE, s->scanning_speed);
+ DBG (7, "HR=%d, SR=%d, HG=%d, SG=%d, HB=%d, SB=%d\n", s->HiliteR,
+ s->ShadowR, s->HiliteG, s->ShadowG, s->HiliteB, s->ShadowB);
+
+ if (s->hw->info.model == FB620) /* modification for FB620S */
+ {
+ wbuf[36] = 0;
+ wbuf[37] = (s->RIF << 7) + 0x3;
+ wbuf[50] = s->GRC << 3;
+ wbuf[54] = 0;
+ wbuf[57] = 0;
+ wbuf[58] = 0;
+ }
+ else if (s->hw->info.model == FB1200) /* modification for FB1200S */
+ {
+#if 0
+ wbuf[34] = (((600 < s->val[OPT_X_RESOLUTION].w)
+ || (600 < s->val[OPT_Y_RESOLUTION].w))
+ && (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0))
+ ? 12 : s->bpp;
+#endif
+ wbuf[36] = 0;
+ wbuf[37] = (s->RIF << 7) + 0x3;
+ wbuf[50] = (1 << 4) | (s->GRC << 3);
+ wbuf[57] = 1;
+ wbuf[58] = 1;
+ }
+ else if (s->hw->info.model == IX4015) /* modification for IX-4015 */
+ {
+ wbuf[36] = 0;
+ wbuf[37] = (s->RIF << 7);
+ wbuf[57] = 0;
+ wbuf[58] = 0;
+ /* no highlight and shadow control */
+ wbuf[59] = 0;
+ wbuf[60] = 0;
+ wbuf[62] = 0;
+ wbuf[64] = 0;
+ wbuf[70] = 0;
+ wbuf[71] = 0;
+ }
+
+ buf_size = sizeof (wbuf);
+ status = set_window (s->fd, wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ if (s->hw->info.model == FS2710)
+ status = set_parameters_fs2710 (s);
+ buf_size = sizeof (wbuf);
+ memset (wbuf, 0, buf_size);
+ status = get_window (s->fd, wbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ DBG (5, "xres=%d\n", (wbuf[10] << 8) + wbuf[11]);
+ DBG (5, "yres=%d\n", (wbuf[12] << 8) + wbuf[13]);
+ DBG (5, "ulx=%d\n", (wbuf[14] << 24) + (wbuf[15] << 16) + (wbuf[16] << 8)
+ + wbuf[17]);
+ DBG (5, "uly=%d\n", (wbuf[18] << 24) + (wbuf[19] << 16) + (wbuf[20] << 8)
+ + wbuf[21]);
+ DBG (5, "width=%d\n", (wbuf[22] << 24) + (wbuf[23] << 16) + (wbuf[24] << 8)
+ + wbuf[25]);
+ DBG (5, "length=%d\n", (wbuf[26] << 24) + (wbuf[27] << 16) + (wbuf[28] << 8)
+ + wbuf[29]);
+ DBG (5, "Highlight Red=%d\n", wbuf[59]);
+ DBG (5, "Shadow Red=%d\n", wbuf[60]);
+ DBG (5, "Highlight (Green)=%d\n", wbuf[62]);
+ DBG (5, "Shadow (Green)=%d\n", wbuf[64]);
+ DBG (5, "Highlight Blue=%d\n", wbuf[70]);
+ DBG (5, "Shadow Blue=%d\n", wbuf[71]);
+
+ if (s->hw->tpu.Status == TPU_STAT_ACTIVE || s->hw->info.is_filmscanner)
+ {
+ DBG (3, "sane_start: sending DEFINE SCAN MODE for transparency unit, "
+ "NP=%d, Negative film type=%d\n", !s->RIF, s->negative_filmtype);
+ memset (wbuf, 0, sizeof (wbuf));
+ wbuf[0] = 0x02;
+ wbuf[1] = 6;
+ wbuf[2] = 0x80;
+ wbuf[3] = 0x05;
+ wbuf[4] = 39;
+ wbuf[5] = 16;
+ wbuf[6] = !s->RIF;
+ wbuf[7] = s->negative_filmtype;
+ status = define_scan_mode (s->fd, TRANSPARENCY_UNIT, wbuf);
+ /* note: If we implement a TPU for the FB1200S, we need
+ TRANSPARENCY_UNIT_FB1200 here. */
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "define scan mode failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ }
+
+ DBG (3, "sane_start: sending DEFINE SCAN MODE for scan control "
+ "conditions\n");
+ memset (wbuf, 0, sizeof (wbuf));
+ wbuf[0] = 0x20;
+ if (s->hw->info.model == FB1200)
+ {
+ wbuf[1] = 17;
+ wbuf[16] = 3;
+ wbuf[17] = 8;
+ wbuf[18] = (1 << 7) | (1 << 3);
+
+ DBG (3, "sane_start: sending DEFINE SCAN MODE for scan control "
+ "conditions of FB1200\n");
+ status = define_scan_mode (s->fd, SCAN_CONTROL_CON_FB1200, wbuf);
+ }
+ else
+ {
+ wbuf[1] = 14;
+ /* For preview use always normal speed: */
+ if (!s->val[OPT_PREVIEW].w && s->hw->info.is_filmscanner)
+ wbuf[11] = s->scanning_speed;
+ wbuf[15] = (s->hw->info.model == FB620
+ && !strcmp (mode_str, SANE_I18N("Fine color"))
+ && !s->val[OPT_PREVIEW].w) ? 1 << 3 : 0;
+ status = define_scan_mode (s->fd, SCAN_CONTROL_CONDITIONS, wbuf);
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "define scan mode failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (3, "sane_start: sending GET SCAN MODE for scan control conditions\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = sizeof (ebuf);
+ status = get_scan_mode (s->fd, SCAN_CONTROL_CONDITIONS, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: GET SCAN MODE for scan control conditions "
+ "failed\n");
+ sanei_scsi_close (s->fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ DBG (3, "scan mode byte[%d] = %d\n", (int) i, ebuf[i]);
+
+ /* Focus, but not for previews or negatives with speed control */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FOCUS_GROUP].cap)
+ && !s->val[OPT_PREVIEW].w && s->AF_NOW
+ && (s->RIF || s->AE || s->scanning_speed == 0))
+ {
+ if ((status = do_focus (s)) != SANE_STATUS_GOOD) return (status);
+ if (s->val[OPT_AF_ONCE].w) s->AF_NOW = SANE_FALSE;
+ }
+
+ /* ============= modification for FB620S ============= */
+ DBG (3, "TEST_UNIT_READY\n");
+ status = test_unit_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "test unit ready failed (%s)\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ if (s->hw->info.can_calibrate)
+ {
+ DBG (3, "sane_start: sending GET_CALIBRATION_STATUS\n");
+ buf_size = sizeof (cbuf);
+ memset (cbuf, 0, buf_size);
+ status = get_calibration_status (s->fd, cbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: GET_CALIBRATION_STATUS failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ DBG (1, "cbuf[0] = %d\n", cbuf[0]);
+ DBG (1, "cbuf[1] = %d\n", cbuf[1]);
+
+ cbuf[0] &= 3;
+ if (cbuf[0] == 1 || cbuf[0] == 2 || cbuf[0] == 3)
+ {
+ status = execute_calibration (s->fd);
+ DBG (3, "sane_start: EXECUTE_CALIBRATION\n");
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: EXECUTE_CALIBRATION failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ DBG (3, "after calibration: GET_CALIBRATION_STATUS\n");
+ buf_size = sizeof (cbuf);
+ memset (cbuf, 0, buf_size);
+ status = get_calibration_status (s->fd, cbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "after calibration: GET_CALIBRATION_STATUS failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (1, "cbuf[0] = %d\n", cbuf[0]);
+ DBG (1, "cbuf[1] = %d\n", cbuf[1]);
+ }
+ }
+
+ status = scan (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ buf_size = sizeof (dbuf);
+ memset (dbuf, 0, buf_size);
+ status = get_data_status (s->fd, dbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ DBG (5, ">> GET DATA STATUS\n");
+ DBG (5, "Scan Data Available=%d\n", (dbuf[9] << 16) + (dbuf[10] << 8)
+ + dbuf[11]);
+ DBG (5, "Magnified Width=%d\n", (dbuf[12] <<24) + (dbuf[13] << 16)
+ + (dbuf[14] << 8) + dbuf[15]);
+ DBG (5, "Magnified Length=%d\n", (dbuf[16] << 24) + (dbuf[17] << 16)
+ + (dbuf[18] << 8) + dbuf[19]);
+ DBG (5, "Rest Data=%d bytes\n", (dbuf[20] << 24) + (dbuf[21] << 16)
+ + (dbuf[22] << 8) + dbuf[23]);
+ DBG (5, "Filled Data Buffer=%d\n", (dbuf[24] << 24) + (dbuf[25] << 16)
+ + (dbuf[26] << 8) + dbuf[27]);
+ DBG (5, "<< GET DATA STATUS\n");
+
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+ if (s->hw->info.model == FB1200)
+ {
+ if (s->bytes_to_read != (((size_t) dbuf[9] << 16)
+ + ((size_t) dbuf[10] << 8) + (size_t) dbuf[11]))
+ {
+ s->params.bytes_per_line = (((size_t) dbuf[12] << 24)
+ + ((size_t) dbuf[13] << 16) + ((size_t) dbuf[14] << 8)
+ + (size_t)dbuf[15]);
+ s->params.lines = (((size_t) dbuf[16] << 24)
+ + ((size_t) dbuf[17] << 16) + ((size_t) dbuf[18] << 8)
+ + (size_t) dbuf[19]);
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+ mode_str = s->val[OPT_MODE].s;
+ if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ if (((600 < s->val[OPT_X_RESOLUTION].w)
+ || (600 < s->val[OPT_Y_RESOLUTION].w)))
+ {
+ s->params.bytes_per_line *= 2;
+ s->params.lines /= 2;
+ }
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ }
+ else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY))
+ s->params.pixels_per_line = s->params.bytes_per_line;
+ else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_COLOR)
+ || !strcmp (mode_str, SANE_I18N("Fine color")))
+ s->params.pixels_per_line = s->params.bytes_per_line / 3;
+ else
+ s->params.pixels_per_line = s->params.bytes_per_line / 6;
+ }
+ }
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read,
+ s->val[OPT_X_RESOLUTION].w);
+
+/**************************************************/
+/* modification for FB620S and FB1200S */
+ s->buf_used = 0;
+ s->buf_pos = 0;
+/**************************************************/
+
+ s->scanning = SANE_TRUE;
+
+ DBG (1, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+
+static SANE_Status
+sane_read_direct (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len,
+ SANE_Int *len)
+{
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+
+ DBG (21, ">> sane_read\n");
+
+ *len = 0;
+ nread = max_len;
+
+ DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread,
+ (int) s->bytes_to_read);
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning) return (do_cancel (s));
+
+ if (nread > s->bytes_to_read) nread = s->bytes_to_read;
+ status = read_data (s->fd, buf, &nread);
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+
+ DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread,
+ (int) s->bytes_to_read);
+ DBG (21, "<< sane_read\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+
+static SANE_Status
+read_fs2710 (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len,
+ SANE_Int *len)
+{
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ int c;
+ size_t i, nread, nread2;
+ u_char *p;
+#if defined(WORDS_BIGENDIAN)
+ u_char b;
+#endif
+
+ DBG (21, ">> sane_read\n");
+
+ *len = 0;
+ nread = max_len;
+
+ DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread,
+ (int) s->bytes_to_read);
+
+ if (nread > s->bytes_to_read) nread = s->bytes_to_read;
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning) return (do_cancel (s));
+
+ /* We must receive 2 little-endian bytes per pixel and colour.
+ In raw mode we must swap the bytes if we are running a big-endian
+ architecture (SANE standard 3.2.1), and pass them both.
+ Otherwise the other subroutines expect only 1 byte, so we must
+ set up an intermediate buffer which is twice as large
+ as buf, and then map this buffer to buf. */
+
+ if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR))
+ {
+ if (max_len > s->auxbuf_len)
+ { /* extend buffer? */
+ if (s->auxbuf_len > 0) free (s->auxbuf);
+ s->auxbuf_len = max_len;
+ if ((s->auxbuf = (u_char *) malloc (2 * max_len)) == NULL)
+ {
+ DBG (1, "sane_read buffer size insufficient\n");
+ do_cancel (s);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ nread2 = 2 * nread;
+ if ((status = read_data (s->fd, s->auxbuf, &nread2)) != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ nread = nread2 / 2;
+ for (i = 0, p = s->auxbuf; i < nread; i++)
+ {
+ c = *p++ >> 4;
+ c |= *p++ << 4;
+ *buf++ = s->gamma_map[s->colour++][c];
+ if (s->colour > 3) s->colour = 1; /* cycle through RGB */
+ }
+ }
+ else
+ {
+ if ((status = read_data (s->fd, buf, &nread)) != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+#if defined(WORDS_BIGENDIAN)
+ for (p = buf; p < buf + nread; p++)
+ {
+ b = *p;
+ *p = *(p + 1);
+ p++;
+ *p = b;
+ }
+#endif
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+
+ DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread,
+ (int) s->bytes_to_read);
+ DBG (21, "<< sane_read\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+/* modification for FB620S */
+
+static SANE_Status
+read_fb620 (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len,
+ SANE_Int *len)
+{
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Byte *out, *red, *green, *blue;
+ SANE_Int ncopy;
+ size_t nread = 0, i, pixel_per_line;
+
+ DBG (21, ">> read_fb620\n");
+
+ *len = 0;
+
+ DBG (21, " read_fb620: nread=%d, bytes_to_read=%d\n", (int) nread,
+ (int) s->bytes_to_read);
+
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ {
+ s->reset_flag = 0; /* no reset */
+
+ do_cancel (s);
+ DBG (21, "do_cancel(EOF)\n");
+ DBG (21, "reset_flag = %d\n", s->reset_flag);
+ return (SANE_STATUS_EOF);
+ }
+ else
+ {
+ s->reset_flag = 1; /* do reset */
+ DBG (21, "reset_flag = %d\n", s->reset_flag);
+ }
+ DBG (21, " read_fb620: buf_pos=%d, buf_used=%d\n", s->buf_pos,
+ s->buf_used);
+
+ if (!s->scanning)
+ return (do_cancel (s));
+
+ if (s->buf_pos < s->buf_used)
+ {
+ ncopy = s->buf_used - s->buf_pos;
+ if (ncopy > max_len)
+ ncopy = max_len;
+ memcpy (buf, &(s->outbuffer[s->buf_pos]), ncopy);
+
+ max_len -= ncopy;
+ *len += ncopy;
+ buf = &(buf[ncopy]);
+ s->buf_pos += ncopy;
+ }
+
+ if (s->buf_pos >= s->buf_used && s->bytes_to_read)
+ {
+ /* buffer is empty: read in scan line and sort color data as shown
+ above */
+
+ nread = s->params.bytes_per_line;
+
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+
+ status = read_data (s->fd, s->inbuffer, &nread);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ s->buf_used = s->params.bytes_per_line;
+
+ out = s->outbuffer;
+ pixel_per_line = s->params.pixels_per_line;
+
+ red = s->inbuffer;
+ green = &(s->inbuffer[pixel_per_line]);
+ blue = &(s->inbuffer[2 * pixel_per_line]);
+
+ for (i = 0; i < pixel_per_line; i++)
+ {
+ *out++ = *red++;
+ *out++ = *green++;
+ *out++ = *blue++;
+ }
+
+ s->buf_pos = 0;
+
+ s->bytes_to_read -= s->buf_used;
+
+ }
+
+ if (max_len && s->buf_pos < s->buf_used)
+ {
+ ncopy = s->buf_used - s->buf_pos;
+ if (ncopy > max_len)
+ ncopy = max_len;
+ memcpy (buf, &(s->outbuffer[s->buf_pos]), ncopy);
+ *len += ncopy;
+ s->buf_pos += ncopy;
+ }
+
+ DBG (21, "<< read_fb620\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+/* modification for FB1200S */
+
+static SANE_Status
+read_fb1200 (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len,
+SANE_Int *len)
+{
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Byte *firstimage, *secondimage/*, inmask, outmask, outbyte,
+ primaryHigh[256], primaryLow[256], secondaryHigh[256],
+ secondaryLow[256] */;
+ SANE_Int ncopy;
+ u_char dbuf[28];
+ size_t buf_size, nread, remain, nwritten, nremain, pos, pix, pixel_per_line,
+ byte, byte_per_line/*, bit*/;
+ ssize_t wres, readres;
+ int maxpix;
+
+ DBG (21, ">> read_fb1200\n");
+
+ buf_size = sizeof (dbuf);
+ memset (dbuf, 0, buf_size);
+ status = get_data_status (s->fd, dbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ DBG (5, ">> GET DATA STATUS\n");
+ DBG (5, "Scan Data Available=%d\n", (dbuf[9] << 16) + (dbuf[10] << 8)
+ + dbuf[11]);
+ DBG (5, "Rest Data=%d bytes\n", (dbuf[20] << 24) + (dbuf[21] << 16)
+ + (dbuf[22] << 8) + dbuf[23]);
+ DBG (5, "Filled Data Buffer=%d\n", (dbuf[24] << 24) + (dbuf[25] << 16)
+ + (dbuf[26] << 8) + dbuf[27]);
+ DBG (5, "temp file position:%u\n", (unsigned int) lseek(s->tmpfile,
+ 0, SEEK_CUR));
+ DBG (5, "<< GET DATA STATUS\n");
+
+ *len = 0;
+
+ DBG (21, " read_fb1200: bytes_to_read=%d\n",
+ (int) s->bytes_to_read);
+
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ {
+ do_cancel (s);
+ DBG (21, "do_cancel(EOF)\n");
+ return (SANE_STATUS_EOF);
+ }
+
+ DBG (21, " read_fb1200: buf_pos=%d, buf_used=%d\n", s->buf_pos,
+ s->buf_used);
+
+ if (!s->scanning)
+ return (do_cancel (s));
+
+ if (s->buf_pos >= s->buf_used && s->bytes_to_read)
+ {
+ nread = s->params.bytes_per_line / 2;
+
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+
+ status = read_data (s->fd, s->inbuffer, &nread);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ /**** save the primary scan data to tmpfile ****/
+
+ if ((SANE_Int) s->bytes_to_read > s->params.bytes_per_line
+ * s->params.lines / 2)
+ {
+ remain = nread;
+ nwritten = 0;
+ while (remain)
+ {
+ errno = 0;
+ wres = write (s->tmpfile, &s->inbuffer[nwritten], remain);
+ if (wres == -1)
+ {
+ DBG(1, "error write tmp file: %i, %s\n", errno,
+ strerror(errno));
+ do_cancel(s);
+ return (SANE_STATUS_NO_MEM);
+ }
+ remain -= wres;
+ nwritten += wres;
+ }
+
+ s->bytes_to_read -= nread;
+
+ if ((SANE_Int) s->bytes_to_read <= s->params.bytes_per_line
+ * s->params.lines / 2)
+ {
+ if ((SANE_Int) s->bytes_to_read < s->params.bytes_per_line
+ * s->params.lines / 2)
+ DBG(1, "warning: read more data for the primary scan "
+ "than expected\n");
+
+ lseek (s->tmpfile, 0L, SEEK_SET);
+ *len = 0;
+ *buf = 0;
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG(1, "writing: the primary data to tmp file\n");
+ *len = 0;
+ *buf = 0;
+ return (SANE_STATUS_GOOD);
+ }
+ /** the primary scan data from tmpfile and the secondary scan data
+ are merged **/
+
+ s->buf_used = s->params.bytes_per_line;
+ byte_per_line = s->params.bytes_per_line;
+ pixel_per_line = s->params.pixels_per_line;
+
+
+ /** read an entire scan line from the primary scan **/
+
+ remain = nread;
+ pos = 0;
+ firstimage = &(s->inbuffer[byte_per_line/2]);
+
+ while (remain > 0)
+ {
+ nremain = (remain < SSIZE_MAX)? remain: SSIZE_MAX;
+ errno = 0;
+ readres = read (s->tmpfile, &(firstimage[pos]), nremain);
+ if (readres == -1)
+ {
+ DBG(1, "error reading tmp file: %i %s\n", errno,
+ strerror(errno));
+ do_cancel(s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ if (readres == 0)
+ {
+ DBG(1, "0 byte read from temp file. premature EOF?\n");
+ return (SANE_STATUS_INVAL);
+ /* perhaps an error return? */
+ }
+ DBG(1, "reading: the primary data from tmp file\n");
+ remain -= readres;
+ pos += readres;
+ }
+
+ secondimage = s->inbuffer;
+
+ if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR))
+ {
+ maxpix = pixel_per_line / 2;
+ for (pix = 0; (int) pix < maxpix; pix++)
+ {
+ s->outbuffer[6 * pix] = secondimage[3 * pix];
+ s->outbuffer[6 * pix + 1] = secondimage[3 * pix + 1];
+ s->outbuffer[6 * pix + 2] = secondimage[3 * pix + 2];
+ s->outbuffer[6 * pix + 3] = firstimage[3 * pix];
+ s->outbuffer[6 * pix + 4] = firstimage[3 * pix + 1];
+ s->outbuffer[6 * pix + 5] = firstimage[3 * pix + 2];
+ }
+ }
+ else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ for (pix = 0; pix < pixel_per_line / 2; pix++)
+ {
+ s->outbuffer[2 * pix] = secondimage[pix];
+ s->outbuffer[2 * pix + 1] = firstimage[pix];
+ }
+ }
+ else /* for lineart mode */
+ {
+ maxpix = byte_per_line / 2;
+ for (byte = 0; (int) byte < maxpix; byte++)
+ {
+ s->outbuffer[2 * byte] = primaryHigh[firstimage[byte]]
+ | secondaryHigh[secondimage[byte]];
+ s->outbuffer[2 * byte + 1] = primaryLow[firstimage[byte]]
+ | secondaryLow[secondimage[byte]];
+#if 0
+ inmask = 128;
+ outmask = 128;
+ outbyte = 0;
+ for (bit = 0; bit < 4; bit++)
+ {
+ if (inmask == (secondimage[byte] & inmask))
+ outbyte = outbyte | outmask;
+ outmask = outmask >> 1;
+ if (inmask == (firstimage[byte] & inmask))
+ outbyte = outbyte | outmask;
+ outmask = outmask >> 1;
+ inmask = inmask >> 1;
+ }
+ s->outbuffer[2 * byte] = outbyte;
+
+ outmask = 128;
+ outbyte = 0;
+ for (bit = 0; bit < 4; bit++)
+ {
+ if (inmask == (secondimage[byte] & inmask))
+ outbyte = outbyte | outmask;
+ outmask = outmask >> 1;
+ if (inmask == (firstimage[byte] & inmask))
+ outbyte = outbyte | outmask;
+ outmask = outmask >> 1;
+ inmask = inmask >> 1;
+ }
+ s->outbuffer[2 * byte + 1] = outbyte;
+#endif
+ }
+ }
+
+ s->buf_pos = 0;
+ s->bytes_to_read -= nread;
+ }
+
+ if (max_len && s->buf_pos < s->buf_used)
+ {
+ ncopy = s->buf_used - s->buf_pos;
+ if (ncopy > max_len)
+ ncopy = max_len;
+ memcpy (buf, &(s->outbuffer[s->buf_pos]), ncopy * 2);
+ *len += ncopy;
+ s->buf_pos += ncopy;
+ }
+
+ DBG (21, "<< read_fb1200\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len,
+ SANE_Int *len)
+{
+ CANON_Scanner *s = handle;
+ SANE_Status status;
+ if (s->hw->info.model == FB620 && s->params.format == SANE_FRAME_RGB)
+ status = read_fb620 (handle, buf, max_len, len);
+ else if (s->hw->info.model == FS2710)
+ status = read_fs2710 (handle, buf, max_len, len);
+ else if (s->hw->info.model == FB1200 && ((600 < s->val[OPT_X_RESOLUTION].w)
+ || (600 < s->val[OPT_Y_RESOLUTION].w)))
+ status = read_fb1200 (handle, buf, max_len, len);
+ else
+ status = sane_read_direct (handle, buf, max_len, len);
+ if (s->time0 == -1)
+ s->time0 = 0;
+ else
+ time (&(s->time0));
+
+ DBG (11, "sane_read: time0 = %ld\n", s->time0);
+ s->switch_preview = s->val[OPT_PREVIEW].w;
+ return (status);
+}
+
+/**************************************************************************/
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ CANON_Scanner *s = handle;
+ DBG (1, ">> sane_cancel\n");
+
+/******** for FB1200S ************/
+ if(s->hw->info.model == FB1200)
+ {
+ if (s->tmpfile != -1)
+ {
+ close (s->tmpfile);
+ DBG(1, " ****** tmpfile is closed ****** \n");
+ }
+ else
+ {
+ DBG(1, "tmpfile is failed\n");
+/* return (SANE_STATUS_INVAL);*/
+ }
+ }
+/*********************************/
+
+ s->scanning = SANE_FALSE;
+ DBG (1, "<< sane_cancel\n");
+}
+
+/**************************************************************************/
+
+SANE_Status
+sane_set_io_mode (SANE_Handle UNUSEDARG handle,
+SANE_Bool UNUSEDARG non_blocking)
+{
+ DBG (1, ">> sane_set_io_mode\n");
+ DBG (1, "<< sane_set_io_mode\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**************************************************************************/
+
+SANE_Status
+sane_get_select_fd (SANE_Handle UNUSEDARG handle,
+SANE_Int UNUSEDARG * fd)
+{
+ DBG (1, ">> sane_get_select_fd\n");
+ DBG (1, "<< sane_get_select_fd\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**************************************************************************/
diff --git a/backend/canon-scsi.c b/backend/canon-scsi.c
new file mode 100644
index 0000000..423a07e
--- /dev/null
+++ b/backend/canon-scsi.c
@@ -0,0 +1,733 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 BYTEC GmbH Germany
+ Written by Helmut Koeberle, Email: helmut.koeberle@bytec.de
+ Modified by Manuel Panea <Manuel.Panea@rzg.mpg.de>
+ and Markus Mertinat <Markus.Mertinat@Physik.Uni-Augsburg.DE>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/* This file implements the low-level scsi-commands. */
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> test_unit_ready\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< test_unit_ready\n");
+ return (status);
+}
+
+#ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS
+static SANE_Status
+request_sense (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> request_sense\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x03;
+ cmd[4] = 14;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< request_sense\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+inquiry (int fd, int evpd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> inquiry\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x12;
+ cmd[1] = evpd;
+ cmd[2] = evpd ? 0xf0 : 0;
+ cmd[4] = evpd ? 74 : 36;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< inquiry\n");
+ return (status);
+}
+
+#ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS
+static SANE_Status
+mode_select (int fd)
+{
+ static u_char cmd[6 + 12];
+ int status;
+ DBG (31, ">> mode_select\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x15;
+ cmd[1] = 16;
+ cmd[4] = 12;
+ cmd[6 + 4] = 3;
+ cmd[6 + 5] = 6;
+ cmd[6 + 8] = 0x02;
+ cmd[6 + 9] = 0x58;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), NULL, NULL);
+
+ DBG (31, "<< mode_select\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+reserve_unit (int fd)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> reserve_unit\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x16;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< reserve_unit\n");
+ return (status);
+}
+
+#ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS
+static SANE_Status
+release_unit (int fd)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> release_unit\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x17;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< release_unit\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+mode_sense (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> mode_sense\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x1a;
+ cmd[2] = 3;
+ cmd[4] = 12;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< mode_sense\n");
+ return (status);
+}
+
+static SANE_Status
+scan (int fd)
+{
+ static u_char cmd[6 + 1];
+ int status;
+ DBG (31, ">> scan\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x1b;
+ cmd[4] = 1;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), NULL, NULL);
+
+ DBG (31, "<< scan\n");
+ return (status);
+}
+
+static SANE_Status
+send_diagnostic (int fd)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> send_diagnostic\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x1d;
+ cmd[1] = 4;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< send_diagnostic\n");
+ return (status);
+}
+
+static SANE_Status
+set_window (int fd, void *data)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> set_window\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x24;
+ cmd[8] = 72;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), data, 72, NULL, NULL);
+
+ DBG (31, "<< set_window\n");
+ return (status);
+}
+
+static SANE_Status
+get_window (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> get_window\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x25;
+ cmd[1] = 1;
+ cmd[8] = 72;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get_window\n");
+ return (status);
+}
+
+static SANE_Status
+read_data (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> read_data\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x28;
+ cmd[6] = *buf_size >> 16;
+ cmd[7] = *buf_size >> 8;
+ cmd[8] = *buf_size;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< read_data\n");
+ return (status);
+}
+
+static SANE_Status
+medium_position (int fd)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> medium_position\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x31;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< medium_position\n");
+ return (status);
+}
+
+#ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS
+static SANE_Status
+execute_shading (int fd)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> execute shading\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xe2;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< execute shading\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+execute_auto_focus (int fd, int AF, int speed, int AE, int count)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (7, ">> execute_auto_focus\n");
+ DBG (7, ">> focus: mode='%d', speed='%d', AE='%d', count='%d'\n",
+ AF, speed, AE, count);
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xe0;
+ cmd[1] = (u_char) AF;
+ cmd[2] = (u_char) ((speed << 7) | AE);
+#if 1
+ cmd[4] = (u_char) count; /* seems to work, but may be unsafe */
+#else /* The Canon software uses this: */
+ cmd[4] = (u_char) (28 * ((int) (count / 28.5)) + 16);
+#endif
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (7, "<< execute_auto_focus\n");
+ return (status);
+}
+
+static SANE_Status
+set_adf_mode (int fd, u_char priority)
+{
+ static u_char cmd[6];
+ int status;
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xd4;
+ cmd[4] = 0x01;
+
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), &priority, 1, NULL, NULL);
+
+ return (status);
+}
+
+static SANE_Status
+get_scan_mode (int fd, u_char page, void *buf, size_t *buf_size)
+{
+ static u_char cmd[6];
+ int status;
+ int PageLen = 0x00;
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xd5;
+ cmd[2] = page;
+
+ switch (page)
+ {
+ case AUTO_DOC_FEEDER_UNIT:
+ case TRANSPARENCY_UNIT:
+ cmd[4] = 0x0c + PageLen;
+ break;
+
+ case SCAN_CONTROL_CONDITIONS:
+ cmd[4] = 0x14 + PageLen;
+ break;
+
+ case SCAN_CONTROL_CON_FB1200:
+ cmd[2] = 0x20;
+ cmd[4] = 0x17 + PageLen;
+ break;
+
+ default:
+ cmd[4] = 0x24 + PageLen;
+ break;
+ }
+
+ DBG (31, "get scan mode: cmd[4]='0x%0X'\n", cmd[4]);
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get scan mode\n");
+ return (status);
+}
+
+static SANE_Status
+define_scan_mode (int fd, u_char page, void *data)
+{
+ static u_char cmd[6];
+ u_char pdata[36];
+ size_t i;
+ int status, pdatalen;
+ DBG (31, ">> define scan mode\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ memset (pdata, 0, sizeof (pdata));
+ cmd[0] = 0xd6;
+ cmd[1] = 0x10;
+ cmd[4] = (page == TRANSPARENCY_UNIT) ? 0x0c
+ : (page == TRANSPARENCY_UNIT_FB1200) ? 0x0c
+ : (page == SCAN_CONTROL_CONDITIONS) ? 0x14
+ : (page == SCAN_CONTROL_CON_FB1200) ? 0x17 : 0x24;
+
+ memcpy (pdata + 4, data, (page == TRANSPARENCY_UNIT) ? 8
+ : (page == TRANSPARENCY_UNIT_FB1200) ? 10
+ : (page == SCAN_CONTROL_CONDITIONS) ? 16
+ : (page == SCAN_CONTROL_CON_FB1200) ? 19 : 32);
+
+ for (i = 0; i < sizeof (cmd); i++)
+ DBG (31, "define scan mode: cmd[%d]='0x%0X'\n", (int) i,
+ cmd[i]);
+
+ for (i = 0; i < sizeof (pdata); i++)
+ DBG (31, "define scan mode: pdata[%d]='0x%0X'\n", (int) i,
+ pdata[i]);
+
+ pdatalen = (page == TRANSPARENCY_UNIT) ? 12
+ : (page == TRANSPARENCY_UNIT_FB1200) ? 14
+ : (page == SCAN_CONTROL_CONDITIONS) ? 20
+ : (page == SCAN_CONTROL_CON_FB1200) ? 23 : 36;
+
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), pdata, pdatalen, NULL,
+ NULL);
+ DBG (31, "<< define scan mode\n");
+ return (status);
+}
+
+static SANE_Status
+get_density_curve (int fd, int component, void *buf, size_t *buf_size,
+ int transfer_data_type)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> get_density_curve\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x28;
+ cmd[2] = transfer_data_type;
+ cmd[4] = component;
+ cmd[5] = 0;
+ cmd[6] = ((*buf_size) >> 16) & 0xff;
+ cmd[7] = ((*buf_size) >> 8) & 0xff;
+ cmd[8] = (*buf_size) & 0xff;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get_density_curve\n");
+ return (status);
+}
+
+#ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS
+static SANE_Status
+get_density_curve_data_format (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> get_density_curve_data_format\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x28;
+ cmd[2] = 0x03;
+ cmd[4] = 0xff;
+ cmd[5] = 0;
+ cmd[6] = 0;
+ cmd[7] = 0;
+ cmd[8] = 14;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get_density_curve_data_format\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+set_density_curve (int fd, int component, void *buf, size_t *buf_size,
+ int transfer_data_type)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> set_density_curve\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x2a;
+ cmd[2] = transfer_data_type;
+ cmd[4] = component;
+ cmd[5] = 0;
+ cmd[6] = ((*buf_size) >> 16) & 0xff;
+ cmd[7] = ((*buf_size) >> 8) & 0xff;
+ cmd[8] = (*buf_size) & 0xff;
+
+ status =
+ sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), buf, *buf_size, NULL, NULL);
+
+ DBG (31, "<< set_density_curve\n");
+ return (status);
+}
+
+
+/* static SANE_Status */
+/* set_density_curve_data_format (int fd, void *buf, size_t *buf_size) */
+/* { */
+/* static u_char cmd[10]; */
+/* int status, i; */
+/* DBG (31, ">> set_density_curve_data_format\n"); */
+
+/* memset (cmd, 0, sizeof (cmd)); */
+/* cmd[0] = 0x2a; */
+/* cmd[2] = 0x03; */
+/* cmd[4] = 0xff; */
+/* cmd[5] = 0; */
+/* cmd[6] = 0; */
+/* cmd[7] = 0; */
+/* cmd[8] = 14; */
+/* status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); */
+
+/* DBG (31, "<< set_density_curve_data_format\n"); */
+/* return (status); */
+/* } */
+
+#ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS
+static SANE_Status
+get_power_on_timer (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> get power on timer\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xe3;
+ cmd[6] = 1;
+ cmd[7] = 0;
+ cmd[8] = 0;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get power on timer\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+get_film_status (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> get film status\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xe1;
+ cmd[6] = 0;
+ cmd[7] = 0;
+ cmd[8] = 4;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get film status\n");
+ return (status);
+}
+
+static SANE_Status
+get_data_status (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> get_data_status\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x34;
+ cmd[8] = 28;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get_data_status\n");
+ return (status);
+}
+
+/*************** modification for FB620S ***************/
+static SANE_Status
+reset_scanner (int fd)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> reset_scanner\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xc1;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< reset_scanner \n");
+ return (status);
+}
+
+static SANE_Status
+execute_calibration (int fd)
+{
+ static u_char cmd[6];
+ u_char data[2];
+ int status;
+ DBG (31, ">> execute_calibration\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ memset (data, 0, sizeof (data));
+ cmd[0] = 0xc2;
+ cmd[4] = 2;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), data, sizeof (data),
+ NULL, NULL);
+
+ DBG (31, "<< execute_calibration\n");
+ return (status);
+}
+
+static SANE_Status
+get_calibration_status (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> get_calibration_status\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xc3;
+ cmd[4] = *buf_size;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get_calibration_status\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+get_switch_status (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[6];
+ int status;
+ DBG (31, ">> get_switch_status\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xc4;
+ cmd[4] = 2;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size);
+
+ DBG (31, "<< get_switch_status\n");
+ return (status);
+}
+
+static SANE_Status
+wait_ready(int fd)
+{
+ SANE_Status status;
+ int retry = 0;
+
+ while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD)
+ {
+ DBG(5, "wait_ready failed (%d)\n", retry);
+ if (retry++ > 15)
+ return SANE_STATUS_IO_ERROR;
+ sleep(3);
+ }
+ return(status);
+}
+#endif
+
+/*************** modification for FB1200S ***************/
+static SANE_Status
+cancel (int fd)
+{
+ static u_char cmd[10];
+ int status;
+ DBG (31, ">> cancel_FB1200S\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0xe4;
+ status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL);
+
+ DBG (31, "<< cancel_FB1200S \n");
+ return (status);
+}
+
+/**************************************************************************/
+/* As long as we do not know how this scanner stores its density curves,
+ we do the gamma correction with a 8 <--> 12 bit translation table
+ stored in the CANON_Scanner structure. */
+
+static SANE_Status
+get_density_curve_fs2710 (SANE_Handle handle, int component, u_char *buf,
+ size_t *buf_size)
+{
+ CANON_Scanner *s = handle;
+ int i;
+
+ for (i = 0; i < 256; i++)
+ *buf++ = s->gamma_map[component][i << 4];
+ *buf_size = 256;
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+set_density_curve_fs2710 (SANE_Handle handle, int component, u_char *buf)
+{
+ CANON_Scanner *s = handle;
+ int i, j, hi, lo;
+ u_char *p;
+
+ for (i = 1, hi = *buf++, p = &s->gamma_map[component][0]; i <= 256; i++)
+ {
+ lo = hi;
+ hi = (i < 256) ? *buf++ : 2 * *(buf - 1) - *(buf - 2);
+ if (hi > 255)
+ hi = 255;
+ for (j = 0; j < 16; j++) /* do a linear interpolation */
+ *p++ = (u_char) (lo + ((double) ((hi - lo) * j)) / 16.0 + 0.5);
+ }
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+set_parameters_fs2710 (SANE_Handle handle)
+{
+ CANON_Scanner *s = handle;
+ int i, j, invert, shadow[4], hilite[4];
+ double x, b, c;
+
+ shadow[1] = s->ShadowR << 4;
+ shadow[2] = s->ShadowG << 4;
+ shadow[3] = s->ShadowB << 4;
+ hilite[1] = s->HiliteR << 4;
+ hilite[2] = s->HiliteG << 4;
+ hilite[3] = s->HiliteB << 4;
+ c = ((double) s->contrast) / 128.0;
+ b = ((double) (s->brightness - 128)) / 128.0;
+
+ invert = strcmp (filmtype_list[1], s->val[OPT_NEGATIVE].s);
+
+ for (i = 1; i < 4; i++)
+ {
+ for (j = 0; j < 4096; j++)
+ {
+ if (j <= shadow[i])
+ s->gamma_map[i][j] = (u_char) ((s->brightness >= 128) ?
+ 2 * s->brightness - 256 : 0);
+ else if (j < hilite[i])
+ {
+ x = ((double) (j - shadow[i]))
+ / ((double) (hilite[i] - shadow[i]));
+ /* first do the contrast correction */
+ x = (x <= 0.5) ? 0.5 * pow (2 * x, c)
+ : 1.0 - 0.5 * pow (2 * (1.0 - x), c);
+ x = pow (x, 0.5); /* default gamma correction */
+ x += b; /* brightness correction */
+ s->gamma_map[i][j] = (u_char) MAX (0, MIN (255,
+ (int) (255.0 * x)));
+ }
+ else
+ s->gamma_map[i][j] = (u_char) ((s->brightness >= 128) ?
+ 255 : 2 * s->brightness);
+ }
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
diff --git a/backend/canon.c b/backend/canon.c
new file mode 100644
index 0000000..168b2d6
--- /dev/null
+++ b/backend/canon.c
@@ -0,0 +1,1881 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 BYTEC GmbH Germany
+ Written by Helmut Koeberle, Email: helmut.koeberle@bytec.de
+ Modified by Manuel Panea <Manuel.Panea@rzg.mpg.de>
+ and Markus Mertinat <Markus.Mertinat@Physik.Uni-Augsburg.DE>
+ FB620 and FB1200 support by Mitsuru Okaniwa <m-okaniwa@bea.hi-ho.ne.jp>
+ FS2710 support by Ulrich Deiters <ulrich.deiters@uni-koeln.de>
+
+ backend version: 1.13e
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/* This file implements the sane-api */
+
+/* SANE-FLOW-DIAGRAMM
+
+ - sane_init() : initialize backend, attach scanners(devicename,0)
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev)
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . . - sane_cancel() : cancel operation, kill reader_process
+
+ . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
+ - sane_exit() : terminate use of backend, free devicename and device-struture
+*/
+
+/* This driver's flow:
+
+ - sane_init
+ . - attach_one
+ . . - inquiry
+ . . - test_unit_ready
+ . . - medium_position
+ . . - extended inquiry
+ . . - mode sense
+ . . - get_density_curve
+ - sane_get_devices
+ - sane_open
+ . - init_options
+ - sane_set_io_mode : set blocking-mode
+ - sane_get_select_fd : get scanner-fd
+ - sane_get_option_descriptor() : get option informations
+ - sane_control_option() : change option values
+ - sane_start() : start image aquisition
+ - sane_get_parameters() : returns actual scan-parameters
+ - sane_read() : read image-data (from pipe)
+ - sane_cancel() : cancel operation, kill reader_process
+ - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
+ - sane_exit() : terminate use of backend, free devicename and device-struture
+*/
+
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <fcntl.h> /* for FB1200S */
+#include <unistd.h> /* for FB1200S */
+#include <errno.h> /* for FB1200S */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+#define BACKEND_NAME canon
+
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define CANON_CONFIG_FILE "canon.conf"
+
+#include <canon.h>
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+
+static SANE_Byte primaryHigh[256], primaryLow[256], secondaryHigh[256],
+ secondaryLow[256]; /* modification for FB1200S */
+
+static int num_devices = 0;
+static CANON_Device *first_dev = NULL;
+static CANON_Scanner *first_handle = NULL;
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+/* modification for FS2710 */
+static const SANE_String_Const mode_list_fs2710[] = {
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_I18N("Raw"), 0
+};
+
+/* modification for FB620S */
+static const SANE_String_Const mode_list_fb620[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_I18N("Fine color"), 0
+};
+
+/* modification for FB1200S */
+static const SANE_String_Const mode_list_fb1200[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static const SANE_String_Const tpu_dc_mode_list[] = {
+ SANE_I18N("No transparency correction"),
+ SANE_I18N("Correction according to film type"),
+ SANE_I18N("Correction according to transparency ratio"),
+ 0
+};
+
+static const SANE_String_Const filmtype_list[] = {
+ SANE_I18N("Negatives"), SANE_I18N("Slides"),
+ 0
+};
+
+static const SANE_String_Const negative_filmtype_list[] = {
+ "Kodak", "Fuji", "Agfa", "Konica",
+ 0
+};
+
+static const SANE_String_Const scanning_speed_list[] = {
+ SANE_I18N("Automatic"), SANE_I18N("Normal speed"),
+ SANE_I18N("1/2 normal speed"), SANE_I18N("1/3 normal speed"),
+ 0
+};
+
+static const SANE_String_Const tpu_filmtype_list[] = {
+ "Film 0", "Film 1", "Film 2", "Film 3",
+ 0
+};
+
+static const SANE_String_Const papersize_list[] = {
+ "A4", "Letter", "B5", "Maximal",
+ 0
+};
+
+/**************************************************/
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+#include "canon-scsi.c"
+
+/**************************************************************************/
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (11, ">> max_string_size\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (11, "<< max_string_size\n");
+ return max_size;
+}
+
+/**************************************************************************/
+
+static void
+get_tpu_stat (int fd, CANON_Device * dev)
+{
+ unsigned char tbuf[12 + 5];
+ size_t buf_size, i;
+ SANE_Status status;
+
+ DBG (3, ">> get tpu stat\n");
+
+ memset (tbuf, 0, sizeof (tbuf));
+ buf_size = sizeof (tbuf);
+ status = get_scan_mode (fd, TRANSPARENCY_UNIT, tbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "get scan mode failed: %s\n", sane_strstatus (status));
+ return;
+ }
+
+ for (i = 0; i < buf_size; i++)
+ DBG (3, "scan mode control byte[%d] = %d\n", (int) i, tbuf[i]);
+ dev->tpu.Status = (tbuf[2 + 4 + 5] >> 7) ?
+ TPU_STAT_INACTIVE : TPU_STAT_NONE;
+ if (dev->tpu.Status != TPU_STAT_NONE) /* TPU available */
+ {
+ dev->tpu.Status = (tbuf[2 + 4 + 5] & 0x04) ?
+ TPU_STAT_INACTIVE : TPU_STAT_ACTIVE;
+ }
+ dev->tpu.ControlMode = tbuf[3 + 4 + 5] & 0x03;
+ dev->tpu.Transparency = tbuf[4 + 4 + 5] * 256 + tbuf[5 + 4 + 5];
+ dev->tpu.PosNeg = tbuf[6 + 4 + 5] & 0x01;
+ dev->tpu.FilmType = tbuf[7 + 4 + 5];
+ if(dev->tpu.FilmType > 3)
+ dev->tpu.FilmType = 0;
+
+ DBG (11, "TPU Status: %d\n", dev->tpu.Status);
+ DBG (11, "TPU ControlMode: %d\n", dev->tpu.ControlMode);
+ DBG (11, "TPU Transparency: %d\n", dev->tpu.Transparency);
+ DBG (11, "TPU PosNeg: %d\n", dev->tpu.PosNeg);
+ DBG (11, "TPU FilmType: %d\n", dev->tpu.FilmType);
+
+ DBG (3, "<< get tpu stat\n");
+
+ return;
+}
+
+/**************************************************************************/
+
+static void
+get_adf_stat (int fd, CANON_Device * dev)
+{
+ size_t buf_size = 0x0C, i;
+ unsigned char abuf[0x0C];
+ SANE_Status status;
+
+ DBG (3, ">> get adf stat\n");
+
+ memset (abuf, 0, buf_size);
+ status = get_scan_mode (fd, AUTO_DOC_FEEDER_UNIT, abuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "get scan mode failed: %s\n", sane_strstatus (status));
+ perror ("get scan mode failed");
+ return;
+ }
+
+ for (i = 0; i < buf_size; i++)
+ DBG (3, "scan mode control byte[%d] = %d\n", (int) i, abuf[i]);
+
+ dev->adf.Status = (abuf[ADF_Status] & ADF_NOT_PRESENT) ?
+ ADF_STAT_NONE : ADF_STAT_INACTIVE;
+
+ if (dev->adf.Status != ADF_STAT_NONE) /* ADF available / INACTIVE */
+ {
+ dev->adf.Status = (abuf[ADF_Status] & ADF_PROBLEM) ?
+ ADF_STAT_INACTIVE : ADF_STAT_ACTIVE;
+ }
+ dev->adf.Problem = (abuf[ADF_Status] & ADF_PROBLEM);
+ dev->adf.Priority = (abuf[ADF_Settings] & ADF_PRIORITY);
+ dev->adf.Feeder = (abuf[ADF_Settings] & ADF_FEEDER);
+
+ DBG (11, "ADF Status: %d\n", dev->adf.Status);
+ DBG (11, "ADF Priority: %d\n", dev->adf.Priority);
+ DBG (11, "ADF Problem: %d\n", dev->adf.Problem);
+ DBG (11, "ADF Feeder: %d\n", dev->adf.Feeder);
+
+ DBG (3, "<< get adf stat\n");
+ return;
+}
+
+/**************************************************************************/
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ static char me[] = "canon_sense_handler";
+ u_char sense;
+ int asc;
+ char *sense_str = NULL;
+ SANE_Status status;
+
+ DBG (1, ">> sense_handler\n");
+ DBG (11, "%s(%ld, %p, %p)\n", me, (long) scsi_fd, (void *) result,
+ (void *) arg);
+ DBG (11, "sense buffer: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x\n", result[0], result[1], result[2],
+ result[3], result[4], result[5], result[6], result[7], result[8],
+ result[9], result[10], result[11], result[12], result[13], result[14],
+ result[15]);
+
+ status = SANE_STATUS_GOOD;
+
+ DBG(11, "sense data interpretation for SCSI-2 devices\n");
+ sense = result[2] & 0x0f; /* extract the sense key */
+ if (result[7] > 3) /* additional sense code available? */
+ {
+ asc = (result[12] << 8) + result[13]; /* 12: additional sense code */
+ } /* 13: a.s.c. qualifier */
+ else
+ asc = 0xffff;
+
+ switch (sense)
+ {
+ case 0x00:
+ DBG(11, "sense category: no error\n");
+ status = SANE_STATUS_GOOD;
+ break;
+
+ case 0x01:
+ DBG(11, "sense category: recovered error\n");
+ switch (asc)
+ {
+ case 0x3700:
+ sense_str = SANE_I18N("rounded parameter");
+ break;
+ default:
+ sense_str = SANE_I18N("unknown");
+ }
+ status = SANE_STATUS_GOOD;
+ break;
+
+ case 0x03:
+ DBG(11, "sense category: medium error\n");
+ switch (asc)
+ {
+ case 0x8000:
+ sense_str = SANE_I18N("ADF jam");
+ break;
+ case 0x8001:
+ sense_str = SANE_I18N("ADF cover open");
+ break;
+ default:
+ sense_str = SANE_I18N("unknown");
+ }
+ status = SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0x04:
+ DBG(11, "sense category: hardware error\n");
+ switch (asc)
+ {
+ case 0x6000:
+ sense_str = SANE_I18N("lamp failure");
+ break;
+ case 0x6200:
+ sense_str = SANE_I18N("scan head positioning error");
+ break;
+ case 0x8001:
+ sense_str = SANE_I18N("CPU check error");
+ break;
+ case 0x8002:
+ sense_str = SANE_I18N("RAM check error");
+ break;
+ case 0x8003:
+ sense_str = SANE_I18N("ROM check error");
+ break;
+ case 0x8004:
+ sense_str = SANE_I18N("hardware check error");
+ break;
+ case 0x8005:
+ sense_str = SANE_I18N("transparency unit lamp failure");
+ break;
+ case 0x8006:
+ sense_str = SANE_I18N("transparency unit scan head "
+ "positioning failure");
+ break;
+ default:
+ sense_str = SANE_I18N("unknown");
+ }
+ status = SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0x05:
+ DBG(11, "sense category: illegal request\n");
+ switch (asc)
+ {
+ case 0x1a00:
+ sense_str = SANE_I18N("parameter list length error");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x2000:
+ sense_str = SANE_I18N("invalid command operation code");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x2400:
+ sense_str = SANE_I18N("invalid field in CDB");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x2500:
+ sense_str = SANE_I18N("unsupported LUN");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x2600:
+ sense_str = SANE_I18N("invalid field in parameter list");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x2c00:
+ sense_str = SANE_I18N("command sequence error");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x2c01:
+ sense_str = SANE_I18N("too many windows specified");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x3a00:
+ sense_str = SANE_I18N("medium not present");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x3d00:
+ sense_str = SANE_I18N("invalid bit IDENTIFY message");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x8002:
+ sense_str = SANE_I18N("option not connect");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ default:
+ sense_str = SANE_I18N("unknown");
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+ break;
+
+ case 0x06:
+ DBG(11, "sense category: unit attention\n");
+ switch (asc)
+ {
+ case 0x2900:
+ sense_str = SANE_I18N("power on reset / bus device reset");
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x2a00:
+ sense_str = SANE_I18N("parameter changed by another initiator");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default:
+ sense_str = SANE_I18N("unknown");
+ status = SANE_STATUS_IO_ERROR;
+ }
+ break;
+
+ case 0x0b:
+ DBG(11, "sense category: non-standard\n");
+ switch (asc)
+ {
+ case 0x0000:
+ sense_str = SANE_I18N("no additional sense information");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4500:
+ sense_str = SANE_I18N("reselect failure");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4700:
+ sense_str = SANE_I18N("SCSI parity error");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4800:
+ sense_str = SANE_I18N("initiator detected error message "
+ "received");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4900:
+ sense_str = SANE_I18N("invalid message error");
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x8000:
+ sense_str = SANE_I18N("timeout error");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8001:
+ sense_str = SANE_I18N("transparency unit shading error");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8003:
+ sense_str = SANE_I18N("lamp not stabilized");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default:
+ sense_str = SANE_I18N("unknown");
+ status = SANE_STATUS_IO_ERROR;
+ }
+ break;
+ default:
+ DBG(11, "sense category: else\n");
+ }
+ DBG (11, "sense message: %s\n", sense_str);
+#if 0 /* superfluous? [U.D.] */
+ s->sense_str = sense_str;
+#endif
+ DBG (1, "<< sense_handler\n");
+ return status;
+}
+
+/***************************************************************/
+static SANE_Status
+do_gamma (CANON_Scanner * s)
+{
+ SANE_Status status;
+ u_char gbuf[256];
+ size_t buf_size;
+ int i, j, neg, transfer_data_type, from;
+
+
+ DBG (7, "sending SET_DENSITY_CURVE\n");
+ buf_size = 256 * sizeof (u_char);
+ transfer_data_type = 0x03;
+
+ neg = (s->hw->info.is_filmscanner) ?
+ strcmp (filmtype_list[1], s->val[OPT_NEGATIVE].s)
+ : s->val[OPT_HNEGATIVE].w;
+
+ if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ /* If scanning in gray mode, use the first curve for the
+ scanner's monochrome gamma component */
+ for (j = 0; j < 256; j++)
+ {
+ if (!neg)
+ {
+ gbuf[j] = (u_char) s->gamma_table[0][j];
+ DBG (22, "set_density %d: gbuf[%d] = [%d]\n", 0, j, gbuf[j]);
+ }
+ else
+ {
+ gbuf[255 - j] = (u_char) (255 - s->gamma_table[0][j]);
+ DBG (22, "set_density %d: gbuf[%d] = [%d]\n", 0, 255 - j,
+ gbuf[255 - j]);
+ }
+ }
+ if ((status = set_density_curve (s->fd, 0, gbuf, &buf_size,
+ transfer_data_type)) != SANE_STATUS_GOOD)
+ {
+ DBG (7, "SET_DENSITY_CURVE\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else
+ { /* colour mode */
+ /* If in RGB mode but with gamma bind, use the first curve
+ for all 3 colors red, green, blue */
+ for (i = 1; i < 4; i++)
+ {
+ from = (s->val[OPT_CUSTOM_GAMMA_BIND].w) ? 0 : i;
+ for (j = 0; j < 256; j++)
+ {
+ if (!neg)
+ {
+ gbuf[j] = (u_char) s->gamma_table[from][j];
+ DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, j, gbuf[j]);
+ }
+ else
+ {
+ gbuf[255 - j] = (u_char) (255 - s->gamma_table[from][j]);
+ DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, 255 - j,
+ gbuf[255 - j]);
+ }
+ }
+ if (s->hw->info.model == FS2710)
+ status = set_density_curve_fs2710 (s, i, gbuf);
+ else
+ {
+ if ((status = set_density_curve (s->fd, i, gbuf, &buf_size,
+ transfer_data_type)) != SANE_STATUS_GOOD)
+ {
+ DBG (7, "SET_DENSITY_CURVE\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ }
+ }
+
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+
+static SANE_Status
+attach (const char *devnam, CANON_Device ** devp)
+{
+ SANE_Status status;
+ CANON_Device *dev;
+
+ int fd;
+ u_char ibuf[36], ebuf[74], mbuf[12];
+ size_t buf_size, i;
+ char *str;
+
+ DBG (1, ">> attach\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (!strcmp (dev->sane.name, devnam))
+ {
+ if (devp) *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ DBG (3, "attach: opening %s\n", devnam);
+ status = sanei_scsi_open (devnam, &fd, sense_handler, dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (3, "attach: sending (standard) INQUIRY\n");
+ memset (ibuf, 0, sizeof (ibuf));
+ buf_size = sizeof (ibuf);
+ status = inquiry (fd, 0, ibuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (status);
+ }
+
+ if (ibuf[0] != 6
+ || strncmp ((char *) (ibuf + 8), "CANON", 5) != 0
+ || strncmp ((char *) (ibuf + 16), "IX-", 3) != 0)
+ {
+ DBG (1, "attach: device doesn't look like a Canon scanner\n");
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (status);
+ }
+
+#if 0
+ DBG (3, "attach: sending REQUEST SENSE\n");
+ memset (sbuf, 0, sizeof (sbuf));
+ buf_size = sizeof (sbuf);
+ status = request_sense (fd, sbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: REQUEST_SENSE failed\n");
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending MEDIUM POSITION\n");
+ status = medium_position (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MEDIUM POSITION failed\n");
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+/* s->val[OPT_AF_NOW].w == SANE_TRUE; */
+#endif
+
+ DBG (3, "attach: sending RESERVE UNIT\n");
+ status = reserve_unit (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: RESERVE UNIT failed\n");
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+#if 0
+ DBG (3, "attach: sending GET SCAN MODE for transparency unit\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = sizeof (ebuf);
+ buf_size = 12;
+ status = get_scan_mode (fd, TRANSPARENCY_UNIT, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET SCAN MODE for transparency unit failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ DBG(3, "scan mode trans byte[%d] = %d\n", i, ebuf[i]);
+#endif
+
+ DBG (3, "attach: sending GET SCAN MODE for scan control conditions\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = sizeof (ebuf);
+ status = get_scan_mode (fd, SCAN_CONTROL_CONDITIONS, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ {
+ DBG (3, "scan mode byte[%d] = %d\n", (int) i, ebuf[i]);
+ }
+
+ DBG (3, "attach: sending (extended) INQUIRY\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = sizeof (ebuf);
+ status = inquiry (fd, 1, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: (extended) INQUIRY failed\n");
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+#if 0
+ DBG (3, "attach: sending GET SCAN MODE for transparency unit\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = 64;
+ status = get_scan_mode (fd, ALL_SCAN_MODE_PAGES, /* transparency unit */
+ ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ DBG (3, "scan mode control byte[%d] = %d\n", i, ebuf[i]);
+#endif
+
+#if 0
+ DBG (3, "attach: sending GET SCAN MODE for all scan mode pages\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = 32;
+ status = get_scan_mode (fd, (u_char)ALL_SCAN_MODE_PAGES, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+ for (i = 0; i < buf_size; i++)
+ DBG(3, "scan mode control byte[%d] = %d\n", i, ebuf[i]);
+#endif
+
+ DBG (3, "attach: sending MODE SENSE\n");
+ memset (mbuf, 0, sizeof (mbuf));
+ buf_size = sizeof (mbuf);
+ status = mode_sense (fd, mbuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE failed\n");
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ {
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_NO_MEM);
+ }
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devnam);
+ dev->sane.vendor = "CANON";
+ if ((str = calloc (16 + 1, 1)) == NULL)
+ {
+ sanei_scsi_close (fd);
+ fd = -1;
+ return (SANE_STATUS_NO_MEM);
+ }
+ strncpy (str, (char *) (ibuf + 16), 16);
+ dev->sane.model = str;
+
+ /* Register the fixed properties of the scanner below:
+ - whether it is a film scanner or a flatbed scanner
+ - whether it can have an automatic document feeder (ADF)
+ - whether it can be equipped with a transparency unit (TPU)
+ - whether it has got focus control
+ - whether it can optimize image parameters (autoexposure)
+ - whether it can calibrate itself
+ - whether it can diagnose itself
+ - whether it can eject the media
+ - whether it can mirror the scanned data
+ - whether it is a film scanner (or can be used as one)
+ - whether it has fixed, hardware-set scan resolutions only
+ */
+ if (!strncmp (str, "IX-27015", 8)) /* FS2700S */
+ {
+ dev->info.model = CS2700;
+ dev->sane.type = SANE_I18N("film scanner");
+ dev->adf.Status = ADF_STAT_NONE;
+ dev->tpu.Status = TPU_STAT_NONE;
+ dev->info.can_focus = SANE_TRUE;
+ dev->info.can_autoexpose = SANE_TRUE;
+ dev->info.can_calibrate = SANE_FALSE;
+ dev->info.can_diagnose = SANE_FALSE;
+ dev->info.can_eject = SANE_TRUE;
+ dev->info.can_mirror = SANE_TRUE;
+ dev->info.is_filmscanner = SANE_TRUE;
+ dev->info.has_fixed_resolutions = SANE_TRUE;
+ }
+ else if (!strncmp (str, "IX-27025E", 9)) /* FS2710S */
+ {
+ dev->info.model = FS2710;
+ dev->sane.type = SANE_I18N("film scanner");
+ dev->adf.Status = ADF_STAT_NONE;
+ dev->tpu.Status = TPU_STAT_NONE;
+ dev->info.can_focus = SANE_TRUE;
+ dev->info.can_autoexpose = SANE_TRUE;
+ dev->info.can_calibrate = SANE_FALSE;
+ dev->info.can_diagnose = SANE_FALSE;
+ dev->info.can_eject = SANE_TRUE;
+ dev->info.can_mirror = SANE_TRUE;
+ dev->info.is_filmscanner = SANE_TRUE;
+ dev->info.has_fixed_resolutions = SANE_TRUE;
+ }
+ else if (!strncmp (str, "IX-06035E", 9)) /* FB620S */
+ {
+ dev->info.model = FB620;
+ dev->sane.type = SANE_I18N("flatbed scanner");
+ dev->adf.Status = ADF_STAT_NONE;
+ dev->tpu.Status = TPU_STAT_NONE;
+ dev->info.can_focus = SANE_FALSE;
+ dev->info.can_autoexpose = SANE_FALSE;
+ dev->info.can_calibrate = SANE_TRUE;
+ dev->info.can_diagnose = SANE_TRUE;
+ dev->info.can_eject = SANE_FALSE;
+ dev->info.can_mirror = SANE_FALSE;
+ dev->info.is_filmscanner = SANE_FALSE;
+ dev->info.has_fixed_resolutions = SANE_TRUE;
+ }
+ else if (!strncmp (str, "IX-12015E", 9)) /* FB1200S */
+ {
+ dev->info.model = FB1200;
+ dev->sane.type = SANE_I18N("flatbed scanner");
+ dev->adf.Status = ADF_STAT_INACTIVE;
+ dev->tpu.Status = TPU_STAT_INACTIVE;
+ dev->info.can_focus = SANE_FALSE;
+ dev->info.can_autoexpose = SANE_FALSE;
+ dev->info.can_calibrate = SANE_FALSE;
+ dev->info.can_diagnose = SANE_FALSE;
+ dev->info.can_eject = SANE_FALSE;
+ dev->info.can_mirror = SANE_FALSE;
+ dev->info.is_filmscanner = SANE_FALSE;
+ dev->info.has_fixed_resolutions = SANE_TRUE;
+ }
+ else if (!strncmp (str, "IX-4015", 7)) /* IX-4015 */
+ {
+ dev->info.model = IX4015;
+ dev->sane.type = SANE_I18N("flatbed scanner");
+ dev->adf.Status = ADF_STAT_INACTIVE;
+ dev->tpu.Status = TPU_STAT_INACTIVE;
+ dev->info.can_focus = SANE_FALSE;
+ dev->info.can_autoexpose = SANE_TRUE;
+ dev->info.can_calibrate = SANE_FALSE;
+ dev->info.can_diagnose = SANE_TRUE;
+ dev->info.can_eject = SANE_FALSE;
+ dev->info.can_mirror = SANE_TRUE;
+ dev->info.is_filmscanner = SANE_FALSE;
+ dev->info.has_fixed_resolutions = SANE_FALSE;
+ }
+ else /* CS300, CS600 */
+ {
+ dev->info.model = CS3_600;
+ dev->sane.type = SANE_I18N("flatbed scanner");
+ dev->adf.Status = ADF_STAT_INACTIVE;
+ dev->tpu.Status = TPU_STAT_INACTIVE;
+ dev->info.can_focus = SANE_FALSE;
+ dev->info.can_autoexpose = SANE_FALSE;
+ dev->info.can_calibrate = SANE_FALSE;
+ dev->info.can_diagnose = SANE_FALSE;
+ dev->info.can_eject = SANE_FALSE;
+ dev->info.can_mirror = SANE_TRUE;
+ dev->info.is_filmscanner = SANE_FALSE;
+ dev->info.has_fixed_resolutions = SANE_FALSE;
+ }
+
+ DBG (5, "dev->sane.name = '%s'\n", dev->sane.name);
+ DBG (5, "dev->sane.vendor = '%s'\n", dev->sane.vendor);
+ DBG (5, "dev->sane.model = '%s'\n", dev->sane.model);
+ DBG (5, "dev->sane.type = '%s'\n", dev->sane.type);
+
+ if (dev->tpu.Status != TPU_STAT_NONE)
+ get_tpu_stat (fd, dev); /* Query TPU */
+ if (dev->adf.Status != ADF_STAT_NONE)
+ get_adf_stat (fd, dev); /* Query ADF */
+
+ dev->info.bmu = mbuf[6];
+ DBG (5, "bmu=%d\n", dev->info.bmu);
+ dev->info.mud = (mbuf[8] << 8) + mbuf[9];
+ DBG (5, "mud=%d\n", dev->info.mud);
+
+ dev->info.xres_default = (ebuf[5] << 8) + ebuf[6];
+ DBG (5, "xres_default=%d\n", dev->info.xres_default);
+ dev->info.xres_range.max = (ebuf[10] << 8) + ebuf[11];
+ DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max);
+ dev->info.xres_range.min = (ebuf[14] << 8) + ebuf[15];
+ DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min);
+ dev->info.xres_range.quant = ebuf[9] >> 4;
+ DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
+
+ dev->info.yres_default = (ebuf[7] << 8) + ebuf[8];
+ DBG (5, "yres_default=%d\n", dev->info.yres_default);
+ dev->info.yres_range.max = (ebuf[12] << 8) + ebuf[13];
+ DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max);
+ dev->info.yres_range.min = (ebuf[16] << 8) + ebuf[17];
+ DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min);
+ dev->info.yres_range.quant = ebuf[9] & 0x0f;
+ DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
+
+ dev->info.x_range.min = SANE_FIX (0.0);
+ dev->info.x_range.max = (ebuf[20] << 24) + (ebuf[21] << 16)
+ + (ebuf[22] << 8) + ebuf[23] - 1;
+ dev->info.x_range.max =
+ SANE_FIX (dev->info.x_range.max * MM_PER_INCH / dev->info.mud);
+ DBG (5, "x_range.max=%d\n", dev->info.x_range.max);
+ dev->info.x_range.quant = 0;
+
+ dev->info.y_range.min = SANE_FIX (0.0);
+ dev->info.y_range.max = (ebuf[24] << 24) + (ebuf[25] << 16)
+ + (ebuf[26] << 8) + ebuf[27] - 1;
+ dev->info.y_range.max =
+ SANE_FIX (dev->info.y_range.max * MM_PER_INCH / dev->info.mud);
+ DBG (5, "y_range.max=%d\n", dev->info.y_range.max);
+ dev->info.y_range.quant = 0;
+
+ dev->info.x_adf_range.max = (ebuf[30] << 24) + (ebuf[31] << 16)
+ + (ebuf[32] << 8) + ebuf[33] - 1;
+ DBG (5, "x_adf_range.max=%d\n", dev->info.x_adf_range.max);
+ dev->info.y_adf_range.max = (ebuf[34] << 24) + (ebuf[35] << 16)
+ + (ebuf[36] << 8) + ebuf[37] - 1;
+ DBG (5, "y_adf_range.max=%d\n", dev->info.y_adf_range.max);
+
+ dev->info.brightness_range.min = 0;
+ dev->info.brightness_range.max = 255;
+ dev->info.brightness_range.quant = 0;
+
+ dev->info.contrast_range.min = 1;
+ dev->info.contrast_range.max = 255;
+ dev->info.contrast_range.quant = 0;
+
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 0;
+
+ dev->info.HiliteR_range.min = 0;
+ dev->info.HiliteR_range.max = 255;
+ dev->info.HiliteR_range.quant = 0;
+
+ dev->info.ShadowR_range.min = 0;
+ dev->info.ShadowR_range.max = 254;
+ dev->info.ShadowR_range.quant = 0;
+
+ dev->info.HiliteG_range.min = 0;
+ dev->info.HiliteG_range.max = 255;
+ dev->info.HiliteG_range.quant = 0;
+
+ dev->info.ShadowG_range.min = 0;
+ dev->info.ShadowG_range.max = 254;
+ dev->info.ShadowG_range.quant = 0;
+
+ dev->info.HiliteB_range.min = 0;
+ dev->info.HiliteB_range.max = 255;
+ dev->info.HiliteB_range.quant = 0;
+
+ dev->info.ShadowB_range.min = 0;
+ dev->info.ShadowB_range.max = 254;
+ dev->info.ShadowB_range.quant = 0;
+
+ dev->info.focus_range.min = 0;
+ dev->info.focus_range.max = 255;
+ dev->info.focus_range.quant = 0;
+
+ dev->info.TPU_Transparency_range.min = 0;
+ dev->info.TPU_Transparency_range.max = 10000;
+ dev->info.TPU_Transparency_range.quant = 100;
+
+ sanei_scsi_close (fd);
+ fd = -1;
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (1, "<< attach\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+
+static SANE_Status
+do_cancel (CANON_Scanner * s)
+{
+ SANE_Status status;
+
+ DBG (1, ">> do_cancel\n");
+
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ if (s->val[OPT_EJECT_AFTERSCAN].w && !(s->val[OPT_PREVIEW].w
+ && s->hw->info.is_filmscanner))
+ {
+ DBG (3, "do_cancel: sending MEDIUM POSITION\n");
+ status = medium_position (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "do_cancel: MEDIUM POSITION failed\n");
+ return (SANE_STATUS_INVAL);
+ }
+ s->AF_NOW = SANE_TRUE;
+ DBG (1, "do_cancel AF_NOW = '%d'\n", s->AF_NOW);
+ }
+
+ DBG (21, "do_cancel: reset_flag = %d\n", s->reset_flag);
+ if ((s->reset_flag == 1) && (s->hw->info.model == FB620))
+ {
+ status = reset_scanner (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (21, "RESET SCANNER failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ DBG (21, "RESET SCANNER\n");
+ s->reset_flag = 0;
+ DBG (21, "do_cancel: reset_flag = %d\n", s->reset_flag);
+ s->time0 = -1;
+ DBG (21, "time0 = %ld\n", s->time0);
+ }
+
+ if (s->hw->info.model == FB1200)
+ {
+ DBG (3, "CANCEL FB1200S\n");
+ status = cancel (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "CANCEL FB1200S failed\n");
+ return (SANE_STATUS_INVAL);
+ }
+ DBG (3, "CANCEL FB1200S OK\n");
+ }
+
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (1, "<< do_cancel\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+/**************************************************************************/
+
+static SANE_Status
+init_options (CANON_Scanner * s)
+{
+ int i;
+ DBG (1, ">> init_options\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ s->AF_NOW = SANE_TRUE;
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+
+ switch (s->hw->info.model)
+ {
+ case FB620:
+ s->opt[OPT_MODE].size = max_string_size (mode_list_fb620);
+ s->opt[OPT_MODE].constraint.string_list = mode_list_fb620;
+ s->val[OPT_MODE].s = strdup (mode_list_fb620[3]);
+ break;
+ case FB1200:
+ s->opt[OPT_MODE].size = max_string_size (mode_list_fb1200);
+ s->opt[OPT_MODE].constraint.string_list = mode_list_fb1200;
+ s->val[OPT_MODE].s = strdup (mode_list_fb1200[2]);
+ break;
+ case FS2710:
+ s->opt[OPT_MODE].size = max_string_size (mode_list_fs2710);
+ s->opt[OPT_MODE].constraint.string_list = mode_list_fs2710;
+ s->val[OPT_MODE].s = strdup (mode_list_fs2710[0]);
+ break;
+ default:
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[3]);
+ }
+
+ /* Slides or negatives */
+ s->opt[OPT_NEGATIVE].name = "film-type";
+ s->opt[OPT_NEGATIVE].title = SANE_I18N("Film type");
+ s->opt[OPT_NEGATIVE].desc = SANE_I18N("Selects the film type, i.e. "
+ "negatives or slides");
+ s->opt[OPT_NEGATIVE].type = SANE_TYPE_STRING;
+ s->opt[OPT_NEGATIVE].size = max_string_size (filmtype_list);
+ s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_NEGATIVE].constraint.string_list = filmtype_list;
+ s->opt[OPT_NEGATIVE].cap |=
+ (s->hw->info.is_filmscanner)? 0 : SANE_CAP_INACTIVE;
+ s->val[OPT_NEGATIVE].s = strdup (filmtype_list[1]);
+
+ /* Negative film type */
+ s->opt[OPT_NEGATIVE_TYPE].name = "negative-film-type";
+ s->opt[OPT_NEGATIVE_TYPE].title = SANE_I18N("Negative film type");
+ s->opt[OPT_NEGATIVE_TYPE].desc = SANE_I18N("Selects the negative film type");
+ s->opt[OPT_NEGATIVE_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_NEGATIVE_TYPE].size = max_string_size (negative_filmtype_list);
+ s->opt[OPT_NEGATIVE_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_NEGATIVE_TYPE].constraint.string_list = negative_filmtype_list;
+ s->opt[OPT_NEGATIVE_TYPE].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_NEGATIVE_TYPE].s = strdup (negative_filmtype_list[0]);
+
+ /* Scanning speed */
+ s->opt[OPT_SCANNING_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SCANNING_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ s->opt[OPT_SCANNING_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ s->opt[OPT_SCANNING_SPEED].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCANNING_SPEED].size = max_string_size (scanning_speed_list);
+ s->opt[OPT_SCANNING_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCANNING_SPEED].constraint.string_list = scanning_speed_list;
+ s->opt[OPT_SCANNING_SPEED].cap |=
+ (s->hw->info.model == CS2700) ? 0 : SANE_CAP_INACTIVE;
+ if (s->hw->info.model != CS2700)
+ s->opt[OPT_SCANNING_SPEED].cap &= ~SANE_CAP_SOFT_SELECT;
+ s->val[OPT_SCANNING_SPEED].s = strdup (scanning_speed_list[0]);
+
+
+ /* "Resolution" group: */
+ s->opt[OPT_RESOLUTION_GROUP].title = SANE_I18N("Scan resolution");
+ s->opt[OPT_RESOLUTION_GROUP].desc = "";
+ s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_RESOLUTION_GROUP].cap = 0;
+ s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* bind resolution */
+ s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ s->val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
+
+ /* hardware resolutions only */
+ s->opt[OPT_HW_RESOLUTION_ONLY].name = "hw-resolution-only";
+ s->opt[OPT_HW_RESOLUTION_ONLY].title = SANE_I18N("Hardware resolution");
+ s->opt[OPT_HW_RESOLUTION_ONLY].desc = SANE_I18N("Use only hardware "
+ "resolutions");
+ s->opt[OPT_HW_RESOLUTION_ONLY].type = SANE_TYPE_BOOL;
+ s->val[OPT_HW_RESOLUTION_ONLY].w = SANE_TRUE;
+ s->opt[OPT_HW_RESOLUTION_ONLY].cap |=
+ (s->hw->info.has_fixed_resolutions)? 0 : SANE_CAP_INACTIVE;
+
+ /* x-resolution */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ if (s->hw->info.has_fixed_resolutions)
+ {
+ int iCnt;
+ float iRes; /* modification for FB620S */
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ iCnt = 0;
+
+ iRes = s->hw->info.xres_range.max;
+ DBG (5, "hw->info.xres_range.max=%d\n", s->hw->info.xres_range.max);
+ s->opt[OPT_X_RESOLUTION].constraint.word_list = s->xres_word_list;
+
+ /* go to minimum resolution by dividing by 2 */
+ while (iRes >= s->hw->info.xres_range.min)
+ iRes /= 2;
+ /* fill array up to maximum resolution */
+ while (iRes < s->hw->info.xres_range.max)
+ {
+ iRes *= 2;
+ s->xres_word_list[++iCnt] = iRes;
+ }
+ s->xres_word_list[0] = iCnt;
+ s->val[OPT_X_RESOLUTION].w = s->xres_word_list[2];
+ }
+ else
+ {
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &s->hw->info.xres_range;
+ s->val[OPT_X_RESOLUTION].w = 300;
+ }
+
+ /* y-resolution */
+ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ if (s->hw->info.has_fixed_resolutions)
+ {
+ int iCnt;
+ float iRes; /* modification for FB620S */
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ iCnt = 0;
+
+ iRes = s->hw->info.yres_range.max;
+ DBG (5, "hw->info.yres_range.max=%d\n", s->hw->info.yres_range.max);
+ s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->yres_word_list;
+
+ /* go to minimum resolution by dividing by 2 */
+ while (iRes >= s->hw->info.yres_range.min)
+ iRes /= 2;
+ /* fill array up to maximum resolution */
+ while (iRes < s->hw->info.yres_range.max)
+ {
+ iRes *= 2;
+ s->yres_word_list[++iCnt] = iRes;
+ }
+ s->yres_word_list[0] = iCnt;
+ s->val[OPT_Y_RESOLUTION].w = s->yres_word_list[2];
+ }
+ else
+ {
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &s->hw->info.yres_range;
+ s->val[OPT_Y_RESOLUTION].w = 300;
+ }
+
+ /* Focus group: */
+ s->opt[OPT_FOCUS_GROUP].title = SANE_I18N("Focus");
+ s->opt[OPT_FOCUS_GROUP].desc = "";
+ s->opt[OPT_FOCUS_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_FOCUS_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_FOCUS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_FOCUS_GROUP].cap |=
+ (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE;
+
+ /* Auto-Focus switch */
+ s->opt[OPT_AF].name = "af";
+ s->opt[OPT_AF].title = SANE_I18N("Auto focus");
+ s->opt[OPT_AF].desc = SANE_I18N("Enable/disable auto focus");
+ s->opt[OPT_AF].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AF].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE;
+ s->val[OPT_AF].w = s->hw->info.can_focus;
+
+ /* Auto-Focus once switch */
+ s->opt[OPT_AF_ONCE].name = "afonce";
+ s->opt[OPT_AF_ONCE].title = SANE_I18N("Auto focus only once");
+ s->opt[OPT_AF_ONCE].desc = SANE_I18N("Do auto focus only once between "
+ "ejects");
+ s->opt[OPT_AF_ONCE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AF_ONCE].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE;
+ s->val[OPT_AF_ONCE].w = s->hw->info.can_focus;
+
+ /* Manual focus */
+ s->opt[OPT_FOCUS].name = "focus";
+ s->opt[OPT_FOCUS].title = SANE_I18N("Manual focus position");
+ s->opt[OPT_FOCUS].desc = SANE_I18N("Set the optical system's focus "
+ "position by hand (default: 128).");
+ s->opt[OPT_FOCUS].type = SANE_TYPE_INT;
+ s->opt[OPT_FOCUS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_FOCUS].constraint.range = &s->hw->info.focus_range;
+ s->opt[OPT_FOCUS].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE;
+ s->val[OPT_FOCUS].w = (s->hw->info.can_focus) ? 128 : 0;
+
+ /* Margins group: */
+ s->opt[OPT_MARGINS_GROUP].title = SANE_I18N("Scan margins");
+ s->opt[OPT_MARGINS_GROUP].desc = "";
+ s->opt[OPT_MARGINS_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MARGINS_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_MARGINS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->info.x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->info.y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->info.x_range;
+ s->val[OPT_BR_X].w = s->hw->info.x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->info.y_range;
+ s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
+
+ /* Colors group: */
+ s->opt[OPT_COLORS_GROUP].title = SANE_I18N("Extra color adjustments");
+ s->opt[OPT_COLORS_GROUP].desc = "";
+ s->opt[OPT_COLORS_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_COLORS_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_COLORS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Positive/Negative switch for the CanoScan 300/600 models */
+ s->opt[OPT_HNEGATIVE].name = SANE_NAME_NEGATIVE;
+ s->opt[OPT_HNEGATIVE].title = SANE_TITLE_NEGATIVE;
+ s->opt[OPT_HNEGATIVE].desc = SANE_DESC_NEGATIVE;
+ s->opt[OPT_HNEGATIVE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_HNEGATIVE].cap |=
+ (s->hw->info.model == CS2700 || s->hw->info.model == FS2710) ?
+ SANE_CAP_INACTIVE : 0;
+ s->val[OPT_HNEGATIVE].w = SANE_FALSE;
+
+ /* Same values for highlight and shadow points for red, green, blue */
+ s->opt[OPT_BIND_HILO].name = "bind-highlight-shadow-points";
+ s->opt[OPT_BIND_HILO].title = SANE_TITLE_RGB_BIND;
+ s->opt[OPT_BIND_HILO].desc = SANE_DESC_RGB_BIND;
+ s->opt[OPT_BIND_HILO].type = SANE_TYPE_BOOL;
+ s->opt[OPT_BIND_HILO].cap |= (s->hw->info.model == FB620 ||
+ s->hw->info.model == IX4015) ? SANE_CAP_INACTIVE : 0;
+ s->val[OPT_BIND_HILO].w = SANE_TRUE;
+
+ /* highlight point for red */
+ s->opt[OPT_HILITE_R].name = SANE_NAME_HIGHLIGHT_R;
+ s->opt[OPT_HILITE_R].title = SANE_TITLE_HIGHLIGHT_R;
+ s->opt[OPT_HILITE_R].desc = SANE_DESC_HIGHLIGHT_R;
+ s->opt[OPT_HILITE_R].type = SANE_TYPE_INT;
+ s->opt[OPT_HILITE_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_HILITE_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_HILITE_R].constraint.range = &s->hw->info.HiliteR_range;
+ s->opt[OPT_HILITE_R].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_HILITE_R].w = 255;
+
+ /* shadow point for red */
+ s->opt[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R;
+ s->opt[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R;
+ s->opt[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R;
+ s->opt[OPT_SHADOW_R].type = SANE_TYPE_INT;
+ s->opt[OPT_SHADOW_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHADOW_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHADOW_R].constraint.range = &s->hw->info.ShadowR_range;
+ s->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_SHADOW_R].w = 0;
+
+ /* highlight point for green */
+ s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT;
+ s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT;
+ s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT;
+ s->opt[OPT_HILITE_G].type = SANE_TYPE_INT;
+ s->opt[OPT_HILITE_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_HILITE_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_HILITE_G].constraint.range = &s->hw->info.HiliteG_range;
+ s->opt[OPT_HILITE_G].cap |=
+ (s->hw->info.model == IX4015) ? SANE_CAP_INACTIVE : 0;
+ s->val[OPT_HILITE_G].w = 255;
+
+ /* shadow point for green */
+ s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW;
+ s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW;
+ s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW;
+ s->opt[OPT_SHADOW_G].type = SANE_TYPE_INT;
+ s->opt[OPT_SHADOW_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHADOW_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHADOW_G].constraint.range = &s->hw->info.ShadowG_range;
+ s->opt[OPT_SHADOW_G].cap |=
+ (s->hw->info.model == IX4015) ? SANE_CAP_INACTIVE : 0;
+ s->val[OPT_SHADOW_G].w = 0;
+
+ /* highlight point for blue */
+ s->opt[OPT_HILITE_B].name = SANE_NAME_HIGHLIGHT_B;
+ s->opt[OPT_HILITE_B].title = SANE_TITLE_HIGHLIGHT_B;
+ s->opt[OPT_HILITE_B].desc = SANE_DESC_HIGHLIGHT_B;
+ s->opt[OPT_HILITE_B].type = SANE_TYPE_INT;
+ s->opt[OPT_HILITE_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_HILITE_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_HILITE_B].constraint.range = &s->hw->info.HiliteB_range;
+ s->opt[OPT_HILITE_B].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_HILITE_B].w = 255;
+
+ /* shadow point for blue */
+ s->opt[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B;
+ s->opt[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B;
+ s->opt[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B;
+ s->opt[OPT_SHADOW_B].type = SANE_TYPE_INT;
+ s->opt[OPT_SHADOW_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHADOW_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHADOW_B].constraint.range = &s->hw->info.ShadowB_range;
+ s->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_SHADOW_B].w = 0;
+
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->info.brightness_range;
+ s->opt[OPT_BRIGHTNESS].cap |= 0;
+ s->val[OPT_BRIGHTNESS].w = 128;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &s->hw->info.contrast_range;
+ s->opt[OPT_CONTRAST].cap |= 0;
+ s->val[OPT_CONTRAST].w = 128;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &s->hw->info.threshold_range;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_THRESHOLD].w = 128;
+
+ s->opt[OPT_MIRROR].name = "mirror";
+ s->opt[OPT_MIRROR].title = SANE_I18N("Mirror image");
+ s->opt[OPT_MIRROR].desc = SANE_I18N("Mirror the image horizontally");
+ s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
+ s->opt[OPT_MIRROR].cap |= (s->hw->info.can_mirror) ? 0: SANE_CAP_INACTIVE;
+ s->val[OPT_MIRROR].w = SANE_FALSE;
+
+ /* analog-gamma curve */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* bind analog-gamma */
+ s->opt[OPT_CUSTOM_GAMMA_BIND].name = "bind-custom-gamma";
+ s->opt[OPT_CUSTOM_GAMMA_BIND].title = SANE_TITLE_RGB_BIND;
+ s->opt[OPT_CUSTOM_GAMMA_BIND].desc = SANE_DESC_RGB_BIND;
+ s->opt[OPT_CUSTOM_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CUSTOM_GAMMA_BIND].w = SANE_TRUE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+
+ s->opt[OPT_AE].name = "ae";
+ s->opt[OPT_AE].title = SANE_I18N("Auto exposure");
+ s->opt[OPT_AE].desc = SANE_I18N("Enable/disable the auto exposure feature");
+ s->opt[OPT_AE].cap |= (s->hw->info.can_autoexpose) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_AE].type = SANE_TYPE_BOOL;
+ s->val[OPT_AE].w = SANE_FALSE;
+
+
+ /* "Calibration" group */
+ s->opt[OPT_CALIBRATION_GROUP].title = SANE_I18N("Calibration");
+ s->opt[OPT_CALIBRATION_GROUP].desc = "";
+ s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CALIBRATION_GROUP].cap |= (s->hw->info.can_calibrate ||
+ s->hw->info.can_diagnose) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* calibration now */
+ s->opt[OPT_CALIBRATION_NOW].name = "calibration-now";
+ s->opt[OPT_CALIBRATION_NOW].title = SANE_I18N("Calibration now");
+ s->opt[OPT_CALIBRATION_NOW].desc = SANE_I18N("Execute calibration *now*");
+ s->opt[OPT_CALIBRATION_NOW].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CALIBRATION_NOW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATION_NOW].cap |=
+ (s->hw->info.can_calibrate) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_CALIBRATION_NOW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_CALIBRATION_NOW].constraint.range = NULL;
+
+ /* scanner self diagnostic */
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].name = "self-diagnostic";
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].title = SANE_I18N("Self diagnosis");
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].desc = SANE_I18N("Perform scanner "
+ "self diagnosis");
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].cap |=
+ (s->hw->info.can_diagnose) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].constraint.range = NULL;
+
+ /* reset scanner for FB620S */
+ s->opt[OPT_RESET_SCANNER].name = "reset-scanner";
+ s->opt[OPT_RESET_SCANNER].title = SANE_I18N("Reset scanner");
+ s->opt[OPT_RESET_SCANNER].desc = SANE_I18N("Reset the scanner");
+ s->opt[OPT_RESET_SCANNER].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_RESET_SCANNER].unit = SANE_UNIT_NONE;
+ s->opt[OPT_RESET_SCANNER].cap |=
+ (s->hw->info.model == FB620) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_RESET_SCANNER].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_RESET_SCANNER].constraint.range = NULL;
+
+
+ /* "Eject" group (active only for film scanners) */
+ s->opt[OPT_EJECT_GROUP].title = SANE_I18N("Medium handling");
+ s->opt[OPT_EJECT_GROUP].desc = "";
+ s->opt[OPT_EJECT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_EJECT_GROUP].cap |=
+ (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_EJECT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* eject after scan */
+ s->opt[OPT_EJECT_AFTERSCAN].name = "eject-after-scan";
+ s->opt[OPT_EJECT_AFTERSCAN].title = SANE_I18N("Eject film after each scan");
+ s->opt[OPT_EJECT_AFTERSCAN].desc = SANE_I18N("Automatically eject the "
+ "film from the device after each scan");
+ s->opt[OPT_EJECT_AFTERSCAN].cap |=
+ (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_EJECT_AFTERSCAN].type = SANE_TYPE_BOOL;
+ /* IX-4015 requires medium_position command after cancel */
+ s->val[OPT_EJECT_AFTERSCAN].w =
+ (s->hw->info.model == IX4015) ? SANE_TRUE : SANE_FALSE;
+
+ /* eject before exit */
+ s->opt[OPT_EJECT_BEFOREEXIT].name = "eject-before-exit";
+ s->opt[OPT_EJECT_BEFOREEXIT].title = SANE_I18N("Eject film before exit");
+ s->opt[OPT_EJECT_BEFOREEXIT].desc = SANE_I18N("Automatically eject the "
+ "film from the device before exiting the program");
+ s->opt[OPT_EJECT_BEFOREEXIT].cap |=
+ (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_EJECT_BEFOREEXIT].type = SANE_TYPE_BOOL;
+ s->val[OPT_EJECT_BEFOREEXIT].w = s->hw->info.can_eject;
+
+ /* eject now */
+ s->opt[OPT_EJECT_NOW].name = "eject-now";
+ s->opt[OPT_EJECT_NOW].title = SANE_I18N("Eject film now");
+ s->opt[OPT_EJECT_NOW].desc = SANE_I18N("Eject the film *now*");
+ s->opt[OPT_EJECT_NOW].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_EJECT_NOW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_EJECT_NOW].cap |=
+ (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE;
+ s->opt[OPT_EJECT_NOW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_EJECT_NOW].constraint.range = NULL;
+
+ /* "NO-ADF" option: */
+ s->opt[OPT_ADF_GROUP].title = SANE_I18N("Document feeder extras");
+ s->opt[OPT_ADF_GROUP].desc = "";
+ s->opt[OPT_ADF_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ADF_GROUP].cap = 0;
+ s->opt[OPT_ADF_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_FLATBED_ONLY].name = "noadf";
+ s->opt[OPT_FLATBED_ONLY].title = SANE_I18N("Flatbed only");
+ s->opt[OPT_FLATBED_ONLY].desc = SANE_I18N("Disable auto document feeder "
+ "and use flatbed only");
+ s->opt[OPT_FLATBED_ONLY].type = SANE_TYPE_BOOL;
+ s->opt[OPT_FLATBED_ONLY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FLATBED_ONLY].size = sizeof (SANE_Word);
+ s->opt[OPT_FLATBED_ONLY].cap |=
+ (s->hw->adf.Status == ADF_STAT_NONE) ? SANE_CAP_INACTIVE : 0;
+ s->val[OPT_FLATBED_ONLY].w = SANE_FALSE;
+
+ /* "TPU" group: */
+ s->opt[OPT_TPU_GROUP].title = SANE_I18N("Transparency unit");
+ s->opt[OPT_TPU_GROUP].desc = "";
+ s->opt[OPT_TPU_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_TPU_GROUP].cap = 0;
+ s->opt[OPT_TPU_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_TPU_GROUP].cap |=
+ (s->hw->tpu.Status != TPU_STAT_NONE) ? 0 : SANE_CAP_INACTIVE;
+
+ /* Transparency Unit (FAU, Film Adapter Unit) */
+ s->opt[OPT_TPU_ON].name = "transparency-unit-on-off";
+ s->opt[OPT_TPU_ON].title = SANE_I18N("Transparency unit");
+ s->opt[OPT_TPU_ON].desc = SANE_I18N("Switch on/off the transparency unit "
+ "(FAU, film adapter unit)");
+ s->opt[OPT_TPU_ON].type = SANE_TYPE_BOOL;
+ s->opt[OPT_TPU_ON].unit = SANE_UNIT_NONE;
+ s->val[OPT_TPU_ON].w =
+ (s->hw->tpu.Status == TPU_STAT_ACTIVE) ? SANE_TRUE : SANE_FALSE;
+ s->opt[OPT_TPU_ON].cap |=
+ (s->hw->tpu.Status != TPU_STAT_NONE) ? 0 : SANE_CAP_INACTIVE;
+
+ s->opt[OPT_TPU_PN].name = "transparency-unit-negative-film";
+ s->opt[OPT_TPU_PN].title = SANE_I18N("Negative film");
+ s->opt[OPT_TPU_PN].desc = SANE_I18N("Positive or negative film");
+ s->opt[OPT_TPU_PN].type = SANE_TYPE_BOOL;
+ s->opt[OPT_TPU_PN].unit = SANE_UNIT_NONE;
+ s->val[OPT_TPU_PN].w = s->hw->tpu.PosNeg;
+ s->opt[OPT_TPU_PN].cap |=
+ (s->hw->tpu.Status == TPU_STAT_ACTIVE) ? 0 : SANE_CAP_INACTIVE;
+
+ /* density control mode */
+ s->opt[OPT_TPU_DCM].name = "TPMDC";
+ s->opt[OPT_TPU_DCM].title = SANE_I18N("Density control");
+ s->opt[OPT_TPU_DCM].desc = SANE_I18N("Set density control mode");
+ s->opt[OPT_TPU_DCM].type = SANE_TYPE_STRING;
+ s->opt[OPT_TPU_DCM].size = max_string_size (tpu_dc_mode_list);
+ s->opt[OPT_TPU_DCM].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_TPU_DCM].constraint.string_list = tpu_dc_mode_list;
+ s->val[OPT_TPU_DCM].s = strdup (tpu_dc_mode_list[s->hw->tpu.ControlMode]);
+ s->opt[OPT_TPU_DCM].cap |=
+ (s->hw->tpu.Status == TPU_STAT_ACTIVE) ? 0 : SANE_CAP_INACTIVE;
+
+ /* Transparency Ratio */
+ s->opt[OPT_TPU_TRANSPARENCY].name = "Transparency-Ratio";
+ s->opt[OPT_TPU_TRANSPARENCY].title = SANE_I18N("Transparency ratio");
+ s->opt[OPT_TPU_TRANSPARENCY].desc = "";
+ s->opt[OPT_TPU_TRANSPARENCY].type = SANE_TYPE_INT;
+ s->opt[OPT_TPU_TRANSPARENCY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_TPU_TRANSPARENCY].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TPU_TRANSPARENCY].constraint.range =
+ &s->hw->info.TPU_Transparency_range;
+ s->val[OPT_TPU_TRANSPARENCY].w = s->hw->tpu.Transparency;
+ s->opt[OPT_TPU_TRANSPARENCY].cap |=
+ (s->hw->tpu.Status == TPU_STAT_ACTIVE &&
+ s->hw->tpu.ControlMode == 3) ? 0 : SANE_CAP_INACTIVE;
+
+ /* Select Film type */
+ s->opt[OPT_TPU_FILMTYPE].name = "Filmtype";
+ s->opt[OPT_TPU_FILMTYPE].title = SANE_I18N("Select film type");
+ s->opt[OPT_TPU_FILMTYPE].desc = SANE_I18N("Select the film type");
+ s->opt[OPT_TPU_FILMTYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_TPU_FILMTYPE].size = max_string_size (tpu_filmtype_list);
+ s->opt[OPT_TPU_FILMTYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_TPU_FILMTYPE].constraint.string_list = tpu_filmtype_list;
+ s->val[OPT_TPU_FILMTYPE].s =
+ strdup (tpu_filmtype_list[s->hw->tpu.FilmType]);
+ s->opt[OPT_TPU_FILMTYPE].cap |=
+ (s->hw->tpu.Status == TPU_STAT_ACTIVE && s->hw->tpu.ControlMode == 1) ?
+ 0 : SANE_CAP_INACTIVE;
+
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ DBG (1, "<< init_options\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**************************************************************************/
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (1, ">> attach_one\n");
+ attach (dev, 0);
+ DBG (1, "<< attach_one\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**************************************************************************/
+
+static SANE_Status
+do_focus (CANON_Scanner * s)
+{
+ SANE_Status status;
+ u_char ebuf[74];
+ size_t buf_size;
+
+ DBG (3, "do_focus: sending GET FILM STATUS\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = 4;
+ status = get_film_status (s->fd, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "do_focus: GET FILM STATUS failed\n");
+ if (status == SANE_STATUS_UNSUPPORTED)
+ return (SANE_STATUS_GOOD);
+ else
+ {
+ DBG (1, "do_focus: ... for unknown reasons\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ DBG (3, "focus point before autofocus : %d\n", ebuf[3]);
+
+ status = execute_auto_focus (s->fd, s->val[OPT_AF].w,
+ (s->scanning_speed == 0 && !s->RIF && s->hw->info.model == CS2700),
+ (int) s->AE, s->val[OPT_FOCUS].w);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (7, "execute_auto_focus failed\n");
+ if (status == SANE_STATUS_UNSUPPORTED)
+ return (SANE_STATUS_GOOD);
+ else
+ {
+ DBG (1, "do_focus: ... for unknown reasons\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ }
+
+ DBG (3, "do_focus: sending GET FILM STATUS\n");
+ memset (ebuf, 0, sizeof (ebuf));
+ buf_size = 4;
+ status = get_film_status (s->fd, ebuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "do_focus: GET FILM STATUS failed\n");
+ if (status == SANE_STATUS_UNSUPPORTED)
+ return (SANE_STATUS_GOOD);
+ else
+ {
+ DBG (1, "do_focus: ... for unknown reasons\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else
+ DBG (3, "focus point after autofocus : %d\n", ebuf[3]);
+
+ return (SANE_STATUS_GOOD);
+}
+
+/**************************************************************************/
+
+#include "canon-sane.c"
diff --git a/backend/canon.conf.in b/backend/canon.conf.in
new file mode 100644
index 0000000..d1fe5b5
--- /dev/null
+++ b/backend/canon.conf.in
@@ -0,0 +1,2 @@
+#canon.conf
+scsi CANON IX
diff --git a/backend/canon.h b/backend/canon.h
new file mode 100644
index 0000000..34f527b
--- /dev/null
+++ b/backend/canon.h
@@ -0,0 +1,408 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 BYTEC GmbH Germany
+ Written by Helmut Koeberle, Email: helmut.koeberle@bytec.de
+ Modified by Manuel Panea <Manuel.Panea@rzg.mpg.de>,
+ Markus Mertinat <Markus.Mertinat@Physik.Uni-Augsburg.DE>,
+ and ULrich Deiters <ulrich.deiters@uni-koeln.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef canon_h
+#define canon_h 1
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+/* all the different possible model names. */
+#define FB1200S "IX-12015E "
+#define FB620S "IX-06035E "
+#define CS300 "IX-03035B "
+#define CS600 "IX-06015C "
+#define CS2700F "IX-27015C "
+#define IX_4025 "IX-4025 "
+#define IX_4015 "IX-4015 "
+#define IX_3010 "IX-3010 "
+
+#include <sys/types.h>
+
+#define AUTO_DOC_FEEDER_UNIT 0x01
+#define TRANSPARENCY_UNIT 0x02
+#define TRANSPARENCY_UNIT_FB1200 0x03
+#define SCAN_CONTROL_CONDITIONS 0x20
+#define SCAN_CONTROL_CON_FB1200 0x21
+#define ALL_SCAN_MODE_PAGES 0x3F
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+#define ADF_STAT_NONE 0
+#define ADF_STAT_INACTIVE 1
+#define ADF_STAT_ACTIVE 2
+#define ADF_STAT_DISABLED 3
+
+#define ADF_Status (4+2) /* byte positioning */
+#define ADF_Settings (4+3) /* in data block */
+
+#define ADF_NOT_PRESENT 0x01 /* bit selection */
+#define ADF_PROBLEM 0x0E /* from bytes in */
+#define ADF_PRIORITY 0x03 /* data block. */
+#define ADF_FEEDER 0x04 /* */
+
+#define TPU_STAT_NONE 0
+#define TPU_STAT_INACTIVE 1
+#define TPU_STAT_ACTIVE 2
+
+#define CS3_600 0 /* CanoScan 300/600 */
+#define CS2700 1 /* CanoScan 2700F */
+#define FB620 2 /* CanoScan FB620S */
+#define FS2710 3 /* CanoScan FS2710S */
+#define FB1200 4 /* CanoScan FB1200S */
+#define IX4015 5 /* IX-4015 */
+
+#ifndef MAX
+#define MAX(A,B) (((A) > (B))? (A) : (B))
+#endif
+#ifndef MIN
+#define MIN(A,B) (((A) < (B))? (A) : (B))
+#endif
+#ifndef SSIZE_MAX
+#define SSIZE_MAX LONG_MAX
+#endif
+
+typedef struct
+{
+ SANE_Int Status; /* Auto Document Feeder Unit Status */
+ SANE_Int Problem; /* ADF Problems list */
+ SANE_Int Priority; /* ADF Priority setting */
+ SANE_Int Feeder; /* ADF Feeder setting */
+
+}
+CANON_ADF;
+
+
+typedef struct
+{
+ SANE_Int Status; /* Transparency Unit Status */
+ SANE_Bool PosNeg; /* Negative/Positive Film */
+ SANE_Int Transparency; /* TPU Transparency */
+ SANE_Int ControlMode; /* TPU Density Control Mode */
+ SANE_Int FilmType; /* TPU Film Type */
+
+}
+CANON_TPU;
+
+
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_NEGATIVE, /* Reverse image format */
+ OPT_NEGATIVE_TYPE, /* Negative film type */
+ OPT_SCANNING_SPEED,
+
+ OPT_RESOLUTION_GROUP,
+ OPT_RESOLUTION_BIND,
+ OPT_HW_RESOLUTION_ONLY,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+
+ OPT_MIRROR,
+
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables? */
+ OPT_CUSTOM_GAMMA_BIND,
+ /* The gamma vectors MUST appear in the order gray, red, green, blue. */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_AE, /* Auto Exposure */
+
+ OPT_CALIBRATION_GROUP, /* Calibration for FB620S */
+ OPT_CALIBRATION_NOW, /* Execute Calibration now for FB620S */
+ OPT_SCANNER_SELF_DIAGNOSTIC, /* Self diagnostic for FB620S */
+ OPT_RESET_SCANNER, /* Reset scanner for FB620S */
+
+ OPT_EJECT_GROUP,
+ OPT_EJECT_AFTERSCAN,
+ OPT_EJECT_BEFOREEXIT,
+ OPT_EJECT_NOW,
+
+ OPT_FOCUS_GROUP,
+ OPT_AF, /* Auto Focus */
+ OPT_AF_ONCE, /* Auto Focus only once between ejects */
+ OPT_FOCUS, /* Manual focus position */
+
+ OPT_MARGINS_GROUP, /* scan margins */
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_COLORS_GROUP,
+ OPT_HNEGATIVE, /* Reverse image format */
+ OPT_BIND_HILO, /* Same values vor highlight and shadow
+ points for red, green, blue */
+ OPT_HILITE_R, /* highlight point for red */
+ OPT_SHADOW_R, /* shadow point for red */
+ OPT_HILITE_G, /* highlight point for green */
+ OPT_SHADOW_G, /* shadow point for green */
+ OPT_HILITE_B, /* highlight point for blue */
+ OPT_SHADOW_B, /* shadow point for blue */
+
+ OPT_ADF_GROUP, /* to allow display of options. */
+ OPT_FLATBED_ONLY, /* in case you have a sheetfeeder
+ but don't want to use it. */
+
+ OPT_TPU_GROUP,
+ OPT_TPU_ON,
+ OPT_TPU_PN,
+ OPT_TPU_DCM,
+ OPT_TPU_TRANSPARENCY,
+ OPT_TPU_FILMTYPE,
+
+ OPT_PREVIEW,
+
+ /* must come last: */
+ NUM_OPTIONS
+}
+CANON_Option;
+
+
+typedef struct CANON_Info
+{
+ int model;
+
+ SANE_Range xres_range;
+ SANE_Range yres_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+ SANE_Range threshold_range;
+ SANE_Range HiliteR_range;
+ SANE_Range ShadowR_range;
+ SANE_Range HiliteG_range;
+ SANE_Range ShadowG_range;
+ SANE_Range HiliteB_range;
+ SANE_Range ShadowB_range;
+ SANE_Range focus_range;
+
+ SANE_Range x_adf_range;
+ SANE_Range y_adf_range;
+ SANE_Int xres_default;
+ SANE_Int yres_default;
+ SANE_Int bmu;
+ SANE_Int mud;
+ SANE_Range TPU_Transparency_range;
+ SANE_Int TPU_Stat;
+
+ SANE_Bool can_focus; /* has got focus control */
+ SANE_Bool can_autoexpose; /* can do autoexposure by hardware */
+ SANE_Bool can_calibrate; /* has got calibration control */
+ SANE_Bool can_diagnose; /* has diagnostic command */
+ SANE_Bool can_eject; /* can eject medium */
+ SANE_Bool can_mirror; /* can mirror image by hardware */
+ SANE_Bool is_filmscanner;
+ SANE_Bool has_fixed_resolutions; /* only a finite number possible */
+}
+CANON_Info;
+
+typedef struct CANON_Device
+{
+ struct CANON_Device *next;
+ SANE_Device sane;
+ CANON_Info info;
+ CANON_ADF adf;
+ CANON_TPU tpu;
+}
+CANON_Device;
+
+typedef struct CANON_Scanner
+{
+ struct CANON_Scanner *next;
+ int fd;
+ CANON_Device *hw;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ char *sense_str; /* sense string */
+ SANE_Int gamma_table[4][256];
+ SANE_Parameters params;
+ SANE_Bool AF_NOW; /* To keep track of when to do AF */
+
+ SANE_Int xres;
+ SANE_Int yres;
+ SANE_Int ulx;
+ SANE_Int uly;
+ SANE_Int width;
+ SANE_Int length;
+ SANE_Int brightness;
+ SANE_Int contrast;
+ SANE_Int threshold;
+ SANE_Int image_composition;
+ SANE_Int bpp;
+ SANE_Bool RIF; /* Reverse Image Format */
+ SANE_Int negative_filmtype;
+ SANE_Int scanning_speed;
+ SANE_Bool GRC; /* Gray Response Curve */
+ SANE_Bool Mirror;
+ SANE_Bool AE; /* Auto Exposure */
+ SANE_Int HiliteR;
+ SANE_Int ShadowR;
+ SANE_Int HiliteG;
+ SANE_Int ShadowG;
+ SANE_Int HiliteB;
+ SANE_Int ShadowB;
+
+ /* 990320, ss: array for fixed resolutions */
+ SANE_Word xres_word_list[16];
+ SANE_Word yres_word_list[16];
+
+ SANE_Byte *inbuffer; /* modification for FB620S */
+ SANE_Byte *outbuffer; /* modification for FB620S */
+ SANE_Int buf_used; /* modification for FB620S */
+ SANE_Int buf_pos; /* modification for FB620S */
+ time_t time0; /* modification for FB620S */
+ time_t time1; /* modification for FB620S */
+ int switch_preview; /* modification for FB620S */
+ int reset_flag; /* modification for FB620S */
+
+ int tmpfile; /* modification for FB1200S */
+
+ size_t bytes_to_read;
+ int scanning;
+
+ u_char gamma_map[4][4096]; /* for FS2710S: */
+ int colour; /* index to gamma_map */
+ int auxbuf_len; /* size of auxiliary buffer */
+ u_char *auxbuf;
+}
+CANON_Scanner;
+
+static char *option_name[] = {
+ "OPT_NUM_OPTS",
+
+ "OPT_MODE_GROUP",
+ "OPT_MODE",
+ "OPT_NEGATIVE",
+ "OPT_NEGATIVE_TYPE",
+ "OPT_SCANNING_SPEED",
+
+ "OPT_RESOLUTION_GROUP",
+ "OPT_RESOLUTION_BIND",
+ "OPT_HW_RESOLUTION_ONLY",
+ "OPT_X_RESOLUTION",
+ "OPT_Y_RESOLUTION",
+
+ "OPT_ENHANCEMENT_GROUP",
+ "OPT_BRIGHTNESS",
+ "OPT_CONTRAST",
+ "OPT_THRESHOLD",
+
+ "OPT_MIRROR",
+
+ "OPT_CUSTOM_GAMMA",
+ "OPT_CUSTOM_GAMMA_BIND",
+ "OPT_GAMMA_VECTOR",
+ "OPT_GAMMA_VECTOR_R",
+ "OPT_GAMMA_VECTOR_G",
+ "OPT_GAMMA_VECTOR_B",
+ "OPT_AE",
+
+ "OPT_CALIBRATION_GROUP",
+ "OPT_CALIBRATION_NOW",
+ "OPT_SCANNER_SELF_DIAGNOSTIC",
+ "OPT_RESET_SCANNER",
+
+ "OPT_EJECT_GROUP",
+ "OPT_EJECT_AFTERSCAN",
+ "OPT_EJECT_BEFOREEXIT",
+ "OPT_EJECT_NOW",
+
+ "OPT_FOCUS_GROUP",
+ "OPT_AF",
+ "OPT_AF_ONCE",
+ "OPT_FOCUS",
+
+ "OPT_MARGINS_GROUP",
+ "OPT_TL_X",
+ "OPT_TL_Y",
+ "OPT_BR_X",
+ "OPT_BR_Y",
+
+ "OPT_COLORS_GROUP",
+ "OPT_HNEGATIVE",
+ "OPT_BIND_HILO",
+
+ "OPT_HILITE_R",
+ "OPT_SHADOW_R",
+ "OPT_HILITE_G",
+ "OPT_SHADOW_G",
+ "OPT_HILITE_B",
+ "OPT_SHADOW_B",
+
+ "OPT_ADF_GROUP",
+ "OPT_FLATBED_ONLY",
+
+ "OPT_TPU_GROUP",
+ "OPT_TPU_ON",
+ "OPT_TPU_PN",
+ "OPT_TPU_DCM",
+ "OPT_TPU_TRANSPARENCY",
+ "OPT_TPU_FILMTYPE",
+
+ "OPT_PREVIEW",
+
+ "NUM_OPTIONS"
+};
+
+
+
+
+#endif /* not canon_h */
diff --git a/backend/canon630u-common.c b/backend/canon630u-common.c
new file mode 100644
index 0000000..4417141
--- /dev/null
+++ b/backend/canon630u-common.c
@@ -0,0 +1,1656 @@
+/*
+ (c) 2001,2002 Nathan Rutman nathan@gordian.com 10/17/01
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ Communication, calibration, and scanning with the Canon CanoScan FB630U
+ flatbed scanner under linux.
+
+ Reworked into SANE-compatible format.
+
+ The usb-parallel port interface chip is GL640usb, on the far side of
+ which is an LM9830 parallel-port scanner-on-a-chip.
+
+ This code has not been tested on anything other than Linux/i386.
+*/
+
+#include <errno.h>
+#include <fcntl.h> /* open */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h> /* usleep */
+#include <time.h>
+#include <math.h> /* exp() */
+#ifdef HAVE_OS2_H
+#include <sys/types.h> /* mode_t */
+#endif
+#include <sys/stat.h>
+#include "lm9830.h"
+
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_RECIP_DEVICE 0x00
+#define USB_DIR_OUT 0x00
+#define USB_DIR_IN 0x80
+
+/* Assign status and verify a good return code */
+#define CHK(A) {if( (status = A) != SANE_STATUS_GOOD ) { \
+ DBG( 1, "Failure on line of %s: %d\n", __FILE__, \
+ __LINE__ ); return A; }}
+
+
+typedef SANE_Byte byte;
+
+
+/*****************************************************
+ GL640 communication primitives
+ Provides I/O routines to Genesys Logic GL640USB USB-IEEE1284 parallel
+ port bridge. Used in HP3300c, Canon FB630u.
+******************************************************/
+
+/* Register codes for the bridge. These are NOT the registers for the
+ scanner chip on the other side of the bridge. */
+typedef enum
+{
+ GL640_BULK_SETUP = 0x82,
+ GL640_EPP_ADDR = 0x83,
+ GL640_EPP_DATA_READ = 0x84,
+ GL640_EPP_DATA_WRITE = 0x85,
+ GL640_SPP_STATUS = 0x86,
+ GL640_SPP_CONTROL = 0x87,
+ GL640_SPP_DATA = 0x88,
+ GL640_GPIO_OE = 0x89,
+ GL640_GPIO_READ = 0x8a,
+ GL640_GPIO_WRITE = 0x8b
+}
+GL640_Request;
+
+/* Write to the usb-parallel port bridge. */
+static SANE_Status
+gl640WriteControl (int fd, GL640_Request req, byte * data, unsigned int size)
+{
+ SANE_Status status;
+ status = sanei_usb_control_msg (fd,
+ /* rqttype */ USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE | USB_DIR_OUT /*0x40? */ ,
+ /* rqt */ (size > 1) ? 0x04 : 0x0C,
+ /* val */ (SANE_Int) req,
+ /* ind */ 0,
+ /* len */ size,
+ /* dat */ data);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "gl640WriteControl error\n");
+ return status;
+}
+
+
+/* Read from the usb-parallel port bridge. */
+static SANE_Status
+gl640ReadControl (int fd, GL640_Request req, byte * data, unsigned int size)
+{
+ SANE_Status status;
+ status = sanei_usb_control_msg (fd,
+ /* rqttype */ USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE | USB_DIR_IN /*0xc0? */ ,
+ /* rqt */ (size > 1) ? 0x04 : 0x0C,
+ /* val */ (SANE_Int) req,
+ /* ind */ 0,
+ /* len */ size,
+ /* dat */ data);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "gl640ReadControl error\n");
+ return status;
+}
+
+
+/* Wrappers to read or write a single byte to the bridge */
+static inline SANE_Status
+gl640WriteReq (int fd, GL640_Request req, byte data)
+{
+ return gl640WriteControl (fd, req, &data, 1);
+}
+
+static inline SANE_Status
+gl640ReadReq (int fd, GL640_Request req, byte * data)
+{
+ return gl640ReadControl (fd, req, data, 1);
+}
+
+
+/* Write USB bulk data
+ setup is an apparently scanner-specific sequence:
+ {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00}
+ hp3400: setup[1] = 0x01
+ fb630u: setup[2] = 0x80
+*/
+static SANE_Status
+gl640WriteBulk (int fd, byte * setup, byte * data, size_t size)
+{
+ SANE_Status status;
+ setup[0] = 1;
+ setup[4] = (size) & 0xFF;
+ setup[5] = (size >> 8) & 0xFF;
+
+ CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8));
+
+ status = sanei_usb_write_bulk (fd, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "gl640WriteBulk error\n");
+
+ return status;
+}
+
+
+/* Read USB bulk data
+ setup is an apparently scanner-specific sequence:
+ {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00}
+ fb630u: setup[2] = 0x80
+*/
+static SANE_Status
+gl640ReadBulk (int fd, byte * setup, byte * data, size_t size)
+{
+ SANE_Status status;
+ setup[0] = 0;
+ setup[4] = (size) & 0xFF;
+ setup[5] = (size >> 8) & 0xFF;
+
+ CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8));
+
+ status = sanei_usb_read_bulk (fd, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "gl640ReadBulk error\n");
+
+ return status;
+}
+
+
+/*****************************************************
+ LM9830 communication primitives
+ parallel-port scanner-on-a-chip.
+******************************************************/
+
+/* write 1 byte to a LM9830 register address */
+static SANE_Status
+write_byte (int fd, byte addr, byte val)
+{
+ SANE_Status status;
+ DBG (14, "write_byte(fd, 0x%02x, 0x%02x);\n", addr, val);
+ CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
+ CHK (gl640WriteReq (fd, GL640_EPP_DATA_WRITE, val));
+ return status;
+}
+
+
+/* read 1 byte from a LM9830 register address */
+static SANE_Status
+read_byte (int fd, byte addr, byte * val)
+{
+ SANE_Status status;
+ CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
+ CHK (gl640ReadReq (fd, GL640_EPP_DATA_READ, val));
+ DBG (14, "read_byte(fd, 0x%02x, &result); /* got %02x */\n", addr, *val);
+ return status;
+}
+
+
+static byte bulk_setup_data[] = { 0, 0, 0x80, 0, 0, 0, 0, 0 };
+
+/* Bulk write */
+static SANE_Status
+write_bulk (int fd, unsigned int addr, void *src, size_t count)
+{
+ SANE_Status status;
+
+ DBG (13, "write_bulk(fd, 0x%02x, buf, 0x%04lx);\n", addr, (u_long) count);
+
+ if (!src)
+ {
+ DBG (1, "write_bulk: bad src\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* destination address */
+ CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
+ /* write */
+ CHK (gl640WriteBulk (fd, bulk_setup_data, src, count));
+ return status;
+}
+
+
+/* Bulk read */
+static SANE_Status
+read_bulk (int fd, unsigned int addr, void *dst, size_t count)
+{
+ SANE_Status status;
+
+ DBG (13, "read_bulk(fd, 0x%02x, buf, 0x%04lx);\n", addr, (u_long) count);
+
+ if (!dst)
+ {
+ DBG (1, "read_bulk: bad dest\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* destination address */
+ CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
+ /* read */
+ CHK (gl640ReadBulk (fd, bulk_setup_data, dst, count));
+ return status;
+}
+
+
+
+/*****************************************************
+ useful macro routines
+******************************************************/
+
+/* write a 16-bit int to two sequential registers */
+static SANE_Status
+write_word (int fd, unsigned int addr, unsigned int data)
+{
+ SANE_Status status;
+ /* MSB */
+ CHK (write_byte (fd, addr, (data >> 8) & 0xff));
+ /* LSB */
+ CHK (write_byte (fd, addr + 1, data & 0xff));
+ return status;
+}
+
+
+/* write multiple bytes, one at a time (non-bulk) */
+static SANE_Status
+write_many (int fd, unsigned int addr, void *src, size_t count)
+{
+ SANE_Status status;
+ size_t i;
+
+ DBG (14, "multi write %lu\n", (u_long) count);
+ for (i = 0; i < count; i++)
+ {
+ DBG (15, " %04lx:%02x", (u_long) (addr + i), ((byte *) src)[i]);
+ status = write_byte (fd, addr + i, ((byte *) src)[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (15, "\n");
+ return status;
+ }
+ }
+ DBG (15, "\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/* read multiple bytes, one at a time (non-bulk) */
+static SANE_Status
+read_many (int fd, unsigned int addr, void *dst, size_t count)
+{
+ SANE_Status status;
+ size_t i;
+ byte val;
+
+ DBG (14, "multi read %lu\n", (u_long) count);
+ for (i = 0; i < count; i++)
+ {
+ status = read_byte (fd, addr + i, &val);
+ ((byte *) dst)[i] = val;
+ DBG (15, " %04lx:%02x", (u_long) (addr + i), ((byte *) dst)[i]);
+ /* on err, return number of success */
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (15, "\n");
+ return status;
+ }
+ }
+ DBG (15, "\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Poll addr until result & mask = val */
+static int
+read_poll_flag (int fd,
+ unsigned int addr, unsigned int mask, unsigned int val)
+{
+ SANE_Status status;
+ byte result = 0;
+ time_t start_time = time (NULL);
+
+ DBG (12, "read_poll_flag...\n");
+ do
+ {
+ status = read_byte (fd, addr, &result);
+ if (status != SANE_STATUS_GOOD)
+ return -1;
+ /* Give it a minute */
+ if ((time (NULL) - start_time) > 60)
+ {
+ DBG (1, "read_poll_flag: timed out (%d)\n", result);
+ return -1;
+ }
+ usleep (100000);
+ }
+ while ((result & mask) != val);
+ return result;
+}
+
+
+/* Keep reading addr until results >= min */
+static int
+read_poll_min (int fd, unsigned int addr, unsigned int min)
+{
+ SANE_Status status;
+ byte result;
+ time_t start_time = time (NULL);
+
+ DBG (12, "waiting...\n");
+ do
+ {
+ status = read_byte (fd, addr, &result);
+ if (status != SANE_STATUS_GOOD)
+ return -1;
+ /* Give it a minute */
+ if ((time (NULL) - start_time) > 60)
+ {
+ DBG (1, "read_poll_min: timed out (%d < %d)\n", result, min);
+ return -1;
+ }
+ /* no sleep here, or calibration gets unhappy. */
+ }
+ while (result < min);
+ return result;
+}
+
+
+/* Bulk read "ks" kilobytes + "remainder" bytes of data, to a buffer if the
+ buffer is valid. */
+static int
+read_bulk_size (int fd, int ks, int remainder, byte * dest, int destsize)
+{
+ byte *buf;
+ int bytes = (ks - 1) * 1024 + remainder;
+ int dropdata = ((dest == 0) || (destsize < bytes));
+
+ if (bytes < 0)
+ {
+ DBG (1, "read_bulk_size: invalid size %02x (%d)\n", ks, bytes);
+ return -1;
+ }
+ if (destsize && (destsize < bytes))
+ {
+ DBG (3, "read_bulk_size: more data than buffer (%d/%d)\n",
+ destsize, bytes);
+ bytes = destsize;
+ }
+
+ if (bytes == 0)
+ return 0;
+
+ if (dropdata)
+ {
+ buf = malloc (bytes);
+ DBG (3, " ignoring data ");
+ }
+ else
+ buf = dest;
+
+ read_bulk (fd, 0x00, buf, bytes);
+
+ if (dropdata)
+ free (buf);
+ return bytes;
+}
+
+
+
+/*****************************************************
+
+ fb630u calibration and scan
+
+******************************************************/
+
+/* data structures and constants */
+
+typedef struct CANON_Handle
+{
+ int fd; /* scanner fd */
+ int x1, x2, y1, y2; /* in pixels, 600 dpi */
+ int width, height; /* at scan resolution */
+ int resolution; /* dpi */
+ char *fname; /* output file name */
+ FILE *fp; /* output file pointer (for reading) */
+ char *buf, *ptr; /* data buffer */
+ unsigned char gain; /* static analog gain, 0 - 31 */
+ double gamma; /* gamma correction */
+ int flags;
+#define FLG_GRAY 0x01 /* grayscale */
+#define FLG_FORCE_CAL 0x02 /* force calibration */
+#define FLG_BUF 0x04 /* save scan to buffer instead of file */
+#define FLG_NO_INTERLEAVE 0x08 /* don't interleave r,g,b pixels; leave them
+ in row format */
+#define FLG_PPM_HEADER 0x10 /* include PPM header in scan file */
+}
+CANON_Handle;
+
+
+/* offset/gain calibration file name */
+#define CAL_FILE_OGN "/tmp/canon.cal"
+
+/* at 600 dpi */
+#define CANON_MAX_WIDTH 5100 /* 8.5in */
+/* this may not be right */
+#define CANON_MAX_HEIGHT 7000 /* 11.66in */
+
+/* scanline end-of-line data byte, returned after each r,g,b segment,
+ specific to the FB630u */
+#define SCANLINE_END 0x0c
+
+
+static const byte seq002[] =
+ { /*r08 */ 0x04, /*300 dpi */ 0x1a, 0x00, 0x0d, 0x4c, 0x2f, 0x00, 0x01,
+/*r10 */ 0x07, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x25, 0x00,
+0x4b, /*r20 */ 0x15, 0xe0, /*data px start */ 0x00, 0x4b, /*data px end */ 0x14, 0x37, 0x15, 0x00 };
+
+static const byte seq003[] =
+ { 0x02, 0x00, 0x00, /*lights out */ 0x03, 0xff, 0x00, 0x01, 0x03, 0xff,
+0x00, 0x01, 0x03, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06,
+0x1d, 0x00, 0x13, 0x04, 0x1a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x3c, 0x35, 0x94,
+0x00, 0x10, 0x08, 0x3f, 0x2b, 0x91, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00 };
+
+
+/* Scanner init, called at calibration and scan time. Returns 1 if this
+ was the first time the scanner was plugged in, 0 afterward, and
+ -1 on error. */
+static int
+init (int fd)
+{
+ byte result, rv;
+
+ if (gl640WriteReq (fd, GL640_GPIO_OE, 0x71) != SANE_STATUS_GOOD) {
+ DBG(1, "Initial write request failed.\n");
+ return -1;
+ }
+ /* Gets 0x04 or 0x05 first run, gets 0x64 subsequent runs. */
+ if (gl640ReadReq (fd, GL640_GPIO_READ, &rv) != SANE_STATUS_GOOD) {
+ DBG(1, "Initial read request failed.\n");
+ return -1;
+ }
+ gl640WriteReq (fd, GL640_GPIO_OE, 0x70);
+
+ DBG (2, "init query: %x\n", rv);
+ if (rv != 0x64)
+ {
+ gl640WriteReq (fd, GL640_GPIO_WRITE, 0x00);
+ gl640WriteReq (fd, GL640_GPIO_WRITE, 0x40);
+ }
+
+ gl640WriteReq (fd, GL640_SPP_DATA, 0x99);
+ gl640WriteReq (fd, GL640_SPP_DATA, 0x66);
+ gl640WriteReq (fd, GL640_SPP_DATA, 0xcc);
+ gl640WriteReq (fd, GL640_SPP_DATA, 0x33);
+ /* parallel port setting */
+ write_byte (fd, PARALLEL_PORT, 0x06);
+ /* sensor control settings */
+ write_byte (fd, 0x0b, 0x0d);
+ write_byte (fd, 0x0c, 0x4c);
+ write_byte (fd, 0x0d, 0x2f);
+ read_byte (fd, 0x0b, &result); /* wants 0d */
+ read_byte (fd, 0x0c, &result); /* wants 4c */
+ read_byte (fd, 0x0d, &result); /* wants 2f */
+ /* parallel port noise filter */
+ write_byte (fd, 0x70, 0x73);
+
+ DBG (2, "init post-reset: %x\n", rv);
+ /* Returns 1 if this was the first time the scanner was plugged in. */
+ return (rv != 0x64);
+}
+
+
+/* Turn off the lamps */
+static void
+lights_out (int fd)
+{
+ write_word (fd, LAMP_R_ON, 0x3fff);
+ write_word (fd, LAMP_R_OFF, 0x0001);
+ write_word (fd, LAMP_G_ON, 0x3fff);
+ write_word (fd, LAMP_G_OFF, 0x0001);
+ write_word (fd, LAMP_B_ON, 0x3fff);
+ write_word (fd, LAMP_B_OFF, 0x0001);
+}
+
+
+/* Do the scan and save the resulting image as r,g,b interleaved PPM
+ file. */
+static SANE_Status
+do_scan (CANON_Handle * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int numbytes, datasize, level = 0, line = 0, pixel = 0;
+ byte *buf, *ptr, *redptr;
+ FILE *fp;
+
+#define BUFSIZE 0xf000
+ buf = malloc (BUFSIZE);
+ if (!buf)
+ return SANE_STATUS_NO_MEM;
+
+ if (s->flags & FLG_BUF)
+ {
+ /* read the whole thing into buf */
+ if (!s->buf)
+ return SANE_STATUS_NO_MEM;
+ s->ptr = s->buf;
+ fp = NULL;
+ }
+ else
+ {
+ fp = fopen (s->fname, "w");
+ if (!fp)
+ {
+ free (buf);
+ DBG (1, "err:%s when opening %s\n", strerror (errno), s->fname);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (fp && (s->flags & FLG_PPM_HEADER))
+ /* PPM format header */
+ fprintf (fp, "P6\n%d %d\n255\n", s->width, s->height);
+
+ /* lights off */
+ write_byte (s->fd, COMMAND, 0x08);
+ /* lights on */
+ write_byte (s->fd, COMMAND, 0x00);
+ /* begin scan */
+ write_byte (s->fd, COMMAND, 0x03);
+
+ ptr = redptr = buf;
+ while (line < s->height)
+ {
+ datasize = read_poll_min (s->fd, IMAGE_DATA_AVAIL, 2);
+ if (datasize < 0)
+ {
+ DBG (1, "no data\n");
+ break;
+ }
+ DBG (12, "scan line %d %dk\n", line, datasize - 1);
+ /* Read may cause scan head to move */
+ numbytes = read_bulk_size (s->fd, datasize, 0, ptr, BUFSIZE - level);
+ if (numbytes < 0)
+ {
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+ /* Data coming back is "width" bytes Red data followed by 0x0c,
+ width bytes Green, 0x0c, width bytes Blue, 0x0c, repeat for
+ "height" lines. */
+ if (s->flags & FLG_NO_INTERLEAVE)
+ {
+ /* number of full lines */
+ line += (numbytes + level) / (s->width * 3);
+ /* remainder (partial line) */
+ level = (numbytes + level) % (s->width * 3);
+ /* but if last line, don't store extra */
+ if (line >= s->height)
+ numbytes -= (line - s->height) * s->width * 3 + level;
+ if (fp)
+ fwrite (buf, 1, numbytes, fp);
+ else
+ {
+ memcpy (s->ptr, buf, numbytes);
+ s->ptr += numbytes;
+ }
+ }
+ else
+ {
+ /* Contorsions to convert data from line-by-line RGB to
+ byte-by-byte RGB, without reading in the whole buffer first.
+ We use the sliding window redptr with the temp buffer buf. */
+ ptr += numbytes; /* point to the end of data */
+ /* while we have RGB triple data */
+ while (redptr + s->width + s->width <= ptr)
+ {
+ if (*redptr == SCANLINE_END)
+ DBG (13, "-%d- ", pixel);
+ if (fp)
+ {
+ /* for PPM binary (P6), 3-byte RGB pixel */
+ fwrite (redptr, 1, 1, fp); /* Red */
+ fwrite (redptr + s->width, 1, 1, fp); /* Green */
+ fwrite (redptr + s->width + s->width, 1, 1, fp); /* Blue */
+ /* for PPM ascii (P3)
+ fprintf(fp, "%3d %3d %3d\n", *redptr,
+ *(redptr + s->width),
+ *(redptr + s->width + s->width));
+ */
+ }
+ else
+ {
+ /* R */ *s->ptr = *redptr;
+ s->ptr++;
+ /* G */ *s->ptr = *(redptr + s->width);
+ s->ptr++;
+ /* B */ *s->ptr = *(redptr + s->width + s->width);
+ s->ptr++;
+ }
+ redptr++;
+ pixel++;
+ if (pixel && !(pixel % s->width))
+ {
+ /* end of a line, move redptr to the next Red section */
+ line++;
+ redptr += s->width + s->width;
+#if 0
+ /* progress */
+ printf ("%2d%%\r", line * 100 / s->height);
+ fflush (stdout);
+#endif
+ /* don't record any extra */
+ if (line >= s->height)
+ break;
+ }
+ }
+ /* keep the extra around for next time */
+ level = ptr - redptr;
+ if (level < 0)
+ level = 0;
+ memmove (buf, redptr, level);
+ ptr = buf + level;
+ redptr = buf;
+ }
+ }
+
+ if (fp)
+ {
+ fclose (fp);
+ DBG (6, "created scan file %s\n", s->fname);
+ }
+ free (buf);
+ DBG (6, "%d lines, %d pixels, %d extra bytes\n", line, pixel, level);
+
+ /* motor off */
+ write_byte (s->fd, COMMAND, 0x00);
+
+ return status;
+}
+
+
+static int
+wait_for_return (int fd)
+{
+ return read_poll_flag (fd, STATUS, STATUS_HOME, STATUS_HOME);
+}
+
+
+static SANE_Status compute_ogn (char *calfilename);
+
+
+/* This is the calibration rountine Win2k goes through when the scanner is
+ first plugged in.
+ Original usb trace from Win2k with USBSnoopy ("usb sniffer for w2k"
+ http://benoit.papillault.free.fr/speedtouch/sniff-2000.en.php3)
+ */
+static int
+plugin_cal (CANON_Handle * s)
+{
+ SANE_Status status;
+ unsigned int temp;
+ byte result;
+ byte *buf;
+ int fd = s->fd;
+
+ DBG (6, "Calibrating\n");
+
+ /* reserved? */
+ read_byte (fd, 0x69, &result); /* wants 02 */
+
+ /* parallel port setting */
+ write_byte (fd, PARALLEL_PORT, 0x06);
+
+ write_many (fd, 0x08, (byte *) seq002, sizeof (seq002));
+ /* addr 0x28 isn't written */
+ write_many (fd, 0x29, (byte *) seq003, sizeof (seq003));
+ /* Verification */
+ buf = malloc (0x400);
+ read_many (fd, 0x08, buf, sizeof (seq002));
+ if (memcmp (seq002, buf, sizeof (seq002)))
+ DBG (1, "seq002 verification error\n");
+ /* addr 0x28 isn't read */
+ read_many (fd, 0x29, buf, sizeof (seq003));
+ if (memcmp (seq003, buf, sizeof (seq003)))
+ DBG (1, "seq003 verification error\n");
+
+ /* parallel port noise filter */
+ write_byte (fd, 0x70, 0x73);
+
+ lights_out (fd);
+
+ /* Home motor */
+ read_byte (fd, STATUS, &result); /* wants 2f or 2d */
+ if (!(result & STATUS_HOME) /*0x2d */ )
+ write_byte (fd, COMMAND, 0x02);
+
+ wait_for_return (fd);
+
+ /* Motor forward */
+ write_byte (fd, COMMAND, 0x01);
+ usleep (600000);
+ read_byte (fd, STATUS, &result); /* wants 0c or 2c */
+ read_byte (fd, STATUS, &result); /* wants 0c */
+ /* Return home */
+ write_byte (fd, COMMAND, 0x02);
+
+ /* Gamma tables */
+ /* Linear gamma */
+ for (temp = 0; temp < 0x0400; temp++)
+ buf[temp] = temp / 4;
+ /* Gamma Red */
+ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 0x0400);
+ /* Gamma Green */
+ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 0x0400);
+ /* Gamma Blue */
+ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 0x0400);
+
+ /* Read back gamma tables. I suppose I should check results... */
+ /* Gamma Red */
+ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_READ);
+ read_bulk (fd, DATAPORT, buf, 0x0400);
+ /* Gamma Green */
+ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_READ);
+ read_bulk (fd, DATAPORT, buf, 0x0400);
+ /* Gamma Blue */
+ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_READ);
+ read_bulk (fd, DATAPORT, buf, 0x0400);
+ free (buf);
+
+ /* Make sure STATUS_HOME */
+ read_byte (fd, STATUS, &result); /* wants 0e */
+ /* stepper forward */
+ write_byte (fd, COMMAND, 0x01);
+ read_byte (fd, STATUS, &result); /* wants 0c */
+ /* not sure if this rigid read/write pattern is required */
+ read_byte (fd, CLOCK_DIV, &result); /* wants 04 */
+ write_byte (fd, CLOCK_DIV, 0x04);
+ read_byte (fd, STEP_SIZE, &result); /* wants 04 */
+ write_byte (fd, STEP_SIZE, 0x3f);
+ read_byte (fd, 0x47, &result); /* wants 1a */
+ write_byte (fd, 0x47, 0xff);
+ read_byte (fd, FAST_STEP, &result); /* wants 01 */
+ write_byte (fd, FAST_STEP, 0x01);
+ read_byte (fd, 0x49, &result); /* wants 04 */
+ write_byte (fd, 0x49, 0x04);
+ read_byte (fd, SKIP_STEPS, &result); /* wants 00 */
+ write_byte (fd, SKIP_STEPS, 0x00);
+ read_byte (fd, 0x4b, &result); /* wants 00 */
+ write_byte (fd, 0x4b, 0xc8);
+ read_byte (fd, BUFFER_LIMIT, &result); /* wants 57 */
+ write_byte (fd, BUFFER_LIMIT, 0x04);
+ read_byte (fd, BUFFER_RESUME, &result); /* wants 02 */
+ write_byte (fd, BUFFER_RESUME, 0x02);
+ read_byte (fd, REVERSE_STEPS, &result); /* wants 00 */
+ write_byte (fd, REVERSE_STEPS, 0x00);
+ write_byte (fd, STEP_PWM, 0x1f);
+
+ /* Reset motor */
+ write_byte (fd, COMMAND, 0x08);
+ write_byte (fd, COMMAND, 0x00);
+ /* Scan */
+ write_byte (fd, COMMAND, 0x03);
+ /* Wants 02 or 03, gets a bunch of 0's first */
+ read_poll_min (fd, IMAGE_DATA_AVAIL, 2);
+ write_byte (fd, COMMAND, 0x00);
+
+ write_byte (fd, STEP_PWM, 0x3f);
+ write_byte (fd, CLOCK_DIV, 0x04);
+ /* 300 dpi */
+ write_word (fd, STEP_SIZE, 0x041a);
+ write_word (fd, FAST_STEP, 0x0104);
+ /* Don't skip the black/white calibration area at the bottom of the
+ scanner. */
+ write_word (fd, SKIP_STEPS, 0x0000);
+ write_byte (fd, BUFFER_LIMIT, 0x57);
+ write_byte (fd, BUFFER_RESUME, 0x02);
+ write_byte (fd, REVERSE_STEPS, 0x00);
+ write_byte (fd, BUFFER_LIMIT, 0x09);
+ write_byte (fd, STEP_PWM, 0x1f);
+ read_byte (fd, MICROSTEP, &result); /* wants 13, active */
+ write_byte (fd, MICROSTEP, 0x03 /* tristate */ );
+
+ /* Calibration data taken under 3 different lighting conditions */
+ /* dark */
+ write_word (fd, LAMP_R_ON, 0x0017);
+ write_word (fd, LAMP_R_OFF, 0x0100);
+ write_word (fd, LAMP_G_ON, 0x0017);
+ write_word (fd, LAMP_G_OFF, 0x0100);
+ write_word (fd, LAMP_B_ON, 0x0017);
+ write_word (fd, LAMP_B_OFF, 0x0100);
+ /* coming in, we've got 300dpi,
+ data px start : 0x004b
+ data px end : 0x1437 for a total of 5100(13ec) 600-dpi pixels,
+ (8.5 inches) or 2550 300-dpi pixels (7653 bytes).
+ Interestingly, the scan head never moves, no matter how many rows
+ are read. */
+ s->width = 2551;
+ s->height = 1;
+ s->flags = FLG_BUF | FLG_NO_INTERLEAVE;
+ s->buf = malloc (s->width * s->height * 3);
+ /* FIXME do something with this data */
+ CHK (do_scan (s));
+
+ /* Lighting */
+ /* medium */
+ write_word (fd, LAMP_R_ON, 0x0017);
+ write_word (fd, LAMP_R_OFF, 0x0200);
+ write_word (fd, LAMP_G_ON, 0x0017);
+ write_word (fd, LAMP_G_OFF, 0x01d7 /* also 01db */ );
+ write_word (fd, LAMP_B_ON, 0x0017);
+ write_word (fd, LAMP_B_OFF, 0x01af /* also 01b2 */ );
+ /* FIXME do something with this data */
+ CHK (do_scan (s));
+
+ /* Lighting */
+ /* bright */
+ write_word (fd, LAMP_R_ON, 0x0017);
+ write_word (fd, LAMP_R_OFF, 0x0e8e /* also 1040 */ );
+ write_word (fd, LAMP_G_ON, 0x0017);
+ write_word (fd, LAMP_G_OFF, 0x0753 /* also 0718 */ );
+ write_word (fd, LAMP_B_ON, 0x0017);
+ write_word (fd, LAMP_B_OFF, 0x03f8 /* also 040d */ );
+ /* FIXME do something with this data */
+ CHK (do_scan (s));
+ free (s->buf);
+ s->buf = NULL;
+
+ /* The trace gets a little iffy from here on out since the log files
+ are missing different urb's. This is kind of a puzzled-out
+ compilation. */
+
+ write_byte (fd, MICROSTEP, 0x13 /* pins active */ );
+ write_byte (fd, STEP_PWM, 0x3f);
+ read_byte (fd, STATUS, &result); /* wants 0c */
+
+ /* Stepper home */
+ write_byte (fd, COMMAND, 0x02);
+ /* Step size */
+ write_word (fd, STEP_SIZE, 0x041a /* 300 dpi */ );
+ /* Skip steps */
+ write_word (fd, SKIP_STEPS, 0x0000);
+ /* Pause buffer levels */
+ write_byte (fd, BUFFER_LIMIT, 0x57);
+ /* Resume buffer levels */
+ write_byte (fd, BUFFER_RESUME, 0x02);
+
+ wait_for_return (fd);
+ /* stepper forward small */
+ write_byte (fd, COMMAND, 0x01);
+ read_byte (fd, STATUS, &result); /* wants 0c */
+ usleep (200000);
+ write_byte (fd, STEP_PWM, 0x1f);
+
+ /* Read in cal strip at bottom of scanner (to adjust gain/offset
+ tables. Note that this isn't the brightest lighting condition.)
+ At 300 dpi: black rows 0-25; white rows 30-75; beginning
+ of glass 90.
+ This produces 574k of data, so save it to a temp file. */
+ if (!s->fname)
+ {
+ DBG (1, "No temp filename!\n");
+ s->fname = strdup ("/tmp/cal.XXXXXX");
+ mktemp (s->fname);
+ }
+ s->width = 2551;
+ s->height = 75;
+ s->flags = FLG_PPM_HEADER | FLG_NO_INTERLEAVE;
+ CHK (do_scan (s));
+ compute_ogn (s->fname);
+ unlink (s->fname);
+
+ write_byte (fd, STEP_PWM, 0x3f);
+ /* stepper home */
+ write_byte (fd, COMMAND, 0x02);
+
+ /* discard the remaining data */
+ read_byte (fd, IMAGE_DATA_AVAIL, &result); /* wants 42,4c */
+ if (result > 1)
+ {
+ read_bulk_size (fd, result, 0, 0, 0);
+ DBG (11, "read %dk extra\n", result);
+ }
+ read_byte (fd, 0x69, &result); /* wants 02 */
+ write_byte (fd, 0x69, 0x0a);
+
+ lights_out (fd);
+ init (fd);
+
+#if 0
+ /* Repeatedly send this every 1 second. Button scan? FIXME */
+ gl640ReadReq (fd, GL640_GPIO_READ, &result); /* wants 00 */
+#endif
+
+ return 0;
+}
+
+
+/* The number of regions in the calibration strip (black & white). */
+#define NREGIONS 2
+
+/* Compute the offset/gain table from the calibration strip. This is
+ somewhat more complicated than necessary because I don't hard-code the
+ strip widths; I try to figure out the regions based on the scan data.
+ Theoretically, the region-finder should work for any number of distinct
+ regions (but there are only 2 on this scanner.)
+ This produces the CAL_FILE_OGN file, the final offset/gain table. */
+static SANE_Status
+compute_ogn (char *calfilename)
+{
+ byte *linebuf, *oldline, *newline;
+ mode_t oldmask;
+ FILE *fp;
+ int width, height, nlines = 0, region = -1, i, transition = 1, badcnt;
+ int pct;
+ int reglines[NREGIONS];
+ float *avg;
+ float max_range[3], tmp1, tmp2;
+
+ fp = fopen (calfilename, "r");
+ if (!fp)
+ {
+ DBG (1, "open %s\n", calfilename);
+ return SANE_STATUS_EOF;
+ }
+ fscanf (fp, "P6 %d %d %*d ", &width, &height);
+ DBG (12, "cal file %s %dx%d\n", calfilename, width, height);
+ width = width * 3; /* 1 byte each of r, g, b */
+ /* make a buffer holding 2 lines of data */
+ linebuf = calloc (width * 2, sizeof (linebuf[0]));
+ /* first line is data read buffer */
+ newline = linebuf;
+ /* second line is a temporary holding spot in case the next line read
+ is the black/white transition, in which case we'll disregard this
+ one. */
+ oldline = linebuf + width;
+ /* column averages per region */
+ avg = calloc (width * NREGIONS, sizeof (avg[0]));
+
+ while (nlines < height)
+ {
+ if (fread (newline, 1, width, fp) != (size_t) width)
+ break;
+ nlines++;
+ /* Check if new line is majorly different than old.
+ Criteria is 10 pixels differing by more than 10%. */
+ badcnt = 0;
+ for (i = 0; i < width; i++)
+ {
+ pct = newline[i] - oldline[i];
+ /* Fix by M.Reinelt <reinelt@eunet.at>
+ * do NOT use 10% (think of a dark area with
+ * oldline=4 and newline=5, which is a change of 20% !!
+ * Use an absolute difference of 10 as criteria
+ */
+ if (pct < -10 || pct > 10)
+ {
+ badcnt++;
+ DBG (16, "pix%d[%d/%d] ", i, newline[i], oldline[i]);
+ }
+ }
+ DBG (13, "line %d changed %d\n", nlines, badcnt);
+ if ((badcnt > 10) || (nlines == height))
+ {
+ /* End of region. Lines are different or end of data. */
+ transition++;
+ if (transition == 1)
+ DBG (12, "Region %d lines %d-%d\n",
+ region, nlines - reglines[region], nlines - 1);
+ }
+ else
+ {
+ /* Lines are similar, so still in region. */
+ if (transition)
+ {
+ /* There was just a transition, so this is the start of a
+ new region. */
+ region++;
+ if (region >= NREGIONS)
+ /* Too many regions detected. Err below. */
+ break;
+ transition = 0;
+ reglines[region] = 0;
+ }
+ /* Add oldline to the current region's average */
+ for (i = 0; i < width; i++)
+ avg[i + region * width] += oldline[i];
+ reglines[region]++;
+ }
+ /* And newline becomes old */
+ memcpy (oldline, newline, width);
+ }
+ fclose (fp);
+ free (linebuf);
+ region++; /* now call it number of regions instead of index */
+ DBG (11, "read %d lines as %d regions\n", nlines, region);
+
+ /* Check to see if we screwed up */
+ if (region != NREGIONS)
+ {
+ DBG (1, "Warning: gain/offset compute failed.\n"
+ "Found %d regions instead of %d.\n", region, NREGIONS);
+ for (i = 0; i < region; i++)
+ DBG (1, " Region %d: %d lines\n",
+ i, (i >= NREGIONS) ? -1 : reglines[i]);
+ free (avg);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* Now we've got regions and sums. Find averages and range. */
+ max_range[0] = max_range[1] = max_range[2] = 0.0;
+ for (i = 0; i < width; i++)
+ {
+ /* Convert sums to averages */
+ /* black region */
+ tmp1 = avg[i] /= reglines[0];
+ /* white region */
+ tmp2 = avg[i + width] /= reglines[1];
+ /* Track largest range for each color.
+ If image is interleaved, use 'i%3', if not, 'i/(width/3)' */
+ if ((tmp2 - tmp1) > max_range[i / (width / 3)])
+ {
+ max_range[i / (width / 3)] = tmp2 - tmp1;
+ DBG (14, "max %d@%d %f-%f=%f\n",
+ i / (width / 3), i, tmp2, tmp1, tmp2 - tmp1);
+ }
+ }
+ DBG (13, "max range r %f\n", max_range[0]);
+ DBG (13, "max range g %f\n", max_range[1]);
+ DBG (13, "max range b %f\n", max_range[2]);
+
+ /* Set umask to world r/w so other users can overwrite common cal... */
+ oldmask = umask (0);
+ fp = fopen (CAL_FILE_OGN, "w");
+ /* ... and set it back. */
+ umask (oldmask);
+ if (!fp)
+ {
+ DBG (1, "open " CAL_FILE_OGN);
+ free (avg);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Finally, compute offset and gain */
+ for (i = 0; i < width; i++)
+ {
+ int gain, offset;
+ byte ogn[2];
+
+ /* skip line termination flags */
+ if (!((i + 1) % (width / 3)))
+ {
+ DBG (13, "skip scanline EOL %d/%d\n", i, width);
+ continue;
+ }
+
+ /* Gain multiplier:
+ 255 : 1.5 times brighter
+ 511 : 2 times brighter
+ 1023: 3 times brighter */
+#if 1
+ /* Original gain/offset */
+ gain = 512 * ((max_range[i / (width / 3)] /
+ (avg[i + width] - avg[i])) - 1);
+ offset = avg[i];
+#else
+ /* This doesn't work for some people. For instance, a negative
+ offset would be bad. */
+
+ /* Enhanced offset and gain calculation by M.Reinelt <reinelt@eunet.at>
+ * These expressions were found by an iterative calibration process,
+ * by changing gain and offset values for every pixel until the desired
+ * values for black and white were reached, and finding an approximation
+ * formula.
+ * Note that offset is linear, but gain isn't!
+ */
+ offset = (double)3.53 * avg[i] - 125;
+ gain = (double)3861.0 * exp(-0.0168 * (avg[i + width] - avg[i]));
+#endif
+
+ DBG (14, "%d wht=%f blk=%f diff=%f gain=%d offset=%d\n", i,
+ avg[i + width], avg[i], avg[i + width] - avg[i], gain, offset);
+ /* 10-bit gain, 6-bit offset (subtractor) in two bytes */
+ ogn[0] = (byte) (((offset << 2) + (gain >> 8)) & 0xFF);
+ ogn[1] = (byte) (gain & 0xFF);
+ fwrite (ogn, sizeof (byte), 2, fp);
+ /* Annoyingly, we seem to use ogn data at 600dpi, while we scanned
+ at 300, so double our file. Much easier than doubling at the
+ read. */
+ fwrite (ogn, sizeof (byte), 2, fp);
+ }
+
+ fclose (fp);
+ free (avg);
+ return SANE_STATUS_GOOD;
+}
+
+static int
+check_ogn_file (void)
+{
+ FILE *fp;
+ fp = fopen (CAL_FILE_OGN, "r");
+ if (fp)
+ {
+ fclose (fp);
+ return 1;
+ }
+ return 0;
+}
+
+/* Load or fake the offset/gain table */
+static void
+install_ogn (int fd)
+{
+ int temp;
+ byte *buf;
+ FILE *fp;
+
+ /* 8.5in at 600dpi = 5104 pixels in scan head
+ 10-bit gain + 6-bit offset = 2 bytes per pixel, so 10208 bytes */
+ buf = malloc (10208);
+
+ fp = fopen (CAL_FILE_OGN, "r");
+ if (fp)
+ {
+ fread (buf, 2, 5100, fp);
+ /* screw the last 4 pixels */
+ }
+ else
+ {
+ /* Make up the gain/offset data. */
+#define GAIN 256 /* 1.5x */
+#define OFFSET 0
+ for (temp = 0; temp < 10208; temp += 2)
+ {
+ buf[temp] = (byte) ((OFFSET << 2) + (GAIN >> 8));
+ buf[temp + 1] = (byte) (GAIN & 0xFF);
+ }
+ }
+ /* Gain/offset table (r,g,b) */
+ write_byte (fd, DATAPORT_TARGET, DP_R | DP_OFFSET);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 10208);
+ if (fp)
+ fread (buf, 2, 5100, fp);
+ write_byte (fd, DATAPORT_TARGET, DP_G | DP_OFFSET);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 10208);
+ if (fp)
+ {
+ fread (buf, 2, 5100, fp);
+ fclose (fp);
+ }
+ write_byte (fd, DATAPORT_TARGET, DP_B | DP_OFFSET);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 10208);
+
+ free (buf);
+ return;
+}
+
+
+/* Scan sequence */
+/* resolution is 75,150,300,600,1200
+ scan coordinates in 600-dpi pixels */
+static SANE_Status
+scan (CANON_Handle * opt)
+{
+ SANE_Status status;
+ const int left_edge = 0x004b; /* Just for my scanner, or is this
+ universal? Calibrate? */
+ int temp;
+ int fd = opt->fd;
+ byte result;
+ byte *buf;
+
+ /* Check status. (not in w2k driver) */
+ read_byte (fd, STATUS, &result); /* wants 2f or 2d */
+ if (!(result & STATUS_HOME) /*0x2d */ )
+ return SANE_STATUS_DEVICE_BUSY;
+ /* or force it to return?
+ write_byte(fd, COMMAND, 0x02);
+ wait_for_return(fd);
+ */
+
+ /* reserved? */
+ read_byte (fd, 0x69, &result); /* wants 0a */
+
+ read_byte (fd, STATUS, &result); /* wants 0e */
+ read_byte (fd, PAPER_SENSOR, &result); /* wants 2b */
+ write_byte (fd, PAPER_SENSOR, 0x2b);
+ /* Color mode:
+ 1-Channel Line Rate Color 0x15.
+ 1-Channel Grayscale 0x14 (and we skip some of these tables) */
+ write_byte (fd, COLOR_MODE, 0x15);
+
+ /* install the offset/gain table */
+ install_ogn (fd);
+
+ read_byte (fd, STATUS, &result); /* wants 0e */
+ /* move forward to "glass 0" */
+ write_byte (fd, COMMAND, 0x01);
+ read_byte (fd, STATUS, &result); /* wants 0c */
+
+ /* create gamma table */
+ buf = malloc (0x400);
+ for (temp = 0; temp < 0x0400; temp++)
+ /* gamma calculation by M.Reinelt <reinelt@eunet.at> */
+ buf[temp] = (double) 255.0 * exp(log((temp + 0.5) / 1023.0) / opt->gamma)
+ + 0.5;
+
+ /* Gamma R, write and verify */
+ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 0x0400);
+ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_READ);
+ read_bulk (fd, DATAPORT, buf, 0x0400);
+ /* Gamma G */
+ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 0x0400);
+ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_READ);
+ read_bulk (fd, DATAPORT, buf, 0x0400);
+ /* Gamma B */
+ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_WRITE);
+ write_bulk (fd, DATAPORT, buf, 0x0400);
+ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
+ write_word (fd, DATAPORT_ADDR, DP_READ);
+ read_bulk (fd, DATAPORT, buf, 0x0400);
+ free (buf);
+
+ write_byte (fd, CLOCK_DIV, 0x04);
+ /* Resolution: dpi 75(ie) 100,150(1c) 200,300(1a) 600,1200(18) */
+ switch (opt->resolution)
+ {
+ case 150:
+ write_byte (fd, 0x09, 0x1c);
+ break;
+ case 300:
+ write_byte (fd, 0x09, 0x1a);
+ break;
+ case 600:
+ case 1200:
+ /* actually 600 dpi horiz max */
+ write_byte (fd, 0x09, 0x18);
+ break;
+ default: /* 75 */
+ write_byte (fd, 0x09, 0x1e);
+ opt->resolution = 75;
+ }
+
+ write_word (fd, ACTIVE_PX_START, left_edge);
+ /* Data pixel start. Measured at 600dpi regardless of
+ scan resolution. 0-position is 0x004b */
+ write_word (fd, DATA_PX_START, left_edge + opt->x1);
+ /* Data pixel end. Measured at 600dpi regardless of scan
+ resolution. */
+ write_word (fd, DATA_PX_END, left_edge + opt->x2);
+ /* greyscale has 14,03, different lights */
+ write_byte (fd, COLOR_MODE, 0x15);
+ write_byte (fd, 0x29, 0x02);
+ /* Lights */
+ write_word (fd, LAMP_R_ON, 0x0017);
+ /* "Hi-low color" selection from windows driver. low(1437) hi(1481) */
+ write_word (fd, LAMP_R_OFF, 0x1437);
+ write_word (fd, LAMP_G_ON, 0x0017);
+ write_word (fd, LAMP_G_OFF, 0x094e);
+ write_word (fd, LAMP_B_ON, 0x0017);
+ write_word (fd, LAMP_B_OFF, 0x0543);
+
+ /* Analog static offset R,G,B. Greyscale has 0,0,0 */
+ write_byte (fd, 0x38, 0x3f);
+ write_byte (fd, 0x39, 0x3f);
+ write_byte (fd, 0x3a, 0x3f);
+ /* Analog static gain R,G,B (normally 0x01) */
+ write_byte (fd, 0x3b, opt->gain);
+ write_byte (fd, 0x3c, opt->gain);
+ write_byte (fd, 0x3d, opt->gain);
+ /* Digital gain/offset settings. Greyscale has 0 */
+ write_byte (fd, 0x3e, 0x1a);
+
+ {
+ /* Stepper motion setup. */
+ int stepsize, faststep = 0x0104, reverse = 0x28, phase, pwm = 0x1f;
+ switch (opt->resolution)
+ {
+ case 75:
+ stepsize = 0x0106;
+ faststep = 0x0106;
+ reverse = 0;
+ phase = 0x39a8;
+ pwm = 0x3f;
+ break;
+ case 150:
+ stepsize = 0x020d;
+ phase = 0x3198;
+ break;
+ case 300:
+ stepsize = 0x041a;
+ phase = 0x2184;
+ break;
+ case 600:
+ stepsize = 0x0835;
+ phase = 0x0074;
+ break;
+ case 1200:
+ /* 1200 dpi y only, x is 600 dpi */
+ stepsize = 0x106b;
+ phase = 0x41ac;
+ break;
+ default:
+ DBG (1, "BAD RESOLUTION");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ write_word (fd, STEP_SIZE, stepsize);
+ write_word (fd, FAST_STEP, faststep);
+ /* There sounds like a weird step disjoint at the end of skipsteps
+ at 75dpi, so I think that's why skipsteps=0 at 75dpi in the
+ Windows driver. It still works at the normal 0x017a though. */
+ /* cal strip is 0x17a steps, plus 2 300dpi microsteps per pixel */
+ write_word (fd, SKIP_STEPS, 0x017a /* cal strip */ + opt->y1 * 2);
+ /* FIXME could be 0x57, why not? */
+ write_byte (fd, BUFFER_LIMIT, 0x20);
+ write_byte (fd, BUFFER_RESUME, 0x02);
+ write_byte (fd, REVERSE_STEPS, reverse);
+ /* motor resume phasing */
+ write_word (fd, 0x52, phase);
+ write_byte (fd, STEP_PWM, pwm);
+ }
+
+ read_byte (fd, PAPER_SENSOR, &result); /* wants 2b */
+ write_byte (fd, PAPER_SENSOR, 0x0b);
+
+ opt->width = (opt->x2 - opt->x1) * opt->resolution / 600 + 1;
+ opt->height = (opt->y2 - opt->y1) * opt->resolution / 600;
+ opt->flags = 0;
+ DBG (1, "width=%d height=%d dpi=%d\n", opt->width, opt->height,
+ opt->resolution);
+ CHK (do_scan (opt));
+
+ read_byte (fd, PAPER_SENSOR, &result); /* wants 0b */
+ write_byte (fd, PAPER_SENSOR, 0x2b);
+ write_byte (fd, STEP_PWM, 0x3f);
+
+ lights_out (fd);
+ /* home */
+ write_byte (fd, COMMAND, 0x02);
+
+ return status;
+}
+
+
+static SANE_Status
+CANON_set_scan_parameters (CANON_Handle * scan,
+ const int forceCal,
+ const int gray,
+ const int left,
+ const int top,
+ const int right,
+ const int bottom,
+ const int res,
+ const int gain,
+ const double gamma)
+{
+ DBG (2, "CANON_set_scan_parameters:\n");
+ DBG (2, "cal = %d\n", forceCal);
+ DBG (2, "gray = %d (ignored)\n", gray);
+ DBG (2, "res = %d\n", res);
+ DBG (2, "gain = %d\n", gain);
+ DBG (2, "gamma = %f\n", gamma);
+ DBG (2, "in 600dpi pixels:\n");
+ DBG (2, "left = %d, top = %d\n", left, top);
+ DBG (2, "right = %d, bottom = %d\n", right, bottom);
+
+ /* Validate the input parameters */
+ if ((left < 0) || (right > CANON_MAX_WIDTH))
+ return SANE_STATUS_INVAL;
+
+ if ((top < 0) || (bottom > CANON_MAX_HEIGHT))
+ return SANE_STATUS_INVAL;
+
+ if (((right - left) < 10) || ((bottom - top) < 10))
+ return SANE_STATUS_INVAL;
+
+ if ((res != 75) && (res != 150) && (res != 300)
+ && (res != 600) && (res != 1200))
+ return SANE_STATUS_INVAL;
+
+ if ((gain < 0) || (gain > 64))
+ return SANE_STATUS_INVAL;
+
+ if (gamma <= 0.0)
+ return SANE_STATUS_INVAL;
+
+ /* Store params */
+ scan->resolution = res;
+ scan->x1 = left;
+ scan->x2 = right - /* subtract 1 pixel */ 600 / scan->resolution;
+ scan->y1 = top;
+ scan->y2 = bottom;
+ scan->gain = gain;
+ scan->gamma = gamma;
+ scan->flags = forceCal ? FLG_FORCE_CAL : 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+CANON_close_device (CANON_Handle * scan)
+{
+ DBG (3, "CANON_close_device:\n");
+ sanei_usb_close (scan->fd);
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+CANON_open_device (CANON_Handle * scan, const char *dev)
+{
+ SANE_Word vendor;
+ SANE_Word product;
+ SANE_Status res;
+
+ DBG (3, "CANON_open_device: `%s'\n", dev);
+
+ scan->fname = NULL;
+ scan->fp = NULL;
+ scan->flags = 0;
+
+ res = sanei_usb_open (dev, &scan->fd);
+ if (res != SANE_STATUS_GOOD)
+ {
+ DBG (1, "CANON_open_device: couldn't open device `%s': %s\n", dev,
+ sane_strstatus (res));
+ return res;
+ }
+
+#ifndef NO_AUTODETECT
+ /* We have opened the device. Check that it is a USB scanner. */
+ if (sanei_usb_get_vendor_product (scan->fd, &vendor, &product) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (1, "CANON_open_device: sanei_usb_get_vendor_product failed\n");
+ /* This is not a USB scanner, or SANE or the OS doesn't support it. */
+ sanei_usb_close (scan->fd);
+ scan->fd = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* Make sure we have a CANON scanner */
+ if ((vendor != 0x04a9) || (product != 0x2204))
+ {
+ DBG (1, "CANON_open_device: incorrect vendor/product (0x%x/0x%x)\n",
+ vendor, product);
+ sanei_usb_close (scan->fd);
+ scan->fd = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static const char *
+CANON_get_device_name (CANON_Handle * scanner)
+{
+ scanner = scanner; /* Eliminate warning about unused parameters */
+ return "Canoscan FB630U";
+}
+
+
+static SANE_Status
+CANON_finish_scan (CANON_Handle * scanner)
+{
+ DBG (3, "CANON_finish_scan:\n");
+ if (scanner->fp)
+ fclose (scanner->fp);
+ scanner->fp = NULL;
+
+ /* remove temp file */
+ if (scanner->fname)
+ {
+ DBG (4, "removing temp file %s\n", scanner->fname);
+ unlink (scanner->fname);
+ free (scanner->fname);
+ }
+ scanner->fname = NULL;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+CANON_start_scan (CANON_Handle * scanner)
+{
+ int rv;
+ SANE_Status status;
+ DBG (3, "CANON_start_scan called\n");
+
+ /* choose a temp file name for scan data */
+ scanner->fname = strdup ("/tmp/scan.XXXXXX");
+ if (!mktemp (scanner->fname))
+ return SANE_STATUS_IO_ERROR;
+
+ /* calibrate if needed */
+ rv = init (scanner->fd);
+ if (rv < 0) {
+ DBG(1, "Can't talk on USB.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((rv == 1)
+ || !check_ogn_file ()
+ || (scanner->flags & FLG_FORCE_CAL)) {
+ plugin_cal (scanner);
+ wait_for_return (scanner->fd);
+ }
+
+ /* scan */
+ if ((status = scan (scanner)) != SANE_STATUS_GOOD)
+ {
+ CANON_finish_scan (scanner);
+ return status;
+ }
+
+ /* read the temp file back out */
+ scanner->fp = fopen (scanner->fname, "r");
+ DBG (4, "reading %s\n", scanner->fname);
+ if (!scanner->fp)
+ {
+ DBG (1, "open %s", scanner->fname);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+CANON_read (CANON_Handle * scanner, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ SANE_Status status;
+ int red_len;
+
+ DBG (5, "CANON_read called\n");
+ if (!scanner->fp)
+ return SANE_STATUS_INVAL;
+ red_len = fread (data, 1, max_length, scanner->fp);
+ /* return some data */
+ if (red_len > 0)
+ {
+ *length = red_len;
+ DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* EOF or file err */
+ *length = 0;
+ if (feof (scanner->fp))
+ {
+ DBG (4, "EOF\n");
+ status = SANE_STATUS_EOF;
+ }
+ else
+ {
+ DBG (4, "IO ERR\n");
+ status = SANE_STATUS_IO_ERROR;
+ }
+
+ CANON_finish_scan (scanner);
+ DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length);
+ return status;
+}
diff --git a/backend/canon630u.c b/backend/canon630u.c
new file mode 100644
index 0000000..6bd3431
--- /dev/null
+++ b/backend/canon630u.c
@@ -0,0 +1,1044 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002, Nathan Rutman <nathan@gordian.com>
+ Copyright (C) 2001, Marcio Luis Teixeira
+
+ Parts copyright (C) 1996, 1997 Andreas Beck
+ Parts copyright (C) 2000, 2001 Michael Herder <crapsite@gmx.net>
+ Parts copyright (C) 2001 Henning Meier-Geinitz <henning@meier-geinitz.de>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#define BUILD 1
+#define MM_IN_INCH 25.4
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+#define BACKEND_NAME canon630u
+#define CANONUSB_CONFIG_FILE "canon630u.conf"
+#include "../include/sane/sanei_backend.h"
+
+#include "canon630u-common.c"
+
+typedef struct Canon_Device
+{
+ struct Canon_Device *next;
+ SANE_String name;
+ SANE_Device sane;
+}
+Canon_Device;
+
+typedef struct Canon_Scanner
+{
+ struct Canon_Scanner *next;
+ Canon_Device *device;
+ CANON_Handle scan;
+}
+Canon_Scanner;
+
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+static Canon_Device *first_dev = NULL;
+static Canon_Scanner *first_handle = NULL;
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8 /* Number of bits per sample. */
+};
+
+
+struct _SANE_Option
+{
+ SANE_Option_Descriptor *descriptor;
+ SANE_Status (*callback) (struct _SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value,
+ SANE_Int * info);
+};
+
+typedef struct _SANE_Option SANE_Option;
+
+
+/*-----------------------------------------------------------------*/
+
+static SANE_Word getNumberOfOptions (void); /* Forward declaration */
+
+/*
+This read-only option returns the number of options available for
+the device. It should be the first option in the options array
+declared below.
+*/
+
+static SANE_Option_Descriptor optionNumOptionsDescriptor = {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+static SANE_Status
+optionNumOptionsCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ option = option;
+ handle = handle;
+ info = info; /* Eliminate warning about unused parameters */
+
+ if (action != SANE_ACTION_GET_VALUE)
+ return SANE_STATUS_INVAL;
+ *(SANE_Word *) value = getNumberOfOptions ();
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+
+/*
+This option lets the user force scanner calibration. Normally, this is
+done only once, at first scan after powerup.
+ */
+
+static SANE_Word optionCalibrateValue = SANE_FALSE;
+
+static SANE_Option_Descriptor optionCalibrateDescriptor = {
+ "cal",
+ SANE_I18N ("Calibrate Scanner"),
+ SANE_I18N ("Force scanner calibration before scan"),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+static SANE_Status
+optionCalibrateCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ handle = handle;
+ option = option; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ optionCalibrateValue = *(SANE_Bool *) value;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Word *) value = optionCalibrateValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+
+/*
+This option lets the user select the scan resolution. The Canon fb630u
+scanner supports the following resolutions: 75, 150, 300, 600, 1200
+*/
+
+static const SANE_Word optionResolutionList[] = {
+ 4, /* Number of elements */
+ 75, 150, 300, 600 /* Resolution list */
+ /* also 600x1200, but ignore that for now. */
+};
+
+static SANE_Option_Descriptor optionResolutionDescriptor = {
+ SANE_NAME_SCAN_RESOLUTION,
+ SANE_TITLE_SCAN_RESOLUTION,
+ SANE_DESC_SCAN_RESOLUTION,
+ SANE_TYPE_INT,
+ SANE_UNIT_DPI,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC,
+ SANE_CONSTRAINT_WORD_LIST,
+ {(const SANE_String_Const *) optionResolutionList}
+};
+
+static SANE_Word optionResolutionValue = 75;
+
+static SANE_Status
+optionResolutionCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Status status;
+ SANE_Word autoValue = 75;
+
+ handle = handle; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ status =
+ sanei_constrain_value (option->descriptor, (void *) &autoValue, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ optionResolutionValue = autoValue;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ optionResolutionValue = *(SANE_Word *) value;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Word *) value = optionResolutionValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+
+#ifdef GRAY
+/*
+This option lets the user select a gray scale scan
+*/
+static SANE_Word optionGrayscaleValue = SANE_FALSE;
+
+static SANE_Option_Descriptor optionGrayscaleDescriptor = {
+ "gray",
+ SANE_I18N ("Grayscale scan"),
+ SANE_I18N ("Do a grayscale rather than color scan"),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+static SANE_Status
+optionGrayscaleCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ handle = handle;
+ option = option; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ optionGrayscaleValue = *(SANE_Bool *) value;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Word *) value = optionGrayscaleValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+#endif /* GRAY */
+
+/*-----------------------------------------------------------------*/
+
+/* Analog Gain setting */
+static const SANE_Range aGainRange = {
+ 0, /* minimum */
+ 64, /* maximum */
+ 1 /* quantization */
+};
+
+static SANE_Int optionAGainValue = 1;
+
+static SANE_Option_Descriptor optionAGainDescriptor = {
+ "gain",
+ SANE_I18N ("Analog Gain"),
+ SANE_I18N ("Increase or decrease the analog gain of the CCD array"),
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Int),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &aGainRange}
+};
+
+static SANE_Status
+optionAGainCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ option = option;
+ handle = handle;
+ info = info; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionAGainValue = *(SANE_Int *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Int *) value = optionAGainValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+
+/* Scanner gamma setting */
+static SANE_Fixed optionGammaValue = SANE_FIX (1.6);
+
+static SANE_Option_Descriptor optionGammaDescriptor = {
+ "gamma",
+ SANE_I18N ("Gamma Correction"),
+ SANE_I18N ("Selects the gamma corrected transfer curve"),
+ SANE_TYPE_FIXED,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Int),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+
+static SANE_Status
+optionGammaCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ option = option;
+ handle = handle;
+ info = info; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionGammaValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionGammaValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+
+/*
+Scan range
+*/
+
+static const SANE_Range widthRange = {
+ 0, /* minimum */
+ SANE_FIX (CANON_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range heightRange = {
+ 0, /* minimum */
+ SANE_FIX (CANON_MAX_HEIGHT * MM_IN_INCH / 600), /* maximum */
+ 0 /* quantization */
+};
+
+/*-----------------------------------------------------------------*/
+/*
+This option controls the top-left-x corner of the scan
+*/
+
+static SANE_Fixed optionTopLeftXValue = 0;
+
+static SANE_Option_Descriptor optionTopLeftXDescriptor = {
+ SANE_NAME_SCAN_TL_X,
+ SANE_TITLE_SCAN_TL_X,
+ SANE_DESC_SCAN_TL_X,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &widthRange}
+};
+
+static SANE_Status
+optionTopLeftXCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ option = option;
+ handle = handle;
+ value = value; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionTopLeftXValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionTopLeftXValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+/*
+This option controls the top-left-y corner of the scan
+*/
+
+static SANE_Fixed optionTopLeftYValue = 0;
+
+static SANE_Option_Descriptor optionTopLeftYDescriptor = {
+ SANE_NAME_SCAN_TL_Y,
+ SANE_TITLE_SCAN_TL_Y,
+ SANE_DESC_SCAN_TL_Y,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &heightRange}
+};
+
+static SANE_Status
+optionTopLeftYCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionTopLeftYValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionTopLeftYValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+/*
+This option controls the bot-right-x corner of the scan
+Default to 215.9mm, max.
+*/
+
+static SANE_Fixed optionBotRightXValue = SANE_FIX (215.9);
+
+static SANE_Option_Descriptor optionBotRightXDescriptor = {
+ SANE_NAME_SCAN_BR_X,
+ SANE_TITLE_SCAN_BR_X,
+ SANE_DESC_SCAN_BR_X,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &widthRange}
+};
+
+static SANE_Status
+optionBotRightXCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionBotRightXValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionBotRightXValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+/*
+This option controls the bot-right-y corner of the scan
+Default to 296.3mm, max
+*/
+
+static SANE_Fixed optionBotRightYValue = SANE_FIX (296.3);
+
+static SANE_Option_Descriptor optionBotRightYDescriptor = {
+ SANE_NAME_SCAN_BR_Y,
+ SANE_TITLE_SCAN_BR_Y,
+ SANE_DESC_SCAN_BR_Y,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &heightRange}
+};
+
+static SANE_Status
+optionBotRightYCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionBotRightYValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionBotRightYValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*-----------------------------------------------------------------*/
+/*
+The following array binds the option descriptors to
+their respective callback routines
+*/
+
+static SANE_Option so[] = {
+ {&optionNumOptionsDescriptor, optionNumOptionsCallback},
+ {&optionResolutionDescriptor, optionResolutionCallback},
+ {&optionCalibrateDescriptor, optionCalibrateCallback},
+#ifdef GRAY
+ {&optionGrayscaleDescriptor, optionGrayscaleCallback},
+#endif
+ {&optionAGainDescriptor, optionAGainCallback},
+ {&optionGammaDescriptor, optionGammaCallback},
+ {&optionTopLeftXDescriptor, optionTopLeftXCallback},
+ {&optionTopLeftYDescriptor, optionTopLeftYCallback},
+ {&optionBotRightXDescriptor, optionBotRightXCallback},
+ {&optionBotRightYDescriptor, optionBotRightYCallback}
+};
+
+static SANE_Word
+getNumberOfOptions (void)
+{
+ return NELEMS (so);
+}
+
+
+/*
+This routine dispatches the control message to the appropriate
+callback routine, it outght to be called by sane_control_option
+after any driver specific validation.
+*/
+static SANE_Status
+dispatch_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Option *op = so + option;
+ SANE_Int myinfo = 0;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (option < 0 || option >= NELEMS (so))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ if ((action == SANE_ACTION_SET_VALUE) &&
+ ((op->descriptor->cap & SANE_CAP_SOFT_SELECT) == 0))
+ return SANE_STATUS_INVAL;
+
+ if ((action == SANE_ACTION_GET_VALUE) &&
+ ((op->descriptor->cap & SANE_CAP_SOFT_DETECT) == 0))
+ return SANE_STATUS_INVAL;
+
+ if ((action == SANE_ACTION_SET_AUTO) &&
+ ((op->descriptor->cap & SANE_CAP_AUTOMATIC) == 0))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_SET_VALUE)
+ {
+ status = sanei_constrain_value (op->descriptor, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ status = (op->callback) (op, handle, action, value, &myinfo);
+
+ if (info)
+ *info = myinfo;
+
+ return status;
+}
+
+
+static SANE_Status
+attach_scanner (const char *devicename, Canon_Device ** devp)
+{
+ CANON_Handle scan;
+ Canon_Device *dev;
+ SANE_Status status;
+
+ DBG (3, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, '\0', sizeof (Canon_Device)); /* clear structure */
+
+ DBG (4, "attach_scanner: opening %s\n", devicename);
+
+ status = CANON_open_device (&scan, devicename);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename);
+ free (dev);
+ return status;
+ }
+ dev->name = strdup (devicename);
+ dev->sane.name = dev->name;
+ dev->sane.vendor = "CANON";
+ dev->sane.model = CANON_get_device_name (&scan);
+ dev->sane.type = "flatbed scanner";
+ CANON_close_device (&scan);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* callback function for sanei_usb_attach_matching_devices
+*/
+static SANE_Status
+attach_one (const char *name)
+{
+ attach_scanner (name, 0);
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ Find our devices
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char config_line[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT ();
+
+#if 0
+ DBG_LEVEL = 10;
+#endif
+
+ DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+ version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+ DBG (1, "sane_init: SANE Canon630u backend version %d.%d.%d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ sanei_usb_init ();
+
+ fp = sanei_config_open (CANONUSB_CONFIG_FILE);
+ if (!fp)
+ {
+ /* no config-file: try these */
+ attach_scanner ("/dev/scanner", 0);
+ attach_scanner ("/dev/usbscanner", 0);
+ attach_scanner ("/dev/usb/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "reading configure file %s\n", CANONUSB_CONFIG_FILE);
+
+ while (sanei_config_read (config_line, sizeof (config_line), fp))
+ {
+ if (config_line[0] == '#')
+ continue; /* ignore line comments */
+
+ len = strlen (config_line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ DBG (4, "attach_matching_devices(%s)\n", config_line);
+ sanei_usb_attach_matching_devices (config_line, attach_one);
+ }
+
+ DBG (4, "finished reading configure file\n");
+
+ fclose (fp);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_exit (void)
+{
+ Canon_Device *dev, *next;
+
+ DBG (3, "sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->name);
+ free (dev);
+ }
+
+ if (devlist)
+ free (devlist);
+ return;
+}
+
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Canon_Device *dev;
+ int i;
+
+ DBG (3, "sane_get_devices(local_only = %d)\n", local_only);
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Canon_Device *dev;
+ SANE_Status status;
+ Canon_Scanner *scanner;
+
+ DBG (3, "sane_open\n");
+
+ if (devicename[0]) /* search for devicename */
+ {
+ DBG (4, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ {
+ DBG (2, "sane_open: no devicename, opening first device\n");
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ scanner = malloc (sizeof (*scanner));
+ if (!scanner)
+ return SANE_STATUS_NO_MEM;
+
+ memset (scanner, 0, sizeof (*scanner));
+ scanner->device = dev;
+
+ status = CANON_open_device (&scanner->scan, dev->sane.name);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (scanner);
+ return status;
+ }
+
+ *handle = scanner;
+
+ /* insert newly opened handle into list of open handles: */
+ scanner->next = first_handle;
+
+ first_handle = scanner;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Canon_Scanner *prev, *scanner;
+ SANE_Status res;
+
+ DBG (3, "sane_close\n");
+
+ if (!first_handle)
+ {
+ DBG (1, "ERROR: sane_close: no handles opened\n");
+ return;
+ }
+
+ /* remove handle from list of open handles: */
+
+ prev = NULL;
+
+ for (scanner = first_handle; scanner; scanner = scanner->next)
+ {
+ if (scanner == handle)
+ break;
+
+ prev = scanner;
+ }
+
+ if (!scanner)
+ {
+ DBG (1, "ERROR: sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (prev)
+ prev->next = scanner->next;
+ else
+ first_handle = scanner->next;
+
+ res = CANON_close_device (&scanner->scan);
+
+ free (scanner);
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ handle = handle; /* Eliminate compiler warning */
+
+ DBG (3, "sane_get_option_descriptor: option = %d\n", option);
+ if (option < 0 || option >= NELEMS (so))
+ return NULL;
+ return so[option].descriptor;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ handle = handle; /* Eliminate compiler warning */
+
+ DBG (3,
+ "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ handle, option, action, value, (void *)info);
+
+ return dispatch_control_option (handle, option, action, value, info);
+}
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int rc = SANE_STATUS_GOOD;
+ int w =
+ SANE_UNFIX (optionBotRightXValue -
+ optionTopLeftXValue) / MM_IN_INCH * optionResolutionValue;
+ int h =
+ SANE_UNFIX (optionBotRightYValue -
+ optionTopLeftYValue) / MM_IN_INCH * optionResolutionValue;
+
+ handle = handle; /* Eliminate compiler warning */
+
+ DBG (3, "sane_get_parameters\n");
+ parms.depth = 8;
+ parms.last_frame = SANE_TRUE;
+ parms.pixels_per_line = w;
+ parms.lines = h;
+
+#ifdef GRAY
+ if (optionGrayscaleValue == SANE_TRUE)
+ {
+ parms.format = SANE_FRAME_GRAY;
+ parms.bytes_per_line = w;
+ }
+ else
+#endif
+ {
+ parms.format = SANE_FRAME_RGB;
+ parms.bytes_per_line = w * 3;
+ }
+ *params = parms;
+ return rc;
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Canon_Scanner *scanner = handle;
+ SANE_Status res;
+
+ DBG (3, "sane_start\n");
+
+ res = CANON_set_scan_parameters (&scanner->scan,
+ optionCalibrateValue,
+#ifdef GRAY
+ optionGrayscaleValue,
+#else
+ SANE_FALSE,
+#endif
+ SANE_UNFIX (optionTopLeftXValue) /
+ MM_IN_INCH * 600,
+ SANE_UNFIX (optionTopLeftYValue) /
+ MM_IN_INCH * 600,
+ SANE_UNFIX (optionBotRightXValue) /
+ MM_IN_INCH * 600,
+ SANE_UNFIX (optionBotRightYValue) /
+ MM_IN_INCH * 600,
+ optionResolutionValue,
+ optionAGainValue,
+ SANE_UNFIX (optionGammaValue));
+
+ if (res != SANE_STATUS_GOOD)
+ return res;
+
+ return CANON_start_scan (&scanner->scan);
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Canon_Scanner *scanner = handle;
+ return CANON_read (&scanner->scan, data, max_length, length);
+}
+
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (3, "sane_cancel: handle = %p\n", handle);
+ DBG (3, "sane_cancel: cancelling is unsupported in this backend\n");
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
+ non_blocking);
+ if (non_blocking != SANE_FALSE)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle; /* silence gcc */
+ fd = fd; /* silence gcc */
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/canon630u.conf.in b/backend/canon630u.conf.in
new file mode 100644
index 0000000..067b346
--- /dev/null
+++ b/backend/canon630u.conf.in
@@ -0,0 +1,8 @@
+# Options for the canonusb backend
+
+# Autodetect the Canon CanoScan FB630u
+usb 0x04a9 0x2204
+
+# device list for non-linux-systems (enable if autodetect fails):
+#/dev/scanner
+#/dev/usb/scanner0
diff --git a/backend/canon_dr-cmd.h b/backend/canon_dr-cmd.h
new file mode 100644
index 0000000..64e6887
--- /dev/null
+++ b/backend/canon_dr-cmd.h
@@ -0,0 +1,545 @@
+#ifndef CANON_DR_CMD_H
+#define CANON_DR_CMD_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ * Please see opening comments in canon_dr.c
+ */
+
+/****************************************************/
+
+#define USB_HEADER_LEN 12
+#define USB_COMMAND_LEN 12
+#define USB_STATUS_LEN 4
+#define USB_COMMAND_TIME 30000
+#define USB_DATA_TIME 30000
+#define USB_STATUS_TIME 30000
+
+/*static inline void */
+static void
+setbitfield (unsigned char *pageaddr, int mask, int shift, int val)
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*static inline int */
+static int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int
+getnbyte (unsigned char *pnt, int nbytes)
+{
+ unsigned int result = 0;
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = 0; i < nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*static inline void */
+static void
+putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes)
+{
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = nbytes - 1; i >= 0; i--)
+
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+/* ==================================================================== */
+/* SCSI commands */
+
+#define set_SCSI_opcode(out, val) out[0]=val
+#define set_SCSI_lun(out, val) setbitfield(out + 1, 7, 5, val)
+
+/* ==================================================================== */
+/* TEST_UNIT_READY */
+#define TEST_UNIT_READY_code 0x00
+#define TEST_UNIT_READY_len 6
+
+/* ==================================================================== */
+/* REQUEST_SENSE */
+#define REQUEST_SENSE_code 0x03
+#define REQUEST_SENSE_len 6
+
+#define RS_return_size 0x0e
+#define set_RS_return_size(icb,val) icb[0x04]=val
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */
+#define get_RS_additional_length(b) b[0x07] /* always 6? */
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid=0 */
+#define get_RS_SKSB(b) getnbyte(b+0x0f, 3)
+
+/* when RS is 0x05/0x26 bad bytes listed in sksb */
+/* #define get_RS_offending_byte(b) getnbyte(b+0x10, 2) */
+
+/* ==================================================================== */
+/* INQUIRY */
+#define INQUIRY_code 0x12
+#define INQUIRY_len 6
+
+#define INQUIRY_std_len 0x30
+#define INQUIRY_vpd_len 0x1e
+
+#define set_IN_evpd(icb, val) setbitfield(icb + 1, 1, 0, val)
+#define set_IN_page_code(icb, val) icb[0x02]=val
+#define set_IN_return_size(icb,val) icb[0x04]=val
+
+#define get_IN_periph_qual(in) getbitfield(in, 0x07, 5)
+#define IN_periph_qual_lun 0x00
+#define IN_periph_qual_nolun 0x03
+#define get_IN_periph_devtype(in) getbitfield(in, 0x1f, 0)
+#define IN_periph_devtype_scanner 0x06
+#define IN_periph_devtype_unknown 0x1f
+#define get_IN_response_format(in) getbitfield(in + 0x03, 0x07, 0)
+#define IN_recognized 0x02
+#define get_IN_vendor(in, buf) strncpy(buf, (char *)in + 0x08, 0x08)
+#define get_IN_product(in, buf) strncpy(buf, (char *)in + 0x10, 0x010)
+#define get_IN_version(in, buf) strncpy(buf, (char *)in + 0x20, 0x04)
+
+/* the VPD response */
+#define get_IN_page_length(in) in[0x04]
+#define get_IN_basic_x_res(in) getnbyte(in + 0x05, 2)
+#define get_IN_basic_y_res(in) getnbyte(in + 0x07, 2)
+#define get_IN_step_x_res(in) getbitfield(in+0x09, 1, 0)
+#define get_IN_step_y_res(in) getbitfield(in+0x09, 1, 4)
+#define get_IN_max_x_res(in) getnbyte(in + 0x0a, 2)
+#define get_IN_max_y_res(in) getnbyte(in + 0x0c, 2)
+#define get_IN_min_x_res(in) getnbyte(in + 0x0e, 2)
+#define get_IN_min_y_res(in) getnbyte(in + 0x10, 2)
+#define get_IN_std_res_60(in) getbitfield(in+ 0x12, 1, 7)
+#define get_IN_std_res_75(in) getbitfield(in+ 0x12, 1, 6)
+#define get_IN_std_res_100(in) getbitfield(in+ 0x12, 1, 5)
+#define get_IN_std_res_120(in) getbitfield(in+ 0x12, 1, 4)
+#define get_IN_std_res_150(in) getbitfield(in+ 0x12, 1, 3)
+#define get_IN_std_res_160(in) getbitfield(in+ 0x12, 1, 2)
+#define get_IN_std_res_180(in) getbitfield(in+ 0x12, 1, 1)
+#define get_IN_std_res_200(in) getbitfield(in+ 0x12, 1, 0)
+#define get_IN_std_res_240(in) getbitfield(in+ 0x13, 1, 7)
+#define get_IN_std_res_300(in) getbitfield(in+ 0x13, 1, 6)
+#define get_IN_std_res_320(in) getbitfield(in+ 0x13, 1, 5)
+#define get_IN_std_res_400(in) getbitfield(in+ 0x13, 1, 4)
+#define get_IN_std_res_480(in) getbitfield(in+ 0x13, 1, 3)
+#define get_IN_std_res_600(in) getbitfield(in+ 0x13, 1, 2)
+#define get_IN_std_res_800(in) getbitfield(in+ 0x13, 1, 1)
+#define get_IN_std_res_1200(in) getbitfield(in+ 0x13, 1, 0)
+#define get_IN_window_width(in) getnbyte(in + 0x14, 4)
+#define get_IN_window_length(in) getnbyte(in + 0x18, 4)
+#define get_IN_awd(in) getbitfield(in+0x1c, 1, 7)
+#define get_IN_ce_emphasis(in) getbitfield(in+0x1c, 1, 6)
+#define get_IN_c_emphasis(in) getbitfield(in+0x1c, 1, 5)
+#define get_IN_high_quality(in) getbitfield(in+0x1c, 1, 4)
+#define get_IN_multilevel(in) getbitfield(in+0x1c, 1, 3)
+#define get_IN_half_tone(in) getbitfield(in+0x1c, 1, 2)
+#define get_IN_monochrome(in) getbitfield(in+0x1c, 1, 1)
+#define get_IN_overflow(in) getbitfield(in+0x1c, 1, 0)
+
+/* some scanners need evpd inquiry data manipulated */
+#define set_IN_page_length(in,val) in[0x04]=val
+
+/* ==================================================================== */
+/* RESERVE_UNIT */
+#define RESERVE_UNIT_code 0x16
+#define RESERVE_UNIT_len 6
+
+/* ==================================================================== */
+/* RELEASE_UNIT */
+
+#define RELEASE_UNIT_code 0x17
+#define RELEASE_UNIT_len 6
+
+/* ==================================================================== */
+/* SCAN */
+#define SCAN_code 0x1b
+#define SCAN_len 6
+
+#define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val
+
+/* ==================================================================== */
+/* SET_WINDOW */
+#define SET_WINDOW_code 0x24
+#define SET_WINDOW_len 10
+
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+#define SW_header_len 8
+#define SW_desc_len 0x2c
+
+/* ==================================================================== */
+/* GET_WINDOW */
+#define GET_WINDOW_code 0x25
+#define GET_WINDOW_len 0
+
+/* ==================================================================== */
+/* READ/SEND page codes */
+#define SR_datatype_image 0x00
+#define SR_datatype_lut 0x03
+#define SR_datatype_panel 0x84
+#define SR_datatype_counters 0x8c
+#define SR_datatype_endorser 0x90
+#define SR_datatype_fineoffset 0x90
+#define SR_datatype_finegain 0x91
+
+/* ==================================================================== */
+/* READ */
+#define READ_code 0x28
+#define READ_len 10
+
+#define set_R_datatype_code(sb, val) sb[0x02] = val
+#define set_R_xfer_uid(sb, val) sb[4] = val
+#define set_R_xfer_lid(sb, val) sb[5] = val
+#define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+/*image needs no macros?*/
+
+/*lut unread?*/
+
+/*panel*/
+#define R_PANEL_len 0x08
+#define get_R_PANEL_start(in) getbitfield(in, 1, 7)
+#define get_R_PANEL_stop(in) getbitfield(in, 1, 6)
+#define get_R_PANEL_butt3(in) getbitfield(in, 1, 2)
+#define get_R_PANEL_new_file(in) getbitfield(in+1, 1, 0)
+#define get_R_PANEL_count_only(in) getbitfield(in+1, 1, 1)
+#define get_R_PANEL_bypass_mode(in) getbitfield(in+1, 1, 2)
+#define get_R_PANEL_enable_led(in) getbitfield(in+2, 1, 0)
+#define get_R_PANEL_counter(in) getnbyte(in + 0x04, 4)
+
+/*counters*/
+#define R_COUNTERS_len 0x80
+#define get_R_COUNTERS_scans(in) getnbyte(in + 0x04, 4)
+
+/*endorser unread?*/
+
+/*fine gain*/
+#define R_FINE_uid_gray 0x07
+#define R_FINE_uid_red 0x0c
+#define R_FINE_uid_green 0x0a
+#define R_FINE_uid_blue 0x09
+#define R_FINE_uid_unknown 0x14
+
+/* ==================================================================== */
+/* SEND */
+#define SEND_code 0x2a
+#define SEND_len 10
+
+#define set_S_xfer_datatype(sb, val) sb[0x02] = (unsigned char)val
+#define set_S_xfer_id(sb, val) putnbyte(sb + 4, val, 2)
+#define set_S_xfer_length(sb, val) putnbyte(sb + 6, val, 3)
+
+/*lut*/
+#define S_LUT_len 0x100
+#define S_LUT_id_front 0x82
+#define S_LUT_id_unk1 0x84
+#define S_LUT_id_unk2 0x88
+#define S_LUT_id_unk3 0x90
+
+/*panel*/
+#define S_PANEL_len 0x08
+#define set_S_PANEL_enable_led(in,val) setbitfield(in+2, 1, 0, val)
+#define set_S_PANEL_counter(sb,val) putnbyte(sb + 0x04, val, 4)
+
+/*counters*/
+/*endorser*/
+
+/* ==================================================================== */
+/* OBJECT_POSITION */
+#define OBJECT_POSITION_code 0x31
+#define OBJECT_POSITION_len 10
+
+#define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val)
+#define OP_Discharge 0x00
+#define OP_Feed 0x01
+
+/* ==================================================================== */
+/* Page codes used by GET/SET SCAN MODE */
+#define SM_pc_adf 0x01
+#define SM_pc_tpu 0x02
+#define SM_pc_scan_ctl 0x20
+
+#define SM_pc_df 0x30
+#define SM_pc_buffer 0x32
+#define SM_pc_imprinter 0x34
+#define SM_pc_dropout 0x36
+#define SM_pc_unknown 0x37
+
+#define SM_pc_all_pc 0x3F
+
+/* ==================================================================== */
+/* GET SCAN MODE */
+#define GET_SCAN_MODE_code 0xd5
+#define GET_SCAN_MODE_len 6
+
+#define set_GSM_unknown(sb, val) sb[0x01] = val
+#define set_GSM_page_code(sb, val) sb[0x02] = val
+#define set_GSM_len(sb, val) sb[0x04] = val
+
+#define GSM_PSIZE_len 0x5a
+
+/* ==================================================================== */
+/* SET SCAN MODE */
+#define SET_SCAN_MODE_code 0xd6
+#define SET_SCAN_MODE_len 6
+
+#define set_SSM_pf(sb, val) setbitfield(sb + 1, 1, 4, val)
+#define set_SSM_pay_len(sb, val) sb[0x04] = val
+
+/* the payload */
+#define SSM_PAY_len 0x14
+#define SSM_PAY_HEAD_len 0x13
+#define set_SSM_pay_head_len(sb, val) sb[0x01] = val
+#define set_SSM_page_code(sb, val) sb[0x04] = val
+#define SSM_PAGE_len 0x0e
+#define set_SSM_page_len(sb, val) sb[0x05] = val
+
+/* for DF (0x30) page */
+#define set_SSM_DF_deskew_roll(sb, val) setbitfield(sb+7, 1, 5, val)
+#define set_SSM_DF_staple(sb, val) setbitfield(sb+7, 1, 4, val)
+#define set_SSM_DF_thick(sb, val) setbitfield(sb+7, 1, 2, val)
+#define set_SSM_DF_len(sb, val) setbitfield(sb+7, 1, 0, val)
+#define set_SSM_DF_textdir(sb, val) setbitfield(sb+9, 0xf, 0, val)
+
+/* for BUFFER (0x32) page */
+#define set_SSM_BUFF_duplex(sb, val) setbitfield(sb+6, 1, 1, val)
+#define set_SSM_BUFF_unk(sb, val) sb[0x07] = val
+#define set_SSM_BUFF_async(sb, val) setbitfield(sb+0x0a, 1, 6, val)
+#define set_SSM_BUFF_ald(sb, val) setbitfield(sb+0x0a, 1, 5, val)
+#define set_SSM_BUFF_fb(sb, val) setbitfield(sb+0x0a, 1, 4, val)
+
+/* for DO (0x36) page */
+#define SSM_DO_none 0
+#define SSM_DO_red 1
+#define SSM_DO_green 2
+#define SSM_DO_blue 3
+#define set_SSM_DO_unk1(sb, val) sb[0x07] = val
+#define set_SSM_DO_unk2(sb, val) sb[0x09] = val
+#define set_SSM_DO_f_do(sb, val) sb[0x0b] = val
+#define set_SSM_DO_b_do(sb, val) sb[0x0c] = val
+#define set_SSM_DO_f_en(sb, val) sb[0x0d] = val
+#define set_SSM_DO_b_en(sb, val) sb[0x0e] = val
+
+/* ==================================================================== */
+/* Cancel */
+#define CANCEL_code 0xd8
+#define CANCEL_len 6
+
+/* ==================================================================== */
+/* Coarse Calibration */
+#define COR_CAL_code 0xe1
+#define COR_CAL_len 10
+
+#define set_CC_version(sb, val) sb[5] = val
+#define set_CC_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+/* the payload */
+#define CC_pay_len 0x20
+#define CC_pay_ver 0x00
+
+#define set_CC_f_gain(sb, val) sb[0] = val
+#define set_CC_unk1(sb, val) sb[1] = val
+#define set_CC_f_offset(sb, val) sb[2] = val
+#define set_CC_unk2(sb, val) sb[3] = val
+
+#define set_CC_exp_f_r1(sb, val) putnbyte(sb + 0x04, val, 2)
+#define set_CC_exp_f_g1(sb, val) putnbyte(sb + 0x06, val, 2)
+#define set_CC_exp_f_b1(sb, val) putnbyte(sb + 0x08, val, 2)
+#define set_CC_exp_f_r2(sb, val) putnbyte(sb + 0x0a, val, 2)
+#define set_CC_exp_f_g2(sb, val) putnbyte(sb + 0x0c, val, 2)
+#define set_CC_exp_f_b2(sb, val) putnbyte(sb + 0x0e, val, 2)
+
+#define set_CC_b_gain(sb, val) sb[0x10] = val
+#define set_CC_b_offset(sb, val) sb[0x12] = val
+
+#define set_CC_exp_b_r1(sb, val) putnbyte(sb + 0x14, val, 2)
+#define set_CC_exp_b_g1(sb, val) putnbyte(sb + 0x16, val, 2)
+#define set_CC_exp_b_b1(sb, val) putnbyte(sb + 0x18, val, 2)
+#define set_CC_exp_b_r2(sb, val) putnbyte(sb + 0x1a, val, 2)
+#define set_CC_exp_b_g2(sb, val) putnbyte(sb + 0x1c, val, 2)
+#define set_CC_exp_b_b2(sb, val) putnbyte(sb + 0x1e, val, 2)
+
+/* the 'version 3' payload (P-208 and P-215) */
+#define CC3_pay_len 0x28
+#define CC3_pay_ver 0x03
+
+#define set_CC3_gain_f_r(sb, val) sb[0] = val
+#define set_CC3_gain_f_g(sb, val) sb[1] = val
+#define set_CC3_gain_f_b(sb, val) sb[2] = val
+
+#define set_CC3_off_f_r(sb, val) sb[4] = val
+#define set_CC3_off_f_g(sb, val) sb[5] = val
+#define set_CC3_off_f_b(sb, val) sb[6] = val
+
+#define set_CC3_exp_f_r(sb, val) putnbyte(sb + 0x08, val, 2)
+#define set_CC3_exp_f_g(sb, val) putnbyte(sb + 0x0a, val, 2)
+#define set_CC3_exp_f_b(sb, val) putnbyte(sb + 0x0c, val, 2)
+
+#define set_CC3_gain_b_r(sb, val) sb[0x14] = val
+#define set_CC3_gain_b_g(sb, val) sb[0x15] = val
+#define set_CC3_gain_b_b(sb, val) sb[0x16] = val
+
+#define set_CC3_off_b_r(sb, val) sb[0x18] = val
+#define set_CC3_off_b_g(sb, val) sb[0x19] = val
+#define set_CC3_off_b_b(sb, val) sb[0x1a] = val
+
+#define set_CC3_exp_b_r(sb, val) putnbyte(sb + 0x1c, val, 2)
+#define set_CC3_exp_b_g(sb, val) putnbyte(sb + 0x1e, val, 2)
+#define set_CC3_exp_b_b(sb, val) putnbyte(sb + 0x20, val, 2)
+
+/* ==================================================================== */
+/* SET SCAN MODE 2 */
+#define SET_SCAN_MODE2_code 0xe5
+#define SET_SCAN_MODE2_len 10
+
+#define set_SSM2_page_code(sb, val) sb[0x02] = val
+#define set_SSM2_pay_len(sb, val) sb[0x08] = val
+
+/* the payload */
+#define SSM2_PAY_len 0x10
+#define set_SSM2_unk(sb, val) sb[0x02] = val
+#define set_SSM2_unk2(sb, val) sb[0x03] = val
+#define set_SSM2_unk3(sb, val) sb[0x04] = val
+
+/* ==================================================================== */
+/* window descriptor macros for SET_WINDOW and GET_WINDOW */
+
+#define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2)
+
+/* ==================================================================== */
+
+ /* 0x00 - Window Identifier */
+#define set_WD_wid(sb, val) sb[0] = val
+#define WD_wid_front 0x00
+#define WD_wid_back 0x01
+
+ /* 0x01 - Reserved (bits 7-1), AUTO (bit 0) */
+#define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val)
+#define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0)
+
+ /* 0x02,0x03 - X resolution in dpi */
+#define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2)
+#define get_WD_Xres(sb) getnbyte(sb + 0x02, 2)
+
+ /* 0x04,0x05 - Y resolution in dpi */
+#define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2)
+#define get_WD_Yres(sb) getnbyte(sb + 0x04, 2)
+
+ /* 0x06-0x09 - Upper Left X in 1/1200 inch */
+#define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4)
+#define get_WD_ULX(sb) getnbyte(sb + 0x06, 4)
+
+ /* 0x0a-0x0d - Upper Left Y in 1/1200 inch */
+#define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4)
+#define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4)
+
+ /* 0x0e-0x11 - Width in 1/1200 inch */
+#define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4)
+#define get_WD_width(sb) getnbyte(sb + 0x0e, 4)
+
+ /* 0x12-0x15 - Height in 1/1200 inch */
+#define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4)
+#define get_WD_length(sb) getnbyte(sb + 0x12, 4)
+
+ /* 0x16 - Brightness */
+#define set_WD_brightness(sb, val) sb[0x16] = val
+#define get_WD_brightness(sb) sb[0x16]
+
+ /* 0x17 - Threshold */
+#define set_WD_threshold(sb, val) sb[0x17] = val
+#define get_WD_threshold(sb) sb[0x17]
+
+ /* 0x18 - Contrast */
+#define set_WD_contrast(sb, val) sb[0x18] = val
+#define get_WD_contrast(sb) sb[0x18]
+
+ /* 0x19 - Image Composition (color mode) */
+#define set_WD_composition(sb, val) sb[0x19] = val
+#define get_WD_composition(sb) sb[0x19]
+#define WD_comp_LA 0
+#define WD_comp_HT 1
+#define WD_comp_GS 2
+#define WD_comp_CL 3
+#define WD_comp_CH 4
+#define WD_comp_CG 5
+
+ /* 0x1a - Depth */
+#define set_WD_bitsperpixel(sb, val) sb[0x1a] = val
+#define get_WD_bitsperpixel(sb) sb[0x1a]
+
+ /* 0x1b,0x1c - Halftone Pattern */
+#define set_WD_ht_type(sb, val) sb[0x1b] = val
+#define get_WD_ht_type(sb) sb[0x1b]
+#define WD_ht_type_DEFAULT 0
+#define WD_ht_type_DITHER 1
+#define WD_ht_type_DIFFUSION 2
+
+#define set_WD_ht_pattern(sb, val) sb[0x1c] = val
+#define get_WD_ht_pattern(sb) sb[0x1c]
+
+ /* 0x1d - Reverse image, reserved area, padding type */
+#define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val)
+#define get_WD_rif(sb) getbitfield(sb + 0x1d, 1, 7)
+#define set_WD_rgb(sb, val) setbitfield(sb + 0x1d, 7, 4, val)
+#define get_WD_rgb(sb) getbitfield(sb + 0x1d, 7, 4)
+#define set_WD_padding(sb, val) setbitfield(sb + 0x1d, 7, 0, val)
+#define get_WD_padding(sb) getbitfield(sb + 0x1d, 7, 0)
+
+ /* 0x1e,0x1f - Bit ordering */
+#define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2)
+#define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2)
+
+ /* 0x20 - compression type */
+#define set_WD_compress_type(sb, val) sb[0x20] = val
+#define get_WD_compress_type(sb) sb[0x20]
+#define WD_cmp_NONE 0
+#define WD_cmp_MH 1
+#define WD_cmp_MR 2
+#define WD_cmp_MMR 3
+#define WD_cmp_JPEG 0x80
+
+ /* 0x21 - compression argument
+ * specify "k" parameter with MR compress,
+ * or with JPEG- Q param, 0-7
+ */
+#define set_WD_compress_arg(sb, val) sb[0x21] = val
+#define get_WD_compress_arg(sb) sb[0x21]
+
+ /* 0x22-0x27 - reserved */
+
+ /* 0x28-0x2c - vendor unique */
+ /* FIXME: more params here? */
+#define set_WD_reserved2(sb, val) sb[0x2a] = val
+#define get_WD_reserved2(sb) sb[0x2a]
+
+
+/* ==================================================================== */
+
+#endif
diff --git a/backend/canon_dr.c b/backend/canon_dr.c
new file mode 100644
index 0000000..594aab2
--- /dev/null
+++ b/backend/canon_dr.c
@@ -0,0 +1,8133 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package, and implements a SANE backend
+ for various Canon DR-series scanners.
+
+ Copyright (C) 2008-2010 m. allan noah
+
+ Yabarana Corp. www.yabarana.com provided significant funding
+ EvriChart, Inc. www.evrichart.com provided funding and loaned equipment
+ Canon, USA. www.usa.canon.com loaned equipment
+ HPrint hprint.com.br provided funding and testing for DR-2510 support
+ Stone-IT www.stone-it.com provided funding for DR-2010 and DR-2050 support
+
+ --------------------------------------------------------------------------
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ The source code is divided in sections which you can easily find by
+ searching for the tag "@@".
+
+ Section 1 - Init & static stuff
+ Section 2 - sane_init, _get_devices, _open & friends
+ Section 3 - sane_*_option functions
+ Section 4 - sane_start, _get_param, _read & friends
+ Section 5 - calibration functions
+ Section 6 - sane_close functions
+ Section 7 - misc functions
+ Section 8 - image processing functions
+
+ Changes:
+ v1 2008-10-29, MAN
+ - initial version
+ v2 2008-11-04, MAN
+ - round scanlines to even bytes
+ - spin RS and usb_clear_halt code into new function
+ - update various scsi payloads
+ - calloc out block so it gets set to 0 initially
+ v3 2008-11-07, MAN
+ - back window uses id 1
+ - add option and functions to read/send page counter
+ - add rif option
+ v4 2008-11-11, MAN
+ - eject document when sane_read() returns EOF
+ v5 2008-11-25, MAN
+ - remove EOF ejection code
+ - add SSM and GSM commands
+ - add dropout, doublefeed, and jpeg compression options
+ - disable adf backside
+ - fix adf duplex
+ - read two extra lines (ignore errors) at end of image
+ - only send scan command at beginning of batch
+ - fix bug in hexdump with 0 length string
+ - DR-7580 support
+ v6 2008-11-29, MAN
+ - fix adf simplex
+ - rename ssm_duplex to ssm_buffer
+ - add --buffer option
+ - reduce inter-page commands when buffering is enabled
+ - improve sense_handler output
+ - enable counter option
+ - drop unused code
+ v7 2008-11-29, MAN
+ - jpeg support (size rounding and header overwrite)
+ - call object_position(load) between pages even if buffering is on
+ - use request sense info bytes on short scsi reads
+ - byte swap color BGR to RGB
+ - round image width down, not up
+ - round image height down to even # of lines
+ - always transfer even # of lines per block
+ - scsi and jpeg don't require reading extra lines to reach EOF
+ - rename buffer option to buffermode to avoid conflict with scanimage
+ - send ssm_do and ssm_df during sane_start
+ - improve sense_handler output
+ v8 2008-12-07, MAN
+ - rename read/send_counter to read/send_panel
+ - enable control panel during init
+ - add options for all buttons
+ - call TUR twice in wait_scanner(), even if first succeeds
+ - disable rif
+ - enable brightness/contrast/threshold options
+ v9 2008-12-07, MAN
+ - add rollerdeskew and stapledetect options
+ - add rollerdeskew and stapledetect bits to ssm_df()
+ v10 2008-12-10, MAN
+ - add all documented request sense codes to sense_handler()
+ - fix color jpeg (remove unneeded BGR to RGB swapping code)
+ - add macros for LUT data
+ v11 2009-01-10, MAN
+ - send_panel() can disable too
+ - add cancel() to send d8 command
+ - call cancel() only after final read from scanner
+ - stop button reqests cancel
+ v12 2009-01-21, MAN
+ - dont export private symbols
+ v13 2009-03-06, MAN
+ - new vendor ID for recent machines
+ - add usb ids for several new machines
+ v14 2009-03-07, MAN
+ - remove HARD_SELECT from counter (Legitimate, but API violation)
+ - attach to CR-series scanners as well
+ v15 2009-03-15, MAN
+ - add byte-oriented duplex interlace code
+ - add RRGGBB color interlace code
+ - add basic support for DR-2580C
+ v16 2009-03-20, MAN
+ - add more unknown setwindow bits
+ - add support for 16 byte status packets
+ - clean do_usb_cmd error handling (call reset more often)
+ - add basic support for DR-2050C, DR-2080C, DR-2510C
+ v17 2009-03-20, MAN
+ - set status packet size from config file
+ v18 2009-03-21, MAN
+ - rewrite config file parsing to reset options after each scanner
+ - add config options for vendor, model, version
+ - dont call inquiry if those 3 options are set
+ - remove default config file from code
+ - add initial gray deinterlacing code for DR-2510C
+ - rename do_usb_reset to do_usb_clear
+ v19 2009-03-22, MAN
+ - pad gray deinterlacing area for DR-2510C
+ - override tl_x and br_x for fixed width scanners
+ v20 2009-03-23, MAN
+ - improved macros for inquiry and set window
+ - shorten inquiry vpd length to match windows driver
+ - remove status-length config option
+ - add padded-read config option
+ - rewrite do_usb_cmd to pad reads and calloc/copy buffers
+ v21 2009-03-24, MAN
+ - correct rgb padding macro
+ - skip send_panel and ssm_df commands for DR-20xx scanners
+ v22 2009-03-25, MAN
+ - add deinterlacing code for DR-2510C in duplex and color
+ v23 2009-03-27, MAN
+ - rewrite all image data processing code
+ - handle more image interlacing formats
+ - re-enable binary mode on some scanners
+ - limit some machines to full-width scanning
+ v24 2009-04-02, MAN
+ - fix DR-2510C duplex deinterlacing code
+ - rewrite sane_read helpers to read until EOF
+ - update sane_start for scanners that dont use object_position
+ - dont call sanei_usb_clear_halt() if device is not open
+ - increase default buffer size to 4 megs
+ - set buffermode on by default
+ - hide modes and resolutions that DR-2510C lies about
+ - read_panel() logs front-end access to sensors instead of timing
+ - rewrite do_usb_cmd() to use remainder from RS info
+ v25 2009-04-12, MAN
+ - disable SANE_FRAME_JPEG
+ v26 2009-04-14, MAN (SANE 1.0.20)
+ - return cmd status for reads on sensors
+ - allow rs to adjust read length for all bad status responses
+ v27 2009-05-08, MAN
+ - bug fix in read_panel()
+ - initialize vars in do_usb_cmd()
+ - set buffermode off by default
+ - clear page counter during init and sane_start()
+ - eject previous page during init and sane_start()
+ - improved SSM_BUFF macros
+ - moved set_window() to after ssm-*()
+ - add coarse calibration (AFE offset/gain & per-channel exposure)
+ - add fine calibration (per-cell offset/gain)
+ - free image and fine cal buffers in sane_close()
+ - compare page counter of small scanners only in non-buffered mode
+ - add back-side gray mirroring code for DR-2580C
+ v28 2009-05-20, MAN
+ - use average instead of min/max for fine offset and gain
+ - rewrite supported resolution list as x and y arrays
+ - merge x and y resolution options into single option
+ - move scan params into two new structs, s->u and s->s
+ - sane_get_parameters() just returns values from s->u
+ - dont call wait_scanner() in object_position()
+ - dont call ssm_*() from option handler
+ - refactor sane_start()
+ - read_from_buffer() can workaround missing res, modes and cropping
+ - set most DR-2xxx machines to use the read_from_buffer workarounds
+ - set default threshold to 90
+ - add option for button #3 of some machines
+ - don't eject paper during init
+ - add DR-2010 quirks
+ - switch counter to HARD_SELECT, not SOFT
+ v29 2009-06-01, MAN
+ - split coarse and fine cal to run independently
+ - add side option
+ - reset scan params to user request if calibration fails
+ - better handling of sane_cancel
+ - better handling of errors during sane_start and sane_read
+ v30 2009-06-17, MAN
+ - add fine cal support for machines with internal buffer (2050/2080)
+ - support fixed-width machines that require even bytes per scanline
+ - pad end of scan with gray if scanner stops prematurely
+ - better handling of errors during calibration
+ - cleanup canceling debug messages
+ - remove old cancel() prototype
+ - small sleep before clearing usb halt condition
+ v31 2009-06-29, MAN
+ - reduce default buffer size to 2 megs
+ v32 2009-07-21, MAN
+ - crop/resample image data before buffering, not after
+ - shink image buffers to size of output image, not input
+ - correct some debug message
+ - better handling of EOF
+ - add intermediate param struct to existing user and scan versions
+ v33 2009-07-23, MAN
+ - add software brightness/contrast for dumb scanners
+ - add blocking mode to allow full-page manipulation options to run
+ - add swdespeck option and support code
+ - add swdeskew and swcrop options (disabled)
+ v34 2009-07-28, MAN
+ - add simplified Hough transform based deskewing code
+ - add extremity detecting cropping code
+ - use per-model background color to fill corners after deskew
+ - request and chop extra scanlines instead of rounding down
+ - remove padding dumb scanners add to top of front side
+ - sane_get_params uses intermediate struct instead of user struct
+ - if scanner stops, clone the last line until the end of buffer
+ - reset some intermediate params between duplex sides
+ v35 2010-02-09, MAN (SANE 1.0.21)
+ - cleanup #includes and copyright
+ - add SANE_I18N to static strings
+ - don't fail if scsi buffer is too small
+ v36 2011-01-03, MAN
+ - initial support for DR-3080 and DR-5060
+ - add code to clamp scan width to an arbitrary byte width boundary
+ - add code to prevent setting of brightness/threshold/contrast
+ - don't send dropout color command on non-color scanners
+ - initial support for DR-7090C
+ - update credits
+ v37 2011-01-26, MAN (SANE 1.0.22)
+ - don't center window when using flatbed
+ - improve request sense error messages
+ - enable flatbed for all known models
+ v38 2011-07-06, MAN
+ - initial support for DR-5020
+ - use ppl_mod instead of Bpl_mod, apply to all modes
+ - invert logic of read_panel tracking
+ - add ability to disable read_panel()
+ - automatically disable read/send_panel if unsupported
+ v39 2011-11-01, MAN
+ - DR-2580C pads the backside of duplex scans
+ v40 2012-11-01, MAN
+ - initial DR-9050C, DR-7550C, DR-6050C and DR-3010C support
+ v41 2013-07-31, MAN
+ - initial P-208 and P-215 support
+ - bug fix for calibration of scanners with duplex_offset
+ - allow duplex_offset to be controlled from config file
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . .
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . . - sane_get_parameters() : returns estimated scan parameters
+ . . - (repeat previous 3 functions)
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times; after sane_read returns EOF,
+ . . loop may continue with sane_start which may return a 2nd page
+ . . when doing duplex scans, or load the next page from the ADF)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+*/
+
+/*
+ * @@ Section 1 - Init
+ */
+
+#include "../include/sane/config.h"
+
+#include <string.h> /*memcpy...*/
+#include <ctype.h> /*isspace*/
+#include <math.h> /*tan*/
+#include <unistd.h> /*usleep*/
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#include "canon_dr-cmd.h"
+#include "canon_dr.h"
+
+#define DEBUG 1
+#define BUILD 41
+
+/* values for SANE_DEBUG_CANON_DR env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - scsi/usb trace 25
+ - scsi/usb detail 30
+ - useless noise 35
+*/
+
+/* ------------------------------------------------------------------------- */
+#define STRING_FLATBED SANE_I18N("Flatbed")
+#define STRING_ADFFRONT SANE_I18N("ADF Front")
+#define STRING_ADFBACK SANE_I18N("ADF Back")
+#define STRING_ADFDUPLEX SANE_I18N("ADF Duplex")
+
+#define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+#define STRING_RED SANE_I18N("Red")
+#define STRING_GREEN SANE_I18N("Green")
+#define STRING_BLUE SANE_I18N("Blue")
+#define STRING_EN_RED SANE_I18N("Enhance Red")
+#define STRING_EN_GREEN SANE_I18N("Enhance Green")
+#define STRING_EN_BLUE SANE_I18N("Enhance Blue")
+
+#define STRING_NONE SANE_I18N("None")
+#define STRING_JPEG SANE_I18N("JPEG")
+
+/* Also set via config file. */
+static int global_buffer_size;
+static int global_buffer_size_default = 2 * 1024 * 1024;
+static int global_padded_read;
+static int global_padded_read_default = 0;
+static int global_duplex_offset;
+static int global_duplex_offset_default = 0;
+static char global_vendor_name[9];
+static char global_model_name[17];
+static char global_version_name[5];
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ * a ptr to a single-linked list of scanner structs
+ */
+static const SANE_Device **sane_devArray = NULL;
+static struct scanner *scanner_devList = NULL;
+
+/*
+ * @@ Section 2 - SANE & scanner init code
+ */
+
+/*
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init: start\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: canon_dr backend %d.%d.%d, from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ DBG (10, "sane_init: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ */
+/*
+ * Read the config file, find scanners with help from sanei_*
+ * and store in global device structs
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct scanner * s;
+ struct scanner * prev = NULL;
+ char line[PATH_MAX];
+ const char *lp;
+ FILE *fp;
+ int num_devices=0;
+ int i=0;
+
+ local_only = local_only; /* get rid of compiler warning */
+
+ DBG (10, "sane_get_devices: start\n");
+
+ /* mark all existing scanners as missing, attach_one will remove mark */
+ for (s = scanner_devList; s; s = s->next) {
+ s->missing = 1;
+ }
+
+ sanei_usb_init();
+
+ /* reset globals before reading the file */
+ default_globals();
+
+ fp = sanei_config_open (CANON_DR_CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n",
+ CANON_DR_CONFIG_FILE);
+
+ while (sanei_config_read (line, PATH_MAX, fp)) {
+
+ lp = line;
+
+ /* ignore comments */
+ if (*lp == '#')
+ continue;
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if (!strncmp ("option", lp, 6) && isspace (lp[6])) {
+
+ lp += 6;
+ lp = sanei_config_skip_whitespace (lp);
+
+ /* BUFFERSIZE: > 4K */
+ if (!strncmp (lp, "buffer-size", 11) && isspace (lp[11])) {
+
+ int buf;
+ lp += 11;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if (buf < 4096) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" "
+ "(%d) is < 4096, ignoring!\n", buf);
+ continue;
+ }
+
+ if (buf > global_buffer_size_default) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" "
+ "(%d) is > %d, scanning problems may result\n", buf,
+ global_buffer_size_default);
+ }
+
+ DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n",
+ buf);
+
+ global_buffer_size = buf;
+ }
+
+ /* PADDED READ: we clamp to 0 or 1 */
+ else if (!strncmp (lp, "padded-read", 11) && isspace (lp[11])) {
+
+ int buf;
+ lp += 11;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if (buf < 0) {
+ DBG (5, "sane_get_devices: config option \"padded-read\" "
+ "(%d) is < 0, ignoring!\n", buf);
+ continue;
+ }
+
+ if (buf > 1) {
+ DBG (5, "sane_get_devices: config option \"padded-read\" "
+ "(%d) is > 1, ignoring!\n", buf);
+ continue;
+ }
+
+ DBG (15, "sane_get_devices: setting \"padded-read\" to %d\n",
+ buf);
+
+ global_padded_read = buf;
+ }
+
+ /* DUPLEXOFFSET: < 1200 */
+ else if (!strncmp (lp, "duplex-offset", 13) && isspace (lp[13])) {
+
+ int buf;
+ lp += 13;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if (buf > 1200) {
+ DBG (5, "sane_get_devices: config option \"duplex-offset\" "
+ "(%d) is > 1200, ignoring!\n", buf);
+ continue;
+ }
+
+ if (buf < 0) {
+ DBG (5, "sane_get_devices: config option \"duplex-offset\" "
+ "(%d) is < 0, ignoring!\n", buf);
+ continue;
+ }
+
+ DBG (15, "sane_get_devices: setting \"duplex-offset\" to %d\n",
+ buf);
+
+ global_duplex_offset = buf;
+ }
+
+ /* VENDOR: we ingest up to 8 bytes */
+ else if (!strncmp (lp, "vendor-name", 11) && isspace (lp[11])) {
+
+ lp += 11;
+ lp = sanei_config_skip_whitespace (lp);
+ strncpy(global_vendor_name, lp, 8);
+ global_vendor_name[8] = 0;
+
+ DBG (15, "sane_get_devices: setting \"vendor-name\" to %s\n",
+ global_vendor_name);
+ }
+
+ /* MODEL: we ingest up to 16 bytes */
+ else if (!strncmp (lp, "model-name", 10) && isspace (lp[10])) {
+
+ lp += 10;
+ lp = sanei_config_skip_whitespace (lp);
+ strncpy(global_model_name, lp, 16);
+ global_model_name[16] = 0;
+
+ DBG (15, "sane_get_devices: setting \"model-name\" to %s\n",
+ global_model_name);
+ }
+
+ /* VERSION: we ingest up to 4 bytes */
+ else if (!strncmp (lp, "version-name", 12) && isspace (lp[12])) {
+
+ lp += 12;
+ lp = sanei_config_skip_whitespace (lp);
+ strncpy(global_version_name, lp, 4);
+ global_version_name[4] = 0;
+
+ DBG (15, "sane_get_devices: setting \"version-name\" to %s\n",
+ global_version_name);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: config option \"%s\" unrecognized "
+ "- ignored.\n", lp);
+ }
+ }
+ else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_usb_attach_matching_devices(lp, attach_one_usb);
+
+ /* re-default these after reading the usb line */
+ default_globals();
+ }
+ else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_config_attach_matching_devices (lp, attach_one_scsi);
+
+ /* re-default these after reading the scsi line */
+ default_globals();
+ }
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" unrecognized - "
+ "ignored.\n", lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: missing required config file '%s'!\n",
+ CANON_DR_CONFIG_FILE);
+ }
+
+ /*delete missing scanners from list*/
+ for (s = scanner_devList; s;) {
+ if(s->missing){
+ DBG (5, "sane_get_devices: missing scanner %s\n",s->device_name);
+
+ /*splice s out of list by changing pointer in prev to next*/
+ if(prev){
+ prev->next = s->next;
+ free(s);
+ s=prev->next;
+ }
+ /*remove s from head of list, using prev to cache it*/
+ else{
+ prev = s;
+ s = s->next;
+ free(prev);
+ prev=NULL;
+
+ /*reset head to next s*/
+ scanner_devList = s;
+ }
+ }
+ else{
+ prev = s;
+ s=prev->next;
+ }
+ }
+
+ for (s = scanner_devList; s; s=s->next) {
+ DBG (15, "sane_get_devices: found scanner %s\n",s->device_name);
+ num_devices++;
+ }
+
+ DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
+ if (!sane_devArray)
+ return SANE_STATUS_NO_MEM;
+
+ for (s = scanner_devList; s; s=s->next) {
+ sane_devArray[i++] = (SANE_Device *)&s->sane;
+ }
+ sane_devArray[i] = 0;
+
+ if(device_list){
+ *device_list = sane_devArray;
+ }
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return ret;
+}
+
+/* callbacks used by sane_get_devices */
+static SANE_Status
+attach_one_scsi (const char *device_name)
+{
+ return attach_one(device_name,CONNECTION_SCSI);
+}
+
+static SANE_Status
+attach_one_usb (const char *device_name)
+{
+ return attach_one(device_name,CONNECTION_USB);
+}
+
+/* build the scanner struct and link to global list
+ * unless struct is already loaded, then pretend
+ */
+static SANE_Status
+attach_one (const char *device_name, int connType)
+{
+ struct scanner *s;
+ int ret;
+
+ DBG (10, "attach_one: start\n");
+ DBG (15, "attach_one: looking for '%s'\n", device_name);
+
+ for (s = scanner_devList; s; s = s->next) {
+ if (strcmp (s->device_name, device_name) == 0){
+ DBG (10, "attach_one: already attached!\n");
+ s->missing = 0;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* build a scanner struct to hold it */
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* config file settings */
+ s->buffer_size = global_buffer_size;
+ s->padded_read = global_padded_read;
+ s->duplex_offset = global_duplex_offset;
+
+ /* copy the device name */
+ strcpy (s->device_name, device_name);
+
+ /* connect the fd */
+ s->connection = connType;
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ free (s);
+ return ret;
+ }
+
+ /* query the device to load its vendor/model/version, */
+ /* if config file doesn't give all three */
+ if ( !strlen(global_vendor_name)
+ || !strlen(global_model_name)
+ || !strlen(global_version_name)
+ ){
+ ret = init_inquire (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: inquiry failed\n");
+ return ret;
+ }
+ }
+
+ /* override any inquiry settings with those from config file */
+ if(strlen(global_vendor_name))
+ strcpy(s->vendor_name, global_vendor_name);
+ if(strlen(global_model_name))
+ strcpy(s->model_name, global_model_name);
+ if(strlen(global_version_name))
+ strcpy(s->version_name, global_version_name);
+
+ /* load detailed specs/capabilities from the device */
+ /* if a model cannot support inquiry vpd, this function will die */
+ ret = init_vpd (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: vpd failed\n");
+ return ret;
+ }
+
+ /* clean up the scanner struct based on model */
+ /* this is the big piece of model specific code */
+ ret = init_model (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: model failed\n");
+ return ret;
+ }
+
+ /* enable/read the buttons */
+ ret = init_panel (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: model failed\n");
+ return ret;
+ }
+
+ /* sets SANE option 'values' to good defaults */
+ ret = init_user (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: user failed\n");
+ return ret;
+ }
+
+ ret = init_options (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: options failed\n");
+ return ret;
+ }
+
+ /* load strings into sane_device struct */
+ s->sane.name = s->device_name;
+ s->sane.vendor = s->vendor_name;
+ s->sane.model = s->model_name;
+ s->sane.type = "scanner";
+
+ /* change name in sane_device struct if scanner has serial number
+ ret = init_serial (s);
+ if (ret == SANE_STATUS_GOOD) {
+ s->sane.name = s->serial_name;
+ }
+ else{
+ DBG (5, "attach_one: serial number unsupported?\n");
+ }
+ */
+
+ /* we close the connection, so that another backend can talk to scanner */
+ disconnect_fd(s);
+
+ /* store this scanner in global vars */
+ s->next = scanner_devList;
+ scanner_devList = s;
+
+ DBG (10, "attach_one: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * connect the fd in the scanner struct
+ */
+static SANE_Status
+connect_fd (struct scanner *s)
+{
+ SANE_Status ret;
+ int buffer_size = s->buffer_size;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else if (s->connection == CONNECTION_USB) {
+ DBG (15, "connect_fd: opening USB device (%s)\n", s->device_name);
+ ret = sanei_usb_open (s->device_name, &(s->fd));
+ if(!ret){
+ ret = sanei_usb_clear_halt(s->fd);
+ }
+ }
+ else {
+ DBG (15, "connect_fd: opening SCSI device (%s)\n", s->device_name);
+ ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler, s,
+ &s->buffer_size);
+ if(!ret && buffer_size != s->buffer_size){
+ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n",
+ buffer_size, s->buffer_size);
+ }
+ }
+
+ if(ret == SANE_STATUS_GOOD){
+
+ /* first generation usb scanners can get flaky if not closed
+ * properly after last use. very first commands sent to device
+ * must be prepared to correct this- see wait_scanner() */
+ ret = wait_scanner(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "connect_fd: could not wait_scanner\n");
+ disconnect_fd(s);
+ }
+
+ }
+ else{
+ DBG (5, "connect_fd: could not open device: %d\n", ret);
+ }
+
+ DBG (10, "connect_fd: finish\n");
+
+ return ret;
+}
+
+/*
+ * This routine will check if a certain device is a Canon scanner
+ * It also copies interesting data from INQUIRY into the handle structure
+ */
+static SANE_Status
+init_inquire (struct scanner *s)
+{
+ int i;
+ SANE_Status ret;
+
+ unsigned char cmd[INQUIRY_len];
+ size_t cmdLen = INQUIRY_len;
+
+ unsigned char in[INQUIRY_std_len];
+ size_t inLen = INQUIRY_std_len;
+
+ DBG (10, "init_inquire: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, INQUIRY_code);
+ set_IN_return_size (cmd, inLen);
+ set_IN_evpd (cmd, 0);
+ set_IN_page_code (cmd, 0);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ DBG (10, "init_inquire: failed: %d\n", ret);
+ return ret;
+ }
+
+ if (get_IN_periph_devtype (in) != IN_periph_devtype_scanner){
+ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ get_IN_vendor (in, s->vendor_name);
+ get_IN_product (in, s->model_name);
+ get_IN_version (in, s->version_name);
+
+ s->vendor_name[8] = 0;
+ s->model_name[16] = 0;
+ s->version_name[4] = 0;
+
+ /* gobble trailing spaces */
+ for (i = 7; s->vendor_name[i] == ' ' && i >= 0; i--)
+ s->vendor_name[i] = 0;
+ for (i = 15; s->model_name[i] == ' ' && i >= 0; i--)
+ s->model_name[i] = 0;
+ for (i = 3; s->version_name[i] == ' ' && i >= 0; i--)
+ s->version_name[i] = 0;
+
+ /*check for vendor name*/
+ if (strcmp ("CANON", s->vendor_name)) {
+ DBG (5, "The device at '%s' is reported to be made by '%s'\n",
+ s->device_name, s->vendor_name);
+ DBG (5, "This backend only supports Canon products.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*check for model name*/
+ if (strncmp ("DR", s->model_name, 2)
+ && strncmp ("CR", s->model_name, 2)
+ && strncmp ("P-", s->model_name, 2)
+ ) {
+ DBG (5, "The device at '%s' is reported to be a '%s'\n",
+ s->device_name, s->model_name);
+ DBG (5, "This backend only supports Canon P-, CR & DR-series products.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "init_inquire: Found %s scanner %s version %s at %s\n",
+ s->vendor_name, s->model_name, s->version_name, s->device_name);
+
+ DBG (10, "init_inquire: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Use INQUIRY VPD to setup more detail about the scanner
+ */
+static SANE_Status
+init_vpd (struct scanner *s)
+{
+ SANE_Status ret;
+
+ unsigned char cmd[INQUIRY_len];
+ size_t cmdLen = INQUIRY_len;
+
+ unsigned char in[INQUIRY_vpd_len];
+ size_t inLen = INQUIRY_vpd_len;
+
+ DBG (10, "init_vpd: start\n");
+
+ /* get EVPD */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, INQUIRY_code);
+ set_IN_return_size (cmd, inLen);
+ set_IN_evpd (cmd, 1);
+ set_IN_page_code (cmd, 0xf0);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ DBG (15, "init_vpd: length=%0x\n",get_IN_page_length (in));
+
+ /* This scanner supports vital product data.
+ * Use this data to set dpi-lists etc. */
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+
+ DBG (15, "standard options\n");
+
+ s->basic_x_res = get_IN_basic_x_res (in);
+ DBG (15, " basic x res: %d dpi\n",s->basic_x_res);
+
+ s->basic_y_res = get_IN_basic_y_res (in);
+ DBG (15, " basic y res: %d dpi\n",s->basic_y_res);
+
+ s->step_x_res = get_IN_step_x_res (in);
+ DBG (15, " step x res: %d dpi\n", s->step_x_res);
+
+ s->step_y_res = get_IN_step_y_res (in);
+ DBG (15, " step y res: %d dpi\n", s->step_y_res);
+
+ s->max_x_res = get_IN_max_x_res (in);
+ DBG (15, " max x res: %d dpi\n", s->max_x_res);
+
+ s->max_y_res = get_IN_max_y_res (in);
+ DBG (15, " max y res: %d dpi\n", s->max_y_res);
+
+ s->min_x_res = get_IN_min_x_res (in);
+ DBG (15, " min x res: %d dpi\n", s->min_x_res);
+
+ s->min_y_res = get_IN_min_y_res (in);
+ DBG (15, " min y res: %d dpi\n", s->min_y_res);
+
+ /* some scanners list B&W resolutions. */
+ s->std_res_x[DPI_60] = get_IN_std_res_60 (in);
+ s->std_res_y[DPI_60] = s->std_res_x[DPI_60];
+ DBG (15, " 60 dpi: %d\n", s->std_res_x[DPI_60]);
+
+ s->std_res_x[DPI_75] = get_IN_std_res_75 (in);
+ s->std_res_y[DPI_75] = s->std_res_x[DPI_75];
+ DBG (15, " 75 dpi: %d\n", s->std_res_x[DPI_75]);
+
+ s->std_res_x[DPI_100] = get_IN_std_res_100 (in);
+ s->std_res_y[DPI_100] = s->std_res_x[DPI_100];
+ DBG (15, " 100 dpi: %d\n", s->std_res_x[DPI_100]);
+
+ s->std_res_x[DPI_120] = get_IN_std_res_120 (in);
+ s->std_res_y[DPI_120] = s->std_res_x[DPI_120];
+ DBG (15, " 120 dpi: %d\n", s->std_res_x[DPI_120]);
+
+ s->std_res_x[DPI_150] = get_IN_std_res_150 (in);
+ s->std_res_y[DPI_150] = s->std_res_x[DPI_150];
+ DBG (15, " 150 dpi: %d\n", s->std_res_x[DPI_150]);
+
+ s->std_res_x[DPI_160] = get_IN_std_res_160 (in);
+ s->std_res_y[DPI_160] = s->std_res_x[DPI_160];
+ DBG (15, " 160 dpi: %d\n", s->std_res_x[DPI_160]);
+
+ s->std_res_x[DPI_180] = get_IN_std_res_180 (in);
+ s->std_res_y[DPI_180] = s->std_res_x[DPI_180];
+ DBG (15, " 180 dpi: %d\n", s->std_res_x[DPI_180]);
+
+ s->std_res_x[DPI_200] = get_IN_std_res_200 (in);
+ s->std_res_y[DPI_200] = s->std_res_x[DPI_200];
+ DBG (15, " 200 dpi: %d\n", s->std_res_x[DPI_200]);
+
+ s->std_res_x[DPI_240] = get_IN_std_res_240 (in);
+ s->std_res_y[DPI_240] = s->std_res_x[DPI_240];
+ DBG (15, " 240 dpi: %d\n", s->std_res_x[DPI_240]);
+
+ s->std_res_x[DPI_300] = get_IN_std_res_300 (in);
+ s->std_res_y[DPI_300] = s->std_res_x[DPI_300];
+ DBG (15, " 300 dpi: %d\n", s->std_res_x[DPI_300]);
+
+ s->std_res_x[DPI_320] = get_IN_std_res_320 (in);
+ s->std_res_y[DPI_320] = s->std_res_x[DPI_320];
+ DBG (15, " 320 dpi: %d\n", s->std_res_x[DPI_320]);
+
+ s->std_res_x[DPI_400] = get_IN_std_res_400 (in);
+ s->std_res_y[DPI_400] = s->std_res_x[DPI_400];
+ DBG (15, " 400 dpi: %d\n", s->std_res_x[DPI_400]);
+
+ s->std_res_x[DPI_480] = get_IN_std_res_480 (in);
+ s->std_res_y[DPI_480] = s->std_res_x[DPI_480];
+ DBG (15, " 480 dpi: %d\n", s->std_res_x[DPI_480]);
+
+ s->std_res_x[DPI_600] = get_IN_std_res_600 (in);
+ s->std_res_y[DPI_600] = s->std_res_x[DPI_600];
+ DBG (15, " 600 dpi: %d\n", s->std_res_x[DPI_600]);
+
+ s->std_res_x[DPI_800] = get_IN_std_res_800 (in);
+ s->std_res_y[DPI_800] = s->std_res_x[DPI_800];
+ DBG (15, " 800 dpi: %d\n", s->std_res_x[DPI_800]);
+
+ s->std_res_x[DPI_1200] = get_IN_std_res_1200 (in);
+ s->std_res_y[DPI_1200] = s->std_res_x[DPI_1200];
+ DBG (15, " 1200 dpi: %d\n", s->std_res_x[DPI_1200]);
+
+ /* maximum window width and length are reported in basic units.*/
+ s->max_x = get_IN_window_width(in) * 1200 / s->basic_x_res;
+ DBG(15, " max width: %d (%2.2f in)\n",s->max_x,(float)s->max_x/1200);
+
+ s->max_y = get_IN_window_length(in) * 1200 / s->basic_y_res;
+ DBG(15, " max length: %d (%2.2f in)\n",s->max_y,(float)s->max_y/1200);
+
+ DBG (15, " AWD: %d\n", get_IN_awd(in));
+ DBG (15, " CE Emphasis: %d\n", get_IN_ce_emphasis(in));
+ DBG (15, " C Emphasis: %d\n", get_IN_c_emphasis(in));
+ DBG (15, " High quality: %d\n", get_IN_high_quality(in));
+
+ /* known modes FIXME more here? */
+ s->can_grayscale = get_IN_multilevel (in);
+ DBG (15, " grayscale: %d\n", s->can_grayscale);
+
+ s->can_halftone = get_IN_half_tone (in);
+ DBG (15, " halftone: %d\n", s->can_halftone);
+
+ s->can_monochrome = get_IN_monochrome (in);
+ DBG (15, " monochrome: %d\n", s->can_monochrome);
+
+ s->can_overflow = get_IN_overflow(in);
+ DBG (15, " overflow: %d\n", s->can_overflow);
+ }
+ /*FIXME no vpd, set some defaults? */
+ else{
+ DBG (5, "init_vpd: Your scanner does not support VPD?\n");
+ DBG (5, "init_vpd: Please contact kitno455 at gmail dot com\n");
+ DBG (5, "init_vpd: with details of your scanner model.\n");
+ }
+
+ DBG (10, "init_vpd: finish\n");
+
+ return ret;
+}
+
+/*
+ * get model specific info that is not in vpd, and correct
+ * errors in vpd data. struct is already initialized to 0.
+ */
+static SANE_Status
+init_model (struct scanner *s)
+{
+
+ DBG (10, "init_model: start\n");
+
+ s->reverse_by_mode[MODE_LINEART] = 1;
+ s->reverse_by_mode[MODE_HALFTONE] = 1;
+ s->reverse_by_mode[MODE_GRAYSCALE] = 0;
+ s->reverse_by_mode[MODE_COLOR] = 0;
+
+ s->always_op = 1;
+ s->has_df = 1;
+ s->has_btc = 1;
+ s->has_counter = 1;
+ s->has_adf = 1;
+ s->has_duplex = 1;
+ s->has_buffer = 1;
+ s->can_read_panel = 1;
+ s->can_write_panel = 1;
+ s->has_ssm = 1;
+
+ s->brightness_steps = 255;
+ s->contrast_steps = 255;
+ s->threshold_steps = 255;
+
+ s->ppl_mod = 1;
+ s->bg_color = 0xee;
+
+ /* assume these are same as adf, override below */
+ s->valid_x = s->max_x;
+ s->max_x_fb = s->max_x;
+ s->max_y_fb = s->max_y;
+
+ /* generic settings missing from vpd */
+ if (strstr (s->model_name,"C")){
+ s->can_color = 1;
+ }
+
+ /* specific settings missing from vpd */
+ if (strstr (s->model_name,"DR-9080")
+ || strstr (s->model_name,"DR-7580")){
+#ifdef SANE_FRAME_JPEG
+ s->has_comp_JPEG = 1;
+#endif
+ s->rgb_format = 2;
+ }
+
+ else if (strstr (s->model_name,"DR-7090")){
+ s->has_flatbed = 1;
+ }
+
+ else if (strstr (s->model_name,"DR-9050")
+ || strstr (s->model_name,"DR-7550")
+ || strstr (s->model_name,"DR-6050")){
+
+ /*missing*/
+ s->std_res_x[DPI_100]=1;
+ s->std_res_y[DPI_100]=1;
+ s->std_res_x[DPI_150]=1;
+ s->std_res_y[DPI_150]=1;
+ s->std_res_x[DPI_200]=1;
+ s->std_res_y[DPI_200]=1;
+ s->std_res_x[DPI_240]=1;
+ s->std_res_y[DPI_240]=1;
+ s->std_res_x[DPI_300]=1;
+ s->std_res_y[DPI_300]=1;
+ s->std_res_x[DPI_400]=1;
+ s->std_res_y[DPI_400]=1;
+ s->std_res_x[DPI_600]=1;
+ s->std_res_y[DPI_600]=1;
+
+ /*weirdness*/
+ s->has_ssm = 0;
+ s->has_ssm2 = 1;
+ }
+
+ else if (strstr (s->model_name,"DR-4080")
+ || strstr (s->model_name,"DR-4580")
+ || strstr (s->model_name,"DR-7080")){
+ s->has_flatbed = 1;
+ }
+
+ else if (strstr (s->model_name,"DR-2580")){
+ s->invert_tly = 1;
+ s->rgb_format = 1;
+ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB;
+ s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB;
+ s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG;
+ s->duplex_interlace = DUPLEX_INTERLACE_FBFB;
+ s->need_ccal = 1;
+ s->need_fcal = 1;
+ /*s->duplex_offset = 432; now set in config file*/
+ s->duplex_offset_side = SIDE_BACK;
+
+ /*lies*/
+ s->can_halftone=0;
+ s->can_monochrome=0;
+ }
+
+ else if (strstr (s->model_name,"DR-2510")
+ || strstr (s->model_name,"DR-2010")
+ ){
+ s->rgb_format = 1;
+ s->always_op = 0;
+ s->unknown_byte2 = 0x80;
+ s->fixed_width = 1;
+ s->valid_x = 8.5 * 1200;
+ s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_2510;
+ s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_2510;
+ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_2510;
+ s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_2510;
+ s->duplex_interlace = DUPLEX_INTERLACE_2510;
+ /*s->duplex_offset = 400; now set in config file*/
+ s->need_ccal = 1;
+ s->need_fcal = 1;
+ s->sw_lut = 1;
+ /*s->invert_tly = 1;*/
+
+ /*only in Y direction, so we trash them in X*/
+ s->std_res_x[DPI_100]=0;
+ s->std_res_x[DPI_150]=0;
+ s->std_res_x[DPI_200]=0;
+ s->std_res_x[DPI_240]=0;
+ s->std_res_x[DPI_400]=0;
+
+ /*lies*/
+ s->can_halftone=0;
+ s->can_monochrome=0;
+ }
+
+ /* copied from 2510, possibly incorrect */
+ else if (strstr (s->model_name,"DR-3010")){
+ s->rgb_format = 1;
+ s->always_op = 0;
+ s->unknown_byte2 = 0x80;
+ s->fixed_width = 1;
+ s->valid_x = 8.5 * 1200;
+ s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_2510;
+ s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_2510;
+ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_2510;
+ s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_2510;
+ s->duplex_interlace = DUPLEX_INTERLACE_2510;
+ /*s->duplex_offset = 400; now set in config file*/
+ s->need_ccal = 1;
+ s->need_fcal = 1;
+ s->sw_lut = 1;
+ s->invert_tly = 1;
+
+ /*only in Y direction, so we trash them in X*/
+ s->std_res_x[DPI_100]=0;
+ s->std_res_x[DPI_150]=0;
+ s->std_res_x[DPI_200]=0;
+ s->std_res_x[DPI_240]=0;
+ s->std_res_x[DPI_400]=0;
+
+ /*lies*/
+ s->can_halftone=0;
+ s->can_monochrome=0;
+ }
+
+ else if (strstr (s->model_name,"DR-2050")
+ || strstr (s->model_name,"DR-2080")){
+ s->can_write_panel = 0;
+ s->has_df = 0;
+ s->fixed_width = 1;
+ s->even_Bpl = 1;
+ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB;
+ s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB;
+ s->duplex_interlace = DUPLEX_INTERLACE_FBFB;
+ s->need_fcal_buffer = 1;
+ s->bg_color = 0x08;
+ /*s->duplex_offset = 840; now set in config file*/
+ s->sw_lut = 1;
+
+ /*lies*/
+ s->can_halftone=0;
+ s->can_monochrome=0;
+ }
+
+ else if (strstr (s->model_name,"DR-3080")){
+ s->can_write_panel = 0;
+ s->has_df = 0;
+ s->has_btc = 0;
+ }
+
+ else if (strstr (s->model_name,"DR-5060F")){
+ s->can_write_panel = 0;
+ s->has_df = 0;
+ s->has_btc = 0;
+ s->ppl_mod = 32;
+ s->reverse_by_mode[MODE_LINEART] = 0;
+ s->reverse_by_mode[MODE_HALFTONE] = 0;
+ }
+
+ else if (strstr (s->model_name,"DR-5020")){
+ s->can_read_panel = 0;
+ s->can_write_panel = 0;
+ s->has_df = 0;
+ s->has_btc = 0;
+ s->ppl_mod = 32;
+ s->reverse_by_mode[MODE_LINEART] = 0;
+ s->reverse_by_mode[MODE_HALFTONE] = 0;
+ }
+
+ else if (strstr (s->model_name, "P-208")) {
+ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB;
+ s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB;
+ s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG;
+ s->duplex_interlace = DUPLEX_INTERLACE_FBFB;
+ s->need_ccal = 1;
+ s->invert_tly = 1;
+ s->can_color = 1;
+ s->unknown_byte2 = 0x88;
+ s->rgb_format = 1;
+ s->has_ssm_pay_head_len = 1;
+ s->ppl_mod = 8;
+ s->ccal_version = 3;
+ }
+
+ else if (strstr (s->model_name, "P-215")) {
+ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_rRgGbB;
+ s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB;
+ s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_gG;
+ s->duplex_interlace = DUPLEX_INTERLACE_FBFB;
+ s->need_ccal = 1;
+ s->invert_tly = 1;
+ s->can_color = 1;
+ s->unknown_byte2 = 0x88;
+ s->rgb_format = 1;
+ s->has_ssm_pay_head_len = 1;
+ s->ppl_mod = 8;
+ s->ccal_version = 3;
+ }
+
+ DBG (10, "init_model: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function enables the buttons and preloads the current panel values
+ */
+static SANE_Status
+init_panel (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "init_panel: start\n");
+
+ ret = read_panel(s,0);
+ if(ret){
+ DBG (5, "init_panel: disabling read_panel\n");
+ s->can_read_panel = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ s->panel_enable_led = 1;
+ s->panel_counter = 0;
+ ret = send_panel(s);
+ if(ret){
+ DBG (5, "init_panel: disabling send_panel\n");
+ s->can_write_panel = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "init_panel: finish\n");
+
+ return ret;
+}
+
+/*
+ * set good default user values.
+ * struct is already initialized to 0.
+ */
+static SANE_Status
+init_user (struct scanner *s)
+{
+
+ DBG (10, "init_user: start\n");
+
+ /* source */
+ if(s->has_flatbed)
+ s->u.source = SOURCE_FLATBED;
+ else if(s->has_adf)
+ s->u.source = SOURCE_ADF_FRONT;
+
+ /* scan mode */
+ if(s->can_monochrome)
+ s->u.mode=MODE_LINEART;
+ else if(s->can_halftone)
+ s->u.mode=MODE_HALFTONE;
+ else if(s->can_grayscale)
+ s->u.mode=MODE_GRAYSCALE;
+ else if(s->can_color)
+ s->u.mode=MODE_COLOR;
+
+ /*x and y res*/
+ s->u.dpi_x = s->basic_x_res;
+ s->u.dpi_y = s->basic_x_res;
+
+ /* page width US-Letter */
+ s->u.page_x = 8.5 * 1200;
+ if(s->u.page_x > s->valid_x){
+ s->u.page_x = s->valid_x;
+ }
+
+ /* page height US-Letter */
+ s->u.page_y = 11 * 1200;
+ if(s->u.page_y > s->max_y){
+ s->u.page_y = s->max_y;
+ }
+
+ /* bottom-right x */
+ s->u.br_x = s->u.page_x;
+
+ /* bottom-right y */
+ s->u.br_y = s->u.page_y;
+
+ s->threshold = 90;
+ s->compress_arg = 50;
+
+ DBG (10, "init_user: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function presets the "option" array to blank
+ */
+static SANE_Status
+init_options (struct scanner *s)
+{
+ int i;
+
+ DBG (10, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].name = "filler";
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ DBG (10, "init_options: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct scanner *dev = NULL;
+ struct scanner *s = NULL;
+ SANE_Status ret;
+
+ DBG (10, "sane_open: start\n");
+
+ if(scanner_devList){
+ DBG (15, "sane_open: searching currently attached scanners\n");
+ }
+ else{
+ DBG (15, "sane_open: no scanners currently attached, attaching\n");
+
+ ret = sane_get_devices(NULL,0);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+ }
+
+ if(name[0] == 0){
+ DBG (15, "sane_open: no device requested, using default\n");
+ s = scanner_devList;
+ }
+ else{
+ DBG (15, "sane_open: device %s requested\n", name);
+
+ for (dev = scanner_devList; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, name) == 0
+ || strcmp (dev->device_name, name) == 0) { /*always allow sanei devname*/
+ s = dev;
+ break;
+ }
+ }
+ }
+
+ if (!s) {
+ DBG (5, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "sane_open: device %s found\n", s->sane.name);
+
+ *handle = s;
+
+ /* connect the fd so we can talk to scanner */
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ DBG (10, "sane_open: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * @@ Section 3 - SANE Options functions
+ */
+
+/*
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+ int i;
+ SANE_Option_Descriptor *opt = &s->opt[option];
+
+ DBG (20, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ /* "Mode" group -------------------------------------------------------- */
+ if(option==OPT_STANDARD_GROUP){
+ opt->name = SANE_NAME_STANDARD;
+ opt->title = SANE_TITLE_STANDARD;
+ opt->desc = SANE_DESC_STANDARD;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* source */
+ if(option==OPT_SOURCE){
+ i=0;
+ if(s->has_flatbed){
+ s->source_list[i++]=STRING_FLATBED;
+ }
+ if(s->has_adf){
+ s->source_list[i++]=STRING_ADFFRONT;
+
+ if(s->has_back){
+ s->source_list[i++]=STRING_ADFBACK;
+ }
+ if(s->has_duplex){
+ s->source_list[i++]=STRING_ADFDUPLEX;
+ }
+ }
+ s->source_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_SOURCE;
+ opt->title = SANE_TITLE_SCAN_SOURCE;
+ opt->desc = SANE_DESC_SCAN_SOURCE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->source_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* scan mode */
+ if(option==OPT_MODE){
+ i=0;
+ if(s->can_monochrome || s->can_grayscale || s->can_color){
+ s->mode_list[i++]=STRING_LINEART;
+ }
+ if(s->can_halftone){
+ s->mode_list[i++]=STRING_HALFTONE;
+ }
+ if(s->can_grayscale || s->can_color){
+ s->mode_list[i++]=STRING_GRAYSCALE;
+ }
+ if(s->can_color){
+ s->mode_list[i++]=STRING_COLOR;
+ }
+ s->mode_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* resolution */
+ /* some scanners only support fixed res
+ * build a list of possible choices */
+ /* we actually only look at the y resolution choices,
+ * and interpolate the image data as required for limited x resolutions */
+ if(option==OPT_RES){
+ i=0;
+ if(s->std_res_y[DPI_60] && s->max_y_res >= 60 && s->min_y_res <= 60){
+ s->res_list[++i] = 60;
+ }
+ if(s->std_res_y[DPI_75] && s->max_y_res >= 75 && s->min_y_res <= 75){
+ s->res_list[++i] = 75;
+ }
+ if(s->std_res_y[DPI_100] && s->max_y_res >= 100 && s->min_y_res <= 100){
+ s->res_list[++i] = 100;
+ }
+ if(s->std_res_y[DPI_120] && s->max_y_res >= 120 && s->min_y_res <= 120){
+ s->res_list[++i] = 120;
+ }
+ if(s->std_res_y[DPI_150] && s->max_y_res >= 150 && s->min_y_res <= 150){
+ s->res_list[++i] = 150;
+ }
+ if(s->std_res_y[DPI_160] && s->max_y_res >= 160 && s->min_y_res <= 160){
+ s->res_list[++i] = 160;
+ }
+ if(s->std_res_y[DPI_180] && s->max_y_res >= 180 && s->min_y_res <= 180){
+ s->res_list[++i] = 180;
+ }
+ if(s->std_res_y[DPI_200] && s->max_y_res >= 200 && s->min_y_res <= 200){
+ s->res_list[++i] = 200;
+ }
+ if(s->std_res_y[DPI_240] && s->max_y_res >= 240 && s->min_y_res <= 240){
+ s->res_list[++i] = 240;
+ }
+ if(s->std_res_y[DPI_300] && s->max_y_res >= 300 && s->min_y_res <= 300){
+ s->res_list[++i] = 300;
+ }
+ if(s->std_res_y[DPI_320] && s->max_y_res >= 320 && s->min_y_res <= 320){
+ s->res_list[++i] = 320;
+ }
+ if(s->std_res_y[DPI_400] && s->max_y_res >= 400 && s->min_y_res <= 400){
+ s->res_list[++i] = 400;
+ }
+ if(s->std_res_y[DPI_480] && s->max_y_res >= 480 && s->min_y_res <= 480){
+ s->res_list[++i] = 480;
+ }
+ if(s->std_res_y[DPI_600] && s->max_y_res >= 600 && s->min_y_res <= 600){
+ s->res_list[++i] = 600;
+ }
+ if(s->std_res_y[DPI_800] && s->max_y_res >= 800 && s->min_y_res <= 800){
+ s->res_list[++i] = 800;
+ }
+ if(s->std_res_y[DPI_1200] && s->max_y_res >= 1200 && s->min_y_res <= 1200){
+ s->res_list[++i] = 1200;
+ }
+ s->res_list[0] = i;
+
+ opt->name = SANE_NAME_SCAN_RESOLUTION;
+ opt->title = SANE_TITLE_SCAN_RESOLUTION;
+ opt->desc = SANE_DESC_SCAN_RESOLUTION;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_DPI;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ if(s->step_y_res){
+ s->res_range.min = s->min_y_res;
+ s->res_range.max = s->max_y_res;
+ s->res_range.quant = s->step_y_res;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->res_range;
+ }
+ else{
+ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opt->constraint.word_list = s->res_list;
+ }
+ }
+
+ /* "Geometry" group ---------------------------------------------------- */
+ if(option==OPT_GEOMETRY_GROUP){
+ opt->name = SANE_NAME_GEOMETRY;
+ opt->title = SANE_TITLE_GEOMETRY;
+ opt->desc = SANE_DESC_GEOMETRY;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* top-left x */
+ if(option==OPT_TL_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s));
+ s->tl_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_X;
+ opt->title = SANE_TITLE_SCAN_TL_X;
+ opt->desc = SANE_DESC_SCAN_TL_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->tl_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* top-left y */
+ if(option==OPT_TL_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s));
+ s->tl_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_Y;
+ opt->title = SANE_TITLE_SCAN_TL_Y;
+ opt->desc = SANE_DESC_SCAN_TL_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->tl_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right x */
+ if(option==OPT_BR_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s));
+ s->br_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_X;
+ opt->title = SANE_TITLE_SCAN_BR_X;
+ opt->desc = SANE_DESC_SCAN_BR_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->br_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right y */
+ if(option==OPT_BR_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s));
+ s->br_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_Y;
+ opt->title = SANE_TITLE_SCAN_BR_Y;
+ opt->desc = SANE_DESC_SCAN_BR_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->br_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* page width */
+ if(option==OPT_PAGE_WIDTH){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->paper_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->paper_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->valid_x);
+ s->paper_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_PAGE_WIDTH;
+ opt->title = SANE_TITLE_PAGE_WIDTH;
+ opt->desc = SANE_DESC_PAGE_WIDTH;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->paper_x_range;
+
+ if(s->has_adf){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->u.source == SOURCE_FLATBED){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* page height */
+ if(option==OPT_PAGE_HEIGHT){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->paper_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->paper_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y);
+ s->paper_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_PAGE_HEIGHT;
+ opt->title = SANE_TITLE_PAGE_HEIGHT;
+ opt->desc = SANE_DESC_PAGE_HEIGHT;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->paper_y_range;
+
+ if(s->has_adf){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->u.source == SOURCE_FLATBED){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* "Enhancement" group ------------------------------------------------- */
+ if(option==OPT_ENHANCEMENT_GROUP){
+ opt->name = SANE_NAME_ENHANCEMENT;
+ opt->title = SANE_TITLE_ENHANCEMENT;
+ opt->desc = SANE_DESC_ENHANCEMENT;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* brightness */
+ if(option==OPT_BRIGHTNESS){
+ opt->name = SANE_NAME_BRIGHTNESS;
+ opt->title = SANE_TITLE_BRIGHTNESS;
+ opt->desc = SANE_DESC_BRIGHTNESS;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->brightness_range;
+ s->brightness_range.quant=1;
+
+ /* some have hardware brightness (always 0 to 255?) */
+ /* some use LUT or GT (-127 to +127)*/
+ if (s->brightness_steps){
+ s->brightness_range.min=-127;
+ s->brightness_range.max=127;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* contrast */
+ if(option==OPT_CONTRAST){
+ opt->name = SANE_NAME_CONTRAST;
+ opt->title = SANE_TITLE_CONTRAST;
+ opt->desc = SANE_DESC_CONTRAST;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->contrast_range;
+ s->contrast_range.quant=1;
+
+ /* some have hardware contrast (always 0 to 255?) */
+ /* some use LUT or GT (-127 to +127)*/
+ if (s->contrast_steps){
+ s->contrast_range.min=-127;
+ s->contrast_range.max=127;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else {
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /*threshold*/
+ if(option==OPT_THRESHOLD){
+ opt->name = SANE_NAME_THRESHOLD;
+ opt->title = SANE_TITLE_THRESHOLD;
+ opt->desc = SANE_DESC_THRESHOLD;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->threshold_range;
+ s->threshold_range.min=0;
+ s->threshold_range.max=s->threshold_steps;
+ s->threshold_range.quant=1;
+
+ if (s->threshold_steps){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->u.mode != MODE_LINEART){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else {
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ if(option==OPT_RIF){
+ opt->name = "rif";
+ opt->title = "RIF";
+ opt->desc = "Reverse image format";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_rif)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* "Advanced" group ------------------------------------------------------ */
+ if(option==OPT_ADVANCED_GROUP){
+ opt->name = SANE_NAME_ADVANCED;
+ opt->title = SANE_TITLE_ADVANCED;
+ opt->desc = SANE_DESC_ADVANCED;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /*image compression*/
+ if(option==OPT_COMPRESS){
+ i=0;
+ s->compress_list[i++]=STRING_NONE;
+
+ if(s->has_comp_JPEG){
+ s->compress_list[i++]=STRING_JPEG;
+ }
+
+ s->compress_list[i]=NULL;
+
+ opt->name = "compression";
+ opt->title = "Compression";
+ opt->desc = "Enable compressed data. May crash your front-end program";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->compress_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (i > 1){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->u.mode != MODE_COLOR && s->u.mode != MODE_GRAYSCALE){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*image compression arg*/
+ if(option==OPT_COMPRESS_ARG){
+
+ opt->name = "compression-arg";
+ opt->title = "Compression argument";
+ opt->desc = "Level of JPEG compression. 1 is small file, 100 is large file.";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->compress_arg_range;
+ s->compress_arg_range.quant=1;
+
+ if(s->has_comp_JPEG){
+ s->compress_arg_range.min=0;
+ s->compress_arg_range.max=100;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ if(s->compress != COMP_JPEG){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed by length*/
+ if(option==OPT_DF_LENGTH){
+ opt->name = "df-length";
+ opt->title = "DF by length";
+ opt->desc = "Detect double feeds by comparing document lengths";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed by thickness */
+ if(option==OPT_DF_THICKNESS){
+
+ opt->name = "df-thickness";
+ opt->title = "DF by thickness";
+ opt->desc = "Detect double feeds using thickness sensor";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ if (1){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*deskew by roller*/
+ if(option==OPT_ROLLERDESKEW){
+ opt->name = "rollerdeskew";
+ opt->title = "Roller deskew";
+ opt->desc = "Request scanner to correct skewed pages mechanically";
+ opt->type = SANE_TYPE_BOOL;
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*deskew by software*/
+ if(option==OPT_SWDESKEW){
+ opt->name = "swdeskew";
+ opt->title = "Software deskew";
+ opt->desc = "Request driver to rotate skewed pages digitally";
+ opt->type = SANE_TYPE_BOOL;
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*software despeckle radius*/
+ if(option==OPT_SWDESPECK){
+
+ opt->name = "swdespeck";
+ opt->title = "Software despeckle diameter";
+ opt->desc = "Maximum diameter of lone dots to remove from scan";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->swdespeck_range;
+ s->swdespeck_range.quant=1;
+
+ if(1){
+ s->swdespeck_range.min=0;
+ s->swdespeck_range.max=9;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*crop by software*/
+ if(option==OPT_SWCROP){
+ opt->name = "swcrop";
+ opt->title = "Software crop";
+ opt->desc = "Request driver to remove border from pages digitally";
+ opt->type = SANE_TYPE_BOOL;
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*staple detection*/
+ if(option==OPT_STAPLEDETECT){
+ opt->name = "stapledetect";
+ opt->title = "Staple detect";
+ opt->desc = "Request scanner to halt if stapled pages are detected";
+ opt->type = SANE_TYPE_BOOL;
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*dropout color front*/
+ if(option==OPT_DROPOUT_COLOR_F){
+ s->do_color_list[0] = STRING_NONE;
+ s->do_color_list[1] = STRING_RED;
+ s->do_color_list[2] = STRING_GREEN;
+ s->do_color_list[3] = STRING_BLUE;
+ s->do_color_list[4] = STRING_EN_RED;
+ s->do_color_list[5] = STRING_EN_GREEN;
+ s->do_color_list[6] = STRING_EN_BLUE;
+ s->do_color_list[7] = NULL;
+
+ opt->name = "dropout-front";
+ opt->title = "Dropout color front";
+ opt->desc = "One-pass scanners use only one color during gray or binary scanning, useful for colored paper or ink";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->do_color_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (1){
+ opt->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ if(s->u.mode == MODE_COLOR)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*dropout color back*/
+ if(option==OPT_DROPOUT_COLOR_B){
+ s->do_color_list[0] = STRING_NONE;
+ s->do_color_list[1] = STRING_RED;
+ s->do_color_list[2] = STRING_GREEN;
+ s->do_color_list[3] = STRING_BLUE;
+ s->do_color_list[4] = STRING_EN_RED;
+ s->do_color_list[5] = STRING_EN_GREEN;
+ s->do_color_list[6] = STRING_EN_BLUE;
+ s->do_color_list[7] = NULL;
+
+ opt->name = "dropout-back";
+ opt->title = "Dropout color back";
+ opt->desc = "One-pass scanners use only one color during gray or binary scanning, useful for colored paper or ink";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->do_color_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (1){
+ opt->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ if(s->u.mode == MODE_COLOR)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*buffer mode*/
+ if(option==OPT_BUFFERMODE){
+ opt->name = "buffermode";
+ opt->title = "Buffer mode";
+ opt->desc = "Request scanner to read pages async into internal memory";
+ opt->type = SANE_TYPE_BOOL;
+ if (s->has_buffer)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SIDE){
+ opt->name = "side";
+ opt->title = "Duplex side";
+ opt->desc = "Tells which side (0=front, 1=back) of a duplex scan the next call to sane_read will return.";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* "Sensor" group ------------------------------------------------------ */
+ if(option==OPT_SENSOR_GROUP){
+ opt->name = SANE_NAME_SENSORS;
+ opt->title = SANE_TITLE_SENSORS;
+ opt->desc = SANE_DESC_SENSORS;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ if(option==OPT_START){
+ opt->name = "start";
+ opt->title = "Start/1 button";
+ opt->desc = "Big green or small 1 button";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if(!s->can_read_panel)
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_STOP){
+ opt->name = "stop";
+ opt->title = "Stop/2 button";
+ opt->desc = "Small orange or small 2 button";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if(!s->can_read_panel)
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_BUTT3){
+ opt->name = "button-3";
+ opt->title = "3 button";
+ opt->desc = "Small 3 button";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if(!s->can_read_panel)
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_NEWFILE){
+ opt->name = "newfile";
+ opt->title = "New File button";
+ opt->desc = "New File button";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if(!s->can_read_panel)
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_COUNTONLY){
+ opt->name = "countonly";
+ opt->title = "Count Only button";
+ opt->desc = "Count Only button";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if(!s->can_read_panel)
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_BYPASSMODE){
+ opt->name = "bypassmode";
+ opt->title = "Bypass Mode button";
+ opt->desc = "Bypass Mode button";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if(!s->can_read_panel)
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_COUNTER){
+ opt->name = "counter";
+ opt->title = "Counter";
+ opt->desc = "Scan counter";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->counter_range;
+ s->counter_range.min=0;
+ s->counter_range.max=500;
+ s->counter_range.quant=1;
+
+ if (s->can_read_panel && s->has_counter)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ return opt;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Int dummy = 0;
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ if (option >= NUM_OPTIONS) {
+ DBG (5, "sane_control_option: %d too big\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: %d inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE) {
+ SANE_Word * val_p = (SANE_Word *) val;
+
+ DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ *val_p = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if(s->u.source == SOURCE_FLATBED){
+ strcpy (val, STRING_FLATBED);
+ }
+ else if(s->u.source == SOURCE_ADF_FRONT){
+ strcpy (val, STRING_ADFFRONT);
+ }
+ else if(s->u.source == SOURCE_ADF_BACK){
+ strcpy (val, STRING_ADFBACK);
+ }
+ else if(s->u.source == SOURCE_ADF_DUPLEX){
+ strcpy (val, STRING_ADFDUPLEX);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if(s->u.mode == MODE_LINEART){
+ strcpy (val, STRING_LINEART);
+ }
+ else if(s->u.mode == MODE_HALFTONE){
+ strcpy (val, STRING_HALFTONE);
+ }
+ else if(s->u.mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->u.mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+ *val_p = s->u.dpi_x;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.tl_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.tl_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.br_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.br_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.page_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.page_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *val_p = s->brightness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *val_p = s->contrast;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ *val_p = s->threshold;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RIF:
+ *val_p = s->rif;
+ return SANE_STATUS_GOOD;
+
+ /* Advanced Group */
+ case OPT_COMPRESS:
+ if(s->compress == COMP_JPEG){
+ strcpy (val, STRING_JPEG);
+ }
+ else{
+ strcpy (val, STRING_NONE);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_COMPRESS_ARG:
+ *val_p = s->compress_arg;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_LENGTH:
+ *val_p = s->df_length;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_THICKNESS:
+ *val_p = s->df_thickness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ROLLERDESKEW:
+ *val_p = s->rollerdeskew;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESKEW:
+ *val_p = s->swdeskew;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESPECK:
+ *val_p = s->swdespeck;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWCROP:
+ *val_p = s->swcrop;
+ return SANE_STATUS_GOOD;
+
+ case OPT_STAPLEDETECT:
+ *val_p = s->stapledetect;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DROPOUT_COLOR_F:
+ switch (s->dropout_color_f) {
+ case COLOR_NONE:
+ strcpy (val, STRING_NONE);
+ break;
+ case COLOR_RED:
+ strcpy (val, STRING_RED);
+ break;
+ case COLOR_GREEN:
+ strcpy (val, STRING_GREEN);
+ break;
+ case COLOR_BLUE:
+ strcpy (val, STRING_BLUE);
+ break;
+ case COLOR_EN_RED:
+ strcpy (val, STRING_EN_RED);
+ break;
+ case COLOR_EN_GREEN:
+ strcpy (val, STRING_EN_GREEN);
+ break;
+ case COLOR_EN_BLUE:
+ strcpy (val, STRING_EN_BLUE);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_DROPOUT_COLOR_B:
+ switch (s->dropout_color_b) {
+ case COLOR_NONE:
+ strcpy (val, STRING_NONE);
+ break;
+ case COLOR_RED:
+ strcpy (val, STRING_RED);
+ break;
+ case COLOR_GREEN:
+ strcpy (val, STRING_GREEN);
+ break;
+ case COLOR_BLUE:
+ strcpy (val, STRING_BLUE);
+ break;
+ case COLOR_EN_RED:
+ strcpy (val, STRING_EN_RED);
+ break;
+ case COLOR_EN_GREEN:
+ strcpy (val, STRING_EN_GREEN);
+ break;
+ case COLOR_EN_BLUE:
+ strcpy (val, STRING_EN_BLUE);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BUFFERMODE:
+ *val_p = s->buffermode;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SIDE:
+ *val_p = s->side;
+ return SANE_STATUS_GOOD;
+
+ /* Sensor Group */
+ case OPT_START:
+ ret = read_panel(s,OPT_START);
+ *val_p = s->panel_start;
+ return ret;
+
+ case OPT_STOP:
+ ret = read_panel(s,OPT_STOP);
+ *val_p = s->panel_stop;
+ return ret;
+
+ case OPT_BUTT3:
+ ret = read_panel(s,OPT_BUTT3);
+ *val_p = s->panel_butt3;
+ return ret;
+
+ case OPT_NEWFILE:
+ ret = read_panel(s,OPT_NEWFILE);
+ *val_p = s->panel_new_file;
+ return ret;
+
+ case OPT_COUNTONLY:
+ ret = read_panel(s,OPT_COUNTONLY);
+ *val_p = s->panel_count_only;
+ return ret;
+
+ case OPT_BYPASSMODE:
+ ret = read_panel(s,OPT_BYPASSMODE);
+ *val_p = s->panel_bypass_mode;
+ return ret;
+
+ case OPT_COUNTER:
+ ret = read_panel(s,OPT_COUNTER);
+ *val_p = s->panel_counter;
+ return ret;
+
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE) {
+ int tmp;
+ SANE_Word val_c;
+ SANE_Status status;
+
+ DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
+
+ if ( s->started ) {
+ DBG (5, "sane_control_option: cant set, device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (5, "sane_control_option: bad value\n");
+ return status;
+ }
+
+ /* may have been changed by constrain, so dont copy until now */
+ val_c = *(SANE_Word *)val;
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option) {
+
+ /* Mode Group */
+ case OPT_SOURCE:
+ if (!strcmp (val, STRING_ADFFRONT)) {
+ tmp = SOURCE_ADF_FRONT;
+ }
+ else if (!strcmp (val, STRING_ADFBACK)) {
+ tmp = SOURCE_ADF_BACK;
+ }
+ else if (!strcmp (val, STRING_ADFDUPLEX)) {
+ tmp = SOURCE_ADF_DUPLEX;
+ }
+ else{
+ tmp = SOURCE_FLATBED;
+ }
+
+ if (s->u.source == tmp)
+ return SANE_STATUS_GOOD;
+
+ s->u.source = tmp;
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (!strcmp (val, STRING_LINEART)) {
+ tmp = MODE_LINEART;
+ }
+ else if (!strcmp (val, STRING_HALFTONE)) {
+ tmp = MODE_HALFTONE;
+ }
+ else if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp == s->u.mode)
+ return SANE_STATUS_GOOD;
+
+ s->u.mode = tmp;
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+
+ if (s->u.dpi_x == val_c && s->u.dpi_y == val_c)
+ return SANE_STATUS_GOOD;
+
+ s->u.dpi_x = val_c;
+ s->u.dpi_y = val_c;
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ /* Geometry Group */
+ case OPT_TL_X:
+ if (s->u.tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->u.tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ if (s->u.tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->u.tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ if (s->u.br_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->u.br_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ if (s->u.br_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->u.br_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ if (s->u.page_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->u.page_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ if (s->u.page_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->u.page_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ /* Enhancement Group */
+ case OPT_BRIGHTNESS:
+ s->brightness = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ s->contrast = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ s->threshold = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RIF:
+ s->rif = val_c;
+ return SANE_STATUS_GOOD;
+
+ /* Advanced Group */
+ case OPT_COMPRESS:
+ if (!strcmp (val, STRING_JPEG)) {
+ s->compress = COMP_JPEG;
+ }
+ else{
+ s->compress = COMP_NONE;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_COMPRESS_ARG:
+ s->compress_arg = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_LENGTH:
+ s->df_length = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_THICKNESS:
+ s->df_thickness = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ROLLERDESKEW:
+ s->rollerdeskew = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESKEW:
+ s->swdeskew = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESPECK:
+ s->swdespeck = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWCROP:
+ s->swcrop = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_STAPLEDETECT:
+ s->stapledetect = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DROPOUT_COLOR_F:
+ if (!strcmp(val, STRING_NONE))
+ s->dropout_color_f = COLOR_NONE;
+ else if (!strcmp(val, STRING_RED))
+ s->dropout_color_f = COLOR_RED;
+ else if (!strcmp(val, STRING_GREEN))
+ s->dropout_color_f = COLOR_GREEN;
+ else if (!strcmp(val, STRING_BLUE))
+ s->dropout_color_f = COLOR_BLUE;
+ else if (!strcmp(val, STRING_EN_RED))
+ s->dropout_color_f = COLOR_EN_RED;
+ else if (!strcmp(val, STRING_EN_GREEN))
+ s->dropout_color_f = COLOR_EN_GREEN;
+ else if (!strcmp(val, STRING_EN_BLUE))
+ s->dropout_color_f = COLOR_EN_BLUE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DROPOUT_COLOR_B:
+ if (!strcmp(val, STRING_NONE))
+ s->dropout_color_b = COLOR_NONE;
+ else if (!strcmp(val, STRING_RED))
+ s->dropout_color_b = COLOR_RED;
+ else if (!strcmp(val, STRING_GREEN))
+ s->dropout_color_b = COLOR_GREEN;
+ else if (!strcmp(val, STRING_BLUE))
+ s->dropout_color_b = COLOR_BLUE;
+ else if (!strcmp(val, STRING_EN_RED))
+ s->dropout_color_b = COLOR_EN_RED;
+ else if (!strcmp(val, STRING_EN_GREEN))
+ s->dropout_color_b = COLOR_EN_GREEN;
+ else if (!strcmp(val, STRING_EN_BLUE))
+ s->dropout_color_b = COLOR_EN_BLUE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BUFFERMODE:
+ s->buffermode = val_c;
+ return SANE_STATUS_GOOD;
+
+ }
+ } /* else */
+
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+ssm_buffer (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SET_SCAN_MODE_len];
+ size_t cmdLen = SET_SCAN_MODE_len;
+
+ unsigned char out[SSM_PAY_len];
+ size_t outLen = SSM_PAY_len;
+
+ DBG (10, "ssm_buffer: start\n");
+
+ if(!s->has_ssm){
+ DBG (10, "ssm_buffer: unsupported\n");
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SET_SCAN_MODE_code);
+ set_SSM_pf(cmd, 1);
+ set_SSM_pay_len(cmd, outLen);
+
+ memset(out,0,outLen);
+ if(s->has_ssm_pay_head_len){
+ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len);
+ }
+ set_SSM_page_code(out, SM_pc_buffer);
+ set_SSM_page_len(out, SSM_PAGE_len);
+
+ if(s->s.source == SOURCE_ADF_DUPLEX){
+ set_SSM_BUFF_duplex(out, 1);
+ }
+ else if(s->s.source == SOURCE_FLATBED){
+ set_SSM_BUFF_fb(out, 1);
+ }
+ if(s->buffermode){
+ set_SSM_BUFF_async(out, 1);
+ }
+ if(0){
+ set_SSM_BUFF_ald(out, 1);
+ }
+ if(0){
+ set_SSM_BUFF_unk(out,1);
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "ssm_buffer: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+ssm_df (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SET_SCAN_MODE_len];
+ size_t cmdLen = SET_SCAN_MODE_len;
+
+ unsigned char out[SSM_PAY_len];
+ size_t outLen = SSM_PAY_len;
+
+ DBG (10, "ssm_df: start\n");
+
+ if(!s->has_ssm || !s->has_df){
+ DBG (10, "ssm_df: unsupported, finishing\n");
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SET_SCAN_MODE_code);
+ set_SSM_pf(cmd, 1);
+ set_SSM_pay_len(cmd, outLen);
+
+ memset(out,0,outLen);
+ if(s->has_ssm_pay_head_len){
+ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len);
+ }
+ set_SSM_page_code(out, SM_pc_df);
+ set_SSM_page_len(out, SSM_PAGE_len);
+
+ /* deskew by roller */
+ if(s->rollerdeskew){
+ set_SSM_DF_deskew_roll(out, 1);
+ }
+
+ /* staple detection */
+ if(s->stapledetect){
+ set_SSM_DF_staple(out, 1);
+ }
+
+ /* thickness */
+ if(s->df_thickness){
+ set_SSM_DF_thick(out, 1);
+ }
+
+ /* length */
+ if(s->df_length){
+ set_SSM_DF_len(out, 1);
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "ssm_df: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+ssm_do (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SET_SCAN_MODE_len];
+ size_t cmdLen = SET_SCAN_MODE_len;
+
+ unsigned char out[SSM_PAY_len];
+ size_t outLen = SSM_PAY_len;
+
+ DBG (10, "ssm_do: start\n");
+
+ if(!s->has_ssm || !s->can_color){
+ DBG (10, "ssm_do: unsupported, finishing\n");
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SET_SCAN_MODE_code);
+ set_SSM_pf(cmd, 1);
+ set_SSM_pay_len(cmd, outLen);
+
+ memset(out,0,outLen);
+ if(s->has_ssm_pay_head_len){
+ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len);
+ }
+ set_SSM_page_code(out, SM_pc_dropout);
+ set_SSM_page_len(out, SSM_PAGE_len);
+
+ set_SSM_DO_unk1(out, 0x03);
+
+ switch(s->dropout_color_f){
+ case COLOR_RED:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_f_do(out,SSM_DO_red);
+ break;
+ case COLOR_GREEN:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_f_do(out,SSM_DO_green);
+ break;
+ case COLOR_BLUE:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_f_do(out,SSM_DO_blue);
+ break;
+ case COLOR_EN_RED:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_f_en(out,SSM_DO_red);
+ break;
+ case COLOR_EN_GREEN:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_f_en(out,SSM_DO_green);
+ break;
+ case COLOR_EN_BLUE:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_f_en(out,SSM_DO_blue);
+ break;
+ }
+
+ switch(s->dropout_color_b){
+ case COLOR_RED:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_b_do(out,SSM_DO_red);
+ break;
+ case COLOR_GREEN:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_b_do(out,SSM_DO_green);
+ break;
+ case COLOR_BLUE:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_b_do(out,SSM_DO_blue);
+ break;
+ case COLOR_EN_RED:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_b_en(out,SSM_DO_red);
+ break;
+ case COLOR_EN_GREEN:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_b_en(out,SSM_DO_green);
+ break;
+ case COLOR_EN_BLUE:
+ set_SSM_DO_unk2(out, 0x05);
+ set_SSM_DO_b_en(out,SSM_DO_blue);
+ break;
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "ssm_do: finish\n");
+
+ return ret;
+}
+
+/* used by recent scanners. meaning of payloads unknown */
+static SANE_Status
+ssm2 (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SET_SCAN_MODE2_len];
+ size_t cmdLen = SET_SCAN_MODE2_len;
+
+ unsigned char out[SSM2_PAY_len];
+ size_t outLen = SSM2_PAY_len;
+
+ DBG (10, "ssm2:start\n");
+
+ if(!s->has_ssm2){
+ DBG (10, "ssm2: unsupported, finishing\n");
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SET_SCAN_MODE2_code);
+ /*FIXME: set this correctly */
+ set_SSM2_page_code(cmd, 0);
+ set_SSM2_pay_len(cmd, outLen);
+
+ /*FIXME: set these correctly */
+ memset(out,0,outLen);
+ set_SSM2_unk(out, 0);
+ set_SSM2_unk2(out, 0);
+ set_SSM2_unk3(out, 0);
+
+ /*
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );*/
+
+ DBG (10, "ssm2: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_panel(struct scanner *s,SANE_Int option)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char in[R_PANEL_len];
+ size_t inLen = R_PANEL_len;
+
+ DBG (10, "read_panel: start %d\n", option);
+
+ if(!s->can_read_panel){
+ DBG (10, "read_panel: unsupported, finishing\n");
+ return ret;
+ }
+
+ /* only run this if frontend has read previous value
+ * or if the caller does not want the data stored */
+ if (!option || !s->hw_read[option-OPT_START]) {
+
+ DBG (15, "read_panel: running\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, SR_datatype_panel);
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+ /*set flags indicating there is data to read*/
+ if(option)
+ memset(s->hw_read,1,sizeof(s->hw_read));
+
+ s->panel_start = get_R_PANEL_start(in);
+ s->panel_stop = get_R_PANEL_stop(in);
+ s->panel_butt3 = get_R_PANEL_butt3(in);
+ s->panel_new_file = get_R_PANEL_new_file(in);
+ s->panel_count_only = get_R_PANEL_count_only(in);
+ s->panel_bypass_mode = get_R_PANEL_bypass_mode(in);
+ s->panel_enable_led = get_R_PANEL_enable_led(in);
+ s->panel_counter = get_R_PANEL_counter(in);
+ ret = SANE_STATUS_GOOD;
+ }
+ }
+
+ if(option)
+ s->hw_read[option-OPT_START] = 0;
+
+ DBG (10, "read_panel: finish %d\n",s->panel_counter);
+
+ return ret;
+}
+
+static SANE_Status
+send_panel(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[SEND_len];
+ size_t cmdLen = SEND_len;
+
+ unsigned char out[S_PANEL_len];
+ size_t outLen = S_PANEL_len;
+
+ DBG (10, "send_panel: start\n");
+
+ if(!s->can_write_panel){
+ DBG (10, "send_panel: unsupported, finishing\n");
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SEND_code);
+ set_S_xfer_datatype (cmd, SR_datatype_panel);
+ set_S_xfer_length (cmd, outLen);
+
+ memset(out,0,outLen);
+ set_S_PANEL_enable_led(out,s->panel_enable_led);
+ set_S_PANEL_counter(out,s->panel_counter);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ if (ret == SANE_STATUS_EOF) {
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "send_panel: finish %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * @@ Section 4 - SANE scanning functions
+ */
+/*
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct scanner *s = (struct scanner *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ if(!s->started){
+ ret = update_params(s,0);
+ if(ret){
+ DBG (5, "sane_get_parameters: up error, returning %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* this backend only sends single frame images */
+ params->last_frame = 1;
+
+ params->format = s->i.format;
+ params->lines = s->i.height;
+ params->depth = s->i.bpp;
+ if(params->depth == 24) params->depth = 8;
+ params->pixels_per_line = s->i.width;
+ params->bytes_per_line = s->i.Bpl;
+
+ DBG(15,"sane_get_parameters: x: max=%d, page=%d, gpw=%d, res=%d\n",
+ s->valid_x, s->i.page_x, get_page_width(s), s->i.dpi_x);
+
+ DBG(15,"sane_get_parameters: y: max=%d, page=%d, gph=%d, res=%d\n",
+ s->max_y, s->i.page_y, get_page_height(s), s->i.dpi_y);
+
+ DBG(15,"sane_get_parameters: area: tlx=%d, brx=%d, tly=%d, bry=%d\n",
+ s->i.tl_x, s->i.br_x, s->i.tl_y, s->i.br_y);
+
+ DBG (15, "sane_get_parameters: params: ppl=%d, Bpl=%d, lines=%d\n",
+ params->pixels_per_line, params->bytes_per_line, params->lines);
+
+ DBG (15, "sane_get_parameters: params: format=%d, depth=%d, last=%d\n",
+ params->format, params->depth, params->last_frame);
+
+ DBG (10, "sane_get_parameters: finish\n");
+
+ return ret;
+}
+
+SANE_Status
+update_params(struct scanner *s, int calib)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "update_params: start\n");
+
+ s->u.width = (s->u.br_x - s->u.tl_x) * s->u.dpi_x / 1200;
+ s->u.height = (s->u.br_y - s->u.tl_y) * s->u.dpi_y / 1200;
+
+ if (s->u.mode == MODE_COLOR) {
+ s->u.format = SANE_FRAME_RGB;
+ s->u.bpp = 24;
+ }
+ else if (s->u.mode == MODE_GRAYSCALE) {
+ s->u.format = SANE_FRAME_GRAY;
+ s->u.bpp = 8;
+ }
+ else {
+ s->u.format = SANE_FRAME_GRAY;
+ s->u.bpp = 1;
+
+ /* round down to byte boundary */
+ s->u.width -= s->u.width % 8;
+ }
+
+ /* round down to pixel boundary for some scanners */
+ s->u.width -= s->u.width % s->ppl_mod;
+
+#ifdef SANE_FRAME_JPEG
+ /* jpeg requires 8x8 squares */
+ if(s->compress == COMP_JPEG && s->u.mode >= MODE_GRAYSCALE){
+ s->u.format = SANE_FRAME_JPEG;
+ s->u.width -= s->u.width % 8;
+ s->u.height -= s->u.height % 8;
+ }
+#endif
+
+ s->u.Bpl = s->u.width * s->u.bpp / 8;
+ s->u.valid_Bpl = s->u.Bpl;
+ s->u.valid_width = s->u.width;
+
+ DBG (15, "update_params: user params: w:%d h:%d m:%d f:%d b:%d\n",
+ s->u.width, s->u.height, s->u.mode, s->u.format, s->u.bpp);
+ DBG (15, "update_params: user params: B:%d vB:%d vw:%d\n",
+ s->u.Bpl, s->u.valid_Bpl, s->u.valid_width);
+ DBG (15, "update_params: user params: x b:%d t:%d d:%d y b:%d t:%d d:%d\n",
+ s->u.br_x, s->u.tl_x, s->u.dpi_x, s->u.br_y, s->u.tl_y, s->u.dpi_y);
+
+ /* some scanners are limited in their valid scan params
+ * make a second version of the params struct, but
+ * override the user's values with what the scanner can actually do */
+
+ memcpy(&s->s,&s->u,sizeof(struct img_params));
+
+ /*********** missing modes (move up to valid one) **************/
+ if(s->s.mode == MODE_LINEART && !s->can_monochrome){
+ s->s.mode = MODE_GRAYSCALE;
+ s->s.format = SANE_FRAME_GRAY;
+ s->s.bpp = 8;
+ }
+ if(s->s.mode == MODE_GRAYSCALE && !s->can_grayscale){
+ s->s.mode = MODE_COLOR;
+ s->s.format = SANE_FRAME_RGB;
+ s->s.bpp = 24;
+ }
+ if(s->s.mode == MODE_COLOR && !s->can_color){
+ DBG (5, "update_params: no valid mode\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /********** missing resolutions (move up to valid one) *********/
+ if(!s->step_x_res){
+ int i;
+ for(i=0;i<DPI_1200;i++){
+
+ /* this res is smaller or invalid, skip it */
+ if(s->s.dpi_x > dpi_list[i] || !s->std_res_x[i])
+ continue;
+
+ /* same & valid res, done */
+ if(s->s.dpi_x == dpi_list[i])
+ break;
+
+ /* different & valid res, switch */
+ s->s.dpi_x = dpi_list[i];
+ break;
+ }
+
+ if(i > DPI_1200){
+ DBG (5, "update_params: no dpi\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /*********** weird scan area (increase to valid one) *********/
+ if(s->fixed_width){
+ s->s.tl_x = 0;
+ s->s.br_x = s->max_x;
+ s->s.page_x = s->max_x;
+ }
+
+ /*recalculate new params*/
+ s->s.width = (s->s.br_x - s->s.tl_x) * s->s.dpi_x / 1200;
+
+ /* round down to byte boundary */
+ if(s->s.mode < MODE_GRAYSCALE){
+ s->s.width -= s->s.width % 8;
+ }
+
+ /* round down to pixel boundary for some scanners */
+ s->s.width -= s->s.width % s->ppl_mod;
+
+ s->s.valid_width = s->s.width;
+ s->s.valid_Bpl = s->s.valid_width * s->s.bpp / 8;
+
+ /* some machines (DR-2050) require even bytes per scanline */
+ /* increase width and Bpl, but not valid_width and valid_Bpl */
+ if(s->even_Bpl && (s->s.width % 2)){
+ s->s.width++;
+ }
+
+ s->s.Bpl = s->s.width * s->s.bpp / 8;
+
+ /* figure out how many valid bytes per line (2510 is padded) */
+ if(s->color_interlace[SIDE_FRONT] == COLOR_INTERLACE_2510){
+ s->s.valid_Bpl = s->s.Bpl*11/12;
+ s->s.valid_width = s->s.width*11/12;
+ }
+
+ /* some scanners need longer scans because front/back is offset */
+ if(s->u.source == SOURCE_ADF_DUPLEX && s->duplex_offset && !calib)
+ s->s.height = (s->u.br_y-s->u.tl_y+s->duplex_offset) * s->u.dpi_y / 1200;
+
+ /* round lines up to even number */
+ s->s.height += s->s.height % 2;
+
+ DBG (15, "update_params: scan params: w:%d h:%d m:%d f:%d b:%d\n",
+ s->s.width, s->s.height, s->s.mode, s->s.format, s->s.bpp);
+ DBG (15, "update_params: scan params: B:%d vB:%d vw:%d\n",
+ s->s.Bpl, s->s.valid_Bpl, s->s.valid_width);
+ DBG (15, "update_params: scan params: x b:%d t:%d d:%d y b:%d t:%d d:%d\n",
+ s->s.br_x, s->s.tl_x, s->s.dpi_x, s->s.br_y, s->s.tl_y, s->s.dpi_y);
+
+ /* make a third (intermediate) version of the params struct,
+ * currently identical to the user's params. this is what
+ * we actually will send back to the user (though buffer_xxx
+ * functions might change these values after this runs) */
+
+ /* calibration code needs the data just as it comes from the scanner */
+ if(calib)
+ memcpy(&s->i,&s->s,sizeof(struct img_params));
+ /* normal scans need the data cleaned for presentation to the user */
+ else{
+ memcpy(&s->i,&s->u,sizeof(struct img_params));
+ /*dumb scanners pad the top of front page in duplex*/
+ if(s->i.source == SOURCE_ADF_DUPLEX)
+ s->i.skip_lines[s->duplex_offset_side] = s->duplex_offset * s->i.dpi_y / 1200;
+ }
+
+ DBG (15, "update_params: i params: w:%d h:%d m:%d f:%d b:%d\n",
+ s->i.width, s->i.height, s->i.mode, s->i.format, s->i.bpp);
+ DBG (15, "update_params: i params: B:%d vB:%d vw:%d\n",
+ s->i.Bpl, s->i.valid_Bpl, s->i.valid_width);
+ DBG (15, "update_params: i params: x b:%d t:%d d:%d y b:%d t:%d d:%d\n",
+ s->i.br_x, s->i.tl_x, s->i.dpi_x, s->i.br_y, s->i.tl_y, s->i.dpi_y);
+
+ DBG (10, "update_params: finish\n");
+ return ret;
+}
+
+/* reset image size parameters after buffer_xxx functions changed them */
+SANE_Status
+update_i_params(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "update_i_params: start\n");
+
+ s->i.width = s->u.width;
+ s->i.Bpl = s->u.Bpl;
+
+ DBG (10, "update_i_params: finish\n");
+ return ret;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ * commands: set window, object pos, and scan
+ *
+ * this will be called between sides of a duplex scan,
+ * and at the start of each page of an adf batch.
+ * hence, we spend alot of time playing with s->started, etc.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = handle;
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "sane_start: start\n");
+ DBG (15, "started=%d, side=%d, source=%d\n",
+ s->started, s->side, s->u.source);
+
+ /* undo any prior sane_cancel calls */
+ s->cancelled=0;
+
+ /* protect this block from sane_cancel */
+ s->reading=1;
+
+ /* not finished with current side, error */
+ if (s->started && !s->u.eof[s->side]) {
+ DBG(5,"sane_start: previous transfer not finished?");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* batch start? inititalize struct and scanner */
+ if(!s->started){
+
+ /* load side marker */
+ if(s->u.source == SOURCE_ADF_BACK){
+ s->side = SIDE_BACK;
+ }
+ else{
+ s->side = SIDE_FRONT;
+ }
+
+ /* eject paper leftover*/
+ if(object_position (s, SANE_FALSE)){
+ DBG (5, "sane_start: ERROR: cannot eject page\n");
+ }
+
+ /* wait for scanner to finish eject */
+ ret = wait_scanner (s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot wait scanner\n");
+ goto errors;
+ }
+
+ /* load the brightness/contrast lut with linear slope for calibration */
+ ret = load_lut (s->lut, 8, 8, 0, 255, 0, 0);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load lut\n");
+ goto errors;
+ }
+
+ /* AFE cal */
+ if((ret = calibrate_AFE(s))){
+ DBG (5, "sane_start: ERROR: cannot cal afe\n");
+ goto errors;
+ }
+
+ /* fine cal */
+ if((ret = calibrate_fine(s))){
+ DBG (5, "sane_start: ERROR: cannot cal fine\n");
+ goto errors;
+ }
+
+ if((ret = calibrate_fine_buffer(s))){
+ DBG (5, "sane_start: ERROR: cannot cal fine from buffer\n");
+ goto errors;
+ }
+
+ /* reset the page counter after calibration */
+ s->panel_counter = 0;
+ s->prev_page = 0;
+ if(send_panel(s)){
+ DBG (5, "sane_start: ERROR: cannot send panel\n");
+ }
+
+ /* load our own private copy of scan params */
+ ret = update_params(s,0);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot update_params\n");
+ goto errors;
+ }
+
+ /* set window command */
+ ret = set_window(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot set window\n");
+ goto errors;
+ }
+
+ /* buffer/duplex/ald command */
+ ret = ssm_buffer(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot ssm buffer\n");
+ goto errors;
+ }
+
+ /* dropout color command */
+ ret = ssm_do(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot ssm do\n");
+ goto errors;
+ }
+
+ /* double feed detection command */
+ ret = ssm_df(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot ssm df\n");
+ goto errors;
+ }
+
+ /* clean scan params for new scan */
+ ret = clean_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot clean_params\n");
+ goto errors;
+ }
+
+ /* make large buffers to hold the images */
+ ret = image_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load buffers\n");
+ goto errors;
+ }
+
+ /* load the brightness/contrast lut with user choices */
+ ret = load_lut (s->lut, 8, 8, 0, 255, s->contrast, s->brightness);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load lut\n");
+ goto errors;
+ }
+
+ /* grab next page */
+ ret = object_position (s, SANE_TRUE);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load page\n");
+ goto errors;
+ }
+
+ /* wait for scanner to finish load */
+ ret = wait_scanner (s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot wait scanner\n");
+ goto errors;
+ }
+
+ /* start scanning */
+ ret = start_scan (s,0);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot start_scan\n");
+ goto errors;
+ }
+
+ s->started = 1;
+ }
+
+ /* stuff done for subsequent images */
+ else{
+
+ /* duplex needs to switch sides */
+ if(s->s.source == SOURCE_ADF_DUPLEX){
+ s->side = !s->side;
+ }
+
+ /* reset the intermediate params */
+ ret = update_i_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot update_i_params\n");
+ goto errors;
+ }
+
+ /* set clean defaults with new sheet of paper */
+ /* dont reset the transfer vars on backside of duplex page */
+ /* otherwise buffered back page will be lost */
+ /* ingest paper with adf (no-op for fb) */
+ /* dont call object pos or scan on back side of duplex scan */
+ if(s->side == SIDE_FRONT || s->s.source == SOURCE_ADF_BACK){
+
+ /* clean scan params for new scan */
+ ret = clean_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot clean_params\n");
+ goto errors;
+ }
+
+ /* big scanners and small ones in non-buff mode: OP to detect paper */
+ if(s->always_op || !s->buffermode){
+ ret = object_position (s, SANE_TRUE);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load page\n");
+ goto errors;
+ }
+
+ /* user wants unbuffered scans */
+ /* send scan command */
+ if(!s->buffermode){
+ ret = start_scan (s,0);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot start_scan\n");
+ goto errors;
+ }
+ }
+ }
+
+ /* small, buffering scanners check for more pages by reading counter */
+ else{
+ ret = read_panel (s, OPT_COUNTER);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load page\n");
+ goto errors;
+ }
+ if(s->prev_page == s->panel_counter){
+ DBG (5, "sane_start: same counter (%d) no paper?\n",s->prev_page);
+ ret = SANE_STATUS_NO_DOCS;
+ goto errors;
+ }
+ DBG (5, "sane_start: diff counter (%d/%d)\n",
+ s->prev_page,s->panel_counter);
+ }
+ }
+ }
+
+ /* reset jpeg params on each page */
+ s->jpeg_stage=JPEG_STAGE_NONE;
+ s->jpeg_ff_offset=0;
+
+ DBG (15, "started=%d, side=%d, source=%d\n",
+ s->started, s->side, s->u.source);
+
+ /* certain options require the entire image to
+ * be collected from the scanner before we can
+ * tell the user the size of the image. the sane
+ * API has no way to inform the frontend of this,
+ * so we block and buffer. yuck */
+ if( (s->swdeskew || s->swdespeck || s->swcrop)
+#ifdef SANE_FRAME_JPEG
+ && s->s.format != SANE_FRAME_JPEG
+#endif
+ ){
+
+ /* get image */
+ while(!s->s.eof[s->side] && !ret){
+ SANE_Int len = 0;
+ ret = sane_read((SANE_Handle)s, NULL, 0, &len);
+ }
+
+ /* check for errors */
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot buffer image\n");
+ goto errors;
+ }
+
+ DBG (5, "sane_start: OK: done buffering\n");
+
+ /* finished buffering, adjust image as required */
+ if(s->swdeskew){
+ buffer_deskew(s,s->side);
+ }
+ if(s->swcrop){
+ buffer_crop(s,s->side);
+ }
+ if(s->swdespeck){
+ buffer_despeck(s,s->side);
+ }
+
+ }
+
+ ret = check_for_cancel(s);
+ s->reading = 0;
+
+ DBG (10, "sane_start: finish %d\n", ret);
+ return ret;
+
+ errors:
+ DBG (10, "sane_start: error %d\n", ret);
+ s->started = 0;
+ s->cancelled = 0;
+ s->reading = 0;
+ return ret;
+}
+
+/*
+ * cleans params for new scan
+ */
+static SANE_Status
+clean_params (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "clean_params: start\n");
+
+ s->u.eof[0]=0;
+ s->u.eof[1]=0;
+ s->u.bytes_sent[0]=0;
+ s->u.bytes_sent[1]=0;
+ s->u.bytes_tot[0]=0;
+ s->u.bytes_tot[1]=0;
+
+ s->i.eof[0]=0;
+ s->i.eof[1]=0;
+ s->i.bytes_sent[0]=0;
+ s->i.bytes_sent[1]=0;
+ s->i.bytes_tot[0]=0;
+ s->i.bytes_tot[1]=0;
+
+ s->s.eof[0]=0;
+ s->s.eof[1]=0;
+ s->s.bytes_sent[0]=0;
+ s->s.bytes_sent[1]=0;
+ s->s.bytes_tot[0]=0;
+ s->s.bytes_tot[1]=0;
+
+ /* store the number of front bytes */
+ if ( s->u.source != SOURCE_ADF_BACK )
+ s->u.bytes_tot[SIDE_FRONT] = s->u.Bpl * s->u.height;
+
+ if ( s->i.source != SOURCE_ADF_BACK )
+ s->i.bytes_tot[SIDE_FRONT] = s->i.Bpl * s->i.height;
+
+ if ( s->s.source != SOURCE_ADF_BACK )
+ s->s.bytes_tot[SIDE_FRONT] = s->s.Bpl * s->s.height;
+
+ /* store the number of back bytes */
+ if ( s->u.source == SOURCE_ADF_DUPLEX || s->u.source == SOURCE_ADF_BACK )
+ s->u.bytes_tot[SIDE_BACK] = s->u.Bpl * s->u.height;
+
+ if ( s->i.source == SOURCE_ADF_DUPLEX || s->i.source == SOURCE_ADF_BACK )
+ s->i.bytes_tot[SIDE_BACK] = s->i.Bpl * s->i.height;
+
+ if ( s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_ADF_BACK )
+ s->s.bytes_tot[SIDE_BACK] = s->s.Bpl * s->s.height;
+
+ DBG (10, "clean_params: finish\n");
+
+ return ret;
+}
+
+/*
+ * frees/callocs buffers to hold the scan data
+ */
+static SANE_Status
+image_buffers (struct scanner *s, int setup)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side;
+
+ DBG (10, "image_buffers: start\n");
+
+ for(side=0;side<2;side++){
+
+ /* free current buffer */
+ if (s->buffers[side]) {
+ DBG (15, "image_buffers: free buffer %d.\n",side);
+ free(s->buffers[side]);
+ s->buffers[side] = NULL;
+ }
+
+ /* build new buffer if asked */
+ if(s->i.bytes_tot[side] && setup){
+ s->buffers[side] = calloc (1,s->i.bytes_tot[side]);
+ if (!s->buffers[side]) {
+ DBG (5, "image_buffers: Error, no buffer %d.\n",side);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ DBG (10, "image_buffers: finish\n");
+
+ return ret;
+}
+
+/*
+ * This routine issues a SCSI SET WINDOW command to the scanner, using the
+ * values currently in the s->s param structure.
+ */
+static SANE_Status
+set_window (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ /* The command specifies the number of bytes in the data phase
+ * the data phase has a header, followed by 1 window desc block
+ * the header specifies the number of bytes in 1 window desc block
+ */
+
+ unsigned char cmd[SET_WINDOW_len];
+ size_t cmdLen = SET_WINDOW_len;
+
+ unsigned char out[SW_header_len + SW_desc_len];
+ size_t outLen = SW_header_len + SW_desc_len;
+
+ unsigned char * header = out; /*header*/
+ unsigned char * desc1 = out + SW_header_len; /*descriptor*/
+
+ DBG (10, "set_window: start\n");
+
+ /*build the payload*/
+ memset(out,0,outLen);
+
+ /* set window desc size in header */
+ set_WPDB_wdblen(header, SW_desc_len);
+
+ /* init the window block */
+ if (s->s.source == SOURCE_ADF_BACK) {
+ set_WD_wid (desc1, WD_wid_back);
+ }
+ else{
+ set_WD_wid (desc1, WD_wid_front);
+ }
+
+ set_WD_Xres (desc1, s->s.dpi_x);
+ set_WD_Yres (desc1, s->s.dpi_y);
+
+ /* some machines need max width */
+ if(s->fixed_width){
+ set_WD_ULX (desc1, 0);
+ set_WD_width (desc1, s->max_x);
+ }
+
+ /* or they align left */
+ else if(s->u.source == SOURCE_FLATBED){
+ set_WD_ULX (desc1, s->s.tl_x);
+ set_WD_width (desc1, s->s.width * 1200/s->s.dpi_x);
+ }
+
+ /* or we have to center the window ourselves */
+ else{
+ set_WD_ULX (desc1, (s->max_x - s->s.page_x) / 2 + s->s.tl_x);
+ set_WD_width (desc1, s->s.width * 1200/s->s.dpi_x);
+ }
+
+ /* some models require that the tly value be inverted? */
+ if(s->invert_tly)
+ set_WD_ULY (desc1, ~s->s.tl_y);
+ else
+ set_WD_ULY (desc1, s->s.tl_y);
+
+ set_WD_length (desc1, s->s.height * 1200/s->s.dpi_y);
+
+ if(s->has_btc){
+ /*convert our common -127 to +127 range into HW's range
+ *FIXME: this code assumes hardware range of 0-255 */
+ set_WD_brightness (desc1, s->brightness+128);
+
+ set_WD_threshold (desc1, s->threshold);
+
+ /*convert our common -127 to +127 range into HW's range
+ *FIXME: this code assumes hardware range of 0-255 */
+ set_WD_contrast (desc1, s->contrast+128);
+ }
+
+ set_WD_composition (desc1, s->s.mode);
+
+ if(s->s.bpp == 24)
+ set_WD_bitsperpixel (desc1, 8);
+ else
+ set_WD_bitsperpixel (desc1, s->s.bpp);
+
+ if(s->s.mode == MODE_HALFTONE){
+ /*set_WD_ht_type(desc1, s->ht_type);
+ set_WD_ht_pattern(desc1, s->ht_pattern);*/
+ }
+
+ set_WD_rif (desc1, s->rif);
+ set_WD_rgb(desc1, s->rgb_format);
+ set_WD_padding(desc1, s->padding);
+
+ /*FIXME: what is this? */
+ set_WD_reserved2(desc1, s->unknown_byte2);
+
+ set_WD_compress_type(desc1, COMP_NONE);
+ set_WD_compress_arg(desc1, 0);
+
+#ifdef SANE_FRAME_JPEG
+ /* some scanners support jpeg image compression, for color/gs only */
+ if(s->s.format == SANE_FRAME_JPEG){
+ set_WD_compress_type(desc1, COMP_JPEG);
+ set_WD_compress_arg(desc1, s->compress_arg);
+ }
+#endif
+
+ /*build the command*/
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SET_WINDOW_code);
+ set_SW_xferlen(cmd, outLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ if (!ret && s->s.source == SOURCE_ADF_DUPLEX) {
+ set_WD_wid (desc1, WD_wid_back);
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+ }
+
+ DBG (10, "set_window: finish\n");
+
+ return ret;
+}
+
+/*
+ * Issues the SCSI OBJECT POSITION command if an ADF is in use.
+ */
+static SANE_Status
+object_position (struct scanner *s, int i_load)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[OBJECT_POSITION_len];
+ size_t cmdLen = OBJECT_POSITION_len;
+
+ DBG (10, "object_position: start\n");
+
+ if (s->u.source == SOURCE_FLATBED) {
+ DBG (10, "object_position: flatbed no-op\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, OBJECT_POSITION_code);
+
+ if (i_load) {
+ DBG (15, "object_position: load\n");
+ set_OP_autofeed (cmd, OP_Feed);
+ }
+ else {
+ DBG (15, "object_position: eject\n");
+ set_OP_autofeed (cmd, OP_Discharge);
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (10, "object_position: finish\n");
+
+ return ret;
+}
+
+/*
+ * Issues SCAN command.
+ *
+ * (This doesn't actually read anything, it just tells the scanner
+ * to start scanning.)
+ */
+static SANE_Status
+start_scan (struct scanner *s, int type)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SCAN_len];
+ size_t cmdLen = SCAN_len;
+
+ unsigned char out[] = {WD_wid_front, WD_wid_back};
+ size_t outLen = 2;
+
+ DBG (10, "start_scan: start\n");
+
+ /* calibration scans use 0xff or 0xfe */
+ if(type){
+ out[0] = type;
+ out[1] = type;
+ }
+
+ if (s->s.source != SOURCE_ADF_DUPLEX) {
+ outLen--;
+ if(s->s.source == SOURCE_ADF_BACK) {
+ out[0] = WD_wid_back;
+ }
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SCAN_code);
+ set_SC_xfer_length (cmd, outLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "start_scan: finish\n");
+
+ return ret;
+}
+
+/*
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "sane_read: start\n");
+
+ *len=0;
+
+ /* maybe cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: not started, call sane_start\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* sane_start required between sides */
+ if(s->u.bytes_sent[s->side] == s->i.bytes_tot[s->side]){
+ s->u.eof[s->side] = 1;
+ DBG (15, "sane_read: returning eof\n");
+ return SANE_STATUS_EOF;
+ }
+
+ s->reading = 1;
+
+ /* double width pnm interlacing */
+ if(s->s.source == SOURCE_ADF_DUPLEX
+ && s->s.format <= SANE_FRAME_RGB
+ && s->duplex_interlace != DUPLEX_INTERLACE_NONE
+ ){
+
+ /* buffer both sides */
+ if(!s->s.eof[SIDE_FRONT] || !s->s.eof[SIDE_BACK]){
+ ret = read_from_scanner_duplex(s, 0);
+ if(ret){
+ DBG(5,"sane_read: front returning %d\n",ret);
+ goto errors;
+ }
+ /*read last block, update counter*/
+ if(s->s.eof[SIDE_FRONT] && s->s.eof[SIDE_BACK]){
+ s->prev_page++;
+ DBG(15,"sane_read: duplex counter %d\n",s->prev_page);
+ }
+ }
+ }
+
+ /* simplex or non-alternating duplex */
+ else{
+ if(!s->s.eof[s->side]){
+ ret = read_from_scanner(s, s->side, 0);
+ if(ret){
+ DBG(5,"sane_read: side %d returning %d\n",s->side,ret);
+ goto errors;
+ }
+ /*read last block, update counter*/
+ if(s->s.eof[s->side]){
+ s->prev_page++;
+ DBG(15,"sane_read: side %d counter %d\n",s->side,s->prev_page);
+ }
+ }
+ }
+
+ /* copy a block from buffer to frontend */
+ ret = read_from_buffer(s,buf,max_len,len,s->side);
+ if(ret)
+ goto errors;
+
+ ret = check_for_cancel(s);
+ s->reading = 0;
+
+ DBG (10, "sane_read: finish %d\n", ret);
+ return ret;
+
+ errors:
+ DBG (10, "sane_read: error %d\n", ret);
+ s->reading = 0;
+ s->cancelled = 0;
+ s->started = 0;
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner(struct scanner *s, int side, int exact)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char * in;
+ size_t inLen = 0;
+
+ size_t bytes = s->buffer_size;
+ size_t remain = s->s.bytes_tot[side] - s->s.bytes_sent[side];
+
+ DBG (10, "read_from_scanner: start\n");
+
+ /* all requests must end on line boundary */
+ bytes -= (bytes % s->s.Bpl);
+
+ /* some larger scanners require even bytes per block */
+ if(bytes % 2){
+ bytes -= s->s.Bpl;
+ }
+
+ /* usually (image) we want to read too much data, and get RS */
+ /* sometimes (calib) we want to do an exact read */
+ if(exact && bytes > remain){
+ bytes = remain;
+ }
+
+ DBG(15, "read_from_scanner: si:%d to:%d rx:%d re:%lu bu:%d pa:%lu ex:%d\n",
+ side, s->s.bytes_tot[side], s->s.bytes_sent[side],
+ (unsigned long)remain, s->buffer_size, (unsigned long)bytes, exact);
+
+ inLen = bytes;
+ in = malloc(inLen);
+ if(!in){
+ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",(int)inLen);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, SR_datatype_image);
+
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+ DBG(15, "read_from_scanner: got GOOD, returning GOOD %lu\n", (unsigned long)inLen);
+ }
+ else if (ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_scanner: got EOF, finishing %lu\n", (unsigned long)inLen);
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_scanner: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_scanner: error reading data block status = %d\n",ret);
+ inLen = 0;
+ }
+
+#ifdef SANE_FRAME_JPEG
+ /* this is jpeg data, we need to fix the missing image size */
+ if(s->s.format == SANE_FRAME_JPEG){
+
+ /* look for the SOF header near the beginning */
+ if(s->jpeg_stage == JPEG_STAGE_NONE || s->jpeg_ff_offset < 0x0d){
+
+ size_t i;
+
+ for(i=0;i<inLen;i++){
+
+ /* about to change stage */
+ if(s->jpeg_stage == JPEG_STAGE_NONE && in[i] == 0xff){
+ s->jpeg_ff_offset=0;
+ continue;
+ }
+
+ s->jpeg_ff_offset++;
+
+ /* last byte was an ff, this byte is SOF */
+ if(s->jpeg_ff_offset == 1 && in[i] == 0xc0){
+ s->jpeg_stage = JPEG_STAGE_SOF;
+ continue;
+ }
+
+ if(s->jpeg_stage == JPEG_STAGE_SOF){
+
+ /* lines in start of frame, overwrite it */
+ if(s->jpeg_ff_offset == 5){
+ in[i] = (s->s.height >> 8) & 0xff;
+ continue;
+ }
+ if(s->jpeg_ff_offset == 6){
+ in[i] = s->s.height & 0xff;
+ continue;
+ }
+
+ /* width in start of frame, overwrite it */
+ if(s->jpeg_ff_offset == 7){
+ in[i] = (s->s.width >> 8) & 0xff;
+ continue;
+ }
+ if(s->jpeg_ff_offset == 8){
+ in[i] = s->s.width & 0xff;
+ continue;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ /*scanner may have sent more data than we asked for, chop it*/
+ if(inLen > remain){
+ inLen = remain;
+ }
+
+ /* we've got some data, descramble and store it */
+ if(inLen){
+ copy_simplex(s,in,inLen,side);
+ }
+
+ free(in);
+
+ /* we've read all data, but not eof. clear and pretend */
+ if(exact && inLen == remain){
+ DBG (10, "read_from_scanner: exact read, clearing\n");
+ ret = object_position (s,SANE_FALSE);
+ if(ret){
+ return ret;
+ }
+ ret = SANE_STATUS_EOF;
+ }
+
+ if(ret == SANE_STATUS_EOF){
+
+ switch (s->s.format){
+
+#ifdef SANE_FRAME_JPEG
+ /* this is jpeg data, we need to change the total size */
+ case SANE_FRAME_JPEG:
+ s->s.bytes_tot[side] = s->s.bytes_sent[side];
+ s->i.bytes_tot[side] = s->i.bytes_sent[side];
+ s->u.bytes_tot[side] = s->i.bytes_sent[side];
+ break;
+#endif
+
+ /* this is non-jpeg data, fill remainder, change rx'd size */
+ default:
+
+ DBG (15, "read_from_scanner: eof: %d %d\n", s->i.bytes_tot[side], s->i.bytes_sent[side]);
+
+ /* clone the last line repeatedly until the end */
+ while(s->i.bytes_tot[side] > s->i.bytes_sent[side]){
+ memcpy(
+ s->buffers[side]+s->i.bytes_sent[side]-s->i.Bpl,
+ s->buffers[side]+s->i.bytes_sent[side],
+ s->i.Bpl
+ );
+ s->i.bytes_sent[side] += s->i.Bpl;
+ }
+
+ DBG (15, "read_from_scanner: eof2: %d %d\n", s->i.bytes_tot[side], s->i.bytes_sent[side]);
+
+ /* pretend we got all the data from scanner */
+ s->s.bytes_sent[side] = s->s.bytes_tot[side];
+ break;
+ }
+
+ s->i.eof[side] = 1;
+ s->s.eof[side] = 1;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG(15, "read_from_scanner: sto:%d srx:%d sef:%d uto:%d urx:%d uef:%d\n",
+ s->s.bytes_tot[side], s->s.bytes_sent[side], s->s.eof[side],
+ s->u.bytes_tot[side], s->u.bytes_sent[side], s->u.eof[side]);
+
+ DBG (10, "read_from_scanner: finish\n");
+
+ return ret;
+}
+
+/* cheaper scanners interlace duplex scans on a byte basis
+ * this code requests double width lines from scanner */
+static SANE_Status
+read_from_scanner_duplex(struct scanner *s,int exact)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char * in;
+ size_t inLen = 0;
+
+ size_t bytes = s->buffer_size;
+ size_t remain = s->s.bytes_tot[SIDE_FRONT] + s->s.bytes_tot[SIDE_BACK]
+ - s->s.bytes_sent[SIDE_FRONT] - s->s.bytes_sent[SIDE_BACK];
+
+ DBG (10, "read_from_scanner_duplex: start\n");
+
+ /* all requests must end on WIDE line boundary */
+ bytes -= (bytes % (s->s.Bpl*2));
+
+ /* usually (image) we want to read too much data, and get RS */
+ /* sometimes (calib) we want to do an exact read */
+ if(exact && bytes > remain){
+ bytes = remain;
+ }
+
+ DBG(15, "read_from_scanner_duplex: re:%lu bu:%d pa:%lu ex:%d\n",
+ (unsigned long)remain, s->buffer_size, (unsigned long)bytes, exact);
+
+ inLen = bytes;
+ in = malloc(inLen);
+ if(!in){
+ DBG(5, "read_from_scanner_duplex: not enough mem for buffer: %d\n",
+ (int)inLen);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, SR_datatype_image);
+
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+ DBG(15, "read_from_scanner_duplex: got GOOD, returning GOOD %lu\n", (unsigned long)inLen);
+ }
+ else if (ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_scanner_duplex: got EOF, finishing %lu\n", (unsigned long)inLen);
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_scanner_duplex: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_scanner_duplex: error reading data block status = %d\n",
+ ret);
+ inLen = 0;
+ }
+
+ /*scanner may have sent more data than we asked for, chop it*/
+ if(inLen > remain){
+ inLen = remain;
+ }
+
+ /* we've got some data, descramble and store it */
+ if(inLen){
+ copy_duplex(s,in,inLen);
+ }
+
+ free(in);
+
+ /* we've read all data, but not eof. clear and pretend */
+ if(exact && inLen == remain){
+ DBG (10, "read_from_scanner_duplex: exact read, clearing\n");
+ ret = object_position (s,SANE_FALSE);
+ if(ret){
+ return ret;
+ }
+ ret = SANE_STATUS_EOF;
+ }
+
+ if(ret == SANE_STATUS_EOF){
+
+ switch (s->s.format){
+
+#ifdef SANE_FRAME_JPEG
+ /* this is jpeg data, we need to change the total size */
+ case SANE_FRAME_JPEG:
+ s->s.bytes_tot[SIDE_FRONT] = s->s.bytes_sent[SIDE_FRONT];
+ s->s.bytes_tot[SIDE_BACK] = s->s.bytes_sent[SIDE_BACK];
+ s->i.bytes_tot[SIDE_FRONT] = s->i.bytes_sent[SIDE_FRONT];
+ s->i.bytes_tot[SIDE_BACK] = s->i.bytes_sent[SIDE_BACK];
+ s->u.bytes_tot[SIDE_FRONT] = s->i.bytes_sent[SIDE_FRONT];
+ s->u.bytes_tot[SIDE_BACK] = s->i.bytes_sent[SIDE_BACK];
+ break;
+#endif
+
+ /* this is non-jpeg data, fill remainder, change rx'd size */
+ default:
+
+ DBG (15, "read_from_scanner_duplex: eof: %d %d %d %d\n",
+ s->i.bytes_tot[SIDE_FRONT], s->i.bytes_sent[SIDE_FRONT],
+ s->i.bytes_tot[SIDE_BACK], s->i.bytes_sent[SIDE_BACK]
+ );
+
+ /* clone the last line repeatedly until the end */
+ while(s->i.bytes_tot[SIDE_FRONT] > s->i.bytes_sent[SIDE_FRONT]){
+ memcpy(
+ s->buffers[SIDE_FRONT]+s->i.bytes_sent[SIDE_FRONT]-s->i.Bpl,
+ s->buffers[SIDE_FRONT]+s->i.bytes_sent[SIDE_FRONT],
+ s->i.Bpl
+ );
+ s->i.bytes_sent[SIDE_FRONT] += s->i.Bpl;
+ }
+
+ /* clone the last line repeatedly until the end */
+ while(s->i.bytes_tot[SIDE_BACK] > s->i.bytes_sent[SIDE_BACK]){
+ memcpy(
+ s->buffers[SIDE_BACK]+s->i.bytes_sent[SIDE_BACK]-s->i.Bpl,
+ s->buffers[SIDE_BACK]+s->i.bytes_sent[SIDE_BACK],
+ s->i.Bpl
+ );
+ s->i.bytes_sent[SIDE_BACK] += s->i.Bpl;
+ }
+
+ DBG (15, "read_from_scanner_duplex: eof2: %d %d %d %d\n",
+ s->i.bytes_tot[SIDE_FRONT], s->i.bytes_sent[SIDE_FRONT],
+ s->i.bytes_tot[SIDE_BACK], s->i.bytes_sent[SIDE_BACK]
+ );
+
+ /* pretend we got all the data from scanner */
+ s->s.bytes_sent[SIDE_FRONT] = s->s.bytes_tot[SIDE_FRONT];
+ s->s.bytes_sent[SIDE_BACK] = s->s.bytes_tot[SIDE_BACK];
+ break;
+ }
+
+ s->i.eof[SIDE_FRONT] = 1;
+ s->i.eof[SIDE_BACK] = 1;
+ s->s.eof[SIDE_FRONT] = 1;
+ s->s.eof[SIDE_BACK] = 1;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "read_from_scanner_duplex: finish\n");
+
+ return ret;
+}
+
+/* these functions copy image data from input buffer to scanner struct
+ * descrambling it, and putting it in the right side buffer */
+/* NOTE: they assume buffer is scanline aligned */
+static SANE_Status
+copy_simplex(struct scanner *s, unsigned char * buf, int len, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int i, j;
+ int bwidth = s->s.Bpl;
+ int pwidth = s->s.width;
+ int t = bwidth/3;
+ int f = bwidth/4;
+ int tw = bwidth/12;
+
+ unsigned char * line = NULL;
+ int line_next = 0;
+
+ /* jpeg data should not pass thru this function, so copy and bail out */
+ if(s->s.format > SANE_FRAME_RGB){
+ DBG (15, "copy_simplex: jpeg bulk copy\n");
+ memcpy(s->buffers[side]+s->i.bytes_sent[side], buf, len);
+ s->i.bytes_sent[side] += len;
+ s->s.bytes_sent[side] += len;
+ return ret;
+ }
+
+ DBG (15, "copy_simplex: per-line copy\n");
+
+ line = malloc(bwidth);
+ if(!line) return SANE_STATUS_NO_MEM;
+
+ /* ingest each line */
+ for(i=0; i<len; i+=bwidth){
+
+ int lineNum = s->s.bytes_sent[side] / bwidth;
+
+ /*increment number of bytes rx'd from scanner*/
+ s->s.bytes_sent[side] += bwidth;
+
+ /*have some padding from scanner to drop*/
+ if ( lineNum < s->i.skip_lines[side]
+ || lineNum - s->i.skip_lines[side] >= s->i.height
+ ){
+ continue;
+ }
+
+ line_next = 0;
+
+ if(s->s.format == SANE_FRAME_GRAY){
+
+ switch (s->gray_interlace[side]) {
+
+ /* one line has the following format: ggg...GGG
+ * where the 'capital' letters are the beginning of the line */
+ case GRAY_INTERLACE_gG:
+ DBG (17, "copy_simplex: gray, gG\n");
+ for (j=bwidth-1; j>=0; j--){
+ line[line_next++] = buf[i+j];
+ }
+ break;
+
+ case GRAY_INTERLACE_2510:
+ DBG (17, "copy_simplex: gray, 2510\n");
+
+ /* first read head (third byte of every three) */
+ for(j=bwidth-1;j>=0;j-=3){
+ line[line_next++] = buf[i+j];
+ }
+ /* second read head (first byte of every three) */
+ for(j=bwidth*3/4-3;j>=0;j-=3){
+ line[line_next++] = buf[i+j];
+ }
+ /* third read head (second byte of every three) */
+ for(j=bwidth-2;j>=0;j-=3){
+ line[line_next++] = buf[i+j];
+ }
+ /* padding */
+ for(j=0;j<tw;j++){
+ line[line_next++] = 0;
+ }
+ break;
+ }
+ }
+
+ else if (s->s.format == SANE_FRAME_RGB){
+
+ switch (s->color_interlace[side]) {
+
+ /* scanner returns color data as bgrbgr... */
+ case COLOR_INTERLACE_BGR:
+ DBG (17, "copy_simplex: color, BGR\n");
+ for (j=0; j<pwidth; j++){
+ line[line_next++] = buf[i+j*3+2];
+ line[line_next++] = buf[i+j*3+1];
+ line[line_next++] = buf[i+j*3];
+ }
+ break;
+
+ /* one line has the following format: RRR...rrrGGG...gggBBB...bbb */
+ case COLOR_INTERLACE_RRGGBB:
+ DBG (17, "copy_simplex: color, RRGGBB\n");
+ for (j=0; j<pwidth; j++){
+ line[line_next++] = buf[i+j];
+ line[line_next++] = buf[i+pwidth+j];
+ line[line_next++] = buf[i+2*pwidth+j];
+ }
+ break;
+
+ /* one line has the following format: rrr...RRRggg...GGGbbb...BBB
+ * where the 'capital' letters are the beginning of the line */
+ case COLOR_INTERLACE_rRgGbB:
+ DBG (17, "copy_simplex: color, rRgGbB\n");
+ for (j=pwidth-1; j>=0; j--){
+ line[line_next++] = buf[i+j];
+ line[line_next++] = buf[i+pwidth+j];
+ line[line_next++] = buf[i+2*pwidth+j];
+ }
+ break;
+
+ case COLOR_INTERLACE_2510:
+ DBG (17, "copy_simplex: color, 2510\n");
+
+ /* first read head (third byte of every three) */
+ for(j=t-1;j>=0;j-=3){
+ line[line_next++] = buf[i+j];
+ line[line_next++] = buf[i+t+j];
+ line[line_next++] = buf[i+2*t+j];
+ }
+ /* second read head (first byte of every three) */
+ for(j=f-3;j>=0;j-=3){
+ line[line_next++] = buf[i+j];
+ line[line_next++] = buf[i+t+j];
+ line[line_next++] = buf[i+2*t+j];
+ }
+ /* third read head (second byte of every three) */
+ for(j=t-2;j>=0;j-=3){
+ line[line_next++] = buf[i+j];
+ line[line_next++] = buf[i+t+j];
+ line[line_next++] = buf[i+2*t+j];
+ }
+ /* padding */
+ for(j=0;j<tw;j++){
+ line[line_next++] = 0;
+ }
+ break;
+ }
+ }
+
+ /* nothing sent above? just copy one line of the block */
+ /* used by uninterlaced gray/color */
+ if(!line_next){
+ DBG (17, "copy_simplex: default\n");
+ memcpy(line+line_next,buf+i,bwidth);
+ line_next = bwidth;
+ }
+
+ /* invert image if scanner needs it for this mode */
+ if(s->reverse_by_mode[s->s.mode]){
+ for(j=0; j<line_next; j++){
+ line[j] ^= 0xff;
+ }
+ }
+
+ /* apply calibration if we have it */
+ if(s->f_offset[side]){
+ DBG (17, "copy_simplex: apply offset\n");
+ for(j=0; j<s->s.valid_Bpl; j++){
+ int curr = line[j] - s->f_offset[side][j];
+ if(curr < 0) curr = 0;
+ line[j] = curr;
+ }
+ }
+
+ if(s->f_gain[side]){
+ DBG (17, "copy_simplex: apply gain\n");
+ for(j=0; j<s->s.valid_Bpl; j++){
+ int curr = line[j] * 240/s->f_gain[side][j];
+ if(curr > 255) curr = 255;
+ line[j] = curr;
+ }
+ }
+
+ /* apply brightness and contrast if hardware cannot do it */
+ if(s->sw_lut && (s->s.mode == MODE_COLOR || s->s.mode == MODE_GRAYSCALE)){
+ DBG (17, "copy_simplex: apply brightness/contrast\n");
+ for(j=0; j<s->s.valid_Bpl; j++){
+ line[j] = s->lut[line[j]];
+ }
+ }
+
+ /*copy the line into the buffer*/
+ ret = copy_line(s,line,side);
+ if(ret){
+ break;
+ }
+ }
+
+ free(line);
+
+ DBG (10, "copy_simplex: finished\n");
+
+ return ret;
+}
+
+/* split the data between two buffers, hand them to copy_simplex()
+ * assumes that the buffer aligns to a double-wide line boundary */
+static SANE_Status
+copy_duplex(struct scanner *s, unsigned char * buf, int len)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int i,j;
+ int bwidth = s->s.Bpl;
+ int dbwidth = 2*bwidth;
+ unsigned char * front;
+ unsigned char * back;
+ int flen=0, blen=0;
+
+ DBG (10, "copy_duplex: start\n");
+
+ /*split the input into two simplex output buffers*/
+ front = calloc(1,len/2);
+ if(!front){
+ DBG (5, "copy_duplex: no front mem\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ back = calloc(1,len/2);
+ if(!back){
+ DBG (5, "copy_duplex: no back mem\n");
+ free(front);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if(s->duplex_interlace == DUPLEX_INTERLACE_2510){
+
+ DBG (10, "copy_duplex: 2510\n");
+
+ for(i=0; i<len; i+=dbwidth){
+
+ for(j=0;j<dbwidth;j+=6){
+
+ /* we are actually only partially descrambling,
+ * copy_simplex() does the rest */
+
+ /* front */
+ /* 2nd head: 2nd byte -> 1st byte */
+ /* 3rd head: 4th byte -> 2nd byte */
+ /* 1st head: 5th byte -> 3rd byte */
+ front[flen++] = buf[i+j+2];
+ front[flen++] = buf[i+j+4];
+ front[flen++] = buf[i+j+5];
+
+ /* back */
+ /* 2nd head: 3rd byte -> 1st byte */
+ /* 3rd head: 0th byte -> 2nd byte */
+ /* 1st head: 1st byte -> 3rd byte */
+ back[blen++] = buf[i+j+3];
+ back[blen++] = buf[i+j];
+ back[blen++] = buf[i+j+1];
+ }
+ }
+ }
+
+ /* no scanners use this? */
+ else if(s->duplex_interlace == DUPLEX_INTERLACE_FFBB){
+ for(i=0; i<len; i+=dbwidth){
+ memcpy(front+flen,buf+i,bwidth);
+ flen+=bwidth;
+ memcpy(back+blen,buf+i+bwidth,bwidth);
+ blen+=bwidth;
+ }
+ }
+
+ /*just alternating bytes, FBFBFB*/
+ else {
+ for(i=0; i<len; i+=2){
+ front[flen++] = buf[i];
+ back[blen++] = buf[i+1];
+ }
+ }
+
+ copy_simplex(s,front,flen,SIDE_FRONT);
+ copy_simplex(s,back,blen,SIDE_BACK);
+
+ free(front);
+ free(back);
+
+ DBG (10, "copy_duplex: finished\n");
+
+ return ret;
+}
+
+/* downsample a single line from scanner's size to user's size */
+/* and copy into final buffer */
+static SANE_Status
+copy_line(struct scanner *s, unsigned char * buff, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int spwidth = s->s.width;
+ int sbwidth = s->s.Bpl;
+ int ibwidth = s->i.Bpl;
+ unsigned char * line;
+ int offset = 0;
+ int i, j;
+
+ DBG (20, "copy_line: start\n");
+
+ /* the 'standard' case: non-stupid scan */
+ if(s->s.width == s->i.width
+ && s->s.dpi_x == s->i.dpi_x
+ && s->s.mode == s->i.mode
+ ){
+
+ memcpy(s->buffers[side]+s->i.bytes_sent[side], buff, sbwidth);
+ s->i.bytes_sent[side] += sbwidth;
+
+ DBG (20, "copy_line: finished smart\n");
+ return ret;
+ }
+
+ /* the 'corner' case: stupid scan */
+
+ /*setup 24 bit color single line buffer*/
+ line = malloc(spwidth*3);
+ if(!line) return SANE_STATUS_NO_MEM;
+
+ /*load single line color buffer*/
+ switch (s->s.mode) {
+
+ case MODE_COLOR:
+ memcpy(line, buff, sbwidth);
+ break;
+
+ case MODE_GRAYSCALE:
+ for(i=0;i<spwidth;i++){
+ line[i*3] = line[i*3+1] = line[i*3+2] = buff[i];
+ }
+ break;
+
+ default:
+ for(i=0;i<sbwidth;i++){
+ unsigned char curr = buff[i];
+
+ line[i*24+0] = line[i*24+1] = line[i*24+2] = ((curr >> 7) & 1) ?0:255;
+ line[i*24+3] = line[i*24+4] = line[i*24+5] = ((curr >> 6) & 1) ?0:255;
+ line[i*24+6] = line[i*24+7] = line[i*24+8] = ((curr >> 5) & 1) ?0:255;
+ line[i*24+9] = line[i*24+10] = line[i*24+11] = ((curr >> 4) & 1) ?0:255;
+ line[i*24+12] = line[i*24+13] = line[i*24+14] =((curr >> 3) & 1) ?0:255;
+ line[i*24+15] = line[i*24+16] = line[i*24+17] =((curr >> 2) & 1) ?0:255;
+ line[i*24+18] = line[i*24+19] = line[i*24+20] =((curr >> 1) & 1) ?0:255;
+ line[i*24+21] = line[i*24+22] = line[i*24+23] =((curr >> 0) & 1) ?0:255;
+ }
+ break;
+ }
+
+ /* scan is higher res than user wanted, scale it */
+ /*FIXME: interpolate instead */
+ if(s->i.dpi_x != s->s.dpi_x){
+ for(i=0;i<spwidth;i++){
+ int source = i * s->s.dpi_x/s->i.dpi_x * 3;
+
+ if(source+2 >= spwidth*3)
+ break;
+
+ line[i*3] = line[source];
+ line[i*3+1] = line[source+1];
+ line[i*3+2] = line[source+2];
+ }
+ }
+
+ /* scan is wider than user wanted, skip some pixels on left side */
+ if(s->i.width != s->s.width){
+ offset = ((s->valid_x-s->i.page_x) / 2 + s->i.tl_x) * s->i.dpi_x/1200*3;
+ }
+
+ /* change mode, store line in buffer */
+ switch (s->i.mode) {
+
+ case MODE_COLOR:
+ memcpy(s->buffers[side]+s->i.bytes_sent[side], line+offset, ibwidth);
+ s->i.bytes_sent[side] += ibwidth;
+ break;
+
+ case MODE_GRAYSCALE:
+ for(i=0;i<ibwidth;i++){
+ int source = (offset+i)*3;
+ s->buffers[side][s->i.bytes_sent[side]++]
+ = ((int)line[source] + line[source+1] + line[source+2])/3;
+ }
+ break;
+
+ default:
+ /*loop over output bytes*/
+ for(i=0;i<ibwidth;i++){
+
+ unsigned char curr = 0;
+ int thresh = s->threshold*3;
+
+ /*loop over output bits*/
+ for(j=0;j<8;j++){
+ int source = (offset+i)*24 + j*3;
+ if( (line[source] + line[source+1] + line[source+2]) < thresh ){
+ curr |= 1 << (7-j);
+ }
+ }
+
+ s->buffers[side][s->i.bytes_sent[side]++] = curr;
+ }
+ break;
+ }
+
+ free(line);
+
+ DBG (20, "copy_line: finish stupid\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int bytes = max_len;
+ int remain = s->i.bytes_sent[side] - s->u.bytes_sent[side];
+
+ DBG (10, "read_from_buffer: start\n");
+
+ /* figure out the max amount to transfer */
+ if(bytes > remain)
+ bytes = remain;
+
+ *len = bytes;
+
+ /*FIXME this needs to timeout eventually */
+ if(!bytes){
+ DBG(5,"read_from_buffer: nothing to do\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(15, "read_from_buffer: si:%d to:%d tx:%d bu:%d pa:%d\n", side,
+ s->i.bytes_tot[side], s->u.bytes_sent[side], max_len, bytes);
+
+ /* copy to caller */
+ memcpy(buf,s->buffers[side]+s->u.bytes_sent[side],bytes);
+ s->u.bytes_sent[side] += bytes;
+
+ DBG (10, "read_from_buffer: finished\n");
+
+ return ret;
+}
+
+/*
+ * @@ Section 5 - calibration functions
+ */
+
+#if 0
+static SANE_Status
+foo_AFE(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[] = {
+ 0x3b, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00
+ };
+ size_t cmdLen = 12;
+
+ unsigned char in[4];
+ size_t inLen = 4;
+
+ DBG (10, "foo_AFE: start\n");
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (10, "foo_AFE: finish\n");
+
+ return ret;
+}
+#endif
+
+/*
+ * makes several scans, adjusts coarse calibration
+ */
+static SANE_Status
+calibrate_AFE (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j, k;
+ int min, max;
+ int lines = 8;
+
+ /*buffer these for later*/
+ int old_tl_y = s->u.tl_y;
+ int old_br_y = s->u.br_y;
+ int old_mode = s->u.mode;
+ int old_source = s->u.source;
+
+ DBG (10, "calibrate_AFE: start\n");
+
+ if(!s->need_ccal){
+ DBG (10, "calibrate_AFE: not required\n");
+ return ret;
+ }
+
+ /* always cal with a short scan in duplex color */
+ s->u.tl_y = 0;
+ s->u.br_y = lines * 1200 / s->u.dpi_y;
+ s->u.mode = MODE_COLOR;
+ s->u.source = SOURCE_ADF_DUPLEX;
+
+ /* load our own private copy of scan params */
+ ret = update_params(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot update_params\n");
+ goto cleanup;
+ }
+
+ if(s->c_res == s->s.dpi_x && s->c_mode == s->s.mode){
+ DBG (10, "calibrate_AFE: already done\n");
+ goto cleanup;
+ }
+
+ /* clean scan params for new scan */
+ ret = clean_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot clean_params\n");
+ goto cleanup;
+ }
+
+ /* make buffers to hold the images */
+ ret = image_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot load buffers\n");
+ goto cleanup;
+ }
+
+ /*blast the existing fine cal data so reading code wont apply it*/
+ ret = offset_buffers(s,0);
+ ret = gain_buffers(s,0);
+
+ /* need to tell it we want duplex */
+ ret = ssm_buffer(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot ssm buffer\n");
+ goto cleanup;
+ }
+
+ /* set window command */
+ ret = set_window(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot set window\n");
+ goto cleanup;
+ }
+
+ /* first pass (black offset), lamp off, no offset/gain/exposure */
+ DBG (15, "calibrate_AFE: offset\n");
+
+ /* blast all the existing coarse cal data */
+ for(i=0;i<2;i++){
+ s->c_gain[i] = 1;
+ s->c_offset[i] = 1;
+ for(j=0;j<3;j++){
+ s->c_exposure[i][j] = 0;
+ }
+ }
+
+ ret = write_AFE(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot write afe\n");
+ goto cleanup;
+ }
+
+ ret = calibration_scan(s,0xff);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot make offset cal scan\n");
+ goto cleanup;
+ }
+
+ for(i=0;i<2;i++){
+ min = 255;
+ for(j=0; j<s->s.valid_Bpl; j++){
+ if(s->buffers[i][j] < min)
+ min = s->buffers[i][j];
+ }
+ s->c_offset[i] = min*3-2;
+ DBG (15, "calibrate_AFE: offset %d %d %02x\n", i, min, s->c_offset[i]);
+ }
+
+ /*handle second pass (per channel exposure), lamp on, overexposed*/
+ DBG (15, "calibrate_AFE: exposure\n");
+ for(i=0;i<2;i++){
+ for(j=0; j<3; j++){
+ s->c_exposure[i][j] = 0x320;
+ }
+ }
+
+ ret = write_AFE(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot write afe\n");
+ goto cleanup;
+ }
+
+ ret = calibration_scan(s,0xfe);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot make exposure cal scan\n");
+ goto cleanup;
+ }
+
+ for(i=0;i<2;i++){ /*sides*/
+ for(j=0;j<3;j++){ /*channels*/
+ max = 0;
+ for(k=j; k<s->s.valid_Bpl; k+=3){ /*bytes*/
+ if(s->buffers[i][k] > max)
+ max = s->buffers[i][k];
+ }
+
+ /*generally we reduce the exposure (smaller number) */
+ if(old_mode == MODE_COLOR)
+ s->c_exposure[i][j] = s->c_exposure[i][j] * 102/max;
+ else
+ s->c_exposure[i][j] = s->c_exposure[i][j] * 64/max;
+
+ DBG (15, "calibrate_AFE: exp %d %d %d %02x\n", i, j, max,
+ s->c_exposure[i][j]);
+ }
+ }
+
+ /*handle third pass (gain), lamp on with current offset/exposure */
+ DBG (15, "calibrate_AFE: gain\n");
+
+ ret = write_AFE(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot write afe\n");
+ goto cleanup;
+ }
+
+ ret = calibration_scan(s,0xfe);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot make gain cal scan\n");
+ goto cleanup;
+ }
+
+ for(i=0;i<2;i++){
+ max = 0;
+ for(j=0; j<s->s.valid_Bpl; j++){
+ if(s->buffers[i][j] > max)
+ max = s->buffers[i][j];
+ }
+
+ if(old_mode == MODE_COLOR)
+ s->c_gain[i] = (250-max)*4/5;
+ else
+ s->c_gain[i] = (125-max)*4/5;
+
+ if(s->c_gain[i] < 1)
+ s->c_gain[i] = 1;
+
+ DBG (15, "calibrate_AFE: gain %d %d %02x\n", i, max, s->c_gain[i]);
+ }
+
+ /*handle fourth pass (offset again), lamp off*/
+#if 0
+ DBG (15, "calibrate_AFE: offset2\n");
+
+ ret = write_AFE(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot write afe\n");
+ goto cleanup;
+ }
+
+ ret = calibration_scan(s,0xff);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot make offset2 cal scan\n");
+ goto cleanup;
+ }
+
+ for(i=0;i<2;i++){
+ min = 255;
+ for(j=0; j<s->s.valid_Bpl; j++){
+ if(s->buffers[i][j] < min)
+ min = s->buffers[i][j];
+ }
+ /*s->c_offset[i] += min*3-2;*/
+ DBG (15, "calibrate_AFE: offset2 %d %d %02x\n", i, min, s->c_offset[i]);
+ }
+#endif
+
+ /*send final afe params to scanner*/
+ ret = write_AFE(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_AFE: ERROR: cannot write afe\n");
+ goto cleanup;
+ }
+
+ /* log current cal type */
+ s->c_res = s->s.dpi_x;
+ s->c_mode = s->s.mode;
+
+ cleanup:
+
+ /* recover user settings */
+ s->u.tl_y = old_tl_y;
+ s->u.br_y = old_br_y;
+ s->u.mode = old_mode;
+ s->u.source = old_source;
+
+ DBG (10, "calibrate_AFE: finish %d\n",ret);
+
+ return ret;
+}
+
+
+/* alternative version- extracts data from scanner memory */
+static SANE_Status
+calibrate_fine_buffer (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j, k;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char * in = NULL;
+ size_t inLen = 0, reqLen = 0;
+
+ /*buffer these for later*/
+ int old_tl_y = s->u.tl_y;
+ int old_br_y = s->u.br_y;
+ int old_source = s->u.source;
+
+ DBG (10, "calibrate_fine_buffer: start\n");
+
+ if(!s->need_fcal_buffer){
+ DBG (10, "calibrate_fine_buffer: not required\n");
+ return ret;
+ }
+
+ /* pretend we are doing a 1 line scan in duplex */
+ s->u.tl_y = 0;
+ s->u.br_y = 1200 / s->u.dpi_y;
+ s->u.source = SOURCE_ADF_DUPLEX;
+
+ /* load our own private copy of scan params */
+ ret = update_params(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine_buffer: ERROR: cannot update_params\n");
+ goto cleanup;
+ }
+
+ if(s->f_res == s->s.dpi_x && s->f_mode == s->s.mode){
+ DBG (10, "calibrate_fine_buffer: already done\n");
+ goto cleanup;
+ }
+
+ /* clean scan params for new scan */
+ ret = clean_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine_buffer: ERROR: cannot clean_params\n");
+ goto cleanup;
+ }
+
+ /*calibration buffers in scanner are single color channel, but duplex*/
+ reqLen = s->s.width*2;
+
+ in = malloc(reqLen);
+ if (!in) {
+ DBG (5, "calibrate_fine_buffer: ERROR: cannot malloc in\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /*fine offset*/
+ ret = offset_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine_buffer: ERROR: cannot load offset buffers\n");
+ goto cleanup;
+ }
+
+ DBG (5, "calibrate_fine_buffer: %d %x\n", s->s.dpi_x/10, s->s.dpi_x/10);
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, SR_datatype_fineoffset);
+ set_R_xfer_lid (cmd, s->s.dpi_x/10);
+ set_R_xfer_length (cmd, reqLen);
+
+ inLen = reqLen;
+
+ hexdump(15, "cmd:", cmd, cmdLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if (ret != SANE_STATUS_GOOD)
+ goto cleanup;
+
+ for(i=0;i<2;i++){
+
+ /*color mode, expand offset across all three channels? */
+ if(s->s.format == SANE_FRAME_RGB){
+ for(j=0; j<s->s.valid_width; j++){
+
+ /*red*/
+ s->f_offset[i][j*3] = in[j*2+i];
+ if(s->f_offset[i][j*3] < 1)
+ s->f_offset[i][j*3] = 1;
+
+ /*green and blue, same as red*/
+ s->f_offset[i][j*3+1] = s->f_offset[i][j*3+2] = s->f_offset[i][j*3];
+ }
+ }
+
+ /*gray mode, copy*/
+ else{
+ for(j=0; j<s->s.valid_width; j++){
+
+ s->f_offset[i][j] = in[j*2+i];
+ if(s->f_offset[i][j] < 1)
+ s->f_offset[i][j] = 1;
+ }
+ }
+
+ hexdump(15, "off:", s->f_offset[i], s->s.valid_Bpl);
+ }
+
+ /*fine gain*/
+ ret = gain_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine_buffer: ERROR: cannot load gain buffers\n");
+ goto cleanup;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, SR_datatype_finegain);
+ set_R_xfer_lid (cmd, s->s.dpi_x/10);
+ set_R_xfer_length (cmd, reqLen);
+
+ /*color gain split into three buffers, grab them and merge*/
+ if(s->s.format == SANE_FRAME_RGB){
+
+ int codes[] = {R_FINE_uid_red,R_FINE_uid_green,R_FINE_uid_blue};
+
+ for(k=0;k<3;k++){
+
+ set_R_xfer_uid (cmd, codes[k]);
+ inLen = reqLen;
+
+ hexdump(15, "cmd:", cmd, cmdLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if (ret != SANE_STATUS_GOOD)
+ goto cleanup;
+
+ for(i=0;i<2;i++){
+ for(j=0; j<s->s.valid_width; j++){
+
+ s->f_gain[i][j*3+k] = in[j*2+i]*3/4;
+
+ if(s->f_gain[i][j*3+k] < 1)
+ s->f_gain[i][j*3+k] = 1;
+ }
+ }
+ }
+ }
+
+ /*gray gain, copy*/
+ else{
+
+ set_R_xfer_uid (cmd, R_FINE_uid_gray);
+ inLen = reqLen;
+
+ hexdump(15, "cmd:", cmd, cmdLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if (ret != SANE_STATUS_GOOD)
+ goto cleanup;
+
+ for(i=0;i<2;i++){
+ for(j=0; j<s->s.valid_width; j++){
+
+ s->f_gain[i][j] = in[j*2+i]*3/4;
+
+ if(s->f_gain[i][j] < 1)
+ s->f_gain[i][j] = 1;
+ }
+ }
+ }
+
+ for(i=0;i<2;i++){
+ hexdump(15, "gain:", s->f_gain[i], s->s.valid_Bpl);
+ }
+
+ /* log current cal type */
+ s->f_res = s->s.dpi_x;
+ s->f_mode = s->s.mode;
+
+ cleanup:
+
+ if(in){
+ free(in);
+ }
+
+ /* recover user settings */
+ s->u.tl_y = old_tl_y;
+ s->u.br_y = old_br_y;
+ s->u.source = old_source;
+
+ DBG (10, "calibrate_fine_buffer: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * makes several scans, adjusts fine calibration
+ */
+static SANE_Status
+calibrate_fine (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j, k;
+ int min, max;
+ int lines = 8;
+
+ /*buffer these for later*/
+ int old_tl_y = s->u.tl_y;
+ int old_br_y = s->u.br_y;
+ int old_source = s->u.source;
+
+ DBG (10, "calibrate_fine: start\n");
+
+ if(!s->need_fcal){
+ DBG (10, "calibrate_fine: not required\n");
+ return ret;
+ }
+
+ /* always cal with a short scan in duplex */
+ s->u.tl_y = 0;
+ s->u.br_y = lines * 1200 / s->u.dpi_y;
+ s->u.source = SOURCE_ADF_DUPLEX;
+
+ /* load our own private copy of scan params */
+ ret = update_params(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot update_params\n");
+ goto cleanup;
+ }
+
+ if(s->f_res == s->s.dpi_x && s->f_mode == s->s.mode){
+ DBG (10, "calibrate_fine: already done\n");
+ goto cleanup;
+ }
+
+ /* clean scan params for new scan */
+ ret = clean_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibration_fine: ERROR: cannot clean_params\n");
+ goto cleanup;
+ }
+
+ /* make buffers to hold the images */
+ ret = image_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot load buffers\n");
+ goto cleanup;
+ }
+
+ /*blast the existing fine cal data so reading code wont apply it*/
+ ret = offset_buffers(s,0);
+ ret = gain_buffers(s,0);
+
+ /* need to tell it we want duplex */
+ ret = ssm_buffer(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot ssm buffer\n");
+ goto cleanup;
+ }
+
+ /* set window command */
+ ret = set_window(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot set window\n");
+ goto cleanup;
+ }
+
+ /*handle fifth pass (fine offset), lamp off*/
+ DBG (15, "calibrate_fine: offset\n");
+ ret = calibration_scan(s,0xff);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot make offset cal scan\n");
+ goto cleanup;
+ }
+
+ ret = offset_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot load offset buffers\n");
+ goto cleanup;
+ }
+
+ for(i=0;i<2;i++){
+ for(j=0; j<s->s.valid_Bpl; j++){
+ min = 0;
+ for(k=j;k<lines*s->s.Bpl;k+=s->s.Bpl){
+ min += s->buffers[i][k];
+ }
+ s->f_offset[i][j] = min/lines;
+ }
+ hexdump(15, "off:", s->f_offset[i], s->s.valid_Bpl);
+ }
+
+ /*handle sixth pass (fine gain), lamp on*/
+ DBG (15, "calibrate_fine: gain\n");
+ ret = calibration_scan(s,0xfe);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot make gain cal scan\n");
+ goto cleanup;
+ }
+
+ ret = gain_buffers(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibrate_fine: ERROR: cannot load gain buffers\n");
+ goto cleanup;
+ }
+
+ for(i=0;i<2;i++){
+ for(j=0; j<s->s.valid_Bpl; j++){
+ max = 0;
+ for(k=j;k<lines*s->s.Bpl;k+=s->s.Bpl){
+ max += s->buffers[i][k];
+ }
+ s->f_gain[i][j] = max/lines;
+
+ if(s->f_gain[i][j] < 1)
+ s->f_gain[i][j] = 1;
+ }
+ hexdump(15, "gain:", s->f_gain[i], s->s.valid_Bpl);
+ }
+
+ /* log current cal type */
+ s->f_res = s->s.dpi_x;
+ s->f_mode = s->s.mode;
+
+ cleanup:
+
+ /* recover user settings */
+ s->u.tl_y = old_tl_y;
+ s->u.br_y = old_br_y;
+ s->u.source = old_source;
+
+ DBG (10, "calibrate_fine: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * sends AFE params, and ingests entire duplex image into buffers
+ */
+static SANE_Status
+calibration_scan (struct scanner *s, int scan)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "calibration_scan: start\n");
+
+ /* clean scan params for new scan */
+ ret = clean_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibration_scan: ERROR: cannot clean_params\n");
+ return ret;
+ }
+
+ /* start scanning */
+ ret = start_scan (s,scan);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "calibration_scan: ERROR: cannot start_scan\n");
+ return ret;
+ }
+
+ while(!s->s.eof[SIDE_FRONT] && !s->s.eof[SIDE_BACK]){
+ ret = read_from_scanner_duplex(s,1);
+ }
+
+ DBG (10, "calibration_scan: finished\n");
+
+ return ret;
+}
+
+/*
+ * sends AFE and exposure params
+ */
+static SANE_Status
+write_AFE(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[COR_CAL_len];
+ size_t cmdLen = COR_CAL_len;
+
+ /*use the longest payload for buffer*/
+ unsigned char pay[CC3_pay_len];
+ size_t payLen = CC3_pay_len;
+
+ DBG (10, "write_AFE: start\n");
+
+ /* newer scanners use a longer cc payload */
+ if(s->ccal_version == 3){
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, COR_CAL_code);
+ set_CC_version(cmd,CC3_pay_ver);
+ set_CC_xferlen(cmd,payLen);
+
+ memset(pay,0,payLen);
+
+ set_CC3_gain_f_r(pay,s->c_gain[SIDE_FRONT]);
+ set_CC3_gain_f_g(pay,s->c_gain[SIDE_FRONT]);
+ set_CC3_gain_f_b(pay,s->c_gain[SIDE_FRONT]);
+
+ set_CC3_off_f_r(pay,s->c_offset[SIDE_FRONT]);
+ set_CC3_off_f_g(pay,s->c_offset[SIDE_FRONT]);
+ set_CC3_off_f_b(pay,s->c_offset[SIDE_FRONT]);
+
+ set_CC3_exp_f_r(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]);
+ set_CC3_exp_f_g(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]);
+ set_CC3_exp_f_b(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]);
+
+ set_CC3_gain_b_r(pay,s->c_gain[SIDE_BACK]);
+ set_CC3_gain_b_g(pay,s->c_gain[SIDE_BACK]);
+ set_CC3_gain_b_b(pay,s->c_gain[SIDE_BACK]);
+
+ set_CC3_off_b_r(pay,s->c_offset[SIDE_BACK]);
+ set_CC3_off_b_g(pay,s->c_offset[SIDE_BACK]);
+ set_CC3_off_b_b(pay,s->c_offset[SIDE_BACK]);
+
+ set_CC3_exp_b_r(pay,s->c_exposure[SIDE_BACK][CHAN_RED]);
+ set_CC3_exp_b_g(pay,s->c_exposure[SIDE_BACK][CHAN_GREEN]);
+ set_CC3_exp_b_b(pay,s->c_exposure[SIDE_BACK][CHAN_BLUE]);
+ }
+
+ else{
+ payLen = CC_pay_len;
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, COR_CAL_code);
+ set_CC_version(cmd,CC_pay_ver);
+ set_CC_xferlen(cmd,payLen);
+
+ memset(pay,0,payLen);
+ set_CC_f_gain(pay,s->c_gain[SIDE_FRONT]);
+ set_CC_unk1(pay,1);
+ set_CC_f_offset(pay,s->c_offset[SIDE_FRONT]);
+ set_CC_unk2(pay,1);
+ set_CC_exp_f_r1(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]);
+ set_CC_exp_f_g1(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]);
+ set_CC_exp_f_b1(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]);
+ set_CC_exp_f_r2(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]);
+ set_CC_exp_f_g2(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]);
+ set_CC_exp_f_b2(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]);
+
+ set_CC_b_gain(pay,s->c_gain[SIDE_BACK]);
+ set_CC_b_offset(pay,s->c_offset[SIDE_BACK]);
+ set_CC_exp_b_r1(pay,s->c_exposure[SIDE_BACK][CHAN_RED]);
+ set_CC_exp_b_g1(pay,s->c_exposure[SIDE_BACK][CHAN_GREEN]);
+ set_CC_exp_b_b1(pay,s->c_exposure[SIDE_BACK][CHAN_BLUE]);
+ set_CC_exp_b_r2(pay,s->c_exposure[SIDE_BACK][CHAN_RED]);
+ set_CC_exp_b_g2(pay,s->c_exposure[SIDE_BACK][CHAN_GREEN]);
+ set_CC_exp_b_b2(pay,s->c_exposure[SIDE_BACK][CHAN_BLUE]);
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (10, "write_AFE: finish\n");
+
+ return ret;
+}
+
+/*
+ * frees/callocs buffers to hold the fine cal offset data
+ */
+static SANE_Status
+offset_buffers (struct scanner *s, int setup)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side;
+
+ DBG (10, "offset_buffers: start\n");
+
+ for(side=0;side<2;side++){
+
+ if (s->f_offset[side]) {
+ DBG (15, "offset_buffers: free f_offset %d.\n",side);
+ free(s->f_offset[side]);
+ s->f_offset[side] = NULL;
+ }
+
+ if(setup){
+ s->f_offset[side] = calloc (1,s->s.Bpl);
+ if (!s->f_offset[side]) {
+ DBG (5, "offset_buffers: error, no f_offset %d.\n",side);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ DBG (10, "offset_buffers: finish\n");
+
+ return ret;
+}
+
+/*
+ * frees/callocs buffers to hold the fine cal gain data
+ */
+static SANE_Status
+gain_buffers (struct scanner *s, int setup)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side;
+
+ DBG (10, "gain_buffers: start\n");
+
+ for(side=0;side<2;side++){
+
+ if (s->f_gain[side]) {
+ DBG (15, "gain_buffers: free f_gain %d.\n",side);
+ free(s->f_gain[side]);
+ s->f_gain[side] = NULL;
+ }
+
+ if(setup){
+ s->f_gain[side] = calloc (1,s->s.Bpl);
+ if (!s->f_gain[side]) {
+ DBG (5, "gain_buffers: error, no f_gain %d.\n",side);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ DBG (10, "gain_buffers: finish\n");
+
+ return ret;
+}
+
+/*
+ * @@ Section 6 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * It has been said on the mailing list that sane_cancel is a bit of a
+ * misnomer because it is routinely called to signal the end of a
+ * batch - quoting David Mosberger-Tang:
+ *
+ * > In other words, the idea is to have sane_start() be called, and
+ * > collect as many images as the frontend wants (which could in turn
+ * > consist of multiple frames each as indicated by frame-type) and
+ * > when the frontend is done, it should call sane_cancel().
+ * > Sometimes it's better to think of sane_cancel() as "sane_stop()"
+ * > but that name would have had some misleading connotations as
+ * > well, that's why we stuck with "cancel".
+ *
+ * The current consensus regarding duplex and ADF scans seems to be
+ * the following call sequence: sane_start; sane_read (repeat until
+ * EOF); sane_start; sane_read... and then call sane_cancel if the
+ * batch is at an end. I.e. do not call sane_cancel during the run but
+ * as soon as you get a SANE_STATUS_NO_DOCS.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ struct scanner * s = (struct scanner *) handle;
+
+ DBG (10, "sane_cancel: start\n");
+ s->cancelled = 1;
+
+ /* if there is no other running function to check, we do it */
+ if(!s->reading)
+ check_for_cancel(s);
+
+ DBG (10, "sane_cancel: finish\n");
+}
+
+/* checks started and cancelled flags in scanner struct,
+ * sends cancel command to scanner if required. don't call
+ * this function asyncronously, wait for pending operation */
+static SANE_Status
+check_for_cancel(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "check_for_cancel: start\n");
+
+ if(s->started && s->cancelled){
+ unsigned char cmd[CANCEL_len];
+ size_t cmdLen = CANCEL_len;
+
+ DBG (15, "check_for_cancel: cancelling\n");
+
+ /* cancel scan */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, CANCEL_code);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "check_for_cancel: ignoring bad cancel: %d\n",ret);
+ }
+
+ ret = object_position(s,SANE_FALSE);
+ if(ret){
+ DBG (5, "check_for_cancel: ignoring bad eject: %d\n",ret);
+ }
+
+ s->started = 0;
+ s->cancelled = 0;
+ ret = SANE_STATUS_CANCELLED;
+ }
+ else if(s->cancelled){
+ DBG (15, "check_for_cancel: already cancelled\n");
+ s->cancelled = 0;
+ ret = SANE_STATUS_CANCELLED;
+ }
+
+ DBG (10, "check_for_cancel: finish %d\n",ret);
+ return ret;
+}
+
+/*
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ struct scanner * s = (struct scanner *) handle;
+
+ DBG (10, "sane_close: start\n");
+ disconnect_fd(s);
+ image_buffers(s,0);
+ offset_buffers(s,0);
+ gain_buffers(s,0);
+ DBG (10, "sane_close: finish\n");
+}
+
+static SANE_Status
+disconnect_fd (struct scanner *s)
+{
+ DBG (10, "disconnect_fd: start\n");
+
+ if(s->fd > -1){
+ if (s->connection == CONNECTION_USB) {
+ DBG (15, "disconnecting usb device\n");
+ sanei_usb_close (s->fd);
+ }
+ else if (s->connection == CONNECTION_SCSI) {
+ DBG (15, "disconnecting scsi device\n");
+ sanei_scsi_close (s->fd);
+ }
+ s->fd = -1;
+ }
+
+ DBG (10, "disconnect_fd: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct scanner *dev, *next;
+
+ DBG (10, "sane_exit: start\n");
+
+ for (dev = scanner_devList; dev; dev = next) {
+ disconnect_fd(dev);
+ next = dev->next;
+ free (dev);
+ }
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ scanner_devList = NULL;
+ sane_devArray = NULL;
+
+ DBG (10, "sane_exit: finish\n");
+}
+
+
+/*
+ * @@ Section 7 - misc helper functions
+ */
+static void
+default_globals(void)
+{
+ global_buffer_size = global_buffer_size_default;
+ global_padded_read = global_padded_read_default;
+ global_duplex_offset = global_duplex_offset_default;
+ global_vendor_name[0] = 0;
+ global_model_name[0] = 0;
+ global_version_name[0] = 0;
+}
+
+/*
+ * Called by the SANE SCSI core and our usb code on device errors
+ * parses the request sense return data buffer,
+ * decides the best SANE_Status for the problem, produces debug msgs,
+ * and copies the sense buffer into the scanner struct
+ */
+static SANE_Status
+sense_handler (int fd, unsigned char * sensed_data, void *arg)
+{
+ struct scanner *s = arg;
+ unsigned int sense = get_RS_sense_key (sensed_data);
+ unsigned int asc = get_RS_ASC (sensed_data);
+ unsigned int ascq = get_RS_ASCQ (sensed_data);
+ unsigned int eom = get_RS_EOM (sensed_data);
+ unsigned int ili = get_RS_ILI (sensed_data);
+ unsigned int info = get_RS_information (sensed_data);
+
+ DBG (5, "sense_handler: start\n");
+
+ /* kill compiler warning */
+ fd = fd;
+
+ /* copy the rs return data into the scanner struct
+ so that the caller can use it if he wants
+ memcpy(&s->rs_buffer,sensed_data,RS_return_size);
+ */
+
+ DBG (5, "Sense=%#02x, ASC=%#02x, ASCQ=%#02x, EOM=%d, ILI=%d, info=%#08x\n", sense, asc, ascq, eom, ili, info);
+
+ switch (sense) {
+ case 0:
+ if (ili == 1) {
+ s->rs_info = info;
+ DBG (5, "No sense: EOM remainder:%d\n",info);
+ return SANE_STATUS_EOF;
+ }
+ DBG (5, "No sense: unknown asc/ascq\n");
+ return SANE_STATUS_GOOD;
+
+ case 1:
+ if (asc == 0x37 && ascq == 0x00) {
+ DBG (5, "Recovered error: parameter rounded\n");
+ return SANE_STATUS_GOOD;
+ }
+ DBG (5, "Recovered error: unknown asc/ascq\n");
+ return SANE_STATUS_GOOD;
+
+ case 2:
+ if (asc == 0x04 && ascq == 0x01) {
+ DBG (5, "Not ready: previous command unfinished\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ DBG (5, "Not ready: unknown asc/ascq\n");
+ return SANE_STATUS_DEVICE_BUSY;
+
+ case 3:
+ if (asc == 0x36 && ascq == 0x00) {
+ DBG (5, "Medium error: no cartridge\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x3a && ascq == 0x00) {
+ DBG (5, "Medium error: hopper empty\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ if (asc == 0x80 && ascq == 0x00) {
+ DBG (5, "Medium error: paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (asc == 0x80 && ascq == 0x01) {
+ DBG (5, "Medium error: cover open\n");
+ return SANE_STATUS_COVER_OPEN;
+ }
+ if (asc == 0x81 && ascq == 0x01) {
+ DBG (5, "Medium error: double feed\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (asc == 0x81 && ascq == 0x02) {
+ DBG (5, "Medium error: skew detected\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (asc == 0x81 && ascq == 0x04) {
+ DBG (5, "Medium error: staple detected\n");
+ return SANE_STATUS_JAMMED;
+ }
+ DBG (5, "Medium error: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 4:
+ if (asc == 0x60 && ascq == 0x00) {
+ DBG (5, "Hardware error: lamp error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x80 && ascq == 0x01) {
+ DBG (5, "Hardware error: CPU check error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x80 && ascq == 0x02) {
+ DBG (5, "Hardware error: RAM check error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x80 && ascq == 0x03) {
+ DBG (5, "Hardware error: ROM check error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x80 && ascq == 0x04) {
+ DBG (5, "Hardware error: hardware check error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Hardware error: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 5:
+ if (asc == 0x1a && ascq == 0x00) {
+ DBG (5, "Illegal request: Parameter list error\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x20 && ascq == 0x00) {
+ DBG (5, "Illegal request: invalid command\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x24 && ascq == 0x00) {
+ DBG (5, "Illegal request: invalid CDB field\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x25 && ascq == 0x00) {
+ DBG (5, "Illegal request: unsupported logical unit\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ if (asc == 0x26 && ascq == 0x00) {
+ DBG (5, "Illegal request: invalid field in parm list\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x2c && ascq == 0x00) {
+ DBG (5, "Illegal request: command sequence error\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x2c && ascq == 0x01) {
+ DBG (5, "Illegal request: too many windows\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x3a && ascq == 0x00) {
+ DBG (5, "Illegal request: no paper\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ if (asc == 0x3d && ascq == 0x00) {
+ DBG (5, "Illegal request: invalid IDENTIFY\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (asc == 0x55 && ascq == 0x00) {
+ DBG (5, "Illegal request: scanner out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (5, "Illegal request: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 6:
+ if (asc == 0x29 && ascq == 0x00) {
+ DBG (5, "Unit attention: device reset\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (asc == 0x2a && ascq == 0x00) {
+ DBG (5, "Unit attention: param changed by 2nd initiator\n");
+ return SANE_STATUS_GOOD;
+ }
+ DBG (5, "Unit attention: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 7:
+ DBG (5, "Data protect: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 8:
+ DBG (5, "Blank check: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 9:
+ DBG (5, "Vendor defined: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 0xa:
+ DBG (5, "Copy aborted: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 0xb:
+ if (asc == 0x00 && ascq == 0x00) {
+ DBG (5, "Aborted command: no sense/cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ if (asc == 0x45 && ascq == 0x00) {
+ DBG (5, "Aborted command: reselect failure\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x47 && ascq == 0x00) {
+ DBG (5, "Aborted command: SCSI parity error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x48 && ascq == 0x00) {
+ DBG (5, "Aborted command: initiator error message\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x49 && ascq == 0x00) {
+ DBG (5, "Aborted command: invalid message\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x80 && ascq == 0x00) {
+ DBG (5, "Aborted command: timeout\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Aborted command: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0xc:
+ DBG (5, "Equal: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 0xd:
+ DBG (5, "Volume overflow: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 0xe:
+ if (asc == 0x3b && ascq == 0x0d) {
+ DBG (5, "Miscompare: too many docs\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (asc == 0x3b && ascq == 0x0e) {
+ DBG (5, "Miscompare: too few docs\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Miscompare: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+
+ default:
+ DBG (5, "Unknown Sense Code\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "sense_handler: should never happen!\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/*
+ * take a bunch of pointers, send commands to scanner
+ */
+static SANE_Status
+do_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ if (s->connection == CONNECTION_SCSI) {
+ return do_scsi_cmd(s, runRS, shortTime,
+ cmdBuff, cmdLen,
+ outBuff, outLen,
+ inBuff, inLen
+ );
+ }
+ if (s->connection == CONNECTION_USB) {
+ return do_usb_cmd(s, runRS, shortTime,
+ cmdBuff, cmdLen,
+ outBuff, outLen,
+ inBuff, inLen
+ );
+ }
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+do_scsi_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ int ret;
+ size_t actLen = 0;
+
+ /*shut up compiler*/
+ runRS=runRS;
+ shortTime=shortTime;
+
+ DBG(10, "do_scsi_cmd: start\n");
+
+ DBG(25, "cmd: writing %d bytes\n", (int)cmdLen);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+
+ if(outBuff && outLen){
+ DBG(25, "out: writing %d bytes\n", (int)outLen);
+ hexdump(30, "out: >>", outBuff, outLen);
+ }
+ if (inBuff && inLen){
+ DBG(25, "in: reading %d bytes\n", (int)*inLen);
+ memset(inBuff,0,*inLen);
+ actLen = *inLen;
+ }
+
+ ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen);
+
+ if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){
+ DBG(5,"do_scsi_cmd: return '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ if (inBuff && inLen){
+ if(ret == SANE_STATUS_EOF){
+ DBG(25, "in: short read, remainder %lu bytes\n", (u_long)s->rs_info);
+ *inLen -= s->rs_info;
+ }
+ hexdump(30, "in: <<", inBuff, *inLen);
+ DBG(25, "in: read %d bytes\n", (int)*inLen);
+ }
+
+ DBG(10, "do_scsi_cmd: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+do_usb_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ size_t cmdOffset = 0;
+ size_t cmdLength = 0;
+ size_t cmdActual = 0;
+ unsigned char * cmdBuffer = NULL;
+ int cmdTimeout = 0;
+
+ size_t outOffset = 0;
+ size_t outLength = 0;
+ size_t outActual = 0;
+ unsigned char * outBuffer = NULL;
+ int outTimeout = 0;
+
+ size_t inOffset = 0;
+ size_t inLength = 0;
+ size_t inActual = 0;
+ unsigned char * inBuffer = NULL;
+ int inTimeout = 0;
+
+ size_t statOffset = 0;
+ size_t statLength = 0;
+ size_t statActual = 0;
+ unsigned char * statBuffer = NULL;
+ int statTimeout = 0;
+
+ int ret = 0;
+ int ret2 = 0;
+
+ DBG (10, "do_usb_cmd: start\n");
+
+ /****************************************************************/
+ /* the command stage */
+ {
+ cmdOffset = USB_HEADER_LEN;
+ cmdLength = cmdOffset+USB_COMMAND_LEN;
+ cmdActual = cmdLength;
+ cmdTimeout = USB_COMMAND_TIME;
+
+ /* change timeout */
+ if(shortTime)
+ cmdTimeout/=60;
+ sanei_usb_set_timeout(cmdTimeout);
+
+ /* build buffer */
+ cmdBuffer = calloc(cmdLength,1);
+ if(!cmdBuffer){
+ DBG(5,"cmd: no mem\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* build a USB packet around the SCSI command */
+ cmdBuffer[3] = cmdLength-4;
+ cmdBuffer[5] = 1;
+ cmdBuffer[6] = 0x90;
+ memcpy(cmdBuffer+cmdOffset,cmdBuff,cmdLen);
+
+ /* write the command out */
+ DBG(25, "cmd: writing %d bytes, timeout %d\n", (int)cmdLength, cmdTimeout);
+ hexdump(30, "cmd: >>", cmdBuffer, cmdLength);
+ ret = sanei_usb_write_bulk(s->fd, cmdBuffer, &cmdActual);
+ DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)cmdActual, ret);
+
+ if(cmdLength != cmdActual){
+ DBG(5,"cmd: wrong size %d/%d\n", (int)cmdLength, (int)cmdActual);
+ free(cmdBuffer);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"cmd: write error '%s'\n",sane_strstatus(ret));
+ free(cmdBuffer);
+ return ret;
+ }
+ free(cmdBuffer);
+ }
+
+ /****************************************************************/
+ /* the output stage */
+ if(outBuff && outLen){
+
+ outOffset = USB_HEADER_LEN;
+ outLength = outOffset+outLen;
+ outActual = outLength;
+ outTimeout = USB_DATA_TIME;
+
+ /* change timeout */
+ if(shortTime)
+ outTimeout/=60;
+ sanei_usb_set_timeout(outTimeout);
+
+ /* build outBuffer */
+ outBuffer = calloc(outLength,1);
+ if(!outBuffer){
+ DBG(5,"out: no mem\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* build a USB packet around the SCSI command */
+ outBuffer[3] = outLength-4;
+ outBuffer[5] = 2;
+ outBuffer[6] = 0xb0;
+ memcpy(outBuffer+outOffset,outBuff,outLen);
+
+ /* write the command out */
+ DBG(25, "out: writing %d bytes, timeout %d\n", (int)outLength, outTimeout);
+ hexdump(30, "out: >>", outBuffer, outLength);
+ ret = sanei_usb_write_bulk(s->fd, outBuffer, &outActual);
+ DBG(25, "out: wrote %d bytes, retVal %d\n", (int)outActual, ret);
+
+ if(outLength != outActual){
+ DBG(5,"out: wrong size %d/%d\n", (int)outLength, (int)outActual);
+ free(outBuffer);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"out: write error '%s'\n",sane_strstatus(ret));
+ free(outBuffer);
+ return ret;
+ }
+ free(outBuffer);
+ }
+
+ /****************************************************************/
+ /* the input stage */
+ if(inBuff && inLen){
+
+ inOffset = 0;
+ if(s->padded_read)
+ inOffset = USB_HEADER_LEN;
+
+ inLength = inOffset+*inLen;
+ inActual = inLength;
+
+ /*blast caller's copy in case we error out*/
+ *inLen = 0;
+
+ inTimeout = USB_DATA_TIME;
+
+ /* change timeout */
+ if(shortTime)
+ inTimeout/=60;
+ sanei_usb_set_timeout(inTimeout);
+
+ /* build inBuffer */
+ inBuffer = calloc(inLength,1);
+ if(!inBuffer){
+ DBG(5,"in: no mem\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(25, "in: reading %d bytes, timeout %d\n", (int)inLength, inTimeout);
+ ret = sanei_usb_read_bulk(s->fd, inBuffer, &inActual);
+ DBG(25, "in: read %d bytes, retval %d\n", (int)inActual, ret);
+ hexdump(30, "in: <<", inBuffer, inActual);
+
+ if(!inActual){
+ DBG(5,"in: got no data, clearing\n");
+ free(inBuffer);
+ return do_usb_clear(s,1,runRS);
+ }
+ if(inActual < inOffset){
+ DBG(5,"in: read shorter than inOffset\n");
+ free(inBuffer);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"in: return error '%s'\n",sane_strstatus(ret));
+ free(inBuffer);
+ return ret;
+ }
+
+ /* note that inBuffer is not copied and freed here...*/
+ }
+
+ /****************************************************************/
+ /* the status stage */
+ statOffset = 0;
+ if(s->padded_read)
+ statOffset = USB_HEADER_LEN;
+
+ statLength = statOffset+USB_STATUS_LEN;
+ statActual = statLength;
+ statTimeout = USB_STATUS_TIME;
+
+ /* change timeout */
+ if(shortTime)
+ statTimeout/=60;
+ sanei_usb_set_timeout(statTimeout);
+
+ /* build statBuffer */
+ statBuffer = calloc(statLength,1);
+ if(!statBuffer){
+ DBG(5,"stat: no mem\n");
+ if(inBuffer) free(inBuffer);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(25, "stat: reading %d bytes, timeout %d\n", (int)statLength, statTimeout);
+ ret2 = sanei_usb_read_bulk(s->fd, statBuffer, &statActual);
+ DBG(25, "stat: read %d bytes, retval %d\n", (int)statActual, ret2);
+ hexdump(30, "stat: <<", statBuffer, statActual);
+
+ /*weird status*/
+ if(ret2 != SANE_STATUS_GOOD){
+ DBG(5,"stat: clearing error '%s'\n",sane_strstatus(ret2));
+ ret2 = do_usb_clear(s,1,runRS);
+ }
+ /*short read*/
+ else if(statLength != statActual){
+ DBG(5,"stat: clearing short %d/%d\n",(int)statLength,(int)statActual);
+ ret2 = do_usb_clear(s,1,runRS);
+ }
+ /*inspect the last byte of the status response*/
+ else if(statBuffer[statLength-1]){
+ DBG(5,"stat: status %d\n",statBuffer[statLength-1]);
+ ret2 = do_usb_clear(s,0,runRS);
+ }
+ free(statBuffer);
+
+ /* if status said EOF, adjust input with remainder count */
+ if(ret2 == SANE_STATUS_EOF && inBuffer){
+
+ /* EOF is ok */
+ ret2 = SANE_STATUS_GOOD;
+
+ if(inActual <= inLength - s->rs_info){
+ DBG(5,"in: we read <= RS, ignoring RS: %d <= %d (%d-%d)\n",
+ (int)inActual,(int)(inLength-s->rs_info),(int)inLength,(int)s->rs_info);
+ }
+ else if(s->rs_info){
+ DBG(5,"in: we read > RS, using RS: %d to %d (%d-%d)\n",
+ (int)inActual,(int)(inLength-s->rs_info),(int)inLength,(int)s->rs_info);
+ inActual = inLength - s->rs_info;
+ }
+ }
+
+ /* bail out on bad RS status */
+ if(ret2){
+ if(inBuffer) free(inBuffer);
+ DBG(5,"stat: bad RS status, %d\n", ret2);
+ return ret2;
+ }
+
+ /* now that we have read status, deal with input buffer */
+ if(inBuffer){
+ if(inLength != inActual){
+ ret = SANE_STATUS_EOF;
+ DBG(5,"in: short read, %d/%d\n", (int)inLength,(int)inActual);
+ }
+
+ /* ignore the USB packet around the SCSI command */
+ *inLen = inActual - inOffset;
+ memcpy(inBuff,inBuffer+inOffset,*inLen);
+
+ free(inBuffer);
+ }
+
+ DBG (10, "do_usb_cmd: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+do_usb_clear(struct scanner *s, int clear, int runRS)
+{
+ SANE_Status ret, ret2;
+
+ DBG (10, "do_usb_clear: start\n");
+
+ usleep(100000);
+
+ if(clear){
+ DBG (15, "do_usb_clear: clear halt\n");
+ ret = sanei_usb_clear_halt(s->fd);
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"do_usb_clear: cant clear halt, returning %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* caller is interested in having RS run on errors */
+ if(runRS){
+
+ unsigned char rs_cmd[REQUEST_SENSE_len];
+ size_t rs_cmdLen = REQUEST_SENSE_len;
+
+ unsigned char rs_in[RS_return_size];
+ size_t rs_inLen = RS_return_size;
+
+ memset(rs_cmd,0,rs_cmdLen);
+ set_SCSI_opcode(rs_cmd, REQUEST_SENSE_code);
+ set_RS_return_size(rs_cmd, rs_inLen);
+
+ DBG(25,"rs sub call >>\n");
+ ret2 = do_cmd(
+ s,0,0,
+ rs_cmd, rs_cmdLen,
+ NULL,0,
+ rs_in, &rs_inLen
+ );
+ DBG(25,"rs sub call <<\n");
+
+ if(ret2 == SANE_STATUS_EOF){
+ DBG(5,"rs: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret2 != SANE_STATUS_GOOD){
+ DBG(5,"rs: return error '%s'\n",sane_strstatus(ret2));
+ return ret2;
+ }
+
+ /* parse the rs data */
+ ret2 = sense_handler( 0, rs_in, (void *)s );
+
+ DBG (10, "do_usb_clear: finish after RS\n");
+ return ret2;
+ }
+
+ DBG (10, "do_usb_clear: finish with io error\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+wait_scanner(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[TEST_UNIT_READY_len];
+ size_t cmdLen = TEST_UNIT_READY_len;
+
+ DBG (10, "wait_scanner: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,TEST_UNIT_READY_code);
+
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret));
+ }
+
+ DBG (10, "wait_scanner: finish\n");
+
+ return ret;
+}
+
+/* s->u.page_x stores the user setting
+ * for the paper width in adf. sometimes,
+ * we need a value that differs from this
+ * due to using FB or overscan.
+ */
+static int
+get_page_width(struct scanner *s)
+{
+ int width = s->u.page_x;
+
+ /* scanner max for fb */
+ if(s->u.source == SOURCE_FLATBED){
+ return s->max_x_fb;
+ }
+
+ /* cant overscan larger than scanner max */
+ if(width > s->valid_x){
+ return s->valid_x;
+ }
+
+ /* overscan adds a margin to both sides */
+ return width;
+}
+
+/* s->u.page_y stores the user setting
+ * for the paper height in adf. sometimes,
+ * we need a value that differs from this
+ * due to using FB or overscan.
+ */
+static int
+get_page_height(struct scanner *s)
+{
+ int height = s->u.page_y;
+
+ /* scanner max for fb */
+ if(s->u.source == SOURCE_FLATBED){
+ return s->max_y_fb;
+ }
+
+ /* cant overscan larger than scanner max */
+ if(height > s->max_y){
+ return s->max_y;
+ }
+
+ /* overscan adds a margin to both sides */
+ return height;
+}
+
+
+/**
+ * Convenience method to determine longest string size in a list.
+ */
+static size_t
+maxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+/*
+ * Prints a hex dump of the given buffer onto the debug output stream.
+ */
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[70]; /* 'xxx: xx xx ... xx xx abc */
+ char *hex = line+4;
+ char *bin = line+53;
+
+ if(DBG_LEVEL < level)
+ return;
+
+ line[0] = 0;
+
+ DBG (level, "%s\n", comment);
+
+ for (i = 0; i < l; i++, p++) {
+
+ /* at start of line */
+ if ((i % 16) == 0) {
+
+ /* not at start of first line, print current, reset */
+ if (i) {
+ DBG (level, "%s\n", line);
+ }
+
+ memset(line,0x20,69);
+ line[69] = 0;
+ hex = line + 4;
+ bin = line + 53;
+
+ sprintf (line, "%3.3x:", i);
+ }
+
+ /* the hex section */
+ sprintf (hex, " %2.2x", *p);
+ hex += 3;
+ *hex = ' ';
+
+ /* the char section */
+ if(*p >= 0x20 && *p <= 0x7e){
+ *bin=*p;
+ }
+ else{
+ *bin='.';
+ }
+ bin++;
+ }
+
+ /* print last (partial) line */
+ DBG (level, "%s\n", line);
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (15, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG (10, "sane_get_select_fd\n");
+ DBG (15, "%p %d\n", h, *fdp);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * @@ Section 8 - Image processing functions
+ */
+
+/* Look in image for likely upper and left paper edges, then rotate
+ * image so that upper left corner of paper is upper left of image.
+ * FIXME: should we do this before we binarize instead of after? */
+static SANE_Status
+buffer_deskew(struct scanner *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int pwidth = s->i.width;
+ int width = s->i.Bpl;
+ int height = s->i.height;
+
+ double TSlope = 0;
+ int TXInter = 0;
+ int TYInter = 0;
+ double TSlopeHalf = 0;
+ int TOffsetHalf = 0;
+
+ double LSlope = 0;
+ int LXInter = 0;
+ int LYInter = 0;
+ double LSlopeHalf = 0;
+ int LOffsetHalf = 0;
+
+ int rotateX = 0;
+ int rotateY = 0;
+
+ int * topBuf = NULL, * botBuf = NULL;
+
+ DBG (10, "buffer_deskew: start\n");
+
+ /* get buffers for edge detection */
+ topBuf = getTransitionsY(s,side,1);
+ if(!topBuf){
+ DBG (5, "buffer_deskew: cant gTY\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ if(0){
+ int i;
+ for(i=0;i<width;i++){
+ if(topBuf[i] >=0 && topBuf[i] < height)
+ s->buffers[side][topBuf[i]*width+i] = 0;
+ }
+ }
+
+ botBuf = getTransitionsY(s,side,0);
+ if(!botBuf){
+ DBG (5, "buffer_deskew: cant gTY\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* find best top line */
+ ret = getEdgeIterate (pwidth, height, s->i.dpi_y, topBuf,
+ &TSlope, &TXInter, &TYInter);
+ if(ret){
+ DBG(5,"buffer_deskew: gEI error: %d",ret);
+ goto cleanup;
+ }
+ DBG(15,"top: %04.04f %d %d\n",TSlope,TXInter,TYInter);
+
+ /* slope is too shallow, don't want to divide by 0 */
+ if(fabs(TSlope) < 0.0001){
+ DBG(15,"buffer_deskew: slope too shallow: %0.08f\n",TSlope);
+ goto cleanup;
+ }
+
+ /* find best left line, perpendicular to top line */
+ LSlope = (double)-1/TSlope;
+ ret = getEdgeSlope (pwidth, height, topBuf, botBuf, LSlope,
+ &LXInter, &LYInter);
+ if(ret){
+ DBG(5,"buffer_deskew: gES error: %d",ret);
+ goto cleanup;
+ }
+ DBG(15,"buffer_deskew: left: %04.04f %d %d\n",LSlope,LXInter,LYInter);
+
+ /* find point about which to rotate */
+ TSlopeHalf = tan(atan(TSlope)/2);
+ TOffsetHalf = LYInter;
+ DBG(15,"buffer_deskew: top half: %04.04f %d\n",TSlopeHalf,TOffsetHalf);
+
+ LSlopeHalf = tan((atan(LSlope) + ((LSlope < 0)?-M_PI_2:M_PI_2))/2);
+ LOffsetHalf = - LSlopeHalf * TXInter;
+ DBG(15,"buffer_deskew: left half: %04.04f %d\n",LSlopeHalf,LOffsetHalf);
+
+ rotateX = (LOffsetHalf-TOffsetHalf) / (TSlopeHalf-LSlopeHalf);
+ rotateY = TSlopeHalf * rotateX + TOffsetHalf;
+ DBG(15,"buffer_deskew: rotate: %d %d\n",rotateX,rotateY);
+
+ ret = rotateOnCenter (s, side, rotateX, rotateY, TSlope);
+ if(ret){
+ DBG(5,"buffer_deskew: gES error: %d",ret);
+ goto cleanup;
+ }
+
+ cleanup:
+ if(topBuf)
+ free(topBuf);
+ if(botBuf)
+ free(botBuf);
+
+ DBG (10, "buffer_deskew: finish\n");
+ return ret;
+}
+
+/* Look in image for likely left/right/bottom paper edges, then crop
+ * image to match. Does not attempt to rotate the image.
+ * FIXME: should we do this before we binarize instead of after? */
+static SANE_Status
+buffer_crop(struct scanner *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int bwidth = s->i.Bpl;
+ int width = s->i.width;
+ int height = s->i.height;
+
+ int top = 0;
+ int bot = 0;
+ int left = width;
+ int right = 0;
+
+ int * topBuf = NULL, * botBuf = NULL;
+ int * leftBuf = NULL, * rightBuf = NULL;
+ int leftCount = 0, rightCount = 0, botCount = 0;
+ int i;
+
+ DBG (10, "buffer_crop: start\n");
+
+ /* get buffers to find sides and bottom */
+ topBuf = getTransitionsY(s,side,1);
+ if(!topBuf){
+ DBG (5, "buffer_crop: no topBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ botBuf = getTransitionsY(s,side,0);
+ if(!botBuf){
+ DBG (5, "buffer_crop: no botBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ leftBuf = getTransitionsX(s,side,1);
+ if(!leftBuf){
+ DBG (5, "buffer_crop: no leftBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ rightBuf = getTransitionsX(s,side,0);
+ if(!rightBuf){
+ DBG (5, "buffer_crop: no rightBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* loop thru top and bottom lists, look for l and r extremes */
+ for(i=0; i<width; i++){
+ if(botBuf[i] > topBuf[i]){
+ if(left > i){
+ left = i;
+ }
+
+ leftCount++;
+ if(leftCount > 3){
+ break;
+ }
+ }
+ else{
+ leftCount = 0;
+ left = width;
+ }
+ }
+
+ for(i=width-1; i>=0; i--){
+ if(botBuf[i] > topBuf[i]){
+ if(right < i){
+ right = i;
+ }
+
+ rightCount++;
+ if(rightCount > 3){
+ break;
+ }
+ }
+ else{
+ rightCount = 0;
+ right = -1;
+ }
+ }
+
+ /* loop thru left and right lists, look for bottom extreme */
+ for(i=height-1; i>=0; i--){
+ if(rightBuf[i] > leftBuf[i]){
+ if(bot < i){
+ bot = i;
+ }
+
+ botCount++;
+ if(botCount > 3){
+ break;
+ }
+ }
+ else{
+ botCount = 0;
+ bot = -1;
+ }
+ }
+
+ DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n",top,bot,left,right);
+
+ /* now crop the image */
+ /*FIXME: crop duplex backside at same time?*/
+ if(left < right && top < bot){
+
+ int pixels = 0;
+ int bytes = 0;
+ unsigned char * line = NULL;
+
+ /*convert left and right to bytes, figure new byte and pixel width */
+ switch (s->i.mode) {
+
+ case MODE_COLOR:
+ pixels = right-left;
+ bytes = pixels * 3;
+ left *= 3;
+ right *= 3;
+ break;
+
+ case MODE_GRAYSCALE:
+ pixels = right-left;
+ bytes = right-left;
+ break;
+
+ case MODE_LINEART:
+ case MODE_HALFTONE:
+ left /= 8;
+ right = (right+7)/8;
+ bytes = right-left;
+ pixels = bytes * 8;
+ break;
+ }
+
+ DBG (15, "buffer_crop: l:%d r:%d p:%d b:%d\n",left,right,pixels,bytes);
+
+ line = malloc(bytes);
+ if(!line){
+ DBG (5, "buffer_crop: no line\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ s->i.bytes_sent[side] = 0;
+
+ for(i=top; i<bot; i++){
+ memcpy(line, s->buffers[side] + i*bwidth + left, bytes);
+ memcpy(s->buffers[side] + s->i.bytes_sent[side], line, bytes);
+ s->i.bytes_sent[side] += bytes;
+ }
+
+ s->i.bytes_tot[side] = s->i.bytes_sent[side];
+ s->i.width = pixels;
+ s->i.height = bot-top;
+ s->i.Bpl = bytes;
+
+ free(line);
+ }
+
+ cleanup:
+ if(topBuf)
+ free(topBuf);
+ if(botBuf)
+ free(botBuf);
+ if(leftBuf)
+ free(leftBuf);
+ if(rightBuf)
+ free(rightBuf);
+
+ DBG (10, "buffer_crop: finish\n");
+ return ret;
+}
+
+/* Look in image for disconnected 'spots' of the requested size.
+ * Replace the spots with the average color of the surrounding pixels.
+ * FIXME: should we do this before we binarize instead of after? */
+static SANE_Status
+buffer_despeck(struct scanner *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i,j,k,l,n;
+ int w = s->i.Bpl;
+ int pw = s->i.width;
+ int h = s->i.height;
+ int t = w*h;
+ int d = s->swdespeck;
+
+ DBG (10, "buffer_despeck: start\n");
+
+ switch (s->i.mode){
+
+ case MODE_COLOR:
+ for(i=w; i<t-w-(w*d); i+=w){
+ for(j=1; j<pw-1-d; j++){
+
+ int thresh = 255*3;
+ int outer[] = {0,0,0};
+ int hits = 0;
+
+ /*loop over rows and columns in window */
+ for(k=0; k<d; k++){
+ for(l=0; l<d; l++){
+ int tmp = 0;
+
+ for(n=0; n<3; n++){
+ tmp += s->buffers[side][i + j*3 + k*w + l*3 + n];
+ }
+
+ if(tmp < thresh)
+ thresh = tmp;
+ }
+ }
+
+ thresh = (thresh + 255*3 + 255*3)/3;
+
+ /*loop over rows and columns around window */
+ for(k=-1; k<d+1; k++){
+ for(l=-1; l<d+1; l++){
+
+ int tmp[3];
+
+ /* dont count pixels in the window */
+ if(k != -1 && k != d && l != -1 && l != d)
+ continue;
+
+ for(n=0; n<3; n++){
+ tmp[n] = s->buffers[side][i + j*3 + k*w + l*3 + n];
+ outer[n] += tmp[n];
+ }
+ if(tmp[0]+tmp[1]+tmp[2] < thresh){
+ hits++;
+ break;
+ }
+ }
+ }
+
+ for(n=0; n<3; n++){
+ outer[n] /= (4*d + 4);
+ }
+
+ /*no hits, overwrite with avg surrounding color*/
+ if(!hits){
+ for(k=0; k<d; k++){
+ for(l=0; l<d; l++){
+ for(n=0; n<3; n++){
+ s->buffers[side][i + j*3 + k*w + l*3 + n] = outer[n];
+ }
+ }
+ }
+ }
+
+ }
+ }
+ break;
+
+ case MODE_GRAYSCALE:
+ for(i=w; i<t-w-(w*d); i+=w){
+ for(j=1; j<w-1-d; j++){
+
+ int thresh = 255;
+ int outer = 0;
+ int hits = 0;
+
+ for(k=0; k<d; k++){
+ for(l=0; l<d; l++){
+ if(s->buffers[side][i + j + k*w + l] < thresh)
+ thresh = s->buffers[side][i + j + k*w + l];
+ }
+ }
+
+ thresh = (thresh + 255 + 255)/3;
+
+ /*loop over rows and columns around window */
+ for(k=-1; k<d+1; k++){
+ for(l=-1; l<d+1; l++){
+
+ int tmp = 0;
+
+ /* dont count pixels in the window */
+ if(k != -1 && k != d && l != -1 && l != d)
+ continue;
+
+ tmp = s->buffers[side][i + j + k*w + l];
+
+ if(tmp < thresh){
+ hits++;
+ break;
+ }
+
+ outer += tmp;
+ }
+ }
+
+ outer /= (4*d + 4);
+
+ /*no hits, overwrite with avg surrounding color*/
+ if(!hits){
+ for(k=0; k<d; k++){
+ for(l=0; l<d; l++){
+ s->buffers[side][i + j + k*w + l] = outer;
+ }
+ }
+ }
+
+ }
+ }
+ break;
+
+ case MODE_LINEART:
+ case MODE_HALFTONE:
+ for(i=w; i<t-w-(w*d); i+=w){
+ for(j=1; j<pw-1-d; j++){
+
+ int curr = 0;
+ int hits = 0;
+
+ for(k=0; k<d; k++){
+ for(l=0; l<d; l++){
+ curr += s->buffers[side][i + k*w + (j+l)/8] >> (7-(j+l)%8) & 1;
+ }
+ }
+
+ if(!curr)
+ continue;
+
+ /*loop over rows and columns around window */
+ for(k=-1; k<d+1; k++){
+ for(l=-1; l<d+1; l++){
+
+ /* dont count pixels in the window */
+ if(k != -1 && k != d && l != -1 && l != d)
+ continue;
+
+ hits += s->buffers[side][i + k*w + (j+l)/8] >> (7-(j+l)%8) & 1;
+
+ if(hits)
+ break;
+ }
+ }
+
+ /*no hits, overwrite with white*/
+ if(!hits){
+ for(k=0; k<d; k++){
+ for(l=0; l<d; l++){
+ s->buffers[side][i + k*w + (j+l)/8] &= ~(1 << (7-(j+l)%8));
+ }
+ }
+ }
+
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ DBG (10, "buffer_despeck: finish\n");
+ return ret;
+}
+
+/* Loop thru the image width and look for first color change in each column.
+ * Return a malloc'd array. Caller is responsible for freeing. */
+int *
+getTransitionsY (struct scanner *s, int side, int top)
+{
+ int * buff;
+
+ int i, j, k;
+ int near, far;
+ int winLen = 9;
+
+ int width = s->i.width;
+ int height = s->i.height;
+ int depth = 1;
+
+ /* defaults for bottom-up */
+ int firstLine = height-1;
+ int lastLine = -1;
+ int direction = -1;
+
+ DBG (10, "getTransitionsY: start\n");
+
+ buff = calloc(width,sizeof(int));
+ if(!buff){
+ DBG (5, "getTransitionsY: no buff\n");
+ return NULL;
+ }
+
+ /* override for top-down */
+ if(top){
+ firstLine = 0;
+ lastLine = height;
+ direction = 1;
+ }
+
+ /* load the buff array with y value for first color change from edge
+ * gray/color uses a different algo from binary/halftone */
+ switch (s->i.mode) {
+
+ case MODE_COLOR:
+ depth = 3;
+
+ case MODE_GRAYSCALE:
+
+ for(i=0; i<width; i++){
+ buff[i] = lastLine;
+
+ /* load the near and far windows with repeated copy of first pixel */
+ near = 0;
+ for(k=0; k<depth; k++){
+ near += s->buffers[side][(firstLine*width+i) * depth + k];
+ }
+ near *= winLen;
+ far = near;
+
+ /* move windows, check delta */
+ for(j=firstLine+direction; j!=lastLine; j+=direction){
+
+ int farLine = j-winLen*2*direction;
+ int nearLine = j-winLen*direction;
+
+ if(farLine < 0 || farLine >= height){
+ farLine = firstLine;
+ }
+ if(nearLine < 0 || nearLine >= height){
+ nearLine = firstLine;
+ }
+
+ for(k=0; k<depth; k++){
+ far -= s->buffers[side][(farLine*width+i)*depth+k];
+ far += s->buffers[side][(nearLine*width+i)*depth+k];
+
+ near -= s->buffers[side][(nearLine*width+i)*depth+k];
+ near += s->buffers[side][(j*width+i)*depth+k];
+ }
+
+ if(abs(near - far) > winLen*depth*9){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ break;
+
+ case MODE_LINEART:
+ case MODE_HALFTONE:
+ for(i=0; i<width; i++){
+ buff[i] = lastLine;
+
+ /* load the near window with first pixel */
+ near = s->buffers[side][(firstLine*width+i)/8] >> (7-(i%8)) & 1;
+
+ /* move */
+ for(j=firstLine+direction; j!=lastLine; j+=direction){
+ if((s->buffers[side][(j*width+i)/8] >> (7-(i%8)) & 1) != near){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ break;
+
+ }
+
+ /* blast any stragglers with no neighbors within .5 inch */
+ for(i=0;i<width-7;i++){
+ int sum = 0;
+ for(j=1;j<=7;j++){
+ if(abs(buff[i+j] - buff[i]) < s->i.dpi_y/2)
+ sum++;
+ }
+ if(sum < 2)
+ buff[i] = lastLine;
+ }
+
+ DBG (10, "getTransitionsY: finish\n");
+
+ return buff;
+}
+
+/* Loop thru the image height and look for first color change in each row.
+ * Return a malloc'd array. Caller is responsible for freeing. */
+int *
+getTransitionsX (struct scanner *s, int side, int left)
+{
+ int * buff;
+
+ int i, j, k;
+ int near, far;
+ int winLen = 9;
+
+ int bwidth = s->i.Bpl;
+ int width = s->i.width;
+ int height = s->i.height;
+ int depth = 1;
+
+ /* defaults for right-first */
+ int firstCol = width-1;
+ int lastCol = -1;
+ int direction = -1;
+
+ DBG (10, "getTransitionsX: start\n");
+
+ buff = calloc(height,sizeof(int));
+ if(!buff){
+ DBG (5, "getTransitionsY: no buff\n");
+ return NULL;
+ }
+
+ /* override for left-first*/
+ if(left){
+ firstCol = 0;
+ lastCol = width;
+ direction = 1;
+ }
+
+ /* load the buff array with x value for first color change from edge
+ * gray/color uses a different algo from binary/halftone */
+ switch (s->i.mode) {
+
+ case MODE_COLOR:
+ depth = 3;
+
+ case MODE_GRAYSCALE:
+
+ for(i=0; i<height; i++){
+ buff[i] = lastCol;
+
+ /* load the near and far windows with repeated copy of first pixel */
+ near = 0;
+ for(k=0; k<depth; k++){
+ near += s->buffers[side][i*bwidth + k];
+ }
+ near *= winLen;
+ far = near;
+
+ /* move windows, check delta */
+ for(j=firstCol+direction; j!=lastCol; j+=direction){
+
+ int farCol = j-winLen*2*direction;
+ int nearCol = j-winLen*direction;
+
+ if(farCol < 0 || farCol >= width){
+ farCol = firstCol;
+ }
+ if(nearCol < 0 || nearCol >= width){
+ nearCol = firstCol;
+ }
+
+ for(k=0; k<depth; k++){
+ far -= s->buffers[side][i*bwidth + farCol*depth + k];
+ far += s->buffers[side][i*bwidth + nearCol*depth + k];
+
+ near -= s->buffers[side][i*bwidth + nearCol*depth + k];
+ near += s->buffers[side][i*bwidth + j*depth + k];
+ }
+
+ if(abs(near - far) > winLen*depth*9){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ break;
+
+ case MODE_LINEART:
+ case MODE_HALFTONE:
+ for(i=0; i<height; i++){
+ buff[i] = lastCol;
+
+ /* load the near window with first pixel */
+ near = s->buffers[side][i*bwidth + firstCol/8] >> (7-(firstCol%8)) & 1;
+
+ /* move */
+ for(j=firstCol+direction; j!=lastCol; j+=direction){
+ if((s->buffers[side][i*bwidth + j/8] >> (7-(j%8)) & 1) != near){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ break;
+
+ }
+
+ /* blast any stragglers with no neighbors within .5 inch */
+ for(i=0;i<height-7;i++){
+ int sum = 0;
+ for(j=1;j<=7;j++){
+ if(abs(buff[i+j] - buff[i]) < s->i.dpi_x/2)
+ sum++;
+ }
+ if(sum < 2)
+ buff[i] = lastCol;
+ }
+
+ DBG (10, "getTransitionsX: finish\n");
+
+ return buff;
+}
+
+/* Loop thru a getTransitions array, and use a simplified Hough transform
+ * to divide likely edges into a 2-d array of bins. Then weight each
+ * bin based on its angle and offset. Return the 'best' bin. */
+static SANE_Status
+getLine (int height, int width, int * buff,
+ int slopes, double minSlope, double maxSlope,
+ int offsets, int minOffset, int maxOffset,
+ double * finSlope, int * finOffset, int * finDensity)
+{
+ SANE_Status ret = 0;
+
+ int ** lines = NULL;
+ int i, j;
+ int rise, run;
+ double slope;
+ int offset;
+ int sIndex, oIndex;
+ int hWidth = width/2;
+
+ double * slopeCenter = NULL;
+ int * slopeScale = NULL;
+ double * offsetCenter = NULL;
+ int * offsetScale = NULL;
+
+ int maxDensity = 1;
+ double absMaxSlope = fabs(maxSlope);
+ double absMinSlope = fabs(minSlope);
+ int absMaxOffset = abs(maxOffset);
+ int absMinOffset = abs(minOffset);
+
+ DBG(10,"getLine: start %+0.4f %+0.4f %d %d\n",
+ minSlope,maxSlope,minOffset,maxOffset);
+
+ /*silence compiler*/
+ height = height;
+
+ if(absMaxSlope < absMinSlope)
+ absMaxSlope = absMinSlope;
+
+ if(absMaxOffset < absMinOffset)
+ absMaxOffset = absMinOffset;
+
+ /* build an array of pretty-print values for slope */
+ slopeCenter = calloc(slopes,sizeof(double));
+ if(!slopeCenter){
+ DBG(5,"getLine: cant load slopeCenter\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* build an array of scaling factors for slope */
+ slopeScale = calloc(slopes,sizeof(int));
+ if(!slopeScale){
+ DBG(5,"getLine: cant load slopeScale\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(j=0;j<slopes;j++){
+
+ /* find central value of this 'bucket' */
+ slopeCenter[j] = (
+ (double)j*(maxSlope-minSlope)/slopes+minSlope
+ + (double)(j+1)*(maxSlope-minSlope)/slopes+minSlope
+ )/2;
+
+ /* scale value from the requested range into an inverted 100-1 range
+ * input close to 0 makes output close to 100 */
+ slopeScale[j] = 101 - fabs(slopeCenter[j])*100/absMaxSlope;
+ }
+
+ /* build an array of pretty-print values for offset */
+ offsetCenter = calloc(offsets,sizeof(double));
+ if(!offsetCenter){
+ DBG(5,"getLine: cant load offsetCenter\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* build an array of scaling factors for offset */
+ offsetScale = calloc(offsets,sizeof(int));
+ if(!offsetScale){
+ DBG(5,"getLine: cant load offsetScale\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(j=0;j<offsets;j++){
+
+ /* find central value of this 'bucket'*/
+ offsetCenter[j] = (
+ (double)j/offsets*(maxOffset-minOffset)+minOffset
+ + (double)(j+1)/offsets*(maxOffset-minOffset)+minOffset
+ )/2;
+
+ /* scale value from the requested range into an inverted 100-1 range
+ * input close to 0 makes output close to 100 */
+ offsetScale[j] = 101 - fabs(offsetCenter[j])*100/absMaxOffset;
+ }
+
+ /* build 2-d array of 'density', divided into slope and offset ranges */
+ lines = calloc(slopes, sizeof(int *));
+ if(!lines){
+ DBG(5,"getLine: cant load lines\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(i=0;i<slopes;i++){
+ if(!(lines[i] = calloc(offsets, sizeof(int)))){
+ DBG(5,"getLine: cant load lines %d\n",i);
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+
+ for(i=0;i<width;i++){
+ for(j=i+1;j<width && j<i+width/3;j++){
+
+ /*FIXME: check for invalid (min/max) values?*/
+ rise = buff[j] - buff[i];
+ run = j-i;
+
+ slope = (double)rise/run;
+ if(slope >= maxSlope || slope < minSlope)
+ continue;
+
+ /* offset in center of width, not y intercept! */
+ offset = slope * hWidth + buff[i] - slope * i;
+ if(offset >= maxOffset || offset < minOffset)
+ continue;
+
+ sIndex = (slope - minSlope) * slopes/(maxSlope-minSlope);
+ if(sIndex >= slopes)
+ continue;
+
+ oIndex = (offset - minOffset) * offsets/(maxOffset-minOffset);
+ if(oIndex >= offsets)
+ continue;
+
+ lines[sIndex][oIndex]++;
+ }
+ }
+
+ /* go thru array, and find most dense line (highest number) */
+ for(i=0;i<slopes;i++){
+ for(j=0;j<offsets;j++){
+ if(lines[i][j] > maxDensity)
+ maxDensity = lines[i][j];
+ }
+ }
+
+ DBG(15,"getLine: maxDensity %d\n",maxDensity);
+
+ *finSlope = 0;
+ *finOffset = 0;
+ *finDensity = 0;
+
+ /* go thru array, and scale densities to % of maximum, plus adjust for
+ * prefered (smaller absolute value) slope and offset */
+ for(i=0;i<slopes;i++){
+ for(j=0;j<offsets;j++){
+ lines[i][j] = lines[i][j] * slopeScale[i] * offsetScale[j] / maxDensity;
+ if(lines[i][j] > *finDensity){
+ *finDensity = lines[i][j];
+ *finSlope = slopeCenter[i];
+ *finOffset = offsetCenter[j];
+ }
+ }
+ }
+
+ if(0){
+ DBG(15,"offsetCenter: ");
+ for(j=0;j<offsets;j++){
+ DBG(15," %+04.0f",offsetCenter[j]);
+ }
+ DBG(15,"\n");
+
+ DBG(15,"offsetScale: ");
+ for(j=0;j<offsets;j++){
+ DBG(15," %04d",offsetScale[j]);
+ }
+ DBG(15,"\n");
+
+ for(i=0;i<slopes;i++){
+ DBG(15,"slope: %02d %+02.2f %03d:",i,slopeCenter[i],slopeScale[i]);
+ for(j=0;j<offsets;j++){
+ DBG(15,"% 5d",lines[i][j]/100);
+ }
+ DBG(15,"\n");
+ }
+ }
+
+ /* dont forget to cleanup */
+ cleanup:
+ for(i=0;i<10;i++){
+ if(lines[i])
+ free(lines[i]);
+ }
+ if(lines)
+ free(lines);
+ if(slopeCenter)
+ free(slopeCenter);
+ if(slopeScale)
+ free(slopeScale);
+ if(offsetCenter)
+ free(offsetCenter);
+ if(offsetScale)
+ free(offsetScale);
+
+ DBG(10,"getLine: finish\n");
+
+ return ret;
+}
+
+/* Repeatedly find the best range of slope and offset via Hough transform.
+ * Shift the ranges thru 4 different positions to avoid splitting data
+ * across multiple bins (false positive). Home-in on the most likely upper
+ * line of the paper inside the image. Return the 'best' line. */
+SANE_Status
+getEdgeIterate (int width, int height, int resolution,
+int * buff, double * finSlope, int * finXInter, int * finYInter)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int slopes = 11;
+ int offsets = 11;
+ double maxSlope = 1;
+ double minSlope = -1;
+ int maxOffset = resolution/6;
+ int minOffset = -resolution/6;
+
+ double topSlope = 0;
+ int topOffset = 0;
+ int topDensity = 0;
+
+ int i,j;
+ int pass = 0;
+
+ DBG(10,"getEdgeIterate: start\n");
+
+ while(pass++ < 7){
+ double sStep = (maxSlope-minSlope)/slopes;
+ int oStep = (maxOffset-minOffset)/offsets;
+
+ double slope = 0;
+ int offset = 0;
+ int density = 0;
+ int go = 0;
+
+ topSlope = 0;
+ topOffset = 0;
+ topDensity = 0;
+
+ /* find lines 4 times with slightly moved params,
+ * to bypass binning errors, highest density wins */
+ for(i=0;i<2;i++){
+ double sStep2 = sStep*i/2;
+ for(j=0;j<2;j++){
+ int oStep2 = oStep*j/2;
+ ret = getLine(height,width,buff,slopes,minSlope+sStep2,maxSlope+sStep2,offsets,minOffset+oStep2,maxOffset+oStep2,&slope,&offset,&density);
+ if(ret){
+ DBG(5,"getEdgeIterate: getLine error %d\n",ret);
+ return ret;
+ }
+ DBG(15,"getEdgeIterate: %d %d %+0.4f %d %d\n",i,j,slope,offset,density);
+
+ if(density > topDensity){
+ topSlope = slope;
+ topOffset = offset;
+ topDensity = density;
+ }
+ }
+ }
+
+ DBG(15,"getEdgeIterate: ok %+0.4f %d %d\n",topSlope,topOffset,topDensity);
+
+ /* did not find anything promising on first pass,
+ * give up instead of fixating on some small, pointless feature */
+ if(pass == 1 && topDensity < width/5){
+ DBG(5,"getEdgeIterate: density too small %d %d\n",topDensity,width);
+ topOffset = 0;
+ topSlope = 0;
+ break;
+ }
+
+ /* if slope can zoom in some more, do so. */
+ if(sStep >= 0.0001){
+ minSlope = topSlope - sStep;
+ maxSlope = topSlope + sStep;
+ go = 1;
+ }
+
+ /* if offset can zoom in some more, do so. */
+ if(oStep){
+ minOffset = topOffset - oStep;
+ maxOffset = topOffset + oStep;
+ go = 1;
+ }
+
+ /* cannot zoom in more, bail out */
+ if(!go){
+ break;
+ }
+
+ DBG(15,"getEdgeIterate: zoom: %+0.4f %+0.4f %d %d\n",
+ minSlope,maxSlope,minOffset,maxOffset);
+ }
+
+ /* topOffset is in the center of the image,
+ * convert to x and y intercept */
+ if(topSlope != 0){
+ *finYInter = topOffset - topSlope * width/2;
+ *finXInter = *finYInter / -topSlope;
+ *finSlope = topSlope;
+ }
+ else{
+ *finYInter = 0;
+ *finXInter = 0;
+ *finSlope = 0;
+ }
+
+ DBG(10,"getEdgeIterate: finish\n");
+
+ return 0;
+}
+
+/* find the left side of paper by moving a line
+ * perpendicular to top slope across the image
+ * the 'left-most' point on the paper is the
+ * one with the smallest X intercept
+ * return x and y intercepts */
+SANE_Status
+getEdgeSlope (int width, int height, int * top, int * bot,
+ double slope, int * finXInter, int * finYInter)
+{
+
+ int i;
+ int topXInter, topYInter;
+ int botXInter, botYInter;
+ int leftCount;
+
+ DBG(10,"getEdgeSlope: start\n");
+
+ topXInter = width;
+ topYInter = 0;
+ leftCount = 0;
+
+ for(i=0;i<width;i++){
+
+ if(top[i] < height){
+ int tyi = top[i] - (slope * i);
+ int txi = tyi/-slope;
+
+ if(topXInter > txi){
+ topXInter = txi;
+ topYInter = tyi;
+ }
+
+ leftCount++;
+ if(leftCount > 5){
+ break;
+ }
+ }
+ else{
+ topXInter = width;
+ topYInter = 0;
+ leftCount = 0;
+ }
+ }
+
+ botXInter = width;
+ botYInter = 0;
+ leftCount = 0;
+
+ for(i=0;i<width;i++){
+
+ if(bot[i] > -1){
+
+ int byi = bot[i] - (slope * i);
+ int bxi = byi/-slope;
+
+ if(botXInter > bxi){
+ botXInter = bxi;
+ botYInter = byi;
+ }
+
+ leftCount++;
+ if(leftCount > 5){
+ break;
+ }
+ }
+ else{
+ botXInter = width;
+ botYInter = 0;
+ leftCount = 0;
+ }
+ }
+
+ if(botXInter < topXInter){
+ *finXInter = botXInter;
+ *finYInter = botYInter;
+ }
+ else{
+ *finXInter = topXInter;
+ *finYInter = topYInter;
+ }
+
+ DBG(10,"getEdgeSlope: finish\n");
+
+ return 0;
+}
+
+/* function to do a simple rotation by a given slope, around
+ * a given point. The point can be outside of image to get
+ * proper edge alignment. Unused areas filled with bg color
+ * FIXME: Do in-place rotation to save memory */
+SANE_Status
+rotateOnCenter (struct scanner *s, int side,
+ int centerX, int centerY, double slope)
+{
+ double slopeRad = -atan(slope);
+ double slopeSin = sin(slopeRad);
+ double slopeCos = cos(slopeRad);
+
+ int bwidth = s->i.Bpl;
+ int pwidth = s->i.width;
+ int height = s->i.height;
+ int depth = 1;
+ int bg_color = s->lut[s->bg_color];
+
+ unsigned char * outbuf;
+ int i, j, k;
+
+ DBG(10,"rotateOnCenter: start: %d %d\n",centerX,centerY);
+
+ outbuf = malloc(s->i.bytes_tot[side]);
+ if(!outbuf){
+ DBG(15,"rotateOnCenter: no outbuf\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ switch (s->i.mode){
+
+ case MODE_COLOR:
+ depth = 3;
+
+ case MODE_GRAYSCALE:
+ memset(outbuf,bg_color,s->i.bytes_tot[side]);
+
+ for (i=0; i<height; i++) {
+ int shiftY = centerY - i;
+
+ for (j=0; j<pwidth; j++) {
+ int shiftX = centerX - j;
+ int sourceX, sourceY;
+
+ sourceX = centerX - (int)(shiftX * slopeCos + shiftY * slopeSin);
+ if (sourceX < 0 || sourceX >= pwidth)
+ continue;
+
+ sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin);
+ if (sourceY < 0 || sourceY >= height)
+ continue;
+
+ for (k=0; k<depth; k++) {
+ outbuf[i*bwidth+j*depth+k]
+ = s->buffers[side][sourceY*bwidth+sourceX*depth+k];
+ }
+ }
+ }
+ break;
+
+ case MODE_LINEART:
+ case MODE_HALFTONE:
+ memset(outbuf,(bg_color<s->threshold)?0xff:0x00,s->i.bytes_tot[side]);
+
+ for (i=0; i<height; i++) {
+ int shiftY = centerY - i;
+
+ for (j=0; j<pwidth; j++) {
+ int shiftX = centerX - j;
+ int sourceX, sourceY;
+
+ sourceX = centerX - (int)(shiftX * slopeCos + shiftY * slopeSin);
+ if (sourceX < 0 || sourceX >= pwidth)
+ continue;
+
+ sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin);
+ if (sourceY < 0 || sourceY >= height)
+ continue;
+
+ /* wipe out old bit */
+ outbuf[i*bwidth + j/8] &= ~(1 << (7-(j%8)));
+
+ /* fill in new bit */
+ outbuf[i*bwidth + j/8] |=
+ ((s->buffers[side][sourceY*bwidth + sourceX/8]
+ >> (7-(sourceX%8))) & 1) << (7-(j%8));
+ }
+ }
+ break;
+ }
+
+ memcpy(s->buffers[side],outbuf,s->i.bytes_tot[side]);
+
+ free(outbuf);
+
+ DBG(10,"rotateOnCenter: finish\n");
+
+ return 0;
+}
+
+/* Function to build a lookup table (LUT), often
+ used by scanners to implement brightness/contrast/gamma
+ or by backends to speed binarization/thresholding
+
+ offset and slope inputs are -127 to +127
+
+ slope rotates line around central input/output val,
+ 0 makes horizontal line
+
+ pos zero neg
+ . x . . x
+ . x . . x
+ out . x .xxxxxxxxxxx . x
+ . x . . x
+ ....x....... ............ .......x....
+ in in in
+
+ offset moves line vertically, and clamps to output range
+ 0 keeps the line crossing the center of the table
+
+ pos zero neg
+ . xxxxxxxx . xx .
+ . x . x .
+ out x . x . x
+ . . x . x
+ ............ xx.......... xxxxxxxx....
+ in in
+
+ out_min/max provide bounds on output values,
+ useful when building thresholding lut.
+ 0 and 255 are good defaults otherwise.
+ */
+static SANE_Status
+load_lut (unsigned char * lut,
+ int in_bits, int out_bits,
+ int out_min, int out_max,
+ int slope, int offset)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j;
+ double shift, rise;
+ int max_in_val = (1 << in_bits) - 1;
+ int max_out_val = (1 << out_bits) - 1;
+ unsigned char * lut_p = lut;
+
+ DBG (10, "load_lut: start %d %d\n", slope, offset);
+
+ /* slope is converted to rise per unit run:
+ * first [-127,127] to [-.999,.999]
+ * then to [-PI/4,PI/4] then [0,PI/2]
+ * then take the tangent (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ rise = tan((double)slope/128 * M_PI_4 + M_PI_4) * max_out_val / max_in_val;
+
+ /* line must stay vertically centered, so figure
+ * out vertical offset at central input value */
+ shift = (double)max_out_val/2 - (rise*max_in_val/2);
+
+ /* convert the user offset setting to scale of output
+ * first [-127,127] to [-1,1]
+ * then to [-max_out_val/2,max_out_val/2]*/
+ shift += (double)offset / 127 * max_out_val / 2;
+
+ for(i=0;i<=max_in_val;i++){
+ j = rise*i + shift;
+
+ if(j<out_min){
+ j=out_min;
+ }
+ else if(j>out_max){
+ j=out_max;
+ }
+
+ *lut_p=j;
+ lut_p++;
+ }
+
+ hexdump(5, "load_lut: ", lut, max_in_val+1);
+
+ DBG (10, "load_lut: finish\n");
+ return ret;
+}
+
diff --git a/backend/canon_dr.conf.in b/backend/canon_dr.conf.in
new file mode 100644
index 0000000..ef1dfdf
--- /dev/null
+++ b/backend/canon_dr.conf.in
@@ -0,0 +1,146 @@
+#######################################################################
+# NOTE: 'option' lines only apply to the devices found by
+# the NEXT 'usb' or 'scsi' line. You may repeat the option line if
+# required for multiple scanners of different models/connections.
+
+#######################################################################
+# Some machines are incapable of providing basic inquiry info, and will
+# lock up if asked for it. The driver will not ask for this info if all
+# three of these options are provided. They should NOT be used unless
+# you know for sure that your machine requires it.
+# NOTE: the vendor and model must be correct. The version need not.
+#option vendor-name CANON
+#option model-name DR-2050C
+#option version-name XXXX
+
+#######################################################################
+# Set data buffer size, in bytes. The value ranges from 4096 - infinity
+# large values may cause timeouts, small causes slow scans. 4MB default
+#option buffer-size 4194304
+
+#######################################################################
+# Most scanners dont pad their reads
+#option padded-read 0
+
+#######################################################################
+# SCSI scanners:
+
+# To search for any CANON scsi device, if name starts with 'CR' or 'DR'
+scsi CANON CR
+scsi CANON DR
+
+# To use a specific scsi device
+#scsi /dev/sg1
+
+#######################################################################
+# USB scanners:
+
+# For Canon scanners connected via USB on a known device (kernel driver):
+#usb /dev/usb/scanner0
+
+# For Canon scanners connected via USB using vendor and device ids (libusb):
+#usb VENDORID PRODUCTID
+
+# NOTE: if you have to add your device here- please send the id and model
+# to the author via email, so it can be included in next version. kitno455 at
+# gmail dot com - with canon_dr in the subject line
+
+# DR-2080C (uses weird protocol)
+option duplex-offset 840
+option vendor-name CANON
+option model-name DR-2080C
+option version-name XXXX
+option padded-read 1
+usb 0x04a9 0x1601
+
+# CR-180
+usb 0x04a9 0x1602
+
+# DR-9080C
+usb 0x04a9 0x1603
+
+# DR-7080C
+usb 0x04a9 0x1604
+
+# DR-5010C
+usb 0x04a9 0x1606
+
+# DR-6080C
+usb 0x04a9 0x1607
+
+# DR-2580C
+option duplex-offset 432
+usb 0x04a9 0x1608
+
+# DR-3080CII
+usb 0x04a9 0x1609
+
+# DR-2050C/SP (uses weird protocol)
+option duplex-offset 840
+option vendor-name CANON
+option model-name DR-2050C
+option version-name XXXX
+option padded-read 1
+usb 0x04a9 0x160a
+
+# DR-7580
+usb 0x04a9 0x160b
+
+# CR-55
+usb 0x1083 0x160c
+
+# DR-1210C (two versions?)
+usb 0x1083 0x160f
+usb 0x04a9 0x2222
+
+# DR-4010C
+usb 0x1083 0x1614
+
+# DR-2510C
+option duplex-offset 400
+usb 0x1083 0x1617
+
+# DR-X10C
+usb 0x1083 0x1618
+
+# CR-25
+usb 0x1083 0x161a
+
+# DR-2010C
+option duplex-offset 400
+usb 0x1083 0x161b
+
+# DR-3010C
+option duplex-offset 400
+usb 0x1083 0x161d
+
+# DR-7090C
+usb 0x1083 0x1620
+
+# DR-9050C
+usb 0x1083 0x1622
+
+# DR-7550C
+usb 0x1083 0x1623
+
+# DR-6050C
+usb 0x1083 0x1624
+
+# DR-6010C
+usb 0x1083 0x1626
+
+# CR-190i
+usb 0x1083 0x162b
+
+# DR-6030C
+usb 0x1083 0x1638
+
+# CR-135i
+usb 0x1083 0x1639
+
+# P-215
+usb 0x1083 0x1646
+
+# P-208
+usb 0x1083 0x164c
+
diff --git a/backend/canon_dr.h b/backend/canon_dr.h
new file mode 100644
index 0000000..1bf95a0
--- /dev/null
+++ b/backend/canon_dr.h
@@ -0,0 +1,587 @@
+#ifndef CANON_DR_H
+#define CANON_DR_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ * Please see opening comments in canon_dr.c
+ */
+
+/* -------------------------------------------------------------------------
+ * This option list has to contain all options for all scanners supported by
+ * this driver. If a certain scanner cannot handle a certain option, there's
+ * still the possibility to say so, later.
+ */
+enum scanner_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_STANDARD_GROUP,
+ OPT_SOURCE, /*fb/adf/front/back/duplex*/
+ OPT_MODE, /*mono/gray/color*/
+ OPT_RES, /*a range or a list*/
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_PAGE_WIDTH,
+ OPT_PAGE_HEIGHT,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+ OPT_RIF,
+
+ OPT_ADVANCED_GROUP,
+ OPT_COMPRESS,
+ OPT_COMPRESS_ARG,
+ OPT_DF_THICKNESS,
+ OPT_DF_LENGTH,
+ OPT_ROLLERDESKEW,
+ OPT_SWDESKEW,
+ OPT_SWDESPECK,
+ OPT_SWCROP,
+ OPT_STAPLEDETECT,
+ OPT_DROPOUT_COLOR_F,
+ OPT_DROPOUT_COLOR_B,
+ OPT_BUFFERMODE,
+ OPT_SIDE,
+
+ /*sensor group*/
+ OPT_SENSOR_GROUP,
+ OPT_START,
+ OPT_STOP,
+ OPT_BUTT3,
+ OPT_NEWFILE,
+ OPT_COUNTONLY,
+ OPT_BYPASSMODE,
+ OPT_COUNTER,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+struct img_params
+{
+ int mode; /*color,lineart,etc*/
+ int source; /*fb,adf front,adf duplex,etc*/
+
+ int dpi_x; /*these are in dpi */
+ int dpi_y;
+
+ int tl_x; /*these are in 1200dpi units */
+ int tl_y;
+ int br_x;
+ int br_y;
+ int page_x;
+ int page_y;
+
+ int width; /*these are in pixels*/
+ int height;
+
+ SANE_Frame format; /*SANE_FRAME_**/
+ int bpp; /* 1,8,24 */
+ int Bpl; /* in bytes */
+
+ int valid_width; /*some machines have black padding*/
+ int valid_Bpl;
+
+ /* done yet? */
+ int eof[2];
+
+ /* how far we have read/written */
+ int bytes_sent[2];
+
+ /* total to read/write */
+ int bytes_tot[2];
+
+ /* dumb scanners send extra data */
+ int skip_lines[2];
+
+};
+
+struct scanner
+{
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during init of scanner. */
+ struct scanner *next;
+ char device_name[1024]; /* The name of the device from sanei */
+ int missing; /* used to mark unplugged scanners */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during reading of config file. */
+ int buffer_size;
+ int connection; /* hardware interface type */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during inquiry probing of the scanner. */
+ /* members in order found in scsi data... */
+ char vendor_name[9]; /* raw data as returned by SCSI inquiry. */
+ char model_name[17]; /* raw data as returned by SCSI inquiry. */
+ char version_name[5]; /* raw data as returned by SCSI inquiry. */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during std VPD probing of the scanner. */
+ /* members in order found in scsi data... */
+ int basic_x_res;
+ int basic_y_res;
+ int step_x_res;
+ int step_y_res;
+ int max_x_res;
+ int max_y_res;
+ int min_x_res;
+ int min_y_res;
+
+ int std_res_x[16];
+ int std_res_y[16];
+
+ /* max scan size in pixels converted to 1200dpi units */
+ int max_x;
+ int max_y;
+
+ /*FIXME: 4 more unknown values here*/
+ int can_grayscale;
+ int can_halftone;
+ int can_monochrome;
+ int can_overflow;
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are hard coded because they are not in vpd */
+
+ int brightness_steps;
+ int threshold_steps;
+ int contrast_steps;
+ int ppl_mod; /* modulus of scanline width */
+
+ /* the scan size in 1/1200th inches, NOT basic_units or sane units */
+ int min_x;
+ int min_y;
+ int valid_x;
+ int max_x_fb;
+ int max_y_fb;
+
+ int can_color; /* actually might be in vpd, but which bit? */
+ int need_ccal; /* scanner needs software to help with afe calibration */
+ int need_fcal; /* scanner needs software to help with fine calibration */
+ int need_fcal_buffer; /* software to apply calibration stored in scanner*/
+ int ccal_version; /* 0 in most scanners, 3 in newer ones */
+
+ int has_counter;
+ int has_rif;
+ int has_adf;
+ int has_flatbed;
+ int has_duplex;
+ int has_back; /* not all duplex scanners can do adf back side only */
+ int has_comp_JPEG;
+ int has_buffer;
+ int has_df;
+ int has_btc;
+ int has_ssm; /* older scanners use this set scan mode command */
+ int has_ssm2; /* newer scanners user this similar command */
+ int has_ssm_pay_head_len; /* newer scanners put the length twice in ssm */
+ int can_read_panel;
+ int can_write_panel;
+ int rgb_format; /* meaning unknown */
+ int padding; /* meaning unknown */
+
+ int always_op; /* send object pos between pages */
+ int invert_tly; /* weird bug in some smaller scanners */
+ int unknown_byte2; /* weird byte, required, meaning unknown */
+ int padded_read; /* some machines need extra 12 bytes on reads */
+ int fixed_width; /* some machines always scan full width */
+ int even_Bpl; /* some machines require even bytes per line */
+
+ int gray_interlace[2]; /* different models interlace heads differently */
+ int color_interlace[2]; /* different models interlace colors differently */
+ int duplex_interlace; /* different models interlace sides differently */
+ int jpeg_interlace; /* different models interlace jpeg sides differently */
+ int duplex_offset; /* number of lines of padding added to front (1/1200)*/
+ int duplex_offset_side; /* padding added to front or back? */
+
+ int sw_lut; /* no hardware brightness/contrast support */
+ int bg_color; /* needed to fill in after rotation */
+
+ int reverse_by_mode[6]; /* mode specific */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during serial number probing scanner */
+ char serial_name[28]; /* 16 char model, ':', 10 byte serial, null */
+
+ /* --------------------------------------------------------------------- */
+ /* struct with pointers to device/vendor/model names, and a type value */
+ /* used to inform sane frontend about the device */
+ SANE_Device sane;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable SANE_Option structs provide our interface to frontend. */
+ /* some options require lists of strings or numbers, we keep them here */
+ /* instead of in global vars so that they can differ for each scanner */
+
+ /* long array of option structs */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+ /*mode group*/
+ SANE_String_Const mode_list[7];
+ SANE_String_Const source_list[5];
+
+ SANE_Int res_list[17];
+ SANE_Range res_range;
+
+ /*geometry group*/
+ SANE_Range tl_x_range;
+ SANE_Range tl_y_range;
+ SANE_Range br_x_range;
+ SANE_Range br_y_range;
+ SANE_Range paper_x_range;
+ SANE_Range paper_y_range;
+
+ /*enhancement group*/
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+ SANE_Range threshold_range;
+
+ /*advanced group*/
+ SANE_String_Const compress_list[3];
+ SANE_Range compress_arg_range;
+ SANE_Range swdespeck_range;
+ SANE_String_Const do_color_list[8];
+
+ /*sensor group*/
+ SANE_Range counter_range;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable vars to hold user input. modified by SANE_Options above */
+
+ /* the user's requested image params */
+ /* exposed in standard and geometry option groups */
+ struct img_params u;
+
+ /*enhancement group*/
+ int brightness;
+ int contrast;
+ int threshold;
+ int rif;
+
+ /*advanced group*/
+ int compress;
+ int compress_arg;
+ int df_length;
+ int df_thickness;
+ int dropout_color_f;
+ int dropout_color_b;
+ int buffermode;
+ int rollerdeskew;
+ int swdeskew;
+ int swdespeck;
+ int swcrop;
+ int stapledetect;
+
+ /* --------------------------------------------------------------------- */
+ /* values which are derived from setting the options above */
+ /* the user never directly modifies these */
+
+ /* the scanner image params (what we ask from scanner) */
+ struct img_params s;
+
+ /* the intermediate image params (like user, but possible higher depth) */
+ struct img_params i;
+
+ /* the brightness/contrast LUT for dumb scanners */
+ unsigned char lut[256];
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by calibration functions */
+ int c_res;
+ int c_mode;
+
+ int c_offset[2];
+ int c_gain[2];
+ int c_exposure[2][3];
+
+ int f_res;
+ int f_mode;
+
+ unsigned char * f_offset[2];
+ unsigned char * f_gain[2];
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by scanning functions to keep track of pages, etc */
+ int started;
+ int reading;
+ int cancelled;
+ int side;
+ int prev_page;
+ int jpeg_stage;
+ int jpeg_ff_offset;
+
+ unsigned char * buffers[2];
+
+ /* --------------------------------------------------------------------- */
+ /* values used by the command and data sending functions (scsi/usb) */
+ int fd; /* The scanner device file descriptor. */
+ size_t rs_info;
+
+ /* --------------------------------------------------------------------- */
+ /* values used to hold hardware or control panel status */
+
+ int panel_start;
+ int panel_stop;
+ int panel_butt3;
+ int panel_new_file;
+ int panel_count_only;
+ int panel_bypass_mode;
+ int panel_enable_led;
+ int panel_counter;
+
+ /* values which are used to track the frontend's access to sensors */
+ char hw_read[NUM_OPTIONS-OPT_START];
+};
+
+#define CONNECTION_SCSI 0 /* SCSI interface */
+#define CONNECTION_USB 1 /* USB interface */
+
+#define SIDE_FRONT 0
+#define SIDE_BACK 1
+
+#define CHAN_RED 0
+#define CHAN_GREEN 1
+#define CHAN_BLUE 2
+
+#define SOURCE_FLATBED 0
+#define SOURCE_ADF_FRONT 1
+#define SOURCE_ADF_BACK 2
+#define SOURCE_ADF_DUPLEX 3
+
+static const int dpi_list[] = {
+60,75,100,120,150,160,180,200,
+240,300,320,400,480,600,800,1200
+};
+
+#define DPI_60 0
+#define DPI_75 1
+#define DPI_100 2
+#define DPI_120 3
+#define DPI_150 4
+#define DPI_160 5
+#define DPI_180 6
+#define DPI_200 7
+#define DPI_240 8
+#define DPI_300 9
+#define DPI_320 10
+#define DPI_400 11
+#define DPI_480 12
+#define DPI_600 13
+#define DPI_800 14
+#define DPI_1200 15
+
+#define COMP_NONE WD_cmp_NONE
+#define COMP_JPEG WD_cmp_JPEG
+
+#define JPEG_STAGE_NONE 0
+#define JPEG_STAGE_SOF 1
+
+/* these are same as scsi data to make code easier */
+#define MODE_LINEART WD_comp_LA
+#define MODE_HALFTONE WD_comp_HT
+#define MODE_GRAYSCALE WD_comp_GS
+#define MODE_COLOR WD_comp_CG
+
+enum {
+ COLOR_NONE = 0,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BLUE,
+ COLOR_EN_RED,
+ COLOR_EN_GREEN,
+ COLOR_EN_BLUE
+};
+
+/* these are same as scsi data to make code easier */
+#define COLOR_WHITE 1
+#define COLOR_BLACK 2
+
+#define GRAY_INTERLACE_NONE 0
+#define GRAY_INTERLACE_2510 1
+#define GRAY_INTERLACE_gG 2
+
+#define COLOR_INTERLACE_RGB 0
+#define COLOR_INTERLACE_BGR 1
+#define COLOR_INTERLACE_RRGGBB 2
+#define COLOR_INTERLACE_rRgGbB 3
+#define COLOR_INTERLACE_2510 4
+
+#define DUPLEX_INTERLACE_NONE 0
+#define DUPLEX_INTERLACE_FFBB 1
+#define DUPLEX_INTERLACE_FBFB 2
+#define DUPLEX_INTERLACE_2510 3
+
+#define JPEG_INTERLACE_ALT 0
+#define JPEG_INTERLACE_NONE 1
+
+#define CROP_RELATIVE 0
+#define CROP_ABSOLUTE 1
+
+/* ------------------------------------------------------------------------- */
+
+#define MM_PER_UNIT_UNFIX SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))
+#define MM_PER_UNIT_FIX SANE_FIX(SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)))
+
+#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX((number) * MM_PER_UNIT_UNFIX)
+#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) / MM_PER_UNIT_UNFIX
+
+#define CANON_DR_CONFIG_FILE "canon_dr.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle);
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking);
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp);
+
+const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option);
+
+SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+
+SANE_Status sane_start (SANE_Handle handle);
+
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params);
+
+SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len);
+
+void sane_cancel (SANE_Handle h);
+
+void sane_close (SANE_Handle h);
+
+void sane_exit (void);
+
+/* ------------------------------------------------------------------------- */
+
+static SANE_Status attach_one_scsi (const char *name);
+static SANE_Status attach_one_usb (const char *name);
+static SANE_Status attach_one (const char *devicename, int connType);
+
+static SANE_Status connect_fd (struct scanner *s);
+static SANE_Status disconnect_fd (struct scanner *s);
+
+static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg);
+
+static SANE_Status init_inquire (struct scanner *s);
+static SANE_Status init_vpd (struct scanner *s);
+static SANE_Status init_model (struct scanner *s);
+static SANE_Status init_panel (struct scanner *s);
+static SANE_Status init_user (struct scanner *s);
+static SANE_Status init_options (struct scanner *s);
+
+static SANE_Status
+do_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status
+do_scsi_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status
+do_usb_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status do_usb_clear(struct scanner *s, int clear, int runRS);
+
+static SANE_Status wait_scanner (struct scanner *s);
+
+static SANE_Status object_position (struct scanner *s, int i_load);
+
+static SANE_Status ssm_buffer (struct scanner *s);
+static SANE_Status ssm_do (struct scanner *s);
+static SANE_Status ssm_df (struct scanner *s);
+
+static int get_page_width (struct scanner *s);
+static int get_page_height (struct scanner *s);
+
+static SANE_Status set_window (struct scanner *s);
+static SANE_Status update_params (struct scanner *s, int calib);
+static SANE_Status update_i_params (struct scanner *s);
+static SANE_Status clean_params (struct scanner *s);
+
+static SANE_Status read_panel(struct scanner *s, SANE_Int option);
+static SANE_Status send_panel(struct scanner *s);
+
+static SANE_Status start_scan (struct scanner *s, int type);
+
+static SANE_Status check_for_cancel(struct scanner *s);
+
+static SANE_Status read_from_scanner(struct scanner *s, int side, int exact);
+static SANE_Status read_from_scanner_duplex(struct scanner *s, int exact);
+
+static SANE_Status copy_simplex(struct scanner *s, unsigned char * buf, int len, int side);
+static SANE_Status copy_duplex(struct scanner *s, unsigned char * buf, int len);
+static SANE_Status copy_line(struct scanner *s, unsigned char * buf, int side);
+
+static SANE_Status buffer_despeck(struct scanner *s, int side);
+static SANE_Status buffer_deskew(struct scanner *s, int side);
+static SANE_Status buffer_crop(struct scanner *s, int side);
+
+int * getTransitionsY (struct scanner *s, int side, int top);
+int * getTransitionsX (struct scanner *s, int side, int top);
+
+SANE_Status getEdgeIterate (int width, int height, int resolution,
+ int * buff, double * finSlope, int * finXInter, int * finYInter);
+
+SANE_Status getEdgeSlope (int width, int height, int * top, int * bot,
+ double slope, int * finXInter, int * finYInter);
+
+SANE_Status rotateOnCenter (struct scanner *s, int side,
+ int centerX, int centerY, double slope);
+
+static SANE_Status getLine (int height, int width, int * buff,
+ int slopes, double minSlope, double maxSlope,
+ int offsets, int minOffset, int maxOffset,
+ double * finSlope, int * finOffset, int * finDensity);
+
+static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits,
+ int out_min, int out_max, int slope, int offset);
+
+static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side);
+
+static SANE_Status image_buffers (struct scanner *s, int setup);
+static SANE_Status offset_buffers (struct scanner *s, int setup);
+static SANE_Status gain_buffers (struct scanner *s, int setup);
+
+static SANE_Status calibrate_AFE(struct scanner *s);
+static SANE_Status calibrate_fine(struct scanner *s);
+static SANE_Status calibrate_fine_buffer(struct scanner *s);
+
+static SANE_Status write_AFE (struct scanner *s);
+static SANE_Status calibration_scan (struct scanner *s, int);
+
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+static void default_globals (void);
+
+static size_t maxStringSize (const SANE_String_Const strings[]);
+
+#endif /* CANON_DR_H */
diff --git a/backend/canon_pp-dev.c b/backend/canon_pp-dev.c
new file mode 100644
index 0000000..a357cf0
--- /dev/null
+++ b/backend/canon_pp-dev.c
@@ -0,0 +1,1368 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ -----
+
+ This file is part of the canon_pp backend, supporting Canon CanoScan
+ Parallel scanners and also distributed as part of the stand-alone driver.
+
+ canon_pp-dev.c: $Revision$
+
+ Misc constants for Canon CanoScan Parallel scanners and high-level scan
+ functions.
+
+ Simon Krix <kinsei@users.sourceforge.net>
+ */
+
+#ifdef _AIX
+#include <lalloca.h>
+#endif
+
+#ifndef NOSANE
+#include "../include/sane/config.h"
+#endif
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ieee1284.h>
+#include "canon_pp-io.h"
+#include "canon_pp-dev.h"
+
+#ifdef NOSANE
+
+/* No SANE, Things that only apply to stand-alone */
+#include <stdio.h>
+#include <stdarg.h>
+
+static void DBG(int level, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ if (level < 50) vfprintf(stderr, format, args);
+ va_end(args);
+}
+#else
+
+/* Definitions which only apply to SANE compiles */
+#ifndef VERSION
+#define VERSION "$Revision$"
+#endif
+
+#define DEBUG_DECLARE_ONLY
+#include "canon_pp.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+#endif
+
+struct scanner_hardware_desc {
+ char *name;
+ unsigned int natural_xresolution;
+ unsigned int natural_yresolution;
+ unsigned int scanbedlength;
+ unsigned int scanheadwidth; /* 0 means provided by scanner */
+ unsigned int type;
+};
+
+static const struct scanner_hardware_desc
+ /* The known scanner types */
+ hw_fb320p = { "FB320P", 2, 2, 3508, 2552, 0 },
+ hw_fb330p = { "FB330P", 2, 2, 3508, 0, 1 },
+ hw_fb620p = { "FB620P", 3, 3, 7016, 5104, 0 },
+ hw_fb630p = { "FB630P", 3, 3, 7016, 0, 1 },
+ hw_n640p = { "N640P", 3, 3, 7016, 0, 1 },
+ hw_n340p = { "N340P", 2, 2, 3508, 0, 1 },
+
+ /* A few generic scanner descriptions for aliens */
+ hw_alien600 = { "Unknown 600dpi", 3, 3, 7016, 0, 1 },
+ hw_alien300 = { "Unknown 300dpi", 2, 2, 3508, 0, 1 },
+ hw_alien = { "Unknown (600dpi?)", 3, 3, 7016, 0, 1 };
+
+/* ID table linking ID strings with hardware descriptions */
+struct scanner_id {
+ char *id;
+ const struct scanner_hardware_desc *hw;
+};
+static const struct scanner_id scanner_id_table[] = {
+ { "CANON IX-03055C", &hw_fb320p },
+ { "CANON IX-06025C", &hw_fb620p },
+ { "CANON IX-03075E", &hw_fb330p },
+ { "CANON IX-06075E", &hw_fb630p },
+ { "CANON IX-03095G", &hw_n340p },
+ { "CANON IX-06115G", &hw_n640p },
+ { NULL, NULL } };
+
+/*const int scanline_count = 6;*/
+static const char *header = "#CANONPP";
+static const int fileversion = 3;
+
+/* Internal functions */
+static unsigned long column_sum(image_segment *image, int x);
+static int adjust_output(image_segment *image, scan_parameters *scanp,
+ scanner_parameters *scannerp);
+static int check8(unsigned char *p, int s);
+/* Converts from weird scanner format -> sequential data */
+static void convdata(unsigned char *srcbuffer, unsigned char *dstbuffer,
+ int width, int mode);
+/* Sets up the scan command. This could use a better name
+ (and a rewrite). */
+static int scanner_setup_params(unsigned char *buf, scanner_parameters *sp,
+ scan_parameters *scanp);
+
+/* file reading and writing helpers */
+static int safe_write(int fd, const char *p, unsigned long len);
+static int safe_read(int fd, char *p, unsigned long len);
+
+/* Command sending loop (waiting for ready status) */
+static int send_command(struct parport *port, unsigned char *buf, int bufsize,
+ int delay, int timeout);
+
+/* Commands ================================================ */
+
+/* Command_1[] moved to canon_pp-io.c for neatness */
+
+
+/* Read device ID command */
+/* after this 0x26 (38) bytes are read */
+static unsigned char cmd_readid[] = { 0xfe, 0x20, 0, 0, 0, 0, 0, 0, 0x26, 0 };
+
+/* Reads 12 bytes of unknown information */
+static unsigned char cmd_readinfo[] = { 0xf3, 0x20, 0, 0, 0, 0, 0, 0, 0x0c, 0 };
+
+/* Scan init command: Always followed immediately by command cmd_scan */
+static unsigned char cmd_initscan[] = { 0xde, 0x20, 0, 0, 0, 0, 0, 0, 0x2e, 0 };
+
+/* Scan information block */
+static unsigned char cmd_scan[45] =
+{ 0x11, 0x2c, 0x11, 0x2c, 0x10, 0x4b, 0x10, 0x4b, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x08, 0x08, 0x01, 0x01, 0x80, 0x01,
+ 0x80, 0x80, 0x02, 0, 0, 0xc1, 0, 0x08, 0x01, 0x01,
+ 0, 0, 0, 0, 0
+};
+
+/* Read 6 byte buffer status block */
+static unsigned char cmd_buf_status[] = { 0xf3, 0x21, 0, 0, 0, 0, 0, 0, 0x06, 0 };
+
+/* Request a block of image data */
+static unsigned char cmd_packet_req[] = {0xd4, 0x20, 0, 0, 0, 0, 0, 0x09, 0x64, 0};
+
+/* "*SCANEND" command - returns the scanner to transparent mode */
+static unsigned char cmd_scanend[] =
+{ 0x1b, 0x2a, 0x53, 0x43, 0x41, 0x4e, 0x45, 0x4e, 0x44, 0x0d };
+
+/* Reads BLACK calibration image */
+static unsigned char cmd_calblack[] ={0xf8, 0x20, 0, 0, 0, 0, 0, 0x4a, 0xc4, 0};
+
+/* Clear the existing gamma table and create a new one */
+static unsigned char cmd_cleargamma[] = {0xc5, 0x20, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/* Read back the gamma table values */
+static unsigned char cmd_readgamma[] = {0xf6, 0x20, 0, 0, 0, 0, 0, 0, 0x20, 0};
+
+/* Reads COLOUR (R,G or B) calibration image */
+static unsigned char cmd_calcolour[]={0xf9, 0x20, 0, 0, 0, 0, 0, 0x4a, 0xc4, 0};
+
+/* Abort scan */
+static unsigned char cmd_abort[] = {0xef, 0x20, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/* Upload the gamma table (followed by 32 byte write) */
+static unsigned char cmd_setgamma[] = {0xe6, 0x20, 0, 0, 0, 0, 0, 0, 0x20, 0};
+
+#if 0
+/* Something about RGB gamma/gain values? Not currently used by this code */
+static unsigned char command_14[32] =
+{ 0x2, 0x0, 0x3, 0x7f,
+ 0x2, 0x0, 0x3, 0x7f,
+ 0x2, 0x0, 0x3, 0x7f,
+ 0, 0, 0, 0,
+ 0x12, 0xd1, 0x14, 0x82,
+ 0, 0, 0, 0,
+ 0x0f, 0xff,
+ 0x0f, 0xff,
+ 0x0f, 0xff, 0, 0 };
+#endif
+
+
+/* Misc functions =================================== */
+
+/*
+ * safe_write(): a small wrapper which ensures all the data is written in calls
+ * to write(), since the POSIX call doesn't ensure it.
+ */
+static int safe_write(int fd, const char *p, unsigned long len) {
+ int diff;
+ unsigned long total = 0;
+
+ do {
+ diff = write(fd, p+total, len-total);
+ if (diff < 0)
+ {
+ if (errno == EINTR) continue;
+ return -1;
+ }
+ total += diff;
+ } while (len > total);
+
+ return 0;
+
+}
+
+/* same dealie for read, except in the case of read the return of 0 bytes with
+ * no INTR error indicates EOF */
+static int safe_read(int fd, char *p, unsigned long len) {
+ int diff;
+ unsigned long total = 0;
+
+ do {
+ diff = read(fd, p+total, len-total);
+ if (diff <= 0)
+ {
+ if (errno == EINTR) continue;
+ if (diff == 0) return -2;
+ return -1;
+
+ }
+ total += diff;
+ } while (len > total);
+
+ return 0;
+
+}
+
+
+/* Scan-related functions =================================== */
+
+int sanei_canon_pp_init_scan(scanner_parameters *sp, scan_parameters *scanp)
+{
+ /* Command for: Initialise and begin the scan procedure */
+ unsigned char command_b[56];
+
+ /* Buffer for buffer info block */
+ unsigned char buffer_info_block[6];
+
+ /* The image size the scanner says we asked for
+ (based on the scanner's replies) */
+ int true_scanline_size, true_scanline_count;
+
+ /* The image size we expect to get (based on *scanp) */
+ int expected_scanline_size, expected_scanline_count;
+
+ /* Set up the default scan command packet */
+ memcpy(command_b, cmd_initscan, 10);
+ memcpy(command_b+10, cmd_scan, 45);
+
+ /* Load the proper settings into it */
+ scanner_setup_params(command_b+10, sp, scanp);
+
+ /* Add checksum byte */
+ command_b[55] = check8(command_b+10, 45);
+
+ if (send_command(sp->port, command_b, 56, 50000, 1000000))
+ return -1;
+
+ /* Ask the scanner about the buffer */
+ if (send_command(sp->port, cmd_buf_status, 10, 50000, 1000000))
+ return -1;
+
+ /* Read buffer information block */
+ sanei_canon_pp_read(sp->port, 6, buffer_info_block);
+
+ if (check8(buffer_info_block, 6))
+ DBG(1, "init_scan: ** Warning: Checksum error reading buffer "
+ "info block.\n");
+
+ expected_scanline_count = scanp->height;
+
+ switch(scanp->mode)
+ {
+ case 0: /* greyscale; 10 bits per pixel */
+ expected_scanline_size = scanp->width * 1.25; break;
+ case 1: /* true-colour; 30 bits per pixel */
+ expected_scanline_size = scanp->width * 3.75; break;
+ default:
+ DBG(1, "init_scan: Illegal mode %i requested in "
+ "init_scan().\n", scanp->mode);
+ DBG(1, "This is a bug. Please report it.\n");
+ return -1;
+ }
+
+ /* The scanner's idea of the length of each scanline in bytes */
+ true_scanline_size = (buffer_info_block[0]<<8) | buffer_info_block[1];
+ /* The scanner's idea of the number of scanlines in total */
+ true_scanline_count = (buffer_info_block[2]<<8) | buffer_info_block[3];
+
+ if ((expected_scanline_size != true_scanline_size)
+ || (expected_scanline_count != true_scanline_count))
+ {
+ DBG(10, "init_scan: Warning: Scanner is producing an image "
+ "of unexpected size:\n");
+ DBG(10, "expected: %i bytes wide, %i scanlines tall.\n",
+ expected_scanline_size,
+ expected_scanline_count);
+ DBG(10, "true: %i bytes wide, %i scanlines tall.\n",
+ true_scanline_size, true_scanline_count);
+
+ if (scanp->mode == 0)
+ scanp->width = true_scanline_size / 1.25;
+ else
+ scanp->width = true_scanline_size / 3.75;
+
+ scanp->height = true_scanline_count;
+ }
+ return 0;
+}
+
+
+/* Wake the scanner, detect it, and fill sp with stuff */
+int sanei_canon_pp_initialise(scanner_parameters *sp, int mode)
+{
+ unsigned char scanner_info[12];
+ const struct scanner_id *cur_id;
+ const struct scanner_hardware_desc *hw;
+
+ /* Hopefully take the scanner out of transparent mode */
+ if (sanei_canon_pp_wake_scanner(sp->port, mode))
+ {
+ DBG(10, "initialise: could not wake scanner\n");
+ return 1;
+ }
+
+ /* This block of code does something unknown but necessary */
+ DBG(50, "initialise: >> scanner_init\n");
+ if (sanei_canon_pp_scanner_init(sp->port))
+ {
+ /* If we're using an unsupported ieee1284 mode here, this is
+ * where it will fail, so fall back to nibble. */
+ sanei_canon_pp_set_ieee1284_mode(M1284_NIBBLE);
+ if (sanei_canon_pp_scanner_init(sp->port))
+ {
+ DBG(10, "initialise: Could not init scanner.\n");
+ return 1;
+ }
+ }
+ DBG(50, "initialise: << scanner_init\n");
+
+ /* Read Device ID */
+ memset(sp->id_string, 0, sizeof sp->id_string);
+ if (send_command(sp->port, cmd_readid, 10, 10000, 100000))
+ return -1;
+ sanei_canon_pp_read(sp->port, 38, (unsigned char *)(sp->id_string));
+
+ /* Read partially unknown data */
+ if (send_command(sp->port, cmd_readinfo, 10, 10000, 100000))
+ return -1;
+ sanei_canon_pp_read(sp->port, 12, scanner_info);
+
+ if (check8(scanner_info, 12))
+ {
+ DBG(10, "initialise: Checksum error reading Info Block.\n");
+ return 2;
+ }
+
+ sp->scanheadwidth = (scanner_info[2] << 8) | scanner_info[3];
+
+ /* Set up various known values */
+ cur_id = scanner_id_table;
+ while (cur_id->id)
+ {
+ if (!strncmp(sp->id_string+8, cur_id->id, strlen(cur_id->id)))
+ break;
+ cur_id++;
+ }
+
+ if (cur_id->id)
+ {
+ hw = cur_id->hw;
+ }
+ else if (sp->scanheadwidth == 5104)
+ {
+ /* Guess 600dpi scanner */
+ hw = &hw_alien600;
+ }
+ else if (sp->scanheadwidth == 2552)
+ {
+ /* Guess 300dpi scanner */
+ hw = &hw_alien300;
+ }
+ else
+ {
+ /* Guinea Pigs :) */
+ hw = &hw_alien;
+ }
+
+ strcpy(sp->name, hw->name);
+ sp->natural_xresolution = hw->natural_xresolution;
+ sp->natural_yresolution = hw->natural_yresolution;
+ sp->scanbedlength = hw->scanbedlength;
+ if (hw->scanheadwidth)
+ sp->scanheadwidth = hw->scanheadwidth;
+ sp->type = hw->type;
+
+ return 0;
+}
+
+/* Shut scanner down */
+int sanei_canon_pp_close_scanner(scanner_parameters *sp)
+{
+ /* Put scanner in transparent mode */
+ sanei_canon_pp_sleep_scanner(sp->port);
+
+ /* Free memory (with purchase of memory of equal or greater value) */
+ if (sp->blackweight != NULL)
+ {
+ free(sp->blackweight);
+ sp->blackweight = NULL;
+ }
+ if (sp->redweight != NULL)
+ {
+ free(sp->redweight);
+ sp->redweight = NULL;
+ }
+ if (sp->greenweight != NULL)
+ {
+ free(sp->greenweight);
+ sp->greenweight = NULL;
+ }
+ if (sp->blueweight != NULL)
+ {
+ free(sp->blueweight);
+ sp->blueweight = NULL;
+ }
+
+ return 0;
+}
+
+/* Read the calibration information from file */
+int sanei_canon_pp_load_weights(const char *filename, scanner_parameters *sp)
+{
+ int fd;
+ int cal_data_size = sp->scanheadwidth * sizeof(unsigned long);
+ int cal_file_size;
+
+ char buffer[10];
+ int temp, ret;
+
+ /* Open file */
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return -1;
+
+ /* Read header and check it's right */
+ ret = safe_read(fd, buffer, strlen(header) + 1);
+ if ((ret < 0) || strcmp(buffer, header) != 0)
+ {
+ DBG(1,"Calibration file header is wrong, recalibrate please\n");
+ close(fd);
+ return -2;
+ }
+
+ /* Read and check file version (the calibrate file
+ format changes from time to time) */
+ ret = safe_read(fd, (char *)&temp, sizeof(int));
+
+ if ((ret < 0) || (temp != fileversion))
+ {
+ DBG(1,"Calibration file is wrong version, recalibrate please\n");
+ close(fd);
+ return -3;
+ }
+
+ /* Allocate memory for calibration values */
+ if (((sp->blueweight = malloc(cal_data_size)) == NULL)
+ || ((sp->redweight = malloc(cal_data_size)) == NULL)
+ || ((sp->greenweight = malloc(cal_data_size)) == NULL)
+ || ((sp->blackweight = malloc(cal_data_size)) == NULL))
+ return -4;
+
+ /* Read width of calibration data */
+ ret = safe_read(fd, (char *)&cal_file_size, sizeof(cal_file_size));
+
+ if ((ret < 0) || (cal_file_size != sp->scanheadwidth))
+ {
+ DBG(1, "Calibration doesn't match scanner, recalibrate?\n");
+ close(fd);
+ return -5;
+ }
+
+ /* Read calibration data */
+ if (safe_read(fd, (char *)(sp->blackweight), cal_data_size) < 0)
+ {
+ DBG(1, "Error reading black calibration data, recalibrate?\n");
+ close(fd);
+ return -6;
+ }
+
+ if (safe_read(fd, (char *)sp->redweight, cal_data_size) < 0)
+ {
+ DBG(1, "Error reading red calibration data, recalibrate?\n");
+ close(fd);
+ return -7;
+ }
+
+ if (safe_read(fd, (char *)sp->greenweight, cal_data_size) < 0)
+ {
+ DBG(1, "Error reading green calibration data, recalibrate?\n");
+ close(fd);
+ return -8;
+ }
+
+ if (safe_read(fd, (char *)sp->blueweight, cal_data_size) < 0)
+ {
+ DBG(1, "Error reading blue calibration data, recalibrate?\n");
+ close(fd);
+ return -9;
+ }
+
+ /* Read white-balance/gamma data */
+
+ if (safe_read(fd, (char *)&(sp->gamma), 32) < 0)
+ {
+ close(fd);
+ return -10;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+/* Mode is 0 for greyscale source data or 1 for RGB */
+static void convert_to_rgb(image_segment *dest, unsigned char *src,
+ int width, int scanlines, int mode)
+{
+ int curline;
+
+ const int colour_size = width * 1.25;
+ const int scanline_size = (mode == 0 ? colour_size : colour_size * 3);
+
+ for (curline = 0; curline < scanlines; curline++)
+ {
+
+ if (mode == 0) /* Grey */
+ {
+ convdata(src + (curline * scanline_size),
+ dest->image_data +
+ (curline * width * 2), width, 1);
+ }
+ else if (mode == 1) /* Truecolour */
+ {
+ /* Red */
+ convdata(src + (curline * scanline_size),
+ dest->image_data +
+ (curline * width *3*2) + 4, width, 2);
+ /* Green */
+ convdata(src + (curline * scanline_size) + colour_size,
+ dest->image_data +
+ (curline * width *3*2) + 2, width, 2);
+ /* Blue */
+ convdata(src + (curline * scanline_size) +
+ (2 * colour_size), dest->image_data +
+ (curline * width *3*2), width, 2);
+ }
+
+ } /* End of scanline loop */
+
+}
+
+int sanei_canon_pp_read_segment(image_segment **dest, scanner_parameters *sp,
+ scan_parameters *scanp, int scanline_number, int do_adjust,
+ int scanlines_left)
+{
+ unsigned char *input_buffer = NULL;
+ image_segment *output_image = NULL;
+
+ unsigned char packet_header[4];
+ unsigned char packet_req_command[10];
+
+ int read_data_size;
+ int scanline_size;
+
+ if (scanp->mode == 1) /* RGB */
+ scanline_size = scanp->width * 3.75;
+ else /* Greyscale */
+ scanline_size = scanp->width * 1.25;
+
+ read_data_size = scanline_size * scanline_number;
+
+ /* Allocate output_image struct */
+ if ((output_image = malloc(sizeof(*output_image))) == NULL)
+ {
+ DBG(1, "read_segment: Error: Not enough memory for scanner "
+ "input buffer\n");
+ goto error_out;
+ }
+
+ /* Allocate memory for input buffer */
+ if ((input_buffer = malloc(scanline_size * scanline_number)) == NULL)
+ {
+ DBG(1, "read_segment: Error: Not enough memory for scanner "
+ "input buffer\n");
+ goto error_out;
+ }
+
+ output_image->width = scanp->width;
+ output_image->height = scanline_number;
+
+ /* Allocate memory for dest image segment */
+
+ output_image->image_data =
+ malloc(output_image->width * output_image->height *
+ (scanp->mode ? 3 : 1) * 2);
+
+ if (output_image->image_data == NULL)
+ {
+ DBG(1, "read_segment: Error: Not enough memory for "
+ "image data\n");
+ goto error_out;
+ }
+
+ /* Set up packet request command */
+ memcpy(packet_req_command, cmd_packet_req, 10);
+ packet_req_command[7] = ((read_data_size + 4) & 0xFF00) >> 8;
+ packet_req_command[8] = (read_data_size + 4) & 0xFF;
+
+ /* Send packet req. and wait for the scanner's READY signal */
+ if (send_command(sp->port, packet_req_command, 10, 9000, 2000000))
+ {
+ DBG(1, "read_segment: Error: didn't get response within 2s "
+ "of sending request");
+ goto error_out;
+ }
+
+ /* Read packet header */
+ if (sanei_canon_pp_read(sp->port, 4, packet_header))
+ {
+ DBG(1, "read_segment: Error reading packet header\n");
+ goto error_out;
+ }
+
+ if ((packet_header[2]<<8) + packet_header[3] != read_data_size)
+ {
+ DBG(1, "read_segment: Error: Expected data size: %i bytes.\n",
+ read_data_size);
+ DBG(1, "read_segment: Expecting %i bytes times %i "
+ "scanlines.\n", scanline_size, scanline_number);
+ DBG(1, "read_segment: Actual data size: %i bytes.\n",
+ (packet_header[2] << 8) + packet_header[3]);
+ goto error_out;
+ }
+
+ /* Read scanlines_this_packet scanlines into the input buf */
+
+ if (sanei_canon_pp_read(sp->port, read_data_size, input_buffer))
+ {
+ DBG(1, "read_segment: Segment read incorrectly, and we don't "
+ "know how to recover.\n");
+ goto error_out;
+ }
+
+ /* This is the only place we can abort safely -
+ * between reading one segment and requesting the next one. */
+ if (sp->abort_now) goto error_out;
+
+ if (scanlines_left >= (scanline_number * 2))
+ {
+ DBG(100, "read_segment: Speculatively starting more scanning "
+ "(%d left)\n", scanlines_left);
+ sanei_canon_pp_write(sp->port, 10, packet_req_command);
+ /* Don't read status, it's unlikely to be ready *just* yet */
+ }
+
+ DBG(100, "read_segment: Convert to RGB\n");
+ /* Convert data */
+ convert_to_rgb(output_image, input_buffer, scanp->width,
+ scanline_number, scanp->mode);
+
+ /* Adjust pixel readings according to calibration data */
+ if (do_adjust) {
+ DBG(100, "read_segment: Adjust output\n");
+ adjust_output(output_image, scanp, sp);
+ }
+
+ /* output */
+ *dest = output_image;
+ /* finished with this now */
+ free(input_buffer);
+ return 0;
+
+ error_out:
+ if (output_image && output_image->image_data)
+ free(output_image->image_data);
+ if (output_image) free(output_image);
+ if (input_buffer) free(input_buffer);
+ sp->abort_now = 0;
+ return -1;
+}
+
+/*
+check8: Calculates the checksum-8 for s bytes pointed to by p.
+
+For messages from the scanner, this should normally end up returning
+0, since the last byte of most packets is the value that makes the
+total up to 0 (or 256 if you're left-handed).
+Hence, usage: if (check8(buffer, size)) {DBG(10, "checksum error!\n");}
+
+Can also be used to generate valid checksums for sending to the scanner.
+*/
+static int check8(unsigned char *p, int s) {
+ int total=0,i;
+ for(i=0;i<s;i++)
+ total-=(signed char)p[i];
+ total &=0xFF;
+ return total;
+}
+
+/* Converts from scanner format -> linear
+ width is in pixels, not bytes. */
+/* This function could use a rewrite */
+static void convdata(unsigned char *srcbuffer, unsigned char *dstbuffer,
+ int width, int mode)
+/* This is a tricky (read: crap) function (read: hack) which is why I probably
+ spent more time commenting it than programming it. The thing to remember
+ here is that the scanner uses interpolated scanlines, so it's
+ RRRRRRRGGGGGGBBBBBB not RGBRGBRGBRGBRGB. So, the calling function just
+ increments the destination pointer slightly to handle green, then a bit
+ more for blue. If you don't understand, tough. */
+{
+ int count;
+ int i, j, k;
+
+ for (count = 0; count < width; count++)
+ {
+ /* The scanner stores data in a bizzare butchered 10-bit
+ format. I'll try to explain it in 100 words or less:
+
+ Scanlines are made up of groups of 4 pixels. Each group of
+ 4 is stored inside 5 bytes. The first 4 bytes of the group
+ contain the lowest 8 bits of one pixel each (in the right
+ order). The 5th byte contains the most significant 2 bits
+ of each pixel in the same order. */
+
+ i = srcbuffer[count + (count >> 2)]; /* Low byte for pixel */
+ j = srcbuffer[(((count / 4) + 1) * 5) - 1]; /* "5th" byte */
+ j = j >> ((count % 4) * 2); /* Get upper 2 bits of intensity */
+ j = j & 0x03; /* Can't hurt */
+ /* And the final 10-bit pixel value is: */
+ k = (j << 8) | i;
+
+ /* now we return this as a 16 bit value */
+ k = k << 6;
+
+ if (mode == 1) /* Scanner -> Grey */
+ {
+ dstbuffer[count * 2] = HIGH_BYTE(k);
+ dstbuffer[(count * 2) + 1] = LOW_BYTE(k);
+ }
+ else if (mode == 2) /* Scanner -> RGB */
+ {
+ dstbuffer[count * 3 * 2] = HIGH_BYTE(k);
+ dstbuffer[(count * 3 * 2) + 1] = LOW_BYTE(k);
+ }
+
+ }
+}
+
+static int adjust_output(image_segment *image, scan_parameters *scanp,
+ scanner_parameters *scannerp)
+/* Needing a good cleanup */
+{
+ /* light and dark points for the CCD sensor in question
+ * (stored in file as 0-1024, scaled to 0-65536) */
+ unsigned long hi, lo;
+ /* The result of our calculations */
+ unsigned long result;
+ unsigned long temp;
+ /* The CCD sensor which read the current pixel - this is a tricky value
+ to get right. */
+ int ccd, scaled_xoff;
+ /* Loop variables */
+ unsigned int scanline, pixelnum, colour;
+ unsigned long int pixel_address;
+ unsigned int cols = scanp->mode ? 3 : 1;
+
+ for (scanline = 0; scanline < image->height; scanline++)
+ {
+ for (pixelnum = 0; pixelnum < image->width; pixelnum++)
+ {
+ /* Figure out CCD sensor number */
+ /* MAGIC FORMULA ALERT! */
+ ccd = (pixelnum << (scannerp->natural_xresolution -
+ scanp->xresolution)) + (1 <<
+ (scannerp->natural_xresolution
+ - scanp->xresolution)) - 1;
+
+ scaled_xoff = scanp->xoffset <<
+ (scannerp->natural_xresolution -
+ scanp->xresolution);
+
+ ccd += scaled_xoff;
+
+ for (colour = 0; colour < cols; colour++)
+ {
+ /* Address of pixel under scrutiny */
+ pixel_address =
+ (scanline * image->width * cols * 2) +
+ (pixelnum * cols * 2) + (colour * 2);
+
+ /* Dark value is easy
+ * Range of lo is 0-18k */
+ lo = (scannerp->blackweight[ccd]) * 3;
+
+ /* Light value depends on the colour,
+ * and is an average in greyscale mode. */
+ if (scanp->mode == 1) /* RGB */
+ {
+ switch (colour)
+ {
+ case 0: hi = scannerp->redweight[ccd] * 3;
+ break;
+ case 1: hi = scannerp->greenweight[ccd] * 3;
+ break;
+ default: hi = scannerp->blueweight[ccd] * 3;
+ break;
+ }
+ }
+ else /* Grey - scanned using green */
+ {
+ hi = scannerp->greenweight[ccd] * 3;
+ }
+
+ /* Check for bad calibration data as it
+ can cause a divide-by-0 error */
+ if (hi <= lo)
+ {
+ DBG(1, "adjust_output: Bad cal data!"
+ " hi: %ld lo: %ld\n"
+ "Recalibrate, that "
+ "should fix it.\n",
+ hi, lo);
+ return -1;
+ }
+
+ /* Start with the pixel value in result */
+ result = MAKE_SHORT(*(image->image_data +
+ pixel_address),
+ *(image->image_data +
+ pixel_address + 1));
+
+ result = result >> 6; /* Range now = 0-1023 */
+ /*
+ if (scanline == 10)
+ DBG(200, "adjust_output: Initial pixel"
+ " value: %ld\n",
+ result);
+ */
+ result *= 54; /* Range now = 0-54k */
+
+ /* Clip to dark and light values */
+ if (result < lo) result = lo;
+ if (result > hi) result = hi;
+
+ /* result = (base-lo) * max_value / (hi-lo) */
+ temp = result - lo;
+ temp *= 65536;
+ temp /= (hi - lo);
+
+ /* Clip output result has been clipped to lo,
+ * and hi >= lo, so temp can't be < 0 */
+ if (temp > 65535)
+ temp = 65535;
+ /*
+ if (scanline == 10)
+ {
+ DBG(200, "adjust_output: %d: base = "
+ "%lu, result %lu (%lu "
+ "- %lu)\n", pixelnum,
+ result, temp, lo, hi);
+ }
+ */
+ result = temp;
+
+ /* Store the value back where it came
+ * from (always bigendian) */
+ *(image->image_data + pixel_address)
+ = HIGH_BYTE(result);
+ *(image->image_data + pixel_address+1)
+ = LOW_BYTE(result);
+ }
+ }
+ }
+ /*DBG(100, "Finished adjusting output\n");*/
+ return 0;
+}
+
+/* Calibration run. Aborting allowed at "safe" points where the scanner won't
+ * be left in a crap state. */
+int sanei_canon_pp_calibrate(scanner_parameters *sp, char *cal_file)
+{
+ int count, readnum, colournum, scanlinenum;
+ int outfile;
+
+ int scanline_size;
+
+ int scanline_count = 6;
+ /* Don't change this unless you also want to change do_adjust */
+ const int calibration_reads = 3;
+
+ unsigned char command_buffer[10];
+
+ image_segment image;
+ unsigned char *databuf;
+
+ char colours[3][6] = {"Red", "Green", "Blue"};
+
+ /* Calibration data is monochromatic (greyscale format) */
+ scanline_size = sp->scanheadwidth * 1.25;
+
+ /* 620P has to be difficult here... */
+ if (!(sp->type) ) scanline_count = 8;
+
+ /* Probably shouldn't have to abort *just* yet, but may as well check */
+ if (sp->abort_now) return -1;
+
+ DBG(40, "Calibrating %ix%i pixels calibration image "
+ "(%i bytes each scan).\n",
+ sp->scanheadwidth, scanline_count,
+ scanline_size * scanline_count);
+
+ /* Allocate memory for calibration data */
+ sp->blackweight = (unsigned long *)
+ calloc(sizeof(unsigned long), sp->scanheadwidth);
+ sp->redweight = (unsigned long *)
+ calloc(sizeof(unsigned long), sp->scanheadwidth);
+ sp->greenweight = (unsigned long *)
+ calloc(sizeof(unsigned long), sp->scanheadwidth);
+ sp->blueweight = (unsigned long *)
+ calloc(sizeof(unsigned long), sp->scanheadwidth);
+
+ /* The data buffer needs to hold a number of images (calibration_reads)
+ * per colour, each sp->scanheadwidth x scanline_count */
+ databuf = malloc(scanline_size * scanline_count * calibration_reads*3);
+
+ /* And allocate space for converted image data in this image_segment */
+ image.image_data = malloc(scanline_count * sp->scanheadwidth * 2 *
+ calibration_reads);
+ image.width = sp->scanheadwidth;
+ image.height = scanline_count * calibration_reads;
+
+ /* Sending the "dark calibration" command */
+ memcpy(command_buffer, cmd_calblack, 10);
+
+ /* Which includes the size of data we expect the scanner to return */
+ command_buffer[7] = ((scanline_size * scanline_count) & 0xff00) >> 8;
+ command_buffer[8] = (scanline_size * scanline_count) & 0xff;
+
+ DBG(40, "Step 1/3: Calibrating black level...\n");
+ for (readnum = 0; readnum < calibration_reads; readnum++)
+ {
+ DBG(40, " * Black scan number %d/%d.\n", readnum + 1,
+ calibration_reads);
+
+ if (sp->abort_now) return -1;
+
+ if (send_command(sp->port, command_buffer, 10, 100000, 5000000))
+ {
+ DBG(1, "Error reading black level!\n");
+ free (image.image_data);
+ free(databuf);
+ return -1;
+
+ }
+
+ /* Black reference data */
+ sanei_canon_pp_read(sp->port, scanline_size * scanline_count,
+ databuf +
+ (readnum * scanline_size * scanline_count));
+ }
+
+ /* Convert scanner format to a greyscale 16bpp image */
+ for (scanlinenum = 0;
+ scanlinenum < scanline_count * calibration_reads;
+ scanlinenum++)
+ {
+ convdata(databuf + (scanlinenum * scanline_size),
+ image.image_data +
+ (scanlinenum * sp->scanheadwidth*2),
+ sp->scanheadwidth, 1);
+ }
+
+ /* Take column totals */
+ for (count = 0; count < sp->scanheadwidth; count++)
+ {
+ /* Value is normalised as if we took 6 scanlines, even if we
+ * didn't (620P I'm looking at you!) */
+ sp->blackweight[count] = (column_sum(&image, count) * 6)
+ / scanline_count >> 6;
+ }
+
+ /* 620P has to be difficult here... */
+ if (!(sp->type) )
+ {
+ scanline_count = 6;
+ image.height = scanline_count * calibration_reads;
+ }
+
+ DBG(40, "Step 2/3: Gamma tables...\n");
+ DBG(40, " * Requesting creation of new of gamma tables...\n");
+ if (sp->abort_now) return -1;
+ if (send_command(sp->port, cmd_cleargamma, 10, 100000, 5000000))
+ {
+ DBG(1,"Error sending gamma command!\n");
+ free (image.image_data);
+ free(databuf);
+ return -1;
+ }
+
+ DBG(20, " * Snoozing for 15 seconds while the scanner calibrates...");
+ usleep(15000000);
+ DBG(40, "done.\n");
+
+ DBG(40, " * Requesting gamma table values...");
+ if (send_command(sp->port, cmd_readgamma, 10, 100000, 10000000))
+ {
+ DBG(1,"Error sending gamma table request!\n");
+ free (image.image_data);
+ free(databuf);
+ return -1;
+ }
+ DBG(40, "done.\n");
+
+ DBG(40, " * Reading white-balance/gamma data... ");
+ sanei_canon_pp_read(sp->port, 32, sp->gamma);
+ DBG(40, "done.\n");
+
+ if (sp->abort_now) return -1;
+
+ memcpy(command_buffer, cmd_calcolour, 10);
+
+ /* Set up returned data size */
+ command_buffer[7] = ((scanline_size * scanline_count) & 0xff00) >> 8;
+ command_buffer[8] = (scanline_size * scanline_count) & 0xff;
+
+ DBG(40, "Step 3/3: Calibrating sensors...\n");
+ /* Now for the RGB high-points */
+ for (colournum = 1; colournum < 4; colournum++)
+ {
+ /* Set the colour we want to read */
+ command_buffer[3] = colournum;
+ for (readnum = 0; readnum < 3; readnum++)
+ {
+ DBG(10, " * %s sensors, scan number %d/%d.\n",
+ colours[colournum-1], readnum + 1,
+ calibration_reads);
+
+ if (sp->abort_now) return -1;
+ if (send_command(sp->port, command_buffer, 10,
+ 100000, 5000000))
+ {
+ DBG(1,"Error sending scan request!");
+ free (image.image_data);
+ free(databuf);
+ return -1;
+ }
+
+ sanei_canon_pp_read(sp->port, scanline_size *
+ scanline_count, databuf +
+ (readnum * scanline_size *
+ scanline_count));
+
+ }
+
+ /* Convert colour data from scanner format to RGB data */
+ for (scanlinenum = 0; scanlinenum < scanline_count *
+ calibration_reads; scanlinenum++)
+ {
+ convdata(databuf + (scanlinenum * scanline_size),
+ image.image_data +
+ (scanlinenum * sp->scanheadwidth * 2),
+ sp->scanheadwidth, 1);
+ }
+
+ /* Sum each column of the image and store the results in sp */
+ for (count = 0; count < sp->scanheadwidth; count++)
+ {
+ if (colournum == 1)
+ sp->redweight[count] =
+ column_sum(&image, count) >> 6;
+ else if (colournum == 2)
+ sp->greenweight[count] =
+ column_sum(&image, count) >> 6;
+ else
+ sp->blueweight[count] =
+ column_sum(&image, count) >> 6;
+ }
+
+ }
+
+ if (sp->abort_now) return -1;
+
+ /* cal_file == NUL indicates we want an in-memory scan only */
+ if (cal_file != NULL)
+ {
+ DBG(40, "Writing calibration to %s\n", cal_file);
+ outfile = open(cal_file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+ if (outfile < 0)
+ {
+ DBG(10, "Error opening cal file for writing\n");
+ }
+
+ /* Header */
+ if (safe_write(outfile, header, strlen(header) + 1) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+ if (safe_write(outfile, (const char *)&fileversion, sizeof(int)) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+
+ /* Data */
+ if (safe_write(outfile, (char *)&(sp->scanheadwidth),
+ sizeof(sp->scanheadwidth)) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+ if (safe_write(outfile, (char *)(sp->blackweight),
+ sp->scanheadwidth * sizeof(long)) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+ if (safe_write(outfile, (char *)(sp->redweight),
+ sp->scanheadwidth * sizeof(long)) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+ if (safe_write(outfile, (char *)(sp->greenweight),
+ sp->scanheadwidth * sizeof(long)) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+ if (safe_write(outfile, (char *)(sp->blueweight),
+ sp->scanheadwidth * sizeof(long)) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+ if (safe_write(outfile, (char *)(sp->gamma), 32) < 0)
+ DBG(10, "Write error on calibration file %s", cal_file);
+
+ close(outfile);
+ }
+
+ free(databuf);
+ free(image.image_data);
+
+ return 0;
+}
+
+static unsigned long column_sum(image_segment *image, int x)
+/* This gives us a number from 0-n*65535 where n is the height of the image */
+{
+ unsigned int row, p;
+ unsigned long total = 0;
+
+ p = x;
+ for (row = 0; row < image->height; row++)
+ {
+ total+= MAKE_SHORT(image->image_data[2*p],
+ image->image_data[2*p+1]);
+ p += image->width;
+ }
+ return total;
+}
+
+
+static int scanner_setup_params(unsigned char *buf, scanner_parameters *sp,
+ scan_parameters *scanp)
+{
+ int scaled_width, scaled_height;
+ int scaled_xoff, scaled_yoff;
+
+ /* Natural resolution (I think) */
+ if (sp->scanheadwidth == 2552)
+ {
+ buf[0] = 0x11; /* 300 | 0x1000 */
+ buf[1] = 0x2c;
+ buf[2] = 0x11;
+ buf[3] = 0x2c;
+ } else {
+ buf[0] = 0x12; /* 600 | 0x1000*/
+ buf[1] = 0x58;
+ buf[2] = 0x12;
+ buf[3] = 0x58;
+ }
+
+ scaled_width = scanp->width <<
+ (sp->natural_xresolution - scanp->xresolution);
+ /* YO! This needs fixing if we ever use yresolution! */
+ scaled_height = scanp->height <<
+ (sp->natural_xresolution - scanp->xresolution);
+ scaled_xoff = scanp->xoffset <<
+ (sp->natural_xresolution - scanp->xresolution);
+ scaled_yoff = scanp->yoffset <<
+ (sp->natural_xresolution - scanp->xresolution);
+
+ /* Input resolution */
+ buf[4] = (((75 << scanp->xresolution) & 0xff00) >> 8) | 0x10;
+ buf[5] = (75 << scanp->xresolution) & 0xff;
+ /* Interpolated resolution */
+ buf[6] = (((75 << scanp->xresolution) & 0xff00) >> 8) | 0x10;;
+ buf[7] = (75 << scanp->xresolution) & 0xff;
+
+ /* X offset */
+ buf[8] = (scaled_xoff & 0xff000000) >> 24;
+ buf[9] = (scaled_xoff & 0xff0000) >> 16;
+ buf[10] = (scaled_xoff & 0xff00) >> 8;
+ buf[11] = scaled_xoff & 0xff;
+
+ /* Y offset */
+ buf[12] = (scaled_yoff & 0xff000000) >> 24;
+ buf[13] = (scaled_yoff & 0xff0000) >> 16;
+ buf[14] = (scaled_yoff & 0xff00) >> 8;
+ buf[15] = scaled_yoff & 0xff;
+
+ /* Width of image to be scanned */
+ buf[16] = (scaled_width & 0xff000000) >> 24;
+ buf[17] = (scaled_width & 0xff0000) >> 16;
+ buf[18] = (scaled_width & 0xff00) >> 8;
+ buf[19] = scaled_width & 0xff;
+
+ /* Height of image to be scanned */
+ buf[20] = (scaled_height & 0xff000000) >> 24;
+ buf[21] = (scaled_height & 0xff0000) >> 16;
+ buf[22] = (scaled_height & 0xff00) >> 8;
+ buf[23] = scaled_height & 0xff;
+
+
+ /* These appear to be the only two colour mode possibilities.
+ Pure black-and-white mode probably just uses greyscale and
+ then gets its contrast adjusted by the driver. I forget. */
+ if (scanp->mode == 1) /* Truecolour */
+ buf[24] = 0x08;
+ else /* Greyscale */
+ buf[24] = 0x04;
+
+ return 0;
+}
+
+int sanei_canon_pp_abort_scan(scanner_parameters *sp)
+{
+ /* The abort command (hopefully) */
+ sanei_canon_pp_write(sp->port, 10, cmd_abort);
+ sanei_canon_pp_check_status(sp->port);
+ return 0;
+}
+
+/* adjust_gamma: Upload a gamma profile to the scanner */
+int sanei_canon_pp_adjust_gamma(scanner_parameters *sp)
+{
+ sp->gamma[31] = check8(sp->gamma, 31);
+ if (sanei_canon_pp_write(sp->port, 10, cmd_setgamma))
+ return -1;
+ if (sanei_canon_pp_write(sp->port, 32, sp->gamma))
+ return -1;
+
+ return 0;
+}
+
+int sanei_canon_pp_sleep_scanner(struct parport *port)
+{
+ /* *SCANEND Command - puts scanner to sleep */
+ sanei_canon_pp_write(port, 10, cmd_scanend);
+ sanei_canon_pp_check_status(port);
+
+ ieee1284_terminate(port);
+
+ return 0;
+ /* FIXME: I murdered Simon's code here */
+ /* expect(port, "Enter Transparent Mode", 0x1f, 0x1f, 1000000); */
+}
+
+int sanei_canon_pp_detect(struct parport *port, int mode)
+{
+ /*int caps;*/
+ /* This code needs to detect whether or not a scanner is present on
+ * the port, quickly and reliably. Fast version of
+ * sanei_canon_pp_initialise()
+ *
+ * If this detect returns true, a more comprehensive check will
+ * be conducted
+ * Return values:
+ * 0 = scanner present
+ * anything else = scanner not present
+ * PRE: port is open/unclaimed
+ * POST: port is closed/unclaimed
+ */
+
+ /* port is already open, just need to claim it */
+
+ if (ieee1284_claim(port) != E1284_OK)
+ {
+ DBG(0,"detect: Unable to claim port\n");
+ return 2;
+ }
+ if (sanei_canon_pp_wake_scanner(port, mode))
+ {
+ DBG(10, "detect: could not wake scanner\n");
+ ieee1284_release(port);
+ return 3;
+ }
+
+ /* Goodo, sleep (snaps fingers) */
+ sanei_canon_pp_sleep_scanner(port);
+
+ ieee1284_release(port);
+ /* ieee1284_close(port); */
+
+ return 0;
+}
+
+static int send_command(struct parport *port, unsigned char *buf, int bufsize,
+ int delay, int timeout)
+/* Sends a command until the scanner says it is ready.
+ * sleeps for delay microsecs between reads
+ * returns -1 on error, -2 on timeout */
+{
+ int retries = 0;
+
+ do
+ {
+ /* Send command */
+ if (sanei_canon_pp_write(port, bufsize, buf))
+ return -1;
+
+ /* sleep a bit */
+ usleep(delay);
+ } while (sanei_canon_pp_check_status(port) &&
+ retries++ < (timeout/delay));
+
+ if (retries >= (timeout/delay)) return -2;
+ return 0;
+
+}
diff --git a/backend/canon_pp-dev.h b/backend/canon_pp-dev.h
new file mode 100644
index 0000000..e8f36a8
--- /dev/null
+++ b/backend/canon_pp-dev.h
@@ -0,0 +1,185 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ -----
+
+ canon_pp-dev.h: $Revision$
+
+ This file is part of the canon_pp backend, supporting Canon FBX30P
+ and NX40P scanners and also part of the stand-alone driver.
+
+ Simon Krix <kinsei@users.sourceforge.net>
+ */
+
+#ifndef CANON_PP_DEV_H
+
+#define CANON_PP_DEV_H
+
+/* Signal names */
+/* C port */
+#define READY 0x1f
+#define NSELECTIN 0x08
+#define NINIT 0x04
+#define HOSTBUSY 0x02
+#define HOSTCLK 0x01
+
+/* S port */
+#define BUSY 0x10
+#define NACK 0x08
+#define PTRCLK 0x08
+#define PERROR 0x04
+#define ACKDATAREQ 0x04
+#define XFLAG 0x02
+#define SELECT 0x02
+#define NERROR 0x01
+#define NFAULT 0x01
+#define NDATAAVAIL 0x01
+
+/* Scanner things */
+#define CALSIZE 18 /* Lines per calibration */
+
+/* Init modes */
+#define INITMODE_20P 1
+#define INITMODE_30P 2
+#define INITMODE_AUTO 3
+
+/* Misc things */
+#define T_SCAN 1
+#define T_CALIBRATE 2
+
+/* Macros */
+#define MAKE_SHORT(a,b) (((short)a)*0x100+(short)b)
+#define LOW_BYTE(a) (a%0x100)
+#define HIGH_BYTE(a) (a/0x100)
+
+typedef struct scanner_parameter_struct
+{
+ /* This is the port the scanner is on, in libieee1284-readable form */
+ struct parport *port;
+
+ /* Width of the scanning head in pixels */
+ int scanheadwidth;
+ int scanbedlength;
+
+ /* Resolution of the scan head where dpi = 75 << natural_resolution */
+ int natural_xresolution;
+ int natural_yresolution;
+
+ int max_xresolution;
+ int max_yresolution;
+
+ /* ID String. Should only be 38(?) bytes long, so we can
+ reduce the size later. */
+ char id_string[80];
+
+ /* Short, readable scanner name, such as "FB330P" */
+ char name[40];
+
+ /* Pixel weight values from calibration, one per pixel on the scan head.
+ These must be allocated before any scanning can be done. */
+ unsigned long *blackweight;
+ unsigned long *redweight;
+ unsigned long *greenweight;
+ unsigned long *blueweight;
+
+ /* Not understood white-balance/gain values */
+ unsigned char gamma[32];
+
+ /* Type of scanner ( 0 = *20P, 1 = [*30P|*40P] ) */
+ unsigned char type;
+
+ /* Are we aborting this scanner now */
+ unsigned char abort_now;
+
+} scanner_parameters;
+
+typedef struct scan_parameter_struct
+{
+ /* Size of image */
+ unsigned int width, height;
+ /* Position of image on the scanner bed */
+ unsigned int xoffset, yoffset;
+ /* Resolution at which to scan (remember it's 75 << resolution) */
+ int xresolution, yresolution;
+ /* Mode of image. 0 = greyscale, 1 = truecolour */
+ int mode;
+} scan_parameters;
+
+typedef struct image_segment_struct
+{
+ /* Size of image segment */
+ unsigned int width, height;
+ /* Which part of the image this is */
+ unsigned int start_scanline;
+ /* Pointer to image data */
+ unsigned char *image_data;
+} image_segment;
+
+/* Scan-related functions ========================= */
+
+/* Brings the scanner in and out of transparent mode
+ and detects model information */
+int sanei_canon_pp_initialise(scanner_parameters *sp, int mode);
+int sanei_canon_pp_close_scanner(scanner_parameters *sp);
+
+/* Image scanning functions */
+int sanei_canon_pp_init_scan(scanner_parameters *sp, scan_parameters *scanp);
+
+int sanei_canon_pp_read_segment(image_segment **dest, scanner_parameters *sp,
+ scan_parameters *scanp, int scanline_count, int do_adjust,
+ int scanlines_left);
+
+int sanei_canon_pp_abort_scan(scanner_parameters *sp);
+
+/* Loads the gain offset values. Needs a new name. */
+int sanei_canon_pp_load_weights(const char *filename, scanner_parameters *sp);
+
+
+int sanei_canon_pp_calibrate(scanner_parameters *sp, char *cal_file);
+
+int sanei_canon_pp_adjust_gamma(scanner_parameters *sp);
+
+/* Detect if a scanner is present on a given port */
+int sanei_canon_pp_detect(struct parport *port, int mode);
+
+/* Put a scanner to sleep */
+int sanei_canon_pp_sleep_scanner(struct parport *port);
+
+#endif
diff --git a/backend/canon_pp-io.c b/backend/canon_pp-io.c
new file mode 100644
index 0000000..881ac24
--- /dev/null
+++ b/backend/canon_pp-io.c
@@ -0,0 +1,618 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ -----
+
+ This file is part of the canon_pp backend, supporting Canon CanoScan
+ Parallel scanners and also distributed as part of the stand-alone driver.
+
+ canon_pp-io.c: $Revision$
+
+ Low Level Function library for Canon CanoScan Parallel Scanners by
+ Simon Krix <kinsei@users.sourceforge.net>
+ */
+
+#ifndef NOSANE
+#include "../include/sane/config.h"
+#endif
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <ieee1284.h>
+#include "canon_pp-io.h"
+#include "canon_pp-dev.h"
+
+#ifdef NOSANE
+
+/* No SANE, Things that only apply to stand-alone */
+#include <stdio.h>
+#include <stdarg.h>
+
+static void DBG(int level, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ if (level < 50) vfprintf(stderr, format, args);
+ va_end(args);
+}
+#else
+
+/* Definitions which only apply to SANE compiles */
+#ifndef VERSION
+#define VERSION "$Revision$"
+#endif
+
+/* Fix problem with DBG macro definition having a - in the name */
+#define DEBUG_DECLARE_ONLY
+#include "canon_pp.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_config.h"
+
+#endif
+
+/* 0x00 = Nibble Mode (M1284_NIBBLE)
+ 0x10 = ECP Mode (M1284_ECP)
+ The scanner driver seems not to support ECP RLE mode
+ (which is a huge bummer because compression would be
+ ace) nor EPP mode.
+ */
+static int ieee_mode = M1284_NIBBLE;
+
+/* For super-verbose debugging */
+/* #define DUMP_PACKETS 1 */
+
+/* Some sort of initialisation command */
+static unsigned char cmd_init[10] = { 0xec, 0x20, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/************* Local Prototypes ******************/
+
+/* Used by wake_scanner */
+static int scanner_reset(struct parport *port);
+static void scanner_chessboard_control(struct parport *port);
+static void scanner_chessboard_data(struct parport *port, int mode);
+
+/* Used by read_data */
+static int ieee_transfer(struct parport *port, int length,
+ unsigned char *data);
+
+/* Low level functions */
+static int readstatus(struct parport *port);
+static int expect(struct parport *port, const char *step, int s,
+ int mask, unsigned int delay);
+
+/* Port-level functions */
+static void outdata(struct parport *port, int d);
+static void outcont(struct parport *port, int d, int mask);
+static void outboth(struct parport *port, int d, int c);
+
+/************************************/
+
+/*
+ * IEEE 1284 defines many values for m,
+ * but these scanners only support 2: nibble and ECP modes.
+ * And no data compression either (argh!)
+ * 0 = Nibble-mode reverse channel transfer
+ * 16 = ECP-mode
+ */
+void sanei_canon_pp_set_ieee1284_mode(int m)
+{
+ ieee_mode = m;
+}
+
+int sanei_canon_pp_wake_scanner(struct parport *port, int mode)
+{
+ /* The scanner tristates the printer's control lines
+ (essentially disabling the passthrough port) and exits
+ from Transparent Mode ready for communication. */
+ int i = 0;
+ int tmp;
+ int max_cycles = 3;
+
+ tmp = readstatus(port);
+
+ /* Reset only works on 30/40 models */
+ if (mode != INITMODE_20P)
+ {
+ if ((tmp != READY))
+ {
+ DBG(40, "Scanner not ready (0x%x). Attempting to "
+ "reset...\n", tmp);
+ scanner_reset(port);
+ /* give it more of a chance to reset in this case */
+ max_cycles = 5;
+ }
+ } else {
+ DBG(0, "WARNING: Don't know how to reset an FBx20P, you may "
+ "have to power cycle\n");
+ }
+
+ do
+ {
+ i++;
+
+ /* Send the wakeup sequence */
+ scanner_chessboard_control(port);
+ scanner_chessboard_data(port, mode);
+
+ if (expect(port, NULL, 0x03, 0x1f, 800000) &&
+ (mode == INITMODE_AUTO))
+ {
+ /* 630 Style init failed, try 620 style */
+ scanner_chessboard_control(port);
+ scanner_chessboard_data(port, INITMODE_20P);
+ }
+
+ if (expect(port, "Scanner wakeup reply 1", 0x03, 0x1f, 50000))
+ {
+ outboth(port, 0x04, 0x0d);
+ usleep(100000);
+ outcont(port, 0x07, 0x0f);
+ usleep(100000);
+ }
+
+ } while ((i < max_cycles) && (!expect(port,"Scanner wakeup reply 2",
+ 0x03, 0x1f, 100000) == 0));
+
+ /* Block just after chessboarding
+ Reply 1 (S3 and S4 on, S5 and S7 off) */
+ outcont(port, 0, HOSTBUSY); /* C1 off */
+ /* Reply 2 - If it ain't happening by now, it ain't gonna happen. */
+ if (expect(port, "Reply 2", 0xc, 0x1f, 800000))
+ return -1;
+ outcont(port, HOSTBUSY, HOSTBUSY); /* C1 on */
+ if (expect(port, "Reply 3", 0x0b, 0x1f, 800000))
+ return -1;
+ outboth(port, 0, NSELECTIN | NINIT | HOSTCLK); /* Clear D, C3+, C1- */
+
+ /* If we had to try the wakeup cycle more than once, we should wait
+ * here for 10 seconds to let the scanner pull itself together -
+ * it can actually take longer, but I can't wait that long! */
+ if (i > 1)
+ {
+ DBG(10, "Had to reset scanner, waiting for the "
+ "head to get back.\n");
+ usleep(10000000);
+ }
+
+ return 0;
+}
+
+
+int sanei_canon_pp_write(struct parport *port, int length, unsigned char *data)
+{
+
+#ifdef DUMP_PACKETS
+ ssize_t count;
+
+ DBG(10,"Sending: ");
+ for (count = 0; count < length; count++)
+ {
+ DBG(10,"%02x ", data[count]);
+ if (count % 20 == 19)
+ DBG(10,"\n ");
+ }
+ if (count % 20 != 19) DBG(10,"\n");
+#endif
+
+ DBG(100, "NEW Send Command (length %i):\n", length);
+
+ switch (ieee_mode)
+ {
+ case M1284_BECP:
+ case M1284_ECPRLE:
+ case M1284_ECPSWE:
+ case M1284_ECP:
+ ieee1284_negotiate(port, ieee_mode);
+ if (ieee1284_ecp_write_data(port, 0, (char *)data,
+ length) != length)
+ return -1;
+ break;
+ case M1284_NIBBLE:
+ if (ieee1284_compat_write(port, 0, (char *)data,
+ length) != length)
+ return -1;
+ break;
+ default:
+ DBG(0, "Invalid mode in write!\n");
+ }
+
+ DBG(100, "<< write");
+
+ return 0;
+}
+
+int sanei_canon_pp_read(struct parport *port, int length, unsigned char *data)
+{
+ int count, offset;
+
+ DBG(200, "NEW read_data (%i bytes):\n", length);
+ ieee1284_negotiate(port, ieee_mode);
+
+ /* This is special; Nibble mode needs a little
+ extra help from us. */
+
+ if (ieee_mode == M1284_NIBBLE)
+ {
+ /* Interrupt phase */
+ outcont(port, NSELECTIN, HOSTBUSY | NSELECTIN);
+ if (expect(port, "Read Data 1", 0, NDATAAVAIL, 6000000))
+ {
+ DBG(10,"Error 1\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+ outcont(port, HOSTBUSY, HOSTBUSY);
+
+ if (expect(port, "Read Data 2", NACK, NACK, 1000000))
+ {
+ DBG(1,"Error 2\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+ if (expect(port, "Read Data 3 (Ready?)", 0, PERROR, 1000000))
+ {
+ DBG(1,"Error 3\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+
+ /* Host-Busy Data Available phase */
+
+ if ((readstatus(port) & NDATAAVAIL) == NDATAAVAIL)
+ {
+ DBG(1,"No data to read.\n");
+ ieee1284_terminate(port);
+ return 1;
+ }
+ }
+
+ offset = 0;
+
+ DBG(100, "-> ieee_transfer(%d) *\n", length);
+ count = ieee_transfer(port, length, data);
+ DBG(100, "<- (%d)\n", count);
+ /* Early-out if it was not implemented */
+ if (count == E1284_NOTIMPL)
+ return 2;
+
+ length -= count;
+ offset+= count;
+ while (length > 0)
+ {
+ /* If 0 bytes were transferred, it's a legal
+ "No data" condition (I think). Otherwise,
+ it may have run out of buffer.. keep reading*/
+
+ if (count < 0) {
+ DBG(10, "Couldn't read enough data (need %d more "
+ "of %d)\n", length+count,length+offset);
+ ieee1284_terminate(port);
+ return 1;
+ }
+
+ DBG(100, "-> ieee_transfer(%d)\n", length);
+ count = ieee_transfer(port, length, data+offset);
+ DBG(100, "<- (%d)\n", count);
+ length-=count;
+ offset+= count;
+
+ }
+
+#ifdef DUMP_PACKETS
+ if (length <= 60)
+ {
+ DBG(10,"Read: ");
+ for (count = 0; count < length; count++)
+ {
+ DBG(10,"%02x ", data[count]);
+ if (count % 20 == 19)
+ DBG(10,"\n ");
+ }
+
+ if (count % 20 != 19) DBG(10,"\n");
+ }
+ else
+ {
+ DBG(10,"Read: %i bytes\n", length);
+ }
+#endif
+
+ if (ieee_mode == M1284_NIBBLE)
+ ieee1284_terminate(port);
+
+ return 0;
+
+}
+
+static int ieee_transfer(struct parport *port, int length, unsigned char *data)
+{
+ int result = 0;
+
+ DBG(100, "IEEE transfer (%i bytes)\n", length);
+
+ switch (ieee_mode)
+ {
+ case M1284_BECP:
+ case M1284_ECP:
+ case M1284_ECPRLE:
+ case M1284_ECPSWE:
+ result = ieee1284_ecp_read_data(port, 0, (char *)data,
+ length);
+ break;
+ case M1284_NIBBLE:
+ result = ieee1284_nibble_read(port, 0, (char *)data,
+ length);
+ break;
+ default:
+ DBG(1, "Internal error: Wrong mode for transfer.\n"
+ "Please email stauff1@users.sourceforge.net\n"
+ "or kinsei@users.sourceforge.net\n");
+ }
+
+ return result;
+}
+
+int sanei_canon_pp_check_status(struct parport *port)
+{
+ int status;
+ unsigned char data[2];
+
+ DBG(200, "* Check Status:\n");
+
+ if (sanei_canon_pp_read(port, 2, data))
+ return -1;
+
+ status = data[0] | (data[1] << 8);
+
+ switch(status)
+ {
+ case 0x0606:
+ DBG(200, "Ready - 0x0606\n");
+ return 0;
+ break;
+ case 0x1414:
+ DBG(200, "Busy - 0x1414\n");
+ return 1;
+ break;
+ case 0x0805:
+ DBG(200, "Resetting - 0x0805\n");
+ return 3;
+ break;
+ case 0x1515:
+ DBG(1, "!! Invalid Command - 0x1515\n");
+ return 2;
+ break;
+ case 0x0000:
+ DBG(200, "Nothing - 0x0000");
+ return 4;
+ break;
+
+ default:
+ DBG(1, "!! Unknown status - %04x\n", status);
+ return 100;
+ }
+}
+
+
+/* Send a raw byte to the printer port */
+static void outdata(struct parport *port, int d)
+{
+ ieee1284_write_data(port, d & 0xff);
+}
+
+/* Send the low nibble of d to the control port.
+ The mask affects which bits are changed. */
+static void outcont(struct parport *port, int d, int mask)
+{
+ static int control_port_status = 0;
+ control_port_status = (control_port_status & ~mask) | (d & mask);
+ ieee1284_write_control(port, (control_port_status & 0x0f));
+}
+
+/* Send a byte to both ports */
+static void outboth(struct parport *port, int d, int c)
+{
+ ieee1284_write_data(port, d & 0xff);
+ outcont(port, c, 0x0f);
+}
+
+/* readstatus():
+ Returns the LOGIC value of the S register (ie: all input lines)
+ shifted right to to make it easier to read. Note: S5 is inverted
+ by ieee1284_read_status so we don't need to */
+static int readstatus(struct parport *port)
+{
+ return (ieee1284_read_status(port) & 0xf8) >> 3;
+}
+
+static void scanner_chessboard_control(struct parport *port)
+{
+ /* Wiggle C1 and C3 (twice) */
+ outboth(port, 0x0, 13);
+ usleep(10);
+ outcont(port, 7, 0xf);
+ usleep(10);
+ outcont(port, 13, 0xf);
+ usleep(10);
+ outcont(port, 7, 0xf);
+ usleep(10);
+}
+
+static void scanner_chessboard_data(struct parport *port, int mode)
+{
+ int count;
+
+ /* initial weirdness here for 620P - seems to go quite fast,
+ * just ignore it! */
+
+ for (count = 0; count < 2; count++)
+ {
+ /* Wiggle data lines (4 times) while strobing C1 */
+ /* 33 here for *30P, 55 for *20P */
+ if (mode == INITMODE_20P)
+ outdata(port, 0x55);
+ else
+ outdata(port, 0x33);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+ outcont(port, 0, HOSTBUSY);
+ usleep(10);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+
+ if (mode == INITMODE_20P)
+ outdata(port, 0xaa);
+ else
+ outdata(port, 0xcc);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+ outcont(port, 0, HOSTBUSY);
+ usleep(10);
+ outcont(port, HOSTBUSY, HOSTBUSY);
+ usleep(10);
+ }
+}
+
+/* Reset the scanner. At least, it works 50% of the time. */
+static int scanner_reset(struct parport *port)
+{
+
+ /* Resetting only works for the *30Ps, sorry */
+ if (readstatus(port) == 0x0b)
+ {
+ /* Init Block 1 - composed of a 0-byte IEEE read */
+ ieee1284_negotiate(port, 0x0);
+ ieee1284_terminate(port);
+ ieee1284_negotiate(port, 0x0);
+ ieee1284_terminate(port);
+ scanner_chessboard_data(port, 1);
+ scanner_chessboard_data(port, 1);
+ scanner_chessboard_data(port, 1);
+ scanner_chessboard_data(port, 1);
+
+ scanner_chessboard_data(port, 0);
+ scanner_chessboard_data(port, 0);
+ scanner_chessboard_data(port, 0);
+ scanner_chessboard_data(port, 0);
+ }
+
+ /* Reset Block 2 =============== */
+ outboth(port, 0x04, 0x0d);
+
+ /* Specifically, we want this: 00111 on S */
+ if (expect(port, "Reset 2 response 1", 0x7, 0x1f, 500000))
+ return 1;
+
+ outcont(port, 0, HOSTCLK);
+ usleep(5);
+ outcont(port, 0x0f, 0xf); /* All lines must be 1. */
+
+ /* All lines 1 */
+ if (expect(port, "Reset 2 response 2 (READY)",
+ 0x1f, 0x1f, 500000))
+ return 1;
+
+ outcont(port, 0, HOSTBUSY);
+ usleep(100000); /* a short pause */
+ outcont(port, HOSTBUSY, HOSTBUSY | NSELECTIN);
+
+ return 0;
+}
+
+/* A timed version of expect, which will wait for delay before erroring
+ This is the one and only one we should be using */
+static int expect(struct parport *port, const char *msg, int s,
+ int mask, unsigned int delay)
+{
+ struct timeval tv;
+
+ tv.tv_sec = delay / 1000000;
+ tv.tv_usec = delay % 1000000;
+
+ if (ieee1284_wait_status(port, mask << 3, s << 3, &tv))
+ {
+ if (msg) DBG(10, "Timeout: %s (0x%02x in 0x%02x) - Status "
+ "= 0x%02x\n", msg, s, mask, readstatus(port));
+ return 1;
+ }
+
+ return 0;
+}
+
+int sanei_canon_pp_scanner_init(struct parport *port)
+{
+
+ int tries = 0;
+ int tmp = 0;
+
+ /* Put the scanner in nibble mode */
+ ieee1284_negotiate(port, 0x0);
+
+ /* No data to read yet - return to idle mode */
+ ieee1284_terminate(port);
+
+ /* In Windows, this is always ECP (or an attempt at it) */
+ if (sanei_canon_pp_write(port, 10, cmd_init))
+ return -1;
+ /* Note that we don't really mind what the status was as long as it
+ * wasn't a read error (returns -1) */
+ /* In fact, the 620P gives an error on that last command, but they
+ * keep going anyway */
+ if (sanei_canon_pp_check_status(port) < 0)
+ return -1;
+
+ /* Try until it's ready */
+ sanei_canon_pp_write(port, 10, cmd_init);
+ while ((tries < 3) && (tmp = sanei_canon_pp_check_status(port)))
+ {
+ if (tmp < 0)
+ return -1;
+ DBG(10, "scanner_init: Giving the scanner a snooze...\n");
+ usleep(500000);
+
+ tries++;
+
+ sanei_canon_pp_write(port, 10, cmd_init);
+ }
+
+ if (tries == 3) return 1;
+
+ return 0;
+}
diff --git a/backend/canon_pp-io.h b/backend/canon_pp-io.h
new file mode 100644
index 0000000..376af8a
--- /dev/null
+++ b/backend/canon_pp-io.h
@@ -0,0 +1,65 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ -----
+
+ canon_pp-io.h: $Revision$
+
+ This file is part of the canon_pp backend, supporting Canon FBX30P
+ and NX40P scanners and also distributed as part of the stand-alone
+ driver.
+
+ Low-level scanner interface
+ */
+
+#ifndef CANON_PP_IO_H
+#define CANON_PP_IO_H
+
+/* Actual Interface */
+void sanei_canon_pp_set_ieee1284_mode(int m);
+int sanei_canon_pp_wake_scanner(struct parport *port, int mode);
+int sanei_canon_pp_write(struct parport *port, int length,
+ unsigned char *data);
+int sanei_canon_pp_read(struct parport *port, int length,
+ unsigned char *data);
+int sanei_canon_pp_check_status(struct parport *port);
+int sanei_canon_pp_scanner_init(struct parport *port);
+
+#endif
diff --git a/backend/canon_pp.c b/backend/canon_pp.c
new file mode 100644
index 0000000..cc7c27e
--- /dev/null
+++ b/backend/canon_pp.c
@@ -0,0 +1,2039 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ -----
+
+ canon_pp.c: $Revision$
+
+ This file is part of the canon_pp backend, supporting Canon FBX30P
+ and NX40P scanners
+ */
+
+#ifdef _AIX
+#include <lalloca.h> /* MUST come first for AIX! */
+#endif
+
+#define BACKEND_NAME canon_pp
+
+#define THREE_BITS 0xE0
+#define TWO_BITS 0xC0
+#define MM_PER_IN 25.4
+
+#ifndef NOSANE
+#include "../include/sane/config.h"
+#endif
+
+#ifndef VERSION
+#define VERSION "$Revision$"
+#endif
+
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ieee1284.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+
+#include "canon_pp-dev.h"
+#include "canon_pp-io.h"
+#include "canon_pp.h"
+
+/* #include "../include/sane/sanei_pio.h" */
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+/* #include "../include/sane/sanei_debug.h" */
+
+
+/* Prototypes */
+static SANE_Status init_device(struct parport *pp);
+
+/* create a calibration file and give it initial values */
+static int init_cal(char *file);
+
+static SANE_Status fix_weights_file(CANONP_Scanner *cs);
+
+static SANE_Status detect_mode(CANONP_Scanner *cs);
+
+/* Global Variables (ack!) */
+
+/* The first device in a linked list of devices */
+static CANONP_Scanner *first_dev = NULL;
+/* The default scanner to open */
+static char *def_scanner = NULL;
+/* The number of devices */
+static int num_devices = 0;
+/* ieee1284 parallel ports */
+struct parport_list pl;
+/* leftover from the last read */
+static SANE_Byte *read_leftover = NULL;
+/* leftover from the last read */
+static SANE_Bool force_nibble = SANE_FALSE;
+
+/* Constants */
+
+/* Colour Modes */
+static const SANE_String_Const cmodes[] = {
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL };
+
+/* bit depths */
+static const SANE_String_Const depths[] = { "8", "12", NULL };
+/* resolutions */
+static const SANE_Int res300[] = {3, 75, 150, 300};
+static const SANE_Int res600[] = {4, 75, 150, 300, 600};
+
+
+/*************************************************************************
+ *
+ * sane_init()
+ *
+ * Initialises data for the list of scanners, stored in canon-p.conf.
+ *
+ * Scanners are not sent any commands until sane_open() is called.
+ *
+ *************************************************************************/
+ SANE_Status
+sane_init (SANE_Int *vc, SANE_Auth_Callback cb)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i, tmp;
+ int tmp_im = INITMODE_AUTO;
+ FILE *fp;
+ char line[81]; /* plus 1 for a null */
+ char *tmp_wf, *tmp_port;
+ CANONP_Scanner *s_tmp;
+
+
+ DBG_INIT();
+
+#if defined PACKAGE && defined VERSION
+ DBG(2, ">> sane_init (version %s null, authorize %s null): " PACKAGE " " VERSION "\n",
+ (vc) ? "!=" : "==", (cb) ? "!=" : "==");
+#endif
+
+ if(vc)
+ *vc = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ DBG(2,"sane_init: >> ieee1284_find_ports\n");
+ /* Find lp ports */
+ tmp = ieee1284_find_ports(&pl, 0);
+ DBG(2,"sane_init: %d << ieee1284_find_ports\n", tmp);
+
+ if (tmp != E1284_OK)
+ {
+ DBG(1,"sane_init: Error trying to get port list\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ if (pl.portc < 1)
+ {
+ DBG(1,"sane_init: Error, no parallel ports found.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(10,"sane_init: %i parallel port(s) found.\n", pl.portc);
+ /* Setup data structures for each port */
+ for(i=0; i<pl.portc; i++)
+ {
+ DBG(10,"sane_init: port %s\n", pl.portv[i]->name);
+ status = init_device(pl.portv[i]);
+ /* Now's a good time to quit if we got an error */
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ /* This should never be true here */
+ if (num_devices == 0)
+ status = SANE_STATUS_IO_ERROR;
+
+ /* just to be extra sure, the line will always have an end: */
+ line[sizeof(line)-1] = '\0';
+
+ /*
+ * Read information from config file: pixel weight location and default
+ * port.
+ */
+ if((fp = sanei_config_open(CANONP_CONFIG_FILE)))
+ {
+ while(sanei_config_read(line, sizeof (line) - 1, fp))
+ {
+ DBG(100, "sane_init: >%s<\n", line);
+ if(line[0] == '#') /* ignore line comments */
+ continue;
+ if(!strlen(line))
+ continue; /* ignore empty lines */
+
+ if(strncmp(line,"calibrate ", 10) == 0)
+ {
+ /* warning: pointer trickyness ahead
+ * Do not free tmp_port! */
+ DBG(40, "sane_init: calibrate line, %s\n",
+ line);
+ tmp_wf = strdup(line+10);
+ tmp_port = strstr(tmp_wf, " ");
+ if ((tmp_port == tmp_wf) || (tmp_port == NULL))
+ {
+ /* They have used an old style config
+ * file which does not specify scanner
+ * Assume first port */
+ DBG(1, "sane_init: old config line:"
+ "\"%s\". Please add "
+ "a port argument.\n",
+ line);
+
+ /* first_dev should never be null here
+ * because we found at least one
+ * parallel port above */
+ first_dev->weights_file = tmp_wf;
+ DBG(100, "sane_init: Successfully "
+ "parsed (old) cal, "
+ "weight file is "
+ "'%s'.\n", tmp_wf);
+ continue;
+
+ }
+
+ /* Now find which scanner wants
+ * this calibration file */
+ s_tmp = first_dev;
+ DBG(100, "sane_init: Finding scanner on port "
+ "'%s'\n", tmp_port+1);
+ while (s_tmp != NULL)
+ {
+ if (!strcmp(s_tmp->params.port->name,
+ tmp_port+1))
+ {
+ DBG(100, "sane_init: Found!\n");
+ /* Now terminate the weight
+ * file string */
+ *tmp_port = '\0';
+ s_tmp->weights_file = tmp_wf;
+ DBG(100, "sane_init: Parsed "
+ "cal, for port"
+ " '%s', weight"
+ " file is '%s'"
+ ".\n",
+ s_tmp->params.
+ port->name,
+ tmp_wf);
+ break;
+ }
+ s_tmp = s_tmp->next;
+ }
+ if (s_tmp == NULL)
+ {
+ /* we made it all the way through the
+ * list and didn't find the port */
+ free(tmp_wf);
+ DBG(10, "sane_init: calibrate line is "
+ "for unknown port!\n");
+ }
+ continue;
+ }
+
+
+ if(strncmp(line,"ieee1284 ", 9) == 0)
+ {
+ DBG(100, "sane_init: Successfully parsed "
+ "default scanner.\n");
+ /* this will be our default scanner */
+ def_scanner = strdup(line+9);
+ continue;
+ }
+
+ if(strncmp(line,"force_nibble", 12) == 0)
+ {
+ DBG(100, "sane_init: force_nibble "
+ "requested.\n");
+ force_nibble = SANE_TRUE;
+ continue;
+ }
+
+ if(strncmp(line,"init_mode ", 10) == 0)
+ {
+
+ /* parse what sort of initialisation mode to
+ * use */
+ if (strncmp(line+10, "FB620P", 6) == 0)
+ tmp_im = INITMODE_20P;
+ else if (strncmp(line+10, "FB630P", 6) == 0)
+ tmp_im = INITMODE_30P;
+ else if (strncmp(line+10, "AUTO", 4) == 0)
+ tmp_im = INITMODE_AUTO;
+
+ /* now work out which port it blongs to */
+
+ tmp_port = strstr(line+10, " ");
+
+ if (tmp_port == NULL)
+ {
+ /* first_dev should never be null here
+ * because we found at least one
+ * parallel port above */
+ first_dev->init_mode = tmp_im;
+ DBG(100, "sane_init: Parsed init-1.\n");
+ continue;
+ }
+
+
+ s_tmp = first_dev;
+ while (s_tmp != NULL)
+ {
+ if (!strcmp(s_tmp->params.port->name,
+ tmp_port+1))
+ {
+ s_tmp->init_mode = tmp_im;
+ DBG(100, "sane_init: Parsed "
+ "init.\n");
+ break;
+ }
+ s_tmp = s_tmp->next;
+ }
+ if (s_tmp == NULL)
+ {
+ /* we made it all the way through the
+ * list and didn't find the port */
+ DBG(10, "sane_init: init_mode line is "
+ "for unknown port!\n");
+ }
+
+ continue;
+ }
+ DBG(1, "sane_init: Unknown configuration command!");
+
+ }
+ fclose (fp);
+ }
+
+ /* There should now be a LL of ports starting at first_dev */
+
+ for (s_tmp = first_dev; s_tmp != NULL; s_tmp = s_tmp->next)
+ {
+ /* Assume there's no scanner present until proven otherwise */
+ s_tmp->scanner_present = SANE_FALSE;
+
+ /* Try to detect if there's a scanner there, and if so,
+ * what sort of scanner it is */
+ status = detect_mode(s_tmp);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(10,"sane_init: Error detecting port mode on %s!\n",
+ s_tmp->params.port->name);
+ s_tmp->scanner_present = SANE_FALSE;
+ continue;
+ }
+
+ /* detect_mode suceeded, so the port is open. This beholdens
+ * us to call ieee1284_close in any of the remaining error
+ * cases in this loop. */
+#if 0
+ tmp = sanei_canon_pp_detect(s_tmp->params.port,
+ s_tmp->init_mode);
+
+
+ if (tmp && (s_tmp->ieee1284_mode != M1284_NIBBLE))
+ {
+ /* A failure, try again in nibble mode... */
+ DBG(1, "sane_init: Failed on ECP mode, falling "
+ "back to nibble mode\n");
+
+ s_tmp->ieee1284_mode = M1284_NIBBLE;
+ sanei_canon_pp_set_ieee1284_mode(s_tmp->ieee1284_mode);
+ tmp = sanei_canon_pp_detect(s_tmp->params.port,
+ s_tmp->init_mode);
+ }
+ /* still no go? */
+ if (tmp)
+ {
+ DBG(1,"sane_init: couldn't find a scanner on port "
+ "%s\n", s_tmp->params.port->name);
+
+ ieee1284_close(s_tmp->params.port);
+ continue;
+ }
+
+#endif
+ /* all signs point to yes, try it out */
+ if (ieee1284_claim(s_tmp->params.port) != E1284_OK) {
+ DBG(10, "sane_init: Couldn't claim port %s.\n",
+ s_tmp->params.port->name);
+
+ ieee1284_close(s_tmp->params.port);
+ continue;
+ }
+
+ DBG(2, "sane_init: >> initialise\n");
+ tmp = sanei_canon_pp_initialise(&(s_tmp->params),
+ s_tmp->init_mode);
+ DBG(2, "sane_init: << %d initialise\n", tmp);
+ if (tmp) {
+ DBG(10, "sane_init: Couldn't contact scanner on port "
+ "%s. Probably no scanner there?\n",
+ s_tmp->params.port->name);
+ ieee1284_release(s_tmp->params.port);
+ ieee1284_close(s_tmp->params.port);
+ s_tmp->scanner_present = SANE_FALSE;
+ continue;
+ }
+
+ /* put it back to sleep until we're ready to
+ * open for business again - this will only work
+ * if we actually have a scanner there! */
+ DBG(100, "sane_init: And back to sleep again\n");
+ sanei_canon_pp_sleep_scanner(s_tmp->params.port);
+
+ /* leave the port open but not claimed - this is regardless
+ * of the return value of initialise */
+ ieee1284_release(s_tmp->params.port);
+
+ /* Finally, we're sure there's a scanner there! Now we
+ * just have to load the weights file...*/
+
+ if (fix_weights_file(s_tmp) != SANE_STATUS_GOOD) {
+ DBG(1, "sane_init: Eeek! fix_weights_file failed for "
+ "scanner on port %s!\n",
+ s_tmp->params.port->name);
+ /* non-fatal.. scans will look ugly as sin unless
+ * they calibrate */
+ }
+
+ /* Cocked, locked and ready to rock */
+ s_tmp->hw.model = s_tmp->params.name;
+ s_tmp->scanner_present = SANE_TRUE;
+ }
+
+ DBG(2, "<< sane_init\n");
+
+ return status;
+}
+
+
+/*************************************************************************
+ *
+ * sane_get_devices()
+ *
+ * Gives a list of devices avaialable. In our case, that's the linked
+ * list produced by sane_init.
+ *
+ *************************************************************************/
+ SANE_Status
+sane_get_devices (const SANE_Device ***dl, SANE_Bool local)
+{
+ static const SANE_Device **devlist;
+ CANONP_Scanner *dev;
+ int i;
+
+ DBG(2, ">> sane_get_devices (%p, %d)\n", (const void*)dl, local);
+
+ if (dl == NULL)
+ {
+ DBG(1, "sane_get_devices: ERROR: devlist pointer is NULL!");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (devlist != NULL)
+ {
+ /* this has been called already */
+ *dl = devlist;
+ return SANE_STATUS_GOOD;
+ }
+ devlist = malloc((num_devices + 1) * sizeof(*devlist));
+ if (devlist == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; dev != NULL; dev = dev->next)
+ {
+ if (dev->scanner_present == SANE_TRUE)
+ {
+ devlist[i] = &(dev->hw);
+ i++;
+ }
+ }
+
+ devlist[i] = NULL;
+
+ *dl = devlist;
+
+ DBG(2, "<< sane_get_devices\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/*************************************************************************
+ *
+ * sane_open()
+ *
+ * Open the scanner described by name. Ask libieee1284 to claim the port
+ * and call Simon's init code. Also configure data structures.
+ *
+ *************************************************************************/
+ SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle *h)
+{
+ CANONP_Scanner *cs;
+ SANE_Range *tmp_range;
+ int tmp;
+
+ DBG(2, ">> sane_open (h=%p, name=\"%s\")\n", (void *)h, name);
+
+ if ((h == NULL) || (name == NULL))
+ {
+ DBG(2,"sane_open: Null pointer received!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!strlen(name))
+ {
+ DBG(10,"sane_open: Empty name given, assuming first/"
+ "default scanner\n");
+ if (def_scanner == NULL)
+ name = first_dev->params.port->name;
+ else
+ name = def_scanner;
+
+ /* we don't _have_ to fit this name, so _don't_ fail if it's
+ * not there */
+
+ cs = first_dev;
+ while((cs != NULL) && strcmp(cs->params.port->name, name))
+ cs = cs->next;
+
+ /* if we didn't find the port they want, or there's no scanner
+ * there, we just want to find _any_ scanner */
+ if ((cs == NULL) || (cs->scanner_present != SANE_TRUE))
+ {
+ cs = first_dev;
+ while((cs != NULL) &&
+ (cs->scanner_present == SANE_FALSE))
+ cs = cs->next;
+ }
+
+ } else {
+
+ /* they're dead keen for this name, so _do_ fail if it's
+ * not there */
+ cs = first_dev;
+ while((cs != NULL) && strcmp(cs->params.port->name, name))
+ cs = cs->next;
+ }
+
+
+ if (cs == NULL)
+ {
+ DBG(2,"sane_open: No scanner found or requested port "
+ "doesn't exist (%s)\n", name);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (cs->scanner_present == SANE_FALSE)
+ {
+ DBG(1,"sane_open: Request to open port with no scanner "
+ "(%s)\n", name);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (cs->opened == SANE_TRUE)
+ {
+ DBG(2,"sane_open; Oi!, That scanner's already open.\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* If the scanner has already been opened once, we don't have to do
+ * this setup again */
+ if (cs->setup == SANE_TRUE)
+ {
+ cs->opened = SANE_TRUE;
+ *h = (SANE_Handle)cs;
+ return SANE_STATUS_GOOD;
+ }
+
+ tmp = ieee1284_claim(cs->params.port);
+ if (tmp != E1284_OK) {
+ DBG(1, "sane_open: Could not claim port!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* I put the scanner to sleep before, better wake it back up */
+
+ DBG(2, "sane_open: >> initialise\n");
+ tmp = sanei_canon_pp_initialise(&(cs->params), cs->init_mode);
+ DBG(2, "sane_open: << %d initialise\n", tmp);
+ if (tmp != 0) {
+ DBG(1, "sane_open: initialise returned %d, something is "
+ "wrong with the scanner!\n", tmp);
+
+ DBG(1, "sane_open: Can't contact scanner. Try power "
+ "cycling scanner, and unplug any "
+ "printers\n");
+ ieee1284_release(cs->params.port);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (cs->weights_file != NULL)
+ DBG(2, "sane_open: >> load_weights(%s, %p)\n",
+ cs->weights_file,
+ (const void *)(&(cs->params)));
+ else
+ DBG(2, "sane_open: >> load_weights(NULL, %p)\n",
+ (const void *)(&(cs->params)));
+ tmp = sanei_canon_pp_load_weights(cs->weights_file, &(cs->params));
+ DBG(2, "sane_open: << %d load_weights\n", tmp);
+
+ if (tmp != 0) {
+ DBG(1, "sane_open: WARNING: Error on load_weights: "
+ "returned %d. This could be due to a corrupt "
+ "calibration file. Try recalibrating and if "
+ "problems persist, please report the problem "
+ "to the canon_pp maintainer\n", tmp);
+ cs->cal_valid = SANE_FALSE;
+ } else {
+ cs->cal_valid = SANE_TRUE;
+ DBG(10, "sane_open: loadweights successful, uploading gamma"
+ " profile...\n");
+ tmp = sanei_canon_pp_adjust_gamma(&(cs->params));
+ if (tmp != 0)
+ DBG(1, "sane_open: WARNING: adjust_gamma returned "
+ "%d!\n", tmp);
+
+ DBG(10, "sane_open: after adjust_gamma Status = %i\n",
+ sanei_canon_pp_check_status(cs->params.port));
+ }
+
+
+ /* Configure ranges etc */
+
+ /* Resolution - determined by magic number */
+
+ if (cs->params.scanheadwidth == 2552)
+ cs->opt[OPT_RESOLUTION].constraint.word_list = res300;
+ else
+ cs->opt[OPT_RESOLUTION].constraint.word_list = res600;
+
+
+ /* TL-X */
+ if(!(tmp_range = malloc(sizeof(*tmp_range))))
+ return SANE_STATUS_NO_MEM;
+ (*tmp_range).min = 0;
+ (*tmp_range).max = 215;
+ cs->opt[OPT_TL_X].constraint.range = tmp_range;
+
+ /* TL-Y */
+ if(!(tmp_range = malloc(sizeof(*tmp_range))))
+ return SANE_STATUS_NO_MEM;
+ (*tmp_range).min = 0;
+ (*tmp_range).max = 296;
+ cs->opt[OPT_TL_Y].constraint.range = tmp_range;
+
+ /* BR-X */
+ if(!(tmp_range = malloc(sizeof(*tmp_range))))
+ return SANE_STATUS_NO_MEM;
+ (*tmp_range).min = 3;
+ (*tmp_range).max = 216;
+ cs->opt[OPT_BR_X].constraint.range = tmp_range;
+
+ /* BR-Y */
+ if(!(tmp_range = malloc(sizeof(*tmp_range))))
+ return SANE_STATUS_NO_MEM;
+ (*tmp_range).min = 1;
+ (*tmp_range).max = 297;
+ cs->opt[OPT_BR_Y].constraint.range = tmp_range;
+
+
+ cs->opened = SANE_TRUE;
+ cs->setup = SANE_TRUE;
+
+ *h = (SANE_Handle)cs;
+
+ DBG(2, "<< sane_open\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*************************************************************************
+ *
+ * sane_get_option_descriptor()
+ *
+ * Return the structure for option number opt.
+ *
+ *************************************************************************/
+ const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int opt)
+{
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+ /*DBG(2, ">> sane_get_option_descriptor (h=%p, opt=%d)\n", h, opt);*/
+
+ if (h == NULL) {
+ DBG(10,"sane_get_option_descriptor: WARNING: h==NULL!\n");
+ return NULL;
+ }
+
+ if ((unsigned)opt >= NUM_OPTIONS) {
+ DBG(10,"sane_get_option_descriptor: Note: opt >= "
+ "NUM_OPTIONS!\n");
+ return NULL;
+ }
+
+ if (cs->opened == SANE_FALSE)
+ {
+ DBG(1,"sane_get_option_descriptor: That scanner (%p) ain't "
+ "open yet\n", h);
+ return NULL;
+ }
+
+ /*DBG(2, "<< sane_get_option_descriptor\n");*/
+
+ return (cs->opt + opt);
+}
+
+
+/*************************************************************************
+ *
+ * sane_control_option()
+ *
+ * Set a value for one of the options provided.
+ *
+ *************************************************************************/
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int opt, SANE_Action act,
+ void *val, SANE_Word *info)
+{
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+ int i = 0, tmp, maxresi;
+
+ DBG(2, ">> sane_control_option (h=%p, opt=%d, act=%d)\n",
+ h,opt,act);
+ /* Do some sanity checks on the parameters
+ * note that val can be null for buttons */
+ if ((h == NULL) || ((val == NULL) && (opt != OPT_CAL)))
+ /* || (info == NULL)) - Don't check this any more..
+ * frontends seem to like passing a null */
+ {
+ DBG(1,"sane_control_option: Frontend passed me a null! "
+ "(h=%p,val=%p,info=%p)\n",(void*)h,
+ val,(void*)info);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (((unsigned)opt) >= NUM_OPTIONS)
+ {
+ DBG(1,"sane_control_option: I don't do option %d.\n", opt);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (cs->opened == SANE_FALSE)
+ {
+ DBG(1,"sane_control_option: That scanner (%p) ain't "
+ "open yet\n", h);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (cs->scanning == SANE_TRUE)
+ {
+ DBG(1,"sane_control_option: That scanner (%p) is scanning!\n",
+ h);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ switch(act)
+ {
+ case SANE_ACTION_GET_VALUE:
+ switch (opt)
+ {
+ case OPT_COLOUR_MODE:
+ strcpy((char *)val,
+ cmodes[cs->vals[opt]]);
+ break;
+ case OPT_DEPTH:
+ strcpy((char *)val,
+ depths[cs->vals[opt]]);
+ break;
+ case OPT_RESOLUTION:
+ *((int *)val) = res600[cs->vals[opt]];
+ break;
+ default:
+ *((int *)val) = cs->vals[opt];
+ break;
+ }
+ break;
+ case SANE_ACTION_SET_VALUE:
+ /* val has been checked for NULL if opt != OPT_CAL */
+ if (opt != OPT_CAL) i = *((int *)val);
+ if (info != NULL) *info = 0;
+ switch (opt) {
+ case OPT_NUM_OPTIONS:
+ /* you can't set that! */
+ return SANE_STATUS_INVAL;
+ case OPT_RESOLUTION:
+ i = cs->vals[opt];
+ cs->vals[opt] = 1;
+ maxresi = cs->opt[OPT_RESOLUTION].
+ constraint.word_list[0];
+
+ while ((cs->vals[opt] <= maxresi) &&
+ (res600[cs->vals[opt]]
+ < *((int *)val)))
+ {
+ cs->vals[opt] += 1;
+ }
+
+ if (res600[cs->vals[opt]] !=
+ *((int *)val))
+ {
+ if (info != NULL) *info |=
+ SANE_INFO_INEXACT;
+ }
+ break;
+ case OPT_COLOUR_MODE:
+ cs->vals[opt] = 0;
+ while ((cmodes[cs->vals[opt]] != NULL)
+ && strcmp(cmodes[cs->vals[opt]],
+ (char *)val))
+ {
+ cs->vals[opt] += 1;
+ }
+ if (info != NULL) *info |=
+ SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_DEPTH:
+ cs->vals[opt] = 0;
+ while ((depths[cs->vals[opt]] != NULL)
+ && strcmp(depths[cs->vals[opt]],
+ (char *)val))
+ {
+ cs->vals[opt] += 1;
+ }
+ if (info != NULL) *info |=
+ SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ if ((i<cs->opt[opt].constraint.range->min) || (i>cs->opt[opt].constraint.range->max))
+ return SANE_STATUS_INVAL;
+ cs->vals[opt] = i;
+ break;
+ case OPT_CAL:
+ /* Call the calibration code */
+ if ((cs->weights_file==NULL) ||
+ cs->cal_readonly
+ )
+ DBG(2, ">> calibrate(x, "
+ "NULL)\n");
+ else
+ DBG(2, ">> calibrate(x,"
+ "%s)\n",
+ cs->weights_file);
+
+ if (cs->cal_readonly) tmp =
+ sanei_canon_pp_calibrate(
+ &(cs->params),
+ NULL);
+ else tmp = sanei_canon_pp_calibrate(
+ &(cs->params),
+ cs->weights_file);
+
+ DBG(2, "<< %d calibrate\n",
+ tmp);
+ if (tmp != 0) {
+ DBG(1, "sane_control_option: "
+ "WARNING: "
+ "calibrate "
+ "returned %d!",
+ tmp);
+ cs->cal_valid =
+ SANE_FALSE;
+ return SANE_STATUS_IO_ERROR;
+ } else {
+ cs->cal_valid =
+ SANE_TRUE;
+ }
+
+ break;
+ /*case OPT_PREVIEW:
+ if (i) cs->vals[opt] = 1;
+ else cs->vals[opt] = 0;
+ break;*/
+ default:
+ /* Should never happen */
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case SANE_ACTION_SET_AUTO:
+ DBG(2, "sane_control_option: attempt at "
+ "automatic control! (unsupported)\n");
+ /* Auto? are they mad? I'm not that smart! */
+ /* fall through. */
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+
+ DBG(2, "<< sane_control_option\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/*************************************************************************
+ *
+ * sane_get_parameters()
+ *
+ * Get information about the next packet. If a scan hasn't started, results
+ * only have to be best guesses.
+ *
+ *************************************************************************/
+ SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters *params)
+{
+ int res, max_width, max_height, max_res;
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+ DBG(2, ">> sane_get_parameters (h=%p, params=%p)\n", (void*)h,
+ (void*)params);
+
+ if (h == NULL) return SANE_STATUS_INVAL;
+
+ if (cs->opened == SANE_FALSE)
+ {
+ DBG(1,"sane_get_parameters: That scanner (%p) ain't "
+ "open yet\n", h);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* We use 600 res list here because the 300 res list is just a shorter
+ * version, so this will always work. */
+ res = res600[cs->vals[OPT_RESOLUTION]];
+
+ /*
+ * These don't change whether we're scanning or not
+ * NOTE: Assumes options don't change after scanning commences, which
+ * is part of the standard
+ */
+
+ /* Copy the options stored in the vals into the scaninfo */
+ params->pixels_per_line =
+ ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) * res) / MM_PER_IN;
+ params->lines = ((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) * res)
+ / MM_PER_IN;
+
+ /* FIXME: Magic numbers ahead! */
+
+ max_res = cs->params.scanheadwidth == 2552 ? 300 : 600;
+
+ /* x values have to be divisible by 4 (round down) */
+ params->pixels_per_line -= (params->pixels_per_line%4);
+
+ /* Can't scan less than 64 */
+ if (params->pixels_per_line < 64) params->pixels_per_line = 64;
+
+ max_width = cs->params.scanheadwidth / (max_res / res);
+
+ max_height = (cs->params.scanheadwidth == 2552 ? 3508 : 7016) /
+ (max_res / res);
+
+ if(params->pixels_per_line > max_width)
+ params->pixels_per_line = max_width;
+ if(params->lines > max_height) params->lines = max_height;
+
+
+ params->depth = cs->vals[OPT_DEPTH] ? 16 : 8;
+
+ switch (cs->vals[OPT_COLOUR_MODE])
+ {
+ case 0:
+ params->format = SANE_FRAME_GRAY;
+ break;
+ case 1:
+ params->format = SANE_FRAME_RGB;
+ break;
+ default:
+ /* shouldn't happen */
+ break;
+ }
+
+
+ if (!(params->pixels_per_line)) {
+ params->last_frame = SANE_TRUE;
+ params->lines = 0;
+ }
+
+ /* Always the "last frame" */
+ params->last_frame = SANE_TRUE;
+
+ params->bytes_per_line = params->pixels_per_line * (params->depth/8) *
+ (cs->vals[OPT_COLOUR_MODE] ? 3 : 1);
+
+ DBG(10, "get_params: bytes_per_line=%d, pixels_per_line=%d, lines=%d\n"
+ "max_res=%d, res=%d, max_height=%d, br_y=%d, tl_y=%d, "
+ "mm_per_in=%f\n",
+ params->bytes_per_line, params->pixels_per_line, params->lines,
+ max_res, res, max_height, cs->vals[OPT_BR_Y],
+ cs->vals[OPT_TL_Y], MM_PER_IN);
+
+ DBG(2, "<< sane_get_parameters\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/*************************************************************************
+ *
+ * sane_start()
+ *
+ * Starts scanning an image.
+ *
+ *************************************************************************/
+ SANE_Status
+sane_start (SANE_Handle h)
+{
+ unsigned int i, res, max_width, max_height, max_res, tmp;
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+ DBG(2, ">> sane_start (h=%p)\n", h);
+
+ if (h == NULL) return SANE_STATUS_INVAL;
+
+ if (cs->scanning) return SANE_STATUS_DEVICE_BUSY;
+ if (cs->opened == SANE_FALSE)
+ {
+ DBG(1,"sane_start: That scanner (%p) ain't "
+ "open yet\n", h);
+ return SANE_STATUS_INVAL;
+ }
+
+
+ /* We use 600 res list here because the 300 res list is just a shorter
+ * version, so this will always work. */
+ res = res600[cs->vals[OPT_RESOLUTION]];
+
+ /* Copy the options stored in the vals into the scaninfo */
+ cs->scan.width = ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) * res)
+ / MM_PER_IN;
+ cs->scan.height = ((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) * res)
+ / MM_PER_IN;
+
+ cs->scan.xoffset = (cs->vals[OPT_TL_X] * res) / MM_PER_IN;
+ cs->scan.yoffset = (cs->vals[OPT_TL_Y] * res) / MM_PER_IN;
+
+ /*
+ * These values have to pass the requirements of not exceeding
+ * dimensions (simple clipping) and both width values have to be some
+ * integer multiple of 4
+ */
+
+ /* FIXME: Magic numbers ahead! */
+
+ max_res = cs->params.scanheadwidth == 2552 ? 300 : 600;
+
+ /* x values have to be divisible by 4 (round down) */
+ cs->scan.width -= (cs->scan.width%4);
+ cs->scan.xoffset -= (cs->scan.xoffset%4);
+
+ /* Can't scan less than 64 */
+ if (cs->scan.width < 64) cs->scan.width = 64;
+
+ max_width = cs->params.scanheadwidth / (max_res / res);
+
+ max_height = (cs->params.scanheadwidth == 2552 ? 3508 : 7016) /
+ (max_res / res);
+
+ if (cs->scan.width > max_width) cs->scan.width = max_width;
+ if (cs->scan.width + cs->scan.xoffset > max_width) cs->scan.xoffset =
+ max_width - cs->scan.width;
+ if (cs->scan.height > max_height) cs->scan.height = max_height;
+
+ /* We pass a value to init_scan which is the power of 2 that 75
+ * is multiplied by for the resolution. ie:
+ * 75 -> 0
+ * 150 -> 1
+ * 300 -> 2
+ * 600 -> 4
+ *
+ * This rather strange parameter is a result of the way the scanner
+ * takes its resolution argument
+ */
+
+ i = 0;
+ while (res > 75)
+ {
+ i++;
+ res = res >> 1;
+ }
+
+ /* FIXME? xres == yres for now. */
+ cs->scan.xresolution = i;
+ cs->scan.yresolution = i;
+
+ if (((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) <= 0) ||
+ ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) <= 0))
+ {
+ DBG(1,"sane_start: height = %d, Width = %d. "
+ "Can't scan void range!",
+ cs->scan.height, cs->scan.width);
+ return SANE_STATUS_INVAL;
+ }
+
+ cs->scan.mode = cs->vals[OPT_COLOUR_MODE];
+
+ DBG(10, ">> init_scan()\n");
+ tmp = sanei_canon_pp_init_scan(&(cs->params), &(cs->scan));
+ DBG(10, "<< %d init_scan\n", tmp);
+
+ if (tmp != 0) {
+ DBG(1,"sane_start: WARNING: init_scan returned %d!", tmp);
+ return SANE_STATUS_IO_ERROR;
+ }
+ cs->scanning = SANE_TRUE;
+ cs->cancelled = SANE_FALSE;
+ cs->sent_eof = SANE_FALSE;
+ cs->lines_scanned = 0;
+ cs->bytes_sent = 0;
+
+ DBG(2, "<< sane_start\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*************************************************************************
+ *
+ * sane_read()
+ *
+ * Reads some information from the buffer.
+ *
+ *************************************************************************/
+ SANE_Status
+sane_read (SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
+{
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+ image_segment *is;
+ unsigned int lines, bytes, bpl;
+ unsigned int i;
+ short *shortptr;
+ SANE_Byte *charptr;
+ int tmp;
+
+ static SANE_Byte *lbuf;
+ static unsigned int bytesleft;
+
+ DBG(2, ">> sane_read (h=%p, buf=%p, maxlen=%d)\n", h,
+ (const void *)buf, maxlen);
+
+ /* default to returning 0 - for errors */
+ *lenp = 0;
+
+ if ((h == NULL) || (buf == NULL) || (lenp == NULL))
+ {
+ DBG(1, "sane_read: This frontend's passing me dodgy gear! "
+ "(h=%p, buf=%p, lenp=%p)\n",
+ (void*)h, (void*)buf, (void*)lenp);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Now we have to see if we have some leftover from last time */
+
+ if (read_leftover != NULL)
+ {
+ /* feed some more data in until we've run out - don't care
+ * whether or not we _think_ the scanner is scanning now,
+ * because we may still have data left over to send */
+ DBG(200, "sane_read: didn't send it all last time\n");
+
+ /* Now feed it some data from lbuf */
+ if (bytesleft <= (unsigned int)maxlen)
+ {
+ /* enough buffer to send the lot */
+ memcpy(buf, read_leftover, bytesleft);
+ free(lbuf);
+ *lenp = bytesleft;
+ lbuf = NULL;
+ read_leftover = NULL;
+ bytesleft = 0;
+ cs->bytes_sent += bytesleft;
+ return SANE_STATUS_GOOD;
+
+ } else {
+ /* only enough to send maxlen */
+ memcpy(buf, read_leftover, maxlen);
+ read_leftover += maxlen;
+ bytesleft -= maxlen;
+ *lenp = maxlen;
+ cs->bytes_sent += maxlen;
+ DBG(100, "sane_read: sent %d bytes, still have %d to "
+ "go\n", maxlen, bytesleft);
+ return SANE_STATUS_GOOD;
+ }
+
+ }
+
+
+ /* Has the last scan ended (other than by cancelling)? */
+ if (((unsigned)cs->scan.height <= (unsigned)cs->lines_scanned)
+ || (cs->sent_eof) || !(cs->scanning))
+ {
+ cs->sent_eof = SANE_TRUE;
+ cs->scanning = SANE_FALSE;
+ cs->cancelled = SANE_FALSE;
+ cs->lines_scanned = 0;
+ cs->bytes_sent = 0;
+ read_leftover = NULL;
+ return SANE_STATUS_EOF;
+ }
+
+ /* At this point we have to read more data from the scanner - or the
+ * scan has been cancelled, which means we have to call read_segment
+ * to leave the scanner consistant */
+
+ /* Decide how many lines we can fit into this buffer */
+ if (cs->vals[OPT_DEPTH] == 0)
+ bpl = cs->scan.width * (cs->vals[OPT_COLOUR_MODE] ? 3 : 1);
+ else
+ bpl = cs->scan.width * (cs->vals[OPT_COLOUR_MODE] ? 6 : 2);
+
+ /* New way: scan a whole scanner buffer full, and return as much as
+ * the frontend wants. It's faster and more reliable since the
+ * scanners crack the shits if we ask for too many small packets */
+ lines = (BUF_MAX * 4 / 5) / bpl;
+
+ if (lines > (cs->scan.height - cs->lines_scanned))
+ lines = cs->scan.height - cs->lines_scanned;
+
+ if (!lines)
+ {
+ /* can't fit a whole line into the buffer
+ * (should never happen!) */
+ lines = 1;
+ }
+
+ bytes = lines * bpl;
+
+ /* Allocate a local buffer to hold the data while we play */
+ if ((lbuf = malloc(bytes)) == NULL)
+ {
+ DBG(10, "sane_read: Not enough memory to hold a "
+ "local buffer. You're doomed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+
+ /* This call required a lot of debugging information.. */
+ DBG(10, "sane_read: Here's what we're sending read_segment:\n");
+ DBG(10, "scanner setup: shw=%d xres=%d yres=%d %d %d id=%s\n",
+ cs->params.scanheadwidth,
+ cs->params.natural_xresolution,
+ cs->params.natural_yresolution,
+ cs->params.max_xresolution,
+ cs->params.max_yresolution,
+ (cs->params.id_string)+8);
+ DBG(10, "scan_params->: width=%d, height=%d, xoffset=%d, "
+ "yoffset=%d\n\txresolution=%d, yresolution=%d, "
+ "mode=%d, (lines=%d)\n",
+ cs->scan.width, cs->scan.height,
+ cs->scan.xoffset, cs->scan.yoffset,
+ cs->scan.xresolution, cs->scan.yresolution,
+ cs->scan.mode, lines);
+
+ DBG(2, ">> read_segment(x, x, x, %d, %d, %d)\n",
+ lines, cs->cal_valid,
+ cs->scan.height - cs->lines_scanned);
+ tmp = sanei_canon_pp_read_segment(&is, &(cs->params), &(cs->scan),
+ lines, cs->cal_valid,
+ cs->scan.height - cs->lines_scanned);
+ DBG(2, "<< %d read_segment\n", tmp);
+
+ if (tmp != 0) {
+ if (cs->cancelled)
+ {
+ DBG(10, "sane_read: cancelling.\n");
+ cs->sent_eof = SANE_TRUE;
+ cs->scanning = SANE_FALSE;
+ read_leftover = NULL;
+ sanei_canon_pp_abort_scan(&(cs->params));
+ return SANE_STATUS_CANCELLED;
+ }
+ DBG(1, "sane_read: WARNING: read_segment returned %d!\n", tmp);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(10, "sane_read: bpl=%d, lines=%d, bytes=%d\n", bpl, lines, bytes);
+
+ cs->lines_scanned += lines;
+
+ /* translate data out of buffer */
+ if (cs->vals[OPT_DEPTH] == 0)
+ {
+ /* 8bpp */
+ for(i = 0; i < bytes; i++)
+ {
+ charptr = lbuf + i;
+ if (cs->vals[OPT_COLOUR_MODE])
+ {
+ if (i % 3 == 0) charptr += 2;
+ if (i % 3 == 2) charptr -= 2;
+ }
+ *charptr = *((char *)(is->image_data) + (i*2));
+ }
+ }
+ else
+ {
+ /* 16bpp */
+ for(i = 0; i < (bytes/2); i++)
+ {
+ shortptr = ((short *)lbuf + i);
+ if (cs->vals[OPT_COLOUR_MODE])
+ {
+ if (i % 3 == 0) shortptr += 2;
+ if (i % 3 == 2) shortptr -= 2;
+ }
+ *shortptr = MAKE_SHORT(
+ *((char *)(is->image_data) + (i*2)),
+ *((char *)(is->image_data) + (i*2)+1)
+ );
+ }
+ }
+
+ /* Free data structures allocated in read_segment */
+ free(is->image_data);
+ free(is);
+
+ /* Now feed it some data from lbuf */
+ if (bytes <= (unsigned int)maxlen)
+ {
+ /* enough buffer to send the lot */
+ memcpy(buf, lbuf, bytes);
+ *lenp = bytes;
+ free(lbuf);
+ lbuf = NULL;
+ read_leftover = NULL;
+ bytesleft = 0;
+ cs->bytes_sent += bytes;
+
+ } else {
+ /* only enough to send maxlen */
+ memcpy(buf, lbuf, maxlen);
+ *lenp = maxlen;
+ read_leftover = lbuf + maxlen;
+ bytesleft = bytes - maxlen;
+ cs->bytes_sent += maxlen;
+ DBG(100, "sane_read: sent %d bytes, still have %d to go\n",
+ maxlen, bytesleft);
+ }
+
+ if ((unsigned)cs->lines_scanned >= cs->scan.height)
+ {
+ /* The scan is over! Don't need to call anything in the
+ * hardware, it will sort itself out */
+ DBG(10, "sane_read: Scan is finished.\n");
+ cs->scanning = SANE_FALSE;
+ cs->lines_scanned = 0;
+ cs->bytes_sent = 0;
+ }
+
+ DBG(2, "<< sane_read\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/*************************************************************************
+ *
+ * sane_cancel()
+ *
+ * Cancels a scan in progress
+ *
+ *************************************************************************/
+ void
+sane_cancel (SANE_Handle h)
+{
+ /* Note: assume handle is valid apart from NULLs */
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+
+ DBG(2, ">> sane_cancel (h=%p)\n", h);
+ if (h == NULL) return;
+
+ read_leftover = NULL;
+
+ if (!(cs->scanning))
+ {
+ DBG(2, "<< sane_cancel (not scanning)\n");
+ return;
+ }
+
+ cs->cancelled = SANE_TRUE;
+ cs->params.abort_now = 1;
+
+ DBG(2, "<< sane_cancel\n");
+}
+
+
+/*************************************************************************
+ *
+ * sane_close()
+ *
+ * Closes a scanner handle. Scanner is assumed to be free after this.
+ *
+ *************************************************************************/
+ void
+sane_close (SANE_Handle h)
+{
+ /* Note: assume handle is valid apart from NULLs */
+ CANONP_Scanner *cs = ((CANONP_Scanner *)h);
+ DBG(2, ">> sane_close (h=%p)\n", h);
+ if (h == NULL) return;
+
+ if (cs->opened == SANE_FALSE)
+ {
+ DBG(1,"sane_close: That scanner (%p) ain't "
+ "open yet\n", h);
+ return;
+ }
+
+ /* Put scanner back in transparent mode */
+ sanei_canon_pp_close_scanner(&(cs->params));
+
+ cs->opened = SANE_FALSE;
+
+ /* if it was scanning, it's not any more */
+ cs->scanning = SANE_FALSE;
+ cs->sent_eof = SANE_TRUE;
+
+ ieee1284_release(cs->params.port);
+
+ DBG(2, "<< sane_close\n");
+}
+
+
+/*************************************************************************
+ *
+ * sane_exit()
+ *
+ * Shut it down!
+ *
+ *************************************************************************/
+ void
+sane_exit (void)
+{
+ CANONP_Scanner *dev, *next;
+
+ DBG(2, ">> sane_exit\n");
+
+ for (dev = first_dev; dev != NULL; dev = next)
+ {
+ next = dev->next;
+
+ /* These were only created if the scanner has been init'd */
+
+ /* Should normally nullify pointers after freeing, but in
+ * this case we're about to free the whole structure so
+ * theres not a lot of point. */
+
+ /* Constraints (mostly) allocated when the scanner is opened */
+ if(dev->opt[OPT_TL_X].constraint.range)
+ free((void *)(dev->opt[OPT_TL_X].constraint.range));
+ if(dev->opt[OPT_TL_Y].constraint.range)
+ free((void *)(dev->opt[OPT_TL_Y].constraint.range));
+ if(dev->opt[OPT_BR_X].constraint.range)
+ free((void *)(dev->opt[OPT_BR_X].constraint.range));
+ if(dev->opt[OPT_BR_Y].constraint.range)
+ free((void *)(dev->opt[OPT_BR_Y].constraint.range));
+
+ /* Weights file now on a per-scanner basis */
+ if (dev->weights_file != NULL)
+ free(dev->weights_file);
+
+ if (dev->scanner_present)
+ {
+ if (dev->opened == SANE_TRUE)
+ {
+ /* naughty boys, should have closed first */
+ ieee1284_release(dev->params.port);
+ }
+ ieee1284_close(dev->params.port);
+ }
+
+ free (dev);
+ }
+
+ first_dev = NULL;
+ def_scanner = NULL;
+ read_leftover = NULL;
+ num_devices = 0;
+
+ /* FIXEDME: this created a segfault in DLL code. */
+ /* Bug was fixed in libieee1284 0.1.5 */
+ ieee1284_free_ports(&pl);
+
+ DBG(2, "<< sane_exit\n");
+}
+
+
+/*************************************************************************
+ *
+ * init_device()
+ *
+ * (Not part of the SANE API)
+ *
+ * Initialises a CANONP_Scanner data structure for a new device.
+ * NOTE: The device is not ready to scan until initialise() has been
+ * called in scan library!
+ *
+ *************************************************************************/
+static SANE_Status init_device(struct parport *pp)
+{
+ int i;
+ static const char *hw_vendor = "CANON";
+ static const char *hw_type = "flatbed scanner";
+ static const char *opt_names[] = {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_NAME_SCAN_RESOLUTION,
+ SANE_NAME_SCAN_MODE,
+ SANE_NAME_BIT_DEPTH,
+ SANE_NAME_SCAN_TL_X,
+ SANE_NAME_SCAN_TL_Y,
+ SANE_NAME_SCAN_BR_X,
+ SANE_NAME_SCAN_BR_Y,
+ SANE_NAME_QUALITY_CAL
+#if 0
+ SANE_NAME_GAMMA_R,
+ SANE_NAME_GAMMA_G,
+ SANE_NAME_GAMMA_B
+#endif
+ };
+ static const char *opt_titles[] = {
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_TITLE_SCAN_RESOLUTION,
+ SANE_TITLE_SCAN_MODE,
+ SANE_TITLE_BIT_DEPTH,
+ SANE_TITLE_SCAN_TL_X,
+ SANE_TITLE_SCAN_TL_Y,
+ SANE_TITLE_SCAN_BR_X,
+ SANE_TITLE_SCAN_BR_Y,
+ SANE_TITLE_QUALITY_CAL
+#if 0
+ SANE_TITLE_GAMMA_R,
+ SANE_TITLE_GAMMA_G,
+ SANE_TITLE_GAMMA_B
+#endif
+ };
+ static const char *opt_descs[] = {
+ SANE_DESC_NUM_OPTIONS,
+ SANE_DESC_SCAN_RESOLUTION,
+ SANE_DESC_SCAN_MODE,
+ SANE_DESC_BIT_DEPTH,
+ SANE_DESC_SCAN_TL_X,
+ SANE_DESC_SCAN_TL_Y,
+ SANE_DESC_SCAN_BR_X,
+ SANE_DESC_SCAN_BR_Y,
+ SANE_DESC_QUALITY_CAL
+#if 0
+ SANE_DESC_GAMMA_R,
+ SANE_DESC_GAMMA_G,
+ SANE_DESC_GAMMA_B
+#endif
+ };
+
+ CANONP_Scanner *cs = NULL;
+
+ DBG(2, ">> init_device\n");
+
+ cs = malloc(sizeof(*cs));
+ if (cs == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(cs, 0, sizeof(*cs));
+
+#if 0
+ if ((cs->params.port = malloc(sizeof(*(cs->params.port)))) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy(cs->params.port, pp, sizeof(*pp));
+#endif
+
+ cs->params.port = pp;
+
+ /* ensure these are null to start off with, otherwise they might be
+ * erroneously free'd. Note that we set everything to 0 above
+ * but that's not *always* the same thing */
+ cs->params.blackweight = NULL;
+ cs->params.redweight = NULL;
+ cs->params.greenweight = NULL;
+ cs->params.blueweight = NULL;
+
+ /* Set some sensible defaults */
+ cs->hw.name = cs->params.port->name;
+ cs->hw.vendor = hw_vendor;
+ cs->hw.type = hw_type;
+ cs->opened = SANE_FALSE;
+ cs->scanning = SANE_FALSE;
+ cs->cancelled = SANE_FALSE;
+ cs->sent_eof = SANE_TRUE;
+ cs->lines_scanned = 0;
+ cs->bytes_sent = 0;
+ cs->init_mode = INITMODE_AUTO;
+
+ DBG(10, "init_device: [configuring options]\n");
+
+ /* take a punt at each option, then we change it later */
+ for (i = 0; i < NUM_OPTIONS; i++)
+ {
+ cs->opt[i].name = opt_names[i];
+ cs->opt[i].title = opt_titles[i];
+ cs->opt[i].desc = opt_descs[i];
+ cs->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ cs->opt[i].type = SANE_TYPE_INT;
+ cs->opt[i].size = sizeof(SANE_Int);
+ }
+
+ DBG(100, "init_device: configuring opt: num_options\n");
+ /* The number of options option */
+
+ cs->opt[OPT_NUM_OPTIONS].unit = SANE_UNIT_NONE;
+ cs->opt[OPT_NUM_OPTIONS].cap = SANE_CAP_SOFT_DETECT;
+ cs->vals[OPT_NUM_OPTIONS] = NUM_OPTIONS;
+
+ DBG(100, "init_device: configuring opt: resolution\n");
+
+ /* The resolution of scanning (X res == Y res for now)*/
+ cs->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ cs->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ /* should never point at first element (wordlist size) */
+ cs->vals[OPT_RESOLUTION] = 1;
+
+ DBG(100, "init_device: configuring opt: colour mode\n");
+
+ /* The colour mode (0=grey 1=rgb) */
+ cs->opt[OPT_COLOUR_MODE].type = SANE_TYPE_STRING;
+ cs->opt[OPT_COLOUR_MODE].size = 20;
+ cs->opt[OPT_COLOUR_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ /* Set this one here because it doesn't change by scanner (yet) */
+ cs->opt[OPT_COLOUR_MODE].constraint.string_list = cmodes;
+
+ DBG(100, "init_device: configuring opt: bit depth\n");
+
+ /* The bit depth */
+ cs->opt[OPT_DEPTH].type = SANE_TYPE_STRING;
+ cs->opt[OPT_DEPTH].size = 20;
+ cs->opt[OPT_DEPTH].cap |= SANE_CAP_EMULATED;
+ cs->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ cs->opt[OPT_DEPTH].constraint.string_list = depths;
+
+ DBG(100, "init_device: configuring opt: tl-x\n");
+
+ /* The top-left-x */
+ cs->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ cs->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ DBG(100, "init_device: configuring opt: tl-y\n");
+
+ /* The top-left-y */
+ cs->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ cs->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ DBG(100, "init_device: configuring opt: br-x\n");
+
+ /* The bottom-right-x */
+ cs->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ cs->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ /* default scan width */
+ cs->vals[OPT_BR_X] = 100;
+
+ DBG(100, "init_device: configuring opt: br-y\n");
+
+ /* The bottom-right-y */
+ cs->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ cs->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ cs->vals[OPT_BR_Y] = 100;
+
+ DBG(100, "init_device: configuring opt: calibrate\n");
+
+ /* The calibration button */
+ cs->opt[OPT_CAL].type = SANE_TYPE_BUTTON;
+ cs->opt[OPT_CAL].constraint_type = SANE_CONSTRAINT_NONE;
+ if (cs->cal_readonly)
+ cs->opt[OPT_CAL].cap |= SANE_CAP_INACTIVE;
+
+#if 0
+ /* the gamma values (once we do them) */
+ cs->opt[OPT_GAMMA_R].caps |= SANE_CAP_ADVANCED;
+ cs->opt[OPT_GAMMA_G].caps |= SANE_CAP_ADVANCED;
+ cs->opt[OPT_GAMMA_B].caps |= SANE_CAP_ADVANCED;
+#endif
+
+ /*
+ * NOTE: Ranges and lists are actually set when scanner is opened,
+ * becase that's when we find out what sort of scanner it is
+ */
+
+ DBG(100, "init_device: done opts\n");
+
+ /* add it to the head of the tree */
+ cs->next = first_dev;
+ first_dev = cs;
+
+ num_devices++;
+
+ DBG(2, "<< init_device\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*************************************************************************
+ *
+ * These two are optional ones... maybe if I get really keen?
+ *
+ *************************************************************************/
+ SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG(2, ">> sane_set_io_mode (%p, %d) (not really supported)\n",
+ h, non_blocking);
+
+ if (non_blocking == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+
+ DBG(2, "<< sane_set_io_mode\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+ SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG(2, ">> sane_get_select_fd (%p, %p) (not supported)\n", h,
+ (const void *)fdp);
+ DBG(2, "<< sane_get_select_fd\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/*************************************************************************
+ *
+ * init_cal(): Try to create a calibration file
+ * has to be changed.
+ *
+ ************************************************************************/
+static int init_cal(char *file)
+{
+ char *tmp, *path;
+ int f, i;
+
+ if ((f = open(file, O_CREAT | O_WRONLY, 0600)) < 0)
+ {
+ if (errno == ENOENT)
+ {
+ /* we need to try and make ~/.sane perhaps -
+ * find the last / in the file path, and try
+ * to create it */
+ if ((tmp = strrchr(file, '/')) == NULL)
+ return -1;
+ path = strdup(file);
+ *(path + (tmp-file)) = '\0';
+ i = mkdir(path, 0777);
+ free(path);
+ if (i) return -1;
+ /* Path has been created, now try this again.. */
+ if ((f = open(file, O_CREAT | O_WRONLY, 0600)) < 0)
+ return -1;
+ }
+ else
+ {
+ /* Error is something like access denied - too
+ * hard to fix, so i give up... */
+ return -1;
+ }
+ }
+ /* should probably set defaults here.. */
+ close(f);
+ return 0;
+}
+
+/*************************************************************************
+ *
+ * fix_weights_file(): Ensures that the weights_file setting for a given
+ * scanner is valid
+ *
+ ************************************************************************/
+static SANE_Status fix_weights_file(CANONP_Scanner *cs)
+{
+ char *tmp, *myhome, buf[PATH_MAX];
+ int i;
+ struct stat *f_stat;
+
+
+ if (cs == NULL)
+ {
+ DBG(0, "fix_weights_file: FATAL: NULL passed by my code, "
+ "please report this!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Assume this is false and then correct it */
+ cs->cal_readonly = SANE_FALSE;
+
+ if (cs->weights_file == NULL)
+ {
+ /* Will be of form canon_pp-calibration-parport0 or -0x378 */
+ sprintf(buf, "~/.sane/canon_pp-calibration-%s",
+ cs->params.port->name);
+ cs->weights_file = strdup(buf);
+ }
+
+ /* Get the user's home dir if they used ~ */
+ if (cs->weights_file[0] == '~')
+ {
+ if ((tmp = malloc(PATH_MAX)) == NULL)
+ return SANE_STATUS_NO_MEM;
+ if ((myhome = getenv("HOME")) == NULL)
+ {
+ DBG(0,"fix_weights_file: FATAL: ~ used, but $HOME not"
+ " set!\n");
+ free(tmp);
+ tmp = NULL;
+ return SANE_STATUS_INVAL;
+ }
+ strncpy(tmp, myhome, PATH_MAX);
+ strncpy(tmp+strlen(tmp), (cs->weights_file)+1,
+ PATH_MAX-strlen(tmp));
+
+ free(cs->weights_file);
+ cs->weights_file = tmp;
+ }
+
+ if ((f_stat = malloc(sizeof(*f_stat))) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ if(stat(cs->weights_file, f_stat))
+ {
+ /* this non-intuitive if basically is if we got some error that
+ * wasn't no-such-file, or we can't create the file.. */
+ if ((errno != ENOENT) || init_cal(cs->weights_file))
+ {
+ /* Some nasty error returned. Give up. */
+ DBG(2,"fix_weights_file: error stating cal file"
+ " (%s)\n", strerror(errno));
+ DBG(2,"fix_weights_file: Changes to cal data won't"
+ " be saved!\n");
+ free(cs->weights_file);
+ cs->weights_file = NULL;
+ }
+ }
+ else
+ {
+
+ /* No error returned.. Check read/writability */
+ i = open(cs->weights_file, O_RDWR | O_APPEND);
+ if (i <= 0)
+ {
+ DBG(10,"fix_weighs_file: Note: Changes to cal data "
+ "won't be saved!\n");
+ i = open(cs->weights_file, O_RDONLY);
+ if (i <= 0)
+ {
+ /*
+ * Open failed (do i care why?)
+ */
+ DBG(2,"fix_weights_file: error opening cal "
+ "(%s)\n", strerror(errno));
+ free(cs->weights_file);
+ cs->weights_file = NULL;
+ }
+ else
+ {
+ DBG(2,"fix_weights_file: file is read-only, "
+ "changes won't be saved\n");
+ cs->cal_readonly = SANE_TRUE;
+ close(i);
+ }
+ }
+ else
+ {
+ /* good! */
+ DBG(10,"fix_weights_file: Calibration file is good "
+ "for opening!\n");
+ close(i);
+ }
+ }
+
+ /* cleanup */
+ free(f_stat);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* detect_mode
+ * PRE:
+ * cs->params.port is not open
+ * POST:
+ * cs->params.port is left opened iff SANE_STATUS_GOOD returned.
+ */
+
+SANE_Status detect_mode(CANONP_Scanner *cs)
+{
+
+ int capabilities, tmp;
+
+ /* Open then claim parallel port using libieee1284 */
+ DBG(10,"detect_mode: Opening port %s\n", (cs->params.port->name));
+
+ tmp = ieee1284_open(cs->params.port, 0, &capabilities);
+
+ if (tmp != E1284_OK)
+ {
+ switch (tmp)
+ {
+ case E1284_INVALIDPORT:
+ DBG(1, "detect_mode: Invalid port.\n");
+ break;
+ case E1284_SYS:
+ DBG(1, "detect_mode: System error: %s\n",
+ strerror(errno));
+ break;
+ case E1284_INIT:
+ DBG(1, "detect_mode: Initialisation error.\n");
+ break;
+ default:
+ DBG(1, "detect_mode: Unknown error.\n");
+ break;
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(10,"detect_mode: Claiming port.\n");
+
+ if (ieee1284_claim(cs->params.port) != E1284_OK)
+ {
+ DBG(1,"detect_mode: Unable to claim port\n");
+ ieee1284_close(cs->params.port);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ /* Check that compatibility-mode (required) is supported */
+ if (!(capabilities & CAP1284_COMPAT))
+ {
+ DBG(0,"detect_mode: Compatibility mode (required) not "
+ "supported.\n");
+ ieee1284_release(cs->params.port);
+ ieee1284_close(cs->params.port);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Check capabilities which will enchance speed */
+ if (capabilities & CAP1284_ECP)
+ DBG(2, "detect_mode: Port supports ECP-H.\n");
+ else if (capabilities & CAP1284_ECPSWE)
+ DBG(2, "detect_mode: Port supports ECP-S.\n");
+ if (capabilities & CAP1284_IRQ)
+ DBG(2, "detect_mode: Port supports interrupts.\n");
+ if (capabilities & CAP1284_DMA)
+ DBG(2, "detect_mode: Port supports DMA.\n");
+
+ /* Check whether ECP mode is possible */
+ if (capabilities & CAP1284_ECP)
+ {
+ cs->ieee1284_mode = M1284_ECP;
+ DBG(10, "detect_mode: Using ECP-H Mode\n");
+ }
+ else if (capabilities & CAP1284_ECPSWE)
+ {
+ cs->ieee1284_mode = M1284_ECPSWE;
+ DBG(10, "detect_mode: Using ECP-S Mode\n");
+ }
+ else if (capabilities & CAP1284_NIBBLE)
+ {
+ cs->ieee1284_mode = M1284_NIBBLE;
+ DBG(10, "detect_mode: Using nibble mode\n");
+ }
+ else
+ {
+ DBG(0, "detect_mode: No supported parport modes available!\n");
+ ieee1284_release(cs->params.port);
+ ieee1284_close(cs->params.port);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Check to make sure ECP mode really is supported */
+ /* Have disabled the hardware ECP check because it's always supported
+ * by libieee1284 now, and it's too prone to hitting a ppdev bug
+ */
+
+ /* Disabled check entirely.. check now in initialise when we
+ * actually do a read */
+#if 0
+ if ((cs->ieee1284_mode == M1284_ECP) ||
+ (cs->ieee1284_mode == M1284_ECPSWE))
+ {
+ DBG(1, "detect_mode: attempting a 0 byte read, if we hang "
+ "here, it's a ppdev bug!\n");
+ /*
+ * 29/06/02
+ * NOTE:
+ * This causes an infinite loop in ppdev on 2.4.18.
+ * Not checking on hardware ECP mode should work-around
+ * effectively.
+ *
+ * I have sent email to twaugh about it, should be fixed in
+ * 2.4.19 and above.
+ */
+ if (ieee1284_ecp_read_data(cs->params.port, 0, NULL, 0) ==
+ E1284_NOTIMPL)
+ {
+ DBG(10, "detect_mode: Your version of libieee1284 "
+ "doesn't support ECP mode - defaulting"
+ " to nibble mode instead.\n");
+ cs->ieee1284_mode = M1284_NIBBLE;
+ }
+ }
+#endif
+
+ if (force_nibble == SANE_TRUE) {
+ DBG(10, "detect_mode: Nibble mode force in effect.\n");
+ cs->ieee1284_mode = M1284_NIBBLE;
+ }
+
+ ieee1284_release(cs->params.port);
+
+ sanei_canon_pp_set_ieee1284_mode(cs->ieee1284_mode);
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/canon_pp.conf.in b/backend/canon_pp.conf.in
new file mode 100644
index 0000000..14b3110
--- /dev/null
+++ b/backend/canon_pp.conf.in
@@ -0,0 +1,36 @@
+# Define which port to use if one isn't specified - you should only have
+# one of these lines!
+# This is the default port to be used - others will be detected
+ieee1284 parport0
+
+
+# Define the location of our pixel weight file, can begin with ~/ if needed.
+# You can have as many of these as you like - lines with ports that don't exist
+# will be ignored.
+#
+# Parameters are:
+# calibrate /path/to/calibration-file port-name
+#
+# The format of port-name is dependant on your OS version.
+#
+# If a file isn't speficied, the default name will be
+# ~/.sane/canon_pp-calibration-[port-name]
+
+calibrate ~/.sane/canon_pp-calibration-pp0 parport0
+
+# calibrate /etc/sane/my_calibration parport1
+
+
+# Enable the next line if you're having trouble with ECP mode such as I/O
+# errors. Nibble mode is slower, but more reliable.
+
+#force_nibble
+
+# Set a default initialisation mode for each port. Valid modes are:
+# AUTO (attempts to automatically detect by trying both methods)
+# FB620P (10101010 style.. also works for FB320P)
+# FB630P (11001100 style.. also works for FB330P, N340P, N640P)
+
+init_mode AUTO parport0
+# init_mode FB620P parport0
+# init_mode FB630P parport0
diff --git a/backend/canon_pp.h b/backend/canon_pp.h
new file mode 100644
index 0000000..4797924
--- /dev/null
+++ b/backend/canon_pp.h
@@ -0,0 +1,125 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ -----
+
+ canon_pp.h: $Revision$
+
+ This file is part of the canon_pp backend, supporting Canon FBX30P
+ and NX40P scanners
+
+*/
+
+
+#ifndef CANON_PARALLEL_H
+
+#define CANON_PARALLEL_H
+
+#ifdef BACKEND_NAME
+#undef BACKEND_NAME
+#define BACKEND_NAME canon_pp
+#endif
+
+#define DEBUG_NOT_STATIC
+#include "../include/sane/sanei_debug.h"
+
+#ifndef PACKAGE
+#define PACKAGE "Canon Parallel SANE Backend"
+#endif
+
+#define CMODE_COLOUR "Colour"
+#define CMODE_MONO "Mono"
+#define CANONP_CONFIG_FILE "canon_pp.conf"
+/* options: num,res,colour,depth,tl-x,tl-y,br-x,br-y,cal */
+/* preview option disabled */
+#define NUM_OPTIONS 9
+#define BUF_MAX 64000
+
+/* Indexes into options array */
+#define OPT_NUM_OPTIONS 0
+#define OPT_RESOLUTION 1
+#define OPT_COLOUR_MODE 2
+#define OPT_DEPTH 3
+#define OPT_TL_X 4
+#define OPT_TL_Y 5
+#define OPT_BR_X 6
+#define OPT_BR_Y 7
+#define OPT_CAL 8
+#define OPT_PREVIEW 9
+#if 0
+#define OPT_GAMMA_R 10
+#define OPT_GAMMA_G 11
+#define OPT_GAMMA_B 12
+#endif
+/*#define OPT_GAMMA 13*/
+
+typedef struct CANONP_Scanner_Struct CANONP_Scanner;
+
+struct CANONP_Scanner_Struct
+{
+ CANONP_Scanner *next;
+ SANE_Device hw;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ SANE_Int vals[NUM_OPTIONS];
+ SANE_Bool opened;
+ SANE_Bool scanning;
+ SANE_Bool sent_eof;
+ SANE_Bool cancelled;
+ SANE_Bool setup;
+ SANE_Int lines_scanned;
+ SANE_Int bytes_sent;
+
+ char *weights_file;
+ SANE_Bool cal_readonly;
+ SANE_Bool cal_valid;
+
+ scanner_parameters params;
+ scan_parameters scan;
+
+ int ieee1284_mode;
+ int init_mode;
+
+ SANE_Bool scanner_present;
+
+};
+
+
+#endif
+
diff --git a/backend/cardscan.c b/backend/cardscan.c
new file mode 100644
index 0000000..6442458
--- /dev/null
+++ b/backend/cardscan.c
@@ -0,0 +1,1686 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package, and implements a SANE backend
+ for various Corex Cardscan scanners.
+
+ Copyright (C) 2007-2010 m. allan noah
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ This file implements a SANE backend for the Corex Cardscan 800C
+
+ The source code is divided in sections which you can easily find by
+ searching for the tag "@@".
+
+ Section 1 - Init & static stuff
+ Section 2 - sane_init, _get_devices, _open & friends
+ Section 3 - sane_*_option functions
+ Section 4 - sane_start, _get_param, _read & friends
+ Section 5 - sane_close functions
+ Section 6 - misc functions
+
+ Changes:
+ v0, 2007-05-09, MAN (SANE v1.0.19)
+ - initial release
+ v1, 2008-02-14, MAN
+ - sanei_config_read has already cleaned string (#310597)
+ v2, 2010-02-10, MAN
+ - add lines_per_block config option
+ - add has_cal_buffer config option
+ - basic support for 600c
+ - clean #include lines
+
+##################################################
+ DATA FROM TRACE OF WINDOWS DRIVER:
+
+cmd packet format:
+cmdcode cmdlenlow cmdlenhigh cmdpayloadbytes
+
+resp packet format:
+respcode paperfound resplenlow resplenhigh respayloadbytes
+
+############ status read loop? ##################
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+>> 34 00 00
+<< b4 00 00 00
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+>> 34 00 00
+<< b4 00 00 00
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+
+############# scanner settings read? (0x04b8 is scan width) #############
+>> 48 00 00
+<< c8 00 0c 00 b8 04 60 00 00 80 00 00 00 58 ca 7d
+
+############## color and gray calibration data read ############
+>> 45 00 00
+<< 0x2600 bytes, bbbBBBgggGGGrrrRRRxxxXXX
+
+############ 34/b4 and 01/81 status loop til paper inserted ##############
+
+>> 35 01 00 00
+<< b5 01 01 00 00
+
+always together? {
+>> 14 05 00 80 1b 28 00 0f
+<< 94 01 05 00 80 1b 28 00 0f
+>> 22 01 00 00
+<< a2 01 01 00 00
+}
+
+>> 1a 01 00 66
+<< 9a 01 01 00 66
+
+>> 19 03 00 51 62 49
+<< 99 01 03 00 51 62 49
+
+############# heat up lamp? #################
+===========color===================
+three times {
+>> 18 07 00 00 01 60 00 61 00 07
+<< 0x40 read and 0x03 read
+the 3 byte drops from f4 f4 f4 to 17 10 08 etc.
+}
+===========gray===================
+three times {
+>> 12 06 00 00 01 60 00 61 00
+<< 0x40 read and 0x01 read
+}
+the 1 byte drops from f4 to 02
+==================================
+
+>> 35 01 00 00
+<< b5 01 01 00 00
+
+>> 13 01 00 28
+<< 93 01 01 00 28
+
+===========color===================
+three times {
+>> 18 07 00 01 10 60 00 18 05 07
+<< 0xe2c0 read
+}
+
+14/94 and 22/a2
+
+many times {
+>> 18 07 00 01 10 60 00 18 05 07
+<< 0xe2c0 read
+}
+===========gray===================
+two times {
+>> 12 06 00 01 10 60 00 18 05
+<< 0x4bc0 read
+}
+
+14/94 and 22/a2
+
+many times {
+>> 12 06 00 01 10 60 00 18 05
+<< 0x4bc0 read
+}
+==================================
+
+>> 35 01 00 ff
+<< b5 00 01 00 ff
+
+14/94 and 22/a2
+
+########### discarge capacitor? ###########
+four times {
+>> 21 02 00 0a 00
+<< a1 00 02 00 0a 00
+}
+
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+
+>> 35 01 00 ff
+<< b5 00 01 00 ff
+
+>> 34 00 00
+<< b4 00 00 00
+#############################################
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . .
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . . - sane_get_parameters() : returns estimated scan parameters
+ . . - (repeat previous 3 functions)
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times; after sane_read returns EOF,
+ . . loop may continue with sane_start which may return a 2nd page
+ . . when doing duplex scans, or load the next page from the ADF)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+*/
+
+/*
+ * @@ Section 1 - Init
+ */
+
+#include "../include/sane/config.h"
+
+#include <string.h> /*memcpy...*/
+#include <ctype.h> /*isspace*/
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#include "cardscan.h"
+
+#define DEBUG 1
+#define BUILD 2
+
+/* values for SANE_DEBUG_CARDSCAN env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - usb cmd trace 25
+ - usb cmd detail 30
+ - useless noise 35
+*/
+
+int global_has_cal_buffer = 1;
+int global_lines_per_block = 16;
+
+/* ------------------------------------------------------------------------- */
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ * a ptr to a single-linked list of scanner structs
+ */
+static const SANE_Device **sane_devArray = NULL;
+static struct scanner *scanner_devList = NULL;
+
+/*
+ * @@ Section 2 - SANE & scanner init code
+ */
+
+/*
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init: start\n");
+
+ sanei_usb_init();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: cardscan backend %d.%d.%d, from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ DBG (10, "sane_init: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ *
+ * Read the config file, find scanners with help from sanei_*
+ * store in global device structs
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ struct scanner *dev;
+ char line[PATH_MAX];
+ const char *lp;
+ FILE *fp;
+ int num_devices=0;
+ int i=0;
+
+ local_only = local_only; /* get rid of compiler warning */
+
+ DBG (10, "sane_get_devices: start\n");
+
+ global_has_cal_buffer = 1;
+ global_lines_per_block = 16;
+
+ fp = sanei_config_open (CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n", CONFIG_FILE);
+
+ while (sanei_config_read (line, PATH_MAX, fp)) {
+
+ lp = line;
+
+ /* ignore comments */
+ if (*lp == '#')
+ continue;
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_usb_attach_matching_devices(lp, attach_one);
+ }
+
+ else if (!strncmp(lp, "has_cal_buffer", 14) && isspace (lp[14])) {
+
+ int buf;
+ lp += 14;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if(buf){
+ global_has_cal_buffer = 1;
+ }
+ else{
+ global_has_cal_buffer = 0;
+ }
+
+ DBG (15, "sane_get_devices: setting \"has_cal_buffer\" to %d\n",
+ global_has_cal_buffer);
+ }
+
+ else if (!strncmp(lp, "lines_per_block", 15) && isspace (lp[15])) {
+
+ int buf;
+ lp += 15;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if(buf < 1 || buf > 32){
+ DBG (15,
+ "sane_get_devices: \"lines_per_block\"=%d\n out of range",
+ buf
+ );
+ continue;
+ }
+
+ DBG (15, "sane_get_devices: \"lines_per_block\" is %d\n", buf);
+ global_lines_per_block = buf;
+ }
+
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" ignored.\n", lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: no config file '%s', using defaults\n",
+ CONFIG_FILE);
+
+ DBG (15, "sane_get_devices: looking for 'usb 0x08F0 0x0005'\n");
+ sanei_usb_attach_matching_devices("usb 0x08F0 0x0005", attach_one);
+ }
+
+ for (dev = scanner_devList; dev; dev=dev->next) {
+ DBG (15, "sane_get_devices: found scanner %s\n",dev->device_name);
+ num_devices++;
+ }
+
+ DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
+
+ sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
+ if (!sane_devArray)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = scanner_devList; dev; dev=dev->next) {
+ sane_devArray[i++] = (SANE_Device *)&dev->sane;
+ }
+
+ sane_devArray[i] = 0;
+
+ *device_list = sane_devArray;
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* callback used by sane_get_devices
+ * build the scanner struct and link to global list
+ * unless struct is already loaded, then pretend
+ */
+static SANE_Status
+attach_one (const char *device_name)
+{
+ struct scanner *s;
+ int ret, i;
+ SANE_Word vid, pid;
+
+ DBG (10, "attach_one: start '%s'\n", device_name);
+
+ for (s = scanner_devList; s; s = s->next) {
+ if (strcmp (s->sane.name, device_name) == 0) {
+ DBG (10, "attach_one: already attached!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* build a scanner struct to hold it */
+ DBG (15, "attach_one: init struct\n");
+
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* copy the device name */
+ s->device_name = strdup (device_name);
+ if (!s->device_name){
+ free (s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* connect the fd */
+ DBG (15, "attach_one: connect fd\n");
+
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ free (s->device_name);
+ free (s);
+ return ret;
+ }
+
+ /* clean up the scanner struct based on model */
+ /* this is the only piece of model specific code */
+ sanei_usb_get_vendor_product(s->fd,&vid,&pid);
+
+ if(vid == 0x08f0){
+ s->vendor_name = "CardScan";
+ if(pid == 0x0005){
+ s->product_name = "800c";
+ }
+ else if(pid == 0x0002){
+ s->product_name = "600c";
+ }
+ else{
+ DBG (5, "Unknown product, using default settings\n");
+ s->product_name = "Unknown";
+ }
+ }
+ else{
+ DBG (5, "Unknown vendor/product, using default settings\n");
+ s->vendor_name = "Unknown";
+ s->product_name = "Unknown";
+ }
+
+ DBG (15, "attach_one: Found %s scanner %s at %s\n",
+ s->vendor_name, s->product_name, s->device_name);
+
+ /*copy config file settings*/
+ s->has_cal_buffer = global_has_cal_buffer;
+ s->lines_per_block = global_lines_per_block;
+ s->color_block_size = s->lines_per_block * PIXELS_PER_LINE * 3;
+ s->gray_block_size = s->lines_per_block * PIXELS_PER_LINE;
+
+ /* try to get calibration */
+ if(s->has_cal_buffer){
+ DBG (15, "attach_one: scanner calibration\n");
+
+ ret = load_calibration(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot calibrate, incompatible?\n");
+ free (s->device_name);
+ free (s);
+ return ret;
+ }
+ }
+ else{
+ DBG (15, "attach_one: skipping calibration\n");
+ }
+
+ /* set SANE option 'values' to good defaults */
+ DBG (15, "attach_one: init options\n");
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ memset (s->opt, 0, sizeof (s->opt));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].name = "filler";
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ DBG (15, "attach_one: init settings\n");
+
+ /* we close the connection, so that another backend can talk to scanner */
+ disconnect_fd(s);
+
+ /* load info into sane_device struct */
+ s->sane.name = s->device_name;
+ s->sane.vendor = s->vendor_name;
+ s->sane.model = s->product_name;
+ s->sane.type = "scanner";
+
+ s->next = scanner_devList;
+ scanner_devList = s;
+
+ DBG (10, "attach_one: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * connect the fd in the scanner struct
+ */
+static SANE_Status
+connect_fd (struct scanner *s)
+{
+ SANE_Status ret;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG (15, "connect_fd: opening USB device\n");
+ ret = sanei_usb_open (s->device_name, &(s->fd));
+ }
+
+ if(ret != SANE_STATUS_GOOD){
+ DBG (5, "connect_fd: could not open device: %d\n", ret);
+ }
+
+ DBG (10, "connect_fd: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+load_calibration(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ unsigned char cmd[] = {0x45, 0x00, 0x00};
+ unsigned char * buf;
+ size_t bytes = HEADER_SIZE + CAL_COLOR_SIZE*2 + CAL_GRAY_SIZE*2;
+ int j;
+
+ DBG (10, "load_calibration: start\n");
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "load_calibration: not enough mem for buffer: %ld\n",(long)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+ DBG(15, "load_calibration: got GOOD\n");
+
+ /*
+ * color cal data comes from scaner like:
+ * bbbbbbbBBBBBBBgggggggGGGGGGGrrrrrrrRRRRRRR
+ * where b=darkblue, B=lightblue, etc
+ * reorder the data into two buffers
+ * bbbbbbbgggggggrrrrrrr and BBBBBBBGGGGGGGRRRRRRR
+ */
+
+ /*dark/light blue*/
+ memcpy(s->cal_color_b, buf+HEADER_SIZE, PIXELS_PER_LINE);
+ memcpy(s->cal_color_w,
+ buf+HEADER_SIZE+PIXELS_PER_LINE, PIXELS_PER_LINE);
+
+ /*dark/light green*/
+ memcpy(s->cal_color_b+PIXELS_PER_LINE,
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*2), PIXELS_PER_LINE);
+ memcpy(s->cal_color_w+PIXELS_PER_LINE,
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*3), PIXELS_PER_LINE);
+
+ /*dark/light red*/
+ memcpy(s->cal_color_b+(PIXELS_PER_LINE*2),
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*4), PIXELS_PER_LINE);
+ memcpy(s->cal_color_w+(PIXELS_PER_LINE*2),
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*5), PIXELS_PER_LINE);
+
+ /* then slide the light data down using the dark offset */
+ for(j=0;j<CAL_COLOR_SIZE;j++){
+ s->cal_color_w[j] -= s->cal_color_b[j];
+ }
+
+ /*dark/light gray*/
+ memcpy(s->cal_gray_b,
+ buf+HEADER_SIZE+(CAL_COLOR_SIZE*2), PIXELS_PER_LINE);
+ memcpy(s->cal_gray_w,
+ buf+HEADER_SIZE+(CAL_COLOR_SIZE*2)+PIXELS_PER_LINE, PIXELS_PER_LINE);
+
+ /* then slide the light data down using the dark offset */
+ for(j=0;j<CAL_GRAY_SIZE;j++){
+ s->cal_gray_w[j] -= s->cal_gray_b[j];
+ }
+
+ hexdump(35, "cal_color_b:", s->cal_color_b, CAL_COLOR_SIZE);
+ hexdump(35, "cal_color_w:", s->cal_color_w, CAL_COLOR_SIZE);
+ hexdump(35, "cal_gray_b:", s->cal_gray_b, CAL_GRAY_SIZE);
+ hexdump(35, "cal_gray_w:", s->cal_gray_w, CAL_GRAY_SIZE);
+ }
+ else {
+ DBG(5, "load_calibration: error reading data block status = %d\n", ret);
+ }
+
+ DBG (10, "load_calibration: finish\n");
+
+ return ret;
+}
+
+/*
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct scanner *dev = NULL;
+ struct scanner *s = NULL;
+ SANE_Status ret;
+
+ DBG (10, "sane_open: start\n");
+
+ if(name[0] == 0){
+ if(scanner_devList){
+ DBG (15, "sane_open: no device requested, using first\n");
+ s = scanner_devList;
+ }
+ else{
+ DBG (15, "sane_open: no device requested, none found\n");
+ }
+ }
+ else{
+ DBG (15, "sane_open: device %s requested, attaching\n", name);
+
+ ret = attach_one(name);
+ if(ret){
+ DBG (5, "sane_open: attach error %d\n",ret);
+ return ret;
+ }
+
+ for (dev = scanner_devList; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, name) == 0) {
+ s = dev;
+ break;
+ }
+ }
+ }
+
+ if (!s) {
+ DBG (5, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "sane_open: device %s found\n", s->sane.name);
+
+ *handle = s;
+
+ /* connect the fd so we can talk to scanner */
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ DBG (10, "sane_open: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * @@ Section 3 - SANE Options functions
+ */
+
+/*
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+ int i;
+ SANE_Option_Descriptor *opt = &s->opt[option];
+
+ DBG (20, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ /* "Mode" group -------------------------------------------------------- */
+ if(option==OPT_MODE_GROUP){
+ opt->title = "Scan Mode";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* scan mode */
+ else if(option==OPT_MODE){
+ i=0;
+ s->mode_list[i++]=STRING_GRAYSCALE;
+ s->mode_list[i++]=STRING_COLOR;
+ s->mode_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ return opt;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Int dummy = 0;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ if (option >= NUM_OPTIONS) {
+ DBG (5, "sane_control_option: %d too big\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: %d inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE) {
+ SANE_Word * val_p = (SANE_Word *) val;
+
+ DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ *val_p = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if(s->mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE) {
+ int tmp;
+ SANE_Word val_c;
+ SANE_Status status;
+
+ DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
+
+ if ( s->started ) {
+ DBG (5, "sane_control_option: cant set, device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (5, "sane_control_option: bad value\n");
+ return status;
+ }
+
+ /* may have been changed by constrain, so dont copy until now */
+ val_c = *(SANE_Word *)val;
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option) {
+
+ /* Mode Group */
+ case OPT_MODE:
+ if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp == s->mode)
+ return SANE_STATUS_GOOD;
+
+ s->mode = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+
+ return SANE_STATUS_INVAL;
+}
+
+/*
+ * @@ Section 4 - SANE scanning functions
+ */
+/*
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct scanner *s = (struct scanner *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ params->pixels_per_line = PIXELS_PER_LINE;
+ params->lines = -1;
+ params->last_frame = 1;
+
+ if (s->mode == MODE_COLOR) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line * 3;
+ }
+ else if (s->mode == MODE_GRAYSCALE) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line;
+ }
+
+ DBG (15, "\tdepth %d\n", params->depth);
+ DBG (15, "\tlines %d\n", params->lines);
+ DBG (15, "\tpixels_per_line %d\n", params->pixels_per_line);
+ DBG (15, "\tbytes_per_line %d\n", params->bytes_per_line);
+
+ DBG (10, "sane_get_parameters: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = handle;
+ SANE_Status ret;
+
+ DBG (10, "sane_start: start\n");
+
+ /* first page of batch */
+ if(s->started){
+ DBG(5,"sane_start: previous transfer not finished?");
+ sane_cancel((SANE_Handle)s);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* set clean defaults */
+ s->started=1;
+ s->bytes_rx=0;
+ s->bytes_tx=0;
+ s->paperless_lines=0;
+
+ /* heat up the lamp */
+ if(s->mode == MODE_COLOR){
+ ret = heat_lamp_color(s);
+ }
+ else{
+ ret = heat_lamp_gray(s);
+ }
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to heat lamp\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ DBG (10, "sane_start: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+heat_lamp_gray(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Status ret2 = SANE_STATUS_GOOD;
+ unsigned char cmd[] =
+ {0x12, 0x06, 0x00, 0x00, 0x01, 0x60, 0x00, 0x61, 0x00};
+ size_t bytes = HEADER_SIZE + 1;
+ unsigned char * buf;
+ int i;
+
+ DBG (10, "heat_lamp_gray: start\n");
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "heat_lamp_gray: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for(i=0;i<10;i++){
+
+ ret2 = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret2 != SANE_STATUS_GOOD) {
+ DBG(5, "heat_lamp_gray: %d error\n",i);
+ ret = ret2;
+ break;
+ }
+
+ if(!buf[1]){
+ DBG(5, "heat_lamp_gray: %d got no docs\n",i);
+ ret = SANE_STATUS_NO_DOCS;
+ break;
+ }
+
+ DBG(15, "heat_lamp_gray: %d got: %d %d\n",i,
+ buf[HEADER_SIZE],s->cal_gray_b[0]);
+
+ if(buf[HEADER_SIZE] < 0x20){
+ DBG(15, "heat_lamp_gray: hot\n");
+ ret = SANE_STATUS_GOOD;
+ break;
+ }
+ else{
+ DBG(15, "heat_lamp_gray: cold\n");
+ ret = SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ free(buf);
+
+ DBG (10, "heat_lamp_gray: finish %d\n",ret);
+
+ return ret;
+}
+
+static SANE_Status
+heat_lamp_color(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Status ret2 = SANE_STATUS_GOOD;
+ unsigned char cmd[] =
+ {0x18, 0x07, 0x00, 0x00, 0x01, 0x60, 0x00, 0x61, 0x00, 0x07};
+ size_t bytes = HEADER_SIZE + 3;
+ unsigned char * buf;
+ int i;
+
+ DBG (10, "heat_lamp_color: start\n");
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "heat_lamp_color: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for(i=0;i<10;i++){
+
+ ret2 = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret2 != SANE_STATUS_GOOD) {
+ DBG(5, "heat_lamp_color: %d error\n",i);
+ ret = ret2;
+ break;
+ }
+
+ if(!buf[1]){
+ DBG(5, "heat_lamp_color: %d got no docs\n",i);
+ ret = SANE_STATUS_NO_DOCS;
+ break;
+ }
+
+ DBG(15, "heat_lamp_color: %d got: %d,%d,%d %d,%d,%d\n",i,
+ buf[HEADER_SIZE],buf[HEADER_SIZE+1],buf[HEADER_SIZE+2],
+ s->cal_color_b[0],s->cal_color_b[1],s->cal_color_b[2]);
+
+ if(buf[HEADER_SIZE] < 0x20
+ && buf[HEADER_SIZE+1] < 0x20
+ && buf[HEADER_SIZE+2] < 0x20){
+ DBG(15, "heat_lamp_color: hot\n");
+ ret = SANE_STATUS_GOOD;
+ break;
+ }
+ else{
+ DBG(15, "heat_lamp_color: cold\n");
+ ret = SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ free(buf);
+
+ DBG (10, "heat_lamp_color: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "sane_read: start\n");
+
+ *len = 0;
+
+ /* cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: call sane_start first\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* have sent all of current buffer */
+ if(s->bytes_tx == s->bytes_rx){
+
+ /* at end of data, stop */
+ if(s->paperless_lines >= MAX_PAPERLESS_LINES){
+ DBG (15, "sane_read: returning eof\n");
+ power_down(s);
+ return SANE_STATUS_EOF;
+ }
+
+ /* more to get, reset and go */
+ s->bytes_tx = 0;
+ s->bytes_rx = 0;
+
+ if(s->mode == MODE_COLOR){
+ ret = read_from_scanner_color(s);
+ }
+ else{
+ ret = read_from_scanner_gray(s);
+ }
+
+ if(ret){
+ DBG(5,"sane_read: returning %d\n",ret);
+ return ret;
+ }
+ }
+
+ /* data in current buffer, send some of it */
+ *len = s->bytes_rx - s->bytes_tx;
+ if(*len > max_len){
+ *len = max_len;
+ }
+
+ memcpy(buf,s->buffer+s->bytes_tx,*len);
+ s->bytes_tx += *len;
+
+ DBG (10, "sane_read: %d,%d,%d finish\n", *len,s->bytes_rx,s->bytes_tx);
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner_gray(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ /*cmd len-le16 move lines ??? ??? ??? ???*/
+ unsigned char cmd[] =
+ {0x12, 0x06, 0x00, 0x01, 0x01, 0x60, 0x00, 0x18, 0x05};
+ size_t bytes = HEADER_SIZE + s->gray_block_size;
+ unsigned char * buf;
+ int i,j;
+
+ DBG (10, "read_from_scanner_gray: start\n");
+
+ cmd[4] = s->lines_per_block;
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "read_from_scanner_gray: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+
+ DBG(15, "read_from_scanner_gray: got GOOD\n");
+
+ if(!buf[1]){
+ s->paperless_lines += s->lines_per_block;
+ }
+
+ s->bytes_rx = s->gray_block_size;
+
+ /*memcpy(s->buffer,buf+HEADER_SIZE,s->gray_block_size);*/
+
+ /* reorder the gray data into the struct's buffer */
+ for(i=0;i<s->gray_block_size;i+=PIXELS_PER_LINE){
+ for(j=0;j<PIXELS_PER_LINE;j++){
+
+ unsigned char byte = buf[ HEADER_SIZE + i + j ];
+ unsigned char bcal = s->cal_gray_b[j];
+ unsigned char wcal = s->cal_gray_w[j];
+
+ byte = (byte <= bcal)?0:(byte-bcal);
+ byte = (byte >= wcal)?255:(byte*255/wcal);
+ s->buffer[i+j] = byte;
+ }
+ }
+ }
+ else {
+ DBG(5, "read_from_scanner_gray: error reading status = %d\n", ret);
+ }
+
+ free(buf);
+
+ DBG (10, "read_from_scanner_gray: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner_color(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ unsigned char cmd[] =
+ {0x18, 0x07, 0x00, 0x01, 0x01, 0x60, 0x00, 0x18, 0x05, 0x07};
+ size_t bytes = HEADER_SIZE + s->color_block_size;
+ unsigned char * buf;
+ int i,j,k;
+
+ DBG (10, "read_from_scanner_color: start\n");
+
+ cmd[4] = s->lines_per_block;
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "read_from_scanner_color: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+
+ DBG(15, "read_from_scanner_color: got GOOD\n");
+
+ if(!buf[1]){
+ s->paperless_lines += s->lines_per_block;
+ }
+
+ s->bytes_rx = s->color_block_size;
+
+ /*memcpy(s->buffer,buf+HEADER_SIZE,s->color_block_size);*/
+
+ /* reorder the color data into the struct's buffer */
+ for(i=0;i<s->color_block_size;i+=PIXELS_PER_LINE*3){
+ for(j=0;j<PIXELS_PER_LINE;j++){
+ for(k=0;k<3;k++){
+
+ int offset = PIXELS_PER_LINE*(2-k) + j;
+ unsigned char byte = buf[ HEADER_SIZE + i + offset ];
+ unsigned char bcal = s->cal_color_b[offset];
+ unsigned char wcal = s->cal_color_w[offset];
+
+ byte = (byte <= bcal)?0:(byte-bcal);
+ byte = (byte >= wcal)?255:(byte*255/wcal);
+ s->buffer[i+j*3+k] = byte;
+ }
+ }
+ }
+ }
+ else {
+ DBG(5, "read_from_scanner_color: error reading status = %d\n", ret);
+ }
+
+ free(buf);
+
+ DBG (10, "read_from_scanner_color: finish\n");
+
+ return ret;
+}
+
+/*
+ * @@ Section 4 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ struct scanner * s = (struct scanner *) handle;
+ DBG (10, "sane_cancel: start\n");
+ s->started = 0;
+ DBG (10, "sane_cancel: finish\n");
+}
+
+static SANE_Status
+power_down(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[] = {0x21, 0x02, 0x00, 0x0a, 0x00};
+ unsigned char buf[6];
+ size_t bytes = sizeof(buf);
+ int i;
+
+ DBG (10, "power_down: start\n");
+
+ for(i=0;i<5;i++){
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if(ret != SANE_STATUS_GOOD){
+ break;
+ }
+ }
+
+#if 0
+ unsigned char cmd[] = {0x35, 0x01, 0x00, 0xff};
+ unsigned char buf[5];
+ size_t bytes = sizeof(buf);
+
+ DBG (10, "power_down: start\n");
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+#endif
+
+ DBG (10, "power_down: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close: start\n");
+
+ sane_cancel(handle);
+ disconnect_fd((struct scanner *) handle);
+
+ DBG (10, "sane_close: finish\n");
+}
+
+static SANE_Status
+disconnect_fd (struct scanner *s)
+{
+ DBG (10, "disconnect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (15, "disconnecting usb device\n");
+ sanei_usb_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (10, "disconnect_fd: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct scanner *dev, *next;
+
+ DBG (10, "sane_exit: start\n");
+
+ for (dev = scanner_devList; dev; dev = next) {
+ disconnect_fd(dev);
+ next = dev->next;
+ free (dev->device_name);
+ free (dev);
+ }
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ scanner_devList = NULL;
+ sane_devArray = NULL;
+
+ DBG (10, "sane_exit: finish\n");
+}
+
+
+/*
+ * @@ Section 5 - misc helper functions
+ */
+/*
+ * take a bunch of pointers, send commands to scanner
+ */
+static SANE_Status
+do_cmd(struct scanner *s, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ /* sanei_usb overwrites the transfer size, so make some local copies */
+ size_t loc_cmdLen = cmdLen;
+ size_t loc_outLen = outLen;
+ size_t loc_inLen = *inLen;
+
+ int cmdTime = USB_COMMAND_TIME;
+ int outTime = USB_DATA_TIME;
+ int inTime = USB_DATA_TIME;
+
+ int ret = 0;
+
+ DBG (10, "do_cmd: start\n");
+
+ if(shortTime){
+ cmdTime /= 20;
+ outTime /= 20;
+ inTime /= 20;
+ }
+
+ /* change timeout */
+ sanei_usb_set_timeout(cmdTime);
+
+ /* write the command out */
+ DBG(25, "cmd: writing %ld bytes, timeout %d\n", (long)cmdLen, cmdTime);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+ ret = sanei_usb_write_bulk(s->fd, cmdBuff, &cmdLen);
+ DBG(25, "cmd: wrote %ld bytes, retVal %d\n", (long)cmdLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"cmd: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(loc_cmdLen != cmdLen){
+ DBG(5,"cmd: wrong size %ld/%ld\n", (long)loc_cmdLen, (long)cmdLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* this command has a write component, and a place to get it */
+ if(outBuff && outLen && outTime){
+
+ /* change timeout */
+ sanei_usb_set_timeout(outTime);
+
+ DBG(25, "out: writing %ld bytes, timeout %d\n", (long)outLen, outTime);
+ hexdump(30, "out: >>", outBuff, outLen);
+ ret = sanei_usb_write_bulk(s->fd, outBuff, &outLen);
+ DBG(25, "out: wrote %ld bytes, retVal %d\n", (long)outLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"out: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"out: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(loc_outLen != outLen){
+ DBG(5,"out: wrong size %ld/%ld\n", (long)loc_outLen, (long)outLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* this command has a read component, and a place to put it */
+ if(inBuff && inLen && inTime){
+
+ memset(inBuff,0,*inLen);
+
+ /* change timeout */
+ sanei_usb_set_timeout(inTime);
+
+ DBG(25, "in: reading %ld bytes, timeout %d\n", (long)*inLen, inTime);
+ ret = sanei_usb_read_bulk(s->fd, inBuff, inLen);
+ DBG(25, "in: retVal %d\n", ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"in: got EOF, continuing\n");
+ }
+ else if(ret != SANE_STATUS_GOOD){
+ DBG(5,"in: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ DBG(25, "in: read %ld bytes\n", (long)*inLen);
+ if(*inLen){
+ hexdump(30, "in: <<", inBuff, *inLen);
+ }
+
+ if(loc_inLen != *inLen){
+ ret = SANE_STATUS_EOF;
+ DBG(5,"in: short read %ld/%ld\n", (long)loc_inLen, (long)*inLen);
+ }
+ }
+
+ DBG (10, "do_cmd: finish\n");
+
+ return ret;
+}
+
+/**
+ * Convenience method to determine longest string size in a list.
+ */
+static size_t
+maxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+/**
+ * Prints a hex dump of the given buffer onto the debug output stream.
+ */
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ if(DBG_LEVEL < level)
+ return;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3x:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (15, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG (10, "sane_get_select_fd\n");
+ DBG (15, "%p %d\n", h, *fdp);
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/cardscan.conf.in b/backend/cardscan.conf.in
new file mode 100644
index 0000000..3c49f26
--- /dev/null
+++ b/backend/cardscan.conf.in
@@ -0,0 +1,17 @@
+# For scanners connected via USB on a known device (kernel driver):
+#usb /dev/usb/scanner0
+
+# For scanners connected via USB using vendor and device ids (libusb):
+#usb VENDORID PRODUCTID
+
+# NOTE: if you have to add your device here- please send the id and model
+# to the author via email, so it can be included in next version. kitno455 at
+# gmail dot com - with cardscan in the subject line
+
+# Corex Cardscan 800c
+usb 0x08f0 0x0005
+
+# Corex Cardscan 600c
+has_cal_buffer 0
+lines_per_block 1
+usb 0x08f0 0x0002
diff --git a/backend/cardscan.h b/backend/cardscan.h
new file mode 100644
index 0000000..a673dc6
--- /dev/null
+++ b/backend/cardscan.h
@@ -0,0 +1,197 @@
+#ifndef CARDSCAN_H
+#define CARDSCAN_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ * Please see opening comment in cardscan.c
+ */
+
+/* -------------------------------------------------------------------------
+ * This option list has to contain all options for all scanners supported by
+ * this driver. If a certain scanner cannot handle a certain option, there's
+ * still the possibility to say so, later.
+ */
+enum scanner_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /*mono/gray/color*/
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+/* values common to calib and image data */
+#define HEADER_SIZE 64
+#define PIXELS_PER_LINE 1208
+
+/* values for calib data */
+#define CAL_COLOR_SIZE (PIXELS_PER_LINE * 3)
+#define CAL_GRAY_SIZE PIXELS_PER_LINE
+
+/* values for image data */
+#define MAX_PAPERLESS_LINES 210
+
+struct scanner
+{
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during init of scanner. */
+ struct scanner *next;
+ char *device_name; /* The name of the scanner device for sane */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during inquiry probing of the scanner. */
+ SANE_Device sane;
+ char * vendor_name;
+ char * product_name;
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during reading of config file. */
+ int has_cal_buffer;
+ int lines_per_block;
+ int color_block_size;
+ int gray_block_size;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable SANE_Option structs provide our interface to frontend. */
+
+ /* long array of option structs */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+ /* --------------------------------------------------------------------- */
+ /* some options require lists of strings or numbers, we keep them here */
+ /* instead of in global vars so that they can differ for each scanner */
+
+ /*mode group*/
+ SANE_String_Const mode_list[3];
+
+ /* --------------------------------------------------------------------- */
+ /* changeable vars to hold user input. modified by SANE_Options above */
+
+ /*mode group*/
+ int mode; /*color,lineart,etc*/
+
+ /* --------------------------------------------------------------------- */
+ /* values which are derived from setting the options above */
+ /* the user never directly modifies these */
+
+ /* this is defined in sane spec as a struct containing:
+ SANE_Frame format;
+ SANE_Bool last_frame;
+ SANE_Int lines;
+ SANE_Int depth; ( binary=1, gray=8, color=8 (!24) )
+ SANE_Int pixels_per_line;
+ SANE_Int bytes_per_line;
+ */
+ SANE_Parameters params;
+
+ /* --------------------------------------------------------------------- */
+ /* calibration data read once */
+ unsigned char cal_color_b[CAL_COLOR_SIZE];
+ unsigned char cal_gray_b[CAL_GRAY_SIZE];
+ unsigned char cal_color_w[CAL_COLOR_SIZE];
+ unsigned char cal_gray_w[CAL_GRAY_SIZE];
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by scanning functions to keep track of pages, etc */
+ int started;
+ int paperless_lines;
+
+ /* buffer part of image */
+ unsigned char buffer[PIXELS_PER_LINE * 3 * 32];
+
+ /* how far we have read from scanner into buffer */
+ int bytes_rx;
+
+ /* how far we have written from buffer to frontend */
+ int bytes_tx;
+
+ /* --------------------------------------------------------------------- */
+ /* values used by the command and data sending function */
+ int fd; /* The scanner device file descriptor. */
+
+};
+
+#define USB_COMMAND_TIME 10000
+#define USB_DATA_TIME 10000
+
+#define MODE_COLOR 0
+#define MODE_GRAYSCALE 1
+
+/* ------------------------------------------------------------------------- */
+
+#define MM_PER_UNIT_UNFIX SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))
+#define MM_PER_UNIT_FIX SANE_FIX(SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)))
+
+#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX((number) * MM_PER_UNIT_UNFIX)
+#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) / MM_PER_UNIT_UNFIX
+
+#define CONFIG_FILE "cardscan.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle);
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking);
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp);
+
+const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option);
+
+SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+
+SANE_Status sane_start (SANE_Handle handle);
+
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params);
+
+SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len);
+
+void sane_cancel (SANE_Handle h);
+
+void sane_close (SANE_Handle h);
+
+void sane_exit (void);
+
+/* ------------------------------------------------------------------------- */
+
+static SANE_Status attach_one (const char *devicename);
+static SANE_Status connect_fd (struct scanner *s);
+static SANE_Status disconnect_fd (struct scanner *s);
+
+static SANE_Status
+do_cmd(struct scanner *s, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status load_calibration (struct scanner *s);
+
+static SANE_Status heat_lamp_color(struct scanner *s);
+static SANE_Status heat_lamp_gray(struct scanner *s);
+
+static SANE_Status read_from_scanner_color(struct scanner *s);
+static SANE_Status read_from_scanner_gray(struct scanner *s);
+
+static SANE_Status power_down(struct scanner *s);
+
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+
+static size_t maxStringSize (const SANE_String_Const strings[]);
+
+#endif /* CARDSCAN_H */
diff --git a/backend/coolscan-scsidef.h b/backend/coolscan-scsidef.h
new file mode 100644
index 0000000..4f1a27e
--- /dev/null
+++ b/backend/coolscan-scsidef.h
@@ -0,0 +1,839 @@
+
+/* ------------------------------------------------------------------------- */
+
+/* coolscan-scsidef.h: scsi-definiton header file for COOLSCAN scanner driver.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+
+/* ------------------------------------------------------------------------- */
+
+#ifndef COOLSCAN_SCSIDEF_H
+#define COOLSCAN_SCSIDEF_H
+
+/* ========================================================================= */
+
+/* I'm using functions derived from Eric Youngdale's scsiinfo
+ * program here for dealing with parts of SCSI commands.
+ */
+
+static inline void
+setbitfield (unsigned char *pageaddr, int mask, int shift, int val) \
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void
+resetbitfield (unsigned char *pageaddr, int mask, int shift, int val) \
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | (((!val) & mask) << shift);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift) \
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline int
+getnbyte (unsigned char *pnt, int nbytes) \
+{
+ unsigned int result = 0;
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = 0; i < nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void
+putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) \
+{
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = nbytes - 1; i >= 0; i--)
+ \
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+
+/* ==================================================================== */
+
+/* Not all of these are defined in scsi.h, so we'll make sure
+ * we agree about them here...
+ */
+#undef WRITE_BUFFER /* correct size write_buffer for scanner */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define MODE_SELECT 0x15
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define MODE_SENSE 0x1a
+#define SCAN 0x1b
+#define SEND_DIAGNOSTIC 0x1d
+#define SET_WINDOW 0x24
+#define GET_WINDOW 0x25
+#define READ 0x28
+#define SEND 0x2a
+#define OBJECT_POSITION 0x31
+#define WRITE_BUFFER 0x3b
+#define READ_BUFFER 0x3c
+#define SABORT 0xc0
+#define COMMAND_C1 0xc1
+#define AUTO_FOCUS 0xc2
+#define UNIT_MOVE 0xe0
+
+
+/* ==================================================================== */
+
+/* wdb_len if nothing is set by inquiry */
+#define STD_WDB_LEN 0x28
+
+/* ==================================================================== */
+/* SCSI commands */
+
+typedef struct
+{
+ unsigned char *cmd;
+ int size;
+}
+scsiblk;
+
+/* ==================================================================== */
+
+#define set_inquiry_return_size(icb,val) icb[0x04]=val
+static unsigned char inquiryC[] =
+{INQUIRY, 0x00, 0x00, 0x00, 0x1f, 0x00};
+static scsiblk inquiry =
+{inquiryC, sizeof (inquiryC)};
+
+#define get_inquiry_periph_qual(in) getbitfield(in, 0x07, 5)
+#define IN_periph_qual_lun 0x00
+#define IN_periph_qual_nolun 0x03
+#define get_inquiry_periph_devtype(in) getbitfield(in, 0x1f, 0)
+#define IN_periph_devtype_scanner 0x06
+#define IN_periph_devtype_unknown 0x1f
+#define get_inquiry_response_format(in) getbitfield(in + 0x03, 0x07, 0)
+#define IN_recognized 0x02
+#define get_inquiry_additional_length(in) in[0x04]
+#define get_inquiry_length(in) in[0x03]
+#define set_inquiry_length(out,n) out[0x04]=n-5
+
+#define get_inquiry_vendor(in, buf) strncpy(buf, in + 0x08, 0x08)
+#define get_inquiry_product(in, buf) strncpy(buf, in + 0x10, 0x010)
+#define get_inquiry_version(in, buf) strncpy(buf, in + 0x20, 0x04)
+
+
+/* ==================================================================== */
+/*
+ static unsigned char mode_selectC[] = {
+ MODE_SELECT, 0x10, 0x00, 0x00, 0x00, 0x00
+
+ static scsiblk mode_select = { mode_selectC,sizeof(mode_selectC) };
+ */
+/* ==================================================================== */
+
+static unsigned char test_unit_readyC[] =
+{
+ TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static scsiblk test_unit_ready =
+{test_unit_readyC, sizeof (test_unit_readyC)};
+
+/* ==================================================================== */
+
+static unsigned char reserve_unitC[] =
+{
+ RESERVE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static scsiblk reserve_unit =
+{reserve_unitC, sizeof (reserve_unitC)};
+
+/* ==================================================================== */
+
+static unsigned char release_unitC[] =
+{
+ RELEASE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static scsiblk release_unit =
+{release_unitC, sizeof (release_unitC)};
+
+/* ==================================================================== */
+
+static unsigned char mode_senseC[] =
+{
+ MODE_SENSE, 0x18, 0x03, 0x00, 0x00, 0x00, /* PF set, page type 03 */
+};
+#define set_MS_DBD(b, val) setbitfield(b, 0x01, 3, (val?1:0))
+#define set_MS_len(b, val) putnbyte(b+0x04, val, 1)
+#define get_MS_MUD(b) getnbyte(b+(0x04+((int)*(b+0x3)))+0x4,2)
+
+static scsiblk mode_sense =
+{mode_senseC, sizeof (mode_senseC)};
+
+/* ==================================================================== */
+
+static unsigned char set_windowC[] =
+{
+ SET_WINDOW, 0x00, /* opcode, lun */
+ 0x00, 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, 0x00, /* transfer length; needs to be set */
+ 0x00, /* control byte */
+};
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+static scsiblk set_window =
+{set_windowC, sizeof (set_windowC)};
+
+/* ==================================================================== */
+
+static unsigned char get_windowC[] =
+{
+ GET_WINDOW, 0x01, /* opcode, lun, misc (should be 0x01? */
+ 0x00, 0x00, 0x00, /* reserved */
+ 0x00, /* Window identifier */
+ 0x00, 0x00, 0x00, /* transfer length; needs to be get */
+ 0x00, /* control byte */
+};
+#define set_GW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+#define set_WindowID_wid(sb, val) sb[5] = val
+
+static scsiblk get_window =
+{get_windowC, sizeof (get_windowC)};
+
+/* ==================================================================== */
+
+/* We use the same structure for both SET WINDOW and GET WINDOW. */
+static unsigned char window_parameter_data_blockC[] =
+{
+ 0x00, 0x00, /* window data length, in case of get, or 0 in case of set */
+ 0x00, 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, /* Window Descriptor Length, value must be
+ set by SHORT_WDB define */
+};
+#define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2)
+
+
+static scsiblk window_parameter_data_block =
+{
+ window_parameter_data_blockC, sizeof (window_parameter_data_blockC)
+};
+
+
+/* ==================================================================== */
+
+static unsigned char window_descriptor_blockC[] =
+{
+/* Any field maked with 'R' (e.g. R0x55) indicate a filed provided for
+ * development. In normal operation, 0 is set here. If any other value is set,
+ * operationis not guaranteed! */
+
+#define max_WDB_size 0xff
+#define used_WDB_size 0x75
+
+ 0x00, /* 0x00 */
+ /* Window Identifier */
+#define set_WD_wid(sb, val) sb[0] = val
+#define WD_wid_all 0x00 /* Only one supported */
+ 0x00, /* 0x01 */
+ /* reserved, AUTO */
+#define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val)
+#define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0)
+ 0x00, 0x00, /* 0x02 */
+ /* X Resolution in dpi */
+#define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2)
+#define get_WD_Xres(sb) getnbyte(sb + 0x02, 2)
+ 0x00, 0x00, /* 0x04 */
+ /* Y Resolution in dpi */
+#define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2)
+#define get_WD_Yres(sb) getnbyte(sb + 0x04, 2)
+ 0x00, 0x00, 0x00, 0x00, /* 0x06 */
+ /* Upper Left X in 1200|2700pt/inch */
+#define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4)
+#define get_WD_ULX(sb) getnbyte(sb + 0x06, 4)
+ 0x00, 0x00, 0x00, 0x00, /* 0x0a */
+ /* Upper Left Y in 1200|2700pt/inch */
+#define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4)
+#define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4)
+ 0x00, 0x00, 0x00, 0x00, /* 0x0e */
+ /* Width 1200pt/inch */
+#define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4)
+#define get_WD_width(sb) getnbyte(sb + 0x0e, 4)
+ 0x00, 0x00, 0x00, 0x00, /* 0x12 */
+ /* Length 1200pt/inch */
+#define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4)
+#define get_WD_length(sb) getnbyte(sb + 0x12, 4)
+ 0x00, /* 0x16 */
+ /* Brightness */
+#define set_WD_brightness(sb, val) sb[0x16] = val
+#define get_WD_brightness(sb) sb[0x16]
+ 0x00, /* 0x17 */
+ /* Reserved */
+ 0x00, /* 0x18 */
+ /* Contrast */
+#define set_WD_contrast(sb, val) sb[0x18] = val
+#define get_WD_contrast(sb) sb[0x18]
+ 0x05, /* 0x19 */
+ /* Image Mode */
+#define set_WD_composition(sb, val) sb[0x19] = val
+#define get_WD_composition(sb) sb[0x19]
+#define WD_comp_grey 0x02
+#define WD_comp_gray 0x02
+#define WD_comp_rgb_full 0x05
+ 0x08, /* 0x1a */ /* Bits/Pixel */
+#define set_WD_bitsperpixel(sb, val) sb[0x1a] = val
+#define get_WD_bitsperpixel(sb) sb[0x1a]
+#define WD_bits_8 0x08
+ 0, 0, 0, 0, 0, /* 0x1b */
+ /* Reserved */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, /* 0x28 */
+ /* X-axis pixel count (1-2592 */
+#define get_WD_xpixels(sb) getnbyte(sb + 0x28, 4)
+ 0, 0, 0, 0, /* 0x2c */
+ /* Y-axis pixel count (1-3888) */
+#define get_WD_ypixels(sb) getnbyte(sb + 0x2c, 4)
+ 0x01, /* 0x30 */
+ /* Reserved, negative/positive
+ * reserved, drop-out color
+ * (Green) */
+#define set_WD_negative(sb, val) setbitfield(sb + 0x30, 0x1, 4, (val?1:0))
+#define get_WD_negative(sb) getbitfield(sb + 0x30, 0x1, 4)
+#define WD_Negative 0x01
+#define WD_Positive 0x00
+#define set_WD_dropoutcolor(sb, val) setbitfield(sb + 0x30, 0x3, 0, val)
+#define get_WD_dropoutcolor(sb) getbitfield(sb + 0x30, 0x3, 0)
+#define WD_Dropout_Red 0x00
+#define WD_Dropout_Green 0x01
+#define WD_Dropout_Blue 0x02
+ 0x00, /* 0x31 */
+ /* scan mode */
+#define set_WD_scanmode(sb, val) setbitfield(sb + 0x31, 0x3, 4, val)
+#define get_WD_scanmode(sb) getbitfield(sb + 0x31, 0x3, 4)
+#define WD_Scan 0x00
+#define WD_Prescan 0x01
+ 0x40, /* 0x32 */
+ /* Data transfer mode */
+#define set_WD_transfermode(sb, val) setbitfield(sb + 0x32, 0x3, 6, val)
+#define get_WD_transfermode(sb) getbitfield(sb + 0x32, 0x3, 6)
+#define WD_LineSequence 0x2
+#define WD_DotSequence 0x1
+ 0x02, /* 0x33 */
+ /* Gamma selection */
+#define set_WD_gammaselection(sb, val) putnbyte(sb + 0x33, val, 1)
+#define get_WD_gammaselection(sb) getnbyte(sb + 0x33, 1)
+#define WD_Linear 0x2
+#define WD_Monitor 0x3
+ 0, /* 0x34 */
+ /* Reserved */
+ 0x40, /* 0x35 */
+ /* Reserved, shading, analog gamma, averaging */
+#define set_WD_shading(sb, val) setbitfield(sb + 0x35, 0x1, 6, val)
+#define get_WD_shading(sb) getbitfield(sb + 0x35, 0x1, 6)
+#define WD_Shading_ON 0x0
+#define WD_Shading_OFF 0x1
+#define set_WD_analog_gamma_R(sb, val) setbitfield(sb + 0x35, 0x1, 5, val)
+#define set_WD_analog_gamma_G(sb, val) setbitfield(sb + 0x35, 0x1, 4, val)
+#define set_WD_analog_gamma_B(sb, val) setbitfield(sb + 0x35, 0x1, 3, val)
+#define get_WD_analog_gamma_R(sb) getbitfield(sb + 0x35, 0x1, 5)
+#define get_WD_analog_gamma_G(sb) getbitfield(sb + 0x35, 0x1, 4)
+#define get_WD_analog_gamma_B(sb) getbitfield(sb + 0x35, 0x1, 3)
+#define WD_Analog_Gamma_ON 0x0
+#define WD_Analog_Gamma_OFF 0x1
+#define set_WD_averaging(sb, val) setbitfield(sb + 0x35, 0x7, 0, (val?7:0))
+#define get_WD_averaging(sb) getbitfield(sb + 0x35, 0x7, 0)
+#define WD_Averaging_ON 0x0
+#define WD_Averaging_OFF 0x1
+
+ 0, /* 0x36 */
+ /* reserved */
+ 0, /* 0x37 */
+ /* R brightness */
+#define set_WD_brightness_R(b, val) putnbyte(b + 0x37, val, 1)
+#define get_WD_brightness_R(b) getnbyte(b + 0x37, 1)
+ 0, /* 0x38 */
+ /* G brightness */
+#define set_WD_brightness_G(b, val) putnbyte(b + 0x38, val, 1)
+#define get_WD_brightness_G(b) getnbyte(b + 0x38, 1)
+ 0, /* 0x39 */
+ /* B brightness */
+#define set_WD_brightness_B(b, val) putnbyte(b + 0x39, val, 1)
+#define get_WD_brightness_B(b) getnbyte(b + 0x39, 1)
+ 0, /* 0x3a */
+ /* R contrast */
+#define set_WD_contrast_R(b, val) putnbyte(b + 0x3a, val, 1)
+#define get_WD_contrast_R(b) getnbyte(b + 0x3a, 1)
+ 0, /* 0x3b */
+ /* G contrast */
+#define set_WD_contrast_G(b, val) putnbyte(b + 0x3b, val, 1)
+#define get_WD_contrast_G(b) getnbyte(b + 0x3b, 1)
+ 0, /* 0x3c */
+ /* B contrast */
+#define set_WD_contrast_B(b, val) putnbyte(b + 0x3c, val, 1)
+#define get_WD_contrast_B(b) getnbyte(b + 0x3c, 1)
+ 0, 0, 0, 0, /* 0x3d */
+ /* Reserved */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, /* 0x49 */
+ /* R exposure time adjustment [0, 12-200] */
+#define set_WD_exposure_R(b, val) putnbyte(b + 0x49, val, 1)
+#define get_WD_exposure_R(b) getnbyte(b + 0x49, 1)
+ 0, /* 0x4a */
+ /* G exposure time adjustment [0, 12-200] */
+#define set_WD_exposure_G(b, val) putnbyte(b + 0x4a, val, 1)
+#define get_WD_exposure_G(b) getnbyte(b + 0x4a, 1)
+ 0, /* 0x4b */
+ /* B exposure time adjustment [0, 12-200] */
+#define set_WD_exposure_B(b, val) putnbyte(b + 0x4b, val, 1)
+#define get_WD_exposure_B(b) getnbyte(b + 0x4b, 1)
+ 0, 0, 0, 0, /* 0x4c */
+ /* Reserved */
+ 0, 0,
+ 0, /* 0x52 */
+ /* Amount of R shift [0, 128+-15] */
+#define set_WD_shift_R(b, val) putnbyte(b + 0x52, val, 1)
+#define get_WD_shift_R(b) getnbyte(b + 0x52, 1)
+ 0, /* 0x53 */
+ /* Amount of G shift [0, 128+-15] */
+#define set_WD_shift_G(b, val) putnbyte(b + 0x53, val, 1)
+#define get_WD_shift_G(b) getnbyte(b + 0x53, 1)
+ 0, /* 0x54 */
+ /* Amount of B shift [0, 128+-15] */
+#define set_WD_shift_B(b, val) putnbyte(b + 0x54, val, 1)
+#define get_WD_shift_B(b) getnbyte(b + 0x54, 1)
+ 0, /* R0x55 */
+ /* Amount of R offset [0-255] */
+ 0, /* R0x56 */
+ /* Amount of G offset [0-255] */
+ 0, /* R0x57 */
+ /* Amount of B offset [0-255] */
+ 0, 0, /* 0x58 */
+ /* Maximum resolution (for GET WINDOW: [2700]) */
+#define get_WD_maxres(b) getnbyte(b + 0x58, 2)
+ 0, 0, /* 0x5a */
+ /* Reserved */
+ 0, /* 0x5c */
+ /* LUT-R, LUT-G */
+#define set_WD_LUT_R(b, val) setbitfield(b + 0x5c, 0x0f, 4, val)
+#define set_WD_LUT_G(b, val) setbitfield(b + 0x5c, 0x0f, 0, val)
+#define get_WD_LUT_R(b) getbitfield(b + 0x5c, 0x0f, 4)
+#define get_WD_LUT_G(b) getbitfield(b + 0x5c, 0x0f, 0)
+ 0, /* 0x5d */
+ /* LUT-B, reserved */
+#define set_WD_LUT_B(b, val) setbitfield(b + 0x5d, 0x0f, 4, val)
+#define get_WD_LUT_B(b) getbitfield(b + 0x5d, 0x0f, 4)
+ 0, /* R0x5e */
+ /* LS-1000: reserved. LS-20: R B/W reference point */
+ 0, /* R0x5f */
+ /* LS-1000: reserved. LS-20: G B/W reference point */
+ 0, /* R0x60 */
+ /* LS-1000: reserved. LS-20: B B/W reference point */
+ 0, /* R0x61 */
+ /* R exposure time unit [0-7] (LS-1000); [0, 2-1] (LS-20) */
+ 0, /* R0x62 */
+ /* G exposure time unit [0-7] (LS-1000); [0, 2-1] (LS-20) */
+ 0, /* R0x63 */
+ /* B exposure time unit [0-7] (LS-1000); [0, 2-1] (LS-20) */
+ 0, /* 0x64 */
+ /* Reserved */
+ 0, /* 0x65 */
+ /* Reserved, stop */
+#define set_WD_stop(b, val) setbitfield(b+0x65, 0x01, 0, val)
+#define get_WD_stop(b) getbitfield(b+0x65, 0x01, 0)
+ 0, /* R0x66 */
+ /* R gain [0-4] (LS-1000), [0-255] (LS-20) */
+ 0, /* R0x67 */
+ /* G gain [0-4] (LS-1000), [0-255] (LS-20) */
+ 0, /* R0x68 */
+ /* B gain [0-4] (LS-1000), [0-255] (LS-20) */
+ 0, 0, 0, 0, /* R0x69 */
+ /* R exposure time variable [0, 64-65535] */
+ 0, 0, 0, 0, /* R0x6d */
+ /* G exposure time variable [0, 64-65535] */
+ 0, 0, 0, 0, /* R0x71 */
+ /* B exposure time variable [0, 64-65535] */
+ /* 0x75 (last) */
+
+};
+
+static scsiblk window_descriptor_block =
+{
+ window_descriptor_blockC, sizeof (window_descriptor_blockC)
+};
+
+
+
+/* LS-30 has different window-descriptor !
+ */
+
+static unsigned char window_descriptor_blockC_LS30[] =
+{
+
+#define used_WDB_size_LS30 0x32
+
+ 0x00, /* 0x00 */
+ /* Window Identifier */
+#define WD_wid_0 0x00 /* Only one supported */
+#define WD_wid_1 0x01
+#define WD_wid_2 0x02
+#define WD_wid_3 0x03
+#define WD_wid_4 0x04
+#define WD_wid_9 0x09
+ 0x00, /* reserved, AUTO */
+
+ 0x00, 0x00, /* 0x02 */
+ /* X Resolution in dpi */
+
+ 0x00, 0x00, /* 0x04 */
+ /* Y Resolution in dpi */
+
+ 0x00, 0x00, 0x00, 0x00, /* 0x06 */
+ /* Upper Left X in 2700pt/inch */
+ 0x00, 0x00, 0x00, 0x00, /* 0x0a */
+ /* Upper Left Y in 2700pt/inch */
+ 0x00, 0x00, 0x00, 0x00, /* 0x0e */
+ /* Width 1200pt/inch */
+ 0x00, 0x00, 0x00, 0x00, /* 0x12 */
+ /* Length 1200pt/inch */
+ 0x00, /* 0x16 */
+ /* Brightness */
+ 0x00, /* 0x17 */
+ /* Reserved */
+ 0x00, /* 0x18 */
+ /* Contrast */
+ 0x05, /* 0x19 */
+ /* Image Mode */
+ 0x08, /* 0x1a */
+ /* Bits/Pixel */
+#define WD_bits_10 0x0a
+ 0, 0, 0, 0, 0, /* 0x1b */
+ /* Reserved */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
+ 0, /* 0x29 */
+ /* Negative/positive prevue/scan */
+#define set_WD_negative_LS30(sb, val) setbitfield(sb + 0x29, 0x1, 0, (val?0:1))
+#define get_WD_negative_LS30(sb) getbitfield(sb + 0x29, 0x1, 0)
+ /* scan mode */
+#define set_WD_scanmode_LS30(sb, val) setbitfield(sb + 0x29, 0x3, 0, val)
+#define get_WD_scanmode_LS30(sb) getbitfield(sb + 0x29, 0x3, 0)
+
+ 0x04, /* 0x2a */
+ 0x02, /* 0x2b */
+ 0x01, /* 0x2c */
+ 0xff, /* 0x2d */
+ 0,0, /* 0x2e */
+ 0,0, /* 0x30 */
+#define set_gain_LS30(sb, val) putnbyte(sb + 0x2e, val, 4)
+#define get_gain_LS30(sb) getnbyte(sb + 0x2e, 4)
+};
+
+
+static scsiblk window_descriptor_block_LS30 =
+{
+ window_descriptor_blockC, sizeof (window_descriptor_blockC_LS30)
+};
+
+/* ==================================================================== */
+
+/*#define set_WDB_length(length) (window_descriptor_block.size = (length)) */
+#define WPDB_OFF(b) (b + set_window.size)
+#define WDB_OFF(b, n) (b + set_window.size + \
+ window_parameter_data_block.size + \
+ ( window_descriptor_block.size * (n - 1) ) )
+#define set_WPDB_wdbnum(sb,n) set_WPDB_wdblen(sb,window_descriptor_block.size*n)
+
+
+
+/* ==================================================================== */
+
+static unsigned char scanC[] =
+{
+ SCAN, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static scsiblk scan =
+{scanC, sizeof (scanC)};
+#define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val
+
+/* ==================================================================== */
+/*
+ static unsigned char send_diagnosticC[] = {
+ SEND_DIAGNOSTIC, 0x04, 0x00, 0x00, 0x00, 0x00
+ };
+
+ static scsiblk send_diagnostic = { send_diagnosticC, sizeof(send_diagnosticC) }; */
+
+/* ==================================================================== */
+
+/* sread instead of read because read is a libc primitive */
+static unsigned char sreadC[] =
+{
+ READ, 0x00,
+ 0x00, /* Data Type Code */
+ 0x00, /* reserved */
+ 0x00, 0x00, /* data type qualifier */
+ 0x00, 0x00, 0x00, /* transfer length */
+ 0x00 /* control */
+};
+
+static scsiblk sread =
+{sreadC, sizeof (sreadC)};
+#define set_R_data1_code(sb, val) sb[0x01] = val
+#define set_R_datatype_code(sb, val) sb[0x02] = val
+#define R_datatype_imagedata 0x00
+#define R_EX_datatype_LUT 0x01 /* Experiment code */
+#define R_image_positions 0x88
+#define R_EX_datatype_shading_data 0xa0 /* Experiment code */
+#define R_user_reg_gamma 0xc0
+#define R_device_internal_info 0xe0
+#define set_R_datatype_qual_upper(sb, val) sb[0x04] = val
+#define set_R_datatype_qual_lower(sb, val) sb[0x05] = val
+#define R_DQ_none 0x00
+#define R_DQ_Rcomp 0x06
+#define R_DQ_Gcomp 0x07
+#define R_DQ_Bcomp 0x08
+#define R_DQ_Reg1 0x01
+#define R_DQ_Reg2 0x02
+#define R_DQ_Reg3 0x03
+#define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+/* ==================================================================== */
+
+/* Length of internal info structure */
+#define DI_length 256
+/* Functions for picking out data from the internal info structure */
+#define get_DI_ADbits(b) getnbyte(b + 0x00, 1)
+#define get_DI_Outputbits(b) getnbyte(b + 0x01, 1)
+#define get_DI_MaxResolution(b) getnbyte(b + 0x02, 2)
+#define get_DI_Xmax(b) getnbyte(b + 0x04, 2)
+#define get_DI_Ymax(b) getnbyte(b + 0x06, 2)
+#define get_DI_Xmaxpixel(b) getnbyte(b + 0x08, 2)
+#define get_DI_Ymaxpixel(b) getnbyte(b + 0x0a, 2)
+#define get_DI_currentY(b) getnbyte(b + 0x10, 2)
+#define get_DI_currentFocus(b) getnbyte(b + 0x12, 2)
+#define get_DI_currentscanpitch(b) getnbyte(b + 0x14, 1)
+#define get_DI_autofeeder(b) getnbyte(b + 0x1e, 1)
+#define get_DI_analoggamma(b) getnbyte(b + 0x1f, 1)
+#define get_DI_deviceerror0(b) getnbyte(b + 0x40, 1)
+#define get_DI_deviceerror1(b) getnbyte(b + 0x41, 1)
+#define get_DI_deviceerror2(b) getnbyte(b + 0x42, 1)
+#define get_DI_deviceerror3(b) getnbyte(b + 0x43, 1)
+#define get_DI_deviceerror4(b) getnbyte(b + 0x44, 1)
+#define get_DI_deviceerror5(b) getnbyte(b + 0x45, 1)
+#define get_DI_deviceerror6(b) getnbyte(b + 0x46, 1)
+#define get_DI_deviceerror7(b) getnbyte(b + 0x47, 1)
+#define get_DI_WBETR_R(b) getnbyte(b + 0x80, 2) /* White balance exposure time variable R */
+#define get_DI_WBETR_G(b) getnbyte(b + 0x82, 2)
+#define get_DI_WBETR_B(b) getnbyte(b + 0x84, 2)
+#define get_DI_PRETV_R(b) getnbyte(b + 0x88, 2) /* Prescan result exposure tim4e variable R */
+#define get_DI_PRETV_G(b) getnbyte(b + 0x8a, 2)
+#define get_DI_PRETV_B(b) getnbyte(b + 0x8c, 2)
+#define get_DI_CETV_R(b) getnbyte(b + 0x90, 2) /* Current exposure time variable R */
+#define get_DI_CETV_G(b) getnbyte(b + 0x92, 2)
+#define get_DI_CETV_B(b) getnbyte(b + 0x94, 2)
+#define get_DI_IETU_R(b) getnbyte(b + 0x98, 1) /* Internal exposure time unit R */
+#define get_DI_IETU_G(b) getnbyte(b + 0x99, 1)
+#define get_DI_IETU_B(b) getnbyte(b + 0x9a, 1)
+#define get_DI_limitcondition(b) getnbyte(b + 0xa0, 1)
+#define get_DI_offsetdata_R(b) getnbyte(b + 0xa1, 1)
+#define get_DI_offsetdata_G(b) getnbyte(b + 0xa2, 1)
+#define get_DI_offsetdata_B(b) getnbyte(b + 0xa3, 1)
+#define get_DI_poweron_errors(b,to) memcpy(to, (b + 0xa8), 8)
+
+/* ==================================================================== */
+
+static unsigned char sendC[] =
+{
+ SEND, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static scsiblk send =
+{sendC, sizeof (sendC)};
+
+#define set_S_datatype_code(sb, val) sb[0x02] = (unsigned char)val
+#define S_datatype_imagedatai 0x00
+#define S_EX_datatype_LUT 0x01 /* Experiment code */
+#define S_EX_datatype_shading_data 0xa0 /* Experiment code */
+#define S_user_reg_gamma 0xc0
+#define S_device_internal_info 0x03
+#define set_S_datatype_qual_upper(sb, val) sb[0x04] = (unsigned char)val
+#define S_DQ_none 0x00
+#define S_DQ_Rcomp 0x06
+#define S_DQ_Gcomp 0x07
+#define S_DQ_Bcomp 0x08
+#define S_DQ_Reg1 0x01
+#define S_DQ_Reg2 0x02
+#define S_DQ_Reg3 0x03
+#define S_DQ_Reg9 0x09
+#define set_S_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+/*
+ static unsigned char gamma_user_LUT_LS1K[512] = { 0x00 };
+ static scsiblk gamma_user_LUT_LS1K_LS1K = {
+ gamma_user_LUT_LS1K, sizeof(gamma_user_LUT_LS1K)
+ };
+ */
+
+/* ==================================================================== */
+
+static unsigned char object_positionC[] =
+{
+ OBJECT_POSITION,
+ 0x00, /* Auto feeder function */
+ 0x00, 0x00, 0x00, /* Count */
+ 0x00, 0x00, 0x00, 0x00, /* Reserved */
+ 0x00 /* Control byte */
+};
+#define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val)
+#define OP_Discharge 0x00
+#define OP_Feed 0x01
+#define OP_Absolute 0x02 /* For development only */
+
+static scsiblk object_position =
+{
+ object_positionC, sizeof (object_positionC)
+};
+/* ==================================================================== */
+static unsigned char autofocusC[] =
+{
+ AUTO_FOCUS, 0x00, 0x00, 0x00,
+ 0x00, /* transfer length (0|8) */
+ 0x00 /* Control byte */
+};
+#define set_AF_transferlength(b, val) b[0x04] = (unsigned char)val
+#define get_AF_transferlength(b) ((int)b[0x04] & 0xff)
+#define set_AF_XPoint(b, val) putnbyte(b+0x06, val, 4)
+#define set_AF_YPoint(b, val) putnbyte(b+0x0a, val, 4)
+#define AF_Point_length 8
+
+static scsiblk autofocus =
+{autofocusC, sizeof (autofocusC)};
+
+/* ==================================================================== */
+
+static unsigned char command_c1_C[] =
+{
+ 0xc1, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, /* */
+ 0x00, 0x00 /* transfer length*/
+};
+static scsiblk command_c1 =
+{command_c1_C, sizeof (command_c1_C)};
+/* ==================================================================== */
+
+static unsigned char autofocusLS30C[] =
+{
+ 0xe0, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, /* */
+ 0x09, 0x00 /* transfer length*/
+};
+
+static unsigned char autofocuspos[] =
+{
+ 0x00,
+ 0x00, 0x00, 0x05, 0x10, /* x-position */
+ 0x00, 0x00, 0x07, 0x9b, /* y-position */
+};
+
+#define set_AF_transferlength(b, val) b[0x04] = (unsigned char)val
+#define get_AF_transferlength(b) ((int)b[0x04] & 0xff)
+#define set_AF_XPoint(b, val) putnbyte(b+0x06, val, 4)
+#define set_AF_YPoint(b, val) putnbyte(b+0x0a, val, 4)
+#define AF_Point_length 8
+
+static scsiblk autofocusLS30 =
+{autofocusLS30C, sizeof (autofocusLS30C)};
+/* ==================================================================== */
+
+static unsigned char commande1C[] =
+{
+ 0xe1, 0x00, 0xc1, 0x00,
+ 0x00, 0x00, 0x00, 0x00, /* */
+ 0x0d, 0x00 /* transfer length*/
+};
+static scsiblk commande1 =
+{commande1C, sizeof (commande1C)};
+
+/* ==================================================================== */
+/*
+static unsigned char request_senseC[] =
+{
+ REQUEST_SENSE, 0x00, 0x00, 0x00, 0x00, 0x00
+#define set_RS_allocation_length(sb,val) sb[0x04] = (unsigned char)val
+};
+
+static scsiblk request_sense =
+{
+ request_senseC, sizeof (request_senseC)
+};
+*/
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */
+#define get_RS_additional_length(b) b[0x07] /* always 10 */
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid, always 0 */
+
+#define rs_return_block_size 18 /* Says Nikon */
+
+#endif
diff --git a/backend/coolscan.c b/backend/coolscan.c
new file mode 100644
index 0000000..00a12e4
--- /dev/null
+++ b/backend/coolscan.c
@@ -0,0 +1,4195 @@
+/* ------------------------------------------------------------------------- */
+/* sane - Scanner Access Now Easy.
+ coolscan.c , version 0.4.4
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for COOLSCAN flatbed scanners. */
+
+/* ------------------------------------------------------------------------- */
+
+
+/* SANE-FLOW-DIAGRAMM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner-device
+ - sane_exit() : terminate use of backend
+ */
+
+#ifdef _AIX
+# include "lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "lalloca.h"
+
+#include <errno.h>
+#include <math.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_thread.h"
+
+#include "../include/sane/sanei_config.h"
+#define COOLSCAN_CONFIG_FILE "coolscan.conf"
+#include "../include/sane/sanei_backend.h"
+
+#include "coolscan.h"
+#include "coolscan-scsidef.h"
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+static const SANE_Int resolution_list[] =
+{
+ 25,
+ 2700, 1350, 900, 675, 540, 450, 385, 337, 300, 270, 245, 225, 207,
+ 192, 180, 168, 158, 150, 142, 135, 128, 122, 117, 112, 108
+};
+
+
+#define coolscan_do_scsi_open(dev, fd, handler) sanei_scsi_open(dev, fd, handler)
+#define coolscan_do_scsi_close(fd) sanei_scsi_close(fd)
+
+#define COOLSCAN_MAX_RETRY 25
+
+
+static SANE_Status sense_handler (int scsi_fd, unsigned char * result, void *arg);
+static int coolscan_check_values (Coolscan_t * s);
+static int get_internal_info (Coolscan_t *);
+static void coolscan_get_inquiry_values (Coolscan_t *);
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+static int swap_res (Coolscan_t * s);
+/* --------------------------- COOLSCAN_DO_SCSI_CMD ----------------------- */
+static int
+do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len)
+{
+ int ret;
+ size_t ol = out_len;
+
+ hexdump (20, "", cmd, cmd_len);
+
+ ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol);
+ if ((out_len != 0) && (out_len != ol))
+ {
+ DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n",
+ (u_long) out_len, (u_long) ol);
+ }
+ if (ret)
+ {
+ DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret);
+ }
+ DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol);
+ if (out != NULL && out_len != 0)
+ hexdump (15, "", out, (out_len > 0x60) ? 0x60 : out_len);
+
+ return ret;
+}
+
+
+static int
+request_sense_parse (unsigned char *sensed_data)
+{
+ int ret, sense, asc, ascq;
+ sense = get_RS_sense_key (sensed_data);
+ asc = get_RS_ASC (sensed_data);
+ ascq = get_RS_ASCQ (sensed_data);
+
+ ret = SANE_STATUS_IO_ERROR;
+
+ switch (sense)
+ {
+ case 0x0:
+ DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq);
+ return SANE_STATUS_GOOD;
+
+ case 0x1:
+ if ((0x37 == asc) && (0x00 == ascq)) {
+ DBG (1, "\t%d/%d/%d: Rounded Parameter\n", sense, asc, ascq);
+ ret = SANE_STATUS_GOOD;
+ }
+ else if ((0x61 == asc) && (0x02 == ascq))
+ DBG (1, "\t%d/%d/%d: Out Of Focus\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x2:
+ if ((0x4 == asc) && (0x1 == ascq)) {
+ DBG (10, "\t%d/%d/%d: Logical unit is in process of becomming ready\n",
+ sense, asc, ascq);
+ ret = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if ((0x3A == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: No Diapo inserted\n", sense, asc, ascq);
+ ret = SANE_STATUS_GOOD;
+ }
+ else if ((0x60 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Lamp Failure\n", sense, asc, ascq);
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ ret = SANE_STATUS_GOOD;
+ }
+ break;
+
+ case 0x3:
+ if ((0x3b == asc) && (0xe == ascq))
+ DBG (1, "\t%d/%d/%d: Medium source element empty\n", sense, asc, ascq);
+ else if ((0x53 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Media Load of Eject Failed\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x4:
+ if ((0x15 == asc) && (0x1 == ascq))
+ DBG (1, "\t%d/%d/%d: Mechanical Positioning Error\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x5:
+ if ((0x00 == asc) && (0x5 == ascq))
+ DBG (1, "\t%d/%d/%d: End-Of-Data Detected\n", sense, asc, ascq);
+ else if ((0x1a == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Parameter List Length Error\n", sense, asc, ascq);
+ else if ((0x20 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Command Operation Code\n", sense, asc, ascq);
+ else if ((0x24 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Field In CDB\n", sense, asc, ascq);
+ else if ((0x25 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Logical Unit Not Supported\n", sense, asc, ascq);
+ else if ((0x26 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Field in Parameter List\n", sense, asc, ascq);
+ else if ((0x2c == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Command Sequence Error\n", sense, asc, ascq);
+ else if ((0x39 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Saving Parameters Not Supported\n", sense, asc, ascq);
+ else if ((0x3d == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Bits In Identify Message\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x6:
+ if ((0x29 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Power On, Reset, or Bus Device Reset Occured\n", sense, asc, ascq);
+ else if ((0x2a == asc) && (0x1 == ascq))
+ DBG (1, "\t%d/%d/%d: Mode Parameters Changed\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0xb:
+ if ((0x43 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Message Error\n", sense, asc, ascq);
+ else if ((0x47 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: SCSI Parity Error\n", sense, asc, ascq);
+ else if ((0x48 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Initiator Detected Error Message Received\n", sense, asc, ascq);
+ else if ((0x49 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Message Error\n", sense, asc, ascq);
+ else if ((0x4e == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Overlapped Commands Attempted\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ default:
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+ } /* switch */
+ return ret;
+}
+
+/*
+ * wait_scanner should spin until TEST_UNIT_READY returns 0 (GOOD)
+ * returns 0 on success,
+ * returns -1 on error.
+ */
+static int
+wait_scanner (Coolscan_t * s)
+{
+ int ret = -1;
+ int cnt = 0;
+ DBG (10, "wait_scanner: Testing if scanner is ready\n");
+
+ while (ret != 0)
+ {
+ ret = do_scsi_cmd (s->sfd, test_unit_ready.cmd,
+ test_unit_ready.size, 0, 0);
+
+ if (ret == SANE_STATUS_DEVICE_BUSY)
+ {
+ usleep (500000); /* wait 0.5 seconds */
+ if (cnt++ > 40)
+ { /* 20 sec. max (prescan takes up to 15 sec. */
+ DBG (1, "wait_scanner: scanner does NOT get ready\n");
+ return -1;
+ }
+ }
+ else if (ret == SANE_STATUS_GOOD)
+ {
+ DBG (10, "wait_scanner: scanner is ready\n");
+ return ret;
+ }
+ else
+ {
+ DBG (1, "wait_scanner: test unit ready failed (%s)\n",
+ sane_strstatus (ret));
+ }
+ }
+ return 0;
+}
+
+/* ------------------------- COOLSCAN GRAB SCANNER ----------------------------- */
+
+
+/* coolscan_grab_scanner should go through the following command sequence:
+ * TEST UNIT READY
+ * CHECK CONDITION \
+ * REQUEST SENSE > These should be handled automagically by
+ * UNIT ATTENTION / the kernel if they happen (powerup/reset)
+ * TEST UNIT READY
+ * GOOD
+ * RESERVE UNIT
+ * GOOD
+ *
+ * It is then responsible for installing appropriate signal handlers
+ * to call emergency_give_scanner() if user aborts.
+ */
+
+static int
+coolscan_grab_scanner (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "grabbing scanner\n");
+
+ wait_scanner (s); /* wait for scanner ready, if not print
+ sense and return 1 */
+ ret = do_scsi_cmd (s->sfd, reserve_unit.cmd, reserve_unit.size, NULL, 0);
+ if (ret)
+ return ret;
+
+ DBG (10, "scanner reserved\n");
+ return 0;
+}
+
+/*
+ * Convert a size in ilu to the units expected by the scanner
+ */
+
+static int
+resDivToVal (int res_div)
+{
+ if (res_div < 1 || res_div > resolution_list[0])
+ {
+ DBG (1, "Invalid resolution divisor %d \n", res_div);
+ return 2700;
+ }
+ else
+ {
+ return resolution_list[res_div];
+ }
+}
+
+static int
+resValToDiv (int res_val)
+{
+ int res_div;
+ int max_res = resolution_list[0];
+ for (res_div = 1; res_div <= max_res; res_div++)
+ {
+ if (resolution_list[res_div] == res_val)
+ break;
+ }
+ if (res_div > max_res)
+ {
+ DBG (1, "Invalid resolution value\n");
+ return 1;
+ }
+ else
+ {
+ return res_div;
+ }
+}
+/*
+ * use mode select to force a mesurement divisor of 2700
+ */
+static unsigned char mode_select[] =
+{
+ MODE_SELECT, 0x10, 0, 0, 20, 0,
+ 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0, 0, 1,
+ 3, 6, 0, 0, 0xA, 0x8C, 0, 0};
+
+static int
+select_MUD (Coolscan_t * s)
+{
+ return do_scsi_cmd (s->sfd, mode_select, 26, NULL, 0);
+}
+
+static int
+coolscan_autofocus_LS30 (Coolscan_t * s)
+{
+ int x, y;
+
+ wait_scanner(s);
+ memcpy(s->buffer, autofocusLS30.cmd, autofocusLS30.size);
+ memcpy(s->buffer+ autofocusLS30.size, autofocuspos, 9);
+
+ x = s->xmaxpix - (s->brx + s->tlx) / 2;
+ y = (s->bry + s->tly) / 2;
+
+ DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
+
+ do_scsi_cmd (s->sfd, s->buffer,
+ autofocusLS30.size + 9, NULL, 0);
+ /* Trashes when used in combination with scsi-driver AM53C974.o */
+ do_scsi_cmd (s->sfd, command_c1.cmd,
+ command_c1.size, NULL, 0);
+
+ DBG (10, "\tWaiting end of Autofocus\n");
+ wait_scanner (s);
+ DBG (10, "AutoFocused.\n");
+ return 0;
+}
+
+static int
+coolscan_autofocus (Coolscan_t * s)
+{
+ int x, y;
+
+ if(s->LS>=2)
+ { return coolscan_autofocus_LS30(s);
+ }
+
+ wait_scanner(s);
+ memcpy(s->buffer, autofocus.cmd, autofocus.size);
+
+ x = s->xmaxpix - (s->brx + s->tlx) / 2;
+ y = (s->bry + s->tly) / 2;
+
+ DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
+
+ set_AF_XPoint (s->buffer, x);
+ set_AF_YPoint (s->buffer, y);
+
+ set_AF_transferlength (s->buffer, 0); /* should be 8 !*/
+ do_scsi_cmd (s->sfd, s->buffer,
+ autofocus.size + AF_Point_length, NULL, 0);
+
+ sleep(5); /* autofocus takes a minimum of 5 sec. */
+
+ DBG (10, "\tWaiting end of Autofocus\n");
+ wait_scanner (s);
+ DBG (10, "AutoFocused.\n");
+ return 0;
+}
+
+/*
+ static int
+ coolscan_abort_scan (Coolscan_t * s)
+ {
+ int ret;
+
+ DBG (5, "Aborting scan...\n");
+ ret = do_scsi_cmd (s->sfd, sabort.cmd, sabort.size, NULL, 0);
+ if (ret)
+ DBG (5, "Scan Aborted\n");
+ else
+ DBG (5, "Not scanning\n");
+ return 0;
+ }
+ */
+static int
+coolscan_mode_sense (Coolscan_t * s)
+{
+ int ret, len;
+
+ DBG (10, "Mode Sense...\n");
+ len = 12;
+ set_MS_DBD (mode_sense.cmd, 1);
+ set_MS_len (mode_sense.cmd, len);
+ ret = do_scsi_cmd (s->sfd, mode_sense.cmd, mode_sense.size,
+ s->buffer, len);
+
+ if (ret == 0)
+ {
+ s->MUD = get_MS_MUD (s->buffer);
+ DBG (10, "\tMode Sensed (MUD is %d)\n", s->MUD);
+ }
+ return ret;
+}
+
+static int
+coolscan_object_discharge (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "Trying to discharge object...\n");
+
+ memcpy (s->buffer, object_position.cmd, object_position.size);
+ set_OP_autofeed (s->buffer, OP_Discharge);
+ ret = do_scsi_cmd (s->sfd, s->buffer,
+ object_position.size, NULL, 0);
+ wait_scanner (s);
+ DBG (10, "Object discharged.\n");
+ return ret;
+}
+
+static int
+coolscan_object_feed (Coolscan_t * s)
+{
+ int ret;
+ DBG (10, "Trying to feed object...\n");
+ if (!s->asf)
+ {
+ DBG (10, "\tAutofeeder not present.\n");
+ return 0;
+ }
+ memcpy (s->buffer, object_position.cmd, object_position.size);
+ set_OP_autofeed (s->buffer, OP_Feed);
+ ret = do_scsi_cmd (s->sfd, s->buffer,
+ object_position.size, NULL, 0);
+ wait_scanner (s);
+ DBG (10, "Object fed.\n");
+ return ret;
+}
+
+/* coolscan_give_scanner should go through the following sequence:
+ * OBJECT POSITION DISCHARGE
+ * GOOD
+ * RELEASE UNIT
+ * GOOD
+ */
+static int
+coolscan_give_scanner (Coolscan_t * s)
+{
+ DBG (10, "trying to release scanner ...\n");
+ coolscan_object_discharge (s);
+ wait_scanner (s);
+ do_scsi_cmd (s->sfd, release_unit.cmd, release_unit.size, NULL, 0);
+ DBG (10, "scanner released\n");
+ return 0;
+}
+
+
+static int
+coolscan_set_window_param_LS20 (Coolscan_t * s, int prescan)
+{
+ unsigned char buffer_r[max_WDB_size];
+ int ret;
+
+ wait_scanner (s);
+ memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
+ memcpy (buffer_r, window_descriptor_block.cmd,
+ window_descriptor_block.size); /* copy preset data */
+
+ set_WD_wid (buffer_r, WD_wid_all); /* window identifier */
+ set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */
+
+ set_WD_negative (buffer_r, s->negative); /* Negative/positive slide */
+
+ if (prescan)
+ {
+ set_WD_scanmode (buffer_r, WD_Prescan);
+ }
+ else
+ {
+ set_WD_scanmode (buffer_r, WD_Scan);
+
+ /* geometry */
+ set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */
+ set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */
+
+ /* the coolscan uses the upper right corner as the origin of coordinates */
+ /* xmax and ymax are given in 1200 dpi */
+ set_WD_ULX (buffer_r, (s->xmaxpix - s->brx));
+ set_WD_ULY (buffer_r, s->tly); /* upper_edge y */
+ set_WD_width (buffer_r, (s->brx - s->tlx + 1));
+ set_WD_length (buffer_r, (s->bry - s->tly + 1));
+
+ /* BTC */
+ if (s->brightness == 128)
+ {
+ set_WD_brightness (buffer_r, 0);
+ }
+ else
+ {
+ set_WD_brightness (buffer_r, s->brightness); /* brightness */
+ }
+
+ if (s->contrast == 128)
+ {
+ set_WD_contrast (buffer_r, 0);
+ }
+ else
+ {
+ set_WD_contrast (buffer_r, s->contrast); /* contrast */
+ }
+
+ /* scanmode */
+ if (s->colormode == GREYSCALE)
+ set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */
+ else
+ set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */
+
+ set_WD_dropoutcolor (buffer_r, s->dropoutcolor); /* Which color to scan with when grayscale scan */
+ set_WD_transfermode (buffer_r, WD_LineSequence);
+ set_WD_gammaselection (buffer_r, s->gammaselection); /* monitor/linear */
+
+ set_WD_shading (buffer_r, WD_Shading_ON); /* default for non-manufacturing */
+
+ if (1 == s->LS)
+ { /* Analog gamma reserved on LS-1000 */
+ set_WD_analog_gamma_R (buffer_r, 0);
+ set_WD_analog_gamma_G (buffer_r, 0);
+ set_WD_analog_gamma_R (buffer_r, 0);
+ }
+ else
+ {
+ /* Quote spec: "It is recomended that analog gamma bits 5, 4 and 3 be
+ * set to 1 (OFF) when the object type of byte 48 is positive and the
+ * gamma specificateion of byte 51 is linear, and to 0 (ON) in all
+ * other cases." */
+ /*
+ int foo;
+ if ((buffer_r[48] == WD_Positive) && (buffer_r[51] == WD_Linear))
+ foo = WD_Analog_Gamma_OFF;
+ else
+ foo = WD_Analog_Gamma_ON;
+ set_WD_analog_gamma_R (buffer_r, foo);
+ set_WD_analog_gamma_G (buffer_r, foo);
+ set_WD_analog_gamma_B (buffer_r, foo);
+ */
+ set_WD_analog_gamma_R (buffer_r, s->analog_gamma_r);
+ set_WD_analog_gamma_G (buffer_r, s->analog_gamma_g);
+ set_WD_analog_gamma_B (buffer_r, s->analog_gamma_b);
+ if (s->gamma_bind)
+ {
+ set_WD_LUT_R (buffer_r, 1);
+ set_WD_LUT_G (buffer_r, 1);
+ set_WD_LUT_B (buffer_r, 1);
+ }
+ else
+ {
+ set_WD_LUT_R (buffer_r, 1);
+ set_WD_LUT_G (buffer_r, 2);
+ set_WD_LUT_B (buffer_r, 3);
+ }
+ }
+ set_WD_averaging (buffer_r, s->averaging);
+
+ set_WD_brightness_R (buffer_r, s->brightness_R);
+ set_WD_brightness_G (buffer_r, s->brightness_G);
+ set_WD_brightness_B (buffer_r, s->brightness_B);
+
+ set_WD_contrast_R (buffer_r, s->contrast_R);
+ set_WD_contrast_G (buffer_r, s->contrast_G);
+ set_WD_contrast_B (buffer_r, s->contrast_B);
+
+ set_WD_exposure_R (buffer_r, s->exposure_R);
+ set_WD_exposure_G (buffer_r, s->exposure_G);
+ set_WD_exposure_B (buffer_r, s->exposure_B);
+ set_WD_shift_R (buffer_r, s->shift_R);
+ set_WD_shift_G (buffer_r, s->shift_G);
+ set_WD_shift_B (buffer_r, s->shift_B);
+
+
+ /* FIXME: LUT-[RGB] */
+ /* FIXME: stop on/off */
+ }
+
+ DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
+ s->x_nres, s->y_nres, s->tlx, s->tly);
+ DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
+ s->brx - s->tlx, s->MUD, s->brx);
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+ DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ /* prepare SCSI-BUFFER */
+ memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */
+ memcpy ((s->buffer + set_window.size), /* add WPDB */
+ window_parameter_data_block.cmd,
+ window_parameter_data_block.size);
+ set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size); /* set WD_len */
+ memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
+ buffer_r, window_descriptor_block.size);
+
+ hexdump (15, "Window set", buffer_r, s->wdb_len);
+
+ set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
+ window_descriptor_block.size));
+
+ ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
+ window_parameter_data_block.size +
+ window_descriptor_block.size,
+ NULL, 0);
+ DBG (10, "window set.\n");
+ return ret;
+}
+
+static int
+coolscan_set_window_param_LS30 (Coolscan_t * s, int wid, int prescan)
+{
+ unsigned char buffer_r[max_WDB_size];
+ int ret;
+
+ wait_scanner (s);
+ memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
+ memcpy (buffer_r, window_descriptor_block_LS30.cmd,
+ window_descriptor_block_LS30.size); /* copy preset data */
+
+ set_WD_wid (buffer_r, wid); /* window identifier */
+ set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */
+
+ /* geometry */
+ set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */
+ set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */
+
+ if (prescan)
+ {
+ set_WD_scanmode_LS30 (buffer_r, WD_Prescan);
+ set_WD_Xres (buffer_r, resDivToVal (1)); /* x res. in dpi */
+ set_WD_Yres (buffer_r, resDivToVal (1)); /* y res. in dpi */
+ buffer_r[0x29]=0x81;
+ buffer_r[0x2a]=0x04;
+ buffer_r[0x2b]=0x02;
+ buffer_r[0x2c]=0x01;
+ buffer_r[0x2d]=0xff;
+ buffer_r[0x30]=0x00;
+ buffer_r[0x31]=0x00;
+ buffer_r[0x32]=0x00;
+ buffer_r[0x33]=0x00;
+ set_WD_width (buffer_r,(2592));
+ set_WD_length (buffer_r,(3894));
+ }
+ else
+ {
+ set_WD_scanmode_LS30 (buffer_r, WD_Scan);
+
+ /* the coolscan LS-30 uses the upper left corner
+ as the origin of coordinates */
+ /* xmax and ymax are given in 1200 dpi */
+ set_WD_ULX (buffer_r, s->tlx);
+ set_WD_ULY (buffer_r, s->tly); /* upper_edge y */
+ set_WD_width (buffer_r, (s->brx - s->tlx+1));
+ set_WD_length (buffer_r, (s->bry - s->tly+1));
+
+ /* BTC */
+ if (s->brightness == 128)
+ {
+ buffer_r[0x32]=0x00;
+ }
+ else
+ {
+ buffer_r[0x32]=s->brightness; /* brightness */
+ }
+
+ if (s->contrast == 128)
+ {
+ buffer_r[0x33]=0x00;
+ }
+ else
+ {
+ buffer_r[0x33]=s->contrast; /* contrast */
+ }
+
+ /* scanmode */
+ if (s->colormode == GREYSCALE)
+ set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */
+ else
+ set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */
+
+ set_WD_composition (buffer_r, WD_comp_rgb_full); /* allways RGB composition */
+
+ /* Bits per pixel */
+ set_WD_bitsperpixel(buffer_r, s->bits_per_color);
+
+ buffer_r[0x29]=0x81;
+ buffer_r[0x2a]=0x01;
+ buffer_r[0x2b]=0x02;
+ buffer_r[0x2c]=0x01;
+ buffer_r[0x2d]=0xff;
+ buffer_r[0x30]=0x00;
+
+ }
+ set_WD_negative_LS30(buffer_r, s->negative); /* Negative/positive slide */
+
+ switch(wid)
+ { case 1: set_gain_LS30(buffer_r,(s->exposure_R*s->pretv_r)/50);
+ break;
+ case 2: set_gain_LS30(buffer_r,(s->exposure_G*s->pretv_g)/50);
+ break;
+ case 3: set_gain_LS30(buffer_r,(s->exposure_B*s->pretv_b)/50);
+ break;
+ }
+
+ DBG (10, "\texpo_r=%d, expo_g=%d, expob=%d\n",
+ s->exposure_R, s->exposure_G, s->exposure_B);
+ DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
+ s->pretv_r, s->pretv_g, s->pretv_b);
+ DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
+ s->x_nres, s->y_nres, s->tlx, s->tly);
+ DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
+ s->brx - s->tlx, s->MUD, s->brx);
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+ DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ /* prepare SCSI-BUFFER */
+ memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */
+ memcpy ((s->buffer + set_window.size), /* add WPDB */
+ window_parameter_data_block.cmd,
+ window_parameter_data_block.size);
+ set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size_LS30); /* set WD_len */
+ memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
+ buffer_r, window_descriptor_block_LS30.size);
+
+ hexdump (15, "Window set", buffer_r, s->wdb_len);
+
+ set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
+ window_descriptor_block_LS30.size));
+
+ ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
+ window_parameter_data_block.size +
+ window_descriptor_block_LS30.size,
+ NULL, 0);
+ DBG (10, "window set.\n");
+ return ret;
+}
+
+static int
+coolscan_set_window_param (Coolscan_t * s, int prescan)
+{
+ int ret;
+ ret=0;
+ DBG (10, "set_window_param\n");
+
+ if(s->LS<2) /* distinquish between old and new scanners */
+ { ret=coolscan_set_window_param_LS20 (s,prescan);
+ }
+ else
+ { do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
+ wait_scanner (s);
+ wait_scanner (s);
+ coolscan_set_window_param_LS30(s,1,prescan);
+ ret=coolscan_set_window_param_LS30(s,2,prescan);
+ ret=coolscan_set_window_param_LS30(s,3,prescan);
+ if(s->colormode&0x08)
+ { ret=coolscan_set_window_param_LS30(s,9,prescan);
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * The only purpose of get_window is debugging. None of the return parameters
+ * is currently used.
+ */
+static int
+coolscan_get_window_param_LS30 (Coolscan_t * s, int wid,int prescanok)
+{
+ int translen;
+ unsigned char *buf;
+
+ DBG (10, "GET_WINDOW_PARAM\n");
+ /* wait_scanner (s); */
+
+ translen = window_parameter_data_block.size + window_descriptor_block_LS30.size;
+
+ /* prepare SCSI-BUFFER */
+ memset (s->buffer, '\0', max_WDB_size); /* clear buffer */
+
+ set_SW_xferlen (get_window.cmd, translen); /* Transfer length */
+ get_window.cmd[5]= wid; /* window identifier */
+
+ hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
+ do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
+ s->buffer, translen);
+
+ buf = s->buffer + window_parameter_data_block.size;
+ hexdump (10, "Window get", buf, 117);
+
+ s->brightness = buf[0x32]; /* brightness */
+ s->contrast = buf[0x33]; /* contrast */
+ DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
+
+ /* Useful? */
+ s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */
+
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+
+ if(prescanok)
+ { switch(wid)
+ { case 1: s->pretv_r = get_gain_LS30(buf);
+ break;
+ case 2: s->pretv_g = get_gain_LS30(buf);
+ break;
+ case 3: s->pretv_b = get_gain_LS30(buf);
+ break;
+ }
+ }
+
+ /* Should this one be set at all, here? */
+ s->transfermode = get_WD_transfermode (buf);
+
+ s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */
+ DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
+ s->pretv_r, s->pretv_g, s->pretv_b);
+
+ DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ DBG (10, "get_window_param - return\n");
+ return 0;
+}
+
+/*
+ * The only purpose of get_window is debugging. None of the return parameters
+ * is currently used.
+ */
+static int
+coolscan_get_window_param_LS20 (Coolscan_t * s)
+{
+ int translen;
+ unsigned char *buf;
+
+ DBG (10, "GET_WINDOW_PARAM\n");
+ wait_scanner (s);
+
+ translen = window_parameter_data_block.size + window_descriptor_block.size;
+
+ /* prepare SCSI-BUFFER */
+ memset (s->buffer, '\0', max_WDB_size); /* clear buffer */
+
+ set_SW_xferlen (get_window.cmd, translen); /* Transfer length */
+
+ hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
+ do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
+ s->buffer, translen);
+
+ buf = s->buffer + window_parameter_data_block.size;
+ hexdump (10, "Window get", buf, 117);
+
+ /* BTC */
+ s->brightness = get_WD_brightness (buf); /* brightness */
+ s->contrast = get_WD_contrast (buf); /* contrast */
+ DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
+
+ if (WD_comp_gray == get_WD_composition (buf))
+ s->colormode = GREYSCALE;
+ else
+ s->colormode = RGB;
+
+ /* Useful? */
+ s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */
+
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+
+
+ s->dropoutcolor = get_WD_dropoutcolor (buf); /* Which color to scan with when grayscale scan */
+
+ /* Should this one be set at all, here? */
+ s->transfermode = get_WD_transfermode (buf);
+
+ s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */
+
+ DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ /* Should this one be set at all? */
+ s->shading = get_WD_shading (buf);
+ s->averaging = get_WD_averaging (buf);
+ DBG (10, "get_window_param - return\n");
+ return 0;
+}
+
+/*
+ * The only purpose of get_window is debugging. None of the return parameters
+ * is currently used.
+ */
+static int
+coolscan_get_window_param (Coolscan_t * s, int prescanok)
+{
+ int ret;
+ DBG (10, "get_window_param\n");
+
+ ret=0;
+ if(s->LS<2) /* distinquish between old and new scanners */
+ { ret=coolscan_get_window_param_LS20 (s);
+ }
+ else
+ {
+ ret=coolscan_get_window_param_LS30(s,1,prescanok);
+ ret=coolscan_get_window_param_LS30(s,2,prescanok);
+ ret=coolscan_get_window_param_LS30(s,3,prescanok);
+ if(s->colormode&0x08)
+ { ret=coolscan_get_window_param_LS30(s,9,prescanok);
+ }
+ }
+ return ret;
+}
+
+static int
+coolscan_start_scanLS30 (Coolscan_t * s)
+{ int channels;
+ DBG (10, "starting scan\n");
+
+ channels=1;
+ memcpy (s->buffer, scan.cmd, scan.size);
+ switch(s->colormode)
+ { case RGB:
+ case GREYSCALE:
+ channels=s->buffer[4]=0x03; /* window 1 */
+ s->buffer[6]=0x01; /* window 1 */
+ s->buffer[7]=0x02; /* window 2 */
+ s->buffer[8]=0x03; /* window 3 */
+
+ break;
+ case RGBI:
+ channels=s->buffer[4]=0x04; /* window 1 */
+ s->buffer[6]=0x01; /* window 1 */
+ s->buffer[7]=0x02; /* window 2 */
+ s->buffer[8]=0x03; /* window 3 */
+ s->buffer[9]=0x09; /* window 3 */
+ break;
+ case IRED:
+ channels=s->buffer[4]=0x01; /* window 1 */
+ s->buffer[8]=0x09; /* window 3 */
+ break;
+ }
+
+ return do_scsi_cmd (s->sfd, s->buffer, scan.size+channels, NULL, 0);
+}
+
+static int
+coolscan_start_scan (Coolscan_t * s)
+{
+ DBG (10, "starting scan\n");
+ if(s->LS>=2)
+ { return coolscan_start_scanLS30(s);
+ }
+ return do_scsi_cmd (s->sfd, scan.cmd, scan.size, NULL, 0);
+}
+
+
+static int
+prescan (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "Starting prescan...\n");
+ if(s->LS<2)
+ { coolscan_set_window_param (s, 1);
+ }
+ else
+ {
+ do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
+ wait_scanner (s);
+ wait_scanner (s);
+ coolscan_set_window_param_LS30 (s,1,1);
+ coolscan_set_window_param_LS30 (s,2,1);
+ coolscan_set_window_param_LS30 (s,3,1);
+
+ }
+ ret = coolscan_start_scan(s);
+
+ sleep(8); /* prescan takes a minimum of 10 sec. */
+ wait_scanner (s);
+ DBG (10, "Prescan done\n");
+ return ret;
+}
+
+static SANE_Status
+do_prescan_now (Coolscan_t * scanner)
+{
+
+ DBG (10, "do_prescan_now \n");
+ if (scanner->scanning == SANE_TRUE)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (scanner->sfd < 0)
+ { /* first call */
+ if (sanei_scsi_open (scanner->sane.name,
+ &(scanner->sfd),
+ sense_handler, 0) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "do_prescan_now: open of %s failed:\n",
+ scanner->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ scanner->scanning = SANE_TRUE;
+
+
+ if (coolscan_check_values (scanner) != 0)
+ { /* Verify values */
+ DBG (1, "ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (coolscan_grab_scanner (scanner))
+ {
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ DBG (5, "WARNING: unable to reserve scanner: device busy\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ prescan (scanner);
+ if(scanner->LS<2)
+ { get_internal_info(scanner);
+ }
+ coolscan_get_window_param (scanner,1);
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ return SANE_STATUS_GOOD;
+}
+
+
+static int
+send_one_LUT (Coolscan_t * s, SANE_Word * LUT, int reg)
+{
+ int i;
+ short lutval;
+ short bytesperval;
+ unsigned char *gamma, *gamma_p;
+ unsigned short *gamma_s;
+
+ DBG (10, "send LUT\n");
+
+ if(s->LS<2)
+ { set_S_datatype_code (send.cmd, R_user_reg_gamma);
+ bytesperval=1;
+ }
+ else
+ {
+ send.cmd[0x02]=3;
+ send.cmd[0x05]=1;
+ bytesperval=2;
+ }
+
+ set_S_xfer_length (send.cmd, s->lutlength*bytesperval);
+ set_S_datatype_qual_upper (send.cmd, reg);
+
+ gamma = alloca (send.size + s->lutlength*2);
+ memcpy (gamma, send.cmd, send.size);
+ if(s->LS<2)
+ { gamma_p = &gamma[send.size];
+ for (i = 0; i < s->lutlength; i++)
+ {
+ if (LUT[i] > 255)
+ LUT[i] = 255; /* broken gtk */
+ *gamma_p++ = (unsigned char) LUT[i];
+ }
+ }
+ else if(s->LS==2)
+ { gamma_s = (unsigned short*)( &gamma[send.size]);
+ for (i = 0; i < s->lutlength; i++)
+ {
+ if(s->negative)
+ {
+ lutval=(unsigned short)(LUT[(s->lutlength-i)]);
+ }
+ else
+ {
+ lutval=(unsigned short)(LUT[i]);
+ }
+ if (LUT[i] >= s->max_lut_val)
+ LUT[i] = s->max_lut_val-1; /* broken gtk */
+ if(s->low_byte_first) /* if on little endian machine: */
+ {
+ lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */
+ }
+ *gamma_s++ = lutval;
+ }
+ }
+ else if(s->LS==3)
+ { gamma_s = (unsigned short*)( &gamma[send.size]);
+ for (i = 0; i < s->lutlength; i++)
+ {
+ if(s->negative)
+ {
+ lutval=(unsigned short)(LUT[s->lutlength-i]);
+ }
+ else
+ {
+ lutval=(unsigned short)(LUT[i]);
+ }
+ if (LUT[i] >= s->max_lut_val)
+ LUT[i] = s->max_lut_val-1; /* broken gtk */
+ if(s->low_byte_first) /* if on little endian machine: */
+ { lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */
+ }
+ *gamma_s++ = lutval;
+ }
+ }
+ return do_scsi_cmd (s->sfd, gamma, send.size + s->lutlength*bytesperval, NULL, 0);
+}
+
+
+static int
+send_LUT (Coolscan_t * s)
+{
+ wait_scanner (s);
+ if (s->gamma_bind)
+ {
+ send_one_LUT (s, s->gamma, S_DQ_Reg1);
+ if(s->LS>=2)
+ { send_one_LUT (s, s->gamma, S_DQ_Reg2);
+ send_one_LUT (s, s->gamma, S_DQ_Reg3);
+ if(s->colormode&0x08)
+ { send_one_LUT (s, s->gamma, S_DQ_Reg9);
+ }
+
+ }
+ }
+ else
+ {
+ send_one_LUT (s, s->gamma_r, S_DQ_Reg1);
+ send_one_LUT (s, s->gamma_g, S_DQ_Reg2);
+ send_one_LUT (s, s->gamma_b, S_DQ_Reg3);
+ if(s->colormode&0x08)
+ { send_one_LUT (s, s->gamma_r, S_DQ_Reg9);
+ }
+ }
+ return 0;
+}
+
+
+static int
+coolscan_read_data_block (Coolscan_t * s, unsigned int datatype, unsigned int length)
+{
+ int r;
+
+ DBG (10, "read_data_block (type= %x length = %d)\n",datatype,length);
+ /*wait_scanner(s); */
+
+ set_R_datatype_code (sread.cmd, datatype);
+ sread.cmd[4]=00;
+ sread.cmd[5]=00;
+ set_R_xfer_length (sread.cmd, length);
+
+ r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, length);
+ return ((r != 0) ? -1 : (int) length);
+}
+
+
+static void
+coolscan_do_inquiry (Coolscan_t * s)
+{
+ int size;
+
+ DBG (10, "do_inquiry\n");
+ memset (s->buffer, '\0', 256); /* clear buffer */
+ size = 36; /* Hardcoded, and as specified by Nikon */
+ /* then get inquiry with actual size */
+ set_inquiry_return_size (inquiry.cmd, size);
+ do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size);
+}
+
+static int
+coolscan_identify_scanner (Coolscan_t * s)
+{
+ unsigned char vendor[9];
+ unsigned char product[0x11];
+ unsigned char version[5];
+ unsigned char *pp;
+ int i;
+
+ vendor[8] = product[0x10] = version[4] = 0;
+ DBG (10, "identify_scanner\n");
+ coolscan_do_inquiry (s); /* get inquiry */
+ if (get_inquiry_periph_devtype (s->buffer) != IN_periph_devtype_scanner)
+ {
+ DBG (5, "identify_scanner: not a scanner\n");
+ return 1;
+ } /* no, continue searching */
+
+ coolscan_get_inquiry_values (s);
+
+ get_inquiry_vendor ((char *)s->buffer, (char *)vendor);
+ get_inquiry_product ((char *)s->buffer, (char *)product);
+ get_inquiry_version ((char *)s->buffer, (char *)version);
+
+ if (strncmp ("Nikon ", (char *)vendor, 8))
+ {
+ DBG (5, "identify_scanner: \"%s\" isn't a Nikon product\n", vendor);
+ return 1;
+ } /* Not a Nikon product */
+
+ pp = &vendor[8];
+ vendor[8] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &product[0x10];
+ product[0x10] = ' ';
+ while (*(pp - 1) == ' ')
+ {
+ *pp-- = '\0';
+ } /* leave one blank at the end! */
+
+ pp = &version[4];
+ version[4] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ DBG (10, "Found Nikon scanner %sversion %s on device %s\n",
+ product, version, s->devicename);
+
+ /* look for scanners that do not give all inquiry-informations */
+ /* and if possible use driver-known inquiry-data */
+ if (get_inquiry_additional_length (s->buffer) >= 0x1f)
+ {
+ /* Now identify full supported scanners */
+ for (i = 0; i < known_scanners; i++)
+ {
+ if (!strncmp ((char *)product, scanner_str[i], strlen (scanner_str[i])))
+ {
+ s->LS = i;
+ return 0;
+ }
+ }
+ if (s->cont)
+ return 0;
+ else
+ return 1;
+ }
+ else
+ return 1;
+}
+
+static int
+pixels_per_line (Coolscan_t * s)
+{
+ int pic_dot;
+ if(s->LS<2)
+ { pic_dot = (s->brx - s->tlx + s->x_nres) / s->x_nres;
+ }
+ else
+ { pic_dot = (s->brx - s->tlx + 1) / s->x_nres;
+ }
+ DBG (10, "pic_dot=%d\n", pic_dot);
+ return pic_dot;
+}
+
+static int
+lines_per_scan (Coolscan_t * s)
+{
+ int pic_line;
+ if(s->LS<2)
+ { pic_line = (s->bry - s->tly + s->y_nres) / s->y_nres;
+ }
+ else
+ { pic_line = (( s->bry - s->tly + 1.0 ) / s->y_nres);
+ }
+ DBG (10, "pic_line=%d\n", pic_line);
+ return pic_line;
+}
+
+static int
+scan_bytes_per_line (Coolscan_t * s)
+{ int bpl;
+ switch(s->colormode)
+ { case RGB:
+ case GREYSCALE:
+ bpl=pixels_per_line (s) * 3;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ case RGBI:
+ case IRED:
+ bpl=pixels_per_line (s) * 4;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ }
+ return 0;
+}
+
+static int
+write_bytes_per_line (Coolscan_t * s)
+{ int bpl;
+ switch(s->colormode)
+ { case RGB:
+ bpl=pixels_per_line (s) * 3;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ case RGBI:
+ bpl=pixels_per_line (s) * 4;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ case IRED:
+ case GREYSCALE:
+ bpl= pixels_per_line (s) ;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ }
+ return 0;
+}
+
+
+static void
+coolscan_trim_rowbufsize (Coolscan_t * s)
+{
+ unsigned int row_len;
+ row_len = scan_bytes_per_line (s);
+ s->row_bufsize = (s->row_bufsize < row_len) ? s->row_bufsize
+ : s->row_bufsize - (s->row_bufsize % row_len);
+ DBG (10, "trim_bufsize to %d\n", s->row_bufsize);
+}
+
+static int
+coolscan_check_values (Coolscan_t * s)
+{
+ DBG (10, "check_values\n");
+ /* -------------------------- asf --------------------------------- */
+ if (s->asf != 0)
+ {
+ if (s->autofeeder == 0)
+ {
+ DBG (1, "ERROR: ASF-MODE NOT SUPPORTED BY SCANNER, ABORTING\n");
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* test_little_endian */
+
+static SANE_Bool
+coolscan_test_little_endian(void)
+{
+ SANE_Int testvalue = 255;
+ unsigned char *firstbyte = (unsigned char *) &testvalue;
+
+ if (*firstbyte == 255)
+ { return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+static int
+get_inquiery_part_LS30 (Coolscan_t * s, unsigned char part)
+{
+ int size;
+ int ret;
+
+ /* Get length of reponse */
+ inquiry.cmd[1]=0x01;
+ inquiry.cmd[2]=part;
+ size=4;
+ set_inquiry_return_size (inquiry.cmd, size);
+ ret = do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
+ s->buffer, size);
+ size=get_inquiry_length(s->buffer);
+ size+=4;
+ /* then get inquiry with actual size */
+ set_inquiry_return_size (inquiry.cmd, size);
+ ret = do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
+ s->buffer, size);
+ return size;
+}
+
+static int
+coolscan_read_var_data_block (Coolscan_t * s,int datatype)
+{
+ int r;
+ int size;
+
+ DBG (10, "read_data_block (type= %x)\n",datatype);
+ /*wait_scanner(s); */
+
+ sread.cmd[2]=datatype;
+ sread.cmd[4]=00;
+ sread.cmd[5]=03;
+ size=6;
+ set_R_xfer_length (sread.cmd, size);
+ r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
+ s->buffer, size);
+ size=s->buffer[5];
+ set_R_xfer_length (sread.cmd, size);
+ r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
+ s->buffer, size);
+ return ((r != 0) ? -1 : size);
+}
+
+static int
+get_inquiery_LS30 (Coolscan_t * s)
+{
+ unsigned char part;
+ unsigned char parts[5];
+ int size;
+ int i;
+
+ /* Get vector of inquiery parts */
+ size=get_inquiery_part_LS30(s, (unsigned char) 0);
+ /* Get the parts of inquiery */
+ for(i=0;i<5;i++)
+ { parts[i]=((unsigned char *)s->buffer)[4+11+i];
+ }
+ for(i=0;i<5;i++)
+ { part=parts[i];
+ size=get_inquiery_part_LS30 (s, part);
+ switch(part)
+ { case 0x0c1:/* max size and resolution */
+ s->adbits = 8;
+ s->outputbits = 8;
+ s->maxres = getnbyte(s->buffer+0x12,2)-1;
+ s->xmaxpix = getnbyte(s->buffer+0x53,2)-1;
+ s->ymaxpix = getnbyte(s->buffer+0x3c,2)-1;
+ break;
+ case 0x0d1:
+ break;
+ case 0x0e1:
+ break;
+ case 0x0f0:
+ break;
+ case 0x0f8:
+ break;
+ }
+ }
+
+ /* get windows */
+ coolscan_get_window_param_LS30 (s,0,0);
+ s->xmax = get_WD_width(s->buffer);
+ s->ymax = get_WD_length(s->buffer);
+ coolscan_get_window_param_LS30 (s,1,0);
+ coolscan_get_window_param_LS30 (s,2,0);
+ coolscan_get_window_param_LS30 (s,3,0);
+ coolscan_get_window_param_LS30 (s,4,0);
+ coolscan_get_window_param_LS30 (s,9,0);
+
+ s->analoggamma = 0;
+ return 1;
+}
+
+static int
+get_feeder_type_LS30 (Coolscan_t * s)
+{
+ int size;
+ unsigned char *ptr;
+ int ima;
+
+ /* find out about Film-strip-feeder or Mount-Feeder */
+ size=get_inquiery_part_LS30(s, (unsigned char) 1);
+ if(strncmp((char *)s->buffer+5,"Strip",5)==0)
+ { s->feeder=STRIP_FEEDER;
+ s->autofeeder = 1;
+ }
+ if(strncmp((char *)s->buffer+5,"Mount",5)==0)
+ { s->feeder=MOUNT_FEEDER;
+ }
+ /* find out about Film-strip-feeder positions*/
+ if(s->feeder==STRIP_FEEDER)
+ { size=coolscan_read_var_data_block (s,(int)0x88);
+ if(size>=4)
+ { s->numima=s->buffer[3];
+ if(s->numima>6) s->numima=6; /* limit to 6 images for now */
+ if(s->numima>(size-4)/16) s->numima=(size-4)/16;
+ ptr=s->buffer+4;
+ for(ima=0;ima<s->numima;ima++)
+ { s->ipos[ima].start=getnbyte(ptr,4);
+ s->ipos[ima].offset=getnbyte(ptr+4,4);
+ s->ipos[ima].end=getnbyte(ptr+8,4);
+ s->ipos[ima].height=getnbyte(ptr+12,4);
+ ptr+=16;
+ }
+ }
+ s->posima=0;
+ }
+ return 1;
+}
+
+
+static int
+get_internal_info_LS20 (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "get_internal_info\n");
+ wait_scanner (s);
+ memset (s->buffer, '\0', DI_length); /* clear buffer */
+
+ set_R_datatype_code (sread.cmd, R_device_internal_info);
+ set_R_datatype_qual_upper (sread.cmd, R_DQ_none);
+ set_R_xfer_length (sread.cmd, DI_length);
+ /* then get inquiry with actual size */
+ ret = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
+ s->buffer, DI_length);
+
+ s->adbits = get_DI_ADbits (s->buffer);
+ s->outputbits = get_DI_Outputbits (s->buffer);
+ s->maxres = get_DI_MaxResolution (s->buffer);
+ s->xmax = get_DI_Xmax (s->buffer);
+ s->ymax = get_DI_Ymax (s->buffer);
+ s->xmaxpix = get_DI_Xmaxpixel (s->buffer);
+ s->ymaxpix = get_DI_Ymaxpixel (s->buffer);
+ s->ycurrent = get_DI_currentY (s->buffer);
+ s->currentfocus = get_DI_currentFocus (s->buffer);
+ s->currentscanpitch = get_DI_currentscanpitch (s->buffer);
+ s->autofeeder = get_DI_autofeeder (s->buffer);
+ s->analoggamma = get_DI_analoggamma (s->buffer);
+ s->derr[0] = get_DI_deviceerror0 (s->buffer);
+ s->derr[1] = get_DI_deviceerror1 (s->buffer);
+ s->derr[2] = get_DI_deviceerror2 (s->buffer);
+ s->derr[3] = get_DI_deviceerror3 (s->buffer);
+ s->derr[4] = get_DI_deviceerror4 (s->buffer);
+ s->derr[5] = get_DI_deviceerror5 (s->buffer);
+ s->derr[6] = get_DI_deviceerror6 (s->buffer);
+ s->derr[7] = get_DI_deviceerror7 (s->buffer);
+ s->wbetr_r = get_DI_WBETR_R (s->buffer);
+ s->webtr_g = get_DI_WBETR_G (s->buffer);
+ s->webtr_b = get_DI_WBETR_B (s->buffer);
+ s->pretv_r = get_DI_PRETV_R (s->buffer);
+ s->pretv_g = get_DI_PRETV_G (s->buffer);
+ s->pretv_r = get_DI_PRETV_R (s->buffer);
+ s->cetv_r = get_DI_CETV_R (s->buffer);
+ s->cetv_g = get_DI_CETV_G (s->buffer);
+ s->cetv_b = get_DI_CETV_B (s->buffer);
+ s->ietu_r = get_DI_IETU_R (s->buffer);
+ s->ietu_g = get_DI_IETU_G (s->buffer);
+ s->ietu_b = get_DI_IETU_B (s->buffer);
+ s->limitcondition = get_DI_limitcondition (s->buffer);
+ s->offsetdata_r = get_DI_offsetdata_R (s->buffer);
+ s->offsetdata_g = get_DI_offsetdata_G (s->buffer);
+ s->offsetdata_b = get_DI_offsetdata_B (s->buffer);
+ get_DI_poweron_errors (s->buffer, s->power_on_errors);
+
+ DBG (10,
+ "\tadbits=%d\toutputbits=%d\tmaxres=%d\txmax=%d\tymax=%d\n"
+ "\txmaxpix=%d\tymaxpix=%d\tycurrent=%d\tcurrentfocus=%d\n"
+ "\tautofeeder=%s\tanaloggamma=%s\tcurrentscanpitch=%d\n",
+ s->adbits, s->outputbits, s->maxres, s->xmax, s->ymax,
+ s->xmaxpix, s->ymaxpix, s->ycurrent, s->currentfocus,
+ s->autofeeder ? "Yes" : "No", s->analoggamma ? "Yes" : "No",
+ s->currentscanpitch);
+ DBG (10,
+ "\tWhite balance exposure time var [RGB]=\t%d %d %d\n"
+ "\tPrescan result exposure time var [RGB]=\t%d %d %d\n"
+ "\tCurrent exposure time var.[RGB]=\t%d %d %d\n"
+ "\tInternal exposure time unit[RGB]=\t%d %d %d\n",
+ s->wbetr_r, s->webtr_g, s->webtr_b, s->pretv_r, s->pretv_g,
+ s->pretv_r, s->cetv_r, s->cetv_g, s->cetv_b, s->ietu_r,
+ s->ietu_g, s->ietu_b);
+ DBG (10,
+ "\toffsetdata_[rgb]=\t0x%x 0x%x 0x%x\n"
+ "\tlimitcondition=0x%x\n"
+ "\tdevice error code = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n"
+ "\tpower-on errors = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ s->offsetdata_r, s->offsetdata_g, s->offsetdata_b,
+ s->limitcondition,
+ s->derr[0], s->derr[1], s->derr[2], s->derr[3], s->derr[4],
+ s->derr[5], s->derr[6], s->derr[7],
+ s->power_on_errors[0], s->power_on_errors[1],
+ s->power_on_errors[2], s->power_on_errors[3],
+ s->power_on_errors[4], s->power_on_errors[5],
+ s->power_on_errors[6], s->power_on_errors[7]);
+
+ return ret;
+}
+
+static int
+get_internal_info (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "get_internal_info\n");
+
+ if(s->LS<2) /* distinquish between old and new scanners */
+ { ret=get_internal_info_LS20 (s);
+ }
+ else
+ { ret=get_inquiery_LS30 (s);
+ }
+ return ret;
+}
+
+static void
+coolscan_get_inquiry_values (Coolscan_t * s)
+{
+ unsigned char *inquiry_block;
+
+ DBG (10, "get_inquiry_values\n");
+
+ inquiry_block = (unsigned char *) s->buffer;
+ s->inquiry_len = 36;
+
+ get_inquiry_vendor ((char *)inquiry_block, (char *)s->vendor);
+ s->vendor[8] = '\0';
+ get_inquiry_product ((char *)inquiry_block, (char *)s->product);
+ s->product[16] = '\0';
+ get_inquiry_version ((char *)inquiry_block, (char *)s->version);
+ s->version[4] = '\0';
+
+ if (s->inquiry_len < 36)
+ {
+ DBG (1, "WARNING: inquiry return block is unexpected short (%d instead of 36).\n", s->inquiry_len);
+ }
+ s->inquiry_wdb_len = 117;
+ return;
+}
+
+static void
+coolscan_initialize_values (Coolscan_t * s)
+{
+ int i;
+ DBG (10, "initialize_values\n");
+ /* Initialize us structure */
+ if(s->LS<2) /* LS-20 or LS-10000 */
+ { select_MUD (s); /* must be before mode_sense - not for LS-30*/
+ coolscan_mode_sense (s); /* Obtain MUD (Measurement Unit Divisor) */
+ get_internal_info (s); /* MUST be called first. */
+ s->wdb_len = 117;
+ }
+ if(s->LS>=2) /* LS-30 */
+ {
+ get_inquiery_LS30(s); /* Info about scanner*/
+ select_MUD (s); /* must be before mode_sense */
+ get_feeder_type_LS30(s);
+ s->wdb_len = 117;
+ }
+
+ s->cont = 0; /* do not continue if scanner is unknown */
+ s->verbose = 2; /* 1=verbose,2=very verbose */
+
+
+ s->x_nres = s->y_nres = 2; /* 2 => 1350 dpi */
+ s->x_p_nres = s->y_p_nres = 9; /* 9 => 300 dpi */
+ s->tlx = 0;
+ s->tly = 0;
+ s->brx = s->xmaxpix; /* 2700 / 1200; */
+ s->bry = s->ymaxpix; /* 2700 / 1200; */
+
+
+ s->set_auto = 0; /* Always 0 on Nikon LS-{100|2}0 */
+ s->preview = 0; /* 1 for preview */
+ s->colormode = RGB; /* GREYSCALE or RGB */
+ s->colormode_p = RGB; /* GREYSCALE or RGB for preview*/
+ s->asf = 0; /* 1 if asf shall be used */
+ s->gammaselection = WD_Linear;
+
+ s->brightness = 128;
+ s->brightness_R = 128;
+ s->brightness_G = 128;
+ s->brightness_B = 128;
+ s->contrast = 128;
+ s->contrast_R = 128;
+ s->contrast_G = 128;
+ s->contrast_B = 128;
+
+ s->exposure_R = 50;
+ s->exposure_G = 50;
+ s->exposure_B = 50;
+
+ s->pretv_r=40000;
+ s->pretv_g=40000;
+ s->pretv_b=40000;
+
+ s->shift_R = 128;
+ s->shift_G = 128;
+ s->shift_B = 128;
+
+ s->ired_red=60;
+ s->ired_green=1;
+ s->ired_blue=1;
+
+ s->prescan = 1;
+ s->bits_per_color = 8;
+ s->rgb_control = 0;
+ s->gamma_bind = 1;
+ switch(s->LS)
+ { case 0:s->lutlength=2048;
+ s->max_lut_val=256;
+ break;
+ case 1:s->lutlength=512;
+ s->max_lut_val=512;
+ break;
+ case 2:s->lutlength=1024;
+ s->max_lut_val=1024;
+ break;
+ case 3:s->lutlength=4096;
+ s->max_lut_val=4096;
+ break;
+ }
+ for (i = 0; i < s->lutlength; i++)
+ {
+ s->gamma[i] =((short)((((double)i)/s->lutlength)*s->max_lut_val));
+ s->gamma_r[i] = s->gamma[i];
+ s->gamma_g[i] = s->gamma[i];
+ s->gamma_b[i] = s->gamma[i];
+ }
+
+ if (coolscan_test_little_endian() == SANE_TRUE)
+ {
+ s->low_byte_first = 1; /* in 2 byte mode send lowbyte first */
+ DBG(10,"backend runs on little endian machine\n");
+ }
+ else
+ {
+ s->low_byte_first = 0; /* in 2 byte mode send highbyte first */
+ DBG(10,"backend runs on big endian machine\n");
+ }
+}
+
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+
+static SANE_Status
+sense_handler (int scsi_fd, unsigned char * result, void *arg)
+{
+ scsi_fd = scsi_fd;
+ arg = arg;
+
+ if (result[0] != 0x70)
+ {
+ return SANE_STATUS_IO_ERROR; /* we only know about this one */
+ }
+ return request_sense_parse(result);
+
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* ilu per mm */
+
+#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 2700.0))
+#define mmToIlu(mm) ((mm) / length_quant)
+#define iluToMm(ilu) ((ilu) * length_quant)
+
+#define P_200_TO_255(per) SANE_UNFIX((per + 100) * 255/200 )
+#define P_100_TO_255(per) SANE_UNFIX(per * 255/100 )
+
+static const char negativeStr[] = "Negative";
+static const char positiveStr[] = "Positive";
+static SANE_String_Const type_list[] =
+{
+ positiveStr,
+ negativeStr,
+ 0
+};
+
+static const char colorStr[] = SANE_VALUE_SCAN_MODE_COLOR;
+static const char grayStr[] = SANE_VALUE_SCAN_MODE_GRAY;
+static const char rgbiStr[] = "RGBI";
+static const char iredStr[] = "Infrared";
+
+static SANE_String_Const scan_mode_list_LS20[] =
+{
+ colorStr,
+ grayStr,
+ NULL
+};
+
+static SANE_String_Const scan_mode_list_LS30[] =
+{
+ colorStr,
+ grayStr,
+#ifdef HAS_IRED
+ rgbiStr,
+#endif /* HAS_IRED */
+ NULL
+};
+
+static SANE_Int bit_depth_list[9];
+
+static const char neverStr[] = "never";
+static const char previewStr[] = "before preview";
+static const char scanStr[] = "before scan";
+static const char preandscanStr[] = "before preview and scan";
+static SANE_String_Const autofocus_mode_list[] =
+{
+ neverStr,
+ previewStr,
+ scanStr,
+ preandscanStr,
+ NULL
+};
+
+static SANE_String_Const source_list[4] =
+{NULL, NULL, NULL, NULL};
+
+static const SANE_Range gamma_range_8 =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 1 /* quantization */
+};
+
+
+static const SANE_Range gamma_range_9 =
+{
+ 0, /* minimum */
+ 511, /* maximum */
+ 1 /* quantization */
+};
+
+static const SANE_Range gamma_range_10 =
+{
+ 0, /* minimum */
+ 1023, /* maximum */
+ 1 /* quantization */
+};
+
+static const SANE_Range gamma_range_12 =
+{
+ 0, /* minimum */
+ 4096, /* maximum */
+ 1 /* quantization */
+};
+
+static const SANE_Range brightness_range =
+{
+ -5,
+ +5,
+ 1
+};
+
+static const SANE_Range contrast_range =
+{
+ -5,
+ +5,
+ 0
+};
+
+static const SANE_Range exposure_range =
+{
+ 24,
+ 400,
+ 2
+};
+
+static const SANE_Range shift_range =
+{
+ -15,
+ +15,
+ 0
+};
+
+static int num_devices;
+static Coolscan_t *first_dev;
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+do_eof (Coolscan_t * scanner)
+{
+ DBG (10, "do_eof\n");
+
+ if (scanner->pipe >= 0)
+ {
+ close (scanner->pipe);
+ scanner->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+
+static SANE_Status
+do_cancel (Coolscan_t * scanner)
+{
+ DBG (10, "do_cancel\n");
+ swap_res (scanner);
+ scanner->scanning = SANE_FALSE;
+
+ do_eof (scanner); /* close pipe and reposition scanner */
+
+ if (scanner->reader_pid != -1)
+ {
+ int exit_status;
+
+ DBG (10, "do_cancel: kill reader_process\n");
+
+ /* ensure child knows it's time to stop: */
+ sanei_thread_kill (scanner->reader_pid);
+ while (sanei_thread_waitpid(scanner->reader_pid, &exit_status) !=
+ scanner->reader_pid );
+ scanner->reader_pid = -1;
+ }
+
+ if (scanner->sfd >= 0)
+ {
+ coolscan_give_scanner (scanner);
+ DBG (10, "do_cancel: close filedescriptor\n");
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+static SANE_Status
+attach_scanner (const char *devicename, Coolscan_t ** devp)
+{
+ Coolscan_t *dev;
+ int sfd;
+
+ DBG (10, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (10, "attach_scanner: opening %s\n", devicename);
+ if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0)
+ {
+ DBG (1, "attach_scanner: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (NULL == (dev = malloc (sizeof (*dev))))
+ return SANE_STATUS_NO_MEM;
+
+
+ dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024)) ?
+ sanei_scsi_max_request_size : 64 * 1024;
+
+ if ((dev->buffer = malloc (dev->row_bufsize)) == NULL)
+/* if ((dev->buffer = malloc (sanei_scsi_max_request_size)) == NULL)*/
+ return SANE_STATUS_NO_MEM;
+
+ if ((dev->obuffer = malloc (dev->row_bufsize)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Nikon manual: Step 1 */
+ if (coolscan_identify_scanner (dev) != 0)
+ {
+ DBG (1, "attach_scanner: scanner-identification failed\n");
+ sanei_scsi_close (dev->sfd);
+ free (dev->buffer);
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Get MUD (via mode_sense), internal info (via get_internal_info), and
+ * initialize values */
+ coolscan_initialize_values (dev);
+
+ /* Why? */
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.model = dev->product;
+ dev->sane.type = "slide scanner";
+
+ dev->x_range.min = SANE_FIX (0);
+ dev->x_range.quant = SANE_FIX (length_quant);
+ dev->x_range.max = SANE_FIX ((double) ((dev->xmaxpix) * length_quant));
+
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.quant = SANE_FIX (length_quant);
+ dev->y_range.max = SANE_FIX ((double) ((dev->ymaxpix) * length_quant));
+
+ /* ...and this?? */
+ dev->dpi_range.min = SANE_FIX (108);
+ dev->dpi_range.quant = SANE_FIX (0);
+ dev->dpi_range.max = SANE_FIX (dev->maxres);
+ DBG (10, "attach: dev->dpi_range.max = %f\n",
+ SANE_UNFIX (dev->dpi_range.max));
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ DBG (10, "attach_scanner done\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *devName)
+{
+ return attach_scanner(devName, 0);
+}
+
+static RETSIGTYPE
+sigterm_handler (int signal)
+{
+ signal = signal;
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+ _exit (SANE_STATUS_GOOD);
+}
+
+
+typedef struct Color_correct_s
+{ int sum; /* number of pixels summed so far */
+ double sumr; /* sum of red pixel values*/
+ double sumi; /* sum of infrared pixel values*/
+ double sumri; /* sum of red*ired pixel values*/
+ double sumii; /* sum of ired*ired pixel values*/
+ double sumrr; /* sum of ired*ired pixel values*/
+ int mr; /* factor between red and ired values (*256) */
+ int br; /* offset of ired values */
+} ColorCorrect;
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix
+
+ taks: Correct the infrared channel
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ int *lutr - lookup table for red correction
+ int *lutg - lookup table for red correction
+ int *lutb - lookup table for red correction
+ int *lutr - lookup table for red correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 19.6.1999
+
+ ----------------------------------------------------------------*/
+
+static int Calc_fix_LUT(Coolscan_t * s)
+{ int uselutr,uselutg,uselutb,useluti;
+/* static int irmulr= -34*25; */
+ int irmulr= -64*25;
+ int irmulg= -1*25;
+ int irmulb= -0*25;
+ int irmuli= 256*25;
+ int div;
+ int i;
+
+ irmulr=s->ired_red*(25);
+ irmulg=s->ired_green*(25);
+ irmulb=s->ired_blue*(25);
+ irmuli=25*256;
+
+ if(s->LS==2) /* TODO: right conversion factors for 10 and 12 bit */
+ { div=4;
+ }
+ else if(s->LS==3)
+ { div=16;
+ }
+ else
+ { return 0;
+ }
+
+ memset(s->lutr, 0,256*4);
+ memset(s->lutg, 0,256*4);
+ memset(s->lutb, 0,256*4);
+ memset(s->luti, 0,256*4);
+
+ for(i=0;i<s->lutlength;i++)
+ { if(s->gamma_bind)
+ { uselutr=uselutg=uselutb=useluti=s->gamma[i]/div;
+ }
+ else
+ { uselutr=s->gamma_r[i]/div;
+ uselutg=s->gamma_g[i]/div;
+ uselutb=s->gamma_b[i]/div;
+ useluti=s->gamma_r[i]/div;
+ }
+ s->lutr[uselutr]=(int)(irmulr*pow((double)i,(double)0.333333));
+ s->lutg[uselutg]=(int)(irmulg*pow((double)i,(double)0.333333));
+ s->lutb[uselutb]=(int)(irmulb*pow((double)i,(double)0.333333));
+ s->luti[useluti]=(int)(irmuli*pow((double)i,(double)0.333333));
+ if(uselutr<255)
+ { if(s->lutr[uselutr+1]==0) s->lutr[uselutr+1]=s->lutr[uselutr];
+ }
+ if(uselutg<255)
+ { if(s->lutg[uselutg+1]==0) s->lutg[uselutg+1]=s->lutg[uselutg];
+ }
+ if(uselutb<255)
+ { if(s->lutb[uselutb+1]==0) s->lutb[uselutb+1]=s->lutb[uselutb];
+ }
+ if(useluti<255)
+ { if(s->luti[useluti+1]==0) s->luti[useluti+1]=s->luti[useluti];
+ }
+ }
+ /* DEBUG
+ for(i=0;i<255;i++)
+ { fprintf(stderr,"%d %d %d %d\n"
+ ,s->lutr[i],s->lutg[i],s->lutb[i],s->luti[i]);
+ }
+ */
+ return 1;
+}
+
+
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix
+
+ taks: Correct the infrared channel
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ int *lutr - lookup table for red correction
+ int *lutg - lookup table for red correction
+ int *lutb - lookup table for red correction
+ int *lutr - lookup table for red correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 19.6.1999
+
+ ----------------------------------------------------------------*/
+
+static int RGBIfix(Coolscan_t * scanner,
+ unsigned char* rgbimat,
+ unsigned char* orgbimat,
+ int size,
+ int *lutr,
+ int *lutg,
+ int *lutb,
+ int *luti)
+
+{
+ unsigned char *pr,*pg,*pb,*pi;
+ unsigned char *opr,*opg,*opb,*opi;
+
+ int r,g,b,i;
+ int ii;
+ int x;
+ for(x=0;x<size;x++)
+ {
+ pr=rgbimat+x*4;
+ pg=pr+1;
+ pb=pg+1;
+ pi=pb+1;
+ opr=orgbimat+x*4;
+ opg=opr+1;
+ opb=opg+1;
+ opi=opb+1;
+ r=lutr[(*pr)];
+ g=lutg[(*pg)];
+ b=lutb[(*pb)];
+ i=luti[(*pi)];
+ ii= i-r-g-b;
+ (*opr)=(*pr);
+ (*opg)=(*pg);
+ (*opb)=(*pb);
+ if(ii<0)ii=0;
+ if(ii>255*256)ii=255*256;
+ if(scanner->negative)
+ {
+ (*opi)=(unsigned char)(255-(ii>>8));
+ }
+ else
+ {
+ (*opi)=(unsigned char)(ii>>8);
+ }
+ }
+ return 1;
+}
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix16
+
+ taks: Correct the infrared channel for 16 bit images
+ (doesn't do anything for now)
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ int *lutr - lookup table for red correction
+ int *lutg - lookup table for red correction
+ int *lutb - lookup table for red correction
+ int *lutr - lookup table for red correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 19.6.1999
+
+ ----------------------------------------------------------------*/
+
+static int RGBIfix16(Coolscan_t * scanner,
+ unsigned short* rgbimat,
+ unsigned short* orgbimat,
+ int size,
+ int *lutr,
+ int *lutg,
+ int *lutb,
+ int *luti)
+
+{
+ unsigned short *pr,*pg,*pb,*pi;
+ unsigned short *opr,*opg,*opb,*opi;
+ int x;
+
+ scanner = scanner; lutr = lutr; lutg = lutg; lutb = lutb; luti = luti;
+
+ for(x=0;x<size;x++)
+ {
+ pr=rgbimat+x*4;
+ pg=pr+1;
+ pb=pg+1;
+ pi=pb+1;
+ opr=orgbimat+x*4;
+ opg=opr+1;
+ opb=opg+1;
+ opi=opb+1;
+ (*opr)=(((*pr)&0x00ff)<<8)+(((*pr)&0xff00)>>8);
+ (*opg)=(((*pg)&0x00ff)<<8)+(((*pg)&0xff00)>>8);
+ (*opb)=(((*pb)&0x00ff)<<8)+(((*pb)&0xff00)>>8);
+ (*opi)=(((*pi)&0x00ff)<<8)+(((*pi)&0xff00)>>8);
+ }
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------
+
+ function: rgb2g
+
+ taks: Convert RGB data to grey
+
+ import: unsigned char * rgbimat - RGB - matrix from scanner
+ int size - size of input data (num pixel)
+
+ export: unsigned char * gomat - Grey matrix
+
+ written by: Andreas RICK 13.7.1999
+
+ ----------------------------------------------------------------*/
+#define RtoG ((int)(0.27*256))
+#define GtoG ((int)(0.54*256))
+#define BtoG ((int)(0.19*256))
+
+static int rgb2g(unsigned char* rgbimat,unsigned char* gomat,
+ int size)
+
+{ unsigned char *pr,*pg,*pb;
+ unsigned char *opg;
+
+ int g;
+ int x;
+ for(x=0;x<size;x++)
+ {
+ pr=rgbimat+x*3;
+ pg=pr+1;
+ pb=pg+1;
+ opg=gomat+x;
+ g= RtoG*(*pr) + GtoG*(*pg) + BtoG*(*pb);
+ (*opg)=(unsigned char)(g>>8);
+ }
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix1
+
+ taks: Correct the infrared channel.
+ The input image data is the output of scaning
+ with LUT. To calculate the original values
+ the lutr and luti is applied.
+ The infrared values is corrected by:
+
+ Ir=mr*lutr(r)+luti(i)
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ ColorCorrect *cc,
+ int *lutr - lookup table for red correction
+ int *luti - lookup table for ired correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 3.7.1999
+
+ ----------------------------------------------------------------*/
+#if 0
+static int RGBIfix1(unsigned char* rgbimat,unsigned char* orgbimat,
+ int size,
+ int *lutr,
+ int *lutg,
+ int *lutb,
+ int *luti)
+
+{ unsigned char *pr,*pg,*pb,*pi;
+ unsigned char *opr,*opg,*opb,*opi;
+ ColorCorrect cc;
+ int r,i;
+ static int thresi=100;
+ int ii;
+ int x;
+
+ lutg = lutg; lutb = lutb;
+
+ /* calculate regression between r and ir */
+ cc.sum=0;
+ cc.sumr=cc.sumii=cc.sumrr=cc.sumi=cc.sumri=0.0;
+ for(x=0;x<size;x++)
+ { pr=rgbimat+x*4;
+ pi=pr+3;
+ r=lutr[(*pr)];
+ i=luti[(*pi)];
+ /* r=(*pr);
+ i=(*pi); */
+ if((*pi)>thresi)
+ { cc.sum++;
+ cc.sumr+=r;
+ cc.sumii+=(i*i);
+ cc.sumrr+=(r*r);
+ cc.sumi+=i;
+ cc.sumri+=(i*r);
+ }
+ }
+ if((cc.sumii!=0)&&(cc.sum!=0))
+ { double dn,dz,dm;
+ dz=(cc.sumri-cc.sumr*cc.sumi/cc.sum);
+ dn=(cc.sumrr-cc.sumr*cc.sumr/cc.sum);
+ DBG (2, "Reg:dz:%e dn:%e\n",dz,dn);
+ if(dn!=0)
+ { dm=(dz/dn);
+ cc.mr=(int)(dm*1024);
+ }
+ else
+ { cc.mr=0;
+ dm=0;
+ }
+ cc.br=(int)((cc.sumi-dm*cc.sumr)/cc.sum);
+ }
+ else
+ { cc.mr=0;
+ }
+ DBG (2, "Regression: size:%d I=%d/1024*R b:%d s:%d sr:%e si:%e sii:%e sri:%e srr:%e\n",
+ size,cc.mr,cc.br,cc.sum,cc.sumr,cc.sumi,cc.sumii,cc.sumri,cc.sumrr);
+ for(x=0;x<size;x++)
+ {
+
+ pr=rgbimat+x*4;
+ pg=pr+1;
+ pb=pg+1;
+ pi=pb+1;
+ opr=orgbimat+x*4;
+ opg=opr+1;
+ opb=opg+1;
+ opi=opb+1;
+ r=lutr[(*pr)];
+ i=luti[(*pi)];
+ /* r=(*pr);
+ i=(*pi); */
+ ii= ((i-((r*cc.mr)>>10)-cc.br)>>2) +128;
+ (*opr)=(*pr);
+ (*opg)=(*pg);
+ (*opb)=(*pb);
+ if(ii<0) ii=0;
+ if(ii>255) ii=255;
+ (*opi)=(unsigned char)(ii);
+ }
+ return 1;
+}
+
+#endif
+/* This function is executed as a child process. */
+static int
+reader_process (void *data )
+{
+ int status;
+ unsigned int i;
+ unsigned char h;
+ unsigned int data_left;
+ unsigned int data_to_read;
+ unsigned int data_to_write;
+ FILE *fp;
+ sigset_t sigterm_set, ignore_set;
+ struct SIGACTION act;
+ unsigned int bpl, linesPerBuf, lineOffset;
+ unsigned char r_data, g_data, b_data;
+ unsigned int j, line;
+ Coolscan_t * scanner = (Coolscan_t*)data;
+
+ if (sanei_thread_is_forked ())
+ {
+ DBG (10, "reader_process started (forked)\n");
+ close (scanner->pipe);
+ scanner->pipe = -1;
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGTERM );
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset ( &ignore_set, SIGUSR2 );
+#endif
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+ }
+ else
+ {
+ DBG (10, "reader_process started (as thread)\n");
+ }
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ fp = fdopen ( scanner->reader_fds, "w");
+ if (!fp)
+ {
+ DBG (1, "reader_process: couldn't open pipe!\n");
+ return 1;
+ }
+
+ DBG (10, "reader_process: starting to READ data\n");
+
+ data_left = scan_bytes_per_line (scanner) *
+ lines_per_scan (scanner);
+
+ /*scanner->row_bufsize = sanei_scsi_max_request_size;*/
+ coolscan_trim_rowbufsize (scanner); /* trim bufsize */
+
+ DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n",
+ data_left, scanner->row_bufsize);
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = sigterm_handler;
+ sigaction (SIGTERM, &act, 0);
+ /* wait_scanner(scanner); */
+ do
+ {
+ data_to_read = (data_left < scanner->row_bufsize) ?
+ data_left : scanner->row_bufsize;
+
+ data_to_write=data_to_read;
+
+ status = coolscan_read_data_block (scanner
+ ,R_datatype_imagedata,data_to_read);
+ if (status == 0)
+ {
+ continue;
+ }
+ if (status == -1)
+ {
+ DBG (1, "reader_process: unable to get image data from scanner!\n");
+ fclose (fp);
+ return (-1);
+ }
+
+ if (scanner->LS == 1) { /* mirror image for LS-1000 */
+ bpl = scan_bytes_per_line(scanner);
+ linesPerBuf = data_to_read / bpl;
+
+ for (line = 0, lineOffset = 0; line < linesPerBuf;
+ line++, lineOffset += bpl ) {
+
+ if (scanner->colormode == RGB) {
+ for (j = 0; j < bpl/2 ; j += 3) {
+ r_data=scanner->buffer[lineOffset + j];
+ g_data=scanner->buffer[lineOffset + j + 1];
+ b_data=scanner->buffer[lineOffset + j + 2];
+
+ scanner->buffer[lineOffset + j] =
+ scanner->buffer[lineOffset + bpl -1 - j - 2 ];
+ scanner->buffer[lineOffset + j + 1] =
+ scanner->buffer[lineOffset + bpl -1 - j - 1 ];
+ scanner->buffer[lineOffset + j + 2] =
+ scanner->buffer[lineOffset + bpl -1 - j ];
+
+ scanner->buffer[lineOffset + bpl -1 - j - 2 ] = r_data;
+ scanner->buffer[lineOffset + bpl -1 - j - 1] = g_data;
+ scanner->buffer[lineOffset + bpl -1 - j] = b_data;
+ }
+ }
+ else {
+ for (j = 0; j < bpl/2; j++) {
+ r_data=scanner->buffer[lineOffset + j];
+ scanner->buffer[lineOffset + j] =
+ scanner->buffer[lineOffset + bpl - 1 - j];
+ scanner->buffer[lineOffset + bpl - 1 - j] = r_data;
+ }
+ }
+ }
+ }
+ if(scanner->colormode==RGBI)
+ { /* Correct Infrared Channel */
+ if(scanner->bits_per_color>8)
+ {
+ RGBIfix16(scanner, (unsigned short * ) scanner->buffer,
+ (unsigned short * )scanner->obuffer,
+ data_to_read/8,scanner->lutr,
+ scanner->lutg,scanner->lutb,scanner->luti);
+ }
+ else
+ {
+ RGBIfix(scanner,scanner->buffer,scanner->obuffer,
+ data_to_read/4,scanner->lutr,
+ scanner->lutg,scanner->lutb,scanner->luti);
+ }
+ }
+ else if((scanner->colormode==GREYSCALE)&&(scanner->LS>=2))
+ { /* Convert to Grey */
+ data_to_write/=3;
+ rgb2g(scanner->buffer,scanner->obuffer,data_to_write);
+ }
+ else
+ { /* or just copy */
+ memcpy (scanner->obuffer, scanner->buffer,data_to_read);
+ }
+ if((!scanner->low_byte_first)&&(scanner->bits_per_color>8))
+ { for(i=0;i<data_to_write;i++) /* inverse byteorder */
+ { h=scanner->obuffer[i];
+ scanner->obuffer[i]=scanner->obuffer[i+1];
+ i++;
+ scanner->obuffer[i]=h;
+ }
+ }
+ fwrite (scanner->obuffer, 1, data_to_write, fp);
+ fflush (fp);
+ data_left -= data_to_read;
+ DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n",
+ data_to_read, data_left);
+ }
+ while (data_left);
+
+ fclose (fp);
+
+ DBG (10, "reader_process: finished reading data\n");
+
+ return 0;
+}
+
+static SANE_Status
+init_options (Coolscan_t * scanner)
+{
+ int i;
+ int bit_depths;
+
+ DBG (10, "init_options\n");
+
+ memset (scanner->opt, 0, sizeof (scanner->opt));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ /* "Mode" group: */
+ scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ scanner->opt[OPT_MODE_GROUP].desc = "";
+ scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_MODE_GROUP].cap = 0;
+ scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ if(scanner->LS<2)
+ { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS20);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS20;
+ }
+ else
+ { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS30);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS30;
+ }
+
+ /* source */
+ source_list[0] = "Slide";
+ source_list[1] = "Automatic Slide Feeder";
+ source_list[2] = NULL;
+ if (!scanner->autofeeder)
+ {
+ scanner->opt[OPT_SOURCE].cap = SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_SOURCE].size = max_string_size (source_list);
+ scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
+
+ /* negative */
+ scanner->opt[OPT_TYPE].name = "type";
+ scanner->opt[OPT_TYPE].title = "Film type";
+ scanner->opt[OPT_TYPE].desc =
+ "Select the film type (positive (slide) or negative)";
+ scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_TYPE].size = max_string_size (type_list);
+ scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_TYPE].constraint.string_list = type_list;
+
+ scanner->opt[OPT_PRESCAN].name = "prescan";
+ scanner->opt[OPT_PRESCAN].title = "Prescan";
+ scanner->opt[OPT_PRESCAN].desc =
+ "Perform a prescan during preview";
+ scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE;
+
+ scanner->opt[OPT_PRESCAN_NOW].name = "prescan now";
+ scanner->opt[OPT_PRESCAN_NOW].title = "Prescan now";
+ scanner->opt[OPT_PRESCAN_NOW].desc =
+ "Perform a prescan now";
+ scanner->opt[OPT_PRESCAN_NOW].type = SANE_TYPE_BUTTON;
+ scanner->opt[OPT_PRESCAN_NOW].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_PRESCAN_NOW].size = 0;
+ scanner->opt[OPT_PRESCAN_NOW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ scanner->opt[OPT_PRESCAN_NOW].constraint_type = SANE_CONSTRAINT_NONE;
+ scanner->opt[OPT_PRESCAN_NOW].constraint.string_list = 0;
+
+ /* bit depth */
+
+ bit_depths=0;
+ bit_depth_list[++bit_depths] = 8;
+ if (scanner->LS==2)
+ {
+ bit_depth_list[++bit_depths] = 10;
+ }
+ if (scanner->LS==3)
+ {
+ bit_depth_list[++bit_depths] = 12;
+ }
+
+ bit_depth_list[0] = bit_depths;
+
+ scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
+ scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
+
+ /* resolution */
+ scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_RESOLUTION].constraint.word_list = resolution_list;
+
+ scanner->opt[OPT_PREVIEW_RESOLUTION].name = "preview-resolution";
+ scanner->opt[OPT_PREVIEW_RESOLUTION].title = "Preview resolution";
+ scanner->opt[OPT_PREVIEW_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].type = SANE_TYPE_INT;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].constraint.word_list = resolution_list;
+
+ /* "Geometry" group: */
+ scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
+ scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_X].constraint.range = &(scanner->x_range);
+
+ /* top-left y */
+ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_Y].constraint.range = &(scanner->y_range);
+
+ /* bottom-right x */
+ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_X].constraint.range = &(scanner->x_range);
+
+ /* bottom-right y */
+ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_Y].constraint.range = &(scanner->y_range);
+
+
+ /* ------------------------------ */
+
+ /* "Enhancement" group: */
+ scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ scanner->opt[OPT_GAMMA_BIND].name = "gamma-bind";
+ scanner->opt[OPT_GAMMA_BIND].title = "Gamma bind";
+ scanner->opt[OPT_GAMMA_BIND].desc =
+ "Use same gamma correction for all colours";
+ scanner->opt[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_GAMMA_BIND].unit = SANE_UNIT_NONE;
+
+
+ scanner->opt[OPT_ANALOG_GAMMA].name = "analog_gamma";
+ scanner->opt[OPT_ANALOG_GAMMA].title = "Analog Gamma";
+ scanner->opt[OPT_ANALOG_GAMMA].desc = "Analog Gamma";
+ scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
+ if (!scanner->analoggamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap = SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_AVERAGING].name = "averaging";
+ scanner->opt[OPT_AVERAGING].title = "Averaging";
+ scanner->opt[OPT_AVERAGING].desc = "Averaging";
+ scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE;
+
+
+ scanner->opt[OPT_RGB_CONTROL].name = "rgb-control";
+ scanner->opt[OPT_RGB_CONTROL].title = "RGB control";
+ scanner->opt[OPT_RGB_CONTROL].desc =
+ "toggles brightness/contrast control over individual colours";
+ scanner->opt[OPT_RGB_CONTROL].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_RGB_CONTROL].unit = SANE_UNIT_NONE;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_RGB_CONTROL].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+
+ /* brightness */
+ scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_R_BRIGHTNESS].name = "red-brightness";
+ scanner->opt[OPT_R_BRIGHTNESS].title = "Red brightness";
+ scanner->opt[OPT_R_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_R_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_R_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_G_BRIGHTNESS].name = "green-brightness";
+ scanner->opt[OPT_G_BRIGHTNESS].title = "Green brightness";
+ scanner->opt[OPT_G_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_G_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_G_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_B_BRIGHTNESS].name = "blue-brightness";
+ scanner->opt[OPT_B_BRIGHTNESS].title = "Blue brightness";
+ scanner->opt[OPT_B_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_B_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_B_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ /* contrast */
+ scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CONTRAST].constraint.range = &contrast_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_R_CONTRAST].name = "red-contrast";
+ scanner->opt[OPT_R_CONTRAST].title = "Red contrast";
+ scanner->opt[OPT_R_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_R_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_R_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_CONTRAST].constraint.range = &contrast_range;
+ scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_G_CONTRAST].name = "green-contrast";
+ scanner->opt[OPT_G_CONTRAST].title = "Green contrast";
+ scanner->opt[OPT_G_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_G_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_G_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_CONTRAST].constraint.range = &contrast_range;
+ scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_B_CONTRAST].name = "blue-contrast";
+ scanner->opt[OPT_B_CONTRAST].title = "Blue contrast";
+ scanner->opt[OPT_B_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_B_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_B_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_CONTRAST].constraint.range = &contrast_range;
+ scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_EXPOSURE].name = "exposure";
+ scanner->opt[OPT_EXPOSURE].title = "Exposure";
+ scanner->opt[OPT_EXPOSURE].desc = "";
+ scanner->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_EXPOSURE].constraint.range = &exposure_range;
+
+ scanner->opt[OPT_R_EXPOSURE].name = "red-exposure";
+ scanner->opt[OPT_R_EXPOSURE].title = "Red exposure";
+ scanner->opt[OPT_R_EXPOSURE].desc = "";
+ scanner->opt[OPT_R_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_R_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_R_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_EXPOSURE].constraint.range = &exposure_range;
+
+ scanner->opt[OPT_G_EXPOSURE].name = "green-exposure";
+ scanner->opt[OPT_G_EXPOSURE].title = "Green exposure";
+ scanner->opt[OPT_G_EXPOSURE].desc = "";
+ scanner->opt[OPT_G_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_G_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_EXPOSURE].constraint.range = &exposure_range;
+ scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_B_EXPOSURE].name = "blue-exposure";
+ scanner->opt[OPT_B_EXPOSURE].title = "Blue exposre";
+ scanner->opt[OPT_B_EXPOSURE].desc = "";
+ scanner->opt[OPT_B_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_B_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_EXPOSURE].constraint.range = &exposure_range;
+ scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_R_SHIFT].name = "red-shift";
+ scanner->opt[OPT_R_SHIFT].title = "Red shift";
+ scanner->opt[OPT_R_SHIFT].desc = "";
+ scanner->opt[OPT_R_SHIFT].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_SHIFT].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_R_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_SHIFT].constraint.range = &shift_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_R_SHIFT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_G_SHIFT].name = "green-shift";
+ scanner->opt[OPT_G_SHIFT].title = "Green shift";
+ scanner->opt[OPT_G_SHIFT].desc = "";
+ scanner->opt[OPT_G_SHIFT].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_SHIFT].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_G_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_SHIFT].constraint.range = &shift_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_G_SHIFT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_B_SHIFT].name = "blue-shift";
+ scanner->opt[OPT_B_SHIFT].title = "Blue shift";
+ scanner->opt[OPT_B_SHIFT].desc = "";
+ scanner->opt[OPT_B_SHIFT].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_SHIFT].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_B_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_SHIFT].constraint.range = &shift_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_B_SHIFT].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* R+G+B gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ if (scanner->LS == 1)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap = SANE_CAP_INACTIVE;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ /* red gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ /* green gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ /* blue gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+
+
+ /* ------------------------------ */
+
+ /* "Advanced" group: */
+ scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
+ scanner->opt[OPT_ADVANCED_GROUP].desc = "";
+ scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* preview */
+ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+
+ /* Autofocus */
+ scanner->opt[OPT_AUTOFOCUS].name = "Autofocus";
+ scanner->opt[OPT_AUTOFOCUS].title ="Autofocus";
+ scanner->opt[OPT_AUTOFOCUS].desc = "When to do autofocussing";
+ scanner->opt[OPT_AUTOFOCUS].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_AUTOFOCUS].size = max_string_size (autofocus_mode_list);
+ scanner->opt[OPT_AUTOFOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_AUTOFOCUS].constraint.string_list = autofocus_mode_list;
+
+ scanner->opt[OPT_IRED_RED].name = "IRED cor. red";
+ scanner->opt[OPT_IRED_RED].title ="IRED cor. red";
+ scanner->opt[OPT_IRED_RED].desc = "Correction of infrared from red";
+ scanner->opt[OPT_IRED_RED].type = SANE_TYPE_INT;
+ scanner->opt[OPT_IRED_RED].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_IRED_RED].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_IRED_RED].constraint.range = &gamma_range_8;
+ scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_ADVANCED;
+ if(scanner->LS<2)
+ { scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+
+ /* scanner->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; */
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize;
+
+ DBG_INIT ();
+ sanei_thread_init ();
+
+ DBG (10, "sane_init\n");
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (COOLSCAN_CONFIG_FILE);
+ if (!fp)
+ {
+ attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#')
+ continue; /* ignore line comments */
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ /*attach_scanner (dev_name, 0);*/
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Coolscan_t *dev, *next;
+
+ DBG (10, "sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->devicename);
+ free (dev->buffer);
+ free (dev->obuffer);
+ free (dev);
+ }
+}
+
+/* ----------------------------- SANE GET DEVICES -------------------------- */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only)
+{
+
+ static const SANE_Device **devlist = 0;
+ Coolscan_t *dev;
+ int i;
+
+ local_only = local_only;
+
+ DBG (10, "sane_get_devices\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = calloc (num_devices + 1, sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Coolscan_t *dev;
+ SANE_Status status;
+
+ DBG (10, "sane_open\n");
+
+ if (devicename[0])
+ { /* search for devicename */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ dev = first_dev; /* empty devicname -> use first device */
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ dev->sfd = -1;
+ dev->pipe = -1;
+ dev->scanning = SANE_FALSE;
+
+ init_options (dev);
+ *handle = dev;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close\n");
+ if (((Coolscan_t *) handle)->scanning)
+ do_cancel (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_get_option_descriptor %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return &scanner->opt[option];
+}
+
+/*
+ static void
+ worddump(char *comment, SANE_Word * p, int l)
+ {
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (5, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 8) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (5, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %4.4d", *p);
+ ptr += 5;
+ }
+ *ptr = '\0';
+ DBG (5, "%s\n", line);
+ }
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info)
+{
+ Coolscan_t *scanner = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ if (info)
+ *info = 0;
+
+ if (scanner->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = scanner->opt[option].cap;
+
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (10, "sane_control_option %d, get value\n", option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_TL_X:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tlx));
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tly));
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->brx));
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->bry));
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW:
+ *(SANE_Word *) val = scanner->preview;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOFOCUS:
+ switch(scanner->autofocus)
+ { case AF_NEVER: strcpy (val,neverStr);
+ break;
+ case AF_PREVIEW:strcpy (val,previewStr);
+ break;
+ case AF_SCAN:if(scanner->LS>=2) strcpy (val,scanStr);
+ break;
+ case AF_PREANDSCAN:if(scanner->LS>=2) strcpy (val,preandscanStr);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ *(SANE_Word *) val = resDivToVal (scanner->x_nres);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW_RESOLUTION:
+ *(SANE_Word *) val = resDivToVal (scanner->x_p_nres);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BIT_DEPTH:
+ *(SANE_Word *) val = scanner->bits_per_color;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast_R - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast_G - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast_B - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness_R - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness_G - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness_B - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_R * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_R * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_G * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_B * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_SHIFT:
+ *(SANE_Word *) val = scanner->shift_R - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_SHIFT:
+ *(SANE_Word *) val = scanner->shift_G - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_SHIFT:
+ *(SANE_Word *) val = scanner->shift_B - 128;
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_IRED_RED:
+ *(SANE_Word *) val = scanner->ired_red;
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_TYPE:
+ strcpy (val, ((scanner->negative) ? negativeStr : positiveStr));
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ switch(scanner->colormode)
+ { case RGB: strcpy (val,colorStr);
+ break;
+ case GREYSCALE:strcpy (val,grayStr);
+ break;
+ case RGBI:if(scanner->LS>=2) strcpy (val,rgbiStr);
+ else strcpy (val,colorStr);
+ break;
+ case IRED:if(scanner->LS>=2) strcpy (val,iredStr);
+ else strcpy (val,grayStr);
+ break;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN:
+ *(SANE_Word *) val = (scanner->prescan) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN_NOW:
+ return SANE_STATUS_GOOD;
+
+ case OPT_RGB_CONTROL:
+ *(SANE_Word *) val = (scanner->rgb_control) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_BIND:
+ *(SANE_Word *) val = (scanner->gamma_bind) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ANALOG_GAMMA:
+ *(SANE_Word *) val =
+ (scanner->analog_gamma_r) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AVERAGING:
+ *(SANE_Word *) val = (scanner->averaging) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR:
+ memcpy (val, scanner->gamma, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR_R:
+ memcpy (val, scanner->gamma_r, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR_G:
+ memcpy (val, scanner->gamma_g, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, scanner->gamma_b, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if (strcmp (val, "Automatic Slide Feeder") == 0)
+ {
+ /* Feed/Discharge/update filename/etc */
+ }
+ else
+ {
+ /* Reset above */
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ }
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (10, "sane_control_option %d, set value\n", option);
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (scanner->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ case OPT_GAMMA_BIND:
+ scanner->gamma_bind = (*(SANE_Word *) val == SANE_TRUE);
+ if (scanner->LS != 1)
+ {
+ if (scanner->gamma_bind)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ }
+ else
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+
+ }
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ANALOG_GAMMA:
+ scanner->analog_gamma_r = scanner->analog_gamma_g =
+ scanner->analog_gamma_b = (*(SANE_Word *) val == SANE_TRUE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_AVERAGING:
+ scanner->averaging = (*(SANE_Word *) val == SANE_TRUE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN:
+ scanner->prescan = (*(SANE_Word *) val == SANE_TRUE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN_NOW:
+ do_prescan_now(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BIT_DEPTH:
+ scanner->bits_per_color=(*(SANE_Word *)val);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_RGB_CONTROL:
+ scanner->rgb_control = (*(SANE_Word *) val == SANE_TRUE);
+ if (scanner->rgb_control)
+ {
+ scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->contrast_R = 128;
+ scanner->contrast_G = 128;
+ scanner->contrast_B = 128;
+ scanner->brightness_R = 128;
+ scanner->brightness_G = 128;
+ scanner->brightness_B = 128;
+ scanner->exposure_R = 50;
+ scanner->exposure_G = 50;
+ scanner->exposure_B = 50;
+ }
+ else
+ {
+ scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->contrast = 128;
+ scanner->brightness = 128;
+ scanner->exposure_R = 50;
+ scanner->exposure_G = 50;
+ scanner->exposure_B = 50;
+
+ scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ scanner->y_nres = scanner->x_nres =
+ resValToDiv (*(SANE_Word *) val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW_RESOLUTION:
+ scanner->y_p_nres = scanner->x_p_nres =
+ resValToDiv (*(SANE_Word *) val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ scanner->tlx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ scanner->tly = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ scanner->brx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ scanner->bry = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_NUM_OPTS:
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW:
+ scanner->preview = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOFOCUS:
+ if(strcmp(val,neverStr)==0)
+ { scanner->autofocus=AF_NEVER;
+ }
+ if(strcmp(val,previewStr)==0)
+ { scanner->autofocus=AF_PREVIEW;
+ }
+ if(strcmp(val,scanStr)==0)
+ { scanner->autofocus=AF_SCAN;
+ }
+ if(strcmp(val,preandscanStr)==0)
+ { scanner->autofocus=AF_PREANDSCAN;;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ scanner->contrast = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_R_CONTRAST:
+ scanner->contrast_R = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_G_CONTRAST:
+ scanner->contrast_G = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_B_CONTRAST:
+ scanner->contrast_B = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ scanner->brightness = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_R_BRIGHTNESS:
+ scanner->brightness_R = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_G_BRIGHTNESS:
+ scanner->brightness_G = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_B_BRIGHTNESS:
+ scanner->brightness_B = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_EXPOSURE:
+ scanner->exposure_R = *(SANE_Word *) val / 2;
+ scanner->exposure_G = *(SANE_Word *) val / 2;
+ scanner->exposure_B = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+ case OPT_R_EXPOSURE:
+ scanner->exposure_R = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+ case OPT_G_EXPOSURE:
+ scanner->exposure_G = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+ case OPT_B_EXPOSURE:
+ scanner->exposure_B = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_SHIFT:
+ scanner->shift_R = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_G_SHIFT:
+ scanner->shift_G = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_B_SHIFT:
+ scanner->shift_B = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_IRED_RED:
+ scanner->ired_red= *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ scanner->asf = (strcmp (val, "Automatic...") == 0);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TYPE:
+ scanner->negative = (strcmp (val, negativeStr) == 0);
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_MODE:
+ if(strcmp(val,colorStr)==0)
+ { scanner->colormode=RGB;
+ scanner->colormode_p=RGB;
+ }
+ if(strcmp(val,grayStr)==0)
+ { scanner->colormode=GREYSCALE;
+ scanner->colormode_p=GREYSCALE;
+ }
+ if(strcmp(val,rgbiStr)==0)
+ { scanner->colormode=RGBI;
+ scanner->colormode_p=RGB;
+ }
+ if(strcmp(val,iredStr)==0)
+ { scanner->colormode=IRED;
+ scanner->colormode_p=GREYSCALE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR:
+ memcpy (scanner->gamma, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ memcpy (scanner->gamma_r, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_G:
+ memcpy (scanner->gamma_g, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (scanner->gamma_b, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_get_parameters");
+ switch(scanner->colormode)
+ { case RGB:
+ params->format = SANE_FRAME_RGB;
+ break;
+#ifdef HAS_IRED
+ case RGBI:
+ params->format = SANE_FRAME_RGBA;
+ break;
+#endif /* HAS_RGBI */
+ case GREYSCALE:
+ params->format = SANE_FRAME_GRAY;
+ break;
+ }
+
+ params->depth = scanner->bits_per_color>8?16:8;
+ params->pixels_per_line = pixels_per_line (scanner);
+ params->lines = lines_per_scan (scanner);
+ params->bytes_per_line = write_bytes_per_line (scanner);
+ params->last_frame = 1;
+ return SANE_STATUS_GOOD;
+}
+static int
+swap_res (Coolscan_t * s)
+{
+ if (s->preview)
+ {
+ /* swap preview/scan resolutions */
+ int xres, yres, cmode;
+ xres = s->x_nres;
+ yres = s->y_nres;
+ s->x_nres = s->x_p_nres;
+ s->y_nres = s->y_p_nres;
+
+ s->x_p_nres = xres;
+ s->y_p_nres = yres;
+ cmode=s->colormode;
+ s->colormode=s->colormode_p;
+ s->colormode_p=cmode;
+ }
+ return 0;
+}
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Coolscan_t *scanner = handle;
+ int fds[2];
+
+ DBG (10, "sane_start\n");
+ if (scanner->scanning == SANE_TRUE)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (scanner->sfd < 0)
+ { /* first call */
+ if (sanei_scsi_open (scanner->sane.name,
+ &(scanner->sfd),
+ sense_handler, 0) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: open of %s failed:\n",
+ scanner->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ scanner->scanning = SANE_TRUE;
+
+
+ if (coolscan_check_values (scanner) != 0)
+ { /* Verify values */
+ DBG (1, "ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (coolscan_grab_scanner (scanner))
+ {
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ DBG (5, "WARNING: unable to reserve scanner: device busy\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* hoho, step 2c, -perm */
+ coolscan_object_feed (scanner);
+
+ swap_res (scanner);
+ if (!scanner->preview)
+ { if(scanner->autofocus & 0x02)
+ { coolscan_autofocus (scanner);
+ }
+ }
+ else
+ {
+ if(scanner->autofocus & 0x01)
+ { coolscan_autofocus (scanner);
+ }
+ if (scanner->prescan) {
+ prescan (scanner);
+ if(scanner->LS<2)
+ { get_internal_info(scanner);
+ }
+ coolscan_get_window_param (scanner,1);
+ }
+ }
+ /*read_LUT(scanner); */
+ if(scanner->LS<2)
+ { send_LUT (scanner);
+ coolscan_set_window_param (scanner, 0);
+ coolscan_get_window_param (scanner,0);
+ coolscan_start_scan (scanner);
+ }
+ else
+ { coolscan_set_window_param (scanner, 0);
+ send_LUT (scanner);
+ Calc_fix_LUT(scanner);
+ coolscan_start_scan (scanner);
+ wait_scanner (scanner);
+ coolscan_get_window_param (scanner,0);
+ }
+
+ DBG (10, "bytes per line = %d\n", scan_bytes_per_line (scanner));
+ DBG (10, "pixels_per_line = %d\n", pixels_per_line (scanner));
+ DBG (10, "lines = %d\n", lines_per_scan (scanner));
+ DBG (10, "negative = %d\n", scanner->negative);
+ DBG (10, "brightness (halftone) = %d\n", scanner->brightness);
+ DBG (10, "contrast (halftone) = %d\n", scanner->contrast);
+ DBG (10, "fast preview function = %d\n", scanner->preview);
+
+ /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
+ if (pipe (fds) < 0)
+ {
+ DBG (1, "ERROR: could not create pipe\n");
+
+ swap_res (scanner);
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe = fds[0];
+ scanner->reader_fds = fds[1];
+ scanner->reader_pid = sanei_thread_begin( reader_process, (void*)scanner );
+ if (scanner->reader_pid == -1)
+ {
+ DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ close (scanner->reader_fds);
+ scanner->reader_fds = -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ Coolscan_t *scanner = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read (scanner->pipe, buf, max_len);
+ DBG (10, "sane_read: read %ld bytes\n", (long) nread);
+
+ if (!(scanner->scanning))
+ {
+ return do_cancel (scanner);
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel (scanner);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0)
+ return do_eof (scanner); /* close pipe */
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Coolscan_t *s = handle;
+
+ if (s->reader_pid != -1)
+ {
+ sanei_thread_kill ( s->reader_pid );
+ sanei_thread_waitpid( s->reader_pid, NULL );
+ s->reader_pid = -1;
+ }
+ swap_res (s);
+ s->scanning = SANE_FALSE;
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_set_io_mode: non_blocking=%d\n", non_blocking);
+
+ if (!scanner->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (fcntl (scanner->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_get_select_fd\n");
+
+ if (!scanner->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ *fd = scanner->pipe;
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/coolscan.conf.in b/backend/coolscan.conf.in
new file mode 100644
index 0000000..512a6d2
--- /dev/null
+++ b/backend/coolscan.conf.in
@@ -0,0 +1,2 @@
+scsi Nikon * Scanner
+/dev/scanner
diff --git a/backend/coolscan.h b/backend/coolscan.h
new file mode 100644
index 0000000..d3e4154
--- /dev/null
+++ b/backend/coolscan.h
@@ -0,0 +1,324 @@
+/* --------------------------------------------------------------------- */
+
+/* coolscan.h - headerfile for SANE-backend for coolscan scanners
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* --------------------------------------------------------------------- */
+
+#ifndef coolscan_h
+#define coolscan_h
+
+#include "sys/types.h"
+
+enum Coolscan_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_SOURCE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW_RESOLUTION,
+ OPT_TYPE,
+ OPT_BIT_DEPTH,
+ OPT_PRESCAN,
+ OPT_PRESCAN_NOW,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_GAMMA_BIND,
+ OPT_ANALOG_GAMMA,
+ OPT_AVERAGING,
+ OPT_RGB_CONTROL,
+
+ OPT_BRIGHTNESS,
+ OPT_R_BRIGHTNESS,
+ OPT_G_BRIGHTNESS,
+ OPT_B_BRIGHTNESS,
+
+ OPT_CONTRAST,
+ OPT_R_CONTRAST,
+ OPT_G_CONTRAST,
+ OPT_B_CONTRAST,
+
+ OPT_EXPOSURE,
+ OPT_R_EXPOSURE,
+ OPT_G_EXPOSURE,
+ OPT_B_EXPOSURE,
+
+ OPT_R_SHIFT,
+ OPT_G_SHIFT,
+ OPT_B_SHIFT,
+
+ OPT_ADVANCED_GROUP,
+ OPT_PREVIEW, /* preview */
+ OPT_AUTOFOCUS, /* autofocus */
+ OPT_IRED_RED,
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+
+typedef struct Image_Pos
+{ int start; /* start position of image on film strip */
+ int end; /* end position of image on film strip */
+ int offset /* always 0 */;
+ int height; /* image height always 2591 */
+} Image_Pos_t;
+
+
+
+
+typedef struct Coolscan
+ {
+ struct Coolscan *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+ SANE_Pid reader_pid;
+ int reader_fds;
+ int pipe;
+ int scanning;
+/*--------------------------*/
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+
+/*--------------------------*/
+ /* buffer used for scsi-transfer and writing*/
+ unsigned char *buffer;
+ unsigned char *obuffer;
+ unsigned int row_bufsize;
+
+ char *devicename; /* name of the scanner device */
+ int sfd; /* output file descriptor, scanner device */
+
+ char vendor[9]; /* will be Nikon */
+ char product[17]; /* e.g. "LS-1000 " or so */
+ char version[5]; /* e.g. V1.6 */
+
+ int LS; /* index in scanner_str */
+ int cont; /* continue although scanner is unknown */
+ int verbose; /* 1,2=output informations */
+ int asf; /* Automatic Slide Feeder enabled? */
+
+ int MUD; /* Measurement Unit Divisor (1200 or 2700) */
+
+ int inquiry_len; /* length of inquiry return block [36] */
+ int inquiry_wdb_len; /* length of window descriptor block [117] */
+
+ int wdb_len; /* use this length of WDB */
+
+ double width; /* use this width of scan-area */
+ double length; /* use this length of scan-area */
+
+ int x_nres;
+ int y_nres;
+
+ int x_p_nres; /* same as above, but apply to preview */
+ int y_p_nres;
+
+
+ int tlx; /* Left edge in 'Internal Length Units'. */
+ int tly; /* Top edge in ILU */
+ int brx; /* Right edge in ILU. */
+ int bry; /* Bottom edge in ILU. */
+
+ int bits_per_color; /* bits per color (8/10/12) */
+ int bits_per_pixel; /* bits per pixel (24/30/40) */
+ int negative; /* Negative/positive object */
+ int dropoutcolor; /* Which color to scan when gray */
+ int transfermode; /**/
+ int gammaselection; /* Linear/Monitor*/
+ int shading;
+ int averaging;
+ int brightness_R;
+ int brightness_G;
+ int brightness_B;
+ int contrast_R;
+ int contrast_G;
+ int contrast_B;
+ int exposure_R;
+ int exposure_G;
+ int exposure_B;
+ int shift_R;
+ int shift_G;
+ int shift_B;
+ int set_auto; /* 0 or 1, don't know what it is */
+ int preview; /* 1 if preview */
+ int autofocus; /* when to do autofocus */
+#define AF_NEVER 0x00
+#define AF_PREVIEW 0x01
+#define AF_SCAN 0x02
+#define AF_PREANDSCAN 0x03
+
+ int colormode; /* GREYSCALE or RGB */
+ int colormode_p; /* GREYSCALE or RGB for preview */
+#define GREYSCALE 0x01
+#define RGB 0x07
+#define IRED 0x08
+#define RGBI 0x0f
+
+ int low_byte_first; /* 1 if little-endian - 0 if big-endian */
+
+ /* Internal information */
+ int adbits; /* Number of A/D bits [8 or 12] */
+ int outputbits; /* Number of output image data bits [8] */
+ int maxres; /* Maximum resolution [2700] (dpi) */
+ int xmax; /* X-axis coordinate maximum value
+ (basic measurement unit when measurement
+ unit divisor = 1200) [1151] */
+ int ymax; /* Y-axis coordinate maximum value
+ (basic measurement unit when measurement
+ unit divisor = 1200) [1727] */
+ int xmaxpix; /* X-axis coordinate maximum value (pixel
+ address value) [2591] */
+ int ymaxpix; /* Y-axis coordinate maximum value (pixel
+ address value) [3887] */
+ int ycurrent; /* Current stage position (Y-axis direction
+ pixel address) [0-7652] */
+ int currentfocus; /* Current focus position (focus direction
+ address) [0-200] */
+ int currentscanpitch; /* Current scan pitch [1-25] */
+ int autofeeder; /* Provision of auto feeder [Yes: 1, No: 0] */
+ int analoggamma; /* Analog gamma support [Yes: 1, No: 0] */
+ int derr[8]; /* Device error code (0 is latest, 7 oldest) */
+ int wbetr_r; /* White balance exposure time variable (R) */
+ int webtr_g; /* White balance exposure time variable (G) */
+ int webtr_b; /* White balance exposure time variable (B) */
+ int pretv_r; /* Prescan result exposure time variable (R) */
+ int pretv_g; /* Prescan result exposure time variable (G) */
+ int pretv_b; /* Prescan result exposure time variable (B) */
+ int cetv_r; /* Current exposure time variable (R) */
+ int cetv_g; /* Current exposure time variable (G) */
+ int cetv_b; /* Current exposure time variable (B) */
+ int ietu_r; /* Internal exposure time unit (R) */
+ int ietu_g; /* Internal exposure time unit (G) */
+ int ietu_b; /* Internal exposure time unit (B) */
+ int limitcondition; /* Condition of each limit SW, DIP SW, etc. */
+ int offsetdata_r; /* Offset data (R) */
+ int offsetdata_g; /* Offset data (G) */
+ int offsetdata_b; /* Offset data (B) */
+ char power_on_errors[8]; /* Records of error code at power on */
+ /* End of internal information */
+
+ int brightness; /* (128) cbhs_range 0-255, halftone mode */
+ int contrast; /* (128) cbhs_range 0-255, halftone-mode */
+
+ int prescan; /* */
+ int rgb_control; /* */
+ int gamma_bind; /* TRUE -> RGB */
+ int lutlength; /* length of gamma table */
+ int max_lut_val; /* maximum value in lut */
+ SANE_Word gamma[4096]; /* gamma value for RGB */
+ SANE_Word gamma_r[4096]; /* gamma value for red */
+ SANE_Word gamma_g[4096]; /* gamma value for green */
+ SANE_Word gamma_b[4096]; /* gamma value for blue */
+
+ int luti[4096]; /* lut value for infrared */
+ int lutr[4096]; /* lut value for red */
+ int lutg[4096]; /* lut value for green */
+ int lutb[4096]; /* lut value for blue */
+
+ char *gamma_file_r; /* file for gamma download */
+ char *gamma_file_g; /* file for gamma download */
+ char *gamma_file_b; /* file for gamma download */
+
+ int analog_gamma_r; /* analog gamma red and grey */
+ int analog_gamma_g; /* analog gamma green */
+ int analog_gamma_b; /* analog gamma blue */
+
+ /* Infrared correction values */
+ int ired_red;
+ int ired_green;
+ int ired_blue;
+
+ int feeder; /* type of feeder used */
+ int numima; /* number of images on film strip */
+ int posima; /* current image */
+ Image_Pos_t ipos[6]; /* positions for 6 images */
+#define STRIP_FEEDER 1
+#define MOUNT_FEEDER 2
+ }
+Coolscan_t;
+
+
+
+
+typedef struct
+ {
+ char *scanner;
+ char *inquiry;
+ int inquiry_len;
+ }
+inquiry_blk;
+
+
+/* ==================================================================== */
+
+/* names of scanners that are supported because */
+/* the inquiry_return_block is ok and driver is tested */
+
+static char *scanner_str[] =
+{
+ "COOLSCAN II ",
+ "LS-1000 ",
+ "COOLSCANIII ",
+ "LS-2000 ",
+};
+
+#define known_scanners 4
+
+/* Comment this line if you havn't patched sane.h to include
+ SANE_FRAME_RGBA */
+/* #define HAS_IRED 1 */
+
+#endif /* coolscan-sane_h */
diff --git a/backend/coolscan2.c b/backend/coolscan2.c
new file mode 100644
index 0000000..9f9efde
--- /dev/null
+++ b/backend/coolscan2.c
@@ -0,0 +1,3092 @@
+/* ========================================================================= */
+/*
+ SANE - Scanner Access Now Easy.
+ coolscan2.c , version 0.1.8
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Nikon Coolscan film scanners.
+
+ Written by András Major (andras@users.sourceforge.net), 2001-2002.
+
+ The developers wish to express their thanks to Nikon Corporation
+ for providing technical information and thus making this backend
+ possible.
+*/
+/* ========================================================================= */
+
+
+/* ========================================================================= */
+/*
+ Revision log:
+
+ 0.1.9, 20/10/2005, ariel: added support for the LS-50/5000
+ 0.1.8, 27/09/2002, andras: added subframe and load options
+ 0.1.7, 22/08/2002, andras: added exposure correction option
+ and hack for LS-40 IR readout
+ 0.1.6, 14/06/2002, andras: types etc. fixed, fixes for LS-8000
+ 0.1.5, 26/04/2002, andras: lots of minor fixes related to saned
+ 0.1.4, 22/04/2002, andras: first version to be included in SANE CVS
+
+*/
+/* ========================================================================= */
+
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+/*
+#include <limits.h>
+#include <sys/types.h>
+*/
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_config.h"
+#define BACKEND_NAME coolscan2
+#include "../include/sane/sanei_backend.h" /* must be last */
+
+#define CS2_VERSION_MAJOR 0
+#define CS2_VERSION_MINOR 1
+#define CS2_REVISION 8
+#define CS2_CONFIG_FILE "coolscan2.conf"
+
+#define WSIZE (sizeof (SANE_Word))
+
+/*
+#define CS2_BLEEDING_EDGE
+*/
+
+
+/* ========================================================================= */
+/* typedefs */
+
+typedef enum
+{
+ CS2_TYPE_UNKOWN,
+ CS2_TYPE_LS30,
+ CS2_TYPE_LS40,
+ CS2_TYPE_LS50,
+ CS2_TYPE_LS2000,
+ CS2_TYPE_LS4000,
+ CS2_TYPE_LS5000,
+ CS2_TYPE_LS8000
+}
+cs2_type_t;
+
+typedef enum
+{
+ CS2_INTERFACE_UNKNOWN,
+ CS2_INTERFACE_SCSI, /* includes IEEE1394 via SBP2 */
+ CS2_INTERFACE_USB
+}
+cs2_interface_t;
+
+typedef enum
+{
+ CS2_PHASE_NONE = 0x00,
+ CS2_PHASE_STATUS = 0x01,
+ CS2_PHASE_OUT = 0x02,
+ CS2_PHASE_IN = 0x03,
+ CS2_PHASE_BUSY = 0x04
+}
+cs2_phase_t;
+
+typedef enum
+{
+ CS2_SCAN_NORMAL,
+ CS2_SCAN_AE,
+ CS2_SCAN_AE_WB
+}
+cs2_scan_t;
+
+typedef enum
+{
+ CS2_INFRARED_OFF,
+ CS2_INFRARED_IN,
+ CS2_INFRARED_OUT
+}
+cs2_infrared_t;
+
+typedef enum
+{
+ CS2_STATUS_READY = 0,
+ CS2_STATUS_BUSY = 1,
+ CS2_STATUS_NO_DOCS = 2,
+ CS2_STATUS_PROCESSING = 4,
+ CS2_STATUS_ERROR = 8,
+ CS2_STATUS_REISSUE = 16,
+ CS2_STATUS_ALL = 31 /* sum of all others */
+}
+cs2_status_t;
+
+typedef enum
+{
+ CS2_OPTION_NUM = 0,
+
+ CS2_OPTION_PREVIEW,
+
+ CS2_OPTION_NEGATIVE,
+
+ CS2_OPTION_INFRARED,
+
+ CS2_OPTION_SAMPLES_PER_SCAN,
+
+ CS2_OPTION_DEPTH,
+
+ CS2_OPTION_EXPOSURE,
+ CS2_OPTION_EXPOSURE_R,
+ CS2_OPTION_EXPOSURE_G,
+ CS2_OPTION_EXPOSURE_B,
+ CS2_OPTION_SCAN_AE,
+ CS2_OPTION_SCAN_AE_WB,
+
+ CS2_OPTION_LUT_R,
+ CS2_OPTION_LUT_G,
+ CS2_OPTION_LUT_B,
+
+ CS2_OPTION_RES,
+ CS2_OPTION_RESX,
+ CS2_OPTION_RESY,
+ CS2_OPTION_RES_INDEPENDENT,
+
+ CS2_OPTION_PREVIEW_RESOLUTION,
+
+ CS2_OPTION_FRAME,
+ CS2_OPTION_SUBFRAME,
+ CS2_OPTION_XMIN,
+ CS2_OPTION_XMAX,
+ CS2_OPTION_YMIN,
+ CS2_OPTION_YMAX,
+
+ CS2_OPTION_LOAD,
+ CS2_OPTION_EJECT,
+ CS2_OPTION_RESET,
+
+ CS2_OPTION_FOCUS_ON_CENTRE,
+ CS2_OPTION_FOCUS,
+ CS2_OPTION_AUTOFOCUS,
+ CS2_OPTION_FOCUSX,
+ CS2_OPTION_FOCUSY,
+
+ CS2_N_OPTIONS /* must be last -- counts number of enum items */
+}
+cs2_option_t;
+
+typedef unsigned int cs2_pixel_t;
+
+typedef struct
+{
+ /* interface */
+ cs2_interface_t interface;
+ int fd;
+ SANE_Byte *send_buf, *recv_buf;
+ size_t send_buf_size, recv_buf_size;
+ size_t n_cmd, n_send, n_recv;
+
+ /* device characteristics */
+ char vendor_string[9], product_string[17], revision_string[5];
+ cs2_type_t type;
+ int maxbits;
+ unsigned int resx_optical, resx_min, resx_max, *resx_list, resx_n_list;
+ unsigned int resy_optical, resy_min, resy_max, *resy_list, resy_n_list;
+ unsigned long boundaryx, boundaryy;
+ unsigned long frame_offset;
+ unsigned int unit_dpi;
+ double unit_mm;
+ int n_frames;
+
+ int focus_min, focus_max;
+
+ /* settings */
+ SANE_Bool preview, negative, infrared;
+ int samples_per_scan, depth, real_depth, bytes_per_pixel, shift_bits,
+ n_colour_in, n_colour_out;
+ cs2_pixel_t n_lut;
+ cs2_pixel_t *lut_r, *lut_g, *lut_b, *lut_neutral;
+ unsigned long resx, resy, res, res_independent, res_preview;
+ unsigned long xmin, xmax, ymin, ymax;
+ int i_frame;
+ double subframe;
+
+ unsigned int real_resx, real_resy, real_pitchx, real_pitchy;
+ unsigned long real_xoffset, real_yoffset, real_width, real_height,
+ logical_width, logical_height;
+ int odd_padding;
+ int block_padding;
+
+ double exposure, exposure_r, exposure_g, exposure_b;
+ unsigned long real_exposure[10];
+
+ SANE_Bool focus_on_centre;
+ unsigned long focusx, focusy, real_focusx, real_focusy;
+ int focus;
+
+ /* status */
+ SANE_Bool scanning;
+ cs2_infrared_t infrared_stage, infrared_next;
+ SANE_Byte *infrared_buf;
+ size_t n_infrared_buf, infrared_index;
+ SANE_Byte *line_buf;
+ ssize_t n_line_buf, i_line_buf;
+ unsigned long sense_key, sense_asc, sense_ascq, sense_info;
+ unsigned long sense_code;
+ cs2_status_t status;
+ size_t xfer_position, xfer_bytes_total;
+
+ /* SANE stuff */
+ SANE_Option_Descriptor option_list[CS2_N_OPTIONS];
+}
+cs2_t;
+
+
+/* ========================================================================= */
+/* prototypes */
+
+static SANE_Status cs2_open (const char *device, cs2_interface_t interface,
+ cs2_t ** sp);
+static void cs2_close (cs2_t * s);
+static SANE_Status cs2_attach (const char *dev);
+static SANE_Status cs2_scsi_sense_handler (int fd, u_char * sense_buffer,
+ void *arg);
+static SANE_Status cs2_parse_sense_data (cs2_t * s);
+static void cs2_init_buffer (cs2_t * s);
+static SANE_Status cs2_pack_byte (cs2_t * s, SANE_Byte byte);
+static SANE_Status cs2_parse_cmd (cs2_t * s, char *text);
+static SANE_Status cs2_grow_send_buffer (cs2_t * s);
+static SANE_Status cs2_issue_cmd (cs2_t * s);
+static cs2_phase_t cs2_phase_check (cs2_t * s);
+static SANE_Status cs2_set_boundary (cs2_t *s);
+static SANE_Status cs2_scanner_ready (cs2_t * s, int flags);
+static SANE_Status cs2_page_inquiry (cs2_t * s, int page);
+static SANE_Status cs2_full_inquiry (cs2_t * s);
+static SANE_Status cs2_execute (cs2_t * s);
+static SANE_Status cs2_load (cs2_t * s);
+static SANE_Status cs2_eject (cs2_t * s);
+static SANE_Status cs2_reset (cs2_t * s);
+static SANE_Status cs2_focus (cs2_t * s);
+static SANE_Status cs2_autofocus (cs2_t * s);
+static SANE_Status cs2_get_exposure (cs2_t * s);
+static SANE_Status cs2_convert_options (cs2_t * s);
+static SANE_Status cs2_scan (cs2_t * s, cs2_scan_t type);
+static void *cs2_xmalloc (size_t size);
+static void *cs2_xrealloc (void *p, size_t size);
+static void cs2_xfree (const void *p);
+
+
+/* ========================================================================= */
+/* global variables */
+
+static int cs2_colour_list[] = { 1, 2, 3, 9 };
+
+static SANE_Device **device_list = NULL;
+static int n_device_list = 0;
+static cs2_interface_t try_interface = CS2_INTERFACE_UNKNOWN;
+static int open_devices = 0;
+
+
+/* ========================================================================= */
+/* SANE entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+ DBG (10, "sane_init() called.\n");
+ DBG (1, "coolscan2 backend, version %i.%i.%i initializing.\n", CS2_VERSION_MAJOR, CS2_VERSION_MINOR, CS2_REVISION);
+
+ authorize = authorize; /* to shut up compiler */
+
+ if (version_code)
+ *version_code =
+ SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ sanei_usb_init ();
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ int i;
+
+ DBG (10, "sane_exit() called.\n");
+
+ for (i = 0; i < n_device_list; i++)
+ {
+ cs2_xfree (device_list[i]->name);
+ cs2_xfree (device_list[i]->vendor);
+ cs2_xfree (device_list[i]->model);
+ cs2_xfree (device_list[i]);
+ }
+ cs2_xfree (device_list);
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** list, SANE_Bool local_only)
+{
+ char line[PATH_MAX], *p;
+ FILE *config;
+
+ local_only = local_only; /* to shut up compiler */
+
+ DBG (10, "sane_get_devices() called.\n");
+
+ if (device_list)
+ DBG (6,
+ "sane_get_devices(): Device list already populated, not probing again.\n");
+ else
+ {
+ if (open_devices)
+ {
+ DBG (4,
+ "sane_get_devices(): Devices open, not scanning for scanners.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ config = sanei_config_open (CS2_CONFIG_FILE);
+ if (config)
+ {
+ DBG (4, "sane_get_devices(): Reading config file.\n");
+ while (sanei_config_read (line, sizeof (line), config))
+ {
+ p = line;
+ p += strspn (line, " \t");
+ if (strlen (p) && (p[0] != '\n') && (p[0] != '#'))
+ cs2_open (line, CS2_INTERFACE_UNKNOWN, NULL);
+ }
+ fclose (config);
+ }
+ else
+ {
+ DBG (4, "sane_get_devices(): No config file found.\n");
+ cs2_open ("auto", CS2_INTERFACE_UNKNOWN, NULL);
+ }
+
+ switch (n_device_list)
+ {
+ case 0:
+ DBG (6, "sane_get_devices(): No devices detected.\n");
+ break;
+ case 1:
+ DBG (6, "sane_get_devices(): 1 device detected.\n");
+ break;
+ default:
+ DBG (6, "sane_get_devices(): %i devices detected.\n",
+ n_device_list);
+ break;
+ }
+ }
+
+ *list = (const SANE_Device **) device_list;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ SANE_Status status;
+ cs2_t *s;
+ int i_option;
+ unsigned int i_list;
+ SANE_Option_Descriptor o;
+ SANE_Word *word_list;
+ SANE_Range *range = NULL;
+ int alloc_failed = 0;
+
+ DBG (10, "sane_open() called.\n");
+
+ status = cs2_open (name, CS2_INTERFACE_UNKNOWN, &s);
+ if (status)
+ return status;
+
+ *h = (SANE_Handle) s;
+
+ /* get device properties */
+
+ s->lut_r = s->lut_g = s->lut_b = s->lut_neutral = NULL;
+ s->resx_list = s->resy_list = NULL;
+ s->resx_n_list = s->resy_n_list = 0;
+
+ status = cs2_full_inquiry (s);
+ if (status)
+ return status;
+
+ /* option descriptors */
+
+ for (i_option = 0; i_option < CS2_N_OPTIONS; i_option++)
+ {
+ o.name = o.title = o.desc = NULL;
+ o.type = o.unit = o.cap = o.constraint_type = o.size = 0;
+ o.constraint.range = NULL; /* only one union member needs to be NULLed */
+ switch (i_option)
+ {
+ case CS2_OPTION_NUM:
+ o.name = "";
+ o.title = SANE_TITLE_NUM_OPTIONS;
+ o.desc = SANE_DESC_NUM_OPTIONS;
+ o.type = SANE_TYPE_INT;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_PREVIEW:
+ o.name = "preview";
+ o.title = "Preview mode";
+ o.desc = "Preview mode";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ break;
+ case CS2_OPTION_NEGATIVE:
+ o.name = "negative";
+ o.title = "Negative";
+ o.desc = "Negative film: make scanner invert colours";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+#ifndef CS2_BLEEDING_EDGE
+ o.cap |= SANE_CAP_INACTIVE;
+#endif
+ break;
+ case CS2_OPTION_INFRARED:
+ o.name = "infrared";
+ o.title = "Read infrared channel";
+ o.desc = "Read infrared channel in addition to scan colours";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_SAMPLES_PER_SCAN:
+ o.name = "samples-per-scan";
+ o.title = "Samples per Scan";
+ o.desc = "Number of samples per scan";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->type != CS2_TYPE_LS2000 && s->type != CS2_TYPE_LS4000
+ && s->type != CS2_TYPE_LS5000 && s->type != CS2_TYPE_LS8000)
+ o.cap |= SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (! range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 1;
+ range->max = 16;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_DEPTH:
+ o.name = "depth";
+ o.title = "Bit depth per channel";
+ o.desc = "Number of bits output by scanner for each channel";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ word_list = (SANE_Word *) cs2_xmalloc (2 * sizeof (SANE_Word));
+ if (!word_list)
+ alloc_failed = 1;
+ else
+ {
+ word_list[1] = 8;
+ word_list[2] = s->maxbits;
+ word_list[0] = 2;
+ o.constraint.word_list = word_list;
+ }
+ break;
+ case CS2_OPTION_EXPOSURE:
+ o.name = "exposure";
+ o.title = "Exposure multiplier";
+ o.desc = "Exposure multiplier for all channels";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = SANE_FIX (0.);
+ range->max = SANE_FIX (10.);
+ range->quant = SANE_FIX (0.1);
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_EXPOSURE_R:
+ o.name = "red-exposure";
+ o.title = "Red exposure time";
+ o.desc = "Exposure time for red channel";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MICROSECOND;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = SANE_FIX (50.);
+ range->max = SANE_FIX (20000.);
+ range->quant = SANE_FIX (10.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_EXPOSURE_G:
+ o.name = "green-exposure";
+ o.title = "Green exposure time";
+ o.desc = "Exposure time for green channel";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MICROSECOND;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = SANE_FIX (50.);
+ range->max = SANE_FIX (20000.);
+ range->quant = SANE_FIX (10.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_EXPOSURE_B:
+ o.name = "blue-exposure";
+ o.title = "Blue exposure time";
+ o.desc = "Exposure time for blue channel";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MICROSECOND;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = SANE_FIX (50.);
+ range->max = SANE_FIX (20000.);
+ range->quant = SANE_FIX (10.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_LUT_R:
+ o.name = "red-gamma-table";
+ o.title = "LUT for red channel";
+ o.desc = "LUT for red channel";
+ o.type = SANE_TYPE_INT;
+ o.size = s->n_lut * WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->n_lut - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_LUT_G:
+ o.name = "green-gamma-table";
+ o.title = "LUT for green channel";
+ o.desc = "LUT for green channel";
+ o.type = SANE_TYPE_INT;
+ o.size = s->n_lut * WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->n_lut - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_LUT_B:
+ o.name = "blue-gamma-table";
+ o.title = "LUT for blue channel";
+ o.desc = "LUT for blue channel";
+ o.type = SANE_TYPE_INT;
+ o.size = s->n_lut * WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->n_lut - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_LOAD:
+ o.name = "load";
+ o.title = "Load";
+ o.desc = "Load next slide";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_EJECT:
+ o.name = "eject";
+ o.title = "Eject";
+ o.desc = "Eject loaded medium";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_RESET:
+ o.name = "reset";
+ o.title = "Reset scanner";
+ o.desc = "Initialize scanner";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_RESX:
+ case CS2_OPTION_RES:
+ case CS2_OPTION_PREVIEW_RESOLUTION:
+ if (i_option == CS2_OPTION_PREVIEW_RESOLUTION)
+ {
+ o.name = "preview-resolution";
+ o.title = "Preview resolution";
+ o.desc =
+ "Scanning resolution for preview mode in dpi, affecting both x and y directions";
+ }
+ else if (i_option == CS2_OPTION_RES)
+ {
+ o.name = "resolution";
+ o.title = "Resolution";
+ o.desc =
+ "Scanning resolution in dpi, affecting both x and y directions";
+ }
+ else
+ {
+ o.name = "x-resolution";
+ o.title = "X resolution";
+ o.desc =
+ "Scanning resolution in dpi, affecting x direction only";
+ }
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_DPI;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (i_option == CS2_OPTION_RESX)
+ o.cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ if (i_option == CS2_OPTION_PREVIEW_RESOLUTION)
+ o.cap |= SANE_CAP_ADVANCED;
+ o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ word_list =
+ (SANE_Word *) cs2_xmalloc ((s->resx_n_list + 1) *
+ sizeof (SANE_Word));
+ if (!word_list)
+ alloc_failed = 1;
+ else
+ {
+ for (i_list = 0; i_list < s->resx_n_list; i_list++)
+ word_list[i_list + 1] = s->resx_list[i_list];
+ word_list[0] = s->resx_n_list;
+ o.constraint.word_list = word_list;
+ }
+ break;
+ case CS2_OPTION_RESY:
+ o.name = "y-resolution";
+ o.title = "Y resolution";
+ o.desc = "Scanning resolution in dpi, affecting y direction only";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_DPI;
+ o.size = WSIZE;
+ o.cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ word_list =
+ (SANE_Word *) cs2_xmalloc ((s->resy_n_list + 1) *
+ sizeof (SANE_Word));
+ if (!word_list)
+ alloc_failed = 1;
+ else
+ {
+ for (i_list = 0; i_list < s->resy_n_list; i_list++)
+ word_list[i_list + 1] = s->resy_list[i_list];
+ word_list[0] = s->resy_n_list;
+ o.constraint.word_list = word_list;
+ }
+ break;
+ case CS2_OPTION_RES_INDEPENDENT:
+ o.name = "independent-res";
+ o.title = "Independent x/y resolutions";
+ o.desc =
+ "Enable independent controls for scanning resolution in x and y direction";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ break;
+ case CS2_OPTION_FRAME:
+ o.name = "frame";
+ o.title = "Frame number";
+ o.desc = "Number of frame to be scanned, starting with 1";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->n_frames <= 1)
+ o.cap |= SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 1;
+ range->max = s->n_frames;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_SUBFRAME:
+ o.name = "subframe";
+ o.title = "Frame shift";
+ o.desc = "Fine position within the selected frame";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MM;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = SANE_FIX (0.);
+ range->max = SANE_FIX ((s->boundaryy - 1) * s->unit_mm);
+ range->quant = SANE_FIX (0.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_XMIN:
+ o.name = "tl-x";
+ o.title = "Left x value of scan area";
+ o.desc = "Left x value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ range->min = 0;
+ range->max = s->boundaryx - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_XMAX:
+ o.name = "br-x";
+ o.title = "Right x value of scan area";
+ o.desc = "Right x value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->boundaryx - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_YMIN:
+ o.name = "tl-y";
+ o.title = "Top y value of scan area";
+ o.desc = "Top y value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->boundaryy - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_YMAX:
+ o.name = "br-y";
+ o.title = "Bottom y value of scan area";
+ o.desc = "Bottom y value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->boundaryy - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_FOCUS_ON_CENTRE:
+ o.name = "focus-on-centre";
+ o.title = "Use centre of scan area as AF point";
+ o.desc =
+ "Use centre of scan area as AF point instead of manual AF point selection";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_FOCUS:
+ o.name = "focus";
+ o.title = "Focus position";
+ o.desc = "Focus position for manual focus";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = s->focus_min;
+ range->max = s->focus_max;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_AUTOFOCUS:
+ o.name = "autofocus";
+ o.title = "Autofocus now";
+ o.desc = "Autofocus now";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_FOCUSX:
+ o.name = "focusx";
+ o.title = "X coordinate of AF point";
+ o.desc = "X coordinate of AF point";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->boundaryx - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_FOCUSY:
+ o.name = "focusy";
+ o.title = "Y coordinate of AF point";
+ o.desc = "Y coordinate of AF point";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 0;
+ range->max = s->boundaryy - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS2_OPTION_SCAN_AE:
+ o.name = "ae";
+ o.title = "Auto-exposure scan now";
+ o.desc = "Perform auto-exposure scan";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS2_OPTION_SCAN_AE_WB:
+ o.name = "ae-wb";
+ o.title = "Auto-exposure scan with white balance now";
+ o.desc = "Perform auto-exposure scan with white balance";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ default:
+ DBG (1, "BUG: sane_open(): Unknown option number.\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ s->option_list[i_option] = o;
+ }
+
+ s->scanning = SANE_FALSE;
+ s->preview = SANE_FALSE;
+ s->negative = SANE_FALSE;
+ s->depth = 8;
+ s->infrared = 0;
+ s->samples_per_scan = 1;
+ s->i_frame = 1;
+ s->subframe = 0.;
+ s->res = s->resx = s->resx_max;
+ s->resy = s->resy_max;
+ s->res_independent = SANE_FALSE;
+ s->res_preview = s->resx_max / 10;
+ if (s->res_preview < s->resx_min)
+ s->res_preview = s->resx_min;
+ s->xmin = 0;
+ s->xmax = s->boundaryx - 1;
+ s->ymin = 0;
+ s->ymax = s->boundaryy - 1;
+ s->focus_on_centre = SANE_TRUE;
+ s->focus = 0;
+ s->focusx = 0;
+ s->focusy = 0;
+ s->exposure = 1.;
+ s->exposure_r = 1200.;
+ s->exposure_g = 1200.;
+ s->exposure_b = 1000.;
+ s->infrared_stage = CS2_INFRARED_OFF;
+ s->infrared_next = CS2_INFRARED_OFF;
+ s->infrared_buf = NULL;
+ s->n_infrared_buf = 0;
+ s->line_buf = NULL;
+ s->n_line_buf = 0;
+
+ if (alloc_failed)
+ {
+ cs2_close (s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ cs2_t *s = (cs2_t *) h;
+
+ DBG (10, "sane_close() called.\n");
+
+ cs2_close (s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ cs2_t *s = (cs2_t *) h;
+
+ DBG (10, "sane_get_option_descriptor() called, option #%i.\n", n);
+
+ if ((n >= 0) && (n < CS2_N_OPTIONS))
+ return &s->option_list[n];
+ else
+ return NULL;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action a, void *v,
+ SANE_Int * i)
+{
+ cs2_t *s = (cs2_t *) h;
+ SANE_Int flags = 0;
+ cs2_pixel_t pixel;
+ SANE_Status status;
+ SANE_Option_Descriptor o = s->option_list[n];
+
+ DBG (10, "sane_control_option() called, option #%i, action #%i.\n", n, a);
+
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+
+ switch (n)
+ {
+ case CS2_OPTION_NUM:
+ *(SANE_Word *) v = CS2_N_OPTIONS;
+ break;
+ case CS2_OPTION_NEGATIVE:
+ *(SANE_Word *) v = s->negative;
+ break;
+ case CS2_OPTION_INFRARED:
+ *(SANE_Word *) v = s->infrared;
+ break;
+ case CS2_OPTION_SAMPLES_PER_SCAN:
+ *(SANE_Word *) v = s->samples_per_scan;
+ break;
+ case CS2_OPTION_DEPTH:
+ *(SANE_Word *) v = s->depth;
+ break;
+ case CS2_OPTION_PREVIEW:
+ *(SANE_Word *) v = s->preview;
+ break;
+ case CS2_OPTION_EXPOSURE:
+ *(SANE_Word *) v = SANE_FIX (s->exposure);
+ break;
+ case CS2_OPTION_EXPOSURE_R:
+ *(SANE_Word *) v = SANE_FIX (s->exposure_r);
+ break;
+ case CS2_OPTION_EXPOSURE_G:
+ *(SANE_Word *) v = SANE_FIX (s->exposure_g);
+ break;
+ case CS2_OPTION_EXPOSURE_B:
+ *(SANE_Word *) v = SANE_FIX (s->exposure_b);
+ break;
+ case CS2_OPTION_LUT_R:
+ if (!(s->lut_r))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ ((SANE_Word *) v)[pixel] = s->lut_r[pixel];
+ break;
+ case CS2_OPTION_LUT_G:
+ if (!(s->lut_g))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ ((SANE_Word *) v)[pixel] = s->lut_g[pixel];
+ break;
+ case CS2_OPTION_LUT_B:
+ if (!(s->lut_b))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ ((SANE_Word *) v)[pixel] = s->lut_b[pixel];
+ break;
+ case CS2_OPTION_EJECT:
+ break;
+ case CS2_OPTION_LOAD:
+ break;
+ case CS2_OPTION_RESET:
+ break;
+ case CS2_OPTION_FRAME:
+ *(SANE_Word *) v = s->i_frame;
+ break;
+ case CS2_OPTION_SUBFRAME:
+ *(SANE_Word *) v = SANE_FIX (s->subframe);
+ break;
+ case CS2_OPTION_RES:
+ *(SANE_Word *) v = s->res;
+ break;
+ case CS2_OPTION_RESX:
+ *(SANE_Word *) v = s->resx;
+ break;
+ case CS2_OPTION_RESY:
+ *(SANE_Word *) v = s->resy;
+ break;
+ case CS2_OPTION_RES_INDEPENDENT:
+ *(SANE_Word *) v = s->res_independent;
+ break;
+ case CS2_OPTION_PREVIEW_RESOLUTION:
+ *(SANE_Word *) v = s->res_preview;
+ break;
+ case CS2_OPTION_XMIN:
+ *(SANE_Word *) v = s->xmin;
+ break;
+ case CS2_OPTION_XMAX:
+ *(SANE_Word *) v = s->xmax;
+ break;
+ case CS2_OPTION_YMIN:
+ *(SANE_Word *) v = s->ymin;
+ break;
+ case CS2_OPTION_YMAX:
+ *(SANE_Word *) v = s->ymax;
+ break;
+ case CS2_OPTION_FOCUS_ON_CENTRE:
+ *(SANE_Word *) v = s->focus_on_centre;
+ break;
+ case CS2_OPTION_FOCUS:
+ *(SANE_Word *) v = s->focus;
+ break;
+ case CS2_OPTION_AUTOFOCUS:
+ break;
+ case CS2_OPTION_FOCUSX:
+ *(SANE_Word *) v = s->focusx;
+ break;
+ case CS2_OPTION_FOCUSY:
+ *(SANE_Word *) v = s->focusy;
+ break;
+ case CS2_OPTION_SCAN_AE:
+ break;
+ case CS2_OPTION_SCAN_AE_WB:
+ break;
+ default:
+ DBG (4, "Error: sane_control_option(): Unknown option (bug?).\n");
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if (s->scanning)
+ return SANE_STATUS_INVAL;
+/* XXXXXXXXXXXXXXXXX do this for all elements of arrays */
+ switch (o.type)
+ {
+ case SANE_TYPE_BOOL:
+ if ((*(SANE_Word *) v != SANE_TRUE)
+ && (*(SANE_Word *) v != SANE_FALSE))
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ switch (o.constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ if (*(SANE_Word *) v < o.constraint.range->min)
+ {
+ *(SANE_Word *) v = o.constraint.range->min;
+ flags |= SANE_INFO_INEXACT;
+ }
+ else if (*(SANE_Word *) v > o.constraint.range->max)
+ {
+ *(SANE_Word *) v = o.constraint.range->max;
+ flags |= SANE_INFO_INEXACT;
+ }
+ break;
+ case SANE_CONSTRAINT_WORD_LIST:
+ break;
+ default:
+ break;
+ }
+ break;
+ case SANE_TYPE_STRING:
+ break;
+ case SANE_TYPE_BUTTON:
+ break;
+ case SANE_TYPE_GROUP:
+ break;
+ }
+ switch (n)
+ {
+ case CS2_OPTION_NUM:
+ return SANE_STATUS_INVAL;
+ break;
+ case CS2_OPTION_NEGATIVE:
+ s->negative = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_INFRARED:
+ s->infrared = *(SANE_Word *) v;
+ /* flags |= SANE_INFO_RELOAD_PARAMS; XXXXXXXXXXXXXXXXX */
+ break;
+ case CS2_OPTION_SAMPLES_PER_SCAN:
+ s->samples_per_scan = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_DEPTH:
+ s->depth = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_PREVIEW:
+ s->preview = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_EXPOSURE:
+ s->exposure = SANE_UNFIX (*(SANE_Word *) v);
+ break;
+ case CS2_OPTION_EXPOSURE_R:
+ s->exposure_r = SANE_UNFIX (*(SANE_Word *) v);
+ break;
+ case CS2_OPTION_EXPOSURE_G:
+ s->exposure_g = SANE_UNFIX (*(SANE_Word *) v);
+ break;
+ case CS2_OPTION_EXPOSURE_B:
+ s->exposure_b = SANE_UNFIX (*(SANE_Word *) v);
+ break;
+ case CS2_OPTION_LUT_R:
+ if (!(s->lut_r))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_r[pixel] = ((SANE_Word *) v)[pixel];
+ break;
+ case CS2_OPTION_LUT_G:
+ if (!(s->lut_g))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_g[pixel] = ((SANE_Word *) v)[pixel];
+ break;
+ case CS2_OPTION_LUT_B:
+ if (!(s->lut_b))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_b[pixel] = ((SANE_Word *) v)[pixel];
+ break;
+ case CS2_OPTION_LOAD:
+ cs2_load (s);
+ break;
+ case CS2_OPTION_EJECT:
+ cs2_eject (s);
+ break;
+ case CS2_OPTION_RESET:
+ cs2_reset (s);
+ break;
+ case CS2_OPTION_FRAME:
+ s->i_frame = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_SUBFRAME:
+ s->subframe = SANE_UNFIX (*(SANE_Word *) v);
+ break;
+ case CS2_OPTION_RES:
+ s->res = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_RESX:
+ s->resx = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_RESY:
+ s->resy = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_RES_INDEPENDENT:
+ s->res_independent = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_PREVIEW_RESOLUTION:
+ s->res_preview = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_XMIN:
+ s->xmin = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_XMAX:
+ s->xmax = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_YMIN:
+ s->ymin = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_YMAX:
+ s->ymax = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS2_OPTION_FOCUS_ON_CENTRE:
+ s->focus_on_centre = *(SANE_Word *) v;
+ if (s->focus_on_centre)
+ {
+ s->option_list[CS2_OPTION_FOCUSX].cap |= SANE_CAP_INACTIVE;
+ s->option_list[CS2_OPTION_FOCUSY].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->option_list[CS2_OPTION_FOCUSX].cap &= ~SANE_CAP_INACTIVE;
+ s->option_list[CS2_OPTION_FOCUSY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ flags |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case CS2_OPTION_FOCUS:
+ s->focus = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_AUTOFOCUS:
+ cs2_autofocus (s);
+ flags |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case CS2_OPTION_FOCUSX:
+ s->focusx = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_FOCUSY:
+ s->focusy = *(SANE_Word *) v;
+ break;
+ case CS2_OPTION_SCAN_AE:
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ status = cs2_scan (s, CS2_SCAN_AE);
+ if (status)
+ return status;
+ status = cs2_get_exposure (s);
+ if (status)
+ return status;
+ s->exposure = 1.;
+ s->exposure_r = s->real_exposure[1] / 100.;
+ s->exposure_g = s->real_exposure[2] / 100.;
+ s->exposure_b = s->real_exposure[3] / 100.;
+ flags |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case CS2_OPTION_SCAN_AE_WB:
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ status = cs2_scan (s, CS2_SCAN_AE_WB);
+ if (status)
+ return status;
+ status = cs2_get_exposure (s);
+ if (status)
+ return status;
+ s->exposure = 1.;
+ s->exposure_r = s->real_exposure[1] / 100.;
+ s->exposure_g = s->real_exposure[2] / 100.;
+ s->exposure_b = s->real_exposure[3] / 100.;
+ flags |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ default:
+ DBG (4,
+ "Error: sane_control_option(): Unknown option number (bug?).\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+
+ default:
+ DBG (1, "BUG: sane_control_option(): Unknown action number.\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (i)
+ *i = flags;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ cs2_t *s = (cs2_t *) h;
+ SANE_Status status;
+
+ DBG (10, "sane_get_parameters() called.\n");
+
+ if (!s->scanning) /* only recalculate when not scanning */
+ {
+ status = cs2_convert_options (s);
+ if (status)
+ return status;
+ }
+
+ if (s->infrared_stage == CS2_INFRARED_OUT)
+ {
+ p->format = SANE_FRAME_GRAY;
+ p->bytes_per_line = s->logical_width * s->bytes_per_pixel;
+ }
+ else
+ {
+ p->format = SANE_FRAME_RGB; /* XXXXXXXX CCCCCCCCCC */
+ p->bytes_per_line =
+ s->n_colour_out * s->logical_width * s->bytes_per_pixel;
+ }
+ p->last_frame = SANE_TRUE;
+ p->lines = s->logical_height;
+ p->depth = 8 * s->bytes_per_pixel;
+ p->pixels_per_line = s->logical_width;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ cs2_t *s = (cs2_t *) h;
+ SANE_Status status;
+
+ DBG (10, "sane_start() called.\n");
+
+ if (s->scanning)
+ return SANE_STATUS_INVAL;
+
+ status = cs2_convert_options (s);
+ if (status)
+ return status;
+
+ s->infrared_index = 0;
+ s->i_line_buf = 0;
+ s->xfer_position = 0;
+
+ s->scanning = SANE_TRUE;
+
+ if (s->infrared_stage == CS2_INFRARED_OUT)
+ return SANE_STATUS_GOOD;
+ else
+ return cs2_scan (s, CS2_SCAN_NORMAL);
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ cs2_t *s = (cs2_t *) h;
+ SANE_Status status;
+ ssize_t xfer_len_in, xfer_len_line, xfer_len_out;
+ unsigned long index;
+ int colour, n_colours, sample_pass;
+ uint8_t *s8 = NULL;
+ uint16_t *s16 = NULL;
+ double m_avg_sum;
+ SANE_Byte *line_buf_new;
+
+ DBG (10, "sane_read() called, maxlen = %i.\n", maxlen);
+
+ if (!s->scanning) {
+ *len = 0;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (s->infrared_stage == CS2_INFRARED_OUT)
+ {
+ xfer_len_out = maxlen;
+
+ if (s->xfer_position + xfer_len_out > s->n_infrared_buf)
+ xfer_len_out = s->n_infrared_buf - s->xfer_position;
+
+ if (xfer_len_out == 0) /* no more data */
+ {
+ *len = 0;
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_EOF;
+ }
+
+ memcpy (buf, &(s->infrared_buf[s->xfer_position]), xfer_len_out);
+
+ s->xfer_position += xfer_len_out;
+
+ if (s->xfer_position >= s->n_infrared_buf)
+ s->infrared_next = CS2_INFRARED_OFF;
+
+ *len = xfer_len_out;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (s->i_line_buf > 0)
+ {
+ xfer_len_out = s->n_line_buf - s->i_line_buf;
+ if (xfer_len_out > maxlen)
+ xfer_len_out = maxlen;
+
+ memcpy (buf, &(s->line_buf[s->i_line_buf]), xfer_len_out);
+
+ s->i_line_buf += xfer_len_out;
+ if (s->i_line_buf >= s->n_line_buf)
+ s->i_line_buf = 0;
+
+ *len = xfer_len_out;
+ return SANE_STATUS_GOOD;
+ }
+
+ xfer_len_line = s->n_colour_out * s->logical_width * s->bytes_per_pixel;
+ xfer_len_in =
+ s->n_colour_in * s->logical_width * s->bytes_per_pixel +
+ s->n_colour_in * s->odd_padding;
+ /* Do not change the behaviour of older models */
+ if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000))
+ {
+ /* Ariel - Check, win driver uses multiple of 64, docu seems to say 512? */
+ ssize_t i;
+ xfer_len_in += s->block_padding;
+ i = (xfer_len_in & 0x3f);
+ if (i != 0)
+ DBG (1, "BUG: sane_read(): Read size is not a multiple of 64. (0x%06lx)\n", (long) i);
+ }
+
+ if (s->xfer_position + xfer_len_line > s->xfer_bytes_total)
+ xfer_len_line = s->xfer_bytes_total - s->xfer_position; /* just in case */
+
+ if (xfer_len_line == 0) /* no more data */
+ {
+ *len = 0;
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_EOF;
+ }
+
+ if (xfer_len_line != s->n_line_buf)
+ {
+ line_buf_new =
+ (SANE_Byte *) cs2_xrealloc (s->line_buf,
+ xfer_len_line * sizeof (SANE_Byte));
+ if (!line_buf_new)
+ {
+ *len = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ s->line_buf = line_buf_new;
+ s->n_line_buf = xfer_len_line;
+ }
+
+ /* adapt for multi-sampling */
+ xfer_len_in *= s->samples_per_scan;
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "28 00 00 00 00 00");
+ cs2_pack_byte (s, (xfer_len_in >> 16) & 0xff);
+ cs2_pack_byte (s, (xfer_len_in >> 8) & 0xff);
+ cs2_pack_byte (s, xfer_len_in & 0xff);
+ cs2_parse_cmd (s, "00");
+ s->n_recv = xfer_len_in;
+ status = cs2_issue_cmd (s);
+
+ if (status)
+ {
+ *len = 0;
+ return status;
+ }
+
+ n_colours = s->n_colour_out +
+ (s->infrared_stage == CS2_INFRARED_IN ? 1 : 0);
+
+ for (index = 0; index < s->logical_width; index++)
+ for (colour = 0; colour < n_colours; colour++) {
+ m_avg_sum = 0.0;
+ switch (s->bytes_per_pixel)
+ {
+ case 1:
+ /* calculate target address */
+ if ((s->infrared_stage == CS2_INFRARED_IN)
+ && (colour == s->n_colour_out))
+ s8 = (uint8_t *) & (s->infrared_buf[s->infrared_index++]);
+ else
+ s8 =
+ (uint8_t *) & (s->line_buf[s->n_colour_out * index + colour]);
+
+ if (s->samples_per_scan > 1)
+ {
+ /* calculate average of multi samples */
+ for (sample_pass = 0;
+ sample_pass < s->samples_per_scan;
+ sample_pass++)
+ m_avg_sum += (double)
+ s->recv_buf[s->logical_width *
+ (sample_pass * n_colours + colour) +
+ (colour + 1) * s->odd_padding + index];
+
+ *s8 = (uint8_t) (m_avg_sum / s->samples_per_scan + 0.5);
+ }
+ else
+ /* shortcut for single sample */
+ *s8 =
+ s->recv_buf[colour * s->logical_width +
+ (colour + 1) * s->odd_padding + index];
+ break;
+ case 2:
+ /* calculate target address */
+ if ((s->infrared_stage == CS2_INFRARED_IN)
+ && (colour == s->n_colour_out))
+ s16 =
+ (uint16_t *) & (s->infrared_buf[2 * (s->infrared_index++)]);
+ else
+ s16 =
+ (uint16_t *) & (s->
+ line_buf[2 *
+ (s->n_colour_out * index + colour)]);
+
+ if (s->samples_per_scan > 1)
+ {
+ /* calculate average of multi samples */
+ for (sample_pass = 0;
+ s->samples_per_scan > 1 && sample_pass < s->samples_per_scan;
+ sample_pass++)
+ m_avg_sum += (double)
+ (s->recv_buf[2 * (s->logical_width * (sample_pass * n_colours + colour) + index)] * 256 +
+ s->recv_buf[2 * (s->logical_width * (sample_pass * n_colours + colour) + index) + 1]);
+
+ *s16 = (uint16_t) (m_avg_sum / s->samples_per_scan + 0.5);
+ }
+ else
+ /* shortcut for single sample */
+ *s16 =
+ s->recv_buf[2 * (colour * s->logical_width + index)] * 256 +
+ s->recv_buf[2 * (colour * s->logical_width + index) + 1];
+ *s16 <<= s->shift_bits;
+ break;
+ default:
+ DBG (1, "BUG: sane_read(): Unknown number of bytes per pixel.\n");
+ *len = 0;
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ }
+ s->xfer_position += xfer_len_line;
+
+ xfer_len_out = xfer_len_line;
+ if (xfer_len_out > maxlen)
+ xfer_len_out = maxlen;
+
+ memcpy (buf, s->line_buf, xfer_len_out);
+ if (xfer_len_out < xfer_len_line)
+ s->i_line_buf = xfer_len_out; /* data left in the line buffer, read out next time */
+
+ if ((s->infrared_stage == CS2_INFRARED_IN)
+ && (s->xfer_position >= s->n_infrared_buf))
+ s->infrared_next = CS2_INFRARED_OUT;
+
+ *len = xfer_len_out;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ cs2_t *s = (cs2_t *) h;
+
+ if (s->scanning)
+ DBG (10, "sane_cancel() called while scanning.\n");
+ else
+ DBG (10, "sane_cancel() called while not scanning.\n");
+
+ if (s->scanning && (s->infrared_stage != CS2_INFRARED_OUT))
+ {
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "c0 00 00 00 00 00");
+ cs2_issue_cmd (s);
+ }
+
+ s->scanning = SANE_FALSE;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ cs2_t *s = (cs2_t *) h;
+
+ DBG (10, "sane_set_io_mode() called.\n");
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+ if (m == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ cs2_t *s = (cs2_t *) h;
+
+ DBG (10, "sane_get_select_fd() called.\n");
+
+ fd = fd; /* to shut up compiler */
+ s = s; /* to shut up compiler */
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/* ========================================================================= */
+/* private functions */
+
+static SANE_Status
+cs2_open (const char *device, cs2_interface_t interface, cs2_t ** sp)
+{
+ SANE_Status status;
+ cs2_t *s;
+ char *prefix = NULL, *line, *device2;
+ int i;
+ int alloc_failed = 0;
+ SANE_Device **device_list_new;
+
+ DBG (6, "cs2_open() called, with device = %s and interface = %i\n", device,
+ interface);
+
+ if (!strncmp (device, "auto", 5))
+ {
+ try_interface = CS2_INTERFACE_SCSI;
+ sanei_config_attach_matching_devices ("scsi Nikon *", cs2_attach);
+ try_interface = CS2_INTERFACE_USB;
+ sanei_usb_attach_matching_devices ("usb 0x04b0 0x4000", cs2_attach);
+ sanei_usb_attach_matching_devices ("usb 0x04b0 0x4001", cs2_attach);
+ sanei_usb_attach_matching_devices ("usb 0x04b0 0x4002", cs2_attach);
+ return SANE_STATUS_GOOD;
+ }
+
+ if ((s = (cs2_t *) cs2_xmalloc (sizeof (cs2_t))) == NULL)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (cs2_t));
+ s->send_buf = s->recv_buf = NULL;
+ s->send_buf_size = s->recv_buf_size = 0;
+
+ switch (interface)
+ {
+ case CS2_INTERFACE_UNKNOWN:
+ for (i = 0; i < 2; i++)
+ {
+ switch (i)
+ {
+ case 1:
+ prefix = "usb:";
+ try_interface = CS2_INTERFACE_USB;
+ break;
+ default:
+ prefix = "scsi:";
+ try_interface = CS2_INTERFACE_SCSI;
+ break;
+ }
+ if (!strncmp (device, prefix, strlen (prefix)))
+ {
+ device2 = device + strlen (prefix);
+ cs2_xfree (s);
+ return cs2_open (device2, try_interface, sp);
+ }
+ }
+ cs2_xfree (s);
+ return SANE_STATUS_INVAL;
+ break;
+ case CS2_INTERFACE_SCSI:
+ s->interface = CS2_INTERFACE_SCSI;
+ DBG (6,
+ "cs2_open(): Trying to open %s, assuming SCSI or SBP2 interface ...\n",
+ device);
+ status = sanei_scsi_open (device, &s->fd, cs2_scsi_sense_handler, s);
+ if (status)
+ {
+ DBG (6, "cs2_open(): ... failed: %s.\n", sane_strstatus (status));
+ cs2_xfree (s);
+ return status;
+ }
+ break;
+ case CS2_INTERFACE_USB:
+ s->interface = CS2_INTERFACE_USB;
+ DBG (6, "cs2_open(): Trying to open %s, assuming USB interface ...\n",
+ device);
+ status = sanei_usb_open (device, &s->fd);
+ if (status)
+ {
+ DBG (6, "cs2_open(): ... failed: %s.\n", sane_strstatus (status));
+ cs2_xfree (s);
+ return status;
+ }
+ break;
+ }
+
+ open_devices++;
+ DBG (6, "cs2_open(): ... looks OK, trying to identify device.\n");
+
+ /* identify scanner */
+ status = cs2_page_inquiry (s, -1);
+ if (status)
+ {
+ DBG (4, "Error: cs2_open(): failed to get page: %s.\n",
+ sane_strstatus (status));
+ cs2_close (s);
+ return status;
+ }
+
+ strncpy (s->vendor_string, (char *)s->recv_buf + 8, 8);
+ s->vendor_string[8] = '\0';
+ strncpy (s->product_string, (char *)s->recv_buf + 16, 16);
+ s->product_string[16] = '\0';
+ strncpy (s->revision_string, (char *)s->recv_buf + 32, 4);
+ s->revision_string[4] = '\0';
+
+ DBG (10,
+ "cs2_open(): Inquiry reveals: vendor = '%s', product = '%s', revision = '%s'.\n",
+ s->vendor_string, s->product_string, s->revision_string);
+
+ if (!strncmp (s->product_string, "COOLSCANIII ", 16))
+ s->type = CS2_TYPE_LS30;
+ else if (!strncmp (s->product_string, "LS-40 ED ", 16))
+ s->type = CS2_TYPE_LS40;
+ else if (!strncmp (s->product_string, "LS-50 ED ", 16))
+ s->type = CS2_TYPE_LS50;
+ else if (!strncmp (s->product_string, "LS-2000 ", 16))
+ s->type = CS2_TYPE_LS2000;
+ else if (!strncmp (s->product_string, "LS-4000 ED ", 16))
+ s->type = CS2_TYPE_LS4000;
+ else if (!strncmp (s->product_string, "LS-5000 ED ", 16))
+ s->type = CS2_TYPE_LS5000;
+ else if (!strncmp (s->product_string, "LS-8000 ED ", 16))
+ s->type = CS2_TYPE_LS8000;
+
+ if (s->type != CS2_TYPE_UNKOWN)
+ DBG (10, "cs2_open(): Device identified as coolscan2 type #%i.\n",
+ s->type);
+ else
+ {
+ DBG (10, "cs2_open(): Device not identified.\n");
+ cs2_close (s);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (sp)
+ *sp = s;
+ else
+ {
+ device_list_new =
+ (SANE_Device **) cs2_xrealloc (device_list,
+ (n_device_list +
+ 2) * sizeof (SANE_Device *));
+ if (!device_list_new)
+ return SANE_STATUS_NO_MEM;
+ device_list = device_list_new;
+ device_list[n_device_list] =
+ (SANE_Device *) cs2_xmalloc (sizeof (SANE_Device));
+ if (!device_list[n_device_list])
+ return SANE_STATUS_NO_MEM;
+ switch (interface)
+ {
+ case CS2_INTERFACE_UNKNOWN:
+ DBG (1, "BUG: cs2_open(): unknown interface.\n");
+ cs2_close (s);
+ return SANE_STATUS_UNSUPPORTED;
+ break;
+ case CS2_INTERFACE_SCSI:
+ prefix = "scsi:";
+ break;
+ case CS2_INTERFACE_USB:
+ prefix = "usb:";
+ break;
+ }
+
+ line = (char *) cs2_xmalloc (strlen (device) + strlen (prefix) + 1);
+ if (!line)
+ alloc_failed = 1;
+ else
+ {
+ strcpy (line, prefix);
+ strcat (line, device);
+ device_list[n_device_list]->name = line;
+ }
+
+ line = (char *) cs2_xmalloc (strlen (s->vendor_string) + 1);
+ if (!line)
+ alloc_failed = 1;
+ else
+ {
+ strcpy (line, s->vendor_string);
+ device_list[n_device_list]->vendor = line;
+ }
+
+ line = (char *) cs2_xmalloc (strlen (s->product_string) + 1);
+ if (!line)
+ alloc_failed = 1;
+ else
+ {
+ strcpy (line, s->product_string);
+ device_list[n_device_list]->model = line;
+ }
+
+ device_list[n_device_list]->type = "film scanner";
+
+ if (alloc_failed)
+ {
+ cs2_xfree (device_list[n_device_list]->name);
+ cs2_xfree (device_list[n_device_list]->vendor);
+ cs2_xfree (device_list[n_device_list]->model);
+ cs2_xfree (device_list[n_device_list]);
+ }
+ else
+ n_device_list++;
+ device_list[n_device_list] = NULL;
+
+ cs2_close (s);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+cs2_close (cs2_t * s)
+{
+ cs2_xfree (s->lut_r);
+ cs2_xfree (s->lut_g);
+ cs2_xfree (s->lut_b);
+ cs2_xfree (s->lut_neutral);
+ cs2_xfree (s->infrared_buf);
+ cs2_xfree (s->line_buf);
+
+ switch (s->interface)
+ {
+ case CS2_INTERFACE_UNKNOWN:
+ DBG (1, "BUG: cs2_close(): Unknown interface number.\n");
+ break;
+ case CS2_INTERFACE_SCSI:
+ sanei_scsi_close (s->fd);
+ open_devices--;
+ break;
+ case CS2_INTERFACE_USB:
+ sanei_usb_close (s->fd);
+ open_devices--;
+ break;
+ }
+
+ cs2_xfree (s);
+}
+
+static SANE_Status
+cs2_attach (const char *dev)
+{
+ SANE_Status status;
+
+ if (try_interface == CS2_INTERFACE_UNKNOWN)
+ return SANE_STATUS_UNSUPPORTED;
+
+ status = cs2_open (dev, try_interface, NULL);
+ return status;
+}
+
+static SANE_Status
+cs2_scsi_sense_handler (int fd, u_char * sense_buffer, void *arg)
+{
+ cs2_t *s = (cs2_t *) arg;
+
+ fd = fd; /* to shut up compiler */
+
+ /* sort this out ! XXXXXXXXX */
+
+ s->sense_key = sense_buffer[2] & 0x0f;
+ s->sense_asc = sense_buffer[12];
+ s->sense_ascq = sense_buffer[13];
+ s->sense_info = sense_buffer[3];
+
+ return cs2_parse_sense_data (s);
+}
+
+static SANE_Status
+cs2_parse_sense_data (cs2_t * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ s->sense_code =
+ (s->sense_key << 24) + (s->sense_asc << 16) + (s->sense_ascq << 8) +
+ s->sense_info;
+
+ if (s->sense_key)
+ DBG (10, "Sense code: %02lx-%02lx-%02lx-%02lx\n", s->sense_key,
+ s->sense_asc, s->sense_ascq, s->sense_info);
+
+ switch (s->sense_key)
+ {
+ case 0x00:
+ s->status = CS2_STATUS_READY;
+ break;
+ case 0x02:
+ switch (s->sense_asc)
+ {
+ case 0x04:
+ s->status = CS2_STATUS_PROCESSING;
+ break;
+ case 0x3a:
+ s->status = CS2_STATUS_NO_DOCS;
+ break;
+ default:
+ s->status = CS2_STATUS_ERROR;
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ break;
+ default:
+ s->status = CS2_STATUS_ERROR;
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+
+ if ((s->sense_code == 0x09800600) || (s->sense_code == 0x09800601))
+ s->status = CS2_STATUS_REISSUE;
+
+ return status;
+}
+
+static void
+cs2_init_buffer (cs2_t * s)
+{
+ s->n_cmd = 0;
+ s->n_send = 0;
+ s->n_recv = 0;
+}
+
+static SANE_Status
+cs2_pack_byte (cs2_t * s, SANE_Byte byte)
+{
+ while (s->send_buf_size <= s->n_send)
+ {
+ s->send_buf_size += 16;
+ s->send_buf =
+ (SANE_Byte *) cs2_xrealloc (s->send_buf, s->send_buf_size);
+ if (!s->send_buf)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->send_buf[s->n_send++] = byte;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_parse_cmd (cs2_t * s, char *text)
+{
+ size_t i, j;
+ char c, h;
+ SANE_Status status;
+
+ for (i = 0; i < strlen (text); i += 2)
+ if (text[i] == ' ')
+ i--; /* a bit dirty... advance by -1+2=1 */
+ else
+ {
+ if ((!isxdigit (text[i])) || (!isxdigit (text[i + 1])))
+ DBG (1, "BUG: cs2_parse_cmd(): Parser got invalid character.\n");
+ c = 0;
+ for (j = 0; j < 2; j++)
+ {
+ h = tolower (text[i + j]);
+ if ((h >= 'a') && (h <= 'f'))
+ c += 10 + h - 'a';
+ else
+ c += h - '0';
+ if (j == 0)
+ c <<= 4;
+ }
+ status = cs2_pack_byte (s, c);
+ if (status)
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_grow_send_buffer (cs2_t * s)
+{
+ if (s->n_send > s->send_buf_size)
+ {
+ s->send_buf_size = s->n_send;
+ s->send_buf =
+ (SANE_Byte *) cs2_xrealloc (s->send_buf, s->send_buf_size);
+ if (!s->send_buf)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_issue_cmd (cs2_t * s)
+{
+ SANE_Status status = SANE_STATUS_INVAL;
+ size_t n_data, n_status;
+ static SANE_Byte status_buf[8];
+ int status_only = 0;
+
+ DBG (20, "cs2_issue_cmd(): opcode = 0x%02x, n_send = %lu, n_recv = %lu.\n",
+ s->send_buf[0], (unsigned long) s->n_send, (unsigned long) s->n_recv);
+
+ s->status = CS2_STATUS_READY;
+
+ if (!s->n_cmd)
+ switch (s->send_buf[0])
+ {
+ case 0x00:
+ case 0x12:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x1a:
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0xc0:
+ case 0xc1:
+ s->n_cmd = 6;
+ break;
+ case 0x24:
+ case 0x25:
+ case 0x28:
+ case 0x2a:
+ case 0xe0:
+ case 0xe1:
+ s->n_cmd = 10;
+ break;
+ default:
+ DBG (1, "BUG: cs2_issue_cmd(): Unknown command opcode 0x%02x.\n",
+ s->send_buf[0]);
+ break;
+ }
+
+ if (s->n_send < s->n_cmd)
+ {
+ DBG (1,
+ "BUG: cs2_issue_cmd(): Negative number of data out bytes requested.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ n_data = s->n_send - s->n_cmd;
+ if (s->n_recv > 0)
+ {
+ if (n_data > 0)
+ {
+ DBG (1,
+ "BUG: cs2_issue_cmd(): Both data in and data out requested.\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ n_data = s->n_recv;
+ }
+ }
+
+ s->recv_buf = (SANE_Byte *) cs2_xrealloc (s->recv_buf, s->n_recv);
+ if (!s->recv_buf)
+ return SANE_STATUS_NO_MEM;
+
+ switch (s->interface)
+ {
+ case CS2_INTERFACE_UNKNOWN:
+ DBG (1,
+ "BUG: cs2_issue_cmd(): Unknown or uninitialized interface number.\n");
+ break;
+ case CS2_INTERFACE_SCSI:
+ sanei_scsi_cmd2 (s->fd, s->send_buf, s->n_cmd, s->send_buf + s->n_cmd,
+ s->n_send - s->n_cmd, s->recv_buf, &s->n_recv);
+ status = SANE_STATUS_GOOD;
+ break;
+ case CS2_INTERFACE_USB:
+ status = sanei_usb_write_bulk (s->fd, s->send_buf, &s->n_cmd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "Error: cs2_issue_cmd(): Could not write command.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ switch (cs2_phase_check (s))
+ {
+ case CS2_PHASE_OUT:
+ if (s->n_send - s->n_cmd < n_data || !n_data)
+ {
+ DBG (4, "Error: cs2_issue_cmd(): Unexpected data out phase.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ status =
+ sanei_usb_write_bulk (s->fd, s->send_buf + s->n_cmd, &n_data);
+ break;
+ case CS2_PHASE_IN:
+ if (s->n_recv < n_data || !n_data)
+ {
+ DBG (4, "Error: cs2_issue_cmd(): Unexpected data in phase.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ status = sanei_usb_read_bulk (s->fd, s->recv_buf, &n_data);
+ s->n_recv = n_data;
+ break;
+ case CS2_PHASE_NONE:
+ DBG (4, "Error: cs2_issue_cmd(): No command received!\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ if (n_data)
+ {
+ DBG (4,
+ "Error: cs2_issue_cmd(): Unexpected non-data phase, but n_data != 0.\n");
+ status_only = 1;
+ }
+ break;
+ }
+ n_status = 8;
+ status = sanei_usb_read_bulk (s->fd, status_buf, &n_status);
+ if (n_status != 8)
+ {
+ DBG (4,
+ "Error: cs2_issue_cmd(): Failed to read 8 status bytes from USB.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ s->sense_key = status_buf[1] & 0x0f;
+ s->sense_asc = status_buf[2] & 0xff;
+ s->sense_ascq = status_buf[3] & 0xff;
+ s->sense_info = status_buf[4] & 0xff;
+ cs2_parse_sense_data (s);
+ break;
+ }
+
+ if (status_only)
+ return SANE_STATUS_IO_ERROR;
+ else
+ return status;
+}
+
+static cs2_phase_t
+cs2_phase_check (cs2_t * s)
+{
+ static SANE_Byte phase_send_buf[1] = { 0xd0 }, phase_recv_buf[1];
+ SANE_Status status = 0;
+ size_t n = 1;
+
+ status = sanei_usb_write_bulk (s->fd, phase_send_buf, &n);
+ status |= sanei_usb_read_bulk (s->fd, phase_recv_buf, &n);
+
+ DBG (6, "cs2_phase_check(): Phase check returned phase = 0x%02x.\n",
+ phase_recv_buf[0]);
+
+ if (status)
+ return -1;
+ else
+ return phase_recv_buf[0];
+}
+
+static SANE_Status
+cs2_scanner_ready (cs2_t * s, int flags)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i = -1;
+ unsigned long count = 0;
+ int retry = 3;
+
+ do
+ {
+ if (i >= 0) /* dirty !!! */
+ usleep (500000);
+ cs2_init_buffer (s);
+ for (i = 0; i < 6; i++)
+ cs2_pack_byte (s, 0x00);
+ status = cs2_issue_cmd (s);
+ if (status)
+ if (--retry < 0)
+ return status;
+ if (++count > 240)
+ { /* 120s timeout */
+ DBG (4, "Error: cs2_scanner_ready(): Timeout expired.\n");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ }
+ while (s->status & ~flags); /* until all relevant bits are 0 */
+
+ return status;
+}
+
+static SANE_Status
+cs2_page_inquiry (cs2_t * s, int page)
+{
+ SANE_Status status;
+
+ size_t n;
+
+ if (page >= 0)
+ {
+
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "12 01");
+ cs2_pack_byte (s, page);
+ cs2_parse_cmd (s, "00 04 00");
+ s->n_recv = 4;
+ status = cs2_issue_cmd (s);
+ if (status)
+ {
+ DBG (4,
+ "Error: cs2_page_inquiry(): Inquiry of page size failed: %s.\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ n = s->recv_buf[3] + 4;
+
+ }
+ else
+ n = 36;
+
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ cs2_init_buffer (s);
+ if (page >= 0)
+ {
+ cs2_parse_cmd (s, "12 01");
+ cs2_pack_byte (s, page);
+ cs2_parse_cmd (s, "00");
+ }
+ else
+ cs2_parse_cmd (s, "12 00 00 00");
+ cs2_pack_byte (s, n);
+ cs2_parse_cmd (s, "00");
+ s->n_recv = n;
+ status = cs2_issue_cmd (s);
+ if (status)
+ {
+ DBG (4, "Error: cs2_page_inquiry(): Inquiry of page failed: %s.\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_full_inquiry (cs2_t * s)
+{
+ SANE_Status status;
+ int pitch, pitch_max;
+ cs2_pixel_t pixel;
+
+ status = cs2_page_inquiry (s, 0xc1);
+ if (status)
+ {
+ DBG (4, "Error: cs2_full_inquiry(): Failed to get page: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ s->maxbits = s->recv_buf[82];
+ if (s->type == CS2_TYPE_LS30) /* must be overridden, LS-30 claims to have 12 bits */
+ s->maxbits = 10;
+
+ s->n_lut = 1;
+ s->n_lut <<= s->maxbits;
+ s->lut_r =
+ (cs2_pixel_t *) cs2_xrealloc (s->lut_r, s->n_lut * sizeof (cs2_pixel_t));
+ s->lut_g =
+ (cs2_pixel_t *) cs2_xrealloc (s->lut_g, s->n_lut * sizeof (cs2_pixel_t));
+ s->lut_b =
+ (cs2_pixel_t *) cs2_xrealloc (s->lut_b, s->n_lut * sizeof (cs2_pixel_t));
+ s->lut_neutral =
+ (cs2_pixel_t *) cs2_xrealloc (s->lut_neutral,
+ s->n_lut * sizeof (cs2_pixel_t));
+
+ if (!s->lut_r || !s->lut_g || !s->lut_b || !s->lut_neutral)
+ {
+ cs2_xfree (s->lut_r);
+ cs2_xfree (s->lut_g);
+ cs2_xfree (s->lut_b);
+ cs2_xfree (s->lut_neutral);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_r[pixel] = s->lut_g[pixel] = s->lut_b[pixel] =
+ s->lut_neutral[pixel] = pixel;
+
+ s->resx_optical = 256 * s->recv_buf[18] + s->recv_buf[19];
+ s->resx_max = 256 * s->recv_buf[20] + s->recv_buf[21];
+ s->resx_min = 256 * s->recv_buf[22] + s->recv_buf[23];
+ s->boundaryx =
+ 65536 * (256 * s->recv_buf[36] + s->recv_buf[37]) +
+ 256 * s->recv_buf[38] + s->recv_buf[39];
+
+ s->resy_optical = 256 * s->recv_buf[40] + s->recv_buf[41];
+ s->resy_max = 256 * s->recv_buf[42] + s->recv_buf[43];
+ s->resy_min = 256 * s->recv_buf[44] + s->recv_buf[45];
+ s->boundaryy =
+ 65536 * (256 * s->recv_buf[58] + s->recv_buf[59]) +
+ 256 * s->recv_buf[60] + s->recv_buf[61];
+
+ s->focus_min = 256 * s->recv_buf[76] + s->recv_buf[77];
+ s->focus_max = 256 * s->recv_buf[78] + s->recv_buf[79];
+
+ s->n_frames = s->recv_buf[75];
+
+ s->frame_offset = s->resy_max * 1.5 + 1; /* works for LS-30, maybe not for others */
+
+ /* generate resolution list for x */
+ s->resx_n_list = pitch_max = floor (s->resx_max / (double) s->resx_min);
+ s->resx_list =
+ (unsigned int *) cs2_xrealloc (s->resx_list,
+ pitch_max * sizeof (unsigned int));
+ for (pitch = 1; pitch <= pitch_max; pitch++)
+ s->resx_list[pitch - 1] = s->resx_max / pitch;
+
+ /* generate resolution list for y */
+ s->resy_n_list = pitch_max = floor (s->resy_max / (double) s->resy_min);
+ s->resy_list =
+ (unsigned int *) cs2_xrealloc (s->resy_list,
+ pitch_max * sizeof (unsigned int));
+ for (pitch = 1; pitch <= pitch_max; pitch++)
+ s->resy_list[pitch - 1] = s->resy_max / pitch;
+
+ s->unit_dpi = s->resx_max;
+ s->unit_mm = 25.4 / s->unit_dpi;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_execute (cs2_t * s)
+{
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "c1 00 00 00 00 00");
+ return cs2_issue_cmd (s);
+}
+
+static SANE_Status
+cs2_load (cs2_t * s)
+{
+ SANE_Status status;
+
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "e0 00 d1 00 00 00 00 00 0d 00");
+ s->n_send += 13;
+ status = cs2_grow_send_buffer (s);
+ if (status)
+ return status;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ return cs2_execute (s);
+}
+
+static SANE_Status
+cs2_eject (cs2_t * s)
+{
+ SANE_Status status;
+
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "e0 00 d0 00 00 00 00 00 0d 00");
+ s->n_send += 13;
+ status = cs2_grow_send_buffer (s);
+ if (status)
+ return status;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ return cs2_execute (s);
+}
+
+static SANE_Status
+cs2_reset (cs2_t * s)
+{
+ SANE_Status status;
+
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "e0 00 80 00 00 00 00 00 0d 00");
+ s->n_send += 13;
+ status = cs2_grow_send_buffer (s);
+ if (status)
+ return status;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ return cs2_execute (s);
+}
+
+static SANE_Status
+cs2_focus (cs2_t * s)
+{
+ SANE_Status status;
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "e0 00 c1 00 00 00 00 00 0d 00 00");
+ cs2_pack_byte (s, (s->focus >> 24) & 0xff);
+ cs2_pack_byte (s, (s->focus >> 16) & 0xff);
+ cs2_pack_byte (s, (s->focus >> 8) & 0xff);
+ cs2_pack_byte (s, s->focus & 0xff);
+ cs2_parse_cmd (s, "00 00 00 00 00 00 00 00");
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ return cs2_execute (s);
+}
+
+static SANE_Status
+cs2_autofocus (cs2_t * s)
+{
+ SANE_Status status;
+
+ cs2_convert_options (s);
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "e0 00 a0 00 00 00 00 00 0d 00 00");
+ cs2_pack_byte (s, (s->real_focusx >> 24) & 0xff);
+ cs2_pack_byte (s, (s->real_focusx >> 16) & 0xff);
+ cs2_pack_byte (s, (s->real_focusx >> 8) & 0xff);
+ cs2_pack_byte (s, s->real_focusx & 0xff);
+ cs2_pack_byte (s, (s->real_focusy >> 24) & 0xff);
+ cs2_pack_byte (s, (s->real_focusy >> 16) & 0xff);
+ cs2_pack_byte (s, (s->real_focusy >> 8) & 0xff);
+ cs2_pack_byte (s, s->real_focusy & 0xff);
+ cs2_parse_cmd (s, "00 00 00 00");
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ status = cs2_execute (s);
+ if (status)
+ return status;
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "e1 00 c1 00 00 00 00 00 0d 00");
+ s->n_recv = 13;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ s->focus =
+ 65536 * (256 * s->recv_buf[1] + s->recv_buf[2]) + 256 * s->recv_buf[3] +
+ s->recv_buf[4];
+
+ return status;
+}
+
+static SANE_Status
+cs2_get_exposure (cs2_t * s)
+{
+ SANE_Status status;
+ int i_colour;
+
+ for (i_colour = 0; i_colour < 3; i_colour++)
+ { /* XXXXXXXXXXXXX CCCCCCCCCCCCC */
+ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "25 01 00 00 00");
+ cs2_pack_byte (s, cs2_colour_list[i_colour]);
+ cs2_parse_cmd (s, "00 00 3a 00");
+ s->n_recv = 58;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ s->real_exposure[cs2_colour_list[i_colour]] =
+ 65536 * (256 * s->recv_buf[54] + s->recv_buf[55]) +
+ 256 * s->recv_buf[56] + s->recv_buf[57];
+
+ DBG (6, "cs2_get_exposure(): exposure for colour %i: %li * 10ns\n", cs2_colour_list[i_colour], s->real_exposure[cs2_colour_list[i_colour]]);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_convert_options (cs2_t * s)
+{
+ int i_colour;
+ unsigned long xmin, xmax, ymin, ymax;
+ SANE_Byte *infrared_buf_new;
+
+ s->real_depth = (s->preview ? 8 : s->depth);
+ s->bytes_per_pixel = (s->real_depth > 8 ? 2 : 1);
+ s->shift_bits = 8 * s->bytes_per_pixel - s->real_depth;
+
+ if (s->preview)
+ {
+ s->real_resx = s->res_preview;
+ s->real_resy = s->res_preview;
+ }
+ else if (s->res_independent)
+ {
+ s->real_resx = s->resx;
+ s->real_resy = s->resy;
+ }
+ else
+ {
+ s->real_resx = s->res;
+ s->real_resy = s->res;
+ }
+ s->real_pitchx = s->resx_max / s->real_resx;
+ s->real_pitchy = s->resy_max / s->real_resy;
+
+ s->real_resx = s->resx_max / s->real_pitchx;
+ s->real_resy = s->resy_max / s->real_pitchy;
+
+ /* The prefix "real_" refers to data in device units (1/maxdpi), "logical_" refers to resolution-dependent data. */
+
+ if (s->xmin < s->xmax)
+ {
+ xmin = s->xmin;
+ xmax = s->xmax;
+ }
+ else
+ {
+ xmin = s->xmax;
+ xmax = s->xmin;
+ }
+
+ if (s->ymin < s->ymax)
+ {
+ ymin = s->ymin;
+ ymax = s->ymax;
+ }
+ else
+ {
+ ymin = s->ymax;
+ ymax = s->ymin;
+ }
+
+ s->real_xoffset = xmin;
+ s->real_yoffset =
+ ymin + (s->i_frame - 1) * s->frame_offset + s->subframe / s->unit_mm;
+ s->logical_width = (xmax - xmin + 1) / s->real_pitchx; /* XXXXXXXXX use mm units */
+ s->logical_height = (ymax - ymin + 1) / s->real_pitchy;
+ s->real_width = s->logical_width * s->real_pitchx;
+ s->real_height = s->logical_height * s->real_pitchy;
+
+ s->odd_padding = 0;
+ if ((s->bytes_per_pixel == 1) && (s->logical_width & 0x01)
+ && (s->type != CS2_TYPE_LS30) && (s->type != CS2_TYPE_LS2000))
+ s->odd_padding = 1;
+
+ if (s->focus_on_centre)
+ {
+ s->real_focusx = s->real_xoffset + s->real_width / 2;
+ s->real_focusy = s->real_yoffset + s->real_height / 2;
+ }
+ else
+ {
+ s->real_focusx = s->focusx;
+ s->real_focusy =
+ s->focusy + (s->i_frame - 1) * s->frame_offset +
+ s->subframe / s->unit_mm;
+ }
+
+ s->real_exposure[1] = s->exposure * s->exposure_r * 100.;
+ s->real_exposure[2] = s->exposure * s->exposure_g * 100.;
+ s->real_exposure[3] = s->exposure * s->exposure_b * 100.;
+
+ for (i_colour = 0; i_colour < 3; i_colour++)
+ if (s->real_exposure[cs2_colour_list[i_colour]] < 1)
+ s->real_exposure[cs2_colour_list[i_colour]] = 1;
+
+ s->n_colour_out = s->n_colour_in = 3; /* XXXXXXXXXXXXXX CCCCCCCCCCCCCC */
+
+ s->xfer_bytes_total =
+ s->bytes_per_pixel * s->n_colour_out * s->logical_width *
+ s->logical_height;
+
+ if (s->preview)
+ s->infrared_stage = s->infrared_next = CS2_INFRARED_OFF;
+ else
+ {
+ if ((s->infrared) && (s->infrared_stage == CS2_INFRARED_OFF))
+ s->infrared_next = CS2_INFRARED_IN;
+
+ s->infrared_stage = s->infrared_next;
+
+ if (s->infrared)
+ {
+ s->n_colour_in ++;
+ s->n_infrared_buf =
+ s->bytes_per_pixel * s->logical_width * s->logical_height;
+ infrared_buf_new =
+ (SANE_Byte *) cs2_xrealloc (s->infrared_buf, s->n_infrared_buf);
+ if (infrared_buf_new)
+ s->infrared_buf = infrared_buf_new;
+ else
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_set_boundary (cs2_t *s)
+{
+ SANE_Status status;
+ int i_boundary;
+ unsigned long lvalue;
+
+/* Ariel - Check this function */
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "2a 00 88 00 00 03");
+ cs2_pack_byte (s, ((4 + s->n_frames * 16) >> 16) & 0xff);
+ cs2_pack_byte (s, ((4 + s->n_frames * 16) >> 8) & 0xff);
+ cs2_pack_byte (s, (4 + s->n_frames * 16) & 0xff);
+ cs2_parse_cmd (s, "00");
+
+ cs2_pack_byte (s, ((4 + s->n_frames * 16) >> 8) & 0xff);
+ cs2_pack_byte (s, (4 + s->n_frames * 16) & 0xff);
+ cs2_pack_byte (s, s->n_frames);
+ cs2_pack_byte (s, s->n_frames);
+ for (i_boundary = 0; i_boundary < s->n_frames; i_boundary++)
+ {
+ lvalue = s->frame_offset * i_boundary + s->subframe / s->unit_mm;
+ cs2_pack_byte (s, (lvalue >> 24) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 16) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 8) & 0xff);
+ cs2_pack_byte (s, lvalue & 0xff);
+
+ lvalue = 0;
+ cs2_pack_byte (s, (lvalue >> 24) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 16) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 8) & 0xff);
+ cs2_pack_byte (s, lvalue & 0xff);
+
+ lvalue = s->frame_offset * i_boundary + s->subframe / s->unit_mm + s->frame_offset - 1;
+ cs2_pack_byte (s, (lvalue >> 24) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 16) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 8) & 0xff);
+ cs2_pack_byte (s, lvalue & 0xff);
+
+ lvalue = s->boundaryx - 1;
+ cs2_pack_byte (s, (lvalue >> 24) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 16) & 0xff);
+ cs2_pack_byte (s, (lvalue >> 8) & 0xff);
+ cs2_pack_byte (s, lvalue & 0xff);
+ }
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs2_scan (cs2_t * s, cs2_scan_t type)
+{
+ SANE_Status status;
+ int i_colour;
+ cs2_pixel_t pixel;
+ cs2_pixel_t *lut;
+
+ /* wait for device to be ready with document, and set device unit */
+
+ status = cs2_scanner_ready (s, CS2_STATUS_NO_DOCS);
+ if (status)
+ return status;
+ if (s->status & CS2_STATUS_NO_DOCS)
+ return SANE_STATUS_NO_DOCS;
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ /* Ariel - the '0b' byte in the 'else' part seems to be wrong, should be 0 */
+ if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000))
+ cs2_parse_cmd (s, "15 10 00 00 14 00 00 00 00 08 00 00 00 00 00 00 00 01 03 06 00 00");
+ else
+ cs2_parse_cmd (s, "15 10 00 00 0c 00 0b 00 00 00 03 06 00 00");
+ cs2_pack_byte (s, (s->unit_dpi >> 8) & 0xff);
+ cs2_pack_byte (s, s->unit_dpi & 0xff);
+ cs2_parse_cmd (s, "00 00");
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+
+ status = cs2_convert_options (s);
+ if (status)
+ return status;
+
+ /* Ariel - Is this the best place to initialize it? */
+ s->block_padding = 0;
+
+ status = cs2_set_boundary (s);
+ if (status)
+ return status;
+
+ switch (type)
+ {
+ case CS2_SCAN_NORMAL:
+
+ for (i_colour = 0; i_colour < s->n_colour_in; i_colour++)
+ {
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+
+ switch (i_colour)
+ {
+ case 0:
+ lut = s->lut_r;
+ break;
+ case 1:
+ lut = s->lut_g;
+ break;
+ case 2:
+ lut = s->lut_b;
+ break;
+ case 3:
+ lut = s->lut_neutral;
+ break;
+ default:
+ DBG (1,
+ "BUG: cs2_scan(): Unknown colour number for LUT download.\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "2a 00 03 00");
+ cs2_pack_byte (s, cs2_colour_list[i_colour]);
+ cs2_pack_byte (s, 2 - 1); /* XXXXXXXXXX number of bytes per data point - 1 */
+ cs2_pack_byte (s, ((2 * s->n_lut) >> 16) & 0xff); /* XXXXXXXXXX 2 bytes per point */
+ cs2_pack_byte (s, ((2 * s->n_lut) >> 8) & 0xff); /* XXXXXXXXXX 2 bytes per point */
+ cs2_pack_byte (s, (2 * s->n_lut) & 0xff); /* XXXXXXXXXX 2 bytes per point */
+ cs2_pack_byte (s, 0x00);
+
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ { /* XXXXXXXXXXXXXXX 2 bytes per point */
+ cs2_pack_byte (s, (lut[pixel] >> 8) & 0xff);
+ cs2_pack_byte (s, lut[pixel] & 0xff);
+ }
+
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ for (i_colour = 0; i_colour < s->n_colour_in; i_colour++)
+ {
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+
+ cs2_init_buffer (s);
+ if ((s->type == CS2_TYPE_LS40) || (s->type == CS2_TYPE_LS4000))
+ cs2_parse_cmd (s, "24 00 00 00 00 00 00 00 3a 80");
+ else
+ cs2_parse_cmd (s, "24 00 00 00 00 00 00 00 3a 00");
+ cs2_parse_cmd (s, "00 00 00 00 00 00 00 32");
+
+ cs2_pack_byte (s, cs2_colour_list[i_colour]);
+
+ cs2_pack_byte (s, 0x00);
+
+ cs2_pack_byte (s, s->real_resx >> 8);
+ cs2_pack_byte (s, s->real_resx & 0xff);
+ cs2_pack_byte (s, s->real_resy >> 8);
+ cs2_pack_byte (s, s->real_resy & 0xff);
+
+ cs2_pack_byte (s, (s->real_xoffset >> 24) & 0xff);
+ cs2_pack_byte (s, (s->real_xoffset >> 16) & 0xff);
+ cs2_pack_byte (s, (s->real_xoffset >> 8) & 0xff);
+ cs2_pack_byte (s, s->real_xoffset & 0xff);
+
+ cs2_pack_byte (s, (s->real_yoffset >> 24) & 0xff);
+ cs2_pack_byte (s, (s->real_yoffset >> 16) & 0xff);
+ cs2_pack_byte (s, (s->real_yoffset >> 8) & 0xff);
+ cs2_pack_byte (s, s->real_yoffset & 0xff);
+
+ cs2_pack_byte (s, (s->real_width >> 24) & 0xff);
+ cs2_pack_byte (s, (s->real_width >> 16) & 0xff);
+ cs2_pack_byte (s, (s->real_width >> 8) & 0xff);
+ cs2_pack_byte (s, s->real_width & 0xff);
+
+ cs2_pack_byte (s, (s->real_height >> 24) & 0xff);
+ cs2_pack_byte (s, (s->real_height >> 16) & 0xff);
+ cs2_pack_byte (s, (s->real_height >> 8) & 0xff);
+ cs2_pack_byte (s, s->real_height & 0xff);
+
+ cs2_pack_byte (s, 0x00); /* brightness, etc. */
+ cs2_pack_byte (s, 0x00);
+ cs2_pack_byte (s, 0x00);
+ cs2_pack_byte (s, 0x05); /* image composition CCCCCCC */
+ cs2_pack_byte (s, s->real_depth); /* pixel composition */
+ cs2_parse_cmd (s, "00 00 00 00 00 00 00 00 00 00 00 00 00");
+ cs2_pack_byte (s, ((s->samples_per_scan - 1) << 4) + 0x00); /* multiread, ordering */
+ /* No need to use an undocumented bit in LS50 */
+ if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000))
+ cs2_pack_byte (s, 0x00 + (s->negative ? 0 : 1)); /* averaging, pos/neg */
+ else
+ cs2_pack_byte (s, 0x80 + (s->negative ? 0 : 1)); /* averaging, pos/neg */
+
+ switch (type)
+ { /* scanning kind */
+ case CS2_SCAN_NORMAL:
+ cs2_pack_byte (s, 0x01);
+ break;
+ case CS2_SCAN_AE:
+ cs2_pack_byte (s, 0x20);
+ break;
+ case CS2_SCAN_AE_WB:
+ cs2_pack_byte (s, 0x40);
+ break;
+ default:
+ DBG (1, "BUG: cs2_scan(): Unknown scanning type.\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->samples_per_scan == 1)
+ cs2_pack_byte (s, 0x02); /* scanning mode single */
+ else
+ cs2_pack_byte (s, 0x10); /* scanning mode multi */
+ cs2_pack_byte (s, 0x02); /* colour interleaving */
+ cs2_pack_byte (s, 0xff); /* (ae) */
+ if (i_colour == 3) /* infrared */
+ cs2_parse_cmd (s, "00 00 00 00"); /* automatic */
+ else
+ {
+ cs2_pack_byte (s,
+ (s->
+ real_exposure[cs2_colour_list[i_colour]] >> 24) &
+ 0xff);
+ cs2_pack_byte (s,
+ (s->
+ real_exposure[cs2_colour_list[i_colour]] >> 16) &
+ 0xff);
+ cs2_pack_byte (s,
+ (s->
+ real_exposure[cs2_colour_list[i_colour]] >> 8) &
+ 0xff);
+ cs2_pack_byte (s,
+ s->real_exposure[cs2_colour_list[i_colour]] & 0xff);
+ }
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+ }
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_focus (s);
+
+ cs2_scanner_ready (s, CS2_STATUS_READY);
+ cs2_init_buffer (s);
+ switch (s->n_colour_in)
+ {
+ case 3:
+ cs2_parse_cmd (s, "1b 00 00 00 03 00 01 02 03");
+ break;
+ case 4:
+ cs2_parse_cmd (s, "1b 00 00 00 04 00 01 02 03 09");
+ break;
+ default:
+ DBG (1, "BUG: cs2_scan(): Unknown number of input colours.\n");
+ break;
+ }
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+ if (s->status == CS2_STATUS_REISSUE)
+ {
+ /* Make sure we don't affect the behaviour for other scanners */
+ if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000))
+ {
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "28 00 87 00 00 00 00 00 06 00");
+ s->n_recv = 6;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+ cs2_init_buffer (s);
+ cs2_parse_cmd (s, "28 00 87 00 00 00 00 00");
+ cs2_pack_byte (s, s->recv_buf[5] + 6);
+ cs2_parse_cmd (s, "00");
+ s->n_recv = s->recv_buf[5] + 6;
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+ if ((s->recv_buf[11] != 0x08) || (s->recv_buf[12] != 0x00))
+ DBG (1, "BUG: cs2_scan(): Unexpected block_padding position.\n");
+ s->block_padding = 256 * s->recv_buf[19] + s->recv_buf[20];
+ cs2_init_buffer (s);
+ switch (s->n_colour_in)
+ {
+ case 3:
+ cs2_parse_cmd (s, "1b 00 00 00 03 00 01 02 03");
+ break;
+ case 4:
+ cs2_parse_cmd (s, "1b 00 00 00 04 00 01 02 03 09");
+ break;
+ }
+ }
+ status = cs2_issue_cmd (s);
+ if (status)
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void *
+cs2_xmalloc (size_t size)
+{
+ register void *value = malloc (size);
+
+ if (!value)
+ DBG (0, "Error: cs2_xmalloc(): Failed to malloc() %lu bytes.\n",
+ (unsigned long) size);
+
+ return value;
+}
+
+static void *
+cs2_xrealloc (void *p, size_t size)
+{
+ register void *value;
+
+ if (!size)
+ return p;
+
+ value = realloc (p, size);
+
+ if (!value)
+ DBG (0, "Error: cs2_xrealloc(): Failed to realloc() %lu bytes.\n",
+ (unsigned long) size);
+
+ return value;
+}
+
+static void
+cs2_xfree (const void *p)
+{
+ if (p)
+ free ((void *) p);
+}
diff --git a/backend/coolscan2.conf.in b/backend/coolscan2.conf.in
new file mode 100644
index 0000000..45b9a7d
--- /dev/null
+++ b/backend/coolscan2.conf.in
@@ -0,0 +1,20 @@
+# coolscan2.conf: sample configuration file for coolscan2 backend
+#
+# The following entrie checks for your scanner by manufacturer (SCSI)
+# and by vendor and product ID (USB). This is what the backend does when
+# no configuration file can be found.
+#
+auto
+
+# You can also configure the backend for specific device files, but this
+# should not normally be necessary (under Linux at least).
+# Syntax for specific devices: <interface>:<device>
+#
+# For a SCSI scanner, uncomment and edit the following line:
+#scsi:/dev/scanner
+#
+# For a USB scanner, uncomment and edit the following line:
+#usb:/dev/usbscanner
+#
+# For an IEEE 1394 scanner, use the SBP2 protocol (under Linux, use the
+# sbp2 kernel module), and your scanner will be handled as a SCSI device.
diff --git a/backend/coolscan3.c b/backend/coolscan3.c
new file mode 100644
index 0000000..a1d6fe6
--- /dev/null
+++ b/backend/coolscan3.c
@@ -0,0 +1,3188 @@
+/*
+ * SANE - Scanner Access Now Easy.
+ * coolscan3.c
+ *
+ * This file implements a SANE backend for Nikon Coolscan film scanners.
+ *
+ * coolscan3.c is based on coolscan2.c, a work of András Major, Ariel Garcia
+ * and Giuseppe Sacco.
+ *
+ * Copyright (C) 2007-08 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+/* ========================================================================= */
+
+#include "../include/sane/config.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_config.h"
+
+#define BACKEND_NAME coolscan3
+#include "../include/sane/sanei_backend.h" /* must be last */
+
+#define CS3_VERSION_MAJOR 1
+#define CS3_VERSION_MINOR 0
+#define CS3_REVISION 0
+#define CS3_CONFIG_FILE "coolscan3.conf"
+
+#define WSIZE (sizeof (SANE_Word))
+
+
+/* ========================================================================= */
+/* typedefs */
+
+typedef enum
+{
+ CS3_TYPE_UNKOWN,
+ CS3_TYPE_LS30,
+ CS3_TYPE_LS40,
+ CS3_TYPE_LS50,
+ CS3_TYPE_LS2000,
+ CS3_TYPE_LS4000,
+ CS3_TYPE_LS5000,
+ CS3_TYPE_LS8000
+}
+cs3_type_t;
+
+typedef enum
+{
+ CS3_INTERFACE_UNKNOWN,
+ CS3_INTERFACE_SCSI, /* includes IEEE1394 via SBP2 */
+ CS3_INTERFACE_USB
+}
+cs3_interface_t;
+
+typedef enum
+{
+ CS3_PHASE_NONE = 0x00,
+ CS3_PHASE_STATUS = 0x01,
+ CS3_PHASE_OUT = 0x02,
+ CS3_PHASE_IN = 0x03,
+ CS3_PHASE_BUSY = 0x04
+}
+cs3_phase_t;
+
+typedef enum
+{
+ CS3_SCAN_NORMAL,
+ CS3_SCAN_AE,
+ CS3_SCAN_AE_WB
+}
+cs3_scan_t;
+
+typedef enum
+{
+ CS3_STATUS_READY = 0,
+ CS3_STATUS_BUSY = 1,
+ CS3_STATUS_NO_DOCS = 2,
+ CS3_STATUS_PROCESSING = 4,
+ CS3_STATUS_ERROR = 8,
+ CS3_STATUS_REISSUE = 16,
+ CS3_STATUS_ALL = 31 /* sum of all others */
+}
+cs3_status_t;
+
+typedef enum
+{
+ CS3_OPTION_NUM = 0,
+
+ CS3_OPTION_PREVIEW,
+
+ CS3_OPTION_NEGATIVE,
+
+ CS3_OPTION_INFRARED,
+
+ CS3_OPTION_SAMPLES_PER_SCAN,
+
+ CS3_OPTION_DEPTH,
+
+ CS3_OPTION_EXPOSURE,
+ CS3_OPTION_EXPOSURE_R,
+ CS3_OPTION_EXPOSURE_G,
+ CS3_OPTION_EXPOSURE_B,
+ CS3_OPTION_SCAN_AE,
+ CS3_OPTION_SCAN_AE_WB,
+
+ CS3_OPTION_LUT_R,
+ CS3_OPTION_LUT_G,
+ CS3_OPTION_LUT_B,
+
+ CS3_OPTION_RES,
+ CS3_OPTION_RESX,
+ CS3_OPTION_RESY,
+ CS3_OPTION_RES_INDEPENDENT,
+
+ CS3_OPTION_PREVIEW_RESOLUTION,
+
+ CS3_OPTION_FRAME,
+ CS3_OPTION_FRAME_COUNT,
+ CS3_OPTION_SUBFRAME,
+ CS3_OPTION_XMIN,
+ CS3_OPTION_XMAX,
+ CS3_OPTION_YMIN,
+ CS3_OPTION_YMAX,
+
+ CS3_OPTION_LOAD,
+ CS3_OPTION_AUTOLOAD,
+ CS3_OPTION_EJECT,
+ CS3_OPTION_RESET,
+
+ CS3_OPTION_FOCUS_ON_CENTRE,
+ CS3_OPTION_FOCUS,
+ CS3_OPTION_AUTOFOCUS,
+ CS3_OPTION_FOCUSX,
+ CS3_OPTION_FOCUSY,
+
+ CS3_N_OPTIONS /* must be last -- counts number of enum items */
+}
+cs3_option_t;
+
+typedef unsigned int cs3_pixel_t;
+
+#define CS3_COLOR_MAX 10 /* 9 + 1, see cs3_colors */
+
+/* Given that there is no way to give scanner vendor
+ * and model to the calling software, I have to use
+ * an ugly hack here. :( That's very sad. Suggestions
+ * that can provide the same features are appreciated.
+ */
+
+#ifndef SANE_COOKIE
+#define SANE_COOKIE 0x0BADCAFE
+
+struct SANE_Cookie
+{
+ uint16_t version;
+ const char *vendor;
+ const char *model;
+ const char *revision;
+};
+#endif
+
+typedef struct
+{
+ /* magic bits :( */
+ uint32_t magic;
+ struct SANE_Cookie *cookie_ptr;
+ struct SANE_Cookie cookie;
+
+ /* interface */
+ cs3_interface_t interface;
+ int fd;
+ SANE_Byte *send_buf, *recv_buf;
+ size_t send_buf_size, recv_buf_size;
+ size_t n_cmd, n_send, n_recv;
+
+ /* device characteristics */
+ char vendor_string[9], product_string[17], revision_string[5];
+ cs3_type_t type;
+ int maxbits;
+ unsigned int resx_optical, resx_min, resx_max, *resx_list,
+ resx_n_list;
+ unsigned int resy_optical, resy_min, resy_max, *resy_list,
+ resy_n_list;
+ unsigned long boundaryx, boundaryy;
+ unsigned long frame_offset;
+ unsigned int unit_dpi;
+ double unit_mm;
+ int n_frames;
+
+ int focus_min, focus_max;
+
+ /* settings */
+ SANE_Bool preview, negative, infrared, autoload, autofocus, ae, aewb;
+ int samples_per_scan, depth, real_depth, bytes_per_pixel, shift_bits,
+ n_colors;
+ cs3_pixel_t n_lut;
+ cs3_pixel_t *lut_r, *lut_g, *lut_b, *lut_neutral;
+ unsigned long resx, resy, res, res_independent, res_preview;
+ unsigned long xmin, xmax, ymin, ymax;
+ int i_frame, frame_count;
+ double subframe;
+
+ unsigned int real_resx, real_resy, real_pitchx, real_pitchy;
+ unsigned long real_xoffset, real_yoffset, real_width, real_height,
+ logical_width, logical_height;
+ int odd_padding;
+ int block_padding;
+
+ double exposure, exposure_r, exposure_g, exposure_b;
+ unsigned long real_exposure[CS3_COLOR_MAX];
+
+
+ SANE_Bool focus_on_centre;
+ unsigned long focusx, focusy, real_focusx, real_focusy;
+ int focus;
+
+ /* status */
+ SANE_Bool scanning;
+ SANE_Byte *line_buf;
+ ssize_t n_line_buf, i_line_buf;
+ unsigned long sense_key, sense_asc, sense_ascq, sense_info;
+ unsigned long sense_code;
+ cs3_status_t status;
+ size_t xfer_position, xfer_bytes_total;
+
+ /* SANE stuff */
+ SANE_Option_Descriptor option_list[CS3_N_OPTIONS];
+}
+cs3_t;
+
+
+/* ========================================================================= */
+/* prototypes */
+
+static SANE_Status cs3_open(const char *device, cs3_interface_t interface,
+ cs3_t ** sp);
+static void cs3_close(cs3_t * s);
+static SANE_Status cs3_attach(const char *dev);
+static SANE_Status cs3_scsi_sense_handler(int fd, u_char * sense_buffer,
+ void *arg);
+static SANE_Status cs3_parse_sense_data(cs3_t * s);
+static void cs3_init_buffer(cs3_t * s);
+static SANE_Status cs3_pack_byte(cs3_t * s, SANE_Byte byte);
+static void cs3_pack_long(cs3_t * s, unsigned long val);
+static void cs3_pack_word(cs3_t * s, unsigned long val);
+static SANE_Status cs3_parse_cmd(cs3_t * s, char *text);
+static SANE_Status cs3_grow_send_buffer(cs3_t * s);
+static SANE_Status cs3_issue_cmd(cs3_t * s);
+static cs3_phase_t cs3_phase_check(cs3_t * s);
+static SANE_Status cs3_set_boundary(cs3_t * s);
+static SANE_Status cs3_scanner_ready(cs3_t * s, int flags);
+static SANE_Status cs3_page_inquiry(cs3_t * s, int page);
+static SANE_Status cs3_full_inquiry(cs3_t * s);
+static SANE_Status cs3_mode_select(cs3_t * s);
+static SANE_Status cs3_reserve_unit(cs3_t * s);
+static SANE_Status cs3_release_unit(cs3_t * s);
+static SANE_Status cs3_execute(cs3_t * s);
+static SANE_Status cs3_load(cs3_t * s);
+static SANE_Status cs3_eject(cs3_t * s);
+static SANE_Status cs3_reset(cs3_t * s);
+static SANE_Status cs3_set_focus(cs3_t * s);
+static SANE_Status cs3_autofocus(cs3_t * s);
+static SANE_Status cs3_autoexposure(cs3_t * s, int wb);
+static SANE_Status cs3_get_exposure(cs3_t * s);
+static SANE_Status cs3_set_window(cs3_t * s, cs3_scan_t type);
+static SANE_Status cs3_convert_options(cs3_t * s);
+static SANE_Status cs3_scan(cs3_t * s, cs3_scan_t type);
+static void *cs3_xmalloc(size_t size);
+static void *cs3_xrealloc(void *p, size_t size);
+static void cs3_xfree(const void *p);
+
+
+/* ========================================================================= */
+/* global variables */
+
+static int cs3_colors[] = { 1, 2, 3, 9 };
+
+static SANE_Device **device_list = NULL;
+static int n_device_list = 0;
+static cs3_interface_t try_interface = CS3_INTERFACE_UNKNOWN;
+static int open_devices = 0;
+
+
+/* ========================================================================= */
+/* SANE entry points */
+
+SANE_Status
+sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT();
+ DBG(1, "coolscan3 backend, version %i.%i.%i initializing.\n",
+ CS3_VERSION_MAJOR, CS3_VERSION_MINOR, CS3_REVISION);
+
+ authorize = authorize; /* to shut up compiler */
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ sanei_usb_init();
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit(void)
+{
+ int i;
+
+ DBG(10, "%s\n", __func__);
+
+ for (i = 0; i < n_device_list; i++) {
+ cs3_xfree(device_list[i]->name);
+ cs3_xfree(device_list[i]->vendor);
+ cs3_xfree(device_list[i]->model);
+ cs3_xfree(device_list[i]);
+ }
+ cs3_xfree(device_list);
+}
+
+SANE_Status
+sane_get_devices(const SANE_Device *** list, SANE_Bool local_only)
+{
+ char line[PATH_MAX], *p;
+ FILE *config;
+
+ local_only = local_only; /* to shut up compiler */
+
+ DBG(10, "%s\n", __func__);
+
+ if (device_list)
+ DBG(6,
+ "sane_get_devices(): Device list already populated, not probing again.\n");
+ else {
+ if (open_devices) {
+ DBG(4,
+ "sane_get_devices(): Devices open, not scanning for scanners.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ config = sanei_config_open(CS3_CONFIG_FILE);
+ if (config) {
+ DBG(4, "sane_get_devices(): Reading config file.\n");
+ while (sanei_config_read(line, sizeof(line), config)) {
+ p = line;
+ p += strspn(line, " \t");
+ if (strlen(p) && (p[0] != '\n')
+ && (p[0] != '#'))
+ cs3_open(line, CS3_INTERFACE_UNKNOWN,
+ NULL);
+ }
+ fclose(config);
+ } else {
+ DBG(4, "sane_get_devices(): No config file found.\n");
+ cs3_open("auto", CS3_INTERFACE_UNKNOWN, NULL);
+ }
+
+ DBG(6, "%s: %i device(s) detected.\n",
+ __func__, n_device_list);
+ }
+
+ *list = (const SANE_Device **) device_list;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle * h)
+{
+ SANE_Status status;
+ cs3_t *s;
+ int i_option;
+ unsigned int i_list;
+ SANE_Option_Descriptor o;
+ SANE_Word *word_list;
+ SANE_Range *range = NULL;
+ int alloc_failed = 0;
+
+ DBG(10, "%s\n", __func__);
+
+ status = cs3_open(name, CS3_INTERFACE_UNKNOWN, &s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ *h = (SANE_Handle) s;
+
+ /* get device properties */
+
+ s->lut_r = s->lut_g = s->lut_b = s->lut_neutral = NULL;
+ s->resx_list = s->resy_list = NULL;
+ s->resx_n_list = s->resy_n_list = 0;
+
+ status = cs3_full_inquiry(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = cs3_mode_select(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* option descriptors */
+
+ for (i_option = 0; i_option < CS3_N_OPTIONS; i_option++) {
+ o.name = o.title = o.desc = NULL;
+ o.type = o.unit = o.cap = o.constraint_type = o.size = 0;
+ o.constraint.range = NULL; /* only one union member needs to be NULLed */
+ switch (i_option) {
+ case CS3_OPTION_NUM:
+ o.name = "";
+ o.title = SANE_TITLE_NUM_OPTIONS;
+ o.desc = SANE_DESC_NUM_OPTIONS;
+ o.type = SANE_TYPE_INT;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_DETECT;
+ break;
+ case CS3_OPTION_PREVIEW:
+ o.name = "preview";
+ o.title = "Preview mode";
+ o.desc = "Preview mode";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_ADVANCED;
+ break;
+ case CS3_OPTION_NEGATIVE:
+ o.name = "negative";
+ o.title = "Negative";
+ o.desc = "Negative film: make scanner invert colors";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ /*o.cap |= SANE_CAP_INACTIVE; */
+ break;
+
+ case CS3_OPTION_INFRARED:
+ o.name = "infrared";
+ o.title = "Read infrared channel";
+ o.desc = "Read infrared channel in addition to scan colors";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+#ifndef SANE_FRAME_RGBI
+ o.cap |= SANE_CAP_INACTIVE;
+#endif
+ break;
+
+ case CS3_OPTION_SAMPLES_PER_SCAN:
+ o.name = "samples-per-scan";
+ o.title = "Samples per Scan";
+ o.desc = "Number of samples per scan";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->type != CS3_TYPE_LS2000 && s->type != CS3_TYPE_LS4000
+ && s->type != CS3_TYPE_LS5000 && s->type != CS3_TYPE_LS8000)
+ o.cap |= SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *) cs3_xmalloc (sizeof (SANE_Range));
+ if (! range)
+ alloc_failed = 1;
+ else
+ {
+ range->min = 1;
+ range->max = 16;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+
+ case CS3_OPTION_DEPTH:
+ o.name = "depth";
+ o.title = "Bit depth per channel";
+ o.desc = "Number of bits output by scanner for each channel";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ word_list =
+ (SANE_Word *) cs3_xmalloc(2 *
+ sizeof(SANE_Word));
+ if (!word_list)
+ alloc_failed = 1;
+ else {
+ word_list[1] = 8;
+ word_list[2] = s->maxbits;
+ word_list[0] = 2;
+ o.constraint.word_list = word_list;
+ }
+ break;
+ case CS3_OPTION_EXPOSURE:
+ o.name = "exposure";
+ o.title = "Exposure multiplier";
+ o.desc = "Exposure multiplier for all channels";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = SANE_FIX(0.);
+ range->max = SANE_FIX(10.);
+ range->quant = SANE_FIX(0.1);
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_EXPOSURE_R:
+ o.name = "red-exposure";
+ o.title = "Red exposure time";
+ o.desc = "Exposure time for red channel";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MICROSECOND;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = SANE_FIX(50.);
+ range->max = SANE_FIX(20000.);
+ range->quant = SANE_FIX(10.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_EXPOSURE_G:
+ o.name = "green-exposure";
+ o.title = "Green exposure time";
+ o.desc = "Exposure time for green channel";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MICROSECOND;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = SANE_FIX(50.);
+ range->max = SANE_FIX(20000.);
+ range->quant = SANE_FIX(10.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_EXPOSURE_B:
+ o.name = "blue-exposure";
+ o.title = "Blue exposure time";
+ o.desc = "Exposure time for blue channel";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MICROSECOND;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = SANE_FIX(50.);
+ range->max = SANE_FIX(20000.);
+ range->quant = SANE_FIX(10.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_LUT_R:
+ o.name = "red-gamma-table";
+ o.title = "LUT for red channel";
+ o.desc = "LUT for red channel";
+ o.type = SANE_TYPE_INT;
+ o.size = s->n_lut * WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->n_lut - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_LUT_G:
+ o.name = "green-gamma-table";
+ o.title = "LUT for green channel";
+ o.desc = "LUT for green channel";
+ o.type = SANE_TYPE_INT;
+ o.size = s->n_lut * WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->n_lut - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_LUT_B:
+ o.name = "blue-gamma-table";
+ o.title = "LUT for blue channel";
+ o.desc = "LUT for blue channel";
+ o.type = SANE_TYPE_INT;
+ o.size = s->n_lut * WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->n_lut - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_LOAD:
+ o.name = "load";
+ o.title = "Load";
+ o.desc = "Load next slide";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->n_frames > 1)
+ o.cap |= SANE_CAP_INACTIVE;
+ break;
+ case CS3_OPTION_AUTOLOAD:
+ o.name = "autoload";
+ o.title = "Autoload";
+ o.desc = "Autoload slide before each scan";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->n_frames > 1)
+ o.cap |= SANE_CAP_INACTIVE;
+ break;
+ case CS3_OPTION_EJECT:
+ o.name = "eject";
+ o.title = "Eject";
+ o.desc = "Eject loaded medium";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS3_OPTION_RESET:
+ o.name = "reset";
+ o.title = "Reset scanner";
+ o.desc = "Initialize scanner";
+ o.type = SANE_TYPE_BUTTON;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS3_OPTION_RESX:
+ case CS3_OPTION_RES:
+ case CS3_OPTION_PREVIEW_RESOLUTION:
+ if (i_option == CS3_OPTION_PREVIEW_RESOLUTION) {
+ o.name = "preview-resolution";
+ o.title = "Preview resolution";
+ o.desc = "Scanning resolution for preview mode in dpi, affecting both x and y directions";
+ } else if (i_option == CS3_OPTION_RES) {
+ o.name = "resolution";
+ o.title = "Resolution";
+ o.desc = "Scanning resolution in dpi, affecting both x and y directions";
+ } else {
+ o.name = "x-resolution";
+ o.title = "X resolution";
+ o.desc = "Scanning resolution in dpi, affecting x direction only";
+ }
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_DPI;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (i_option == CS3_OPTION_RESX)
+ o.cap |= SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ if (i_option == CS3_OPTION_PREVIEW_RESOLUTION)
+ o.cap |= SANE_CAP_ADVANCED;
+ o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ word_list =
+ (SANE_Word *) cs3_xmalloc((s->resx_n_list + 1)
+ *
+ sizeof(SANE_Word));
+ if (!word_list)
+ alloc_failed = 1;
+ else {
+ for (i_list = 0; i_list < s->resx_n_list;
+ i_list++)
+ word_list[i_list + 1] =
+ s->resx_list[i_list];
+ word_list[0] = s->resx_n_list;
+ o.constraint.word_list = word_list;
+ }
+ break;
+ case CS3_OPTION_RESY:
+ o.name = "y-resolution";
+ o.title = "Y resolution";
+ o.desc = "Scanning resolution in dpi, affecting y direction only";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_DPI;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ word_list =
+ (SANE_Word *) cs3_xmalloc((s->resy_n_list + 1)
+ *
+ sizeof(SANE_Word));
+ if (!word_list)
+ alloc_failed = 1;
+ else {
+ for (i_list = 0; i_list < s->resy_n_list;
+ i_list++)
+ word_list[i_list + 1] =
+ s->resy_list[i_list];
+ word_list[0] = s->resy_n_list;
+ o.constraint.word_list = word_list;
+ }
+ break;
+ case CS3_OPTION_RES_INDEPENDENT:
+ o.name = "independent-res";
+ o.title = "Independent x/y resolutions";
+ o.desc = "Enable independent controls for scanning resolution in x and y direction";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ break;
+ case CS3_OPTION_FRAME:
+ o.name = "frame";
+ o.title = "Frame number";
+ o.desc = "Number of frame to be scanned, starting with 1";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->n_frames <= 1)
+ o.cap |= SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 1;
+ range->max = s->n_frames;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_FRAME_COUNT:
+ o.name = "frame-count";
+ o.title = "Frame count";
+ o.desc = "Amount of frames to scan";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->n_frames <= 1)
+ o.cap |= SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 1;
+ range->max = s->n_frames - s->i_frame + 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_SUBFRAME:
+ o.name = "subframe";
+ o.title = "Frame shift";
+ o.desc = "Fine position within the selected frame";
+ o.type = SANE_TYPE_FIXED;
+ o.unit = SANE_UNIT_MM;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = SANE_FIX(0.);
+ range->max =
+ SANE_FIX((s->boundaryy -
+ 1) * s->unit_mm);
+ range->quant = SANE_FIX(0.);
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_XMIN:
+ o.name = "tl-x";
+ o.title = "Left x value of scan area";
+ o.desc = "Left x value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ range->min = 0;
+ range->max = s->boundaryx - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_XMAX:
+ o.name = "br-x";
+ o.title = "Right x value of scan area";
+ o.desc = "Right x value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->boundaryx - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_YMIN:
+ o.name = "tl-y";
+ o.title = "Top y value of scan area";
+ o.desc = "Top y value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->boundaryy - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_YMAX:
+ o.name = "br-y";
+ o.title = "Bottom y value of scan area";
+ o.desc = "Bottom y value of scan area";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->boundaryy - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_FOCUS_ON_CENTRE:
+ o.name = "focus-on-centre";
+ o.title = "Use centre of scan area as AF point";
+ o.desc = "Use centre of scan area as AF point instead of manual AF point selection";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS3_OPTION_FOCUS:
+ o.name = "focus";
+ o.title = "Focus position";
+ o.desc = "Focus position for manual focus";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_NONE;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = s->focus_min;
+ range->max = s->focus_max;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_AUTOFOCUS:
+ o.name = "autofocus";
+ o.title = "Autofocus";
+ o.desc = "Perform autofocus before scan";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS3_OPTION_FOCUSX:
+ o.name = "focusx";
+ o.title = "X coordinate of AF point";
+ o.desc = "X coordinate of AF point";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->boundaryx - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_FOCUSY:
+ o.name = "focusy";
+ o.title = "Y coordinate of AF point";
+ o.desc = "Y coordinate of AF point";
+ o.type = SANE_TYPE_INT;
+ o.unit = SANE_UNIT_PIXEL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_INACTIVE;
+ o.constraint_type = SANE_CONSTRAINT_RANGE;
+ range = (SANE_Range *)
+ cs3_xmalloc(sizeof(SANE_Range));
+ if (!range)
+ alloc_failed = 1;
+ else {
+ range->min = 0;
+ range->max = s->boundaryy - 1;
+ range->quant = 1;
+ o.constraint.range = range;
+ }
+ break;
+ case CS3_OPTION_SCAN_AE:
+ o.name = "ae";
+ o.title = "Auto-exposure";
+ o.desc = "Perform auto-exposure before scan";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ case CS3_OPTION_SCAN_AE_WB:
+ o.name = "ae-wb";
+ o.title = "Auto-exposure with white balance";
+ o.desc = "Perform auto-exposure with white balance before scan";
+ o.type = SANE_TYPE_BOOL;
+ o.size = WSIZE;
+ o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ break;
+ default:
+ DBG(1, "BUG: sane_open(): Unknown option number: %d\n", i_option);
+ break;
+ }
+ s->option_list[i_option] = o;
+ }
+
+ s->scanning = SANE_FALSE;
+ s->preview = SANE_FALSE;
+ s->negative = SANE_FALSE;
+ s->autoload = SANE_FALSE;
+ s->infrared = SANE_FALSE;
+ s->ae = SANE_FALSE;
+ s->aewb = SANE_FALSE;
+ s->samples_per_scan = 1;
+ s->depth = 8;
+ s->i_frame = 1;
+ s->frame_count = 1;
+ s->subframe = 0.;
+ s->res = s->resx = s->resx_max;
+ s->resy = s->resy_max;
+ s->res_independent = SANE_FALSE;
+ s->res_preview = s->resx_max / 10;
+ if (s->res_preview < s->resx_min)
+ s->res_preview = s->resx_min;
+ s->xmin = 0;
+ s->xmax = s->boundaryx - 1;
+ s->ymin = 0;
+ s->ymax = s->boundaryy - 1;
+ s->focus_on_centre = SANE_TRUE;
+ s->focus = 0;
+ s->focusx = 0;
+ s->focusy = 0;
+ s->exposure = 1.;
+ s->exposure_r = 1200.;
+ s->exposure_g = 1200.;
+ s->exposure_b = 1000.;
+ s->line_buf = NULL;
+ s->n_line_buf = 0;
+
+ if (alloc_failed) {
+ cs3_close(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ return cs3_reserve_unit(s);
+}
+
+void
+sane_close(SANE_Handle h)
+{
+ cs3_t *s = (cs3_t *) h;
+
+ DBG(10, "%s\n", __func__);
+
+ cs3_release_unit(s);
+ cs3_close(s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle h, SANE_Int n)
+{
+ cs3_t *s = (cs3_t *) h;
+
+ DBG(24, "%s, option %i\n", __func__, n);
+
+ if ((n >= 0) && (n < CS3_N_OPTIONS))
+ return &s->option_list[n];
+ else
+ return NULL;
+}
+
+SANE_Status
+sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v,
+ SANE_Int * i)
+{
+ cs3_t *s = (cs3_t *) h;
+ SANE_Int flags = 0;
+ cs3_pixel_t pixel;
+ SANE_Option_Descriptor o = s->option_list[n];
+
+ DBG(24, "%s, option %i, action %i.\n", __func__, n, a);
+
+ switch (a) {
+ case SANE_ACTION_GET_VALUE:
+
+ switch (n) {
+ case CS3_OPTION_NUM:
+ *(SANE_Word *) v = CS3_N_OPTIONS;
+ break;
+ case CS3_OPTION_NEGATIVE:
+ *(SANE_Word *) v = s->negative;
+ break;
+ case CS3_OPTION_INFRARED:
+ *(SANE_Word *) v = s->infrared;
+ break;
+ case CS3_OPTION_SAMPLES_PER_SCAN:
+ *(SANE_Word *) v = s->samples_per_scan;
+ break;
+ case CS3_OPTION_DEPTH:
+ *(SANE_Word *) v = s->depth;
+ break;
+ case CS3_OPTION_PREVIEW:
+ *(SANE_Word *) v = s->preview;
+ break;
+ case CS3_OPTION_AUTOLOAD:
+ *(SANE_Word *) v = s->autoload;
+ break;
+ case CS3_OPTION_EXPOSURE:
+ *(SANE_Word *) v = SANE_FIX(s->exposure);
+ break;
+ case CS3_OPTION_EXPOSURE_R:
+ *(SANE_Word *) v = SANE_FIX(s->exposure_r);
+ break;
+ case CS3_OPTION_EXPOSURE_G:
+ *(SANE_Word *) v = SANE_FIX(s->exposure_g);
+ break;
+ case CS3_OPTION_EXPOSURE_B:
+ *(SANE_Word *) v = SANE_FIX(s->exposure_b);
+ break;
+ case CS3_OPTION_LUT_R:
+ if (!(s->lut_r))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ ((SANE_Word *) v)[pixel] = s->lut_r[pixel];
+ break;
+ case CS3_OPTION_LUT_G:
+ if (!(s->lut_g))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ ((SANE_Word *) v)[pixel] = s->lut_g[pixel];
+ break;
+ case CS3_OPTION_LUT_B:
+ if (!(s->lut_b))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ ((SANE_Word *) v)[pixel] = s->lut_b[pixel];
+ break;
+ case CS3_OPTION_EJECT:
+ break;
+ case CS3_OPTION_LOAD:
+ break;
+ case CS3_OPTION_RESET:
+ break;
+ case CS3_OPTION_FRAME:
+ *(SANE_Word *) v = s->i_frame;
+ break;
+ case CS3_OPTION_FRAME_COUNT:
+ *(SANE_Word *) v = s->frame_count;
+ break;
+ case CS3_OPTION_SUBFRAME:
+ *(SANE_Word *) v = SANE_FIX(s->subframe);
+ break;
+ case CS3_OPTION_RES:
+ *(SANE_Word *) v = s->res;
+ break;
+ case CS3_OPTION_RESX:
+ *(SANE_Word *) v = s->resx;
+ break;
+ case CS3_OPTION_RESY:
+ *(SANE_Word *) v = s->resy;
+ break;
+ case CS3_OPTION_RES_INDEPENDENT:
+ *(SANE_Word *) v = s->res_independent;
+ break;
+ case CS3_OPTION_PREVIEW_RESOLUTION:
+ *(SANE_Word *) v = s->res_preview;
+ break;
+ case CS3_OPTION_XMIN:
+ *(SANE_Word *) v = s->xmin;
+ break;
+ case CS3_OPTION_XMAX:
+ *(SANE_Word *) v = s->xmax;
+ break;
+ case CS3_OPTION_YMIN:
+ *(SANE_Word *) v = s->ymin;
+ break;
+ case CS3_OPTION_YMAX:
+ *(SANE_Word *) v = s->ymax;
+ break;
+ case CS3_OPTION_FOCUS_ON_CENTRE:
+ *(SANE_Word *) v = s->focus_on_centre;
+ break;
+ case CS3_OPTION_FOCUS:
+ *(SANE_Word *) v = s->focus;
+ break;
+ case CS3_OPTION_AUTOFOCUS:
+ *(SANE_Word *) v = s->autofocus;
+ break;
+ case CS3_OPTION_FOCUSX:
+ *(SANE_Word *) v = s->focusx;
+ break;
+ case CS3_OPTION_FOCUSY:
+ *(SANE_Word *) v = s->focusy;
+ break;
+ case CS3_OPTION_SCAN_AE:
+ *(SANE_Word *) v = s->ae;
+ break;
+ case CS3_OPTION_SCAN_AE_WB:
+ *(SANE_Word *) v = s->aewb;
+ break;
+ default:
+ DBG(4, "%s: Unknown option (bug?).\n", __func__);
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if (s->scanning)
+ return SANE_STATUS_INVAL;
+ /* XXX do this for all elements of arrays */
+ switch (o.type) {
+ case SANE_TYPE_BOOL:
+ if ((*(SANE_Word *) v != SANE_TRUE)
+ && (*(SANE_Word *) v != SANE_FALSE))
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ switch (o.constraint_type) {
+ case SANE_CONSTRAINT_RANGE:
+ if (*(SANE_Word *) v <
+ o.constraint.range->min) {
+ *(SANE_Word *) v =
+ o.constraint.range->min;
+ flags |= SANE_INFO_INEXACT;
+ } else if (*(SANE_Word *) v >
+ o.constraint.range->max) {
+ *(SANE_Word *) v =
+ o.constraint.range->max;
+ flags |= SANE_INFO_INEXACT;
+ }
+ break;
+ case SANE_CONSTRAINT_WORD_LIST:
+ break;
+ default:
+ break;
+ }
+ break;
+ case SANE_TYPE_STRING:
+ break;
+ case SANE_TYPE_BUTTON:
+ break;
+ case SANE_TYPE_GROUP:
+ break;
+ }
+ switch (n) {
+ case CS3_OPTION_NUM:
+ return SANE_STATUS_INVAL;
+ break;
+ case CS3_OPTION_NEGATIVE:
+ s->negative = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_INFRARED:
+ s->infrared = *(SANE_Word *) v;
+ /* flags |= SANE_INFO_RELOAD_PARAMS; XXX */
+ break;
+ case CS3_OPTION_SAMPLES_PER_SCAN:
+ s->samples_per_scan = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_DEPTH:
+ if (*(SANE_Word *) v > s->maxbits)
+ return SANE_STATUS_INVAL;
+
+ s->depth = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case CS3_OPTION_PREVIEW:
+ s->preview = *(SANE_Word *) v;
+ break;
+
+ case CS3_OPTION_AUTOLOAD:
+ s->autoload = *(SANE_Word *) v;
+ break;
+
+ case CS3_OPTION_EXPOSURE:
+ s->exposure = SANE_UNFIX(*(SANE_Word *) v);
+ break;
+ case CS3_OPTION_EXPOSURE_R:
+ s->exposure_r = SANE_UNFIX(*(SANE_Word *) v);
+ break;
+ case CS3_OPTION_EXPOSURE_G:
+ s->exposure_g = SANE_UNFIX(*(SANE_Word *) v);
+ break;
+ case CS3_OPTION_EXPOSURE_B:
+ s->exposure_b = SANE_UNFIX(*(SANE_Word *) v);
+ break;
+ case CS3_OPTION_LUT_R:
+ if (!(s->lut_r))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_r[pixel] = ((SANE_Word *) v)[pixel];
+ break;
+ case CS3_OPTION_LUT_G:
+ if (!(s->lut_g))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_g[pixel] = ((SANE_Word *) v)[pixel];
+ break;
+ case CS3_OPTION_LUT_B:
+ if (!(s->lut_b))
+ return SANE_STATUS_INVAL;
+ for (pixel = 0; pixel < s->n_lut; pixel++)
+ s->lut_b[pixel] = ((SANE_Word *) v)[pixel];
+ break;
+ case CS3_OPTION_LOAD:
+ cs3_load(s);
+ break;
+ case CS3_OPTION_EJECT:
+ cs3_eject(s);
+ break;
+ case CS3_OPTION_RESET:
+ cs3_reset(s);
+ break;
+ case CS3_OPTION_FRAME:
+ s->i_frame = *(SANE_Word *) v;
+ break;
+
+ case CS3_OPTION_FRAME_COUNT:
+ if (*(SANE_Word *) v > (s->n_frames - s->i_frame + 1))
+ return SANE_STATUS_INVAL;
+ s->frame_count = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case CS3_OPTION_SUBFRAME:
+ s->subframe = SANE_UNFIX(*(SANE_Word *) v);
+ break;
+ case CS3_OPTION_RES:
+ s->res = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_RESX:
+ s->resx = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_RESY:
+ s->resy = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_RES_INDEPENDENT:
+ s->res_independent = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_PREVIEW_RESOLUTION:
+ s->res_preview = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_XMIN:
+ s->xmin = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_XMAX:
+ s->xmax = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_YMIN:
+ s->ymin = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_YMAX:
+ s->ymax = *(SANE_Word *) v;
+ flags |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case CS3_OPTION_FOCUS_ON_CENTRE:
+ s->focus_on_centre = *(SANE_Word *) v;
+ if (s->focus_on_centre) {
+ s->option_list[CS3_OPTION_FOCUSX].cap |=
+ SANE_CAP_INACTIVE;
+ s->option_list[CS3_OPTION_FOCUSY].cap |=
+ SANE_CAP_INACTIVE;
+ } else {
+ s->option_list[CS3_OPTION_FOCUSX].cap &=
+ ~SANE_CAP_INACTIVE;
+ s->option_list[CS3_OPTION_FOCUSY].cap &=
+ ~SANE_CAP_INACTIVE;
+ }
+ flags |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case CS3_OPTION_FOCUS:
+ s->focus = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_AUTOFOCUS:
+ s->autofocus = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_FOCUSX:
+ s->focusx = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_FOCUSY:
+ s->focusy = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_SCAN_AE:
+ s->ae = *(SANE_Word *) v;
+ break;
+ case CS3_OPTION_SCAN_AE_WB:
+ s->aewb = *(SANE_Word *) v;
+ break;
+ default:
+ DBG(4,
+ "Error: sane_control_option(): Unknown option number (bug?).\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+
+ default:
+ DBG(1,
+ "BUG: sane_control_option(): Unknown action number.\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (i)
+ *i = flags;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters(SANE_Handle h, SANE_Parameters * p)
+{
+ cs3_t *s = (cs3_t *) h;
+ SANE_Status status;
+
+ DBG(10, "%s\n", __func__);
+
+ if (!s->scanning) { /* only recalculate when not scanning */
+ status = cs3_convert_options(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ p->bytes_per_line =
+ s->n_colors * s->logical_width * s->bytes_per_pixel;
+
+#ifdef SANE_FRAME_RGBI
+ if (s->infrared) {
+ p->format = SANE_FRAME_RGBI;
+
+ } else {
+#endif
+ p->format = SANE_FRAME_RGB; /* XXXXXXXX CCCCCCCCCC */
+#ifdef SANE_FRAME_RGBI
+ }
+#endif
+
+ p->last_frame = SANE_TRUE;
+ p->lines = s->logical_height;
+ p->depth = 8 * s->bytes_per_pixel;
+ p->pixels_per_line = s->logical_width;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start(SANE_Handle h)
+{
+ cs3_t *s = (cs3_t *) h;
+ SANE_Status status;
+
+ DBG(10, "%s\n", __func__);
+
+ if (s->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (s->n_frames > 1 && s->frame_count == 0) {
+ DBG(4, "%s: no more frames\n", __func__);
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ if (s->n_frames > 1) {
+ DBG(4, "%s: scanning frame at position %d, %d to go\n",
+ __func__, s->i_frame, s->frame_count);
+ }
+
+ status = cs3_convert_options(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->i_line_buf = 0;
+ s->xfer_position = 0;
+
+ s->scanning = SANE_TRUE;
+
+ /* load if appropriate */
+ if (s->autoload) {
+ status = cs3_load(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* check for documents */
+ status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (s->status & CS3_STATUS_NO_DOCS)
+ return SANE_STATUS_NO_DOCS;
+
+ if (s->autofocus) {
+ status = cs3_autofocus(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (s->aewb) {
+ status = cs3_autoexposure(s, 1);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ } else if (s->ae) {
+ status = cs3_autoexposure(s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return cs3_scan(s, CS3_SCAN_NORMAL);
+}
+
+SANE_Status
+sane_read(SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ cs3_t *s = (cs3_t *) h;
+ SANE_Status status;
+ ssize_t xfer_len_in, xfer_len_line, xfer_len_out;
+ unsigned long index;
+ int color, sample_pass;
+ uint8_t *s8 = NULL;
+ uint16_t *s16 = NULL;
+ double m_avg_sum;
+ SANE_Byte *line_buf_new;
+
+ DBG(32, "%s, maxlen = %i.\n", __func__, maxlen);
+
+ if (!s->scanning) {
+ *len = 0;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* transfer from buffer */
+ if (s->i_line_buf > 0) {
+ xfer_len_out = s->n_line_buf - s->i_line_buf;
+ if (xfer_len_out > maxlen)
+ xfer_len_out = maxlen;
+
+ memcpy(buf, &(s->line_buf[s->i_line_buf]), xfer_len_out);
+
+ s->i_line_buf += xfer_len_out;
+ if (s->i_line_buf >= s->n_line_buf)
+ s->i_line_buf = 0;
+
+ *len = xfer_len_out;
+ return SANE_STATUS_GOOD;
+ }
+
+ xfer_len_line = s->n_colors * s->logical_width * s->bytes_per_pixel;
+ xfer_len_in = xfer_len_line + (s->n_colors * s->odd_padding);
+
+ if ((xfer_len_in & 0x3f)) {
+ int d = ((xfer_len_in / 512) * 512) + 512;
+ s->block_padding = d - xfer_len_in;
+ }
+
+ DBG(22, "%s: block_padding = %d, odd_padding = %d\n",
+ __func__, s->block_padding, s->odd_padding);
+
+ DBG(22,
+ "%s: colors = %d, logical_width = %ld, bytes_per_pixel = %d\n",
+ __func__, s->n_colors, s->logical_width, s->bytes_per_pixel);
+
+
+ /* Do not change the behaviour of older models, pad to 512 */
+ if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) {
+ xfer_len_in += s->block_padding;
+ if (xfer_len_in & 0x3f)
+ DBG(1, "BUG: %s, not a multiple of 64. (0x%06lx)\n",
+ __func__, (long) xfer_len_in);
+ }
+
+ if (s->xfer_position + xfer_len_line > s->xfer_bytes_total)
+ xfer_len_line = s->xfer_bytes_total - s->xfer_position; /* just in case */
+
+ if (xfer_len_line == 0) { /* no more data */
+ *len = 0;
+
+ /* increment frame number if appropriate */
+ if (s->n_frames > 1 && --s->frame_count) {
+ s->i_frame++;
+ }
+
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_EOF;
+ }
+
+ if (xfer_len_line != s->n_line_buf) {
+ line_buf_new =
+ (SANE_Byte *) cs3_xrealloc(s->line_buf,
+ xfer_len_line *
+ sizeof(SANE_Byte));
+ if (!line_buf_new) {
+ *len = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ s->line_buf = line_buf_new;
+ s->n_line_buf = xfer_len_line;
+ }
+
+ /* adapt for multi-sampling */
+ xfer_len_in *= s->samples_per_scan;
+
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "28 00 00 00 00 00");
+ cs3_pack_byte(s, (xfer_len_in >> 16) & 0xff);
+ cs3_pack_byte(s, (xfer_len_in >> 8) & 0xff);
+ cs3_pack_byte(s, xfer_len_in & 0xff);
+ cs3_parse_cmd(s, "00");
+ s->n_recv = xfer_len_in;
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD) {
+ *len = 0;
+ return status;
+ }
+
+ for (index = 0; index < s->logical_width; index++) {
+ for (color = 0; color < s->n_colors; color++) {
+ int where = s->bytes_per_pixel
+ * (s->n_colors * index + color);
+
+ m_avg_sum = 0.0;
+
+ switch (s->bytes_per_pixel) {
+ case 1:
+ {
+ /* target address */
+ s8 = (uint8_t *) & (s->line_buf[where]);
+
+ if (s->samples_per_scan > 1) {
+ /* calculate average of multi samples */
+ for (sample_pass = 0;
+ sample_pass < s->samples_per_scan;
+ sample_pass++) {
+ /* source index */
+ int p8 = (sample_pass * s->n_colors + color)
+ * s->logical_width
+ + (color + 1) * s->odd_padding
+ + index;
+ m_avg_sum += (double) s->recv_buf[p8];
+ }
+ *s8 = (uint8_t) (m_avg_sum / s->samples_per_scan + 0.5);
+ } else {
+ /* shortcut for single sample */
+ int p8 = s->logical_width * color
+ + (color + 1) * s->odd_padding
+ + index;
+ *s8 = s->recv_buf[p8];
+ }
+ }
+ break;
+ case 2:
+ {
+ /* target address */
+ s16 = (uint16_t *) & (s->line_buf[where]);
+
+ if (s->samples_per_scan > 1) {
+ /* calculate average of multi samples */
+ for (sample_pass = 0;
+ sample_pass < s->samples_per_scan;
+ sample_pass++) {
+ /* source index */
+ int p16 = 2 * ((sample_pass * s->n_colors + color)
+ * s->logical_width + index);
+ m_avg_sum += (double) ((s->recv_buf[p16] << 8)
+ + s->recv_buf[p16 + 1]);
+ }
+ *s16 = (uint16_t) (m_avg_sum / s->samples_per_scan + 0.5);
+ } else {
+ /* shortcut for single sample */
+ int p16 = 2 * (color * s->logical_width + index);
+
+ *s16 = (s->recv_buf[p16] << 8)
+ + s->recv_buf[p16 + 1];
+ }
+
+ *s16 <<= s->shift_bits;
+ }
+ break;
+
+ default:
+ DBG(1,
+ "BUG: sane_read(): Unknown number of bytes per pixel.\n");
+ *len = 0;
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ }
+ }
+
+ s->xfer_position += xfer_len_line;
+
+ xfer_len_out = xfer_len_line;
+ if (xfer_len_out > maxlen)
+ xfer_len_out = maxlen;
+
+ memcpy(buf, s->line_buf, xfer_len_out);
+ if (xfer_len_out < xfer_len_line)
+ s->i_line_buf = xfer_len_out; /* data left in the line buffer, read out next time */
+
+ *len = xfer_len_out;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel(SANE_Handle h)
+{
+ cs3_t *s = (cs3_t *) h;
+
+ DBG(10, "%s, scanning = %d.\n", __func__, s->scanning);
+
+ if (s->scanning) {
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "c0 00 00 00 00 00");
+ cs3_issue_cmd(s);
+ }
+
+ s->scanning = SANE_FALSE;
+}
+
+SANE_Status
+sane_set_io_mode(SANE_Handle h, SANE_Bool m)
+{
+ cs3_t *s = (cs3_t *) h;
+
+ DBG(10, "%s\n", __func__);
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+ if (m == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd(SANE_Handle h, SANE_Int * fd)
+{
+ cs3_t *s = (cs3_t *) h;
+
+ DBG(10, "%s\n", __func__);
+
+ fd = fd; /* to shut up compiler */
+ s = s; /* to shut up compiler */
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/* ========================================================================= */
+/* private functions */
+
+static void
+cs3_trim(char *s)
+{
+ int i, l = strlen(s);
+
+ for (i = l - 1; i > 0; i--) {
+ if (s[i] == ' ')
+ s[i] = '\0';
+ else
+ break;
+ }
+}
+
+static SANE_Status
+cs3_open(const char *device, cs3_interface_t interface, cs3_t ** sp)
+{
+ SANE_Status status;
+ cs3_t *s;
+ char *prefix = NULL, *line;
+ int i;
+ int alloc_failed = 0;
+ SANE_Device **device_list_new;
+
+ DBG(6, "%s, device = %s, interface = %i\n",
+ __func__, device, interface);
+
+ if (!strncmp(device, "auto", 5)) {
+ try_interface = CS3_INTERFACE_SCSI;
+ sanei_config_attach_matching_devices("scsi Nikon *",
+ cs3_attach);
+ try_interface = CS3_INTERFACE_USB;
+ sanei_usb_attach_matching_devices("usb 0x04b0 0x4000",
+ cs3_attach);
+ sanei_usb_attach_matching_devices("usb 0x04b0 0x4001",
+ cs3_attach);
+ sanei_usb_attach_matching_devices("usb 0x04b0 0x4002",
+ cs3_attach);
+ return SANE_STATUS_GOOD;
+ }
+
+ if ((s = (cs3_t *) cs3_xmalloc(sizeof(cs3_t))) == NULL)
+ return SANE_STATUS_NO_MEM;
+ memset(s, 0, sizeof(cs3_t));
+
+ /* fill magic bits */
+ s->magic = SANE_COOKIE;
+ s->cookie_ptr = &s->cookie;
+
+ s->cookie.version = 0x01;
+ s->cookie.vendor = s->vendor_string;
+ s->cookie.model = s->product_string;
+ s->cookie.revision = s->revision_string;
+
+ s->send_buf = s->recv_buf = NULL;
+ s->send_buf_size = s->recv_buf_size = 0;
+
+ switch (interface) {
+ case CS3_INTERFACE_UNKNOWN:
+ for (i = 0; i < 2; i++) {
+ switch (i) {
+ case 1:
+ prefix = "usb:";
+ try_interface = CS3_INTERFACE_USB;
+ break;
+ default:
+ prefix = "scsi:";
+ try_interface = CS3_INTERFACE_SCSI;
+ break;
+ }
+ if (!strncmp(device, prefix, strlen(prefix))) {
+ const void *p = device + strlen(prefix);
+ cs3_xfree(s);
+ return cs3_open(p, try_interface, sp);
+ }
+ }
+ cs3_xfree(s);
+ return SANE_STATUS_INVAL;
+ break;
+ case CS3_INTERFACE_SCSI:
+ s->interface = CS3_INTERFACE_SCSI;
+ DBG(6,
+ "%s, trying to open %s, assuming SCSI or SBP2 interface\n",
+ __func__, device);
+ status = sanei_scsi_open(device, &s->fd,
+ cs3_scsi_sense_handler, s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(6, " ...failed: %s.\n", sane_strstatus(status));
+ cs3_xfree(s);
+ return status;
+ }
+ break;
+ case CS3_INTERFACE_USB:
+ s->interface = CS3_INTERFACE_USB;
+ DBG(6, "%s, trying to open %s, assuming USB interface\n",
+ __func__, device);
+ status = sanei_usb_open(device, &s->fd);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(6, " ...failed: %s.\n", sane_strstatus(status));
+ cs3_xfree(s);
+ return status;
+ }
+ break;
+ }
+
+ open_devices++;
+ DBG(6, "%s, trying to identify device.\n", __func__);
+
+ /* identify scanner */
+ status = cs3_page_inquiry(s, -1);
+ if (status != SANE_STATUS_GOOD) {
+ cs3_close(s);
+ return status;
+ }
+
+ strncpy(s->vendor_string, (char *) s->recv_buf + 8, 8);
+ s->vendor_string[8] = '\0';
+ strncpy(s->product_string, (char *) s->recv_buf + 16, 16);
+ s->product_string[16] = '\0';
+ strncpy(s->revision_string, (char *) s->recv_buf + 32, 4);
+ s->revision_string[4] = '\0';
+
+ DBG(10,
+ "%s, vendor = '%s', product = '%s', revision = '%s'.\n",
+ __func__, s->vendor_string, s->product_string,
+ s->revision_string);
+
+ if (!strncmp(s->product_string, "COOLSCANIII ", 16))
+ s->type = CS3_TYPE_LS30;
+ else if (!strncmp(s->product_string, "LS-40 ED ", 16))
+ s->type = CS3_TYPE_LS40;
+ else if (!strncmp(s->product_string, "LS-50 ED ", 16))
+ s->type = CS3_TYPE_LS50;
+ else if (!strncmp(s->product_string, "LS-2000 ", 16))
+ s->type = CS3_TYPE_LS2000;
+ else if (!strncmp(s->product_string, "LS-4000 ED ", 16))
+ s->type = CS3_TYPE_LS4000;
+ else if (!strncmp(s->product_string, "LS-5000 ED ", 16))
+ s->type = CS3_TYPE_LS5000;
+ else if (!strncmp(s->product_string, "LS-8000 ED ", 16))
+ s->type = CS3_TYPE_LS8000;
+
+ if (s->type != CS3_TYPE_UNKOWN)
+ DBG(10,
+ "%s, device identified as coolscan3 type #%i.\n",
+ __func__, s->type);
+ else {
+ DBG(10, "%s, device not identified.\n", __func__);
+ cs3_close(s);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ cs3_trim(s->vendor_string);
+ cs3_trim(s->product_string);
+ cs3_trim(s->revision_string);
+
+ if (sp)
+ *sp = s;
+ else {
+ device_list_new =
+ (SANE_Device **) cs3_xrealloc(device_list,
+ (n_device_list +
+ 2) *
+ sizeof(SANE_Device *));
+ if (!device_list_new)
+ return SANE_STATUS_NO_MEM;
+ device_list = device_list_new;
+ device_list[n_device_list] =
+ (SANE_Device *) cs3_xmalloc(sizeof(SANE_Device));
+ if (!device_list[n_device_list])
+ return SANE_STATUS_NO_MEM;
+ switch (interface) {
+ case CS3_INTERFACE_UNKNOWN:
+ DBG(1, "BUG: cs3_open(): unknown interface.\n");
+ cs3_close(s);
+ return SANE_STATUS_UNSUPPORTED;
+ break;
+ case CS3_INTERFACE_SCSI:
+ prefix = "scsi:";
+ break;
+ case CS3_INTERFACE_USB:
+ prefix = "usb:";
+ break;
+ }
+
+ line = (char *) cs3_xmalloc(strlen(device) + strlen(prefix) +
+ 1);
+ if (!line)
+ alloc_failed = 1;
+ else {
+ strcpy(line, prefix);
+ strcat(line, device);
+ device_list[n_device_list]->name = line;
+ }
+
+ line = (char *) cs3_xmalloc(strlen(s->vendor_string) + 1);
+ if (!line)
+ alloc_failed = 1;
+ else {
+ strcpy(line, s->vendor_string);
+ device_list[n_device_list]->vendor = line;
+ }
+
+ line = (char *) cs3_xmalloc(strlen(s->product_string) + 1);
+ if (!line)
+ alloc_failed = 1;
+ else {
+ strcpy(line, s->product_string);
+ device_list[n_device_list]->model = line;
+ }
+
+ device_list[n_device_list]->type = "film scanner";
+
+ if (alloc_failed) {
+ cs3_xfree(device_list[n_device_list]->name);
+ cs3_xfree(device_list[n_device_list]->vendor);
+ cs3_xfree(device_list[n_device_list]->model);
+ cs3_xfree(device_list[n_device_list]);
+ } else
+ n_device_list++;
+ device_list[n_device_list] = NULL;
+
+ cs3_close(s);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+cs3_close(cs3_t * s)
+{
+ cs3_xfree(s->lut_r);
+ cs3_xfree(s->lut_g);
+ cs3_xfree(s->lut_b);
+ cs3_xfree(s->lut_neutral);
+ cs3_xfree(s->line_buf);
+
+ switch (s->interface) {
+ case CS3_INTERFACE_UNKNOWN:
+ DBG(0, "BUG: %s: Unknown interface number.\n", __func__);
+ break;
+ case CS3_INTERFACE_SCSI:
+ sanei_scsi_close(s->fd);
+ open_devices--;
+ break;
+ case CS3_INTERFACE_USB:
+ sanei_usb_close(s->fd);
+ open_devices--;
+ break;
+ }
+
+ cs3_xfree(s);
+}
+
+static SANE_Status
+cs3_attach(const char *dev)
+{
+ SANE_Status status;
+
+ if (try_interface == CS3_INTERFACE_UNKNOWN)
+ return SANE_STATUS_UNSUPPORTED;
+
+ status = cs3_open(dev, try_interface, NULL);
+ return status;
+}
+
+static SANE_Status
+cs3_scsi_sense_handler(int fd, u_char * sense_buffer, void *arg)
+{
+ cs3_t *s = (cs3_t *) arg;
+
+ fd = fd; /* to shut up compiler */
+
+ /* sort this out ! XXX */
+
+ s->sense_key = sense_buffer[2] & 0x0f;
+ s->sense_asc = sense_buffer[12];
+ s->sense_ascq = sense_buffer[13];
+ s->sense_info = sense_buffer[3];
+
+ return cs3_parse_sense_data(s);
+}
+
+static SANE_Status
+cs3_parse_sense_data(cs3_t * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ s->sense_code =
+ (s->sense_key << 24) + (s->sense_asc << 16) +
+ (s->sense_ascq << 8) + s->sense_info;
+
+ if (s->sense_key)
+ DBG(14, "sense code: %02lx-%02lx-%02lx-%02lx\n", s->sense_key,
+ s->sense_asc, s->sense_ascq, s->sense_info);
+
+ switch (s->sense_key) {
+ case 0x00:
+ s->status = CS3_STATUS_READY;
+ break;
+
+ case 0x02:
+ switch (s->sense_asc) {
+ case 0x04:
+ DBG(15, " processing\n");
+ s->status = CS3_STATUS_PROCESSING;
+ break;
+ case 0x3a:
+ DBG(15, " no docs\n");
+ s->status = CS3_STATUS_NO_DOCS;
+ break;
+ default:
+ DBG(15, " default\n");
+ s->status = CS3_STATUS_ERROR;
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ break;
+
+ case 0x09:
+ if ((s->sense_code == 0x09800600)
+ || (s->sense_code == 0x09800601))
+ s->status = CS3_STATUS_REISSUE;
+ break;
+
+ default:
+ s->status = CS3_STATUS_ERROR;
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+
+ return status;
+}
+
+static void
+cs3_init_buffer(cs3_t * s)
+{
+ s->n_cmd = 0;
+ s->n_send = 0;
+ s->n_recv = 0;
+}
+
+static SANE_Status
+cs3_pack_byte(cs3_t * s, SANE_Byte byte)
+{
+ while (s->send_buf_size <= s->n_send) {
+ s->send_buf_size += 16;
+ s->send_buf =
+ (SANE_Byte *) cs3_xrealloc(s->send_buf,
+ s->send_buf_size);
+ if (!s->send_buf)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->send_buf[s->n_send++] = byte;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+cs3_pack_long(cs3_t * s, unsigned long val)
+{
+ cs3_pack_byte(s, (val >> 24) & 0xff);
+ cs3_pack_byte(s, (val >> 16) & 0xff);
+ cs3_pack_byte(s, (val >> 8) & 0xff);
+ cs3_pack_byte(s, val & 0xff);
+}
+
+static void
+cs3_pack_word(cs3_t * s, unsigned long val)
+{
+ cs3_pack_byte(s, (val >> 8) & 0xff);
+ cs3_pack_byte(s, val & 0xff);
+}
+
+static SANE_Status
+cs3_parse_cmd(cs3_t * s, char *text)
+{
+ size_t i, j;
+ char c, h;
+ SANE_Status status;
+
+ for (i = 0; i < strlen(text); i += 2)
+ if (text[i] == ' ')
+ i--; /* a bit dirty... advance by -1+2=1 */
+ else {
+ if ((!isxdigit(text[i])) || (!isxdigit(text[i + 1])))
+ DBG(1,
+ "BUG: cs3_parse_cmd(): Parser got invalid character.\n");
+ c = 0;
+ for (j = 0; j < 2; j++) {
+ h = tolower(text[i + j]);
+ if ((h >= 'a') && (h <= 'f'))
+ c += 10 + h - 'a';
+ else
+ c += h - '0';
+ if (j == 0)
+ c <<= 4;
+ }
+ status = cs3_pack_byte(s, c);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_grow_send_buffer(cs3_t * s)
+{
+ if (s->n_send > s->send_buf_size) {
+ s->send_buf_size = s->n_send;
+ s->send_buf =
+ (SANE_Byte *) cs3_xrealloc(s->send_buf,
+ s->send_buf_size);
+ if (!s->send_buf)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_issue_cmd(cs3_t * s)
+{
+ SANE_Status status = SANE_STATUS_INVAL;
+ size_t n_data, n_status;
+ static SANE_Byte status_buf[8];
+ int status_only = 0;
+
+ DBG(20,
+ "cs3_issue_cmd(): opcode = 0x%02x, n_send = %lu, n_recv = %lu.\n",
+ s->send_buf[0], (unsigned long) s->n_send,
+ (unsigned long) s->n_recv);
+
+ s->status = CS3_STATUS_READY;
+
+ if (!s->n_cmd)
+ switch (s->send_buf[0]) {
+ case 0x00:
+ case 0x12:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x1a:
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0xc0:
+ case 0xc1:
+ s->n_cmd = 6;
+ break;
+ case 0x24:
+ case 0x25:
+ case 0x28:
+ case 0x2a:
+ case 0xe0:
+ case 0xe1:
+ s->n_cmd = 10;
+ break;
+ default:
+ DBG(1,
+ "BUG: cs3_issue_cmd(): Unknown command opcode 0x%02x.\n",
+ s->send_buf[0]);
+ break;
+ }
+
+ if (s->n_send < s->n_cmd) {
+ DBG(1,
+ "BUG: cs3_issue_cmd(): Negative number of data out bytes requested.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ n_data = s->n_send - s->n_cmd;
+ if (s->n_recv > 0) {
+ if (n_data > 0) {
+ DBG(1,
+ "BUG: cs3_issue_cmd(): Both data in and data out requested.\n");
+ return SANE_STATUS_INVAL;
+ } else {
+ n_data = s->n_recv;
+ }
+ }
+
+ s->recv_buf = (SANE_Byte *) cs3_xrealloc(s->recv_buf, s->n_recv);
+ if (!s->recv_buf)
+ return SANE_STATUS_NO_MEM;
+
+ switch (s->interface) {
+ case CS3_INTERFACE_UNKNOWN:
+ DBG(1,
+ "BUG: cs3_issue_cmd(): Unknown or uninitialized interface number.\n");
+ break;
+
+ case CS3_INTERFACE_SCSI:
+ sanei_scsi_cmd2(s->fd, s->send_buf, s->n_cmd,
+ s->send_buf + s->n_cmd, s->n_send - s->n_cmd,
+ s->recv_buf, &s->n_recv);
+ status = SANE_STATUS_GOOD;
+ break;
+
+ case CS3_INTERFACE_USB:
+ status = sanei_usb_write_bulk(s->fd, s->send_buf, &s->n_cmd);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1,
+ "Error: cs3_issue_cmd(): Could not write command.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ switch (cs3_phase_check(s)) {
+ case CS3_PHASE_OUT:
+ if (s->n_send - s->n_cmd < n_data || !n_data) {
+ DBG(4,
+ "Error: cs3_issue_cmd(): Unexpected data out phase.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ status = sanei_usb_write_bulk(s->fd,
+ s->send_buf + s->n_cmd,
+ &n_data);
+ break;
+
+ case CS3_PHASE_IN:
+ if (s->n_recv < n_data || !n_data) {
+ DBG(4,
+ "Error: cs3_issue_cmd(): Unexpected data in phase.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ status = sanei_usb_read_bulk(s->fd, s->recv_buf,
+ &n_data);
+ s->n_recv = n_data;
+ break;
+
+ case CS3_PHASE_NONE:
+ DBG(4, "%s: No command received!\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+
+ default:
+ if (n_data) {
+ DBG(4,
+ "%s: Unexpected non-data phase, but n_data != 0 (%lu).\n",
+ __func__, (u_long) n_data);
+ status_only = 1;
+ }
+ break;
+ }
+
+ n_status = 8;
+ status = sanei_usb_read_bulk(s->fd, status_buf, &n_status);
+ if (n_status != 8) {
+ DBG(4,
+ "Error: cs3_issue_cmd(): Failed to read 8 status bytes from USB.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->sense_key = status_buf[1] & 0x0f;
+ s->sense_asc = status_buf[2] & 0xff;
+ s->sense_ascq = status_buf[3] & 0xff;
+ s->sense_info = status_buf[4] & 0xff;
+ status = cs3_parse_sense_data(s);
+ break;
+ }
+
+ if (status_only)
+ return SANE_STATUS_IO_ERROR;
+ else
+ return status;
+}
+
+static cs3_phase_t
+cs3_phase_check(cs3_t * s)
+{
+ static SANE_Byte phase_send_buf[1] = { 0xd0 }, phase_recv_buf[1];
+ SANE_Status status = 0;
+ size_t n = 1;
+
+ status = sanei_usb_write_bulk(s->fd, phase_send_buf, &n);
+ status |= sanei_usb_read_bulk(s->fd, phase_recv_buf, &n);
+
+ DBG(40, "%s: returned phase = 0x%02x.\n", __func__,
+ phase_recv_buf[0]);
+
+ if (status != SANE_STATUS_GOOD)
+ return -1;
+ else
+ return phase_recv_buf[0];
+}
+
+static SANE_Status
+cs3_scanner_ready(cs3_t * s, int flags)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i = -1;
+ unsigned long count = 0;
+ int retry = 3;
+
+ do {
+ if (i >= 0) /* dirty !!! */
+ usleep(1000000);
+ /* test unit ready */
+ cs3_init_buffer(s);
+ for (i = 0; i < 6; i++)
+ cs3_pack_byte(s, 0x00);
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ if (--retry < 0)
+ return status;
+
+ if (++count > 120) { /* 120s timeout */
+ DBG(4, "Error: %s: Timeout expired.\n", __func__);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ }
+ while (s->status & ~flags); /* until all relevant bits are 0 */
+
+ return status;
+}
+
+static SANE_Status
+cs3_page_inquiry(cs3_t * s, int page)
+{
+ SANE_Status status;
+
+ size_t n;
+
+ if (page >= 0) {
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "12 01");
+ cs3_pack_byte(s, page);
+ cs3_parse_cmd(s, "00 04 00");
+ s->n_recv = 4;
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(4,
+ "Error: cs3_page_inquiry(): Inquiry of page size failed: %s.\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ n = s->recv_buf[3] + 4;
+
+ } else
+ n = 36;
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+ if (page >= 0) {
+ cs3_parse_cmd(s, "12 01");
+ cs3_pack_byte(s, page);
+ cs3_parse_cmd(s, "00");
+ } else
+ cs3_parse_cmd(s, "12 00 00 00");
+ cs3_pack_byte(s, n);
+ cs3_parse_cmd(s, "00");
+ s->n_recv = n;
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(4, "Error: %s: inquiry of page failed: %s.\n",
+ __func__, sane_strstatus(status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_full_inquiry(cs3_t * s)
+{
+ SANE_Status status;
+ int pitch, pitch_max;
+ cs3_pixel_t pixel;
+
+ DBG(4, "%s\n", __func__);
+
+ status = cs3_page_inquiry(s, 0xc1);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->maxbits = s->recv_buf[82];
+ if (s->type == CS3_TYPE_LS30) /* must be overridden, LS-30 claims to have 12 bits */
+ s->maxbits = 10;
+
+ s->n_lut = 1;
+ s->n_lut <<= s->maxbits;
+ s->lut_r =
+ (cs3_pixel_t *) cs3_xrealloc(s->lut_r,
+ s->n_lut * sizeof(cs3_pixel_t));
+ s->lut_g =
+ (cs3_pixel_t *) cs3_xrealloc(s->lut_g,
+ s->n_lut * sizeof(cs3_pixel_t));
+ s->lut_b =
+ (cs3_pixel_t *) cs3_xrealloc(s->lut_b,
+ s->n_lut * sizeof(cs3_pixel_t));
+ s->lut_neutral =
+ (cs3_pixel_t *) cs3_xrealloc(s->lut_neutral,
+ s->n_lut * sizeof(cs3_pixel_t));
+
+ if (!s->lut_r || !s->lut_g || !s->lut_b || !s->lut_neutral) {
+ cs3_xfree(s->lut_r);
+ cs3_xfree(s->lut_g);
+ cs3_xfree(s->lut_b);
+ cs3_xfree(s->lut_neutral);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (pixel = 0; pixel < s->n_lut; pixel++) {
+ s->lut_r[pixel] = s->lut_g[pixel] = s->lut_b[pixel] =
+ s->lut_neutral[pixel] = pixel;
+ }
+
+ s->resx_optical = 256 * s->recv_buf[18] + s->recv_buf[19];
+ s->resx_max = 256 * s->recv_buf[20] + s->recv_buf[21];
+ s->resx_min = 256 * s->recv_buf[22] + s->recv_buf[23];
+ s->boundaryx =
+ 65536 * (256 * s->recv_buf[36] + s->recv_buf[37]) +
+ 256 * s->recv_buf[38] + s->recv_buf[39];
+
+ s->resy_optical = 256 * s->recv_buf[40] + s->recv_buf[41];
+ s->resy_max = 256 * s->recv_buf[42] + s->recv_buf[43];
+ s->resy_min = 256 * s->recv_buf[44] + s->recv_buf[45];
+ s->boundaryy =
+ 65536 * (256 * s->recv_buf[58] + s->recv_buf[59]) +
+ 256 * s->recv_buf[60] + s->recv_buf[61];
+
+ s->focus_min = 256 * s->recv_buf[76] + s->recv_buf[77];
+ s->focus_max = 256 * s->recv_buf[78] + s->recv_buf[79];
+
+ s->n_frames = s->recv_buf[75];
+
+ s->frame_offset = s->resy_max * 1.5 + 1; /* works for LS-30, maybe not for others */
+
+ /* generate resolution list for x */
+ s->resx_n_list = pitch_max =
+ floor(s->resx_max / (double) s->resx_min);
+ s->resx_list =
+ (unsigned int *) cs3_xrealloc(s->resx_list,
+ pitch_max *
+ sizeof(unsigned int));
+ for (pitch = 1; pitch <= pitch_max; pitch++)
+ s->resx_list[pitch - 1] = s->resx_max / pitch;
+
+ /* generate resolution list for y */
+ s->resy_n_list = pitch_max =
+ floor(s->resy_max / (double) s->resy_min);
+ s->resy_list =
+ (unsigned int *) cs3_xrealloc(s->resy_list,
+ pitch_max *
+ sizeof(unsigned int));
+
+ for (pitch = 1; pitch <= pitch_max; pitch++)
+ s->resy_list[pitch - 1] = s->resy_max / pitch;
+
+ s->unit_dpi = s->resx_max;
+ s->unit_mm = 25.4 / s->unit_dpi;
+
+ DBG(4, " maximum depth: %d\n", s->maxbits);
+ DBG(4, " focus: %d/%d\n", s->focus_min, s->focus_max);
+ DBG(4, " resolution (x): %d (%d-%d)\n", s->resx_optical,
+ s->resx_min, s->resx_max);
+ DBG(4, " resolution (y): %d (%d-%d)\n", s->resy_optical,
+ s->resy_min, s->resy_max);
+ DBG(4, " frames: %d\n", s->n_frames);
+ DBG(4, " frame offset: %ld\n", s->frame_offset);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_execute(cs3_t * s)
+{
+ DBG(16, "%s\n", __func__);
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "c1 00 00 00 00 00");
+ return cs3_issue_cmd(s);
+}
+
+static SANE_Status
+cs3_issue_and_execute(cs3_t * s)
+{
+ SANE_Status status;
+
+ DBG(10, "%s, opcode = %02x\n", __func__, s->send_buf[0]);
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return cs3_execute(s);
+}
+
+static SANE_Status
+cs3_mode_select(cs3_t * s)
+{
+ DBG(4, "%s\n", __func__);
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+
+ cs3_parse_cmd(s,
+ "15 10 00 00 14 00 00 00 00 08 00 00 00 00 00 00 00 01 03 06 00 00");
+ cs3_pack_word(s, s->unit_dpi);
+ cs3_parse_cmd(s, "00 00");
+
+ return cs3_issue_cmd(s);
+}
+
+static SANE_Status
+cs3_load(cs3_t * s)
+{
+ SANE_Status status;
+
+ DBG(6, "%s\n", __func__);
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "e0 00 d1 00 00 00 00 00 0d 00");
+ s->n_send += 13;
+
+ status = cs3_grow_send_buffer(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return cs3_issue_and_execute(s);
+}
+
+static SANE_Status
+cs3_eject(cs3_t * s)
+{
+ SANE_Status status;
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "e0 00 d0 00 00 00 00 00 0d 00");
+ s->n_send += 13;
+
+ status = cs3_grow_send_buffer(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return cs3_issue_and_execute(s);
+}
+
+static SANE_Status
+cs3_reset(cs3_t * s)
+{
+ SANE_Status status;
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "e0 00 80 00 00 00 00 00 0d 00");
+ s->n_send += 13;
+
+ status = cs3_grow_send_buffer(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return cs3_issue_and_execute(s);
+}
+
+
+static SANE_Status
+cs3_reserve_unit(cs3_t * s)
+{
+ DBG(10, "%s\n", __func__);
+
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "16 00 00 00 00 00");
+ return cs3_issue_cmd(s);
+}
+
+static SANE_Status
+cs3_release_unit(cs3_t * s)
+{
+ DBG(10, "%s\n", __func__);
+
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "17 00 00 00 00 00");
+ return cs3_issue_cmd(s);
+}
+
+
+static SANE_Status
+cs3_set_focus(cs3_t * s)
+{
+ DBG(6, "%s: setting focus to %d\n", __func__, s->focus);
+
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "e0 00 c1 00 00 00 00 00 09 00 00");
+ cs3_pack_long(s, s->focus);
+ cs3_parse_cmd(s, "00 00 00 00");
+
+ return cs3_issue_and_execute(s);
+}
+
+static SANE_Status
+cs3_read_focus(cs3_t * s)
+{
+ SANE_Status status;
+
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "e1 00 c1 00 00 00 00 00 0d 00");
+ s->n_recv = 13;
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->focus =
+ 65536 * (256 * s->recv_buf[1] + s->recv_buf[2]) +
+ 256 * s->recv_buf[3] + s->recv_buf[4];
+
+ DBG(4, "%s: focus at %d\n", __func__, s->focus);
+
+ return status;
+}
+
+static SANE_Status
+cs3_autofocus(cs3_t * s)
+{
+ SANE_Status status;
+
+ DBG(6, "%s: focusing at %ld,%ld\n", __func__,
+ s->real_focusx, s->real_focusy);
+
+ cs3_convert_options(s);
+
+ status = cs3_read_focus(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* set parameter, autofocus */
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "e0 00 a0 00 00 00 00 00 09 00 00");
+ cs3_pack_long(s, s->real_focusx);
+ cs3_pack_long(s, s->real_focusy);
+ /*cs3_parse_cmd(s, "00 00 00 00"); */
+
+ status = cs3_issue_and_execute(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return cs3_read_focus(s);
+}
+
+static SANE_Status
+cs3_autoexposure(cs3_t * s, int wb)
+{
+ SANE_Status status;
+
+ DBG(6, "%s, wb = %d\n", __func__, wb);
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ status = cs3_scan(s, wb ? CS3_SCAN_AE_WB : CS3_SCAN_AE);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = cs3_get_exposure(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->exposure = 1.;
+ s->exposure_r = s->real_exposure[1] / 100.;
+ s->exposure_g = s->real_exposure[2] / 100.;
+ s->exposure_b = s->real_exposure[3] / 100.;
+
+ return status;
+}
+
+static SANE_Status
+cs3_get_exposure(cs3_t * s)
+{
+ SANE_Status status;
+ int i_color, colors = s->n_colors;
+
+ DBG(6, "%s\n", __func__);
+
+ if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000))
+ colors = 3;
+
+ cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+
+ /* GET WINDOW */
+ for (i_color = 0; i_color < colors; i_color++) { /* XXXXXXXXXXXXX CCCCCCCCCCCCC */
+
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "25 01 00 00 00");
+ cs3_pack_byte(s, cs3_colors[i_color]);
+ cs3_parse_cmd(s, "00 00 3a 00");
+ s->n_recv = 58;
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->real_exposure[cs3_colors[i_color]] =
+ 65536 * (256 * s->recv_buf[54] + s->recv_buf[55]) +
+ 256 * s->recv_buf[56] + s->recv_buf[57];
+
+ DBG(6,
+ "%s, exposure for color %i: %li * 10ns\n",
+ __func__,
+ cs3_colors[i_color],
+ s->real_exposure[cs3_colors[i_color]]);
+
+ DBG(6, "%02x %02x %02x %02x\n", s->recv_buf[48],
+ s->recv_buf[49], s->recv_buf[50], s->recv_buf[51]);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_convert_options(cs3_t * s)
+{
+ int i_color;
+ unsigned long xmin, xmax, ymin, ymax;
+
+ DBG(4, "%s\n", __func__);
+
+ s->real_depth = (s->preview ? 8 : s->depth);
+ s->bytes_per_pixel = (s->real_depth > 8 ? 2 : 1);
+ s->shift_bits = 8 * s->bytes_per_pixel - s->real_depth;
+
+ DBG(12, " depth = %d, bpp = %d, shift = %d\n",
+ s->real_depth, s->bytes_per_pixel, s->shift_bits);
+
+ if (s->preview) {
+ s->real_resx = s->res_preview;
+ s->real_resy = s->res_preview;
+ } else if (s->res_independent) {
+ s->real_resx = s->resx;
+ s->real_resy = s->resy;
+ } else {
+ s->real_resx = s->res;
+ s->real_resy = s->res;
+ }
+
+ s->real_pitchx = s->resx_max / s->real_resx;
+ s->real_pitchy = s->resy_max / s->real_resy;
+
+ s->real_resx = s->resx_max / s->real_pitchx;
+ s->real_resy = s->resy_max / s->real_pitchy;
+
+ DBG(12, " resx = %d, resy = %d, pitchx = %d, pitchy = %d\n",
+ s->real_resx, s->real_resy, s->real_pitchx, s->real_pitchy);
+
+ /* The prefix "real_" refers to data in device units (1/maxdpi),
+ * "logical_" refers to resolution-dependent data.
+ */
+
+ if (s->xmin < s->xmax) {
+ xmin = s->xmin;
+ xmax = s->xmax;
+ } else {
+ xmin = s->xmax;
+ xmax = s->xmin;
+ }
+
+ if (s->ymin < s->ymax) {
+ ymin = s->ymin;
+ ymax = s->ymax;
+ } else {
+ ymin = s->ymax;
+ ymax = s->ymin;
+ }
+
+ DBG(12, " xmin = %ld, xmax = %ld\n", xmin, xmax);
+ DBG(12, " ymin = %ld, ymax = %ld\n", ymin, ymax);
+
+ s->real_xoffset = xmin;
+ s->real_yoffset =
+ ymin + (s->i_frame - 1) * s->frame_offset +
+ s->subframe / s->unit_mm;
+
+ DBG(12, " xoffset = %ld, yoffset = %ld\n",
+ s->real_xoffset, s->real_yoffset);
+
+
+ s->logical_width = (xmax - xmin + 1) / s->real_pitchx; /* XXX use mm units */
+ s->logical_height = (ymax - ymin + 1) / s->real_pitchy;
+ s->real_width = s->logical_width * s->real_pitchx;
+ s->real_height = s->logical_height * s->real_pitchy;
+
+ DBG(12, " lw = %ld, lh = %ld, rw = %ld, rh = %ld\n",
+ s->logical_width, s->logical_height,
+ s->real_width, s->real_height);
+
+ s->odd_padding = 0;
+ if ((s->bytes_per_pixel == 1) && (s->logical_width & 0x01)
+ && (s->type != CS3_TYPE_LS30) && (s->type != CS3_TYPE_LS2000))
+ s->odd_padding = 1;
+
+ if (s->focus_on_centre) {
+ s->real_focusx = s->real_xoffset + s->real_width / 2;
+ s->real_focusy = s->real_yoffset + s->real_height / 2;
+ } else {
+ s->real_focusx = s->focusx;
+ s->real_focusy =
+ s->focusy + (s->i_frame - 1) * s->frame_offset +
+ s->subframe / s->unit_mm;
+ }
+
+ DBG(12, " focusx = %ld, focusy = %ld\n",
+ s->real_focusx, s->real_focusy);
+
+ s->real_exposure[1] = s->exposure * s->exposure_r * 100.;
+ s->real_exposure[2] = s->exposure * s->exposure_g * 100.;
+ s->real_exposure[3] = s->exposure * s->exposure_b * 100.;
+
+ /* XXX IR? */
+ for (i_color = 0; i_color < 3; i_color++)
+ if (s->real_exposure[cs3_colors[i_color]] < 1)
+ s->real_exposure[cs3_colors[i_color]] = 1;
+
+ s->n_colors = 3; /* XXXXXXXXXXXXXX CCCCCCCCCCCCCC */
+ if (s->infrared)
+ s->n_colors = 4;
+
+ s->xfer_bytes_total =
+ s->bytes_per_pixel * s->n_colors * s->logical_width *
+ s->logical_height;
+
+ if (s->preview)
+ s->infrared = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_set_boundary(cs3_t * s)
+{
+ SANE_Status status;
+ int i_boundary;
+
+ /* Ariel - Check this function */
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "2a 00 88 00 00 03");
+ cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 16) & 0xff);
+ cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff);
+ cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff);
+ cs3_parse_cmd(s, "00");
+
+ cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff);
+ cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff);
+ cs3_pack_byte(s, s->n_frames);
+ cs3_pack_byte(s, s->n_frames);
+ for (i_boundary = 0; i_boundary < s->n_frames; i_boundary++) {
+ unsigned long lvalue = s->frame_offset * i_boundary +
+ s->subframe / s->unit_mm;
+
+ cs3_pack_long(s, lvalue);
+
+ cs3_pack_long(s, 0);
+
+ lvalue = s->frame_offset * i_boundary +
+ s->subframe / s->unit_mm + s->frame_offset - 1;
+ cs3_pack_long(s, lvalue);
+
+ cs3_pack_long(s, s->boundaryx - 1);
+
+ }
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cs3_send_lut(cs3_t * s)
+{
+ int color;
+ SANE_Status status;
+ cs3_pixel_t *lut, pixel;
+
+ DBG(6, "%s\n", __func__);
+
+ for (color = 0; color < s->n_colors; color++) {
+ /*cs3_scanner_ready(s, CS3_STATUS_READY); */
+
+ switch (color) {
+ case 0:
+ lut = s->lut_r;
+ break;
+ case 1:
+ lut = s->lut_g;
+ break;
+ case 2:
+ lut = s->lut_b;
+ break;
+ case 3:
+ lut = s->lut_neutral;
+ break;
+ default:
+ DBG(1,
+ "BUG: %s: Unknown color number for LUT download.\n",
+ __func__);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+
+ cs3_init_buffer(s);
+ cs3_parse_cmd(s, "2a 00 03 00");
+ cs3_pack_byte(s, cs3_colors[color]);
+ cs3_pack_byte(s, 2 - 1); /* XXX number of bytes per data point - 1 */
+ cs3_pack_byte(s, ((2 * s->n_lut) >> 16) & 0xff); /* XXX 2 bytes per point */
+ cs3_pack_byte(s, ((2 * s->n_lut) >> 8) & 0xff); /* XXX 2 bytes per point */
+ cs3_pack_byte(s, (2 * s->n_lut) & 0xff); /* XXX 2 bytes per point */
+ cs3_pack_byte(s, 0x00);
+
+ for (pixel = 0; pixel < s->n_lut; pixel++) { /* XXX 2 bytes per point */
+ cs3_pack_word(s, lut[pixel]);
+ }
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return status;
+}
+
+static SANE_Status
+cs3_set_window(cs3_t * s, cs3_scan_t type)
+{
+ int color;
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ /* SET WINDOW */
+ for (color = 0; color < s->n_colors; color++) {
+
+ DBG(8, "%s: color %d\n", __func__, cs3_colors[color]);
+
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+
+ cs3_init_buffer(s);
+ if ((s->type == CS3_TYPE_LS40)
+ || (s->type == CS3_TYPE_LS4000)
+ || (s->type == CS3_TYPE_LS50)
+ || (s->type == CS3_TYPE_LS5000))
+ cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 80");
+ else
+ cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 00");
+
+ cs3_parse_cmd(s, "00 00 00 00 00 00 00 32");
+
+ cs3_pack_byte(s, cs3_colors[color]);
+
+ cs3_pack_byte(s, 0x00);
+
+ cs3_pack_word(s, s->real_resx);
+ cs3_pack_word(s, s->real_resy);
+ cs3_pack_long(s, s->real_xoffset);
+ cs3_pack_long(s, s->real_yoffset);
+ cs3_pack_long(s, s->real_width);
+ cs3_pack_long(s, s->real_height);
+ cs3_pack_byte(s, 0x00); /* brightness, etc. */
+ cs3_pack_byte(s, 0x00);
+ cs3_pack_byte(s, 0x00);
+ cs3_pack_byte(s, 0x05); /* image composition CCCCCCC */
+ cs3_pack_byte(s, s->real_depth); /* pixel composition */
+ cs3_parse_cmd(s, "00 00 00 00 00 00 00 00 00 00 00 00 00");
+ cs3_pack_byte(s, ((s->samples_per_scan - 1) << 4) | 0x00); /* multiread, ordering */
+
+ cs3_pack_byte(s, 0x80 | (s->negative ? 0 : 1)); /* averaging, pos/neg */
+
+ switch (type) { /* scanning kind */
+ case CS3_SCAN_NORMAL:
+ cs3_pack_byte(s, 0x01);
+ break;
+ case CS3_SCAN_AE:
+ cs3_pack_byte(s, 0x20);
+ break;
+ case CS3_SCAN_AE_WB:
+ cs3_pack_byte(s, 0x40);
+ break;
+ default:
+ DBG(1, "BUG: cs3_scan(): Unknown scanning type.\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->samples_per_scan == 1)
+ cs3_pack_byte(s, 0x02); /* scanning mode single */
+ else
+ cs3_pack_byte(s, 0x10); /* scanning mode multi */
+ cs3_pack_byte(s, 0x02); /* color interleaving */
+ cs3_pack_byte(s, 0xff); /* (ae) */
+ if (color == 3) /* infrared */
+ cs3_parse_cmd(s, "00 00 00 00"); /* automatic */
+ else {
+ DBG(4, "%s: exposure = %ld * 10ns\n", __func__,
+ s->real_exposure[cs3_colors[color]]);
+ cs3_pack_long(s, s->real_exposure[cs3_colors[color]]);
+ }
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return status;
+}
+
+
+static SANE_Status
+cs3_scan(cs3_t * s, cs3_scan_t type)
+{
+ SANE_Status status;
+
+ s->block_padding = 0;
+
+ DBG(6, "%s, type = %d, colors = %d\n", __func__, type, s->n_colors);
+
+ switch (type) {
+ case CS3_SCAN_NORMAL:
+ DBG(16, "%s: normal scan\n", __func__);
+ break;
+ case CS3_SCAN_AE:
+ DBG(16, "%s: ae scan\n", __func__);
+ break;
+ case CS3_SCAN_AE_WB:
+ DBG(16, "%s: ae wb scan\n", __func__);
+ break;
+ }
+
+ /* wait for device to be ready with document, and set device unit */
+ status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (s->status & CS3_STATUS_NO_DOCS)
+ return SANE_STATUS_NO_DOCS;
+
+ status = cs3_convert_options(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = cs3_set_boundary(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ cs3_set_focus(s);
+
+ cs3_scanner_ready(s, CS3_STATUS_READY);
+
+ if (type == CS3_SCAN_NORMAL)
+ cs3_send_lut(s);
+
+ status = cs3_set_window(s, type);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = cs3_get_exposure(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+/* cs3_scanner_ready(s, CS3_STATUS_READY); */
+
+ cs3_init_buffer(s);
+ switch (s->n_colors) {
+ case 3:
+ cs3_parse_cmd(s, "1b 00 00 00 03 00 01 02 03");
+ break;
+ case 4:
+ cs3_parse_cmd(s, "1b 00 00 00 04 00 01 02 03 09");
+ break;
+ default:
+ DBG(0, "BUG: %s: Unknown number of input colors.\n",
+ __func__);
+ break;
+ }
+
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(6, "scan setup failed\n");
+ return status;
+ }
+
+ if (s->status == CS3_STATUS_REISSUE) {
+ status = cs3_issue_cmd(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void *
+cs3_xmalloc(size_t size)
+{
+ register void *value = malloc(size);
+
+ if (value == NULL) {
+ DBG(0, "error: %s: failed to malloc() %lu bytes.\n",
+ __func__, (unsigned long) size);
+ }
+ return value;
+}
+
+static void *
+cs3_xrealloc(void *p, size_t size)
+{
+ register void *value;
+
+ if (!size)
+ return p;
+
+ value = realloc(p, size);
+
+ if (value == NULL) {
+ DBG(0, "error: %s: failed to realloc() %lu bytes.\n",
+ __func__, (unsigned long) size);
+ }
+
+ return value;
+}
+
+static void
+cs3_xfree(const void *p)
+{
+ if (p)
+ free(p);
+}
diff --git a/backend/coolscan3.conf.in b/backend/coolscan3.conf.in
new file mode 100644
index 0000000..5150d33
--- /dev/null
+++ b/backend/coolscan3.conf.in
@@ -0,0 +1,20 @@
+# coolscan3.conf: sample configuration file for coolscan3 backend
+#
+# The following entrie checks for your scanner by manufacturer (SCSI)
+# and by vendor and product ID (USB). This is what the backend does when
+# no configuration file can be found.
+#
+auto
+
+# You can also configure the backend for specific device files, but this
+# should not normally be necessary (under Linux at least).
+# Syntax for specific devices: <interface>:<device>
+#
+# For a SCSI scanner, uncomment and edit the following line:
+#scsi:/dev/scanner
+#
+# For a USB scanner, uncomment and edit the following line:
+#usb:/dev/usbscanner
+#
+# For an IEEE 1394 scanner, use the SBP2 protocol (under Linux, use the
+# sbp2 kernel module), and your scanner will be handled as a SCSI device.
diff --git a/backend/dc210.c b/backend/dc210.c
new file mode 100644
index 0000000..acfe99a
--- /dev/null
+++ b/backend/dc210.c
@@ -0,0 +1,1531 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ dc210.c
+
+ 11/11/98
+
+ This file (C) 1998 Brian J. Murrell
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-210
+ digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
+
+ (feedback to: sane-dc210@interlinx.bc.ca
+
+ This backend is based somewhat on the dc25 backend included in this
+ package by Peter Fales
+
+ ***************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "../include/sane/sanei_jpeg.h"
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME dc210
+#include "../include/sane/sanei_backend.h"
+
+#include "dc210.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MAGIC (void *)0xab730324
+#define DC210_CONFIG_FILE "dc210.conf"
+#define THUMBSIZE 20736
+
+#ifdef B115200
+# define DEFAULT_BAUD_RATE B115200
+#else
+# define DEFAULT_BAUD_RATE B38400
+#endif
+
+#if defined (__sgi)
+# define DEFAULT_TTY "/dev/ttyd1" /* Irix */
+#elif defined (__sun)
+# define DEFAULT_TTY "/dev/term/a" /* Solaris */
+#elif defined (hpux)
+# define DEFAULT_TTY "/dev/tty1d0" /* HP-UX */
+#elif defined (__osf__)
+# define DEFAULT_TTY "/dev/tty00" /* Digital UNIX */
+#else
+# define DEFAULT_TTY "/dev/ttyS0" /* Linux */
+#endif
+
+static SANE_Bool is_open = 0;
+
+static SANE_Bool dc210_opt_thumbnails;
+static SANE_Bool dc210_opt_snap;
+static SANE_Bool dc210_opt_lowres;
+static SANE_Bool dc210_opt_erase;
+static SANE_Bool dumpinquiry;
+
+static struct jpeg_decompress_struct cinfo;
+static djpeg_dest_ptr dest_mgr = NULL;
+
+static unsigned long cmdrespause = 250000UL; /* pause after sending cmd */
+static unsigned long breakpause = 1000000UL; /* pause after sending break */
+
+static int bytes_in_buffer;
+static int bytes_read_from_buffer;
+static int total_bytes_read;
+
+static DC210 Camera;
+
+static SANE_Range image_range = {
+ 0,
+ 14,
+ 0
+};
+
+static SANE_Option_Descriptor sod[] = {
+ {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define D25_OPT_IMAGE_SELECTION 1
+ {
+ "",
+ "Image Selection",
+ "Selection of the image to load.",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC210_OPT_IMAGE_NUMBER 2
+ {
+ "image",
+ "Image Number",
+ "Select Image Number to load from camera",
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ 4,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(void *) & image_range}
+ }
+ ,
+
+#define DC210_OPT_THUMBS 3
+ {
+ "thumbs",
+ "Load Thumbnail",
+ "Load the image as thumbnail.",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC210_OPT_SNAP 4
+ {
+ "snap",
+ "Snap new picture",
+ "Take new picture and download it",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ ,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC210_OPT_LOWRES 5
+ {
+ "lowres",
+ "Low Resolution",
+ "Resolution of new pictures",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE
+ /* | SANE_CAP_ADVANCED */ ,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC210_OPT_ERASE 6
+ {
+ "erase",
+ "Erase",
+ "Erase the picture after downloading",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC210_OPT_DEFAULT 7
+ {
+ "default-enhancements",
+ "Defaults",
+ "Set default values for enhancement controls.",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC210_OPT_INIT_DC210 8
+ {
+ "camera-init",
+ "Re-establish Communications",
+ "Re-establish communications with camera (in case of timeout, etc.)",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+};
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+
+
+
+static unsigned char shoot_pck[] = SHOOT_PCK;
+static unsigned char init_pck[] = INIT_PCK;
+static unsigned char thumb_pck[] = THUMBS_PCK;
+static unsigned char pic_pck[] = PICS_PCK;
+static unsigned char pic_info_pck[] = PICS_INFO_PCK;
+static unsigned char info_pck[] = INFO_PCK;
+static unsigned char erase_pck[] = ERASE_PCK;
+static unsigned char res_pck[] = RES_PCK;
+
+static struct pkt_speed speeds[] = SPEEDS;
+static struct termios tty_orig;
+
+#include <sys/time.h>
+#include <unistd.h>
+
+static int
+send_pck (int fd, unsigned char *pck)
+{
+ int n;
+ unsigned char r = 0xf0; /* prime the loop with a "camera busy" */
+
+ /* keep trying if camera says it's busy */
+ while (r == 0xf0)
+ {
+ /*
+ * Not quite sure why we need this, but the program works a whole
+ * lot better (at least on the DC210) with this short delay.
+ */
+
+ if (write (fd, (char *) pck, 8) != 8)
+ {
+ DBG (2, "send_pck: error: write returned -1\n");
+ return -1;
+ }
+ /* need to wait before we read command result */
+ usleep (cmdrespause);
+
+ if ((n = read (fd, (char *) &r, 1)) != 1)
+ {
+ DBG (2, "send_pck: error: read returned -1\n");
+ return -1;
+ }
+ }
+ return (r == 0xd1) ? 0 : -1;
+}
+
+static int
+init_dc210 (DC210 * camera)
+{
+ struct termios tty_new;
+ int speed_index;
+
+ for (speed_index = 0; speed_index < NELEMS (speeds); speed_index++)
+ {
+ if (speeds[speed_index].baud == camera->baud)
+ {
+ init_pck[2] = speeds[speed_index].pkt_code[0];
+ init_pck[3] = speeds[speed_index].pkt_code[1];
+ break;
+ }
+ }
+
+ if (init_pck[2] == 0)
+ {
+ DBG (2, "unsupported baud rate.\n");
+ return -1;
+ }
+
+ /*
+ Open device file.
+ */
+ if ((camera->fd = open (camera->tty_name, O_RDWR)) == -1)
+ {
+ DBG (2, "init_dc210: error: could not open %s for read/write\n",
+ camera->tty_name);
+ return -1;
+ }
+ /*
+ Save old device information to restore when we are done.
+ */
+ if (tcgetattr (camera->fd, &tty_orig) == -1)
+ {
+ DBG (2, "init_dc210: error: could not get attributes\n");
+ return -1;
+ }
+
+ memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios));
+ /*
+ We need the device to be raw. 8 bits even parity on 9600 baud to start.
+ */
+#ifdef HAVE_CFMAKERAW
+ cfmakeraw (&tty_new);
+#else
+ /* Modified to set the port REALLY as required. Code inspired by
+ the gPhoto2 serial port setup */
+
+ /* input control settings */
+ tty_new.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | IUCLC |
+ IXANY | IXON | IXOFF | INPCK | ISTRIP);
+ tty_new.c_iflag |= (BRKINT | IGNPAR);
+ /* output control settings */
+ tty_new.c_oflag &= ~OPOST;
+ /* hardware control settings */
+ tty_new.c_cflag = (tty_new.c_cflag & ~CSIZE) | CS8;
+ tty_new.c_cflag &= ~(PARENB | PARODD | CSTOPB);
+# if defined(__sgi)
+ tty_new.c_cflag &= ~CNEW_RTSCTS;
+# else
+/* OS/2 doesn't have CRTSCTS - will this work for them? */
+# ifdef CRTSCTS
+ tty_new.c_cflag &= ~CRTSCTS;
+# endif
+# endif
+ tty_new.c_cflag |= CLOCAL | CREAD;
+#endif
+ /* line discipline settings */
+ tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | ECHOE |
+ ECHOK | IEXTEN);
+ tty_new.c_cc[VMIN] = 0;
+ tty_new.c_cc[VTIME] = 5;
+ cfsetospeed (&tty_new, B9600);
+ cfsetispeed (&tty_new, B9600);
+
+ if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (2, "init_dc210: error: could not set attributes\n");
+ return -1;
+ }
+
+ /* send a break to get it back to a known state */
+ /* Used to supply a non-zero argument to tcsendbreak(), TCSBRK,
+ * and TCSBRKP, but that is system dependent. e.g. on irix a non-zero
+ * value does a drain instead of a break. A zero value is universally
+ * used to send a break.
+ */
+
+#ifdef HAVE_TCSENDBREAK
+ tcsendbreak (camera->fd, 0);
+# if defined(__sgi)
+ tcdrain (camera->fd);
+# endif
+# elif defined(TCSBRKP)
+ ioctl (camera->fd, TCSBRKP, 0);
+# elif defined(TCSBRK)
+ ioctl (camera->fd, TCSBRK, 0);
+#endif
+
+ /* and wait for it to recover from the break */
+
+#ifdef HAVE_USLEEP
+ usleep (breakpause);
+#else
+ sleep (1);
+#endif
+
+ if (send_pck (camera->fd, init_pck) == -1)
+ {
+ /*
+ * The camera always powers up at 9600, so we try
+ * that first. However, it may be already set to
+ * a different speed. Try the entries in the table:
+ */
+
+ for (speed_index = NELEMS (speeds) - 1; speed_index > 0; speed_index--)
+ {
+ int x;
+ DBG (3, "init_dc210: changing speed to %d\n",
+ (int) speeds[speed_index].baud);
+
+ cfsetospeed (&tty_new, speeds[speed_index].baud);
+ cfsetispeed (&tty_new, speeds[speed_index].baud);
+
+ if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (2, "init_dc210: error: could not set attributes\n");
+ return -1;
+ }
+ for (x = 0; x < 3; x++)
+ if (send_pck (camera->fd, init_pck) != -1)
+ break;
+ }
+
+ if (speed_index == 0)
+ {
+ tcsetattr (camera->fd, TCSANOW, &tty_orig);
+ DBG (2, "init_dc210: error: no suitable baud rate\n");
+ return -1;
+ }
+ }
+ /*
+ Set speed to requested speed.
+ */
+ cfsetospeed (&tty_new, Camera.baud);
+ cfsetispeed (&tty_new, Camera.baud);
+
+ if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (2, "init_dc210: error: could not set attributes\n");
+ return -1;
+ }
+
+ return camera->fd;
+}
+
+static void
+close_dc210 (int fd)
+{
+ /*
+ * Put the camera back to 9600 baud
+ */
+
+ if (close (fd) == -1)
+ {
+ DBG (4, "close_dc210: error: could not close device\n");
+ }
+}
+
+int
+get_info (DC210 * camera)
+{
+
+ char f[] = "get_info";
+ unsigned char buf[256];
+
+ if (send_pck (camera->fd, info_pck) == -1)
+ {
+ DBG (2, "%s: error: send_pck returned -1\n", f);
+ return -1;
+ }
+
+ DBG (9, "%s: read info packet\n", f);
+
+ if (read_data (camera->fd, buf, 256) == -1)
+ {
+ DBG (2, "%s: error: read_data returned -1\n", f);
+ return -1;
+ }
+
+ if (end_of_data (camera->fd) == -1)
+ {
+ DBG (2, "%s: error: end_of_data returned -1\n", f);
+ return -1;
+ }
+
+ camera->model = buf[1];
+ camera->ver_major = buf[2];
+ camera->ver_minor = buf[3];
+ camera->pic_taken = buf[56] << 8 | buf[57];
+ camera->pic_left = buf[72] << 8 | buf[73];
+ camera->flags.low_res = buf[22];
+ camera->flags.low_batt = buf[8];
+
+ return 0;
+}
+
+static int
+read_data (int fd, unsigned char *buf, int sz)
+{
+ unsigned char ccsum;
+ unsigned char rcsum;
+ unsigned char c;
+ int n;
+ int r = 0;
+ int i;
+
+/* read the control byte */
+ if (read (fd, &c, 1) != 1)
+ {
+ DBG (2,
+ "read_data: error: read for packet control byte returned bad status\n");
+ return -1;
+ }
+ if (c != 1)
+ {
+ DBG (2, "read_data: error: incorrect packet control byte: %02x\n", c);
+ return -1;
+ }
+ for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0; n += r)
+ ;
+
+ if (r <= 0)
+ {
+ DBG (2, "read_data: error: read returned -1\n");
+ return -1;
+ }
+
+ if (n < sz || read (fd, &rcsum, 1) != 1)
+ {
+ DBG (2, "read_data: error: buffer underrun or no checksum\n");
+ return -1;
+ }
+
+ for (i = 0, ccsum = 0; i < n; i++)
+ ccsum ^= buf[i];
+
+ if (ccsum != rcsum)
+ {
+ DBG (2, "read_data: error: bad checksum (%02x !=%02x)\n", rcsum, ccsum);
+ return -1;
+ }
+
+ c = 0xd2;
+
+ if (write (fd, (char *) &c, 1) != 1)
+ {
+ DBG (2, "read_data: error: write ack\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+end_of_data (int fd)
+{
+ unsigned char c;
+
+ do
+ { /* loop until the camera isn't busy */
+ if (read (fd, &c, 1) != 1)
+ {
+ DBG (2, "end_of_data: error: read returned -1\n");
+ return -1;
+ }
+ if (c == 0) /* got successful end of data */
+ return 0; /* return success */
+ sleep (1); /* not too fast */
+ }
+ while (c == 0xf0);
+
+ /* Accck! Not busy, but not a good end of data either */
+ if (c != 0)
+ {
+ DBG (2, "end_of_data: error: bad EOD from camera (%02x)\n",
+ (unsigned) c);
+ return -1;
+ }
+ return 0; /* should never get here but shut gcc -Wall up */
+}
+
+static int
+erase (int fd)
+{
+ if (send_pck (fd, erase_pck) == -1)
+ {
+ DBG (3, "erase: error: send_pck returned -1\n");
+ return -1;
+ }
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (3, "erase: error: end_of_data returned -1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+change_res (int fd, unsigned char res)
+{
+ char f[] = "change_res";
+
+ DBG (127, "%s called\n", f);
+ if (res != 0 && res != 1)
+ {
+ DBG (3, "%s: error: unsupported resolution\n", f);
+ return -1;
+ }
+
+ /* cameras resolution semantics are opposite of ours */
+ res = !res;
+ DBG (127, "%s: setting res to %d\n", f, res);
+ res_pck[2] = res;
+
+ if (send_pck (fd, res_pck) == -1)
+ {
+ DBG (4, "%s: error: send_pck returned -1\n", f);
+ }
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (4, "%s: error: end_of_data returned -1\n", f);
+ }
+ return 0;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+
+ char f[] = "sane_init";
+ char dev_name[PATH_MAX], *p;
+ size_t len;
+ FILE *fp;
+ int baud;
+
+ DBG_INIT ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (DC210_CONFIG_FILE);
+
+ /* defaults */
+ Camera.baud = DEFAULT_BAUD_RATE;
+ Camera.tty_name = DEFAULT_TTY;
+
+ if (!fp)
+ {
+ /* default to /dev/whatever instead of insisting on config file */
+ DBG (1, "%s: missing config file '%s'\n", f, DC210_CONFIG_FILE);
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ dev_name[sizeof (dev_name) - 1] = '\0';
+ DBG (20, "%s: config- %s\n", f, dev_name);
+
+ if (dev_name[0] == '#')
+ continue; /* ignore line comments */
+ len = strlen (dev_name);
+ if (!len)
+ continue; /* ignore empty lines */
+ if (strncmp (dev_name, "port=", 5) == 0)
+ {
+ p = strchr (dev_name, '/');
+ if (p)
+ Camera.tty_name = strdup (p);
+ DBG (20, "Config file port=%s\n", Camera.tty_name);
+ }
+ else if (strncmp (dev_name, "baud=", 5) == 0)
+ {
+ baud = atoi (&dev_name[5]);
+ switch (baud)
+ {
+ case 9600:
+ Camera.baud = B9600;
+ break;
+ case 19200:
+ Camera.baud = B19200;
+ break;
+ case 38400:
+ Camera.baud = B38400;
+ break;
+#ifdef B57600
+ case 57600:
+ Camera.baud = B57600;
+ break;
+#endif
+#ifdef B115200
+ case 115200:
+ Camera.baud = B115200;
+ break;
+#endif
+ }
+ DBG (20, "Config file baud=%d\n", Camera.baud);
+ }
+ else if (strcmp (dev_name, "dumpinquiry") == 0)
+ {
+ dumpinquiry = SANE_TRUE;
+ }
+ else if (strncmp (dev_name, "cmdrespause=", 12) == 0)
+ {
+ cmdrespause = atoi (&dev_name[12]);
+ DBG (20, "Config file cmdrespause=%lu\n", cmdrespause);
+ }
+ else if (strncmp (dev_name, "breakpause=", 11) == 0)
+ {
+ breakpause = atoi (&dev_name[11]);
+ DBG (20, "Config file breakpause=%lu\n", breakpause);
+ }
+ }
+ fclose (fp);
+ }
+
+ if (init_dc210 (&Camera) == -1)
+ return SANE_STATUS_INVAL;
+
+ if (get_info (&Camera) == -1)
+ {
+ DBG (2, "error: could not get info\n");
+ close_dc210 (Camera.fd);
+ return SANE_STATUS_INVAL;
+ }
+ if (Camera.pic_taken == 0)
+ {
+ sod[DC210_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ image_range.min = 0;
+ image_range.max = 0;
+ }
+ else
+ {
+ sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ image_range.min = 1;
+ image_range.max = Camera.pic_taken;
+ }
+
+
+ /* load the current images array */
+ Camera.Pictures = get_pictures_info ();
+
+ if (Camera.pic_taken == 0)
+ {
+ Camera.current_picture_number = 0;
+ parms.bytes_per_line = 0;
+ parms.pixels_per_line = 0;
+ parms.lines = 0;
+ }
+ else
+ {
+ Camera.current_picture_number = 1;
+ if (Camera.Pictures[Camera.current_picture_number - 1].low_res)
+ {
+ parms.bytes_per_line = 640 * 3;
+ parms.pixels_per_line = 640;
+ parms.lines = 480;
+ }
+ else
+ {
+ parms.bytes_per_line = 1152 * 3;
+ parms.pixels_per_line = 1152;
+ parms.lines = 864;
+ }
+ }
+
+ if (dumpinquiry)
+ {
+ DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n");
+ DBG (0, "Model...........: DC%x\n", Camera.model);
+ DBG (0, "Firmware version: %d.%d\n", Camera.ver_major,
+ Camera.ver_minor);
+ DBG (0, "Pictures........: %d/%d\n", Camera.pic_taken,
+ Camera.pic_taken + Camera.pic_left);
+ DBG (0, "Resolution......: %s\n",
+ Camera.flags.low_res ? "low" : "high");
+ DBG (0, "Battery state...: %s\n",
+ Camera.flags.low_batt ? "low" : "good");
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+}
+
+/* Device select/open/close */
+
+static const SANE_Device dev[] = {
+ {
+ "0",
+ "Kodak",
+ "DC-210",
+ "still camera"},
+};
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ static const SANE_Device *devlist[] = {
+ dev + 0, 0
+ };
+
+ DBG (127, "sane_get_devices called\n");
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ int i;
+
+ DBG (127, "sane_open for device %s\n", devicename);
+ if (!devicename[0])
+ {
+ i = 0;
+ }
+ else
+ {
+ for (i = 0; i < NELEMS (dev); ++i)
+ {
+ if (strcmp (devicename, dev[i].name) == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ if (i >= NELEMS (dev))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_open)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ is_open = 1;
+ *handle = MAGIC;
+
+ DBG (3, "sane_open: pictures taken=%d\n", Camera.pic_taken);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (127, "sane_close called\n");
+ if (handle == MAGIC)
+ is_open = 0;
+
+ DBG (127, "sane_close returning\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ if (handle != MAGIC || !is_open)
+ return NULL; /* wrong device */
+ if (option < 0 || option >= NELEMS (sod))
+ return NULL;
+ return &sod[option];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Int myinfo = 0;
+ SANE_Status status;
+
+ DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n",
+ handle, sod[option].title,
+ (action ==
+ SANE_ACTION_SET_VALUE ? "SET" : (action ==
+ SANE_ACTION_GET_VALUE ? "GET" :
+ "SETAUTO")), value, (void *)info);
+
+ if (handle != MAGIC || !is_open)
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (option < 0 || option >= NELEMS (sod))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_VALUE:
+ status = sanei_constrain_value (sod + option, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "Constraint error in control_option\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ case DC210_OPT_IMAGE_NUMBER:
+ Camera.current_picture_number = *(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ /* get the image's resolution */
+
+ if (Camera.Pictures[Camera.current_picture_number - 1].low_res)
+ {
+ parms.bytes_per_line = 640 * 3;
+ parms.pixels_per_line = 640;
+ parms.lines = 480;
+ }
+ else
+ {
+ parms.bytes_per_line = 1152 * 3;
+ parms.pixels_per_line = 1152;
+ parms.lines = 864;
+ }
+ break;
+
+ case DC210_OPT_THUMBS:
+ dc210_opt_thumbnails = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ if (dc210_opt_thumbnails)
+ {
+ /*
+ * DC210 thumbnail are 96x72x8x3
+ */
+ parms.bytes_per_line = 96 * 3;
+ parms.pixels_per_line = 96;
+ parms.lines = 72;
+ }
+ else
+ {
+ if (Camera.Pictures[Camera.current_picture_number - 1].low_res)
+ {
+ parms.bytes_per_line = 640 * 3;
+ parms.pixels_per_line = 640;
+ parms.lines = 480;
+ }
+ else
+ {
+ parms.bytes_per_line = 1152 * 3;
+ parms.pixels_per_line = 1152;
+ parms.lines = 864;
+ }
+ }
+ break;
+
+ case DC210_OPT_SNAP:
+ dc210_opt_snap = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ /* if we are snapping a new one */
+ if (dc210_opt_snap)
+ {
+ /* activate the resolution setting */
+ sod[DC210_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE;
+ /* and de-activate the image number selector */
+ sod[DC210_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* deactivate the resolution setting */
+ sod[DC210_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
+ /* and activate the image number selector */
+ sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ }
+ /* set params according to resolution settings */
+ if (dc210_opt_lowres)
+ {
+ parms.bytes_per_line = 640 * 3;
+ parms.pixels_per_line = 640;
+ parms.lines = 480;
+ }
+ else
+ {
+ parms.bytes_per_line = 1152 * 3;
+ parms.pixels_per_line = 1152;
+ parms.lines = 864;
+ }
+ break;
+
+ case DC210_OPT_LOWRES:
+ dc210_opt_lowres = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ if (!dc210_opt_thumbnails)
+ {
+
+/* XXX - change the number of pictures left depending on resolution
+ perhaps just call get_info again?
+ */
+ if (dc210_opt_lowres)
+ {
+ parms.bytes_per_line = 640 * 3;
+ parms.pixels_per_line = 640;
+ parms.lines = 480;
+ }
+ else
+ {
+ parms.bytes_per_line = 1152 * 3;
+ parms.pixels_per_line = 1152;
+ parms.lines = 864;
+ }
+
+ }
+ break;
+
+ case DC210_OPT_ERASE:
+ dc210_opt_erase = !!*(SANE_Word *) value;
+ break;
+
+ case DC210_OPT_DEFAULT:
+ DBG (1, "Fixme: Set all defaults here!\n");
+ break;
+ case DC210_OPT_INIT_DC210:
+ if ((Camera.fd = init_dc210 (&Camera)) == -1)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ case 0:
+ *(SANE_Word *) value = NELEMS (sod);
+ break;
+
+ case DC210_OPT_IMAGE_NUMBER:
+ *(SANE_Word *) value = Camera.current_picture_number;
+ break;
+
+ case DC210_OPT_THUMBS:
+ *(SANE_Word *) value = dc210_opt_thumbnails;
+ break;
+
+ case DC210_OPT_SNAP:
+ *(SANE_Word *) value = dc210_opt_snap;
+ break;
+
+ case DC210_OPT_LOWRES:
+ *(SANE_Word *) value = dc210_opt_lowres;
+ break;
+
+ case DC210_OPT_ERASE:
+ *(SANE_Word *) value = dc210_opt_erase;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ switch (option)
+ {
+ default:
+ return SANE_STATUS_UNSUPPORTED; /* We are DUMB */
+ }
+ }
+
+ if (info)
+ *info = myinfo;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int rc = SANE_STATUS_GOOD;
+
+ DBG (127, "sane_get_params called\n");
+
+ if (handle != MAGIC || !is_open)
+ rc = SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ parms.last_frame = SANE_TRUE; /* Have no idea what this does */
+ *params = parms;
+ DBG (127, "sane_get_params return %d\n", rc);
+ return rc;
+}
+
+typedef struct
+{
+ struct jpeg_source_mgr pub;
+ JOCTET *buffer;
+}
+my_source_mgr;
+typedef my_source_mgr *my_src_ptr;
+
+METHODDEF (void)
+sanei_jpeg_init_source (j_decompress_ptr __sane_unused__ cinfo)
+{
+ /* nothing to do */
+}
+
+METHODDEF (boolean) sanei_jpeg_fill_input_buffer (j_decompress_ptr cinfo)
+{
+
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (read_data (Camera.fd, src->buffer, 1024) == -1)
+ {
+ DBG (5, "sane_start: read_data failed\n");
+ src->buffer[0] = (JOCTET) 0xFF;
+ src->buffer[1] = (JOCTET) JPEG_EOI;
+ return FALSE;
+ }
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = 1024;
+
+ return TRUE;
+}
+
+METHODDEF (void)
+sanei_jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (num_bytes > 0)
+ {
+ while (num_bytes > (long) src->pub.bytes_in_buffer)
+ {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ (void) sanei_jpeg_fill_input_buffer (cinfo);
+ }
+ }
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+}
+
+METHODDEF (void)
+sanei_jpeg_term_source (j_decompress_ptr __sane_unused__ cinfo)
+{
+ /* no work necessary here */
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+
+ DBG (127, "sane_start called\n");
+ if (handle != MAGIC || !is_open ||
+ (Camera.current_picture_number == 0 && dc210_opt_snap == SANE_FALSE))
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (Camera.scanning)
+ return SANE_STATUS_EOF;
+
+ if (dc210_opt_snap)
+ {
+
+ /*
+ * Don't allow picture unless there is room in the
+ * camera.
+ */
+ if (Camera.pic_left == 0)
+ {
+ DBG (3, "No room to store new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ if (snap_pic (Camera.fd) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "Failed to snap new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (dc210_opt_thumbnails)
+ {
+
+ thumb_pck[3] = (unsigned char) Camera.current_picture_number - 1;
+ thumb_pck[4] = 1;
+
+ if (send_pck (Camera.fd, thumb_pck) == -1)
+ {
+ DBG (4, "sane_start: error: send_pck returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ parms.bytes_per_line = 96 * 3;
+ parms.pixels_per_line = 96;
+ parms.lines = 72;
+
+ bytes_in_buffer = 0;
+ bytes_read_from_buffer = 0;
+
+ }
+ else
+ {
+ my_src_ptr src;
+
+ struct jpeg_error_mgr jerr;
+ int row_stride;
+
+ pic_pck[3] = (unsigned char) Camera.current_picture_number - 1;
+
+ if (send_pck (Camera.fd, pic_pck) == -1)
+ {
+ DBG (4, "sane_start: error: send_pck returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_decompress (&cinfo);
+
+ cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr));
+ src = (my_src_ptr) cinfo.src;
+
+ src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &
+ cinfo,
+ JPOOL_PERMANENT,
+ 1024 *
+ sizeof (JOCTET));
+ src->pub.init_source = sanei_jpeg_init_source;
+ src->pub.fill_input_buffer = sanei_jpeg_fill_input_buffer;
+ src->pub.skip_input_data = sanei_jpeg_skip_input_data;
+ src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */
+ src->pub.term_source = sanei_jpeg_term_source;
+ src->pub.bytes_in_buffer = 0;
+ src->pub.next_input_byte = NULL;
+
+ (void) jpeg_read_header (&cinfo, TRUE);
+ dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo);
+ (void) jpeg_start_decompress (&cinfo);
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ }
+
+ Camera.scanning = SANE_TRUE; /* don't overlap scan requests */
+ total_bytes_read = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle __sane_unused__ handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+
+ static char buffer[1024];
+
+ if (dc210_opt_thumbnails)
+ {
+ if (total_bytes_read == THUMBSIZE)
+ {
+ if (dc210_opt_erase)
+ {
+ if (erase (Camera.fd) == -1)
+ {
+ DBG (1, "Failed to erase memory\n");
+ return SANE_STATUS_INVAL;
+ }
+ Camera.pic_taken--;
+ Camera.pic_left++;
+ Camera.current_picture_number = Camera.pic_taken;
+ image_range.max--;
+ }
+ return SANE_STATUS_EOF;
+ }
+
+ *length = 0;
+ if (!(bytes_in_buffer - bytes_read_from_buffer))
+ {
+ if (read_data (Camera.fd, (unsigned char *) buffer, 1024) == -1)
+ {
+ DBG (5, "sane_read: read_data failed\n");
+ return SANE_STATUS_INVAL;
+ }
+ bytes_in_buffer = 1024;
+ bytes_read_from_buffer = 0;
+ }
+
+ while (bytes_read_from_buffer < bytes_in_buffer &&
+ max_length && total_bytes_read < THUMBSIZE)
+ {
+ *data++ = buffer[bytes_read_from_buffer++];
+ (*length)++;
+ max_length--;
+ total_bytes_read++;
+ }
+
+ if (total_bytes_read == THUMBSIZE)
+ {
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (4, "sane_read: end_of_data error\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ int lines = 0;
+
+ if (cinfo.output_scanline >= cinfo.output_height)
+ {
+ /* clean up comms with the camera */
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (2, "sane_read: error: end_of_data returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (dc210_opt_erase)
+ {
+ DBG (127, "sane_read bp%d, erase image\n", __LINE__);
+ if (erase (Camera.fd) == -1)
+ {
+ DBG (1, "Failed to erase memory\n");
+ return SANE_STATUS_INVAL;
+ }
+ Camera.pic_taken--;
+ Camera.pic_left++;
+ Camera.current_picture_number = Camera.pic_taken;
+ image_range.max--;
+ }
+ return SANE_STATUS_EOF;
+ }
+
+/* XXX - we should read more than 1 line at a time here */
+ lines = 1;
+ (void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines);
+ (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) data);
+ *length = cinfo.output_width * cinfo.output_components * lines;
+
+ return SANE_STATUS_GOOD;
+
+ }
+}
+
+void
+sane_cancel (SANE_Handle __sane_unused__ handle)
+{
+ DBG (127, "sane_cancel() called\n");
+ if (Camera.scanning)
+ Camera.scanning = SANE_FALSE; /* done with scan */
+ else
+ DBG (127, "sane_cancel() aborted, scanner not scanning\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+static PictureInfo *
+get_pictures_info (void)
+{
+
+ char f[] = "get_pictures_info";
+ unsigned int p;
+ PictureInfo *pics;
+
+ if ((pics = (PictureInfo *) malloc (Camera.pic_taken *
+ sizeof (PictureInfo))) == NULL)
+ {
+ DBG (4, "%s: error: allocate memory for pictures array\n", f);
+ return NULL;
+ }
+
+ for (p = 0; p < (unsigned int) Camera.pic_taken; p++)
+ {
+ if (get_picture_info (pics + p, p) == -1)
+ {
+ free (pics);
+ return NULL;
+ }
+ }
+
+ return pics;
+}
+
+static int
+get_picture_info (PictureInfo * pic, int p)
+{
+
+ char f[] = "get_picture_info";
+ static char buffer[256];
+
+ DBG (4, "%s: info for pic #%d\n", f, p);
+
+ pic_info_pck[3] = (unsigned char) p;
+
+ if (send_pck (Camera.fd, pic_info_pck) == -1)
+ {
+ DBG (4, "%s: error: send_pck returned -1\n", f);
+ return -1;
+ }
+
+ if (read_data (Camera.fd, (unsigned char *) buffer, 256) == -1)
+ {
+ DBG (2, "%s: error: read_data returned -1\n", f);
+ return -1;
+ }
+
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (2, "%s: error: end_of_data returned -1\n", f);
+ return -1;
+ }
+
+ if (buffer[3] == 0)
+ {
+ pic->low_res = SANE_TRUE;
+ }
+ else if (buffer[3] == 1)
+ {
+ pic->low_res = SANE_FALSE;
+ }
+ else
+ {
+ DBG (2, "%s: error: unknown resolution code %u\n", f, buffer[3]);
+ return -1;
+ }
+ pic->size = (buffer[8] & 0xFF) << 24;
+ pic->size |= (buffer[9] & 0xFF) << 16;
+ pic->size |= (buffer[10] & 0xFF) << 8;
+ pic->size |= (buffer[11] & 0xFF);
+
+ return 0;
+}
+
+static SANE_Status
+snap_pic (int fd)
+{
+
+ char f[] = "snap_pic";
+
+ /* make sure camera is set to our settings state */
+ if (change_res (Camera.fd, dc210_opt_lowres) == -1)
+ {
+ DBG (1, "%s: Failed to set resolution\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* take the picture */
+ if (send_pck (fd, shoot_pck) == -1)
+ {
+ DBG (4, "%s: error: send_pck returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (2, "%s: error: end_of_data returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ Camera.pic_taken++;
+ Camera.pic_left--;
+ Camera.current_picture_number = Camera.pic_taken;
+ image_range.max++;
+ sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+
+ /* add this one to the Pictures array */
+ if ((Camera.Pictures =
+ (PictureInfo *) realloc (Camera.Pictures,
+ Camera.pic_taken * sizeof (PictureInfo))) ==
+ NULL)
+ {
+ DBG (4, "%s: error: allocate memory for pictures array\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (get_picture_info (Camera.Pictures + Camera.pic_taken,
+ Camera.pic_taken) == -1)
+ {
+ DBG (1, "%s: Failed to get new picture info\n", f);
+ /* XXX - I guess we should try to erase the image here */
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/dc210.conf.in b/backend/dc210.conf.in
new file mode 100644
index 0000000..9281043
--- /dev/null
+++ b/backend/dc210.conf.in
@@ -0,0 +1,29 @@
+# Serial port where the camera is connected
+## Linux
+port=/dev/ttyS0
+## IRIX
+#port=/dev/ttyd1
+## Solaris
+#port=/dev/term/a
+## HP-UX
+#port=/dev/tty0p0
+## Digital UNIX
+#port=/dev/tty01
+# Max baud rate for download. Camera always starts at 9600 baud, then
+# switches to the higher rate
+## This works for Linux. Also works for IRIX (6.3 or higher), providing that
+## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or
+## a newer SGI hardware [see serial(7)].
+#baud=115200
+## This works for most UNIX's
+baud=38400
+# Prints some extra information during the init phase. This can be
+# handy, but note that printing anything to stderr breaks the saned
+# network scanning.
+#dumpinquiry
+# How many usec (1,000,000ths of a) between writing the command and reading the
+# result. 125000 seems to be the lowest I could go reliably.
+cmdrespause=125000
+# How many usec (1,000,000ths of a) between sending the "back to default" break
+# sending commands.
+breakpause=1000000;
diff --git a/backend/dc210.h b/backend/dc210.h
new file mode 100644
index 0000000..fe2b633
--- /dev/null
+++ b/backend/dc210.h
@@ -0,0 +1,279 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ dc210.c
+
+ 11/11/98
+
+ This file (C) 1998 Brian J. Murrell
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-210
+ digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
+
+ (feedback to: sane-dc210@interlinx.bc.ca
+
+ This backend is based somewhat on the dc25 backend included in this
+ package by Peter Fales
+
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE (1==1)
+#endif
+
+#ifndef FALSE
+#define FALSE (!TRUE)
+#endif
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+typedef struct picture_info
+ {
+ unsigned int low_res;
+ unsigned int size;
+ }
+PictureInfo;
+
+typedef struct DC210_s
+ {
+ int fd; /* file descriptor to talk to it */
+ char *tty_name; /* the tty port name it's on */
+ speed_t baud; /* current tty speed */
+ SANE_Bool scanning; /* currently scanning an image? */
+ unsigned char model;
+ unsigned char ver_major;
+ unsigned char ver_minor;
+ int pic_taken;
+ int pic_left;
+ struct
+ {
+ unsigned int low_res:1;
+ unsigned int low_batt:1;
+ }
+ flags;
+ PictureInfo *Pictures; /* array of pictures */
+ unsigned int current_picture_number; /* picture being operated on */
+ }
+DC210;
+
+typedef struct dc210_info_s
+ {
+ unsigned char model;
+ unsigned char ver_major;
+ unsigned char ver_minor;
+ int pic_taken;
+ int pic_left;
+ struct
+ {
+ unsigned int low_res:1;
+ unsigned int low_batt:1;
+ }
+ flags;
+ }
+Dc210Info, *Dc210InfoPtr;
+
+static int get_info (DC210 *);
+
+#define INIT_PCK {0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^^^^^^^
+ * Baud rate: (see pkt_speed structure)
+ * 0x96 0x00 -> 9600 baud
+ * 0x19 0x20 -> 19200 baud
+ * 0x38 0x40 -> 38400 baud
+ * 0x57 0x60 -> 57600 baud
+ * 0x11 0x52 -> 115200 baud
+ */
+#define INFO_PCK {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define SHOOT_PCK {0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define ERASE_PCK {0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define RES_PCK {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Resolution: 0x00 = high, 0x01 = low
+ */
+#define THUMBS_PCK {0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Thumbnail number
+ */
+#define PICS_PCK {0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Picture number
+ */
+#define PICS_INFO_PCK {0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Picture number
+ */
+
+struct pkt_speed
+ {
+ speed_t baud;
+ unsigned char pkt_code[2];
+ };
+
+#if defined (B57600) && defined (B115200)
+# define SPEEDS { { B9600, { 0x96, 0x00 } }, \
+ { B19200, { 0x19, 0x20 } }, \
+ { B38400, { 0x38, 0x40 } }, \
+ { B57600, { 0x57, 0x60 } }, \
+ { B115200, { 0x11, 0x52 } } }
+#else
+# define SPEEDS { { B9600, { 0x96, 0x00 } }, \
+ { B19200, { 0x19, 0x20 } }, \
+ { B38400, { 0x38, 0x40 } } }
+#endif
+
+#define HIGH_RES 0
+#define LOW_RES 1
+
+/*
+ * Image parameters
+ */
+
+#define LOW_CAMERA_HEADER 256
+#define HIGH_CAMERA_HEADER 512
+#define CAMERA_HEADER(r) ( (r) ? LOW_CAMERA_HEADER : HIGH_CAMERA_HEADER )
+
+#define LOW_WIDTH 256
+#define HIGH_WIDTH 512
+#define WIDTH(r) ( (r) ? LOW_WIDTH : HIGH_WIDTH )
+
+#define HEIGHT 243
+
+#define LEFT_MARGIN 1
+
+#define LOW_RIGHT_MARGIN 5
+#define HIGH_RIGHT_MARGIN 10
+#define RIGHT_MARGIN(r) ( (r) ? LOW_RIGHT_MARGIN : HIGH_RIGHT_MARGIN )
+
+#define TOP_MARGIN 1
+
+#define BOTTOM_MARGIN 1
+
+#define BLOCK_SIZE 1024
+
+#define LOW_BLOCKS 61
+#define HIGH_BLOCKS 122
+#define BLOCKS(r) ( (r) ? LOW_BLOCKS : HIGH_BLOCKS )
+
+#define LOW_IMAGE_SIZE ( LOW_BLOCKS * BLOCK_SIZE )
+#define HIGH_IMAGE_SIZE ( HIGH_BLOCKS * BLOCK_SIZE )
+#define IMAGE_SIZE(r) ( (r) ? LOW_IMAGE_SIZE : HIGH_IMAGE_SIZE )
+#define MAX_IMAGE_SIZE ( HIGH_IMAGE_SIZE )
+
+/*
+ * Comet file
+ */
+
+#define COMET_MAGIC "COMET"
+#define COMET_HEADER_SIZE 128
+#define COMET_EXT "cmt"
+
+/*
+ * Pixmap structure
+ */
+
+struct pixmap
+ {
+ int width;
+ int height;
+ int components;
+ unsigned char *planes;
+ };
+
+/*
+ * Rotations
+ */
+
+#define ROT_STRAIGHT 0x00
+#define ROT_LEFT 0x01
+#define ROT_RIGHT 0x02
+#define ROT_HEADDOWN 0x03
+
+#define ROT_MASK 0x03
+
+/*
+ * File formats
+ */
+
+#define SAVE_RAW 0x01
+#define SAVE_GREYSCALE 0x02
+#define SAVE_24BITS 0x04
+#define SAVE_FILES 0x07
+#define SAVE_FORMATS 0x38
+#define SAVE_ADJASPECT 0x80
+
+/*
+ * External definitions
+ */
+
+extern char *__progname; /* Defined in /usr/lib/crt0.o */
+
+
+
+#include <sys/types.h>
+
+FILE *sanei_config_open (const char *filename);
+
+static int init_dc210 (DC210 *);
+
+static void close_dc210 (int);
+
+static int read_data (int fd, unsigned char *buf, int sz);
+
+static int end_of_data (int fd);
+
+static PictureInfo *get_pictures_info (void);
+
+static int get_picture_info (PictureInfo * pic, int p);
+
+static SANE_Status snap_pic (int);
+
+char *sanei_config_read (char *str, int n, FILE * stream);
diff --git a/backend/dc240.c b/backend/dc240.c
new file mode 100644
index 0000000..001a937
--- /dev/null
+++ b/backend/dc240.c
@@ -0,0 +1,2150 @@
+/***************************************************************************
+ * _S_A_N_E - Scanner Access Now Easy.
+
+ dc240.c
+
+ 03/12/01 - Peter Fales
+
+ Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is
+ based on dc25 driver (C) 1998 by Peter Fales)
+
+ This file (C) 2001 by Peter Fales
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-240
+ digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
+
+ (feedback to: dc240-devel@fales-lorenz.net)
+
+ This backend is based somewhat on the dc25 backend included in this
+ package by Peter Fales, and the dc210 backend by Brian J. Murrell
+
+ ***************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "../include/sane/sanei_jpeg.h"
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME dc240
+#include "../include/sane/sanei_backend.h"
+
+#include "dc240.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MAGIC (void *)0xab730324
+#define DC240_CONFIG_FILE "dc240.conf"
+#define THUMBSIZE 20736
+
+#ifdef B115200
+# define DEFAULT_BAUD_RATE B115200
+#else
+# define DEFAULT_BAUD_RATE B38400
+#endif
+
+#if defined (__sgi)
+# define DEFAULT_TTY "/dev/ttyd1" /* Irix */
+#elif defined (__sun)
+# define DEFAULT_TTY "/dev/term/a" /* Solaris */
+#elif defined (hpux)
+# define DEFAULT_TTY "/dev/tty1d0" /* HP-UX */
+#elif defined (__osf__)
+# define DEFAULT_TTY "/dev/tty00" /* Digital UNIX */
+#else
+# define DEFAULT_TTY "/dev/ttyS0" /* Linux */
+#endif
+
+static SANE_Bool is_open = 0;
+
+static SANE_Bool dc240_opt_thumbnails;
+static SANE_Bool dc240_opt_snap;
+static SANE_Bool dc240_opt_lowres;
+static SANE_Bool dc240_opt_erase;
+static SANE_Bool dc240_opt_autoinc;
+static SANE_Bool dumpinquiry;
+
+static struct jpeg_decompress_struct cinfo;
+static djpeg_dest_ptr dest_mgr = NULL;
+
+static unsigned long cmdrespause = 250000UL; /* pause after sending cmd */
+static unsigned long breakpause = 1000000UL; /* pause after sending break */
+
+static DC240 Camera;
+
+static SANE_Range image_range = {
+ 0,
+ 0,
+ 0
+};
+
+static SANE_String **folder_list;
+static SANE_Int current_folder = 0;
+
+static SANE_Option_Descriptor sod[] = {
+ {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_IMAGE_SELECTION 1
+ {
+ "",
+ "Image Selection",
+ "Selection of the image to load.",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_FOLDER 2
+ {
+ "folder",
+ "Folder",
+ "Select folder within camera",
+ SANE_TYPE_STRING,
+ SANE_UNIT_NONE,
+ 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_STRING_LIST,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_IMAGE_NUMBER 3
+ {
+ "image",
+ "Image Number",
+ "Select Image Number to load from camera",
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ 4,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & image_range} /* this is ANSI conformant! */
+ }
+ ,
+
+#define DC240_OPT_THUMBS 4
+ {
+ "thumbs",
+ "Load Thumbnail",
+ "Load the image as thumbnail.",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC240_OPT_SNAP 5
+ {
+ "snap",
+ "Snap new picture",
+ "Take new picture and download it",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ ,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC240_OPT_LOWRES 6
+ {
+ "lowres",
+ "Low Resolution",
+ "Resolution of new pictures",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE
+ /* | SANE_CAP_ADVANCED */ ,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_ERASE 7
+ {
+ "erase",
+ "Erase",
+ "Erase the picture after downloading",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_DEFAULT 8
+ {
+ "default-enhancements",
+ "Defaults",
+ "Set default values for enhancement controls.",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_INIT_DC240 9
+ {
+ "camera-init",
+ "Re-establish Communications",
+ "Re-establish communications with camera (in case of timeout, etc.)",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC240_OPT_AUTOINC 10
+ {
+ "autoinc",
+ "Auto Increment",
+ "Increment image number after each scan",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+};
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+
+
+
+static SANE_Byte shoot_pck[] = SHOOT_PCK;
+static SANE_Byte init_pck[] = INIT_PCK;
+static SANE_Byte thumb_pck[] = THUMBS_PCK;
+static SANE_Byte pic_pck[] = PICS_PCK;
+static SANE_Byte pic_info_pck[] = PICS_INFO_PCK;
+static SANE_Byte info_pck[] = INFO_PCK;
+static SANE_Byte erase_pck[] = ERASE_PCK;
+static SANE_Byte res_pck[] = RES_PCK;
+static SANE_Byte open_card_pck[] = OPEN_CARD_PCK;
+static SANE_Byte read_dir_pck[] = READ_DIR_PCK;
+
+static struct pkt_speed speeds[] = SPEEDS;
+static struct termios tty_orig;
+
+SANE_Byte dir_buf2[2 + CAMDIRENTRYSIZE * DIRENTRIES];
+
+static struct cam_dirlist *dir_head = NULL;
+
+static SANE_Byte info_buf[256];
+static SANE_Byte name_buf[60];
+
+#include <sys/time.h>
+#include <unistd.h>
+
+static SANE_Int
+send_pck (SANE_Int fd, SANE_Byte * pck)
+{
+ SANE_Int n;
+ SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */
+
+ DBG (127, "send_pck<%x %x %x %x %x %x %x %x>\n",
+ pck[0], pck[1], pck[2], pck[3], pck[4], pck[5], pck[6], pck[7]);
+
+ /* keep trying if camera says it's busy */
+ while (r == 0xf0)
+ {
+ if (write (fd, (char *) pck, 8) != 8)
+ {
+ DBG (1, "send_pck: error: write returned -1\n");
+ return -1;
+ }
+ /* need to wait before we read command result */
+ usleep (cmdrespause);
+
+ if ((n = read (fd, (char *) &r, 1)) != 1)
+ {
+ DBG (1, "send_pck: error: read returned -1\n");
+ return -1;
+ }
+ }
+ DBG (127, "send_pck: read one byte result from camera = %x\n", r);
+ return (r == 0xd1) ? 0 : -1;
+}
+
+static SANE_Int
+init_dc240 (DC240 * camera)
+{
+ struct termios tty_new;
+ SANE_Int speed_index;
+ SANE_Char buf[5], n;
+
+ DBG (1, "DC-240 Backend 05/16/01\n");
+
+ for (speed_index = 0; speed_index < NELEMS (speeds); speed_index++)
+ {
+ if (speeds[speed_index].baud == camera->baud)
+ {
+ init_pck[2] = speeds[speed_index].pkt_code[0];
+ init_pck[3] = speeds[speed_index].pkt_code[1];
+ break;
+ }
+ }
+
+ if (init_pck[2] == 0)
+ {
+ DBG (1, "unsupported baud rate.\n");
+ return -1;
+ }
+
+ /*
+ Open device file.
+ */
+ if ((camera->fd = open (camera->tty_name, O_RDWR)) == -1)
+ {
+ DBG (1, "init_dc240: error: could not open %s for read/write\n",
+ camera->tty_name);
+ return -1;
+ }
+ /*
+ Save old device information to restore when we are done.
+ */
+ if (tcgetattr (camera->fd, &tty_orig) == -1)
+ {
+ DBG (1, "init_dc240: error: could not get attributes\n");
+ return -1;
+ }
+
+ memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios));
+ /*
+ We need the device to be raw. 8 bits even parity on 9600 baud to start.
+ */
+#ifdef HAVE_CFMAKERAW
+ cfmakeraw (&tty_new);
+#else
+ /* Modified to set the port REALLY as required (9600, 8b, 1sb, NO parity).
+ Code inspired by the gPhoto2 serial port setup */
+
+ /* input control settings */
+ tty_new.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | IUCLC |
+ IXANY | IXON | IXOFF | INPCK | ISTRIP);
+ tty_new.c_iflag |= (BRKINT | IGNPAR);
+ /* output control settings */
+ tty_new.c_oflag &= ~OPOST;
+ /* hardware control settings */
+ tty_new.c_cflag = (tty_new.c_cflag & ~CSIZE) | CS8;
+ tty_new.c_cflag &= ~(PARENB | PARODD | CSTOPB);
+# if defined(__sgi)
+ tty_new.c_cflag &= ~CNEW_RTSCTS;
+# else
+/* OS/2 doesn't have CRTSCTS - will this work for them? */
+# ifdef CRTSCTS
+ tty_new.c_cflag &= ~CRTSCTS;
+# endif
+# endif
+ tty_new.c_cflag |= CLOCAL | CREAD;
+#endif
+ /* line discipline settings */
+ tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | ECHOE |
+ ECHOK | IEXTEN);
+ tty_new.c_cc[VMIN] = 0;
+ tty_new.c_cc[VTIME] = 5;
+ cfsetospeed (&tty_new, B9600);
+ cfsetispeed (&tty_new, B9600);
+
+ if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (1, "init_dc240: error: could not set attributes\n");
+ return -1;
+ }
+
+ /* send a break to get it back to a known state */
+ /* Used to supply a non-zero argument to tcsendbreak(), TCSBRK,
+ * and TCSBRKP, but that is system dependent. e.g. on irix a non-zero
+ * value does a drain instead of a break. A zero value is universally
+ * used to send a break.
+ */
+
+#ifdef HAVE_TCSENDBREAK
+ tcsendbreak (camera->fd, 0);
+# if defined(__sgi)
+ tcdrain (camera->fd);
+# endif
+# elif defined(TCSBRKP)
+ ioctl (camera->fd, TCSBRKP, 0);
+# elif defined(TCSBRK)
+ ioctl (camera->fd, TCSBRK, 0);
+#endif
+
+ /* and wait for it to recover from the break */
+
+#ifdef HAVE_USLEEP
+ usleep (breakpause);
+#else
+ sleep (1);
+#endif
+
+ /* We seem to get some garbage following the break, so
+ * read anything pending */
+
+ n = read (camera->fd, buf, 5);
+
+ DBG (127, "init_dc240 flushed %d bytes: %x %x %x %x %x\n", n, buf[0],
+ buf[1], buf[2], buf[3], buf[4]);
+
+ if (send_pck (camera->fd, init_pck) == -1)
+ {
+ /*
+ * The camera always powers up at 9600, so we try
+ * that first. However, it may be already set to
+ * a different speed. Try the entries in the table:
+ */
+
+ tcsetattr (camera->fd, TCSANOW, &tty_orig);
+ DBG (1, "init_dc240: error: no response from camera\n");
+ return -1;
+ }
+
+ n = read (camera->fd, buf, 5);
+ DBG (127, "init_dc240 flushed %d bytes: %x %x %x %x %x\n", n, buf[0],
+ buf[1], buf[2], buf[3], buf[4]);
+
+ /*
+ Set speed to requested speed.
+ */
+ cfsetospeed (&tty_new, Camera.baud);
+ cfsetispeed (&tty_new, Camera.baud);
+
+ if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (1, "init_dc240: error: could not set attributes\n");
+ return -1;
+ }
+
+
+ if (send_pck (camera->fd, open_card_pck) == -1)
+ {
+ DBG (1, "init_dc240: error: send_pck returned -1\n");
+ return -1;
+ }
+
+ if (end_of_data (camera->fd) == -1)
+ {
+ DBG (1, "init_dc240: error: end_of_data returned -1\n");
+ return -1;
+ }
+
+ return camera->fd;
+
+}
+
+static void
+close_dc240 (SANE_Int fd)
+{
+ /*
+ * Put the camera back to 9600 baud
+ */
+
+ if (close (fd) == -1)
+ {
+ DBG (1, "close_dc240: error: could not close device\n");
+ }
+}
+
+int
+get_info (DC240 * camera)
+{
+
+ SANE_Char f[] = "get_info";
+ SANE_Byte buf[256];
+ SANE_Int n;
+ struct cam_dirlist *e;
+
+ if (send_pck (camera->fd, info_pck) == -1)
+ {
+ DBG (1, "%s: error: send_pck returned -1\n", f);
+ return -1;
+ }
+
+ DBG (9, "%s: read info packet\n", f);
+
+ if (read_data (camera->fd, buf, 256) == -1)
+ {
+ DBG (1, "%s: error: read_data returned -1\n", f);
+ return -1;
+ }
+
+ if (end_of_data (camera->fd) == -1)
+ {
+ DBG (1, "%s: error: end_of_data returned -1\n", f);
+ return -1;
+ }
+
+ camera->model = buf[1];
+
+ if (camera->model != 0x5)
+ {
+ DBG (0,
+ "Camera model (%d) is not DC-240 (5). "
+ "Only the DC-240 is supported by this driver.\n", camera->model);
+ }
+
+ camera->ver_major = buf[2];
+ camera->ver_minor = buf[3];
+ camera->pic_taken = buf[14] << 8 | buf[15];
+ DBG (4, "pic_taken=%d\n", camera->pic_taken);
+ camera->pic_left = buf[64] << 8 | buf[65];
+ DBG (4, "pictures left (at current res)=%d\n", camera->pic_left);
+ camera->flags.low_batt = buf[8];
+ DBG (4, "battery=%d (0=OK, 1=weak, 2=empty)\n", camera->flags.low_batt);
+ DBG (4, "AC adapter status=%d\n", buf[9]);
+ dc240_opt_lowres = !buf[79];
+
+ if (Camera.pic_taken == 0)
+ {
+ sod[DC240_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ image_range.min = 0;
+ image_range.max = 0;
+ }
+ else
+ {
+ sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ image_range.min = 1;
+ image_range.max = Camera.pic_taken;
+ }
+
+ n = read_dir ("\\PCCARD\\DCIM\\*.*");
+
+ /* If we've already got a folder_list, free it up before starting
+ * the new one
+ */
+ if (folder_list != NULL)
+ {
+ int tmp;
+ for (tmp = 0; folder_list[tmp]; tmp++)
+ {
+ free (folder_list[tmp]);
+ }
+ free (folder_list);
+ }
+
+ folder_list = (SANE_String * *)malloc ((n + 1) * sizeof (SANE_String *));
+ for (e = dir_head, n = 0; e; e = e->next, n++)
+ {
+ folder_list[n] = (SANE_String *) strdup (e->name);
+ if (strchr ((char *) folder_list[n], ' '))
+ {
+ *strchr ((char *) folder_list[n], ' ') = '\0';
+ }
+ }
+ folder_list[n] = NULL;
+ sod[DC240_OPT_FOLDER].constraint.string_list =
+ (SANE_String_Const *) folder_list;
+
+ return 0;
+
+}
+
+/* NEW */
+static SANE_Int
+read_data (SANE_Int fd, SANE_Byte * buf, SANE_Int sz)
+{
+ SANE_Byte ccsum;
+ SANE_Byte rcsum;
+ SANE_Byte c;
+ SANE_Int retries = 0;
+ SANE_Int n;
+ SANE_Int r = 0;
+ SANE_Int i;
+
+ while (retries++ < 5)
+ {
+
+ /*
+ * If this is not the first time through, then it must be
+ * a retry - signal the camera that we didn't like what
+ * we got. In either case, start filling the packet
+ */
+ if (retries != 1)
+ {
+
+ DBG (2, "Attempt retry %d\n", retries);
+ c = 0xe3;
+ if (write (fd, (char *) &c, 1) != 1)
+ {
+ DBG (1, "read_data: error: write ack\n");
+ return -1;
+ }
+
+ }
+
+ /* read the control byte */
+ if (read (fd, &c, 1) != 1)
+ {
+ DBG (3,
+ "read_data: error: "
+ "read for packet control byte returned bad stat!us\n");
+ return -1;
+ }
+ if (c != 1 && c != 0)
+ {
+ DBG (1, "read_data: error: incorrect packet control byte: %02x\n",
+ c);
+ return -1;
+ }
+
+ for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0;
+ n += r);
+
+ if (r <= 0)
+ {
+ DBG (2, "read_data: warning: read returned -1\n");
+ continue;
+ }
+
+ if (n < sz || read (fd, &rcsum, 1) != 1)
+ {
+ DBG (2, "read_data: warning: buffer underrun or no checksum\n");
+ continue;
+ }
+
+ for (i = 0, ccsum = 0; i < n; i++)
+ ccsum ^= buf[i];
+
+ if (ccsum != rcsum)
+ {
+ DBG (2,
+ "read_data: warning: "
+ "bad checksum (got %02x != expected %02x)\n", rcsum, ccsum);
+ continue;
+ }
+
+ /* If we got this far, then the packet is OK */
+ break;
+
+
+ }
+
+ c = 0xd2;
+
+ if (write (fd, (char *) &c, 1) != 1)
+ {
+ DBG (1, "read_data: error: write ack\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static SANE_Int
+end_of_data (SANE_Int fd)
+{
+ SANE_Int n;
+ SANE_Byte c;
+
+ do
+ { /* loop until the camera isn't busy */
+ if ((n = read (fd, &c, 1)) == -1)
+ {
+ DBG (1, "end_of_data: error: read returned -1\n");
+ return -1;
+ }
+ if (n == 1 && c == 0) /* got successful end of data */
+ return 0; /* return success */
+ if (n == 1)
+ {
+ DBG (127, "end_of_data: got %x while waiting\n", c);
+ }
+ else
+ {
+ DBG (127, "end_of_data: waiting...\n");
+ }
+ sleep (1); /* not too fast */
+ }
+/* It's not documented, but we see a d1 after snapping a picture */
+ while (c == 0xf0 || c == 0xd1);
+
+ /* Accck! Not busy, but not a good end of data either */
+ if (c != 0)
+ {
+ DBG (1, "end_of_data: error: bad EOD from camera (%02x)\n",
+ (unsigned) c);
+ return -1;
+ }
+ return 0; /* should never get here but shut gcc -Wall up */
+}
+
+static SANE_Int
+erase (SANE_Int fd)
+{
+ if (send_pck (fd, erase_pck) == -1)
+ {
+ DBG (1, "erase: error: send_pck returned -1\n");
+ return -1;
+ }
+
+ if (send_data (name_buf) == -1)
+ {
+ DBG (1, "erase: error: send_data returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (1, "erase: error: end_of_data returned -1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static SANE_Int
+change_res (SANE_Int fd, SANE_Byte res)
+{
+ SANE_Char f[] = "change_res";
+
+ DBG (127, "%s called, low_res=%d\n", f, res);
+
+ if (res != 0 && res != 1)
+ {
+ DBG (1, "%s: error: unsupported resolution\n", f);
+ return -1;
+ }
+
+ /* cameras resolution semantics are opposite of ours */
+ res = !res;
+ DBG (127, "%s: setting res to %d\n", f, res);
+ res_pck[2] = res;
+
+ if (send_pck (fd, res_pck) == -1)
+ {
+ DBG (1, "%s: error: send_pck returned -1\n", f);
+ }
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (1, "%s: error: end_of_data returned -1\n", f);
+ }
+ return 0;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize)
+{
+
+ SANE_Char f[] = "sane_init";
+ SANE_Char dev_name[PATH_MAX], *p;
+ size_t len;
+ FILE *fp;
+ SANE_Int baud;
+
+ DBG_INIT ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (DC240_CONFIG_FILE);
+
+ /* defaults */
+ Camera.baud = DEFAULT_BAUD_RATE;
+ Camera.tty_name = DEFAULT_TTY;
+
+ if (!fp)
+ {
+ /* default to /dev/whatever instead of insisting on config file */
+ DBG (1, "%s: missing config file '%s'\n", f, DC240_CONFIG_FILE);
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ dev_name[sizeof (dev_name) - 1] = '\0';
+ DBG (20, "%s: config- %s\n", f, dev_name);
+
+ if (dev_name[0] == '#')
+ continue; /* ignore line comments */
+ len = strlen (dev_name);
+ if (!len)
+ continue; /* ignore empty lines */
+ if (strncmp (dev_name, "port=", 5) == 0)
+ {
+ p = strchr (dev_name, '/');
+ if (p)
+ Camera.tty_name = strdup (p);
+ DBG (20, "Config file port=%s\n", Camera.tty_name);
+ }
+ else if (strncmp (dev_name, "baud=", 5) == 0)
+ {
+ baud = atoi (&dev_name[5]);
+ switch (baud)
+ {
+ case 9600:
+ Camera.baud = B9600;
+ break;
+ case 19200:
+ Camera.baud = B19200;
+ break;
+ case 38400:
+ Camera.baud = B38400;
+ break;
+#ifdef B57600
+ case 57600:
+ Camera.baud = B57600;
+ break;
+#endif
+#ifdef B115200
+ case 115200:
+ Camera.baud = B115200;
+ break;
+#endif
+ }
+ DBG (20, "Config file baud=%d\n", Camera.baud);
+ }
+ else if (strcmp (dev_name, "dumpinquiry") == 0)
+ {
+ dumpinquiry = SANE_TRUE;
+ }
+ else if (strncmp (dev_name, "cmdrespause=", 12) == 0)
+ {
+ cmdrespause = atoi (&dev_name[12]);
+ DBG (20, "Config file cmdrespause=%lu\n", cmdrespause);
+ }
+ else if (strncmp (dev_name, "breakpause=", 11) == 0)
+ {
+ breakpause = atoi (&dev_name[11]);
+ DBG (20, "Config file breakpause=%lu\n", breakpause);
+ }
+ }
+ fclose (fp);
+ }
+
+ if (init_dc240 (&Camera) == -1)
+ return SANE_STATUS_INVAL;
+
+ if (get_info (&Camera) == -1)
+ {
+ DBG (1, "error: could not get info\n");
+ close_dc240 (Camera.fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* load the current images array */
+ get_pictures_info ();
+
+ if (Camera.pic_taken == 0)
+ {
+ Camera.current_picture_number = 0;
+ parms.bytes_per_line = 0;
+ parms.pixels_per_line = 0;
+ parms.lines = 0;
+ }
+ else
+ {
+ Camera.current_picture_number = 1;
+ set_res (Camera.Pictures[Camera.current_picture_number - 1].low_res);
+ }
+
+ if (dumpinquiry)
+ {
+ DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n");
+ DBG (0, "Model...........: DC%s\n", "240");
+ DBG (0, "Firmware version: %d.%d\n", Camera.ver_major,
+ Camera.ver_minor);
+ DBG (0, "Pictures........: %d/%d\n", Camera.pic_taken,
+ Camera.pic_taken + Camera.pic_left);
+ DBG (0, "Battery state...: %s\n",
+ Camera.flags.low_batt == 0 ? "good" : (Camera.flags.low_batt ==
+ 1 ? "weak" : "empty"));
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+}
+
+/* Device select/open/close */
+
+static const SANE_Device dev[] = {
+ {
+ "0",
+ "Kodak",
+ "DC-240",
+ "still camera"},
+};
+
+static const SANE_Device *devlist[] = {
+ dev + 0, 0
+};
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool
+ UNUSEDARG local_only)
+{
+
+ DBG (127, "sane_get_devices called\n");
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Int i;
+
+ DBG (127, "sane_open for device %s\n", devicename);
+ if (!devicename[0])
+ {
+ i = 0;
+ }
+ else
+ {
+ for (i = 0; i < NELEMS (dev); ++i)
+ {
+ if (strcmp (devicename, dev[i].name) == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ if (i >= NELEMS (dev))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_open)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ is_open = 1;
+ *handle = MAGIC;
+
+ DBG (4, "sane_open: pictures taken=%d\n", Camera.pic_taken);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (127, "sane_close called\n");
+ if (handle == MAGIC)
+ is_open = 0;
+
+ DBG (127, "sane_close returning\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ if (handle != MAGIC || !is_open)
+ return NULL; /* wrong device */
+ if (option < 0 || option >= NELEMS (sod))
+ return NULL;
+ return &sod[option];
+}
+
+static SANE_Int myinfo = 0;
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Status status;
+
+ if (option < 0 || option >= NELEMS (sod))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ /* Need to put this DBG line after the range check on option */
+ DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n",
+ handle, sod[option].title,
+ (action ==
+ SANE_ACTION_SET_VALUE ? "SET" : (action ==
+ SANE_ACTION_GET_VALUE ? "GET" :
+ "SETAUTO")), value, (void *) info);
+
+ if (handle != MAGIC || !is_open)
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_VALUE:
+
+ /* Can't set disabled options */
+ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap))
+ {
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* initialize info to zero - we'll OR in various values later */
+ if (info)
+ *info = 0;
+
+ status = sanei_constrain_value (sod + option, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "Constraint error in control_option\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ case DC240_OPT_IMAGE_NUMBER:
+ if (*(SANE_Word *) value <= Camera.pic_taken)
+ Camera.current_picture_number = *(SANE_Word *) value;
+ else
+ Camera.current_picture_number = Camera.pic_taken;
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ /* get the image's resolution, unless the camera has no
+ * pictures yet
+ */
+ if (Camera.pic_taken != 0)
+ {
+ set_res (Camera.
+ Pictures[Camera.current_picture_number - 1].low_res);
+ }
+ break;
+
+ case DC240_OPT_THUMBS:
+ dc240_opt_thumbnails = !!*(SANE_Word *) value;
+
+ /* Thumbnail forces an image size change: */
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ if (Camera.pic_taken != 0)
+ {
+ set_res (Camera.
+ Pictures[Camera.current_picture_number - 1].low_res);
+ }
+ break;
+
+ case DC240_OPT_SNAP:
+ switch (*(SANE_Bool *) value)
+ {
+ case SANE_TRUE:
+ dc240_opt_snap = SANE_TRUE;
+ break;
+ case SANE_FALSE:
+ dc240_opt_snap = SANE_FALSE;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Snap forces new image size and changes image range */
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ /* if we are snapping a new one */
+ if (dc240_opt_snap)
+ {
+ /* activate the resolution setting */
+ sod[DC240_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE;
+ /* and de-activate the image number selector */
+ sod[DC240_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* deactivate the resolution setting */
+ sod[DC240_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
+ /* and activate the image number selector */
+ sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ }
+ /* set params according to resolution settings */
+ set_res (dc240_opt_lowres);
+
+ break;
+
+ case DC240_OPT_LOWRES:
+ dc240_opt_lowres = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+/* XXX - change the number of pictures left depending on resolution
+ perhaps just call get_info again?
+*/
+ set_res (dc240_opt_lowres);
+
+ break;
+
+ case DC240_OPT_ERASE:
+ dc240_opt_erase = !!*(SANE_Word *) value;
+ break;
+
+ case DC240_OPT_AUTOINC:
+ dc240_opt_autoinc = !!*(SANE_Word *) value;
+ break;
+
+ case DC240_OPT_FOLDER:
+ DBG (1, "FIXME set folder not implemented yet\n");
+ break;
+
+ case DC240_OPT_DEFAULT:
+ dc240_opt_thumbnails = 0;
+ dc240_opt_snap = 0;
+
+ /* deactivate the resolution setting */
+ sod[DC240_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
+ /* and activate the image number selector */
+ sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ DBG (1, "Fixme: Set all defaults here!\n");
+ break;
+
+ case DC240_OPT_INIT_DC240:
+ if ((Camera.fd = init_dc240 (&Camera)) == -1)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (get_info (&Camera) == -1)
+ {
+ DBG (1, "error: could not get info\n");
+ close_dc240 (Camera.fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* load the current images array */
+ get_pictures_info ();
+
+ myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_GET_VALUE:
+ /* Can't return status for disabled options */
+ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap))
+ {
+ return (SANE_STATUS_INVAL);
+ }
+
+ switch (option)
+ {
+ case 0:
+ *(SANE_Word *) value = NELEMS (sod);
+ break;
+
+ case DC240_OPT_IMAGE_NUMBER:
+ *(SANE_Word *) value = Camera.current_picture_number;
+ break;
+
+ case DC240_OPT_THUMBS:
+ *(SANE_Word *) value = dc240_opt_thumbnails;
+ break;
+
+ case DC240_OPT_SNAP:
+ *(SANE_Word *) value = dc240_opt_snap;
+ break;
+
+ case DC240_OPT_LOWRES:
+ *(SANE_Word *) value = dc240_opt_lowres;
+ break;
+
+ case DC240_OPT_ERASE:
+ *(SANE_Word *) value = dc240_opt_erase;
+ break;
+
+ case DC240_OPT_AUTOINC:
+ *(SANE_Word *) value = dc240_opt_autoinc;
+ break;
+
+ case DC240_OPT_FOLDER:
+ strcpy ((char *) value, (char *) folder_list[current_folder]);
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ switch (option)
+ {
+ default:
+ return SANE_STATUS_UNSUPPORTED; /* We are DUMB */
+ }
+ }
+
+ if (info && action == SANE_ACTION_SET_VALUE)
+ {
+ *info = myinfo;
+ myinfo = 0;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Int rc = SANE_STATUS_GOOD;
+
+ DBG (127, "sane_get_params called, wid=%d,height=%d\n",
+ parms.pixels_per_line, parms.lines);
+
+ if (handle != MAGIC || !is_open)
+ rc = SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ parms.last_frame = SANE_TRUE; /* Have no idea what this does */
+ *params = parms;
+ DBG (127, "sane_get_params return %d\n", rc);
+ return rc;
+}
+
+typedef struct
+{
+ struct jpeg_source_mgr pub;
+ JOCTET *buffer;
+}
+my_source_mgr;
+typedef my_source_mgr *my_src_ptr;
+
+METHODDEF (void)
+jpeg_init_source (j_decompress_ptr UNUSEDARG cinfo)
+{
+ /* nothing to do */
+}
+
+METHODDEF (boolean) jpeg_fill_input_buffer (j_decompress_ptr cinfo)
+{
+
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (read_data (Camera.fd, src->buffer, 512) == -1)
+ {
+ DBG (5, "sane_start: read_data failed\n");
+ src->buffer[0] = (JOCTET) 0xFF;
+ src->buffer[1] = (JOCTET) JPEG_EOI;
+ return FALSE;
+ }
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = 512;
+
+ return TRUE;
+}
+
+METHODDEF (void) jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (num_bytes > 0)
+ {
+ while (num_bytes > (long) src->pub.bytes_in_buffer)
+ {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ (void) jpeg_fill_input_buffer (cinfo);
+ }
+ }
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+}
+
+static SANE_Byte linebuffer[HIGHRES_WIDTH * 3];
+static SANE_Int linebuffer_size = 0;
+static SANE_Int linebuffer_index = 0;
+
+
+METHODDEF (void)
+jpeg_term_source (j_decompress_ptr UNUSEDARG cinfo)
+{
+ /* no work necessary here */
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ SANE_Int i;
+
+ DBG (127, "sane_start called\n");
+ if (handle != MAGIC || !is_open ||
+ (Camera.current_picture_number == 0 && dc240_opt_snap == SANE_FALSE))
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (Camera.scanning)
+ return SANE_STATUS_EOF;
+
+/*
+ * This shouldn't normally happen, but we allow it as a special case
+ * when batch/autoinc are in effect. The first illegal picture number
+ * terminates the scan
+ */
+ if (Camera.current_picture_number > Camera.pic_taken)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dc240_opt_snap)
+ {
+ /*
+ * Don't allow picture unless there is room in the
+ * camera.
+ */
+ if (Camera.pic_left == 0)
+ {
+ DBG (3, "No room to store new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ if (snap_pic (Camera.fd) == SANE_STATUS_INVAL)
+ {
+ DBG (1, "Failed to snap new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (dc240_opt_thumbnails)
+ {
+
+ if (send_pck (Camera.fd, thumb_pck) == -1)
+ {
+ DBG (1, "sane_start: error: send_pck returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Picture size should have been set when thumbnails were
+ * selected. But, check just in case
+ */
+
+ if (parms.pixels_per_line != 160 ||
+ parms.bytes_per_line != 160 * 3 || parms.lines != 120)
+ {
+ DBG (1, "sane_start: fixme! thumbnail image size is wrong\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ {
+ if (send_pck (Camera.fd, pic_pck) == -1)
+ {
+ DBG (1, "sane_start: error: send_pck returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ }
+ {
+ my_src_ptr src;
+
+ struct jpeg_error_mgr jerr;
+ SANE_Int row_stride, n;
+ SANE_Char f[] = "sane_start";
+ SANE_Char path[256];
+ struct cam_dirlist *e;
+ name_buf[0] = 0x80;
+
+ for (n = 1, e = dir_head; e; n++, e = e->next)
+ {
+ if (n == Camera.current_picture_number)
+ break;
+ }
+
+ strcpy (path, "\\PCCARD\\DCIM\\");
+ strcat (path, (char *) folder_list[current_folder]);
+ strcat (path, "\\");
+ strcat (path, e->name);
+ path[strlen (path) - 3] = '\0';
+ strcat (path, ".JPG");
+
+ DBG (9, "%s: pic to read is %d name is %s\n", f, n, path);
+
+ strcpy ((char *) &name_buf[1], path);
+ for (i = 49; i <= 56; i++)
+ {
+ name_buf[i] = 0xff;
+ }
+
+ if (send_data (name_buf) == -1)
+ {
+ DBG (1, "%s: error: send_data returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_decompress (&cinfo);
+
+ cinfo.src =
+ (struct jpeg_source_mgr *) (*cinfo.mem->
+ alloc_small) ((j_common_ptr) & cinfo,
+ JPOOL_PERMANENT,
+ sizeof (my_source_mgr));
+ src = (my_src_ptr) cinfo.src;
+
+ src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &
+ cinfo,
+ JPOOL_PERMANENT,
+ 1024 *
+ sizeof (JOCTET));
+ src->pub.init_source = jpeg_init_source;
+ src->pub.fill_input_buffer = jpeg_fill_input_buffer;
+ src->pub.skip_input_data = jpeg_skip_input_data;
+ src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */
+ src->pub.term_source = jpeg_term_source;
+ src->pub.bytes_in_buffer = 0;
+ src->pub.next_input_byte = NULL;
+
+ (void) jpeg_read_header (&cinfo, TRUE);
+ dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo);
+ (void) jpeg_start_decompress (&cinfo);
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ linebuffer_size = 0;
+ linebuffer_index = 0;
+ }
+
+ Camera.scanning = SANE_TRUE; /* don't overlap scan requests */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle UNUSEDARG handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ SANE_Int lines = 0;
+ SANE_Char filename_buf[256];
+
+ if (Camera.scanning == SANE_FALSE)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ /* If there is anything in the buffer, satisfy the read from there */
+ if (linebuffer_size && linebuffer_index < linebuffer_size)
+ {
+ *length = linebuffer_size - linebuffer_index;
+
+ if (*length > max_length)
+ {
+ *length = max_length;
+ }
+ memcpy (data, linebuffer + linebuffer_index, *length);
+ linebuffer_index += *length;
+
+ return SANE_STATUS_GOOD;
+ }
+
+ if (cinfo.output_scanline >= cinfo.output_height)
+ {
+ *length = 0;
+
+ /* clean up comms with the camera */
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (1, "sane_read: error: end_of_data returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (dc240_opt_erase)
+ {
+ DBG (127, "sane_read bp%d, erase image\n", __LINE__);
+ if (erase (Camera.fd) == -1)
+ {
+ DBG (1, "Failed to erase memory\n");
+ return SANE_STATUS_INVAL;
+ }
+ Camera.pic_taken--;
+ Camera.pic_left++;
+ Camera.current_picture_number = Camera.pic_taken;
+ image_range.max--;
+
+ myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ strcpy ((char *) filename_buf,
+ strrchr ((char *) name_buf + 1, '\\') + 1);
+ strcpy (strrchr ((char *) filename_buf, '.'), "JPG");
+ dir_delete ((SANE_String) filename_buf);
+
+ }
+ if (dc240_opt_autoinc)
+ {
+ if (Camera.current_picture_number <= Camera.pic_taken)
+ {
+ Camera.current_picture_number++;
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ /* get the image's resolution */
+ set_res (Camera.Pictures[Camera.current_picture_number - 1].
+ low_res);
+ }
+ DBG (4, "Increment count to %d (total %d)\n",
+ Camera.current_picture_number, Camera.pic_taken);
+ }
+ return SANE_STATUS_EOF;
+ }
+
+/* XXX - we should read more than 1 line at a time here */
+ lines = 1;
+ (void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines);
+ (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) linebuffer);
+
+ *length = cinfo.output_width * cinfo.output_components * lines;
+ linebuffer_size = *length;
+ linebuffer_index = 0;
+
+ if (*length > max_length)
+ {
+ *length = max_length;
+ }
+ memcpy (data, linebuffer + linebuffer_index, *length);
+ linebuffer_index += *length;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle UNUSEDARG handle)
+{
+ unsigned char cancel_byte[] = { 0xe4 };
+
+ if (Camera.scanning)
+ {
+
+ /* Flush any pending data from the camera before continuing */
+ {
+ SANE_Int n;
+ SANE_Char flush[1024];
+ do
+ {
+ sleep (1);
+ n = read (Camera.fd, flush, 1024);
+ if (n > 0)
+ {
+ DBG (127, "%s: flushed %d bytes\n", "sane_cancel", n);
+ }
+ else
+ {
+ DBG (127, "%s: nothing to flush\n", "sane_cancel");
+ }
+ }
+ while (n > 0);
+ }
+
+ if (cinfo.output_scanline < cinfo.output_height)
+ {
+ write (Camera.fd, cancel_byte, 1);
+ }
+
+ Camera.scanning = SANE_FALSE; /* done with scan */
+ }
+ else
+ DBG (4, "sane_cancel: not scanning - nothing to do\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle UNUSEDARG handle, SANE_Bool
+ UNUSEDARG non_blocking)
+{
+ /* sane_set_io_mode() is only valid during a scan */
+ if (Camera.scanning)
+ {
+ if (non_blocking == SANE_FALSE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else
+ {
+ /* We aren't currently scanning */
+ return SANE_STATUS_INVAL;
+ }
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle UNUSEDARG handle, SANE_Int * UNUSEDARG fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * get_pictures_info - load information about all pictures currently in
+ * camera: Mainly the mapping of picture number
+ * to picture name, and the resolution of each picture.
+ */
+static PictureInfo *
+get_pictures_info (void)
+{
+ SANE_Char f[] = "get_pictures_info";
+ SANE_Char path[256];
+ SANE_Int num_pictures;
+ SANE_Int p;
+ PictureInfo *pics;
+
+ if (Camera.Pictures)
+ {
+ free (Camera.Pictures);
+ Camera.Pictures = NULL;
+ }
+
+ strcpy (path, "\\PCCARD\\DCIM\\");
+ strcat (path, (char *) folder_list[current_folder]);
+ strcat (path, "\\*.*");
+
+ num_pictures = read_dir (path);
+ if (num_pictures != Camera.pic_taken)
+ {
+ DBG (2,
+ "%s: warning: Number of pictures in directory (%d) doesn't match camera status table (%d). Using directory count\n",
+ f, num_pictures, Camera.pic_taken);
+ Camera.pic_taken = num_pictures;
+ image_range.max = num_pictures;
+ }
+
+ if ((pics = (PictureInfo *) malloc (Camera.pic_taken *
+ sizeof (PictureInfo))) == NULL)
+ {
+ DBG (1, "%s: error: allocate memory for pictures array\n", f);
+ return NULL;
+ }
+
+ for (p = 0; p < Camera.pic_taken; p++)
+ {
+ if (get_picture_info (pics + p, p) == -1)
+ {
+ free (pics);
+ return NULL;
+ }
+ }
+
+ Camera.Pictures = pics;
+ return pics;
+}
+
+static SANE_Int
+get_picture_info (PictureInfo * pic, SANE_Int p)
+{
+
+ SANE_Char f[] = "get_picture_info";
+ SANE_Int n;
+ struct cam_dirlist *e;
+
+ DBG (4, "%s: info for pic #%d\n", f, p);
+
+ for (n = 0, e = dir_head; e && n < p; n++, e = e->next)
+ ;
+
+ DBG (4, "Name is %s\n", e->name);
+
+ read_info (e->name);
+
+ /* Validate picture info
+ * byte 0 - 1 == picture info
+ * byte 1 - 5 == DC240 Camera
+ * byte 2 - 3 == JFIF file
+ * byte 6 - 0 == Image is complete
+ */
+ if (info_buf[0] != 1 || info_buf[1] != 5 || info_buf[2] != 3
+ || info_buf[6] != 0)
+ {
+
+ DBG (1, "%s: error: Image %s does not come from a DC-240.\n", f,
+ e->name);
+ return -1;
+ }
+
+ pic->low_res = info_buf[3] == 0 ? SANE_TRUE : SANE_FALSE;
+
+ /*
+ * byte 12 - Year MSB
+ * byte 13 - Year LSB
+ * byte 14 - Month
+ * byte 15 - Day
+ * byte 16 - Hour
+ * byte 17 - Minute
+ * byte 18 - Second
+ */
+ DBG (1, "Picture %d taken %02d/%02d/%02d %02d:%02d:%02d\n",
+ p, info_buf[14],
+ info_buf[15], (info_buf[12] << 8) + info_buf[13],
+ info_buf[16], info_buf[17], info_buf[18]);
+
+ return 0;
+}
+
+/*
+ * snap_pic - take a picture (and call get_pictures_info to re-create
+ * the directory related data structures)
+ */
+static SANE_Status
+snap_pic (SANE_Int fd)
+{
+
+ SANE_Char f[] = "snap_pic";
+
+#if 0
+/* Just checking... It looks like after snapping the picture, we
+ * get two "d1" ACK responses back, even though the documentation seems
+ * to say that there should only be one. I thought the first one could
+ * have been an unread response from an earlier command, so I
+ * I tried flushing any data here. Still seeing the multiple
+ * d1 bytes, however...
+ */
+ {
+ SANE_Int n;
+ SANE_Char flush[10];
+ n = read (Camera.fd, flush, 10);
+ if (n > 0)
+ {
+ DBG (127, "%s: flushed %d bytes\n", f, n);
+ }
+ else
+ {
+ DBG (127, "%s: nothing to flush\n", f);
+ }
+ }
+#endif
+
+ /* make sure camera is set to our settings state */
+ if (change_res (Camera.fd, dc240_opt_lowres) == -1)
+ {
+ DBG (1, "%s: Failed to set resolution\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* take the picture */
+ if (send_pck (fd, shoot_pck) == -1)
+ {
+ DBG (1, "%s: error: send_pck returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (1, "%s: error: end_of_data returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ Camera.pic_taken++;
+ Camera.pic_left--;
+ Camera.current_picture_number = Camera.pic_taken;
+ image_range.max++;
+ sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+
+ /* add this one to the Pictures array */
+
+ if (get_pictures_info () == NULL)
+ {
+ DBG (1, "%s: Failed to get new picture info\n", f);
+ /* XXX - I guess we should try to erase the image here */
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * read_dir - read a list of file names from the specified directory
+ * and create a linked list of file name entries in
+ * alphabetical order. The first entry in the list will
+ * be "picture #1", etc.
+ */
+static SANE_Int
+read_dir (SANE_String dir)
+{
+ SANE_Int retval = 0;
+ SANE_Byte buf[256];
+ SANE_Byte *next_buf;
+ SANE_Int i, entries;
+ SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */
+ SANE_Char f[] = "read_dir";
+ struct cam_dirlist *e, *next;
+
+ /* Free up current list */
+ for (e = dir_head; e; e = next)
+ {
+ DBG (127, "%s: free entry %s\n", f, e->name);
+ next = e->next;
+ free (e);
+ }
+ dir_head = NULL;
+
+ if (send_pck (Camera.fd, read_dir_pck) == -1)
+ {
+ DBG (1, "%s: error: send_pck returned -1\n", f);
+ return -1;
+ }
+
+ buf[0] = 0x80;
+ strcpy ((char *) &buf[1], dir);
+ for (i = 49; i <= 56; i++)
+ {
+ buf[i] = 0xff;
+ }
+
+ if (send_data (buf) == -1)
+ {
+ DBG (1, "%s: error: send_data returned -1\n", f);
+ return -1;
+ }
+
+
+ if (read_data (Camera.fd, (SANE_Byte *) & dir_buf2, 256) == -1)
+ {
+ DBG (1, "%s: error: read_data returned -1\n", f);
+ return -1;
+ }
+
+ entries = (dir_buf2[0] << 8) + dir_buf2[1];
+ DBG (127, "%s: result of dir read is %x, number of entries=%d\n", f, r,
+ entries);
+
+ if (entries > 1001)
+ {
+ DBG (1, "%s: error: more than 999 pictures not supported yet\n", f);
+ return -1;
+ }
+
+ /* Determine if it's time to read another 256 byte buffer from the camera */
+
+ next_buf = ((SANE_Byte *) & dir_buf2) + 256;
+ while (dir_buf2 + 2 + CAMDIRENTRYSIZE * entries >= (SANE_Byte *) next_buf)
+ {
+
+ DBG (127, "%s: reading additional directory buffer\n", f);
+ if (read_data (Camera.fd, next_buf, 256) == -1)
+ {
+ DBG (1, "%s: error: read_data returned -1\n", f);
+ return -1;
+ }
+ next_buf += 256;
+ }
+
+ for (i = 0; i < entries; i++)
+
+ {
+ /* Hack: I don't know what attr is used for, so setting it
+ * to zero is a convenient way to put in the null terminator
+ */
+ get_attr (i) = 0;
+ DBG (127, "%s: entry=%s\n", f, get_name (i));
+
+ if ((get_name (i))[0] == '.')
+ {
+ continue;
+ }
+
+ if (dir_insert
+ ((struct cam_dirent *) &dir_buf2[2 + CAMDIRENTRYSIZE * i]) != 0)
+ {
+ DBG (1, "%s: error: failed to insert dir entry\n", f);
+ return -1;
+ }
+ retval++;
+ }
+
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (1, "%s: error: end_of_data returned -1\n", f);
+ return -1;
+ }
+
+ return retval;
+}
+
+/*
+ * read_info - read the info block from camera for the specified file
+ */
+static SANE_Int
+read_info (SANE_String fname)
+{
+ SANE_Byte buf[256];
+ SANE_Int i;
+ SANE_Char f[] = "read_info";
+ SANE_Char path[256];
+
+ strcpy (path, "\\PCCARD\\DCIM\\");
+ strcat (path, (char *) folder_list[current_folder]);
+ strcat (path, "\\");
+ strcat (path, fname);
+ path[strlen (path) - 3] = '\0';
+ strcat (path, ".JPG");
+
+ if (send_pck (Camera.fd, pic_info_pck) == -1)
+ {
+ DBG (1, "%s: error: send_pck returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ buf[0] = 0x80;
+ strcpy ((char *) &buf[1], path);
+ for (i = 49; i <= 56; i++)
+ {
+ buf[i] = 0xff;
+ }
+
+ if (send_data (buf) == -1)
+ {
+ DBG (1, "%s: error: send_data returned -1\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (read_data (Camera.fd, info_buf, 256) != 0)
+ {
+ DBG (1, "%s: error: Failed in read_data\n", f);
+ return -1;
+ }
+
+ DBG (9, "%s: data type=%d, cam type=%d, file type=%d\n", f,
+ info_buf[0], info_buf[1], info_buf[2]);
+
+ if (end_of_data (Camera.fd) == -1)
+ {
+ DBG (1, "%s: error: end_of_data returned -1\n", f);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * send_data - Send a data block - assumes all data blocks to camera
+ * are 60 bytes long
+ */
+
+static SANE_Int
+send_data (SANE_Byte * buf)
+{
+ SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */
+ SANE_Int i, n;
+ SANE_Byte csum;
+ SANE_Char f[] = "send_data";
+
+ for (i = 1, csum = 0; i < 59; i++)
+ {
+ csum ^= buf[i];
+ }
+ buf[59] = csum;
+ DBG (127, "%s: about to send data block\n", f);
+
+ /* keep trying if camera says it's busy */
+ while (r == 0xf0)
+ {
+ if (write (Camera.fd, (char *) buf, 60) != 60)
+ {
+ DBG (1, "%s: error: write returned -1\n", f);
+ return -1;
+ }
+
+ /* need to wait before we read command result */
+#ifdef HAVE_USLEEP
+ usleep (cmdrespause);
+#else
+ sleep (1);
+#endif
+
+ if ((n = read (Camera.fd, (char *) &r, 1)) != 1)
+ {
+ DBG (1, "%s: error: read returned -1\n", f);
+ return -1;
+ }
+ }
+
+ if (r != 0xd2)
+ {
+ DBG (1, "%s: error: bad response to send_data (%d)\n", f, r);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * dir_insert - Add (in alphabetical order) a directory entry to the
+ * current list of entries.
+ */
+static SANE_Int
+dir_insert (struct cam_dirent *entry)
+{
+ struct cam_dirlist *cur, *e;
+
+ cur = (struct cam_dirlist *) malloc (sizeof (struct cam_dirlist));
+ if (cur == NULL)
+ {
+ DBG (1, "dir_insert: error: could not malloc entry\n");
+ return -1;
+ }
+
+ strcpy (cur->name, entry->name);
+ DBG (127, "dir_insert: name is %s\n", cur->name);
+
+ cur->next = NULL;
+
+ if (dir_head == NULL)
+ {
+ dir_head = cur;
+ }
+ else if (strcmp (cur->name, dir_head->name) < 0)
+ {
+ cur->next = dir_head;
+ dir_head = cur;
+ return 0;
+ }
+ else
+ {
+ for (e = dir_head; e->next; e = e->next)
+ {
+ if (strcmp (e->next->name, cur->name) > 0)
+ {
+ cur->next = e->next;
+ e->next = cur;
+ return 0;
+ }
+ }
+ e->next = cur;
+ }
+ return 0;
+}
+
+/*
+ * dir_delete - Delete a directory entry from the linked list of file
+ * names
+ */
+static SANE_Int
+dir_delete (SANE_String fname)
+{
+ struct cam_dirlist *cur, *e;
+
+ DBG (127, "dir_delete: %s\n", fname);
+
+ if (strcmp (fname, dir_head->name) == 0)
+ {
+ cur = dir_head;
+ dir_head = dir_head->next;
+ free (cur);
+ return 0;
+ }
+
+ for (e = dir_head; e->next; e = e->next)
+ {
+ if (strcmp (fname, e->next->name) == 0)
+ {
+ cur = e->next;
+ e->next = e->next->next;
+ free (cur);
+ return (0);
+ }
+ }
+ DBG (1, "dir_delete: Couldn't find entry %s in dir list\n", fname);
+ return -1;
+}
+
+/*
+ * set_res - set picture size depending on resolution settings
+ */
+static void
+set_res (SANE_Int lowres)
+{
+ if (dc240_opt_thumbnails)
+ {
+ parms.bytes_per_line = 160 * 3;
+ parms.pixels_per_line = 160;
+ parms.lines = 120;
+ }
+ else if (lowres)
+ {
+ parms.bytes_per_line = LOWRES_WIDTH * 3;
+ parms.pixels_per_line = LOWRES_WIDTH;
+ parms.lines = LOWRES_HEIGHT;
+ }
+ else
+ {
+ parms.bytes_per_line = HIGHRES_WIDTH * 3;
+ parms.pixels_per_line = HIGHRES_WIDTH;
+ parms.lines = HIGHRES_HEIGHT;
+ }
+}
diff --git a/backend/dc240.conf.in b/backend/dc240.conf.in
new file mode 100644
index 0000000..9281043
--- /dev/null
+++ b/backend/dc240.conf.in
@@ -0,0 +1,29 @@
+# Serial port where the camera is connected
+## Linux
+port=/dev/ttyS0
+## IRIX
+#port=/dev/ttyd1
+## Solaris
+#port=/dev/term/a
+## HP-UX
+#port=/dev/tty0p0
+## Digital UNIX
+#port=/dev/tty01
+# Max baud rate for download. Camera always starts at 9600 baud, then
+# switches to the higher rate
+## This works for Linux. Also works for IRIX (6.3 or higher), providing that
+## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or
+## a newer SGI hardware [see serial(7)].
+#baud=115200
+## This works for most UNIX's
+baud=38400
+# Prints some extra information during the init phase. This can be
+# handy, but note that printing anything to stderr breaks the saned
+# network scanning.
+#dumpinquiry
+# How many usec (1,000,000ths of a) between writing the command and reading the
+# result. 125000 seems to be the lowest I could go reliably.
+cmdrespause=125000
+# How many usec (1,000,000ths of a) between sending the "back to default" break
+# sending commands.
+breakpause=1000000;
diff --git a/backend/dc240.h b/backend/dc240.h
new file mode 100644
index 0000000..3323fed
--- /dev/null
+++ b/backend/dc240.h
@@ -0,0 +1,278 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ dc240.h
+
+ 03/12/01 - Peter Fales
+
+ Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is
+ based on dc25 driver (C) 1998 by Peter Fales)
+
+ This file (C) 2001 by Peter Fales
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-240
+ digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
+
+ (feedback to: dc240-devel@fales-lorenz.net)
+
+ This backend is based somewhat on the dc25 backend included in this
+ package by Peter Fales, and the dc210 backend by Brian J. Murrell
+
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE (1==1)
+#endif
+
+#ifndef FALSE
+#define FALSE (!TRUE)
+#endif
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+typedef struct picture_info
+{
+ int low_res;
+ int size;
+}
+PictureInfo;
+
+typedef struct DC240_s
+{
+ SANE_Int fd; /* file descriptor to talk to it */
+ char *tty_name; /* the tty port name it's on */
+ speed_t baud; /* current tty speed */
+ SANE_Bool scanning; /* currently scanning an image? */
+ SANE_Byte model;
+ SANE_Byte ver_major;
+ SANE_Byte ver_minor;
+ SANE_Int pic_taken;
+ SANE_Int pic_left;
+ struct
+ {
+ unsigned int low_res:1;
+ unsigned int low_batt:1;
+ }
+ flags;
+ PictureInfo *Pictures; /* array of pictures */
+ SANE_Int current_picture_number; /* picture being operated on */
+}
+DC240;
+
+typedef struct dc240_info_s
+{
+ SANE_Byte model;
+ SANE_Byte ver_major;
+ SANE_Byte ver_minor;
+ SANE_Int pic_taken;
+ SANE_Int pic_left;
+ struct
+ {
+ SANE_Int low_res:1;
+ SANE_Int low_batt:1;
+ }
+ flags;
+}
+Dc240Info, *Dc240InfoPtr;
+
+static SANE_Int get_info (DC240 *);
+
+#define INIT_PCK {0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^^^^^^^
+ * Baud rate: (see pkt_speed structure)
+ * 0x96 0x00 -> 9600 baud
+ * 0x19 0x20 -> 19200 baud
+ * 0x38 0x40 -> 38400 baud
+ * 0x57 0x60 -> 57600 baud
+ * 0x11 0x52 -> 115200 baud
+ */
+#define INFO_PCK {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define SHOOT_PCK {0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define ERASE_PCK {0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define RES_PCK {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Resolution: 0x00 = low, 0x01 = high
+ */
+#define THUMBS_PCK {0x93, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x1A}
+/*
+ *
+ */
+#define PICS_PCK {0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/*
+ *
+ */
+#define PICS_INFO_PCK {0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/*
+ *
+ */
+#define OPEN_CARD_PCK {0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define READ_DIR_PCK {0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * 1=Report number of entries only
+ */
+
+struct pkt_speed
+{
+ speed_t baud;
+ SANE_Byte pkt_code[2];
+};
+
+#if defined (B57600) && defined (B115200)
+# define SPEEDS { { B9600, { 0x96, 0x00 } }, \
+ { B19200, { 0x19, 0x20 } }, \
+ { B38400, { 0x38, 0x40 } }, \
+ { B57600, { 0x57, 0x60 } }, \
+ { B115200, { 0x11, 0x52 } } }
+#else
+# define SPEEDS { { B9600, { 0x96, 0x00 } }, \
+ { B19200, { 0x19, 0x20 } }, \
+ { B38400, { 0x38, 0x40 } } }
+#endif
+
+#define HIGH_RES 0
+#define LOW_RES 1
+
+#define HIGHRES_WIDTH 1280
+#define HIGHRES_HEIGHT 960
+
+#define LOWRES_WIDTH 640
+#define LOWRES_HEIGHT 480
+
+/*
+ * External definitions
+ */
+
+extern char *__progname; /* Defined in /usr/lib/crt0.o */
+
+
+struct cam_dirent
+{
+ SANE_Char name[11];
+ SANE_Byte attr;
+ SANE_Byte create_time[2];
+ SANE_Byte creat_date[2];
+ long size;
+};
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+#ifdef OLD
+
+/* This is the layout of the directory in the camera - Unfortunately,
+ * this only works in gcc.
+ */
+struct dir_buf
+{
+ SANE_Byte entries_msb PACKED;
+ SANE_Byte entries_lsb PACKED;
+ struct cam_dirent entry[1000] PACKED;
+};
+#else
+
+/* So, we have to do it the hard way... */
+
+#define CAMDIRENTRYSIZE 20
+#define DIRENTRIES 1000
+
+
+#define get_name(entry) (SANE_Char*) &dir_buf2[2+CAMDIRENTRYSIZE*(entry)]
+#define get_attr(entry) dir_buf2[2+11+CAMDIRENTRYSIZE*(entry)]
+#define get_create_time(entry) \
+ ( dir_buf2[2+12+CAMDIRENTRYSIZE*(entry)] << 8 \
+ + dir_buf2[2+13+CAMDIRENTRYSIZE*(entry)])
+
+
+#endif
+
+struct cam_dirlist
+{
+ SANE_Char name[48];
+ struct cam_dirlist *next;
+};
+
+
+
+#include <sys/types.h>
+
+FILE *sanei_config_open (const char *filename);
+
+static SANE_Int init_dc240 (DC240 *);
+
+static void close_dc240 (SANE_Int);
+
+static SANE_Int read_data (SANE_Int fd, SANE_Byte * buf, SANE_Int sz);
+
+static SANE_Int end_of_data (SANE_Int fd);
+
+static PictureInfo *get_pictures_info (void);
+
+static SANE_Int get_picture_info (PictureInfo * pic, SANE_Int p);
+
+static SANE_Status snap_pic (SANE_Int fd);
+
+char *sanei_config_read (char *str, int n, FILE * stream);
+
+static SANE_Int read_dir (SANE_String dir);
+
+static SANE_Int read_info (SANE_String fname);
+
+static SANE_Int dir_insert (struct cam_dirent *entry);
+
+static SANE_Int dir_delete (SANE_String name);
+
+static SANE_Int send_data (SANE_Byte * buf);
+
+static void set_res (SANE_Int lowres);
diff --git a/backend/dc25.c b/backend/dc25.c
new file mode 100644
index 0000000..2c9e78c
--- /dev/null
+++ b/backend/dc25.c
@@ -0,0 +1,2760 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ dc25.c
+
+ $Id$
+
+ This file (C) 1998 Peter Fales
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-25 (and
+ probably the DC-20) digital cameras. THIS IS EXTREMELY ALPHA CODE!
+ USE AT YOUR OWN RISK!!
+
+ (feedback to: dc25-devel@fales-lorenz.net)
+
+ This backend is based heavily on the dc20ctrl package by Ugo
+ Paternostro <paterno@dsi.unifi.it>. I've attached his header below:
+
+ ***************************************************************************
+
+ * Copyright (C) 1998 Ugo Paternostro <paterno@dsi.unifi.it>
+ *
+ * This file is part of the dc20ctrl package. The complete package can be
+ * downloaded from:
+ * http://aguirre.dsi.unifi.it/~paterno/binaries/dc20ctrl.tar.gz
+ *
+ * This package is derived from the dc20 package, built by Karl Hakimian
+ * <hakimian@aha.com> that you can find it at ftp.eecs.wsu.edu in the
+ * /pub/hakimian directory. The complete URL is:
+ * ftp://ftp.eecs.wsu.edu/pub/hakimian/dc20.tar.gz
+ *
+ * This package also includes a sligthly modified version of the Comet to ppm
+ * conversion routine written by YOSHIDA Hideki <hideki@yk.rim.or.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+
+ ***************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME dc25
+#include "../include/sane/sanei_backend.h"
+
+#include "dc25.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MAGIC (void *)0xab730324
+#define DC25_CONFIG_FILE "dc25.conf"
+#define THUMBSIZE ( (CameraInfo.model == 0x25 ) ? 14400 : 5120 )
+
+static SANE_Bool is_open = 0;
+
+static SANE_Byte dc25_opt_image_number = 1; /* Image to load */
+static SANE_Bool dc25_opt_thumbnails; /* Load thumbnails */
+static SANE_Bool dc25_opt_snap; /* Take new picture */
+static SANE_Bool dc25_opt_lowres; /* Use low resoluiton */
+#define DC25_OPT_CONTRAST_DEFAULT 1.6
+ /* Contrast enhancement */
+static SANE_Fixed dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
+#define DC25_OPT_GAMMA_DEFAULT 4.5
+ /* Gamma correction (10x) */
+static SANE_Fixed dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
+static SANE_Bool dc25_opt_erase; /* Erase all after download */
+static SANE_Bool dc25_opt_erase_one; /* Erase one after download */
+static SANE_Bool dumpinquiry;
+
+static SANE_Int info_flags;
+
+static int tfd; /* Camera File Descriptor */
+static char tty_name[PATH_MAX];
+#define DEF_TTY_NAME "/dev/ttyS0"
+
+static speed_t tty_baud = DEFAULT_TTY_BAUD;
+static char *tmpname;
+static char tmpnamebuf[] = "/tmp/dc25XXXXXX";
+
+static Dc20Info *dc20_info;
+static Dc20Info CameraInfo;
+
+static SANE_Byte contrast_table[256];
+
+static struct pixmap *pp;
+
+static const SANE_Range percentage_range = {
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 0 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static const SANE_Range contrast_range = {
+ 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 3 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 16384 /* quantization ~ 0.025 */
+};
+
+static const SANE_Range gamma_range = {
+ 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 10 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 16384 /* quantization ~ 0.025 */
+};
+
+static SANE_Range image_range = {
+ 0,
+ 14,
+ 0
+};
+
+static SANE_Option_Descriptor sod[] = {
+ {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define D25_OPT_IMAGE_SELECTION 1
+ {
+ "",
+ "Image Selection",
+ "Selection of the image to load.",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC25_OPT_IMAGE_NUMBER 2
+ {
+ "image",
+ "Image Number",
+ "Select Image Number to load from camera",
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ 4,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & image_range} /* this is ANSI conformant! */
+ }
+ ,
+
+#define DC25_OPT_THUMBS 3
+ {
+ "thumbs",
+ "Load Thumbnail",
+ "Load the image as thumbnail.",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC25_OPT_SNAP 4
+ {
+ "snap",
+ "Snap new picture",
+ "Take new picture and download it",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+#define DC25_OPT_LOWRES 5
+ {
+ "lowres",
+ "Low Resolution",
+ "New pictures taken in low resolution",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC25_OPT_ERASE 6
+ {
+ "erase",
+ "Erase",
+ "Erase all pictures after downloading",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC25_OPT_ERASE_ONE 7
+ {
+ "erase-one",
+ "Erase One",
+ "Erase downloaded picture after downloading",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC25_OPT_ENHANCE 8
+ {
+ "",
+ "Image Parameters",
+ "Modifications to image parameters",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define DC25_OPT_CONTRAST 9
+ {
+ "contrast",
+ "Contrast Adjustment",
+ "Values > 1 enhance contrast",
+ SANE_TYPE_FIXED,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &contrast_range} /* this is ANSI conformant! */
+ },
+
+#define DC25_OPT_GAMMA 10
+ {
+ "gamma",
+ "Gamma Adjustment",
+ "Larger values make image darker",
+ SANE_TYPE_FIXED,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) &gamma_range} /* this is ANSI conformant! */
+ },
+
+#define DC25_OPT_DEFAULT 11
+ {
+ "default-enhancements",
+ "Defaults",
+ "Set default values for enhancement controls (i.e. contrast).",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+};
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 1,
+ 500, /* Number of bytes returned per scan line: */
+ 500, /* Number of pixels per scan line. */
+ 373, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+
+
+
+static unsigned char init_pck[] = INIT_PCK;
+
+/*
+ * List of speeds to try to establish connection with the camera.
+ * Check 9600 first, as it's the speed the camera comes up in, then
+ * 115200, as that is the one most likely to be configured from a
+ * previous run
+ */
+static struct pkt_speed speeds[] = { {B9600, {0x96, 0x00}},
+#ifdef B115200
+{B115200, {0x11, 0x52}},
+#endif
+#ifdef B57600
+{B57600, {0x57, 0x60}},
+#endif
+{B38400, {0x38, 0x40}},
+{B19200, {0x19, 0x20}},
+};
+#define NUM_OF_SPEEDS ((int)(sizeof(speeds) / sizeof(struct pkt_speed)))
+
+static struct termios tty_orig;
+
+static int
+send_pck (int fd, unsigned char *pck)
+{
+ int n;
+ unsigned char r;
+
+ /*
+ * Not quite sure why we need this, but the program works a whole
+ * lot better (at least on the DC25) with this short delay.
+ */
+#ifdef HAVE_USLEEP
+ usleep (10);
+#else
+ sleep (1);
+#endif
+ if (write (fd, (char *) pck, 8) != 8)
+ {
+ DBG (2, "send_pck: error: write returned -1\n");
+ return -1;
+ }
+
+ if ((n = read (fd, (char *) &r, 1)) != 1)
+ {
+ DBG (2, "send_pck: error: read returned -1\n");
+ return -1;
+ }
+
+ return (r == 0xd1) ? 0 : -1;
+}
+
+static int
+init_dc20 (char *device, speed_t speed)
+{
+ struct termios tty_new;
+ int speed_index;
+
+ DBG (1, "DC-20/25 Backend 05/07/01\n");
+
+ for (speed_index = 0; speed_index < NUM_OF_SPEEDS; speed_index++)
+ {
+ if (speeds[speed_index].baud == speed)
+ {
+ init_pck[2] = speeds[speed_index].pkt_code[0];
+ init_pck[3] = speeds[speed_index].pkt_code[1];
+ break;
+ }
+ }
+
+ if (init_pck[2] == 0)
+ {
+ DBG (2, "unsupported baud rate.\n");
+ return -1;
+ }
+
+ /*
+ Open device file.
+ */
+ if ((tfd = open (device, O_RDWR)) == -1)
+ {
+ DBG (2, "init_dc20: error: could not open %s for read/write\n", device);
+ return -1;
+ }
+ /*
+ Save old device information to restore when we are done.
+ */
+ if (tcgetattr (tfd, &tty_orig) == -1)
+ {
+ DBG (2, "init_dc20: error: could not get attributes\n");
+ return -1;
+ }
+
+ memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios));
+ /*
+ We need the device to be raw. 8 bits even parity on 9600 baud to start.
+ */
+#ifdef HAVE_CFMAKERAW
+ cfmakeraw (&tty_new);
+#else
+ tty_new.c_lflag &= ~(ICANON | ECHO | ISIG);
+#endif
+ tty_new.c_oflag &= ~CSTOPB;
+ tty_new.c_cflag |= PARENB;
+ tty_new.c_cflag &= ~PARODD;
+ tty_new.c_cc[VMIN] = 0;
+ tty_new.c_cc[VTIME] = 50;
+ cfsetospeed (&tty_new, B9600);
+ cfsetispeed (&tty_new, B9600);
+
+ if (tcsetattr (tfd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (2, "init_dc20: error: could not set attributes\n");
+ return -1;
+ }
+
+ if (send_pck (tfd, init_pck) == -1)
+ {
+ /*
+ * The camera always powers up at 9600, so we try
+ * that first. However, it may be already set to
+ * a different speed. Try the entries in the table:
+ */
+
+ for (speed_index = NUM_OF_SPEEDS - 1; speed_index > 0; speed_index--)
+ {
+ DBG (3, "init_dc20: changing speed to %d\n",
+ (int) speeds[speed_index].baud);
+
+ cfsetospeed (&tty_new, speeds[speed_index].baud);
+ cfsetispeed (&tty_new, speeds[speed_index].baud);
+
+ if (tcsetattr (tfd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (2, "init_dc20: error: could not set attributes\n");
+ return -1;
+ }
+ if (send_pck (tfd, init_pck) != -1)
+ break;
+ }
+
+ if (speed_index == 0)
+ {
+ tcsetattr (tfd, TCSANOW, &tty_orig);
+ DBG (2, "init_dc20: error: no suitable baud rate\n");
+ return -1;
+ }
+ }
+ /*
+ Set speed to requested speed. Also, make a long timeout (we need this for
+ erase and shoot operations)
+ */
+ tty_new.c_cc[VTIME] = 150;
+ cfsetospeed (&tty_new, speed);
+ cfsetispeed (&tty_new, speed);
+
+ if (tcsetattr (tfd, TCSANOW, &tty_new) == -1)
+ {
+ DBG (2, "init_dc20: error: could not set attributes\n");
+ return -1;
+ }
+
+ return tfd;
+}
+
+static void
+close_dc20 (int fd)
+{
+ DBG (127, "close_dc20() called\n");
+ /*
+ * Put the camera back to 9600 baud
+ */
+
+ init_pck[2] = speeds[0].pkt_code[0];
+ init_pck[3] = speeds[0].pkt_code[1];
+ if (send_pck (fd, init_pck) == -1)
+ {
+ DBG (4, "close_dc20: error: could not set attributes\n");
+ }
+
+ /*
+ Restore original device settings.
+ */
+ if (tcsetattr (fd, TCSANOW, &tty_orig) == -1)
+ {
+ DBG (4, "close_dc20: error: could not set attributes\n");
+ }
+
+ if (close (fd) == -1)
+ {
+ DBG (4, "close_dc20: error: could not close device\n");
+ }
+}
+
+static unsigned char info_pck[] = INFO_PCK;
+
+static Dc20Info *
+get_info (int fd)
+{
+ unsigned char buf[256];
+
+ if (send_pck (fd, info_pck) == -1)
+ {
+ DBG (2, "get_info: error: send_pck returned -1\n");
+ return NULL;
+ }
+
+ DBG (9, "get_info: read info packet\n");
+
+ if (read_data (fd, buf, 256) == -1)
+ {
+ DBG (2, "get_info: error: read_data returned -1\n");
+ return NULL;
+ }
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (2, "get_info: error: end_of_data returned -1\n");
+ return NULL;
+ }
+
+ CameraInfo.model = buf[1];
+ CameraInfo.ver_major = buf[2];
+ CameraInfo.ver_minor = buf[3];
+ CameraInfo.pic_taken = buf[8] << 8 | buf[9];
+ if (CameraInfo.model == 0x25)
+ {
+
+ /* Not sure where the previous line came from. All the
+ * information I have says that even on the DC20 the number of
+ * standard res pics is in byte 17 and the number of high res pics
+ * is in byte 19. This is definitely true on my DC25.
+ */
+ CameraInfo.pic_taken = buf[17] + buf[19];
+ }
+
+ image_range.max = CameraInfo.pic_taken;
+ image_range.min = CameraInfo.pic_taken ? 1 : 0;
+
+ CameraInfo.pic_left = buf[10] << 8 | buf[11];
+
+ if (CameraInfo.model == 0x25)
+ {
+ /* Not sure where the previous line came from. All the
+ * information I have says that even on the DC20 the number of
+ * standard res pics left is in byte 23 and the number of high res
+ * pics left is in byte 21. It seems to me that the conservative
+ * approach is to report the number of high res pics left.
+ */
+ CameraInfo.pic_left = buf[21];
+ }
+ CameraInfo.flags.low_res = buf[23];
+
+ if (CameraInfo.model == 0x25)
+ {
+ /* Not sure where the previous line came from. All the
+ * information I have says that even on the DC20 the low_res
+ * byte is 11.
+ */
+ CameraInfo.flags.low_res = buf[11];
+ }
+ CameraInfo.flags.low_batt = buf[29];
+
+ return &CameraInfo;
+}
+
+static int
+read_data (int fd, unsigned char *buf, int sz)
+{
+ unsigned char ccsum;
+ unsigned char rcsum;
+ unsigned char c;
+ int retries = 0;
+ int n;
+ int r = 0;
+ int i;
+
+ while (retries++ < 5)
+ {
+
+ /*
+ * If this is not the first time through, then it must be
+ * a retry - signal the camera that we didn't like what
+ * we got. In either case, start filling the packet
+ */
+ if (retries != 1)
+ {
+
+ DBG (2, "Attempt retry %d\n", retries);
+ c = 0xe3;
+ if (write (fd, (char *) &c, 1) != 1)
+ {
+ DBG (2, "read_data: error: write ack\n");
+ return -1;
+ }
+
+ }
+
+ for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0;
+ n += r)
+ ;
+
+ if (r <= 0)
+ {
+ DBG (2, "read_data: error: read returned -1\n");
+ continue;
+ }
+
+ if (n < sz || read (fd, &rcsum, 1) != 1)
+ {
+ DBG (2, "read_data: error: buffer underrun or no checksum\n");
+ continue;
+ }
+
+ for (i = 0, ccsum = 0; i < n; i++)
+ ccsum ^= buf[i];
+
+ if (ccsum != rcsum)
+ {
+ DBG (2, "read_data: error: bad checksum (%02x != %02x)\n", rcsum,
+ ccsum);
+ continue;
+ }
+
+ /* If we got this far, then the packet is OK */
+ break;
+ }
+
+ c = 0xd2;
+
+ if (write (fd, (char *) &c, 1) != 1)
+ {
+ DBG (2, "read_data: error: write ack\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+end_of_data (int fd)
+{
+ char c;
+
+ if (read (fd, &c, 1) != 1)
+ {
+ DBG (2, "end_of_data: error: read returned -1\n");
+ return -1;
+ }
+
+ if (c != 0)
+ {
+ DBG (2, "end_of_data: error: bad EOD from camera (%02x)\n",
+ (unsigned) c);
+ return -1;
+ }
+
+ return 0;
+}
+
+#include <math.h>
+
+#define BIDIM_ARRAY(name, x, y, width) (name[((x) + ((y) * (width)))])
+
+/*
+ * These definitions depend on the resolution of the image
+ */
+
+#define MY_LOW_RIGHT_MARGIN 6
+
+/*
+ * These definitions are constant with resolution
+ */
+
+#define MY_LEFT_MARGIN 2
+
+#define NET_COLUMNS (columns - MY_LEFT_MARGIN - right_margin)
+#define NET_LINES (HEIGHT - TOP_MARGIN - BOTTOM_MARGIN)
+#define NET_PIXELS (NET_COLUMNS * NET_LINES)
+
+
+#define SCALE 64
+#define SMAX (256 * SCALE - 1)
+#define HORIZONTAL_INTERPOLATIONS 3
+#define HISTOGRAM_STEPS 4096
+
+#define RFACTOR 0.64
+#define GFACTOR 0.58
+#define BFACTOR 1.00
+#define RINTENSITY 0.476
+#define GINTENSITY 0.299
+#define BINTENSITY 0.175
+
+#define SATURATION 1.0
+#define NORM_PERCENTAGE 3
+
+static int columns = HIGH_WIDTH,
+ right_margin = HIGH_RIGHT_MARGIN, camera_header_size = HIGH_CAMERA_HEADER;
+static int low_i = -1, high_i = -1, norm_percentage = NORM_PERCENTAGE;
+static float saturation = SATURATION,
+ rfactor = RFACTOR, gfactor = GFACTOR, bfactor = BFACTOR;
+
+static void
+set_initial_interpolation (const unsigned char ccd[],
+ short horizontal_interpolation[])
+{
+ int column, line;
+ for (line = 0; line < HEIGHT; line++)
+ {
+ BIDIM_ARRAY (horizontal_interpolation, MY_LEFT_MARGIN, line, columns) =
+ BIDIM_ARRAY (ccd, MY_LEFT_MARGIN + 1, line, columns) * SCALE;
+ BIDIM_ARRAY (horizontal_interpolation, columns - right_margin - 1, line,
+ columns) =
+ BIDIM_ARRAY (ccd, columns - right_margin - 2, line, columns) * SCALE;
+ for (column = MY_LEFT_MARGIN + 1; column < columns - right_margin - 1;
+ column++)
+ {
+ BIDIM_ARRAY (horizontal_interpolation, column, line, columns) =
+ (BIDIM_ARRAY (ccd, column - 1, line, columns) +
+ BIDIM_ARRAY (ccd, column + 1, line, columns)) * (SCALE / 2);
+ }
+ }
+}
+
+static void
+interpolate_horizontally (const unsigned char ccd[],
+ short horizontal_interpolation[])
+{
+ int column, line, i, initial_column;
+ for (line = TOP_MARGIN - 1; line < HEIGHT - BOTTOM_MARGIN + 1; line++)
+ {
+ for (i = 0; i < HORIZONTAL_INTERPOLATIONS; i++)
+ {
+ for (initial_column = MY_LEFT_MARGIN + 1;
+ initial_column <= MY_LEFT_MARGIN + 2; initial_column++)
+ {
+ for (column = initial_column;
+ column < columns - right_margin - 1; column += 2)
+ {
+ BIDIM_ARRAY (horizontal_interpolation, column, line,
+ columns) =
+ ((float) BIDIM_ARRAY (ccd, column - 1, line, columns) /
+ BIDIM_ARRAY (horizontal_interpolation, column - 1, line,
+ columns) + (float) BIDIM_ARRAY (ccd,
+ column + 1,
+ line,
+ columns) /
+ BIDIM_ARRAY (horizontal_interpolation, column + 1, line,
+ columns)) * BIDIM_ARRAY (ccd, column, line,
+ columns) * (SCALE *
+ SCALE /
+ 2) +
+ 0.5;
+ }
+ }
+ }
+ }
+}
+
+static void
+interpolate_vertically (const unsigned char ccd[],
+ const short horizontal_interpolation[],
+ short red[], short green[], short blue[])
+{
+ int column, line;
+ for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
+ {
+ for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
+ {
+ int r2gb, g2b, rg2, rgb2, r, g, b;
+ int this_ccd = BIDIM_ARRAY (ccd, column, line, columns) * SCALE;
+ int up_ccd = BIDIM_ARRAY (ccd, column, line - 1, columns) * SCALE;
+ int down_ccd = BIDIM_ARRAY (ccd, column, line + 1, columns) * SCALE;
+ int this_horizontal_interpolation =
+ BIDIM_ARRAY (horizontal_interpolation, column, line, columns);
+ int this_intensity = this_ccd + this_horizontal_interpolation;
+ int up_intensity =
+ BIDIM_ARRAY (horizontal_interpolation, column, line - 1,
+ columns) + up_ccd;
+ int down_intensity =
+ BIDIM_ARRAY (horizontal_interpolation, column, line + 1,
+ columns) + down_ccd;
+ int this_vertical_interpolation;
+
+ /*
+ * PSF: I don't understand all this code, but I've found pictures
+ * where up_intensity or down_intensity are zero, resulting in a
+ * divide by zero error. It looks like this only happens when
+ * up_ccd or down_ccd are also zero, so we just set the intensity
+ * value to non-zero to prevent the error.
+ */
+ if (down_ccd == 0)
+ DBG (10, "down_ccd==0 at %d,%d\n", line, column);
+ if (up_ccd == 0)
+ DBG (10, "up_ccd==0 at %d,%d\n", line, column);
+ if (down_intensity == 0)
+ {
+ DBG (9, "Found down_intensity==0 at %d,%d down_ccd=%d\n", line,
+ column, down_ccd);
+ down_intensity = 1;
+ }
+ if (up_intensity == 0)
+ {
+ DBG (9, "Found up_intensity==0 at %d,%d up_ccd=%d\n", line,
+ column, up_ccd);
+ up_intensity = 1;
+ }
+
+ if (line == TOP_MARGIN)
+ {
+ this_vertical_interpolation =
+ (float) down_ccd / down_intensity * this_intensity + 0.5;
+ }
+ else if (line == HEIGHT - BOTTOM_MARGIN - 1)
+ {
+ this_vertical_interpolation =
+ (float) up_ccd / up_intensity * this_intensity + 0.5;
+ }
+ else
+ {
+ this_vertical_interpolation =
+ ((float) up_ccd / up_intensity +
+ (float) down_ccd / down_intensity) * this_intensity / 2.0 +
+ 0.5;
+ }
+ if (line & 1)
+ {
+ if (column & 1)
+ {
+ r2gb = this_ccd;
+ g2b = this_horizontal_interpolation;
+ rg2 = this_vertical_interpolation;
+ r = (2 * (r2gb - g2b) + rg2) / 5;
+ g = (rg2 - r) / 2;
+ b = g2b - 2 * g;
+ }
+ else
+ {
+ g2b = this_ccd;
+ r2gb = this_horizontal_interpolation;
+ rgb2 = this_vertical_interpolation;
+ r = (3 * r2gb - g2b - rgb2) / 5;
+ g = 2 * r - r2gb + g2b;
+ b = g2b - 2 * g;
+ }
+ }
+ else
+ {
+ if (column & 1)
+ {
+ rg2 = this_ccd;
+ rgb2 = this_horizontal_interpolation;
+ r2gb = this_vertical_interpolation;
+ b = (3 * rgb2 - r2gb - rg2) / 5;
+ g = (rgb2 - r2gb + rg2 - b) / 2;
+ r = rg2 - 2 * g;
+ }
+ else
+ {
+ rgb2 = this_ccd;
+ rg2 = this_horizontal_interpolation;
+ g2b = this_vertical_interpolation;
+ b = (g2b - 2 * (rg2 - rgb2)) / 5;
+ g = (g2b - b) / 2;
+ r = rg2 - 2 * g;
+ }
+ }
+ if (r < 0)
+ r = 0;
+ if (g < 0)
+ g = 0;
+ if (b < 0)
+ b = 0;
+ BIDIM_ARRAY (red, column, line, columns) = r;
+ BIDIM_ARRAY (green, column, line, columns) = g;
+ BIDIM_ARRAY (blue, column, line, columns) = b;
+ }
+ }
+}
+
+static void
+adjust_color_and_saturation (short red[], short green[], short blue[])
+{
+ int line, column;
+ int r_min = SMAX, g_min = SMAX, b_min = SMAX;
+ int r_max = 0, g_max = 0, b_max = 0;
+ int r_sum = 0, g_sum = 0, b_sum = 0;
+ float sqr_saturation = sqrt (saturation);
+ for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
+ {
+ for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
+ {
+ float r = BIDIM_ARRAY (red, column, line, columns) * rfactor;
+ float g = BIDIM_ARRAY (green, column, line, columns) * gfactor;
+ float b = BIDIM_ARRAY (blue, column, line, columns) * bfactor;
+ if (saturation != 1.0)
+ {
+ float *min, *mid, *max, new_intensity;
+ float intensity =
+ r * RINTENSITY + g * GINTENSITY + b * BINTENSITY;
+ if (r > g)
+ {
+ if (r > b)
+ {
+ max = &r;
+ if (g > b)
+ {
+ min = &b;
+ mid = &g;
+ }
+ else
+ {
+ min = &g;
+ mid = &b;
+ }
+ }
+ else
+ {
+ min = &g;
+ mid = &r;
+ max = &b;
+ }
+ }
+ else
+ {
+ if (g > b)
+ {
+ max = &g;
+ if (r > b)
+ {
+ min = &b;
+ mid = &r;
+ }
+ else
+ {
+ min = &r;
+ mid = &b;
+ }
+ }
+ else
+ {
+ min = &r;
+ mid = &g;
+ max = &b;
+ }
+ }
+ *mid = *min + sqr_saturation * (*mid - *min);
+ *max = *min + saturation * (*max - *min);
+ new_intensity =
+ r * RINTENSITY + g * GINTENSITY + b * BINTENSITY;
+ r *= intensity / new_intensity;
+ g *= intensity / new_intensity;
+ b *= intensity / new_intensity;
+ }
+ r += 0.5;
+ g += 0.5;
+ b += 0.5;
+ if (r_min > r)
+ r_min = r;
+ if (g_min > g)
+ g_min = g;
+ if (b_min > b)
+ b_min = b;
+ if (r_max < r)
+ r_max = r;
+ if (g_max < g)
+ g_max = g;
+ if (b_max < b)
+ b_max = b;
+ r_sum += r;
+ g_sum += g;
+ b_sum += b;
+ BIDIM_ARRAY (red, column, line, columns) = r;
+ BIDIM_ARRAY (green, column, line, columns) = g;
+ BIDIM_ARRAY (blue, column, line, columns) = b;
+ }
+ }
+}
+
+static int
+min3 (int x, int y, int z)
+{
+ return (x < y ? (x < z ? x : z) : (y < z ? y : z));
+}
+
+static int
+max3 (int x, int y, int z)
+{
+ return (x > y ? (x > z ? x : z) : (y > z ? y : z));
+}
+
+static void
+determine_limits (const short red[],
+ const short green[],
+ const short blue[], int *low_i_ptr, int *high_i_ptr)
+{
+ unsigned int histogram[HISTOGRAM_STEPS + 1];
+ int column, line, i, s;
+ int low_i = *low_i_ptr, high_i = *high_i_ptr;
+ int max_i = 0;
+ for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
+ {
+ for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
+ {
+ i = max3 (BIDIM_ARRAY (red, column, line, columns),
+ BIDIM_ARRAY (green, column, line, columns),
+ BIDIM_ARRAY (blue, column, line, columns));
+ if (i > max_i)
+ max_i = i;
+ }
+ }
+ if (low_i == -1)
+ {
+ for (i = 0; i <= HISTOGRAM_STEPS; i++)
+ histogram[i] = 0;
+ for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
+ {
+ for (column = MY_LEFT_MARGIN; column < columns - right_margin;
+ column++)
+ {
+ i = min3 (BIDIM_ARRAY (red, column, line, columns),
+ BIDIM_ARRAY (green, column, line, columns),
+ BIDIM_ARRAY (blue, column, line, columns));
+ histogram[i * HISTOGRAM_STEPS / max_i]++;
+ }
+ }
+ for (low_i = 0, s = 0;
+ low_i <= HISTOGRAM_STEPS && s < NET_PIXELS * norm_percentage / 100;
+ low_i++)
+ {
+ s += histogram[low_i];
+ }
+ low_i = (low_i * max_i + HISTOGRAM_STEPS / 2) / HISTOGRAM_STEPS;
+ *low_i_ptr = low_i;
+ }
+ if (high_i == -1)
+ {
+ for (i = 0; i <= HISTOGRAM_STEPS; i++)
+ histogram[i] = 0;
+ for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
+ {
+ for (column = MY_LEFT_MARGIN; column < columns - right_margin;
+ column++)
+ {
+ i = max3 (BIDIM_ARRAY (red, column, line, columns),
+ BIDIM_ARRAY (green, column, line, columns),
+ BIDIM_ARRAY (blue, column, line, columns));
+ histogram[i * HISTOGRAM_STEPS / max_i]++;
+ }
+ }
+ for (high_i = HISTOGRAM_STEPS, s = 0;
+ high_i >= 0 && s < NET_PIXELS * norm_percentage / 100; high_i--)
+ {
+ s += histogram[high_i];
+ }
+ high_i = (high_i * max_i + HISTOGRAM_STEPS / 2) / HISTOGRAM_STEPS;
+ *high_i_ptr = high_i;
+ }
+/*
+if (verbose) printf ("%s: determine_limits: low_i = %d, high_i = %d\n", __progname, low_i, high_i);
+*/
+}
+
+/*
+ * The original dc20ctrl program used a default gamma of 0.35, but I thougt
+ * 0.45 looks better. In addition, since xscanimage seems to always force
+ * a resolution of 0.1, I multiply everything by 10 and make the default
+ * 4.5.
+ */
+
+static unsigned char *
+make_gamma_table (int range)
+{
+ int i;
+ double factor =
+ pow (256.0, 1.0 / (SANE_UNFIX (dc25_opt_gamma) / 10.0)) / range;
+ unsigned char *gamma_table;
+ if ((gamma_table = malloc (range * sizeof (unsigned char))) == NULL)
+ {
+ DBG (1, "make_gamma_table: can't allocate memory for gamma table\n");
+ return NULL;
+ }
+ for (i = 0; i < range; i++)
+ {
+ int g =
+ pow ((double) i * factor, (SANE_UNFIX (dc25_opt_gamma) / 10.0)) + 0.5;
+/*
+ if (verbose) fprintf (stderr, "%s: make_gamma_table: gamma[%4d] = %3d\n", __progname, i, g);
+*/
+ if (g > 255)
+ g = 255;
+ gamma_table[i] = g;
+ }
+ return gamma_table;
+}
+
+static int
+lookup_gamma_table (int i, int low_i, int high_i,
+ const unsigned char gamma_table[])
+{
+ if (i <= low_i)
+ return 0;
+ if (i >= high_i)
+ return 255;
+ return gamma_table[i - low_i];
+}
+
+static int
+output_rgb (const short red[],
+ const short green[],
+ const short blue[], int low_i, int high_i, struct pixmap *pp)
+{
+ int r_min = 255, g_min = 255, b_min = 255;
+ int r_max = 0, g_max = 0, b_max = 0;
+ int r_sum = 0, g_sum = 0, b_sum = 0;
+ int column, line;
+ unsigned char *gamma_table = make_gamma_table (high_i - low_i);
+
+ if (gamma_table == NULL)
+ {
+ DBG (10, "output_rgb: error: cannot make gamma table\n");
+ return -1;
+ }
+
+ for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++)
+ {
+ for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++)
+ {
+ int r =
+ lookup_gamma_table (BIDIM_ARRAY (red, column, line, columns),
+ low_i, high_i, gamma_table);
+ int g =
+ lookup_gamma_table (BIDIM_ARRAY (green, column, line, columns),
+ low_i, high_i, gamma_table);
+ int b =
+ lookup_gamma_table (BIDIM_ARRAY (blue, column, line, columns),
+ low_i, high_i, gamma_table);
+ if (r > 255)
+ r = 255;
+ else if (r < 0)
+ r = 0;
+ if (g > 255)
+ g = 255;
+ else if (g < 0)
+ g = 0;
+ if (b > 255)
+ b = 255;
+ else if (b < 0)
+ b = 0;
+ set_pixel_rgb (pp, column - MY_LEFT_MARGIN, line - TOP_MARGIN, r, g,
+ b);
+ if (r_min > r)
+ r_min = r;
+ if (g_min > g)
+ g_min = g;
+ if (b_min > b)
+ b_min = b;
+ if (r_max < r)
+ r_max = r;
+ if (g_max < g)
+ g_max = g;
+ if (b_max < b)
+ b_max = b;
+ r_sum += r;
+ g_sum += g;
+ b_sum += b;
+ }
+ }
+ free (gamma_table);
+/*
+ {
+ fprintf (stderr, "%s: output_rgb: r: min = %d, max = %d, ave = %d\n", __progname, r_min, r_max, r_sum / NET_PIXELS);
+ fprintf (stderr, "%s: output_rgb: g: min = %d, max = %d, ave = %d\n", __progname, g_min, g_max, g_sum / NET_PIXELS);
+ fprintf (stderr, "%s: output_rgb: b: min = %d, max = %d, ave = %d\n", __progname, b_min, b_max, b_sum / NET_PIXELS);
+ }
+*/
+ return 0;
+}
+
+static int
+comet_to_pixmap (unsigned char *pic, struct pixmap *pp)
+{
+ unsigned char *ccd;
+ short *horizontal_interpolation, *red, *green, *blue;
+ int retval = 0;
+
+ if (pic == NULL)
+ {
+ DBG (1, "cmttoppm: error: no input image\n");
+ return -1;
+ }
+
+ if (pic[4] == 0x01)
+ {
+ /* Low resolution mode */
+ columns = LOW_WIDTH;
+ right_margin = MY_LOW_RIGHT_MARGIN;
+ camera_header_size = LOW_CAMERA_HEADER;
+ }
+ else
+ {
+ /* High resolution mode */
+ columns = HIGH_WIDTH;
+ right_margin = HIGH_RIGHT_MARGIN;
+ camera_header_size = HIGH_CAMERA_HEADER;
+ }
+ ccd = pic + camera_header_size;
+
+ if ((horizontal_interpolation =
+ malloc (sizeof (short) * HEIGHT * columns)) == NULL)
+ {
+ DBG (1,
+ "cmttoppm: error: not enough memory for horizontal_interpolation\n");
+ return -1;
+ }
+
+
+ if ((red = malloc (sizeof (short) * HEIGHT * columns)) == NULL)
+ {
+ DBG (1, "error: not enough memory for red\n");
+ return -1;
+ }
+
+ if ((green = malloc (sizeof (short) * HEIGHT * columns)) == NULL)
+ {
+ DBG (1, "error: not enough memory for green\n");
+ return -1;
+ }
+
+ if ((blue = malloc (sizeof (short) * HEIGHT * columns)) == NULL)
+ {
+ DBG (1, "error: not enough memory for blue\n");
+ return -1;
+ }
+
+ /* Decode raw CCD data to RGB */
+ set_initial_interpolation (ccd, horizontal_interpolation);
+ interpolate_horizontally (ccd, horizontal_interpolation);
+ interpolate_vertically (ccd, horizontal_interpolation, red, green, blue);
+
+ adjust_color_and_saturation (red, green, blue);
+
+ /* Determine lower and upper limit using histogram */
+ if (low_i == -1 || high_i == -1)
+ {
+ determine_limits (red, green, blue, &low_i, &high_i);
+ }
+
+ /* Output pixmap structure */
+ retval = output_rgb (red, green, blue, low_i, high_i, pp);
+
+ return retval;
+}
+
+static int
+convert_pic (char *base_name, int format)
+{
+ FILE *ifp;
+ unsigned char pic[MAX_IMAGE_SIZE];
+ int res, image_size, image_width, net_width, camera_header, components;
+ struct pixmap *pp2;
+
+ DBG (127, "convert_pic() called\n");
+
+ /*
+ * Read the image in memory
+ */
+
+ if ((ifp = fopen (base_name, "rb")) == NULL)
+ {
+ DBG (10, "convert_pic: error: cannot open %s for reading\n", base_name);
+ return -1;
+ }
+
+ if (fread (pic, COMET_HEADER_SIZE, 1, ifp) != 1)
+ {
+ DBG (10, "convert_pic: error: cannot read COMET header\n");
+ fclose (ifp);
+ return -1;
+ }
+
+ if (strncmp ((char *) pic, COMET_MAGIC, sizeof (COMET_MAGIC)) != 0)
+ {
+ DBG (10, "convert_pic: error: file %s is not in COMET format\n",
+ base_name);
+ fclose (ifp);
+ return -1;
+ }
+
+ if (fread (pic, LOW_CAMERA_HEADER, 1, ifp) != 1)
+ {
+ DBG (10, "convert_pic: error: cannot read camera header\n");
+ fclose (ifp);
+ return -1;
+ }
+
+ res = pic[4];
+ if (res == 0)
+ {
+ /*
+ * We just read a LOW_CAMERA_HEADER block, so resync with the
+ * HIGH_CAMERA_HEADER length by reading once more one of this.
+ */
+ if (fread (pic + LOW_CAMERA_HEADER, LOW_CAMERA_HEADER, 1, ifp) != 1)
+ {
+ DBG (10,
+ "convert_pic: error: cannot resync with high resolution header\n");
+ fclose (ifp);
+ return -1;
+ }
+ }
+
+ if (fread (pic + CAMERA_HEADER (res), WIDTH (res), HEIGHT, ifp) != HEIGHT)
+ {
+ DBG (9, "convert_pic: error: cannot read picture\n");
+ fclose (ifp);
+ return -1;
+ }
+
+ fclose (ifp);
+
+ /*
+ * Setup image size with resolution
+ */
+
+ image_size = IMAGE_SIZE (res);
+ image_width = WIDTH (res);
+ net_width = image_width - LEFT_MARGIN - RIGHT_MARGIN (res);
+ camera_header = CAMERA_HEADER (res);
+ components = (format & SAVE_24BITS) ? 3 : 1;
+
+ /*
+ * Convert the image to 24 bits
+ */
+
+ if ((pp =
+ alloc_pixmap (net_width - 1, HEIGHT - BOTTOM_MARGIN - 1,
+ components)) == NULL)
+ {
+ DBG (1, "convert_pic: error: alloc_pixmap\n");
+ return -1;
+ }
+
+ comet_to_pixmap (pic, pp);
+
+ if (format & SAVE_ADJASPECT)
+ {
+ /*
+ * Strech image
+ */
+
+ if (res)
+ pp2 = alloc_pixmap (320, HEIGHT - BOTTOM_MARGIN - 1, components);
+ else
+ pp2 = alloc_pixmap (net_width - 1, 373, components);
+
+ if (pp2 == NULL)
+ {
+ DBG (2, "convert_pic: error: alloc_pixmap\n");
+ free_pixmap (pp);
+ return -1;
+ }
+
+ if (res)
+ zoom_x (pp, pp2);
+ else
+ zoom_y (pp, pp2);
+
+ free_pixmap (pp);
+ pp = pp2;
+ pp2 = NULL;
+
+ }
+
+ return 0;
+}
+
+#define PGM_EXT "pgm"
+#define PPM_EXT "ppm"
+
+#define RED 0.30
+#define GREEN 0.59
+#define BLUE 0.11
+
+#define RED_OFFSET 0
+#define GREEN_OFFSET 1
+#define BLUE_OFFSET 2
+
+#define GET_COMP(pp, x, y, c) (pp->planes[((x) + (y)*pp->width)*pp->components + (c)])
+
+#define GET_R(pp, x, y) (GET_COMP(pp, x, y, RED_OFFSET))
+#define GET_G(pp, x, y) (GET_COMP(pp, x, y, GREEN_OFFSET))
+#define GET_B(pp, x, y) (GET_COMP(pp, x, y, BLUE_OFFSET))
+
+static struct pixmap *
+alloc_pixmap (int x, int y, int d)
+{
+ struct pixmap *result = NULL;
+
+ if (d == 1 || d == 3)
+ {
+ if (x > 0)
+ {
+ if (y > 0)
+ {
+ if ((result = malloc (sizeof (struct pixmap))) != NULL)
+ {
+ result->width = x;
+ result->height = y;
+ result->components = d;
+ if (!(result->planes = malloc (x * y * d)))
+ {
+ DBG (10,
+ "alloc_pixmap: error: not enough memory for bitplanes\n");
+ free (result);
+ result = NULL;
+ }
+ }
+ else
+ DBG (10,
+ "alloc_pixmap: error: not enough memory for pixmap\n");
+ }
+ else
+ DBG (10, "alloc_pixmap: error: y is out of range\n");
+ }
+ else
+ DBG (10, "alloc_pixmap: error: x is out of range\n");
+ }
+ else
+ DBG (10, "alloc_pixmap: error: cannot handle %d components\n", d);
+
+ return result;
+}
+
+static void
+free_pixmap (struct pixmap *p)
+{
+ if (p)
+ {
+ free (p->planes);
+ free (p);
+ }
+}
+
+static int
+set_pixel_rgb (struct pixmap *p, int x, int y, unsigned char r,
+ unsigned char g, unsigned char b)
+{
+ int result = 0;
+
+ if (p)
+ {
+ if (x >= 0 && x < p->width)
+ {
+ if (y >= 0 && y < p->height)
+ {
+ if (p->components == 1)
+ {
+ GET_R (p, x, y) = RED * r + GREEN * g + BLUE * b;
+ }
+ else
+ {
+ GET_R (p, x, y) = r;
+ GET_G (p, x, y) = g;
+ GET_B (p, x, y) = b;
+ }
+ }
+ else
+ {
+ DBG (10, "set_pixel_rgb: error: y out of range\n");
+ result = -1;
+ }
+ }
+ else
+ {
+ DBG (10, "set_pixel_rgb: error: x out of range\n");
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+static int
+zoom_x (struct pixmap *source, struct pixmap *dest)
+{
+ int result = 0, dest_col, row, component, src_index;
+ float ratio, src_ptr, delta;
+ unsigned char src_component;
+
+ if (source && dest)
+ {
+ /*
+ * We could think of resizing a pixmap and changing the number of
+ * components at the same time. Maybe this will be implemented later.
+ */
+ if (source->height == dest->height
+ && source->components == dest->components)
+ {
+ if (source->width < dest->width)
+ {
+ ratio = ((float) source->width / (float) dest->width);
+
+ for (src_ptr = 0, dest_col = 0; dest_col < dest->width;
+ src_ptr += ratio, dest_col++)
+ {
+ /*
+ * dest[dest_col] = source[(int)src_ptr] +
+ * (source[((int)src_ptr) + 1] - source[(int)src_ptr])
+ * * (src_ptr - (int)src_ptr);
+ */
+ src_index = (int) src_ptr;
+ delta = src_ptr - src_index;
+
+ for (row = 0; row < source->height; row++)
+ {
+ for (component = 0; component < source->components;
+ component++)
+ {
+ src_component =
+ GET_COMP (source, src_index, row, component);
+
+ GET_COMP (dest, dest_col, row, component) =
+ src_component +
+ (GET_COMP (source, src_index + 1, row,
+ component) - src_component) * delta;
+ }
+ }
+ }
+ }
+ else
+ {
+ DBG (10, "zoom_x: error: can only zoom out\n");
+ result = -1;
+ }
+ }
+ else
+ {
+ DBG (10, "zoom_x: error: incompatible pixmaps\n");
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+static int
+zoom_y (struct pixmap *source, struct pixmap *dest)
+{
+ int result = 0, dest_row, column, component, src_index;
+ float ratio, src_ptr, delta;
+ unsigned char src_component;
+
+ if (source && dest)
+ {
+ /*
+ * We could think of resizing a pixmap and changing the number of
+ * components at the same time. Maybe this will be implemented later.
+ */
+ if (source->width == dest->width
+ && source->components == dest->components)
+ {
+ if (source->height < dest->height)
+ {
+ ratio = ((float) source->height / (float) dest->height);
+
+ for (src_ptr = 0, dest_row = 0; dest_row < dest->height;
+ src_ptr += ratio, dest_row++)
+ {
+ /*
+ * dest[dest_row] = source[(int)src_ptr] +
+ * (source[((int)src_ptr) + 1] - source[(int)src_ptr])
+ * * (src_ptr - (int)src_ptr);
+ */
+ src_index = (int) src_ptr;
+ delta = src_ptr - src_index;
+
+ for (column = 0; column < source->width; column++)
+ {
+ for (component = 0; component < source->components;
+ component++)
+ {
+ src_component =
+ GET_COMP (source, column, src_index, component);
+
+ GET_COMP (dest, column, dest_row, component) =
+ src_component +
+ (GET_COMP (source, column, src_index + 1,
+ component) - src_component) * delta;
+ }
+ }
+ }
+ }
+ else
+ {
+ DBG (10, "zoom_y: error: can only zoom out\n");
+ result = -1;
+ }
+ }
+ else
+ {
+ DBG (10, "zoom_y: error: incompatible pixmaps\n");
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+static unsigned char shoot_pck[] = SHOOT_PCK;
+
+static int
+shoot (int fd)
+{
+ struct termios tty_temp, tty_old;
+ int result = 0;
+
+ DBG (127, "shoot() called\n");
+
+ if (write (fd, (char *) shoot_pck, 8) != 8)
+ {
+ DBG (3, "shoot: error: write error\n");
+ return -1;
+ }
+
+ if (CameraInfo.model != 0x25)
+ {
+ /*
+ * WARNING: now we set the serial port to 9600 baud!
+ */
+
+ if (tcgetattr (fd, &tty_old) == -1)
+ {
+ DBG (3, "shoot: error: could not get attributes\n");
+ return -1;
+ }
+
+ memcpy ((char *) &tty_temp, (char *) &tty_old, sizeof (struct termios));
+
+ cfsetispeed (&tty_temp, B9600);
+ cfsetospeed (&tty_temp, B9600);
+
+ /*
+ * Apparently there is a bug in the DC20 where the response to
+ * the shoot request is always at 9600. The DC25 does not have
+ * this bug, so we skip this block.
+ */
+ if (tcsetattr (fd, TCSANOW, &tty_temp) == -1)
+ {
+ DBG (3, "shoot: error: could not set attributes\n");
+ return -1;
+ }
+ }
+
+ if (read (fd, (char *) &result, 1) != 1)
+ {
+ DBG (3, "shoot: error: read returned -1\n");
+ result = -1;
+ }
+ else
+ {
+ result = (result == 0xD1) ? 0 : -1;
+ }
+
+ if (CameraInfo.model != 0x25)
+ {
+ /*
+ * We reset the serial to its original speed.
+ * We can skip this on the DC25 also.
+ */
+ if (tcsetattr (fd, TCSANOW, &tty_old) == -1)
+ {
+ DBG (3, "shoot: error: could not reset attributes\n");
+ result = -1;
+ }
+ }
+
+ if (result == 0)
+ {
+ if (CameraInfo.model == 0x25)
+ {
+ /*
+ * If we don't put this in, the next read will time out
+ * and return failure. Does the DC-20 need it too?
+ */
+ sleep (3);
+ }
+ if (end_of_data (fd) == -1)
+ {
+ DBG (3, "shoot: error: end_of_data returned -1\n");
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+
+static unsigned char erase_pck[] = ERASE_PCK;
+
+static int
+erase (int fd)
+{
+ int count = 0;
+
+ DBG (127, "erase() called for image %d\n", dc25_opt_image_number);
+ erase_pck[3] = dc25_opt_image_number;
+ if (dc25_opt_erase)
+ {
+ erase_pck[3] = 0;
+ }
+
+ if (send_pck (fd, erase_pck) == -1)
+ {
+ DBG (3, "erase: error: send_pck returned -1\n");
+ return -1;
+ }
+
+ if (CameraInfo.model == 0x25)
+ {
+ /*
+ * This block may really apply to the DC20 also, but since I
+ * don't have one, it's hard to say for sure. On the DC25, erase
+ * takes long enought that the read may timeout without returning
+ * any data before the erase is complete. We let this happen
+ * up to 4 times, then give up.
+ */
+ while (count < 4)
+ {
+ if (end_of_data (fd) == -1)
+ {
+ count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (count == 4)
+ {
+ DBG (3, "erase: error: end_of_data returned -1\n");
+ return -1;
+ }
+ }
+ else
+ { /* Assume DC-20 */
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (3, "erase: error: end_of_data returned -1\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned char res_pck[] = RES_PCK;
+
+static int
+change_res (int fd, unsigned char res)
+{
+ DBG (127, "change_res called\n");
+ if (res != 0 && res != 1)
+ {
+ DBG (3, "change_res: error: unsupported resolution\n");
+ return -1;
+ }
+
+ res_pck[2] = res;
+
+ if (send_pck (fd, res_pck) == -1)
+ {
+ DBG (4, "change_res: error: send_pck returned -1\n");
+ }
+
+ if (end_of_data (fd) == -1)
+ {
+ DBG (4, "change_res: error: end_of_data returned -1\n");
+ }
+ return 0;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize)
+{
+ char dev_name[PATH_MAX], *p;
+ size_t len;
+ FILE *fp;
+ int baud;
+
+ strcpy (tty_name, DEF_TTY_NAME);
+
+ DBG_INIT ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (DC25_CONFIG_FILE);
+
+ DBG (127,
+ "sane_init() $Id$\n");
+
+ if (!fp)
+ {
+ /* default to /dev/ttyS0 instead of insisting on config file */
+ DBG (1, "sane_init: missing config file '%s'\n", DC25_CONFIG_FILE);
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ dev_name[sizeof (dev_name) - 1] = '\0';
+ DBG (20, "sane_init: config- %s", dev_name);
+
+ if (dev_name[0] == '#')
+ continue; /* ignore line comments */
+ len = strlen (dev_name);
+ if (!len)
+ continue; /* ignore empty lines */
+ if (strncmp (dev_name, "port=", 5) == 0)
+ {
+ p = strchr (dev_name, '/');
+ if (p)
+ {
+ strcpy (tty_name, p);
+ }
+ DBG (20, "Config file port=%s\n", tty_name);
+ }
+ else if (strncmp (dev_name, "baud=", 5) == 0)
+ {
+ baud = atoi (&dev_name[5]);
+ switch (baud)
+ {
+ case 9600:
+ tty_baud = B9600;
+ break;
+ case 19200:
+ tty_baud = B19200;
+ break;
+ case 38400:
+ tty_baud = B38400;
+ break;
+#ifdef B57600
+ case 57600:
+ tty_baud = B57600;
+ break;
+#endif
+#ifdef B115200
+ case 115200:
+ tty_baud = B115200;
+ break;
+#endif
+ default:
+ DBG (20, "Unknown baud=%d\n", baud);
+ tty_baud = DEFAULT_TTY_BAUD;
+ break;
+ }
+ DBG (20, "Config file baud=%lu\n", (u_long) tty_baud);
+ }
+ else if (strcmp (dev_name, "dumpinquiry") == 0)
+ {
+ dumpinquiry = SANE_TRUE;
+ }
+ }
+ fclose (fp);
+ }
+
+ if ((tfd = init_dc20 (tty_name, tty_baud)) == -1)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if ((dc20_info = get_info (tfd)) == NULL)
+ {
+ DBG (2, "error: could not get info\n");
+ close_dc20 (tfd);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dumpinquiry)
+ {
+ DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n");
+ DBG (0, "Model...........: DC%x\n", dc20_info->model);
+ DBG (0, "Firmware version: %d.%d\n", dc20_info->ver_major,
+ dc20_info->ver_minor);
+ DBG (0, "Pictures........: %d/%d\n", dc20_info->pic_taken,
+ dc20_info->pic_taken + dc20_info->pic_left);
+ DBG (0, "Resolution......: %s\n",
+ dc20_info->flags.low_res ? "low" : "high");
+ DBG (0, "Battery state...: %s\n",
+ dc20_info->flags.low_batt ? "low" : "good");
+ }
+
+ if (CameraInfo.pic_taken == 0)
+ {
+/*
+ sod[DC25_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+*/
+ image_range.min = 0;
+ dc25_opt_image_number = 0;
+
+ }
+ else
+ {
+/*
+ sod[DC25_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+*/
+ image_range.min = 1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+}
+
+/* Device select/open/close */
+
+static const SANE_Device dev[] = {
+ {
+ "0",
+ "Kodak",
+ "DC-25",
+ "still camera"},
+};
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool UNUSEDARG local_only)
+{
+ static const SANE_Device *devlist[] = {
+ dev + 0, 0
+ };
+
+ DBG (127, "sane_get_devices called\n");
+
+ if (dc20_info == NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ int i;
+
+ DBG (127, "sane_open for device %s\n", devicename);
+ if (!devicename[0])
+ {
+ i = 0;
+ }
+ else
+ {
+ for (i = 0; i < NELEMS (dev); ++i)
+ {
+ if (strcmp (devicename, dev[i].name) == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ if (i >= NELEMS (dev))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_open)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ is_open = 1;
+ *handle = MAGIC;
+
+ if (dc20_info == NULL)
+ {
+ DBG (1, "No device info\n");
+ }
+
+ if (tmpname == NULL)
+ {
+ tmpname = tmpnamebuf;
+ if (mktemp (tmpname) == NULL)
+ {
+ DBG (1, "Unable to make temp file %s\n", tmpname);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (3, "sane_open: pictures taken=%d\n", dc20_info->pic_taken);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (127, "sane_close called\n");
+ if (handle == MAGIC)
+ is_open = 0;
+
+ if (pp)
+ {
+ free_pixmap (pp);
+ pp = NULL;
+ }
+
+ close_dc20 (tfd);
+
+ DBG (127, "sane_close returning\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ if (handle != MAGIC || !is_open)
+ return NULL; /* wrong device */
+ if (option < 0 || option >= NELEMS (sod))
+ return NULL;
+ return &sod[option];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Int myinfo = info_flags;
+ SANE_Status status;
+
+ info_flags = 0;
+
+ DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n",
+ handle, sod[option].title,
+ (action ==
+ SANE_ACTION_SET_VALUE ? "SET" : (action ==
+ SANE_ACTION_GET_VALUE ? "GET" :
+ "SETAUTO")), value, (void *)info);
+
+ if (handle != MAGIC || !is_open)
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (option < 0 || option >= NELEMS (sod))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_VALUE:
+ status = sanei_constrain_value (sod + option, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "Constraint error in control_option\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ case DC25_OPT_IMAGE_NUMBER:
+ dc25_opt_image_number = *(SANE_Word *) value;
+/* myinfo |= SANE_INFO_RELOAD_OPTIONS; */
+ break;
+
+ case DC25_OPT_THUMBS:
+ dc25_opt_thumbnails = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ if (dc25_opt_thumbnails)
+ {
+ /*
+ * DC20 thumbnail are 80x60 grayscale, DC25
+ * thumbnails are color.
+ */
+ parms.format =
+ (CameraInfo.model == 0x25) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ parms.bytes_per_line = 80 * 3;
+ parms.pixels_per_line = 80;
+ parms.lines = 60;
+ }
+ else
+ {
+ parms.format = SANE_FRAME_RGB;
+ if (dc20_info->flags.low_res)
+ {
+ parms.bytes_per_line = 320 * 3;
+ parms.pixels_per_line = 320;
+ parms.lines = 243;
+ }
+ else
+ {
+ parms.bytes_per_line = 500 * 3;
+ parms.pixels_per_line = 500;
+ parms.lines = 373;
+ }
+ }
+ break;
+
+ case DC25_OPT_SNAP:
+ dc25_opt_snap = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ if (dc25_opt_snap)
+ {
+ sod[DC25_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[DC25_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
+ }
+ break;
+
+ case DC25_OPT_LOWRES:
+ dc25_opt_lowres = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ if (!dc25_opt_thumbnails)
+ {
+
+ parms.format = SANE_FRAME_RGB;
+
+ if (dc20_info->flags.low_res)
+ {
+ parms.bytes_per_line = 320 * 3;
+ parms.pixels_per_line = 320;
+ parms.lines = 243;
+ }
+ else
+ {
+ parms.bytes_per_line = 500 * 3;
+ parms.pixels_per_line = 500;
+ parms.lines = 373;
+ }
+
+ }
+ break;
+
+ case DC25_OPT_CONTRAST:
+ dc25_opt_contrast = *(SANE_Word *) value;
+ break;
+
+ case DC25_OPT_GAMMA:
+ dc25_opt_gamma = *(SANE_Word *) value;
+ break;
+
+ case DC25_OPT_ERASE:
+ dc25_opt_erase = !!*(SANE_Word *) value;
+
+ /*
+ * erase and erase_one are mutually exclusive. If
+ * this one is turned on, the other must be off
+ */
+ if (dc25_opt_erase && dc25_opt_erase_one)
+ {
+ dc25_opt_erase_one = SANE_FALSE;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case DC25_OPT_ERASE_ONE:
+ dc25_opt_erase_one = !!*(SANE_Word *) value;
+
+ /*
+ * erase and erase_one are mutually exclusive. If
+ * this one is turned on, the other must be off
+ */
+ if (dc25_opt_erase_one && dc25_opt_erase)
+ {
+ dc25_opt_erase = SANE_FALSE;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case DC25_OPT_DEFAULT:
+
+ dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
+ dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ case 0:
+ *(SANE_Word *) value = NELEMS (sod);
+ break;
+
+ case DC25_OPT_IMAGE_NUMBER:
+ *(SANE_Word *) value = dc25_opt_image_number;
+ break;
+
+ case DC25_OPT_THUMBS:
+ *(SANE_Word *) value = dc25_opt_thumbnails;
+ break;
+
+ case DC25_OPT_SNAP:
+ *(SANE_Word *) value = dc25_opt_snap;
+ break;
+
+ case DC25_OPT_LOWRES:
+ *(SANE_Word *) value = dc25_opt_lowres;
+ break;
+
+ case DC25_OPT_CONTRAST:
+ *(SANE_Word *) value = dc25_opt_contrast;
+ break;
+
+ case DC25_OPT_GAMMA:
+ *(SANE_Word *) value = dc25_opt_gamma;
+ break;
+
+ case DC25_OPT_ERASE:
+ *(SANE_Word *) value = dc25_opt_erase;
+ break;
+
+ case DC25_OPT_ERASE_ONE:
+ *(SANE_Word *) value = dc25_opt_erase_one;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ switch (option)
+ {
+#if 0
+ case DC25_OPT_CONTRAST:
+ dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT);
+ break;
+
+ case DC25_OPT_GAMMA:
+ dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT);
+ break;
+#endif
+
+ default:
+ return SANE_STATUS_UNSUPPORTED; /* We are DUMB */
+ }
+ }
+
+ if (info)
+ *info = myinfo;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int rc = SANE_STATUS_GOOD;
+
+ DBG (127, "sane_get_params called\n");
+
+ if (handle != MAGIC || !is_open)
+ rc = SANE_STATUS_INVAL; /* Unknown handle ... */
+
+
+ *params = parms;
+ return rc;
+}
+
+static unsigned char thumb_pck[] = THUMBS_PCK;
+
+static unsigned char pic_pck[] = PICS_PCK;
+
+static int bytes_in_buffer;
+static int bytes_read_from_buffer;
+static SANE_Byte buffer[1024];
+static int total_bytes_read;
+static SANE_Bool started = SANE_FALSE;
+static int outbytes;
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int n, i;
+ FILE *f;
+
+ DBG (127, "sane_start called, handle=%lx\n", (u_long) handle);
+
+ if (handle != MAGIC || !is_open ||
+ (dc25_opt_image_number == 0 && dc25_opt_snap == SANE_FALSE))
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (started)
+ {
+ return SANE_STATUS_EOF;
+ }
+
+ if (dc25_opt_snap)
+ {
+
+ /*
+ * Don't allow picture unless there is room in the
+ * camera.
+ */
+ if (CameraInfo.pic_left == 0)
+ {
+ DBG (3, "No room to store new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * DC-20 can only change resolution when camer is empty.
+ * DC-25 can do it any time.
+ */
+ if (CameraInfo.model != 0x20 || CameraInfo.pic_taken == 0)
+ {
+ if (change_res (tfd, dc25_opt_lowres) == -1)
+ {
+ DBG (1, "Failed to set resolution\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /*
+ * Not sure why this delay is needed, but it seems to help:
+ */
+#ifdef HAVE_USLEEP
+ usleep (10);
+#else
+ sleep (1);
+#endif
+ if (shoot (tfd) == -1)
+ {
+ DBG (1, "Failed to snap new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ info_flags |= SANE_INFO_RELOAD_OPTIONS;
+ CameraInfo.pic_taken++;
+ CameraInfo.pic_left--;
+ dc25_opt_image_number = CameraInfo.pic_taken;
+ if (image_range.min == 0)
+ image_range.min = 1;
+ image_range.max++;
+ sod[DC25_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if (dc25_opt_thumbnails)
+ {
+
+ /*
+ * For thumbnails, we can do things right where we
+ * start the download, and grab the first block
+ * from the camera. The reamining blocks will be
+ * fetched as necessary by sane_read().
+ */
+ thumb_pck[3] = (unsigned char) dc25_opt_image_number;
+
+ if (send_pck (tfd, thumb_pck) == -1)
+ {
+ DBG (4, "sane_start: error: send_pck returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (read_data (tfd, buffer, 1024) == -1)
+ {
+ DBG (4, "sane_start: read_data failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * DC20 thumbnail are 80x60 grayscale, DC25
+ * thumbnails are color.
+ */
+ parms.format =
+ (CameraInfo.model == 0x25) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ parms.bytes_per_line = 80 * 3; /* 80 pixels, 3 colors */
+ parms.pixels_per_line = 80;
+ parms.lines = 60;
+
+ bytes_in_buffer = 1024;
+ bytes_read_from_buffer = 0;
+
+ }
+ else
+ {
+ /*
+ * We do something a little messy, and violates the SANE
+ * philosophy. However, since it is fairly tricky to
+ * convert the DC2x "comet" files on the fly, we read in
+ * the entire data stream in sane_open(), and use convert_pic
+ * to convert it to an in-memory pixpmap. Then when
+ * sane_read() is called, we fill the requests from
+ * memory. A good project for me (or some kind volunteer)
+ * would be to rewrite this and move the actual download
+ * to sane_read(). However, one argument for keeping it
+ * this way is that the data comes down pretty fast, and
+ * it helps to dedicate the processor to this task. We
+ * might get serial port overruns if we try to do other
+ * things at the same time.
+ *
+ * Also, as a side note, I was constantly getting serial
+ * port overruns on a 90MHz pentium until I used hdparm
+ * to set the "-u1" flag on the system drives.
+ */
+ int fd;
+
+ fd = open (tmpname, O_CREAT | O_EXCL | O_WRONLY, 0600);
+ if (fd == -1)
+ {
+ DBG (0, "Unable to open tmp file\n");
+ return SANE_STATUS_INVAL;
+ }
+ f = fdopen (fd, "wb");
+ if (f == NULL)
+ {
+ DBG (0, "Unable to fdopen tmp file\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ strcpy ((char *) buffer, COMET_MAGIC);
+ fwrite (buffer, 1, COMET_HEADER_SIZE, f);
+
+ pic_pck[3] = (unsigned char) dc25_opt_image_number;
+
+ if (send_pck (tfd, pic_pck) == -1)
+ {
+ DBG (4, "sane_start: error: send_pck returned -1\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (read_data (tfd, buffer, 1024) == -1)
+ {
+ DBG (5, "sane_start: read_data failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (buffer[4] == 0)
+ { /* hi-res image */
+ DBG (5, "sane_start: hi-res image\n");
+ n = 122;
+
+ parms.bytes_per_line = 500 * 3; /* 3 colors */
+ parms.pixels_per_line = 500;
+ parms.lines = 373;
+
+ bytes_in_buffer = 1024;
+ bytes_read_from_buffer = 0;
+ }
+ else
+ {
+ n = 61;
+ DBG (5, "sane_start: low-res image\n");
+
+ parms.bytes_per_line = 320 * 3; /* 3 Colors */
+ parms.pixels_per_line = 320;
+ parms.lines = 243;
+
+ bytes_in_buffer = 1024;
+ bytes_read_from_buffer = 0;
+ }
+
+
+ fwrite (buffer, 1, 1024, f);
+
+ for (i = 1; i < n; i++)
+ {
+ if (read_data (tfd, buffer, 1024) == -1)
+ {
+ DBG (5, "sane_start: read_data failed\n");
+ return SANE_STATUS_INVAL;
+ }
+ fwrite (buffer, 1, 1024, f);
+ }
+
+ if (end_of_data (tfd) == -1)
+ {
+ fclose (f);
+ DBG (4, "sane_open: end_of_data error\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ fclose (f);
+ if (convert_pic (tmpname, SAVE_ADJASPECT | SAVE_24BITS) == -1)
+ {
+ DBG (3, "sane_open: unable to convert\n");
+ return SANE_STATUS_INVAL;
+ }
+ unlink (tmpname);
+ outbytes = 0;
+ }
+ }
+
+ started = SANE_TRUE;
+ total_bytes_read = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle UNUSEDARG handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ DBG (127, "sane_read called, maxlen=%d\n", max_length);
+
+ if ( ! started ) {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dc25_opt_thumbnails)
+ {
+ if (total_bytes_read == THUMBSIZE)
+ {
+ if (dc25_opt_erase || dc25_opt_erase_one)
+ {
+
+ if (erase (tfd) == -1)
+ {
+ DBG (1, "Failed to erase memory\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ dc25_opt_erase = SANE_FALSE;
+ dc25_opt_erase_one = SANE_FALSE;
+ info_flags |= SANE_INFO_RELOAD_OPTIONS;
+
+ if (get_info (tfd) == NULL)
+ {
+ DBG (2, "error: could not get info\n");
+ close_dc20 (tfd);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (10, "Call get_info!, image range=%d,%d\n", image_range.min,
+ image_range.max);
+ }
+ return SANE_STATUS_EOF;
+ }
+
+ *length = 0;
+ if (!(bytes_in_buffer - bytes_read_from_buffer))
+ {
+ if (read_data (tfd, buffer, 1024) == -1)
+ {
+ DBG (5, "sane_read: read_data failed\n");
+ return SANE_STATUS_INVAL;
+ }
+ bytes_in_buffer = 1024;
+ bytes_read_from_buffer = 0;
+ }
+
+ while (bytes_read_from_buffer < bytes_in_buffer &&
+ max_length && total_bytes_read < THUMBSIZE)
+ {
+ *data++ = buffer[bytes_read_from_buffer++];
+ (*length)++;
+ max_length--;
+ total_bytes_read++;
+ }
+
+ if (total_bytes_read == THUMBSIZE)
+ {
+ if (end_of_data (tfd) == -1)
+ {
+ DBG (4, "sane_read: end_of_data error\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ int i;
+ int filesize = parms.bytes_per_line * parms.lines;
+
+ /*
+ * If outbytes is zero, then this is the first time
+ * we've been called, so update the contrast table.
+ * The formula is something I came up with that has the
+ * following prooperties:
+ * 1) It's a smooth curve that provides the effect I wanted
+ * (bright pixels are made brighter, dim pixels are made
+ * dimmer)
+ * 2) The contrast parameter can be adjusted to provide
+ * different amounts of contrast.
+ * 3) A parameter of 1.0 can be used to pass the data
+ * through unchanged (but values around 1.75 look
+ * a lot better
+ */
+ if (outbytes == 0)
+ {
+ double d;
+ double cont = SANE_UNFIX (dc25_opt_contrast);
+
+ for (i = 0; i < 256; i++)
+ {
+ d = (i * 2.0) / 255 - 1.0;
+ d =
+ ((-pow (1 - d, cont)) + 1) * (d >=
+ 0) + (((pow (d + 1, cont)) -
+ 1)) * (d < 0);
+ contrast_table[i] = (d * 127.5) + 127.5;
+/*
+ fprintf (stderr,"%03d %03d\n",i,contrast_table[i]);
+*/
+ }
+ }
+
+ /* We're done, so return EOF */
+ if (outbytes >= filesize)
+ {
+ free_pixmap (pp);
+ pp = NULL;
+
+ if (dc25_opt_erase || dc25_opt_erase_one)
+ {
+ if (erase (tfd) == -1)
+ {
+ DBG (1, "Failed to erase memory\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (get_info (tfd) == NULL)
+ {
+ DBG (2, "error: could not get info\n");
+ close_dc20 (tfd);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (10, "Call get_info!, image range=%d,%d\n", image_range.min,
+ image_range.max);
+
+ get_info (tfd);
+
+ *length=0;
+
+ return SANE_STATUS_EOF;
+ }
+
+ if (max_length > filesize - outbytes)
+ {
+ *length = filesize - outbytes;
+ }
+ else
+ {
+ *length = max_length;
+ }
+
+ memcpy (data, pp->planes + outbytes, *length);
+ outbytes += *length;
+
+
+ for (i = 0; i < *length; i++)
+ {
+ data[i] = contrast_table[data[i]];
+ }
+
+ return SANE_STATUS_GOOD;
+
+ }
+}
+
+void
+sane_cancel (SANE_Handle UNUSEDARG handle)
+{
+ DBG (127, "sane_cancel() called\n");
+ started = SANE_FALSE;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle UNUSEDARG handle,
+ SANE_Bool UNUSEDARG non_blocking)
+{
+ /* sane_set_io_mode() is only valid during a scan */
+ if (started)
+ {
+ if (non_blocking == SANE_FALSE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else
+ {
+ /* We aren't currently scanning */
+ return SANE_STATUS_INVAL;
+ }
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle UNUSEDARG handle, SANE_Int UNUSEDARG * fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/dc25.conf.in b/backend/dc25.conf.in
new file mode 100644
index 0000000..33cfd89
--- /dev/null
+++ b/backend/dc25.conf.in
@@ -0,0 +1,23 @@
+# Serial port where the camera is connected
+## Linux
+port=/dev/ttyS0
+## IRIX
+#port=/dev/ttyd1
+## Solaris
+#port=/dev/term/a
+## HP-UX
+#port=/dev/tty0p0
+## Digital UNIX
+#port=/dev/tty01
+# Max baud rate for download. Camera always starts at 9600 baud, then
+# switches to the higher rate
+## This works for Linux. Also works for IRIX (6.3 or higher), providing that
+## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or
+## a newer SGI hardware [see serial(7)].
+#baud=115200
+## This works for most UNIX's
+baud=38400
+# Prints some extra information during the init phase. This can be
+# handy, but note that printing anything to stderr breaks the saned
+# network scanning.
+#dumpinquiry
diff --git a/backend/dc25.h b/backend/dc25.h
new file mode 100644
index 0000000..df95271
--- /dev/null
+++ b/backend/dc25.h
@@ -0,0 +1,276 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ dc25.h
+
+ 6/1/98
+
+ This file (C) 1998 Peter Fales
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-25 (and
+ probably the DC-20) digital cameras. THIS IS EXTREMELY ALPHA CODE!
+ USE AT YOUR OWN RISK!!
+
+ (feedback to: dc25-devel@fales-lorenz.net)
+
+ This backend is based heavily on the dc20ctrl package by Ugo
+ Paternostro <paterno@dsi.unifi.it>. I've attached his header below:
+
+ ***************************************************************************
+
+ * Copyright (C) 1998 Ugo Paternostro <paterno@dsi.unifi.it>
+ *
+ * This file is part of the dc20ctrl package. The complete package can be
+ * downloaded from:
+ * http://aguirre.dsi.unifi.it/~paterno/binaries/dc20ctrl.tar.gz
+ *
+ * This package is derived from the dc20 package, built by Karl Hakimian
+ * <hakimian@aha.com> that you can find it at ftp.eecs.wsu.edu in the
+ * /pub/hakimian directory. The complete URL is:
+ * ftp://ftp.eecs.wsu.edu/pub/hakimian/dc20.tar.gz
+ *
+ * This package also includes a sligthly modified version of the Comet to ppm
+ * conversion routine written by YOSHIDA Hideki <hideki@yk.rim.or.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE (1==1)
+#endif
+
+#ifndef FALSE
+#define FALSE (!TRUE)
+#endif
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+typedef struct dc20_info_s {
+ unsigned char model;
+ unsigned char ver_major;
+ unsigned char ver_minor;
+ int pic_taken;
+ int pic_left;
+ struct {
+ unsigned int low_res:1;
+ unsigned int low_batt:1;
+ } flags;
+} Dc20Info, *Dc20InfoPtr;
+
+static Dc20Info *get_info (int);
+
+#define INIT_PCK {0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^^^^^^^
+ * Baud rate: (see pkt_speed structure)
+ * 0x96 0x00 -> 9600 baud
+ * 0x19 0x20 -> 19200 baud
+ * 0x38 0x40 -> 38400 baud
+ * 0x57 0x60 -> 57600 baud
+ * 0x11 0x52 -> 115200 baud
+ */
+#define INFO_PCK {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define SHOOT_PCK {0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define ERASE_PCK {0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+#define RES_PCK {0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Resolution: 0x00 = high, 0x01 = low
+ */
+#define THUMBS_PCK {0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Thumbnail number
+ */
+#define PICS_PCK {0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A}
+/* ^^^^
+ * Picture number
+ */
+
+struct pkt_speed {
+ speed_t baud;
+ unsigned char pkt_code[2];
+};
+
+#define DEFAULT_TTY_BAUD B38400
+
+#define HIGH_RES 0
+#define LOW_RES 1
+
+/*
+ * Image parameters
+ */
+
+#define LOW_CAMERA_HEADER 256
+#define HIGH_CAMERA_HEADER 512
+#define CAMERA_HEADER(r) ( (r) ? LOW_CAMERA_HEADER : HIGH_CAMERA_HEADER )
+
+#define LOW_WIDTH 256
+#define HIGH_WIDTH 512
+#define WIDTH(r) ( (r) ? LOW_WIDTH : HIGH_WIDTH )
+
+#define HEIGHT 243
+
+#define LEFT_MARGIN 1
+
+#define LOW_RIGHT_MARGIN 5
+#define HIGH_RIGHT_MARGIN 10
+#define RIGHT_MARGIN(r) ( (r) ? LOW_RIGHT_MARGIN : HIGH_RIGHT_MARGIN )
+
+#define TOP_MARGIN 1
+
+#define BOTTOM_MARGIN 1
+
+#define BLOCK_SIZE 1024
+
+#define LOW_BLOCKS 61
+#define HIGH_BLOCKS 122
+#define BLOCKS(r) ( (r) ? LOW_BLOCKS : HIGH_BLOCKS )
+
+#define LOW_IMAGE_SIZE ( LOW_BLOCKS * BLOCK_SIZE )
+#define HIGH_IMAGE_SIZE ( HIGH_BLOCKS * BLOCK_SIZE )
+#define IMAGE_SIZE(r) ( (r) ? LOW_IMAGE_SIZE : HIGH_IMAGE_SIZE )
+#define MAX_IMAGE_SIZE ( HIGH_IMAGE_SIZE )
+
+/*
+ * Comet file
+ */
+
+#define COMET_MAGIC "COMET"
+#define COMET_HEADER_SIZE 128
+#define COMET_EXT "cmt"
+
+/*
+ * Pixmap structure
+ */
+
+struct pixmap {
+ int width;
+ int height;
+ int components;
+ unsigned char *planes;
+};
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+/*
+ * Rotations
+ */
+
+#define ROT_STRAIGHT 0x00
+#define ROT_LEFT 0x01
+#define ROT_RIGHT 0x02
+#define ROT_HEADDOWN 0x03
+
+#define ROT_MASK 0x03
+
+/*
+ * File formats
+ */
+
+#define SAVE_RAW 0x01
+#define SAVE_GREYSCALE 0x02
+#define SAVE_24BITS 0x04
+#define SAVE_FILES 0x07
+#define SAVE_FORMATS 0x38
+#define SAVE_ADJASPECT 0x80
+
+/*
+ * External definitions
+ */
+
+extern char *__progname; /* Defined in /usr/lib/crt0.o */
+
+
+
+#include <sys/types.h>
+
+FILE * sanei_config_open (const char *filename);
+
+char *sanei_config_read (char *str, int n, FILE * stream);
+
+static int init_dc20 (char *, speed_t);
+
+static void close_dc20 (int);
+
+static int read_data (int fd, unsigned char *buf, int sz);
+
+static int end_of_data (int fd);
+
+static int set_pixel_rgb (struct pixmap *, int, int, unsigned char, unsigned char, unsigned char);
+
+static struct pixmap *alloc_pixmap (int x, int y, int d);
+
+static void free_pixmap (struct pixmap *p);
+
+static int zoom_x (struct pixmap *source, struct pixmap *dest);
+
+static int zoom_y (struct pixmap *source, struct pixmap *dest);
+
+static int comet_to_pixmap (unsigned char *, struct pixmap *);
+
+
diff --git a/backend/dell1600n_net.c b/backend/dell1600n_net.c
new file mode 100644
index 0000000..d19059b
--- /dev/null
+++ b/backend/dell1600n_net.c
@@ -0,0 +1,2065 @@
+/*
+ sane - Scanner Access Now Easy.
+ Copyright (C) 2006 Jon Chambers <jon@jon.demon.co.uk>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Dell 1600n network scan driver for SANE.
+
+ To debug:
+ SANE_DEBUG_DELL1600N_NET=255 scanimage --verbose 2>scan.errs 1>scan.png
+*/
+
+/***********************************************************
+ * INCLUDES
+ ***********************************************************/
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+
+#define BACKEND_NAME dell1600n_net
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+
+/* :NOTE: these are likely to be platform-specific! */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <jpeglib.h>
+#include <tiffio.h>
+
+/* OS/2... */
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
+
+
+/***********************************************************
+ * DEFINITIONS
+ ***********************************************************/
+
+/* Maximum number of scanners */
+#define MAX_SCANNERS 32
+
+/* version number */
+#define DRIVER_VERSION SANE_VERSION_CODE( SANE_CURRENT_MAJOR, V_MINOR, 0 )
+
+/* size of buffer for socket communication */
+#define SOCK_BUF_SIZE 2048
+
+/* size of registation name */
+#define REG_NAME_SIZE 64
+
+struct DeviceRecord
+{
+ SANE_Device m_device;
+ char * m_pName; /* storage of name */
+ char * m_pModel; /* storage of model */
+};
+
+/* a buffer struct to store "stuff" */
+struct ComBuf
+{
+ size_t m_capacity; /* current allocated size in bytes */
+ size_t m_used; /* current used size in bytes */
+ unsigned char *m_pBuf; /* storage (or NULL if none allocated) */
+};
+
+/* state data for a single scanner connection */
+struct ScannerState
+{
+ int m_udpFd; /* file descriptor to UDP socket */
+ int m_tcpFd; /* file descriptor to TCP socket */
+ struct sockaddr_in m_sockAddr; /* printer address */
+ struct ComBuf m_buf; /* buffer for network data */
+ struct ComBuf m_imageData; /* storage for decoded image data */
+ int m_numPages; /* number of complete pages (host byte order) */
+ struct ComBuf m_pageInfo; /* "array" of numPages PageInfo structs */
+ int m_bFinish; /* set non-0 to signal that we are finished */
+ int m_bCancelled; /* set non-0 that bFinish state arose from cancelation */
+ char m_regName[REG_NAME_SIZE]; /* name with which to register */
+ unsigned short m_xres; /* x resolution (network byte order) */
+ unsigned short m_yres; /* y resolution (network byte order) */
+ unsigned int m_composition; /* composition (0x01=>TIFF/PDF,0x40=>JPEG) (network byte order) */
+ unsigned char m_brightness; /* brightness */
+ unsigned int m_compression; /* compression (0x08=>CCIT Group 4,0x20=>JPEG) (network byte order) */
+ unsigned int m_fileType; /* file type (2=>TIFF,4=>PDF,8=>JPEG)(network byte order) */
+ unsigned int m_pixelWidth; /* width in pixels (network byte order) */
+ unsigned int m_pixelHeight; /* height in pixels (network byte order) */
+ unsigned int m_bytesRead; /* bytes read by SANE (host byte order) */
+ unsigned int m_currentPageBytes;/* number of bytes of current page read (host byte order) */
+};
+
+/* state data for a single page
+ NOTE: all ints are in host byte order
+*/
+struct PageInfo
+{
+ int m_width; /* pixel width */
+ int m_height; /* pixel height */
+ int m_totalSize; /* total page size (bytes) */
+ int m_bytesRemaining; /* number of bytes not yet passed to SANE client */
+};
+
+/* struct for in-memory jpeg decompression */
+struct JpegDataDecompState
+{
+ struct jpeg_decompress_struct m_cinfo; /* base struct */
+ unsigned char *m_pData; /* data pointer */
+ unsigned int m_bytesRemaining; /* amount of unprocessed data */
+};
+
+/* initial ComBuf allocation */
+#define INITIAL_COM_BUF_SIZE 1024
+
+/***********************************************************
+ * FUNCTION PROTOTYPES
+ ***********************************************************/
+
+/* print hex buffer to stdout */
+static void HexDump (int debugLevel, const unsigned char *buf,
+ size_t bufSize);
+
+/* clears gKnownDevices array */
+static void ClearKnownDevices (void);
+
+/* initialise a ComBuf struct */
+static int InitComBuf (struct ComBuf *pBuf);
+
+/* free a ComBuf struct */
+static void FreeComBuf (struct ComBuf *pBuf);
+
+/* add data to a ComBuf struct */
+static int AppendToComBuf (struct ComBuf *pBuf, const unsigned char *pData,
+ size_t datSize);
+
+/* remove data from the front of a ComBuf struct */
+static int PopFromComBuf (struct ComBuf *pBuf, size_t datSize);
+
+/* initialise a packet */
+static int InitPacket (struct ComBuf *pBuf, char type);
+
+/* append message to a packet */
+static int AppendMessageToPacket (struct ComBuf *pBuf,
+ char messageType,
+ char *messageName,
+ char valueType,
+ void *pValue, size_t valueLen);
+
+/* write length data to packet header */
+static void FinalisePacket (struct ComBuf *pBuf);
+
+/* \return 1 if message is complete, 0 otherwise */
+static int MessageIsComplete (unsigned char *pData, size_t size);
+
+/* process a registration broadcast response
+ \return DeviceRecord pointer on success (caller frees), NULL on failure
+*/
+static struct DeviceRecord *ProcessFindResponse (unsigned char *pData, size_t size);
+
+/* frees a scanner state struct stored in gOpenScanners */
+static void FreeScannerState (int iHandle);
+
+/* \return 1 if iHandle is a valid member of gOpenScanners, 0 otherwise */
+static int ValidScannerNumber (int iHandle);
+
+/* process UDP responses, \return 0 in success, >0 otherwise */
+static int ProcessUdpResponse (unsigned char *pData, size_t size,
+ struct ScannerState *pState);
+
+/* process TCP responses, \return 0 in success, >0 otherwise */
+static int ProcessTcpResponse (struct ScannerState *pState,
+ struct ComBuf *pTcpBufBuf);
+
+/* Process the data from a single scanned page, \return 0 in success, >0 otherwise */
+static int ProcessPageData (struct ScannerState *pState);
+
+/* Libjpeg decompression interface */
+static void JpegDecompInitSource (j_decompress_ptr cinfo);
+static boolean JpegDecompFillInputBuffer (j_decompress_ptr cinfo);
+static void JpegDecompSkipInputData (j_decompress_ptr cinfo, long numBytes);
+static void JpegDecompTermSource (j_decompress_ptr cinfo);
+
+/***********************************************************
+ * GLOBALS
+ ***********************************************************/
+
+/* Results of last call to sane_get_devices */
+static struct DeviceRecord *gKnownDevices[MAX_SCANNERS];
+
+/* Array of open scanner device states.
+ :NOTE: (int)SANE_Handle is an offset into this array */
+static struct ScannerState *gOpenScanners[MAX_SCANNERS];
+
+/* scanner port */
+static unsigned short gScannerPort = 1124;
+
+/* ms to wait for registration replies */
+static unsigned short gRegReplyWaitMs = 300;
+
+/***********************************************************
+ * FUNCTION IMPLEMENTATIONS
+ ***********************************************************/
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+
+ /* init globals */
+ memset (gKnownDevices, 0, sizeof (gKnownDevices));
+ memset (gOpenScanners, 0, sizeof (gOpenScanners));
+
+ /* report version */
+ *version_code = DRIVER_VERSION;
+
+ /* init debug */
+ DBG_INIT ();
+
+ return SANE_STATUS_GOOD;
+
+} /* sane_init */
+
+/***********************************************************/
+
+void
+sane_exit (void)
+{
+
+ int iHandle;
+
+ /* clean up */
+ ClearKnownDevices ();
+
+ for (iHandle = 0; iHandle < MAX_SCANNERS; ++iHandle)
+ {
+ if (gOpenScanners[iHandle])
+ FreeScannerState (iHandle);
+ }
+
+} /* sane_exit */
+
+/***********************************************************/
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+
+ int ret;
+ unsigned char sockBuf[SOCK_BUF_SIZE];
+ int sock, optYes;
+ struct DeviceRecord *pDevice;
+ struct ComBuf queryPacket;
+ struct sockaddr_in remoteAddr;
+ unsigned char ucVal;
+ fd_set readFds;
+ struct timeval selTimeVal;
+ int nread, iNextDevice;
+ FILE *fConfig;
+ char configBuf[ 256 ];
+ const char *pVal;
+ int valLen;
+
+ /* init variables */
+ ret = SANE_STATUS_GOOD;
+ sock = 0;
+ pDevice = NULL;
+ optYes = 1;
+ InitComBuf (&queryPacket);
+
+ /* clear previous results */
+ ClearKnownDevices ();
+ iNextDevice = 0;
+
+ /* look for a config file */
+ fConfig = sanei_config_open( "dell1600n_net.conf" );
+ if ( fConfig )
+ {
+ while ( ! feof( fConfig ) )
+ {
+ if ( ! sanei_config_read ( configBuf, sizeof( configBuf ), fConfig ) ) break;
+
+ /* skip whitespace */
+ pVal = sanei_config_skip_whitespace ( configBuf );
+
+ /* skip comments */
+ if ( *pVal == '#' ) continue;
+
+ /* process named_scanner */
+ valLen = strlen( "named_scanner:" );
+ if ( ! strncmp( pVal, "extra_scanner:", valLen ) ){
+
+ pVal = sanei_config_skip_whitespace ( pVal + valLen );
+
+ pDevice = malloc (sizeof (struct DeviceRecord));
+ if (!pDevice)
+ {
+ DBG (1, "sane_get_devices: memory allocation failure\n");
+ break;
+ }
+
+ pDevice->m_pName = strdup (pVal);
+ pDevice->m_device.vendor = "Dell";
+ pDevice->m_pModel = strdup( "1600n" );
+ pDevice->m_device.type = "multi-function peripheral";
+
+ pDevice->m_device.name = pDevice->m_pName;
+ pDevice->m_device.model = pDevice->m_pModel;
+
+ /* add to list */
+ gKnownDevices[iNextDevice++] = pDevice;
+
+ continue;
+ } /* if */
+
+ } /* while */
+
+ /* Close the file */
+ fclose( fConfig );
+ }
+
+ /* open UDP socket */
+ sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock == -1)
+ {
+ DBG (1, "Error creating socket\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ setsockopt (sock, SOL_SOCKET, SO_BROADCAST, &optYes, sizeof (optYes));
+
+ /* prepare select mask */
+ FD_ZERO (&readFds);
+ FD_SET (sock, &readFds);
+ selTimeVal.tv_sec = 0;
+ selTimeVal.tv_usec = gRegReplyWaitMs * 1000;
+
+ /* init a packet */
+ InitPacket (&queryPacket, 0x01);
+
+ /* add query */
+ ucVal = 0;
+ AppendMessageToPacket (&queryPacket, 0x25, "std-scan-discovery-all",
+ 0x02, &ucVal, sizeof (ucVal));
+
+ FinalisePacket (&queryPacket);
+
+ DBG (10, "Sending:\n");
+ HexDump (10, queryPacket.m_pBuf, queryPacket.m_used);
+
+
+ remoteAddr.sin_family = AF_INET;
+ remoteAddr.sin_port = htons (gScannerPort);
+ remoteAddr.sin_addr.s_addr = 0xFFFFFFFF; /* broadcast */
+
+ if (sendto (sock, queryPacket.m_pBuf, queryPacket.m_used, 0,
+ &remoteAddr, sizeof (remoteAddr)) == -1)
+ {
+ DBG (1, "Error sending broadcast packet\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* process replies */
+ while (select (sock + 1, &readFds, NULL, NULL, &selTimeVal))
+ {
+
+ /* break if we've got no more storage space in array */
+ if (iNextDevice >= MAX_SCANNERS)
+ {
+ DBG (1, "sane_get_devices: more than %d devices, ignoring\n",
+ MAX_SCANNERS);
+ break;
+ }
+
+ nread = read (sock, sockBuf, sizeof (sockBuf));
+ DBG (5, "Got a broadcast response, (%d bytes)\n", nread);
+
+ if (nread <= 0)
+ break;
+
+ HexDump (10, sockBuf, nread);
+
+ /* process response (skipping bad ones) */
+ if (!(pDevice = ProcessFindResponse (sockBuf, nread))) continue;
+
+ /* add to list */
+ gKnownDevices[iNextDevice++] = pDevice;
+
+ } /* while */
+
+ /* report our finds */
+ *device_list = (const SANE_Device **) gKnownDevices;
+
+cleanup:
+
+ if (sock)
+ close (sock);
+ FreeComBuf (&queryPacket);
+
+ return ret;
+
+} /* sane_get_devices */
+
+/***********************************************************/
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+
+ int iHandle = -1, i;
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct hostent *pHostent;
+ char *pDot;
+
+ DBG( 5, "sane_open: %s\n", devicename );
+
+ /* find the next available scanner pointer in gOpenScanners */
+ for (i = 0; i < MAX_SCANNERS; ++i)
+ {
+
+ if (gOpenScanners[i]) continue;
+
+ iHandle = i;
+ break;
+
+ } /* for */
+ if (iHandle == -1)
+ {
+ DBG (1, "sane_open: no space left in gOpenScanners array\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* allocate some space */
+ if (!(gOpenScanners[iHandle] = malloc (sizeof (struct ScannerState))))
+ {
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* init data */
+ memset (gOpenScanners[iHandle], 0, sizeof (struct ScannerState));
+ InitComBuf (&gOpenScanners[iHandle]->m_buf);
+ InitComBuf (&gOpenScanners[iHandle]->m_imageData);
+ InitComBuf (&gOpenScanners[iHandle]->m_pageInfo);
+ gOpenScanners[iHandle]->m_xres = ntohs (200);
+ gOpenScanners[iHandle]->m_yres = ntohs (200);
+ gOpenScanners[iHandle]->m_composition = ntohl (0x01);
+ gOpenScanners[iHandle]->m_brightness = 0x80;
+ gOpenScanners[iHandle]->m_compression = ntohl (0x08);
+ gOpenScanners[iHandle]->m_fileType = ntohl (0x02);
+
+
+ /* look up scanner name */
+ pHostent = gethostbyname (devicename);
+ if ((!pHostent) || (!pHostent->h_addr_list))
+ {
+ DBG (1, "sane_open: error looking up scanner name %s\n", devicename);
+ status = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ /* open a UDP socket */
+ if (!(gOpenScanners[iHandle]->m_udpFd =
+ socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)))
+ {
+ DBG (1, "sane_open: error opening socket\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* connect to the scanner */
+ memset (&gOpenScanners[iHandle]->m_sockAddr, 0,
+ sizeof (gOpenScanners[iHandle]->m_sockAddr));
+ gOpenScanners[iHandle]->m_sockAddr.sin_family = AF_INET;
+ gOpenScanners[iHandle]->m_sockAddr.sin_port = htons (gScannerPort);
+ memcpy (&gOpenScanners[iHandle]->m_sockAddr.sin_addr,
+ pHostent->h_addr_list[0], pHostent->h_length);
+ if (connect (gOpenScanners[iHandle]->m_udpFd,
+ (struct sockaddr *) &gOpenScanners[iHandle]->m_sockAddr,
+ sizeof (gOpenScanners[iHandle]->m_sockAddr)))
+ {
+ DBG (1, "sane_open: error connecting to %s:%d\n", devicename,
+ gScannerPort);
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* set fallback registration name */
+ sprintf (gOpenScanners[iHandle]->m_regName, "Sane");
+
+ /* try to fill in hostname */
+ gethostname (gOpenScanners[iHandle]->m_regName, REG_NAME_SIZE);
+
+ /* just in case... */
+ gOpenScanners[iHandle]->m_regName[REG_NAME_SIZE - 1] = 0;
+
+ /* chop off any domain (if any) */
+ if ((pDot = strchr (gOpenScanners[iHandle]->m_regName, '.')))
+ *pDot = 0;
+
+ DBG (5, "sane_open: connected to %s:%d as %s\n", devicename, gScannerPort,
+ gOpenScanners[iHandle]->m_regName);
+
+
+ /* set the handle */
+ *handle = (SANE_Handle) (unsigned long)iHandle;
+
+ return status;
+
+cleanup:
+
+ if (iHandle != -1)
+ FreeScannerState (iHandle);
+
+ return status;
+
+} /* sane_open */
+
+/***********************************************************/
+
+void
+sane_close (SANE_Handle handle)
+{
+
+ DBG( 5, "sane_close: %lx\n", (unsigned long)handle );
+
+ FreeScannerState ((unsigned long) handle);
+
+} /* sane_close */
+
+/***********************************************************/
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle __sane_unused__ handle,
+ SANE_Int option)
+{
+
+ static SANE_Option_Descriptor numOptions = {
+ "num_options",
+ "Number of options",
+ "Number of options",
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ 1,
+ 0,
+ 0,
+ {0}
+ };
+
+ if (option == 0)
+ return &numOptions;
+ else
+ return NULL;
+
+} /* sane_get_option_descriptor */
+
+/***********************************************************/
+
+SANE_Status
+sane_control_option (SANE_Handle __sane_unused__ handle, SANE_Int option,
+ SANE_Action action, void *value,
+ SANE_Int __sane_unused__ * info)
+{
+
+ static int numOptions = 1;
+
+ if (action == SANE_ACTION_GET_VALUE && option == 0)
+ *(int *) value = numOptions;
+
+ return SANE_STATUS_GOOD;
+
+} /* sane_control_option */
+
+/***********************************************************/
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int iHandle = (int) (unsigned long)handle;
+ unsigned int width, height, imageSize;
+ struct PageInfo pageInfo;
+
+ if (!gOpenScanners[iHandle])
+ return SANE_STATUS_INVAL;
+
+ /* fetch page info */
+ memcpy( & pageInfo, gOpenScanners[iHandle]->m_pageInfo.m_pBuf, sizeof( pageInfo ) );
+
+ width = pageInfo.m_width;
+ height = pageInfo.m_height;
+ imageSize = width * height * 3;
+
+ DBG( 5, "sane_get_parameters: bytes remaining on this page: %d, num pages: %d, size: %dx%d\n",
+ pageInfo.m_bytesRemaining,
+ gOpenScanners[iHandle]->m_numPages,
+ width,
+ height );
+
+ DBG (5,
+ "sane_get_parameters: handle %x: bytes outstanding: %lu, image size: %d\n",
+ iHandle, (unsigned long)gOpenScanners[iHandle]->m_imageData.m_used, imageSize);
+
+ /* check for enough data */
+ /*
+ if (gOpenScanners[iHandle]->m_imageData.m_used < imageSize)
+ {
+ DBG (1, "sane_get_parameters: handle %d: not enough data: %d < %d\n",
+ iHandle, gOpenScanners[iHandle]->m_imageData.m_used, imageSize);
+ return SANE_STATUS_INVAL;
+ }
+ */
+
+
+ params->format = SANE_FRAME_RGB;
+ params->last_frame = SANE_TRUE;
+ params->lines = height;
+ params->depth = 8;
+ params->pixels_per_line = width;
+ params->bytes_per_line = width * 3;
+
+ return SANE_STATUS_GOOD;
+
+} /* sane_get_parameters */
+
+/***********************************************************/
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct ComBuf buf;
+ unsigned char sockBuf[SOCK_BUF_SIZE];
+ int iHandle, nread;
+ int errorCheck = 0;
+ struct sockaddr_in myAddr;
+ socklen_t addrSize;
+ fd_set readFds;
+ struct timeval selTimeVal;
+
+ iHandle = (int) (unsigned long)handle;
+
+ DBG( 5, "sane_start: %x\n", iHandle );
+
+ /* fetch and check scanner index */
+ if (!ValidScannerNumber (iHandle))
+ return SANE_STATUS_INVAL;
+
+ /* check if we still have oustanding pages of data on this handle */
+ if (gOpenScanners[iHandle]->m_imageData.m_used){
+
+ /* remove empty page */
+ PopFromComBuf ( & gOpenScanners[iHandle]->m_pageInfo, sizeof( struct PageInfo ) );
+ return SANE_STATUS_GOOD;
+
+ }
+
+ /* determine local IP address */
+ addrSize = sizeof (myAddr);
+ if (getsockname (gOpenScanners[iHandle]->m_udpFd, &myAddr, &addrSize))
+ {
+ DBG (1, "sane_start: Error getting own IP address\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* init a buffer for our registration message */
+ errorCheck |= InitComBuf (&buf);
+
+ /* build packet */
+ errorCheck |= InitPacket (&buf, 1);
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-subscribe-user-name", 0x0b,
+ gOpenScanners[iHandle]->m_regName,
+ strlen (gOpenScanners[iHandle]->m_regName));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-subscribe-ip-address", 0x0a,
+ &myAddr.sin_addr, 4);
+ FinalisePacket (&buf);
+
+ /* check nothing went wrong along the way */
+ if (errorCheck)
+ {
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* send the packet */
+ send (gOpenScanners[iHandle]->m_udpFd, buf.m_pBuf, buf.m_used, 0);
+
+
+ /* loop until done */
+ gOpenScanners[iHandle]->m_bFinish = 0;
+ while (!gOpenScanners[iHandle]->m_bFinish)
+ {
+
+ /* prepare select mask */
+ FD_ZERO (&readFds);
+ FD_SET (gOpenScanners[iHandle]->m_udpFd, &readFds);
+ selTimeVal.tv_sec = 1;
+ selTimeVal.tv_usec = 0;
+
+
+
+ DBG (5, "sane_start: waiting for scan signal\n");
+
+ /* wait again if nothing received */
+ if (!select (gOpenScanners[iHandle]->m_udpFd + 1,
+ &readFds, NULL, NULL, &selTimeVal))
+ continue;
+
+ /* read from socket */
+ nread =
+ read (gOpenScanners[iHandle]->m_udpFd, sockBuf, sizeof (sockBuf));
+
+ if (nread <= 0)
+ {
+ DBG (1, "sane_start: read returned %d\n", nread);
+ break;
+ }
+
+ /* process the response */
+ if (ProcessUdpResponse (sockBuf, nread, gOpenScanners[iHandle]))
+ {
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ } /* while */
+
+ /* check whether we were cancelled */
+ if ( gOpenScanners[iHandle]->m_bCancelled ) status = SANE_STATUS_CANCELLED;
+
+cleanup:
+
+ FreeComBuf (&buf);
+
+ return status;
+
+} /* sane_start */
+
+/***********************************************************/
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+
+ int iHandle = (int) (unsigned long)handle;
+ int dataSize;
+ struct PageInfo pageInfo;
+
+ DBG( 5, "sane_read: %x (max_length=%d)\n", iHandle, max_length );
+
+ *length = 0;
+
+ if (!gOpenScanners[iHandle])
+ return SANE_STATUS_INVAL;
+
+ /* check for end of data (no further pages) */
+ if ( ( ! gOpenScanners[iHandle]->m_imageData.m_used )
+ || ( ! gOpenScanners[iHandle]->m_numPages ) )
+ {
+ /* remove empty page if there are no more cached pages */
+ PopFromComBuf ( & gOpenScanners[iHandle]->m_pageInfo, sizeof( struct PageInfo ) );
+
+ return SANE_STATUS_EOF;
+ }
+
+ /* fetch page info */
+ memcpy( & pageInfo, gOpenScanners[iHandle]->m_pageInfo.m_pBuf, sizeof( pageInfo ) );
+
+ /* check for end of page data (we still have further cached pages) */
+ if ( pageInfo.m_bytesRemaining < 1 ) return SANE_STATUS_EOF;
+
+ /* send the remainder of the current image */
+ dataSize = pageInfo.m_bytesRemaining;
+
+ /* unless there's not enough room in the output buffer */
+ if (dataSize > max_length)
+ dataSize = max_length;
+
+ /* update the data sent counters */
+ gOpenScanners[iHandle]->m_bytesRead += dataSize;
+ pageInfo.m_bytesRemaining -= dataSize;
+
+ /* update counter */
+ memcpy( gOpenScanners[iHandle]->m_pageInfo.m_pBuf, & pageInfo, sizeof( pageInfo ) );
+
+ /* check for end of page */
+ if ( pageInfo.m_bytesRemaining < 1 ){
+
+ /* yes, so remove page info */
+ gOpenScanners[iHandle]->m_numPages--;
+
+ } /* if */
+
+ DBG (5,
+ "sane_read: sending %d bytes, image total %d, %d page bytes remaining, %lu total remaining, image: %dx%d\n",
+ dataSize, gOpenScanners[iHandle]->m_bytesRead, pageInfo.m_bytesRemaining ,
+ (unsigned long)(gOpenScanners[iHandle]->m_imageData.m_used - dataSize),
+ pageInfo.m_width,
+ pageInfo.m_height);
+
+ /* copy the data */
+ memcpy (data, gOpenScanners[iHandle]->m_imageData.m_pBuf, dataSize);
+ if (PopFromComBuf (&gOpenScanners[iHandle]->m_imageData, dataSize))
+ return SANE_STATUS_NO_MEM;
+
+ *length = dataSize;
+
+ return SANE_STATUS_GOOD;
+
+} /* sane_read */
+
+/***********************************************************/
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ int iHandle = (int) (unsigned long)handle;
+
+ DBG( 5, "sane_cancel: %x\n", iHandle );
+
+ /* signal that bad things are afoot */
+ gOpenScanners[iHandle]->m_bFinish = 1;
+ gOpenScanners[iHandle]->m_bCancelled = 1;
+
+} /* sane_cancel */
+
+/***********************************************************/
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+
+ return SANE_STATUS_UNSUPPORTED;
+
+} /* sane_set_io_mode */
+
+/***********************************************************/
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ * fd)
+{
+
+ return SANE_STATUS_UNSUPPORTED;
+
+} /* sane_get_select_fd */
+
+/***********************************************************/
+
+/* Clears the contents of gKnownDevices and zeros it */
+void
+ClearKnownDevices ()
+{
+
+ int i;
+
+ for (i = 0; i < MAX_SCANNERS; ++i)
+ {
+
+ if (gKnownDevices[i])
+ {
+ if (gKnownDevices[i]->m_pName) free ( gKnownDevices[i]->m_pName );
+ if (gKnownDevices[i]->m_pModel) free ( gKnownDevices[i]->m_pModel );
+ free ( gKnownDevices[i] );
+ }
+ gKnownDevices[i] = NULL;
+
+ }
+
+} /* ClearKnownDevices */
+
+/***********************************************************/
+
+/* print hex buffer to debug output */
+void
+HexDump (int debugLevel, const unsigned char *buf, size_t bufSize)
+{
+
+ unsigned int i, j;
+
+ char itemBuf[16] = { 0 }, lineBuf[256] = { 0 };
+
+ if (DBG_LEVEL < debugLevel)
+ return;
+
+ for (i = 0; i < bufSize; ++i)
+ {
+
+ if (!(i % 16))
+ sprintf (lineBuf, "%p: ", (buf + i));
+
+ sprintf (itemBuf, "%02x ", (const unsigned int) buf[i]);
+
+ strncat (lineBuf, itemBuf, sizeof (lineBuf));
+
+ if ((i + 1) % 16)
+ continue;
+
+ /* print string equivalent */
+ for (j = i - 15; j <= i; ++j)
+ {
+
+ if ((buf[j] >= 0x20) && (!(buf[j] & 0x80)))
+ {
+ sprintf (itemBuf, "%c", buf[j]);
+ }
+ else
+ {
+ sprintf (itemBuf, ".");
+ }
+ strncat (lineBuf, itemBuf, sizeof (lineBuf));
+
+ } /* for j */
+
+ DBG (debugLevel, "%s\n", lineBuf);
+ lineBuf[0] = 0;
+
+ } /* for i */
+
+ if (i % 16)
+ {
+
+ for (j = (i % 16); j < 16; ++j)
+ {
+ strncat (lineBuf, " ", sizeof (lineBuf));
+ }
+ for (j = 1 + i - ((i + 1) % 16); j < i; ++j)
+ {
+ if ((buf[j] >= 0x20) && (!(buf[j] & 0x80)))
+ {
+ sprintf (itemBuf, "%c", buf[j]);
+ }
+ else
+ {
+ strcpy (itemBuf, ".");
+ }
+ strncat (lineBuf, itemBuf, sizeof (lineBuf));
+ }
+ DBG (debugLevel, "%s\n", lineBuf);
+ }
+} /* HexDump */
+
+/***********************************************************/
+
+/* initialise a ComBuf struct
+ \return 0 on success, >0 on failure
+*/
+int
+InitComBuf (struct ComBuf *pBuf)
+{
+
+ memset (pBuf, 0, sizeof (struct ComBuf));
+
+ pBuf->m_pBuf = malloc (INITIAL_COM_BUF_SIZE);
+ if (!pBuf->m_pBuf)
+ return 1;
+
+ pBuf->m_capacity = INITIAL_COM_BUF_SIZE;
+ pBuf->m_used = 0;
+
+ return 0;
+
+} /* InitComBuf */
+
+/***********************************************************/
+
+/* free a ComBuf struct */
+void
+FreeComBuf (struct ComBuf *pBuf)
+{
+
+ if (pBuf->m_pBuf)
+ free (pBuf->m_pBuf);
+ memset (pBuf, 0, sizeof (struct ComBuf));
+
+} /* FreeComBuf */
+
+/***********************************************************/
+
+/* add data to a ComBuf struct
+ \return 0 on success, >0 on failure
+ \note If pData is NULL then buffer size will be increased but no copying will take place
+ \note In case of failure pBuf will be released using FreeComBuf
+*/
+int
+AppendToComBuf (struct ComBuf *pBuf, const unsigned char *pData,
+ size_t datSize)
+{
+
+ size_t newSize;
+
+ /* check we have enough space */
+ if (pBuf->m_used + datSize > pBuf->m_capacity)
+ {
+ /* nope - allocate some more */
+ newSize = pBuf->m_used + datSize + INITIAL_COM_BUF_SIZE;
+ pBuf->m_pBuf = realloc (pBuf->m_pBuf, newSize);
+ if (!pBuf->m_pBuf)
+ {
+ DBG (1, "AppendToComBuf: memory allocation error");
+ FreeComBuf (pBuf);
+ return (1);
+ }
+ pBuf->m_capacity = newSize;
+ } /* if */
+
+ /* add data */
+ if (pData)
+ memcpy (pBuf->m_pBuf + pBuf->m_used, pData, datSize);
+ pBuf->m_used += datSize;
+
+ return 0;
+
+} /* AppendToComBuf */
+
+/***********************************************************/
+
+/* append message to a packet
+ \return 0 if ok, 1 if bad */
+int
+AppendMessageToPacket (struct ComBuf *pBuf, /* packet to which to append */
+ char messageType, /* type of message */
+ char *messageName, /* name of message */
+ char valueType, /* type of value */
+ void *pValue, /* pointer to value */
+ size_t valueLen /* length of value (bytes) */
+ )
+{
+
+ unsigned short slen;
+
+ /* message type */
+ AppendToComBuf (pBuf, (void *) &messageType, 1);
+
+ /* message length */
+ slen = htons (strlen (messageName));
+ AppendToComBuf (pBuf, (void *) &slen, 2);
+
+ /* and name */
+ AppendToComBuf (pBuf, (void *) messageName, strlen (messageName));
+
+ /* and value type */
+ AppendToComBuf (pBuf, (void *) &valueType, 1);
+
+ /* value length */
+ slen = htons (valueLen);
+ AppendToComBuf (pBuf, (void *) &slen, 2);
+
+ /* and value */
+ return (AppendToComBuf (pBuf, (void *) pValue, valueLen));
+
+} /* AppendMessageToPacket */
+
+/***********************************************************/
+
+/* Initialise a packet
+ \param pBuf : An initialise ComBuf
+ \param type : either 0x01 ("normal" ) or 0x02 ("reply" )
+ \return 0 on success, >0 otherwise
+*/
+int
+InitPacket (struct ComBuf *pBuf, char type)
+{
+
+ char header[8] = { 2, 0, 0, 2, 0, 0, 0, 0 };
+
+ header[2] = type;
+
+ /* reset size */
+ pBuf->m_used = 0;
+
+ /* add header */
+ return (AppendToComBuf (pBuf, (void *) &header, 8));
+
+} /* InitPacket */
+
+/***********************************************************/
+
+/* write length data to packet header
+*/
+void
+FinalisePacket (struct ComBuf *pBuf)
+{
+
+ /* sanity check */
+ if (pBuf->m_used < 8)
+ return;
+
+ /* set the size */
+ *((unsigned short *) (pBuf->m_pBuf + 6)) = htons (pBuf->m_used - 8);
+
+ DBG (20, "FinalisePacket: outgoing packet:\n");
+ HexDump (20, pBuf->m_pBuf, pBuf->m_used);
+
+} /* FinalisePacket */
+
+/***********************************************************/
+
+/* \return 1 if message is complete, 0 otherwise */
+int
+MessageIsComplete (unsigned char *pData, size_t size)
+{
+ unsigned short dataSize;
+
+ /* sanity check */
+ if (size < 8)
+ return 0;
+
+ /* :NOTE: we can't just cast to a short as data may not be aligned */
+ dataSize = (((unsigned short) pData[6]) << 8) | pData[7];
+
+ DBG (20, "MessageIsComplete: data size = %d\n", dataSize);
+
+ if (size >= (size_t) (dataSize + 8))
+ return 1;
+ else
+ return 0;
+
+} /* MessageIsComplete */
+
+/***********************************************************/
+
+/* process a registration broadcast response
+ \return struct DeviceRecord pointer on success (caller frees), NULL on failure
+*/
+struct DeviceRecord *
+ProcessFindResponse (unsigned char *pData, size_t size)
+{
+
+ struct DeviceRecord *pDevice = NULL;
+ unsigned short messageSize, nameSize, valueSize;
+ unsigned char *pItem, *pEnd, *pValue;
+ char printerName[256] = { 0 };
+ char printerModel[256] = "1600n";
+ char *pModel, *pName;
+
+
+ DBG (10, "ProcessFindResponse: processing %lu bytes, pData=%p\n",
+ (unsigned long)size, pData);
+
+ /* check we have a complete packet */
+ if (!MessageIsComplete (pData, size))
+ {
+ DBG (1, "ProcessFindResponse: Ignoring incomplete packet\n");
+ return NULL;
+ }
+
+ /* extract data size */
+ messageSize = (((unsigned short) (pData[6])) << 8) | pData[7];
+
+ /* loop through items in message */
+ pItem = pData + 8;
+ pEnd = pItem + messageSize;
+ while (pItem < pEnd)
+ {
+
+ pItem++;
+ nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
+ pItem += 2;
+ pName = (char *) pItem;
+
+ pItem += nameSize;
+
+ pItem++;
+ valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
+ pItem += 2;
+
+ pValue = pItem;
+
+ pItem += valueSize;
+
+ /* process the item */
+ if (!strncmp ("std-scan-discovery-ip", pName, nameSize))
+ {
+
+ snprintf (printerName, sizeof (printerName), "%d.%d.%d.%d",
+ (int) pValue[0],
+ (int) pValue[1], (int) pValue[2], (int) pValue[3]);
+ DBG (2, "%s\n", printerName);
+
+ }
+ else if (!strncmp ("std-scan-discovery-model-name", pName, nameSize))
+ {
+
+ memset (printerModel, 0, sizeof (printerModel));
+ if (valueSize > (sizeof (printerModel) - 1))
+ valueSize = sizeof (printerModel) - 1;
+ memcpy (printerModel, pValue, valueSize);
+ DBG (2, "std-scan-discovery-model-name: %s\n", printerModel);
+
+ }
+
+ } /* while pItem */
+
+ /* just in case nothing sensible was found */
+ if ( ! strlen( printerName ) ) return NULL;
+
+ pDevice = malloc (sizeof (struct DeviceRecord));
+ if (!pDevice)
+ {
+ DBG (1, "ProcessFindResponse: memory allocation failure\n");
+ return NULL;
+ }
+
+ /* knock off "Dell " from start of model name */
+ pModel = printerModel;
+ if ( ! strncmp( pModel, "Dell ", 5 ) )
+ pModel += 5;
+
+ pDevice->m_pName = strdup( printerName );
+ pDevice->m_device.vendor = "Dell";
+ pDevice->m_pModel = strdup (pModel);
+ pDevice->m_device.type = "multi-function peripheral";
+
+ pDevice->m_device.name = pDevice->m_pName;
+ pDevice->m_device.model = pDevice->m_pModel;
+
+ return pDevice;
+
+} /* ProcessFindResponse */
+
+/***********************************************************/
+
+/* frees a scanner state struct stored in gOpenScanners */
+void
+FreeScannerState (int iHandle)
+{
+
+ /* check range etc */
+ if (!ValidScannerNumber (iHandle))
+ return;
+
+ /* close UDP handle */
+ if (gOpenScanners[iHandle]->m_udpFd)
+ close (gOpenScanners[iHandle]->m_udpFd);
+
+ /* free m_buf */
+ FreeComBuf (&gOpenScanners[iHandle]->m_buf);
+
+ /* free m_imageData */
+ FreeComBuf (&gOpenScanners[iHandle]->m_imageData);
+
+ /* free the struct */
+ free (gOpenScanners[iHandle]);
+
+ /* set pointer to NULL */
+ gOpenScanners[iHandle] = NULL;
+
+} /* FreeScannerState */
+
+/***********************************************************/
+
+/* \return 1 if iHandle is a valid member of gOpenScanners, 0 otherwise */
+int
+ValidScannerNumber (int iHandle)
+{
+ /* check range */
+ if ((iHandle < 0) || (iHandle >= MAX_SCANNERS))
+ {
+ DBG (1, "ValidScannerNumber: invalid scanner index %d", iHandle);
+ return 0;
+ }
+
+ /* check non-NULL pointer */
+ if (!gOpenScanners[iHandle])
+ {
+ DBG (1, "ValidScannerNumber: NULL scanner struct %d", iHandle);
+ return 0;
+ }
+
+ /* OK */
+ return 1;
+
+} /* ValidScannerNumber */
+
+/***********************************************************/
+
+/* process UDP responses
+ \return 0 in success, >0 otherwise */
+static int
+ProcessUdpResponse (unsigned char *pData, size_t size,
+ struct ScannerState *pState)
+{
+
+ unsigned short messageSize, nameSize, valueSize;
+ unsigned char *pItem, *pEnd, *pValue;
+ char sockBuf[SOCK_BUF_SIZE], *pName;
+ struct ComBuf tcpBuf;
+ int nread;
+ unsigned int numUsed;
+
+ HexDump (15, pData, size);
+
+ DBG (10, "ProcessUdpResponse: processing %lu bytes, pData=%p\n",
+ (unsigned long)size, pData);
+
+ /* check we have a complete packet */
+ if (!MessageIsComplete (pData, size))
+ {
+ DBG (1, "ProcessUdpResponse: Ignoring incomplete packet\n");
+ return 1;
+ }
+
+ /* init a com buf for use in tcp communication */
+ InitComBuf (&tcpBuf);
+
+ /* extract data size */
+ messageSize = (((unsigned short) (pData[6])) << 8) | pData[7];
+
+ /* loop through items in message */
+ pItem = pData + 8;
+ pEnd = pItem + messageSize;
+ while (pItem < pEnd)
+ {
+
+ pItem++;
+ nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
+ pItem += 2;
+ pName = (char *) pItem;
+
+ pItem += nameSize;
+
+ pItem++;
+ valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
+ pItem += 2;
+
+ pValue = pItem;
+
+ pItem += valueSize;
+
+ /* process the item */
+ if (!strncmp ("std-scan-request-tcp-connection", pName, nameSize))
+ {
+
+ /* open TCP socket to scanner */
+ if (!(pState->m_tcpFd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)))
+ {
+ DBG (1, "ProcessUdpResponse: error opening TCP socket\n");
+ return 2;
+ }
+ if (connect (pState->m_tcpFd,
+ (struct sockaddr *) &pState->m_sockAddr,
+ sizeof (pState->m_sockAddr)))
+ {
+ DBG (1,
+ "ProcessUdpResponse: error connecting to scanner TCP port\n");
+ goto cleanup;
+ }
+
+ DBG (1, "ProcessUdpResponse: opened TCP connection to scanner\n");
+
+ /* clear read buf */
+ tcpBuf.m_used = 0;
+
+ /* TCP read loop */
+ while (1)
+ {
+
+ nread = read (pState->m_tcpFd, sockBuf, sizeof (sockBuf));
+
+ if (nread <= 0)
+ {
+ DBG (1, "ProcessUdpResponse: TCP read returned %d\n",
+ nread);
+ break;
+ }
+
+ /* append message to buffer */
+ if (AppendToComBuf (&tcpBuf, (unsigned char *) sockBuf, nread))
+ goto cleanup;
+
+ /* process all available responses */
+ while (tcpBuf.m_used)
+ {
+
+ /* note the buffer size before the call */
+ numUsed = tcpBuf.m_used;
+
+ /* process the response */
+ if (ProcessTcpResponse (pState, &tcpBuf))
+ goto cleanup;
+
+ /* if the buffer size has not changed then assume no more processing is possible */
+ if (numUsed == tcpBuf.m_used)
+ break;
+
+ } /* while */
+
+ } /* while */
+
+ close (pState->m_tcpFd);
+ DBG (1, "ProcessUdpResponse: closed TCP connection to scanner\n");
+
+ /* signal end of session */
+ pState->m_bFinish = 1;
+
+ } /* if */
+
+ } /* while pItem */
+
+ return 0;
+
+cleanup:
+
+ FreeComBuf (&tcpBuf);
+ close (pState->m_tcpFd);
+ return 3;
+
+} /* ProcessUdpResponse */
+
+/***********************************************************/
+
+/* process TCP responses, \return 0 in success, >0 otherwise */
+int
+ProcessTcpResponse (struct ScannerState *pState, struct ComBuf *pTcpBuf)
+{
+
+ struct ComBuf buf;
+ unsigned short messageSize = 0, nameSize, valueSize, dataChunkSize;
+ unsigned char *pItem, *pEnd, *pValue;
+ unsigned char *pData = pTcpBuf->m_pBuf;
+ char *pName;
+ unsigned int uiVal;
+ int errorCheck = 0;
+ int bProcessImage = 0;
+
+ DBG (10, "ProcessTcpResponse: processing %lu bytes, pData=%p\n",
+ (unsigned long)pTcpBuf->m_used, pData);
+ HexDump (15, pData, pTcpBuf->m_used);
+
+ /* if message not complete then wait for more to arrive */
+ if (!MessageIsComplete (pData, pTcpBuf->m_used))
+ {
+ DBG (10, "ProcessTcpResponse: incomplete message, returning\n");
+ return 0;
+ }
+
+ /* init a buffer for our outbound messages */
+ if (InitComBuf (&buf))
+ {
+ errorCheck |= 1;
+ goto cleanup;
+ }
+
+ /* extract data size */
+ messageSize = (((unsigned short) (pData[6])) << 8) | pData[7];
+
+ /* loop through items in message */
+ pItem = pData + 8;
+ pEnd = pItem + messageSize;
+ while (pItem < pEnd)
+ {
+
+ pItem++;
+ nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
+ pItem += 2;
+ pName = (char *) pItem;
+
+ pItem += nameSize;
+
+ pItem++;
+ valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
+ pItem += 2;
+
+ pValue = pItem;
+
+ pItem += valueSize;
+
+ /* process the item */
+ if (!strncmp ("std-scan-session-open", pName, nameSize))
+ {
+
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-session-open-response", 0x05,
+ &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+
+ }
+ else if (!strncmp ("std-scan-getclientpref", pName, nameSize))
+ {
+
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-x1",
+ 0x05, &uiVal, sizeof (uiVal));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-x2",
+ 0x05, &uiVal, sizeof (uiVal));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-y1",
+ 0x05, &uiVal, sizeof (uiVal));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-y2",
+ 0x05, &uiVal, sizeof (uiVal));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-xresolution", 0x04,
+ &pState->m_xres, sizeof (pState->m_xres));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-yresolution", 0x04,
+ &pState->m_yres, sizeof (pState->m_yres));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-image-composition",
+ 0x06, &pState->m_composition,
+ sizeof (pState->m_composition));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-brightness", 0x02,
+ &pState->m_brightness,
+ sizeof (pState->m_brightness));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-image-compression",
+ 0x06, &pState->m_compression,
+ sizeof (pState->m_compression));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-file-type", 0x06,
+ &pState->m_fileType,
+ sizeof (pState->m_fileType));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-paper-size-detect",
+ 0x06, &uiVal, sizeof (uiVal));
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-getclientpref-paper-scanner-type",
+ 0x06, &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+
+ }
+ else if (!strncmp ("std-scan-document-start", pName, nameSize))
+ {
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-document-start-response", 0x05,
+ &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+ }
+ else if (!strncmp ("std-scan-document-file-type", pName, nameSize))
+ {
+ memcpy (&pState->m_fileType, pValue, sizeof (pState->m_fileType));
+ DBG (5, "File type: %x\n", ntohl (pState->m_fileType));
+ }
+ else
+ if (!strncmp ("std-scan-document-image-compression", pName, nameSize))
+ {
+ memcpy (&pState->m_compression, pValue,
+ sizeof (pState->m_compression));
+ DBG (5, "Compression: %x\n", ntohl (pState->m_compression));
+
+ }
+ else if (!strncmp ("std-scan-document-xresolution", pName, nameSize))
+ {
+ memcpy (&pState->m_xres, pValue, sizeof (pState->m_xres));
+ DBG (5, "X resolution: %d\n", ntohs (pState->m_xres));
+ }
+ else if (!strncmp ("std-scan-document-yresolution", pName, nameSize))
+ {
+ memcpy (&pState->m_yres, pValue, sizeof (pState->m_yres));
+ DBG (5, "Y resolution: %d\n", ntohs (pState->m_yres));
+ }
+ else if (!strncmp ("std-scan-page-widthpixel", pName, nameSize))
+ {
+ if (1 || !pState->m_pixelWidth)
+ {
+ memcpy (&pState->m_pixelWidth, pValue,
+ sizeof (pState->m_pixelWidth));
+ DBG (5, "Width: %d\n", ntohl (pState->m_pixelWidth));
+ }
+ else
+ {
+ DBG (5, "Ignoring width (already have a value)\n");
+ }
+ }
+ else if (!strncmp ("std-scan-page-heightpixel", pName, nameSize))
+ {
+ if (1 || !pState->m_pixelHeight)
+ {
+ memcpy (&pState->m_pixelHeight, pValue,
+ sizeof (pState->m_pixelHeight));
+ DBG (5, "Height: %d\n", ntohl (pState->m_pixelHeight));
+ }
+ else
+ {
+ DBG (5, "Ignoring height (already have a value)\n");
+ }
+ }
+ else if (!strncmp ("std-scan-page-start", pName, nameSize))
+ {
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-page-start-response",
+ 0x05, &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+
+ /* reset the data buffer ready to store a new page */
+ pState->m_buf.m_used = 0;
+
+ /* init current page size */
+ pState->m_currentPageBytes = 0;
+
+ pState->m_pixelWidth = 0;
+ pState->m_pixelHeight = 0;
+ }
+ else if (!strncmp ("std-scan-page-end", pName, nameSize))
+ {
+ bProcessImage = 1;
+
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22, "std-scan-page-end-response",
+ 0x05, &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+ }
+ else if (!strncmp ("std-scan-document-end", pName, nameSize))
+ {
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-document-end-response", 0x05,
+ &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+
+ /* reset the data buffer ready to store a new page */
+ pState->m_buf.m_used = 0;
+ }
+ else if (!strncmp ("std-scan-session-end", pName, nameSize))
+ {
+ errorCheck |= InitPacket (&buf, 0x02);
+ uiVal = 0;
+ errorCheck |=
+ AppendMessageToPacket (&buf, 0x22,
+ "std-scan-session-end-response", 0x05,
+ &uiVal, sizeof (uiVal));
+ FinalisePacket (&buf);
+ send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
+
+ /* initialise a shutodwn of the socket */
+ shutdown (pState->m_tcpFd, SHUT_RDWR);
+ }
+ else if (!strncmp ("std-scan-scandata-error", pName, nameSize))
+ {
+ /* determine the size of data in this chunk */
+ dataChunkSize = (pItem[6] << 8) + pItem[7];
+
+ pItem += 8;
+
+ DBG (10, "Reading %d bytes of scan data\n", dataChunkSize);
+
+ /* append message to buffer */
+ errorCheck |= AppendToComBuf (&pState->m_buf, pItem, dataChunkSize);
+
+ pItem += dataChunkSize;
+
+ DBG (10, "Accumulated %lu bytes of scan data so far\n",
+ (unsigned long)pState->m_buf.m_used);
+ } /* if */
+ } /* while */
+
+ /* process page data if required */
+ if ( bProcessImage ) errorCheck |= ProcessPageData (pState);
+
+cleanup:
+
+ /* remove processed data (including 8 byte header) from start of tcp buffer */
+ PopFromComBuf (pTcpBuf, messageSize + 8);
+
+ /* free com buf */
+ FreeComBuf (&buf);
+
+ return errorCheck;
+
+} /* ProcessTcpResponse */
+
+/***********************************************************/
+
+/* remove data from the front of a ComBuf struct
+ \return 0 if sucessful, >0 otherwise
+*/
+int
+PopFromComBuf (struct ComBuf *pBuf, size_t datSize)
+{
+
+ /* check if we're trying to remove more data than is present */
+ if (datSize > pBuf->m_used)
+ {
+ pBuf->m_used = 0;
+ return 1;
+ }
+
+ /* check easy cases */
+ if ((!datSize) || (datSize == pBuf->m_used))
+ {
+ pBuf->m_used -= datSize;
+ return 0;
+ }
+
+ /* move remaining memory contents to start */
+ memmove (pBuf->m_pBuf, pBuf->m_pBuf + datSize, pBuf->m_used - datSize);
+
+ pBuf->m_used -= datSize;
+ return 0;
+
+} /* PopFromComBuf */
+
+/***********************************************************/
+
+/* Process the data from a single scanned page, \return 0 in success, >0 otherwise */
+int
+ProcessPageData (struct ScannerState *pState)
+{
+
+ FILE *fTmp;
+ int fdTmp;
+ struct jpeg_source_mgr jpegSrcMgr;
+ struct JpegDataDecompState jpegCinfo;
+ struct jpeg_error_mgr jpegErr;
+ int numPixels, iPixel, width, height, scanLineSize, imageBytes;
+ int ret = 0;
+ struct PageInfo pageInfo;
+
+ JSAMPLE *pJpegLine = NULL;
+ uint32 *pTiffRgba = NULL;
+ unsigned char *pOut;
+ char tiffErrBuf[1024];
+
+ TIFF *pTiff = NULL;
+
+ /* If there's no data then there's nothing to write */
+ if (!pState->m_buf.m_used)
+ return 0;
+
+ DBG (1, "ProcessPageData: Got compression %x\n",
+ ntohl (pState->m_compression));
+
+ switch (ntohl (pState->m_compression))
+ {
+
+ case 0x20:
+ /* decode as JPEG if appropriate */
+ {
+
+ jpegSrcMgr.resync_to_restart = jpeg_resync_to_restart;
+ jpegSrcMgr.init_source = JpegDecompInitSource;
+ jpegSrcMgr.fill_input_buffer = JpegDecompFillInputBuffer;
+ jpegSrcMgr.skip_input_data = JpegDecompSkipInputData;
+ jpegSrcMgr.term_source = JpegDecompTermSource;
+
+ jpegCinfo.m_cinfo.err = jpeg_std_error (&jpegErr);
+ jpeg_create_decompress (&jpegCinfo.m_cinfo);
+ jpegCinfo.m_cinfo.src = &jpegSrcMgr;
+ jpegCinfo.m_bytesRemaining = pState->m_buf.m_used;
+ jpegCinfo.m_pData = pState->m_buf.m_pBuf;
+
+ jpeg_read_header (&jpegCinfo.m_cinfo, TRUE);
+ jpeg_start_decompress (&jpegCinfo.m_cinfo);
+
+ /* allocate space for a single scanline */
+ scanLineSize = jpegCinfo.m_cinfo.output_width
+ * jpegCinfo.m_cinfo.output_components;
+ DBG (1, "ProcessPageData: image dimensions: %d x %d, line size: %d\n",
+ jpegCinfo.m_cinfo.output_width,
+ jpegCinfo.m_cinfo.output_height, scanLineSize);
+
+ pJpegLine = calloc (scanLineSize, sizeof (JSAMPLE));
+ if (!pJpegLine)
+ {
+ DBG (1, "ProcessPageData: memory allocation error\n");
+ ret = 1;
+ goto JPEG_CLEANUP;
+ } /* if */
+
+ /* note dimensions - may be different from those previously reported */
+ pState->m_pixelWidth = htonl (jpegCinfo.m_cinfo.output_width);
+ pState->m_pixelHeight = htonl (jpegCinfo.m_cinfo.output_height);
+
+ /* decode scanlines */
+ while (jpegCinfo.m_cinfo.output_scanline
+ < jpegCinfo.m_cinfo.output_height)
+ {
+ DBG (20, "Reading scanline %d of %d\n",
+ jpegCinfo.m_cinfo.output_scanline,
+ jpegCinfo.m_cinfo.output_height);
+
+ /* read scanline */
+ jpeg_read_scanlines (&jpegCinfo.m_cinfo, &pJpegLine, 1);
+
+ /* append to output buffer */
+ ret |= AppendToComBuf (&pState->m_imageData,
+ pJpegLine, scanLineSize);
+
+ } /* while */
+
+ /* update info for this page */
+ pageInfo.m_width = jpegCinfo.m_cinfo.output_width;
+ pageInfo.m_height = jpegCinfo.m_cinfo.output_height;
+ pageInfo.m_totalSize = pageInfo.m_width * pageInfo.m_height * 3;
+ pageInfo.m_bytesRemaining = pageInfo.m_totalSize;
+
+ DBG( 1, "Process page data: page %d: JPEG image: %d x %d, %d bytes\n",
+ pState->m_numPages, pageInfo.m_width, pageInfo.m_height, pageInfo.m_totalSize );
+
+ ret |= AppendToComBuf( & pState->m_pageInfo, (unsigned char*)& pageInfo, sizeof( pageInfo ) );
+ ++( pState->m_numPages );
+
+ JPEG_CLEANUP:
+ jpeg_finish_decompress (&jpegCinfo.m_cinfo);
+ jpeg_destroy_decompress (&jpegCinfo.m_cinfo);
+
+ if (pJpegLine)
+ free (pJpegLine);
+
+ return ret;
+ } /* case JPEG */
+
+ case 0x08:
+ /* CCITT Group 4 Fax data */
+ {
+ /* get a temp file
+ :TODO: 2006-04-18: Use TIFFClientOpen and do everything in RAM
+ */
+ fTmp = tmpfile ();
+ fdTmp = fileno (fTmp);
+
+ pTiff = TIFFFdOpen (fdTmp, "tempfile", "w");
+ if (!pTiff)
+ {
+ DBG (1, "ProcessPageData: Error opening temp TIFF file");
+ ret = SANE_STATUS_IO_ERROR;
+ goto TIFF_CLEANUP;
+ }
+
+ /* create a TIFF file */
+ width = ntohl (pState->m_pixelWidth);
+ height = ntohl (pState->m_pixelHeight);
+ TIFFSetField (pTiff, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField (pTiff, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField (pTiff, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField (pTiff, TIFFTAG_PHOTOMETRIC, 0); /* 0 is white */
+ TIFFSetField (pTiff, TIFFTAG_COMPRESSION, 4); /* CCITT Group 4 */
+ TIFFSetField (pTiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+
+ TIFFWriteRawStrip (pTiff, 0, pState->m_buf.m_pBuf,
+ pState->m_buf.m_used);
+
+ if (0 > TIFFRGBAImageOK (pTiff, tiffErrBuf))
+ {
+ DBG (1, "ProcessPageData: %s\n", tiffErrBuf);
+ ret = SANE_STATUS_IO_ERROR;
+ goto TIFF_CLEANUP;
+ }
+
+ /* allocate space for RGBA representation of image */
+ numPixels = height * width;
+ DBG (20, "ProcessPageData: num TIFF RGBA pixels: %d\n", numPixels);
+ if (!(pTiffRgba = calloc (numPixels, sizeof (u_long))))
+ {
+ ret = SANE_STATUS_NO_MEM;
+ goto TIFF_CLEANUP;
+ }
+
+ /* make space in image buffer to store the results */
+ imageBytes = width * height * 3;
+ ret |= AppendToComBuf (&pState->m_imageData, NULL, imageBytes);
+ if (ret)
+ goto TIFF_CLEANUP;
+
+ /* get a pointer to the start of the output data */
+ pOut = pState->m_imageData.m_pBuf
+ + pState->m_imageData.m_used - imageBytes;
+
+ /* read RGBA image */
+ DBG (20, "ProcessPageData: setting up read buffer\n");
+ TIFFReadBufferSetup (pTiff, NULL, width * height * sizeof (u_long));
+ DBG (20, "ProcessPageData: reading RGBA data\n");
+ TIFFReadRGBAImageOriented (pTiff, width, height, pTiffRgba,
+ ORIENTATION_TOPLEFT, 0);
+
+ /* loop over pixels */
+ for (iPixel = 0; iPixel < numPixels; ++iPixel)
+ {
+
+ *(pOut++) = TIFFGetR (pTiffRgba[iPixel]);
+ *(pOut++) = TIFFGetG (pTiffRgba[iPixel]);
+ *(pOut++) = TIFFGetB (pTiffRgba[iPixel]);
+
+ } /* for iRow */
+
+
+
+ /* update info for this page */
+ pageInfo.m_width = width;
+ pageInfo.m_height = height;
+ pageInfo.m_totalSize = pageInfo.m_width * pageInfo.m_height * 3;
+ pageInfo.m_bytesRemaining = pageInfo.m_totalSize;
+
+ DBG( 1, "Process page data: page %d: TIFF image: %d x %d, %d bytes\n",
+ pState->m_numPages, width, height, pageInfo.m_totalSize );
+
+ ret |= AppendToComBuf( & pState->m_pageInfo, (unsigned char*)& pageInfo, sizeof( pageInfo ) );
+ ++( pState->m_numPages );
+
+ TIFF_CLEANUP:
+ if (pTiff)
+ TIFFClose (pTiff);
+ if (fTmp)
+ fclose (fTmp);
+ if (pTiffRgba)
+ free (pTiffRgba);
+ return ret;
+
+ } /* case CCITT */
+ default:
+ /* this is not expected or very useful */
+ {
+ DBG (1, "ProcessPageData: Unexpected compression flag %d\n", ntohl (pState->m_compression));
+ ret = SANE_STATUS_IO_ERROR;
+ }
+ } /* switch */
+
+ return ret;
+} /* ProcessPageData */
+
+/***********************************************************/
+
+void
+JpegDecompInitSource (j_decompress_ptr cinfo)
+/* Libjpeg decompression interface */
+{
+ cinfo->src->bytes_in_buffer = 0;
+
+} /* JpegDecompInitSource */
+
+/***********************************************************/
+
+boolean
+JpegDecompFillInputBuffer (j_decompress_ptr cinfo)
+/* Libjpeg decompression interface */
+{
+ struct JpegDataDecompState *pState = (struct JpegDataDecompState *) cinfo;
+ static const unsigned char eoiByte[] = {
+ 0xFF, JPEG_EOI
+ };
+
+ DBG (10, "JpegDecompFillInputBuffer: bytes remaining: %d\n",
+ pState->m_bytesRemaining);
+
+ if (!pState->m_bytesRemaining)
+ {
+
+ /* no input data available so return dummy data */
+ cinfo->src->bytes_in_buffer = 2;
+ cinfo->src->next_input_byte = (const JOCTET *) eoiByte;
+
+ }
+ else
+ {
+
+ /* point to data */
+ cinfo->src->bytes_in_buffer = pState->m_bytesRemaining;
+ cinfo->src->next_input_byte = (const JOCTET *) pState->m_pData;
+
+ /* note that data is now gone */
+ pState->m_bytesRemaining = 0;
+
+ } /* if */
+
+ return TRUE;
+
+} /* JpegDecompFillInputBuffer */
+
+/***********************************************************/
+
+void
+JpegDecompSkipInputData (j_decompress_ptr cinfo, long numBytes)
+/* Libjpeg decompression interface */
+{
+ DBG (10, "JpegDecompSkipInputData: skipping %ld bytes\n", numBytes);
+
+ cinfo->src->bytes_in_buffer -= numBytes;
+ cinfo->src->next_input_byte += numBytes;
+
+} /* JpegDecompSkipInputData */
+
+/***********************************************************/
+
+void
+JpegDecompTermSource (j_decompress_ptr __sane_unused__ cinfo)
+/* Libjpeg decompression interface */
+{
+ /* nothing to do */
+
+} /* JpegDecompTermSource */
+
+/***********************************************************/
diff --git a/backend/dell1600n_net.conf.in b/backend/dell1600n_net.conf.in
new file mode 100644
index 0000000..35d4eae
--- /dev/null
+++ b/backend/dell1600n_net.conf.in
@@ -0,0 +1,14 @@
+# An example config file for dell1600n_net backend.
+#
+# Jon Chambers <jon@jon.demon.co.uk>, 2006-08-12
+#
+# For scanner(s) not detected by the default UDP broadcast method (eg: perhaps it
+# is not on your local network) then you can add one or more "named_scanner" entries
+# explicitly giving the hostname/IP address. Uncomment one of the following and
+# modify the hostname appropriately:
+#
+#named_scanner: printer
+#named_scanner: 192.168.0.20
+#named_scanner: myscanner.somewhere.else.org
+
+
diff --git a/backend/dll.aliases b/backend/dll.aliases
new file mode 100644
index 0000000..922954d
--- /dev/null
+++ b/backend/dll.aliases
@@ -0,0 +1,6 @@
+# List of aliased or hiddend backends. See sane-dll(5) for details.
+#
+# Format:
+# alias SomeName SaneDeviceName
+# alias "Some Name" SaneDeviceName
+# hide SaneDeviceName
diff --git a/backend/dll.c b/backend/dll.c
new file mode 100644
index 0000000..619ee55
--- /dev/null
+++ b/backend/dll.c
@@ -0,0 +1,1305 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a dynamic linking based SANE meta backend. It
+ allows managing an arbitrary number of SANE backends by using
+ dynamic linking to load backends on demand. */
+
+/* Please increase version number with every change
+ (don't forget to update dll.desc) */
+#define DLL_VERSION "1.0.13"
+
+#ifdef _AIX
+# include "lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#ifdef __BEOS__
+#include <kernel/OS.h>
+#include <storage/FindDirectory.h>
+#include <kernel/image.h>
+#include <posix/dirent.h>
+#endif
+
+#include "../include/sane/config.h"
+#include "lalloca.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(HAVE_DLOPEN) && defined(HAVE_DLFCN_H)
+# include <dlfcn.h>
+
+ /* Older versions of dlopen() don't define RTLD_NOW and RTLD_LAZY.
+ They all seem to use a mode of 1 to indicate RTLD_NOW and some do
+ not support RTLD_LAZY at all. Hence, unless defined, we define
+ both macros as 1 to play it safe. */
+# ifndef RTLD_NOW
+# define RTLD_NOW 1
+# endif
+# ifndef RTLD_LAZY
+# define RTLD_LAZY 1
+# endif
+# define HAVE_DLL
+#endif
+
+/* HP/UX DLL support */
+#if defined (HAVE_SHL_LOAD) && defined(HAVE_DL_H)
+# include <dl.h>
+# define HAVE_DLL
+#endif
+
+/* Mac OS X/Darwin support */
+#if defined (HAVE_NSLINKMODULE) && defined(HAVE_MACH_O_DYLD_H)
+# include <mach-o/dyld.h>
+# define HAVE_DLL
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+
+#define BACKEND_NAME dll
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#if defined(_WIN32) || defined(HAVE_OS2_H)
+# define DIR_SEP ";"
+#else
+# define DIR_SEP ":"
+#endif
+
+
+#include "../include/sane/sanei_config.h"
+#define DLL_CONFIG_FILE "dll.conf"
+#define DLL_ALIASES_FILE "dll.aliases"
+
+enum SANE_Ops
+{
+ OP_INIT = 0,
+ OP_EXIT,
+ OP_GET_DEVS,
+ OP_OPEN,
+ OP_CLOSE,
+ OP_GET_OPTION_DESC,
+ OP_CTL_OPTION,
+ OP_GET_PARAMS,
+ OP_START,
+ OP_READ,
+ OP_CANCEL,
+ OP_SET_IO_MODE,
+ OP_GET_SELECT_FD,
+ NUM_OPS
+};
+
+typedef SANE_Status (*op_init_t) (SANE_Int *, SANE_Auth_Callback);
+typedef void (*op_exit_t) (void);
+typedef SANE_Status (*op_get_devs_t) (const SANE_Device ***, SANE_Bool);
+typedef SANE_Status (*op_open_t) (SANE_String_Const, SANE_Handle *);
+typedef void (*op_close_t) (SANE_Handle);
+typedef const SANE_Option_Descriptor * (*op_get_option_desc_t) (SANE_Handle,
+ SANE_Int);
+typedef SANE_Status (*op_ctl_option_t) (SANE_Handle, SANE_Int, SANE_Action,
+ void *, SANE_Int *);
+typedef SANE_Status (*op_get_params_t) (SANE_Handle, SANE_Parameters *);
+typedef SANE_Status (*op_start_t) (SANE_Handle);
+typedef SANE_Status (*op_read_t) (SANE_Handle, SANE_Byte *, SANE_Int,
+ SANE_Int *);
+typedef void (*op_cancel_t) (SANE_Handle);
+typedef SANE_Status (*op_set_io_mode_t) (SANE_Handle, SANE_Bool);
+typedef SANE_Status (*op_get_select_fd_t) (SANE_Handle, SANE_Int *);
+
+struct backend
+{
+ struct backend *next;
+ char *name;
+ u_int permanent:1; /* is the backend preloaded? */
+ u_int loaded:1; /* are the functions available? */
+ u_int inited:1; /* has the backend been initialized? */
+ void *handle; /* handle returned by dlopen() */
+ void *(*op[NUM_OPS]) (void);
+};
+
+#define BE_ENTRY(be,func) sane_##be##_##func
+
+#define PRELOAD_DECL(name) \
+ extern SANE_Status BE_ENTRY(name,init) (SANE_Int *, SANE_Auth_Callback); \
+ extern void BE_ENTRY(name,exit) (void); \
+ extern SANE_Status BE_ENTRY(name,get_devices) (const SANE_Device ***, SANE_Bool); \
+ extern SANE_Status BE_ENTRY(name,open) (SANE_String_Const, SANE_Handle *); \
+ extern void BE_ENTRY(name,close) (SANE_Handle); \
+ extern const SANE_Option_Descriptor *BE_ENTRY(name,get_option_descriptor) (SANE_Handle, SANE_Int); \
+ extern SANE_Status BE_ENTRY(name,control_option) (SANE_Handle, SANE_Int, SANE_Action, void *, SANE_Int *); \
+ extern SANE_Status BE_ENTRY(name,get_parameters) (SANE_Handle, SANE_Parameters *); \
+ extern SANE_Status BE_ENTRY(name,start) (SANE_Handle); \
+ extern SANE_Status BE_ENTRY(name,read) (SANE_Handle, SANE_Byte *, SANE_Int, SANE_Int *); \
+ extern void BE_ENTRY(name,cancel) (SANE_Handle); \
+ extern SANE_Status BE_ENTRY(name,set_io_mode) (SANE_Handle, SANE_Bool); \
+ extern SANE_Status BE_ENTRY(name,get_select_fd) (SANE_Handle, SANE_Int *);
+
+#define PRELOAD_DEFN(name) \
+{ \
+ 0 /* next */, #name, \
+ 1 /* permanent */, \
+ 1 /* loaded */, \
+ 0 /* inited */, \
+ 0 /* handle */, \
+ { \
+ BE_ENTRY(name,init), \
+ BE_ENTRY(name,exit), \
+ BE_ENTRY(name,get_devices), \
+ BE_ENTRY(name,open), \
+ BE_ENTRY(name,close), \
+ BE_ENTRY(name,get_option_descriptor), \
+ BE_ENTRY(name,control_option), \
+ BE_ENTRY(name,get_parameters), \
+ BE_ENTRY(name,start), \
+ BE_ENTRY(name,read), \
+ BE_ENTRY(name,cancel), \
+ BE_ENTRY(name,set_io_mode), \
+ BE_ENTRY(name,get_select_fd) \
+ } \
+}
+
+#ifndef __BEOS__
+#ifdef ENABLE_PRELOAD
+#include "dll-preload.h"
+#else
+static struct backend preloaded_backends[] = {
+ { 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}
+};
+#endif
+#endif
+
+struct meta_scanner
+{
+ struct backend *be;
+ SANE_Handle handle;
+};
+
+struct alias
+{
+ struct alias *next;
+ char *oldname;
+ char *newname;
+};
+
+/*
+ * List of available devices, allocated by sane_get_devices, released
+ * by sane_exit()
+ */
+static SANE_Device **devlist = NULL;
+static int devlist_size = 0, devlist_len = 0;
+
+static struct alias *first_alias;
+static SANE_Auth_Callback auth_callback;
+static struct backend *first_backend;
+
+#ifndef __BEOS__
+static const char *op_name[] = {
+ "init", "exit", "get_devices", "open", "close", "get_option_descriptor",
+ "control_option", "get_parameters", "start", "read", "cancel",
+ "set_io_mode", "get_select_fd"
+};
+#else
+static const char *op_name[] = {
+ "sane_init", "sane_exit", "sane_get_devices", "sane_open", "sane_close", "sane_get_option_descriptor",
+ "sane_control_option", "sane_get_parameters", "sane_start", "sane_read", "sane_cancel",
+ "sane_set_io_mode", "sane_get_select_fd"
+};
+#endif /* __BEOS__ */
+
+static void *
+op_unsupported (void)
+{
+ DBG (1, "op_unsupported: call to unsupported backend operation\n");
+ return (void *) (long) SANE_STATUS_UNSUPPORTED;
+}
+
+
+static SANE_Status
+add_backend (const char *name, struct backend **bep)
+{
+ struct backend *be, *prev;
+
+ DBG (3, "add_backend: adding backend `%s'\n", name);
+
+ if (strcmp (name, "dll") == 0)
+ {
+ DBG (0, "add_backend: remove the dll-backend from your dll.conf!\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ for (prev = 0, be = first_backend; be; prev = be, be = be->next)
+ if (strcmp (be->name, name) == 0)
+ {
+ DBG (1, "add_backend: `%s' is already there\n", name);
+ /* move to front so we preserve order that we'd get with
+ dynamic loading: */
+ if (prev)
+ {
+ prev->next = be->next;
+ be->next = first_backend;
+ first_backend = be;
+ }
+ if (bep)
+ *bep = be;
+ return SANE_STATUS_GOOD;
+ }
+
+ be = calloc (1, sizeof (*be));
+ if (!be)
+ return SANE_STATUS_NO_MEM;
+
+ be->name = strdup (name);
+ if (!be->name)
+ return SANE_STATUS_NO_MEM;
+ be->next = first_backend;
+ first_backend = be;
+ if (bep)
+ *bep = be;
+ return SANE_STATUS_GOOD;
+}
+
+#if defined(HAVE_NSLINKMODULE)
+static const char *dyld_get_error_str ();
+
+static const char *
+dyld_get_error_str ()
+{
+ NSLinkEditErrors c;
+ int errorNumber;
+ const char *fileName;
+ const char *errorString;
+
+ NSLinkEditError (&c, &errorNumber, &fileName, &errorString);
+ return errorString;
+}
+#endif
+
+#ifdef __BEOS__
+#include <FindDirectory.h>
+
+static SANE_Status
+load (struct backend *be)
+{
+ /* use BeOS kernel function to load scanner addons from ~/config/add-ons/SANE */
+ char path[PATH_MAX];
+ image_id id = -1;
+ int i, w;
+ directory_which which[3] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_BEOS_ADDONS_DIRECTORY };
+
+ /* look for config files in SANE/conf */
+ for (w = 0; (w < 3) && (id < 0) && (find_directory(which[w],0,true,path,PATH_MAX) == 0); w++)
+ {
+ strcat(path,"/SANE/");
+ strcat(path,be->name);
+ DBG(1, "loading backend %s\n", be->name);
+
+ /* initialize all ops to "unsupported" so we can "use" the backend
+ even if the stuff later in this function fails */
+ be->loaded = 1;
+ be->handle = 0;
+ for (i = 0; i < NUM_OPS; ++i) be->op[i] = op_unsupported;
+ DBG(2, "dlopen()ing `%s'\n", path);
+ id=load_add_on(path);
+ if (id < 0)
+ {
+ continue; /* try next path */
+ }
+ be->handle=(void *)id;
+
+ for (i = 0; i < NUM_OPS; ++i)
+ {
+ void *(*op) ();
+ op = NULL;
+ /* Look for the symbol */
+ if ((get_image_symbol(id, op_name[i],B_SYMBOL_TYPE_TEXT,(void **)&op) < 0) || !op)
+ DBG(2, "unable to find %s\n", op_name[i]);
+ else be->op[i]=op;
+ }
+ }
+ if (id < 0)
+ {
+ DBG(2, "load: couldn't find %s\n",path);
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+#else
+static SANE_Status
+load (struct backend *be)
+{
+#ifdef HAVE_DLL
+ int mode = 0;
+ char *funcname, *src, *orig_src = 0, *dir, *path = 0;
+ char libname[PATH_MAX];
+ int i;
+ int src_len;
+ FILE *fp = 0;
+
+#if defined(HAVE_DLOPEN)
+# define PREFIX "libsane-"
+# ifdef __hpux
+# define POSTFIX ".sl.%u"
+# define ALT_POSTFIX ".so.%u"
+# elif defined (HAVE_WINDOWS_H)
+# undef PREFIX
+# define PREFIX "cygsane-"
+# define POSTFIX "-%u.dll"
+# elif defined (HAVE_OS2_H)
+# undef PREFIX
+# define PREFIX ""
+# define POSTFIX ".dll"
+# elif defined (__APPLE__) && defined (__MACH__)
+# define POSTFIX ".%u.so"
+# else
+# define POSTFIX ".so.%u"
+# endif
+ mode = getenv ("LD_BIND_NOW") ? RTLD_NOW : RTLD_LAZY;
+#elif defined(HAVE_SHL_LOAD)
+# define PREFIX "libsane-"
+# define POSTFIX ".sl.%u"
+ mode = BIND_DEFERRED;
+#elif defined(HAVE_NSLINKMODULE)
+# define PREFIX "libsane-"
+# define POSTFIX ".%u.so"
+ mode = NSLINKMODULE_OPTION_RETURN_ON_ERROR + NSLINKMODULE_OPTION_PRIVATE;
+#else
+# error "Tried to compile unsupported DLL."
+#endif /* HAVE_DLOPEN */
+
+ /* initialize all ops to "unsupported" so we can "use" the backend
+ even if the stuff later in this function fails */
+ be->loaded = 1;
+ be->handle = 0;
+ for (i = 0; i < NUM_OPS; ++i)
+ be->op[i] = op_unsupported;
+
+ path = getenv ("LD_LIBRARY_PATH");
+ if (!path)
+ path = getenv ("SHLIB_PATH"); /* for HP-UX */
+ if (!path)
+ path = getenv ("LIBPATH"); /* for AIX */
+
+ if (path)
+ {
+ src_len = strlen (path) + strlen (STRINGIFY (LIBDIR)) + 1 + 1;
+ src = malloc (src_len);
+ if (!src)
+ {
+ DBG (1, "load: malloc failed: %s\n", strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+ orig_src = src;
+ snprintf (src, src_len, "%s:%s", path, STRINGIFY (LIBDIR));
+ }
+ else
+ {
+ src = STRINGIFY (LIBDIR);
+ src = strdup (src);
+ if (!src)
+ {
+ DBG (1, "load: strdup failed: %s\n", strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ DBG (3, "load: searching backend `%s' in `%s'\n", be->name, src);
+
+ dir = strsep (&src, DIR_SEP);
+
+ while (dir)
+ {
+#ifdef HAVE_OS2_H /* only max 7.3 names work with dlopen() for DLLs on OS/2 */
+ snprintf (libname, sizeof (libname), "%s/" PREFIX "%.2s%.5s" POSTFIX,
+ dir, be->name, strlen(be->name)>7 ? (be->name)+strlen(be->name)-5 :
+ (be->name)+2, V_MAJOR);
+#else
+ snprintf (libname, sizeof (libname), "%s/" PREFIX "%s" POSTFIX,
+ dir, be->name, V_MAJOR);
+#endif
+ DBG (4, "load: trying to load `%s'\n", libname);
+ fp = fopen (libname, "r");
+ if (fp)
+ break;
+ DBG (4, "load: couldn't open `%s' (%s)\n", libname, strerror (errno));
+
+#ifdef ALT_POSTFIX
+ /* Some platforms have two ways of storing their libraries, try both
+ postfixes */
+ snprintf (libname, sizeof (libname), "%s/" PREFIX "%s" ALT_POSTFIX,
+ dir, be->name, V_MAJOR);
+ DBG (4, "load: trying to load `%s'\n", libname);
+ fp = fopen (libname, "r");
+ if (fp)
+ break;
+ DBG (4, "load: couldn't open `%s' (%s)\n", libname, strerror (errno));
+#endif
+
+ dir = strsep (&src, DIR_SEP);
+ }
+ if (orig_src)
+ free (orig_src);
+ if (!fp)
+ {
+ DBG (1, "load: couldn't find backend `%s' (%s)\n",
+ be->name, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (fp);
+ DBG (3, "load: dlopen()ing `%s'\n", libname);
+
+#ifdef HAVE_DLOPEN
+ be->handle = dlopen (libname, mode);
+#elif defined(HAVE_SHL_LOAD)
+ be->handle = (shl_t) shl_load (libname, mode, 0L);
+#elif defined(HAVE_NSLINKMODULE)
+ {
+ NSObjectFileImage objectfile_img = NULL;
+ if (NSCreateObjectFileImageFromFile (libname, &objectfile_img)
+ == NSObjectFileImageSuccess)
+ {
+ be->handle = NSLinkModule (objectfile_img, libname, mode);
+ NSDestroyObjectFileImage (objectfile_img);
+ }
+ }
+#else
+# error "Tried to compile unsupported DLL."
+#endif /* HAVE_DLOPEN */
+ if (!be->handle)
+ {
+#ifdef HAVE_DLOPEN
+ DBG (1, "load: dlopen() failed (%s)\n", dlerror ());
+#elif defined(HAVE_NSLINKMODULE)
+ DBG (1, "load: dyld error (%s)\n", dyld_get_error_str ());
+#else
+ DBG (1, "load: dlopen() failed (%s)\n", strerror (errno));
+#endif
+ return SANE_STATUS_INVAL;
+ }
+
+ /* all is dandy---lookup and fill in backend ops: */
+ funcname = alloca (strlen (be->name) + 64);
+ for (i = 0; i < NUM_OPS; ++i)
+ {
+ void *(*op) (void);
+
+ sprintf (funcname, "_sane_%s_%s", be->name, op_name[i]);
+
+ /* First try looking up the symbol without a leading underscore. */
+#ifdef HAVE_DLOPEN
+ op = (void *(*)(void)) dlsym (be->handle, funcname + 1);
+#elif defined(HAVE_SHL_LOAD)
+ shl_findsym ((shl_t *) & (be->handle), funcname + 1, TYPE_UNDEFINED,
+ &op);
+#elif defined(HAVE_NSLINKMODULE)
+ {
+ NSSymbol *nssym = NSLookupSymbolInModule (be->handle, funcname);
+ if (!nssym)
+ {
+ DBG (15, "dyld error: %s\n", dyld_get_error_str ());
+ }
+ else
+ {
+ op = (void *(*)(void)) NSAddressOfSymbol (nssym);
+ }
+ }
+#else
+# error "Tried to compile unsupported DLL."
+#endif /* HAVE_DLOPEN */
+ if (op)
+ be->op[i] = op;
+ else
+ {
+ /* Try again, with an underscore prepended. */
+#ifdef HAVE_DLOPEN
+ op = (void *(*)(void)) dlsym (be->handle, funcname);
+#elif defined(HAVE_SHL_LOAD)
+ shl_findsym (be->handle, funcname, TYPE_UNDEFINED, &op);
+#elif defined(HAVE_NSLINKMODULE)
+ {
+ NSSymbol *nssym = NSLookupSymbolInModule (be->handle, funcname);
+ if (!nssym)
+ {
+ DBG (15, "dyld error: %s\n", dyld_get_error_str ());
+ }
+ else
+ {
+ op = (void *(*)(void)) NSAddressOfSymbol (nssym);
+ }
+ }
+#else
+# error "Tried to compile unsupported DLL."
+#endif /* HAVE_DLOPEN */
+ if (op)
+ be->op[i] = op;
+ }
+ if (NULL == op)
+ DBG (1, "load: unable to find %s\n", funcname);
+ }
+
+ return SANE_STATUS_GOOD;
+
+# undef PREFIX
+# undef POSTFIX
+#else /* HAVE_DLL */
+ DBG (1,
+ "load: ignoring attempt to load `%s'; compiled without dl support\n",
+ be->name);
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_DLL */
+}
+#endif /* __BEOS__ */
+
+static SANE_Status
+init (struct backend *be)
+{
+ SANE_Status status;
+ SANE_Int version;
+
+ if (!be->loaded)
+ {
+ status = load (be);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ DBG (3, "init: initializing backend `%s'\n", be->name);
+
+ status = (*(op_init_t)be->op[OP_INIT]) (&version, auth_callback);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (SANE_VERSION_MAJOR (version) != SANE_CURRENT_MAJOR)
+ {
+ DBG (1,
+ "init: backend `%s' has a wrong major version (%d instead of %d)\n",
+ be->name, SANE_VERSION_MAJOR (version), SANE_CURRENT_MAJOR);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (4, "init: backend `%s' is version %d.%d.%d\n", be->name,
+ SANE_VERSION_MAJOR (version), SANE_VERSION_MINOR (version),
+ SANE_VERSION_BUILD (version));
+
+ be->inited = 1;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static void
+add_alias (const char *line_param)
+{
+#ifndef __BEOS__
+ const char *command;
+ enum
+ { CMD_ALIAS, CMD_HIDE }
+ cmd;
+ const char *oldname, *oldend, *newname;
+ size_t oldlen, newlen;
+ struct alias *alias;
+ char *line;
+
+ command = sanei_config_skip_whitespace (line_param);
+ if (!*command)
+ return;
+
+ line = strchr (command, '#');
+ if (line)
+ *line = '\0';
+
+ line = strpbrk (command, " \t");
+ if (!line)
+ return;
+ *line++ = '\0';
+
+ if (strcmp (command, "alias") == 0)
+ cmd = CMD_ALIAS;
+ else if (strcmp (command, "hide") == 0)
+ cmd = CMD_HIDE;
+ else
+ return;
+
+ newlen = 0;
+ newname = NULL;
+ if (cmd == CMD_ALIAS)
+ {
+ char *newend;
+
+ newname = sanei_config_skip_whitespace (line);
+ if (!*newname)
+ return;
+ if (*newname == '\"')
+ {
+ ++newname;
+ newend = strchr (newname, '\"');
+ }
+ else
+ newend = strpbrk (newname, " \t");
+ if (!newend)
+ return;
+
+ newlen = newend - newname;
+ line = (char *) (newend + 1);
+ }
+
+ oldname = sanei_config_skip_whitespace (line);
+ if (!*oldname)
+ return;
+ oldend = oldname + strcspn (oldname, " \t");
+
+ oldlen = oldend - oldname;
+
+ alias = malloc (sizeof (struct alias));
+ if (alias)
+ {
+ alias->oldname = malloc (oldlen + newlen + 2);
+ if (alias->oldname)
+ {
+ strncpy (alias->oldname, oldname, oldlen);
+ alias->oldname[oldlen] = '\0';
+ if (cmd == CMD_ALIAS)
+ {
+ alias->newname = alias->oldname + oldlen + 1;
+ strncpy (alias->newname, newname, newlen);
+ alias->newname[newlen] = '\0';
+ }
+ else
+ alias->newname = NULL;
+
+ alias->next = first_alias;
+ first_alias = alias;
+ return;
+ }
+ free (alias);
+ }
+ return;
+#endif
+}
+
+
+static void
+read_config (const char *conffile)
+{
+ FILE *fp;
+ char config_line[PATH_MAX];
+ char *backend_name;
+
+ fp = sanei_config_open (conffile);
+ if (!fp)
+ {
+ DBG (1, "sane_init/read_config: Couldn't open config file (%s): %s\n",
+ conffile, strerror (errno));
+ return; /* don't insist on config file */
+ }
+
+ DBG (5, "sane_init/read_config: reading %s\n", conffile);
+ while (sanei_config_read (config_line, sizeof (config_line), fp))
+ {
+ char *comment;
+ SANE_String_Const cp;
+
+ cp = sanei_config_get_string (config_line, &backend_name);
+ /* ignore empty lines */
+ if (!backend_name || cp == config_line)
+ {
+ if (backend_name)
+ free (backend_name);
+ continue;
+ }
+ /* ignore line comments */
+ if (backend_name[0] == '#')
+ {
+ free (backend_name);
+ continue;
+ }
+ /* ignore comments after backend names */
+ comment = strchr (backend_name, '#');
+ if (comment)
+ *comment = '\0';
+ add_backend (backend_name, 0);
+ free (backend_name);
+ }
+ fclose (fp);
+}
+
+static void
+read_dlld (void)
+{
+ DIR *dlld;
+ struct dirent *dllconf;
+ struct stat st;
+ char conffile[PATH_MAX], dlldir[PATH_MAX];
+ size_t len, plen;
+ const char *dir_list;
+ char *copy, *next, *dir;
+
+ dir_list = sanei_config_get_paths ();
+ if (!dir_list)
+ {
+ DBG(2, "sane_init/read_dlld: Unable to detect configuration directories\n");
+ return;
+ }
+
+ copy = strdup (dir_list);
+
+ for (next = copy; (dir = strsep (&next, DIR_SEP)) != NULL;)
+ {
+ snprintf (dlldir, sizeof (dlldir), "%s%s", dir, "/dll.d");
+
+ DBG(4, "sane_init/read_dlld: attempting to open directory `%s'\n", dlldir);
+
+ dlld = opendir (dlldir);
+ if (dlld)
+ {
+ /* length of path to parent dir of dll.d/ */
+ plen = strlen (dir) + 1;
+
+ DBG(3, "sane_init/read_dlld: using config directory `%s'\n", dlldir);
+ break;
+ }
+ }
+ free (copy);
+
+ if (dlld == NULL)
+ {
+ DBG (1, "sane_init/read_dlld: opendir failed: %s\n",
+ strerror (errno));
+ return;
+ }
+
+ while ((dllconf = readdir (dlld)) != NULL)
+ {
+ /* dotfile (or directory) */
+ if (dllconf->d_name[0] == '.')
+ continue;
+
+ len = strlen (dllconf->d_name);
+
+ /* backup files */
+ if ((dllconf->d_name[len-1] == '~')
+ || (dllconf->d_name[len-1] == '#'))
+ continue;
+
+ snprintf (conffile, PATH_MAX, "%s/%s", dlldir, dllconf->d_name);
+
+ DBG (5, "sane_init/read_dlld: considering %s\n", conffile);
+
+ if (stat (conffile, &st) != 0)
+ continue;
+
+ if (!S_ISREG (st.st_mode))
+ continue;
+
+ /* expects a path relative to PATH_SANE_CONFIG_DIR */
+ read_config (conffile+plen);
+ }
+
+ closedir (dlld);
+
+ DBG (5, "sane_init/read_dlld: done.\n");
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+#ifndef __BEOS__
+ char config_line[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ int i;
+#else
+ DIR *dir;
+ struct dirent *dirent;
+ char path[1024];
+ directory_which which[3] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_BEOS_ADDONS_DIRECTORY };
+ int i;
+#endif
+
+ DBG_INIT ();
+
+ auth_callback = authorize;
+
+ DBG (1, "sane_init: SANE dll backend version %s from %s\n", DLL_VERSION,
+ PACKAGE_STRING);
+
+#ifndef __BEOS__
+ /* chain preloaded backends together: */
+ for (i = 0; i < NELEMS (preloaded_backends); ++i)
+ {
+ if (!preloaded_backends[i].name)
+ continue;
+ DBG (3, "sane_init: adding backend `%s' (preloaded)\n", preloaded_backends[i].name);
+ preloaded_backends[i].next = first_backend;
+ first_backend = &preloaded_backends[i];
+ }
+
+ /* Return the version number of the sane-backends package to allow
+ the frontend to print them. This is done only for net and dll,
+ because these backends are usually called by the frontend. */
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_DLL_V_MAJOR, SANE_DLL_V_MINOR,
+ SANE_DLL_V_BUILD);
+
+ /*
+ * Read dll.conf & dll.d
+ * Read dll.d first, so that the extras backends will be tried last
+ */
+ read_dlld ();
+ read_config (DLL_CONFIG_FILE);
+
+ fp = sanei_config_open (DLL_ALIASES_FILE);
+ if (!fp)
+ return SANE_STATUS_GOOD; /* don't insist on aliases file */
+
+ DBG (5, "sane_init: reading %s\n", DLL_ALIASES_FILE);
+ while (sanei_config_read (config_line, sizeof (config_line), fp))
+ {
+ if (config_line[0] == '#') /* ignore line comments */
+ continue;
+
+ len = strlen (config_line);
+ if (!len)
+ continue; /* ignore empty lines */
+
+ add_alias (config_line);
+ }
+ fclose (fp);
+
+#else
+ /* no ugly config files, just get scanners from their ~/config/add-ons/SANE */
+ /* look for drivers */
+ for (i = 0; i < 3; i++)
+ {
+ if (find_directory(which[i],0,true,path,1024) < B_OK)
+ continue;
+ strcat(path,"/SANE/");
+ dir=opendir(path);
+ if(!dir) continue;
+
+ while((dirent=readdir(dir)))
+ {
+ if((strcmp(dirent->d_name,".")==0) || (strcmp(dirent->d_name,"..")==0)) continue;
+ if((strcmp(dirent->d_name,"dll")==0)) continue;
+ add_backend(dirent->d_name,0);
+ }
+ closedir(dir);
+ }
+#endif /* __BEOS__ */
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ struct backend *be, *next;
+ struct alias *alias;
+
+ DBG (2, "sane_exit: exiting\n");
+
+ for (be = first_backend; be; be = next)
+ {
+ next = be->next;
+ if (be->loaded)
+ {
+ if (be->inited)
+ {
+ DBG (3, "sane_exit: calling backend `%s's exit function\n",
+ be->name);
+ (*(op_exit_t)be->op[OP_EXIT]) ();
+ }
+#ifdef __BEOS__
+ /* use BeOS kernel functions to unload add-ons */
+ if(be->handle) unload_add_on((image_id)be->handle);
+#else
+#ifdef HAVE_DLL
+
+#ifdef HAVE_DLOPEN
+ if (be->handle)
+ dlclose (be->handle);
+#elif defined(HAVE_SHL_LOAD)
+ if (be->handle)
+ shl_unload (be->handle);
+#elif defined(HAVE_NSLINKMODULE)
+ if (be->handle)
+ NSUnLinkModule (be->handle, NSUNLINKMODULE_OPTION_NONE
+# ifdef __ppc__
+ | NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES
+# endif
+ );
+#else
+# error "Tried to compile unsupported DLL."
+#endif /* HAVE_DLOPEN */
+
+#endif /* HAVE_DLL */
+#endif /* __BEOS__ */
+ }
+ if (!be->permanent)
+ {
+ if (be->name)
+ free ((void *) be->name);
+ free (be);
+ }
+ else
+ {
+ be->inited = 0;
+ }
+ }
+ first_backend = 0;
+
+ while ((alias = first_alias) != NULL)
+ {
+ first_alias = first_alias->next;
+ free (alias->oldname);
+ free (alias);
+ }
+
+ if (NULL != devlist)
+ { /* Release memory allocated by sane_get_devices(). */
+ int i = 0;
+ while (devlist[i])
+ free (devlist[i++]);
+ free (devlist);
+
+ devlist = NULL;
+ devlist_size = 0;
+ devlist_len = 0;
+ }
+ DBG (3, "sane_exit: finished\n");
+}
+
+/* Note that a call to get_devices() implies that we'll have to load
+ all backends. To avoid this, you can call sane_open() directly
+ (assuming you know the name of the backend/device). This is
+ appropriate for the command-line interface of SANE, for example.
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ const SANE_Device **be_list;
+ struct backend *be;
+ SANE_Status status;
+ char *full_name;
+ int i, num_devs;
+ size_t len;
+#define ASSERT_SPACE(n) \
+ { \
+ if (devlist_len + (n) > devlist_size) \
+ { \
+ devlist_size += (n) + 15; \
+ if (devlist) \
+ devlist = realloc (devlist, devlist_size * sizeof (devlist[0])); \
+ else \
+ devlist = malloc (devlist_size * sizeof (devlist[0])); \
+ if (!devlist) \
+ return SANE_STATUS_NO_MEM; \
+ } \
+ }
+
+ DBG (3, "sane_get_devices\n");
+
+ if (devlist)
+ for (i = 0; i < devlist_len; ++i)
+ free ((void *) devlist[i]);
+ devlist_len = 0;
+
+ for (be = first_backend; be; be = be->next)
+ {
+ if (!be->inited)
+ if (init (be) != SANE_STATUS_GOOD)
+ continue;
+
+ status = (*(op_get_devs_t)be->op[OP_GET_DEVS]) (&be_list, local_only);
+ if (status != SANE_STATUS_GOOD || !be_list)
+ continue;
+
+ /* count the number of devices for this backend: */
+ for (num_devs = 0; be_list[num_devs]; ++num_devs);
+
+ ASSERT_SPACE (num_devs);
+
+ for (i = 0; i < num_devs; ++i)
+ {
+ SANE_Device *dev;
+ char *mem;
+ struct alias *alias;
+
+ for (alias = first_alias; alias != NULL; alias = alias->next)
+ {
+ len = strlen (be->name);
+ if (strlen (alias->oldname) <= len)
+ continue;
+ if (strncmp (alias->oldname, be->name, len) == 0
+ && alias->oldname[len] == ':'
+ && strcmp (&alias->oldname[len + 1], be_list[i]->name) == 0)
+ break;
+ }
+
+ if (alias)
+ {
+ if (!alias->newname) /* hidden device */
+ continue;
+
+ len = strlen (alias->newname);
+ mem = malloc (sizeof (*dev) + len + 1);
+ if (!mem)
+ return SANE_STATUS_NO_MEM;
+
+ full_name = mem + sizeof (*dev);
+ strcpy (full_name, alias->newname);
+ }
+ else
+ {
+ /* create a new device entry with a device name that is the
+ sum of the backend name a colon and the backend's device
+ name: */
+ len = strlen (be->name) + 1 + strlen (be_list[i]->name);
+ mem = malloc (sizeof (*dev) + len + 1);
+ if (!mem)
+ return SANE_STATUS_NO_MEM;
+
+ full_name = mem + sizeof (*dev);
+ strcpy (full_name, be->name);
+ strcat (full_name, ":");
+ strcat (full_name, be_list[i]->name);
+ }
+
+ dev = (SANE_Device *) mem;
+ dev->name = full_name;
+ dev->vendor = be_list[i]->vendor;
+ dev->model = be_list[i]->model;
+ dev->type = be_list[i]->type;
+
+ devlist[devlist_len++] = dev;
+ }
+ }
+
+ /* terminate device list with NULL entry: */
+ ASSERT_SPACE (1);
+ devlist[devlist_len++] = 0;
+
+ *device_list = (const SANE_Device **) devlist;
+ DBG (3, "sane_get_devices: found %d devices\n", devlist_len - 1);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
+{
+ const char *be_name, *dev_name;
+ struct meta_scanner *s;
+ SANE_Handle handle;
+ struct backend *be;
+ SANE_Status status;
+ struct alias *alias;
+
+ DBG (3, "sane_open: trying to open `%s'\n", full_name);
+
+ for (alias = first_alias; alias != NULL; alias = alias->next)
+ {
+ if (!alias->newname)
+ continue;
+ if (strcmp (alias->newname, full_name) == 0)
+ {
+ full_name = alias->oldname;
+ break;
+ }
+ }
+
+ dev_name = strchr (full_name, ':');
+ if (dev_name)
+ {
+#ifdef strndupa
+ be_name = strndupa (full_name, dev_name - full_name);
+#else
+ char *tmp;
+
+ tmp = alloca (dev_name - full_name + 1);
+ memcpy (tmp, full_name, dev_name - full_name);
+ tmp[dev_name - full_name] = '\0';
+ be_name = tmp;
+#endif
+ ++dev_name; /* skip colon */
+ }
+ else
+ {
+ /* if no colon interpret full_name as the backend name; an empty
+ backend device name will cause us to open the first device of
+ that backend. */
+ be_name = full_name;
+ dev_name = "";
+ }
+
+ if (!be_name[0])
+ be = first_backend;
+ else
+ for (be = first_backend; be; be = be->next)
+ if (strcmp (be->name, be_name) == 0)
+ break;
+
+ if (!be)
+ {
+ status = add_backend (be_name, &be);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (!be->inited)
+ {
+ status = init (be);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ status = (*(op_open_t)be->op[OP_OPEN]) (dev_name, &handle);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s = calloc (1, sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+
+ s->be = be;
+ s->handle = handle;
+ *meta_handle = s;
+
+ DBG (3, "sane_open: open successful\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_close(handle=%p)\n", handle);
+ (*(op_close_t)s->be->op[OP_CLOSE]) (s->handle);
+ free (s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_get_option_descriptor(handle=%p,option=%d)\n", handle,
+ option);
+ return (*(op_get_option_desc_t)s->be->op[OP_GET_OPTION_DESC]) (s->handle, option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Word * info)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3,
+ "sane_control_option(handle=%p,option=%d,action=%d,value=%p,info=%p)\n",
+ handle, option, action, value, (void *) info);
+ return (*(op_ctl_option_t)s->be->op[OP_CTL_OPTION]) (s->handle, option, action, value,
+ info);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_get_parameters(handle=%p,params=%p)\n", handle, (void *) params);
+ return (*(op_get_params_t)s->be->op[OP_GET_PARAMS]) (s->handle, params);
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_start(handle=%p)\n", handle);
+ return (*(op_start_t)s->be->op[OP_START]) (s->handle);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_read(handle=%p,data=%p,maxlen=%d,lenp=%p)\n",
+ handle, data, max_length, (void *) length);
+ return (*(op_read_t)s->be->op[OP_READ]) (s->handle, data, max_length, length);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_cancel(handle=%p)\n", handle);
+ (*(op_cancel_t)s->be->op[OP_CANCEL]) (s->handle);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_set_io_mode(handle=%p,nonblocking=%d)\n", handle,
+ non_blocking);
+ return (*(op_set_io_mode_t)s->be->op[OP_SET_IO_MODE]) (s->handle, non_blocking);
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ struct meta_scanner *s = handle;
+
+ DBG (3, "sane_get_select_fd(handle=%p,fdp=%p)\n", handle, (void *) fd);
+ return (*(op_get_select_fd_t)s->be->op[OP_GET_SELECT_FD]) (s->handle, fd);
+}
diff --git a/backend/dll.conf.in b/backend/dll.conf.in
new file mode 100644
index 0000000..a7e4b3e
--- /dev/null
+++ b/backend/dll.conf.in
@@ -0,0 +1,87 @@
+# enable the next line if you want to allow access through the network:
+net
+abaton
+agfafocus
+apple
+avision
+artec
+artec_eplus48u
+as6e
+bh
+canon
+canon630u
+canon_dr
+#canon_pp
+cardscan
+coolscan
+#coolscan2
+coolscan3
+#dc25
+#dc210
+#dc240
+dell1600n_net
+dmc
+epjitsu
+#epson
+epson2
+fujitsu
+#gphoto2
+genesys
+gt68xx
+hp
+hp3900
+hpsj5s
+hp3500
+hp4200
+hp5400
+hp5590
+hpljm1005
+hs2p
+ibm
+kodak
+kodakaio
+kvs1025
+kvs20xx
+leo
+lexmark
+ma1509
+magicolor
+matsushita
+microtek
+microtek2
+mustek
+#mustek_pp
+mustek_usb
+mustek_usb2
+nec
+niash
+#p5
+pie
+pint
+pixma
+plustek
+#plustek_pp
+#pnm
+qcam
+ricoh
+rts8891
+s9036
+sceptre
+sharp
+sm3600
+sm3840
+snapscan
+sp15c
+#st400
+#stv680
+tamarack
+teco1
+teco2
+teco3
+#test
+u12
+umax
+#umax_pp
+umax1220u
+v4l
+xerox_mfp
diff --git a/backend/dmc.c b/backend/dmc.c
new file mode 100644
index 0000000..96f9186
--- /dev/null
+++ b/backend/dmc.c
@@ -0,0 +1,1404 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998 David F. Skoll
+ Heavily based on "hp.c" driver for HP Scanners, by
+ David Mosberger-Tang.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Polaroid Digital
+ Microscope Camera. */
+
+/* $Id$ */
+
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+#define BACKEND_NAME dmc
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define DMC_CONFIG_FILE "dmc.conf"
+
+#include "dmc.h"
+
+/* A linked-list of attached devices and handles */
+static DMC_Device *FirstDevice = NULL;
+static DMC_Camera *FirstHandle = NULL;
+static int NumDevices = 0;
+
+static SANE_String_Const ValidModes[] = { "Full frame", "Viewfinder",
+ "Raw", "Thumbnail",
+ "Super-Resolution",
+ NULL };
+
+static SANE_String_Const ValidBalances[] = { "Daylight", "Incandescent",
+ "Fluorescent", NULL };
+
+static SANE_Word ValidASAs[] = { 3, 25, 50, 100 };
+
+/* Convert between 32-us ticks and milliseconds */
+#define MS_TO_TICKS(x) (((x) * 1000 + 16) / 32)
+#define TICKS_TO_MS(x) (((x) * 32) / 1000)
+
+/* Macros for stepping along the raw lines for super-resolution mode
+ They are very ugly because they handle boundary conditions at
+ the edges of the image. Yuck... */
+
+#define PREV_RED(i) (((i)/3)*3)
+#define NEXT_RED(i) (((i) >= BYTES_PER_RAW_LINE-3) ? BYTES_PER_RAW_LINE-3 : \
+ PREV_RED(i)+3)
+#define PREV_GREEN(i) ((i)<1 ? 1 : PREV_RED((i)-1)+1)
+#define NEXT_GREEN(i) ((i)<1 ? 1 : ((i) >= BYTES_PER_RAW_LINE-2) ? \
+ BYTES_PER_RAW_LINE-2 : PREV_GREEN(i)+3)
+#define PREV_BLUE(i) ((i)<2 ? 2 : PREV_RED((i)-2)+2)
+#define NEXT_BLUE(i) ((i)<2 ? 2 : ((i) >= BYTES_PER_RAW_LINE-1) ? \
+ BYTES_PER_RAW_LINE-1 : PREV_BLUE(i)+3)
+
+#define ADVANCE_COEFF(i) (((i)==1) ? 3 : (i)-1);
+
+/**********************************************************************
+//%FUNCTION: DMCRead
+//%ARGUMENTS:
+// fd -- file descriptor
+// typecode -- data type code
+// qualifier -- data type qualifier
+// maxlen -- tranfer length
+// buf -- buffer to store data in
+// len -- set to actual length of data
+//%RETURNS:
+// A SANE status code
+//%DESCRIPTION:
+// Reads the particular data selected by typecode and qualifier
+// *********************************************************************/
+static SANE_Status
+DMCRead(int fd, unsigned int typecode, unsigned int qualifier,
+ SANE_Byte *buf, size_t maxlen, size_t *len)
+{
+ uint8_t readCmd[10];
+ SANE_Status status;
+
+ readCmd[0] = 0x28;
+ readCmd[1] = 0;
+ readCmd[2] = typecode;
+ readCmd[3] = 0;
+ readCmd[4] = (qualifier >> 8) & 0xFF;
+ readCmd[5] = qualifier & 0xFF;
+ readCmd[6] = (maxlen >> 16) & 0xFF;
+ readCmd[7] = (maxlen >> 8) & 0xFF;
+ readCmd[8] = maxlen & 0xFF;
+ readCmd[9] = 0;
+ DBG(3, "DMCRead: typecode=%x, qualifier=%x, maxlen=%lu\n",
+ typecode, qualifier, (u_long) maxlen);
+
+ *len = maxlen;
+ status = sanei_scsi_cmd(fd, readCmd, sizeof(readCmd), buf, len);
+ DBG(3, "DMCRead: Read %lu bytes\n", (u_long) *len);
+ return status;
+}
+
+/**********************************************************************
+//%FUNCTION: DMCWrite
+//%ARGUMENTS:
+// fd -- file descriptor
+// typecode -- data type code
+// qualifier -- data type qualifier
+// maxlen -- tranfer length
+// buf -- buffer to store data in
+//%RETURNS:
+// A SANE status code
+//%DESCRIPTION:
+// Writes the particular data selected by typecode and qualifier
+// *********************************************************************/
+static SANE_Status
+DMCWrite(int fd, unsigned int typecode, unsigned int qualifier,
+ SANE_Byte *buf, size_t maxlen)
+{
+ uint8_t *writeCmd;
+ SANE_Status status;
+
+ writeCmd = malloc(maxlen + 10);
+ if (!writeCmd) return SANE_STATUS_NO_MEM;
+
+ writeCmd[0] = 0x2A;
+ writeCmd[1] = 0;
+ writeCmd[2] = typecode;
+ writeCmd[3] = 0;
+ writeCmd[4] = (qualifier >> 8) & 0xFF;
+ writeCmd[5] = qualifier & 0xFF;
+ writeCmd[6] = (maxlen >> 16) & 0xFF;
+ writeCmd[7] = (maxlen >> 8) & 0xFF;
+ writeCmd[8] = maxlen & 0xFF;
+ writeCmd[9] = 0;
+ memcpy(writeCmd+10, buf, maxlen);
+
+ DBG(3, "DMCWrite: typecode=%x, qualifier=%x, maxlen=%lu\n",
+ typecode, qualifier, (u_long) maxlen);
+
+ status = sanei_scsi_cmd(fd, writeCmd, 10+maxlen, NULL, NULL);
+ free(writeCmd);
+ return status;
+}
+
+/**********************************************************************
+//%FUNCTION: DMCAttach
+//%ARGUMENTS:
+// devname -- name of device file to open
+// devp -- a DMC_Device structure which we fill in if it's not NULL.
+//%RETURNS:
+// SANE_STATUS_GOOD -- We have a Polaroid DMC attached and all looks good.
+// SANE_STATUS_INVAL -- There's a problem.
+//%DESCRIPTION:
+// Verifies that a Polaroid DMC is attached. Sets up device options in
+// DMC_Device structure.
+// *********************************************************************/
+#define INQ_LEN 255
+static SANE_Status
+DMCAttach(char const *devname, DMC_Device **devp)
+{
+ DMC_Device *dev;
+ SANE_Status status;
+ int fd;
+ size_t size;
+ char result[INQ_LEN];
+
+ uint8_t exposureCalculationResults[16];
+ uint8_t userInterfaceSettings[16];
+
+ static uint8_t const inquiry[] =
+ { 0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00 };
+
+ static uint8_t const test_unit_ready[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static uint8_t const no_viewfinder[] =
+ { 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ /* If we're already attached, do nothing */
+
+ for (dev = FirstDevice; dev; dev = dev->next) {
+ if (!strcmp(dev->sane.name, devname)) {
+ if (devp) *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG(3, "DMCAttach: opening `%s'\n", devname);
+ status = sanei_scsi_open(devname, &fd, 0, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "DMCAttach: open failed (%s)\n", sane_strstatus(status));
+ return status;
+ }
+
+ DBG(3, "DMCAttach: sending INQUIRY\n");
+ size = sizeof(result);
+ status = sanei_scsi_cmd(fd, inquiry, sizeof(inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD || size < 32) {
+ if (status == SANE_STATUS_GOOD) status = SANE_STATUS_INVAL;
+ DBG(1, "DMCAttach: inquiry failed (%s)\n", sane_strstatus(status));
+ sanei_scsi_close(fd);
+ return status;
+ }
+
+ /* Verify that we have a Polaroid DMC */
+
+ if (result[0] != 6 ||
+ strncmp(result+8, "POLAROID", 8) ||
+ strncmp(result+16, "DMC ", 8)) {
+ sanei_scsi_close(fd);
+ DBG(1, "DMCAttach: Device does not look like a Polaroid DMC\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(3, "DMCAttach: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd(fd, test_unit_ready, sizeof(test_unit_ready),
+ NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "DMCAttach: test unit ready failed (%s)\n",
+ sane_strstatus(status));
+ sanei_scsi_close(fd);
+ return status;
+ }
+
+ /* Read current ASA and shutter speed settings */
+ status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults,
+ sizeof(exposureCalculationResults), &size);
+ if (status != SANE_STATUS_GOOD ||
+ size < sizeof(exposureCalculationResults)) {
+ DBG(1, "DMCAttach: Couldn't read exposure calculation results (%s)\n",
+ sane_strstatus(status));
+ sanei_scsi_close(fd);
+ if (status == SANE_STATUS_GOOD) status = SANE_STATUS_IO_ERROR;
+ return status;
+ }
+
+ /* Read current white balance settings */
+ status = DMCRead(fd, 0x82, 0x0, userInterfaceSettings,
+ sizeof(userInterfaceSettings), &size);
+ if (status != SANE_STATUS_GOOD ||
+ size < sizeof(userInterfaceSettings)) {
+ DBG(1, "DMCAttach: Couldn't read user interface settings (%s)\n",
+ sane_strstatus(status));
+ sanei_scsi_close(fd);
+ if (status == SANE_STATUS_GOOD) status = SANE_STATUS_IO_ERROR;
+ return status;
+ }
+
+ /* Shut off viewfinder mode */
+ status = sanei_scsi_cmd(fd, no_viewfinder, sizeof(no_viewfinder),
+ NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ sanei_scsi_close(fd);
+ return status;
+ }
+ sanei_scsi_close(fd);
+
+ DBG(3, "DMCAttach: Looks like we have a Polaroid DMC\n");
+
+ dev = malloc(sizeof(*dev));
+ if (!dev) return SANE_STATUS_NO_MEM;
+ memset(dev, 0, sizeof(*dev));
+
+ dev->sane.name = strdup(devname);
+ dev->sane.vendor = "Polaroid";
+ dev->sane.model = "DMC";
+ dev->sane.type = "still camera";
+ dev->next = FirstDevice;
+ dev->whiteBalance = userInterfaceSettings[5];
+ if (dev->whiteBalance > WHITE_BALANCE_FLUORESCENT) {
+ dev->whiteBalance = WHITE_BALANCE_FLUORESCENT;
+ }
+
+ /* Bright Eyes documentation gives these as shutter speed ranges (ms) */
+ /* dev->shutterSpeedRange.min = 8; */
+ /* dev->shutterSpeedRange.max = 320; */
+
+ /* User's manual says these are shutter speed ranges (ms) */
+ dev->shutterSpeedRange.min = 8;
+ dev->shutterSpeedRange.max = 1000;
+ dev->shutterSpeedRange.quant = 2;
+ dev->shutterSpeed =
+ (exposureCalculationResults[10] << 8) +
+ exposureCalculationResults[11];
+
+ /* Convert from ticks to ms */
+ dev->shutterSpeed = TICKS_TO_MS(dev->shutterSpeed);
+
+ dev->asa = exposureCalculationResults[13];
+ if (dev->asa > ASA_100) dev->asa = ASA_100;
+ dev->asa = ValidASAs[dev->asa + 1];
+ FirstDevice = dev;
+ NumDevices++;
+ if (devp) *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: ValidateHandle
+//%ARGUMENTS:
+// handle -- a handle for an opened camera
+//%RETURNS:
+// A validated pointer to the camera or NULL if handle is not valid.
+// *********************************************************************/
+static DMC_Camera *
+ValidateHandle(SANE_Handle handle)
+{
+ DMC_Camera *c;
+ for (c = FirstHandle; c; c = c->next) {
+ if (c == handle) return c;
+ }
+ DBG(1, "ValidateHandle: invalid handle %p\n", handle);
+ return NULL;
+}
+
+/**********************************************************************
+//%FUNCTION: DMCInitOptions
+//%ARGUMENTS:
+// c -- a DMC camera device
+//%RETURNS:
+// SANE_STATUS_GOOD -- OK
+// SANE_STATUS_INVAL -- There's a problem.
+//%DESCRIPTION:
+// Initializes the options in the DMC_Camera structure
+// *********************************************************************/
+static SANE_Status
+DMCInitOptions(DMC_Camera *c)
+{
+ int i;
+
+ /* Image is initially 801x600 */
+ c->tl_x_range.min = 0;
+ c->tl_x_range.max = c->tl_x_range.min;
+ c->tl_x_range.quant = 1;
+ c->tl_y_range.min = 0;
+ c->tl_y_range.max = c->tl_y_range.min;
+ c->tl_y_range.quant = 1;
+
+ c->br_x_range.min = 800;
+ c->br_x_range.max = c->br_x_range.min;
+ c->br_x_range.quant = 1;
+ c->br_y_range.min = 599;
+ c->br_y_range.max = c->br_y_range.min;
+ c->br_y_range.quant = 1;
+
+ memset(c->opt, 0, sizeof(c->opt));
+ memset(c->val, 0, sizeof(c->val));
+
+ for (i=0; i<NUM_OPTIONS; i++) {
+ c->opt[i].type = SANE_TYPE_INT;
+ c->opt[i].size = sizeof(SANE_Word);
+ c->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ c->opt[i].unit = SANE_UNIT_NONE;
+ }
+
+ c->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ c->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ c->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ c->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ c->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ c->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ c->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ c->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ c->opt[OPT_GEOMETRY_GROUP].name = "";
+ c->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ c->opt[OPT_GEOMETRY_GROUP].desc = "";
+ c->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ c->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ c->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ c->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ c->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ c->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ c->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ c->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ c->opt[OPT_TL_X].constraint.range = &c->tl_x_range;
+ c->val[OPT_TL_X].w = c->tl_x_range.min;
+
+ /* top-left y */
+ c->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ c->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ c->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ c->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ c->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ c->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ c->opt[OPT_TL_Y].constraint.range = &c->tl_y_range;
+ c->val[OPT_TL_Y].w = c->tl_y_range.min;
+
+ /* bottom-right x */
+ c->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ c->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ c->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ c->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ c->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ c->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ c->opt[OPT_BR_X].constraint.range = &c->br_x_range;
+ c->val[OPT_BR_X].w = c->br_x_range.min;
+
+ /* bottom-right y */
+ c->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ c->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ c->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ c->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ c->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ c->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ c->opt[OPT_BR_Y].constraint.range = &c->br_y_range;
+ c->val[OPT_BR_Y].w = c->br_y_range.min;
+
+ c->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ c->opt[OPT_MODE_GROUP].name = "";
+ c->opt[OPT_MODE_GROUP].title = "Imaging Mode";
+ c->opt[OPT_MODE_GROUP].desc = "";
+ c->opt[OPT_MODE_GROUP].cap = SANE_CAP_ADVANCED;
+ c->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ c->opt[OPT_IMAGE_MODE].name = "imagemode";
+ c->opt[OPT_IMAGE_MODE].title = "Image Mode";
+ c->opt[OPT_IMAGE_MODE].desc = "Selects image mode: 800x600 full frame, 270x201 viewfinder mode, 1599x600 \"raw\" image, 80x60 thumbnail image or 1599x1200 \"super-resolution\" image";
+ c->opt[OPT_IMAGE_MODE].type = SANE_TYPE_STRING;
+ c->opt[OPT_IMAGE_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ c->opt[OPT_IMAGE_MODE].constraint.string_list = ValidModes;
+ c->opt[OPT_IMAGE_MODE].size = 16;
+ c->val[OPT_IMAGE_MODE].s = "Full frame";
+
+ c->opt[OPT_ASA].name = "asa";
+ c->opt[OPT_ASA].title = "ASA Setting";
+ c->opt[OPT_ASA].desc = "Equivalent ASA setting";
+ c->opt[OPT_ASA].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ c->opt[OPT_ASA].constraint.word_list = ValidASAs;
+ c->val[OPT_ASA].w = c->hw->asa;
+
+ c->opt[OPT_SHUTTER_SPEED].name = "shutterspeed";
+ c->opt[OPT_SHUTTER_SPEED].title = "Shutter Speed (ms)";
+ c->opt[OPT_SHUTTER_SPEED].desc = "Shutter Speed in milliseconds";
+ c->opt[OPT_SHUTTER_SPEED].constraint_type = SANE_CONSTRAINT_RANGE;
+ c->opt[OPT_SHUTTER_SPEED].constraint.range = &c->hw->shutterSpeedRange;
+ c->val[OPT_SHUTTER_SPEED].w = c->hw->shutterSpeed;
+
+ c->opt[OPT_WHITE_BALANCE].name = "whitebalance";
+ c->opt[OPT_WHITE_BALANCE].title = "White Balance";
+ c->opt[OPT_WHITE_BALANCE].desc = "Selects white balance";
+ c->opt[OPT_WHITE_BALANCE].type = SANE_TYPE_STRING;
+ c->opt[OPT_WHITE_BALANCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ c->opt[OPT_WHITE_BALANCE].constraint.string_list = ValidBalances;
+ c->opt[OPT_WHITE_BALANCE].size = 16;
+ c->val[OPT_WHITE_BALANCE].s = (SANE_String) ValidBalances[c->hw->whiteBalance];
+
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: DMCSetMode
+//%ARGUMENTS:
+// c -- a DMC camera device
+// mode -- Imaging mode
+//%RETURNS:
+// SANE_STATUS_GOOD -- OK
+// SANE_STATUS_INVAL -- There's a problem.
+//%DESCRIPTION:
+// Sets the camera's imaging mode.
+// *********************************************************************/
+static SANE_Status
+DMCSetMode(DMC_Camera *c, int mode)
+{
+ switch(mode) {
+ case IMAGE_MFI:
+ c->tl_x_range.min = 0;
+ c->tl_x_range.max = c->tl_x_range.max;
+ c->tl_y_range.min = 0;
+ c->tl_y_range.max = c->tl_y_range.max;
+ c->br_x_range.min = 800;
+ c->br_x_range.max = c->br_x_range.max;
+ c->br_y_range.min = 599;
+ c->br_y_range.max = c->br_y_range.max;
+ break;
+ case IMAGE_VIEWFINDER:
+ c->tl_x_range.min = 0;
+ c->tl_x_range.max = c->tl_x_range.max;
+ c->tl_y_range.min = 0;
+ c->tl_y_range.max = c->tl_y_range.max;
+ c->br_x_range.min = 269;
+ c->br_x_range.max = c->br_x_range.max;
+ c->br_y_range.min = 200;
+ c->br_y_range.max = c->br_y_range.max;
+ break;
+ case IMAGE_RAW:
+ c->tl_x_range.min = 0;
+ c->tl_x_range.max = c->tl_x_range.max;
+ c->tl_y_range.min = 0;
+ c->tl_y_range.max = c->tl_y_range.max;
+ c->br_x_range.min = 1598;
+ c->br_x_range.max = c->br_x_range.max;
+ c->br_y_range.min = 599;
+ c->br_y_range.max = c->br_y_range.max;
+ break;
+ case IMAGE_THUMB:
+ c->tl_x_range.min = 0;
+ c->tl_x_range.max = c->tl_x_range.max;
+ c->tl_y_range.min = 0;
+ c->tl_y_range.max = c->tl_y_range.max;
+ c->br_x_range.min = 79;
+ c->br_x_range.max = c->br_x_range.max;
+ c->br_y_range.min = 59;
+ c->br_y_range.max = c->br_y_range.max;
+ break;
+ case IMAGE_SUPER_RES:
+ c->tl_x_range.min = 0;
+ c->tl_x_range.max = c->tl_x_range.max;
+ c->tl_y_range.min = 0;
+ c->tl_y_range.max = c->tl_y_range.max;
+ c->br_x_range.min = 1598;
+ c->br_x_range.max = c->br_x_range.max;
+ c->br_y_range.min = 1199;
+ c->br_y_range.max = c->br_y_range.max;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ c->imageMode = mode;
+ c->val[OPT_TL_X].w = c->tl_x_range.min;
+ c->val[OPT_TL_Y].w = c->tl_y_range.min;
+ c->val[OPT_BR_X].w = c->br_x_range.min;
+ c->val[OPT_BR_Y].w = c->br_y_range.min;
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: DMCCancel
+//%ARGUMENTS:
+// c -- a DMC camera device
+//%RETURNS:
+// SANE_STATUS_CANCELLED
+//%DESCRIPTION:
+// Cancels DMC image acquisition
+// *********************************************************************/
+static SANE_Status
+DMCCancel(DMC_Camera *c)
+{
+ if (c->fd >= 0) {
+ sanei_scsi_close(c->fd);
+ c->fd = -1;
+ }
+ return SANE_STATUS_CANCELLED;
+}
+
+/**********************************************************************
+//%FUNCTION: DMCSetASA
+//%ARGUMENTS:
+// fd -- SCSI file descriptor
+// asa -- the ASA to set
+//%RETURNS:
+// A sane status value
+//%DESCRIPTION:
+// Sets the equivalent ASA setting of the camera.
+// *********************************************************************/
+static SANE_Status
+DMCSetASA(int fd, unsigned int asa)
+{
+ uint8_t exposureCalculationResults[16];
+ SANE_Status status;
+ size_t len;
+ int i;
+
+ DBG(3, "DMCSetAsa: %d\n", asa);
+ for (i=1; i<=ASA_100+1; i++) {
+ if (asa == (unsigned int) ValidASAs[i]) break;
+ }
+
+ if (i > ASA_100+1) return SANE_STATUS_INVAL;
+
+ status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults,
+ sizeof(exposureCalculationResults), &len);
+ if (status != SANE_STATUS_GOOD) return status;
+ if (len < sizeof(exposureCalculationResults)) return SANE_STATUS_IO_ERROR;
+
+ exposureCalculationResults[13] = (uint8_t) i - 1;
+
+ return DMCWrite(fd, 0x87, 0x4, exposureCalculationResults,
+ sizeof(exposureCalculationResults));
+}
+
+/**********************************************************************
+//%FUNCTION: DMCSetWhiteBalance
+//%ARGUMENTS:
+// fd -- SCSI file descriptor
+// mode -- white balance mode
+//%RETURNS:
+// A sane status value
+//%DESCRIPTION:
+// Sets the equivalent ASA setting of the camera.
+// *********************************************************************/
+static SANE_Status
+DMCSetWhiteBalance(int fd, int mode)
+{
+ uint8_t userInterfaceSettings[16];
+ SANE_Status status;
+ size_t len;
+
+ DBG(3, "DMCSetWhiteBalance: %d\n", mode);
+ status = DMCRead(fd, 0x82, 0x0, userInterfaceSettings,
+ sizeof(userInterfaceSettings), &len);
+ if (status != SANE_STATUS_GOOD) return status;
+ if (len < sizeof(userInterfaceSettings)) return SANE_STATUS_IO_ERROR;
+
+ userInterfaceSettings[5] = (uint8_t) mode;
+
+ return DMCWrite(fd, 0x82, 0x0, userInterfaceSettings,
+ sizeof(userInterfaceSettings));
+}
+
+/**********************************************************************
+//%FUNCTION: DMCSetShutterSpeed
+//%ARGUMENTS:
+// fd -- SCSI file descriptor
+// speed -- shutter speed in ms
+//%RETURNS:
+// A sane status value
+//%DESCRIPTION:
+// Sets the shutter speed of the camera
+// *********************************************************************/
+static SANE_Status
+DMCSetShutterSpeed(int fd, unsigned int speed)
+{
+ uint8_t exposureCalculationResults[16];
+ SANE_Status status;
+ size_t len;
+
+ DBG(3, "DMCSetShutterSpeed: %u\n", speed);
+ /* Convert from ms to ticks */
+ speed = MS_TO_TICKS(speed);
+
+ status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults,
+ sizeof(exposureCalculationResults), &len);
+ if (status != SANE_STATUS_GOOD) return status;
+ if (len < sizeof(exposureCalculationResults)) return SANE_STATUS_IO_ERROR;
+
+ exposureCalculationResults[10] = (speed >> 8) & 0xFF;
+ exposureCalculationResults[11] = speed & 0xFF;
+
+ return DMCWrite(fd, 0x87, 0x4, exposureCalculationResults,
+ sizeof(exposureCalculationResults));
+}
+
+/**********************************************************************
+//%FUNCTION: DMCReadTwoSuperResolutionLines
+//%ARGUMENTS:
+// c -- DMC Camera
+// buf -- where to put output.
+// lastLine -- if true, these are the last two lines in the super-resolution
+// image to read.
+//%RETURNS:
+// Nothing
+//%DESCRIPTION:
+// Reads a single "raw" line from the camera (if needed) and constructs
+// two "super-resolution" output lines in "buf"
+// *********************************************************************/
+static SANE_Status
+DMCReadTwoSuperResolutionLines(DMC_Camera *c, SANE_Byte *buf, int lastLine)
+{
+ SANE_Status status;
+ size_t len;
+
+ SANE_Byte *output, *prev;
+ int redCoeff, greenCoeff, blueCoeff;
+ int red, green, blue;
+ int i;
+
+ if (c->nextRawLineValid) {
+ memcpy(c->currentRawLine, c->nextRawLine, BYTES_PER_RAW_LINE);
+ } else {
+ status = DMCRead(c->fd, 0x00, IMAGE_RAW,
+ c->currentRawLine, BYTES_PER_RAW_LINE, &len);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ if (!lastLine) {
+ status = DMCRead(c->fd, 0x00, IMAGE_RAW,
+ c->nextRawLine, BYTES_PER_RAW_LINE, &len);
+ if (status != SANE_STATUS_GOOD) return status;
+ c->nextRawLineValid = 1;
+ }
+
+ redCoeff = 3;
+ greenCoeff = 1;
+ blueCoeff = 2;
+
+ /* Do the first super-resolution line */
+ output = buf;
+ for (i=0; i<BYTES_PER_RAW_LINE; i++) {
+ red = redCoeff * c->currentRawLine[PREV_RED(i)] +
+ (3-redCoeff) * c->currentRawLine[NEXT_RED(i)];
+ green = greenCoeff * c->currentRawLine[PREV_GREEN(i)] +
+ (3-greenCoeff) * c->currentRawLine[NEXT_GREEN(i)];
+ blue = blueCoeff * c->currentRawLine[PREV_BLUE(i)] +
+ (3-blueCoeff) * c->currentRawLine[NEXT_BLUE(i)];
+ *output++ = red/3;
+ *output++ = green/3;
+ *output++ = blue/3;
+ redCoeff = ADVANCE_COEFF(redCoeff);
+ greenCoeff = ADVANCE_COEFF(greenCoeff);
+ blueCoeff = ADVANCE_COEFF(blueCoeff);
+ }
+
+ /* Do the next super-resolution line and interpolate vertically */
+ if (lastLine) {
+ memcpy(buf+BYTES_PER_RAW_LINE*3, buf, BYTES_PER_RAW_LINE*3);
+ return SANE_STATUS_GOOD;
+ }
+ redCoeff = 3;
+ greenCoeff = 1;
+ blueCoeff = 2;
+
+ prev = buf;
+ for (i=0; i<BYTES_PER_RAW_LINE; i++) {
+ red = redCoeff * c->nextRawLine[PREV_RED(i)] +
+ (3-redCoeff) * c->nextRawLine[NEXT_RED(i)];
+ green = greenCoeff * c->nextRawLine[PREV_GREEN(i)] +
+ (3-greenCoeff) * c->nextRawLine[NEXT_GREEN(i)];
+ blue = blueCoeff * c->nextRawLine[PREV_BLUE(i)] +
+ (3-blueCoeff) * c->nextRawLine[NEXT_BLUE(i)];
+ *output++ = (red/3 + *prev++) / 2;
+ *output++ = (green/3 + *prev++) / 2;
+ *output++ = (blue/3 + *prev++) / 2;
+ redCoeff = ADVANCE_COEFF(redCoeff);
+ greenCoeff = ADVANCE_COEFF(greenCoeff);
+ blueCoeff = ADVANCE_COEFF(blueCoeff);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/***********************************************************************
+//%FUNCTION: attach_one (static function)
+//%ARGUMENTS:
+// dev -- device to attach
+//%RETURNS:
+// SANE_STATUS_GOOD
+//%DESCRIPTION:
+// tries to attach a device found by sanei_config_attach_matching_devices
+// *********************************************************************/
+static SANE_Status
+attach_one (const char *dev)
+{
+ DMCAttach (dev, 0);
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_init
+//%ARGUMENTS:
+// version_code -- pointer to where we stick our version code
+// authorize -- authorization function
+//%RETURNS:
+// A sane status value
+//%DESCRIPTION:
+// Initializes DMC sane system.
+// *********************************************************************/
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize;
+
+ DBG_INIT();
+ if (version_code) {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+ }
+
+ fp = sanei_config_open(DMC_CONFIG_FILE);
+ if (!fp) {
+ /* default to /dev/camera instead of insisting on config file */
+ if (DMCAttach ("/dev/camera", NULL) != SANE_STATUS_GOOD) {
+ /* OK, try /dev/scanner */
+ DMCAttach("/dev/scanner", NULL);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp)) {
+ if (dev_name[0] == '#') { /* ignore line comments */
+ continue;
+ }
+ len = strlen (dev_name);
+
+ if (!len) continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices(dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_exit
+//%ARGUMENTS:
+// None
+//%RETURNS:
+// Nothing
+//%DESCRIPTION:
+// Cleans up all the SANE information
+// *********************************************************************/
+void
+sane_exit(void)
+{
+ DMC_Device *dev, *next;
+
+ /* Close all handles */
+ while(FirstHandle) {
+ sane_close(FirstHandle);
+ }
+
+ /* Free all devices */
+ dev = FirstDevice;
+ while(dev) {
+ next = dev->next;
+ free((char *) dev->sane.model);
+ free(dev);
+ dev = next;
+ }
+}
+
+/**********************************************************************
+//%FUNCTION: sane_get_devices
+//%ARGUMENTS:
+// device_list -- set to allocated list of devices
+// local_only -- ignored
+//%RETURNS:
+// A SANE status
+//%DESCRIPTION:
+// Returns a list of all known DMC devices
+// *********************************************************************/
+SANE_Status
+sane_get_devices(SANE_Device const ***device_list, SANE_Bool local_only)
+{
+ static SANE_Device const **devlist = 0;
+ DMC_Device *dev;
+ int i = 0;
+
+ local_only = local_only;
+
+ if (devlist) free(devlist);
+ devlist = malloc((NumDevices+1) * sizeof(devlist[0]));
+ if (!devlist) return SANE_STATUS_NO_MEM;
+
+ for (dev=FirstDevice; dev; dev = dev->next) {
+ devlist[i++] = &dev->sane;
+ }
+ devlist[i] = NULL;
+
+ if (device_list) *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_open
+//%ARGUMENTS:
+// name -- name of device to open
+// handle -- set to a handle for the opened device
+//%RETURNS:
+// A SANE status
+//%DESCRIPTION:
+// Opens a DMC camera device
+// *********************************************************************/
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *handle)
+{
+ SANE_Status status;
+ DMC_Device *dev;
+ DMC_Camera *c;
+
+ /* If we're given a device name, search for it */
+ if (*name) {
+ for (dev = FirstDevice; dev; dev = dev->next) {
+ if (!strcmp(dev->sane.name, name)) {
+ break;
+ }
+ }
+ if (!dev) {
+ status = DMCAttach(name, &dev);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ } else {
+ dev = FirstDevice;
+ }
+
+ if (!dev) return SANE_STATUS_INVAL;
+
+ c = malloc(sizeof(*c));
+ if (!c) return SANE_STATUS_NO_MEM;
+
+ memset(c, 0, sizeof(*c));
+
+ c->fd = -1;
+ c->hw = dev;
+ c->readBuffer = NULL;
+ c->readPtr = NULL;
+ c->imageMode = IMAGE_MFI;
+ c->inViewfinderMode = 0;
+ c->nextRawLineValid = 0;
+
+ DMCInitOptions(c);
+
+ c->next = FirstHandle;
+ FirstHandle = c;
+ if (handle) *handle = c;
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_close
+//%ARGUMENTS:
+// handle -- handle of device to close
+//%RETURNS:
+// A SANE status
+//%DESCRIPTION:
+// Closes a DMC camera device
+// *********************************************************************/
+void
+sane_close(SANE_Handle handle)
+{
+ DMC_Camera *prev, *c;
+ prev = NULL;
+ for (c = FirstHandle; c; c = c->next) {
+ if (c == handle) break;
+ prev = c;
+ }
+ if (!c) {
+ DBG(1, "close: invalid handle %p\n", handle);
+ return;
+ }
+ DMCCancel(c);
+
+ if (prev) prev->next = c->next;
+ else FirstHandle = c->next;
+
+ if (c->readBuffer) {
+ free(c->readBuffer);
+ }
+ free(c);
+}
+
+/**********************************************************************
+//%FUNCTION: sane_get_option_descriptor
+//%ARGUMENTS:
+// handle -- handle of device
+// option -- option number to retrieve
+//%RETURNS:
+// An option descriptor or NULL on error
+// *********************************************************************/
+SANE_Option_Descriptor const *
+sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+ DMC_Camera *c = ValidateHandle(handle);
+ if (!c) return NULL;
+
+ if ((unsigned) option >= NUM_OPTIONS) return NULL;
+ return c->opt + option;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_control_option
+//%ARGUMENTS:
+// handle -- handle of device
+// option -- option number to retrieve
+// action -- what to do with the option
+// val -- value to set option to
+// info -- returned info flags
+//%RETURNS:
+// SANE status
+//%DESCRIPTION:
+// Sets or queries option values
+// *********************************************************************/
+SANE_Status
+sane_control_option(SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int *info)
+{
+ DMC_Camera *c;
+ SANE_Word cap;
+ SANE_Status status;
+ int i;
+
+ if (info) *info = 0;
+
+ c = ValidateHandle(handle);
+ if (!c) return SANE_STATUS_INVAL;
+
+ if (c->fd >= 0) return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL;
+
+ cap = c->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE(cap)) return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE) {
+ switch(c->opt[option].type) {
+ case SANE_TYPE_INT:
+ * (SANE_Int *) val = c->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ case SANE_TYPE_STRING:
+ strcpy(val, c->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ default:
+ DBG(3, "impossible option type!\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (action == SANE_ACTION_SET_AUTO) {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ switch(option) {
+ case OPT_IMAGE_MODE:
+ for (i=0; i<NUM_IMAGE_MODES; i++) {
+ if (!strcmp(val, ValidModes[i])) {
+ status = DMCSetMode(c, i);
+ c->val[OPT_IMAGE_MODE].s = (SANE_String) ValidModes[i];
+ if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ break;
+ case OPT_WHITE_BALANCE:
+ for (i=0; i<=WHITE_BALANCE_FLUORESCENT; i++) {
+ if (!strcmp(val, ValidBalances[i])) {
+ c->val[OPT_WHITE_BALANCE].s = (SANE_String) ValidBalances[i];
+ return SANE_STATUS_GOOD;
+ }
+ }
+ break;
+ case OPT_ASA:
+ for (i=1; i<= ASA_100+1; i++) {
+ if (* ((SANE_Int *) val) == ValidASAs[i]) {
+ c->val[OPT_ASA].w = ValidASAs[i];
+ return SANE_STATUS_GOOD;
+ }
+ }
+ break;
+ case OPT_SHUTTER_SPEED:
+ if (* (SANE_Int *) val < c->hw->shutterSpeedRange.min ||
+ * (SANE_Int *) val > c->hw->shutterSpeedRange.max) {
+ return SANE_STATUS_INVAL;
+ }
+ c->val[OPT_SHUTTER_SPEED].w = * (SANE_Int *) val;
+ /* Do any roundoff */
+ c->val[OPT_SHUTTER_SPEED].w =
+ TICKS_TO_MS(MS_TO_TICKS(c->val[OPT_SHUTTER_SPEED].w));
+ if (c->val[OPT_SHUTTER_SPEED].w != * (SANE_Int *) val) {
+ if (info) *info |= SANE_INFO_INEXACT;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ default:
+ /* Should really be INVAL, but just bit-bucket set requests... */
+ return SANE_STATUS_GOOD;
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_get_parameters
+//%ARGUMENTS:
+// handle -- handle of device
+// params -- set to device parameters
+//%RETURNS:
+// SANE status
+//%DESCRIPTION:
+// Returns parameters for current or next image.
+// *********************************************************************/
+SANE_Status
+sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ DMC_Camera *c = ValidateHandle(handle);
+ if (!c) return SANE_STATUS_INVAL;
+
+ if (c->fd < 0) {
+ int width, height;
+ memset(&c->params, 0, sizeof(c->params));
+
+ width = c->val[OPT_BR_X].w - c->val[OPT_TL_X].w;
+ height = c->val[OPT_BR_Y].w - c->val[OPT_TL_Y].w;
+ c->params.pixels_per_line = width + 1;
+ c->params.lines = height+1;
+ c->params.depth = 8;
+ c->params.last_frame = SANE_TRUE;
+ switch(c->imageMode) {
+ case IMAGE_SUPER_RES:
+ case IMAGE_MFI:
+ case IMAGE_THUMB:
+ c->params.format = SANE_FRAME_RGB;
+ c->params.bytes_per_line = c->params.pixels_per_line * 3;
+ break;
+ case IMAGE_RAW:
+ case IMAGE_VIEWFINDER:
+ c->params.format = SANE_FRAME_GRAY;
+ c->params.bytes_per_line = c->params.pixels_per_line;
+ break;
+ }
+ }
+ if (params) *params = c->params;
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_start
+//%ARGUMENTS:
+// handle -- handle of device
+//%RETURNS:
+// SANE status
+//%DESCRIPTION:
+// Starts acquisition
+// *********************************************************************/
+SANE_Status
+sane_start(SANE_Handle handle)
+{
+ static uint8_t const acquire[] =
+ { 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static uint8_t const viewfinder[] =
+ { 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static uint8_t const no_viewfinder[] =
+ { 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ DMC_Camera *c = ValidateHandle(handle);
+ SANE_Status status;
+ int i;
+
+ if (!c) return SANE_STATUS_INVAL;
+
+ /* If we're already open, barf -- not sure this is the best status */
+ if (c->fd >= 0) return SANE_STATUS_DEVICE_BUSY;
+
+ /* Get rid of old read buffers */
+ if (c->readBuffer) {
+ free(c->readBuffer);
+ c->readBuffer = NULL;
+ c->readPtr = NULL;
+ }
+
+ c->nextRawLineValid = 0;
+
+ /* Refresh parameter list */
+ status = sane_get_parameters(c, NULL);
+ if (status != SANE_STATUS_GOOD) return status;
+
+ status = sanei_scsi_open(c->hw->sane.name, &c->fd, NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ c->fd = -1;
+ DBG(1, "DMC: Open of `%s' failed: %s\n",
+ c->hw->sane.name, sane_strstatus(status));
+ return status;
+ }
+
+ /* Set ASA and shutter speed if they're no longer current */
+ if (c->val[OPT_ASA].w != c->hw->asa) {
+ status = DMCSetASA(c->fd, c->val[OPT_ASA].w);
+ if (status != SANE_STATUS_GOOD) {
+ DMCCancel(c);
+ return status;
+ }
+ c->hw->asa = c->val[OPT_ASA].w;
+ }
+
+ if ((unsigned int) c->val[OPT_SHUTTER_SPEED].w != c->hw->shutterSpeed) {
+ status = DMCSetShutterSpeed(c->fd, c->val[OPT_SHUTTER_SPEED].w);
+ if (status != SANE_STATUS_GOOD) {
+ DMCCancel(c);
+ return status;
+ }
+ c->hw->shutterSpeed = c->val[OPT_SHUTTER_SPEED].w;
+ }
+
+ /* Set white balance mode if needed */
+ for (i=0; i<=WHITE_BALANCE_FLUORESCENT; i++) {
+ if (!strcmp(ValidBalances[i], c->val[OPT_WHITE_BALANCE].s)) {
+ if (i != c->hw->whiteBalance) {
+ status = DMCSetWhiteBalance(c->fd, i);
+ if (status != SANE_STATUS_GOOD) {
+ DMCCancel(c);
+ return status;
+ }
+ c->hw->whiteBalance = i;
+ }
+ }
+ }
+
+ /* Flip into viewfinder mode if needed */
+ if (c->imageMode == IMAGE_VIEWFINDER && !c->inViewfinderMode) {
+ status = sanei_scsi_cmd(c->fd, viewfinder, sizeof(viewfinder),
+ NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DMCCancel(c);
+ return status;
+ }
+ c->inViewfinderMode = 1;
+ }
+
+ /* Flip out of viewfinder mode if needed */
+ if (c->imageMode != IMAGE_VIEWFINDER && c->inViewfinderMode) {
+ status = sanei_scsi_cmd(c->fd, no_viewfinder, sizeof(no_viewfinder),
+ NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DMCCancel(c);
+ return status;
+ }
+ c->inViewfinderMode = 0;
+ }
+
+
+ status = sanei_scsi_cmd(c->fd, acquire, sizeof(acquire), NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DMCCancel(c);
+ return status;
+ }
+ c->bytes_to_read = c->params.bytes_per_line * c->params.lines;
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_read
+//%ARGUMENTS:
+// handle -- handle of device
+// buf -- destination for data
+// max_len -- maximum amount of data to store
+// len -- set to actual amount of data stored.
+//%RETURNS:
+// SANE status
+//%DESCRIPTION:
+// Reads image data from the camera
+// *********************************************************************/
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
+{
+ SANE_Status status;
+ DMC_Camera *c = ValidateHandle(handle);
+ size_t size;
+ SANE_Int i;
+
+ if (!c) return SANE_STATUS_INVAL;
+
+ if (c->fd < 0) return SANE_STATUS_INVAL;
+
+ if (c->bytes_to_read == 0) {
+ if (c->readBuffer) {
+ free(c->readBuffer);
+ c->readBuffer = NULL;
+ c->readPtr = NULL;
+ }
+ DMCCancel(c);
+ return SANE_STATUS_EOF;
+ }
+
+ if (max_len == 0) {
+ return SANE_STATUS_GOOD;
+ }
+
+ if (c->imageMode == IMAGE_SUPER_RES) {
+ /* We have to read *two* complete rows... */
+ max_len = (max_len / (2*c->params.bytes_per_line)) *
+ (2*c->params.bytes_per_line);
+ /* If user is trying to read less than two complete lines, fail */
+ if (max_len == 0) return SANE_STATUS_INVAL;
+ if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read;
+ for (i=0; i<max_len; i += 2*c->params.bytes_per_line) {
+ c->bytes_to_read -= 2*c->params.bytes_per_line;
+ status = DMCReadTwoSuperResolutionLines(c, buf+i,
+ !c->bytes_to_read);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ *len = max_len;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (c->imageMode == IMAGE_MFI || c->imageMode == IMAGE_RAW) {
+ /* We have to read complete rows... */
+ max_len = (max_len / c->params.bytes_per_line) * c->params.bytes_per_line;
+
+ /* If user is trying to read less than one complete row, fail */
+ if (max_len == 0) return SANE_STATUS_INVAL;
+ if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read;
+ c->bytes_to_read -= (unsigned int) max_len;
+ status = DMCRead(c->fd, 0x00, c->imageMode, buf, max_len, &size);
+ *len = size;
+ return status;
+ }
+
+ if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read;
+ if (c->readPtr) {
+ *len = max_len;
+ memcpy(buf, c->readPtr, max_len);
+ c->readPtr += max_len;
+ c->bytes_to_read -= max_len;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* Fill the read buffer completely */
+ c->readBuffer = malloc(c->bytes_to_read);
+ if (!c->readBuffer) return SANE_STATUS_NO_MEM;
+ c->readPtr = c->readBuffer;
+ status = DMCRead(c->fd, 0x00, c->imageMode, (SANE_Byte *) c->readBuffer,
+ c->bytes_to_read, &size);
+ *len = size;
+ if (status != SANE_STATUS_GOOD) return status;
+ if ((unsigned int) *len != c->bytes_to_read) return SANE_STATUS_IO_ERROR;
+
+ /* Now copy */
+ *len = max_len;
+ memcpy(buf, c->readPtr, max_len);
+ c->readPtr += max_len;
+ c->bytes_to_read -= max_len;
+ return SANE_STATUS_GOOD;
+}
+
+/**********************************************************************
+//%FUNCTION: sane_cancel
+//%ARGUMENTS:
+// handle -- handle of device
+//%RETURNS:
+// Nothing
+//%DESCRIPTION:
+// A quick cancellation of the scane
+// *********************************************************************/
+void
+sane_cancel (SANE_Handle handle)
+{
+ DMC_Camera *c = ValidateHandle(handle);
+ if (!c) return;
+
+ DMCCancel(c);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ handle = handle;
+ non_blocking = non_blocking;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+ handle = handle;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/dmc.conf.in b/backend/dmc.conf.in
new file mode 100644
index 0000000..908359f
--- /dev/null
+++ b/backend/dmc.conf.in
@@ -0,0 +1 @@
+/dev/camera
diff --git a/backend/dmc.h b/backend/dmc.h
new file mode 100644
index 0000000..aa866bc
--- /dev/null
+++ b/backend/dmc.h
@@ -0,0 +1,125 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998 David F. Skoll
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/* $Id$ */
+
+#ifndef polaroid_dmc_h
+#define polaroid_dmc_h
+
+#include <sys/types.h>
+
+#define BYTES_PER_RAW_LINE 1599
+
+typedef enum {
+ OPT_NUM_OPTS = 0,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_MODE_GROUP, /* Image acquisition mode */
+ OPT_IMAGE_MODE, /* Thumbnail, center cut or MFI'd image */
+ OPT_ASA, /* ASA Settings */
+ OPT_SHUTTER_SPEED, /* Shutter speed */
+ OPT_WHITE_BALANCE, /* White balance */
+
+ /* must come last: */
+ NUM_OPTIONS
+} DMC_Option;
+
+typedef struct DMC_Device {
+ struct DMC_Device *next;
+ SANE_Device sane;
+ SANE_Range shutterSpeedRange;
+ unsigned int shutterSpeed;
+ int asa;
+ int whiteBalance;
+} DMC_Device;
+
+typedef struct DMC_Camera {
+ /* all the state needed to define a scan request: */
+ struct DMC_Camera *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Parameters params;
+ size_t bytes_to_read;
+
+ SANE_Range tl_x_range;
+ SANE_Range tl_y_range;
+ SANE_Range br_x_range;
+ SANE_Range br_y_range;
+
+ int imageMode;
+
+ /* The DMC needs certain reads to be done in one chunk, meaning
+ we might have to buffer them. */
+ char *readBuffer;
+ char *readPtr;
+ int inViewfinderMode;
+ int fd; /* SCSI filedescriptor */
+ SANE_Byte currentRawLine[BYTES_PER_RAW_LINE];
+ SANE_Byte nextRawLine[BYTES_PER_RAW_LINE];
+ int nextRawLineValid;
+
+ /* scanner dependent/low-level state: */
+ DMC_Device *hw;
+} DMC_Camera;
+
+/* We only support the following four imaging modes */
+#define IMAGE_MFI 0x0000 /* 801x600 filtered image */
+#define IMAGE_VIEWFINDER 0x0001 /* 270x201 viewfinder image */
+#define IMAGE_RAW 0x0002 /* 1599x600 raw image */
+#define IMAGE_THUMB 0x0003 /* 80x60 thumbnail image */
+#define IMAGE_SUPER_RES 0x0004
+#define NUM_IMAGE_MODES 5
+
+#define ASA_25 0
+#define ASA_50 1
+#define ASA_100 2
+
+#define WHITE_BALANCE_DAYLIGHT 0
+#define WHITE_BALANCE_INCANDESCENT 1
+#define WHITE_BALANCE_FLUORESCENT 2
+
+#endif /* polaroid_dmc_h */
diff --git a/backend/epjitsu-cmd.h b/backend/epjitsu-cmd.h
new file mode 100644
index 0000000..2e914d9
--- /dev/null
+++ b/backend/epjitsu-cmd.h
@@ -0,0 +1,459 @@
+static void
+putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes)
+{
+ int i;
+
+ for (i = nbytes - 1; i >= 0; i--) {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+#define SET_WINDOW_LEN 72
+#define set_SW_byte7(out, val) out[7] = val
+#define set_SW_xres(out, val) putnbyte(out + 0x0a, val, 2)
+#define set_SW_yres(out, val) putnbyte(out + 0x0c, val, 2)
+#define set_SW_xpix(out, val) putnbyte(out + 0x16, val, 4)
+#define set_SW_ypix(out, val) putnbyte(out + 0x1a, val, 4)
+#define set_SW_compo(out, val) out[0x21] = val /*color=5,gray=2*/
+#define set_SW_bpp(out, val) out[0x22] = val
+#define set_SW_byte31(out, val) out[0x31] = val
+#define set_SW_byte32(out, val) out[0x32] = val
+#define set_SW_byte33(out, val) out[0x33] = val
+#define set_SW_lpb(out, val) out[0x34] = val
+#define set_SW_byte35(out, val) out[0x35] = val
+#define set_SW_byte36(out, val) out[0x36] = val
+#define set_SW_byte38(out, val) out[0x38] = val /*move motor?*/
+#define set_SW_fres(out, val) putnbyte(out + 0x39, val, 2)
+
+/*************** COARSE CALIBRATION DEFAULT PAYLOAD *************/
+/* 1b c6 (send coarse cal) command payload - not resolution specific? */
+/* first group of 3 is offset?, larger # == brighter */
+/* second group of 3 is gain?, larger # == brighter */
+static unsigned char coarseCalData_FI60F[] = {
+0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x26, 0x00, 0x26, 0x00, 0x26,
+0x00, 0x00, 0x0b, 0x22, 0x00, 0x00, 0x0b, 0x22, 0x00, 0x00, 0x0b, 0x22
+};
+static unsigned char coarseCalData_S300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x24, 0x00, 0x28, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** fi-60F 150dpi *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x____ bytes) */
+static unsigned char setWindowCoarseCal_FI60F_150[] = {
+0
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x____ bytes) */
+static unsigned char setWindowFineCal_FI60F_150[] = {
+0
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0x____ bytes) */
+static unsigned char setWindowSendCal_FI60F_150[] = {
+0
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_FI60F_150[] = {
+0
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_FI60F_150[] = {
+0
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_FI60F_150[] = {
+0
+};
+
+/*************** fi-60F 300dpi *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x1c20 bytes) */
+static unsigned char setWindowCoarseCal_FI60F_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x1c20 bytes) */
+static unsigned char setWindowFineCal_FI60F_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0x3840 bytes) */
+static unsigned char setWindowSendCal_FI60F_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_FI60F_300[] = { /* plus 0x3840 data bytes */
+0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x00, 0x04
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_FI60F_300[] = {
+0x39, 0x3f, 0x39, 0x3f, 0x39, 0x3f, 0x07
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_FI60F_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x06, 0xd5, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x48, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** fi-60F 600dpi *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x2160 bytes) */
+static unsigned char setWindowCoarseCal_FI60F_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x2160 bytes) */
+static unsigned char setWindowFineCal_FI60F_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0x42c0 bytes) */
+static unsigned char setWindowSendCal_FI60F_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x0d, 0xaa, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_FI60F_600[] = {
+0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x00, 0x04
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_FI60F_600[] = {
+0x2b, 0x40, 0x2b, 0x40, 0x2b, 0x40, 0x07
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_FI60F_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x0d, 0xaa, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 150dpi *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */
+static unsigned char setWindowCoarseCal_S300_150[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */
+static unsigned char setWindowFineCal_S300_150[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x90, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */
+static unsigned char setWindowSendCal_S300_150[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_150[] = { /* plus 0xc780 data bytes */
+0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04
+/*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_150[] = { /* plus 0xc780 data bytes */
+0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07
+/*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_150[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc8, 0x00, 0x00, 0x06, 0xe2, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 225dpi *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */
+static unsigned char setWindowCoarseCal_S300_225[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */
+static unsigned char setWindowFineCal_S300_225[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */
+static unsigned char setWindowSendCal_S300_225[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_225[] = { /* plus 0xc780 data bytes */
+0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04
+/*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_225[] = { /* plus 0xc780 data bytes */
+0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07
+/*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_225[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xe1, 0x00, 0xc8, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x34, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 300dpi *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x6000 bytes) */
+static unsigned char setWindowCoarseCal_S300_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x6000 bytes) */
+static unsigned char setWindowFineCal_S300_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc000 bytes) */
+static unsigned char setWindowSendCal_S300_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_300[] = { /* plus 0xc000 data bytes */
+0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04
+/*0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x05*/
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_300[] = { /* plus 0xc000 data bytes */
+0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07
+/*0xb8, 0x34, 0xb8, 0x34, 0xb8, 0x34, 0x08*/
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_300[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x0d, 0xc4, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 600dpi USB and AC power *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0xbc40 bytes) */
+static unsigned char setWindowCoarseCal_S300_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0xbc40 bytes) */
+static unsigned char setWindowFineCal_S300_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0x17880 bytes) */
+static unsigned char setWindowSendCal_S300_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_600[] = {
+0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x00, 0x04
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_600[] = {
+0xc7, 0x23, 0xc7, 0x23, 0xc7, 0x23, 0x07
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_600[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x40, 0x00, 0x00, 0x24, 0x21, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*S300 can also use a USB power cable, but it requires a different set of params?*/
+/*************** S300 150dpi USB *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */
+static unsigned char setWindowCoarseCal_S300_150_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */
+static unsigned char setWindowFineCal_S300_150_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */
+static unsigned char setWindowSendCal_S300_150_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_150_U[] = { /* plus 0xc780 data bytes */
+0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04
+/*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_150_U[] = { /* plus 0xc780 data bytes */
+0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07
+/*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_150_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x90, 0x00, 0x00, 0x09, 0x0d, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 225dpi USB *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */
+static unsigned char setWindowCoarseCal_S300_225_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */
+static unsigned char setWindowFineCal_S300_225_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */
+static unsigned char setWindowSendCal_S300_225_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_225_U[] = { /* plus 0xc780 data bytes */
+0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04
+/*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_225_U[] = { /* plus 0xc780 data bytes */
+0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07
+/*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_225_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xe1, 0x00, 0xc8, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xe0, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 300dpi USB *************/
+/* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */
+static unsigned char setWindowCoarseCal_S300_300_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */
+static unsigned char setWindowFineCal_S300_300_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */
+static unsigned char setWindowSendCal_S300_300_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 1b c3 (gain?) command header */
+static unsigned char sendCal1Header_S300_300_U[] = { /* plus 0xc780 data bytes */
+0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04
+/*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/
+};
+/* 1b c4 (offset?) command header */
+static unsigned char sendCal2Header_S300_300_U[] = { /* plus 0xc780 data bytes */
+0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07
+/*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/
+};
+/* 1b d1 (set window) before scan */
+static unsigned char setWindowScan_S300_300_U[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf0, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00,
+0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0x80, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*************** S300 600dpi USB is same as AC power *************/
diff --git a/backend/epjitsu.c b/backend/epjitsu.c
new file mode 100644
index 0000000..3e102da
--- /dev/null
+++ b/backend/epjitsu.c
@@ -0,0 +1,4330 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ This file implements a SANE backend for the Fujitsu fi-60F, the
+ ScanSnap S300/S1300, and (hopefully) other Epson-based scanners.
+
+ Copyright 2007-2010 by m. allan noah <kitno455 at gmail dot com>
+ Copyright 2009 by Richard Goedeken <richard at fascinationsoftware dot com>
+
+ Development funded by Microdea, Inc., TrueCheck, Inc. and Archivista, GmbH
+
+ --------------------------------------------------------------------------
+
+ The source code is divided in sections which you can easily find by
+ searching for the tag "@@".
+
+ Section 1 - Init & static stuff
+ Section 2 - sane_init, _get_devices, _open & friends
+ Section 3 - sane_*_option functions
+ Section 4 - sane_start, _get_param, _read & friends
+ Section 5 - sane_close functions
+ Section 6 - misc functions
+
+ Changes:
+ v0, 2007-08-08, MAN
+ - initial alpha release, S300 raw data only
+ v1, 2007-09-03, MAN
+ - only supports 300dpi duplex binary for S300
+ v2, 2007-09-05, MAN
+ - add resolution option (only one choice)
+ - add simplex option
+ v3, 2007-09-12, MAN
+ - add support for 150 dpi resolution
+ v4, 2007-10-03, MAN
+ - change binarization algo to use average of all channels
+ v5, 2007-10-10, MAN
+ - move data blocks to separate file
+ - add basic fi-60F support (600dpi color)
+ v6, 2007-11-12, MAN
+ - move various data vars into transfer structs
+ - move most of read_from_scanner to sane_read
+ - add single line reads to calibration code
+ - generate calibration buffer from above reads
+ v7, 2007-12-05, MAN
+ - split calibration into fine and coarse functions
+ - add S300 fine calibration code
+ - add S300 color and grayscale support
+ v8, 2007-12-06, MAN
+ - change sane_start to call ingest earlier
+ - enable SOURCE_ADF_BACK
+ - add if() around memcopy and better debugs in sane_read
+ - shorten default scan sizes from 15.4 to 11.75 inches
+ v9, 2007-12-17, MAN
+ - fi-60F 300 & 600 dpi support (150 is non-square?)
+ - fi-60F gray & binary support
+ - fi-60F improved calibration
+ v10, 2007-12-19, MAN (SANE v1.0.19)
+ - fix missing function (and memory leak)
+ v11 2008-02-14, MAN
+ - sanei_config_read has already cleaned string (#310597)
+ v12 2008-02-28, MAN
+ - cleanup double free bug with new destroy()
+ v13 2008-09-18, MAN
+ - add working page-height control
+ - add working brightness, contrast and threshold controls
+ - add disabled threshold curve and geometry controls
+ - move initialization code to sane_get_devices, for hotplugging
+ v14 2008-09-24, MAN
+ - support S300 on USB power
+ - support S300 225x200 and 600x600 scans
+ - support for automatic paper length detection (parm.lines = -1)
+ v15 2008-09-24, MAN
+ - expose hardware buttons/sensors as options for S300
+ v16 2008-10-01, MAN
+ - split fill_frontback_buffers_S300 into 3 functions
+ - enable threshold_curve option
+ - add 1-D dynamic binary thresholding code
+ - remove y-resolution option
+ - pad 225x200 data to 225x225
+ v17 2008-10-03, MAN
+ - increase scan height ~1/2 inch due to head offset
+ - change page length autodetection condition
+ v18 2009-01-21, MAN
+ - dont export private symbols
+ v19 2009-08-31, RG
+ - rewritten calibration routines
+ v20 2010-02-09, MAN (SANE 1.0.21 & 1.0.22)
+ - cleanup #include lines & copyright
+ - add S1300
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . .
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . . - sane_get_parameters() : returns estimated scan parameters
+ . . - (repeat previous 3 functions)
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times; after sane_read returns EOF,
+ . . loop may continue with sane_start which may return a 2nd page
+ . . when doing duplex scans, or load the next page from the ADF)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+*/
+
+/*
+ * @@ Section 1 - Init
+ */
+
+#include "../include/sane/config.h"
+
+#include <string.h> /*memcpy...*/
+#include <ctype.h> /*isspace*/
+#include <math.h> /*tan*/
+#include <unistd.h> /*usleep*/
+#include <time.h> /*time*/
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#include "epjitsu.h"
+#include "epjitsu-cmd.h"
+
+#define DEBUG 1
+#define BUILD 20
+
+#ifndef MAX3
+ #define MAX3(a,b,c) ((a) > (b) ? ((a) > (c) ? a : c) : ((b) > (c) ? b : c))
+#endif
+
+unsigned char global_firmware_filename[PATH_MAX];
+
+/* values for SANE_DEBUG_EPJITSU env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - usb cmd trace 25
+ - usb cmd detail 30
+ - useless noise 35
+*/
+
+/* Calibration settings */
+#define COARSE_OFFSET_TARGET 15
+static int coarse_gain_min[3] = { 88, 88, 88 }; /* front, back, FI-60F 3rd plane */
+static int coarse_gain_max[3] = { 92, 92, 92 };
+static int fine_gain_target[3] = {185, 150, 170}; /* front, back, FI-60F is this ok? */
+static float white_factor[3] = {1.0, 0.93, 0.98}; /* Blue, Red, Green */
+
+/* ------------------------------------------------------------------------- */
+#define STRING_FLATBED SANE_I18N("Flatbed")
+#define STRING_ADFFRONT SANE_I18N("ADF Front")
+#define STRING_ADFBACK SANE_I18N("ADF Back")
+#define STRING_ADFDUPLEX SANE_I18N("ADF Duplex")
+
+#define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ * a ptr to a single-linked list of scanner structs
+ */
+static const SANE_Device **sane_devArray = NULL;
+static struct scanner *scanner_devList = NULL;
+
+/*
+ * @@ Section 2 - SANE & scanner init code
+ */
+
+/*
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init: start\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: epjitsu backend %d.%d.%d, from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ DBG (10, "sane_init: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ *
+ * Read the config file, find scanners with help from sanei_*
+ * store in global device structs
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct scanner * s;
+ struct scanner * prev = NULL;
+ char line[PATH_MAX];
+ const char *lp;
+ FILE *fp;
+ int num_devices=0;
+ int i=0;
+
+ local_only = local_only; /* get rid of compiler warning */
+
+ DBG (10, "sane_get_devices: start\n");
+
+ /* mark all existing scanners as missing, attach_one will remove mark */
+ for (s = scanner_devList; s; s = s->next) {
+ s->missing = 1;
+ }
+
+ sanei_usb_init();
+
+ fp = sanei_config_open (CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n", CONFIG_FILE);
+
+ while (sanei_config_read (line, PATH_MAX, fp)) {
+
+ lp = line;
+
+ /* ignore comments */
+ if (*lp == '#')
+ continue;
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if ((strncmp ("firmware", lp, 8) == 0) && isspace (lp[8])) {
+ lp += 8;
+ lp = sanei_config_skip_whitespace (lp);
+ DBG (15, "sane_get_devices: firmware '%s'\n", lp);
+ strncpy((char *)global_firmware_filename,lp,PATH_MAX);
+ }
+ else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_usb_attach_matching_devices(lp, attach_one);
+ }
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" ignored.\n", lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: no config file '%s'!\n",
+ CONFIG_FILE);
+ }
+
+ /*delete missing scanners from list*/
+ for (s = scanner_devList; s;) {
+ if(s->missing){
+ DBG (5, "sane_get_devices: missing scanner %s\n",s->sane.name);
+
+ /*splice s out of list by changing pointer in prev to next*/
+ if(prev){
+ prev->next = s->next;
+ free(s);
+ s=prev->next;
+ }
+ /*remove s from head of list, using prev to cache it*/
+ else{
+ prev = s;
+ s = s->next;
+ free(prev);
+ prev=NULL;
+
+ /*reset head to next s*/
+ scanner_devList = s;
+ }
+ }
+ else{
+ prev = s;
+ s=prev->next;
+ }
+ }
+
+ for (s = scanner_devList; s; s=s->next) {
+ DBG (15, "sane_get_devices: found scanner %s\n",s->sane.name);
+ num_devices++;
+ }
+
+ DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
+ if (!sane_devArray)
+ return SANE_STATUS_NO_MEM;
+
+ for (s = scanner_devList; s; s=s->next) {
+ sane_devArray[i++] = (SANE_Device *)&s->sane;
+ }
+ sane_devArray[i] = 0;
+
+ if(device_list){
+ *device_list = sane_devArray;
+ }
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return ret;
+}
+
+/* callback used by sane_init
+ * build the scanner struct and link to global list
+ * unless struct is already loaded, then pretend
+ */
+static SANE_Status
+attach_one (const char *name)
+{
+ struct scanner *s;
+ int ret, i;
+
+ DBG (10, "attach_one: start '%s'\n", name);
+
+ for (s = scanner_devList; s; s = s->next) {
+ if (strcmp (s->sane.name, name) == 0) {
+ DBG (10, "attach_one: already attached!\n");
+ s->missing = 0;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* build a scanner struct to hold it */
+ DBG (15, "attach_one: init struct\n");
+
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* copy the device name */
+ s->sane.name = strdup (name);
+ if (!s->sane.name){
+ destroy(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* connect the fd */
+ DBG (15, "attach_one: connect fd\n");
+
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ destroy(s);
+ return ret;
+ }
+
+ /* load the firmware file into scanner */
+ ret = load_fw(s);
+ if (ret != SANE_STATUS_GOOD) {
+ destroy(s);
+ DBG (5, "attach_one: firmware load failed\n");
+ return ret;
+ }
+
+ /* Now query the device to load its vendor/model/version */
+ ret = get_ident(s);
+ if (ret != SANE_STATUS_GOOD) {
+ destroy(s);
+ DBG (5, "attach_one: identify failed\n");
+ return ret;
+ }
+
+ DBG (15, "attach_one: Found %s scanner %s at %s\n",
+ s->sane.vendor, s->sane.model, s->sane.name);
+
+ if (strstr (s->sane.model, "S300") || strstr (s->sane.model, "S1300")){
+ unsigned char stat;
+
+ DBG (15, "attach_one: Found S300/S1300\n");
+
+ stat = get_stat(s);
+ if(stat & 0x01){
+ DBG (5, "attach_one: on USB power?\n");
+ s->usb_power=1;
+ }
+
+ s->model = MODEL_S300;
+
+ s->has_adf = 1;
+ s->x_res_150 = 1;
+ s->x_res_225 = 1;
+ s->x_res_300 = 1;
+ s->x_res_600 = 1;
+ s->y_res_150 = 1;
+ s->y_res_225 = 1;
+ s->y_res_300 = 1;
+ s->y_res_600 = 1;
+
+ s->source = SOURCE_ADF_FRONT;
+ s->mode = MODE_LINEART;
+ s->resolution_x = 300;
+ s->page_height = 11.5 * 1200;
+
+ s->threshold = 120;
+ s->threshold_curve = 55;
+ }
+
+ else if (strstr (s->sane.model, "fi-60F")){
+ DBG (15, "attach_one: Found fi-60F\n");
+
+ s->model = MODEL_FI60F;
+
+ s->has_fb = 1;
+ s->x_res_150 = 0;
+ s->x_res_300 = 1;
+ s->x_res_600 = 1;
+ s->y_res_150 = 0;
+ s->y_res_300 = 1;
+ s->y_res_600 = 1;
+
+ s->source = SOURCE_FLATBED;
+ s->mode = MODE_COLOR;
+ s->resolution_x = 300;
+ s->page_height = 5.83 * 1200;
+
+ s->threshold = 120;
+ s->threshold_curve = 55;
+ }
+
+ else{
+ DBG (15, "attach_one: Found other\n");
+ }
+
+ /* set SANE option 'values' to good defaults */
+ DBG (15, "attach_one: init options\n");
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ memset (s->opt, 0, sizeof (s->opt));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].name = "filler";
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ DBG (15, "attach_one: init settings\n");
+ ret = change_params(s);
+
+ /* we close the connection, so that another backend can talk to scanner */
+ disconnect_fd(s);
+
+ s->next = scanner_devList;
+ scanner_devList = s;
+
+ DBG (10, "attach_one: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * connect the fd in the scanner struct
+ */
+static SANE_Status
+connect_fd (struct scanner *s)
+{
+ SANE_Status ret;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG (15, "connect_fd: opening USB device\n");
+ ret = sanei_usb_open (s->sane.name, &(s->fd));
+ }
+
+ if(ret != SANE_STATUS_GOOD){
+ DBG (5, "connect_fd: could not open device: %d\n", ret);
+ }
+
+ DBG (10, "connect_fd: finish\n");
+
+ return ret;
+}
+
+/*
+ * try to load fw into scanner
+ */
+static SANE_Status
+load_fw (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int file, i;
+ int len = 0;
+ unsigned char * buf;
+
+ unsigned char cmd[4];
+ size_t cmdLen;
+ unsigned char stat[2];
+ size_t statLen;
+
+ DBG (10, "load_fw: start\n");
+
+ /*check status*/
+ /*reuse stat buffer*/
+ stat[0] = get_stat(s);
+
+ if(stat[0] & 0x10){
+ DBG (5, "load_fw: firmware already loaded?\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ if(!global_firmware_filename[0]){
+ DBG (5, "load_fw: missing filename\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ file = open((char *)global_firmware_filename,O_RDONLY);
+ if(!file){
+ DBG (5, "load_fw: failed to open file %s\n",global_firmware_filename);
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ if(lseek(file,0x100,SEEK_SET) != 0x100){
+ DBG (5, "load_fw: failed to lseek file %s\n",global_firmware_filename);
+ close(file);
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ buf = malloc(FIRMWARE_LENGTH);
+ if(!buf){
+ DBG (5, "load_fw: failed to alloc mem\n");
+ close(file);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ len = read(file,buf,FIRMWARE_LENGTH);
+ close(file);
+
+ if(len != FIRMWARE_LENGTH){
+ DBG (5, "load_fw: firmware file %s wrong length\n",
+ global_firmware_filename);
+ free(buf);
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ DBG (15, "load_fw: read firmware file %s ok\n", global_firmware_filename);
+
+ /* firmware upload is in three commands */
+
+ /*start/status*/
+ cmd[0] = 0x1b;
+ cmd[1] = 0x06;
+ cmdLen = 2;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "load_fw: error on cmd 1\n");
+ free(buf);
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "load_fw: bad stat on cmd 1\n");
+ free(buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*length/data*/
+ cmd[0] = 0x01;
+ cmd[1] = 0x00;
+ cmd[2] = 0x01;
+ cmd[3] = 0x00;
+ cmdLen = 4;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ buf, FIRMWARE_LENGTH,
+ NULL, 0
+ );
+ if(ret){
+ DBG (5, "load_fw: error on cmd 2\n");
+ free(buf);
+ return ret;
+ }
+
+ /*checksum/status*/
+ cmd[0] = 0;
+ for(i=0;i<FIRMWARE_LENGTH;i++){
+ cmd[0] += buf[i];
+ }
+ free(buf);
+
+ cmdLen = 1;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "load_fw: error on cmd 3\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "load_fw: bad stat on cmd 3\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*reinit*/
+ cmd[0] = 0x1b;
+ cmd[1] = 0x16;
+ cmdLen = 2;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "load_fw: error reinit cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "load_fw: reinit cmd bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ cmd[0] = 0x80;
+ cmdLen = 1;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "load_fw: error reinit payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "load_fw: reinit payload bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*reuse stat buffer*/
+ stat[0] = get_stat(s);
+
+ if(!(stat[0] & 0x10)){
+ DBG (5, "load_fw: firmware not loaded? %#x\n",stat[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return ret;
+}
+
+/*
+ * try to load fw into scanner
+ */
+static unsigned char
+get_stat(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[2];
+ size_t cmdLen;
+ unsigned char stat[2];
+ size_t statLen;
+
+ DBG (10, "get_stat: start\n");
+
+ /*check status*/
+ cmd[0] = 0x1b;
+ cmd[1] = 0x03;
+ cmdLen = 2;
+ statLen = 2;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "get_stat: error checking status\n");
+ return 0;
+ }
+
+ return stat[0];
+}
+
+static SANE_Status
+get_ident(struct scanner *s)
+{
+ int i;
+ SANE_Status ret;
+
+ unsigned char cmd[] = {0x1b,0x13};
+ size_t cmdLen = 2;
+ unsigned char in[0x20];
+ size_t inLen = sizeof(in);
+
+ DBG (10, "get_ident: start\n");
+
+ ret = do_cmd (
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ /*hmm, similar to scsi?*/
+ for (i = 7; (in[i] == ' ' || in[i] == 0xff) && i >= 0; i--){
+ in[i] = 0;
+ }
+ s->sane.vendor = strndup((char *)in, 8);
+
+ for (i = 23; (in[i] == ' ' || in[i] == 0xff) && i >= 8; i--){
+ in[i] = 0;
+ }
+ s->sane.model= strndup((char *)in+8, 24);
+
+ s->sane.type = "scanner";
+
+ DBG (10, "get_ident: finish\n");
+ return ret;
+}
+
+/*
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct scanner *dev = NULL;
+ struct scanner *s = NULL;
+ SANE_Status ret;
+
+ DBG (10, "sane_open: start\n");
+
+ if(scanner_devList){
+ DBG (15, "sane_open: searching currently attached scanners\n");
+ }
+ else{
+ DBG (15, "sane_open: no scanners currently attached, attaching\n");
+
+ ret = sane_get_devices(NULL,0);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+ }
+
+ if(name[0] == 0){
+ DBG (15, "sane_open: no device requested, using default\n");
+ s = scanner_devList;
+ }
+ else{
+ DBG (15, "sane_open: device %s requested, attaching\n", name);
+
+ for (dev = scanner_devList; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, name) == 0) {
+ s = dev;
+ break;
+ }
+ }
+ }
+
+ if (!s) {
+ DBG (5, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "sane_open: device %s found\n", s->sane.name);
+
+ *handle = s;
+
+ /* connect the fd so we can talk to scanner */
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ DBG (10, "sane_open: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * @@ Section 3 - SANE Options functions
+ */
+
+/*
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+ int i;
+ SANE_Option_Descriptor *opt = &s->opt[option];
+
+ DBG (20, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ /* "Mode" group -------------------------------------------------------- */
+ if(option==OPT_MODE_GROUP){
+ opt->title = "Scan Mode";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* source */
+ else if(option==OPT_SOURCE){
+ i=0;
+ if(s->has_fb){
+ s->source_list[i++]=STRING_FLATBED;
+ }
+ if(s->has_adf){
+ s->source_list[i++]=STRING_ADFFRONT;
+ s->source_list[i++]=STRING_ADFBACK;
+ s->source_list[i++]=STRING_ADFDUPLEX;
+ }
+ s->source_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_SOURCE;
+ opt->title = SANE_TITLE_SCAN_SOURCE;
+ opt->desc = SANE_DESC_SCAN_SOURCE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->source_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if(i > 1){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ }
+
+ /* scan mode */
+ else if(option==OPT_MODE){
+ i=0;
+ s->mode_list[i++]=STRING_LINEART;
+ s->mode_list[i++]=STRING_GRAYSCALE;
+ s->mode_list[i++]=STRING_COLOR;
+ s->mode_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if(i > 1){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ }
+
+ else if(option==OPT_X_RES){
+ i=0;
+ if(s->x_res_150){
+ s->x_res_list[++i] = 150;
+ }
+ if(s->x_res_225){
+ s->x_res_list[++i] = 225;
+ }
+ if(s->x_res_300){
+ s->x_res_list[++i] = 300;
+ }
+ if(s->x_res_600){
+ s->x_res_list[++i] = 600;
+ }
+ s->x_res_list[0] = i;
+
+ opt->name = SANE_NAME_SCAN_RESOLUTION;
+ opt->title = SANE_TITLE_SCAN_X_RESOLUTION;
+ opt->desc = SANE_DESC_SCAN_X_RESOLUTION;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_DPI;
+ if(i > 1){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opt->constraint.word_list = s->x_res_list;
+ }
+
+ /* "Geometry" group ---------------------------------------------------- */
+ if(option==OPT_GEOMETRY_GROUP){
+ opt->name = SANE_NAME_GEOMETRY;
+ opt->title = SANE_TITLE_GEOMETRY;
+ opt->desc = SANE_DESC_GEOMETRY;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* top-left x */
+ if(option==OPT_TL_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
+ s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s)-s->min_x);
+ s->tl_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_X;
+ opt->title = SANE_TITLE_SCAN_TL_X;
+ opt->desc = SANE_DESC_SCAN_TL_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->tl_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* top-left y */
+ if(option==OPT_TL_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
+ s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s)-s->min_y);
+ s->tl_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_Y;
+ opt->title = SANE_TITLE_SCAN_TL_Y;
+ opt->desc = SANE_DESC_SCAN_TL_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->tl_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* bottom-right x */
+ if(option==OPT_BR_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s));
+ s->br_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_X;
+ opt->title = SANE_TITLE_SCAN_BR_X;
+ opt->desc = SANE_DESC_SCAN_BR_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->br_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* bottom-right y */
+ if(option==OPT_BR_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s));
+ s->br_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_Y;
+ opt->title = SANE_TITLE_SCAN_BR_Y;
+ opt->desc = SANE_DESC_SCAN_BR_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->br_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* page width */
+ if(option==OPT_PAGE_WIDTH){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->paper_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->paper_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_x);
+ s->paper_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_PAGE_WIDTH;
+ opt->title = SANE_TITLE_PAGE_WIDTH;
+ opt->desc = SANE_DESC_PAGE_WIDTH;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->paper_x_range;
+
+ if(s->has_adf){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->source == SOURCE_FLATBED){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* page height */
+ if(option==OPT_PAGE_HEIGHT){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->paper_y_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
+ s->paper_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y);
+ s->paper_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_PAGE_HEIGHT;
+ opt->title = SANE_TITLE_PAGE_HEIGHT;
+ opt->desc = "Specifies the height of the media, 0 will auto-detect.";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->paper_y_range;
+
+ if(s->has_adf){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->source == SOURCE_FLATBED){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* "Enhancement" group ------------------------------------------------- */
+ if(option==OPT_ENHANCEMENT_GROUP){
+ opt->name = SANE_NAME_ENHANCEMENT;
+ opt->title = SANE_TITLE_ENHANCEMENT;
+ opt->desc = SANE_DESC_ENHANCEMENT;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* brightness */
+ if(option==OPT_BRIGHTNESS){
+ opt->name = SANE_NAME_BRIGHTNESS;
+ opt->title = SANE_TITLE_BRIGHTNESS;
+ opt->desc = SANE_DESC_BRIGHTNESS;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->brightness_range;
+ s->brightness_range.quant=1;
+ s->brightness_range.min=-127;
+ s->brightness_range.max=127;
+
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* contrast */
+ if(option==OPT_CONTRAST){
+ opt->name = SANE_NAME_CONTRAST;
+ opt->title = SANE_TITLE_CONTRAST;
+ opt->desc = SANE_DESC_CONTRAST;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->contrast_range;
+ s->contrast_range.quant=1;
+ s->contrast_range.min=-127;
+ s->contrast_range.max=127;
+
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* gamma */
+ if(option==OPT_GAMMA){
+ opt->name = "gamma";
+ opt->title = "Gamma function exponent";
+ opt->desc = "Changes intensity of midtones";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->gamma_range;
+
+ /* value ranges from .3 to 5, should be log scale? */
+ s->gamma_range.quant=SANE_FIX(0.01);
+ s->gamma_range.min=SANE_FIX(0.3);
+ s->gamma_range.max=SANE_FIX(5);
+
+ /*if (s->num_download_gamma){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }*/
+
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*threshold*/
+ if(option==OPT_THRESHOLD){
+ opt->name = SANE_NAME_THRESHOLD;
+ opt->title = SANE_TITLE_THRESHOLD;
+ opt->desc = SANE_DESC_THRESHOLD;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->threshold_range;
+ s->threshold_range.min=0;
+ s->threshold_range.max=255;
+ s->threshold_range.quant=1;
+
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->mode != MODE_LINEART){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+
+ if(option==OPT_THRESHOLD_CURVE){
+ opt->name = "threshold-curve";
+ opt->title = "Threshold curve";
+ opt->desc = "Dynamic threshold curve, from light to dark, normally 50-65";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->threshold_curve_range;
+ s->threshold_curve_range.min=0;
+ s->threshold_curve_range.max=127;
+ s->threshold_curve_range.quant=1;
+
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->mode != MODE_LINEART){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* "Sensor" group ------------------------------------------------------ */
+ if(option==OPT_SENSOR_GROUP){
+ opt->name = SANE_NAME_SENSORS;
+ opt->title = SANE_TITLE_SENSORS;
+ opt->desc = SANE_DESC_SENSORS;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /*flaming hack to get scanimage to hide group*/
+ if (!s->has_adf)
+ opt->type = SANE_TYPE_BOOL;
+ }
+
+ if(option==OPT_SCAN_SW){
+ opt->name = SANE_NAME_SCAN;
+ opt->title = SANE_TITLE_SCAN;
+ opt->desc = SANE_DESC_SCAN;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_adf)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_HOPPER){
+ opt->name = SANE_NAME_PAGE_LOADED;
+ opt->title = SANE_TITLE_PAGE_LOADED;
+ opt->desc = SANE_DESC_PAGE_LOADED;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_adf)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_TOP){
+ opt->name = "top-edge";
+ opt->title = "Top edge";
+ opt->desc = "Paper is pulled partly into adf";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_adf)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_ADF_OPEN){
+ opt->name = SANE_NAME_COVER_OPEN;
+ opt->title = SANE_TITLE_COVER_OPEN;
+ opt->desc = SANE_DESC_COVER_OPEN;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_adf)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SLEEP){
+ opt->name = "power-save";
+ opt->title = "Power saving";
+ opt->desc = "Scanner in power saving mode";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_adf)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ return opt;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Int dummy = 0;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ if (option >= NUM_OPTIONS) {
+ DBG (5, "sane_control_option: %d too big\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: %d inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE) {
+ SANE_Word * val_p = (SANE_Word *) val;
+
+ DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ *val_p = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if(s->source == SOURCE_FLATBED){
+ strcpy (val, STRING_FLATBED);
+ }
+ else if(s->source == SOURCE_ADF_FRONT){
+ strcpy (val, STRING_ADFFRONT);
+ }
+ else if(s->source == SOURCE_ADF_BACK){
+ strcpy (val, STRING_ADFBACK);
+ }
+ else if(s->source == SOURCE_ADF_DUPLEX){
+ strcpy (val, STRING_ADFDUPLEX);
+ }
+ else{
+ DBG(5,"missing option val for source\n");
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if(s->mode == MODE_LINEART){
+ strcpy (val, STRING_LINEART);
+ }
+ else if(s->mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_X_RES:
+ *val_p = s->resolution_x;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_width);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_height);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *val_p = s->brightness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *val_p = s->contrast;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA:
+ *val_p = SANE_FIX(s->gamma);
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ *val_p = s->threshold;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD_CURVE:
+ *val_p = s->threshold_curve;
+ return SANE_STATUS_GOOD;
+
+ /* Sensor Group */
+ case OPT_SCAN_SW:
+ get_hardware_status(s);
+ *val_p = s->hw_scan_sw;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HOPPER:
+ get_hardware_status(s);
+ *val_p = s->hw_hopper;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TOP:
+ get_hardware_status(s);
+ *val_p = s->hw_top;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ADF_OPEN:
+ get_hardware_status(s);
+ *val_p = s->hw_adf_open;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SLEEP:
+ get_hardware_status(s);
+ *val_p = s->hw_sleep;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE) {
+ int tmp;
+ SANE_Word val_c;
+ SANE_Status status;
+
+ DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
+
+ if ( s->started ) {
+ DBG (5, "sane_control_option: cant set, device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (5, "sane_control_option: bad value\n");
+ return status;
+ }
+
+ /* may have been changed by constrain, so dont copy until now */
+ val_c = *(SANE_Word *)val;
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option) {
+
+ /* Mode Group */
+ case OPT_SOURCE:
+ if (!strcmp (val, STRING_ADFFRONT)) {
+ tmp = SOURCE_ADF_FRONT;
+ }
+ else if (!strcmp (val, STRING_ADFBACK)) {
+ tmp = SOURCE_ADF_BACK;
+ }
+ else if (!strcmp (val, STRING_ADFDUPLEX)) {
+ tmp = SOURCE_ADF_DUPLEX;
+ }
+ else{
+ tmp = SOURCE_FLATBED;
+ }
+
+ if (s->source == tmp)
+ return SANE_STATUS_GOOD;
+
+ s->source = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (!strcmp (val, STRING_LINEART)) {
+ tmp = MODE_LINEART;
+ }
+ else if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp == s->mode)
+ return SANE_STATUS_GOOD;
+
+ s->mode = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return change_params(s);
+
+ case OPT_X_RES:
+
+ if (s->resolution_x == val_c)
+ return SANE_STATUS_GOOD;
+
+ s->resolution_x = val_c;
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return change_params(s);
+
+ /* Geometry Group */
+ case OPT_TL_X:
+ if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ if (s->tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ if (s->br_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ if (s->br_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ if (s->page_width == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->page_width = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ if (s->page_height == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->page_height = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return change_params(s);
+
+ /* Enhancement Group */
+ case OPT_BRIGHTNESS:
+ s->brightness = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ s->contrast = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA:
+ s->gamma = SANE_UNFIX(val_c);
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ s->threshold = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD_CURVE:
+ s->threshold_curve = val_c;
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+
+ return SANE_STATUS_INVAL;
+}
+
+/* use height and width to initialize rest of transfer vals */
+static void
+update_transfer_totals(struct transfer * t)
+{
+ if (t->image == NULL) return;
+
+ t->total_bytes = t->line_stride * t->image->height;
+ t->rx_bytes = 0;
+ t->done = 0;
+}
+
+/* each model has various settings that differ based on X resolution */
+/* we hard-code the list (determined from usb snoops) here */
+struct model_res {
+ int model;
+ int x_res;
+ int y_res;
+ int usb_power;
+
+ int max_x;
+ int min_x;
+ int max_y;
+ int min_y;
+
+ int act_width; /* total data width output, in pixels per side (always 3 sides) */
+ int req_width; /* stride in pixels per side between color planes (always 3 sides) */
+ int head_width;
+ int pad_width;
+
+ int block_height;
+
+ int cal_width;
+ int cal_headwidth;
+ int cal_reqwidth;
+
+ unsigned char * sw_coarsecal;
+ unsigned char * sw_finecal;
+ unsigned char * sw_sendcal;
+
+ unsigned char * head_cal1;
+ unsigned char * head_cal2;
+ unsigned char * sw_scan;
+
+};
+
+static struct model_res settings[] = {
+
+ /*S300 AC*/
+/* model xres yres u mxx mnx mxy mny actw reqw hedw padw bh calw cal_hedw cal_reqw */
+ { MODEL_S300, 150, 150, 0, 1296, 32, 2662, 32, 4256, 1480, 1296, 184, 41, 8512, 2592, 2960,
+ setWindowCoarseCal_S300_150, setWindowFineCal_S300_150,
+ setWindowSendCal_S300_150, sendCal1Header_S300_150,
+ sendCal2Header_S300_150, setWindowScan_S300_150 },
+
+ { MODEL_S300, 225, 200, 0, 1944, 32, 3993, 32, 6144, 2100, 1944, 156, 28, 8192, 2592, 2800,
+ setWindowCoarseCal_S300_225, setWindowFineCal_S300_225,
+ setWindowSendCal_S300_225, sendCal1Header_S300_225,
+ sendCal2Header_S300_225, setWindowScan_S300_225 },
+
+ { MODEL_S300, 300, 300, 0, 2592, 32, 5324, 32, 8192, 2800, 2592, 208, 21, 8192, 2592, 2800,
+ setWindowCoarseCal_S300_300, setWindowFineCal_S300_300,
+ setWindowSendCal_S300_300, sendCal1Header_S300_300,
+ sendCal2Header_S300_300, setWindowScan_S300_300 },
+
+ { MODEL_S300, 600, 600, 0, 5184, 32, 10648, 32, 16064, 5440, 5184, 256, 10, 16064, 5184, 5440,
+ setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
+ setWindowSendCal_S300_600, sendCal1Header_S300_600,
+ sendCal2Header_S300_600, setWindowScan_S300_600 },
+
+ /*S300 USB*/
+/* model xres yres u mxx mnx mxy mny actw reqw hedw padw bh calw cal_hedw cal_reqw */
+ { MODEL_S300, 150, 150, 1, 1296, 32, 2662, 32, 7216, 2960, 1296, 1664, 24, 14432, 2592, 5920,
+ setWindowCoarseCal_S300_150_U, setWindowFineCal_S300_150_U,
+ setWindowSendCal_S300_150_U, sendCal1Header_S300_150_U,
+ sendCal2Header_S300_150_U, setWindowScan_S300_150_U },
+
+ { MODEL_S300, 225, 200, 1, 1944, 32, 3993, 32, 10584, 4320, 1944, 2376, 16, 14112, 2592, 5760,
+ setWindowCoarseCal_S300_225_U, setWindowFineCal_S300_225_U,
+ setWindowSendCal_S300_225_U, sendCal1Header_S300_225_U,
+ sendCal2Header_S300_225_U, setWindowScan_S300_225_U },
+
+ { MODEL_S300, 300, 300, 1, 2592, 32, 5324, 32, 15872, 6640, 2592, 4048, 11, 15872, 2592, 6640,
+ setWindowCoarseCal_S300_300_U, setWindowFineCal_S300_300_U,
+ setWindowSendCal_S300_300_U, sendCal1Header_S300_300_U,
+ sendCal2Header_S300_300_U, setWindowScan_S300_300_U },
+
+ { MODEL_S300, 600, 600, 1, 5184, 32, 10648, 32, 16064, 5440, 5184, 256, 10, 16064, 5184, 5440,
+ setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
+ setWindowSendCal_S300_600, sendCal1Header_S300_600,
+ sendCal2Header_S300_600, setWindowScan_S300_600 },
+
+ /*fi-60F*/
+/* model xres yres u mxx mnx mxy mny actw reqw hedw padw bh calw cal_hedw cal_reqw */
+ { MODEL_FI60F, 150, 150, 0, 648, 32, 875, 32, 1480, 632, 216, 416, 41, 1480, 216, 632,
+ setWindowCoarseCal_FI60F_150, setWindowFineCal_FI60F_150,
+ setWindowSendCal_FI60F_150, sendCal1Header_FI60F_150,
+ sendCal2Header_FI60F_150, setWindowScan_FI60F_150 },
+
+ { MODEL_FI60F, 300, 300, 0, 1296, 32, 1749, 32, 2400, 958, 432, 526, 72, 2400, 432, 958,
+ setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300,
+ setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300,
+ sendCal2Header_FI60F_300, setWindowScan_FI60F_300 },
+
+ { MODEL_FI60F, 600, 600, 0, 2592, 32, 3498, 32, 2848, 978, 864, 114, 61, 2848, 864, 978,
+ setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600,
+ setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600,
+ sendCal2Header_FI60F_600, setWindowScan_FI60F_600 },
+
+ { MODEL_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+};
+
+/*
+ * clean up scanner struct vals when user changes mode, res, etc
+ */
+static SANE_Status
+change_params(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int img_heads, img_pages, width;
+ int i=0;
+
+ DBG (10, "change_params: start\n");
+
+ do {
+ if(settings[i].model == s->model
+ && settings[i].x_res == s->resolution_x
+ && settings[i].usb_power == s->usb_power){
+
+ /*pull in closest y resolution*/
+ s->resolution_y = settings[i].y_res;
+
+ /*1200 dpi*/
+ s->max_x = settings[i].max_x * 1200/s->resolution_x;
+ s->min_x = settings[i].min_x * 1200/s->resolution_x;
+ s->max_y = settings[i].max_y * 1200/s->resolution_y;
+ s->min_y = settings[i].min_y * 1200/s->resolution_y;
+
+ s->page_width = s->max_x;
+ s->br_x = s->max_x;
+ s->br_y = s->max_y;
+
+ /*current dpi*/
+ s->setWindowCoarseCal = settings[i].sw_coarsecal;
+ s->setWindowCoarseCalLen = SET_WINDOW_LEN;
+
+ s->setWindowFineCal = settings[i].sw_finecal;
+ s->setWindowFineCalLen = SET_WINDOW_LEN;
+
+ s->setWindowSendCal = settings[i].sw_sendcal;
+ s->setWindowSendCalLen = SET_WINDOW_LEN;
+
+ s->sendCal1Header = settings[i].head_cal1;
+ s->sendCal1HeaderLen = 14;
+
+ s->sendCal2Header = settings[i].head_cal2;
+ s->sendCal2HeaderLen = 7;
+
+ s->setWindowScan = settings[i].sw_scan;
+ s->setWindowScanLen = SET_WINDOW_LEN;
+
+ break;
+ }
+ i++;
+ } while (settings[i].model);
+
+ if (!settings[i].model)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->model == MODEL_S300)
+ {
+ img_heads = 1; /* image width is the same as the plane width on the S300 */
+ img_pages = 2;
+ }
+ else /* (s->model == MODEL_FI60F) */
+ {
+ img_heads = 3; /* image width is 3* the plane width on the FI-60F */
+ img_pages = 1;
+ }
+
+ /* set up the transfer structs */
+ s->cal_image.plane_width = settings[i].cal_headwidth;
+ s->cal_image.plane_stride = settings[i].cal_reqwidth * 3;
+ s->cal_image.line_stride = settings[i].cal_width * 3;
+ s->cal_image.raw_data = NULL;
+ s->cal_image.image = NULL;
+
+ s->cal_data.plane_width = settings[i].cal_headwidth; /* width is the same, but there are 2 bytes per pixel component */
+ s->cal_data.plane_stride = settings[i].cal_reqwidth * 6;
+ s->cal_data.line_stride = settings[i].cal_width * 6;
+ s->cal_data.raw_data = NULL;
+ s->cal_data.image = &s->sendcal;
+
+ s->block_xfr.plane_width = settings[i].head_width;
+ s->block_xfr.plane_stride = settings[i].req_width * 3;
+ s->block_xfr.line_stride = settings[i].act_width * 3;
+ s->block_xfr.raw_data = NULL;
+ s->block_xfr.image = &s->block_img;
+
+ /* set up the block image used during scanning operation */
+ width = s->block_xfr.plane_width * img_heads;
+ s->block_img.width_pix = width;
+ s->block_img.width_bytes = width * 3;
+ s->block_img.height = settings[i].block_height;
+ s->block_img.pages = img_pages;
+ s->block_img.buffer = NULL;
+
+ /* set up the calibration image blocks */
+ width = s->cal_image.plane_width * img_heads;
+ s->coarsecal.width_pix = s->darkcal.width_pix = s->lightcal.width_pix = width;
+ s->coarsecal.width_bytes = s->darkcal.width_bytes = s->lightcal.width_bytes = width * 3;
+ s->coarsecal.height = 1;
+ s->darkcal.height = s->lightcal.height = 16;
+ s->coarsecal.pages = s->darkcal.pages = s->lightcal.pages = img_pages;
+ s->coarsecal.buffer = s->darkcal.buffer = s->lightcal.buffer = NULL;
+
+ /* set up the calibration data block */
+ width = s->cal_data.plane_width * img_heads;
+ s->sendcal.width_pix = width;
+ s->sendcal.width_bytes = width * 6; /* 2 bytes of cal data per pixel component */
+ s->sendcal.height = 1;
+ s->sendcal.pages = img_pages;
+ s->sendcal.buffer = NULL;
+
+ /* set up the fullscan parameters */
+ s->fullscan.width_bytes = s->block_xfr.line_stride;
+ if(s->source == SOURCE_FLATBED || !s->page_height)
+ {
+ /* flatbed and adf in autodetect always ask for all*/
+ s->fullscan.height = s->max_y * s->resolution_y / 1200;
+ }
+ else
+ {
+ /* adf with specified paper size requires padding (~1/2in) */
+ s->fullscan.height = (s->page_height+600) * s->resolution_y / 1200;
+ }
+
+ /* fill in front settings */
+ s->front.width_pix = s->block_img.width_pix;
+ switch (s->mode) {
+ case MODE_COLOR:
+ s->front.width_bytes = s->front.width_pix*3;
+ break;
+ case MODE_GRAYSCALE:
+ s->front.width_bytes = s->front.width_pix;
+ break;
+ default: /*binary*/
+ s->front.width_bytes = s->front.width_pix/8;
+ break;
+ }
+ /*output image might be taller than scan due to interpolation*/
+ s->front.height = s->fullscan.height * s->resolution_x / s->resolution_y;
+ s->front.pages = 1;
+ s->front.buffer = NULL;
+
+ /* back settings always same as front settings */
+ s->back.width_pix = s->front.width_pix;
+ s->back.width_bytes = s->front.width_bytes;
+ s->back.height = s->front.height;
+ s->back.pages = 1;
+ s->back.buffer = NULL;
+
+ /* dynamic threshold temp buffer, in gray */
+ s->dt.width_pix = s->front.width_pix;
+ s->dt.width_bytes = s->front.width_pix;
+ s->dt.height = 1;
+ s->dt.pages = 1;
+ s->dt.buffer = NULL;
+
+ /* set up the pointers to the page images in the page structs */
+ s->pages[SIDE_FRONT].image = &s->front;
+ s->pages[SIDE_BACK].image = &s->back;
+ s->pages[SIDE_FRONT].done = 0;
+ s->pages[SIDE_BACK].done = 0;
+
+ DBG (10, "change_params: finish\n");
+
+ return ret;
+}
+
+/* Function to build a lookup table (LUT), often
+ used by scanners to implement brightness/contrast/gamma
+ or by backends to speed binarization/thresholding
+
+ offset and slope inputs are -127 to +127
+
+ slope rotates line around central input/output val,
+ 0 makes horizontal line
+
+ pos zero neg
+ . x . . x
+ . x . . x
+ out . x .xxxxxxxxxxx . x
+ . x . . x
+ ....x....... ............ .......x....
+ in in in
+
+ offset moves line vertically, and clamps to output range
+ 0 keeps the line crossing the center of the table
+
+ high low
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+
+ out_min/max provide bounds on output values,
+ useful when building thresholding lut.
+ 0 and 255 are good defaults otherwise.
+ */
+static SANE_Status
+load_lut (unsigned char * lut,
+ int in_bits, int out_bits,
+ int out_min, int out_max,
+ int slope, int offset)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j;
+ double shift, rise;
+ int max_in_val = (1 << in_bits) - 1;
+ int max_out_val = (1 << out_bits) - 1;
+ unsigned char * lut_p = lut;
+
+ DBG (10, "load_lut: start\n");
+
+ /* slope is converted to rise per unit run:
+ * first [-127,127] to [-1,1]
+ * then multiply by PI/2 to convert to radians
+ * then take the tangent (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ rise = tan((double)slope/127 * M_PI/2) * max_out_val / max_in_val;
+
+ /* line must stay vertically centered, so figure
+ * out vertical offset at central input value */
+ shift = (double)max_out_val/2 - (rise*max_in_val/2);
+
+ /* convert the user offset setting to scale of output
+ * first [-127,127] to [-1,1]
+ * then to [-max_out_val/2,max_out_val/2]*/
+ shift += (double)offset / 127 * max_out_val / 2;
+
+ for(i=0;i<=max_in_val;i++){
+ j = rise*i + shift;
+
+ if(j<out_min){
+ j=out_min;
+ }
+ else if(j>out_max){
+ j=out_max;
+ }
+
+ *lut_p=j;
+ lut_p++;
+ }
+
+ hexdump(5, "load_lut: ", lut, max_in_val+1);
+
+ DBG (10, "load_lut: finish\n");
+ return ret;
+}
+
+/*
+ * @@ Section 4 - SANE scanning functions
+ */
+/*
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct scanner *s = (struct scanner *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ params->pixels_per_line = s->front.width_pix;
+ params->bytes_per_line = s->front.width_bytes;
+ if(!s->page_height){
+ params->lines = -1;
+ }
+ else{
+ params->lines = s->front.height;
+ }
+ params->last_frame = 1;
+
+ if (s->mode == MODE_COLOR) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ }
+ else if (s->mode == MODE_GRAYSCALE) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ }
+ else if (s->mode == MODE_LINEART) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 1;
+ }
+
+ DBG (15, "\tdepth %d\n", params->depth);
+ DBG (15, "\tlines %d\n", params->lines);
+ DBG (15, "\tpixels_per_line %d\n", params->pixels_per_line);
+ DBG (15, "\tbytes_per_line %d\n", params->bytes_per_line);
+
+ DBG (10, "sane_get_parameters: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ * FIXME: wont handle SOURCE_ADF_BACK
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = handle;
+ SANE_Status ret;
+ int i;
+
+ DBG (10, "sane_start: start\n");
+
+ /* set side marker on first page */
+ if(!s->started){
+ if(s->source == SOURCE_ADF_BACK){
+ s->side = SIDE_BACK;
+ }
+ else{
+ s->side = SIDE_FRONT;
+ }
+ }
+ /* if already running, duplex needs to switch sides */
+ else if(s->source == SOURCE_ADF_DUPLEX){
+ s->side = !s->side;
+ }
+
+ /* ingest paper with adf */
+ if( s->source == SOURCE_ADF_BACK || s->source == SOURCE_ADF_FRONT
+ || (s->source == SOURCE_ADF_DUPLEX && s->side == SIDE_FRONT) ){
+ ret = ingest(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to ingest\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+ }
+
+ /* first page requires buffers, etc */
+ if(!s->started){
+
+ DBG(15,"sane_start: first page\n");
+
+ s->started=1;
+
+ ret = teardown_buffers(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to teardown buffers\n");
+ sane_cancel((SANE_Handle)s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = change_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to change_params\n");
+ sane_cancel((SANE_Handle)s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = setup_buffers(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to setup buffers\n");
+ sane_cancel((SANE_Handle)s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = load_lut(s->dt_lut, 8, 8, 50, 205,
+ s->threshold_curve, s->threshold-127);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to load_lut for dt\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ ret = coarsecal(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to coarsecal\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ ret = finecal(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to finecal\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ ret = send_lut(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to send lut\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ ret = lamp(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to heat lamp\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ /*should this be between each page*/
+ ret = set_window(s,WINDOW_SCAN);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to set window\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ }
+
+ /* reset everything when starting any front, or just back */
+ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK){
+
+ DBG(15,"sane_start: reset counters\n");
+
+ /* reset scan */
+ s->fullscan.done = 0;
+ s->fullscan.rx_bytes = 0;
+ s->fullscan.total_bytes = s->fullscan.width_bytes * s->fullscan.height;
+
+ /* reset block */
+ update_transfer_totals(&s->block_xfr);
+
+ /* reset front and back page counters */
+ for (i = 0; i < 2; i++)
+ {
+ struct image *page_img = s->pages[i].image;
+ s->pages[i].bytes_total = page_img->width_bytes * page_img->height;
+ s->pages[i].bytes_scanned = 0;
+ s->pages[i].bytes_read = 0;
+ s->pages[i].done = 0;
+ }
+
+ ret = scan(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to start scan\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+ }
+ else{
+ DBG(15,"sane_start: back side\n");
+ }
+
+ DBG (10, "sane_start: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* the +8 on all the lengths is to makeup for potential block trailers */
+static SANE_Status
+setup_buffers(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "setup_buffers: start\n");
+
+ /* temporary cal data */
+ s->coarsecal.buffer = calloc (1,s->coarsecal.width_bytes * s->coarsecal.height * s->coarsecal.pages);
+ if(!s->coarsecal.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup coarse cal buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->darkcal.buffer = calloc (1,s->darkcal.width_bytes * s->darkcal.height * s->darkcal.pages);
+ if(!s->darkcal.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup fine cal buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->lightcal.buffer = calloc (1,s->lightcal.width_bytes * s->lightcal.height * s->lightcal.pages);
+ if(!s->lightcal.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup fine cal buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->sendcal.buffer = calloc (1,s->sendcal.width_bytes * s->sendcal.height * s->sendcal.pages);
+ if(!s->sendcal.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup send cal buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->cal_image.raw_data = calloc(1, s->cal_image.line_stride * 16 + 8); /* maximum 16 lines input for fine calibration */
+ if(!s->cal_image.raw_data){
+ DBG (5, "setup_buffers: ERROR: failed to setup calibration input raw data buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->cal_data.raw_data = calloc(1, s->cal_data.line_stride); /* only 1 line of data is sent */
+ if(!s->cal_data.raw_data){
+ DBG (5, "setup_buffers: ERROR: failed to setup calibration output raw data buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* grab up to 512K at a time */
+ s->block_img.buffer = calloc (1,s->block_img.width_bytes * s->block_img.height * s->block_img.pages);
+ if(!s->block_img.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup block image buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ s->block_xfr.raw_data = calloc(1, s->block_xfr.line_stride * s->block_img.height + 8);
+ if(!s->block_xfr.raw_data){
+ DBG (5, "setup_buffers: ERROR: failed to setup block raw data buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* one grayscale line for dynamic threshold */
+ s->dt.buffer = calloc (1,s->dt.width_bytes * s->dt.height * s->dt.pages);
+ if(!s->dt.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup dt buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* make image buffer to hold frontside data */
+ if(s->source != SOURCE_ADF_BACK){
+
+ s->front.buffer = calloc (1,s->front.width_bytes * s->front.height * s->front.pages);
+ if(!s->front.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup front buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ /* make image buffer to hold backside data */
+ if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
+
+ s->back.buffer = calloc (1,s->back.width_bytes * s->back.height * s->back.pages);
+ if(!s->back.buffer){
+ DBG (5, "setup_buffers: ERROR: failed to setup back buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ DBG (10, "setup_buffers: finish\n");
+ return ret;
+}
+
+/*
+ coarse calibration consists of:
+ 1. turn lamp off (d0)
+ 2. set window for single line of data (d1)
+ 3. get line (d2)
+ 4. update dark coarse cal (c6)
+ 5. return to #3 if not dark enough
+ 6. turn lamp on (d0)
+ 7. get line (d2)
+ 8. update light coarse cal (c6)
+ 9. return to #7 if not light enough
+*/
+
+static SANE_Status
+coarsecal(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ size_t cmdLen = 2;
+ unsigned char cmd[2];
+
+ size_t statLen = 1;
+ unsigned char stat[1];
+
+ size_t payLen = 28;
+ unsigned char pay[28];
+
+ int try_count, cal_good[2], x, i, j;
+ int param[2], zcount[2], high_param[2], low_param[2], avg[2], maxval[2];
+ int rgb_avg[2][3], rgb_hicount[2][3];
+
+ DBG (10, "coarsecal: start\n");
+
+ if(s->model == MODEL_S300){
+ memcpy(pay,coarseCalData_S300,payLen);
+ }
+ else{
+ memcpy(pay,coarseCalData_FI60F,payLen);
+ }
+
+ /* ask for 1 line */
+ ret = set_window(s, WINDOW_COARSECAL);
+ if(ret){
+ DBG (5, "coarsecal: error sending setwindow\n");
+ return ret;
+ }
+
+ /* dark cal, lamp off */
+ ret = lamp(s,0);
+ if(ret){
+ DBG (5, "coarsecal: error lamp off\n");
+ return ret;
+ }
+
+ try_count = 8;
+ param[0] = 63;
+ param[1] = 63;
+ low_param[0] = low_param[1] = -64; /* The S300 will accept coarse offsets from -128 to 127 */
+ high_param[0] = high_param[1] = 63; /* By our range is limited to converge faster */
+ cal_good[0] = cal_good[1] = 0;
+
+ while (try_count > 0){
+ try_count--;
+
+ /* update the coarsecal payload to use our new dark offset parameters */
+ if (s->model == MODEL_S300)
+ {
+ pay[5] = param[0];
+ pay[7] = param[1];
+ }
+ else /* (s->model == MODEL_FI60F) */
+ {
+ pay[5] = param[0];
+ pay[7] = param[0];
+ pay[9] = param[0];
+ }
+
+ /* send coarse cal (c6) */
+ cmd[0] = 0x1b;
+ cmd[1] = 0xc6;
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "coarsecal: error sending c6 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "coarsecal: cmd bad c6 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*send coarse cal payload*/
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ pay, payLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "coarsecal: error sending c6 payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "coarsecal: c6 payload bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(15, "coarsecal offset: parameter front: %i back: %i\n", param[0], param[1]);
+
+ /* send scan d2 command */
+ cmd[0] = 0x1b;
+ cmd[1] = 0xd2;
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "coarsecal: error sending d2 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "coarsecal: cmd bad d2 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->cal_image.image = &s->coarsecal;
+ update_transfer_totals(&s->cal_image);
+
+ while(!s->cal_image.done){
+ ret = read_from_scanner(s,&s->cal_image);
+ if(ret){
+ DBG (5, "coarsecal: cant read from scanner\n");
+ return ret;
+ }
+ }
+ /* convert the raw data into normal packed pixel data */
+ descramble_raw(s, &s->cal_image);
+
+ /* gather statistics: count the proportion of 0-valued pixels */
+ /* since the lamp is off, there's no point in looking at the green or blue data - they're all from the same sensor anyway */
+ zcount[0] = zcount[1] = 0;
+ avg[0] = avg[1] = 0;
+ maxval[0] = maxval[1] = 0;
+ for (j = 0; j < s->coarsecal.pages; j++)
+ {
+ int page_offset = j * s->coarsecal.width_bytes * s->coarsecal.height;
+ for (x = 0; x < s->coarsecal.width_bytes; x++)
+ {
+ int val = s->coarsecal.buffer[page_offset + x];
+ avg[j] += val;
+ if (val == 0) zcount[j]++;
+ if (val > maxval[j]) maxval[j] = val;
+ }
+ }
+ /* convert the zero counts from a pixel count to a proportion in tenths of a percent */
+ for (j = 0; j < s->coarsecal.pages; j++)
+ {
+ avg[j] /= s->coarsecal.width_bytes;
+ zcount[j] = zcount[j] * 1000 / s->coarsecal.width_bytes;
+ }
+ DBG(15, "coarsecal offset: average pixel values front: %i back: %i\n", avg[0], avg[1]);
+ DBG(15, "coarsecal offset: maximum pixel values front: %i back: %i\n", maxval[0], maxval[1]);
+ DBG(15, "coarsecal offset: 0-valued pixel count front: %f%% back: %f%%\n", zcount[0] / 10.0f, zcount[1] / 10.0f);
+
+ /* check the values, adjust parameters if they are not within the target range */
+ for (j = 0; j < s->coarsecal.pages; j++)
+ {
+ if (!cal_good[j])
+ {
+ if (avg[j] > COARSE_OFFSET_TARGET)
+ {
+ high_param[j] = param[j];
+ param[j] = (low_param[j] + high_param[j]) / 2;
+ }
+ else if (avg[j] < COARSE_OFFSET_TARGET)
+ {
+ low_param[j] = param[j];
+ param[j] = (low_param[j] + high_param[j]) / 2;
+ }
+ else cal_good[j] = 1;
+ }
+ }
+ if (cal_good[0] + cal_good[1] == s->coarsecal.pages) break;
+
+ } /* continue looping for up to 8 tries */
+
+ /* light cal, lamp on */
+ ret = lamp(s,1);
+ if(ret){
+ DBG (5, "coarsecal: error lamp on\n");
+ return ret;
+ }
+
+ try_count = 8;
+ param[0] = pay[11];
+ param[1] = pay[13];
+ low_param[0] = low_param[1] = 0;
+ high_param[0] = high_param[1] = 63;
+ cal_good[0] = cal_good[1] = 0;
+
+ while (try_count > 0){
+ try_count--;
+
+ /* send coarse cal (c6) */
+ cmd[0] = 0x1b;
+ cmd[1] = 0xc6;
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "coarsecal: error sending c6 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "coarsecal: cmd bad c6 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*send coarse cal payload*/
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ pay, payLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "coarsecal: error sending c6 payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "coarsecal: c6 payload bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(15, "coarsecal gain: parameter front: %i back: %i\n", param[0], param[1]);
+
+ /* send scan d2 command */
+ cmd[0] = 0x1b;
+ cmd[1] = 0xd2;
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "coarsecal: error sending d2 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "coarsecal: cmd bad d2 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->cal_image.image = &s->coarsecal;
+ update_transfer_totals(&s->cal_image);
+
+ while(!s->cal_image.done){
+ ret = read_from_scanner(s,&s->cal_image);
+ if(ret){
+ DBG (5, "coarsecal: cant read from scanner\n");
+ return ret;
+ }
+ }
+ /* convert the raw data into normal packed pixel data */
+ descramble_raw(s, &s->cal_image);
+
+ /* gather statistics: count the proportion of 255-valued pixels in each color channel */
+ /* count the average pixel value in each color channel */
+ for (i = 0; i < s->coarsecal.pages; i++)
+ for (j = 0; j < 3; j++)
+ rgb_avg[i][j] = rgb_hicount[i][j] = 0;
+ for (i = 0; i < s->coarsecal.pages; i++)
+ {
+ for (x = 0; x < s->coarsecal.width_pix; x++)
+ {
+ /* get color channel values and count of pixels pegged at 255 */
+ unsigned char *rgbpix = s->coarsecal.buffer + (i * s->coarsecal.width_bytes * s->coarsecal.height) + x * 3;
+ for (j = 0; j < 3; j++)
+ {
+ rgb_avg[i][j] += rgbpix[j];
+ if (rgbpix[j] == 255)
+ rgb_hicount[i][j]++;
+ }
+ }
+ }
+ /* apply the color correction factors to the averages */
+ for (i = 0; i < s->coarsecal.pages; i++)
+ for (j = 0; j < 3; j++)
+ rgb_avg[i][j] *= white_factor[j];
+ /* set the gain so that none of the color channels are clipping, ie take the highest channel values */
+ for (i = 0; i < s->coarsecal.pages; i++)
+ {
+ avg[i] = MAX3(rgb_avg[i][0], rgb_avg[i][1], rgb_avg[i][2]) / s->coarsecal.width_pix;
+ for (j = 0; j < 3; j++)
+ rgb_avg[i][j] /= s->coarsecal.width_pix;
+ }
+ /* convert the 255-counts from a pixel count to a proportion in tenths of a percent */
+ for (i = 0; i < s->coarsecal.pages; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ rgb_hicount[i][j] = rgb_hicount[i][j] * 1000 / s->coarsecal.width_pix;
+ }
+ zcount[i] = MAX3(rgb_hicount[i][0], rgb_hicount[i][1], rgb_hicount[i][2]);
+ }
+ DBG(15, "coarsecal gain: average RGB values front: (%i,%i,%i) back: (%i,%i,%i)\n",
+ rgb_avg[0][0], rgb_avg[0][1], rgb_avg[0][2], rgb_avg[1][0], rgb_avg[1][1], rgb_avg[1][2]);
+ DBG(15, "coarsecal gain: 255-valued pixel count front: (%g,%g,%g) back: (%g,%g,%g)\n",
+ rgb_hicount[0][0]/10.0f, rgb_hicount[0][1]/10.0f, rgb_hicount[0][2]/10.0f,
+ rgb_hicount[1][0]/10.0f, rgb_hicount[1][1]/10.0f, rgb_hicount[1][2]/10.0f);
+
+ /* check the values, adjust parameters if they are not within the target range */
+ for (x = 0; x < s->coarsecal.pages; x++)
+ {
+ if (!cal_good[x])
+ {
+ if (zcount[x] > 9 || avg[x] > coarse_gain_max[x])
+ {
+ high_param[x] = param[x];
+ param[x] = (low_param[x] + high_param[x]) / 2;
+ }
+ else if (avg[x] < coarse_gain_min[x])
+ {
+ low_param[x] = param[x];
+ param[x] = (low_param[x] + high_param[x]) / 2;
+ }
+ else cal_good[x] = 1;
+ }
+ }
+ if (cal_good[0] + cal_good[1] == s->coarsecal.pages) break;
+
+ /* update the coarsecal payload to use the new gain parameters */
+ if (s->model == MODEL_S300)
+ {
+ pay[11] = param[0];
+ pay[13] = param[1];
+ }
+ else /* (s->model == MODEL_FI60F) */
+ {
+ pay[11] = param[0];
+ pay[13] = param[0];
+ pay[15] = param[0];
+ }
+ }
+
+ DBG (10, "coarsecal: finish\n");
+ return ret;
+}
+
+static SANE_Status
+finecal_send_cal(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ size_t cmdLen = 2;
+ unsigned char cmd[2];
+
+ size_t statLen = 1;
+ unsigned char stat[2];
+
+ int i, j, k;
+ unsigned short *p_out, *p_in = (unsigned short *) s->sendcal.buffer;
+ int planes = (s->model == MODEL_S300) ? 2 : 3;
+
+ /* scramble the raster buffer data into scanner raw format */
+ memset(s->cal_data.raw_data, 0, s->cal_data.line_stride);
+ for (i = 0; i < planes; i++)
+ for (j = 0; j < s->cal_data.plane_width; j++)
+ for (k = 0; k < 3; k++)
+ {
+ p_out = (unsigned short *) (s->cal_data.raw_data + k * s->cal_data.plane_stride + j * 6 + i * 2);
+ *p_out = *p_in++; /* dark offset, gain */
+ }
+
+ ret = set_window(s, WINDOW_SENDCAL);
+ if(ret){
+ DBG (5, "finecal_send_cal: error sending setwindow\n");
+ return ret;
+ }
+
+ /*first unknown cal block*/
+ cmd[0] = 0x1b;
+ cmd[1] = 0xc3;
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "finecal_send_cal: error sending c3 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "finecal_send_cal: cmd bad c3 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*send header*/
+ /*send payload*/
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ s->sendCal1Header, s->sendCal1HeaderLen,
+ s->cal_data.raw_data, s->cal_data.line_stride,
+ stat, &statLen
+ );
+
+ if(ret){
+ DBG (5, "finecal_send_cal: error sending c3 payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "finecal_send_cal: payload bad c3 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*second unknown cal block*/
+ cmd[1] = 0xc4;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+
+ if(ret){
+ DBG (5, "finecal_send_cal: error sending c4 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "finecal_send_cal: cmd bad c4 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*send header*/
+ /*send payload*/
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ s->sendCal2Header, s->sendCal2HeaderLen,
+ s->cal_data.raw_data, s->cal_data.line_stride,
+ stat, &statLen
+ );
+
+ if(ret){
+ DBG (5, "finecal_send_cal: error sending c4 payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "finecal_send_cal: payload bad c4 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return ret;
+}
+
+static SANE_Status
+finecal_get_line(struct scanner *s, struct image *img)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ size_t cmdLen = 2;
+ unsigned char cmd[2];
+
+ size_t statLen = 1;
+ unsigned char stat[2];
+
+ int round_offset = img->height / 2;
+ int i, j, k;
+
+ /* ask for 16 lines */
+ ret = set_window(s, WINDOW_FINECAL);
+ if(ret){
+ DBG (5, "finecal_get_line: error sending setwindowcal\n");
+ return ret;
+ }
+
+ /* send scan d2 command */
+ cmd[0] = 0x1b;
+ cmd[1] = 0xd2;
+ stat[0] = 0;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "finecal_get_line: error sending d2 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "finecal_get_line: cmd bad d2 status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->cal_image.image = img;
+ update_transfer_totals(&s->cal_image);
+
+ while(!s->cal_image.done){
+ ret = read_from_scanner(s,&s->cal_image);
+ if(ret){
+ DBG (5, "finecal_get_line: cant read from scanner\n");
+ return ret;
+ }
+ }
+ /* convert the raw data into normal packed pixel data */
+ descramble_raw(s, &s->cal_image);
+
+ /* average the columns of pixels together and put the results in the top line(s) */
+ for (i = 0; i < img->pages; i++)
+ {
+ unsigned char *linepix = img->buffer + i * img->width_bytes * img->height;
+ unsigned char *avgpix = img->buffer + i * img->width_bytes;
+ for (j = 0; j < img->width_bytes; j++)
+ {
+ int total = 0;
+
+ for (k = 0; k < img->height; k++)
+ total += linepix[j + k * img->width_bytes];
+
+ avgpix[j] = (total + round_offset) / img->height;
+ }
+ }
+ return ret;
+}
+
+/* roundf() is c99, so we provide our own, though this version wont return -0 */
+static float
+round2(float x)
+{
+ return (float)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5);
+}
+
+static SANE_Status
+finecal(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ const int max_pages = (s->model == MODEL_S300 ? 2 : 1);
+ int gain_delta = 0xff - 0xbf;
+ float *gain_slope, *last_error;
+ int i, j, k, idx, try_count, cal_good;
+
+ DBG (10, "finecal: start\n");
+
+ /* set fine dark offset to 0 and fix all fine gains to lowest parameter (0xFF) */
+ for (i = 0; i < s->sendcal.width_bytes * s->sendcal.pages / 2; i++)
+ {
+ s->sendcal.buffer[i*2] = 0;
+ s->sendcal.buffer[i*2+1] = 0xff;
+ }
+ ret = finecal_send_cal(s);
+ if(ret) return ret;
+
+ /* grab rows with lamp on */
+ ret = lamp(s,1);
+ if(ret){
+ DBG (5, "finecal: error lamp on\n");
+ return ret;
+ }
+
+ /* read the low-gain average of 16 lines */
+ ret = finecal_get_line(s, &s->darkcal);
+ if(ret) return ret;
+
+ /* set fine dark offset to 0 and fine gain to a fixed higher-gain parameter (0xBF) */
+ for (i = 0; i < s->sendcal.width_bytes * s->sendcal.pages / 2; i++)
+ {
+ s->sendcal.buffer[i*2] = 0;
+ s->sendcal.buffer[i*2+1] = 0xbf;
+ }
+ ret = finecal_send_cal(s);
+ if(ret) return ret;
+
+ /* read the high-gain average of 16 lines */
+ ret = finecal_get_line(s, &s->lightcal);
+ if(ret) return ret;
+
+ /* calculate the per pixel slope of pixel value delta over gain delta */
+ gain_slope = malloc(s->lightcal.width_bytes * s->lightcal.pages * sizeof(float));
+ if (!gain_slope)
+ return SANE_STATUS_NO_MEM;
+ idx = 0;
+ for (i = 0; i < s->lightcal.pages; i++)
+ {
+ for (j = 0; j < s->lightcal.width_pix; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ int value_delta = s->lightcal.buffer[idx] - s->darkcal.buffer[idx];
+ /* limit this slope to 1 or less, to avoid overshoot if the lightcal ref input is clipped at 255 */
+ if (value_delta < gain_delta)
+ gain_slope[idx] = -1.0;
+ else
+ gain_slope[idx] = (float) -gain_delta / value_delta;
+ idx++;
+ }
+ }
+ }
+
+ /* keep track of the last iteration's pixel error. If we overshoot, we can reduce the value of the gain slope */
+ last_error = malloc(s->lightcal.width_bytes * s->lightcal.pages * sizeof(float));
+ if (!last_error)
+ {
+ free(gain_slope);
+ return SANE_STATUS_NO_MEM;
+ }
+ for (i = 0; i < s->lightcal.width_bytes * s->lightcal.pages; i++)
+ last_error[i] = 0.0;
+
+ /* fine calibration feedback loop */
+ try_count = 8;
+ while (try_count > 0)
+ {
+ int min_value[2][3], max_value[2][3];
+ float avg_value[2][3], variance[2][3];
+ int high_pegs = 0, low_pegs = 0;
+ try_count--;
+
+ /* clear statistics arrays */
+ for (i = 0; i < max_pages; i++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ min_value[i][k] = 0xff;
+ max_value[i][k] = 0;
+ avg_value[i][k] = 0;
+ variance[i][k] = 0;
+ }
+ }
+
+ /* gather statistics and calculate new fine gain parameters based on observed error and the value/gain slope */
+ idx = 0;
+ for (i = 0; i < max_pages; i++)
+ {
+ for (j = 0; j < s->lightcal.width_pix; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ int pixvalue = s->lightcal.buffer[idx];
+ float pixerror = (fine_gain_target[i] * white_factor[k] - pixvalue);
+ int oldgain = s->sendcal.buffer[idx * 2 + 1];
+ int newgain;
+ /* if we overshot the last correction, reduce the gain_slope */
+ if (pixerror * last_error[idx] < 0.0)
+ gain_slope[idx] *= 0.75;
+ last_error[idx] = pixerror;
+ /* set the new gain */
+ newgain = oldgain + (int) round2(pixerror * gain_slope[idx]);
+ if (newgain < 0)
+ {
+ low_pegs++;
+ s->sendcal.buffer[idx * 2 + 1] = 0;
+ }
+ else if (newgain > 0xff)
+ {
+ high_pegs++;
+ s->sendcal.buffer[idx * 2 + 1] = 0xff;
+ }
+ else
+ s->sendcal.buffer[idx * 2 + 1] = newgain;
+ /* update statistics */
+ if (pixvalue < min_value[i][k]) min_value[i][k] = pixvalue;
+ if (pixvalue > max_value[i][k]) max_value[i][k] = pixvalue;
+ avg_value[i][k] += pixerror;
+ variance[i][k] += (pixerror * pixerror);
+ idx++;
+ }
+ }
+ }
+ /* finish the statistics calculations */
+ cal_good = 1;
+ for (i = 0; i < max_pages; i++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ float sum = avg_value[i][k];
+ float sum2 = variance[i][k];
+ avg_value[i][k] = sum / s->lightcal.width_pix;
+ variance[i][k] = ((sum2 - (sum * sum / s->lightcal.width_pix)) / s->lightcal.width_pix);
+ /* if any color channel is too far out of whack, set cal_good to 0 so we'll iterate again */
+ if (fabs(avg_value[i][k]) > 1.0 || variance[i][k] > 3.0)
+ cal_good = 0;
+ }
+ }
+
+ /* print debug info */
+ DBG (15, "finecal: -------------------- Gain\n");
+ DBG (15, "finecal: RGB Average Error - Front: (%.1f,%.1f,%.1f) - Back: (%.1f,%.1f,%.1f)\n",
+ avg_value[0][0], avg_value[0][1], avg_value[0][2], avg_value[1][0], avg_value[1][1], avg_value[1][2]);
+ DBG (15, "finecal: RGB Maximum - Front: (%i,%i,%i) - Back: (%i,%i,%i)\n",
+ max_value[0][0], max_value[0][1], max_value[0][2], max_value[1][0], max_value[1][1], max_value[1][2]);
+ DBG (15, "finecal: RGB Minimum - Front: (%i,%i,%i) - Back: (%i,%i,%i)\n",
+ min_value[0][0], min_value[0][1], min_value[0][2], min_value[1][0], min_value[1][1], min_value[1][2]);
+ DBG (15, "finecal: Variance - Front: (%.1f,%.1f,%.1f) - Back: (%.1f,%.1f,%.1f)\n",
+ variance[0][0], variance[0][1], variance[0][2], variance[1][0], variance[1][1], variance[1][2]);
+ DBG (15, "finecal: Pegged gain parameters - High (0xff): %i - Low (0): %i\n", high_pegs, low_pegs);
+
+ /* break out of the loop if our calibration is done */
+ if (cal_good) break;
+
+ /* send the new calibration and read a new line */
+ ret = finecal_send_cal(s);
+ if(ret) { free(gain_slope); free(last_error); return ret; }
+ ret = finecal_get_line(s, &s->lightcal);
+ if(ret) { free(gain_slope); free(last_error); return ret; }
+ }
+
+ /* release the memory for the reference slope data */
+ free(gain_slope);
+ free(last_error);
+
+ DBG (10, "finecal: finish\n");
+ return ret;
+}
+
+static SANE_Status
+lamp(struct scanner *s, unsigned char set)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ unsigned char cmd[2];
+ size_t cmdLen = 2;
+ unsigned char stat[1];
+ size_t statLen = 1;
+
+ DBG (10, "lamp: start (%d)\n", set);
+
+ /*send cmd*/
+ cmd[0] = 0x1b;
+ cmd[1] = 0xd0;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "lamp: error sending cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "lamp: cmd bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*send payload*/
+ cmd[0] = set;
+ cmdLen = 1;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "lamp: error sending payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "lamp: payload bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (10, "lamp: finish\n");
+ return ret;
+}
+
+static SANE_Status
+set_window(struct scanner *s, int window)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[] = {0x1b, 0xd1};
+ size_t cmdLen = sizeof(cmd);
+ unsigned char stat[] = {0};
+ size_t statLen = sizeof(stat);
+ unsigned char * payload;
+ size_t paylen = SET_WINDOW_LEN;
+
+ DBG (10, "set_window: start, window %d\n",window);
+
+ switch (window) {
+ case WINDOW_COARSECAL:
+ payload = s->setWindowCoarseCal;
+ paylen = s->setWindowCoarseCalLen;
+ break;
+ case WINDOW_FINECAL:
+ payload = s->setWindowFineCal;
+ paylen = s->setWindowFineCalLen;
+ break;
+ case WINDOW_SENDCAL:
+ payload = s->setWindowSendCal;
+ paylen = s->setWindowSendCalLen;
+ break;
+ case WINDOW_SCAN:
+ payload = s->setWindowScan;
+ paylen = s->setWindowScanLen;
+ set_SW_ypix(payload,s->fullscan.height);
+ break;
+ default:
+ DBG (5, "set_window: unknown window\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*send cmd*/
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "set_window: error sending cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "set_window: cmd bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*send payload*/
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ payload, paylen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "set_window: error sending payload\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "set_window: payload bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (10, "set_window: finish\n");
+ return ret;
+}
+
+/* instead of internal brightness/contrast/gamma
+ scanners uses 12bit x 12bit LUT
+ default is linear table of slope 1
+ brightness and contrast inputs are -127 to +127
+
+ contrast rotates slope of line around central input val
+
+ high low
+ . x .
+ . x . xx
+ out . x . xxxxxxxx
+ . x xx
+ ....x....... ............
+ in in
+
+ then brightness moves line vertically, and clamps to 8bit
+
+ bright dark
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+ */
+static SANE_Status
+send_lut (struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[] = {0x1b, 0xc5};
+ size_t cmdLen = 2;
+ unsigned char stat[1];
+ size_t statLen = 1;
+ unsigned char out[0x6000];
+ size_t outLen = 0x6000;
+
+ int i, j;
+ double b, slope, offset;
+ int width = outLen / 6; /* 3 colors, 2 bytes */
+ int height = width; /* square table */
+
+ DBG (10, "send_lut: start\n");
+
+ /* contrast is converted to a slope [0,90] degrees:
+ * first [-127,127] to [0,254] then to [0,1]
+ * then multiply by PI/2 to convert to radians
+ * then take the tangent to get slope (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ slope = tan(((double)s->contrast+127)/254 * M_PI/2);
+
+ /* contrast slope must stay centered, so figure
+ * out vertical offset at central input value */
+ offset = height/2 - slope*width/2;
+
+ /* convert the user brightness setting (-127 to +127)
+ * into a scale that covers the range required
+ * to slide the contrast curve entirely off the table */
+ b = ((double)s->brightness/127) * (slope*(width-1) + offset);
+
+ DBG (15, "send_lut: %d %f %d %f %f\n", s->brightness, b,
+ s->contrast, slope, offset);
+
+ for(i=0;i<width;i++){
+ j=slope*i + offset + b;
+
+ if(j<0){
+ j=0;
+ }
+
+ if(j>(height-1)){
+ j=height-1;
+ }
+
+ /*first table, le order*/
+ out[i*2] = j & 0xff;
+ out[i*2+1] = (j >> 8) & 0x0f;
+
+ /*second table, le order*/
+ out[width*2 + i*2] = j & 0xff;
+ out[width*2 + i*2+1] = (j >> 8) & 0x0f;
+
+ /*third table, le order*/
+ out[width*4 + i*2] = j & 0xff;
+ out[width*4 + i*2+1] = (j >> 8) & 0x0f;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "send_lut: error sending cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "send_lut: cmd bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ statLen = 1;
+ ret = do_cmd(
+ s, 0,
+ out, outLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "send_lut: error sending out\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "send_lut: out bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (10, "send_lut: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+get_hardware_status (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "get_hardware_status: start\n");
+
+ /* only run this once every second */
+ if (s->last_ghs < time(NULL)) {
+
+ unsigned char cmd[2];
+ size_t cmdLen = sizeof(cmd);
+ unsigned char pay[4];
+ size_t payLen = sizeof(pay);
+
+ DBG (15, "get_hardware_status: running\n");
+
+ cmd[0] = 0x1b;
+ cmd[1] = 0x33;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ pay, &payLen
+ );
+ if(ret){
+ DBG (5, "get_hardware_status: error sending cmd\n");
+ return ret;
+ }
+
+ hexdump(5,"ghspayload: ", pay, payLen);
+
+ s->last_ghs = time(NULL);
+
+ s->hw_top = ((pay[0] >> 7) & 0x01);
+ s->hw_hopper = !((pay[0] >> 6) & 0x01);
+ s->hw_adf_open = ((pay[0] >> 5) & 0x01);
+
+ s->hw_sleep = ((pay[1] >> 7) & 0x01);
+ s->hw_scan_sw = ((pay[1] >> 0) & 0x01);
+ }
+
+ DBG (10, "get_hardware_status: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+ingest(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i;
+
+ unsigned char cmd[2];
+ size_t cmdLen = sizeof(cmd);
+ unsigned char stat[1];
+ size_t statLen = sizeof(stat);
+ unsigned char pay[2];
+ size_t payLen = sizeof(pay);
+
+ DBG (10, "ingest: start\n");
+
+ for(i=0;i<5;i++){
+
+ /*send paper load cmd*/
+ cmd[0] = 0x1b;
+ cmd[1] = 0xd4;
+ statLen = 1;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "ingest: error sending cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "ingest: cmd bad status? %d\n",stat[0]);
+ continue;
+ }
+
+ /*send payload*/
+ statLen = 1;
+ payLen = 1;
+ pay[0] = 1;
+
+ ret = do_cmd(
+ s, 0,
+ pay, payLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "ingest: error sending payload\n");
+ return ret;
+ }
+ if(stat[0] == 6){
+ DBG (5, "ingest: found paper?\n");
+ break;
+ }
+ else if(stat[0] == 0x15 || stat[0] == 0){
+ DBG (5, "ingest: no paper?\n");
+ ret=SANE_STATUS_NO_DOCS;
+ continue;
+ }
+ else{
+ DBG (5, "ingest: payload bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ DBG (10, "ingest: finish\n");
+ return ret;
+}
+
+static SANE_Status
+scan(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ unsigned char cmd[] = {0x1b, 0xd2};
+ size_t cmdLen = 2;
+ unsigned char stat[1];
+ size_t statLen = 1;
+
+ DBG (10, "scan: start\n");
+
+ if(s->model == MODEL_S300){
+ cmd[1] = 0xd6;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "scan: error sending cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "scan: cmd bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (10, "scan: finish\n");
+
+ return ret;
+}
+
+/*
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status ret=SANE_STATUS_GOOD;
+ struct page * page;
+
+ DBG (10, "sane_read: start si:%d len:%d max:%d\n",s->side,*len,max_len);
+
+ *len = 0;
+
+ /* cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: call sane_start first\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ page = &s->pages[s->side];
+
+ /* have sent all of current buffer */
+ if(page->done){
+ DBG (10, "sane_read: returning eof\n");
+ return SANE_STATUS_EOF;
+ }
+
+ /* scan not finished, get more into block buffer */
+ if(!s->fullscan.done)
+ {
+ /* block buffer currently empty, clean up */
+ if(!s->block_xfr.rx_bytes)
+ {
+ /* block buffer bigger than remainder of scan, shrink block */
+ int remainTotal = s->fullscan.total_bytes - s->fullscan.rx_bytes;
+ if(remainTotal < s->block_xfr.total_bytes)
+ {
+ DBG (15, "sane_read: shrinking block to %lu\n", (unsigned long)remainTotal);
+ s->block_xfr.total_bytes = remainTotal;
+ }
+ /* send d3 cmd for S300 */
+ if(s->model == MODEL_S300)
+ {
+ unsigned char cmd[] = {0x1b, 0xd3};
+ size_t cmdLen = 2;
+ unsigned char stat[1];
+ size_t statLen = 1;
+
+ DBG (15, "sane_read: d3\n");
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ stat, &statLen
+ );
+ if(ret){
+ DBG (5, "sane_read: error sending d3 cmd\n");
+ return ret;
+ }
+ if(stat[0] != 6){
+ DBG (5, "sane_read: cmd bad status?\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ ret = read_from_scanner(s, &s->block_xfr);
+ if(ret){
+ DBG (5, "sane_read: cant read from scanner\n");
+ return ret;
+ }
+
+ /* block filled, copy to front/back */
+ if(s->block_xfr.done)
+ {
+ DBG (15, "sane_read: block buffer full\n");
+
+ /* convert the raw data into normal packed pixel data */
+ descramble_raw(s, &s->block_xfr);
+
+ s->block_xfr.done = 0;
+
+ /* get the 0x43 cmd for the S300 */
+ if(s->model == MODEL_S300){
+
+ unsigned char cmd[] = {0x1b, 0x43};
+ size_t cmdLen = 2;
+ unsigned char in[10];
+ size_t inLen = 10;
+
+ ret = do_cmd(
+ s, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ hexdump(15, "cmd 43: ", in, inLen);
+
+ if(ret){
+ DBG (5, "sane_read: error sending 43 cmd\n");
+ return ret;
+ }
+
+ /*copy backside data into buffer*/
+ if( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK )
+ ret = copy_block_to_page(s, SIDE_BACK);
+
+ /*copy frontside data into buffer*/
+ if( s->source != SOURCE_ADF_BACK )
+ ret = copy_block_to_page(s, SIDE_FRONT);
+
+ if(ret){
+ DBG (5, "sane_read: cant copy to front/back\n");
+ return ret;
+ }
+
+ s->fullscan.rx_bytes += s->block_xfr.rx_bytes;
+
+ /* autodetect mode, check for change length */
+ if( s->source != SOURCE_FLATBED && !s->page_height ){
+ int get = (in[6] << 8) | in[7];
+
+ /*always have to get full blocks*/
+ if(get % s->block_img.height){
+ get += s->block_img.height - (get % s->block_img.height);
+ }
+
+ if(get < s->fullscan.height){
+ DBG (15, "sane_read: paper out? %d\n",get);
+ s->fullscan.total_bytes = s->fullscan.width_bytes * get;
+ }
+ }
+ }
+
+ else { /*fi-60f*/
+ ret = copy_block_to_page(s, SIDE_FRONT);
+ if(ret){
+ DBG (5, "sane_read: cant copy to front/back\n");
+ return ret;
+ }
+
+ s->fullscan.rx_bytes += s->block_xfr.rx_bytes;
+ }
+
+ /* reset for next pass */
+ update_transfer_totals(&s->block_xfr);
+
+ /* scan now finished */
+ if(s->fullscan.rx_bytes == s->fullscan.total_bytes){
+ DBG (15, "sane_read: last block\n");
+ s->fullscan.done = 1;
+ }
+ }
+ }
+
+ *len = page->bytes_scanned - page->bytes_read;
+ if(*len > max_len){
+ *len = max_len;
+ }
+
+ if(*len){
+ DBG (10, "sane_read: copy rx:%d tx:%d tot:%d len:%d\n",
+ page->bytes_scanned, page->bytes_read, page->bytes_total,*len);
+
+ memcpy(buf, page->image->buffer + page->bytes_read, *len);
+ page->bytes_read += *len;
+
+ /* sent it all, return eof on next read */
+ if(s->fullscan.done && page->bytes_read == page->bytes_scanned){
+ DBG (10, "sane_read: side done\n");
+ page->done = 1;
+ }
+ }
+
+ DBG (10, "sane_read: finish si:%d len:%d max:%d\n",s->side,*len,max_len);
+
+ return ret;
+}
+
+/* de-scrambles the raw data from the scanner into the image buffer */
+static SANE_Status
+descramble_raw(struct scanner *s, struct transfer * tp)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ unsigned char *p_in, *p_out = tp->image->buffer;
+ int height = tp->total_bytes / tp->line_stride;
+ int i, j, k, l;
+
+ if (s->model == MODEL_S300)
+ {
+ for (i = 0; i < 2; i++) /* page, front/back */
+ for (j = 0; j < height; j++) /* row (y)*/
+ for (k = 0; k < tp->plane_width; k++) /* column (x) */
+ for (l = 0; l < 3; l++) /* color component */
+ {
+ p_in = (unsigned char *) tp->raw_data + (j * tp->line_stride) + (l * tp->plane_stride) + k * 3 + i;
+ *p_out++ = *p_in;
+ }
+ }
+ else /* MODEL_FI60F */
+ {
+ for (i = 0; i < height; i++) /* row (y)*/
+ for (j = 0; j < 3; j++) /* read head */
+ for (k = 0; k < tp->plane_width; k++) /* column within the read head */
+ for (l = 0; l < 3; l++) /* color component */
+ {
+ p_in = (unsigned char *) tp->raw_data + (i * tp->line_stride) + (l * tp->plane_stride) + k * 3 + j;
+ *p_out++ = *p_in;
+ }
+ }
+
+ return ret;
+}
+
+/* fills block buffer a little per pass */
+static SANE_Status
+read_from_scanner(struct scanner *s, struct transfer * tp)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ size_t bytes = MAX_IMG_PASS;
+ size_t remainBlock = tp->total_bytes - tp->rx_bytes + 8;
+
+ /* determine amount to ask for */
+ if(bytes > remainBlock){
+ bytes = remainBlock;
+ }
+
+ if (tp->image == NULL)
+ {
+ DBG(5, "internal error: read_from_scanner called with no destination image.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (10, "read_from_scanner: start rB:%lu len:%lu\n",
+ (unsigned long)remainBlock, (unsigned long)bytes);
+
+ if(!bytes){
+ DBG(10, "read_from_scanner: no bytes!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ NULL, 0,
+ NULL, 0,
+ tp->raw_data + tp->rx_bytes, &bytes
+ );
+
+ /* full read or short read */
+ if (ret == SANE_STATUS_GOOD || (ret == SANE_STATUS_EOF && bytes) ) {
+
+ DBG(15,"read_from_scanner: got GOOD/EOF (%lu)\n",(unsigned long)bytes);
+
+ if(bytes == remainBlock){
+ DBG(15,"read_from_scanner: block done, ignoring trailer\n");
+ bytes -= 8;
+ tp->done = 1;
+ }
+
+ ret = SANE_STATUS_GOOD;
+ tp->rx_bytes += bytes;
+ }
+ else {
+ DBG(5, "read_from_scanner: error reading status = %d\n", ret);
+ }
+
+ DBG (10, "read_from_scanner: finish rB:%lu len:%lu\n",
+ (unsigned long)(tp->total_bytes - tp->rx_bytes), (unsigned long)bytes);
+
+ return ret;
+}
+
+/* copies block buffer into front or back image buffer */
+/* converts pixel data from RGB Color to the output format */
+static SANE_Status
+copy_block_to_page(struct scanner *s,int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct transfer * block = &s->block_xfr;
+ struct page * page = &s->pages[side];
+ int height = block->total_bytes / block->line_stride;
+ int width = block->image->width_pix;
+ int block_page_stride = block->image->width_bytes * block->image->height;
+ int page_y_offset = page->bytes_scanned / page->image->width_bytes;
+ int line_reverse = (side == SIDE_BACK) || (s->model == MODEL_FI60F);
+ int i,j;
+
+ DBG (10, "copy_block_to_page: start\n");
+
+ /* loop over all the lines in the block */
+ for (i = 0; i < height; i++)
+ {
+ unsigned char * p_in = block->image->buffer + (side * block_page_stride) + (i * block->image->width_bytes);
+ unsigned char * p_out = page->image->buffer + ((i + page_y_offset) * page->image->width_bytes);
+ unsigned char * lineStart = p_out;
+ /* reverse order for back side or FI-60F scanner */
+ if (line_reverse)
+ p_in += (width - 1) * 3;
+ /* convert all of the pixels in this row */
+ for (j = 0; j < width; j++)
+ {
+ unsigned char r, g, b;
+ if (s->model == MODEL_S300)
+ { r = p_in[1]; g = p_in[2]; b = p_in[0]; }
+ else /* (s->model == MODEL_FI60F) */
+ { r = p_in[0]; g = p_in[1]; b = p_in[2]; }
+ if (s->mode == MODE_COLOR)
+ {
+ *p_out++ = r;
+ *p_out++ = g;
+ *p_out++ = b;
+ }
+ else if (s->mode == MODE_GRAYSCALE)
+ {
+ *p_out++ = (r + g + b) / 3;
+ }
+ else if (s->mode == MODE_LINEART)
+ {
+ s->dt.buffer[j] = (r + g + b) / 3;
+ }
+ if (line_reverse)
+ p_in -= 3;
+ else
+ p_in += 3;
+ }
+ /* for MODE_LINEART, binarize the gray line stored in the temp image buffer */
+ if (s->mode == MODE_LINEART)
+ binarize_line(s, lineStart, width);
+ /*add a periodic row because of non-square pixels*/
+ /*FIXME: only works with 225x200*/
+ if (s->resolution_x > s->resolution_y && (i + page_y_offset) % 9 == 8)
+ {
+ memcpy(lineStart + page->image->width_bytes, lineStart, page->image->width_bytes);
+ page_y_offset += 1;
+ page->bytes_scanned += page->image->width_bytes;
+ }
+ }
+
+ /* update the page counter of bytes scanned */
+ page->bytes_scanned += page->image->width_bytes * height;
+
+ DBG (10, "copy_block_to_page: finish\n");
+
+ return ret;
+}
+
+/*uses the threshold/threshold_curve to control binarization*/
+static SANE_Status
+binarize_line(struct scanner *s, unsigned char *lineOut, int width)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int j, windowX, sum = 0;
+
+ /* ~1mm works best, but the window needs to have odd # of pixels */
+ windowX = 6 * s->resolution_x / 150;
+ if (!(windowX % 2)) windowX++;
+
+ /*second, prefill the sliding sum*/
+ for (j = 0; j < windowX; j++)
+ sum += s->dt.buffer[j];
+
+ /* third, walk the dt buffer, update the sliding sum, */
+ /* determine threshold, output bits */
+ for (j = 0; j < width; j++)
+ {
+ /*output image location*/
+ int offset = j % 8;
+ unsigned char mask = 0x80 >> offset;
+ int thresh = s->threshold;
+
+ /* move sum/update threshold only if there is a curve*/
+ if (s->threshold_curve)
+ {
+ int addCol = j + windowX/2;
+ int dropCol = addCol - windowX;
+
+ if (dropCol >= 0 && addCol < width)
+ {
+ sum -= s->dt.buffer[dropCol];
+ sum += s->dt.buffer[addCol];
+ }
+ thresh = s->dt_lut[sum/windowX];
+ }
+
+ /*use average to lookup threshold*/
+ if (s->dt.buffer[j] > thresh)
+ *lineOut &= ~mask; /* white */
+ else
+ *lineOut |= mask; /* black */
+
+ if (offset == 7)
+ lineOut++;
+ }
+
+ return ret;
+}
+
+/*
+ * @@ Section 4 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ /*FIXME: actually ask the scanner to stop?*/
+ struct scanner * s = (struct scanner *) handle;
+ DBG (10, "sane_cancel: start\n");
+ s->started = 0;
+ DBG (10, "sane_cancel: finish\n");
+}
+
+/*
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ struct scanner * s = (struct scanner *) handle;
+
+ DBG (10, "sane_close: start\n");
+
+ /* still connected- drop it */
+ if(s->fd >= 0){
+ sane_cancel(handle);
+ lamp(s, 0);
+ disconnect_fd(s);
+ }
+
+ DBG (10, "sane_close: finish\n");
+}
+
+static SANE_Status
+disconnect_fd (struct scanner *s)
+{
+ DBG (10, "disconnect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (15, "disconnecting usb device\n");
+ sanei_usb_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (10, "disconnect_fd: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+destroy(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "destroy: start\n");
+
+ teardown_buffers(s);
+
+ if(s->sane.name){
+ free(s->sane.name);
+ }
+ if(s->sane.vendor){
+ free(s->sane.vendor);
+ }
+ if(s->sane.model){
+ free(s->sane.model);
+ }
+
+ free(s);
+
+ DBG (10, "destroy: finish\n");
+ return ret;
+}
+
+static SANE_Status
+teardown_buffers(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "teardown_buffers: start\n");
+
+ /* temporary cal data */
+ if(s->coarsecal.buffer){
+ free(s->coarsecal.buffer);
+ s->coarsecal.buffer = NULL;
+ }
+
+ if(s->darkcal.buffer){
+ free(s->darkcal.buffer);
+ s->darkcal.buffer = NULL;
+ }
+
+ if(s->sendcal.buffer){
+ free(s->sendcal.buffer);
+ s->sendcal.buffer = NULL;
+ }
+
+ if(s->cal_image.raw_data){
+ free(s->cal_image.raw_data);
+ s->cal_image.raw_data = NULL;
+ }
+
+ if(s->cal_data.raw_data){
+ free(s->cal_data.raw_data);
+ s->cal_data.raw_data = NULL;
+ }
+
+ /* image slice */
+ if(s->block_img.buffer){
+ free(s->block_img.buffer);
+ s->block_img.buffer = NULL;
+ }
+ if(s->block_xfr.raw_data){
+ free(s->block_xfr.raw_data);
+ s->block_xfr.raw_data = NULL;
+ }
+
+ /* dynamic thresh slice */
+ if(s->dt.buffer){
+ free(s->dt.buffer);
+ s->dt.buffer = NULL;
+ }
+
+ /* image buffer to hold frontside data */
+ if(s->front.buffer){
+ free(s->front.buffer);
+ s->front.buffer = NULL;
+ }
+
+ /* image buffer to hold backside data */
+ if(s->back.buffer){
+ free(s->back.buffer);
+ s->back.buffer = NULL;
+ }
+
+ DBG (10, "teardown_buffers: finish\n");
+ return ret;
+}
+
+/*
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct scanner *dev, *next;
+
+ DBG (10, "sane_exit: start\n");
+
+ for (dev = scanner_devList; dev; dev = next) {
+ next = dev->next;
+ destroy(dev);
+ }
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ scanner_devList = NULL;
+ sane_devArray = NULL;
+
+ DBG (10, "sane_exit: finish\n");
+}
+
+/*
+ * @@ Section 5 - misc helper functions
+ */
+/*
+ * take a bunch of pointers, send commands to scanner
+ */
+static SANE_Status
+do_cmd(struct scanner *s, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ /* sanei_usb overwrites the transfer size, so make some local copies */
+ size_t loc_cmdLen = cmdLen;
+ size_t loc_outLen = outLen;
+ size_t loc_inLen = 0;
+
+ int cmdTime = USB_COMMAND_TIME;
+ int outTime = USB_DATA_TIME;
+ int inTime = USB_DATA_TIME;
+
+ int ret = 0;
+
+ DBG (10, "do_cmd: start\n");
+
+ if(shortTime){
+ cmdTime /= 20;
+ outTime /= 20;
+ inTime /= 20;
+ }
+
+ /* this command has a cmd component, and a place to get it */
+ if(cmdBuff && cmdLen && cmdTime){
+
+ /* change timeout */
+ sanei_usb_set_timeout(cmdTime);
+
+ /* write the command out */
+ DBG(25, "cmd: writing %ld bytes, timeout %d\n", (long)cmdLen, cmdTime);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+ ret = sanei_usb_write_bulk(s->fd, cmdBuff, &cmdLen);
+ DBG(25, "cmd: wrote %ld bytes, retVal %d\n", (long)cmdLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"cmd: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(loc_cmdLen != cmdLen){
+ DBG(5,"cmd: wrong size %ld/%ld\n", (long)loc_cmdLen, (long)cmdLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* this command has a write component, and a place to get it */
+ if(outBuff && outLen && outTime){
+
+ /* change timeout */
+ sanei_usb_set_timeout(outTime);
+
+ DBG(25, "out: writing %ld bytes, timeout %d\n", (long)outLen, outTime);
+ hexdump(30, "out: >>", outBuff, outLen);
+ ret = sanei_usb_write_bulk(s->fd, outBuff, &outLen);
+ DBG(25, "out: wrote %ld bytes, retVal %d\n", (long)outLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"out: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"out: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(loc_outLen != outLen){
+ DBG(5,"out: wrong size %ld/%ld\n", (long)loc_outLen, (long)outLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* this command has a read component, and a place to put it */
+ if(inBuff && inLen && inTime){
+
+ loc_inLen = *inLen;
+ DBG(25, "in: memset %ld bytes\n", (long)*inLen);
+ memset(inBuff,0,*inLen);
+
+ /* change timeout */
+ sanei_usb_set_timeout(inTime);
+
+ DBG(25, "in: reading %ld bytes, timeout %d\n", (long)*inLen, inTime);
+ ret = sanei_usb_read_bulk(s->fd, inBuff, inLen);
+ DBG(25, "in: retVal %d\n", ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"in: got EOF, continuing\n");
+ }
+ else if(ret != SANE_STATUS_GOOD){
+ DBG(5,"in: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ DBG(25, "in: read %ld bytes\n", (long)*inLen);
+ if(*inLen){
+ hexdump(30, "in: <<", inBuff, *inLen);
+ }
+
+ if(loc_inLen != *inLen){
+ ret = SANE_STATUS_EOF;
+ DBG(5,"in: short read %ld/%ld\n", (long)loc_inLen, (long)*inLen);
+ }
+ }
+
+ DBG (10, "do_cmd: finish\n");
+
+ return ret;
+}
+
+/**
+ * Convenience method to determine longest string size in a list.
+ */
+static size_t
+maxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+/**
+ * Prints a hex dump of the given buffer onto the debug output stream.
+ */
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ if(DBG_LEVEL < level)
+ return;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3x:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (15, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG (10, "sane_get_select_fd\n");
+ DBG (15, "%p %d\n", h, *fdp);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* s->page_width stores the user setting
+ * for the paper width in adf. sometimes,
+ * we need a value that differs from this
+ * due to using FB
+ */
+static int
+get_page_width(struct scanner *s)
+{
+ /* scanner max for fb */
+ if(s->source == SOURCE_FLATBED){
+ return s->max_x;
+ }
+
+ return s->page_width;
+}
+
+/* s->page_height stores the user setting
+ * for the paper height in adf. sometimes,
+ * we need a value that differs from this
+ * due to using FB.
+ */
+static int
+get_page_height(struct scanner *s)
+{
+ /* scanner max for fb */
+ if(s->source == SOURCE_FLATBED){
+ return s->max_y;
+ }
+
+ return s->page_height;
+}
+
diff --git a/backend/epjitsu.conf.in b/backend/epjitsu.conf.in
new file mode 100644
index 0000000..cabe6b2
--- /dev/null
+++ b/backend/epjitsu.conf.in
@@ -0,0 +1,37 @@
+# For scanners connected via USB on a known device (kernel driver):
+#usb /dev/usb/scanner0
+
+# For scanners connected via USB using vendor and device ids (libusb):
+#usb VENDORID PRODUCTID
+
+# NOTE: if you have to add your device here- please send the id and model
+# to the author via email, so it can be included in next version. kitno455 at
+# gmail dot com - with epjitsu in the subject line
+
+# These devices require a firmware file in order to function, which must be
+# extracted from the Fujitsu Windows driver. Presumably the Mac versions
+# contain the firmware as well, but the author has no access such a machine.
+
+# Firmware is installed in several different locations by the fujitsu software,
+# using the windows 'search' feature to look for '*.nal' is the easiest way to
+# find them. They should be ~65K, and have the scanner's name as part of the
+# file name. They are often inside a .cab file.
+
+# Copy the file someplace sane can reach it. Then update the line below.
+# NOTE: the firmware line must occur BEFORE the usb line for your scanner
+
+# Fujitsu fi-60F
+firmware @DATADIR@/sane/epjitsu/60f_0A00.nal
+usb 0x04c5 0x10c7
+
+# Fujitsu S300
+firmware @DATADIR@/sane/epjitsu/300_0C00.nal
+usb 0x04c5 0x1156
+
+# Fujitsu S300M
+firmware @DATADIR@/sane/epjitsu/300M_0C00.nal
+usb 0x04c5 0x117f
+
+# Fujitsu S1300
+firmware @DATADIR@/sane/epjitsu/1300_0C26.nal
+usb 0x04c5 0x11ed
diff --git a/backend/epjitsu.h b/backend/epjitsu.h
new file mode 100644
index 0000000..ed63504
--- /dev/null
+++ b/backend/epjitsu.h
@@ -0,0 +1,384 @@
+#ifndef EPJITSU_H
+#define EPJITSU_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ * Please see opening comment in epjitsu.c
+ */
+
+/* -------------------------------------------------------------------------
+ * This option list has to contain all options for all scanners supported by
+ * this driver. If a certain scanner cannot handle a certain option, there's
+ * still the possibility to say so, later.
+ */
+enum scanner_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_SOURCE, /*adffront/adfback/adfduplex/fb*/
+ OPT_MODE, /*mono/gray/color*/
+ OPT_X_RES,
+ OPT_Y_RES,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_PAGE_WIDTH,
+ OPT_PAGE_HEIGHT,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_GAMMA,
+ OPT_THRESHOLD,
+ OPT_THRESHOLD_CURVE,
+
+ OPT_SENSOR_GROUP,
+ OPT_SCAN_SW,
+ OPT_HOPPER,
+ OPT_TOP,
+ OPT_ADF_OPEN,
+ OPT_SLEEP,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+#define FIRMWARE_LENGTH 0x10000
+#define MAX_IMG_PASS 0x10000
+#define MAX_IMG_BLOCK 0x80000
+
+struct image {
+ int width_pix;
+ int width_bytes;
+ int height;
+ int pages;
+
+ unsigned char * buffer;
+};
+
+struct transfer {
+ int plane_width; /* in RGB pixels */
+ int plane_stride; /* in bytes */
+ int line_stride; /* in bytes */
+
+ int total_bytes;
+ int rx_bytes;
+ int done;
+
+ unsigned char * raw_data;
+ struct image * image;
+};
+
+struct page {
+ int bytes_total;
+ int bytes_scanned;
+ int bytes_read;
+ int done;
+ struct image *image;
+};
+
+struct scanner
+{
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during init of scanner. */
+ struct scanner *next;
+
+ int missing;
+
+ int model;
+ int usb_power;
+
+ int has_fb;
+ int has_adf;
+ int x_res_150;
+ int x_res_225;
+ int x_res_300;
+ int x_res_600;
+
+ int y_res_150;
+ int y_res_225;
+ int y_res_300;
+ int y_res_600;
+
+ /* the scan size in 1/1200th inches, NOT basic_units or sane units */
+ int max_x;
+ int max_y;
+ int min_x;
+ int min_y;
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during inquiry probing of the scanner. */
+ SANE_Device sane; /*contains: name, vendor, model, type*/
+
+ /* --------------------------------------------------------------------- */
+ /* changeable SANE_Option structs provide our interface to frontend. */
+
+ /* long array of option structs */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+ /* --------------------------------------------------------------------- */
+ /* some options require lists of strings or numbers, we keep them here */
+ /* instead of in global vars so that they can differ for each scanner */
+
+ /*mode group, room for lineart, gray, color, null */
+ SANE_String_Const source_list[5];
+ SANE_String_Const mode_list[4];
+ SANE_Int x_res_list[4];
+ SANE_Int y_res_list[4];
+
+ /*geometry group*/
+ SANE_Range tl_x_range;
+ SANE_Range tl_y_range;
+ SANE_Range br_x_range;
+ SANE_Range br_y_range;
+ SANE_Range paper_x_range;
+ SANE_Range paper_y_range;
+
+ /*enhancement group*/
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+ SANE_Range gamma_range;
+ SANE_Range threshold_range;
+ SANE_Range threshold_curve_range;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable vars to hold user input. modified by SANE_Options above */
+
+ /*mode group*/
+ int source; /* adf or fb */
+ int mode; /* color,lineart,etc */
+ int res; /* from a limited list, x and y same */
+ int resolution_x; /* unused dummy */
+ int resolution_y; /* unused dummy */
+
+ /*geometry group*/
+ /* The desired size of the scan, all in 1/1200 inch */
+ int tl_x;
+ int tl_y;
+ int br_x;
+ int br_y;
+ int page_width;
+ int page_height;
+
+ /*enhancement group*/
+ int brightness;
+ int contrast;
+ int gamma;
+ int threshold;
+ int threshold_curve;
+
+ int height; /* may run out on adf */
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by user parameter changes, scanner specific */
+ unsigned char * setWindowCoarseCal; /* sent before coarse cal */
+ size_t setWindowCoarseCalLen;
+
+ unsigned char * setWindowFineCal; /* sent before fine cal */
+ size_t setWindowFineCalLen;
+
+ unsigned char * setWindowSendCal; /* sent before send cal */
+ size_t setWindowSendCalLen;
+
+ unsigned char * sendCal1Header; /* part of 1b c3 command */
+ size_t sendCal1HeaderLen;
+
+ unsigned char * sendCal2Header; /* part of 1b c4 command */
+ size_t sendCal2HeaderLen;
+
+ unsigned char * setWindowScan; /* sent before scan */
+ size_t setWindowScanLen;
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by scanning functions to keep track of pages, etc */
+ int started;
+ int side;
+
+ /* holds temp buffers for getting 16 lines of cal data */
+ struct transfer cal_image;
+ struct image coarsecal;
+ struct image darkcal;
+ struct image lightcal;
+
+ /* holds temp buffer for building calibration data */
+ struct transfer cal_data;
+ struct image sendcal;
+
+ /* scanner transmits more data per line than requested */
+ /* due to padding and/or duplex interlacing */
+ /* the scan struct holds these larger numbers, but image buffer is unused */
+ struct {
+ int done;
+ int height;
+ int rx_bytes;
+ int width_bytes;
+ int total_bytes;
+ } fullscan;
+
+ /* The page structs contain data about the progress as the application reads */
+ /* data from the front/back image buffers via the sane_read() function */
+ struct page pages[2];
+
+ /* scanner transmits data in blocks, up to 512k */
+ /* but always ends on a scanline. */
+ /* the block struct holds the most recent buffer */
+ struct transfer block_xfr;
+ struct image block_img;
+
+ /* temporary buffers used by dynamic threshold code */
+ struct image dt;
+ unsigned char dt_lut[256];
+
+ /* final-sized front image, always used */
+ struct image front;
+
+ /* final-sized back image, only used during duplex/backside */
+ struct image back;
+
+ /* --------------------------------------------------------------------- */
+ /* values used by the command and data sending function */
+ int fd; /* The scanner device file descriptor. */
+
+ /* --------------------------------------------------------------------- */
+ /* values which are used by the get hardware status command */
+ time_t last_ghs;
+
+ int hw_scan_sw;
+ int hw_hopper;
+ int hw_top;
+ int hw_adf_open;
+ int hw_sleep;
+};
+
+#define MODEL_NONE 0
+#define MODEL_S300 1
+#define MODEL_FI60F 2
+
+#define USB_COMMAND_TIME 10000
+#define USB_DATA_TIME 10000
+
+#define SIDE_FRONT 0
+#define SIDE_BACK 1
+
+#define SOURCE_FLATBED 0
+#define SOURCE_ADF_FRONT 1
+#define SOURCE_ADF_BACK 2
+#define SOURCE_ADF_DUPLEX 3
+
+#define MODE_COLOR 0
+#define MODE_GRAYSCALE 1
+#define MODE_LINEART 2
+
+#define WINDOW_COARSECAL 0
+#define WINDOW_FINECAL 1
+#define WINDOW_SENDCAL 2
+#define WINDOW_SCAN 3
+
+/* ------------------------------------------------------------------------- */
+
+#define MM_PER_UNIT_UNFIX SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))
+#define MM_PER_UNIT_FIX SANE_FIX(SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)))
+
+#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX((number) * MM_PER_UNIT_UNFIX)
+#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) / MM_PER_UNIT_UNFIX
+
+#define CONFIG_FILE "epjitsu.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle);
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking);
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp);
+
+const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option);
+
+SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+
+SANE_Status sane_start (SANE_Handle handle);
+
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params);
+
+SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len);
+
+void sane_cancel (SANE_Handle h);
+
+void sane_close (SANE_Handle h);
+
+void sane_exit (void);
+
+/* ------------------------------------------------------------------------- */
+
+static SANE_Status attach_one (const char *devicename);
+static SANE_Status connect_fd (struct scanner *s);
+static SANE_Status disconnect_fd (struct scanner *s);
+
+static SANE_Status
+do_cmd(struct scanner *s, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+/*
+static SANE_Status load_calibration (struct scanner *s);
+static SANE_Status read_from_scanner_gray(struct scanner *s);
+*/
+
+/* commands */
+static SANE_Status load_fw(struct scanner *s);
+static SANE_Status get_ident(struct scanner *s);
+
+static SANE_Status change_params(struct scanner *s);
+
+static SANE_Status destroy(struct scanner *s);
+static SANE_Status teardown_buffers(struct scanner *s);
+static SANE_Status setup_buffers(struct scanner *s);
+
+static SANE_Status ingest(struct scanner *s);
+static SANE_Status coarsecal(struct scanner *s);
+static SANE_Status finecal(struct scanner *s);
+static SANE_Status send_lut(struct scanner *s);
+static SANE_Status lamp(struct scanner *s, unsigned char set);
+static SANE_Status set_window(struct scanner *s, int window);
+static SANE_Status scan(struct scanner *s);
+
+static SANE_Status read_from_scanner(struct scanner *s, struct transfer *tp);
+static SANE_Status descramble_raw(struct scanner *s, struct transfer * tp);
+static SANE_Status copy_block_to_page(struct scanner *s, int side);
+static SANE_Status binarize_line(struct scanner *s, unsigned char *lineOut, int width);
+
+static SANE_Status get_hardware_status (struct scanner *s);
+
+static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits,
+ int out_min, int out_max, int slope, int offset);
+
+static int get_page_width (struct scanner *s);
+static int get_page_height (struct scanner *s);
+static unsigned char get_stat(struct scanner *s);
+
+/* utils */
+static void update_transfer_totals(struct transfer * t);
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+static size_t maxStringSize (const SANE_String_Const strings[]);
+
+#endif /* EPJITSU_H */
diff --git a/backend/epson.c b/backend/epson.c
new file mode 100644
index 0000000..2cae65a
--- /dev/null
+++ b/backend/epson.c
@@ -0,0 +1,6403 @@
+/*
+ epson.c - SANE library for Epson flatbed scanners.
+
+ Based on Kazuhiro Sasayama previous
+ Work on epson.[ch] file from the SANE package.
+
+ Original code taken from sane-0.71
+ Copyright (C) 1997 Hypercore Software Design, Ltd.
+
+ modifications
+ Copyright (C) 1998-1999 Christian Bucher <bucher@vernetzt.at>
+ Copyright (C) 1998-1999 Kling & Hautzinger GmbH
+ Copyright (C) 1999 Norihiko Sawa <sawa@yb3.so-net.ne.jp>
+ Copyright (C) 2000 Mike Porter <mike@udel.edu> (mjp)
+ Copyright (C) 2003 EPSON KOWA Corporation
+ Copyright (C) 1999-2005 Karl Heinz Kremer <khk@khk.net>
+ Copyright (C) 2006 Claus Boje <claus@egehuset.dk>
+*/
+
+#define SANE_EPSON_VERSION "SANE Epson Backend v0.2.47 - 2006-08-21"
+#define SANE_EPSON_BUILD 247
+
+/*
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/*
+ 2006-08-21 Fix buffer overflow error (submitted by Johannes Meixner)
+ 2006-06-11 Applied patch from Henning. Fixed a number of compiler warnings
+ 2006-03-12 Added support for perfetion 4990 photo 4800 dpi
+ 2005-01-09 "flaming hack to get USB scanners working without timeouts under linux"
+ submitted by "Steve" (in comment to bug #300830)
+ 2004-12-18 Added USB IDs for CX-4600 and CX-3650
+ 2004-10-16 Added USB ID for Expression 10000XL
+ 2004-05-08 Disable feed() for Perfection1640
+ 2004-02-08 Reformat all source code with "indent -bli0"
+ 2004-02-01 Added D7 function level as copy of D1 for CX-6400
+ Added IDs for CX-6400 and Perfection 4870
+ 2003-10-27 Replaced DBG(0, ... with DBG(1, ...
+ 2003-09-12 Increment only once in loop to find USB scanners
+ Fix rounding problem when determining number of lines to scan
+ 2003-08-21 Removed '//' comments - again ...
+ Added EPSON Kowa copyright message
+ 2003-08-15 Added support for GT-30000, with support for the ADF in simplex mode
+ Borrowed some code from the EPSON Kowa IScan version of the backend
+ Use sanei_scsi_cmd2() to send commands. This makes this backend
+ useable for SBP-2 under FreeBSD
+ 2003-05-11 Initialize OPT_LIMIT_RESOLUTION before first call to filter_resolution_list()
+ Fix memory problem in get_identity_information(). Both problems were
+ reported to the Debian bug database.
+ 2003-03-26 Fixed two warnings reported by der Mouse
+ 2003-02-16 Code cleanup, use more descriptive variable names.
+ 2003-02-15 Move sanei_usb_init() to sane_init(). Thanks to Ron Cemer
+ for providing the patch.
+ 2003-02-15 Fix problem with "usb <vendor> <product> syntax in config file
+ 2002-12-28 Added advanced option to display only short resolution list for
+ displays that can not show the complete list.
+ 2002-11-23 Fixed problem with dropout color.
+ 2002-11-03 Full libusb support.
+ 2002-10-05 Fixed problem with incorrect response to sane_get_parameters()
+ in certain situations.
+ 2002-09-01 USB scanners are now using libsane-usb funtions
+ 2002-08-17 Fixed typo in variable name.
+ Fixed IEEE-1394 problem with Perfection-2450.
+ Fixed problem with older B3 level SCSI scanners that do
+ not support the extended status request.
+ 2002-04-22 Declare close_scanner() and open_scanner() before they
+ are used.
+ 2002-04-13 Check if scanner needs to be opened for the reset call.
+ (Thanks to Thomas Wenrich for pointing this out)
+ Added product IDs for Perfection 1650 and 2450
+ 2002-01-18 Recognize GT-xxxx type scanners also when using the SCSI
+ or IEEE-1394 interface
+ 2002-01-06 Disable TEST_IOCTL again, which was enabled by accident. Also
+ protect the ioctl portion with an #ifdef __linux__
+ 2002-01-05 Version 0.2.17
+ Check for and set s->fd to -1 when device is closed.
+ Removed black gamma table - only use RGB even for grayscale
+ 2002-01-01 Do not call access() for OS/2 systems
+ 2001-11-13 Version 0.2.16
+ Do not call access() for parallel port scanners.
+ 2001-11-11 Version 0.2.15
+ Fixed "wait-for-button" functionality, accidentially merged back wrong
+ version after code freeze.
+ Corrected "need-strange-reorder" recognition.
+ Added IOCTL support to header file.
+ 2001-11-10 Version 0.2.14
+ Added "wait-for-button" functionality
+ 2001-10-30 I18N patches (Stefan Roellin)
+ 2001-10-28 Fixed bug with 1650 recognition
+ 2001-06-09 Version 0.2.09
+ Changed debug level for sense handler from 0 to 2
+ 2001-05-25 Version 0.2.07
+ Allow more than 8 bit color depth even for preview mode
+ since Xsane can handle this. Some code cleanup.
+ 2001-05-24 Removed ancient code that was used to determine the resolution
+ back when the backend still had a slider for the resolution
+ selection.
+ 2001-05-22 Version 0.2.06
+ Added sense_handler to support the GT-8000 scanner. Thanks to Matthias Trute
+ for figuring out the details.
+ Also added experimental code to use USB scanner probing. Need kernel patch
+ for this.
+ 2001-05-19 Version 0.2.05
+ fixed the year in the recent change log entries - I now that it's
+ 2001...
+ Finally fixed the TPU problem with B4 level scanners
+ 2001-05-13 Version 0.2.04
+ Removed check for '\n' before end of line
+ Free memory malloced in sane_get_devices() in sane_exit() again
+ 2001-04-22 Version 0.2.03
+ Check first if the scanner does support the set film type
+ and set focus position before the GUI elements are displayed.
+ This caused problems with older (B4 level) scanners when a TPU
+ was connected.
+ 2001-03-31 Version 0.2.02
+ 2001-03-17 Next attempt to get the reported number of lines correct
+ for the "color shuffling" part.
+ Added more comments.
+ 2000-12-25 Version 0.2.01
+ Fixed problem with bilevel scanning with Perfection610: The
+ line count has to be an even number with this scanner.
+ Several initialization fixes regarding bit depth selection.
+ This version goes back into the CVS repository, the 1.0.4
+ release is out and therefore the code freeze is over.
+ Some general cleanup, added more comments.
+ 2000-12-09 Version 0.2.00
+ Cleaned up printing of gamma table data. 16 elements
+ are now printed in one line without the [epson] in
+ between the values. Values are only printed for
+ Debug levels >= 10.
+ 2000-12-04 We've introduced the concept of inverting images
+ when scanning from a TPU. This is fine, but
+ the user supplied gamma tables no longer work.
+ This is because the data a frontend is going
+ to compute a gamma table for is not what the
+ scanner actually sent. So, we have to back into
+ the proper gamma table. I think this works. See
+ set_gamma_table. (mjp)
+ 2000-12-03 added the 12/14/16 bit support again.
+ 2000-12-03 Version 0.1.38
+ removed changes regarding 12/14 bit support because
+ of SANE feature freeze for 1.0.4. The D1 fix for
+ reading the values from the scanner instead of using
+ hardcoded values and the fix for the off-by-one error
+ in the reorder routine are still in the code base.
+ Also force reload after change of scan mode.
+ The full backend can be downloaded from my web site at
+ http://www.freecolormanagement.com/sane
+ 2000-12-03 Fixed off-by-one error in color reordering function.
+ 2000-12-02 Read information about optical resolution and line
+ distance from scanner instead of hardcoded values.
+ Add support for color depth > 8 bits per channel.
+ 2000-11-23 Display "Set Focus" control only for scanners that
+ can actually handle the command.
+ 2000-11-19 Added support for the "set focus position" command,
+ this is necessary for the Expression1600.
+ 2000-07-28 Changed #include <...> to #include "..." for the
+ sane/... include files.
+ 2000-07-26 Fixed problem with Perfection610: The variable
+ s->color_shuffle_line was never correctly initialized
+ 2000-06-28 When closing the scanner device the data that's
+ still in the scanner, waiting to be transferred
+ is flushed. This fixes the problem with scanimage -T
+ 2000-06-13 Invert image when scanning negative with TPU,
+ Show film type only when TPU is selected
+ 2000-06-13 Initialize optical_res to 0 (Dave Hill)
+ 2000-06-07 Fix in sane_close() - found by Henning Meier-Geinitz
+ 2000-06-01 Threshhold should only be active when scan depth
+ is 1 and halftoning is off. (mjp)
+ 2000-05-28 Turned on scanner based color correction.
+ Dependancies between many options are now
+ being enforced. For instance, auto area seg
+ (AAS) should only be on when scan depth == 1.
+ Added some routines to active and deactivate
+ options. Routines report if option changed.
+ Help prevent extraneous option reloads. Split
+ sane_control_option in getvalue and setvalue.
+ Further split up setvalue into several different
+ routines. (mjp)
+ 2000-05-21 In sane_close use close_scanner instead of just the
+ SCSI close function.
+ 2000-05-20 ... finally fixed the problem with the 610
+ Added resolution_list to Epson_Device structure in
+ epson.h - this fixes a bug that caused problems when
+ more than one EPSON scanner was connected.
+ 2000-05-13 Fixed the color problem with the Perfection 610. The few
+ lines with "garbage" at the beginning of the scan are not
+ yet removed.
+ 2000-05-06 Added support for multiple EPSON scanners. At this time
+ this may not be bug free, but it's a start and it seems
+ to work well with just one scanner.
+ 2000-04-06 Did some cleanup on the gamma correction part. The user
+ defined table is now initialized to gamma=1, the gamma
+ handling is also no longer depending on platform specific
+ tables (handled instead by pointers to the actual tables)
+ 2000-03-27 Disable request for push button status
+ 2000-03-22 Removed free() calls to static strings to remove
+ compile warnings. These were introduced to apparently
+ fix an OS/2 bug. It now turned out that they are not
+ necessary. The real fix was in the repository for a
+ long time (2000-01-25).
+ 2000-03-19 Fixed problem with A4 level devices - they use the
+ line mode instead of the block mode. The routine to
+ handle this was screwed up pretty bad. Now I have
+ a solid version that handles all variations of line
+ mode (automatically deals with the order the color
+ lines are sent).
+ 2000-03-06 Fixed occasional crash after warm up when the "in warmup
+ state" went away in between doing ESC G and getting the
+ extended status message.
+ 2000-03-02 Code cleanup, disabled ZOOM until I have time to
+ deal with all the side effects.
+ 2000-03-01 More D1 fixes. In the future I have to come up with
+ a more elegant solution to destinguish between different
+ function levels. The level > n does not work anymore with
+ D1.
+ Added support for "set threshold" and "set zoom".
+ 2000-02-23 First stab at level D1 support, also added a test
+ for valid "set halftone" command to enable OPT_HALFTONE
+ 2000-02-21 Check for "warming up" in after sane_start. This is
+ IMHO a horrible hack, but that's the only way without
+ a major redesign that will work. (KHK)
+ 2000-02-20 Added some cleanup on error conditions in attach()
+ Use new sanei_config_read() instead of fgets() for
+ compatibility with OS/2 (Yuri Dario)
+ 2000-02-19 Changed some "int" to "size_t" types
+ Removed "Preview Resolution"
+ Implemented resolution list as WORD_LIST instead of
+ a RANGE (KHK)
+ 2000-02-11 Default scan source is always "Flatbed", regardless
+ of installed options. Corrected some typos. (KHK)
+ 2000-02-03 Gamma curves now coupled with gamma correction menu.
+ Only when "User defined" is selected are the curves
+ selected. (Dave Hill)
+ Renamed "Contrast" to "Gamma Correction" (KHK)
+ 2000-02-02 "Brown Paper Bag Release" Put the USB fix finally
+ into the CVS repository.
+ 2000-02-01 Fixed problem with USB scanner not being recognized
+ because of hte changes to attach a few days ago. (KHK)
+ 2000-01-29 fixed core dump with xscanimage by moving the gamma
+ curves to the standard interface (no longer advanced)
+ Removed pragma pack() from source code to make it
+ easier to compile on non-gcc compilers (KHK)
+ 2000-01-26 fixed problem with resolution selection when using the
+ resolution list in xsane (KHK)
+ 2000-01-25 moved the section where the device name is assigned
+ in attach. This avoids the core dump of frontend
+ applications when no scanner is found (Dave Hill)
+ 2000-01-24 reorganization of SCSI related "helper" functions
+ started support for user defined color correction -
+ this is not yet available via the UI (Christian Bucher)
+ 2000-01-24 Removed C++ style comments '//' (KHK)
+*/
+
+
+/* #define TEST_IOCTL */
+
+/* DON'T CHANGE THE NEXT LINE ! */
+/* #undef FORCE_COLOR_SHUFFLE */
+
+
+#ifdef _AIX
+#include <lalloca.h> /* MUST come first for AIX! */
+#endif
+
+/* --------------------- SANE INTERNATIONALISATION ------------------ */
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) (text)
+#endif
+
+#include "../include/sane/config.h"
+
+#include <lalloca.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+#include "../include/sane/sanei_usb.h"
+
+#include "../include/sane/sanei_pio.h"
+
+#define BACKEND_NAME epson
+#include "../include/sane/sanei_backend.h"
+
+#include "../include/sane/sanei_config.h"
+
+#include "epson.h"
+#include "epson_scsi.h"
+#include "epson_usb.h"
+
+#define EPSON_CONFIG_FILE "epson.conf"
+
+#ifndef PATH_MAX
+#define PATH_MAX (1024)
+#endif
+
+#define walloc(x) (x *)malloc(sizeof(x))
+#define walloca(x) (x *)alloca(sizeof(x))
+
+#ifndef XtNumber
+#define XtNumber(x) ( sizeof(x)/ sizeof(x[0]) )
+#define XtOffset(p_type,field) ((size_t)&(((p_type)NULL)->field))
+#define XtOffsetOf(s_type,field) XtOffset(s_type*,field)
+#endif
+
+#define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */
+#define DEVICE_NAME_LEN (16) /* length of device name in extended status */
+
+/* NOTE: you can find these codes with "man ascii". */
+#define STX 0x02
+#define ACK 0x06
+#define NAK 0x15
+#define CAN 0x18
+#define ESC 0x1B
+#define PF 0x19
+
+#define S_ACK "\006"
+#define S_CAN "\030"
+
+#define STATUS_FER 0x80 /* fatal error */
+#define STATUS_AREA_END 0x20 /* area end */
+#define STATUS_OPTION 0x10 /* option installed */
+
+#define EXT_STATUS_FER 0x80 /* fatal error */
+#define EXT_STATUS_FBF 0x40 /* flat bed scanner */
+#define EXT_STATUS_WU 0x02 /* warming up */
+#define EXT_STATUS_PB 0x01 /* scanner has a push button */
+
+#define EXT_STATUS_IST 0x80 /* option detected */
+#define EXT_STATUS_EN 0x40 /* option enabled */
+#define EXT_STATUS_ERR 0x20 /* other error */
+#define EXT_STATUS_PE 0x08 /* no paper */
+#define EXT_STATUS_PJ 0x04 /* paper jam */
+#define EXT_STATUS_OPN 0x02 /* cover open */
+
+#define EPSON_LEVEL_A1 0
+#define EPSON_LEVEL_A2 1
+#define EPSON_LEVEL_B1 2
+#define EPSON_LEVEL_B2 3
+#define EPSON_LEVEL_B3 4
+#define EPSON_LEVEL_B4 5
+#define EPSON_LEVEL_B5 6
+#define EPSON_LEVEL_B6 7
+#define EPSON_LEVEL_B7 8
+#define EPSON_LEVEL_B8 9
+#define EPSON_LEVEL_F5 10
+#define EPSON_LEVEL_D1 11
+#define EPSON_LEVEL_D7 12
+#define EPSON_LEVEL_D8 13
+
+/* there is also a function level "A5", which I'm igoring here until somebody can
+ convince me that this is still needed. The A5 level was for the GT-300, which
+ was (is) a monochrome only scanner. So if somebody really wants to use this
+ scanner with SANE get in touch with me and we can work something out - khk */
+
+#define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3
+
+static EpsonCmdRec epson_cmd[] = {
+/*
+ * request identity
+ * | request identity2
+ * | | request status
+ * | | | request condition
+ * | | | | set color mode
+ * | | | | | start scanning
+ * | | | | | | set data format
+ * | | | | | | | set resolution
+ * | | | | | | | | set zoom
+ * | | | | | | | | | set scan area
+ * | | | | | | | | | | set brightness
+ * | | | | | | | | | | | set gamma
+ * | | | | | | | | | | | | set halftoning
+ * | | | | | | | | | | | | | set color correction
+ * | | | | | | | | | | | | | | initialize scanner
+ * | | | | | | | | | | | | | | | set speed
+ * | | | | | | | | | | | | | | | | set lcount
+ * | | | | | | | | | | | | | | | | | mirror image
+ * | | | | | | | | | | | | | | | | | | set gamma table
+ * | | | | | | | | | | | | | | | | | | | set outline emphasis
+ * | | | | | | | | | | | | | | | | | | | | set dither
+ * | | | | | | | | | | | | | | | | | | | | | set color correction coefficients
+ * | | | | | | | | | | | | | | | | | | | | | | request extension status
+ * | | | | | | | | | | | | | | | | | | | | | | | control an extension
+ * | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject
+ * | | | | | | | | | | | | | | | | | | | | | | | | | feed
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ */
+ {"A1", 'I', 0, 'F','S', 0, 'G', 0, 'R', 0, 'A', 0, {0,0,0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"A2", 'I', 0, 'F','S', 0, 'G','D','R','H','A','L',{-3,3,0}, 'Z','B', 0, '@', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B1", 'I', 0, 'F','S','C','G','D','R', 0, 'A', 0, {0,0,0}, 0, 'B', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B2", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B', 0, '@', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B3", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@', 0, 0, 0, 0, 0, 0, 'm','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B4", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d', 0, 'z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B5", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B6", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {"B7", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-4,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0, '!','s','N', 0, 0, 't', 0, 0},
+ {"B8", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-4,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0x19,'!','s','N', 0, 0, 0, 'p','q'},
+ {"F5", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z', 0, 'M','@','g','d','K','z','Q', 0, 'm','f','e','\f', 0, 0, 0, 'N','T','P', 0, 0, 0},
+ {"D1", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0},
+ {"D7", 'I', 0, 'F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0},
+ {"D8", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f','e', 0, 0, '!', 0, 0, 0, 0, 0, 0, 0},
+};
+
+
+
+/*
+ * Definition of the mode_param struct, that is used to
+ * specify the valid parameters for the different scan modes.
+ *
+ * The depth variable gets updated when the bit depth is modified.
+ */
+
+struct mode_param
+{
+ int color;
+ int mode_flags;
+ int dropout_mask;
+ int depth;
+};
+
+static struct mode_param mode_params[] = {
+ {0, 0x00, 0x30, 1},
+ {0, 0x00, 0x30, 8},
+ {1, 0x02, 0x00, 8}
+};
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static const SANE_String_Const adf_mode_list[] = {
+ SANE_I18N ("Simplex"),
+ SANE_I18N ("Duplex"),
+ NULL
+};
+
+
+/*
+ * Define the different scan sources:
+ */
+
+#define FBF_STR SANE_I18N("Flatbed")
+#define TPU_STR SANE_I18N("Transparency Unit")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+/*
+ * source list need one dummy entry (save device settings is crashing).
+ * NOTE: no const - this list gets created while exploring the capabilities
+ * of the scanner.
+ */
+
+static SANE_String_Const source_list[] = {
+ FBF_STR,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* some defines to make handling the TPU easier: */
+#define FILM_TYPE_POSITIVE (0)
+#define FILM_TYPE_NEGATIVE (1)
+
+static const SANE_String_Const film_list[] = {
+ SANE_I18N ("Positive Film"),
+ SANE_I18N ("Negative Film"),
+ NULL
+};
+
+static const SANE_String_Const focus_list[] = {
+ SANE_I18N ("Focus on glass"),
+ SANE_I18N ("Focus 2.5mm above glass"),
+ NULL
+};
+
+/*
+ * TODO: add some missing const.
+ */
+
+#define HALFTONE_NONE 0x01
+#define HALFTONE_TET 0x03
+
+static int halftone_params[] = {
+ HALFTONE_NONE,
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x80,
+ 0x90,
+ 0xa0,
+ 0xb0,
+ HALFTONE_TET,
+ 0xc0,
+ 0xd0
+};
+
+static const SANE_String_Const halftone_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Halftone A (Hard Tone)"),
+ SANE_I18N ("Halftone B (Soft Tone)"),
+ SANE_I18N ("Halftone C (Net Screen)"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list_4[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Halftone A (Hard Tone)"),
+ SANE_I18N ("Halftone B (Soft Tone)"),
+ SANE_I18N ("Halftone C (Net Screen)"),
+ SANE_I18N ("Dither A (4x4 Bayer)"),
+ SANE_I18N ("Dither B (4x4 Spiral)"),
+ SANE_I18N ("Dither C (4x4 Net Screen)"),
+ SANE_I18N ("Dither D (8x4 Net Screen)"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list_7[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Halftone A (Hard Tone)"),
+ SANE_I18N ("Halftone B (Soft Tone)"),
+ SANE_I18N ("Halftone C (Net Screen)"),
+ SANE_I18N ("Dither A (4x4 Bayer)"),
+ SANE_I18N ("Dither B (4x4 Spiral)"),
+ SANE_I18N ("Dither C (4x4 Net Screen)"),
+ SANE_I18N ("Dither D (8x4 Net Screen)"),
+ SANE_I18N ("Text Enhanced Technology"),
+ SANE_I18N ("Download pattern A"),
+ SANE_I18N ("Download pattern B"),
+ NULL
+};
+
+static int dropout_params[] = {
+ 0x00, /* none */
+ 0x10, /* red */
+ 0x20, /* green */
+ 0x30 /* blue */
+};
+
+static const SANE_String_Const dropout_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Red"),
+ SANE_I18N ("Green"),
+ SANE_I18N ("Blue"),
+ NULL
+};
+
+/*
+ * Color correction:
+ * One array for the actual parameters that get sent to the scanner (color_params[]),
+ * one array for the strings that get displayed in the user interface (color_list[])
+ * and one array to mark the user defined color correction (dolor_userdefined[]).
+ */
+
+static int color_params[] = {
+ 0x00,
+ 0x01,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80
+};
+
+static SANE_Bool color_userdefined[] = {
+ SANE_FALSE,
+ SANE_TRUE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE
+};
+
+static const SANE_String_Const color_list[] = {
+ SANE_I18N ("No Correction"),
+ SANE_I18N ("User defined"),
+ SANE_I18N ("Impact-dot printers"),
+ SANE_I18N ("Thermal printers"),
+ SANE_I18N ("Ink-jet printers"),
+ SANE_I18N ("CRT monitors"),
+ NULL
+};
+
+/*
+ * Gamma correction:
+ * The A and B level scanners work differently than the D level scanners, therefore
+ * I define two different sets of arrays, plus one set of variables that get set to
+ * the actally used params and list arrays at runtime.
+ */
+
+static int gamma_params_ab[] = {
+ 0x01,
+ 0x03,
+ 0x00,
+ 0x10,
+ 0x20
+};
+
+static const SANE_String_Const gamma_list_ab[] = {
+ SANE_I18N ("Default"),
+ SANE_I18N ("User defined"),
+ SANE_I18N ("High density printing"),
+ SANE_I18N ("Low density printing"),
+ SANE_I18N ("High contrast printing"),
+ NULL
+};
+
+static SANE_Bool gamma_userdefined_ab[] = {
+ SANE_FALSE,
+ SANE_TRUE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE,
+};
+
+static int gamma_params_d[] = {
+ 0x03,
+ 0x04
+};
+
+static const SANE_String_Const gamma_list_d[] = {
+ SANE_I18N ("User defined (Gamma=1.0)"),
+ SANE_I18N ("User defined (Gamma=1.8)"),
+ NULL
+};
+
+static SANE_Bool gamma_userdefined_d[] = {
+ SANE_TRUE,
+ SANE_TRUE
+};
+
+static SANE_Bool *gamma_userdefined;
+static int *gamma_params;
+
+/* flaming hack to get USB scanners
+ working without timeouts under linux */
+/* (cribbed from fujitsu.c) */
+static unsigned int r_cmd_count = 0;
+static unsigned int w_cmd_count = 0;
+
+
+
+
+/* Bay list:
+ * this is used for the FilmScan
+ */
+
+static const SANE_String_Const bay_list[] = {
+ " 1 ",
+ " 2 ",
+ " 3 ",
+ " 4 ",
+ " 5 ",
+ " 6 ",
+ NULL
+};
+
+/*
+ * minimum, maximum, quantization.
+ */
+
+static const SANE_Range u8_range = { 0, 255, 0 };
+static const SANE_Range s8_range = { -127, 127, 0 };
+static const SANE_Range zoom_range = { 50, 200, 0 };
+
+/*
+ * The "switch_params" are used for several boolean choices
+ */
+static int switch_params[] = {
+ 0,
+ 1
+};
+
+#define mirror_params switch_params
+#define speed_params switch_params
+#define film_params switch_params
+
+static const SANE_Range outline_emphasis_range = { -2, 2, 0 };
+
+/* static const SANE_Range gamma_range = { -2, 2, 0 }; */
+
+struct qf_param
+{
+ SANE_Word tl_x;
+ SANE_Word tl_y;
+ SANE_Word br_x;
+ SANE_Word br_y;
+};
+
+/* gcc don't like to overwrite const field */
+static /*const */ struct qf_param qf_params[] = {
+ {0, 0, SANE_FIX (120.0), SANE_FIX (120.0)},
+ {0, 0, SANE_FIX (148.5), SANE_FIX (210.0)},
+ {0, 0, SANE_FIX (210.0), SANE_FIX (148.5)},
+ {0, 0, SANE_FIX (215.9), SANE_FIX (279.4)}, /* 8.5" x 11" */
+ {0, 0, SANE_FIX (210.0), SANE_FIX (297.0)},
+ {0, 0, 0, 0}
+};
+
+static const SANE_String_Const qf_list[] = {
+ SANE_I18N ("CD"),
+ SANE_I18N ("A5 portrait"),
+ SANE_I18N ("A5 landscape"),
+ SANE_I18N ("Letter"),
+ SANE_I18N ("A4"),
+ SANE_I18N ("Max"),
+ NULL
+};
+
+
+static SANE_Word *bitDepthList = NULL;
+
+
+
+/*
+ * List of pointers to devices - will be dynamically allocated depending
+ * on the number of devices found.
+ */
+static const SANE_Device **devlist = 0;
+
+
+/*
+ * Some utility functions
+ */
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+typedef struct
+{
+ u_char code;
+ u_char status;
+ u_char count1;
+ u_char count2;
+ u_char buf[1];
+
+} EpsonHdrRec, *EpsonHdr;
+
+typedef struct
+{
+ u_char code;
+ u_char status;
+ u_char count1;
+ u_char count2;
+
+ u_char type;
+ u_char level;
+
+ u_char buf[1];
+
+} EpsonIdentRec, *EpsonIdent;
+
+
+typedef struct
+{
+ u_char code;
+ u_char status;
+ u_short count;
+
+ u_char buf[1];
+
+} EpsonParameterRec, *EpsonParameter;
+
+typedef struct
+{
+ u_char code;
+ u_char status;
+
+ u_char buf[4];
+
+} EpsonDataRec, *EpsonData;
+
+/*
+ *
+ *
+ */
+
+static EpsonHdr command (Epson_Scanner * s, u_char * cmd, size_t cmd_size,
+ SANE_Status * status);
+static SANE_Status get_identity_information (SANE_Handle handle);
+static SANE_Status get_identity2_information (SANE_Handle handle);
+static int send (Epson_Scanner * s, void *buf, size_t buf_size,
+ SANE_Status * status);
+static ssize_t receive (Epson_Scanner * s, void *buf, ssize_t buf_size,
+ SANE_Status * status);
+static SANE_Status color_shuffle (SANE_Handle handle, int *new_length);
+static SANE_Status request_focus_position (SANE_Handle handle,
+ u_char * position);
+static SANE_Bool request_push_button_status (SANE_Handle handle,
+ SANE_Bool * theButtonStatus);
+static void activateOption (Epson_Scanner * s, SANE_Int option,
+ SANE_Bool * change);
+static void deactivateOption (Epson_Scanner * s, SANE_Int option,
+ SANE_Bool * change);
+static void setOptionState (Epson_Scanner * s, SANE_Bool state,
+ SANE_Int option, SANE_Bool * change);
+static void close_scanner (Epson_Scanner * s);
+static SANE_Status open_scanner (Epson_Scanner * s);
+SANE_Status sane_auto_eject (Epson_Scanner * s);
+static SANE_Status attach_one_usb (SANE_String_Const devname);
+static void filter_resolution_list (Epson_Scanner * s);
+static void get_size (char c1, char c2, double *w, double *h);
+static void scan_finish (Epson_Scanner * s);
+
+/*
+ *
+ *
+ */
+
+static int
+send (Epson_Scanner * s, void *buf, size_t buf_size, SANE_Status * status)
+{
+ DBG (3, "send buf, size = %lu\n", (u_long) buf_size);
+
+#if 1
+ {
+ unsigned int k;
+ const u_char *s = buf;
+
+ for (k = 0; k < buf_size; k++)
+ {
+ DBG (125, "buf[%d] %02x %c\n", k, s[k], isprint (s[k]) ? s[k] : '.');
+ }
+ }
+#endif
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ return sanei_epson_scsi_write (s->fd, buf, buf_size, status);
+ }
+ else if (s->hw->connection == SANE_EPSON_PIO)
+ {
+ size_t n;
+
+ if (buf_size == (n = sanei_pio_write (s->fd, buf, buf_size)))
+ *status = SANE_STATUS_GOOD;
+ else
+ *status = SANE_STATUS_INVAL;
+
+ return n;
+ }
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ size_t n;
+ n = buf_size;
+ *status = sanei_usb_write_bulk (s->fd, buf, &n);
+ w_cmd_count++;
+ DBG (5, "w_cmd_count = %d\n",w_cmd_count);
+ DBG (5, "r_cmd_count = %d\n",r_cmd_count);
+
+ return n;
+ }
+
+ return SANE_STATUS_INVAL;
+ /* never reached */
+}
+
+/*
+ *
+ *
+ */
+
+static ssize_t
+receive (Epson_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status)
+{
+ ssize_t n = 0;
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ n = sanei_epson_scsi_read (s->fd, buf, buf_size, status);
+ }
+ else if (s->hw->connection == SANE_EPSON_PIO)
+ {
+ if (buf_size == (n = sanei_pio_read (s->fd, buf, (size_t) buf_size)))
+ *status = SANE_STATUS_GOOD;
+ else
+ *status = SANE_STATUS_INVAL;
+ }
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ /* !!! only report an error if we don't read anything */
+ n = buf_size; /* buf_size gets overwritten */
+ *status = sanei_usb_read_bulk (s->fd, (SANE_Byte *) buf, (size_t *) & n);
+ r_cmd_count += (n+63)/64; /* add # of packets, rounding up */
+ DBG (5, "w_cmd_count = %d\n",w_cmd_count);
+ DBG (5, "r_cmd_count = %d\n",r_cmd_count);
+
+ if (n > 0)
+ *status = SANE_STATUS_GOOD;
+ }
+
+ DBG (7, "receive buf, expected = %lu, got = %ld\n", (u_long) buf_size, (long) n);
+
+#if 1
+ if (n > 0)
+ {
+ int k;
+ const u_char *s = buf;
+
+ for (k = 0; k < n; k++)
+ {
+ DBG (127, "buf[%d] %02x %c\n", k, s[k], isprint (s[k]) ? s[k] : '.');
+ }
+ }
+#else
+ {
+ int i;
+ ssize_t k;
+ ssize_t hex_start = 0;
+ const u_char *s = buf;
+ char hex_str[NUM_OF_HEX_ELEMENTS * 3 + 1];
+ char tmp_str[NUM_OF_HEX_ELEMENTS * 3 + 1];
+ char ascii_str[NUM_OF_HEX_ELEMENTS * 2 + 1];
+
+ hex_str[0] = '\0';
+ ascii_str[0] = '\0';
+
+ for (k = 0; k < buf_size; k++)
+ {
+ /* write out the data in lines of 16 bytes */
+ /* add the next hex value to the hex string */
+ sprintf (tmp_str, "%s %02x", hex_str, s[k]);
+ strcpy (hex_str, tmp_str);
+
+ /* add the character to the ascii string */
+ sprintf (tmp_str, "%s %c", ascii_str, isprint (s[k]) ? s[k] : '.');
+ strcpy (ascii_str, tmp_str);
+
+ if ((k % (NUM_OF_HEX_ELEMENTS)) == 0)
+ {
+ if (k != 0) /* don't do this the first time */
+ {
+ for (i = strlen (hex_str); i < (NUM_OF_HEX_ELEMENTS * 3); i++)
+ {
+ hex_str[i] = ' ';
+ }
+ hex_str[NUM_OF_HEX_ELEMENTS + 1] = '\0';
+
+ DBG (125, "recv buf[%05d]: %s %s\n", hex_start, hex_str,
+ ascii_str);
+ hex_start = k;
+ hex_str[0] = '\0';
+ ascii_str[0] = '\0';
+ }
+ }
+ }
+
+ for (i = strlen (hex_str); i < NUM_OF_HEX_ELEMENTS * 3; i++)
+ {
+ hex_str[i] = ' ';
+ }
+ hex_str[NUM_OF_HEX_ELEMENTS + 1] = '\0';
+
+ DBG (125, "recv buf[%05d]: %s %s\n", hex_start, hex_str, ascii_str);
+ }
+#endif
+
+ return n;
+}
+
+/*
+ *
+ *
+ */
+
+static SANE_Status
+expect_ack (Epson_Scanner * s)
+{
+ u_char result[1];
+ size_t len;
+ SANE_Status status;
+
+ len = sizeof (result);
+
+ receive (s, result, len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ if (ACK != result[0])
+ return SANE_STATUS_INVAL;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ *
+ *
+ */
+
+static SANE_Status
+set_cmd (Epson_Scanner * s, u_char cmd, int val)
+{
+ SANE_Status status;
+ u_char params[2];
+
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ send (s, params, 2, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ return status;
+
+ params[0] = val;
+ send (s, params, 1, &status);
+ status = expect_ack (s);
+
+ return status;
+}
+
+/* A little helper function to correct the extended status reply
+ gotten from scanners with known buggy firmware.
+ */
+static void
+fix_up_extended_status_reply (const char *model, u_char * buf)
+{
+
+ if (0 == strncmp (model, "ES-9000H", strlen ("ES-9000H"))
+ || 0 == strncmp (model, "GT-30000", strlen ("GT-30000")))
+ {
+ DBG (1, "Fixing up buggy ADF max scan dimensions.\n");
+ buf[2] = 0xB0;
+ buf[3] = 0x6D;
+ buf[4] = 0x60;
+ buf[5] = 0x9F;
+ }
+}
+
+static void
+print_params (const SANE_Parameters params)
+{
+ DBG (5, "params.format = %d\n", params.format);
+ DBG (5, "params.last_frame = %d\n", params.last_frame);
+ DBG (5, "params.bytes_per_line = %d\n", params.bytes_per_line);
+ DBG (5, "params.pixels_per_line = %d\n", params.pixels_per_line);
+ DBG (5, "params.lines = %d\n", params.lines);
+ DBG (5, "params.depth = %d\n", params.depth);
+}
+
+
+/*
+ *
+ *
+ */
+
+#define set_focus_position(s,v) set_cmd( s,(s)->hw->cmd->set_focus_position,v)
+#define set_color_mode(s,v) set_cmd( s,(s)->hw->cmd->set_color_mode,v)
+#define set_data_format(s,v) set_cmd( s,(s)->hw->cmd->set_data_format, v)
+#define set_halftoning(s,v) set_cmd( s,(s)->hw->cmd->set_halftoning, v)
+#define set_gamma(s,v) set_cmd( s,(s)->hw->cmd->set_gamma, v)
+#define set_color_correction(s,v) set_cmd( s,(s)->hw->cmd->set_color_correction, v)
+#define set_lcount(s,v) set_cmd( s,(s)->hw->cmd->set_lcount, v)
+#define set_bright(s,v) set_cmd( s,(s)->hw->cmd->set_bright, v)
+#define mirror_image(s,v) set_cmd( s,(s)->hw->cmd->mirror_image, v)
+#define set_speed(s,v) set_cmd( s,(s)->hw->cmd->set_speed, v)
+#define set_outline_emphasis(s,v) set_cmd( s,(s)->hw->cmd->set_outline_emphasis, v)
+#define control_auto_area_segmentation(s,v) set_cmd( s,(s)->hw->cmd->control_auto_area_segmentation, v)
+#define set_film_type(s,v) set_cmd( s,(s)->hw->cmd->set_film_type, v)
+#define set_exposure_time(s,v) set_cmd( s,(s)->hw->cmd->set_exposure_time, v)
+#define set_bay(s,v) set_cmd( s,(s)->hw->cmd->set_bay, v)
+#define set_threshold(s,v) set_cmd( s,(s)->hw->cmd->set_threshold, v)
+#define control_extension(s,v) set_cmd( s,(s)->hw->cmd->control_an_extension, v)
+
+/*#define (s,v) set_cmd( s,(s)->hw->cmd->, v) */
+
+static SANE_Status
+set_zoom (Epson_Scanner * s, int x_zoom, int y_zoom)
+{
+ SANE_Status status;
+ u_char cmd[2];
+ u_char params[2];
+
+ if (!s->hw->cmd->set_zoom)
+ return SANE_STATUS_GOOD;
+
+ cmd[0] = ESC;
+ cmd[1] = s->hw->cmd->set_zoom;
+
+ send (s, cmd, 2, &status);
+ status = expect_ack (s);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x_zoom;
+ params[1] = y_zoom;
+
+ send (s, params, 2, &status);
+ status = expect_ack (s);
+
+ return status;
+}
+
+
+static SANE_Status
+set_resolution (Epson_Scanner * s, int xres, int yres)
+{
+ SANE_Status status;
+ u_char params[4];
+
+ if (!s->hw->cmd->set_resolution)
+ return SANE_STATUS_GOOD;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_resolution;
+
+ send (s, params, 2, &status);
+ status = expect_ack (s);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = xres;
+ params[1] = xres >> 8;
+ params[2] = yres;
+ params[3] = yres >> 8;
+
+ send (s, params, 4, &status);
+ status = expect_ack (s);
+
+ return status;
+}
+
+/*
+ * set_scan_area()
+ *
+ * Sends the "set scan area" command to the scanner with the currently selected
+ * scan area. This scan area is already corrected for "color shuffling" if
+ * necessary.
+ */
+static SANE_Status
+set_scan_area (Epson_Scanner * s, int x, int y, int width, int height)
+{
+ SANE_Status status;
+ u_char params[8];
+
+ DBG (1, "set_scan_area: %p %d %d %d %d\n", (void *) s, x, y, width, height);
+
+ if (!s->hw->cmd->set_scan_area)
+ {
+ DBG (1, "set_scan_area not supported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* verify the scan area */
+ if (x < 0 || y < 0 || width <= 0 || height <= 0)
+ return SANE_STATUS_INVAL;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_scan_area;
+
+ send (s, params, 2, &status);
+ status = expect_ack (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x;
+ params[1] = x >> 8;
+ params[2] = y;
+ params[3] = y >> 8;
+ params[4] = width;
+ params[5] = width >> 8;
+ params[6] = height;
+ params[7] = height >> 8;
+
+ send (s, params, 8, &status);
+ status = expect_ack (s);
+
+ return status;
+}
+
+/*
+ * set_color_correction_coefficients()
+ *
+ * Sends the "set color correction coefficients" command with the currently selected
+ * parameters to the scanner.
+ */
+
+static SANE_Status
+set_color_correction_coefficients (Epson_Scanner * s)
+{
+ SANE_Status status;
+ u_char cmd = s->hw->cmd->set_color_correction_coefficients;
+ u_char params[2];
+ const int length = 9;
+ signed char cct[9];
+
+ DBG (1, "set_color_correction_coefficients: starting.\n");
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ send (s, params, 2, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ return status;
+
+ cct[0] = s->val[OPT_CCT_1].w;
+ cct[1] = s->val[OPT_CCT_2].w;
+ cct[2] = s->val[OPT_CCT_3].w;
+ cct[3] = s->val[OPT_CCT_4].w;
+ cct[4] = s->val[OPT_CCT_5].w;
+ cct[5] = s->val[OPT_CCT_6].w;
+ cct[6] = s->val[OPT_CCT_7].w;
+ cct[7] = s->val[OPT_CCT_8].w;
+ cct[8] = s->val[OPT_CCT_9].w;
+
+ DBG (1, "set_color_correction_coefficients: %d,%d,%d %d,%d,%d %d,%d,%d.\n",
+ cct[0], cct[1], cct[2], cct[3],
+ cct[4], cct[5], cct[6], cct[7], cct[8]);
+
+ send (s, cct, length, &status);
+ status = expect_ack (s);
+ DBG (1, "set_color_correction_coefficients: ending=%d.\n", status);
+
+ return status;
+}
+
+/*
+ *
+ *
+ */
+
+static SANE_Status
+set_gamma_table (Epson_Scanner * s)
+{
+
+ SANE_Status status;
+ u_char cmd = s->hw->cmd->set_gamma_table;
+ u_char params[2];
+ const int length = 257;
+ u_char gamma[257];
+ int n;
+ int table;
+/* static const char gamma_cmds[] = { 'M', 'R', 'G', 'B' }; */
+ static const char gamma_cmds[] = { 'R', 'G', 'B' };
+
+
+ DBG (1, "set_gamma_table: starting.\n");
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+/*
+ * Print the gamma tables before sending them to the scanner.
+ */
+
+ if (DBG_LEVEL > 0)
+ {
+ int c, i, j;
+
+ DBG (1, "set_gamma_table()\n");
+ for (c = 0; c < 3; c++)
+ {
+ for (i = 0; i < 256; i += 16)
+ {
+ char gammaValues[16 * 3 + 1], newValue[4];
+
+ gammaValues[0] = '\0';
+
+ for (j = 0; j < 16; j++)
+ {
+ sprintf (newValue, " %02x", s->gamma_table[c][i + j]);
+ strcat (gammaValues, newValue);
+ }
+
+ DBG (10, "Gamma Table[%d][%d] %s\n", c, i, gammaValues);
+ }
+ }
+ }
+
+
+/*
+ * TODO: &status in send makes no sense like that.
+ */
+
+/*
+ * When handling inverted images, we must also invert the user
+ * supplied gamma function. This is *not* just 255-gamma -
+ * this gives a negative image.
+ */
+
+ for (table = 0; table < 3; table++)
+ {
+ gamma[0] = gamma_cmds[table];
+ if (s->invert_image)
+ {
+ for (n = 0; n < 256; ++n)
+ {
+ gamma[n + 1] = 255 - s->gamma_table[table][255 - n];
+ }
+ }
+ else
+ {
+ for (n = 0; n < 256; ++n)
+ {
+ gamma[n + 1] = s->gamma_table[table][n];
+ }
+ }
+
+ send (s, params, 2, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ return status;
+
+ send (s, gamma, length, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ return status;
+
+ }
+
+ DBG (1, "set_gamma_table: complete = %d.\n", status);
+
+ return status;
+}
+
+
+
+void
+get_size (char c1, char c2, double *w, double *h)
+{
+ int ind;
+ unsigned char flag;
+
+ double wsizetbl[] = {
+ 11.60, /* A3V */
+ 11.00, /* WLT */
+ 10.12, /* B4V */
+ 8.50, /* LGV */
+ 8.27, /* A4V */
+ 11.69, /* A4H */
+ 8.50, /* LTV */
+ 11.00, /* LTH */
+ 7.17, /* B5V */
+ 10.12, /* B5H */
+ 5.83, /* A5V */
+ 8.27, /* A5H */
+ 7.25, /* EXV */
+ 10.50, /* EXH */
+ 11.69, /* unknown */
+ 11.69, /* unknown */
+ 11.69, /* unknown */
+ };
+ double hsizetbl[] = {
+ 16.54, /* A3V */
+ 17.00, /* WLT */
+ 14.33, /* B4V */
+ 14.00, /* LGV */
+ 11.69, /* A4V */
+ 8.27, /* A4H */
+ 11.00, /* LTV */
+ 8.50, /* LTH */
+ 10.12, /* B5V */
+ 7.17, /* B5H */
+ 8.27, /* A5V */
+ 5.83, /* A5H */
+ 10.50, /* EXV */
+ 7.25, /* EXH */
+ 17.00, /* unknown */
+ 17.00, /* unknown */
+ 17.00, /* unknown */
+ };
+
+ flag = c1;
+ for (ind = 0; ind < 8; ind++)
+ {
+ if (flag & 0x80)
+ goto DetectSize;
+ flag = flag << 1;
+ }
+ flag = c2;
+ for (; ind < 16; ind++)
+ {
+ if (flag & 0x80)
+ goto DetectSize;
+ flag = flag << 1;
+ }
+
+DetectSize:
+
+ *w = wsizetbl[ind];
+ *h = hsizetbl[ind];
+
+ DBG (10, "detected width: %f\n", *w);
+ DBG (10, "detected height: %f\n", *h);
+}
+
+
+/*
+ * check_ext_status()
+ *
+ * Requests the extended status flag from the scanner. The "warming up" condition
+ * is reported as a warning (only visible if debug level is set to 10 or greater) -
+ * every other condition is reported as an error.
+ *
+ * This function only gets called when we are dealing with a scanner that supports the
+ * "warming up" code, so it's not a problem for B3 level scanners, that don't handle
+ * request extended status commands.
+ */
+
+static SANE_Status
+check_ext_status (Epson_Scanner * s, int *max_x, int *max_y)
+{
+ SANE_Status status;
+ u_char cmd = s->hw->cmd->request_extended_status;
+ u_char params[2];
+ u_char *buf;
+ EpsonHdr head;
+
+ *max_x = 0;
+ *max_y = 0;
+
+ if (cmd == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ head = (EpsonHdr) command (s, params, 2, &status);
+ if (NULL == head)
+ {
+ DBG (1, "Extended status flag request failed\n");
+ return status;
+ }
+
+ buf = &head->buf[0];
+
+ if (buf[0] & EXT_STATUS_WU)
+ {
+ DBG (10, "option: warming up\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (buf[0] & EXT_STATUS_FER)
+ {
+ DBG (1, "option: fatal error\n");
+ status = SANE_STATUS_INVAL;
+ }
+
+ if (s->hw->ADF && s->hw->use_extension && s->hw->cmd->feed)
+ {
+ fix_up_extended_status_reply (s->hw->sane.model, buf);
+
+ *max_x = buf[3] << 8 | buf[2];
+ *max_y = buf[5] << 8 | buf[4];
+
+ if (0 == strcmp ("ES-9000H", s->hw->sane.model)
+ || 0 == strcmp ("GT-30000", s->hw->sane.model))
+ {
+ /* set size of current sheet, but don't clobber zoom
+ settings (which should always be smaller than the
+ detected sheet size) */
+ double w, h;
+ get_size (buf[16], buf[17], &w, &h);
+ w = SANE_FIX (w * MM_PER_INCH);
+ h = SANE_FIX (h * MM_PER_INCH);
+ if (w < s->val[OPT_BR_X].w)
+ s->val[OPT_BR_X].w = w;
+ if (h < s->val[OPT_BR_Y].w)
+ s->val[OPT_BR_Y].w = h;
+ }
+ }
+
+
+ if (buf[1] & EXT_STATUS_ERR)
+ {
+ DBG (1, "ADF: other error\n");
+ status = SANE_STATUS_INVAL;
+ }
+
+ if (buf[1] & EXT_STATUS_PE)
+ {
+ DBG (1, "ADF: no paper\n");
+ status = SANE_STATUS_NO_DOCS;
+ return status;
+ }
+
+ if (buf[1] & EXT_STATUS_PJ)
+ {
+ DBG (1, "ADF: paper jam\n");
+ status = SANE_STATUS_JAMMED;
+ }
+
+ if (buf[1] & EXT_STATUS_OPN)
+ {
+ DBG (1, "ADF: cover open\n");
+ status = SANE_STATUS_COVER_OPEN;
+ }
+
+ if (buf[6] & EXT_STATUS_ERR)
+ {
+ DBG (1, "TPU: other error\n");
+ status = SANE_STATUS_INVAL;
+ }
+
+ /* return the max. scan area for the ADF */
+ if (buf[6] & EXT_STATUS_IST)
+ {
+ *max_x = buf[8] << 8 | buf[7];
+ *max_y = buf[10] << 8 | buf[9];
+ }
+
+ /* return the max. scan area for the flatbed */
+ if (s->hw->devtype == 3 && s->hw->use_extension == 0)
+ {
+ double w, h;
+ get_size (buf[18], buf[19], &w, &h);
+ *max_x = (int) (w * s->hw->dpi_range.max);
+ *max_y = (int) (h * s->hw->dpi_range.max);
+ }
+
+ free (head);
+
+ return status;
+}
+
+/*
+ * reset()
+ *
+ * Send the "initialize scanner" command to the device and reset it.
+ *
+ */
+
+static SANE_Status
+reset (Epson_Scanner * s)
+{
+ SANE_Status status;
+ u_char param[2];
+ SANE_Bool needToClose = SANE_FALSE;
+
+ DBG (5, "reset()\n");
+
+ if (!s->hw->cmd->initialize_scanner)
+ return SANE_STATUS_GOOD;
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->initialize_scanner;
+
+ if (s->fd == -1)
+ {
+ needToClose = SANE_TRUE;
+ DBG (5, "reset calling open_scanner\n");
+ if ((status = open_scanner (s)) != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ send (s, param, 2, &status);
+ status = expect_ack (s);
+
+ if (needToClose)
+ {
+ close_scanner (s);
+ }
+
+ return status;
+}
+
+
+/*
+ * close_scanner()
+ *
+ * Close the open scanner. Depending on the connection method, a different
+ * close function is called.
+ */
+
+static void
+close_scanner (Epson_Scanner * s)
+{
+ DBG (5, "close_scanner(fd = %d)\n", s->fd);
+
+ if (s->fd == -1)
+ return;
+
+ if (r_cmd_count % 2)
+ {
+ /* send a request_status. This toggles w_cmd_count and r_cmd_count */
+ u_char param[3];
+ u_char result[5];
+ SANE_Status status;
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->request_status;
+ param[2]='\0';
+ send(s,param,2,&status);
+ receive(s,result,4,&status);
+ }
+
+
+ DBG (5, "w_cmd_count = %d\n",w_cmd_count);
+ DBG (5, "r_cmd_count = %d\n",r_cmd_count);
+
+ if (w_cmd_count % 2)
+ {
+ int junk1,junk2;
+
+ /* check extended status. This toggles w_cmd_count%2 only */
+ check_ext_status (s,&junk1,&junk2);
+ }
+
+ DBG (5, "w_cmd_count = %d\n",w_cmd_count);
+ DBG (5, "r_cmd_count = %d\n",r_cmd_count);
+
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ sanei_scsi_close (s->fd);
+ }
+ else if (s->hw->connection == SANE_EPSON_PIO)
+ {
+ sanei_pio_close (s->fd);
+ }
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ sanei_usb_close (s->fd);
+ }
+
+ s->fd = -1;
+ return;
+}
+
+/*
+ * open_scanner()
+ *
+ * Open the scanner device. Depending on the connection method,
+ * different open functions are called.
+ */
+
+static SANE_Status
+open_scanner (Epson_Scanner * s)
+{
+ SANE_Status status = 0;
+
+ DBG (5, "open_scanner()\n");
+
+ if (s->fd != -1)
+ {
+ DBG (5, "scanner is already open: fd = %d\n", s->fd);
+ return SANE_STATUS_GOOD; /* no need to open the scanner */
+ }
+
+ /* don't do this for OS2: */
+#ifndef HAVE_OS2_H
+#if 0
+ /* test the device name */
+ if ((s->hw->connection != SANE_EPSON_PIO)
+ && (access (s->hw->sane.name, R_OK | W_OK) != 0))
+ {
+ DBG (1, "sane_start: access(%s, R_OK | W_OK) failed\n", s->hw->sane.name);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+#endif
+#endif
+
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd,
+ sanei_epson_scsi_sense_handler, NULL);
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: %s open failed: %s\n", s->hw->sane.name,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else if (s->hw->connection == SANE_EPSON_PIO)
+ {
+ status = sanei_pio_open (s->hw->sane.name, &s->fd);
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: %s open failed: %s\n", s->hw->sane.name,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ status = sanei_usb_open (s->hw->sane.name, &s->fd);
+
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ * feed ( )
+ */
+
+static SANE_Status
+feed (Epson_Scanner * s)
+{
+ SANE_Status status;
+ u_char params[2];
+ u_char cmd = s->hw->cmd->feed;
+
+ DBG (5, "feed()\n");
+
+ if (!cmd)
+ {
+ DBG (5, "feed() is not supported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ params[0] = cmd;
+
+ send (s, params, 1, &status);
+
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ {
+ close_scanner (s);
+ return status;
+ }
+
+ return status;
+}
+
+
+/*
+ * eject()
+ *
+ * Eject the current page from the ADF. The scanner is opened prior to
+ * sending the command and closed afterwards.
+ *
+ */
+
+static SANE_Status
+eject (Epson_Scanner * s)
+{
+ SANE_Status status;
+ u_char params[2];
+ u_char cmd = s->hw->cmd->eject;
+ SANE_Bool needToClose = SANE_FALSE;
+
+ DBG (5, "eject()\n");
+
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (s->fd == -1)
+ {
+ needToClose = SANE_TRUE;
+ if (SANE_STATUS_GOOD != (status = open_scanner (s)))
+ return status;
+ }
+
+ params[0] = cmd;
+
+ send (s, params, 1, &status);
+
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ {
+ close_scanner (s);
+ return status;
+ }
+
+ if (needToClose)
+ close_scanner (s);
+
+ return status;
+}
+
+/*
+ *
+ *
+ */
+
+static int num_devices = 0; /* number of EPSON scanners attached to backend */
+static Epson_Device *first_dev = NULL; /* first EPSON scanner in list */
+static Epson_Scanner *first_handle = NULL;
+
+
+static EpsonHdr
+command (Epson_Scanner * s, u_char * cmd, size_t cmd_size,
+ SANE_Status * status)
+{
+ EpsonHdr head;
+ u_char *buf;
+ int count;
+
+ if (NULL == (head = walloc (EpsonHdrRec)))
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ *status = SANE_STATUS_NO_MEM;
+ return (EpsonHdr) 0;
+ }
+
+ send (s, cmd, cmd_size, status);
+
+ if (SANE_STATUS_GOOD != *status)
+ {
+ /* this is necessary for the GT-8000. I don't know why, but
+ it seems to fix the problem. It should not have any
+ ill effects on other scanners. */
+ *status = SANE_STATUS_GOOD;
+ send (s, cmd, cmd_size, status);
+ if (SANE_STATUS_GOOD != *status)
+ return (EpsonHdr) 0;
+ }
+
+ buf = (u_char *) head;
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ receive (s, buf, 4, status);
+ buf += 4;
+ }
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ int bytes_read;
+ bytes_read = receive (s, buf, 4, status);
+ buf += bytes_read;
+ }
+ else
+ {
+ receive (s, buf, 1, status);
+ buf += 1;
+ }
+
+ if (SANE_STATUS_GOOD != *status)
+ return (EpsonHdr) 0;
+
+ DBG (4, "code %02x\n", (int) head->code);
+
+ switch (head->code)
+ {
+
+ case NAK:
+ /* fall through */
+ /* !!! is this really sufficient to report an error ? */
+ case ACK:
+ break; /* no need to read any more data after ACK or NAK */
+
+ case STX:
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ /* nope */
+ }
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ /* we've already read the complete data */
+ }
+ else
+ {
+ receive (s, buf, 3, status);
+ /* buf += 3; */
+ }
+
+ if (SANE_STATUS_GOOD != *status)
+ return (EpsonHdr) 0;
+
+ DBG (4, "status %02x\n", (int) head->status);
+
+ count = head->count2 * 255 + head->count1;
+ DBG (4, "count %d\n", count);
+
+ if (NULL == (head = realloc (head, sizeof (EpsonHdrRec) + count)))
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ *status = SANE_STATUS_NO_MEM;
+ return (EpsonHdr) 0;
+ }
+
+ buf = head->buf;
+ receive (s, buf, count, status);
+
+ if (SANE_STATUS_GOOD != *status)
+ return (EpsonHdr) 0;
+
+ break;
+
+ default:
+ if (0 == head->code)
+ DBG (1, "Incompatible printer port (probably bi/directional)\n");
+ else if (cmd[cmd_size - 1] == head->code)
+ DBG (1, "Incompatible printer port (probably not bi/directional)\n");
+
+ DBG (2, "Illegal response of scanner for command: %02x\n", head->code);
+ break;
+ }
+
+ return head;
+}
+
+
+/*
+ * static SANE_Status attach()
+ *
+ * Attach one device with name *dev_name to the backend.
+ */
+
+static SANE_Status
+attach (const char *dev_name, Epson_Device * *devp, int type)
+{
+ SANE_Status status;
+ Epson_Scanner *s = walloca (Epson_Scanner);
+ char *str;
+ struct Epson_Device *dev;
+ SANE_String_Const *source_list_add = source_list;
+ int port;
+
+ DBG (1, "%s\n", SANE_EPSON_VERSION);
+
+ DBG (5, "attach(%s, %d)\n", dev_name, type);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, dev_name) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* check for PIO devices */
+ /* can we convert the device name to an integer? This is only possible
+ with PIO devices */
+ port = atoi (dev_name);
+ if (port != 0)
+ {
+ type = SANE_EPSON_PIO;
+ }
+
+ if (strncmp
+ (dev_name, SANE_EPSON_CONFIG_PIO, strlen (SANE_EPSON_CONFIG_PIO)) == 0)
+ {
+ /* we have a match for the PIO string and adjust the device name */
+ dev_name += strlen (SANE_EPSON_CONFIG_PIO);
+ dev_name = sanei_config_skip_whitespace (dev_name);
+ type = SANE_EPSON_PIO;
+ }
+
+
+ /*
+ * set dummy values.
+ */
+
+ s->hw = dev;
+ s->hw->sane.name = NULL;
+ s->hw->sane.type = "flatbed scanner";
+ s->hw->sane.vendor = "Epson";
+ s->hw->sane.model = NULL;
+ s->hw->optical_res = 0; /* just to have it initialized */
+ s->hw->color_shuffle = SANE_FALSE;
+ s->hw->extension = SANE_FALSE;
+ s->hw->use_extension = SANE_FALSE;
+
+ s->hw->need_color_reorder = SANE_FALSE;
+ s->hw->need_double_vertical = SANE_FALSE;
+
+ s->hw->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; /* default function level */
+ s->hw->connection = type;
+
+ DBG (3, "attach: opening %s\n", dev_name);
+
+ s->hw->last_res = 0;
+ s->hw->last_res_preview = 0; /* set resolution to safe values */
+
+ /*
+ * decide if interface is USB, SCSI or parallel.
+ */
+
+ /*
+ * if interface is SCSI do an inquiry.
+ */
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ {
+ u_char buf[INQUIRY_BUF_SIZE + 1];
+ size_t buf_size = INQUIRY_BUF_SIZE;
+
+ status =
+ sanei_scsi_open (dev_name, &s->fd, sanei_epson_scsi_sense_handler,
+ NULL);
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ DBG (3, "attach: sending INQUIRY\n");
+
+ status = sanei_epson_scsi_inquiry (s->fd, 0, buf, &buf_size);
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ close_scanner (s);
+ return status;
+ }
+
+ buf[INQUIRY_BUF_SIZE] = 0;
+ DBG (1, ">%s<\n", buf + 8);
+
+ /*
+ * For USB and PIO scanners this will be done later, once
+ * we have communication established with the device.
+ */
+
+ if (buf[0] != TYPE_PROCESSOR
+ || strncmp ((char *) (buf + 8), "EPSON", 5) != 0
+ || (strncmp ((char *) buf + 16, "SCANNER ", 8) != 0
+ && strncmp ((char *) buf + 14, "SCANNER ", 8) != 0
+ && strncmp ((char *) buf + 14, "Perfection", 10) != 0
+ && strncmp ((char *) buf + 16, "Perfection", 10) != 0
+ && strncmp ((char *) buf + 16, "Expression", 10) != 0
+ && strncmp ((char *) buf + 16, "GT", 2) != 0))
+ {
+ DBG (1, "attach: device doesn't look like an EPSON scanner\n");
+ close_scanner (s);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ /* use the SANEI functions to handle a PIO device */
+ else if (s->hw->connection == SANE_EPSON_PIO)
+ {
+ if (SANE_STATUS_GOOD != (status = sanei_pio_open (dev_name, &s->fd)))
+ {
+ DBG (1, "Cannot open %s as a parallel-port device: %s\n",
+ dev_name, sane_strstatus (status));
+ return status;
+ }
+ }
+ /* use the SANEI functions to handle a USB device */
+ else if (s->hw->connection == SANE_EPSON_USB)
+ {
+ SANE_Word vendor;
+ SANE_Word product;
+ SANE_Bool isLibUSB;
+
+ isLibUSB = (strncmp (dev_name, "libusb:", strlen ("libusb:")) == 0);
+
+ if ((!isLibUSB) && (strlen (dev_name) == 0))
+ {
+ int i;
+ int numIds;
+
+ numIds = sanei_epson_getNumberOfUSBProductIds ();
+
+ for (i = 0; i < numIds; i++)
+ {
+ product = sanei_epson_usb_product_ids[i];
+ vendor = 0x4b8;
+
+ status = sanei_usb_find_devices (vendor, product, attach_one_usb);
+ }
+ return SANE_STATUS_INVAL; /* return - the attach_one_usb()
+ will take care of this */
+ }
+
+ status = sanei_usb_open (dev_name, &s->fd);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ /* if the sanei_usb_get_vendor_product call is not supported,
+ then we just ignore this and rely on the user to config
+ the correct device.
+ */
+
+ if (sanei_usb_get_vendor_product (s->fd, &vendor, &product) ==
+ SANE_STATUS_GOOD)
+ {
+ int i; /* loop variable */
+ int numIds;
+ SANE_Bool is_valid;
+
+ /* check the vendor ID to see if we are dealing with an EPSON device */
+ if (vendor != SANE_EPSON_VENDOR_ID)
+ {
+ /* this is not a supported vendor ID */
+ DBG (1,
+ "The device at %s is not manufactured by EPSON (vendor id=0x%x)\n",
+ dev_name, vendor);
+ sanei_usb_close (s->fd);
+ s->fd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ numIds = sanei_epson_getNumberOfUSBProductIds ();
+ is_valid = SANE_FALSE;
+ i = 0;
+
+ /* check all known product IDs to verify that we know
+ about the device */
+ while (i != numIds && !is_valid)
+ {
+ if (product == sanei_epson_usb_product_ids[i])
+ is_valid = SANE_TRUE;
+
+ i++;
+ }
+
+ if (is_valid == SANE_FALSE)
+ {
+ DBG (1,
+ "The device at %s is not a supported EPSON scanner (product id=0x%x)\n",
+ dev_name, product);
+ sanei_usb_close (s->fd);
+ s->fd = -1;
+ return SANE_STATUS_INVAL;
+ }
+ DBG (1, "Found valid EPSON scanner: 0x%x/0x%x (vendorID/productID)\n",
+ vendor, product);
+ }
+ else
+ {
+ DBG (1,
+ "Cannot use IOCTL interface to verify that device is a scanner - will continue\n");
+ }
+ }
+
+ /*
+ * Initialize the scanner (ESC @).
+ */
+ reset (s);
+
+
+
+ /*
+ * Identification Request (ESC I).
+ */
+ if (s->hw->cmd->request_identity != 0)
+ {
+ status = get_identity_information (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ } /* request identity */
+
+
+ /*
+ * Check for "Request Identity 2" command. If this command is available
+ * get the information from the scanner and store it in dev
+ */
+
+ if (s->hw->cmd->request_identity2 != 0)
+ {
+ status = get_identity2_information (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ } /* request identity 2 */
+
+
+ /*
+ * Check for the max. supported color depth and assign
+ * the values to the bitDepthList.
+ */
+
+ bitDepthList = malloc (sizeof (SANE_Word) * 4);
+ if (bitDepthList == NULL)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ bitDepthList[0] = 1; /* we start with one element in the list */
+ bitDepthList[1] = 8; /* 8bit is the default */
+
+ if (set_data_format (s, 16) == SANE_STATUS_GOOD)
+ {
+ s->hw->maxDepth = 16;
+
+ bitDepthList[0]++;
+ bitDepthList[bitDepthList[0]] = 16;
+
+ }
+ else if (set_data_format (s, 14) == SANE_STATUS_GOOD)
+ {
+ s->hw->maxDepth = 14;
+
+ bitDepthList[0]++;
+ bitDepthList[bitDepthList[0]] = 14;
+ }
+ else if (set_data_format (s, 12) == SANE_STATUS_GOOD)
+ {
+ s->hw->maxDepth = 12;
+
+ bitDepthList[0]++;
+ bitDepthList[bitDepthList[0]] = 12;
+ }
+ else
+ {
+ s->hw->maxDepth = 8;
+
+ /* the default depth is already in the list */
+ }
+
+ DBG (1, "Max. supported color depth = %d\n", s->hw->maxDepth);
+
+
+ /*
+ * Check for "request focus position" command. If this command is
+ * supported, then the scanner does also support the "set focus
+ * position" command.
+ */
+
+ if (request_focus_position (s, &s->currentFocusPosition) ==
+ SANE_STATUS_GOOD)
+ {
+ DBG (1, "Enabling 'Set Focus' support\n");
+ s->hw->focusSupport = SANE_TRUE;
+ s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
+
+ /* reflect the current focus position in the GUI */
+ if (s->currentFocusPosition < 0x4C)
+ {
+ /* focus on glass */
+ s->val[OPT_FOCUS].w = 0;
+ }
+ else
+ {
+ /* focus 2.5mm above glass */
+ s->val[OPT_FOCUS].w = 1;
+ }
+
+ }
+ else
+ {
+ DBG (1, "Disabling 'Set Focus' support\n");
+ s->hw->focusSupport = SANE_FALSE;
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_FOCUS].w = 0; /* on glass - just in case */
+ }
+
+
+
+/*
+ * Set defaults for no extension.
+ */
+
+ dev->x_range = &dev->fbf_x_range;
+ dev->y_range = &dev->fbf_y_range;
+
+/*
+ * Correct for a firmware bug in some Perfection 1650 scanners:
+ * Firmware version 1.08 reports only half the vertical scan area, we have
+ * to double the number. To find out if we have to do this, we just compare
+ * is the vertical range is smaller than the horizontal range.
+ */
+
+ if ((dev->x_range->max - dev->x_range->min) >
+ (dev->y_range->max - dev->y_range->min))
+ {
+ dev->y_range->max += (dev->y_range->max - dev->y_range->min);
+ dev->need_double_vertical = SANE_TRUE;
+ dev->need_color_reorder = SANE_TRUE;
+ }
+
+
+/*
+ * Extended status flag request (ESC f).
+ * this also requests the scanner device name from the the scanner
+ */
+ /*
+ * because we are also using the device name from this command,
+ * we have to run this block even if the scanner does not report
+ * an extension. The extensions are only reported if the ADF or
+ * the TPU are actually detected.
+ */
+ if (s->hw->cmd->request_extended_status != 0)
+ {
+ u_char *buf;
+ u_char params[2];
+ EpsonHdr head;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_extended_status;
+
+ if (NULL == (head = (EpsonHdr) command (s, params, 2, &status)))
+ {
+ DBG (1, "Extended status flag request failed\n");
+ dev->sane.model = strdup ("Unknown model");
+ *source_list_add++ = FBF_STR;
+ }
+ else
+ {
+ buf = &head->buf[0];
+
+/*
+ * Add the flatbed option to the source list
+ */
+
+ *source_list_add++ = FBF_STR;
+
+ s->hw->devtype = buf[11] >> 6;
+
+/*
+ * Get the device name and copy it to dev->sane.model.
+ * The device name starts at buf[0x1A] and is up to 16 bytes long
+ * We are overwriting whatever was set previously!
+ */
+ {
+ char device_name[DEVICE_NAME_LEN + 1];
+ char *end_ptr;
+ int len;
+
+ /* make sure that the end of string is marked */
+ device_name[DEVICE_NAME_LEN] = '\0';
+
+ /* copy the string to an area where we can work with it */
+ memcpy (device_name, buf + 0x1A, DEVICE_NAME_LEN);
+ end_ptr = strchr (device_name, ' ');
+ if (end_ptr != NULL)
+ {
+ *end_ptr = '\0';
+ }
+
+ len = strlen (device_name);
+
+ str = malloc (len + 1);
+ str[len] = '\0';
+
+ dev->sane.model = (char *) memcpy (str, device_name, len);
+ }
+/*
+ * ADF
+ */
+
+ if (dev->extension && (buf[1] & EXT_STATUS_IST))
+ {
+ DBG (1, "ADF detected\n");
+
+ /* the GT-30000 does not report the ADF scan area */
+ if ((strcmp (dev->sane.model, "GT-30000") == 0) ||
+ (strcmp (dev->sane.model, "ES-9000H") == 0))
+ {
+ fix_up_extended_status_reply ((const char *) buf + 26, buf);
+
+ dev->duplexSupport = (buf[0] & 0x10) != 0;
+ if (dev->duplexSupport)
+ {
+ DBG (1, "Found DUPLEX ADF\n");
+ }
+
+
+
+
+ }
+
+ if (buf[1] & EXT_STATUS_EN)
+ {
+ DBG (1, "ADF is enabled\n");
+ dev->x_range = &dev->adf_x_range;
+ dev->y_range = &dev->adf_y_range;
+ }
+
+ dev->adf_x_range.min = 0;
+ dev->adf_x_range.max =
+ SANE_FIX ((buf[3] << 8 | buf[2]) * 25.4 / dev->dpi_range.max);
+ dev->adf_x_range.quant = 0;
+
+ dev->adf_max_x = buf[3] << 8 | buf[2];
+
+ dev->adf_y_range.min = 0;
+ dev->adf_y_range.max =
+ SANE_FIX ((buf[5] << 8 | buf[4]) * 25.4 / dev->dpi_range.max);
+ dev->adf_y_range.quant = 0;
+
+ dev->adf_max_y = buf[5] << 8 | buf[4];
+
+ DBG (5, "adf tlx %f tly %f brx %f bry %f [mm]\n",
+ SANE_UNFIX (dev->adf_x_range.min),
+ SANE_UNFIX (dev->adf_y_range.min),
+ SANE_UNFIX (dev->adf_x_range.max),
+ SANE_UNFIX (dev->adf_y_range.max));
+
+ *source_list_add++ = ADF_STR;
+
+ dev->ADF = SANE_TRUE;
+ }
+
+
+/*
+ * TPU
+ */
+
+ if (dev->extension && (buf[6] & EXT_STATUS_IST))
+ {
+ DBG (1, "TPU detected\n");
+
+ if (buf[6] & EXT_STATUS_EN)
+ {
+ DBG (1, "TPU is enabled\n");
+ dev->x_range = &dev->tpu_x_range;
+ dev->y_range = &dev->tpu_y_range;
+ }
+
+ dev->tpu_x_range.min = 0;
+ dev->tpu_x_range.max =
+ SANE_FIX ((buf[8] << 8 | buf[7]) * 25.4 / dev->dpi_range.max);
+ dev->tpu_x_range.quant = 0;
+
+ dev->tpu_y_range.min = 0;
+ dev->tpu_y_range.max =
+ SANE_FIX ((buf[10] << 8 | buf[9]) * 25.4 / dev->dpi_range.max);
+ dev->tpu_y_range.quant = 0;
+
+ /*
+ * Check for Perfection 4990 photo/GT-X800 scanner.
+ * This scanner only report 3200 dpi back.
+ * The scanner fysical supports 4800 dpi.
+ * This is simulated here...
+ * Futher details read:
+ * EPSON Programming guide for EPSON Color Image Scanner Perfection 4990
+ */
+ if (strncmp((char *) buf + 0x1A,"GT-X800",7) == 0)
+ {
+ dev->tpu_x_range.max = (dev->tpu_x_range.max/32)*48;
+ dev->tpu_y_range.max = (dev->tpu_y_range.max/32)*48;
+ DBG (5, "dpi_range.max %x \n", dev->dpi_range.max);
+ }
+
+ DBG (5, "tpu tlx %f tly %f brx %f bry %f [mm]\n",
+ SANE_UNFIX (dev->tpu_x_range.min),
+ SANE_UNFIX (dev->tpu_y_range.min),
+ SANE_UNFIX (dev->tpu_x_range.max),
+ SANE_UNFIX (dev->tpu_y_range.max));
+
+ *source_list_add++ = TPU_STR;
+
+ dev->TPU = SANE_TRUE;
+ }
+
+/*
+ * Get the device name and copy it to dev->sane.model.
+ * The device name starts at buf[0x1A] and is up to 16 bytes long
+ * We are overwriting whatever was set previously!
+ */
+ {
+ char device_name[DEVICE_NAME_LEN + 1];
+ char *end_ptr;
+ int len;
+
+ /* make sure that the end of string is marked */
+ device_name[DEVICE_NAME_LEN] = '\0';
+
+ /* copy the string to an area where we can work with it */
+ memcpy (device_name, buf + 0x1A, DEVICE_NAME_LEN);
+ end_ptr = strchr (device_name, ' ');
+ if (end_ptr != NULL)
+ {
+ *end_ptr = '\0';
+ }
+
+ len = strlen (device_name);
+
+ str = malloc (len + 1);
+ str[len] = '\0';
+
+ /* finally copy the device name to the structure */
+ dev->sane.model = (char *) memcpy (str, device_name, len);
+ }
+ }
+ }
+ else /* command is not known */
+ {
+ dev->sane.model = strdup ("EPSON Scanner");
+ }
+
+ *source_list_add = NULL; /* add end marker to source list */
+
+ DBG (1, "scanner model: %s\n", dev->sane.model);
+
+ /* establish defaults */
+ s->hw->need_reset_on_source_change = SANE_FALSE;
+
+ if (strcmp ("ES-9000H", dev->sane.model) == 0 ||
+ strcmp ("GT-30000", dev->sane.model) == 0)
+ {
+ s->hw->cmd->set_focus_position = 0;
+ s->hw->cmd->feed = 0x19;
+ }
+ else if (strcmp ("GT-8200", dev->sane.model) == 0 ||
+ strcmp ("Perfection1650", dev->sane.model) == 0 ||
+ strcmp ("Perfection1640", dev->sane.model) == 0 ||
+ strcmp ("GT-8700", dev->sane.model) == 0)
+ {
+ s->hw->cmd->feed = 0;
+ s->hw->cmd->set_focus_position = 0;
+ s->hw->need_reset_on_source_change = SANE_TRUE;
+ }
+
+
+/*
+ * Set values for quick format "max" entry.
+ */
+
+ qf_params[XtNumber (qf_params) - 1].tl_x = dev->x_range->min;
+ qf_params[XtNumber (qf_params) - 1].tl_y = dev->y_range->min;
+ qf_params[XtNumber (qf_params) - 1].br_x = dev->x_range->max;
+ qf_params[XtNumber (qf_params) - 1].br_y = dev->y_range->max;
+
+
+/*
+ * Now we can finally set the device name:
+ */
+ str = malloc (strlen (dev_name) + 1);
+ dev->sane.name = strcpy (str, dev_name);
+
+ close_scanner (s);
+
+ /*
+ * we are done with this one, prepare for the next scanner:
+ */
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/*
+ * attach_one()
+ *
+ * Part of the SANE API: Attaches the scanner with the device name in *dev.
+ */
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (5, "attach_one(%s)\n", dev);
+
+ return attach (dev, 0, SANE_EPSON_SCSI);
+}
+
+SANE_Status
+attach_one_usb (SANE_String_Const devname)
+{
+ int len = strlen (devname);
+ char *attach_string;
+
+ DBG (5, "attach_one_usb(%s)\n", devname);
+
+ attach_string = alloca (len + 5);
+ if (attach_string == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ return attach (devname, 0, SANE_EPSON_USB);
+}
+
+/*
+ * sane_init()
+ *
+ *
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* get rid of compiler warning */
+
+ /* sanei_authorization(devicename, STRINGIFY(BACKEND_NAME), auth_callback); */
+
+ DBG_INIT ();
+#if defined PACKAGE && defined VERSION
+ DBG (2, "sane_init: " PACKAGE " " VERSION "\n");
+#endif
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, SANE_EPSON_BUILD);
+
+ sanei_usb_init ();
+
+ /* default to /dev/scanner instead of insisting on config file */
+ if ((fp = sanei_config_open (EPSON_CONFIG_FILE)))
+ {
+ char line[PATH_MAX];
+
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ int vendor, product;
+
+ DBG (4, "sane_init, >%s<\n", line);
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+ if (!len)
+ continue; /* ignore empty lines */
+
+ if (sscanf (line, "usb %i %i", &vendor, &product) == 2)
+ {
+ int numIds;
+
+ /* add the vendor and product IDs to the list of
+ known devices before we call the attach function */
+ numIds = sanei_epson_getNumberOfUSBProductIds ();
+ if (vendor != 0x4b8)
+ continue; /* this is not an EPSON device */
+
+ sanei_epson_usb_product_ids[numIds - 1] = product;
+ sanei_usb_attach_matching_devices (line, attach_one_usb);
+ }
+ else if (strncmp (line, "usb", 3) == 0)
+ {
+ const char *dev_name;
+ /* remove the "usb" sub string */
+ dev_name = sanei_config_skip_whitespace (line + 3);
+ attach_one_usb (dev_name);
+ }
+ else
+ {
+ sanei_config_attach_matching_devices (line, attach_one);
+ }
+ }
+ fclose (fp);
+ }
+
+ /* read the option section and assign the connection type to the
+ scanner structure - which we don't have at this time. So I have
+ to come up with something :-) */
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * void sane_exit(void)
+ *
+ * Clean up the list of attached scanners.
+ */
+
+void
+sane_exit (void)
+{
+ Epson_Device *dev, *next;
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+
+ free (devlist);
+}
+
+/*
+ *
+ *
+ */
+
+SANE_Status
+sane_get_devices (const SANE_Device * **device_list, SANE_Bool local_only)
+{
+ Epson_Device *dev;
+ int i;
+
+ DBG (5, "sane_get_devices()\n");
+
+ local_only = local_only; /* just to get rid of the compiler warning */
+
+ if (devlist)
+ {
+ free (devlist);
+ }
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ {
+ devlist[i++] = &dev->sane;
+ }
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ *
+ *
+ */
+
+static SANE_Status
+init_options (Epson_Scanner * s)
+{
+ int i;
+ SANE_Bool dummy;
+
+ DBG (5, "init_options()\n");
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 0; /* Binary */
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitDepthList;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = bitDepthList[1]; /* the first "real" element is the default */
+
+ if (bitDepthList[0] == 1) /* only one element in the list -> hide the option */
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ /* halftone */
+ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
+ s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ s->opt[OPT_HALFTONE].desc = SANE_I18N ("Selects the halftone.");
+
+ s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE].size = max_string_size (halftone_list_7);
+ s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+
+ if (s->hw->level >= 7)
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7;
+ else if (s->hw->level >= 4)
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4;
+ else
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
+
+ s->val[OPT_HALFTONE].w = 1; /* Halftone A */
+
+ if (!s->hw->cmd->set_halftoning)
+ {
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* dropout */
+ s->opt[OPT_DROPOUT].name = "dropout";
+ s->opt[OPT_DROPOUT].title = SANE_I18N ("Dropout");
+ s->opt[OPT_DROPOUT].desc = SANE_I18N ("Selects the dropout.");
+
+ s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING;
+ s->opt[OPT_DROPOUT].size = max_string_size (dropout_list);
+ s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_DROPOUT].constraint.string_list = dropout_list;
+ s->val[OPT_DROPOUT].w = 0; /* None */
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_I18N ("Selects the brightness.");
+
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range;
+ s->val[OPT_BRIGHTNESS].w = 0; /* Normal */
+
+ if (!s->hw->cmd->set_bright)
+ {
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* sharpness */
+ s->opt[OPT_SHARPNESS].name = "sharpness";
+ s->opt[OPT_SHARPNESS].title = SANE_I18N ("Sharpness");
+ s->opt[OPT_SHARPNESS].desc = "";
+
+ s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range;
+ s->val[OPT_SHARPNESS].w = 0; /* Normal */
+
+ if (!s->hw->cmd->set_outline_emphasis)
+ {
+ s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* gamma */
+ s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION;
+ s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION;
+ s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION;
+
+ s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ /*
+ * special handling for D1 function level - at this time I'm not
+ * testing for D1, I'm just assuming that all D level scanners will
+ * behave the same way. This has to be confirmed with the next D-level
+ * scanner
+ */
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_d);
+ s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_d;
+ s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */
+ gamma_userdefined = gamma_userdefined_d;
+ gamma_params = gamma_params_d;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_ab);
+ s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_ab;
+ s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */
+ gamma_userdefined = gamma_userdefined_ab;
+ gamma_params = gamma_params_ab;
+ }
+
+ if (!s->hw->cmd->set_gamma)
+ {
+ s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* gamma vector */
+/*
+ s->opt[ OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[ OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[ OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+
+ s->opt[ OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[ OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[ OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[ OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[ OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[ OPT_GAMMA_VECTOR].wa = &s->gamma_table [ 0] [ 0];
+*/
+
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0];
+
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0];
+
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0];
+
+ if (s->hw->cmd->set_gamma_table &&
+ gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w] == SANE_TRUE)
+ {
+/* s->opt[ OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; */
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+/* s->opt[ OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; */
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* initialize the Gamma tables */
+ memset (&s->gamma_table[0], 0, 256 * sizeof (SANE_Word));
+ memset (&s->gamma_table[1], 0, 256 * sizeof (SANE_Word));
+ memset (&s->gamma_table[2], 0, 256 * sizeof (SANE_Word));
+/* memset(&s->gamma_table[3], 0, 256 * sizeof(SANE_Word)); */
+ for (i = 0; i < 256; i++)
+ {
+ s->gamma_table[0][i] = i;
+ s->gamma_table[1][i] = i;
+ s->gamma_table[2][i] = i;
+/* s->gamma_table[3][i] = i; */
+ }
+
+
+ /* color correction */
+ s->opt[OPT_COLOR_CORRECTION].name = "color-correction";
+ s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N ("Color correction");
+ s->opt[OPT_COLOR_CORRECTION].desc =
+ SANE_I18N
+ ("Sets the color correction table for the selected output device.");
+
+ s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_COLOR_CORRECTION].size = 32;
+ s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list;
+ s->val[OPT_COLOR_CORRECTION].w = 5; /* scanner default: CRT monitors */
+
+ if (!s->hw->cmd->set_color_correction)
+ {
+ s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->resolution_list;
+ s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->val[OPT_THRESHOLD].w = 0x80;
+
+ if (!s->hw->cmd->set_threshold)
+ {
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_CCT_GROUP].title = SANE_I18N ("Color correction coefficients");
+ s->opt[OPT_CCT_GROUP].desc = SANE_I18N ("Matrix multiplication of RGB");
+ s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED;
+
+
+ /* color correction coefficients */
+ s->opt[OPT_CCT_1].name = "cct-1";
+ s->opt[OPT_CCT_2].name = "cct-2";
+ s->opt[OPT_CCT_3].name = "cct-3";
+ s->opt[OPT_CCT_4].name = "cct-4";
+ s->opt[OPT_CCT_5].name = "cct-5";
+ s->opt[OPT_CCT_6].name = "cct-6";
+ s->opt[OPT_CCT_7].name = "cct-7";
+ s->opt[OPT_CCT_8].name = "cct-8";
+ s->opt[OPT_CCT_9].name = "cct-9";
+
+ s->opt[OPT_CCT_1].title = SANE_I18N ("Green");
+ s->opt[OPT_CCT_2].title = SANE_I18N ("Shift green to red");
+ s->opt[OPT_CCT_3].title = SANE_I18N ("Shift green to blue");
+ s->opt[OPT_CCT_4].title = SANE_I18N ("Shift red to green");
+ s->opt[OPT_CCT_5].title = SANE_I18N ("Red");
+ s->opt[OPT_CCT_6].title = SANE_I18N ("Shift red to blue");
+ s->opt[OPT_CCT_7].title = SANE_I18N ("Shift blue to green");
+ s->opt[OPT_CCT_8].title = SANE_I18N ("Shift blue to red");
+ s->opt[OPT_CCT_9].title = SANE_I18N ("Blue");
+
+ s->opt[OPT_CCT_1].desc = SANE_I18N ("Controls green level");
+ s->opt[OPT_CCT_2].desc = SANE_I18N ("Adds to red based on green level");
+ s->opt[OPT_CCT_3].desc = SANE_I18N ("Adds to blue based on green level");
+ s->opt[OPT_CCT_4].desc = SANE_I18N ("Adds to green based on red level");
+ s->opt[OPT_CCT_5].desc = SANE_I18N ("Controls red level");
+ s->opt[OPT_CCT_6].desc = SANE_I18N ("Adds to blue based on red level");
+ s->opt[OPT_CCT_7].desc = SANE_I18N ("Adds to green based on blue level");
+ s->opt[OPT_CCT_8].desc = SANE_I18N ("Adds to red based on blue level");
+ s->opt[OPT_CCT_9].desc = SANE_I18N ("Controls blue level");
+
+ s->opt[OPT_CCT_1].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_2].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_3].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_4].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_5].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_6].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_7].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_8].type = SANE_TYPE_INT;
+ s->opt[OPT_CCT_9].type = SANE_TYPE_INT;
+
+ s->opt[OPT_CCT_1].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_2].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_3].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_4].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_5].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_6].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_7].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_8].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_9].cap |= SANE_CAP_ADVANCED;
+
+ s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_CCT_1].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_2].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_3].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_4].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_5].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_6].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_7].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_8].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_9].unit = SANE_UNIT_NONE;
+
+ s->opt[OPT_CCT_1].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_2].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_3].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_4].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_5].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_6].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_7].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_8].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_9].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ s->opt[OPT_CCT_1].constraint.range = &s8_range;
+ s->opt[OPT_CCT_2].constraint.range = &s8_range;
+ s->opt[OPT_CCT_3].constraint.range = &s8_range;
+ s->opt[OPT_CCT_4].constraint.range = &s8_range;
+ s->opt[OPT_CCT_5].constraint.range = &s8_range;
+ s->opt[OPT_CCT_6].constraint.range = &s8_range;
+ s->opt[OPT_CCT_7].constraint.range = &s8_range;
+ s->opt[OPT_CCT_8].constraint.range = &s8_range;
+ s->opt[OPT_CCT_9].constraint.range = &s8_range;
+
+ s->val[OPT_CCT_1].w = 32;
+ s->val[OPT_CCT_2].w = 0;
+ s->val[OPT_CCT_3].w = 0;
+ s->val[OPT_CCT_4].w = 0;
+ s->val[OPT_CCT_5].w = 32;
+ s->val[OPT_CCT_6].w = 0;
+ s->val[OPT_CCT_7].w = 0;
+ s->val[OPT_CCT_8].w = 0;
+ s->val[OPT_CCT_9].w = 32;
+
+ if (!s->hw->cmd->set_color_correction_coefficients)
+ {
+ s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* "Advanced" group: */
+ s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N ("Advanced");
+ s->opt[OPT_ADVANCED_GROUP].desc = "";
+ s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+
+
+ /* mirror */
+ s->opt[OPT_MIRROR].name = "mirror";
+ s->opt[OPT_MIRROR].title = SANE_I18N ("Mirror image");
+ s->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror the image.");
+
+ s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
+ s->val[OPT_MIRROR].w = SANE_FALSE;
+
+ if (!s->hw->cmd->mirror_image)
+ {
+ s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* speed */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+
+ s->opt[OPT_SPEED].type = SANE_TYPE_BOOL;
+ s->val[OPT_SPEED].w = SANE_FALSE;
+
+ if (!s->hw->cmd->set_speed)
+ {
+ s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* preview speed */
+ s->opt[OPT_PREVIEW_SPEED].name = "preview-speed";
+ s->opt[OPT_PREVIEW_SPEED].title = SANE_I18N ("Fast preview");
+ s->opt[OPT_PREVIEW_SPEED].desc = "";
+
+ s->opt[OPT_PREVIEW_SPEED].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW_SPEED].w = SANE_FALSE;
+
+ if (!s->hw->cmd->set_speed)
+ {
+ s->opt[OPT_PREVIEW_SPEED].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* auto area segmentation */
+ s->opt[OPT_AAS].name = "auto-area-segmentation";
+ s->opt[OPT_AAS].title = SANE_I18N ("Auto area segmentation");
+ s->opt[OPT_AAS].desc = "";
+
+ s->opt[OPT_AAS].type = SANE_TYPE_BOOL;
+ s->val[OPT_AAS].w = SANE_TRUE;
+
+ if (!s->hw->cmd->control_auto_area_segmentation)
+ {
+ s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* limit resolution list */
+ s->opt[OPT_LIMIT_RESOLUTION].name = "short-resolution";
+ s->opt[OPT_LIMIT_RESOLUTION].title = SANE_I18N ("Short resolution list");
+ s->opt[OPT_LIMIT_RESOLUTION].desc =
+ SANE_I18N ("Display short resolution list");
+ s->opt[OPT_LIMIT_RESOLUTION].type = SANE_TYPE_BOOL;
+ s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE;
+
+
+ /* zoom */
+ s->opt[OPT_ZOOM].name = "zoom";
+ s->opt[OPT_ZOOM].title = SANE_I18N ("Zoom");
+ s->opt[OPT_ZOOM].desc =
+ SANE_I18N ("Defines the zoom factor the scanner will use");
+
+ s->opt[OPT_ZOOM].type = SANE_TYPE_INT;
+ s->opt[OPT_ZOOM].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ZOOM].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ZOOM].constraint.range = &zoom_range;
+ s->val[OPT_ZOOM].w = 100;
+
+/* if( ! s->hw->cmd->set_zoom) */
+ {
+ s->opt[OPT_ZOOM].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* "Preview settings" group: */
+ s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW_GROUP].desc = "";
+ s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ /* Quick format */
+ s->opt[OPT_QUICK_FORMAT].name = "quick-format";
+ s->opt[OPT_QUICK_FORMAT].title = SANE_I18N ("Quick format");
+ s->opt[OPT_QUICK_FORMAT].desc = "";
+
+ s->opt[OPT_QUICK_FORMAT].type = SANE_TYPE_STRING;
+ s->opt[OPT_QUICK_FORMAT].size = max_string_size (qf_list);
+ s->opt[OPT_QUICK_FORMAT].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_QUICK_FORMAT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_QUICK_FORMAT].constraint.string_list = qf_list;
+ s->val[OPT_QUICK_FORMAT].w = XtNumber (qf_params) - 1; /* max */
+
+ /* "Optional equipment" group: */
+ s->opt[OPT_EQU_GROUP].title = SANE_I18N ("Optional equipment");
+ s->opt[OPT_EQU_GROUP].desc = "";
+ s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED;
+
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+
+ if (!s->hw->extension)
+ {
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ }
+ s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */
+
+
+ /* film type */
+ s->opt[OPT_FILM_TYPE].name = "film-type";
+ s->opt[OPT_FILM_TYPE].title = SANE_I18N ("Film type");
+ s->opt[OPT_FILM_TYPE].desc = "";
+
+ s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_FILM_TYPE].size = max_string_size (film_list);
+
+ s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FILM_TYPE].constraint.string_list = film_list;
+
+ s->val[OPT_FILM_TYPE].w = 0;
+
+ deactivateOption (s, OPT_FILM_TYPE, &dummy); /* default is inactive */
+
+ /* focus position */
+ s->opt[OPT_FOCUS].name = SANE_EPSON_FOCUS_NAME;
+ s->opt[OPT_FOCUS].title = SANE_EPSON_FOCUS_TITLE;
+ s->opt[OPT_FOCUS].desc = SANE_EPSON_FOCUS_DESC;
+ s->opt[OPT_FOCUS].type = SANE_TYPE_STRING;
+ s->opt[OPT_FOCUS].size = max_string_size (focus_list);
+ s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FOCUS].constraint.string_list = focus_list;
+ s->val[OPT_FOCUS].w = 0;
+
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED;
+ if (s->hw->focusSupport == SANE_TRUE)
+ {
+ s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+
+#if 0
+ if ((!s->hw->TPU) && (!s->hw->cmd->set_bay))
+ { /* Hack: Using set_bay to indicate. */
+ SANE_Bool dummy;
+ deactivateOption (s, OPT_FILM_TYPE, &dummy);
+
+ }
+#endif
+
+
+ /* forward feed / eject */
+ s->opt[OPT_EJECT].name = "eject";
+ s->opt[OPT_EJECT].title = SANE_I18N ("Eject");
+ s->opt[OPT_EJECT].desc = SANE_I18N ("Eject the sheet in the ADF");
+
+ s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON;
+
+ if ((!s->hw->ADF) && (!s->hw->cmd->set_bay))
+ { /* Hack: Using set_bay to indicate. */
+ s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* auto forward feed / eject */
+ s->opt[OPT_AUTO_EJECT].name = "auto-eject";
+ s->opt[OPT_AUTO_EJECT].title = SANE_I18N ("Auto eject");
+ s->opt[OPT_AUTO_EJECT].desc = SANE_I18N ("Eject document after scanning");
+
+ s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL;
+ s->val[OPT_AUTO_EJECT].w = SANE_FALSE;
+
+ if (!s->hw->ADF)
+ {
+ s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ s->opt[OPT_ADF_MODE].name = "adf_mode";
+ s->opt[OPT_ADF_MODE].title = SANE_I18N ("ADF Mode");
+ s->opt[OPT_ADF_MODE].desc =
+ SANE_I18N ("Selects the ADF mode (simplex/duplex)");
+ s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_ADF_MODE].size = max_string_size (adf_mode_list);
+ s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
+ s->val[OPT_ADF_MODE].w = 0; /* simplex */
+
+ if ((!s->hw->ADF) || (s->hw->duplexSupport == SANE_FALSE))
+ {
+ s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* select bay */
+ s->opt[OPT_BAY].name = "bay";
+ s->opt[OPT_BAY].title = SANE_I18N ("Bay");
+ s->opt[OPT_BAY].desc = SANE_I18N ("Select bay to scan");
+
+ s->opt[OPT_BAY].type = SANE_TYPE_STRING;
+ s->opt[OPT_BAY].size = max_string_size (bay_list);
+ s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BAY].constraint.string_list = bay_list;
+ s->val[OPT_BAY].w = 0; /* Bay 1 */
+
+ if (!s->hw->cmd->set_bay)
+ {
+ s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME;
+ s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE;
+ s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC;
+
+ s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL;
+ s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL;
+ s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED;
+
+ if (!s->hw->cmd->request_push_button_status)
+ {
+ s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ *
+ *
+ */
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Epson_Device *dev;
+ Epson_Scanner *s;
+
+ DBG (5, "sane_open(%s)\n", devicename);
+
+ /* search for device */
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+#if 0
+ status = attach (devicename, &dev, SANE_EPSON_);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+#endif
+ DBG (1, "Error opening the device");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ s = calloc (sizeof (Epson_Scanner), 1);
+ if (!s)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->fd = -1;
+ s->hw = dev;
+
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = (SANE_Handle) s;
+
+ open_scanner (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ *
+ *
+ */
+
+void
+sane_close (SANE_Handle handle)
+{
+ Epson_Scanner *s, *prev;
+
+ /*
+ * Test if there is still data pending from
+ * the scanner. If so, then do a cancel
+ */
+
+ s = (Epson_Scanner *) handle;
+
+ /* remove handle from list of open handles */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+
+ if (!s)
+ {
+ DBG (1, "close: invalid handle (0x%p)\n", handle);
+ return;
+ }
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ if (s->fd != -1)
+ close_scanner (s);
+
+ free (s);
+}
+
+/*
+ *
+ *
+ */
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return NULL;
+
+ return (s->opt + option);
+}
+
+/*
+ *
+ *
+ */
+
+static const SANE_String_Const *
+search_string_list (const SANE_String_Const * list, SANE_String value)
+{
+ while (*list != NULL && strcmp (value, *list) != 0)
+ {
+ ++list;
+ }
+
+ return ((*list == NULL) ? NULL : list);
+}
+
+/*
+ *
+ *
+ */
+
+/*
+ Activate, deactivate an option. Subroutines so we can add
+ debugging info if we want. The change flag is set to TRUE
+ if we changed an option. If we did not change an option,
+ then the value of the changed flag is not modified.
+*/
+
+static void
+activateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change)
+{
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ {
+ s->opt[option].cap &= ~SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+deactivateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change)
+{
+ if (SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ {
+ s->opt[option].cap |= SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+setOptionState (Epson_Scanner * s, SANE_Bool state,
+ SANE_Int option, SANE_Bool * change)
+{
+ if (state)
+ {
+ activateOption (s, option, change);
+ }
+ else
+ {
+ deactivateOption (s, option, change);
+ }
+}
+
+/**
+ End of activateOption, deactivateOption, setOptionState.
+**/
+
+static SANE_Status
+getvalue (SANE_Handle handle, SANE_Int option, void *value)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ switch (option)
+ {
+/* case OPT_GAMMA_VECTOR: */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (value, sval->wa, sopt->size);
+ break;
+
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_MIRROR:
+ case OPT_SPEED:
+ case OPT_PREVIEW_SPEED:
+ case OPT_AAS:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_SHARPNESS:
+ case OPT_AUTO_EJECT:
+ case OPT_CCT_1:
+ case OPT_CCT_2:
+ case OPT_CCT_3:
+ case OPT_CCT_4:
+ case OPT_CCT_5:
+ case OPT_CCT_6:
+ case OPT_CCT_7:
+ case OPT_CCT_8:
+ case OPT_CCT_9:
+ case OPT_THRESHOLD:
+ case OPT_ZOOM:
+ case OPT_BIT_DEPTH:
+ case OPT_WAIT_FOR_BUTTON:
+ case OPT_LIMIT_RESOLUTION:
+ *((SANE_Word *) value) = sval->w;
+ break;
+ case OPT_MODE:
+ case OPT_ADF_MODE:
+ case OPT_HALFTONE:
+ case OPT_DROPOUT:
+ case OPT_QUICK_FORMAT:
+ case OPT_SOURCE:
+ case OPT_FILM_TYPE:
+ case OPT_GAMMA_CORRECTION:
+ case OPT_COLOR_CORRECTION:
+ case OPT_BAY:
+ case OPT_FOCUS:
+ strcpy ((char *) value, sopt->constraint.string_list[sval->w]);
+ break;
+#if 0
+ case OPT_MODEL:
+ strcpy (value, sval->s);
+ break;
+#endif
+
+
+ default:
+ return SANE_STATUS_INVAL;
+
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ End of getvalue.
+**/
+
+
+static void
+handle_depth_halftone (Epson_Scanner * s, SANE_Bool * reload)
+/*
+ This routine handles common options between OPT_MODE and
+ OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS
+ - auto area segmentation, and threshold. Apparently AAS
+ is some method to differentiate between text and photos.
+ Or something like that.
+
+ AAS is available when the scan color depth is 1 and the
+ halftone method is not TET.
+
+ Threshold is available when halftone is NONE, and depth is 1.
+*/
+{
+ int hti = s->val[OPT_HALFTONE].w;
+ int mdi = s->val[OPT_MODE].w;
+ SANE_Bool aas = SANE_FALSE;
+ SANE_Bool thresh = SANE_FALSE;
+
+ if (!s->hw->cmd->control_auto_area_segmentation)
+ return;
+
+ if (mode_params[mdi].depth == 1)
+ {
+ if (halftone_params[hti] != HALFTONE_TET)
+ {
+ aas = SANE_TRUE;
+ }
+ if (halftone_params[hti] == HALFTONE_NONE)
+ {
+ thresh = SANE_TRUE;
+ }
+ }
+ setOptionState (s, aas, OPT_AAS, reload);
+ setOptionState (s, thresh, OPT_THRESHOLD, reload);
+}
+
+/**
+ End of handle_depth_halftone.
+**/
+
+
+static void
+handle_source (Epson_Scanner * s, SANE_Int optindex, char *value)
+/*
+ Handles setting the source (flatbed, transparency adapter (TPU),
+ or auto document feeder (ADF)).
+
+ For newer scanners it also sets the focus according to the
+ glass / TPU settings.
+*/
+{
+ int force_max = SANE_FALSE;
+ SANE_Bool dummy;
+
+ /* reset the scanner when we are changing the source setting -
+ this is necessary for the Perfection 1650 */
+ if (s->hw->need_reset_on_source_change)
+ reset (s);
+
+ s->focusOnGlass = SANE_TRUE; /* this is the default */
+
+ if (s->val[OPT_SOURCE].w == optindex)
+ return;
+
+ s->val[OPT_SOURCE].w = optindex;
+
+ if (s->val[OPT_TL_X].w == s->hw->x_range->min
+ && s->val[OPT_TL_Y].w == s->hw->y_range->min
+ && s->val[OPT_BR_X].w == s->hw->x_range->max
+ && s->val[OPT_BR_Y].w == s->hw->y_range->max)
+ {
+ force_max = SANE_TRUE;
+ }
+ if (strcmp (ADF_STR, value) == 0)
+ {
+ s->hw->x_range = &s->hw->adf_x_range;
+ s->hw->y_range = &s->hw->adf_y_range;
+ s->hw->use_extension = SANE_TRUE;
+ /* disable film type option */
+ deactivateOption (s, OPT_FILM_TYPE, &dummy);
+ s->val[OPT_FOCUS].w = 0;
+ if (s->hw->duplexSupport)
+ {
+ activateOption (s, OPT_ADF_MODE, &dummy);
+ }
+ else
+ {
+ deactivateOption (s, OPT_ADF_MODE, &dummy);
+ s->val[OPT_ADF_MODE].w = 0;
+ }
+ }
+ else if (strcmp (TPU_STR, value) == 0)
+ {
+ s->hw->x_range = &s->hw->tpu_x_range;
+ s->hw->y_range = &s->hw->tpu_y_range;
+ s->hw->use_extension = SANE_TRUE;
+ /* enable film type option only if the scanner supports it */
+ if (s->hw->cmd->set_film_type != 0)
+ {
+ activateOption (s, OPT_FILM_TYPE, &dummy);
+ }
+ else
+ {
+ deactivateOption (s, OPT_FILM_TYPE, &dummy);
+ }
+ /* enable focus position if the scanner supports it */
+ if (s->hw->cmd->set_focus_position != 0)
+ {
+ s->val[OPT_FOCUS].w = 1;
+ s->focusOnGlass = SANE_FALSE;
+ }
+ deactivateOption (s, OPT_ADF_MODE, &dummy);
+ deactivateOption (s, OPT_EJECT, &dummy);
+ deactivateOption (s, OPT_AUTO_EJECT, &dummy);
+ }
+ else /* neither ADF nor TPU active */
+ {
+ s->hw->x_range = &s->hw->fbf_x_range;
+ s->hw->y_range = &s->hw->fbf_y_range;
+ s->hw->use_extension = SANE_FALSE;
+ /* disable film type option */
+ deactivateOption (s, OPT_FILM_TYPE, &dummy);
+ s->val[OPT_FOCUS].w = 0;
+ deactivateOption (s, OPT_ADF_MODE, &dummy);
+ }
+
+ qf_params[XtNumber (qf_params) - 1].tl_x = s->hw->x_range->min;
+ qf_params[XtNumber (qf_params) - 1].tl_y = s->hw->y_range->min;
+ qf_params[XtNumber (qf_params) - 1].br_x = s->hw->x_range->max;
+ qf_params[XtNumber (qf_params) - 1].br_y = s->hw->y_range->max;
+
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+
+ if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
+ s->val[OPT_TL_X].w = s->hw->x_range->min;
+
+ if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
+ s->val[OPT_TL_Y].w = s->hw->y_range->min;
+
+ if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ setOptionState (s, s->hw->ADF && s->hw->use_extension,
+ OPT_AUTO_EJECT, &dummy);
+ setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_EJECT, &dummy);
+
+#if 0
+ BAY is part of the filmscan device.We are not sure
+ if we are really going to support this device in this
+ code.Is there an online manual for it ?
+ setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_BAY, &reload);
+#endif
+}
+
+/**
+ End of handle_source.
+**/
+
+static SANE_Status
+setvalue (SANE_Handle handle, SANE_Int option, void *value, SANE_Int * info)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ SANE_Status status;
+ const SANE_String_Const *optval;
+ int optindex;
+ SANE_Bool reload = SANE_FALSE;
+
+ DBG (5, "setvalue(option = %d, value = %p)\n", option, value);
+
+ status = sanei_constrain_value (sopt, value, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->option_has_changed = SANE_TRUE;
+
+ optval = NULL;
+ optindex = 0;
+
+ if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST)
+ {
+ optval = search_string_list (sopt->constraint.string_list,
+ (char *) value);
+
+ if (optval == NULL)
+ return SANE_STATUS_INVAL;
+ optindex = optval - sopt->constraint.string_list;
+ }
+
+ switch (option)
+ {
+/* case OPT_GAMMA_VECTOR: */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (sval->wa, value, sopt->size); /* Word arrays */
+ break;
+
+ case OPT_CCT_1:
+ case OPT_CCT_2:
+ case OPT_CCT_3:
+ case OPT_CCT_4:
+ case OPT_CCT_5:
+ case OPT_CCT_6:
+ case OPT_CCT_7:
+ case OPT_CCT_8:
+ case OPT_CCT_9:
+ sval->w = *((SANE_Word *) value); /* Simple values */
+ break;
+
+ case OPT_DROPOUT:
+ case OPT_FILM_TYPE:
+ case OPT_BAY:
+ case OPT_FOCUS:
+ sval->w = optindex; /* Simple lists */
+ break;
+
+ case OPT_EJECT:
+/* return eject( s ); */
+ eject (s);
+ break;
+
+ case OPT_RESOLUTION:
+ sval->w = *((SANE_Word *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ sval->w = *((SANE_Word *) value);
+ DBG (1, "set = %f\n", SANE_UNFIX (sval->w));
+ if (NULL != info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_SOURCE:
+ handle_source (s, optindex, (char *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_MODE:
+ {
+ SANE_Bool isColor = mode_params[optindex].color;
+ SANE_Bool userDefined =
+ color_userdefined[s->val[OPT_COLOR_CORRECTION].w];
+
+ sval->w = optindex;
+
+ if (s->hw->cmd->set_halftoning != 0)
+ {
+ setOptionState (s, mode_params[optindex].depth == 1,
+ OPT_HALFTONE, &reload);
+ }
+
+ setOptionState (s, !isColor, OPT_DROPOUT, &reload);
+ if (s->hw->cmd->set_color_correction)
+ {
+ setOptionState (s, isColor, OPT_COLOR_CORRECTION, &reload);
+ }
+ if (s->hw->cmd->set_color_correction_coefficients)
+ {
+ setOptionState (s, isColor && userDefined, OPT_CCT_1, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_2, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_3, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_4, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_5, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_6, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_7, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_8, &reload);
+ setOptionState (s, isColor && userDefined, OPT_CCT_9, &reload);
+ }
+
+ /* if binary, then disable the bit depth selection */
+ if (optindex == 0)
+ {
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ if (bitDepthList[0] == 1)
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ else
+ {
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth;
+ }
+ }
+
+ handle_depth_halftone (s, &reload);
+ reload = SANE_TRUE;
+
+ break;
+ }
+
+ case OPT_ADF_MODE:
+ sval->w = optindex;
+ break;
+
+ case OPT_BIT_DEPTH:
+ sval->w = *((SANE_Word *) value);
+ mode_params[s->val[OPT_MODE].w].depth = sval->w;
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_HALFTONE:
+ sval->w = optindex;
+ handle_depth_halftone (s, &reload);
+ break;
+
+ case OPT_COLOR_CORRECTION:
+ {
+ SANE_Bool f = color_userdefined[optindex];
+
+ sval->w = optindex;
+ setOptionState (s, f, OPT_CCT_1, &reload);
+ setOptionState (s, f, OPT_CCT_2, &reload);
+ setOptionState (s, f, OPT_CCT_3, &reload);
+ setOptionState (s, f, OPT_CCT_4, &reload);
+ setOptionState (s, f, OPT_CCT_5, &reload);
+ setOptionState (s, f, OPT_CCT_6, &reload);
+ setOptionState (s, f, OPT_CCT_7, &reload);
+ setOptionState (s, f, OPT_CCT_8, &reload);
+ setOptionState (s, f, OPT_CCT_9, &reload);
+
+ break;
+ }
+
+ case OPT_GAMMA_CORRECTION:
+ {
+ SANE_Bool f = gamma_userdefined[optindex];
+
+ sval->w = optindex;
+/* setOptionState(s, f, OPT_GAMMA_VECTOR, &reload ); */
+ setOptionState (s, f, OPT_GAMMA_VECTOR_R, &reload);
+ setOptionState (s, f, OPT_GAMMA_VECTOR_G, &reload);
+ setOptionState (s, f, OPT_GAMMA_VECTOR_B, &reload);
+ setOptionState (s, !f, OPT_BRIGHTNESS, &reload); /* Note... */
+
+ break;
+ }
+
+ case OPT_MIRROR:
+ case OPT_SPEED:
+ case OPT_PREVIEW_SPEED:
+ case OPT_AAS:
+ case OPT_PREVIEW: /* needed? */
+ case OPT_BRIGHTNESS:
+ case OPT_SHARPNESS:
+ case OPT_AUTO_EJECT:
+ case OPT_THRESHOLD:
+ case OPT_ZOOM:
+ case OPT_WAIT_FOR_BUTTON:
+ sval->w = *((SANE_Word *) value);
+ break;
+
+ case OPT_LIMIT_RESOLUTION:
+ sval->w = *((SANE_Word *) value);
+ filter_resolution_list (s);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_QUICK_FORMAT:
+ sval->w = optindex;
+
+ s->val[OPT_TL_X].w = qf_params[sval->w].tl_x;
+ s->val[OPT_TL_Y].w = qf_params[sval->w].tl_y;
+ s->val[OPT_BR_X].w = qf_params[sval->w].br_x;
+ s->val[OPT_BR_Y].w = qf_params[sval->w].br_y;
+
+ if (s->val[OPT_TL_X].w < s->hw->x_range->min)
+ s->val[OPT_TL_X].w = s->hw->x_range->min;
+
+ if (s->val[OPT_TL_Y].w < s->hw->y_range->min)
+ s->val[OPT_TL_Y].w = s->hw->y_range->min;
+
+ if (s->val[OPT_BR_X].w > s->hw->x_range->max)
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ if (s->val[OPT_BR_Y].w > s->hw->y_range->max)
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ reload = SANE_TRUE;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ if (reload && info != NULL)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ End of setvalue.
+**/
+
+SANE_Status
+sane_control_option (SANE_Handle handle,
+ SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ if (info != NULL)
+ *info = 0;
+
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ return (getvalue (handle, option, value));
+
+ case SANE_ACTION_SET_VALUE:
+ return (setvalue (handle, option, value, info));
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_get_parameters()
+ *
+ * This function is part of the SANE API and gets called when the front end
+ * requests information aobut the scan configuration (e.g. color depth, mode,
+ * bytes and pixels per line, number of lines. This information is returned
+ * in the SANE_Parameters structure.
+ *
+ * Once a scan was started, this routine has to report the correct values, if
+ * it is called before the scan is actually started, the values are based on
+ * the current settings.
+ *
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ int ndpi, max_x, max_y;
+ int bytes_per_pixel;
+
+ DBG (5, "sane_get_parameters()\n");
+
+ /*
+ * If sane_start was already called, then just retrieve the parameters
+ * from the scanner data structure
+ */
+
+ if (!s->eof && s->ptr != NULL)
+ {
+ DBG (5, "Returning saved params structure\n");
+ if (params != NULL)
+ {
+ DBG(1, "Restoring parameters from saved parameters\n");
+ *params = s->params;
+ }
+
+ DBG (3, "Preview = %d\n", s->val[OPT_PREVIEW].w);
+ DBG (3, "Resolution = %d\n", s->val[OPT_RESOLUTION].w);
+
+ DBG (1, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n", (void *) s,
+ (void *) s->val, SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w),
+ SANE_UNFIX (s->val[OPT_BR_Y].w));
+
+ print_params (s->params);
+
+ return SANE_STATUS_GOOD;
+ }
+
+ /* otherwise initialize the params structure and gather the data */
+
+ memset (&s->params, 0, sizeof (SANE_Parameters));
+
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ max_x = max_y = 0;
+
+ s->params.pixels_per_line =
+ SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / 25.4 * ndpi + 0.5;
+ s->params.lines =
+ SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / 25.4 * ndpi + 0.5;
+
+ /*
+ * Make sure that the number of lines is correct for color shuffling:
+ * The shuffling alghorithm produces 2xline_distance lines at the
+ * beginning and the same amount at the end of the scan that are not
+ * useable. If s->params.lines gets negative, 0 lines are reported
+ * back to the frontend.
+ */
+ if (s->hw->color_shuffle)
+ {
+ s->params.lines -= 4 * s->line_distance;
+ if (s->params.lines < 0)
+ {
+ s->params.lines = 0;
+ }
+ DBG (1, "Adjusted params.lines for color_shuffle by %d to %d\n",
+ 4 * s->line_distance, s->params.lines);
+ }
+
+ DBG (3, "Preview = %d\n", s->val[OPT_PREVIEW].w);
+ DBG (3, "Resolution = %d\n", s->val[OPT_RESOLUTION].w);
+
+ DBG (1, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n", (void *) s,
+ (void *) s->val, SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w),
+ SANE_UNFIX (s->val[OPT_BR_Y].w));
+
+
+ /*
+ * Calculate bytes_per_pixel and bytes_per_line for
+ * any color depths.
+ *
+ * The default color depth is stored in mode_params.depth:
+ */
+
+ if (mode_params[s->val[OPT_MODE].w].depth == 1)
+ {
+ s->params.depth = 1;
+ }
+ else
+ {
+ s->params.depth = s->val[OPT_BIT_DEPTH].w;
+ }
+
+ if (s->params.depth > 8)
+ {
+ s->params.depth = 16; /*
+ * The frontends can only handle 8 or 16 bits
+ * for gray or color - so if it's more than 8,
+ * it gets automatically set to 16. This works
+ * as long as EPSON does not come out with a
+ * scanner that can handle more than 16 bits
+ * per color channel.
+ */
+
+ }
+
+ bytes_per_pixel = s->params.depth / 8; /* this works because it can only be set to 1, 8 or 16 */
+ if (s->params.depth % 8) /* just in case ... */
+ {
+ bytes_per_pixel++;
+ }
+
+ /* pixels_per_line is rounded to the next 8bit boundary */
+ s->params.pixels_per_line = s->params.pixels_per_line & ~7;
+
+ s->params.last_frame = SANE_TRUE;
+
+ if (mode_params[s->val[OPT_MODE].w].color)
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line =
+ 3 * s->params.pixels_per_line * bytes_per_pixel;
+ }
+ else
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line =
+ s->params.pixels_per_line * s->params.depth / 8;
+ }
+
+ if (NULL != params)
+ *params = s->params;
+
+ print_params (s->params);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_start()
+ *
+ * This function is part of the SANE API and gets called from the front end to
+ * start the scan process.
+ *
+ */
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ SANE_Bool button_status;
+ const struct mode_param *mparam;
+ u_char params[4];
+ int ndpi;
+ int left, top;
+ int lcount;
+ int i, j; /* loop counter */
+
+ DBG (5, "sane_start()\n");
+
+ open_scanner (s);
+
+/*
+ * There is some undocumented special behavior with the TPU enable/disable.
+ * TPU power ESC e status
+ * on 0 NAK
+ * on 1 ACK
+ * off 0 ACK
+ * off 1 NAK
+ *
+ * It makes no sense to scan with TPU powered on and source flatbed, because
+ * light will come from both sides.
+ */
+
+ if (s->hw->extension)
+ {
+ int max_x, max_y;
+
+ int extensionCtrl;
+ extensionCtrl = (s->hw->use_extension ? 1 : 0);
+ if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1))
+ extensionCtrl = 2;
+
+ status = control_extension (s, extensionCtrl);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "You may have to power %s your TPU\n",
+ s->hw->use_extension ? "on" : "off");
+
+ DBG (1, "Also you may have to restart the Sane frontend.\n");
+ close_scanner (s);
+ return status;
+ }
+
+ if (s->hw->cmd->request_extended_status != 0)
+ {
+ status = check_ext_status (s, &max_x, &max_y);
+
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ close_scanner (s);
+ return status;
+ }
+ }
+
+ if (s->hw->ADF && s->hw->use_extension && s->hw->cmd->feed)
+ {
+ status = feed (s);
+ if (SANE_STATUS_GOOD != status)
+ {
+ close_scanner (s);
+ return status;
+ }
+
+ check_ext_status (s, &max_x, &max_y);
+ s->hw->adf_max_x = max_x;
+ s->hw->adf_max_y = max_y;
+ }
+
+
+ /*
+ * set the focus position according to the extension used:
+ * if the TPU is selected, then focus 2.5mm above the glass,
+ * otherwise focus on the glass. Scanners that don't support
+ * this feature, will just ignore these calls.
+ */
+
+ if (s->hw->focusSupport == SANE_TRUE)
+ {
+ if (s->val[OPT_FOCUS].w == 0)
+ {
+ DBG (1, "Setting focus to glass surface\n");
+ set_focus_position (s, 0x40);
+ }
+ else
+ {
+ DBG (1, "Setting focus to 2.5mm above glass\n");
+ set_focus_position (s, 0x59);
+ }
+ }
+ }
+
+ /* use the flatbed size for the max. scansize for the GT-30000
+ and similar scanners if the ADF is not enabled */
+ if (s->hw->devtype == 3 && s->hw->use_extension == 0)
+ {
+ int max_x, max_y;
+
+ status = check_ext_status (s, &max_x, &max_y);
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ close_scanner (s);
+ return status;
+ }
+
+ s->hw->fbf_max_x = max_x;
+ s->hw->fbf_max_y = max_y;
+ }
+
+
+ mparam = mode_params + s->val[OPT_MODE].w;
+ DBG (1, "sane_start: Setting data format to %d bits\n", mparam->depth);
+ status = set_data_format (s, mparam->depth);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_data_format failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /*
+ * The byte sequence mode was introduced in B5, for B[34] we need line sequence mode
+ */
+
+ if ((s->hw->cmd->level[0] == 'D' ||
+ (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5)) &&
+ mparam->mode_flags == 0x02)
+ {
+ status = set_color_mode (s, 0x13);
+ }
+ else
+ {
+ status = set_color_mode (s, mparam->mode_flags | (mparam->dropout_mask
+ & dropout_params[s->
+ val
+ [OPT_DROPOUT].
+ w]));
+ }
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_color_mode failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (s->hw->cmd->set_halftoning &&
+ SANE_OPTION_IS_ACTIVE (s->opt[OPT_HALFTONE].cap))
+ {
+ status = set_halftoning (s, halftone_params[s->val[OPT_HALFTONE].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_halftoning failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BRIGHTNESS].cap))
+ {
+ status = set_bright (s, s->val[OPT_BRIGHTNESS].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_bright failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_MIRROR].cap))
+ {
+ status = mirror_image (s, mirror_params[s->val[OPT_MIRROR].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: mirror_image failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SPEED].cap))
+ {
+
+ if (s->val[OPT_PREVIEW].w)
+ status = set_speed (s, speed_params[s->val[OPT_PREVIEW_SPEED].w]);
+ else
+ status = set_speed (s, speed_params[s->val[OPT_SPEED].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_speed failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ }
+
+/*
+ * use of speed_params is ok here since they are false and true.
+ * NOTE: I think I should throw that "params" stuff as long w is already the value.
+ */
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AAS].cap))
+ {
+ status = control_auto_area_segmentation (s,
+ speed_params[s->val[OPT_AAS].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: control_auto_area_segmentation failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ s->invert_image = SANE_FALSE; /* default: to not inverting the image */
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_FILM_TYPE].cap))
+ {
+ s->invert_image = (s->val[OPT_FILM_TYPE].w == FILM_TYPE_NEGATIVE);
+ status = set_film_type (s, film_params[s->val[OPT_FILM_TYPE].w]);
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_film_type failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BAY].cap))
+ {
+ status = set_bay (s, s->val[OPT_BAY].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_bay: %s\n", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SHARPNESS].cap))
+ {
+
+ status = set_outline_emphasis (s, s->val[OPT_SHARPNESS].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_outline_emphasis failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->cmd->set_gamma &&
+ SANE_OPTION_IS_ACTIVE (s->opt[OPT_GAMMA_CORRECTION].cap))
+ {
+ int val;
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ /*
+ * The D1 level has only the two user defined gamma
+ * settings.
+ */
+ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
+ }
+ else
+ {
+ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
+
+ /*
+ * If "Default" is selected then determine the actual value
+ * to send to the scanner: If bilevel mode, just send the
+ * value from the table (0x01), for grayscale or color mode
+ * add one and send 0x02.
+ */
+/* if( s->val[ OPT_GAMMA_CORRECTION].w <= 1) { */
+ if (s->val[OPT_GAMMA_CORRECTION].w == 0)
+ {
+ val += mparam->depth == 1 ? 0 : 1;
+ }
+ }
+
+ DBG (1, "sane_start: set_gamma( s, 0x%x ).\n", val);
+ status = set_gamma (s, val);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_gamma failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->cmd->set_gamma_table &&
+ gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w])
+ { /* user defined. */
+ status = set_gamma_table (s);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_gamma_table failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+/*
+ * TODO: think about if SANE_OPTION_IS_ACTIVE is a good criteria to send commands.
+ */
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_COLOR_CORRECTION].cap))
+ {
+ int val = color_params[s->val[OPT_COLOR_CORRECTION].w];
+
+ DBG (1, "sane_start: set_color_correction( s, 0x%x )\n", val);
+ status = set_color_correction (s, val);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_color_correction failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ if (1 == s->val[OPT_COLOR_CORRECTION].w)
+ { /* user defined. */
+ status = set_color_correction_coefficients (s);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_color_correction_coefficients failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+
+ if (s->hw->cmd->set_threshold != 0
+ && SANE_OPTION_IS_ACTIVE (s->opt[OPT_THRESHOLD].cap))
+ {
+ status = set_threshold (s, s->val[OPT_THRESHOLD].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_threshold(%d) failed: %s\n",
+ s->val[OPT_THRESHOLD].w, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ status = set_resolution (s, ndpi, ndpi);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_resolution(%d, %d) failed: %s\n",
+ ndpi, ndpi, sane_strstatus (status));
+ return status;
+ }
+
+ status = sane_get_parameters (handle, NULL);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* set the zoom */
+ if (s->hw->cmd->set_zoom != 0
+ && SANE_OPTION_IS_ACTIVE (s->opt[OPT_ZOOM].cap))
+ {
+ status = set_zoom (s, s->val[OPT_ZOOM].w, s->val[OPT_ZOOM].w);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: set_zoom(%d) failed: %s\n",
+ s->val[OPT_ZOOM].w, sane_strstatus (status));
+ return status;
+ }
+ }
+
+
+/*
+ * Now s->params is initialized.
+ */
+
+
+/*
+ * If WAIT_FOR_BUTTON is active, then do just that: Wait until the button is
+ * pressed. If the button was already pressed, then we will get the button
+ * Pressed event right away.
+ */
+
+ if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE)
+ {
+ s->hw->wait_for_button = SANE_TRUE;
+
+ while (s->hw->wait_for_button == SANE_TRUE)
+ {
+ if (s->canceling == SANE_TRUE)
+ {
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ /* get the button status from the scanner */
+ else if (request_push_button_status (s, &button_status) ==
+ SANE_STATUS_GOOD)
+ {
+ if (button_status == SANE_TRUE)
+ {
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ else
+ {
+ sleep (1);
+ }
+ }
+ else
+ {
+ /* we run into an eror condition, just continue */
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ }
+ }
+
+
+/*
+ * in file:frontend/preview.c
+ *
+ * The preview strategy is as follows:
+ *
+ * 1) A preview always acquires an image that covers the entire
+ * scan surface. This is necessary so the user can see not
+ * only what is, but also what isn't selected.
+ */
+
+ left = SANE_UNFIX (s->val[OPT_TL_X].w) / 25.4 * ndpi + 0.5;
+ top = SANE_UNFIX (s->val[OPT_TL_Y].w) / 25.4 * ndpi + 0.5;
+
+ /*
+ * Calculate correction for line_distance in D1 scanner:
+ * Start line_distance lines earlier and add line_distance lines at the end
+ *
+ * Because the actual line_distance is not yet calculated we have to do this
+ * first.
+ */
+
+ s->hw->color_shuffle = SANE_FALSE;
+ s->current_output_line = 0;
+ s->lines_written = 0;
+ s->color_shuffle_line = 0;
+
+ if ((s->hw->optical_res != 0) && (mparam->depth == 8)
+ && (mparam->mode_flags != 0))
+ {
+ s->line_distance = s->hw->max_line_distance * ndpi / s->hw->optical_res;
+ if (s->line_distance != 0)
+ {
+ s->hw->color_shuffle = SANE_TRUE;
+ }
+ else
+ s->hw->color_shuffle = SANE_FALSE;
+ }
+
+/*
+ * for debugging purposes:
+ */
+#ifdef FORCE_COLOR_SHUFFLE
+ DBG (1, "Test mode: FORCE_COLOR_SHUFFLE = TRUE\n");
+ s->hw->color_shuffle = SANE_TRUE;
+#endif
+
+
+ /*
+ * Modify the scan area: If the scanner requires color shuffling, then we try to
+ * scan more lines to compensate for the lines that will be removed from the scan
+ * due to the color shuffling alghorithm.
+ * At this time we add two times the line distance to the number of scan lines if
+ * this is possible - if not, then we try to calculate the number of additional
+ * lines according to the selected scan area.
+ */
+ if (s->hw->color_shuffle == SANE_TRUE)
+ {
+
+ /* start the scan 2*line_distance earlier */
+ top -= 2 * s->line_distance;
+ if (top < 0)
+ {
+ top = 0;
+ }
+
+ /* scan 4*line_distance lines more */
+ s->params.lines += 4 * s->line_distance;
+ }
+
+ /*
+ * If (top + s->params.lines) is larger than the max scan area, reset
+ * the number of scan lines:
+ */
+ if (SANE_UNFIX (s->val[OPT_BR_Y].w) / 25.4 * ndpi < (s->params.lines + top))
+ {
+ s->params.lines = ((int) SANE_UNFIX (s->val[OPT_BR_Y].w) /
+ 25.4 * ndpi + 0.5) - top;
+ }
+
+
+ status =
+ set_scan_area (s, left, top, s->params.pixels_per_line, s->params.lines);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_scan_area failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ s->block = SANE_FALSE;
+ lcount = 1;
+
+ /*
+ * The set line count commands needs to be sent for certain scanners in
+ * color mode. The D1 level requires it, we are however only testing for
+ * 'D' and not for the actual numeric level.
+ */
+
+ if (((s->hw->cmd->level[0] == 'B') &&
+ ((s->hw->level >= 5) || ((s->hw->level >= 4) &&
+ (!mode_params[s->val[OPT_MODE].w].color))))
+ || (s->hw->cmd->level[0] == 'D'))
+ {
+ s->block = SANE_TRUE;
+ lcount = sanei_scsi_max_request_size / s->params.bytes_per_line;
+
+ if (lcount >= 255)
+ {
+ lcount = 255;
+ }
+
+ if (s->hw->TPU && s->hw->use_extension && lcount > 32)
+ {
+ lcount = 32;
+ }
+
+
+ /*
+ * The D1 series of scanners only allow an even line number
+ * for bi-level scanning. If a bit depth of 1 is selected, then
+ * make sure the next lower even number is selected.
+ */
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ if (lcount % 2)
+ {
+ lcount -= 1;
+ }
+ }
+
+ if (lcount == 0)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = set_lcount (s, lcount);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: set_lcount(%d) failed: %s\n",
+ lcount, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->cmd->request_extended_status != 0
+ && SANE_TRUE == s->hw->extension)
+ {
+ u_char result[4];
+ u_char *buf;
+ size_t len;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_extended_status;
+
+ send (s, params, 2, &status); /* send ESC f (request extended status) */
+
+ if (SANE_STATUS_GOOD == status)
+ {
+ len = 4; /* receive header */
+
+ receive (s, result, len, &status);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = result[3] << 8 | result[2];
+ buf = alloca (len);
+
+ receive (s, buf, len, &status); /* receive actual status data */
+
+ if (buf[0] & 0x80)
+ {
+ close_scanner (s);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ {
+ DBG (1, "Extended status flag request failed\n");
+ }
+ }
+
+/*
+ * for debug purpose
+ * check scanner conditions
+ */
+#if 1
+ if (s->hw->cmd->request_condition != 0)
+ {
+ u_char result[4];
+ u_char *buf;
+ size_t len;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_condition;
+
+ send (s, params, 2, &status); /* send request condition */
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = 4;
+ receive (s, result, len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = result[3] << 8 | result[2];
+ buf = alloca (len);
+ receive (s, buf, len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+#if 0
+ DBG (10, "SANE_START: length=%d\n", len);
+ for (i = 1; i <= len; i++)
+ {
+ DBG (10, "SANE_START: %d: %c\n", i, buf[i - 1]);
+ }
+#endif
+
+ DBG (5, "SANE_START: Color: %d\n", (int) buf[1]);
+ DBG (5, "SANE_START: Resolution (x, y): (%d, %d)\n",
+ (int) (buf[4] << 8 | buf[3]), (int) (buf[6] << 8 | buf[5]));
+ DBG (5,
+ "SANE_START: Scan area(pixels) (x0, y0), (x1, y1): (%d, %d), (%d, %d)\n",
+ (int) (buf[9] << 8 | buf[8]), (int) (buf[11] << 8 | buf[10]),
+ (int) (buf[13] << 8 | buf[12]), (int) (buf[15] << 8 | buf[14]));
+ DBG (5, "SANE_START: Data format: %d\n", (int) buf[17]);
+ DBG (5, "SANE_START: Halftone: %d\n", (int) buf[19]);
+ DBG (5, "SANE_START: Brightness: %d\n", (int) buf[21]);
+ DBG (5, "SANE_START: Gamma: %d\n", (int) buf[23]);
+ DBG (5, "SANE_START: Zoom (x, y): (%d, %d)\n", (int) buf[26],
+ (int) buf[25]);
+ DBG (5, "SANE_START: Color correction: %d\n", (int) buf[28]);
+ DBG (5, "SANE_START: Sharpness control: %d\n", (int) buf[30]);
+ DBG (5, "SANE_START: Scanning mode: %d\n", (int) buf[32]);
+ DBG (5, "SANE_START: Mirroring: %d\n", (int) buf[34]);
+ DBG (5, "SANE_START: Auto area segmentation: %d\n", (int) buf[36]);
+ DBG (5, "SANE_START: Threshold: %d\n", (int) buf[38]);
+ DBG (5, "SANE_START: Line counter: %d\n", (int) buf[40]);
+ DBG (5, "SANE_START: Option unit control: %d\n", (int) buf[42]);
+ DBG (5, "SANE_START: Film type: %d\n", (int) buf[44]);
+ }
+#endif
+
+
+ /* set the retry count to 0 */
+ s->retry_count = 0;
+
+ if (s->hw->color_shuffle == SANE_TRUE)
+ {
+
+ /* initialize the line buffers */
+ for (i = 0; i < s->line_distance * 2 + 1; i++)
+ {
+ if (s->line_buffer[i] != NULL)
+ free (s->line_buffer[i]);
+
+ s->line_buffer[i] = malloc (s->params.bytes_per_line);
+ if (s->line_buffer[i] == NULL)
+ {
+ /* free the memory we've malloced so far */
+ for (j = 0; j < i; j++)
+ {
+ free (s->line_buffer[j]);
+ s->line_buffer[j] = NULL;
+ }
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->start_scanning;
+
+ send (s, params, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "sane_start: start failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ s->eof = SANE_FALSE;
+ s->buf = realloc (s->buf, lcount * s->params.bytes_per_line);
+ s->ptr = s->end = s->buf;
+ s->canceling = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+} /* sane_start */
+
+/*
+ *
+ * TODO: clean up the eject and direct cmd mess.
+ */
+
+SANE_Status
+sane_auto_eject (Epson_Scanner * s)
+{
+
+ DBG (5, "sane_auto_eject()\n");
+
+ if (s->hw->ADF && s->hw->use_extension && s->val[OPT_AUTO_EJECT].w)
+ { /* sequence! */
+ SANE_Status status;
+
+ u_char params[1];
+ u_char cmd = s->hw->cmd->eject;
+
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = cmd;
+
+ send (s, params, 1, &status);
+
+ if (SANE_STATUS_GOOD != (status = expect_ack (s)))
+ {
+ return status;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ *
+ *
+ */
+
+static SANE_Status
+read_data_block (Epson_Scanner * s, EpsonDataRec * result)
+{
+ SANE_Status status;
+ u_char param[3];
+
+ receive (s, result, s->block ? 6 : 4, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ if (STX != result->code)
+ {
+ DBG (1, "code %02x\n", (int) result->code);
+ DBG (1, "error, expected STX\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ if (result->status & STATUS_FER)
+ {
+ int dummy_x, dummy_y;
+
+ DBG (1, "fatal error - Status = %02x\n", result->status);
+
+ status = check_ext_status (s, &dummy_x, &dummy_y);
+
+ /*
+ * Hack Alert!!!
+ * If the status is SANE_STATUS_DEVICE_BUSY then we need to
+ * re-issue the command again. We can assume that the command that
+ * caused this problem was ESC G, so in a loop with a sleep 1 we
+ * are testing this over and over and over again, until the lamp
+ * "thinks" it is ready.
+ *
+ * TODO: Store the last command and execute what was actually used
+ * as the last command. For all situations this error may occur
+ * ESC G is very very likely to be the command in question, but
+ * we better make sure that this is the case.
+ *
+ */
+
+ /*
+ * let's safe some stack space: If this is not the first go around,
+ * then just return the status and let the loop handle this - otherwise
+ * we would run this function recursively.
+ */
+
+ if ((status == SANE_STATUS_DEVICE_BUSY && s->retry_count > 0) ||
+ (status == SANE_STATUS_GOOD && s->retry_count > 0))
+ {
+ return SANE_STATUS_DEVICE_BUSY; /* return busy even if we just read OK
+ so that the following loop can end
+ gracefully */
+ }
+
+ while (status == SANE_STATUS_DEVICE_BUSY)
+ {
+ if (s->retry_count > SANE_EPSON_MAX_RETRIES)
+ {
+ DBG (1, "Max retry count exceeded (%d)\n", s->retry_count);
+ return SANE_STATUS_INVAL;
+ }
+
+ sleep (1); /* wait one second for the next attempt */
+
+ DBG (1, "retrying ESC G - %d\n", ++(s->retry_count));
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->start_scanning;
+
+ send (s, param, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "read_data_block: start failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = read_data_block (s, result);
+ }
+
+ }
+
+ return status;
+}
+
+/*
+ *
+ *
+ */
+
+
+void
+scan_finish (Epson_Scanner * s)
+{
+ SANE_Status status;
+ int i, x, y;
+
+ DBG (5, "scan_finish()\n");
+
+ free (s->buf);
+ s->buf = NULL;
+
+ status = check_ext_status (s, &x, &y);
+
+ if (SANE_STATUS_NO_DOCS == status && s->hw->ADF && s->hw->use_extension)
+ sane_auto_eject (s);
+
+ for (i = 0; i < s->line_distance; i++)
+ {
+ if (s->line_buffer[i] != NULL)
+ {
+ free (s->line_buffer[i]);
+ s->line_buffer[i] = NULL;
+ }
+ }
+}
+
+#define GET_COLOR(x) ((x.status>>2) & 0x03)
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ int index = 0;
+ SANE_Bool reorder = SANE_FALSE;
+ SANE_Bool needStrangeReorder = SANE_FALSE;
+ int bytes_to_process = 0;
+
+START_READ:
+ DBG (5, "sane_read: begin\n");
+
+ if (s->ptr == s->end)
+ {
+ EpsonDataRec result;
+ size_t buf_len;
+
+ if ((s->fd != -1) && s->eof)
+ {
+ if (s->hw->color_shuffle)
+ {
+ DBG (1, "Written %d lines after color shuffle\n", s->lines_written);
+ DBG (1, "Lines requested: %d\n", s->params.lines);
+ }
+
+ *length = 0;
+ scan_finish (s);
+
+ return SANE_STATUS_EOF;
+ }
+
+
+ DBG (5, "sane_read: begin scan1\n");
+
+ if (SANE_STATUS_GOOD != (status = read_data_block (s, &result)))
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+
+ buf_len = result.buf[1] << 8 | result.buf[0];
+
+ DBG (5, "sane_read: buf len = %lu\n", (u_long) buf_len);
+
+ if (s->block)
+ {
+ buf_len *= (result.buf[3] << 8 | result.buf[2]);
+ DBG (5, "sane_read: buf len (adjusted) = %lu\n", (u_long) buf_len);
+ }
+
+ if (!s->block && SANE_FRAME_RGB == s->params.format)
+ {
+ /*
+ * Read color data in line mode
+ */
+
+
+ /*
+ * read the first color line - the number of bytes to read
+ * is already known (from last call to read_data_block()
+ * We determine where to write the line from the color information
+ * in the data block. At the end we want the order RGB, but the
+ * way the data is delivered does not guarantee this - actually it's
+ * most likely that the order is GRB if it's not RGB!
+ */
+ switch (GET_COLOR (result))
+ {
+ case 1:
+ index = 1;
+ break;
+ case 2:
+ index = 0;
+ break;
+ case 3:
+ index = 2;
+ break;
+ }
+
+ receive (s, s->buf + index * s->params.pixels_per_line, buf_len,
+ &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+ /*
+ * send the ACK signal to the scanner in order to make
+ * it ready for the next data block.
+ */
+ send (s, S_ACK, 1, &status);
+
+ /*
+ * ... and request the next data block
+ */
+ if (SANE_STATUS_GOOD != (status = read_data_block (s, &result)))
+ return status;
+
+ buf_len = result.buf[1] << 8 | result.buf[0];
+ /*
+ * this should never happen, because we are already in
+ * line mode, but it does not hurt to check ...
+ */
+ if (s->block)
+ buf_len *= (result.buf[3] << 8 | result.buf[2]);
+
+ DBG (5, "sane_read: buf len2 = %lu\n", (u_long) buf_len);
+
+ switch (GET_COLOR (result))
+ {
+ case 1:
+ index = 1;
+ break;
+ case 2:
+ index = 0;
+ break;
+ case 3:
+ index = 2;
+ break;
+ }
+
+ receive (s, s->buf + index * s->params.pixels_per_line, buf_len,
+ &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ scan_finish (s);
+ *length = 0;
+ return status;
+ }
+
+ send (s, S_ACK, 1, &status);
+
+ /*
+ * ... and the last data block
+ */
+ if (SANE_STATUS_GOOD != (status = read_data_block (s, &result)))
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+
+ buf_len = result.buf[1] << 8 | result.buf[0];
+
+ if (s->block)
+ buf_len *= (result.buf[3] << 8 | result.buf[2]);
+
+ DBG (5, "sane_read: buf len3 = %lu\n", (u_long) buf_len);
+
+ switch (GET_COLOR (result))
+ {
+ case 1:
+ index = 1;
+ break;
+ case 2:
+ index = 0;
+ break;
+ case 3:
+ index = 2;
+ break;
+ }
+
+ receive (s, s->buf + index * s->params.pixels_per_line, buf_len,
+ &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+ }
+ else
+ {
+ /*
+ * Read data in block mode
+ */
+
+ /* do we have to reorder the data ? */
+ if (GET_COLOR (result) == 0x01)
+ {
+ reorder = SANE_TRUE;
+ }
+
+ bytes_to_process = receive (s, s->buf, buf_len, &status);
+
+ /* bytes_to_process = buf_len; */
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+ }
+
+ if (result.status & STATUS_AREA_END)
+ {
+ s->eof = SANE_TRUE;
+ }
+ else
+ {
+ if (s->canceling)
+ {
+ send (s, S_CAN, 1, &status);
+ expect_ack (s);
+
+ *length = 0;
+
+ scan_finish (s);
+
+ return SANE_STATUS_CANCELLED;
+ }
+ else
+ send (s, S_ACK, 1, &status);
+ }
+
+ s->end = s->buf + buf_len;
+ s->ptr = s->buf;
+
+ /*
+ * if we have to re-order the color components (GRB->RGB) we
+ * are doing this here:
+ */
+
+ /*
+ * Some scaners (e.g. the Perfection 1640 and GT-2200) seem
+ * to have the R and G channels swapped.
+ * The GT-8700 is the Asian version of the Perfection1640.
+ * If the scanner name is one of these, and the scan mode is
+ * RGB then swap the colors.
+ */
+
+ needStrangeReorder =
+ (strstr (s->hw->sane.model, "GT-2200") ||
+ ((strstr (s->hw->sane.model, "1640") &&
+ strstr (s->hw->sane.model, "Perfection")) ||
+ strstr (s->hw->sane.model, "GT-8700"))) &&
+ s->params.format == SANE_FRAME_RGB;
+
+ /*
+ * Certain Perfection 1650 also need this re-ordering of the two
+ * color channels. These scanners are identified by the problem
+ * with the half vertical scanning area. When we corrected this,
+ * we also set the variable s->hw->need_color_reorder
+ */
+ if (s->hw->need_color_reorder)
+ {
+ needStrangeReorder = SANE_TRUE;
+ }
+
+ if (needStrangeReorder)
+ reorder = SANE_FALSE; /* reordering once is enough */
+
+ if (s->params.format != SANE_FRAME_RGB)
+ reorder = SANE_FALSE; /* don't reorder for BW or gray */
+
+ if (reorder)
+ {
+ SANE_Byte *ptr;
+
+ ptr = s->buf;
+ while (ptr < s->end)
+ {
+ if (s->params.depth > 8)
+ {
+ SANE_Byte tmp;
+
+ /* R->G G->R */
+ tmp = ptr[0];
+ ptr[0] = ptr[2]; /* first Byte G */
+ ptr[2] = tmp; /* first Byte R */
+
+ tmp = ptr[1];
+ ptr[1] = ptr[3]; /* second Byte G */
+ ptr[3] = tmp; /* second Byte R */
+
+ ptr += 6; /* go to next pixel */
+ }
+ else
+ {
+ /* R->G G->R */
+ SANE_Byte tmp;
+
+ tmp = ptr[0];
+ ptr[0] = ptr[1]; /* G */
+ ptr[1] = tmp; /* R */
+ /* B stays the same */
+ ptr += 3; /* go to next pixel */
+ }
+ }
+ }
+
+ /*
+ * Do the color_shuffle if everything else is correct - at this time
+ * most of the stuff is hardcoded for the Perfection 610
+ */
+
+ if (s->hw->color_shuffle)
+ {
+ int new_length = 0;
+
+ status = color_shuffle (s, &new_length);
+
+ /*
+ * If no bytes are returned, check if the scanner is already done, if so,
+ * we'll probably just return, but if there is more data to process get
+ * the next batch.
+ */
+
+ if (new_length == 0 && s->end != s->ptr)
+ {
+ goto START_READ;
+ }
+
+ s->end = s->buf + new_length;
+ s->ptr = s->buf;
+
+ }
+
+
+ DBG (5, "sane_read: begin scan2\n");
+ }
+
+
+
+ /*
+ * copy the image data to the data memory area
+ */
+
+ if (!s->block && SANE_FRAME_RGB == s->params.format)
+ {
+
+ max_length /= 3;
+
+ if (max_length > s->end - s->ptr)
+ max_length = s->end - s->ptr;
+
+ *length = 3 * max_length;
+
+ if (s->invert_image == SANE_TRUE)
+ {
+ while (max_length-- != 0)
+ {
+ /* invert the three values */
+ *data++ = (u_char) ~ (s->ptr[0]);
+ *data++ = (u_char) ~ (s->ptr[s->params.pixels_per_line]);
+ *data++ = (u_char) ~ (s->ptr[2 * s->params.pixels_per_line]);
+ ++s->ptr;
+ }
+ }
+ else
+ {
+ while (max_length-- != 0)
+ {
+ *data++ = s->ptr[0];
+ *data++ = s->ptr[s->params.pixels_per_line];
+ *data++ = s->ptr[2 * s->params.pixels_per_line];
+ ++s->ptr;
+ }
+ }
+ }
+ else
+ {
+ if (max_length > s->end - s->ptr)
+ max_length = s->end - s->ptr;
+
+ *length = max_length;
+
+ if (1 == s->params.depth)
+ {
+ if (s->invert_image == SANE_TRUE)
+ {
+ while (max_length-- != 0)
+ *data++ = *s->ptr++;
+ }
+ else
+ {
+ while (max_length-- != 0)
+ *data++ = ~*s->ptr++;
+ }
+ }
+ else
+ {
+
+ if (s->invert_image == SANE_TRUE)
+ {
+ int i;
+
+ for (i = 0; i < max_length; i++)
+ {
+ data[i] = (u_char) ~ (s->ptr[i]);
+ }
+ }
+ else
+ {
+ memcpy (data, s->ptr, max_length);
+ }
+ s->ptr += max_length;
+ }
+ }
+
+ DBG (5, "sane_read: end\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+color_shuffle (SANE_Handle handle, int *new_length)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Byte *buf = s->buf;
+ int length = s->end - s->buf;
+
+ if (s->hw->color_shuffle == SANE_TRUE)
+ {
+ SANE_Byte *data_ptr; /* ptr to data to process */
+ SANE_Byte *data_end; /* ptr to end of processed data */
+ SANE_Byte *out_data_ptr; /* ptr to memory when writing data */
+ int i; /* loop counter */
+
+ /*
+ * It looks like we are dealing with a scanner that has an odd way
+ * of dealing with colors... The red and blue scan lines are shifted
+ * up or down by a certain number of lines relative to the green line.
+ */
+ DBG (5, "sane_read: color_shuffle\n");
+
+
+ /*
+ * Initialize the variables we are going to use for the
+ * copying of the data. data_ptr is the pointer to
+ * the currently worked on scan line. data_end is the
+ * end of the data area as calculated from adding *length
+ * to the start of data.
+ * out_data_ptr is used when writing out the processed data
+ * and always points to the beginning of the next line to
+ * write.
+ */
+
+ data_ptr = out_data_ptr = buf;
+ data_end = data_ptr + length;
+
+ /*
+ * The image data is in *buf, we know that the buffer contains s->end - s->buf ( = length)
+ * bytes of data. The width of one line is in s->params.bytes_per_line
+ */
+
+ /*
+ * The buffer area is supposed to have a number of full scan
+ * lines, let's test if this is the case.
+ */
+
+ if (length % s->params.bytes_per_line != 0)
+ {
+ DBG (1, "ERROR in size of buffer: %d / %d\n",
+ length, s->params.bytes_per_line);
+ return SANE_STATUS_INVAL;
+ }
+
+ while (data_ptr < data_end)
+ {
+ SANE_Byte *source_ptr, *dest_ptr;
+ int loop;
+
+ /* copy the green information into the current line */
+
+ source_ptr = data_ptr + 1;
+ dest_ptr = s->line_buffer[s->color_shuffle_line] + 1;
+
+ for (i = 0; i < s->params.bytes_per_line / 3; i++)
+ {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+
+ /* copy the red information n lines back */
+
+ if (s->color_shuffle_line >= s->line_distance)
+ {
+ source_ptr = data_ptr + 2;
+ dest_ptr =
+ s->line_buffer[s->color_shuffle_line - s->line_distance] + 2;
+
+/* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */
+ for (loop = 0; loop < s->params.bytes_per_line / 3; loop++)
+
+ {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+ }
+
+ /* copy the blue information n lines forward */
+
+ source_ptr = data_ptr;
+ dest_ptr = s->line_buffer[s->color_shuffle_line + s->line_distance];
+
+/* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */
+ for (loop = 0; loop < s->params.bytes_per_line / 3; loop++)
+ {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+
+ data_ptr += s->params.bytes_per_line;
+
+ if (s->color_shuffle_line == s->line_distance)
+ {
+ /*
+ * we just finished the line in line_buffer[0] - write it to the
+ * output buffer and continue.
+ */
+
+
+ /*
+ * The ouput buffer ist still "buf", but because we are
+ * only overwriting from the beginning of the memory area
+ * we are not interfering with the "still to shuffle" data
+ * in the same area.
+ */
+
+ /*
+ * Strip the first and last n lines and limit to
+ */
+ if ((s->current_output_line >= s->line_distance) &&
+ (s->current_output_line < s->params.lines + s->line_distance))
+ {
+ memcpy (out_data_ptr, s->line_buffer[0], s->params.bytes_per_line);
+ out_data_ptr += s->params.bytes_per_line;
+
+ s->lines_written++;
+ }
+
+ s->current_output_line++;
+
+
+ /*
+ * Now remove the 0-entry and move all other
+ * lines up by one. There are 2*line_distance + 1
+ * buffers, * therefore the loop has to run from 0
+ * to * 2*line_distance, and because we want to
+ * copy every n+1st entry to n the loop runs
+ * from - to 2*line_distance-1!
+ */
+
+ free (s->line_buffer[0]);
+
+ for (i = 0; i < s->line_distance * 2; i++)
+ {
+ s->line_buffer[i] = s->line_buffer[i + 1];
+ }
+
+ /*
+ * and create one new buffer at the end
+ */
+
+ s->line_buffer[s->line_distance * 2] =
+ malloc (s->params.bytes_per_line);
+ if (s->line_buffer[s->line_distance * 2] == NULL)
+ {
+ int i;
+ for (i = 0; i < s->line_distance * 2; i++)
+ {
+ free (s->line_buffer[i]);
+ s->line_buffer[i] = NULL;
+ }
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ else
+ {
+ s->color_shuffle_line++; /* increase the buffer number */
+ }
+ }
+
+ /*
+ * At this time we've used up all the new data from the scanner, some of
+ * it is still in the line_buffers, but we are ready to return some of it
+ * to the front end software. To do so we have to adjust the size of the
+ * data area and the *new_length variable.
+ */
+
+ *new_length = out_data_ptr - buf;
+ }
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+
+
+/*
+ * static SANE_Status get_identity_information ( SANE_Handle handle)
+ *
+ * Request Identity information from scanner and fill in information
+ * into dev and/or scanner structures.
+ */
+static SANE_Status
+get_identity_information (SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ Epson_Device *dev = s->hw;
+ EpsonIdent ident;
+ u_char param[3];
+ SANE_Status status;
+ u_char *buf;
+
+ DBG (5, "get_identity_information()\n");
+
+ if (!s->hw->cmd->request_identity)
+ return SANE_STATUS_INVAL;
+
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->request_identity;
+ param[2] = '\0';
+
+ if (NULL == (ident = (EpsonIdent) command (s, param, 2, &status)))
+ {
+ DBG (1, "ident failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (1, "type %3c 0x%02x\n", ident->type, ident->type);
+ DBG (1, "level %3c 0x%02x\n", ident->level, ident->level);
+
+ {
+ char *force = getenv ("SANE_EPSON_CMD_LVL");
+
+ if (force)
+ {
+ ident->type = force[0];
+ ident->level = force[1];
+
+ DBG (1, "type %3c 0x%02x\n", ident->type, ident->type);
+ DBG (1, "level %3c 0x%02x\n", ident->level, ident->level);
+
+ DBG (1, "forced\n");
+ }
+ }
+
+/*
+ * check if option equipment is installed.
+ */
+
+ if (ident->status & STATUS_OPTION)
+ {
+ DBG (1, "option equipment is installed\n");
+ dev->extension = SANE_TRUE;
+ }
+ else
+ {
+ DBG (1, "no option equipment installed\n");
+ dev->extension = SANE_FALSE;
+ }
+
+ dev->TPU = SANE_FALSE;
+ dev->ADF = SANE_FALSE;
+
+/*
+ * set command type and level.
+ */
+
+ {
+ int n;
+
+ for (n = 0; n < NELEMS (epson_cmd); n++)
+ {
+ char type_level[3];
+ sprintf(type_level, "%c%c", ident->type, ident->level);
+ if (!strncmp (type_level, epson_cmd[n].level, 2))
+ break;
+ }
+
+ if (n < NELEMS (epson_cmd))
+ {
+ dev->cmd = &epson_cmd[n];
+ }
+ else
+ {
+ dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT];
+ DBG (1, "Unknown type %c or level %c, using %s\n",
+ ident->type, ident->level, dev->cmd->level);
+ }
+
+ s->hw->level = dev->cmd->level[1] - '0';
+ } /* set comand type and level */
+
+/*
+ * Setting available resolutions and xy ranges for sane frontend.
+ */
+
+ s->hw->res_list_size = 0;
+ s->hw->res_list =
+ (SANE_Int *) calloc (s->hw->res_list_size, sizeof (SANE_Int));
+
+ if (NULL == s->hw->res_list)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ {
+ int n, k;
+ int x = 0, y = 0;
+ int count = ident->count2 * 255 + ident->count1;
+
+ /* we need to correct for the difference in size between
+ the EpsonIdentRec and the EpsonHdrRec */
+ int correction = sizeof (EpsonIdentRec) - sizeof (EpsonHdrRec);
+ for (n = (count - correction), buf = ident->buf; n; n -= k, buf += k)
+ {
+ switch (*buf)
+ {
+ case 'R':
+ {
+ int val = buf[2] << 8 | buf[1];
+
+ s->hw->res_list_size++;
+ s->hw->res_list =
+ (SANE_Int *) realloc (s->hw->res_list,
+ s->hw->res_list_size * sizeof (SANE_Int));
+
+ if (NULL == s->hw->res_list)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->hw->res_list[s->hw->res_list_size - 1] = (SANE_Int) val;
+
+ DBG (1, "resolution (dpi): %d\n", val);
+ k = 3;
+ continue;
+ }
+ case 'A':
+ {
+ x = buf[2] << 8 | buf[1];
+ y = buf[4] << 8 | buf[3];
+
+ DBG (1, "maximum scan area: x %d y %d\n", x, y);
+ k = 5;
+
+ /*
+ * Check for Perfection 4990 photo/GT-X800 scanner.
+ * This scanner only report 3200 dpi back.
+ * The scanner fysical supports 4800 dpi.
+ * This is simulated here...
+ * Futher details read:
+ * EPSON Programming guide for EPSON Color Image Scanner Perfection 4990
+ */
+ if (s->hw->cmd->request_extended_status != 0)
+ {
+ u_char *buf;
+ u_char params[2];
+ EpsonHdr head;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_extended_status;
+
+ if (NULL != (head = (EpsonHdr) command (s, params, 2, &status)))
+ {
+ buf = &head->buf[0x1A];
+ DBG (1, "product name %x %x %x %x %x %x %x %x \n", buf[0], buf[1],buf[2],buf[3],buf[4], buf[5],buf[6], buf[7] );
+ if (strncmp((char *) buf,"GT-X800",7) == 0)
+ {
+ int val = 0x12 << 8 | 0xC0;
+
+ s->hw->res_list_size++;
+ s->hw->res_list =
+ (SANE_Int *) realloc (s->hw->res_list,
+ s->hw->res_list_size * sizeof (SANE_Int));
+
+ if (NULL == s->hw->res_list)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->hw->res_list[s->hw->res_list_size - 1] = (SANE_Int) val;
+ x = (x/32)*48;
+ y = (y/32)*48;
+
+ DBG (1, "resolution (dpi): %d\n", val);
+ DBG (1, "maximum scan area GT-X800: x %d y %d\n", x, y);
+ }
+ }
+ }
+
+ continue;
+ }
+ default:
+ break;
+ } /* case */
+
+ break;
+ } /* for */
+
+ dev->dpi_range.min = s->hw->res_list[0];
+ dev->dpi_range.max = s->hw->res_list[s->hw->res_list_size - 1];
+ dev->dpi_range.quant = 0;
+
+ dev->fbf_x_range.min = 0;
+ dev->fbf_x_range.max = SANE_FIX (x * 25.4 / dev->dpi_range.max);
+ dev->fbf_x_range.quant = 0;
+
+ dev->fbf_y_range.min = 0;
+ dev->fbf_y_range.max = SANE_FIX (y * 25.4 / dev->dpi_range.max);
+ dev->fbf_y_range.quant = 0;
+
+ DBG (5, "fbf tlx %f tly %f brx %f bry %f [mm]\n",
+ SANE_UNFIX (dev->fbf_x_range.min), SANE_UNFIX (dev->fbf_y_range.min),
+ SANE_UNFIX (dev->fbf_x_range.max),
+ SANE_UNFIX (dev->fbf_y_range.max));
+
+ }
+
+ /*
+ * Copy the resolution list to the resolution_list array so that the frontend can
+ * display the correct values
+ */
+
+ s->hw->resolution_list =
+ malloc ((s->hw->res_list_size + 1) * sizeof (SANE_Word));
+
+ if (s->hw->resolution_list == NULL)
+ {
+ DBG (1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+ *(s->hw->resolution_list) = s->hw->res_list_size;
+ memcpy (&(s->hw->resolution_list[1]), s->hw->res_list,
+ s->hw->res_list_size * sizeof (SANE_Word));
+
+ /* filter the resolution list */
+ /* the option is not yet initialized, for now just set it to false */
+ s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE;
+ filter_resolution_list (s);
+
+ return SANE_STATUS_GOOD;
+
+} /* request identity */
+
+
+/*
+ * static SANE_Status get_identity2_information ( SANE_Handle handle)
+ *
+ * Request Identity2 information from scanner and fill in information
+ * into dev and/or scanner structures.
+ */
+static SANE_Status
+get_identity2_information (SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ int len;
+ u_char param[3];
+ u_char result[4];
+ u_char *buf;
+
+ DBG (5, "get_identity2_information()\n");
+
+ if (s->hw->cmd->request_identity2 == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->request_identity2;
+ param[2] = '\0';
+
+ send (s, param, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = 4; /* receive header */
+
+ receive (s, result, len, &status);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = result[3] << 8 | result[2];
+ buf = alloca (len);
+
+ receive (s, buf, len, &status); /* reveive actual status data */
+
+ /* the first two bytes of the buffer contain the optical resolution */
+ s->hw->optical_res = buf[1] << 8 | buf[0];
+
+ /*
+ * the 4th and 5th byte contain the line distance. Both values have to
+ * be identical, otherwise this software can not handle this scanner.
+ */
+ if (buf[4] != buf[5])
+ {
+ close_scanner (s);
+ return SANE_STATUS_INVAL;
+ }
+ s->hw->max_line_distance = buf[4];
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * void sane_cancel(SANE_Handle handle)
+ *
+ * Set the cancel flag to true. The next time the backend requests data
+ * from the scanner the CAN message will be sent.
+ */
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ /*
+ * If the s->ptr pointer is not NULL, then a scan operation
+ * was started and if s->eof is FALSE, it was not finished.
+ */
+
+ if (s->buf != NULL)
+ {
+ u_char *dummy;
+ int len;
+
+ /* malloc one line */
+ dummy = malloc (s->params.bytes_per_line);
+ if (dummy == NULL)
+ {
+ DBG (1, "Out of memory\n");
+ return;
+ }
+ else
+ {
+
+ /* there is still data to read from the scanner */
+
+ s->canceling = SANE_TRUE;
+
+ while (!s->eof &&
+ SANE_STATUS_CANCELLED != sane_read (s, dummy,
+ s->params.bytes_per_line,
+ &len))
+ {
+ /* empty body, the while condition does the processing */
+ }
+ free (dummy);
+ }
+
+ }
+}
+
+
+static SANE_Status
+request_focus_position (SANE_Handle handle, u_char * position)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ int len;
+ u_char param[3];
+ u_char result[4];
+ u_char *buf;
+
+ DBG (5, "request_focus_position()\n");
+
+ if (s->hw->cmd->request_focus_position == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->request_focus_position;
+ param[2] = '\0';
+
+ send (s, param, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = 4; /* receive header */
+
+ receive (s, result, len, &status);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = result[3] << 8 | result[2];
+ buf = alloca (len);
+
+ receive (s, buf, len, &status); /* reveive actual status data */
+
+ *position = buf[1];
+ DBG (1, "Focus position = 0x%x\n", buf[1]);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ * Request the push button status
+ * returns SANE_TRUE if the button was pressed
+ * and SANE_FALSE if the button was not pressed
+ * it also returns SANE_TRUE in case of an error.
+ * This is necessary so that a process that waits for
+ * the button does not block indefinitely.
+ */
+static SANE_Bool
+request_push_button_status (SANE_Handle handle, SANE_Bool * theButtonStatus)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ int len;
+ u_char param[3];
+ u_char result[4];
+ u_char *buf;
+
+ DBG (5, "request_push_button_status()\n");
+
+ if (s->hw->cmd->request_push_button_status == 0)
+ {
+ DBG (1, "push button status unsupported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ param[0] = ESC;
+ param[1] = s->hw->cmd->request_push_button_status;
+ param[2] = '\0';
+
+ send (s, param, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ DBG (1, "error sending command\n");
+ return status;
+ }
+
+ len = 4; /* receive header */
+
+ receive (s, result, len, &status);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ len = result[3] << 8 | result[2]; /* this should be 1 for scanners with one button */
+ buf = alloca (len);
+
+ receive (s, buf, len, &status); /* reveive actual status data */
+
+ DBG (1, "Push button status = %d\n", buf[0] & 0x01);
+ *theButtonStatus = ((buf[0] & 0x01) != 0);
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+
+static void
+filter_resolution_list (Epson_Scanner * s)
+{
+ /* re-create the list */
+
+ if (s->val[OPT_LIMIT_RESOLUTION].w == SANE_TRUE)
+ {
+ /* copy the short list */
+
+ /* filter out all values that are not 300 or 400 dpi based */
+ int i;
+
+ int new_size = 0;
+ SANE_Bool is_correct_resolution = SANE_FALSE;
+
+ for (i = 1; i <= s->hw->res_list_size; i++)
+ {
+ SANE_Word res;
+ res = s->hw->res_list[i];
+ if ((res < 100) || (0 == (res % 300)) || (0 == (res % 400)))
+ {
+ /* add the value */
+ new_size++;
+
+ s->hw->resolution_list[new_size] = s->hw->res_list[i];
+
+ /* check for a valid current resolution */
+ if (res == s->val[OPT_RESOLUTION].w)
+ {
+ is_correct_resolution = SANE_TRUE;
+ }
+ }
+ }
+ s->hw->resolution_list[0] = new_size;
+
+ if (is_correct_resolution == SANE_FALSE)
+ {
+ for (i = 1; i <= new_size; i++)
+ {
+ if (s->val[OPT_RESOLUTION].w < s->hw->resolution_list[i])
+ {
+ s->val[OPT_RESOLUTION].w = s->hw->resolution_list[i];
+ i = new_size + 1;
+ }
+ }
+ }
+
+ }
+ else
+ {
+ /* copy the full list */
+ s->hw->resolution_list[0] = s->hw->res_list_size;
+ memcpy (&(s->hw->resolution_list[1]), s->hw->res_list,
+ s->hw->res_list_size * sizeof (SANE_Word));
+ }
+}
+
+/**********************************************************************************/
+
+/*
+ * SANE_Status sane_set_io_mode()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ /* get rid of compiler warning */
+ handle = handle;
+ non_blocking = non_blocking;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * SANE_Status sane_get_select_fd()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ /* get rid of compiler warnings */
+ handle = handle;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+vim:ts=2:sw=2:cindent:
+*/
diff --git a/backend/epson.conf.in b/backend/epson.conf.in
new file mode 100644
index 0000000..2cd505f
--- /dev/null
+++ b/backend/epson.conf.in
@@ -0,0 +1,27 @@
+# epson.conf
+#
+# here are some examples for how to configure the EPSON backend
+#
+# SCSI scanner:
+scsi EPSON
+# for the GT-6500:
+scsi "EPSON SC"
+#
+# Parallel port scanner:
+#pio 0x278
+#pio 0x378
+#pio 0x3BC
+#
+# USB scanner:
+# There are two different methods of configuring a USB scanner: libusb and the kernel module
+# For any system with libusb support (which is pretty much any recent Linux distribution) the
+# following line is sufficient. This however assumes that the connected scanner (or to be more
+# accurate, it's device ID) is known to the backend.
+usb
+# For libusb support for unknown scanners use the following command
+# usb <product ID> <device ID>
+# e.g.:
+# usb 0x4b8 0x110
+# And for the scanner module, use the following configuration:
+#usb /dev/usbscanner0
+#usb /dev/usb/scanner0
diff --git a/backend/epson.h b/backend/epson.h
new file mode 100644
index 0000000..cacfc80
--- /dev/null
+++ b/backend/epson.h
@@ -0,0 +1,262 @@
+/* epson.h - SANE library for Epson flatbed scanners.
+
+ based on Kazuhiro Sasayama previous
+ Work on epson.[ch] file from the SANE package.
+
+ original code taken from sane-0.71
+ Copyright (C) 1997 Hypercore Software Design, Ltd.
+
+ modifications
+ Copyright (C) 1998-1999 Christian Bucher <bucher@vernetzt.at>
+ Copyright (C) 1998-1999 Kling & Hautzinger GmbH
+ Copyright (C) 1999 Norihiko Sawa <sawa@yb3.so-net.ne.jp>
+ Copyright (C) 2000 Karl Heinz Kremer <khk@khk.net>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef epson_h
+#define epson_h
+
+#include <sys/ioctl.h>
+
+#include <sys/types.h>
+
+/* some string constants that are used in the config file */
+
+#define SANE_EPSON_CONFIG_USB "usb"
+#define SANE_EPSON_CONFIG_PIO "pio"
+
+/* string constants for GUI elements that are not defined SANE-wide */
+
+#define SANE_NAME_GAMMA_CORRECTION "gamma-correction"
+#define SANE_TITLE_GAMMA_CORRECTION SANE_I18N("Gamma Correction")
+#define SANE_DESC_GAMMA_CORRECTION SANE_I18N("Selects the gamma correction value from a list of pre-defined devices or the user defined table, which can be downloaded to the scanner")
+
+#define SANE_EPSON_FOCUS_NAME "focus-position"
+#define SANE_EPSON_FOCUS_TITLE SANE_I18N("Focus Position")
+#define SANE_EPSON_FOCUS_DESC SANE_I18N("Sets the focus position to either the glass or 2.5mm above the glass")
+#define SANE_EPSON_WAIT_FOR_BUTTON_NAME "wait-for-button"
+#define SANE_EPSON_WAIT_FOR_BUTTON_TITLE SANE_I18N("Wait for Button")
+#define SANE_EPSON_WAIT_FOR_BUTTON_DESC SANE_I18N("After sending the scan command, wait until the button on the scanner is pressed to actually start the scan process.");
+
+
+#define LINES_SHUFFLE_MAX (17) /* 2 x 8 lines plus 1 */
+
+#define SANE_EPSON_MAX_RETRIES (120) /* how often do we retry during warmup ? */
+
+typedef struct
+{
+ char *level;
+
+ unsigned char request_identity;
+ unsigned char request_identity2; /* new request identity command for Dx command level */
+ unsigned char request_status;
+ unsigned char request_condition;
+ unsigned char set_color_mode;
+ unsigned char start_scanning;
+ unsigned char set_data_format;
+ unsigned char set_resolution;
+ unsigned char set_zoom;
+ unsigned char set_scan_area;
+ unsigned char set_bright;
+ SANE_Range bright_range;
+ unsigned char set_gamma;
+ unsigned char set_halftoning;
+ unsigned char set_color_correction;
+ unsigned char initialize_scanner;
+ unsigned char set_speed; /* B4 and later */
+ unsigned char set_lcount;
+ unsigned char mirror_image; /* B5 and later */
+ unsigned char set_gamma_table; /* B4 and later */
+ unsigned char set_outline_emphasis; /* B4 and later */
+ unsigned char set_dither; /* B4 and later */
+ unsigned char set_color_correction_coefficients; /* B3 and later */
+ unsigned char request_extended_status; /* get extended status from scanner */
+ unsigned char control_an_extension; /* for extension control */
+ unsigned char eject; /* for extension control */
+ unsigned char feed;
+ unsigned char request_push_button_status;
+ unsigned char control_auto_area_segmentation;
+ unsigned char set_film_type; /* for extension control */
+ unsigned char set_exposure_time; /* F5 only */
+ unsigned char set_bay; /* F5 only */
+ unsigned char set_threshold;
+ unsigned char set_focus_position; /* B8 only */
+ unsigned char request_focus_position; /* B8 only */
+} EpsonCmdRec, *EpsonCmd;
+
+enum
+{ OPT_NUM_OPTS =
+ 0, OPT_MODE_GROUP, OPT_MODE, OPT_BIT_DEPTH, OPT_HALFTONE, OPT_DROPOUT,
+ OPT_BRIGHTNESS, OPT_SHARPNESS, OPT_GAMMA_CORRECTION, OPT_COLOR_CORRECTION,
+ OPT_RESOLUTION, OPT_THRESHOLD, OPT_ADVANCED_GROUP, OPT_MIRROR, OPT_SPEED,
+ OPT_AAS, OPT_LIMIT_RESOLUTION, OPT_ZOOM, /* OPT_GAMMA_VECTOR */
+ OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B,
+ OPT_WAIT_FOR_BUTTON, OPT_CCT_GROUP, OPT_CCT_1, OPT_CCT_2, OPT_CCT_3,
+ OPT_CCT_4, OPT_CCT_5, OPT_CCT_6, OPT_CCT_7, OPT_CCT_8, OPT_CCT_9,
+ OPT_PREVIEW_GROUP, OPT_PREVIEW, OPT_PREVIEW_SPEED, OPT_GEOMETRY_GROUP,
+ OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_QUICK_FORMAT, OPT_EQU_GROUP,
+ OPT_SOURCE, OPT_AUTO_EJECT, OPT_FILM_TYPE, OPT_FOCUS, OPT_BAY,
+ OPT_EJECT, OPT_ADF_MODE, NUM_OPTIONS
+};
+
+typedef enum
+{ /* hardware connection to the scanner */
+ SANE_EPSON_NODEV, /* default, no HW specified yet */
+ SANE_EPSON_SCSI, /* SCSI interface */
+ SANE_EPSON_PIO, /* parallel interface */
+ SANE_EPSON_USB /* USB interface */
+} Epson_Connection_Type;
+
+
+typedef struct
+{
+ u_short opt_resolution;
+ u_char sensor;
+ u_char scan_order;
+ u_char line_dist1;
+ u_char line_dist2;
+
+ u_short main_res1;
+ u_short main_res2;
+ u_short main_res3;
+ u_short main_res4;
+ u_short main_res5;
+ u_short main_res6;
+ u_short main_res7;
+
+ u_short sub_res1;
+ u_short sub_res2;
+ u_short sub_res3;
+ u_short sub_res4;
+ u_short sub_res5;
+ u_short sub_res6;
+} Epson_Identity2;
+
+
+struct Epson_Device
+{
+ struct Epson_Device *next;
+ SANE_Device sane;
+ SANE_Int level;
+ SANE_Range dpi_range;
+
+ SANE_Range *x_range; /* x range w/out extension */
+ SANE_Range *y_range; /* y range w/out extension */
+
+ SANE_Range fbf_x_range; /* flattbed x range */
+ SANE_Range fbf_y_range; /* flattbed y range */
+ SANE_Range adf_x_range; /* autom. document feeder x range */
+ SANE_Range adf_y_range; /* autom. document feeder y range */
+ SANE_Range tpu_x_range; /* transparency unit x range */
+ SANE_Range tpu_y_range; /* transparency unit y range */
+
+ Epson_Connection_Type connection;
+ /* hardware interface type */
+
+ SANE_Int *res_list; /* list of resolutions */
+ SANE_Int res_list_size; /* number of entries in this list */
+ SANE_Int last_res; /* last selected resolution */
+ SANE_Int last_res_preview; /* last selected preview resolution */
+
+ SANE_Word *resolution_list; /* for display purposes we store a second copy */
+
+ SANE_Bool extension; /* extension is installed */
+ SANE_Int use_extension; /* use the installed extension */
+ SANE_Bool TPU; /* TPU is installed */
+ SANE_Bool ADF; /* ADF is installed */
+ SANE_Bool duplexSupport; /* does the ADF handle duplex scanning */
+ SANE_Bool focusSupport; /* does this scanner have support for "set focus position" ? */
+ SANE_Bool color_shuffle; /* does this scanner need color shuffling */
+ SANE_Int maxDepth; /* max. color depth */
+
+ SANE_Int optical_res; /* optical resolution */
+ SANE_Int max_line_distance;
+
+ SANE_Bool need_double_vertical;
+ SANE_Bool need_color_reorder;
+ SANE_Bool need_reset_on_source_change;
+
+ SANE_Bool wait_for_button; /* do we have to wait until the scanner button is pressed? */
+
+ SANE_Int fbf_max_x;
+ SANE_Int fbf_max_y;
+ SANE_Int adf_max_x;
+ SANE_Int adf_max_y;
+
+ SANE_Int devtype;
+
+
+ EpsonCmd cmd;
+};
+
+typedef struct Epson_Device Epson_Device;
+
+
+
+struct Epson_Scanner
+{
+ struct Epson_Scanner *next;
+ int fd;
+ Epson_Device *hw;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+ SANE_Bool block;
+ SANE_Bool eof;
+ SANE_Byte *buf, *end, *ptr;
+ SANE_Bool canceling;
+ SANE_Bool invert_image;
+ SANE_Bool focusOnGlass;
+ SANE_Byte currentFocusPosition;
+/* SANE_Word gamma_table [ 4] [ 256]; */
+ SANE_Word gamma_table[3][256];
+ SANE_Int retry_count;
+ SANE_Byte *line_buffer[LINES_SHUFFLE_MAX];
+ /* buffer lines for color shuffling */
+ SANE_Int color_shuffle_line; /* current line number for color shuffling */
+ SANE_Int line_distance; /* current line distance */
+ SANE_Int current_output_line; /* line counter when color shuffling */
+ SANE_Int lines_written; /* debug variable */
+ SANE_Bool option_has_changed; /* did one of the options change it's value? */
+};
+
+typedef struct Epson_Scanner Epson_Scanner;
+
+#endif /* not epson_h */
diff --git a/backend/epson2-cct.c b/backend/epson2-cct.c
new file mode 100644
index 0000000..8ebed70
--- /dev/null
+++ b/backend/epson2-cct.c
@@ -0,0 +1,613 @@
+/* epson2 hardware colour correction coefficients
+ * Copyright (C) 2001-2009 SEIKO EPSON Corporation
+ * Copyright (C) 2009 Tower Technologies
+ *
+ * License: GPLv2
+ *
+ * This file is part of the SANE epson2 backend and has been derived
+ * from the epkowa backend distributed with Image Scan!
+ *
+ */
+
+/*! Hardware colour correction coefficients (CCC).
+
+ Each entry starts with a unique identifier, followed by four CCC
+ profiles; the first is for reflective materials, the second for
+ colour negatives, the third for monochrome negatives, and the
+ fourth and last one is for colour positives.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "epson2.h"
+
+const struct epson_profile epson_cct_profiles[] = {
+ {0x00, /* default */
+ {{1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x05, /* ES-6000H */
+ {{1.1419,-0.0596,-0.0825,-0.1234, 1.2812,-0.1413, 0.0703,-0.5720, 1.5016},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1419,-0.0596,-0.0825,-0.1234, 1.2812,-0.1413, 0.0703,-0.5720, 1.5016}}},
+ {0x06, /* GT-6600 */
+ {{1.1442,-0.0705,-0.0737,-0.0702, 1.1013,-0.0311,-0.0080,-0.3588, 1.3668},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x07, /* GT-7600 */
+ {{1.1967,-0.1379,-0.0588,-0.0538, 1.0385, 0.0153, 0.0348,-0.4070, 1.3721},
+ {1.0010,-0.0010, 0.0000,-0.1120, 1.1710,-0.0590, 0.0000,-0.0910, 1.0920},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1967,-0.1379,-0.0588,-0.0538, 1.0385, 0.0153, 0.0348,-0.4070, 1.3721}}},
+ {0x0D, /* ES-2000 */
+ {{1.1980,-0.1365,-0.0616,-0.1530, 1.1729,-0.0198,-0.0025,-0.2776, 1.2801},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1980,-0.1365,-0.0616,-0.1530, 1.1729,-0.0198,-0.0025,-0.2776, 1.2801}}},
+ {0x0F, /* ES-8500 */
+ {{1.0961,-0.0181,-0.0779,-0.1279, 1.1957,-0.0678, 0.0315,-0.3891, 1.3576},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0961,-0.0181,-0.0779,-0.1279, 1.1957,-0.0678, 0.0315,-0.3891, 1.3576}}},
+ {0x15, /* GT-6700 */
+ {{1.0999,-0.0425,-0.0574,-0.0806, 1.0835,-0.0028, 0.0057,-0.2924, 1.2866},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x16, /* GT-8700 */
+ {{1.2020,-0.1518,-0.0502,-0.0847, 1.1385,-0.0538, 0.0059,-0.3255, 1.3196},
+ {1.0030,-0.0030, 0.0000,-0.0980, 1.1500,-0.0520,-0.0030,-0.0840, 1.0880},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2456,-0.1617,-0.0839,-0.1160, 1.1862,-0.0702,-0.0036,-0.3438, 1.3473}}},
+ {0x18, /* GT-7700 */
+ {{1.1339,-0.0526,-0.0813,-0.1177, 1.1661,-0.0485,-0.0030,-0.3298, 1.3328},
+ {1.0010,-0.0010, 0.0000,-0.1120, 1.1710,-0.0590, 0.0000,-0.0910, 1.0920},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2066,-0.0360,-0.1706,-0.1313, 1.2523,-0.1210,-0.0299,-0.3377, 1.3676}}},
+ {0x1A, /* ES-9000H */
+ {{1.0986, 0.0235,-0.1221,-0.1294, 1.0896, 0.0399, 0.0928,-0.6043, 1.5115},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x1B, /* ES-2200 */
+ {{1.1855,-0.1372,-0.0483,-0.2060, 1.2468,-0.0407, 0.0358,-0.3059, 1.2701},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1976,-0.1182,-0.0794,-0.1578, 1.2720,-0.1142, 0.0122,-0.3467, 1.3345}}},
+ {0x1D, /* GT-7200 */
+ {{1.0675,-0.0586,-0.0088,-0.0332, 0.9716, 0.0616, 0.0175,-0.4054, 1.3879},
+ {1.0090,-0.0090, 0.0000,-0.0390, 1.0750,-0.0360,-0.0070,-0.1060, 1.1130},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1394,-0.0829,-0.0564,-0.0003, 1.0008,-0.0004,-0.0059,-0.3674, 1.3733}}},
+ {0x1F, /* GT-8200 */
+ {{1.0800,-0.0607,-0.0193,-0.0787, 1.0846,-0.0059, 0.0135,-0.3334, 1.3199},
+ {1.0040,-0.0040, 0.0000,-0.0780, 1.1360,-0.0570,-0.0020,-0.0810, 1.0830},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1334,-0.0929,-0.0405,-0.0418, 1.0689,-0.0271,-0.0521,-0.3262, 1.3783}}},
+ {0x21, /* GT-9700 */
+ {{1.0919,-0.0739,-0.0180,-0.0941, 1.1150,-0.0209, 0.0220,-0.3744, 1.3524},
+ {1.0090,-0.0100, 0.0010,-0.0720, 1.1310,-0.0600, 0.0000,-0.1000, 1.1000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1374,-0.1396, 0.0021,-0.0489, 1.0655,-0.0166, 0.0081,-0.3492, 1.3411}}},
+ {0x23, /* GT-7300 */
+ {{1.0339,-0.0166,-0.0173,-0.0117, 0.9797, 0.0319, 0.0010,-0.3609, 1.3599},
+ {1.0090,-0.0090, 0.0000,-0.0390, 1.0750,-0.0360,-0.0070,-0.1060, 1.1130},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1666,-0.0898,-0.0768,-0.0076, 1.0157,-0.0081, 0.0012,-0.3048, 1.3036}}},
+ {0x25, /* GT-8300 */
+ {{1.0800,-0.0607,-0.0193,-0.0787, 1.0846,-0.0059, 0.0135,-0.3334, 1.3199},
+ {1.0040,-0.0040, 0.0000,-0.0780, 1.1360,-0.0570,-0.0020,-0.0810, 1.0830},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1334,-0.0929,-0.0405,-0.0418, 1.0689,-0.0271,-0.0521,-0.3262, 1.3783}}},
+ {0x27, /* GT-9300 */
+ {{1.0919,-0.0739,-0.0180,-0.0941, 1.1150,-0.0209, 0.0220,-0.3744, 1.3524},
+ {1.0083,-0.0094, 0.0011,-0.0760, 1.1379,-0.0619,-0.0002,-0.0945, 1.0947},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1952,-0.1519,-0.0433,-0.0932, 1.1613,-0.0681,-0.0418,-0.3140, 1.3558}}},
+ {0x29, /* GT-9800F */
+ {{1.0369,-0.0210,-0.0160,-0.0820, 1.1160,-0.0341, 0.0150,-0.5035, 1.4885},
+ {1.0122,-0.0151, 0.0029,-0.0861, 1.1402,-0.0542,-0.0061,-0.1607, 1.1669},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1764,-0.1749,-0.0014,-0.0590, 1.0983,-0.0393, 0.0208,-0.5194, 1.4986}}},
+ {0x2B, /* ES-7000H */
+ {{1.0305,-0.0116,-0.0189,-0.0936, 1.1245,-0.0309,-0.0072,-0.1413, 1.1485},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x32, /* GT-9400 */
+ {{1.0932,-0.0529,-0.0403,-0.1077, 1.1416,-0.0338, 0.0079,-0.5525, 1.5446},
+ {1.0259,-0.0356, 0.0097,-0.1085, 1.2225,-0.1140,-0.0046,-0.1848, 1.1894},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2720,-0.2665,-0.0054,-0.0672, 1.1301,-0.0629,-0.0048,-0.3917, 1.3965}}},
+ {0x2D, /* CC-600PX */
+ {{1.0436,-0.0078,-0.0359,-0.0169, 1.0114, 0.0056, 0.0308,-0.4425, 1.4117},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x3A, /* PM-A850 */
+ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806},
+ {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}},
+ {0x36, /* CX5300/CX5400 */
+ {{1.0848,-0.0153,-0.0695,-0.0902, 1.0611, 0.0291, 0.0344,-0.5002, 1.4658},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x34, /* GT-X700 */
+ {{1.1032,-0.0590,-0.0442,-0.1915, 1.3371,-0.1456, 0.0387,-0.5804, 1.5417},
+ {1.0232,-0.0258, 0.0026,-0.1296, 1.2882,-0.1587,-0.0011,-0.1928, 1.1940},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2662,-0.2664, 0.0002,-0.1050, 1.3168,-0.2118,-0.0058,-0.4370, 1.4428}}},
+ {0x38, /* RX500/RX510 */
+ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806},
+ {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}},
+ {0x37, /* CX6300/CX6400 */
+ {{0.9640, 0.1455,-0.1095, 0.0108, 1.1933,-0.2041, 0.0071,-0.3487, 1.3416},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x3F, /* ES-10000G */
+ {{1.1223,-0.0985,-0.0238,-0.0847, 1.1502,-0.0655, 0.0118,-0.5022, 1.4904},
+ {1.0077,-0.0129, 0.0052,-0.0904, 1.1785,-0.0881, 0.0000,-0.1528, 1.1528},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1927,-0.1646,-0.0280,-0.0655, 1.1033,-0.0378, 0.0034,-0.4173, 1.4139}}},
+ {0x41, /* GT-F500/F550 */
+ {{1.0732,-0.0581,-0.0150,-0.0897, 1.1553,-0.0657,-0.0179,-0.6500, 1.6679},
+ {1.0163,-0.0203, 0.0040,-0.1125, 1.1797,-0.0672,-0.0091,-0.2343, 1.2434},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2437,-0.2022,-0.0415,-0.0352, 1.0735,-0.0383,-0.0188,-0.5020, 1.5209}}},
+ {0x43, /* GT-F600 */
+ {{1.0782,-0.0697,-0.0085,-0.1605, 1.2862,-0.1257, 0.0148,-0.5854, 1.5706},
+ {1.0136,-0.0151, 0.0016,-0.1836, 1.3422,-0.1586,-0.0014,-0.1851, 1.1865},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1491,-0.1456,-0.0035,-0.0990, 1.2657,-0.1666, 0.0015,-0.3868, 1.3853}}},
+ {0x46, /* CX3500/CX3600/CX4500/CX4600 */
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x48, /* PM-A700/RX420/RX430 */
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x49, /* CX6500/CX6600 */
+ {{0.9640, 0.1455,-0.1095, 0.0108, 1.1933,-0.2041, 0.0071,-0.3487, 1.3416},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x4B, /* PM-A870 */
+ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806},
+ {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}},
+ {0x4D, /* PM-A900 */
+ {{1.1011,-0.0824,-0.0186,-0.0970, 1.1991,-0.1021,-0.0161,-0.6247, 1.6408},
+ {1.0259,-0.0356, 0.0097,-0.1085, 1.2225,-0.1140,-0.0046,-0.1848, 1.1894},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2150,-0.2074,-0.0076,-0.0521, 1.1430,-0.0909,-0.0204,-0.4156, 1.4360}}},
+ {0x4F, /* GT-X800 */
+ {{1.1052,-0.0850,-0.0202,-0.1050, 1.2294,-0.1245,-0.0486,-0.4160, 1.4646},
+ {1.0255,-0.0272, 0.0017,-0.0919, 1.2098,-0.1180,-0.0021,-0.1296, 1.1317},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2950,-0.2619,-0.0332,-0.0562, 1.1587,-0.1025,-0.0397,-0.3100, 1.3497}}},
+ {0x51, /* LP-A500 */
+ {{1.0614,-0.0361,-0.0253,-0.1081, 1.1320,-0.0240,-0.0536,-0.2045, 1.2580},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x52, /* GT-F520/F570 */
+ {{1.0978,-0.0806,-0.0173,-0.0802, 1.1515,-0.0713,-0.0476,-0.4656, 1.5132},
+ {1.0192,-0.0192, 0.0000,-0.0974, 1.1846,-0.0872,-0.0031,-0.1797, 1.1828},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2490,-0.2030,-0.0460,-0.0469, 1.1046,-0.0577,-0.0361,-0.3857, 1.4217}}},
+ {0x54, /* GT-X750 */
+ {{1.0905,-0.0654,-0.0251,-0.1030, 1.1801,-0.0771,-0.0685,-0.4238, 1.4923},
+ {1.0206,-0.0207, 0.0000,-0.0890, 1.1770,-0.0880,-0.0014,-0.1450, 1.1464},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.3041,-0.2907,-0.0134,-0.0383, 1.0908,-0.0525,-0.0327,-0.2947, 1.3275}}},
+ {0x56, /* LP-M5500 */
+ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x57, /* Stylus CX3700/CX3800/DX3800 */
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x58, /* PX-A650/Stylus CX4700/CX4800/DX4800 */
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x59, /* Stylus CX4100/CX4200/DX4200 */
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x5B, /* Stylus CX7700/CX7800 */
+ {{0.9764, 0.1095,-0.0859, 0.0149, 1.1154,-0.1303, 0.0051,-0.2851, 1.2800},
+ {1.0024,-0.0149, 0.0124,-0.2569, 1.3432,-0.0864,-0.0043,-0.1306, 1.1349},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1003,-0.0493,-0.0510,-0.1607, 1.2748,-0.1142,-0.0059,-0.3161, 1.3220}}},
+ {0x5D, /* Stylus Photo RX520/RX530 */
+ {{0.9764, 0.1095,-0.0859, 0.0149, 1.1154,-0.1303, 0.0051,-0.2851, 1.2800},
+ {1.0024,-0.0149, 0.0124,-0.2569, 1.3432,-0.0864,-0.0043,-0.1306, 1.1349},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1003,-0.0493,-0.0510,-0.1607, 1.2748,-0.1142,-0.0059,-0.3161, 1.3220}}},
+ {0x5F, /* Stylus Photo RX640/RX650 */
+ {{1.0697,-0.0561,-0.0137,-0.0824, 1.1291,-0.0467,-0.0390,-0.5218, 1.5608},
+ {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2606,-0.2125,-0.0482,-0.0567, 1.1441,-0.0874,-0.0431,-0.3490, 1.3921}}},
+ {0x61, /* PM-A950 */
+ {{1.0921,-0.0722,-0.0199,-0.0831, 1.1550,-0.0718,-0.0452,-0.3721, 1.4173},
+ {1.0168,-0.0168, 0.0000,-0.0953, 1.1928,-0.0975,-0.0012,-0.1235, 1.1247},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2603,-0.2763, 0.0155,-0.0398, 1.1033,-0.0635,-0.0249,-0.2675, 1.2924}}},
+ {0x63, /* GT-X900 */
+ {{1.0976,-0.0789,-0.0187,-0.0958, 1.1821,-0.0863,-0.0565,-0.4179, 1.4744},
+ {1.0250,-0.0267, 0.0016,-0.0930, 1.2108,-0.1178,-0.0022,-0.1296, 1.1317},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.3111,-0.2979,-0.0132,-0.0441, 1.1148,-0.0707,-0.0348,-0.2971, 1.3319}}},
+ {0x65, /* ES-H300 */
+ {{1.0359,-0.0146,-0.0213,-0.0752, 1.0963,-0.0211,-0.0456,-0.3238, 1.3693},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x66, /* GT-S600/F650, Perfection V10/V100 */
+ {{1.0878,-0.0667,-0.0211,-0.0892, 1.1513,-0.0622,-0.0654,-0.5175, 1.5829},
+ {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2688,-0.2522,-0.0166,-0.0559, 1.1291,-0.0733,-0.0377,-0.3519, 1.3896}}},
+ {0x68, /* GT-F700, Perfection V350 */
+ {{1.0950,-0.0646,-0.0305,-0.0792, 1.1398,-0.0606,-0.0123,-0.5175, 1.5298},
+ {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2697,-0.2501,-0.0195,-0.0351, 1.1236,-0.0885,-0.0131,-0.3268, 1.3400}}},
+ {0x6A, /* Stylus CX2800/CX2900/ME200 */
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x6B, /* Stylus PX-A620, CX3900/DX4000 */
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x6C, /* Stylus CX5900/CX6000/DX6000 */
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x70, /* Stylus Photo RX560/RX580/RX590 */
+ {{0.9533, 0.0885,-0.0418, 0.0033, 1.0627,-0.0660,-0.0137,-0.1904, 1.2041},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x71, /* PM-A920 */
+ {{1.0697,-0.0561,-0.0137,-0.0824, 1.1291,-0.0467,-0.0390,-0.5218, 1.5608},
+ {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2606,-0.2125,-0.0482,-0.0567, 1.1441,-0.0874,-0.0431,-0.3490, 1.3921}}},
+ {0x73, /* PM-A970 */
+ {{1.0828,-0.0739,-0.0089,-0.0895, 1.1597,-0.0702,-0.0531,-0.4291, 1.4822},
+ {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2579,-0.2384,-0.0195,-0.0569, 1.1454,-0.0884,-0.0411,-0.3072, 1.3483}}},
+ {0x75, /* PM-T990 */
+ {{1.0828,-0.0739,-0.0089,-0.0895, 1.1597,-0.0702,-0.0531,-0.4291, 1.4822},
+ {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2579,-0.2384,-0.0195,-0.0569, 1.1454,-0.0884,-0.0411,-0.3072, 1.3483}}},
+ {0x77, /* Stylus CX4900/CX5000/DX5000 */
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x78, /* LP-M5600 */
+ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x79, /* AcuLaser CX21 */
+ {{1.0614,-0.0361,-0.0253,-0.1081, 1.1320,-0.0240,-0.0536,-0.2045, 1.2580},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x7A, /* GT-F670, Perfection V200 */
+ {{1.1754,-0.1173,-0.0580,-0.0687, 1.1307,-0.0620,-0.0255,-0.4699, 1.4954},
+ {1.0150,-0.0173, 0.0022,-0.0853, 1.2238,-0.1384,-0.0073,-0.1490, 1.1562},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4283,-0.4335, 0.0052,-0.0170, 1.1308,-0.1138,-0.0147,-0.2230, 1.2377}}},
+ {0x7C, /* GT-X770, Perfection V500 */
+ {{1.2470,-0.2041,-0.0429,-0.1920, 1.2918,-0.0998,-0.0100,-0.2503, 1.2603},
+ {1.0050,-0.0076, 0.0026,-0.2532, 1.1289, 0.1243,-0.0733,-0.0960, 1.1693},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4724,-0.4599,-0.0125,-0.0876, 1.1562,-0.0686,-0.0097,-0.2278, 1.2375}}},
+ {0x7E, /* Stylus CX4300/CX4400/CX5500/CX5600/DX4400 */
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x7F, /* PX-A640, Stylus CX7300/CX7400/DX7400 */
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x80, /* PX-A740, Stylus CX8300/CX8400/DX8400 */
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x81, /* PX-FA700, Stylus CX9300F/CX9400Fax/DX9400F */
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x82, /* PM-T960 */
+ {{1.1622,-0.1102,-0.0519,-0.0717, 1.1060,-0.0343,-0.0248,-0.4138, 1.4385},
+ {0.9913, 0.0082, 0.0005,-0.1259, 1.0452, 0.0807,-0.0072,-0.0767, 1.0839},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.3900,-0.3008,-0.0892,-0.0254, 1.0890,-0.0636,-0.0300,-0.2501, 1.2801}}},
+ {0x84, /* PM-A940, Stylus Photo RX680/RX685/RX690 */
+ {{1.0934,-0.0042,-0.0892, 0.0052, 1.1019,-0.1071, 0.0259,-0.2651, 1.2392},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x85, /* PM-A840/A840S, Stylus Photo RX585/RX595/RX610 */
+ {{1.0534, 0.0399,-0.0934, 0.0098, 1.0589,-0.0687, 0.0016,-0.1131, 1.1115},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x86, /* GT-D1000, GT-1500 */
+ {{1.1945,-0.1413,-0.0532,-0.1929, 1.2525,-0.0596,-0.0235,-0.2761, 1.2996},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x87, /* GT-X970 */
+ {{1.1978,-0.1417,-0.0561,-0.0852, 1.1610,-0.0758,-0.0395,-0.3212, 1.3607},
+ {1.0000, 0.0009,-0.0009,-0.1268, 1.0523, 0.0745,-0.0075,-0.0873, 1.0948},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4475,-0.3957,-0.0518,-0.0138, 1.0644,-0.0506,-0.0199,-0.2050, 1.2249}}},
+ {0x97, /* LP-M5000 */
+ {{1.1115,-0.0377,-0.0738,-0.0658, 1.0624, 0.0034, 0.0042,-0.2883, 1.2841},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x89, /* LP-M6000 */
+ {{1.1115,-0.0377,-0.0738,-0.0658, 1.0624, 0.0034, 0.0042,-0.2883, 1.2841},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8A, /* ES-H7200, GT-20000 */
+ {{1.1221,-0.0396,-0.0825,-0.0718, 1.0822,-0.0104, 0.0112,-0.2995, 1.2883},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8B, /* GT-F720/S620, Perfection V30/V300 */
+ {{1.2402,-0.1891,-0.0511,-0.1535, 1.2008,-0.0473,-0.0316,-0.3293, 1.3609},
+ {1.0027,-0.0048, 0.0021,-0.2067, 1.0878, 0.1189,-0.0408,-0.0767, 1.1175},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4524,-0.4346,-0.0178,-0.0601, 1.1273,-0.0672,-0.0173,-0.1823, 1.1996}}},
+ {0x8D, /* Stylus NX200/SX200/TX200 */
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8E, /* PX-501A, Stylus NX400/SX400/TX400 */
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8F, /* Stylus NX300 / Stylus Office BX300F/TX300F / ME Office 600F */
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x90, /* PX-601F, Stylus SX600FW/TX600FW / Stylus Office BX600FW / WorkForce 600 */
+ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x91, /* EP-901A/901F, Artisan 800 / Stylus Photo PX800FW/FX800FW */
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x92, /* EP-801A, Artisan 700 / Stylus Photo PX700W/TX700W */
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x93, /* PX-401A, Stylus NX100/SX100/TX100 / ME 300 */
+ {{1.0934,-0.0042,-0.0892, 0.0052, 1.1019,-0.1071, 0.0259,-0.2651, 1.2392},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x96, /* WorkForce 500 */
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x98,
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x99,
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9A,
+ {{1.0779, 0.0132,-0.0911, 0.0214, 1.1003,-0.1217, 0.0109,-0.1487, 1.1378},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9B,
+ {{1.0779, 0.0132,-0.0911, 0.0214, 1.1003,-0.1217, 0.0109,-0.1487, 1.1378},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9C,
+ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9D,
+ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9E,
+ {{1.0534, 0.0399,-0.0934, 0.0098, 1.0589,-0.0687, 0.0016,-0.1131, 1.1115},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9F,
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0xA0,
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0xA1,
+ {{1.2578,-0.2140,-0.0438,-0.1939, 1.2856,-0.0917,-0.0258,-0.2642, 1.2900},
+ {0.9989,-0.0018, 0.0029,-0.2608, 1.1305, 0.1303,-0.0802,-0.0807, 1.1609},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4431,-0.4193,-0.0238,-0.0915, 1.1507,-0.0592,-0.0226,-0.1978, 1.2204}}},
+
+ {0xFF, /* terminator */
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0}}}
+};
+
+/* cat hw-data.c | grep '{"' | cut -d ',' -f1,2 | awk '{ print $0 " },"}' */
+
+const struct epson_profile_map epson_cct_models[] = {
+ {"GT-10000", 0x05 },
+ {"ES-6000", 0x05 },
+ {"Perfection610", 0x06 },
+ {"GT-6600", 0x06 },
+ {"Perfection1200", 0x07 },
+ {"GT-7600", 0x07 },
+ {"Expression1600", 0x0D },
+ {"ES-2000", 0x0D },
+ {"Expression1640XL", 0x0F },
+ {"ES-8500", 0x0F },
+ {"Perfection640", 0x15 },
+ {"GT-6700", 0x15 },
+ {"Perfection1640", 0x16 },
+ {"GT-8700", 0x16 },
+ {"Perfection1240", 0x18 },
+ {"GT-7700", 0x18 },
+ {"GT-30000", 0x1A },
+ {"ES-9000H", 0x1A },
+ {"Expression1680", 0x1B },
+ {"ES-2200", 0x1B },
+ {"GT-7200", 0x1D },
+ {"GT-8200", 0x1F },
+ {"GT-9700", 0x21 },
+ {"GT-7300", 0x23 },
+ {"GT-8300", 0x25 },
+ {"GT-9300", 0x27 },
+ {"GT-9800", 0x29 },
+ {"ES-7000H", 0x2B },
+ {"LP-A500", 0x51 },
+ {"AL-CX11", 0x51 },
+ {"GT-9400", 0x32 },
+ {"CC-600PX", 0x2D },
+ {"PM-A850", 0x3A },
+ {"CX5400", 0x36 },
+ {"GT-X700", 0x34 },
+ {"RX500", 0x38 },
+ {"PX-A650", 0x37 },
+ {"ES-10000G", 0x3F },
+ {"Expression10000", 0x3F },
+ {"CX4600", 0x46 },
+ {"CX6600", 0x49 },
+ {"CX3600", 0x46 },
+ {"RX420", 0x48 },
+ {"PM-A700", 0x48 },
+ {"PM-A870", 0x4B },
+ {"GT-F500", 0x41 },
+ {"GT-F600", 0x43 },
+ {"PM-A900", 0x4D },
+ {"GT-X800", 0x4F },
+ {"GT-X750", 0x54 },
+ {"LP-M5500", 0x56 },
+ {"LP-M5600", 0x78 },
+ {"GT-F520", 0x52 },
+ {"CX3800", 0x57 },
+ {"CX7800", 0x5B },
+ {"PM-A750", 0x5D },
+ {"CX4800", 0x58 },
+ {"CX4200", 0x59 },
+ {"PM-A950", 0x61 },
+ {"PM-A890", 0x5F },
+ {"GT-X900", 0x63 },
+ {"CX4000", 0x6B },
+ {"CX3000v", 0x6A },
+ {"ES-H300", 0x65 },
+ {"CX6000", 0x6C },
+ {"PM-A820", 0x70 },
+ {"PM-A920", 0x71 },
+ {"PM-A970", 0x73 },
+ {"PM-T990", 0x75 },
+ {"CX5000", 0x77 },
+ {"GT-S600", 0x66 },
+ {"GT-F700", 0x68 },
+ {"AL-CX21", 0x79 },
+ {"GT-F670", 0x7A },
+ {"GT-X770", 0x7C },
+ {"CX4400", 0x7E },
+ {"CX7400", 0x7F },
+ {"CX8400", 0x80 },
+ {"CX9400Fax", 0x81 },
+ {"PM-T960", 0x82 },
+ {"PM-A940", 0x84 },
+ {"PM-A840", 0x85 },
+ {"GT-D1000", 0x86 },
+ {"GT-X970", 0x87 },
+ {"LP-M5000", 0x97 },
+ {"LP-M6000", 0x89 },
+ {"ES-H7200", 0x8A },
+ {"GT-20000", 0x8A },
+ {"NX200", 0x8D },
+ {"NX400", 0x8E },
+ {"NX100", 0x93 },
+ {"NX300", 0x8F },
+ {"WorkForce 600", 0x90 },
+ {"Artisan 800", 0x91 },
+ {"Artisan 700", 0x92 },
+ {"WorkForce 500", 0x96 },
+ {"GT-F720", 0x8B },
+ {"GT-S620", 0x8B },
+ {"GT-S50", 0x00 },
+ {"GT-S80", 0x00 },
+ {"PID 0851", 0x98 },
+ {"PID 084D", 0x99 },
+ {"PID 084F", 0x9A },
+ {"PID 0854", 0x9B },
+ {"PID 0856", 0x9C },
+ {"PID 0855", 0x9D },
+ {"PID 0850", 0x9E },
+ {"PID 0852", 0xA0 },
+ {"PID 0853", 0x9F },
+ {"GT-X820", 0xA1 },
+
+ {NULL, 0x00 } /* terminator */
+};
diff --git a/backend/epson2-commands.c b/backend/epson2-commands.c
new file mode 100644
index 0000000..a84630b
--- /dev/null
+++ b/backend/epson2-commands.c
@@ -0,0 +1,1088 @@
+/*
+ * ESC/I commands for Epson scanners
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sane/config.h"
+
+#include <byteorder.h>
+#include <math.h>
+#include <sys/types.h>
+
+#include "epson2.h"
+#include "epson2-io.h"
+#include "epson2-commands.h"
+
+
+/* ESC H, set zoom */
+SANE_Status
+esci_set_zoom(Epson_Scanner * s, unsigned char x, unsigned char y)
+{
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s: x = %d, y = %d\n", __func__, x, y);
+
+ if (!s->hw->cmd->set_zoom) {
+ DBG(1, "%s: not supported\n", __func__);
+ return SANE_STATUS_GOOD;
+ }
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_zoom;
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x;
+ params[1] = y;
+
+ return e2_cmd_simple(s, params, 2);
+}
+
+/* ESC R */
+SANE_Status
+esci_set_resolution(Epson_Scanner * s, int x, int y)
+{
+ SANE_Status status;
+ unsigned char params[4];
+
+ DBG(8, "%s: x = %d, y = %d\n", __func__, x, y);
+
+ if (!s->hw->cmd->set_resolution) {
+ DBG(1, "%s: not supported\n", __func__);
+ return SANE_STATUS_GOOD;
+ }
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_resolution;
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x;
+ params[1] = x >> 8;
+ params[2] = y;
+ params[3] = y >> 8;
+
+ return e2_cmd_simple(s, params, 4);
+}
+
+/*
+ * Sends the "set scan area" command to the scanner with the currently selected
+ * scan area. This scan area must be already corrected for "color shuffling" if
+ * necessary.
+ */
+
+SANE_Status
+esci_set_scan_area(Epson_Scanner * s, int x, int y, int width, int height)
+{
+ SANE_Status status;
+ unsigned char params[8];
+
+ DBG(8, "%s: x = %d, y = %d, w = %d, h = %d\n",
+ __func__, x, y, width, height);
+
+ if (!s->hw->cmd->set_scan_area) {
+ DBG(1, "%s: not supported\n", __func__);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* verify the scan area */
+ if (x < 0 || y < 0 || width <= 0 || height <= 0)
+ return SANE_STATUS_INVAL;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_scan_area;
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x;
+ params[1] = x >> 8;
+ params[2] = y;
+ params[3] = y >> 8;
+ params[4] = width;
+ params[5] = width >> 8;
+ params[6] = height;
+ params[7] = height >> 8;
+
+ return e2_cmd_simple(s, params, 8);
+}
+
+static int
+get_roundup_index(double frac[], int n)
+{
+ int i, index = -1;
+ double max_val = 0.0;
+
+ for (i = 0; i < n; i++) {
+
+ if (frac[i] < 0)
+ continue;
+
+ if (max_val < frac[i]) {
+ index = i;
+ max_val = frac[i];
+ }
+ }
+
+ return index;
+}
+
+static int
+get_rounddown_index(double frac[], int n)
+{
+ int i, index = -1;
+ double min_val = 1.0;
+
+ for (i = 0; i < n; i++) {
+
+ if (frac[i] > 0)
+ continue;
+
+ if (min_val > frac[i]) {
+ index = i;
+ min_val = frac[i];
+ }
+ }
+
+ return index;
+}
+
+static unsigned char
+int2cpt(int val)
+{
+ if (val >= 0) {
+
+ if (val > 127)
+ val = 127;
+
+ return (unsigned char) val;
+
+ } else {
+
+ val = -val;
+
+ if (val > 127)
+ val = 127;
+
+ return (unsigned char) (0x80 | val);
+ }
+}
+
+static void
+round_cct(double org_cct[], int rnd_cct[])
+{
+ int loop = 0;
+ int i, j, sum[3];
+ double mult_cct[9], frac[9];
+
+ for (i = 0; i < 9; i++) {
+ mult_cct[i] = org_cct[i] * 32;
+ rnd_cct[i] = (int) floor(mult_cct[i] + 0.5);
+ }
+
+ do {
+ for (i = 0; i < 3; i++) {
+
+ int k = i * 3;
+
+ if ((rnd_cct[k] == 11) &&
+ (rnd_cct[k] == rnd_cct[k + 1]) &&
+ (rnd_cct[k] == rnd_cct[k + 2])) {
+
+ rnd_cct[k + i]--;
+ mult_cct[k + i] = rnd_cct[k + i];
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+
+ int k = i * 3;
+
+ for (sum[i] = j = 0; j < 3; j++)
+ sum[i] += rnd_cct[k + j];
+ }
+
+ for (i = 0; i < 9; i++)
+ frac[i] = mult_cct[i] - rnd_cct[i];
+
+ for (i = 0; i < 3; i++) {
+
+ int k = i * 3;
+
+ if (sum[i] < 32) {
+
+ int index = get_roundup_index(&frac[k], 3);
+ if (index != -1) {
+ rnd_cct[k + index]++;
+ mult_cct[k + index] = rnd_cct[k + index];
+ sum[i]++;
+ }
+
+ } else if (sum[i] > 32) {
+
+ int index = get_rounddown_index(&frac[k], 3);
+ if (index != -1) {
+ rnd_cct[k + index]--;
+ mult_cct[k + index] = rnd_cct[k + index];
+ sum[i]--;
+ }
+ }
+ }
+ }
+
+ while ((++loop < 2)
+ && ((sum[0] != 32) || (sum[1] != 32) || (sum[2] != 32)));
+}
+
+static void
+profile_to_colorcoeff(double *profile, unsigned char *color_coeff)
+{
+ int cc_idx[] = { 4, 1, 7, 3, 0, 6, 5, 2, 8 };
+ int i, color_table[9];
+
+ round_cct(profile, color_table);
+
+ for (i = 0; i < 9; i++)
+ color_coeff[i] = int2cpt(color_table[cc_idx[i]]);
+}
+
+
+/*
+ * Sends the "set color correction coefficients" command with the
+ * currently selected parameters to the scanner.
+ */
+
+SANE_Status
+esci_set_color_correction_coefficients(Epson_Scanner * s, SANE_Word *table)
+{
+ SANE_Status status;
+ unsigned char params[2];
+ unsigned char data[9];
+ double cct[9];
+
+ DBG(8, "%s\n", __func__);
+ if (!s->hw->cmd->set_color_correction_coefficients) {
+ DBG(1, "%s: not supported\n", __func__);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_color_correction_coefficients;
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ cct[0] = SANE_UNFIX(table[0]);
+ cct[1] = SANE_UNFIX(table[1]);
+ cct[2] = SANE_UNFIX(table[2]);
+ cct[3] = SANE_UNFIX(table[3]);
+ cct[4] = SANE_UNFIX(table[4]);
+ cct[5] = SANE_UNFIX(table[5]);
+ cct[6] = SANE_UNFIX(table[6]);
+ cct[7] = SANE_UNFIX(table[7]);
+ cct[8] = SANE_UNFIX(table[8]);
+
+ profile_to_colorcoeff(cct, data);
+
+ DBG(11, "%s: %d,%d,%d %d,%d,%d %d,%d,%d\n", __func__,
+ data[0] , data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7], data[8]);
+
+ return e2_cmd_simple(s, data, 9);
+}
+
+SANE_Status
+esci_set_gamma_table(Epson_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char params[2];
+ unsigned char gamma[257];
+ int n;
+ int table;
+
+/* static const char gamma_cmds[] = { 'M', 'R', 'G', 'B' }; */
+ static const char gamma_cmds[] = { 'R', 'G', 'B' };
+
+ DBG(8, "%s\n", __func__);
+ if (!s->hw->cmd->set_gamma_table)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->set_gamma_table;
+
+ /* Print the gamma tables before sending them to the scanner */
+
+ if (DBG_LEVEL >= 10) {
+ int c, i, j;
+
+ for (c = 0; c < 3; c++) {
+ for (i = 0; i < 256; i += 16) {
+ char gammaValues[16 * 3 + 1], newValue[4];
+
+ gammaValues[0] = '\0';
+
+ for (j = 0; j < 16; j++) {
+ sprintf(newValue, " %02x",
+ s->gamma_table[c][i + j]);
+ strcat(gammaValues, newValue);
+ }
+ DBG(11, "gamma table[%d][%d] %s\n", c, i,
+ gammaValues);
+ }
+ }
+ }
+
+ for (table = 0; table < 3; table++) {
+ gamma[0] = gamma_cmds[table];
+
+ for (n = 0; n < 256; ++n)
+ gamma[n + 1] = s->gamma_table[table][n];
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = e2_cmd_simple(s, gamma, 257);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return status;
+}
+
+/* ESC F - Request Status
+ * -> ESC f
+ * <- Information block
+ */
+
+SANE_Status
+esci_request_status(SANE_Handle handle, unsigned char *scanner_status)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_status == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_status;
+
+ e2_send(s, params, 2, 4, &status);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = e2_recv_info_block(s, params, 4, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (scanner_status)
+ *scanner_status = params[0];
+
+ DBG(1, "status: %02x\n", params[0]);
+
+ if (params[0] & STATUS_NOT_READY)
+ DBG(1, " scanner in use on another interface\n");
+ else
+ DBG(1, " ready\n");
+
+ if (params[0] & STATUS_FER)
+ DBG(1, " system error\n");
+
+ if (params[0] & STATUS_OPTION)
+ DBG(1, " option equipment is installed\n");
+ else
+ DBG(1, " no option equipment installed\n");
+
+ if (params[0] & STATUS_EXT_COMMANDS)
+ DBG(1, " support extended commands\n");
+ else
+ DBG(1, " does NOT support extended commands\n");
+
+ if (params[0] & STATUS_RESERVED)
+ DBG(0,
+ " a reserved bit is set, please contact the author.\n");
+
+ return status;
+}
+
+/* extended commands */
+
+/* FS I, Request Extended Identity
+ * -> FS I
+ * <- Extended identity data (80)
+ *
+ * Request the properties of the scanner.
+ */
+
+SANE_Status
+esci_request_extended_identity(SANE_Handle handle, unsigned char *buf)
+{
+ unsigned char model[17];
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (buf == NULL)
+ return SANE_STATUS_INVAL;
+
+ if (s->hw->cmd->request_extended_identity == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = FS;
+ params[1] = s->hw->cmd->request_extended_identity;
+
+ status = e2_txrx(s, params, 2, buf, 80);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, " command level : %c%c\n", buf[0], buf[1]);
+ DBG(1, " basic resolution: %lu\n", (unsigned long) le32atoh(&buf[4]));
+ DBG(1, " min resolution : %lu\n", (unsigned long) le32atoh(&buf[8]));
+ DBG(1, " max resolution : %lu\n", (unsigned long) le32atoh(&buf[12]));
+ DBG(1, " max pixel num : %lu\n", (unsigned long) le32atoh(&buf[16]));
+ DBG(1, " scan area : %lux%lu\n",
+ (unsigned long) le32atoh(&buf[20]), (unsigned long) le32atoh(&buf[24]));
+
+ DBG(1, " adf area : %lux%lu\n",
+ (unsigned long) le32atoh(&buf[28]), (unsigned long) le32atoh(&buf[32]));
+
+ DBG(1, " tpu area : %lux%lu\n",
+ (unsigned long) le32atoh(&buf[36]), (unsigned long) le32atoh(&buf[40]));
+
+ DBG(1, " capabilities (1): 0x%02x\n", buf[44]);
+ DBG(1, " capabilities (2): 0x%02x\n", buf[45]);
+ DBG(1, " input depth : %d\n", buf[66]);
+ DBG(1, " max output depth: %d\n", buf[67]);
+ DBG(1, " rom version : %c%c%c%c\n",
+ buf[62], buf[63], buf[64], buf[65]);
+
+ memcpy(model, &buf[46], 16);
+ model[16] = '\0';
+ DBG(1, " model name : %s\n", model);
+
+ DBG(1, "options:\n");
+
+ if (le32atoh(&buf[28]) > 0)
+ DBG(1, " ADF detected\n");
+
+ if (le32atoh(&buf[36]) > 0)
+ DBG(1, " TPU detected\n");
+
+ if (buf[44])
+ DBG(1, "capabilities (1):\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_DLF)
+ DBG(1, " main lamp change is supported\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_NOTFBF)
+ DBG(1, " the device is NOT flatbed\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_ADFT)
+ DBG(1, " page type ADF is installed\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_ADFS)
+ DBG(1, " ADF is duplex capable\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_ADFO)
+ DBG(1, " page type ADF loads from the first sheet\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_LID)
+ DBG(1, " lid type option is installed\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_TPIR)
+ DBG(1, " infrared scanning is supported\n");
+
+ if (buf[44] & EXT_IDTY_CAP1_PB)
+ DBG(1, " push button is supported\n");
+
+
+ if (buf[45])
+ DBG(1, "capabilities (2):\n");
+
+ if (buf[45] & EXT_IDTY_CAP2_AFF)
+ DBG(1, " ADF has auto form feed\n");
+
+ if (buf[45] & EXT_IDTY_CAP2_DFD)
+ DBG(1, " ADF has double feed detection\n");
+
+ if (buf[45] & EXT_IDTY_CAP2_ADFAS)
+ DBG(1, " ADF has auto scan\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* FS F, request scanner status */
+SANE_Status
+esci_request_scanner_status(SANE_Handle handle, unsigned char *buf)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (!s->hw->extended_commands)
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (buf == NULL)
+ return SANE_STATUS_INVAL;
+
+ params[0] = FS;
+ params[1] = 'F';
+
+ status = e2_txrx(s, params, 2, buf, 16);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "global status : 0x%02x\n", buf[0]);
+
+ if (buf[0] & FSF_STATUS_MAIN_FER)
+ DBG(1, " system error\n");
+
+ if (buf[0] & FSF_STATUS_MAIN_NR)
+ DBG(1, " not ready\n");
+
+ if (buf[0] & FSF_STATUS_MAIN_WU)
+ DBG(1, " scanner is warming up\n");
+
+ if (buf[0] & FSF_STATUS_MAIN_CWU)
+ DBG(1, " warmup can be cancelled\n");
+
+
+ DBG(1, "adf status : 0x%02x\n", buf[1]);
+
+ if (buf[1] & FSF_STATUS_ADF_IST)
+ DBG(11, " installed\n");
+ else
+ DBG(11, " not installed\n");
+
+ if (buf[1] & FSF_STATUS_ADF_EN)
+ DBG(11, " enabled\n");
+ else
+ DBG(11, " not enabled\n");
+
+ if (buf[1] & FSF_STATUS_ADF_ERR)
+ DBG(1, " error\n");
+
+ if (buf[1] & FSF_STATUS_ADF_PE)
+ DBG(1, " paper empty\n");
+
+ if (buf[1] & FSF_STATUS_ADF_PJ)
+ DBG(1, " paper jam\n");
+
+ if (buf[1] & FSF_STATUS_ADF_OPN)
+ DBG(1, " cover open\n");
+
+ if (buf[1] & FSF_STATUS_ADF_PAG)
+ DBG(1, " duplex capable\n");
+
+
+ DBG(1, "tpu status : 0x%02x\n", buf[2]);
+
+ if (buf[2] & FSF_STATUS_TPU_IST)
+ DBG(11, " installed\n");
+ else
+ DBG(11, " not installed\n");
+
+ if (buf[2] & FSF_STATUS_TPU_EN)
+ DBG(11, " enabled\n");
+ else
+ DBG(11, " not enabled\n");
+
+ if (buf[2] & FSF_STATUS_TPU_ERR)
+ DBG(1, " error\n");
+
+ if (buf[1] & FSF_STATUS_TPU_OPN)
+ DBG(1, " cover open\n");
+
+
+ DBG(1, "device type : 0x%02x\n", buf[3] & 0xC0);
+ DBG(1, "main body status: 0x%02x\n", buf[3] & 0x3F);
+
+ if (buf[3] & FSF_STATUS_MAIN2_PE)
+ DBG(1, " paper empty\n");
+
+ if (buf[3] & FSF_STATUS_MAIN2_PJ)
+ DBG(1, " paper jam\n");
+
+ if (buf[3] & FSF_STATUS_MAIN2_OPN)
+ DBG(1, " cover open\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+esci_set_scanning_parameter(SANE_Handle handle, unsigned char *buf)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (buf == NULL)
+ return SANE_STATUS_INVAL;
+
+ params[0] = FS;
+ params[1] = 'W';
+
+ DBG(10, "resolution of main scan : %lu\n", (unsigned long) le32atoh(&buf[0]));
+ DBG(10, "resolution of sub scan : %lu\n", (unsigned long) le32atoh(&buf[4]));
+ DBG(10, "offset length of main scan : %lu\n", (unsigned long) le32atoh(&buf[8]));
+ DBG(10, "offset length of sub scan : %lu\n", (unsigned long) le32atoh(&buf[12]));
+ DBG(10, "scanning length of main scan: %lu\n", (unsigned long) le32atoh(&buf[16]));
+ DBG(10, "scanning length of sub scan : %lu\n", (unsigned long) le32atoh(&buf[20]));
+ DBG(10, "scanning color : %d\n", buf[24]);
+ DBG(10, "data format : %d\n", buf[25]);
+ DBG(10, "option control : %d\n", buf[26]);
+ DBG(10, "scanning mode : %d\n", buf[27]);
+ DBG(10, "block line number : %d\n", buf[28]);
+ DBG(10, "gamma correction : %d\n", buf[29]);
+ DBG(10, "brightness : %d\n", buf[30]);
+ DBG(10, "color correction : %d\n", buf[31]);
+ DBG(10, "halftone processing : %d\n", buf[32]);
+ DBG(10, "threshold : %d\n", buf[33]);
+ DBG(10, "auto area segmentation : %d\n", buf[34]);
+ DBG(10, "sharpness control : %d\n", buf[35]);
+ DBG(10, "mirroring : %d\n", buf[36]);
+ DBG(10, "film type : %d\n", buf[37]);
+ DBG(10, "main lamp lighting mode : %d\n", buf[38]);
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = e2_cmd_simple(s, buf, 64);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* FS S */
+
+SANE_Status
+esci_get_scanning_parameter(SANE_Handle handle, unsigned char *buf)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (buf == NULL)
+ return SANE_STATUS_INVAL;
+
+ params[0] = FS;
+ params[1] = 'S';
+
+ status = e2_txrx(s, params, 2, buf, 64);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(10, "resolution of main scan : %lu\n",
+ (u_long) le32atoh(&buf[0]));
+ DBG(10, "resolution of sub scan : %lu\n",
+ (u_long) le32atoh(&buf[4]));
+ DBG(10, "offset length of main scan : %lu\n",
+ (u_long) le32atoh(&buf[8]));
+ DBG(10, "offset length of sub scan : %lu\n",
+ (u_long) le32atoh(&buf[12]));
+ DBG(10, "scanning length of main scan: %lu\n",
+ (u_long) le32atoh(&buf[16]));
+ DBG(10, "scanning length of sub scan : %lu\n",
+ (u_long) le32atoh(&buf[20]));
+ DBG(10, "scanning color : %d\n", buf[24]);
+ DBG(10, "data format : %d\n", buf[25]);
+ DBG(10, "option control : %d\n", buf[26]);
+ DBG(10, "scanning mode : %d\n", buf[27]);
+ DBG(10, "block line number : %d\n", buf[28]);
+ DBG(10, "gamma correction : %d\n", buf[29]);
+ DBG(10, "brightness : %d\n", buf[30]);
+ DBG(10, "color correction : %d\n", buf[31]);
+ DBG(10, "halftone processing : %d\n", buf[32]);
+ DBG(10, "threshold : %d\n", buf[33]);
+ DBG(10, "auto area segmentation : %d\n", buf[34]);
+ DBG(10, "sharpness control : %d\n", buf[35]);
+ DBG(10, "mirroring : %d\n", buf[36]);
+ DBG(10, "film type : %d\n", buf[37]);
+ DBG(10, "main lamp lighting mode : %d\n", buf[38]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* ESC # */
+
+SANE_Status
+esci_enable_infrared(SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ int i;
+ unsigned char params[2];
+ unsigned char buf[64];
+
+ unsigned char seq[32] = {
+ 0xCA, 0xFB, 0x77, 0x71, 0x20, 0x16, 0xDA, 0x09,
+ 0x5F, 0x57, 0x09, 0x12, 0x04, 0x83, 0x76, 0x77,
+ 0x3C, 0x73, 0x9C, 0xBE, 0x7A, 0xE0, 0x52, 0xE2,
+ 0x90, 0x0D, 0xFF, 0x9A, 0xEF, 0x4C, 0x2C, 0x81
+ };
+
+ DBG(8, "%s\n", __func__);
+
+ status = esci_get_scanning_parameter(handle, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ for (i = 0; i < 32; i++) {
+ buf[i] = seq[i] ^ buf[i];
+ }
+
+ params[0] = ESC;
+ params[1] = '#';
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = e2_cmd_simple(s, buf, 32);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+esci_request_command_parameter(SANE_Handle handle, unsigned char *buf)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_condition == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_condition;
+
+ status = e2_cmd_info_block(s, params, 2, 45, &buf, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "scanning parameters:\n");
+ DBG(1, "color : %d\n", buf[1]);
+ DBG(1, "resolution : %dx%d\n",
+ buf[4] << 8 | buf[3], buf[6] << 8 | buf[5]);
+ DBG(1, "halftone : %d\n", buf[19]);
+ DBG(1, "brightness : %d\n", buf[21]);
+ DBG(1, "color correction : %d\n", buf[28]);
+ DBG(1, "gamma : %d\n", buf[23]);
+ DBG(1, "sharpness : %d\n", buf[30]);
+ DBG(1, "threshold : %d\n", buf[38]);
+ DBG(1, "data format : %d\n", buf[17]);
+ DBG(1, "mirroring : %d\n", buf[34]);
+ DBG(1, "option unit control : %d\n", buf[42]);
+ DBG(1, "film type : %d\n", buf[44]);
+ DBG(1, "auto area segmentation : %d\n", buf[36]);
+ DBG(1, "line counter : %d\n", buf[40]);
+ DBG(1, "scanning mode : %d\n", buf[32]);
+ DBG(1, "zoom : %d,%d\n", buf[26], buf[25]);
+ DBG(1, "scan area : %d,%d %d,%d\n",
+ buf[9] << 8 | buf[8], buf[11] << 8 | buf[10],
+ buf[13] << 8 | buf[12], buf[15] << 8 | buf[14]);
+ return status;
+}
+
+/* ESC q - Request Focus Position
+ * -> ESC q
+ * <- Information block
+ * <- Focus position status (2)
+ * 0 - Error status
+ * 1 - Focus position
+ */
+
+SANE_Status
+esci_request_focus_position(SANE_Handle handle, unsigned char *position)
+{
+ SANE_Status status;
+ unsigned char *buf;
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_focus_position == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_focus_position;
+
+ status = e2_cmd_info_block(s, params, 2, 2, &buf, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (buf[0] & 0x01)
+ DBG(1, "autofocus error\n");
+
+ *position = buf[1];
+ DBG(8, " focus position = 0x%x\n", buf[1]);
+
+ free(buf);
+
+ return status;
+}
+
+/* ESC ! - Request Push Button Status
+ * -> ESC !
+ * <- Information block
+ * <- Push button status (1)
+ */
+
+SANE_Status
+esci_request_push_button_status(SANE_Handle handle, unsigned char *bstatus)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[2];
+ unsigned char *buf;
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_push_button_status == 0) {
+ DBG(1, "push button status unsupported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_push_button_status;
+
+ status = e2_cmd_info_block(s, params, 2, 1, &buf, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "push button status = %d\n", buf[0]);
+ *bstatus = buf[0];
+
+ free(buf);
+
+ return status;
+}
+
+
+/*
+ * Request Identity information from scanner and fill in information
+ * into dev and/or scanner structures.
+ * XXX information should be parsed separately.
+ */
+SANE_Status
+esci_request_identity(SANE_Handle handle, unsigned char **buf, size_t *len)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (!s->hw->cmd->request_identity)
+ return SANE_STATUS_INVAL;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_identity;
+
+ return e2_cmd_info_block(s, params, 2, 0, buf, len);
+}
+
+
+/*
+ * Request information from scanner
+ */
+SANE_Status
+esci_request_identity2(SANE_Handle handle, unsigned char **buf)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ size_t len;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_identity2 == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_identity2;
+
+ status = e2_cmd_info_block(s, params, 2, 0, buf, &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return status;
+}
+
+/* Send the "initialize scanner" command to the device and reset it */
+
+SANE_Status
+esci_reset(Epson_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s\n", __func__);
+
+ if (!s->hw->cmd->initialize_scanner)
+ return SANE_STATUS_GOOD;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->initialize_scanner;
+
+ if (s->fd == -1)
+ return SANE_STATUS_GOOD;
+
+ status = e2_cmd_simple(s, params, 2);
+
+ return status;
+}
+
+SANE_Status
+esci_feed(Epson_Scanner * s)
+{
+ unsigned char params[1];
+
+ DBG(8, "%s\n", __func__);
+
+ if (!s->hw->cmd->feed)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = s->hw->cmd->feed;
+
+ return e2_cmd_simple(s, params, 1);
+}
+
+
+/*
+ * Eject the current page from the ADF. The scanner is opened prior to
+ * sending the command and closed afterwards.
+ */
+
+SANE_Status
+esci_eject(Epson_Scanner * s)
+{
+ unsigned char params[1];
+
+ DBG(8, "%s\n", __func__);
+
+ if (!s->hw->cmd->eject)
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (s->fd == -1)
+ return SANE_STATUS_GOOD;
+
+ params[0] = s->hw->cmd->eject;
+
+ return e2_cmd_simple(s, params, 1);
+}
+
+SANE_Status
+esci_request_extended_status(SANE_Handle handle, unsigned char **data,
+ size_t * data_len)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char params[2];
+ unsigned char *buf;
+ size_t buf_len;
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_extended_status == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_extended_status;
+
+ /* This command returns 33 bytes of data on old scanners
+ * and 42 (CMD_SIZE_EXT_STATUS) on new ones.
+ */
+ status = e2_cmd_info_block(s, params, 2, CMD_SIZE_EXT_STATUS,
+ &buf, &buf_len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (buf_len) {
+ case 33:
+ case 42:
+ break;
+ default:
+ DBG(1, "%s: unknown reply length (%lu)\n", __func__,
+ (unsigned long) buf_len);
+ break;
+ }
+
+ DBG(4, "main = %02x, ADF = %02x, TPU = %02x, main 2 = %02x\n",
+ buf[0], buf[1], buf[6], buf[11]);
+
+ if (buf[0] & EXT_STATUS_FER)
+ DBG(1, "system error\n");
+
+ if (buf[0] & EXT_STATUS_WU)
+ DBG(1, "scanner is warming up\n");
+
+ if (buf[1] & EXT_STATUS_ERR)
+ DBG(1, "ADF: other error\n");
+
+ if (buf[1] & EXT_STATUS_PE)
+ DBG(1, "ADF: no paper\n");
+
+ if (buf[1] & EXT_STATUS_PJ)
+ DBG(1, "ADF: paper jam\n");
+
+ if (buf[1] & EXT_STATUS_OPN)
+ DBG(1, "ADF: cover open\n");
+
+ if (buf[6] & EXT_STATUS_ERR)
+ DBG(1, "TPU: other error\n");
+
+ /* give back a pointer to the payload
+ * if the user requested it, otherwise
+ * free it.
+ */
+
+ if (data)
+ *data = buf;
+ else
+ free(buf);
+
+ if (data_len)
+ *data_len = buf_len;
+
+ return status;
+}
diff --git a/backend/epson2-commands.h b/backend/epson2-commands.h
new file mode 100644
index 0000000..4be065a
--- /dev/null
+++ b/backend/epson2-commands.h
@@ -0,0 +1,63 @@
+/*
+ * Prototypes for Epson ESC/I commands
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+/* simple scanner commands, ESC <x> */
+
+#define esci_set_focus_position(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_focus_position, v)
+#define esci_set_color_mode(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_color_mode, v)
+#define esci_set_data_format(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_data_format, v)
+#define esci_set_halftoning(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_halftoning, v)
+#define esci_set_gamma(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_gamma, v)
+#define esci_set_color_correction(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_color_correction, v)
+#define esci_set_lcount(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_lcount, v)
+#define esci_set_bright(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_bright, v)
+#define esci_mirror_image(s,v) e2_esc_cmd( s,(s)->hw->cmd->mirror_image, v)
+#define esci_set_speed(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_speed, v)
+#define esci_set_sharpness(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_outline_emphasis, v)
+#define esci_set_auto_area_segmentation(s,v) e2_esc_cmd( s,(s)->hw->cmd->control_auto_area_segmentation, v)
+#define esci_set_film_type(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_film_type, v)
+#define esci_set_exposure_time(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_exposure_time, v)
+#define esci_set_bay(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_bay, v)
+#define esci_set_threshold(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_threshold, v)
+#define esci_control_extension(s,v) e2_esc_cmd( s,(s)->hw->cmd->control_an_extension, v)
+
+SANE_Status esci_set_zoom(Epson_Scanner * s, unsigned char x, unsigned char y);
+SANE_Status esci_set_resolution(Epson_Scanner * s, int x, int y);
+SANE_Status esci_set_scan_area(Epson_Scanner * s, int x, int y, int width,
+ int height);
+SANE_Status esci_set_color_correction_coefficients(Epson_Scanner * s, SANE_Word *table);
+SANE_Status esci_set_gamma_table(Epson_Scanner * s);
+
+SANE_Status esci_request_status(SANE_Handle handle, unsigned char *scanner_status);
+SANE_Status esci_request_extended_identity(SANE_Handle handle, unsigned char *buf);
+SANE_Status esci_request_scanner_status(SANE_Handle handle, unsigned char *buf);
+SANE_Status esci_set_scanning_parameter(SANE_Handle handle, unsigned char *buf);
+SANE_Status esci_get_scanning_parameter(SANE_Handle handle, unsigned char *buf);
+SANE_Status esci_request_command_parameter(SANE_Handle handle, unsigned char *buf);
+SANE_Status esci_request_focus_position(SANE_Handle handle,
+ unsigned char *position);
+SANE_Status esci_request_push_button_status(SANE_Handle handle,
+ unsigned char *bstatus);
+SANE_Status esci_request_identity(SANE_Handle handle, unsigned char **buf, size_t *len);
+
+SANE_Status esci_request_identity2(SANE_Handle handle, unsigned char **buf);
+SANE_Status esci_reset(Epson_Scanner * s);
+SANE_Status esci_feed(Epson_Scanner * s);
+SANE_Status esci_eject(Epson_Scanner * s);
+SANE_Status esci_request_extended_status(SANE_Handle handle, unsigned char **data,
+ size_t * data_len);
+SANE_Status esci_enable_infrared(SANE_Handle handle);
diff --git a/backend/epson2-io.c b/backend/epson2-io.c
new file mode 100644
index 0000000..13adef4
--- /dev/null
+++ b/backend/epson2-io.c
@@ -0,0 +1,392 @@
+/*
+ * I/O routines for Epson scanners
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sane/config.h"
+
+#include <ctype.h>
+
+#include "epson2.h"
+#include "epson2-io.h"
+
+#include "sane/sanei_scsi.h"
+#include "sane/sanei_usb.h"
+#include "sane/sanei_pio.h"
+#include "sane/sanei_tcp.h"
+
+#include "epson2_scsi.h"
+#include "epson_usb.h"
+#include "epson2_net.h"
+
+#include "byteorder.h"
+
+/* flaming hack to get USB scanners
+ * working without timeouts under linux
+ * (cribbed from fujitsu.c)
+ */
+unsigned int r_cmd_count = 0;
+unsigned int w_cmd_count = 0;
+
+int
+e2_send(Epson_Scanner * s, void *buf, size_t buf_size, size_t reply_len,
+ SANE_Status * status)
+{
+ DBG(15, "%s: size = %lu, reply = %lu\n",
+ __func__, (u_long) buf_size, (u_long) reply_len);
+
+ if (buf_size == 2) {
+ char *cmd = buf;
+
+ switch (cmd[0]) {
+ case ESC:
+ DBG(9, "%s: ESC %c\n", __func__, cmd[1]);
+ break;
+
+ case FS:
+ DBG(9, "%s: FS %c\n", __func__, cmd[1]);
+ break;
+ }
+ }
+
+ if (DBG_LEVEL >= 125) {
+ unsigned int k;
+ const unsigned char *s = buf;
+
+ for (k = 0; k < buf_size; k++) {
+ DBG(125, "buf[%d] %02x %c\n", k, s[k],
+ isprint(s[k]) ? s[k] : '.');
+ }
+ }
+
+ if (s->hw->connection == SANE_EPSON_NET) {
+ if (reply_len == 0) {
+ DBG(0,
+ "Cannot send this command to a networked scanner\n");
+ return SANE_STATUS_INVAL;
+ }
+ return sanei_epson_net_write(s, 0x2000, buf, buf_size,
+ reply_len, status);
+ } else if (s->hw->connection == SANE_EPSON_SCSI) {
+ return sanei_epson2_scsi_write(s->fd, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_EPSON_PIO) {
+ size_t n;
+
+ if (buf_size == (n = sanei_pio_write(s->fd, buf, buf_size)))
+ *status = SANE_STATUS_GOOD;
+ else
+ *status = SANE_STATUS_INVAL;
+
+ return n;
+
+ } else if (s->hw->connection == SANE_EPSON_USB) {
+ size_t n;
+ n = buf_size;
+ *status = sanei_usb_write_bulk(s->fd, buf, &n);
+ w_cmd_count++;
+ DBG(20, "%s: cmd count, r = %d, w = %d\n",
+ __func__, r_cmd_count, w_cmd_count);
+
+ return n;
+ }
+
+ *status = SANE_STATUS_INVAL;
+ return 0;
+ /* never reached */
+}
+
+ssize_t
+e2_recv(Epson_Scanner * s, void *buf, ssize_t buf_size,
+ SANE_Status * status)
+{
+ ssize_t n = 0;
+
+ DBG(15, "%s: size = %ld, buf = %p\n", __func__, (long) buf_size, buf);
+
+ if (s->hw->connection == SANE_EPSON_NET) {
+ n = sanei_epson_net_read(s, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_EPSON_SCSI) {
+ n = sanei_epson2_scsi_read(s->fd, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_EPSON_PIO) {
+ if (buf_size ==
+ (n = sanei_pio_read(s->fd, buf, (size_t) buf_size)))
+ *status = SANE_STATUS_GOOD;
+ else
+ *status = SANE_STATUS_INVAL;
+ } else if (s->hw->connection == SANE_EPSON_USB) {
+ /* !!! only report an error if we don't read anything */
+ n = buf_size; /* buf_size gets overwritten */
+ *status =
+ sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf,
+ (size_t *) & n);
+ r_cmd_count += (n + 63) / 64; /* add # of packets, rounding up */
+ DBG(20, "%s: cmd count, r = %d, w = %d\n",
+ __func__, r_cmd_count, w_cmd_count);
+
+ if (n > 0)
+ *status = SANE_STATUS_GOOD;
+ }
+
+ if (n < buf_size) {
+ DBG(1, "%s: expected = %lu, got = %ld\n", __func__,
+ (u_long) buf_size, (long) n);
+ *status = SANE_STATUS_IO_ERROR;
+ }
+
+ /* dump buffer if appropriate */
+ if (DBG_LEVEL >= 127 && n > 0) {
+ int k;
+ const unsigned char *s = buf;
+
+ for (k = 0; k < n; k++)
+ DBG(127, "buf[%d] %02x %c\n", k, s[k],
+ isprint(s[k]) ? s[k] : '.');
+ }
+
+ return n;
+}
+
+/* Simple function to exchange a fixed amount of
+ * data with the scanner
+ */
+
+SANE_Status
+e2_txrx(Epson_Scanner * s, unsigned char *txbuf, size_t txlen,
+ unsigned char *rxbuf, size_t rxlen)
+{
+ SANE_Status status;
+
+ e2_send(s, txbuf, txlen, rxlen, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ e2_recv(s, rxbuf, rxlen, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status));
+ }
+
+ return status;
+}
+
+/* This function should be used to send codes that only requires the scanner
+ * to give back an ACK or a NAK.
+ */
+SANE_Status
+e2_cmd_simple(Epson_Scanner * s, void *buf, size_t buf_size)
+{
+ unsigned char result;
+ SANE_Status status;
+
+ DBG(12, "%s: size = %lu\n", __func__, (u_long) buf_size);
+
+ status = e2_txrx(s, buf, buf_size, &result, 1);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ if (result == ACK)
+ return SANE_STATUS_GOOD;
+
+ if (result == NAK) {
+ DBG(3, "%s: NAK\n", __func__);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(1, "%s: result is neither ACK nor NAK but 0x%02x\n", __func__,
+ result);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* receives a 4 or 6 bytes information block from the scanner*/
+SANE_Status
+e2_recv_info_block(Epson_Scanner * s, unsigned char *scanner_status,
+ size_t info_size, size_t * payload_size)
+{
+ SANE_Status status;
+ unsigned char info[6];
+
+ if (s->hw->connection == SANE_EPSON_PIO)
+ e2_recv(s, info, 1, &status);
+ else
+ e2_recv(s, info, info_size, &status);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* check for explicit NAK */
+ if (info[0] == NAK) {
+ DBG(1, "%s: command not supported\n", __func__);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* check the first byte: if it's not STX, bail out */
+ if (info[0] != STX) {
+ DBG(1, "%s: expecting STX, got %02X\n", __func__, info[0]);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* if connection is PIO read the remaining bytes. */
+ if (s->hw->connection == SANE_EPSON_PIO) {
+ e2_recv(s, &info[1], info_size - 1, &status);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (scanner_status)
+ *scanner_status = info[1];
+
+ if (payload_size) {
+ *payload_size = le16atoh(&info[2]);
+
+ if (info_size == 6)
+ *payload_size *= le16atoh(&info[4]);
+
+ DBG(14, "%s: payload length: %lu\n", __func__,
+ (u_long) *payload_size);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* This function can be called for commands that
+ * will be answered by the scanner with an info block of 4 bytes
+ * and a variable payload. The payload is passed back to the caller
+ * in **buf. The caller must free it if != NULL,
+ * even if the status != SANE_STATUS_GOOD.
+ */
+
+SANE_Status
+e2_cmd_info_block(SANE_Handle handle, unsigned char *params,
+ unsigned char params_len, size_t reply_len,
+ unsigned char **buf, size_t * buf_len)
+{
+ SANE_Status status;
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ size_t len;
+
+ DBG(13, "%s, params len = %d, reply len = %lu, buf = %p\n",
+ __func__, params_len, (u_long) reply_len, (void *) buf);
+
+ if (buf == NULL)
+ return SANE_STATUS_INVAL;
+
+ /* initialize */
+ *buf = NULL;
+
+ /* send command, we expect the info block + reply_len back */
+ e2_send(s, params, params_len,
+ reply_len ? reply_len + 4 : 0, &status);
+
+ if (status != SANE_STATUS_GOOD)
+ goto end;
+
+ status = e2_recv_info_block(s, NULL, 4, &len);
+ if (status != SANE_STATUS_GOOD)
+ goto end;
+
+ /* do we need to provide the length of the payload? */
+ if (buf_len)
+ *buf_len = len;
+
+ /* no payload, stop here */
+ if (len == 0)
+ goto end;
+
+ /* if a reply_len has been specified and the actual
+ * length differs, throw a warning
+ */
+ if (reply_len && (len != reply_len)) {
+ DBG(1, "%s: mismatched len - expected %lu, got %lu\n",
+ __func__, (u_long) reply_len, (u_long) len);
+ }
+
+ /* allocate and receive the payload */
+ *buf = malloc(len);
+
+ if (*buf) {
+ memset(*buf, 0x00, len);
+ e2_recv(s, *buf, len, &status); /* receive actual data */
+ } else
+ status = SANE_STATUS_NO_MEM;
+ end:
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status));
+
+ if (*buf) {
+ free(*buf);
+ *buf = NULL;
+ }
+ }
+
+ return status;
+}
+
+
+/* This is used for ESC commands with a single byte parameter. Scanner
+ * will answer with ACK/NAK.
+ */
+SANE_Status
+e2_esc_cmd(Epson_Scanner * s, unsigned char cmd, unsigned char val)
+{
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s: cmd = 0x%02x, val = %d\n", __func__, cmd, val);
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = val;
+
+ return e2_cmd_simple(s, params, 1);
+}
+
+/* Send an ACK to the scanner */
+
+SANE_Status
+e2_ack(Epson_Scanner * s)
+{
+ SANE_Status status;
+ e2_send(s, S_ACK, 1, 0, &status);
+ return status;
+}
+
+SANE_Status
+e2_ack_next(Epson_Scanner * s, size_t reply_len)
+{
+ SANE_Status status;
+ e2_send(s, S_ACK, 1, reply_len, &status);
+ return status;
+}
+
+SANE_Status
+e2_cancel(Epson_Scanner * s)
+{
+ DBG(1, "%s\n", __func__);
+ return e2_cmd_simple(s, S_CAN, 1);
+}
diff --git a/backend/epson2-io.h b/backend/epson2-io.h
new file mode 100644
index 0000000..9d29cdb
--- /dev/null
+++ b/backend/epson2-io.h
@@ -0,0 +1,50 @@
+/*
+ * Prototypes for epson2 I/O functions
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#ifndef epson2_io_h
+#define epson2_io_h
+
+extern unsigned int r_cmd_count;
+extern unsigned int w_cmd_count;
+
+
+SANE_Status e2_cmd_simple(Epson_Scanner * s, void *buf, size_t buf_size);
+int e2_send(Epson_Scanner * s, void *buf, size_t buf_size,
+ size_t reply_len, SANE_Status * status);
+ssize_t e2_recv(Epson_Scanner * s, void *buf, ssize_t buf_size,
+ SANE_Status * status);
+
+SANE_Status
+e2_txrx(Epson_Scanner * s, unsigned char *txbuf, size_t txlen,
+ unsigned char *rxbuf, size_t rxlen);
+
+SANE_Status
+e2_recv_info_block(Epson_Scanner * s, unsigned char *scanner_status,
+ size_t info_size, size_t * payload_size);
+
+SANE_Status
+e2_cmd_info_block(SANE_Handle handle, unsigned char *params,
+ unsigned char params_len, size_t reply_len,
+ unsigned char **buf, size_t * buf_len);
+
+SANE_Status e2_ack(Epson_Scanner * s);
+SANE_Status e2_ack_next(Epson_Scanner * s, size_t reply_len);
+SANE_Status e2_cancel(Epson_Scanner * s);
+
+SANE_Status
+e2_esc_cmd(Epson_Scanner * s, unsigned char cmd, unsigned char val);
+#endif /* epson2_io_h */
diff --git a/backend/epson2-ops.c b/backend/epson2-ops.c
new file mode 100644
index 0000000..df6958c
--- /dev/null
+++ b/backend/epson2-ops.c
@@ -0,0 +1,2211 @@
+/*
+ * epson2.c - SANE library for Epson scanners.
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for additional copyrights.
+ *
+ * Copyright (C) 2006-09 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sane/config.h"
+
+#include <unistd.h> /* sleep */
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include "byteorder.h"
+
+#include "epson2.h"
+#include "epson2-ops.h"
+
+#include "epson2-io.h"
+#include "epson2-commands.h"
+
+/*
+ * request identity
+ * | request identity2
+ * | | request status
+ * | | | request condition
+ * | | | | set color mode
+ * | | | | | start scanning
+ * | | | | | | set data format
+ * | | | | | | | set resolution
+ * | | | | | | | | set zoom
+ * | | | | | | | | | set scan area
+ * | | | | | | | | | | set brightness
+ * | | | | | | | | | | | set gamma
+ * | | | | | | | | | | | | set halftoning
+ * | | | | | | | | | | | | | set color correction
+ * | | | | | | | | | | | | | | initialize scanner
+ * | | | | | | | | | | | | | | | set speed
+ * | | | | | | | | | | | | | | | | set lcount
+ * | | | | | | | | | | | | | | | | | mirror image
+ * | | | | | | | | | | | | | | | | | | set gamma table
+ * | | | | | | | | | | | | | | | | | | | set outline emphasis
+ * | | | | | | | | | | | | | | | | | | | | set dither
+ * | | | | | | | | | | | | | | | | | | | | | set color correction coefficients
+ * | | | | | | | | | | | | | | | | | | | | | | request extension status
+ * | | | | | | | | | | | | | | | | | | | | | | | control an extension
+ * | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject
+ * | | | | | | | | | | | | | | | | | | | | | | | | | feed
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request extended identity
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request scanner status
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ */
+
+static struct EpsonCmd epson_cmd[] = {
+ {"A1",'I', 0 ,'F','S', 0 ,'G', 0 ,'R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"A2",'I', 0 ,'F','S', 0 ,'G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B1",'I', 0 ,'F','S','C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 ,'B', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B2",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B3",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@', 0 , 0 , 0 , 0 , 0 , 0 ,'m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B4",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d', 0 ,'z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B6",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B7",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-4, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0 ,'!','s','N', 0 , 0 ,'t', 0 , 0 ,'I','F'},
+ {"B8",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-4, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0x19,'!','s','N', 0 , 0 ,'t','p','q','I','F'},
+/* XXX 'f' probably not supported on F5 */
+ {"F5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z', 0 ,'M','@','g','d','K','z','Q', 0 ,'m','f','e','\f', 0 , 0 , 0 ,'N','T','P', 0 , 0 , 0 , 0 , 0 },
+ {"D1",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f', 0 , 0 , 0 ,'!', 0 , 0 , 0 , 0 ,'t', 0 , 0 , 0 , 0 },
+ {"D2",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e', 0 , 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 , 0 , 0 },
+ {"D7",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 , 0 , 0 },
+ {"D8",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 , 0 , 0 },
+};
+
+
+
+extern struct mode_param mode_params[];
+
+/* Define the different scan sources */
+
+#define FBF_STR SANE_I18N("Flatbed")
+#define TPU_STR SANE_I18N("Transparency Unit")
+#define TP2_STR SANE_I18N("TPU8x10")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+/*
+ * source list need one dummy entry (save device settings is crashing).
+ * NOTE: no const - this list gets created while exploring the capabilities
+ * of the scanner.
+ */
+
+extern SANE_String_Const source_list[];
+
+static int film_params[] = { 0, 1, 2, 3 };
+
+extern const int halftone_params[];
+
+static const int dropout_params[] = {
+ 0x00, /* none */
+ 0x10, /* red */
+ 0x20, /* green */
+ 0x30 /* blue */
+};
+
+/*
+ * Color correction:
+ * One array for the actual parameters that get sent to the scanner (color_params[]),
+ * one array for the strings that get displayed in the user interface (correction_list[])
+ * and one array to mark the user defined color correction (correction_userdefined[]).
+ */
+static const int correction_params[] = {
+ 0x00, /* None */
+ 0x01, /* Auto */
+ 0x01, /* User defined */
+};
+
+void
+e2_dev_init(Epson_Device *dev, const char *devname, int conntype)
+{
+ DBG(5, "%s\n", __func__);
+
+ dev->name = NULL;
+ dev->model = NULL;
+ dev->connection = conntype;
+
+ dev->model_id = 0;
+
+ dev->sane.name = devname;
+ dev->sane.model = NULL;
+
+ dev->sane.type = "flatbed scanner";
+ dev->sane.vendor = "Epson";
+
+ dev->optical_res = 0; /* just to have it initialized */
+ dev->color_shuffle = SANE_FALSE;
+ dev->extension = SANE_FALSE;
+ dev->use_extension = SANE_FALSE;
+
+ dev->need_color_reorder = SANE_FALSE;
+ dev->need_double_vertical = SANE_FALSE;
+
+ dev->cct_profile = &epson_cct_profiles[0]; /* default profile */
+
+ dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT];
+
+ /* Change default level when using a network connection */
+ if (dev->connection == SANE_EPSON_NET)
+ dev->cmd = &epson_cmd[EPSON_LEVEL_B7];
+
+ dev->last_res = 0;
+ dev->last_res_preview = 0; /* set resolution to safe values */
+
+ dev->res_list_size = 0;
+ dev->res_list = NULL;
+}
+
+SANE_Status
+e2_dev_post_init(struct Epson_Device *dev)
+{
+ int i;
+
+ DBG(5, "%s\n", __func__);
+
+ /* find cct model id */
+ for (i = 0; epson_cct_models[i].name != NULL; i++) {
+ if (strcmp(epson_cct_models[i].name, dev->model) == 0) {
+ dev->model_id = epson_cct_models[i].id;
+ break;
+ }
+ }
+
+ /* find cct profile */
+ for (i = 0; epson_cct_profiles[i].model != 0xFF; i++) {
+ if (epson_cct_profiles[i].model == dev->model_id) {
+ dev->cct_profile = &epson_cct_profiles[i];
+ break;
+ }
+ }
+
+ DBG(1, "CCT model id is 0x%02x, profile offset %d\n", dev->model_id, i);
+
+ /* If we have been unable to obtain supported resolutions
+ * due to the fact we are on the network transport,
+ * add some convenient ones
+ */
+
+ if (dev->res_list_size == 0) {
+
+ int val = (dev->dpi_range.min < 150) ? 150 : dev->dpi_range.min;
+
+ DBG(1, "cannot obtain resolution list, faking (%d-%d)\n",
+ dev->dpi_range.min, dev->dpi_range.max);
+
+ if (dev->dpi_range.min <= 25)
+ e2_add_resolution(dev, 25);
+
+ if (dev->dpi_range.min <= 50)
+ e2_add_resolution(dev, 50);
+
+ if (dev->dpi_range.min <= 75)
+ e2_add_resolution(dev, 75);
+
+ if (dev->dpi_range.min <= 100)
+ e2_add_resolution(dev, 100);
+
+ while (val <= dev->dpi_range.max) {
+ e2_add_resolution(dev, val);
+ val *= 2;
+ }
+ }
+
+ /* try to expand the resolution list where appropriate */
+
+ int last = dev->res_list[dev->res_list_size - 1];
+
+ DBG(1, "highest available resolution: %d\n", last);
+
+ if (dev->optical_res > last) {
+ DBG(1, "adding optical resolution (%d)\n", dev->optical_res);
+ e2_add_resolution(dev, dev->optical_res);
+ }
+
+ if (dev->dpi_range.max > last && dev->dpi_range.max != dev->optical_res) {
+
+ int val = last + last;
+
+ DBG(1, "integrating resolution list (%d-%d)\n",
+ val, dev->dpi_range.max);
+
+ while (val <= dev->dpi_range.max) {
+ e2_add_resolution(dev, val);
+ val += last;
+ }
+ }
+
+
+ /*
+ * Copy the resolution list to the resolution_list array so that the frontend can
+ * display the correct values
+ */
+
+ dev->resolution_list =
+ malloc((dev->res_list_size + 1) * sizeof(SANE_Word));
+
+ if (dev->resolution_list == NULL)
+ return SANE_STATUS_NO_MEM;
+
+
+ *(dev->resolution_list) = dev->res_list_size;
+
+ memcpy(&(dev->resolution_list[1]), dev->res_list,
+ dev->res_list_size * sizeof(SANE_Word));
+
+
+ /* establish defaults */
+ dev->need_reset_on_source_change = SANE_FALSE;
+
+ if (e2_dev_model(dev, "ES-9000H") || e2_dev_model(dev, "GT-30000")) {
+ dev->cmd->set_focus_position = 0;
+ dev->cmd->feed = 0x19;
+ }
+
+ if (e2_dev_model(dev, "GT-8200") || e2_dev_model(dev, "Perfection1650")
+ || e2_dev_model(dev, "Perfection1640") || e2_dev_model(dev, "GT-8700")) {
+ dev->cmd->feed = 0;
+ dev->cmd->set_focus_position = 0;
+ dev->need_reset_on_source_change = SANE_TRUE;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Bool
+e2_dev_model(Epson_Device *dev, const char *model)
+{
+ if (dev->model == NULL)
+ return SANE_FALSE;
+
+ if (strncmp(dev->model, model, strlen(model)) == 0)
+ return SANE_TRUE;
+
+ return SANE_FALSE;
+}
+
+void
+e2_set_cmd_level(SANE_Handle handle, unsigned char *level)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ Epson_Device *dev = s->hw;
+
+ int n;
+
+ DBG(1, "%s: %c%c\n", __func__, level[0], level[1]);
+
+ /* set command type and level */
+ for (n = 0; n < NELEMS(epson_cmd); n++) {
+ char type_level[3];
+ sprintf(type_level, "%c%c", level[0], level[1]);
+ if (!strncmp(type_level, epson_cmd[n].level, 2))
+ break;
+ }
+
+ if (n < NELEMS(epson_cmd)) {
+ dev->cmd = &epson_cmd[n];
+ } else {
+ dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT];
+ DBG(1, " unknown type %c or level %c, using %s\n",
+ level[0], level[1], dev->cmd->level);
+ }
+
+ s->hw->level = dev->cmd->level[1] - '0';
+}
+
+SANE_Status
+e2_set_model(Epson_Scanner * s, unsigned char *model, size_t len)
+{
+ unsigned char *buf;
+ unsigned char *p;
+ struct Epson_Device *dev = s->hw;
+
+ buf = malloc(len + 1);
+ if (buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy(buf, model, len);
+ buf[len] = '\0';
+
+ p = &buf[len - 1];
+
+ while (*p == ' ') {
+ *p = '\0';
+ p--;
+ }
+
+ if (dev->model)
+ free(dev->model);
+
+ dev->model = strndup((const char *) buf, len);
+ dev->sane.model = dev->model;
+
+ DBG(10, "%s: model is '%s'\n", __func__, dev->model);
+
+ free(buf);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+e2_add_resolution(Epson_Device *dev, int r)
+{
+ dev->res_list_size++;
+ dev->res_list = (SANE_Int *) realloc(dev->res_list,
+ dev->res_list_size *
+ sizeof(SANE_Word));
+
+ DBG(10, "%s: add (dpi): %d\n", __func__, r);
+
+ if (dev->res_list == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->res_list[dev->res_list_size - 1] = (SANE_Int) r;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+e2_set_fbf_area(Epson_Scanner * s, int x, int y, int unit)
+{
+ struct Epson_Device *dev = s->hw;
+
+ if (x == 0 || y == 0)
+ return;
+
+ dev->fbf_x_range.min = 0;
+ dev->fbf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->fbf_x_range.quant = 0;
+
+ dev->fbf_y_range.min = 0;
+ dev->fbf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->fbf_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->fbf_x_range.min),
+ SANE_UNFIX(dev->fbf_y_range.min),
+ SANE_UNFIX(dev->fbf_x_range.max),
+ SANE_UNFIX(dev->fbf_y_range.max), unit);
+}
+
+void
+e2_set_adf_area(struct Epson_Scanner *s, int x, int y, int unit)
+{
+ struct Epson_Device *dev = s->hw;
+
+ dev->adf_x_range.min = 0;
+ dev->adf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->adf_x_range.quant = 0;
+
+ dev->adf_y_range.min = 0;
+ dev->adf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->adf_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->adf_x_range.min),
+ SANE_UNFIX(dev->adf_y_range.min),
+ SANE_UNFIX(dev->adf_x_range.max),
+ SANE_UNFIX(dev->adf_y_range.max), unit);
+}
+
+void
+e2_set_tpu_area(struct Epson_Scanner *s, int x, int y, int unit)
+{
+ struct Epson_Device *dev = s->hw;
+
+ dev->tpu_x_range.min = 0;
+ dev->tpu_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->tpu_x_range.quant = 0;
+
+ dev->tpu_y_range.min = 0;
+ dev->tpu_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->tpu_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->tpu_x_range.min),
+ SANE_UNFIX(dev->tpu_y_range.min),
+ SANE_UNFIX(dev->tpu_x_range.max),
+ SANE_UNFIX(dev->tpu_y_range.max), unit);
+}
+
+void
+e2_set_tpu2_area(struct Epson_Scanner *s, int x, int y, int unit)
+{
+ struct Epson_Device *dev = s->hw;
+
+ dev->tpu2_x_range.min = 0;
+ dev->tpu2_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->tpu2_x_range.quant = 0;
+
+ dev->tpu2_y_range.min = 0;
+ dev->tpu2_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->tpu2_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->tpu2_x_range.min),
+ SANE_UNFIX(dev->tpu2_y_range.min),
+ SANE_UNFIX(dev->tpu2_x_range.max),
+ SANE_UNFIX(dev->tpu2_y_range.max), unit);
+}
+
+void
+e2_add_depth(Epson_Device * dev, SANE_Word depth)
+{
+ if (depth > dev->maxDepth)
+ dev->maxDepth = depth;
+
+ dev->depth_list[0]++;
+ dev->depth_list[dev->depth_list[0]] = depth;
+}
+
+/* A little helper function to correct the extended status reply
+ * gotten from scanners with known buggy firmware.
+ */
+static void
+fix_up_extended_status_reply(Epson_Scanner * s, unsigned char *buf)
+{
+ if (e2_model(s, "ES-9000H") || e2_model(s, "GT-30000")) {
+ DBG(1, "fixing up buggy ADF max scan dimensions.\n");
+ buf[2] = 0xB0;
+ buf[3] = 0x6D;
+ buf[4] = 0x60;
+ buf[5] = 0x9F;
+ }
+}
+
+
+SANE_Status
+e2_discover_capabilities(Epson_Scanner *s)
+{
+ SANE_Status status;
+
+ unsigned char scanner_status;
+ Epson_Device *dev = s->hw;
+
+ SANE_String_Const *source_list_add = source_list;
+
+ DBG(5, "%s\n", __func__);
+
+ /* always add flatbed */
+ *source_list_add++ = FBF_STR;
+
+ /* ESC I, request identity
+ * this must be the first command on the FilmScan 200
+ */
+ if (dev->connection != SANE_EPSON_NET) {
+ unsigned int n, k, x = 0, y = 0;
+ unsigned char *buf, *area;
+ size_t len;
+
+ status = esci_request_identity(s, &buf, &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ e2_set_cmd_level(s, &buf[0]);
+
+ /* Setting available resolutions and xy ranges for sane frontend. */
+ /* cycle thru the resolutions, saving them in a list */
+ for (n = 2, k = 0; n < len; n += k) {
+
+ area = buf + n;
+
+ switch (*area) {
+ case 'R':
+ {
+ int val = area[2] << 8 | area[1];
+
+ status = e2_add_resolution(s->hw, val);
+ k = 3;
+ continue;
+ }
+ case 'A':
+ {
+ x = area[2] << 8 | area[1];
+ y = area[4] << 8 | area[3];
+
+ DBG(1, "maximum scan area: %dx%d\n", x, y);
+ k = 5;
+ continue;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* min and max dpi */
+ dev->dpi_range.min = dev->res_list[0];
+ dev->dpi_range.max = dev->res_list[dev->res_list_size - 1];
+ dev->dpi_range.quant = 0;
+
+ e2_set_fbf_area(s, x, y, dev->dpi_range.max);
+
+ free(buf);
+ }
+
+ /* ESC F, request status */
+ status = esci_request_status(s, &scanner_status);
+ if (status != SANE_STATUS_GOOD)
+ return status;;
+
+ /* set capabilities */
+ if (scanner_status & STATUS_OPTION)
+ dev->extension = SANE_TRUE;
+
+ if (scanner_status & STATUS_EXT_COMMANDS)
+ dev->extended_commands = 1;
+
+ /*
+ * Extended status flag request (ESC f).
+ * this also requests the scanner device name from the the scanner.
+ * It seems unsupported on the network transport (CX11NF/LP-A500).
+ */
+
+ if (dev->cmd->request_extended_status && dev->connection != SANE_EPSON_NET) {
+
+ unsigned char *es;
+ size_t es_len;
+
+ DBG(1, "detection with request_extended_status\n");
+
+ status = esci_request_extended_status(s, &es, &es_len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /*
+ * Get the device name and copy it to dev->sane.model.
+ * The device name starts at es[0x1A] and is up to 16 bytes long
+ * We are overwriting whatever was set previously!
+ */
+ if (es_len == CMD_SIZE_EXT_STATUS) /* 42 */
+ e2_set_model(s, es + 0x1A, 16);
+
+ if (es[0] & EXT_STATUS_LID)
+ DBG(1, "LID detected\n");
+
+ if (es[0] & EXT_STATUS_PB)
+ DBG(1, "push button detected\n");
+ else
+ dev->cmd->request_push_button_status = 0;
+
+ /* Flatbed */
+ e2_set_fbf_area(s, es[13] << 8 | es[12], es[15] << 8 | es[14],
+ dev->dpi_range.max);
+
+ /* ADF */
+ if (dev->extension && (es[1] & EXT_STATUS_IST)) {
+ DBG(1, "ADF detected\n");
+
+ fix_up_extended_status_reply(s, es);
+
+ dev->duplex = (es[0] & EXT_STATUS_ADFS) != 0;
+ if (dev->duplex)
+ DBG(1, "ADF supports duplex\n");
+
+ if (es[1] & EXT_STATUS_EN) {
+ DBG(1, "ADF is enabled\n");
+ dev->x_range = &dev->adf_x_range;
+ dev->y_range = &dev->adf_y_range;
+ }
+
+ e2_set_adf_area(s, es[3] << 8 | es[2],
+ es[5] << 8 | es[4],
+ dev->dpi_range.max);
+ *source_list_add++ = ADF_STR;
+
+ dev->ADF = SANE_TRUE;
+ }
+
+ /* TPU */
+ if (dev->extension && (es[6] & EXT_STATUS_IST)) {
+ DBG(1, "TPU detected\n");
+
+ if (es[6] & EXT_STATUS_EN) {
+ DBG(1, "TPU is enabled\n");
+ dev->x_range = &dev->tpu_x_range;
+ dev->y_range = &dev->tpu_y_range;
+ }
+
+ e2_set_tpu_area(s,
+ (es[8] << 8 | es[7]),
+ (es[10] << 8 | es[9]),
+ dev->dpi_range.max);
+
+ *source_list_add++ = TPU_STR;
+ dev->TPU = SANE_TRUE;
+ }
+
+ free(es);
+
+ *source_list_add = NULL; /* add end marker to source list */
+ }
+
+ /* FS I, request extended identity (B7/B8) */
+ if (dev->extended_commands && dev->cmd->request_extended_identity) {
+ unsigned char buf[80];
+
+ DBG(1, "detection with request_extended_identity\n");
+
+ status = esci_request_extended_identity(s, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ e2_set_cmd_level(s, &buf[0]);
+
+ dev->maxDepth = buf[67];
+
+ /* set model name. it will probably be
+ * different than the one reported by request_identity
+ * for the same unit (i.e. LP-A500 vs CX11) .
+ */
+ e2_set_model(s, &buf[46], 16);
+
+ dev->optical_res = le32atoh(&buf[4]);
+
+ dev->dpi_range.min = le32atoh(&buf[8]);
+ dev->dpi_range.max = le32atoh(&buf[12]);
+
+ /* Flatbed */
+ e2_set_fbf_area(s, le32atoh(&buf[20]),
+ le32atoh(&buf[24]), dev->optical_res);
+
+ /* ADF */
+ if (le32atoh(&buf[28]) > 0) {
+ e2_set_adf_area(s, le32atoh(&buf[28]),
+ le32atoh(&buf[32]), dev->optical_res);
+
+ if (!dev->ADF) {
+ *source_list_add++ = ADF_STR;
+ dev->ADF = SANE_TRUE;
+ }
+ }
+
+ /* TPU */
+ if (le32atoh(&buf[36]) > 0 && !dev->TPU) {
+ e2_set_tpu_area(s,
+ le32atoh(&buf[36]),
+ le32atoh(&buf[40]), dev->optical_res);
+
+ *source_list_add++ = TPU_STR;
+ dev->TPU = SANE_TRUE;
+ }
+
+ /* TPU2 */
+ if (e2_model(s, "GT-X800") || e2_model(s, "GT-X900")) {
+ if (le32atoh(&buf[68]) > 0 ) {
+ e2_set_tpu2_area(s,
+ le32atoh(&buf[68]),
+ le32atoh(&buf[72]),
+ dev->optical_res);
+
+ *source_list_add++ = TP2_STR;
+ }
+ }
+
+ *source_list_add = NULL; /* add end marker to source list */
+
+ } else {
+ DBG(1, "no command available to detect capabilities\n");
+ }
+
+ /*
+ * request identity 2 (ESC i), if available will
+ * get the information from the scanner and store it in dev
+ */
+
+ if (dev->cmd->request_identity2 && dev->connection != SANE_EPSON_NET) {
+ unsigned char *buf;
+ status = esci_request_identity2(s, &buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* the first two bytes of the buffer contain the optical resolution */
+ dev->optical_res = buf[1] << 8 | buf[0];
+
+ /*
+ * the 4th and 5th byte contain the line distance. Both values have to
+ * be identical, otherwise this software can not handle this scanner.
+ */
+ if (buf[4] != buf[5]) {
+ status = SANE_STATUS_INVAL;
+ return status;
+ }
+
+ dev->max_line_distance = buf[4];
+ }
+
+ /*
+ * Check for the max. supported color depth and assign
+ * the values to the bitDepthList.
+ */
+ dev->depth_list = malloc(sizeof(SANE_Word) * 4);
+ if (dev->depth_list == NULL) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->depth_list[0] = 0;
+
+ /* maximum depth discovery */
+ DBG(3, "discovering max depth, NAKs are expected\n");
+
+ if (dev->maxDepth >= 16 || dev->maxDepth == 0) {
+ if (esci_set_data_format(s, 16) == SANE_STATUS_GOOD)
+ e2_add_depth(dev, 16);
+ }
+
+ if (dev->maxDepth >= 14 || dev->maxDepth == 0) {
+ if (esci_set_data_format(s, 14) == SANE_STATUS_GOOD)
+ e2_add_depth(dev, 14);
+ }
+
+ if (dev->maxDepth >= 12 || dev->maxDepth == 0) {
+ if (esci_set_data_format(s, 12) == SANE_STATUS_GOOD)
+ e2_add_depth(dev, 12);
+ }
+
+ /* add default depth */
+ e2_add_depth(dev, 8);
+
+ DBG(1, "maximum supported color depth: %d\n", dev->maxDepth);
+
+ /*
+ * Check for "request focus position" command. If this command is
+ * supported, then the scanner does also support the "set focus
+ * position" command.
+ * XXX ???
+ */
+
+ if (esci_request_focus_position(s, &s->currentFocusPosition) ==
+ SANE_STATUS_GOOD) {
+ DBG(1, "setting focus is supported\n");
+ dev->focusSupport = SANE_TRUE;
+ s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
+
+ /* reflect the current focus position in the GUI */
+ if (s->currentFocusPosition < 0x4C) {
+ /* focus on glass */
+ s->val[OPT_FOCUS].w = 0;
+ } else {
+ /* focus 2.5mm above glass */
+ s->val[OPT_FOCUS].w = 1;
+ }
+
+ } else {
+ DBG(1, "setting focus is not supported\n");
+ dev->focusSupport = SANE_FALSE;
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_FOCUS].w = 0; /* on glass - just in case */
+ }
+
+ /* Set defaults for no extension. */
+ dev->x_range = &dev->fbf_x_range;
+ dev->y_range = &dev->fbf_y_range;
+
+ /*
+ * Correct for a firmware bug in some Perfection 1650 scanners:
+ * Firmware version 1.08 reports only half the vertical scan area, we have
+ * to double the number. To find out if we have to do this, we just compare
+ * is the vertical range is smaller than the horizontal range.
+ */
+
+ if ((dev->x_range->max - dev->x_range->min) >
+ (dev->y_range->max - dev->y_range->min)) {
+ DBG(1, "found buggy scan area, doubling it.\n");
+ dev->y_range->max += (dev->y_range->max - dev->y_range->min);
+ dev->need_double_vertical = SANE_TRUE;
+ dev->need_color_reorder = SANE_TRUE;
+ }
+
+ /* FS F, request scanner status */
+ if (dev->extended_commands) {
+ unsigned char buf[16];
+
+ status = esci_request_scanner_status(s, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return status;
+}
+
+
+SANE_Status
+e2_set_extended_scanning_parameters(Epson_Scanner * s)
+{
+ unsigned char buf[64];
+
+ const struct mode_param *mparam;
+
+ DBG(1, "%s\n", __func__);
+
+ mparam = &mode_params[s->val[OPT_MODE].w];
+
+ memset(buf, 0x00, sizeof(buf));
+
+ /* ESC R, resolution */
+ htole32a(&buf[0], s->val[OPT_RESOLUTION].w);
+ htole32a(&buf[4], s->val[OPT_RESOLUTION].w);
+
+ /* ESC A, scanning area */
+ htole32a(&buf[8], s->left);
+ htole32a(&buf[12], s->top);
+ htole32a(&buf[16], s->params.pixels_per_line);
+ htole32a(&buf[20], s->params.lines);
+
+ /*
+ * The byte sequence mode was introduced in B5,
+ *for B[34] we need line sequence mode
+ */
+
+ /* ESC C, set color */
+ if ((s->hw->cmd->level[0] == 'D'
+ || (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5))
+ && mparam->flags == 0x02) {
+ buf[24] = 0x13;
+ } else {
+ buf[24] = mparam->flags | (mparam->dropout_mask
+ & dropout_params[s->
+ val[OPT_DROPOUT].
+ w]);
+ }
+
+ /* ESC D, set data format */
+ mparam = &mode_params[s->val[OPT_MODE].w];
+ buf[25] = mparam->depth;
+
+ /* ESC e, control option */
+ if (s->hw->extension) {
+
+ char extensionCtrl;
+ extensionCtrl = (s->hw->use_extension ? 1 : 0);
+ if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1))
+ extensionCtrl = 2;
+
+ /* Test for TPU2
+ * Epson Perfection 4990 Command Specifications
+ * JZIS-0075 Rev. A, page 31
+ */
+ if (s->hw->use_extension && s->hw->TPU2)
+ extensionCtrl = 5;
+
+ if (s->val[OPT_MODE].w == MODE_INFRARED)
+ /* only infrared in TPU mode (NOT in TPU2 or flatbeth)
+ * XXX investigate this ... only tested on GT-X800
+ */
+
+ if (extensionCtrl == 1) /* test for TPU */
+ extensionCtrl = 3;
+ else
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* ESC e */
+ buf[26] = extensionCtrl;
+
+ /* XXX focus */
+ }
+
+ /* ESC g, scanning mode (normal or high speed) */
+ if (s->val[OPT_PREVIEW].w)
+ buf[27] = 1; /* High speed */
+ else
+ buf[27] = 0;
+
+ /* ESC d, block line number */
+ buf[28] = s->lcount;
+
+ /* ESC Z, set gamma correction */
+ buf[29] = 0x01; /* default */
+
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_GAMMA_CORRECTION].cap)) {
+ char val;
+ if (s->hw->cmd->level[0] == 'D') {
+ /* The D1 level has only the two user defined gamma
+ * settings.
+ */
+ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
+ } else {
+ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
+
+ /*
+ * If "Default" is selected then determine the actual value
+ * to send to the scanner: If bilevel mode, just send the
+ * value from the table (0x01), for grayscale or color mode
+ * add one and send 0x02.
+ */
+
+ if (s->val[OPT_GAMMA_CORRECTION].w == 0) {
+ val += mparam->depth == 1 ? 0 : 1;
+ }
+ }
+
+ buf[29] = val;
+ }
+
+ /* ESC L, set brightness */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap))
+ buf[30] = s->val[OPT_BRIGHTNESS].w;
+
+ /* ESC B, set halftoning mode / halftone processing */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_HALFTONE].cap))
+ buf[32] = halftone_params[s->val[OPT_HALFTONE].w];
+
+ /* ESC s, auto area segmentation */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_AAS].cap))
+ buf[34] = s->val[OPT_AAS].w;
+
+ /* ESC Q, set sharpness / sharpness control */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_SHARPNESS].cap))
+ buf[35] = s->val[OPT_SHARPNESS].w;
+
+ /* ESC K, set data order / mirroring */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_MIRROR].cap))
+ buf[36] = s->val[OPT_MIRROR].w;
+
+ /* ESC N, film type */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FILM_TYPE].cap))
+ buf[37] = film_params[s->val[OPT_FILM_TYPE].w];
+
+ /* ESC M, color correction */
+ buf[31] = correction_params[s->val[OPT_COLOR_CORRECTION].w];
+
+ /* ESC t, threshold */
+ buf[33] = s->val[OPT_THRESHOLD].w;
+
+ return esci_set_scanning_parameter(s, buf);
+}
+
+SANE_Status
+e2_set_scanning_parameters(Epson_Scanner * s)
+{
+ SANE_Status status;
+ struct mode_param *mparam = &mode_params[s->val[OPT_MODE].w];
+ unsigned char color_mode;
+
+ DBG(1, "%s\n", __func__);
+
+ /*
+ * There is some undocumented special behavior with the TPU enable/disable.
+ * TPU power ESC e status
+ * on 0 NAK
+ * on 1 ACK
+ * off 0 ACK
+ * off 1 NAK
+ *
+ * It makes no sense to scan with TPU powered on and source flatbed, because
+ * light will come from both sides.
+ */
+
+ if (s->hw->extension) {
+
+ int extensionCtrl;
+ extensionCtrl = (s->hw->use_extension ? 1 : 0);
+ if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1))
+ extensionCtrl = 2;
+
+ status = esci_control_extension(s, extensionCtrl);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "you may have to power %s your TPU\n",
+ s->hw->use_extension ? "on" : "off");
+ DBG(1,
+ "and you may also have to restart the SANE frontend.\n");
+ return status;
+ }
+
+ /* XXX use request_extended_status and analyze
+ * buffer to set the scan area for
+ * ES-9000H and GT-30000
+ */
+
+ /*
+ * set the focus position according to the extension used:
+ * if the TPU is selected, then focus 2.5mm above the glass,
+ * otherwise focus on the glass. Scanners that don't support
+ * this feature, will just ignore these calls.
+ */
+
+ if (s->hw->focusSupport == SANE_TRUE) {
+ if (s->val[OPT_FOCUS].w == 0) {
+ DBG(1, "setting focus to glass surface\n");
+ esci_set_focus_position(s, 0x40);
+ } else {
+ DBG(1,
+ "setting focus to 2.5mm above glass\n");
+ esci_set_focus_position(s, 0x59);
+ }
+ }
+ }
+
+ /* ESC C, Set color */
+ color_mode = mparam->flags | (mparam->dropout_mask
+ & dropout_params[s->val[OPT_DROPOUT].
+ w]);
+
+ /*
+ * The byte sequence mode was introduced in B5, for B[34] we need line sequence mode
+ * XXX Check what to do for the FilmScan 200
+ */
+ if ((s->hw->cmd->level[0] == 'D'
+ || (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5))
+ && mparam->flags == 0x02)
+ color_mode = 0x13;
+
+ status = esci_set_color_mode(s, color_mode);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* ESC D, set data format */
+ DBG(1, "%s: setting data format to %d bits\n", __func__,
+ mparam->depth);
+ status = esci_set_data_format(s, mparam->depth);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* ESC B, set halftoning mode */
+ if (s->hw->cmd->set_halftoning
+ && SANE_OPTION_IS_ACTIVE(s->opt[OPT_HALFTONE].cap)) {
+ status = esci_set_halftoning(s,
+ halftone_params[s->
+ val
+ [OPT_HALFTONE].
+ w]);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* ESC L, set brightness */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap)) {
+ status = esci_set_bright(s, s->val[OPT_BRIGHTNESS].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_AAS].cap)) {
+ status = esci_set_auto_area_segmentation(s,
+ s->val[OPT_AAS].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FILM_TYPE].cap)) {
+ status = esci_set_film_type(s,
+ film_params[s->val[OPT_FILM_TYPE].w]);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (s->hw->cmd->set_gamma
+ && SANE_OPTION_IS_ACTIVE(s->opt[OPT_GAMMA_CORRECTION].cap)) {
+ int val;
+ if (s->hw->cmd->level[0] == 'D') {
+ /*
+ * The D1 level has only the two user defined gamma
+ * settings.
+ */
+ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
+ } else {
+ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
+
+ /*
+ * If "Default" is selected then determine the actual value
+ * to send to the scanner: If bilevel mode, just send the
+ * value from the table (0x01), for grayscale or color mode
+ * add one and send 0x02.
+ */
+
+/* if( s->val[ OPT_GAMMA_CORRECTION].w <= 1) { */
+ if (s->val[OPT_GAMMA_CORRECTION].w == 0) {
+ val += mparam->depth == 1 ? 0 : 1;
+ }
+ }
+
+ status = esci_set_gamma(s, val);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (s->hw->cmd->set_threshold != 0
+ && SANE_OPTION_IS_ACTIVE(s->opt[OPT_THRESHOLD].cap)) {
+ status = esci_set_threshold(s, s->val[OPT_THRESHOLD].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* XXX ESC Z here */
+
+ /* ESC M, set color correction */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_COLOR_CORRECTION].cap)) {
+
+ status = esci_set_color_correction(s,
+ correction_params[s->val[OPT_COLOR_CORRECTION].w]);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* ESC Q, set sharpness */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_SHARPNESS].cap)) {
+
+ status = esci_set_sharpness(s, s->val[OPT_SHARPNESS].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* ESC g, set scanning mode */
+ if (s->val[OPT_PREVIEW].w)
+ status = esci_set_speed(s, 1);
+ else
+ status = esci_set_speed(s, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* ESC K, set data order */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_MIRROR].cap)) {
+ status = esci_mirror_image(s, s->val[OPT_MIRROR].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* ESC R */
+ status = esci_set_resolution(s, s->val[OPT_RESOLUTION].w,
+ s->val[OPT_RESOLUTION].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* ESC H, set zoom */
+ /* not implemented */
+
+ /* ESC A, set scanning area */
+
+ /*
+ * Modify the scan area: If the scanner requires color shuffling, then we try to
+ * scan more lines to compensate for the lines that will be removed from the scan
+ * due to the color shuffling algorithm.
+ */
+
+ if (s->hw->color_shuffle == SANE_TRUE) {
+
+ unsigned int lines = s->params.lines + (2 * s->line_distance);
+ int top = s->top - (1 * s->line_distance);
+
+ if (top < 0)
+ top = 0;
+
+ status = esci_set_scan_area(s, s->left, top,
+ s->params.pixels_per_line,
+ lines);
+
+ } else {
+
+ status = esci_set_scan_area(s, s->left, s->top,
+ s->params.pixels_per_line,
+ s->params.lines);
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* ESC d, set block line number / set line counter */
+ status = esci_set_lcount(s, s->lcount);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+e2_setup_block_mode(Epson_Scanner * s)
+{
+ int maxreq;
+
+ DBG(5, "%s\n", __func__);
+
+ s->block = SANE_TRUE;
+
+ if (s->hw->connection == SANE_EPSON_SCSI)
+ maxreq = sanei_scsi_max_request_size;
+ else if (s->hw->connection == SANE_EPSON_USB)
+ maxreq = 128 * 1024;
+ else
+ maxreq = 32 * 1024;
+
+ /* XXX verify if this can b extended to other models */
+ if (s->hw->connection == SANE_EPSON_NET && e2_model(s, "LP-A500"))
+ maxreq = 64 * 1024;
+
+ s->lcount = maxreq / s->params.bytes_per_line;
+
+ DBG(1, "max req size: %d, line count: %d\n", maxreq, s->lcount);
+
+ /* XXX investigate this */
+ if (s->lcount < 3 && (e2_model(s, "GT-X800") || e2_model(s, "GT-X900"))) {
+ s->lcount = 21;
+ DBG(17,
+ "%s: set lcount = %i bigger than sanei_scsi_max_request_size\n",
+ __func__, s->lcount);
+ }
+
+ if (s->lcount >= 255)
+ s->lcount = 255;
+
+ /* XXX why this? */
+ if (s->hw->TPU && s->hw->use_extension && s->lcount > 32)
+ s->lcount = 32;
+
+ /*
+ * The D1 series of scanners only allow an even line number
+ * for bi-level scanning. If a bit depth of 1 is selected, then
+ * make sure the next lower even number is selected.
+ */
+
+ /* XXX check bith depth? */
+ if (s->hw->cmd->level[0] == 'D' && s->lcount > 3 && s->lcount % 2)
+ s->lcount -= 1;
+
+ DBG(1, "final line count is %d\n", s->lcount);
+}
+
+SANE_Status
+e2_init_parameters(Epson_Scanner * s)
+{
+ int dpi, bytes_per_pixel;
+ struct mode_param *mparam;
+
+ DBG(5, "%s\n", __func__);
+
+ memset(&s->params, 0, sizeof(SANE_Parameters));
+
+ dpi = s->val[OPT_RESOLUTION].w;
+
+ mparam = &mode_params[s->val[OPT_MODE].w];
+
+ if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 ||
+ SANE_UNFIX(s->val[OPT_BR_X].w) == 0)
+ return SANE_STATUS_INVAL;
+
+ s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) *
+ s->val[OPT_RESOLUTION].w) + 0.5;
+
+ s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) *
+ s->val[OPT_RESOLUTION].w) + 0.5;
+
+ s->params.pixels_per_line =
+ ((SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH) * dpi) + 0.5;
+ s->params.lines =
+ ((SANE_UNFIX(s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH) * dpi) + 0.5;
+
+
+ DBG(1, "%s: resolution = %d, preview = %d\n",
+ __func__, s->val[OPT_RESOLUTION].w, s->val[OPT_PREVIEW].w);
+
+ DBG(1, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n",
+ __func__, (void *) s, (void *) s->val,
+ SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w),
+ SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w));
+
+ /*
+ * Calculate bytes_per_pixel and bytes_per_line for
+ * any color depths.
+ *
+ * The default color depth is stored in mode_params.depth:
+ */
+
+ if (mode_params[s->val[OPT_MODE].w].depth == 1)
+ s->params.depth = 1;
+ else
+ s->params.depth = s->val[OPT_BIT_DEPTH].w;
+
+ if (s->params.depth > 8) {
+ s->params.depth = 16; /*
+ * The frontends can only handle 8 or 16 bits
+ * for gray or color - so if it's more than 8,
+ * it gets automatically set to 16. This works
+ * as long as EPSON does not come out with a
+ * scanner that can handle more than 16 bits
+ * per color channel.
+ */
+ }
+
+ /* this works because it can only be set to 1, 8 or 16 */
+ bytes_per_pixel = s->params.depth / 8;
+ if (s->params.depth % 8) { /* just in case ... */
+ bytes_per_pixel++;
+ }
+
+ /* pixels_per_line is rounded to the next 8bit boundary */
+ s->params.pixels_per_line = s->params.pixels_per_line & ~7;
+
+ s->params.last_frame = SANE_TRUE;
+
+ switch (s->val[OPT_MODE].w) {
+ case MODE_BINARY:
+ case MODE_GRAY:
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line =
+ s->params.pixels_per_line * s->params.depth / 8;
+ break;
+ case MODE_COLOR:
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line =
+ 3 * s->params.pixels_per_line * bytes_per_pixel;
+ break;
+#ifdef SANE_FRAME_IR
+ case MODE_INFRARED:
+ s->params.format = SANE_FRAME_IR;
+ s->params.bytes_per_line =
+ s->params.pixels_per_line * s->params.depth / 8;
+ break;
+#endif
+ }
+
+ if (s->params.bytes_per_line == 0)
+ return SANE_STATUS_INVAL;
+
+ /*
+ * Calculate correction for line_distance in D1 scanner:
+ * Start line_distance lines earlier and add line_distance lines at the end
+ *
+ * Because the actual line_distance is not yet calculated we have to do this
+ * first.
+ */
+
+ s->hw->color_shuffle = SANE_FALSE;
+
+ s->lines_written = 0;
+ s->color_shuffle_line = 0;
+ s->current_output_line = 0;
+
+ if ((s->hw->optical_res != 0) && (mparam->depth == 8)
+ && (mparam->flags != 0)) {
+
+ s->line_distance =
+ s->hw->max_line_distance * dpi / s->hw->optical_res;
+
+ if (s->line_distance != 0) {
+
+ s->hw->color_shuffle = SANE_TRUE;
+
+ DBG(1, "%s: color shuffling required\n", __func__);
+ }
+ }
+
+ /*
+ * If (s->top + s->params.lines) is larger than the max scan area, reset
+ * the number of scan lines:
+ * XXX: precalculate the maximum scanning area elsewhere (use dev max_y)
+ */
+
+ if (SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH * dpi <
+ (s->params.lines + s->top)) {
+ s->params.lines =
+ ((int) SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH *
+ dpi + 0.5) - s->top;
+ }
+
+ s->block = SANE_FALSE;
+ s->lcount = 1;
+
+ /*
+ * The set line count commands needs to be sent for certain scanners in
+ * color mode. The D1 level requires it, we are however only testing for
+ * 'D' and not for the actual numeric level.
+ */
+
+ if ((s->hw->cmd->level[0] == 'B') && (s->hw->level >= 5)) /* >= B5 */
+ e2_setup_block_mode(s);
+
+ else if ((s->hw->cmd->level[0] == 'B') && (s->hw->level == 4) /* B4 !color */
+ && (!mode_params[s->val[OPT_MODE].w].color))
+ e2_setup_block_mode(s);
+
+ else if (s->hw->cmd->level[0] == 'D') /* Dx */
+ e2_setup_block_mode(s);
+
+ return (s->params.lines > 0) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL;
+}
+
+void
+e2_wait_button(Epson_Scanner * s)
+{
+ DBG(5, "%s\n", __func__);
+
+ s->hw->wait_for_button = SANE_TRUE;
+
+ while (s->hw->wait_for_button == SANE_TRUE) {
+ unsigned char button_status = 0;
+
+ if (s->canceling == SANE_TRUE)
+ s->hw->wait_for_button = SANE_FALSE;
+
+ /* get the button status from the scanner */
+ else if (esci_request_push_button_status(s, &button_status) ==
+ SANE_STATUS_GOOD) {
+ if (button_status)
+ s->hw->wait_for_button = SANE_FALSE;
+ else
+ sleep(1);
+ } else {
+ /* we run into an error condition, just continue */
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ }
+}
+
+/*
+SANE_Status
+e2_check_extended_status(Epson_Scanner *s)
+{
+
+ SANE_Status status = esci_request_scanner_status(s, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (buf[0] & FSF_STATUS_MAIN_WU)
+
+ main -> 0
+ fbf -> 3
+ adf -> 1, 10
+ tpu -> 2
+}
+*/
+
+SANE_Status
+e2_check_warm_up(Epson_Scanner * s, SANE_Bool * wup)
+{
+ SANE_Status status;
+
+ DBG(5, "%s\n", __func__);
+
+ *wup = SANE_FALSE;
+
+ if (s->hw->extended_commands) {
+ unsigned char buf[16];
+
+ status = esci_request_scanner_status(s, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (buf[0] & FSF_STATUS_MAIN_WU)
+ *wup = SANE_TRUE;
+
+ } else {
+ unsigned char *es;
+
+ /* this command is not available on some scanners */
+ if (!s->hw->cmd->request_extended_status)
+ return SANE_STATUS_GOOD;
+
+ status = esci_request_extended_status(s, &es, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (es[0] & EXT_STATUS_WU)
+ *wup = SANE_TRUE;
+
+ free(es);
+ }
+
+ return status;
+}
+
+SANE_Status
+e2_wait_warm_up(Epson_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Bool wup;
+
+ DBG(5, "%s\n", __func__);
+
+ s->retry_count = 0;
+
+ while (1) {
+
+ if (s->canceling)
+ return SANE_STATUS_CANCELLED;
+
+ status = e2_check_warm_up(s, &wup);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (wup == SANE_FALSE)
+ break;
+
+ s->retry_count++;
+
+ if (s->retry_count > SANE_EPSON_MAX_RETRIES) {
+ DBG(1, "max retry count exceeded (%d)\n",
+ s->retry_count);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ sleep(5);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+e2_check_adf(Epson_Scanner * s)
+{
+ SANE_Status status;
+
+ DBG(5, "%s\n", __func__);
+
+ if (s->hw->use_extension == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+
+ if (s->hw->extended_commands) {
+ unsigned char buf[16];
+
+ status = esci_request_scanner_status(s, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (buf[1] & FSF_STATUS_ADF_PE)
+ return SANE_STATUS_NO_DOCS;
+
+ if (buf[1] & FSF_STATUS_ADF_PJ)
+ return SANE_STATUS_JAMMED;
+
+ } else {
+ unsigned char *buf, t;
+
+ status = esci_request_extended_status(s, &buf, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;;
+
+ t = buf[1];
+
+ free(buf);
+
+ if (t & EXT_STATUS_PE)
+ return SANE_STATUS_NO_DOCS;
+
+ if (t & EXT_STATUS_PJ)
+ return SANE_STATUS_JAMMED;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+e2_start_std_scan(Epson_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(5, "%s\n", __func__);
+
+ /* ESC g */
+ params[0] = ESC;
+ params[1] = s->hw->cmd->start_scanning;
+
+ e2_send(s, params, 2, 6 + (s->lcount * s->params.bytes_per_line),
+ &status);
+
+ return status;
+}
+
+SANE_Status
+e2_start_ext_scan(Epson_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char params[2];
+ unsigned char buf[14];
+
+ DBG(5, "%s\n", __func__);
+
+ params[0] = FS;
+ params[1] = 'G';
+
+ status = e2_txrx(s, params, 2, buf, 14);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (buf[0] != STX)
+ return SANE_STATUS_INVAL;
+
+ if (buf[1] & 0x80) {
+ DBG(1, "%s: fatal error\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->ext_block_len = le32atoh(&buf[2]);
+ s->ext_blocks = le32atoh(&buf[6]);
+ s->ext_last_len = le32atoh(&buf[10]);
+
+ s->ext_counter = 0;
+
+ DBG(5, " status : 0x%02x\n", buf[1]);
+ DBG(5, " block size : %u\n", (unsigned int) le32atoh(&buf[2]));
+ DBG(5, " block count : %u\n", (unsigned int) le32atoh(&buf[6]));
+ DBG(5, " last block size: %u\n", (unsigned int) le32atoh(&buf[10]));
+
+ if (s->ext_last_len) {
+ s->ext_blocks++;
+ DBG(1, "adjusted block count: %d\n", s->ext_blocks);
+ }
+
+ /* adjust block len if we have only one block to read */
+ if (s->ext_block_len == 0 && s->ext_last_len)
+ s->ext_block_len = s->ext_last_len;
+
+ return status;
+}
+
+void
+e2_scan_finish(Epson_Scanner * s)
+{
+ DBG(5, "%s\n", __func__);
+
+ free(s->buf);
+ s->buf = NULL;
+
+ if (s->hw->ADF && s->hw->use_extension && s->val[OPT_AUTO_EJECT].w)
+ if (e2_check_adf(s) == SANE_STATUS_NO_DOCS)
+ esci_eject(s);
+
+ /* XXX required? */
+ if (s->hw->connection != SANE_EPSON_NET)
+ esci_reset(s);
+}
+
+void
+e2_copy_image_data(Epson_Scanner * s, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+{
+ if (!s->block && s->params.format == SANE_FRAME_RGB) {
+
+ max_length /= 3;
+
+ if (max_length > s->end - s->ptr)
+ max_length = s->end - s->ptr;
+
+ *length = 3 * max_length;
+
+ while (max_length-- != 0) {
+ *data++ = s->ptr[0];
+ *data++ = s->ptr[s->params.pixels_per_line];
+ *data++ = s->ptr[2 * s->params.pixels_per_line];
+ ++s->ptr;
+ }
+
+ } else {
+ if (max_length > s->end - s->ptr)
+ max_length = s->end - s->ptr;
+
+ *length = max_length;
+
+ if (s->params.depth == 1) {
+ while (max_length-- != 0)
+ *data++ = ~*s->ptr++;
+ } else {
+ memcpy(data, s->ptr, max_length);
+ s->ptr += max_length;
+ }
+ }
+}
+
+SANE_Status
+e2_ext_read(struct Epson_Scanner *s)
+{
+ struct Epson_Device *dev = s->hw;
+ SANE_Status status = SANE_STATUS_GOOD;
+ ssize_t buf_len = 0, read;
+
+ /* did we passed everything we read to sane? */
+ if (s->ptr == s->end) {
+
+ if (s->eof)
+ return SANE_STATUS_EOF;
+
+ s->ext_counter++;
+
+ /* sane has already got the data, read some more, the final
+ * error byte must not be included in buf_len
+ */
+ buf_len = s->ext_block_len;
+
+ if (s->ext_counter == s->ext_blocks && s->ext_last_len)
+ buf_len = s->ext_last_len;
+
+ DBG(18, "%s: block %d/%d, size %lu\n", __func__,
+ s->ext_counter, s->ext_blocks,
+ (unsigned long) buf_len);
+
+ /* receive image data + error code */
+ read = e2_recv(s, s->buf, buf_len + 1, &status);
+
+ DBG(18, "%s: read %lu bytes\n", __func__, (unsigned long) read);
+
+ if (read != buf_len + 1)
+ return SANE_STATUS_IO_ERROR;
+
+ if (e2_dev_model(dev, "GT-8200") || e2_dev_model(dev, "Perfection1650")) {
+ /* See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=597922#127 */
+ s->buf[buf_len] &= 0xc0;
+ }
+
+ if (s->buf[buf_len] & FSG_STATUS_CANCEL_REQ) {
+ DBG(0, "%s: cancel request received\n", __func__);
+ e2_cancel(s);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (s->buf[buf_len] & (FSG_STATUS_FER | FSG_STATUS_NOT_READY))
+ return SANE_STATUS_IO_ERROR;
+
+ /* ack every block except the last one */
+ if (s->ext_counter < s->ext_blocks) {
+ size_t next_len = s->ext_block_len;
+
+ if (s->ext_counter == (s->ext_blocks - 1))
+ next_len = s->ext_last_len;
+
+ if (s->canceling) {
+ e2_cancel(s);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ status = e2_ack_next(s, next_len + 1);
+ } else
+ s->eof = SANE_TRUE;
+
+ s->end = s->buf + buf_len;
+ s->ptr = s->buf;
+ }
+
+ return status;
+}
+
+/* XXXX use routine from sane-evolution */
+
+typedef struct
+{
+ unsigned char code;
+ unsigned char status;
+
+ unsigned char buf[4];
+
+} EpsonDataRec;
+
+
+/* XXX this routine is ugly and should be avoided */
+static SANE_Status
+read_info_block(Epson_Scanner * s, EpsonDataRec * result)
+{
+ SANE_Status status;
+ unsigned char params[2];
+
+ retry:
+ e2_recv(s, result, s->block ? 6 : 4, &status);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (result->code != STX) {
+ DBG(1, "error: got %02x, expected STX\n", result->code);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* XXX */
+ if (result->status & STATUS_FER) {
+ unsigned char *ext_status;
+
+ DBG(1, "fatal error, status = %02x\n", result->status);
+
+ if (s->retry_count > SANE_EPSON_MAX_RETRIES) {
+ DBG(1, "max retry count exceeded (%d)\n",
+ s->retry_count);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* if the scanner is warming up, retry after a few secs */
+ status = esci_request_extended_status(s, &ext_status, NULL);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (ext_status[0] & EXT_STATUS_WU) {
+ free(ext_status);
+
+ sleep(5); /* for the next attempt */
+
+ DBG(1, "retrying ESC G - %d\n", ++(s->retry_count));
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->start_scanning;
+
+ e2_send(s, params, 2, 0, &status);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ goto retry;
+ } else
+ free(ext_status);
+ }
+
+ return status;
+}
+
+static SANE_Status
+color_shuffle(SANE_Handle handle, int *new_length)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Byte *buf = s->buf;
+ int length = s->end - s->buf;
+
+ SANE_Byte *data_ptr; /* ptr to data to process */
+ SANE_Byte *data_end; /* ptr to end of processed data */
+ SANE_Byte *out_data_ptr; /* ptr to memory when writing data */
+ int i; /* loop counter */
+
+ /*
+ * It looks like we are dealing with a scanner that has an odd way
+ * of dealing with colors... The red and blue scan lines are shifted
+ * up or down by a certain number of lines relative to the green line.
+ */
+ DBG(5, "%s\n", __func__);
+
+ /*
+ * Initialize the variables we are going to use for the
+ * copying of the data. data_ptr is the pointer to
+ * the currently worked on scan line. data_end is the
+ * end of the data area as calculated from adding *length
+ * to the start of data.
+ * out_data_ptr is used when writing out the processed data
+ * and always points to the beginning of the next line to
+ * write.
+ */
+ data_ptr = out_data_ptr = buf;
+ data_end = data_ptr + length;
+
+ /*
+ * The image data is in *buf, we know that the buffer contains s->end - s->buf ( = length)
+ * bytes of data. The width of one line is in s->params.bytes_per_line
+ *
+ * The buffer area is supposed to have a number of full scan
+ * lines, let's test if this is the case.
+ */
+
+ if (length % s->params.bytes_per_line != 0) {
+ DBG(1, "error in buffer size: %d / %d\n", length,
+ s->params.bytes_per_line);
+ return SANE_STATUS_INVAL;
+ }
+
+ while (data_ptr < data_end) {
+ SANE_Byte *source_ptr, *dest_ptr;
+ int loop;
+
+ /* copy the green information into the current line */
+
+ source_ptr = data_ptr + 1;
+ dest_ptr = s->line_buffer[s->color_shuffle_line] + 1;
+
+ for (i = 0; i < s->params.bytes_per_line / 3; i++) {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+
+ /* copy the red information n lines back */
+
+ if (s->color_shuffle_line >= s->line_distance) {
+ source_ptr = data_ptr + 2;
+ dest_ptr =
+ s->line_buffer[s->color_shuffle_line -
+ s->line_distance] + 2;
+
+/* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */
+ for (loop = 0;
+ loop < s->params.bytes_per_line / 3;
+ loop++) {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+ }
+
+ /* copy the blue information n lines forward */
+
+ source_ptr = data_ptr;
+ dest_ptr =
+ s->line_buffer[s->color_shuffle_line +
+ s->line_distance];
+
+/* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */
+ for (loop = 0; loop < s->params.bytes_per_line / 3;
+ loop++) {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+
+ data_ptr += s->params.bytes_per_line;
+
+ if (s->color_shuffle_line == s->line_distance) {
+ /*
+ * We just finished the line in line_buffer[0] - write it to the
+ * output buffer and continue.
+ *
+ * The ouput buffer ist still "buf", but because we are
+ * only overwriting from the beginning of the memory area
+ * we are not interfering with the "still to shuffle" data
+ * in the same area.
+ */
+
+ /*
+ * Strip the first and last n lines and limit to
+ */
+ if ((s->current_output_line >=
+ s->line_distance)
+ && (s->current_output_line <
+ s->params.lines + s->line_distance)) {
+ memcpy(out_data_ptr,
+ s->line_buffer[0],
+ s->params.bytes_per_line);
+ out_data_ptr +=
+ s->params.bytes_per_line;
+
+ s->lines_written++;
+ }
+
+ s->current_output_line++;
+
+ /*
+ * Now remove the 0-entry and move all other
+ * lines up by one. There are 2*line_distance + 1
+ * buffers, * therefore the loop has to run from 0
+ * to * 2*line_distance, and because we want to
+ * copy every n+1st entry to n the loop runs
+ * from - to 2*line_distance-1!
+ */
+
+ free(s->line_buffer[0]);
+
+ for (i = 0; i < s->line_distance * 2; i++) {
+ s->line_buffer[i] =
+ s->line_buffer[i + 1];
+ }
+
+ /*
+ * and create one new buffer at the end
+ */
+
+ s->line_buffer[s->line_distance * 2] =
+ malloc(s->params.bytes_per_line);
+ if (s->line_buffer[s->line_distance * 2] ==
+ NULL) {
+ DBG(1, "out of memory (line %d)\n",
+ __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+ } else {
+ s->color_shuffle_line++; /* increase the buffer number */
+ }
+ }
+
+ /*
+ * At this time we've used up all the new data from the scanner, some of
+ * it is still in the line_buffers, but we are ready to return some of it
+ * to the front end software. To do so we have to adjust the size of the
+ * data area and the *new_length variable.
+ */
+
+ *new_length = out_data_ptr - buf;
+
+ return SANE_STATUS_GOOD;
+}
+
+static inline int
+get_color(int status)
+{
+ switch ((status >> 2) & 0x03) {
+ case 1:
+ return 1;
+ case 2:
+ return 0;
+ case 3:
+ return 2;
+ default:
+ return 0; /* required to make the compiler happy */
+ }
+}
+
+
+SANE_Status
+e2_block_read(struct Epson_Scanner *s)
+{
+ SANE_Status status;
+ SANE_Bool reorder = SANE_FALSE;
+ SANE_Bool needStrangeReorder = SANE_FALSE;
+
+ START_READ:
+ DBG(18, "%s: begin\n", __func__);
+
+ if (s->ptr == s->end) {
+ EpsonDataRec result;
+ unsigned int buf_len;
+
+ if (s->eof) {
+ if (s->hw->color_shuffle) {
+ DBG(1,
+ "written %d lines after color shuffle\n",
+ s->lines_written);
+ DBG(1, "lines requested: %d\n",
+ s->params.lines);
+ }
+
+ return SANE_STATUS_EOF;
+ }
+
+ status = read_info_block(s, &result);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ buf_len = result.buf[1] << 8 | result.buf[0];
+ buf_len *= (result.buf[3] << 8 | result.buf[2]);
+
+ DBG(18, "%s: buf len = %u\n", __func__, buf_len);
+
+ {
+ /* do we have to reorder the data ? */
+ if (get_color(result.status) == 0x01)
+ reorder = SANE_TRUE;
+
+ e2_recv(s, s->buf, buf_len, &status);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ }
+
+ if (result.status & STATUS_AREA_END) {
+ DBG(1, "%s: EOF\n", __func__);
+ s->eof = SANE_TRUE;
+ } else {
+ if (s->canceling) {
+ e2_cancel(s);
+ return SANE_STATUS_CANCELLED;
+ } else {
+ status = e2_ack(s);
+ }
+ }
+
+ s->end = s->buf + buf_len;
+ s->ptr = s->buf;
+
+ /*
+ * if we have to re-order the color components (GRB->RGB) we
+ * are doing this here:
+ */
+
+ /*
+ * Some scanners (e.g. the Perfection 1640 and GT-2200) seem
+ * to have the R and G channels swapped.
+ * The GT-8700 is the Asian version of the Perfection 1640.
+ * If the scanner name is one of these and the scan mode is
+ * RGB then swap the colors.
+ */
+
+ needStrangeReorder =
+ (strstr(s->hw->model, "GT-2200") ||
+ ((strstr(s->hw->model, "1640")
+ && strstr(s->hw->model, "Perfection"))
+ || strstr(s->hw->model, "GT-8700")))
+ && s->params.format == SANE_FRAME_RGB;
+
+ /*
+ * Certain Perfection 1650 also need this re-ordering of the two
+ * color channels. These scanners are identified by the problem
+ * with the half vertical scanning area. When we corrected this,
+ * we also set the variable s->hw->need_color_reorder
+ */
+ if (s->hw->need_color_reorder)
+ reorder = SANE_FALSE; /* reordering once is enough */
+
+ if (reorder && s->params.format == SANE_FRAME_RGB) {
+ SANE_Byte *ptr;
+
+ ptr = s->buf;
+ while (ptr < s->end) {
+ if (s->params.depth > 8) {
+ SANE_Byte tmp;
+
+ /* R->G G->R */
+ tmp = ptr[0];
+ ptr[0] = ptr[2]; /* first Byte G */
+ ptr[2] = tmp; /* first Byte R */
+
+ tmp = ptr[1];
+ ptr[1] = ptr[3]; /* second Byte G */
+ ptr[3] = tmp; /* second Byte R */
+
+ ptr += 6; /* go to next pixel */
+ } else {
+ /* R->G G->R */
+ SANE_Byte tmp;
+
+ tmp = ptr[0];
+ ptr[0] = ptr[1]; /* G */
+ ptr[1] = tmp; /* R */
+ /* B stays the same */
+ ptr += 3; /* go to next pixel */
+ }
+ }
+ }
+
+ /*
+ * Do the color_shuffle if everything else is correct - at this time
+ * most of the stuff is hardcoded for the Perfection 610
+ */
+
+ if (s->hw->color_shuffle) {
+ int new_length = 0;
+
+ status = color_shuffle(s, &new_length);
+ /* XXX check status here */
+
+ /*
+ * If no bytes are returned, check if the scanner is already done, if so,
+ * we'll probably just return, but if there is more data to process get
+ * the next batch.
+ */
+ if (new_length == 0 && s->end != s->ptr)
+ goto START_READ;
+
+ s->end = s->buf + new_length;
+ s->ptr = s->buf;
+ }
+
+ DBG(18, "%s: begin scan2\n", __func__);
+ }
+
+ DBG(18, "%s: end\n", __func__);
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/epson2-ops.h b/backend/epson2-ops.h
new file mode 100644
index 0000000..9c36326
--- /dev/null
+++ b/backend/epson2-ops.h
@@ -0,0 +1,52 @@
+/*
+ * epson2.c - SANE library for Epson scanners.
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for additional copyrights.
+ *
+ * Copyright (C) 2006-07 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+/* some defines to make handling the TPU easier */
+#define FILM_TYPE_NEGATIVE (1L << 0)
+#define FILM_TYPE_SLIDE (1L << 1)
+
+#define e2_model(s,m) e2_dev_model((s)->hw,(m))
+
+extern int sanei_scsi_max_request_size;
+extern int *gamma_params;
+
+extern void e2_dev_init(Epson_Device *dev, const char *devname, int conntype);
+extern SANE_Status e2_dev_post_init(struct Epson_Device *dev);
+extern SANE_Bool e2_dev_model(Epson_Device *dev, const char *model);
+extern void e2_set_cmd_level(SANE_Handle handle, unsigned char *level);
+extern SANE_Status e2_set_model(Epson_Scanner *s, unsigned char *model, size_t len);
+extern SANE_Status e2_add_resolution(Epson_Device *dev, int r);
+extern void e2_set_fbf_area(Epson_Scanner *s, int x, int y, int unit);
+extern void e2_set_adf_area(struct Epson_Scanner *s, int x, int y, int unit);
+extern void e2_set_tpu_area(struct Epson_Scanner *s, int x, int y, int unit);
+extern void e2_add_depth(Epson_Device *dev, SANE_Word depth);
+extern SANE_Status e2_discover_capabilities(Epson_Scanner *s);
+extern SANE_Status e2_set_extended_scanning_parameters(Epson_Scanner *s);
+extern SANE_Status e2_set_scanning_parameters(Epson_Scanner *s);
+extern void e2_setup_block_mode(Epson_Scanner *s);
+extern SANE_Status e2_init_parameters(Epson_Scanner *s);
+extern void e2_wait_button(Epson_Scanner *s);
+extern SANE_Status e2_check_warm_up(Epson_Scanner *s, SANE_Bool *wup);
+extern SANE_Status e2_wait_warm_up(Epson_Scanner *s);
+extern SANE_Status e2_check_adf(Epson_Scanner *s);
+extern SANE_Status e2_start_std_scan(Epson_Scanner *s);
+extern SANE_Status e2_start_ext_scan(Epson_Scanner *s);
+extern void e2_scan_finish(Epson_Scanner *s);
+extern void e2_copy_image_data(Epson_Scanner *s, SANE_Byte *data, SANE_Int max_length,
+ SANE_Int *length);
+extern SANE_Status e2_ext_read(struct Epson_Scanner *s);
+extern SANE_Status e2_block_read(struct Epson_Scanner *s);
diff --git a/backend/epson2.c b/backend/epson2.c
new file mode 100644
index 0000000..9f39c68
--- /dev/null
+++ b/backend/epson2.c
@@ -0,0 +1,2351 @@
+/*
+ * epson2.c - SANE library for Epson scanners.
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for additional copyrights.
+ *
+ * Copyright (C) 2006-10 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#define EPSON2_VERSION 1
+#define EPSON2_REVISION 0
+#define EPSON2_BUILD 124
+
+/* debugging levels:
+ *
+ * 127 e2_recv buffer
+ * 125 e2_send buffer
+ * 32 more network progression
+ * 24 network header
+ * 23 network info
+ * 20 usb cmd counters
+ * 18 sane_read
+ * 17 setvalue, getvalue, control_option
+ * 15 e2_send, e2_recv calls
+ * 13 e2_cmd_info_block
+ * 12 epson_cmd_simple
+ * 11 even more
+ * 10 more debug in ESC/I commands
+ * 9 ESC x/FS x in e2_send
+ * 8 ESC/I commands
+ * 7 open/close/attach
+ * 6 print_params
+ * 5 basic functions
+ * 3 status information
+ * 1 scanner info and capabilities
+ * warnings
+ */
+
+#include "sane/config.h"
+
+#include "epson2.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "sane/saneopts.h"
+#include "sane/sanei_scsi.h"
+#include "sane/sanei_usb.h"
+#include "sane/sanei_pio.h"
+#include "sane/sanei_tcp.h"
+#include "sane/sanei_udp.h"
+#include "sane/sanei_backend.h"
+#include "sane/sanei_config.h"
+
+#include "epson2-io.h"
+#include "epson2-commands.h"
+#include "epson2-ops.h"
+
+#include "epson2_scsi.h"
+#include "epson_usb.h"
+#include "epson2_net.h"
+
+/*
+ * Definition of the mode_param struct, that is used to
+ * specify the valid parameters for the different scan modes.
+ *
+ * The depth variable gets updated when the bit depth is modified.
+ */
+
+struct mode_param mode_params[] = {
+ {0, 0x00, 0x30, 1},
+ {0, 0x00, 0x30, 8},
+ {1, 0x02, 0x00, 8},
+ {0, 0x00, 0x30, 1}
+};
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+#ifdef SANE_FRAME_IR
+ SANE_I18N("Infrared"),
+#endif
+ NULL
+};
+
+static const SANE_String_Const adf_mode_list[] = {
+ SANE_I18N("Simplex"),
+ SANE_I18N("Duplex"),
+ NULL
+};
+
+/* Define the different scan sources */
+
+#define FBF_STR SANE_I18N("Flatbed")
+#define TPU_STR SANE_I18N("Transparency Unit")
+#define TPU_STR2 SANE_I18N("TPU8x10")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+/*
+ * source list need one dummy entry (save device settings is crashing).
+ * NOTE: no const - this list gets created while exploring the capabilities
+ * of the scanner.
+ */
+
+SANE_String_Const source_list[] = {
+ FBF_STR,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const SANE_String_Const film_list[] = {
+ SANE_I18N("Positive Film"),
+ SANE_I18N("Negative Film"),
+ SANE_I18N("Positive Slide"),
+ SANE_I18N("Negative Slide"),
+ NULL
+};
+
+static const SANE_String_Const focus_list[] = {
+ SANE_I18N("Focus on glass"),
+ SANE_I18N("Focus 2.5mm above glass"),
+ NULL
+};
+
+#define HALFTONE_NONE 0x01
+#define HALFTONE_TET 0x03
+
+const int halftone_params[] = {
+ HALFTONE_NONE,
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x80,
+ 0x90,
+ 0xa0,
+ 0xb0,
+ HALFTONE_TET,
+ 0xc0,
+ 0xd0
+};
+
+static const SANE_String_Const halftone_list[] = {
+ SANE_I18N("None"),
+ SANE_I18N("Halftone A (Hard Tone)"),
+ SANE_I18N("Halftone B (Soft Tone)"),
+ SANE_I18N("Halftone C (Net Screen)"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list_4[] = {
+ SANE_I18N("None"),
+ SANE_I18N("Halftone A (Hard Tone)"),
+ SANE_I18N("Halftone B (Soft Tone)"),
+ SANE_I18N("Halftone C (Net Screen)"),
+ SANE_I18N("Dither A (4x4 Bayer)"),
+ SANE_I18N("Dither B (4x4 Spiral)"),
+ SANE_I18N("Dither C (4x4 Net Screen)"),
+ SANE_I18N("Dither D (8x4 Net Screen)"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list_7[] = {
+ SANE_I18N("None"),
+ SANE_I18N("Halftone A (Hard Tone)"),
+ SANE_I18N("Halftone B (Soft Tone)"),
+ SANE_I18N("Halftone C (Net Screen)"),
+ SANE_I18N("Dither A (4x4 Bayer)"),
+ SANE_I18N("Dither B (4x4 Spiral)"),
+ SANE_I18N("Dither C (4x4 Net Screen)"),
+ SANE_I18N("Dither D (8x4 Net Screen)"),
+ SANE_I18N("Text Enhanced Technology"),
+ SANE_I18N("Download pattern A"),
+ SANE_I18N("Download pattern B"),
+ NULL
+};
+
+static const SANE_String_Const dropout_list[] = {
+ SANE_I18N("None"),
+ SANE_I18N("Red"),
+ SANE_I18N("Green"),
+ SANE_I18N("Blue"),
+ NULL
+};
+
+static const SANE_Bool correction_userdefined[] = {
+ SANE_FALSE,
+ SANE_TRUE,
+ SANE_TRUE,
+};
+
+static const SANE_String_Const correction_list[] = {
+ SANE_I18N("None"),
+ SANE_I18N("Built in CCT profile"),
+ SANE_I18N("User defined CCT profile"),
+ NULL
+};
+
+enum {
+ CORR_NONE, CORR_AUTO, CORR_USER
+};
+
+static const SANE_String_Const cct_mode_list[] = {
+ "Automatic",
+ "Reflective",
+ "Colour negatives",
+ "Monochrome negatives",
+ "Colour positives",
+ NULL
+};
+
+enum {
+ CCT_AUTO, CCT_REFLECTIVE, CCT_COLORNEG, CCT_MONONEG,
+ CCT_COLORPOS
+};
+
+/*
+ * Gamma correction:
+ * The A and B level scanners work differently than the D level scanners,
+ * therefore I define two different sets of arrays, plus one set of
+ * variables that get set to the actally used params and list arrays at runtime.
+ */
+
+static int gamma_params_ab[] = {
+ 0x01,
+ 0x03,
+ 0x00,
+ 0x10,
+ 0x20
+};
+
+static const SANE_String_Const gamma_list_ab[] = {
+ SANE_I18N("Default"),
+ SANE_I18N("User defined"),
+ SANE_I18N("High density printing"),
+ SANE_I18N("Low density printing"),
+ SANE_I18N("High contrast printing"),
+ NULL
+};
+
+static SANE_Bool gamma_userdefined_ab[] = {
+ SANE_FALSE,
+ SANE_TRUE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE,
+};
+
+static int gamma_params_d[] = {
+ 0x03,
+ 0x04
+};
+
+static const SANE_String_Const gamma_list_d[] = {
+ SANE_I18N("User defined (Gamma=1.0)"),
+ SANE_I18N("User defined (Gamma=1.8)"),
+ NULL
+};
+
+static SANE_Bool gamma_userdefined_d[] = {
+ SANE_TRUE,
+ SANE_TRUE
+};
+
+static SANE_Bool *gamma_userdefined;
+int *gamma_params;
+
+/* Bay list:
+ * this is used for the FilmScan
+ * XXX Add APS loader support
+ */
+
+static const SANE_String_Const bay_list[] = {
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ NULL
+};
+
+/* minimum, maximum, quantization */
+static const SANE_Range u8_range = { 0, 255, 0 };
+static const SANE_Range s8_range = { -127, 127, 0 };
+static const SANE_Range fx_range = { SANE_FIX(-2.0), SANE_FIX(2.0), 0 };
+
+static const SANE_Range outline_emphasis_range = { -2, 2, 0 };
+
+
+/*
+ * List of pointers to devices - will be dynamically allocated depending
+ * on the number of devices found.
+ */
+static const SANE_Device **devlist;
+
+
+/* Some utility functions */
+
+static size_t
+max_string_size(const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++) {
+ size = strlen(strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status attach_one_usb(SANE_String_Const devname);
+static SANE_Status attach_one_net(SANE_String_Const devname);
+
+static void
+print_params(const SANE_Parameters params)
+{
+ DBG(6, "params.format = %d\n", params.format);
+ DBG(6, "params.last_frame = %d\n", params.last_frame);
+ DBG(6, "params.bytes_per_line = %d\n", params.bytes_per_line);
+ DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line);
+ DBG(6, "params.lines = %d\n", params.lines);
+ DBG(6, "params.depth = %d\n", params.depth);
+}
+
+/*
+ * close_scanner()
+ *
+ * Close the open scanner. Depending on the connection method, a different
+ * close function is called.
+ */
+
+static void
+close_scanner(Epson_Scanner *s)
+{
+ DBG(7, "%s: fd = %d\n", __func__, s->fd);
+
+ if (s->fd == -1)
+ return;
+
+ /* send a request_status. This toggles w_cmd_count and r_cmd_count */
+ if (r_cmd_count % 2)
+ esci_request_status(s, NULL);
+
+ /* request extended status. This toggles w_cmd_count only */
+ if (w_cmd_count % 2)
+ esci_request_extended_status(s, NULL, NULL);
+
+ if (s->hw->connection == SANE_EPSON_NET) {
+ sanei_epson_net_unlock(s);
+ sanei_tcp_close(s->fd);
+ } else if (s->hw->connection == SANE_EPSON_SCSI) {
+ sanei_scsi_close(s->fd);
+ } else if (s->hw->connection == SANE_EPSON_PIO) {
+ sanei_pio_close(s->fd);
+ } else if (s->hw->connection == SANE_EPSON_USB) {
+ sanei_usb_close(s->fd);
+ }
+
+ s->fd = -1;
+}
+
+static void
+e2_network_discovery(void)
+{
+ fd_set rfds;
+ int fd, len;
+ SANE_Status status;
+
+ char *ip, *query = "EPSONP\x00\xff\x00\x00\x00\x00\x00\x00\x00";
+ unsigned char buf[76];
+
+ struct timeval to;
+
+ status = sanei_udp_open_broadcast(&fd);
+ if (status != SANE_STATUS_GOOD)
+ return;
+
+ sanei_udp_write_broadcast(fd, 3289, (unsigned char *) query, 15);
+
+ DBG(5, "%s, sent discovery packet\n", __func__);
+
+ to.tv_sec = 1;
+ to.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ sanei_udp_set_nonblock(fd, SANE_TRUE);
+ if (select(fd + 1, &rfds, NULL, NULL, &to) > 0) {
+ while ((len = sanei_udp_recvfrom(fd, buf, 76, &ip)) == 76) {
+ DBG(5, " response from %s\n", ip);
+
+ /* minimal check, protocol unknown */
+ if (strncmp((char *) buf, "EPSON", 5) == 0)
+ attach_one_net(ip);
+ }
+ }
+
+ DBG(5, "%s, end\n", __func__);
+
+ sanei_udp_close(fd);
+}
+
+/*
+ * open_scanner()
+ *
+ * Open the scanner device. Depending on the connection method,
+ * different open functions are called.
+ */
+
+static SANE_Status
+open_scanner(Epson_Scanner *s)
+{
+ SANE_Status status = 0;
+
+ DBG(7, "%s: %s\n", __func__, s->hw->sane.name);
+
+ if (s->fd != -1) {
+ DBG(5, "scanner is already open: fd = %d\n", s->fd);
+ return SANE_STATUS_GOOD; /* no need to open the scanner */
+ }
+
+ if (s->hw->connection == SANE_EPSON_NET) {
+ unsigned char buf[5];
+
+ /* device name has the form net:ipaddr */
+ status = sanei_tcp_open(&s->hw->sane.name[4], 1865, &s->fd);
+ if (status == SANE_STATUS_GOOD) {
+
+ ssize_t read;
+ struct timeval tv;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+ s->netlen = 0;
+
+ DBG(32, "awaiting welcome message\n");
+
+ /* the scanner sends a kind of welcome msg */
+ read = e2_recv(s, buf, 5, &status);
+ if (read != 5) {
+ sanei_tcp_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(32, "welcome message received, locking the scanner...\n");
+
+ /* lock the scanner for use by sane */
+ status = sanei_epson_net_lock(s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s cannot lock scanner: %s\n", s->hw->sane.name,
+ sane_strstatus(status));
+
+ sanei_tcp_close(s->fd);
+ s->fd = -1;
+
+ return status;
+ }
+
+ DBG(32, "scanner locked\n");
+ }
+
+ } else if (s->hw->connection == SANE_EPSON_SCSI)
+ status = sanei_scsi_open(s->hw->sane.name, &s->fd,
+ sanei_epson2_scsi_sense_handler,
+ NULL);
+ else if (s->hw->connection == SANE_EPSON_PIO)
+ /* device name has the form pio:0xnnn */
+ status = sanei_pio_open(&s->hw->sane.name[4], &s->fd);
+
+ else if (s->hw->connection == SANE_EPSON_USB)
+ status = sanei_usb_open(s->hw->sane.name, &s->fd);
+
+ if (status == SANE_STATUS_ACCESS_DENIED) {
+ DBG(1, "please check that you have permissions on the device.\n");
+ DBG(1, "if this is a multi-function device with a printer,\n");
+ DBG(1, "disable any conflicting driver (like usblp).\n");
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ DBG(1, "%s open failed: %s\n", s->hw->sane.name,
+ sane_strstatus(status));
+ else
+ DBG(5, "scanner opened\n");
+
+ return status;
+}
+
+static SANE_Status detect_scsi(struct Epson_Scanner *s)
+{
+ SANE_Status status;
+ struct Epson_Device *dev = s->hw;
+
+ char buf[INQUIRY_BUF_SIZE + 1];
+ size_t buf_size = INQUIRY_BUF_SIZE;
+
+ char *vendor = buf + 8;
+ char *model = buf + 16;
+ char *rev = buf + 32;
+
+ status = sanei_epson2_scsi_inquiry(s->fd, buf, &buf_size);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: inquiry failed: %s\n", __func__,
+ sane_strstatus(status));
+ return status;
+ }
+
+ buf[INQUIRY_BUF_SIZE] = 0;
+ DBG(1, "inquiry data:\n");
+ DBG(1, " vendor : %.8s\n", vendor);
+ DBG(1, " model : %.16s\n", model);
+ DBG(1, " revision: %.4s\n", rev);
+
+ if (buf[0] != TYPE_PROCESSOR) {
+ DBG(1, "%s: device is not of processor type (%d)\n",
+ __func__, buf[0]);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (strncmp(vendor, "EPSON", 5) != 0) {
+ DBG(1,
+ "%s: device doesn't look like an EPSON scanner\n",
+ __func__);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (strncmp(model, "SCANNER ", 8) != 0
+ && strncmp(model, "FilmScan 200", 12) != 0
+ && strncmp(model, "Perfection", 10) != 0
+ && strncmp(model, "Expression", 10) != 0
+ && strncmp(model, "GT", 2) != 0) {
+ DBG(1, "%s: this EPSON scanner is not supported\n",
+ __func__);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (strncmp(model, "FilmScan 200", 12) == 0) {
+ dev->sane.type = "film scanner";
+ e2_set_model(s, (unsigned char *) model, 12);
+ }
+
+ /* Issue a test unit ready SCSI command. The FilmScan 200
+ * requires it for a sort of "wake up". We might eventually
+ * get the return code and reissue it in case of failure.
+ */
+ sanei_epson2_scsi_test_unit_ready(s->fd);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+detect_usb(struct Epson_Scanner *s)
+{
+ SANE_Status status;
+ int vendor, product;
+ int i, numIds;
+ SANE_Bool is_valid;
+
+ /* if the sanei_usb_get_vendor_product call is not supported,
+ * then we just ignore this and rely on the user to config
+ * the correct device.
+ */
+
+ status = sanei_usb_get_vendor_product(s->fd, &vendor, &product);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "the device cannot be verified - will continue\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* check the vendor ID to see if we are dealing with an EPSON device */
+ if (vendor != SANE_EPSON_VENDOR_ID) {
+ /* this is not a supported vendor ID */
+ DBG(1, "not an Epson device at %s (vendor id=0x%x)\n",
+ s->hw->sane.name, vendor);
+ return SANE_STATUS_INVAL;
+ }
+
+ numIds = sanei_epson_getNumberOfUSBProductIds();
+ is_valid = SANE_FALSE;
+ i = 0;
+
+ /* check all known product IDs to verify that we know
+ about the device */
+ while (i != numIds && !is_valid) {
+ if (product == sanei_epson_usb_product_ids[i])
+ is_valid = SANE_TRUE;
+ i++;
+ }
+
+ if (is_valid == SANE_FALSE) {
+ DBG(1, "the device at %s is not a supported (product id=0x%x)\n",
+ s->hw->sane.name, product);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(1, "found valid Epson scanner: 0x%x/0x%x (vendorID/productID)\n",
+ vendor, product);
+
+ return SANE_STATUS_GOOD;
+}
+
+static int num_devices; /* number of scanners attached to backend */
+static Epson_Device *first_dev; /* first EPSON scanner in list */
+
+static struct Epson_Scanner *
+scanner_create(struct Epson_Device *dev, SANE_Status *status)
+{
+ struct Epson_Scanner *s;
+
+ s = malloc(sizeof(struct Epson_Scanner));
+ if (s == NULL) {
+ *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ memset(s, 0x00, sizeof(struct Epson_Scanner));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ return s;
+}
+
+static struct Epson_Scanner *
+device_detect(const char *name, int type, SANE_Status *status)
+{
+ struct Epson_Scanner *s;
+ struct Epson_Device *dev;
+
+ /* try to find the device in our list */
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, name) == 0) {
+
+ /* the device might have been just probed,
+ * sleep a bit.
+ */
+ if (dev->connection == SANE_EPSON_NET)
+ sleep(1);
+
+ return scanner_create(dev, status);
+ }
+ }
+
+ if (type == SANE_EPSON_NODEV) {
+ *status = SANE_STATUS_INVAL;
+ return NULL;
+ }
+
+ /* alloc and clear our device structure */
+ dev = malloc(sizeof(*dev));
+ if (!dev) {
+ *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+ memset(dev, 0x00, sizeof(struct Epson_Device));
+
+ s = scanner_create(dev, status);
+ if (s == NULL)
+ return NULL;
+
+ e2_dev_init(dev, name, type);
+
+ *status = open_scanner(s);
+ if (*status != SANE_STATUS_GOOD) {
+ free(s);
+ return NULL;
+ }
+
+ /* from now on, close_scanner() must be called */
+
+ /* SCSI and USB requires special care */
+ if (dev->connection == SANE_EPSON_SCSI) {
+
+ *status = detect_scsi(s);
+
+ } else if (dev->connection == SANE_EPSON_USB) {
+
+ *status = detect_usb(s);
+ }
+
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ /* set name and model (if not already set) */
+ if (dev->model == NULL)
+ e2_set_model(s, (unsigned char *) "generic", 7);
+
+ dev->name = strdup(name);
+ dev->sane.name = dev->name;
+
+ /* ESC @, reset */
+ *status = esci_reset(s);
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ *status = e2_discover_capabilities(s);
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ if (source_list[0] == NULL || dev->dpi_range.min == 0) {
+ DBG(1, "something is wrong in the discovery process, aborting.\n");
+ *status = SANE_STATUS_IO_ERROR;
+ goto close;
+ }
+
+ e2_dev_post_init(dev);
+
+ *status = esci_reset(s);
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ DBG(1, "scanner model: %s\n", dev->model);
+
+ /* add this scanner to the device list */
+
+ num_devices++;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ return s;
+
+close:
+ close_scanner(s);
+ free(s);
+ return NULL;
+}
+
+
+static SANE_Status
+attach(const char *name, int type)
+{
+ SANE_Status status;
+ Epson_Scanner *s;
+
+ DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type);
+
+ s = device_detect(name, type, &status);
+ if(s == NULL)
+ return status;
+
+ close_scanner(s);
+ free(s);
+ return status;
+}
+
+static SANE_Status
+attach_one_scsi(const char *dev)
+{
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ return attach(dev, SANE_EPSON_SCSI);
+}
+
+SANE_Status
+attach_one_usb(const char *dev)
+{
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ return attach(dev, SANE_EPSON_USB);
+}
+
+static SANE_Status
+attach_one_net(const char *dev)
+{
+ char name[39+4];
+
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+
+ strcpy(name, "net:");
+ strcat(name, dev);
+ return attach(name, SANE_EPSON_NET);
+}
+
+static SANE_Status
+attach_one_pio(const char *dev)
+{
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ return attach(dev, SANE_EPSON_PIO);
+}
+
+static SANE_Status
+attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
+{
+ int vendor, product;
+
+ int len = strlen(line);
+
+ DBG(7, "%s: len = %d, line = %s\n", __func__, len, line);
+
+ if (sscanf(line, "usb %i %i", &vendor, &product) == 2) {
+ /* add the vendor and product IDs to the list of
+ known devices before we call the attach function */
+
+ int numIds = sanei_epson_getNumberOfUSBProductIds();
+
+ if (vendor != 0x4b8)
+ return SANE_STATUS_INVAL; /* this is not an EPSON device */
+
+ sanei_epson_usb_product_ids[numIds - 1] = product;
+ sanei_usb_attach_matching_devices(line, attach_one_usb);
+
+ } else if (strncmp(line, "usb", 3) == 0 && len == 3) {
+
+ int i, numIds;
+
+ numIds = sanei_epson_getNumberOfUSBProductIds();
+
+ for (i = 0; i < numIds; i++) {
+ sanei_usb_find_devices(0x4b8,
+ sanei_epson_usb_product_ids[i], attach_one_usb);
+ }
+
+ } else if (strncmp(line, "net", 3) == 0) {
+
+ /* remove the "net" sub string */
+ const char *name = sanei_config_skip_whitespace(line + 3);
+
+ if (strncmp(name, "autodiscovery", 13) == 0)
+ e2_network_discovery();
+ else
+ attach_one_net(name);
+
+ } else if (strncmp(line, "pio", 3) == 0) {
+
+ /* remove the "pio" sub string */
+ const char *name = sanei_config_skip_whitespace(line + 3);
+
+ attach_one_pio(name);
+
+ } else {
+ sanei_config_attach_matching_devices(line, attach_one_scsi);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+free_devices(void)
+{
+ Epson_Device *dev, *next;
+
+ DBG(5, "%s\n", __func__);
+
+ for (dev = first_dev; dev; dev = next) {
+ next = dev->next;
+ free(dev->name);
+ free(dev->model);
+ free(dev);
+ }
+
+ free(devlist);
+
+ first_dev = NULL;
+}
+
+static void
+probe_devices(void)
+{
+ DBG(5, "%s\n", __func__);
+
+ free_devices();
+
+ sanei_configure_attach(EPSON2_CONFIG_FILE, NULL,
+ attach_one_config);
+}
+
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ DBG_INIT();
+ DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__);
+
+ DBG(1, "epson2 backend, version %i.%i.%i\n",
+ EPSON2_VERSION, EPSON2_REVISION, EPSON2_BUILD);
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR,
+ EPSON2_BUILD);
+
+ sanei_usb_init();
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Clean up the list of attached scanners. */
+void
+sane_exit(void)
+{
+ DBG(5, "%s\n", __func__);
+ free_devices();
+}
+
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Epson_Device *dev;
+ int i;
+
+ DBG(5, "%s\n", __func__);
+
+ probe_devices();
+
+ devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
+ if (!devlist) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(5, "%s - results:\n", __func__);
+
+ for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) {
+ DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model);
+ devlist[i] = &dev->sane;
+ }
+
+ devlist[i] = NULL;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options(Epson_Scanner *s)
+{
+ int i;
+
+ for (i = 0; i < NUM_OPTIONS; i++) {
+ s->opt[i].size = sizeof(SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size(mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 0; /* Binary */
+
+ /* disable infrared on unsupported scanners */
+ if (!e2_model(s, "GT-X800") && !e2_model(s, "GT-X700") && !e2_model(s, "GT-X900"))
+ mode_list[MODE_INFRARED] = NULL;
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->depth_list;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = s->hw->depth_list[1]; /* the first "real" element is the default */
+
+ if (s->hw->depth_list[0] == 1) /* only one element in the list -> hide the option */
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ /* halftone */
+ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
+ s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ s->opt[OPT_HALFTONE].desc = SANE_I18N("Selects the halftone.");
+
+ s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE].size = max_string_size(halftone_list_7);
+ s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+
+ /* XXX use defines */
+ if (s->hw->level >= 7)
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7;
+ else if (s->hw->level >= 4)
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4;
+ else
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
+
+ s->val[OPT_HALFTONE].w = 1; /* Halftone A */
+
+ if (!s->hw->cmd->set_halftoning)
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+
+ /* dropout */
+ s->opt[OPT_DROPOUT].name = "dropout";
+ s->opt[OPT_DROPOUT].title = SANE_I18N("Dropout");
+ s->opt[OPT_DROPOUT].desc = SANE_I18N("Selects the dropout.");
+
+ s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING;
+ s->opt[OPT_DROPOUT].size = max_string_size(dropout_list);
+ s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_DROPOUT].constraint.string_list = dropout_list;
+ s->val[OPT_DROPOUT].w = 0; /* None */
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_I18N("Selects the brightness.");
+
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range;
+ s->val[OPT_BRIGHTNESS].w = 0; /* Normal */
+
+ if (!s->hw->cmd->set_bright)
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ /* sharpness */
+ s->opt[OPT_SHARPNESS].name = "sharpness";
+ s->opt[OPT_SHARPNESS].title = SANE_I18N("Sharpness");
+ s->opt[OPT_SHARPNESS].desc = "";
+
+ s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range;
+ s->val[OPT_SHARPNESS].w = 0; /* Normal */
+
+ if (!s->hw->cmd->set_outline_emphasis)
+ s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE;
+
+ /* gamma */
+ s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION;
+ s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION;
+ s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION;
+
+ s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA_CORRECTION].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+
+ /*
+ * special handling for D1 function level - at this time I'm not
+ * testing for D1, I'm just assuming that all D level scanners will
+ * behave the same way. This has to be confirmed with the next D-level
+ * scanner
+ */
+ if (s->hw->cmd->level[0] == 'D') {
+ s->opt[OPT_GAMMA_CORRECTION].size =
+ max_string_size(gamma_list_d);
+ s->opt[OPT_GAMMA_CORRECTION].constraint.string_list =
+ gamma_list_d;
+ s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */
+ gamma_userdefined = gamma_userdefined_d;
+ gamma_params = gamma_params_d;
+ } else {
+ s->opt[OPT_GAMMA_CORRECTION].size =
+ max_string_size(gamma_list_ab);
+ s->opt[OPT_GAMMA_CORRECTION].constraint.string_list =
+ gamma_list_ab;
+ s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */
+ gamma_userdefined = gamma_userdefined_ab;
+ gamma_params = gamma_params_ab;
+ }
+
+ if (!s->hw->cmd->set_gamma)
+ s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof(SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof(SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0];
+
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof(SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0];
+
+ if (s->hw->cmd->set_gamma_table
+ && gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w] ==
+ SANE_TRUE) {
+
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* initialize the Gamma tables */
+ memset(&s->gamma_table[0], 0, 256 * sizeof(SANE_Word));
+ memset(&s->gamma_table[1], 0, 256 * sizeof(SANE_Word));
+ memset(&s->gamma_table[2], 0, 256 * sizeof(SANE_Word));
+
+/* memset(&s->gamma_table[3], 0, 256 * sizeof(SANE_Word)); */
+ for (i = 0; i < 256; i++) {
+ s->gamma_table[0][i] = i;
+ s->gamma_table[1][i] = i;
+ s->gamma_table[2][i] = i;
+
+/* s->gamma_table[3][i] = i; */
+ }
+
+
+ /* color correction */
+ s->opt[OPT_COLOR_CORRECTION].name = "color-correction";
+ s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N("Color correction");
+ s->opt[OPT_COLOR_CORRECTION].desc =
+ SANE_I18N("Sets the color correction table for the selected output device.");
+
+ s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_COLOR_CORRECTION].size = max_string_size(correction_list);
+ s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_COLOR_CORRECTION].constraint.string_list = correction_list;
+ s->val[OPT_COLOR_CORRECTION].w = CORR_AUTO;
+
+ if (!s->hw->cmd->set_color_correction)
+ s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->resolution_list;
+ s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->val[OPT_THRESHOLD].w = 0x80;
+
+ if (!s->hw->cmd->set_threshold)
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+
+ /* "Advanced" group: */
+ s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N("Advanced");
+ s->opt[OPT_ADVANCED_GROUP].desc = "";
+ s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* "Color correction" group: */
+ s->opt[OPT_CCT_GROUP].title = SANE_I18N("Color correction");
+ s->opt[OPT_CCT_GROUP].desc = "";
+ s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* XXX disabled for now */
+ s->opt[OPT_CCT_MODE].name = "cct-type";
+ s->opt[OPT_CCT_MODE].title = "CCT Profile Type";
+ s->opt[OPT_CCT_MODE].desc = "Color correction profile type";
+ s->opt[OPT_CCT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_CCT_MODE].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_MODE].size = max_string_size(cct_mode_list);
+ s->opt[OPT_CCT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_CCT_MODE].constraint.string_list = cct_mode_list;
+ s->val[OPT_CCT_MODE].w = CCT_AUTO;
+
+ s->opt[OPT_CCT_PROFILE].name = "cct-profile";
+ s->opt[OPT_CCT_PROFILE].title = "CCT Profile";
+ s->opt[OPT_CCT_PROFILE].desc = "Color correction profile data";
+ s->opt[OPT_CCT_PROFILE].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_PROFILE].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_PROFILE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_PROFILE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_PROFILE].constraint.range = &fx_range;
+ s->opt[OPT_CCT_PROFILE].size = 9 * sizeof(SANE_Word);
+ s->val[OPT_CCT_PROFILE].wa = s->cct_table;
+
+/* if (!s->hw->cmd->set_color_correction)
+ s->opt[OPT_FILM_TYPE].cap |= SANE_CAP_INACTIVE;
+*/
+
+ /* mirror */
+ s->opt[OPT_MIRROR].name = "mirror";
+ s->opt[OPT_MIRROR].title = SANE_I18N("Mirror image");
+ s->opt[OPT_MIRROR].desc = SANE_I18N("Mirror the image.");
+
+ s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
+ s->val[OPT_MIRROR].w = SANE_FALSE;
+
+ if (!s->hw->cmd->mirror_image)
+ s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE;
+
+ /* auto area segmentation */
+ s->opt[OPT_AAS].name = "auto-area-segmentation";
+ s->opt[OPT_AAS].title = SANE_I18N("Auto area segmentation");
+ s->opt[OPT_AAS].desc =
+ "Enables different dithering modes in image and text areas";
+
+ s->opt[OPT_AAS].type = SANE_TYPE_BOOL;
+ s->val[OPT_AAS].w = SANE_TRUE;
+
+ if (!s->hw->cmd->control_auto_area_segmentation)
+ s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE;
+
+ /* "Preview settings" group: */
+ s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW_GROUP].desc = "";
+ s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ /* "Optional equipment" group: */
+ s->opt[OPT_EQU_GROUP].title = SANE_I18N("Optional equipment");
+ s->opt[OPT_EQU_GROUP].desc = "";
+ s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size(source_list);
+
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+
+ if (!s->hw->extension)
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+
+ s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */
+
+
+ /* film type */
+ s->opt[OPT_FILM_TYPE].name = "film-type";
+ s->opt[OPT_FILM_TYPE].title = SANE_I18N("Film type");
+ s->opt[OPT_FILM_TYPE].desc = "";
+ s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_FILM_TYPE].size = max_string_size(film_list);
+ s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FILM_TYPE].constraint.string_list = film_list;
+ s->val[OPT_FILM_TYPE].w = 0;
+
+ if (!s->hw->cmd->set_bay)
+ s->opt[OPT_FILM_TYPE].cap |= SANE_CAP_INACTIVE;
+
+ /* focus position */
+ s->opt[OPT_FOCUS].name = SANE_EPSON_FOCUS_NAME;
+ s->opt[OPT_FOCUS].title = SANE_EPSON_FOCUS_TITLE;
+ s->opt[OPT_FOCUS].desc = SANE_EPSON_FOCUS_DESC;
+ s->opt[OPT_FOCUS].type = SANE_TYPE_STRING;
+ s->opt[OPT_FOCUS].size = max_string_size(focus_list);
+ s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FOCUS].constraint.string_list = focus_list;
+ s->val[OPT_FOCUS].w = 0;
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED;
+
+ if (s->hw->focusSupport == SANE_TRUE)
+ s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
+ else
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
+
+ /* forward feed / eject */
+ s->opt[OPT_EJECT].name = "eject";
+ s->opt[OPT_EJECT].title = SANE_I18N("Eject");
+ s->opt[OPT_EJECT].desc = SANE_I18N("Eject the sheet in the ADF");
+ s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON;
+
+ if ((!s->hw->ADF) && (!s->hw->cmd->set_bay)) { /* Hack: Using set_bay to indicate. */
+ s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* auto forward feed / eject */
+ s->opt[OPT_AUTO_EJECT].name = "auto-eject";
+ s->opt[OPT_AUTO_EJECT].title = SANE_I18N("Auto eject");
+ s->opt[OPT_AUTO_EJECT].desc =
+ SANE_I18N("Eject document after scanning");
+
+ s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL;
+ s->val[OPT_AUTO_EJECT].w = SANE_FALSE;
+
+ if (!s->hw->ADF)
+ s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE;
+
+
+ s->opt[OPT_ADF_MODE].name = "adf-mode";
+ s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode");
+ s->opt[OPT_ADF_MODE].desc =
+ SANE_I18N("Selects the ADF mode (simplex/duplex)");
+ s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list);
+ s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
+ s->val[OPT_ADF_MODE].w = 0; /* simplex */
+
+ if ((!s->hw->ADF) || (s->hw->duplex == SANE_FALSE))
+ s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
+
+ /* select bay */
+ s->opt[OPT_BAY].name = "bay";
+ s->opt[OPT_BAY].title = SANE_I18N("Bay");
+ s->opt[OPT_BAY].desc = SANE_I18N("Select bay to scan");
+ s->opt[OPT_BAY].type = SANE_TYPE_STRING;
+ s->opt[OPT_BAY].size = max_string_size(bay_list);
+ s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BAY].constraint.string_list = bay_list;
+ s->val[OPT_BAY].w = 0; /* Bay 1 */
+
+ if (!s->hw->cmd->set_bay)
+ s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE;
+
+
+ s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME;
+ s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE;
+ s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC;
+
+ s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL;
+ s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL;
+ s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED;
+
+ if (!s->hw->cmd->request_push_button_status)
+ s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *handle)
+{
+ SANE_Status status;
+ Epson_Scanner *s = NULL;
+
+ int l = strlen(name);
+
+ DBG(7, "%s: name = %s\n", __func__, name);
+
+ /* probe if empty device name provided */
+ if (l == 0) {
+
+ probe_devices();
+
+ if (first_dev == NULL) {
+ DBG(1, "no device detected\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ s = device_detect(first_dev->sane.name, first_dev->connection,
+ &status);
+ if (s == NULL) {
+ DBG(1, "cannot open a perfectly valid device (%s),"
+ " please report to the authors\n", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ } else {
+
+ if (strncmp(name, "net:", 4) == 0) {
+ s = device_detect(name, SANE_EPSON_NET, &status);
+ if (s == NULL)
+ return status;
+ } else if (strncmp(name, "libusb:", 7) == 0) {
+ s = device_detect(name, SANE_EPSON_USB, &status);
+ if (s == NULL)
+ return status;
+ } else if (strncmp(name, "pio:", 4) == 0) {
+ s = device_detect(name, SANE_EPSON_PIO, &status);
+ if (s == NULL)
+ return status;
+ } else {
+
+ /* as a last resort, check for a match
+ * in the device list. This should handle SCSI
+ * devices and platforms without libusb.
+ */
+
+ if (first_dev == NULL)
+ probe_devices();
+
+ s = device_detect(name, SANE_EPSON_NODEV, &status);
+ if (s == NULL) {
+ DBG(1, "invalid device name: %s\n", name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ }
+
+
+ /* s is always valid here */
+
+ DBG(1, "handle obtained\n");
+
+ init_options(s);
+
+ *handle = (SANE_Handle) s;
+
+ status = open_scanner(s);
+ if (status != SANE_STATUS_GOOD) {
+ free(s);
+ return status;
+ }
+
+ status = esci_reset(s);
+ if (status != SANE_STATUS_GOOD)
+ close_scanner(s);
+
+ return status;
+}
+
+void
+sane_close(SANE_Handle handle)
+{
+ int i;
+ Epson_Scanner *s;
+
+ /*
+ * XXX Test if there is still data pending from
+ * the scanner. If so, then do a cancel
+ */
+
+ s = (Epson_Scanner *) handle;
+
+ if (s->fd != -1)
+ close_scanner(s);
+
+ for (i = 0; i < LINES_SHUFFLE_MAX; i++) {
+ if (s->line_buffer[i] != NULL)
+ free(s->line_buffer[i]);
+ }
+
+ free(s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return NULL;
+
+ return s->opt + option;
+}
+
+static const SANE_String_Const *
+search_string_list(const SANE_String_Const *list, SANE_String value)
+{
+ while (*list != NULL && strcmp(value, *list) != 0)
+ list++;
+
+ return ((*list == NULL) ? NULL : list);
+}
+
+/*
+ Activate, deactivate an option. Subroutines so we can add
+ debugging info if we want. The change flag is set to TRUE
+ if we changed an option. If we did not change an option,
+ then the value of the changed flag is not modified.
+*/
+
+static void
+activateOption(Epson_Scanner *s, SANE_Int option, SANE_Bool *change)
+{
+ if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
+ s->opt[option].cap &= ~SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+deactivateOption(Epson_Scanner *s, SANE_Int option, SANE_Bool *change)
+{
+ if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
+ s->opt[option].cap |= SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+setOptionState(Epson_Scanner *s, SANE_Bool state, SANE_Int option,
+ SANE_Bool *change)
+{
+ if (state)
+ activateOption(s, option, change);
+ else
+ deactivateOption(s, option, change);
+}
+
+static SANE_Status
+getvalue(SANE_Handle handle, SANE_Int option, void *value)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ DBG(17, "%s: option = %d\n", __func__, option);
+
+ switch (option) {
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_CCT_PROFILE:
+ memcpy(value, sval->wa, sopt->size);
+ break;
+
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_MIRROR:
+ case OPT_AAS:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_SHARPNESS:
+ case OPT_AUTO_EJECT:
+ case OPT_THRESHOLD:
+ case OPT_BIT_DEPTH:
+ case OPT_WAIT_FOR_BUTTON:
+ *((SANE_Word *) value) = sval->w;
+ break;
+
+ case OPT_MODE:
+ case OPT_CCT_MODE:
+ case OPT_ADF_MODE:
+ case OPT_HALFTONE:
+ case OPT_DROPOUT:
+ case OPT_SOURCE:
+ case OPT_FILM_TYPE:
+ case OPT_GAMMA_CORRECTION:
+ case OPT_COLOR_CORRECTION:
+ case OPT_BAY:
+ case OPT_FOCUS:
+ strcpy((char *) value, sopt->constraint.string_list[sval->w]);
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This routine handles common options between OPT_MODE and
+ * OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS
+ * - auto area segmentation, and threshold. Apparently AAS
+ * is some method to differentiate between text and photos.
+ * Or something like that.
+ *
+ * AAS is available when the scan color depth is 1 and the
+ * halftone method is not TET.
+ *
+ * Threshold is available when halftone is NONE, and depth is 1.
+ */
+static void
+handle_depth_halftone(Epson_Scanner *s, SANE_Bool *reload)
+{
+ int hti = s->val[OPT_HALFTONE].w;
+ int mdi = s->val[OPT_MODE].w;
+ SANE_Bool aas = SANE_FALSE;
+ SANE_Bool thresh = SANE_FALSE;
+
+ /* this defaults to false */
+ setOptionState(s, thresh, OPT_THRESHOLD, reload);
+
+ if (!s->hw->cmd->control_auto_area_segmentation)
+ return;
+
+ if (mode_params[mdi].depth == 1) {
+
+ if (halftone_params[hti] != HALFTONE_TET)
+ aas = SANE_TRUE;
+
+ if (halftone_params[hti] == HALFTONE_NONE)
+ thresh = SANE_TRUE;
+ }
+ setOptionState(s, aas, OPT_AAS, reload);
+ setOptionState(s, thresh, OPT_THRESHOLD, reload);
+}
+
+/*
+ * Handles setting the source (flatbed, transparency adapter (TPU),
+ * or auto document feeder (ADF)).
+ *
+ * For newer scanners it also sets the focus according to the
+ * glass / TPU settings.
+ */
+
+static void
+change_source(Epson_Scanner *s, SANE_Int optindex, char *value)
+{
+ int force_max = SANE_FALSE;
+ SANE_Bool dummy;
+
+ DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex,
+ value);
+
+ /* reset the scanner when we are changing the source setting -
+ this is necessary for the Perfection 1650 */
+ if (s->hw->need_reset_on_source_change)
+ esci_reset(s);
+
+ s->focusOnGlass = SANE_TRUE; /* this is the default */
+
+ if (s->val[OPT_SOURCE].w == optindex)
+ return;
+
+ s->val[OPT_SOURCE].w = optindex;
+
+ if (s->val[OPT_TL_X].w == s->hw->x_range->min
+ && s->val[OPT_TL_Y].w == s->hw->y_range->min
+ && s->val[OPT_BR_X].w == s->hw->x_range->max
+ && s->val[OPT_BR_Y].w == s->hw->y_range->max) {
+ force_max = SANE_TRUE;
+ }
+
+ if (strcmp(ADF_STR, value) == 0) {
+ s->hw->x_range = &s->hw->adf_x_range;
+ s->hw->y_range = &s->hw->adf_y_range;
+ s->hw->use_extension = SANE_TRUE;
+ /* disable film type option */
+ deactivateOption(s, OPT_FILM_TYPE, &dummy);
+ s->val[OPT_FOCUS].w = 0;
+ if (s->hw->duplex) {
+ activateOption(s, OPT_ADF_MODE, &dummy);
+ } else {
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ s->val[OPT_ADF_MODE].w = 0;
+ }
+
+ DBG(1, "adf activated (%d %d)\n", s->hw->use_extension,
+ s->hw->duplex);
+
+ } else if (strcmp(TPU_STR, value) == 0 || strcmp(TPU_STR2, value) == 0) {
+ if (strcmp(TPU_STR, value) == 0) {
+ s->hw->x_range = &s->hw->tpu_x_range;
+ s->hw->y_range = &s->hw->tpu_y_range;
+ s->hw->TPU2 = SANE_FALSE;
+ }
+ if (strcmp(TPU_STR2, value) == 0) {
+ s->hw->x_range = &s->hw->tpu2_x_range;
+ s->hw->y_range = &s->hw->tpu2_y_range;
+ s->hw->TPU2 = SANE_TRUE;
+ }
+ s->hw->use_extension = SANE_TRUE;
+
+ /* enable film type option only if the scanner supports it */
+ if (s->hw->cmd->set_film_type != 0)
+ activateOption(s, OPT_FILM_TYPE, &dummy);
+ else
+ deactivateOption(s, OPT_FILM_TYPE, &dummy);
+
+ /* enable focus position if the scanner supports it */
+ if (s->hw->cmd->set_focus_position != 0) {
+ s->val[OPT_FOCUS].w = 1;
+ s->focusOnGlass = SANE_FALSE;
+ }
+
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ deactivateOption(s, OPT_EJECT, &dummy);
+ deactivateOption(s, OPT_AUTO_EJECT, &dummy);
+ } else {
+ /* neither ADF nor TPU active */
+ s->hw->x_range = &s->hw->fbf_x_range;
+ s->hw->y_range = &s->hw->fbf_y_range;
+ s->hw->use_extension = SANE_FALSE;
+
+ /* disable film type option */
+ deactivateOption(s, OPT_FILM_TYPE, &dummy);
+ s->val[OPT_FOCUS].w = 0;
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ }
+
+ /* special handling for FilmScan 200 */
+ if (s->hw->cmd->level[0] == 'F')
+ activateOption(s, OPT_FILM_TYPE, &dummy);
+
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+
+ if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
+ s->val[OPT_TL_X].w = s->hw->x_range->min;
+
+ if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
+ s->val[OPT_TL_Y].w = s->hw->y_range->min;
+
+ if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ setOptionState(s, s->hw->ADF
+ && s->hw->use_extension, OPT_AUTO_EJECT, &dummy);
+ setOptionState(s, s->hw->ADF
+ && s->hw->use_extension, OPT_EJECT, &dummy);
+}
+
+static SANE_Status
+setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ SANE_Status status;
+ const SANE_String_Const *optval = NULL;
+ int optindex = 0;
+ SANE_Bool reload = SANE_FALSE;
+
+ DBG(17, "%s: option = %d, value = %p\n", __func__, option, value);
+
+ status = sanei_constrain_value(sopt, value, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (info && value && (*info & SANE_INFO_INEXACT)
+ && sopt->type == SANE_TYPE_INT)
+ DBG(17, "%s: constrained val = %d\n", __func__,
+ *(SANE_Word *) value);
+
+ if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) {
+ optval = search_string_list(sopt->constraint.string_list,
+ (char *) value);
+ if (optval == NULL)
+ return SANE_STATUS_INVAL;
+ optindex = optval - sopt->constraint.string_list;
+ }
+
+ switch (option) {
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_CCT_PROFILE:
+ memcpy(sval->wa, value, sopt->size); /* Word arrays */
+ break;
+
+ case OPT_CCT_MODE:
+ case OPT_ADF_MODE:
+ case OPT_DROPOUT:
+ case OPT_FILM_TYPE:
+ case OPT_BAY:
+ case OPT_FOCUS:
+ sval->w = optindex; /* Simple lists */
+ break;
+
+ case OPT_EJECT:
+ /* XXX required? control_extension(s, 1); */
+ esci_eject(s);
+ break;
+
+ case OPT_RESOLUTION:
+ sval->w = *((SANE_Word *) value);
+ DBG(17, "setting resolution to %d\n", sval->w);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ sval->w = *((SANE_Word *) value);
+ if (SANE_UNFIX(sval->w) == 0) {
+ DBG(17, "invalid br-x or br-y\n");
+ return SANE_STATUS_INVAL;
+ }
+ /* passthru */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ sval->w = *((SANE_Word *) value);
+ DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w));
+ if (NULL != info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_SOURCE:
+ change_source(s, optindex, (char *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_MODE:
+ {
+ SANE_Bool isColor = mode_params[optindex].color;
+
+ sval->w = optindex;
+
+ /* halftoning available only on bw scans */
+ if (s->hw->cmd->set_halftoning != 0)
+ setOptionState(s, mode_params[optindex].depth == 1,
+ OPT_HALFTONE, &reload);
+
+ /* disable dropout on non-color scans */
+ setOptionState(s, !isColor, OPT_DROPOUT, &reload);
+
+ if (s->hw->cmd->set_color_correction)
+ setOptionState(s, isColor,
+ OPT_COLOR_CORRECTION, &reload);
+
+ /* if binary, then disable the bit depth selection */
+ if (optindex == 0) {
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ } else {
+ if (s->hw->depth_list[0] == 1)
+ s->opt[OPT_BIT_DEPTH].cap |=
+ SANE_CAP_INACTIVE;
+ else {
+ s->opt[OPT_BIT_DEPTH].cap &=
+ ~SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w =
+ mode_params[optindex].depth;
+ }
+ }
+
+ handle_depth_halftone(s, &reload);
+ reload = SANE_TRUE;
+
+ break;
+ }
+
+ case OPT_BIT_DEPTH:
+ sval->w = *((SANE_Word *) value);
+ mode_params[s->val[OPT_MODE].w].depth = sval->w;
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_HALFTONE:
+ sval->w = optindex;
+ handle_depth_halftone(s, &reload);
+ break;
+
+ case OPT_COLOR_CORRECTION:
+ {
+ sval->w = optindex;
+ break;
+ }
+
+ case OPT_GAMMA_CORRECTION:
+ {
+ SANE_Bool f = gamma_userdefined[optindex];
+
+ sval->w = optindex;
+
+ setOptionState(s, f, OPT_GAMMA_VECTOR_R, &reload);
+ setOptionState(s, f, OPT_GAMMA_VECTOR_G, &reload);
+ setOptionState(s, f, OPT_GAMMA_VECTOR_B, &reload);
+ setOptionState(s, !f, OPT_BRIGHTNESS, &reload); /* Note... */
+
+ break;
+ }
+
+ case OPT_MIRROR:
+ case OPT_AAS:
+ case OPT_PREVIEW: /* needed? */
+ case OPT_BRIGHTNESS:
+ case OPT_SHARPNESS:
+ case OPT_AUTO_EJECT:
+ case OPT_THRESHOLD:
+ case OPT_WAIT_FOR_BUTTON:
+ sval->w = *((SANE_Word *) value);
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ if (reload && info != NULL)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ DBG(17, "%s: end\n", __func__);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *value, SANE_Int *info)
+{
+ DBG(17, "%s: action = %x, option = %d\n", __func__, action, option);
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ if (info != NULL)
+ *info = 0;
+
+ switch (action) {
+ case SANE_ACTION_GET_VALUE:
+ return getvalue(handle, option, value);
+
+ case SANE_ACTION_SET_VALUE:
+ return setvalue(handle, option, value, info);
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ DBG(5, "%s\n", __func__);
+
+ if (params == NULL)
+ DBG(1, "%s: params is NULL\n", __func__);
+
+ /*
+ * If sane_start was already called, then just retrieve the parameters
+ * from the scanner data structure
+ */
+
+ if (!s->eof && s->ptr != NULL) {
+ DBG(5, "scan in progress, returning saved params structure\n");
+ } else {
+ /* otherwise initialize the params structure and gather the data */
+ e2_init_parameters(s);
+ }
+
+ if (params != NULL)
+ *params = s->params;
+
+ print_params(s->params);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void e2_load_cct_profile(struct Epson_Scanner *s, unsigned int index)
+{
+ s->cct_table[0] = SANE_FIX(s->hw->cct_profile->cct[index][0]);
+ s->cct_table[1] = SANE_FIX(s->hw->cct_profile->cct[index][1]);
+ s->cct_table[2] = SANE_FIX(s->hw->cct_profile->cct[index][2]);
+ s->cct_table[3] = SANE_FIX(s->hw->cct_profile->cct[index][3]);
+ s->cct_table[4] = SANE_FIX(s->hw->cct_profile->cct[index][4]);
+ s->cct_table[5] = SANE_FIX(s->hw->cct_profile->cct[index][5]);
+ s->cct_table[6] = SANE_FIX(s->hw->cct_profile->cct[index][6]);
+ s->cct_table[7] = SANE_FIX(s->hw->cct_profile->cct[index][7]);
+ s->cct_table[8] = SANE_FIX(s->hw->cct_profile->cct[index][8]);
+}
+
+/*
+ * This function is part of the SANE API and gets called from the front end to
+ * start the scan process.
+ */
+
+SANE_Status
+sane_start(SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ Epson_Device *dev = s->hw;
+ SANE_Status status;
+
+ DBG(5, "%s\n", __func__);
+
+ /* check if we just have finished working with the ADF */
+ status = e2_check_adf(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* calc scanning parameters */
+ status = e2_init_parameters(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ print_params(s->params);
+
+ /* enable infrared */
+ if (s->val[OPT_MODE].w == MODE_INFRARED)
+ esci_enable_infrared(handle);
+
+ /* ESC , bay */
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BAY].cap)) {
+ status = esci_set_bay(s, s->val[OPT_BAY].w);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* set scanning parameters */
+ if (dev->extended_commands)
+ status = e2_set_extended_scanning_parameters(s);
+ else
+ status = e2_set_scanning_parameters(s);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* ESC z, user defined gamma table */
+ if (dev->cmd->set_gamma_table
+ && gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w]) {
+ status = esci_set_gamma_table(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+
+ if (s->val[OPT_COLOR_CORRECTION].w == CORR_AUTO) { /* Automatic */
+
+ DBG(1, "using built in CCT profile\n");
+
+ if (dev->model_id == 0)
+ DBG(1, " specific profile not available, using default\n");
+
+
+ if (0) { /* XXX TPU */
+
+ /* XXX check this */
+ if (s->val[OPT_FILM_TYPE].w == 0)
+ e2_load_cct_profile(s, CCTP_COLORPOS);
+ else
+ e2_load_cct_profile(s, CCTP_COLORNEG);
+
+ } else {
+ e2_load_cct_profile(s, CCTP_REFLECTIVE);
+ }
+ }
+
+ /* ESC m, user defined color correction */
+ if (s->hw->cmd->set_color_correction_coefficients
+ && correction_userdefined[s->val[OPT_COLOR_CORRECTION].w]) {
+
+ status = esci_set_color_correction_coefficients(s,
+ s->cct_table);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* check if we just have finished working with the ADF.
+ * this seems to work only after the scanner has been
+ * set up with scanning parameters
+ */
+ status = e2_check_adf(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /*
+ * If WAIT_FOR_BUTTON is active, then do just that:
+ * Wait until the button is pressed. If the button was already
+ * pressed, then we will get the button pressed event right away.
+ */
+ if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE)
+ e2_wait_button(s);
+
+ /* for debug, request command parameter */
+/* if (DBG_LEVEL) {
+ unsigned char buf[45];
+ request_command_parameter(s, buf);
+ }
+*/
+ /* set the retry count to 0 */
+ s->retry_count = 0;
+
+ /* allocate buffers for color shuffling */
+ if (dev->color_shuffle == SANE_TRUE) {
+ int i;
+ /* initialize the line buffers */
+ for (i = 0; i < s->line_distance * 2 + 1; i++) {
+
+ if (s->line_buffer[i] != NULL)
+ free(s->line_buffer[i]);
+
+ s->line_buffer[i] = malloc(s->params.bytes_per_line);
+ if (s->line_buffer[i] == NULL) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ /* prepare buffer here so that a memory allocation failure
+ * will leave the scanner in a sane state.
+ * the buffer will have to hold the image data plus
+ * an error code in the extended handshaking mode.
+ */
+ s->buf = realloc(s->buf, (s->lcount * s->params.bytes_per_line) + 1);
+ if (s->buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ s->eof = SANE_FALSE;
+ s->ptr = s->end = s->buf;
+ s->canceling = SANE_FALSE;
+
+ /* feed the first sheet in the ADF */
+ if (dev->ADF && dev->use_extension && dev->cmd->feed) {
+ status = esci_feed(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* this seems to work only for some devices */
+ status = e2_wait_warm_up(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* start scanning */
+ DBG(1, "%s: scanning...\n", __func__);
+
+ if (dev->extended_commands) {
+ status = e2_start_ext_scan(s);
+
+ /* sometimes the scanner gives an io error when
+ * it's warming up.
+ */
+ if (status == SANE_STATUS_IO_ERROR) {
+ status = e2_wait_warm_up(s);
+ if (status == SANE_STATUS_GOOD)
+ status = e2_start_ext_scan(s);
+ }
+ } else
+ status = e2_start_std_scan(s);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: start failed: %s\n", __func__,
+ sane_strstatus(status));
+
+ return status;
+ }
+
+ /* this is a kind of read request */
+ if (dev->connection == SANE_EPSON_NET) {
+ sanei_epson_net_write(s, 0x2000, NULL, 0,
+ s->ext_block_len + 1, &status);
+ }
+
+ return status;
+}
+
+static inline int
+get_color(int status)
+{
+ switch ((status >> 2) & 0x03) {
+ case 1:
+ return 1;
+ case 2:
+ return 0;
+ case 3:
+ return 2;
+ default:
+ return 0; /* required to make the compiler happy */
+ }
+}
+
+/* this moves data from our buffers to SANE */
+
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length,
+ SANE_Int *length)
+{
+ SANE_Status status;
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ if (s->buf == NULL || s->canceling)
+ return SANE_STATUS_CANCELLED;
+
+ *length = 0;
+
+ if (s->hw->extended_commands)
+ status = e2_ext_read(s);
+ else
+ status = e2_block_read(s);
+
+ if (status == SANE_STATUS_CANCELLED) {
+ e2_scan_finish(s);
+ return status;
+ }
+
+ /* XXX if FS G and STATUS_IOERR, use e2_check_extended_status */
+
+ DBG(18, "moving data %p %p, %d (%d lines)\n",
+ s->ptr, s->end,
+ max_length, max_length / s->params.bytes_per_line);
+
+ e2_copy_image_data(s, data, max_length, length);
+
+ DBG(18, "%d lines read, eof: %d, status: %d\n",
+ *length / s->params.bytes_per_line,
+ s->eof, status);
+
+ /* continue reading if appropriate */
+ if (status == SANE_STATUS_GOOD)
+ return status;
+
+ e2_scan_finish(s);
+
+ return status;
+}
+
+/*
+ * void sane_cancel(SANE_Handle handle)
+ *
+ * Set the cancel flag to true. The next time the backend requests data
+ * from the scanner the CAN message will be sent.
+ */
+
+void
+sane_cancel(SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ s->canceling = SANE_TRUE;
+}
+
+/*
+ * SANE_Status sane_set_io_mode()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_set_io_mode(SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * SANE_Status sane_get_select_fd()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_get_select_fd(SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ *fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/epson2.conf.in b/backend/epson2.conf.in
new file mode 100644
index 0000000..a996487
--- /dev/null
+++ b/backend/epson2.conf.in
@@ -0,0 +1,27 @@
+# epson2.conf
+#
+# here are some examples for how to configure the EPSON2 backend
+
+# SCSI
+scsi EPSON
+# for the GT-6500:
+#scsi "EPSON SC"
+
+# Parallel port
+#pio 0x278
+#pio 0x378
+#pio 0x3BC
+
+# USB
+usb
+
+# For libusb support for unknown scanners use the following command
+# usb <product ID> <device ID>
+# e.g.:
+# usb 0x4b8 0x110
+
+# Network
+#
+# net 192.168.1.123
+net autodiscovery
+
diff --git a/backend/epson2.h b/backend/epson2.h
new file mode 100644
index 0000000..bb6c9e0
--- /dev/null
+++ b/backend/epson2.h
@@ -0,0 +1,425 @@
+/*
+ * epson2.h - SANE library for Epson scanners.
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#ifndef epson2_h
+#define epson2_h
+
+#undef BACKEND_NAME
+#define BACKEND_NAME epson2
+#define DEBUG_NOT_STATIC
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h> /* for memset and memcpy */
+#include <stdio.h>
+
+#include "sane/sane.h"
+#include "sane/sanei_backend.h"
+#include "sane/sanei_debug.h"
+
+#ifdef __GNUC__
+#define __func__ __FUNCTION__
+#else
+#define __func__ "(undef)"
+/* I cast my vote for C99... :) */
+#endif
+
+#define EPSON2_CONFIG_FILE "epson2.conf"
+
+#ifndef PATH_MAX
+#define PATH_MAX (1024)
+#endif
+
+#ifndef XtNumber
+#define XtNumber(x) (sizeof(x) / sizeof(x[0]))
+#define XtOffset(p_type, field) ((size_t)&(((p_type)NULL)->field))
+#define XtOffsetOf(s_type, field) XtOffset(s_type*, field)
+#endif
+
+#define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */
+#define DEVICE_NAME_LEN (16) /* length of device name in extended status */
+
+
+/* string constants for GUI elements that are not defined SANE-wide */
+
+#define SANE_NAME_GAMMA_CORRECTION "gamma-correction"
+#define SANE_TITLE_GAMMA_CORRECTION SANE_I18N("Gamma Correction")
+#define SANE_DESC_GAMMA_CORRECTION SANE_I18N("Selects the gamma correction value from a list of pre-defined devices or the user defined table, which can be downloaded to the scanner")
+
+#define SANE_EPSON_FOCUS_NAME "focus-position"
+#define SANE_EPSON_FOCUS_TITLE SANE_I18N("Focus Position")
+#define SANE_EPSON_FOCUS_DESC SANE_I18N("Sets the focus position to either the glass or 2.5mm above the glass")
+#define SANE_EPSON_WAIT_FOR_BUTTON_NAME "wait-for-button"
+#define SANE_EPSON_WAIT_FOR_BUTTON_TITLE SANE_I18N("Wait for Button")
+#define SANE_EPSON_WAIT_FOR_BUTTON_DESC SANE_I18N("After sending the scan command, wait until the button on the scanner is pressed to actually start the scan process.");
+
+/* misc constants */
+
+#define LINES_SHUFFLE_MAX 17 /* 2 x 8 lines plus 1 */
+#define SANE_EPSON_MAX_RETRIES 14 /* warmup max retry */
+#define CMD_SIZE_EXT_STATUS 42
+
+/* NOTE: you can find these codes with "man ascii". */
+#define STX 0x02
+#define ACK 0x06
+#define NAK 0x15
+#define CAN 0x18
+#define ESC 0x1B
+#define PF 0x19
+#define FS 0x1C
+
+#define S_ACK "\006"
+#define S_CAN "\030"
+
+/* status bits */
+
+#define STATUS_FER 0x80 /* fatal error */
+#define STATUS_NOT_READY 0x40 /* scanner is in use on another interface */
+#define STATUS_AREA_END 0x20 /* area end */
+#define STATUS_OPTION 0x10 /* option installed */
+#define STATUS_EXT_COMMANDS 0x02 /* scanners supports extended commands */
+#define STATUS_RESERVED 0x01 /* this should be always 0 */
+
+#define EXT_STATUS_FER 0x80 /* fatal error */
+#define EXT_STATUS_FBF 0x40 /* flat bed scanner */
+#define EXT_STATUS_ADFT 0x20 /* page type ADF */
+#define EXT_STATUS_ADFS 0x10 /* ADF is duplex capable */
+#define EXT_STATUS_ADFO 0x08 /* ADF loads from the first sheet (page type only) */
+#define EXT_STATUS_LID 0x04 /* lid is open */
+#define EXT_STATUS_WU 0x02 /* warming up */
+#define EXT_STATUS_PB 0x01 /* scanner has a push button */
+
+#define EXT_STATUS_IST 0x80 /* option detected */
+#define EXT_STATUS_EN 0x40 /* option enabled */
+#define EXT_STATUS_ERR 0x20 /* other error */
+#define EXT_STATUS_PE 0x08 /* no paper */
+#define EXT_STATUS_PJ 0x04 /* paper jam */
+#define EXT_STATUS_OPN 0x02 /* cover open */
+
+#define EXT_IDTY_CAP1_DLF 0x80
+#define EXT_IDTY_CAP1_NOTFBF 0x40 /* not a flat bed scanner */
+#define EXT_IDTY_CAP1_ADFT 0x20 /* page type ADF ? */
+#define EXT_IDTY_CAP1_ADFS 0x10 /* ADF is duplex capable */
+#define EXT_IDTY_CAP1_ADFO 0x08 /* ADF loads from the first sheet (page type only) */
+#define EXT_IDTY_CAP1_LID 0x04 /* lid type option ? */
+#define EXT_IDTY_CAP1_TPIR 0x02 /* TPU with infrared */
+#define EXT_IDTY_CAP1_PB 0x01 /* scanner has a push button */
+
+#define EXT_IDTY_CAP2_AFF 0x04 /* auto form feed */
+#define EXT_IDTY_CAP2_DFD 0x08 /* double feed detection */
+#define EXT_IDTY_CAP2_ADFAS 0x10 /* ADF with auto scan support */
+
+#define FSF_STATUS_MAIN_FER 0x80 /* system error */
+#define FSF_STATUS_MAIN_NR 0x40 /* not ready */
+#define FSF_STATUS_MAIN_WU 0x02 /* warming up */
+#define FSF_STATUS_MAIN_CWU 0x01 /* warm up can be cancelled (?) */
+
+#define FSF_STATUS_ADF_IST 0x80 /* installed */
+#define FSF_STATUS_ADF_EN 0x40 /* enabled */
+#define FSF_STATUS_ADF_ERR 0x20 /* system error */
+#define FSF_STATUS_ADF_PE 0x08 /* paper empty */
+#define FSF_STATUS_ADF_PJ 0x04 /* paper jam */
+#define FSF_STATUS_ADF_OPN 0x02 /* cover open */
+#define FSF_STATUS_ADF_PAG 0x01 /* duplex */
+
+#define FSF_STATUS_TPU_IST 0x80 /* installed */
+#define FSF_STATUS_TPU_EN 0x40 /* enabled */
+#define FSF_STATUS_TPU_ERR 0x20 /* system error */
+#define FSF_STATUS_TPU_OPN 0x02 /* cover open */
+
+#define FSF_STATUS_MAIN2_ERR 0x20 /* system error */
+#define FSF_STATUS_MAIN2_PE 0x08 /* paper empty */
+#define FSF_STATUS_MAIN2_PJ 0x04 /* paper jam */
+#define FSF_STATUS_MAIN2_OPN 0x02 /* cover open */
+
+#define FSG_STATUS_FER 0x80
+#define FSG_STATUS_NOT_READY 0x40 /* in use via other interface */
+#define FSG_STATUS_CANCEL_REQ 0x10 /* cancel request from scanner */
+
+#define EPSON_LEVEL_A1 0
+#define EPSON_LEVEL_A2 1
+#define EPSON_LEVEL_B1 2
+#define EPSON_LEVEL_B2 3
+#define EPSON_LEVEL_B3 4
+#define EPSON_LEVEL_B4 5
+#define EPSON_LEVEL_B5 6
+#define EPSON_LEVEL_B6 7
+#define EPSON_LEVEL_B7 8
+#define EPSON_LEVEL_B8 9
+#define EPSON_LEVEL_F5 10
+#define EPSON_LEVEL_D1 11
+#define EPSON_LEVEL_D7 12
+#define EPSON_LEVEL_D8 13
+
+/* there is also a function level "A5", which I'm igoring here until somebody can
+ * convince me that this is still needed. The A5 level was for the GT-300, which
+ * was (is) a monochrome only scanner. So if somebody really wants to use this
+ * scanner with SANE get in touch with me and we can work something out - khk
+ */
+
+#define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3
+
+struct EpsonCmd
+{
+ char *level;
+
+ unsigned char request_identity;
+ unsigned char request_identity2; /* new request identity level Dx */
+ unsigned char request_status;
+ unsigned char request_condition;
+ unsigned char set_color_mode;
+ unsigned char start_scanning;
+ unsigned char set_data_format;
+ unsigned char set_resolution;
+ unsigned char set_zoom;
+ unsigned char set_scan_area;
+ unsigned char set_bright;
+ SANE_Range bright_range;
+ unsigned char set_gamma;
+ unsigned char set_halftoning;
+ unsigned char set_color_correction;
+ unsigned char initialize_scanner;
+ unsigned char set_speed; /* B4 and later */
+ unsigned char set_lcount;
+ unsigned char mirror_image; /* B5 and later */
+ unsigned char set_gamma_table; /* B4 and later */
+ unsigned char set_outline_emphasis; /* B4 and later */
+ unsigned char set_dither; /* B4 and later */
+ unsigned char set_color_correction_coefficients; /* B3 and later */
+ unsigned char request_extended_status; /* get extended status from scanner */
+ unsigned char control_an_extension; /* for extension control */
+ unsigned char eject; /* for extension control */
+ unsigned char feed;
+ unsigned char request_push_button_status;
+ unsigned char control_auto_area_segmentation;
+ unsigned char set_film_type; /* for extension control */
+ unsigned char set_exposure_time; /* F5 only */
+ unsigned char set_bay; /* F5 only */
+ unsigned char set_threshold;
+ unsigned char set_focus_position; /* B8 only */
+ unsigned char request_focus_position; /* B8 only */
+ unsigned char request_extended_identity;
+ unsigned char request_scanner_status;
+};
+
+enum {
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_BIT_DEPTH,
+ OPT_HALFTONE,
+ OPT_DROPOUT,
+ OPT_BRIGHTNESS,
+ OPT_SHARPNESS,
+ OPT_GAMMA_CORRECTION,
+ OPT_COLOR_CORRECTION,
+ OPT_RESOLUTION,
+ OPT_THRESHOLD,
+ OPT_ADVANCED_GROUP,
+ OPT_MIRROR,
+ OPT_AAS,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_WAIT_FOR_BUTTON,
+ OPT_CCT_GROUP,
+ OPT_CCT_MODE,
+ OPT_CCT_PROFILE,
+ OPT_PREVIEW_GROUP,
+ OPT_PREVIEW,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_EQU_GROUP,
+ OPT_SOURCE,
+ OPT_AUTO_EJECT,
+ OPT_FILM_TYPE,
+ OPT_FOCUS,
+ OPT_BAY,
+ OPT_EJECT,
+ OPT_ADF_MODE,
+ NUM_OPTIONS
+};
+
+typedef enum
+{ /* hardware connection to the scanner */
+ SANE_EPSON_NODEV, /* default, no HW specified yet */
+ SANE_EPSON_SCSI, /* SCSI interface */
+ SANE_EPSON_PIO, /* parallel interface */
+ SANE_EPSON_USB, /* USB interface */
+ SANE_EPSON_NET /* network interface */
+} Epson_Connection_Type;
+
+struct epson_profile
+{
+ unsigned int model;
+ double cct[4][9];
+};
+
+enum {
+ CCTP_REFLECTIVE = 0, CCTP_COLORNEG,
+ CCTP_MONONEG, CCTP_COLORPOS
+};
+
+struct epson_profile_map
+{
+ char *name;
+ unsigned int id;
+};
+
+extern const struct epson_profile epson_cct_profiles[];
+extern const struct epson_profile_map epson_cct_models[];
+
+/* hardware description */
+
+struct Epson_Device
+{
+ struct Epson_Device *next;
+
+ char *name;
+ char *model;
+
+ unsigned int model_id;
+
+ SANE_Device sane;
+ SANE_Int level;
+ SANE_Range dpi_range;
+
+ SANE_Range *x_range; /* x range w/out extension */
+ SANE_Range *y_range; /* y range w/out extension */
+
+ SANE_Range fbf_x_range; /* flattbed x range */
+ SANE_Range fbf_y_range; /* flattbed y range */
+ SANE_Range adf_x_range; /* autom. document feeder x range */
+ SANE_Range adf_y_range; /* autom. document feeder y range */
+ SANE_Range tpu_x_range; /* transparency unit x range */
+ SANE_Range tpu_y_range; /* transparency unit y range */
+ SANE_Range tpu2_x_range; /* transparency unit 2 x range */
+ SANE_Range tpu2_y_range; /* transparency unit 2 y range */
+
+ Epson_Connection_Type connection;
+
+ SANE_Int *res_list; /* list of resolutions */
+ SANE_Int res_list_size; /* number of entries in this list */
+ SANE_Int last_res; /* last selected resolution */
+ SANE_Int last_res_preview; /* last selected preview resolution */
+
+ SANE_Word *resolution_list; /* for display purposes we store a second copy */
+
+ SANE_Bool extension; /* extension is installed */
+ SANE_Int use_extension; /* use the installed extension */
+ SANE_Bool TPU; /* TPU is installed */
+ SANE_Bool TPU2; /* TPU2 is installed */
+ SANE_Bool ADF; /* ADF is installed */
+ SANE_Bool duplex; /* does the ADF handle duplex scanning */
+ SANE_Bool focusSupport; /* does this scanner have support for "set focus position" ? */
+ SANE_Bool color_shuffle; /* does this scanner need color shuffling */
+
+ SANE_Int maxDepth; /* max. color depth */
+ SANE_Word *depth_list;
+
+ SANE_Int optical_res; /* optical resolution */
+ SANE_Int max_line_distance;
+
+ SANE_Bool need_double_vertical;
+ SANE_Bool need_color_reorder;
+ SANE_Bool need_reset_on_source_change;
+
+ SANE_Bool wait_for_button; /* do we have to wait until the scanner button is pressed? */
+
+ SANE_Bool extended_commands;
+
+ struct EpsonCmd *cmd;
+ const struct epson_profile *cct_profile;
+};
+
+typedef struct Epson_Device Epson_Device;
+
+/* an instance of a scanner */
+
+struct Epson_Scanner
+{
+ struct Epson_Scanner *next;
+ struct Epson_Device *hw;
+
+ int fd;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+
+ SANE_Bool block;
+ SANE_Bool eof;
+ SANE_Byte *buf, *end, *ptr;
+ SANE_Bool canceling;
+ SANE_Word gamma_table[3][256];
+ SANE_Word cct_table[9];
+ SANE_Int retry_count;
+
+ /* buffer lines for color shuffling */
+ SANE_Byte *line_buffer[LINES_SHUFFLE_MAX];
+ SANE_Int color_shuffle_line; /* current line number for color shuffling */
+ SANE_Int line_distance; /* current line distance */
+ SANE_Int current_output_line; /* line counter when color shuffling */
+ SANE_Int lines_written; /* debug variable */
+
+ SANE_Int left, top, lcount;
+ SANE_Bool focusOnGlass;
+ SANE_Byte currentFocusPosition;
+
+ /* network buffers */
+ unsigned char *netbuf, *netptr;
+ size_t netlen;
+
+ /* extended image data handshaking */
+ SANE_Int ext_block_len;
+ SANE_Int ext_last_len;
+ SANE_Int ext_blocks;
+ SANE_Int ext_counter;
+};
+
+typedef struct Epson_Scanner Epson_Scanner;
+
+struct mode_param
+{
+ int color;
+ int flags;
+ int dropout_mask;
+ int depth;
+};
+
+enum {
+ MODE_BINARY, MODE_GRAY, MODE_COLOR, MODE_INFRARED
+};
+
+#endif
diff --git a/backend/epson2_net.c b/backend/epson2_net.c
new file mode 100644
index 0000000..c7b4873
--- /dev/null
+++ b/backend/epson2_net.c
@@ -0,0 +1,229 @@
+/*
+ * epson2_net.c - SANE library for Epson scanners.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sane/config.h"
+
+#include "sane/sane.h"
+#include "sane/saneopts.h"
+#include "sane/sanei_tcp.h"
+#include "sane/sanei_config.h"
+#include "sane/sanei_backend.h"
+
+#include "epson2.h"
+#include "epson2_net.h"
+
+#include "byteorder.h"
+
+#include "sane/sanei_debug.h"
+
+int
+sanei_epson_net_read_raw(Epson_Scanner *s, unsigned char *buf, size_t wanted,
+ SANE_Status * status)
+{
+ size_t size, read = 0;
+
+ *status = SANE_STATUS_GOOD;
+
+ while (read < wanted) {
+ size = sanei_tcp_read(s->fd, buf + read, wanted - read);
+
+ if (size == 0)
+ break;
+
+ read += size;
+ }
+
+ if (read < wanted)
+ *status = SANE_STATUS_IO_ERROR;
+
+ return read;
+}
+
+int
+sanei_epson_net_read(Epson_Scanner *s, unsigned char *buf, ssize_t wanted,
+ SANE_Status * status)
+{
+ ssize_t size;
+ ssize_t read = 0;
+ unsigned char header[12];
+
+ /* read from buffer, if available */
+ if (s->netptr != s->netbuf) {
+ DBG(23, "reading %lu from buffer at %p, %lu available\n",
+ (u_long) wanted, s->netptr, (u_long) s->netlen);
+
+ memcpy(buf, s->netptr, wanted);
+ read = wanted;
+
+ s->netlen -= wanted;
+
+ if (s->netlen == 0) {
+ DBG(23, "%s: freeing %p\n", __func__, s->netbuf);
+ free(s->netbuf);
+ s->netbuf = s->netptr = NULL;
+ s->netlen = 0;
+ }
+
+ return read;
+ }
+
+ /* receive net header */
+ size = sanei_tcp_read(s->fd, header, 12);
+ if (size != 12) {
+ *status = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+
+ if (header[0] != 'I' || header[1] != 'S') {
+ DBG(1, "header mismatch: %02X %02x\n", header[0], header[1]);
+ *status = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+
+ size = be32atoh(&header[6]);
+
+ DBG(23, "%s: wanted = %lu, available = %lu\n", __FUNCTION__,
+ (u_long) wanted, (u_long) size);
+
+ *status = SANE_STATUS_GOOD;
+
+ if (size == wanted) {
+
+ DBG(15, "%s: full read\n", __func__);
+ read = sanei_tcp_read(s->fd, buf, size);
+
+ if (s->netbuf) {
+ free(s->netbuf);
+ s->netbuf = NULL;
+ s->netlen = 0;
+ }
+
+ if (read < 0) {
+ *status = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+
+/* } else if (wanted < size && s->netlen == size) { */
+ } else {
+ DBG(23, "%s: partial read\n", __func__);
+
+ read = sanei_tcp_read(s->fd, s->netbuf, size);
+ if (read != size) {
+ *status = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+
+ s->netlen = size - wanted;
+ s->netptr += wanted;
+ read = wanted;
+
+ DBG(23, "0,4 %02x %02x\n", s->netbuf[0], s->netbuf[4]);
+ DBG(23, "storing %lu to buffer at %p, next read at %p, %lu bytes left\n",
+ (u_long) size, s->netbuf, s->netptr, (u_long) s->netlen);
+
+ memcpy(buf, s->netbuf, wanted);
+ }
+
+ return read;
+}
+
+
+int
+sanei_epson_net_write(Epson_Scanner *s, unsigned int cmd, const unsigned char *buf,
+ size_t buf_size, size_t reply_len, SANE_Status *status)
+{
+ unsigned char *h1, *h2, *payload;
+ unsigned char *packet = malloc(12 + 8 + buf_size);
+
+ /* XXX check allocation failure */
+
+ h1 = packet;
+ h2 = packet + 12;
+ payload = packet + 12 + 8;
+
+ if (reply_len) {
+ s->netbuf = s->netptr = malloc(reply_len);
+ s->netlen = reply_len;
+ DBG(24, "allocated %lu bytes at %p\n",
+ (u_long) reply_len, s->netbuf);
+ }
+
+ DBG(24, "%s: cmd = %04x, buf = %p, buf_size = %lu, reply_len = %lu\n",
+ __FUNCTION__, cmd, buf, (u_long) buf_size, (u_long) reply_len);
+
+ memset(h1, 0x00, 12);
+ memset(h2, 0x00, 8);
+
+ h1[0] = 'I';
+ h1[1] = 'S';
+
+ h1[2] = cmd >> 8;
+ h1[3] = cmd;
+
+ h1[4] = 0x00;
+ h1[5] = 0x0C; /* Don't know what's that */
+
+ DBG(24, "H1[0]: %02x %02x %02x %02x\n", h1[0], h1[1], h1[2], h1[3]);
+
+ if((cmd >> 8) == 0x20) {
+ htobe32a(&h1[6], buf_size + 8);
+
+ htobe32a(&h2[0], buf_size);
+ htobe32a(&h2[4], reply_len);
+
+ DBG(24, "H1[6]: %02x %02x %02x %02x (%lu)\n", h1[6], h1[7], h1[8], h1[9], (u_long) (buf_size + 8));
+ DBG(24, "H2[0]: %02x %02x %02x %02x (%lu)\n", h2[0], h2[1], h2[2], h2[3], (u_long) buf_size);
+ DBG(24, "H2[4]: %02x %02x %02x %02x (%lu)\n", h2[4], h2[5], h2[6], h2[7], (u_long) reply_len);
+ }
+
+ if ((cmd >> 8) == 0x20 && (buf_size || reply_len)) {
+ if (buf_size)
+ memcpy(payload, buf, buf_size);
+
+ sanei_tcp_write(s->fd, packet, 12 + 8 + buf_size);
+ }
+ else
+ sanei_tcp_write(s->fd, packet, 12);
+
+ free(packet);
+
+ *status = SANE_STATUS_GOOD;
+ return buf_size;
+}
+
+SANE_Status
+sanei_epson_net_lock(struct Epson_Scanner *s)
+{
+ SANE_Status status;
+ unsigned char buf[1];
+
+ DBG(1, "%s\n", __func__);
+
+ sanei_epson_net_write(s, 0x2100, NULL, 0, 0, &status);
+ sanei_epson_net_read(s, buf, 1, &status);
+ return status;
+}
+
+SANE_Status
+sanei_epson_net_unlock(struct Epson_Scanner *s)
+{
+ SANE_Status status;
+
+ DBG(1, "%s\n", __func__);
+
+ sanei_epson_net_write(s, 0x2101, NULL, 0, 0, &status);
+/* sanei_epson_net_read(s, buf, 1, &status); */
+ return status;
+}
diff --git a/backend/epson2_net.h b/backend/epson2_net.h
new file mode 100644
index 0000000..6a6e8d8
--- /dev/null
+++ b/backend/epson2_net.h
@@ -0,0 +1,18 @@
+#ifndef _EPSON2_NET_H_
+#define _EPSON2_NET_H_
+
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+
+extern int sanei_epson_net_read(struct Epson_Scanner *s, unsigned char *buf, ssize_t buf_size,
+ SANE_Status *status);
+extern int sanei_epson_net_write(struct Epson_Scanner *s, unsigned int cmd, const unsigned char *buf,
+ size_t buf_size, size_t reply_len,
+ SANE_Status *status);
+extern int
+sanei_epson_net_read_raw(Epson_Scanner *s, unsigned char *buf, size_t wanted,
+ SANE_Status * status);
+extern SANE_Status sanei_epson_net_lock(struct Epson_Scanner *s);
+extern SANE_Status sanei_epson_net_unlock(struct Epson_Scanner *s);
+
+#endif
diff --git a/backend/epson2_scsi.c b/backend/epson2_scsi.c
new file mode 100644
index 0000000..89394cd
--- /dev/null
+++ b/backend/epson2_scsi.c
@@ -0,0 +1,103 @@
+#undef BACKEND_NAME
+#define BACKEND_NAME epson2_scsi
+
+#include "../include/sane/config.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_scsi.h"
+#include "epson2_scsi.h"
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h> /* for memset and memcpy */
+#include <stdio.h>
+
+/* sense handler for the sanei_scsi_xxx comands */
+SANE_Status
+sanei_epson2_scsi_sense_handler(int scsi_fd,
+ unsigned char *result, void *arg)
+{
+ /* to get rid of warnings */
+ scsi_fd = scsi_fd;
+ arg = arg;
+
+ if (result[0] && result[0] != 0x70) {
+ DBG(2, "%s: sense code = 0x%02x\n",
+ __FUNCTION__, result[0]);
+ return SANE_STATUS_IO_ERROR;
+ } else {
+ return SANE_STATUS_GOOD;
+ }
+}
+
+SANE_Status
+sanei_epson2_scsi_inquiry(int fd, void *buf, size_t *buf_size)
+{
+ unsigned char cmd[6];
+ int status;
+
+ memset(cmd, 0, 6);
+ cmd[0] = INQUIRY_COMMAND;
+ cmd[4] = *buf_size > 255 ? 255 : *buf_size;
+ status = sanei_scsi_cmd(fd, cmd, sizeof cmd, buf, buf_size);
+
+ return status;
+}
+
+SANE_Status
+sanei_epson2_scsi_test_unit_ready(int fd)
+{
+ unsigned char cmd[6];
+
+ memset(cmd, 0, 6);
+ cmd[0] = TEST_UNIT_READY_COMMAND;
+
+ return sanei_scsi_cmd2(fd, cmd, sizeof(cmd), NULL, 0, NULL, NULL);
+}
+
+int
+sanei_epson2_scsi_read(int fd, void *buf, size_t buf_size,
+ SANE_Status *status)
+{
+ unsigned char cmd[6];
+
+ memset(cmd, 0, 6);
+ cmd[0] = READ_6_COMMAND;
+ cmd[2] = buf_size >> 16;
+ cmd[3] = buf_size >> 8;
+ cmd[4] = buf_size;
+
+ *status = sanei_scsi_cmd2(fd, cmd, sizeof(cmd), NULL, 0, buf, &buf_size);
+ if (*status == SANE_STATUS_GOOD)
+ return buf_size;
+
+ return 0;
+}
+
+int
+sanei_epson2_scsi_write(int fd, const void *buf, size_t buf_size,
+ SANE_Status *status)
+{
+ unsigned char cmd[6];
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = WRITE_6_COMMAND;
+ cmd[2] = buf_size >> 16;
+ cmd[3] = buf_size >> 8;
+ cmd[4] = buf_size;
+
+ *status = sanei_scsi_cmd2(fd, cmd, sizeof(cmd), buf, buf_size, NULL, NULL);
+ if (*status == SANE_STATUS_GOOD)
+ return buf_size;
+
+ return 0;
+}
diff --git a/backend/epson2_scsi.h b/backend/epson2_scsi.h
new file mode 100644
index 0000000..7a85b7c
--- /dev/null
+++ b/backend/epson2_scsi.h
@@ -0,0 +1,24 @@
+#ifndef _EPSON2_SCSI_H_
+#define _EPSON2_SCSI_H_
+
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+
+#define TEST_UNIT_READY_COMMAND (0x00)
+#define READ_6_COMMAND (0x08)
+#define WRITE_6_COMMAND (0x0a)
+#define INQUIRY_COMMAND (0x12)
+#define TYPE_PROCESSOR (0x03)
+
+#define INQUIRY_BUF_SIZE (36)
+
+SANE_Status sanei_epson2_scsi_sense_handler(int scsi_fd, unsigned char *result,
+ void *arg);
+SANE_Status sanei_epson2_scsi_inquiry(int fd, void *buf,
+ size_t *buf_size);
+int sanei_epson2_scsi_read(int fd, void *buf, size_t buf_size,
+ SANE_Status *status);
+int sanei_epson2_scsi_write(int fd, const void *buf, size_t buf_size,
+ SANE_Status *status);
+SANE_Status sanei_epson2_scsi_test_unit_ready(int fd);
+#endif
diff --git a/backend/epson_scsi.c b/backend/epson_scsi.c
new file mode 100644
index 0000000..698ec61
--- /dev/null
+++ b/backend/epson_scsi.c
@@ -0,0 +1,114 @@
+#ifdef _AIX
+#include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+#undef BACKEND_NAME
+#define BACKEND_NAME epson_scsi
+#include "../include/sane/config.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_scsi.h"
+#include "epson_scsi.h"
+
+#include "../include/lalloca.h"
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h> /* for memset and memcpy */
+#include <stdio.h>
+
+/*
+ * sense handler for the sanei_scsi_XXX comands
+ */
+SANE_Status
+sanei_epson_scsi_sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ /* to get rid of warnings */
+ scsi_fd = scsi_fd;
+ arg = arg;
+
+ if (result[0] && result[0] != 0x70)
+ {
+ DBG (2, "sense_handler() : sense code = 0x%02x\n", result[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+}
+
+/*
+ *
+ *
+ */
+SANE_Status
+sanei_epson_scsi_inquiry (int fd, int page_code, void *buf, size_t * buf_size)
+{
+ u_char cmd[6];
+ int status;
+
+ memset (cmd, 0, 6);
+ cmd[0] = INQUIRY_COMMAND;
+ cmd[2] = page_code;
+ cmd[4] = *buf_size > 255 ? 255 : *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof cmd, buf, buf_size);
+
+ return status;
+}
+
+/*
+ *
+ *
+ */
+int
+sanei_epson_scsi_read (int fd, void *buf, size_t buf_size,
+ SANE_Status * status)
+{
+ u_char cmd[6];
+
+ memset (cmd, 0, 6);
+ cmd[0] = READ_6_COMMAND;
+ cmd[2] = buf_size >> 16;
+ cmd[3] = buf_size >> 8;
+ cmd[4] = buf_size;
+
+ if (SANE_STATUS_GOOD ==
+ (*status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, &buf_size)))
+ return buf_size;
+
+ return 0;
+}
+
+/*
+ *
+ *
+ */
+int
+sanei_epson_scsi_write (int fd, const void *buf, size_t buf_size,
+ SANE_Status * status)
+{
+ u_char *cmd;
+
+ cmd = alloca (8 + buf_size);
+ memset (cmd, 0, 8);
+ cmd[0] = WRITE_6_COMMAND;
+ cmd[2] = buf_size >> 16;
+ cmd[3] = buf_size >> 8;
+ cmd[4] = buf_size;
+ memcpy (cmd + 8, buf, buf_size);
+
+ if (SANE_STATUS_GOOD ==
+ (*status = sanei_scsi_cmd2 (fd, cmd, 6, cmd + 8, buf_size, NULL, NULL)))
+ return buf_size;
+
+ return 0;
+}
diff --git a/backend/epson_scsi.h b/backend/epson_scsi.h
new file mode 100644
index 0000000..5033014
--- /dev/null
+++ b/backend/epson_scsi.h
@@ -0,0 +1,24 @@
+#ifndef _EPSON_SCSI_H_
+#define _EPSON_SCSI_H_
+
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+
+#define TEST_UNIT_READY_COMMAND (0x00)
+#define READ_6_COMMAND (0x08)
+#define WRITE_6_COMMAND (0x0a)
+#define INQUIRY_COMMAND (0x12)
+#define TYPE_PROCESSOR (0x03)
+
+#define INQUIRY_BUF_SIZE (36)
+
+SANE_Status sanei_epson_scsi_sense_handler (int scsi_fd, u_char * result,
+ void *arg);
+SANE_Status sanei_epson_scsi_inquiry (int fd, int page_code, void *buf,
+ size_t * buf_size);
+int sanei_epson_scsi_read (int fd, void *buf, size_t buf_size,
+ SANE_Status * status);
+int sanei_epson_scsi_write (int fd, const void *buf, size_t buf_size,
+ SANE_Status * status);
+
+#endif
diff --git a/backend/epson_usb.c b/backend/epson_usb.c
new file mode 100644
index 0000000..848ef63
--- /dev/null
+++ b/backend/epson_usb.c
@@ -0,0 +1,93 @@
+#include <sys/types.h>
+#include "../include/sane/sanei_usb.h"
+#include "epson_usb.h"
+
+/* generated with epson2usb.pl doc/descriptions/epson2.desc */
+
+SANE_Word sanei_epson_usb_product_ids[] = {
+ 0x101, /* GT-7000U, Perfection 636U */
+ 0x103, /* GT-6600U, Perfection 610 */
+ 0x104, /* GT-7600U, GT-7600UF, Perfection 1200U, Perfection 1200U PHOTO */
+ 0x105, /* Stylus Scan 2000 */
+ 0x106, /* Stylus Scan 2500 */
+ 0x107, /* ES-2000, Expression 1600 */
+ 0x109, /* ES-8500, Expression 1640XL */
+ 0x10a, /* GT-8700, GT-8700F, Perfection 1640SU, Perfection 1640SU PHOTO */
+ 0x10b, /* GT-7700U, Perfection 1240U */
+ 0x10c, /* GT-6700U, Perfection 640U */
+ 0x10e, /* ES-2200, Expression 1680 */
+ 0x110, /* GT-8200U, GT-8200UF, Perfection 1650, Perfection 1650 PHOTO */
+ 0x112, /* GT-9700F, Perfection 2450 PHOTO */
+ 0x11b, /* GT-9300UF, Perfection 2400 PHOTO */
+ 0x11c, /* GT-9800F, Perfection 3200 PHOTO */
+ 0x11e, /* GT-8300UF, Perfection 1660 PHOTO */
+ 0x126, /* ES-7000H, GT-15000 */
+ 0x128, /* GT-X700, Perfection 4870 PHOTO */
+ 0x129, /* ES-10000G, Expression 10000XL */
+ 0x12a, /* GT-X800, Perfection 4990 PHOTO */
+ 0x12b, /* ES-H300, GT-2500 */
+ 0x12c, /* GT-X900, Perfection V700 Photo, Perfection V750 Photo */
+ 0x135, /* GT-X970 */
+ 0x801, /* CC-600PX, Stylus CX5100, Stylus CX5200 */
+ 0x802, /* CC-570L, Stylus CX3100, Stylus CX3200 */
+ 0x805, /* Stylus CX6300, Stylus CX6400 */
+ 0x806, /* PM-A850, Stylus Photo RX600 */
+ 0x807, /* Stylus Photo RX500, Stylus Photo RX510 */
+ 0x808, /* Stylus CX5300, Stylus CX5400 */
+ 0x80d, /* Stylus CX4500, Stylus CX4600 */
+ 0x80e, /* PX-A550, Stylus CX3500, Stylus CX3600, Stylus CX3650 */
+ 0x80f, /* Stylus Photo RX420, Stylus Photo RX425, Stylus Photo RX430 */
+ 0x810, /* PM-A900, Stylus Photo RX700 */
+ 0x811, /* PM-A870, Stylus Photo RX620, Stylus Photo RX630 */
+ 0x813, /* Stylus CX6500, Stylus CX6600 */
+ 0x814, /* PM-A700 */
+ 0x815, /* AcuLaser CX11, AcuLaser CX11NF, LP-A500 */
+ 0x817, /* LP-M5500, LP-M5500F */
+ 0x818, /* Stylus CX3700, Stylus CX3800, Stylus DX3800 */
+ 0x819, /* PX-A650, Stylus CX4700, Stylus CX4800, Stylus DX4800, Stylus DX4850 */
+ 0x81a, /* PM-A750, Stylus Photo RX520, Stylus Photo RX530 */
+ 0x81c, /* PM-A890, Stylus Photo RX640, Stylus Photo RX650 */
+ 0x81d, /* PM-A950 */
+ 0x81f, /* Stylus CX7700, Stylus CX7800 */
+ 0x820, /* Stylus CX4100, Stylus CX4200, Stylus DX4200 */
+ 0x827, /* PM-A820, Stylus Photo RX560, Stylus Photo RX580, Stylus Photo RX590 */
+ 0x828, /* PM-A970 */
+ 0x829, /* PM-T990 */
+ 0x82a, /* PM-A920 */
+ 0x82b, /* Stylus CX4900, Stylus CX5000, Stylus DX5000 */
+ 0x82e, /* PX-A720, Stylus CX5900, Stylus CX6000, Stylus DX6000 */
+ 0x82f, /* PX-A620, Stylus CX3900, Stylus DX4000 */
+ 0x830, /* ME 200, Stylus CX2800, Stylus CX2900 */
+ 0x833, /* LP-M5600 */
+ 0x834, /* LP-M6000 */
+ 0x835, /* AcuLaser CX21 */
+ 0x836, /* PM-T960 */
+ 0x837, /* PM-A940, Stylus Photo RX680, Stylus Photo RX685, Stylus Photo RX690 */
+ 0x838, /* PX-A640, Stylus CX7300, Stylus CX7400, Stylus DX7400 */
+ 0x839, /* PX-A740, Stylus CX8300, Stylus CX8400, Stylus DX8400 */
+ 0x83a, /* PX-FA700, Stylus CX9300F, Stylus CX9400Fax, Stylus DX9400F */
+ 0x83c, /* PM-A840, PM-A840S, Stylus Photo RX585, Stylus Photo RX595, Stylus Photo RX610 */
+ 0x841, /* ME 300, PX-401A, Stylus NX100, Stylus SX100, Stylus TX100 */
+ 0x843, /* LP-M5000 */
+ 0x844, /* Artisan 800, EP-901A, EP-901F, Stylus Photo PX800FW, Stylus Photo TX800FW */
+ 0x846, /* Artisan 700, EP-801A, Stylus Photo PX700W, Stylus Photo TX700W */
+ 0x847, /* ME Office 700FW, PX-601F, Stylus Office BX600FW, Stylus Office TX600FW, Stylus SX600FW, WorkForce 600 */
+ 0x848, /* ME Office 600F, Stylus Office BX300F, Stylus Office TX300F, Stylus NX300 */
+ 0x849, /* Stylus NX200, Stylus SX200, Stylus SX205, Stylus TX200, Stylus TX203, Stylus TX209 */
+ 0x84a, /* PX-501A, Stylus NX400, Stylus SX400, Stylus SX405, Stylus TX400 */
+ 0x84c, /* WorkForce 500 */
+ 0x84d, /* PX-402A, Stylus NX110 Series, Stylus SX110 Series, Stylus TX110 Series */
+ 0x84f, /* ME OFFICE 510, Stylus NX210 Series, Stylus SX210 Series, Stylus TX210 Series */
+ 0x851, /* Stylus NX410 Series, Stylus SX410 Series, Stylus TX410 Series */
+ 0x854, /* ME OFFICE 650FN Series, Stylus Office BX310FN Series, Stylus Office TX510FN Series, WorkForce 310 Series */
+ 0x856, /* PX-502A, Stylus NX510 Series, Stylus SX510W Series, Stylus TX550W Series */
+ 0x85c, /* Stylus SX125 */
+ 0 /* last entry - this is used for devices that are specified
+ in the config file as "usb <vendor> <product>" */
+};
+
+int
+sanei_epson_getNumberOfUSBProductIds (void)
+{
+ return sizeof (sanei_epson_usb_product_ids) / sizeof (SANE_Word);
+}
diff --git a/backend/epson_usb.h b/backend/epson_usb.h
new file mode 100644
index 0000000..1d9ff75
--- /dev/null
+++ b/backend/epson_usb.h
@@ -0,0 +1,10 @@
+#ifndef _EPSON_USB_H_
+#define _EPSON_USB_H_
+
+#define SANE_EPSON_VENDOR_ID (0x4b8)
+
+extern SANE_Word sanei_epson_usb_product_ids[];
+
+extern int sanei_epson_getNumberOfUSBProductIds (void);
+
+#endif
diff --git a/backend/fujitsu-scsi.h b/backend/fujitsu-scsi.h
new file mode 100644
index 0000000..52ac7c6
--- /dev/null
+++ b/backend/fujitsu-scsi.h
@@ -0,0 +1,1132 @@
+#ifndef FUJITSU_SCSI_H
+#define FUJITSU_SCSI_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ *
+ * Please see to opening comments in fujitsu.c
+ */
+
+/****************************************************/
+
+#define USB_COMMAND_CODE 0x43
+#define USB_COMMAND_LEN 0x1F
+#define USB_COMMAND_OFFSET 0x13
+#define USB_COMMAND_TIME 30000
+#define USB_DATA_TIME 30000
+#define USB_STATUS_CODE 0x53
+#define USB_STATUS_LEN 0x0D
+#define USB_STATUS_OFFSET 0x09
+#define USB_STATUS_TIME 30000
+
+/*static inline void */
+static void
+setbitfield (unsigned char *pageaddr, int mask, int shift, int val)
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*static inline int */
+static int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int
+getnbyte (unsigned char *pnt, int nbytes)
+{
+ unsigned int result = 0;
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = 0; i < nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*static inline void */
+static void
+putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes)
+{
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = nbytes - 1; i >= 0; i--)
+
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+/* ==================================================================== */
+/* SCSI commands */
+
+#define set_SCSI_opcode(out, val) out[0]=val
+#define set_SCSI_lun(out, val) setbitfield(out + 1, 7, 5, val)
+
+/* ==================================================================== */
+/* TEST_UNIT_READY */
+#define TEST_UNIT_READY_code 0x00
+#define TEST_UNIT_READY_len 6
+
+/* ==================================================================== */
+/* REQUEST_SENSE */
+#define REQUEST_SENSE_code 0x03
+#define REQUEST_SENSE_len 6
+
+#define RS_return_size 0x12
+#define set_RS_return_size(icb,val) icb[0x04]=val
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */
+#define get_RS_additional_length(b) b[0x07] /* always 10 */
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid=0 */
+#define get_RS_SKSB(b) getnbyte(b+0x0f, 3)
+
+/* when RS is 0x05/0x26 bad bytes listed in sksb */
+#define get_RS_offending_byte(b) getnbyte(b+0x10, 2)
+
+/* 3091 and 3092 use RS instead of ghs. RS must be 0x00/0x80 */
+/* in ascq */
+#define get_RS_adf_open(in) getbitfield(in+0x0d, 1, 7)
+#define get_RS_send_sw(in) getbitfield(in+0x0d, 1, 5)
+#define get_RS_scan_sw(in) getbitfield(in+0x0d, 1, 4)
+#define get_RS_duplex_sw(in) getbitfield(in+0x0d, 1, 2)
+#define get_RS_top(in) getbitfield(in+0x0d, 1, 1)
+#define get_RS_hopper(in) getbitfield(in+0x0d, 1, 0)
+
+/* in sksb */
+#define get_RS_function(in) getbitfield(in+0x0f, 0x0f, 3)
+#define get_RS_density(in) getbitfield(in+0x0f, 0x07, 0)
+
+/* ==================================================================== */
+/* INQUIRY */
+#define INQUIRY_code 0x12
+#define INQUIRY_len 6
+
+#define INQUIRY_std_len 96
+#define INQUIRY_vpd_len 204 /* unlikely maximum value */
+
+#define set_IN_evpd(icb, val) setbitfield(icb + 1, 1, 0, val)
+#define set_IN_page_code(icb, val) icb[0x02]=val
+#define set_IN_return_size(icb,val) icb[0x04]=val
+#define set_IN_length(out,n) out[0x04]=n-5
+
+#define get_IN_periph_qual(in) getbitfield(in, 0x07, 5)
+#define IN_periph_qual_lun 0x00
+#define IN_periph_qual_nolun 0x03
+#define get_IN_periph_devtype(in) getbitfield(in, 0x1f, 0)
+#define IN_periph_devtype_scanner 0x06
+#define IN_periph_devtype_unknown 0x1f
+#define get_IN_response_format(in) getbitfield(in + 0x03, 0x07, 0)
+#define IN_recognized 0x02
+#define get_IN_vendor(in, buf) strncpy(buf, (char *)in + 0x08, 0x08)
+#define get_IN_product(in, buf) strncpy(buf, (char *)in + 0x10, 0x010)
+#define get_IN_version(in, buf) strncpy(buf, (char *)in + 0x20, 0x04)
+#define get_IN_color_offset(in) getnbyte (in+0x2A, 2) /* offset between colors */
+
+/* these only in some scanners */
+#define get_IN_long_gray(in) getbitfield(in+0x2C, 1, 1)
+#define get_IN_long_color(in) getbitfield(in+0x2C, 1, 0)
+
+#define get_IN_emulation(in) getbitfield(in+0x2D, 1, 6)
+#define get_IN_cmp_cga(in) getbitfield(in+0x2D, 1, 5)
+#define get_IN_bg_back(in) getbitfield(in+0x2D, 1, 3)
+#define get_IN_bg_front(in) getbitfield(in+0x2D, 1, 2)
+#define get_IN_bg_fb(in) getbitfield(in+0x2D, 1, 1)
+#define get_IN_has_back(in) getbitfield(in+0x2D, 1, 0)
+
+#define get_IN_duplex_offset(in) getnbyte (in+0x2E, 2)
+
+/* the VPD response */
+#define get_IN_page_length(in) in[0x04]
+#define get_IN_basic_x_res(in) getnbyte(in + 0x05, 2)
+#define get_IN_basic_y_res(in) getnbyte(in + 0x07, 2)
+#define get_IN_step_x_res(in) getbitfield(in+0x09, 1, 0)
+#define get_IN_step_y_res(in) getbitfield(in+0x09, 1, 4)
+#define get_IN_max_x_res(in) getnbyte(in + 0x0a, 2)
+#define get_IN_max_y_res(in) getnbyte(in + 0x0c, 2)
+#define get_IN_min_x_res(in) getnbyte(in + 0x0e, 2)
+#define get_IN_min_y_res(in) getnbyte(in + 0x10, 2)
+#define get_IN_std_res_200(in) getbitfield(in+ 0x12, 1, 0)
+#define get_IN_std_res_180(in) getbitfield(in+ 0x12, 1, 1)
+#define get_IN_std_res_160(in) getbitfield(in+ 0x12, 1, 2)
+#define get_IN_std_res_150(in) getbitfield(in+ 0x12, 1, 3)
+#define get_IN_std_res_120(in) getbitfield(in+ 0x12, 1, 4)
+#define get_IN_std_res_100(in) getbitfield(in+ 0x12, 1, 5)
+#define get_IN_std_res_75(in) getbitfield(in+ 0x12, 1, 6)
+#define get_IN_std_res_60(in) getbitfield(in+ 0x12, 1, 7)
+#define get_IN_std_res_1200(in) getbitfield(in+ 0x13, 1, 0)
+#define get_IN_std_res_800(in) getbitfield(in+ 0x13, 1, 1)
+#define get_IN_std_res_600(in) getbitfield(in+ 0x13, 1, 2)
+#define get_IN_std_res_480(in) getbitfield(in+ 0x13, 1, 3)
+#define get_IN_std_res_400(in) getbitfield(in+ 0x13, 1, 4)
+#define get_IN_std_res_320(in) getbitfield(in+ 0x13, 1, 5)
+#define get_IN_std_res_300(in) getbitfield(in+ 0x13, 1, 6)
+#define get_IN_std_res_240(in) getbitfield(in+ 0x13, 1, 7)
+#define get_IN_window_width(in) getnbyte(in + 0x14, 4)
+#define get_IN_window_length(in) getnbyte(in + 0x18, 4)
+#define get_IN_overflow(in) getbitfield(in+0x1c, 1, 0)
+#define get_IN_monochrome(in) getbitfield(in+0x1c, 1, 1)
+#define get_IN_half_tone(in) getbitfield(in+0x1c, 1, 2)
+#define get_IN_multilevel(in) getbitfield(in+0x1c, 1, 3)
+#define get_IN_monochrome_rgb(in) getbitfield(in+0x1c, 1, 5)
+#define get_IN_half_tone_rgb(in) getbitfield(in+0x1c, 1, 6)
+#define get_IN_multilevel_rgb(in) getbitfield(in+0x1c, 1, 7)
+
+/* vendor unique section */
+#define get_IN_adf(in) getbitfield(in+0x20, 1, 7)
+#define get_IN_flatbed(in) getbitfield(in+0x20, 1, 6)
+#define get_IN_transparency(in) getbitfield(in+0x20, 1, 5)
+#define get_IN_duplex(in) getbitfield(in+0x20, 1, 4)
+#define get_IN_endorser_b(in) getbitfield(in+0x20, 1, 3)
+#define get_IN_barcode(in) getbitfield(in+0x20, 1, 2)
+#define get_IN_operator_panel(in) getbitfield(in+0x20, 1, 1)
+#define get_IN_endorser_f(in) getbitfield(in+0x20, 1, 0)
+
+#define get_IN_mp_stacker(in) getbitfield(in+0x21, 1, 7)
+#define get_IN_prepick(in) getbitfield(in+0x21, 1, 6)
+#define get_IN_mf_detect(in) getbitfield(in+0x21, 1, 5)
+#define get_IN_paperprot(in) getbitfield(in+0x21, 1, 4)
+#define get_IN_adbits(in) getbitfield(in+0x21, 0x0f, 0)
+
+#define get_IN_buffer_bytes(in) getnbyte(in + 0x22, 4)
+
+/*supported scsi commands*/
+#define get_IN_has_cmd_msen10(in) getbitfield(in+0x26, 1, 1)
+#define get_IN_has_cmd_msel10(in) getbitfield(in+0x26, 1, 0)
+
+#define get_IN_has_cmd_lsen(in) getbitfield(in+0x27, 1, 7)
+#define get_IN_has_cmd_lsel(in) getbitfield(in+0x27, 1, 6)
+#define get_IN_has_cmd_change(in) getbitfield(in+0x27, 1, 5)
+#define get_IN_has_cmd_rbuff(in) getbitfield(in+0x27, 1, 4)
+#define get_IN_has_cmd_wbuff(in) getbitfield(in+0x27, 1, 3)
+#define get_IN_has_cmd_cav(in) getbitfield(in+0x27, 1, 2)
+#define get_IN_has_cmd_comp(in) getbitfield(in+0x27, 1, 1)
+#define get_IN_has_cmd_gdbs(in) getbitfield(in+0x27, 1, 0)
+
+#define get_IN_has_cmd_op(in) getbitfield(in+0x28, 1, 7)
+#define get_IN_has_cmd_send(in) getbitfield(in+0x28, 1, 6)
+#define get_IN_has_cmd_read(in) getbitfield(in+0x28, 1, 5)
+#define get_IN_has_cmd_gwin(in) getbitfield(in+0x28, 1, 4)
+#define get_IN_has_cmd_swin(in) getbitfield(in+0x28, 1, 3)
+#define get_IN_has_cmd_sdiag(in) getbitfield(in+0x28, 1, 2)
+#define get_IN_has_cmd_rdiag(in) getbitfield(in+0x28, 1, 1)
+#define get_IN_has_cmd_scan(in) getbitfield(in+0x28, 1, 0)
+
+#define get_IN_has_cmd_msen6(in) getbitfield(in+0x29, 1, 7)
+#define get_IN_has_cmd_copy(in) getbitfield(in+0x29, 1, 6)
+#define get_IN_has_cmd_rel(in) getbitfield(in+0x29, 1, 5)
+#define get_IN_has_cmd_runit(in) getbitfield(in+0x29, 1, 4)
+#define get_IN_has_cmd_msel6(in) getbitfield(in+0x29, 1, 3)
+#define get_IN_has_cmd_inq(in) getbitfield(in+0x29, 1, 2)
+#define get_IN_has_cmd_rs(in) getbitfield(in+0x29, 1, 1)
+#define get_IN_has_cmd_tur(in) getbitfield(in+0x29, 1, 0)
+
+/* more stuff here? (vendor commands) */
+#define get_IN_has_cmd_subwindow(in) getbitfield(in+0x2b, 1, 0)
+#define get_IN_has_cmd_endorser(in) getbitfield(in+0x2b, 1, 1)
+#define get_IN_has_cmd_hw_status(in) getbitfield(in+0x2b, 1, 2)
+#define get_IN_has_cmd_hw_status_2(in) getbitfield(in+0x2b, 1, 3)
+#define get_IN_has_cmd_hw_status_3(in) getbitfield(in+0x2b, 1, 4)
+#define get_IN_has_cmd_scanner_ctl(in) getbitfield(in+0x31, 1, 1)
+#define get_IN_has_cmd_device_restart(in) getbitfield(in+0x31, 1, 2)
+
+#define get_IN_brightness_steps(in) getnbyte(in+0x52, 1)
+#define get_IN_threshold_steps(in) getnbyte(in+0x53, 1)
+#define get_IN_contrast_steps(in) getnbyte(in+0x54, 1)
+
+#define get_IN_num_dither_internal(in) getbitfield(in+0x56, 15, 4)
+#define get_IN_num_dither_download(in) getbitfield(in+0x56, 15, 0)
+
+#define get_IN_num_gamma_internal(in) getbitfield(in+0x57, 15, 4)
+#define get_IN_num_gamma_download(in) getbitfield(in+0x57, 15, 0)
+
+#define get_IN_ipc_bw_rif(in) getbitfield(in+0x58, 1, 7)
+#define get_IN_ipc_dtc(in) getbitfield(in+0x58, 1, 6)
+#define get_IN_ipc_sdtc(in) getbitfield(in+0x58, 1, 5)
+#define get_IN_ipc_outline_extraction(in) getbitfield(in+0x58, 1, 4)
+#define get_IN_ipc_image_emphasis(in) getbitfield(in+0x58, 1, 3)
+#define get_IN_ipc_auto_separation(in) getbitfield(in+0x58, 1, 2)
+#define get_IN_ipc_mirroring(in) getbitfield(in+0x58, 1, 1)
+#define get_IN_ipc_wl_follow(in) getbitfield(in+0x58, 1, 0)
+
+#define get_IN_ipc_subwindow(in) getbitfield(in+0x59, 1, 7)
+#define get_IN_ipc_diffusion(in) getbitfield(in+0x59, 1, 6)
+#define get_IN_ipc_ipc3(in) getbitfield(in+0x59, 1, 5)
+#define get_IN_ipc_rotation(in) getbitfield(in+0x59, 1, 4)
+#define get_IN_ipc_hybrid_crop_deskew(in) getbitfield(in+0x59, 1, 3)
+#define get_IN_ipc_ipc2_byte67(in) getbitfield(in+0x59, 1, 0)
+
+#define get_IN_compression_MH(in) getbitfield(in+0x5a, 1, 7)
+#define get_IN_compression_MR(in) getbitfield(in+0x5a, 1, 6)
+#define get_IN_compression_MMR(in) getbitfield(in+0x5a, 1, 5)
+#define get_IN_compression_JBIG(in) getbitfield(in+0x5a, 1, 4)
+#define get_IN_compression_JPG_BASE(in) getbitfield(in+0x5a, 1, 3)
+#define get_IN_compression_JPG_EXT(in) getbitfield(in+0x5a, 1, 2)
+#define get_IN_compression_JPG_INDEP(in) getbitfield(in+0x5a, 1, 1)
+
+#define get_IN_compression_JPG_gray(in) getbitfield(in+0x5b, 3, 6)
+#define IN_comp_JPG_gray_unsup 1
+#define IN_comp_JPG_gray_color 2
+#define IN_comp_JPG_gray_gray 3
+#define get_IN_compression_JPG_YUV_422(in) getbitfield(in+0x5b, 1, 0)
+
+#define get_IN_endorser_b_mech(in) getbitfield(in+0x5c, 1, 7)
+#define get_IN_endorser_b_stamp(in) getbitfield(in+0x5c, 1, 6)
+#define get_IN_endorser_b_elec(in) getbitfield(in+0x5c, 1, 5)
+#define get_IN_endorser_max_id(in) getbitfield(in+0x5c, 0x0f, 0)
+
+#define get_IN_endorser_f_mech(in) getbitfield(in+0x5d, 1, 7)
+#define get_IN_endorser_f_stamp(in) getbitfield(in+0x5d, 1, 6)
+#define get_IN_endorser_f_elec(in) getbitfield(in+0x5d, 1, 5)
+#define get_IN_endorser_f_type(in) getbitfield(in+0x5d, 3, 2)
+#define get_IN_endorser_b_type(in) getbitfield(in+0x5d, 3, 0)
+
+#define get_IN_connection(in) getbitfield(in+0x62, 3, 0)
+
+#define get_IN_endorser_type_ext(in) getbitfield(in+0x63, 1, 4)
+#define get_IN_endorser_pre_back(in) getbitfield(in+0x63, 1, 3)
+#define get_IN_endorser_pre_front(in) getbitfield(in+0x63, 1, 2)
+#define get_IN_endorser_post_back(in) getbitfield(in+0x63, 1, 1)
+#define get_IN_endorser_post_front(in) getbitfield(in+0x63, 1, 0)
+
+#define get_IN_x_overscan_size(in) getnbyte(in + 0x64, 2)
+#define get_IN_y_overscan_size(in) getnbyte(in + 0x66, 2)
+
+#define get_IN_default_bg_adf_b(in) getbitfield(in+0x68, 1, 3)
+#define get_IN_default_bg_adf_f(in) getbitfield(in+0x68, 1, 2)
+#define get_IN_default_bg_fb(in) getbitfield(in+0x68, 1, 1)
+
+#define get_IN_auto_color(in) getbitfield(in+0x69, 1, 7)
+#define get_IN_blank_skip(in) getbitfield(in+0x69, 1, 6)
+#define get_IN_multi_image(in) getbitfield(in+0x69, 1, 5)
+#define get_IN_f_b_type_indep(in) getbitfield(in+0x69, 1, 4)
+#define get_IN_f_b_res_indep(in) getbitfield(in+0x69, 1, 3)
+
+#define get_IN_dropout_spec(in) getbitfield(in+0x6a, 1, 7)
+#define get_IN_dropout_non(in) getbitfield(in+0x6a, 1, 7)
+#define get_IN_dropout_white(in) getbitfield(in+0x6a, 1, 7)
+
+#define get_IN_skew_check(in) getbitfield(in+0x6d, 1, 7)
+#define get_IN_new_fd_roll(in) getbitfield(in+0x6d, 1, 6)
+
+#define get_IN_evpd_len(in) getnbyte(in + 0x6f, 1)
+
+#define get_IN_paper_count(in) getbitfield(in+0x70, 1, 7)
+#define get_IN_paper_number(in) getbitfield(in+0x70, 1, 6)
+#define get_IN_ext_send_to(in) getbitfield(in+0x70, 1, 5)
+#define get_IN_staple_det(in) getbitfield(in+0x70, 1, 4)
+#define get_IN_pause_host(in) getbitfield(in+0x70, 1, 3)
+#define get_IN_pause_panel(in) getbitfield(in+0x70, 1, 2)
+#define get_IN_pause_conf(in) getbitfield(in+0x70, 1, 1)
+#define get_IN_hq_print(in) getbitfield(in+0x70, 1, 0)
+
+#define get_IN_ext_GHS_len(in) getnbyte(in + 0x71, 1)
+
+#define get_IN_smbc_func(in) getbitfield(in+0x72, 1, 7)
+#define get_IN_imprint_chk_b(in) getbitfield(in+0x72, 1, 6)
+#define get_IN_imprint_chk_f(in) getbitfield(in+0x72, 1, 5)
+#define get_IN_force_w_bg(in) getbitfield(in+0x72, 1, 4)
+#define get_IN_mf_recover_lvl(in) getbitfield(in+0x72, 0x0f, 0)
+
+#define get_IN_first_read_time(in) getbitfield(in+0x73, 1, 7)
+#define get_IN_div_scanning(in) getbitfield(in+0x73, 1, 6)
+#define get_IN_start_job(in) getbitfield(in+0x73, 1, 5)
+#define get_IN_lifetime_log(in) getbitfield(in+0x73, 1, 4)
+#define get_IN_imff_save_rest(in) getbitfield(in+0x73, 1, 3)
+#define get_IN_wide_scsi_type(in) getbitfield(in+0x73, 0x07, 0)
+
+#define get_IN_lut_hybrid_crop(in) getbitfield(in+0x74, 1, 7)
+#define get_IN_over_under_amt(in) getbitfield(in+0x74, 1, 6)
+#define get_IN_rgb_lut(in) getbitfield(in+0x74, 1, 5)
+#define get_IN_num_lut_dl(in) getbitfield(in+0x74, 0x0f, 0)
+
+/*byte 75 is poorly documented*/
+
+#define get_IN_sync_next_feed(in) getbitfield(in+0x76, 0x07, 0)
+
+/* some scanners need evpd inquiry data manipulated */
+#define set_IN_page_length(in,val) in[0x04]=val
+
+/* ==================================================================== */
+/* page codes used by mode_sense and mode_select */
+#define MS_pc_unk 0x2c /* Used by iX500 */
+#define MS_pc_patch 0x2e /* Patch code scanning */
+#define MS_pc_counter 0x2f /* Page number and counter reset */
+#define MS_pc_autocolor 0x32 /* Automatic color detection */
+#define MS_pc_prepick 0x33 /* Prepick next adf page */
+#define MS_pc_sleep 0x34 /* Sleep mode */
+#define MS_pc_duplex 0x35 /* ADF duplex transfer mode */
+#define MS_pc_rand 0x36 /* All sorts of device controls */
+#define MS_pc_bg 0x37 /* Backing switch control */
+#define MS_pc_df 0x38 /* Double feed detection */
+#define MS_pc_dropout 0x39 /* Drop out color */
+#define MS_pc_buff 0x3a /* Scan buffer control */
+#define MS_pc_auto 0x3c /* Auto paper size detection */
+#define MS_pc_lamp 0x3d /* Lamp light timer set */
+#define MS_pc_jobsep 0x3e /* Detect job separation sheet */
+#define MS_pc_all 0x3f /* Only used with mode_sense */
+
+/* ==================================================================== */
+/* MODE_SELECT */
+#define MODE_SELECT_code 0x15
+#define MODE_SELECT_len 6
+
+#define set_MSEL_pf(sb, val) setbitfield(sb + 1, 1, 4, val)
+#define set_MSEL_xferlen(sb, val) sb[0x04] = (unsigned char)val
+
+/* MS payloads are combined 4 byte header and 8 or 10 byte page
+ * there is also 'descriptor block' & 'vendor-specific block'
+ * but fujitsu seems not to use these */
+/* 10 byte page only used by dropout? */
+#define MSEL_header_len 4
+#define MSEL_data_min_len 8
+#define MSEL_data_max_len 10
+
+#define set_MSEL_pc(sb, val) sb[0x00]=val
+#define set_MSEL_page_len(sb, val) sb[0x01]=val
+
+#define set_MSEL_sleep_mode(sb, val) sb[0x02]=val
+
+#define set_MSEL_transfer_mode(sb, val) setbitfield(sb + 0x02, 0x01, 0, val)
+
+#define set_MSEL_bg_enable(sb, val) setbitfield(sb + 2, 1, 7, val)
+#define set_MSEL_bg_front(sb, val) setbitfield(sb + 2, 1, 5, val)
+#define set_MSEL_bg_back(sb, val) setbitfield(sb + 2, 1, 4, val)
+#define set_MSEL_bg_fb(sb, val) setbitfield(sb + 2, 1, 3, val)
+
+#define set_MSEL_df_enable(sb, val) setbitfield(sb + 2, 1, 7, val)
+#define set_MSEL_df_continue(sb, val) setbitfield(sb + 2, 1, 6, val)
+#define set_MSEL_df_skew(sb, val) setbitfield(sb + 2, 1, 5, val)
+#define set_MSEL_df_thickness(sb, val) setbitfield(sb + 2, 1, 4, val)
+#define set_MSEL_df_length(sb, val) setbitfield(sb + 2, 1, 3, val)
+#define set_MSEL_df_diff(sb, val) setbitfield(sb + 2, 3, 0, val)
+#define MSEL_df_diff_DEFAULT 0
+#define MSEL_df_diff_10MM 1
+#define MSEL_df_diff_15MM 2
+#define MSEL_df_diff_20MM 3
+#define set_MSEL_df_paperprot(sb, val) setbitfield(sb + 3, 3, 6, val)
+#define set_MSEL_df_stapledet(sb, val) setbitfield(sb + 3, 3, 4, val)
+#define set_MSEL_df_recovery(sb, val) setbitfield(sb + 3, 3, 2, val)
+
+#define set_MSEL_dropout_front(sb, val) setbitfield(sb + 0x02, 0x0f, 0, val)
+#define set_MSEL_dropout_back(sb, val) setbitfield(sb + 0x02, 0x0f, 4, val)
+#define MSEL_dropout_DEFAULT 0
+#define MSEL_dropout_GREEN 8
+#define MSEL_dropout_RED 9
+#define MSEL_dropout_BLUE 11
+#define MSEL_dropout_CUSTOM 12
+
+#define set_MSEL_buff_mode(sb, val) setbitfield(sb + 0x02, 0x03, 6, val)
+#define set_MSEL_buff_clear(sb, val) setbitfield(sb + 0x03, 0x03, 6, val)
+
+#define set_MSEL_prepick(sb, val) setbitfield(sb + 0x02, 0x03, 6, val)
+
+/*more automatic stuff with this one...*/
+#define set_MSEL_awd(sb, val) setbitfield(sb + 0x02, 0x01, 7, val)
+#define set_MSEL_w_wfill(sb, val) setbitfield(sb + 0x02, 0x01, 6, val)
+#define set_MSEL_req_driv_lut(sb, val) setbitfield(sb + 0x02, 0x01, 1, val)
+#define set_MSEL_req_driv_crop(sb, val) setbitfield(sb + 0x02, 0x01, 0, val)
+
+#define set_MSEL_ald(sb, val) setbitfield(sb + 0x03, 0x01, 7, val)
+#define set_MSEL_l_wfill(sb, val) setbitfield(sb + 0x03, 0x01, 6, val)
+
+#define set_MSEL_deskew(sb, val) setbitfield(sb + 0x04, 0x01, 7, val)
+
+#define set_MSEL_overscan(sb, val) setbitfield(sb + 0x05, 0x03, 6, val)
+#define set_MSEL_overcrop(sb, val) setbitfield(sb + 0x05, 0x01, 5, val)
+#define set_MSEL_undercrop(sb, val) setbitfield(sb + 0x05, 0x01, 4, val)
+
+#define set_MSEL_over_under_amt(sb, val) sb[0x06]=val
+
+/*buffer, prepick, overscan and df use these*/
+#define MSEL_DEFAULT 0
+#define MSEL_OFF 2
+#define MSEL_ON 3
+
+/* ==================================================================== */
+/* RESERVE_UNIT */
+#define RESERVE_UNIT_code 0x16
+#define RESERVE_UNIT_len 6
+
+/* ==================================================================== */
+/* RELEASE_UNIT */
+
+#define RELEASE_UNIT_code 0x17
+#define RELEASE_UNIT_len 6
+
+/* ==================================================================== */
+/* MODE_SENSE */
+#define MODE_SENSE_code 0x1a
+#define MODE_SENSE_len 6
+
+#define MODE_SENSE_data_len 0x14
+
+#define set_MSEN_DBD(b, val) setbitfield(b, 0x01, 3, (val?1:0))
+#define set_MSEN_pc(sb, val) setbitfield(sb + 0x02, 0x3f, 0, val)
+#define set_MSEN_xfer_length(sb, val) sb[0x04] = (unsigned char)val
+#define get_MSEN_MUD(b) getnbyte(b+(0x04+((int)*(b+0x3)))+0x4,2)
+
+/* ==================================================================== */
+/* SCAN */
+#define SCAN_code 0x1b
+#define SCAN_len 6
+
+#define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val
+
+/* ==================================================================== */
+/* READ_DIAGNOSTIC */
+#define READ_DIAGNOSTIC_code 0x1c
+#define READ_DIAGNOSTIC_len 6
+
+#define set_RD_xferlen(in, len) putnbyte(in + 3, len, 2)
+
+/* for 'FIRST READ DATE \0YMD' */
+#define RD_frd_len 10
+#define get_RD_date_status(in) in[0]
+#define RD_date_stored 0
+#define RD_date_not_stored 0xff
+
+/* for 'GET FIRST DATE ' */
+#define RD_gfd_len 10
+#define get_RD_date_year(in) in[1]
+#define get_RD_date_month(in) in[2]
+#define get_RD_date_date(in) in[3]
+
+/* for 'GET DEVICE ID ' */
+#define RD_gdi_len 10
+#define get_RD_id_serial(in) getnbyte (in, 4)
+
+/* ==================================================================== */
+/* SEND_DIAGNOSTIC */
+#define SEND_DIAGNOSTIC_code 0x1d
+#define SEND_DIAGNOSTIC_len 6
+
+#define set_SD_slftst(in, val) setbitfield(in + 1, 1, 2, val)
+#define set_SD_xferlen(in, len) putnbyte(in + 3, len, 2)
+
+#define SD_frd_string "FIRST READ DATE \0YMD"
+#define SD_frd_len 20
+#define set_SD_frd_year(in, b) putnbyte(in + 0x11, b, 1)
+#define set_SD_frd_month(in, b) putnbyte(in + 0x12, b, 1)
+#define set_SD_frd_date(in, b) putnbyte(in + 0x13, b, 1)
+
+#define SD_gfd_string "GET FIRST DATE "
+#define SD_gfd_len 16
+
+#define SD_gdi_string "GET DEVICE ID "
+#define SD_gdi_len 16
+
+#define SD_preread_string "SET PRE READMODE"
+#define SD_preread_stringlen 16
+#define SD_preread_len 32
+#define set_SD_preread_xres(in, b) putnbyte(in + 0x10, b, 2)
+#define set_SD_preread_yres(in, b) putnbyte(in + 0x12, b, 2)
+#define set_SD_preread_paper_width(sb, val) putnbyte(sb + 0x14, val, 4)
+#define set_SD_preread_paper_length(sb, val) putnbyte(sb + 0x18, val, 4)
+#define set_SD_preread_composition(sb, val) putnbyte(sb + 0x1c, val, 1)
+#define set_SD_preread_escan(sb, val) putnbyte(sb + 0x1d, val, 1)
+
+/* ==================================================================== */
+/* SET_WINDOW */
+#define SET_WINDOW_code 0x24
+#define SET_WINDOW_len 10
+
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+#define SW_header_len 8
+#define SW_desc_len 64
+
+/* ==================================================================== */
+/* GET_WINDOW */
+#define GET_WINDOW_code 0x25
+#define GET_WINDOW_len 0
+
+/* ==================================================================== */
+/* READ */
+#define READ_code 0x28
+#define READ_len 10
+
+#define set_R_datatype_code(sb, val) sb[0x02] = val
+#define R_datatype_imagedata 0x00
+#define R_datatype_pixelsize 0x80
+#define R_datatype_papersize 0x81
+#define R_datatype_effective_id 0x82
+#define set_R_window_id(sb, val) sb[0x05] = val
+#define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+#define R_PSIZE_len 0x20
+#define get_PSIZE_num_x(in) getnbyte(in + 0x00, 4)
+#define get_PSIZE_num_y(in) getnbyte(in + 0x04, 4)
+#define get_PSIZE_paper_w(in) getnbyte(in + 0x08, 4)
+#define get_PSIZE_paper_l(in) getnbyte(in + 0x0C, 4)
+#define get_PSIZE_req_driv_crop(in) getbitfield(in + 0x10, 1, 7)
+#define get_PSIZE_req_driv_lut(in) getbitfield(in + 0x10, 1, 6)
+#define get_PSIZE_req_driv_valid(in) getbitfield(in + 0x10, 1, 0)
+
+#define R_PAPER_len 0x08
+#define get_PAPER_job_sep(in) getnbyte(in + 0x02, 1)
+#define get_PAPER_paper_w(in) getnbyte(in + 0x03, 1)
+
+/* ==================================================================== */
+/* SEND */
+#define SEND_code 0x2a
+#define SEND_len 10
+
+#define set_S_xfer_datatype(sb, val) sb[0x02] = (unsigned char)val
+/*#define S_datatype_imagedatai 0x00
+#define S_datatype_halftone_mask 0x02
+#define S_datatype_gamma_function 0x03*/
+#define S_datatype_lut_data 0x83
+#define S_datatype_jpg_q_table 0x88
+#define S_datatype_endorser_data 0x90
+/*#define S_EX_datatype_lut 0x01
+#define S_EX_datatype_shading_data 0xa0
+#define S_user_reg_gamma 0xc0
+#define S_device_internal_info 0x03
+#define set_S_datatype_qual_upper(sb, val) sb[0x04] = (unsigned char)val
+#define S_DQ_none 0x00
+#define S_DQ_Rcomp 0x06
+#define S_DQ_Gcomp 0x07
+#define S_DQ_Bcomp 0x08
+#define S_DQ_Reg1 0x01
+#define S_DQ_Reg2 0x02
+#define S_DQ_Reg3 0x03*/
+#define set_S_xfer_id(sb, val) putnbyte(sb + 4, val, 2)
+#define set_S_xfer_length(sb, val) putnbyte(sb + 6, val, 3)
+
+/*lut*/
+#define S_lut_header_len 0x0a
+#define set_S_lut_order(sb, val) putnbyte(sb + 2, val, 1)
+#define S_lut_order_single 0x10
+#define set_S_lut_ssize(sb, val) putnbyte(sb + 4, val, 2)
+#define set_S_lut_dsize(sb, val) putnbyte(sb + 6, val, 2)
+#define S_lut_data_min_len 256
+#define S_lut_data_max_len 1024
+
+/*q-table*/
+#define S_q_table_header_len 0x0a
+#define S_q_table_y_len 0x40
+#define set_S_q_table_y_len(sb, val) putnbyte(sb + 4, val, 2)
+#define S_q_table_uv_len 0x40
+#define set_S_q_table_uv_len(sb, val) putnbyte(sb + 6, val, 2)
+
+/*endorser*/
+#define S_e_data_min_len 18 /*minimum 18 bytes no string bytes*/
+#define S_e_data_max_len 98 /*maximum 18 bytes plus 80 string bytes*/
+
+#define set_S_endorser_data_id(sb, val) sb[0] = val
+
+#define set_S_endorser_stamp(sb, val) setbitfield(sb + 0x01, 1, 7, val)
+#define set_S_endorser_elec(sb, val) setbitfield(sb + 0x01, 1, 6, val)
+#define set_S_endorser_decr(sb, val) setbitfield(sb + 0x01, 1, 5, val)
+#define S_e_decr_inc 0
+#define S_e_decr_dec 1
+#define set_S_endorser_lap24(sb, val) setbitfield(sb + 0x01, 1, 4, val)
+#define S_e_lap_24bit 1
+#define S_e_lap_16bit 0
+#define set_S_endorser_ctstep(sb, val) setbitfield(sb + 0x01, 0x03, 0, val)
+
+#define set_S_endorser_ulx(sb, val) putnbyte(sb + 0x02, val, 4)
+#define set_S_endorser_uly(sb, val) putnbyte(sb + 0x06, val, 4)
+
+#define set_S_endorser_font(sb, val) sb[0xa] = val
+#define S_e_font_horiz 0
+#define S_e_font_vert 1
+#define S_e_font_horiz_narrow 2
+#define set_S_endorser_size(sb, val) sb[0xb] = val
+
+#define set_S_endorser_revs(sb, val) setbitfield(sb + 0x0c, 0x01, 7, val)
+#define S_e_revs_fwd 0
+#define S_e_revs_rev 1
+#define set_S_endorser_bold(sb, val) setbitfield(sb + 0x0c, 0x01, 2, val)
+#define set_S_endorser_dirs(sb, val) setbitfield(sb + 0x0c, 0x03, 0, val)
+#define S_e_dir_left_right 0
+#define S_e_dir_top_bottom 1
+#define S_e_dir_right_left 2
+#define S_e_dir_bottom_top 3
+
+#define set_S_endorser_string_length(sb, len) sb[0x11] = len
+#define set_S_endorser_string(sb,val,len) memcpy(sb+0x12,val,(size_t)len)
+
+/* ==================================================================== */
+/* OBJECT_POSITION */
+#define OBJECT_POSITION_code 0x31
+#define OBJECT_POSITION_len 10
+
+#define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val)
+#define OP_Discharge 0x00
+#define OP_Feed 0x01
+
+/* ==================================================================== */
+/* SET_SUBWINDOW */
+#define SET_SUBWINDOW_code 0xc0
+#define SET_SUBWINDOW_len 0
+
+/* ==================================================================== */
+/* ENDORSER */
+#define ENDORSER_code 0xc1
+#define ENDORSER_len 10
+
+#define set_E_xferlen(sb, val) putnbyte(sb + 0x7, val, 2)
+
+/*endorser data*/
+#define ED_min_len 4
+#define ED_max_len 6
+
+#define set_ED_endorser_data_id(sb, val) sb[0] = val
+
+/* enable/disable endorser printing*/
+#define set_ED_stop(sb, val) setbitfield(sb + 0x01, 1, 7, val)
+#define ED_start 0
+#define ED_stop 1
+/* specifies the side of a document to be printed */
+#define set_ED_side(sb, val) setbitfield(sb + 0x01, 1, 6, val)
+#define ED_front 0
+#define ED_back 1
+
+/* format of the counter 16/24 bit*/
+#define set_ED_lap24(sb, val) setbitfield(sb + 0x01, 1, 5, val)
+#define ED_lap_16bit 0
+#define ED_lap_24bit 1
+
+/* initial count */
+#define set_ED_initial_count_16(sb, val) putnbyte(sb + 0x02, val, 2)
+#define set_ED_initial_count_24(sb, val) putnbyte(sb + 0x03, val, 3)
+
+/* ==================================================================== */
+/* GET_HW_STATUS*/
+#define GET_HW_STATUS_code 0xc2
+#define GET_HW_STATUS_len 10
+
+#define set_GHS_allocation_length(sb, len) putnbyte(sb + 0x07, len, 2)
+
+#define GHS_data_len 12
+
+#define get_GHS_top(in) getbitfield(in+0x02, 1, 7)
+#define get_GHS_A3(in) getbitfield(in+0x02, 1, 3)
+#define get_GHS_B4(in) getbitfield(in+0x02, 1, 2)
+#define get_GHS_A4(in) getbitfield(in+0x02, 1, 1)
+#define get_GHS_B5(in) getbitfield(in+0x02, 1, 0)
+
+#define get_GHS_hopper(in) !getbitfield(in+0x03, 1, 7)
+#define get_GHS_omr(in) getbitfield(in+0x03, 1, 6)
+#define get_GHS_adf_open(in) getbitfield(in+0x03, 1, 5)
+#define get_GHS_imp_open(in) getbitfield(in+0x03, 1, 4)
+#define get_GHS_fb_open(in) getbitfield(in+0x03, 1, 3)
+#define get_GHS_paper_end(in) getbitfield(in+0x03, 1, 2)
+#define get_GHS_fb_on(in) getbitfield(in+0x03, 1, 1)
+
+#define get_GHS_sleep(in) getbitfield(in+0x04, 1, 7)
+#define get_GHS_clean(in) getbitfield(in+0x04, 1, 6)
+#define get_GHS_send_sw(in) getbitfield(in+0x04, 1, 2)
+#define get_GHS_manual_feed(in) getbitfield(in+0x04, 1, 1)
+#define get_GHS_scan_sw(in) getbitfield(in+0x04, 1, 0)
+
+#define get_GHS_picalm(in) getbitfield(in+0x05, 1, 7)
+#define get_GHS_fadalm(in) getbitfield(in+0x05, 1, 6)
+#define get_GHS_brkalm(in) getbitfield(in+0x05, 1, 5)
+#define get_GHS_sepalm(in) getbitfield(in+0x05, 1, 4)
+#define get_GHS_function(in) getbitfield(in+0x05, 0x0f, 0)
+
+#define get_GHS_ink_empty(in) getbitfield(in+0x06, 1, 7)
+#define get_GHS_consume(in) getbitfield(in+0x06, 1, 6)
+#define get_GHS_overskew(in) getbitfield(in+0x06, 1, 5)
+#define get_GHS_overthick(in) getbitfield(in+0x06, 1, 4)
+#define get_GHS_plen(in) getbitfield(in+0x06, 1, 3)
+#define get_GHS_ink_side(in) getbitfield(in+0x06, 1, 2)
+#define get_GHS_mf_to(in) getbitfield(in+0x06, 1, 1)
+#define get_GHS_double_feed(in) getbitfield(in+0x06, 1, 0)
+
+#define get_GHS_error_code(in) in[0x07]
+
+#define get_GHS_skew_angle(in) getnbyte(in+0x08, 2)
+
+#define get_GHS_ink_remain(in) in[0x0a]
+
+/* ==================================================================== */
+/* SCANNER_CONTROL */
+#define SCANNER_CONTROL_code 0xf1
+#define SCANNER_CONTROL_len 10
+
+#define set_SC_ric(icb, val) setbitfield(icb + 1, 1, 4, val)
+#define set_SC_function(icb, val) setbitfield(icb + 1, 0xf, 0, val)
+#define SC_function_adf 0x00
+#define SC_function_fb 0x01
+#define SC_function_fb_hs 0x02
+#define SC_function_lamp_off 0x03
+#define SC_function_cancel 0x04
+#define SC_function_lamp_on 0x05
+#define SC_function_lamp_normal 0x06
+#define SC_function_lamp_saving 0x07
+#define SC_function_panel 0x08
+#define SC_function_scan_complete 0x09
+#define SC_function_eject_complete 0x0a
+#define SC_function_manual_feed 0x0c
+
+#define set_SC_ric_dtq(sb, val) sb[2] = val
+#define set_SC_ric_len(sb, val) putnbyte(sb + 0x06, val, 3)
+
+/* ==================================================================== */
+/* window descriptor macros for SET_WINDOW and GET_WINDOW */
+
+#define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2)
+
+/* ==================================================================== */
+
+ /* 0x00 - Window Identifier
+ * 0x00 for 3096
+ * 0x00 (front) or 0x80 (back) for 3091
+ */
+#define set_WD_wid(sb, val) sb[0] = val
+#define WD_wid_front 0x00
+#define WD_wid_back 0x80
+
+ /* 0x01 - Reserved (bits 7-1), AUTO (bit 0)
+ * Use 0x00 for 3091, 3096
+ */
+#define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val)
+#define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0)
+
+ /* 0x02,0x03 - X resolution in dpi
+ * 3091 supports 50-300 in steps of 1
+ * 3096 suppors 200,240,300,400; or 100-1600 in steps of 4
+ * if image processiong option installed
+ */
+#define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2)
+#define get_WD_Xres(sb) getnbyte(sb + 0x02, 2)
+
+ /* 0x04,0x05 - X resolution in dpi
+ * 3091 supports 50-600 in steps of 1; 75,150,300,600 only
+ * in color mode
+ * 3096 suppors 200,240,300,400; or 100-1600 in steps of 4
+ * if image processiong option installed
+ */
+#define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2)
+#define get_WD_Yres(sb) getnbyte(sb + 0x04, 2)
+
+ /* 0x06-0x09 - Upper Left X in 1/1200 inch
+ */
+#define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4)
+#define get_WD_ULX(sb) getnbyte(sb + 0x06, 4)
+
+ /* 0x0a-0x0d - Upper Left Y in 1/1200 inch
+ */
+#define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4)
+#define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4)
+
+ /* 0x0e-0x11 - Width in 1/1200 inch
+ * 3091 left+width max 10200
+ * 3096 left+width max 14592
+ * also limited to page size, see bytes 0x35ff.
+ */
+#define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4)
+#define get_WD_width(sb) getnbyte(sb + 0x0e, 4)
+
+ /* 0x12-0x15 - Height in 1/1200 inch
+ * 3091 top+height max 16832
+ * 3096 top+height max 20736, also if left+width>13199,
+ * top+height has to be less than 19843
+ */
+#define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4)
+#define get_WD_length(sb) getnbyte(sb + 0x12, 4)
+
+ /* 0x16 - Brightness
+ * 3091 always use 0x00
+ * 3096 if in halftone mode, 8 levels supported (01-1F, 20-3F,
+ ..., E0-FF)
+ * use 0x00 for user defined dither pattern
+ */
+#define set_WD_brightness(sb, val) sb[0x16] = val
+#define get_WD_brightness(sb) sb[0x16]
+
+ /* 0x17 - Threshold
+ * 3091 0x00 = use floating slice; 0x01..0xff fixed slice
+ * with 0x01=brightest, 0x80=medium, 0xff=darkest;
+ * only effective for line art mode.
+ * 3096 0x00 = use "simplified dynamic treshold", otherwise
+ * same as above but resolution is only 64 steps.
+ */
+#define set_WD_threshold(sb, val) sb[0x17] = val
+#define get_WD_threshold(sb) sb[0x17]
+
+ /* 0x18 - Contrast
+ * 3091 - not supported, always use 0x00
+ * 3096 - the same
+ */
+#define set_WD_contrast(sb, val) sb[0x18] = val
+#define get_WD_contrast(sb) sb[0x18]
+
+ /* 0x19 - Image Composition (color mode)
+ * 3091 - use 0x00 for line art, 0x01 for halftone,
+ * 0x02 for grayscale, 0x05 for color.
+ * 3096 - same but minus color.
+ */
+#define set_WD_composition(sb, val) sb[0x19] = val
+#define get_WD_composition(sb) sb[0x19]
+#define WD_comp_LA 0
+#define WD_comp_HT 1
+#define WD_comp_GS 2
+#define WD_comp_CL 3
+#define WD_comp_CH 4
+#define WD_comp_CG 5
+
+ /* 0x1a - Depth
+ * 3091 - use 0x01 for b/w or 0x08 for gray/color
+ * 3096 - use 0x01 for b/w or 0x08 for gray
+ */
+#define set_WD_bitsperpixel(sb, val) sb[0x1a] = val
+#define get_WD_bitsperpixel(sb) sb[0x1a]
+
+ /* 0x1b,0x1c - Halftone Pattern
+ * 3091 byte 1b: 00h default(=dither), 01h dither,
+ * 02h error dispersion
+ * 1c: 00 dark images, 01h dark text+images,
+ * 02h light images,
+ * 03h light text+images, 80h download pattern
+ * 3096: 1b unused; 1c bit 7=1: use downloadable pattern,
+ * bit 7=0: use builtin pattern; rest of byte 1b denotes
+ * pattern number, three builtin and five downloadable
+ * supported; higher numbers = error.
+ */
+#define set_WD_ht_type(sb, val) sb[0x1b] = val
+#define get_WD_ht_type(sb) sb[0x1b]
+#define WD_ht_type_DEFAULT 0
+#define WD_ht_type_DITHER 1
+#define WD_ht_type_DIFFUSION 2
+
+#define set_WD_ht_pattern(sb, val) sb[0x1c] = val
+#define get_WD_ht_pattern(sb) sb[0x1c]
+
+ /* 0x1d - Reverse image, padding type
+ * 3091: bit 7=1: reverse black&white
+ * bits 0-2: padding type, must be 0
+ * 3096: the same; bit 7 must be set for gray and not
+ * set for b/w.
+ */
+#define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val)
+#define get_WD_rif(sb) getbitfield(sb + 0x1d, 1, 7)
+
+ /* 0x1e,0x1f - Bit ordering
+ * 3091 not supported, use 0x00
+ * 3096 not supported, use 0x00
+ */
+#define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2)
+#define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2)
+
+ /* 0x20 - compression type
+ * not supported on smaller models, use 0x00
+ */
+#define set_WD_compress_type(sb, val) sb[0x20] = val
+#define get_WD_compress_type(sb) sb[0x20]
+#define WD_cmp_NONE 0
+#define WD_cmp_MH 1
+#define WD_cmp_MR 2
+#define WD_cmp_MMR 3
+#define WD_cmp_JBIG 0x80
+#define WD_cmp_JPG1 0x81
+#define WD_cmp_JPG2 0x82
+#define WD_cmp_JPG3 0x83
+
+
+ /* 0x21 - compression argument
+ * specify "k" parameter with MR compress,
+ * or with JPEG- Q param, 0-7
+ */
+#define set_WD_compress_arg(sb, val) sb[0x21] = val
+#define get_WD_compress_arg(sb) sb[0x21]
+
+ /* 0x22-0x27 - reserved */
+
+ /* 0x28 - vendor unique id code, decides meaning of remaining bytes
+ * 0xc1 = color mode (fi-series)
+ * 0xc0 = weird mode (M3091 and M3092)
+ * 0x00 = mono mode (other M-series and fi-series)
+ */
+#define set_WD_vendor_id_code(sb, val) sb[0x28] = val
+#define get_WD_vendor_id_code(sb) sb[0x28]
+#define WD_VUID_MONO 0x00
+#define WD_VUID_3091 0xc0
+#define WD_VUID_COLOR 0xc1
+
+ /* 0x29 common gamma */
+#define set_WD_gamma(sb, val) sb[0x29] = val
+#define get_WD_gamma(sb) sb[0x29]
+#define WD_gamma_DEFAULT 0
+#define WD_gamma_NORMAL 1
+#define WD_gamma_SOFT 2
+#define WD_gamma_SHARP 3
+
+/*==================================================================*/
+/* 0x2a-0x3F - vary based on vuid */
+
+/*==================================================================*/
+/* vuid 0x00, mono params */
+
+#define set_WD_outline(sb, val) setbitfield(sb + 0x2a, 1, 7, val)
+#define get_WD_outline(sb) getbitfield(sb + 0x2a, 1, 7)
+
+#define set_WD_emphasis(sb, val) sb[0x2b] = val
+#define get_WD_emphasis(sb) sb[0x2b]
+
+#define set_WD_separation(sb, val) setbitfield(sb + 0x2c, 1, 7, val)
+#define get_WD_separation(sb) getbitfield(sb + 0x2c, 1, 7)
+
+#define set_WD_mirroring(sb, val) setbitfield(sb + 0x2d, 1, 7, val)
+#define get_WD_mirroring(sb) getbitfield(sb + 0x2d, 1, 7)
+
+/* SDTC also called Auto-II mode?*/
+#define set_WD_variance(sb, val) sb[0x2e] = val
+#define get_WD_variance(sb) sb[0x2e]
+
+/* DTC also called Auto-I mode?*/
+/*warning: filtering uses inverse logic*/
+#define set_WD_filtering(sb, val) setbitfield(sb + 0x2f, 1, 7, val)
+#define get_WD_filtering(sb) getbitfield(sb + 0x2f, 1, 7)
+
+/*warning: smoothing uses inverse logic*/
+#define set_WD_smoothing(sb, val) setbitfield(sb + 0x2f, 3, 5, val)
+#define get_WD_smoothing(sb) getbitfield(sb + 0x2f, 3, 5)
+
+#define set_WD_gamma_curve(sb, val) setbitfield(sb + 0x2f, 3, 3, val)
+#define get_WD_gamma_curve(sb) getbitfield(sb + 0x2f, 3, 3)
+
+#define set_WD_threshold_curve(sb, val) setbitfield(sb + 0x2f, 7, 0, val)
+#define get_WD_threshold_curve(sb) getbitfield(sb + 0x2f, 7, 0)
+
+/*warning: noise removal uses inverse logic*/
+#define set_WD_noise_removal(sb, val) setbitfield(sb + 0x30, 1, 5, !val)
+#define get_WD_noise_removal(sb) !getbitfield(sb + 0x30, 1, 5)
+
+#define set_WD_matrix5x5(sb, val) setbitfield(sb + 0x30, 1, 4, val)
+#define get_WD_matrix5x5(sb) getbitfield(sb + 0x30, 1, 4)
+#define set_WD_matrix4x4(sb, val) setbitfield(sb + 0x30, 1, 3, val)
+#define get_WD_matrix4x4(sb) getbitfield(sb + 0x30, 1, 3)
+#define set_WD_matrix3x3(sb, val) setbitfield(sb + 0x30, 1, 2, val)
+#define get_WD_matrix3x3(sb) getbitfield(sb + 0x30, 1, 2)
+#define set_WD_matrix2x2(sb, val) setbitfield(sb + 0x30, 1, 1, val)
+#define get_WD_matrix2x2(sb) getbitfield(sb + 0x30, 1, 1)
+
+#define set_WD_background(sb, val) setbitfield(sb + 0x30, 1, 0, val)
+#define get_WD_background(sb) getbitfield(sb + 0x30, 1, 0)
+#define WD_background_WHITE 0
+#define WD_background_BLACK 1
+
+/*31 reserved*/
+
+#define set_WD_wl_follow(sb, val) setbitfield(sb + 0x32, 3, 6, val)
+#define get_WD_wl_follow(sb) getbitfield(sb + 0x32, 3, 6)
+#define WD_wl_follow_DEFAULT 0
+#define WD_wl_follow_ON 2
+#define WD_wl_follow_OFF 3
+
+#define set_WD_subwindow_list(sb, val) putnbyte(sb + 0x33, val, 2)
+#define get_WD_subwindow_list(sb) getnbyte(sb + 0x33, 2)
+
+/* 0x35-0x3d - paper size */
+#define set_WD_paper_selection(sb, val) setbitfield(sb + 0x35, 3, 6, val)
+#define WD_paper_SEL_UNDEFINED 0
+#define WD_paper_SEL_NON_STANDARD 3
+
+#define set_WD_paper_width_X(sb, val) putnbyte(sb + 0x36, val, 4)
+#define get_WD_paper_width_X(sb) getnbyte(sb + 0x36, 4)
+
+#define set_WD_paper_length_Y(sb, val) putnbyte(sb+0x3a, val, 4)
+#define get_WD_paper_length_Y(sb) getnbyte(sb+0x3a, 4)
+
+/* 3e switch ipc mode */
+#define set_WD_ipc_mode(sb, val) setbitfield(sb + 0x3e, 3, 6, val)
+#define get_WD_ipc_mode(sb) getbitfield(sb + 0x3e, 3, 6)
+#define WD_ipc_DEFAULT 0
+#define WD_ipc_DTC 1
+#define WD_ipc_SDTC 2
+
+/*3f reserved*/
+
+/*==================================================================*/
+/* vuid 0xc1, color params */
+
+#define set_WD_scanning_order(sb, val) sb[0x2a] = val
+#define get_WD_scanning_order(sb) sb[0x2a]
+#define WD_SCAN_ORDER_LINE 0
+#define WD_SCAN_ORDER_DOT 1
+#define WD_SCAN_ORDER_FACE 2
+
+#define set_WD_scanning_order_arg(sb, val) sb[0x2b] = val
+#define get_WD_scanning_order_arg(sb) sb[0x2b]
+#define WD_SCAN_ARG_RGB 0
+#define WD_SCAN_ARG_RBG 1
+#define WD_SCAN_ARG_GRB 2
+#define WD_SCAN_ARG_GBR 3
+#define WD_SCAN_ARG_BRG 4
+#define WD_SCAN_ARG_BGR 5
+
+/*2c-2d reserved*/
+
+/*like vuid 00, but in different location*/
+#define set_WD_c1_emphasis(sb, val) sb[0x2e] = val
+#define get_WD_c1_emphasis(sb) sb[0x2e]
+#define set_WD_c1_mirroring(sb, val) setbitfield(sb + 0x2f, 1, 7, val)
+#define get_WD_c1_mirroring(sb) getbitfield(sb + 0x2f, 1, 7)
+
+/*30-31 reserved*/
+
+/*32 wlf (see vuid 00)*/
+
+/*33-34 reserved*/
+
+/*35-3d paper size (see vuid 00)*/
+
+/*3e-3f reserved*/
+
+/*==================================================================*/
+/* vuid 0xc0, 3091/2 params */
+
+/*2a-2b same as vuid 0xc1*/
+
+#define set_WD_lamp_color(sb, val) sb[0x2d] = val
+#define get_WD_lamp_color(sb) sb[0x2d]
+#define WD_LAMP_DEFAULT 0x00
+#define WD_LAMP_BLUE 0x01
+#define WD_LAMP_RED 0x02
+#define WD_LAMP_GREEN 0x04
+
+/*2e-31 reserved*/
+
+#define set_WD_quality(sb, val) sb[0x32] = val
+#define get_WD_quality(sb) sb[0x32]
+#define WD_QUAL_NORMAL 0x00
+#define WD_QUAL_HIGH 0x02
+
+/*33-34 reserved*/
+
+/*35-3d paper size (see vuid 00)*/
+
+/*3e-3f reserved*/
+
+/*FIXME: more params here*/
+
+/* ==================================================================== */
+
+#endif
diff --git a/backend/fujitsu.c b/backend/fujitsu.c
new file mode 100644
index 0000000..48d55dc
--- /dev/null
+++ b/backend/fujitsu.c
@@ -0,0 +1,9757 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package, and implements a SANE backend
+ for various Fujitsu scanners.
+
+ Copyright (C) 2000 Randolph Bentson
+ Copyright (C) 2001 Frederik Ramm
+ Copyright (C) 2001-2004 Oliver Schirrmeister
+ Copyright (C) 2003-2011 m. allan noah
+
+ JPEG output and low memory usage support funded by:
+ Archivista GmbH, www.archivista.ch
+ Endorser support funded by:
+ O A S Oilfield Accounting Service Ltd, www.oas.ca
+ Automatic length detection support funded by:
+ Martin G. Miller, mgmiller at optonline.net
+ Software image enhancement routines funded by:
+ Fujitsu Computer Products of America, Inc. www.fcpa.com
+
+ --------------------------------------------------------------------------
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ The source code is divided in sections which you can easily find by
+ searching for the tag "@@".
+
+ Section 1 - Boilerplate: Init & static stuff
+ Section 2 - Init: sane_init, _get_devices, _open ...
+ Section 3 - Options: sane_*_option functions
+ Section 4 - Scanning: sane_start, _get_param, _read ...
+ Section 5 - Cleanup: sane_cancel, ...
+ Section 6 - Misc: sense_handler, hexdump, ...
+ Section 7 - Image processing: deskew, crop, despeck
+
+ Changes:
+ v1, 2002-05-05, OS
+ - release memory allocated by sane_get_devices
+ - several bugfixes
+ - supports the M3097
+ - get threshold, contrast and brightness from vpd
+ - imprinter support
+ - get_hardware_status now works before calling sane_start
+ - avoid unnecessary reload of options when using source=fb
+ v2, 2002-08-08, OS
+ - bugfix. Imprinter didn't print the first time after
+ switching on the scanner
+ - bugfix. reader_generic_passthrough ignored the number of bytes
+ returned by the scanner
+ v3, 2002-09-13, OS
+ - 3092 support (mgoppold a t tbz-pariv.de)
+ - tested 4097 support
+ - changed some functions to receive compressed data
+ v4, 2003-02-13, OS
+ - fi-4220C support (ron a t roncemer.com)
+ - SCSI over USB support (ron a t roncemer.com)
+ v5, 2003-02-20, OS
+ - set availability of options THRESHOLD und VARIANCE
+ - option RIF is available for 3091 and 3092
+ v6, 2003-03-04, OS
+ - renamed some variables
+ - bugfix: duplex scanning now works when disconnect is enabled
+ v7, 2003-03-10, OS
+ - displays the offending byte in the window descriptor block
+ v8, 2003-03-28, OS
+ - fi-4120C support, MAN
+ - display information about gamma in vital_product_data
+ v9 2003-06-04, MAN
+ - separated the 4120 and 4220 into another model
+ - color support for the 4x20
+ v10 2003-06-04, MAN
+ - removed SP15 code
+ - sane_open actually opens the device you request
+ v11 2003-06-11, MAN
+ - fixed bug in that code when a scanner is disconnected
+ v12 2003-10-06, MAN
+ - added code to support color modes of more recent scanners
+ v13 2003-11-07, OS
+ - Bugfix. If a scanner returned a color image
+ in format rr...r gg...g bb...b the reader process crashed
+ - Bugfix. Disable option gamma was for the fi-4120
+ v14 2003-12-15, OS
+ - Bugfix: set default threshold range to 0..255 There is a problem
+ with the M3093 when you are not allows to set the threshold to 0
+ - Bugfix: set the allowable x- and y-DPI values from VPD. Scanning
+ with x=100 and y=100 dpi with an fi4120 resulted in an image
+ with 100,75 dpi
+ - Bugfix: Set the default value of gamma to 0x80 for all scanners
+ that don't have built in gamma patterns
+ - Bugfix: fi-4530 and fi-4210 don't support standard paper size
+ v15 2003-12-16, OS
+ - Bugfix: pagewidth and pageheight were disabled for the fi-4530C
+ v16 2004-02-20, OS
+ - merged the 3092-routines with the 3091-routines
+ - inverted the image in mode color and grayscale
+ - jpg hardware compression support (fi-4530C)
+ v17 2004-03-04, OS
+ - enabled option dropoutcolor for the fi-4530C, and fi-4x20C
+ v18 2004-06-02, OS
+ - bugfix: can read duplex color now
+ v19 2004-06-28, MAN
+ - 4220 use model code not strcmp (stan a t saticed.me.uk)
+ v20 2004-08-24, OS
+ - bugfix: 3091 did not work since 15.12.2003
+ - M4099 supported (bw only)
+ v21 2006-05-01, MAN
+ - Complete rewrite, half code size
+ - better (read: correct) usb command support
+ - basic support for most fi-series
+ - most scanner capabilities read from VPD
+ - reduced model-specific code
+ - improved scanner detection/initialization
+ - improved SANE_Option handling
+ - basic button support
+ - all IPC and Imprinter options removed temporarily
+ - duplex broken temporarily
+ v22 2006-05-04, MAN
+ - do_scsi_cmd gets basic looping capability
+ - reverse now divided by mode
+ - re-write sane_fix/unfix value handling
+ - fix several bugs in options code
+ - some options' ranges modified by other options vals
+ - added advanced read-only options for all
+ known hardware sensors and buttons
+ - rewrote hw status function
+ - initial testing with M3091dc- color mode broken
+ v23 2006-05-14, MAN
+ - initial attempt to recover duplex mode
+ - fix bad usb prodID when config file missing
+ v24 2006-05-17, MAN
+ - sane_read must set len=0 when return != good
+ - simplify do_cmd() calls by removing timeouts
+ - lengthen most timeouts, shorten those for wait_scanner()
+ v25 2006-05-19, MAN
+ - rename scsi-buffer-size to buffer-size, usb uses it too
+ - default buffer-size increased to 64k
+ - use sanei_scsi_open_extended() to set buffer size
+ - fix some compiler warns: 32&64 bit gcc
+ v26 2006-05-23, MAN
+ - dont send scanner control (F1) if unsupported
+ v27 2006-05-30, MAN
+ - speed up hexdump (adeuring A T gmx D O T net)
+ - duplex request same size block from both sides
+ - dont #include or call sanei_thread
+ - split usb/scsi command DBG into 25 and 30
+ v28 2006-06-01, MAN
+ - sane_read() usleep if scanner is busy
+ - do_*_cmd() no looping (only one caller used it),
+ remove unneeded casts, cleanup/add error messages
+ - scanner_control() look at correct has_cmd_* var,
+ handles own looping on busy
+ v29 2006-06-04, MAN
+ - M3091/2 Color mode support (duplex still broken)
+ - all sensors option names start with 'button-'
+ - rewrite sane_read and helpers to use buffers,
+ currently an extreme waste of ram, but should
+ work with saned and scanimage -T
+ - merge color conversion funcs into read_from_buf()
+ - compare bytes tx v/s rx instead of storing EOFs
+ - remove scanner cmd buf, use buf per func instead
+ - print color and duplex raster offsets (inquiry)
+ - print EOM, ILI, and info bytes (request sense)
+ v30 2006-06-06, MAN
+ - M3091/2 duplex support, color/gray/ht/lineart ok
+ - sane_read helpers share code, report more errors
+ - add error msg if VPD missing or non-extended
+ - remove references to color_lineart and ht units
+ - rework init_model to support more known models
+ - dont send paper size data if using flatbed
+ v31 2006-06-13, MAN
+ - add 5220C usb id
+ - dont show ink level buttons if no imprinter
+ - run ghs/rs every second instead of every other
+ v32 2006-06-14, MAN
+ - add 4220C2 usb id
+ v33 2006-06-14, MAN (SANE v1.0.18)
+ - add Fi-5900 usb id and init_model section
+ v34 2006-07-04, MAN
+ - add S500 usb id
+ - gather more data from inq and vpd
+ - allow background color setting
+ v35 2006-07-05, MAN
+ - allow double feed sensor settings
+ - more consistent naming of global strings
+ v36 2006-07-06, MAN
+ - deal with fi-5900 even bytes problem
+ - less verbose calculateDerivedValues()
+ v37 2006-07-14, MAN
+ - mode sense command support
+ - detect mode page codes instead of hardcoding
+ - send command support
+ - brightness/contrast support via LUT
+ - merge global mode page buffers
+ v38 2006-07-15, MAN
+ - add 'useless noise' debug level (35)
+ - move mode sense probe errors to DBG 35
+ v39 2006-07-17, MAN
+ - rewrite contrast slope math for readability
+ v40 2006-08-26, MAN
+ - rewrite brightness/contrast more like xsane
+ - initial gamma support
+ - add fi-5530 usb id
+ - rewrite do_*_cmd functions to handle short reads
+ and to use ptr to return read in length
+ - new init_user function split from init_model
+ - init_vpd allows short vpd block for older models
+ - support MS buffer (s.scipioni AT harvardgroup DOT it)
+ - support MS prepick
+ - read only 1 byte of mode sense output
+ v41 2006-08-28, MAN
+ - do_usb_cmd() returns io error on cmd/out/status/rs EOF
+ - fix bug in MS buffer/prepick scsi data block
+ v42 2006-08-31, MAN
+ - fix bug in get_hardware_status (#303798)
+ v43 2006-09-19, MAN
+ - add model-specific code to init_vpd for M3099
+ v44 2007-01-26, MAN
+ - set SANE_CAP_HARD_SELECT on all buttons/sensors
+ - disable sending gamma LUT, seems wrong on some units?
+ - support MS overscan
+ - clamp the scan area to the pagesize on ADF
+ v45 2007-01-28, MAN
+ - update overscan code to extend max scan area
+ v46 2007-03-08, MAN
+ - tweak fi-4x20c2 and M3093 settings
+ - add fi-5110EOXM usb id
+ - add M3093 non-alternating duplex code
+ v47 2007-04-13, MAN
+ - change window_gamma determination
+ - add fi-5650C usb id and color mode
+ v48 2007-04-16, MAN
+ - re-enable brightness/contrast for built-in models
+ v49 2007-06-28, MAN
+ - add fi-5750C usb id and color mode
+ v50 2007-07-10, MAN
+ - updated overscan and bgcolor option descriptions
+ - added jpeg output support
+ - restructured usb reading code to use RS len for short reads
+ - combined calcDerivedValues with sane_get_params
+ v51 2007-07-26, MAN
+ - fix bug in jpeg output support
+ v52 2007-07-27, MAN
+ - remove unused jpeg function
+ - reactivate look-up-table based brightness and contrast options
+ - change range of hardware brightness/contrast to match LUT versions
+ - call send_lut() from sane_control_option instead of sane_start
+ v53 2007-11-18, MAN
+ - add S510 usb id
+ - OPT_NUM_OPTS type is SANE_TYPE_INT (jblache)
+ v54 2007-12-29, MAN
+ - disable SANE_FRAME_JPEG support until SANE 1.1.0
+ v55 2007-12-29, MAN (SANE v1.0.19)
+ - add S500M usb id
+ v56 2008-02-14, MAN
+ - sanei_config_read has already cleaned string (#310597)
+ v57 2008-02-24, MAN
+ - fi-5900 does not (initially) interlace colors
+ - add mode sense for color interlacing? (page code 32)
+ - more debug output in init_ms()
+ v58 2008-04-19, MAN
+ - page code 32 is not color interlacing, rename to 'unknown'
+ - increase number of bytes in response buffer of init_ms()
+ - protect debug modification code in init_ms() if NDEBUG is set
+ - proper async sane_cancel support
+ - re-enable JPEG support
+ - replace s->img_count with s->side
+ - sane_get_parameters(): dont round up larger than current paper size
+ - sane_start() rewritten, shorter, more clear
+ - return values are SANE_Status, not int
+ - hide unused functions
+ v59 2008-04-22, MAN
+ - add fi-6140 usb ID, and fi-6x40 color mode
+ v60 2008-04-27, MAN
+ - move call to sanei_usb_init() from sane_init() to find_scanners
+ - free sane_devArray before calloc'ing a new one
+ v61 2008-05-11, MAN
+ - minor cleanups to init_ms()
+ - add fi-5530C2 usb id
+ - merge find_scanners into sane_get_devices
+ - inspect correct bool to enable prepick mode option
+ v62 2008-05-20, MAN
+ - check for all supported scsi commands
+ - use well-known option group strings from saneopts.h
+ - rename pagewidth to page-width, to meet sane 1.1.0, same for height
+ - add unused get_window()
+ v63 2008-05-21, MAN
+ - use sane 1.1.0 well-known option names for some buttons
+ - remove 'button-' from other buttons and sensors
+ v64 2008-05-28, MAN
+ - strcpy device_name[] instead of strdup/free *device_name
+ - add send/read diag commands to get scanner serial number
+ - use model and serial to build sane.name (idea from Ryan Duryea)
+ - allow both serial_name and device_name to sane_open scanner
+ - correct mode select/sense 6 vs 10 booleans
+ - rename product_name to model_name
+ - simulate missing VPD data for M3097G
+ - hide get_window
+ - improve handling of vendor unique section of set_window
+ - add init_interlace to detect proper color mode without hardcoding
+ - add ascii output to hexdump
+ v65 2008-06-24, MAN
+ - detect endorser type during init_inquiry()
+ - add endorser options
+ - add send_endorser() and call from sane_control_option()
+ - add endorser() and call from sane_start()
+ - convert set_window() to use local cmd and payload copies
+ - remove get_window()
+ - mode_select_buff() now clears the buffer, and called in sane_close()
+ - fi-4990 quirks added, including modified even_scan_line code
+ v66 2008-06-26, MAN
+ - restructure double feed detection options for finer-grained control
+ - add endorser side option
+ - prevent init_interlace() from overriding init_model()
+ - simplify sane_start() and fix interlaced duplex jpeg support
+ - simplify sane_read() and add non-interlaced duplex jpeg support
+ - removed unused code
+ v67 2008-07-01, MAN
+ - add IPC/DTC/SDTC options
+ - call check_for_cancel() in sane_cancel, unless s->reader flag is set
+ v68 2008-07-02, MAN
+ - add halftone type and pattern options
+ - support M3097G with IPC and CMP options via modified VPD response
+ v69 2008-07-03, MAN
+ - support hot-unplugging scanners
+ v70 2008-07-05, MAN
+ - fix bug in sane_get_parameters (failed to copy values)
+ - autodetect jpeg duplex interlacing mode by inspecting scan width
+ v71 2008-07-13, MAN
+ - disable overscan option if vpd does not tell overscan size
+ - fi-5110EOX crops scan area based on absolute maximum, not paper
+ - fi-5530C/2 and fi-5650C can't handle 10 bit LUT via USB
+ - fi-5900 has background color, though it reports otherwise
+ v72 2008-07-13, MAN
+ - use mode_sense to determine background color support
+ - remove fi-5900 background color override
+ v73 2008-07-14, MAN
+ - correct overscan dimension calculation
+ - provide correct overscan size overrides for fi-5110C and fi-4x20C2
+ - add fi-6130 usb ID
+ - fi-5750C can't handle 10 bit LUT via USB
+ v74 2008-08-02, MAN
+ - replace global scsi blocks with local ones in each function
+ v75 2008-08-07, ReneR
+ - added fi-6230 usb ID
+ v76 2008-08-13, MAN
+ - add independent maximum area values for flatbed
+ - override said values for fi-4220C, fi-4220C2 and fi-5220C
+ v77 2008-08-26, MAN
+ - override flatbed maximum area for fi-6230C and fi-6240C
+ - set PF bit in all mode_select(6) CDB's
+ - set SANE_CAP_INACTIVE on all disabled options
+ - fix bug in mode_select page for sleep timer
+ v78 2008-08-26, MAN
+ - recent model names (fi-6xxx) dont end in 'C'
+ - simplify flatbed area overrides
+ - call scanner_control to change source during sane_start
+ v79 2008-10-01, MAN
+ - add usb ids for several models
+ - print additional hardware capability bits
+ - detect front-side endorser
+ - disable endorser-side controls if only one side installed
+ - add quirks for fi-6x70
+ v80 2008-10-08, MAN
+ - front-side endorser uses data ID 0x80
+ v81 2008-10-20, MAN
+ - increase USB timeouts
+ - enable get_pixelsize() to update scan params after set_window()
+ - remove even_scan_line hack
+ v82 2008-10-31, MAN
+ - improved front-side endorser vpd detection
+ - send scanner_control_ric during sane_read of each side
+ - add fi-6770A and fi-6670A USB ID's
+ v83 2008-11-06, MAN
+ - round binary bpl and Bpl up to byte boundary
+ - use s->params instead of user data in set_window()
+ - read_from_scanner() only grabs an even number of lines
+ v84 2008-11-07, MAN
+ - round lines down to even number to get even # of total bytes
+ - round binary bpl and Bpl down to byte boundary
+ v85 2008-12-10, MAN
+ - round pixels_per_line down to arbitrary limits for fi-4990 & fi-4860
+ - fi-4860 returns random garbage to serial number queries
+ - initialize *info to 0 in sane_control_option()
+ v86 2008-12-18, MAN
+ - get_pixelsize() sets back window ID for back side scans
+ v87 2008-12-21, MAN
+ - accept null pointer as empty device name
+ - track frontend reading sensor/button values to reload
+ - deactivate double feed options if df-action == default
+ v88 2009-01-21, MAN
+ - dont export private symbols
+ v89 2009-02-20, MAN
+ - fi-4750 returns random garbage to serial number queries
+ v90 2009-02-23, MAN
+ - added ScanSnap S510M usb ids
+ v91 2009-03-20, MAN
+ - remove unused temp file code
+ v92 2009-04-12, MAN
+ - disable SANE_FRAME_JPEG support (again)
+ v93 2009-04-14, MAN (SANE 1.0.20)
+ - return cmd status for reads on sensors
+ - ignore errors in scanner_control(),
+ M3091 has not worked since sane 1.0.19, due to this.
+ - copy_buffer needs to count lines, or M309[12] cannot duplex
+ v94 2009-05-22, MAN
+ - add side option to show which duplex image is being transferred
+ - convert front and simplex buffers to use much less ram
+ - add lowmemory option which makes duplex back buffer small too
+ - refactor image handling code to track eof's instead of lengths
+ - do color deinterlacing after reading from scanner, before buffering
+ v95 2009-06-02, MAN
+ - scanner_control_ric should return a subset of the possible errors
+ v96 2009-08-07, MAN
+ - split sane_get_parameters into two functions
+ - remove unused code from get_pixelsize
+ - support hardware based auto length detection
+ v97 2009-09-14, MAN
+ - use sanei_magic to provide software deskew, autocrop and despeckle
+ v98 2010-02-09, MAN (SANE 1.0.21)
+ - clean up #include lines and copyright
+ - add SANE_I18N to static strings
+ - don't fail if scsi buffer is too small
+ - disable bg_color for S1500
+ - enable flatbed for M3092
+ v99 2010-05-14, MAN
+ - sense_handler(): collect rs_info for any ILI, not just EOM
+ - do_usb_cmd(): use rs_info whenever set, not just EOF
+ - read_from_*(): better handling of EOF from lower level functions
+ - sane_read(): improve duplexing logic
+ v100 2010-06-01, MAN
+ - store more Request Sense data in scanner struct
+ - clear Request Sense data at start of every do_cmd() call
+ - track per-side ILI and global EOM flags
+ - set per-side EOF flag if ILI and EOM are set
+ v101 2010-06-23, MAN
+ - fix compilation bug when jpeg is enabled
+ v102 2010-09-22, MAN
+ - fix infinite loop when scan is an odd number of lines
+ v103 2010-11-23, MAN
+ - remove compiled-in default config file
+ - initial support for new fi-6xxx machines
+ v104 2010-11-24, MAN
+ - never request more than s->buffer_size from scanner
+ - silence noisy set_window() calls from init_interlace()
+ v105 2010-12-02, MAN
+ - backup and restore image params around image processing code
+ - cache software crop/deskew parameters for use on backside of duplex
+ - fi-6110 does not support bgcolor or prepick
+ v106 2011-01-30, MAN (SANE 1.0.22)
+ - dont call mode_select with a page code the scanner does not support
+ v107 2011-11-03, MAN
+ - M3091 does not support scanner_control(adf)
+ - Correct buffer overflow in read_from_3091duplex()
+ - sane_read() now always calls read_from_*()
+ - read_from_*() are callable when there is no data, and read to eof
+ - sane_read() will keep alternate duplex reads to similar length
+ - Added debugging statements
+ - Corrected comments
+ - Updated Copyright
+ v108 2011-11-21, MAN
+ - merged x/y resolution options
+ - moved page width/height to start of geometry group
+ - use mode to pick resolution list v/s range
+ - improved M3091 resolution choices
+ v109 2011-12-20, MAN
+ - added some MS and INQ information
+ - increased default buffer size for later machines in config file
+ - renamed new fi-6xx0Z models
+ v110 2012-05-09, MAN
+ - correct max_y_fb for fi-62x0 series
+ - add must_fully_buffer helper routine
+ - add hwdeskewcrop option, with fallback to software versions
+ - add 'actual' param to get_pixelsize for post-scan
+ - add recent model VPD params
+ - only set params->lines = -1 when using ald without buffering
+ - fix bugs in background color when using software deskew
+ v111 2012-05-10, MAN (SANE 1.0.23)
+ - call send_* and mode_select_* from sane_start
+ - split read payloads into new debug level
+ - add paper-protect, staple-detect and df-recovery options
+ v112 2013-02-22, MAN
+ - some scanners (fi-6x70 and later) don't enable IPC by default
+ v113 2013-02-24, MAN
+ - support for ScanSnap iX500
+ - fix bug with jpeg de-interlacing code
+ - allow has_MS_* and has_pixelsize to be set in init_model
+ - fix use of uninitialized buffer in send_lut
+ - add send_q_table()
+ - allow wait_scanner() to be bypassed in object_position
+ - moved send_lut() to after set_window
+ v114 2013-03-01, MAN
+ - support resolutions > 300 for iX500 using diag_preread()
+ - remove most communication with scanner during sane_control_option()
+ v115 2013-03-09, MAN
+ - separate s->mode into s_mode and u_mode
+ - separate s->params into s_params and u_params
+ - generate grayscale and binary in software if required (iX500)
+ v116 2013-03-23, MAN
+ - call set_mode() in init_interlace
+ - add swskip option
+ v117 2013-06-11, MAN
+ - default buffer-mode to off
+ - improved error handling in sane_start
+ - image width must be multiple of 8 when swcrop is used before binarization (iX500)
+ - check hopper sensor before calling object_position(load) on iX500
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . .
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . . - sane_get_parameters() : returns estimated scan parameters
+ . . - (repeat previous 3 functions)
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times; after sane_read returns EOF,
+ . . loop may continue with sane_start which may return a 2nd page
+ . . when doing duplex scans, or load the next page from the ADF)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+*/
+
+/*
+ * @@ Section 1 - Boilerplate
+ */
+
+#include "../include/sane/config.h"
+
+#include <string.h> /*memcpy...*/
+#include <ctype.h> /*isspace*/
+#include <math.h> /*tan*/
+#include <unistd.h> /*usleep*/
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_magic.h"
+
+#include "fujitsu-scsi.h"
+#include "fujitsu.h"
+
+#define DEBUG 1
+#define BUILD 117
+
+/* values for SANE_DEBUG_FUJITSU env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - scsi/usb trace 25
+ - scsi/usb writes 30
+ - scsi/usb reads 31
+ - useless noise 35
+*/
+
+/* ------------------------------------------------------------------------- */
+#define STRING_FLATBED SANE_I18N("Flatbed")
+#define STRING_ADFFRONT SANE_I18N("ADF Front")
+#define STRING_ADFBACK SANE_I18N("ADF Back")
+#define STRING_ADFDUPLEX SANE_I18N("ADF Duplex")
+
+#define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+#define STRING_DEFAULT SANE_I18N("Default")
+#define STRING_ON SANE_I18N("On")
+#define STRING_OFF SANE_I18N("Off")
+
+#define STRING_DTC SANE_I18N("DTC")
+#define STRING_SDTC SANE_I18N("SDTC")
+
+#define STRING_DITHER SANE_I18N("Dither")
+#define STRING_DIFFUSION SANE_I18N("Diffusion")
+
+#define STRING_RED SANE_I18N("Red")
+#define STRING_GREEN SANE_I18N("Green")
+#define STRING_BLUE SANE_I18N("Blue")
+#define STRING_WHITE SANE_I18N("White")
+#define STRING_BLACK SANE_I18N("Black")
+
+#define STRING_NONE SANE_I18N("None")
+#define STRING_JPEG SANE_I18N("JPEG")
+
+#define STRING_CONTINUE SANE_I18N("Continue")
+#define STRING_STOP SANE_I18N("Stop")
+
+#define STRING_10MM SANE_I18N("10mm")
+#define STRING_15MM SANE_I18N("15mm")
+#define STRING_20MM SANE_I18N("20mm")
+
+#define STRING_HORIZONTAL SANE_I18N("Horizontal")
+#define STRING_HORIZONTALBOLD SANE_I18N("Horizontal bold")
+#define STRING_HORIZONTALNARROW SANE_I18N("Horizontal narrow")
+#define STRING_VERTICAL SANE_I18N("Vertical")
+#define STRING_VERTICALBOLD SANE_I18N("Vertical bold")
+
+#define STRING_TOPTOBOTTOM SANE_I18N("Top to bottom")
+#define STRING_BOTTOMTOTOP SANE_I18N("Bottom to top")
+
+#define STRING_FRONT SANE_I18N("Front")
+#define STRING_BACK SANE_I18N("Back")
+
+#define max(a,b) (((a)>(b))?(a):(b))
+
+/* Also set via config file. */
+static int global_buffer_size = 64 * 1024;
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ * a ptr to a single-linked list of fujitsu structs
+ */
+static const SANE_Device **sane_devArray = NULL;
+static struct fujitsu *fujitsu_devList = NULL;
+
+/*
+ * @@ Section 2 - SANE & scanner init code
+ */
+
+/*
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init: start\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: fujitsu backend %d.%d.%d, from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ sanei_magic_init();
+
+ DBG (10, "sane_init: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ */
+/*
+ * Read the config file, find scanners with help from sanei_*
+ * and store in global device structs
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct fujitsu * s;
+ struct fujitsu * prev = NULL;
+ char line[PATH_MAX];
+ const char *lp;
+ FILE *fp;
+ int num_devices=0;
+ int i=0;
+
+ local_only = local_only; /* get rid of compiler warning */
+
+ DBG (10, "sane_get_devices: start\n");
+
+ /* mark all existing scanners as missing, attach_one will remove mark */
+ for (s = fujitsu_devList; s; s = s->next) {
+ s->missing = 1;
+ }
+
+ sanei_usb_init();
+
+ /* set this to 64K before reading the file */
+ global_buffer_size = 64 * 1024;
+
+ fp = sanei_config_open (FUJITSU_CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n",
+ FUJITSU_CONFIG_FILE);
+
+ while (sanei_config_read (line, PATH_MAX, fp)) {
+
+ lp = line;
+
+ /* ignore comments */
+ if (*lp == '#')
+ continue;
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if ((strncmp ("option", lp, 6) == 0) && isspace (lp[6])) {
+
+ lp += 6;
+ lp = sanei_config_skip_whitespace (lp);
+
+ /* we allow setting buffersize too big */
+ if ((strncmp (lp, "buffer-size", 11) == 0) && isspace (lp[11])) {
+
+ int buf;
+ lp += 11;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if (buf < 4096) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" (%d) is < 4096, ignoring!\n", buf);
+ continue;
+ }
+
+ if (buf > 64*1024) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" (%d) is > %d, warning!\n", buf, 64*1024);
+ }
+
+ DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n", buf);
+ global_buffer_size = buf;
+ }
+ else {
+ DBG (5, "sane_get_devices: config option \"%s\" unrecognized - ignored.\n", lp);
+ }
+ }
+ else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_usb_attach_matching_devices(lp, attach_one_usb);
+ }
+ else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_config_attach_matching_devices (lp, attach_one_scsi);
+ }
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" unrecognized - ignored.\n", lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: missing required config file '%s'!\n",
+ FUJITSU_CONFIG_FILE);
+ }
+
+ /*delete missing scanners from list*/
+ for (s = fujitsu_devList; s;) {
+ if(s->missing){
+ DBG (5, "sane_get_devices: missing scanner %s\n",s->device_name);
+
+ /*splice s out of list by changing pointer in prev to next*/
+ if(prev){
+ prev->next = s->next;
+ free(s);
+ s=prev->next;
+ }
+ /*remove s from head of list, using prev to cache it*/
+ else{
+ prev = s;
+ s = s->next;
+ free(prev);
+ prev=NULL;
+
+ /*reset head to next s*/
+ fujitsu_devList = s;
+ }
+ }
+ else{
+ prev = s;
+ s=prev->next;
+ }
+ }
+
+ for (s = fujitsu_devList; s; s=s->next) {
+ DBG (15, "sane_get_devices: found scanner %s\n",s->device_name);
+ num_devices++;
+ }
+
+ DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
+ if (!sane_devArray)
+ return SANE_STATUS_NO_MEM;
+
+ for (s = fujitsu_devList; s; s=s->next) {
+ sane_devArray[i++] = (SANE_Device *)&s->sane;
+ }
+ sane_devArray[i] = 0;
+
+ if(device_list){
+ *device_list = sane_devArray;
+ }
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return ret;
+}
+
+/* callbacks used by sane_get_devices */
+static SANE_Status
+attach_one_scsi (const char *device_name)
+{
+ return attach_one(device_name,CONNECTION_SCSI);
+}
+
+static SANE_Status
+attach_one_usb (const char *device_name)
+{
+ return attach_one(device_name,CONNECTION_USB);
+}
+
+/* build the scanner struct and link to global list
+ * unless struct is already loaded, then pretend
+ */
+static SANE_Status
+attach_one (const char *device_name, int connType)
+{
+ struct fujitsu *s;
+ int ret;
+
+ DBG (10, "attach_one: start\n");
+ DBG (15, "attach_one: looking for '%s'\n", device_name);
+
+ for (s = fujitsu_devList; s; s = s->next) {
+ if (strcmp (s->device_name, device_name) == 0){
+ DBG (10, "attach_one: already attached!\n");
+ s->missing = 0;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* build a fujitsu struct to hold it */
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* scsi command/data buffer */
+ s->buffer_size = global_buffer_size;
+
+ /* copy the device name */
+ strcpy (s->device_name, device_name);
+
+ /* connect the fd */
+ s->connection = connType;
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ free (s);
+ return ret;
+ }
+
+ /* Now query the device to load its vendor/model/version */
+ ret = init_inquire (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: inquiry failed\n");
+ return ret;
+ }
+
+ /* load detailed specs/capabilities from the device */
+ ret = init_vpd (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: vpd failed\n");
+ return ret;
+ }
+
+ /* see what mode pages device supports */
+ ret = init_ms (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: ms failed\n");
+ return ret;
+ }
+
+ /* clean up the scanner struct based on model */
+ /* this is the only piece of model specific code */
+ ret = init_model (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: model failed\n");
+ return ret;
+ }
+
+ /* sets SANE option 'values' to good defaults */
+ ret = init_user (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: user failed\n");
+ return ret;
+ }
+
+ ret = init_options (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: options failed\n");
+ return ret;
+ }
+
+ ret = init_interlace (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s);
+ DBG (5, "attach_one: interlace failed\n");
+ return ret;
+ }
+
+ /* load strings into sane_device struct */
+ s->sane.name = s->device_name;
+ s->sane.vendor = s->vendor_name;
+ s->sane.model = s->model_name;
+ s->sane.type = "scanner";
+
+ /* change name in sane_device struct if scanner has serial number */
+ ret = init_serial (s);
+ if (ret == SANE_STATUS_GOOD) {
+ s->sane.name = s->serial_name;
+ }
+ else{
+ DBG (5, "attach_one: serial number unsupported?\n");
+ }
+
+ /* we close the connection, so that another backend can talk to scanner */
+ disconnect_fd(s);
+
+ /* store this scanner in global vars */
+ s->next = fujitsu_devList;
+ fujitsu_devList = s;
+
+ DBG (10, "attach_one: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * connect the fd in the scanner struct
+ */
+static SANE_Status
+connect_fd (struct fujitsu *s)
+{
+ SANE_Status ret;
+ int buffer_size = s->buffer_size;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else if (s->connection == CONNECTION_USB) {
+ DBG (15, "connect_fd: opening USB device\n");
+ ret = sanei_usb_open (s->device_name, &(s->fd));
+ }
+ else {
+ DBG (15, "connect_fd: opening SCSI device\n");
+ ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler, s,
+ &s->buffer_size);
+ if(!ret && buffer_size != s->buffer_size){
+ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n",
+ buffer_size, s->buffer_size);
+ }
+ }
+
+ if(ret == SANE_STATUS_GOOD){
+
+ /* first generation usb scanners can get flaky if not closed
+ * properly after last use. very first commands sent to device
+ * must be prepared to correct this- see wait_scanner() */
+ ret = wait_scanner(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "connect_fd: could not wait_scanner\n");
+ disconnect_fd(s);
+ }
+
+ }
+ else{
+ DBG (5, "connect_fd: could not open device: %d\n", ret);
+ }
+
+ DBG (10, "connect_fd: finish\n");
+
+ return ret;
+}
+
+/*
+ * This routine will check if a certain device is a Fujitsu scanner
+ * It also copies interesting data from INQUIRY into the handle structure
+ */
+static SANE_Status
+init_inquire (struct fujitsu *s)
+{
+ int i;
+ SANE_Status ret;
+
+ unsigned char cmd[INQUIRY_len];
+ size_t cmdLen = INQUIRY_len;
+
+ unsigned char in[INQUIRY_std_len];
+ size_t inLen = INQUIRY_std_len;
+
+ DBG (10, "init_inquire: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, INQUIRY_code);
+ set_IN_return_size (cmd, inLen);
+ set_IN_evpd (cmd, 0);
+ set_IN_page_code (cmd, 0);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ if (get_IN_periph_devtype (in) != IN_periph_devtype_scanner){
+ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ get_IN_vendor (in, s->vendor_name);
+ get_IN_product (in, s->model_name);
+ get_IN_version (in, s->version_name);
+
+ s->vendor_name[8] = 0;
+ s->model_name[16] = 0;
+ s->version_name[4] = 0;
+
+ /* gobble trailing spaces */
+ for (i = 7; s->vendor_name[i] == ' ' && i >= 0; i--)
+ s->vendor_name[i] = 0;
+ for (i = 15; s->model_name[i] == ' ' && i >= 0; i--)
+ s->model_name[i] = 0;
+ for (i = 3; s->version_name[i] == ' ' && i >= 0; i--)
+ s->version_name[i] = 0;
+
+ if (strcmp ("FUJITSU", s->vendor_name)) {
+ DBG (5, "The device at '%s' is reported to be made by '%s'\n", s->device_name, s->vendor_name);
+ DBG (5, "This backend only supports Fujitsu products.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "init_inquire: Found %s scanner %s version %s at %s\n",
+ s->vendor_name, s->model_name, s->version_name, s->device_name);
+
+ /*some scanners list random data here*/
+ DBG (15, "inquiry options\n");
+
+ s->color_raster_offset = get_IN_color_offset(in);
+ DBG (15, " color offset: %d lines\n",s->color_raster_offset);
+
+ /* FIXME: we dont store all of these? */
+ DBG (15, " long gray scan: %d\n",get_IN_long_gray(in));
+ DBG (15, " long color scan: %d\n",get_IN_long_color(in));
+
+ DBG (15, " emulation mode: %d\n",get_IN_emulation(in));
+ DBG (15, " CMP/CGA: %d\n",get_IN_cmp_cga(in));
+ DBG (15, " background back: %d\n",get_IN_bg_back(in));
+ DBG (15, " background front: %d\n",get_IN_bg_front(in));
+ DBG (15, " background fb: %d\n",get_IN_bg_fb(in));
+ DBG (15, " back only scan: %d\n",get_IN_has_back(in));
+
+ s->duplex_raster_offset = get_IN_duplex_offset(in);
+ DBG (15, " duplex offset: %d lines\n",s->duplex_raster_offset);
+
+ DBG (10, "init_inquire: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Use INQUIRY VPD to setup more detail about the scanner
+ */
+static SANE_Status
+init_vpd (struct fujitsu *s)
+{
+ SANE_Status ret;
+
+ unsigned char cmd[INQUIRY_len];
+ size_t cmdLen = INQUIRY_len;
+
+ unsigned char in[INQUIRY_vpd_len];
+ size_t inLen = INQUIRY_vpd_len;
+
+ DBG (10, "init_vpd: start\n");
+
+ /* get EVPD */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, INQUIRY_code);
+ set_IN_return_size (cmd, inLen);
+ set_IN_evpd (cmd, 1);
+ set_IN_page_code (cmd, 0xf0);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ /* M3099 gives all data, but wrong length */
+ if (strstr (s->model_name, "M3099")
+ && (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF)
+ && get_IN_page_length (in) == 0x19){
+ DBG (5, "init_vpd: M3099 repair\n");
+ set_IN_page_length(in,0x5f);
+ }
+
+ /* M3097G has short vpd, fill in missing part */
+ else if (strstr (s->model_name, "M3097G")
+ && (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF)
+ && get_IN_page_length (in) == 0x19){
+ unsigned char vpd3097g[] = {
+0, 0,
+0xc2, 0x08, 0, 0, 0, 0, 0, 0, 0xed, 0xbf, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0xff, 0xff, 0xff, 0, 0x45, 0x35, 0, 0xe0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0
+ };
+ DBG (5, "init_vpd: M3097G repair\n");
+ set_IN_page_length(in,0x5f);
+ memcpy(in+0x1e,vpd3097g,sizeof(vpd3097g));
+
+ /*IPC*/
+ if(strstr (s->model_name, "i")){
+ DBG (5, "init_vpd: M3097G IPC repair\n");
+
+ /*subwin cmd*/
+ in[0x2b] = 1;
+
+ /*rif/dtc/sdtc/outline/emph/sep/mirr/wlf*/
+ in[0x58] = 0xff;
+
+ /*subwin/diffusion*/
+ in[0x59] = 0xc0;
+ }
+
+ /*CMP*/
+ if(strstr (s->model_name, "m")){
+ DBG (5, "init_vpd: M3097G CMP repair\n");
+
+ /*4megs*/
+ in[0x23] = 0x40;
+
+ /*mh/mr/mmr*/
+ in[0x5a] = 0xe0;
+ }
+ }
+
+ DBG (15, "init_vpd: length=%0x\n",get_IN_page_length (in));
+
+ /* This scanner supports vital product data.
+ * Use this data to set dpi-lists etc. */
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+
+ DBG (15, "standard options\n");
+
+ s->basic_x_res = get_IN_basic_x_res (in);
+ DBG (15, " basic x res: %d dpi\n",s->basic_x_res);
+
+ s->basic_y_res = get_IN_basic_y_res (in);
+ DBG (15, " basic y res: %d dpi\n",s->basic_y_res);
+
+ s->step_x_res[MODE_LINEART] = get_IN_step_x_res (in);
+ DBG (15, " step x res: %d dpi\n", s->step_x_res[MODE_LINEART]);
+
+ s->step_y_res[MODE_LINEART] = get_IN_step_y_res (in);
+ DBG (15, " step y res: %d dpi\n", s->step_y_res[MODE_LINEART]);
+
+ s->max_x_res = get_IN_max_x_res (in);
+ DBG (15, " max x res: %d dpi\n", s->max_x_res);
+
+ s->max_y_res = get_IN_max_y_res (in);
+ DBG (15, " max y res: %d dpi\n", s->max_y_res);
+
+ s->min_x_res = get_IN_min_x_res (in);
+ DBG (15, " min x res: %d dpi\n", s->min_x_res);
+
+ s->min_y_res = get_IN_min_y_res (in);
+ DBG (15, " min y res: %d dpi\n", s->min_y_res);
+
+ /* some scanners list B&W resolutions. */
+ s->std_res[0] = get_IN_std_res_60 (in);
+ DBG (15, " 60 dpi: %d\n", s->std_res[0]);
+
+ s->std_res[1] = get_IN_std_res_75 (in);
+ DBG (15, " 75 dpi: %d\n", s->std_res[1]);
+
+ s->std_res[2] = get_IN_std_res_100 (in);
+ DBG (15, " 100 dpi: %d\n", s->std_res[2]);
+
+ s->std_res[3] = get_IN_std_res_120 (in);
+ DBG (15, " 120 dpi: %d\n", s->std_res[3]);
+
+ s->std_res[4] = get_IN_std_res_150 (in);
+ DBG (15, " 150 dpi: %d\n", s->std_res[4]);
+
+ s->std_res[5] = get_IN_std_res_160 (in);
+ DBG (15, " 160 dpi: %d\n", s->std_res[5]);
+
+ s->std_res[6] = get_IN_std_res_180 (in);
+ DBG (15, " 180 dpi: %d\n", s->std_res[6]);
+
+ s->std_res[7] = get_IN_std_res_200 (in);
+ DBG (15, " 200 dpi: %d\n", s->std_res[7]);
+
+ s->std_res[8] = get_IN_std_res_240 (in);
+ DBG (15, " 240 dpi: %d\n", s->std_res[8]);
+
+ s->std_res[9] = get_IN_std_res_300 (in);
+ DBG (15, " 300 dpi: %d\n", s->std_res[9]);
+
+ s->std_res[10] = get_IN_std_res_320 (in);
+ DBG (15, " 320 dpi: %d\n", s->std_res[10]);
+
+ s->std_res[11] = get_IN_std_res_400 (in);
+ DBG (15, " 400 dpi: %d\n", s->std_res[11]);
+
+ s->std_res[12] = get_IN_std_res_480 (in);
+ DBG (15, " 480 dpi: %d\n", s->std_res[12]);
+
+ s->std_res[13] = get_IN_std_res_600 (in);
+ DBG (15, " 600 dpi: %d\n", s->std_res[13]);
+
+ s->std_res[14] = get_IN_std_res_800 (in);
+ DBG (15, " 800 dpi: %d\n", s->std_res[14]);
+
+ s->std_res[15] = get_IN_std_res_1200 (in);
+ DBG (15, " 1200 dpi: %d\n", s->std_res[15]);
+
+ /* maximum window width and length are reported in basic units.*/
+ s->max_x_basic = get_IN_window_width(in);
+ DBG(15, " max width: %2.2f inches\n",(float)s->max_x_basic/s->basic_x_res);
+
+ s->max_y_basic = get_IN_window_length(in);
+ DBG(15, " max length: %2.2f inches\n",(float)s->max_y_basic/s->basic_y_res);
+
+ /* known modes */
+ s->can_overflow = get_IN_overflow(in);
+ DBG (15, " overflow: %d\n", s->can_overflow);
+
+ s->can_mode[MODE_LINEART] = get_IN_monochrome (in);
+ DBG (15, " monochrome: %d\n", s->can_mode[MODE_LINEART]);
+
+ s->can_mode[MODE_HALFTONE] = get_IN_half_tone (in);
+ DBG (15, " halftone: %d\n", s->can_mode[MODE_HALFTONE]);
+
+ s->can_mode[MODE_GRAYSCALE] = get_IN_multilevel (in);
+ DBG (15, " grayscale: %d\n", s->can_mode[MODE_GRAYSCALE]);
+
+ DBG (15, " color_monochrome: %d\n", get_IN_monochrome_rgb(in));
+ DBG (15, " color_halftone: %d\n", get_IN_half_tone_rgb(in));
+
+ s->can_mode[MODE_COLOR] = get_IN_multilevel_rgb (in);
+ DBG (15, " color_grayscale: %d\n", s->can_mode[MODE_COLOR]);
+
+ /* now we look at vendor specific data */
+ if (get_IN_page_length (in) >= 0x5f) {
+
+ DBG (15, "vendor options\n");
+
+ s->has_adf = get_IN_adf(in);
+ DBG (15, " adf: %d\n", s->has_adf);
+
+ s->has_flatbed = get_IN_flatbed(in);
+ DBG (15, " flatbed: %d\n", s->has_flatbed);
+
+ s->has_transparency = get_IN_transparency(in);
+ DBG (15, " transparency: %d\n", s->has_transparency);
+
+ s->has_duplex = get_IN_duplex(in);
+ s->has_back = s->has_duplex;
+ DBG (15, " duplex: %d\n", s->has_duplex);
+
+ s->has_endorser_b = get_IN_endorser_b(in);
+ DBG (15, " back endorser: %d\n", s->has_endorser_b);
+
+ s->has_barcode = get_IN_barcode(in);
+ DBG (15, " barcode: %d\n", s->has_barcode);
+
+ s->has_operator_panel = get_IN_operator_panel(in);
+ DBG (15, " operator panel: %d\n", s->has_operator_panel);
+
+ s->has_endorser_f = get_IN_endorser_f(in);
+ DBG (15, " front endorser: %d\n", s->has_endorser_f);
+
+ DBG (15, " multi-purpose stacker: %d\n", get_IN_mp_stacker(in));
+
+ DBG (15, " prepick: %d\n", get_IN_prepick(in));
+ DBG (15, " mf detect: %d\n", get_IN_mf_detect(in));
+
+ s->has_paper_protect = get_IN_paperprot(in);
+ DBG (15, " paper protection: %d\n", s->has_paper_protect);
+
+ s->adbits = get_IN_adbits(in);
+ DBG (15, " A/D bits: %d\n",s->adbits);
+
+ s->buffer_bytes = get_IN_buffer_bytes(in);
+ DBG (15, " buffer bytes: %d\n",s->buffer_bytes);
+
+ DBG (15, "Standard commands\n");
+
+ /* std scsi command support byte 26*/
+ s->has_cmd_msen10 = get_IN_has_cmd_msen10(in);
+ DBG (15, " mode_sense_10 cmd: %d\n", s->has_cmd_msen10);
+
+ s->has_cmd_msel10 = get_IN_has_cmd_msel10(in);
+ DBG (15, " mode_select_10 cmd: %d\n", s->has_cmd_msel10);
+
+ /* std scsi command support byte 27*/
+ s->has_cmd_lsen = get_IN_has_cmd_lsen(in);
+ DBG (15, " log_sense cmd: %d\n", s->has_cmd_lsen);
+
+ s->has_cmd_lsel = get_IN_has_cmd_lsel(in);
+ DBG (15, " log_select cmd: %d\n", s->has_cmd_lsel);
+
+ s->has_cmd_change = get_IN_has_cmd_change(in);
+ DBG (15, " change cmd: %d\n", s->has_cmd_change);
+
+ s->has_cmd_rbuff = get_IN_has_cmd_rbuff(in);
+ DBG (15, " read_buffer cmd: %d\n", s->has_cmd_rbuff);
+
+ s->has_cmd_wbuff = get_IN_has_cmd_wbuff(in);
+ DBG (15, " write_buffer cmd: %d\n", s->has_cmd_wbuff);
+
+ s->has_cmd_cav = get_IN_has_cmd_cav(in);
+ DBG (15, " copy_and_verify cmd: %d\n", s->has_cmd_cav);
+
+ s->has_cmd_comp = get_IN_has_cmd_comp(in);
+ DBG (15, " compare cmd: %d\n", s->has_cmd_comp);
+
+ s->has_cmd_gdbs = get_IN_has_cmd_gdbs(in);
+ DBG (15, " get_d_b_status cmd: %d\n", s->has_cmd_gdbs);
+
+ /* std scsi command support byte 28*/
+ s->has_cmd_op = get_IN_has_cmd_op(in);
+ DBG (15, " object_pos cmd: %d\n", s->has_cmd_op);
+
+ s->has_cmd_send = get_IN_has_cmd_send(in);
+ DBG (15, " send cmd: %d\n", s->has_cmd_send);
+
+ s->has_cmd_read = get_IN_has_cmd_read(in);
+ DBG (15, " read cmd: %d\n", s->has_cmd_read);
+
+ s->has_cmd_gwin = get_IN_has_cmd_gwin(in);
+ DBG (15, " get_window cmd: %d\n", s->has_cmd_gwin);
+
+ s->has_cmd_swin = get_IN_has_cmd_swin(in);
+ DBG (15, " set_window cmd: %d\n", s->has_cmd_swin);
+
+ s->has_cmd_sdiag = get_IN_has_cmd_sdiag(in);
+ DBG (15, " send_diag cmd: %d\n", s->has_cmd_sdiag);
+
+ s->has_cmd_rdiag = get_IN_has_cmd_rdiag(in);
+ DBG (15, " read_diag cmd: %d\n", s->has_cmd_rdiag);
+
+ s->has_cmd_scan = get_IN_has_cmd_scan(in);
+ DBG (15, " scan cmd: %d\n", s->has_cmd_scan);
+
+ /* std scsi command support byte 29*/
+ s->has_cmd_msen6 = get_IN_has_cmd_msen6(in);
+ DBG (15, " mode_sense_6 cmd: %d\n", s->has_cmd_msen6);
+
+ s->has_cmd_copy = get_IN_has_cmd_copy(in);
+ DBG (15, " copy cmd: %d\n", s->has_cmd_copy);
+
+ s->has_cmd_rel = get_IN_has_cmd_rel(in);
+ DBG (15, " release cmd: %d\n", s->has_cmd_rel);
+
+ s->has_cmd_runit = get_IN_has_cmd_runit(in);
+ DBG (15, " reserve_unit cmd: %d\n", s->has_cmd_runit);
+
+ s->has_cmd_msel6 = get_IN_has_cmd_msel6(in);
+ DBG (15, " mode_select_6 cmd: %d\n", s->has_cmd_msel6);
+
+ s->has_cmd_inq = get_IN_has_cmd_inq(in);
+ DBG (15, " inquiry cmd: %d\n", s->has_cmd_inq);
+
+ s->has_cmd_rs = get_IN_has_cmd_rs(in);
+ DBG (15, " request_sense cmd: %d\n", s->has_cmd_rs);
+
+ s->has_cmd_tur = get_IN_has_cmd_tur(in);
+ DBG (15, " test_unit_ready cmd: %d\n", s->has_cmd_tur);
+
+ /* vendor added scsi command support */
+ /* FIXME: there are more of these... */
+ DBG (15, "Vendor commands\n");
+
+ s->has_cmd_subwindow = get_IN_has_cmd_subwindow(in);
+ DBG (15, " subwindow cmd: %d\n", s->has_cmd_subwindow);
+
+ s->has_cmd_endorser = get_IN_has_cmd_endorser(in);
+ DBG (15, " endorser cmd: %d\n", s->has_cmd_endorser);
+
+ s->has_cmd_hw_status = get_IN_has_cmd_hw_status (in);
+ DBG (15, " hardware status cmd: %d\n", s->has_cmd_hw_status);
+
+ s->has_cmd_hw_status_2 = get_IN_has_cmd_hw_status_2 (in);
+ DBG (15, " hardware status 2 cmd: %d\n", s->has_cmd_hw_status_2);
+
+ s->has_cmd_hw_status_3 = get_IN_has_cmd_hw_status_3 (in);
+ DBG (15, " hardware status 3 cmd: %d\n", s->has_cmd_hw_status_3);
+
+ s->has_cmd_scanner_ctl = get_IN_has_cmd_scanner_ctl(in);
+ DBG (15, " scanner control cmd: %d\n", s->has_cmd_scanner_ctl);
+
+ s->has_cmd_device_restart = get_IN_has_cmd_device_restart(in);
+ DBG (15, " device restart cmd: %d\n", s->has_cmd_device_restart);
+
+ /* get threshold, brightness and contrast ranges. */
+ s->brightness_steps = get_IN_brightness_steps(in);
+ DBG (15, " brightness steps: %d\n", s->brightness_steps);
+
+ s->threshold_steps = get_IN_threshold_steps(in);
+ DBG (15, " threshold steps: %d\n", s->threshold_steps);
+
+ s->contrast_steps = get_IN_contrast_steps(in);
+ DBG (15, " contrast steps: %d\n", s->contrast_steps);
+
+ /* dither/gamma patterns */
+ s->num_internal_gamma = get_IN_num_gamma_internal (in);
+ DBG (15, " built in gamma patterns: %d\n", s->num_internal_gamma);
+
+ s->num_download_gamma = get_IN_num_gamma_download (in);
+ DBG (15, " download gamma patterns: %d\n", s->num_download_gamma);
+
+ s->num_internal_dither = get_IN_num_dither_internal (in);
+ DBG (15, " built in dither patterns: %d\n", s->num_internal_dither);
+
+ s->num_download_dither = get_IN_num_dither_download (in);
+ DBG (15, " download dither patterns: %d\n", s->num_download_dither);
+
+ /* ipc functions */
+ s->has_rif = get_IN_ipc_bw_rif (in);
+ DBG (15, " RIF: %d\n", s->has_rif);
+
+ s->has_dtc = get_IN_ipc_dtc(in);
+ DBG (15, " DTC (AutoI): %d\n", s->has_dtc);
+
+ s->has_sdtc = get_IN_ipc_sdtc(in);
+ DBG (15, " SDTC (AutoII): %d\n", s->has_sdtc);
+
+ s->has_outline = get_IN_ipc_outline_extraction (in);
+ DBG (15, " outline extraction: %d\n", s->has_outline);
+
+ s->has_emphasis = get_IN_ipc_image_emphasis (in);
+ DBG (15, " image emphasis: %d\n", s->has_emphasis);
+
+ s->has_autosep = get_IN_ipc_auto_separation (in);
+ DBG (15, " automatic separation: %d\n", s->has_autosep);
+
+ s->has_mirroring = get_IN_ipc_mirroring (in);
+ DBG (15, " mirror image: %d\n", s->has_mirroring);
+
+ s->has_wl_follow = get_IN_ipc_wl_follow (in);
+ DBG (15, " white level follower: %d\n", s->has_wl_follow);
+
+ /* byte 58 */
+ s->has_subwindow = get_IN_ipc_subwindow (in);
+ DBG (15, " subwindow: %d\n", s->has_subwindow);
+
+ s->has_diffusion = get_IN_ipc_diffusion (in);
+ DBG (15, " diffusion: %d\n", s->has_diffusion);
+
+ s->has_ipc3 = get_IN_ipc_ipc3 (in);
+ DBG (15, " ipc3: %d\n", s->has_ipc3);
+
+ s->has_rotation = get_IN_ipc_rotation (in);
+ DBG (15, " rotation: %d\n", s->has_rotation);
+
+ s->has_hybrid_crop_deskew = get_IN_ipc_hybrid_crop_deskew(in);
+ DBG (15, " hybrid crop deskew: %d\n", s->has_hybrid_crop_deskew);
+
+ DBG (15, " ipc2 byte 67: %d\n", get_IN_ipc_ipc2_byte67(in));
+
+ /* compression modes */
+ s->has_comp_MH = get_IN_compression_MH (in);
+ DBG (15, " compression MH: %d\n", s->has_comp_MH);
+
+ s->has_comp_MR = get_IN_compression_MR (in);
+ DBG (15, " compression MR: %d\n", s->has_comp_MR);
+
+ s->has_comp_MMR = get_IN_compression_MMR (in);
+ DBG (15, " compression MMR: %d\n", s->has_comp_MMR);
+
+ s->has_comp_JBIG = get_IN_compression_JBIG (in);
+ DBG (15, " compression JBIG: %d\n", s->has_comp_JBIG);
+
+ s->has_comp_JPG1 = get_IN_compression_JPG_BASE (in);
+ DBG (15, " compression JPG1: %d\n", s->has_comp_JPG1);
+#ifndef SANE_FRAME_JPEG
+ DBG (15, " (Disabled)\n");
+ s->has_comp_JPG1 = 0;
+#endif
+
+ s->has_comp_JPG2 = get_IN_compression_JPG_EXT (in);
+ DBG (15, " compression JPG2: %d\n", s->has_comp_JPG2);
+
+ s->has_comp_JPG3 = get_IN_compression_JPG_INDEP (in);
+ DBG (15, " compression JPG3: %d\n", s->has_comp_JPG3);
+
+ /* FIXME: we dont store these? */
+ DBG (15, " back endorser mech: %d\n", get_IN_endorser_b_mech(in));
+ DBG (15, " back endorser stamp: %d\n", get_IN_endorser_b_stamp(in));
+ DBG (15, " back endorser elec: %d\n", get_IN_endorser_b_elec(in));
+ DBG (15, " endorser max id: %d\n", get_IN_endorser_max_id(in));
+
+ DBG (15, " front endorser mech: %d\n", get_IN_endorser_f_mech(in));
+ DBG (15, " front endorser stamp: %d\n", get_IN_endorser_f_stamp(in));
+ DBG (15, " front endorser elec: %d\n", get_IN_endorser_f_elec(in));
+
+ s->endorser_type_b = get_IN_endorser_b_type(in);
+ DBG (15, " back endorser type: %d\n", s->endorser_type_b);
+
+ s->endorser_type_f = get_IN_endorser_f_type(in);
+ DBG (15, " back endorser type: %d\n", s->endorser_type_f);
+
+ /*not all scanners go this far*/
+ if (get_IN_page_length (in) >= 0x67-5) {
+ DBG (15, " connection type: %d\n", get_IN_connection(in));
+
+ DBG (15, " endorser ext: %d\n", get_IN_endorser_type_ext(in));
+ DBG (15, " endorser pr_b: %d\n", get_IN_endorser_pre_back(in));
+ DBG (15, " endorser pr_f: %d\n", get_IN_endorser_pre_front(in));
+ DBG (15, " endorser po_b: %d\n", get_IN_endorser_post_back(in));
+ DBG (15, " endorser po_f: %d\n", get_IN_endorser_post_front(in));
+
+ s->os_x_basic = get_IN_x_overscan_size(in);
+ DBG (15, " horizontal overscan: %d\n", s->os_x_basic);
+
+ s->os_y_basic = get_IN_y_overscan_size(in);
+ DBG (15, " vertical overscan: %d\n", s->os_y_basic);
+ }
+
+ if (get_IN_page_length (in) >= 0x70-5) {
+ DBG (15, " default bg adf b: %d\n", get_IN_default_bg_adf_b(in));
+ DBG (15, " default bg adf f: %d\n", get_IN_default_bg_adf_f(in));
+ DBG (15, " default bg fb: %d\n", get_IN_default_bg_fb(in));
+
+ DBG (15, " auto color: %d\n", get_IN_auto_color(in));
+ DBG (15, " blank skip: %d\n", get_IN_blank_skip(in));
+ DBG (15, " multi image: %d\n", get_IN_multi_image(in));
+ DBG (15, " f b type indep: %d\n", get_IN_f_b_type_indep(in));
+ DBG (15, " f b res indep: %d\n", get_IN_f_b_res_indep(in));
+
+ DBG (15, " dropout spec: %d\n", get_IN_dropout_spec(in));
+ DBG (15, " dropout non: %d\n", get_IN_dropout_non(in));
+ DBG (15, " dropout white: %d\n", get_IN_dropout_white(in));
+
+ DBG (15, " skew check: %d\n", get_IN_skew_check(in));
+ DBG (15, " new feed roller: %d\n", get_IN_new_fd_roll(in));
+ }
+
+ if (get_IN_page_length (in) > 0x70-5) {
+
+ DBG (15, " paper count: %d\n", get_IN_paper_count(in));
+ DBG (15, " paper number: %d\n", get_IN_paper_number(in));
+ DBG (15, " ext send to: %d\n", get_IN_ext_send_to(in));
+ DBG (15, " staple det: %d\n", get_IN_staple_det(in));
+ DBG (15, " pause host: %d\n", get_IN_pause_host(in));
+ DBG (15, " pause panel: %d\n", get_IN_pause_panel(in));
+ DBG (15, " pause conf: %d\n", get_IN_pause_conf(in));
+ DBG (15, " hq print: %d\n", get_IN_hq_print(in));
+
+ DBG (15, " ext GHS len: %d\n", get_IN_ext_GHS_len(in));
+
+ DBG (15, " smbc func: %d\n", get_IN_smbc_func(in));
+ DBG (15, " imprint chk b: %d\n", get_IN_imprint_chk_b(in));
+ DBG (15, " imprint chk f: %d\n", get_IN_imprint_chk_f(in));
+ DBG (15, " force w bg: %d\n", get_IN_force_w_bg(in));
+ DBG (15, " mf recover lvl: %d\n", get_IN_mf_recover_lvl(in));
+
+ DBG (15, " first read time: %d\n", get_IN_first_read_time(in));
+ DBG (15, " div scanning: %d\n", get_IN_div_scanning(in));
+ DBG (15, " start job: %d\n", get_IN_start_job(in));
+ DBG (15, " lifetime log: %d\n", get_IN_lifetime_log(in));
+ DBG (15, " imff save rest: %d\n", get_IN_imff_save_rest(in));
+ DBG (15, " wide scsi type: %d\n", get_IN_wide_scsi_type(in));
+
+ DBG (15, " lut hybrid crop: %d\n", get_IN_lut_hybrid_crop(in));
+ DBG (15, " over under amt: %d\n", get_IN_over_under_amt(in));
+ DBG (15, " rgb lut: %d\n", get_IN_rgb_lut(in));
+ DBG (15, " num lut dl: %d\n", get_IN_num_lut_dl(in));
+
+ DBG (15, " sync next feed: %d\n", get_IN_sync_next_feed(in));
+ }
+
+ ret = SANE_STATUS_GOOD;
+ }
+
+ /*FIXME no vendor vpd, set some defaults? */
+ else{
+ DBG (5, "init_vpd: Your scanner supports only partial VPD?\n");
+ DBG (5, "init_vpd: Please contact kitno455 at gmail dot com\n");
+ DBG (5, "init_vpd: with details of your scanner model.\n");
+ ret = SANE_STATUS_INVAL;
+ }
+ }
+ /*FIXME no vpd, set some defaults? */
+ else{
+ DBG (5, "init_vpd: Your scanner does not support VPD?\n");
+ DBG (5, "init_vpd: Please contact kitno455 at gmail dot com\n");
+ DBG (5, "init_vpd: with details of your scanner model.\n");
+ }
+
+ DBG (10, "init_vpd: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+init_ms(struct fujitsu *s)
+{
+ int ret;
+ int oldDbg=0;
+
+ unsigned char cmd[MODE_SENSE_len];
+ size_t cmdLen = MODE_SENSE_len;
+
+ unsigned char in[MODE_SENSE_data_len];
+ size_t inLen = MODE_SENSE_data_len;
+
+ DBG (10, "init_ms: start\n");
+
+ if(!s->has_cmd_msen6){
+ DBG (10, "init_ms: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* some of the following probes will produce errors */
+ /* so we reduce the dbg level to reduce the noise */
+ /* however, if user builds with NDEBUG, we can't do that */
+ /* so we protect the code with the following macro */
+ IF_DBG( oldDbg=DBG_LEVEL; )
+ IF_DBG( if(DBG_LEVEL < 35){ DBG_LEVEL = 0; } )
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SENSE_code);
+ set_MSEN_xfer_length (cmd, inLen);
+
+ if(s->has_MS_autocolor){
+ DBG (35, "init_ms: autocolor\n");
+ set_MSEN_pc(cmd, MS_pc_autocolor);
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_autocolor=0;
+ }
+ }
+
+ if(s->has_MS_prepick){
+ DBG (35, "init_ms: prepick\n");
+ set_MSEN_pc(cmd, MS_pc_prepick);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_prepick=0;
+ }
+ }
+
+ if(s->has_MS_sleep){
+ DBG (35, "init_ms: sleep\n");
+ set_MSEN_pc(cmd, MS_pc_sleep);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_sleep=0;
+ }
+ }
+
+ if(s->has_MS_duplex){
+ DBG (35, "init_ms: duplex\n");
+ set_MSEN_pc(cmd, MS_pc_duplex);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_duplex=0;
+ }
+ }
+
+ if(s->has_MS_rand){
+ DBG (35, "init_ms: rand\n");
+ set_MSEN_pc(cmd, MS_pc_rand);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_rand=0;
+ }
+ }
+
+ if(s->has_MS_bg){
+ DBG (35, "init_ms: bg\n");
+ set_MSEN_pc(cmd, MS_pc_bg);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_bg=0;
+ }
+ }
+
+ if(s->has_MS_df){
+ DBG (35, "init_ms: df\n");
+ set_MSEN_pc(cmd, MS_pc_df);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_df=0;
+ }
+ }
+
+ if(s->has_MS_dropout){
+ DBG (35, "init_ms: dropout\n");
+ set_MSEN_pc(cmd, MS_pc_dropout);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_dropout=0;
+ }
+ }
+
+ if(s->has_MS_buff){
+ DBG (35, "init_ms: buffer\n");
+ set_MSEN_pc(cmd, MS_pc_buff);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_buff=0;
+ }
+ }
+
+ if(s->has_MS_auto){
+ DBG (35, "init_ms: auto\n");
+ set_MSEN_pc(cmd, MS_pc_auto);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_auto=0;
+ }
+ }
+
+ if(s->has_MS_lamp){
+ DBG (35, "init_ms: lamp\n");
+ set_MSEN_pc(cmd, MS_pc_lamp);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_lamp=0;
+ }
+ }
+
+ if(s->has_MS_jobsep){
+ DBG (35, "init_ms: jobsep\n");
+ set_MSEN_pc(cmd, MS_pc_jobsep);
+ inLen = MODE_SENSE_data_len;
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if(ret != SANE_STATUS_GOOD){
+ s->has_MS_jobsep=0;
+ }
+ }
+
+ IF_DBG (DBG_LEVEL = oldDbg;)
+
+ DBG (15, " autocolor: %d\n", s->has_MS_autocolor);
+ DBG (15, " prepick: %d\n", s->has_MS_prepick);
+ DBG (15, " sleep: %d\n", s->has_MS_sleep);
+ DBG (15, " duplex: %d\n", s->has_MS_duplex);
+ DBG (15, " rand: %d\n", s->has_MS_rand);
+ DBG (15, " bg: %d\n", s->has_MS_bg);
+ DBG (15, " df: %d\n", s->has_MS_df);
+ DBG (15, " dropout: %d\n", s->has_MS_dropout);
+ DBG (15, " buff: %d\n", s->has_MS_buff);
+ DBG (15, " auto: %d\n", s->has_MS_auto);
+ DBG (15, " lamp: %d\n", s->has_MS_lamp);
+ DBG (15, " jobsep: %d\n", s->has_MS_jobsep);
+
+ DBG (10, "init_ms: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * get model specific info that is not in vpd, and correct
+ * errors in vpd data. struct is already initialized to 0.
+ */
+static SANE_Status
+init_model (struct fujitsu *s)
+{
+ int i;
+
+ DBG (10, "init_model: start\n");
+
+ /* for most scanners these are good defaults */
+ if(s->can_mode[MODE_LINEART]
+ || s->can_mode[MODE_HALFTONE]
+ || s->can_mode[MODE_GRAYSCALE]
+ ){
+ s->has_vuid_mono = 1;
+ }
+ if(s->can_mode[MODE_COLOR]){
+ s->has_vuid_color = 1;
+ }
+
+ for(i=MODE_HALFTONE;i<=MODE_COLOR;i++){
+ s->step_x_res[i] = s->step_x_res[MODE_LINEART];
+ s->step_y_res[i] = s->step_y_res[MODE_LINEART];
+ }
+
+ s->reverse_by_mode[MODE_LINEART] = 0;
+ s->reverse_by_mode[MODE_HALFTONE] = 0;
+ s->reverse_by_mode[MODE_GRAYSCALE] = 1;
+ s->reverse_by_mode[MODE_COLOR] = 1;
+
+ s->ppl_mod_by_mode[MODE_LINEART] = 8;
+ s->ppl_mod_by_mode[MODE_HALFTONE] = 8;
+ s->ppl_mod_by_mode[MODE_GRAYSCALE] = 1;
+ s->ppl_mod_by_mode[MODE_COLOR] = 1;
+
+ /* if scanner has built-in gamma tables, we use the first one (0) */
+ /* otherwise, we use the first downloaded one (0x80) */
+ /* note that you may NOT need to send the table to use it? */
+ if (!s->num_internal_gamma && s->num_download_gamma){
+ s->window_gamma = 0x80;
+ }
+
+ /* older scanners would enable their highest */
+ /* IPC mode by default. Newer scanners don't, */
+ /* so we go ahead and turn it on. */
+ if (s->has_sdtc)
+ s->ipc_mode = WD_ipc_SDTC;
+ else if (s->has_dtc)
+ s->ipc_mode = WD_ipc_DTC;
+
+ /* endorser type tells string length (among other things) */
+ if(s->has_endorser_b){
+ /*old-style is 40 bytes*/
+ if(s->endorser_type_b == ET_OLD){
+ s->endorser_string_len = 40;
+ }
+ /*short new style is 60 bytes*/
+ else if(s->endorser_type_b == ET_30){
+ s->endorser_string_len = 60;
+ }
+ /*long new style is 80 bytes*/
+ else if(s->endorser_type_b == ET_40){
+ s->endorser_string_len = 80;
+ }
+ }
+ else if(s->has_endorser_f){
+ /*old-style is 40 bytes*/
+ if(s->endorser_type_f == ET_OLD){
+ s->endorser_string_len = 40;
+ }
+ /*short new style is 60 bytes*/
+ else if(s->endorser_type_f == ET_30){
+ s->endorser_string_len = 60;
+ }
+ /*long new style is 80 bytes*/
+ else if(s->endorser_type_f == ET_40){
+ s->endorser_string_len = 80;
+ }
+ }
+
+ /* convert to 1200dpi units */
+ s->max_x = s->max_x_basic * 1200 / s->basic_x_res;
+ s->max_y = s->max_y_basic * 1200 / s->basic_y_res;
+
+ /* assume these are same as adf, override below */
+ s->max_x_fb = s->max_x;
+ s->max_y_fb = s->max_y;
+
+ /* assume we can do these. we will disable
+ * them at runtime if they cannot */
+ s->has_pixelsize = 1;
+ s->has_MS_autocolor = 1;
+ s->has_MS_prepick = 1;
+ s->has_MS_sleep = 1;
+ s->has_MS_duplex = 1;
+ s->has_MS_rand = 1;
+ s->has_MS_bg = 1;
+ s->has_MS_df = 1;
+ s->has_MS_dropout = 1;
+ s->has_MS_buff = 1;
+ s->has_MS_auto = 1;
+ s->has_MS_lamp = 1;
+ s->has_MS_jobsep = 1;
+
+ /* these two scanners lie about their capabilities,
+ * and/or differ significantly from most other models */
+ if (strstr (s->model_name, "M3091")
+ || strstr (s->model_name, "M3092")) {
+
+ /* lies */
+ s->has_rif = 1;
+ s->has_back = 0;
+ s->adbits = 8;
+ if (strstr (s->model_name, "M3092"))
+ s->has_flatbed = 1;
+
+ /*actually does have res range in non-color modes */
+ for(i=MODE_LINEART;i<MODE_COLOR;i++){
+ s->step_x_res[i] = 1;
+ s->step_y_res[i] = 1;
+ }
+
+ /*but the color mode y list is very limited, only 75, 150, 300 (and 600)*/
+ for(i=0;i<16;i++){
+ s->std_res[i] = 0;
+ }
+ s->std_res[1] = 1;
+ s->std_res[4] = 1;
+ s->std_res[9] = 1;
+
+ /* weirdness */
+ s->has_vuid_3091 = 1;
+ s->has_vuid_color = 0;
+ s->has_vuid_mono = 0;
+
+ s->color_interlace = COLOR_INTERLACE_3091;
+ s->duplex_interlace = DUPLEX_INTERLACE_3091;
+ s->ghs_in_rs = 1;
+ s->window_gamma = 0;
+
+ s->reverse_by_mode[MODE_LINEART] = 1;
+ s->reverse_by_mode[MODE_HALFTONE] = 1;
+ s->reverse_by_mode[MODE_GRAYSCALE] = 0;
+ s->reverse_by_mode[MODE_COLOR] = 0;
+ }
+
+ else if (strstr (s->model_name, "M3093")){
+
+ /* lies */
+ s->has_back = 0;
+ s->adbits = 8;
+
+ /* weirdness */
+ s->duplex_interlace = DUPLEX_INTERLACE_NONE;
+ }
+
+ else if ( strstr (s->model_name, "M309")
+ || strstr (s->model_name, "M409")){
+
+ /* lies */
+ s->adbits = 8;
+ }
+
+ else if (strstr (s->model_name, "fi-4120C2")
+ || strstr (s->model_name, "fi-4220C2") ) {
+
+ /* missing from vpd */
+ s->os_x_basic = 118;
+ s->os_y_basic = 118;
+ s->max_y_fb = 14032;
+ }
+
+ else if (strstr (s->model_name, "fi-4220C")){
+
+ /* missing from vpd */
+ s->max_y_fb = 14032;
+ }
+
+ else if (strstr (s->model_name,"fi-5110C")){
+
+ /* missing from vpd */
+ s->os_x_basic = 147;
+ s->os_y_basic = 147;
+ }
+
+ else if (strstr (s->model_name,"fi-5110EOX")){
+
+ /* weirdness */
+ s->cropping_mode = CROP_ABSOLUTE;
+ }
+
+ else if (strstr (s->model_name,"fi-5220C")){
+
+ /* missing from vpd */
+ s->max_x_fb = 10764;
+ s->max_y_fb = 14032;
+ }
+
+ else if (strstr (s->model_name,"fi-5530")
+ || strstr (s->model_name,"fi-5650")
+ || strstr (s->model_name,"fi-5750")){
+
+ /* lies - usb only */
+ if(s->connection == CONNECTION_USB)
+ s->adbits = 8;
+ }
+
+ /* some firmware versions use capital f? */
+ else if (strstr (s->model_name, "Fi-4990")
+ || strstr (s->model_name, "fi-4990") ) {
+
+ /* weirdness */
+ s->duplex_interlace = DUPLEX_INTERLACE_NONE;
+ s->color_interlace = COLOR_INTERLACE_RRGGBB;
+
+ s->ppl_mod_by_mode[MODE_LINEART] = 32;
+ s->ppl_mod_by_mode[MODE_HALFTONE] = 32;
+ s->ppl_mod_by_mode[MODE_GRAYSCALE] = 4;
+ s->ppl_mod_by_mode[MODE_COLOR] = 4;
+ }
+
+ else if (strstr (s->model_name, "fi-4750") ) {
+ /* weirdness */
+ s->broken_diag_serial = 1;
+ }
+
+ /* some firmware versions use capital f? */
+ else if (strstr (s->model_name, "Fi-4860")
+ || strstr (s->model_name, "fi-4860") ) {
+
+ /* weirdness */
+ s->broken_diag_serial = 1;
+
+ s->ppl_mod_by_mode[MODE_LINEART] = 32;
+ s->ppl_mod_by_mode[MODE_HALFTONE] = 32;
+ s->ppl_mod_by_mode[MODE_GRAYSCALE] = 4;
+ s->ppl_mod_by_mode[MODE_COLOR] = 4;
+ }
+
+ /* also includes the 'Z' models */
+ else if (strstr (s->model_name,"fi-6230")
+ || strstr (s->model_name,"fi-6240")){
+
+ /* missing from vpd */
+ s->max_x_fb = 10488;
+ s->max_y_fb = 14032; /* some scanners can be slightly more? */
+ }
+
+ else if (strstr (s->model_name,"S1500")
+ || strstr (s->model_name,"fi-6110")){
+ /*lies*/
+ s->has_MS_bg=0;
+ s->has_MS_prepick=0;
+ }
+
+ else if (strstr (s->model_name,"fi-6800")
+ || strstr (s->model_name,"fi-5900")){ /* guessing this scanner too */
+ /* missing from vpd */
+ s->has_staple_detect=1; /* may not actually work? */
+ s->has_df_recovery=1;
+ }
+
+ else if (strstr (s->model_name,"iX500")){
+ /* locks up scanner if we try to auto detect */
+ s->has_MS_lamp = 0;
+
+ /* weirdness */
+ s->need_q_table = 1;
+ s->need_diag_preread = 1;
+ s->ppl_mod_by_mode[MODE_COLOR] = 2;
+ s->hopper_before_op = 1;
+ s->no_wait_after_op = 1;
+
+ /* lies */
+ s->adbits = 8;
+
+ /* we have to simulate these in software*/
+ s->can_mode[MODE_LINEART] = 2;
+ s->can_mode[MODE_GRAYSCALE] = 2;
+
+ /* dont bother with this one */
+ s->can_mode[MODE_HALFTONE] = 0;
+ }
+
+ DBG (10, "init_model: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+set_mode (struct fujitsu *s, int mode)
+{
+ int i;
+ /* give the user what they asked for */
+ s->u_mode = mode;
+
+ /* give the scanner the closest mode */
+ for(i=MODE_COLOR;i>=mode;i--){
+ if(s->can_mode[i] == 1){
+ s->s_mode = i;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * set good default user values.
+ * struct is already initialized to 0.
+ */
+static SANE_Status
+init_user (struct fujitsu *s)
+{
+
+ DBG (10, "init_user: start\n");
+
+ /* source */
+ if(s->has_flatbed)
+ s->source = SOURCE_FLATBED;
+ else if(s->has_adf)
+ s->source = SOURCE_ADF_FRONT;
+
+ /* scan mode */
+ if(s->can_mode[MODE_LINEART])
+ set_mode(s,MODE_LINEART);
+ else if(s->can_mode[MODE_HALFTONE])
+ set_mode(s,MODE_HALFTONE);
+ else if(s->can_mode[MODE_GRAYSCALE])
+ set_mode(s,MODE_GRAYSCALE);
+ else if(s->can_mode[MODE_COLOR])
+ set_mode(s,MODE_COLOR);
+
+ /*x res*/
+ s->resolution_x = s->basic_x_res;
+
+ /*y res*/
+ s->resolution_y = s->basic_y_res;
+ if(s->resolution_y > s->resolution_x){
+ s->resolution_y = s->resolution_x;
+ }
+
+ /* page width US-Letter */
+ s->page_width = 8.5 * 1200;
+ if(s->page_width > s->max_x){
+ s->page_width = s->max_x;
+ }
+
+ /* page height US-Letter */
+ s->page_height = 11 * 1200;
+ if(s->page_height > s->max_y){
+ s->page_height = s->max_y;
+ }
+
+ /* bottom-right x */
+ s->br_x = s->page_width;
+
+ /* bottom-right y */
+ s->br_y = s->page_height;
+
+ /* gamma ramp exponent */
+ s->gamma = 1;
+
+ /* safe endorser settings */
+ s->u_endorser_bits=16;
+ s->u_endorser_step=1;
+ s->u_endorser_side=ED_back;
+ if(s->has_endorser_f){
+ s->u_endorser_side=ED_front;
+ }
+ s->u_endorser_dir=DIR_TTB;
+ strcpy((char *)s->u_endorser_string,"%05ud");
+
+ /* inverted logic ipc settings */
+ s->noise_removal = 1;
+ s->bp_filter = 1;
+ s->smoothing = 1;
+
+ /* more recent machines default to this being 'on', *
+ * which causes the scanner to ingest multiple pages *
+ * even when the user only wants one */
+ s->buff_mode = MSEL_OFF;
+
+ DBG (10, "init_user: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function presets the "option" array to blank
+ */
+static SANE_Status
+init_options (struct fujitsu *s)
+{
+ int i;
+
+ DBG (10, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].name = "filler";
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ DBG (10, "init_options: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * send set window repeatedly to color scanners,
+ * searching for valid color interlacing mode
+ */
+static SANE_Status
+init_interlace (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int curr_mode = s->u_mode;
+ int oldDbg=0;
+
+ DBG (10, "init_interlace: start\n");
+
+ if(s->color_interlace != COLOR_INTERLACE_UNK){
+ DBG (10, "init_interlace: already loaded\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ if(!s->has_vuid_color){
+ DBG (10, "init_interlace: color unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* set to color mode first */
+ set_mode(s,MODE_COLOR);
+
+ /* load our own private copy of scan params */
+ ret = update_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "init_interlace: ERROR: cannot update params\n");
+ return ret;
+ }
+
+ /*loop thru all the formats we support*/
+ for(s->color_interlace = COLOR_INTERLACE_RGB;
+ s->color_interlace <= COLOR_INTERLACE_RRGGBB;
+ s->color_interlace++){
+
+ /* some of the following probes will produce errors */
+ /* so we reduce the dbg level to reduce the noise */
+ /* however, if user builds with NDEBUG, we can't do that */
+ /* so we protect the code with the following macro */
+ IF_DBG( oldDbg=DBG_LEVEL; )
+ IF_DBG( if(DBG_LEVEL < 35){ DBG_LEVEL = 0; } )
+
+ ret = set_window(s);
+
+ IF_DBG (DBG_LEVEL = oldDbg;)
+
+ if (ret == SANE_STATUS_GOOD){
+ break;
+ }
+ else{
+ DBG (15, "init_interlace: not %d\n", s->color_interlace);
+ }
+ }
+
+ if (ret != SANE_STATUS_GOOD){
+ DBG (5, "init_interlace: no valid interlacings\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "init_interlace: color_interlace: %d\n",s->color_interlace);
+
+ /* restore mode */
+ set_mode(s,curr_mode);
+
+ DBG (10, "init_interlace: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * send diag query for serial number, and read result back
+ * use it to build a unique name for scanner in s->serial_name
+ */
+static SANE_Status
+init_serial (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ unsigned int sn = 0;
+
+ unsigned char cmd[SEND_DIAGNOSTIC_len]; /*also big enough for READ_DIAG*/
+ size_t cmdLen = SEND_DIAGNOSTIC_len;
+
+ unsigned char out[SD_gdi_len];
+ size_t outLen = SD_gdi_len;
+
+ unsigned char in[RD_gdi_len];
+ size_t inLen = RD_gdi_len;
+
+ DBG (10, "init_serial: start\n");
+
+ if (!s->has_cmd_sdiag || !s->has_cmd_rdiag || s->broken_diag_serial){
+ DBG (5, "init_serial: send/read diag not supported, returning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SEND_DIAGNOSTIC_code);
+ set_SD_slftst(cmd, 0);
+ set_SD_xferlen(cmd, outLen);
+
+ memcpy(out,SD_gdi_string,outLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ DBG (5, "init_serial: send diag error: %d\n", ret);
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_DIAGNOSTIC_code);
+ set_RD_xferlen(cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ DBG (5, "init_serial: read diag error: %d\n", ret);
+ return ret;
+ }
+
+ sn = get_RD_id_serial(in);
+
+ DBG (15, "init_serial: found sn %d\n",sn);
+
+ sprintf(s->serial_name, "%s:%d", s->model_name, sn);
+
+ DBG (15, "init_serial: serial_name: %s\n",s->serial_name);
+
+ DBG (10, "init_serial: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct fujitsu *dev = NULL;
+ struct fujitsu *s = NULL;
+ SANE_Status ret;
+
+ DBG (10, "sane_open: start\n");
+
+ if(fujitsu_devList){
+ DBG (15, "sane_open: searching currently attached scanners\n");
+ }
+ else{
+ DBG (15, "sane_open: no scanners currently attached, attaching\n");
+
+ ret = sane_get_devices(NULL,0);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+ }
+
+ if(!name || !name[0]){
+ DBG (15, "sane_open: no device requested, using default\n");
+ s = fujitsu_devList;
+ }
+ else{
+ DBG (15, "sane_open: device %s requested\n", name);
+
+ for (dev = fujitsu_devList; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, name) == 0
+ || strcmp (dev->device_name, name) == 0) { /*always allow sanei devname*/
+ s = dev;
+ break;
+ }
+ }
+ }
+
+ if (!s) {
+ DBG (5, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "sane_open: device %s found\n", s->sane.name);
+
+ *handle = s;
+
+ /* connect the fd so we can talk to scanner */
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ DBG (10, "sane_open: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * @@ Section 3 - SANE Options functions
+ */
+
+/*
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct fujitsu *s = handle;
+ int i,j;
+ SANE_Option_Descriptor *opt = &s->opt[option];
+
+ DBG (20, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ /* "Mode" group -------------------------------------------------------- */
+ if(option==OPT_STANDARD_GROUP){
+ opt->name = SANE_NAME_STANDARD;
+ opt->title = SANE_TITLE_STANDARD;
+ opt->desc = SANE_DESC_STANDARD;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* source */
+ if(option==OPT_SOURCE){
+ i=0;
+ if(s->has_flatbed){
+ s->source_list[i++]=STRING_FLATBED;
+ }
+ if(s->has_adf){
+ s->source_list[i++]=STRING_ADFFRONT;
+
+ if(s->has_back){
+ s->source_list[i++]=STRING_ADFBACK;
+ }
+ if(s->has_duplex){
+ s->source_list[i++]=STRING_ADFDUPLEX;
+ }
+ }
+ s->source_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_SOURCE;
+ opt->title = SANE_TITLE_SCAN_SOURCE;
+ opt->desc = SANE_DESC_SCAN_SOURCE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->source_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* scan mode */
+ if(option==OPT_MODE){
+ i=0;
+ if(s->can_mode[MODE_LINEART]){
+ s->mode_list[i++]=STRING_LINEART;
+ }
+ if(s->can_mode[MODE_HALFTONE]){
+ s->mode_list[i++]=STRING_HALFTONE;
+ }
+ if(s->can_mode[MODE_GRAYSCALE]){
+ s->mode_list[i++]=STRING_GRAYSCALE;
+ }
+ if(s->can_mode[MODE_COLOR]){
+ s->mode_list[i++]=STRING_COLOR;
+ }
+ s->mode_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* resolution */
+ /* some scanners only support fixed res
+ * build a list of possible choices */
+ if(option==OPT_RES){
+ opt->name = SANE_NAME_SCAN_RESOLUTION;
+ opt->title = SANE_TITLE_SCAN_RESOLUTION;
+ opt->desc = SANE_DESC_SCAN_RESOLUTION;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_DPI;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ if(s->step_x_res[s->s_mode] && s->step_y_res[s->s_mode]){
+ s->res_range.min = s->min_x_res;
+ s->res_range.max = s->max_x_res;
+ s->res_range.quant = s->step_x_res[s->s_mode];
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->res_range;
+ }
+ else{
+ int reses[]
+ = {60,75,100,120,150,160,180,200,240,300,320,400,480,600,800,1200};
+
+ i=0;
+ for(j=0;j<16;j++){
+ if(s->std_res[j]
+ && s->max_x_res >= reses[j] && s->min_x_res <= reses[j]
+ && s->max_y_res >= reses[j] && s->min_y_res <= reses[j]
+ ){
+ s->res_list[++i] = reses[j];
+ }
+ }
+ s->res_list[0] = i;
+
+ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opt->constraint.word_list = s->res_list;
+ }
+ }
+
+ /* "Geometry" group ---------------------------------------------------- */
+ if(option==OPT_GEOMETRY_GROUP){
+ opt->name = SANE_NAME_GEOMETRY;
+ opt->title = SANE_TITLE_GEOMETRY;
+ opt->desc = SANE_DESC_GEOMETRY;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* top-left x */
+ if(option==OPT_TL_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s));
+ s->tl_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_X;
+ opt->title = SANE_TITLE_SCAN_TL_X;
+ opt->desc = SANE_DESC_SCAN_TL_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->tl_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* top-left y */
+ if(option==OPT_TL_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s));
+ s->tl_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_Y;
+ opt->title = SANE_TITLE_SCAN_TL_Y;
+ opt->desc = SANE_DESC_SCAN_TL_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->tl_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right x */
+ if(option==OPT_BR_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_width(s));
+ s->br_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_X;
+ opt->title = SANE_TITLE_SCAN_BR_X;
+ opt->desc = SANE_DESC_SCAN_BR_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->br_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right y */
+ if(option==OPT_BR_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s));
+ s->br_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_Y;
+ opt->title = SANE_TITLE_SCAN_BR_Y;
+ opt->desc = SANE_DESC_SCAN_BR_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->br_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* page width */
+ if(option==OPT_PAGE_WIDTH){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->paper_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
+ s->paper_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_x);
+ s->paper_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_PAGE_WIDTH;
+ opt->title = SANE_TITLE_PAGE_WIDTH;
+ opt->desc = SANE_DESC_PAGE_WIDTH;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->paper_x_range;
+
+ if(s->has_adf){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->source == SOURCE_FLATBED){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* page height */
+ if(option==OPT_PAGE_HEIGHT){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->paper_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
+ s->paper_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y);
+ s->paper_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_PAGE_HEIGHT;
+ opt->title = SANE_TITLE_PAGE_HEIGHT;
+ opt->desc = SANE_DESC_PAGE_HEIGHT;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->paper_y_range;
+
+ if(s->has_adf){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->source == SOURCE_FLATBED){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* "Enhancement" group ------------------------------------------------- */
+ if(option==OPT_ENHANCEMENT_GROUP){
+ opt->name = SANE_NAME_ENHANCEMENT;
+ opt->title = SANE_TITLE_ENHANCEMENT;
+ opt->desc = SANE_DESC_ENHANCEMENT;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* brightness */
+ if(option==OPT_BRIGHTNESS){
+ opt->name = SANE_NAME_BRIGHTNESS;
+ opt->title = SANE_TITLE_BRIGHTNESS;
+ opt->desc = SANE_DESC_BRIGHTNESS;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->brightness_range;
+ s->brightness_range.quant=1;
+
+ /* some have hardware brightness (always 0 to 255?) */
+ /* some use LUT or GT (-127 to +127)*/
+ if (s->brightness_steps || s->num_download_gamma){
+ s->brightness_range.min=-127;
+ s->brightness_range.max=127;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* contrast */
+ if(option==OPT_CONTRAST){
+ opt->name = SANE_NAME_CONTRAST;
+ opt->title = SANE_TITLE_CONTRAST;
+ opt->desc = SANE_DESC_CONTRAST;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->contrast_range;
+ s->contrast_range.quant=1;
+
+ /* some have hardware contrast (always 0 to 255?) */
+ /* some use LUT or GT (-127 to +127)*/
+ if (s->contrast_steps || s->num_download_gamma){
+ s->contrast_range.min=-127;
+ s->contrast_range.max=127;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else {
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* gamma */
+ if(option==OPT_GAMMA){
+ opt->name = "gamma";
+ opt->title = "Gamma function exponent";
+ opt->desc = "Changes intensity of midtones";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->gamma_range;
+
+ /* value ranges from .3 to 5, should be log scale? */
+ s->gamma_range.quant=SANE_FIX(0.01);
+ s->gamma_range.min=SANE_FIX(0.3);
+ s->gamma_range.max=SANE_FIX(5);
+
+ /* scanner has gamma via LUT or GT */
+ /*if (s->num_download_gamma){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else {
+ opt->cap = SANE_CAP_INACTIVE;
+ }*/
+
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*threshold*/
+ if(option==OPT_THRESHOLD){
+ opt->name = SANE_NAME_THRESHOLD;
+ opt->title = SANE_TITLE_THRESHOLD;
+ opt->desc = SANE_DESC_THRESHOLD;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->threshold_range;
+ s->threshold_range.min=0;
+ s->threshold_range.max=s->threshold_steps;
+ s->threshold_range.quant=1;
+
+ if (s->threshold_steps){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->u_mode != MODE_LINEART){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else {
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* =============== common ipc params ================================ */
+ if(option==OPT_RIF){
+ opt->name = "rif";
+ opt->title = "RIF";
+ opt->desc = "Reverse image format";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_rif)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_HT_TYPE){
+ i=0;
+ s->ht_type_list[i++]=STRING_DEFAULT;
+ s->ht_type_list[i++]=STRING_DITHER;
+ s->ht_type_list[i++]=STRING_DIFFUSION;
+ s->ht_type_list[i]=NULL;
+
+ opt->name = "ht-type";
+ opt->title = "Halftone type";
+ opt->desc = "Control type of halftone filter";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->ht_type_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if(s->has_diffusion){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->s_mode != MODE_HALFTONE){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_HT_PATTERN){
+ opt->name = "ht-pattern";
+ opt->title = "Halftone pattern";
+ opt->desc = "Control pattern of halftone filter";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->ht_pattern_range;
+ s->ht_pattern_range.min=0;
+ s->ht_pattern_range.max=s->num_internal_dither - 1;
+ s->ht_pattern_range.quant=1;
+
+ if (s->num_internal_dither){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->s_mode != MODE_HALFTONE){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_OUTLINE){
+ opt->name = "outline";
+ opt->title = "Outline";
+ opt->desc = "Perform outline extraction";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_outline)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_EMPHASIS){
+ opt->name = "emphasis";
+ opt->title = "Emphasis";
+ opt->desc = "Negative to smooth or positive to sharpen image";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->emphasis_range;
+ s->emphasis_range.min=-128;
+ s->emphasis_range.max=127;
+ s->emphasis_range.quant=1;
+
+ if (s->has_emphasis)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SEPARATION){
+ opt->name = "separation";
+ opt->title = "Separation";
+ opt->desc = "Enable automatic separation of image and text";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_autosep)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_MIRRORING){
+ opt->name = "mirroring";
+ opt->title = "Mirroring";
+ opt->desc = "Reflect output image horizontally";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_mirroring)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_WL_FOLLOW){
+ i=0;
+ s->wl_follow_list[i++]=STRING_DEFAULT;
+ s->wl_follow_list[i++]=STRING_ON;
+ s->wl_follow_list[i++]=STRING_OFF;
+ s->wl_follow_list[i]=NULL;
+
+ opt->name = "wl-follow";
+ opt->title = "White level follower";
+ opt->desc = "Control white level follower";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->wl_follow_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (s->has_wl_follow)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_IPC_MODE){
+ i=0;
+ s->ipc_mode_list[i++]=STRING_DEFAULT;
+ if(s->has_dtc){
+ s->ipc_mode_list[i++]=STRING_DTC;
+ }
+ if(s->has_sdtc){
+ s->ipc_mode_list[i++]=STRING_SDTC;
+ }
+ s->ipc_mode_list[i]=NULL;
+
+ opt->name = "ipc-mode";
+ opt->title = "IPC mode";
+ opt->desc = "Image processing mode, enables additional options";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->ipc_mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if ( i > 2 ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->s_mode != MODE_HALFTONE && s->s_mode != MODE_LINEART){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* =============== DTC params ================================ */
+ /* enabled when in dtc mode (manually or by default) */
+ if(option==OPT_BP_FILTER){
+ opt->name = "bp-filter";
+ opt->title = "BP filter";
+ opt->desc = "Improves quality of high resolution ball-point pen text";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SMOOTHING){
+ opt->name = "smoothing";
+ opt->title = "Smoothing";
+ opt->desc = "Enable smoothing for improved OCR";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_GAMMA_CURVE){
+ opt->name = "gamma-curve";
+ opt->title = "Gamma curve";
+ opt->desc = "Gamma curve";
+ opt->desc = "Gamma curve, from light to dark, but upper two may not work";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->gamma_curve_range;
+ s->gamma_curve_range.min=0;
+ s->gamma_curve_range.max=3;
+ s->gamma_curve_range.quant=1;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_THRESHOLD_CURVE){
+ opt->name = "threshold-curve";
+ opt->title = "Threshold curve";
+ opt->desc = "Threshold curve, from light to dark, but upper two may not be linear";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->threshold_curve_range;
+ s->threshold_curve_range.min=0;
+ s->threshold_curve_range.max=7;
+ s->threshold_curve_range.quant=1;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_THRESHOLD_WHITE){
+ opt->name = "threshold-white";
+ opt->title = "Threshold white";
+ opt->desc = "Set pixels equal to threshold to white instead of black";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_NOISE_REMOVAL){
+ opt->name = "noise-removal";
+ opt->title = "Noise removal";
+ opt->desc = "Noise removal";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if(s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_MATRIX_5){
+ opt->name = "matrix-5x5";
+ opt->title = "Matrix 5x5";
+ opt->desc = "Remove 5 pixel square noise";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if( !s->noise_removal
+ || s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_MATRIX_4){
+ opt->name = "matrix-4x4";
+ opt->title = "Matrix 4x4";
+ opt->desc = "Remove 4 pixel square noise";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if( !s->noise_removal
+ || s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_MATRIX_3){
+ opt->name = "matrix-3x3";
+ opt->title = "Matrix 3x3";
+ opt->desc = "Remove 3 pixel square noise";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if( !s->noise_removal
+ || s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_MATRIX_2){
+ opt->name = "matrix-2x2";
+ opt->title = "Matrix 2x2";
+ opt->desc = "Remove 2 pixel square noise";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+
+ if ( s->has_dtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if( !s->noise_removal
+ || s->ipc_mode == WD_ipc_SDTC
+ || (s->has_sdtc && s->ipc_mode == WD_ipc_DEFAULT)){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* =============== SDTC param ================================ */
+ /* enabled when in sdtc mode (manually or by default) */
+ /* called variance with ipc2, sensitivity with ipc3 */
+ if(option==OPT_VARIANCE){
+ opt->name = "variance";
+ opt->title = "Variance";
+ opt->desc = "Set SDTC variance rate (sensitivity), 0 equals 127";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->variance_range;
+ s->variance_range.min=0;
+ s->variance_range.max=255;
+ s->variance_range.quant=1;
+
+ if ( s->has_sdtc ){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if (s->ipc_mode == WD_ipc_DTC){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* "Advanced" group ------------------------------------------------------ */
+ if(option==OPT_ADVANCED_GROUP){
+ opt->name = SANE_NAME_ADVANCED;
+ opt->title = SANE_TITLE_ADVANCED;
+ opt->desc = SANE_DESC_ADVANCED;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /*automatic width detection */
+ if(option==OPT_AWD){
+
+ opt->name = "awd";
+ opt->title = "Auto width detection";
+ opt->desc = "Scanner detects paper sides. May reduce scanning speed.";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* this option is useless by itself? */
+ if (0 && s->has_MS_auto && s->has_hybrid_crop_deskew){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*automatic length detection */
+ if(option==OPT_ALD){
+
+ opt->name = "ald";
+ opt->title = "Auto length detection";
+ opt->desc = "Scanner detects paper lower edge. May confuse some frontends.";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ if (s->has_MS_auto){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*image compression*/
+ if(option==OPT_COMPRESS){
+ i=0;
+ s->compress_list[i++]=STRING_NONE;
+
+ if(s->has_comp_JPG1){
+ s->compress_list[i++]=STRING_JPEG;
+ }
+
+ s->compress_list[i]=NULL;
+
+ opt->name = "compression";
+ opt->title = "Compression";
+ opt->desc = "Enable compressed data. May crash your front-end program";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->compress_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (i > 1){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ if ( must_downsample(s) || s->s_mode < MODE_GRAYSCALE ){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*image compression arg*/
+ if(option==OPT_COMPRESS_ARG){
+
+ opt->name = "compression-arg";
+ opt->title = "Compression argument";
+ opt->desc = "Level of JPEG compression. 1 is small file, 7 is large file. 0 (default) is same as 4";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->compress_arg_range;
+ s->compress_arg_range.quant=1;
+
+ if(s->has_comp_JPG1){
+ s->compress_arg_range.min=0;
+ s->compress_arg_range.max=7;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ if(s->compress != COMP_JPEG){
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed detection*/
+ if(option==OPT_DF_ACTION){
+ s->df_action_list[0] = STRING_DEFAULT;
+ s->df_action_list[1] = STRING_CONTINUE;
+ s->df_action_list[2] = STRING_STOP;
+ s->df_action_list[3] = NULL;
+
+ opt->name = "df-action";
+ opt->title = "DF action";
+ opt->desc = "Action following double feed error";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->df_action_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (s->has_MS_df)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed by skew*/
+ if(option==OPT_DF_SKEW){
+
+ opt->name = "df-skew";
+ opt->title = "DF skew";
+ opt->desc = "Enable double feed error due to skew";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ if (s->has_MS_df){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->df_action)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed by thickness */
+ if(option==OPT_DF_THICKNESS){
+
+ opt->name = "df-thickness";
+ opt->title = "DF thickness";
+ opt->desc = "Enable double feed error due to paper thickness";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ if (s->has_MS_df){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->df_action)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed by length*/
+ if(option==OPT_DF_LENGTH){
+
+ opt->name = "df-length";
+ opt->title = "DF length";
+ opt->desc = "Enable double feed error due to paper length";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ if (s->has_MS_df){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->df_action)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*double feed length difference*/
+ if(option==OPT_DF_DIFF){
+ s->df_diff_list[0] = STRING_DEFAULT;
+ s->df_diff_list[1] = STRING_10MM;
+ s->df_diff_list[2] = STRING_15MM;
+ s->df_diff_list[3] = STRING_20MM;
+ s->df_diff_list[4] = NULL;
+
+ opt->name = "df-diff";
+ opt->title = "DF length difference";
+ opt->desc = "Difference in page length to trigger double feed error";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->df_diff_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (s->has_MS_df){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->df_action || !s->df_diff)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*df recovery*/
+ if(option==OPT_DF_RECOVERY){
+ s->df_recovery_list[0] = STRING_DEFAULT;
+ s->df_recovery_list[1] = STRING_OFF;
+ s->df_recovery_list[2] = STRING_ON;
+ s->df_recovery_list[3] = NULL;
+
+ opt->name = "df-recovery";
+ opt->title = "DF recovery mode";
+ opt->desc = "Request scanner to reverse feed on paper jam";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->df_recovery_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_df && s->has_df_recovery)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*paper protection*/
+ if(option==OPT_PAPER_PROTECT){
+ s->paper_protect_list[0] = STRING_DEFAULT;
+ s->paper_protect_list[1] = STRING_OFF;
+ s->paper_protect_list[2] = STRING_ON;
+ s->paper_protect_list[3] = NULL;
+
+ opt->name = "paper-protect";
+ opt->title = "Paper protection";
+ opt->desc = "Request scanner to predict jams in the ADF";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->paper_protect_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_df && s->has_paper_protect)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*staple detection*/
+ if(option==OPT_STAPLE_DETECT){
+ s->staple_detect_list[0] = STRING_DEFAULT;
+ s->staple_detect_list[1] = STRING_OFF;
+ s->staple_detect_list[2] = STRING_ON;
+ s->staple_detect_list[3] = NULL;
+
+ opt->name = "staple-detect";
+ opt->title = "Staple detection";
+ opt->desc = "Request scanner to detect jams in the ADF caused by staples";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->staple_detect_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_df && s->has_staple_detect)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*background color*/
+ if(option==OPT_BG_COLOR){
+ s->bg_color_list[0] = STRING_DEFAULT;
+ s->bg_color_list[1] = STRING_WHITE;
+ s->bg_color_list[2] = STRING_BLACK;
+ s->bg_color_list[3] = NULL;
+
+ opt->name = "bgcolor";
+ opt->title = "Background color";
+ opt->desc = "Set color of background for scans. May conflict with overscan option";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->bg_color_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_bg)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*dropout color*/
+ if(option==OPT_DROPOUT_COLOR){
+ s->do_color_list[0] = STRING_DEFAULT;
+ s->do_color_list[1] = STRING_RED;
+ s->do_color_list[2] = STRING_GREEN;
+ s->do_color_list[3] = STRING_BLUE;
+ s->do_color_list[4] = NULL;
+
+ opt->name = "dropoutcolor";
+ opt->title = "Dropout color";
+ opt->desc = "One-pass scanners use only one color during gray or binary scanning, useful for colored paper or ink";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->do_color_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+
+ if (s->has_MS_dropout || s->has_vuid_3091 || must_downsample(s)){
+ opt->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ if(s->u_mode == MODE_COLOR)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*buffer mode*/
+ if(option==OPT_BUFF_MODE){
+ s->buff_mode_list[0] = STRING_DEFAULT;
+ s->buff_mode_list[1] = STRING_OFF;
+ s->buff_mode_list[2] = STRING_ON;
+ s->buff_mode_list[3] = NULL;
+
+ opt->name = "buffermode";
+ opt->title = "Buffer mode";
+ opt->desc = "Request scanner to read pages quickly from ADF into internal memory";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->buff_mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_buff)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*prepick*/
+ if(option==OPT_PREPICK){
+ s->prepick_list[0] = STRING_DEFAULT;
+ s->prepick_list[1] = STRING_OFF;
+ s->prepick_list[2] = STRING_ON;
+ s->prepick_list[3] = NULL;
+
+ opt->name = "prepick";
+ opt->title = "Prepick";
+ opt->desc = "Request scanner to grab next page from ADF";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->prepick_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_prepick)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*overscan*/
+ if(option==OPT_OVERSCAN){
+ s->overscan_list[0] = STRING_DEFAULT;
+ s->overscan_list[1] = STRING_OFF;
+ s->overscan_list[2] = STRING_ON;
+ s->overscan_list[3] = NULL;
+
+ opt->name = "overscan";
+ opt->title = "Overscan";
+ opt->desc = "Collect a few mm of background on top side of scan, before paper enters ADF, and increase maximum scan area beyond paper size, to allow collection on remaining sides. May conflict with bgcolor option";
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->overscan_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ if (s->has_MS_auto && (s->os_x_basic || s->os_y_basic))
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*sleep time*/
+ if(option==OPT_SLEEP_TIME){
+ s->sleep_time_range.min = 0;
+ s->sleep_time_range.max = 60;
+ s->sleep_time_range.quant = 1;
+
+ opt->name = "sleeptimer";
+ opt->title = "Sleep timer";
+ opt->desc = "Time in minutes until the internal power supply switches to sleep mode";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range=&s->sleep_time_range;
+ if(s->has_MS_sleep)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*duplex offset*/
+ if(option==OPT_DUPLEX_OFFSET){
+ s->duplex_offset_range.min = -16;
+ s->duplex_offset_range.max = 16;
+ s->duplex_offset_range.quant = 1;
+
+ opt->name = "duplexoffset";
+ opt->title = "Duplex offset";
+ opt->desc = "Adjust front/back offset";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->duplex_offset_range;
+ if(s->duplex_interlace == DUPLEX_INTERLACE_3091)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_GREEN_OFFSET){
+ s->green_offset_range.min = -16;
+ s->green_offset_range.max = 16;
+ s->green_offset_range.quant = 1;
+
+ opt->name = "greenoffset";
+ opt->title = "Green offset";
+ opt->desc = "Adjust green/red offset";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->green_offset_range;
+ if(s->color_interlace == COLOR_INTERLACE_3091)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_BLUE_OFFSET){
+ s->blue_offset_range.min = -16;
+ s->blue_offset_range.max = 16;
+ s->blue_offset_range.quant = 1;
+
+ opt->name = "blueoffset";
+ opt->title = "Blue offset";
+ opt->desc = "Adjust blue/red offset";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->blue_offset_range;
+ if(s->color_interlace == COLOR_INTERLACE_3091)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_LOW_MEM){
+ opt->name = "lowmemory";
+ opt->title = "Low Memory";
+ opt->desc = "Limit driver memory usage for use in embedded systems. Causes some duplex transfers to alternate sides on each call to sane_read. Value of option 'side' can be used to determine correct image. This option should only be used with custom front-end software.";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+
+ if (1)
+ opt->cap= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ if(option==OPT_SIDE){
+ opt->name = "side";
+ opt->title = "Duplex side";
+ opt->desc = "Tells which side (0=front, 1=back) of a duplex scan the next call to sane_read will return.";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /*deskew and crop by hardware*/
+ if(option==OPT_HWDESKEWCROP){
+ opt->name = "hwdeskewcrop";
+ opt->title = "Hardware deskew and crop";
+ opt->desc = "Request scanner to rotate and crop pages digitally.";
+ opt->type = SANE_TYPE_BOOL;
+ if (s->has_hybrid_crop_deskew)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*deskew by software*/
+ if(option==OPT_SWDESKEW){
+ opt->name = "swdeskew";
+ opt->title = "Software deskew";
+ opt->desc = "Request driver to rotate skewed pages digitally.";
+ opt->type = SANE_TYPE_BOOL;
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*software despeckle radius*/
+ if(option==OPT_SWDESPECK){
+
+ opt->name = "swdespeck";
+ opt->title = "Software despeckle diameter";
+ opt->desc = "Maximum diameter of lone dots to remove from scan.";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->swdespeck_range;
+ s->swdespeck_range.quant=1;
+
+ if(1){
+ s->swdespeck_range.min=0;
+ s->swdespeck_range.max=9;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /*crop by software*/
+ if(option==OPT_SWCROP){
+ opt->name = "swcrop";
+ opt->title = "Software crop";
+ opt->desc = "Request driver to remove border from pages digitally.";
+ opt->type = SANE_TYPE_BOOL;
+ if (1)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ /* Software blank page skip */
+ if(option==OPT_SWSKIP){
+
+ opt->name = "swskip";
+ opt->title = SANE_I18N ("Software blank skip percentage");
+ opt->desc = SANE_I18N("Request driver to discard pages with low percentage of dark pixels");
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_PERCENT;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->swskip_range;
+
+ s->swskip_range.quant=1;
+ s->swskip_range.min=SANE_FIX(0);
+ s->swskip_range.max=SANE_FIX(100);
+
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* "Endorser" group ------------------------------------------------------ */
+ if(option==OPT_ENDORSER_GROUP){
+ opt->name = "endorser-options";
+ opt->title = "Endorser Options";
+ opt->desc = "Controls for endorser unit";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /*flaming hack to get scanimage to hide group*/
+ if ( !(s->has_endorser_f || s->has_endorser_b) )
+ opt->type = SANE_TYPE_BOOL;
+ }
+
+ if(option==OPT_ENDORSER){
+ opt->name = "endorser";
+ opt->title = "Endorser";
+ opt->desc = "Enable endorser unit";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+
+ if (s->has_endorser_f || s->has_endorser_b)
+ opt->cap= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ if(option==OPT_ENDORSER_BITS){
+ opt->name = "endorser-bits";
+ opt->title = "Endorser bits";
+ opt->desc = "Determines maximum endorser counter value.";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+
+ /*old type cant do this?*/
+ if ((s->has_endorser_f && s->endorser_type_f != ET_OLD)
+ || (s->has_endorser_b && s->endorser_type_b != ET_OLD)){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->endorser_bits_range;
+
+ s->endorser_bits_range.min = 16;
+ s->endorser_bits_range.max = 24;
+ s->endorser_bits_range.quant = 8;
+ }
+
+ if(option==OPT_ENDORSER_VAL){
+ opt->name = "endorser-val";
+ opt->title = "Endorser value";
+ opt->desc = "Initial endorser counter value.";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+
+ if (s->has_endorser_f || s->has_endorser_b){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->endorser_val_range;
+
+ s->endorser_val_range.min = 0;
+ s->endorser_val_range.max = (1 << s->u_endorser_bits)-1;
+ s->endorser_val_range.quant = 1;
+ }
+
+ if(option==OPT_ENDORSER_STEP){
+ opt->name = "endorser-step";
+ opt->title = "Endorser step";
+ opt->desc = "Change endorser counter value by this much for each page.";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = sizeof(SANE_Word);
+
+ if (s->has_endorser_f || s->has_endorser_b){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->endorser_step_range;
+
+ s->endorser_step_range.min = -2;
+ s->endorser_step_range.max = 2;
+ s->endorser_step_range.quant = 1;
+ }
+
+ if(option==OPT_ENDORSER_Y){
+ opt->name = "endorser-y";
+ opt->title = "Endorser Y";
+ opt->desc = "Endorser print offset from top of paper.";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->size = sizeof(SANE_Word);
+
+ if (s->has_endorser_f || s->has_endorser_b){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->endorser_y_range);
+
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->endorser_y_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
+ s->endorser_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s));
+ s->endorser_y_range.quant = MM_PER_UNIT_FIX;
+ }
+
+ if(option==OPT_ENDORSER_FONT){
+ opt->name = "endorser-font";
+ opt->title = "Endorser font";
+ opt->desc = "Endorser printing font.";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+
+ /*only newest can do this?*/
+ if ((s->has_endorser_f && s->endorser_type_f == ET_40)
+ || (s->has_endorser_b && s->endorser_type_b == ET_40)){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->endorser_font_list;
+
+ s->endorser_font_list[0] = STRING_HORIZONTAL;
+ s->endorser_font_list[1] = STRING_HORIZONTALBOLD;
+ s->endorser_font_list[2] = STRING_HORIZONTALNARROW;
+ s->endorser_font_list[3] = STRING_VERTICAL;
+ s->endorser_font_list[4] = STRING_VERTICALBOLD;
+ s->endorser_font_list[5] = NULL;
+
+ opt->size = maxStringSize (opt->constraint.string_list);
+ }
+
+ if(option==OPT_ENDORSER_DIR){
+ opt->name = "endorser-dir";
+ opt->title = "Endorser direction";
+ opt->desc = "Endorser printing direction.";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+
+ if (s->has_endorser_f || s->has_endorser_b){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->endorser_dir_list;
+
+ s->endorser_dir_list[0] = STRING_TOPTOBOTTOM;
+ s->endorser_dir_list[1] = STRING_BOTTOMTOTOP;
+ s->endorser_dir_list[2] = NULL;
+
+ opt->size = maxStringSize (opt->constraint.string_list);
+ }
+
+ if(option==OPT_ENDORSER_SIDE){
+ opt->name = "endorser-side";
+ opt->title = "Endorser side";
+ opt->desc = "Endorser printing side, requires hardware support to change";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+
+ /* only show if both endorsers are installed */
+ if (s->has_endorser_f && s->has_endorser_b){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->endorser_side_list;
+
+ s->endorser_side_list[0] = STRING_FRONT;
+ s->endorser_side_list[1] = STRING_BACK;
+ s->endorser_side_list[2] = NULL;
+
+ opt->size = maxStringSize (opt->constraint.string_list);
+ }
+
+ if(option==OPT_ENDORSER_STRING){
+ opt->name = "endorser-string";
+ opt->title = "Endorser string";
+ opt->desc = "Endorser alphanumeric print format. %05ud or %08ud at the end will be replaced by counter value.";
+ opt->type = SANE_TYPE_STRING;
+ opt->unit = SANE_UNIT_NONE;
+ opt->size = s->endorser_string_len + 1;
+
+ if (s->has_endorser_f || s->has_endorser_b){
+ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if(!s->u_endorser)
+ opt->cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* "Sensor" group ------------------------------------------------------ */
+ if(option==OPT_SENSOR_GROUP){
+ opt->name = SANE_NAME_SENSORS;
+ opt->title = SANE_TITLE_SENSORS;
+ opt->desc = SANE_DESC_SENSORS;
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ if(option==OPT_TOP){
+ opt->name = "top-edge";
+ opt->title = "Top edge";
+ opt->desc = "Paper is pulled partly into adf";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_A3){
+ opt->name = "a3-paper";
+ opt->title = "A3 paper";
+ opt->desc = "A3 paper detected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_B4){
+ opt->name = "b4-paper";
+ opt->title = "B4 paper";
+ opt->desc = "B4 paper detected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_A4){
+ opt->name = "a4-paper";
+ opt->title = "A4 paper";
+ opt->desc = "A4 paper detected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_B5){
+ opt->name = "b5-paper";
+ opt->title = "B5 paper";
+ opt->desc = "B5 paper detected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_HOPPER){
+ opt->name = SANE_NAME_PAGE_LOADED;
+ opt->title = SANE_TITLE_PAGE_LOADED;
+ opt->desc = SANE_DESC_PAGE_LOADED;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_OMR){
+ opt->name = "omr-df";
+ opt->title = "OMR or DF";
+ opt->desc = "OMR or double feed detected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_ADF_OPEN){
+ opt->name = SANE_NAME_COVER_OPEN;
+ opt->title = SANE_TITLE_COVER_OPEN;
+ opt->desc = SANE_DESC_COVER_OPEN;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SLEEP){
+ opt->name = "power-save";
+ opt->title = "Power saving";
+ opt->desc = "Scanner in power saving mode";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SEND_SW){
+ opt->name = SANE_NAME_EMAIL;
+ opt->title = SANE_TITLE_EMAIL;
+ opt->desc = SANE_DESC_EMAIL;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_MANUAL_FEED){
+ opt->name = "manual-feed";
+ opt->title = "Manual feed";
+ opt->desc = "Manual feed selected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SCAN_SW){
+ opt->name = SANE_NAME_SCAN;
+ opt->title = SANE_TITLE_SCAN;
+ opt->desc = SANE_DESC_SCAN;
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_FUNCTION){
+ opt->name = "function";
+ opt->title = "Function";
+ opt->desc = "Function character on screen";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_INK_EMPTY){
+ opt->name = "ink-low";
+ opt->title = "Ink low";
+ opt->desc = "Imprinter ink running low";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status && (s->has_endorser_f || s->has_endorser_b))
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_DOUBLE_FEED){
+ opt->name = "double-feed";
+ opt->title = "Double feed";
+ opt->desc = "Double feed detected";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status || s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_ERROR_CODE){
+ opt->name = "error-code";
+ opt->title = "Error code";
+ opt->desc = "Hardware error code";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_SKEW_ANGLE){
+ opt->name = "skew-angle";
+ opt->title = "Skew angle";
+ opt->desc = "Requires black background for scanning";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_INK_REMAIN){
+ opt->name = "ink-remain";
+ opt->title = "Ink remaining";
+ opt->desc = "Imprinter ink level";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->has_cmd_hw_status && (s->has_endorser_f || s->has_endorser_b))
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_DENSITY_SW){
+ opt->name = "density";
+ opt->title = "Density";
+ opt->desc = "Density dial";
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ if(option==OPT_DUPLEX_SW){
+ opt->name = "duplex";
+ opt->title = "Duplex switch";
+ opt->desc = "Duplex switch";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->ghs_in_rs)
+ opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ return opt;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct fujitsu *s = (struct fujitsu *) handle;
+ SANE_Int dummy = 0;
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ /*blast info in case frontend forgot*/
+ *info = 0;
+
+ if (option >= NUM_OPTIONS) {
+ DBG (5, "sane_control_option: %d too big\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: %d inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE) {
+ SANE_Word * val_p = (SANE_Word *) val;
+
+ DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ *val_p = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if(s->source == SOURCE_FLATBED){
+ strcpy (val, STRING_FLATBED);
+ }
+ else if(s->source == SOURCE_ADF_FRONT){
+ strcpy (val, STRING_ADFFRONT);
+ }
+ else if(s->source == SOURCE_ADF_BACK){
+ strcpy (val, STRING_ADFBACK);
+ }
+ else if(s->source == SOURCE_ADF_DUPLEX){
+ strcpy (val, STRING_ADFDUPLEX);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if(s->u_mode == MODE_LINEART){
+ strcpy (val, STRING_LINEART);
+ }
+ else if(s->u_mode == MODE_HALFTONE){
+ strcpy (val, STRING_HALFTONE);
+ }
+ else if(s->u_mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->u_mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+ *val_p = s->resolution_x;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_width);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_height);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *val_p = s->brightness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *val_p = s->contrast;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA:
+ *val_p = SANE_FIX(s->gamma);
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ *val_p = s->threshold;
+ return SANE_STATUS_GOOD;
+
+ /* IPC */
+ case OPT_RIF:
+ *val_p = s->rif;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HT_TYPE:
+ switch (s->ht_type) {
+ case WD_ht_type_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case WD_ht_type_DITHER:
+ strcpy (val, STRING_DITHER);
+ break;
+ case WD_ht_type_DIFFUSION:
+ strcpy (val, STRING_DIFFUSION);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_HT_PATTERN:
+ *val_p = s->ht_pattern;
+ return SANE_STATUS_GOOD;
+
+ case OPT_OUTLINE:
+ *val_p = s->outline;
+ return SANE_STATUS_GOOD;
+
+ case OPT_EMPHASIS:
+ *val_p = s->emphasis;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SEPARATION:
+ *val_p = s->separation;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MIRRORING:
+ *val_p = s->mirroring;
+ return SANE_STATUS_GOOD;
+
+ case OPT_WL_FOLLOW:
+ switch (s->wl_follow) {
+ case WD_wl_follow_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case WD_wl_follow_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case WD_wl_follow_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_IPC_MODE:
+ if(s->ipc_mode == WD_ipc_DEFAULT){
+ strcpy (val, STRING_DEFAULT);
+ }
+ else if(s->ipc_mode == WD_ipc_DTC){
+ strcpy (val, STRING_DTC);
+ }
+ else if(s->ipc_mode == WD_ipc_SDTC){
+ strcpy (val, STRING_SDTC);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BP_FILTER:
+ *val_p = s->bp_filter;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SMOOTHING:
+ *val_p = s->smoothing;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_CURVE:
+ *val_p = s->gamma_curve;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD_CURVE:
+ *val_p = s->threshold_curve;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD_WHITE:
+ *val_p = s->threshold_white;
+ return SANE_STATUS_GOOD;
+
+ case OPT_NOISE_REMOVAL:
+ *val_p = s->noise_removal;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_5:
+ *val_p = s->matrix_5;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_4:
+ *val_p = s->matrix_4;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_3:
+ *val_p = s->matrix_3;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_2:
+ *val_p = s->matrix_2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_VARIANCE:
+ *val_p = s->variance;
+ return SANE_STATUS_GOOD;
+
+ /* Advanced Group */
+ case OPT_AWD:
+ *val_p = s->awd;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ALD:
+ *val_p = s->ald;
+ return SANE_STATUS_GOOD;
+
+ case OPT_COMPRESS:
+ if(s->compress == COMP_JPEG){
+ strcpy (val, STRING_JPEG);
+ }
+ else{
+ strcpy (val, STRING_NONE);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_COMPRESS_ARG:
+ *val_p = s->compress_arg;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_ACTION:
+ switch (s->df_action) {
+ case DF_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case DF_CONTINUE:
+ strcpy (val, STRING_CONTINUE);
+ break;
+ case DF_STOP:
+ strcpy (val, STRING_STOP);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_SKEW:
+ *val_p = s->df_skew;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_THICKNESS:
+ *val_p = s->df_thickness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_LENGTH:
+ *val_p = s->df_length;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_DIFF:
+ switch (s->df_diff) {
+ case MSEL_df_diff_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_df_diff_10MM:
+ strcpy (val, STRING_10MM);
+ break;
+ case MSEL_df_diff_15MM:
+ strcpy (val, STRING_15MM);
+ break;
+ case MSEL_df_diff_20MM:
+ strcpy (val, STRING_20MM);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_RECOVERY:
+ switch (s->df_recovery) {
+ case MSEL_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case MSEL_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAPER_PROTECT:
+ switch (s->paper_protect) {
+ case MSEL_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case MSEL_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_STAPLE_DETECT:
+ switch (s->staple_detect) {
+ case MSEL_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case MSEL_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BG_COLOR:
+ switch (s->bg_color) {
+ case COLOR_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case COLOR_WHITE:
+ strcpy (val, STRING_WHITE);
+ break;
+ case COLOR_BLACK:
+ strcpy (val, STRING_BLACK);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_DROPOUT_COLOR:
+ switch (s->dropout_color) {
+ case COLOR_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case COLOR_RED:
+ strcpy (val, STRING_RED);
+ break;
+ case COLOR_GREEN:
+ strcpy (val, STRING_GREEN);
+ break;
+ case COLOR_BLUE:
+ strcpy (val, STRING_BLUE);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BUFF_MODE:
+ switch (s->buff_mode) {
+ case MSEL_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case MSEL_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREPICK:
+ switch (s->prepick) {
+ case MSEL_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case MSEL_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_OVERSCAN:
+ switch (s->overscan) {
+ case MSEL_DEFAULT:
+ strcpy (val, STRING_DEFAULT);
+ break;
+ case MSEL_ON:
+ strcpy (val, STRING_ON);
+ break;
+ case MSEL_OFF:
+ strcpy (val, STRING_OFF);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_SLEEP_TIME:
+ *val_p = s->sleep_time;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DUPLEX_OFFSET:
+ *val_p = s->duplex_offset;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GREEN_OFFSET:
+ *val_p = s->green_offset;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BLUE_OFFSET:
+ *val_p = s->blue_offset;
+ return SANE_STATUS_GOOD;
+
+ case OPT_LOW_MEM:
+ *val_p = s->low_mem;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SIDE:
+ *val_p = s->side;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HWDESKEWCROP:
+ *val_p = s->hwdeskewcrop;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESKEW:
+ *val_p = s->swdeskew;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESPECK:
+ *val_p = s->swdespeck;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWCROP:
+ *val_p = s->swcrop;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWSKIP:
+ *val_p = SANE_FIX(s->swskip);
+ return SANE_STATUS_GOOD;
+
+ /* Endorser Group */
+ case OPT_ENDORSER:
+ *val_p = s->u_endorser;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_BITS:
+ *val_p = s->u_endorser_bits;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_VAL:
+ *val_p = s->u_endorser_val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_STEP:
+ *val_p = s->u_endorser_step;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_endorser_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_FONT:
+ switch (s->u_endorser_font) {
+ case FONT_H:
+ strcpy (val, STRING_HORIZONTAL);
+ break;
+ case FONT_HB:
+ strcpy (val, STRING_HORIZONTALBOLD);
+ break;
+ case FONT_HN:
+ strcpy (val, STRING_HORIZONTALNARROW);
+ break;
+ case FONT_V:
+ strcpy (val, STRING_VERTICAL);
+ break;
+ case FONT_VB:
+ strcpy (val, STRING_VERTICALBOLD);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_DIR:
+ switch (s->u_endorser_dir) {
+ case DIR_TTB:
+ strcpy (val, STRING_TOPTOBOTTOM);
+ break;
+ case DIR_BTT:
+ strcpy (val, STRING_BOTTOMTOTOP);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_SIDE:
+ switch (s->u_endorser_side) {
+ case ED_front:
+ strcpy (val, STRING_FRONT);
+ break;
+ case ED_back:
+ strcpy (val, STRING_BACK);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_STRING:
+ strncpy(
+ (SANE_String)val,
+ (SANE_String)s->u_endorser_string,
+ s->endorser_string_len+1
+ );
+ return SANE_STATUS_GOOD;
+
+ /* Sensor Group */
+ case OPT_TOP:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_top;
+ return ret;
+
+ case OPT_A3:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_A3;
+ return ret;
+
+ case OPT_B4:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_B4;
+ return ret;
+
+ case OPT_A4:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_A4;
+ return ret;
+
+ case OPT_B5:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_B5;
+ return ret;
+
+ case OPT_HOPPER:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_hopper;
+ return ret;
+
+ case OPT_OMR:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_omr;
+ return ret;
+
+ case OPT_ADF_OPEN:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_adf_open;
+ return ret;
+
+ case OPT_SLEEP:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_sleep;
+ return ret;
+
+ case OPT_SEND_SW:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_send_sw;
+ return ret;
+
+ case OPT_MANUAL_FEED:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_manual_feed;
+ return ret;
+
+ case OPT_SCAN_SW:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_scan_sw;
+ return ret;
+
+ case OPT_FUNCTION:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_function;
+ return ret;
+
+ case OPT_INK_EMPTY:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_ink_empty;
+ return ret;
+
+ case OPT_DOUBLE_FEED:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_double_feed;
+ return ret;
+
+ case OPT_ERROR_CODE:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_error_code;
+ return ret;
+
+ case OPT_SKEW_ANGLE:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_skew_angle;
+ return ret;
+
+ case OPT_INK_REMAIN:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_ink_remain;
+ return ret;
+
+ case OPT_DENSITY_SW:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_density_sw;
+ return ret;
+
+ case OPT_DUPLEX_SW:
+ ret = get_hardware_status(s,option);
+ *val_p = s->hw_duplex_sw;
+ return ret;
+
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE) {
+ int tmp;
+ SANE_Word val_c;
+ SANE_Status status;
+
+ DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
+
+ if ( s->started ) {
+ DBG (5, "sane_control_option: cant set, device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (5, "sane_control_option: bad value\n");
+ return status;
+ }
+
+ /* may have been changed by constrain, so dont copy until now */
+ val_c = *(SANE_Word *)val;
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option) {
+
+ /* Mode Group */
+ case OPT_SOURCE:
+ if (!strcmp (val, STRING_ADFFRONT)) {
+ tmp = SOURCE_ADF_FRONT;
+ }
+ else if (!strcmp (val, STRING_ADFBACK)) {
+ tmp = SOURCE_ADF_BACK;
+ }
+ else if (!strcmp (val, STRING_ADFDUPLEX)) {
+ tmp = SOURCE_ADF_DUPLEX;
+ }
+ else{
+ tmp = SOURCE_FLATBED;
+ }
+
+ if (s->source == tmp)
+ return SANE_STATUS_GOOD;
+
+ s->source = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (!strcmp (val, STRING_LINEART)) {
+ tmp = MODE_LINEART;
+ }
+ else if (!strcmp (val, STRING_HALFTONE)) {
+ tmp = MODE_HALFTONE;
+ }
+ else if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp == s->u_mode)
+ return SANE_STATUS_GOOD;
+
+ set_mode(s,tmp);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+
+ if (s->resolution_x == val_c)
+ return SANE_STATUS_GOOD;
+
+ s->resolution_x = val_c;
+ s->resolution_y = val_c;
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ /* Geometry Group */
+ case OPT_TL_X:
+ if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ if (s->tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ if (s->br_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ if (s->br_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ if (s->page_width == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->page_width = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ if (s->page_height == FIXED_MM_TO_SCANNER_UNIT(val_c))
+ return SANE_STATUS_GOOD;
+
+ s->page_height = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ /* Enhancement Group */
+ case OPT_BRIGHTNESS:
+ s->brightness = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ s->contrast = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA:
+ s->gamma = SANE_UNFIX(val_c);
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ s->threshold = val_c;
+ return SANE_STATUS_GOOD;
+
+ /* IPC */
+ case OPT_RIF:
+ s->rif = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HT_TYPE:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->ht_type = WD_ht_type_DEFAULT;
+ else if (!strcmp(val, STRING_DITHER))
+ s->ht_type = WD_ht_type_DITHER;
+ else if (!strcmp(val, STRING_DIFFUSION))
+ s->ht_type = WD_ht_type_DIFFUSION;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HT_PATTERN:
+ s->ht_pattern = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_OUTLINE:
+ s->outline = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_EMPHASIS:
+ s->emphasis = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SEPARATION:
+ s->separation = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MIRRORING:
+ s->mirroring = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_WL_FOLLOW:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->wl_follow = WD_wl_follow_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->wl_follow = WD_wl_follow_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->wl_follow = WD_wl_follow_OFF;
+ return SANE_STATUS_GOOD;
+
+ case OPT_IPC_MODE:
+ if (!strcmp (val, STRING_DEFAULT)) {
+ tmp = WD_ipc_DEFAULT;
+ }
+ else if (!strcmp (val, STRING_DTC)) {
+ tmp = WD_ipc_DTC;
+ }
+ else {
+ tmp = WD_ipc_SDTC;
+ }
+
+ if (tmp != s->ipc_mode)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->ipc_mode = tmp;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BP_FILTER:
+ s->bp_filter = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SMOOTHING:
+ s->smoothing = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_CURVE:
+ s->gamma_curve = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD_CURVE:
+ s->threshold_curve = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD_WHITE:
+ s->threshold_white = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_NOISE_REMOVAL:
+ if (val_c != s->noise_removal)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->noise_removal = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_5:
+ s->matrix_5 = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_4:
+ s->matrix_4 = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_3:
+ s->matrix_3 = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MATRIX_2:
+ s->matrix_2 = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_VARIANCE:
+ s->variance = val_c;
+ return SANE_STATUS_GOOD;
+
+ /* Advanced Group */
+ case OPT_AWD:
+ s->awd = val_c;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ALD:
+ s->ald = val_c;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_COMPRESS:
+ if (!strcmp (val, STRING_JPEG)) {
+ tmp = COMP_JPEG;
+ }
+ else{
+ tmp = COMP_NONE;
+ }
+
+ if (tmp == s->compress)
+ return SANE_STATUS_GOOD;
+
+ s->compress = tmp;
+ return SANE_STATUS_GOOD;
+
+ case OPT_COMPRESS_ARG:
+ s->compress_arg = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_ACTION:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->df_action = DF_DEFAULT;
+ else if (!strcmp(val, STRING_CONTINUE))
+ s->df_action = DF_CONTINUE;
+ else if (!strcmp(val, STRING_STOP))
+ s->df_action = DF_STOP;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_SKEW:
+ s->df_skew = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_THICKNESS:
+ s->df_thickness = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_LENGTH:
+ s->df_length = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_DIFF:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->df_diff = MSEL_df_diff_DEFAULT;
+ else if (!strcmp(val, STRING_10MM))
+ s->df_diff = MSEL_df_diff_10MM;
+ else if (!strcmp(val, STRING_15MM))
+ s->df_diff = MSEL_df_diff_15MM;
+ else if (!strcmp(val, STRING_20MM))
+ s->df_diff = MSEL_df_diff_20MM;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DF_RECOVERY:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->df_recovery = MSEL_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->df_recovery = MSEL_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->df_recovery = MSEL_OFF;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAPER_PROTECT:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->paper_protect = MSEL_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->paper_protect = MSEL_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->paper_protect = MSEL_OFF;
+ return SANE_STATUS_GOOD;
+
+ case OPT_STAPLE_DETECT:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->staple_detect = MSEL_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->staple_detect = MSEL_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->staple_detect = MSEL_OFF;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BG_COLOR:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->bg_color = COLOR_DEFAULT;
+ else if (!strcmp(val, STRING_WHITE))
+ s->bg_color = COLOR_WHITE;
+ else if (!strcmp(val, STRING_BLACK))
+ s->bg_color = COLOR_BLACK;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DROPOUT_COLOR:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->dropout_color = COLOR_DEFAULT;
+ else if (!strcmp(val, STRING_RED))
+ s->dropout_color = COLOR_RED;
+ else if (!strcmp(val, STRING_GREEN))
+ s->dropout_color = COLOR_GREEN;
+ else if (!strcmp(val, STRING_BLUE))
+ s->dropout_color = COLOR_BLUE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BUFF_MODE:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->buff_mode = MSEL_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->buff_mode= MSEL_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->buff_mode= MSEL_OFF;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREPICK:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->prepick = MSEL_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->prepick = MSEL_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->prepick = MSEL_OFF;
+ return SANE_STATUS_GOOD;
+
+ case OPT_OVERSCAN:
+ if (!strcmp(val, STRING_DEFAULT))
+ s->overscan = MSEL_DEFAULT;
+ else if (!strcmp(val, STRING_ON))
+ s->overscan = MSEL_ON;
+ else if (!strcmp(val, STRING_OFF))
+ s->overscan = MSEL_OFF;
+
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SLEEP_TIME:
+ s->sleep_time = val_c;
+ return set_sleep_mode(s);
+
+ case OPT_DUPLEX_OFFSET:
+ s->duplex_offset = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GREEN_OFFSET:
+ s->green_offset = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BLUE_OFFSET:
+ s->blue_offset = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_LOW_MEM:
+ s->low_mem = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HWDESKEWCROP:
+ s->hwdeskewcrop = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESKEW:
+ s->swdeskew = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWDESPECK:
+ s->swdespeck = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWCROP:
+ s->swcrop = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SWSKIP:
+ s->swskip = SANE_UNFIX(val_c);
+ return SANE_STATUS_GOOD;
+
+ /* Endorser Group */
+ case OPT_ENDORSER:
+ s->u_endorser = val_c;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_BITS:
+ s->u_endorser_bits = val_c;
+ return SANE_STATUS_GOOD;
+
+ /*this val not used in send_endorser*/
+ case OPT_ENDORSER_VAL:
+ s->u_endorser_val = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_STEP:
+ s->u_endorser_step = val_c;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_Y:
+ s->u_endorser_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_FONT:
+
+ if (!strcmp (val, STRING_HORIZONTAL)){
+ s->u_endorser_font = FONT_H;
+ }
+ else if (!strcmp (val, STRING_HORIZONTALBOLD)){
+ s->u_endorser_font = FONT_HB;
+ }
+ else if (!strcmp (val, STRING_HORIZONTALNARROW)){
+ s->u_endorser_font = FONT_HN;
+ }
+ else if (!strcmp (val, STRING_VERTICAL)){
+ s->u_endorser_font = FONT_V;
+ }
+ else if (!strcmp (val, STRING_VERTICALBOLD)){
+ s->u_endorser_font = FONT_VB;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_DIR:
+ if (!strcmp (val, STRING_TOPTOBOTTOM)){
+ s->u_endorser_dir = DIR_TTB;
+ }
+ else if (!strcmp (val, STRING_BOTTOMTOTOP)){
+ s->u_endorser_dir = DIR_BTT;
+ }
+ return SANE_STATUS_GOOD;
+
+ /*this val not used in send_endorser*/
+ case OPT_ENDORSER_SIDE:
+ if (!strcmp (val, STRING_FRONT)){
+ s->u_endorser_side = ED_front;
+ }
+ else if (!strcmp (val, STRING_BACK)){
+ s->u_endorser_side = ED_back;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ENDORSER_STRING:
+ strncpy(
+ (SANE_String)s->u_endorser_string,
+ (SANE_String)val,
+ s->endorser_string_len+1
+ );
+ return SANE_STATUS_GOOD;
+ } /* switch */
+ } /* else */
+
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+set_sleep_mode(struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "set_sleep_mode: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_sleep);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+ set_MSEL_sleep_mode(page, s->sleep_time);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "set_sleep_mode: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+get_hardware_status (struct fujitsu *s, SANE_Int option)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "get_hardware_status: start\n");
+
+ /* only run this if frontend has already read the last time we got it */
+ /* or if we don't care for such bookkeeping (private use) */
+ if (!option || s->hw_read[option-OPT_TOP]) {
+
+ DBG (15, "get_hardware_status: running\n");
+
+ /* mark all values as unread */
+ memset(s->hw_read,0,sizeof(s->hw_read));
+
+ if (s->has_cmd_hw_status){
+ unsigned char cmd[GET_HW_STATUS_len];
+ size_t cmdLen = GET_HW_STATUS_len;
+
+ unsigned char in[GHS_data_len];
+ size_t inLen = GHS_data_len;
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, GET_HW_STATUS_code);
+ set_GHS_allocation_length(cmd, inLen);
+
+ DBG (15, "get_hardware_status: calling ghs\n");
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+
+ s->hw_top = get_GHS_top(in);
+ s->hw_A3 = get_GHS_A3(in);
+ s->hw_B4 = get_GHS_B4(in);
+ s->hw_A4 = get_GHS_A4(in);
+ s->hw_B5 = get_GHS_B5(in);
+
+ s->hw_hopper = get_GHS_hopper(in);
+ s->hw_omr = get_GHS_omr(in);
+ s->hw_adf_open = get_GHS_adf_open(in);
+
+ s->hw_sleep = get_GHS_sleep(in);
+ s->hw_send_sw = get_GHS_send_sw(in);
+ s->hw_manual_feed = get_GHS_manual_feed(in);
+ s->hw_scan_sw = get_GHS_scan_sw(in);
+
+ s->hw_function = get_GHS_function(in);
+ s->hw_ink_empty = get_GHS_ink_empty(in);
+
+ s->hw_double_feed = get_GHS_double_feed(in);
+
+ s->hw_error_code = get_GHS_error_code(in);
+
+ s->hw_skew_angle = get_GHS_skew_angle(in);
+
+ if(inLen > 9){
+ s->hw_ink_remain = get_GHS_ink_remain(in);
+ }
+
+ ret = SANE_STATUS_GOOD;
+ }
+ }
+
+ /* 3091/2 put hardware status in RS data */
+ else if (s->ghs_in_rs){
+ unsigned char cmd[REQUEST_SENSE_len];
+ size_t cmdLen = REQUEST_SENSE_len;
+
+ unsigned char in[RS_return_size];
+ size_t inLen = RS_return_size;
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, REQUEST_SENSE_code);
+ set_RS_return_size(cmd, inLen);
+
+ DBG(15,"get_hardware_status: calling rs\n");
+
+ ret = do_cmd(
+ s,0,0,
+ cmd, cmdLen,
+ NULL,0,
+ in, &inLen
+ );
+
+ /* parse the rs data */
+ if(ret == SANE_STATUS_GOOD){
+ if(get_RS_sense_key(in)==0 && get_RS_ASC(in)==0x80){
+
+ s->hw_adf_open = get_RS_adf_open(in);
+ s->hw_send_sw = get_RS_send_sw(in);
+ s->hw_scan_sw = get_RS_scan_sw(in);
+ s->hw_duplex_sw = get_RS_duplex_sw(in);
+ s->hw_top = get_RS_top(in);
+ s->hw_hopper = get_RS_hopper(in);
+ s->hw_function = get_RS_function(in);
+ s->hw_density_sw = get_RS_density(in);
+ }
+ else{
+ DBG (10, "get_hardware_status: unexpected RS values\n");
+ }
+ }
+ }
+ }
+
+ if(option)
+ s->hw_read[option-OPT_TOP] = 1;
+
+ DBG (10, "get_hardware_status: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+send_endorser(struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SEND_len];
+ size_t cmdLen = SEND_len;
+
+ size_t strLen = strlen(s->u_endorser_string);
+
+ unsigned char out[S_e_data_max_len]; /*we probably send less below*/
+ size_t outLen = S_e_data_min_len + strLen; /*fi-5900 might want 1 more byte?*/
+
+ DBG (10, "send_endorser: start\n");
+
+ if (!s->has_endorser_f && !s->has_endorser_b){
+ DBG (10, "send_endorser: unsupported\n");
+ return ret;
+ }
+
+ /*build the payload*/
+ memset(out,0,outLen);
+
+ /*fi-5900 front side uses 0x80, assume all others*/
+ if(s->u_endorser_side == ED_front){
+ set_S_endorser_data_id(out,0x80);
+ }
+ else{
+ set_S_endorser_data_id(out,0);
+ }
+
+ set_S_endorser_stamp(out,0);
+ set_S_endorser_elec(out,0);
+
+ if(s->u_endorser_step < 0){
+ set_S_endorser_decr(out,S_e_decr_dec);
+ }
+ else{
+ set_S_endorser_decr(out,S_e_decr_inc);
+ }
+
+ if(s->u_endorser_bits == 24){
+ set_S_endorser_lap24(out,S_e_lap_24bit);
+ }
+ else{
+ set_S_endorser_lap24(out,S_e_lap_16bit);
+ }
+
+ set_S_endorser_ctstep(out,abs(s->u_endorser_step));
+ set_S_endorser_ulx(out,0);
+ set_S_endorser_uly(out,s->u_endorser_y);
+
+ switch (s->u_endorser_font) {
+ case FONT_H:
+ set_S_endorser_font(out,S_e_font_horiz);
+ set_S_endorser_bold(out,0);
+ break;
+ case FONT_HB:
+ set_S_endorser_font(out,S_e_font_horiz);
+ set_S_endorser_bold(out,1);
+ break;
+ case FONT_HN:
+ set_S_endorser_font(out,S_e_font_horiz_narrow);
+ set_S_endorser_bold(out,0);
+ break;
+ case FONT_V:
+ set_S_endorser_font(out,S_e_font_vert);
+ set_S_endorser_bold(out,0);
+ break;
+ case FONT_VB:
+ set_S_endorser_font(out,S_e_font_vert);
+ set_S_endorser_bold(out,1);
+ break;
+ }
+
+ set_S_endorser_size(out,0);
+ set_S_endorser_revs(out,0);
+
+ if(s->u_endorser_dir == DIR_BTT){
+ set_S_endorser_dirs(out,S_e_dir_bottom_top);
+ }
+ else{
+ set_S_endorser_dirs(out,S_e_dir_top_bottom);
+ }
+
+ set_S_endorser_string_length(out, strLen);
+ set_S_endorser_string(out, s->u_endorser_string, strLen);
+
+ /*build the command*/
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SEND_code);
+ set_S_xfer_datatype (cmd, S_datatype_endorser_data);
+ set_S_xfer_length (cmd, outLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "send_endorser: finish %d\n", ret);
+
+ return ret;
+}
+
+/* instead of internal brightness/contrast/gamma
+ most scanners use a 256x256 or 1024x256 LUT
+ default is linear table of slope 1 or 1/4 resp.
+ brightness and contrast inputs are -127 to +127
+
+ contrast rotates slope of line around central input val
+
+ high low
+ . x .
+ . x . xx
+ out . x . xxxxxxxx
+ . x xx
+ ....x....... ............
+ in in
+
+ then brightness moves line vertically, and clamps to 8bit
+
+ bright dark
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+ */
+static SANE_Status
+send_lut (struct fujitsu *s)
+{
+ int i, j, bytes = 1 << s->adbits;
+ double b, slope, offset;
+
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SEND_len];
+ size_t cmdLen = SEND_len;
+
+ unsigned char out[S_lut_header_len + S_lut_data_max_len];
+ size_t outLen = S_lut_header_len + S_lut_data_max_len;
+ unsigned char * p = out + S_lut_header_len;
+
+ DBG (10, "send_lut: start\n");
+
+ if(!s->num_download_gamma || !s->adbits){
+ DBG (10, "send_lut: unsupported\n");
+ return ret;
+ }
+
+ /* contrast is converted to a slope [0,90] degrees:
+ * first [-127,127] to [0,254] then to [0,1]
+ * then multiply by PI/2 to convert to radians
+ * then take the tangent to get slope (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ slope = tan(((double)s->contrast+127)/254 * M_PI/2) * 256/bytes;
+
+ /* contrast slope must stay centered, so figure
+ * out vertical offset at central input value */
+ offset = 127.5-(slope*bytes/2);
+
+ /* convert the user brightness setting (-127 to +127)
+ * into a scale that covers the range required
+ * to slide the contrast curve entirely off the table */
+ b = ((double)s->brightness/127) * (256 - offset);
+
+ DBG (15, "send_lut: %d %f %d %f %f\n", s->brightness, b,
+ s->contrast, slope, offset);
+
+ outLen = S_lut_header_len + bytes;
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SEND_code);
+
+ set_S_xfer_datatype (cmd, S_datatype_lut_data);
+ set_S_xfer_length (cmd, outLen);
+
+ memset(out,0,outLen);
+ set_S_lut_order (out, S_lut_order_single);
+ set_S_lut_ssize (out, bytes);
+ set_S_lut_dsize (out, 256);
+
+ for(i=0;i<bytes;i++){
+ j=slope*i + offset + b;
+
+ if(j<0){
+ j=0;
+ }
+
+ if(j>255){
+ j=255;
+ }
+
+ *p=j;
+ p++;
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "send_lut: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+send_q_table (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SEND_len];
+ size_t cmdLen = SEND_len;
+
+ unsigned char out[S_q_table_header_len + S_q_table_y_len + S_q_table_uv_len];
+ size_t outLen = S_q_table_header_len + S_q_table_y_len + S_q_table_uv_len;
+ unsigned char * yp = out + S_q_table_header_len;
+ unsigned char * uvp = out + S_q_table_header_len + S_q_table_y_len;
+
+ /* FIXME: generate these instead of hardcode */
+ unsigned char ydata[] = {
+ 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04,
+ 0x03, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0c,
+ 0x07, 0x07, 0x06, 0x06, 0x07, 0x0e, 0x0a, 0x0b,
+ 0x08, 0x0c, 0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f,
+ 0x10, 0x10, 0x13, 0x15, 0x1b, 0x17, 0x13, 0x14,
+ 0x1a, 0x14, 0x10, 0x10, 0x18, 0x20, 0x18, 0x1a,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x1e, 0x12, 0x17, 0x21,
+ 0x24, 0x21, 0x1e, 0x24, 0x1b, 0x1e, 0x1e, 0x1d };
+
+ unsigned char uvdata[] = {
+ 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0e, 0x07,
+ 0x07, 0x0e, 0x1d, 0x13, 0x10, 0x13, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d };
+
+ DBG (10, "send_q_table: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SEND_code);
+ set_S_xfer_datatype (cmd, S_datatype_jpg_q_table);
+ set_S_xfer_length (cmd, outLen);
+
+ memset(out,0,outLen);
+ set_S_q_table_y_len (out, S_q_table_y_len);
+ set_S_q_table_uv_len (out, S_q_table_uv_len);
+ memcpy (yp, ydata, S_q_table_y_len);
+ memcpy (uvp, uvdata, S_q_table_uv_len);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "send_q_table: finish\n");
+
+ return ret;
+}
+
+/* only used by iX500? */
+#if 0
+static SANE_Status
+mode_select_unk (struct fujitsu *s, int foo)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_unk: start\n");
+
+ /*if (!s->has_MS_unk){
+ DBG (10, "mode_select_unk: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }*/
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_unk);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+
+ *(page + 0x02) = foo;
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_unk: finish\n");
+
+ return ret;
+}
+#endif
+
+/* only used by iX500? */
+static SANE_Status
+diag_preread (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SEND_DIAGNOSTIC_len]; /*also big enough for READ_DIAG*/
+ size_t cmdLen = SEND_DIAGNOSTIC_len;
+
+ unsigned char out[SD_preread_len];
+ size_t outLen = SD_preread_len;
+
+ DBG (10, "diag_preread: start\n");
+
+ if (!s->has_cmd_sdiag || !s->has_cmd_rdiag || !s->need_diag_preread){
+ DBG (5, "diag_preread: not supported, returning\n");
+ return ret;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SEND_DIAGNOSTIC_code);
+ set_SD_slftst(cmd, 0);
+ set_SD_xferlen(cmd, outLen);
+
+ memcpy(out,SD_preread_string,SD_preread_stringlen);
+ set_SD_preread_xres(out,s->resolution_x);
+ set_SD_preread_yres(out,s->resolution_y);
+ /* call helper function, scanner wants lies about paper width */
+ set_SD_preread_paper_width(out, get_page_width(s));
+ /* dont call helper function, scanner wants actual length? */
+ set_SD_preread_paper_length(out, s->page_height);
+ set_SD_preread_composition(out, s->s_mode);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ DBG (5, "diag_preread: send diag error: %d\n", ret);
+ return ret;
+ }
+
+ DBG (10, "diag_preread: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+mode_select_df (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_df: start\n");
+
+ if(!s->has_MS_df){
+ DBG (10, "mode_select_df: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_df);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+
+ /* continue/stop */
+ if(s->df_action != DF_DEFAULT){
+ set_MSEL_df_enable (page, 1);
+
+ /* continue */
+ if(s->df_action == DF_CONTINUE){
+ set_MSEL_df_continue (page, 1);
+ }
+
+ /* skew */
+ if(s->df_skew){
+ set_MSEL_df_skew (page, 1);
+ }
+
+ /* thickness */
+ if(s->df_thickness){
+ set_MSEL_df_thickness (page, 1);
+ }
+
+ /* length */
+ if(s->df_length){
+ set_MSEL_df_length (page, 1);
+ set_MSEL_df_diff (page, s->df_diff);
+ }
+ }
+
+ set_MSEL_df_paperprot(page,s->paper_protect);
+ set_MSEL_df_stapledet(page,s->staple_detect);
+ set_MSEL_df_recovery(page,s->df_recovery);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_df: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+mode_select_bg (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_bg: start\n");
+
+ if(!s->has_MS_bg){
+ DBG (10, "mode_select_bg: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_bg);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+
+ if(s->bg_color != COLOR_DEFAULT){
+ set_MSEL_bg_enable (page, 1);
+
+ if(s->bg_color == COLOR_BLACK){
+ set_MSEL_bg_front (page, 1);
+ set_MSEL_bg_back (page, 1);
+ set_MSEL_bg_fb (page, 1);
+ }
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_bg: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+mode_select_dropout (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_max_len];
+ size_t outLen = MSEL_header_len + MSEL_data_max_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_dropout: start\n");
+
+ if(!s->has_MS_dropout){
+ DBG (10, "mode_select_dropout: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_dropout);
+ set_MSEL_page_len(page, MSEL_data_max_len-2);
+
+ set_MSEL_dropout_front (page, s->dropout_color);
+ set_MSEL_dropout_back (page, s->dropout_color);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_dropout: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+mode_select_buff (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_buff: start\n");
+
+ if (!s->has_MS_buff){
+ DBG (10, "mode_select_buff: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_buff);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+
+ set_MSEL_buff_mode(page, s->buff_mode);
+ set_MSEL_buff_clear(page, 3);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_buff: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+mode_select_prepick (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_prepick: start\n");
+
+ if (!s->has_MS_prepick){
+ DBG (10, "mode_select_prepick: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_prepick);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+
+ set_MSEL_prepick(page, s->prepick);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_prepick: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+mode_select_auto (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[MODE_SELECT_len];
+ size_t cmdLen = MODE_SELECT_len;
+
+ unsigned char out[MSEL_header_len + MSEL_data_min_len];
+ size_t outLen = MSEL_header_len + MSEL_data_min_len;
+ unsigned char * page = out+MSEL_header_len;
+
+ DBG (10, "mode_select_auto: start\n");
+
+ if(!s->has_MS_auto){
+ DBG (10, "mode_select_auto: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, MODE_SELECT_code);
+ set_MSEL_pf(cmd, 1);
+ set_MSEL_xferlen(cmd, outLen);
+
+ memset(out,0,outLen);
+ set_MSEL_pc(page, MS_pc_auto);
+ set_MSEL_page_len(page, MSEL_data_min_len-2);
+
+ set_MSEL_overscan(page, s->overscan);
+ set_MSEL_ald(page, s->ald || s->hwdeskewcrop);
+ set_MSEL_awd(page, s->awd || s->hwdeskewcrop);
+ set_MSEL_req_driv_crop(page, s->hwdeskewcrop && (s->swcrop || s->swdeskew));
+ set_MSEL_deskew(page, s->hwdeskewcrop);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "mode_select_auto: finish\n");
+
+ return ret;
+}
+
+
+/*
+ * @@ Section 4 - SANE scanning functions
+ */
+/*
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct fujitsu *s = (struct fujitsu *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ /* not started? update param data from user settings */
+ if(!s->started){
+ ret = update_params(s);
+ if(ret)
+ return ret;
+ }
+
+ params->format = s->u_params.format;
+ params->last_frame = s->u_params.last_frame;
+ params->lines = s->u_params.lines;
+ params->depth = s->u_params.depth;
+ params->pixels_per_line = s->u_params.pixels_per_line;
+ params->bytes_per_line = s->u_params.bytes_per_line;
+
+ /* we wont know the end until we get to it */
+ if(s->ald && !must_fully_buffer(s)){
+ DBG (15, "sane_get_parameters: hand-scanner mode\n");
+ params->lines = -1;
+ }
+
+ DBG (10, "sane_get_parameters: finish\n");
+ return ret;
+}
+
+/* set s_param and u_param data based on user settings
+ * and scanner capabilities. */
+SANE_Status
+update_params (struct fujitsu * s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Parameters * params = &(s->s_params);
+
+ DBG (10, "update_params: start\n");
+
+ /* first, we setup s_params to describe the image to the scanner */
+ /* this backend only sends single frame images */
+ params->last_frame = 1;
+
+ /* initial ppl from user settings */
+ params->pixels_per_line = s->resolution_x * (s->br_x - s->tl_x) / 1200;
+
+ /* some scanners require even number of bytes in each transfer block,
+ * so we round to even # of total lines, to ensure last block is even */
+ params->lines = s->resolution_y * (s->br_y - s->tl_y) / 1200;
+ params->lines -= params->lines % 2;
+
+ if (s->s_mode == MODE_COLOR) {
+ params->depth = 8;
+
+#ifdef SANE_FRAME_JPEG
+ /* jpeg requires 8x8 squares */
+ if(s->compress == COMP_JPEG){
+ params->format = SANE_FRAME_JPEG;
+ params->pixels_per_line -= params->pixels_per_line % 8;
+ params->lines -= params->lines % 8;
+ }
+ else{
+#endif
+ params->format = SANE_FRAME_RGB;
+ params->pixels_per_line -= params->pixels_per_line
+ % max(s->ppl_mod_by_mode[s->s_mode], s->ppl_mod_by_mode[s->u_mode]);
+#ifdef SANE_FRAME_JPEG
+ }
+#endif
+
+ params->bytes_per_line = params->pixels_per_line * 3;
+ }
+ else if (s->s_mode == MODE_GRAYSCALE) {
+ params->depth = 8;
+
+#ifdef SANE_FRAME_JPEG
+ /* jpeg requires 8x8 squares */
+ if(s->compress == COMP_JPEG){
+ params->format = SANE_FRAME_JPEG;
+ params->pixels_per_line -= params->pixels_per_line % 8;
+ params->lines -= params->lines % 8;
+ }
+ else{
+#endif
+ params->format = SANE_FRAME_GRAY;
+ params->pixels_per_line -= params->pixels_per_line
+ % max(s->ppl_mod_by_mode[s->s_mode], s->ppl_mod_by_mode[s->u_mode]);
+#ifdef SANE_FRAME_JPEG
+ }
+#endif
+
+ params->bytes_per_line = params->pixels_per_line;
+ }
+ else {
+ params->depth = 1;
+ params->format = SANE_FRAME_GRAY;
+ params->pixels_per_line -= params->pixels_per_line
+ % max(s->ppl_mod_by_mode[s->s_mode], s->ppl_mod_by_mode[s->u_mode]);
+ params->bytes_per_line = params->pixels_per_line / 8;
+ }
+
+ DBG(15,"update_params: x: max=%d, page=%d, gpw=%d, res=%d\n",
+ s->max_x, s->page_width, get_page_width(s), s->resolution_x);
+
+ DBG(15,"update_params: y: max=%d, page=%d, gph=%d, res=%d\n",
+ s->max_y, s->page_height, get_page_height(s), s->resolution_y);
+
+ DBG(15,"update_params: area: tlx=%d, brx=%d, tly=%d, bry=%d\n",
+ s->tl_x, s->br_x, s->tl_y, s->br_y);
+
+ DBG(15,"update_params: params: ppl=%d, Bpl=%d, lines=%d\n",
+ params->pixels_per_line, params->bytes_per_line, params->lines);
+
+ DBG(15,"update_params: params: format=%d, depth=%d, last=%d\n",
+ params->format, params->depth, params->last_frame);
+
+ /* second, we setup u_params to describe the image to the user */
+ /* use a helper function cause it is called elsewhere */
+ ret = update_u_params(s);
+
+ DBG (10, "update_params: finish\n");
+ return ret;
+}
+
+/* set u_param data based on user settings, and s_params */
+SANE_Status
+update_u_params (struct fujitsu * s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Parameters * params = &(s->u_params);
+
+ DBG (10, "update_u_params: start\n");
+
+ /* for most machines, it is the same, so we just copy */
+ memcpy(&(s->u_params), &(s->s_params), sizeof(SANE_Parameters));
+
+ /* some scanners don't support the user's mode, so params differ */
+ /* but not in jpeg mode. we don't support that. */
+ if(must_downsample(s)){
+
+ /* making gray from a color scan */
+ if (s->u_mode == MODE_GRAYSCALE) {
+ params->format = SANE_FRAME_GRAY;
+ params->bytes_per_line = params->pixels_per_line;
+ }
+ /* making binary from a gray or color scan */
+ else if (s->u_mode == MODE_LINEART) {
+ params->depth = 1;
+ params->format = SANE_FRAME_GRAY;
+ params->bytes_per_line = params->pixels_per_line / 8;
+ }
+
+ DBG(15,"update_u_params: x: max=%d, page=%d, gpw=%d, res=%d\n",
+ s->max_x, s->page_width, get_page_width(s), s->resolution_x);
+
+ DBG(15,"update_u_params: y: max=%d, page=%d, gph=%d, res=%d\n",
+ s->max_y, s->page_height, get_page_height(s), s->resolution_y);
+
+ DBG(15,"update_u_params: area: tlx=%d, brx=%d, tly=%d, bry=%d\n",
+ s->tl_x, s->br_x, s->tl_y, s->br_y);
+
+ DBG(15,"update_u_params: params: ppl=%d, Bpl=%d, lines=%d\n",
+ params->pixels_per_line, params->bytes_per_line, params->lines);
+
+ DBG(15,"update_u_params: params: format=%d, depth=%d, last=%d\n",
+ params->format, params->depth, params->last_frame);
+ }
+
+ DBG (10, "update_u_params: finish\n");
+ return ret;
+}
+
+/* make backup of param data, in case original is overwritten */
+SANE_Status
+backup_params (struct fujitsu * s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Parameters * params = &(s->s_params);
+ SANE_Parameters * params_bk = &(s->s_params_bk);
+
+ DBG (10, "backup_params: start\n");
+
+ params_bk->format = params->format;
+ params_bk->last_frame = params->last_frame;
+ params_bk->bytes_per_line = params->bytes_per_line;
+ params_bk->pixels_per_line = params->pixels_per_line;
+ params_bk->lines = params->lines;
+ params_bk->depth = params->depth;
+
+ /* also have to save the user params */
+ params = &(s->u_params);
+ params_bk = &(s->u_params_bk);
+
+ params_bk->format = params->format;
+ params_bk->last_frame = params->last_frame;
+ params_bk->bytes_per_line = params->bytes_per_line;
+ params_bk->pixels_per_line = params->pixels_per_line;
+ params_bk->lines = params->lines;
+ params_bk->depth = params->depth;
+
+ DBG (10, "backup_params: finish\n");
+ return ret;
+}
+
+/* restore backup of param data, in case original was overwritten */
+SANE_Status
+restore_params (struct fujitsu * s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Parameters * params = &(s->s_params);
+ SANE_Parameters * params_bk = &(s->s_params_bk);
+
+ DBG (10, "restore_params: start\n");
+
+ params->format = params_bk->format;
+ params->last_frame = params_bk->last_frame;
+ params->bytes_per_line = params_bk->bytes_per_line;
+ params->pixels_per_line = params_bk->pixels_per_line;
+ params->lines = params_bk->lines;
+ params->depth = params_bk->depth;
+
+ /* also have to restore the user params */
+ params = &(s->u_params);
+ params_bk = &(s->u_params_bk);
+
+ params->format = params_bk->format;
+ params->last_frame = params_bk->last_frame;
+ params->bytes_per_line = params_bk->bytes_per_line;
+ params->pixels_per_line = params_bk->pixels_per_line;
+ params->lines = params_bk->lines;
+ params->depth = params_bk->depth;
+
+ DBG (10, "restore_params: finish\n");
+ return ret;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ * commands: scanner control (lampon), send (lut), send (dither),
+ * set window, object pos, and scan
+ *
+ * this will be called between sides of a duplex scan,
+ * and at the start of each page of an adf batch.
+ * hence, we spend alot of time playing with s->started, etc.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct fujitsu *s = handle;
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "sane_start: start\n");
+ DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->source);
+
+ /* undo any prior sane_cancel calls */
+ s->cancelled=0;
+
+ /* protect this block from sane_cancel */
+ s->reading=1;
+
+ /* not finished with current side, error */
+ if (s->started && !s->eof_tx[s->side]) {
+ DBG(5,"sane_start: previous transfer not finished?");
+ ret = SANE_STATUS_INVAL;
+ goto errors;
+ }
+
+ /* low mem mode messes up the side marker, reset it */
+ if(s->source == SOURCE_ADF_DUPLEX && s->low_mem
+ && s->eof_tx[SIDE_FRONT] && s->eof_tx[SIDE_BACK]
+ ){
+ s->side = SIDE_BACK;
+ }
+
+ /* batch start? initialize struct and scanner */
+ if(!s->started){
+
+ /* load side marker */
+ if(s->source == SOURCE_ADF_BACK){
+ s->side = SIDE_BACK;
+ }
+ else{
+ s->side = SIDE_FRONT;
+ }
+
+ /* load our own private copy of scan params */
+ ret = update_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot update params\n");
+ goto errors;
+ }
+
+ /* switch source */
+ if(s->source == SOURCE_FLATBED){
+ ret = scanner_control(s, SC_function_fb);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot control fb, ignoring\n");
+ }
+ }
+ else{
+ ret = scanner_control(s, SC_function_adf);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot control adf, ignoring\n");
+ }
+ }
+
+ /* required for hi res scans on iX500? */
+ ret = diag_preread(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot diag_preread %d\n", ret);
+
+ /* enable overscan/auto detection */
+ ret = mode_select_auto(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot mode_select_auto %d\n", ret);
+
+ /* enable double feed detection */
+ ret = mode_select_df(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot mode_select_df %d\n", ret);
+
+ /* enable background color setting */
+ ret = mode_select_bg(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot mode_select_bg %d\n", ret);
+
+ /* enable dropout color setting */
+ ret = mode_select_dropout(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot mode_select_dropout %d\n", ret);
+
+ /* enable buffering setting */
+ ret = mode_select_buff(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot mode_select_buff %d\n", ret);
+
+ /* enable prepick setting */
+ ret = mode_select_prepick(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot mode_select_prepick %d\n", ret);
+
+ /* send endorser config */
+ ret = send_endorser(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot send_endorser %d\n", ret);
+
+ /* set window command */
+ ret = set_window(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot set window\n");
+ goto errors;
+ }
+
+ /* send lut if scanner has no hardware brightness/contrast */
+ if (!s->brightness_steps || !s->contrast_steps){
+ ret = send_lut(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot send_lut %d\n", ret);
+ }
+
+ /* some scanners need the q table sent, even when not scanning jpeg */
+ if (s->need_q_table){
+ ret = send_q_table(s);
+ if (ret != SANE_STATUS_GOOD)
+ DBG (5, "sane_start: WARNING: cannot send_q_table %d\n", ret);
+ }
+
+ /* try to read scan size from scanner */
+ ret = get_pixelsize(s,0);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot get pixelsize\n");
+ goto errors;
+ }
+
+ /* make backup copy of params because later functions overwrite */
+ ret = backup_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot backup params\n");
+ goto errors;
+ }
+
+ /* start/stop endorser */
+ ret = endorser(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot start/stop endorser\n");
+ goto errors;
+ }
+
+ /* turn lamp on */
+ ret = scanner_control(s, SC_function_lamp_on);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: WARNING: cannot start lamp, ignoring\n");
+ }
+ }
+ /* if already running, duplex needs to switch sides */
+ else if(s->source == SOURCE_ADF_DUPLEX){
+ s->side = !s->side;
+ }
+
+ /* restore backup copy of params at the start of each image */
+ ret = restore_params(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot restore params\n");
+ goto errors;
+ }
+
+ /* set clean defaults with new sheet of paper */
+ /* dont reset the transfer vars on backside of duplex page */
+ /* otherwise buffered back page will be lost */
+ /* ingest paper with adf (no-op for fb) */
+ /* dont call object pos or scan on back side of duplex scan */
+ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK){
+
+ s->bytes_rx[0]=0;
+ s->bytes_rx[1]=0;
+ s->lines_rx[0]=0;
+ s->lines_rx[1]=0;
+ s->eof_rx[0]=0;
+ s->eof_rx[1]=0;
+ s->ili_rx[0]=0;
+ s->ili_rx[1]=0;
+ s->eom_rx=0;
+
+ s->bytes_tx[0]=0;
+ s->bytes_tx[1]=0;
+ s->eof_tx[0]=0;
+ s->eof_tx[1]=0;
+
+ s->buff_rx[0]=0;
+ s->buff_rx[1]=0;
+ s->buff_tx[0]=0;
+ s->buff_tx[1]=0;
+
+ /* reset jpeg just in case... */
+ s->jpeg_stage = JPEG_STAGE_HEAD;
+ s->jpeg_ff_offset = 0;
+ s->jpeg_front_rst = 0;
+ s->jpeg_back_rst = 0;
+
+ /* store the number of front bytes */
+ if ( s->source != SOURCE_ADF_BACK ){
+ s->bytes_tot[SIDE_FRONT] = s->s_params.bytes_per_line * s->s_params.lines;
+ s->buff_tot[SIDE_FRONT] = s->buffer_size;
+
+ /* the front buffer is normally very small, but some scanners or
+ * option combinations can't handle it, so we make a big one */
+ if(
+ (s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091)
+ || must_fully_buffer(s)
+ ){
+ s->buff_tot[SIDE_FRONT] = s->bytes_tot[SIDE_FRONT];
+ }
+ }
+ else{
+ s->bytes_tot[SIDE_FRONT] = 0;
+ s->buff_tot[SIDE_FRONT] = 0;
+ }
+
+ /* store the number of back bytes */
+ if ( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK ){
+ s->bytes_tot[SIDE_BACK] = s->s_params.bytes_per_line * s->s_params.lines;
+ s->buff_tot[SIDE_BACK] = s->bytes_tot[SIDE_BACK];
+
+ /* the back buffer is normally very large, but some scanners or
+ * option combinations dont need it, so we make a small one */
+ if(s->low_mem || s->source == SOURCE_ADF_BACK
+ || s->duplex_interlace == DUPLEX_INTERLACE_NONE)
+ s->buff_tot[SIDE_BACK] = s->buffer_size;
+ }
+ else{
+ s->bytes_tot[SIDE_BACK] = 0;
+ s->buff_tot[SIDE_BACK] = 0;
+ }
+
+ /* first page of batch */
+ /* make large buffer to hold the images */
+ /* and set started flag */
+ if(!s->started){
+ ret = setup_buffers(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load buffers\n");
+ goto errors;
+ }
+
+ s->started=1;
+ }
+
+ ret = object_position (s, SANE_TRUE);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot load page\n");
+ goto errors;
+ }
+
+ ret = start_scan (s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot start_scan\n");
+ goto errors;
+ }
+ }
+
+ DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->source);
+
+ /* certain options require the entire image to
+ * be collected from the scanner before we can
+ * tell the user the size of the image. the sane
+ * API has no way to inform the frontend of this,
+ * so we block and buffer. yuck */
+ if( must_fully_buffer(s) ){
+
+ /* get image */
+ while(!s->eof_rx[s->side] && !ret){
+ SANE_Int len = 0;
+ ret = sane_read((SANE_Handle)s, NULL, 0, &len);
+ }
+
+ /* check for errors */
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot buffer image\n");
+ goto errors;
+ }
+
+ DBG (5, "sane_start: OK: done buffering\n");
+
+ /* hardware deskew will tell image size after transfer */
+ ret = get_pixelsize(s,1);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot get final pixelsize\n");
+ goto errors;
+ }
+
+ /* finished buffering, adjust image as required */
+ if(s->swdeskew && (!s->hwdeskewcrop || s->req_driv_crop)){
+ buffer_deskew(s,s->side);
+ }
+ if(s->swcrop && (!s->hwdeskewcrop || s->req_driv_crop)){
+ buffer_crop(s,s->side);
+ }
+ if(s->swdespeck){
+ buffer_despeck(s,s->side);
+ }
+ if(s->swskip){
+ /* Skipping means throwing out this image.
+ * Pretend the user read the whole thing
+ * and call sane_start again.
+ * This assumes we are running in batch mode. */
+ if(buffer_isblank(s,s->side)){
+ s->bytes_tx[s->side] = s->bytes_rx[s->side];
+ s->eof_tx[s->side] = 1;
+ return sane_start(handle);
+ }
+ }
+
+ }
+
+ /* check if user cancelled during this start */
+ ret = check_for_cancel(s);
+
+ /* unprotect this block from sane_cancel */
+ s->reading=0;
+
+ DBG (10, "sane_start: finish %d\n", ret);
+ return ret;
+
+ errors:
+ DBG (10, "sane_start: error %d\n", ret);
+
+ /* if we are started, but something went wrong,
+ * chances are there is image data inside scanner,
+ * which should be discarded via cancel command */
+ if(s->started){
+ s->cancelled = 1;
+ check_for_cancel(s);
+ }
+
+ s->started = 0;
+ s->cancelled = 0;
+ s->reading = 0;
+ return ret;
+}
+
+static SANE_Status
+endorser(struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[ENDORSER_len];
+ size_t cmdLen = ENDORSER_len;
+
+ unsigned char out[ED_max_len];
+ size_t outLen = ED_max_len;
+
+ DBG (10, "endorser: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, ENDORSER_code);
+
+ memset(out,0,outLen);
+
+ if (s->has_endorser_f || s->has_endorser_b){
+
+ /*fi-5900 front side uses 0x80, assume all others*/
+ if(s->u_endorser_side == ED_front){
+ set_ED_endorser_data_id(out,0x80);
+ }
+ else{
+ set_ED_endorser_data_id(out,0);
+ }
+
+ if(s->u_endorser){
+ set_ED_stop(out,ED_start);
+ }
+ else{
+ set_ED_stop(out,ED_stop);
+ }
+
+ set_ED_side(out,s->u_endorser_side);
+
+ if(s->u_endorser_bits == 24){
+ set_ED_lap24(out,ED_lap_24bit);
+ set_ED_initial_count_24(out,s->u_endorser_val);
+ }
+
+ else{
+ outLen = ED_min_len;
+ set_ED_lap24(out,ED_lap_16bit);
+ set_ED_initial_count_16(out,s->u_endorser_val);
+ }
+
+ set_E_xferlen(cmd, outLen);
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+ }
+
+ DBG (10, "endorser: finish %d\n", ret);
+
+ return ret;
+}
+
+static SANE_Status
+scanner_control (struct fujitsu *s, int function)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int tries = 0;
+
+ unsigned char cmd[SCANNER_CONTROL_len];
+ size_t cmdLen = SCANNER_CONTROL_len;
+
+ DBG (10, "scanner_control: start\n");
+
+ if(s->has_cmd_scanner_ctl){
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SCANNER_CONTROL_code);
+ set_SC_function (cmd, function);
+
+ DBG (15, "scanner_control: function %d\n",function);
+
+ /* don't really need to ask for adf if that's the only option */
+ /* doing so causes the 3091 to complain */
+ if(function == SC_function_adf && !s->has_flatbed){
+ DBG (10, "scanner_control: adf function not required\n");
+ return ret;
+ }
+
+ /* extremely long retry period */
+ while(tries++ < 120){
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if(ret == SANE_STATUS_GOOD || function != SC_function_lamp_on){
+ break;
+ }
+
+ usleep(500000);
+ }
+
+ if(ret == SANE_STATUS_GOOD){
+ DBG (15, "scanner_control: success, tries %d, ret %d\n",tries,ret);
+ }
+ else{
+ DBG (5, "scanner_control: error, tries %d, ret %d\n",tries,ret);
+ }
+ }
+
+ DBG (10, "scanner_control: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+scanner_control_ric (struct fujitsu *s, int bytes, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int tries = 0;
+
+ unsigned char cmd[SCANNER_CONTROL_len];
+ size_t cmdLen = SCANNER_CONTROL_len;
+
+ DBG (10, "scanner_control_ric: start\n");
+
+ if(s->has_cmd_scanner_ctl){
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SCANNER_CONTROL_code);
+
+ set_SC_ric(cmd, 1);
+ if (side == SIDE_BACK) {
+ set_SC_ric_dtq(cmd, WD_wid_back);
+ }
+ else{
+ set_SC_ric_dtq(cmd, WD_wid_front);
+ }
+
+ set_SC_ric_len(cmd, bytes);
+
+ DBG (15, "scanner_control_ric: %d %d\n",bytes,side);
+
+ /* extremely long retry period */
+ while(tries++ < 120){
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if(ret != SANE_STATUS_DEVICE_BUSY){
+ break;
+ }
+
+ usleep(500000);
+ }
+
+ if(ret == SANE_STATUS_GOOD){
+ DBG (15, "scanner_control_ric: success, tries %d, ret %d\n",tries,ret);
+ }
+ /* some errors pass thru unchanged */
+ else if(ret == SANE_STATUS_CANCELLED || ret == SANE_STATUS_JAMMED
+ || ret == SANE_STATUS_NO_DOCS || ret == SANE_STATUS_COVER_OPEN
+ ){
+ DBG (5, "scanner_control_ric: error, tries %d, ret %d\n",tries,ret);
+ }
+ /* other errors are ignored, since scanner may not support RIC */
+ else{
+ DBG (5, "scanner_control_ric: ignoring, tries %d, ret %d\n",tries,ret);
+ ret = SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (10, "scanner_control_ric: finish\n");
+
+ return ret;
+}
+
+/*
+ * callocs a buffer to hold the scan data
+ */
+static SANE_Status
+setup_buffers (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side;
+
+ DBG (10, "setup_buffers: start\n");
+
+ for(side=0;side<2;side++){
+
+ /* free old mem */
+ if (s->buffers[side]) {
+ DBG (15, "setup_buffers: free buffer %d.\n",side);
+ free(s->buffers[side]);
+ s->buffers[side] = NULL;
+ }
+
+ if(s->buff_tot[side]){
+ s->buffers[side] = calloc (1,s->buff_tot[side]);
+
+ if (!s->buffers[side]) {
+ DBG (5, "setup_buffers: Error, no buffer %d.\n",side);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ DBG (10, "setup_buffers: finish\n");
+
+ return ret;
+}
+
+/*
+ * This routine issues a SCSI SET WINDOW command to the scanner, using the
+ * values currently in the scanner data structure.
+ */
+static SANE_Status
+set_window (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ /* The command specifies the number of bytes in the data phase
+ * the data phase has a header, followed by 1 or 2 window desc blocks
+ * the header specifies the number of bytes in 1 window desc block
+ */
+
+ unsigned char cmd[SET_WINDOW_len];
+ size_t cmdLen = SET_WINDOW_len;
+
+ /*this is max size, we might send less below*/
+ unsigned char out[SW_header_len + SW_desc_len + SW_desc_len];
+ size_t outLen = SW_header_len + SW_desc_len + SW_desc_len;
+
+ unsigned char * header = out; /*header*/
+ unsigned char * desc1 = out + SW_header_len; /*1st desc*/
+ unsigned char * desc2 = out + SW_header_len + SW_desc_len; /*2nd desc*/
+
+ int length = 0;
+
+ DBG (10, "set_window: start\n");
+
+ /*build the payload*/
+ memset(out,0,outLen);
+
+ /* set window desc size in header */
+ set_WPDB_wdblen(header, SW_desc_len);
+
+ /* init the window block */
+ if (s->source == SOURCE_ADF_BACK) {
+ set_WD_wid (desc1, WD_wid_back);
+ }
+ else{
+ set_WD_wid (desc1, WD_wid_front);
+ }
+
+ set_WD_Xres (desc1, s->resolution_x);
+ set_WD_Yres (desc1, s->resolution_y);
+
+ set_WD_ULX (desc1, s->tl_x);
+ /* low-end scanners ignore paper-size,
+ * so we have to center the window ourselves */
+ if(s->cropping_mode == CROP_ABSOLUTE){
+ set_WD_ULX (desc1, s->tl_x + (s->max_x - s->page_width) / 2);
+ }
+
+ set_WD_ULY (desc1, s->tl_y);
+ set_WD_width (desc1, s->s_params.pixels_per_line * 1200/s->resolution_x);
+
+ length = s->s_params.lines * 1200/s->resolution_y;
+
+ /* stupid trick. 3091/2 require reading extra lines,
+ * because they have a gap between R G and B */
+ if(s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){
+ length += (s->color_raster_offset+s->green_offset) * 1200/300 * 2;
+ DBG(5,"set_window: Increasing length to %d\n",length);
+ }
+ set_WD_length (desc1, length);
+
+ set_WD_brightness (desc1, 0);
+ if(s->brightness_steps){
+ /*convert our common -127 to +127 range into HW's range
+ *FIXME: this code assumes hardware range of 0-255 */
+ set_WD_brightness (desc1, s->brightness+128);
+ }
+
+ set_WD_threshold (desc1, s->threshold);
+
+ set_WD_contrast (desc1, 0);
+ if(s->contrast_steps){
+ /*convert our common -127 to +127 range into HW's range
+ *FIXME: this code assumes hardware range of 0-255 */
+ set_WD_contrast (desc1, s->contrast+128);
+ }
+
+ set_WD_composition (desc1, s->s_mode);
+
+ set_WD_bitsperpixel (desc1, s->s_params.depth);
+
+ if(s->s_mode == MODE_HALFTONE){
+ set_WD_ht_type(desc1, s->ht_type);
+ set_WD_ht_pattern(desc1, s->ht_pattern);
+ }
+
+ set_WD_rif (desc1, s->rif);
+
+ set_WD_compress_type(desc1, COMP_NONE);
+ set_WD_compress_arg(desc1, 0);
+
+#ifdef SANE_FRAME_JPEG
+ /* some scanners support jpeg image compression, for color/gs only */
+ if(s->s_params.format == SANE_FRAME_JPEG){
+ set_WD_compress_type(desc1, COMP_JPEG);
+ set_WD_compress_arg(desc1, s->compress_arg);
+ }
+#endif
+
+ /* the remainder of the block varies based on model and mode,
+ * except for gamma and paper size, those are in the same place */
+
+ /*vuid c0*/
+ if(s->has_vuid_3091){
+ set_WD_vendor_id_code (desc1, WD_VUID_3091);
+ set_WD_gamma (desc1, s->window_gamma);
+
+ if (s->s_mode != MODE_COLOR){
+ switch (s->dropout_color) {
+ case COLOR_RED:
+ set_WD_lamp_color (desc1, WD_LAMP_RED);
+ break;
+ case COLOR_GREEN:
+ set_WD_lamp_color (desc1, WD_LAMP_GREEN);
+ break;
+ case COLOR_BLUE:
+ set_WD_lamp_color (desc1, WD_LAMP_BLUE);
+ break;
+ default:
+ set_WD_lamp_color (desc1, WD_LAMP_DEFAULT);
+ break;
+ }
+ }
+ /*set_WD_quality(desc1,s->quality);*/
+ }
+
+ /*vuid c1*/
+ else if(s->s_mode == MODE_COLOR && s->has_vuid_color){
+ set_WD_vendor_id_code (desc1, WD_VUID_COLOR);
+ set_WD_gamma (desc1, s->window_gamma);
+
+ if(s->color_interlace == COLOR_INTERLACE_RGB){
+ set_WD_scanning_order (desc1, WD_SCAN_ORDER_DOT);
+ set_WD_scanning_order_arg (desc1, WD_SCAN_ARG_RGB);
+ }
+ else if(s->color_interlace == COLOR_INTERLACE_BGR){
+ set_WD_scanning_order (desc1, WD_SCAN_ORDER_DOT);
+ set_WD_scanning_order_arg (desc1, WD_SCAN_ARG_BGR);
+ }
+ else if(s->color_interlace == COLOR_INTERLACE_RRGGBB){
+ set_WD_scanning_order (desc1, WD_SCAN_ORDER_LINE);
+ set_WD_scanning_order_arg (desc1, WD_SCAN_ARG_RGB);
+ }
+ else{
+ DBG (5,"set_window: unknown color interlacing\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*scanner emphasis ranges from 0 to 7f and smoothing from 80 to ff*/
+ /* but we expose them to user as a single linear range smooth->emphasis */
+ /* flip the smooth part over, and tack it onto the upper end of emphasis */
+ if(s->emphasis < 0)
+ set_WD_c1_emphasis(desc1,127-s->emphasis);
+ else
+ set_WD_c1_emphasis(desc1,s->emphasis);
+
+ set_WD_c1_mirroring(desc1,s->mirroring);
+
+ set_WD_wl_follow(desc1,s->wl_follow);
+ }
+
+ /*vuid 00*/
+ else if(s->has_vuid_mono){
+ set_WD_vendor_id_code (desc1, WD_VUID_MONO);
+ set_WD_gamma (desc1, s->window_gamma);
+
+ set_WD_outline(desc1,s->outline);
+
+ /*scanner emphasis ranges from 0 to 7f and smoothing from 80 to ff*/
+ /* but we expose them to user as a single linear range smooth->emphasis */
+ /* flip the smooth part over, and tack it onto the upper end of emphasis */
+ if(s->emphasis < 0)
+ set_WD_emphasis(desc1,127-s->emphasis);
+ else
+ set_WD_emphasis(desc1,s->emphasis);
+
+ set_WD_separation(desc1,s->separation);
+ set_WD_mirroring(desc1,s->mirroring);
+
+ if (s->has_sdtc && s->ipc_mode != WD_ipc_DTC)
+ set_WD_variance(desc1,s->variance);
+
+ if ((s->has_dtc && !s->has_sdtc) || s->ipc_mode == WD_ipc_DTC){
+ set_WD_filtering(desc1,!s->bp_filter);
+ set_WD_smoothing(desc1,!s->smoothing);
+ set_WD_gamma_curve(desc1,s->gamma_curve);
+ set_WD_threshold_curve(desc1,s->threshold_curve);
+ set_WD_noise_removal(desc1,!s->noise_removal);
+ if(s->noise_removal){
+ set_WD_matrix5x5(desc1,s->matrix_5);
+ set_WD_matrix4x4(desc1,s->matrix_4);
+ set_WD_matrix3x3(desc1,s->matrix_3);
+ set_WD_matrix2x2(desc1,s->matrix_2);
+ }
+ set_WD_background(desc1,s->threshold_white);
+ }
+
+ set_WD_wl_follow(desc1,s->wl_follow);
+ set_WD_subwindow_list(desc1,0);
+ set_WD_ipc_mode(desc1,s->ipc_mode);
+ }
+
+ else{
+ DBG (5,"set_window: no vuid to send?\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* common to all vuids */
+ if(s->source == SOURCE_FLATBED){
+ set_WD_paper_selection(desc1,WD_paper_SEL_UNDEFINED);
+ }
+ else{
+ set_WD_paper_selection (desc1, WD_paper_SEL_NON_STANDARD);
+
+ /* call helper function, scanner wants lies about paper width */
+ set_WD_paper_width_X (desc1, get_page_width(s));
+
+ /* dont call helper function, scanner wants actual length? */
+ set_WD_paper_length_Y (desc1, s->page_height);
+ }
+
+ /* when in duplex mode, copy first desc block into second */
+ if (s->source == SOURCE_ADF_DUPLEX) {
+ memcpy (desc2, desc1, SW_desc_len);
+
+ set_WD_wid (desc2, WD_wid_back);
+
+ /* FIXME: do we really need these on back of page? */
+ set_WD_paper_selection (desc2, WD_paper_SEL_UNDEFINED);
+ set_WD_paper_width_X (desc2, 0);
+ set_WD_paper_length_Y (desc2, 0);
+ }
+ /* output shorter if not using duplex */
+ else{
+ outLen -= SW_desc_len;
+ }
+
+ /*build the command*/
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SET_WINDOW_code);
+ set_SW_xferlen(cmd, outLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "set_window: finish\n");
+
+ return ret;
+}
+
+/* update s_params with actual data size scanner reports */
+/* then copy as required to the u_params to send to user */
+static SANE_Status
+get_pixelsize(struct fujitsu *s, int actual)
+{
+ SANE_Status ret;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char in[R_PSIZE_len];
+ size_t inLen = R_PSIZE_len;
+
+ DBG (10, "get_pixelsize: start %d\n",actual);
+
+ if (!s->has_pixelsize){
+ DBG (10, "get_pixelsize: unsupported\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, R_datatype_pixelsize);
+ if(s->source == SOURCE_ADF_BACK){
+ set_R_window_id (cmd, WD_wid_back);
+ }
+ else{
+ set_R_window_id (cmd, WD_wid_front);
+ }
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+ if (ret == SANE_STATUS_GOOD){
+
+ /* when we are called post-scan, the scanner may give
+ * more accurate data in other fields */
+ if(actual && get_PSIZE_paper_w(in)){
+ s->s_params.pixels_per_line = get_PSIZE_paper_w(in);
+ DBG(5,"get_pixelsize: Actual width\n");
+ }
+ else{
+ s->s_params.pixels_per_line = get_PSIZE_num_x(in);
+ }
+
+ /* stupid trick. 3091/2 require reading extra lines,
+ * because they have a gap between R G and B
+ * we only want to report the shorter value to the frontend */
+ if(s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){
+ DBG(5,"get_pixelsize: Ignoring length %d\n",get_PSIZE_num_y(in));
+ }
+ /* when we are called post-scan, the scanner may give
+ * more accurate data in other fields */
+ else if(actual && get_PSIZE_paper_l(in)){
+ s->s_params.lines = get_PSIZE_paper_l(in);
+ DBG(5,"get_pixelsize: Actual length\n");
+ }
+ else{
+ s->s_params.lines = get_PSIZE_num_y(in);
+ }
+
+ /* bytes per line differs by mode */
+ if (s->s_mode == MODE_COLOR) {
+ s->s_params.bytes_per_line = s->s_params.pixels_per_line * 3;
+ }
+ else if (s->s_mode == MODE_GRAYSCALE) {
+ s->s_params.bytes_per_line = s->s_params.pixels_per_line;
+ }
+ else {
+ s->s_params.bytes_per_line = s->s_params.pixels_per_line / 8;
+ }
+
+ /* some scanners can request that the driver clean img */
+ if(get_PSIZE_req_driv_valid(in)){
+ s->req_driv_crop = get_PSIZE_req_driv_crop(in);
+ s->req_driv_lut = get_PSIZE_req_driv_lut(in);
+ DBG(5,"get_pixelsize: scanner requests: crop=%d, lut=%d\n",
+ s->req_driv_crop,s->req_driv_lut);
+ }
+
+ DBG (15, "get_pixelsize: scan_x=%d, Bpl=%d, scan_y=%d\n",
+ s->s_params.pixels_per_line, s->s_params.bytes_per_line, s->s_params.lines );
+
+ /* the user params are usually the same */
+ s->u_params.pixels_per_line = s->s_params.pixels_per_line;
+ s->u_params.lines = s->s_params.lines;
+
+ /* bytes per line differs by mode */
+ if (s->u_mode == MODE_COLOR) {
+ s->u_params.bytes_per_line = s->u_params.pixels_per_line * 3;
+ }
+ else if (s->u_mode == MODE_GRAYSCALE) {
+ s->u_params.bytes_per_line = s->u_params.pixels_per_line;
+ }
+ else {
+ s->u_params.bytes_per_line = s->u_params.pixels_per_line / 8;
+ }
+
+ }
+ else{
+ DBG (10, "get_pixelsize: got bad status %d, ignoring\n", ret);
+ s->has_pixelsize = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "get_pixelsize: finish\n");
+
+ return ret;
+}
+
+/*
+ * Issues the SCSI OBJECT POSITION command if an ADF is in use.
+ */
+static SANE_Status
+object_position (struct fujitsu *s, int i_load)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[OBJECT_POSITION_len];
+ size_t cmdLen = OBJECT_POSITION_len;
+
+ DBG (10, "object_position: start\n");
+
+ if (s->source == SOURCE_FLATBED) {
+ DBG (10, "object_position: flatbed no-op\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ if(s->hopper_before_op && i_load){
+ ret = get_hardware_status(s,0);
+ if(!s->hw_hopper){
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, OBJECT_POSITION_code);
+
+ if (i_load) {
+ DBG (15, "object_position: load\n");
+ set_OP_autofeed (cmd, OP_Feed);
+ }
+ else {
+ DBG (15, "object_position: eject\n");
+ set_OP_autofeed (cmd, OP_Discharge);
+ }
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if(!s->no_wait_after_op)
+ wait_scanner (s);
+
+ DBG (10, "object_position: finish\n");
+
+ return ret;
+}
+
+/*
+ * Issues SCAN command.
+ *
+ * (This doesn't actually read anything, it just tells the scanner
+ * to start scanning.)
+ */
+static SANE_Status
+start_scan (struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SCAN_len];
+ size_t cmdLen = SCAN_len;
+
+ unsigned char out[] = {WD_wid_front, WD_wid_back};
+ size_t outLen = 2;
+
+ DBG (10, "start_scan: start\n");
+
+ if (s->source != SOURCE_ADF_DUPLEX) {
+ outLen--;
+ if(s->source == SOURCE_ADF_BACK) {
+ out[0] = WD_wid_back;
+ }
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, SCAN_code);
+ set_SC_xfer_length (cmd, outLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, outLen,
+ NULL, NULL
+ );
+
+ DBG (10, "start_scan: finish\n");
+
+ return ret;
+}
+
+/* checks started and cancelled flags in scanner struct,
+ * sends cancel command to scanner if required. don't call
+ * this function asyncronously, wait for pending operation */
+static SANE_Status
+check_for_cancel(struct fujitsu *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "check_for_cancel: start\n");
+
+ if(s->started && s->cancelled){
+ DBG (15, "check_for_cancel: cancelling\n");
+
+ /* cancel scan */
+ ret = scanner_control(s, SC_function_cancel);
+ if (ret == SANE_STATUS_GOOD) {
+ ret = SANE_STATUS_CANCELLED;
+ }
+ else{
+ DBG (5, "check_for_cancel: ERROR: cannot cancel\n");
+ }
+
+ s->started = 0;
+ s->cancelled = 0;
+ }
+ else if(s->cancelled){
+ DBG (15, "check_for_cancel: already cancelled\n");
+ ret = SANE_STATUS_CANCELLED;
+ s->cancelled = 0;
+ }
+
+ DBG (10, "check_for_cancel: finish %d\n",ret);
+ return ret;
+}
+
+/*
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ struct fujitsu *s = (struct fujitsu *) handle;
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "sane_read: start\n");
+
+ *len=0;
+
+ /* maybe cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: not started, call sane_start\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* sane_start required between sides */
+ if(s->eof_rx[s->side] && s->bytes_tx[s->side] == s->bytes_rx[s->side]){
+ DBG (15, "sane_read: returning eof\n");
+ s->eof_tx[s->side] = 1;
+
+ /* swap sides if user asked for low-mem mode, we are duplexing,
+ * and there is data waiting on the other side */
+ if(s->low_mem && s->source == SOURCE_ADF_DUPLEX
+ && (s->bytes_rx[!s->side] > s->bytes_tx[!s->side]
+ || (s->eof_rx[!s->side] && !s->eof_tx[!s->side])
+ )
+ ){
+ s->side = !s->side;
+ }
+
+ return SANE_STATUS_EOF;
+ }
+
+ /* protect this block from sane_cancel */
+ s->reading = 1;
+
+ /* ----------------------------------------------
+ * try to read some data from scanner into buffer
+ * these functions are expected not to overrun */
+
+ /* 3091/2 are on crack, get their own duplex reader function */
+ if(s->source == SOURCE_ADF_DUPLEX
+ && s->duplex_interlace == DUPLEX_INTERLACE_3091
+ ){
+ ret = read_from_3091duplex(s);
+ if(ret){
+ DBG(5,"sane_read: 3091 returning %d\n",ret);
+ return ret;
+ }
+ } /* end 3091 */
+
+#ifdef SANE_FRAME_JPEG
+ /* alternating jpeg duplex interlacing */
+ else if(s->source == SOURCE_ADF_DUPLEX
+ && s->s_params.format == SANE_FRAME_JPEG
+ && s->jpeg_interlace == JPEG_INTERLACE_ALT
+ ){
+ ret = read_from_JPEGduplex(s);
+ if(ret){
+ DBG(5,"sane_read: jpeg duplex returning %d\n",ret);
+ return ret;
+ }
+ } /* end alt jpeg */
+#endif
+
+ /* alternating pnm duplex interlacing */
+ else if(s->source == SOURCE_ADF_DUPLEX
+ && s->s_params.format <= SANE_FRAME_RGB
+ && s->duplex_interlace == DUPLEX_INTERLACE_ALT
+ ){
+
+ /* buffer front side */
+ ret = read_from_scanner(s, SIDE_FRONT);
+ if(ret){
+ DBG(5,"sane_read: front returning %d\n",ret);
+ return ret;
+ }
+
+ /* buffer back side, but don't get too far ahead of the front! */
+ if(s->bytes_rx[SIDE_BACK] < s->bytes_rx[SIDE_FRONT] + s->buffer_size){
+ ret = read_from_scanner(s, SIDE_BACK);
+ if(ret){
+ DBG(5,"sane_read: back returning %d\n",ret);
+ return ret;
+ }
+ }
+ } /* end alt pnm */
+
+ /* simplex or non-alternating duplex */
+ else{
+ ret = read_from_scanner(s, s->side);
+ if(ret){
+ DBG(5,"sane_read: side %d returning %d\n",s->side,ret);
+ return ret;
+ }
+ } /*end simplex*/
+
+ /* uncommon case, downsample and copy a block from buffer to frontend */
+ if(must_downsample(s)){
+ ret = downsample_from_buffer(s,buf,max_len,len,s->side);
+ }
+
+ /* common case, memcpy a block from buffer to frontend */
+ else{
+ ret = read_from_buffer(s,buf,max_len,len,s->side);
+ }
+
+ /*finished sending small buffer, reset it*/
+ if(s->buff_tx[s->side] == s->buff_rx[s->side]
+ && s->buff_tot[s->side] < s->bytes_tot[s->side]
+ ){
+ DBG (15, "sane_read: reset buffers\n");
+ s->buff_rx[s->side] = 0;
+ s->buff_tx[s->side] = 0;
+ }
+
+ /* check if user cancelled during this read */
+ ret = check_for_cancel(s);
+
+ /* swap sides if user asked for low-mem mode, we are duplexing,
+ * and there is data waiting on the other side */
+ if(s->low_mem && s->source == SOURCE_ADF_DUPLEX
+ && (s->bytes_rx[!s->side] > s->bytes_tx[!s->side]
+ || (s->eof_rx[!s->side] && !s->eof_tx[!s->side])
+ )
+ ){
+ s->side = !s->side;
+ }
+
+ /* unprotect this block from sane_cancel */
+ s->reading = 0;
+
+ DBG (10, "sane_read: finish %d\n", ret);
+ return ret;
+}
+
+#ifdef SANE_FRAME_JPEG
+static SANE_Status
+read_from_JPEGduplex(struct fujitsu *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char * in;
+ size_t inLen = 0;
+
+ int bytes = s->buffer_size;
+ int i = 0;
+
+ DBG (10, "read_from_JPEGduplex: start\n");
+
+ if(s->eof_rx[SIDE_FRONT] && s->eof_rx[SIDE_BACK]){
+ DBG (10, "read_from_JPEGduplex: already have eofs, done\n");
+ return ret;
+ }
+
+ /* we don't know if the following read will give us front or back data
+ * so we only get enough to fill whichever is smaller (and not yet done) */
+ if(!s->eof_rx[SIDE_FRONT]){
+ int avail = s->buff_tot[SIDE_FRONT] - s->buff_rx[SIDE_FRONT];
+ if(bytes > avail)
+ bytes = avail;
+ }
+ if(!s->eof_rx[SIDE_BACK]){
+ int avail = s->buff_tot[SIDE_BACK] - s->buff_rx[SIDE_BACK];
+ if(bytes > avail)
+ bytes = avail;
+ }
+
+ DBG(15, "read_from_JPEGduplex: fto:%d frx:%d bto:%d brx:%d pa:%d\n",
+ s->bytes_tot[SIDE_FRONT], s->bytes_rx[SIDE_FRONT],
+ s->bytes_tot[SIDE_BACK], s->bytes_rx[SIDE_BACK],
+ bytes);
+
+ /* this will happen if buffer is not drained yet */
+ if(bytes < 1){
+ DBG(5, "read_from_JPEGduplex: Warning: no bytes this pass\n");
+ return ret;
+ }
+
+ /* fi-6770A gets mad if you 'read' too soon on usb, see if it is ready */
+ if(!s->bytes_rx[SIDE_FRONT] && s->connection == CONNECTION_USB){
+ DBG (15, "read: start of usb page, checking RIC\n");
+ ret = scanner_control_ric(s,bytes,SIDE_FRONT);
+ if(ret){
+ DBG(5,"read: ric returning %d\n",ret);
+ return ret;
+ }
+ }
+
+ inLen = bytes;
+ in = malloc(inLen);
+ if(!in){
+ DBG(5, "read_from_JPEGduplex: not enough mem for buffer: %d\n",(int)inLen);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, R_datatype_imagedata);
+ /* interlaced jpeg duplex always reads from front */
+ set_R_window_id (cmd, WD_wid_front);
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_JPEGduplex: got GOOD/EOF, returning GOOD\n");
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_JPEGduplex: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_JPEGduplex: error reading data status = %d\n", ret);
+ inLen = 0;
+ }
+
+ for(i=0;i<(int)inLen;i++){
+
+ /* about to change stage */
+ if(in[i] == 0xff){
+ s->jpeg_ff_offset=0;
+ continue;
+ }
+
+ /* last byte was an ff, this byte will change stage */
+ if(s->jpeg_ff_offset == 0){
+
+ /* headers (SOI/HuffTab/QTab/DRI), in both sides */
+ if(in[i] == 0xd8 || in[i] == 0xc4
+ || in[i] == 0xdb || in[i] == 0xdd){
+ s->jpeg_stage = JPEG_STAGE_HEAD;
+ DBG(15, "read_from_JPEGduplex: stage head\n");
+ }
+
+ /* start of frame, in both sides, update x first */
+ else if(in[i]==0xc0){
+ s->jpeg_stage = JPEG_STAGE_SOF;
+ DBG(15, "read_from_JPEGduplex: stage sof\n");
+ }
+
+ /* start of scan, first few bytes of marker in both sides
+ * but rest in front */
+ else if(in[i]==0xda){
+ s->jpeg_stage = JPEG_STAGE_SOS;
+ DBG(15, "read_from_JPEGduplex: stage sos\n");
+ }
+
+ /* found image block. images are not interlaced */
+ /* copy to front, don't change RST */
+ else if(in[i] >= 0xd0 && in[i] <= 0xd7
+ && s->jpeg_interlace == JPEG_INTERLACE_NONE){
+ s->jpeg_stage = JPEG_STAGE_FRONT;
+ DBG(35, "read_from_JPEGduplex: stage front (all)\n");
+ }
+
+ /* found even numbered image block. */
+ /* images are interlaced, so switch to back. */
+ /* also change from even RST to proper one */
+ else if(in[i] == 0xd0 || in[i] == 0xd2
+ || in[i] == 0xd4 || in[i] == 0xd6){
+ s->jpeg_stage = JPEG_STAGE_BACK;
+ DBG(35, "read_from_JPEGduplex: stage back\n");
+
+ /* skip first RST for back side*/
+ if(!s->jpeg_back_rst){
+ DBG(15, "read_from_JPEGduplex: stage back jump\n");
+ s->jpeg_ff_offset++;
+ s->jpeg_back_rst++;
+ continue;
+ }
+
+ in[i] = 0xd0 + (s->jpeg_back_rst-1) % 8;
+ s->jpeg_back_rst++;
+ }
+
+ /* finished back image block, switch to front */
+ /* also change from odd RST to proper one */
+ else if(in[i] == 0xd1 || in[i] == 0xd3
+ || in[i] == 0xd5 || in[i] == 0xd7){
+ s->jpeg_stage = JPEG_STAGE_FRONT;
+ DBG(35, "read_from_JPEGduplex: stage front\n");
+ in[i] = 0xd0 + (s->jpeg_front_rst % 8);
+ s->jpeg_front_rst++;
+ }
+
+ /* finished image, update totals */
+ else if(in[i]==0xd9){
+ s->jpeg_stage = JPEG_STAGE_EOI;
+ DBG(15, "read_from_JPEGduplex: stage eoi %d %d\n",(int)inLen,i);
+ }
+ }
+ s->jpeg_ff_offset++;
+
+ /* first x byte in start of frame, buffer it */
+ if(s->jpeg_stage == JPEG_STAGE_SOF && s->jpeg_ff_offset == 7){
+ s->jpeg_x_byte = in[i];
+ continue;
+ }
+
+ /* second x byte in start of frame */
+ if(s->jpeg_stage == JPEG_STAGE_SOF && s->jpeg_ff_offset == 8){
+
+ int width = (s->jpeg_x_byte << 8) | in[i];
+
+ /* if image width equals what we asked for, then
+ * the image is not interlaced, clean up the mess */
+ if(width == s->s_params.pixels_per_line){
+
+ DBG(15, "read_from_JPEGduplex: right width, req:%d got:%d\n",
+ s->s_params.pixels_per_line,width);
+
+ /* stop copying to the back */
+ s->jpeg_interlace = JPEG_INTERLACE_NONE;
+
+ /* clear what is already in the back */
+ s->bytes_rx[SIDE_BACK]=0;
+ s->lines_rx[SIDE_BACK]=0;
+ s->buff_rx[SIDE_BACK]=0;
+
+ /* and put the high-order width byte into front unchanged */
+ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = s->jpeg_x_byte;
+ s->bytes_rx[SIDE_FRONT]++;
+ }
+
+ /* image is interlaced afterall, continue */
+ else{
+ DBG(15, "read_from_JPEGduplex: wrong width, req:%d got:%d\n",
+ s->s_params.pixels_per_line,width);
+
+ /* put the high-order width byte into front side, shifted down */
+ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = width >> 9;
+ s->bytes_rx[SIDE_FRONT]++;
+
+ /* put the high-order width byte into back side, shifted down */
+ s->buffers[SIDE_BACK][s->buff_rx[SIDE_BACK]++] = width >> 9;
+ s->bytes_rx[SIDE_BACK]++;
+
+ /* shift down low order byte */
+ in[i] = (width >> 1) & 0xff;
+ }
+ }
+
+ /* copy these stages to front */
+ if(s->jpeg_stage == JPEG_STAGE_HEAD
+ || s->jpeg_stage == JPEG_STAGE_SOF
+ || s->jpeg_stage == JPEG_STAGE_SOS
+ || s->jpeg_stage == JPEG_STAGE_EOI
+ || s->jpeg_stage == JPEG_STAGE_FRONT
+ ){
+ /* first byte after ff, send the ff first */
+ if(s->jpeg_ff_offset == 1){
+ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = 0xff;
+ s->bytes_rx[SIDE_FRONT]++;
+ }
+ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = in[i];
+ s->bytes_rx[SIDE_FRONT]++;
+ }
+
+ /* copy these stages to back */
+ if( s->jpeg_interlace == JPEG_INTERLACE_ALT
+ &&
+ ( s->jpeg_stage == JPEG_STAGE_HEAD
+ || s->jpeg_stage == JPEG_STAGE_SOF
+ || s->jpeg_stage == JPEG_STAGE_SOS
+ || s->jpeg_stage == JPEG_STAGE_EOI
+ || s->jpeg_stage == JPEG_STAGE_BACK )
+ ){
+ /* first byte after ff, send the ff first */
+ if(s->jpeg_ff_offset == 1){
+ s->buffers[SIDE_BACK][s->buff_rx[SIDE_BACK]++] = 0xff;
+ s->bytes_rx[SIDE_BACK]++;
+ }
+ s->buffers[SIDE_BACK][s->buff_rx[SIDE_BACK]++] = in[i];
+ s->bytes_rx[SIDE_BACK]++;
+ }
+
+ /* reached last byte of SOS section, next byte front */
+ if(s->jpeg_stage == JPEG_STAGE_SOS && s->jpeg_ff_offset == 0x0d){
+ s->jpeg_stage = JPEG_STAGE_FRONT;
+ }
+
+ /* last byte of file, update totals, bail out */
+ if(s->jpeg_stage == JPEG_STAGE_EOI){
+ s->eof_rx[SIDE_FRONT] = 1;
+ if(s->jpeg_interlace == JPEG_INTERLACE_ALT)
+ s->eof_rx[SIDE_BACK] = 1;
+ }
+ }
+
+ free(in);
+
+ /* jpeg uses in-band EOI marker, so this is ususally redundant */
+ if(ret == SANE_STATUS_EOF){
+ DBG(15, "read_from_JPEGduplex: got EOF, finishing\n");
+ s->eof_rx[SIDE_FRONT] = 1;
+ if(s->jpeg_interlace == JPEG_INTERLACE_ALT)
+ s->eof_rx[SIDE_BACK] = 1;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "read_from_JPEGduplex: finish\n");
+
+ return ret;
+}
+#endif
+
+static SANE_Status
+read_from_3091duplex(struct fujitsu *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char * in;
+ size_t inLen = 0;
+
+ int side = SIDE_FRONT;
+ int bytes = s->buffer_size;
+ int off = (s->duplex_raster_offset+s->duplex_offset) * s->resolution_y/300;
+ unsigned int i;
+
+ DBG (10, "read_from_3091duplex: start\n");
+
+ if(s->eof_rx[SIDE_FRONT] && s->eof_rx[SIDE_BACK]){
+ DBG (10, "read_from_3091duplex: already have eofs, done\n");
+ return ret;
+ }
+
+ /* we don't know if the following read will give us front,back or both data
+ * so we only get enough to fill whichever is smaller (and not yet done) */
+ if(!s->eof_rx[SIDE_FRONT]){
+ int avail = s->buff_tot[SIDE_FRONT] - s->buff_rx[SIDE_FRONT];
+ if(bytes > avail)
+ bytes = avail;
+ }
+ if(!s->eof_rx[SIDE_BACK]){
+ int avail = s->buff_tot[SIDE_BACK] - s->buff_rx[SIDE_BACK];
+ if(bytes > avail)
+ bytes = avail;
+ }
+
+ /* all requests must end on a line boundary */
+ bytes -= (bytes % s->s_params.bytes_per_line);
+
+ DBG(15, "read_from_3091duplex: front img: to:%d rx:%d tx:%d li:%d\n",
+ s->bytes_tot[SIDE_FRONT], s->bytes_rx[SIDE_FRONT],
+ s->bytes_tx[SIDE_FRONT], s->lines_rx[SIDE_FRONT]);
+
+ DBG(15, "read_from_3091duplex: front buf: to:%d rx:%d tx:%d\n",
+ s->buff_tot[SIDE_FRONT], s->buff_rx[SIDE_FRONT],
+ s->buff_tx[SIDE_FRONT]);
+
+ DBG(15, "read_from_3091duplex: back img: to:%d rx:%d tx:%d li:%d\n",
+ s->bytes_tot[SIDE_BACK], s->bytes_rx[SIDE_BACK],
+ s->bytes_tx[SIDE_BACK], s->lines_rx[SIDE_BACK]);
+
+ DBG(15, "read_from_3091duplex: back buf: to:%d rx:%d tx:%d\n",
+ s->buff_tot[SIDE_BACK], s->buff_rx[SIDE_BACK],
+ s->buff_tx[SIDE_BACK]);
+
+ DBG(15, "read_from_3091duplex: bu:%d pa:%d of:%d\n",
+ s->buffer_size, bytes, off);
+
+ /* this could happen if the front buffer is not drained fast enough */
+ if(bytes < 1){
+ DBG(10, "read_from_3091duplex: Warning: no bytes this pass\n");
+ return ret;
+ }
+
+ inLen = bytes;
+
+ in = malloc(inLen);
+ if(!in){
+ DBG(5, "read_from_3091duplex: not enough mem for buffer: %d\n",(int)inLen);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, R_datatype_imagedata);
+ /* 3091 duplex always reads from front */
+ set_R_window_id (cmd, WD_wid_front);
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_3091duplex: got GOOD/EOF, returning GOOD\n");
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_3091duplex: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_3091duplex: error reading data block status = %d\n", ret);
+ inLen = 0;
+ }
+
+ /* loop thru all lines in read buffer */
+ for(i=0;i<inLen/s->s_params.bytes_per_line;i++){
+
+ /* start is front */
+ if(s->lines_rx[SIDE_FRONT] < off){
+ side=SIDE_FRONT;
+ }
+
+ /* end is back */
+ else if(s->eof_rx[SIDE_FRONT]){
+ side=SIDE_BACK;
+ }
+
+ /* odd are back */
+ else if( ((s->lines_rx[SIDE_FRONT] + s->lines_rx[SIDE_BACK] - off) % 2) ){
+ side=SIDE_BACK;
+ }
+
+ /* even are front */
+ else{
+ side=SIDE_FRONT;
+ }
+
+ if(s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){
+ copy_3091 (s, in + i*s->s_params.bytes_per_line, s->s_params.bytes_per_line, side);
+ }
+ else{
+ copy_buffer (s, in + i*s->s_params.bytes_per_line, s->s_params.bytes_per_line, side);
+ }
+ }
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(15, "read_from_3091duplex: got EOF, finishing both sides\n");
+ s->eof_rx[SIDE_FRONT] = 1;
+ s->eof_rx[SIDE_BACK] = 1;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ free(in);
+
+ DBG (10, "read_from_3091duplex: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner(struct fujitsu *s, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+
+ unsigned char * in;
+ size_t inLen = 0;
+
+ int bytes = s->buffer_size;
+ int avail = s->buff_tot[side] - s->buff_rx[side];
+ int remain = s->bytes_tot[side] - s->bytes_rx[side];
+
+ DBG (10, "read_from_scanner: start %d\n", side);
+
+ if(s->eof_rx[side]){
+ DBG (10, "read_from_scanner: already have eof, done\n");
+ return ret;
+ }
+
+ /* figure out the max amount to transfer */
+ if(bytes > avail)
+ bytes = avail;
+
+ /* all requests must end on line boundary */
+ bytes -= (bytes % s->s_params.bytes_per_line);
+
+ /* some larger scanners require even bytes per block */
+ /* so we get even lines, but not on the last block */
+ /* cause odd number of lines would never finish */
+ if(bytes % 2 && bytes < remain){
+ bytes -= s->s_params.bytes_per_line;
+ }
+
+ DBG(15, "read_from_scanner: si:%d re:%d bs:%d by:%d av:%d\n",
+ side, remain, s->buffer_size, bytes, avail);
+
+ DBG(15, "read_from_scanner: img to:%d rx:%d tx:%d li:%d\n",
+ s->bytes_tot[side], s->bytes_rx[side], s->bytes_tx[side],
+ s->lines_rx[side]);
+
+ DBG(15, "read_from_scanner: buf to:%d rx:%d tx:%d\n",
+ s->buff_tot[side], s->buff_rx[side], s->buff_tx[side]);
+
+ /* this will happen if buffer is not drained yet */
+ if(bytes < 1){
+ DBG(5, "read_from_scanner: no bytes this pass\n");
+ return ret;
+ }
+
+ /* fi-6770A gets mad if you 'read' too soon on usb, see if it is ready */
+ if(!s->bytes_rx[side] && s->connection == CONNECTION_USB){
+ DBG (15, "read_from_scanner: start of usb page, checking RIC\n");
+ ret = scanner_control_ric(s,bytes,side);
+ if(ret){
+ DBG(5,"read_from_scanner: ric returning %d\n",ret);
+ return ret;
+ }
+ }
+
+ inLen = bytes;
+ in = malloc(inLen);
+ if(!in){
+ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",(int)inLen);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+ set_R_datatype_code (cmd, R_datatype_imagedata);
+
+ if (side == SIDE_BACK) {
+ set_R_window_id (cmd, WD_wid_back);
+ }
+ else{
+ set_R_window_id (cmd, WD_wid_front);
+ }
+
+ set_R_xfer_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_scanner: got GOOD/EOF, returning GOOD\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_scanner: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_scanner: error reading data block status = %d\n",ret);
+ inLen = 0;
+ }
+
+ DBG(15, "read_from_scanner: read %lu bytes\n",(unsigned long)inLen);
+
+ if(inLen){
+ if(s->s_mode==MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){
+ copy_3091 (s, in, inLen, side);
+ }
+ else{
+ copy_buffer (s, in, inLen, side);
+ }
+ }
+
+ free(in);
+
+ /* if this was a short read or not, log it */
+ s->ili_rx[side] = s->rs_ili;
+ if(s->ili_rx[side]){
+ DBG(15, "read_from_scanner: got ILI\n");
+ }
+
+ /* if this was an end of medium, log it */
+ if(s->rs_eom){
+ DBG(15, "read_from_scanner: got EOM\n");
+ s->eom_rx = 1;
+ }
+
+ /* paper ran out. lets try to set the eof flag on both sides,
+ * but only if that side had a short read last time */
+ if(s->eom_rx){
+ int i;
+ for(i=0;i<2;i++){
+ if(s->ili_rx[i]){
+ DBG(15, "read_from_scanner: finishing side %d\n",i);
+ s->eof_rx[i] = 1;
+ }
+ }
+ }
+
+ DBG (10, "read_from_scanner: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+copy_3091(struct fujitsu *s, unsigned char * buf, int len, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int i, j, dest, boff, goff;
+
+ DBG (10, "copy_3091: start\n");
+
+ /* Data is RR...GG...BB... on each line,
+ * green is back 8 lines from red at 300 dpi
+ * blue is back 4 lines from red at 300 dpi.
+ *
+ * Here, we get things on correct line, and
+ * interlace to make RGBRGB.
+ *
+ * We add the user-supplied offsets before we scale
+ * so that they are independent of scanning resolution.
+ */
+ goff = (s->color_raster_offset+s->green_offset) * s->resolution_y/150;
+ boff = (s->color_raster_offset+s->blue_offset) * s->resolution_y/300;
+
+ /* loop thru all lines in read buffer */
+ for(i=0;i<len;i+=s->s_params.bytes_per_line){
+
+ /* red at start of line */
+ dest = s->lines_rx[side] * s->s_params.bytes_per_line;
+
+ if(dest >= 0 && dest < s->bytes_tot[side]){
+ for (j=0; j<s->s_params.pixels_per_line; j++){
+ s->buffers[side][dest+j*3] = buf[i+j];
+ }
+ }
+
+ /* green is in middle of line */
+ dest = (s->lines_rx[side] - goff) * s->s_params.bytes_per_line;
+
+ if(dest >= 0 && dest < s->bytes_tot[side]){
+ for (j=0; j<s->s_params.pixels_per_line; j++){
+ s->buffers[side][dest+j*3+1] = buf[i+s->s_params.pixels_per_line+j];
+ }
+ }
+
+ /* blue is at end of line */
+ dest = (s->lines_rx[side] - boff) * s->s_params.bytes_per_line;
+
+ if(dest >= 0 && dest < s->bytes_tot[side]){
+ for (j=0; j<s->s_params.pixels_per_line; j++){
+ s->buffers[side][dest+j*3+2] = buf[i+2*s->s_params.pixels_per_line+j];
+ }
+ }
+
+ s->lines_rx[side]++;
+ }
+
+ /* even if we have read data, we may not have any
+ * full lines loaded yet, so we may have to lie */
+ i = (s->lines_rx[side]-goff) * s->s_params.bytes_per_line;
+ if(i < 0){
+ i = 0;
+ }
+ s->bytes_rx[side] = i;
+ s->buff_rx[side] = i;
+
+ if(s->bytes_rx[side] == s->bytes_tot[side]){
+ s->eof_rx[side] = 1;
+ }
+
+ DBG(15, "copy_3091: si:%d imgrx:%d bufrx:%d li:%d eof:%d\n",
+ side, s->bytes_rx[side], s->buff_rx[side], s->lines_rx[side],
+ s->eof_rx[side]);
+
+ DBG (10, "copy_3091: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+copy_buffer(struct fujitsu *s, unsigned char * buf, int len, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int i, j;
+ int bwidth = s->s_params.bytes_per_line;
+ int pwidth = s->s_params.pixels_per_line;
+
+ DBG (10, "copy_buffer: start\n");
+
+ /* invert image if scanner needs it for this mode */
+ /* jpeg data does not use inverting */
+ if(s->s_params.format <= SANE_FRAME_RGB && s->reverse_by_mode[s->s_mode]){
+ for(i=0; i<len; i++){
+ buf[i] ^= 0xff;
+ }
+ }
+
+ /* scanners interlace colors in many different ways */
+ if(s->s_params.format == SANE_FRAME_RGB){
+
+ switch (s->color_interlace) {
+
+ /* scanner returns pixel data as bgrbgr... */
+ case COLOR_INTERLACE_BGR:
+ for(i=0; i<len; i+=bwidth){
+ for (j=0; j<pwidth; j++){
+ s->buffers[side][s->buff_rx[side]++] = buf[i+j*3+2];
+ s->buffers[side][s->buff_rx[side]++] = buf[i+j*3+1];
+ s->buffers[side][s->buff_rx[side]++] = buf[i+j*3];
+ }
+ }
+ break;
+
+ /* one line has the following format: rrr...rrrggg...gggbbb...bbb */
+ case COLOR_INTERLACE_RRGGBB:
+ for(i=0; i<len; i+=bwidth){
+ for (j=0; j<pwidth; j++){
+ s->buffers[side][s->buff_rx[side]++] = buf[i+j];
+ s->buffers[side][s->buff_rx[side]++] = buf[i+pwidth+j];
+ s->buffers[side][s->buff_rx[side]++] = buf[i+2*pwidth+j];
+ }
+ }
+ break;
+
+ default:
+ memcpy(s->buffers[side]+s->buff_rx[side],buf,len);
+ s->buff_rx[side] += len;
+ break;
+ }
+ }
+
+ /* jpeg/gray/ht/binary */
+ else{
+ memcpy(s->buffers[side]+s->buff_rx[side],buf,len);
+ s->buff_rx[side] += len;
+ }
+
+ s->bytes_rx[side] += len;
+ s->lines_rx[side] += len/s->s_params.bytes_per_line;
+
+ if(s->bytes_rx[side] == s->bytes_tot[side]){
+ s->eof_rx[side] = 1;
+ }
+
+ DBG (10, "copy_buffer: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_buffer(struct fujitsu *s, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int bytes = max_len;
+ int remain = s->buff_rx[side] - s->buff_tx[side];
+
+ DBG (10, "read_from_buffer: start\n");
+
+ /* figure out the max amount to transfer */
+ if(bytes > remain){
+ bytes = remain;
+ }
+
+ *len = bytes;
+
+ DBG(15, "read_from_buffer: si:%d re:%d ml:%d by:%d\n",
+ side, remain, max_len, bytes);
+
+ DBG(15, "read_from_buffer: img to:%d rx:%d tx:%d\n",
+ s->bytes_tot[side], s->bytes_rx[side], s->bytes_tx[side]);
+
+ DBG(15, "read_from_buffer: buf to:%d rx:%d tx:%d\n",
+ s->buff_tot[side], s->buff_rx[side], s->buff_tx[side]);
+
+ /*FIXME this needs to timeout eventually */
+ if(!bytes){
+ DBG(5,"read_from_buffer: nothing to do\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy(buf,s->buffers[side]+s->buff_tx[side],bytes);
+ s->buff_tx[side] += bytes;
+ s->bytes_tx[side] += bytes;
+
+ DBG (10, "read_from_buffer: finish\n");
+
+ return ret;
+}
+
+/* we have bytes of higher mode image data in s->buffers */
+/* user asked for lower mode image. downsample and copy to buf */
+
+static SANE_Status
+downsample_from_buffer(struct fujitsu *s, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len, int side)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "downsample_from_buffer: start %d %d %d %d\n", s->bytes_rx[side], s->bytes_tx[side], s->buff_rx[side], s->buff_tx[side]);
+
+ if(s->s_mode == MODE_COLOR && s->u_mode == MODE_GRAYSCALE){
+
+ while(*len < max_len && s->buff_rx[side] - s->buff_tx[side] >= 3){
+
+ int gray = 0;
+
+ switch (s->dropout_color) {
+ case COLOR_RED:
+ gray = *(s->buffers[side]+s->buff_tx[side]) * 3;
+ break;
+ case COLOR_GREEN:
+ gray = *(s->buffers[side]+s->buff_tx[side]+1) * 3;
+ break;
+ case COLOR_BLUE:
+ gray = *(s->buffers[side]+s->buff_tx[side]+2) * 3;
+ break;
+ default:
+ gray = *(s->buffers[side]+s->buff_tx[side])
+ + *(s->buffers[side]+s->buff_tx[side]+1)
+ + *(s->buffers[side]+s->buff_tx[side]+2);
+ break;
+ }
+
+ /* bookkeeping for input */
+ s->buff_tx[side] += 3;
+ s->bytes_tx[side] += 3;
+
+ /* add byte to output */
+ *(buf + *len) = gray/3;
+ (*len)++;
+ }
+ }
+
+ else if(s->s_mode == MODE_COLOR && s->u_mode == MODE_LINEART){
+
+ /* threshold of 0 is actually middle of range */
+ /*FIXME: add dynamic threshold? */
+ unsigned char thresh = (s->threshold ? s->threshold : 127);
+
+ while(*len < max_len && s->buff_rx[side] - s->buff_tx[side] >= 24){
+
+ int i;
+ unsigned char out = 0;
+
+ for(i=0; i<8; i++){
+
+ int gray = 0;
+
+ switch (s->dropout_color) {
+ case COLOR_RED:
+ gray = *(s->buffers[side]+s->buff_tx[side]) * 3;
+ break;
+ case COLOR_GREEN:
+ gray = *(s->buffers[side]+s->buff_tx[side]+1) * 3;
+ break;
+ case COLOR_BLUE:
+ gray = *(s->buffers[side]+s->buff_tx[side]+2) * 3;
+ break;
+ default:
+ gray = *(s->buffers[side]+s->buff_tx[side])
+ + *(s->buffers[side]+s->buff_tx[side]+1)
+ + *(s->buffers[side]+s->buff_tx[side]+2);
+ break;
+ }
+
+ /* black if input gray is lower than threshold */
+ if(gray/3 < thresh){
+ out |= (0x80 >> i);
+ }
+
+ /* bookkeeping for input */
+ s->buff_tx[side] += 3;
+ s->bytes_tx[side] += 3;
+ }
+
+ /* add byte to output */
+ *(buf + *len) = out;
+ (*len)++;
+ }
+ }
+
+ else{
+ DBG (5, "downsample_from_buffer: invalid mode combination\n");
+ ret = SANE_STATUS_INVAL;
+ }
+
+ DBG (10, "downsample_from_buffer: finish %d %d %d %d\n", s->bytes_rx[side], s->bytes_tx[side], s->buff_rx[side], s->buff_tx[side]);
+
+ return ret;
+}
+
+
+/*
+ * @@ Section 5 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * It has been said on the mailing list that sane_cancel is a bit of a
+ * misnomer because it is routinely called to signal the end of a
+ * batch - quoting David Mosberger-Tang:
+ *
+ * > In other words, the idea is to have sane_start() be called, and
+ * > collect as many images as the frontend wants (which could in turn
+ * > consist of multiple frames each as indicated by frame-type) and
+ * > when the frontend is done, it should call sane_cancel().
+ * > Sometimes it's better to think of sane_cancel() as "sane_stop()"
+ * > but that name would have had some misleading connotations as
+ * > well, that's why we stuck with "cancel".
+ *
+ * The current consensus regarding duplex and ADF scans seems to be
+ * the following call sequence: sane_start; sane_read (repeat until
+ * EOF); sane_start; sane_read... and then call sane_cancel if the
+ * batch is at an end. I.e. do not call sane_cancel during the run but
+ * as soon as you get a SANE_STATUS_NO_DOCS.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ struct fujitsu * s = (struct fujitsu *) handle;
+
+ DBG (10, "sane_cancel: start\n");
+ s->cancelled = 1;
+
+ /* if there is no other running function to check, we do it */
+ if(!s->reading)
+ check_for_cancel(s);
+
+ DBG (10, "sane_cancel: finish\n");
+}
+
+/*
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ struct fujitsu * s = (struct fujitsu *) handle;
+
+ DBG (10, "sane_close: start\n");
+ /*clears any held scans*/
+ mode_select_buff(s);
+ disconnect_fd(s);
+ DBG (10, "sane_close: finish\n");
+}
+
+static SANE_Status
+disconnect_fd (struct fujitsu *s)
+{
+ DBG (10, "disconnect_fd: start\n");
+
+ if(s->fd > -1){
+ if (s->connection == CONNECTION_USB) {
+ DBG (15, "disconnecting usb device\n");
+ sanei_usb_close (s->fd);
+ }
+ else if (s->connection == CONNECTION_SCSI) {
+ DBG (15, "disconnecting scsi device\n");
+ sanei_scsi_close (s->fd);
+ }
+ s->fd = -1;
+ }
+
+ DBG (10, "disconnect_fd: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct fujitsu *dev, *next;
+
+ DBG (10, "sane_exit: start\n");
+
+ for (dev = fujitsu_devList; dev; dev = next) {
+ disconnect_fd(dev);
+ next = dev->next;
+ free (dev);
+ }
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ fujitsu_devList = NULL;
+ sane_devArray = NULL;
+
+ DBG (10, "sane_exit: finish\n");
+}
+
+/*
+ * @@ Section 6 - misc helper functions
+ */
+/*
+ * Called by the SANE SCSI core and our usb code on device errors
+ * parses the request sense return data buffer,
+ * decides the best SANE_Status for the problem, produces debug msgs,
+ * and copies the sense buffer into the scanner struct
+ */
+static SANE_Status
+sense_handler (int fd, unsigned char * sensed_data, void *arg)
+{
+ struct fujitsu *s = arg;
+ unsigned int sense = get_RS_sense_key (sensed_data);
+ unsigned int asc = get_RS_ASC (sensed_data);
+ unsigned int ascq = get_RS_ASCQ (sensed_data);
+
+ DBG (5, "sense_handler: start\n");
+
+ /* kill compiler warning */
+ fd = fd;
+
+ /* copy the rs return data into the scanner struct
+ so that the caller can use it if he wants */
+ s->rs_info = get_RS_information (sensed_data);
+ s->rs_eom = get_RS_EOM (sensed_data);
+ s->rs_ili = get_RS_ILI (sensed_data);
+
+ DBG (5, "Sense=%#02x, ASC=%#02x, ASCQ=%#02x, EOM=%d, ILI=%d, info=%#08lx\n", sense, asc, ascq, s->rs_eom, s->rs_ili, (unsigned long)s->rs_info);
+
+ switch (sense) {
+ case 0x0:
+ if (0x80 == asc) {
+ DBG (5, "No sense: hardware status bits?\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (0x00 != asc) {
+ DBG (5, "No sense: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x00 != ascq) {
+ DBG (5, "No sense: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* ready, but short read */
+ if (s->rs_ili) {
+ DBG (5, "No sense: ILI remainder:%lu\n",(unsigned long)s->rs_info);
+ }
+ /* ready, but end of paper */
+ if (s->rs_eom) {
+ DBG (5, "No sense: EOM\n");
+ return SANE_STATUS_EOF;
+ }
+ DBG (5, "No sense: ready\n");
+ return SANE_STATUS_GOOD;
+
+ case 0x2:
+ if (0x00 != asc) {
+ DBG (5, "Not ready: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x00 != ascq) {
+ DBG (5, "Not ready: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Not ready: busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ break;
+
+ case 0x3:
+ if (0x80 != asc) {
+ DBG (5, "Medium error: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x01 == ascq) {
+ DBG (5, "Medium error: paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (0x02 == ascq) {
+ DBG (5, "Medium error: cover open\n");
+ return SANE_STATUS_COVER_OPEN;
+ }
+ if (0x03 == ascq) {
+ DBG (5, "Medium error: hopper empty\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ if (0x04 == ascq) {
+ DBG (5, "Medium error: unusual paper\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (0x07 == ascq) {
+ DBG (5, "Medium error: double feed\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (0x10 == ascq) {
+ DBG (5, "Medium error: no ink cartridge\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x13 == ascq) {
+ DBG (5, "Medium error: temporary no data\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x14 == ascq) {
+ DBG (5, "Medium error: endorser error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Medium error: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0x4:
+ if (0x80 != asc && 0x44 != asc && 0x47 != asc) {
+ DBG (5, "Hardware error: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x44 == asc) && (0x00 == ascq)) {
+ DBG (5, "Hardware error: EEPROM error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x01 == ascq)) {
+ DBG (5, "Hardware error: FB motor fuse\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x02 == ascq)) {
+ DBG (5, "Hardware error: heater fuse\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x04 == ascq)) {
+ DBG (5, "Hardware error: ADF motor fuse\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x05 == ascq)) {
+ DBG (5, "Hardware error: mechanical error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x06 == ascq)) {
+ DBG (5, "Hardware error: optical error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x07 == ascq)) {
+ DBG (5, "Hardware error: Fan error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x08 == ascq)) {
+ DBG (5, "Hardware error: IPC option error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x10 == ascq)) {
+ DBG (5, "Hardware error: endorser error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Hardware error: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0x5:
+ if ((0x00 == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: paper edge detected too soon\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((0x1a == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: Parameter list error\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((0x20 == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: invalid command\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((0x24 == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: invalid CDB field\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((0x25 == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: unsupported logical unit\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ if ((0x26 == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: invalid field in parm list\n");
+ if (get_RS_additional_length(sensed_data) >= 0x0a) {
+ DBG (5, "Offending byte is %#02x\n", get_RS_offending_byte(sensed_data));
+
+ /* move this to set_window() ? */
+ if (get_RS_offending_byte(sensed_data) >= 8) {
+ DBG (5, "Window desc block? byte %#02x\n",get_RS_offending_byte(sensed_data)-8);
+ }
+ }
+ return SANE_STATUS_INVAL;
+ }
+ if ((0x2C == asc) && (0x00 == ascq)) {
+ DBG (5, "Illegal request: command sequence error\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((0x2C == asc) && (0x02 == ascq)) {
+ DBG (5, "Illegal request: wrong window combination \n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "Illegal request: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0x6:
+ if ((0x00 == asc) && (0x00 == ascq)) {
+ DBG (5, "Unit attention: device reset\n");
+ return SANE_STATUS_GOOD;
+ }
+ if ((0x80 == asc) && (0x01 == ascq)) {
+ DBG (5, "Unit attention: power saving\n");
+ return SANE_STATUS_GOOD;
+ }
+ DBG (5, "Unit attention: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0xb:
+ if ((0x43 == asc) && (0x00 == ascq)) {
+ DBG (5, "Aborted command: message error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x45 == asc) && (0x00 == ascq)) {
+ DBG (5, "Aborted command: select failure\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x47 == asc) && (0x00 == ascq)) {
+ DBG (5, "Aborted command: SCSI parity error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x48 == asc) && (0x00 == ascq)) {
+ DBG (5, "Aborted command: initiator error message\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x4e == asc) && (0x00 == ascq)) {
+ DBG (5, "Aborted command: overlapped commands\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x01 == ascq)) {
+ DBG (5, "Aborted command: image transfer error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((0x80 == asc) && (0x03 == ascq)) {
+ DBG (5, "Aborted command: JPEG overflow error\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (5, "Aborted command: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ default:
+ DBG (5, "Unknown Sense Code\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "sense_handler: should never happen!\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/*
+ * take a bunch of pointers, send commands to scanner
+ */
+static SANE_Status
+do_cmd(struct fujitsu *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+
+ /* unset the request sense vars first */
+ s->rs_info = 0;
+ s->rs_ili = 0;
+ s->rs_eom = 0;
+
+ if (s->connection == CONNECTION_SCSI) {
+ return do_scsi_cmd(s, runRS, shortTime,
+ cmdBuff, cmdLen,
+ outBuff, outLen,
+ inBuff, inLen
+ );
+ }
+ if (s->connection == CONNECTION_USB) {
+ return do_usb_cmd(s, runRS, shortTime,
+ cmdBuff, cmdLen,
+ outBuff, outLen,
+ inBuff, inLen
+ );
+ }
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+do_scsi_cmd(struct fujitsu *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ int ret;
+ size_t actLen = 0;
+
+ /*shut up compiler*/
+ runRS=runRS;
+ shortTime=shortTime;
+
+ DBG(10, "do_scsi_cmd: start\n");
+
+ DBG(25, "cmd: writing %d bytes\n", (int)cmdLen);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+
+ if(outBuff && outLen){
+ DBG(25, "out: writing %d bytes\n", (int)outLen);
+ hexdump(30, "out: >>", outBuff, outLen);
+ }
+ if (inBuff && inLen){
+ DBG(25, "in: reading %d bytes\n", (int)*inLen);
+ memset(inBuff,0,*inLen);
+ actLen = *inLen;
+ }
+
+ ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen);
+
+ if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){
+ DBG(5,"do_scsi_cmd: return '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ /* FIXME: should we look at s->rs_info here? */
+ if (inBuff && inLen){
+ hexdump(30, "in: <<", inBuff, *inLen);
+ DBG(25, "in: read %d bytes\n", (int)*inLen);
+ }
+
+ DBG(10, "do_scsi_cmd: finish\n");
+
+ return ret;
+}
+
+SANE_Status
+do_usb_cmd(struct fujitsu *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ /*sanei_usb overwrites the transfer size,
+ * so make some local copies */
+ size_t usb_cmdLen = USB_COMMAND_LEN;
+ size_t usb_outLen = outLen;
+ size_t usb_statLen = USB_STATUS_LEN;
+ size_t askLen = 0;
+
+ /*copy the callers buffs into larger, padded ones*/
+ unsigned char usb_cmdBuff[USB_COMMAND_LEN];
+ unsigned char usb_statBuff[USB_STATUS_LEN];
+
+ int cmdTime = USB_COMMAND_TIME;
+ int outTime = USB_DATA_TIME;
+ int inTime = USB_DATA_TIME;
+ int statTime = USB_STATUS_TIME;
+
+ int ret = 0;
+ int ret2 = 0;
+
+ DBG (10, "do_usb_cmd: start\n");
+
+ if(shortTime){
+ cmdTime = USB_COMMAND_TIME/60;
+ outTime = USB_DATA_TIME/60;
+ inTime = USB_DATA_TIME/60;
+ statTime = USB_STATUS_TIME/60;
+ }
+
+ /* build a USB packet around the SCSI command */
+ memset(&usb_cmdBuff,0,USB_COMMAND_LEN);
+ usb_cmdBuff[0] = USB_COMMAND_CODE;
+ memcpy(&usb_cmdBuff[USB_COMMAND_OFFSET],cmdBuff,cmdLen);
+
+ /* change timeout */
+ sanei_usb_set_timeout(cmdTime);
+
+ /* write the command out */
+ DBG(25, "cmd: writing %d bytes, timeout %d\n", USB_COMMAND_LEN, cmdTime);
+ hexdump(30, "cmd: >>", usb_cmdBuff, USB_COMMAND_LEN);
+ ret = sanei_usb_write_bulk(s->fd, usb_cmdBuff, &usb_cmdLen);
+ DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)usb_cmdLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"cmd: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(usb_cmdLen != USB_COMMAND_LEN){
+ DBG(5,"cmd: wrong size %d/%d\n", USB_COMMAND_LEN, (int)usb_cmdLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* this command has a write component, and a place to get it */
+ if(outBuff && outLen && outTime){
+
+ /* change timeout */
+ sanei_usb_set_timeout(outTime);
+
+ DBG(25, "out: writing %d bytes, timeout %d\n", (int)outLen, outTime);
+ hexdump(30, "out: >>", outBuff, outLen);
+ ret = sanei_usb_write_bulk(s->fd, outBuff, &usb_outLen);
+ DBG(25, "out: wrote %d bytes, retVal %d\n", (int)usb_outLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"out: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"out: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(usb_outLen != outLen){
+ DBG(5,"out: wrong size %d/%d\n", (int)outLen, (int)usb_outLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* this command has a read component, and a place to put it */
+ if(inBuff && inLen && inTime){
+
+ askLen = *inLen;
+ memset(inBuff,0,askLen);
+
+ /* change timeout */
+ sanei_usb_set_timeout(inTime);
+
+ DBG(25, "in: reading %lu bytes, timeout %d\n",
+ (unsigned long)askLen, inTime);
+
+ ret = sanei_usb_read_bulk(s->fd, inBuff, inLen);
+ DBG(25, "in: retVal %d\n", ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"in: got EOF, continuing\n");
+ ret = SANE_STATUS_GOOD;
+ }
+
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"in: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ DBG(25, "in: read %lu bytes\n", (unsigned long)*inLen);
+ if(*inLen){
+ hexdump(31, "in: <<", inBuff, *inLen);
+ }
+
+ if(*inLen && *inLen != askLen){
+ ret = SANE_STATUS_EOF;
+ DBG(5,"in: short read, %lu/%lu\n",
+ (unsigned long)*inLen,(unsigned long)askLen);
+ }
+ }
+
+ /*gather the scsi status byte. use ret2 instead of ret for status*/
+
+ memset(&usb_statBuff,0,USB_STATUS_LEN);
+
+ /* change timeout */
+ sanei_usb_set_timeout(statTime);
+
+ DBG(25, "stat: reading %d bytes, timeout %d\n", USB_STATUS_LEN, statTime);
+ ret2 = sanei_usb_read_bulk(s->fd, usb_statBuff, &usb_statLen);
+ hexdump(30, "stat: <<", usb_statBuff, usb_statLen);
+ DBG(25, "stat: read %d bytes, retVal %d\n", (int)usb_statLen, ret2);
+
+ if(ret2 == SANE_STATUS_EOF){
+ DBG(5,"stat: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret2 != SANE_STATUS_GOOD){
+ DBG(5,"stat: return error '%s'\n",sane_strstatus(ret2));
+ return ret2;
+ }
+ if(usb_statLen != USB_STATUS_LEN){
+ DBG(5,"stat: wrong size %d/%d\n", USB_STATUS_LEN, (int)usb_statLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* busy status */
+ if(usb_statBuff[USB_STATUS_OFFSET] == 8){
+ DBG(25,"stat: busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* if there is a non-busy status >0, try to figure out why */
+ if(usb_statBuff[USB_STATUS_OFFSET] > 0){
+ DBG(25,"stat: value %d\n", usb_statBuff[USB_STATUS_OFFSET]);
+
+ /* caller is interested in having RS run on errors */
+ if(runRS){
+ unsigned char rs_cmd[REQUEST_SENSE_len];
+ size_t rs_cmdLen = REQUEST_SENSE_len;
+
+ unsigned char rs_in[RS_return_size];
+ size_t rs_inLen = RS_return_size;
+
+ memset(rs_cmd,0,rs_cmdLen);
+ set_SCSI_opcode(rs_cmd, REQUEST_SENSE_code);
+ set_RS_return_size(rs_cmd, rs_inLen);
+
+ DBG(25,"rs sub call >>\n");
+ ret2 = do_cmd(
+ s,0,0,
+ rs_cmd, rs_cmdLen,
+ NULL,0,
+ rs_in, &rs_inLen
+ );
+ DBG(25,"rs sub call <<\n");
+
+ if(ret2 == SANE_STATUS_EOF){
+ DBG(5,"rs: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret2 != SANE_STATUS_GOOD){
+ DBG(5,"rs: return error '%s'\n",sane_strstatus(ret2));
+ return ret2;
+ }
+
+ /* parse the rs data */
+ ret2 = sense_handler( 0, rs_in, (void *)s );
+
+ /* this was a short read, but the usb layer did not know */
+ if(s->rs_ili && inBuff && inLen && inTime){
+ *inLen = askLen - s->rs_info;
+ DBG(5,"do_usb_cmd: short read via rs, %lu/%lu\n",
+ (unsigned long)*inLen,(unsigned long)askLen);
+ }
+ return ret2;
+ }
+ else{
+ DBG(5,"do_usb_cmd: Not calling rs!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ DBG (10, "do_usb_cmd: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+wait_scanner(struct fujitsu *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[TEST_UNIT_READY_len];
+ size_t cmdLen = TEST_UNIT_READY_len;
+
+ DBG (10, "wait_scanner: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,TEST_UNIT_READY_code);
+
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret));
+ }
+
+ DBG (10, "wait_scanner: finish\n");
+
+ return ret;
+}
+
+/* certain options require the entire image to
+ * be collected from the scanner before we can
+ * tell the user the size of the image. */
+static int
+must_fully_buffer(struct fujitsu *s)
+{
+ if(s->hwdeskewcrop){
+ return 1;
+ }
+
+ if(
+ (s->swdeskew || s->swdespeck || s->swcrop || s->swskip)
+#ifdef SANE_FRAME_JPEG
+ && s->s_params.format != SANE_FRAME_JPEG
+#endif
+ ){
+ return 1;
+ }
+
+ return 0;
+}
+
+/* certain scanners require the mode of the
+ * image to be changed in software. */
+static int
+must_downsample(struct fujitsu *s)
+{
+ if(s->s_mode != s->u_mode
+#ifdef SANE_FRAME_JPEG
+ && s->compress != COMP_JPEG
+#endif
+ ){
+ return 1;
+ }
+
+ return 0;
+}
+
+/* s->page_width stores the user setting
+ * for the paper width in adf. sometimes,
+ * we need a value that differs from this
+ * due to using FB or overscan.
+ */
+static int
+get_page_width(struct fujitsu *s)
+{
+ int width = s->page_width + 2 * (s->os_x_basic*1200/s->basic_x_res);
+
+ /* scanner max for fb */
+ if(s->source == SOURCE_FLATBED){
+ return s->max_x_fb;
+ }
+
+ /* current paper size for adf not overscan */
+ if(s->overscan != MSEL_ON){
+ return s->page_width;
+ }
+
+ /* cant overscan larger than scanner max */
+ if(width > s->max_x){
+ return s->max_x;
+ }
+
+ /* overscan adds a margin to both sides */
+ return width;
+}
+
+/* s->page_height stores the user setting
+ * for the paper height in adf. sometimes,
+ * we need a value that differs from this
+ * due to using FB or overscan.
+ */
+static int
+get_page_height(struct fujitsu *s)
+{
+ int height = s->page_height + 2 * (s->os_y_basic*1200/s->basic_y_res);
+
+ /* scanner max for fb */
+ if(s->source == SOURCE_FLATBED){
+ return s->max_y_fb;
+ }
+
+ /* current paper size for adf not overscan */
+ if(s->overscan != MSEL_ON){
+ return s->page_height;
+ }
+
+ /* cant overscan larger than scanner max */
+ if(height > s->max_y){
+ return s->max_y;
+ }
+
+ /* overscan adds a margin to both sides */
+ return height;
+}
+
+
+/**
+ * Convenience method to determine longest string size in a list.
+ */
+static size_t
+maxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+/*
+ * Prints a hex dump of the given buffer onto the debug output stream.
+ */
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[70]; /* 'xxx: xx xx ... xx xx abc */
+ char *hex = line+4;
+ char *bin = line+53;
+
+ if(DBG_LEVEL < level)
+ return;
+
+ DBG (level, "%s\n", comment);
+
+ for (i = 0; i < l; i++, p++) {
+
+ /* at start of line */
+ if ((i % 16) == 0) {
+
+ /* not at start of first line, print current, reset */
+ if (i) {
+ DBG (level, "%s\n", line);
+ }
+
+ memset(line,0x20,69);
+ line[69] = 0;
+ hex = line + 4;
+ bin = line + 53;
+
+ sprintf (line, "%3.3x:", i);
+ }
+
+ /* the hex section */
+ sprintf (hex, " %2.2x", *p);
+ hex += 3;
+ *hex = ' ';
+
+ /* the char section */
+ if(*p >= 0x20 && *p <= 0x7e){
+ *bin=*p;
+ }
+ else{
+ *bin='.';
+ }
+ bin++;
+ }
+
+ /* print last (partial) line */
+ DBG (level, "%s\n", line);
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (15, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG (10, "sane_get_select_fd\n");
+ DBG (15, "%p %d\n", h, *fdp);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * @@ Section 7 - Image processing functions
+ */
+
+/* Look in image for likely upper and left paper edges, then rotate
+ * image so that upper left corner of paper is upper left of image.
+ * FIXME: should we do this before we binarize instead of after? */
+static SANE_Status
+buffer_deskew(struct fujitsu *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int bg_color = 0xd6;
+
+ DBG (10, "buffer_deskew: start\n");
+
+ /*only find skew on first image from a page, or if first image had error */
+ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK || s->deskew_stat){
+
+ s->deskew_stat = sanei_magic_findSkew(
+ &s->s_params,s->buffers[side],s->resolution_x,s->resolution_y,
+ &s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope);
+
+ if(s->deskew_stat){
+ DBG (5, "buffer_deskew: bad findSkew, bailing\n");
+ goto cleanup;
+ }
+ }
+ /* backside images can use a 'flipped' version of frontside data */
+ else{
+ s->deskew_slope *= -1;
+ s->deskew_vals[0] = s->s_params.pixels_per_line - s->deskew_vals[0];
+ }
+
+ /* tweak the bg color based on scanner settings */
+ if(s->s_mode == MODE_HALFTONE || s->s_mode == MODE_LINEART){
+ if(s->bg_color == COLOR_BLACK || s->hwdeskewcrop || s->overscan)
+ bg_color = 0xff;
+ else
+ bg_color = 0;
+ }
+ else if(s->bg_color == COLOR_BLACK || s->hwdeskewcrop || s->overscan)
+ bg_color = 0;
+
+ ret = sanei_magic_rotate(&s->s_params,s->buffers[side],
+ s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color);
+
+ if(ret){
+ DBG(5,"buffer_deskew: rotate error: %d",ret);
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ cleanup:
+ DBG (10, "buffer_deskew: finish\n");
+ return ret;
+}
+
+/* Look in image for likely left/right/bottom paper edges, then crop image.
+ * Does not attempt to rotate the image, that should be done first.
+ * FIXME: should we do this before we binarize instead of after? */
+static SANE_Status
+buffer_crop(struct fujitsu *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "buffer_crop: start\n");
+
+ /*only find edges on first image from a page, or if first image had error */
+ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK || s->crop_stat){
+
+ s->crop_stat = sanei_magic_findEdges(
+ &s->s_params,s->buffers[side],s->resolution_x,s->resolution_y,
+ &s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]);
+
+ if(s->crop_stat){
+ DBG (5, "buffer_crop: bad edges, bailing\n");
+ goto cleanup;
+ }
+
+ DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n",
+ s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
+
+ /* we dont listen to the 'top' value, since fujitsu does not pad the top */
+ s->crop_vals[0] = 0;
+
+ /* if we will later binarize this image, make sure the width
+ * is a multiple of 8 pixels, by adjusting the right side */
+ if ( must_downsample(s) && s->u_mode < MODE_GRAYSCALE ){
+ s->crop_vals[3] -= (s->crop_vals[3]-s->crop_vals[2]) % 8;
+ }
+ }
+ /* backside images can use a 'flipped' version of frontside data */
+ else{
+ int left = s->crop_vals[2];
+ int right = s->crop_vals[3];
+
+ s->crop_vals[2] = s->s_params.pixels_per_line - right;
+ s->crop_vals[3] = s->s_params.pixels_per_line - left;
+ }
+
+ /* now crop the image */
+ ret = sanei_magic_crop(&s->s_params,s->buffers[side],
+ s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
+
+ if(ret){
+ DBG (5, "buffer_crop: bad crop, bailing\n");
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ /* need to update user with new size */
+ update_u_params(s);
+
+ /* update image size counter to new, smaller size */
+ s->bytes_rx[side] = s->s_params.lines * s->s_params.bytes_per_line;
+ s->buff_rx[side] = s->bytes_rx[side];
+
+ cleanup:
+ DBG (10, "buffer_crop: finish\n");
+ return ret;
+}
+
+/* Look in image for disconnected 'spots' of the requested size.
+ * Replace the spots with the average color of the surrounding pixels.
+ * FIXME: should we do this before we binarize instead of after? */
+static SANE_Status
+buffer_despeck(struct fujitsu *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ DBG (10, "buffer_despeck: start\n");
+
+ ret = sanei_magic_despeck(&s->s_params,s->buffers[side],s->swdespeck);
+ if(ret){
+ DBG (5, "buffer_despeck: bad despeck, bailing\n");
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ cleanup:
+ DBG (10, "buffer_despeck: finish\n");
+ return ret;
+}
+
+/* Look if image has too few dark pixels.*/
+static int
+buffer_isblank(struct fujitsu *s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int status = 0;
+
+ DBG (10, "buffer_isblank: start\n");
+
+ ret = sanei_magic_isBlank2(&s->s_params, s->buffers[side],
+ s->resolution_x, s->resolution_y, s->swskip);
+
+ if(ret == SANE_STATUS_NO_DOCS){
+ DBG (5, "buffer_isblank: blank!\n");
+ status = 1;
+ }
+ else if(ret){
+ DBG (5, "buffer_isblank: error %d\n",ret);
+ }
+
+ DBG (10, "buffer_isblank: finished\n");
+ return status;
+}
+
diff --git a/backend/fujitsu.conf.in b/backend/fujitsu.conf.in
new file mode 100644
index 0000000..8034f25
--- /dev/null
+++ b/backend/fujitsu.conf.in
@@ -0,0 +1,149 @@
+# NOTE: any 'option' lines only apply to
+# scanners discovered later in this file
+
+# to set data buffer size, in bytes
+# the value ranges from 4096 - infinity
+# but old scanners may have scanning problems
+# with a value larger than 65536 (the default)
+# NOTE: this option is set to a larger value
+# later in this file, for more recent scanners
+option buffer-size 65536
+
+# To search for all FUJITSU scsi devices
+scsi FUJITSU
+
+# To use a specific scsi device
+#scsi /dev/sg1
+
+# For Fujitsu scanners connected via USB on a known device (kernel driver):
+#usb /dev/usb/scanner0
+
+# For Fujitsu scanners connected via USB using vendor and device ids (libusb):
+#usb VENDORID PRODUCTID
+
+# NOTE: if you have to add your device here- please send the id and model
+# to the author via email, so it can be included in next version. kitno455 at
+# gmail dot com - with Fujitsu in the subject line
+
+#fi-4x20C
+usb 0x04c5 0x1041
+usb 0x04c5 0x1042
+
+#fi-4530C
+usb 0x04c5 0x1078
+
+#fi-5750C
+usb 0x04c5 0x1095
+
+#fi-5110eox/2
+usb 0x04c5 0x1096
+
+#fi-5110C
+usb 0x04c5 0x1097
+
+#fi-5650C
+usb 0x04c5 0x10ad
+
+#fi-4x20C2
+usb 0x04c5 0x10ae
+usb 0x04c5 0x10af
+
+#fi-4340C
+usb 0x04c5 0x10cf
+
+#fi-5x20C
+usb 0x04c5 0x10e0
+usb 0x04c5 0x10e1
+
+#fi-5530C
+usb 0x04c5 0x10e2
+
+#fi-5110eox3
+usb 0x04c5 0x10e6
+
+#fi-5900C
+usb 0x04c5 0x10e7
+
+#fi-5110EOXM
+usb 0x04c5 0x10f2
+
+#ScanSnap S500
+usb 0x04c5 0x10fe
+
+#ScanSnap S500M
+usb 0x04c5 0x1135
+
+#fi-5530C2
+usb 0x04c5 0x114a
+
+# More recent scanners need a larger buffer for maximum speed
+option buffer-size 262144
+
+#fi-6140
+usb 0x04c5 0x114d
+
+#fi-6240
+usb 0x04c5 0x114e
+
+#fi-6130
+usb 0x04c5 0x114f
+
+#fi-6230
+usb 0x04c5 0x1150
+
+#ScanSnap S510
+usb 0x04c5 0x1155
+
+#ScanSnap S510M
+usb 0x04c5 0x116f
+
+#fi-6770
+usb 0x04c5 0x1174
+
+#fi-6770A
+usb 0x04c5 0x1175
+
+#fi-6670
+usb 0x04c5 0x1176
+
+#fi-6670A
+usb 0x04c5 0x1177
+
+#fi-6750S
+usb 0x04c5 0x1178
+
+#fi-6800
+usb 0x04c5 0x119d
+
+#fi-6800-CGA
+usb 0x04c5 0x119e
+
+#fi-6900
+usb 0x04c5 0x119f
+
+#fi-6900-CGA
+usb 0x04c5 0x11a0
+
+#S1500 & S1500M
+usb 0x04c5 0x11a2
+
+#fi-6140Z
+usb 0x04c5 0x11f1
+
+#fi-6240Z
+usb 0x04c5 0x11f2
+
+#fi-6130Z
+usb 0x04c5 0x11f3
+
+#fi-6230Z
+usb 0x04c5 0x11f4
+
+#fi-6110
+usb 0x04c5 0x11fc
+
+#fi-5950
+usb 0x04c5 0x1213
+
+#ScanSnap iX500
+usb 0x04c5 0x132b
diff --git a/backend/fujitsu.h b/backend/fujitsu.h
new file mode 100644
index 0000000..4c6804b
--- /dev/null
+++ b/backend/fujitsu.h
@@ -0,0 +1,837 @@
+#ifndef FUJITSU_H
+#define FUJITSU_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ * Please see opening comment in fujitsu.c
+ */
+
+/* -------------------------------------------------------------------------
+ * This option list has to contain all options for all scanners supported by
+ * this driver. If a certain scanner cannot handle a certain option, there's
+ * still the possibility to say so, later.
+ */
+enum fujitsu_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_STANDARD_GROUP,
+ OPT_SOURCE, /*fb/adf/front/back/duplex*/
+ OPT_MODE, /*mono/gray/color*/
+ OPT_RES, /*a range or a list*/
+
+ OPT_GEOMETRY_GROUP,
+ OPT_PAGE_WIDTH,
+ OPT_PAGE_HEIGHT,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_GAMMA,
+ OPT_THRESHOLD,
+
+ /*IPC*/
+ OPT_RIF,
+ OPT_HT_TYPE,
+ OPT_HT_PATTERN,
+ OPT_OUTLINE,
+ OPT_EMPHASIS,
+ OPT_SEPARATION,
+ OPT_MIRRORING,
+ OPT_WL_FOLLOW,
+ OPT_IPC_MODE,
+
+ /*IPC/DTC*/
+ OPT_BP_FILTER,
+ OPT_SMOOTHING,
+ OPT_GAMMA_CURVE,
+ OPT_THRESHOLD_CURVE,
+ OPT_THRESHOLD_WHITE,
+ OPT_NOISE_REMOVAL,
+ OPT_MATRIX_5,
+ OPT_MATRIX_4,
+ OPT_MATRIX_3,
+ OPT_MATRIX_2,
+
+ /*IPC/SDTC*/
+ OPT_VARIANCE,
+
+ OPT_ADVANCED_GROUP,
+ OPT_AWD,
+ OPT_ALD,
+ OPT_COMPRESS,
+ OPT_COMPRESS_ARG,
+ OPT_DF_ACTION,
+ OPT_DF_SKEW,
+ OPT_DF_THICKNESS,
+ OPT_DF_LENGTH,
+ OPT_DF_DIFF,
+ OPT_DF_RECOVERY,
+ OPT_PAPER_PROTECT,
+ OPT_STAPLE_DETECT,
+ OPT_BG_COLOR,
+ OPT_DROPOUT_COLOR,
+ OPT_BUFF_MODE,
+ OPT_PREPICK,
+ OPT_OVERSCAN,
+ OPT_SLEEP_TIME,
+ OPT_DUPLEX_OFFSET,
+ OPT_GREEN_OFFSET,
+ OPT_BLUE_OFFSET,
+ OPT_LOW_MEM,
+ OPT_SIDE,
+ OPT_HWDESKEWCROP,
+ OPT_SWDESKEW,
+ OPT_SWDESPECK,
+ OPT_SWCROP,
+ OPT_SWSKIP,
+
+ OPT_ENDORSER_GROUP,
+ OPT_ENDORSER,
+ OPT_ENDORSER_BITS,
+ OPT_ENDORSER_VAL,
+ OPT_ENDORSER_STEP,
+ OPT_ENDORSER_Y,
+ OPT_ENDORSER_FONT,
+ OPT_ENDORSER_DIR,
+ OPT_ENDORSER_SIDE,
+ OPT_ENDORSER_STRING,
+
+ OPT_SENSOR_GROUP,
+ OPT_TOP,
+ OPT_A3,
+ OPT_B4,
+ OPT_A4,
+ OPT_B5,
+ OPT_HOPPER,
+ OPT_OMR,
+ OPT_ADF_OPEN,
+ OPT_SLEEP,
+ OPT_SEND_SW,
+ OPT_MANUAL_FEED,
+ OPT_SCAN_SW,
+ OPT_FUNCTION,
+ OPT_INK_EMPTY,
+ OPT_DOUBLE_FEED,
+ OPT_ERROR_CODE,
+ OPT_SKEW_ANGLE,
+ OPT_INK_REMAIN,
+ OPT_DUPLEX_SW,
+ OPT_DENSITY_SW,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+struct fujitsu
+{
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during init of scanner. */
+ struct fujitsu *next;
+ char device_name[1024]; /* The name of the device from sanei */
+ int missing; /* used to mark unplugged scanners */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during reading of config file. */
+ int buffer_size;
+ int connection; /* hardware interface type */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during inquiry probing of the scanner. */
+ /* members in order found in scsi data... */
+ char vendor_name[9]; /* raw data as returned by SCSI inquiry. */
+ char model_name[17]; /* raw data as returned by SCSI inquiry. */
+ char version_name[5]; /* raw data as returned by SCSI inquiry. */
+
+ int color_raster_offset; /* offset between r and b scan line and */
+ /* between b and g scan line (0 or 4) */
+
+ int duplex_raster_offset; /* offset between front and rear page when */
+ /* when scanning 3091 style duplex */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during std VPD probing of the scanner. */
+ /* members in order found in scsi data... */
+ int basic_x_res;
+ int basic_y_res;
+ int step_x_res[6]; /*one for each mode*/
+ int step_y_res[6]; /*one for each mode*/
+ int max_x_res;
+ int max_y_res;
+ int min_x_res;
+ int min_y_res;
+
+ int std_res[16]; /*some scanners only support a few resolutions*/
+
+ /* max scan size in pixels comes from scanner in basic res units */
+ int max_x_basic;
+ int max_y_basic;
+
+ int can_overflow;
+ int can_mode[6]; /* mode specific */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during vndr VPD probing of the scanner */
+ /* members in order found in scsi data... */
+ int has_adf;
+ int has_flatbed;
+ int has_transparency;
+ int has_duplex;
+ int has_endorser_b;
+ int has_barcode;
+ int has_operator_panel;
+ int has_endorser_f;
+
+ int adbits;
+ int buffer_bytes;
+
+ /*supported scsi commands*/
+ int has_cmd_msen10;
+ int has_cmd_msel10;
+
+ int has_cmd_lsen;
+ int has_cmd_lsel;
+ int has_cmd_change;
+ int has_cmd_rbuff;
+ int has_cmd_wbuff;
+ int has_cmd_cav;
+ int has_cmd_comp;
+ int has_cmd_gdbs;
+
+ int has_cmd_op;
+ int has_cmd_send;
+ int has_cmd_read;
+ int has_cmd_gwin;
+ int has_cmd_swin;
+ int has_cmd_sdiag;
+ int has_cmd_rdiag;
+ int has_cmd_scan;
+
+ int has_cmd_msen6;
+ int has_cmd_copy;
+ int has_cmd_rel;
+ int has_cmd_runit;
+ int has_cmd_msel6;
+ int has_cmd_inq;
+ int has_cmd_rs;
+ int has_cmd_tur;
+
+ /*FIXME: there are more vendor cmds? */
+ int has_cmd_subwindow;
+ int has_cmd_endorser;
+ int has_cmd_hw_status;
+ int has_cmd_hw_status_2;
+ int has_cmd_hw_status_3;
+ int has_cmd_scanner_ctl;
+ int has_cmd_device_restart;
+
+ /*FIXME: do we need the vendor window param list? */
+
+ int brightness_steps;
+ int threshold_steps;
+ int contrast_steps;
+
+ int num_internal_gamma;
+ int num_download_gamma;
+ int num_internal_dither;
+ int num_download_dither;
+
+ int has_df_recovery;
+ int has_paper_protect;
+ int has_staple_detect;
+
+ int has_rif;
+ int has_dtc;
+ int has_sdtc;
+ int has_outline;
+ int has_emphasis;
+ int has_autosep;
+ int has_mirroring;
+ int has_wl_follow;
+ int has_subwindow;
+ int has_diffusion;
+ int has_ipc3;
+ int has_rotation;
+ int has_hybrid_crop_deskew;
+
+ int has_comp_MH;
+ int has_comp_MR;
+ int has_comp_MMR;
+ int has_comp_JBIG;
+ int has_comp_JPG1;
+ int has_comp_JPG2;
+ int has_comp_JPG3;
+
+ /*FIXME: more endorser data? */
+ int endorser_type_f;
+ int endorser_type_b;
+
+ /*FIXME: barcode data? */
+
+ /* overscan size in pixels comes from scanner in basic res units */
+ int os_x_basic;
+ int os_y_basic;
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are gathered by mode_sense command */
+
+ int has_MS_autocolor;
+ int has_MS_prepick;
+ int has_MS_sleep;
+ int has_MS_duplex;
+ int has_MS_rand;
+ int has_MS_bg;
+ int has_MS_df;
+ int has_MS_dropout; /* dropout color specified in mode select data */
+ int has_MS_buff;
+ int has_MS_auto;
+ int has_MS_lamp;
+ int has_MS_jobsep;
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are hard coded because they are not in vpd */
+ /* this section replaces all the old 'switch (s->model)' code */
+
+ /* the scan size in 1/1200th inches, NOT basic_units or sane units */
+ int max_x;
+ int max_y;
+ int min_x;
+ int min_y;
+ int max_x_fb;
+ int max_y_fb;
+
+ int has_back; /* not all duplex scanners can do adf back side only */
+ int color_interlace; /* different models interlace colors differently */
+ int duplex_interlace; /* different models interlace sides differently */
+ int jpeg_interlace; /* different models interlace jpeg sides differently */
+ int cropping_mode; /* lower-end scanners dont crop from paper size */
+ int ghs_in_rs;
+ int window_gamma;
+ int endorser_string_len;
+ int has_pixelsize;
+
+ int broken_diag_serial; /* some scanners are just plain borked */
+ int need_q_table; /* some scanners wont work without these */
+ int need_diag_preread;
+ int hopper_before_op; /* some scanners dont like OP when hopper empty */
+ int no_wait_after_op; /* some scanners dont like TUR after OP */
+
+ int has_vuid_mono; /* mono set window data */
+ int has_vuid_3091; /* 3091/2 set window data */
+ int has_vuid_color; /* color set window data */
+
+ int reverse_by_mode[6]; /* mode specific */
+ int ppl_mod_by_mode[6]; /* mode specific scanline length limitation */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during serial number probing scanner */
+ char serial_name[28]; /* 16 char model, ':', 10 byte serial, null */
+
+ /* --------------------------------------------------------------------- */
+ /* struct with pointers to device/vendor/model names, and a type value */
+ /* used to inform sane frontend about the device */
+ SANE_Device sane;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable SANE_Option structs provide our interface to frontend. */
+ /* some options require lists of strings or numbers, we keep them here */
+ /* instead of in global vars so that they can differ for each scanner */
+
+ /* long array of option structs */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+ /*mode group*/
+ SANE_String_Const mode_list[7];
+ SANE_String_Const source_list[5];
+
+ SANE_Int res_list[17];
+ SANE_Range res_range;
+
+ /*geometry group*/
+ SANE_Range tl_x_range;
+ SANE_Range tl_y_range;
+ SANE_Range br_x_range;
+ SANE_Range br_y_range;
+ SANE_Range paper_x_range;
+ SANE_Range paper_y_range;
+
+ /*enhancement group*/
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+ SANE_Range gamma_range;
+ SANE_Range threshold_range;
+
+ /*ipc group*/
+ SANE_String_Const ht_type_list[4];
+ SANE_Range ht_pattern_range;
+ SANE_Range emphasis_range;
+ SANE_String_Const wl_follow_list[4];
+ SANE_String_Const ipc_mode_list[4];
+ SANE_Range gamma_curve_range;
+ SANE_Range threshold_curve_range;
+ SANE_Range variance_range;
+
+ /*advanced group*/
+ SANE_String_Const compress_list[3];
+ SANE_Range compress_arg_range;
+ SANE_String_Const df_action_list[4];
+ SANE_String_Const df_diff_list[5];
+ SANE_String_Const df_recovery_list[4];
+ SANE_String_Const paper_protect_list[4];
+ SANE_String_Const staple_detect_list[4];
+ SANE_String_Const bg_color_list[4];
+ SANE_String_Const do_color_list[5];
+ SANE_String_Const lamp_color_list[5];
+ SANE_String_Const buff_mode_list[4];
+ SANE_String_Const prepick_list[4];
+ SANE_String_Const overscan_list[4];
+ SANE_Range sleep_time_range;
+ SANE_Range duplex_offset_range;
+ SANE_Range green_offset_range;
+ SANE_Range blue_offset_range;
+ SANE_Range swdespeck_range;
+ SANE_Range swskip_range;
+
+ /*endorser group*/
+ SANE_Range endorser_bits_range;
+ SANE_Range endorser_val_range;
+ SANE_Range endorser_step_range;
+ SANE_Range endorser_y_range;
+ SANE_String_Const endorser_font_list[6];
+ SANE_String_Const endorser_dir_list[3];
+ SANE_String_Const endorser_side_list[3];
+
+ /* --------------------------------------------------------------------- */
+ /* changeable vars to hold user input. modified by SANE_Options above */
+
+ /*mode group*/
+ int u_mode; /*color,lineart,etc*/
+ int source; /*fb,adf front,adf duplex,etc*/
+ int resolution_x; /* X resolution in dpi */
+ int resolution_y; /* Y resolution in dpi */
+
+ /*geometry group*/
+ /* The desired size of the scan, all in 1/1200 inch */
+ int tl_x;
+ int tl_y;
+ int br_x;
+ int br_y;
+ int page_width;
+ int page_height;
+
+ /*enhancement group*/
+ int brightness;
+ int contrast;
+ double gamma;
+ int threshold;
+
+ /* ipc */
+ int rif;
+ int ht_type;
+ int ht_pattern;
+ int outline;
+ int emphasis;
+ int separation;
+ int mirroring;
+ int wl_follow;
+ int ipc_mode;
+
+ /* ipc_mode=DTC */
+ int bp_filter;
+ int smoothing;
+ int gamma_curve;
+ int threshold_curve;
+ int threshold_white;
+ int noise_removal;
+ int matrix_5;
+ int matrix_4;
+ int matrix_3;
+ int matrix_2;
+
+ /* ipc_mode = SDTC */
+ int variance;
+
+ /*advanced group*/
+ int awd;
+ int ald;
+ int compress;
+ int compress_arg;
+ int df_action;
+ int df_skew;
+ int df_thickness;
+ int df_length;
+ int df_diff;
+ int df_recovery;
+ int paper_protect;
+ int staple_detect;
+ int bg_color;
+ int dropout_color;
+ int buff_mode;
+ int prepick;
+ int overscan;
+ int lamp_color;
+ int sleep_time;
+ int duplex_offset;
+ int green_offset;
+ int blue_offset;
+ int low_mem;
+ int hwdeskewcrop;
+ int swdeskew;
+ int swdespeck;
+ int swcrop;
+ double swskip;
+
+ /*endorser group*/
+ int u_endorser;
+ int u_endorser_bits;
+ int u_endorser_val;
+ int u_endorser_step;
+ int u_endorser_y;
+ int u_endorser_font;
+ int u_endorser_dir;
+ int u_endorser_side;
+ char u_endorser_string[81]; /*max length, plus null byte*/
+
+ /* --------------------------------------------------------------------- */
+ /* values which are derived from setting the options above */
+ /* the user never directly modifies these */
+
+ int s_mode; /*color,lineart,etc: sent to scanner*/
+
+ /* this is defined in sane spec as a struct containing:
+ SANE_Frame format;
+ SANE_Bool last_frame;
+ SANE_Int lines;
+ SANE_Int depth; ( binary=1, gray=8, color=8 (!24) )
+ SANE_Int pixels_per_line;
+ SANE_Int bytes_per_line;
+ */
+ SANE_Parameters u_params;
+ SANE_Parameters s_params;
+
+ /* also keep a backup copy, in case the software enhancement code overwrites*/
+ SANE_Parameters u_params_bk;
+ SANE_Parameters s_params_bk;
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by scanning functions to keep track of pages, etc */
+ int started;
+ int reading;
+ int cancelled;
+ int side;
+
+ /* total to read/write */
+ int bytes_tot[2];
+
+ /* how far we have read */
+ int bytes_rx[2];
+ int lines_rx[2]; /*only used by 3091*/
+ int eof_rx[2];
+ int ili_rx[2];
+ int eom_rx;
+
+ /* how far we have written */
+ int bytes_tx[2];
+ int eof_tx[2];
+
+ /*size of buffers (can be smaller than above*/
+ int buff_tot[2];
+ int buff_rx[2];
+ int buff_tx[2];
+
+ unsigned char * buffers[2];
+
+ /* --------------------------------------------------------------------- */
+ /*hardware feature bookkeeping*/
+ int req_driv_crop;
+ int req_driv_lut;
+
+ /* --------------------------------------------------------------------- */
+ /* values used by the software enhancment code (deskew, crop, etc) */
+ SANE_Status deskew_stat;
+ int deskew_vals[2];
+ double deskew_slope;
+
+ SANE_Status crop_stat;
+ int crop_vals[4];
+
+ /* --------------------------------------------------------------------- */
+ /* values used by the compression functions, esp. jpeg with duplex */
+ int jpeg_stage;
+ int jpeg_ff_offset;
+ int jpeg_front_rst;
+ int jpeg_back_rst;
+ int jpeg_x_byte;
+
+ /* --------------------------------------------------------------------- */
+ /* values which used by the command and data sending functions (scsi/usb)*/
+ int fd; /* The scanner device file descriptor. */
+ size_t rs_info;
+ int rs_eom;
+ int rs_ili;
+
+ /* --------------------------------------------------------------------- */
+ /* values which are used by the get hardware status command */
+
+ int hw_top;
+ int hw_A3;
+ int hw_B4;
+ int hw_A4;
+ int hw_B5;
+
+ int hw_hopper;
+ int hw_omr;
+ int hw_adf_open;
+
+ int hw_sleep;
+ int hw_send_sw;
+ int hw_manual_feed;
+ int hw_scan_sw;
+
+ int hw_function;
+
+ int hw_ink_empty;
+ int hw_double_feed;
+
+ int hw_error_code;
+ int hw_skew_angle;
+ int hw_ink_remain;
+
+ int hw_duplex_sw;
+ int hw_density_sw;
+
+ /* values which are used to track the frontend's access to sensors */
+ char hw_read[NUM_OPTIONS-OPT_TOP];
+};
+
+#define CONNECTION_SCSI 0 /* SCSI interface */
+#define CONNECTION_USB 1 /* USB interface */
+
+#define SIDE_FRONT 0
+#define SIDE_BACK 1
+
+#define SOURCE_FLATBED 0
+#define SOURCE_ADF_FRONT 1
+#define SOURCE_ADF_BACK 2
+#define SOURCE_ADF_DUPLEX 3
+
+#define COMP_NONE WD_cmp_NONE
+#define COMP_JPEG WD_cmp_JPG1
+
+#define JPEG_STAGE_HEAD 0
+#define JPEG_STAGE_SOF 1
+#define JPEG_STAGE_SOS 2
+#define JPEG_STAGE_FRONT 3
+#define JPEG_STAGE_BACK 4
+#define JPEG_STAGE_EOI 5
+
+/* these are same as scsi data to make code easier */
+#define MODE_LINEART WD_comp_LA
+#define MODE_HALFTONE WD_comp_HT
+#define MODE_GRAYSCALE WD_comp_GS
+#define MODE_COLOR_LINEART WD_comp_CL
+#define MODE_COLOR_HALFTONE WD_comp_CH
+#define MODE_COLOR WD_comp_CG
+
+/* these are same as dropout scsi data to make code easier */
+#define COLOR_DEFAULT 0
+#define COLOR_GREEN 8
+#define COLOR_RED 9
+#define COLOR_BLUE 11
+
+#define COLOR_WHITE 1
+#define COLOR_BLACK 2
+
+#define COLOR_INTERLACE_UNK 0
+#define COLOR_INTERLACE_RGB 1
+#define COLOR_INTERLACE_BGR 2
+#define COLOR_INTERLACE_RRGGBB 3
+#define COLOR_INTERLACE_3091 4
+
+#define DUPLEX_INTERLACE_ALT 0
+#define DUPLEX_INTERLACE_NONE 1
+#define DUPLEX_INTERLACE_3091 2
+
+#define JPEG_INTERLACE_ALT 0
+#define JPEG_INTERLACE_NONE 1
+
+#define CROP_RELATIVE 0
+#define CROP_ABSOLUTE 1
+
+#define DF_DEFAULT 0
+#define DF_CONTINUE 1
+#define DF_STOP 2
+
+#define FONT_H 0
+#define FONT_HB 1
+#define FONT_HN 2
+#define FONT_V 3
+#define FONT_VB 4
+
+#define DIR_TTB 0
+#define DIR_BTT 1
+
+/* endorser type, same as scsi inquiry data */
+#define ET_OLD 0
+#define ET_30 1
+#define ET_40 2
+
+/* ------------------------------------------------------------------------- */
+
+#define MM_PER_UNIT_UNFIX SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))
+#define MM_PER_UNIT_FIX SANE_FIX(SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)))
+
+#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX((number) * MM_PER_UNIT_UNFIX)
+#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) / MM_PER_UNIT_UNFIX
+
+#define FUJITSU_CONFIG_FILE "fujitsu.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle);
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking);
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp);
+
+const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option);
+
+SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+
+SANE_Status sane_start (SANE_Handle handle);
+
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params);
+
+SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len);
+
+void sane_cancel (SANE_Handle h);
+
+void sane_close (SANE_Handle h);
+
+void sane_exit (void);
+
+/* ------------------------------------------------------------------------- */
+
+static SANE_Status attach_one_scsi (const char *name);
+static SANE_Status attach_one_usb (const char *name);
+static SANE_Status attach_one (const char *devicename, int connType);
+
+static SANE_Status connect_fd (struct fujitsu *s);
+static SANE_Status disconnect_fd (struct fujitsu *s);
+
+static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg);
+
+static SANE_Status init_inquire (struct fujitsu *s);
+static SANE_Status init_vpd (struct fujitsu *s);
+static SANE_Status init_ms (struct fujitsu *s);
+static SANE_Status init_model (struct fujitsu *s);
+static SANE_Status init_user (struct fujitsu *s);
+static SANE_Status init_options (struct fujitsu *scanner);
+static SANE_Status init_interlace (struct fujitsu *scanner);
+static SANE_Status init_serial (struct fujitsu *scanner);
+
+static SANE_Status
+do_cmd(struct fujitsu *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status
+do_scsi_cmd(struct fujitsu *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status
+do_usb_cmd(struct fujitsu *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+static SANE_Status wait_scanner (struct fujitsu *s);
+
+static SANE_Status object_position (struct fujitsu *s, int i_load);
+
+static SANE_Status scanner_control (struct fujitsu *s, int function);
+static SANE_Status scanner_control_ric (struct fujitsu *s, int bytes, int side);
+
+static SANE_Status mode_select_df(struct fujitsu *s);
+
+static SANE_Status mode_select_dropout(struct fujitsu *s);
+
+static SANE_Status mode_select_bg(struct fujitsu *s);
+
+static SANE_Status mode_select_buff (struct fujitsu *s);
+
+static SANE_Status mode_select_prepick (struct fujitsu *s);
+
+static SANE_Status mode_select_auto (struct fujitsu *s);
+
+static SANE_Status set_sleep_mode(struct fujitsu *s);
+
+static int must_downsample (struct fujitsu *s);
+static int must_fully_buffer (struct fujitsu *s);
+static int get_page_width (struct fujitsu *s);
+static int get_page_height (struct fujitsu *s);
+
+static SANE_Status send_lut (struct fujitsu *s);
+static SANE_Status send_endorser (struct fujitsu *s);
+static SANE_Status endorser (struct fujitsu *s);
+static SANE_Status set_window (struct fujitsu *s);
+static SANE_Status get_pixelsize(struct fujitsu *s, int actual);
+
+static SANE_Status update_params (struct fujitsu *s);
+static SANE_Status update_u_params (struct fujitsu *s);
+static SANE_Status backup_params (struct fujitsu *s);
+static SANE_Status restore_params (struct fujitsu *s);
+static SANE_Status start_scan (struct fujitsu *s);
+
+static SANE_Status check_for_cancel(struct fujitsu *s);
+
+#ifdef SANE_FRAME_JPEG
+static SANE_Status read_from_JPEGduplex(struct fujitsu *s);
+#endif
+static SANE_Status read_from_3091duplex(struct fujitsu *s);
+static SANE_Status read_from_scanner(struct fujitsu *s, int side);
+
+static SANE_Status copy_3091(struct fujitsu *s, unsigned char * buf, int len, int side);
+static SANE_Status copy_buffer(struct fujitsu *s, unsigned char * buf, int len, int side);
+
+static SANE_Status read_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side);
+static SANE_Status downsample_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side);
+
+static SANE_Status setup_buffers (struct fujitsu *s);
+
+static SANE_Status get_hardware_status (struct fujitsu *s, SANE_Int option);
+
+static SANE_Status buffer_deskew(struct fujitsu *s, int side);
+static SANE_Status buffer_crop(struct fujitsu *s, int side);
+static SANE_Status buffer_despeck(struct fujitsu *s, int side);
+static int buffer_isblank(struct fujitsu *s, int side);
+
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+
+static size_t maxStringSize (const SANE_String_Const strings[]);
+
+#endif /* FUJITSU_H */
diff --git a/backend/genesys.c b/backend/genesys.c
new file mode 100644
index 0000000..6e7caad
--- /dev/null
+++ b/backend/genesys.c
@@ -0,0 +1,7897 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
+ Copyright (C) 2007 Luke <iceyfor@gmail.com>
+ Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
+ for Plustek Opticbook 3600 support
+
+ Dynamic rasterization code was taken from the epjistsu backend by
+ m. allan noah <kitno455 at gmail dot com>
+
+ Software processing for deskew, crop and dspeckle are inspired by allan's
+ noah work in the fujitsu backend
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ * SANE backend for Genesys Logic GL646/GL841/GL842/GL843/GL846/GL847/GL124 based scanners
+ */
+
+#define BUILD 2411
+#define BACKEND_NAME genesys
+
+#include "genesys.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_magic.h"
+#include "genesys_devices.c"
+
+static SANE_Int num_devices = 0;
+static Genesys_Device *first_dev = 0;
+static Genesys_Scanner *first_handle = 0;
+static const SANE_Device **devlist = 0;
+/* Array of newly attached devices */
+static Genesys_Device **new_dev = 0;
+/* Length of new_dev array */
+static SANE_Int new_dev_len = 0;
+/* Number of entries alloced for new_dev */
+static SANE_Int new_dev_alloced = 0;
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ /* SANE_TITLE_HALFTONE, currently unused */
+ SANE_VALUE_SCAN_MODE_LINEART,
+ 0
+};
+
+static SANE_String_Const color_filter_list[] = {
+ SANE_I18N ("Red"),
+ SANE_I18N ("Green"),
+ SANE_I18N ("Blue"),
+ 0
+};
+
+static SANE_String_Const cis_color_filter_list[] = {
+ SANE_I18N ("Red"),
+ SANE_I18N ("Green"),
+ SANE_I18N ("Blue"),
+ SANE_I18N ("None"),
+ 0
+};
+
+static SANE_String_Const source_list[] = {
+ SANE_I18N (FLATBED),
+ SANE_I18N (TRANSPARENCY_ADAPTER),
+ 0
+};
+
+static SANE_Range swdespeck_range = {
+ 1,
+ 9,
+ 1
+};
+
+static SANE_Range time_range = {
+ 0, /* minimum */
+ 60, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range u12_range = {
+ 0, /* minimum */
+ 4095, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range u14_range = {
+ 0, /* minimum */
+ 16383, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range u16_range = {
+ 0, /* minimum */
+ 65535, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range percentage_range = {
+ SANE_FIX (0), /* minimum */
+ SANE_FIX (100), /* maximum */
+ SANE_FIX (1) /* quantization */
+};
+
+static const SANE_Range threshold_curve_range = {
+ 0, /* minimum */
+ 127, /* maximum */
+ 1 /* quantization */
+};
+
+/**
+ * range for brightness and contrast
+ */
+static const SANE_Range enhance_range = {
+ -100, /* minimum */
+ 100, /* maximum */
+ 1 /* quantization */
+};
+
+void
+sanei_genesys_init_structs (Genesys_Device * dev)
+{
+ unsigned int i, sensor_ok = 0, gpo_ok = 0, motor_ok = 0;
+
+ /* initialize the sensor data stuff */
+ for (i = 0; i < sizeof (Sensor) / sizeof (Genesys_Sensor); i++)
+ {
+ if (dev->model->ccd_type == Sensor[i].sensor_id)
+ {
+ memcpy (&dev->sensor, &Sensor[i], sizeof (Genesys_Sensor));
+ sensor_ok = 1;
+ }
+ }
+
+ /* initialize the GPO data stuff */
+ for (i = 0; i < sizeof (Gpo) / sizeof (Genesys_Gpo); i++)
+ {
+ if (dev->model->gpo_type == Gpo[i].gpo_id)
+ {
+ memcpy (&dev->gpo, &Gpo[i], sizeof (Genesys_Gpo));
+ gpo_ok = 1;
+ }
+ }
+
+ /* initialize the motor data stuff */
+ for (i = 0; i < sizeof (Motor) / sizeof (Genesys_Motor); i++)
+ {
+ if (dev->model->motor_type == Motor[i].motor_id)
+ {
+ memcpy (&dev->motor, &Motor[i], sizeof (Genesys_Motor));
+ motor_ok = 1;
+ }
+ }
+
+ /* sanity check */
+ if (sensor_ok == 0 || motor_ok == 0 || gpo_ok == 0)
+ {
+ DBG (DBG_error0,
+ "sanei_genesys_init_structs: bad description(s) for ccd/gpo/motor=%d/%d/%d\n",
+ dev->model->ccd_type, dev->model->gpo_type,
+ dev->model->motor_type);
+ }
+
+ /* set up initial line distance shift */
+ dev->ld_shift_r = dev->model->ld_shift_r;
+ dev->ld_shift_g = dev->model->ld_shift_g;
+ dev->ld_shift_b = dev->model->ld_shift_b;
+}
+
+void
+sanei_genesys_init_fe (Genesys_Device * dev)
+{
+ unsigned int i;
+
+ DBGSTART;
+ for (i = 0; i < sizeof (Wolfson) / sizeof (Genesys_Frontend); i++)
+ {
+ if (dev->model->dac_type == Wolfson[i].fe_id)
+ {
+ memcpy (&dev->frontend, &Wolfson[i], sizeof (Genesys_Frontend));
+ return;
+ }
+ }
+ DBG (DBG_error0,
+ "sanei_genesys_init_fe: failed to find description for dac_type %d\n",
+ dev->model->dac_type);
+ DBG (DBG_info, "sanei_genesys_init_fe: dac_type %d set up\n",
+ dev->model->dac_type);
+ DBGCOMPLETED;
+}
+
+/* main function for slope creation */
+/**
+ * This function generates a slope table using the given slope
+ * truncated at the given exposure time or step count, whichever comes first.
+ * The reached step time is then stored in final_exposure and used for the rest
+ * of the table. The summed time of the acceleration steps is returned, and the
+ * number of accerelation steps is put into used_steps.
+ *
+ * @param slope_table Table to write to
+ * @param max_steps Size of slope_table in steps
+ * @param use_steps Maximum number of steps to use for acceleration
+ * @param stop_at Minimum step time to use
+ * @param vstart Start step time of default slope
+ * @param vend End step time of default slope
+ * @param steps Step count of default slope
+ * @param g Power for default slope
+ * @param used_steps Final number of steps is stored here
+ * @param vfinal Final step time is stored here
+ * @return Time for acceleration
+ * @note All times in pixel time. Correction for other motor timings is not
+ * done.
+ */
+SANE_Int
+sanei_genesys_generate_slope_table (uint16_t * slope_table,
+ unsigned int max_steps,
+ unsigned int use_steps,
+ uint16_t stop_at,
+ uint16_t vstart,
+ uint16_t vend,
+ unsigned int steps,
+ double g,
+ unsigned int *used_steps,
+ unsigned int *vfinal)
+{
+ double t;
+ SANE_Int sum = 0;
+ unsigned int i;
+ unsigned int c = 0;
+ uint16_t t2;
+ unsigned int dummy;
+ unsigned int _vfinal;
+ if (!used_steps)
+ used_steps = &dummy;
+ if (!vfinal)
+ vfinal = &_vfinal;
+
+ DBG (DBG_proc, "sanei_genesys_generate_slope_table: table size: %d\n",
+ max_steps);
+
+ DBG (DBG_proc,
+ "sanei_genesys_generate_slope_table: stop at time: %d, use %d steps max\n",
+ stop_at, use_steps);
+
+ DBG (DBG_proc,
+ "sanei_genesys_generate_slope_table: target slope: "
+ "vstart: %d, vend: %d, steps: %d, g: %g\n", vstart, vend, steps, g);
+
+ sum = 0;
+ c = 0;
+ *used_steps = 0;
+
+ if (use_steps < 1)
+ use_steps = 1;
+
+ if (stop_at < vstart)
+ {
+ t2 = vstart;
+ for (i = 0; i < steps && i < use_steps - 1 && i < max_steps; i++, c++)
+ {
+ t = pow (((double) i) / ((double) (steps - 1)), g);
+ t2 = vstart * (1 - t) + t * vend;
+ if (t2 < stop_at)
+ break;
+ *slope_table++ = t2;
+ /* DBG (DBG_io, "slope_table[%3d] = %5d\n", c, t2); */
+ sum += t2;
+ }
+ if (t2 > stop_at)
+ {
+ DBG (DBG_warn, "Can not reach target speed(%d) in %d steps.\n",
+ stop_at, use_steps);
+ DBG (DBG_warn, "Expect image to be distorted. "
+ "Ignore this if only feeding.\n");
+ }
+ *vfinal = t2;
+ *used_steps += i;
+ max_steps -= i;
+ }
+ else
+ *vfinal = stop_at;
+
+ for (i = 0; i < max_steps; i++, c++)
+ {
+ *slope_table++ = *vfinal;
+ /* DBG (DBG_io, "slope_table[%3d] = %5d\n", c, *vfinal); */
+ }
+
+ (*used_steps)++;
+ sum += *vfinal;
+
+ DBG (DBG_proc,
+ "sanei_genesys_generate_slope_table: returns sum=%d, used %d steps, completed\n",
+ sum, *used_steps);
+
+ return sum;
+}
+
+/* Generate slope table for motor movement */
+/**
+ * This function generates a slope table using the slope from the motor struct
+ * truncated at the given exposure time or step count, whichever comes first.
+ * The reached step time is then stored in final_exposure and used for the rest
+ * of the table. The summed time of the acceleration steps is returned, and the
+ * number of accerelation steps is put into used_steps.
+ *
+ * @param dev Device struct
+ * @param slope_table Table to write to
+ * @param max_step Size of slope_table in steps
+ * @param use_steps Maximum number of steps to use for acceleration
+ * @param step_type Generate table for this step_type. 0=>full, 1=>half,
+ * 2=>quarter
+ * @param exposure_time Minimum exposure time of a scan line
+ * @param yres Resolution of a scan line
+ * @param used_steps Final number of steps is stored here
+ * @param final_exposure Final step time is stored here
+ * @param power_mode Power mode (related to the Vref used) of the motor
+ * @return Time for acceleration
+ * @note all times in pixel time
+ */
+SANE_Int
+sanei_genesys_create_slope_table3 (Genesys_Device * dev,
+ uint16_t * slope_table,
+ int max_step,
+ unsigned int use_steps,
+ int step_type,
+ int exposure_time,
+ double yres,
+ unsigned int *used_steps,
+ unsigned int *final_exposure,
+ int power_mode)
+{
+ unsigned int sum_time = 0;
+ unsigned int vtarget;
+ unsigned int vend;
+ unsigned int vstart;
+ unsigned int vfinal;
+
+ DBG (DBG_proc,
+ "%s: step_type = %d, "
+ "exposure_time = %d, yres = %g, power_mode = %d\n", __FUNCTION__, step_type,
+ exposure_time, yres, power_mode);
+
+ /* final speed */
+ vtarget = (exposure_time * yres) / dev->motor.base_ydpi;
+
+ vstart = dev->motor.slopes[power_mode][step_type].maximum_start_speed;
+ vend = dev->motor.slopes[power_mode][step_type].maximum_speed;
+
+ vtarget >>= step_type;
+ if (vtarget > 65535)
+ vtarget = 65535;
+
+ vstart >>= step_type;
+ if (vstart > 65535)
+ vstart = 65535;
+
+ vend >>= step_type;
+ if (vend > 65535)
+ vend = 65535;
+
+ sum_time = sanei_genesys_generate_slope_table (slope_table,
+ max_step,
+ use_steps,
+ vtarget,
+ vstart,
+ vend,
+ dev->motor.slopes[power_mode][step_type].minimum_steps << step_type,
+ dev->motor.slopes[power_mode][step_type].g,
+ used_steps,
+ &vfinal);
+
+ if (final_exposure)
+ *final_exposure = (vfinal * dev->motor.base_ydpi) / yres;
+
+ DBG (DBG_proc,
+ "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n",
+ sum_time);
+
+ return sum_time;
+}
+
+
+/* alternate slope table creation function */
+/* the hardcoded values (g and vstart) will go in a motor struct */
+static SANE_Int
+genesys_create_slope_table2 (Genesys_Device * dev,
+ uint16_t * slope_table, int steps,
+ int step_type, int exposure_time,
+ SANE_Bool same_speed, double yres,
+ int power_mode)
+{
+ double t, g;
+ SANE_Int sum = 0;
+ int vstart, vend;
+ int i;
+
+ DBG (DBG_proc,
+ "sanei_genesys_create_slope_table2: %d steps, step_type = %d, "
+ "exposure_time = %d, same_speed = %d, yres = %.2f, power_mode = %d\n",
+ steps, step_type, exposure_time, same_speed, yres, power_mode);
+
+ /* start speed */
+ if (dev->model->motor_type == MOTOR_5345)
+ {
+ if (yres < dev->motor.base_ydpi / 6)
+ vstart = 2500;
+ else
+ vstart = 2000;
+ }
+ else
+ {
+ if (steps == 2)
+ vstart = exposure_time;
+ else if (steps == 3)
+ vstart = 2 * exposure_time;
+ else if (steps == 4)
+ vstart = 1.5 * exposure_time;
+ else if (steps == 120)
+ vstart = 1.81674 * exposure_time;
+ else
+ vstart = exposure_time;
+ }
+
+ /* final speed */
+ vend = (exposure_time * yres) / (dev->motor.base_ydpi * (1 << step_type));
+
+ /*
+ type=1 : full
+ type=2 : half
+ type=4 : quarter
+ vend * type * base_ydpi / exposure = yres
+ */
+
+ /* acceleration */
+ switch (steps)
+ {
+ case 255:
+ /* test for special case: fast moving slope */
+ /* todo: a 'fast' boolean parameter should be better */
+ if (vstart == 2000)
+ g = 0.2013;
+ else
+ g = 0.1677;
+ break;
+ case 120:
+ g = 0.5;
+ break;
+ case 67:
+ g = 0.5;
+ break;
+ case 64:
+ g = 0.2555;
+ break;
+ case 44:
+ g = 0.5;
+ break;
+ case 4:
+ g = 0.5;
+ break;
+ case 3:
+ g = 1;
+ break;
+ case 2:
+ vstart = vend;
+ g = 1;
+ break;
+ default:
+ g = 0.2635;
+ }
+
+ /* if same speed, no 'g' */
+ sum = 0;
+ if (same_speed)
+ {
+ for (i = 0; i < 255; i++)
+ {
+ slope_table[i] = vend;
+ sum += slope_table[i];
+ DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]);
+ }
+ }
+ else
+ {
+ for (i = 0; i < steps; i++)
+ {
+ t = pow (((double) i) / ((double) (steps - 1)), g);
+ slope_table[i] = vstart * (1 - t) + t * vend;
+ DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]);
+ sum += slope_table[i];
+ }
+ for (i = steps; i < 255; i++)
+ {
+ slope_table[i] = vend;
+ DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]);
+ sum += slope_table[i];
+ }
+ }
+
+ DBG (DBG_proc,
+ "sanei_genesys_create_slope_table2: returns sum=%d, completed\n", sum);
+
+ return sum;
+}
+
+/* Generate slope table for motor movement */
+/* todo: check details */
+SANE_Int
+sanei_genesys_create_slope_table (Genesys_Device * dev,
+ uint16_t * slope_table, int steps,
+ int step_type, int exposure_time,
+ SANE_Bool same_speed, double yres,
+ int power_mode)
+{
+ double t;
+ double start_speed;
+ double g;
+ uint32_t time_period;
+ int sum_time = 0;
+ int i, divider;
+ int same_step;
+
+ if (dev->model->motor_type == MOTOR_5345
+ || dev->model->motor_type == MOTOR_HP2300
+ || dev->model->motor_type == MOTOR_HP2400)
+ return genesys_create_slope_table2 (dev, slope_table, steps,
+ step_type, exposure_time,
+ same_speed, yres, power_mode);
+
+ DBG (DBG_proc,
+ "sanei_genesys_create_slope_table: %d steps, step_type = %d, "
+ "exposure_time = %d, same_speed =%d\n", steps, step_type,
+ exposure_time, same_speed);
+ DBG (DBG_proc, "sanei_genesys_create_slope_table: yres = %.2f\n", yres);
+
+ g = 0.6;
+ start_speed = 0.01;
+ same_step = 4;
+ divider = 1 << step_type;
+
+ time_period =
+ (uint32_t) (yres * exposure_time / dev->motor.base_ydpi /*MOTOR_GEAR */ );
+ if ((time_period < 2000) && (same_speed))
+ same_speed = SANE_FALSE;
+
+ time_period = time_period / divider;
+
+ if (same_speed)
+ {
+ for (i = 0; i < steps; i++)
+ {
+ slope_table[i] = (uint16_t) time_period;
+ sum_time += time_period;
+
+ DBG (DBG_io, "slope_table[%d] = %d\n", i, time_period);
+ }
+ DBG (DBG_info,
+ "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n",
+ sum_time);
+ return sum_time;
+ }
+
+ if (time_period > MOTOR_SPEED_MAX * 5)
+ {
+ g = 1.0;
+ start_speed = 0.05;
+ same_step = 2;
+ }
+ else if (time_period > MOTOR_SPEED_MAX * 4)
+ {
+ g = 0.8;
+ start_speed = 0.04;
+ same_step = 2;
+ }
+ else if (time_period > MOTOR_SPEED_MAX * 3)
+ {
+ g = 0.7;
+ start_speed = 0.03;
+ same_step = 2;
+ }
+ else if (time_period > MOTOR_SPEED_MAX * 2)
+ {
+ g = 0.6;
+ start_speed = 0.02;
+ same_step = 3;
+ }
+
+ if (dev->model->motor_type == MOTOR_ST24)
+ {
+ steps = 255;
+ switch ((int) yres)
+ {
+ case 2400:
+ g = 0.1672;
+ start_speed = 1.09;
+ break;
+ case 1200:
+ g = 1;
+ start_speed = 6.4;
+ break;
+ case 600:
+ g = 0.1672;
+ start_speed = 1.09;
+ break;
+ case 400:
+ g = 0.2005;
+ start_speed = 20.0 / 3.0 /*7.5 */ ;
+ break;
+ case 300:
+ g = 0.253;
+ start_speed = 2.182;
+ break;
+ case 150:
+ g = 0.253;
+ start_speed = 4.367;
+ break;
+ default:
+ g = 0.262;
+ start_speed = 7.29;
+ }
+ same_step = 1;
+ }
+
+ if (steps <= same_step)
+ {
+ time_period =
+ (uint32_t) (yres * exposure_time /
+ dev->motor.base_ydpi /*MOTOR_GEAR */ );
+ time_period = time_period / divider;
+
+ if (time_period > 65535)
+ time_period = 65535;
+
+ for (i = 0; i < same_step; i++)
+ {
+ slope_table[i] = (uint16_t) time_period;
+ sum_time += time_period;
+
+ DBG (DBG_io, "slope_table[%d] = %d\n", i, time_period);
+ }
+
+ DBG (DBG_proc,
+ "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n",
+ sum_time);
+ return sum_time;
+ }
+
+ for (i = 0; i < steps; i++)
+ {
+ double j = ((double) i) - same_step + 1; /* start from 1/16 speed */
+
+ if (j <= 0)
+ t = 0;
+ else
+ t = pow (j / (steps - same_step), g);
+
+ time_period = /* time required for full steps */
+ (uint32_t) (yres * exposure_time /
+ dev->motor.base_ydpi /*MOTOR_GEAR */ *
+ (start_speed + (1 - start_speed) * t));
+
+ time_period = time_period / divider;
+ if (time_period > 65535)
+ time_period = 65535;
+
+ slope_table[i] = (uint16_t) time_period;
+ sum_time += time_period;
+
+ DBG (DBG_io, "slope_table[%d] = %d\n", i, slope_table[i]);
+ }
+
+ DBG (DBG_proc,
+ "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n",
+ sum_time);
+
+ return sum_time;
+}
+
+/** @brief computes gamma table
+ * Generates a gamma table of the given length within 0 and the given
+ * maximum value
+ * @param gamma_table gamma table to fill
+ * @param size size of the table
+ * @param maximum value allowed for gamma
+ * @param gamma_max maximum gamma value
+ * @param gamma gamma to compute values
+ * @return a gamma table filled with the computed values
+ * */
+void
+sanei_genesys_create_gamma_table (uint16_t * gamma_table, int size,
+ float maximum, float gamma_max, float gamma)
+{
+ int i;
+ float value;
+
+ if(gamma_table==NULL)
+ {
+ DBG (DBG_proc, "sanei_genesys_create_gamma_table: gamma tbale is NULL\n");
+ return;
+ }
+ DBG (DBG_proc,
+ "sanei_genesys_create_gamma_table: size = %d, "
+ "maximum = %g, gamma_max = %g, gamma = %g\n",
+ size, maximum, gamma_max, gamma);
+ for (i = 0; i < size; i++)
+ {
+ value = gamma_max * pow ((float) i / size, 1.0 / gamma);
+ if (value > maximum)
+ value = maximum;
+ gamma_table[i] = value;
+ }
+ DBG (DBG_proc, "sanei_genesys_create_gamma_table: completed\n");
+}
+
+
+/* computes the exposure_time on the basis of the given vertical dpi,
+ the number of pixels the ccd needs to send,
+ the step_type and the corresponding maximum speed from the motor struct */
+/*
+ Currently considers maximum motor speed at given step_type, minimum
+ line exposure needed for conversion and led exposure time.
+
+ TODO: Should also consider maximum transfer rate: ~6.5MB/s.
+ Note: The enhance option of the scanners does _not_ help. It only halves
+ the amount of pixels transfered.
+ */
+SANE_Int
+sanei_genesys_exposure_time2 (Genesys_Device * dev, float ydpi,
+ int step_type, int endpixel,
+ int led_exposure, int power_mode)
+{
+ int exposure_by_ccd = endpixel + 32;
+ int exposure_by_motor =
+ (dev->motor.slopes[power_mode][step_type].maximum_speed
+ * dev->motor.base_ydpi) / ydpi;
+ int exposure_by_led = led_exposure;
+
+ int exposure = exposure_by_ccd;
+
+ if (exposure < exposure_by_motor)
+ exposure = exposure_by_motor;
+
+ if (exposure < exposure_by_led && dev->model->is_cis)
+ exposure = exposure_by_led;
+
+ return exposure;
+}
+
+/* computes the exposure_time on the basis of the given horizontal dpi */
+/* we will clean/simplify it by using constants from a future motor struct */
+SANE_Int
+sanei_genesys_exposure_time (Genesys_Device * dev, Genesys_Register_Set * reg,
+ int xdpi)
+{
+ if (dev->model->motor_type == MOTOR_5345)
+ {
+ if (dev->model->cmd_set->get_filter_bit (reg))
+ {
+ /* monochrome */
+ switch (xdpi)
+ {
+ case 600:
+ return 8500;
+ case 500:
+ case 400:
+ case 300:
+ case 250:
+ case 200:
+ case 150:
+ return 5500;
+ case 100:
+ return 6500;
+ case 50:
+ return 12000;
+ default:
+ return 11000;
+ }
+ }
+ else
+ {
+ /* color scan */
+ switch (xdpi)
+ {
+ case 300:
+ case 250:
+ case 200:
+ return 5500;
+ case 50:
+ return 12000;
+ default:
+ return 11000;
+ }
+ }
+ }
+ else if (dev->model->motor_type == MOTOR_HP2400)
+ {
+ if (dev->model->cmd_set->get_filter_bit (reg))
+ {
+ /* monochrome */
+ switch (xdpi)
+ {
+ case 200:
+ return 7210;
+ default:
+ return 11111;
+ }
+ }
+ else
+ {
+ /* color scan */
+ switch (xdpi)
+ {
+ case 600:
+ return 8751; /*11902; 19200 */
+ default:
+ return 11111;
+ }
+ }
+ }
+ else if (dev->model->motor_type == MOTOR_HP2300)
+ {
+ if (dev->model->cmd_set->get_filter_bit (reg))
+ {
+ /* monochrome */
+ switch (xdpi)
+ {
+ case 600:
+ return 8699; /* 3200; */
+ case 300:
+ return 3200; /*10000;, 3200 -> too dark */
+ case 150:
+ return 4480; /* 3200 ???, warmup needs 4480 */
+ case 75:
+ return 5500;
+ default:
+ return 11111;
+ }
+ }
+ else
+ {
+ /* color scan */
+ switch (xdpi)
+ {
+ case 600:
+ return 8699;
+ case 300:
+ return 4349;
+ case 150:
+ case 75:
+ return 4480;
+ default:
+ return 11111;
+ }
+ }
+ }
+ return dev->settings.exposure_time;
+}
+
+
+
+/* Sends a block of shading information to the scanner.
+ The data is placed at address 0x0000 for color mode, gray mode and
+ unconditionally for the following CCD chips: HP2300, HP2400 and HP5345
+ In the other cases (lineart, halftone on ccd chips not mentioned) the
+ addresses are 0x2a00 for dpihw==0, 0x5500 for dpihw==1 and 0xa800 for
+ dpihw==2. //Note: why this?
+
+ The data needs to be of size "size", and in little endian byte order.
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+genesys_send_offset_and_shading (Genesys_Device * dev, uint8_t * data,
+ int size)
+{
+ int dpihw;
+ int start_address;
+ SANE_Status status;
+
+ DBG (DBG_proc, "genesys_send_offset_and_shading (size = %d)\n", size);
+
+ /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to
+ * a per ASIC shading data loading function if available */
+ if(dev->model->cmd_set->send_shading_data!=NULL)
+ {
+ status=dev->model->cmd_set->send_shading_data(dev, data, size);
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* gl646, gl84[123] case */
+ dpihw = sanei_genesys_read_reg_from_set (dev->reg, 0x05) >> 6;
+
+ /* TODO invert the test so only the 2 models behaving like that are
+ * tested instead of adding all the others */
+ /* many scanners send coefficient for lineart/gray like in color mode */
+ if (dev->settings.scan_mode < 2
+ && dev->model->ccd_type != CCD_PLUSTEK3800
+ && dev->model->ccd_type != CCD_KVSS080
+ && dev->model->ccd_type != CCD_G4050
+ && dev->model->ccd_type != CCD_CS4400F
+ && dev->model->ccd_type != CCD_CS8400F
+ && dev->model->ccd_type != CCD_DSMOBILE600
+ && dev->model->ccd_type != CCD_XP300
+ && dev->model->ccd_type != CCD_DP665
+ && dev->model->ccd_type != CCD_DP685
+ && dev->model->ccd_type != CCD_ROADWARRIOR
+ && dev->model->ccd_type != CCD_HP2300
+ && dev->model->ccd_type != CCD_HP2400
+ && dev->model->ccd_type != CCD_HP3670
+ && dev->model->ccd_type != CCD_5345) /* lineart, halftone */
+ {
+ if (dpihw == 0) /* 600 dpi */
+ start_address = 0x02a00;
+ else if (dpihw == 1) /* 1200 dpi */
+ start_address = 0x05500;
+ else if (dpihw == 2) /* 2400 dpi */
+ start_address = 0x0a800;
+ else /* reserved */
+ return SANE_STATUS_INVAL;
+ }
+ else /* color */
+ start_address = 0x00;
+
+ status = sanei_genesys_set_buffer_address (dev, start_address);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_send_offset_and_shading: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->bulk_write_data (dev, 0x3c, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_send_offset_and_shading: failed to send shading table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* ? */
+SANE_Status
+sanei_genesys_init_shading_data (Genesys_Device * dev, int pixels_per_line)
+{
+ SANE_Status status;
+ uint8_t *shading_data, *shading_data_ptr;
+ int channels;
+ int i;
+
+ /* these models don't need to init shading data due to the use of specific send shading data
+ function */
+ if (dev->model->ccd_type==CCD_KVSS080
+ || dev->model->ccd_type==CCD_G4050
+ || dev->model->ccd_type==CCD_CS4400F
+ || dev->model->ccd_type==CCD_CS8400F
+ || dev->model->cmd_set->send_shading_data!=NULL)
+ return SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "sanei_genesys_init_shading_data (pixels_per_line = %d)\n",
+ pixels_per_line);
+
+ if (dev->settings.scan_mode >= 2) /* 3 pass or single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ shading_data = malloc (pixels_per_line * 4 * channels); /* 16 bit black, 16 bit white */
+ if (!shading_data)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_init_shading_data: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shading_data_ptr = shading_data;
+
+ for (i = 0; i < pixels_per_line * channels; i++)
+ {
+ *shading_data_ptr++ = 0x00; /* dark lo */
+ *shading_data_ptr++ = 0x00; /* dark hi */
+ *shading_data_ptr++ = 0x00; /* white lo */
+ *shading_data_ptr++ = 0x40; /* white hi -> 0x4000 */
+ }
+
+ status =
+ genesys_send_offset_and_shading (dev, shading_data,
+ pixels_per_line * 4 * channels);
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error,
+ "sanei_genesys_init_shading_data: failed to send shading data: %s\n",
+ sane_strstatus (status));
+
+ free (shading_data);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/* Find the position of the reference point:
+ takes gray level 8 bits data and find
+ first CCD usable pixel and top of scanning area */
+SANE_Status
+sanei_genesys_search_reference_point (Genesys_Device * dev, uint8_t * data,
+ int start_pixel, int dpi, int width,
+ int height)
+{
+ int x, y;
+ int current, left, top = 0;
+ uint8_t *image;
+ int size, count;
+ int level = 80; /* edge threshold level */
+
+ /*sanity check */
+ if ((width < 3) || (height < 3))
+ return SANE_STATUS_INVAL;
+
+ /* transformed image data */
+ size = width * height;
+ image = malloc (size);
+ if (!image)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_search_reference_point: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* laplace filter to denoise picture */
+ memcpy (image, data, size); /* to initialize unprocessed part of the image buffer */
+ for (y = 1; y < height - 1; y++)
+ for (x = 1; x < width - 1; x++)
+ {
+ image[y * width + x] =
+ (data[(y - 1) * width + x + 1] + 2 * data[(y - 1) * width + x] +
+ data[(y - 1) * width + x - 1] + 2 * data[y * width + x + 1] +
+ 4 * data[y * width + x] + 2 * data[y * width + x - 1] +
+ data[(y + 1) * width + x + 1] + 2 * data[(y + 1) * width + x] +
+ data[(y + 1) * width + x - 1]) / 16;
+ }
+
+ memcpy (data, image, size);
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("laplace.pnm", image, 8, 1, width, height);
+
+ /* apply X direction sobel filter
+ -1 0 1
+ -2 0 2
+ -1 0 1
+ and finds threshold level
+ */
+ level = 0;
+ for (y = 2; y < height - 2; y++)
+ for (x = 2; x < width - 2; x++)
+ {
+ current =
+ data[(y - 1) * width + x + 1] - data[(y - 1) * width + x - 1] +
+ 2 * data[y * width + x + 1] - 2 * data[y * width + x - 1] +
+ data[(y + 1) * width + x + 1] - data[(y + 1) * width + x - 1];
+ if (current < 0)
+ current = -current;
+ if (current > 255)
+ current = 255;
+ image[y * width + x] = current;
+ if (current > level)
+ level = current;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("xsobel.pnm", image, 8, 1, width, height);
+
+ /* set up detection level */
+ level = level / 3;
+
+ /* find left black margin first
+ todo: search top before left
+ we average the result of N searches */
+ left = 0;
+ count = 0;
+ for (y = 2; y < 11; y++)
+ {
+ x = 8;
+ while ((x < width / 2) && (image[y * width + x] < level))
+ {
+ image[y * width + x] = 255;
+ x++;
+ }
+ count++;
+ left += x;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("detected-xsobel.pnm", image, 8, 1, width,
+ height);
+ left = left / count;
+
+ /* turn it in CCD pixel at full sensor optical resolution */
+ dev->sensor.CCD_start_xoffset =
+ start_pixel + (left * dev->sensor.optical_res) / dpi;
+
+ /* find top edge by detecting black strip */
+ /* apply Y direction sobel filter
+ -1 -2 -1
+ 0 0 0
+ 1 2 1
+ */
+ level = 0;
+ for (y = 2; y < height - 2; y++)
+ for (x = 2; x < width - 2; x++)
+ {
+ current =
+ -data[(y - 1) * width + x + 1] - 2 * data[(y - 1) * width + x] -
+ data[(y - 1) * width + x - 1] + data[(y + 1) * width + x + 1] +
+ 2 * data[(y + 1) * width + x] + data[(y + 1) * width + x - 1];
+ if (current < 0)
+ current = -current;
+ if (current > 255)
+ current = 255;
+ image[y * width + x] = current;
+ if (current > level)
+ level = current;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("ysobel.pnm", image, 8, 1, width, height);
+
+ /* set up detection level */
+ level = level / 3;
+
+ /* search top of horizontal black stripe : TODO yet another flag */
+ if (dev->model->ccd_type == CCD_5345
+ && dev->model->motor_type == MOTOR_5345)
+ {
+ top = 0;
+ count = 0;
+ for (x = width / 2; x < width - 1; x++)
+ {
+ y = 2;
+ while ((y < height) && (image[x + y * width] < level))
+ {
+ image[y * width + x] = 255;
+ y++;
+ }
+ count++;
+ top += y;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("detected-ysobel.pnm", image, 8, 1,
+ width, height);
+ top = top / count;
+
+ /* bottom of black stripe is of fixed witdh, this hardcoded value
+ * will be moved into device struct if more such values are needed */
+ top += 10;
+ dev->model->y_offset_calib = SANE_FIX ((top * MM_PER_INCH) / dpi);
+ DBG (DBG_info,
+ "sanei_genesys_search_reference_point: black stripe y_offset = %f mm \n",
+ SANE_UNFIX (dev->model->y_offset_calib));
+ }
+
+ /* find white corner in dark area : TODO yet another flag */
+ if ((dev->model->ccd_type == CCD_HP2300
+ && dev->model->motor_type == MOTOR_HP2300)
+ || (dev->model->ccd_type == CCD_HP2400
+ && dev->model->motor_type == MOTOR_HP2400)
+ || (dev->model->ccd_type == CCD_HP3670
+ && dev->model->motor_type == MOTOR_HP3670))
+ {
+ top = 0;
+ count = 0;
+ for (x = 10; x < 60; x++)
+ {
+ y = 2;
+ while ((y < height) && (image[x + y * width] < level))
+ y++;
+ top += y;
+ count++;
+ }
+ top = top / count;
+ dev->model->y_offset_calib = SANE_FIX ((top * MM_PER_INCH) / dpi);
+ DBG (DBG_info,
+ "sanei_genesys_search_reference_point: white corner y_offset = %f mm\n",
+ SANE_UNFIX (dev->model->y_offset_calib));
+ }
+
+ free (image);
+ DBG (DBG_proc,
+ "sanei_genesys_search_reference_point: CCD_start_xoffset = %d, left = %d, top = %d\n",
+ dev->sensor.CCD_start_xoffset, left, top);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sanei_genesys_calculate_zmode2 (SANE_Bool two_table,
+ uint32_t exposure_time,
+ uint16_t * slope_table,
+ int reg21,
+ int move, int reg22, uint32_t * z1,
+ uint32_t * z2)
+{
+ int i;
+ int sum;
+ DBG (DBG_info, "sanei_genesys_calculate_zmode2: two_table=%d\n", two_table);
+
+ /* acceleration total time */
+ sum = 0;
+ for (i = 0; i < reg21; i++)
+ sum += slope_table[i];
+
+ /* compute Z1MOD */
+ /* c=sum(slope_table;reg21)
+ d=reg22*cruising speed
+ Z1MOD=(c+d) % exposure_time */
+ *z1 = (sum + reg22 * slope_table[reg21 - 1]) % exposure_time;
+
+ /* compute Z2MOD */
+ /* a=sum(slope_table;reg21), b=move or 1 if 2 tables */
+ /* Z2MOD=(a+b) % exposure_time */
+ if (!two_table)
+ sum = sum + (move * slope_table[reg21 - 1]);
+ else
+ sum = sum + slope_table[reg21 - 1];
+ *z2 = sum % exposure_time;
+}
+
+
+/* huh? */
+/* todo: double check */
+/* Z1 and Z2 seem to be a time to synchronize with clock or a phase correction */
+/* steps_sum is the result of create_slope_table */
+/* last_speed is the last entry of the slope_table */
+/* feedl is registers 3d,3e,3f */
+/* fastfed is register 02 bit 3 */
+/* scanfed is register 1f */
+/* fwdstep is register 22 */
+/* tgtime is register 6c bit 6+7 >> 6 */
+
+void
+sanei_genesys_calculate_zmode (uint32_t exposure_time,
+ uint32_t steps_sum, uint16_t last_speed,
+ uint32_t feedl, uint8_t fastfed,
+ uint8_t scanfed, uint8_t fwdstep,
+ uint8_t tgtime, uint32_t * z1, uint32_t * z2)
+{
+ uint8_t exposure_factor;
+
+ exposure_factor = pow (2, tgtime); /* todo: originally, this is always 2^0 ! */
+
+ /* Z1 is for buffer-full backward forward moving */
+ *z1 =
+ exposure_factor * ((steps_sum + fwdstep * last_speed) % exposure_time);
+
+ /* Z2 is for acceleration before scan */
+ if (fastfed) /* two curve mode */
+ {
+ *z2 =
+ exposure_factor * ((steps_sum + scanfed * last_speed) %
+ exposure_time);
+ }
+ else /* one curve mode */
+ {
+ *z2 =
+ exposure_factor * ((steps_sum + feedl * last_speed) % exposure_time);
+ }
+}
+
+
+static void
+genesys_adjust_gain (double *applied_multi,
+ uint8_t * new_gain, double multi, uint8_t gain)
+{
+ double voltage, original_voltage;
+
+ DBG (DBG_proc, "genesys_adjust_gain: multi=%f, gain=%d\n", multi, gain);
+
+ voltage = 0.5 + gain * 0.25;
+ original_voltage = voltage;
+
+ voltage *= multi;
+
+ *new_gain = (uint8_t) ((voltage - 0.5) * 4);
+ if (*new_gain > 0x0e)
+ *new_gain = 0x0e;
+
+ voltage = 0.5 + (*new_gain) * 0.25;
+
+ *applied_multi = voltage / original_voltage;
+
+ DBG (DBG_proc,
+ "genesys_adjust_gain: orig voltage=%.2f, new voltage=%.2f, "
+ "*applied_multi=%f, *new_gain=%d\n", original_voltage, voltage,
+ *applied_multi, *new_gain);
+ return;
+}
+
+
+/* todo: is return status necessary (unchecked?) */
+static SANE_Status
+genesys_average_white (Genesys_Device * dev, int channels, int channel,
+ uint8_t * data, int size, int *max_average)
+{
+ int gain_white_ref, sum, range;
+ int average;
+ int i;
+
+ DBG (DBG_proc,
+ "genesys_average_white: channels=%d, channel=%d, size=%d\n",
+ channels, channel, size);
+
+ range = size / 50;
+
+ if (dev->settings.scan_method == SCAN_METHOD_TRANSPARENCY) /* transparency mode */
+ gain_white_ref = dev->sensor.fau_gain_white_ref * 256;
+ else
+ gain_white_ref = dev->sensor.gain_white_ref * 256;
+
+ if (range < 1)
+ range = 1;
+
+ size = size / (2 * range * channels);
+
+ data += (channel * 2);
+
+ *max_average = 0;
+
+ while (size--)
+ {
+ sum = 0;
+ for (i = 0; i < range; i++)
+ {
+ sum += (*data);
+ sum += *(data + 1) * 256;
+ data += (2 * channels); /* byte based */
+ }
+
+ average = (sum / range);
+ if (average > *max_average)
+ *max_average = average;
+ }
+
+ DBG (DBG_proc,
+ "genesys_average_white: max_average=%d, gain_white_ref = %d, finished\n",
+ *max_average, gain_white_ref);
+
+ if (*max_average >= gain_white_ref)
+ return SANE_STATUS_INVAL;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* todo: understand, values are too high */
+static int
+genesys_average_black (Genesys_Device * dev, int channel,
+ uint8_t * data, int pixels)
+{
+ int i;
+ int sum;
+ int pixel_step;
+
+ DBG (DBG_proc, "genesys_average_black: channel=%d, pixels=%d\n",
+ channel, pixels);
+
+ sum = 0;
+
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ {
+ data += (channel * 2);
+ pixel_step = 3 * 2;
+ }
+ else
+ {
+ pixel_step = 2;
+ }
+
+ for (i = 0; i < pixels; i++)
+ {
+ sum += *data;
+ sum += *(data + 1) * 256;
+
+ data += pixel_step;
+ }
+
+ DBG (DBG_proc, "genesys_average_black = %d\n", sum / pixels);
+
+ return (int) (sum / pixels);
+}
+
+
+/* todo: check; it works but the lines 1, 2, and 3 are too dark even with the
+ same offset and gain settings? */
+static SANE_Status
+genesys_coarse_calibration (Genesys_Device * dev)
+{
+ int size;
+ int black_pixels;
+ int white_average;
+ int channels;
+ SANE_Status status;
+ uint8_t offset[4] = { 0xa0, 0x00, 0xa0, 0x40 }; /* first value isn't used */
+ uint16_t white[12], dark[12];
+ int i, j;
+ uint8_t *calibration_data, *all_data;
+
+ DBG (DBG_info, "genesys_coarse_calibration (scan_mode = %d)\n",
+ dev->settings.scan_mode);
+
+ black_pixels = dev->sensor.black_pixels
+ * dev->settings.xres / dev->sensor.optical_res;
+
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ DBG (DBG_info, "channels %d y_size %d xres %d\n",
+ channels, dev->model->y_size, dev->settings.xres);
+ size =
+ channels * 2 * SANE_UNFIX (dev->model->y_size) * dev->settings.xres /
+ 25.4;
+ /* 1 1 mm 1/inch inch/mm */
+
+ calibration_data = malloc (size);
+ if (!calibration_data)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: failed to allocate memory(%d bytes)\n",
+ size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ all_data = calloc (1, size * 4);
+
+ status = dev->model->cmd_set->set_fe (dev, AFE_INIT);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to set frontend: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ free(all_data);
+ free(calibration_data);
+ return status;
+ }
+
+ dev->frontend.sign[0] = 0;
+ dev->frontend.sign[1] = 0;
+ dev->frontend.sign[2] = 0;
+ dev->frontend.gain[0] = 2;
+ dev->frontend.gain[1] = 2;
+ dev->frontend.gain[2] = 2; /* todo: ? was 2 */
+ dev->frontend.offset[0] = offset[0];
+ dev->frontend.offset[1] = offset[0];
+ dev->frontend.offset[2] = offset[0];
+
+ for (i = 0; i < 4; i++) /* read 4 lines */
+ {
+ if (i < 3) /* first 3 lines */
+ {
+ dev->frontend.offset[0] = offset[i];
+ dev->frontend.offset[1] = offset[i];
+ dev->frontend.offset[2] = offset[i];
+ }
+
+ if (i == 1) /* second line */
+ {
+ double applied_multi;
+ double gain_white_ref;
+
+ if (dev->settings.scan_method == SCAN_METHOD_TRANSPARENCY) /* Transparency */
+ gain_white_ref = dev->sensor.fau_gain_white_ref * 256;
+ else
+ gain_white_ref = dev->sensor.gain_white_ref * 256;
+ /* white and black are defined downwards */
+
+ genesys_adjust_gain (&applied_multi,
+ &dev->frontend.gain[0],
+ gain_white_ref / (white[0] - dark[0]),
+ dev->frontend.gain[0]);
+ genesys_adjust_gain (&applied_multi,
+ &dev->frontend.gain[1],
+ gain_white_ref / (white[1] - dark[1]),
+ dev->frontend.gain[1]);
+ genesys_adjust_gain (&applied_multi,
+ &dev->frontend.gain[2],
+ gain_white_ref / (white[2] - dark[2]),
+ dev->frontend.gain[2]);
+
+ dev->frontend.gain[0] = dev->frontend.gain[1] =
+ dev->frontend.gain[2] = 2;
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x28, dev->frontend.gain[0]);
+ if (status != SANE_STATUS_GOOD) /* todo: this was 0x28 + 3 ? */
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to write gain[0]: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x29, dev->frontend.gain[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to write gain[1]: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x2a, dev->frontend.gain[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to write gain[2]: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (i == 3) /* last line */
+ {
+ double x, y, rate;
+
+ for (j = 0; j < 3; j++)
+ {
+
+ x =
+ (double) (dark[(i - 2) * 3 + j] -
+ dark[(i - 1) * 3 + j]) * 254 / (offset[i - 1] / 2 -
+ offset[i - 2] / 2);
+ y = x - x * (offset[i - 1] / 2) / 254 - dark[(i - 1) * 3 + j];
+ rate = (x - DARK_VALUE - y) * 254 / x + 0.5;
+
+ dev->frontend.offset[j] = (uint8_t) (rate);
+
+ if (dev->frontend.offset[j] > 0x7f)
+ dev->frontend.offset[j] = 0x7f;
+ dev->frontend.offset[j] <<= 1;
+ }
+ }
+ status =
+ sanei_genesys_fe_write_data (dev, 0x20, dev->frontend.offset[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to write offset[0]: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x21, dev->frontend.offset[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to write offset[1]: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x22, dev->frontend.offset[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to write offset[2]: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info,
+ "genesys_coarse_calibration: doing scan: sign: %d/%d/%d, gain: %d/%d/%d, offset: %d/%d/%d\n",
+ dev->frontend.sign[0], dev->frontend.sign[1],
+ dev->frontend.sign[2], dev->frontend.gain[0],
+ dev->frontend.gain[1], dev->frontend.gain[2],
+ dev->frontend.offset[0], dev->frontend.offset[1],
+ dev->frontend.offset[2]);
+
+ status =
+ dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_read_data_from_scanner (dev, calibration_data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ memcpy (all_data + i * size, calibration_data, size);
+ if (i == 3) /* last line */
+ {
+ SANE_Byte *all_data_8 = malloc (size * 4 / 2);
+ unsigned int count;
+
+ for (count = 0; count < (unsigned int) (size * 4 / 2); count++)
+ all_data_8[count] = all_data[count * 2 + 1];
+ status =
+ sanei_genesys_write_pnm_file ("coarse.pnm", all_data_8, 8,
+ channels, size / 6, 4);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: sanei_genesys_write_pnm_file failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_coarse_calibration: Failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ {
+ for (j = 0; j < 3; j++)
+ {
+ genesys_average_white (dev, 3, j, calibration_data, size,
+ &white_average);
+ white[i * 3 + j] = white_average;
+ dark[i * 3 + j] =
+ genesys_average_black (dev, j, calibration_data,
+ black_pixels);
+ DBG (DBG_info,
+ "genesys_coarse_calibration: white[%d]=%d, black[%d]=%d\n",
+ i * 3 + j, white[i * 3 + j], i * 3 + j, dark[i * 3 + j]);
+ }
+ }
+ else /* one color-component modes */
+ {
+ genesys_average_white (dev, 1, 0, calibration_data, size,
+ &white_average);
+ white[i * 3 + 0] = white[i * 3 + 1] = white[i * 3 + 2] =
+ white_average;
+ dark[i * 3 + 0] = dark[i * 3 + 1] = dark[i * 3 + 2] =
+ genesys_average_black (dev, 0, calibration_data, black_pixels);
+ }
+
+ if (i == 3)
+ {
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ {
+ /* todo: huh? */
+ dev->dark[0] =
+ (uint16_t) (1.6925 * dark[i * 3 + 0] + 0.1895 * 256);
+ dev->dark[1] =
+ (uint16_t) (1.4013 * dark[i * 3 + 1] + 0.3147 * 256);
+ dev->dark[2] =
+ (uint16_t) (1.2931 * dark[i * 3 + 2] + 0.1558 * 256);
+ }
+ else /* one color-component modes */
+ {
+ switch (dev->settings.color_filter)
+ {
+ case 0:
+ default:
+ dev->dark[0] =
+ (uint16_t) (1.6925 * dark[i * 3 + 0] +
+ (1.1895 - 1.0) * 256);
+ dev->dark[1] = dev->dark[2] = dev->dark[0];
+ break;
+
+ case 1:
+ dev->dark[1] =
+ (uint16_t) (1.4013 * dark[i * 3 + 1] +
+ (1.3147 - 1.0) * 256);
+ dev->dark[0] = dev->dark[2] = dev->dark[1];
+ break;
+
+ case 2:
+ dev->dark[2] =
+ (uint16_t) (1.2931 * dark[i * 3 + 2] +
+ (1.1558 - 1.0) * 256);
+ dev->dark[0] = dev->dark[1] = dev->dark[2];
+ break;
+ }
+ }
+ }
+ } /* for (i = 0; i < 4; i++) */
+
+ free(all_data);
+ DBG (DBG_info,
+ "genesys_coarse_calibration: final: sign: %d/%d/%d, gain: %d/%d/%d, offset: %d/%d/%d\n",
+ dev->frontend.sign[0], dev->frontend.sign[1], dev->frontend.sign[2],
+ dev->frontend.gain[0], dev->frontend.gain[1], dev->frontend.gain[2],
+ dev->frontend.offset[0], dev->frontend.offset[1],
+ dev->frontend.offset[2]);
+ DBGCOMPLETED;
+
+ return status;
+}
+
+/* Averages image data.
+ average_data and calibration_data are little endian 16 bit words.
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+void
+genesys_average_data (uint8_t * average_data,
+ uint8_t * calibration_data,
+ uint32_t lines,
+ uint32_t pixel_components_per_line)
+{
+ uint32_t x, y;
+ uint32_t sum;
+
+ for (x = 0; x < pixel_components_per_line; x++)
+ {
+ sum = 0;
+ for (y = 0; y < lines; y++)
+ {
+ sum += calibration_data[(x + y * pixel_components_per_line) * 2];
+ sum +=
+ calibration_data[(x + y * pixel_components_per_line) * 2 +
+ 1] * 256;
+ }
+ sum /= lines;
+ *average_data++ = sum & 255;
+ *average_data++ = sum / 256;
+ }
+}
+
+/**
+ * scans a white area with motor and lamp off to get the per CCD pixel offset
+ * that will be used to compute shading coefficient
+ * @param dev scanner's device
+ * @return SANE_STATUS_GOOD if OK, else an error
+ */
+static SANE_Status
+genesys_dark_shading_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ size_t size;
+ uint32_t pixels_per_line;
+ uint8_t channels;
+ uint8_t *calibration_data;
+ SANE_Bool motor;
+
+ DBGSTART;
+
+ /* end pixel - start pixel */
+ pixels_per_line = dev->calib_pixels;
+ channels = dev->calib_channels;
+
+ FREE_IFNOT_NULL (dev->dark_average_data);
+
+ dev->average_size = channels * 2 * pixels_per_line;
+
+ dev->dark_average_data = malloc (dev->average_size);
+ if (!dev->dark_average_data)
+ {
+ DBG (DBG_error,
+ "genesys_dark_shading_calibration: failed to allocate average memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* size is size in bytes for scanarea: bytes_per_line * lines */
+ size = channels * 2 * pixels_per_line * (dev->calib_lines + 1);
+
+ calibration_data = malloc (size);
+ if (!calibration_data)
+ {
+ DBG (DBG_error,
+ "genesys_dark_shading_calibration: failed to allocate calibration data memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ motor=SANE_TRUE;
+ if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
+ {
+ motor=SANE_FALSE;
+ }
+
+ /* turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
+ * because they have a calibration sheet with a sufficient black strip */
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_FALSE);
+ dev->model->cmd_set->set_motor_power (dev->calib_reg, motor);
+ }
+ else
+ {
+ dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_TRUE);
+ dev->model->cmd_set->set_motor_power (dev->calib_reg, motor);
+ }
+
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg,
+ dev->model->
+ cmd_set->bulk_full_size ());
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_shading_calibration: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ usleep (200 * 1000); /* wait 200 ms: lamp needs some time to get dark */
+
+ status = dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_shading_calibration: Failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_read_data_from_scanner (dev, calibration_data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_shading_calibration: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_shading_calibration: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ genesys_average_data (dev->dark_average_data, calibration_data,
+ dev->calib_lines,
+ pixels_per_line * channels);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file ("black_shading.pnm", calibration_data, 16,
+ channels, pixels_per_line,
+ dev->calib_lines);
+ sanei_genesys_write_pnm_file ("black_average.pnm",
+ dev->dark_average_data, 16, channels,
+ pixels_per_line, 1);
+ }
+
+ free (calibration_data);
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * this function builds dummy dark calibration data so that we can
+ * compute shading coefficient in a clean way
+ * todo: current values are hardcoded, we have to find if they
+ * can be computed from previous calibration data (when doing offset
+ * calibration ?)
+ */
+static SANE_Status
+genesys_dummy_dark_shading (Genesys_Device * dev)
+{
+ uint32_t pixels_per_line;
+ uint8_t channels;
+ uint32_t x, skip, xend;
+ int dummy1, dummy2, dummy3; /* dummy black average per channel */
+
+ DBGSTART;
+
+ pixels_per_line = dev->calib_pixels;
+ channels = dev->calib_channels;
+
+ FREE_IFNOT_NULL (dev->dark_average_data);
+
+ dev->average_size = channels * 2 * pixels_per_line;
+ dev->dark_average_data = malloc (dev->average_size);
+ if (!dev->dark_average_data)
+ {
+ DBG (DBG_error,
+ "genesys_dummy_dark_shading: failed to allocate average memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (dev->dark_average_data, 0x00, channels * 2 * pixels_per_line);
+
+ /* we average values on 'the left' where CCD pixels are under casing and
+ give darkest values. We then use these as dummy dark calibration */
+ if (dev->settings.xres <= dev->sensor.optical_res / 2)
+ {
+ skip = 4;
+ xend = 36;
+ }
+ else
+ {
+ skip = 4;
+ xend = 68;
+ }
+ if (dev->model->ccd_type==CCD_G4050
+ || dev->model->ccd_type==CCD_CS4400F
+ || dev->model->ccd_type==CCD_CS8400F
+ || dev->model->ccd_type==CCD_KVSS080)
+ {
+ skip = 2;
+ xend = dev->sensor.black_pixels;
+ }
+
+ /* average each channels on half left margin */
+ dummy1 = 0;
+ dummy2 = 0;
+ dummy3 = 0;
+
+ for (x = skip + 1; x <= xend; x++)
+ {
+ dummy1 +=
+ dev->white_average_data[channels * 2 * x] +
+ 256 * dev->white_average_data[channels * 2 * x + 1];
+ if (channels > 1)
+ {
+ dummy2 +=
+ (dev->white_average_data[channels * 2 * x + 2] +
+ 256 * dev->white_average_data[channels * 2 * x + 3]);
+ dummy3 +=
+ (dev->white_average_data[channels * 2 * x + 4] +
+ 256 * dev->white_average_data[channels * 2 * x + 5]);
+ }
+ }
+
+ dummy1 /= (xend - skip);
+ if (channels > 1)
+ {
+ dummy2 /= (xend - skip);
+ dummy3 /= (xend - skip);
+ }
+ DBG (DBG_proc,
+ "genesys_dummy_dark_shading: dummy1=%d, dummy2=%d, dummy3=%d \n",
+ dummy1, dummy2, dummy3);
+
+ /* fill dark_average */
+ for (x = 0; x < pixels_per_line; x++)
+ {
+ dev->dark_average_data[channels * 2 * x] = dummy1 & 0xff;
+ dev->dark_average_data[channels * 2 * x + 1] = dummy1 >> 8;
+ if (channels > 1)
+ {
+ dev->dark_average_data[channels * 2 * x + 2] = dummy2 & 0xff;
+ dev->dark_average_data[channels * 2 * x + 3] = dummy2 >> 8;
+ dev->dark_average_data[channels * 2 * x + 4] = dummy3 & 0xff;
+ dev->dark_average_data[channels * 2 * x + 5] = dummy3 >> 8;
+ }
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+genesys_white_shading_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ size_t size;
+ uint32_t pixels_per_line;
+ uint8_t *calibration_data;
+ uint8_t channels;
+ SANE_Bool motor;
+
+ DBG (DBG_proc, "genesys_white_shading_calibration (lines = %d)\n",
+ (unsigned int)dev->calib_lines);
+
+ pixels_per_line = dev->calib_pixels;
+ channels = dev->calib_channels;
+
+ if (dev->white_average_data)
+ free (dev->white_average_data);
+
+ dev->white_average_data = malloc (channels * 2 * pixels_per_line);
+ if (!dev->white_average_data)
+ {
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: failed to allocate average memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ size = channels * 2 * pixels_per_line * (dev->calib_lines + 1);
+
+ calibration_data = malloc (size);
+ if (!calibration_data)
+ {
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: failed to allocate calibration memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ motor=SANE_TRUE;
+ if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
+ {
+ motor=SANE_FALSE;
+ }
+
+ /* turn on motor and lamp power */
+ dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_TRUE);
+ dev->model->cmd_set->set_motor_power (dev->calib_reg, motor);
+ if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK)
+ {
+ status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
+ }
+
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg,
+ dev->model->
+ cmd_set->bulk_full_size ());
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
+ usleep (500 * 1000); /* wait 500ms to make sure lamp is bright again */
+
+ status = dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: Failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_read_data_from_scanner (dev, calibration_data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("white_shading.pnm", calibration_data, 16,
+ channels, pixels_per_line,
+ dev->calib_lines);
+
+ genesys_average_data (dev->white_average_data, calibration_data,
+ dev->calib_lines,
+ pixels_per_line * channels);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("white_average.pnm",
+ dev->white_average_data, 16, channels,
+ pixels_per_line, 1);
+
+ free (calibration_data);
+
+ /* in case we haven't done dark calibration, build dummy data from white_average */
+ if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION))
+ {
+ status = genesys_dummy_dark_shading (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_white_shading_calibration: failed to do dummy dark shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK)
+ {
+ status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
+ }
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+/* This calibration uses a scan over the calibration target, comprising a
+ * black and a white strip. (So the motor must be on.)
+ */
+static SANE_Status
+genesys_dark_white_shading_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ size_t size;
+ uint32_t pixels_per_line;
+ uint8_t *calibration_data, *average_white, *average_dark;
+ uint8_t channels;
+ unsigned int x;
+ int y;
+ uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col,
+ dif;
+ SANE_Bool motor;
+
+
+ DBG (DBG_proc, "genesys_black_white_shading_calibration (lines = %d)\n",
+ (unsigned int)dev->calib_lines);
+
+ pixels_per_line = dev->calib_pixels;
+ channels = dev->calib_channels;
+
+ if (dev->white_average_data)
+ free (dev->white_average_data);
+
+ dev->average_size = channels * 2 * pixels_per_line;
+
+ dev->white_average_data = malloc (dev->average_size);
+ if (!dev->white_average_data)
+ {
+ DBG (DBG_error,
+ "genesys_dark_white_shading_calibration: failed to allocate average memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (dev->dark_average_data)
+ free (dev->dark_average_data);
+
+ dev->dark_average_data = malloc (channels * 2 * pixels_per_line);
+ if (!dev->dark_average_data)
+ {
+ DBG (DBG_error,
+ "genesys_dark_white_shading_shading_calibration: failed to allocate average memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ size = channels * 2 * pixels_per_line * dev->calib_lines;
+
+ calibration_data = malloc (size);
+ if (!calibration_data)
+ {
+ DBG (DBG_error,
+ "genesys_dark_white_shading_calibration: failed to allocate calibration memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ motor=SANE_TRUE;
+ if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
+ {
+ motor=SANE_FALSE;
+ }
+
+ /* turn on motor and lamp power */
+ dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_TRUE);
+ dev->model->cmd_set->set_motor_power (dev->calib_reg, motor);
+
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg,
+ dev->model->
+ cmd_set->bulk_full_size ());
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_white_shading_calibration: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_white_shading_calibration: Failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_read_data_from_scanner (dev, calibration_data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_white_shading_calibration: Failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (calibration_data);
+ DBG (DBG_error,
+ "genesys_dark_white_shading_calibration: Failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("black_white_shading.pnm", calibration_data,
+ 16, channels, pixels_per_line,
+ dev->calib_lines);
+
+
+ average_white = dev->white_average_data;
+ average_dark = dev->dark_average_data;
+
+ for (x = 0; x < pixels_per_line * channels; x++)
+ {
+ dark = 0xffff;
+ white = 0;
+
+ for (y = 0; y < (int)dev->calib_lines; y++)
+ {
+ col = calibration_data[(x + y * pixels_per_line * channels) * 2];
+ col |=
+ calibration_data[(x + y * pixels_per_line * channels) * 2 +
+ 1] << 8;
+
+ if (col > white)
+ white = col;
+ if (col < dark)
+ dark = col;
+ }
+
+ dif = white - dark;
+
+ dark = dark + dif / 8;
+ white = white - dif / 8;
+
+ dark_count = 0;
+ dark_sum = 0;
+
+ white_count = 0;
+ white_sum = 0;
+
+ for (y = 0; y < (int)dev->calib_lines; y++)
+ {
+ col = calibration_data[(x + y * pixels_per_line * channels) * 2];
+ col |=
+ calibration_data[(x + y * pixels_per_line * channels) * 2 +
+ 1] << 8;
+
+ if (col >= white)
+ {
+ white_sum += col;
+ white_count++;
+ }
+ if (col <= dark)
+ {
+ dark_sum += col;
+ dark_count++;
+ }
+
+ }
+
+ dark_sum /= dark_count;
+ white_sum /= white_count;
+
+ *average_dark++ = dark_sum & 255;
+ *average_dark++ = dark_sum >> 8;
+
+ *average_white++ = white_sum & 255;
+ *average_white++ = white_sum >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file ("white_average.pnm",
+ dev->white_average_data, 16, channels,
+ pixels_per_line, 1);
+ sanei_genesys_write_pnm_file ("dark_average.pnm",
+ dev->dark_average_data, 16, channels,
+ pixels_per_line, 1);
+ }
+
+ free (calibration_data);
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* computes one coefficient given bright-dark value
+ * @param coeff factor giving 1.00 gain
+ * @param target desired target code
+ * @param value brght-dark value
+ * */
+static unsigned int
+compute_coefficient (unsigned int coeff, unsigned int target, unsigned int value)
+{
+ int result;
+
+ if (value > 0)
+ {
+ result = (coeff * target) / value;
+ if (result >= 65535)
+ {
+ result = 65535;
+ }
+ }
+ else
+ {
+ result = coeff;
+ }
+ return result;
+}
+
+/** @brief compute shading coefficients for LiDE scanners
+ * The dark/white shading is actually performed _after_ reducing
+ * resolution via averaging. only dark/white shading data for what would be
+ * first pixel at full resolution is used.
+ *
+ * scanner raw input to output value calculation:
+ * o=(i-off)*(gain/coeff)
+ *
+ * from datasheet:
+ * off=dark_average
+ * gain=coeff*bright_target/(bright_average-dark_average)
+ * works for dark_target==0
+ *
+ * what we want is these:
+ * bright_target=(bright_average-off)*(gain/coeff)
+ * dark_target=(dark_average-off)*(gain/coeff)
+ * leading to
+ * off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target)
+ * gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff
+ *
+ * @param dev scanner's device
+ * @param shading_data memory area where to store the computed shading coefficients
+ * @param pixels_per_line number of pixels per line
+ * @param words_per_color memory words per color channel
+ * @param channels number of color channels (actually 1 or 3)
+ * @param o shading coefficients left offset
+ * @param coeff 4000h or 2000h depending on fast scan mode or not (GAIN4 bit)
+ * @param target_bright value of the white target code
+ * @param target_dark value of the black target code
+*/
+#ifndef UNIT_TESTING
+static
+#endif
+void
+compute_averaged_planar (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int pixels_per_line,
+ unsigned int words_per_color,
+ unsigned int channels,
+ unsigned int o,
+ unsigned int coeff,
+ unsigned int target_bright,
+ unsigned int target_dark)
+{
+ unsigned int x, i, j, br, dk, res, avgpixels, basepixels, val;
+
+ DBG (DBG_info, "%s: pixels=%d, offset=%d\n", __FUNCTION__, pixels_per_line,
+ o);
+ /* initialize result */
+ memset (shading_data, 0xff, words_per_color * 3 * 2);
+
+ /*
+ strangely i can write 0x20000 bytes beginning at 0x00000 without overwriting
+ slope tables - which begin at address 0x10000(for 1200dpi hw mode):
+ memory is organized in words(2 bytes) instead of single bytes. explains
+ quite some things
+ */
+/*
+ another one: the dark/white shading is actually performed _after_ reducing
+ resolution via averaging. only dark/white shading data for what would be
+ first pixel at full resolution is used.
+ */
+/*
+ scanner raw input to output value calculation:
+ o=(i-off)*(gain/coeff)
+
+ from datasheet:
+ off=dark_average
+ gain=coeff*bright_target/(bright_average-dark_average)
+ works for dark_target==0
+
+ what we want is these:
+ bright_target=(bright_average-off)*(gain/coeff)
+ dark_target=(dark_average-off)*(gain/coeff)
+ leading to
+ off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target)
+ gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff
+ */
+ /* duplicate half-ccd logic */
+ res = dev->settings.xres;
+ if ((dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE) &&
+ dev->settings.xres <= dev->sensor.optical_res / 2)
+ res *= 2; /* scanner is using half-ccd mode */
+ /*this should be evenly dividable */
+ basepixels = dev->sensor.optical_res / res;
+
+/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
+ if (basepixels < 1)
+ avgpixels = 1;
+ else if (basepixels < 6)
+ avgpixels = basepixels;
+ else if (basepixels < 8)
+ avgpixels = 6;
+ else if (basepixels < 10)
+ avgpixels = 8;
+ else if (basepixels < 12)
+ avgpixels = 10;
+ else if (basepixels < 15)
+ avgpixels = 12;
+ else
+ avgpixels = 15;
+
+ DBG (DBG_info, "%s: averaging over %d pixels\n", __FUNCTION__, avgpixels);
+
+ for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels)
+ {
+
+ if ((x + o) * 2 * 2 + 3 > words_per_color * 2)
+ break;
+
+ for (j = 0; j < channels; j++)
+ {
+
+ dk = 0;
+ br = 0;
+ for (i = 0; i < avgpixels; i++)
+ {
+ /* dark data */
+ dk +=
+ (dev->dark_average_data[(x + i +
+ pixels_per_line * j) *
+ 2] |
+ (dev->dark_average_data
+ [(x + i + pixels_per_line * j) * 2 + 1] << 8));
+
+ /* white data */
+ br +=
+ (dev->white_average_data[(x + i +
+ pixels_per_line * j) *
+ 2] |
+ (dev->white_average_data
+ [(x + i + pixels_per_line * j) * 2 + 1] << 8));
+ }
+
+ br /= avgpixels;
+ dk /= avgpixels;
+
+ if (br * target_dark > dk * target_bright)
+ val = 0;
+ else if (dk * target_bright - br * target_dark >
+ 65535 * (target_bright - target_dark))
+ val = 65535;
+ else
+ val =
+ (dk * target_bright - br * target_dark) / (target_bright -
+ target_dark);
+
+/*fill all pixels, even if only the last one is relevant*/
+ for (i = 0; i < avgpixels; i++)
+ {
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j] = val & 0xff;
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j + 1] = val >> 8;
+ }
+
+ val = br - dk;
+
+ if (65535 * val > (target_bright - target_dark) * coeff)
+ val = (coeff * (target_bright - target_dark)) / val;
+ else
+ val = 65535;
+
+/*fill all pixels, even if only the last one is relevant*/
+ for (i = 0; i < avgpixels; i++)
+ {
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j + 2] = val & 0xff;
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j + 3] = val >> 8;
+ }
+ }
+
+/*fill remaining channels*/
+ for (j = channels; j < 3; j++)
+ {
+ for (i = 0; i < avgpixels; i++)
+ {
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j] =
+ shading_data[(x + o + i) * 2 * 2 + words_per_color * 0];
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j + 1] =
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * 0 + 1];
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j + 2] =
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * 0 + 2];
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * j + 3] =
+ shading_data[(x + o + i) * 2 * 2 +
+ words_per_color * 2 * 0 + 3];
+ }
+ }
+
+ }
+}
+
+/**
+ * Computes shading coefficient using formula in data sheet. 16bit data values
+ * manipulated here are little endian. For now we assume deletion scanning type
+ * and that there is always 3 channels.
+ * @param dev scanner's device
+ * @param shading_data memory area where to store the computed shading coefficients
+ * @param pixels_per_line number of pixels per line
+ * @param channels number of color channels (actually 1 or 3)
+ * @param cmat color transposition matrix
+ * @param offset shading coefficients left offset
+ * @param coeff 4000h or 2000h depending on fast scan mode or not
+ * @param target value of the target code
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+void
+compute_coefficients (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int pixels_per_line,
+ unsigned int channels,
+ unsigned int cmat[3],
+ int offset,
+ unsigned int coeff,
+ unsigned int target)
+{
+ uint8_t *ptr; /* contain 16bit words in little endian */
+ unsigned int x, c;
+ unsigned int val, br, dk;
+ unsigned int start, end;
+
+ DBG (DBG_io,
+ "compute_coefficients: pixels_per_line=%d, coeff=0x%04x\n", pixels_per_line, coeff);
+
+ /* compute start & end values depending of the offset */
+ if (offset < 0)
+ {
+ start = -1 * offset;
+ end = pixels_per_line;
+ }
+ else
+ {
+ start = 0;
+ end = pixels_per_line - offset;
+ }
+
+ for (c = 0; c < channels; c++)
+ {
+ for (x = start; x < end; x++)
+ {
+ /* TODO if channels=1 , use filter to know the base addr */
+ ptr = shading_data + 4 * ((x + offset) * channels + cmat[c]);
+
+ /* dark data */
+ dk = dev->dark_average_data[x * 2 * channels + c * 2];
+ dk += 256 * dev->dark_average_data[x * 2 * channels + c * 2 + 1];
+
+ /* white data */
+ br = dev->white_average_data[x * 2 * channels + c * 2];
+ br += 256 * dev->white_average_data[x * 2 * channels + c * 2 + 1];
+
+ /* compute coeff */
+ val=compute_coefficient(coeff,target,br-dk);
+
+ /* assign it */
+ ptr[0] = dk & 255;
+ ptr[1] = dk / 256;
+ ptr[2] = val & 0xff;
+ ptr[3] = val / 256;
+
+ }
+ }
+}
+
+/**
+ * Computes shading coefficient using formula in data sheet. 16bit data values
+ * manipulated here are little endian. Data is in planar form, ie grouped by
+ * lines of the same color component.
+ * @param dev scanner's device
+ * @param shading_data memory area where to store the computed shading coefficients
+ * @param factor averaging factor when the calibration scan is done at a higher resolution
+ * than the final scan
+ * @param pixels_per_line number of pixels per line
+ * @param words_per_color total number of shading data words for one color element
+ * @param channels number of color channels (actually 1 or 3)
+ * @param cmat transcoding matrix for color channel order
+ * @param offset shading coefficients left offset
+ * @param coeff 4000h or 2000h depending on fast scan mode or not
+ * @param target white target value
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+void
+compute_planar_coefficients (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int factor,
+ unsigned int pixels_per_line,
+ unsigned int words_per_color,
+ unsigned int channels,
+ unsigned int cmat[3],
+ unsigned int offset,
+ unsigned int coeff,
+ unsigned int target)
+{
+ uint8_t *ptr; /* contains 16bit words in little endian */
+ uint32_t x, c, i;
+ uint32_t val, dk, br;
+
+ DBG (DBG_io,
+ "compute_planar_coefficients: factor=%d, pixels_per_line=%d, words=0x%X, coeff=0x%04x\n", factor,
+ pixels_per_line, words_per_color, coeff);
+ for (c = 0; c < channels; c++)
+ {
+ /* shading data is larger than pixels_per_line so offset can be neglected */
+ for (x = 0; x < pixels_per_line; x+=factor)
+ {
+ /* x2 because of 16 bit values, and x2 since one coeff for dark
+ * and another for white */
+ ptr = shading_data + words_per_color * cmat[c] * 2 + (x + offset) * 4;
+
+ dk = 0;
+ br = 0;
+
+ /* average case */
+ for(i=0;i<factor;i++)
+ {
+ dk +=
+ 256 * dev->dark_average_data[((x+i) + pixels_per_line * c) * 2 + 1];
+ dk += dev->dark_average_data[((x+i) + pixels_per_line * c) * 2];
+ br +=
+ 256 * dev->white_average_data[((x+i) + pixels_per_line * c) * 2 + 1];
+ br += dev->white_average_data[((x+i) + pixels_per_line * c) * 2];
+ }
+ dk /= factor;
+ br /= factor;
+
+ val = compute_coefficient (coeff, target, br - dk);
+
+ /* we duplicate the information to have calibration data at optical resolution */
+ for (i = 0; i < factor; i++)
+ {
+ ptr[0 + 4 * i] = dk & 255;
+ ptr[1 + 4 * i] = dk / 256;
+ ptr[2 + 4 * i] = val & 0xff;
+ ptr[3 + 4 * i] = val / 256;
+ }
+ }
+ }
+ /* in case of gray level scan, we duplicate shading information on all
+ * three color channels */
+ if(channels==1)
+ {
+ memcpy(shading_data+cmat[1]*2*words_per_color,
+ shading_data+cmat[0]*2*words_per_color,
+ words_per_color*2);
+ memcpy(shading_data+cmat[2]*2*words_per_color,
+ shading_data+cmat[0]*2*words_per_color,
+ words_per_color*2);
+ }
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+void
+compute_shifted_coefficients (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int pixels_per_line,
+ unsigned int channels,
+ unsigned int cmat[3],
+ int offset,
+ unsigned int coeff,
+ unsigned int target_dark,
+ unsigned int target_bright,
+ unsigned int patch_size) /* contigous extent */
+{
+ unsigned int x, avgpixels, basepixels, i, j, val1, val2;
+ unsigned int br_tmp [3], dk_tmp [3];
+ uint8_t *ptr = shading_data + offset * 3 * 4; /* contain 16bit words in little endian */
+ unsigned int patch_cnt = offset * 3; /* at start, offset of first patch */
+
+ x = dev->settings.xres;
+ if ((dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE) &&
+ (dev->settings.xres <= dev->sensor.optical_res / 2))
+ x *= 2; /* scanner is using half-ccd mode */
+ basepixels = dev->sensor.optical_res / x; /*this should be evenly dividable */
+
+ /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
+ if (basepixels < 1)
+ avgpixels = 1;
+ else if (basepixels < 6)
+ avgpixels = basepixels;
+ else if (basepixels < 8)
+ avgpixels = 6;
+ else if (basepixels < 10)
+ avgpixels = 8;
+ else if (basepixels < 12)
+ avgpixels = 10;
+ else if (basepixels < 15)
+ avgpixels = 12;
+ else
+ avgpixels = 15;
+ DBG (DBG_info, "compute_shifted_coefficients: pixels_per_line=%d, coeff=0x%04x, averaging over %d pixels\n", pixels_per_line, coeff, avgpixels);
+
+ for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) {
+ memset (&br_tmp, 0, sizeof(br_tmp));
+ memset (&dk_tmp, 0, sizeof(dk_tmp));
+
+ for (i = 0; i < avgpixels; i++) {
+ for (j = 0; j < channels; j++) {
+ br_tmp[j] += (dev->white_average_data[((x + i) * channels + j) * 2] |
+ (dev->white_average_data[((x + i) * channels + j) * 2 + 1] << 8));
+ dk_tmp[i] += (dev->dark_average_data[((x + i) * channels + j) * 2] |
+ (dev->dark_average_data[((x + i) * channels + j) * 2 + 1] << 8));
+ }
+ }
+ for (j = 0; j < channels; j++) {
+ br_tmp[j] /= avgpixels;
+ dk_tmp[j] /= avgpixels;
+
+ if (br_tmp[j] * target_dark > dk_tmp[j] * target_bright)
+ val1 = 0;
+ else if (dk_tmp[j] * target_bright - br_tmp[j] * target_dark > 65535 * (target_bright - target_dark))
+ val1 = 65535;
+ else
+ val1 = (dk_tmp[j] * target_bright - br_tmp[j] * target_dark) / (target_bright - target_dark);
+
+ val2 = br_tmp[j] - dk_tmp[j];
+ if (65535 * val2 > (target_bright - target_dark) * coeff)
+ val2 = (coeff * (target_bright - target_dark)) / val2;
+ else
+ val2 = 65535;
+
+ br_tmp[j] = val1;
+ dk_tmp[j] = val2;
+ }
+ for (i = 0; i < avgpixels; i++) {
+ for (j = 0; j < channels; j++) {
+ * ptr++ = br_tmp[ cmat[j] ] & 0xff;
+ * ptr++ = br_tmp[ cmat[j] ] >> 8;
+ * ptr++ = dk_tmp[ cmat[j] ] & 0xff;
+ * ptr++ = dk_tmp[ cmat[j] ] >> 8;
+ patch_cnt++;
+ if (patch_cnt == patch_size) {
+ patch_cnt = 0;
+ val1 = cmat[2];
+ cmat[2] = cmat[1];
+ cmat[1] = cmat[0];
+ cmat[0] = val1;
+ }
+ }
+ }
+ }
+}
+
+static SANE_Status
+genesys_send_shading_coefficient (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint32_t pixels_per_line;
+ uint8_t *shading_data; /**> contains 16bit words in little endian */
+ uint8_t channels;
+ int o;
+ unsigned int length; /**> number of shading calibration data words */
+ unsigned int factor;
+ unsigned int cmat[3]; /**> matrix of color channels */
+ unsigned int coeff, target_code, words_per_color = 0;
+
+ DBGSTART;
+
+ pixels_per_line = dev->calib_pixels;
+ channels = dev->calib_channels;
+
+ /* we always build data for three channels, even for gray
+ * we make the shading data such that each color channel data line is contiguous
+ * to the next one, which allow to write the 3 channels in 1 write
+ * during genesys_send_shading_coefficient, some values are words, other bytes
+ * hence the x2 factor */
+ switch (sanei_genesys_read_reg_from_set (dev->reg, 0x05) >> 6)
+ {
+ /* 600 dpi */
+ case 0:
+ words_per_color = 0x2a00;
+ break;
+ /* 1200 dpi */
+ case 1:
+ words_per_color = 0x5500;
+ break;
+ /* 2400 dpi */
+ case 2:
+ words_per_color = 0xa800;
+ break;
+ /* 4800 dpi */
+ case 3:
+ words_per_color = 0x15000;
+ break;
+ }
+
+ length = words_per_color * 3 * 2;
+ /* allocate computed size */
+ shading_data = malloc (length);
+ if (!shading_data)
+ {
+ DBG (DBG_error,
+ "genesys_send_shading_coefficient: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (shading_data, 0, length);
+
+ /* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000
+ or 0x4000 to give an integer
+ Wn = white average for column n
+ Dn = dark average for column n
+ */
+ if (dev->model->cmd_set->get_gain4_bit (dev->calib_reg))
+ coeff = 0x4000;
+ else
+ coeff = 0x2000;
+
+ /* compute avg factor */
+ if(dev->settings.xres>dev->sensor.optical_res)
+ {
+ factor=1;
+ }
+ else
+ {
+ factor=dev->sensor.optical_res/dev->settings.xres;
+ }
+
+ /* for GL646, shading data is planar if REG01_FASTMOD is set and
+ * chunky if not. For now we rely on the fact that we know that
+ * each sensor is used only in one mode. Currently only the CIS_XP200
+ * sets REG01_FASTMOD.
+ */
+
+ /* TODO setup a struct in genesys_devices that
+ * will handle these settings instead of having this switch growing up */
+ cmat[0] = 0;
+ cmat[1] = 1;
+ cmat[2] = 2;
+ switch (dev->model->ccd_type)
+ {
+ case CCD_XP300:
+ case CCD_ROADWARRIOR:
+ case CCD_DP665:
+ case CCD_DP685:
+ case CCD_DSMOBILE600:
+ target_code = 0xdc00;
+ o = 4;
+ compute_planar_coefficients (dev,
+ shading_data,
+ factor,
+ pixels_per_line,
+ words_per_color,
+ channels,
+ cmat,
+ o,
+ coeff,
+ target_code);
+ break;
+ case CIS_XP200:
+ target_code = 0xdc00;
+ o = 2;
+ cmat[0] = 2; /* red is last */
+ cmat[1] = 0; /* green is first */
+ cmat[2] = 1; /* blue is second */
+ compute_planar_coefficients (dev,
+ shading_data,
+ 1,
+ pixels_per_line,
+ words_per_color,
+ channels,
+ cmat,
+ o,
+ coeff,
+ target_code);
+ break;
+ case CCD_HP2300:
+ target_code = 0xdc00;
+ o = 2;
+ if(dev->settings.xres<=dev->sensor.optical_res/2)
+ {
+ o = o - dev->sensor.dummy_pixel / 2;
+ }
+ compute_coefficients (dev,
+ shading_data,
+ pixels_per_line,
+ 3,
+ cmat,
+ o,
+ coeff,
+ target_code);
+ break;
+ case CCD_5345:
+ target_code = 0xe000;
+ o = 4;
+ if(dev->settings.xres<=dev->sensor.optical_res/2)
+ {
+ o = o - dev->sensor.dummy_pixel;
+ }
+ compute_coefficients (dev,
+ shading_data,
+ pixels_per_line,
+ 3,
+ cmat,
+ o,
+ coeff,
+ target_code);
+ break;
+ case CCD_HP3670:
+ case CCD_HP2400:
+ target_code = 0xe000;
+ /* offset is cksel dependent, but we can't use this in common code */
+ if(dev->settings.xres<=300)
+ {
+ o = -10; /* OK for <=300 */
+ }
+ else if(dev->settings.xres<=600)
+ {
+ o = -6; /* ok at 600 */
+ }
+ else
+ {
+ o = +2;
+ }
+ compute_coefficients (dev,
+ shading_data,
+ pixels_per_line,
+ 3,
+ cmat,
+ o,
+ coeff,
+ target_code);
+ break;
+ case CCD_KVSS080:
+ case CCD_PLUSTEK3800:
+ case CCD_G4050:
+ case CCD_CS4400F:
+ case CCD_CS8400F:
+ target_code = 0xe000;
+ o = 0;
+ compute_coefficients (dev,
+ shading_data,
+ pixels_per_line,
+ 3,
+ cmat,
+ o,
+ coeff,
+ target_code);
+ break;
+ case CIS_CANONLIDE700:
+ case CIS_CANONLIDE100:
+ case CIS_CANONLIDE200:
+ case CIS_CANONLIDE110:
+ case CIS_CANONLIDE210:
+ /* TODO store this in a data struct so we avoid
+ * growing this switch */
+ if(dev->model->ccd_type!=CIS_CANONLIDE110
+ && dev->model->ccd_type!=CIS_CANONLIDE210)
+ target_code=0xdc00;
+ else
+ target_code=0xf000;
+ words_per_color=pixels_per_line*2;
+ length = words_per_color * 3 * 2;
+ free(shading_data);
+ shading_data = malloc (length);
+ if (!shading_data)
+ {
+ DBG (DBG_error,
+ "genesys_send_shading_coefficient: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (shading_data, 0, length);
+ compute_planar_coefficients (dev,
+ shading_data,
+ 1,
+ pixels_per_line,
+ words_per_color,
+ channels,
+ cmat,
+ 0,
+ coeff,
+ target_code);
+ break;
+ case CCD_CANONLIDE35:
+ compute_averaged_planar (dev,
+ shading_data,
+ pixels_per_line,
+ words_per_color,
+ channels,
+ 4,
+ coeff,
+ 0xfa00,
+ 0x0a00);
+ break;
+ case CCD_PLUSTEK_3600:
+ compute_shifted_coefficients (dev,
+ shading_data,
+ pixels_per_line,
+ channels,
+ cmat,
+ 12, /* offset */
+ coeff,
+ 0x0001, /* target_dark */
+ 0xf900, /* target_bright */
+ 256); /* patch_size: contigous extent */
+ break;
+ default:
+ DBG (DBG_error,
+ "genesys_send_shading_coefficient: sensor %d not supported\n",
+ dev->model->ccd_type);
+ return SANE_STATUS_UNSUPPORTED;
+ break;
+ }
+
+ /* do the actual write of shading calibration data to the scanner */
+ status = genesys_send_offset_and_shading (dev, shading_data, length);
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error,
+ "genesys_send_shading_coefficient: failed to send shading data: %s\n",
+ sane_strstatus (status));
+
+ free (shading_data);
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * search calibration cache list for an entry matching required scan.
+ * If one is found, set device calibration with it
+ * @param dev scanner's device
+ * @return SANE_STATUS_UNSUPPORTED if no matching cache entry has been
+ * found, SANE_STATUS_GOOD if one has been found and used.
+ */
+static SANE_Status
+genesys_restore_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ Genesys_Calibration_Cache *cache;
+
+ DBGSTART;
+
+ /* if no cache or no function to evaluate cache entry ther can be no match */
+ if (!dev->model->cmd_set->is_compatible_calibration
+ || dev->calibration_cache == NULL)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* we walk the link list of calibration cache in search for a
+ * matching one */
+ for (cache = dev->calibration_cache; cache; cache = cache->next)
+ {
+ status = dev->model->cmd_set->is_compatible_calibration (dev, cache, SANE_FALSE);
+ /* SANE_STATUS_GOOD, a matching cache has been found
+ * so we use it to populate calibration data
+ */
+ if (status == SANE_STATUS_GOOD)
+ {
+ memcpy (&dev->frontend, &cache->frontend, sizeof (dev->frontend));
+ /* we don't restore the gamma fields */
+ memcpy (dev->sensor.regs_0x10_0x1d, cache->sensor.regs_0x10_0x1d, 6);
+ free (dev->dark_average_data);
+ free (dev->white_average_data);
+
+ dev->average_size = cache->average_size;
+ dev->calib_pixels = cache->calib_pixels;
+ dev->calib_channels = cache->calib_channels;
+
+ dev->dark_average_data = (uint8_t *) malloc (cache->average_size);
+ dev->white_average_data = (uint8_t *) malloc (cache->average_size);
+
+ if (!dev->dark_average_data || !dev->white_average_data)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy (dev->dark_average_data,
+ cache->dark_average_data, dev->average_size);
+ memcpy (dev->white_average_data,
+ cache->white_average_data, dev->average_size);
+
+
+ if(dev->model->cmd_set->send_shading_data==NULL)
+ {
+ status = genesys_send_shading_coefficient (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_restore_calibration: failed to send shading calibration coefficients: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (DBG_proc, "genesys_restore_calibration: restored\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* here status is either SANE_STATUS_UNSUPPORTED which mean tested cache
+ * entry doesn't match, or an fatal error */
+ if (status != SANE_STATUS_UNSUPPORTED)
+ {
+ DBG (DBG_error,
+ "genesys_restore_calibration: fail while checking compatibility: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ DBG (DBG_proc, "genesys_restore_calibration: completed(nothing found)\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+static SANE_Status
+genesys_save_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_UNSUPPORTED;
+ Genesys_Calibration_Cache *cache = NULL;
+#ifdef HAVE_SYS_TIME_H
+ struct timeval time;
+#endif
+
+ DBGSTART;
+
+ if (!dev->model->cmd_set->is_compatible_calibration)
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (dev->calibration_cache != NULL)
+ {
+ for (cache = dev->calibration_cache; cache; cache = cache->next)
+ {
+ status = dev->model->cmd_set->is_compatible_calibration (dev, cache, SANE_TRUE);
+ if (status == SANE_STATUS_UNSUPPORTED)
+ {
+ continue;
+ }
+ else if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_save_calibration: fail while checking compatibility: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ break;
+ }
+ }
+
+ /* if we found on overridable cache, we reuse it */
+ if (cache)
+ {
+ free(cache->dark_average_data);
+ free(cache->white_average_data);
+ }
+ else
+ {
+ /* create a new cache entry and insert it in the linked list */
+ cache = malloc (sizeof (Genesys_Calibration_Cache));
+ if (!cache)
+ return SANE_STATUS_NO_MEM;
+
+ memset (cache, 0, sizeof (Genesys_Calibration_Cache));
+
+ cache->next = dev->calibration_cache;
+ dev->calibration_cache = cache;
+ }
+
+ cache->average_size = dev->average_size;
+
+ cache->dark_average_data = (uint8_t *) malloc (cache->average_size);
+ if (!cache->dark_average_data)
+ return SANE_STATUS_NO_MEM;
+ cache->white_average_data = (uint8_t *) malloc (cache->average_size);
+ if (!cache->white_average_data)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy (&cache->used_setup, &dev->current_setup, sizeof (cache->used_setup));
+ memcpy (&cache->frontend, &dev->frontend, sizeof (cache->frontend));
+ memcpy (&cache->sensor, &dev->sensor, sizeof (cache->sensor));
+
+ cache->calib_pixels = dev->calib_pixels;
+ cache->calib_channels = dev->calib_channels;
+ memcpy (cache->dark_average_data, dev->dark_average_data, cache->average_size);
+ memcpy (cache->white_average_data, dev->white_average_data, cache->average_size);
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday(&time,NULL);
+ cache->last_calibration = time.tv_sec;
+#endif
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * does the calibration process for a flatbed scanner
+ * - offset calibration
+ * - gain calibration
+ * - shading calibration
+ * @param dev device to calibrate
+ * @return SANE_STATUS_GOOD if everything when all right, else the error code.
+ */
+static SANE_Status
+genesys_flatbed_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint32_t pixels_per_line;
+ int yres;
+
+ DBG (DBG_info, "genesys_flatbed_calibration\n");
+
+ yres = dev->sensor.optical_res;
+ if (dev->settings.yres <= dev->sensor.optical_res / 2)
+ yres /= 2;
+
+ /* do offset calibration if needed */
+ if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
+ {
+ status = dev->model->cmd_set->offset_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: offset calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* since all the registers are set up correctly, just use them */
+ status = dev->model->cmd_set->coarse_gain_calibration (dev, yres);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: coarse gain calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ }
+ else
+ /* since we have 2 gain calibration proc, skip second if first one was
+ used. */
+ {
+ status = dev->model->cmd_set->init_regs_for_coarse_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to send calibration registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = genesys_coarse_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to do coarse gain calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ }
+
+ if (dev->model->is_cis)
+ {
+ /* the afe now sends valid data for doing led calibration */
+ status = dev->model->cmd_set->led_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: led calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* calibrate afe again to match new exposure */
+ if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
+ {
+ status = dev->model->cmd_set->offset_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: offset calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* since all the registers are set up correctly, just use them */
+
+ status = dev->model->cmd_set->coarse_gain_calibration (dev, yres);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: coarse gain calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else
+ /* since we have 2 gain calibration proc, skip second if first one was
+ used. */
+ {
+ status =
+ dev->model->cmd_set->init_regs_for_coarse_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to send calibration registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = genesys_coarse_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to do static calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ }
+
+ /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */
+ if (!(dev->model->flags & GENESYS_FLAG_SIS_SENSOR))
+ {
+ pixels_per_line = (SANE_UNFIX (dev->model->x_size) * dev->settings.xres) / MM_PER_INCH;
+ }
+ else
+ {
+ pixels_per_line = dev->sensor.sensor_pixels;
+ }
+
+ /* send default shading data */
+ status = sanei_genesys_init_shading_data (dev, pixels_per_line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to init shading process: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* shading calibration */
+ status = dev->model->cmd_set->init_regs_for_shading (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "genesys_flatbed_calibration: failed to send shading "
+ "registers: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION)
+ {
+ status = genesys_dark_white_shading_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to do dark+white shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else
+ {
+ if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
+ {
+ status = genesys_dark_shading_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to do dark shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = genesys_white_shading_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to do white shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if(dev->model->cmd_set->send_shading_data==NULL)
+ {
+ status = genesys_send_shading_coefficient (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_flatbed_calibration: failed to send shading calibration coefficients: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (DBG_info, "genesys_flatbed_calibration: completed\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Does the calibration process for a sheetfed scanner
+ * - offset calibration
+ * - gain calibration
+ * - shading calibration
+ * During calibration a predefined calibration sheet with specific black and white
+ * areas is used.
+ * @param dev device to calibrate
+ * @return SANE_STATUS_GOOD if everything when all right, else the error code.
+ */
+static SANE_Status
+genesys_sheetfed_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool forward = SANE_TRUE;
+ int xres;
+
+ DBGSTART;
+ if (dev->model->cmd_set->search_strip == NULL)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: no strip searching function available\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* first step, load document */
+ status = dev->model->cmd_set->load_document (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to load document: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+
+ DBG (DBG_info, "genesys_sheetfed_calibration\n");
+
+ /* led, offset and gain calibration are influenced by scan
+ * settings. So we set it to sensor resolution */
+ xres = dev->sensor.optical_res;
+ dev->settings.xres = dev->sensor.optical_res;
+ /* XP200 needs to calibrate a full and half sensor's resolution */
+ if (dev->model->ccd_type == CIS_XP200
+ && dev->settings.xres <= dev->sensor.optical_res / 2)
+ dev->settings.xres /= 2;
+
+ /* the afe needs to sends valid data even before calibration */
+
+ /* go to a white area */
+ status = dev->model->cmd_set->search_strip (dev, forward, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to find white strip: %s\n",
+ sane_strstatus (status));
+ dev->model->cmd_set->eject_document (dev);
+ return status;
+ }
+
+ if (dev->model->is_cis)
+ {
+ status = dev->model->cmd_set->led_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: led calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* calibrate afe */
+ if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
+ {
+ status = dev->model->cmd_set->offset_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: offset calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* since all the registers are set up correctly, just use them */
+
+ status = dev->model->cmd_set->coarse_gain_calibration (dev, xres);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: coarse gain calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else
+ /* since we have 2 gain calibration proc, skip second if first one was
+ used. */
+ {
+ status =
+ dev->model->cmd_set->init_regs_for_coarse_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to send calibration registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = genesys_coarse_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to do static calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* search for a full width black strip and then do a 16 bit scan to
+ * gather black shading data */
+ if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
+ {
+ /* seek black/white reverse/forward */
+ status = dev->model->cmd_set->search_strip (dev, forward, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to find black strip: %s\n",
+ sane_strstatus (status));
+ dev->model->cmd_set->eject_document (dev);
+ return status;
+ }
+
+ status = dev->model->cmd_set->init_regs_for_shading (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to do set up registers for shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = genesys_dark_shading_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ dev->model->cmd_set->eject_document (dev);
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to do dark shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ forward = SANE_FALSE;
+ }
+
+
+ /* go to a white area */
+ status = dev->model->cmd_set->search_strip (dev, forward, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to find white strip: %s\n",
+ sane_strstatus (status));
+ dev->model->cmd_set->eject_document (dev);
+ return status;
+ }
+
+ status = dev->model->cmd_set->init_regs_for_shading (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to do set up registers for shading calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = genesys_white_shading_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ dev->model->cmd_set->eject_document (dev);
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed eject target: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* in case we haven't black shading data, build it from black pixels
+ * of white calibration */
+ if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION))
+ {
+ FREE_IFNOT_NULL (dev->dark_average_data);
+ dev->dark_average_data = malloc (dev->average_size);
+ memset (dev->dark_average_data, 0x0f, dev->average_size);
+ /* XXX STEF XXX
+ * with black point in white shading, build an average black
+ * pixel and use it to fill the dark_average
+ * dev->calib_pixels
+ (dev->sensor.sensor_pixels * dev->settings.xres) / dev->sensor.optical_res,
+ dev->calib_lines,
+ */
+ }
+
+ /* send the shading coefficient when doing whole line shading
+ * but not when using SHDAREA like GL124 */
+ if(dev->model->cmd_set->send_shading_data==NULL)
+ {
+ status = genesys_send_shading_coefficient (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to send shading calibration coefficients: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* save the calibration data */
+ genesys_save_calibration (dev);
+
+ /* and finally eject calibration sheet */
+ status = dev->model->cmd_set->eject_document (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_sheetfed_calibration: failed to eject document: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* resotre settings */
+ dev->settings.xres = xres;
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * does the calibration process for a device
+ * @param dev device to calibrate
+ */
+static SANE_Status
+genesys_scanner_calibration (Genesys_Device * dev)
+{
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ return genesys_flatbed_calibration (dev);
+ }
+ return genesys_sheetfed_calibration (dev);
+}
+
+/* unused function kept in case it may be usefull in the futur */
+#if 0
+static SANE_Status
+genesys_wait_not_moving (Genesys_Device * dev, int mseconds)
+{
+ uint8_t value;
+ SANE_Status status;
+
+ DBG (DBG_proc,
+ "genesys_wait_not_moving: waiting %d mseconds for motor to stop\n",
+ mseconds);
+ while (mseconds > 0)
+ {
+ RIE (sanei_genesys_get_status (dev, &value));
+
+ if (dev->model->cmd_set->test_motor_flag_bit (value))
+ {
+ usleep (100 * 1000);
+ mseconds -= 100;
+ DBG (DBG_io,
+ "genesys_wait_not_moving: motor is moving, %d mseconds to go\n",
+ mseconds);
+ }
+ else
+ {
+ DBG (DBG_info,
+ "genesys_wait_not_moving: motor is not moving, exiting\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ }
+ DBG (DBG_error,
+ "genesys_wait_not_moving: motor is still moving, timeout exceeded\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+#endif
+
+
+/* ------------------------------------------------------------------------ */
+/* High level (exported) functions */
+/* ------------------------------------------------------------------------ */
+
+/*
+ * wait lamp to be warm enough by scanning the same line until
+ * differences between two scans are below a threshold
+ */
+static SANE_Status
+genesys_warmup_lamp (Genesys_Device * dev)
+{
+ uint8_t *first_line, *second_line;
+ int seconds = 0;
+ int pixel;
+ int channels, total_size;
+ double first_average = 0;
+ double second_average = 0;
+ int difference = 255;
+ int empty, lines = 3;
+ SANE_Status status = SANE_STATUS_IO_ERROR;
+
+ DBGSTART;
+
+ /* check if the current chipset implements warmup */
+ if(dev->model->cmd_set->init_regs_for_warmup==NULL)
+ {
+ DBG (DBG_error, "%s: init_regs_for_warmup not implemented\n", __FUNCTION__);
+ return status;
+ }
+
+ dev->model->cmd_set->init_regs_for_warmup (dev, dev->reg, &channels, &total_size);
+ first_line = malloc (total_size);
+ if (!first_line)
+ return SANE_STATUS_NO_MEM;
+
+ second_line = malloc (total_size);
+ if (!second_line)
+ {
+ free(first_line);
+ DBGCOMPLETED;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ do
+ {
+ DBG (DBG_info, "genesys_warmup_lamp: one more loop\n");
+ RIEF2 (dev->model->cmd_set->begin_scan (dev, dev->reg, SANE_FALSE), first_line, second_line);
+ do
+ {
+ sanei_genesys_test_buffer_empty (dev, &empty);
+ }
+ while (empty);
+
+ status = sanei_genesys_read_data_from_scanner (dev, first_line, total_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ RIEF2 (sanei_genesys_read_data_from_scanner
+ (dev, first_line, total_size), first_line, second_line);
+ }
+
+ RIEF2 (dev->model->cmd_set->end_scan (dev, dev->reg, SANE_TRUE), first_line, second_line);
+
+ sleep (1); /* sleep 1 s */
+ seconds++;
+
+ RIEF2 (dev->model->cmd_set->begin_scan (dev, dev->reg, SANE_FALSE), first_line, second_line);
+ do
+ {
+ sanei_genesys_test_buffer_empty (dev, &empty);
+ usleep (100 * 1000);
+ }
+ while (empty);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->end_scan (dev, dev->reg, SANE_TRUE), first_line, second_line);
+
+ /* compute difference between the two scans */
+ for (pixel = 0; pixel < total_size; pixel++)
+ {
+ /* 16 bit data */
+ if (dev->model->cmd_set->get_bitset_bit (dev->reg))
+ {
+ first_average += (first_line[pixel] + first_line[pixel + 1] * 256);
+ second_average += (second_line[pixel] + second_line[pixel + 1] * 256);
+ pixel++;
+ }
+ else
+ {
+ first_average += first_line[pixel];
+ second_average += second_line[pixel];
+ }
+ }
+ if (dev->model->cmd_set->get_bitset_bit (dev->reg))
+ {
+ first_average /= pixel;
+ second_average /= pixel;
+ difference = abs (first_average - second_average);
+ DBG (DBG_info,
+ "genesys_warmup_lamp: average = %.2f, diff = %.3f\n",
+ 100 * ((second_average) / (256 * 256)),
+ 100 * (difference / second_average));
+
+ if (second_average > (100 * 256)
+ && (difference / second_average) < 0.002)
+ break;
+ }
+ else
+ {
+ first_average /= pixel;
+ second_average /= pixel;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file ("warmup1.pnm", first_line, 8,
+ channels,
+ total_size / (lines * channels),
+ lines);
+ sanei_genesys_write_pnm_file ("warmup2.pnm", second_line, 8,
+ channels,
+ total_size / (lines * channels),
+ lines);
+ }
+ DBG (DBG_info, "genesys_warmup_lamp: average 1 = %.2f, average 2 = %.2f\n", first_average, second_average);
+ /* if delta below 15/255 ~= 5.8%, lamp is considred warm enough */
+ if (abs (first_average - second_average) < 15
+ && second_average > 55)
+ break;
+ }
+
+ /* sleep another second before next loop */
+ sleep (1);
+ seconds++;
+ }
+ while (seconds < WARMUP_TIME);
+
+ if (seconds >= WARMUP_TIME)
+ {
+ DBG (DBG_error,
+ "genesys_warmup_lamp: warmup timed out after %d seconds. Lamp defective?\n",
+ seconds);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ DBG (DBG_info,
+ "genesys_warmup_lamp: warmup succeeded after %d seconds\n",
+ seconds);
+ }
+
+ free (first_line);
+ free (second_line);
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+
+/* High-level start of scanning */
+static SANE_Status
+genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
+{
+ SANE_Status status;
+ unsigned int steps, expected;
+ SANE_Bool empty;
+
+ DBGSTART;
+
+ /* since not all scanners are set ot wait for head to park
+ * we check we are not still parking before starting a new scan */
+ if (dev->parking == SANE_TRUE)
+ {
+ status = sanei_genesys_wait_for_home (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to wait for head to park: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* disable power saving*/
+ status = dev->model->cmd_set->save_power (dev, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to disable power saving mode: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip
+ * it when scanning from XPA. */
+ if (!(dev->model->flags & GENESYS_FLAG_SKIP_WARMUP)
+ && (dev->settings.scan_method == SCAN_METHOD_FLATBED))
+ {
+ RIE (genesys_warmup_lamp (dev));
+ }
+
+ /* set top left x and y values by scanning the internals if flatbed scanners */
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ /* do the geometry detection only once */
+ if ((dev->model->flags & GENESYS_FLAG_SEARCH_START)
+ && (dev->model->y_offset_calib == 0))
+ {
+ status = dev->model->cmd_set->search_start_position (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to search start position: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->parking = SANE_FALSE;
+ status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to move scanhead to "
+ "home position: %s\n", sane_strstatus (status));
+ return status;
+ }
+ dev->scanhead_position_in_steps = 0;
+ }
+ else
+ {
+ /* Go home */
+ /* TODO: check we can drop this since we cannot have the
+ scanner's head wandering here */
+ dev->parking = SANE_FALSE;
+ status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to move scanhead to "
+ "home position: %s\n", sane_strstatus (status));
+ return status;
+ }
+ dev->scanhead_position_in_steps = 0;
+ }
+ }
+
+ /* move to calibration area for transparency adapter */
+ if ((dev->settings.scan_method == SCAN_METHOD_TRANSPARENCY)
+ && dev->model->cmd_set->move_to_ta != NULL)
+ {
+ status=dev->model->cmd_set->move_to_ta(dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to move to start of transparency adapter: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* load document if needed (for sheetfed scanner for instance) */
+ if (dev->model->is_sheetfed == SANE_TRUE
+ && dev->model->cmd_set->load_document != NULL)
+ {
+ status = dev->model->cmd_set->load_document (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to load document: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* send gamma tables. They have been set to device or user value
+ * when setting option value */
+ status = dev->model->cmd_set->send_gamma_table (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to init gamma table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* try to use cached calibration first */
+ status = genesys_restore_calibration (dev);
+ if (status == SANE_STATUS_UNSUPPORTED)
+ {
+ /* calibration : sheetfed scanners can't calibrate before each scan */
+ /* and also those who have the NO_CALIBRATION flag */
+ if (!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)
+ &&dev->model->is_sheetfed == SANE_FALSE)
+ {
+ status = genesys_scanner_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to do scanner calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ genesys_save_calibration (dev);
+ }
+ else
+ {
+ DBG (DBG_warn, "genesys_start_scan: no calibration done\n");
+ }
+ }
+ else if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to restore calibration: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* build look up table for dynamic lineart */
+ if(dev->settings.dynamic_lineart==SANE_TRUE)
+ {
+ status = sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205,
+ dev->settings.threshold_curve,
+ dev->settings.threshold-127);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "genesys_start_scan: failed to build lut\n");
+ return status;
+ }
+ }
+
+ status = dev->model->cmd_set->init_regs_for_scan (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to do init registers for scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* no lamp during scan */
+ if(lamp_off == SANE_TRUE)
+ {
+ dev->model->cmd_set->set_lamp_power (dev, dev->reg, SANE_FALSE);
+ }
+
+ /* GL124 is using SHDAREA, so we have to wait for scan to be set up before
+ * sending shading data */
+ if( (dev->model->cmd_set->send_shading_data!=NULL)
+ && !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ status = genesys_send_shading_coefficient (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to send shading calibration coefficients: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* now send registers for scan */
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg,
+ dev->model->
+ cmd_set->bulk_full_size ());
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to bulk write registers, status = %d\n",
+ status);
+ return status;
+ }
+
+ /* start effective scan */
+ status = dev->model->cmd_set->begin_scan (dev, dev->reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*do we really need this? the valid data check should be sufficent -- pierre*/
+ /* waits for head to reach scanning position */
+ expected = sanei_genesys_read_reg_from_set (dev->reg, 0x3d) * 65536
+ + sanei_genesys_read_reg_from_set (dev->reg, 0x3e) * 256
+ + sanei_genesys_read_reg_from_set (dev->reg, 0x3f);
+ do
+ {
+ /* wait 1/10th of second between each test to avoid
+ overloading USB and CPU */
+ usleep (100 * 1000);
+ status = sanei_genesys_read_feed_steps (dev, &steps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: Failed to read feed steps: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ while (steps < expected);
+
+ /* wait for buffers to be filled */
+ do
+ {
+ RIE (sanei_genesys_test_buffer_empty (dev, &empty));
+ }
+ while (empty);
+
+ /* when doing one or two-table movement, let the motor settle to scanning speed */
+ /* and scanning start before reading data */
+/* the valid data check already waits until the scanner delivers data. this here leads to unnecessary buffer full conditions in the scanner.
+ if (dev->model->cmd_set->get_fast_feed_bit (dev->reg))
+ usleep (1000 * 1000);
+ else
+ usleep (500 * 1000);
+*/
+ /* then we wait for at least one word of valid scan data
+
+ this is also done in sanei_genesys_read_data_from_scanner -- pierre */
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ do
+ {
+ usleep (100 * 1000);
+ status = sanei_genesys_read_valid_words (dev, &steps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_start_scan: failed to read valid words: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ while (steps < 1);
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* this is _not_ a ringbuffer.
+ if we need a block which does not fit at the end of our available data,
+ we move the available data to the beginning.
+ */
+
+SANE_Status
+sanei_genesys_buffer_alloc (Genesys_Buffer * buf, size_t size)
+{
+ buf->buffer = (SANE_Byte *) malloc (size);
+ if (!buf->buffer)
+ return SANE_STATUS_NO_MEM;
+ buf->avail = 0;
+ buf->pos = 0;
+ buf->size = size;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_genesys_buffer_free (Genesys_Buffer * buf)
+{
+ SANE_Byte *tmp = buf->buffer;
+ buf->avail = 0;
+ buf->size = 0;
+ buf->pos = 0;
+ buf->buffer = NULL;
+ if (tmp)
+ free (tmp);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Byte *
+sanei_genesys_buffer_get_write_pos (Genesys_Buffer * buf, size_t size)
+{
+ if (buf->avail + size > buf->size)
+ return NULL;
+ if (buf->pos + buf->avail + size > buf->size)
+ {
+ memmove (buf->buffer, buf->buffer + buf->pos, buf->avail);
+ buf->pos = 0;
+ }
+ return buf->buffer + buf->pos + buf->avail;
+}
+
+SANE_Byte *
+sanei_genesys_buffer_get_read_pos (Genesys_Buffer * buf)
+{
+ return buf->buffer + buf->pos;
+}
+
+SANE_Status
+sanei_genesys_buffer_produce (Genesys_Buffer * buf, size_t size)
+{
+ if (size > buf->size - buf->avail)
+ return SANE_STATUS_INVAL;
+ buf->avail += size;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_genesys_buffer_consume (Genesys_Buffer * buf, size_t size)
+{
+ if (size > buf->avail)
+ return SANE_STATUS_INVAL;
+ buf->avail -= size;
+ buf->pos += size;
+ return SANE_STATUS_GOOD;
+}
+
+
+#include "genesys_conv.c"
+
+static SANE_Status accurate_line_read(Genesys_Device * dev,
+ SANE_Byte *buffer,
+ size_t size)
+{
+ SANE_Status status;
+ status = dev->model->cmd_set->bulk_read_data (dev, 0x45, buffer, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "accurate_line_read: failed to read %lu bytes (%s)\n",
+ (u_long) size, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* done reading */
+ dev->oe_buffer.avail = size;
+ dev->oe_buffer.pos = 0;
+ return status;
+}
+
+/** @brief fill buffer while reducing vertical resolution
+ * This function fills a read buffer with scanned data from a sensor
+ * which puts odd and even pixels in 2 different data segment. So a complete
+ * must be read and bytes interleaved to get usable by the other stages
+ * of the backend
+ */
+static SANE_Status
+genesys_fill_line_interp_buffer (Genesys_Device * dev, uint8_t *work_buffer_dst, size_t size)
+{
+ size_t count;
+ SANE_Status status;
+
+ /* fill buffer if needed */
+ if (dev->oe_buffer.avail == 0)
+ {
+ status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read %lu bytes (%s)\n", __FUNCTION__,
+ (u_long) dev->oe_buffer.size, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* copy size bytes of data, copying from a line when line count matches */
+ count = 0;
+ while (count < size)
+ {
+ /* line counter */
+ /* dev->line_interp holds the number of lines scanned for one line of data sent */
+ if(((dev->line_count/dev->current_setup.channels) % dev->line_interp)==0)
+ {
+ /* copy pixel when line matches */
+ work_buffer_dst[count] = dev->oe_buffer.buffer[dev->cur + dev->oe_buffer.pos];
+ count++;
+ }
+
+ /* always update pointer so we skip uncopied data */
+ dev->cur++;
+
+ /* go to next line if needed */
+ if (dev->cur == dev->len)
+ {
+ dev->oe_buffer.pos += dev->bpl;
+ dev->cur = 0;
+ dev->line_count++;
+ }
+
+ /* read a new buffer if needed */
+ if (dev->oe_buffer.pos >= dev->oe_buffer.avail)
+ {
+ status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read %lu bytes (%s)\n", __FUNCTION__,
+ (u_long) dev->oe_buffer.size, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief fill buffer for segmented sensors
+ * This function fills a read buffer with scanned data from a sensor segmented
+ * in several parts (multi-lines sensors). Data of the same valid area is read
+ * back to back and must be interleaved to get usable by the other stages
+ * of the backend
+ */
+static SANE_Status
+genesys_fill_segmented_buffer (Genesys_Device * dev, uint8_t *work_buffer_dst, size_t size)
+{
+ size_t count;
+ SANE_Status status;
+ int depth,i,n,k;
+
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART && dev->settings.dynamic_lineart==SANE_FALSE)
+ depth = 1;
+
+ /* fill buffer if needed */
+ if (dev->oe_buffer.avail == 0)
+ {
+ status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read %lu bytes (%s)\n", __FUNCTION__,
+ (u_long) dev->oe_buffer.size, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* copy size bytes of data, copying from a subwindow of each line
+ * when last line of buffer is exhausted, read another one */
+ count = 0;
+ while (count < size)
+ {
+ if(dev->settings.double_xres==SANE_TRUE)
+ {
+ /* copy only even pixel */
+ work_buffer_dst[count] = dev->oe_buffer.buffer[dev->cur + dev->oe_buffer.pos];
+ /* update counter and pointer */
+ count++;
+ dev->cur++;
+ }
+ else
+ {
+ if(depth==1)
+ {
+ while (dev->cur < dev->len && count < size)
+ {
+ for(n=0;n<dev->segnb;n++)
+ {
+ work_buffer_dst[count+n] = 0;
+ }
+ /* interleaving is at bit level */
+ for(i=0;i<8;i++)
+ {
+ k=count+(i*dev->segnb)/8;
+ for(n=0;n<dev->segnb;n++)
+ {
+ work_buffer_dst[k] = work_buffer_dst[k] << 1;
+ if((dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos])&(128>>i))
+ {
+ work_buffer_dst[k] |= 1;
+ }
+ }
+ }
+
+ /* update counter and pointer */
+ count += dev->segnb;
+ dev->cur++;
+ }
+ }
+ if(depth==8)
+ {
+ while (dev->cur < dev->len && count < size)
+ {
+ for(n=0;n<dev->segnb;n++)
+ {
+ work_buffer_dst[count+n] = dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos];
+ }
+ /* update counter and pointer */
+ count += dev->segnb;
+ dev->cur++;
+ }
+ }
+ if(depth==16)
+ {
+ while (dev->cur < dev->len && count < size)
+ {
+ for(n=0;n<dev->segnb;n++)
+ {
+ work_buffer_dst[count+n*2] = dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos];
+ work_buffer_dst[count+n*2+1] = dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos+1];
+ }
+ /* update counter and pointer */
+ count += dev->segnb*2;
+ dev->cur+=2;
+ }
+ }
+ }
+
+ /* go to next line if needed */
+ if (dev->cur == dev->len)
+ {
+ dev->oe_buffer.pos += dev->bpl;
+ dev->cur = 0;
+ }
+
+ /* read a new buffer if needed */
+ if (dev->oe_buffer.pos >= dev->oe_buffer.avail)
+ {
+ status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read %lu bytes (%s)\n", __FUNCTION__,
+ (u_long) dev->oe_buffer.size, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ *
+ */
+static SANE_Status
+genesys_fill_read_buffer (Genesys_Device * dev)
+{
+ size_t size;
+ size_t space;
+ SANE_Status status;
+ uint8_t *work_buffer_dst;
+
+ DBGSTART;
+
+ /* for sheetfed scanner, we must check is document is shorter than
+ * the requested scan */
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = dev->model->cmd_set->detect_document_end (dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ space = dev->read_buffer.size - dev->read_buffer.avail;
+
+ work_buffer_dst = sanei_genesys_buffer_get_write_pos (&(dev->read_buffer),
+ space);
+
+ size = space;
+
+ /* never read an odd number. exception: last read
+ the chip internal counter does not count half words. */
+ size &= ~1;
+ /* Some setups need the reads to be multiples of 256 bytes */
+ size &= ~0xff;
+
+ if (dev->read_bytes_left < size)
+ {
+ size = dev->read_bytes_left;
+ /*round up to a multiple of 256 bytes */
+ size += (size & 0xff) ? 0x100 : 0x00;
+ size &= ~0xff;
+ }
+
+ /* early out if our remaining buffer capacity is too low */
+ if (size == 0)
+ return SANE_STATUS_GOOD;
+
+ DBG (DBG_io, "genesys_fill_read_buffer: reading %lu bytes\n",
+ (u_long) size);
+
+ /* size is already maxed to our needs. for most models bulk_read_data
+ will read as much data as requested. */
+
+ /* due to sensors and motors, not all data can be directly used. It
+ * may have to be read from another intermediate buffer and then processed.
+ * There are currently 3 intermediate stages:
+ * - handling of odd/even sensors
+ * - handling of line interpolation for motors that can't have low
+ * enough dpi
+ * - handling of multi-segments sensors
+ *
+ * This is also the place where full duplex data will be handled.
+ */
+ if (dev->line_interp>0)
+ {
+ /* line interpolation */
+ status = genesys_fill_line_interp_buffer (dev, work_buffer_dst, size);
+ }
+ else if (dev->segnb>1)
+ {
+ /* multi-segment sensors processing */
+ status = genesys_fill_segmented_buffer (dev, work_buffer_dst, size);
+ }
+ else /* regular case with no extra copy */
+ {
+ status = dev->model->cmd_set->bulk_read_data (dev, 0x45, work_buffer_dst, size);
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_fill_read_buffer: failed to read %lu bytes (%s)\n",
+ (u_long) size, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (size > dev->read_bytes_left)
+ size = dev->read_bytes_left;
+
+ dev->read_bytes_left -= size;
+
+ RIE (sanei_genesys_buffer_produce (&(dev->read_buffer), size));
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* this function does the effective data read in a manner that suits
+ the scanner. It does data reordering and resizing if need.
+ It also manages EOF and I/O errors, and line distance correction.
+ */
+static SANE_Status
+genesys_read_ordered_data (Genesys_Device * dev, SANE_Byte * destination,
+ size_t * len)
+{
+ SANE_Status status;
+ size_t bytes, extra;
+ unsigned int channels, depth, src_pixels;
+ unsigned int ccd_shift[12], shift_count;
+ uint8_t *work_buffer_src;
+ uint8_t *work_buffer_dst;
+ unsigned int dst_lines;
+ unsigned int step_1_mode;
+ unsigned int needs_reorder;
+ unsigned int needs_ccd;
+ unsigned int needs_shrink;
+ unsigned int needs_reverse;
+ Genesys_Buffer *src_buffer;
+ Genesys_Buffer *dst_buffer;
+
+ DBGSTART;
+ if (dev->read_active != SANE_TRUE)
+ {
+ DBG (DBG_error, "genesys_read_ordered_data: read not active!\n");
+ *len = 0;
+ return SANE_STATUS_INVAL;
+ }
+
+
+ DBG (DBG_info, "genesys_read_ordered_data: dumping current_setup:\n"
+ "\tpixels: %d\n"
+ "\tlines: %d\n"
+ "\tdepth: %d\n"
+ "\tchannels: %d\n"
+ "\texposure_time: %d\n"
+ "\txres: %g\n"
+ "\tyres: %g\n"
+ "\thalf_ccd: %s\n"
+ "\tstagger: %d\n"
+ "\tmax_shift: %d\n",
+ dev->current_setup.pixels,
+ dev->current_setup.lines,
+ dev->current_setup.depth,
+ dev->current_setup.channels,
+ dev->current_setup.exposure_time,
+ dev->current_setup.xres,
+ dev->current_setup.yres,
+ dev->current_setup.half_ccd ? "yes" : "no",
+ dev->current_setup.stagger, dev->current_setup.max_shift);
+
+ /* prepare conversion */
+ /* current settings */
+ channels = dev->current_setup.channels;
+ depth = dev->current_setup.depth;
+
+ src_pixels = dev->current_setup.pixels;
+
+ needs_reorder = 1;
+ if (channels != 3 && depth != 16)
+ needs_reorder = 0;
+#ifndef WORDS_BIGENDIAN
+ if (channels != 3 && depth == 16)
+ needs_reorder = 0;
+ if (channels == 3 && depth == 16 && !dev->model->is_cis &&
+ dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ needs_reorder = 0;
+#endif
+ if (channels == 3 && depth == 8 && !dev->model->is_cis &&
+ dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ needs_reorder = 0;
+
+ needs_ccd = dev->current_setup.max_shift > 0;
+ needs_shrink = dev->settings.pixels != src_pixels;
+ needs_reverse = depth == 1;
+
+ DBG (DBG_info,
+ "genesys_read_ordered_data: using filters:%s%s%s%s\n",
+ needs_reorder ? " reorder" : "",
+ needs_ccd ? " ccd" : "",
+ needs_shrink ? " shrink" : "",
+ needs_reverse ? " reverse" : "");
+
+ DBG (DBG_info,
+ "genesys_read_ordered_data: frontend requested %lu bytes\n",
+ (u_long) * len);
+
+ DBG (DBG_info,
+ "genesys_read_ordered_data: bytes_to_read=%lu, total_bytes_read=%lu\n",
+ (u_long) dev->total_bytes_to_read, (u_long) dev->total_bytes_read);
+ /* is there data left to scan */
+ if (dev->total_bytes_read >= dev->total_bytes_to_read)
+ {
+ DBG (DBG_proc,
+ "genesys_read_ordered_data: nothing more to scan: EOF\n");
+ *len = 0;
+
+ /* issue park command immediatly in case scanner can handle it
+ * so we save time */
+ if (dev->model->is_sheetfed == SANE_FALSE
+ && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT)
+ && dev->parking == SANE_FALSE)
+ {
+ dev->model->cmd_set->slow_back_home (dev, SANE_FALSE);
+ dev->parking = SANE_TRUE;
+ }
+ return SANE_STATUS_EOF;
+ }
+
+ DBG (DBG_info, "genesys_read_ordered_data: %lu lines left by output\n",
+ ((dev->total_bytes_to_read - dev->total_bytes_read) * 8UL) /
+ (dev->settings.pixels * channels * depth));
+ DBG (DBG_info, "genesys_read_ordered_data: %lu lines left by input\n",
+ ((dev->read_bytes_left + dev->read_buffer.avail) * 8UL) /
+ (src_pixels * channels * depth));
+
+ if (channels == 1)
+ {
+ ccd_shift[0] = 0;
+ ccd_shift[1] = dev->current_setup.stagger;
+ shift_count = 2;
+ }
+ else
+ {
+ ccd_shift[0] =
+ ((dev->ld_shift_r * dev->settings.yres) /
+ dev->motor.base_ydpi);
+ ccd_shift[1] =
+ ((dev->ld_shift_g * dev->settings.yres) /
+ dev->motor.base_ydpi);
+ ccd_shift[2] =
+ ((dev->ld_shift_b * dev->settings.yres) /
+ dev->motor.base_ydpi);
+
+ ccd_shift[3] = ccd_shift[0] + dev->current_setup.stagger;
+ ccd_shift[4] = ccd_shift[1] + dev->current_setup.stagger;
+ ccd_shift[5] = ccd_shift[2] + dev->current_setup.stagger;
+
+ shift_count = 6;
+ }
+
+
+/* convert data */
+/*
+ 0. fill_read_buffer
+-------------- read_buffer ----------------------
+ 1a). (opt)uncis (assumes color components to be laid out
+ planar)
+ 1b). (opt)reverse_RGB (assumes pixels to be BGR or BBGGRR))
+-------------- lines_buffer ----------------------
+ 2a). (opt)line_distance_correction (assumes RGB or RRGGBB)
+ 2b). (opt)unstagger (assumes pixels to be depth*channels/8
+ bytes long, unshrinked)
+------------- shrink_buffer ---------------------
+ 3. (opt)shrink_lines (assumes component separation in pixels)
+-------------- out_buffer -----------------------
+ 4. memcpy to destination (for lineart with bit reversal)
+*/
+/*FIXME: for lineart we need sub byte addressing in buffers, or conversion to
+ bytes at 0. and back to bits at 4.
+Problems with the first approach:
+ - its not clear how to check if we need to output an incomplete byte
+ because it is the last one.
+ */
+/*FIXME: add lineart support for gl646. in the meantime add logic to convert
+ from gray to lineart at the end? would suffer the above problem,
+ total_bytes_to_read and total_bytes_read help in that case.
+ */
+
+ status = genesys_fill_read_buffer (dev);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_read_ordered_data: genesys_fill_read_buffer failed\n");
+ return status;
+ }
+
+ src_buffer = &(dev->read_buffer);
+
+/* maybe reorder components/bytes */
+ if (needs_reorder)
+ {
+/*not implemented for depth == 1.*/
+ if (depth == 1)
+ {
+ DBG (DBG_error, "Can't reorder single bit data\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ dst_buffer = &(dev->lines_buffer);
+
+ work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer);
+ bytes = src_buffer->avail;
+
+/*how many bytes can be processed here?*/
+/*we are greedy. we work as much as possible*/
+ if (bytes > dst_buffer->size - dst_buffer->avail)
+ bytes = dst_buffer->size - dst_buffer->avail;
+
+ dst_lines = (bytes * 8) / (src_pixels * channels * depth);
+ bytes = (dst_lines * src_pixels * channels * depth) / 8;
+
+ work_buffer_dst = sanei_genesys_buffer_get_write_pos (dst_buffer,
+ bytes);
+
+ DBG (DBG_info, "genesys_read_ordered_data: reordering %d lines\n",
+ dst_lines);
+
+ if (dst_lines != 0)
+ {
+
+ if (channels == 3)
+ {
+ step_1_mode = 0;
+
+ if (depth == 16)
+ step_1_mode |= 1;
+
+ if (dev->model->is_cis)
+ step_1_mode |= 2;
+
+ if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ step_1_mode |= 4;
+
+ switch (step_1_mode)
+ {
+ case 1: /* RGB, chunky, 16 bit */
+#ifdef WORDS_BIGENDIAN
+ status =
+ genesys_reorder_components_endian_16 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels, 3);
+ break;
+#endif /*WORDS_BIGENDIAN */
+ case 0: /* RGB, chunky, 8 bit */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 2: /* RGB, cis, 8 bit */
+ status =
+ genesys_reorder_components_cis_8 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines, src_pixels);
+ break;
+ case 3: /* RGB, cis, 16 bit */
+ status =
+ genesys_reorder_components_cis_16 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines, src_pixels);
+ break;
+ case 4: /* BGR, chunky, 8 bit */
+ status =
+ genesys_reorder_components_bgr_8 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines, src_pixels);
+ break;
+ case 5: /* BGR, chunky, 16 bit */
+ status =
+ genesys_reorder_components_bgr_16 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines, src_pixels);
+ break;
+ case 6: /* BGR, cis, 8 bit */
+ status =
+ genesys_reorder_components_cis_bgr_8 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels);
+ break;
+ case 7: /* BGR, cis, 16 bit */
+ status =
+ genesys_reorder_components_cis_bgr_16 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels);
+ break;
+ }
+ }
+ else
+ {
+#ifdef WORDS_BIGENDIAN
+ if (depth == 16)
+ {
+ status =
+ genesys_reorder_components_endian_16 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels, 1);
+ }
+ else
+ {
+ status = SANE_STATUS_GOOD;
+ }
+#else /*!WORDS_BIGENDIAN */
+ status = SANE_STATUS_GOOD;
+#endif /*WORDS_BIGENDIAN */
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_read_ordered_data: failed to convert byte ordering(%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ RIE (sanei_genesys_buffer_produce (dst_buffer, bytes));
+
+ RIE (sanei_genesys_buffer_consume (src_buffer, bytes));
+ }
+ src_buffer = dst_buffer;
+ }
+
+/* maybe reverse effects of ccd layout */
+ if (needs_ccd)
+ {
+/*should not happen with depth == 1.*/
+ if (depth == 1)
+ {
+ DBG (DBG_error, "Can't reverse ccd for single bit data\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ dst_buffer = &(dev->shrink_buffer);
+
+ work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer);
+ bytes = src_buffer->avail;
+
+ extra =
+ (dev->current_setup.max_shift * src_pixels * channels * depth) / 8;
+
+/*extra bytes are reserved, and should not be consumed*/
+ if (bytes < extra)
+ bytes = 0;
+ else
+ bytes -= extra;
+
+/*how many bytes can be processed here?*/
+/*we are greedy. we work as much as possible*/
+ if (bytes > dst_buffer->size - dst_buffer->avail)
+ bytes = dst_buffer->size - dst_buffer->avail;
+
+ dst_lines = (bytes * 8) / (src_pixels * channels * depth);
+ bytes = (dst_lines * src_pixels * channels * depth) / 8;
+
+ work_buffer_dst =
+ sanei_genesys_buffer_get_write_pos (dst_buffer, bytes);
+
+ DBG (DBG_info, "genesys_read_ordered_data: un-ccd-ing %d lines\n",
+ dst_lines);
+
+ if (dst_lines != 0)
+ {
+
+ if (depth == 8)
+ status = genesys_reverse_ccd_8 (work_buffer_src, work_buffer_dst,
+ dst_lines,
+ src_pixels * channels,
+ ccd_shift, shift_count);
+ else
+ status = genesys_reverse_ccd_16 (work_buffer_src, work_buffer_dst,
+ dst_lines,
+ src_pixels * channels,
+ ccd_shift, shift_count);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_read_ordered_data: failed to reverse ccd effects(%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ RIE (sanei_genesys_buffer_produce (dst_buffer, bytes));
+
+ RIE (sanei_genesys_buffer_consume (src_buffer, bytes));
+ }
+ src_buffer = dst_buffer;
+ }
+
+/* maybe shrink(or enlarge) lines */
+ if (needs_shrink)
+ {
+
+ dst_buffer = &(dev->out_buffer);
+
+ work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer);
+ bytes = src_buffer->avail;
+
+/*lines in input*/
+ dst_lines = (bytes * 8) / (src_pixels * channels * depth);
+
+ /* how many lines can be processed here? */
+ /* we are greedy. we work as much as possible */
+ bytes = dst_buffer->size - dst_buffer->avail;
+
+ if (dst_lines > (bytes * 8) / (dev->settings.pixels * channels * depth))
+ dst_lines = (bytes * 8) / (dev->settings.pixels * channels * depth);
+
+ bytes = (dst_lines * dev->settings.pixels * channels * depth) / 8;
+
+ work_buffer_dst =
+ sanei_genesys_buffer_get_write_pos (dst_buffer, bytes);
+
+ DBG (DBG_info, "genesys_read_ordered_data: shrinking %d lines\n",
+ dst_lines);
+
+ if (dst_lines != 0)
+ {
+ if (depth == 1)
+ status = genesys_shrink_lines_1 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels,
+ dev->settings.pixels,
+ channels);
+ else if (depth == 8)
+ status = genesys_shrink_lines_8 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels,
+ dev->settings.pixels, channels);
+ else
+ status = genesys_shrink_lines_16 (work_buffer_src,
+ work_buffer_dst,
+ dst_lines,
+ src_pixels,
+ dev->settings.pixels, channels);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_read_ordered_data: failed to shrink lines(%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* we just consumed this many bytes*/
+ bytes = (dst_lines * src_pixels * channels * depth) / 8;
+ RIE (sanei_genesys_buffer_consume (src_buffer, bytes));
+
+ /* we just created this many bytes*/
+ bytes = (dst_lines * dev->settings.pixels * channels * depth) / 8;
+ RIE (sanei_genesys_buffer_produce (dst_buffer, bytes));
+
+ }
+ src_buffer = dst_buffer;
+ }
+
+ /* move data to destination */
+ bytes = src_buffer->avail;
+ if (bytes > *len)
+ bytes = *len;
+ work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer);
+
+ if (needs_reverse)
+ {
+ status = genesys_reverse_bits (work_buffer_src, destination, bytes);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_read_ordered_data: failed to reverse bits(%s)\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ *len = bytes;
+ }
+ else
+ {
+ memcpy (destination, work_buffer_src, bytes);
+ *len = bytes;
+ }
+
+ /* avoid signaling some extra data because we have treated a full block
+ * on the last block */
+ if (dev->total_bytes_read + *len > dev->total_bytes_to_read)
+ *len = dev->total_bytes_to_read - dev->total_bytes_read;
+
+ /* count bytes sent to frontend */
+ dev->total_bytes_read += *len;
+
+ RIE (sanei_genesys_buffer_consume (src_buffer, bytes));
+
+ /* end scan if all needed data have been read */
+ if(dev->total_bytes_read >= dev->total_bytes_to_read)
+ {
+ dev->model->cmd_set->end_scan (dev, dev->reg, SANE_TRUE);
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ dev->model->cmd_set->eject_document (dev);
+ }
+ }
+
+ DBG (DBG_proc, "genesys_read_ordered_data: completed, %lu bytes read\n",
+ (u_long) bytes);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Start of higher level functions */
+/* ------------------------------------------------------------------------ */
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+calc_parameters (Genesys_Scanner * s)
+{
+ SANE_String mode, source, color_filter;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int depth = 0, resolution = 0;
+ double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0;
+
+ mode = s->val[OPT_MODE].s;
+ source = s->val[OPT_SOURCE].s;
+ color_filter = s->val[OPT_COLOR_FILTER].s;
+ depth = s->val[OPT_BIT_DEPTH].w;
+ resolution = s->val[OPT_RESOLUTION].w;
+ tl_x = SANE_UNFIX (s->val[OPT_TL_X].w);
+ tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w);
+ br_x = SANE_UNFIX (s->val[OPT_BR_X].w);
+ br_y = SANE_UNFIX (s->val[OPT_BR_Y].w);
+
+ s->params.last_frame = SANE_TRUE; /* only single pass scanning supported */
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0
+ || strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->params.format = SANE_FRAME_GRAY;
+ else /* Color */
+ s->params.format = SANE_FRAME_RGB;
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->params.depth = 1;
+ else
+ s->params.depth = depth;
+ s->dev->settings.depth = depth;
+
+ /* interpolation */
+ s->dev->settings.disable_interpolation =
+ s->val[OPT_DISABLE_INTERPOLATION].w == SANE_TRUE;
+
+ /* hardware settings */
+ if (resolution > s->dev->sensor.optical_res &&
+ s->dev->settings.disable_interpolation)
+ s->dev->settings.xres = s->dev->sensor.optical_res;
+ else
+ s->dev->settings.xres = resolution;
+ s->dev->settings.yres = resolution;
+
+ s->params.lines = ((br_y - tl_y) * s->dev->settings.yres) / MM_PER_INCH;
+ s->params.pixels_per_line =
+ ((br_x - tl_x) * resolution) / MM_PER_INCH;
+
+ /* we need an even pixels number
+ * TODO invert test logic or generalize behaviour across all ASICs */
+ if ((s->dev->model->flags & GENESYS_FLAG_SIS_SENSOR)
+ || s->dev->model->asic_type == GENESYS_GL847
+ || s->dev->model->asic_type == GENESYS_GL124
+ || s->dev->model->asic_type == GENESYS_GL845
+ || s->dev->model->asic_type == GENESYS_GL846
+ || s->dev->model->asic_type == GENESYS_GL843)
+ {
+ if (s->dev->settings.xres <= 1200)
+ s->params.pixels_per_line = (s->params.pixels_per_line/4)*4;
+ else
+ s->params.pixels_per_line = (s->params.pixels_per_line/16)*16;
+ }
+
+ /* corner case for true lineart for sensor with several segments
+ * or when xres is doubled to match yres */
+ if (s->dev->settings.xres >= 1200
+ && ( s->dev->model->asic_type == GENESYS_GL124
+ || s->dev->model->asic_type == GENESYS_GL847
+ || s->dev->current_setup.xres < s->dev->current_setup.yres
+ )
+ )
+ {
+ s->params.pixels_per_line = (s->params.pixels_per_line/16)*16;
+ }
+
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ if (s->params.depth > 8)
+ {
+ s->params.depth = 16;
+ s->params.bytes_per_line *= 2;
+ }
+ else if (s->params.depth == 1)
+ {
+ s->params.bytes_per_line /= 8;
+ /* round down pixel number
+ really? rounding down means loss of at most 7 pixels! -- pierre */
+ s->params.pixels_per_line = 8 * s->params.bytes_per_line;
+ }
+
+ if (s->params.format == SANE_FRAME_RGB)
+ s->params.bytes_per_line *= 3;
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ s->dev->settings.scan_mode = SCAN_MODE_COLOR;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->dev->settings.scan_mode = SCAN_MODE_GRAY;
+ else if (strcmp (mode, SANE_TITLE_HALFTONE) == 0)
+ s->dev->settings.scan_mode = SCAN_MODE_HALFTONE;
+ else /* Lineart */
+ s->dev->settings.scan_mode = SCAN_MODE_LINEART;
+
+ /* TODO: change and check */
+ if (strcmp (source, FLATBED) == 0)
+ s->dev->settings.scan_method = SCAN_METHOD_FLATBED;
+ else /* transparency */
+ s->dev->settings.scan_method = SCAN_METHOD_TRANSPARENCY;
+
+ s->dev->settings.lines = s->params.lines;
+ s->dev->settings.pixels = s->params.pixels_per_line;
+ s->dev->settings.tl_x = tl_x;
+ s->dev->settings.tl_y = tl_y;
+
+ /* threshold setting */
+ s->dev->settings.threshold = 2.55 * (SANE_UNFIX (s->val[OPT_THRESHOLD].w));
+
+ /* color filter */
+ if (strcmp (color_filter, "Red") == 0)
+ s->dev->settings.color_filter = 0;
+ else if (strcmp (color_filter, "Green") == 0)
+ s->dev->settings.color_filter = 1;
+ else if (strcmp (color_filter, "Blue") == 0)
+ s->dev->settings.color_filter = 2;
+ else
+ s->dev->settings.color_filter = 3;
+
+ /* true gray */
+ if (strcmp (color_filter, "None") == 0)
+ s->dev->settings.true_gray = 1;
+ else
+ s->dev->settings.true_gray = 0;
+
+ /* dynamic lineart */
+ s->dev->settings.dynamic_lineart = SANE_FALSE;
+ s->dev->settings.threshold_curve=0;
+ if(s->val[OPT_DISABLE_DYNAMIC_LINEART].w ==SANE_FALSE
+ &&s->dev->settings.scan_mode == SCAN_MODE_LINEART)
+ {
+ s->dev->settings.dynamic_lineart = SANE_TRUE;
+ }
+
+ /* hardware lineart works only when we don't have interleave data
+ * for GL847 scanners, ie up to 600 DPI, then we have to rely on
+ * dynamic_lineart */
+ if(s->dev->settings.xres > 600
+ && s->dev->model->asic_type==GENESYS_GL847
+ && s->dev->settings.scan_mode == SCAN_MODE_LINEART)
+ {
+ s->dev->settings.dynamic_lineart = SANE_TRUE;
+ }
+
+ /* threshold curve for dynamic rasterization */
+ s->dev->settings.threshold_curve=s->val[OPT_THRESHOLD_CURVE].w;
+
+ /* some digital processing requires the whole picture to be buffered */
+ /* no digital processing takes place when doing preview, or when bit depth is
+ * higher than 8 bits */
+ if ((s->val[OPT_SWDESPECK].b
+ || s->val[OPT_SWCROP].b
+ || s->val[OPT_SWDESKEW].b
+ || s->val[OPT_SWDEROTATE].b
+ ||(SANE_UNFIX(s->val[OPT_SWSKIP].w)>0))
+ && (!s->val[OPT_PREVIEW].b)
+ && (s->val[OPT_BIT_DEPTH].w <= 8))
+ {
+ s->dev->buffer_image=SANE_TRUE;
+ }
+ else
+ {
+ s->dev->buffer_image=SANE_FALSE;
+ }
+
+ /* brigthness and contrast only for for 8 bit scans */
+ if(s->val[OPT_BIT_DEPTH].w <= 8)
+ {
+ s->dev->settings.contrast=(s->val[OPT_CONTRAST].w*127)/100;
+ s->dev->settings.brightness=(s->val[OPT_BRIGHTNESS].w*127)/100;
+ }
+ else
+ {
+ s->dev->settings.contrast=0;
+ s->dev->settings.brightness=0;
+ }
+
+ return status;
+}
+
+
+static SANE_Status
+create_bpp_list (Genesys_Scanner * s, SANE_Int * bpp)
+{
+ int count;
+
+ for (count = 0; bpp[count] != 0; count++)
+ ;
+ s->bpp_list[0] = count;
+ for (count = 0; bpp[count] != 0; count++)
+ {
+ s->bpp_list[s->bpp_list[0] - count] = bpp[count];
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief this function initialize a gamma vector based on the ASIC:
+ * Set up a default gamma table vector based on device description
+ * gl646: 12 or 14 bits gamma table depending on GENESYS_FLAG_14BIT_GAMMA
+ * gl84x: 16 bits
+ * gl12x: 16 bits
+ * @param scanner pointer to scanner session to get options
+ * @param option option number of the gamma table to set
+ */
+static void
+init_gamma_vector_option (Genesys_Scanner * scanner, int option)
+{
+ /* the option is inactive until the custom gamma control
+ * is enabled */
+ scanner->opt[option].type = SANE_TYPE_INT;
+ scanner->opt[option].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ scanner->opt[option].unit = SANE_UNIT_NONE;
+ scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE;
+ if (scanner->dev->model->asic_type == GENESYS_GL646)
+ {
+ if ((scanner->dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) != 0)
+ {
+ scanner->opt[option].size = 16384 * sizeof (SANE_Word);
+ scanner->opt[option].constraint.range = &u14_range;
+ }
+ else
+ { /* 12 bits gamma tables */
+ scanner->opt[option].size = 4096 * sizeof (SANE_Word);
+ scanner->opt[option].constraint.range = &u12_range;
+ }
+ }
+ else
+ { /* other asics have 16 bits words gamma table */
+ scanner->opt[option].size = 256 * sizeof (SANE_Word);
+ scanner->opt[option].constraint.range = &u16_range;
+ }
+ /* default value is NULL */
+ scanner->val[option].wa = NULL;
+}
+
+/**
+ * allocate a geometry range
+ * @param size maximum size of the range
+ * @return a pointer to a valid range or NULL
+ */
+static SANE_Range *create_range(SANE_Fixed size)
+{
+SANE_Range *range=NULL;
+
+ range=(SANE_Range *)malloc(sizeof(SANE_Range));
+ if(range!=NULL)
+ {
+ range->min = SANE_FIX (0.0);
+ range->max = size;
+ range->quant = SANE_FIX (0.0);
+ }
+ return range;
+}
+
+
+static SANE_Status
+init_options (Genesys_Scanner * s)
+{
+ SANE_Int option, count, min_dpi;
+ SANE_Status status;
+ SANE_Word *dpi_list;
+ Genesys_Model *model = s->dev->model;
+ SANE_Range *x_range, *y_range;
+
+ DBGSTART;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->opt[option].size = sizeof (SANE_Word);
+ s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (SANE_VALUE_SCAN_MODE_GRAY);
+
+ /* scan source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].s = strdup (FLATBED);
+ if (!(model->flags & GENESYS_FLAG_HAS_UTA))
+ {
+ DISABLE (OPT_SOURCE);
+ }
+ else
+ {
+ ENABLE (OPT_SOURCE);
+ }
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = 0;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list;
+ create_bpp_list (s, model->bpp_gray_values);
+ s->val[OPT_BIT_DEPTH].w = 8;
+ if (s->opt[OPT_BIT_DEPTH].constraint.word_list[0] < 2)
+ DISABLE (OPT_BIT_DEPTH);
+
+ /* resolution */
+ min_dpi=200000;
+ for (count = 0; model->xdpi_values[count] != 0; count++)
+ {
+ if(model->xdpi_values[count]<min_dpi)
+ {
+ min_dpi=model->xdpi_values[count];
+ }
+ }
+ dpi_list = malloc ((count + 1) * sizeof (SANE_Word));
+ if (!dpi_list)
+ return SANE_STATUS_NO_MEM;
+ dpi_list[0] = count;
+ for (count = 0; model->xdpi_values[count] != 0; count++)
+ dpi_list[count + 1] = model->xdpi_values[count];
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+ s->val[OPT_RESOLUTION].w = min_dpi;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ x_range=create_range(model->x_size);
+ if(x_range==NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ y_range=create_range(model->y_size);
+ if(y_range==NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = x_range;
+ s->val[OPT_BR_X].w = x_range->max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = y_range;
+ s->val[OPT_BR_Y].w = y_range->max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED;
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ init_gamma_vector_option (s, OPT_GAMMA_VECTOR);
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ init_gamma_vector_option (s, OPT_GAMMA_VECTOR_R);
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ init_gamma_vector_option (s, OPT_GAMMA_VECTOR_G);
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ init_gamma_vector_option (s, OPT_GAMMA_VECTOR_B);
+
+ /* currently, there are only gamma table options in this group,
+ * so if the scanner doesn't support gamma table, disable the
+ * whole group */
+ if (!(model->flags & GENESYS_FLAG_CUSTOM_GAMMA))
+ {
+ s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ DBG (DBG_info, "init_options: custom gamma disabled\n");
+ }
+
+ /* software base image enhancements, these are consuming as many
+ * memory than used by the full scanned image and may fail at high
+ * resolution
+ */
+ /* software deskew */
+ s->opt[OPT_SWDESKEW].name = "swdeskew";
+ s->opt[OPT_SWDESKEW].title = "Software deskew";
+ s->opt[OPT_SWDESKEW].desc = "Request backend to rotate skewed pages digitally";
+ s->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SWDESKEW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->val[OPT_SWDESKEW].b = SANE_FALSE;
+
+ /* software deskew */
+ s->opt[OPT_SWDESPECK].name = "swdespeck";
+ s->opt[OPT_SWDESPECK].title = "Software despeck";
+ s->opt[OPT_SWDESPECK].desc = "Request backend to remove lone dots digitally";
+ s->opt[OPT_SWDESPECK].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SWDESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->val[OPT_SWDESPECK].b = SANE_FALSE;
+
+ /* software despeckle radius */
+ s->opt[OPT_DESPECK].name = "despeck";
+ s->opt[OPT_DESPECK].title = "Software despeckle diameter";
+ s->opt[OPT_DESPECK].desc = "Maximum diameter of lone dots to remove from scan";
+ s->opt[OPT_DESPECK].type = SANE_TYPE_INT;
+ s->opt[OPT_DESPECK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DESPECK].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_DESPECK].constraint.range = &swdespeck_range;
+ s->opt[OPT_DESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->val[OPT_DESPECK].w = 1;
+
+ /* crop by software */
+ s->opt[OPT_SWCROP].name = "swcrop";
+ s->opt[OPT_SWCROP].title = SANE_I18N ("Software crop");
+ s->opt[OPT_SWCROP].desc = SANE_I18N ("Request backend to remove border from pages digitally");
+ s->opt[OPT_SWCROP].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SWCROP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->opt[OPT_SWCROP].unit = SANE_UNIT_NONE;
+ s->val[OPT_SWCROP].b = SANE_FALSE;
+
+ /* Software blank page skip */
+ s->opt[OPT_SWSKIP].name = "swskip";
+ s->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage");
+ s->opt[OPT_SWSKIP].desc = SANE_I18N("Request driver to discard pages with low numbers of dark pixels");
+ s->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED;
+ s->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SWSKIP].constraint.range = &(percentage_range);
+ s->opt[OPT_SWSKIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ /* disable by default */
+ s->val[OPT_SWSKIP].w = 0;
+
+ /* Software Derotate */
+ s->opt[OPT_SWDEROTATE].name = "swderotate";
+ s->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate");
+ s->opt[OPT_SWDEROTATE].desc = SANE_I18N("Request driver to detect and correct 90 degree image rotation");
+ s->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SWDEROTATE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE;
+ s->val[OPT_SWDEROTATE].b = SANE_FALSE;
+
+ /* Software brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &(enhance_range);
+ s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ /* disable by default */
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* Sowftware contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &(enhance_range);
+ s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ /* disable by default */
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* "Extras" group: */
+ s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras");
+ s->opt[OPT_EXTRAS_GROUP].desc = "";
+ s->opt[OPT_EXTRAS_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_EXTRAS_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_EXTRAS_GROUP].size = 0;
+ s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* BW threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &percentage_range;
+ s->val[OPT_THRESHOLD].w = SANE_FIX (50);
+
+ /* BW threshold curve */
+ s->opt[OPT_THRESHOLD_CURVE].name = "threshold-curve";
+ s->opt[OPT_THRESHOLD_CURVE].title = SANE_I18N ("Threshold curve");
+ s->opt[OPT_THRESHOLD_CURVE].desc = SANE_I18N ("Dynamic threshold curve, from light to dark, normally 50-65");
+ s->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range;
+ s->val[OPT_THRESHOLD_CURVE].w = 50;
+
+ /* dynamic linart */
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].name = "disable-dynamic-lineart";
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].title = SANE_I18N ("Disable dynamic lineart");
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].desc =
+ SANE_I18N ("Disable use of a software adaptive algorithm to generate lineart relying instead on hardware lineart.");
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DISABLE_DYNAMIC_LINEART].w = SANE_FALSE;
+
+ /* fastmod is required for hw lineart to work */
+ if ((s->dev->model->asic_type == GENESYS_GL646)
+ &&(s->dev->model->motor_type != MOTOR_XP200))
+ {
+ s->opt[OPT_DISABLE_DYNAMIC_LINEART].cap = SANE_CAP_INACTIVE;
+ }
+
+ /* disable_interpolation */
+ s->opt[OPT_DISABLE_INTERPOLATION].name = "disable-interpolation";
+ s->opt[OPT_DISABLE_INTERPOLATION].title =
+ SANE_I18N ("Disable interpolation");
+ s->opt[OPT_DISABLE_INTERPOLATION].desc =
+ SANE_I18N
+ ("When using high resolutions where the horizontal resolution is smaller "
+ "than the vertical resolution this disables horizontal interpolation.");
+ s->opt[OPT_DISABLE_INTERPOLATION].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DISABLE_INTERPOLATION].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DISABLE_INTERPOLATION].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DISABLE_INTERPOLATION].w = SANE_FALSE;
+
+ /* color filter */
+ s->opt[OPT_COLOR_FILTER].name = "color-filter";
+ s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter");
+ s->opt[OPT_COLOR_FILTER].desc =
+ SANE_I18N
+ ("When using gray or lineart this option selects the used color.");
+ s->opt[OPT_COLOR_FILTER].type = SANE_TYPE_STRING;
+ s->opt[OPT_COLOR_FILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ /* true gray not yet supported for GL847 and GL124 scanners */
+ if(!model->is_cis || model->asic_type==GENESYS_GL847 || model->asic_type==GENESYS_GL124)
+ {
+ s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list);
+ s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list;
+ s->val[OPT_COLOR_FILTER].s = strdup (s->opt[OPT_COLOR_FILTER].constraint.string_list[1]);
+ }
+ else
+ {
+ s->opt[OPT_COLOR_FILTER].size = max_string_size (cis_color_filter_list);
+ s->opt[OPT_COLOR_FILTER].constraint.string_list = cis_color_filter_list;
+ /* default to "None" ie true gray */
+ s->val[OPT_COLOR_FILTER].s = strdup (s->opt[OPT_COLOR_FILTER].constraint.string_list[3]);
+ }
+
+ /* no support for color filter for cis+gl646 scanners */
+ if (model->asic_type == GENESYS_GL646 && model->is_cis)
+ {
+ DISABLE (OPT_COLOR_FILTER);
+ }
+
+ /* calibration stor file name */
+ s->opt[OPT_CALIBRATION_FILE].name = "calibration-file";
+ s->opt[OPT_CALIBRATION_FILE].title = SANE_I18N ("Calibration file");
+ s->opt[OPT_CALIBRATION_FILE].desc = SANE_I18N ("Specify the calibration file to use");
+ s->opt[OPT_CALIBRATION_FILE].type = SANE_TYPE_STRING;
+ s->opt[OPT_CALIBRATION_FILE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATION_FILE].size = PATH_MAX;
+ s->opt[OPT_CALIBRATION_FILE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ s->opt[OPT_CALIBRATION_FILE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CALIBRATION_FILE].s = NULL;
+ /* disable option if ran as root */
+#ifdef HAVE_GETUID
+ if(geteuid()==0)
+ {
+ DISABLE (OPT_CALIBRATION_FILE);
+ }
+#endif
+
+ /* Powersave time (turn lamp off) */
+ s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time";
+ s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time");
+ s->opt[OPT_LAMP_OFF_TIME].desc =
+ SANE_I18N
+ ("The lamp will be turned off after the given time (in minutes). "
+ "A value of 0 means, that the lamp won't be turned off.");
+ s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_LAMP_OFF_TIME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_LAMP_OFF_TIME].constraint.range = &time_range;
+ s->val[OPT_LAMP_OFF_TIME].w = 15; /* 15 minutes */
+
+ /* turn lamp off during scan */
+ s->opt[OPT_LAMP_OFF].name = "lamp-off-scan";
+ s->opt[OPT_LAMP_OFF].title = SANE_I18N ("Lamp off during scan");
+ s->opt[OPT_LAMP_OFF].desc = SANE_I18N ("The lamp will be turned off during scan. ");
+ s->opt[OPT_LAMP_OFF].type = SANE_TYPE_BOOL;
+ s->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_LAMP_OFF].w = SANE_FALSE;
+
+ s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS;
+ s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS;
+ s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_SENSOR_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_SENSOR_GROUP].size = 0;
+ s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_SCAN_SW].name = SANE_NAME_SCAN;
+ s->opt[OPT_SCAN_SW].title = SANE_TITLE_SCAN;
+ s->opt[OPT_SCAN_SW].desc = SANE_DESC_SCAN;
+ s->opt[OPT_SCAN_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SCAN_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_SCAN_SW)
+ s->opt[OPT_SCAN_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_SCAN_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_SCAN_SW].b = 0;
+ s->last_val[OPT_SCAN_SW].b = 0;
+
+ /* SANE_NAME_FILE is not for buttons */
+ s->opt[OPT_FILE_SW].name = "file";
+ s->opt[OPT_FILE_SW].title = SANE_I18N ("File button");
+ s->opt[OPT_FILE_SW].desc = SANE_I18N ("File button");
+ s->opt[OPT_FILE_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_FILE_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_FILE_SW)
+ s->opt[OPT_FILE_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_FILE_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_FILE_SW].b = 0;
+ s->last_val[OPT_FILE_SW].b = 0;
+
+ s->opt[OPT_EMAIL_SW].name = SANE_NAME_EMAIL;
+ s->opt[OPT_EMAIL_SW].title = SANE_TITLE_EMAIL;
+ s->opt[OPT_EMAIL_SW].desc = SANE_DESC_EMAIL;
+ s->opt[OPT_EMAIL_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_EMAIL_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_EMAIL_SW)
+ s->opt[OPT_EMAIL_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_EMAIL_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_EMAIL_SW].b = 0;
+ s->last_val[OPT_EMAIL_SW].b = 0;
+
+ s->opt[OPT_COPY_SW].name = SANE_NAME_COPY;
+ s->opt[OPT_COPY_SW].title = SANE_TITLE_COPY;
+ s->opt[OPT_COPY_SW].desc = SANE_DESC_COPY;
+ s->opt[OPT_COPY_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_COPY_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_COPY_SW)
+ s->opt[OPT_COPY_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_COPY_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_COPY_SW].b = 0;
+ s->last_val[OPT_COPY_SW].b = 0;
+
+ s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED;
+ s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED;
+ s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED;
+ s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_PAGE_LOADED_SW)
+ s->opt[OPT_PAGE_LOADED_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_PAGE_LOADED_SW].b = 0;
+ s->last_val[OPT_PAGE_LOADED_SW].b = 0;
+
+ /* OCR button */
+ s->opt[OPT_OCR_SW].name = "ocr";
+ s->opt[OPT_OCR_SW].title = SANE_I18N ("OCR button");
+ s->opt[OPT_OCR_SW].desc = SANE_I18N ("OCR button");
+ s->opt[OPT_OCR_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_OCR_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_OCR_SW)
+ s->opt[OPT_OCR_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_OCR_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_OCR_SW].b = 0;
+ s->last_val[OPT_OCR_SW].b = 0;
+
+ /* power button */
+ s->opt[OPT_POWER_SW].name = "power";
+ s->opt[OPT_POWER_SW].title = SANE_I18N ("Power button");
+ s->opt[OPT_POWER_SW].desc = SANE_I18N ("Power button");
+ s->opt[OPT_POWER_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_POWER_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_POWER_SW)
+ s->opt[OPT_POWER_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_POWER_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_POWER_SW].b = 0;
+ s->last_val[OPT_POWER_SW].b = 0;
+
+ /* extra button */
+ s->opt[OPT_EXTRA_SW].name = "extra";
+ s->opt[OPT_EXTRA_SW].title = SANE_I18N ("Extra button");
+ s->opt[OPT_EXTRA_SW].desc = SANE_I18N ("Extra button");
+ s->opt[OPT_EXTRA_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_EXTRA_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_EXTRA_SW)
+ s->opt[OPT_EXTRA_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_EXTRA_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_EXTRA_SW].b = 0;
+ s->last_val[OPT_EXTRA_SW].b = 0;
+
+ /* calibration needed */
+ s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration";
+ s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Need calibration");
+ s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings");
+ s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_CALIBRATE)
+ s->opt[OPT_NEED_CALIBRATION_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_NEED_CALIBRATION_SW].b = 0;
+ s->last_val[OPT_NEED_CALIBRATION_SW].b = 0;
+
+ /* button group */
+ s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons");
+ s->opt[OPT_BUTTON_GROUP].desc = "";
+ s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_BUTTON_GROUP].size = 0;
+ s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* calibrate button */
+ s->opt[OPT_CALIBRATE].name = "calibrate";
+ s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate");
+ s->opt[OPT_CALIBRATE].desc =
+ SANE_I18N ("Start calibration using special sheet");
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
+ if (model->buttons & GENESYS_HAS_CALIBRATE)
+ s->opt[OPT_CALIBRATE].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
+ SANE_CAP_AUTOMATIC;
+ else
+ s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_CALIBRATE].b = 0;
+ s->last_val[OPT_CALIBRATE].b = 0;
+
+ /* clear calibration cache button */
+ s->opt[OPT_CLEAR_CALIBRATION].name = "clear-calibration";
+ s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration");
+ s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache");
+ s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CLEAR_CALIBRATION].size = 0;
+ s->opt[OPT_CLEAR_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_CLEAR_CALIBRATION].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ s->val[OPT_CLEAR_CALIBRATION].b = 0;
+ s->last_val[OPT_CLEAR_CALIBRATION].b = 0;
+
+ RIE (calc_parameters (s));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Bool present;
+static SANE_Status
+check_present (SANE_String_Const devname)
+{
+ present=SANE_TRUE;
+ DBG (DBG_io, "check_present: %s detected.\n",devname);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, Genesys_Device ** devp, SANE_Bool may_wait)
+{
+ Genesys_Device *dev = 0;
+ SANE_Int dn, vendor, product;
+ SANE_Status status;
+ int i;
+
+
+ DBG (DBG_proc, "attach: start: devp %s NULL, may_wait = %d\n",
+ devp ? "!=" : "==", may_wait);
+
+ if (devp)
+ *devp = 0;
+
+ if (!devname)
+ {
+ DBG (DBG_error, "attach: devname == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->file_name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ DBG (DBG_info, "attach: device `%s' was already in device list\n",
+ devname);
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (DBG_info, "attach: trying to open device `%s'\n", devname);
+
+ status = sanei_usb_open (devname, &dn);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "attach: couldn't open device `%s': %s\n", devname,
+ sane_strstatus (status));
+ return status;
+ }
+ else
+ DBG (DBG_info, "attach: device `%s' successfully opened\n", devname);
+
+ status = sanei_usb_get_vendor_product (dn, &vendor, &product);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "attach: couldn't get vendor and product ids of device `%s': %s\n",
+ devname, sane_strstatus (status));
+ return status;
+ }
+
+ /* KV-SS080 is an auxiliary device which requires a master device to be here */
+ if(vendor == 0x04da && product == 0x100f)
+ {
+ present=SANE_FALSE;
+ sanei_usb_find_devices (vendor, 0x1006, check_present);
+ sanei_usb_find_devices (vendor, 0x1007, check_present);
+ sanei_usb_find_devices (vendor, 0x1010, check_present);
+ if(present==SANE_FALSE)
+ {
+ DBG (DBG_error,"attach: master device not present\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ for (i = 0; i < MAX_SCANNERS && genesys_usb_device_list[i].model != 0; i++)
+ {
+ if (vendor == genesys_usb_device_list[i].vendor &&
+ product == genesys_usb_device_list[i].product)
+ {
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error,
+ "attach: vendor %d product %d is not supported by this backend\n",
+ vendor, product);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev->file_name = strdup (devname);
+ if (!dev->file_name)
+ return SANE_STATUS_NO_MEM;
+
+ dev->model = genesys_usb_device_list[i].model;
+ dev->vendorId = genesys_usb_device_list[i].vendor;
+ dev->productId = genesys_usb_device_list[i].product;
+ dev->already_initialized = SANE_FALSE;
+
+ DBG (DBG_info, "attach: found %s flatbed scanner %s at %s\n",
+ dev->model->vendor, dev->model->model, dev->file_name);
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ sanei_usb_close (dn);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ Genesys_Device *dev;
+ SANE_Status status;
+ Genesys_Device **tmp_dev;
+
+ RIE (attach (devname, &dev, SANE_FALSE));
+
+ if (dev)
+ {
+ /* Keep track of newly attached devices so we can set options as
+ necessary. */
+ tmp_dev=NULL;
+ /* increase device list capacity if needed */
+ if (new_dev_len >= new_dev_alloced)
+ {
+ new_dev_alloced += 4;
+ if (new_dev)
+ {
+ tmp_dev = new_dev;
+ new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0]));
+ }
+ else
+ {
+ new_dev = malloc (new_dev_alloced * sizeof (new_dev[0]));
+ tmp_dev = NULL;
+ }
+ if (!new_dev)
+ {
+ FREE_IFNOT_NULL(tmp_dev)
+ DBG (DBG_error, "attach_one_device: out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ new_dev[new_dev_len++] = dev;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* configuration framework functions */
+static SANE_Status
+config_attach_genesys (SANEI_Config __sane_unused__ *config, const char *devname)
+{
+ /* the devname has been processed and is ready to be used
+ * directly. Since the backend is an USB only one, we can
+ * call sanei_usb_attach_matching_devices straight */
+ sanei_usb_attach_matching_devices (devname, attach_one_device);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* probes for scanner to attach to the backend */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+probe_genesys_devices (void)
+{
+ SANEI_Config config;
+ SANE_Status status;
+
+ DBGSTART;
+
+ new_dev = 0;
+ new_dev_len = 0;
+ new_dev_alloced = 0;
+
+ /* set configuration options structure : no option for this backend */
+ config.descriptors = NULL;
+ config.values = NULL;
+ config.count = 0;
+
+ /* generic configure and attach function */
+ status = sanei_configure_attach (GENESYS_CONFIG_FILE, &config,
+ config_attach_genesys);
+
+ if (new_dev_alloced > 0)
+ {
+ new_dev_len = new_dev_alloced = 0;
+ free (new_dev);
+ }
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+/**
+ * This should be changed if one of the substructures of
+ Genesys_Calibration_Cache change, but it must be changed if there are
+ changes that don't change size -- at least for now, as we store most
+ of Genesys_Calibration_Cache as is.
+*/
+#define CALIBRATION_VERSION 1
+
+/**
+ * reads previously cached calibration data
+ * from file define in dev->calib_file
+ */
+SANE_Status
+sanei_genesys_read_calibration (Genesys_Device * dev)
+{
+ FILE *fp;
+ uint8_t vers = 0;
+ uint32_t size = 0;
+ struct Genesys_Calibration_Cache *cache;
+ SANE_Status status=SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ /* open calibration cache file */
+ fp = fopen (dev->calib_file, "rb");
+ if (!fp)
+ {
+ DBG (DBG_info, "Calibration: Cannot open %s\n", dev->calib_file);
+ DBGCOMPLETED;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* these two checks ensure that most bad things cannot happen */
+ fread (&vers, 1, 1, fp);
+ if (vers != CALIBRATION_VERSION)
+ {
+ DBG (DBG_info, "Calibration: Bad version\n");
+ fclose (fp);
+ DBGCOMPLETED;
+ return SANE_STATUS_INVAL;
+ }
+ fread (&size, 4, 1, fp);
+ if (size != sizeof (struct Genesys_Calibration_Cache))
+ {
+ DBG (DBG_info,
+ "Calibration: Size of calibration cache struct differs\n");
+ fclose (fp);
+ DBGCOMPLETED;
+ return SANE_STATUS_INVAL;
+ }
+
+ /* clear device calibration cache */
+ while(dev->calibration_cache!=NULL)
+ {
+ cache=dev->calibration_cache;
+ dev->calibration_cache=dev->calibration_cache->next;
+ free(cache);
+ }
+
+ /* loop on cache records in file */
+ while (!feof (fp) && status==SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "sanei_genesys_read_calibration: reading one record\n");
+ cache = (struct Genesys_Calibration_Cache *) malloc (sizeof (*cache));
+
+ if (!cache)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_calibration: could not allocate cache struct\n");
+ break;
+ }
+
+#define BILT1( x ) \
+ do \
+ { \
+ if ((x) < 1) \
+ { \
+ free(cache); \
+ DBG (DBG_warn, "sanei_genesys_read_calibration: partial calibration record\n"); \
+ status=SANE_STATUS_EOF; \
+ break; \
+ } \
+ } while(0)
+
+
+ if (fread (&cache->used_setup, sizeof (cache->used_setup), 1, fp) < 1)
+ { /* eof is only detected here */
+ free (cache);
+ status=SANE_STATUS_GOOD;
+ break;
+ }
+ BILT1 (fread (&cache->last_calibration, sizeof (cache->last_calibration), 1, fp));
+ BILT1 (fread (&cache->frontend, sizeof (cache->frontend), 1, fp));
+ /* the gamma (and later) fields are not stored */
+ BILT1 (fread (&cache->sensor, offsetof (Genesys_Sensor, gamma[0]), 1, fp));
+ BILT1 (fread (&cache->calib_pixels, sizeof (cache->calib_pixels), 1, fp));
+ BILT1 (fread (&cache->calib_channels, sizeof (cache->calib_channels), 1, fp));
+ BILT1 (fread (&cache->average_size, sizeof (cache->average_size), 1, fp));
+
+ cache->white_average_data = (uint8_t *) malloc (cache->average_size);
+ cache->dark_average_data = (uint8_t *) malloc (cache->average_size);
+
+ if (!cache->white_average_data || !cache->dark_average_data)
+ {
+ status=SANE_STATUS_NO_MEM;
+ FREE_IFNOT_NULL (cache->white_average_data);
+ FREE_IFNOT_NULL (cache->dark_average_data);
+ free (cache);
+ DBG (DBG_error,
+ "sanei_genesys_read_calibration: could not allocate space for average data\n");
+ break;
+ }
+
+ if (fread (cache->white_average_data, cache->average_size, 1, fp) < 1)
+ {
+ status=SANE_STATUS_EOF;
+ DBG (DBG_warn, "sanei_genesys_read_calibration: partial calibration record\n");
+ free (cache->white_average_data);
+ free (cache->dark_average_data);
+ free (cache);
+ break;
+ }
+ if (fread (cache->dark_average_data, cache->average_size, 1, fp) < 1)
+ {
+ DBG (DBG_warn, "sanei_genesys_read_calibration: partial calibration record\n");
+ free (cache->white_average_data);
+ free (cache->dark_average_data);
+ free (cache);
+ status=SANE_STATUS_EOF;
+ break;
+ }
+#undef BILT1
+ DBG (DBG_info, "sanei_genesys_read_calibration: adding record to list\n");
+ cache->next = dev->calibration_cache;
+ dev->calibration_cache = cache;
+ }
+
+ fclose (fp);
+ DBGCOMPLETED;
+ return status;
+}
+
+static void
+write_calibration (Genesys_Device * dev)
+{
+ FILE *fp;
+ uint8_t vers = 0;
+ uint32_t size = 0;
+ struct Genesys_Calibration_Cache *cache;
+
+ DBGSTART;
+ fp = fopen (dev->calib_file, "wb");
+ if (!fp)
+ {
+ DBG (DBG_info, "write_calibration: Cannot open %s for writing\n", dev->calib_file);
+ return;
+ }
+
+ vers = CALIBRATION_VERSION;
+ fwrite (&vers, 1, 1, fp);
+ size = sizeof (struct Genesys_Calibration_Cache);
+ fwrite (&size, 4, 1, fp);
+
+ for (cache = dev->calibration_cache; cache; cache = cache->next)
+ {
+ fwrite (&cache->used_setup, sizeof (cache->used_setup), 1, fp);
+ fwrite (&cache->last_calibration, sizeof (cache->last_calibration), 1, fp);
+ fwrite (&cache->frontend, sizeof (cache->frontend), 1, fp);
+ /* the gamma (and later) fields are not stored */
+ fwrite (&cache->sensor, offsetof (Genesys_Sensor, gamma[0]), 1, fp);
+
+ fwrite (&cache->calib_pixels, sizeof (cache->calib_pixels), 1, fp);
+ fwrite (&cache->calib_channels, sizeof (cache->calib_channels), 1, fp);
+ fwrite (&cache->average_size, sizeof (cache->average_size), 1, fp);
+ fwrite (cache->white_average_data, cache->average_size, 1, fp);
+ fwrite (cache->dark_average_data, cache->average_size, 1, fp);
+ }
+ DBGCOMPLETED;
+ fclose (fp);
+}
+
+/** @brief buffer scanned picture
+ * In order to allow digital processing, we must be able to put all the
+ * scanned picture in a buffer.
+ */
+static SANE_Status
+genesys_buffer_image(Genesys_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t maximum; /**> maximum bytes size of the scan */
+ size_t len; /**> length of scanned data read */
+ size_t total; /**> total of butes read */
+ size_t size; /**> size of image buffer */
+ size_t read_size; /**> size of reads */
+ int lines; /** number of lines of the scan */
+ Genesys_Device *dev = s->dev;
+ SANE_Byte *lineart=NULL;
+
+ /* compute maximum number of lines for the scan */
+ if (s->params.lines > 0)
+ {
+ lines = s->params.lines;
+ }
+ else
+ {
+ lines =
+ (SANE_UNFIX (dev->model->y_size) * dev->settings.yres) / MM_PER_INCH;
+ }
+ DBG (DBG_info, "%s: buffering %d lines of %d bytes\n", __FUNCTION__, lines,
+ s->params.bytes_per_line);
+
+ /* maximum bytes to read */
+ maximum = s->params.bytes_per_line * lines;
+ if(s->dev->settings.dynamic_lineart==SANE_TRUE)
+ {
+ maximum *= 8;
+ }
+
+ /* initial size of the read buffer */
+ size =
+ ((2048 * 2048) / s->params.bytes_per_line) * s->params.bytes_per_line;
+
+ /* read size */
+ read_size = size / 2;
+
+ /* allocate memory */
+ dev->img_buffer = (SANE_Byte *) malloc (size);
+ if (dev->img_buffer == NULL)
+ {
+ DBG (DBG_error,
+ "%s: digital processing requires too much memory.\nConsider disabling it\n",
+ __FUNCTION__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* loop reading data until we reach maximum or EOF */
+ total = 0;
+ while (total < maximum && status != SANE_STATUS_EOF)
+ {
+ len = size - maximum;
+ if (len > read_size)
+ {
+ len = read_size;
+ }
+ status = genesys_read_ordered_data (dev, dev->img_buffer + total, &len);
+ if (status != SANE_STATUS_EOF && status != SANE_STATUS_GOOD)
+ {
+ free (s->dev->img_buffer);
+ DBG (DBG_error, "%s: %s buffering failed\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ total += len;
+
+ /* do we need to enlarge read buffer ? */
+ if (total + read_size > size && status != SANE_STATUS_EOF)
+ {
+ size += read_size;
+ dev->img_buffer = (SANE_Byte *) realloc (dev->img_buffer, size);
+ if (dev->img_buffer == NULL)
+ {
+ DBG (DBG_error0,
+ "%s: digital processing requires too much memory.\nConsider disabling it\n",
+ __FUNCTION__);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+
+ /* since digital processing is going to take place,
+ * issue head parking command so that the head move while
+ * computing so we can save time
+ */
+ if (dev->model->is_sheetfed == SANE_FALSE &&
+ dev->parking == SANE_FALSE)
+ {
+ dev->model->cmd_set->slow_back_home (dev, dev->model->flags & GENESYS_FLAG_MUST_WAIT);
+ dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
+ }
+
+ /* in case of dynamic lineart, we have buffered gray data which
+ * must be converted to lineart first */
+ if(s->dev->settings.dynamic_lineart==SANE_TRUE)
+ {
+ total/=8;
+ lineart=(SANE_Byte *)malloc(total);
+ if (lineart == NULL)
+ {
+ DBG (DBG_error0,
+ "%s: digital processing requires too much memory.\nConsider disabling it\n",
+ __FUNCTION__);
+ return SANE_STATUS_NO_MEM;
+ }
+ genesys_gray_lineart (dev,
+ dev->img_buffer,
+ lineart,
+ dev->settings.pixels,
+ (total*8)/dev->settings.pixels,
+ dev->settings.threshold);
+ free(dev->img_buffer);
+ dev->img_buffer = lineart;
+ }
+
+ /* update counters */
+ dev->total_bytes_to_read = total;
+ dev->total_bytes_read = 0;
+
+ /* update params */
+ s->params.lines = total / s->params.bytes_per_line;
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sanei_genesys_write_pnm_file ("unprocessed.pnm",
+ dev->img_buffer,
+ s->params.depth,
+ s->params.format==SANE_FRAME_RGB ? 3:1,
+ s->params.pixels_per_line,
+ s->params.lines);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* -------------------------- SANE API functions ------------------------- */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+ DBG (DBG_init, "SANE Genesys backend version %d.%d build %d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+#ifdef HAVE_LIBUSB_1_0
+ DBG (DBG_init, "SANE Genesys backend built with libusb-1.0\n");
+#endif
+#ifdef HAVE_LIBUSB
+ DBG (DBG_init, "SANE Genesys backend built with libusb\n");
+#endif
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (DBG_proc, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ /* init usb use */
+ sanei_usb_init ();
+
+ /* init sanei_magic */
+ sanei_magic_init();
+
+ DBG (DBG_info, "sane_init: %s endian machine\n",
+#ifdef WORDS_BIGENDIAN
+ "big"
+#else
+ "little"
+#endif
+ );
+
+ /* set up to no devices at first */
+ num_devices = 0;
+ first_dev = 0;
+ first_handle = 0;
+ devlist = 0;
+
+ /* cold-plug case :detection of allready connected scanners */
+ status = probe_genesys_devices ();
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+void
+sane_exit (void)
+{
+ Genesys_Device *dev, *next;
+
+ DBGSTART;
+ for (dev = first_dev; dev; dev = next)
+ {
+ /* sane_close() free many fields, not much things left to
+ * do here */
+ next = dev->next;
+ free (dev->file_name);
+ free (dev);
+ }
+ first_dev = 0;
+ first_handle = 0;
+ if (devlist)
+ free (devlist);
+ devlist = 0;
+
+ sanei_usb_exit();
+
+ DBGCOMPLETED;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Genesys_Device *dev, *prev;
+ SANE_Int index;
+ SANE_Device *sane_device;
+
+ DBG (DBG_proc, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ /* hot-plug case : detection of newly connected scanners */
+ sanei_usb_scan_devices ();
+ probe_genesys_devices ();
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ prev = NULL;
+ index = 0;
+ dev = first_dev;
+ while (dev != NULL)
+ {
+ /* check if device removed */
+ present = SANE_FALSE;
+ sanei_usb_find_devices (dev->vendorId, dev->productId, check_present);
+ if (present)
+ {
+ sane_device = malloc (sizeof (*sane_device));
+ if (!sane_device)
+ return SANE_STATUS_NO_MEM;
+ sane_device->name = dev->file_name;
+ sane_device->vendor = dev->model->vendor;
+ sane_device->model = dev->model->model;
+ sane_device->type = strdup ("flatbed scanner");
+ devlist[index] = sane_device;
+ index++;
+ prev = dev;
+ dev = dev->next;
+ }
+ else
+ {
+ /* remove device from internal list */
+ /* case 1 : removed device is first_dev */
+ if (prev == NULL)
+ {
+ /* test for another dev */
+ if (dev->next == NULL)
+ {
+ /* empty the whole list */
+ free (dev);
+ first_dev = NULL;
+ num_devices = 0;
+ dev = NULL;
+ }
+ else
+ {
+ /* assign new start */
+ first_dev = dev->next;
+ num_devices--;
+ free (dev);
+ dev = prev->next;
+ }
+ }
+ /* case 2 : removed device is not first_dev */
+ else
+ {
+ /* link previous dev to next dev */
+ prev->next = dev->next;
+ free (dev);
+ num_devices--;
+
+ /* next loop */
+ dev = prev->next;
+ }
+ }
+ }
+ devlist[index] = 0;
+
+ *device_list = devlist;
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Genesys_Device *dev;
+ SANE_Status status;
+ Genesys_Scanner *s;
+ char tmp_str[PATH_MAX];
+ char *ptr;
+
+ DBG (DBG_proc, "sane_open: start (devicename = `%s')\n", devicename);
+
+ /* devicename="" or devicename="genesys" are default values that use
+ * first available device
+ */
+ if (devicename[0] && strcmp ("genesys", devicename) != 0)
+ {
+ /* search for the given devicename in the device list */
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->file_name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ DBG (DBG_info,
+ "sane_open: couldn't find `%s' in devlist, trying attach\n",
+ devicename);
+ RIE (attach (devicename, &dev, SANE_TRUE));
+ }
+ else
+ DBG (DBG_info, "sane_open: found `%s' in devlist\n",
+ dev->model->name);
+ }
+ else
+ {
+ /* empty devicename or "genesys" -> use first device */
+ dev = first_dev;
+ if (dev)
+ {
+ devicename = dev->file_name;
+ DBG (DBG_info, "sane_open: empty devicename, trying `%s'\n",
+ devicename);
+ }
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (dev->model->flags & GENESYS_FLAG_UNTESTED)
+ {
+ DBG (DBG_error0,
+ "WARNING: Your scanner is not fully supported or at least \n");
+ DBG (DBG_error0,
+ " had only limited testing. Please be careful and \n");
+ DBG (DBG_error0, " report any failure/success to \n");
+ DBG (DBG_error0,
+ " sane-devel@lists.alioth.debian.org. Please provide as many\n");
+ DBG (DBG_error0,
+ " details as possible, e.g. the exact name of your\n");
+ DBG (DBG_error0, " scanner and what does (not) work.\n");
+ }
+
+ status = sanei_usb_open (dev->file_name, &dev->dn);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "sane_open: couldn't open device `%s': %s\n",
+ dev->file_name, sane_strstatus (status));
+ return status;
+ }
+
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+
+ s->dev = dev;
+ s->scanning = SANE_FALSE;
+ s->dev->read_buffer.buffer = NULL;
+ s->dev->lines_buffer.buffer = NULL;
+ s->dev->shrink_buffer.buffer = NULL;
+ s->dev->out_buffer.buffer = NULL;
+ s->dev->binarize_buffer.buffer = NULL;
+ s->dev->local_buffer.buffer = NULL;
+ s->dev->parking = SANE_FALSE;
+ s->dev->read_active = SANE_FALSE;
+ s->dev->white_average_data = NULL;
+ s->dev->dark_average_data = NULL;
+ s->dev->calibration_cache = NULL;
+ s->dev->calib_file = NULL;
+ s->dev->img_buffer = NULL;
+ s->dev->line_interp = 0;
+ s->dev->line_count = 0;
+ s->dev->segnb = 0;
+ s->dev->oe_buffer.buffer=NULL;
+ s->dev->binary=NULL;
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+
+ if (!dev->already_initialized)
+ sanei_genesys_init_structs (dev);
+
+ RIE (init_options (s));
+
+ if (sanei_genesys_init_cmd_set (s->dev) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error0, "This device doesn't have a valid command set!!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ RIE (dev->model->cmd_set->init (dev));
+
+ /* here is the place to fetch a stored calibration cache */
+
+ /* create calibration-filename
+ lifted from plustek-usb.c
+ */
+ /* we should add a unique identifying feature to the file name
+ to support multiple scanners of the same model, but to my
+ knowledge, there is no such thing in these scanners.
+ (At least the usb serial is always "0".)
+ TODO add an storedir option to genesys.conf
+ */
+
+ ptr = getenv ("HOME");
+ if (NULL == ptr)
+ {
+ sprintf (tmp_str, "/tmp/%s.cal", s->dev->model->name);
+ }
+ else
+ {
+#ifdef HAVE_MKDIR
+ /* make sure .sane directory exists */
+ sprintf (tmp_str, "%s/.sane", ptr);
+ mkdir(tmp_str,0700);
+#endif
+ sprintf (tmp_str, "%s/.sane/%s.cal", ptr, s->dev->model->name);
+ }
+
+ s->val[OPT_CALIBRATION_FILE].s = strdup (tmp_str);
+ s->dev->calib_file = strdup (tmp_str);
+ DBG (DBG_info, "Calibration filename set to:\n");
+ DBG (DBG_info, ">%s<\n", s->dev->calib_file);
+
+ /* now open file, fetch calibration records */
+
+ sanei_genesys_read_calibration (s->dev);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Genesys_Scanner *prev, *s;
+ Genesys_Calibration_Cache *cache, *next_cache;
+ SANE_Status status;
+
+ DBGSTART;
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (DBG_error, "sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ /* eject document for sheetfed scanners */
+ if (s->dev->model->is_sheetfed == SANE_TRUE)
+ {
+ s->dev->model->cmd_set->eject_document (s->dev);
+ }
+ else
+ {
+ /* in case scanner is parking, wait for the head
+ * to reach home position */
+ if(s->dev->parking==SANE_TRUE)
+ {
+ status = sanei_genesys_wait_for_home (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sane_close: failed to wait for head to park: %s\n",
+ sane_strstatus (status));
+ }
+ }
+ }
+
+ /* enable power saving before leaving */
+ status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sane_close: failed to enable power saving mode: %s\n",
+ sane_strstatus (status));
+ }
+
+ /* here is the place to store calibration cache */
+ write_calibration (s->dev);
+
+ for (cache = s->dev->calibration_cache; cache; cache = next_cache)
+ {
+ next_cache = cache->next;
+ free (cache->dark_average_data);
+ free (cache->white_average_data);
+ free (cache);
+ }
+
+ sanei_genesys_buffer_free (&(s->dev->read_buffer));
+ sanei_genesys_buffer_free (&(s->dev->lines_buffer));
+ sanei_genesys_buffer_free (&(s->dev->shrink_buffer));
+ sanei_genesys_buffer_free (&(s->dev->out_buffer));
+ sanei_genesys_buffer_free (&(s->dev->binarize_buffer));
+ sanei_genesys_buffer_free (&(s->dev->local_buffer));
+ FREE_IFNOT_NULL (s->dev->white_average_data);
+ FREE_IFNOT_NULL (s->dev->dark_average_data);
+ FREE_IFNOT_NULL (s->dev->calib_file);
+
+ /* free allocated gamma tables */
+ FREE_IFNOT_NULL (s->dev->sensor.gamma_table[0]);
+ FREE_IFNOT_NULL (s->dev->sensor.gamma_table[1]);
+ FREE_IFNOT_NULL (s->dev->sensor.gamma_table[2]);
+
+ /* for an handful of bytes .. */
+ free ((void *)(size_t)s->opt[OPT_RESOLUTION].constraint.word_list);
+ free (s->val[OPT_SOURCE].s);
+ free (s->val[OPT_MODE].s);
+ free (s->val[OPT_COLOR_FILTER].s);
+ free ((void *)(size_t)s->opt[OPT_TL_X].constraint.range);
+ free ((void *)(size_t)s->opt[OPT_TL_Y].constraint.range);
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ /* LAMP OFF : same register across all the ASICs */
+ sanei_genesys_write_register (s->dev, 0x03, 0x00);
+
+ /* we need this to avoid ASIC getting stuck
+ * in bulk writes */
+ if(s->dev->model->asic_type==GENESYS_GL847
+ ||s->dev->model->asic_type==GENESYS_GL845
+ ||s->dev->model->asic_type==GENESYS_GL845
+ ||s->dev->model->asic_type==GENESYS_GL843)
+ sanei_usb_reset (s->dev->dn);
+
+ sanei_usb_close (s->dev->dn);
+ free (s);
+
+ DBGCOMPLETED;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Genesys_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ DBG (DBG_io2, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option);
+ return s->opt + option;
+}
+
+/* gets an option , called by sane_control_option */
+static SANE_Status
+get_option_value (Genesys_Scanner * s, int option, void *val)
+{
+ unsigned int i;
+ SANE_Word *table ,tmp;
+ uint16_t *gamma;
+ SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Calibration_Cache *cache;
+
+ switch (option)
+ {
+ /* geometry */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *) val = s->val[option].w;
+ /* switch coordinate to keep them coherent */
+ if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w)
+ {
+ tmp=s->val[OPT_BR_X].w;
+ s->val[OPT_BR_X].w=s->val[OPT_TL_X].w;
+ s->val[OPT_TL_X].w=tmp;
+ }
+ if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w)
+ {
+ tmp=s->val[OPT_BR_Y].w;
+ s->val[OPT_BR_Y].w=s->val[OPT_TL_Y].w;
+ s->val[OPT_TL_Y].w=tmp;
+ }
+ break;
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_PREVIEW:
+ case OPT_THRESHOLD:
+ case OPT_THRESHOLD_CURVE:
+ case OPT_DISABLE_DYNAMIC_LINEART:
+ case OPT_DISABLE_INTERPOLATION:
+ case OPT_LAMP_OFF:
+ case OPT_LAMP_OFF_TIME:
+ case OPT_SWDESKEW:
+ case OPT_SWCROP:
+ case OPT_SWDESPECK:
+ case OPT_SWDEROTATE:
+ case OPT_SWSKIP:
+ case OPT_DESPECK:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ case OPT_CUSTOM_GAMMA:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_COLOR_FILTER:
+ case OPT_CALIBRATION_FILE:
+ case OPT_SOURCE:
+ strcpy (val, s->val[option].s);
+ break;
+
+ /* word array options */
+ case OPT_GAMMA_VECTOR:
+ table = (SANE_Word *) val;
+ if (strcmp (s->val[OPT_COLOR_FILTER].s, "Red") == 0)
+ {
+ gamma = s->dev->sensor.gamma_table[GENESYS_RED];
+ }
+ else if (strcmp (s->val[OPT_COLOR_FILTER].s, "Blue") == 0)
+ {
+ gamma = s->dev->sensor.gamma_table[GENESYS_BLUE];
+ }
+ else
+ {
+ gamma = s->dev->sensor.gamma_table[GENESYS_GREEN];
+ }
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ table[i] = gamma[i];
+ }
+ break;
+ case OPT_GAMMA_VECTOR_R:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ table[i] = s->dev->sensor.gamma_table[GENESYS_RED][i];
+ }
+ break;
+ case OPT_GAMMA_VECTOR_G:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ table[i] = s->dev->sensor.gamma_table[GENESYS_GREEN][i];
+ }
+ break;
+ case OPT_GAMMA_VECTOR_B:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ table[i] = s->dev->sensor.gamma_table[GENESYS_BLUE][i];
+ }
+ break;
+ /* sensors */
+ case OPT_SCAN_SW:
+ case OPT_FILE_SW:
+ case OPT_EMAIL_SW:
+ case OPT_COPY_SW:
+ case OPT_PAGE_LOADED_SW:
+ case OPT_OCR_SW:
+ case OPT_POWER_SW:
+ case OPT_EXTRA_SW:
+ RIE (s->dev->model->cmd_set->update_hardware_sensors (s));
+ *(SANE_Bool *) val = s->val[option].b;
+ s->last_val[option].b = *(SANE_Bool *) val;
+ break;
+ case OPT_NEED_CALIBRATION_SW:
+ /* scanner needs calibration for current mode unless a matching
+ * calibration cache is found */
+ *(SANE_Bool *) val = SANE_TRUE;
+ for (cache = s->dev->calibration_cache; cache; cache = cache->next)
+ {
+ if (s->dev->model->
+ cmd_set->is_compatible_calibration (s->dev, cache, SANE_FALSE) == SANE_STATUS_GOOD)
+ {
+ *(SANE_Bool *) val = SANE_FALSE;
+ }
+ }
+ break;
+ default:
+ DBG (DBG_warn, "get_option_value: can't get unknown option %d\n",
+ option);
+ }
+ return status;
+}
+
+/** @brief set calibration file value
+ * Set calibration file value. Load new cache values from file if it exists,
+ * else creates the file*/
+static SANE_Status set_calibration_value (Genesys_Scanner * s, int option, void *val)
+{
+ SANE_Status status=SANE_STATUS_GOOD;
+ char *tmp;
+ Genesys_Calibration_Cache *cache;
+ Genesys_Device *dev=s->dev;
+
+ /* try to load file */
+ tmp=dev->calib_file;
+ dev->calib_file=val;
+ status=sanei_genesys_read_calibration (dev);
+
+ /* file exists but is invalid */
+ if (status!=SANE_STATUS_IO_ERROR && status!=SANE_STATUS_GOOD)
+ {
+ dev->calib_file=tmp;
+ return status;
+ }
+
+ /* we can set no file name value */
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (tmp)
+ free (tmp);
+ dev->calib_file = strdup (val);
+
+ /* clear device calibration cache */
+ while(dev->calibration_cache!=NULL)
+ {
+ cache=dev->calibration_cache;
+ dev->calibration_cache=dev->calibration_cache->next;
+ free(cache);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* sets an option , called by sane_control_option */
+static SANE_Status
+set_option_value (Genesys_Scanner * s, int option, void *val,
+ SANE_Int * myinfo)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Word *table;
+ unsigned int i;
+ SANE_Range *x_range, *y_range;
+ Genesys_Calibration_Cache *cache, *next_cache;
+
+ switch (option)
+ {
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (calc_parameters (s));
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_RESOLUTION:
+ case OPT_THRESHOLD:
+ case OPT_THRESHOLD_CURVE:
+ case OPT_DISABLE_DYNAMIC_LINEART:
+ case OPT_SWCROP:
+ case OPT_SWDESKEW:
+ case OPT_DESPECK:
+ case OPT_SWDEROTATE:
+ case OPT_SWSKIP:
+ case OPT_DISABLE_INTERPOLATION:
+ case OPT_LAMP_OFF:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (calc_parameters (s));
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_SWDESPECK:
+ s->val[option].w = *(SANE_Word *) val;
+ if (s->val[OPT_SWDESPECK].b == SANE_TRUE)
+ {
+ ENABLE(OPT_DESPECK);
+ }
+ else
+ {
+ DISABLE(OPT_DESPECK);
+ }
+ RIE (calc_parameters (s));
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ /* software enhancement functions only apply to 8 or 1 bits data */
+ case OPT_BIT_DEPTH:
+ s->val[option].w = *(SANE_Word *) val;
+ if(s->val[OPT_BIT_DEPTH].w>8)
+ {
+ DISABLE(OPT_SWDESKEW);
+ DISABLE(OPT_SWDESPECK);
+ DISABLE(OPT_SWCROP);
+ DISABLE(OPT_DESPECK);
+ DISABLE(OPT_SWDEROTATE);
+ DISABLE(OPT_SWSKIP);
+ DISABLE(OPT_CONTRAST);
+ DISABLE(OPT_BRIGHTNESS);
+ }
+ else
+ {
+ ENABLE(OPT_SWDESKEW);
+ ENABLE(OPT_SWDESPECK);
+ ENABLE(OPT_SWCROP);
+ ENABLE(OPT_DESPECK);
+ ENABLE(OPT_SWDEROTATE);
+ ENABLE(OPT_SWSKIP);
+ ENABLE(OPT_CONTRAST);
+ ENABLE(OPT_BRIGHTNESS);
+ }
+ RIE (calc_parameters (s));
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_SOURCE:
+ if (strcmp (s->val[option].s, val) != 0)
+ { /* something changed */
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+
+ /* change geometry constraint to the new source value */
+ if (strcmp (s->val[option].s, FLATBED) == 0)
+ {
+ x_range=create_range(s->dev->model->x_size);
+ y_range=create_range(s->dev->model->y_size);
+ }
+ else
+ {
+ x_range=create_range(s->dev->model->x_size_ta);
+ y_range=create_range(s->dev->model->y_size_ta);
+ }
+ if(x_range==NULL || y_range==NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* assign new values */
+ free((void *)(size_t)s->opt[OPT_TL_X].constraint.range);
+ free((void *)(size_t)s->opt[OPT_TL_Y].constraint.range);
+ s->opt[OPT_TL_X].constraint.range = x_range;
+ s->val[OPT_TL_X].w = 0;
+ s->opt[OPT_TL_Y].constraint.range = y_range;
+ s->val[OPT_TL_Y].w = 0;
+ s->opt[OPT_BR_X].constraint.range = x_range;
+ s->val[OPT_BR_Y].w = y_range->max;
+ s->opt[OPT_BR_Y].constraint.range = y_range;
+ s->val[OPT_BR_X].w = x_range->max;
+
+ /* signals reload */
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ ENABLE (OPT_THRESHOLD);
+ ENABLE (OPT_THRESHOLD_CURVE);
+ DISABLE (OPT_BIT_DEPTH);
+ if (s->dev->model->asic_type != GENESYS_GL646 || !s->dev->model->is_cis)
+ {
+ ENABLE (OPT_COLOR_FILTER);
+ }
+ ENABLE (OPT_DISABLE_DYNAMIC_LINEART);
+ }
+ else
+ {
+ DISABLE (OPT_THRESHOLD);
+ DISABLE (OPT_THRESHOLD_CURVE);
+ DISABLE (OPT_DISABLE_DYNAMIC_LINEART);
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ if (s->dev->model->asic_type != GENESYS_GL646 || !s->dev->model->is_cis)
+ {
+ ENABLE (OPT_COLOR_FILTER);
+ }
+ create_bpp_list (s, s->dev->model->bpp_gray_values);
+ }
+ else
+ {
+ DISABLE (OPT_COLOR_FILTER);
+ create_bpp_list (s, s->dev->model->bpp_color_values);
+ }
+ if (s->bpp_list[0] < 2)
+ DISABLE (OPT_BIT_DEPTH);
+ else
+ ENABLE (OPT_BIT_DEPTH);
+ }
+ RIE (calc_parameters (s));
+
+ /* if custom gamma, toggle gamma table options according to the mode */
+ if (s->val[OPT_CUSTOM_GAMMA].b == SANE_TRUE)
+ {
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ DISABLE (OPT_GAMMA_VECTOR);
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ else
+ {
+ ENABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ }
+
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_COLOR_FILTER:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ RIE (calc_parameters (s));
+ break;
+ case OPT_CALIBRATION_FILE:
+ RIE(set_calibration_value (s, option, val));
+ break;
+ case OPT_LAMP_OFF_TIME:
+ if (*(SANE_Word *) val != s->val[option].w)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (s->dev->model->cmd_set->
+ set_powersaving (s->dev, s->val[option].w));
+ }
+ break;
+
+ case OPT_CUSTOM_GAMMA:
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ s->val[OPT_CUSTOM_GAMMA].b = *(SANE_Bool *) val;
+
+ if (s->val[OPT_CUSTOM_GAMMA].b == SANE_TRUE)
+ {
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ DISABLE (OPT_GAMMA_VECTOR);
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ else
+ {
+ ENABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ }
+ else
+ {
+ DISABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ /* restore default sensor gamma table */
+ /* currently there is no sensor's specific gamma table,
+ * tables are built by sanei_genesys_create_gamma_table */
+ sanei_genesys_create_gamma_table (s->dev->sensor.gamma_table[GENESYS_RED],
+ s->opt[OPT_GAMMA_VECTOR_R].size / sizeof (SANE_Word),
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range->max,
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range->max,
+ s->dev->sensor.gamma[GENESYS_RED]);
+ sanei_genesys_create_gamma_table (s->dev->sensor.gamma_table[GENESYS_GREEN],
+ s->opt[OPT_GAMMA_VECTOR_G].size / sizeof (SANE_Word),
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range->max,
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range->max,
+ s->dev->sensor.gamma[GENESYS_GREEN]);
+ sanei_genesys_create_gamma_table (s->dev->sensor.gamma_table[GENESYS_BLUE],
+ s->opt[OPT_GAMMA_VECTOR_B].size / sizeof (SANE_Word),
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range->max,
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range->max,
+ s->dev->sensor.gamma[GENESYS_BLUE]);
+ }
+ break;
+
+ case OPT_GAMMA_VECTOR:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ s->dev->sensor.gamma_table[GENESYS_RED][i] = table[i];
+ s->dev->sensor.gamma_table[GENESYS_GREEN][i] = table[i];
+ s->dev->sensor.gamma_table[GENESYS_BLUE][i] = table[i];
+ }
+ break;
+ case OPT_GAMMA_VECTOR_R:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ s->dev->sensor.gamma_table[GENESYS_RED][i] = table[i];
+ }
+ break;
+ case OPT_GAMMA_VECTOR_G:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ s->dev->sensor.gamma_table[GENESYS_GREEN][i] = table[i];
+ }
+ break;
+ case OPT_GAMMA_VECTOR_B:
+ table = (SANE_Word *) val;
+ for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++)
+ {
+ s->dev->sensor.gamma_table[GENESYS_BLUE][i] = table[i];
+ }
+ break;
+ case OPT_CALIBRATE:
+ status = s->dev->model->cmd_set->save_power (s->dev, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to disable power saving mode: %s\n",
+ __FUNCTION__, sane_strstatus (status));
+ }
+ else
+ status = genesys_scanner_calibration (s->dev);
+ /* not critical if this fails*/
+ s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE);
+ /* signals that sensors will have to be read again */
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_CLEAR_CALIBRATION:
+ /* clear calibration cache */
+ if (s->dev->calibration_cache != NULL)
+ {
+ for (cache = s->dev->calibration_cache; cache; cache = next_cache)
+ {
+ next_cache = cache->next;
+ free (cache->dark_average_data);
+ free (cache->white_average_data);
+ free (cache);
+ }
+ }
+ s->dev->calibration_cache = NULL;
+ /* remove file */
+ unlink (s->dev->calib_file);
+ /* signals that sensors will have to be read again */
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ default:
+ DBG (DBG_warn, "set_option_value: can't set unknown option %d\n",
+ option);
+ }
+ return status;
+}
+
+
+/* sets and gets scanner option values */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Genesys_Scanner *s = handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (DBG_io2,
+ "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
+ SANE_ACTION_SET_VALUE) ?
+ "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->opt[option].name, option);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ {
+ DBG (DBG_warn, "sane_control_option: don't call this function while "
+ "scanning (option = %s (%d))\n", s->opt[option].name, option);
+
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (DBG_warn,
+ "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (DBG_warn, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ status = get_option_value (s, option, val);
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_warn, "sane_control_option: option %d is not settable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn,
+ "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = set_option_value (s, option, val, &myinfo);
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ DBG (DBG_error,
+ "sane_control_option: SANE_ACTION_SET_AUTO unsupported since no option has SANE_CAP_AUTOMATIC\n");
+ status = SANE_STATUS_INVAL;
+ break;
+
+ default:
+ DBG (DBG_warn, "sane_control_option: unknown action %d for option %d\n",
+ action, option);
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (info)
+ *info = myinfo;
+
+ DBG (DBG_io2, "sane_control_option: exit\n");
+ return status;
+}
+
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Genesys_Scanner *s = handle;
+ SANE_Status status;
+
+ DBGSTART;
+
+ /* don't recompute parameters once data reading is active, ie during scan */
+ if(s->dev->read_active == SANE_FALSE)
+ {
+ RIE (calc_parameters (s));
+ }
+ if (params)
+ {
+ *params = s->params;
+
+ /* in the case of a sheetfed scanner, when full height is specified
+ * we override the computed line number with -1 to signal that we
+ * don't know the real document height.
+ * We don't do that doing buffering image for digital processing
+ */
+ if (s->dev->model->is_sheetfed == SANE_TRUE
+ && s->dev->buffer_image == SANE_FALSE
+ && s->val[OPT_BR_Y].w == s->opt[OPT_BR_Y].constraint.range->max)
+ {
+ params->lines = -1;
+ }
+ }
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Genesys_Scanner *s = handle;
+ SANE_Status status=SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w)
+ {
+ DBG (DBG_error0,
+ "sane_start: top left x >= bottom right x --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w)
+ {
+ DBG (DBG_error0,
+ "sane_start: top left y >= bottom right y --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+
+ RIE (calc_parameters (s));
+ RIE (genesys_start_scan (s->dev, s->val[OPT_LAMP_OFF].w));
+
+ s->scanning = SANE_TRUE;
+
+ /* allocate intermediate buffer when doing dynamic lineart */
+ if(s->dev->settings.dynamic_lineart==SANE_TRUE)
+ {
+ RIE (sanei_genesys_buffer_free (&(s->dev->binarize_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(s->dev->binarize_buffer), s->dev->settings.pixels));
+ RIE (sanei_genesys_buffer_free (&(s->dev->local_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(s->dev->local_buffer), s->dev->binarize_buffer.size * 8));
+ }
+
+ /* if one of the software enhancement option is selected,
+ * we do the scan internally, process picture then put it an internal
+ * buffer. Since cropping may change scan parameters, we recompute them
+ * at the end */
+ if (s->dev->buffer_image)
+ {
+ RIE(genesys_buffer_image(s));
+
+ /* check if we need to skip this page, sheetfed scanners
+ * can go to next doc while flatbed ones can't */
+ if (s->val[OPT_SWSKIP].w && IS_ACTIVE(OPT_SWSKIP))
+ {
+ status = sanei_magic_isBlank(&s->params,
+ s->dev->img_buffer,
+ SANE_UNFIX(s->val[OPT_SWSKIP].w));
+ if(status == SANE_STATUS_NO_DOCS)
+ {
+ if (s->dev->model->is_sheetfed == SANE_TRUE)
+ {
+ DBG (DBG_info, "sane_start: blank page, recurse\n");
+ return sane_start(handle);
+ }
+ return status;
+ }
+ }
+
+ /* deskew image if required */
+ if(s->val[OPT_SWDESKEW].b == SANE_TRUE)
+ {
+ RIE(genesys_deskew(s));
+ }
+
+ /* despeck image if required */
+ if(s->val[OPT_SWDESPECK].b == SANE_TRUE)
+ {
+ RIE(genesys_despeck(s));
+ }
+
+ /* crop image if required */
+ if(s->val[OPT_SWCROP].b == SANE_TRUE)
+ {
+ RIE(genesys_crop(s));
+ }
+
+ /* de-rotate image if required */
+ if(s->val[OPT_SWDEROTATE].b == SANE_TRUE)
+ {
+ RIE(genesys_derotate(s));
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Genesys_Scanner *s = handle;
+ Genesys_Device *dev;
+ SANE_Status status=SANE_STATUS_GOOD;
+ size_t local_len;
+
+ if (!s)
+ {
+ DBG (DBG_error, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ dev=s->dev;
+ if (!dev)
+ {
+ DBG (DBG_error, "sane_read: dev is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!buf)
+ {
+ DBG (DBG_error, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (DBG_error, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *len = 0;
+
+ if (!s->scanning)
+ {
+ DBG (DBG_warn, "sane_read: scan was cancelled, is over or has not been "
+ "initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ DBG (DBG_proc, "sane_read: start, %d maximum bytes required\n", max_len);
+ DBG (DBG_io2, "sane_read: bytes_to_read=%lu, total_bytes_read=%lu\n",
+ (u_long) dev->total_bytes_to_read, (u_long) dev->total_bytes_read);
+ DBG (DBG_io2, "sane_read: physical bytes to read = %lu\n", (u_long) dev->read_bytes_left);
+
+ if(dev->total_bytes_read>=dev->total_bytes_to_read)
+ {
+ DBG (DBG_proc, "sane_read: nothing more to scan: EOF\n");
+
+ /* issue park command immediatly in case scanner can handle it
+ * so we save time */
+ if (dev->model->is_sheetfed == SANE_FALSE
+ && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT)
+ && dev->parking == SANE_FALSE)
+ {
+ dev->model->cmd_set->slow_back_home (dev, SANE_FALSE);
+ dev->parking = SANE_TRUE;
+ }
+ return SANE_STATUS_EOF;
+ }
+
+ local_len = max_len;
+
+ /* in case of image processing, all data has been stored in
+ * buffer_image. So read data from it if it exists, else from scanner */
+ if(!dev->buffer_image)
+ {
+ /* dynamic lineart is another kind of digital processing that needs
+ * another layer of buffering on top of genesys_read_ordered_data */
+ if(dev->settings.dynamic_lineart==SANE_TRUE)
+ {
+ /* if buffer is empty, fill it with genesys_read_ordered_data */
+ if(dev->binarize_buffer.avail==0)
+ {
+ /* store gray data */
+ local_len=dev->local_buffer.size;
+ status = genesys_read_ordered_data (dev, dev->local_buffer.buffer, &local_len);
+
+ /* binarize data is read successful */
+ if(status==SANE_STATUS_GOOD)
+ {
+ dev->local_buffer.avail=local_len;
+ dev->local_buffer.pos=0;
+ dev->binarize_buffer.avail=local_len/8;
+ dev->binarize_buffer.pos=0;
+ genesys_gray_lineart (dev,
+ dev->local_buffer.buffer,
+ dev->binarize_buffer.buffer,
+ dev->settings.pixels,
+ local_len/dev->settings.pixels,
+ dev->settings.threshold);
+ }
+
+ }
+
+ /* return data from lineart buffer if any, up to the available amount */
+ local_len = max_len;
+ if((size_t)max_len>dev->binarize_buffer.avail)
+ {
+ local_len=dev->binarize_buffer.avail;
+ }
+ if(local_len)
+ {
+ memcpy(buf,sanei_genesys_buffer_get_read_pos (&(dev->binarize_buffer)),local_len);
+ RIE (sanei_genesys_buffer_consume (&(dev->binarize_buffer), local_len));
+ }
+ }
+ else
+ {
+ /* most usual case, direct read of data from scanner */
+ status = genesys_read_ordered_data (dev, buf, &local_len);
+ }
+ }
+ else /* read data from buffer */
+ {
+ if(dev->total_bytes_read+local_len>dev->total_bytes_to_read)
+ {
+ local_len=dev->total_bytes_to_read-dev->total_bytes_read;
+ }
+ memcpy(buf,dev->img_buffer+dev->total_bytes_read,local_len);
+ dev->total_bytes_read+=local_len;
+ }
+
+ *len = local_len;
+ if(local_len>(size_t)max_len)
+ {
+ fprintf (stderr, "[genesys] sane_read: returning incorrect length!!\n");
+ }
+ DBG (DBG_proc, "sane_read: %d bytes returned\n", *len);
+ return status;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Genesys_Scanner *s = handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ /* end binary logging if needed */
+ if (s->dev->binary!=NULL)
+ {
+ fclose(s->dev->binary);
+ s->dev->binary=NULL;
+ }
+
+ s->scanning = SANE_FALSE;
+ s->dev->read_active = SANE_FALSE;
+ if(s->dev->img_buffer!=NULL)
+ {
+ free(s->dev->img_buffer);
+ s->dev->img_buffer=NULL;
+ }
+
+ /* no need to end scan if we are parking the head */
+ if(s->dev->parking==SANE_FALSE)
+ {
+ status = s->dev->model->cmd_set->end_scan (s->dev, s->dev->reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_cancel: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+ }
+
+ /* park head if flatbed scanner */
+ if (s->dev->model->is_sheetfed == SANE_FALSE)
+ {
+ if(s->dev->parking==SANE_FALSE)
+ {
+ status = s->dev->model->cmd_set->slow_back_home (s->dev, s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sane_cancel: failed to move scanhead to home position: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+ s->dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
+ }
+ }
+ else
+ { /* in case of sheetfed scanners, we have to eject the document if still present */
+ status = s->dev->model->cmd_set->eject_document (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_cancel: failed to eject document: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+ }
+
+ /* enable power saving mode unless we are parking .... */
+ if(s->dev->parking==SANE_FALSE)
+ {
+ status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_cancel: failed to enable power saving mode: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+ }
+
+ DBGCOMPLETED;
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Genesys_Scanner *s = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: handle = %p, non_blocking = %s\n",
+ handle, non_blocking == SANE_TRUE ? "true" : "false");
+
+ if (!s->scanning)
+ {
+ DBG (DBG_error, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Genesys_Scanner *s = handle;
+
+ DBG (DBG_proc, "sane_get_select_fd: handle = %p, fd = %p\n", handle,
+ (void *) fd);
+
+ if (!s->scanning)
+ {
+ DBG (DBG_error, "sane_get_select_fd: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys.conf.in b/backend/genesys.conf.in
new file mode 100644
index 0000000..58cb351
--- /dev/null
+++ b/backend/genesys.conf.in
@@ -0,0 +1,125 @@
+# genesys.conf: Configuration file for Genesys Logic GL646 and GL841 based scanners
+
+#
+# scanners that are not yet supported
+# uncomment them only for developpment purpose
+#
+
+# UMAX Astra 4500 and Avision iVina 1600
+#usb 0x0638 0x0a10
+
+# Hewlett Packard ScanJet 2400c
+usb 0x03f0 0x0a01
+
+# Hewlett Packard ScanJet 3670c/3690c
+usb 0x03f0 0x1405
+
+# Plustek OpticPro ST24
+#usb 0x07b3 0x0601
+
+# Syscan DocketPort 465
+#usb 0x0a82 0x4802
+
+#
+# supported scanners
+#
+
+# Medion MD5345/MD6228/MD6471
+usb 0x0461 0x0377
+
+# Hewlett Packard ScanJet 2300c
+usb 0x03f0 0x0901
+
+# Canon LiDE 35/40/50
+usb 0x04a9 0x2213
+
+# Canon LiDE 60
+usb 0x04a9 0x221c
+
+# Canon 4400F
+usb 0x04a9 0x2228
+
+# Canon LiDE 100
+usb 0x04a9 0x1904
+
+# Canon LiDE 110
+usb 0x04a9 0x1909
+
+# Canon LiDE 200
+usb 0x04a9 0x1905
+
+# Canon 5600F
+usb 0x04a9 0x1906
+
+# Canon LiDE 700F
+usb 0x04a9 0x1907
+
+# Canon LiDE 210
+usb 0x04a9 0x190a
+
+# Canon 5600f
+usb 0x04a9 0x1906
+
+# Visioneer Strobe XP200
+usb 0x04a7 0x0426
+
+# Visioneer Strobe XP300
+usb 0x04a7 0x0474
+
+# Ambir/Syscan DocketPort 665
+usb 0x0a82 0x4803
+
+# Visioneer Roadwarrior
+usb 0x04a7 0x0494
+
+# Visioneer XP100 rev 3
+usb 0x04a7 0x049b
+
+#Pentax DSmobile 600
+usb 0x0a17 0x3210
+usb 0x04f9 0x2038
+
+# Syscan DocketPort 467
+usb 0x1dcc 0x4812
+
+# Syscan DocketPort 485
+usb 0x0a82 0x4800
+
+# DCT DocketPort 487
+usb 0x1dcc 0x4810
+
+# Syscan/Ambir DocketPort 685
+usb 0x0a82 0x480c
+
+# Visioneer OneTouch 7100
+usb 0x04a7 0x0229
+
+# Xerox Travel Scanner 100
+usb 0x04a7 0x04ac
+
+# Panasonic KV-SS080
+usb 0x04da 0x100f
+
+# Hewlett Packard ScanJet 4850C
+usb 0x03f0 0x1b05
+
+# Hewlett Packard ScanJet G4010
+usb 0x03f0 0x4505
+
+# Hewlett Packard ScanJet G4050
+usb 0x03f0 0x4605
+
+# Plustek OpticBook 3600
+usb 0x07b3 0x0900
+
+# Primax Electronics, Ltd Xerox 2400 Onetouch
+usb 0x0461 0x038b
+
+#Hewlett Packard ScanJet N6310
+usb 0x03f0 0x4705
+
+# Canon Image Formula 101
+usb 0x1803 0x162e
+
+# Plustek OpticBook 3800
+usb 0x07b3 0x1300
diff --git a/backend/genesys.h b/backend/genesys.h
new file mode 100644
index 0000000..4d5fd94
--- /dev/null
+++ b/backend/genesys.h
@@ -0,0 +1,149 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2005-2013 Stephane Voltz <stef.dev@free.fr>
+ Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
+ Copyright (C) 2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GENESYS_H
+#define GENESYS_H
+
+#include "genesys_low.h"
+
+
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+#define GENESYS_CONFIG_FILE "genesys.conf"
+
+/* Maximum time for lamp warm-up */
+#define WARMUP_TIME 65
+
+#define FLATBED "Flatbed"
+#define TRANSPARENCY_ADAPTER "Transparency Adapter"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/** List of SANE options
+ */
+enum Genesys_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_SOURCE,
+ OPT_PREVIEW,
+ OPT_BIT_DEPTH,
+ OPT_RESOLUTION,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ /* advanced image enhancement options */
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CUSTOM_GAMMA, /* toggle to enable custom gamma tables */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_SWDESKEW,
+ OPT_SWCROP,
+ OPT_SWDESPECK,
+ OPT_DESPECK,
+ OPT_SWSKIP,
+ OPT_SWDEROTATE,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ OPT_EXTRAS_GROUP,
+ OPT_LAMP_OFF_TIME,
+ OPT_LAMP_OFF,
+ OPT_THRESHOLD,
+ OPT_THRESHOLD_CURVE,
+ OPT_DISABLE_DYNAMIC_LINEART,
+ OPT_DISABLE_INTERPOLATION,
+ OPT_COLOR_FILTER,
+ OPT_CALIBRATION_FILE,
+
+ OPT_SENSOR_GROUP,
+ OPT_SCAN_SW,
+ OPT_FILE_SW,
+ OPT_EMAIL_SW,
+ OPT_COPY_SW,
+ OPT_PAGE_LOADED_SW,
+ OPT_OCR_SW,
+ OPT_POWER_SW,
+ OPT_EXTRA_SW,
+ OPT_NEED_CALIBRATION_SW,
+ OPT_BUTTON_GROUP,
+ OPT_CALIBRATE,
+ OPT_CLEAR_CALIBRATION,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+
+/** Scanner object. Should have better be called Session than Scanner
+ */
+typedef struct Genesys_Scanner
+{
+ struct Genesys_Scanner *next; /**< Next scanner in list */
+ Genesys_Device *dev; /**< Low-level device object */
+
+ /* SANE data */
+ SANE_Bool scanning; /**< We are currently scanning */
+ SANE_Option_Descriptor opt[NUM_OPTIONS]; /**< Option descriptors */
+ Option_Value val[NUM_OPTIONS]; /**< Option values */
+ Option_Value last_val[NUM_OPTIONS]; /**< Option values as read by the frontend. used for sensors. */
+ SANE_Parameters params; /**< SANE Parameters */
+ SANE_Int bpp_list[5]; /**< */
+} Genesys_Scanner;
+
+#endif /* not GENESYS_H */
diff --git a/backend/genesys_conv.c b/backend/genesys_conv.c
new file mode 100644
index 0000000..61ac782
--- /dev/null
+++ b/backend/genesys_conv.c
@@ -0,0 +1,478 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005, 2006 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ * Conversion filters for genesys backend
+ */
+
+
+/*8 bit*/
+#define SINGLE_BYTE
+#define BYTES_PER_COMPONENT 1
+#define COMPONENT_TYPE uint8_t
+
+#define FUNC_NAME(f) f ## _8
+
+#include "genesys_conv_hlp.c"
+
+#undef FUNC_NAME
+
+#undef COMPONENT_TYPE
+#undef BYTES_PER_COMPONENT
+#undef SINGLE_BYTE
+
+/*16 bit*/
+#define DOUBLE_BYTE
+#define BYTES_PER_COMPONENT 2
+#define COMPONENT_TYPE uint16_t
+
+#define FUNC_NAME(f) f ## _16
+
+#include "genesys_conv_hlp.c"
+
+#undef FUNC_NAME
+
+#undef COMPONENT_TYPE
+#undef BYTES_PER_COMPONENT
+#undef DOUBLE_BYTE
+
+static SANE_Status
+genesys_reverse_bits(
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ size_t bytes)
+{
+ size_t i;
+ for(i = 0; i < bytes; i++) {
+ *dst_data++ = ~ *src_data++;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * uses the threshold/threshold_curve to control software binarization
+ * This code was taken from the epjistsu backend by m. allan noah
+ * @param dev device set up for the scan
+ * @param src pointer to raw data
+ * @param dst pointer where to store result
+ * @param width width of the processed line
+ * */
+static SANE_Status
+binarize_line(Genesys_Device * dev, uint8_t *src, uint8_t *dst, int width)
+{
+ int j, windowX, sum = 0;
+ int thresh;
+ int offset, addCol, dropCol;
+ unsigned char mask;
+
+ int x;
+ uint8_t min, max;
+
+ /* normalize line */
+ min = 255;
+ max = 0;
+ for (x = 0; x < width; x++)
+ {
+ if (src[x] > max)
+ {
+ max = src[x];
+ }
+ if (src[x] < min)
+ {
+ min = src[x];
+ }
+ }
+
+ /* safeguard against dark or white areas */
+ if(min>80)
+ min=0;
+ if(max<80)
+ max=255;
+ for (x = 0; x < width; x++)
+ {
+ src[x] = ((src[x] - min) * 255) / (max - min);
+ }
+
+ /* ~1mm works best, but the window needs to have odd # of pixels */
+ windowX = (6 * dev->settings.xres) / 150;
+ if (!(windowX % 2))
+ windowX++;
+
+ /* second, prefill the sliding sum */
+ for (j = 0; j < windowX; j++)
+ sum += src[j];
+
+ /* third, walk the input buffer, update the sliding sum, */
+ /* determine threshold, output bits */
+ for (j = 0; j < width; j++)
+ {
+ /* output image location */
+ offset = j % 8;
+ mask = 0x80 >> offset;
+ thresh = dev->settings.threshold;
+
+ /* move sum/update threshold only if there is a curve */
+ if (dev->settings.threshold_curve)
+ {
+ addCol = j + windowX / 2;
+ dropCol = addCol - windowX;
+
+ if (dropCol >= 0 && addCol < width)
+ {
+ sum -= src[dropCol];
+ sum += src[addCol];
+ }
+ thresh = dev->lineart_lut[sum / windowX];
+ }
+
+ /* use average to lookup threshold */
+ if (src[j] > thresh)
+ *dst &= ~mask; /* white */
+ else
+ *dst |= mask; /* black */
+
+ if (offset == 7)
+ dst++;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * software lineart using data from a 8 bit gray scan. We assume true gray
+ * or monochrome scan as input.
+ */
+static SANE_Status
+genesys_gray_lineart(
+ Genesys_Device *dev,
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ size_t pixels,
+ size_t lines,
+ uint8_t threshold)
+{
+ size_t y;
+
+ DBG (DBG_io2, "genesys_gray_lineart: converting %lu lines of %lu pixels\n",
+ (unsigned long)lines, (unsigned long)pixels);
+ DBG (DBG_io2, "genesys_gray_lineart: threshold=%d\n",threshold);
+
+ for (y = 0; y < lines; y++)
+ {
+ binarize_line (dev, src_data + y * pixels, dst_data, pixels);
+ dst_data += pixels / 8;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief shrink or grow scanned data to fit the final scan size
+ * This function shrinks the scanned data it the required resolution is lower than the hardware one,
+ * or grows it in case it is the opposite like when motor resolution is higher than
+ * sensor's one.
+ */
+static SANE_Status
+genesys_shrink_lines_1 (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int src_pixels,
+ unsigned int dst_pixels,
+ unsigned int channels)
+{
+ unsigned int dst_x, src_x, y, c, cnt;
+ unsigned int avg[3], val;
+ uint8_t *src = (uint8_t *) src_data;
+ uint8_t *dst = (uint8_t *) dst_data;
+
+ /* choose between case where me must reduce or grow the scanned data */
+ if (src_pixels > dst_pixels)
+ {
+ /* shrink data */
+ /* TODO action must be taken at bit level, no bytes */
+ src_pixels /= 8;
+ dst_pixels /= 8;
+ /*take first _byte_ */
+ for (y = 0; y < lines; y++)
+ {
+ cnt = src_pixels / 2;
+ src_x = 0;
+ for (dst_x = 0; dst_x < dst_pixels; dst_x++)
+ {
+ while (cnt < src_pixels && src_x < src_pixels)
+ {
+ cnt += dst_pixels;
+
+ for (c = 0; c < channels; c++)
+ avg[c] = *src++;
+ src_x++;
+ }
+ cnt -= src_pixels;
+
+ for (c = 0; c < channels; c++)
+ *dst++ = avg[c];
+ }
+ }
+ }
+ else
+ {
+ /* common case where y res is double x res */
+ for (y = 0; y < lines; y++)
+ {
+ if (2 * src_pixels == dst_pixels)
+ {
+ /* double and interleave on line */
+ for (c = 0; c < src_pixels/8; c++)
+ {
+ /* first 4 bits */
+ val = 0;
+ val |= (*src & 0x80) >> 0; /* X___ ____ --> X___ ____ */
+ val |= (*src & 0x80) >> 1; /* X___ ____ --> _X__ ____ */
+ val |= (*src & 0x40) >> 1; /* _X__ ____ --> __X_ ____ */
+ val |= (*src & 0x40) >> 2; /* _X__ ____ --> ___X ____ */
+ val |= (*src & 0x20) >> 2; /* __X_ ____ --> ____ X___ */
+ val |= (*src & 0x20) >> 3; /* __X_ ____ --> ____ _X__ */
+ val |= (*src & 0x10) >> 3; /* ___X ____ --> ____ __X_ */
+ val |= (*src & 0x10) >> 4; /* ___X ____ --> ____ ___X */
+ *dst = val;
+ dst++;
+
+ /* last for bits */
+ val = 0;
+ val |= (*src & 0x08) << 4; /* ____ X___ --> X___ ____ */
+ val |= (*src & 0x08) << 3; /* ____ X___ --> _X__ ____ */
+ val |= (*src & 0x04) << 3; /* ____ _X__ --> __X_ ____ */
+ val |= (*src & 0x04) << 2; /* ____ _X__ --> ___X ____ */
+ val |= (*src & 0x02) << 2; /* ____ __X_ --> ____ X___ */
+ val |= (*src & 0x02) << 1; /* ____ __X_ --> ____ _X__ */
+ val |= (*src & 0x01) << 1; /* ____ ___X --> ____ __X_ */
+ val |= (*src & 0x01) << 0; /* ____ ___X --> ____ ___X */
+ *dst = val;
+ dst++;
+ src++;
+ }
+ }
+ else
+ {
+ /* TODO: since depth is 1, we must interpolate bit within bytes */
+ DBG (DBG_warn, "%s: inaccurate bit expansion!\n", __FUNCTION__);
+ cnt = dst_pixels / 2;
+ dst_x = 0;
+ for (src_x = 0; src_x < src_pixels; src_x++)
+ {
+ for (c = 0; c < channels; c++)
+ avg[c] = *src++;
+ while (cnt < dst_pixels && dst_x < dst_pixels)
+ {
+ cnt += src_pixels;
+ for (c = 0; c < channels; c++)
+ *dst++ = avg[c];
+ dst_x++;
+ }
+ cnt -= dst_pixels;
+ }
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/** Look in image for likely left/right/bottom paper edges, then crop image.
+ * Since failing to crop isn't fatal, we always return SANE_STATUS_GOOD .
+ */
+static SANE_Status
+genesys_crop(Genesys_Scanner *s)
+{
+ SANE_Status status;
+ Genesys_Device *dev = s->dev;
+ int top = 0;
+ int bottom = 0;
+ int left = 0;
+ int right = 0;
+
+ DBG (DBG_proc, "%s: start\n", __FUNCTION__);
+
+ /* first find edges if any */
+ status = sanei_magic_findEdges (&s->params,
+ dev->img_buffer,
+ dev->settings.xres,
+ dev->settings.yres,
+ &top,
+ &bottom,
+ &left,
+ &right);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "%s: bad or no edges, bailing\n", __FUNCTION__);
+ goto cleanup;
+ }
+ DBG (DBG_io, "%s: t:%d b:%d l:%d r:%d\n", __FUNCTION__, top, bottom, left,
+ right);
+
+ /* now crop the image */
+ status =
+ sanei_magic_crop (&(s->params), dev->img_buffer, top, bottom, left, right);
+ if (status)
+ {
+ DBG (DBG_warn, "%s: failed to crop\n", __FUNCTION__);
+ goto cleanup;
+ }
+
+ /* update counters to new image size */
+ dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+cleanup:
+ DBG (DBG_proc, "%s: completed\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+}
+
+/** Look in image for likely upper and left paper edges, then rotate
+ * image so that upper left corner of paper is upper left of image.
+ * @return since failure doens't prevent scanning, we always return
+ * SANE_STATUS_GOOD
+ */
+static SANE_Status
+genesys_deskew(Genesys_Scanner *s)
+{
+ SANE_Status status;
+ Genesys_Device *dev = s->dev;
+
+ int x = 0, y = 0, bg;
+ double slope = 0;
+
+ DBG (DBG_proc, "%s: start\n", __FUNCTION__);
+
+ bg=0;
+ if(s->params.format==SANE_FRAME_GRAY && s->params.depth == 1)
+ {
+ bg=0xff;
+ }
+ status = sanei_magic_findSkew (&s->params,
+ dev->img_buffer,
+ dev->sensor.optical_res,
+ dev->sensor.optical_res,
+ &x,
+ &y,
+ &slope);
+ if (status!=SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: bad findSkew, bailing\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+ }
+ DBG(DBG_info, "%s: slope=%f => %f\n",__FUNCTION__,slope, (slope/M_PI_2)*90);
+ /* rotate image slope is in [-PI/2,PI/2]
+ * positive values rotate trigonometric direction wise */
+ status = sanei_magic_rotate (&s->params,
+ dev->img_buffer,
+ x,
+ y,
+ slope,
+ bg);
+ if (status!=SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: rotate error: %s", __FUNCTION__, sane_strstatus(status));
+ }
+
+ DBG (DBG_proc, "%s: completed\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+}
+
+/** remove lone dots
+ * @return since failure doens't prevent scanning, we always return
+ * SANE_STATUS_GOOD
+ */
+static SANE_Status
+genesys_despeck(Genesys_Scanner *s)
+{
+ if(sanei_magic_despeck(&s->params,
+ s->dev->img_buffer,
+ s->val[OPT_DESPECK].w)!=SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: bad despeck, bailing\n",__FUNCTION__);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Look if image needs rotation and apply it
+ * */
+static SANE_Status
+genesys_derotate (Genesys_Scanner * s)
+{
+ SANE_Status status;
+ int angle = 0;
+ int resolution = s->val[OPT_RESOLUTION].w;
+
+ DBGSTART;
+ status = sanei_magic_findTurn (&s->params,
+ s->dev->img_buffer,
+ resolution,
+ resolution,
+ &angle);
+
+ if (status)
+ {
+ DBG (DBG_warn, "%s: failed : %d\n", __FUNCTION__, status);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* apply rotation angle found */
+ status = sanei_magic_turn (&s->params, s->dev->img_buffer, angle);
+ if (status)
+ {
+ DBG (DBG_warn, "%s: failed : %d\n", __FUNCTION__, status);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* update counters to new image size */
+ s->dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_conv_hlp.c b/backend/genesys_conv_hlp.c
new file mode 100644
index 0000000..cae4116
--- /dev/null
+++ b/backend/genesys_conv_hlp.c
@@ -0,0 +1,345 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ * Conversion filters for genesys backend
+ */
+
+static SANE_Status
+FUNC_NAME(genesys_reorder_components_cis) (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int pixels)
+{
+ unsigned int x, y;
+ uint8_t *src[3];
+ uint8_t *dst = dst_data;
+ unsigned int rest = pixels * 2 * BYTES_PER_COMPONENT;
+
+ src[0] = src_data + pixels * BYTES_PER_COMPONENT * 0;
+ src[1] = src_data + pixels * BYTES_PER_COMPONENT * 1;
+ src[2] = src_data + pixels * BYTES_PER_COMPONENT * 2;
+
+ for(y = 0; y < lines; y++) {
+ for(x = 0; x < pixels; x++) {
+
+#ifndef DOUBLE_BYTE
+ *dst++ = *src[0]++;
+ *dst++ = *src[1]++;
+ *dst++ = *src[2]++;
+#else
+# ifndef WORDS_BIGENDIAN
+ *dst++ = *src[0]++;
+ *dst++ = *src[0]++;
+ *dst++ = *src[1]++;
+ *dst++ = *src[1]++;
+ *dst++ = *src[2]++;
+ *dst++ = *src[2]++;
+# else
+ *dst++ = src[0][1];
+ *dst++ = src[0][0];
+ *dst++ = src[1][1];
+ *dst++ = src[1][0];
+ *dst++ = src[2][1];
+ *dst++ = src[2][0];
+ src[0] += 2;
+ src[1] += 2;
+ src[2] += 2;
+# endif
+#endif
+ }
+
+ src[0] += rest;
+ src[1] += rest;
+ src[2] += rest;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+FUNC_NAME(genesys_reorder_components_cis_bgr) (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int pixels)
+{
+ unsigned int x, y;
+ uint8_t *src[3];
+ uint8_t *dst = dst_data;
+ unsigned int rest = pixels * 2 * BYTES_PER_COMPONENT;
+
+ src[0] = src_data + pixels * BYTES_PER_COMPONENT * 0;
+ src[1] = src_data + pixels * BYTES_PER_COMPONENT * 1;
+ src[2] = src_data + pixels * BYTES_PER_COMPONENT * 2;
+
+ for(y = 0; y < lines; y++) {
+ for(x = 0; x < pixels; x++) {
+#ifndef DOUBLE_BYTE
+ *dst++ = *src[2]++;
+ *dst++ = *src[1]++;
+ *dst++ = *src[0]++;
+#else
+# ifndef WORDS_BIGENDIAN
+ *dst++ = *src[2]++;
+ *dst++ = *src[2]++;
+ *dst++ = *src[1]++;
+ *dst++ = *src[1]++;
+ *dst++ = *src[0]++;
+ *dst++ = *src[0]++;
+# else
+ *dst++ = src[2][1];
+ *dst++ = src[2][0];
+ *dst++ = src[1][1];
+ *dst++ = src[1][0];
+ *dst++ = src[0][1];
+ *dst++ = src[0][0];
+ src[0] += 2;
+ src[1] += 2;
+ src[2] += 2;
+# endif
+#endif
+ }
+
+ src[0] += rest;
+ src[1] += rest;
+ src[2] += rest;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+FUNC_NAME(genesys_reorder_components_bgr) (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int pixels)
+{
+ unsigned int c;
+ uint8_t *src = src_data;
+ uint8_t *dst = dst_data;
+
+ for(c = 0; c < lines * pixels; c++) {
+
+#ifndef DOUBLE_BYTE
+ *dst++ = src[2];
+ *dst++ = src[1];
+ *dst++ = src[0];
+ src += 3;
+#else
+# ifndef WORDS_BIGENDIAN
+ *dst++ = src[2 * 2 + 0];
+ *dst++ = src[2 * 2 + 1];
+ *dst++ = src[1 * 2 + 0];
+ *dst++ = src[1 * 2 + 1];
+ *dst++ = src[0 * 2 + 0];
+ *dst++ = src[0 * 2 + 1];
+# else
+ *dst++ = src[2 * 2 + 1];
+ *dst++ = src[2 * 2 + 0];
+ *dst++ = src[1 * 2 + 1];
+ *dst++ = src[1 * 2 + 0];
+ *dst++ = src[0 * 2 + 1];
+ *dst++ = src[0 * 2 + 0];
+# endif
+ src += 3 * 2;
+#endif
+
+ }
+ return SANE_STATUS_GOOD;
+}
+
+#if defined(DOUBLE_BYTE) && defined(WORDS_BIGENDIAN)
+static SANE_Status
+FUNC_NAME(genesys_reorder_components_endian) (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int pixels,
+ unsigned int channels)
+{
+ unsigned int c;
+ uint8_t *src = src_data;
+ uint8_t *dst = dst_data;
+
+ for(c = 0; c < lines * pixels * channels; c++) {
+ *dst++ = src[1];
+ *dst++ = src[0];
+ src += 2;
+ }
+return SANE_STATUS_GOOD;
+}
+#endif /*defined(DOUBLE_BYTE) && defined(WORDS_BIGENDIAN)*/
+
+
+static SANE_Status
+FUNC_NAME(genesys_reverse_ccd) (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int components_per_line,
+ unsigned int *ccd_shift,
+ unsigned int component_count)
+{
+ unsigned int x, y, c;
+ COMPONENT_TYPE *src = (COMPONENT_TYPE *)src_data;
+ COMPONENT_TYPE *dst = (COMPONENT_TYPE *)dst_data;
+ COMPONENT_TYPE *srcp;
+ COMPONENT_TYPE *dstp;
+ unsigned int pitch = components_per_line;
+ unsigned int ccd_shift_pitch[12];
+ unsigned int *csp;
+
+ for (c = 0; c < component_count; c++)
+ ccd_shift_pitch[c] = ccd_shift[c] * pitch;
+
+/*
+ * cache efficiency:
+ we are processing a single line component_count times, so it should fit
+ into the cpu cache for maximum efficiency. our lines take
+ maximum 252kb(3 channels, 16bit, 2400dpi, full gl841 shading range)
+ * instruction efficiency:
+ the innermost loop runs long and consists of 3 adds, one compare,
+ 2 derefences.
+ */
+/*
+ for (y = 0; y < lines; y++) {
+ csp = ccd_shift_pitch;
+ for (c = 0; c < component_count; c++) {
+ srcp = src + c + *csp++;
+ dstp = dst + c;
+ for (x = 0; x < pitch; x += component_count) {
+ *dstp = *srcp;
+ srcp += component_count;
+ dstp += component_count;
+ }
+ }
+ dst += pitch;
+ src += pitch;
+ }
+ */
+/*
+ * cache efficency:
+ here only line_dist_pitch needs to stay in cache. 12*4 = 48 bytes
+ * instruction efficiency:
+ we have a short running inner loop, consisting of 4 incs, 2 compare, 1 add,
+ 2 dereference and 1 indexed dereference.
+ the enclosing loop is long running, consisting of 1 add, 1 compare.
+ */
+ srcp = src;
+ dstp = dst;
+ for (y = 0; y < lines; y++) {
+ for (x = 0; x < pitch; x += component_count) {
+ csp = ccd_shift_pitch;
+ for (c = 0; c < component_count && c + x < pitch; c++) {
+ *dstp = srcp[*csp++];
+ dstp++;
+ srcp++;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+FUNC_NAME(genesys_shrink_lines) (
+ uint8_t *src_data,
+ uint8_t *dst_data,
+ unsigned int lines,
+ unsigned int src_pixels,
+ unsigned int dst_pixels,
+ unsigned int channels)
+{
+ unsigned int dst_x, src_x, y, c, cnt;
+ unsigned int avg[3];
+ unsigned int count;
+ COMPONENT_TYPE *src = (COMPONENT_TYPE *)src_data;
+ COMPONENT_TYPE *dst = (COMPONENT_TYPE *)dst_data;
+
+ if (src_pixels > dst_pixels) {
+/*average*/
+ for (c = 0; c < channels; c++)
+ avg[c] = 0;
+ for(y = 0; y < lines; y++) {
+ cnt = src_pixels / 2;
+ src_x = 0;
+ for (dst_x = 0; dst_x < dst_pixels; dst_x++) {
+ count = 0;
+ while (cnt < src_pixels && src_x < src_pixels) {
+ cnt += dst_pixels;
+
+ for (c = 0; c < channels; c++)
+ avg[c] += *src++;
+ src_x++;
+ count++;
+ }
+ cnt -= src_pixels;
+
+ for (c = 0; c < channels; c++) {
+ *dst++ = avg[c] / count;
+ avg[c] = 0;
+ }
+ }
+ }
+ } else {
+/*interpolate. copy pixels*/
+ for(y = 0; y < lines; y++) {
+ cnt = dst_pixels / 2;
+ dst_x = 0;
+ for (src_x = 0; src_x < src_pixels; src_x++) {
+ for (c = 0; c < channels; c++)
+ avg[c] = *src++;
+ while ((cnt < dst_pixels || src_x + 1 == src_pixels) &&
+ dst_x < dst_pixels) {
+ cnt += src_pixels;
+
+ for (c = 0; c < channels; c++)
+ *dst++ = avg[c];
+ dst_x++;
+ }
+ cnt -= dst_pixels;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/genesys_devices.c b/backend/genesys_devices.c
new file mode 100644
index 0000000..fb3cd43
--- /dev/null
+++ b/backend/genesys_devices.c
@@ -0,0 +1,3415 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003-2005 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2007 Luke <iceyfor@gmail.com>
+ Copyright (C) 2010 Jack McGill <jmcgill85258@yahoo.com>
+ Copyright (C) 2010 Andrey Loginov <avloginov@gmail.com>,
+ xerox travelscan device entry
+ Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
+ for Plustek Opticbook 3600 support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ------------------------------------------------------------------------ */
+/* Some setup DAC and CCD tables */
+/* ------------------------------------------------------------------------ */
+
+/** Setup table for various scanners using a Wolfson DAC
+ */
+static Genesys_Frontend Wolfson[] = {
+ { DAC_WOLFSON_UMAX, {0x00, 0x03, 0x05, 0x11}
+ , {0x00, 0x00, 0x00}
+ , {0x80, 0x80, 0x80}
+ , {0x02, 0x02, 0x02}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* 0: UMAX */
+ {DAC_WOLFSON_ST12, {0x00, 0x03, 0x05, 0x03}
+ , {0x00, 0x00, 0x00}
+ , {0xc8, 0xc8, 0xc8}
+ , {0x04, 0x04, 0x04}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* 1: ST12 */
+ {DAC_WOLFSON_ST24,{0x00, 0x03, 0x05, 0x21}
+ , {0x00, 0x00, 0x00}
+ , {0xc8, 0xc8, 0xc8}
+ , {0x06, 0x06, 0x06}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* 2: ST24 */
+ {DAC_WOLFSON_5345,{0x00, 0x03, 0x05, 0x12}
+ , {0x00, 0x00, 0x00}
+ , {0xb8, 0xb8, 0xb8}
+ , {0x04, 0x04, 0x04}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* 3: MD6228/MD6471 */
+ {DAC_WOLFSON_HP2400,
+ /* reg0 reg1 reg2 reg3 */
+ {0x00, 0x03, 0x05, 0x02} /* reg3=0x02 for 50-600 dpi, 0x32 (0x12 also works well) at 1200 */
+ , {0x00, 0x00, 0x00}
+ , {0xb4, 0xb6, 0xbc}
+ , {0x06, 0x09, 0x08}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* 4: HP2400c */
+ {DAC_WOLFSON_HP2300,
+ {0x00, 0x03, 0x04, 0x02}
+ , {0x00, 0x00, 0x00}
+ , {0xbe, 0xbe, 0xbe}
+ , {0x04, 0x04, 0x04}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* 5: HP2300c */
+ {DAC_CANONLIDE35,{0x00, 0x3d, 0x08, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0xe1, 0xe1, 0xe1}
+ , {0x93, 0x93, 0x93}
+ , {0x00, 0x19, 0x06}
+ }
+ , /* 6: CANONLIDE35 */
+ {DAC_AD_XP200,
+ {0x58, 0x80, 0x00, 0x00} /* reg1=0x80 ? */
+ , {0x00, 0x00, 0x00}
+ , {0x09, 0x09, 0x09}
+ , {0x09, 0x09, 0x09}
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_WOLFSON_XP300,{0x00, 0x35, 0x20, 0x14} /* 7: XP300 */
+ , {0x00, 0x00, 0x00}
+ , {0xe1, 0xe1, 0xe1}
+ , {0x93, 0x93, 0x93}
+ , {0x07, 0x00, 0x00}
+ }
+ , /* 8: HP3670 */
+ {DAC_WOLFSON_HP3670,
+ /* reg0 reg1 reg2 reg3 */
+ {0x00, 0x03, 0x05, 0x32} /* reg3=0x32 for 100-300 dpi, 0x12 at 1200 */
+ , {0x00, 0x00, 0x00} /* sign */
+ , {0xba, 0xb8, 0xb8} /* offset */
+ , {0x06, 0x05, 0x04} /* gain 4,3,2 at 1200 ?*/
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_WOLFSON_DSM600,{0x00, 0x35, 0x20, 0x14} /* 9: DSMOBILE600 */
+ , {0x00, 0x00, 0x00}
+ , {0x85, 0x85, 0x85}
+ , {0xa0, 0xa0, 0xa0}
+ , {0x07, 0x00, 0x00}
+ }
+ ,
+ {DAC_CANONLIDE200,
+ {0x9d, 0x91, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x3f, 0x00} /* 0x00 0x3f 0x00 : offset/brigthness ? */
+ , {0x32, 0x04, 0x00}
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_CANONLIDE700,
+ {0x9d, 0x9e, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x3f, 0x00} /* 0x00 0x3f 0x00 : offset/brigthness ? */
+ , {0x2f, 0x04, 0x00}
+ , {0x00, 0x00, 0x00}
+ }
+ , /* KV-SS080 */
+ {DAC_KVSS080,
+ {0x00, 0x23, 0x24, 0x0f}
+ , {0x00, 0x00, 0x00}
+ , {0x80, 0x80, 0x80}
+ , {0x4b, 0x4b, 0x4b}
+ , {0x00,0x00,0x00}
+ }
+ ,
+ {DAC_G4050,
+ {0x00, 0x23, 0x24, 0x1f}
+ , {0x00, 0x00, 0x00}
+ , {0x45, 0x45, 0x45} /* 0x20, 0x21, 0x22 */
+ , {0x4b, 0x4b, 0x4b} /* 0x28, 0x29, 0x2a */
+ , {0x00,0x00,0x00}
+ }
+ ,
+ {DAC_CANONLIDE110,
+ {0x80, 0x8a, 0x23, 0x4c}
+ , {0x00, 0xca, 0x94}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_PLUSTEK_3600,
+ {0x70, 0x80, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x3f, 0x3d, 0x3d}
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_CS8400F,
+ {0x00, 0x23, 0x24, 0x0f}
+ , {0x00, 0x00, 0x00}
+ , {0x60, 0x5c, 0x6c} /* 0x20, 0x21, 0x22 */
+ , {0x8a, 0x9f, 0xc2} /* 0x28, 0x29, 0x2a */
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_IMG101,
+ {0x78, 0xf0, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x00, 0x00} /* 0x20, 0x21, 0x22 */
+ , {0x00, 0x00, 0x00} /* 0x28, 0x29, 0x2a */
+ , {0x00, 0x00, 0x00}
+ }
+ ,
+ {DAC_PLUSTEK3800,
+ {0x78, 0xf0, 0x00, 0x00}
+ , {0x00, 0x00, 0x00}
+ , {0x00, 0x00, 0x00} /* 0x20, 0x21, 0x22 */
+ , {0x00, 0x00, 0x00} /* 0x28, 0x29, 0x2a */
+ , {0x00, 0x00, 0x00}
+ }
+};
+
+
+/** for setting up the sensor-specific settings:
+ * Optical Resolution, number of black pixels, number of dummy pixels,
+ * CCD_start_xoffset, and overall number of sensor pixels
+ * registers 0x08-0x0b, 0x10-0x1d and 0x52-0x5e
+ */
+static Genesys_Sensor Sensor[] = {
+ /* 0: UMAX */
+ {CCD_UMAX,
+ 1200, 48, 64, 0, 10800, 210, 230,
+ {0x01, 0x03, 0x05, 0x07}
+ ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x05, 0x31, 0x2a, 0x00, 0x00,
+ 0x00, 0x02}
+ ,
+ {0x13, 0x17, 0x03, 0x07, 0x0b, 0x0f, 0x23, 0x00, 0xc1, 0x00, 0x00, 0x00,
+ 0x00}
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}}
+ ,
+ /* 1: Plustek OpticPro S12/ST12 */
+ {CCD_ST12,
+ 600, 48, 85, 152, 5416, 210, 230,
+ {0x02, 0x00, 0x06, 0x04} ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x08, 0x20, 0x2a, 0x00, 0x00, 0x0c, 0x03} ,
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00} ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 2: Plustek OpticPro S24/ST24 */
+ {CCD_ST24,
+ 1200,
+ 48, 64, 0, 10800, 210, 230,
+ {0x0e, 0x0c, 0x00, 0x0c} ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x08, 0x31, 0x2a, 0x00, 0x00, 0x00, 0x02} ,
+ {0x17, 0x03, 0x07, 0x0b, 0x0f, 0x13, 0x03, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00} ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 3: MD6471 */
+ {CCD_5345,
+ 1200,
+ 48,
+ 16, 0, 10872,
+ 190, 190,
+ {0x0d, 0x0f, 0x11, 0x13} ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0a, 0x30, 0x2a, 0x00, 0x00, 0x00, 0x03} ,
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x23, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00} ,
+ {2.38, 2.35, 2.34},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 4: HP2400c */
+ {CCD_HP2400,
+ 1200,
+ 48,
+ 15, 0, 10872, 210, 200,
+ {0x14, 0x15, 0x00, 0x00} /* registers 0x08-0x0b */ ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x08, 0x3f, 0x2a, 0x00, 0x00, 0x00, 0x02} ,
+ {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63, 0x00, 0xc1, 0x00, 0x0e, 0x00, 0x00} ,
+ {2.1, 2.1, 2.1},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 5: HP2300c */
+ {CCD_HP2300,
+ 600,
+ 48,
+ 20, 0, 5368, 180, 180, /* 5376 */
+ {0x16, 0x00, 0x01, 0x03} ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x0a, 0x20, 0x2a, 0x6a, 0x8a, 0x00, 0x05} ,
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x00, 0xc1, 0x06, 0x0b, 0x10, 0x16} ,
+ {2.1, 2.1, 2.1},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* CANOLIDE35 */
+ {CCD_CANONLIDE35, 1200,
+ 87, /*(black) */
+ 87, /* (dummy) */
+ 0, /* (startxoffset) */
+ 10400, /*sensor_pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x50,
+ 0x00, 0x00, 0x00, 0x02 /* TODO(these do no harm, but may be neccessery for CCD) */
+ },
+ {0x05, 0x07,
+ 0x00, 0x00, 0x00, 0x00, /*[GB](HI|LOW) not needed for cis */
+ 0x3a, 0x03,
+ 0x40, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x00 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 7: Strobe XP200 */
+ {CIS_XP200, 600,
+ 5,
+ 38, 0, 5200, 200, 200, /* 5125 */
+ {0x16, 0x00, 0x01, 0x03} ,
+ {0x14, 0x50, 0x0c, 0x80, 0x0a, 0x28, 0xb7, 0x0a, 0x20, 0x2a, 0x6a, 0x8a, 0x00, 0x05} ,
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x00, 0xc1, 0x06, 0x0b, 0x10, 0x16} ,
+ {2.1, 2.1, 2.1},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* HP3670 */
+ {CCD_HP3670,1200,
+ 48,
+ 16, 0, 10872,
+ 210, 200,
+ {0x00, 0x0a, 0x0b, 0x0d} ,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x07, 0x20, 0x2a, 0x00, 0x00, 0xc0, 0x43} ,
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x00, 0x15, 0x05, 0x0a, 0x0f, 0x00},
+ {1.00, 1.00, 1.00},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* Syscan DP 665 */
+ {CCD_DP665, 600,
+ 27, /*(black) */
+ 27, /* (dummy) */
+ 0, /* (startxoffset) */
+ 2496, /*sensor_pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x00, 0x02, 0x04, 0x50,
+ 0x10, 0x00, 0x20, 0x02
+ },
+ {0x04, 0x05,
+ 0x00, 0x00, 0x00, 0x00, /*[GB](HI|LOW) not needed for cis */
+ 0x54, 0x03,
+ 0x00, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x01 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* Visioneer Roadwarrior */
+ {CCD_ROADWARRIOR, 600,
+ 27, /*(black) */
+ 27, /* (dummy) */
+ 0, /* (startxoffset) */
+ 5200, /*sensor_pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x00, 0x02, 0x04, 0x50,
+ 0x10, 0x00, 0x20, 0x02
+ },
+ {0x04, 0x05,
+ 0x00, 0x00, 0x00, 0x00, /*[GB](HI|LOW) not needed for cis */
+ 0x54, 0x03,
+ 0x00, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x01 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+,
+ /* Pentax DS Mobile 600 */
+ {CCD_DSMOBILE600, 600,
+ 28, /*(black) */
+ 28, /* (dummy) */
+ 0, /* (startxoffset) */
+ 5200, /*sensor_pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x15, 0x44, 0x15, 0x44, 0x15, 0x44, 0x00, 0x02, 0x04, 0x50,
+ 0x10, 0x00, 0x20, 0x02
+ },
+ {0x04, 0x05,
+ 0x00, 0x00, 0x00, 0x00, /*[GB](HI|LOW) not needed for cis */
+ 0x54, 0x03,
+ 0x00, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x01 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 13: Strobe XP300 */
+ {CCD_XP300, 600,
+ 27, /*(black) */
+ 27, /* (dummy) */
+ 0, /* (startxoffset) */
+ 10240, /*sensor_pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x00, 0x02, 0x04, 0x50,
+ 0x10, 0x00, 0x20, 0x02
+ },
+ {0x04, 0x05,
+ 0x00, 0x00, 0x00, 0x00, /*[GB](HI|LOW) not needed for cis */
+ 0x54, 0x03,
+ 0x00, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x01 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* 13: Strobe XP300 */
+ {CCD_DP685, 600,
+ 27, /*(black) */
+ 27, /* (dummy) */
+ 0, /* (startxoffset) */
+ 5020, /*sensor_pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x00, 0x02, 0x04, 0x50,
+ 0x10, 0x00, 0x20, 0x02
+ },
+ {0x04, 0x05,
+ 0x00, 0x00, 0x00, 0x00, /*[GB](HI|LOW) not needed for cis */
+ 0x54, 0x03,
+ 0x00, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x01 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* CANONLIDE200 */
+ {CIS_CANONLIDE200,
+ 4800, /* optical resolution */
+ 87*4, /* black pixels */
+ 16*4, /* dummy pixels */
+ 320*8, /* CCD_startx_offset 323 */
+ 5136*8,
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ /* reg 0x10 - 0x1d */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* EXPR/EXPG/EXPB */
+ 0x10, 0x08, 0x00, 0xff, 0x34, 0x00, 0x02, 0x04 },
+ /* reg 0x52 - 0x5e */
+ {0x03, 0x07,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x2a, 0xe1,
+ 0x55,
+ 0x00, 0x00, 0x00,
+ 0x41
+ }
+ ,
+ {1.7, 1.7, 1.7},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* CANONLIDE700 */
+ {CIS_CANONLIDE700,
+ 4800, /* optical resolution */
+ 73*8, /* black pixels 73 at 600 dpi */
+ 16*8, /* dummy pixels */
+ 384*8, /* CCD_startx_offset 384 at 600 dpi */
+ 5188*8, /* 8x5570 segments , 5187+1 for rounding */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ /* reg 0x10 - 0x1d */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* EXPR/EXPG/EXPB */
+ 0x10, 0x08, 0x00, 0xff, 0x34, 0x00, 0x02, 0x04 },
+ /* reg 0x52 - 0x5e */
+ {0x07, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x2a, 0xe1,
+ 0x55,
+ 0x00, 0x00, 0x00,
+ 0x41
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* CANONLIDE100 */
+ {CIS_CANONLIDE100,
+ 2400, /* optical resolution */
+ 87*4, /* black pixels */
+ 16*4, /* dummy pixels 16 */
+ 320*4, /* 323 */
+ 5136*4, /* 10272 */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ /* reg 0x10 - 0x15 */
+ {0x01, 0xc1, 0x01, 0x26, 0x00, 0xe5, /* EXPR/EXPG/EXPB */
+ /* reg 0x16 - 0x1d 0x19=0x50*/
+ 0x10, 0x08, 0x00, 0x50, 0x34, 0x00, 0x02, 0x04 },
+ /* reg 0x52 - 0x5e */
+ {0x03, 0x07,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x2a, 0xe1,
+ 0x55,
+ 0x00, 0x00, 0x00,
+ 0x41
+ }
+ ,
+ {1.7, 1.7, 1.7},
+ {NULL, NULL, NULL}
+ }
+ ,
+ {CCD_KVSS080,
+ 600,
+ 38, /* black pixels on left */
+ 38, /* 36 dummy pixels */
+ 152,
+ 5376, /* 5100-> 5200 */
+ 160, /* TAU white ref */
+ 160, /* gain white ref */
+ /* 08 09 0a 0b */
+ {0x00, 0x00, 0x00, 0x6a} ,
+ /* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x1c, 0x00, 0x2a, 0x2c, 0x00, 0x20, 0x04} , /* 18=00 at 600 dpi */
+ /* 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e */
+ {0x0c, 0x0f, 0x00, 0x03, 0x06, 0x09, 0x6b, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x23} ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ {CCD_G4050,
+ 4800,
+ 50*8, /* black_pixels */
+ 58, /* 31 at 600 dpi dummy_pixels 58 at 1200 */
+ 152,
+ 5360*8, /* 5360 max at 600 dpi */
+ 160,
+ 160,
+ /* 08 09 0a 0b */
+ {0x00, 0x00, 0x18, 0x69} ,
+ /* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d */
+ {0x2c, 0x09, 0x22, 0xb8, 0x10, 0xf0, 0x33, 0x0c, 0x00, 0x2a, 0x30, 0x00, 0x00, 0x08} ,
+ /* 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e */
+ {0x0b, 0x0e, 0x11, 0x02, 0x05, 0x08, 0x63, 0x00, 0x40, 0x00, 0x00, 0x00, 0x6f} ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ {CCD_CS4400F,
+ 4800,
+ 50*8, /* black_pixels */
+ 20, /* 31 at 600 dpi dummy_pixels 58 at 1200 */
+ 152,
+ 5360*8, /* 5360 max at 600 dpi */
+ 160,
+ 160,
+ /* 08 09 0a 0b */
+ {0x00, 0x00, 0x18, 0x69} ,
+ /* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d */
+ {0x9c, 0x40, 0x9c, 0x40, 0x9c, 0x40, 0x13, 0x0a, 0x10, 0x2a, 0x30, 0x00, 0x00, 0x6b},
+ /* 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e */
+ {0x0a, 0x0d, 0x00, 0x03, 0x06, 0x08, 0x5b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3f},
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ {CCD_CS8400F,
+ 4800,
+ 50*8, /* black_pixels */
+ 20, /* 31 at 600 dpi dummy_pixels 58 at 1200 */
+ 152,
+ 5360*8, /* 5360 max at 600 dpi */
+ 160,
+ 160,
+ /* 08 09 0a 0b */
+ {0x00, 0x00, 0x18, 0x69} ,
+ /* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d */
+ {0x9c, 0x40, 0x9c, 0x40, 0x9c, 0x40, 0x13, 0x0a, 0x10, 0x2a, 0x30, 0x00, 0x00, 0x6b},
+ /* 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e */
+ {0x0a, 0x0d, 0x00, 0x03, 0x06, 0x08, 0x5b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3f},
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* HP N6310 */
+ {CCD_HP_N6310,
+ 2400,
+ 96,
+ 26,
+ 128,
+ 42720,
+ 210,
+ 230,
+ /* 08 09 0a 0b */
+ {0x00, 0x10, 0x10, 0x0c} ,
+ /* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x0c, 0x02, 0x2a, 0x30, 0x00, 0x00, 0x08} ,
+ /* 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e */
+ {0x0b, 0x0e, 0x11, 0x02, 0x05, 0x08, 0x63, 0x00, 0x40, 0x00, 0x00, 0x06, 0x6f} ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}
+ }
+ ,
+
+ /* CANONLIDE110 */
+ {CIS_CANONLIDE110,
+ 2400, /* optical resolution */
+ 87, /* black pixels */
+ 16, /* dummy pixels 16 */
+ 303, /* 303 */
+ 5168*4, /* total pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ /* reg 0x10 - 0x15 : EXPR, EXPG and EXPB */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg 0x16 - 0x1d */
+ 0x10, 0x04, 0x00, 0x01, 0x30, 0x00, 0x02, 0x01 },
+ /* reg 0x52 - 0x5e */
+ {
+ 0x00, 0x02, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04,
+ 0x1a, 0x00, 0xc0, 0x00, 0x00
+ }
+ ,
+ {2.1, 2.1, 2.1},
+ {NULL, NULL, NULL}}
+ ,
+ /* CANON LIDE 210 sensor */
+ {CIS_CANONLIDE210,
+ 2400, /* optical resolution */
+ 87, /* black pixels */
+ 16, /* dummy pixels 16 */
+ 303, /* 303 */
+ 5168*4, /* total pixels */
+ 210,
+ 200,
+ {0x00, 0x00, 0x00, 0x00},
+ /* reg 0x10 - 0x15 : EXPR, EXPG and EXPB */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg 0x16 - 0x1d */
+ 0x10, 0x04, 0x00, 0x01, 0x30, 0x00, 0x02, 0x01 },
+ /* reg 0x52 - 0x5e */
+ {
+ 0x00, 0x02, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04,
+ 0x1a, 0x00, 0xc0, 0x00, 0x00
+ }
+ ,
+ {2.1, 2.1, 2.1},
+ {NULL, NULL, NULL}}
+ ,
+ {CCD_PLUSTEK_3600,
+ 1200,
+ 87, /*(black) */
+ 87, /* (dummy) */
+ 0, /* (startxoffset) */
+ 10100, /*sensor_pixels */
+ 210,
+ 230,
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x0b, 0x11, 0x2a,
+ 0x00, 0x00, 0x00, 0xc4 /* TODO(these do no harm, but may be neccessery for CCD) */
+ },
+ {0x07, 0x0a,
+ 0x0c, 0x00, 0x02, 0x06, /*[GB](HI|LOW) not needed for cis */
+ 0x22, 0x69,
+ 0x40, /*TODO: bit7 */
+ 0x00, 0x00, 0x00, 0x02 /*TODO (these do no harm, but may be neccessery for CCD) */
+ }
+ ,
+ {1.0, 1.0, 1.0},
+ {NULL, NULL, NULL}}
+ ,
+ /* Canon Image formula 101 */
+ {CCD_IMG101,
+ 1200, /* optical resolution */
+ 31,
+ 31,
+ 0,
+ 10800,
+ 210,
+ 200,
+ {0x60, 0x00, 0x00, 0x8b},
+ /* reg 0x10 - 0x15 */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* EXPR/EXPG/EXPB */
+ /* reg 0x16 - 0x1d 0x19=0x50*/
+ 0xbb, 0x13, 0x10, 0x2a, 0x34, 0x00, 0x20, 0x06 },
+ /* reg 0x52 - 0x5e */
+ {0x02, 0x04,
+ 0x06, 0x08, 0x0a, 0x00,
+ 0x59, 0x31,
+ 0x40,
+ 0x00, 0x00, 0x00,
+ 0x1f
+ }
+ ,
+ {1.7, 1.7, 1.7},
+ {NULL, NULL, NULL}
+ }
+ ,
+ /* Plustek OpticBook 3800 */
+ {CCD_PLUSTEK3800,
+ 1200, /* optical resolution */
+ 31,
+ 31,
+ 0,
+ 10200,
+ 210,
+ 200,
+ {0x60, 0x00, 0x00, 0x8b},
+ /* reg 0x10 - 0x15 */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* EXPR/EXPG/EXPB */
+ /* reg 0x16 - 0x1d 0x19=0x50*/
+ 0xbb, 0x13, 0x10, 0x2a, 0x34, 0x00, 0x20, 0x06 },
+ /* reg 0x52 - 0x5e */
+ {0x02, 0x04,
+ 0x06, 0x08, 0x0a, 0x00,
+ 0x59, 0x31,
+ 0x40,
+ 0x00, 0x00, 0x00,
+ 0x1f
+ }
+ ,
+ {1.7, 1.7, 1.7},
+ {NULL, NULL, NULL}
+ }
+};
+
+/** for General Purpose Output specific settings:
+ * initial GPO value (registers 0x66-0x67/0x6c-0x6d)
+ * GPO enable mask (registers 0x68-0x69/0x6e-0x6f)
+ * The first register is for GPIO9-GPIO16, the second for GPIO1-GPIO8
+ */
+static Genesys_Gpo Gpo[] = {
+ /* UMAX */
+ {GPO_UMAX,
+ {0x11, 0x00}
+ ,
+ {0x51, 0x20}
+ ,
+ }
+ ,
+ /* Plustek OpticPro S12/ST12 */
+ {GPO_ST12,
+ {0x11, 0x00}
+ ,
+ {0x51, 0x20}
+ ,
+ }
+ ,
+ /* Plustek OpticPro S24/ST24 */
+ {GPO_ST24,
+ {0x00, 0x00}
+ ,
+ {0x51, 0x20}
+ ,
+ }
+ ,
+ /* MD5345/MD6471 */
+ {GPO_5345,
+ {0x30, 0x18}
+ , /* bits 11-12 are for bipolar V-ref input voltage */
+ {0xa0, 0x18}
+ ,
+ }
+ ,
+ /* HP2400C */
+ {GPO_HP2400,
+ {0x30, 0x00}
+ ,
+ {0x31, 0x00}
+ ,
+ }
+ ,
+ /* HP2300C */
+ {GPO_HP2300,
+ {0x00, 0x00}
+ ,
+ {0x00, 0x00}
+ ,
+ }
+ ,
+ /* CANONLIDE35 */
+ {GPO_CANONLIDE35,
+ {0x12, 0x80}
+ ,
+ {0xef, 0x80}
+ ,
+ }
+ ,
+ /* 7: XP200 */
+ {GPO_XP200,
+ {0x30, 0x00}
+ ,
+ {0xb0, 0x00}
+ ,
+ },
+ /* HP3670 */
+ {GPO_HP3670,
+ {0x00, 0x00}
+ ,
+ {0x00, 0x00}
+ }
+ ,
+ /* 8: XP300 */
+ {GPO_XP300,
+ {0x09, 0xc6},
+ {0xbb, 0x00},
+ }
+ ,
+ /* Syscan DP 665 */
+ {
+ GPO_DP665,
+ {0x18, 0x00},/*0x19,0x00*/
+ {0xbb, 0x00},
+ }
+ ,
+ /* Syscan DP 685 */
+ {
+ GPO_DP685,
+ {0x3f, 0x46}, /* 6c, 6d */
+ {0xfb, 0x00}, /* 6e, 6f */
+ },
+ /* CANONLIDE200 */
+ {GPO_CANONLIDE200,
+ {0xfb, 0x20}, /* 0xfb when idle , 0xf9/0xe9 (1200) when scanning */
+ {0xff, 0x00},
+ },
+ /* CANONLIDE700 */
+ {GPO_CANONLIDE700,
+ {0xdb, 0xff},
+ {0xff, 0x80},
+ },
+ {GPO_KVSS080,
+ {0xf5, 0x20},
+ {0x7e, 0xa1},
+ }
+ ,
+ {GPO_G4050,
+ {0x20, 0x00},
+ {0xfc, 0x00},
+ }
+ ,
+ /* HP N6310 */
+ {GPO_HP_N6310,
+ {0xa3, 0x00},
+ {0x7f, 0x00},
+ }
+ ,
+ /* CANONLIDE110 */
+ {GPO_CANONLIDE110,
+ {0xfb, 0x20},
+ {0xff, 0x00},
+ }
+ ,
+ /* CANONLIDE210 */
+ {GPO_CANONLIDE210,
+ {0xfb, 0x20},
+ {0xff, 0x00},
+ }
+ ,
+ /* Plustek 3600 */
+ {GPO_PLUSTEK_3600,
+ {0x02, 0x00},
+ {0x1e, 0x80},
+ }
+ /* CanoScan 4400f */
+ ,
+ {GPO_CS4400F,
+ {0x01, 0x7f},
+ {0xff, 0x00},
+ }
+ /* CanoScan 8400f */
+ ,
+ {GPO_CS8400F,
+ {0x9a, 0xdf},
+ {0xfe, 0x60},
+ }
+ /* Canon Image formula 101 */
+ ,
+ {GPO_IMG101,
+ {0x41, 0xa4},
+ {0x13, 0xa7}
+ }
+ /* Plustek OpticBook 3800 */
+ ,
+ {GPO_PLUSTEK3800,
+ {0x41, 0xa4},
+ {0x13, 0xa7}
+ }
+};
+
+static Genesys_Motor Motor[] = {
+ /* UMAX */
+ {MOTOR_UMAX,
+ 1200, /* motor base steps */
+ 2400, /* maximum motor resolution */
+ 1, /* maximum step mode */
+ 1, /* number of power modes*/
+ {{{
+ 11000, /* maximum start speed */
+ 3000, /* maximum end speed */
+ 128, /* step count */
+ 1.0, /* nonlinearity */
+ },
+ {
+ 11000,
+ 3000,
+ 128,
+ 1.0,
+ },},},
+ },
+ {MOTOR_5345, /* MD5345/6228/6471 */
+ 1200,
+ 2400,
+ 1,
+ 1,
+ {{{
+ 2000,
+ 1375,
+ 128,
+ 0.5,
+ },
+ {
+ 2000,
+ 1375,
+ 128,
+ 0.5,
+ },},},
+ },
+ {MOTOR_ST24, /* ST24 */
+ 2400,
+ 2400,
+ 1,
+ 1,
+ {{{
+ 2289,
+ 2100,
+ 128,
+ 0.3,
+ },
+ {
+ 2289,
+ 2100,
+ 128,
+ 0.3,
+ },},},
+ },
+ {MOTOR_HP3670, /* HP 3670 */
+ 1200,
+ 2400,
+ 1,
+ 1,
+ {{{
+ 11000, /* start speed */
+ 3000, /* max speed */
+ 128, /* min steps */
+ 0.25,
+ },
+ {
+ 11000,
+ 3000,
+ 128,
+ 0.5,
+ },},},
+ },
+ {MOTOR_HP2400, /* HP 2400c */
+ 1200,
+ 1200,
+ 1,
+ 1,
+ {{{
+ 11000, /* start speed */
+ 3000, /* max speed */
+ 128, /* min steps */
+ 0.25,
+ },
+ {
+ 11000,
+ 3000,
+ 128,
+ 0.5,
+ },},},
+ },
+ {MOTOR_HP2300, /* HP 2300c */
+ 600, /* 600/1200 */
+ 1200,
+ 1,
+ 1,
+ {{{
+ 3200,
+ 1200,
+ 128,
+ 0.5,
+ },
+ {
+ 3200,
+ 1200,
+ 128,
+ 0.5,
+ },},},
+ },
+ {MOTOR_CANONLIDE35, /* Canon LiDE 35 */
+ 1200,
+ 2400,
+ 1,
+ 1,
+ {{{
+ 3500,
+ 1300,
+ 60,
+ 0.8,
+ },
+ {
+ 3500,
+ 1400,
+ 60,
+ 0.8,
+ },},},
+ },
+ {MOTOR_XP200, /* Strobe XP200 */
+ 600,
+ 600,
+ 1,
+ 1,
+ {{{
+ 3500,
+ 1300,
+ 60,
+ 0.25,
+ },
+ {
+ 3500,
+ 1400,
+ 60,
+ 0.5,
+ },},},
+ },
+ {MOTOR_XP300, /* 7: Visioneer Strobe XP300 */
+ 300,
+ 600,
+ 1,
+ 1,
+ {{{ /* works best with GPIO10, GPIO14 off */
+ 3700,
+ 3700,
+ 2,
+ 0.8,
+ },
+ {
+ 11000,
+ 11000,
+ 2,
+ 0.8,
+ },},},
+ },
+ {MOTOR_DP665, /* Syscan DP 665 */
+ 750,
+ 1500,
+ 1,
+ 1,
+ {{{
+ 3000,
+ 2500,
+ 10,
+ 0.8,
+ },
+ {
+ 11000,
+ 11000,
+ 2,
+ 0.8,
+ },},},
+ },
+ {MOTOR_ROADWARRIOR, /* Visioneer Roadwarrior */
+ 750,
+ 1500,
+ 1,
+ 1,
+ {{{
+ 3000,
+ 2600,
+ 10,
+ 0.8,
+ },
+ {
+ 11000,
+ 11000,
+ 2,
+ 0.8,
+ },},},
+ },
+ {MOTOR_DSMOBILE_600, /* Pentax DSmobile 600 */
+ 750,
+ 1500,
+ 2,
+ 1,
+ {{{
+ 6666,
+ 3700,
+ 8,
+ 0.8,
+ },
+ {
+ 6666,
+ 3700,
+ 8,
+ 0.8,
+ },},},
+ },
+ {MOTOR_CANONLIDE100, /* Canon LiDE 100 */
+ 1200,
+ 6400,
+ 2, /* maximum step type count */
+ 1, /* maximum power modes count */
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3000, 1000, 127, 0.50}, /* full step */
+ { 3000, 1500, 127, 0.50}, /* half step */
+ { 3*2712, 3*2712, 16, 0.80}, /* quarter step 0.75*2712 */
+ },
+ },
+ },
+ {MOTOR_CANONLIDE200, /* Canon LiDE 200 */
+ 1200,
+ 6400,
+ 2,
+ 1,
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3000, 1000, 127, 0.50}, /* full step */
+ { 3000, 1500, 127, 0.50}, /* half step */
+ { 3*2712, 3*2712, 16, 0.80}, /* quarter step 0.75*2712 */
+ },
+ },
+ },
+ {MOTOR_CANONLIDE700, /* Canon LiDE 700 */
+ 1200,
+ 6400,
+ 2,
+ 1,
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3000, 1000, 127, 0.50}, /* full step */
+ { 3000, 1500, 127, 0.50}, /* half step */
+ { 3*2712, 3*2712, 16, 0.80}, /* quarter step 0.75*2712 */
+ },
+ },
+ },
+ {MOTOR_KVSS080,
+ 1200,
+ 1200,
+ 2,
+ 1,
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 22222, 500, 246, 0.5 }, /* max speed / dpi * base dpi => exposure */
+ { 22222, 500, 246, 0.5 },
+ { 22222, 500, 246, 0.5 },
+ },
+ },
+ },
+ {MOTOR_G4050,
+ 2400,
+ 9600,
+ 2,
+ 1,
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3961, 240, 246, 0.8 }, /* full step */
+ { 3961, 240, 246, 0.8 }, /* half step */
+ { 3961, 240, 246, 0.8 }, /* quarter step */
+ },
+ },
+ },
+ {MOTOR_CS8400F,
+ 2400,
+ 9600,
+ 2,
+ 1,
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3961, 240, 246, 0.8 }, /* full step */
+ { 3961, 240, 246, 0.8 }, /* half step */
+ { 3961, 240, 246, 0.8 }, /* quarter step */
+ },
+ },
+ },
+ {MOTOR_CANONLIDE110, /* Canon LiDE 110 */
+ 4800,
+ 9600,
+ 1, /* maximum step type count */
+ 1, /* maximum power modes count */
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3000, 1000, 256, 0.50}, /* full step */
+ },
+ },
+ },
+ {MOTOR_CANONLIDE210, /* Canon LiDE 210 */
+ 4800,
+ 9600,
+ 1, /* maximum step type count */
+ 1, /* maximum power modes count */
+ { /* motor slopes */
+ { /* power mode 0 */
+ { 3000, 1000, 256, 0.50}, /* full step */
+ },
+ },
+ },
+ {MOTOR_PLUSTEK_3600, /* PLUSTEK 3600 */
+ 1200,
+ 2400,
+ 1,
+ 1,
+ {
+ {
+ { 3500, 1300, 60, 0.8 },
+ { 3500, 3250, 60, 0.8 },
+ },
+ },},
+ {MOTOR_IMG101, /* Canon Image Formula 101 */
+ 600,
+ 1200,
+ 1,
+ 1,
+ {
+ {
+ { 3500, 1300, 60, 0.8 },
+ { 3500, 3250, 60, 0.8 },
+ },
+ },},
+ {MOTOR_PLUSTEK3800, /* Plustek OpticBook 3800 */
+ 600,
+ 1200,
+ 1,
+ 1,
+ {
+ {
+ { 3500, 1300, 60, 0.8 },
+ { 3500, 3250, 60, 0.8 },
+ },
+ },},
+};
+
+/* here we have the various device settings...
+ */
+static Genesys_Model umax_astra_4500_model = {
+ "umax-astra-4500", /* Name */
+ "UMAX", /* Device vendor string */
+ "Astra 4500", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_UMAX,
+ DAC_WOLFSON_UMAX,
+ GPO_UMAX,
+ MOTOR_UMAX,
+ GENESYS_FLAG_UNTESTED, /* Which flags are needed for this scanner? */
+ /* untested, values set by hmg */
+ GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
+ 20,
+ 200
+};
+
+static Genesys_Model canon_lide_50_model = {
+ "canon-lide-50", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 35/40/50", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.42), /* Start of scan area in mm (x) */
+ SANE_FIX (7.9), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_CANONLIDE35,
+ DAC_CANONLIDE35,
+ GPO_CANONLIDE35,
+ MOTOR_CANONLIDE35,
+ GENESYS_FLAG_LAZY_INIT | /* Which flags are needed for this scanner? */
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_WHITE_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_HALF_CCD_MODE,
+ GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_FILE_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_COPY_SW,
+ 280,
+ 400
+};
+
+static Genesys_Model panasonic_kvss080_model = {
+ "panasonic-kv-ss080", /* Name */
+ "Panasonic", /* Device vendor string */
+ "KV-SS080", /* Device model name */
+ GENESYS_GL843,
+ NULL,
+
+ { 600, /* 500, 400,*/ 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
+ { 1200, 600, /* 500, 400, */ 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.2), /* Start of scan area in mm (x) */
+ SANE_FIX (14.7), /* Start of scan area in mm (y) */
+ SANE_FIX (217.7), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (9.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_KVSS080,
+ DAC_KVSS080,
+ GPO_KVSS080,
+ MOTOR_KVSS080,
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW ,
+ 100,
+ 100
+};
+
+static Genesys_Model hp4850c_model = {
+ "hewlett-packard-scanjet-4850c", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet 4850C", /* Device model name */
+ GENESYS_GL843,
+ NULL,
+
+ {2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ {2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.9), /* Start of scan area in mm (x) */
+ SANE_FIX (5.9), /* Start of scan area in mm (y) */
+ SANE_FIX (219.6), /* Size of scan area in mm (x) */
+ SANE_FIX (314.5), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in line number */
+ /* 0 38 76 OK 1200/2400 */
+ /* 0 24 48 OK [100,600] dpi */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_G4050,
+ DAC_G4050,
+ GPO_G4050,
+ MOTOR_G4050,
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_STAGGERED_LINE |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
+ 100,
+ 100
+};
+
+static Genesys_Model hpg4010_model = {
+ "hewlett-packard-scanjet-g4010", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet G4010", /* Device model name */
+ GENESYS_GL843,
+ NULL,
+
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (8.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
+ SANE_FIX (315.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in line number */
+ /* 0 38 76 OK 1200/2400 */
+ /* 0 24 48 OK [100,600] dpi */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_G4050,
+ DAC_G4050,
+ GPO_G4050,
+ MOTOR_G4050,
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_STAGGERED_LINE |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
+ 100,
+ 100
+};
+
+static Genesys_Model hpg4050_model = {
+ "hewlett-packard-scanjet-g4050", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet G4050", /* Device model name */
+ GENESYS_GL843,
+ NULL,
+
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (8.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
+ SANE_FIX (315.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (8.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (250.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (40.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in line number */
+ /* 0 38 76 OK 1200/2400 */
+ /* 0 24 48 OK [100,600] dpi */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_G4050,
+ DAC_G4050,
+ GPO_G4050,
+ MOTOR_G4050,
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_STAGGERED_LINE |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
+ 100,
+ 100
+};
+
+
+static Genesys_Model canon_4400f_model = {
+ "canon-canoscan-4400f", /* Name */
+ "Canon", /* Device vendor string */
+ "Canoscan 4400f", /* Device model name */
+ GENESYS_GL843,
+ NULL,
+
+ { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (6.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
+ SANE_FIX (315.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (8.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (250.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (40.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in line number */
+ /* 0 38 76 OK 1200/2400 */
+ /* 0 24 48 OK [100,600] dpi */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_CS4400F,
+ DAC_G4050,
+ GPO_CS4400F,
+ MOTOR_G4050,
+ GENESYS_FLAG_NO_CALIBRATION |
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_STAGGERED_LINE |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_FULL_HWDPI_MODE |
+ GENESYS_FLAG_HALF_CCD_MODE | /* actually quarter CCD mode ... */
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
+ 100,
+ 100
+};
+
+
+static Genesys_Model canon_8400f_model = {
+ "canon-canoscan-8400f", /* Name */
+ "Canon", /* Device vendor string */
+ "Canoscan 8400f", /* Device model name */
+ GENESYS_GL843,
+ NULL,
+
+ { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
+ SANE_FIX (315.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (8.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (13.00), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (217.9), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (250.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (40.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in line number */
+ /* 0 38 76 OK 1200/2400 */
+ /* 0 24 48 OK [100,600] dpi */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_CS8400F,
+ DAC_CS8400F,
+ GPO_CS8400F,
+ MOTOR_CS8400F,
+ GENESYS_FLAG_NO_CALIBRATION |
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_STAGGERED_LINE |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_FULL_HWDPI_MODE |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
+ 100,
+ 100
+};
+
+
+
+static Genesys_Model canon_lide_100_model = {
+ "canon-lide-100", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 100", /* Device model name */
+ GENESYS_GL847,
+ NULL,
+
+ {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
+ {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (1.1), /* Start of scan area in mm (x) */
+ SANE_FIX (8.3), /* Start of scan area in mm (y) */
+ SANE_FIX (216.07), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (1.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CIS_CANONLIDE100,
+ DAC_CANONLIDE200,
+ GPO_CANONLIDE200,
+ MOTOR_CANONLIDE100,
+ /* Which flags are needed for this scanner? */
+ GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_SIS_SENSOR
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_SHADING_REPARK
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
+ 50,
+ 400
+};
+
+static Genesys_Model canon_lide_110_model = {
+ "canon-lide-110", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 110", /* Device model name */
+ GENESYS_GL124,
+ NULL,
+
+ {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible x-resolutions */
+ {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (2.2), /* Start of scan area in mm (x) */
+ SANE_FIX (9.0), /* Start of scan area in mm (y) */
+ SANE_FIX (216.70), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (1.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CIS_CANONLIDE110,
+ DAC_CANONLIDE110,
+ GPO_CANONLIDE110,
+ MOTOR_CANONLIDE110,
+ GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_HALF_CCD_MODE
+ | GENESYS_FLAG_SHADING_REPARK
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
+ 50,
+ 400
+};
+
+
+static Genesys_Model canon_lide_210_model = {
+ "canon-lide-210", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 210", /* Device model name */
+ GENESYS_GL124,
+ NULL,
+
+ {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible x-resolutions */
+ {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (2.2), /* Start of scan area in mm (x) */
+ SANE_FIX (8.7), /* Start of scan area in mm (y) */
+ SANE_FIX (216.70), /* Size of scan area in mm (x) */
+ SANE_FIX (297.5), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CIS_CANONLIDE210,
+ DAC_CANONLIDE110,
+ GPO_CANONLIDE210,
+ MOTOR_CANONLIDE210,
+ GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_HALF_CCD_MODE
+ | GENESYS_FLAG_SHADING_REPARK
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EXTRA_SW,
+ 60,
+ 400
+};
+
+static Genesys_Model canon_5600f_model = {
+ "canon-5600f", /* Name */
+ "Canon", /* Device vendor string */
+ "5600F", /* Device model name */
+ GENESYS_GL847,
+ NULL,
+
+ {1200, 600, 400, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 400, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (1.1), /* Start of scan area in mm (x) */
+ SANE_FIX (8.3), /* Start of scan area in mm (y) */
+ SANE_FIX (216.07), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CIS_CANONLIDE200,
+ DAC_CANONLIDE200,
+ GPO_CANONLIDE200,
+ MOTOR_CANONLIDE200,
+ GENESYS_FLAG_UNTESTED /* not working yet */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_SIS_SENSOR
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
+ 50,
+ 400
+};
+
+static Genesys_Model canon_lide_700f_model = {
+ "canon-lide-700f", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 700F", /* Device model name */
+ GENESYS_GL847,
+ NULL,
+
+ {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
+ {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.1), /* Start of scan area in mm (x) */
+ SANE_FIX (8.1), /* Start of scan area in mm (y) */
+ SANE_FIX (216.07), /* Size of scan area in mm (x) */
+ SANE_FIX (297.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (1.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CIS_CANONLIDE700,
+ DAC_CANONLIDE700,
+ GPO_CANONLIDE700,
+ MOTOR_CANONLIDE700,
+ GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_SIS_SENSOR
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_SHADING_REPARK
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
+ 70,
+ 400
+};
+
+
+
+static Genesys_Model canon_lide_200_model = {
+ "canon-lide-200", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 200", /* Device model name */
+ GENESYS_GL847,
+ NULL,
+
+ {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
+ {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (1.1), /* Start of scan area in mm (x) */
+ SANE_FIX (8.3), /* Start of scan area in mm (y) */
+ SANE_FIX (216.07), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CIS_CANONLIDE200,
+ DAC_CANONLIDE200,
+ GPO_CANONLIDE200,
+ MOTOR_CANONLIDE200,
+ GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_SIS_SENSOR
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_SHADING_REPARK
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
+ 50,
+ 400
+};
+
+
+static Genesys_Model canon_lide_60_model = {
+ "canon-lide-60", /* Name */
+ "Canon", /* Device vendor string */
+ "LiDE 60", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.42), /* Start of scan area in mm (x) */
+ SANE_FIX (7.9), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_CANONLIDE35,
+ DAC_CANONLIDE35,
+ GPO_CANONLIDE35,
+ MOTOR_CANONLIDE35,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_WHITE_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_HALF_CCD_MODE,
+
+ GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
+ 300,
+ 400
+}; /* this is completely untested -- hmg */
+
+static Genesys_Model hp2300c_model = {
+ "hewlett-packard-scanjet-2300c", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet 2300c", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions, motor can go up to 1200 dpi */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (2.0), /* Start of scan area in mm (x_offset) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y_offset) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (295.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 16, 8, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_HP2300,
+ DAC_WOLFSON_HP2300,
+ GPO_HP2300,
+ MOTOR_HP2300,
+ GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_SEARCH_START
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_HALF_CCD_MODE
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW,
+ 40,
+ 132
+};
+
+static
+Genesys_Model hp2400c_model = {
+ "hewlett-packard-scanjet-2400c", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet 2400c", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (6.5), /* Start of scan area in mm (x) */
+ SANE_FIX (2.5), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (297.2), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_HP2400,
+ DAC_WOLFSON_HP2400,
+ GPO_HP2400,
+ MOTOR_HP2400,
+ GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_STAGGERED_LINE
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW,
+ 20,
+ 132
+};
+
+static
+Genesys_Model visioneer_xp200_model = {
+ "visioneer-strobe-xp200", /* Name */
+ "Visioneer", /* Device vendor string */
+ "Strobe XP200", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {600, 300, 200, 100, 75, 0}, /* possible x-resolutions */
+ {600, 300, 200, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.5), /* Start of scan area in mm (x) */
+ SANE_FIX (16.0), /* Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (297.2), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CIS_XP200,
+ DAC_AD_XP200, /* Analog Device frontend */
+ GPO_XP200,
+ MOTOR_XP200,
+ GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 120,
+ 132
+};
+
+static Genesys_Model hp3670c_model = {
+ "hewlett-packard-scanjet-3670c", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet 3670c", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (8.5), /* Start of scan area in mm (x) */
+ SANE_FIX (11.0), /* Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (104.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (55.6), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (25.6), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (78.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (76.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_HP3670,
+ DAC_WOLFSON_HP3670,
+ GPO_HP3670,
+ MOTOR_HP3670,
+ GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_XPA
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_STAGGERED_LINE
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW,
+ 20,
+ 200
+};
+
+static Genesys_Model plustek_st12_model = {
+ "plustek-opticpro-st12", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticPro ST12", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_ST12,
+ DAC_WOLFSON_ST12,
+ GPO_ST12,
+ MOTOR_UMAX,
+ GENESYS_FLAG_UNTESTED | GENESYS_FLAG_14BIT_GAMMA, /* Which flags are needed for this scanner? */
+ GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
+ 20,
+ 200
+};
+
+static Genesys_Model plustek_st24_model = {
+ "plustek-opticpro-st24", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticPro ST24", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_ST24,
+ DAC_WOLFSON_ST24,
+ GPO_ST24,
+ MOTOR_ST24,
+ GENESYS_FLAG_UNTESTED
+ | GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_SEARCH_START
+ | GENESYS_FLAG_OFFSET_CALIBRATION,
+ GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
+ 20,
+ 200
+};
+
+static Genesys_Model medion_md5345_model = {
+ "medion-md5345-model", /* Name */
+ "Medion", /* Device vendor string */
+ "MD5345/MD6228/MD6471", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX ( 0.30), /* Start of scan area in mm (x) */
+ SANE_FIX ( 0.80), /* 2.79 < Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (296.4), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.00), /* Start of white strip in mm (y) */
+ SANE_FIX (0.00), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.00), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.00), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.00), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.00), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.00), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_5345,
+ DAC_WOLFSON_5345,
+ GPO_5345,
+ MOTOR_5345,
+ GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_SEARCH_START
+ | GENESYS_FLAG_STAGGERED_LINE
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_HALF_CCD_MODE
+ | GENESYS_FLAG_SHADING_NO_MOVE
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW,
+ 40,
+ 200
+};
+
+static Genesys_Model visioneer_xp300_model = {
+ "visioneer-strobe-xp300", /* Name */
+ "Visioneer", /* Device vendor string */
+ "Strobe XP300", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.0), /* Start of scan area in mm (y) */
+ SANE_FIX (435.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (26.5), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ /* this is larger than needed -- accounts for second sensor head, which is a
+ calibration item */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_XP300,
+ DAC_WOLFSON_XP300,
+ GPO_XP300,
+ MOTOR_XP300,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model syscan_docketport_665_model = {
+ "syscan-docketport-665", /* Name */
+ "Syscan/Ambir", /* Device vendor string */
+ "DocketPORT 665", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (108.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (17.5), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_DP665,
+ DAC_WOLFSON_XP300,
+ GPO_DP665,
+ MOTOR_DP665,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model visioneer_roadwarrior_model = {
+ "visioneer-roadwarrior", /* Name */
+ "Visioneer", /* Device vendor string */
+ "Readwarrior", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (16.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_ROADWARRIOR,
+ DAC_WOLFSON_XP300,
+ GPO_DP665,
+ MOTOR_ROADWARRIOR,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model syscan_docketport_465_model = {
+ "syscan-docketport-465", /* Name */
+ "Syscan", /* Device vendor string */
+ "DocketPORT 465", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (16.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_ROADWARRIOR,
+ DAC_WOLFSON_XP300,
+ GPO_DP665,
+ MOTOR_ROADWARRIOR,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_NO_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_UNTESTED,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW,
+ 300,
+ 400
+};
+
+static Genesys_Model visioneer_xp100_r3_model = {
+ "visioneer-xp100-revision3", /* Name */
+ "Visioneer", /* Device vendor string */
+ "XP100 Revision 3", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (16.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_ROADWARRIOR,
+ DAC_WOLFSON_XP300,
+ GPO_DP665,
+ MOTOR_ROADWARRIOR,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model pentax_dsmobile_600_model = {
+ "pentax-dsmobile-600", /* Name */
+ "Pentax", /* Device vendor string */
+ "DSmobile 600", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (16.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_DSMOBILE600,
+ DAC_WOLFSON_DSM600,
+ GPO_DP665,
+ MOTOR_DSMOBILE_600,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model syscan_docketport_467_model = {
+ "syscan-docketport-467", /* Name */
+ "Syscan", /* Device vendor string */
+ "DocketPORT 467", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (16.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_DSMOBILE600,
+ DAC_WOLFSON_DSM600,
+ GPO_DP665,
+ MOTOR_DSMOBILE_600,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model syscan_docketport_685_model = {
+ "syscan-docketport-685", /* Name */
+ "Syscan/Ambir", /* Device vendor string */
+ "DocketPORT 685", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.0), /* Start of scan area in mm (y) */
+ SANE_FIX (212.0), /* Size of scan area in mm (x) */
+ SANE_FIX (500), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (26.5), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ /* this is larger than needed -- accounts for second sensor head, which is a
+ calibration item */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_DP685,
+ DAC_WOLFSON_DSM600,
+ GPO_DP685,
+ MOTOR_XP300,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model syscan_docketport_485_model = {
+ "syscan-docketport-485", /* Name */
+ "Syscan/Ambir", /* Device vendor string */
+ "DocketPORT 485", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.0), /* Start of scan area in mm (y) */
+ SANE_FIX (435.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (26.5), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ /* this is larger than needed -- accounts for second sensor head, which is a
+ calibration item */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_XP300,
+ DAC_WOLFSON_XP300,
+ GPO_XP300,
+ MOTOR_XP300,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model dct_docketport_487_model = {
+ "dct-docketport-487", /* Name */
+ "DCT", /* Device vendor string */
+ "DocketPORT 487", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.0), /* Start of scan area in mm (y) */
+ SANE_FIX (435.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (26.5), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ /* this is larger than needed -- accounts for second sensor head, which is a
+ calibration item */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_XP300,
+ DAC_WOLFSON_XP300,
+ GPO_XP300,
+ MOTOR_XP300,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_UNTESTED,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model visioneer_7100_model = {
+ "visioneer-7100-model", /* Name */
+ "Visioneer", /* Device vendor string */
+ "OneTouch 7100", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX ( 4.00), /* Start of scan area in mm (x) */
+ SANE_FIX ( 0.80), /* 2.79 < Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (296.4), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.00), /* Start of white strip in mm (y) */
+ SANE_FIX (0.00), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.00), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.00), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.00), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.00), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.00), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
+/* 48, 24, 0, */
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_5345,
+ DAC_WOLFSON_5345,
+ GPO_5345,
+ MOTOR_5345,
+ GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_SEARCH_START
+ | GENESYS_FLAG_STAGGERED_LINE
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_HALF_CCD_MODE
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW,
+ 40,
+ 200
+};
+
+static Genesys_Model xerox_2400_model = {
+ "xerox-2400-model", /* Name */
+ "Xerox", /* Device vendor string */
+ "OneTouch 2400", /* Device model name */
+ GENESYS_GL646,
+ NULL,
+
+ {1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX ( 4.00), /* Start of scan area in mm (x) */
+ SANE_FIX ( 0.80), /* 2.79 < Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (296.4), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.00), /* Start of white strip in mm (y) */
+ SANE_FIX (0.00), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.00), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.00), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.00), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.00), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.00), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
+/* 48, 24, 0, */
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_5345,
+ DAC_WOLFSON_5345,
+ GPO_5345,
+ MOTOR_5345,
+ GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_SEARCH_START
+ | GENESYS_FLAG_STAGGERED_LINE
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_HALF_CCD_MODE
+ | GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW,
+ 40,
+ 200
+};
+
+
+static Genesys_Model xerox_travelscanner_model = {
+ "xerox-travelscanner", /* Name */
+ "Xerox", /* Device vendor string */
+ "Travelscanner 100", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (16.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ SANE_TRUE, /* Is this a sheetfed scanner? */
+ CCD_ROADWARRIOR,
+ DAC_WOLFSON_XP300,
+ GPO_DP665,
+ MOTOR_ROADWARRIOR,
+ GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION,
+ GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
+ 100,
+ 400
+};
+
+static Genesys_Model plustek_3600_model = {
+ "plustek-opticbook-3600", /* Name */
+ "PLUSTEK", /* Device vendor string */
+ "OpticBook 3600", /* Device model name */
+ GENESYS_GL841,
+ NULL,
+ {/*1200,*/ 600, 400, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
+ {/*2400,*/ 1200, 600, 400, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.42),/*SANE_FIX (0.42), Start of scan area in mm (x) */
+ SANE_FIX (6.75),/*SANE_FIX (7.9), Start of scan area in mm (y) */
+ SANE_FIX (216.0),/*SANE_FIX (216.0), Size of scan area in mm (x) */
+ SANE_FIX (297.0),/*SANE_FIX (297.0), Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_PLUSTEK_3600,
+ DAC_PLUSTEK_3600,
+ GPO_PLUSTEK_3600,
+ MOTOR_PLUSTEK_3600,
+ GENESYS_FLAG_UNTESTED /* not fully working yet */
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_HALF_CCD_MODE,/*
+ | GENESYS_FLAG_NO_CALIBRATION,*/
+ GENESYS_HAS_NO_BUTTONS,
+ 7,
+ 200
+};
+
+static Genesys_Model hpn6310_model = {
+ "hewlett-packard-scanjet-N6310", /* Name */
+ "Hewlett Packard", /* Device vendor string */
+ "ScanJet N6310", /* Device model name */
+ GENESYS_GL847,
+ NULL,
+
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 0},
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 0},
+
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (6), /* Start of scan area in mm (x) */
+ SANE_FIX (2), /* Start of scan area in mm (y) */
+ SANE_FIX (216), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
+ SANE_FIX (511), /* Size of scan area in mm (y) */
+
+ SANE_FIX (3.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_HP_N6310,
+ DAC_CANONLIDE200, /*Not defined yet for N6310 */
+ GPO_HP_N6310,
+ MOTOR_CANONLIDE200, /*Not defined yet for N6310 */
+ GENESYS_FLAG_UNTESTED /* not fully working yet */
+ | GENESYS_FLAG_LAZY_INIT
+ | GENESYS_FLAG_14BIT_GAMMA
+ | GENESYS_FLAG_DARK_CALIBRATION
+ | GENESYS_FLAG_OFFSET_CALIBRATION
+ | GENESYS_FLAG_CUSTOM_GAMMA
+ | GENESYS_FLAG_SKIP_WARMUP
+ | GENESYS_FLAG_NO_CALIBRATION,
+/* | GENESYS_FLAG_HALF_CCD_MODE,*/
+
+ GENESYS_HAS_NO_BUTTONS,
+ 100,
+ 100
+};
+
+
+static Genesys_Model plustek_3800_model = {
+ "plustek-opticbook-3800", /* Name */
+ "PLUSTEK", /* Device vendor string */
+ "OpticBook 3800", /* Device model name */
+ GENESYS_GL845,
+ NULL,
+
+ {1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.2), /* Start of scan area in mm (x) */
+ SANE_FIX (14.7), /* Start of scan area in mm (y) */
+ SANE_FIX (217.7), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (9.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_PLUSTEK3800,
+ DAC_PLUSTEK3800,
+ GPO_PLUSTEK3800,
+ MOTOR_PLUSTEK3800,
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_NO_BUTTONS, /* TODO there are 4 buttons to support */
+ 100,
+ 100
+};
+
+
+static Genesys_Model canon_formula101_model = {
+ "canon-image-formula-101", /* Name */
+ "Canon", /* Device vendor string */
+ "Image Formula 101", /* Device model name */
+ GENESYS_GL846,
+ NULL,
+
+ {1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.2), /* Start of scan area in mm (x) */
+ SANE_FIX (14.7), /* Start of scan area in mm (y) */
+ SANE_FIX (217.7), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (9.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_FIX (0.0), /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ SANE_FALSE, /* Is this a sheetfed scanner? */
+ CCD_IMG101,
+ DAC_IMG101,
+ GPO_IMG101,
+ MOTOR_IMG101,
+ GENESYS_FLAG_LAZY_INIT |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA,
+ GENESYS_HAS_NO_BUTTONS ,
+ 100,
+ 100
+};
+
+
+static Genesys_USB_Device_Entry genesys_usb_device_list[] = {
+ /* GL646 devices */
+ {0x03f0, 0x0901, &hp2300c_model},
+ {0x03f0, 0x0a01, &hp2400c_model},
+ {0x03f0, 0x1405, &hp3670c_model},
+ {0x0461, 0x0377, &medion_md5345_model},
+ {0x04a7, 0x0229, &visioneer_7100_model},
+ {0x0461, 0x038b, &xerox_2400_model},
+ {0x04a7, 0x0426, &visioneer_xp200_model},
+ {0x0638, 0x0a10, &umax_astra_4500_model},
+ {0x07b3, 0x0600, &plustek_st12_model},
+ {0x07b3, 0x0601, &plustek_st24_model},
+ /* GL841 devices */
+ {0x04a7, 0x0474, &visioneer_xp300_model},
+ {0x04a7, 0x0494, &visioneer_roadwarrior_model},
+ {0x04a7, 0x049b, &visioneer_xp100_r3_model},
+ {0x04a7, 0x04ac, &xerox_travelscanner_model},
+ {0x04a9, 0x2213, &canon_lide_50_model},
+ {0x04a9, 0x221c, &canon_lide_60_model},
+ {0x07b3, 0x0900, &plustek_3600_model},
+ {0x0a17, 0x3210, &pentax_dsmobile_600_model},
+ {0x04f9, 0x2038, &pentax_dsmobile_600_model}, /* clone, only usb id is different */
+ {0x0a82, 0x4800, &syscan_docketport_485_model},
+ {0x0a82, 0x4802, &syscan_docketport_465_model},
+ {0x0a82, 0x4803, &syscan_docketport_665_model},
+ {0x0a82, 0x480c, &syscan_docketport_685_model},
+ {0x1dcc, 0x4810, &dct_docketport_487_model},
+ {0x1dcc, 0x4812, &syscan_docketport_467_model},
+ /* GL843 devices */
+ {0x04da, 0x100f, &panasonic_kvss080_model},
+ {0x03f0, 0x1b05, &hp4850c_model},
+ {0x03f0, 0x4505, &hpg4010_model},
+ {0x03f0, 0x4605, &hpg4050_model},
+ {0x04a9, 0x2228, &canon_4400f_model},
+ {0x04a9, 0x221e, &canon_8400f_model},
+ /* GL845 devices */
+ {0x07b3, 0x1300, &plustek_3800_model},
+ /* GL846 devices */
+ {0x1083, 0x162e, &canon_formula101_model},
+ /* GL847 devices */
+ {0x04a9, 0x1904, &canon_lide_100_model},
+ {0x04a9, 0x1905, &canon_lide_200_model},
+ {0x04a9, 0x1906, &canon_5600f_model},
+ {0x04a9, 0x1907, &canon_lide_700f_model},
+ {0x03f0, 0x4705, &hpn6310_model},
+ /* GL124 devices */
+ {0x04a9, 0x1909, &canon_lide_110_model},
+ {0x04a9, 0x190a, &canon_lide_210_model},
+ {0, 0, NULL}
+};
diff --git a/backend/genesys_gl124.c b/backend/genesys_gl124.c
new file mode 100644
index 0000000..9e2fb8a
--- /dev/null
+++ b/backend/genesys_gl124.c
@@ -0,0 +1,3891 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_gl124
+
+#include "genesys_gl124.h"
+
+/****************************************************************************
+ Low level function
+ ****************************************************************************/
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+
+/** @brief read scanned data
+ * Read in 0xeff0 maximum sized blocks. This read is done in 2
+ * parts if not multple of 512. First read is rounded to a multiple of 512 bytes, last read fetches the
+ * remainder. Read addr is always 0x10000000 with the memory layout setup.
+ * @param dev device to read data from
+ * @param addr address within ASIC emory space
+ * @param data pointer where to store the read data
+ * @param len size to read
+ */
+static SANE_Status
+gl124_bulk_read_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size, target, read, done;
+ uint8_t outdata[8], *buffer;
+
+ DBG (DBG_io, "gl124_bulk_read_data: requesting %lu bytes (unused addr=0x%02x)\n", (u_long) len,addr);
+
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ target = len;
+ buffer = data;
+
+ /* loop until computed data size is read */
+ while (target)
+ {
+ if (target > 0xeff0)
+ {
+ size = 0xeff0;
+ }
+ else
+ {
+ size = target;
+ }
+
+ /* hard coded 0x10000000 addr */
+ outdata[0] = 0;
+ outdata[1] = 0;
+ outdata[2] = 0;
+ outdata[3] = 0x10;
+
+ /* data size to transfer */
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, 0x00, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s failed while writing command: %s\n",
+ __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ /* blocks must be multiple of 512 but not last block */
+ read = size;
+ read /= 512;
+ read *= 512;
+
+ if(read>0)
+ {
+ DBG (DBG_io2,
+ "gl124_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) read);
+ status = sanei_usb_read_bulk (dev->dn, data, &read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* read less than 512 bytes remainder */
+ if (read < size)
+ {
+ done = read;
+ read = size - read;
+ DBG (DBG_io2,
+ "gl124_bulk_read_data: trying to read remaining %lu bytes of data\n",
+ (u_long) read);
+ status = sanei_usb_read_bulk (dev->dn, data+done, &read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (DBG_io2, "%s: read %lu bytes, %lu remaining\n", __FUNCTION__,
+ (u_long) size, (u_long) (target - size));
+
+ target -= size;
+ data += size;
+ }
+
+ if (DBG_LEVEL >= DBG_data && dev->binary!=NULL)
+ {
+ fwrite(buffer, len, 1, dev->binary);
+ }
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/****************************************************************************
+ Mid level functions
+ ****************************************************************************/
+
+static SANE_Bool
+gl124_get_fast_feed_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG02);
+ if (r && (r->value & REG02_FASTFED))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl124_get_filter_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_FILTER))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl124_get_lineart_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_LINEART))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl124_get_bitset_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_BITSET))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl124_get_gain4_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG06);
+ if (r && (r->value & REG06_GAIN4))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl124_test_buffer_empty_bit (SANE_Byte val)
+{
+ if (val & BUFEMPTY)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl124_test_motor_flag_bit (SANE_Byte val)
+{
+ if (val & MOTORENB)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/** @brief sensor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at a specific dpihw. Use LiDE 110 table by default.
+ * @param sensor_type sensor id
+ * @param dpi hardware dpi for the scan
+ * @param half_ccd flag to signal half ccd mode
+ * @return a pointer to a Sensor_Profile struct
+ */
+static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi, int half_ccd)
+{
+ unsigned int i;
+ int idx;
+
+ i=0;
+ idx=-1;
+ while(i<sizeof(sensors)/sizeof(Sensor_Profile))
+ {
+ /* exact match */
+ if(sensors[i].sensor_type==sensor_type
+ && sensors[i].dpi==dpi
+ && sensors[i].half_ccd==half_ccd)
+ {
+ return &(sensors[i]);
+ }
+
+ /* closest match */
+ if(sensors[i].sensor_type==sensor_type
+ && sensors[i].half_ccd==half_ccd)
+ {
+ if(idx<0)
+ {
+ idx=i;
+ }
+ else
+ {
+ if(sensors[i].dpi>=dpi
+ && sensors[i].dpi<sensors[idx].dpi)
+ {
+ idx=i;
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default sensor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(sensors[idx]);
+}
+
+/** @brief motor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at full step and at a reference exposure. Use LiDE 110 table
+ * by default.
+ * @param motor_type motor id
+ * @param exposure exposure time
+ * @return a pointer to a Motor_Profile struct
+ */
+static Motor_Profile *get_motor_profile(int motor_type, int exposure)
+{
+ unsigned int i;
+ int idx;
+
+ i=0;
+ idx=-1;
+ while(i<sizeof(motors)/sizeof(Motor_Profile))
+ {
+ /* exact match */
+ if(motors[i].motor_type==motor_type && motors[i].exposure==exposure)
+ {
+ return &(motors[i]);
+ }
+
+ /* closest match */
+ if(motors[i].motor_type==motor_type)
+ {
+ if(idx<0)
+ {
+ idx=i;
+ }
+ else
+ {
+ if(motors[i].exposure>=exposure
+ && motors[i].exposure<motors[idx].exposure)
+ {
+ idx=i;
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default motor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(motors[idx]);
+}
+
+/** @brief generate slope table
+ * Generate the slope table to use for the scan using a reference slope
+ * table.
+ * @param slope pointer to the slope table to fill
+ * @param steps pointer to return used step number
+ * @param dpi desired motor resolution
+ * @param exposure exposure used
+ * @param base_dpi base resolution of the motor
+ * @param step_type step type used for scan
+ * @param factor shrink factor for the slope
+ * @param motor_type motor id
+ */
+static int gl124_slope_table(uint16_t *slope,
+ int *steps,
+ int dpi,
+ int exposure,
+ int base_dpi,
+ int step_type,
+ int factor,
+ int motor_type)
+{
+int sum, i;
+uint16_t target,current;
+Motor_Profile *profile;
+
+ /* required speed */
+ target=((exposure * dpi) / base_dpi)>>step_type;
+
+ /* fill result with target speed */
+ for(i=0;i<SLOPE_TABLE_SIZE;i++)
+ slope[i]=target;
+
+ profile=get_motor_profile(motor_type,exposure);
+
+ /* use profile to build table */
+ i=0;
+ sum=0;
+
+ /* first step is used unmodified */
+ current=profile->table[0];
+
+ /* loop on profile copying and apply step type */
+ while(i<SLOPE_TABLE_SIZE && current>=target)
+ {
+ slope[i]=current;
+ sum+=slope[i];
+ i++;
+ current=profile->table[i*factor]>>step_type;
+ }
+ if(i<3 && DBG_LEVEL >= DBG_warn)
+ {
+ DBG (DBG_warn,"%s: short slope table, failed to reach %d\n",__FUNCTION__,target);
+ }
+
+ /* ensure minimal slope size */
+ while(i<8)
+ {
+ sum+=slope[i];
+ i++;
+ }
+
+ /* return used steps and acceleration sum */
+ *steps=i;
+ return sum;
+}
+
+/* returns the max register bulk size */
+static int
+gl124_bulk_full_size (void)
+{
+ return GENESYS_GL124_MAX_REGS;
+}
+
+static SANE_Status
+gl124_homsnr_gpio(Genesys_Device *dev)
+{
+uint8_t val;
+SANE_Status status=SANE_STATUS_GOOD;
+
+ RIE (sanei_genesys_read_register (dev, REG32, &val));
+ val &= ~REG32_GPIO10;
+ RIE (sanei_genesys_write_register (dev, REG32, val));
+ return status;
+}
+
+/**@brief compute half ccd mode
+ * Compute half CCD mode flag. Half CCD is on when dpiset it twice
+ * the actual scanning resolution. Used for fast scans.
+ * @param model pointer to device model
+ * @param xres required horizontal resolution
+ * @return SANE_TRUE if half CCD mode enabled
+ */
+static SANE_Bool compute_half_ccd(Genesys_Model *model, int xres)
+{
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if (xres<=300 && (model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl124_init_registers (Genesys_Device * dev)
+{
+ DBGSTART;
+
+ memset (dev->reg, 0, GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* default to LiDE 110 */
+ SETREG (0x01,0xa2); /* + REG01_SHDAREA */
+ SETREG (0x02,0x90);
+ SETREG (0x03,0x50);
+ SETREG (0x03,0x50 & ~REG03_AVEENB);
+ SETREG (0x04,0x03);
+ SETREG (0x05,0x00);
+ SETREG (0x06,0x50 | REG06_GAIN4);
+ SETREG (0x09,0x00);
+ SETREG (0x0a,0xc0);
+ SETREG (0x0b,0x2a);
+ SETREG (0x0c,0x12);
+ SETREG (0x11,0x00);
+ SETREG (0x12,0x00);
+ SETREG (0x13,0x0f);
+ SETREG (0x14,0x00);
+ SETREG (0x15,0x80);
+ SETREG (0x16,0x10);
+ SETREG (0x17,0x04);
+ SETREG (0x18,0x00);
+ SETREG (0x19,0x01);
+ SETREG (0x1a,0x30);
+ SETREG (0x1b,0x00);
+ SETREG (0x1c,0x00);
+ SETREG (0x1d,0x01);
+ SETREG (0x1e,0x10);
+ SETREG (0x1f,0x00);
+ SETREG (0x20,0x15);
+ SETREG (0x21,0x00);
+ SETREG (0x22,0x02);
+ SETREG (0x23,0x00);
+ SETREG (0x24,0x00);
+ SETREG (0x25,0x00);
+ SETREG (0x26,0x0d);
+ SETREG (0x27,0x48);
+ SETREG (0x28,0x00);
+ SETREG (0x29,0x56);
+ SETREG (0x2a,0x5e);
+ SETREG (0x2b,0x02);
+ SETREG (0x2c,0x02);
+ SETREG (0x2d,0x58);
+ SETREG (0x3b,0x00);
+ SETREG (0x3c,0x00);
+ SETREG (0x3d,0x00);
+ SETREG (0x3e,0x00);
+ SETREG (0x3f,0x02);
+ SETREG (0x40,0x00);
+ SETREG (0x41,0x00);
+ SETREG (0x42,0x00);
+ SETREG (0x43,0x00);
+ SETREG (0x44,0x00);
+ SETREG (0x45,0x00);
+ SETREG (0x46,0x00);
+ SETREG (0x47,0x00);
+ SETREG (0x48,0x00);
+ SETREG (0x49,0x00);
+ SETREG (0x4f,0x00);
+ SETREG (0x52,0x00);
+ SETREG (0x53,0x02);
+ SETREG (0x54,0x04);
+ SETREG (0x55,0x06);
+ SETREG (0x56,0x04);
+ SETREG (0x57,0x04);
+ SETREG (0x58,0x04);
+ SETREG (0x59,0x04);
+ SETREG (0x5a,0x1a);
+ SETREG (0x5b,0x00);
+ SETREG (0x5c,0xc0);
+ SETREG (0x5f,0x00);
+ SETREG (0x60,0x02);
+ SETREG (0x61,0x00);
+ SETREG (0x62,0x00);
+ SETREG (0x63,0x00);
+ SETREG (0x64,0x00);
+ SETREG (0x65,0x00);
+ SETREG (0x66,0x00);
+ SETREG (0x67,0x00);
+ SETREG (0x68,0x00);
+ SETREG (0x69,0x00);
+ SETREG (0x6a,0x00);
+ SETREG (0x6b,0x00);
+ SETREG (0x6c,0x00);
+ SETREG (0x6d,0xd0);
+ SETREG (0x6e,0x00);
+ SETREG (0x6f,0x00);
+ SETREG (0x70,0x06);
+ SETREG (0x71,0x08);
+ SETREG (0x72,0x08);
+ SETREG (0x73,0x0a);
+
+ /* CKxMAP */
+ SETREG (0x74,0x00);
+ SETREG (0x75,0x00);
+ SETREG (0x76,0x3c);
+ SETREG (0x77,0x00);
+ SETREG (0x78,0x00);
+ SETREG (0x79,0x9f);
+ SETREG (0x7a,0x00);
+ SETREG (0x7b,0x00);
+ SETREG (0x7c,0x55);
+
+ SETREG (0x7d,0x00);
+ SETREG (0x7e,0x08);
+ SETREG (0x7f,0x58);
+ SETREG (0x80,0x00);
+ SETREG (0x81,0x14);
+
+ /* STRPIXEL */
+ SETREG (0x82,0x00);
+ SETREG (0x83,0x00);
+ SETREG (0x84,0x00);
+ /* ENDPIXEL */
+ SETREG (0x85,0x00);
+ SETREG (0x86,0x00);
+ SETREG (0x87,0x00);
+
+ SETREG (0x88,0x00);
+ SETREG (0x89,0x65);
+ SETREG (0x8a,0x00);
+ SETREG (0x8b,0x00);
+ SETREG (0x8c,0x00);
+ SETREG (0x8d,0x00);
+ SETREG (0x8e,0x00);
+ SETREG (0x8f,0x00);
+ SETREG (0x90,0x00);
+ SETREG (0x91,0x00);
+ SETREG (0x92,0x00);
+ SETREG (0x93,0x00);
+ SETREG (0x94,0x14);
+ SETREG (0x95,0x30);
+ SETREG (0x96,0x00);
+ SETREG (0x97,0x90);
+ SETREG (0x98,0x01);
+ SETREG (0x99,0x1f);
+ SETREG (0x9a,0x00);
+ SETREG (0x9b,0x80);
+ SETREG (0x9c,0x80);
+ SETREG (0x9d,0x3f);
+ SETREG (0x9e,0x00);
+ SETREG (0x9f,0x00);
+ SETREG (0xa0,0x20);
+ SETREG (0xa1,0x30);
+ SETREG (0xa2,0x00);
+ SETREG (0xa3,0x20);
+ SETREG (0xa4,0x01);
+ SETREG (0xa5,0x00);
+ SETREG (0xa6,0x00);
+ SETREG (0xa7,0x08);
+ SETREG (0xa8,0x00);
+ SETREG (0xa9,0x08);
+ SETREG (0xaa,0x01);
+ SETREG (0xab,0x00);
+ SETREG (0xac,0x00);
+ SETREG (0xad,0x40);
+ SETREG (0xae,0x01);
+ SETREG (0xaf,0x00);
+ SETREG (0xb0,0x00);
+ SETREG (0xb1,0x40);
+ SETREG (0xb2,0x00);
+ SETREG (0xb3,0x09);
+ SETREG (0xb4,0x5b);
+ SETREG (0xb5,0x00);
+ SETREG (0xb6,0x10);
+ SETREG (0xb7,0x3f);
+ SETREG (0xb8,0x00);
+ SETREG (0xbb,0x00);
+ SETREG (0xbc,0xff);
+ SETREG (0xbd,0x00);
+ SETREG (0xbe,0x07);
+ SETREG (0xc3,0x00);
+ SETREG (0xc4,0x00);
+
+ /* gamma
+ SETREG (0xc5,0x00);
+ SETREG (0xc6,0x00);
+ SETREG (0xc7,0x00);
+ SETREG (0xc8,0x00);
+ SETREG (0xc9,0x00);
+ SETREG (0xca,0x00);
+ SETREG (0xcb,0x00);
+ SETREG (0xcc,0x00);
+ SETREG (0xcd,0x00);
+ SETREG (0xce,0x00);
+ */
+
+ /* memory layout
+ SETREG (0xd0,0x0a);
+ SETREG (0xd1,0x1f);
+ SETREG (0xd2,0x34); */
+ SETREG (0xd3,0x00);
+ SETREG (0xd4,0x00);
+ SETREG (0xd5,0x00);
+ SETREG (0xd6,0x00);
+ SETREG (0xd7,0x00);
+ SETREG (0xd8,0x00);
+ SETREG (0xd9,0x00);
+
+ /* memory layout
+ SETREG (0xe0,0x00);
+ SETREG (0xe1,0x48);
+ SETREG (0xe2,0x15);
+ SETREG (0xe3,0x90);
+ SETREG (0xe4,0x15);
+ SETREG (0xe5,0x91);
+ SETREG (0xe6,0x2a);
+ SETREG (0xe7,0xd9);
+ SETREG (0xe8,0x2a);
+ SETREG (0xe9,0xad);
+ SETREG (0xea,0x40);
+ SETREG (0xeb,0x22);
+ SETREG (0xec,0x40);
+ SETREG (0xed,0x23);
+ SETREG (0xee,0x55);
+ SETREG (0xef,0x6b);
+ SETREG (0xf0,0x55);
+ SETREG (0xf1,0x6c);
+ SETREG (0xf2,0x6a);
+ SETREG (0xf3,0xb4);
+ SETREG (0xf4,0x6a);
+ SETREG (0xf5,0xb5);
+ SETREG (0xf6,0x7f);
+ SETREG (0xf7,0xfd);*/
+
+ SETREG (0xf8,0x01); /* other value is 0x05 */
+ SETREG (0xf9,0x00);
+ SETREG (0xfa,0x00);
+ SETREG (0xfb,0x00);
+ SETREG (0xfc,0x00);
+ SETREG (0xff,0x00);
+
+ /* fine tune upon device description */
+ dev->reg[reg_0x05].value &= ~REG05_DPIHW;
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* initalize calibration reg */
+ memcpy (dev->calib_reg, dev->reg,
+ GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ DBGCOMPLETED;
+}
+
+/**@brief send slope table for motor movement
+ * Send slope_table in machine byte order
+ * @param dev device to send slope table
+ * @param table_nr index of the slope table in ASIC memory
+ * Must be in the [0-4] range.
+ * @param slope_table pointer to 16 bit values array of the slope table
+ * @param steps number of elemnts in the slope table
+ */
+static SANE_Status
+gl124_send_slope_table (Genesys_Device * dev, int table_nr,
+ uint16_t * slope_table, int steps)
+{
+ SANE_Status status;
+ uint8_t *table;
+ int i;
+ char msg[10000];
+
+ DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __FUNCTION__,
+ table_nr, steps);
+
+ /* sanity check */
+ if(table_nr<0 || table_nr>4)
+ {
+ DBG (DBG_error, "%s: invalid table number %d!\n", __FUNCTION__, table_nr);
+ return SANE_STATUS_INVAL;
+ }
+
+ table = (uint8_t *) malloc (steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sprintf (msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++)
+ {
+ sprintf (msg+strlen(msg), ",%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __FUNCTION__, msg);
+ }
+
+ /* slope table addresses are fixed */
+ status =
+ sanei_genesys_write_ahb (dev->dn, dev->usb_mode, 0x10000000 + 0x4000 * table_nr, steps * 2, table);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: write to AHB failed writing slope table %d (%s)\n",
+ __FUNCTION__, table_nr, sane_strstatus (status));
+ }
+
+ free (table);
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * Set register values of 'special' type frontend
+ * */
+static SANE_Status
+gl124_set_ti_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ uint16_t val;
+
+ DBGSTART;
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "%s: setting DAC %u\n", __FUNCTION__,
+ dev->model->dac_type);
+
+ /* sets to default values */
+ sanei_genesys_init_fe (dev);
+ }
+
+ /* start writing to DAC */
+ status = sanei_genesys_fe_write_data (dev, 0x00, 0x80);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to write reg0: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* write values to analog frontend */
+ for (i = 1; i < 4; i++)
+ {
+ val = dev->frontend.reg[i];
+ status = sanei_genesys_fe_write_data (dev, i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write reg %d: %s\n", __FUNCTION__, i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x04, 0x00);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to write reg4: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* these are not really sign */
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.sign[i];
+ status = sanei_genesys_fe_write_data (dev, 0x05 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write reg %d: %s\n", __FUNCTION__, i+5,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* close writing to DAC */
+ status = sanei_genesys_fe_write_data (dev, 0x00, 0x11);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to write reg0: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+
+/* Set values of analog frontend */
+static SANE_Status
+gl124_set_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBG (DBG_proc, "gl124_set_fe (%s)\n",
+ set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
+ AFE_POWER_SAVE ? "powersave" : "huh?");
+
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl124_set_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+ sanei_genesys_init_fe (dev);
+ }
+
+ RIE (sanei_genesys_read_register (dev, REG0A, &val));
+
+ if(dev->usb_mode<0)
+ {
+ val=3<<REG0AS_SIFSEL;
+ }
+
+ /* route to correct analog FE */
+ switch ((val & REG0A_SIFSEL)>>REG0AS_SIFSEL)
+ {
+ case 3:
+ status=gl124_set_ti_fe (dev, set);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ default:
+ DBG (DBG_error, "%s: unsupported analog FE 0x%02x\n",__FUNCTION__,val);
+ status=SANE_STATUS_INVAL;
+ break;
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/**@brief compute exposure to use
+ * compute the sensor exposure based on target resolution
+ * @param dev pointer to device description
+ * @param xres sensor's required resolution
+ * @param half_ccd flag for half ccd mode
+ */
+static int gl124_compute_exposure(Genesys_Device *dev, int xres, int half_ccd)
+{
+ Sensor_Profile *sensor;
+
+ sensor=get_sensor_profile(dev->model->ccd_type, xres, half_ccd);
+ return sensor->exposure;
+}
+
+/**@brief compute motor step type to use
+ * compute the step type (full, half, quarter, ...) to use based
+ * on target resolution
+ * @param dev device description
+ * @param exposure sensor exposure
+ * @return 0 for full step
+ * 1 for half step
+ * 2 for quarter step
+ * 3 for eighth step
+ */
+static int gl124_compute_step_type(Genesys_Device *dev, int exposure)
+{
+Motor_Profile *profile;
+
+ profile=get_motor_profile(dev->model->motor_type,exposure);
+ return profile->step_type;
+}
+
+
+static SANE_Status
+gl124_init_motor_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int scan_exposure_time,
+ float scan_yres,
+ int scan_step_type,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ int scan_mode,
+ unsigned int flags)
+{
+ SANE_Status status;
+ int use_fast_fed;
+ unsigned int lincnt, fast_dpi;
+ uint16_t scan_table[SLOPE_TABLE_SIZE];
+ uint16_t fast_table[SLOPE_TABLE_SIZE];
+ int scan_steps,fast_steps,factor;
+ unsigned int feedl,dist;
+ Genesys_Register_Set *r;
+ uint32_t z1, z2;
+ float yres;
+ int min_speed;
+ unsigned int linesel;
+
+ DBGSTART;
+ DBG (DBG_info, "gl124_init_motor_regs_scan : scan_exposure_time=%d, "
+ "scan_yres=%g, scan_step_type=%d, scan_lines=%d, scan_dummy=%d, "
+ "feed_steps=%d, scan_mode=%d, flags=%x\n",
+ scan_exposure_time,
+ scan_yres,
+ scan_step_type,
+ scan_lines, scan_dummy, feed_steps, scan_mode, flags);
+
+ /* we never use fast fed since we do manual feed for the scans */
+ use_fast_fed=0;
+ factor=1;
+
+ /* enforce motor minimal scan speed
+ * @TODO extend motor struct for this value */
+ if (scan_mode == SCAN_MODE_COLOR)
+ {
+ min_speed = 900;
+ }
+ else
+ {
+ min_speed = 600;
+ if(dev->model->ccd_type==MOTOR_CANONLIDE110)
+ {
+ min_speed = 300;
+ }
+ }
+
+ /* compute min_speed and linesel */
+ if(scan_yres<min_speed)
+ {
+ yres=min_speed;
+ linesel=yres/scan_yres-1;
+ }
+ else
+ {
+ yres=scan_yres;
+ linesel=0;
+ }
+
+ DBG (DBG_io2, "%s: linesel=%d\n", __FUNCTION__, linesel);
+
+ lincnt=scan_lines*(linesel+1);
+ sanei_genesys_set_triple(reg,REG_LINCNT,lincnt);
+ DBG (DBG_io, "%s: lincnt=%d\n", __FUNCTION__, lincnt);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address (reg, REG02);
+ r->value = REG02_NOTHOME;
+ r->value |= REG02_MTRPWR;
+
+ if (use_fast_fed)
+ r->value |= REG02_FASTFED;
+ else
+ r->value &= ~REG02_FASTFED;
+
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME)
+ r->value |= REG02_AGOHOME;
+
+ if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ ||(yres>=dev->sensor.optical_res))
+ r->value |= REG02_ACDCDIS;
+
+ /* SCANFED */
+ sanei_genesys_set_double(reg,REG_SCANFED,4);
+
+ /* scan and backtracking slope table */
+ gl124_slope_table(scan_table,
+ &scan_steps,
+ yres,
+ scan_exposure_time,
+ dev->motor.base_ydpi,
+ scan_step_type,
+ factor,
+ dev->model->motor_type);
+ RIE(gl124_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps));
+ RIE(gl124_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps));
+
+ /* STEPNO */
+ sanei_genesys_set_double(reg,REG_STEPNO,scan_steps);
+
+ /* fast table */
+ fast_dpi=yres;
+ if (scan_mode != SCAN_MODE_COLOR)
+ {
+ fast_dpi*=3;
+ }
+ gl124_slope_table(fast_table,
+ &fast_steps,
+ fast_dpi,
+ scan_exposure_time,
+ dev->motor.base_ydpi,
+ scan_step_type,
+ factor,
+ dev->model->motor_type);
+ RIE(gl124_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps));
+ RIE(gl124_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps));
+
+ /* FASTNO */
+ sanei_genesys_set_double(reg,REG_FASTNO,fast_steps);
+
+ /* FSHDEC */
+ sanei_genesys_set_double(reg,REG_FSHDEC,fast_steps);
+
+ /* FMOVNO */
+ sanei_genesys_set_double(reg,REG_FMOVNO,fast_steps);
+
+ /* substract acceleration distance from feedl */
+ feedl=feed_steps;
+ feedl<<=scan_step_type;
+
+ dist = scan_steps;
+ if (flags & MOTOR_FLAG_FEED)
+ dist *=2;
+ if (use_fast_fed)
+ {
+ dist += fast_steps*2;
+ }
+ DBG (DBG_io2, "%s: acceleration distance=%d\n", __FUNCTION__, dist);
+
+ /* get sure we don't use insane value */
+ if(dist<feedl)
+ feedl -= dist;
+ else
+ feedl = 0;
+
+ sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
+ DBG (DBG_io, "%s: feedl=%d\n", __FUNCTION__, feedl);
+
+ /* doesn't seem to matter that much */
+ sanei_genesys_calculate_zmode2 (use_fast_fed,
+ scan_exposure_time,
+ scan_table,
+ scan_steps,
+ feedl,
+ scan_steps,
+ &z1,
+ &z2);
+
+ sanei_genesys_set_triple(reg,REG_Z1MOD,z1);
+ DBG (DBG_info, "gl124_init_motor_regs_scan: z1 = %d\n", z1);
+
+ sanei_genesys_set_triple(reg,REG_Z2MOD,z2);
+ DBG (DBG_info, "gl124_init_motor_regs_scan: z2 = %d\n", z2);
+
+ /* LINESEL */
+ r = sanei_genesys_get_address (reg, REG1D);
+ r->value = (r->value & ~REG1D_LINESEL) | linesel;
+
+ r = sanei_genesys_get_address (reg, REGA0);
+ r->value = (scan_step_type << REGA0S_STEPSEL) | (scan_step_type << REGA0S_FSTPSEL);
+
+ /* FMOVDEC */
+ sanei_genesys_set_double(reg,REG_FMOVDEC,fast_steps);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief copy sensor specific settings
+ * Set up register set for the given sensor resolution.
+ * @param dev device to set up
+ * @param regs register set to modify
+ * @param dpi resolution of the sensor during scan
+ * @param half_ccd flag for half ccd mode
+ * */
+static void
+gl124_setup_sensor (Genesys_Device * dev, Genesys_Register_Set * regs, int dpi, int half_ccd)
+{
+ Genesys_Register_Set *r;
+ int i;
+ Sensor_Profile *sensor;
+ int dpihw;
+ uint32_t exp;
+
+ DBGSTART;
+
+ /* we start at 6, 0-5 is a 16 bits cache for exposure */
+ for (i = 0x06; i < 0x0e; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ if (r)
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+
+ for (i = 0; i < 11; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ if (r)
+ r->value = dev->sensor.regs_0x52_0x5e[i];
+ }
+
+ /* set EXPDUMMY and CKxMAP */
+ dpihw=sanei_genesys_compute_dpihw(dev,dpi);
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw, half_ccd);
+
+ r = sanei_genesys_get_address (regs, 0x18);
+ if (r)
+ {
+ r->value = sensor->reg18;
+ }
+ r = sanei_genesys_get_address (regs, 0x20);
+ if (r)
+ {
+ r->value = sensor->reg20;
+ }
+ r = sanei_genesys_get_address (regs, 0x61);
+ if (r)
+ {
+ r->value = sensor->reg61;
+ }
+ r = sanei_genesys_get_address (regs, 0x98);
+ if (r)
+ {
+ r->value = sensor->reg98;
+ }
+
+ sanei_genesys_set_triple(regs,REG_SEGCNT,sensor->segcnt);
+ sanei_genesys_set_double(regs,REG_TG0CNT,sensor->tg0cnt);
+ sanei_genesys_set_double(regs,REG_EXPDMY,sensor->expdummy);
+
+ /* if no calibration has been done, set default values for exposures */
+ exp=dev->sensor.regs_0x10_0x1d[0]*256+dev->sensor.regs_0x10_0x1d[1];
+ if(exp==0)
+ {
+ exp=sensor->expr;
+ }
+ sanei_genesys_set_triple(regs,REG_EXPR,exp);
+
+ exp=dev->sensor.regs_0x10_0x1d[2]*256+dev->sensor.regs_0x10_0x1d[3];
+ if(exp==0)
+ {
+ exp=sensor->expg;
+ }
+ sanei_genesys_set_triple(regs,REG_EXPG,exp);
+
+ exp=dev->sensor.regs_0x10_0x1d[4]*256+dev->sensor.regs_0x10_0x1d[5];
+ if(exp==0)
+ {
+ exp=sensor->expb;
+ }
+ sanei_genesys_set_triple(regs,REG_EXPB,exp);
+
+ sanei_genesys_set_triple(regs,REG_CK1MAP,sensor->ck1map);
+ sanei_genesys_set_triple(regs,REG_CK3MAP,sensor->ck3map);
+ sanei_genesys_set_triple(regs,REG_CK4MAP,sensor->ck4map);
+
+ /* order of the sub-segments */
+ dev->order=sensor->order;
+
+ DBGCOMPLETED;
+}
+
+/** @brief setup optical related registers
+ * start and pixels are expressed in optical sensor resolution coordinate
+ * space.
+ * @param dev scanner device to use
+ * @param reg registers to set up
+ * @param exposure_time exposure time to use
+ * @param used_res scanning resolution used, may differ from
+ * scan's one
+ * @param start logical start pixel coordinate
+ * @param pixels logical number of pixels to use
+ * @param channels number of color channels (currently 1 or 3)
+ * @param depth bit depth of the scan (1, 8 or 16)
+ * @param half_ccd SANE_TRUE if sensor's timings are such that x coordinates
+ * must be halved
+ * @param color_filter color channel to use as gray data
+ * @param flags optical flags (@see )
+ * @return SANE_STATUS_GOOD if OK
+ */
+static SANE_Status
+gl124_init_optical_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int exposure_time,
+ int used_res,
+ unsigned int start,
+ unsigned int pixels,
+ int channels,
+ int depth,
+ SANE_Bool half_ccd,
+ int color_filter,
+ int flags)
+{
+ unsigned int words_per_line, segcnt;
+ unsigned int startx, endx, used_pixels, segnb;
+ unsigned int dpiset, cksel, dpihw, factor;
+ unsigned int bytes;
+ Genesys_Register_Set *r;
+ SANE_Status status;
+ uint32_t expmax, exp;
+
+ DBG (DBG_proc, "%s : exposure_time=%d, "
+ "used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
+ "half_ccd=%d, flags=%x\n", __FUNCTION__, exposure_time,
+ used_res, start, pixels, channels, depth, half_ccd, flags);
+
+ /* resolution is divided according to CKSEL */
+ r = sanei_genesys_get_address (reg, REG18);
+ cksel= (r->value & REG18_CKSEL)+1;
+ DBG (DBG_io2, "%s: cksel=%d\n", __FUNCTION__, cksel);
+
+ /* to manage high resolution device while keeping good
+ * low resolution scanning speed, we make hardware dpi vary */
+ dpihw=sanei_genesys_compute_dpihw(dev, used_res * cksel);
+ factor=dev->sensor.optical_res/dpihw;
+ DBG (DBG_io2, "%s: dpihw=%d (factor=%d)\n", __FUNCTION__, dpihw, factor);
+
+ /* sensor parameters */
+ gl124_setup_sensor (dev, reg, dpihw, half_ccd);
+ dpiset = used_res * cksel;
+
+ /* start and end coordinate in optical dpi coordinates */
+ /* startx = start/cksel + dev->sensor.dummy_pixel; XXX STEF XXX */
+ startx = start/cksel;
+ used_pixels=pixels/cksel;
+ endx = startx + used_pixels;
+
+ /* pixel coordinate factor correction when used dpihw is not maximal one */
+ startx/=factor;
+ endx/=factor;
+ used_pixels=endx-startx;
+
+ status = gl124_set_fe (dev, AFE_SET);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to set frontend: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* enable shading */
+ r = sanei_genesys_get_address (reg, REG01);
+ r->value &= ~REG01_SCAN;
+ if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG01_DVDSET;
+ }
+ else
+ {
+ r->value |= REG01_DVDSET;
+ }
+ r->value &= ~REG01_SCAN;
+
+ r = sanei_genesys_get_address (reg, REG03);
+ r->value &= ~REG03_AVEENB;
+
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value &= ~REG03_LAMPPWR;
+ else
+ r->value |= REG03_LAMPPWR;
+
+ /* BW threshold */
+ RIE (sanei_genesys_write_register (dev, REG114, dev->settings.threshold));
+ RIE (sanei_genesys_write_register (dev, REG115, dev->settings.threshold));
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, REG04);
+ switch (depth)
+ {
+ case 1:
+ r->value &= ~REG04_BITSET;
+ r->value |= REG04_LINEART;
+ break;
+ case 8:
+ r->value &= ~(REG04_LINEART | REG04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG04_LINEART;
+ r->value |= REG04_BITSET;
+ break;
+ }
+
+ r->value &= ~REG04_FILTER;
+ if (channels == 1)
+ {
+ switch (color_filter)
+ {
+ case 0:
+ r->value |= 0x10; /* red filter */
+ break;
+ case 2:
+ r->value |= 0x30; /* blue filter */
+ break;
+ default:
+ r->value |= 0x20; /* green filter */
+ break;
+ }
+ }
+
+ /* register 05 */
+ r = sanei_genesys_get_address (reg, REG05);
+
+ /* set up dpihw */
+ r->value &= ~REG05_DPIHW;
+ switch(dpihw)
+ {
+ case 600:
+ r->value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ r->value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ r->value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ r->value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* enable gamma tables */
+ if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
+ r->value &= ~REG05_GMMENB;
+ else
+ r->value |= REG05_GMMENB;
+
+ if(half_ccd)
+ {
+ sanei_genesys_set_double(reg,REG_DPISET,dpiset*2);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __FUNCTION__, dpiset*2);
+ }
+ else
+ {
+ sanei_genesys_set_double(reg,REG_DPISET,dpiset);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __FUNCTION__, dpiset);
+ }
+
+ r = sanei_genesys_get_address (reg, REG06);
+ r->value |= REG06_GAIN4;
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ /* we set up LEDADD only when asked */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ r = sanei_genesys_get_address (reg, REG60);
+ r->value &= ~REG60_LEDADD;
+ if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG60_LEDADD;
+ sanei_genesys_get_triple(reg,REG_EXPR,&expmax);
+ sanei_genesys_get_triple(reg,REG_EXPG,&exp);
+ if(exp>expmax)
+ {
+ expmax=exp;
+ }
+ sanei_genesys_get_triple(reg,REG_EXPB,&exp);
+ if(exp>expmax)
+ {
+ expmax=exp;
+ }
+ sanei_genesys_set_triple(dev->reg,REG_EXPR,expmax);
+ sanei_genesys_set_triple(dev->reg,REG_EXPG,expmax);
+ sanei_genesys_set_triple(dev->reg,REG_EXPB,expmax);
+ }
+ /* RGB weighting, REG_TRUER,G and B are to be set */
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG01_TRUEGRAY;
+ if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG01_TRUEGRAY;
+ sanei_genesys_write_register (dev, REG_TRUER, 0x80);
+ sanei_genesys_write_register (dev, REG_TRUEG, 0x80);
+ sanei_genesys_write_register (dev, REG_TRUEB, 0x80);
+ }
+ }
+
+ /* segment number */
+ r = sanei_genesys_get_address (reg, 0x98);
+ segnb = r->value & 0x0f;
+
+ sanei_genesys_set_triple(reg,REG_STRPIXEL,startx/segnb);
+ DBG (DBG_io2, "%s: strpixel used=%d\n", __FUNCTION__, startx/segnb);
+ sanei_genesys_get_triple(reg,REG_SEGCNT,&segcnt);
+ if(endx/segnb==segcnt)
+ {
+ endx=0;
+ }
+ sanei_genesys_set_triple(reg,REG_ENDPIXEL,endx/segnb);
+ DBG (DBG_io2, "%s: endpixel used=%d\n", __FUNCTION__, endx/segnb);
+
+ /* words(16bit) before gamma, conversion to 8 bit or lineart */
+ words_per_line = (used_pixels * dpiset) / dpihw;
+ bytes = depth / 8;
+ if (depth == 1)
+ {
+ words_per_line = (words_per_line >> 3) + ((words_per_line & 7) ? 1 : 0);
+ }
+ else
+ {
+ words_per_line *= bytes;
+ }
+
+ dev->bpl = words_per_line;
+ dev->cur = 0;
+ dev->skip = 0;
+ dev->len = dev->bpl/segnb;
+ dev->dist = dev->bpl/segnb;
+ dev->segnb = segnb;
+ dev->line_count = 0;
+ dev->line_interp = 0;
+
+ DBG (DBG_io2, "%s: used_pixels =%d\n", __FUNCTION__, used_pixels);
+ DBG (DBG_io2, "%s: pixels =%d\n", __FUNCTION__, pixels);
+ DBG (DBG_io2, "%s: depth =%d\n", __FUNCTION__, depth);
+ DBG (DBG_io2, "%s: dev->bpl =%lu\n", __FUNCTION__, (unsigned long)dev->bpl);
+ DBG (DBG_io2, "%s: dev->len =%lu\n", __FUNCTION__, (unsigned long)dev->len);
+ DBG (DBG_io2, "%s: dev->dist =%lu\n", __FUNCTION__, (unsigned long)dev->dist);
+ DBG (DBG_io2, "%s: dev->line_interp=%lu\n", __FUNCTION__, (unsigned long)dev->line_interp);
+
+ words_per_line *= channels;
+ dev->wpl = words_per_line;
+
+ /* allocate buffer for odd/even pixels handling */
+ if(dev->oe_buffer.buffer!=NULL)
+ {
+ sanei_genesys_buffer_free (&(dev->oe_buffer));
+ }
+ RIE (sanei_genesys_buffer_alloc (&(dev->oe_buffer), dev->wpl));
+
+ /* MAXWD is expressed in 2 words unit */
+ sanei_genesys_set_triple(reg,REG_MAXWD,(words_per_line));
+ DBG (DBG_io2, "%s: words_per_line used=%d\n", __FUNCTION__, words_per_line);
+
+ sanei_genesys_set_triple(reg,REG_LPERIOD,exposure_time);
+ DBG (DBG_io2, "%s: exposure_time used=%d\n", __FUNCTION__, exposure_time);
+
+ sanei_genesys_set_double(reg,REG_DUMMY,dev->sensor.dummy_pixel);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags)
+{
+ int used_res;
+ int start, used_pixels;
+ int bytes_per_line;
+ int move;
+ unsigned int lincnt;
+ unsigned int oflags, mflags; /**> optical and motor flags */
+ int exposure_time;
+ int stagger;
+
+ int dummy = 0;
+ int slope_dpi = 0;
+ int scan_step_type = 1;
+ int max_shift;
+ size_t requested_buffer_size, read_buffer_size;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl124_init_scan_regs settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g/%g\n"
+ "Depth/Channels: %u/%u\n"
+ "Flags : %x\n\n",
+ xres, yres, lines, pixels, startx, starty, depth, channels, flags);
+
+ half_ccd=compute_half_ccd(dev->model, xres);
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 2;
+ DBG (DBG_info, "%s: optical_res=%d\n", __FUNCTION__, optical_res);
+
+ /* stagger */
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl124_init_scan_regs : stagger=%d lines\n", stagger);
+
+ /** @brief compute used resolution */
+ if (flags & SCAN_FLAG_USE_OPTICAL_RES)
+ {
+ used_res = optical_res;
+ }
+ else
+ {
+ /* resolution is choosen from a fixed list and can be used directly,
+ * unless we have ydpi higher than sensor's maximum one */
+ if(xres>optical_res)
+ used_res=optical_res;
+ else
+ used_res = xres;
+ }
+
+ /* compute scan parameters values */
+ /* pixels are allways given at full optical resolution */
+ /* use detected left margin and fixed value */
+ /* start */
+ /* add x coordinates */
+ start = startx;
+
+ if (stagger > 0)
+ start |= 1;
+
+ /* compute correct pixels number */
+ used_pixels = (pixels * optical_res) / xres;
+ DBG (DBG_info, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+
+ /* round up pixels number if needed */
+ if (used_pixels * xres < pixels * optical_res)
+ used_pixels++;
+
+ /* we want even number of pixels here */
+ if(used_pixels & 1)
+ used_pixels++;
+
+ /* slope_dpi */
+ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres * channels;
+ else
+ slope_dpi = yres;
+
+ /* scan_step_type */
+ if(flags & SCAN_FLAG_FEEDING)
+ {
+ scan_step_type=0;
+ exposure_time=MOVE_EXPOSURE;
+ }
+ else
+ {
+ exposure_time = gl124_compute_exposure (dev, used_res, half_ccd);
+ scan_step_type = gl124_compute_step_type(dev, exposure_time);
+ }
+
+ DBG (DBG_info, "gl124_init_scan_regs : exposure_time=%d pixels\n", exposure_time);
+ DBG (DBG_info, "gl124_init_scan_regs : scan_step_type=%d\n", scan_step_type);
+
+ /*** optical parameters ***/
+ /* in case of dynamic lineart, we use an internal 8 bit gray scan
+ * to generate 1 lineart data */
+ if ((flags & SCAN_FLAG_DYNAMIC_LINEART)
+ && (dev->settings.scan_mode == SCAN_MODE_LINEART))
+ {
+ depth = 8;
+ }
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ oflags = 0;
+ if (flags & SCAN_FLAG_DISABLE_SHADING)
+ oflags |= OPTICAL_FLAG_DISABLE_SHADING;
+ if (flags & SCAN_FLAG_DISABLE_GAMMA)
+ oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
+ if (flags & SCAN_FLAG_DISABLE_LAMP)
+ oflags |= OPTICAL_FLAG_DISABLE_LAMP;
+ if (flags & SCAN_FLAG_CALIBRATION)
+ oflags |= OPTICAL_FLAG_DISABLE_DOUBLE;
+
+ if (dev->model->is_cis && dev->settings.true_gray)
+ {
+ oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
+ }
+
+ /* now _LOGICAL_ optical values used are known, setup registers */
+ status = gl124_init_optical_regs_scan (dev,
+ reg,
+ exposure_time,
+ used_res,
+ start,
+ used_pixels,
+ channels,
+ depth,
+ half_ccd,
+ color_filter,
+ oflags);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /*** motor parameters ***/
+
+ /* max_shift */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,flags);
+
+ /* lines to scan */
+ lincnt = lines + max_shift + stagger;
+
+ /* add tl_y to base movement */
+ move = starty;
+ DBG (DBG_info, "gl124_init_scan_regs: move=%d steps\n", move);
+
+ mflags=0;
+ if(flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ mflags|=MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
+ if(flags & SCAN_FLAG_FEEDING)
+ mflags|=MOTOR_FLAG_FEED;
+
+ status = gl124_init_motor_regs_scan (dev,
+ reg,
+ exposure_time,
+ slope_dpi,
+ scan_step_type,
+ dev->model->is_cis ? lincnt * channels : lincnt,
+ dummy,
+ move,
+ dev->settings.scan_mode,
+ mflags);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /*** prepares data reordering ***/
+
+ /* words_per_line */
+ bytes_per_line = (used_pixels * used_res) / optical_res;
+ bytes_per_line = (bytes_per_line * channels * depth) / 8;
+
+ /* since we don't have sheetfed scanners to handle,
+ * use huge read buffer */
+ /* TODO find the best size according to settings */
+ requested_buffer_size = 16 * bytes_per_line;
+
+ read_buffer_size =
+ 2 * requested_buffer_size +
+ ((max_shift + stagger) * used_pixels * channels * depth) / 8;
+
+ RIE (sanei_genesys_buffer_free (&(dev->read_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->read_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->lines_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->lines_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->shrink_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->shrink_buffer),
+ requested_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->out_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->out_buffer),
+ (8 * dev->settings.pixels * channels *
+ depth) / 8));
+
+
+ dev->read_bytes_left = bytes_per_line * lincnt;
+
+ DBG (DBG_info,
+ "gl124_init_scan_regs: physical bytes to read = %lu\n",
+ (u_long) dev->read_bytes_left);
+ dev->read_active = SANE_TRUE;
+
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ DBG (DBG_info, "%s: current_setup.pixels=%d\n", __FUNCTION__, dev->current_setup.pixels);
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ dev->total_bytes_read = 0;
+ if (depth == 1)
+ dev->total_bytes_to_read =
+ ((dev->settings.pixels * dev->settings.lines) / 8 +
+ (((dev->settings.pixels * dev->settings.lines) % 8) ? 1 : 0)) *
+ channels;
+ else
+ dev->total_bytes_to_read =
+ dev->settings.pixels * dev->settings.lines * channels * (depth / 8);
+
+ DBG (DBG_info, "gl124_init_scan_regs: total bytes to send = %lu\n",
+ (u_long) dev->total_bytes_to_read);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl124_calculate_current_setup (Genesys_Device * dev)
+{
+ int channels;
+ int depth;
+ int start;
+
+ float xres; /*dpi */
+ float yres; /*dpi */
+ float startx; /*optical_res, from dummy_pixel+1 */
+ float pixels;
+ float lines;
+
+ int used_res;
+ int used_pixels;
+ unsigned int lincnt;
+ int exposure_time;
+ int stagger;
+ SANE_Bool half_ccd;
+
+ int max_shift, dpihw;
+ Sensor_Profile *sensor;
+
+ int optical_res;
+
+ DBG (DBG_info,
+ "gl124_calculate_current_setup settings:\n"
+ "Resolution: %ux%uDPI\n"
+ "Lines : %u\n"
+ "PPL : %u\n"
+ "Startpos : %.3f/%.3f\n"
+ "Scan mode : %d\n\n",
+ dev->settings.xres,
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ /* channels */
+ if (dev->settings.scan_mode == 4) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == 0)
+ depth = 1;
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+
+ xres = dev->settings.xres;
+ yres = dev->settings.yres;
+ startx = start;
+ pixels = dev->settings.pixels;
+ lines = dev->settings.lines;
+
+ half_ccd=compute_half_ccd(dev->model, xres);
+
+ DBG (DBG_info,
+ "gl124_calculate_current_setup settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g\n"
+ "Half ccd : %d\n"
+ "Depth/Channels: %u/%u\n\n",
+ xres, yres, lines, pixels, startx, depth, half_ccd, channels);
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+
+ if(xres<=optical_res)
+ used_res = xres;
+ else
+ used_res=optical_res;
+
+ /* compute scan parameters values */
+ /* pixels are allways given at half or full CCD optical resolution */
+ /* use detected left margin and fixed value */
+
+ /* compute correct pixels number */
+ used_pixels = (pixels * optical_res) / xres;
+ DBG (DBG_info, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+
+ /* exposure */
+ exposure_time = gl124_compute_exposure (dev, xres, half_ccd);
+ DBG (DBG_info, "%s : exposure_time=%d pixels\n", __FUNCTION__, exposure_time);
+
+ /* max_shift */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,0);
+
+ /* compute hw dpi for sensor */
+ dpihw=sanei_genesys_compute_dpihw(dev,used_res);
+
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw, half_ccd);
+ dev->segnb=sensor->reg98 & 0x0f;
+
+ /* stagger */
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "%s: stagger=%d lines\n", __FUNCTION__, stagger);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ DBG (DBG_info, "%s: current_setup.pixels=%d\n", __FUNCTION__, dev->current_setup.pixels);
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gl124_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
+{
+
+ DBG (DBG_proc, "gl124_set_motor_power\n");
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ | REG02_MTRPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ & ~REG02_MTRPWR);
+ }
+}
+
+static void
+gl124_set_lamp_power (Genesys_Device * dev,
+ Genesys_Register_Set * regs, SANE_Bool set)
+{
+ if (dev == NULL || regs==NULL)
+ return;
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x03,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x03)
+ | REG03_LAMPPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x03,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x03)
+ & ~REG03_LAMPPWR);
+ }
+}
+
+/**
+ * for fast power saving methods only, like disabling certain amplifiers
+ * @param dev device to use
+ * @param enable true to set inot powersaving
+ * */
+static SANE_Status
+gl124_save_power (Genesys_Device * dev, SANE_Bool enable)
+{
+ DBG (DBG_proc, "gl124_save_power: enable = %d\n", enable);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl124_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
+{
+ Genesys_Register_Set *r;
+
+ DBG (DBG_proc, "gl124_set_powersaving (delay = %d)\n", delay);
+
+ r = sanei_genesys_get_address (dev->reg, REG03);
+ r->value &= ~0xf0;
+ if(delay<15)
+ {
+ r->value |= delay;
+ }
+ else
+ {
+ r->value |= 0x0f;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_start_action (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x01);
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl124_stop_action (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val40, val;
+ unsigned int loop;
+
+ DBGSTART;
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl124_homsnr_gpio(dev);
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ status = sanei_genesys_read_register (dev, REG100, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read reg100: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* only stop action if needed */
+ if (!(val40 & REG100_DATAENB) && !(val40 & REG100_MOTMFLG))
+ {
+ DBG (DBG_info, "%s: already stopped\n", __FUNCTION__);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* ends scan */
+ val = sanei_genesys_read_reg_from_set (dev->reg, REG01);
+ val &= ~REG01_SCAN;
+ sanei_genesys_set_reg_from_set (dev->reg, REG01, val);
+ status = sanei_genesys_write_register (dev, REG01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write register 01: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (100 * 1000);
+
+ loop = 10;
+ while (loop > 0)
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ status = sanei_genesys_read_register (dev, REG100, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* if scanner is in command mode, we are done */
+ if (!(val40 & REG100_DATAENB) && !(val40 & REG100_MOTMFLG)
+ && !(val & MOTORENB))
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ usleep (100 * 1000);
+ loop--;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_IO_ERROR;
+}
+
+
+static SANE_Status
+gl124_setup_scan_gpio(Genesys_Device *dev, int resolution)
+{
+SANE_Status status;
+uint8_t val;
+
+ DBGSTART;
+ RIE (sanei_genesys_read_register (dev, REG32, &val));
+ if(resolution>=dev->motor.base_ydpi/2)
+ {
+ val &= 0xf7;
+ }
+ else if(resolution>=dev->motor.base_ydpi/4)
+ {
+ val &= 0xef;
+ }
+ else
+ {
+ val |= 0x10;
+ }
+ val |= 0x02;
+ RIE (sanei_genesys_write_register (dev, REG32, val));
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* Send the low-level scan command */
+/* todo : is this that useful ? */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBGSTART;
+ if (reg == NULL)
+ return SANE_STATUS_INVAL;
+
+ /* set up GPIO for scan */
+ RIE(gl124_setup_scan_gpio(dev,dev->settings.yres));
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* enable scan and motor */
+ RIE (sanei_genesys_read_register (dev, REG01, &val));
+ val |= REG01_SCAN;
+ RIE (sanei_genesys_write_register (dev, REG01, val));
+
+ if (start_motor)
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 1));
+ }
+ else
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 0));
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl124_end_scan (check_stop = %d)\n", check_stop);
+ if (reg == NULL)
+ return SANE_STATUS_INVAL;
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else /* flat bed scanners */
+ {
+ status = gl124_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_end_scan: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** @brief Moves the slider to the home (top) position slowly
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL124_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ uint8_t val;
+ float resolution;
+ int loop = 0;
+ int scan_mode;
+
+ DBG (DBG_proc, "gl124_slow_back_home (wait_until_home = %d)\n",
+ wait_until_home);
+
+ if(dev->usb_mode<0)
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl124_homsnr_gpio(dev);
+
+ /* first read gives HOME_SENSOR true */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ usleep (100000); /* sleep 100 ms */
+
+ /* second is reliable */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ /* is sensor at home? */
+ if (val & HOMESNR)
+ {
+ DBG (DBG_info, "%s: already at home, completed\n", __FUNCTION__);
+ dev->scanhead_position_in_steps = 0;
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy (local_reg, dev->reg, GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+ resolution=sanei_genesys_get_lowest_dpi(dev);
+
+ /* TODO add scan_mode to the API */
+ scan_mode= dev->settings.scan_mode;
+ dev->settings.scan_mode=SCAN_MODE_GRAY;
+ gl124_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 100,
+ 30000,
+ 100,
+ 100,
+ 8,
+ 1,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ dev->settings.scan_mode=scan_mode;
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* set up for reverse and no scan */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ r->value |= REG02_MTRREV;
+
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL124_MAX_REGS));
+
+ RIE(gl124_setup_scan_gpio(dev,resolution));
+
+ status = gl124_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_slow_back_home: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl124_stop_action (dev);
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL124_MAX_REGS);
+ return status;
+ }
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl124_homsnr_gpio(dev);
+
+ if (wait_until_home)
+ {
+
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (val & HOMESNR) /* home sensor */
+ {
+ DBG (DBG_info, "gl124_slow_back_home: reached home position\n");
+ DBGCOMPLETED;
+ dev->scanhead_position_in_steps = 0;
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl124_stop_action (dev);
+ DBG (DBG_error,
+ "gl124_slow_back_home: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_info, "gl124_slow_back_home: scanhead is still moving\n");
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief moves the slider to steps at motor base dpi
+ * @param dev device to work on
+ * @param steps number of steps to move
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl124_feed (Genesys_Device * dev, unsigned int steps)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL124_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ float resolution;
+ uint8_t val;
+
+ DBGSTART;
+ DBG (DBG_io, "%s: steps=%d\n", __FUNCTION__, steps);
+
+ /* prepare local registers */
+ memcpy (local_reg, dev->reg, GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+ gl124_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 0,
+ steps,
+ 100,
+ 3,
+ 8,
+ 3,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_FEEDING |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ /* set exposure to zero */
+ sanei_genesys_set_triple(local_reg,REG_EXPR,0);
+ sanei_genesys_set_triple(local_reg,REG_EXPG,0);
+ sanei_genesys_set_triple(local_reg,REG_EXPB,0);
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
+
+ /* set up for no scan */
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ /* send registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL124_MAX_REGS));
+
+ status = gl124_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to start motor: %s\n", __FUNCTION__, sane_strstatus (status));
+ gl124_stop_action (dev);
+
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL124_MAX_REGS);
+ return status;
+ }
+
+ /* wait until feed count reaches the required value, but do not
+ * exceed 30s */
+ do
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ }
+ while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
+
+ /* then stop scanning */
+ RIE(gl124_stop_action (dev));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
+ area at 600 dpi from very top of scanner */
+static SANE_Status
+gl124_search_start_position (Genesys_Device * dev)
+{
+ int size;
+ SANE_Status status;
+ uint8_t *data;
+ Genesys_Register_Set local_reg[GENESYS_GL124_MAX_REGS];
+ int steps;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ DBG (DBG_proc, "gl124_search_start_position\n");
+
+ memcpy (local_reg, dev->reg,
+ GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ status = gl124_init_scan_regs (dev, local_reg, dpi, dpi, 0, 0, /*we should give a small offset here~60 steps */
+ 600, dev->model->search_lines, 8, 1, 1, /*green */
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE);
+ if (status!=SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to init scan registers: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send to scanner */
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL124_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_search_start_position: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ size = pixels * dev->model->search_lines;
+
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error,
+ "gl124_search_start_position: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = gl124_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl124_search_start_position: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl124_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("search_position.pnm", data, 8, 1, pixels,
+ dev->model->search_lines);
+
+ status = gl124_end_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl124_search_start_position: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* update regs to copy ASIC internal state */
+ memcpy (dev->reg, local_reg,
+ GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ status =
+ sanei_genesys_search_reference_point (dev, data, 0, dpi, pixels,
+ dev->model->search_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl124_search_start_position: failed to set search reference point: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ free (data);
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sets up register for coarse gain calibration
+ * todo: check it for scanners using it */
+static SANE_Status
+gl124_init_regs_for_coarse_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t channels;
+ uint8_t cksel;
+
+ DBGSTART;
+ cksel = (dev->calib_reg[reg_0x18].value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
+
+ /* set line size */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ status = gl124_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ dev->sensor.optical_res / cksel,
+ 20,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_FEEDING |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_init_register_for_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gl124_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ DBG (DBG_info,
+ "gl124_init_register_for_coarse_calibration: optical sensor res: %d dpi, actual res: %d\n",
+ dev->sensor.optical_res / cksel, dev->settings.xres);
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_init_register_for_coarse_calibration: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* init registers for shading calibration */
+/* shading calibration is done at dpihw */
+static SANE_Status
+gl124_init_regs_for_shading (Genesys_Device * dev)
+{
+ SANE_Status status;
+ int move, resolution, dpihw, factor;
+
+ DBGSTART;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ dev->calib_channels = 3;
+ dev->calib_lines = dev->model->shading_lines;
+ dpihw=sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ if(dpihw>=2400)
+ {
+ dev->calib_lines *= 2;
+ }
+ resolution=dpihw;
+
+ /* if half CCD mode, use half resolution */
+ if(compute_half_ccd(dev->model, dev->settings.xres)==SANE_TRUE)
+ {
+ resolution /= 2;
+ dev->calib_lines /= 2;
+ }
+ dev->calib_resolution = resolution;
+ factor=dev->sensor.optical_res/resolution;
+ dev->calib_pixels = dev->sensor.sensor_pixels/factor;
+
+ /* distance to move to reach white target at high resolution */
+ move=0;
+ if(dev->settings.yres>=1200)
+ {
+ move = SANE_UNFIX (dev->model->y_offset_calib);
+ move = (move * (dev->motor.base_ydpi/4)) / MM_PER_INCH;
+ }
+ DBG (DBG_io, "%s: move=%d steps\n", __FUNCTION__, move);
+
+ status = gl124_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ move,
+ dev->calib_pixels,
+ dev->calib_lines,
+ 16,
+ dev->calib_channels,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ gl124_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->scanhead_position_in_steps += dev->calib_lines + move;
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to bulk write registers: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief set up registers for the actual scan
+ */
+static SANE_Status
+gl124_init_regs_for_scan (Genesys_Device * dev)
+{
+ int channels;
+ int flags;
+ int depth;
+ float move;
+ int move_dpi;
+ float start;
+ uint8_t val40,val;
+
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl124_init_regs_for_scan settings:\nResolution: %ux%uDPI\n"
+ "Lines : %u\npixels : %u\nStartpos : %.3f/%.3f\nScan mode : %d\n\n",
+ dev->settings.xres,
+ dev->settings.yres,
+ dev->settings.lines,
+ dev->settings.pixels,
+ dev->settings.tl_x,
+ dev->settings.tl_y,
+ dev->settings.scan_mode);
+
+ /* wait for motor to stop first */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to read status: %s\n", __FUNCTION__, sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+ status = sanei_genesys_read_register (dev, REG100, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to read reg100: %s\n", __FUNCTION__, sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+ if((val & MOTORENB) || (val40 & REG100_MOTMFLG))
+ {
+ do
+ {
+ usleep(10000);
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to read status: %s\n", __FUNCTION__, sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+ status = sanei_genesys_read_register (dev, REG100, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to read reg100: %s\n", __FUNCTION__, sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+ } while ((val & MOTORENB) || (val40 & REG100_MOTMFLG));
+ usleep(50000);
+ }
+
+ /* ensure head is parked in case of calibration */
+ RIE (gl124_slow_back_home (dev, SANE_TRUE));
+
+ /* channels */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR)
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART)
+ depth = 1;
+
+ /* y (motor) distance to move to reach scanned area */
+ move_dpi = dev->motor.base_ydpi/4;
+ move = SANE_UNFIX (dev->model->y_offset);
+ move += dev->settings.tl_y;
+ move = (move * move_dpi) / MM_PER_INCH;
+ DBG (DBG_info, "%s: move=%f steps\n", __FUNCTION__, move);
+
+ if(channels*dev->settings.yres>=600 && move>700)
+ {
+ status = gl124_feed (dev, move-500);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to move to scan area\n",__FUNCTION__);
+ return status;
+ }
+ move=500;
+ }
+ DBG (DBG_info, "gl124_init_regs_for_scan: move=%f steps\n", move);
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ if(compute_half_ccd(dev->model, dev->settings.xres)==SANE_TRUE)
+ {
+ start /=2;
+ }
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+ flags = 0;
+
+ /* enable emulated lineart from gray data */
+ if(dev->settings.scan_mode == SCAN_MODE_LINEART
+ && dev->settings.dynamic_lineart)
+ {
+ flags |= SCAN_FLAG_DYNAMIC_LINEART;
+ }
+
+ status = gl124_init_scan_regs (dev,
+ dev->reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ start,
+ move,
+ dev->settings.pixels,
+ dev->settings.lines,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ flags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl124_send_shading_data (Genesys_Device * dev, uint8_t * data, int size)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint32_t addr, length, strpixel ,endpixel, x, factor, segcnt, pixels, i;
+ uint32_t lines, channels;
+ uint16_t dpiset,dpihw;
+ uint8_t val,*buffer,*ptr,*src;
+
+ DBGSTART;
+ DBG( DBG_io2, "%s: writing %d bytes of shading data\n",__FUNCTION__,size);
+
+ /* logical size of a color as seen by generic code of the frontend */
+ length = (uint32_t) (size / 3);
+ sanei_genesys_get_triple(dev->reg,REG_STRPIXEL,&strpixel);
+ sanei_genesys_get_triple(dev->reg,REG_ENDPIXEL,&endpixel);
+ sanei_genesys_get_triple(dev->reg,REG_SEGCNT,&segcnt);
+ if(endpixel==0)
+ {
+ endpixel=segcnt;
+ }
+ DBG( DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d, SEGCNT=%d\n",__FUNCTION__,strpixel,endpixel,endpixel-strpixel,segcnt);
+
+ /* compute deletion factor */
+ sanei_genesys_get_double(dev->reg,REG_DPISET,&dpiset);
+ dpihw=sanei_genesys_compute_dpihw(dev,dpiset);
+ factor=dpihw/dpiset;
+ DBG( DBG_io2, "%s: factor=%d\n",__FUNCTION__,factor);
+
+ /* binary data logging */
+ if(DBG_LEVEL>=DBG_data)
+ {
+ dev->binary=fopen("binary.pnm","wb");
+ sanei_genesys_get_triple(dev->reg, REG_LINCNT, &lines);
+ channels=dev->current_setup.channels;
+ if(dev->binary!=NULL)
+ {
+ fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels*dev->segnb,lines/channels,255);
+ }
+ }
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2; /* 2 words of 2 bytes */
+ endpixel*=2*2;
+ segcnt*=2*2;
+ pixels=endpixel-strpixel;
+
+ DBG( DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n",__FUNCTION__,length, length/4);
+ buffer=(uint8_t *)malloc(pixels*dev->segnb);
+ memset(buffer,0,pixels*dev->segnb);
+
+ /* write actual red data */
+ for(i=0;i<3;i++)
+ {
+ /* copy data to work buffer and process it */
+ /* coefficent destination */
+ ptr=buffer;
+
+ /* iterate on both sensor segment */
+ for(x=0;x<pixels;x+=4*factor)
+ {
+ /* coefficient source */
+ src=data+x+strpixel+i*length;
+
+ /* iterate over all the segments */
+ switch(dev->segnb)
+ {
+ case 1:
+ ptr[0+pixels*0]=src[0+segcnt*0];
+ ptr[1+pixels*0]=src[1+segcnt*0];
+ ptr[2+pixels*0]=src[2+segcnt*0];
+ ptr[3+pixels*0]=src[3+segcnt*0];
+ break;
+ case 2:
+ ptr[0+pixels*0]=src[0+segcnt*0];
+ ptr[1+pixels*0]=src[1+segcnt*0];
+ ptr[2+pixels*0]=src[2+segcnt*0];
+ ptr[3+pixels*0]=src[3+segcnt*0];
+ ptr[0+pixels*1]=src[0+segcnt*1];
+ ptr[1+pixels*1]=src[1+segcnt*1];
+ ptr[2+pixels*1]=src[2+segcnt*1];
+ ptr[3+pixels*1]=src[3+segcnt*1];
+ break;
+ case 4:
+ ptr[0+pixels*0]=src[0+segcnt*0];
+ ptr[1+pixels*0]=src[1+segcnt*0];
+ ptr[2+pixels*0]=src[2+segcnt*0];
+ ptr[3+pixels*0]=src[3+segcnt*0];
+ ptr[0+pixels*1]=src[0+segcnt*2];
+ ptr[1+pixels*1]=src[1+segcnt*2];
+ ptr[2+pixels*1]=src[2+segcnt*2];
+ ptr[3+pixels*1]=src[3+segcnt*2];
+ ptr[0+pixels*2]=src[0+segcnt*1];
+ ptr[1+pixels*2]=src[1+segcnt*1];
+ ptr[2+pixels*2]=src[2+segcnt*1];
+ ptr[3+pixels*2]=src[3+segcnt*1];
+ ptr[0+pixels*3]=src[0+segcnt*3];
+ ptr[1+pixels*3]=src[1+segcnt*3];
+ ptr[2+pixels*3]=src[2+segcnt*3];
+ ptr[3+pixels*3]=src[3+segcnt*3];
+ break;
+ }
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+ RIE (sanei_genesys_read_register (dev, 0xd0+i, &val));
+ addr = val * 8192 + 0x10000000;
+ status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, addr, pixels*dev->segnb, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl124_send_shading_data; write to AHB failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ free(buffer);
+ DBGCOMPLETED;
+
+ return status;
+}
+
+
+/** @brief move to calibration area
+ * This functions moves scanning head to calibration area
+ * by doing a 600 dpi scan
+ * @param dev scanner device
+ * @return SANE_STATUS_GOOD on success, else the error code
+ */
+static SANE_Status
+move_to_calibration_area (Genesys_Device * dev)
+{
+ int pixels;
+ int size;
+ uint8_t *line;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ pixels = (dev->sensor.sensor_pixels*600)/dev->sensor.optical_res;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* set up for the calibration scan */
+ status = gl124_init_scan_regs (dev,
+ dev->calib_reg,
+ 600,
+ 600,
+ 0,
+ 0,
+ pixels,
+ 1,
+ 8,
+ 3,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ size = pixels * 3;
+ line = malloc (size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ /* write registers and scan data */
+ RIEF (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS), line);
+
+ DBG (DBG_info, "%s: starting line reading\n", __FUNCTION__);
+ RIEF (gl124_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, size), line);
+
+ /* stop scanning */
+ RIE (gl124_stop_action (dev));
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file ("movetocalarea.pnm", line, 8, 3, pixels, 1);
+ }
+
+ /* cleanup before return */
+ free (line);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* this function does the led calibration by scanning one line of the calibration
+ area below scanner's top on white strip.
+
+-needs working coarse/gain
+*/
+static SANE_Status
+gl124_led_calibration (Genesys_Device * dev)
+{
+ int num_pixels;
+ int total_size;
+ int resolution;
+ int dpihw;
+ uint8_t *line;
+ int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ int channels, depth;
+ int avg[3];
+ int turn;
+ char fn[20];
+ uint16_t exp[3],target;
+ Sensor_Profile *sensor;
+ SANE_Bool acceptable;
+ SANE_Bool half_ccd;
+
+ DBGSTART;
+
+ /* move to calibration area */
+ move_to_calibration_area(dev);
+
+ /* offset calibration is always done in 16 bit depth color mode */
+ channels = 3;
+ depth=16;
+ dpihw=sanei_genesys_compute_dpihw(dev, dev->settings.xres);
+ half_ccd=compute_half_ccd(dev->model, dev->settings.xres);
+ if(half_ccd==SANE_TRUE)
+ {
+ resolution = dpihw/2;
+ }
+ else
+ {
+ resolution = dpihw;
+ }
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw, half_ccd);
+ num_pixels = (dev->sensor.sensor_pixels*resolution)/dev->sensor.optical_res;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL124_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* set up for the calibration scan */
+ status = gl124_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ num_pixels,
+ 1,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ total_size = num_pixels * channels * (depth/8) * 1; /* colors * bytes_per_color * scan lines */
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ /* initial loop values and boundaries */
+ exp[0]=sensor->expr;
+ exp[1]=sensor->expg;
+ exp[2]=sensor->expb;
+ target=dev->sensor.gain_white_ref*256;
+
+ turn = 0;
+
+ /* no move during led calibration */
+ gl124_set_motor_power (dev->calib_reg, SANE_FALSE);
+ do
+ {
+ /* set up exposure */
+ sanei_genesys_set_triple(dev->calib_reg,REG_EXPR,exp[0]);
+ sanei_genesys_set_triple(dev->calib_reg,REG_EXPG,exp[1]);
+ sanei_genesys_set_triple(dev->calib_reg,REG_EXPB,exp[2]);
+
+ /* write registers and scan data */
+ RIEF (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS), line);
+
+ DBG (DBG_info, "gl124_led_calibration: starting line reading\n");
+ RIEF (gl124_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ /* stop scanning */
+ RIEF (gl124_stop_action (dev), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf (fn, 20, "led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file (fn, line, depth, channels, num_pixels, 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG (DBG_info, "gl124_led_calibration: average: %d,%d,%d\n", avg[0], avg[1], avg[2]);
+
+ /* check if exposure gives average within the boundaries */
+ acceptable = SANE_TRUE;
+ for(i=0;i<3;i++)
+ {
+ /* we accept +- 2% delta from target */
+ if(abs(avg[i]-target)>target/50)
+ {
+ exp[i]=(exp[i]*target)/avg[i];
+ acceptable = SANE_FALSE;
+ }
+ }
+
+ turn++;
+ }
+ while (!acceptable && turn < 100);
+
+ DBG (DBG_info, "gl124_led_calibration: acceptable exposure: %d,%d,%d\n", exp[0], exp[1], exp[2]);
+
+ /* set these values as final ones for scan */
+ sanei_genesys_set_triple(dev->reg,REG_EXPR,exp[0]);
+ sanei_genesys_set_triple(dev->reg,REG_EXPG,exp[1]);
+ sanei_genesys_set_triple(dev->reg,REG_EXPB,exp[2]);
+
+ /* store in this struct since it is the one used by cache calibration */
+ dev->sensor.regs_0x10_0x1d[0] = (exp[0] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[1] = exp[0] & 0xff;
+ dev->sensor.regs_0x10_0x1d[2] = (exp[1] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[3] = exp[1] & 0xff;
+ dev->sensor.regs_0x10_0x1d[4] = (exp[2] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[5] = exp[2] & 0xff;
+
+ /* cleanup before return */
+ free (line);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * average dark pixels of a 8 bits scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG (DBG_info, "dark_average: avg[%d] = %d\n", k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG (DBG_info, "dark_average: average = %d\n", average);
+ return average;
+}
+
+
+static SANE_Status
+gl124_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t *first_line, *second_line, reg0a;
+ unsigned int channels, bpp;
+ char title[32];
+ int pass = 0, avg, total_size;
+ int topavg, bottomavg, resolution, lines;
+ int top, bottom, black_pixels, pixels;
+
+ DBGSTART;
+
+ /* no gain nor offset for TI AFE */
+ RIE (sanei_genesys_read_register (dev, REG0A, &reg0a));
+ if(((reg0a & REG0A_SIFSEL)>>REG0AS_SIFSEL)==3)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ resolution=dev->sensor.optical_res;
+ dev->calib_pixels = dev->sensor.sensor_pixels;
+ lines=1;
+ bpp=8;
+ pixels= (dev->sensor.sensor_pixels*resolution) / dev->sensor.optical_res;
+ black_pixels = (dev->sensor.black_pixels * resolution) / dev->sensor.optical_res;
+ DBG (DBG_io2, "gl124_offset_calibration: black_pixels=%d\n", black_pixels);
+
+ status = gl124_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_offset_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gl124_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ /* allocate memory for scans */
+ total_size = pixels * channels * lines * (bpp/8); /* colors * bytes_per_color * scan lines */
+
+ first_line = malloc (total_size);
+ if (!first_line)
+ return SANE_STATUS_NO_MEM;
+
+ second_line = malloc (total_size);
+ if (!second_line)
+ {
+ free (first_line);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* init gain */
+ dev->frontend.gain[0] = 0;
+ dev->frontend.gain[1] = 0;
+ dev->frontend.gain[2] = 0;
+
+ /* scan with no move */
+ bottom = 10;
+ dev->frontend.offset[0] = bottom;
+ dev->frontend.offset[1] = bottom;
+ dev->frontend.offset[2] = bottom;
+
+ RIEF2 (gl124_set_fe(dev, AFE_SET), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl124_offset_calibration: starting first line reading\n");
+ RIEF2 (gl124_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, first_line, total_size), first_line, second_line);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf(title,20,"offset%03d.pnm",bottom);
+ sanei_genesys_write_pnm_file (title, first_line, bpp, channels, pixels, lines);
+ }
+
+ bottomavg = dark_average (first_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_io2, "gl124_offset_calibration: bottom avg=%d\n", bottomavg);
+
+ /* now top value */
+ top = 255;
+ dev->frontend.offset[0] = top;
+ dev->frontend.offset[1] = top;
+ dev->frontend.offset[2] = top;
+ RIEF2 (gl124_set_fe(dev, AFE_SET), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl124_offset_calibration: starting second line reading\n");
+ RIEF2 (gl124_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ topavg = dark_average (second_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_io2, "gl124_offset_calibration: top avg=%d\n", topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.offset[0] = (top + bottom) / 2;
+ dev->frontend.offset[1] = (top + bottom) / 2;
+ dev->frontend.offset[2] = (top + bottom) / 2;
+
+ /* scan with no move */
+ RIEF2 (gl124_set_fe(dev, AFE_SET), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl124_offset_calibration: starting second line reading\n");
+ RIEF2 (gl124_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", dev->frontend.offset[1]);
+ sanei_genesys_write_pnm_file (title, second_line, bpp, channels, pixels, lines);
+ }
+
+ avg = dark_average (second_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_info, "gl124_offset_calibration: avg=%d offset=%d\n", avg,
+ dev->frontend.offset[1]);
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.offset[1];
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.offset[1];
+ }
+ }
+ DBG (DBG_info, "gl124_offset_calibration: offset=(%d,%d,%d)\n", dev->frontend.offset[0], dev->frontend.offset[1], dev->frontend.offset[2]);
+
+ /* cleanup before return */
+ free (first_line);
+ free (second_line);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* alternative coarse gain calibration
+ this on uses the settings from offset_calibration and
+ uses only one scanline
+ */
+/*
+ with offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+ */
+static SANE_Status
+gl124_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ int pixels;
+ int total_size;
+ uint8_t *line, reg0a;
+ int i, j, channels;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+ int resolution;
+ int bpp;
+
+ DBG (DBG_proc, "gl124_coarse_gain_calibration: dpi = %d\n", dpi);
+
+ /* no gain nor offset for TI AFE */
+ RIE (sanei_genesys_read_register (dev, REG0A, &reg0a));
+ if(((reg0a & REG0A_SIFSEL)>>REG0AS_SIFSEL)==3)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ /* follow CKSEL */
+ if(dev->settings.xres<dev->sensor.optical_res)
+ {
+ coeff=0.9;
+ /*resolution=dev->sensor.optical_res/2;*/
+ resolution=dev->sensor.optical_res;
+ }
+ else
+ {
+ resolution=dev->sensor.optical_res;
+ coeff=1.0;
+ }
+ lines=10;
+ bpp=8;
+ pixels = (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+
+ status = gl124_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ gl124_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (dev->model->cmd_set->bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL124_MAX_REGS));
+
+ total_size = pixels * channels * (16/bpp) * lines;
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ RIEF (gl124_set_fe(dev, AFE_SET), line);
+ RIEF (gl124_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("coarse.pnm", line, bpp, channels, pixels, lines);
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if(bpp==16)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * pixels + 1] * 256 +
+ line[i * 2 + j * 2 * pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ }
+ else
+ {
+ if (dev->model->is_cis)
+ val = line[i + j * pixels];
+ else
+ val = line[i * channels + j];
+ }
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = ((float) dev->sensor.gain_white_ref*coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = 283 - 208 / gain[j];
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.gain[j] = code;
+
+ DBG (DBG_proc,
+ "gl124_coarse_gain_calibration: channel %d, max=%d, gain = %f, setting:%d\n",
+ j, max[j], gain[j], dev->frontend.gain[j]);
+ }
+
+ if (dev->model->is_cis)
+ {
+ if (dev->frontend.gain[0] > dev->frontend.gain[1])
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ if (dev->frontend.gain[0] > dev->frontend.gain[2])
+ dev->frontend.gain[0] = dev->frontend.gain[2];
+ dev->frontend.gain[2] = dev->frontend.gain[1] = dev->frontend.gain[0];
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ dev->frontend.gain[2] = dev->frontend.gain[1];
+ }
+
+ free (line);
+
+ RIE (gl124_stop_action (dev));
+
+ status = gl124_slow_back_home (dev, SANE_TRUE);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/*
+ * wait for lamp warmup by scanning the same line until difference
+ * between 2 scans is below a threshold
+ */
+static SANE_Status
+gl124_init_regs_for_warmup (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ int *channels, int *total_size)
+{
+ int num_pixels;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBGSTART;
+ if (dev == NULL || reg == NULL || channels == NULL || total_size == NULL)
+ return SANE_STATUS_INVAL;
+
+ *channels=3;
+
+ memcpy (reg, dev->reg, (GENESYS_GL124_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+ status = gl124_init_scan_regs (dev,
+ reg,
+ dev->sensor.optical_res,
+ dev->motor.base_ydpi,
+ dev->sensor.sensor_pixels/4,
+ 0,
+ dev->sensor.sensor_pixels/2,
+ 1,
+ 8,
+ *channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl124_init_regs_for_warmup: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ num_pixels = dev->current_setup.pixels;
+
+ *total_size = num_pixels * 3 * 1; /* colors * bytes_per_color * scan lines */
+
+ gl124_set_motor_power (reg, SANE_FALSE);
+ RIE (dev->model->cmd_set->bulk_write_register (dev, reg, GENESYS_GL124_MAX_REGS));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+WRITE GPIO[17-21]= GPIO19
+WRITE GPOE[17-21]= GPOE21 GPOE20 GPOE19 GPOE18
+genesys_write_register(0xa8,0x3e)
+GPIO(0xa8)=0x3e
+ */
+static SANE_Status
+gl124_init_gpio (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx;
+
+ DBGSTART;
+
+ /* per model GPIO layout */
+ if (strcmp (dev->model->name, "canon-lide-110") == 0)
+ {
+ idx = 0;
+ }
+ else
+ { /* canon LiDE 210 case */
+ idx = 1;
+ }
+
+ RIE (sanei_genesys_write_register (dev, REG31, gpios[idx].r31));
+ RIE (sanei_genesys_write_register (dev, REG32, gpios[idx].r32));
+ RIE (sanei_genesys_write_register (dev, REG33, gpios[idx].r33));
+ RIE (sanei_genesys_write_register (dev, REG34, gpios[idx].r34));
+ RIE (sanei_genesys_write_register (dev, REG35, gpios[idx].r35));
+ RIE (sanei_genesys_write_register (dev, REG36, gpios[idx].r36));
+ RIE (sanei_genesys_write_register (dev, REG38, gpios[idx].r38));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * set memory layout by filling values in dedicated registers
+ */
+static SANE_Status
+gl124_init_memory_layout (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx = 0;
+
+ DBGSTART;
+
+ /* point to per model memory layout */
+ if (strcmp (dev->model->name, "canon-lide-110") == 0)
+ {
+ idx = 0;
+ }
+ else
+ { /* canon LiDE 210 case */
+ idx = 1;
+ }
+
+ /* setup base address for shading data. */
+ /* values must be multiplied by 8192=0x4000 to give address on AHB */
+ /* R-Channel shading bank0 address setting for CIS */
+ sanei_genesys_write_register (dev, 0xd0, layouts[idx].rd0);
+ /* G-Channel shading bank0 address setting for CIS */
+ sanei_genesys_write_register (dev, 0xd1, layouts[idx].rd1);
+ /* B-Channel shading bank0 address setting for CIS */
+ sanei_genesys_write_register (dev, 0xd2, layouts[idx].rd2);
+
+ /* setup base address for scanned data. */
+ /* values must be multiplied by 1024*2=0x0800 to give address on AHB */
+ /* R-Channel ODD image buffer 0x0124->0x92000 */
+ /* size for each buffer is 0x16d*1k word */
+ sanei_genesys_write_register (dev, 0xe0, layouts[idx].re0);
+ sanei_genesys_write_register (dev, 0xe1, layouts[idx].re1);
+/* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
+ sanei_genesys_write_register (dev, 0xe2, layouts[idx].re2);
+ sanei_genesys_write_register (dev, 0xe3, layouts[idx].re3);
+
+ /* R-Channel EVEN image buffer 0x0292 */
+ sanei_genesys_write_register (dev, 0xe4, layouts[idx].re4);
+ sanei_genesys_write_register (dev, 0xe5, layouts[idx].re5);
+/* R-Channel EVEN image buffer end-address 0x03ff*/
+ sanei_genesys_write_register (dev, 0xe6, layouts[idx].re6);
+ sanei_genesys_write_register (dev, 0xe7, layouts[idx].re7);
+
+/* same for green, since CIS, same addresses */
+ sanei_genesys_write_register (dev, 0xe8, layouts[idx].re0);
+ sanei_genesys_write_register (dev, 0xe9, layouts[idx].re1);
+ sanei_genesys_write_register (dev, 0xea, layouts[idx].re2);
+ sanei_genesys_write_register (dev, 0xeb, layouts[idx].re3);
+ sanei_genesys_write_register (dev, 0xec, layouts[idx].re4);
+ sanei_genesys_write_register (dev, 0xed, layouts[idx].re5);
+ sanei_genesys_write_register (dev, 0xee, layouts[idx].re6);
+ sanei_genesys_write_register (dev, 0xef, layouts[idx].re7);
+
+/* same for blue, since CIS, same addresses */
+ sanei_genesys_write_register (dev, 0xf0, layouts[idx].re0);
+ sanei_genesys_write_register (dev, 0xf1, layouts[idx].re1);
+ sanei_genesys_write_register (dev, 0xf2, layouts[idx].re2);
+ sanei_genesys_write_register (dev, 0xf3, layouts[idx].re3);
+ sanei_genesys_write_register (dev, 0xf4, layouts[idx].re4);
+ sanei_genesys_write_register (dev, 0xf5, layouts[idx].re5);
+ sanei_genesys_write_register (dev, 0xf6, layouts[idx].re6);
+ sanei_genesys_write_register (dev, 0xf7, layouts[idx].re7);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl124_init (Genesys_Device * dev)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+ DBGSTART;
+
+ status=sanei_genesys_asic_init(dev, GENESYS_GL124_MAX_REGS);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/* *
+ * initialize ASIC from power on condition
+ */
+static SANE_Status
+gl124_boot (Genesys_Device * dev, SANE_Bool cold)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBGSTART;
+
+ /* reset ASIC in case of cold boot */
+ if(cold)
+ {
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
+ }
+
+ /* enable GPOE 17 */
+ RIE (sanei_genesys_write_register (dev, 0x36, 0x01));
+
+ /* set GPIO 17 */
+ RIE (sanei_genesys_read_register (dev, 0x33, &val));
+ val |= 0x01;
+ RIE (sanei_genesys_write_register (dev, 0x33, val));
+
+ /* test CHKVER */
+ RIE (sanei_genesys_read_register (dev, REG100, &val));
+ if (val & REG100_CHKVER)
+ {
+ RIE (sanei_genesys_read_register (dev, 0x00, &val));
+ DBG (DBG_info,
+ "gl124_cold_boot: reported version for genesys chip is 0x%02x\n",
+ val);
+ }
+
+ /* Set default values for registers */
+ gl124_init_registers (dev);
+
+ /* Write initial registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL124_MAX_REGS));
+
+ /* tune reg 0B */
+ val = REG0B_30MHZ | REG0B_ENBDRAM | REG0B_64M;
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].address = 0x00;
+
+ /* set up end access */
+ RIE (sanei_genesys_write_0x8c (dev, 0x10, 0x0b));
+ RIE (sanei_genesys_write_0x8c (dev, 0x13, 0x0e));
+
+ /* CIS_LINE */
+ SETREG (0x08, REG08_CIS_LINE);
+ RIE (sanei_genesys_write_register (dev, 0x08, dev->reg[reg_0x08].value));
+
+ /* setup gpio */
+ RIE (gl124_init_gpio (dev));
+
+ /* setup internal memory layout */
+ RIE (gl124_init_memory_layout (dev));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+gl124_update_hardware_sensors (Genesys_Scanner * s)
+{
+ /* do what is needed to get a new set of events, but try to not loose
+ any of them.
+ */
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t val=0;
+
+ RIE (sanei_genesys_read_register (s->dev, REG31, &val));
+
+ /* TODO : for the next scanner special case,
+ * add another per scanner button profile struct to avoid growing
+ * hard-coded button mapping here.
+ */
+ if(s->dev->model->gpo_type == GPO_CANONLIDE110)
+ {
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & 0x01) == 0;
+ if (s->val[OPT_FILE_SW].b == s->last_val[OPT_FILE_SW].b)
+ s->val[OPT_FILE_SW].b = (val & 0x08) == 0;
+ if (s->val[OPT_EMAIL_SW].b == s->last_val[OPT_EMAIL_SW].b)
+ s->val[OPT_EMAIL_SW].b = (val & 0x04) == 0;
+ if (s->val[OPT_COPY_SW].b == s->last_val[OPT_COPY_SW].b)
+ s->val[OPT_COPY_SW].b = (val & 0x02) == 0;
+ }
+ else
+ { /* LiDE 210 case */
+ if (s->val[OPT_EXTRA_SW].b == s->last_val[OPT_EXTRA_SW].b)
+ s->val[OPT_EXTRA_SW].b = (val & 0x01) == 0;
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & 0x02) == 0;
+ if (s->val[OPT_COPY_SW].b == s->last_val[OPT_COPY_SW].b)
+ s->val[OPT_COPY_SW].b = (val & 0x04) == 0;
+ if (s->val[OPT_EMAIL_SW].b == s->last_val[OPT_EMAIL_SW].b)
+ s->val[OPT_EMAIL_SW].b = (val & 0x08) == 0;
+ if (s->val[OPT_FILE_SW].b == s->last_val[OPT_FILE_SW].b)
+ s->val[OPT_FILE_SW].b = (val & 0x10) == 0;
+ }
+ return status;
+}
+
+
+/** the gl124 command set */
+static Genesys_Command_Set gl124_cmd_set = {
+ "gl124-generic", /* the name of this set */
+
+ gl124_init,
+ gl124_init_regs_for_warmup,
+ gl124_init_regs_for_coarse_calibration,
+ gl124_init_regs_for_shading,
+ gl124_init_regs_for_scan,
+
+ gl124_get_filter_bit,
+ gl124_get_lineart_bit,
+ gl124_get_bitset_bit,
+ gl124_get_gain4_bit,
+ gl124_get_fast_feed_bit,
+ gl124_test_buffer_empty_bit,
+ gl124_test_motor_flag_bit,
+
+ gl124_bulk_full_size,
+
+ gl124_set_fe,
+ gl124_set_powersaving,
+ gl124_save_power,
+
+ gl124_set_motor_power,
+ gl124_set_lamp_power,
+
+ gl124_begin_scan,
+ gl124_end_scan,
+
+ sanei_genesys_send_gamma_table,
+
+ gl124_search_start_position,
+
+ gl124_offset_calibration,
+ gl124_coarse_gain_calibration,
+ gl124_led_calibration,
+
+ gl124_slow_back_home,
+
+ sanei_genesys_bulk_write_register,
+ NULL,
+ gl124_bulk_read_data,
+
+ gl124_update_hardware_sensors,
+
+ /* no sheetfed support for now */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ sanei_genesys_is_compatible_calibration,
+ NULL,
+ gl124_send_shading_data,
+ gl124_calculate_current_setup,
+ gl124_boot
+};
+
+SANE_Status
+sanei_gl124_init_cmd_set (Genesys_Device * dev)
+{
+ dev->model->cmd_set = &gl124_cmd_set;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl124.h b/backend/genesys_gl124.h
new file mode 100644
index 0000000..1b78af3
--- /dev/null
+++ b/backend/genesys_gl124.h
@@ -0,0 +1,711 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+
+#define REG01 0x01
+#define REG01_CISSET 0x80
+#define REG01_DOGENB 0x40
+#define REG01_DVDSET 0x20
+#define REG01_STAGGER 0x10
+#define REG01_COMPENB 0x08
+#define REG01_TRUEGRAY 0x04
+#define REG01_SHDAREA 0x02
+#define REG01_SCAN 0x01
+
+#define REG02 0x02
+#define REG02_NOTHOME 0x80
+#define REG02_ACDCDIS 0x40
+#define REG02_AGOHOME 0x20
+#define REG02_MTRPWR 0x10
+#define REG02_FASTFED 0x08
+#define REG02_MTRREV 0x04
+#define REG02_HOMENEG 0x02
+#define REG02_LONGCURV 0x01
+
+#define REG03 0x03
+#define REG03_LAMPDOG 0x80
+#define REG03_AVEENB 0x40
+#define REG03_XPASEL 0x20
+#define REG03_LAMPPWR 0x10
+#define REG03_LAMPTIM 0x0f
+
+#define REG04 0x04
+#define REG04_LINEART 0x80
+#define REG04_BITSET 0x40
+#define REG04_FILTER 0x30
+#define REG04_AFEMOD 0x07
+
+#define REG05 0x05
+#define REG05_DPIHW 0xc0
+#define REG05_DPIHW_600 0x00
+#define REG05_DPIHW_1200 0x40
+#define REG05_DPIHW_2400 0x80
+#define REG05_DPIHW_4800 0xc0
+#define REG05_MTLLAMP 0x30
+#define REG05_GMMENB 0x08
+#define REG05_ENB20M 0x04
+#define REG05_MTLBASE 0x03
+
+#define REG06 0x06
+#define REG06_SCANMOD 0xe0
+#define REG06S_SCANMOD 5
+#define REG06_PWRBIT 0x10
+#define REG06_GAIN4 0x08
+#define REG06_OPTEST 0x07
+
+#define REG07_LAMPSIM 0x80
+
+#define REG08_DRAM2X 0x80
+#define REG08_MPENB 0x20
+#define REG08_CIS_LINE 0x10
+#define REG08_IR2_ENB 0x08
+#define REG08_IR1_ENB 0x04
+#define REG08_ENB24M 0x01
+
+#define REG09_MCNTSET 0xc0
+#define REG09_EVEN1ST 0x20
+#define REG09_BLINE1ST 0x10
+#define REG09_BACKSCAN 0x08
+#define REG09_OUTINV 0x04
+#define REG09_SHORTTG 0x02
+
+#define REG09S_MCNTSET 6
+#define REG09S_CLKSET 4
+
+#define REG0A 0x0a
+#define REG0A_SIFSEL 0xc0
+#define REG0AS_SIFSEL 6
+#define REG0A_SHEETFED 0x20
+#define REG0A_LPWMEN 0x10
+
+#define REG0B 0x0b
+#define REG0B_DRAMSEL 0x07
+#define REG0B_16M 0x01
+#define REG0B_64M 0x02
+#define REG0B_128M 0x03
+#define REG0B_256M 0x04
+#define REG0B_512M 0x05
+#define REG0B_1G 0x06
+#define REG0B_ENBDRAM 0x08
+#define REG0B_RFHDIS 0x10
+#define REG0B_CLKSET 0xe0
+#define REG0B_24MHZ 0x00
+#define REG0B_30MHZ 0x20
+#define REG0B_40MHZ 0x40
+#define REG0B_48MHZ 0x60
+#define REG0B_60MHZ 0x80
+
+#define REG0D 0x0d
+#define REG0D_MTRP_RDY 0x80
+#define REG0D_FULLSTP 0x10
+#define REG0D_CLRMCNT 0x04
+#define REG0D_CLRDOCJM 0x02
+#define REG0D_CLRLNCNT 0x01
+
+#define REG0F 0x0f
+
+#define REG16_CTRLHI 0x80
+#define REG16_TOSHIBA 0x40
+#define REG16_TGINV 0x20
+#define REG16_CK1INV 0x10
+#define REG16_CK2INV 0x08
+#define REG16_CTRLINV 0x04
+#define REG16_CKDIS 0x02
+#define REG16_CTRLDIS 0x01
+
+#define REG17_TGMODE 0xc0
+#define REG17_SNRSYN 0x0f
+
+#define REG18 0x18
+#define REG18_CNSET 0x80
+#define REG18_DCKSEL 0x60
+#define REG18_CKTOGGLE 0x10
+#define REG18_CKDELAY 0x0c
+#define REG18_CKSEL 0x03
+
+#define REG1A_SW2SET 0x80
+#define REG1A_SW1SET 0x40
+#define REG1A_MANUAL3 0x02
+#define REG1A_MANUAL1 0x01
+#define REG1A_CK4INV 0x08
+#define REG1A_CK3INV 0x04
+#define REG1A_LINECLP 0x02
+
+#define REG1C_TBTIME 0x07
+
+#define REG1D 0x1d
+#define REG1D_CK4LOW 0x80
+#define REG1D_CK3LOW 0x40
+#define REG1D_CK1LOW 0x20
+#define REG1D_LINESEL 0x1f
+#define REG1DS_LINESEL 0
+
+#define REG1E 0x1e
+#define REG1E_WDTIME 0xf0
+#define REG1ES_WDTIME 4
+#define REG1E_WDTIME 0xf0
+
+#define REG30 0x30
+#define REG31 0x31
+#define REG32 0x32
+#define REG32_GPIO16 0x80
+#define REG32_GPIO15 0x40
+#define REG32_GPIO14 0x20
+#define REG32_GPIO13 0x10
+#define REG32_GPIO12 0x08
+#define REG32_GPIO11 0x04
+#define REG32_GPIO10 0x02
+#define REG32_GPIO9 0x01
+#define REG33 0x33
+#define REG34 0x34
+#define REG35 0x35
+#define REG36 0x36
+#define REG37 0x37
+#define REG38 0x38
+#define REG39 0x39
+
+#define REG60 0x60
+#define REG60_LED4TG 0x80
+#define REG60_YENB 0x40
+#define REG60_YBIT 0x20
+#define REG60_ACYNCNRLC 0x10
+#define REG60_ENOFFSET 0x08
+#define REG60_LEDADD 0x04
+#define REG60_CK4ADC 0x02
+#define REG60_AUTOCONF 0x01
+
+#define REG80 0x80
+#define REG81 0x81
+
+#define REGA0 0xa0
+#define REGA0_FSTPSEL 0x28
+#define REGA0S_FSTPSEL 3
+#define REGA0_STEPSEL 0x03
+#define REGA0S_STEPSEL 0
+
+#define REGA1 0xa1
+#define REGA2 0xa2
+#define REGA3 0xa3
+#define REGA4 0xa4
+#define REGA5 0xa5
+#define REGA6 0xa6
+#define REGA7 0xa7
+#define REGA8 0xa8
+#define REGA9 0xa9
+#define REGAA 0xaa
+#define REGAB 0xab
+#define REGAC 0xac
+#define REGAD 0xad
+#define REGAE 0xae
+#define REGAF 0xaf
+#define REGB0 0xb0
+#define REGB1 0xb1
+
+#define REGB2 0xb2
+#define REGB2_Z1MOD 0x1f
+#define REGB3 0xb3
+#define REGB3_Z1MOD 0xff
+#define REGB4 0xb4
+#define REGB4_Z1MOD 0xff
+
+#define REGB5 0xb5
+#define REGB5_Z2MOD 0x1f
+#define REGB6 0xb6
+#define REGB6_Z2MOD 0xff
+#define REGB7 0xb7
+#define REGB7_Z2MOD 0xff
+
+#define REG100 0x100
+#define REG100_DOCSNR 0x80
+#define REG100_ADFSNR 0x40
+#define REG100_COVERSNR 0x20
+#define REG100_CHKVER 0x10
+#define REG100_DOCJAM 0x08
+#define REG100_HISPDFLG 0x04
+#define REG100_MOTMFLG 0x02
+#define REG100_DATAENB 0x01
+
+#define REG114 0x114
+#define REG115 0x115
+
+#define REG_LINCNT 0x25
+#define REG_MAXWD 0x28
+#define REG_DPISET 0x2c
+#define REG_FEEDL 0x3d
+#define REG_CK1MAP 0x74
+#define REG_CK3MAP 0x77
+#define REG_CK4MAP 0x7a
+#define REG_LPERIOD 0x7d
+#define REG_DUMMY 0x80
+#define REG_STRPIXEL 0x82
+#define REG_ENDPIXEL 0x85
+#define REG_EXPDMY 0x88
+#define REG_EXPR 0x8a
+#define REG_EXPG 0x8d
+#define REG_EXPB 0x90
+#define REG_SEGCNT 0x93
+#define REG_TG0CNT 0x96
+#define REG_SCANFED 0xa2
+#define REG_STEPNO 0xa4
+#define REG_FWDSTEP 0xa6
+#define REG_BWDSTEP 0xa8
+#define REG_FASTNO 0xaa
+#define REG_FSHDEC 0xac
+#define REG_FMOVNO 0xae
+#define REG_FMOVDEC 0xb0
+#define REG_Z1MOD 0xb2
+#define REG_Z2MOD 0xb5
+
+#define REG_TRUER 0x110
+#define REG_TRUEG 0x111
+#define REG_TRUEB 0x112
+
+/**
+ * writable scanner registers */
+enum
+{
+ reg_0x01 = 0,
+ reg_0x02,
+ reg_0x03,
+ reg_0x04,
+ reg_0x05,
+ reg_0x06,
+ reg_0x07,
+ reg_0x08,
+ reg_0x09,
+ reg_0x0a,
+ reg_0x0b,
+ reg_0x0c,
+ reg_0x11,
+ reg_0x12,
+ reg_0x13,
+ reg_0x14,
+ reg_0x15,
+ reg_0x16,
+ reg_0x17,
+ reg_0x18,
+ reg_0x19,
+ reg_0x1a,
+ reg_0x1b,
+ reg_0x1c,
+ reg_0x1d,
+ reg_0x1e,
+ reg_0x1f,
+ reg_0x20,
+ reg_0x21,
+ reg_0x22,
+ reg_0x23,
+ reg_0x24,
+ reg_0x25,
+ reg_0x26,
+ reg_0x27,
+ reg_0x28,
+ reg_0x29,
+ reg_0x2a,
+ reg_0x2b,
+ reg_0x2c,
+ reg_0x2d,
+ reg_0x3b,
+ reg_0x3c,
+ reg_0x3d,
+ reg_0x3e,
+ reg_0x3f,
+ reg_0x40,
+ reg_0x41,
+ reg_0x42,
+ reg_0x43,
+ reg_0x44,
+ reg_0x45,
+ reg_0x46,
+ reg_0x47,
+ reg_0x48,
+ reg_0x49,
+ reg_0x4f,
+ reg_0x52,
+ reg_0x53,
+ reg_0x54,
+ reg_0x55,
+ reg_0x56,
+ reg_0x57,
+ reg_0x58,
+ reg_0x59,
+ reg_0x5a,
+ reg_0x5b,
+ reg_0x5c,
+ reg_0x5f,
+ reg_0x60,
+ reg_0x61,
+ reg_0x62,
+ reg_0x63,
+ reg_0x64,
+ reg_0x65,
+ reg_0x66,
+ reg_0x67,
+ reg_0x68,
+ reg_0x69,
+ reg_0x6a,
+ reg_0x6b,
+ reg_0x6c,
+ reg_0x6d,
+ reg_0x6e,
+ reg_0x6f,
+ reg_0x70,
+ reg_0x71,
+ reg_0x72,
+ reg_0x73,
+ reg_0x74,
+ reg_0x75,
+ reg_0x76,
+ reg_0x77,
+ reg_0x78,
+ reg_0x79,
+ reg_0x7a,
+ reg_0x7b,
+ reg_0x7c,
+ reg_0x7d,
+ reg_0x7e,
+ reg_0x7f,
+ reg_0x80,
+ reg_0x81,
+ reg_0x82,
+ reg_0x83,
+ reg_0x84,
+ reg_0x85,
+ reg_0x86,
+ reg_0x87,
+ reg_0x88,
+ reg_0x89,
+ reg_0x8a,
+ reg_0x8b,
+ reg_0x8c,
+ reg_0x8d,
+ reg_0x8e,
+ reg_0x8f,
+ reg_0x90,
+ reg_0x91,
+ reg_0x92,
+ reg_0x93,
+ reg_0x94,
+ reg_0x95,
+ reg_0x96,
+ reg_0x97,
+ reg_0x98,
+ reg_0x99,
+ reg_0x9a,
+ reg_0x9b,
+ reg_0x9c,
+ reg_0x9d,
+ reg_0x9e,
+ reg_0x9f,
+ reg_0xa0,
+ reg_0xa1,
+ reg_0xa2,
+ reg_0xa3,
+ reg_0xa4,
+ reg_0xa5,
+ reg_0xa6,
+ reg_0xa7,
+ reg_0xa8,
+ reg_0xa9,
+ reg_0xaa,
+ reg_0xab,
+ reg_0xac,
+ reg_0xad,
+ reg_0xae,
+ reg_0xaf,
+ reg_0xb0,
+ reg_0xb1,
+ reg_0xb2,
+ reg_0xb3,
+ reg_0xb4,
+ reg_0xb5,
+ reg_0xb6,
+ reg_0xb7,
+ reg_0xb8,
+ reg_0xbb,
+ reg_0xbc,
+ reg_0xbd,
+ reg_0xbe,
+ reg_0xc3,
+ reg_0xc4,
+ reg_0xc5,
+ reg_0xc6,
+ reg_0xc7,
+ reg_0xc8,
+ reg_0xc9,
+ reg_0xca,
+ reg_0xcb,
+ reg_0xcc,
+ reg_0xcd,
+ reg_0xce,
+ reg_0xd0,
+ reg_0xd1,
+ reg_0xd2,
+ reg_0xd3,
+ reg_0xd4,
+ reg_0xd5,
+ reg_0xd6,
+ reg_0xd7,
+ reg_0xd8,
+ reg_0xd9,
+ reg_0xe0,
+ reg_0xe1,
+ reg_0xe2,
+ reg_0xe3,
+ reg_0xe4,
+ reg_0xe5,
+ reg_0xe6,
+ reg_0xe7,
+ reg_0xe8,
+ reg_0xe9,
+ reg_0xea,
+ reg_0xeb,
+ reg_0xec,
+ reg_0xed,
+ reg_0xee,
+ reg_0xef,
+ reg_0xf0,
+ reg_0xf1,
+ reg_0xf2,
+ reg_0xf3,
+ reg_0xf4,
+ reg_0xf5,
+ reg_0xf6,
+ reg_0xf7,
+ reg_0xf8,
+ reg_0xf9,
+ reg_0xfa,
+ reg_0xfb,
+ reg_0xfc,
+ reg_0xff,
+ GENESYS_GL124_MAX_REGS
+};
+
+#define SETREG(adr,val) {dev->reg[reg_##adr].address=adr;dev->reg[reg_##adr].value=val;}
+
+typedef struct
+{
+ uint8_t r31;
+ uint8_t r32;
+ uint8_t r33;
+ uint8_t r34;
+ uint8_t r35;
+ uint8_t r36;
+ uint8_t r38;
+} Gpio_layout;
+
+/** @brief gpio layout
+ * describes initial gpio settings for a given model
+ */
+static Gpio_layout gpios[]={
+ /* LiDE 110 */
+ { /* 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38 */
+ 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00
+ },
+ /* LiDE 210 */
+ {
+ 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00
+ },
+};
+
+typedef struct
+{
+ uint8_t rd0;
+ uint8_t rd1;
+ uint8_t rd2;
+ uint8_t re0;
+ uint8_t re1;
+ uint8_t re2;
+ uint8_t re3;
+ uint8_t re4;
+ uint8_t re5;
+ uint8_t re6;
+ uint8_t re7;
+} Memory_layout;
+
+static Memory_layout layouts[]={
+ /* LIDE 110 */
+ {
+ 0x0a, 0x15, 0x20,
+ 0x00, 0xac, 0x08, 0x55, 0x08, 0x56, 0x0f, 0xff
+ },
+ /* LIDE 210 */
+ {
+ 0x0a, 0x1f, 0x34,
+ 0x01, 0x24, 0x08, 0x91, 0x08, 0x92, 0x0f, 0xff
+ }
+};
+
+/** @brief structure for sensor settings
+ * this structure describes the sensor settings to use for a given
+ * exposure. Data settings are identified by
+ * - sensor id
+ * - sensor hardware dpi
+ * - half ccd mode
+ */
+typedef struct {
+ int sensor_type; /**> sensor id */
+ int dpi; /**> maximum dpi for which data are valid */
+ int half_ccd; /**> half ccd mode */
+ int exposure; /**> exposure */
+ int ck1map; /**> CK1MAP */
+ int ck3map; /**> CK2MAP */
+ int ck4map; /**> CK3MAP */
+ int segcnt; /**> SEGCNT */
+ int tg0cnt; /**> TG0CNT */
+ int expdummy; /**> exposure dummy */
+ int expr; /**> initial red exposure */
+ int expg; /**> initial green exposure */
+ int expb; /**> initial blue exposure */
+ size_t *order; /**> order of sub-segments */
+ uint8_t reg18; /**> register 0x18 value */
+ uint8_t reg20; /**> register 0x20 value */
+ uint8_t reg61; /**> register 0x61 value */
+ uint8_t reg98; /**> register 0x98 value */
+} Sensor_Profile;
+
+static size_t order_01[]={0,1};
+static size_t order_0213[]={0,2,1,3};
+
+/* *INDENT-OFF* */
+
+/**
+ * database of sensor profiles
+ */
+static Sensor_Profile sensors[]={
+ {CIS_CANONLIDE210, 600, 1, 2768, 0x1e, 0x9f, 0x55, 2584, 154, 101, 388, 574, 393, NULL , 0x00, 0x0c, 0x20, 0x21},
+ {CIS_CANONLIDE110, 600, 0, 5360, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, NULL , 0x00, 0x0a, 0x20, 0x21},
+ {CIS_CANONLIDE110, 1200, 0, 10528, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, order_01 , 0x00, 0x08, 0x20, 0x22},
+ {CIS_CANONLIDE110, 2400, 0, 20864, 0x1e, 0x9f, 0x55, 5168, 163, 4679, 6839, 8401, 6859, order_0213, 0x00, 0x06, 0x20, 0x24},
+ {CIS_CANONLIDE210, 600, 1, 2768, 0x1e, 0x9f, 0x55, 2584, 154, 101, 388, 574, 393, NULL , 0x00, 0x0c, 0x20, 0x21},
+ {CIS_CANONLIDE210, 600, 0, 5360, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, NULL , 0x00, 0x0a, 0x20, 0x21},
+ {CIS_CANONLIDE210, 1200, 0, 10528, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, order_01 , 0x00, 0x08, 0x20, 0x22},
+ {CIS_CANONLIDE210, 2400, 0, 20864, 0x1e, 0x9f, 0x55, 5168, 163, 4679, 6839, 8401, 6859, order_0213, 0x00, 0x06, 0x20, 0x24},
+};
+
+
+#define MOVE_DPI 200
+#define MOVE_EXPOSURE 2304
+
+static uint32_t lide210_max[] = { 62496, 31296, 0};
+static uint32_t lide210_slow[] = { 62496, 7896, 0};
+static uint32_t lide210_fast[] = { 62496, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2051, 1432, 1372, 1323, 1280, 1246, 1216, 1188, 1163, 1142, 1121, 1101, 1084, 1068, 1051, 1036, 1020, 1007, 995, 983, 971, 959, 949, 938, 929, 917, 908, 900, 891, 882, 874, 866, 857, 849, 843, 835, 829, 821, 816, 808, 802, 795, 789, 784, 778, 773, 765, 760, 755, 749, 744, 739, 734, 731, 726, 721, 716, 711, 707, 702, 698, 693, 690, 685, 682, 677, 672, 669, 665, 662, 657, 654, 650, 647, 644, 639, 637, 632, 629, 626, 622, 619, 617, 614, 610, 607, 604, 601, 599, 595, 592, 589, 587, 584, 581, 579, 576, 572, 570, 567, 564, 562, 559, 557, 554, 552, 549, 547, 544, 542, 539, 538, 536, 533, 531, 529, 526, 524, 522, 519, 518, 516, 513, 511, 509, 506, 505, 503, 501, 498, 497, 495, 493, 491, 490, 487, 485, 483, 482, 480, 477, 476, 474, 472, 470, 469, 467, 465, 464, 462, 460, 458, 456, 455, 453, 451, 450, 448, 447, 445, 444, 442, 440, 439, 437, 436, 434, 433, 431, 430, 428, 427, 425, 423, 422, 420, 419, 417, 417, 415, 414, 413, 411, 410, 408, 407, 405, 404, 402, 401, 400, 399, 398, 396, 395, 393, 392, 391, 390, 389, 387, 386, 385, 383, 382, 381, 380, 379, 377, 376, 375, 374, 373, 371, 370, 369, 368, 367, 366, 364, 363, 363, 361, 360, 359, 358, 357, 356, 355, 353, 352, 352, 350, 349, 348, 347, 346, 345, 344, 343, 342, 341, 340, 339, 338, 335, 335, 0};
+static uint32_t lide210_ok[] = { 62496, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2051, 1961, 1901, 1852, 1809, 1775, 1745, 1717, 1692, 1671, 1650, 1630, 1613, 1597,1580,1565,1549,1536,1524,1512,1500,1488,1478,1467,1458,1446,1437,1429,1420,1411,1403,1395,1386,1378,1372,1364,1358,1350,1345,1337,1331,1324,1318,1313,1307,1302,1294,1289,1284,1278,1273,1268,1263,1260,1255,1250,1245,1240,1236,1231,1227,1222,1219,1214,1211,1206,1201,1198,1194,1191,1186,1183,1179,1176,1173,1168,1166,1161,1158,1155,1151,1148,1146,1143,1139,1136,1133,1130,1128,1124,1121,1118,1116,1113,1110,1108,1105,1101,1099,1096,1093,1091,1088,1086,1083,1081,1078,1076,1073,1071,1068,1067,1065,1062,1060,1058,1055,1053,1051,1048,1047,1045,1042,1040,1038,1035,1034,1032,1030,1027,1026,1024,1022,1020,1019,1016,1014,1012,1011,1009,1006,1005,1003,1001,999,998,996,994,993,991,989,987,985,984,982,980,979,977,976,974,973,971,969,968,966,965,963,962,960,959,957,956,954,952,951,949,948,946,946,944,943,942,940,939,937,936,934,933,931,930,929,928,927,925,924,922,921,920,919,918,916,915,914,912,911,910,909,908,906,905,904,903,902,900,899,898,897,896,895,893,892,892,890,889,888,887,886,885,884,882,881,881,879,878,877,876,875,874,873,872,871,870,869,868,867,864,864, 0};
+
+/**
+ * database of motor profiles
+ */
+
+
+/* NEXT LPERIOD=PREVIOUS*2-192 */
+static Motor_Profile motors[]={
+ {MOTOR_CANONLIDE110, 2768, 0, lide210_fast},
+ {MOTOR_CANONLIDE110, 5360, 0, lide210_ok},
+ {MOTOR_CANONLIDE110, 10528, 1, lide210_slow},
+ {MOTOR_CANONLIDE110, 20864, 2, lide210_max},
+ {MOTOR_CANONLIDE210, 2768, 0, lide210_fast},
+ {MOTOR_CANONLIDE210, 5360, 0, lide210_ok},
+ {MOTOR_CANONLIDE210, 10528, 1, lide210_slow},
+ {MOTOR_CANONLIDE210, 20864, 2, lide210_max},
+ {0, 0, 0, NULL},
+};
+/* *INDENT-ON* */
+
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status gl124_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter, unsigned int flags);
+
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status gl124_start_action (Genesys_Device * dev);
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor);
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop);
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl124_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl124_init (Genesys_Device * dev);
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl124_send_shading_data (Genesys_Device * dev, uint8_t * data, int size);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl124_feed (Genesys_Device * dev, unsigned int steps);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl124_stop_action (Genesys_Device * dev);
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl646.c b/backend/genesys_gl646.c
new file mode 100644
index 0000000..c7fa175
--- /dev/null
+++ b/backend/genesys_gl646.c
@@ -0,0 +1,5811 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2007 Luke <iceyfor@gmail.com>
+ Copyright (C) 2011 Alexey Osipov <simba@lerlan.ru> for HP2400 description
+ and tuning
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_gl646
+
+#include "genesys_gl646.h"
+
+/**
+ * returns the value hold by a 3 word register
+ * @param regs register set from which reading the value
+ * @param regnum number of the register to read
+ * @return 24 bit value of the register
+ */
+static uint32_t
+gl646_get_triple_reg (Genesys_Register_Set * regs, int regnum)
+{
+ Genesys_Register_Set *r = NULL;
+ uint32_t ret = 0;
+
+ r = sanei_genesys_get_address (regs, regnum);
+ ret = r->value;
+ r = sanei_genesys_get_address (regs, regnum + 1);
+ ret = (ret << 8) + r->value;
+ r = sanei_genesys_get_address (regs, regnum + 2);
+ ret = (ret << 8) + r->value;
+
+ return ret;
+}
+
+/**
+ * returns the value hold by a 2 word register
+ * @param regs register set from which reading the value
+ * @param regnum number of the register to read
+ * @return 16 bit value of the register
+ */
+static uint32_t
+gl646_get_double_reg (Genesys_Register_Set * regs, int regnum)
+{
+ Genesys_Register_Set *r = NULL;
+ uint32_t ret = 0;
+
+ r = sanei_genesys_get_address (regs, regnum);
+ ret = r->value;
+ r = sanei_genesys_get_address (regs, regnum + 1);
+ ret = (ret << 8) + r->value;
+
+ return ret;
+}
+
+/* Write to many registers */
+static SANE_Status
+gl646_bulk_write_register (Genesys_Device * dev,
+ Genesys_Register_Set * reg, size_t elems)
+{
+ SANE_Status status;
+ uint8_t outdata[8];
+ uint8_t buffer[GENESYS_MAX_REGS * 2];
+ size_t size;
+ unsigned int i;
+
+ /* handle differently sized register sets, reg[0x00] may be the last one */
+ i = 0;
+ while ((i < elems) && (reg[i].address != 0))
+ i++;
+ elems = i;
+ size = i * 2;
+
+ DBG (DBG_io, "gl646_bulk_write_register (elems= %lu, size = %lu)\n",
+ (u_long) elems, (u_long) size);
+
+
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_REGISTER;
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata), outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_write_register: failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* copy registers and values in data buffer */
+ for (i = 0; i < size; i += 2)
+ {
+ buffer[i] = reg[i / 2].address;
+ buffer[i + 1] = reg[i / 2].value;
+ }
+
+ status = sanei_usb_write_bulk (dev->dn, buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_write_register: failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ for (i = 0; i < size; i += 2)
+ {
+ DBG (DBG_io2, "reg[0x%02x] = 0x%02x\n", buffer[i], buffer[i + 1]);
+ }
+ /* when full size, decode register content */
+ if (elems > 60)
+ {
+ DBG (DBG_io2, "DPISET =%d\n",
+ gl646_get_double_reg (reg, REG_DPISET));
+ DBG (DBG_io2, "DUMMY =%d\n",
+ sanei_genesys_get_address (reg, REG_DUMMY)->value);
+ DBG (DBG_io2, "STRPIXEL =%d\n",
+ gl646_get_double_reg (reg, REG_STRPIXEL));
+ DBG (DBG_io2, "ENDPIXEL =%d\n",
+ gl646_get_double_reg (reg, REG_ENDPIXEL));
+ DBG (DBG_io2, "LINCNT =%d\n",
+ gl646_get_triple_reg (reg, REG_LINCNT));
+ DBG (DBG_io2, "MAXWD =%d\n",
+ gl646_get_triple_reg (reg, REG_MAXWD));
+ DBG (DBG_io2, "LPERIOD =%d\n",
+ gl646_get_double_reg (reg, REG_LPERIOD));
+ DBG (DBG_io2, "FEEDL =%d\n",
+ gl646_get_triple_reg (reg, REG_FEEDL));
+ }
+ }
+
+ DBG (DBG_io, "gl646_bulk_write_register: wrote %lu bytes, %lu registers\n",
+ (u_long) size, (u_long) elems);
+ return status;
+}
+
+/* Write bulk data (e.g. shading, gamma) */
+static SANE_Status
+gl646_bulk_write_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBG (DBG_io, "gl646_bulk_write_data writing %lu bytes\n", (u_long) len);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_write_data failed while setting register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if (len > BULKOUT_MAXSIZE)
+ size = BULKOUT_MAXSIZE;
+ else
+ size = len;
+
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_RAM;
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_write_data failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_usb_write_bulk (dev->dn, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_write_data failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "gl646_bulk_write_data wrote %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ DBG (DBG_io, "gl646_bulk_write_data: end\n");
+
+ return status;
+}
+
+/**
+ * reads value from gpio endpoint
+ */
+static SANE_Status
+gl646_gpio_read (SANE_Int dn, uint8_t * value)
+{
+ return sanei_usb_control_msg (dn, REQUEST_TYPE_IN,
+ REQUEST_REGISTER, GPIO_READ, INDEX, 1, value);
+}
+
+/**
+ * writes the given value to gpio endpoint
+ */
+static SANE_Status
+gl646_gpio_write (SANE_Int dn, uint8_t value)
+{
+ DBG (DBG_proc, "gl646_gpio_write(0x%02x)\n", value);
+ return sanei_usb_control_msg (dn, REQUEST_TYPE_OUT,
+ REQUEST_REGISTER, GPIO_WRITE,
+ INDEX, 1, &value);
+}
+
+/**
+ * writes the given value to gpio output enable endpoint
+ */
+static SANE_Status
+gl646_gpio_output_enable (SANE_Int dn, uint8_t value)
+{
+ DBG (DBG_proc, "gl646_gpio_output_enable(0x%02x)\n", value);
+ return sanei_usb_control_msg (dn, REQUEST_TYPE_OUT,
+ REQUEST_REGISTER, GPIO_OUTPUT_ENABLE,
+ INDEX, 1, &value);
+}
+
+/* Read bulk data (e.g. scanned data) */
+static SANE_Status
+gl646_bulk_read_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBG (DBG_io, "gl646_bulk_read_data: requesting %lu bytes\n", (u_long) len);
+
+ /* write requested size */
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_read_data failed while setting register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ outdata[0] = BULK_IN;
+ outdata[1] = BULK_RAM;
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ outdata[4] = (len & 0xff);
+ outdata[5] = ((len >> 8) & 0xff);
+ outdata[6] = ((len >> 16) & 0xff);
+ outdata[7] = ((len >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata), outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_read_data failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if (len > GL646_BULKIN_MAXSIZE)
+ size = GL646_BULKIN_MAXSIZE;
+ else
+ size = len;
+
+ DBG (DBG_io2,
+ "gl646_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) size);
+ status = sanei_usb_read_bulk (dev->dn, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "gl646_bulk_read_data read %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ gl646_detect_document_end (dev);
+ }
+
+ DBG (DBG_io, "gl646_bulk_read_data: end\n");
+
+ return status;
+}
+
+#if 0
+static SANE_Status
+read_triple_reg (Genesys_Device * dev, int index, unsigned int *words)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBG (DBG_proc, "read_triple_reg\n");
+
+ RIE (sanei_genesys_read_register (dev, index + 2, &value));
+ *words = value;
+ RIE (sanei_genesys_read_register (dev, index + 1, &value));
+ *words += (value * 256);
+ RIE (sanei_genesys_read_register (dev, index, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *words += ((value & 0x03) * 256 * 256);
+ else
+ *words += ((value & 0x0f) * 256 * 256);
+
+ DBG (DBG_proc, "read_triple_reg: value=%d\n", *words);
+ return status;
+}
+#endif
+
+
+static SANE_Bool
+gl646_get_fast_feed_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x02);
+ if (r && (r->value & REG02_FASTFED))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl646_get_filter_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x04);
+ if (r && (r->value & REG04_FILTER))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl646_get_lineart_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x04);
+ if (r && (r->value & REG04_LINEART))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl646_get_bitset_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x04);
+ if (r && (r->value & REG04_BITSET))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl646_get_gain4_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x06);
+ if (r && (r->value & REG06_GAIN4))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl646_test_buffer_empty_bit (SANE_Byte val)
+{
+ if (val & REG41_BUFEMPTY)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl646_test_motor_flag_bit (SANE_Byte val)
+{
+ if (val & REG41_MOTMFLG)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static void
+gl646_set_triple_reg (Genesys_Register_Set * regs, int regnum, uint32_t value)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, regnum);
+ r->value = LOBYTE (HIWORD (value));
+ r = sanei_genesys_get_address (regs, regnum + 1);
+ r->value = HIBYTE (LOWORD (value));
+ r = sanei_genesys_get_address (regs, regnum + 2);
+ r->value = LOBYTE (LOWORD (value));
+}
+
+static void
+gl646_set_double_reg (Genesys_Register_Set * regs, int regnum, uint16_t value)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, regnum);
+ r->value = HIBYTE (LOWORD (value));
+ r = sanei_genesys_get_address (regs, regnum + 1);
+ r->value = LOBYTE (LOWORD (value));
+}
+
+/**
+ * decodes and prints content of status (0x41) register
+ * @param val value read from reg41
+ */
+static void
+print_status (uint8_t val)
+{
+ char msg[80];
+
+ sprintf (msg, "%s%s%s%s%s%s%s%s",
+ val & REG41_PWRBIT ? "PWRBIT " : "",
+ val & REG41_BUFEMPTY ? "BUFEMPTY " : "",
+ val & REG41_FEEDFSH ? "FEEDFSH " : "",
+ val & REG41_SCANFSH ? "SCANFSH " : "",
+ val & REG41_HOMESNR ? "HOMESNR " : "",
+ val & REG41_LAMPSTS ? "LAMPSTS " : "",
+ val & REG41_FEBUSY ? "FEBUSY " : "",
+ val & REG41_MOTMFLG ? "MOTMFLG" : "");
+ DBG (DBG_info, "status=%s\n", msg);
+}
+
+/**
+ * start scanner's motor
+ * @param dev scanner's device
+ */
+static SANE_Status
+gl646_start_motor (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x01);
+}
+
+
+/**
+ * stop scanner's motor
+ * @param dev scanner's device
+ */
+static SANE_Status
+gl646_stop_motor (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x00);
+}
+
+
+/**
+ * find the lowest resolution for the sensor in the given mode.
+ * @param sensor id of the sensor
+ * @param color true is color mode
+ * @return the closest resolution for the sensor and mode
+ */
+static int
+get_lowest_resolution (int sensor, SANE_Bool color)
+{
+ int i, nb;
+ int dpi;
+
+ i = 0;
+ dpi = 9600;
+ nb = sizeof (sensor_master) / sizeof (Sensor_Master);
+ while (i < nb)
+ {
+ /* computes distance and keep mode if it is closer than previous */
+ if (sensor == sensor_master[i].sensor
+ && sensor_master[i].color == color)
+ {
+ if (sensor_master[i].dpi < dpi)
+ {
+ dpi = sensor_master[i].dpi;
+ }
+ }
+ i++;
+ }
+ DBG (DBG_info, "get_lowest_resolution: %d\n", dpi);
+ return dpi;
+}
+
+/**
+ * find the closest match in mode tables for the given resolution and scan mode.
+ * @param sensor id of the sensor
+ * @param required required resolution
+ * @param color true is color mode
+ * @return the closest resolution for the sensor and mode
+ */
+static int
+get_closest_resolution (int sensor, int required, SANE_Bool color)
+{
+ int i, nb;
+ int dist, dpi;
+
+ i = 0;
+ dpi = 0;
+ dist = 9600;
+ nb = sizeof (sensor_master) / sizeof (Sensor_Master);
+ while (i < nb)
+ {
+ /* exit on perfect match */
+ if (sensor == sensor_master[i].sensor
+ && sensor_master[i].dpi == required
+ && sensor_master[i].color == color)
+ {
+ DBG (DBG_info, "get_closest_resolution: match found for %d\n",
+ required);
+ return required;
+ }
+ /* computes distance and keep mode if it is closer than previous */
+ if (sensor == sensor_master[i].sensor
+ && sensor_master[i].color == color)
+ {
+ if (abs (sensor_master[i].dpi - required) < dist)
+ {
+ dpi = sensor_master[i].dpi;
+ dist = abs (sensor_master[i].dpi - required);
+ }
+ }
+ i++;
+ }
+ DBG (DBG_info, "get_closest_resolution: closest match for %d is %d\n",
+ required, dpi);
+ return dpi;
+}
+
+/**
+ * Computes if sensor will be set up for half ccd pixels for the given
+ * scan mode.
+ * @param sensor id of the sensor
+ * @param required required resolution
+ * @param color true is color mode
+ * @return SANE_TRUE if half ccd is used
+ */
+static SANE_Bool
+is_half_ccd (int sensor, int required, SANE_Bool color)
+{
+ int i, nb;
+
+ i = 0;
+ nb = sizeof (sensor_master) / sizeof (Sensor_Master);
+ while (i < nb)
+ {
+ /* exit on perfect match */
+ if (sensor == sensor_master[i].sensor
+ && sensor_master[i].dpi == required
+ && sensor_master[i].color == color)
+ {
+ DBG (DBG_io, "is_half_ccd: match found for %d (half_ccd=%d)\n",
+ required, sensor_master[i].half_ccd);
+ return sensor_master[i].half_ccd;
+ }
+ i++;
+ }
+ DBG (DBG_info, "is_half_ccd: failed to find match for %d dpi\n", required);
+ return SANE_FALSE;
+}
+
+/**
+ * Returns the cksel values used by the required scan mode.
+ * @param sensor id of the sensor
+ * @param required required resolution
+ * @param color true is color mode
+ * @return cksel value for mode
+ */
+static int
+get_cksel (int sensor, int required, SANE_Bool color)
+{
+ int i, nb;
+
+ i = 0;
+ nb = sizeof (sensor_master) / sizeof (Sensor_Master);
+ while (i < nb)
+ {
+ /* exit on perfect match */
+ if (sensor == sensor_master[i].sensor
+ && sensor_master[i].dpi == required
+ && sensor_master[i].color == color)
+ {
+ DBG (DBG_io, "get_cksel: match found for %d (cksel=%d)\n",
+ required, sensor_master[i].cksel);
+ return sensor_master[i].cksel;
+ }
+ i++;
+ }
+ DBG (DBG_error, "get_cksel: failed to find match for %d dpi\n", required);
+ /* fail safe fallback */
+ return 1;
+}
+
+/**
+ * Setup register and motor tables for a scan at the
+ * given resolution and color mode. TODO try to not use any filed from
+ * the device.
+ * @param dev pointer to a struct describing the device
+ * @param regs register set to fill
+ * @param scan_settings scan's settings
+ * @param slope_table1 first motor table to fill
+ * @param slope_table2 second motor table to fill
+ * @param resolution dpi of the scan
+ * @param move distance to move (at scan's dpi) before scan
+ * @param linecnt number of lines to scan at scan's dpi
+ * @param startx start of scan area on CCD at CCD's optical resolution
+ * @param endx end of scan area on CCD at CCD's optical resolution
+ * @param color SANE_TRUE is color scan
+ * @param depth 1, 8 or 16 bits data sample
+ * @return SANE_STATUS_GOOD if registers could be set, SANE_STATUS_INVAL if
+ * conditions can't be met.
+ * @note No harcoded SENSOR or MOTOR 'names' should be present and
+ * registers are set from settings tables and flags related
+ * to the hardware capabilities.
+ * */
+static SANE_Status
+gl646_setup_registers (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ Genesys_Settings scan_settings,
+ uint16_t * slope_table1,
+ uint16_t * slope_table2,
+ SANE_Int resolution,
+ uint32_t move,
+ uint32_t linecnt,
+ uint16_t startx,
+ uint16_t endx, SANE_Bool color,
+ SANE_Int depth)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i, nb;
+ Sensor_Master *sensor = NULL;
+ Motor_Master *motor = NULL;
+ Sensor_Settings *settings = NULL;
+ Genesys_Register_Set *r;
+ unsigned int used1, used2, vfinal;
+ unsigned int bpp; /**> bytes per pixel */
+ uint32_t z1, z2;
+ uint16_t ex, sx;
+ int channels = 1, stagger, words_per_line, max_shift;
+ size_t requested_buffer_size;
+ size_t read_buffer_size;
+ SANE_Bool half_ccd = SANE_FALSE;
+ SANE_Int xresolution;
+ int feedl;
+
+ DBG (DBG_proc, "gl646_setup_registers: start\n");
+ DBG (DBG_info, "gl646_setup_registers: startx=%d, endx=%d, linecnt=%d\n",
+ startx, endx, linecnt);
+
+ /* x resolution is capped by sensor's capability */
+ if (resolution > dev->sensor.optical_res)
+ {
+ xresolution = dev->sensor.optical_res;
+ }
+ else
+ {
+ xresolution = resolution;
+ }
+
+ /* for the given resolution, search for master
+ * sensor mode setting */
+ i = 0;
+ nb = sizeof (sensor_master) / sizeof (Sensor_Master);
+ while (i < nb)
+ {
+ if (dev->model->ccd_type == sensor_master[i].sensor
+ && sensor_master[i].dpi == xresolution
+ && sensor_master[i].color == color)
+ {
+ sensor = &sensor_master[i];
+ }
+ i++;
+ }
+ if (sensor == NULL)
+ {
+ DBG (DBG_error,
+ "gl646_setup_registers: unable to find settings for sensor %d at %d dpi color=%d\n",
+ dev->model->ccd_type, xresolution, color);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* for the given resolution, search for master
+ * motor mode setting */
+ i = 0;
+ nb = sizeof (motor_master) / sizeof (Motor_Master);
+ while (i < nb)
+ {
+ if (dev->model->motor_type == motor_master[i].motor
+ && motor_master[i].dpi == resolution
+ && motor_master[i].color == color)
+ {
+ motor = &motor_master[i];
+ }
+ i++;
+ }
+ if (motor == NULL)
+ {
+ DBG (DBG_error,
+ "gl646_setup_registers: unable to find settings for motor %d at %d dpi, color=%d\n",
+ dev->model->motor_type, resolution, color);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now we can search for the specific sensor settings */
+ i = 0;
+ nb = sizeof (sensor_settings) / sizeof (Sensor_Settings);
+ while (i < nb)
+ {
+ if (sensor->sensor == sensor_settings[i].sensor
+ && sensor->cksel == sensor_settings[i].cksel)
+ {
+ settings = &sensor_settings[i];
+ }
+ i++;
+ }
+ if (settings == NULL)
+ {
+ DBG (DBG_error,
+ "gl646_setup_registers: unable to find settings for sensor %d with '%d' ccd timing\n",
+ sensor->sensor, sensor->cksel);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* half_ccd if manual clock programming or dpi is half dpiset */
+ half_ccd = sensor->half_ccd;
+
+ /* now apply values from settings to registers */
+ if (sensor->regs_0x10_0x15 != NULL)
+ {
+ for (i = 0; i < 6; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ r->value = sensor->regs_0x10_0x15[i];
+ }
+ }
+ else
+ {
+ for (i = 0; i < 6; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ r->value = 0;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x08 + i);
+ if (half_ccd == SANE_TRUE)
+ r->value = settings->manual_0x08_0x0b[i];
+ else
+ r->value = settings->regs_0x08_0x0b[i];
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x16 + i);
+ r->value = settings->regs_0x16_0x1d[i];
+ }
+
+ for (i = 0; i < 13; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ r->value = settings->regs_0x52_0x5e[i];
+ }
+ if (half_ccd == SANE_TRUE)
+ {
+ for (i = 0; i < 7; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ r->value = settings->manual_0x52_0x58[i];
+ }
+ }
+
+ /* now generate slope tables : we are not using generate_slope_table3 yet */
+ sanei_genesys_generate_slope_table (slope_table1, motor->steps1,
+ motor->steps1 + 1, motor->vend1,
+ motor->vstart1, motor->vend1,
+ motor->steps1, motor->g1, &used1,
+ &vfinal);
+ sanei_genesys_generate_slope_table (slope_table2, motor->steps2,
+ motor->steps2 + 1, motor->vend2,
+ motor->vstart2, motor->vend2,
+ motor->steps2, motor->g2, &used2,
+ &vfinal);
+
+ if (color == SANE_TRUE)
+ channels = 3;
+ else
+ channels = 1;
+
+ /* R01 */
+ /* now setup other registers for final scan (ie with shading enabled) */
+ /* watch dog + shading + scan enable */
+ regs[reg_0x01].value |= REG01_DOGENB | REG01_DVDSET | REG01_SCAN;
+ if (dev->model->is_cis == SANE_TRUE)
+ regs[reg_0x01].value |= REG01_CISSET;
+ else
+ regs[reg_0x01].value &= ~REG01_CISSET;
+
+ /* if device has no calibration, don't enable shading correction */
+ if (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)
+ {
+ regs[reg_0x01].value &= ~REG01_DVDSET;
+ }
+
+ regs[reg_0x01].value &= ~REG01_FASTMOD;
+ if (motor->fastmod)
+ regs[reg_0x01].value |= REG01_FASTMOD;
+
+ /* R02 */
+ /* allow moving when buffer full by default */
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ dev->reg[reg_0x02].value &= ~REG02_ACDCDIS;
+ else
+ dev->reg[reg_0x02].value |= REG02_ACDCDIS;
+
+ /* setup motor power and direction */
+ regs[reg_0x02].value |= REG02_MTRPWR;
+ regs[reg_0x02].value &= ~REG02_MTRREV;
+
+ /* fastfed enabled (2 motor slope tables) */
+ if (motor->fastfed)
+ regs[reg_0x02].value |= REG02_FASTFED;
+ else
+ regs[reg_0x02].value &= ~REG02_FASTFED;
+
+ /* step type */
+ regs[reg_0x02].value &= ~REG02_STEPSEL;
+ switch (motor->steptype)
+ {
+ case FULL_STEP:
+ break;
+ case HALF_STEP:
+ regs[reg_0x02].value |= 1;
+ break;
+ case QUATER_STEP:
+ regs[reg_0x02].value |= 2;
+ break;
+ default:
+ regs[reg_0x02].value |= 3;
+ break;
+ }
+
+ /* if sheetfed, no AGOHOME */
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ regs[reg_0x02].value &= ~REG02_AGOHOME;
+ }
+ else
+ {
+ regs[reg_0x02].value |= REG02_AGOHOME;
+ }
+
+ /* R03 */
+ regs[reg_0x03].value &= ~REG03_AVEENB;
+ /* regs[reg_0x03].value |= REG03_AVEENB; */
+ regs[reg_0x03].value &= ~REG03_LAMPDOG;
+
+ /* select XPA */
+ regs[reg_0x03].value &= ~REG03_XPASEL;
+ if (scan_settings.scan_method == SCAN_METHOD_TRANSPARENCY)
+ {
+ regs[reg_0x03].value |= REG03_XPASEL;
+ }
+
+ /* R04 */
+ /* monochrome / color scan */
+ switch (depth)
+ {
+ case 1:
+ regs[reg_0x04].value &= ~REG04_BITSET;
+ regs[reg_0x04].value |= REG04_LINEART;
+ break;
+ case 8:
+ regs[reg_0x04].value &= ~(REG04_LINEART | REG04_BITSET);
+ break;
+ case 16:
+ regs[reg_0x04].value &= ~REG04_LINEART;
+ regs[reg_0x04].value |= REG04_BITSET;
+ break;
+ }
+
+ /* R05 */
+ regs[reg_0x05].value &= ~REG05_DPIHW;
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ regs[reg_0x05].value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ regs[reg_0x05].value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ regs[reg_0x05].value |= REG05_DPIHW_2400;
+ break;
+ default:
+ regs[reg_0x05].value |= REG05_DPIHW;
+ }
+
+ /* gamma enable for scans */
+ if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
+ regs[reg_0x05].value |= REG05_GMM14BIT;
+ if (depth < 16)
+ regs[reg_0x05].value |= REG05_GMMENB;
+ else
+ regs[reg_0x05].value &= ~REG05_GMMENB;
+
+ /* true CIS gray if needed */
+ if (dev->model->is_cis == SANE_TRUE && color == SANE_FALSE
+ && dev->settings.true_gray)
+ {
+ regs[reg_0x05].value |= REG05_LEDADD;
+ }
+ else
+ {
+ regs[reg_0x05].value &= ~REG05_LEDADD;
+ }
+
+ /* cktoggle, ckdelay and cksel at once, cktdelay=2 => half_ccd for md5345 */
+ regs[reg_0x18].value = sensor->r18;
+
+ /* manual CCD/2 clock programming => half_ccd for hp2300 */
+ regs[reg_0x1d].value = sensor->r1d;
+
+ /* HP2400 1200dpi mode tuning */
+
+ if (dev->model->ccd_type == CCD_HP2400)
+ {
+ /* reset count of dummy lines to zero */
+ regs[reg_0x1e].value &= ~REG1E_LINESEL;
+ if (scan_settings.xres >= 1200)
+ {
+ /* there must be one dummy line */
+ regs[reg_0x1e].value |= 1 & REG1E_LINESEL;
+
+ /* GPO12 need to be set to zero */
+ regs[reg_0x66].value &= ~0x20;
+ }
+ else
+ {
+ /* set GPO12 back to one */
+ regs[reg_0x66].value |= 0x20;
+ }
+ }
+
+ /* motor steps used */
+ regs[reg_0x21].value = motor->steps1;
+ regs[reg_0x22].value = motor->fwdbwd;
+ regs[reg_0x23].value = motor->fwdbwd;
+ regs[reg_0x24].value = motor->steps1;
+
+ /* scanned area height must be enlarged by max color shift needed */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,scan_settings.yres,0);
+
+ /* we adjust linecnt according to real motor dpi */
+ linecnt = (linecnt * motor->ydpi) / scan_settings.yres + max_shift;
+
+ /* at QUATER_STEP lines are 'staggered' and need correction */
+ stagger = 0;
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ {
+ /* for HP3670, stagger happens only at >=1200 dpi */
+ if ((dev->model->motor_type != MOTOR_HP3670
+ && dev->model->motor_type != MOTOR_HP2400)
+ || scan_settings.yres >= dev->sensor.optical_res)
+ {
+ stagger = (4 * scan_settings.yres) / dev->motor.base_ydpi;
+ }
+ }
+ linecnt += stagger;
+
+ DBG (DBG_info, "gl646_setup_registers : max_shift=%d, stagger=%d lines\n",
+ max_shift, stagger);
+
+ /* CIS scanners read one line per color channel
+ * since gray mode use 'add' we also read 3 channels even not in
+ * color mode */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ gl646_set_triple_reg (regs, REG_LINCNT, linecnt * 3);
+ linecnt *= channels;
+ }
+ else
+ {
+ gl646_set_triple_reg (regs, REG_LINCNT, linecnt);
+ }
+
+ /* scanner's x coordinates are expressed in physical DPI but they must be divided by cksel */
+ sx = startx / sensor->cksel;
+ ex = endx / sensor->cksel;
+ if (half_ccd == SANE_TRUE)
+ {
+ sx /= 2;
+ ex /= 2;
+ }
+ gl646_set_double_reg (regs, REG_STRPIXEL, sx);
+ gl646_set_double_reg (regs, REG_ENDPIXEL, ex);
+ DBG (DBG_info, "gl646_setup_registers: startx=%d, endx=%d, half_ccd=%d\n",
+ sx, ex, half_ccd);
+
+ /* words_per_line must be computed according to the scan's resolution */
+ /* in fact, words_per_line _gives_ the actual scan resolution */
+ words_per_line = (((endx - startx) * sensor->xdpi) / dev->sensor.optical_res);
+ bpp=depth/8;
+ if (depth == 1)
+ {
+ words_per_line = (words_per_line+7)/8 ;
+ bpp=1;
+ }
+ else
+ {
+ words_per_line *= bpp;
+ }
+ dev->bpl = words_per_line;
+ words_per_line *= channels;
+ dev->wpl = words_per_line;
+
+ DBG (DBG_info, "gl646_setup_registers: wpl=%d\n", words_per_line);
+ gl646_set_triple_reg (regs, REG_MAXWD, words_per_line);
+
+ gl646_set_double_reg (regs, REG_DPISET, sensor->dpiset);
+ gl646_set_double_reg (regs, REG_LPERIOD, sensor->exposure);
+
+ /* move distance must be adjusted to take into account the extra lines
+ * read to reorder data */
+ feedl = move;
+ if (stagger + max_shift > 0 && feedl != 0)
+ {
+ if (feedl >
+ ((max_shift + stagger) * dev->motor.optical_ydpi) / motor->ydpi)
+ feedl =
+ feedl -
+ ((max_shift + stagger) * dev->motor.optical_ydpi) / motor->ydpi;
+ }
+
+ /* we assume all scans are done with 2 tables */
+ /*
+ feedl = feed_steps - fast_slope_steps*2 -
+ (slow_slope_steps >> scan_step_type); */
+ /* but head has moved due to shading calibration => dev->scanhead_position_in_steps */
+ if (feedl > 0)
+ {
+ /* take into account the distance moved during calibration */
+ /* feedl -= dev->scanhead_position_in_steps; */
+ DBG (DBG_info, "gl646_setup_registers: initial move=%d\n", feedl);
+ DBG (DBG_info, "gl646_setup_registers: scanhead_position_in_steps=%d\n",
+ dev->scanhead_position_in_steps);
+
+ /* TODO clean up this when I'll fully understand.
+ * for now, special casing each motor */
+ switch (dev->model->motor_type)
+ {
+ case MOTOR_5345:
+ switch (motor->ydpi)
+ {
+ case 200:
+ feedl -= 70;
+ break;
+ case 300:
+ feedl -= 70;
+ break;
+ case 400:
+ feedl += 130;
+ break;
+ case 600:
+ feedl += 160;
+ break;
+ case 1200:
+ feedl += 160;
+ break;
+ case 2400:
+ feedl += 180;
+ break;
+ default:
+ break;
+ }
+ break;
+ case MOTOR_HP2300:
+ switch (motor->ydpi)
+ {
+ case 75:
+ feedl -= 180;
+ break;
+ case 150:
+ feedl += 0;
+ break;
+ case 300:
+ feedl += 30;
+ break;
+ case 600:
+ feedl += 35;
+ break;
+ case 1200:
+ feedl += 45;
+ break;
+ default:
+ break;
+ }
+ break;
+ case MOTOR_HP2400:
+ switch (motor->ydpi)
+ {
+ case 150:
+ feedl += 150;
+ break;
+ case 300:
+ feedl += 220;
+ break;
+ case 600:
+ feedl += 260;
+ break;
+ case 1200:
+ feedl += 280; /* 300 */
+ break;
+ case 50:
+ feedl += 0;
+ break;
+ case 100:
+ feedl += 100;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* theorical value */
+ default:
+ if (motor->fastfed)
+ {
+ feedl =
+ feedl - 2 * motor->steps2 -
+ (motor->steps1 >> motor->steptype);
+ }
+ else
+ {
+ feedl = feedl - (motor->steps1 >> motor->steptype);
+ }
+ break;
+ }
+ /* security */
+ if (feedl < 0)
+ feedl = 0;
+ }
+
+ DBG (DBG_info, "gl646_setup_registers: final move=%d\n", feedl);
+ gl646_set_triple_reg (regs, REG_FEEDL, feedl);
+
+ regs[reg_0x65].value = motor->mtrpwm;
+
+ sanei_genesys_calculate_zmode2 (regs[reg_0x02].value & REG02_FASTFED,
+ sensor->exposure,
+ slope_table1,
+ motor->steps1,
+ move, motor->fwdbwd, &z1, &z2);
+
+ /* no z1/z2 for sheetfed scanners */
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ z1 = 0;
+ z2 = 0;
+ }
+ gl646_set_double_reg (regs, REG_Z1MOD, z1);
+ gl646_set_double_reg (regs, REG_Z2MOD, z2);
+ regs[reg_0x6b].value = motor->steps2;
+ regs[reg_0x6c].value =
+ (regs[reg_0x6c].value & REG6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16)
+ & 0x07);
+
+ RIE (write_control (dev, xresolution));
+
+ /* setup analog frontend */
+ RIE (gl646_set_fe (dev, AFE_SET, xresolution));
+
+ /* now we're done with registers setup values used by data transfer */
+ /* we setup values needed for the data transfer */
+
+ /* we must use a round number of words_per_line */
+ requested_buffer_size = 8 * words_per_line;
+ read_buffer_size =
+ 2 * requested_buffer_size +
+ ((max_shift + stagger) * scan_settings.pixels * channels * depth) / 8;
+
+ RIE (sanei_genesys_buffer_free (&(dev->read_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->read_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->lines_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->lines_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->shrink_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->shrink_buffer),
+ requested_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->out_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->out_buffer),
+ 8 * scan_settings.pixels * channels * bpp));
+
+ /* scan bytes to read */
+ dev->read_bytes_left = words_per_line * linecnt;
+
+ DBG (DBG_info,
+ "gl646_setup_registers: physical bytes to read = %lu\n",
+ (u_long) dev->read_bytes_left);
+ dev->read_active = SANE_TRUE;
+
+ dev->current_setup.pixels =
+ ((endx - startx) * sensor->xdpi) / dev->sensor.optical_res;
+ dev->current_setup.lines = linecnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = sensor->exposure;
+ dev->current_setup.xres = sensor->xdpi;
+ dev->current_setup.yres = motor->ydpi;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ /* total_bytes_to_read is the number of byte to send to frontend
+ * total_bytes_read is the number of bytes sent to frontend
+ * read_bytes_left is the number of bytes to read from the scanner
+ */
+ dev->total_bytes_read = 0;
+ if (depth == 1)
+ dev->total_bytes_to_read =
+ ((scan_settings.pixels * scan_settings.lines) / 8 +
+ (((scan_settings.pixels * scan_settings.lines) % 8) ? 1 : 0)) *
+ channels;
+ else
+ dev->total_bytes_to_read =
+ scan_settings.pixels * scan_settings.lines * channels * bpp;
+
+ DBG (DBG_proc, "gl646_setup_registers: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/** copy sensor specific settings */
+/* *dev : device infos
+ *regs : regiters to be set
+ extended : do extended set up
+ half_ccd: set up for half ccd resolution
+ all registers 08-0B, 10-1D, 52-5E are set up. They shouldn't
+ appear anywhere else but in register init
+*/
+static void
+gl646_setup_sensor (Genesys_Device * dev, Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r;
+ int i;
+
+ DBG (DBG_proc, "gl646_setup_sensor: start\n");
+ for (i = 0; i < 4; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x08 + i);
+ r->value = dev->sensor.regs_0x08_0x0b[i];
+ }
+
+ for (i = 0; i < 14; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+
+ for (i = 0; i < 13; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ r->value = dev->sensor.regs_0x52_0x5e[i];
+ }
+ DBG (DBG_proc, "gl646_setup_sensor: end\n");
+
+}
+
+/** Test if the ASIC works
+ */
+static SANE_Status
+gl646_asic_test (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val;
+ uint8_t *data;
+ uint8_t *verify_data;
+ size_t size, verify_size;
+ unsigned int i;
+
+ DBG (DBG_proc, "gl646_asic_test: start\n");
+
+ /* set and read exposure time, compare if it's the same */
+ status = sanei_genesys_write_register (dev, 0x38, 0xde);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to write register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_write_register (dev, 0x39, 0xad);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to write register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_read_register (dev, 0x4e, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val != 0xde) /* value of register 0x38 */
+ {
+ DBG (DBG_error, "gl646_asic_test: register contains invalid value\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status = sanei_genesys_read_register (dev, 0x4f, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val != 0xad) /* value of register 0x39 */
+ {
+ DBG (DBG_error, "gl646_asic_test: register contains invalid value\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* ram test: */
+ size = 0x40000;
+ verify_size = size + 0x80;
+ /* todo: looks like the read size must be a multiple of 128?
+ otherwise the read doesn't succeed the second time after the scanner has
+ been plugged in. Very strange. */
+
+ data = (uint8_t *) malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error, "gl646_asic_test: could not allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ verify_data = (uint8_t *) malloc (verify_size);
+ if (!verify_data)
+ {
+ free (data);
+ DBG (DBG_error, "gl646_asic_test: could not allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < (size - 1); i += 2)
+ {
+ data[i] = i / 512;
+ data[i + 1] = (i / 2) % 256;
+ }
+
+ status = sanei_genesys_set_buffer_address (dev, 0x0000);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+ status = gl646_bulk_write_data (dev, 0x3c, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to bulk write data: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+ status = sanei_genesys_set_buffer_address (dev, 0x0000);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+ status =
+ gl646_bulk_read_data (dev, 0x45, (uint8_t *) verify_data, verify_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_asic_test: failed to bulk read data: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+ /* i + 2 is needed as the changed address goes into effect only after one
+ data word is sent. */
+ for (i = 0; i < size; i++)
+ {
+ if (verify_data[i + 2] != data[i])
+ {
+ DBG (DBG_error, "gl646_asic_test: data verification error\n");
+ free (data);
+ free (verify_data);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ free (data);
+ free (verify_data);
+
+ DBG (DBG_info, "gl646_asic_test: end\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* returns the max register bulk size */
+static int
+gl646_bulk_full_size (void)
+{
+ return GENESYS_GL646_MAX_REGS;
+}
+
+/**
+ * Set all registers to default values after init
+ * @param dev scannerr's device to set
+ */
+static void
+gl646_init_regs (Genesys_Device * dev)
+{
+ int nr, addr;
+
+ DBG (DBG_proc, "gl646_init_regs\n");
+
+ nr = 0;
+ memset (dev->reg, 0, GENESYS_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ for (addr = 1; addr <= 0x0b; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x10; addr <= 0x29; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x2c; addr <= 0x39; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x3d; addr <= 0x3f; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x52; addr <= 0x5e; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x60; addr <= 0x6d; addr++)
+ dev->reg[nr++].address = addr;
+
+ dev->reg[reg_0x01].value = 0x20 /*0x22 */ ; /* enable shading, CCD, color, 1M */
+ dev->reg[reg_0x02].value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
+ if (dev->model->motor_type == MOTOR_5345)
+ dev->reg[reg_0x02].value |= 0x01; /* half-step */
+ switch (dev->model->motor_type)
+ {
+ case MOTOR_5345:
+ dev->reg[reg_0x02].value |= 0x01; /* half-step */
+ break;
+ case MOTOR_XP200:
+ /* for this sheetfed scanner, no AGOHOME, nor backtracking */
+ dev->reg[reg_0x02].value = 0x50;
+ break;
+ default:
+ break;
+ }
+ dev->reg[reg_0x03].value = 0x1f /*0x17 */ ; /* lamp on */
+ dev->reg[reg_0x04].value = 0x13 /*0x03 */ ; /* 8 bits data, 16 bits A/D, color, Wolfson fe *//* todo: according to spec, 0x0 is reserved? */
+ switch (dev->model->dac_type)
+ {
+ case DAC_AD_XP200:
+ dev->reg[reg_0x04].value = 0x12;
+ break;
+ default:
+ /* Wolfson frontend */
+ dev->reg[reg_0x04].value = 0x13;
+ break;
+ }
+
+ dev->reg[reg_0x05].value = 0x00; /* 12 bits gamma, disable gamma, 24 clocks/pixel */
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ break;
+ default:
+ dev->reg[reg_0x05].value |= REG05_DPIHW;
+ break;
+ }
+ if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
+ dev->reg[reg_0x05].value |= REG05_GMM14BIT;
+ if (dev->model->dac_type == DAC_AD_XP200)
+ dev->reg[reg_0x05].value |= 0x01; /* 12 clocks/pixel */
+
+ if (dev->model->ccd_type == CCD_HP2300)
+ dev->reg[reg_0x06].value = 0x00; /* PWRBIT off, shading gain=4, normal AFE image capture */
+ else
+ dev->reg[reg_0x06].value = 0x18; /* PWRBIT on, shading gain=8, normal AFE image capture */
+
+
+ gl646_setup_sensor (dev, dev->reg);
+
+ dev->reg[reg_0x1e].value = 0xf0; /* watch-dog time */
+
+ switch (dev->model->ccd_type)
+ {
+ case CCD_HP2300:
+ dev->reg[reg_0x1e].value = 0xf0;
+ dev->reg[reg_0x1f].value = 0x10;
+ dev->reg[reg_0x20].value = 0x20;
+ break;
+ case CCD_HP2400:
+ dev->reg[reg_0x1e].value = 0x80;
+ dev->reg[reg_0x1f].value = 0x10;
+ dev->reg[reg_0x20].value = 0x20;
+ break;
+ case CCD_HP3670:
+ dev->reg[reg_0x19].value = 0x2a;
+ dev->reg[reg_0x1e].value = 0x80;
+ dev->reg[reg_0x1f].value = 0x10;
+ dev->reg[reg_0x20].value = 0x20;
+ break;
+ case CIS_XP200:
+ dev->reg[reg_0x1e].value = 0x10;
+ dev->reg[reg_0x1f].value = 0x01;
+ dev->reg[reg_0x20].value = 0x50;
+ break;
+ default:
+ dev->reg[reg_0x1f].value = 0x01;
+ dev->reg[reg_0x20].value = 0x50;
+ break;
+ }
+
+ dev->reg[reg_0x21].value = 0x08 /*0x20 */ ; /* table one steps number for forward slope curve of the acc/dec */
+ dev->reg[reg_0x22].value = 0x10 /*0x08 */ ; /* steps number of the forward steps for start/stop */
+ dev->reg[reg_0x23].value = 0x10 /*0x08 */ ; /* steps number of the backward steps for start/stop */
+ dev->reg[reg_0x24].value = 0x08 /*0x20 */ ; /* table one steps number backward slope curve of the acc/dec */
+ dev->reg[reg_0x25].value = 0x00; /* scan line numbers (7000) */
+ dev->reg[reg_0x26].value = 0x00 /*0x1b */ ;
+ dev->reg[reg_0x27].value = 0xd4 /*0x58 */ ;
+ dev->reg[reg_0x28].value = 0x01; /* PWM duty for lamp control */
+ dev->reg[reg_0x29].value = 0xff;
+
+ dev->reg[reg_0x2c].value = 0x02; /* set resolution (600 DPI) */
+ dev->reg[reg_0x2d].value = 0x58;
+ dev->reg[reg_0x2e].value = 0x78; /* set black&white threshold high level */
+ dev->reg[reg_0x2f].value = 0x7f; /* set black&white threshold low level */
+
+ dev->reg[reg_0x30].value = 0x00; /* begin pixel position (16) */
+ dev->reg[reg_0x31].value = dev->sensor.dummy_pixel /*0x10 */ ; /* TGW + 2*TG_SHLD + x */
+ dev->reg[reg_0x32].value = 0x2a /*0x15 */ ; /* end pixel position (5390) */
+ dev->reg[reg_0x33].value = 0xf8 /*0x0e */ ; /* TGW + 2*TG_SHLD + y */
+ dev->reg[reg_0x34].value = dev->sensor.dummy_pixel;
+ dev->reg[reg_0x35].value = 0x01 /*0x00 */ ; /* set maximum word size per line, for buffer full control (10800) */
+ dev->reg[reg_0x36].value = 0x00 /*0x2a */ ;
+ dev->reg[reg_0x37].value = 0x00 /*0x30 */ ;
+ dev->reg[reg_0x38].value = HIBYTE (dev->settings.exposure_time) /*0x2a */ ; /* line period (exposure time = 11000 pixels) */
+ dev->reg[reg_0x39].value = LOBYTE (dev->settings.exposure_time) /*0xf8 */ ;
+ dev->reg[reg_0x3d].value = 0x00; /* set feed steps number of motor move */
+ dev->reg[reg_0x3e].value = 0x00;
+ dev->reg[reg_0x3f].value = 0x01 /*0x00 */ ;
+
+ dev->reg[reg_0x60].value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
+ dev->reg[reg_0x61].value = 0x00; /* (21h+22h)/LPeriod */
+ dev->reg[reg_0x62].value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
+ dev->reg[reg_0x63].value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
+ dev->reg[reg_0x64].value = 0x00; /* motor PWM frequency */
+ dev->reg[reg_0x65].value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
+ if (dev->model->motor_type == MOTOR_5345)
+ dev->reg[reg_0x65].value = 0x02; /* PWM duty cycle for table one motor phase (63 = max) */
+ dev->reg[reg_0x66].value = dev->gpo.value[0];
+ dev->reg[reg_0x67].value = dev->gpo.value[1];
+ dev->reg[reg_0x68].value = dev->gpo.enable[0];
+ dev->reg[reg_0x69].value = dev->gpo.enable[1];
+
+ switch (dev->model->motor_type)
+ {
+ case MOTOR_HP2300:
+ case MOTOR_HP2400:
+ dev->reg[reg_0x6a].value = 0x7f; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6b].value = 0x78; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6d].value = 0x7f;
+ break;
+ case MOTOR_5345:
+ dev->reg[reg_0x6a].value = 0x42; /* table two fast moving step type, PWM duty for table two */
+ dev->reg[reg_0x6b].value = 0xff; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6d].value = 0x41; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
+ break;
+ case MOTOR_XP200:
+ dev->reg[reg_0x6a].value = 0x7f; /* table two fast moving step type, PWM duty for table two */
+ dev->reg[reg_0x6b].value = 0x08; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6d].value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
+ break;
+ case MOTOR_HP3670:
+ dev->reg[reg_0x6a].value = 0x41; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6b].value = 0xc8; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6d].value = 0x7f;
+ break;
+ default:
+ dev->reg[reg_0x6a].value = 0x40; /* table two fast moving step type, PWM duty for table two */
+ dev->reg[reg_0x6b].value = 0xff; /* table two steps number for acc/dec */
+ dev->reg[reg_0x6d].value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
+ break;
+ }
+ dev->reg[reg_0x6c].value = 0x00; /* peroid times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
+}
+
+
+/* Send slope table for motor movement
+ slope_table in machine byte order
+*/
+static SANE_Status
+gl646_send_slope_table (Genesys_Device * dev, int table_nr,
+ uint16_t * slope_table, int steps)
+{
+ int dpihw;
+ int start_address;
+ SANE_Status status;
+ uint8_t *table;
+#ifdef WORDS_BIGENDIAN
+ int i;
+#endif
+
+ DBG (DBG_proc,
+ "gl646_send_slope_table (table_nr = %d, steps = %d)=%d .. %d\n",
+ table_nr, steps, slope_table[0], slope_table[steps - 1]);
+
+ dpihw = dev->reg[reg_0x05].value >> 6;
+
+ if (dpihw == 0) /* 600 dpi */
+ start_address = 0x08000;
+ else if (dpihw == 1) /* 1200 dpi */
+ start_address = 0x10000;
+ else if (dpihw == 2) /* 2400 dpi */
+ start_address = 0x1f800;
+ else /* reserved */
+ return SANE_STATUS_INVAL;
+
+#ifdef WORDS_BIGENDIAN
+ table = (uint8_t *) malloc (steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+#else
+ table = (uint8_t *) slope_table;
+#endif
+
+ status =
+ sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x100);
+ if (status != SANE_STATUS_GOOD)
+ {
+#ifdef WORDS_BIGENDIAN
+ free (table);
+#endif
+ DBG (DBG_error,
+ "gl646_send_slope_table: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl646_bulk_write_data (dev, 0x3c, (uint8_t *) table, steps * 2);
+ if (status != SANE_STATUS_GOOD)
+ {
+#ifdef WORDS_BIGENDIAN
+ free (table);
+#endif
+ DBG (DBG_error,
+ "gl646_send_slope_table: failed to send slope table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+#ifdef WORDS_BIGENDIAN
+ free (table);
+#endif
+ DBG (DBG_proc, "gl646_send_slope_table: end\n");
+ return status;
+}
+
+/* Set values of Analog Device type frontend */
+static SANE_Status
+gl646_set_ad_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ uint16_t val;
+
+ DBG (DBG_proc, "gl646_set_ad_fe(): start\n");
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl646_set_ad_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+
+ /* sets to default values */
+ sanei_genesys_init_fe (dev);
+
+ /* write them to analog frontend */
+ val = dev->frontend.reg[0];
+ status = sanei_genesys_fe_write_data (dev, 0x00, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_ad_fe: failed to write reg0: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ val = dev->frontend.reg[1];
+ status = sanei_genesys_fe_write_data (dev, 0x01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_ad_fe: failed to write reg1: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ if (set == AFE_SET)
+ {
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.gain[i];
+ status = sanei_genesys_fe_write_data (dev, 0x02 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_set_ad_fe: failed to write gain %d: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.offset[i];
+ status = sanei_genesys_fe_write_data (dev, 0x05 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_set_ad_fe: failed to write offset %d: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ }
+ /*
+ if (set == AFE_POWER_SAVE)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0] | 0x04);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_ad_fe: failed to write reg0: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ } */
+ DBG (DBG_proc, "gl646_set_ad_fe(): end\n");
+
+ return status;
+}
+
+/** set up analog frontend
+ * set up analog frontend
+ * @param dev device to set up
+ * @param set action from AFE_SET, AFE_INIT and AFE_POWERSAVE
+ * @param dpi resolution of the scan since it affects settings
+ * @return SANE_STATUS_GOOD if evrithing OK
+ */
+static SANE_Status
+gl646_wm_hp3670 (Genesys_Device * dev, uint8_t set, int dpi)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+
+ DBG (DBG_proc, "gl646_wm_hp3670: start \n");
+ switch (set)
+ {
+ case AFE_INIT:
+ status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: reset failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (200000UL);
+ RIE (sanei_genesys_write_register (dev, 0x50, 0x00));
+ sanei_genesys_init_fe (dev);
+ status = sanei_genesys_fe_write_data (dev, 0x01, dev->frontend.reg[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing reg1 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x02, dev->frontend.reg[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing reg2 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = gl646_gpio_output_enable (dev->dn, 0x07);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: failed to enable GPIO: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ break;
+ case AFE_POWER_SAVE:
+ status = sanei_genesys_fe_write_data (dev, 0x01, 0x06);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing reg1 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x06, 0x0f);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing reg6 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ return status;
+ break;
+ default: /* AFE_SET */
+ /* mode setup */
+ i = dev->frontend.reg[3];
+ if (dpi > dev->sensor.optical_res / 2)
+ {
+ /* fe_reg_0x03 must be 0x12 for 1200 dpi in DAC_WOLFSON_HP3670.
+ * DAC_WOLFSON_HP2400 in 1200 dpi mode works well with
+ * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */
+ i = 0x12;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x03, i);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing reg3 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ /* offset and sign (or msb/lsb ?) */
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x20 + i,
+ dev->frontend.offset[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_wm_hp3670: writing offset%d failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x24 + i, dev->frontend.sign[i]); /* MSB/LSB ? */
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing sign%d failed: %s\n",
+ i, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* gain */
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x28 + i,
+ dev->frontend.gain[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_wm_hp3670: writing gain%d failed: %s\n",
+ i, sane_strstatus (status));
+ return status;
+ }
+ }
+ }
+
+ DBG (DBG_proc, "gl646_wm_hp3670: success \n");
+ return status;
+}
+
+/** Set values of analog frontend
+ * @param dev device to set
+ * @param set action to execute
+ * @param dpi dpi to setup the AFE
+ * @return error or SANE_STATUS_GOOD */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl646_set_fe (Genesys_Device * dev, uint8_t set, int dpi)
+{
+ SANE_Status status;
+ int i;
+ uint8_t val;
+
+ DBG (DBG_proc, "gl646_set_fe (%s,%d)\n",
+ set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
+ AFE_POWER_SAVE ? "powersave" : "huh?", dpi);
+
+ /* Analog Device type frontend */
+ if ((dev->reg[reg_0x04].value & REG04_FESET) == 0x02)
+ return gl646_set_ad_fe (dev, set);
+
+ /* Wolfson type frontend */
+ if ((dev->reg[reg_0x04].value & REG04_FESET) != 0x03)
+ {
+ DBG (DBG_proc, "gl646_set_fe(): unspported frontend type %d\n",
+ dev->reg[reg_0x04].value & REG04_FESET);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* per frontend function to keep code clean */
+ switch (dev->model->dac_type)
+ {
+ case DAC_WOLFSON_HP3670:
+ case DAC_WOLFSON_HP2400:
+ return gl646_wm_hp3670 (dev, set, dpi);
+ break;
+ default:
+ DBG (DBG_proc, "gl646_set_fe(): using old method\n");
+ break;
+ }
+
+ /* initialize analog frontend */
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl646_set_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+ sanei_genesys_init_fe (dev);
+
+ /* reset only done on init */
+ status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: init fe failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* enable GPIO for some models */
+ if (dev->model->ccd_type == CCD_HP2300)
+ {
+ val = 0x07;
+ status = gl646_gpio_output_enable (dev->dn, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_set_fe: failed to enable GPIO: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ return status;
+ }
+
+ /* set fontend to power saving mode */
+ if (set == AFE_POWER_SAVE)
+ {
+ status = sanei_genesys_fe_write_data (dev, 0x01, 0x02);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing data failed: %s\n",
+ sane_strstatus (status));
+ }
+ return status;
+ }
+
+ /* here starts AFE_SET */
+ /* TODO : base this test on cfg reg3 or a CCD family flag to be created */
+ /* if (dev->model->ccd_type != CCD_HP2300
+ && dev->model->ccd_type != CCD_HP3670
+ && dev->model->ccd_type != CCD_HP2400) */
+ {
+ status = sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing reg0 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x02, dev->frontend.reg[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing reg2 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* start with reg3 */
+ status = sanei_genesys_fe_write_data (dev, 0x03, dev->frontend.reg[3]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing reg3 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ switch (dev->model->ccd_type)
+ {
+ default:
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x24 + i,
+ dev->frontend.sign[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing sign[%d] failed: %s\n",
+ i, sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x28 + i,
+ dev->frontend.gain[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing gain[%d] failed: %s\n",
+ i, sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x20 + i,
+ dev->frontend.offset[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_set_fe: writing offset[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ break;
+ /* just can't have it to work ....
+ case CCD_HP2300:
+ case CCD_HP2400:
+ case CCD_HP3670:
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x23, dev->frontend.offset[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_set_fe: writing offset[1] failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x28, dev->frontend.gain[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing gain[1] failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ break; */
+ }
+
+ /* end with reg1 */
+ status = sanei_genesys_fe_write_data (dev, 0x01, dev->frontend.reg[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_set_fe: writing reg1 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+
+ DBG (DBG_proc, "gl646_set_fe: end\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Set values of analog frontend
+ * this this the public interface, the gl646 as to use one more
+ * parameter to work effectively, hence the redirection
+ * @param dev device to set
+ * @param set action to execute
+ * @return error or SANE_STATUS_GOOD */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl646_public_set_fe (Genesys_Device * dev, uint8_t set)
+{
+ return gl646_set_fe (dev, set, dev->settings.yres);
+}
+
+static void
+gl646_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
+{
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x02,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x02) |
+ REG02_MTRPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x02,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x02) &
+ ~REG02_MTRPWR);
+ }
+}
+
+static void
+gl646_set_lamp_power (Genesys_Device * dev,
+ Genesys_Register_Set * regs, SANE_Bool set)
+{
+ if (dev)
+ {
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x03,
+ sanei_genesys_read_reg_from_set
+ (regs, 0x03) | REG03_LAMPPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x03,
+ sanei_genesys_read_reg_from_set
+ (regs, 0x03) & ~REG03_LAMPPWR);
+ }
+ }
+}
+
+/**
+ * enters or leaves power saving mode
+ * limited to AFE for now.
+ * @param dev scanner's device
+ * @param enable SANE_TRUE to enable power saving, SANE_FALSE to leave it
+ * @return allways SANE_STATUS_GOOD
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl646_save_power (Genesys_Device * dev, SANE_Bool enable)
+{
+
+ DBG (DBG_proc, "gl646_save_power: start\n");
+ DBG (DBG_info, "gl646_save_power: enable = %d\n", enable);
+
+ if (enable)
+ {
+ /* gl646_set_fe (dev, AFE_POWER_SAVE); */
+ }
+ else
+ {
+ gl646_set_fe (dev, AFE_INIT, 0);
+ }
+
+ DBG (DBG_proc, "gl646_save_power: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl646_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Register_Set local_reg[6];
+ int rate, exposure_time, tgtime, time;
+
+ DBG (DBG_proc, "gl646_set_powersaving (delay = %d)\n", delay);
+
+ local_reg[0].address = 0x01;
+ local_reg[0].value = sanei_genesys_read_reg_from_set (dev->reg, 0x01); /* disable fastmode */
+
+ local_reg[1].address = 0x03;
+ local_reg[1].value = sanei_genesys_read_reg_from_set (dev->reg, 0x03); /* Lamp power control */
+
+ local_reg[2].address = 0x05;
+ local_reg[2].value = sanei_genesys_read_reg_from_set (dev->reg, 0x05) & ~REG05_BASESEL; /* 24 clocks/pixel */
+
+ local_reg[3].address = 0x38; /* line period low */
+ local_reg[3].value = 0x00;
+
+ local_reg[4].address = 0x39; /* line period high */
+ local_reg[4].value = 0x00;
+
+ local_reg[5].address = 0x6c; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE */
+ local_reg[5].value = 0x00;
+
+ if (!delay)
+ local_reg[1].value = local_reg[1].value & 0xf0; /* disable lampdog and set lamptime = 0 */
+ else if (delay < 20)
+ local_reg[1].value = (local_reg[1].value & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
+ else
+ local_reg[1].value = (local_reg[1].value & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
+
+ time = delay * 1000 * 60; /* -> msec */
+ exposure_time =
+ (uint32_t) (time * 32000.0 /
+ (24.0 * 64.0 * (local_reg[1].value & REG03_LAMPTIM) *
+ 1024.0) + 0.5);
+ /* 32000 = system clock, 24 = clocks per pixel */
+ rate = (exposure_time + 65536) / 65536;
+ if (rate > 4)
+ {
+ rate = 8;
+ tgtime = 3;
+ }
+ else if (rate > 2)
+ {
+ rate = 4;
+ tgtime = 2;
+ }
+ else if (rate > 1)
+ {
+ rate = 2;
+ tgtime = 1;
+ }
+ else
+ {
+ rate = 1;
+ tgtime = 0;
+ }
+
+ local_reg[5].value |= tgtime << 6;
+ exposure_time /= rate;
+
+ if (exposure_time > 65535)
+ exposure_time = 65535;
+
+ local_reg[3].value = exposure_time / 256; /* highbyte */
+ local_reg[4].value = exposure_time & 255; /* lowbyte */
+
+ status = gl646_bulk_write_register (dev, local_reg,
+ sizeof (local_reg) /
+ sizeof (local_reg[0]));
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error,
+ "gl646_set_powersaving: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+
+ DBG (DBG_proc, "gl646_set_powersaving: end\n");
+ return status;
+}
+
+
+/**
+ * loads document into scanner
+ * currently only used by XP200
+ * bit2 (0x04) of gpio is paper event (document in/out) on XP200
+ * HOMESNR is set if no document in front of sensor, the sequence of events is
+ * paper event -> document is in the sheet feeder
+ * HOMESNR becomes 0 -> document reach sensor
+ * HOMESNR becomes 1 ->document left sensor
+ * paper event -> document is out
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl646_load_document (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Register_Set regs[11];
+ unsigned int used, vfinal, count;
+ uint16_t slope_table[255];
+ uint8_t val;
+
+ DBG (DBG_proc, "gl646_load_document: start\n");
+
+ /* no need to load document is flatbed scanner */
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ DBG (DBG_proc, "gl646_load_document: nothing to load\n");
+ DBG (DBG_proc, "gl646_load_document: end\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to read status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* HOMSNR is set if a document is inserted */
+ if ((val & REG41_HOMESNR))
+ {
+ /* if no document, waits for a paper event to start loading */
+ /* with a 60 seconde minutes timeout */
+ count = 0;
+ do
+ {
+ status = gl646_gpio_read (dev->dn, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to read paper sensor %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_info, "gl646_load_document: GPIO=0x%02x\n", val);
+ if ((val & 0x04) != 0x04)
+ {
+ DBG (DBG_warn, "gl646_load_document: no paper detected\n");
+ }
+ usleep (200000UL); /* sleep 200 ms */
+ count++;
+ }
+ while (((val & 0x04) != 0x04) && (count < 300)); /* 1 min time out */
+ if (count == 300)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: timeout waiting for document\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ /* set up to fast move before scan then move until document is detected */
+ regs[0].address = 0x01;
+ regs[0].value = 0x90;
+
+ /* AGOME, 2 slopes motor moving */
+ regs[1].address = 0x02;
+ regs[1].value = 0x79;
+
+ /* motor feeding steps to 0 */
+ regs[2].address = 0x3d;
+ regs[2].value = 0;
+ regs[3].address = 0x3e;
+ regs[3].value = 0;
+ regs[4].address = 0x3f;
+ regs[4].value = 0;
+
+ /* 50 fast moving steps */
+ regs[5].address = 0x6b;
+ regs[5].value = 50;
+
+ /* set GPO */
+ regs[6].address = 0x66;
+ regs[6].value = 0x30;
+
+ /* stesp NO */
+ regs[7].address = 0x21;
+ regs[7].value = 4;
+ regs[8].address = 0x22;
+ regs[8].value = 1;
+ regs[9].address = 0x23;
+ regs[9].value = 1;
+ regs[10].address = 0x24;
+ regs[10].value = 4;
+
+ /* generate slope table 2 */
+ sanei_genesys_generate_slope_table (slope_table,
+ 50,
+ 51,
+ 2400,
+ 6000, 2400, 50, 0.25, &used, &vfinal);
+/* document loading:
+ * send regs
+ * start motor
+ * wait e1 status to become e0
+ */
+ status = gl646_send_slope_table (dev, 1, slope_table, 50);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to send slope table 1: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status =
+ gl646_bulk_write_register (dev, regs, sizeof (regs) / sizeof (regs[0]));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl646_start_motor (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to start motor: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ count = 0;
+ do
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to read status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (200000UL); /* sleep 200 ms */
+ count++;
+ }
+ while ((val & REG41_MOTMFLG) && (count < 300));
+ if (count == 300)
+ {
+ DBG (DBG_error, "gl646_load_document: can't load document\n");
+ return SANE_STATUS_JAMMED;
+ }
+
+ /* when loading OK, document is here */
+ dev->document = SANE_TRUE;
+
+ /* set up to idle */
+ regs[1].value = 0x71;
+ regs[4].value = 1;
+ regs[5].value = 8;
+ status =
+ gl646_bulk_write_register (dev, regs, sizeof (regs) / sizeof (regs[0]));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_load_document: failed to bulk write idle registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "gl646_load_document: end\n");
+
+ return status;
+}
+
+/**
+ * detects end of document and adjust current scan
+ * to take it into account
+ * used by sheetfed scanners
+ */
+static SANE_Status
+gl646_detect_document_end (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t val, gpio;
+ unsigned int bytes_left, lines;
+
+ DBG (DBG_proc, "gl646_detect_document_end: start\n");
+
+ /* test for document presence */
+ RIE (sanei_genesys_get_status (dev, &val));
+ if (DBG_LEVEL > DBG_info)
+ {
+ print_status (val);
+ }
+ status = gl646_gpio_read (dev->dn, &gpio);
+ DBG (DBG_info, "gl646_detect_document_end: GPIO=0x%02x\n", gpio);
+
+ /* detect document event. There one event when the document go in,
+ * then another when it leaves */
+ if ((dev->document == SANE_TRUE) && (gpio & 0x04)
+ && (dev->total_bytes_read > 0))
+ {
+ DBG (DBG_info, "gl646_detect_document_end: no more document\n");
+ dev->document = SANE_FALSE;
+
+ /* adjust number of bytes to read:
+ * total_bytes_to_read is the number of byte to send to frontend
+ * total_bytes_read is the number of bytes sent to frontend
+ * read_bytes_left is the number of bytes to read from the scanner
+ */
+ DBG (DBG_io, "gl646_detect_document_end: total_bytes_to_read=%lu\n",
+ (u_long) dev->total_bytes_to_read);
+ DBG (DBG_io, "gl646_detect_document_end: total_bytes_read =%lu\n",
+ (u_long) dev->total_bytes_read);
+ DBG (DBG_io, "gl646_detect_document_end: read_bytes_left =%lu\n",
+ (u_long) dev->read_bytes_left);
+
+ /* amount of data available from scanner is what to scan */
+ status = sanei_genesys_read_valid_words (dev, &bytes_left);
+
+ /* we add the number of lines needed to read the last part of the document in */
+ lines =
+ (SANE_UNFIX (dev->model->y_offset) * dev->current_setup.yres) /
+ MM_PER_INCH;
+ DBG (DBG_io, "gl646_detect_document_end: adding %d line to flush\n",
+ lines);
+ bytes_left += lines * dev->wpl;
+ if (dev->current_setup.depth > 8)
+ {
+ bytes_left = 2 * bytes_left;
+ }
+ if (dev->current_setup.channels > 1)
+ {
+ bytes_left = 3 * bytes_left;
+ }
+ if (bytes_left < dev->read_bytes_left)
+ {
+ dev->total_bytes_to_read = dev->total_bytes_read + bytes_left;
+ dev->read_bytes_left = bytes_left;
+ }
+ DBG (DBG_io, "gl646_detect_document_end: total_bytes_to_read=%lu\n",
+ (u_long) dev->total_bytes_to_read);
+ DBG (DBG_io, "gl646_detect_document_end: total_bytes_read =%lu\n",
+ (u_long) dev->total_bytes_read);
+ DBG (DBG_io, "gl646_detect_document_end: read_bytes_left =%lu\n",
+ (u_long) dev->read_bytes_left);
+ }
+ DBG (DBG_proc, "gl646_detect_document_end: end\n");
+
+ return status;
+}
+
+/**
+ * eject document from the feeder
+ * currently only used by XP200
+ * TODO we currently rely on AGOHOME not being set for sheetfed scanners,
+ * maybe check this flag in eject to let the document being eject automaticaly
+ */
+static SANE_Status
+gl646_eject_document (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Register_Set regs[11];
+ unsigned int used, vfinal, count;
+ uint16_t slope_table[255];
+ uint8_t gpio, state;
+
+ DBG (DBG_proc, "gl646_eject_document: start\n");
+
+ /* at the end there will be noe more document */
+ dev->document = SANE_FALSE;
+
+ /* first check for document event */
+ status = gl646_gpio_read (dev->dn, &gpio);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to read paper sensor %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_info, "gl646_eject_document: GPIO=0x%02x\n", gpio);
+
+ /* test status : paper event + HOMESNR -> no more doc ? */
+ status = sanei_genesys_get_status (dev, &state);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to read status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_info, "gl646_eject_document: state=0x%02x\n", state);
+ if (DBG_LEVEL > DBG_info)
+ {
+ print_status (state);
+ }
+
+ /* HOMSNR=0 if no document inserted */
+ if ((state & REG41_HOMESNR) != 0)
+ {
+ dev->document = SANE_FALSE;
+ DBG (DBG_info, "gl646_eject_document: no more document to eject\n");
+ DBG (DBG_proc, "gl646_eject_document: end\n");
+ return status;
+ }
+
+ /* there is a document inserted, eject it */
+ status = sanei_genesys_write_register (dev, 0x01, 0xb0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to write register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* wait for motor to stop */
+ do
+ {
+ usleep (200000UL);
+ status = sanei_genesys_get_status (dev, &state);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to read status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ while (state & REG41_MOTMFLG);
+
+ /* set up to fast move before scan then move until document is detected */
+ regs[0].address = 0x01;
+ regs[0].value = 0xb0;
+
+ /* AGOME, 2 slopes motor moving , eject 'backward' */
+ regs[1].address = 0x02;
+ regs[1].value = 0x5d;
+
+ /* motor feeding steps to 119880 */
+ regs[2].address = 0x3d;
+ regs[2].value = 1;
+ regs[3].address = 0x3e;
+ regs[3].value = 0xd4;
+ regs[4].address = 0x3f;
+ regs[4].value = 0x48;
+
+ /* 60 fast moving steps */
+ regs[5].address = 0x6b;
+ regs[5].value = 60;
+
+ /* set GPO */
+ regs[6].address = 0x66;
+ regs[6].value = 0x30;
+
+ /* stesp NO */
+ regs[7].address = 0x21;
+ regs[7].value = 4;
+ regs[8].address = 0x22;
+ regs[8].value = 1;
+ regs[9].address = 0x23;
+ regs[9].value = 1;
+ regs[10].address = 0x24;
+ regs[10].value = 4;
+
+ /* generate slope table 2 */
+ sanei_genesys_generate_slope_table (slope_table,
+ 60,
+ 61,
+ 1600,
+ 10000, 1600, 60, 0.25, &used, &vfinal);
+/* document eject:
+ * send regs
+ * start motor
+ * wait c1 status to become c8 : HOMESNR and ~MOTFLAG
+ */
+ status = gl646_send_slope_table (dev, 1, slope_table, 60);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to send slope table 1: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status =
+ gl646_bulk_write_register (dev, regs, sizeof (regs) / sizeof (regs[0]));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl646_start_motor (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to start motor: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* loop until paper sensor tells paper is out, and till motor is running */
+ /* use a 30 timeout */
+ count = 0;
+ do
+ {
+ status = sanei_genesys_get_status (dev, &state);
+ print_status (state);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to read status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (200000UL); /* sleep 200 ms */
+ count++;
+ }
+ while (((state & REG41_HOMESNR) == 0) && (count < 150));
+
+ /* read GPIO on exit */
+ status = gl646_gpio_read (dev->dn, &gpio);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_eject_document: failed to read paper sensor %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_info, "gl646_eject_document: GPIO=0x%02x\n", gpio);
+
+ DBG (DBG_proc, "gl646_eject_document: end\n");
+ return status;
+}
+
+/* Send the low-level scan command */
+static SANE_Status
+gl646_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor)
+{
+ SANE_Status status;
+ Genesys_Register_Set local_reg[3];
+
+ DBG (DBG_proc, "gl646_begin_scan\n");
+
+ local_reg[0].address = 0x03;
+ local_reg[0].value = sanei_genesys_read_reg_from_set (reg, 0x03);
+
+ local_reg[1].address = 0x01;
+ local_reg[1].value = sanei_genesys_read_reg_from_set (reg, 0x01) | REG01_SCAN; /* set scan bit */
+
+ local_reg[2].address = 0x0f;
+ if (start_motor)
+ local_reg[2].value = 0x01;
+ else
+ local_reg[2].value = 0x00; /* do not start motor yet */
+
+ status = gl646_bulk_write_register (dev, local_reg,
+ sizeof (local_reg) /
+ sizeof (local_reg[0]));
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_begin_scan: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "gl646_begin_scan: end\n");
+
+ return status;
+}
+
+
+/* Send the stop scan command */
+static SANE_Status
+end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop, SANE_Bool eject)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i = 0;
+ uint8_t val, scanfsh = 0;
+
+ DBG (DBG_proc, "end_scan (check_stop = %d, eject = %d)\n", check_stop,
+ eject);
+
+ /* we need to compute scanfsh before cancelling scan */
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "end_scan: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val & REG41_SCANFSH)
+ scanfsh = 1;
+ if (DBG_LEVEL > DBG_io2)
+ {
+ print_status (val);
+ }
+ }
+
+ /* ends scan */
+ val = sanei_genesys_read_reg_from_set (reg, 0x01);
+ val &= ~REG01_SCAN;
+ sanei_genesys_set_reg_from_set (reg, 0x01, val);
+ status = sanei_genesys_write_register (dev, 0x01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "end_scan: failed to write register 01: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* for sheetfed scanners, we may have to eject document */
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ if (eject == SANE_TRUE && dev->document == SANE_TRUE)
+ {
+ status = gl646_eject_document (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "end_scan: failed to eject document\n");
+ return status;
+ }
+ }
+ if (check_stop)
+ {
+ for (i = 0; i < 30; i++) /* do not wait longer than wait 3 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "end_scan: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val & REG41_SCANFSH)
+ scanfsh = 1;
+ if (DBG_LEVEL > DBG_io2)
+ {
+ print_status (val);
+ }
+
+ if (!(val & REG41_MOTMFLG) && (val & REG41_FEEDFSH) && scanfsh)
+ {
+ DBG (DBG_proc, "end_scan: scanfeed finished\n");
+ break; /* leave for loop */
+ }
+
+ usleep (10000UL); /* sleep 100 ms */
+ }
+ }
+ }
+ else /* flat bed scanners */
+ {
+ if (check_stop)
+ {
+ for (i = 0; i < 300; i++) /* do not wait longer than wait 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "end_scan: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val & REG41_SCANFSH)
+ scanfsh = 1;
+ if (DBG_LEVEL > DBG_io)
+ {
+ print_status (val);
+ }
+
+ if (!(val & REG41_MOTMFLG) && (val & REG41_FEEDFSH) && scanfsh)
+ {
+ DBG (DBG_proc, "end_scan: scanfeed finished\n");
+ break; /* leave while loop */
+ }
+
+ if ((!(val & REG41_MOTMFLG)) && (val & REG41_HOMESNR))
+ {
+ DBG (DBG_proc, "end_scan: head at home\n");
+ break; /* leave while loop */
+ }
+
+ usleep (10000UL); /* sleep 100 ms */
+ }
+ }
+ }
+
+ DBG (DBG_proc, "end_scan: end (i=%u)\n", i);
+
+ return status;
+}
+
+/* Send the stop scan command */
+static SANE_Status
+gl646_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop)
+{
+ return end_scan (dev, reg, check_stop, SANE_FALSE);
+}
+
+/**
+ * parks head
+ * @param dev scanner's device
+ * @param wait_until_home true if the function waits until head parked
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl646_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
+{
+ SANE_Status status;
+ Genesys_Settings settings;
+ uint8_t val;
+ int i;
+ int loop = 0;
+
+ DBG (DBG_proc, "gl646_slow_back_home: start , wait_until_home = %d\n",
+ wait_until_home);
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL > DBG_io)
+ {
+ print_status (val);
+ }
+
+ dev->scanhead_position_in_steps = 0;
+
+ if (val & REG41_HOMESNR) /* is sensor at home? */
+ {
+ DBG (DBG_info, "gl646_slow_back_home: end since already at home\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* stop motor if needed */
+ if (val & REG41_MOTMFLG)
+ {
+ status = gl646_stop_motor (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_slow_back_home: failed to stop motor: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ usleep (200000UL);
+ }
+
+ /* when scanhead is moving then wait until scanhead stops or timeout */
+ DBG (DBG_info, "gl646_slow_back_home: ensuring that motor is off\n");
+ val = REG41_MOTMFLG;
+ for (i = 400; i > 0 && (val & REG41_MOTMFLG); i--) /* do not wait longer than 40 seconds, count down to get i = 0 when busy */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_slow_back_home: Failed to read home sensor & motor status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (((val & (REG41_MOTMFLG | REG41_HOMESNR)) == REG41_HOMESNR)) /* at home and motor is off */
+ {
+ DBG (DBG_info,
+ "gl646_slow_back_home: already at home and not moving\n");
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100 * 1000); /* sleep 100 ms (todo: fixed to really sleep 100 ms) */
+ }
+
+ if (!i) /* the loop counted down to 0, scanner still is busy */
+ {
+ DBG (DBG_error,
+ "gl646_slow_back_home: motor is still on: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* setup for a backward scan of 65535 steps, with no actual data reading */
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_COLOR;
+ settings.xres = get_lowest_resolution (dev->model->ccd_type, SANE_FALSE);
+ settings.yres = settings.xres;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = 600;
+ settings.lines = 1;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ status = setup_for_scan (dev, settings, SANE_TRUE, SANE_TRUE, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to setup for scan: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* backward , no actual data scanned TODO more setup flags to avoid this register manipulations ? */
+ dev->reg[reg_0x02].value |= REG02_MTRREV;
+ dev->reg[reg_0x01].value &= ~REG01_SCAN;
+ gl646_set_triple_reg (dev->reg, REG_FEEDL, 65535);
+
+ /* sets frontend */
+ status = gl646_set_fe (dev, AFE_SET, settings.xres);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to set frontend: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* write scan registers */
+ status = gl646_bulk_write_register (dev, dev->reg,
+ sizeof (dev->reg) /
+ sizeof (dev->reg[0]));
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error,
+ "gl646_slow_back_home: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+
+ /* registers are restored to an iddl state, give up if no head to park */
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ DBG (DBG_proc, "gl646_slow_back_home: end \n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* starts scan */
+ status = gl646_begin_scan (dev, dev->reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_slow_back_home: failed to begin scan: \n");
+ return status;
+ }
+
+ /* loop until head parked */
+ if (wait_until_home)
+ {
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_slow_back_home: Failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (val & 0x08) /* home sensor */
+ {
+ DBG (DBG_info, "gl646_slow_back_home: reached home position\n");
+ DBG (DBG_proc, "gl646_slow_back_home: end\n");
+ usleep (500000); /* sleep 500 ms before returning */
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl646_stop_motor (dev);
+ end_scan (dev, dev->reg, SANE_TRUE, SANE_FALSE);
+ DBG (DBG_error,
+ "gl646_slow_back_home: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ DBG (DBG_info, "gl646_slow_back_home: scanhead is still moving\n");
+ DBG (DBG_proc, "gl646_slow_back_home: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Automatically set top-left edge of the scan area by scanning an
+ * area at 300 dpi from very top of scanner
+ * @param dev device stucture describing the scanner
+ * @return SANE_STATUS_GOOD in cas of success, else failure code
+ */
+static SANE_Status
+gl646_search_start_position (Genesys_Device * dev)
+{
+ SANE_Status status;
+ unsigned char *data = NULL;
+ Genesys_Settings settings;
+ unsigned int resolution, x, y;
+
+ DBG (DBG_proc, "gl646_search_start_position: start\n");
+
+ /* we scan at 300 dpi */
+ resolution = get_closest_resolution (dev->model->ccd_type, 300, SANE_FALSE);
+
+ /* fill settings for a gray level scan */
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_GRAY;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = 600;
+ settings.lines = dev->model->search_lines;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* scan the desired area */
+ status =
+ simple_scan (dev, settings, SANE_TRUE, SANE_TRUE, SANE_FALSE, &data);
+
+ /* process data if scan is OK */
+ if (status == SANE_STATUS_GOOD)
+ {
+ /* handle stagger case : reorder gray data and thus loose some lines */
+ if (dev->current_setup.stagger > 0)
+ {
+ DBG (DBG_proc, "gl646_search_start_position: 'un-staggering'\n");
+ for (y = 0; y < settings.lines - dev->current_setup.stagger; y++)
+ {
+ /* one point out of 2 is 'unaligned' */
+ for (x = 0; x < settings.pixels; x += 2)
+ {
+ data[y * settings.pixels + x] =
+ data[(y + dev->current_setup.stagger) * settings.pixels +
+ x];
+ }
+ }
+ /* correct line number */
+ settings.lines -= dev->current_setup.stagger;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file ("search_position.pnm",
+ data,
+ settings.depth,
+ 1, settings.pixels, settings.lines);
+ }
+ }
+ else
+ {
+ DBG (DBG_error, "gl646_search_start_position: simple_scan failed\n");
+ free (data);
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* now search reference points on the data */
+ status =
+ sanei_genesys_search_reference_point (dev, data,
+ dev->sensor.CCD_start_xoffset,
+ resolution, settings.pixels,
+ settings.lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_search_start_position: failed to set search reference point: %s\n",
+ sane_strstatus (status));
+ }
+
+ free (data);
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * internally overriden during effective calibration
+ * sets up register for coarse gain calibration
+ */
+static SANE_Status
+gl646_init_regs_for_coarse_calibration (Genesys_Device * dev)
+{
+ DBG (DBG_proc, "gl646_init_regs_for_coarse_calibration\n");
+ DBG (DBG_proc, "gl646_init_register_for_coarse_calibration: end\n");
+
+ /* to make compilers happy ... */
+ if (!dev)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * init registers for shading calibration
+ * we assume that scanner's head is on an area suiting shading calibration.
+ * We scan a full scan width area by the shading line number for the device
+ * at either at full sensor's resolution or half depending upon half_ccd
+ * @param dev scanner's device
+ * @return SANE_STATUS_GOOD if success, else error code
+ */
+static SANE_Status
+gl646_init_regs_for_shading (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Settings settings;
+ /* 1: no half_ccd, 2: use half number of pixels */
+ int half_ccd = 1;
+ int cksel = 1;
+
+ DBG (DBG_proc, "gl646_init_register_for_shading: start\n");
+
+ /* when shading all (full width) line, we must adapt to half_ccd case */
+ if (dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE)
+ {
+ /* walk the master mode list to find if half_ccd */
+ if (is_half_ccd (dev->model->ccd_type, dev->settings.xres, SANE_TRUE) ==
+ SANE_TRUE)
+ {
+ half_ccd = 2;
+ }
+ }
+
+ /* fill settings for scan : always a color scan */
+ settings.scan_method = dev->settings.scan_method;
+ settings.scan_mode = dev->settings.scan_mode;
+ if (dev->model->is_cis == SANE_FALSE)
+ {
+ settings.scan_mode = SCAN_MODE_COLOR;
+ }
+ settings.xres = dev->sensor.optical_res / half_ccd;
+ cksel = get_cksel (dev->model->ccd_type, dev->settings.xres, SANE_TRUE);
+ settings.xres = settings.xres / cksel;
+ settings.yres = settings.xres;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * settings.xres) / dev->sensor.optical_res;
+ dev->calib_lines = dev->model->shading_lines;
+ settings.lines = dev->calib_lines * (3 - half_ccd);
+ settings.depth = 16;
+ settings.color_filter = dev->settings.color_filter;
+
+ settings.disable_interpolation = dev->settings.disable_interpolation;
+ settings.threshold = dev->settings.threshold;
+ settings.exposure_time = dev->settings.exposure_time;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* keep account of the movement for final scan move */
+ dev->scanhead_position_in_steps += settings.lines;
+
+ /* we don't want top offset, but we need right margin to be the same
+ * than the one for the final scan */
+ status = setup_for_scan (dev, settings, SANE_TRUE, SANE_FALSE, SANE_FALSE);
+
+ /* used when sending shading calibration data */
+ dev->calib_pixels = settings.pixels;
+ dev->calib_channels = dev->current_setup.channels;
+ if (dev->model->is_cis == SANE_FALSE)
+ {
+ dev->calib_channels = 3;
+ }
+
+ /* no shading */
+ dev->reg[reg_0x01].value &= ~REG01_DVDSET;
+ dev->reg[reg_0x02].value |= REG02_ACDCDIS; /* ease backtracking */
+ dev->reg[reg_0x02].value &= ~(REG02_FASTFED | REG02_AGOHOME);
+ dev->reg[reg_0x05].value &= ~REG05_GMMENB;
+ gl646_set_motor_power (dev->reg, SANE_FALSE);
+
+ /* TODO another flag to setup regs ? */
+ /* enforce needed LINCNT, getting rid of extra lines for color reordering */
+ if (dev->model->is_cis == SANE_FALSE)
+ {
+ gl646_set_triple_reg (dev->reg, REG_LINCNT, dev->calib_lines);
+ }
+ else
+ {
+ gl646_set_triple_reg (dev->reg, REG_LINCNT, dev->calib_lines * 3);
+ }
+
+ /* copy reg to calib_reg */
+ memcpy (dev->calib_reg, dev->reg,
+ GENESYS_GL646_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* this is an hack to make calibration cache working .... */
+ /* if we don't do this, cache will be identified at the shading calibration
+ * dpi which is different from calibration one */
+ dev->current_setup.xres = dev->settings.xres;
+ DBG (DBG_info,
+ "gl646_init_register_for_shading:\n\tdev->settings.xres=%d\n\tdev->settings.yres=%d\n",
+ dev->settings.xres, dev->settings.yres);
+
+ DBG (DBG_proc, "gl646_init_register_for_shading: end\n");
+ return status;
+}
+
+
+/**
+ * set up registers for the actual scan. The scan's parameters are given
+ * through the device settings. It allocates the scan buffers.
+ */
+static SANE_Status
+gl646_init_regs_for_scan (Genesys_Device * dev)
+{
+ SANE_Status status;
+
+ /* park head after calibration if needed */
+ if (dev->scanhead_position_in_steps > 0
+ && dev->settings.scan_method == SCAN_METHOD_FLATBED)
+ {
+ status = gl646_slow_back_home (dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+
+ }
+ dev->scanhead_position_in_steps = 0;
+ }
+
+ return setup_for_scan (dev, dev->settings, SANE_FALSE, SANE_TRUE,
+ SANE_TRUE);
+}
+
+/**
+ * set up registers for the actual scan. The scan's parameters are given
+ * through the device settings. It allocates the scan buffers.
+ * @param dev scanner's device
+ * @param settings settings of scan
+ * @param split SANE_TRUE if move to scan area is split from scan, SANE_FALSE is
+ * scan first moves to area
+ * @param xcorrection take x geometry correction into account (fixed and detected offsets)
+ * @param ycorrection take y geometry correction into account
+ */
+static SANE_Status
+setup_for_scan (Genesys_Device * dev, Genesys_Settings settings,
+ SANE_Bool split, SANE_Bool xcorrection, SANE_Bool ycorrection)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool color;
+ SANE_Int depth;
+ int channels;
+ uint16_t startx = 0, endx, pixels;
+ int move = 0;
+
+ DBG (DBG_proc, "setup_for_scan: start\n");
+ DBG (DBG_info,
+ "setup_for_scan settings:\nResolution: %ux%uDPI\n"
+ "Lines : %u\nPixels : %u\nStartpos : %.3f/%.3f\nScan mode : %d\nScan method: %s\n\n",
+ settings.xres, settings.yres, settings.lines, settings.pixels,
+ settings.tl_x, settings.tl_y, settings.scan_mode,
+ settings.scan_method == SCAN_METHOD_FLATBED ? "flatbed" : "XPA");
+
+ if (settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ {
+ channels = 3;
+ color = SANE_TRUE;
+ }
+ else
+ {
+ channels = 1;
+ color = SANE_FALSE;
+ }
+
+ depth=settings.depth;
+ if (settings.scan_mode == SCAN_MODE_LINEART)
+ {
+ if (settings.dynamic_lineart == SANE_TRUE)
+ {
+ depth = 8;
+ }
+ else
+ {
+ /* XXX STEF XXX : why does the common layer never send depth=1 ? */
+ depth = 1;
+ }
+ }
+
+ /* compute distance to move */
+ move = 0;
+ /* XXX STEF XXX MD5345 -> optical_ydpi, other base_ydpi => half/full step ? */
+ if (split == SANE_FALSE)
+ {
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ if (ycorrection == SANE_TRUE)
+ {
+ move =
+ (SANE_UNFIX (dev->model->y_offset) *
+ dev->motor.optical_ydpi) / MM_PER_INCH;
+ }
+
+ /* add tl_y to base movement */
+ move += (settings.tl_y * dev->motor.optical_ydpi) / MM_PER_INCH;
+
+ }
+ else
+ {
+ move += (settings.tl_y * dev->motor.optical_ydpi) / MM_PER_INCH;
+ }
+
+ DBG (DBG_info, "setup_for_scan: move=%d steps\n", move);
+
+ /* security check */
+ if (move < 0)
+ {
+ DBG (DBG_error,
+ "setup_for_scan: overriding negative move value %d\n", move);
+ move = 0;
+ }
+ }
+ DBG (DBG_info, "setup_for_scan: move=%d steps\n", move);
+
+ /* pixels are allways given at full CCD optical resolution */
+ /* use detected left margin and fixed value */
+ if (xcorrection == SANE_TRUE)
+ {
+ if (dev->sensor.CCD_start_xoffset > 0)
+ startx = dev->sensor.CCD_start_xoffset;
+ else
+ startx = dev->sensor.dummy_pixel;
+ if (settings.scan_method == SCAN_METHOD_FLATBED)
+ {
+ startx +=
+ ((SANE_UNFIX (dev->model->x_offset) * dev->sensor.optical_res) /
+ MM_PER_INCH);
+ }
+ else
+ {
+ startx +=
+ ((SANE_UNFIX (dev->model->x_offset_ta) *
+ dev->sensor.optical_res) / MM_PER_INCH);
+ }
+ }
+ else
+ {
+ /* startx cannot be below dummy pixel value */
+ startx = dev->sensor.dummy_pixel;
+ }
+
+ /* add x coordinates : expressed in sensor max dpi */
+ startx += (settings.tl_x * dev->sensor.optical_res) / MM_PER_INCH;
+
+ /* stagger works with odd start cordinates */
+ if (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE)
+ startx |= 1;
+
+ pixels = (settings.pixels * dev->sensor.optical_res) / settings.xres;
+ /* special requirement for 400 dpi on 1200 dpi sensors */
+ if (settings.xres == 400)
+ {
+ pixels = (pixels / 6) * 6;
+ }
+ endx = startx + pixels;
+
+ /* TODO check for pixel width overflow */
+
+ /* set up correct values for scan (gamma and shading enabled) */
+ status = gl646_setup_registers (dev,
+ dev->reg,
+ settings,
+ dev->slope_table0,
+ dev->slope_table1,
+ settings.xres,
+ move,
+ settings.lines,
+ startx, endx, color,
+ depth);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "setup_for_scan: failed setup registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* now post-process values for register and options fine tuning */
+
+ /* select color filter based on settings */
+ dev->reg[reg_0x04].value &= ~REG04_FILTER;
+ if (channels == 1)
+ {
+ switch (settings.color_filter)
+ {
+ /* red */
+ case 0:
+ dev->reg[reg_0x04].value |= 0x04;
+ break;
+ /* green */
+ case 1:
+ dev->reg[reg_0x04].value |= 0x08;
+ break;
+ /* blue */
+ case 2:
+ dev->reg[reg_0x04].value |= 0x0c;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* send computed slope tables */
+ status =
+ gl646_send_slope_table (dev, 0, dev->slope_table0,
+ sanei_genesys_read_reg_from_set (dev->reg, 0x21));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "setup_for_scan: failed to send slope table 0: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ gl646_send_slope_table (dev, 1, dev->slope_table1,
+ sanei_genesys_read_reg_from_set (dev->reg, 0x6b));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "setup_for_scan: failed to send slope table 1: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "setup_for_scan: end\n");
+ return status;
+}
+
+/**
+ * this function sen gamm table to ASIC
+ */
+static SANE_Status
+gl646_send_gamma_table (Genesys_Device * dev)
+{
+ int size;
+ int address;
+ SANE_Status status;
+ uint8_t *gamma;
+ int bits;
+
+ DBGSTART;
+
+ /* gamma table size */
+ if (dev->reg[reg_0x05].value & REG05_GMMTYPE)
+ {
+ size = 16384;
+ bits = 14;
+ }
+ else
+ {
+ size = 4096;
+ bits = 12;
+ }
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ gamma = (uint8_t *) malloc (size * 2 * 3);
+ if (gamma==NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ RIE(sanei_genesys_generate_gamma_buffer(dev, bits, size-1, size, gamma));
+
+ /* table address */
+ switch (dev->reg[reg_0x05].value >> 6)
+ {
+ case 0: /* 600 dpi */
+ address = 0x09000;
+ break;
+ case 1: /* 1200 dpi */
+ address = 0x11000;
+ break;
+ case 2: /* 2400 dpi */
+ address = 0x20000;
+ break;
+ default:
+ free (gamma);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* send address */
+ status = sanei_genesys_set_buffer_address (dev, address);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "gl646_send_gamma_table: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send data */
+ status = gl646_bulk_write_data (dev, 0x3c, (uint8_t *) gamma, size * 2 * 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "gl646_send_gamma_table: failed to send gamma table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ free (gamma);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief this function does the led calibration.
+ * this function does the led calibration by scanning one line of the calibration
+ * area below scanner's top on white strip. The scope of this function is
+ * currently limited to the XP200
+ */
+static SANE_Status
+gl646_led_calibration (Genesys_Device * dev)
+{
+ int total_size;
+ uint8_t *line;
+ unsigned int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ unsigned int channels;
+ int avg[3], avga, avge;
+ int turn;
+ char fn[20];
+ uint16_t expr, expg, expb;
+ Genesys_Settings settings;
+ SANE_Int resolution;
+
+ SANE_Bool acceptable = SANE_FALSE;
+
+ DBG (DBG_proc, "gl646_led_calibration\n");
+ if (!dev->model->is_cis)
+ {
+ DBG (DBG_proc,
+ "gl646_led_calibration: not a cis scanner, nothing to do...\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* get led calibration resolution */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR)
+ {
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->sensor.optical_res,
+ SANE_TRUE);
+ settings.scan_mode = SCAN_MODE_COLOR;
+ channels = 3;
+ }
+ else
+ {
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->sensor.optical_res,
+ SANE_FALSE);
+ settings.scan_mode = SCAN_MODE_GRAY;
+ channels = 1;
+ }
+
+ /* offset calibration is always done in color mode */
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+ settings.lines = 1;
+ settings.depth = 16;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* colors * bytes_per_color * scan lines */
+ total_size = settings.pixels * channels * 2 * 1;
+
+ line = malloc (total_size);
+ if (!line)
+ {
+ DBG (DBG_error, "gl646_led_calibration: failed to allocate %d bytes\n",
+ total_size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+/*
+ we try to get equal bright leds here:
+
+ loop:
+ average per color
+ adjust exposure times
+
+ Sensor_Master uint8_t regs_0x10_0x15[6];
+ */
+
+ expr = (dev->sensor.regs_0x10_0x1d[0] << 8) | dev->sensor.regs_0x10_0x1d[1];
+ expg = (dev->sensor.regs_0x10_0x1d[2] << 8) | dev->sensor.regs_0x10_0x1d[3];
+ expb = (dev->sensor.regs_0x10_0x1d[4] << 8) | dev->sensor.regs_0x10_0x1d[5];
+
+ turn = 0;
+
+ do
+ {
+
+ dev->sensor.regs_0x10_0x1d[0] = (expr >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[1] = expr & 0xff;
+ dev->sensor.regs_0x10_0x1d[2] = (expg >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[3] = expg & 0xff;
+ dev->sensor.regs_0x10_0x1d[4] = (expb >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[5] = expb & 0xff;
+
+ DBG (DBG_info, "gl646_led_calibration: starting first line reading\n");
+
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, &line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(line);
+ DBG (DBG_error,
+ "gl646_led_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf (fn, 20, "led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file (fn,
+ line,
+ 16, channels, settings.pixels, 1);
+ }
+
+ acceptable = SANE_TRUE;
+
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < settings.pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * settings.pixels + 1] * 256 +
+ line[i * 2 + j * 2 * settings.pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= settings.pixels;
+ }
+
+ DBG (DBG_info, "gl646_led_calibration: average: "
+ "%d,%d,%d\n", avg[0], avg[1], avg[2]);
+
+ acceptable = SANE_TRUE;
+
+ if (!acceptable)
+ {
+ avga = (avg[0] + avg[1] + avg[2]) / 3;
+ expr = (expr * avga) / avg[0];
+ expg = (expg * avga) / avg[1];
+ expb = (expb * avga) / avg[2];
+
+ /* keep exposure time in a working window */
+ avge = (expr + expg + expb) / 3;
+ if (avge > 0x2000)
+ {
+ expr = (expr * 0x2000) / avge;
+ expg = (expg * 0x2000) / avge;
+ expb = (expb * 0x2000) / avge;
+ }
+ if (avge < 0x400)
+ {
+ expr = (expr * 0x400) / avge;
+ expg = (expg * 0x400) / avge;
+ expb = (expb * 0x400) / avge;
+ }
+ }
+
+ turn++;
+
+ }
+ while (!acceptable && turn < 100);
+
+ DBG (DBG_info,
+ "gl646_led_calibration: acceptable exposure: 0x%04x,0x%04x,0x%04x\n",
+ expr, expg, expb);
+
+ /* cleanup before return */
+ free (line);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * average dark pixels of a scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG (DBG_info, "dark_average: avg[%d] = %d\n", k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG (DBG_info, "dark_average: average = %d\n", average);
+ return average;
+}
+
+
+/** @brief calibration for AD frontend devices
+ * we do simple scan until all black_pixels are higher than 0,
+ * raising offset at each turn.
+ */
+static SANE_Status
+ad_fe_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t *line;
+ unsigned int channels;
+ char title[32];
+ int pass = 0;
+ SANE_Int resolution;
+ Genesys_Settings settings;
+ unsigned int x, y, adr, min;
+ unsigned int bottom, black_pixels;
+
+ DBG (DBG_proc, "ad_fe_offset_calibration: start\n");
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->sensor.optical_res,
+ SANE_TRUE);
+ channels = 3;
+ black_pixels =
+ (dev->sensor.black_pixels * resolution) / dev->sensor.optical_res;
+ DBG (DBG_io2, "ad_fe_offset_calibration: black_pixels=%d\n", black_pixels);
+
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_COLOR;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* scan first line of data with no gain */
+ dev->frontend.gain[0] = 0;
+ dev->frontend.gain[1] = 0;
+ dev->frontend.gain[2] = 0;
+
+ /* scan with no move */
+ bottom = 1;
+ do
+ {
+ pass++;
+ dev->frontend.offset[0] = bottom;
+ dev->frontend.offset[1] = bottom;
+ dev->frontend.offset[2] = bottom;
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, &line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(line);
+ DBG (DBG_error,
+ "ad_fe_offset_calibration: failed to scan first line\n");
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", (int)bottom);
+ sanei_genesys_write_pnm_file (title, line, 8, channels,
+ settings.pixels, settings.lines);
+ }
+
+ min = 0;
+ for (y = 0; y < settings.lines; y++)
+ {
+ for (x = 0; x < black_pixels; x++)
+ {
+ adr = (x + y * settings.pixels) * channels;
+ if (line[adr] > min)
+ min = line[adr];
+ if (line[adr + 1] > min)
+ min = line[adr + 1];
+ if (line[adr + 2] > min)
+ min = line[adr + 2];
+ }
+ }
+
+ free (line);
+ DBG (DBG_io2, "ad_fe_offset_calibration: pass=%d, min=%d\n", pass, min);
+ bottom++;
+ }
+ while (pass < 128 && min == 0);
+ if (pass == 128)
+ {
+ DBG (DBG_error,
+ "ad_fe_offset_calibration: failed to find correct offset\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (DBG_info, "ad_fe_offset_calibration: offset=(%d,%d,%d)\n",
+ dev->frontend.offset[0], dev->frontend.offset[1],
+ dev->frontend.offset[2]);
+ DBG (DBG_proc, "ad_fe_offset_calibration: end\n");
+ return status;
+}
+
+#define DARK_TARGET 8
+/**
+ * This function does the offset calibration by scanning one line of the calibration
+ * area below scanner's top. There is a black margin and the remaining is white.
+ * genesys_search_start() must have been called so that the offsets and margins
+ * are already known.
+ * @param dev scanner's device
+ * @return SANE_STATUS_GOOD if success, else error code is failure
+*/
+static SANE_Status
+gl646_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t *first_line, *second_line;
+ unsigned int channels;
+ char title[32];
+ int pass = 0, avg;
+ SANE_Int resolution;
+ Genesys_Settings settings;
+ int topavg, bottomavg;
+ int top, bottom, black_pixels;
+
+ /* Analog Device fronted have a different calibration */
+ if (dev->model->dac_type == DAC_AD_XP200)
+ {
+ return ad_fe_offset_calibration (dev);
+ }
+
+ DBG (DBG_proc, "gl646_offset_calibration: start\n");
+
+ /* setup for a RGB scan, one full sensor's width line */
+ /* resolution is the one from the final scan */
+ if (dev->settings.xres > dev->sensor.optical_res)
+ {
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->sensor.optical_res,
+ SANE_TRUE);
+ }
+ else
+ {
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->settings.xres,
+ SANE_TRUE);
+ }
+ channels = 3;
+ black_pixels =
+ (dev->sensor.black_pixels * resolution) / dev->sensor.optical_res;
+ DBG (DBG_io2, "gl646_offset_calibration: black_pixels=%d\n", black_pixels);
+
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_COLOR;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* scan first line of data with no gain, but with offset from
+ * last calibration */
+ dev->frontend.gain[0] = 0;
+ dev->frontend.gain[1] = 0;
+ dev->frontend.gain[2] = 0;
+
+ /* scan with no move */
+ bottom = 90;
+ dev->frontend.offset[0] = bottom;
+ dev->frontend.offset[1] = bottom;
+ dev->frontend.offset[2] = bottom;
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE,
+ &first_line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(first_line);
+ DBG (DBG_error,
+ "gl646_offset_calibration: failed to scan first line\n");
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", bottom);
+ sanei_genesys_write_pnm_file (title, first_line, 8, channels,
+ settings.pixels, settings.lines);
+ }
+ bottomavg =
+ dark_average (first_line, settings.pixels, settings.lines, channels,
+ black_pixels);
+ free (first_line);
+ DBG (DBG_io2, "gl646_offset_calibration: bottom avg=%d\n", bottomavg);
+
+ /* now top value */
+ top = 231;
+ dev->frontend.offset[0] = top;
+ dev->frontend.offset[1] = top;
+ dev->frontend.offset[2] = top;
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE,
+ &second_line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(second_line);
+ DBG (DBG_error,
+ "gl646_offset_calibration: failed to scan first line\n");
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", top);
+ sanei_genesys_write_pnm_file (title, second_line, 8, channels,
+ settings.pixels, settings.lines);
+ }
+ topavg =
+ dark_average (second_line, settings.pixels, settings.lines, channels,
+ black_pixels);
+ free (second_line);
+ DBG (DBG_io2, "gl646_offset_calibration: top avg=%d\n", topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.offset[0] = (top + bottom) / 2;
+ dev->frontend.offset[1] = (top + bottom) / 2;
+ dev->frontend.offset[2] = (top + bottom) / 2;
+
+ /* scan with no move */
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE,
+ &second_line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(second_line);
+ DBG (DBG_error,
+ "gl646_offset_calibration: failed to scan first line\n");
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", dev->frontend.offset[1]);
+ sanei_genesys_write_pnm_file (title, second_line, 8, channels,
+ settings.pixels, settings.lines);
+ }
+
+ avg =
+ dark_average (second_line, settings.pixels, settings.lines, channels,
+ black_pixels);
+ DBG (DBG_info, "gl646_offset_calibration: avg=%d offset=%d\n", avg,
+ dev->frontend.offset[1]);
+ free (second_line);
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.offset[1];
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.offset[1];
+ }
+ }
+
+ /* in case of debug do a final scan to get result */
+ if (DBG_LEVEL >= DBG_data)
+ {
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE,
+ &second_line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(second_line);
+ DBG (DBG_error,
+ "gl646_offset_calibration: failed to scan final line\n");
+ return status;
+ }
+ sanei_genesys_write_pnm_file ("offset-final.pnm", second_line, 8,
+ channels, settings.pixels,
+ settings.lines);
+ free (second_line);
+ }
+
+ DBG (DBG_info, "gl646_offset_calibration: offset=(%d,%d,%d)\n",
+ dev->frontend.offset[0], dev->frontend.offset[1],
+ dev->frontend.offset[2]);
+ DBG (DBG_proc, "gl646_offset_calibration: end\n");
+ return status;
+}
+
+/** @brief gain calibration for Analog Device frontends
+ * Alternative coarse gain calibration
+ */
+static SANE_Status
+ad_fe_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ uint8_t *line;
+ unsigned int i, channels, val;
+ unsigned int size, count, resolution, pass;
+ SANE_Status status = SANE_STATUS_GOOD;
+ float average;
+ Genesys_Settings settings;
+ char title[32];
+
+ DBG (DBG_proc, "ad_fe_coarse_gain_calibration: start\n");
+
+ /* setup for a RGB scan, one full sensor's width line */
+ /* resolution is the one from the final scan */
+ resolution = get_closest_resolution (dev->model->ccd_type, dpi, SANE_TRUE);
+ channels = 3;
+ settings.scan_mode = SCAN_MODE_COLOR;
+
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ size = channels * settings.pixels * settings.lines;
+
+ /* start gain value */
+ dev->frontend.gain[0] = 1;
+ dev->frontend.gain[1] = 1;
+ dev->frontend.gain[2] = 1;
+
+ average = 0;
+ pass = 0;
+
+ /* loop until each channel raises to acceptable level */
+ while ((average < dev->sensor.gain_white_ref) && (pass < 30))
+ {
+ /* scan with no move */
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, &line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(line);
+ DBG (DBG_error,
+ "ad_fe_coarse_gain_calibration: failed to scan first line\n");
+ return status;
+ }
+
+ /* log scanning data */
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "alternative_coarse%02d.pnm", (int)pass);
+ sanei_genesys_write_pnm_file (title, line, 8,
+ channels, settings.pixels,
+ settings.lines);
+ }
+ pass++;
+
+ /* computes white average */
+ average = 0;
+ count = 0;
+ for (i = 0; i < size; i++)
+ {
+ val = line[i];
+ average += val;
+ count++;
+ }
+ average = average / count;
+
+ /* adjusts gain for the channel */
+ if (average < dev->sensor.gain_white_ref)
+ dev->frontend.gain[0]++;
+ dev->frontend.gain[1] = dev->frontend.gain[0];
+ dev->frontend.gain[2] = dev->frontend.gain[0];
+
+ DBG (DBG_proc,
+ "ad_fe_coarse_gain_calibration: average = %.2f, gain = %d\n",
+ average, dev->frontend.gain[0]);
+ free (line);
+ }
+
+ DBG (DBG_info, "ad_fe_coarse_gain_calibration: gains=(%d,%d,%d)\n",
+ dev->frontend.gain[0], dev->frontend.gain[1], dev->frontend.gain[2]);
+ DBG (DBG_proc, "ad_fe_coarse_gain_calibration: end\n");
+ return status;
+}
+
+/**
+ * Alternative coarse gain calibration
+ * this on uses the settings from offset_calibration. First scan moves so
+ * we can go to calibration area for XPA.
+ * @param dev device for scan
+ * @param dpi resolutnio to calibrate at
+ */
+static SANE_Status
+gl646_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ uint8_t *line;
+ unsigned int i, j, k, channels, val, maximum, idx;
+ unsigned int count, resolution, pass;
+ SANE_Status status = SANE_STATUS_GOOD;
+ float average[3];
+ Genesys_Settings settings;
+ char title[32];
+
+ if (dev->model->ccd_type == CIS_XP200)
+ {
+ return ad_fe_coarse_gain_calibration (dev, dev->sensor.optical_res);
+ }
+ DBG (DBG_proc, "gl646_coarse_gain_calibration: start\n");
+
+ /* setup for a RGB scan, one full sensor's width line */
+ /* resolution is the one from the final scan */
+ channels = 3;
+
+ /* we are searching a sensor resolution */
+ if (dpi > dev->sensor.optical_res)
+ {
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->sensor.optical_res,
+ SANE_TRUE);
+ }
+ else
+ {
+ resolution =
+ get_closest_resolution (dev->model->ccd_type, dev->settings.xres,
+ SANE_TRUE);
+ }
+
+ settings.scan_method = dev->settings.scan_method;
+ settings.scan_mode = SCAN_MODE_COLOR;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_y = 0;
+ if (settings.scan_method == SCAN_METHOD_FLATBED)
+ {
+ settings.tl_x = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+ }
+ else
+ {
+ settings.tl_x = SANE_UNFIX (dev->model->x_offset_ta);
+ settings.pixels =
+ (SANE_UNFIX (dev->model->x_size_ta) * resolution) / MM_PER_INCH;
+ }
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* start gain value */
+ dev->frontend.gain[0] = 1;
+ dev->frontend.gain[1] = 1;
+ dev->frontend.gain[2] = 1;
+
+ if (channels > 1)
+ {
+ average[0] = 0;
+ average[1] = 0;
+ average[2] = 0;
+ idx = 0;
+ }
+ else
+ {
+ average[0] = 255;
+ average[1] = 255;
+ average[2] = 255;
+ idx = dev->settings.color_filter;
+ average[idx] = 0;
+ }
+ pass = 0;
+
+ /* loop until each channel raises to acceptable level */
+ while (((average[0] < dev->sensor.gain_white_ref)
+ || (average[1] < dev->sensor.gain_white_ref)
+ || (average[2] < dev->sensor.gain_white_ref)) && (pass < 30))
+ {
+ /* scan with no move */
+ status =
+ simple_scan (dev, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, &line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(line);
+ DBG (DBG_error,
+ "gl646_coarse_gain_calibration: failed to scan first line\n");
+ return status;
+ }
+
+ /* log scanning data */
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "coarse_gain%02d.pnm", (int)pass);
+ sanei_genesys_write_pnm_file (title, line, 8,
+ channels, settings.pixels,
+ settings.lines);
+ }
+ pass++;
+
+ /* average high level for each channel and compute gain
+ to reach the target code
+ we only use the central half of the CCD data */
+ for (k = idx; k < idx + channels; k++)
+ {
+ /* we find the maximum white value, so we can deduce a threshold
+ to average white values */
+ maximum = 0;
+ for (i = 0; i < settings.lines; i++)
+ {
+ for (j = 0; j < settings.pixels; j++)
+ {
+ val = line[i * channels * settings.pixels + j + k];
+ if (val > maximum)
+ maximum = val;
+ }
+ }
+
+ /* threshold */
+ maximum *= 0.9;
+
+ /* computes white average */
+ average[k] = 0;
+ count = 0;
+ for (i = 0; i < settings.lines; i++)
+ {
+ for (j = 0; j < settings.pixels; j++)
+ {
+ /* averaging only white points allow us not to care about dark margins */
+ val = line[i * channels * settings.pixels + j + k];
+ if (val > maximum)
+ {
+ average[k] += val;
+ count++;
+ }
+ }
+ }
+ average[k] = average[k] / count;
+
+ /* adjusts gain for the channel */
+ if (average[k] < dev->sensor.gain_white_ref)
+ dev->frontend.gain[k]++;
+
+ DBG (DBG_proc,
+ "gl646_coarse_gain_calibration: channel %d, average = %.2f, gain = %d\n",
+ k, average[k], dev->frontend.gain[k]);
+ }
+ free (line);
+ }
+
+ if (channels < 3)
+ {
+ dev->frontend.gain[1] = dev->frontend.gain[0];
+ dev->frontend.gain[2] = dev->frontend.gain[0];
+ }
+
+ DBG (DBG_info, "gl646_coarse_gain_calibration: gains=(%d,%d,%d)\n",
+ dev->frontend.gain[0], dev->frontend.gain[1], dev->frontend.gain[2]);
+ DBG (DBG_proc, "gl646_coarse_gain_calibration: end\n");
+ return status;
+}
+
+/**
+ * sets up the scanner's register for warming up. We scan 2 lines without moving.
+ *
+ */
+static SANE_Status
+gl646_init_regs_for_warmup (Genesys_Device * dev,
+ Genesys_Register_Set * local_reg,
+ int *channels, int *total_size)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Settings settings;
+ int resolution, lines;
+
+ DBG (DBG_proc, "gl646_init_regs_for_warmup: start\n");
+
+ sanei_genesys_init_fe (dev);
+
+ resolution = get_closest_resolution (dev->model->ccd_type, 300, SANE_FALSE);
+
+ /* set up for a half width 2 lines gray scan without moving */
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_GRAY;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+ settings.lines = 2;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* setup for scan */
+ status = setup_for_scan (dev, settings, SANE_TRUE, SANE_FALSE, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_init_regs_for_warmup: setup_for_scan failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* we are not going to move, so clear these bits */
+ dev->reg[reg_0x02].value &= ~(REG02_FASTFED | REG02_AGOHOME);
+
+ /* don't enable any correction for this scan */
+ dev->reg[reg_0x01].value &= ~REG01_DVDSET;
+ /* XXX STEF XXX
+ dev->reg[reg_0x05].value &= ~REG05_GMMENB; */
+
+ /* copy to local_reg */
+ memcpy (local_reg, dev->reg, (GENESYS_GL646_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+
+ /* turn off motor during this scan */
+ gl646_set_motor_power (local_reg, SANE_FALSE);
+
+ /* returned value to higher level warmup function */
+ *channels = 1;
+ lines = gl646_get_triple_reg (local_reg, REG_LINCNT) + 1;
+ *total_size = lines * settings.pixels;
+
+ /* now registers are ok, write them to scanner */
+ RIE (gl646_set_fe (dev, AFE_SET, settings.xres));
+ RIE (gl646_bulk_write_register (dev, local_reg, GENESYS_GL646_MAX_REGS));
+
+ DBG (DBG_proc, "gl646_init_regs_for_warmup: end\n");
+ return status;
+}
+
+
+/*
+ * this function moves head without scanning, forward, then backward
+ * so that the head goes to park position.
+ * as a by-product, also check for lock
+ */
+static SANE_Status
+gl646_repark_head (Genesys_Device * dev)
+{
+ SANE_Status status;
+ Genesys_Settings settings;
+ unsigned int expected, steps;
+
+ DBG (DBG_proc, "gl646_repark_head: start\n");
+
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_COLOR;
+ settings.xres =
+ get_closest_resolution (dev->model->ccd_type, 75, SANE_FALSE);
+ settings.yres = settings.xres;
+ settings.tl_x = 0;
+ settings.tl_y = 5;
+ settings.pixels = 600;
+ settings.lines = 4;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ status = setup_for_scan (dev, settings, SANE_FALSE, SANE_FALSE, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_repark_head: failed to setup for scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* TODO seems wrong ... no effective scan */
+ dev->reg[reg_0x01].value &= ~REG01_SCAN;
+
+ status = gl646_bulk_write_register (dev, dev->reg, GENESYS_GL646_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_repark_head: failed to send registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* start scan */
+ status = gl646_begin_scan (dev, dev->reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_repark_head: failed to begin scan: \n");
+ return status;
+ }
+
+ expected = gl646_get_triple_reg (dev->reg, REG_FEEDL);
+ do
+ {
+ usleep (100 * 1000);
+ status = sanei_genesys_read_feed_steps (dev, &steps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_repark_head: failed to read feed steps: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ while (steps < expected);
+
+ /* toggle motor flag, put an huge step number and redo move backward */
+ status = gl646_slow_back_home (dev, 1);
+ DBG (DBG_proc, "gl646_repark_head: end\n");
+ return status;
+}
+
+/* *
+ * initialize ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ * @param dev device description of the scanner to initailize
+ * @return SANE_STATUS_GOOD if success, error code if failure
+ */
+static SANE_Status
+gl646_init (Genesys_Device * dev)
+{
+ SANE_Status status;
+ struct timeval tv;
+ uint8_t cold = 0, val = 0;
+ uint32_t addr = 0xdead;
+ int size, i;
+ size_t len;
+
+ DBG_INIT ();
+ DBG (DBG_proc, "gl646_init: start\n");
+
+ /* to detect real power up condition, we write to REG41
+ * with pwrbit set, then read it back. When scanner is cold (just replugged)
+ * PWRBIT will be set in the returned value
+ */
+ RIE (sanei_genesys_get_status (dev, &cold));
+ DBG (DBG_info, "gl646_init: status=0x%02x\n", cold);
+ cold = !(cold & REG41_PWRBIT);
+ if (cold)
+ {
+ DBG (DBG_info, "gl646_init: device is cold\n");
+ }
+ else
+ {
+ DBG (DBG_info, "gl646_init: device is hot\n");
+ }
+
+ /* if scanning session hasn't been initialized, set it up */
+ if (!dev->already_initialized)
+ {
+ dev->dark_average_data = NULL;
+ dev->white_average_data = NULL;
+
+ dev->settings.color_filter = 1; /* green filter by default */
+ gettimeofday (&tv, NULL);
+ dev->init_date = tv.tv_sec;
+
+ switch (dev->model->motor_type)
+ {
+ /* set to 11111 to spot bugs, sanei_genesys_exposure_time should
+ have obsoleted this field */
+ case MOTOR_5345:
+ dev->settings.exposure_time = 11111;
+ break;
+
+ case MOTOR_ST24:
+ dev->settings.exposure_time = 11000;
+ break;
+ default:
+ dev->settings.exposure_time = 11000;
+ break;
+ }
+
+ /* Set default values for registers */
+ gl646_init_regs (dev);
+
+ /* build default gamma tables */
+ if (dev->reg[reg_0x05].value & REG05_GMMTYPE)
+ size = 16384;
+ else
+ size = 4096;
+
+ for(i=0;i<3;i++)
+ {
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ dev->sensor.gamma_table[i] = (uint16_t *) malloc (2 * size);
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ DBG (DBG_error, "gl646_init: could not allocate memory for gamma table %d\n",i);
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_genesys_create_gamma_table (dev->sensor.gamma_table[i],
+ size,
+ size - 1,
+ size - 1,
+ dev->sensor.gamma[i]);
+ }
+ }
+
+ /* Init shading data */
+ RIE (sanei_genesys_init_shading_data (dev, dev->sensor.sensor_pixels));
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg,
+ (GENESYS_GL646_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+ }
+
+ /* execute physical unit init only if cold */
+ if (cold)
+ {
+ DBG (DBG_info, "gl646_init: device is cold\n");
+ val = 0x04;
+ RIE (sanei_usb_control_msg
+ (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_INIT,
+ INDEX, 1, &val));
+
+ /* ASIC reset */
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
+ usleep (100000UL); /* sleep 100 ms */
+
+ /* Write initial registers */
+ RIE (gl646_bulk_write_register (dev, dev->reg, GENESYS_GL646_MAX_REGS));
+
+ /* Test ASIC and RAM */
+ if (!(dev->model->flags & GENESYS_FLAG_LAZY_INIT))
+ {
+ RIE (gl646_asic_test (dev));
+ }
+
+ /* send gamma tables if needed */
+ status = gl646_send_gamma_table (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_init: failed to send generic gamma tables: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Set powersaving (default = 15 minutes) */
+ RIE (gl646_set_powersaving (dev, 15));
+ } /* end if cold */
+
+ /* Set analog frontend */
+ RIE (gl646_set_fe (dev, AFE_INIT, 0));
+
+ /* GPO enabling for XP200 */
+ if (dev->model->ccd_type == CIS_XP200)
+ {
+ sanei_genesys_write_register (dev, 0x68, dev->gpo.enable[0]);
+ sanei_genesys_write_register (dev, 0x69, dev->gpo.enable[1]);
+
+ /* enable GPIO */
+ val = 6;
+ status = gl646_gpio_output_enable (dev->dn, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_init: GPO enable failed ... %s\n",
+ sane_strstatus (status));
+ }
+ val = 0;
+
+ /* writes 0 to GPIO */
+ status = gl646_gpio_write (dev->dn, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_init: GPO write failed ... %s\n",
+ sane_strstatus (status));
+ }
+
+ /* clear GPIO enable */
+ status = gl646_gpio_output_enable (dev->dn, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_init: GPO disable failed ... %s\n",
+ sane_strstatus (status));
+ }
+ sanei_genesys_write_register (dev, 0x66, 0x10);
+ sanei_genesys_write_register (dev, 0x66, 0x00);
+ sanei_genesys_write_register (dev, 0x66, 0x10);
+ }
+
+ /* MD6471/G2410 and XP200 read/write data from an undocumented memory area which
+ * is after the second slope table */
+ if (dev->model->gpo_type != GPO_HP3670
+ && dev->model->gpo_type != GPO_HP2400)
+ {
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ addr = 0x08200;
+ break;
+ case 1200:
+ addr = 0x10200;
+ break;
+ case 2400:
+ addr = 0x1fa00;
+ break;
+ }
+ status = sanei_genesys_set_buffer_address (dev, addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_init: failed to set up control address\n");
+ return SANE_STATUS_INVAL;
+ }
+ sanei_usb_set_timeout (2 * 1000);
+ len = 6;
+ status = gl646_bulk_read_data (dev, 0x45, dev->control, len);
+ /* for some reason, read fails here for MD6471, HP2300 and XP200
+ * one time out of 2 scanimage launches
+ */
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "gl646_init: failed to read control\n");
+ status = gl646_bulk_read_data (dev, 0x45, dev->control, len);
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "gl646_init: failed to read control\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ DBG (DBG_info,
+ "gl646_init: control read=0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ dev->control[0], dev->control[1], dev->control[2],
+ dev->control[3], dev->control[4], dev->control[5]);
+ }
+ sanei_usb_set_timeout (30 * 1000);
+ }
+ else
+ /* HP2400 and HP3670 case */
+ {
+ dev->control[0] = 0x00;
+ dev->control[1] = 0x00;
+ dev->control[2] = 0x01;
+ dev->control[3] = 0x00;
+ dev->control[4] = 0x00;
+ dev->control[5] = 0x00;
+ }
+
+ /* ensure head is correctly parked, and check lock */
+ if (dev->model->is_sheetfed == SANE_FALSE)
+ {
+ if (dev->model->flags & GENESYS_FLAG_REPARK)
+ {
+ status = gl646_repark_head (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_INVAL)
+ {
+ DBG (DBG_error0,
+ "Your scanner is locked. Please move the lock switch "
+ "to the unlocked position\n");
+#ifdef SANE_STATUS_HW_LOCKED
+ return SANE_STATUS_HW_LOCKED;
+#else
+ return SANE_STATUS_JAMMED;
+#endif
+ }
+ else
+ DBG (DBG_error,
+ "gl646_init: gl646_repark_head failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else
+ {
+ RIE (gl646_slow_back_home (dev, SANE_TRUE));
+ }
+ }
+
+ /* here session and device are initialized */
+ dev->already_initialized = SANE_TRUE;
+
+ DBG (DBG_proc, "gl646_init: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl646_move_to_ta (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "gl646_move_to_ta: starting\n");
+ if (simple_move (dev, SANE_UNFIX (dev->model->y_offset_calib_ta)) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_move_to_ta: failed to move to calibration area\n");
+ return status;
+ }
+ DBG (DBG_proc, "gl646_move_to_ta: end\n");
+
+ return status;
+}
+
+
+/**
+ * Does a simple scan: ie no line reordering and avanced data buffering and
+ * shading correction. Memory for data is allocated in this function
+ * and must be freed by caller.
+ * @param dev device of the scanner
+ * @param settings parameters of the scan
+ * @param move SANE_TRUE if moving during scan
+ * @param forward SANE_TRUE if moving forward during scan
+ * @param shading SANE_TRUE to enable shading correction
+ * @param data pointer for the data
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+simple_scan (Genesys_Device * dev, Genesys_Settings settings, SANE_Bool move,
+ SANE_Bool forward, SANE_Bool shading, unsigned char **data)
+{
+ SANE_Status status = SANE_STATUS_INVAL;
+ unsigned int size, lines, x, y, bpp;
+ SANE_Bool empty, split;
+ unsigned char *buffer;
+ int count;
+ uint8_t val;
+
+ DBG (DBG_proc, "simple_scan: starting\n");
+ DBG (DBG_io, "simple_scan: move=%d, forward=%d, shading=%d\n", move,
+ forward, shading);
+
+ /* round up to multiple of 3 in case of CIS scanner */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ settings.lines = ((settings.lines + 2) / 3) * 3;
+ }
+
+ /* setup for move then scan */
+ if (move == SANE_TRUE && settings.tl_y > 0)
+ {
+ split = SANE_FALSE;
+ }
+ else
+ {
+ split = SANE_TRUE;
+ }
+ status = setup_for_scan (dev, settings, split, SANE_FALSE, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "simple_scan: setup_for_scan failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* allocate memory fo scan : LINCNT may have been adjusted for CCD reordering */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ lines = gl646_get_triple_reg (dev->reg, REG_LINCNT) / 3;
+ }
+ else
+ {
+ lines = gl646_get_triple_reg (dev->reg, REG_LINCNT) + 1;
+ }
+ size = lines * settings.pixels;
+ if (settings.depth == 16)
+ bpp = 2;
+ else
+ bpp = 1;
+ size *= bpp;
+ if (settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ size *= 3;
+ *data = malloc (size);
+ if (!*data)
+ {
+ DBG (DBG_error,
+ "simple_scan: failed to allocate %d bytes of memory\n", size);
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (DBG_io, "simple_scan: allocated %d bytes of memory for %d lines\n",
+ size, lines);
+
+ /* put back real line number in settings */
+ settings.lines = lines;
+
+ /* initialize frontend */
+ status = gl646_set_fe (dev, AFE_SET, settings.xres);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (*data);
+ DBG (DBG_error,
+ "simple_scan: failed to set frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* no shading correction and not watch dog for simple scan */
+ dev->reg[reg_0x01].value &= ~(REG01_DVDSET | REG01_DOGENB);
+ if (shading == SANE_TRUE)
+ {
+ dev->reg[reg_0x01].value |= REG01_DVDSET;
+ }
+
+ /* one table movement for simple scan */
+ dev->reg[reg_0x02].value &= ~REG02_FASTFED;
+
+ if (move == SANE_FALSE)
+ {
+ /* clear motor power flag if no move */
+ dev->reg[reg_0x02].value &= ~REG02_MTRPWR;
+
+ /* no automatic go home if no movement */
+ dev->reg[reg_0x02].value &= ~REG02_AGOHOME;
+ }
+ if (forward == SANE_FALSE)
+ {
+ dev->reg[reg_0x02].value |= REG02_MTRREV;
+ }
+ else
+ {
+ dev->reg[reg_0x02].value &= ~REG02_MTRREV;
+ }
+
+ /* no automatic go home when using XPA */
+ if (settings.scan_method == SCAN_METHOD_TRANSPARENCY)
+ {
+ dev->reg[reg_0x02].value &= ~REG02_AGOHOME;
+ }
+
+ /* write scan registers */
+ status = gl646_bulk_write_register (dev, dev->reg,
+ sizeof (dev->reg) /
+ sizeof (dev->reg[0]));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "simple_scan: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ free (*data);
+ return status;
+ }
+
+ /* starts scan */
+ status = gl646_begin_scan (dev, dev->reg, move);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (*data);
+ DBG (DBG_error, "simple_scan: failed to begin scan: \n");
+ return status;
+ }
+
+ /* wait for buffers to be filled */
+ count = 0;
+ do
+ {
+ usleep (10000UL);
+ RIE (sanei_genesys_get_status (dev, &val));
+ if (DBG_LEVEL > DBG_info)
+ {
+ print_status (val);
+ }
+ RIE (sanei_genesys_test_buffer_empty (dev, &empty));
+ count++;
+ }
+ while (empty && count < 1000);
+ if (count == 1000)
+ {
+ free (*data);
+ DBG (DBG_error, "simple_scan: failed toread data\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, *data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (*data);
+ DBG (DBG_error,
+ "simple_scan: failed to read data: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ /* in case of CIS scanner, we must reorder data */
+ if (dev->model->is_cis == SANE_TRUE
+ && settings.scan_mode == SCAN_MODE_COLOR)
+ {
+ /* alloc one line sized working buffer */
+ buffer = (unsigned char *) malloc (settings.pixels * 3 * bpp);
+ if (buffer == NULL)
+ {
+ DBG (DBG_error,
+ "simple_scan: failed to allocate %d bytes of memory\n",
+ settings.pixels * 3);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* reorder one line of data and put it back to buffer */
+ if (bpp == 1)
+ {
+ for (y = 0; y < lines; y++)
+ {
+ /* reorder line */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ buffer[x * 3] = (*data)[y * settings.pixels * 3 + x];
+ buffer[x * 3 + 1] =
+ (*data)[y * settings.pixels * 3 + settings.pixels + x];
+ buffer[x * 3 + 2] =
+ (*data)[y * settings.pixels * 3 + 2 * settings.pixels +
+ x];
+ }
+ /* copy line back */
+ memcpy ((*data) + settings.pixels * 3 * y, buffer,
+ settings.pixels * 3);
+ }
+ }
+ else
+ {
+ for (y = 0; y < lines; y++)
+ {
+ /* reorder line */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ buffer[x * 6] = (*data)[y * settings.pixels * 6 + x * 2];
+ buffer[x * 6 + 1] =
+ (*data)[y * settings.pixels * 6 + x * 2 + 1];
+ buffer[x * 6 + 2] =
+ (*data)[y * settings.pixels * 6 + 2 * settings.pixels +
+ x * 2];
+ buffer[x * 6 + 3] =
+ (*data)[y * settings.pixels * 6 + 2 * settings.pixels +
+ x * 2 + 1];
+ buffer[x * 6 + 4] =
+ (*data)[y * settings.pixels * 6 + 4 * settings.pixels +
+ x * 2];
+ buffer[x * 6 + 5] =
+ (*data)[y * settings.pixels * 6 + 4 * settings.pixels +
+ x * 2 + 1];
+ }
+ /* copy line back */
+ memcpy ((*data) + settings.pixels * 6 * y, buffer,
+ settings.pixels * 6);
+ }
+ }
+ free (buffer);
+ }
+
+ /* end scan , waiting the motor to stop if needed (if moving), but without ejecting doc */
+ status = end_scan (dev, dev->reg, SANE_TRUE, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (*data);
+ DBG (DBG_error,
+ "simple_scan: failed to end scan: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "simple_scan: end\n");
+ return status;
+}
+
+/**
+ * Does a simple move of the given distance by doing a scan at lowest resolution
+ * shading correction. Memory for data is allocated in this function
+ * and must be freed by caller.
+ * @param dev device of the scanner
+ * @param distance distance to move in MM
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+simple_move (Genesys_Device * dev, SANE_Int distance)
+{
+ SANE_Status status;
+ unsigned char *data = NULL;
+ Genesys_Settings settings;
+
+ DBG (DBG_proc, "simple_move: %d mm\n", distance);
+
+ /* TODO give a no AGOHOME flag */
+ settings.scan_method = SCAN_METHOD_TRANSPARENCY;
+ settings.scan_mode = SCAN_MODE_COLOR;
+ settings.xres = get_lowest_resolution (dev->model->ccd_type, SANE_TRUE);
+ settings.yres = settings.xres;
+ settings.tl_y = 0;
+ settings.tl_x = 0;
+ settings.pixels =
+ (dev->sensor.sensor_pixels * settings.xres) / dev->sensor.optical_res;
+ settings.lines = (distance * settings.xres) / MM_PER_INCH;
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ status = simple_scan (dev, settings, SANE_TRUE, SANE_TRUE, SANE_FALSE, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "simple_move: simple_scan failed\n");
+ }
+
+ free (data);
+ DBGCOMPLETED
+ return status;
+}
+
+/**
+ * update the status of the required sensor in the scanner session
+ * the last_val fileds are used to make events 'sticky'
+ */
+static SANE_Status
+gl646_update_hardware_sensors (Genesys_Scanner * session)
+{
+ Genesys_Device *dev = session->dev;
+ uint8_t value;
+ SANE_Status status;
+
+ /* do what is needed to get a new set of events, but try to not loose
+ any of them.
+ */
+ status = gl646_gpio_read (dev->dn, &value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl646_update_hardware_sensors: failed to read GPIO %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_io, "gl646_update_hardware_sensors: GPIO=0x%02x\n", value);
+
+ /* scan button */
+ if ((dev->model->buttons & GENESYS_HAS_SCAN_SW)
+ && session->val[OPT_SCAN_SW].b == session->last_val[OPT_SCAN_SW].b)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_XP200:
+ session->val[OPT_SCAN_SW].b = ((value & 0x02) != 0);
+ break;
+ case GPO_5345:
+ session->val[OPT_SCAN_SW].b = (value == 0x16);
+ break;
+ case GPO_HP2300:
+ session->val[OPT_SCAN_SW].b = (value == 0x6c);
+ break;
+ case GPO_HP3670:
+ case GPO_HP2400:
+ session->val[OPT_SCAN_SW].b = ((value & 0x20) == 0);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* email button */
+ if ((dev->model->buttons & GENESYS_HAS_EMAIL_SW)
+ && session->val[OPT_EMAIL_SW].b == session->last_val[OPT_EMAIL_SW].b)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_5345:
+ session->val[OPT_EMAIL_SW].b = (value == 0x12);
+ break;
+ case GPO_HP3670:
+ case GPO_HP2400:
+ session->val[OPT_EMAIL_SW].b = ((value & 0x08) == 0);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* copy button */
+ if ((dev->model->buttons & GENESYS_HAS_COPY_SW)
+ && session->val[OPT_COPY_SW].b == session->last_val[OPT_COPY_SW].b)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_5345:
+ session->val[OPT_COPY_SW].b = (value == 0x11);
+ break;
+ case GPO_HP2300:
+ session->val[OPT_COPY_SW].b = (value == 0x5c);
+ break;
+ case GPO_HP3670:
+ case GPO_HP2400:
+ session->val[OPT_COPY_SW].b = ((value & 0x10) == 0);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* power button */
+ if ((dev->model->buttons & GENESYS_HAS_POWER_SW)
+ && session->val[OPT_POWER_SW].b == session->last_val[OPT_POWER_SW].b)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_5345:
+ session->val[OPT_POWER_SW].b = (value == 0x14);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* ocr button */
+ if ((dev->model->buttons & GENESYS_HAS_OCR_SW)
+ && session->val[OPT_OCR_SW].b == session->last_val[OPT_OCR_SW].b)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_5345:
+ session->val[OPT_OCR_SW].b = (value == 0x13);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* document detection */
+ if ((dev->model->buttons & GENESYS_HAS_PAGE_LOADED_SW)
+ && session->val[OPT_PAGE_LOADED_SW].b ==
+ session->last_val[OPT_PAGE_LOADED_SW].b)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_XP200:
+ session->val[OPT_PAGE_LOADED_SW].b = ((value & 0x04) != 0);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* XPA detection */
+ if (dev->model->flags & GENESYS_FLAG_XPA)
+ {
+ switch (dev->model->gpo_type)
+ {
+ case GPO_HP3670:
+ case GPO_HP2400:
+ /* test if XPA is plugged-in */
+ if ((value & 0x40) == 0)
+ {
+ DBG (DBG_io, "gl646_update_hardware_sensors: enabling XPA\n");
+ session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ DBG (DBG_io, "gl646_update_hardware_sensors: disabling XPA\n");
+ session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ }
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ return status;
+}
+
+
+static SANE_Status
+write_control (Genesys_Device * dev, int resolution)
+{
+ SANE_Status status;
+ uint8_t control[4];
+ uint32_t addr = 0xdead;
+
+ /* 2300 does not write to 'control' */
+ if (dev->model->motor_type == MOTOR_HP2300)
+ return SANE_STATUS_GOOD;
+
+ /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which
+ * is after the second slope table */
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ addr = 0x08200;
+ break;
+ case 1200:
+ addr = 0x10200;
+ break;
+ case 2400:
+ addr = 0x1fa00;
+ break;
+ default:
+ DBG (DBG_error, "write_control: failed to compute control address\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* XP200 sets dpi, what other scanner put is unknown yet */
+ switch (dev->model->motor_type)
+ {
+ case MOTOR_XP200:
+ /* we put scan's dpi, not motor one */
+ control[0] = LOBYTE (resolution);
+ control[1] = HIBYTE (resolution);
+ control[2] = dev->control[4];
+ control[3] = dev->control[5];
+ break;
+ case MOTOR_HP3670:
+ case MOTOR_HP2400:
+ case MOTOR_5345:
+ default:
+ control[0] = dev->control[2];
+ control[1] = dev->control[3];
+ control[2] = dev->control[4];
+ control[3] = dev->control[5];
+ break;
+ }
+
+ DBG (DBG_info,
+ "write_control: control write=0x%02x 0x%02x 0x%02x 0x%02x\n",
+ control[0], control[1], control[2], control[3]);
+ status = sanei_genesys_set_buffer_address (dev, addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "write_control: failed to set up control address\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = gl646_bulk_write_data (dev, 0x3c, control, 4);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "write_control: failed to set up control\n");
+ return SANE_STATUS_INVAL;
+ }
+ return status;
+}
+
+/**
+ * check if a stored calibration is compatible with requested scan.
+ * @return SANE_STATUS_GOOD if compatible, SANE_STATUS_UNSUPPORTED if not.
+ * Whenever an error is met, it is returned.
+ * @param dev scanner device
+ * @param cache cache entry to test
+ * @param for_overwrite reserved for future use ...
+ */
+static SANE_Status
+gl646_is_compatible_calibration (Genesys_Device * dev,
+ Genesys_Calibration_Cache * cache,
+ int for_overwrite)
+{
+#ifdef HAVE_SYS_TIME_H
+ struct timeval time;
+#endif
+ int compatible = 1;
+
+ DBG (DBG_proc,
+ "gl646_is_compatible_calibration: start (for_overwrite=%d)\n",
+ for_overwrite);
+
+ if (cache == NULL)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* build minimal current_setup for calibration cache use only, it will be better
+ * computed when during setup for scan
+ */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR)
+ {
+ dev->current_setup.channels = 3;
+ }
+ else
+ {
+ dev->current_setup.channels = 1;
+ }
+ dev->current_setup.xres = dev->settings.xres;
+ dev->current_setup.scan_method = dev->settings.scan_method;
+
+ DBG (DBG_io,
+ "gl646_is_compatible_calibration: requested=(%d,%f), tested=(%d,%f)\n",
+ dev->current_setup.channels, dev->current_setup.xres,
+ cache->used_setup.channels, cache->used_setup.xres);
+
+ /* a calibration cache is compatible if color mode and x dpi match the user
+ * requested scan. In the case of CIS scanners, dpi isn't a criteria */
+ if (dev->model->is_cis == SANE_FALSE)
+ {
+ compatible =
+ ((dev->current_setup.channels == cache->used_setup.channels)
+ && (((int) dev->current_setup.xres) ==
+ ((int) cache->used_setup.xres)));
+ }
+ else
+ {
+ compatible =
+ (dev->current_setup.channels == cache->used_setup.channels);
+ }
+ if (dev->current_setup.scan_method != cache->used_setup.scan_method)
+ {
+ DBG (DBG_io,
+ "gl646_is_compatible_calibration: current method=%d, used=%d\n",
+ dev->current_setup.scan_method, cache->used_setup.scan_method);
+ compatible = 0;
+ }
+ if (!compatible)
+ {
+ DBG (DBG_proc,
+ "gl646_is_compatible_calibration: completed, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* a cache entry expires after 30 minutes for non sheetfed scanners */
+ /* this is not taken into account when overwriting cache entries */
+#ifdef HAVE_SYS_TIME_H
+ if(for_overwrite == SANE_FALSE)
+ {
+ gettimeofday (&time, NULL);
+ if ((time.tv_sec - cache->last_calibration > 30 * 60)
+ && (dev->model->is_sheetfed == SANE_FALSE))
+ {
+ DBG (DBG_proc,
+ "gl646_is_compatible_calibration: expired entry, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+#endif
+
+ DBG (DBG_proc,
+ "gl646_is_compatible_calibration: completed, cache compatible\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * search for a full width black or white strip.
+ * @param dev scanner device
+ * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
+ * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
+ * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
+ */
+static SANE_Status
+gl646_search_strip (Genesys_Device * dev, SANE_Bool forward, SANE_Bool black)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool half_ccd = SANE_FALSE;
+ Genesys_Settings settings;
+ int res = get_closest_resolution (dev->model->ccd_type, 75, SANE_FALSE);
+ unsigned char *data = NULL;
+ unsigned int pass, count, found, x, y;
+ char title[80];
+
+ DBG (DBG_proc, "gl646_search_strip: start\n");
+ /* adapt to half_ccd case */
+ if (dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE)
+ {
+ /* walk the master mode list to find if half_ccd */
+ if (is_half_ccd (dev->model->ccd_type, res, SANE_TRUE) == SANE_TRUE)
+ {
+ half_ccd = SANE_TRUE;
+ }
+ }
+
+ /* we set up for a lowest available resolution color grey scan, full width */
+ settings.scan_method = SCAN_METHOD_FLATBED;
+ settings.scan_mode = SCAN_MODE_GRAY;
+ settings.xres = res;
+ settings.yres = res;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (SANE_UNFIX (dev->model->x_size) * res) / MM_PER_INCH;
+ if (half_ccd == SANE_TRUE)
+ {
+ settings.pixels /= 2;
+ }
+
+ /* 15 mm at at time */
+ settings.lines = (15 * settings.yres) / MM_PER_INCH; /* may become a parameter from genesys_devices.c */
+ settings.depth = 8;
+ settings.color_filter = 0;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+ settings.exposure_time = 0;
+ settings.dynamic_lineart = SANE_FALSE;
+
+ /* signals if a strip of the given color has been found */
+ found = 0;
+
+ /* detection pass done */
+ pass = 0;
+
+ /* loop until strip is found or maximum pass number done */
+ while (pass < 20 && !found)
+ {
+ /* scan a full width strip */
+ status =
+ simple_scan (dev, settings, SANE_TRUE, forward, SANE_FALSE, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl646_search_strip: simple_scan failed\n");
+ free (data);
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s%02d.pnm", forward ? "fwd" : "bwd",
+ (int)pass);
+ sanei_genesys_write_pnm_file (title, data, settings.depth, 1,
+ settings.pixels, settings.lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < settings.lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * settings.pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * settings.pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / settings.pixels < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl646_search_strip: strip found forward during pass %d at line %d\n",
+ pass, y);
+ }
+ else
+ {
+ DBG (DBG_data, "gl646_search_strip: pixels=%d, count=%d\n",
+ settings.pixels, count);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < settings.lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * settings.pixels + x] > 60)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * settings.pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (settings.pixels * settings.lines) < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl646_search_strip: strip found backward during pass %d \n",
+ pass);
+ }
+ else
+ {
+ DBG (DBG_data, "gl646_search_strip: pixels=%d, count=%d\n",
+ settings.pixels, count);
+ }
+ }
+ pass++;
+ }
+ free (data);
+ if (found)
+ {
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_info, "gl646_search_strip: strip found\n");
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ DBG (DBG_info, "gl646_search_strip: strip not found\n");
+ }
+ return status;
+}
+
+/** the gl646 command set */
+static Genesys_Command_Set gl646_cmd_set = {
+ "gl646-generic", /* the name of this set */
+
+ gl646_init,
+ gl646_init_regs_for_warmup,
+ gl646_init_regs_for_coarse_calibration,
+ gl646_init_regs_for_shading,
+ gl646_init_regs_for_scan,
+
+ gl646_get_filter_bit,
+ gl646_get_lineart_bit,
+ gl646_get_bitset_bit,
+ gl646_get_gain4_bit,
+ gl646_get_fast_feed_bit,
+ gl646_test_buffer_empty_bit,
+ gl646_test_motor_flag_bit,
+
+ gl646_bulk_full_size,
+
+ gl646_public_set_fe,
+ gl646_set_powersaving,
+ gl646_save_power,
+ gl646_set_motor_power,
+ gl646_set_lamp_power,
+
+ gl646_begin_scan,
+ gl646_end_scan,
+
+ gl646_send_gamma_table,
+
+ gl646_search_start_position,
+
+ gl646_offset_calibration,
+ gl646_coarse_gain_calibration,
+ gl646_led_calibration,
+
+ gl646_slow_back_home,
+
+ gl646_bulk_write_register,
+ gl646_bulk_write_data,
+ gl646_bulk_read_data,
+
+ gl646_update_hardware_sensors,
+
+ /* sheetfed related functions */
+ gl646_load_document,
+ gl646_detect_document_end,
+ gl646_eject_document,
+ gl646_search_strip,
+
+ gl646_is_compatible_calibration,
+ gl646_move_to_ta,
+ NULL,
+ NULL,
+ NULL
+};
+
+SANE_Status
+sanei_gl646_init_cmd_set (Genesys_Device * dev)
+{
+ dev->model->cmd_set = &gl646_cmd_set;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl646.h b/backend/genesys_gl646.h
new file mode 100644
index 0000000..e9124fd
--- /dev/null
+++ b/backend/genesys_gl646.h
@@ -0,0 +1,680 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003-2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004-2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+
+/*
+ * Genesys Logic GL646 based scanners
+ */
+/* Individual bits */
+#define REG01_CISSET 0x80
+#define REG01_DOGENB 0x40
+#define REG01_DVDSET 0x20
+#define REG01_FASTMOD 0x10
+#define REG01_COMPENB 0x08
+#define REG01_DRAMSEL 0x04
+#define REG01_SHDAREA 0x02
+#define REG01_SCAN 0x01
+
+#define REG02_NOTHOME 0x80
+#define REG02_ACDCDIS 0x40
+#define REG02_AGOHOME 0x20
+#define REG02_MTRPWR 0x10
+#define REG02_FASTFED 0x08
+#define REG02_MTRREV 0x04
+#define REG02_STEPSEL 0x03
+
+#define REG02_FULLSTEP 0x00
+#define REG02_HALFSTEP 0x01
+#define REG02_QUATERSTEP 0x02
+
+#define REG03_TG3 0x80
+#define REG03_AVEENB 0x40
+#define REG03_XPASEL 0x20
+#define REG03_LAMPPWR 0x10
+#define REG03_LAMPDOG 0x08
+#define REG03_LAMPTIM 0x07
+
+#define REG04_LINEART 0x80
+#define REG04_BITSET 0x40
+#define REG04_ADTYPE 0x30
+#define REG04_FILTER 0x0c
+#define REG04_FESET 0x03
+
+#define REG05_DPIHW 0xc0
+#define REG05_DPIHW_600 0x00
+#define REG05_DPIHW_1200 0x40
+#define REG05_DPIHW_2400 0x80
+#define REG05_DPIHW_4800 0xc0
+#define REG05_GMMTYPE 0x30
+#define REG05_GMM14BIT 0x10
+#define REG05_GMMENB 0x08
+#define REG05_LEDADD 0x04
+#define REG05_BASESEL 0x03
+
+#define REG06_PWRBIT 0x10
+#define REG06_GAIN4 0x08
+#define REG06_OPTEST 0x07
+
+#define REG07_DMASEL 0x02
+#define REG07_DMARDWR 0x01
+
+#define REG16_CTRLHI 0x80
+#define REG16_SELINV 0x40
+#define REG16_TGINV 0x20
+#define REG16_CK1INV 0x10
+#define REG16_CK2INV 0x08
+#define REG16_CTRLINV 0x04
+#define REG16_CKDIS 0x02
+#define REG16_CTRLDIS 0x01
+
+#define REG17_TGMODE 0xc0
+#define REG17_TGMODE_NO_DUMMY 0x00
+#define REG17_TGMODE_REF 0x40
+#define REG17_TGMODE_XPA 0x80
+#define REG17_TGW 0x3f
+
+#define REG18_CNSET 0x80
+#define REG18_DCKSEL 0x60
+#define REG18_CKTOGGLE 0x10
+#define REG18_CKDELAY 0x0c
+#define REG18_CKSEL 0x03
+
+#define REG1D_CKMANUAL 0x80
+
+#define REG1E_WDTIME 0xf0
+#define REG1E_LINESEL 0x0f
+
+#define REG41_PWRBIT 0x80
+#define REG41_BUFEMPTY 0x40
+#define REG41_FEEDFSH 0x20
+#define REG41_SCANFSH 0x10
+#define REG41_HOMESNR 0x08
+#define REG41_LAMPSTS 0x04
+#define REG41_FEBUSY 0x02
+#define REG41_MOTMFLG 0x01
+
+#define REG66_LOW_CURRENT 0x10
+
+#define REG6A_FSTPSEL 0xc0
+#define REG6A_FASTPWM 0x3f
+
+#define REG6C_TGTIME 0xc0
+#define REG6C_Z1MOD 0x38
+#define REG6C_Z2MOD 0x07
+
+#define REG_SCANFED 0x1f
+#define REG_BUFSEL 0x20
+#define REG_LINCNT 0x25
+#define REG_DPISET 0x2c
+#define REG_STRPIXEL 0x30
+#define REG_ENDPIXEL 0x32
+#define REG_DUMMY 0x34
+#define REG_MAXWD 0x35
+#define REG_LPERIOD 0x38
+#define REG_FEEDL 0x3d
+#define REG_VALIDWORD 0x42
+#define REG_FEDCNT 0x48
+#define REG_SCANCNT 0x4b
+#define REG_Z1MOD 0x60
+#define REG_Z2MOD 0x62
+
+
+#include "genesys.h"
+
+enum
+{
+ reg_0x01 = 0,
+ reg_0x02,
+ reg_0x03,
+ reg_0x04,
+ reg_0x05,
+ reg_0x06,
+ reg_0x07,
+ reg_0x08,
+ reg_0x09,
+ reg_0x0a,
+ reg_0x0b,
+ reg_0x10,
+ reg_0x11,
+ reg_0x12,
+ reg_0x13,
+ reg_0x14,
+ reg_0x15,
+ reg_0x16,
+ reg_0x17,
+ reg_0x18,
+ reg_0x19,
+ reg_0x1a,
+ reg_0x1b,
+ reg_0x1c,
+ reg_0x1d,
+ reg_0x1e,
+ reg_0x1f,
+ reg_0x20,
+ reg_0x21,
+ reg_0x22,
+ reg_0x23,
+ reg_0x24,
+ reg_0x25,
+ reg_0x26,
+ reg_0x27,
+ reg_0x28,
+ reg_0x29,
+ reg_0x2c,
+ reg_0x2d,
+ reg_0x2e,
+ reg_0x2f,
+ reg_0x30,
+ reg_0x31,
+ reg_0x32,
+ reg_0x33,
+ reg_0x34,
+ reg_0x35,
+ reg_0x36,
+ reg_0x37,
+ reg_0x38,
+ reg_0x39,
+ reg_0x3d,
+ reg_0x3e,
+ reg_0x3f,
+ reg_0x52,
+ reg_0x53,
+ reg_0x54,
+ reg_0x55,
+ reg_0x56,
+ reg_0x57,
+ reg_0x58,
+ reg_0x59,
+ reg_0x5a,
+ reg_0x5b,
+ reg_0x5c,
+ reg_0x5d,
+ reg_0x5e,
+ reg_0x60,
+ reg_0x61,
+ reg_0x62,
+ reg_0x63,
+ reg_0x64,
+ reg_0x65,
+ reg_0x66,
+ reg_0x67,
+ reg_0x68,
+ reg_0x69,
+ reg_0x6a,
+ reg_0x6b,
+ reg_0x6c,
+ reg_0x6d,
+ GENESYS_GL646_MAX_REGS
+};
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl646_set_fe (Genesys_Device * dev, uint8_t set, int dpi);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl646_public_set_fe (Genesys_Device * dev, uint8_t set);
+
+/**
+ * sets up the scanner for a scan, registers, gamma tables, shading tables
+ * and slope tables, based on the parameter struct.
+ * @param device device to set up
+ * @param settings settings of the scan
+ * @param split true if move before scan has to be done
+ * @param xcorrection true if scanner's X geometry must be taken into account to
+ * compute X, ie add left margins
+ * @param ycorrection true if scanner's Y geometry must be taken into account to
+ * compute Y, ie add top margins
+ */
+static SANE_Status
+setup_for_scan (Genesys_Device * device, Genesys_Settings settings,
+ SANE_Bool split, SANE_Bool xcorrection,
+ SANE_Bool ycorrection);
+
+/**
+ * sets up the registers for a scan corresponding to the settings.
+ * Builds motor slope tables. Computes buffer sizes and data amount to
+ * transfer. It also sets up analog frontend.
+ * */
+static SANE_Status
+gl646_setup_registers (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ Genesys_Settings scan_settings,
+ uint16_t * slope_table1,
+ uint16_t * slope_table2,
+ SANE_Int resolution,
+ uint32_t move,
+ uint32_t linecnt,
+ uint16_t startx,
+ uint16_t endx, SANE_Bool color, SANE_Int depth);
+
+/**
+ * Does a simple move of the given distance by doing a scan at lowest resolution
+ * shading correction. Memory for data is allocated in this function
+ * and must be freed by caller.
+ * @param dev device of the scanner
+ * @param distance distance to move in MM
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+simple_move (Genesys_Device * dev, SANE_Int distance);
+
+/**
+ * Does a simple scan of the area given by the settings. Scanned data
+ * it put in an allocated area which must be freed by the caller.
+ * and slope tables, based on the parameter struct. There is no shading
+ * correction while gamma correction is active.
+ * @param dev device to set up
+ * @param settings settings of the scan
+ * @param move flag to enable scanhead to move
+ * @param forward flag to tell movement direction
+ * @param shading flag to tell if shading correction should be done
+ * @param data pointer that will point to the scanned data
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+simple_scan (Genesys_Device * dev, Genesys_Settings settings, SANE_Bool move, SANE_Bool forward,
+ SANE_Bool shading, unsigned char **data);
+
+/**
+ * Send the stop scan command
+ * */
+static SANE_Status
+end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop, SANE_Bool eject);
+/**
+ * writes control data to an area behind the last motor table.
+ */
+static SANE_Status write_control (Genesys_Device * dev, int resolution);
+
+
+/**
+ * initialize scanner's registers at SANE init time
+ */
+static void gl646_init_regs (Genesys_Device * dev);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl646_load_document (Genesys_Device * dev);
+
+static SANE_Status
+gl646_detect_document_end (Genesys_Device * dev);
+
+#define FULL_STEP 0
+#define HALF_STEP 1
+#define QUATER_STEP 2
+
+#define CALIBRATION_LINES 10
+
+/**
+ * master motor settings table entry
+ */
+typedef struct
+{
+ /* key */
+ SANE_Int motor;
+ SANE_Int dpi;
+ SANE_Bool color;
+
+ /* settings */
+ SANE_Int ydpi; /* real motor dpi, may be different from the resolution */
+ SANE_Int steptype; /* 0=full, 1=half, 2=quarter */
+ SANE_Bool fastmod; /* fast scanning 0/1 */
+ SANE_Bool fastfed; /* fast fed slope tables */
+ SANE_Int mtrpwm;
+ SANE_Int steps1; /* table 1 informations */
+ SANE_Int vstart1;
+ SANE_Int vend1;
+ SANE_Int steps2; /* table 2 informations */
+ SANE_Int vstart2;
+ SANE_Int vend2;
+ float g1;
+ float g2;
+ SANE_Int fwdbwd; /* forward/backward steps */
+} Motor_Master;
+
+/**
+ * master sensor settings table entry
+ */
+typedef struct
+{
+ /* key */
+ SANE_Int sensor; /**< sensor identifier */
+ SANE_Int dpi; /**< required dpi */
+ SANE_Bool color; /**< SANE_TRUE if color scan */
+
+ /* settings */
+ SANE_Int xdpi; /**< real sensor dpi, may be different from the required resolution */
+ SANE_Int exposure; /**< exposure time */
+ SANE_Int dpiset; /**< set sensor dpi */
+ SANE_Int cksel; /**< dpiset 'divisor', part of reg 18h */
+ SANE_Int dummy; /**< dummy exposure time */
+ /* uint8_t regs_0x10_0x15[6];*/
+ uint8_t *regs_0x10_0x15; /**< per color exposure time for CIS scanners */
+ SANE_Bool half_ccd; /**> true if manual CCD/2 clock programming or real dpi is half dpiset */
+ uint8_t r18; /**> content of register 18h */
+ uint8_t r1d; /**> content of register 1dh */
+} Sensor_Master;
+
+/**
+ * settings for a given resolution and DPISET
+ * TODO clean up this when all scanners will have been added
+ */
+typedef struct
+{
+ /* key */
+ SANE_Int sensor;
+ SANE_Int cksel;
+
+ /* values */
+ uint8_t regs_0x08_0x0b[4]; /**< settings for normal CCD clock */
+ uint8_t manual_0x08_0x0b[4]; /**< settings for CCD/2 clock */
+ uint8_t regs_0x16_0x1d[8];
+ uint8_t regs_0x52_0x5e[13];
+ uint8_t manual_0x52_0x58[7];
+} Sensor_Settings;
+
+static uint8_t xp200_color[6]={0x16, 0x44, 0x0c, 0x80, 0x09, 0x2e};
+static uint8_t xp200_gray[6]={0x05, 0x0a, 0x0f, 0xa0, 0x10, 0x10};
+
+/**
+ * master sensor settings, for a given sensor and dpi,
+ * it gives exposure and CCD time
+ */
+/* *INDENT-OFF* */
+static Sensor_Master sensor_master[] = {
+ /* HP3670 master settings */
+ {CCD_HP3670, 75, SANE_TRUE , 75, 4879, 300, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 100, SANE_TRUE , 100, 4487, 400, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 150, SANE_TRUE , 150, 4879, 600, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 300, SANE_TRUE , 300, 4503, 1200, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 600, SANE_TRUE , 600, 10251, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x43},
+ {CCD_HP3670,1200, SANE_TRUE , 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
+ {CCD_HP3670,2400, SANE_TRUE , 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
+ {CCD_HP3670, 75, SANE_FALSE, 75, 4879, 300, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 100, SANE_FALSE, 100, 4487, 400, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 150, SANE_FALSE, 150, 4879, 600, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 300, SANE_FALSE, 300, 4503, 1200, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
+ {CCD_HP3670, 600, SANE_FALSE, 600, 10251, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x43},
+ {CCD_HP3670,1200, SANE_FALSE, 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
+ {CCD_HP3670,2400, SANE_FALSE, 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
+
+ /* HP 2400 master settings */
+ {CCD_HP2400, 50, SANE_TRUE , 50, 7211, 200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 100, SANE_TRUE , 100, 7211, 400, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 150, SANE_TRUE , 150, 7211, 600, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 300, SANE_TRUE , 300, 8751, 1200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 600, SANE_TRUE , 600, 18760, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x02},
+ {CCD_HP2400,1200, SANE_TRUE , 1200, 21749, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x42},
+ {CCD_HP2400, 50, SANE_FALSE, 50, 7211, 200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 100, SANE_FALSE, 100, 7211, 400, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 150, SANE_FALSE, 150, 7211, 600, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 300, SANE_FALSE, 300, 8751, 1200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
+ {CCD_HP2400, 600, SANE_FALSE, 600, 18760, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x02},
+ {CCD_HP2400,1200, SANE_FALSE, 1200, 21749, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x42},
+
+ /* XP 200 master settings */
+ {CIS_XP200 , 75, SANE_TRUE , 75, 5700, 75, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 100, SANE_TRUE , 100, 5700, 100, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 200, SANE_TRUE , 200, 5700, 200, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 300, SANE_TRUE , 300, 9000, 300, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 600, SANE_TRUE , 600, 16000, 600, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
+
+ {CIS_XP200 , 75, SANE_FALSE, 75, 16000, 75, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 100, SANE_FALSE, 100, 7800, 100, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 200, SANE_FALSE, 200, 11000, 200, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 300, SANE_FALSE, 300, 13000, 300, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
+ {CIS_XP200 , 600, SANE_FALSE, 600, 24000, 600, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
+
+ /* HP 2300 master settings */
+ {CCD_HP2300, 75, SANE_TRUE , 75, 4480, 150, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
+ {CCD_HP2300, 150, SANE_TRUE , 150, 4350, 300, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
+ {CCD_HP2300, 300, SANE_TRUE, 300, 4350, 600, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
+ {CCD_HP2300, 600, SANE_TRUE , 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
+ {CCD_HP2300,1200, SANE_TRUE , 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
+ {CCD_HP2300, 75, SANE_FALSE, 75, 4480, 150, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
+ {CCD_HP2300, 150, SANE_FALSE, 150, 4350, 300, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
+ {CCD_HP2300, 300, SANE_FALSE, 300, 4350, 600, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
+ {CCD_HP2300, 600, SANE_FALSE, 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
+ {CCD_HP2300,1200, SANE_FALSE, 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
+ /* non half ccd 300 dpi settings
+ {CCD_HP2300, 300, SANE_TRUE , 300, 8700, 300, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
+ {CCD_HP2300, 300, SANE_FALSE, 300, 8700, 300, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
+ */
+
+ /* MD5345/6471 master settings */
+ {CCD_5345 , 50, SANE_TRUE , 50, 12000, 100, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 75, SANE_TRUE , 75, 11000, 150, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 100, SANE_TRUE , 100, 11000, 200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 150, SANE_TRUE , 150, 11000, 300, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 200, SANE_TRUE , 200, 11000, 400, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 300, SANE_TRUE , 300, 11000, 600, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 400, SANE_TRUE , 400, 11000, 800, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 600, SANE_TRUE , 600, 11000,1200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 ,1200, SANE_TRUE ,1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
+ {CCD_5345 ,2400, SANE_TRUE ,1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
+ {CCD_5345 , 50, SANE_FALSE, 50, 12000, 100, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 75, SANE_FALSE, 75, 11000, 150, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 100, SANE_FALSE, 100, 11000, 200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 150, SANE_FALSE, 150, 11000, 300, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 200, SANE_FALSE, 200, 11000, 400, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 300, SANE_FALSE, 300, 11000, 600, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 400, SANE_FALSE, 400, 11000, 800, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 , 600, SANE_FALSE, 600, 11000,1200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
+ {CCD_5345 ,1200, SANE_FALSE,1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
+ {CCD_5345 ,2400, SANE_FALSE,1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
+
+};
+
+/**
+ * master motor settings, for a given motor and dpi,
+ * it gives steps and speed informations
+ */
+static Motor_Master motor_master[] = {
+ /* HP3670 motor settings */
+ {MOTOR_HP3670, 75, SANE_TRUE , 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 1, 200, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 100, SANE_TRUE , 100, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 143, 2905, 187, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 150, SANE_TRUE , 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 73, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 300, SANE_TRUE , 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 11, 1055, 563, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 600, SANE_TRUE , 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 10687, 5126, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670,1200, SANE_TRUE ,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 6375, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670,2400, SANE_TRUE ,2400, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 12750, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 75, SANE_FALSE, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 1, 200, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 100, SANE_FALSE, 100, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 143, 2905, 187, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 150, SANE_FALSE, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 73, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 300, SANE_FALSE, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 11, 1055, 563, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670, 600, SANE_FALSE, 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 10687, 5126, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670,1200, SANE_FALSE,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 6375, 192, 3399, 337, 0.3, 0.4, 192},
+ {MOTOR_HP3670,2400, SANE_TRUE ,2400, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 12750, 192, 3399, 337, 0.3, 0.4, 192},
+
+ /* HP2400/G2410 motor settings base motor dpi = 600 */
+ {MOTOR_HP2400, 50, SANE_TRUE , 50, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 100, SANE_TRUE , 100, HALF_STEP, SANE_FALSE, SANE_TRUE, 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 150, SANE_TRUE , 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 15902, 902, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 300, SANE_TRUE , 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 32, 16703, 2188, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 600, SANE_TRUE , 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 18761, 18761, 192, 4905, 627, 0.30, 0.4, 192},
+ {MOTOR_HP2400,1200, SANE_TRUE ,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 43501, 43501, 192, 4905, 627, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 50, SANE_FALSE, 50, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 100, SANE_FALSE, 100, HALF_STEP, SANE_FALSE, SANE_TRUE, 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 150, SANE_FALSE, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 15902, 902, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 300, SANE_FALSE, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 32, 16703, 2188, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400, 600, SANE_FALSE, 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 18761, 18761, 192, 4905, 337, 0.30, 0.4, 192},
+ {MOTOR_HP2400,1200, SANE_FALSE,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 43501, 43501, 192, 4905, 337, 0.30, 0.4, 192},
+
+ /* XP 200 motor settings */
+ {MOTOR_XP200, 75, SANE_TRUE, 75, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 2136, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 100, SANE_TRUE, 100, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 2850, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 200, SANE_TRUE, 200, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6999, 5700, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 250, SANE_TRUE, 250, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6999, 6999, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 300, SANE_TRUE, 300, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 13500, 13500, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 600, SANE_TRUE, 600, HALF_STEP, SANE_TRUE , SANE_TRUE, 0, 4, 31998, 31998, 2, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 75, SANE_FALSE, 75, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 2000, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 100, SANE_FALSE, 100, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 1300, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 200, SANE_FALSE, 200, HALF_STEP, SANE_TRUE , SANE_TRUE, 0, 4, 6000, 3666, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 300, SANE_FALSE, 300, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6500, 6500, 8, 12000, 1200, 0.3, 0.5, 1},
+ {MOTOR_XP200, 600, SANE_FALSE, 600, HALF_STEP, SANE_TRUE , SANE_TRUE, 0, 4, 24000, 24000, 2, 12000, 1200, 0.3, 0.5, 1},
+
+ /* HP scanjet 2300c */
+ {MOTOR_HP2300, 75, SANE_TRUE, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8139, 560, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 150, SANE_TRUE, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 7903, 543, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 300, SANE_TRUE, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 2175, 1087, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 600, SANE_TRUE, 600, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 8700, 4350, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300,1200, SANE_TRUE,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 17400, 8700, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 75, SANE_FALSE, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8139, 560, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 150, SANE_FALSE, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 7903, 543, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 300, SANE_FALSE, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 2175, 1087, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 600, SANE_FALSE, 600, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 8700, 4350, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300,1200, SANE_FALSE,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 17400, 8700, 120, 4905, 337, 0.3, 0.4, 16},
+ /* non half ccd settings for 300 dpi
+ {MOTOR_HP2300, 300, SANE_TRUE, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 44, 5386, 2175, 120, 4905, 337, 0.3, 0.4, 16},
+ {MOTOR_HP2300, 300, SANE_FALSE, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 44, 5386, 2175, 120, 4905, 337, 0.3, 0.4, 16},
+ */
+
+ /* MD5345/6471 motor settings */
+ /* vfinal=(exposure/(1200/dpi))/step_type */
+ {MOTOR_5345, 50, SANE_TRUE , 50, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 250, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 75, SANE_TRUE , 75, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 343, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 100, SANE_TRUE , 100, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 458, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 150, SANE_TRUE , 150, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 687, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 200, SANE_TRUE , 200, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 916, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 300, SANE_TRUE, 300, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 1375, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 400, SANE_TRUE, 400, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2000, 1833, 255, 2000, 300, 0.3, 0.4, 32},
+ {MOTOR_5345, 500, SANE_TRUE, 500, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2291, 2291, 255, 2000, 300, 0.3, 0.4, 32},
+ {MOTOR_5345, 600, SANE_TRUE, 600, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 32},
+ {MOTOR_5345, 1200, SANE_TRUE ,1200, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 146},
+ {MOTOR_5345, 2400, SANE_TRUE ,2400, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 5500, 5500, 255, 2000, 300, 0.3, 0.4, 146},
+ {MOTOR_5345, 50, SANE_FALSE, 50, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 250, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 75, SANE_FALSE, 75, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 343, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 100, SANE_FALSE, 100, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 458, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 150, SANE_FALSE, 150, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 687, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 200, SANE_FALSE, 200, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 916, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 300, SANE_FALSE, 300, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 1375, 255, 2000, 300, 0.3, 0.4, 64},
+ {MOTOR_5345, 400, SANE_FALSE, 400, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2000, 1833, 255, 2000, 300, 0.3, 0.4, 32},
+ {MOTOR_5345, 500, SANE_FALSE, 500, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2291, 2291, 255, 2000, 300, 0.3, 0.4, 32},
+ {MOTOR_5345, 600, SANE_FALSE, 600, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 32},
+ {MOTOR_5345, 1200, SANE_FALSE,1200, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 146},
+ {MOTOR_5345, 2400, SANE_FALSE,2400, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 5500, 5500, 255, 2000, 300, 0.3, 0.4, 146}, /* 5500 guessed */
+};
+
+/**
+ * sensor settings for a given sensor and timing method
+ */
+static Sensor_Settings sensor_settings[] = {
+ /* HP 3670 */
+ {CCD_HP3670, 1,
+ {0x0d, 0x0f, 0x11, 0x13},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x2b, 0x07, 0x30, 0x2a, 0x00, 0x00, 0xc0, 0x43},
+ {0x03, 0x07, 0x0b, 0x0f, 0x13, 0x17, 0x23, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, },
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ },
+ {CCD_HP3670, 2,
+ {0x00, 0x05, 0x06, 0x08},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x33, 0x07, 0x31, 0x2a, 0x02, 0x0e, 0xc0, 0x43},
+ {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63, 0x00, 0xc1, 0x02, 0x0e, 0x00, 0x00, },
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ },
+ {CCD_HP3670, 4,
+ {0x00, 0x0a, 0x0b, 0x0d},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x33, 0x07, 0x33, 0x2a, 0x02, 0x13, 0xc0, 0x43},
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x15, 0xc1, 0x05, 0x0a, 0x0f, 0x00, },
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ },
+ /* HP 2400 */
+ {CCD_HP2400, 4,
+ {0x14, 0x15, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0xbf, 0x08, 0x3f, 0x2a, 0x00, 0x00, 0x00, 0x02},
+ {11, 15, 19, 23, 3, 7, 0x63, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00},
+ {11, 15, 19, 23, 3, 7, 0x63}
+ },
+ {CCD_HP2400, 2,
+ {14, 15, 0, 0},
+ {14, 15, 0, 0},
+ {0xbf, 0x08, 0x31, 0x2a, 0, 0, 0, 0x02},
+ {3, 7, 11, 15, 19, 23, 0x23, 0, 0xc1, 0, 0, 0, 0},
+ {3, 7, 11, 15, 19, 23, 0x23}
+ },
+ {CCD_HP2400, 1,
+ {0x02, 0x04, 0x00, 0x00},
+ {0x02, 0x04, 0x00, 0x00},
+ {0xbf, 0x08, 0x30, 0x2a, 0x00, 0x00, 0xc0, 0x42},
+ {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63, 0x00, 0xc1, 0x00, 0x0e, 0x00, 0x00},
+ {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63}
+ },
+ {CIS_XP200, 1,
+ {6, 7, 10, 4},
+ {6, 7, 10, 4},
+ {0x24, 0x04, 0x00, 0x2a, 0x0a, 0x0a, 0, 0x11},
+ {8, 2, 0, 0, 0, 0, 0x1a, 0x51, 0, 0, 0, 0, 0},
+ {8, 2, 0, 0, 0, 0, 0x1a}
+ },
+ {CCD_HP2300, 1,
+ {0x01, 0x03, 0x04, 0x06},
+ {0x16, 0x00, 0x01, 0x03},
+ {0xb7, 0x0a, 0x20, 0x2a, 0x6a, 0x8a, 0x00, 0x05},
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x00, 0xc1, 0x06, 0x0b, 0x10, 0x16},
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83}
+ },
+ {CCD_5345, 1,
+ {0x0d, 0x0f, 0x11, 0x13},
+ {0x00, 0x05, 0x06, 0x08}, /* manual clock 1/2 settings or half ccd */
+ {0x0b, 0x0a, 0x30, 0x2a, 0x00, 0x00, 0x00, 0x03, },
+ {0x03, 0x07, 0x0b, 0x0f, 0x13, 0x17, 0x23, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00},
+ {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83} /* half ccd settings */
+ },
+};
+/* *INDENT-ON* */
diff --git a/backend/genesys_gl841.c b/backend/genesys_gl841.c
new file mode 100644
index 0000000..2b22d2c
--- /dev/null
+++ b/backend/genesys_gl841.c
@@ -0,0 +1,5733 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005 Philipp Schmid <philipp8288@web.de>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
+ Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
+ for Plustek Opticbook 3600 support
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_gl841
+
+#include "genesys_gl841.h"
+
+/****************************************************************************
+ Low level function
+ ****************************************************************************/
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+/* Write to many registers */
+/* Note: There is no known bulk register write,
+ this function is sending single registers instead */
+static SANE_Status
+gl841_bulk_write_register (Genesys_Device * dev,
+ Genesys_Register_Set * reg, size_t elems)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned int i, c;
+ uint8_t buffer[GENESYS_MAX_REGS * 2];
+
+ /* handle differently sized register sets, reg[0x00] is the last one */
+ i = 0;
+ while ((i < elems) && (reg[i].address != 0))
+ i++;
+
+ elems = i;
+
+ DBG (DBG_io, "gl841_bulk_write_register (elems = %lu)\n",
+ (u_long) elems);
+
+ for (i = 0; i < elems; i++) {
+
+ buffer[i * 2 + 0] = reg[i].address;
+ buffer[i * 2 + 1] = reg[i].value;
+
+ DBG (DBG_io2, "reg[0x%02x] = 0x%02x\n", buffer[i * 2 + 0],
+ buffer[i * 2 + 1]);
+ }
+
+ for (i = 0; i < elems;) {
+ c = elems - i;
+ if (c > 32) /*32 is max. checked that.*/
+ c = 32;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_SET_REGISTER, INDEX, c * 2, buffer + i * 2);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_write_register: failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ i += c;
+ }
+
+ DBG (DBG_io, "gl841_bulk_write_register: wrote %lu registers\n",
+ (u_long) elems);
+ return status;
+}
+
+/* Write bulk data (e.g. shading, gamma) */
+static SANE_Status
+gl841_bulk_write_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBG (DBG_io, "gl841_bulk_write_data writing %lu bytes\n",
+ (u_long) len);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_write_data failed while setting register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if (len > BULKOUT_MAXSIZE)
+ size = BULKOUT_MAXSIZE;
+ else
+ size = len;
+
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_RAM;
+ outdata[2] = VALUE_BUFFER & 0xff;
+ outdata[3] = (VALUE_BUFFER >> 8) & 0xff;
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_write_data failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_usb_write_bulk (dev->dn, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_write_data failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "gl841_bulk_write_data wrote %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ DBG (DBG_io, "gl841_bulk_write_data: completed\n");
+
+ return status;
+}
+
+/* for debugging transfer rate*/
+/*
+#include <sys/time.h>
+static struct timeval start_time;
+static void
+starttime(){
+ gettimeofday(&start_time,NULL);
+}
+static void
+printtime(char *p) {
+ struct timeval t;
+ long long int dif;
+ gettimeofday(&t,NULL);
+ dif = t.tv_sec - start_time.tv_sec;
+ dif = dif*1000000 + t.tv_usec - start_time.tv_usec;
+ fprintf(stderr,"%s %lluµs\n",p,dif);
+}
+*/
+
+/* Read bulk data (e.g. scanned data) */
+static SANE_Status
+gl841_bulk_read_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBG (DBG_io, "gl841_bulk_read_data: requesting %lu bytes\n",
+ (u_long) len);
+
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_read_data failed while setting register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ outdata[0] = BULK_IN;
+ outdata[1] = BULK_RAM;
+ outdata[2] = VALUE_BUFFER & 0xff;
+ outdata[3] = (VALUE_BUFFER >> 8) & 0xff;
+ outdata[4] = (len & 0xff);
+ outdata[5] = ((len >> 8) & 0xff);
+ outdata[6] = ((len >> 16) & 0xff);
+ outdata[7] = ((len >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata), outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_read_data failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if (len > BULKIN_MAXSIZE)
+ size = BULKIN_MAXSIZE;
+ else
+ size = len;
+
+ DBG (DBG_io2,
+ "gl841_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) size);
+
+ status = sanei_usb_read_bulk (dev->dn, data, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "gl841_bulk_read_data read %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ DBG (DBG_io, "gl841_bulk_read_data: completed\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Set address for writing data */
+static SANE_Status
+gl841_set_buffer_address_gamma (Genesys_Device * dev, uint32_t addr)
+{
+ SANE_Status status;
+
+ DBG (DBG_io, "gl841_set_buffer_address_gamma: setting address to 0x%05x\n",
+ addr & 0xfffffff0);
+
+ addr = addr >> 4;
+
+ status = sanei_genesys_write_register (dev, 0x5c, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_buffer_address_gamma: failed while writing low byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ addr = addr >> 8;
+ status = sanei_genesys_write_register (dev, 0x5b, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_buffer_address_gamma: failed while writing high byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "gl841_set_buffer_address_gamma: completed\n");
+
+ return status;
+}
+
+/* Write bulk data (e.g. gamma) */
+static SANE_Status
+gl841_bulk_write_data_gamma (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBG (DBG_io, "gl841_bulk_write_data_gamma writing %lu bytes\n",
+ (u_long) len);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_bulk_write_data_gamma failed while setting register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if (len > BULKOUT_MAXSIZE)
+ size = BULKOUT_MAXSIZE;
+ else
+ size = len;
+
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_RAM;
+ outdata[2] = 0x00;/* 0x82 works, too */
+ outdata[3] = 0x00;
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_bulk_write_data_gamma failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_usb_write_bulk (dev->dn, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "genesys_bulk_write_data_gamma failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "genesys_bulk_write_data:gamma wrote %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ DBG (DBG_io, "genesys_bulk_write_data_gamma: completed\n");
+
+ return status;
+}
+
+
+/****************************************************************************
+ Mid level functions
+ ****************************************************************************/
+
+static SANE_Bool
+gl841_get_fast_feed_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x02);
+ if (r && (r->value & REG02_FASTFED))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl841_get_filter_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x04);
+ if (r && (r->value & REG04_FILTER))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl841_get_lineart_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x04);
+ if (r && (r->value & REG04_LINEART))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl841_get_bitset_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x04);
+ if (r && (r->value & REG04_BITSET))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl841_get_gain4_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x06);
+ if (r && (r->value & REG06_GAIN4))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl841_test_buffer_empty_bit (SANE_Byte val)
+{
+ if (val & REG41_BUFEMPTY)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl841_test_motor_flag_bit (SANE_Byte val)
+{
+ if (val & REG41_MOTORENB)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/** copy sensor specific settings */
+/* *dev : device infos
+ *regs : registers to be set
+ extended : do extended set up
+ half_ccd: set up for half ccd resolution
+ all registers 08-0B, 10-1D, 52-59 are set up. They shouldn't
+ appear anywhere else but in register_ini
+
+Responsible for signals to CCD/CIS:
+ CCD_CK1X (CK1INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1MTGL(0x1C),CK1LOW(0x1D),CK1MAP(0x74,0x75,0x76),CK1NEG(0x7D))
+ CCD_CK2X (CK2INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1LOW(0x1D),CK1NEG(0x7D))
+ CCD_CK3X (MANUAL3(0x1A),CK3INV(0x1A),CK3MTGL(0x1C),CK3LOW(0x1D),CK3MAP(0x77,0x78,0x79),CK3NEG(0x7D))
+ CCD_CK4X (MANUAL3(0x1A),CK4INV(0x1A),CK4MTGL(0x1C),CK4LOW(0x1D),CK4MAP(0x7A,0x7B,0x7C),CK4NEG(0x7D))
+ CCD_CPX (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),CPH(0x72),CPL(0x73),CPNEG(0x7D))
+ CCD_RSX (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),RSH(0x70),RSL(0x71),RSNEG(0x7D))
+ CCD_TGX (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPR(0x10,0x11),TGSHLD(0x1D))
+ CCD_TGG (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPG(0x12,0x13),TGSHLD(0x1D))
+ CCD_TGB (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPB(0x14,0x15),TGSHLD(0x1D))
+ LAMP_SW (EXPR(0x10,0x11),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29))
+ XPA_SW (EXPG(0x12,0x13),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29))
+ LAMP_B (EXPB(0x14,0x15),LAMP_PWR(0x03))
+
+other registers:
+ CISSET(0x01),CNSET(0x18),DCKSEL(0x18),SCANMOD(0x18),EXPDMY(0x19),LINECLP(0x1A),CKAREA(0x1C),TGTIME(0x1C),LINESEL(0x1E),DUMMY(0x34)
+
+Responsible for signals to AFE:
+ VSMP (VSMP(0x58),VSMPW(0x58))
+ BSMP (BSMP(0x59),BSMPW(0x59))
+
+other register settings depending on this:
+ RHI(0x52),RLOW(0x53),GHI(0x54),GLOW(0x55),BHI(0x56),BLOW(0x57),
+
+*/
+static void
+sanei_gl841_setup_sensor (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ SANE_Bool extended, SANE_Bool half_ccd)
+{
+ Genesys_Register_Set *r;
+ int i;
+
+ DBG (DBG_proc, "gl841_setup_sensor\n");
+
+ r = sanei_genesys_get_address (regs, 0x70);
+ for (i = 0; i < 4; i++, r++)
+ r->value = dev->sensor.regs_0x08_0x0b[i];
+
+ r = sanei_genesys_get_address (regs, 0x16);
+ for (i = 0x06; i < 0x0a; i++, r++)
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+
+ r = sanei_genesys_get_address (regs, 0x1a);
+ for (i = 0x0a; i < 0x0e; i++, r++)
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+
+ r = sanei_genesys_get_address (regs, 0x52);
+ for (i = 0; i < 9; i++, r++)
+ r->value = dev->sensor.regs_0x52_0x5e[i];
+
+ /* don't go any further if no extended setup */
+ if (!extended)
+ return;
+
+ /* todo : add more CCD types if needed */
+ /* we might want to expand the Sensor struct to have these
+ 2 kind of settings */
+ if (dev->model->ccd_type == CCD_5345)
+ {
+ if (half_ccd)
+ {
+ /* settings for CCD used at half is max resolution */
+ r = sanei_genesys_get_address (regs, 0x70);
+ r->value = 0x00;
+ r = sanei_genesys_get_address (regs, 0x71);
+ r->value = 0x05;
+ r = sanei_genesys_get_address (regs, 0x72);
+ r->value = 0x06;
+ r = sanei_genesys_get_address (regs, 0x73);
+ r->value = 0x08;
+ r = sanei_genesys_get_address (regs, 0x18);
+ r->value = 0x28;
+ r = sanei_genesys_get_address (regs, 0x58);
+ r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
+ }
+ else
+ {
+ /* swap latch times */
+ r = sanei_genesys_get_address (regs, 0x18);
+ r->value = 0x30;
+ r = sanei_genesys_get_address (regs, 0x52);
+ for (i = 0; i < 6; i++, r++)
+ r->value = dev->sensor.regs_0x52_0x5e[(i + 3) % 6];
+ r = sanei_genesys_get_address (regs, 0x58);
+ r->value = 0x20 | (r->value & 0x03); /* VSMP=4 */
+ }
+ return;
+ }
+
+ if (dev->model->ccd_type == CCD_HP2300)
+ {
+ /* settings for CCD used at half is max resolution */
+ if (half_ccd)
+ {
+ r = sanei_genesys_get_address (regs, 0x70);
+ r->value = 0x16;
+ r = sanei_genesys_get_address (regs, 0x71);
+ r->value = 0x00;
+ r = sanei_genesys_get_address (regs, 0x72);
+ r->value = 0x01;
+ r = sanei_genesys_get_address (regs, 0x73);
+ r->value = 0x03;
+ /* manual clock programming */
+ r = sanei_genesys_get_address (regs, 0x1d);
+ r->value |= 0x80;
+ }
+ else
+ {
+ r = sanei_genesys_get_address (regs, 0x70);
+ r->value = 1;
+ r = sanei_genesys_get_address (regs, 0x71);
+ r->value = 3;
+ r = sanei_genesys_get_address (regs, 0x72);
+ r->value = 4;
+ r = sanei_genesys_get_address (regs, 0x73);
+ r->value = 6;
+ }
+ r = sanei_genesys_get_address (regs, 0x58);
+ r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
+ return;
+ }
+}
+
+/** Test if the ASIC works
+ */
+/*TODO: make this functional*/
+static SANE_Status
+sanei_gl841_asic_test (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val;
+ uint8_t *data;
+ uint8_t *verify_data;
+ size_t size, verify_size;
+ unsigned int i;
+
+ DBG (DBG_proc, "sanei_gl841_asic_test\n");
+
+ return SANE_STATUS_INVAL;
+
+ /* set and read exposure time, compare if it's the same */
+ status = sanei_genesys_write_register (dev, 0x38, 0xde);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: failed to write register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_write_register (dev, 0x39, 0xad);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: failed to write register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_read_register (dev, 0x38, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val != 0xde) /* value of register 0x38 */
+ {
+ DBG (DBG_error,
+ "sanei_gl841_asic_test: register contains invalid value\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status = sanei_genesys_read_register (dev, 0x39, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: failed to read register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (val != 0xad) /* value of register 0x39 */
+ {
+ DBG (DBG_error,
+ "sanei_gl841_asic_test: register contains invalid value\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* ram test: */
+ size = 0x40000;
+ verify_size = size + 0x80;
+ /* todo: looks like the read size must be a multiple of 128?
+ otherwise the read doesn't succeed the second time after the scanner has
+ been plugged in. Very strange. */
+
+ data = (uint8_t *) malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: could not allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ verify_data = (uint8_t *) malloc (verify_size);
+ if (!verify_data)
+ {
+ free (data);
+ DBG (DBG_error, "sanei_gl841_asic_test: could not allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < (size - 1); i += 2)
+ {
+ data[i] = i / 512;
+ data[i + 1] = (i / 2) % 256;
+ }
+
+ status = sanei_genesys_set_buffer_address (dev, 0x0000);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_gl841_asic_test: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+/* status = gl841_bulk_write_data (dev, 0x3c, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_gl841_asic_test: failed to bulk write data: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }*/
+
+ status = sanei_genesys_set_buffer_address (dev, 0x0000);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_gl841_asic_test: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+ status =
+ gl841_bulk_read_data (dev, 0x45, (uint8_t *) verify_data,
+ verify_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: failed to bulk read data: %s\n",
+ sane_strstatus (status));
+ free (data);
+ free (verify_data);
+ return status;
+ }
+
+ /* todo: why i + 2 ? */
+ for (i = 0; i < size; i++)
+ {
+ if (verify_data[i] != data[i])
+ {
+ DBG (DBG_error, "sanei_gl841_asic_test: data verification error\n");
+ DBG (DBG_info, "0x%.8x: got %.2x %.2x %.2x %.2x, expected %.2x %.2x %.2x %.2x\n",
+ i,
+ verify_data[i],
+ verify_data[i+1],
+ verify_data[i+2],
+ verify_data[i+3],
+ data[i],
+ data[i+1],
+ data[i+2],
+ data[i+3]);
+ free (data);
+ free (verify_data);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ free (data);
+ free (verify_data);
+
+ DBG (DBG_info, "sanei_gl841_asic_test: completed\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* returns the max register bulk size */
+static int
+gl841_bulk_full_size (void)
+{
+ return GENESYS_GL841_MAX_REGS;
+}
+
+/*
+ * Set all registers to default values
+ * (function called only once at the beginning)
+ */
+static void
+gl841_init_registers (Genesys_Device * dev)
+{
+ int nr, addr;
+
+ DBG (DBG_proc, "gl841_init_registers\n");
+
+ nr = 0;
+ memset (dev->reg, 0, GENESYS_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ for (addr = 1; addr <= 0x0a; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x10; addr <= 0x27; addr++)
+ dev->reg[nr++].address = addr;
+ dev->reg[nr++].address = 0x29;
+ for (addr = 0x2c; addr <= 0x39; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x3d; addr <= 0x3f; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x52; addr <= 0x5a; addr++)
+ dev->reg[nr++].address = addr;
+ for (addr = 0x5d; addr <= 0x87; addr++)
+ dev->reg[nr++].address = addr;
+
+
+ dev->reg[reg_0x01].value = 0x20; /* (enable shading), CCD, color, 1M */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ dev->reg[reg_0x01].value |= REG01_CISSET;
+ }
+ else
+ {
+ dev->reg[reg_0x01].value &= ~REG01_CISSET;
+ }
+
+ dev->reg[reg_0x02].value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
+ dev->reg[reg_0x02].value |= REG02_AGOHOME;
+ dev->reg[reg_0x02].value |= REG02_MTRPWR;
+ dev->reg[reg_0x02].value |= REG02_FASTFED;
+
+ dev->reg[reg_0x03].value = 0x1f /*0x17 */ ; /* lamp on */
+ dev->reg[reg_0x03].value |= REG03_AVEENB;
+
+ if (dev->model->ccd_type == CCD_PLUSTEK_3600) /* AD front end */
+ {
+ dev->reg[reg_0x04].value = (2 << REG04S_AFEMOD) | 0x02;
+ }
+ else /* Wolfson front end */
+ {
+ dev->reg[reg_0x04].value |= 1 << REG04S_AFEMOD;
+ }
+
+ dev->reg[reg_0x05].value = 0x00; /* disable gamma, 24 clocks/pixel */
+ if (dev->sensor.sensor_pixels < 0x1500)
+ dev->reg[reg_0x05].value |= REG05_DPIHW_600;
+ else if (dev->sensor.sensor_pixels < 0x2a80)
+ dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
+ else if (dev->sensor.sensor_pixels < 0x5400)
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ else
+ {
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ DBG (DBG_warn,
+ "gl841_init_registers: Cannot handle sensor pixel count %d\n",
+ dev->sensor.sensor_pixels);
+ }
+
+
+ dev->reg[reg_0x06].value |= REG06_PWRBIT;
+ dev->reg[reg_0x06].value |= REG06_GAIN4;
+
+ /* XP300 CCD needs different clock and clock/pixels values */
+ if (dev->model->ccd_type != CCD_XP300 && dev->model->ccd_type != CCD_DP685
+ && dev->model->ccd_type != CCD_PLUSTEK_3600)
+ {
+ dev->reg[reg_0x06].value |= 0 << REG06S_SCANMOD;
+ dev->reg[reg_0x09].value |= 1 << REG09S_CLKSET;
+ }
+ else
+ {
+ dev->reg[reg_0x06].value |= 0x05 << REG06S_SCANMOD; /* 15 clocks/pixel */
+ dev->reg[reg_0x09].value = 0; /* 24 MHz CLKSET */
+ }
+
+ dev->reg[reg_0x1e].value = 0xf0; /* watch-dog time */
+
+ dev->reg[reg_0x17].value |= 1 << REG17S_TGW;
+
+ dev->reg[reg_0x19].value = 0x50;
+
+ dev->reg[reg_0x1d].value |= 1 << REG1DS_TGSHLD;
+
+ dev->reg[reg_0x1e].value |= 1 << REG1ES_WDTIME;
+
+/*SCANFED*/
+ dev->reg[reg_0x1f].value = 0x01;
+
+/*BUFSEL*/
+ dev->reg[reg_0x20].value = 0x20;
+
+/*LAMPPWM*/
+ dev->reg[reg_0x29].value = 0xff;
+
+/*BWHI*/
+ dev->reg[reg_0x2e].value = 0x80;
+
+/*BWLOW*/
+ dev->reg[reg_0x2f].value = 0x80;
+
+/*LPERIOD*/
+ dev->reg[reg_0x38].value = 0x4f;
+ dev->reg[reg_0x39].value = 0xc1;
+
+/*VSMPW*/
+ dev->reg[reg_0x58].value |= 3 << REG58S_VSMPW;
+
+/*BSMPW*/
+ dev->reg[reg_0x59].value |= 3 << REG59S_BSMPW;
+
+/*RLCSEL*/
+ dev->reg[reg_0x5a].value |= REG5A_RLCSEL;
+
+/*STOPTIM*/
+ dev->reg[reg_0x5e].value |= 0x2 << REG5ES_STOPTIM;
+
+ sanei_gl841_setup_sensor (dev, dev->reg, 0, 0);
+
+ /* set up GPIO */
+ dev->reg[reg_0x6c].value = dev->gpo.value[0];
+ dev->reg[reg_0x6d].value = dev->gpo.value[1];
+ dev->reg[reg_0x6e].value = dev->gpo.enable[0];
+ dev->reg[reg_0x6f].value = dev->gpo.enable[1];
+
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+ dev->reg[reg_0x6b].value |= REG6B_GPO18;
+ dev->reg[reg_0x6b].value &= ~REG6B_GPO17;
+ }
+
+ if (dev->model->gpo_type == GPO_XP300)
+ {
+ dev->reg[reg_0x6b].value |= REG6B_GPO17;
+ }
+
+ if (dev->model->gpo_type == GPO_DP685)
+ {
+ /* REG6B_GPO18 lights on green led */
+ dev->reg[reg_0x6b].value |= REG6B_GPO17|REG6B_GPO18;
+ }
+
+ DBG (DBG_proc, "gl841_init_registers complete\n");
+}
+
+/* Send slope table for motor movement
+ slope_table in machine byte order
+ */
+static SANE_Status
+gl841_send_slope_table (Genesys_Device * dev, int table_nr,
+ uint16_t * slope_table, int steps)
+{
+ int dpihw;
+ int start_address;
+ SANE_Status status;
+ uint8_t *table;
+/*#ifdef WORDS_BIGENDIAN*/
+ int i;
+/*#endif*/
+
+ DBG (DBG_proc, "gl841_send_slope_table (table_nr = %d, steps = %d)\n",
+ table_nr, steps);
+
+ dpihw = dev->reg[reg_0x05].value >> 6;
+
+ if (dpihw == 0) /* 600 dpi */
+ start_address = 0x08000;
+ else if (dpihw == 1) /* 1200 dpi */
+ start_address = 0x10000;
+ else if (dpihw == 2) /* 2400 dpi */
+ start_address = 0x20000;
+ else /* reserved */
+ return SANE_STATUS_INVAL;
+
+/*#ifdef WORDS_BIGENDIAN*/
+ table = (uint8_t*)malloc(steps * 2);
+ for(i = 0; i < steps; i++) {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+/*#else
+ table = (uint8_t*)slope_table;
+ #endif*/
+
+ status =
+ sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x200);
+ if (status != SANE_STATUS_GOOD)
+ {
+/*#ifdef WORDS_BIGENDIAN*/
+ free(table);
+/*#endif*/
+ DBG (DBG_error,
+ "gl841_send_slope_table: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ gl841_bulk_write_data (dev, 0x3c, (uint8_t *) table,
+ steps * 2);
+ if (status != SANE_STATUS_GOOD)
+ {
+/*#ifdef WORDS_BIGENDIAN*/
+ free(table);
+/*#endif*/
+ DBG (DBG_error,
+ "gl841_send_slope_table: failed to send slope table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+/*#ifdef WORDS_BIGENDIAN*/
+ free(table);
+/*#endif*/
+ DBG (DBG_proc, "gl841_send_slope_table: completed\n");
+ return status;
+}
+
+/* Set values of Analog Device type frontend */
+static SANE_Status
+gl841_set_ad_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ DBG (DBG_proc, "gl841_set_ad_fe(): start\n");
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl841_set_ad_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+
+ /* sets to default values */
+ sanei_genesys_init_fe (dev);
+
+ /* write them to analog frontend */
+ status = sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing reg 0x00 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x01, dev->frontend.reg[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing reg 0x01 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x02 + i, 0x00);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_ad_fe: writing sign[%d] failed: %s\n", 0x02 + i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ }
+ if (set == AFE_SET)
+ {
+ /* write them to analog frontend */
+ status = sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing reg 0x00 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x01, dev->frontend.reg[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing reg 0x01 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Write fe 0x02 (red gain)*/
+ status = sanei_genesys_fe_write_data (dev, 0x02, dev->frontend.gain[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing fe 0x02 (gain r) fail: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Write fe 0x03 (green gain)*/
+ status = sanei_genesys_fe_write_data (dev, 0x03, dev->frontend.gain[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing fe 0x03 (gain g) fail: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Write fe 0x04 (blue gain)*/
+ status = sanei_genesys_fe_write_data (dev, 0x04, dev->frontend.gain[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: writing fe 0x04 (gain b) fail: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Write fe 0x05 (red offset)*/
+ status =
+ sanei_genesys_fe_write_data (dev, 0x05, dev->frontend.offset[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: write fe 0x05 (offset r) fail: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Write fe 0x06 (green offset)*/
+ status =
+ sanei_genesys_fe_write_data (dev, 0x06, dev->frontend.offset[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: write fe 0x06 (offset g) fail: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* Write fe 0x07 (blue offset)*/
+ status =
+ sanei_genesys_fe_write_data (dev, 0x07, dev->frontend.offset[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_ad_fe: write fe 0x07 (offset b) fail: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ DBG (DBG_proc, "gl841_set_ad_fe(): end\n");
+
+ return status;
+}
+
+/* Set values of analog frontend */
+static SANE_Status
+gl841_set_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "gl841_set_fe (%s)\n",
+ set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
+ AFE_POWER_SAVE ? "powersave" : "huh?");
+
+ /* Analog Device type frontend */
+ if ((dev->reg[reg_0x04].value & REG04_FESET) == 0x02)
+ {
+ return gl841_set_ad_fe (dev, set);
+ }
+
+ if ((dev->reg[reg_0x04].value & REG04_FESET) != 0x00)
+ {
+ DBG (DBG_proc, "gl841_set_fe(): unsupported frontend type %d\n",
+ dev->reg[reg_0x04].value & REG04_FESET);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl841_set_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+ sanei_genesys_init_fe (dev);
+
+ /* reset only done on init */
+ status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: reset fe failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ /*
+ if (dev->model->ccd_type == CCD_HP2300
+ || dev->model->ccd_type == CCD_HP2400)
+ {
+ val = 0x07;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT,
+ REQUEST_REGISTER, GPIO_OUTPUT_ENABLE,
+ INDEX, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_fe failed resetting frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }*/
+ }
+ DBG (DBG_proc, "gl841_set_fe(): frontend reset complete\n");
+ }
+
+
+ if (set == AFE_POWER_SAVE)
+ {
+ status = sanei_genesys_fe_write_data (dev, 0x01, 0x02);
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "gl841_set_fe: writing data failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* todo : base this test on cfg reg3 or a CCD family flag to be created */
+ /*if (dev->model->ccd_type!=CCD_HP2300 && dev->model->ccd_type!=CCD_HP2400) */
+ {
+
+ status = sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg0 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ status = sanei_genesys_fe_write_data (dev, 0x02, dev->frontend.reg[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg2 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x01, dev->frontend.reg[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg1 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x03, dev->frontend.reg[3]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg3 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x06, dev->frontend.reg2[0]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg6 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x08, dev->frontend.reg2[1]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg8 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_fe_write_data (dev, 0x09, dev->frontend.reg2[2]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl841_set_fe: writing reg9 failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x24 + i, dev->frontend.sign[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_fe: writing sign[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x28 + i, dev->frontend.gain[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_fe: writing gain[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_genesys_fe_write_data (dev, 0x20 + i,
+ dev->frontend.offset[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_set_fe: writing offset[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+
+ DBG (DBG_proc, "gl841_set_fe: completed\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+#define MOTOR_ACTION_FEED 1
+#define MOTOR_ACTION_GO_HOME 2
+#define MOTOR_ACTION_HOME_FREE 3
+
+/** @brief turn off motor
+ *
+ */
+static SANE_Status
+gl841_init_motor_regs_off(Genesys_Register_Set * reg,
+ unsigned int scan_lines)
+{
+ unsigned int feedl;
+ Genesys_Register_Set * r;
+
+ DBG (DBG_proc, "gl841_init_motor_regs_off : scan_lines=%d\n",
+ scan_lines);
+
+ feedl = 2;
+
+ r = sanei_genesys_get_address (reg, 0x3d);
+ r->value = (feedl >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x3e);
+ r->value = (feedl >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x3f);
+ r->value = feedl & 0xff;
+ r = sanei_genesys_get_address (reg, 0x5e);
+ r->value &= ~0xe0;
+
+ r = sanei_genesys_get_address (reg, 0x25);
+ r->value = (scan_lines >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x26);
+ r->value = (scan_lines >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x27);
+ r->value = scan_lines & 0xff;
+
+ r = sanei_genesys_get_address (reg, 0x02);
+ r->value &= ~0x01; /*LONGCURV OFF*/
+ r->value &= ~0x80; /*NOT_HOME OFF*/
+
+ r->value &= ~0x10;
+
+ r->value &= ~0x06;
+
+ r->value &= ~0x08;
+
+ r->value &= ~0x20;
+
+ r->value &= ~0x40;
+
+ r = sanei_genesys_get_address (reg, 0x67);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address (reg, 0x68);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address (reg, 0x21);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x24);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x69);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x6a);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x5f);
+ r->value = 0;
+
+
+ DBG (DBG_proc, "gl841_init_motor_regs_off : completed. \n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_init_motor_regs(Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int feed_steps,/*1/base_ydpi*/
+/*maybe float for half/quarter step resolution?*/
+ unsigned int action,
+ unsigned int flags)
+{
+ SANE_Status status;
+ unsigned int fast_exposure;
+ int use_fast_fed = 0;
+ uint16_t fast_slope_table[256];
+ unsigned int fast_slope_steps = 0;
+ unsigned int feedl;
+ Genesys_Register_Set * r;
+/*number of scan lines to add in a scan_lines line*/
+
+ DBG (DBG_proc, "gl841_init_motor_regs : feed_steps=%d, action=%d, flags=%x\n",
+ feed_steps,
+ action,
+ flags);
+
+ memset(fast_slope_table,0xff,512);
+
+ gl841_send_slope_table (dev, 0, fast_slope_table, 256);
+ gl841_send_slope_table (dev, 1, fast_slope_table, 256);
+ gl841_send_slope_table (dev, 2, fast_slope_table, 256);
+ gl841_send_slope_table (dev, 3, fast_slope_table, 256);
+ gl841_send_slope_table (dev, 4, fast_slope_table, 256);
+
+
+ if (action == MOTOR_ACTION_FEED || action == MOTOR_ACTION_GO_HOME) {
+/* FEED and GO_HOME can use fastest slopes available */
+ fast_slope_steps = 256;
+ fast_exposure = sanei_genesys_exposure_time2(
+ dev,
+ dev->motor.base_ydpi / 4,
+ 0,/*step_type*/
+ 0,/*last used pixel*/
+ 0,
+ 0);
+
+ DBG (DBG_info, "gl841_init_motor_regs : fast_exposure=%d pixels\n",
+ fast_exposure);
+ }
+
+ if (action == MOTOR_ACTION_HOME_FREE) {
+/* HOME_FREE must be able to stop in one step, so do not try to get faster */
+ fast_slope_steps = 256;
+ fast_exposure = dev->motor.slopes[0][0].maximum_start_speed;
+ }
+
+ sanei_genesys_create_slope_table3 (
+ dev,
+ fast_slope_table, 256,
+ fast_slope_steps,
+ 0,
+ fast_exposure,
+ dev->motor.base_ydpi / 4,
+ &fast_slope_steps,
+ &fast_exposure, 0);
+
+ feedl = feed_steps - fast_slope_steps*2;
+ use_fast_fed = 1;
+
+/* all needed slopes available. we did even decide which mode to use.
+ what next?
+ - transfer slopes
+SCAN:
+flags \ use_fast_fed ! 0 1
+------------------------\--------------------
+ 0 ! 0,1,2 0,1,2,3
+MOTOR_FLAG_AUTO_GO_HOME ! 0,1,2,4 0,1,2,3,4
+OFF: none
+FEED: 3
+GO_HOME: 3
+HOME_FREE: 3
+ - setup registers
+ * slope specific registers (already done)
+ * DECSEL for HOME_FREE/GO_HOME/SCAN
+ * FEEDL
+ * MTRREV
+ * MTRPWR
+ * FASTFED
+ * STEPSEL
+ * MTRPWM
+ * FSTPSEL
+ * FASTPWM
+ * HOMENEG
+ * BWDSTEP
+ * FWDSTEP
+ * Z1
+ * Z2
+ */
+
+ r = sanei_genesys_get_address (reg, 0x3d);
+ r->value = (feedl >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x3e);
+ r->value = (feedl >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x3f);
+ r->value = feedl & 0xff;
+ r = sanei_genesys_get_address (reg, 0x5e);
+ r->value &= ~0xe0;
+
+ r = sanei_genesys_get_address (reg, 0x25);
+ r->value = 0;
+ r = sanei_genesys_get_address (reg, 0x26);
+ r->value = 0;
+ r = sanei_genesys_get_address (reg, 0x27);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x02);
+ r->value &= ~0x01; /*LONGCURV OFF*/
+ r->value &= ~0x80; /*NOT_HOME OFF*/
+
+ r->value |= 0x10;
+
+ if (action == MOTOR_ACTION_GO_HOME)
+ r->value |= 0x06;
+ else
+ r->value &= ~0x06;
+
+ if (use_fast_fed)
+ r->value |= 0x08;
+ else
+ r->value &= ~0x08;
+
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME)
+ r->value |= 0x20;
+ else
+ r->value &= ~0x20;
+
+ r->value &= ~0x40;
+
+ status = gl841_send_slope_table (dev, 3, fast_slope_table, 256);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ r = sanei_genesys_get_address (reg, 0x67);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address (reg, 0x68);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address (reg, 0x21);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x24);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x69);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x6a);
+ r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
+
+ r = sanei_genesys_get_address (reg, 0x5f);
+ r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
+
+
+ DBG (DBG_proc, "gl841_init_motor_regs : completed. \n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_init_motor_regs_scan(Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int scan_exposure_time,/*pixel*/
+ float scan_yres,/*dpi, motor resolution*/
+ int scan_step_type,/*0: full, 1: half, 2: quarter*/
+ unsigned int scan_lines,/*lines, scan resolution*/
+ unsigned int scan_dummy,
+/*number of scan lines to add in a scan_lines line*/
+ unsigned int feed_steps,/*1/base_ydpi*/
+/*maybe float for half/quarter step resolution?*/
+ int scan_power_mode,
+ unsigned int flags)
+{
+ SANE_Status status;
+ unsigned int fast_exposure;
+ int use_fast_fed = 0;
+ unsigned int fast_time;
+ unsigned int slow_time;
+ uint16_t slow_slope_table[256];
+ uint16_t fast_slope_table[256];
+ uint16_t back_slope_table[256];
+ unsigned int slow_slope_time;
+ unsigned int fast_slope_time;
+ unsigned int slow_slope_steps = 0;
+ unsigned int fast_slope_steps = 0;
+ unsigned int back_slope_steps = 0;
+ unsigned int feedl;
+ Genesys_Register_Set * r;
+ unsigned int min_restep = 0x20;
+ uint32_t z1, z2;
+
+ DBG (DBG_proc, "gl841_init_motor_regs_scan : scan_exposure_time=%d, "
+ "scan_yres=%g, scan_step_type=%d, scan_lines=%d, scan_dummy=%d, "
+ "feed_steps=%d, scan_power_mode=%d, flags=%x\n",
+ scan_exposure_time,
+ scan_yres,
+ scan_step_type,
+ scan_lines,
+ scan_dummy,
+ feed_steps,
+ scan_power_mode,
+ flags);
+
+ fast_exposure = sanei_genesys_exposure_time2(
+ dev,
+ dev->motor.base_ydpi / 4,
+ 0,/*step_type*/
+ 0,/*last used pixel*/
+ 0,
+ scan_power_mode);
+
+ DBG (DBG_info, "gl841_init_motor_regs_scan : fast_exposure=%d pixels\n",
+ fast_exposure);
+
+
+ memset(slow_slope_table,0xff,512);
+
+ gl841_send_slope_table (dev, 0, slow_slope_table, 256);
+ gl841_send_slope_table (dev, 1, slow_slope_table, 256);
+ gl841_send_slope_table (dev, 2, slow_slope_table, 256);
+ gl841_send_slope_table (dev, 3, slow_slope_table, 256);
+ gl841_send_slope_table (dev, 4, slow_slope_table, 256);
+
+
+/*
+ we calculate both tables for SCAN. the fast slope step count depends on
+ how many steps we need for slow acceleration and how much steps we are
+ allowed to use.
+ */
+ slow_slope_time = sanei_genesys_create_slope_table3 (
+ dev,
+ slow_slope_table, 256,
+ 256,
+ scan_step_type,
+ scan_exposure_time,
+ scan_yres,
+ &slow_slope_steps,
+ NULL,
+ scan_power_mode);
+
+ sanei_genesys_create_slope_table3 (
+ dev,
+ back_slope_table, 256,
+ 256,
+ scan_step_type,
+ 0,
+ scan_yres,
+ &back_slope_steps,
+ NULL,
+ scan_power_mode);
+
+ if (feed_steps < (slow_slope_steps >> scan_step_type)) {
+ /*TODO: what should we do here?? go back to exposure calculation?*/
+ feed_steps = slow_slope_steps >> scan_step_type;
+ }
+
+ if (feed_steps > fast_slope_steps*2 -
+ (slow_slope_steps >> scan_step_type))
+ fast_slope_steps = 256;
+ else
+/* we need to shorten fast_slope_steps here. */
+ fast_slope_steps = (feed_steps -
+ (slow_slope_steps >> scan_step_type))/2;
+
+ DBG(DBG_info,"gl841_init_motor_regs_scan: Maximum allowed slope steps for fast slope: %d\n",fast_slope_steps);
+
+ fast_slope_time = sanei_genesys_create_slope_table3 (
+ dev,
+ fast_slope_table, 256,
+ fast_slope_steps,
+ 0,
+ fast_exposure,
+ dev->motor.base_ydpi / 4,
+ &fast_slope_steps,
+ &fast_exposure,
+ scan_power_mode);
+
+ if (dev->model->gpo_type == GPO_XP300 || dev->model->gpo_type == GPO_DP685)
+ {
+ /* quirk: looks like at least this scanner is unable to use
+ 2-feed mode */
+ use_fast_fed = 0;
+ }
+ else if (feed_steps < fast_slope_steps*2 + (slow_slope_steps >> scan_step_type)) {
+ use_fast_fed = 0;
+ DBG(DBG_info,"gl841_init_motor_regs_scan: feed too short, slow move forced.\n");
+ } else {
+/* for deciding whether we should use fast mode we need to check how long we
+ need for (fast)accelerating, moving, decelerating, (TODO: stopping?)
+ (slow)accelerating again versus (slow)accelerating and moving. we need
+ fast and slow tables here.
+*/
+/*NOTE: scan_exposure_time is per scan_yres*/
+/*NOTE: fast_exposure is per base_ydpi/4*/
+/*we use full steps as base unit here*/
+ fast_time =
+ fast_exposure / 4 *
+ (feed_steps - fast_slope_steps*2 -
+ (slow_slope_steps >> scan_step_type))
+ + fast_slope_time*2 + slow_slope_time;
+ slow_time =
+ (scan_exposure_time * scan_yres) / dev->motor.base_ydpi *
+ (feed_steps - (slow_slope_steps >> scan_step_type))
+ + slow_slope_time;
+
+ DBG(DBG_info,"gl841_init_motor_regs_scan: Time for slow move: %d\n",
+ slow_time);
+ DBG(DBG_info,"gl841_init_motor_regs_scan: Time for fast move: %d\n",
+ fast_time);
+
+ use_fast_fed = fast_time < slow_time;
+ }
+
+ if (use_fast_fed)
+ feedl = feed_steps - fast_slope_steps*2 -
+ (slow_slope_steps >> scan_step_type);
+ else
+ if ((feed_steps << scan_step_type) < slow_slope_steps)
+ feedl = 0;
+ else
+ feedl = (feed_steps << scan_step_type) - slow_slope_steps;
+ DBG(DBG_info,"gl841_init_motor_regs_scan: Decided to use %s mode\n",
+ use_fast_fed?"fast feed":"slow feed");
+
+/* all needed slopes available. we did even decide which mode to use.
+ what next?
+ - transfer slopes
+SCAN:
+flags \ use_fast_fed ! 0 1
+------------------------\--------------------
+ 0 ! 0,1,2 0,1,2,3
+MOTOR_FLAG_AUTO_GO_HOME ! 0,1,2,4 0,1,2,3,4
+OFF: none
+FEED: 3
+GO_HOME: 3
+HOME_FREE: 3
+ - setup registers
+ * slope specific registers (already done)
+ * DECSEL for HOME_FREE/GO_HOME/SCAN
+ * FEEDL
+ * MTRREV
+ * MTRPWR
+ * FASTFED
+ * STEPSEL
+ * MTRPWM
+ * FSTPSEL
+ * FASTPWM
+ * HOMENEG
+ * BWDSTEP
+ * FWDSTEP
+ * Z1
+ * Z2
+ */
+
+ r = sanei_genesys_get_address (reg, 0x3d);
+ r->value = (feedl >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x3e);
+ r->value = (feedl >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x3f);
+ r->value = feedl & 0xff;
+ r = sanei_genesys_get_address (reg, 0x5e);
+ r->value &= ~0xe0;
+
+ r = sanei_genesys_get_address (reg, 0x25);
+ r->value = (scan_lines >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x26);
+ r->value = (scan_lines >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x27);
+ r->value = scan_lines & 0xff;
+
+ r = sanei_genesys_get_address (reg, 0x02);
+ r->value &= ~0x01; /*LONGCURV OFF*/
+ r->value &= ~0x80; /*NOT_HOME OFF*/
+ r->value |= 0x10;
+
+ r->value &= ~0x06;
+
+ if (use_fast_fed)
+ r->value |= 0x08;
+ else
+ r->value &= ~0x08;
+
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME)
+ r->value |= 0x20;
+ else
+ r->value &= ~0x20;
+
+ if (flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ r->value |= 0x40;
+ else
+ r->value &= ~0x40;
+
+ status = gl841_send_slope_table (dev, 0, slow_slope_table, 256);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = gl841_send_slope_table (dev, 1, back_slope_table, 256);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = gl841_send_slope_table (dev, 2, slow_slope_table, 256);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (use_fast_fed) {
+ status = gl841_send_slope_table (dev, 3, fast_slope_table, 256);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME){
+ status = gl841_send_slope_table (dev, 4, fast_slope_table, 256);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+
+/* now reg 0x21 and 0x24 are available, we can calculate reg 0x22 and 0x23,
+ reg 0x60-0x62 and reg 0x63-0x65
+ rule:
+ 2*STEPNO+FWDSTEP=2*FASTNO+BWDSTEP
+*/
+/* steps of table 0*/
+ if (min_restep < slow_slope_steps*2+2)
+ min_restep = slow_slope_steps*2+2;
+/* steps of table 1*/
+ if (min_restep < back_slope_steps*2+2)
+ min_restep = back_slope_steps*2+2;
+/* steps of table 0*/
+ r = sanei_genesys_get_address (reg, 0x22);
+ r->value = min_restep - slow_slope_steps*2;
+/* steps of table 1*/
+ r = sanei_genesys_get_address (reg, 0x23);
+ r->value = min_restep - back_slope_steps*2;
+
+/*
+ for z1/z2:
+ in dokumentation mentioned variables a-d:
+ a = time needed for acceleration, table 1
+ b = time needed for reg 0x1f... wouldn't that be reg0x1f*exposure_time?
+ c = time needed for acceleration, table 1
+ d = time needed for reg 0x22... wouldn't that be reg0x22*exposure_time?
+ z1 = (c+d-1) % exposure_time
+ z2 = (a+b-1) % exposure_time
+*/
+/* i don't see any effect of this. i can only guess that this will enhance
+ sub-pixel accuracy
+ z1 = (slope_0_time-1) % exposure_time;
+ z2 = (slope_0_time-1) % exposure_time;
+*/
+ z1 = z2 = 0;
+
+ DBG (DBG_info, "gl841_init_motor_regs_scan: z1 = %d\n", z1);
+ DBG (DBG_info, "gl841_init_motor_regs_scan: z2 = %d\n", z2);
+ r = sanei_genesys_get_address (reg, 0x60);
+ r->value = ((z1 >> 16) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x61);
+ r->value = ((z1 >> 8) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x62);
+ r->value = (z1 & 0xff);
+ r = sanei_genesys_get_address (reg, 0x63);
+ r->value = ((z2 >> 16) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x64);
+ r->value = ((z2 >> 8) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x65);
+ r->value = (z2 & 0xff);
+
+ r = sanei_genesys_get_address (reg, 0x1e);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ r = sanei_genesys_get_address (reg, 0x67);
+ r->value = 0x3f | (scan_step_type << 6);
+
+ r = sanei_genesys_get_address (reg, 0x68);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address (reg, 0x21);
+ r->value = (slow_slope_steps >> 1) + (slow_slope_steps & 1);
+
+ r = sanei_genesys_get_address (reg, 0x24);
+ r->value = (back_slope_steps >> 1) + (back_slope_steps & 1);
+
+ r = sanei_genesys_get_address (reg, 0x69);
+ r->value = (slow_slope_steps >> 1) + (slow_slope_steps & 1);
+
+ r = sanei_genesys_get_address (reg, 0x6a);
+ r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
+
+ r = sanei_genesys_get_address (reg, 0x5f);
+ r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
+
+
+ DBG (DBG_proc, "gl841_init_motor_regs_scan : completed. \n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static int
+gl841_get_dpihw(Genesys_Device * dev)
+{
+ Genesys_Register_Set * r;
+ r = sanei_genesys_get_address (dev->reg, 0x05);
+ if ((r->value & REG05_DPIHW) == REG05_DPIHW_600)
+ return 600;
+ if ((r->value & REG05_DPIHW) == REG05_DPIHW_1200)
+ return 1200;
+ if ((r->value & REG05_DPIHW) == REG05_DPIHW_2400)
+ return 2400;
+ return 0;
+}
+
+static SANE_Status
+gl841_init_optical_regs_off(Genesys_Register_Set * reg)
+{
+ Genesys_Register_Set * r;
+
+ DBGSTART;
+
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG01_SCAN;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_init_optical_regs_scan(Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int exposure_time,
+ unsigned int used_res,
+ unsigned int start,
+ unsigned int pixels,
+ int channels,
+ int depth,
+ SANE_Bool half_ccd,
+ int color_filter,
+ int flags
+ )
+{
+ unsigned int words_per_line;
+ unsigned int end;
+ unsigned int dpiset;
+ unsigned int i;
+ Genesys_Register_Set * r;
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl841_init_optical_regs_scan : exposure_time=%d, "
+ "used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
+ "half_ccd=%d, flags=%x\n",
+ exposure_time,
+ used_res,
+ start,
+ pixels,
+ channels,
+ depth,
+ half_ccd,
+ flags);
+
+ end = start + pixels;
+
+ status = gl841_set_fe (dev, AFE_SET);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init_optical_regs_scan: failed to set frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* adjust used_res for chosen dpihw */
+ used_res = used_res * gl841_get_dpihw(dev) / dev->sensor.optical_res;
+
+/*
+ with half_ccd the optical resolution of the ccd is halfed. We don't apply this
+ to dpihw, so we need to double dpiset.
+
+ For the scanner only the ratio of dpiset and dpihw is of relevance to scale
+ down properly.
+*/
+ if (half_ccd)
+ dpiset = used_res * 2;
+ else
+ dpiset = used_res;
+
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+/* gpio part.*/
+ r = sanei_genesys_get_address (reg, REG6C);
+ if (half_ccd)
+ r->value &= ~0x80;
+ else
+ r->value |= 0x80;
+ }
+
+ /* enable shading */
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value |= REG01_SCAN;
+ if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ r->value &= ~REG01_DVDSET;
+ else
+ r->value |= REG01_DVDSET;
+
+ /* average looks better than deletion, and we are already set up to
+ use one of the average enabled resolutions
+ */
+ r = sanei_genesys_get_address (reg, 0x03);
+ r->value |= REG03_AVEENB;
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value &= ~REG03_LAMPPWR;
+ else
+ r->value |= REG03_LAMPPWR;
+
+ /* exposure times */
+ r = sanei_genesys_get_address (reg, 0x10);
+ for (i = 0; i < 6; i++, r++) {
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value = 0x01;/* 0x0101 is as off as possible */
+ else
+ { /* EXP[R,G,B] only matter for CIS scanners */
+ if (dev->sensor.regs_0x10_0x1d[i] == 0x00)
+ r->value = 0x01; /*0x00 will not be accepted*/
+ else
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+ }
+
+ r = sanei_genesys_get_address (reg, 0x19);
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value = 0xff;
+ else
+ r->value = 0x50;
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, 0x2e);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, 0x2f);
+ r->value = dev->settings.threshold;
+
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, 0x04);
+ switch (depth) {
+ case 1:
+ r->value &= ~REG04_BITSET;
+ r->value |= REG04_LINEART;
+ break;
+ case 8:
+ r->value &= ~(REG04_LINEART | REG04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG04_LINEART;
+ r->value |= REG04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG04_FILTER | REG04_AFEMOD);
+ if (channels == 1)
+ {
+ switch (color_filter)
+ {
+ case 0:
+ r->value |= 0x14; /* red filter */
+ break;
+ case 2:
+ r->value |= 0x1c; /* blue filter */
+ break;
+ default:
+ r->value |= 0x18; /* green filter */
+ break;
+ }
+ }
+ else
+ {
+ if (dev->model->ccd_type == CCD_PLUSTEK_3600)
+ {
+ r->value |= 0x22; /* slow color pixel by pixel */
+ }
+ else
+ {
+ r->value |= 0x10; /* color pixel by pixel */
+ }
+ }
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ r = sanei_genesys_get_address (reg, 0x87);
+ r->value &= ~REG87_LEDADD;
+ /* we set up LEDADD only when asked */
+ if (channels==1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG87_LEDADD;
+ }
+ }
+
+ /* enable gamma tables */
+ r = sanei_genesys_get_address (reg, 0x05);
+ if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
+ r->value &= ~REG05_GMMENB;
+ else
+ r->value |= REG05_GMMENB;
+
+ /* sensor parameters */
+ sanei_gl841_setup_sensor (dev, dev->reg, 1, half_ccd);
+
+ r = sanei_genesys_get_address (reg, 0x29);
+ r->value = 255; /*<<<"magic" number, only suitable for cis*/
+
+ r = sanei_genesys_get_address (reg, 0x2c);
+ r->value = HIBYTE (dpiset);
+ r = sanei_genesys_get_address (reg, 0x2d);
+ r->value = LOBYTE (dpiset);
+
+ r = sanei_genesys_get_address (reg, 0x30);
+ r->value = HIBYTE (start);
+ r = sanei_genesys_get_address (reg, 0x31);
+ r->value = LOBYTE (start);
+ r = sanei_genesys_get_address (reg, 0x32);
+ r->value = HIBYTE (end);
+ r = sanei_genesys_get_address (reg, 0x33);
+ r->value = LOBYTE (end);
+
+/* words(16bit) before gamma, conversion to 8 bit or lineart*/
+ words_per_line = (pixels * dpiset) / gl841_get_dpihw(dev);
+
+ words_per_line *= channels;
+
+ if (depth == 1)
+ words_per_line = (words_per_line >> 3) + ((words_per_line & 7)?1:0);
+ else
+ words_per_line *= depth / 8;
+
+ dev->wpl = words_per_line;
+ dev->bpl = words_per_line;
+
+ r = sanei_genesys_get_address (reg, 0x35);
+ r->value = LOBYTE (HIWORD (words_per_line));
+ r = sanei_genesys_get_address (reg, 0x36);
+ r->value = HIBYTE (LOWORD (words_per_line));
+ r = sanei_genesys_get_address (reg, 0x37);
+ r->value = LOBYTE (LOWORD (words_per_line));
+
+ r = sanei_genesys_get_address (reg, 0x38);
+ r->value = HIBYTE (exposure_time);
+ r = sanei_genesys_get_address (reg, 0x39);
+ r->value = LOBYTE (exposure_time);
+
+ r = sanei_genesys_get_address (reg, 0x34);
+ r->value = dev->sensor.dummy_pixel;
+
+ DBG (DBG_proc, "gl841_init_optical_regs_scan : completed. \n");
+ return SANE_STATUS_GOOD;
+}
+
+
+static int
+gl841_get_led_exposure(Genesys_Device * dev)
+{
+ int d,r,g,b,m;
+ if (!dev->model->is_cis)
+ return 0;
+ d = dev->reg[reg_0x19].value;
+ r = dev->sensor.regs_0x10_0x1d[1] | (dev->sensor.regs_0x10_0x1d[0] << 8);
+ g = dev->sensor.regs_0x10_0x1d[3] | (dev->sensor.regs_0x10_0x1d[2] << 8);
+ b = dev->sensor.regs_0x10_0x1d[5] | (dev->sensor.regs_0x10_0x1d[4] << 8);
+
+ m = r;
+ if (m < g)
+ m = g;
+ if (m < b)
+ m = b;
+
+ return m + d;
+}
+
+/* set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl841_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres,/*dpi*/
+ float yres,/*dpi*/
+ float startx,/*optical_res, from dummy_pixel+1*/
+ float starty,/*base_ydpi, from home!*/
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags
+ )
+{
+ int used_res;
+ int start, used_pixels;
+ int bytes_per_line;
+ int move;
+ unsigned int lincnt;
+ int exposure_time, exposure_time2, led_exposure;
+ int i;
+ int stagger;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+ int scan_step_type = 1;
+ int scan_power_mode = 0;
+ int max_shift;
+ size_t requested_buffer_size, read_buffer_size;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl841_init_scan_regs settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g/%g\n"
+ "Depth/Channels: %u/%u\n"
+ "Flags : %x\n\n",
+ xres, yres, lines, pixels,
+ startx, starty,
+ depth, channels,
+ flags);
+
+/*
+results:
+
+for scanner:
+half_ccd
+start
+end
+dpiset
+exposure_time
+dummy
+z1
+z2
+
+for ordered_read:
+ dev->words_per_line
+ dev->read_factor
+ dev->requested_buffer_size
+ dev->read_buffer_size
+ dev->read_pos
+ dev->read_bytes_in_buffer
+ dev->read_bytes_left
+ dev->max_shift
+ dev->stagger
+
+independent of our calculated values:
+ dev->total_bytes_read
+ dev->bytes_to_read
+ */
+
+/* half_ccd */
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if (dev->sensor.optical_res < 2 * xres ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE)) {
+ half_ccd = SANE_FALSE;
+ } else {
+ half_ccd = SANE_TRUE;
+ }
+
+/* optical_res */
+
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 2;
+
+/* stagger */
+
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl841_init_scan_regs : stagger=%d lines\n",
+ stagger);
+
+/* used_res */
+ i = optical_res / xres;
+
+/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
+
+ if (i < 2 || (flags & SCAN_FLAG_USE_OPTICAL_RES)) /* optical_res >= xres > optical_res/2 */
+ used_res = optical_res;
+ else if (i < 3) /* optical_res/2 >= xres > optical_res/3 */
+ used_res = optical_res/2;
+ else if (i < 4) /* optical_res/3 >= xres > optical_res/4 */
+ used_res = optical_res/3;
+ else if (i < 5) /* optical_res/4 >= xres > optical_res/5 */
+ used_res = optical_res/4;
+ else if (i < 6) /* optical_res/5 >= xres > optical_res/6 */
+ used_res = optical_res/5;
+ else if (i < 8) /* optical_res/6 >= xres > optical_res/8 */
+ used_res = optical_res/6;
+ else if (i < 10) /* optical_res/8 >= xres > optical_res/10 */
+ used_res = optical_res/8;
+ else if (i < 12) /* optical_res/10 >= xres > optical_res/12 */
+ used_res = optical_res/10;
+ else if (i < 15) /* optical_res/12 >= xres > optical_res/15 */
+ used_res = optical_res/12;
+ else
+ used_res = optical_res/15;
+
+ /* compute scan parameters values */
+ /* pixels are allways given at half or full CCD optical resolution */
+ /* use detected left margin and fixed value */
+/* start */
+ /* add x coordinates */
+ start =
+ ((dev->sensor.CCD_start_xoffset + startx) * used_res) /
+ dev->sensor.optical_res;
+
+/* needs to be aligned for used_res */
+ start = (start * optical_res) / used_res;
+
+ start += dev->sensor.dummy_pixel + 1;
+
+ if (stagger > 0)
+ start |= 1;
+
+ /* compute correct pixels number */
+/* pixels */
+ used_pixels =
+ (pixels * optical_res) / xres;
+
+ /* round up pixels number if needed */
+ if (used_pixels * xres < pixels * optical_res)
+ used_pixels++;
+
+/* dummy */
+ /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1
+ dummy line. Maybe the dummy line adds correctness since the motor runs
+ slower (higher dpi)
+ */
+/* for cis this creates better aligned color lines:
+dummy \ scanned lines
+ 0: R G B R ...
+ 1: R G B - R ...
+ 2: R G B - - R ...
+ 3: R G B - - - R ...
+ 4: R G B - - - - R ...
+ 5: R G B - - - - - R ...
+ 6: R G B - - - - - - R ...
+ 7: R G B - - - - - - - R ...
+ 8: R G B - - - - - - - - R ...
+ 9: R G B - - - - - - - - - R ...
+ 10: R G B - - - - - - - - - - R ...
+ 11: R G B - - - - - - - - - - - R ...
+ 12: R G B - - - - - - - - - - - - R ...
+ 13: R G B - - - - - - - - - - - - - R ...
+ 14: R G B - - - - - - - - - - - - - - R ...
+ 15: R G B - - - - - - - - - - - - - - - R ...
+ -- pierre
+ */
+ dummy = 0;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres*channels;
+ else
+ slope_dpi = yres;
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+/* scan_step_type */
+/* Try to do at least 4 steps per line. if that is impossible we will have to
+ live with that
+ */
+ if (yres*4 < dev->motor.base_ydpi
+ || dev->motor.max_step_type <= 0)
+ scan_step_type = 0;
+ else if (yres*4 < dev->motor.base_ydpi*2
+ || dev->motor.max_step_type <= 1)
+ scan_step_type = 1;
+ else
+ scan_step_type = 2;
+
+/* exposure_time */
+ led_exposure = gl841_get_led_exposure(dev);
+
+ exposure_time = sanei_genesys_exposure_time2(
+ dev,
+ slope_dpi,
+ scan_step_type,
+ start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
+ led_exposure,
+ scan_power_mode);
+
+ while(scan_power_mode + 1 < dev->motor.power_mode_count) {
+ exposure_time2 = sanei_genesys_exposure_time2(
+ dev,
+ slope_dpi,
+ scan_step_type,
+ start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
+ led_exposure,
+ scan_power_mode + 1);
+ if (exposure_time < exposure_time2)
+ break;
+ exposure_time = exposure_time2;
+ scan_power_mode++;
+ }
+
+
+ DBG (DBG_info, "gl841_init_scan_regs : exposure_time=%d pixels\n",
+ exposure_time);
+
+/*** optical parameters ***/
+ /* in case of dynamic lineart, we use an internal 8 bit gray scan
+ * to generate 1 lineart data */
+ if(flags & SCAN_FLAG_DYNAMIC_LINEART)
+ {
+ depth=8;
+ }
+ if (depth == 16)
+ flags |= SCAN_FLAG_DISABLE_GAMMA;
+
+ status = gl841_init_optical_regs_scan(dev,
+ reg,
+ exposure_time,
+ used_res,
+ start,
+ used_pixels,
+ channels,
+ depth,
+ half_ccd,
+ color_filter,
+ ((flags & SCAN_FLAG_DISABLE_SHADING)?
+ OPTICAL_FLAG_DISABLE_SHADING:0) |
+ ((flags & SCAN_FLAG_DISABLE_GAMMA)?
+ OPTICAL_FLAG_DISABLE_GAMMA:0) |
+ ((flags & SCAN_FLAG_DISABLE_LAMP)?
+ OPTICAL_FLAG_DISABLE_LAMP:0)
+ );
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+/*** motor parameters ***/
+
+ /* scanned area must be enlarged by max color shift needed */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,flags);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ /* add tl_y to base movement */
+ move = starty;
+ DBG (DBG_info, "gl841_init_scan_regs: move=%d steps\n", move);
+
+ /* subtract current head position */
+ move -= dev->scanhead_position_in_steps;
+ DBG (DBG_info, "gl841_init_scan_regs: move=%d steps\n", move);
+
+ if (move < 0)
+ move = 0;
+
+ /* round it */
+/* the move is not affected by dummy -- pierre */
+/* move = ((move + dummy) / (dummy + 1)) * (dummy + 1);
+ DBG (DBG_info, "gl841_init_scan_regs: move=%d steps\n", move);*/
+
+ if (flags & SCAN_FLAG_SINGLE_LINE)
+ status = gl841_init_motor_regs_off(reg, dev->model->is_cis?lincnt*channels:lincnt);
+ else
+ status = gl841_init_motor_regs_scan(dev,
+ reg,
+ exposure_time,
+ slope_dpi,
+ scan_step_type,
+ dev->model->is_cis?lincnt*channels:lincnt,
+ dummy,
+ move,
+ scan_power_mode,
+ (flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)?
+ MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE:0
+ );
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ /*** prepares data reordering ***/
+
+/* words_per_line */
+ bytes_per_line = (used_pixels * used_res) / optical_res;
+ bytes_per_line = (bytes_per_line * channels * depth) / 8;
+
+ requested_buffer_size = 8 * bytes_per_line;
+ /* we must use a round number of bytes_per_line */
+ if (requested_buffer_size > BULKIN_MAXSIZE)
+ requested_buffer_size =
+ (BULKIN_MAXSIZE / bytes_per_line) * bytes_per_line;
+
+ read_buffer_size =
+ 2 * requested_buffer_size +
+ ((max_shift + stagger) * used_pixels * channels * depth) / 8;
+
+ RIE(sanei_genesys_buffer_free(&(dev->read_buffer)));
+ RIE(sanei_genesys_buffer_alloc(&(dev->read_buffer), read_buffer_size));
+
+ RIE(sanei_genesys_buffer_free(&(dev->lines_buffer)));
+ RIE(sanei_genesys_buffer_alloc(&(dev->lines_buffer), read_buffer_size));
+
+ RIE(sanei_genesys_buffer_free(&(dev->shrink_buffer)));
+ RIE(sanei_genesys_buffer_alloc(&(dev->shrink_buffer),
+ requested_buffer_size));
+
+ RIE(sanei_genesys_buffer_free(&(dev->out_buffer)));
+ RIE(sanei_genesys_buffer_alloc(&(dev->out_buffer),
+ (8 * dev->settings.pixels * channels * depth) / 8));
+
+
+ dev->read_bytes_left = bytes_per_line * lincnt;
+
+ DBG (DBG_info,
+ "gl841_init_scan_regs: physical bytes to read = %lu\n",
+ (u_long) dev->read_bytes_left);
+ dev->read_active = SANE_TRUE;
+
+
+ dev->current_setup.pixels = (used_pixels * used_res)/optical_res;
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+/* TODO: should this be done elsewhere? */
+ /* scan bytes to send to the frontend */
+ /* theory :
+ target_size =
+ (dev->settings.pixels * dev->settings.lines * channels * depth) / 8;
+ but it suffers from integer overflow so we do the following:
+
+ 1 bit color images store color data byte-wise, eg byte 0 contains
+ 8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
+ 8 bits of blue.
+ This does not fix the overflow, though.
+ 644mp*16 = 10gp, leading to an overflow
+ -- pierre
+ */
+
+ dev->total_bytes_read = 0;
+ if (depth == 1)
+ dev->total_bytes_to_read =
+ ((dev->settings.pixels * dev->settings.lines) / 8 +
+ (((dev->settings.pixels * dev->settings.lines)%8)?1:0)
+ ) * channels;
+ else
+ dev->total_bytes_to_read =
+ dev->settings.pixels * dev->settings.lines * channels * (depth / 8);
+
+ DBG (DBG_info, "gl841_init_scan_regs: total bytes to send = %lu\n",
+ (u_long) dev->total_bytes_to_read);
+/* END TODO */
+
+ DBG (DBG_proc, "gl841_init_scan_regs: completed\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_calculate_current_setup (Genesys_Device * dev)
+{
+ int channels;
+ int depth;
+ int start;
+
+ float xres;/*dpi*/
+ float yres;/*dpi*/
+ float startx;/*optical_res, from dummy_pixel+1*/
+ float pixels;
+ float lines;
+
+ int used_res;
+ int used_pixels;
+ unsigned int lincnt;
+ int exposure_time, exposure_time2, led_exposure;
+ int i;
+ int stagger;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+ int scan_step_type = 1;
+ int scan_power_mode = 0;
+ int max_shift;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+
+ DBG (DBG_info,
+ "gl841_calculate_current_setup settings:\n"
+ "Resolution: %uDPI\n"
+ "Lines : %u\n"
+ "PPL : %u\n"
+ "Startpos : %.3f/%.3f\n"
+ "Scan mode : %d\n\n",
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+/* channels */
+ if (dev->settings.scan_mode == 4) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+/* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == 0)
+ depth = 1;
+
+/* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+
+ start += dev->settings.tl_x;
+
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+
+ xres = dev->settings.xres;/*dpi*/
+ yres = dev->settings.yres;/*dpi*/
+ startx = start;/*optical_res, from dummy_pixel+1*/
+ pixels = dev->settings.pixels;
+ lines = dev->settings.lines;
+
+ DBG (DBG_info,
+ "gl841_calculate_current_setup settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g\n"
+ "Depth/Channels: %u/%u\n\n",
+ xres, yres, lines, pixels,
+ startx,
+ depth, channels);
+
+/* half_ccd */
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if ((dev->sensor.optical_res < 2 * xres) ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE)) {
+ half_ccd = SANE_FALSE;
+ } else {
+ half_ccd = SANE_TRUE;
+ }
+
+/* optical_res */
+
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 2;
+
+/* stagger */
+
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl841_calculate_current_setup: stagger=%d lines\n",
+ stagger);
+
+/* used_res */
+ i = optical_res / xres;
+
+/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
+
+ if (i < 2) /* optical_res >= xres > optical_res/2 */
+ used_res = optical_res;
+ else if (i < 3) /* optical_res/2 >= xres > optical_res/3 */
+ used_res = optical_res/2;
+ else if (i < 4) /* optical_res/3 >= xres > optical_res/4 */
+ used_res = optical_res/3;
+ else if (i < 5) /* optical_res/4 >= xres > optical_res/5 */
+ used_res = optical_res/4;
+ else if (i < 6) /* optical_res/5 >= xres > optical_res/6 */
+ used_res = optical_res/5;
+ else if (i < 8) /* optical_res/6 >= xres > optical_res/8 */
+ used_res = optical_res/6;
+ else if (i < 10) /* optical_res/8 >= xres > optical_res/10 */
+ used_res = optical_res/8;
+ else if (i < 12) /* optical_res/10 >= xres > optical_res/12 */
+ used_res = optical_res/10;
+ else if (i < 15) /* optical_res/12 >= xres > optical_res/15 */
+ used_res = optical_res/12;
+ else
+ used_res = optical_res/15;
+
+ /* compute scan parameters values */
+ /* pixels are allways given at half or full CCD optical resolution */
+ /* use detected left margin and fixed value */
+/* start */
+ /* add x coordinates */
+ start =
+ ((dev->sensor.CCD_start_xoffset + startx) * used_res) /
+ dev->sensor.optical_res;
+
+/* needs to be aligned for used_res */
+ start = (start * optical_res) / used_res;
+
+ start += dev->sensor.dummy_pixel + 1;
+
+ if (stagger > 0)
+ start |= 1;
+
+ /* compute correct pixels number */
+/* pixels */
+ used_pixels =
+ (pixels * optical_res) / xres;
+
+ /* round up pixels number if needed */
+ if (used_pixels * xres < pixels * optical_res)
+ used_pixels++;
+
+/* dummy */
+ /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1
+ dummy line. Maybe the dummy line adds correctness since the motor runs
+ slower (higher dpi)
+ */
+/* for cis this creates better aligned color lines:
+dummy \ scanned lines
+ 0: R G B R ...
+ 1: R G B - R ...
+ 2: R G B - - R ...
+ 3: R G B - - - R ...
+ 4: R G B - - - - R ...
+ 5: R G B - - - - - R ...
+ 6: R G B - - - - - - R ...
+ 7: R G B - - - - - - - R ...
+ 8: R G B - - - - - - - - R ...
+ 9: R G B - - - - - - - - - R ...
+ 10: R G B - - - - - - - - - - R ...
+ 11: R G B - - - - - - - - - - - R ...
+ 12: R G B - - - - - - - - - - - - R ...
+ 13: R G B - - - - - - - - - - - - - R ...
+ 14: R G B - - - - - - - - - - - - - - R ...
+ 15: R G B - - - - - - - - - - - - - - - R ...
+ -- pierre
+ */
+ dummy = 0;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres*channels;
+ else
+ slope_dpi = yres;
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+/* scan_step_type */
+/* Try to do at least 4 steps per line. if that is impossible we will have to
+ live with that
+ */
+ if (yres*4 < dev->motor.base_ydpi
+ || dev->motor.max_step_type <= 0)
+ scan_step_type = 0;
+ else if (yres*4 < dev->motor.base_ydpi*2
+ || dev->motor.max_step_type <= 1)
+ scan_step_type = 1;
+ else
+ scan_step_type = 2;
+
+ led_exposure = gl841_get_led_exposure(dev);
+
+/* exposure_time */
+ exposure_time = sanei_genesys_exposure_time2(
+ dev,
+ slope_dpi,
+ scan_step_type,
+ start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
+ led_exposure,
+ scan_power_mode);
+
+ while(scan_power_mode + 1 < dev->motor.power_mode_count) {
+ exposure_time2 = sanei_genesys_exposure_time2(
+ dev,
+ slope_dpi,
+ scan_step_type,
+ start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
+ led_exposure,
+ scan_power_mode + 1);
+ if (exposure_time < exposure_time2)
+ break;
+ exposure_time = exposure_time2;
+ scan_power_mode++;
+ }
+
+ DBG (DBG_info, "gl841_calculate_current_setup : exposure_time=%d pixels\n",
+ exposure_time);
+
+ /* scanned area must be enlarged by max color shift needed */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,0);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ dev->current_setup.pixels = (used_pixels * used_res)/optical_res;
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ DBG (DBG_proc, "gl841_calculate_current_setup: completed\n");
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gl841_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
+{
+
+ DBG (DBG_proc, "gl841_set_motor_power\n");
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x02,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x02) |
+ REG02_MTRPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x02,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x02) &
+ ~REG02_MTRPWR);
+ }
+}
+
+static void
+gl841_set_lamp_power (Genesys_Device * dev,
+ Genesys_Register_Set * regs, SANE_Bool set)
+{
+ Genesys_Register_Set * r;
+ int i;
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x03,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x03) |
+ REG03_LAMPPWR);
+
+ r = sanei_genesys_get_address (regs, 0x10);
+ for (i = 0; i < 6; i++, r++) {
+ if (dev->sensor.regs_0x10_0x1d[i] == 0x00)
+ r->value = 0x01;/*0x00 will not be accepted*/
+ else
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+ r = sanei_genesys_get_address (regs, 0x19);
+ r->value = 0x50;
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, 0x03,
+ sanei_genesys_read_reg_from_set (regs,
+ 0x03) &
+ ~REG03_LAMPPWR);
+
+ r = sanei_genesys_get_address (regs, 0x10);
+ for (i = 0; i < 6; i++, r++) {
+ r->value = 0x01;/* 0x0101 is as off as possible */
+ }
+ r = sanei_genesys_get_address (regs, 0x19);
+ r->value = 0xff;
+ }
+}
+
+/*for fast power saving methods only, like disabling certain amplifiers*/
+static SANE_Status
+gl841_save_power(Genesys_Device * dev, SANE_Bool enable) {
+ uint8_t val;
+
+ DBG(DBG_proc, "gl841_save_power: enable = %d\n", enable);
+
+ if (enable)
+ {
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+/* expect GPIO17 to be enabled, and GPIO9 to be disabled,
+ while GPIO8 is disabled*/
+/* final state: GPIO8 disabled, GPIO9 enabled, GPIO17 disabled,
+ GPIO18 disabled*/
+
+ sanei_genesys_read_register(dev, REG6D, &val);
+ sanei_genesys_write_register(dev, REG6D, val | 0x80);
+
+ usleep(1000);
+
+ /*enable GPIO9*/
+ sanei_genesys_read_register(dev, REG6C, &val);
+ sanei_genesys_write_register(dev, REG6C, val | 0x01);
+
+ /*disable GPO17*/
+ sanei_genesys_read_register(dev, REG6B, &val);
+ sanei_genesys_write_register(dev, REG6B, val & ~REG6B_GPO17);
+
+ /*disable GPO18*/
+ sanei_genesys_read_register(dev, REG6B, &val);
+ sanei_genesys_write_register(dev, REG6B, val & ~REG6B_GPO18);
+
+ usleep(1000);
+
+ sanei_genesys_read_register(dev, REG6D, &val);
+ sanei_genesys_write_register(dev, REG6D, val & ~0x80);
+
+ }
+ if (dev->model->gpo_type == GPO_DP685)
+ {
+ sanei_genesys_read_register(dev, REG6B, &val);
+ sanei_genesys_write_register(dev, REG6B, val & ~REG6B_GPO17);
+ dev->reg[reg_0x6b].value &= ~REG6B_GPO17;
+ dev->calib_reg[reg_0x6b].value &= ~REG6B_GPO17;
+ }
+
+ gl841_set_fe (dev, AFE_POWER_SAVE);
+
+ }
+ else
+ {
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+/* expect GPIO17 to be enabled, and GPIO9 to be disabled,
+ while GPIO8 is disabled*/
+/* final state: GPIO8 enabled, GPIO9 disabled, GPIO17 enabled,
+ GPIO18 enabled*/
+
+ sanei_genesys_read_register(dev, REG6D, &val);
+ sanei_genesys_write_register(dev, REG6D, val | 0x80);
+
+ usleep(10000);
+
+ /*disable GPIO9*/
+ sanei_genesys_read_register(dev, REG6C, &val);
+ sanei_genesys_write_register(dev, REG6C, val & ~0x01);
+
+ /*enable GPIO10*/
+ sanei_genesys_read_register(dev, REG6C, &val);
+ sanei_genesys_write_register(dev, REG6C, val | 0x02);
+
+ /*enable GPO17*/
+ sanei_genesys_read_register(dev, REG6B, &val);
+ sanei_genesys_write_register(dev, REG6B, val | REG6B_GPO17);
+ dev->reg[reg_0x6b].value |= REG6B_GPO17;
+ dev->calib_reg[reg_0x6b].value |= REG6B_GPO17;
+
+ /*enable GPO18*/
+ sanei_genesys_read_register(dev, REG6B, &val);
+ sanei_genesys_write_register(dev, REG6B, val | REG6B_GPO18);
+ dev->reg[reg_0x6b].value |= REG6B_GPO18;
+ dev->calib_reg[reg_0x6b].value |= REG6B_GPO18;
+
+ }
+ if (dev->model->gpo_type == GPO_DP665
+ || dev->model->gpo_type == GPO_DP685)
+ {
+ sanei_genesys_read_register(dev, REG6B, &val);
+ sanei_genesys_write_register(dev, REG6B, val | REG6B_GPO17);
+ dev->reg[reg_0x6b].value |= REG6B_GPO17;
+ dev->calib_reg[reg_0x6b].value |= REG6B_GPO17;
+ }
+
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_set_powersaving (Genesys_Device * dev,
+ int delay /* in minutes */ )
+{
+ SANE_Status status;
+ Genesys_Register_Set local_reg[7];
+ int rate, exposure_time, tgtime, time;
+
+ DBG (DBG_proc, "gl841_set_powersaving (delay = %d)\n", delay);
+
+ local_reg[0].address = 0x01;
+ local_reg[0].value = sanei_genesys_read_reg_from_set (dev->reg, 0x01); /* disable fastmode */
+
+ local_reg[1].address = 0x03;
+ local_reg[1].value = sanei_genesys_read_reg_from_set (dev->reg, 0x03); /* Lamp power control */
+
+ local_reg[2].address = 0x05;
+ local_reg[2].value = sanei_genesys_read_reg_from_set (dev->reg, 0x05) /*& ~REG05_BASESEL*/; /* 24 clocks/pixel */
+
+ local_reg[3].address = 0x18; /* Set CCD type */
+ local_reg[3].value = 0x00;
+
+ local_reg[4].address = 0x38; /* line period low */
+ local_reg[4].value = 0x00;
+
+ local_reg[5].address = 0x39; /* line period high */
+ local_reg[5].value = 0x00;
+
+ local_reg[6].address = 0x1c; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE */
+ local_reg[6].value = sanei_genesys_read_reg_from_set (dev->reg, 0x05) & ~REG1C_TGTIME;
+
+ if (!delay)
+ local_reg[1].value = local_reg[1].value & 0xf0; /* disable lampdog and set lamptime = 0 */
+ else if (delay < 20)
+ local_reg[1].value = (local_reg[1].value & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
+ else
+ local_reg[1].value = (local_reg[1].value & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
+
+ time = delay * 1000 * 60; /* -> msec */
+ exposure_time =
+ (uint32_t) (time * 32000.0 /
+ (24.0 * 64.0 * (local_reg[1].value & REG03_LAMPTIM) *
+ 1024.0) + 0.5);
+ /* 32000 = system clock, 24 = clocks per pixel */
+ rate = (exposure_time + 65536) / 65536;
+ if (rate > 4)
+ {
+ rate = 8;
+ tgtime = 3;
+ }
+ else if (rate > 2)
+ {
+ rate = 4;
+ tgtime = 2;
+ }
+ else if (rate > 1)
+ {
+ rate = 2;
+ tgtime = 1;
+ }
+ else
+ {
+ rate = 1;
+ tgtime = 0;
+ }
+
+ local_reg[6].value |= tgtime;
+ exposure_time /= rate;
+
+ if (exposure_time > 65535)
+ exposure_time = 65535;
+
+ local_reg[4].value = exposure_time >> 8; /* highbyte */
+ local_reg[5].value = exposure_time & 255; /* lowbyte */
+
+ status =
+ gl841_bulk_write_register (dev, local_reg,
+ sizeof (local_reg)/sizeof (local_reg[0]));
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error,
+ "gl841_set_powersaving: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+
+ DBG (DBG_proc, "gl841_set_powersaving: completed\n");
+ return status;
+}
+
+static SANE_Status
+gl841_start_action (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x01);
+}
+
+static SANE_Status
+gl841_stop_action (Genesys_Device * dev)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL841_MAX_REGS+1];
+ SANE_Status status;
+ uint8_t val40, val;
+ unsigned int loop;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ status = sanei_genesys_read_register(dev, 0x40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n",__FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* only stop action if needed */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
+ {
+ DBG (DBG_info, "%s: already stopped\n", __FUNCTION__);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy (local_reg, dev->reg, (GENESYS_GL841_MAX_REGS+1) * sizeof (Genesys_Register_Set));
+
+ gl841_init_optical_regs_off(local_reg);
+
+ gl841_init_motor_regs_off(local_reg,0);
+ status = gl841_bulk_write_register (dev, local_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to bulk write registers: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* looks like writing the right registers to zero is enough to get the chip
+ out of scan mode into command mode, actually triggering(writing to
+ register 0x0f) seems to be unnecessary */
+
+ loop = 10;
+ while (loop > 0)
+ {
+ status = sanei_genesys_read_register(dev, 0x40, &val40);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n",__FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* if scanner is in command mode, we are done */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ usleep(100*1000);
+ loop--;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+gl841_get_paper_sensor(Genesys_Device * dev, SANE_Bool * paper_loaded)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ status = sanei_genesys_read_register(dev, REG6D, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_get_paper_sensor: failed to read gpio: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ *paper_loaded = (val & 0x1) == 0;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_eject_document (Genesys_Device * dev)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL841_MAX_REGS+1];
+ SANE_Status status;
+ uint8_t val;
+ SANE_Bool paper_loaded;
+ unsigned int init_steps;
+ float feed_mm;
+ int loop;
+
+ DBG (DBG_proc, "gl841_eject_document\n");
+
+ if (!dev->model->is_sheetfed == SANE_TRUE)
+ {
+ DBG (DBG_proc, "gl841_eject_document: there is no \"eject sheet\"-concept for non sheet fed\n");
+ DBG (DBG_proc, "gl841_eject_document: finished\n");
+ return SANE_STATUS_GOOD;
+ }
+
+
+ memset (local_reg, 0, sizeof (local_reg));
+ val = 0;
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to read status register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to stop motor: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ memcpy (local_reg, dev->reg, (GENESYS_GL841_MAX_REGS+1) * sizeof (Genesys_Register_Set));
+
+ gl841_init_optical_regs_off(local_reg);
+
+ gl841_init_motor_regs(dev,local_reg,
+ 65536,MOTOR_ACTION_FEED,0);
+
+ status =
+ gl841_bulk_write_register (dev, local_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl841_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl841_stop_action (dev);
+ /* send original registers */
+ gl841_bulk_write_register (dev, dev->reg, GENESYS_GL841_MAX_REGS);
+ return status;
+ }
+
+ RIE(gl841_get_paper_sensor(dev, &paper_loaded));
+ if (paper_loaded)
+ {
+ DBG (DBG_info,
+ "gl841_eject_document: paper still loaded\n");
+ /* force document TRUE, because it is definitely present */
+ dev->document = SANE_TRUE;
+ dev->scanhead_position_in_steps = 0;
+
+ loop = 300;
+ while (loop > 0) /* do not wait longer then 30 seconds */
+ {
+
+ RIE(gl841_get_paper_sensor(dev, &paper_loaded));
+
+ if (!paper_loaded)
+ {
+ DBG (DBG_info,
+ "gl841_eject_document: reached home position\n");
+ DBG (DBG_proc, "gl841_eject_document: finished\n");
+ break;
+ }
+ usleep (100000); /* sleep 100 ms */
+ --loop;
+ }
+
+ if (loop == 0)
+ {
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl841_stop_action (dev);
+ DBG (DBG_error,
+ "gl841_eject_document: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ feed_mm = SANE_UNFIX(dev->model->eject_feed);
+ if (dev->document)
+ {
+ feed_mm += SANE_UNFIX(dev->model->post_scan);
+ }
+
+ status = sanei_genesys_read_feed_steps(dev, &init_steps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to read feed steps: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* now feed for extra <number> steps */
+ loop = 0;
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ unsigned int steps;
+
+ status = sanei_genesys_read_feed_steps(dev, &steps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to read feed steps: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info, "gl841_eject_document: init_steps: %d, steps: %d\n",
+ init_steps, steps);
+
+ if (steps > init_steps + (feed_mm * dev->motor.base_ydpi) / MM_PER_INCH)
+ {
+ break;
+ }
+
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ status = gl841_stop_action(dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_eject_document: failed to stop motor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->document = SANE_FALSE;
+
+ DBG (DBG_proc, "gl841_eject_document: finished\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+gl841_load_document (Genesys_Device * dev)
+{
+ SANE_Status status;
+ SANE_Bool paper_loaded;
+ int loop = 300;
+ DBG (DBG_proc, "gl841_load_document\n");
+ while (loop > 0) /* do not wait longer then 30 seconds */
+ {
+
+ RIE(gl841_get_paper_sensor(dev, &paper_loaded));
+
+ if (paper_loaded)
+ {
+ DBG (DBG_info,
+ "gl841_load_document: document inserted\n");
+
+ /* when loading OK, document is here */
+ dev->document = SANE_TRUE;
+
+ usleep (1000000); /* give user 1000ms to place document correctly */
+ break;
+ }
+ usleep (100000); /* sleep 100 ms */
+ --loop;
+ }
+
+ if (loop == 0)
+ {
+ /* when we come here then the user needed to much time for this */
+ DBG (DBG_error,
+ "gl841_load_document: timeout while waiting for document\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_proc, "gl841_load_document: finished\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * detects end of document and adjust current scan
+ * to take it into account
+ * used by sheetfed scanners
+ */
+static SANE_Status
+gl841_detect_document_end (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool paper_loaded;
+ unsigned int scancnt = 0, lincnt, postcnt;
+ uint8_t val;
+ size_t total_bytes_to_read;
+
+ DBG (DBG_proc, "%s: begin\n", __FUNCTION__);
+
+ RIE (gl841_get_paper_sensor (dev, &paper_loaded));
+
+ /* sheetfed scanner uses home sensor as paper present */
+ if ((dev->document == SANE_TRUE) && !paper_loaded)
+ {
+ DBG (DBG_info, "%s: no more document\n", __FUNCTION__);
+ dev->document = SANE_FALSE;
+
+ /* we can't rely on total_bytes_to_read since the frontend
+ * might have been slow to read data, so we re-evaluate the
+ * amount of data to scan form the hardware settings
+ */
+ status=sanei_genesys_read_scancnt(dev,&scancnt);
+ if(status!=SANE_STATUS_GOOD)
+ {
+ dev->total_bytes_to_read = dev->total_bytes_read;
+ dev->read_bytes_left = 0;
+ DBG (DBG_proc, "%s: finished\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+ }
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR && dev->model->is_cis)
+ {
+ scancnt/=3;
+ }
+ DBG (DBG_io, "%s: scancnt=%u lines\n",__FUNCTION__, scancnt);
+
+ RIE(sanei_genesys_read_register(dev, 0x25, &val));
+ lincnt=65536*val;
+ RIE(sanei_genesys_read_register(dev, 0x26, &val));
+ lincnt+=256*val;
+ RIE(sanei_genesys_read_register(dev, 0x27, &val));
+ lincnt+=val;
+ DBG (DBG_io, "%s: lincnt=%u lines\n",__FUNCTION__, lincnt);
+ postcnt=(SANE_UNFIX(dev->model->post_scan)/MM_PER_INCH)*dev->settings.yres;
+ DBG (DBG_io, "%s: postcnt=%u lines\n",__FUNCTION__, postcnt);
+
+ /* the current scancnt is also the final one, so we use it to
+ * compute total bytes to read. We also add the line count to eject document */
+ total_bytes_to_read=(scancnt+postcnt)*dev->wpl;
+
+ DBG (DBG_io, "%s: old total_bytes_to_read=%u\n",__FUNCTION__,(unsigned int)dev->total_bytes_to_read);
+ DBG (DBG_io, "%s: new total_bytes_to_read=%u\n",__FUNCTION__,(unsigned int)total_bytes_to_read);
+
+ /* assign new end value */
+ if(dev->total_bytes_to_read>total_bytes_to_read)
+ {
+ DBG (DBG_io, "%s: scan shorten\n",__FUNCTION__);
+ dev->total_bytes_to_read=total_bytes_to_read;
+ }
+ }
+
+ DBG (DBG_proc, "%s: finished\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+}
+
+/* Send the low-level scan command */
+/* todo : is this that useful ? */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl841_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor)
+{
+ SANE_Status status;
+ Genesys_Register_Set local_reg[4];
+
+ DBG (DBG_proc, "gl841_begin_scan\n");
+
+ local_reg[0].address = 0x03;
+ if (dev->model->ccd_type != CCD_PLUSTEK_3600)
+ {
+ local_reg[0].value = sanei_genesys_read_reg_from_set (reg, 0x03) | REG03_LAMPPWR;
+ }
+ else
+ {
+ local_reg[0].value = sanei_genesys_read_reg_from_set (reg, 0x03); /* TODO PLUSTEK_3600: why ?? */
+ }
+
+ local_reg[1].address = 0x01;
+ local_reg[1].value = sanei_genesys_read_reg_from_set (reg, 0x01) | REG01_SCAN; /* set scan bit */
+
+ local_reg[2].address = 0x0d;
+ local_reg[2].value = 0x01;
+
+ local_reg[3].address = 0x0f;
+ if (start_motor)
+ local_reg[3].value = 0x01;
+ else
+ local_reg[3].value = 0x00; /* do not start motor yet */
+
+ status =
+ gl841_bulk_write_register (dev, local_reg,
+ sizeof (local_reg)/sizeof (local_reg[0]));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_begin_scan: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "gl841_begin_scan: completed\n");
+
+ return status;
+}
+
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl841_end_scan (Genesys_Device * dev, Genesys_Register_Set __sane_unused__ * reg,
+ SANE_Bool check_stop)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl841_end_scan (check_stop = %d)\n", check_stop);
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else /* flat bed scanners */
+ {
+ status = gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_end_scan: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (DBG_proc, "gl841_end_scan: completed\n");
+
+ return status;
+}
+
+/* Moves the slider to steps */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl841_feed (Genesys_Device * dev, int steps)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL841_MAX_REGS+1];
+ SANE_Status status;
+ uint8_t val;
+ int loop;
+
+ DBG (DBG_proc, "gl841_feed (steps = %d)\n",
+ steps);
+
+ status =
+ gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_feed: failed to stop action: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ memcpy (local_reg, dev->reg, (GENESYS_GL841_MAX_REGS+1) * sizeof (Genesys_Register_Set));
+
+ gl841_init_optical_regs_off(local_reg);
+
+ gl841_init_motor_regs(dev,local_reg,
+ steps,MOTOR_ACTION_FEED,0);
+
+ status =
+ gl841_bulk_write_register (dev, local_reg,
+ GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_feed: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl841_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_feed: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl841_stop_action (dev);
+ /* send original registers */
+ gl841_bulk_write_register (dev, dev->reg,
+ GENESYS_GL841_MAX_REGS);
+ return status;
+ }
+
+ loop = 0;
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_feed: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (!(val & REG41_MOTORENB)) /* motor enabled */
+ {
+ DBG (DBG_proc, "gl841_feed: finished\n");
+ dev->scanhead_position_in_steps += steps;
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl841_stop_action (dev);
+
+ DBG (DBG_error,
+ "gl841_feed: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Moves the slider to the home (top) position slowly */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl841_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL841_MAX_REGS+1];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ uint8_t val;
+ int loop = 0;
+
+ DBG (DBG_proc, "gl841_slow_back_home (wait_until_home = %d)\n",
+ wait_until_home);
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ DBG (DBG_proc, "gl841_slow_back_home: there is no \"home\"-concept for sheet fed\n");
+ DBG (DBG_proc, "gl841_slow_back_home: finished\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* reset gpio pin */
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ val = dev->gpo.value[0];
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ }
+ gl841_save_power(dev, SANE_FALSE);
+
+ /* first read gives HOME_SENSOR true */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ usleep (100000); /* sleep 100 ms */
+
+ /* second is reliable */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ dev->scanhead_position_in_steps = 0;
+
+ if (val & REG41_HOMESNR) /* is sensor at home? */
+ {
+ DBG (DBG_info,
+ "gl841_slow_back_home: already at home, completed\n");
+ dev->scanhead_position_in_steps = 0;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* end previous scan if any */
+ r = sanei_genesys_get_address (dev->reg, REG01);
+ r->value &= ~REG01_SCAN;
+ status = sanei_genesys_write_register (dev, REG01, r->value);
+
+ /* if motor is on, stop current action */
+ if (val & REG41_MOTORENB)
+ {
+ status = gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_slow_back_home: failed to stop motor: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ memcpy (local_reg, dev->reg, (GENESYS_GL841_MAX_REGS+1) * sizeof (Genesys_Register_Set));
+
+ gl841_init_motor_regs(dev,local_reg, 65536,MOTOR_ACTION_GO_HOME,0);
+
+ /* set up for reverse and no scan */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ r->value |= REG02_MTRREV;
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ RIE (gl841_bulk_write_register (dev, local_reg, GENESYS_GL841_MAX_REGS));
+
+ status = gl841_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_slow_back_home: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl841_stop_action (dev);
+ /* send original registers */
+ gl841_bulk_write_register (dev, dev->reg, GENESYS_GL841_MAX_REGS);
+ return status;
+ }
+
+ if (wait_until_home)
+ {
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (val & REG41_HOMESNR) /* home sensor */
+ {
+ DBG (DBG_info, "gl841_slow_back_home: reached home position\n");
+ DBG (DBG_proc, "gl841_slow_back_home: finished\n");
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl841_stop_action (dev);
+ DBG (DBG_error,
+ "gl841_slow_back_home: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_info, "gl841_slow_back_home: scanhead is still moving\n");
+ DBG (DBG_proc, "gl841_slow_back_home: finished\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
+ area at 600 dpi from very top of scanner */
+static SANE_Status
+gl841_search_start_position (Genesys_Device * dev)
+{
+ int size;
+ SANE_Status status;
+ uint8_t *data;
+ Genesys_Register_Set local_reg[GENESYS_GL841_MAX_REGS+1];
+ int steps;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ DBGSTART;
+
+ memcpy (local_reg, dev->reg, (GENESYS_GL841_MAX_REGS +1) * sizeof (Genesys_Register_Set));
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ status = gl841_init_scan_regs (dev,
+ local_reg,
+ dpi,
+ dpi,
+ 0,
+ 0,/*we should give a small offset here~60 steps*/
+ 600,
+ dev->model->search_lines,
+ 8,
+ 1,
+ 1,/*green*/
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE);
+ if(status!=SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to init scan registers: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send to scanner */
+ status =
+ gl841_bulk_write_register (dev, local_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to bulk write registers: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ size = pixels * dev->model->search_lines;
+
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = gl841_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("search_position.pnm", data, 8, 1, pixels,
+ dev->model->search_lines);
+
+ status = gl841_end_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* update regs to copy ASIC internal state */
+ memcpy (dev->reg, local_reg, (GENESYS_GL841_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+
+/*TODO: find out where sanei_genesys_search_reference_point
+ stores information, and use that correctly*/
+ status =
+ sanei_genesys_search_reference_point (dev, data, 0, dpi, pixels,
+ dev->model->search_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to set search reference point: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ free (data);
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sets up register for coarse gain calibration
+ * todo: check it for scanners using it */
+static SANE_Status
+gl841_init_regs_for_coarse_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t channels;
+ uint8_t cksel;
+
+ DBG (DBG_proc, "gl841_init_regs_for_coarse_calibration\n");
+
+
+ cksel = (dev->calib_reg[reg_0x18].value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
+
+ /* set line size */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ status = gl841_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ dev->sensor.optical_res / cksel,
+ 20,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE
+ );
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init_register_for_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info,
+ "gl841_init_register_for_coarse_calibration: optical sensor res: %d dpi, actual res: %d\n",
+ dev->sensor.optical_res / cksel, dev->settings.xres);
+
+ status =
+ gl841_bulk_write_register (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init_register_for_coarse_calibration: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc,
+ "gl841_init_register_for_coarse_calibration: completed\n");
+
+/* if (DBG_LEVEL >= DBG_info)
+ sanei_gl841_print_registers (dev->calib_reg);*/
+
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* init registers for shading calibration */
+static SANE_Status
+gl841_init_regs_for_shading (Genesys_Device * dev)
+{
+ SANE_Status status;
+ SANE_Int ydpi;
+
+ DBG (DBG_proc, "gl841_init_regs_for_shading: lines = %d\n", (int)dev->calib_lines);
+
+ ydpi = dev->motor.base_ydpi;
+ if (dev->motor.motor_id == MOTOR_PLUSTEK_3600) /* TODO PLUSTEK_3600: 1200dpi not yet working, produces dark bar */
+ {
+ ydpi = 600;
+ }
+
+ dev->calib_channels = 3;
+ dev->calib_lines = dev->model->shading_lines;
+ status = gl841_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ ydpi,
+ 0,
+ 0,
+ (dev->sensor.sensor_pixels * dev->settings.xres) / dev->sensor.optical_res,
+ dev->calib_lines,
+ 16,
+ dev->calib_channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+/* we don't handle differing shading areas very well */
+/* SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |*/
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_USE_OPTICAL_RES
+ );
+
+ dev->calib_pixels = dev->current_setup.pixels;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init_registers_for_shading: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->scanhead_position_in_steps += dev->calib_lines;
+
+ status =
+ gl841_bulk_write_register (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init_registers_for_shading: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "gl841_init_regs_for_shading: completed\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* set up registers for the actual scan
+ */
+static SANE_Status
+gl841_init_regs_for_scan (Genesys_Device * dev)
+{
+ int channels;
+ int flags;
+ int depth;
+ float move;
+ int move_dpi;
+ float start;
+
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl841_init_regs_for_scan settings:\nResolution: %uDPI\n"
+ "Lines : %u\nPPL : %u\nStartpos : %.3f/%.3f\nScan mode : %d\n\n",
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ gl841_slow_back_home(dev,SANE_TRUE);
+
+/* channels */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+/* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART)
+ depth = 1;
+
+
+ /* steps to move to reach scanning area:
+ - first we move to physical start of scanning
+ either by a fixed steps amount from the black strip
+ or by a fixed amount from parking position,
+ minus the steps done during shading calibration
+ - then we move by the needed offset whitin physical
+ scanning area
+
+ assumption: steps are expressed at maximum motor resolution
+
+ we need:
+ SANE_Fixed y_offset;
+ SANE_Fixed y_size;
+ SANE_Fixed y_offset_calib;
+ mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
+
+ /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
+ relative from origin, else, it is from parking position */
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = 0;
+ if (dev->model->flags & GENESYS_FLAG_SEARCH_START)
+ move += SANE_UNFIX (dev->model->y_offset_calib);
+
+ DBG (DBG_info, "gl841_init_regs_for_scan: move=%f steps\n", move);
+
+ move += SANE_UNFIX (dev->model->y_offset);
+ DBG (DBG_info, "gl841_init_regs_for_scan: move=%f steps\n", move);
+
+ move += dev->settings.tl_y;
+ DBG (DBG_info, "gl841_init_regs_for_scan: move=%f steps\n", move);
+
+ move = (move * move_dpi) / MM_PER_INCH;
+
+/* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+
+ start += dev->settings.tl_x;
+
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+ flags=0;
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ flags = 0;
+
+ if(dev->model->is_cis && dev->settings.true_gray
+ &&dev->model->ccd_type != CCD_CANONLIDE35)
+ {
+ flags |= OPTICAL_FLAG_ENABLE_LEDADD;
+ }
+ /* enable emulated lineart from gray data */
+ if(dev->settings.scan_mode == SCAN_MODE_LINEART
+ && dev->settings.dynamic_lineart)
+ {
+ flags |= SCAN_FLAG_DYNAMIC_LINEART;
+ }
+
+ status = gl841_init_scan_regs (dev,
+ dev->reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ start,
+ move,
+ dev->settings.pixels,
+ dev->settings.lines,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ flags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ DBG (DBG_proc, "gl841_init_register_for_scan: completed\n");
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * this function sends generic gamma table (ie linear ones)
+ * or the Sensor specific one if provided
+ */
+static SANE_Status
+gl841_send_gamma_table (Genesys_Device * dev)
+{
+ int size;
+ SANE_Status status;
+ uint8_t *gamma;
+
+ DBGSTART;
+
+ size = 256;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ gamma = (uint8_t *) malloc (size * 2 * 3);
+ if (gamma==NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ RIE(sanei_genesys_generate_gamma_buffer(dev, 16, 65535, size, gamma));
+
+ /* send address */
+ status = gl841_set_buffer_address_gamma (dev, 0x00000);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "gl841_send_gamma_table: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send data */
+ status = gl841_bulk_write_data_gamma (dev, 0x28, (uint8_t *) gamma, size * 2 * 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "gl841_send_gamma_table: failed to send gamma table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ free (gamma);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* this function does the led calibration by scanning one line of the calibration
+ area below scanner's top on white strip.
+
+-needs working coarse/gain
+*/
+static SANE_Status
+gl841_led_calibration (Genesys_Device * dev)
+{
+ int num_pixels;
+ int total_size;
+ uint8_t *line;
+ int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ int channels;
+ int avg[3], avga, avge;
+ int turn;
+ char fn[20];
+ uint16_t expr, expg, expb;
+ Genesys_Register_Set *r;
+
+ SANE_Bool acceptable = SANE_FALSE;
+
+ /* these 2 boundaries should be per sensor */
+ uint16_t min_exposure=500;
+ uint16_t max_exposure;
+
+ DBG (DBG_proc, "gl841_led_calibration\n");
+
+
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+ status = gl841_feed(dev, 280);/*feed to white strip. canon lide 35 only.*/
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_led_calibration: failed to feed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+
+ status = gl841_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ (dev->sensor.sensor_pixels*dev->settings.xres) / dev->sensor.optical_res,
+ 1,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_USE_OPTICAL_RES
+ );
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_led_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (gl841_bulk_write_register(dev, dev->calib_reg, GENESYS_GL841_MAX_REGS));
+
+ num_pixels = dev->current_setup.pixels;
+
+ total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+/*
+ we try to get equal bright leds here:
+
+ loop:
+ average per color
+ adjust exposure times
+ */
+
+ expr = (dev->sensor.regs_0x10_0x1d[0] << 8) | dev->sensor.regs_0x10_0x1d[1];
+ expg = (dev->sensor.regs_0x10_0x1d[2] << 8) | dev->sensor.regs_0x10_0x1d[3];
+ expb = (dev->sensor.regs_0x10_0x1d[4] << 8) | dev->sensor.regs_0x10_0x1d[5];
+
+ turn = 0;
+ /* max exposure is set to ~2 time initial average
+ * exposure, or 2 time last calibration exposure */
+ max_exposure=((expr+expg+expb)/3)*2;
+
+ do {
+
+ dev->sensor.regs_0x10_0x1d[0] = (expr >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[1] = expr & 0xff;
+ dev->sensor.regs_0x10_0x1d[2] = (expg >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[3] = expg & 0xff;
+ dev->sensor.regs_0x10_0x1d[4] = (expb >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[5] = expb & 0xff;
+
+ r = &(dev->calib_reg[reg_0x10]);
+ for (i = 0; i < 6; i++, r++) {
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ RIE (sanei_genesys_write_register (dev, 0x10+i, dev->sensor.regs_0x10_0x1d[i]));
+ }
+
+ RIE (gl841_bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS));
+
+ DBG (DBG_info,
+ "gl841_led_calibration: starting first line reading\n");
+ RIE (gl841_begin_scan (dev, dev->calib_reg, SANE_TRUE));
+ RIE (sanei_genesys_read_data_from_scanner (dev, line, total_size));
+
+ if (DBG_LEVEL >= DBG_data) {
+ snprintf(fn,20,"led_%d.pnm",turn);
+ sanei_genesys_write_pnm_file (fn,
+ line,
+ 16,
+ channels,
+ num_pixels, 1);
+ }
+
+ acceptable = SANE_TRUE;
+
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG(DBG_info,"gl841_led_calibration: average: "
+ "%d,%d,%d\n",
+ avg[0],avg[1],avg[2]);
+
+ acceptable = SANE_TRUE;
+
+ if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
+ avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
+ avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
+ acceptable = SANE_FALSE;
+
+ if (!acceptable) {
+ avga = (avg[0]+avg[1]+avg[2])/3;
+ expr = (expr * avga) / avg[0];
+ expg = (expg * avga) / avg[1];
+ expb = (expb * avga) / avg[2];
+/*
+ keep the resulting exposures below this value.
+ too long exposure drives the ccd into saturation.
+ we may fix this by relying on the fact that
+ we get a striped scan without shading, by means of
+ statistical calculation
+*/
+ avge = (expr + expg + expb) / 3;
+
+ if (avge > max_exposure) {
+ expr = (expr * max_exposure) / avge;
+ expg = (expg * max_exposure) / avge;
+ expb = (expb * max_exposure) / avge;
+ }
+ if (avge < min_exposure) {
+ expr = (expr * min_exposure) / avge;
+ expg = (expg * min_exposure) / avge;
+ expb = (expb * min_exposure) / avge;
+ }
+
+ }
+
+ RIE (gl841_stop_action (dev));
+
+ turn++;
+
+ } while (!acceptable && turn < 100);
+
+ DBG(DBG_info,"gl841_led_calibration: acceptable exposure: %d,%d,%d\n",
+ expr,expg,expb);
+
+ /* cleanup before return */
+ free (line);
+
+ gl841_slow_back_home(dev, SANE_TRUE);
+
+ DBG (DBG_proc, "gl841_led_calibration: completed\n");
+ return status;
+}
+
+/** @brief calibration for AD frontend devices
+ * experiments show that modifying offset is of little (if no) influence
+ * so we just return
+ * CHRIS: This was added from gl646.c as again offset seems to make no
+ * difference
+ *
+ * TODO PLUSTEK_3600 Michael Rickmann:
+ * offset calibration makes a lot of a difference but currently
+ * makes everything to dark
+ */
+static SANE_Status
+ad_fe_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "ad_fe_offset_calibration: start\n");
+ DBG (DBG_info, "ad_fe_offset_calibration: offset=(%d,%d,%d)\n",
+ dev->frontend.offset[0], dev->frontend.offset[1],
+ dev->frontend.offset[2]);
+ DBG (DBG_proc, "ad_fe_offset_calibration: end\n");
+ return status;
+}
+
+/* this function does the offset calibration by scanning one line of the calibration
+ area below scanner's top. There is a black margin and the remaining is white.
+ sanei_genesys_search_start() must have been called so that the offsets and margins
+ are allready known.
+
+this function expects the slider to be where?
+*/
+static SANE_Status
+gl841_offset_calibration (Genesys_Device * dev)
+{
+ int num_pixels;
+ int total_size;
+ uint8_t *first_line, *second_line;
+ int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ int channels;
+ int off[3],offh[3],offl[3],off1[3],off2[3];
+ int min1[3],min2[3];
+ int cmin[3],cmax[3];
+ int turn;
+ char fn[20];
+ SANE_Bool acceptable = SANE_FALSE;
+ int mintgt = 0x400;
+
+ /* Analog Device fronted have a different calibration */
+ if (dev->model->dac_type == DAC_PLUSTEK_3600)
+ {
+ return ad_fe_offset_calibration (dev);
+ }
+
+ DBG (DBG_proc, "gl841_offset_calibration\n");
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+
+ status = gl841_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ (dev->sensor.sensor_pixels*dev->settings.xres) / dev->sensor.optical_res,
+ 1,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_USE_OPTICAL_RES |
+ SCAN_FLAG_DISABLE_LAMP
+ );
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_offset_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ num_pixels = dev->current_setup.pixels;
+
+ total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ first_line = malloc (total_size);
+ if (!first_line)
+ return SANE_STATUS_NO_MEM;
+
+ second_line = malloc (total_size);
+ if (!second_line)
+ {
+ free (first_line);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* scan first line of data with no offset nor gain */
+/*WM8199: gain=0.73; offset=-260mV*/
+/*okay. the sensor black level is now at -260mV. we only get 0 from AFE...*/
+/* we should probably do real calibration here:
+ * -detect acceptable offset with binary search
+ * -calculate offset from this last version
+ *
+ * acceptable offset means
+ * - few completely black pixels(<10%?)
+ * - few completely white pixels(<10%?)
+ *
+ * final offset should map the minimum not completely black
+ * pixel to 0(16 bits)
+ *
+ * this does account for dummy pixels at the end of ccd
+ * this assumes slider is at black strip(which is not quite as black as "no
+ * signal").
+ *
+ */
+ dev->frontend.gain[0] = 0x00;
+ dev->frontend.gain[1] = 0x00;
+ dev->frontend.gain[2] = 0x00;
+ offh[0] = 0xff;
+ offh[1] = 0xff;
+ offh[2] = 0xff;
+ offl[0] = 0x00;
+ offl[1] = 0x00;
+ offl[2] = 0x00;
+ turn = 0;
+
+ do {
+
+ RIEF2 (gl841_bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS), first_line, second_line);
+
+ for (j=0; j < channels; j++) {
+ off[j] = (offh[j]+offl[j])/2;
+ dev->frontend.offset[j] = off[j];
+ }
+
+ status = gl841_set_fe(dev, AFE_SET);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_offset_calibration: failed to setup frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info,
+ "gl841_offset_calibration: starting first line reading\n");
+ RIEF2 (gl841_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, first_line, total_size), first_line, second_line);
+
+ if (DBG_LEVEL >= DBG_data) {
+ snprintf(fn,20,"offset1_%02d.pnm",turn);
+ sanei_genesys_write_pnm_file (fn,
+ first_line,
+ 16,
+ channels,
+ num_pixels, 1);
+ }
+
+ acceptable = SANE_TRUE;
+
+ for (j = 0; j < channels; j++)
+ {
+ cmin[j] = 0;
+ cmax[j] = 0;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ first_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ first_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ first_line[i * 2 * channels + 2 * j + 1] * 256 +
+ first_line[i * 2 * channels + 2 * j];
+ if (val < 10)
+ cmin[j]++;
+ if (val > 65525)
+ cmax[j]++;
+ }
+
+ /* TODO the DP685 has a black strip in the middle of the sensor
+ * should be handled in a more elegant way , could be a bug */
+ if (dev->model->ccd_type == CCD_DP685)
+ cmin[j] -= 20;
+
+ if (cmin[j] > num_pixels/100) {
+ acceptable = SANE_FALSE;
+ if (dev->model->is_cis)
+ offl[0] = off[0];
+ else
+ offl[j] = off[j];
+ }
+ if (cmax[j] > num_pixels/100) {
+ acceptable = SANE_FALSE;
+ if (dev->model->is_cis)
+ offh[0] = off[0];
+ else
+ offh[j] = off[j];
+ }
+ }
+
+ DBG(DBG_info,"gl841_offset_calibration: black/white pixels: "
+ "%d/%d,%d/%d,%d/%d\n",
+ cmin[0],cmax[0],cmin[1],cmax[1],cmin[2],cmax[2]);
+
+ if (dev->model->is_cis) {
+ offh[2] = offh[1] = offh[0];
+ offl[2] = offl[1] = offl[0];
+ }
+
+ RIEF2 (gl841_stop_action (dev), first_line, second_line);
+
+ turn++;
+ } while (!acceptable && turn < 100);
+
+ DBG(DBG_info,"gl841_offset_calibration: acceptable offsets: %d,%d,%d\n",
+ off[0],off[1],off[2]);
+
+
+ for (j = 0; j < channels; j++)
+ {
+ off1[j] = off[j];
+
+ min1[j] = 65536;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ first_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ first_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ first_line[i * 2 * channels + 2 * j + 1] * 256 +
+ first_line[i * 2 * channels + 2 * j];
+ if (min1[j] > val && val >= 10)
+ min1[j] = val;
+ }
+ }
+
+
+ offl[0] = off[0];
+ offl[1] = off[0];
+ offl[2] = off[0];
+ turn = 0;
+
+ do {
+
+ for (j=0; j < channels; j++) {
+ off[j] = (offh[j]+offl[j])/2;
+ dev->frontend.offset[j] = off[j];
+ }
+
+ status = gl841_set_fe(dev, AFE_SET);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (first_line);
+ free (second_line);
+ DBG (DBG_error,
+ "gl841_offset_calibration: failed to setup frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info,
+ "gl841_offset_calibration: starting second line reading\n");
+ RIEF2 (gl841_bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS), first_line, second_line);
+ RIEF2 (gl841_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ if (DBG_LEVEL >= DBG_data) {
+ snprintf(fn,20,"offset2_%d.pnm",turn);
+ sanei_genesys_write_pnm_file (fn,
+ second_line,
+ 16,
+ channels,
+ num_pixels, 1);
+ }
+
+ acceptable = SANE_TRUE;
+
+ for (j = 0; j < channels; j++)
+ {
+ cmin[j] = 0;
+ cmax[j] = 0;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ second_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ second_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ second_line[i * 2 * channels + 2 * j + 1] * 256 +
+ second_line[i * 2 * channels + 2 * j];
+ if (val < 10)
+ cmin[j]++;
+ if (val > 65525)
+ cmax[j]++;
+ }
+
+ if (cmin[j] > num_pixels/100) {
+ acceptable = SANE_FALSE;
+ if (dev->model->is_cis)
+ offl[0] = off[0];
+ else
+ offl[j] = off[j];
+ }
+ if (cmax[j] > num_pixels/100) {
+ acceptable = SANE_FALSE;
+ if (dev->model->is_cis)
+ offh[0] = off[0];
+ else
+ offh[j] = off[j];
+ }
+ }
+
+ DBG(DBG_info,"gl841_offset_calibration: black/white pixels: "
+ "%d/%d,%d/%d,%d/%d\n",
+ cmin[0],cmax[0],cmin[1],cmax[1],cmin[2],cmax[2]);
+
+ if (dev->model->is_cis) {
+ offh[2] = offh[1] = offh[0];
+ offl[2] = offl[1] = offl[0];
+ }
+
+ RIEF2 (gl841_stop_action (dev), first_line, second_line);
+
+ turn++;
+
+ } while (!acceptable && turn < 100);
+
+ DBG(DBG_info,"gl841_offset_calibration: acceptable offsets: %d,%d,%d\n",
+ off[0],off[1],off[2]);
+
+
+ for (j = 0; j < channels; j++)
+ {
+ off2[j] = off[j];
+
+ min2[j] = 65536;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ second_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ second_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ second_line[i * 2 * channels + 2 * j + 1] * 256 +
+ second_line[i * 2 * channels + 2 * j];
+ if (min2[j] > val && val != 0)
+ min2[j] = val;
+ }
+ }
+
+ DBG(DBG_info,"gl841_offset_calibration: first set: %d/%d,%d/%d,%d/%d\n",
+ off1[0],min1[0],off1[1],min1[1],off1[2],min1[2]);
+
+ DBG(DBG_info,"gl841_offset_calibration: second set: %d/%d,%d/%d,%d/%d\n",
+ off2[0],min2[0],off2[1],min2[1],off2[2],min2[2]);
+
+/*
+ calculate offset for each channel
+ based on minimal pixel value min1 at offset off1 and minimal pixel value min2
+ at offset off2
+
+ to get min at off, values are linearly interpolated:
+ min=real+off*fact
+ min1=real+off1*fact
+ min2=real+off2*fact
+
+ fact=(min1-min2)/(off1-off2)
+ real=min1-off1*(min1-min2)/(off1-off2)
+
+ off=(min-min1+off1*(min1-min2)/(off1-off2))/((min1-min2)/(off1-off2))
+
+ off=(min*(off1-off2)+min1*off2-off1*min2)/(min1-min2)
+
+ */
+ for (j = 0; j < channels; j++)
+ {
+ if (min2[j]-min1[j] == 0) {
+/*TODO: try to avoid this*/
+ DBG(DBG_warn,"gl841_offset_calibration: difference too small\n");
+ if (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j] >= 0)
+ off[j] = 0x0000;
+ else
+ off[j] = 0xffff;
+ } else
+ off[j] = (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j])/(min1[j]-min2[j]);
+ if (off[j] > 255)
+ off[j] = 255;
+ if (off[j] < 0)
+ off[j] = 0;
+ dev->frontend.offset[j] = off[j];
+ }
+
+ DBG(DBG_info,"gl841_offset_calibration: final offsets: %d,%d,%d\n",
+ off[0],off[1],off[2]);
+
+ if (dev->model->is_cis) {
+ if (off[0] < off[1])
+ off[0] = off[1];
+ if (off[0] < off[2])
+ off[0] = off[2];
+ dev->frontend.offset[0] = off[0];
+ dev->frontend.offset[1] = off[0];
+ dev->frontend.offset[2] = off[0];
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.offset[1] = dev->frontend.offset[0];
+ dev->frontend.offset[2] = dev->frontend.offset[0];
+ }
+
+ /* cleanup before return */
+ free (first_line);
+ free (second_line);
+ DBG (DBG_proc, "gl841_offset_calibration: completed\n");
+ return status;
+}
+
+
+/* alternative coarse gain calibration
+ this on uses the settings from offset_calibration and
+ uses only one scanline
+ */
+/*
+ with offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+ */
+static SANE_Status
+gl841_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ int num_pixels;
+ int total_size;
+ uint8_t *line;
+ int i, j, channels;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int max[3];
+ float gain[3];
+ int val;
+
+ DBG (DBG_proc, "gl841_coarse_gain_calibration dpi=%d\n", dpi);
+
+ if (dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+ status = gl841_feed(dev, 280);/*feed to white strip. canon lide 35 only.*/
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_coarse_gain_calibration: failed to feed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* coarse gain calibration is allways done in color mode */
+ channels = 3;
+
+ status = gl841_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ (dev->sensor.sensor_pixels*dev->settings.xres) / dev->sensor.optical_res,
+ 1,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_USE_OPTICAL_RES
+ );
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (gl841_bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS));
+
+ num_pixels = dev->current_setup.pixels;
+
+ total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ RIEF (gl841_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("coarse.pnm", line, 16,
+ channels, num_pixels, 1);
+
+ /* average high level for each channel and compute gain
+ to reach the target code
+ we only use the central half of the CCD data */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+
+ if (val > max[j])
+ max[j] = val;
+ }
+
+ gain[j] = 65535.0/max[j];
+
+ if (dev->model->dac_type == DAC_CANONLIDE35 ||
+ dev->model->dac_type == DAC_WOLFSON_XP300 ||
+ dev->model->dac_type == DAC_WOLFSON_DSM600) {
+ gain[j] *= 0.69;/*seems we don't get the real maximum. empirically derived*/
+ if (283 - 208/gain[j] > 255)
+ dev->frontend.gain[j] = 255;
+ else if (283 - 208/gain[j] < 0)
+ dev->frontend.gain[j] = 0;
+ else
+ dev->frontend.gain[j] = 283 - 208/gain[j];
+ }
+
+ DBG (DBG_proc,
+ "gl841_coarse_gain_calibration: channel %d, max=%d, gain = %f, setting:%d\n",
+ j, max[j], gain[j],dev->frontend.gain[j]);
+ }
+
+ for (j = 0; j < channels; j++)
+ {
+ if(gain[j] > 10)
+ {
+ DBG (DBG_error0, "**********************************************\n");
+ DBG (DBG_error0, "**********************************************\n");
+ DBG (DBG_error0, "**** ****\n");
+ DBG (DBG_error0, "**** Extremely low Brightness detected. ****\n");
+ DBG (DBG_error0, "**** Check the scanning head is ****\n");
+ DBG (DBG_error0, "**** unlocked and moving. ****\n");
+ DBG (DBG_error0, "**** ****\n");
+ DBG (DBG_error0, "**********************************************\n");
+ DBG (DBG_error0, "**********************************************\n");
+#ifdef SANE_STATUS_HW_LOCKED
+ return SANE_STATUS_HW_LOCKED;
+#else
+ return SANE_STATUS_JAMMED;
+#endif
+ }
+
+ }
+
+ if (dev->model->is_cis) {
+ if (dev->frontend.gain[0] > dev->frontend.gain[1])
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ if (dev->frontend.gain[0] > dev->frontend.gain[2])
+ dev->frontend.gain[0] = dev->frontend.gain[2];
+ dev->frontend.gain[2] = dev->frontend.gain[1] = dev->frontend.gain[0];
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ dev->frontend.gain[2] = dev->frontend.gain[1];
+ }
+
+ free (line);
+
+ RIE (gl841_stop_action (dev));
+
+ gl841_slow_back_home(dev, SANE_TRUE);
+
+ DBG (DBG_proc, "gl841_coarse_gain_calibration: completed\n");
+ return status;
+}
+
+/*
+ * wait for lamp warmup by scanning the same line until difference
+ * between 2 scans is below a threshold
+ */
+static SANE_Status
+gl841_init_regs_for_warmup (Genesys_Device * dev,
+ Genesys_Register_Set * local_reg,
+ int *channels, int *total_size)
+{
+ int num_pixels = (int) (4 * 300);
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "sanei_gl841_warmup_lamp\n");
+
+ memcpy (local_reg, dev->reg, (GENESYS_GL841_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+
+/* okay.. these should be defaults stored somewhere */
+ dev->frontend.gain[0] = 0x00;
+ dev->frontend.gain[1] = 0x00;
+ dev->frontend.gain[2] = 0x00;
+ dev->frontend.offset[0] = 0x80;
+ dev->frontend.offset[1] = 0x80;
+ dev->frontend.offset[2] = 0x80;
+
+ status = gl841_init_scan_regs (dev,
+ local_reg,
+ dev->sensor.optical_res,
+ dev->settings.yres,
+ dev->sensor.dummy_pixel,
+ 0,
+ num_pixels,
+ 1,
+ 16,
+ *channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_USE_OPTICAL_RES
+ );
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init_regs_for_warmup: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ num_pixels = dev->current_setup.pixels;
+
+ *total_size = num_pixels * 3 * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ RIE (gl841_bulk_write_register
+ (dev, local_reg, GENESYS_GL841_MAX_REGS));
+
+ return status;
+}
+
+
+/*
+ * this function moves head without scanning, forward, then backward
+ * so that the head goes to park position.
+ * as a by-product, also check for lock
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+sanei_gl841_repark_head (Genesys_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "sanei_gl841_repark_head\n");
+
+ status = gl841_feed(dev,232);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_repark_head: failed to feed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* toggle motor flag, put an huge step number and redo move backward */
+ status = gl841_slow_back_home (dev, SANE_TRUE);
+ DBG (DBG_proc, "gl841_park_head: completed\n");
+ return status;
+}
+
+static SANE_Status
+gl841_is_compatible_calibration (Genesys_Device * dev,
+ Genesys_Calibration_Cache *cache,
+ int for_overwrite)
+{
+ SANE_Status status;
+#ifdef HAVE_SYS_TIME_H
+ struct timeval time;
+#endif
+
+ DBGSTART;
+
+ /* calibration cache not working yet for this model */
+ if (dev->model->ccd_type == CCD_PLUSTEK_3600)
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ status = gl841_calculate_current_setup (dev);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_is_compatible_calibration: failed to calculate current setup: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "gl841_is_compatible_calibration: checking\n");
+
+ if (dev->current_setup.half_ccd != cache->used_setup.half_ccd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* a cache entry expires after 30 minutes for non sheetfed scanners */
+ /* this is not taken into account when overwriting cache entries */
+#ifdef HAVE_SYS_TIME_H
+ if(for_overwrite == SANE_FALSE)
+ {
+ gettimeofday (&time, NULL);
+ if ((time.tv_sec - cache->last_calibration > 30 * 60)
+ && (dev->model->is_sheetfed == SANE_FALSE))
+ {
+ DBG (DBG_proc, "%s: expired entry, non compatible cache\n",__FUNCTION__);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+#endif
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * initialize ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+static SANE_Status
+gl841_init (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val;
+ size_t size;
+ uint8_t *line;
+ int i;
+
+ DBG_INIT ();
+ DBGSTART;
+
+ dev->scanhead_position_in_steps = 0;
+
+ /* Check if the device has already been initialized and powered up */
+ if (dev->already_initialized)
+ {
+ RIE (sanei_genesys_get_status (dev, &val));
+ if (val & REG41_PWRBIT)
+ {
+ DBG (DBG_info, "gl841_init: already initialized\n");
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ dev->dark_average_data = NULL;
+ dev->white_average_data = NULL;
+
+ dev->settings.color_filter = 0;
+
+ /* Set default values for registers */
+ gl841_init_registers (dev);
+
+ /* ASIC reset */
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
+
+ /* Write initial registers */
+ RIE (gl841_bulk_write_register
+ (dev, dev->reg, GENESYS_GL841_MAX_REGS));
+
+ /* Test ASIC and RAM */
+ if (!(dev->model->flags & GENESYS_FLAG_LAZY_INIT))
+ {
+ RIE (sanei_gl841_asic_test (dev));
+ }
+
+ /* Set analog frontend */
+ RIE (gl841_set_fe (dev, AFE_INIT));
+
+ /* Move home */
+ RIE (gl841_slow_back_home (dev, SANE_TRUE));
+
+ /* Init shading data */
+ RIE (sanei_genesys_init_shading_data (dev, dev->sensor.sensor_pixels));
+
+ /* ensure head is correctly parked, and check lock */
+ if (dev->model->flags & GENESYS_FLAG_REPARK)
+ {
+ status = sanei_gl841_repark_head (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_INVAL)
+ DBG (DBG_error0,
+ "Your scanner is locked. Please move the lock switch "
+ "to the unlocked position\n");
+ else
+ DBG (DBG_error,
+ "gl841_init: sanei_gl841_repark_head failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* initalize sensor gamma tables */
+ size = 256;
+
+ for(i=0;i<3;i++)
+ {
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ dev->sensor.gamma_table[i] = (uint16_t *) malloc (2 * size);
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ DBG (DBG_error,
+ "gl841_init: could not allocate memory for gamma table %d\n",i);
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_genesys_create_gamma_table (dev->sensor.gamma_table[i],
+ size,
+ 65535,
+ 65535,
+ dev->sensor.gamma[i]);
+ }
+ }
+
+ /* send gamma tables */
+ status = gl841_send_gamma_table (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_init: failed to send initial gamma tables: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, (GENESYS_GL841_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+
+ status = gl841_init_scan_regs (dev,
+ dev->calib_reg,
+ 300,
+ 300,
+ 0,
+ 0,
+ (16 * 300) / dev->sensor.optical_res,
+ 1,
+ 16,
+ 3,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_USE_OPTICAL_RES
+ );
+
+ RIE (gl841_bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL841_MAX_REGS));
+
+ size = dev->current_setup.pixels * 3 * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ line = malloc (size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ DBG (DBG_info,
+ "gl841_init: starting dummy data reading\n");
+ RIEF (gl841_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+
+ sanei_usb_set_timeout(1000);/* 1 second*/
+
+/*ignore errors. next read will succeed*/
+ sanei_genesys_read_data_from_scanner (dev, line, size);
+ free(line);
+
+ sanei_usb_set_timeout(30 * 1000);/* 30 seconds*/
+
+ RIE (gl841_end_scan (dev, dev->calib_reg, SANE_TRUE));
+
+
+ memcpy (dev->calib_reg, dev->reg, (GENESYS_GL841_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+
+ /* Set powersaving (default = 15 minutes) */
+ RIE (gl841_set_powersaving (dev, 15));
+ dev->already_initialized = SANE_TRUE;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl841_update_hardware_sensors (Genesys_Scanner * s)
+{
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t val;
+
+ if (s->dev->model->gpo_type == GPO_CANONLIDE35)
+ {
+ RIE(sanei_genesys_read_register(s->dev, REG6D, &val));
+
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & 0x01) == 0;
+ if (s->val[OPT_FILE_SW].b == s->last_val[OPT_FILE_SW].b)
+ s->val[OPT_FILE_SW].b = (val & 0x02) == 0;
+ if (s->val[OPT_EMAIL_SW].b == s->last_val[OPT_EMAIL_SW].b)
+ s->val[OPT_EMAIL_SW].b = (val & 0x04) == 0;
+ if (s->val[OPT_COPY_SW].b == s->last_val[OPT_COPY_SW].b)
+ s->val[OPT_COPY_SW].b = (val & 0x08) == 0;
+ }
+
+ if (s->dev->model->gpo_type == GPO_XP300 ||
+ s->dev->model->gpo_type == GPO_DP665 ||
+ s->dev->model->gpo_type == GPO_DP685)
+ {
+ RIE(sanei_genesys_read_register(s->dev, REG6D, &val));
+
+ if (s->val[OPT_PAGE_LOADED_SW].b == s->last_val[OPT_PAGE_LOADED_SW].b)
+ s->val[OPT_PAGE_LOADED_SW].b = (val & 0x01) == 0;
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & 0x02) == 0;
+ }
+
+ return status;
+}
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
+ * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
+ * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
+ */
+static SANE_Status
+gl841_search_strip (Genesys_Device * dev, SANE_Bool forward, SANE_Bool black)
+{
+ unsigned int pixels, lines, channels;
+ SANE_Status status;
+ Genesys_Register_Set local_reg[GENESYS_GL841_MAX_REGS + 1];
+ size_t size;
+ uint8_t *data;
+ int steps, depth, dpi;
+ unsigned int pass, count, found, x, y, length;
+ char title[80];
+ Genesys_Register_Set *r;
+ uint8_t white_level=90; /**< default white level to detect white dots */
+ uint8_t black_level=60; /**< default black level to detect black dots */
+
+ DBG (DBG_proc, "gl841_search_strip %s %s\n", black ? "black" : "white",
+ forward ? "forward" : "reverse");
+
+ /* use maximum gain when doing forward white strip detection
+ * since we don't have calibrated the sensor yet */
+ if(!black && forward)
+ {
+ dev->frontend.gain[0] = 0xff;
+ dev->frontend.gain[1] = 0xff;
+ dev->frontend.gain[2] = 0xff;
+ }
+
+ gl841_set_fe (dev, AFE_SET);
+ status = gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_search_strip: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for a gray scan at lowest dpi */
+ dpi = 9600;
+ for (x = 0; x < MAX_RESOLUTIONS; x++)
+ {
+ if (dev->model->xdpi_values[x] > 0 && dev->model->xdpi_values[x] < dpi)
+ dpi = dev->model->xdpi_values[x];
+ }
+ channels = 1;
+
+ /* shading calibation is done with dev->motor.base_ydpi */
+ /* lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; */
+ lines = (10*dpi)/MM_PER_INCH;
+
+ depth = 8;
+ pixels = (dev->sensor.sensor_pixels * dpi) / dev->sensor.optical_res;
+ size = pixels * channels * lines * (depth / 8);
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error, "gl841_search_strip: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* 20 cm max length for calibration sheet */
+ length = ((200 * dpi) / MM_PER_INCH)/lines;
+
+ dev->scanhead_position_in_steps = 0;
+
+ memcpy (local_reg, dev->reg,
+ (GENESYS_GL841_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+
+ status = gl841_init_scan_regs (dev,
+ local_reg,
+ dpi,
+ dpi,
+ 0,
+ 0,
+ pixels,
+ lines,
+ depth,
+ channels,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error, "%s: failed to setup for scan: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for reverse or forward */
+ r = sanei_genesys_get_address (local_reg, 0x02);
+ if (forward)
+ r->value &= ~4;
+ else
+ r->value |= 4;
+
+
+ status = gl841_bulk_write_register (dev, local_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl841_search_strip: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl841_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl841_search_strip: gl841_stop_action failed\n");
+ return status;
+ }
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02u.pnm", black ? "black" : "white",
+ forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels, pixels,
+ lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < length && !found)
+ {
+ status =
+ gl841_bulk_write_register (dev, local_reg, GENESYS_GL841_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl841_search_strip: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* now start scan */
+ status = gl841_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl841_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl841_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl841_search_strip: gl841_stop_action failed\n");
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02u.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels, pixels,
+ lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > white_level)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < black_level)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl841_search_strip: strip found forward during pass %d at line %d\n",
+ pass, y);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl841_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > white_level)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < black_level)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl841_search_strip: strip found backward during pass %d \n",
+ pass);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl841_search_strip: pixels=%d, count=%d (%d%%)\n", pixels,
+ count, (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+ free (data);
+ if (found)
+ {
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_info, "gl841_search_strip: %s strip found\n",
+ black ? "black" : "white");
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ DBG (DBG_info, "gl841_search_strip: %s strip not found\n",
+ black ? "black" : "white");
+ }
+
+ DBG (DBG_proc, "gl841_search_strip: completed\n");
+ return status;
+}
+
+/** the gl841 command set */
+static Genesys_Command_Set gl841_cmd_set = {
+ "gl841-generic", /* the name of this set */
+
+ gl841_init,
+ gl841_init_regs_for_warmup,
+ gl841_init_regs_for_coarse_calibration,
+ gl841_init_regs_for_shading,
+ gl841_init_regs_for_scan,
+
+ gl841_get_filter_bit,
+ gl841_get_lineart_bit,
+ gl841_get_bitset_bit,
+ gl841_get_gain4_bit,
+ gl841_get_fast_feed_bit,
+ gl841_test_buffer_empty_bit,
+ gl841_test_motor_flag_bit,
+
+ gl841_bulk_full_size,
+
+ gl841_set_fe,
+ gl841_set_powersaving,
+ gl841_save_power,
+
+ gl841_set_motor_power,
+ gl841_set_lamp_power,
+
+ gl841_begin_scan,
+ gl841_end_scan,
+
+ gl841_send_gamma_table,
+
+ gl841_search_start_position,
+
+ gl841_offset_calibration,
+ gl841_coarse_gain_calibration,
+ gl841_led_calibration,
+
+ gl841_slow_back_home,
+
+ gl841_bulk_write_register,
+ gl841_bulk_write_data,
+ gl841_bulk_read_data,
+
+ gl841_update_hardware_sensors,
+
+ gl841_load_document,
+ gl841_detect_document_end,
+ gl841_eject_document,
+ gl841_search_strip,
+
+ gl841_is_compatible_calibration,
+ NULL,
+ NULL,
+ gl841_calculate_current_setup,
+ NULL
+};
+
+SANE_Status
+sanei_gl841_init_cmd_set (Genesys_Device * dev)
+{
+ dev->model->cmd_set = &gl841_cmd_set;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl841.h b/backend/genesys_gl841.h
new file mode 100644
index 0000000..bbf2ee1
--- /dev/null
+++ b/backend/genesys_gl841.h
@@ -0,0 +1,390 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2011-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+
+/* Individual bits */
+#define REG01 0x01
+#define REG01_CISSET 0x80
+#define REG01_DOGENB 0x40
+#define REG01_DVDSET 0x20
+#define REG01_M16DRAM 0x08
+#define REG01_DRAMSEL 0x04
+#define REG01_SHDAREA 0x02
+#define REG01_SCAN 0x01
+
+#define REG02 0x02
+#define REG02_NOTHOME 0x80
+#define REG02_ACDCDIS 0x40
+#define REG02_AGOHOME 0x20
+#define REG02_MTRPWR 0x10
+#define REG02_FASTFED 0x08
+#define REG02_MTRREV 0x04
+#define REG02_HOMENEG 0x02
+#define REG02_LONGCURV 0x01
+
+#define REG03_LAMPDOG 0x80
+#define REG03_AVEENB 0x40
+#define REG03_XPASEL 0x20
+#define REG03_LAMPPWR 0x10
+#define REG03_LAMPTIM 0x0f
+
+#define REG04_LINEART 0x80
+#define REG04_BITSET 0x40
+#define REG04_AFEMOD 0x30
+#define REG04_FILTER 0x0c
+#define REG04_FESET 0x03
+
+#define REG04S_AFEMOD 4
+
+#define REG05_DPIHW 0xc0
+#define REG05_DPIHW_600 0x00
+#define REG05_DPIHW_1200 0x40
+#define REG05_DPIHW_2400 0x80
+#define REG05_MTLLAMP 0x30
+#define REG05_GMMENB 0x08
+#define REG05_MTLBASE 0x03
+
+#define REG06_SCANMOD 0xe0
+#define REG06S_SCANMOD 5
+#define REG06_PWRBIT 0x10
+#define REG06_GAIN4 0x08
+#define REG06_OPTEST 0x07
+
+#define REG07_SRAMSEL 0x08
+#define REG07_FASTDMA 0x04
+#define REG07_DMASEL 0x02
+#define REG07_DMARDWR 0x01
+
+#define REG08_DECFLAG 0x40
+#define REG08_GMMFFR 0x20
+#define REG08_GMMFFG 0x10
+#define REG08_GMMFFB 0x08
+#define REG08_GMMZR 0x04
+#define REG08_GMMZG 0x02
+#define REG08_GMMZB 0x01
+
+#define REG09_MCNTSET 0xc0
+#define REG09_CLKSET 0x30
+#define REG09_BACKSCAN 0x08
+#define REG09_ENHANCE 0x04
+#define REG09_SHORTTG 0x02
+#define REG09_NWAIT 0x01
+
+#define REG09S_MCNTSET 6
+#define REG09S_CLKSET 4
+
+
+#define REG0A_SRAMBUF 0x01
+
+#define REG0D 0x0d
+#define REG0D_CLRLNCNT 0x01
+
+#define REG16_CTRLHI 0x80
+#define REG16_TOSHIBA 0x40
+#define REG16_TGINV 0x20
+#define REG16_CK1INV 0x10
+#define REG16_CK2INV 0x08
+#define REG16_CTRLINV 0x04
+#define REG16_CKDIS 0x02
+#define REG16_CTRLDIS 0x01
+
+#define REG17_TGMODE 0xc0
+#define REG17_TGMODE_NO_DUMMY 0x00
+#define REG17_TGMODE_REF 0x40
+#define REG17_TGMODE_XPA 0x80
+#define REG17_TGW 0x3f
+#define REG17S_TGW 0
+
+#define REG18_CNSET 0x80
+#define REG18_DCKSEL 0x60
+#define REG18_CKTOGGLE 0x10
+#define REG18_CKDELAY 0x0c
+#define REG18_CKSEL 0x03
+
+#define REG1A_MANUAL3 0x02
+#define REG1A_MANUAL1 0x01
+#define REG1A_CK4INV 0x08
+#define REG1A_CK3INV 0x04
+#define REG1A_LINECLP 0x02
+
+#define REG1C_TGTIME 0x07
+
+#define REG1D_CK4LOW 0x80
+#define REG1D_CK3LOW 0x40
+#define REG1D_CK1LOW 0x20
+#define REG1D_TGSHLD 0x1f
+#define REG1DS_TGSHLD 0
+
+
+#define REG1E_WDTIME 0xf0
+#define REG1ES_WDTIME 4
+#define REG1E_LINESEL 0x0f
+#define REG1ES_LINESEL 0
+
+#define REG40_HISPDFLG 0x04
+#define REG40_MOTMFLG 0x02
+#define REG40_DATAENB 0x01
+
+#define REG41_PWRBIT 0x80
+#define REG41_BUFEMPTY 0x40
+#define REG41_FEEDFSH 0x20
+#define REG41_SCANFSH 0x10
+#define REG41_HOMESNR 0x08
+#define REG41_LAMPSTS 0x04
+#define REG41_FEBUSY 0x02
+#define REG41_MOTORENB 0x01
+
+#define REG58_VSMP 0xf8
+#define REG58S_VSMP 3
+#define REG58_VSMPW 0x07
+#define REG58S_VSMPW 0
+
+#define REG59_BSMP 0xf8
+#define REG59S_BSMP 3
+#define REG59_BSMPW 0x07
+#define REG59S_BSMPW 0
+
+#define REG5A_ADCLKINV 0x80
+#define REG5A_RLCSEL 0x40
+#define REG5A_CDSREF 0x30
+#define REG5AS_CDSREF 4
+#define REG5A_RLC 0x0f
+#define REG5AS_RLC 0
+
+#define REG5E_DECSEL 0xe0
+#define REG5ES_DECSEL 5
+#define REG5E_STOPTIM 0x1f
+#define REG5ES_STOPTIM 0
+
+#define REG60_ZIMOD 0x1f
+#define REG61_Z1MOD 0xff
+#define REG62_Z1MOD 0xff
+
+#define REG63_Z2MOD 0x1f
+#define REG64_Z2MOD 0xff
+#define REG65_Z2MOD 0xff
+
+#define REG67_STEPSEL 0xc0
+#define REG67_FULLSTEP 0x00
+#define REG67_HALFSTEP 0x40
+#define REG67_QUATERSTEP 0x80
+#define REG67_MTRPWM 0x3f
+
+#define REG68_FSTPSEL 0xc0
+#define REG68_FULLSTEP 0x00
+#define REG68_HALFSTEP 0x40
+#define REG68_QUATERSTEP 0x80
+#define REG68_FASTPWM 0x3f
+
+#define REG6B_MULTFILM 0x80
+#define REG6B_GPOM13 0x40
+#define REG6B_GPOM12 0x20
+#define REG6B_GPOM11 0x10
+#define REG6B_GPO18 0x02
+#define REG6B_GPO17 0x01
+
+#define REG6B 0x6b
+
+#define REG6C 0x6c
+#define REG6C_GPIOH 0xff
+#define REG6C_GPIOL 0xff
+
+#define REG6D 0x6d
+
+#define REG6E 0x6e
+
+#define REG87_LEDADD 0x04
+
+enum
+{
+ reg_0x01 = 0,
+ reg_0x02,
+ reg_0x03,
+ reg_0x04,
+ reg_0x05,
+ reg_0x06,
+ reg_0x07,
+ reg_0x08,
+ reg_0x09,
+ reg_0x0a,
+
+ reg_0x10,
+ reg_0x11,
+ reg_0x12,
+ reg_0x13,
+ reg_0x14,
+ reg_0x15,
+ reg_0x16,
+ reg_0x17,
+ reg_0x18,
+ reg_0x19,
+ reg_0x1a,
+ reg_0x1b,
+ reg_0x1c,
+ reg_0x1d,
+ reg_0x1e,
+ reg_0x1f,
+ reg_0x20,
+ reg_0x21,
+ reg_0x22,
+ reg_0x23,
+ reg_0x24,
+ reg_0x25,
+ reg_0x26,
+ reg_0x27,
+
+ reg_0x29,
+
+ reg_0x2c,
+ reg_0x2d,
+ reg_0x2e,
+ reg_0x2f,
+ reg_0x30,
+ reg_0x31,
+ reg_0x32,
+ reg_0x33,
+ reg_0x34,
+ reg_0x35,
+ reg_0x36,
+ reg_0x37,
+ reg_0x38,
+ reg_0x39,
+
+ reg_0x3d,
+ reg_0x3e,
+ reg_0x3f,
+
+ reg_0x52,
+ reg_0x53,
+ reg_0x54,
+ reg_0x55,
+ reg_0x56,
+ reg_0x57,
+ reg_0x58,
+ reg_0x59,
+ reg_0x5a,
+
+ reg_0x5d,
+ reg_0x5e,
+ reg_0x5f,
+ reg_0x60,
+ reg_0x61,
+ reg_0x62,
+ reg_0x63,
+ reg_0x64,
+ reg_0x65,
+ reg_0x66,
+ reg_0x67,
+ reg_0x68,
+ reg_0x69,
+ reg_0x6a,
+ reg_0x6b,
+ reg_0x6c,
+ reg_0x6d,
+ reg_0x6e,
+ reg_0x6f,
+ reg_0x70,
+ reg_0x71,
+ reg_0x72,
+ reg_0x73,
+ reg_0x74,
+ reg_0x75,
+ reg_0x76,
+ reg_0x77,
+ reg_0x78,
+ reg_0x79,
+ reg_0x7a,
+ reg_0x7b,
+ reg_0x7c,
+ reg_0x7d,
+ reg_0x7e,
+ reg_0x7f,
+ reg_0x80,
+ reg_0x81,
+ reg_0x82,
+ reg_0x83,
+ reg_0x84,
+ reg_0x85,
+ reg_0x86,
+ reg_0x87,
+ GENESYS_GL841_MAX_REGS
+};
+/**
+ * prototypes declaration in case of unit testing
+ */
+#ifdef UNIT_TESTING
+SANE_Status
+gl841_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres,
+ float yres,
+ float startx,
+ float starty,
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags);
+
+SANE_Status
+gl841_begin_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ SANE_Bool start_motor);
+
+SANE_Status
+gl841_end_scan (Genesys_Device * dev,
+ Genesys_Register_Set __sane_unused__ * reg,
+ SANE_Bool check_stop);
+
+SANE_Status
+gl841_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
+
+SANE_Status
+sanei_gl841_repark_head (Genesys_Device * dev);
+
+SANE_Status
+gl841_feed (Genesys_Device * dev, int steps);
+
+#endif
diff --git a/backend/genesys_gl843.c b/backend/genesys_gl843.c
new file mode 100644
index 0000000..3648d09
--- /dev/null
+++ b/backend/genesys_gl843.c
@@ -0,0 +1,4459 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_gl843
+
+#include "genesys_gl843.h"
+
+/****************************************************************************
+ Low level function
+ ****************************************************************************/
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+
+/**
+ * Write bulk data (e.g. gamma or shading data) */
+static SANE_Status
+gl843_bulk_write_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBGSTART;
+ DBG (DBG_io, "gl843_bulk_write_data writing %lu bytes\n", (u_long) len);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_bulk_write_data failed while setting register: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if(len>65472)
+ size=65472;
+ else
+ size = len;
+
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_RAM;
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_bulk_write_data failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_usb_write_bulk (dev->dn, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_bulk_write_data failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "gl843_bulk_write_data: wrote %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* Set address for writing data */
+static SANE_Status
+gl843_set_buffer_address (Genesys_Device * dev, uint32_t addr)
+{
+ SANE_Status status;
+
+ DBG (DBG_io, "gl843_set_buffer_address: setting address to 0x%05x\n",
+ addr & 0xffff);
+
+ status = sanei_genesys_write_register (dev, 0x5b, ((addr >> 8) & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_set_buffer_address: failed while writing high byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_genesys_write_register (dev, 0x5c, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_set_buffer_address: failed while writing low byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "gl843_set_buffer_address: completed\n");
+
+ return status;
+}
+
+/**
+ * writes a block of data to RAM
+ * @param dev USB device
+ * @param addr RAM address to write to
+ * @param size size of the chunk of data
+ * @param data pointer to the data to write
+ */
+static SANE_Status
+write_data (Genesys_Device * dev, uint32_t addr, uint32_t size,
+ uint8_t * data)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ status = gl843_set_buffer_address (dev, addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "write_data: failed while setting address for bulk write data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* write actual data */
+ status = gl843_bulk_write_data (dev, 0x28, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "write_data: failed while writing bulk write data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set back address to 0 */
+ status = gl843_set_buffer_address (dev, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "write_data: failed setting to default RAM address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBGCOMPLETED;
+ return status;
+}
+
+
+static SANE_Status
+gl843_bulk_read_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size;
+ uint8_t outdata[8];
+
+ DBGSTART;
+ DBG (DBG_io,
+ "gl843_bulk_read_data: requesting %lu bytes from 0x%02x addr\n",
+ (u_long) len, addr);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, 0, 1, &addr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "write_data: failed to set register address %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ outdata[0] = BULK_IN;
+ outdata[1] = BULK_RAM;
+ outdata[2] = VALUE_BUFFER;
+ outdata[3] = 0;
+ outdata[4] = (len & 0xff);
+ outdata[5] = ((len >> 8) & 0xff);
+ outdata[6] = ((len >> 16) & 0xff);
+ outdata[7] = ((len >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, INDEX, sizeof (outdata), outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_bulk_read_data failed while writing command: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ while (len)
+ {
+ if (len > 0xF000)
+ size = 0xF000;
+ else
+ size = len;
+ if (size >= 512)
+ {
+ size /= 512;
+ size *= 512;
+ }
+
+ DBG (DBG_io2,
+ "gl843_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) size);
+
+ status = sanei_usb_read_bulk (dev->dn, data, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io2,
+ "gl843_bulk_read_data read %lu bytes, %lu remaining\n",
+ (u_long) size, (u_long) (len - size));
+
+ len -= size;
+ data += size;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/****************************************************************************
+ Mid level functions
+ ****************************************************************************/
+
+static SANE_Bool
+gl843_get_fast_feed_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG02);
+ if (r && (r->value & REG02_FASTFED))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl843_get_filter_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_FILTER))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl843_get_lineart_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_LINEART))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl843_get_bitset_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_BITSET))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl843_get_gain4_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG06);
+ if (r && (r->value & REG06_GAIN4))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/**
+ * compute the step multiplier used
+ */
+static int
+gl843_get_step_multiplier (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+ int value = 1;
+
+ r = sanei_genesys_get_address (regs, REG9D);
+ if (r != NULL)
+ {
+ switch (r->value & 0x0c)
+ {
+ case 0x04:
+ value = 2;
+ break;
+ case 0x08:
+ value = 4;
+ break;
+ default:
+ value = 1;
+ }
+ }
+ DBG (DBG_io, "%s: step multiplier is %d\n", __FUNCTION__, value);
+ return value;
+}
+
+static SANE_Bool
+gl843_test_buffer_empty_bit (SANE_Byte val)
+{
+ if (val & REG41_BUFEMPTY)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl843_test_motor_flag_bit (SANE_Byte val)
+{
+ if (val & REG41_MOTORENB)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/** @brief sensor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at a specific dpihw. Use first entry of table by default.
+ * @param sensor_type sensor id
+ * @param dpi hardware dpi for the scan
+ * @param flags to select between XPA, XPA+IR or regular scan from OPTICAL_FLAGS_*
+ * @return a pointer to a Sensor_Profile struct
+ */
+static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi, int flags)
+{
+ unsigned int i, count;
+ int idx;
+ Sensor_Profile *sp;
+
+ if(flags & OPTICAL_FLAG_USE_XPA)
+ {
+ sp=xpa_sensors;
+ count=sizeof(xpa_sensors)/sizeof(Sensor_Profile);
+ }
+ else
+ {
+ sp=sensors;
+ count=sizeof(sensors)/sizeof(Sensor_Profile);
+ }
+ i=0;
+ idx=-1;
+ while(i<count)
+ {
+ /* exact match */
+ if(sp[i].sensor_type==sensor_type && sp[i].dpi==dpi)
+ {
+ return &(sp[i]);
+ }
+
+ /* closest match */
+ if(sp[i].sensor_type==sensor_type)
+ {
+ if(idx<0)
+ {
+ idx=i;
+ }
+ else
+ {
+ if(sp[i].dpi>=dpi
+ && sp[i].dpi<sp[idx].dpi)
+ {
+ idx=i;
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default sensor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(sp[idx]);
+}
+
+
+/** copy sensor specific settings */
+static void
+gl843_setup_sensor (Genesys_Device * dev, Genesys_Register_Set * regs, int dpi,int flags)
+{
+ Genesys_Register_Set *r;
+ Sensor_Profile *sensor;
+ int i,dpihw;
+
+ DBGSTART;
+
+ dpihw=sanei_genesys_compute_dpihw(dev,dpi);
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw, flags);
+
+ for (i = 0x06; i < 0x0e; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ if (r)
+ r->value = sensor->regs_0x10_0x1d[i];
+ }
+ for (i = 0; i < 9; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ if (r)
+ r->value = sensor->regs_0x52_0x5e[i];
+ }
+
+ /* specific registers */
+ r = sanei_genesys_get_address (regs, 0x0c);
+ if (r)
+ {
+ r->value = sensor->reg0c;
+ }
+ r = sanei_genesys_get_address (regs, 0x70);
+ if (r)
+ {
+ r->value = sensor->reg70;
+ }
+ r = sanei_genesys_get_address (regs, 0x71);
+ if (r)
+ {
+ r->value = sensor->reg71;
+ }
+ r = sanei_genesys_get_address (regs, 0x7d);
+ if (r)
+ {
+ if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE))
+ {
+ r->value = 0x90;
+ }
+ }
+ r = sanei_genesys_get_address (regs, 0x9e);
+ if (r)
+ {
+ r->value = sensor->reg9e;
+ }
+ /* undocumented register */
+ r = sanei_genesys_get_address (regs, 0xaa);
+ if (r)
+ {
+ r->value = sensor->regaa;
+ }
+
+ /* CKxMAP */
+ sanei_genesys_set_triple(regs,REG_CK1MAP,sensor->ck1map);
+ sanei_genesys_set_triple(regs,REG_CK3MAP,sensor->ck3map);
+ sanei_genesys_set_triple(regs,REG_CK4MAP,sensor->ck4map);
+
+ DBGCOMPLETED;
+}
+
+
+/* returns the max register bulk size */
+static int
+gl843_bulk_full_size (void)
+{
+ return GENESYS_GL843_MAX_REGS;
+}
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl843_init_registers (Genesys_Device * dev)
+{
+ DBGSTART;
+
+ memset (dev->reg, 0, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* default to KV-SS080 */
+ SETREG (0xa2, 0x0f);
+ SETREG (0x01, 0x00);
+ SETREG (0x02, 0x78);
+ SETREG (0x03, 0x1f);
+ SETREG (0x04, 0x10);
+ SETREG (0x05, 0x80);
+ SETREG (0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */
+ SETREG (0x08, 0x00);
+ SETREG (0x09, 0x00);
+ SETREG (0x0a, 0x00);
+ SETREG (0x0b, 0x6a);
+ SETREG (0x10, 0x00);
+ SETREG (0x11, 0x00);
+ SETREG (0x12, 0x00);
+ SETREG (0x13, 0x00);
+ SETREG (0x14, 0x00);
+ SETREG (0x15, 0x00);
+ SETREG (0x16, 0x33);
+ SETREG (0x17, 0x1c);
+ SETREG (0x18, 0x10);
+ SETREG (0x19, 0x2a);
+ SETREG (0x1a, 0x04);
+ SETREG (0x1b, 0x00);
+ SETREG (0x1c, 0x20);
+ SETREG (0x1d, 0x04);
+ SETREG (0x1e, 0x10);
+ SETREG (0x1f, 0x01);
+ SETREG (0x20, 0x10);
+ SETREG (0x21, 0x04);
+ SETREG (0x22, 0x01);
+ SETREG (0x23, 0x01);
+ SETREG (0x24, 0x04);
+ SETREG (0x25, 0x00);
+ SETREG (0x26, 0x00);
+ SETREG (0x27, 0x00);
+ SETREG (0x2c, 0x02);
+ SETREG (0x2d, 0x58);
+ SETREG (0x2e, 0x80);
+ SETREG (0x2f, 0x80);
+ SETREG (0x30, 0x00);
+ SETREG (0x31, 0x14);
+ SETREG (0x32, 0x27);
+ SETREG (0x33, 0xec);
+ SETREG (0x34, 0x24);
+ SETREG (0x35, 0x00);
+ SETREG (0x36, 0xff);
+ SETREG (0x37, 0xff);
+ SETREG (0x38, 0x55);
+ SETREG (0x39, 0xf0);
+ SETREG (0x3d, 0x00);
+ SETREG (0x3e, 0x00);
+ SETREG (0x3f, 0x01);
+ SETREG (0x52, 0x01);
+ SETREG (0x53, 0x04);
+ SETREG (0x54, 0x07);
+ SETREG (0x55, 0x0a);
+ SETREG (0x56, 0x0d);
+ SETREG (0x57, 0x10);
+ SETREG (0x58, 0x1b);
+ SETREG (0x59, 0x00);
+ SETREG (0x5a, 0x40);
+ SETREG (0x5e, 0x23);
+ SETREG (0x5f, 0x01);
+ SETREG (0x60, 0x00);
+ SETREG (0x61, 0x00);
+ SETREG (0x62, 0x00);
+ SETREG (0x63, 0x00);
+ SETREG (0x64, 0x00);
+ SETREG (0x65, 0x00);
+ SETREG (0x67, 0x7f);
+ SETREG (0x68, 0x7f);
+ SETREG (0x69, 0x01);
+ SETREG (0x6a, 0x04);
+ SETREG (0x6b, 0x30);
+ SETREG (0x70, 0x01);
+ SETREG (0x71, 0x03);
+ SETREG (0x72, 0x04);
+ SETREG (0x73, 0x05);
+
+ /* CKxMAP */
+ SETREG (0x74, 0x00);
+ SETREG (0x75, 0x00);
+ SETREG (0x76, 0x3c);
+ SETREG (0x77, 0x00);
+ SETREG (0x78, 0x00);
+ SETREG (0x79, 0x9f);
+ SETREG (0x7a, 0x00);
+ SETREG (0x7b, 0x00);
+ SETREG (0x7c, 0x55);
+
+ SETREG (0x7d, 0x00);
+ SETREG (0x7f, 0x00);
+ SETREG (0x80, 0x00);
+ if (strcmp (dev->model->name, "canon-canoscan-4400f") != 0)
+ {
+ SETREG (0x81, 0x00);
+ SETREG (0x82, 0x00);
+ SETREG (0x83, 0x00);
+ SETREG (0x84, 0x00);
+ SETREG (0x85, 0x00);
+ SETREG (0x86, 0x00);
+ }
+ SETREG (0x87, 0x00);
+ SETREG (0x9d, 0x04);
+ SETREG (0x9e, 0x00);
+ if (strcmp (dev->model->name, "canon-canoscan-8400f") != 0)
+ {
+ SETREG (0x0c, 0x00);
+ SETREG (0x94, 0xff);
+ SETREG (0xab, 0x50);
+ }
+
+ /* so many time burnt for this register ....*/
+ if (strcmp (dev->model->name, "canon-canoscan-4400f") != 0
+ &&strcmp (dev->model->name, "canon-canoscan-8400f") != 0)
+ {
+ SETREG (0xaa, 0x00);
+ }
+
+ /* G4050 values */
+ if ((strcmp (dev->model->name, "hewlett-packard-scanjet-g4050") == 0)
+ || (strcmp (dev->model->name, "hewlett-packard-scanjet-4850c") == 0)
+ || (strcmp (dev->model->name, "hewlett-packard-scanjet-g4010") == 0))
+ {
+ SETREG (0x03, 0x1d);
+ SETREG (0x05, 0x08);
+ SETREG (0x06, 0xd0); /* SCANMOD=110, PWRBIT and no GAIN4 */
+ SETREG (0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */
+ SETREG (0x0a, 0x18);
+ SETREG (0x0b, 0x69);
+
+ /* CIS exposure is used for XPA lamp movement */
+ SETREG (0x10, 0x2c);
+ SETREG (0x11, 0x09);
+ SETREG (0x12, 0x22);
+ SETREG (0x13, 0xb8);
+ SETREG (0x14, 0x10);
+ SETREG (0x15, 0xf0);
+
+ SETREG (0x6b, 0xf4);
+
+ SETREG (0x70, 0x00);
+ SETREG (0x71, 0x02);
+ SETREG (0x72, 0x00);
+ SETREG (0x73, 0x00);
+
+ SETREG (0x80, 0x50);
+ SETREG (0x9d, 0x08);
+ SETREG (0xab, 0x40);
+
+ /* XXX STEF XXX TODO move to set for scan */
+ SETREG (0x98, 0x03);
+ SETREG (0x99, 0x30);
+ SETREG (0x9a, 0x01);
+ SETREG (0x9b, 0x80);
+ SETREG (0xac, 0x00);
+ }
+
+ if (strcmp (dev->model->name, "canon-canoscan-4400f") == 0)
+ {
+ SETREG (0x06, 0xf0); /* SCANMOD=111, PWRBIT and no GAIN4 */
+ SETREG (0x0b, 0x69); /* 16M only */
+ SETREG (0x1e, 0x20);
+ SETREG (0x22, 0xc8);
+ SETREG (0x23, 0xc8);
+ SETREG (0x5e, 0x3f);
+ SETREG (0x5f, 0xf0);
+ SETREG (0x6b, 0x72);
+ SETREG (0x72, 0x01);
+ SETREG (0x73, 0x03);
+ SETREG (0x80, 0x0c);
+ SETREG (0x87, 0x02); /* MCLOCK -> CK4MAP */
+ SETREG (0x9d, 0x08); /* STEPTIM=2 */
+ SETREG (0xa2, 0x1f);
+ SETREG (0xab, 0x00);
+ sanei_genesys_set_double(dev->reg,REG_EXPR,0x9c40);
+ sanei_genesys_set_double(dev->reg,REG_EXPG,0x9c40);
+ sanei_genesys_set_double(dev->reg,REG_EXPB,0x9c40);
+ }
+
+ if (strcmp (dev->model->name, "canon-canoscan-8400f") == 0)
+ {
+ SETREG (0x03, 0x1c);
+ SETREG (0x06, 0xd0); /* SCANMOD=110, PWRBIT and no GAIN4 */
+ SETREG (0x0a, 0x10);
+ SETREG (0x22, 0x50);
+ SETREG (0x23, 0x50);
+ SETREG (0x5e, 0x85);
+ SETREG (0x6b, 0xb1);
+ SETREG (0x1e, 0xa0);
+ SETREG (0x72, 0x03);
+ SETREG (0x73, 0x04);
+ SETREG (0x7d, 0x20);
+ SETREG (0x80, 0x28);
+ SETREG (0x87, 0x02); /* MCLOCK -> CK4MAP */
+ SETREG (0x9d, 0x08); /* STEPTIM=2 */
+ }
+
+ /* fine tune upon device description */
+ dev->reg[reg_0x05].value &= ~REG05_DPIHW;
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* initalize calibration reg */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ DBGCOMPLETED;
+}
+
+/* Send slope table for motor movement
+ slope_table in machine byte order
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl843_send_slope_table (Genesys_Device * dev, int table_nr,
+ uint16_t * slope_table, int steps)
+{
+ SANE_Status status;
+ uint8_t *table;
+ int i;
+ char msg[10000];
+
+ DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __FUNCTION__,
+ table_nr, steps);
+
+ table = (uint8_t *) malloc (steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sprintf (msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++)
+ {
+ sprintf (msg+strlen(msg), "%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __FUNCTION__, msg);
+ }
+
+
+ /* slope table addresses are fixed : 0x4000, 0x4800, 0x5000, 0x5800, 0x6000 */
+ /* XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); */
+ status = write_data (dev, 0x4000 + 0x800 * table_nr, steps * 2, table);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: write data failed writing slope table %d (%s)\n",
+ __FUNCTION__, table_nr, sane_strstatus (status));
+ }
+
+ free (table);
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/* Set values of analog frontend */
+static SANE_Status
+gl843_set_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status;
+ uint8_t val;
+ int i;
+
+ DBG (DBG_proc, "gl843_set_fe (%s)\n",
+ set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
+ AFE_POWER_SAVE ? "powersave" : "huh?");
+
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl843_set_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+ sanei_genesys_init_fe (dev);
+ }
+
+ /* check analog frontend type */
+ RIE (sanei_genesys_read_register (dev, REG04, &val));
+ if ((val & REG04_FESET) != 0x00)
+ {
+ /* for now there is no support for AD fe */
+ DBG (DBG_proc, "gl843_set_fe(): unsupported frontend type %d\n",
+ dev->reg[reg_0x04].value & REG04_FESET);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "gl843_set_fe(): frontend reset complete\n");
+
+ for (i = 1; i <= 3; i++)
+ {
+ status = sanei_genesys_fe_write_data (dev, i, dev->frontend.reg[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_set_fe: writing reg[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x20 + i, dev->frontend.offset[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_set_fe: writing offset[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (dev->model->ccd_type == CCD_KVSS080)
+ {
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x24 + i,
+ dev->frontend.sign[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_set_fe: writing sign[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ status =
+ sanei_genesys_fe_write_data (dev, 0x28 + i, dev->frontend.gain[i]);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_set_fe: writing gain[%d] failed: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+gl843_init_motor_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int exposure,
+ float scan_yres,
+ int scan_step_type,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ int scan_power_mode,
+ unsigned int flags)
+{
+ SANE_Status status;
+ int use_fast_fed, coeff;
+ unsigned int lincnt;
+ uint16_t scan_table[1024];
+ uint16_t fast_table[1024];
+ int scan_steps,fast_steps, fast_step_type;
+ unsigned int feedl,factor,dist;
+ Genesys_Register_Set *r;
+ uint32_t z1, z2;
+
+ DBGSTART;
+ DBG (DBG_info, "gl843_init_motor_regs_scan : exposure=%d, "
+ "scan_yres=%g, scan_step_type=%d, scan_lines=%d, scan_dummy=%d, "
+ "feed_steps=%d, scan_power_mode=%d, flags=%x\n",
+ exposure, scan_yres, scan_step_type,
+ scan_lines, scan_dummy, feed_steps, scan_power_mode, flags);
+
+ /* get step multiplier */
+ factor = gl843_get_step_multiplier (reg);
+
+ use_fast_fed = 0;
+
+ if((scan_yres>=300 && feed_steps>900) || (flags & MOTOR_FLAG_FEED))
+ use_fast_fed=1;
+
+ lincnt=scan_lines;
+ sanei_genesys_set_triple(reg,REG_LINCNT,lincnt);
+ DBG (DBG_io, "%s: lincnt=%d\n", __FUNCTION__, lincnt);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address (reg, REG02);
+ r->value = 0x00;
+ r->value |= REG02_MTRPWR;
+
+ if (use_fast_fed)
+ r->value |= REG02_FASTFED;
+ else
+ r->value &= ~REG02_FASTFED;
+
+ /* in case of automatic go home, move until home sensor */
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME)
+ r->value |= REG02_AGOHOME | REG02_NOTHOME;
+
+ /* disable backtracking */
+ if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ ||(scan_yres>=2400)
+ ||(scan_yres>=dev->sensor.optical_res))
+ r->value |= REG02_ACDCDIS;
+
+ /* scan and backtracking slope table */
+ sanei_genesys_slope_table(scan_table,
+ &scan_steps,
+ scan_yres,
+ exposure,
+ dev->motor.base_ydpi,
+ scan_step_type,
+ factor,
+ dev->model->motor_type,
+ gl843_motors);
+ RIE(gl843_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps*factor));
+ RIE(gl843_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps*factor));
+
+ /* STEPNO */
+ r = sanei_genesys_get_address (reg, REG_STEPNO);
+ r->value = scan_steps;
+
+ /* FSHDEC */
+ r = sanei_genesys_get_address (reg, REG_FSHDEC);
+ r->value = scan_steps;
+
+ /* fast table */
+ fast_step_type=0;
+ if(scan_step_type<=fast_step_type)
+ {
+ fast_step_type=scan_step_type;
+ }
+ sanei_genesys_slope_table(fast_table,
+ &fast_steps,
+ sanei_genesys_get_lowest_ydpi(dev),
+ exposure,
+ dev->motor.base_ydpi,
+ fast_step_type,
+ factor,
+ dev->model->motor_type,
+ gl843_motors);
+ RIE(gl843_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps*factor));
+ RIE(gl843_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps*factor));
+ RIE(gl843_send_slope_table (dev, HOME_TABLE, fast_table, fast_steps*factor));
+
+ /* FASTNO */
+ r = sanei_genesys_get_address (reg, REG_FASTNO);
+ r->value = fast_steps;
+
+ /* FMOVNO */
+ r = sanei_genesys_get_address (reg, REG_FMOVNO);
+ r->value = fast_steps;
+
+ /* substract acceleration distance from feedl */
+ feedl=feed_steps;
+ feedl<<=scan_step_type;
+
+ dist = scan_steps;
+ if (use_fast_fed)
+ {
+ dist += fast_steps*2;
+ }
+ DBG (DBG_io2, "%s: acceleration distance=%d\n", __FUNCTION__, dist);
+
+ /* get sure when don't insane value : XXX STEF XXX in this case we should
+ * fall back to single table move */
+ if(dist<feedl)
+ feedl -= dist;
+ else
+ feedl = 1;
+
+ sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
+ DBG (DBG_io, "%s: feedl=%d\n", __FUNCTION__, feedl);
+
+ /* doesn't seem to matter that much */
+ sanei_genesys_calculate_zmode2 (use_fast_fed,
+ exposure,
+ scan_table,
+ scan_steps,
+ feedl,
+ scan_steps,
+ &z1,
+ &z2);
+ if(scan_yres>600)
+ {
+ z1=0;
+ z2=0;
+ }
+
+ sanei_genesys_set_triple(reg,REG_Z1MOD,z1);
+ DBG (DBG_info, "gl843_init_motor_regs_scan: z1 = %d\n", z1);
+
+ sanei_genesys_set_triple(reg,REG_Z2MOD,z2);
+ DBG (DBG_info, "gl843_init_motor_regs_scan: z2 = %d\n", z2);
+
+ r = sanei_genesys_get_address (reg, REG1E);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ r = sanei_genesys_get_address (reg, REG67);
+ r->value = 0x3f | (scan_step_type << REG67S_STEPSEL);
+
+ r = sanei_genesys_get_address (reg, REG68);
+ r->value = 0x3f | (scan_step_type << REG68S_FSTPSEL);
+
+ /* steps for STOP table */
+ r = sanei_genesys_get_address (reg, REG_FMOVDEC);
+ r->value = fast_steps;
+
+ /* Vref XXX STEF XXX : optical divider or step type ? */
+ r = sanei_genesys_get_address (reg, 0x80);
+ if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE))
+ {
+ r->value = 0x50;
+ coeff=dev->sensor.optical_res/sanei_genesys_compute_dpihw(dev, scan_yres);
+ if (dev->model->motor_type == MOTOR_KVSS080)
+ {
+ if(coeff>=1)
+ {
+ r->value |= 0x05;
+ }
+ }
+ else {
+ switch(coeff)
+ {
+ case 4:
+ r->value |= 0x0a;
+ break;
+ case 2:
+ r->value |= 0x0f;
+ break;
+ case 1:
+ r->value |= 0x0f;
+ break;
+ }
+ }
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/**@brief compute exposure to use
+ * compute the sensor exposure based on target resolution
+ */
+static int gl843_compute_exposure(Genesys_Device *dev, int xres, int flags)
+{
+ Sensor_Profile *sensor;
+
+ sensor=get_sensor_profile(dev->model->ccd_type, xres, flags);
+ return sensor->exposure;
+}
+
+/** @brief setup optical related registers
+ * start and pixels are expressed in optical sensor resolution coordinate
+ * space.
+ * @param dev device to use
+ * @param reg registers to set up
+ * @param exposure exposure time to use
+ * @param used_res scanning resolution used, may differ from
+ * scan's one
+ * @param start logical start pixel coordinate
+ * @param pixels logical number of pixels to use
+ * @param channels number of color channles used (1 or 3)
+ * @param depth bit depth of the scan (1, 8 or 16 bits)
+ * @param half_ccd SANE_TRUE if timings are such that x coordiantes must be halved
+ * @param color_filter to choose the color channel used in gray scans
+ * @param flags to drive specific settings such no calibration, XPA use ...
+ * @return SANE_STATUS_GOOD if OK
+ */
+static SANE_Status
+gl843_init_optical_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int exposure,
+ int used_res,
+ unsigned int start,
+ unsigned int pixels,
+ int channels,
+ int depth,
+ SANE_Bool half_ccd,
+ int color_filter,
+ int flags)
+{
+ unsigned int words_per_line;
+ unsigned int startx, endx, used_pixels;
+ unsigned int dpiset, dpihw, factor;
+ unsigned int bytes;
+ unsigned int tgtime; /**> exposure time multiplier */
+ unsigned int cksel; /**> clock per system pixel time in capturing image */
+ Genesys_Register_Set *r;
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl843_init_optical_regs_scan : exposure=%d, "
+ "used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
+ "half_ccd=%d, flags=%x\n",
+ exposure,
+ used_res, start, pixels, channels, depth, half_ccd, flags);
+
+ /* tgtime */
+ tgtime=1;
+ if (dev->model->ccd_type == CCD_G4050 && used_res>2400)
+ {
+ tgtime=2;
+ }
+ DBG (DBG_io2, "%s: tgtime=%d\n", __FUNCTION__, tgtime);
+
+ /* to manage high resolution device while keeping good
+ * low resolution scanning speed, we make hardware dpi vary */
+ dpihw=sanei_genesys_compute_dpihw(dev, used_res);
+ factor=dev->sensor.optical_res/dpihw;
+ DBG (DBG_io2, "%s: dpihw=%d (factor=%d)\n", __FUNCTION__, dpihw, factor);
+
+ /* sensor parameters */
+ gl843_setup_sensor (dev, reg, dpihw, flags);
+
+ /* resolution is divided according to CKSEL which is known once sensor is set up */
+ r = sanei_genesys_get_address (reg, REG18);
+ cksel= (r->value & REG18_CKSEL)+1;
+ DBG (DBG_io2, "%s: cksel=%d\n", __FUNCTION__, cksel);
+ dpiset = used_res * cksel;
+
+ /* start and end coordinate in optical dpi coordinates */
+ startx = (start + dev->sensor.dummy_pixel * tgtime)/cksel;
+
+ used_pixels=pixels/cksel;
+ endx = startx + used_pixels;
+
+ /* pixel coordinate factor correction when used dpihw is not maximal one */
+ startx/=factor;
+ endx/=factor;
+ used_pixels=endx-startx;
+
+ /* in case of stagger we have to start at an odd coordinate */
+ if ((flags & OPTICAL_FLAG_STAGGER)
+ &&((startx & 1)==0))
+ {
+ startx++;
+ endx++;
+ }
+
+ status = gl843_set_fe (dev, AFE_SET);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to set frontend: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* enable shading */
+ r = sanei_genesys_get_address (reg, REG01);
+ r->value &= ~REG01_SCAN;
+ if ((flags & OPTICAL_FLAG_DISABLE_SHADING) || (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG01_DVDSET;
+ }
+ else
+ {
+ r->value |= REG01_DVDSET;
+ }
+ if(dpihw>600)
+ {
+ r->value |= REG01_SHDAREA;
+ }
+ else
+ {
+ r->value &= ~REG01_SHDAREA;
+ }
+
+ r = sanei_genesys_get_address (reg, REG03);
+ r->value &= ~REG03_AVEENB;
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value &= ~REG03_LAMPPWR;
+ else
+ r->value |= REG03_LAMPPWR;
+
+ /* select XPA */
+ r->value &= ~REG03_XPASEL;
+ if (flags & OPTICAL_FLAG_USE_XPA)
+ {
+ r->value |= REG03_XPASEL;
+ }
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, REG2E);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, REG2F);
+ r->value = dev->settings.threshold;
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, REG04);
+ switch (depth)
+ {
+ case 1:
+ r->value &= ~REG04_BITSET;
+ r->value |= REG04_LINEART;
+ break;
+ case 8:
+ r->value &= ~(REG04_LINEART | REG04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG04_LINEART;
+ r->value |= REG04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG04_FILTER | REG04_AFEMOD);
+ if (channels == 1)
+ {
+ switch (color_filter)
+ {
+ case 0:
+ r->value |= 0x14; /* red filter */
+ break;
+ case 2:
+ r->value |= 0x1c; /* blue filter */
+ break;
+ default:
+ r->value |= 0x18; /* green filter */
+ break;
+ }
+ }
+ else
+ r->value |= 0x10; /* mono */
+
+ /* register 05 */
+ r = sanei_genesys_get_address (reg, REG05);
+
+ /* set up dpihw */
+ r->value &= ~REG05_DPIHW;
+ switch(dpihw)
+ {
+ case 600:
+ r->value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ r->value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ r->value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ r->value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* enable gamma tables */
+ if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
+ r->value &= ~REG05_GMMENB;
+ else
+ r->value |= REG05_GMMENB;
+
+ if(half_ccd)
+ {
+ sanei_genesys_set_double(reg,REG_DPISET,dpiset*4);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __FUNCTION__, dpiset*4);
+ }
+ else
+ {
+ sanei_genesys_set_double(reg,REG_DPISET,dpiset);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __FUNCTION__, dpiset);
+ }
+
+ sanei_genesys_set_double(reg,REG_STRPIXEL,startx/tgtime);
+ sanei_genesys_set_double(reg,REG_ENDPIXEL,endx/tgtime);
+
+ /* words(16bit) before gamma, conversion to 8 bit or lineart */
+ words_per_line = (used_pixels * dpiset) / dpihw;
+ bytes = depth / 8;
+ if (depth == 1)
+ {
+ words_per_line = (words_per_line >> 3) + ((words_per_line & 7) ? 1 : 0);
+ }
+ else
+ {
+ words_per_line *= bytes;
+ }
+
+ dev->wpl = words_per_line;
+ dev->bpl = words_per_line;
+
+ DBG (DBG_io2, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+ DBG (DBG_io2, "%s: pixels =%d\n", __FUNCTION__, pixels);
+ DBG (DBG_io2, "%s: depth =%d\n", __FUNCTION__, depth);
+ DBG (DBG_io2, "%s: dev->bpl =%lu\n", __FUNCTION__, (unsigned long) dev->bpl);
+ DBG (DBG_io2, "%s: dev->len =%lu\n", __FUNCTION__, (unsigned long)dev->len);
+ DBG (DBG_io2, "%s: dev->dist =%lu\n", __FUNCTION__, (unsigned long)dev->dist);
+
+ words_per_line *= channels;
+
+ /* MAXWD is expressed in 2 words unit */
+ /* nousedspace = (mem_bank_range * 1024 / 256 -1 ) * 4; */
+ sanei_genesys_set_triple(reg,REG_MAXWD,(words_per_line)>>1);
+ DBG (DBG_io2, "%s: words_per_line used=%d\n", __FUNCTION__, words_per_line);
+
+ sanei_genesys_set_double(reg,REG_LPERIOD,exposure/tgtime);
+ DBG (DBG_io2, "%s: exposure used=%d\n", __FUNCTION__, exposure/tgtime);
+
+ r = sanei_genesys_get_address (reg, REG_DUMMY);
+ r->value = dev->sensor.dummy_pixel * tgtime;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl843_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int scan_mode,
+ int color_filter,
+ unsigned int flags)
+{
+ int used_res;
+ int start, used_pixels;
+ int bytes_per_line;
+ int move;
+ unsigned int lincnt; /**> line count to scan */
+ unsigned int oflags, mflags; /**> optical and motor flags */
+ int exposure;
+ int stagger;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+ int scan_step_type = 1;
+ int scan_power_mode = 0;
+ int max_shift;
+ size_t requested_buffer_size, read_buffer_size;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl843_init_scan_regs settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g/%g\n"
+ "Depth/Channels: %u/%u\n"
+ "Flags : %x\n\n",
+ xres, yres, lines, pixels, startx, starty, depth, channels, flags);
+
+
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if (dev->sensor.optical_res < 4 * xres ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ half_ccd = SANE_FALSE;
+ }
+ else
+ {
+ half_ccd = SANE_TRUE;
+ }
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 4;
+
+ /* stagger starting at 2400, and not applied for calibration */
+ stagger = 0;
+ if ( (yres>1200)
+ && ((flags & SCAN_FLAG_IGNORE_LINE_DISTANCE)==0)
+ && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ {
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ }
+ DBG (DBG_info, "%s : stagger=%d lines\n", __FUNCTION__, stagger);
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ oflags = 0;
+ if (flags & SCAN_FLAG_DISABLE_SHADING)
+ oflags |= OPTICAL_FLAG_DISABLE_SHADING;
+ if (flags & SCAN_FLAG_DISABLE_GAMMA)
+ oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
+ if (flags & SCAN_FLAG_DISABLE_LAMP)
+ oflags |= OPTICAL_FLAG_DISABLE_LAMP;
+ if (flags & SCAN_FLAG_CALIBRATION)
+ oflags |= OPTICAL_FLAG_DISABLE_DOUBLE;
+ if(stagger)
+ oflags |= OPTICAL_FLAG_STAGGER;
+ if (flags & SCAN_FLAG_USE_XPA)
+ oflags |= OPTICAL_FLAG_USE_XPA;
+
+ /** @brief compute used resolution */
+ if (flags & SCAN_FLAG_USE_OPTICAL_RES)
+ {
+ used_res = optical_res;
+ }
+ else
+ {
+ /* resolution is choosen from a fixed list and can be used directly
+ * unless we have ydpi higher than sensor's maximum one */
+ if(xres>optical_res)
+ used_res=optical_res;
+ else
+ used_res = xres;
+ }
+
+ /* compute scan parameters values */
+ /* pixels are allways given at full optical resolution */
+ /* use detected left margin and fixed value */
+ /* start */
+ start = startx;
+
+ /* compute correct pixels number */
+ used_pixels = (pixels * optical_res) / xres;
+ DBG (DBG_info, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+
+ /* round up pixels number if needed */
+ if (used_pixels * xres < pixels * optical_res)
+ used_pixels++;
+
+ /* we want even number of pixels here */
+ if(used_pixels & 1)
+ used_pixels++;
+
+ dummy = 0;
+ /* dummy = 1; XXX STEF XXX */
+
+ /* slope_dpi */
+ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres * channels;
+ else
+ slope_dpi = yres;
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ /* scan_step_type */
+ if(flags & SCAN_FLAG_FEEDING)
+ {
+ exposure=gl843_compute_exposure (dev, sanei_genesys_get_lowest_ydpi(dev), oflags);
+ scan_step_type=sanei_genesys_compute_step_type (gl843_motors, dev->model->motor_type, exposure);
+ }
+ else
+ {
+ exposure = gl843_compute_exposure (dev, used_res, oflags);
+ scan_step_type = sanei_genesys_compute_step_type(gl843_motors, dev->model->motor_type, exposure);
+ }
+
+ DBG (DBG_info, "%s : exposure=%d pixels\n", __FUNCTION__, exposure);
+ DBG (DBG_info, "%s : scan_step_type=%d\n", __FUNCTION__, scan_step_type);
+
+ /*** optical parameters ***/
+ /* in case of dynamic lineart, we use an internal 8 bit gray scan
+ * to generate 1 lineart data */
+ if ((flags & SCAN_FLAG_DYNAMIC_LINEART) && (scan_mode == SCAN_MODE_LINEART))
+ {
+ depth = 8;
+ }
+ /* no 16 bit gamma for this ASIC */
+ if (depth == 16)
+ {
+ flags |= SCAN_FLAG_DISABLE_GAMMA;
+ oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
+ }
+
+ /* now _LOGICAL_ optical values used are known, setup registers */
+ status = gl843_init_optical_regs_scan (dev,
+ reg,
+ exposure,
+ used_res,
+ start,
+ used_pixels,
+ channels,
+ depth,
+ half_ccd,
+ color_filter,
+ oflags);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /*** motor parameters ***/
+
+ /* it seems base_dpi of the G4050 motor is changed above 600 dpi*/
+ if (dev->model->motor_type == MOTOR_G4050 && yres>600)
+ {
+ dev->ld_shift_r = (dev->model->ld_shift_r*3800)/dev->motor.base_ydpi;
+ dev->ld_shift_g = (dev->model->ld_shift_g*3800)/dev->motor.base_ydpi;
+ dev->ld_shift_b = (dev->model->ld_shift_b*3800)/dev->motor.base_ydpi;
+ }
+ else
+ {
+ dev->ld_shift_r = dev->model->ld_shift_r;
+ dev->ld_shift_g = dev->model->ld_shift_g;
+ dev->ld_shift_b = dev->model->ld_shift_b;
+ }
+
+ /* max_shift */
+ /* scanned area must be enlarged by max color shift needed */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,flags);
+
+ /* lines to scan */
+ lincnt = lines + max_shift + stagger;
+
+ /* add tl_y to base movement */
+ move = starty;
+ DBG (DBG_info, "gl843_init_scan_regs: move=%d steps\n", move);
+
+
+ mflags=0;
+ if(flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ mflags|=MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
+ if(flags & SCAN_FLAG_FEEDING)
+ mflags|=MOTOR_FLAG_FEED;
+ if (flags & SCAN_FLAG_USE_XPA)
+ mflags |= MOTOR_FLAG_USE_XPA;
+
+ status = gl843_init_motor_regs_scan (dev,
+ reg,
+ exposure,
+ slope_dpi,
+ scan_step_type,
+ dev->model->is_cis ? lincnt * channels : lincnt,
+ dummy,
+ move,
+ scan_power_mode,
+ mflags);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /*** prepares data reordering ***/
+
+ /* words_per_line */
+ bytes_per_line = (used_pixels * used_res) / optical_res;
+ bytes_per_line = (bytes_per_line * channels * depth) / 8;
+
+ /* since we don't have sheetfed scanners to handle,
+ * use huge read buffer */
+ /* TODO find the best size according to settings */
+ requested_buffer_size = 16 * bytes_per_line;
+
+ read_buffer_size =
+ 2 * requested_buffer_size +
+ ((max_shift + stagger) * used_pixels * channels * depth) / 8;
+
+ RIE (sanei_genesys_buffer_free (&(dev->read_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->read_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->lines_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->lines_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->shrink_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->shrink_buffer),
+ requested_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->out_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->out_buffer),
+ (8 * dev->settings.pixels * channels *
+ depth) / 8));
+
+
+ dev->read_bytes_left = bytes_per_line * lincnt;
+
+ DBG (DBG_info,
+ "gl843_init_scan_regs: physical bytes to read = %lu\n",
+ (u_long) dev->read_bytes_left);
+ dev->read_active = SANE_TRUE;
+
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ DBG (DBG_info, "%s: current_setup.pixels=%d\n", __FUNCTION__, dev->current_setup.pixels);
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ dev->total_bytes_read = 0;
+ if (depth == 1)
+ dev->total_bytes_to_read =
+ ((dev->settings.pixels * dev->settings.lines) / 8 +
+ (((dev->settings.pixels * dev->settings.lines) % 8) ? 1 : 0)) *
+ channels;
+ else
+ dev->total_bytes_to_read =
+ dev->settings.pixels * dev->settings.lines * channels * (depth / 8);
+
+ DBG (DBG_info, "gl843_init_scan_regs: total bytes to send = %lu\n",
+ (u_long) dev->total_bytes_to_read);
+
+ DBG (DBG_proc, "gl843_init_scan_regs: completed\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl843_calculate_current_setup (Genesys_Device * dev)
+{
+ int channels;
+ int depth;
+ int start;
+
+ float xres; /*dpi */
+ float yres; /*dpi */
+ float startx; /*optical_res, from dummy_pixel+1 */
+ float pixels;
+ float lines;
+
+ int used_res;
+ int used_pixels;
+ unsigned int lincnt;
+ int exposure;
+ int stagger;
+
+ int max_shift;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+ int oflags;
+
+ DBG (DBG_info,
+ "gl843_calculate_current_setup settings:\n"
+ "Resolution: %ux%uDPI\n"
+ "Lines : %u\n"
+ "PPL : %u\n"
+ "Startpos : %.3f/%.3f\n"
+ "Scan mode : %d\n\n",
+ dev->settings.xres,
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ /* channels */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART)
+ depth = 1;
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+ /* optical flags */
+ oflags=0;
+ if(dev->settings.scan_method==SCAN_METHOD_TRANSPARENCY)
+ {
+ oflags=OPTICAL_FLAG_USE_XPA;
+ }
+
+ xres = dev->settings.xres;
+ yres = dev->settings.yres;
+ startx = start;
+ pixels = dev->settings.pixels;
+ lines = dev->settings.lines;
+
+ DBG (DBG_info,
+ "gl843_calculate_current_setup settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g\n"
+ "Depth/Channels: %u/%u\n\n",
+ xres, yres, lines, pixels, startx, depth, channels);
+
+/* half_ccd */
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if ((dev->sensor.optical_res < 4 * xres) ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ half_ccd = SANE_FALSE;
+ }
+ else
+ {
+ half_ccd = SANE_TRUE;
+ }
+
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 4;
+
+ /* stagger */
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "%s: stagger=%d lines\n", __FUNCTION__, stagger);
+
+ if(xres<=optical_res)
+ used_res = xres;
+ else
+ used_res=optical_res;
+
+ /* compute scan parameters values */
+ /* pixels are allways given at half or full CCD optical resolution */
+ /* use detected left margin and fixed value */
+
+ /* compute correct pixels number */
+ used_pixels = (pixels * optical_res) / xres;
+ DBG (DBG_info, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+
+ /* exposure */
+ exposure = gl843_compute_exposure (dev, used_res, oflags);
+ DBG (DBG_info, "%s : exposure=%d pixels\n", __FUNCTION__, exposure);
+
+ /* it seems base_dpi of the G4050 motor is changed above 600 dpi*/
+ if (dev->model->motor_type == MOTOR_G4050 && yres>600)
+ {
+ dev->ld_shift_r = (dev->model->ld_shift_r*3800)/dev->motor.base_ydpi;
+ dev->ld_shift_g = (dev->model->ld_shift_g*3800)/dev->motor.base_ydpi;
+ dev->ld_shift_b = (dev->model->ld_shift_b*3800)/dev->motor.base_ydpi;
+ }
+ else
+ {
+ dev->ld_shift_r = dev->model->ld_shift_r;
+ dev->ld_shift_g = dev->model->ld_shift_g;
+ dev->ld_shift_b = dev->model->ld_shift_b;
+ }
+
+ /* scanned area must be enlarged by max color shift needed */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,0);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ DBG (DBG_info, "%s: current_setup.pixels=%d\n", __FUNCTION__, dev->current_setup.pixels);
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ DBG (DBG_proc, "gl843_calculate_current_setup: completed\n");
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gl843_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
+{
+
+ DBG (DBG_proc, "gl843_set_motor_power\n");
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ | REG02_MTRPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ & ~REG02_MTRPWR);
+ }
+}
+
+static void
+gl843_set_lamp_power (Genesys_Device * dev,
+ Genesys_Register_Set * regs, SANE_Bool set)
+{
+ Genesys_Register_Set *r;
+ int i;
+ uint8_t val;
+
+ val = sanei_genesys_read_reg_from_set (regs, REG03);
+ if (set)
+ {
+ val |= REG03_LAMPPWR;
+ sanei_genesys_set_reg_from_set (regs, REG03, val);
+ for (i = 0; i < 6; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+ }
+ else
+ {
+ val &= ~REG03_LAMPPWR;
+ sanei_genesys_set_reg_from_set (regs, REG03, val);
+ for (i = 0; i < 6; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ r->value = 0x00;
+ }
+ }
+}
+
+/**
+ * for fast power saving methods only, like disabling certain amplifiers
+ * @param dev device to use
+ * @param enable true to set inot powersaving
+ * */
+static SANE_Status
+gl843_save_power (Genesys_Device * dev, SANE_Bool enable)
+{
+ uint8_t val;
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl843_save_power: enable = %d\n", enable);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ /* switch KV-SS080 lamp off */
+ if (dev->model->gpo_type == GPO_KVSS080)
+ {
+ RIE(sanei_genesys_read_register (dev, REG6C, &val));
+ if(enable)
+ val &= 0xef;
+ else
+ val |= 0x10;
+ RIE(sanei_genesys_write_register(dev,REG6C,val));
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl843_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "gl843_set_powersaving (delay = %d)\n", delay);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ DBGCOMPLETED;
+ return status;
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl843_start_action (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x01);
+}
+
+static SANE_Status
+gl843_stop_action (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val40, val;
+ unsigned int loop;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ val40 = 0;
+ status = sanei_genesys_read_register (dev, REG40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBG (DBG_proc, "%s: completed\n", __FUNCTION__);
+ return status;
+ }
+
+ /* only stop action if needed */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
+ {
+ DBG (DBG_info, "%s: already stopped\n", __FUNCTION__);
+ DBG (DBG_proc, "%s: completed\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* ends scan 646 */
+ val = sanei_genesys_read_reg_from_set (dev->reg, REG01);
+ val &= ~REG01_SCAN;
+ sanei_genesys_set_reg_from_set (dev->reg, REG01, val);
+ status = sanei_genesys_write_register (dev, REG01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to write register 01: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (100 * 1000);
+
+ loop = 10;
+ while (loop > 0)
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ val40 = 0;
+ status = sanei_genesys_read_register (dev, 0x40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* if scanner is in command mode, we are done */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG)
+ && !(val & REG41_MOTORENB))
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ usleep (100 * 1000);
+ loop--;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+gl843_get_paper_sensor (Genesys_Device * dev, SANE_Bool * paper_loaded)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ status = sanei_genesys_read_register (dev, REG6D, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_get_paper_sensor: failed to read gpio: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ *paper_loaded = (val & 0x1) == 0;
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+gl843_eject_document (Genesys_Device * dev)
+{
+ DBG (DBG_proc, "%s: not implemented \n", __FUNCTION__);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+gl843_load_document (Genesys_Device * dev)
+{
+ DBG (DBG_proc, "%s: not implemented \n", __FUNCTION__);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * detects end of document and adjust current scan
+ * to take it into account
+ * used by sheetfed scanners
+ */
+static SANE_Status
+gl843_detect_document_end (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool paper_loaded;
+ unsigned int scancnt = 0;
+ int flines, channels, depth, bytes_remain, sublines,
+ bytes_to_flush, lines, sub_bytes, tmp, read_bytes_left;
+ DBG (DBG_proc, "%s: begin\n", __FUNCTION__);
+
+ RIE (gl843_get_paper_sensor (dev, &paper_loaded));
+
+ /* sheetfed scanner uses home sensor as paper present */
+ if ((dev->document == SANE_TRUE) && !paper_loaded)
+ {
+ DBG (DBG_info, "%s: no more document\n", __FUNCTION__);
+ dev->document = SANE_FALSE;
+
+ channels = dev->current_setup.channels;
+ depth = dev->current_setup.depth;
+ read_bytes_left = (int) dev->read_bytes_left;
+ DBG (DBG_io, "gl843_detect_document_end: read_bytes_left=%d\n",
+ read_bytes_left);
+
+ /* get lines read */
+ status = sanei_genesys_read_scancnt (dev, &scancnt);
+ if (status != SANE_STATUS_GOOD)
+ {
+ flines = 0;
+ }
+ else
+ {
+ /* compute number of line read */
+ tmp = (int) dev->total_bytes_read;
+ if (depth == 1 || dev->settings.scan_mode == SCAN_MODE_LINEART)
+ flines = tmp * 8 / dev->settings.pixels / channels;
+ else
+ flines = tmp / (depth / 8) / dev->settings.pixels / channels;
+
+ /* number of scanned lines, but no read yet */
+ flines = scancnt - flines;
+
+ DBG (DBG_io,
+ "gl843_detect_document_end: %d scanned but not read lines\n",
+ flines);
+ }
+
+ /* adjust number of bytes to read
+ * we need to read the final bytes which are word per line * number of last lines
+ * to have doc leaving feeder */
+ lines =
+ (SANE_UNFIX (dev->model->post_scan) * dev->current_setup.yres) /
+ MM_PER_INCH + flines;
+ DBG (DBG_io, "gl843_detect_document_end: adding %d line to flush\n",
+ lines);
+
+ /* number of bytes to read from scanner to get document out of it after
+ * end of document dectected by hardware sensor */
+ bytes_to_flush = lines * dev->wpl;
+
+ /* if we are already close to end of scan, flushing isn't needed */
+ if (bytes_to_flush < read_bytes_left)
+ {
+ /* we take all these step to work around an overflow on some plateforms */
+ tmp = (int) dev->total_bytes_read;
+ DBG (DBG_io, "gl843_detect_document_end: tmp=%d\n", tmp);
+ bytes_remain = (int) dev->total_bytes_to_read;
+ DBG (DBG_io, "gl843_detect_document_end: bytes_remain=%d\n",
+ bytes_remain);
+ bytes_remain = bytes_remain - tmp;
+ DBG (DBG_io, "gl843_detect_document_end: bytes_remain=%d\n",
+ bytes_remain);
+
+ /* remaining lines to read by frontend for the current scan */
+ if (depth == 1 || dev->settings.scan_mode == SCAN_MODE_LINEART)
+ {
+ flines = bytes_remain * 8 / dev->settings.pixels / channels;
+ }
+ else
+ flines = bytes_remain / (depth / 8)
+ / dev->settings.pixels / channels;
+ DBG (DBG_io, "gl843_detect_document_end: flines=%d\n", flines);
+
+ if (flines > lines)
+ {
+ /* change the value controlling communication with the frontend :
+ * total bytes to read is current value plus the number of remaining lines
+ * multiplied by bytes per line */
+ sublines = flines - lines;
+
+ if (depth == 1 || dev->settings.scan_mode == SCAN_MODE_LINEART)
+ sub_bytes =
+ ((dev->settings.pixels * sublines) / 8 +
+ (((dev->settings.pixels * sublines) % 8) ? 1 : 0)) *
+ channels;
+ else
+ sub_bytes =
+ dev->settings.pixels * sublines * channels * (depth / 8);
+
+ dev->total_bytes_to_read -= sub_bytes;
+
+ /* then adjust the physical bytes to read */
+ if (read_bytes_left > sub_bytes)
+ {
+ dev->read_bytes_left -= sub_bytes;
+ }
+ else
+ {
+ dev->total_bytes_to_read = dev->total_bytes_read;
+ dev->read_bytes_left = 0;
+ }
+
+ DBG (DBG_io, "gl843_detect_document_end: sublines=%d\n",
+ sublines);
+ DBG (DBG_io, "gl843_detect_document_end: subbytes=%d\n",
+ sub_bytes);
+ DBG (DBG_io,
+ "gl843_detect_document_end: total_bytes_to_read=%lu\n",
+ (unsigned long) dev->total_bytes_to_read);
+ DBG (DBG_io,
+ "gl843_detect_document_end: read_bytes_left=%d\n",
+ read_bytes_left);
+ }
+ }
+ else
+ {
+ DBG (DBG_io, "gl843_detect_document_end: no flushing needed\n");
+ }
+ }
+
+ DBG (DBG_proc, "%s: finished\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief disable XPA slider motor
+ * toggle gpios to switch disble XPA slider motor
+ * @param dev device to set up
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl843_xpa_motor_off(Genesys_Device *dev)
+{
+ uint8_t val;
+ SANE_Status status=SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ /* unset GPOADF */
+ RIE (sanei_genesys_read_register (dev, REG6B, &val));
+ val &= ~REG6B_GPOADF;
+ RIE (sanei_genesys_write_register (dev, REG6B, val));
+
+ RIE (sanei_genesys_read_register (dev, REGA8, &val));
+ val |= REGA8_GPO27;
+ RIE (sanei_genesys_write_register (dev, REGA8, val));
+
+ RIE (sanei_genesys_read_register (dev, REGA9, &val));
+ val &= ~REGA9_GPO31;
+ RIE (sanei_genesys_write_register (dev, REGA9, val));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** @brief enable XPA slider motor
+ * toggle gpios to switch enable XPA slider motor
+ * @param dev device to set up
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl843_xpa_motor_on(Genesys_Device *dev)
+{
+ uint8_t val;
+ SANE_Status status=SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ /* set MULTFILM et GPOADF */
+ RIE (sanei_genesys_read_register (dev, REG6B, &val));
+ val |=REG6B_MULTFILM|REG6B_GPOADF;
+ RIE (sanei_genesys_write_register (dev, REG6B, val));
+
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ val &= ~REG6C_GPIO15;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+
+ /* Motor power ? No move at all without this one */
+ RIE (sanei_genesys_read_register (dev, REGA6, &val));
+ val |= REGA6_GPIO20;
+ RIE (sanei_genesys_write_register(dev,REGA6,val));
+
+ RIE (sanei_genesys_read_register (dev, REGA8, &val));
+ val &= ~REGA8_GPO27;
+ RIE (sanei_genesys_write_register (dev, REGA8, val));
+
+ RIE (sanei_genesys_read_register (dev, REGA9, &val));
+ val |= REGA9_GPO32|REGA9_GPO31;
+ RIE (sanei_genesys_write_register (dev, REGA9, val));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** @brief light XPA lamp
+ * toggle gpios to switch off regular lamp and light on the
+ * XPA light
+ * @param dev device to set up
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl843_xpa_lamp_on(Genesys_Device *dev)
+{
+ uint8_t val;
+ SANE_Status status=SANE_STATUS_GOOD;
+
+ DBGSTART;
+
+ /* REGA6 */
+ RIE(sanei_genesys_read_register(dev, REGA6, &val));
+
+ /* cut regular lamp power */
+ val &= ~(REGA6_GPIO24|REGA6_GPIO23);
+
+ /* set XPA lamp power */
+ val |= REGA6_GPIO22 | REGA6_GPIO21 | REGA6_GPIO19;
+
+ RIE(sanei_genesys_write_register(dev, REGA6, val));
+
+ RIE(sanei_genesys_read_register(dev, REGA7, &val));
+ val|=REGA7_GPOE24; /* lamp 1 off GPOE 24 */
+ val|=REGA7_GPOE23; /* lamp 2 off GPOE 23 */
+ val|=REGA7_GPOE22; /* full XPA lamp power */
+ RIE(sanei_genesys_write_register(dev, REGA7, val));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* Send the low-level scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl843_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor)
+{
+ SANE_Status status;
+ uint8_t val,r03;
+ uint16_t dpiset, dpihw;
+
+ DBGSTART;
+
+ /* get back the target dpihw */
+ sanei_genesys_get_double (reg, REG_DPISET, &dpiset);
+ dpihw = sanei_genesys_compute_dpihw (dev, dpiset);
+
+ /* set up GPIO for scan */
+ switch(dev->model->gpo_type)
+ {
+ /* KV case */
+ case GPO_KVSS080:
+ RIE (sanei_genesys_write_register (dev, REGA9, 0x00));
+ RIE (sanei_genesys_write_register (dev, REGA6, 0xf6));
+ /* blinking led */
+ RIE (sanei_genesys_write_register (dev, 0x7e, 0x04));
+ break;
+ case GPO_G4050:
+ RIE (sanei_genesys_write_register (dev, REGA7, 0xfe));
+ RIE (sanei_genesys_write_register (dev, REGA8, 0x3e));
+ RIE (sanei_genesys_write_register (dev, REGA9, 0x06));
+ switch (dpihw)
+ {
+ case 1200:
+ case 2400:
+ case 4800:
+ RIE (sanei_genesys_write_register (dev, REG6C, 0x60));
+ RIE (sanei_genesys_write_register (dev, REGA6, 0x46));
+ break;
+ default: /* 600 dpi case */
+ RIE (sanei_genesys_write_register (dev, REG6C, 0x20));
+ RIE (sanei_genesys_write_register (dev, REGA6, 0x44));
+ }
+
+ /* turn on XPA lamp if XPA is selected and lamp power on*/
+ r03 = sanei_genesys_read_reg_from_set (reg, REG03);
+ if ((r03 & REG03_XPASEL) && (r03 & REG03_LAMPPWR))
+ {
+ RIE(gl843_xpa_lamp_on(dev));
+ }
+
+ /* enable XPA lamp motor */
+ if (r03 & REG03_XPASEL)
+ {
+ RIE(gl843_xpa_motor_on(dev));
+ }
+
+ /* blinking led */
+ RIE (sanei_genesys_write_register (dev, REG7E, 0x01));
+ break;
+ case GPO_CS4400F:
+ case GPO_CS8400F:
+ default:
+ break;
+ }
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register
+ (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* enable scan and motor */
+ RIE (sanei_genesys_read_register (dev, REG01, &val));
+ val |= REG01_SCAN;
+ RIE (sanei_genesys_write_register (dev, REG01, val));
+
+ if (start_motor)
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 1));
+ }
+ else
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 0));
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl843_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBG (DBG_proc, "gl843_end_scan (check_stop = %d)\n", check_stop);
+ if (reg == NULL)
+ return SANE_STATUS_INVAL;
+
+ /* post scan gpio */
+ RIE(sanei_genesys_write_register(dev,0x7e,0x00));
+
+ /* turn off XPA lamp if XPA is selected and lamp power on*/
+ val = sanei_genesys_read_reg_from_set (reg, REG03);
+ if (val & (REG03_XPASEL|REG03_LAMPPWR))
+ {
+ sanei_genesys_read_register (dev, REGA6, &val);
+
+ /* switch on regular lamp */
+ val |= 0x40;
+
+ /* no XPA lamp power (2 bits for level: __11 ____) */
+ val &= ~0x30;
+
+ RIE (sanei_genesys_write_register (dev, REGA6, val));
+ }
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else /* flat bed scanners */
+ {
+ status = gl843_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_end_scan: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/** @brief park XPA lamp
+ * park the XPA lamp if needed
+ */
+static SANE_Status gl843_park_xpa_lamp (Genesys_Device * dev)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL843_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ uint8_t val;
+ int loop = 0;
+
+ DBGSTART;
+
+ /* copy scan settings */
+ memcpy (local_reg, dev->reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* set a huge feedl and reverse direction */
+ sanei_genesys_set_triple(local_reg,REG_FEEDL,0xbdcd);
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* set up for reverse and no scan */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ r->value |= REG02_MTRREV;
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ /* write to scanner and start action */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL843_MAX_REGS));
+ RIE (gl843_xpa_motor_on(dev));
+ status = gl843_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to start motor: %s\n",__FUNCTION__, sane_strstatus (status));
+ gl843_stop_action (dev);
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL843_MAX_REGS);
+ return status;
+ }
+
+ while (loop < 600) /* do not wait longer then 60 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n",__FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ if (val & REG41_HOMESNR) /* home sensor */
+ {
+ DBG (DBG_info, "%s: reached home position\n",__FUNCTION__);
+ DBG (DBG_proc, "%s: finished\n",__FUNCTION__);
+
+ /* clear GPOADF to avoid reparking again */
+ sanei_genesys_read_register (dev, REG6B, &val);
+ val &= ~REG6B_GPOADF;
+ sanei_genesys_write_register (dev, REG6B, val);
+
+ /* disable XPA slider motor */
+ gl843_xpa_motor_off(dev);
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* we are not parked here.... should we fail ? */
+ DBG (DBG_info, "%s: XPA lamp is not parked\n", __FUNCTION__);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief Moves the slider to the home (top) position slowly
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl843_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL843_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ uint8_t val;
+ float resolution;
+ int loop = 0;
+
+ DBG (DBG_proc, "gl843_slow_back_home (wait_until_home = %d)\n",
+ wait_until_home);
+
+ if (dev->model->gpo_type == GPO_G4050)
+ {
+ /* test if we need to park XPA lamp, we check GPOADF */
+ RIE (sanei_genesys_read_register (dev, REG6B, &val));
+ if(val & REG6B_GPOADF)
+ {
+ RIE(gl843_park_xpa_lamp(dev));
+ }
+ }
+
+ /* regular slow back home */
+ dev->scanhead_position_in_steps = 0;
+
+ /* first read gives HOME_SENSOR true */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to read home sensor: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+ usleep (100000); /* sleep 100 ms */
+
+ /* second is reliable */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ if (val & HOMESNR) /* is sensor at home? */
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy (local_reg, dev->reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+
+ gl843_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 100,
+ 40000,
+ 100,
+ 100,
+ 8,
+ 1,
+ SCAN_MODE_LINEART,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* set up for reverse and no scan */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ r->value |= REG02_MTRREV;
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL843_MAX_REGS));
+
+ status = gl843_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_slow_back_home: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl843_stop_action (dev);
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL843_MAX_REGS);
+ return status;
+ }
+
+ if (wait_until_home)
+ {
+
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ if (val & REG41_HOMESNR) /* home sensor */
+ {
+ DBG (DBG_info, "gl843_slow_back_home: reached home position\n");
+ DBG (DBG_proc, "gl843_slow_back_home: finished\n");
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl843_stop_action (dev);
+ DBG (DBG_error,
+ "gl843_slow_back_home: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_info, "%s: scanhead is still moving\n", __FUNCTION__);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
+ area at 600 dpi from very top of scanner */
+static SANE_Status
+gl843_search_start_position (Genesys_Device * dev)
+{
+ int size;
+ SANE_Status status;
+ uint8_t *data;
+ Genesys_Register_Set local_reg[GENESYS_GL843_MAX_REGS];
+ int steps;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ DBG (DBG_proc, "gl843_search_start_position\n");
+
+ memcpy (local_reg, dev->reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ status = gl843_init_scan_regs (dev,
+ local_reg,
+ dpi,
+ dpi,
+ 0,
+ 0, /*we should give a small offset here~60 steps */
+ 600,
+ dev->model->search_lines,
+ 8,
+ 1,
+ SCAN_MODE_GRAY,
+ 1, /*green */
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to bulk setup registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send to scanner */
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL843_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ size = pixels * dev->model->search_lines;
+
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = gl843_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("search_position.pnm", data, 8, 1, pixels,
+ dev->model->search_lines);
+
+ status = gl843_end_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* update regs to copy ASIC internal state */
+ memcpy (dev->reg, local_reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ status =
+ sanei_genesys_search_reference_point (dev, data, 0, dpi, pixels,
+ dev->model->search_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to set search reference point: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ free (data);
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sets up register for coarse gain calibration
+ * todo: check it for scanners using it */
+static SANE_Status
+gl843_init_regs_for_coarse_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t channels;
+ uint8_t cksel;
+
+ DBGSTART;
+ cksel = (dev->calib_reg[reg_0x18].value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
+
+ /* set line size */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ status = gl843_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ /* XXX STEF XXX dpi instead of pixels !*/
+ dev->sensor.optical_res / cksel,
+ 20,
+ 16,
+ channels,
+ dev->settings.scan_mode,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_init_register_for_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gl843_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ DBG (DBG_info,
+ "gl843_init_register_for_coarse_calibration: optical sensor res: %d dpi, actual res: %d\n",
+ dev->sensor.optical_res / cksel, dev->settings.xres);
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_init_register_for_coarse_calibration: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief moves the slider to steps at motor base dpi
+ * @param dev device to work on
+ * @param steps number of steps to move
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl843_feed (Genesys_Device * dev, unsigned int steps)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL843_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ float resolution;
+ uint8_t val;
+
+ DBGSTART;
+
+ /* prepare local registers */
+ memcpy (local_reg, dev->reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+ gl843_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 0,
+ steps,
+ 100,
+ 3,
+ 8,
+ 3,
+ SCAN_MODE_COLOR,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_FEEDING |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
+
+ /* set up for no scan */
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ /* send registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL843_MAX_REGS));
+
+ status = gl843_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to start motor: %s\n", __FUNCTION__, sane_strstatus (status));
+ gl843_stop_action (dev);
+
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL843_MAX_REGS);
+ return status;
+ }
+
+ /* wait until feed count reaches the required value, but do not
+ * exceed 30s */
+ do
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ }
+ while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* init registers for shading calibration */
+/* shading calibration is done at dpihw */
+static SANE_Status
+gl843_init_regs_for_shading (Genesys_Device * dev)
+{
+ SANE_Status status;
+ int move, resolution, dpihw, factor;
+ uint16_t strpixel;
+
+ DBGSTART;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ dev->calib_channels = 3;
+ dev->calib_lines = dev->model->shading_lines;
+ dpihw=sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ factor=dev->sensor.optical_res/dpihw;
+ resolution=dpihw;
+ dev->calib_resolution = resolution;
+ dev->calib_pixels = dev->sensor.sensor_pixels/factor;
+
+ /* distance to move to reach white target */
+ move = SANE_UNFIX (dev->model->y_offset_calib);
+ move = (move * resolution) / MM_PER_INCH;
+
+ status = gl843_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ move,
+ dev->calib_pixels,
+ dev->calib_lines,
+ 16,
+ dev->calib_channels,
+ dev->settings.scan_mode,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_init_registers_for_shading: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->scanhead_position_in_steps += dev->calib_lines + move;
+ sanei_genesys_get_double(dev->calib_reg,REG_STRPIXEL,&strpixel);
+ DBG (DBG_info, "%s: STRPIXEL=%d\n", __FUNCTION__, strpixel);
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_init_registers_for_shading: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief set up registers for the actual scan
+ */
+static SANE_Status
+gl843_init_regs_for_scan (Genesys_Device * dev)
+{
+ int channels;
+ int flags;
+ int depth;
+ float move;
+ int move_dpi;
+ float start;
+
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl843_init_regs_for_scan settings:\nResolution: %ux%uDPI\n"
+ "Lines : %u\npixels : %u\nStartpos : %.3f/%.3f\nScan mode : %d\n\n",
+ dev->settings.xres,
+ dev->settings.yres,
+ dev->settings.lines,
+ dev->settings.pixels,
+ dev->settings.tl_x,
+ dev->settings.tl_y,
+ dev->settings.scan_mode);
+
+ /* ensure head is parked in case of calibration */
+ gl843_slow_back_home (dev, SANE_TRUE);
+
+ /* channels */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR)
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART)
+ depth = 1;
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = SANE_UNFIX (dev->model->y_offset);
+ move += dev->settings.tl_y;
+ move = (move * move_dpi) / MM_PER_INCH;
+ DBG (DBG_info, "gl843_init_regs_for_scan: move=%f steps\n", move);
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+ flags = 0;
+
+ /* enable emulated lineart from gray data */
+ if(dev->settings.scan_mode == SCAN_MODE_LINEART
+ && dev->settings.dynamic_lineart)
+ {
+ flags |= SCAN_FLAG_DYNAMIC_LINEART;
+ }
+
+ status = gl843_init_scan_regs (dev,
+ dev->reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ start,
+ move,
+ dev->settings.pixels,
+ dev->settings.lines,
+ depth,
+ channels,
+ dev->settings.scan_mode,
+ dev->settings.color_filter,
+ flags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * This function sends gamma tables to ASIC
+ */
+static SANE_Status
+gl843_send_gamma_table (Genesys_Device * dev)
+{
+ int size;
+ int status;
+ uint8_t *gamma;
+ int i;
+
+ DBGSTART;
+
+ size = 256;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ gamma = (uint8_t *) malloc (size * 2 * 3);
+ if (!gamma)
+ return SANE_STATUS_NO_MEM;
+
+ /* copy sensor specific's gamma tables */
+ for (i = 0; i < size; i++)
+ {
+ gamma[i * 2 + size * 0 + 0] = dev->sensor.gamma_table[GENESYS_RED][i] & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (dev->sensor.gamma_table[GENESYS_RED][i] >> 8) & 0xff;
+ gamma[i * 2 + size * 2 + 0] = dev->sensor.gamma_table[GENESYS_GREEN][i] & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (dev->sensor.gamma_table[GENESYS_GREEN][i] >> 8) & 0xff;
+ gamma[i * 2 + size * 4 + 0] = dev->sensor.gamma_table[GENESYS_BLUE][i] & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (dev->sensor.gamma_table[GENESYS_BLUE][i] >> 8) & 0xff;
+ }
+
+ /* send address */
+ status = gl843_set_buffer_address (dev, 0x0000);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "gl843_send_gamma_table: failed to set buffer address: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* send data */
+ status = gl843_bulk_write_data (dev, 0x28, (uint8_t *) gamma, size * 2 * 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "gl843_send_gamma_table: failed to send gamma table: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "gl843_send_gamma_table: completed\n");
+ free (gamma);
+ return SANE_STATUS_GOOD;
+}
+
+/* this function does the led calibration by scanning one line of the calibration
+ area below scanner's top on white strip.
+
+-needs working coarse/gain
+*/
+static SANE_Status
+gl843_led_calibration (Genesys_Device * dev)
+{
+ int num_pixels;
+ int total_size;
+ int used_res;
+ uint8_t *line;
+ int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ int channels, depth;
+ int avg[3], avga, avge;
+ int turn;
+ char fn[20];
+ uint16_t expr, expg, expb;
+ Genesys_Register_Set *r;
+
+ SANE_Bool acceptable = SANE_FALSE;
+
+ DBG (DBG_proc, "gl843_led_calibration\n");
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ depth = 16;
+ used_res = dev->sensor.optical_res;
+ num_pixels =
+ (dev->sensor.sensor_pixels * used_res) / dev->sensor.optical_res;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg,
+ GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ status = gl843_init_scan_regs (dev,
+ dev->calib_reg,
+ used_res,
+ dev->motor.base_ydpi,
+ 0,
+ 0,
+ num_pixels,
+ 1,
+ depth,
+ channels,
+ SCAN_MODE_COLOR,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_led_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (dev->model->cmd_set->bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS));
+
+
+ total_size = num_pixels * channels * (depth / 8) * 1; /* colors * bytes_per_color * scan lines */
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+/*
+ we try to get equal bright leds here:
+
+ loop:
+ average per color
+ adjust exposure times
+ */
+
+ expr = (dev->sensor.regs_0x10_0x1d[0] << 8) | dev->sensor.regs_0x10_0x1d[1];
+ expg = (dev->sensor.regs_0x10_0x1d[2] << 8) | dev->sensor.regs_0x10_0x1d[3];
+ expb = (dev->sensor.regs_0x10_0x1d[4] << 8) | dev->sensor.regs_0x10_0x1d[5];
+
+ turn = 0;
+
+ do
+ {
+
+ dev->sensor.regs_0x10_0x1d[0] = (expr >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[1] = expr & 0xff;
+ dev->sensor.regs_0x10_0x1d[2] = (expg >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[3] = expg & 0xff;
+ dev->sensor.regs_0x10_0x1d[4] = (expb >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[5] = expb & 0xff;
+
+ for (i = 0; i < 6; i++)
+ {
+ r = sanei_genesys_get_address (dev->calib_reg, 0x10 + i);
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+
+ RIE (dev->model->cmd_set->bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS));
+
+ DBG (DBG_info, "gl843_led_calibration: starting first line reading\n");
+ RIE (gl843_begin_scan (dev, dev->calib_reg, SANE_TRUE));
+ RIE (sanei_genesys_read_data_from_scanner (dev, line, total_size));
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf (fn, 20, "led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file (fn,
+ line, depth, channels, num_pixels, 1);
+ }
+
+ acceptable = SANE_TRUE;
+
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG (DBG_info, "gl843_led_calibration: average: "
+ "%d,%d,%d\n", avg[0], avg[1], avg[2]);
+
+ acceptable = SANE_TRUE;
+
+ if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
+ avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
+ avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
+ acceptable = SANE_FALSE;
+
+ if (!acceptable)
+ {
+ avga = (avg[0] + avg[1] + avg[2]) / 3;
+ expr = (expr * avga) / avg[0];
+ expg = (expg * avga) / avg[1];
+ expb = (expb * avga) / avg[2];
+/*
+ keep the resulting exposures below this value.
+ too long exposure drives the ccd into saturation.
+ we may fix this by relying on the fact that
+ we get a striped scan without shading, by means of
+ statistical calculation
+*/
+ avge = (expr + expg + expb) / 3;
+
+ /* don't overflow max exposure */
+ if (avge > 3000)
+ {
+ expr = (expr * 2000) / avge;
+ expg = (expg * 2000) / avge;
+ expb = (expb * 2000) / avge;
+ }
+ if (avge < 50)
+ {
+ expr = (expr * 50) / avge;
+ expg = (expg * 50) / avge;
+ expb = (expb * 50) / avge;
+ }
+
+ }
+
+ RIE (gl843_stop_action (dev));
+
+ turn++;
+
+ }
+ while (!acceptable && turn < 100);
+
+ DBG (DBG_info, "gl843_led_calibration: acceptable exposure: %d,%d,%d\n",
+ expr, expg, expb);
+
+ /* cleanup before return */
+ free (line);
+
+ gl843_slow_back_home (dev, SANE_TRUE);
+
+ DBG (DBG_proc, "gl843_led_calibration: completed\n");
+ return status;
+}
+
+
+
+/**
+ * average dark pixels of a 8 bits scan of a given channel
+ */
+static int
+dark_average_channel (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black, int channel)
+{
+ unsigned int i, j, k, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average values on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j*channels + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG (DBG_info, "%s: avg[%d] = %d\n", __FUNCTION__, k, avg[k]);
+ }
+ DBG (DBG_info, "%s: average = %d\n", __FUNCTION__, avg[channel]);
+ return avg[channel];
+}
+
+/** @brief calibrate AFE offset
+ * Iterate doing scans at target dpi until AFE offset if correct. One
+ * color line is scanned at a time. Scanning head doesn't move.
+ * @param dev device to calibrate
+ */
+static SANE_Status
+gl843_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t *first_line, *second_line;
+ unsigned int channels, bpp;
+ char title[32];
+ int pass, total_size, i, resolution, lines;
+ int topavg[3], bottomavg[3], avg[3];
+ int top[3], bottom[3], black_pixels, pixels, factor, dpihw;
+
+ DBGSTART;
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ lines = 8;
+ bpp = 8;
+
+ /* compute divider factor to compute final pixels number */
+ dpihw = sanei_genesys_compute_dpihw (dev, dev->settings.xres);
+ factor = dev->sensor.optical_res / dpihw;
+ resolution = dpihw;
+ pixels = dev->sensor.sensor_pixels / factor;
+ black_pixels = dev->sensor.black_pixels / factor;
+ DBG (DBG_io, "gl843_offset_calibration: dpihw =%d\n", dpihw);
+ DBG (DBG_io, "gl843_offset_calibration: factor =%d\n", factor);
+ DBG (DBG_io, "gl843_offset_calibration: resolution =%d\n", resolution);
+ DBG (DBG_io, "gl843_offset_calibration: pixels =%d\n", pixels);
+ DBG (DBG_io, "gl843_offset_calibration: black_pixels=%d\n", black_pixels);
+
+ status = gl843_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ SCAN_MODE_COLOR,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl843_offset_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gl843_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ /* allocate memory for scans */
+ total_size = pixels * channels * lines * (bpp / 8); /* colors * bytes_per_color * scan lines */
+
+ first_line = malloc (total_size);
+ if (!first_line)
+ return SANE_STATUS_NO_MEM;
+
+ second_line = malloc (total_size);
+ if (!second_line)
+ {
+ free (first_line);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* init gain and offset */
+ for (i = 0; i < 3; i++)
+ {
+ bottom[i] = 10;
+ dev->frontend.offset[i] = bottom[i];
+ dev->frontend.gain[i] = 0;
+ }
+ RIEF2 (gl843_set_fe (dev, AFE_SET), first_line, second_line);
+
+ /* scan with obttom AFE settings */
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl843_offset_calibration: starting first line reading\n");
+ RIEF2 (gl843_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, first_line, total_size), first_line, second_line);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ for (i = 0; i < 3; i++)
+ {
+ snprintf (title, 20, "offset_%d_%03d.pnm", i, bottom[i]);
+ sanei_genesys_write_pnm_file (title, first_line, bpp, channels, pixels, lines);
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ bottomavg[i] = dark_average_channel (first_line, pixels, lines, channels, black_pixels, i);
+ DBG (DBG_io2, "gl843_offset_calibration: bottom avg %d=%d\n", i, bottomavg[i]);
+ }
+
+ /* now top value */
+ for (i = 0; i < 3; i++)
+ {
+ top[i] = 255;
+ dev->frontend.offset[i] = top[i];
+ }
+ RIEF2 (gl843_set_fe (dev, AFE_SET), first_line, second_line);
+
+ /* scan with top AFE values */
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl843_offset_calibration: starting second line reading\n");
+ RIEF2 (gl843_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ for (i = 0; i < 3; i++)
+ {
+ topavg[i] = dark_average_channel (second_line, pixels, lines, channels, black_pixels, i);
+ DBG (DBG_io2, "gl843_offset_calibration: top avg %d=%d\n", i, topavg[i]);
+ }
+
+ pass = 0;
+
+ /* loop until acceptable level */
+ while ((pass < 32)
+ && ((top[0] - bottom[0] > 1)
+ || (top[1] - bottom[1] > 1) || (top[2] - bottom[2] > 1)))
+ {
+ pass++;
+
+ /* settings for new scan */
+ for (i = 0; i < 3; i++)
+ {
+ if (top[i] - bottom[i] > 1)
+ {
+ dev->frontend.offset[i] = (top[i] + bottom[i]) / 2;
+ }
+ }
+ RIEF2 (gl843_set_fe (dev, AFE_SET), first_line, second_line);
+
+ /* scan with no move */
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl843_offset_calibration: starting second line reading\n");
+ RIEF2 (gl843_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ for (i = 0; i < 3; i++)
+ {
+ sprintf (title, "offset_%d_%03d.pnm", i, dev->frontend.offset[i]);
+ sanei_genesys_write_pnm_file (title, second_line, bpp, channels, pixels, lines);
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ avg[i] = dark_average_channel (second_line, pixels, lines, channels, black_pixels, i);
+ DBG (DBG_info, "gl843_offset_calibration: avg[%d]=%d offset=%d\n", i, avg[i], dev->frontend.offset[i]);
+ }
+
+ /* compute new boundaries */
+ for (i = 0; i < 3; i++)
+ {
+ if (topavg[i] >= avg[i])
+ {
+ topavg[i] = avg[i];
+ top[i] = dev->frontend.offset[i];
+ }
+ else
+ {
+ bottomavg[i] = avg[i];
+ bottom[i] = dev->frontend.offset[i];
+ }
+ }
+ }
+ DBG (DBG_info, "gl843_offset_calibration: offset=(%d,%d,%d)\n",
+ dev->frontend.offset[0],
+ dev->frontend.offset[1],
+ dev->frontend.offset[2]);
+
+ /* cleanup before return */
+ free (first_line);
+ free (second_line);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* alternative coarse gain calibration
+ this on uses the settings from offset_calibration and
+ uses only one scanline
+ */
+/*
+ with offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+ */
+static SANE_Status
+gl843_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ int pixels, factor, dpihw;
+ int total_size;
+ uint8_t *line;
+ int i, j, channels;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+ int resolution;
+ int bpp;
+
+ DBG (DBG_proc, "gl843_coarse_gain_calibration: dpi = %d\n", dpi);
+ dpihw=sanei_genesys_compute_dpihw(dev, dpi);
+ factor=dev->sensor.optical_res/dpihw;
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ /* follow CKSEL */
+ if (dev->model->ccd_type == CCD_KVSS080)
+ {
+ if(dev->settings.xres<dev->sensor.optical_res)
+ {
+ coeff=0.9;
+ }
+ else
+ {
+ coeff=1.0;
+ }
+ }
+ else
+ {
+ coeff=1.0;
+ }
+ resolution=dpihw;
+ lines=10;
+ bpp=8;
+ pixels = dev->sensor.sensor_pixels / factor;
+
+ status = gl843_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ SCAN_MODE_COLOR,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ gl843_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (dev->model->cmd_set->bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL843_MAX_REGS));
+
+ total_size = pixels * channels * (16/bpp) * lines;
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ RIEF (gl843_set_fe(dev, AFE_SET), line);
+ RIEF (gl843_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("coarse.pnm", line, bpp, channels, pixels, lines);
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if(bpp==16)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * pixels + 1] * 256 +
+ line[i * 2 + j * 2 * pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ }
+ else
+ {
+ if (dev->model->is_cis)
+ val = line[i + j * pixels];
+ else
+ val = line[i * channels + j];
+ }
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = ((float) dev->sensor.gain_white_ref*coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = 283 - 208 / gain[j];
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.gain[j] = code;
+
+ DBG (DBG_proc,
+ "gl843_coarse_gain_calibration: channel %d, max=%d, gain = %f, setting:%d\n",
+ j, max[j], gain[j], dev->frontend.gain[j]);
+ }
+
+ if (dev->model->is_cis)
+ {
+ if (dev->frontend.gain[0] > dev->frontend.gain[1])
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ if (dev->frontend.gain[0] > dev->frontend.gain[2])
+ dev->frontend.gain[0] = dev->frontend.gain[2];
+ dev->frontend.gain[2] = dev->frontend.gain[1] = dev->frontend.gain[0];
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ dev->frontend.gain[2] = dev->frontend.gain[1];
+ }
+
+ free (line);
+
+ RIE (gl843_stop_action (dev));
+
+ status=gl843_slow_back_home (dev, SANE_TRUE);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/*
+ * wait for lamp warmup by scanning the same line until difference
+ * between 2 scans is below a threshold
+ */
+static SANE_Status
+gl843_init_regs_for_warmup (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ int *channels, int *total_size)
+{
+ int num_pixels;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int dpihw;
+ int resolution;
+ int factor;
+
+ DBGSTART;
+ if (dev == NULL || reg == NULL || channels == NULL || total_size == NULL)
+ return SANE_STATUS_INVAL;
+
+ /* setup scan */
+ *channels=3;
+ resolution=600;
+ dpihw=sanei_genesys_compute_dpihw(dev, resolution);
+ factor=dev->sensor.optical_res/dpihw;
+ num_pixels=dev->sensor.sensor_pixels/(factor*2);
+ *total_size = num_pixels * 3 * 1;
+
+ memcpy (reg, dev->reg, (GENESYS_GL843_MAX_REGS + 1) * sizeof (Genesys_Register_Set));
+ status = gl843_init_scan_regs (dev,
+ reg,
+ resolution,
+ resolution,
+ num_pixels/2,
+ 0,
+ num_pixels,
+ 1,
+ 8,
+ *channels,
+ SCAN_MODE_COLOR,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ gl843_set_motor_power (reg, SANE_FALSE);
+ RIE (dev->model->cmd_set->bulk_write_register (dev, reg, GENESYS_GL843_MAX_REGS));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+WRITE GPIO[17-21]= GPIO19
+WRITE GPOE[17-21]= GPOE21 GPOE20 GPOE19 GPOE18
+genesys_write_register(0xa8,0x3e)
+GPIO(0xa8)=0x3e
+ */
+static SANE_Status
+gl843_init_gpio (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx;
+
+ DBGSTART;
+
+ RIE (sanei_genesys_write_register (dev, REG6E, dev->gpo.enable[0]));
+ RIE (sanei_genesys_write_register (dev, REG6F, dev->gpo.enable[1]));
+ RIE (sanei_genesys_write_register (dev, REG6C, dev->gpo.value[0]));
+ RIE (sanei_genesys_write_register (dev, REG6D, dev->gpo.value[1]));
+
+ idx=0;
+ while(dev->model->gpo_type != gpios[idx].gpo_type && gpios[idx].gpo_type!=0)
+ {
+ idx++;
+ }
+ if (gpios[idx].gpo_type!=0)
+ {
+ RIE (sanei_genesys_write_register (dev, REGA6, gpios[idx].ra6));
+ RIE (sanei_genesys_write_register (dev, REGA7, gpios[idx].ra7));
+ RIE (sanei_genesys_write_register (dev, REGA8, gpios[idx].ra8));
+ RIE (sanei_genesys_write_register (dev, REGA9, gpios[idx].ra9));
+ }
+ else
+ {
+ status=SANE_STATUS_INVAL;
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/* *
+ * initialize ASIC from power on condition
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl843_boot (Genesys_Device * dev, SANE_Bool cold)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBGSTART;
+
+ if(cold)
+ {
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
+ }
+
+ if(dev->usb_mode == 1)
+ {
+ val = 0x14;
+ }
+ else
+ {
+ val = 0x11;
+ }
+ RIE (sanei_genesys_write_0x8c (dev, 0x0f, val));
+
+ /* test CHKVER */
+ RIE (sanei_genesys_read_register (dev, REG40, &val));
+ if (val & REG40_CHKVER)
+ {
+ RIE (sanei_genesys_read_register (dev, 0x00, &val));
+ DBG (DBG_info,
+ "%s: reported version for genesys chip is 0x%02x\n", __FUNCTION__,
+ val);
+ }
+
+ /* Set default values for registers */
+ gl843_init_registers (dev);
+
+ RIE (sanei_genesys_write_register (dev, REG6B, 0x02));
+
+ /* Write initial registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL843_MAX_REGS));
+
+ /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
+ val = dev->reg[reg_0x0b].value & REG0B_DRAMSEL;
+ val = (val | REG0B_ENBDRAM);
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].value = val;
+ /* URB 14 control 0x40 0x0c 0x8c 0x10 len 1 wrote 0xb4 */
+ RIE (sanei_genesys_write_0x8c (dev, 0x10, 0xb4));
+
+ /* CLKSET */
+ val = (dev->reg[reg_0x0b].value & ~REG0B_CLKSET) | REG0B_48MHZ;
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].value = val;
+
+ /* prevent further writings by bulk write register */
+ dev->reg[reg_0x0b].address = 0x00;
+
+ /* set up end access */
+ sanei_genesys_write_register (dev, REGA7, 0x04);
+ sanei_genesys_write_register (dev, REGA9, 0x00);
+
+ /* set RAM read address */
+ RIE (sanei_genesys_write_register (dev, REG29, 0x00));
+ RIE (sanei_genesys_write_register (dev, REG2A, 0x00));
+ RIE (sanei_genesys_write_register (dev, REG2B, 0x00));
+
+ /* setup gpio */
+ RIE (gl843_init_gpio (dev));
+
+ gl843_feed (dev, 300);
+ usleep (100000);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* *
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl843_init (Genesys_Device * dev)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+ DBGSTART;
+
+ status=sanei_genesys_asic_init(dev, GENESYS_GL843_MAX_REGS);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+static SANE_Status
+gl843_update_hardware_sensors (Genesys_Scanner * s)
+{
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t val;
+
+ RIE (sanei_genesys_read_register (s->dev, REG6D, &val));
+
+ switch (s->dev->model->gpo_type)
+ {
+ case GPO_KVSS080:
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & 0x04) == 0;
+ break;
+ case GPO_G4050:
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & 0x01) == 0;
+ if (s->val[OPT_FILE_SW].b == s->last_val[OPT_FILE_SW].b)
+ s->val[OPT_FILE_SW].b = (val & 0x02) == 0;
+ if (s->val[OPT_EMAIL_SW].b == s->last_val[OPT_EMAIL_SW].b)
+ s->val[OPT_EMAIL_SW].b = (val & 0x04) == 0;
+ if (s->val[OPT_COPY_SW].b == s->last_val[OPT_COPY_SW].b)
+ s->val[OPT_COPY_SW].b = (val & 0x08) == 0;
+ break;
+ case GPO_CS4400F:
+ case GPO_CS8400F:
+ default:
+ break;
+ }
+
+ return status;
+}
+#ifndef UNIT_TESTING
+static
+#endif
+/** @brief move sensor to transparency adaptor
+ * Move sensor to the calibration of the transparency adapator (XPA).
+ * @param dev device to use
+ */
+SANE_Status gl843_move_to_ta (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ float resolution;
+ unsigned int feed;
+
+ DBGSTART;
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+ feed = 16*(SANE_UNFIX (dev->model->y_offset_calib_ta) * resolution) / MM_PER_INCH;
+ status = gl843_feed (dev, feed);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to move to XPA calibration area\n", __FUNCTION__);
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
+ * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
+ * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
+ */
+static SANE_Status
+gl843_search_strip (Genesys_Device * dev, SANE_Bool forward, SANE_Bool black)
+{
+ unsigned int pixels, lines, channels;
+ SANE_Status status;
+ Genesys_Register_Set local_reg[GENESYS_GL843_MAX_REGS];
+ size_t size;
+ uint8_t *data;
+ int steps, depth, dpi;
+ unsigned int pass, count, found, x, y;
+ char title[80];
+ Genesys_Register_Set *r;
+
+ DBG (DBG_proc, "gl843_search_strip %s %s\n", black ? "black" : "white",
+ forward ? "forward" : "reverse");
+
+ gl843_set_fe (dev, AFE_SET);
+ status = gl843_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_search_strip: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for a gray scan at lowest dpi */
+ dpi = sanei_genesys_get_lowest_dpi(dev);
+ channels = 1;
+
+ /* 10 MM */
+ /* lines = (10 * dpi) / MM_PER_INCH; */
+ /* shading calibation is done with dev->motor.base_ydpi */
+ lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
+ depth = 8;
+ pixels = (dev->sensor.sensor_pixels * dpi) / dev->sensor.optical_res;
+ size = pixels * channels * lines * (depth / 8);
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error, "gl843_search_strip: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->scanhead_position_in_steps = 0;
+
+ memcpy (local_reg, dev->reg,
+ GENESYS_GL843_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ status = gl843_init_scan_regs (dev,
+ local_reg,
+ dpi,
+ dpi,
+ 0,
+ 0,
+ pixels,
+ lines,
+ depth,
+ channels,
+ SCAN_MODE_GRAY,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl843_search_strip: failed to setup for scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for reverse or forward */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ if (forward)
+ r->value &= ~REG02_MTRREV;
+ else
+ r->value |= REG02_MTRREV;
+
+
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL843_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl843_search_strip: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl843_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl843_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl843_search_strip: gl843_stop_action failed\n");
+ return status;
+ }
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels, pixels,
+ lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < 20 && !found)
+ {
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL843_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl843_search_strip: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* now start scan */
+ status = gl843_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl843_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl843_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl843_search_strip: gl843_stop_action failed\n");
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels,
+ pixels, lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl843_search_strip: strip found forward during pass %d at line %d\n",
+ pass, y);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl843_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl843_search_strip: strip found backward during pass %d \n",
+ pass);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl843_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+ free (data);
+ if (found)
+ {
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_info, "gl843_search_strip: %s strip found\n",
+ black ? "black" : "white");
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ DBG (DBG_info, "gl843_search_strip: %s strip not found\n",
+ black ? "black" : "white");
+ }
+
+ DBG (DBG_proc, "gl843_search_strip: completed\n");
+ return status;
+}
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl843_send_shading_data (Genesys_Device * dev, uint8_t * data, int size)
+{
+ SANE_Status status;
+ uint32_t final_size, length, i;
+ uint8_t *final_data;
+ uint8_t *buffer;
+ int count,offset;
+ unsigned int tgtime;
+ unsigned int cksel;
+ Genesys_Register_Set *r;
+ uint16_t dpiset, strpixel, endpixel, startx, factor;
+
+ DBGSTART;
+
+ offset=0;
+ length=size;
+ r = sanei_genesys_get_address (dev->reg, REG01);
+ if(r->value & REG01_SHDAREA)
+ {
+ /* recompute STRPIXEL used shading calibration so we can
+ * compute offset within data for SHDAREA case */
+ r = sanei_genesys_get_address (dev->reg, REG18);
+ cksel= (r->value & REG18_CKSEL)+1;
+ sanei_genesys_get_double(dev->reg,REG_DPISET,&strpixel);
+ tgtime=1;
+ sanei_genesys_get_double(dev->reg,REG_DPISET,&dpiset);
+ factor=dev->sensor.optical_res/sanei_genesys_compute_dpihw(dev,dpiset);
+ if (dev->model->ccd_type == CCD_G4050 && dpiset>2400)
+ {
+ tgtime=2;
+ }
+
+ /* start coordinate in optical dpi coordinates */
+ startx = ((dev->sensor.dummy_pixel * tgtime)/cksel)/factor;
+
+ /* current scan coordinates */
+ sanei_genesys_get_double(dev->reg,REG_STRPIXEL,&strpixel);
+ sanei_genesys_get_double(dev->reg,REG_ENDPIXEL,&endpixel);
+ strpixel*=tgtime;
+ endpixel*=tgtime;
+
+ /* 16 bit words, 2 words per color, 3 color channels */
+ offset=(strpixel-startx)*2*2*3;
+ length=(endpixel-strpixel)*2*2*3;
+ DBG (DBG_info, "%s: STRPIXEL=%d, ENDPIXEL=%d, startx=%d\n", __FUNCTION__, strpixel, endpixel, startx);
+ }
+
+ /* compute and allocate size for final data */
+ final_size = ((length+251) / 252) * 256;
+ DBG (DBG_io, "%s: final shading size=%04x (length=%d)\n", __FUNCTION__, final_size, length);
+ final_data = (uint8_t *) malloc (final_size);
+ if(final_data==NULL)
+ {
+ DBG (DBG_error, "%s: failed to allocate memory for shading data\n", __FUNCTION__);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(final_data,0x00,final_size);
+
+ /* copy regular shading data to the expected layout */
+ buffer = final_data;
+ count = 0;
+
+ /* loop over calibration data */
+ for (i = 0; i < length; i++)
+ {
+ buffer[count] = data[offset+i];
+ count++;
+ if ((count % (256*2)) == (252*2))
+ {
+ count += 4*2;
+ }
+ }
+
+ /* send data */
+ status = sanei_genesys_set_buffer_address (dev, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to set buffer address: %s\n", __FUNCTION__, sane_strstatus (status));
+ free(final_data);
+ return status;
+ }
+
+ status = dev->model->cmd_set->bulk_write_data (dev, 0x3c, final_data, count);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to send shading table: %s\n", __FUNCTION__, sane_strstatus (status));
+ }
+
+ free(final_data);
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** the gl843 command set */
+static Genesys_Command_Set gl843_cmd_set = {
+ "gl843-generic", /* the name of this set */
+
+ gl843_init,
+ gl843_init_regs_for_warmup,
+ gl843_init_regs_for_coarse_calibration,
+ gl843_init_regs_for_shading,
+ gl843_init_regs_for_scan,
+
+ gl843_get_filter_bit,
+ gl843_get_lineart_bit,
+ gl843_get_bitset_bit,
+ gl843_get_gain4_bit,
+ gl843_get_fast_feed_bit,
+ gl843_test_buffer_empty_bit,
+ gl843_test_motor_flag_bit,
+
+ gl843_bulk_full_size,
+
+ gl843_set_fe,
+ gl843_set_powersaving,
+ gl843_save_power,
+
+ gl843_set_motor_power,
+ gl843_set_lamp_power,
+
+ gl843_begin_scan,
+ gl843_end_scan,
+
+ gl843_send_gamma_table,
+
+ gl843_search_start_position,
+
+ gl843_offset_calibration,
+ gl843_coarse_gain_calibration,
+ gl843_led_calibration,
+
+ gl843_slow_back_home,
+
+ sanei_genesys_bulk_write_register,
+ gl843_bulk_write_data,
+ gl843_bulk_read_data,
+
+ gl843_update_hardware_sensors,
+
+ gl843_load_document,
+ gl843_detect_document_end,
+ gl843_eject_document,
+ gl843_search_strip,
+
+ sanei_genesys_is_compatible_calibration,
+ gl843_move_to_ta,
+ gl843_send_shading_data,
+ gl843_calculate_current_setup,
+ gl843_boot
+};
+
+SANE_Status
+sanei_gl843_init_cmd_set (Genesys_Device * dev)
+{
+ dev->model->cmd_set = &gl843_cmd_set;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl843.h b/backend/genesys_gl843.h
new file mode 100644
index 0000000..241a2ed
--- /dev/null
+++ b/backend/genesys_gl843.h
@@ -0,0 +1,700 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+
+#ifdef UNIT_TESTING
+SANE_Status gl843_send_slope_table (Genesys_Device * dev, int table_nr, uint16_t * slope_table, int steps);
+SANE_Status gl843_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int scan_mode,
+ int color_filter,
+ unsigned int flags);
+SANE_Status gl843_start_action (Genesys_Device * dev);
+SANE_Status gl843_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool start_motor);
+SANE_Status gl843_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool check_stop);
+SANE_Status gl843_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
+SANE_Status gl843_feed (Genesys_Device * dev, unsigned int steps);
+SANE_Status gl843_init (Genesys_Device * dev);
+SANE_Status gl843_boot (Genesys_Device * dev, SANE_Bool cold);
+SANE_Status gl843_send_shading_data (Genesys_Device * dev, uint8_t * data, int size);
+SANE_Status gl843_bulk_write_register (Genesys_Device * dev, Genesys_Register_Set * reg, size_t elems);
+SANE_Status gl843_xpa_lamp_on (Genesys_Device * dev);
+SANE_Status gl843_xpa_motor_on (Genesys_Device * dev);
+SANE_Status gl843_xpa_motor_off (Genesys_Device * dev);
+SANE_Status gl843_move_to_ta (Genesys_Device * dev);
+#endif
+
+#define DBGSTART DBG (DBG_proc, "%s start\n", __FUNCTION__);
+#define DBGCOMPLETED DBG (DBG_proc, "%s completed\n", __FUNCTION__);
+
+#define REG01 0x01
+#define REG01_CISSET 0x80
+#define REG01_DOGENB 0x40
+#define REG01_DVDSET 0x20
+#define REG01_STAGGER 0x10
+#define REG01_COMPENB 0x08
+#define REG01_TRUEGRAY 0x04
+#define REG01_SHDAREA 0x02
+#define REG01_SCAN 0x01
+
+#define REG02 0x02
+#define REG02_NOTHOME 0x80
+#define REG02_ACDCDIS 0x40
+#define REG02_AGOHOME 0x20
+#define REG02_MTRPWR 0x10
+#define REG02_FASTFED 0x08
+#define REG02_MTRREV 0x04
+#define REG02_HOMENEG 0x02
+#define REG02_LONGCURV 0x01
+
+#define REG03 0x03
+#define REG03_LAMPDOG 0x80
+#define REG03_AVEENB 0x40
+#define REG03_XPASEL 0x20
+#define REG03_LAMPPWR 0x10
+#define REG03_LAMPTIM 0x0f
+
+#define REG04 0x04
+#define REG04_LINEART 0x80
+#define REG04_BITSET 0x40
+#define REG04_AFEMOD 0x30
+#define REG04_FILTER 0x0c
+#define REG04_FESET 0x03
+
+#define REG04S_AFEMOD 4
+
+#define REG05 0x05
+#define REG05_DPIHW 0xc0
+#define REG05_DPIHW_600 0x00
+#define REG05_DPIHW_1200 0x40
+#define REG05_DPIHW_2400 0x80
+#define REG05_DPIHW_4800 0xc0
+#define REG05_MTLLAMP 0x30
+#define REG05_GMMENB 0x08
+#define REG05_MTLBASE 0x03
+
+#define REG06 0x06
+#define REG06_SCANMOD 0xe0
+#define REG06S_SCANMOD 5
+#define REG06_PWRBIT 0x10
+#define REG06_GAIN4 0x08
+#define REG06_OPTEST 0x07
+
+#define REG07_LAMPSIM 0x80
+
+#define REG08_DECFLAG 0x40
+#define REG08_GMMFFR 0x20
+#define REG08_GMMFFG 0x10
+#define REG08_GMMFFB 0x08
+#define REG08_GMMZR 0x04
+#define REG08_GMMZG 0x02
+#define REG08_GMMZB 0x01
+
+#define REG09_MCNTSET 0xc0
+#define REG09_EVEN1ST 0x20
+#define REG09_BLINE1ST 0x10
+#define REG09_BACKSCAN 0x08
+#define REG09_ENHANCE 0x04
+#define REG09_SHORTTG 0x02
+#define REG09_NWAIT 0x01
+
+#define REG09S_MCNTSET 6
+#define REG09S_CLKSET 4
+
+#define REG0B 0x0b
+#define REG0B_DRAMSEL 0x07
+#define REG0B_ENBDRAM 0x08
+#define REG0B_ENBDRAM 0x08
+#define REG0B_RFHDIS 0x10
+#define REG0B_CLKSET 0xe0
+#define REG0B_24MHZ 0x00
+#define REG0B_30MHZ 0x20
+#define REG0B_40MHZ 0x40
+#define REG0B_48MHZ 0x60
+#define REG0B_60MHZ 0x80
+
+#define REG0D 0x0d
+#define REG0D_JAMPCMD 0x80
+#define REG0D_DOCCMD 0x40
+#define REG0D_CCDCMD 0x20
+#define REG0D_FULLSTP 0x10
+#define REG0D_SEND 0x08
+#define REG0D_CLRMCNT 0x04
+#define REG0D_CLRDOCJM 0x02
+#define REG0D_CLRLNCNT 0x01
+
+#define REG0F 0x0f
+
+#define REG_EXPR 0x10
+#define REG_EXPG 0x12
+#define REG_EXPB 0x14
+
+#define REG16_CTRLHI 0x80
+#define REG16_TOSHIBA 0x40
+#define REG16_TGINV 0x20
+#define REG16_CK1INV 0x10
+#define REG16_CK2INV 0x08
+#define REG16_CTRLINV 0x04
+#define REG16_CKDIS 0x02
+#define REG16_CTRLDIS 0x01
+
+#define REG17_TGMODE 0xc0
+#define REG17_TGMODE_NO_DUMMY 0x00
+#define REG17_TGMODE_REF 0x40
+#define REG17_TGMODE_XPA 0x80
+#define REG17_TGW 0x3f
+#define REG17S_TGW 0
+
+#define REG18 0x18
+#define REG18_CNSET 0x80
+#define REG18_DCKSEL 0x60
+#define REG18_CKTOGGLE 0x10
+#define REG18_CKDELAY 0x0c
+#define REG18_CKSEL 0x03
+
+#define REG_EXPDMY 0x19
+
+#define REG1A_TGLSW2 0x80
+#define REG1A_TGLSW1 0x40
+#define REG1A_MANUAL3 0x02
+#define REG1A_MANUAL1 0x01
+#define REG1A_CK4INV 0x08
+#define REG1A_CK3INV 0x04
+#define REG1A_LINECLP 0x02
+
+#define REG1C 0x1c
+#define REG1C_TGTIME 0x07
+
+#define REG1D_CK4LOW 0x80
+#define REG1D_CK3LOW 0x40
+#define REG1D_CK1LOW 0x20
+#define REG1D_TGSHLD 0x1f
+#define REG1DS_TGSHLD 0
+
+
+#define REG1E 0x1e
+#define REG1E_WDTIME 0xf0
+#define REG1ES_WDTIME 4
+#define REG1E_LINESEL 0x0f
+#define REG1ES_LINESEL 0
+
+#define REG21 0x21
+#define REG_STEPNO 0x21
+#define REG_FWDSTEP 0x22
+#define REG_BWDSTEP 0x23
+#define REG_FASTNO 0x24
+#define REG_LINCNT 0x25
+
+#define REG29 0x29
+#define REG2A 0x2a
+#define REG2B 0x2b
+#define REG_DPISET 0x2c
+#define REG2E 0x2e
+#define REG2F 0x2f
+
+#define REG_STRPIXEL 0x30
+#define REG_ENDPIXEL 0x32
+#define REG_DUMMY 0x34
+#define REG_MAXWD 0x35
+#define REG_LPERIOD 0x38
+#define REG_FEEDL 0x3d
+
+#define REG40 0x40
+#define REG40_DOCSNR 0x80
+#define REG40_ADFSNR 0x40
+#define REG40_COVERSNR 0x20
+#define REG40_CHKVER 0x10
+#define REG40_DOCJAM 0x08
+#define REG40_HISPDFLG 0x04
+#define REG40_MOTMFLG 0x02
+#define REG40_DATAENB 0x01
+
+#define REG41_PWRBIT 0x80
+#define REG41_BUFEMPTY 0x40
+#define REG41_FEEDFSH 0x20
+#define REG41_SCANFSH 0x10
+#define REG41_HOMESNR 0x08
+#define REG41_LAMPSTS 0x04
+#define REG41_FEBUSY 0x02
+#define REG41_MOTORENB 0x01
+
+#define REG58_VSMP 0xf8
+#define REG58S_VSMP 3
+#define REG58_VSMPW 0x07
+#define REG58S_VSMPW 0
+
+#define REG59_BSMP 0xf8
+#define REG59S_BSMP 3
+#define REG59_BSMPW 0x07
+#define REG59S_BSMPW 0
+
+#define REG5A_ADCLKINV 0x80
+#define REG5A_RLCSEL 0x40
+#define REG5A_CDSREF 0x30
+#define REG5AS_CDSREF 4
+#define REG5A_RLC 0x0f
+#define REG5AS_RLC 0
+
+#define REG5E 0x5e
+#define REG5E_DECSEL 0xe0
+#define REG5ES_DECSEL 5
+#define REG5E_STOPTIM 0x1f
+#define REG5ES_STOPTIM 0
+
+#define REG_FMOVDEC 0x5f
+
+#define REG60 0x60
+#define REG60_Z1MOD 0x1f
+#define REG61 0x61
+#define REG61_Z1MOD 0xff
+#define REG62 0x62
+#define REG62_Z1MOD 0xff
+
+#define REG63 0x63
+#define REG63_Z2MOD 0x1f
+#define REG64 0x64
+#define REG64_Z2MOD 0xff
+#define REG65 0x65
+#define REG65_Z2MOD 0xff
+
+#define REG67 0x67
+
+#define REG68 0x68
+
+#define REG67S_STEPSEL 6
+#define REG67_STEPSEL 0xc0
+#define REG67_FULLSTEP 0x00
+#define REG67_HALFSTEP 0x20
+#define REG67_EIGHTHSTEP 0x60
+#define REG67_16THSTEP 0x80
+
+#define REG68S_FSTPSEL 6
+#define REG68_FSTPSEL 0xc0
+#define REG68_FULLSTEP 0x00
+#define REG68_HALFSTEP 0x20
+#define REG68_EIGHTHSTEP 0x60
+#define REG68_16THSTEP 0x80
+
+#define REG_FSHDEC 0x69
+#define REG_FMOVNO 0x6a
+
+#define REG6B 0x6b
+#define REG6B_MULTFILM 0x80
+#define REG6B_GPOM13 0x40
+#define REG6B_GPOM12 0x20
+#define REG6B_GPOM11 0x10
+#define REG6B_GPOCK4 0x08
+#define REG6B_GPOCP 0x04
+#define REG6B_GPOLEDB 0x02
+#define REG6B_GPOADF 0x01
+
+#define REG6C 0x6c
+#define REG6C_GPIO16 0x80
+#define REG6C_GPIO15 0x40
+#define REG6C_GPIO14 0x20
+#define REG6C_GPIO13 0x10
+#define REG6C_GPIO12 0x08
+#define REG6C_GPIO11 0x04
+#define REG6C_GPIO10 0x02
+#define REG6C_GPIO9 0x01
+#define REG6C_GPIOH 0xff
+#define REG6C_GPIOL 0xff
+
+#define REG_Z1MOD 0x60
+#define REG_Z2MOD 0x63
+
+#define REG6D 0x6d
+#define REG6E 0x6e
+#define REG6F 0x6f
+
+#define REG_CK1MAP 0x74
+#define REG_CK3MAP 0x77
+#define REG_CK4MAP 0x7a
+
+#define REG7E 0x7e
+
+#define REG9D 0x9d
+#define REG9DS_STEPTIM 2
+
+#define REG87_LEDADD 0x04
+
+#define REGA6 0xa6
+#define REGA6_GPIO24 0x80
+#define REGA6_GPIO23 0x40
+#define REGA6_GPIO22 0x20
+#define REGA6_GPIO21 0x10
+#define REGA6_GPIO20 0x08
+#define REGA6_GPIO19 0x04
+#define REGA6_GPIO18 0x02
+#define REGA6_GPIO17 0x01
+#define REGA7 0xa7
+#define REGA7_GPOE24 0x80
+#define REGA7_GPOE23 0x40
+#define REGA7_GPOE22 0x20
+#define REGA7_GPOE21 0x10
+#define REGA7_GPOE20 0x08
+#define REGA7_GPOE19 0x04
+#define REGA7_GPOE18 0x02
+#define REGA7_GPOE17 0x01
+#define REGA8 0xa8
+#define REGA8_GPOE27 0x20
+#define REGA8_GPOE26 0x10
+#define REGA8_GPOE25 0x08
+#define REGA8_GPO27 0x04
+#define REGA8_GPO26 0x02
+#define REGA8_GPO25 0x01
+#define REGA9 0xa9
+#define REGA9_GPO33 0x20
+#define REGA9_GPO32 0x10
+#define REGA9_GPO31 0x08
+#define REGA9_GPO30 0x04
+#define REGA9_GPO29 0x02
+#define REGA9_GPO28 0x01
+
+#define SCAN_TABLE 0 /* table 1 at 0x4000 */
+#define BACKTRACK_TABLE 1 /* table 2 at 0x4800 */
+#define STOP_TABLE 2 /* table 3 at 0x5000 */
+#define FAST_TABLE 3 /* table 4 at 0x5800 */
+#define HOME_TABLE 4 /* table 5 at 0x6000 */
+
+#define SCAN_FLAG_SINGLE_LINE 0x001
+#define SCAN_FLAG_DISABLE_SHADING 0x002
+#define SCAN_FLAG_DISABLE_GAMMA 0x004
+#define SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE 0x008
+#define SCAN_FLAG_IGNORE_LINE_DISTANCE 0x010
+#define SCAN_FLAG_USE_OPTICAL_RES 0x020
+#define SCAN_FLAG_DISABLE_LAMP 0x040
+#define SCAN_FLAG_DYNAMIC_LINEART 0x080
+
+/**
+ * writable scanner registers */
+enum
+{
+ reg_0x01 = 0,
+ reg_0x02,
+ reg_0x03,
+ reg_0x04,
+ reg_0x05,
+ reg_0x06,
+ reg_0x08,
+ reg_0x09,
+ reg_0x0a,
+ reg_0x0b,
+ reg_0x0c,
+ reg_0x0f,
+ reg_0x10,
+ reg_0x11,
+ reg_0x12,
+ reg_0x13,
+ reg_0x14,
+ reg_0x15,
+ reg_0x16,
+ reg_0x17,
+ reg_0x18,
+ reg_0x19,
+ reg_0x1a,
+ reg_0x1b,
+ reg_0x1c,
+ reg_0x1d,
+ reg_0x1e,
+ reg_0x1f,
+ reg_0x20,
+ reg_0x21,
+ reg_0x22,
+ reg_0x23,
+ reg_0x24,
+ reg_0x25,
+ reg_0x26,
+ reg_0x27,
+ reg_0x28,
+ reg_0x2c,
+ reg_0x2d,
+ reg_0x2e,
+ reg_0x2f,
+ reg_0x30,
+ reg_0x31,
+ reg_0x32,
+ reg_0x33,
+ reg_0x34,
+ reg_0x35,
+ reg_0x36,
+ reg_0x37,
+ reg_0x38,
+ reg_0x39,
+ reg_0x3a,
+ reg_0x3b,
+ reg_0x3c,
+ reg_0x3d,
+ reg_0x3e,
+ reg_0x3f,
+ reg_0x51,
+ reg_0x52,
+ reg_0x53,
+ reg_0x54,
+ reg_0x55,
+ reg_0x56,
+ reg_0x57,
+ reg_0x58,
+ reg_0x59,
+ reg_0x5a,
+ reg_0x5d,
+ reg_0x5e,
+ reg_0x5f,
+ reg_0x60,
+ reg_0x61,
+ reg_0x62,
+ reg_0x63,
+ reg_0x64,
+ reg_0x65,
+ reg_0x67,
+ reg_0x68,
+ reg_0x69,
+ reg_0x6a,
+ reg_0x6b,
+ reg_0x70,
+ reg_0x71,
+ reg_0x72,
+ reg_0x73,
+ reg_0x74,
+ reg_0x75,
+ reg_0x76,
+ reg_0x77,
+ reg_0x78,
+ reg_0x79,
+ reg_0x7a,
+ reg_0x7b,
+ reg_0x7c,
+ reg_0x7d,
+ reg_0x7e,
+ reg_0x7f,
+ reg_0x80,
+ reg_0x81,
+ reg_0x82,
+ reg_0x83,
+ reg_0x84,
+ reg_0x85,
+ reg_0x86,
+ reg_0x87,
+ reg_0x88,
+ reg_0x89,
+ reg_0x8a,
+ reg_0x8b,
+ reg_0x8c,
+ reg_0x8d,
+ reg_0x8e,
+ reg_0x8f,
+ reg_0x90,
+ reg_0x91,
+ reg_0x92,
+ reg_0x93,
+ reg_0x94,
+ reg_0x95,
+ reg_0x96,
+ reg_0x97,
+ reg_0x98,
+ reg_0x99,
+ reg_0x9a,
+ reg_0x9b,
+ reg_0x9c,
+ reg_0x9d,
+ reg_0x9e,
+ reg_0xa0,
+ reg_0xa1,
+ reg_0xa2,
+ reg_0xa3,
+ reg_0xa4,
+ reg_0xa5,
+ reg_0xaa,
+ reg_0xab,
+ reg_0xac,
+ reg_0xad,
+ reg_0xae,
+ reg_0xaf,
+ GENESYS_GL843_MAX_REGS
+};
+
+#define SETREG(adr,val) {dev->reg[reg_##adr].address=adr;dev->reg[reg_##adr].value=val;}
+
+typedef struct
+{
+ SANE_Int gpo_type;
+ uint8_t ra6;
+ uint8_t ra7;
+ uint8_t ra8;
+ uint8_t ra9;
+} Gpio_layout;
+
+static Gpio_layout gpios[]={
+ /* G4050 */
+ {
+ GPO_G4050, 0x08, 0x1e, 0x3e, 0x06
+ },
+ /* KV-SS080 */
+ {
+ GPO_KVSS080, 0x06, 0x0f, 0x00, 0x08
+ },
+ /* 4400F */
+ {
+ GPO_CS4400F, 0x00, 0xff, 0x07, 0x00
+ },
+ /* 8400F */
+ {
+ GPO_CS8400F, 0x00, 0x03, 0x00, 0x02
+ },
+ /* end marker */
+ {
+ 0, 0, 0, 0, 0
+ },
+};
+
+/** @brief structure for sensor settings
+ * this structure describes the sensor settings to use for a given
+ * exposure.
+ */
+typedef struct {
+ int sensor_type; /**> sensor id */
+ int dpi; /**> maximum dpi for which data are valid */
+ int exposure; /**> exposure */
+ int ck1map; /**> CK1MAP */
+ int ck3map; /**> CK2MAP */
+ int ck4map; /**> CK3MAP */
+ int segcnt; /**> SEGCNT */
+ int tg0cnt; /**> TG0CNT */
+ int expdummy; /**> exposure dummy */
+ int expr; /**> initial red exposure */
+ int expg; /**> initial green exposure */
+ int expb; /**> initial blue exposure */
+ uint8_t reg0c; /**> register 0x0c value */
+ uint8_t reg70; /**> register 0x70 value */
+ uint8_t reg71; /**> register 0x71 value */
+ uint8_t reg9e; /**> register 0x9e value */
+ uint8_t regaa; /**> either undocumented or mapping to somewhere else */
+ uint8_t regs_0x10_0x1d[14];
+ uint8_t regs_0x52_0x5e[13];
+} Sensor_Profile;
+
+/**
+ * database of sensor profiles
+ */
+static Sensor_Profile xpa_sensors[]={
+ {CCD_G4050 , 600, 15624, 0x001c7f, 0x03ffff, 0x03ffff, 5168, 0, 0x2a, 0, 0, 0, 0x00, 0x00, 0x02, 0x00, 0x00,
+ {0x2c, 0x09, 0x22, 0xb8, 0x10, 0xf0, 0x33, 0x4c, 0x01, 0x2a, 0x30, 0x00, 0x00, 0x08} ,
+ {0x0e, 0x11, 0x02, 0x05, 0x08, 0x0b, 0x6b, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x6f},
+ },
+};
+static Sensor_Profile sensors[]={
+ /* 0c 70 71 9e aa*/
+ {CCD_KVSS080, 600, 8000, 0x000000, 0x00ffff, 0x03ffff, 5168, 0, 0x2a, 0, 0, 0, 0x00, 0x01, 0x03, 0x00, 0x00,
+ /* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x1c, 0x00, 0x2a, 0x2c, 0x00, 0x20, 0x04} ,
+ /* 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e */
+ {0x0c, 0x0f, 0x00, 0x03, 0x06, 0x09, 0x6b, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x23},
+ },
+
+ {CCD_G4050 , 600, 8016, 0x0001ff, 0x03ffff, 0x03ffff, 5168, 0, 0x2a, 0, 0, 0, 0x00, 0x00, 0x02, 0x00, 0x00,
+ {0x2c, 0x09, 0x22, 0xb8, 0x10, 0xf0, 0x33, 0x0c, 0x00, 0x2a, 0x30, 0x00, 0x00, 0x08} ,
+ {0x0b, 0x0e, 0x11, 0x02, 0x05, 0x08, 0x63, 0x00, 0x40, 0x00, 0x00, 0x00, 0x63},
+ },
+
+ {CCD_G4050 , 1200, 56064, 0x0fffff, 0x0001ff, 0x0001ff, 5168, 0, 0x2a, 0, 0, 0, 0x20, 0x08, 0x0c, 0xc0, 0x05,
+ {0x2c, 0x09, 0x22, 0xb8, 0x10, 0xf0, 0x3b, 0x0c, 0x10, 0x2a, 0x38, 0x10, 0x00, 0x08} ,
+ {0x02, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x1b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x63},
+ },
+
+ {CCD_G4050 , 2400, 56064, 0x0fffff, 0x000000, 0x000000, 5168, 0, 0x2a, 0, 0, 0, 0x20, 0x08, 0x0a, 0xc0, 0x05,
+ {0x2c, 0x09, 0x22, 0xb8, 0x10, 0xf0, 0x3b, 0x0c, 0x10, 0x2a, 0x38, 0x10, 0xc0, 0x08} ,
+ {0x02, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x1b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x63},
+ },
+
+ {CCD_G4050 , 4800, 42752, 0x0fffff, 0x000000, 0x000000, 5168, 0, 0x2a, 0, 0, 0, 0x21, 0x08, 0x0a, 0xc0, 0x07,
+ {0x2c, 0x09, 0x22, 0xb8, 0x10, 0xf0, 0x3b, 0x0c, 0x10, 0x2a, 0x38, 0x10, 0xc1, 0x08} ,
+ {0x02, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x1b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x63},
+ },
+ {CCD_CS4400F, 600, 11640, 0xf838, 0xfc00, 0x92a4, 5168, 0, 0x2a, 0x9c40, 0x9c40, 0x9c40, 0x00, 0x00, 0x02, 0x2d, 0x00,
+ {0x9c, 0x40, 0x9c, 0x40, 0x9c, 0x40, 0x13, 0x0a, 0x10, 0x2a, 0x30, 0x00, 0x00, 0x6b},
+ {0x0a, 0x0d, 0x00, 0x03, 0x06, 0x08, 0x5b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3f},
+ },
+ {CCD_CS8400F, 600, 7200, 0x0e3f, 0x0000, 0x1b6db, 5168, 0, 0x2a, 0x0, 0x0, 0x0, 0x00, 0x01, 0x02, 0x00, 0x00,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x0c, 0x13, 0x2a, 0x30, 0x00, 0x00, 0x84},
+ {0x0d, 0x10, 0x01, 0x04, 0x07, 0x0a, 0x6b, 0x00, 0x40, 0x00, 0x00, 0x00, 0x85},
+ }
+};
+
+static uint32_t kvss080[]={44444, 34188, 32520, 29630, 26666, 24242, 22222, 19048, 16666, 15686, 14814, 14034, 12402, 11110, 8888, 7618, 6666, 5926, 5228, 4678, 4172, 3682, 3336, 3074, 2866, 2702, 2566, 2450, 2352, 2266, 2188, 2118, 2056, 2002, 1950, 1904, 1860, 1820, 1784, 1748, 1716, 1684, 1656, 1628, 1600, 1576, 1552, 1528, 1506, 1486, 1466, 1446, 1428, 1410, 1394, 1376, 1360, 1346, 1330, 1316, 1302, 1288, 1276, 1264, 1250, 1238, 1228, 1216, 1206, 1194, 1184, 1174, 1164, 1154, 1146, 1136, 1128, 1120, 1110, 1102, 1094, 1088, 1080, 1072, 1064, 1058, 1050, 1044, 1038, 1030, 1024, 1018, 1012, 1006, 1000, 994, 988, 984, 978, 972, 968, 962, 958, 952, 948, 942, 938, 934, 928, 924, 920, 916, 912, 908, 904, 900, 896, 892, 888, 884, 882, 878, 874, 870, 868, 864, 860, 858, 854, 850, 848, 844, 842, 838, 836, 832, 830, 826, 824, 822, 820, 816, 814, 812, 808, 806, 804, 802, 800, 796, 794, 792, 790, 788, 786, 784, 782, 778, 776, 774, 772, 770, 768, 766, 764, 762, 760, 758, 756, 754, 752, 750, 750, 748, 746, 744, 742, 740, 738, 736, 734, 734, 732, 730, 728, 726, 724, 724, 722, 720, 718, 716, 716, 714, 712, 710, 710, 708, 706, 704, 704, 702, 700, 698, 698, 696, 694, 694, 692, 690, 690, 688, 686, 686, 684, 682, 682, 680, 678, 678, 676, 674, 674, 672, 672, 670, 668, 668, 666, 666, 664, 662, 662, 660, 660, 658, 656, 656, 654, 654, 652, 652, 650, 650, 648, 646, 646, 644, 644, 642, 642, 640, 640, 638, 638, 636, 636, 636, 634, 634, 632, 632, 630, 630, 628, 628, 626, 626, 624, 624, 624, 622, 622, 620, 620, 618, 618, 618, 616, 616, 614, 614, 612, 612, 612, 610, 610, 608, 608, 608, 606, 606, 606, 604, 604, 602, 602, 602, 600, 600, 600, 598, 598, 596, 596, 596, 594, 594, 594, 592, 592, 592, 590, 590, 590, 588, 588, 588, 586, 586, 586, 584, 584, 584, 582, 582, 582, 590, 590, 590, 588, 588, 588, 586, 586, 586, 584, 584, 584, 582, 582, 582, 580, 580, 580, 578, 578, 578, 576, 576, 576, 576, 574, 574, 574, 572, 572, 572, 570, 570, 570, 568, 568, 568, 568, 566, 566, 566, 564, 564, 564, 562, 562, 562, 562, 560, 560, 560, 558, 558, 558, 558, 556, 556, 556, 554, 554, 554, 552, 552, 552, 552, 550, 550, 550, 548, 548, 548, 548, 546, 546, 546, 546, 544, 544, 544, 542, 542, 542, 542, 540, 540, 540, 538, 538, 538, 538, 536, 536, 536, 536, 534, 534, 534, 534, 532, 532, 532, 530, 530, 530, 530, 528, 528, 528, 528, 526, 526, 526, 526, 524, 524, 524, 524, 522, 522, 522, 522, 520, 520, 520, 520, 518, 518, 518, 516, 516, 516, 516, 514, 514, 514, 514, 514, 512, 512, 512, 512, 510, 510, 510, 510, 508, 508, 508, 508, 506, 506, 506, 506, 504, 504, 504, 504, 502, 502, 502, 502, 500, 500, 500, 500, 0};
+static uint32_t g4050_fast[]={7842,5898,4384,4258,4152,4052,3956,3864,3786,3714,3632,3564,3498,3444,3384,3324,3276,3228,3174,3128,3086,3044,3002,2968,2930,2892,2860,2824,2794,2760,2732,2704,2676,2650,2618,2594,2568,2548,2524,2500,2478,2454,2436,2414,2392,2376,2354,2338,2318,2302,2282,2266,2252,2232,2218,2202,2188,2174,2160,2142,2128,2116,2102,2088,2076,2062,2054,2040,2028,2020,2014,2008,2004,2002,2002,2002,1946,1882,1826,1770,1716,1662,1612,1568,1526,1488,1454,1422,1390,1362,1336,1310,1288,1264,1242,1222,1204,1184,1166,1150,1134,1118,1104,1090,1076,1064,1050,1038,1026,1016,1004,994,984,972,964,954,944,936,928,920,910,902,896,888,880,874,866,860,854,848,840,834,828,822,816,812,806,800,796,790,784,780,776,770,766,760,756,752,748,744,740,736,732,728,724,720,716,712,708,704,702,698,694,690,688,684,682,678,674,672,668,666,662,660,656,654,650,648,646,644,640,638,636,632,630,628,624,622,620,618,616,614,610,608,606,604,602,600,598,596,594,592,590,588,586,584,582,580,578,576,574,572,570,568,566,564,564,562,560,558,556,554,552,552,550,548,546,546,544,542,540,538,538,536,534,532,532,530,528,528,526,524,522,522,520,518,518,516,514,514,512,512,510,508,508,506,504,504,502,502,500,498,498,496,496,494,494,492,490,490,488,488,486,486,484,484,482,480,480,478,478,476,476,474,474,472,472,470,470,468,468,468,466,466,464,464,462,462,460,460,458,458,456,456,456,454,454,452,452,450,450,450,448,448,446,446,444,444,444,442,442,440,440,440,438,438,438,436,436,434,434,434,432,432,432,430,430,428,428,428,426,426,426,424,424,424,422,422,422,420,420,420,418,418,418,416,416,416,414,414,414,412,412,412,410,410,410,408,408,408,406,406,406,404,404,404,404,402,402,402,400,400,400,400,398,398,398,396,396,396,396,394,394,394,392,392,392,392,390,390,390,388,388,388,388,386,386,386,386,384,384,384,384,382,382,382,382,380,380,380,380,378,378,378,378,376,376,376,376,376,374,374,374,374,374,372,372,372,372,372,370,370,370,370,370,368,368,368,368,368,366,366,366,366,366,364,364,364,364,364,364,362,362,362,362,362,360,360,360,360,360,360,358,358,358,358,358,358,356,356,356,356,356,356,354,354,354,354,354,352,352,352,352,352,352,350,350,350,350,350,350,350,348,348,348,348,348,348,346,346,346,346,346,346,344,344,344,344,344,344,344,342,342,342,342,342,342,340,340,340,340,340,340,340,338,338,338,338,338,338,338,336,336,336,336,336,336,336,334,334,334,334,334,334,334,332,332,332,332,332,332,332,332,330,330,330,330,330,330,330,328,328,328,328,328,328,328,328,326,326,326,326,326,326,326,324,324,324,324,324,324,324,324,322,322,322,322,322,322,322,322,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320, 0};
+static uint32_t g4050_high[]={28032,28032,28032,28032,28032,28032,28032,28032, 27668,27024,26479,25975,25402,24926,24465,24087,23667,23248,22912,22576,22198,21877,21583,21289,20996,20758,20492,20226,20002,19751,19541,19303,19107,18911,18715,18534,18310,18142,17960,17820,17652,17485,17331,17163,17037,16883,16729,16617,16463,16352,16212,16100,15960,15848,15750,15610,15512,15400,15302,15204,15107,14981,14883,14799,14701,14603,14519,14421,14365,14267,14183,14127,14085,14043,14016,14002,14002,14002,13610,13162,12771,12379,12001,11624,11274,10966,10672,10407,10169,9945,9721,9525,9344,9162,9008,8840,8686,8546,8420,8280,8155,8043,7931,7819,7721,7623,7525,7441,7343,7259,7175,7105,7021,6952,6882,6798,6742,6672,6602,6546,6490,6434,6364,6308,6266,6210,6154,6112,6056,6014,5972,5930,5874,5833,5791,5749,5707,5679,5637,5595,5567,5525,5483,5455,5427,5385,5357,5315,5287,5259,5231,5203,5175,5147,5119,5091,5063,5035,5007,4979,4951,4923,4909,4881,4853,4825,4811,4783,4769,4741,4713,4699,4672,4658,4630,4616,4588,4574,4546,4532,4518,4504,4476,4462,4448,4420,4406,4392,4364,4350,4336,4322,4308,4294,4266,4252,4238,4224,4210,4196,4182,4168,4154,4140,4126,4112,4098,4084,4070,4056,4042,4028,4014,4000,3986,3972,3958,3944,3944,3930,3916,3902,3888,3874,3860,3860,3846,3832,3818,3818,3804,3790,3776,3762,3762,3748,3734,3720,3720,3706,3692,3692,3678,3664,3650,3650,3636,3622,3622,3608,3594,3594,3580,3580,3566,3552,3552,3538,3524,3524,3510,3510,3497,3483,3483,3469,3469,3455,3455,3441,3427,3427,3413,3413,3399,3399,3385,3385,3371,3357,3357,3343,3343,3329,3329,3315,3315,3301,3301,3287,3287,3273,3273,3273,3259,3259,3245,3245,3231,3231,3217,3217,3203,3203,3189,3189,3189,3175,3175,3161,3161,3147,3147,3147,3133,3133,3119,3119,3105,3105,3105,3091,3091,3077,3077,3077,3063,3063,3063,3049,3049,3035,3035,3035,3021,3021,3021,3007,3007,2993,2993,2993,2979,2979,2979,2965,2965,2965,2951,2951,2951,2937,2937,2937,2923,2923,2923,2909,2909,2909,2895,2895,2895,2881,2881,2881,2867,2867,2867,2853,2853,2853,2839,2839,2839,2825,2825,2825,2825,2811,2811,2811,2797,2797,2797,2797,2783,2783,2783,2769,2769,2769,2769,2755,2755,2755,2741,2741,2741,2741,2727,2727,2727,2713,2713,2713,2713,2699,2699,2699,2699,2685,2685,2685,2685,2671,2671,2671,2671,2657,2657,2657,2657,2643,2643,2643,2643,2629,2629,2629,2629,2629,2615,2615,2615,2615,2615,2601,2601,2601,2601,2601,2587,2587,2587,2587,2587,2573,2573,2573,2573,2573,2559,2559,2559,2559,2559,2545,2545,2545,2545,2545,2545,2531,2531,2531,2531,2531,2517,2517,2517,2517,2517,2517,2503,2503,2503,2503,2503,2503,2489,2489,2489,2489,2489,2489,2475,2475,2475,2475,2475,2461,2461,2461,2461,2461,2461,2447,2447,2447,2447,2447,2447,2447,2433,2433,2433,2433,2433,2433,2419,2419,2419,2419,2419,2419,2405,2405,2405,2405,2405,2405,2405,2391,2391,2391,2391,2391,2391,2377,2377,2377,2377,2377,2377,2377,2363,2363,2363,2363,2363,2363,2363,2349,2349,2349,2349,2349,2349,2349,2336,2336,2336,2336,2336,2336,2336,2322,2322,2322,2322,2322,2322,2322,2322,2308,2308,2308,2308,2308,2308,2308,2294,2294,2294,2294,2294,2294,2294,2294,2280,2280,2280,2280,2280,2280,2280,2266,2266,2266,2266,2266,2266,2266,2266,2252,2252,2252,2252,2252,2252,2252,2252,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238, 0};
+static uint32_t g4050_max[]={42752,42752,42752,42752,42752,42752,42752,42752, 41824,31456,23381,22709,22144,21610,21098,20608,20192,19808,19370,19008,18656,18368,18048,17728,17472,17216,16928,16682,16458,16234,16010,15829,15626,15424,15253,15061,14901,14720,14570,14421,14272,14133,13962,13834,13696,13589,13461,13333,13216,13088,12992,12874,12757,12672,12554,12469,12362,12277,12170,12085,12010,11904,11829,11744,11669,11594,11520,11424,11349,11285,11210,11136,11072,10997,10954,10880,10816,10773,10741,10709,10688,10677,10677,10677,10378,10037,9738,9440,9152,8864,8597,8362,8138,7936,7754,7584,7413,7264,7125,6986,6869,6741,6624,6517,6421,6314,6218,6133,6048,5962,5888,5813,5738,5674,5600,5536,5472,5418,5354,5301,5248,5184,5141,5088,5034,4992,4949,4906,4853,4810,4778,4736,4693,4661,4618,4586,4554,4522,4480,4448,4416,4384,4352,4330,4298,4266,4245,4213,4181,4160,4138,4106,4085,4053,4032,4010,3989,3968,3946,3925,3904,3882,3861,3840,3818,3797,3776,3754,3744,3722,3701,3680,3669,3648,3637,3616,3594,3584,3562,3552,3530,3520,3498,3488,3466,3456,3445,3434,3413,3402,3392,3370,3360,3349,3328,3317,3306,3296,3285,3274,3253,3242,3232,3221,3210,3200,3189,3178,3168,3157,3146,3136,3125,3114,3104,3093,3082,3072,3061,3050,3040,3029,3018,3008,3008,2997,2986,2976,2965,2954,2944,2944,2933,2922,2912,2912,2901,2890,2880,2869,2869,2858,2848,2837,2837,2826,2816,2816,2805,2794,2784,2784,2773,2762,2762,2752,2741,2741,2730,2730,2720,2709,2709,2698,2688,2688,2677,2677,2666,2656,2656,2645,2645,2634,2634,2624,2613,2613,2602,2602,2592,2592,2581,2581,2570,2560,2560,2549,2549,2538,2538,2528,2528,2517,2517,2506,2506,2496,2496,2496,2485,2485,2474,2474,2464,2464,2453,2453,2442,2442,2432,2432,2432,2421,2421,2410,2410,2400,2400,2400,2389,2389,2378,2378,2368,2368,2368,2357,2357,2346,2346,2346,2336,2336,2336,2325,2325,2314,2314,2314,2304,2304,2304,2293,2293,2282,2282,2282,2272,2272,2272,2261,2261,2261,2250,2250,2250,2240,2240,2240,2229,2229,2229,2218,2218,2218,2208,2208,2208,2197,2197,2197,2186,2186,2186,2176,2176,2176,2165,2165,2165,2154,2154,2154,2154,2144,2144,2144,2133,2133,2133,2133,2122,2122,2122,2112,2112,2112,2112,2101,2101,2101,2090,2090,2090,2090,2080,2080,2080,2069,2069,2069,2069,2058,2058,2058,2058,2048,2048,2048,2048,2037,2037,2037,2037,2026,2026,2026,2026,2016,2016,2016,2016,2005,2005,2005,2005,2005,1994,1994,1994,1994,1994,1984,1984,1984,1984,1984,1973,1973,1973,1973,1973,1962,1962,1962,1962,1962,1952,1952,1952,1952,1952,1941,1941,1941,1941,1941,1941,1930,1930,1930,1930,1930,1920,1920,1920,1920,1920,1920,1909,1909,1909,1909,1909,1909,1898,1898,1898,1898,1898,1898,1888,1888,1888,1888,1888,1877,1877,1877,1877,1877,1877,1866,1866,1866,1866,1866,1866,1866,1856,1856,1856,1856,1856,1856,1845,1845,1845,1845,1845,1845,1834,1834,1834,1834,1834,1834,1834,1824,1824,1824,1824,1824,1824,1813,1813,1813,1813,1813,1813,1813,1802,1802,1802,1802,1802,1802,1802,1792,1792,1792,1792,1792,1792,1792,1781,1781,1781,1781,1781,1781,1781,1770,1770,1770,1770,1770,1770,1770,1770,1760,1760,1760,1760,1760,1760,1760,1749,1749,1749,1749,1749,1749,1749,1749,1738,1738,1738,1738,1738,1738,1738,1728,1728,1728,1728,1728,1728,1728,1728,1717,1717,1717,1717,1717,1717,1717,1717,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,0};
+static uint32_t g4050_xpa[]={9422,5978,4736,4028,3560,3220,2914,2756,2588,2448,2328,2224,2132,2052,1978,1914,1854,1800,1752,1706,1664,1626,1588,1554,1522,1492,1464,1438,1412,1388,1366,1344,1324,1304,1284,1268,1250,1232,1218,1202,1188,1172,1160,1146,1134,1120,1110,1098,1086,1076,1066,1056,1046,1036,1026,1018,1008,1000,992,984,976,968,960,952,946,938,932,924,918,912,906,898,892,888,882,876,870,864,860,854,848,844,838,834,828,824,820,814,810,806,802,798,794,790,786,782,778,774,770,766,762,758,754,752,748,744,740,738,734,730,728,724,722,718,716,712,710,706,704,700,698,696,692,690,686,684,682,678,676,674,672,668,666,664,662,660,656,654,652,650,648,646,644,642,638,636,634,632,630,628,626,624,622,620,618,616,614,612,612,610,608,606,604,602,600,598,596,594,594,592,590,588,586,584,584,582,580,578,576,576,574,572,570,570,568,566,564,564,562,560,560,558,556,554,554,552,550,550,548,546,546,544,542,542,540,540,538,536,536,534,532,532,530,530,528,526,526,524,524,522,522,520,518,518,516,516,514,514,512,512,510,508,508,506,506,504,504,502,502,500,500,498,498,496,496,494,494,492,492,492,490,490,488,488,486,486,484,484,482,482,480,480,480,478,478,476,476,474,474,474,472,472,470,470,468,468,468,466,466,464,464,464,462,462,462,460,460,458,458,458,456,456,454,454,454,452,452,452,450,450,450,448,448,446,446,446,444,444,444,442,442,442,440,440,440,438,438,438,436,436,436,434,434,434,432,432,432,430,430,430,430,428,428,428,426,426,426,424,424,424,424,422,422,422,420,420,420,418,418,418,418,416,416,416,414,414,414,414,412,412,412,412,410,410,410,408,408,408,408,406,406,406,406,404,404,404,404,402,402,402,402,400,400,400,400,398,398,398,398,396,396,396,396,394,394,394,394,392,392,392,392,392,390,390,390,390,388,388,388,388,386,386,386,386,386,384,384,384,384,384,382,382,382,382,380,380,380,380,380,378,378,378,378,378,376,376,376,376,376,374,374,374,374,374,372,372,372,372,372,370,370,370,370,370,368,368,368,368,368,366,366,366,366,366,364,364,364,364,364,364,362,362,362,362,362,360,360,360,360,360,360,358,358,358,358,358,358,356,356,356,356,356,354,354,354,354,354,354,352,352,352,352,352,352,350,350,350,350,350,350,350,348,348,348,348,348,348,346,346,346,346,346,346,344,344,344,344,344,344,344,342,342,342,342,342,342,340,340,340,340,340,340,340,338,338,338,338,338,338,338,336,336,336,336,336,336,336,334,334,334,334,334,334,334,332,332,332,332,332,332,332,330,330,330,330,330,330,330,330,328,328,328,328,328,328,328,326,326,326,326,326,326,326,326,324,324,324,324,324,324,324,324,322,322,322,322,322,322,322,322,320,320,320,320,320,320,320,320,318,318,318,318,318,318,318,318,318,316,316,316,316,316,316,316,316,314,314,314,314,314,314,314,314,314,312,312,312,312,312,312,312,312,312,310,310,310,310,310,310,310,310,310,308,308,308,308,308,308,308,308,308,306,306,306,306,306,306,306,306,306,306,304,304,304,304,304,304,304,304,304,302,302,302,302,302,302,302,302,302,302,300,300,300,300,300,300,300,300,300,300,298,298,298,298,298,298,298,298,298,298,298,296,296,296,296,296,296,296,296,296,296,294,294,294,294,294,294,294,294,294,294,294,292,292,292,292,292,292,292,292,292,292,292,290,290,290,290,290,290,290,290,290,290,290,288,288,288,288,288,288,288,288,288,288,288,288,286,286,286,286,286,286,286,286,286,286,286,286,284,284,284,284,284,284,284,284,284,284,284,284,282,282,282,282,282,282,282,282,282,282,282,282,280,280,280,280,280,280,280,280,280,280,280,280,280,278,278,278,278,278,278,278,278,278,278,278,278,278,276,276,276,276,276,276,276,276,276,276,276,276,276,274,274,274,274,274,274,274,274,274,274,274,274,274,274,272,272,272,272,272,272,272,272,272,272,272,272,272,272,270,270,270,270,270,270,270,270,270,270,270,270,270,270,268,268,268,268,268,268,268,268,268,268,268,268,268,268,268,266,266,266,266,266,266,266,266,266,266,266,266,266,266,266,264,264,264,264,264,264,264,264,264,264,264,264,264,264,264,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,260,260,260,260,260,260,260,260,260,260,260,260,260,260,260,260,258,258,258,258,258,258,258,258,258,258,258,258,258,258,258,258,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,0};
+static uint32_t cs4400f_fast[]={49152, 49152, 31144, 23652, 19538, 16822, 14908, 13442, 12288, 11356, 10590, 9922, 9362, 8886, 8456, 8064, 7728, 7418, 7148, 6882, 6664, 6446, 6252, 6060, 5890, 5740, 5586, 5450, 5322, 5198, 5080, 4968, 4868, 4766, 4674, 4584, 4500, 4418, 4338, 4262, 4194, 4122, 4058, 3996, 3932, 3874, 3816, 3766, 3712, 3662, 3610, 3566, 3518, 3474, 3430, 3388, 3350, 3310, 3272, 3236, 3200, 3164, 3130, 3096, 3062, 3032, 3000, 2972, 2942, 2914, 2884, 2858, 2832, 2806, 2780, 2756, 2732, 2708, 2686, 2662, 2640, 2618, 2596, 2576, 2554, 2536, 2516, 2496, 2478, 2458, 2440, 2422, 2404, 2388, 2370, 2354, 2338, 2320, 2306, 2290, 2276, 2258, 2244, 2230, 2216, 2202, 2188, 2174, 2162, 2148, 2134, 2122, 2108, 2096, 2084, 2072, 2060, 2048, 2036, 2026, 2014, 2002, 1992, 1982, 1970, 1960, 1950, 1940, 1928, 1920, 1908, 1900, 1890, 1880, 1872, 1862, 1852, 1844, 1834, 1826, 1818, 1808, 1800, 1792, 1784, 1776, 1768, 1760, 1752, 1742, 1736, 1728, 1720, 1712, 1704, 1698, 1690, 1684, 1676, 1670, 1662, 1656, 1648, 1642, 1634, 1628, 1622, 1616, 1608, 1602, 1596, 1590, 1584, 1578, 1572, 1566, 1560, 1554, 1548, 1542, 1536, 1532, 1526, 1520, 1514, 1508, 1504, 1498, 1492, 1488, 1482, 1478, 1472, 1466, 1462, 1456, 1452, 1446, 1442, 1438, 1432, 1428, 1424, 1418, 1414, 1410, 1404, 1400, 1396, 1390, 1386, 1382, 1378, 1374, 1370, 1364, 1360, 1356, 1352, 1348, 1344, 1340, 1336, 1332, 1328, 1324, 1320, 1316, 1312, 1308, 1304, 1302, 1298, 1294, 1290, 1286, 1282, 1278, 1276, 1272, 1268, 1264, 1262, 1258, 1254, 1250, 1248, 1244, 1240, 1238, 1234, 1230, 1228, 1224, 1222, 1218, 1214, 1212, 1208, 1206, 1202, 1200, 1196, 1194, 1190, 1186, 1184, 1182, 1178, 1176, 1172, 1170, 1166, 1164, 1162, 1158, 1156, 1152, 1150, 1148, 1144, 1142, 1138, 1136, 1134, 1130, 1128, 1126, 1122, 1120, 1118, 1116, 1112, 1110, 1108, 1106, 1102, 1100, 1098, 1096, 1092, 1090, 1088, 1086, 1082, 1080, 1078, 1076, 1074, 1072, 1068, 1066, 1064, 1062, 1060, 1058, 1056, 1054, 1052, 1048, 1046, 1044, 1042, 1040, 1038, 1036, 1034, 1032, 1030, 1028, 1026, 1024, 1022, 1020, 1018, 1016, 1014, 1012, 1010, 1008, 1006, 1004, 1002, 1000, 998, 996, 994, 992, 990, 988, 986, 984, 982, 980, 978, 976, 974, 972, 972, 970, 968, 966, 964, 962, 960, 958, 956, 956, 954, 952, 950, 948, 946, 944, 944, 942, 940, 938, 936, 934, 934, 932, 930, 928, 926, 926, 924, 922, 920, 918, 918, 916, 914, 912, 912, 910, 908, 906, 904, 904, 902, 900, 898, 898, 896, 894, 892, 892, 890, 888, 888, 886, 884, 882, 882, 880, 878, 878, 876, 874, 874, 872, 870, 868, 868, 866, 864, 864, 862, 860, 860, 858, 856, 856, 854, 852, 852, 850, 848, 848, 846, 846, 844, 842, 842, 840, 838, 838, 836, 834, 834, 832, 832, 830, 828, 828, 826, 826, 824, 822, 822, 820, 820, 818, 816, 816, 814, 814, 812, 812, 810, 808, 808, 806, 806, 804, 802, 802, 800, 800, 798, 798, 796, 796, 794, 792, 792, 790, 790, 788, 788, 786, 786, 784, 784, 782, 782, 780, 780, 778, 778, 776, 774, 774, 772, 772, 770, 770, 768, 768, 766, 766, 764, 764, 762, 762, 760, 760, 758, 758, 758, 756, 756, 754, 754, 752, 752, 750, 750, 748, 748, 746, 746, 744, 744, 742, 742, 740, 740, 738, 738, 738, 736, 736, 734, 734, 732, 732, 730, 730, 730, 728, 728, 726, 726, 724, 724, 722, 722, 722, 720, 720, 718, 718, 718, 716, 716, 714, 714, 712, 712, 712, 710, 710, 708, 708, 708, 706, 706, 704, 704, 702, 702, 702, 700, 700, 698, 698, 698, 696, 696, 694, 694, 694, 692, 692, 692, 690, 690, 688, 688, 688, 686, 686, 684, 684, 684, 682, 682, 682, 680, 680, 680, 678, 678, 676, 676, 676, 674, 674, 674, 672, 672, 670, 670, 670, 668, 668, 668, 666, 666, 666, 664, 664, 664, 662, 662, 660, 660, 660, 658, 658, 658, 656, 656, 656, 654, 654, 654, 652, 652, 652, 650, 650, 650, 648, 648, 648, 646, 646, 646, 644, 644, 644, 642, 642, 642, 640, 640, 640, 640, 638, 638, 638, 636, 636, 636, 634, 634, 634, 632, 632, 632, 630, 630, 630, 630, 628, 628, 628, 626, 626, 626, 624, 624, 624, 624, 622, 622, 622, 620, 620, 620, 618, 618, 618, 618, 616, 616, 616, 614, 614, 614, 614, 612, 612, 612, 610, 610, 610, 610, 608, 608, 608, 606, 606, 606, 606, 604, 604, 604, 604, 602, 602, 602, 600, 600, 600, 600, 598, 598, 598, 598, 596, 596, 596, 594, 594, 594, 594, 592, 592, 592, 592, 590, 590, 590, 590, 588, 588, 588, 586, 586, 586, 586, 584, 584, 584, 584, 582, 582, 582, 582, 580, 580, 580, 580, 578, 578, 578, 578, 576, 576, 576, 576, 574, 574, 574, 574, 574, 572, 572, 572, 572, 570, 570, 570, 570, 568, 568, 568, 568, 566, 566, 566, 566, 564, 564, 564, 564, 564, 562, 562, 562, 562, 560, 560, 560, 560, 558, 558, 558, 558, 558, 556, 556, 556, 556, 554, 554, 554, 554, 554, 552, 552, 552, 552, 550, 550, 550, 550, 550, 548, 548, 548, 548, 546, 546, 546, 546, 546, 544, 544, 544, 544, 544, 542, 542, 542, 542, 540, 540, 540, 540, 540, 538, 538, 538, 538, 538, 536, 536, 536, 536, 536, 534, 534, 534, 534, 534, 532, 532, 532, 532, 532, 530, 530, 530, 530, 530, 528, 528, 528, 528, 528, 526, 526, 526, 526, 526, 524, 524, 524, 524, 524, 522, 522, 522, 522, 522, 520, 520, 520, 520, 520, 520, 518, 518, 518, 518, 518, 516, 516, 516, 516, 516, 514, 514, 514, 514, 514, 514, 512, 512, 512, 512, 512, 510, 510, 510, 510, 510, 510, 508, 508, 508, 508, 508, 506, 506, 506, 506, 506, 506, 504, 504, 504, 504, 504, 502, 502, 502, 502, 502, 502, 500, 500, 500, 500, 500, 500, 498, 498, 498, 498, 498, 498, 496, 496, 496, 496, 496, 496, 494, 494, 494, 494, 494, 494, 492, 492, 492, 492, 492, 492, 490, 490, 490, 490, 490, 490, 488, 488, 488, 488, 488, 488, 486, 486, 486, 486, 486, 486, 484, 484, 484, 484, 484, 484, 484, 0, 0, 0, 0, 0};
+static uint32_t cs8400f_fast[]={8743, 8205, 7017, 6201, 4938, 4016, 3371, 2966, 2682, 2469, 2296, 2159, 2041, 1942, 1857, 1782, 1716, 1656, 1602, 1554, 1510, 1470, 1432, 1398, 1366, 1336, 1309, 1282, 1258, 1235, 1213, 1193, 1173, 1154, 1137, 1120, 1104, 1089, 1074, 1060, 1047, 1034, 1022, 1010, 998, 987, 976, 966, 956, 946, 937, 928, 919, 911, 902, 894, 887, 879, 872, 864, 858, 851, 844, 838, 832, 825, 819, 814, 808, 802, 797, 792, 786, 781, 776, 771, 766, 762, 757, 753, 748, 744, 740, 736, 731, 728, 724, 720, 716, 712, 709, 705, 701, 698, 695, 691, 688, 685, 682, 679, 675, 672, 669, 666, 664, 661, 658, 655, 652, 650, 647, 644, 642, 639, 637, 634, 632, 629, 627, 625, 622, 620, 618, 616, 613, 611, 609, 607, 605, 603, 601, 599, 597, 595, 593, 591, 589, 587, 585, 583, 581, 580, 578, 576, 574, 573, 571, 569, 567, 566, 564, 563, 561, 559, 558, 556, 555, 553, 552, 550, 549, 547, 546, 544, 543, 542, 540, 539, 537, 536, 535, 533, 532, 531, 529, 528, 527, 526, 524, 523, 522, 521, 519, 518, 517, 516, 515, 514, 512, 511, 510, 509, 508, 507, 506, 505, 504, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 488, 487, 486, 485, 484, 483, 482, 481, 480, 479, 478, 477, 477, 476, 475, 474, 473, 472, 472, 471, 470, 469, 468, 467, 467, 466, 465, 464, 464, 463, 462, 461, 460, 460, 459, 458, 457, 457, 456, 455, 454, 454, 453, 452, 452, 451, 450, 449, 449, 448, 448, 447, 446, 446, 445, 444, 444, 443, 442, 442, 441, 440, 440, 439, 438, 438, 437, 437, 436, 435, 435, 434, 434, 433, 432, 432, 431, 431, 430, 429, 429, 428, 428, 427, 427, 426, 425, 425, 424, 424, 423, 423, 422, 422, 421, 421, 420, 420, 419, 419, 418, 417, 417, 416, 416, 416, 415, 414, 414, 414, 413, 412, 412, 412, 411, 411, 410, 410, 409, 409, 408, 408, 407, 407, 406, 406, 406, 405, 405, 404, 404, 403, 403, 402, 402, 402, 401, 401, 400, 400, 399, 399, 398, 398, 398, 397, 397, 396, 396, 396, 395, 395, 394, 394, 394, 393, 393, 392, 392, 392, 391, 391, 391, 390, 390, 389, 389, 389, 388, 388, 387, 387, 387, 386, 386, 385, 385, 385, 384, 384, 384, 383, 383, 383, 382, 382, 382, 381, 381, 381, 380, 380, 380, 379, 379, 379, 378, 378, 378, 377, 377, 377, 376, 376, 376, 375, 375, 375, 374, 374, 374, 373, 373, 373, 372, 372, 372, 371, 371, 371, 370, 370, 370, 370, 369, 369, 369, 368, 368, 368, 368, 367, 367, 367, 367, 366, 366, 366, 365, 365, 365, 365, 364, 364, 364, 363, 363, 363, 363, 362, 362, 362, 362, 361, 361, 361, 360, 360, 360, 360, 359, 359, 359, 358, 358, 358, 358, 357, 357, 357, 356, 356, 356, 356, 355, 355, 355, 355, 354, 354, 354, 354, 354, 353, 353, 353, 353, 352, 352, 352, 352, 352, 351, 351, 351, 351, 350, 350, 350, 350, 350, 349, 349, 349, 349, 348, 348, 348, 348, 348, 347, 347, 347, 347, 346, 346, 346, 346, 346, 345, 345, 345, 345, 344, 344, 344, 344, 344, 343, 343, 343, 343, 342, 342, 342, 342, 342, 341, 341, 341, 341, 340, 340, 340, 340, 340, 339, 339, 339, 339, 338, 338, 338, 338, 338, 337, 337, 337, 337, 337, 337, 336, 336, 336, 336, 336, 336, 335, 335, 335, 335, 335, 335, 334, 334, 334, 334, 334, 334, 333, 333, 333, 333, 333, 333, 332, 332, 332, 332, 332, 332, 331, 331, 331, 331, 331, 331, 330, 330, 330, 330, 330, 330, 329, 329, 329, 329, 329, 329, 328, 328, 328, 328, 328, 328, 327, 327, 327, 327, 327, 327, 326, 326, 326, 326, 326, 326, 325, 325, 325, 325, 325, 325, 324, 324, 324, 324, 324, 324, 323, 323, 323, 323, 323, 323, 322, 322, 322, 322, 322, 322, 321, 321, 321, 321, 321, 321, 320, 320, 320, 320, 320, 320, 319, 319, 319, 319, 319, 319, 318, 318, 318, 318, 318, 318, 317, 317, 317, 317, 317, 317, 316, 316, 316, 316, 316, 316, 315, 315, 315, 315, 315, 315, 314, 314, 314, 314, 314, 314, 313, 313, 313, 313, 313, 313, 312, 312, 312, 312, 312, 312, 311, 311, 311, 311, 311, 311, 310, 310, 310, 310, 310, 310, 309, 309, 309, 309, 309, 309, 308, 308, 308, 308, 308, 308, 307, 307, 307, 307, 307, 307, 306, 306, 306, 306, 306, 306, 305, 305, 305, 305, 305, 305, 304, 304, 304, 304, 304, 304, 303, 303, 303, 303, 303, 303, 302, 302, 302, 302, 302, 302, 302, 301, 301, 301, 301, 301, 301, 301, 301, 301, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 0, 0, 0, 0, 0 };
+
+/**
+ * database of motor profiles
+ */
+
+/* *INDENT-OFF* */
+static Motor_Profile gl843_motors[]={
+ /* KV-SS080 */
+ {MOTOR_KVSS080, 8000, 1, kvss080},
+ /* G4010/G4050/CS4400F */
+ {MOTOR_G4050, 8016, 1, g4050_fast},
+ {MOTOR_G4050, 11640, 1, cs4400f_fast},
+ {MOTOR_G4050, 15624, 1, g4050_xpa},
+ {MOTOR_G4050, 42752, 2, g4050_max},
+ {MOTOR_G4050, 56064, 1, g4050_high},
+ /* CS8400F */
+ {MOTOR_CS8400F, 7200, 0, cs8400f_fast},
+ {0, 0, 0, NULL},
+};
+/* *INDENT-ON* */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl846.c b/backend/genesys_gl846.c
new file mode 100644
index 0000000..5e1f0f4
--- /dev/null
+++ b/backend/genesys_gl846.c
@@ -0,0 +1,3704 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2012-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ *
+ * This file handles GL846 and GL845 ASICs since they are really close to each other.
+ */
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_gl846
+
+#include "genesys_gl846.h"
+
+/****************************************************************************
+ Low level function
+ ****************************************************************************/
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+
+/** @brief read scanned data
+ * Read in 0xeff0 maximum sized blocks. This read is done in 2
+ * parts if not multple of 512. First read is rounded to a multiple of 512 bytes, last read fetches the
+ * remainder. Read addr is always 0x10000000 with the memory layout setup.
+ * @param dev device to read data from
+ * @param addr address within ASIC memory space, unused but kept for API
+ * @param data pointer where to store the read data
+ * @param len size to read
+ */
+static SANE_Status
+gl846_bulk_read_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size, target, read, done;
+ uint8_t outdata[8];
+ uint8_t *buffer;
+
+ DBG (DBG_io, "gl846_bulk_read_data: requesting %lu bytes at addr=0x%02x\n", (u_long) len, addr);
+
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ target = len;
+ buffer = data;
+
+ /* loop until computed data size is read */
+ while (target)
+ {
+ if (target > 0xeff0)
+ {
+ size = 0xeff0;
+ }
+ else
+ {
+ size = target;
+ }
+
+ /* hard coded 0x10000000 addr */
+ outdata[0] = 0;
+ outdata[1] = 0;
+ outdata[2] = 0;
+ outdata[3] = 0x10;
+
+ /* data size to transfer */
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, 0x00, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s failed while writing command: %s\n",
+ __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ /* blocks must be multiple of 512 but not last block */
+ read = size;
+ if (read >= 512)
+ {
+ read /= 512;
+ read *= 512;
+ }
+
+ DBG (DBG_io2,
+ "gl846_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) read);
+ status = sanei_usb_read_bulk (dev->dn, buffer, &read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ done=read;
+ DBG (DBG_io2, "gl846_bulk_read_data: %lu bytes of data read\n", (u_long) done);
+
+ /* read less than 512 bytes remainder */
+ if (read < size)
+ {
+ read = size - read;
+ DBG (DBG_io2,
+ "gl846_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) read);
+ status = sanei_usb_read_bulk (dev->dn, buffer+done, &read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ done=read;
+ DBG (DBG_io2, "gl846_bulk_read_data: %lu bytes of data read\n", (u_long) done);
+ }
+
+ DBG (DBG_io2, "%s: read %lu bytes, %lu remaining\n", __FUNCTION__,
+ (u_long) size, (u_long) (target - size));
+
+ target -= size;
+ buffer += size;
+ }
+
+ if (DBG_LEVEL >= DBG_data && dev->binary!=NULL)
+ {
+ fwrite(data, len, 1, dev->binary);
+ }
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/****************************************************************************
+ Mid level functions
+ ****************************************************************************/
+
+static SANE_Bool
+gl846_get_fast_feed_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG02);
+ if (r && (r->value & REG02_FASTFED))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl846_get_filter_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_FILTER))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl846_get_lineart_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_LINEART))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl846_get_bitset_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_BITSET))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl846_get_gain4_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x06);
+ if (r && (r->value & REG06_GAIN4))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl846_test_buffer_empty_bit (SANE_Byte val)
+{
+ if (val & REG41_BUFEMPTY)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl846_test_motor_flag_bit (SANE_Byte val)
+{
+ if (val & REG41_MOTORENB)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/**
+ * compute the step multiplier used
+ */
+static int
+gl846_get_step_multiplier (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+ int value = 1;
+
+ r = sanei_genesys_get_address (regs, 0x9d);
+ if (r != NULL)
+ {
+ value = (r->value & 0x0f)>>1;
+ value = 1 << value;
+ }
+ DBG (DBG_io, "%s: step multiplier is %d\n", __FUNCTION__, value);
+ return value;
+}
+
+/** @brief sensor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at a specific dpihw. Use LiDE 110 table by default.
+ * @param sensor_type sensor id
+ * @param dpi hardware dpi for the scan
+ * @return a pointer to a Sensor_Profile struct
+ */
+static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi)
+{
+ unsigned int i;
+ int idx;
+
+ i=0;
+ idx=-1;
+ while(i<sizeof(sensors)/sizeof(Sensor_Profile))
+ {
+ /* exact match */
+ if(sensors[i].sensor_type==sensor_type && sensors[i].dpi==dpi)
+ {
+ return &(sensors[i]);
+ }
+
+ /* closest match */
+ if(sensors[i].sensor_type==sensor_type)
+ {
+ if(idx<0)
+ {
+ idx=i;
+ }
+ else
+ {
+ if(sensors[i].dpi>=dpi
+ && sensors[i].dpi<sensors[idx].dpi)
+ {
+ idx=i;
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default sensor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(sensors[idx]);
+}
+
+/**@brief compute exposure to use
+ * compute the sensor exposure based on target resolution
+ */
+static int gl846_compute_exposure(Genesys_Device *dev, int xres)
+{
+ Sensor_Profile *sensor;
+
+ sensor=get_sensor_profile(dev->model->ccd_type, xres);
+ return sensor->exposure;
+}
+
+
+/** @brief sensor specific settings
+*/
+static void
+gl846_setup_sensor (Genesys_Device * dev, Genesys_Register_Set * regs, int dpi)
+{
+ Genesys_Register_Set *r;
+ Sensor_Profile *sensor;
+ int dpihw, i;
+ uint16_t exp;
+
+ DBGSTART;
+ dpihw=sanei_genesys_compute_dpihw(dev,dpi);
+
+ for (i = 0x06; i < 0x0e; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ if (r)
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+
+ for (i = 0; i < 9; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ if (r)
+ r->value = dev->sensor.regs_0x52_0x5e[i];
+ }
+
+ /* set EXPDUMMY and CKxMAP */
+ dpihw=sanei_genesys_compute_dpihw(dev,dpi);
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw);
+
+ sanei_genesys_set_reg_from_set(regs,REG_EXPDMY,(uint8_t)((sensor->expdummy) & 0xff));
+
+ /* if no calibration has been done, set default values for exposures */
+ exp=dev->sensor.regs_0x10_0x1d[0]*256+dev->sensor.regs_0x10_0x1d[1];
+ if(exp==0)
+ {
+ exp=sensor->expr;
+ }
+ sanei_genesys_set_double(regs,REG_EXPR,exp);
+
+ exp=dev->sensor.regs_0x10_0x1d[2]*256+dev->sensor.regs_0x10_0x1d[3];
+ if(exp==0)
+ {
+ exp=sensor->expg;
+ }
+ sanei_genesys_set_double(regs,REG_EXPG,exp);
+
+ exp=dev->sensor.regs_0x10_0x1d[4]*256+dev->sensor.regs_0x10_0x1d[5];
+ if(exp==0)
+ {
+ exp=sensor->expb;
+ }
+ sanei_genesys_set_double(regs,REG_EXPB,exp);
+
+ sanei_genesys_set_triple(regs,REG_CK1MAP,sensor->ck1map);
+ sanei_genesys_set_triple(regs,REG_CK3MAP,sensor->ck3map);
+ sanei_genesys_set_triple(regs,REG_CK4MAP,sensor->ck4map);
+
+ /* order of the sub-segments */
+ dev->order=sensor->order;
+
+ r = sanei_genesys_get_address (regs, 0x17);
+ r->value = sensor->r17;
+
+ DBGCOMPLETED;
+}
+
+
+/* returns the max register bulk size */
+static int
+gl846_bulk_full_size (void)
+{
+ return GENESYS_GL846_MAX_REGS;
+}
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl846_init_registers (Genesys_Device * dev)
+{
+ DBGSTART;
+
+ memset (dev->reg, 0,
+ GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ SETREG (0x01,0x60);
+ SETREG (0x02,0x38);
+ SETREG (0x03,0x03);
+ SETREG (0x04,0x22);
+ SETREG (0x05,0x60);
+ SETREG (0x06,0x10);
+ SETREG (0x08,0x60);
+ SETREG (0x09,0x00);
+ SETREG (0x0a,0x00);
+ SETREG (0x0b,0x8b);
+ SETREG (0x0c,0x00);
+ SETREG (0x0d,0x00);
+ SETREG (0x10,0x00);
+ SETREG (0x11,0x00);
+ SETREG (0x12,0x00);
+ SETREG (0x13,0x00);
+ SETREG (0x14,0x00);
+ SETREG (0x15,0x00);
+ SETREG (0x16,0xbb);
+ SETREG (0x17,0x13);
+ SETREG (0x18,0x10);
+ SETREG (0x19,0x2a);
+ SETREG (0x1a,0x34);
+ SETREG (0x1b,0x00);
+ SETREG (0x1c,0x20);
+ SETREG (0x1d,0x06);
+ SETREG (0x1e,0xf0);
+ SETREG (0x1f,0x01);
+ SETREG (0x20,0x03);
+ SETREG (0x21,0x10);
+ SETREG (0x22,0x60);
+ SETREG (0x23,0x60);
+ SETREG (0x24,0x60);
+ SETREG (0x25,0x00);
+ SETREG (0x26,0x00);
+ SETREG (0x27,0x00);
+ SETREG (0x2c,0x00);
+ SETREG (0x2d,0x00);
+ SETREG (0x2e,0x80);
+ SETREG (0x2f,0x80);
+ SETREG (0x30,0x00);
+ SETREG (0x31,0x00);
+ SETREG (0x32,0x00);
+ SETREG (0x33,0x00);
+ SETREG (0x34,0x1f);
+ SETREG (0x35,0x00);
+ SETREG (0x36,0x40);
+ SETREG (0x37,0x00);
+ SETREG (0x38,0x2a);
+ SETREG (0x39,0xf8);
+ SETREG (0x3d,0x00);
+ SETREG (0x3e,0x00);
+ SETREG (0x3f,0x01);
+ SETREG (0x52,0x02);
+ SETREG (0x53,0x04);
+ SETREG (0x54,0x06);
+ SETREG (0x55,0x08);
+ SETREG (0x56,0x0a);
+ SETREG (0x57,0x00);
+ SETREG (0x58,0x59);
+ SETREG (0x59,0x31);
+ SETREG (0x5a,0x40);
+ SETREG (0x5e,0x1f);
+ SETREG (0x5f,0x01);
+ SETREG (0x60,0x00);
+ SETREG (0x61,0x00);
+ SETREG (0x62,0x00);
+ SETREG (0x63,0x00);
+ SETREG (0x64,0x00);
+ SETREG (0x65,0x00);
+ SETREG (0x67,0x7f);
+ SETREG (0x68,0x7f);
+ SETREG (0x69,0x01);
+ SETREG (0x6a,0x01);
+ SETREG (0x70,0x01);
+ SETREG (0x71,0x00);
+ SETREG (0x72,0x02);
+ SETREG (0x73,0x01);
+ SETREG (0x74,0x00);
+ SETREG (0x75,0x00);
+ SETREG (0x76,0x00);
+ SETREG (0x77,0x00);
+ SETREG (0x78,0x00);
+ SETREG (0x79,0x3f);
+ SETREG (0x7a,0x00);
+ SETREG (0x7b,0x09);
+ SETREG (0x7c,0x99);
+ SETREG (0x7d,0x20);
+ SETREG (0x7f,0x05);
+ SETREG (0x80,0x4f);
+ SETREG (0x87,0x02);
+ SETREG (0x94,0xff);
+ SETREG (0x9d,0x04);
+ SETREG (0x9e,0x00);
+ SETREG (0xa1,0xe0);
+ SETREG (0xa2,0x1f);
+ SETREG (0xab,0xc0);
+ SETREG (0xbb,0x00);
+ SETREG (0xbc,0x0f);
+ SETREG (0xdb,0xff);
+ SETREG (0xfe,0x08);
+ SETREG (0xff,0x02);
+ SETREG (0x98,0x20);
+ SETREG (0x99,0x00);
+ SETREG (0x9a,0x90);
+ SETREG (0x9b,0x00);
+ SETREG (0xf8,0x05);
+
+ /* fine tune upon device description */
+ dev->reg[reg_0x05].value &= ~REG05_DPIHW;
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* initalize calibration reg */
+ memcpy (dev->calib_reg, dev->reg,
+ GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ DBGCOMPLETED;
+}
+
+/**@brief send slope table for motor movement
+ * Send slope_table in machine byte order
+ * @param dev device to send slope table
+ * @param table_nr index of the slope table in ASIC memory
+ * Must be in the [0-4] range.
+ * @param slope_table pointer to 16 bit values array of the slope table
+ * @param steps number of elements in the slope table
+ */
+static SANE_Status
+gl846_send_slope_table (Genesys_Device * dev, int table_nr,
+ uint16_t * slope_table, int steps)
+{
+ SANE_Status status;
+ uint8_t *table;
+ int i;
+ char msg[10000];
+
+ DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __FUNCTION__,
+ table_nr, steps);
+
+ /* sanity check */
+ if(table_nr<0 || table_nr>4)
+ {
+ DBG (DBG_error, "%s: invalid table number %d!\n", __FUNCTION__, table_nr);
+ return SANE_STATUS_INVAL;
+ }
+
+ table = (uint8_t *) malloc (steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sprintf (msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++)
+ {
+ sprintf (msg+strlen(msg), "%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __FUNCTION__, msg);
+ }
+
+ /* slope table addresses are fixed */
+ status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, 0x10000000 + 0x4000 * table_nr, steps * 2, table);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: write to AHB failed writing slope table %d (%s)\n",
+ __FUNCTION__, table_nr, sane_strstatus (status));
+ }
+
+ free (table);
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * Set register values of Analog Device type frontend
+ * */
+static SANE_Status
+gl846_set_adi_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ uint16_t val;
+ uint8_t val8;
+
+ DBGSTART;
+
+ /* wait for FE to be ready */
+ status = sanei_genesys_get_status (dev, &val8);
+ while (val8 & REG41_FEBUSY);
+ {
+ usleep (10000);
+ status = sanei_genesys_get_status (dev, &val8);
+ };
+
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "%s(): setting DAC %u\n", __FUNCTION__, dev->model->dac_type);
+
+ /* sets to default values */
+ sanei_genesys_init_fe (dev);
+ }
+
+ /* write them to analog frontend */
+ val = dev->frontend.reg[0];
+ status = sanei_genesys_fe_write_data (dev, 0x00, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to write reg0: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ val = dev->frontend.reg[1];
+ status = sanei_genesys_fe_write_data (dev, 0x01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to write reg1: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.gain[i];
+ status = sanei_genesys_fe_write_data (dev, 0x02 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write gain %d: %s\n", __FUNCTION__, i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.offset[i];
+ status = sanei_genesys_fe_write_data (dev, 0x05 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write offset %d: %s\n", __FUNCTION__, i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+static SANE_Status
+gl846_homsnr_gpio(Genesys_Device *dev)
+{
+uint8_t val;
+SANE_Status status=SANE_STATUS_GOOD;
+
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ val |= 0x41;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+
+ return status;
+}
+
+/* Set values of analog frontend */
+static SANE_Status
+gl846_set_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "gl846_set_fe (%s)\n",
+ set == AFE_INIT ? "init" : set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?");
+
+ /* route to specific analog frontend setup */
+ switch (dev->reg[reg_0x04].value & REG04_FESET)
+ {
+ case 0x02: /* ADI FE */
+ status = gl846_set_adi_fe(dev, set);
+ break;
+ default:
+ DBG(DBG_proc, "gl846_set_fe(): unsupported frontend type %d\n",
+ dev->reg[reg_0x04].value & REG04_FESET);
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** @brief set up motor related register for scan
+ */
+static SANE_Status
+gl846_init_motor_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int scan_exposure_time,
+ float scan_yres,
+ int scan_step_type,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ int scan_power_mode,
+ unsigned int flags)
+{
+ SANE_Status status;
+ int use_fast_fed;
+ unsigned int fast_dpi;
+ uint16_t scan_table[SLOPE_TABLE_SIZE];
+ uint16_t fast_table[SLOPE_TABLE_SIZE];
+ int scan_steps, fast_steps, factor;
+ unsigned int feedl, dist;
+ Genesys_Register_Set *r;
+ uint32_t z1, z2;
+ unsigned int min_restep = 0x20;
+ uint8_t val;
+ int fast_step_type;
+ unsigned int ccdlmt,tgtime;
+
+ DBGSTART;
+ DBG (DBG_proc, "gl846_init_motor_regs_scan : scan_exposure_time=%d, "
+ "scan_yres=%g, scan_step_type=%d, scan_lines=%d, scan_dummy=%d, "
+ "feed_steps=%d, scan_power_mode=%d, flags=%x\n",
+ scan_exposure_time,
+ scan_yres,
+ scan_step_type,
+ scan_lines, scan_dummy, feed_steps, scan_power_mode, flags);
+
+ /* get step multiplier */
+ factor = gl846_get_step_multiplier (reg);
+
+ use_fast_fed=0;
+ /* no fast fed since feed works well */
+ if(dev->settings.yres==4444 && feed_steps>100
+ && ((flags & MOTOR_FLAG_FEED)==0))
+ {
+ use_fast_fed=1;
+ }
+ DBG (DBG_io, "%s: use_fast_fed=%d\n", __FUNCTION__, use_fast_fed);
+
+ sanei_genesys_set_triple(reg, REG_LINCNT, scan_lines);
+ DBG (DBG_io, "%s: lincnt=%d\n", __FUNCTION__, scan_lines);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address (reg, REG02);
+ r->value = 0x00;
+ r->value |= REG02_MTRPWR;
+
+ if (use_fast_fed)
+ r->value |= REG02_FASTFED;
+ else
+ r->value &= ~REG02_FASTFED;
+
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME)
+ r->value |= REG02_AGOHOME | REG02_NOTHOME;
+
+ if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ ||(scan_yres>=dev->sensor.optical_res))
+ {
+ r->value |= REG02_ACDCDIS;
+ }
+
+ /* scan and backtracking slope table */
+ sanei_genesys_slope_table(scan_table,
+ &scan_steps,
+ scan_yres,
+ scan_exposure_time,
+ dev->motor.base_ydpi,
+ scan_step_type,
+ factor,
+ dev->model->motor_type,
+ gl846_motors);
+ RIE(gl846_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps*factor));
+ RIE(gl846_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps*factor));
+
+ /* fast table */
+ fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
+ fast_step_type=scan_step_type;
+ if(scan_step_type>=2)
+ {
+ fast_step_type=2;
+ }
+
+ sanei_genesys_slope_table(fast_table,
+ &fast_steps,
+ fast_dpi,
+ scan_exposure_time,
+ dev->motor.base_ydpi,
+ fast_step_type,
+ factor,
+ dev->model->motor_type,
+ gl846_motors);
+
+ /* manual override of high start value */
+ fast_table[0]=fast_table[1];
+
+ RIE(gl846_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps*factor));
+ RIE(gl846_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps*factor));
+ RIE(gl846_send_slope_table (dev, HOME_TABLE, fast_table, fast_steps*factor));
+
+ /* correct move distance by acceleration and deceleration amounts */
+ feedl=feed_steps;
+ if (use_fast_fed)
+ {
+ feedl<<=fast_step_type;
+ dist=(scan_steps+2*fast_steps)*factor;
+ /* TODO read and decode REGAB */
+ r = sanei_genesys_get_address (reg, 0x5e);
+ dist += (r->value & 31);
+ /* FEDCNT */
+ r = sanei_genesys_get_address (reg, REG_FEDCNT);
+ dist += r->value;
+ }
+ else
+ {
+ feedl<<=scan_step_type;
+ dist=scan_steps*factor;
+ if (flags & MOTOR_FLAG_FEED)
+ dist *=2;
+ }
+ DBG (DBG_io2, "%s: scan steps=%d\n", __FUNCTION__, scan_steps);
+ DBG (DBG_io2, "%s: acceleration distance=%d\n", __FUNCTION__, dist);
+
+ /* check for overflow */
+ if(dist<feedl)
+ feedl -= dist;
+ else
+ feedl = 0;
+
+ sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
+ DBG (DBG_io ,"%s: feedl=%d\n",__FUNCTION__,feedl);
+
+ r = sanei_genesys_get_address (reg, REG0C);
+ ccdlmt=(r->value & REG0C_CCDLMT)+1;
+
+ r = sanei_genesys_get_address (reg, REG1C);
+ tgtime=1<<(r->value & REG1C_TGTIME);
+
+ /* hi res motor speed GPIO */
+ /*
+ RIE (sanei_genesys_read_register (dev, REG6C, &effective));
+ */
+
+ /* if quarter step, bipolar Vref2 */
+ /* XXX STEF XXX GPIO
+ if (scan_step_type > 1)
+ {
+ if (scan_step_type < 3)
+ {
+ val = effective & ~REG6C_GPIO13;
+ }
+ else
+ {
+ val = effective | REG6C_GPIO13;
+ }
+ }
+ else
+ {
+ val = effective;
+ }
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ */
+
+ /* effective scan */
+ /*
+ RIE (sanei_genesys_read_register (dev, REG6C, &effective));
+ val = effective | REG6C_GPIO10;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ */
+
+ if(dev->model->gpo_type==GPO_IMG101)
+ {
+ if(scan_yres==sanei_genesys_compute_dpihw(dev,scan_yres))
+ {
+ val=1;
+ }
+ else
+ {
+ val=0;
+ }
+ RIE (sanei_genesys_write_register (dev, REG7E, val));
+ }
+
+ min_restep=scan_steps/2-1;
+ if (min_restep < 1)
+ min_restep = 1;
+ r = sanei_genesys_get_address (reg, REG_FWDSTEP);
+ r->value = min_restep;
+ r = sanei_genesys_get_address (reg, REG_BWDSTEP);
+ r->value = min_restep;
+
+ sanei_genesys_calculate_zmode2(use_fast_fed,
+ scan_exposure_time*ccdlmt*tgtime,
+ scan_table,
+ scan_steps*factor,
+ feedl,
+ min_restep*factor,
+ &z1,
+ &z2);
+
+ DBG (DBG_info, "gl846_init_motor_regs_scan: z1 = %d\n", z1);
+ sanei_genesys_set_triple(reg, REG60, z1 | (scan_step_type << (16+REG60S_STEPSEL)));
+
+ DBG (DBG_info, "gl846_init_motor_regs_scan: z2 = %d\n", z2);
+ sanei_genesys_set_triple(reg, REG63, z2 | (scan_step_type << (16+REG63S_FSTPSEL)));
+
+ r = sanei_genesys_get_address (reg, 0x1e);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ r = sanei_genesys_get_address (reg, REG67);
+ r->value = 0x7f;
+
+ r = sanei_genesys_get_address (reg, REG68);
+ r->value = 0x7f;
+
+ r = sanei_genesys_get_address (reg, REG_STEPNO);
+ r->value = scan_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FASTNO);
+ r->value = scan_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FSHDEC);
+ r->value = scan_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FMOVNO);
+ r->value = fast_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FMOVDEC);
+ r->value = fast_steps;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief set up registers related to sensor
+ * Set up the following registers
+ 0x01
+ 0x03
+ 0x10-0x015 R/G/B exposures
+ 0x19 EXPDMY
+ 0x2e BWHI
+ 0x2f BWLO
+ 0x04
+ 0x87
+ 0x05
+ 0x2c,0x2d DPISET
+ 0x30,0x31 STRPIXEL
+ 0x32,0x33 ENDPIXEL
+ 0x35,0x36,0x37 MAXWD [25:2] (>>2)
+ 0x38,0x39 LPERIOD
+ 0x34 DUMMY
+ */
+static SANE_Status
+gl846_init_optical_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int exposure_time,
+ int used_res,
+ unsigned int start,
+ unsigned int pixels,
+ int channels,
+ int depth,
+ SANE_Bool half_ccd, int color_filter, int flags)
+{
+ unsigned int words_per_line;
+ unsigned int startx, endx, used_pixels;
+ unsigned int dpiset, dpihw,segnb,cksel,factor;
+ unsigned int bytes;
+ Genesys_Register_Set *r;
+ SANE_Status status;
+ Sensor_Profile *sensor;
+
+ DBG (DBG_proc, "gl846_init_optical_regs_scan : exposure_time=%d, "
+ "used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
+ "half_ccd=%d, flags=%x\n", exposure_time,
+ used_res, start, pixels, channels, depth, half_ccd, flags);
+
+ /* resolution is divided according to CKSEL */
+ r = sanei_genesys_get_address (reg, REG18);
+ cksel= (r->value & REG18_CKSEL)+1;
+ DBG (DBG_io2, "%s: cksel=%d\n", __FUNCTION__, cksel);
+
+ /* to manage high resolution device while keeping good
+ * low resolution scanning speed, we make hardware dpi vary */
+ dpihw=sanei_genesys_compute_dpihw(dev, used_res * cksel);
+ factor=dev->sensor.optical_res/dpihw;
+ DBG (DBG_io2, "%s: dpihw=%d (factor=%d)\n", __FUNCTION__, dpihw, factor);
+
+ /* sensor parameters */
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw);
+ gl846_setup_sensor (dev, reg, dpihw);
+ dpiset = used_res * cksel;
+
+ /* start and end coordinate in optical dpi coordinates */
+ startx = start/cksel+dev->sensor.CCD_start_xoffset;
+ used_pixels=pixels/cksel;
+
+ /* end of sensor window */
+ endx = startx + used_pixels;
+
+ /* sensors are built from 600 dpi segments for LiDE 100/200
+ * and 1200 dpi for the 700F */
+ if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR)
+ {
+ segnb=dpihw/600;
+ }
+ else
+ {
+ segnb=1;
+ }
+
+ /* compute pixel coordinate in the given dpihw space,
+ * taking segments into account */
+ startx/=factor*segnb;
+ endx/=factor*segnb;
+ dev->len=endx-startx;
+ dev->dist=0;
+ dev->skip=0;
+
+ /* in cas of multi-segments sensor, we have to add the witdh
+ * of the sensor crossed by the scan area */
+ if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR && segnb>1)
+ {
+ dev->dist = sensor->segcnt;
+ }
+
+ /* use a segcnt rounded to next even number */
+ endx += ((dev->dist+1)&0xfffe)*(segnb-1);
+ used_pixels=endx-startx;
+
+ status = gl846_set_fe (dev, AFE_SET);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_init_optical_regs_scan: failed to set frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* enable shading */
+ r = sanei_genesys_get_address (reg, REG01);
+ r->value &= ~REG01_SCAN;
+ r->value |= REG01_SHDAREA;
+ if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG01_DVDSET;
+ }
+ else
+ {
+ r->value |= REG01_DVDSET;
+ }
+
+ r = sanei_genesys_get_address (reg, REG03);
+ r->value &= ~REG03_AVEENB;
+
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value &= ~REG03_LAMPPWR;
+ else
+ r->value |= REG03_LAMPPWR;
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, 0x2e);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, 0x2f);
+ r->value = dev->settings.threshold;
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, REG04);
+ switch (depth)
+ {
+ case 1:
+ r->value &= ~REG04_BITSET;
+ r->value |= REG04_LINEART;
+ break;
+ case 8:
+ r->value &= ~(REG04_LINEART | REG04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG04_LINEART;
+ r->value |= REG04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG04_FILTER | REG04_AFEMOD);
+ if (channels == 1)
+ {
+ switch (color_filter)
+ {
+ case 0:
+ r->value |= 0x24; /* red filter */
+ break;
+ case 2:
+ r->value |= 0x2c; /* blue filter */
+ break;
+ default:
+ r->value |= 0x28; /* green filter */
+ break;
+ }
+ }
+ else
+ r->value |= 0x20; /* mono */
+
+ /* register 05 */
+ r = sanei_genesys_get_address (reg, REG05);
+
+ /* set up dpihw */
+ r->value &= ~REG05_DPIHW;
+ switch(dpihw)
+ {
+ case 600:
+ r->value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ r->value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ r->value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ r->value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* enable gamma tables */
+ if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
+ r->value &= ~REG05_GMMENB;
+ else
+ r->value |= REG05_GMMENB;
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ /* we set up LEDADD only when asked */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ r = sanei_genesys_get_address (reg, 0x87);
+ r->value &= ~REG87_LEDADD;
+ if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG87_LEDADD;
+ }
+ /* RGB weighting
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG01_TRUEGRAY;
+ if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG01_TRUEGRAY;
+ }*/
+ }
+
+ /* words(16bit) before gamma, conversion to 8 bit or lineart*/
+ words_per_line = (used_pixels * dpiset) / dpihw;
+ bytes=depth/8;
+ if (depth == 1)
+ {
+ words_per_line = (words_per_line+7)/8 ;
+ dev->len = (dev->len >> 3) + ((dev->len & 7) ? 1 : 0);
+ dev->dist = (dev->dist >> 3) + ((dev->dist & 7) ? 1 : 0);
+ }
+ else
+ {
+ words_per_line *= bytes;
+ dev->dist *= bytes;
+ dev->len *= bytes;
+ }
+
+ dev->bpl = words_per_line;
+ dev->cur=0;
+ dev->segnb=segnb;
+ dev->line_interp = 0;
+
+ sanei_genesys_set_double(reg,REG_DPISET,dpiset);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __FUNCTION__, dpiset);
+
+ sanei_genesys_set_double(reg,REG_STRPIXEL,startx);
+ sanei_genesys_set_double(reg,REG_ENDPIXEL,endx);
+ DBG (DBG_io2, "%s: startx=%d\n", __FUNCTION__, startx);
+ DBG (DBG_io2, "%s: endx =%d\n", __FUNCTION__, endx);
+
+ DBG (DBG_io2, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+ DBG (DBG_io2, "%s: pixels =%d\n", __FUNCTION__, pixels);
+ DBG (DBG_io2, "%s: depth =%d\n", __FUNCTION__, depth);
+ DBG (DBG_io2, "%s: dev->bpl =%lu\n", __FUNCTION__, (unsigned long)dev->bpl);
+ DBG (DBG_io2, "%s: dev->len =%lu\n", __FUNCTION__, (unsigned long)dev->len);
+ DBG (DBG_io2, "%s: dev->dist =%lu\n", __FUNCTION__, (unsigned long)dev->dist);
+ DBG (DBG_io2, "%s: dev->segnb =%lu\n", __FUNCTION__, (unsigned long)dev->segnb);
+
+ words_per_line *= channels;
+ dev->wpl = words_per_line;
+
+ if(dev->oe_buffer.buffer!=NULL)
+ {
+ sanei_genesys_buffer_free (&(dev->oe_buffer));
+ }
+ RIE (sanei_genesys_buffer_alloc (&(dev->oe_buffer), dev->wpl));
+
+ /* MAXWD is expressed in 4 words unit */
+ sanei_genesys_set_triple(reg, REG_MAXWD, (words_per_line >> 2));
+ DBG (DBG_io2, "%s: words_per_line used=%d\n", __FUNCTION__, words_per_line);
+
+ sanei_genesys_set_double(reg, REG_LPERIOD, exposure_time);
+ DBG (DBG_io2, "%s: exposure_time used=%d\n", __FUNCTION__, exposure_time);
+
+ r = sanei_genesys_get_address (reg, 0x34);
+ r->value = dev->sensor.dummy_pixel;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl846_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags)
+{
+ int used_res;
+ int start, used_pixels;
+ int bytes_per_line;
+ int move;
+ unsigned int lincnt;
+ unsigned int oflags; /**> optical flags */
+ unsigned int mflags; /**> motor flags */
+ int exposure_time;
+ int stagger;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+ int scan_step_type = 1;
+ int scan_power_mode = 0;
+ int max_shift;
+ size_t requested_buffer_size, read_buffer_size;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl846_init_scan_regs settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g/%g\n"
+ "Depth/Channels: %u/%u\n"
+ "Flags : %x\n\n",
+ xres, yres, lines, pixels, startx, starty, depth, channels, flags);
+
+ /* we may have 2 domains for ccd: xres below or above half ccd max dpi */
+ if (dev->sensor.optical_res < 2 * xres ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ half_ccd = SANE_FALSE;
+ }
+ else
+ {
+ half_ccd = SANE_TRUE;
+ }
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 2;
+
+ /* stagger */
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl846_init_scan_regs : stagger=%d lines\n", stagger);
+
+ /* used_res */
+ if (flags & SCAN_FLAG_USE_OPTICAL_RES)
+ {
+ used_res = optical_res;
+ }
+ else
+ {
+ /* resolution is choosen from a list */
+ used_res = xres;
+ }
+
+ /* compute scan parameters values */
+ /* pixels are allways given at full optical resolution */
+ /* use detected left margin and fixed value */
+ /* start */
+ /* add x coordinates */
+ start = startx;
+
+ if (stagger > 0)
+ start |= 1;
+
+ /* compute correct pixels number */
+ /* pixels */
+ used_pixels = (pixels * optical_res) / xres;
+
+ /* round up pixels number if needed */
+ if (used_pixels * xres < pixels * optical_res)
+ used_pixels++;
+
+ dummy = 3-channels;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres * channels;
+ else
+ slope_dpi = yres;
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ exposure_time = gl846_compute_exposure (dev, used_res);
+ scan_step_type = sanei_genesys_compute_step_type(gl846_motors, dev->model->motor_type, exposure_time);
+
+ DBG (DBG_info, "gl846_init_scan_regs : exposure_time=%d pixels\n", exposure_time);
+ DBG (DBG_info, "gl846_init_scan_regs : scan_step_type=%d\n", scan_step_type);
+
+/*** optical parameters ***/
+ /* in case of dynamic lineart, we use an internal 8 bit gray scan
+ * to generate 1 lineart data */
+ if ((flags & SCAN_FLAG_DYNAMIC_LINEART) && (dev->settings.scan_mode == SCAN_MODE_LINEART))
+ {
+ depth = 8;
+ }
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ oflags = 0;
+ if(flags & SCAN_FLAG_DISABLE_SHADING)
+ oflags |= OPTICAL_FLAG_DISABLE_SHADING;
+ if(flags & SCAN_FLAG_DISABLE_GAMMA)
+ oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
+ if(flags & SCAN_FLAG_DISABLE_LAMP)
+ oflags |= OPTICAL_FLAG_DISABLE_LAMP;
+
+ if (dev->model->is_cis && dev->settings.true_gray)
+ {
+ oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
+ }
+
+ status = gl846_init_optical_regs_scan (dev,
+ reg,
+ exposure_time,
+ used_res,
+ start,
+ used_pixels,
+ channels,
+ depth,
+ half_ccd,
+ color_filter,
+ oflags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+/*** motor parameters ***/
+
+ /* max_shift */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,flags);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ /* add tl_y to base movement */
+ move = starty;
+ DBG (DBG_info, "gl846_init_scan_regs: move=%d steps\n", move);
+
+ mflags=0;
+ if(flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ mflags |= MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
+ if(flags & SCAN_FLAG_FEEDING)
+ mflags |= MOTOR_FLAG_FEED;
+
+ status = gl846_init_motor_regs_scan (dev,
+ reg,
+ exposure_time,
+ slope_dpi,
+ scan_step_type,
+ dev->model->is_cis ? lincnt *
+ channels : lincnt, dummy, move,
+ scan_power_mode,
+ mflags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ /*** prepares data reordering ***/
+
+/* words_per_line */
+ bytes_per_line = (used_pixels * used_res) / optical_res;
+ bytes_per_line = (bytes_per_line * channels * depth) / 8;
+
+ requested_buffer_size = 8 * bytes_per_line;
+ /* we must use a round number of bytes_per_line */
+ /* XXX STEF XXX
+ if (requested_buffer_size > BULKIN_MAXSIZE)
+ requested_buffer_size =
+ (BULKIN_MAXSIZE / bytes_per_line) * bytes_per_line;
+ */
+
+ read_buffer_size =
+ 2 * requested_buffer_size +
+ ((max_shift + stagger) * used_pixels * channels * depth) / 8;
+
+ RIE (sanei_genesys_buffer_free (&(dev->read_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->read_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->lines_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->lines_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->shrink_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->shrink_buffer),
+ requested_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->out_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->out_buffer),
+ (8 * dev->settings.pixels * channels *
+ depth) / 8));
+
+
+ dev->read_bytes_left = bytes_per_line * lincnt;
+
+ DBG (DBG_info,
+ "gl846_init_scan_regs: physical bytes to read = %lu\n",
+ (u_long) dev->read_bytes_left);
+ dev->read_active = SANE_TRUE;
+
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+/* TODO: should this be done elsewhere? */
+ /* scan bytes to send to the frontend */
+ /* theory :
+ target_size =
+ (dev->settings.pixels * dev->settings.lines * channels * depth) / 8;
+ but it suffers from integer overflow so we do the following:
+
+ 1 bit color images store color data byte-wise, eg byte 0 contains
+ 8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
+ 8 bits of blue.
+ This does not fix the overflow, though.
+ 644mp*16 = 10gp, leading to an overflow
+ -- pierre
+ */
+
+ dev->total_bytes_read = 0;
+ if (depth == 1)
+ dev->total_bytes_to_read =
+ ((dev->settings.pixels * dev->settings.lines) / 8 +
+ (((dev->settings.pixels * dev->settings.lines) % 8) ? 1 : 0)) *
+ channels;
+ else
+ dev->total_bytes_to_read =
+ dev->settings.pixels * dev->settings.lines * channels * (depth / 8);
+
+ DBG (DBG_info, "gl846_init_scan_regs: total bytes to send = %lu\n",
+ (u_long) dev->total_bytes_to_read);
+/* END TODO */
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl846_calculate_current_setup (Genesys_Device * dev)
+{
+ int channels;
+ int depth;
+ int start;
+
+ float xres; /*dpi */
+ float yres; /*dpi */
+ float startx; /*optical_res, from dummy_pixel+1 */
+ float pixels;
+ float lines;
+
+ int used_res;
+ int used_pixels;
+ unsigned int lincnt;
+ int exposure_time;
+ int stagger;
+
+ int slope_dpi;
+ int dummy = 0;
+ int max_shift;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+
+ DBG (DBG_info,
+ "gl846_calculate_current_setup settings:\n"
+ "Resolution: %uDPI\n"
+ "Lines : %u\n"
+ "PPL : %u\n"
+ "Startpos : %.3f/%.3f\n"
+ "Scan mode : %d\n\n",
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ /* channels */
+ if (dev->settings.scan_mode == 4) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == 0)
+ depth = 1;
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+
+ xres = dev->settings.xres; /*dpi */
+ yres = dev->settings.yres; /*dpi */
+ startx = start; /*optical_res, from dummy_pixel+1 */
+ pixels = dev->settings.pixels;
+ lines = dev->settings.lines;
+
+ DBG (DBG_info,
+ "gl846_calculate_current_setup settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g\n"
+ "Depth/Channels: %u/%u\n\n",
+ xres, yres, lines, pixels, startx, depth, channels);
+
+/* half_ccd */
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if ((dev->sensor.optical_res < 2 * xres) ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ half_ccd = SANE_FALSE;
+ }
+ else
+ {
+ half_ccd = SANE_TRUE;
+ }
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+
+ /* stagger */
+ if (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE)
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl846_calculate_current_setup: stagger=%d lines\n",
+ stagger);
+
+ /* resolution is choosen from a fixed list */
+ used_res = xres;
+
+ /* compute scan parameters values */
+ /* pixels are allways given at half or full CCD optical resolution */
+ /* use detected left margin and fixed value */
+
+ /* compute correct pixels number */
+ used_pixels = (pixels * optical_res) / used_res;
+ dummy = 3-channels;
+
+ /* slope_dpi */
+ /* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres * channels;
+ else
+ slope_dpi = yres;
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ exposure_time = gl846_compute_exposure (dev, used_res);
+ DBG (DBG_info, "%s : exposure_time=%d pixels\n", __FUNCTION__, exposure_time);
+
+ /* max_shift */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,0);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gl846_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
+{
+
+ DBG (DBG_proc, "gl846_set_motor_power\n");
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ | REG02_MTRPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ & ~REG02_MTRPWR);
+ }
+}
+
+static void
+gl846_set_lamp_power (Genesys_Device __sane_unused__ * dev,
+ Genesys_Register_Set * regs, SANE_Bool set)
+{
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, REG03,
+ sanei_genesys_read_reg_from_set (regs, REG03)
+ | REG03_LAMPPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, REG03,
+ sanei_genesys_read_reg_from_set (regs, REG03)
+ & ~REG03_LAMPPWR);
+ }
+}
+
+/*for fast power saving methods only, like disabling certain amplifiers*/
+static SANE_Status
+gl846_save_power (Genesys_Device * dev, SANE_Bool enable)
+{
+ DBG (DBG_proc, "gl846_save_power: enable = %d\n", enable);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl846_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
+{
+ DBG (DBG_proc, "gl846_set_powersaving (delay = %d)\n", delay);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl846_start_action (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x01);
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_stop_action (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val40, val;
+ unsigned int loop;
+
+ DBGSTART;
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl846_homsnr_gpio(dev);
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ status = sanei_genesys_read_register (dev, REG40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* only stop action if needed */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
+ {
+ DBG (DBG_info, "%s: already stopped\n", __FUNCTION__);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* ends scan */
+ val = sanei_genesys_read_reg_from_set (dev->reg, REG01);
+ val &= ~REG01_SCAN;
+ sanei_genesys_set_reg_from_set (dev->reg, REG01, val);
+ status = sanei_genesys_write_register (dev, REG01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write register 01: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (100 * 1000);
+
+ loop = 10;
+ while (loop > 0)
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ status = sanei_genesys_read_register (dev, REG40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* if scanner is in command mode, we are done */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG)
+ && !(val & REG41_MOTORENB))
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ usleep (100 * 1000);
+ loop--;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Send the low-level scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl846_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor)
+{
+ SANE_Status status;
+ uint8_t val;
+ Genesys_Register_Set *r;
+
+ DBGSTART;
+
+ /* XXX STEF XXX SCAN GPIO */
+ /*
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ */
+
+ val = REG0D_CLRLNCNT;
+ RIE (sanei_genesys_write_register (dev, REG0D, val));
+ val = REG0D_CLRMCNT;
+ RIE (sanei_genesys_write_register (dev, REG0D, val));
+
+ RIE (sanei_genesys_read_register (dev, REG01, &val));
+ val |= REG01_SCAN;
+ RIE (sanei_genesys_write_register (dev, REG01, val));
+ r = sanei_genesys_get_address (reg, REG01);
+ r->value = val;
+
+ if (start_motor)
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 1));
+ }
+ else
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 0));
+ }
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl846_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl846_end_scan (check_stop = %d)\n", check_stop);
+ if (reg == NULL)
+ return SANE_STATUS_INVAL;
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else /* flat bed scanners */
+ {
+ status = gl846_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_end_scan: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* Moves the slider to the home (top) postion slowly */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL846_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ float resolution;
+ uint8_t val;
+ int loop = 0;
+ int scan_mode;
+
+ DBG (DBG_proc, "gl846_slow_back_home (wait_until_home = %d)\n",
+ wait_until_home);
+
+ if(dev->usb_mode<0)
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl846_homsnr_gpio(dev);
+
+ /* first read gives HOME_SENSOR true */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ usleep (100000); /* sleep 100 ms */
+
+ /* second is reliable */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ /* is sensor at home? */
+ if (val & HOMESNR)
+ {
+ DBG (DBG_info, "%s: already at home, completed\n", __FUNCTION__);
+ dev->scanhead_position_in_steps = 0;
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy (local_reg, dev->reg, GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+
+ /* TODO add scan_mode to the API */
+ scan_mode= dev->settings.scan_mode;
+ dev->settings.scan_mode=SCAN_MODE_LINEART;
+ gl846_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 100,
+ 30000,
+ 100,
+ 100,
+ 8,
+ 1,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ dev->settings.scan_mode=scan_mode;
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* set up for reverse */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ r->value |= REG02_MTRREV;
+
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL846_MAX_REGS));
+
+ status = gl846_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_slow_back_home: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl846_stop_action (dev);
+ /* send original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL846_MAX_REGS);
+ return status;
+ }
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl846_homsnr_gpio(dev);
+
+ if (wait_until_home)
+ {
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (val & HOMESNR) /* home sensor */
+ {
+ DBG (DBG_info, "gl846_slow_back_home: reached home position\n");
+ gl846_stop_action (dev);
+ dev->scanhead_position_in_steps = 0;
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl846_stop_action (dev);
+ DBG (DBG_error,
+ "gl846_slow_back_home: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_info, "gl846_slow_back_home: scanhead is still moving\n");
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
+ area at 600 dpi from very top of scanner */
+static SANE_Status
+gl846_search_start_position (Genesys_Device * dev)
+{
+ int size;
+ SANE_Status status;
+ uint8_t *data;
+ Genesys_Register_Set local_reg[GENESYS_GL846_MAX_REGS];
+ int steps;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ DBG (DBG_proc, "gl846_search_start_position\n");
+
+ memcpy (local_reg, dev->reg,
+ GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ status = gl846_init_scan_regs (dev, local_reg, dpi, dpi, 0, 0, /*we should give a small offset here~60 steps */
+ 600, dev->model->search_lines, 8, 1, 1, /*green */
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to set up registers: %s\n",
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* send to scanner */
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL846_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ size = pixels * dev->model->search_lines;
+
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = gl846_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("search_position.pnm", data, 8, 1, pixels,
+ dev->model->search_lines);
+
+ status = gl846_end_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* update regs to copy ASIC internal state */
+ memcpy (dev->reg, local_reg,
+ GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+/*TODO: find out where sanei_genesys_search_reference_point
+ stores information, and use that correctly*/
+ status =
+ sanei_genesys_search_reference_point (dev, data, 0, dpi, pixels,
+ dev->model->search_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to set search reference point: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ free (data);
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sets up register for coarse gain calibration
+ * todo: check it for scanners using it */
+static SANE_Status
+gl846_init_regs_for_coarse_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t channels;
+ uint8_t cksel;
+
+ DBG (DBG_proc, "gl846_init_regs_for_coarse_calibration\n");
+
+
+ cksel = (dev->calib_reg[reg_0x18].value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
+
+ /* set line size */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ status = gl846_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ dev->sensor.optical_res / cksel,
+ 20,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_init_register_for_coarse_calibration: Failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info,
+ "gl846_init_register_for_coarse_calibration: optical sensor res: %d dpi, actual res: %d\n",
+ dev->sensor.optical_res / cksel, dev->settings.xres);
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_init_register_for_coarse_calibration: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief moves the slider to steps at motor base dpi
+ * @param dev device to work on
+ * @param steps number of steps to move in base_dpi line count
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_feed (Genesys_Device * dev, unsigned int steps)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL846_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ float resolution;
+ uint8_t val;
+
+ DBGSTART;
+ DBG (DBG_io, "%s: steps=%d\n", __FUNCTION__, steps);
+
+ /* prepare local registers */
+ memcpy (local_reg, dev->reg, GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+ gl846_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 0,
+ steps,
+ 100,
+ 3,
+ 8,
+ 3,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_FEEDING |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ /* set exposure to zero */
+ sanei_genesys_set_triple(local_reg,REG_EXPR,0);
+ sanei_genesys_set_triple(local_reg,REG_EXPG,0);
+ sanei_genesys_set_triple(local_reg,REG_EXPB,0);
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
+
+ /* set up for no scan */
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ /* send registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL846_MAX_REGS));
+
+ status = gl846_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to start motor: %s\n", __FUNCTION__, sane_strstatus (status));
+ gl846_stop_action (dev);
+
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL846_MAX_REGS);
+ return status;
+ }
+
+ /* wait until feed count reaches the required value, but do not
+ * exceed 30s */
+ do
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ }
+ while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
+
+ /* then stop scanning */
+ RIE(gl846_stop_action (dev));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* init registers for shading calibration */
+static SANE_Status
+gl846_init_regs_for_shading (Genesys_Device * dev)
+{
+ SANE_Status status;
+ float move;
+
+ DBGSTART;
+ dev->calib_channels = 3;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ dev->calib_resolution = sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ dev->calib_lines = dev->model->shading_lines;
+ if(dev->calib_resolution==4800)
+ dev->calib_lines *= 2;
+ dev->calib_pixels = (dev->sensor.sensor_pixels*dev->calib_resolution)/dev->sensor.optical_res;
+ DBG (DBG_io, "%s: calib_lines = %d\n", __FUNCTION__, (unsigned int)dev->calib_lines);
+ DBG (DBG_io, "%s: calib_pixels = %d\n", __FUNCTION__, (unsigned int)dev->calib_pixels);
+
+ /* this is aworkaround insufficent distance for slope
+ * motor acceleration TODO special motor slope for shading */
+ move=1;
+ if(dev->calib_resolution<1200)
+ {
+ move=40;
+ }
+
+ status = gl846_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->calib_resolution,
+ dev->calib_resolution,
+ 0,
+ move,
+ dev->calib_pixels,
+ dev->calib_lines,
+ 16,
+ dev->calib_channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to bulk write registers: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ /* we use GENESYS_FLAG_SHADING_REPARK */
+ dev->scanhead_position_in_steps = 0;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief set up registers for the actual scan
+ */
+static SANE_Status
+gl846_init_regs_for_scan (Genesys_Device * dev)
+{
+ int channels;
+ int flags;
+ int depth;
+ float move;
+ int move_dpi;
+ float start;
+
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl846_init_regs_for_scan settings:\nResolution: %uDPI\n"
+ "Lines : %u\nPPL : %u\nStartpos : %.3f/%.3f\nScan mode : %d\n\n",
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ /* channels */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART)
+ depth = 1;
+
+
+ /* steps to move to reach scanning area:
+ - first we move to physical start of scanning
+ either by a fixed steps amount from the black strip
+ or by a fixed amount from parking position,
+ minus the steps done during shading calibration
+ - then we move by the needed offset whitin physical
+ scanning area
+
+ assumption: steps are expressed at maximum motor resolution
+
+ we need:
+ SANE_Fixed y_offset;
+ SANE_Fixed y_size;
+ SANE_Fixed y_offset_calib;
+ mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
+
+ /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
+ relative from origin, else, it is from parking position */
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = SANE_UNFIX (dev->model->y_offset);
+ move += dev->settings.tl_y;
+ move = (move * move_dpi) / MM_PER_INCH;
+ move -= dev->scanhead_position_in_steps;
+ DBG (DBG_info, "%s: move=%f steps\n",__FUNCTION__, move);
+
+ /* fast move to scan area */
+ /* we don't move fast the whole distance since it would involve
+ * computing acceleration/deceleration distance for scan
+ * resolution. So leave a remainder for it so scan makes the final
+ * move tuning */
+ if(channels*dev->settings.yres>=600 && move>700)
+ {
+ status = gl846_feed (dev, move-500);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to move to scan area\n",__FUNCTION__);
+ return status;
+ }
+ move=500;
+ }
+
+ DBG (DBG_info, "gl846_init_regs_for_scan: move=%f steps\n", move);
+ DBG (DBG_info, "%s: move=%f steps\n", __FUNCTION__, move);
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+ flags = 0;
+
+ /* emulated lineart from gray data is required for now */
+ if(dev->settings.scan_mode == SCAN_MODE_LINEART
+ && dev->settings.dynamic_lineart)
+ {
+ flags |= SCAN_FLAG_DYNAMIC_LINEART;
+ }
+
+ /* backtracking isn't handled well, so don't enable it */
+ flags |= SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
+
+ status = gl846_init_scan_regs (dev,
+ dev->reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ start,
+ move,
+ dev->settings.pixels,
+ dev->settings.lines,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ flags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+static SANE_Status
+gl846_send_shading_data (Genesys_Device * dev, uint8_t * data, int size)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint32_t addr, length, i, x, factor, pixels;
+ uint32_t dpiset, dpihw, strpixel, endpixel;
+ uint16_t tempo;
+ uint32_t lines, channels;
+ uint8_t val,*buffer,*ptr,*src;
+
+ DBGSTART;
+ DBG( DBG_io2, "%s: writing %d bytes of shading data\n",__FUNCTION__,size);
+
+ /* shading data is plit in 3 (up to 5 with IR) areas
+ write(0x10014000,0x00000dd8)
+ URB 23429 bulk_out len 3544 wrote 0x33 0x10 0x....
+ write(0x1003e000,0x00000dd8)
+ write(0x10068000,0x00000dd8)
+ */
+ length = (uint32_t) (size / 3);
+ sanei_genesys_get_double(dev->reg,REG_STRPIXEL,&tempo);
+ strpixel=tempo;
+ sanei_genesys_get_double(dev->reg,REG_ENDPIXEL,&tempo);
+ endpixel=tempo;
+
+ /* compute deletion factor */
+ sanei_genesys_get_double(dev->reg,REG_DPISET,&tempo);
+ dpiset=tempo;
+ DBG( DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d, DPISET=%d\n",__FUNCTION__,strpixel,endpixel,endpixel-strpixel,dpiset);
+ dpihw=sanei_genesys_compute_dpihw(dev,dpiset);
+ factor=dpihw/dpiset;
+ DBG( DBG_io2, "%s: factor=%d\n",__FUNCTION__,factor);
+
+ if(DBG_LEVEL>=DBG_data)
+ {
+ dev->binary=fopen("binary.pnm","wb");
+ sanei_genesys_get_triple(dev->reg, REG_LINCNT, &lines);
+ channels=dev->current_setup.channels;
+ if(dev->binary!=NULL)
+ {
+ fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels,lines/channels,255);
+ }
+ }
+
+ pixels=endpixel-strpixel;
+
+ /* since we're using SHDAREA, substract startx coordinate from shading */
+ strpixel-=((dev->sensor.CCD_start_xoffset*600)/dev->sensor.optical_res);
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2;
+ pixels*=2*2;
+
+ /* allocate temporary buffer */
+ buffer=(uint8_t *)malloc(pixels);
+ memset(buffer,0,pixels);
+ DBG( DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n",__FUNCTION__,pixels,pixels);
+
+ /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address
+ * is 8192*reg value */
+
+ /* write actual color channel data */
+ for(i=0;i<3;i++)
+ {
+ /* build up actual shading data by copying the part from the full width one
+ * to the one corresponding to SHDAREA */
+ ptr=buffer;
+
+ /* iterate on both sensor segment */
+ for(x=0;x<pixels;x+=4*factor)
+ {
+ /* coefficient source */
+ src=(data+strpixel+i*length)+x;
+
+ /* coefficient copy */
+ ptr[0]=src[0];
+ ptr[1]=src[1];
+ ptr[2]=src[2];
+ ptr[3]=src[3];
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+
+ RIE (sanei_genesys_read_register (dev, 0xd0+i, &val));
+ addr = val * 8192 + 0x10000000;
+ status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, addr, pixels, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl846_send_shading_data; write to AHB failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ free(buffer);
+ DBGCOMPLETED;
+
+ return status;
+}
+
+/** @brief calibrates led exposure
+ * Calibrate exposure by scanning a white area until the used exposure gives
+ * data white enough.
+ * @param dev device to calibrate
+ */
+static SANE_Status
+gl846_led_calibration (Genesys_Device * dev)
+{
+ int num_pixels;
+ int total_size;
+ int used_res;
+ uint8_t *line;
+ int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ int channels, depth;
+ int avg[3], top[3], bottom[3];
+ int turn;
+ char fn[20];
+ uint16_t exp[3];
+ Sensor_Profile *sensor;
+ float move;
+ SANE_Bool acceptable;
+
+ DBGSTART;
+
+ move = SANE_UNFIX (dev->model->y_offset_calib);
+ move = (move * (dev->motor.base_ydpi/4)) / MM_PER_INCH;
+ if(move>20)
+ {
+ RIE(gl846_feed (dev, move));
+ }
+ DBG (DBG_io, "%s: move=%f steps\n", __FUNCTION__, move);
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ depth=16;
+ used_res=sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ sensor=get_sensor_profile(dev->model->ccd_type, used_res);
+ num_pixels = (dev->sensor.sensor_pixels*used_res)/dev->sensor.optical_res;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* set up for the calibration scan */
+ status = gl846_init_scan_regs (dev,
+ dev->calib_reg,
+ used_res,
+ used_res,
+ 0,
+ 0,
+ num_pixels,
+ 1,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ total_size = num_pixels * channels * (depth/8) * 1; /* colors * bytes_per_color * scan lines */
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ /* initial loop values and boundaries */
+ exp[0]=sensor->expr;
+ exp[1]=sensor->expg;
+ exp[2]=sensor->expb;
+
+ bottom[0]=29000;
+ bottom[1]=29000;
+ bottom[2]=29000;
+
+ top[0]=41000;
+ top[1]=51000;
+ top[2]=51000;
+
+ turn = 0;
+
+ /* no move during led calibration */
+ gl846_set_motor_power (dev->calib_reg, SANE_FALSE);
+ do
+ {
+ /* set up exposure */
+ sanei_genesys_set_double(dev->calib_reg,REG_EXPR,exp[0]);
+ sanei_genesys_set_double(dev->calib_reg,REG_EXPG,exp[1]);
+ sanei_genesys_set_double(dev->calib_reg,REG_EXPB,exp[2]);
+
+ /* write registers and scan data */
+ RIEF (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS), line);
+
+ DBG (DBG_info, "gl846_led_calibration: starting line reading\n");
+ RIEF (gl846_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ /* stop scanning */
+ RIEF (gl846_stop_action (dev), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf (fn, 20, "led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file (fn, line, depth, channels, num_pixels, 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG (DBG_info, "gl846_led_calibration: average: %d,%d,%d\n", avg[0], avg[1], avg[2]);
+
+ /* check if exposure gives average within the boundaries */
+ acceptable = SANE_TRUE;
+ for(i=0;i<3;i++)
+ {
+ if(avg[i]<bottom[i])
+ {
+ exp[i]=(exp[i]*bottom[i])/avg[i];
+ acceptable = SANE_FALSE;
+ }
+ if(avg[i]>top[i])
+ {
+ exp[i]=(exp[i]*top[i])/avg[i];
+ acceptable = SANE_FALSE;
+ }
+ }
+
+ turn++;
+ }
+ while (!acceptable && turn < 100);
+
+ DBG (DBG_info, "gl846_led_calibration: acceptable exposure: %d,%d,%d\n", exp[0], exp[1], exp[2]);
+
+ /* set these values as final ones for scan */
+ sanei_genesys_set_double(dev->reg,REG_EXPR,exp[0]);
+ sanei_genesys_set_double(dev->reg,REG_EXPG,exp[1]);
+ sanei_genesys_set_double(dev->reg,REG_EXPB,exp[2]);
+
+ /* store in this struct since it is the one used by cache calibration */
+ dev->sensor.regs_0x10_0x1d[0] = (exp[0] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[1] = exp[0] & 0xff;
+ dev->sensor.regs_0x10_0x1d[2] = (exp[1] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[3] = exp[1] & 0xff;
+ dev->sensor.regs_0x10_0x1d[4] = (exp[2] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[5] = exp[2] & 0xff;
+
+ /* cleanup before return */
+ free (line);
+
+ /* go back home */
+ if(move>20)
+ {
+ status=gl846_slow_back_home (dev, SANE_TRUE);
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+ */
+static SANE_Status
+gl846_init_gpio (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx=0;
+
+ DBGSTART;
+
+ /* search GPIO profile */
+ while(gpios[idx].sensor_id!=0 && dev->model->gpo_type!=gpios[idx].sensor_id)
+ {
+ idx++;
+ }
+ if(gpios[idx].sensor_id==0)
+ {
+ DBG (DBG_error, "%s: failed to find GPIO profile for sensor_id=%d\n", __FUNCTION__, dev->model->ccd_type);
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (sanei_genesys_write_register (dev, REGA7, gpios[idx].ra7));
+ RIE (sanei_genesys_write_register (dev, REGA6, gpios[idx].ra6));
+
+ RIE (sanei_genesys_write_register (dev, REG6B, gpios[idx].r6b));
+ RIE (sanei_genesys_write_register (dev, REG6C, gpios[idx].r6c));
+ RIE (sanei_genesys_write_register (dev, REG6D, gpios[idx].r6d));
+ RIE (sanei_genesys_write_register (dev, REG6E, gpios[idx].r6e));
+ RIE (sanei_genesys_write_register (dev, REG6F, gpios[idx].r6f));
+
+ RIE (sanei_genesys_write_register (dev, REGA8, gpios[idx].ra8));
+ RIE (sanei_genesys_write_register (dev, REGA9, gpios[idx].ra9));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * set memory layout by filling values in dedicated registers
+ */
+static SANE_Status
+gl846_init_memory_layout (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx = 0, i;
+ uint8_t val;
+
+ DBGSTART
+
+ /* point to per model memory layout */
+ idx = 0;
+ while(layouts[idx].model!=NULL && strcmp(dev->model->name,layouts[idx].model)!=0)
+ {
+ if(strcmp(dev->model->name,layouts[idx].model)!=0)
+ idx++;
+ }
+ if(layouts[idx].model==NULL)
+ {
+ DBG(DBG_error, "%s: failed to find memory layout for model %s!\n", __FUNCTION__, dev->model->name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* CLKSET and DRAMSEL */
+ val = layouts[idx].dramsel;
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].value = val;
+
+ /* prevent further writings by bulk write register */
+ dev->reg[reg_0x0b].address = 0x00;
+
+ /* setup base address for shading and scanned data. */
+ for(i=0;i<10;i++)
+ {
+ sanei_genesys_write_register (dev, 0xe0+i, layouts[idx].rx[i]);
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* *
+ * initialize ASIC from power on condition
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_boot (Genesys_Device * dev, SANE_Bool cold)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBGSTART;
+
+ /* reset ASIC if cold boot */
+ if(cold)
+ {
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
+ }
+
+ if(dev->usb_mode == 1)
+ {
+ val = 0x14;
+ }
+ else
+ {
+ val = 0x11;
+ }
+ RIE (sanei_genesys_write_0x8c (dev, 0x0f, val));
+
+ /* test CHKVER */
+ RIE (sanei_genesys_read_register (dev, REG40, &val));
+ if (val & REG40_CHKVER)
+ {
+ RIE (sanei_genesys_read_register (dev, 0x00, &val));
+ DBG (DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __FUNCTION__, val);
+ }
+
+ /* Set default values for registers */
+ gl846_init_registers (dev);
+
+ /* Write initial registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL846_MAX_REGS));
+
+ /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
+ val = dev->reg[reg_0x0b].value & REG0B_DRAMSEL;
+ val = (val | REG0B_ENBDRAM);
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].value = val;
+
+ /* CIS_LINE */
+ if (dev->model->is_cis)
+ {
+ SETREG (0x08, REG08_CIS_LINE);
+ RIE (sanei_genesys_write_register (dev, 0x08, dev->reg[reg_0x08].value));
+ }
+
+ /* set up clocks */
+ RIE (sanei_genesys_write_0x8c (dev, 0x10, 0x0e));
+ RIE (sanei_genesys_write_0x8c (dev, 0x13, 0x0e));
+
+ /* setup gpio */
+ RIE (gl846_init_gpio (dev));
+
+ /* setup internal memory layout */
+ RIE (gl846_init_memory_layout (dev));
+
+ SETREG (0xf8, 0x05);
+ RIE (sanei_genesys_write_register (dev, 0xf8, dev->reg[reg_0xf8].value));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl846_init (Genesys_Device * dev)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+ DBGSTART;
+
+ status=sanei_genesys_asic_init(dev, GENESYS_GL846_MAX_REGS);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+static SANE_Status
+gl846_update_hardware_sensors (Genesys_Scanner * s)
+{
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t val;
+ uint8_t scan, file, email, copy;
+ switch(s->dev->model->gpo_type)
+ {
+ default:
+ scan=0x01;
+ file=0x02;
+ email=0x04;
+ copy=0x08;
+ }
+ RIE (sanei_genesys_read_register (s->dev, REG6D, &val));
+
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & scan) == 0;
+ if (s->val[OPT_FILE_SW].b == s->last_val[OPT_FILE_SW].b)
+ s->val[OPT_FILE_SW].b = (val & file) == 0;
+ if (s->val[OPT_EMAIL_SW].b == s->last_val[OPT_EMAIL_SW].b)
+ s->val[OPT_EMAIL_SW].b = (val & email) == 0;
+ if (s->val[OPT_COPY_SW].b == s->last_val[OPT_COPY_SW].b)
+ s->val[OPT_COPY_SW].b = (val & copy) == 0;
+
+ return status;
+}
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
+ * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
+ * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
+ */
+static SANE_Status
+gl846_search_strip (Genesys_Device * dev, SANE_Bool forward, SANE_Bool black)
+{
+ unsigned int pixels, lines, channels;
+ SANE_Status status;
+ Genesys_Register_Set local_reg[GENESYS_GL846_MAX_REGS];
+ size_t size;
+ uint8_t *data;
+ int steps, depth, dpi;
+ unsigned int pass, count, found, x, y;
+ char title[80];
+ Genesys_Register_Set *r;
+
+ DBG (DBG_proc, "gl846_search_strip %s %s\n", black ? "black" : "white",
+ forward ? "forward" : "reverse");
+
+ gl846_set_fe (dev, AFE_SET);
+ status = gl846_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_search_strip: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for a gray scan at lowest dpi */
+ dpi = 9600;
+ for (x = 0; x < MAX_RESOLUTIONS; x++)
+ {
+ if (dev->model->xdpi_values[x] > 0 && dev->model->xdpi_values[x] < dpi)
+ dpi = dev->model->xdpi_values[x];
+ }
+ channels = 1;
+ /* 10 MM */
+ /* lines = (10 * dpi) / MM_PER_INCH; */
+ /* shading calibation is done with dev->motor.base_ydpi */
+ lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
+ depth = 8;
+ pixels = (dev->sensor.sensor_pixels * dpi) / dev->sensor.optical_res;
+ size = pixels * channels * lines * (depth / 8);
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error, "gl846_search_strip: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->scanhead_position_in_steps = 0;
+
+ memcpy (local_reg, dev->reg,
+ GENESYS_GL846_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ status = gl846_init_scan_regs (dev,
+ local_reg,
+ dpi,
+ dpi,
+ 0,
+ 0,
+ pixels,
+ lines,
+ depth,
+ channels,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl846_search_strip: failed to setup for scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for reverse or forward */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ if (forward)
+ r->value &= ~REG02_MTRREV;
+ else
+ r->value |= REG02_MTRREV;
+
+
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL846_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl846_search_strip: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl846_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl846_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl846_search_strip: gl846_stop_action failed\n");
+ return status;
+ }
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels, pixels,
+ lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < 20 && !found)
+ {
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL846_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_search_strip: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* now start scan */
+ status = gl846_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl846_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl846_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl846_search_strip: gl846_stop_action failed\n");
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels,
+ pixels, lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl846_search_strip: strip found forward during pass %d at line %d\n",
+ pass, y);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl846_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl846_search_strip: strip found backward during pass %d \n",
+ pass);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl846_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+ free (data);
+ if (found)
+ {
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_info, "gl846_search_strip: %s strip found\n",
+ black ? "black" : "white");
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ DBG (DBG_info, "gl846_search_strip: %s strip not found\n",
+ black ? "black" : "white");
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * average dark pixels of a 8 bits scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG (DBG_info, "dark_average: avg[%d] = %d\n", k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG (DBG_info, "dark_average: average = %d\n", average);
+ return average;
+}
+
+static SANE_Status
+gl846_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t *first_line, *second_line, reg04;
+ unsigned int channels, bpp;
+ char title[32];
+ int pass = 0, avg, total_size;
+ int topavg, bottomavg, resolution, lines;
+ int top, bottom, black_pixels, pixels;
+
+ DBGSTART;
+
+ /* no gain nor offset for AKM AFE */
+ RIE (sanei_genesys_read_register (dev, REG04, &reg04));
+ if ((reg04 & REG04_FESET) == 0x02)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ resolution=dev->sensor.optical_res;
+ dev->calib_pixels = dev->sensor.sensor_pixels;
+ lines=1;
+ bpp=8;
+ pixels= (dev->sensor.sensor_pixels*resolution) / dev->sensor.optical_res;
+ black_pixels = (dev->sensor.black_pixels * resolution) / dev->sensor.optical_res;
+ DBG (DBG_io2, "gl846_offset_calibration: black_pixels=%d\n", black_pixels);
+
+ status = gl846_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_offset_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gl846_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ /* allocate memory for scans */
+ total_size = pixels * channels * lines * (bpp/8); /* colors * bytes_per_color * scan lines */
+
+ first_line = malloc (total_size);
+ if (!first_line)
+ return SANE_STATUS_NO_MEM;
+
+ second_line = malloc (total_size);
+ if (!second_line)
+ {
+ free (first_line);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* init gain */
+ dev->frontend.gain[0] = 0;
+ dev->frontend.gain[1] = 0;
+ dev->frontend.gain[2] = 0;
+
+ /* scan with no move */
+ bottom = 10;
+ dev->frontend.offset[0] = bottom;
+ dev->frontend.offset[1] = bottom;
+ dev->frontend.offset[2] = bottom;
+
+ RIEF2 (gl846_set_fe(dev, AFE_SET), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl846_offset_calibration: starting first line reading\n");
+ RIEF2 (gl846_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, first_line, total_size), first_line, second_line);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf(title,20,"offset%03d.pnm",bottom);
+ sanei_genesys_write_pnm_file (title, first_line, bpp, channels, pixels, lines);
+ }
+
+ bottomavg = dark_average (first_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_io2, "gl846_offset_calibration: bottom avg=%d\n", bottomavg);
+
+ /* now top value */
+ top = 255;
+ dev->frontend.offset[0] = top;
+ dev->frontend.offset[1] = top;
+ dev->frontend.offset[2] = top;
+ RIEF2 (gl846_set_fe(dev, AFE_SET), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl846_offset_calibration: starting second line reading\n");
+ RIEF2 (gl846_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ topavg = dark_average (second_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_io2, "gl846_offset_calibration: top avg=%d\n", topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.offset[0] = (top + bottom) / 2;
+ dev->frontend.offset[1] = (top + bottom) / 2;
+ dev->frontend.offset[2] = (top + bottom) / 2;
+
+ /* scan with no move */
+ RIEF2 (gl846_set_fe(dev, AFE_SET), first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS), first_line, second_line);
+ DBG (DBG_info, "gl846_offset_calibration: starting second line reading\n");
+ RIEF2 (gl846_begin_scan (dev, dev->calib_reg, SANE_TRUE), first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", dev->frontend.offset[1]);
+ sanei_genesys_write_pnm_file (title, second_line, bpp, channels, pixels, lines);
+ }
+
+ avg = dark_average (second_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_info, "gl846_offset_calibration: avg=%d offset=%d\n", avg,
+ dev->frontend.offset[1]);
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.offset[1];
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.offset[1];
+ }
+ }
+ DBG (DBG_info, "gl846_offset_calibration: offset=(%d,%d,%d)\n", dev->frontend.offset[0], dev->frontend.offset[1], dev->frontend.offset[2]);
+
+ /* cleanup before return */
+ free (first_line);
+ free (second_line);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl846_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ int pixels;
+ int total_size;
+ uint8_t *line, reg04;
+ int i, j, channels;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+ int resolution;
+ int bpp;
+
+ DBG (DBG_proc, "gl846_coarse_gain_calibration: dpi = %d\n", dpi);
+
+ /* no gain nor offset for AKM AFE */
+ RIE (sanei_genesys_read_register (dev, REG04, &reg04));
+ if ((reg04 & REG04_FESET) == 0x02)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ /* follow CKSEL */
+ if(dev->settings.xres<dev->sensor.optical_res)
+ {
+ coeff=0.9;
+ /*resolution=dev->sensor.optical_res/2;*/
+ resolution=dev->sensor.optical_res;
+ }
+ else
+ {
+ resolution=dev->sensor.optical_res;
+ coeff=1.0;
+ }
+ lines=10;
+ bpp=8;
+ pixels = (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+
+ status = gl846_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ gl846_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl846_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (dev->model->cmd_set->bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL846_MAX_REGS));
+
+ total_size = pixels * channels * (16/bpp) * lines;
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ RIEF (gl846_set_fe(dev, AFE_SET), line);
+ RIEF (gl846_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("coarse.pnm", line, bpp, channels, pixels, lines);
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if(bpp==16)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * pixels + 1] * 256 +
+ line[i * 2 + j * 2 * pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ }
+ else
+ {
+ if (dev->model->is_cis)
+ val = line[i + j * pixels];
+ else
+ val = line[i * channels + j];
+ }
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = ((float) dev->sensor.gain_white_ref*coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = 283 - 208 / gain[j];
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.gain[j] = code;
+
+ DBG (DBG_proc,
+ "gl846_coarse_gain_calibration: channel %d, max=%d, gain = %f, setting:%d\n",
+ j, max[j], gain[j], dev->frontend.gain[j]);
+ }
+
+ if (dev->model->is_cis)
+ {
+ if (dev->frontend.gain[0] > dev->frontend.gain[1])
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ if (dev->frontend.gain[0] > dev->frontend.gain[2])
+ dev->frontend.gain[0] = dev->frontend.gain[2];
+ dev->frontend.gain[2] = dev->frontend.gain[1] = dev->frontend.gain[0];
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ dev->frontend.gain[2] = dev->frontend.gain[1];
+ }
+
+ free (line);
+
+ RIE (gl846_stop_action (dev));
+
+ status=gl846_slow_back_home (dev, SANE_TRUE);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** the gl846 command set */
+static Genesys_Command_Set gl846_cmd_set = {
+ "gl846-generic", /* the name of this set */
+
+ gl846_init,
+ NULL,
+ gl846_init_regs_for_coarse_calibration,
+ gl846_init_regs_for_shading,
+ gl846_init_regs_for_scan,
+
+ gl846_get_filter_bit,
+ gl846_get_lineart_bit,
+ gl846_get_bitset_bit,
+ gl846_get_gain4_bit,
+ gl846_get_fast_feed_bit,
+ gl846_test_buffer_empty_bit,
+ gl846_test_motor_flag_bit,
+
+ gl846_bulk_full_size,
+
+ gl846_set_fe,
+ gl846_set_powersaving,
+ gl846_save_power,
+
+ gl846_set_motor_power,
+ gl846_set_lamp_power,
+
+ gl846_begin_scan,
+ gl846_end_scan,
+
+ sanei_genesys_send_gamma_table,
+
+ gl846_search_start_position,
+
+ gl846_offset_calibration,
+ gl846_coarse_gain_calibration,
+ gl846_led_calibration,
+
+ gl846_slow_back_home,
+
+ sanei_genesys_bulk_write_register,
+ NULL,
+ gl846_bulk_read_data,
+
+ gl846_update_hardware_sensors,
+
+ NULL,
+ NULL,
+ NULL,
+ gl846_search_strip,
+
+ sanei_genesys_is_compatible_calibration,
+ NULL,
+ gl846_send_shading_data,
+ gl846_calculate_current_setup,
+ gl846_boot
+};
+
+SANE_Status
+sanei_gl846_init_cmd_set (Genesys_Device * dev)
+{
+ dev->model->cmd_set = &gl846_cmd_set;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl846.h b/backend/genesys_gl846.h
new file mode 100644
index 0000000..58e9702
--- /dev/null
+++ b/backend/genesys_gl846.h
@@ -0,0 +1,705 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2012-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+
+#define REG01 0x01
+#define REG01_CISSET 0x80
+#define REG01_DOGENB 0x40
+#define REG01_DVDSET 0x20
+#define REG01_STAGGER 0x10
+#define REG01_COMPENB 0x08
+#define REG01_TRUEGRAY 0x04
+#define REG01_SHDAREA 0x02
+#define REG01_SCAN 0x01
+
+#define REG02 0x02
+#define REG02_NOTHOME 0x80
+#define REG02_ACDCDIS 0x40
+#define REG02_AGOHOME 0x20
+#define REG02_MTRPWR 0x10
+#define REG02_FASTFED 0x08
+#define REG02_MTRREV 0x04
+#define REG02_HOMENEG 0x02
+#define REG02_LONGCURV 0x01
+
+#define REG03 0x03
+#define REG03_LAMPDOG 0x80
+#define REG03_AVEENB 0x40
+#define REG03_XPASEL 0x20
+#define REG03_LAMPPWR 0x10
+#define REG03_LAMPTIM 0x0f
+
+#define REG04 0x04
+#define REG04_LINEART 0x80
+#define REG04_BITSET 0x40
+#define REG04_AFEMOD 0x30
+#define REG04_FILTER 0x0c
+#define REG04_FESET 0x03
+
+#define REG04S_AFEMOD 4
+
+#define REG05 0x05
+#define REG05_DPIHW 0xc0
+#define REG05_DPIHW_600 0x00
+#define REG05_DPIHW_1200 0x40
+#define REG05_DPIHW_2400 0x80
+#define REG05_DPIHW_4800 0xc0
+#define REG05_MTLLAMP 0x30
+#define REG05_GMMENB 0x08
+#define REG05_MTLBASE 0x03
+
+#define REG06_SCANMOD 0xe0
+#define REG06S_SCANMOD 5
+#define REG06_PWRBIT 0x10
+#define REG06_GAIN4 0x08
+#define REG06_OPTEST 0x07
+
+#define REG07_LAMPSIM 0x80
+
+#define REG08_DRAM2X 0x80
+#define REG08_MPENB 0x20
+#define REG08_CIS_LINE 0x10
+#define REG08_IR1ENB 0x08
+#define REG08_IR2ENB 0x04
+#define REG08_ENB24M 0x01
+
+#define REG09_MCNTSET 0xc0
+#define REG09_EVEN1ST 0x20
+#define REG09_BLINE1ST 0x10
+#define REG09_BACKSCAN 0x08
+#define REG09_ENHANCE 0x04
+#define REG09_SHORTTG 0x02
+#define REG09_NWAIT 0x01
+
+#define REG09S_MCNTSET 6
+#define REG09S_CLKSET 4
+
+
+#define REG0A_LPWMEN 0x10
+
+#define REG0B 0x0b
+#define REG0B_DRAMSEL 0x07
+#define REG0B_ENBDRAM 0x08
+#define REG0B_ENBDRAM 0x08
+#define REG0B_RFHDIS 0x10
+#define REG0B_CLKSET 0xe0
+#define REG0B_24MHZ 0x00
+#define REG0B_30MHZ 0x20
+#define REG0B_40MHZ 0x40
+#define REG0B_48MHZ 0x60
+#define REG0B_60MHZ 0x80
+
+#define REG0C 0x0c
+#define REG0C_CCDLMT 0x0f
+
+#define REG0D 0x0d
+#define REG0D_SCSYNC 0x40
+#define REG0D_CLRERR 0x20
+#define REG0D_FULLSTP 0x10
+#define REG0D_SEND 0x80
+#define REG0D_CLRMCNT 0x04
+#define REG0D_CLRDOCJM 0x02
+#define REG0D_CLRLNCNT 0x01
+
+#define REG0F 0x0f
+
+#define REG16_CTRLHI 0x80
+#define REG16_TOSHIBA 0x40
+#define REG16_TGINV 0x20
+#define REG16_CK1INV 0x10
+#define REG16_CK2INV 0x08
+#define REG16_CTRLINV 0x04
+#define REG16_CKDIS 0x02
+#define REG16_CTRLDIS 0x01
+
+#define REG17_TGMODE 0xc0
+#define REG17_TGMODE_NO_DUMMY 0x00
+#define REG17_TGMODE_REF 0x40
+#define REG17_TGMODE_XPA 0x80
+#define REG17_TGW 0x3f
+#define REG17S_TGW 0
+
+#define REG18 0x18
+#define REG18_CNSET 0x80
+#define REG18_DCKSEL 0x60
+#define REG18_CKTOGGLE 0x10
+#define REG18_CKDELAY 0x0c
+#define REG18_CKSEL 0x03
+
+#define REG1A_SW2SET 0x80
+#define REG1A_SW1SET 0x40
+#define REG1A_MANUAL3 0x02
+#define REG1A_MANUAL1 0x01
+#define REG1A_CK4INV 0x08
+#define REG1A_CK3INV 0x04
+#define REG1A_LINECLP 0x02
+
+#define REG1C 0x1c
+#define REG1C_TGTIME 0x07
+
+#define REG1D_CK4LOW 0x80
+#define REG1D_CK3LOW 0x40
+#define REG1D_CK1LOW 0x20
+#define REG1D_TGSHLD 0x1f
+#define REG1DS_TGSHLD 0
+
+
+#define REG1E_WDTIME 0xf0
+#define REG1ES_WDTIME 4
+#define REG1E_LINESEL 0x0f
+#define REG1ES_LINESEL 0
+
+#define REG_FEDCNT 0x1f
+
+#define REG24 0x1c
+#define REG40 0x40
+#define REG40_DOCSNR 0x80
+#define REG40_ADFSNR 0x40
+#define REG40_COVERSNR 0x20
+#define REG40_CHKVER 0x10
+#define REG40_DOCJAM 0x08
+#define REG40_HISPDFLG 0x04
+#define REG40_MOTMFLG 0x02
+#define REG40_DATAENB 0x01
+
+#define REG41_PWRBIT 0x80
+#define REG41_BUFEMPTY 0x40
+#define REG41_FEEDFSH 0x20
+#define REG41_SCANFSH 0x10
+#define REG41_HOMESNR 0x08
+#define REG41_LAMPSTS 0x04
+#define REG41_FEBUSY 0x02
+#define REG41_MOTORENB 0x01
+
+#define REG58_VSMP 0xf8
+#define REG58S_VSMP 3
+#define REG58_VSMPW 0x07
+#define REG58S_VSMPW 0
+
+#define REG59_BSMP 0xf8
+#define REG59S_BSMP 3
+#define REG59_BSMPW 0x07
+#define REG59S_BSMPW 0
+
+#define REG5A_ADCLKINV 0x80
+#define REG5A_RLCSEL 0x40
+#define REG5A_CDSREF 0x30
+#define REG5AS_CDSREF 4
+#define REG5A_RLC 0x0f
+#define REG5AS_RLC 0
+
+#define REG5E_DECSEL 0xe0
+#define REG5ES_DECSEL 5
+#define REG5E_STOPTIM 0x1f
+#define REG5ES_STOPTIM 0
+
+#define REG60 0x60
+#define REG60_Z1MOD 0x1f
+#define REG61 0x61
+#define REG61_Z1MOD 0xff
+#define REG62 0x62
+#define REG62_Z1MOD 0xff
+
+#define REG63 0x63
+#define REG63_Z2MOD 0x1f
+#define REG64 0x64
+#define REG64_Z2MOD 0xff
+#define REG65 0x65
+#define REG65_Z2MOD 0xff
+
+#define REG60S_STEPSEL 5
+#define REG60_STEPSEL 0xe0
+#define REG60_FULLSTEP 0x00
+#define REG60_HALFSTEP 0x20
+#define REG60_EIGHTHSTEP 0x60
+#define REG60_16THSTEP 0x80
+
+#define REG63S_FSTPSEL 5
+#define REG63_FSTPSEL 0xe0
+#define REG63_FULLSTEP 0x00
+#define REG63_HALFSTEP 0x20
+#define REG63_EIGHTHSTEP 0x60
+#define REG63_16THSTEP 0x80
+
+#define REG67 0x67
+#define REG67_MTRPWM 0x80
+
+#define REG68 0x68
+#define REG68_FASTPWM 0x80
+
+#define REG6B 0x6b
+#define REG6B_MULTFILM 0x80
+#define REG6B_GPOM13 0x40
+#define REG6B_GPOM12 0x20
+#define REG6B_GPOM11 0x10
+#define REG6B_GPO18 0x02
+#define REG6B_GPO17 0x01
+
+#define REG6C 0x6c
+#define REG6C_GPIO16 0x80
+#define REG6C_GPIO15 0x40
+#define REG6C_GPIO14 0x20
+#define REG6C_GPIO13 0x10
+#define REG6C_GPIO12 0x08
+#define REG6C_GPIO11 0x04
+#define REG6C_GPIO10 0x02
+#define REG6C_GPIO9 0x01
+#define REG6C_GPIOH 0xff
+#define REG6C_GPIOL 0xff
+
+#define REG6D 0x6d
+#define REG6E 0x6e
+#define REG6F 0x6f
+#define REG7E 0x7e
+
+#define REG87_ACYCNRLC 0x10
+#define REG87_ENOFFSET 0x08
+#define REG87_LEDADD 0x04
+#define REG87_CK4ADC 0x02
+#define REG87_AUTOCONF 0x01
+
+#define REG9E 0x9e
+#define REG9F 0x9f
+
+#define REGA6 0xa6
+#define REGA7 0xa7
+#define REGA8 0xa8
+#define REGA9 0xa9
+#define REGAB 0xab
+
+#define REG_EXPR 0x10
+#define REG_EXPG 0x12
+#define REG_EXPB 0x14
+#define REG_EXPDMY 0x19
+#define REG_STEPNO 0x21
+#define REG_FWDSTEP 0x22
+#define REG_BWDSTEP 0x23
+#define REG_FASTNO 0x24
+#define REG_DPISET 0x2c
+#define REG_STRPIXEL 0x30
+#define REG_ENDPIXEL 0x32
+#define REG_LINCNT 0x25
+#define REG_MAXWD 0x35
+#define REG_LPERIOD 0x38
+#define REG_FEEDL 0x3d
+#define REG_FMOVDEC 0x5f
+#define REG_FSHDEC 0x69
+#define REG_FMOVNO 0x6a
+#define REG_CK1MAP 0x74
+#define REG_CK3MAP 0x77
+#define REG_CK4MAP 0x7a
+
+#define REGF8 0xf8
+#define REGF8_MAXSEL 0xf0
+#define REGF8_SMAXSEL 4
+#define REGF8_MINSEL 0x0f
+
+/**
+ * writable scanner registers */
+enum
+{
+ reg_0x01 = 0,
+ reg_0x02,
+ reg_0x03,
+ reg_0x04,
+ reg_0x05,
+ reg_0x06,
+ reg_0x08,
+ reg_0x09,
+ reg_0x0a,
+ reg_0x0b,
+ reg_0x0c,
+ reg_0x0d,
+ reg_0x0e,
+ reg_0x0f,
+ reg_0x10,
+ reg_0x11,
+ reg_0x12,
+ reg_0x13,
+ reg_0x14,
+ reg_0x15,
+ reg_0x16,
+ reg_0x17,
+ reg_0x18,
+ reg_0x19,
+ reg_0x1a,
+ reg_0x1b,
+ reg_0x1c,
+ reg_0x1d,
+ reg_0x1e,
+ reg_0x1f,
+ reg_0x20,
+ reg_0x21,
+ reg_0x22,
+ reg_0x23,
+ reg_0x24,
+ reg_0x25,
+ reg_0x26,
+ reg_0x27,
+ reg_0x2c,
+ reg_0x2d,
+ reg_0x2e,
+ reg_0x2f,
+ reg_0x30,
+ reg_0x31,
+ reg_0x32,
+ reg_0x33,
+ reg_0x34,
+ reg_0x35,
+ reg_0x36,
+ reg_0x37,
+ reg_0x38,
+ reg_0x39,
+ reg_0x3a,
+ reg_0x3b,
+ reg_0x3d,
+ reg_0x3e,
+ reg_0x3f,
+ reg_0x51,
+ reg_0x52,
+ reg_0x53,
+ reg_0x54,
+ reg_0x55,
+ reg_0x56,
+ reg_0x57,
+ reg_0x58,
+ reg_0x59,
+ reg_0x5a,
+ reg_0x5e,
+ reg_0x5f,
+ reg_0x60,
+ reg_0x61,
+ reg_0x62,
+ reg_0x63,
+ reg_0x64,
+ reg_0x65,
+ reg_0x67,
+ reg_0x68,
+ reg_0x69,
+ reg_0x6a,
+ reg_0x6b,
+ reg_0x6c,
+ reg_0x6d,
+ reg_0x6e,
+ reg_0x6f,
+ reg_0x70,
+ reg_0x71,
+ reg_0x72,
+ reg_0x73,
+ reg_0x74,
+ reg_0x75,
+ reg_0x76,
+ reg_0x77,
+ reg_0x78,
+ reg_0x79,
+ reg_0x7a,
+ reg_0x7b,
+ reg_0x7c,
+ reg_0x7d,
+ reg_0x7e,
+ reg_0x7f,
+ reg_0x80,
+ reg_0x87,
+ reg_0x94,
+ reg_0x98,
+ reg_0x99,
+ reg_0x9a,
+ reg_0x9b,
+ reg_0x9d,
+ reg_0x9e,
+ reg_0xa1,
+ reg_0xa2,
+ reg_0xa3,
+ reg_0xa4,
+ reg_0xa5,
+ reg_0xa6,
+ reg_0xa7,
+ reg_0xa8,
+ reg_0xa9,
+ reg_0xab,
+ reg_0xbb,
+ reg_0xbc,
+ reg_0xbd,
+ reg_0xbe,
+ reg_0xc5,
+ reg_0xc6,
+ reg_0xc7,
+ reg_0xc8,
+ reg_0xc9,
+ reg_0xca,
+ reg_0xd0,
+ reg_0xd1,
+ reg_0xd2,
+ reg_0xdb,
+ reg_0xe0,
+ reg_0xe1,
+ reg_0xe2,
+ reg_0xe3,
+ reg_0xe4,
+ reg_0xe5,
+ reg_0xe6,
+ reg_0xe7,
+ reg_0xe8,
+ reg_0xe9,
+ reg_0xea,
+ reg_0xeb,
+ reg_0xec,
+ reg_0xed,
+ reg_0xee,
+ reg_0xef,
+ reg_0xf0,
+ reg_0xf1,
+ reg_0xf2,
+ reg_0xf3,
+ reg_0xf4,
+ reg_0xf5,
+ reg_0xf6,
+ reg_0xf7,
+ reg_0xf8,
+ reg_0xfe,
+ reg_0xff,
+ GENESYS_GL846_MAX_REGS
+};
+
+#define SETREG(adr,val) {dev->reg[reg_##adr].address=adr;dev->reg[reg_##adr].value=val;}
+
+
+/** set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl846_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags);
+
+/* Send the low-level scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl846_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool start_motor);
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl846_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool check_stop);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl846_init (Genesys_Device * dev);
+
+/** @brief moves the slider to steps at motor base dpi
+ * @param dev device to work on
+ * @param steps number of steps to move
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_feed (Genesys_Device * dev, unsigned int steps);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_stop_action (Genesys_Device * dev);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl846_boot (Genesys_Device * dev, SANE_Bool cold);
+
+
+
+typedef struct
+{
+ uint8_t sensor_id;
+ uint8_t r6b;
+ uint8_t r6c;
+ uint8_t r6d;
+ uint8_t r6e;
+ uint8_t r6f;
+ uint8_t ra6;
+ uint8_t ra7;
+ uint8_t ra8;
+ uint8_t ra9;
+} Gpio_Profile;
+
+static Gpio_Profile gpios[]={
+ { GPO_IMG101, 0x72, 0x1f, 0xa4, 0x13, 0xa7, 0x11, 0xff, 0x19, 0x05},
+ { GPO_PLUSTEK3800, 0x30, 0x01, 0x80, 0x2d, 0x80, 0x0c, 0x8f, 0x08, 0x04},
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+typedef struct
+{
+ char *model;
+ uint8_t dramsel;
+ /* shading data address */
+ uint8_t rd0;
+ uint8_t rd1;
+ uint8_t rd2;
+ /* scanned data address */
+ uint8_t rx[24];
+} Memory_layout;
+
+static Memory_layout layouts[]={
+ /* Image formula 101 */
+ {
+ "canon-image-formula-101",
+ 0x8b,
+ 0x0a, 0x1b, 0x00,
+ { /* RED ODD START / RED ODD END */
+ 0x00, 0xb0, 0x05, 0xe7, /* [0x00b0, 0x05e7] 1336*4000w */
+ /* RED EVEN START / RED EVEN END */
+ 0x05, 0xe8, 0x0b, 0x1f, /* [0x05e8, 0x0b1f] */
+ /* GREEN ODD START / GREEN ODD END */
+ 0x0b, 0x20, 0x10, 0x57, /* [0x0b20, 0x1057] */
+ /* GREEN EVEN START / GREEN EVEN END */
+ 0x10, 0x58, 0x15, 0x8f, /* [0x1058, 0x158f] */
+ /* BLUE ODD START / BLUE ODD END */
+ 0x15, 0x90, 0x1a, 0xc7, /* [0x1590,0x1ac7] */
+ /* BLUE EVEN START / BLUE EVEN END */
+ 0x1a, 0xc8, 0x1f, 0xff /* [0x1ac8,0x1fff] */
+ }
+ },
+ /* OpticBook 3800 */
+ {
+ "plustek-opticbook-3800",
+ 0x2a,
+ 0x0a, 0x0a, 0x0a,
+ { /* RED ODD START / RED ODD END */
+ 0x00, 0x68, 0x03, 0x00,
+ /* RED EVEN START / RED EVEN END */
+ 0x03, 0x01, 0x05, 0x99,
+ /* GREEN ODD START / GREEN ODD END */
+ 0x05, 0x9a, 0x08, 0x32,
+ /* GREEN EVEN START / GREEN EVEN END */
+ 0x08, 0x33, 0x0a, 0xcb,
+ /* BLUE ODD START / BLUE ODD END */
+ 0x0a, 0xcc, 0x0d, 0x64,
+ /* BLUE EVEN START / BLUE EVEN END */
+ 0x0d, 0x65, 0x0f, 0xfd
+ }
+ },
+ /* list terminating entry */
+ { NULL, 0, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }
+};
+
+/** @brief structure for sensor settings
+ * this structure describes the sensor settings to use for a given
+ * exposure.
+ */
+typedef struct {
+ int sensor_type; /**> sensor id */
+ int dpi; /**> maximum dpi for which data are valid */
+ int exposure; /**> exposure */
+ int ck1map; /**> CK1MAP */
+ int ck3map; /**> CK3MAP */
+ int ck4map; /**> CK4MAP */
+ int segcnt; /**> SEGCNT */
+ int expdummy; /**> exposure dummy */
+ int expr; /**> initial red exposure */
+ int expg; /**> initial green exposure */
+ int expb; /**> initial blue exposure */
+ size_t *order; /**> order of sub-segments */
+ uint8_t r17; /**> TG width */
+} Sensor_Profile;
+
+/* *INDENT-OFF* */
+
+/**
+ * order of the scanned pixel
+ */
+static size_t order_01[]={0,1};
+
+/**
+ * database of sensor profiles
+ */
+static Sensor_Profile sensors[]={
+ {CCD_IMG101, 1200, 11000, 60, 159, 85, 5136, 255, 0, 0, 0, order_01 , 0x13},
+ {CCD_PLUSTEK3800, 1200, 11000, 60, 159, 85, 5136, 255, 0, 0, 0, order_01 , 0x13},
+};
+/* *INDENT-ON* */
+
+/* base motor slopes in full step unit */
+/* target=((exposure * dpi) / base_dpi)>>step_type; */
+static uint32_t img101_high[] = {22000, 22000, 22000, 18450, 15974, 14284, 13054, 12076, 11286, 10660, 10100, 9632, 9224, 8864, 8532, 8250, 7986, 7750, 7530, 7330, 7142, 6972, 6810, 6656, 6518, 6384, 6264, 6150, 6038, 5930, 5834, 5732, 5642, 5560, 5476, 5398, 5324, 5252, 5180, 5112, 5050, 4990, 4926, 4868, 4816, 4760, 4708, 4658, 4608, 4562, 4516, 4472, 4428, 4384, 4344, 4306, 4266, 4230, 4194, 4156, 4122, 4088, 4054, 4022, 3990, 3960, 3930, 3900, 3872, 3842, 3816, 3790, 3762, 3736, 3710, 3686, 3662, 3638, 3614, 3590, 3570, 3548, 3526, 3506, 3484, 3462, 3442, 3424, 3402, 3384, 3366, 3346, 3328, 3310, 3292, 3276, 3258, 3242, 3224, 3208, 3192, 3176, 3162, 3146, 3130, 3114, 3100, 3086, 3072, 3058, 3044, 3030, 3016, 3002, 2990, 2976, 2964, 2950, 2938, 2926, 2914, 2902, 2890, 2878, 2866, 2854, 2844, 2832, 2820, 2810, 2800, 2790, 2778, 2768, 2756, 2748, 2738, 2726, 2716, 2708, 2698, 2688, 2678, 2668, 2660, 2650, 2642, 2632, 2624, 2616, 2606, 2598, 2588, 2580, 2572, 2564, 2556, 2548, 2540, 2530, 2522, 2516, 2508, 2500, 2492, 2486, 2476, 2470, 2462, 2454, 2448, 2440, 2434, 2426, 2420, 2412, 2406, 2400, 2392, 2386, 2378, 2372, 2366, 2360, 2354, 2346, 2340, 2334, 2328, 2322, 2314, 2310, 2304, 2296, 2292, 2286, 2280, 2274, 2268, 2262, 2256, 2252, 2246, 2240, 2234, 2230, 2224, 2218, 2214, 2208, 2202, 2196, 2192, 2188, 2182, 2176, 2172, 2166, 2162, 2156, 2152, 2146, 2142, 2136, 2132, 2128, 2124, 2118, 2114, 2108, 2104, 2100, 2096, 2092, 2086, 2082, 2078, 2072, 2068, 2064, 2060, 2056, 2052, 2048, 2044, 2038, 2034, 2030, 2026, 2022, 2018, 2014, 2010, 2006, 2002, 1998, 1994, 1990, 1988, 1984, 1980, 1976, 1972, 1968, 1964, 1960, 1956, 1952, 1950, 1946, 1942, 1938, 1934, 1932, 1928, 1924, 1920, 1918, 1914, 1910, 1908, 1904, 1900, 1898, 1894, 1890, 1888, 1884, 1880, 1878, 1874, 1870, 1868, 1864, 1862, 1858, 1854, 1852, 1848, 1846, 1842, 1840, 1836, 1834, 1830, 1828, 1824, 1822, 1818, 1816, 1812, 1810, 1806, 1804, 1800, 1798, 1794, 1792, 1790, 1786, 1784, 1780, 1778, 1776, 1772, 1770, 1768, 1764, 1762, 1758, 1756, 1754, 1752, 1748, 1746, 1744, 1740, 1738, 1736, 1734, 1730, 1728, 1726, 1724, 1720, 1718, 1716, 1714, 1710, 1708, 1706, 1704, 1700, 1698, 1696, 1694, 1692, 1688, 1686, 1684, 1682, 1680, 1676, 1674, 1672, 1670, 1668, 1666, 1664, 1662, 1660, 1656, 1654, 1652, 1650, 1648, 1646, 1644, 1642, 1638, 1636, 1634, 1632, 1630, 1628, 1626, 1624, 1622, 1620, 1618, 1616, 1614, 1612, 1610, 1608, 1606, 1604, 1602, 1600, 1598, 1596, 1594, 1592, 1590, 1588, 1586, 1584, 1582, 1580, 1578, 1576, 1574, 1572, 1570, 1568, 1566, 1564, 1562, 1560, 1558, 1556, 1556, 1554, 1552, 1550, 1548, 1546, 1544, 1542, 1540, 1538, 1536, 1536, 1534, 1532, 1530, 1528, 1526, 1524, 1522, 1522, 1520, 1518, 1516, 1514, 1512, 1510, 1510, 1508, 1506, 1504, 1502, 1500, 1500, 1498, 1496, 1494, 1492, 1490, 1490, 1488, 1486, 1484, 1482, 1482, 1480, 1478, 1476, 1474, 1474, 1472, 1470, 1468, 1466, 1466, 1464, 1462, 1460, 1460, 1458, 1456, 1454, 1454, 1452, 1450, 1448, 1448, 1446, 1444, 1442, 1442, 1440, 1438, 1438, 1436, 1434, 1432, 1432, 1430, 1428, 1426, 1426, 1424, 1422, 1422, 1420, 1418, 1418, 1416, 1414, 1412, 1412, 1410, 1408, 1408, 1406, 1404, 1404, 1402, 1400, 1400, 1398, 1396, 1394, 1394, 1392, 1390, 1390, 1388, 1388, 1386, 1384, 1384, 1382, 1380, 1380, 1378, 1376, 1376, 1374, 1374, 1372, 1370, 1370, 1368, 1366, 1366, 1364, 1362, 1362, 1360, 1360, 1358, 1356, 1356, 1354, 1352, 1352, 1350, 1350, 1348, 1348, 1346, 1344, 1344, 1342, 1340, 1340, 1338, 1338, 1336, 1336, 1334, 1332, 1332, 1330, 1330, 1328, 1328, 1326, 1324, 1324, 1322, 1322, 1320, 1318, 1318, 1316, 1316, 1314, 1314, 1312, 1310, 1310, 1308, 1308, 1306, 1306, 1304, 1304, 1302, 1302, 1300, 1300, 1298, 1298, 1296, 1294, 1294, 1292, 1292, 1290, 1290, 1288, 1288, 1286, 1286, 1284, 1284, 1282, 1282, 1280, 1280, 1278, 1278, 1276, 1276, 1274, 1272, 1272, 1270, 1270, 1270, 1268, 1268, 1266, 1264, 1264, 1262, 1262, 1260, 1260, 1260, 1258, 1258, 1256, 1254, 1254, 1254, 1252, 1252, 1250, 1250, 1248, 1248, 1246, 1246, 1244, 1244, 1242, 1242, 1240, 1240, 1238, 1238, 1236, 1236, 1236, 1234, 1234, 1232, 1232, 1230, 1230, 1228, 1228, 1226, 1226, 1226, 1224, 1224, 1222, 1222, 1220, 1220, 1218, 1218, 1218, 1216, 1216, 1214, 1214, 1212, 1212, 1210, 1210, 1210, 1208, 1208, 1206, 1206, 1204, 1204, 1204, 1202, 1202, 1200, 1200, 1198, 1198, 1198, 1196, 1196, 1194, 1194, 1192, 1192, 1192, 1190, 1190, 1188, 1188, 1188, 1186, 1186, 1184, 1184, 1182, 1182, 1182, 1180, 1180, 1180, 1178, 1178, 1176, 1176, 1174, 1174, 1174, 1172, 1172, 1172, 1170, 1170, 1168, 1168, 1168, 1166, 1166, 1164, 1164, 1164, 1162, 1162, 1160, 1160, 1160, 1158, 1158, 1156, 1156, 1156, 1154, 1154, 1154, 1152, 1152, 1152, 1150, 1150, 1148, 1148, 1148, 1146, 1146, 1146, 1144, 1144, 1142, 1142, 1142, 1140, 1140, 1140, 1138, 1138, 1136, 1136, 1136, 1134, 1134, 1134, 1132, 1132, 1132, 1130, 1130, 1130, 1128, 1128, 1126, 1126, 1126, 1124, 1124, 1124, 1122, 1122, 1122, 1120, 1120, 1120, 1118, 1118, 1118, 1116, 1116, 1116, 1114, 1114, 1114, 1112, 1112, 1112, 1110, 1110, 1110, 1108, 1108, 1108, 1106, 1106, 1106, 1104, 1104, 1104, 1102, 1102, 1102, 1100, 1100, 1100, 1098, 1098, 1098, 1096, 1096, 1096, 1094, 1094, 1094, 1092, 1092, 1092, 1090, 1090, 1090, 1088, 1088, 1088, 1086, 1086, 1086, 1086, 1084, 1084, 1084, 1082, 1082, 1082, 1080, 1080, 1080, 1078, 1078, 1078, 1078, 1076, 1076, 1076, 1074, 1074, 1074, 1072, 1072, 1072, 1072, 1070, 1070, 1070, 1068, 1068, 1068, 1066, 1066, 1066, 1064, 1064, 1064, 1064, 1062, 1062, 1062, 1060, 1060, 1060, 1058, 1058, 1058, 1058, 1056, 1056, 1056, 1054, 1054, 1054, 1054, 1052, 1052, 1052, 1050, 1050, 1050, 1050, 1048, 1048, 1048, 1046, 1046, 1046, 1046, 1044, 1044, 1044, 1044, 1042, 1042, 1042, 1040, 1040, 1040, 1040, 1038, 1038, 1038, 1036, 1036, 1036, 1036, 1034, 1034, 1034, 1034, 1032, 1032, 1032, 1030, 1030, 1030, 1030, 1028, 1028, 1028, 1028, 1026, 1026, 1026, 1026, 1024, 1024, 1024, 1022, 1022, 1022, 1022, 1020, 1020, 1020, 1020, 1018, 1018, 1018, 1018, 1016, 1016, 1016, 1016, 1014, 1014, 1014, 1014, 1012, 1012, 1012, 1012, 1010, 1010, 1010, 1010, 1008, 1008, 1008, 1008, 1006, 1006, 1006, 1006, 1004, 1004, 1004, 1004, 1002, 1002, 1002, 1002, 1000, 1000, 1000, 1000, 0 };
+
+/**
+ * database of motor profiles
+ */
+
+/* *INDENT-OFF* */
+static Motor_Profile gl846_motors[]={
+ /* Image Formula 101 */
+ {MOTOR_IMG101, 11000, HALF_STEP , img101_high},
+ {MOTOR_PLUSTEK3800, 11000, HALF_STEP , img101_high},
+
+ /* end of database entry */
+ {0, 0, 0, NULL},
+};
+/* *INDENT-ON* */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl847.c b/backend/genesys_gl847.c
new file mode 100644
index 0000000..7c6a3c3
--- /dev/null
+++ b/backend/genesys_gl847.c
@@ -0,0 +1,3765 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_gl847
+
+#include "genesys_gl847.h"
+
+/****************************************************************************
+ Low level function
+ ****************************************************************************/
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+
+/** @brief read scanned data
+ * Read in 0xeff0 maximum sized blocks. This read is done in 2
+ * parts if not multple of 512. First read is rounded to a multiple of 512 bytes, last read fetches the
+ * remainder. Read addr is always 0x10000000 with the memory layout setup.
+ * @param dev device to read data from
+ * @param addr address within ASIC memory space, unused but kept for API
+ * @param data pointer where to store the read data
+ * @param len size to read
+ */
+static SANE_Status
+gl847_bulk_read_data (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len)
+{
+ SANE_Status status;
+ size_t size, target, read, done;
+ uint8_t outdata[8];
+ uint8_t *buffer;
+
+ DBG (DBG_io, "gl847_bulk_read_data: requesting %lu bytes at addr=0x%02x\n", (u_long) len, addr);
+
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ target = len;
+ buffer = data;
+
+ /* loop until computed data size is read */
+ while (target)
+ {
+ if (target > 0xeff0)
+ {
+ size = 0xeff0;
+ }
+ else
+ {
+ size = target;
+ }
+
+ /* hard coded 0x10000000 addr */
+ outdata[0] = 0;
+ outdata[1] = 0;
+ outdata[2] = 0;
+ outdata[3] = 0x10;
+
+ /* data size to transfer */
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_BUFFER, 0x00, sizeof (outdata),
+ outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s failed while writing command: %s\n",
+ __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ /* blocks must be multiple of 512 but not last block */
+ read = size;
+ if (read >= 512)
+ {
+ read /= 512;
+ read *= 512;
+ }
+
+ DBG (DBG_io2,
+ "gl847_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) read);
+ status = sanei_usb_read_bulk (dev->dn, buffer, &read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ done=read;
+ DBG (DBG_io2, "gl847_bulk_read_data: %lu bytes of data read\n", (u_long) done);
+
+ /* read less than 512 bytes remainder */
+ if (read < size)
+ {
+ read = size - read;
+ DBG (DBG_io2,
+ "gl847_bulk_read_data: trying to read %lu bytes of data\n",
+ (u_long) read);
+ status = sanei_usb_read_bulk (dev->dn, buffer+done, &read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_bulk_read_data failed while reading bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ done=read;
+ DBG (DBG_io2, "gl847_bulk_read_data: %lu bytes of data read\n", (u_long) done);
+ }
+
+ DBG (DBG_io2, "%s: read %lu bytes, %lu remaining\n", __FUNCTION__,
+ (u_long) size, (u_long) (target - size));
+
+ target -= size;
+ buffer += size;
+ }
+
+ if (DBG_LEVEL >= DBG_data && dev->binary!=NULL)
+ {
+ fwrite(data, len, 1, dev->binary);
+ }
+
+ DBGCOMPLETED;
+
+ return SANE_STATUS_GOOD;
+}
+
+/****************************************************************************
+ Mid level functions
+ ****************************************************************************/
+
+static SANE_Bool
+gl847_get_fast_feed_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG02);
+ if (r && (r->value & REG02_FASTFED))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl847_get_filter_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_FILTER))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl847_get_lineart_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_LINEART))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl847_get_bitset_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, REG04);
+ if (r && (r->value & REG04_BITSET))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl847_get_gain4_bit (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+
+ r = sanei_genesys_get_address (regs, 0x06);
+ if (r && (r->value & REG06_GAIN4))
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl847_test_buffer_empty_bit (SANE_Byte val)
+{
+ if (val & REG41_BUFEMPTY)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static SANE_Bool
+gl847_test_motor_flag_bit (SANE_Byte val)
+{
+ if (val & REG41_MOTORENB)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/**
+ * compute the step multiplier used
+ */
+static int
+gl847_get_step_multiplier (Genesys_Register_Set * regs)
+{
+ Genesys_Register_Set *r = NULL;
+ int value = 1;
+
+ r = sanei_genesys_get_address (regs, 0x9d);
+ if (r != NULL)
+ {
+ value = (r->value & 0x0f)>>1;
+ value = 1 << value;
+ }
+ DBG (DBG_io, "%s: step multiplier is %d\n", __FUNCTION__, value);
+ return value;
+}
+
+/** @brief sensor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at a specific dpihw. Use LiDE 110 table by default.
+ * @param sensor_type sensor id
+ * @param dpi hardware dpi for the scan
+ * @return a pointer to a Sensor_Profile struct
+ */
+static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi)
+{
+ unsigned int i;
+ int idx;
+
+ i=0;
+ idx=-1;
+ while(i<sizeof(sensors)/sizeof(Sensor_Profile))
+ {
+ /* exact match */
+ if(sensors[i].sensor_type==sensor_type && sensors[i].dpi==dpi)
+ {
+ return &(sensors[i]);
+ }
+
+ /* closest match */
+ if(sensors[i].sensor_type==sensor_type)
+ {
+ if(idx<0)
+ {
+ idx=i;
+ }
+ else
+ {
+ if(sensors[i].dpi>=dpi
+ && sensors[i].dpi<sensors[idx].dpi)
+ {
+ idx=i;
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default sensor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(sensors[idx]);
+}
+
+/**@brief compute exposure to use
+ * compute the sensor exposure based on target resolution
+ */
+static int gl847_compute_exposure(Genesys_Device *dev, int xres)
+{
+ Sensor_Profile *sensor;
+
+ sensor=get_sensor_profile(dev->model->ccd_type, xres);
+ return sensor->exposure;
+}
+
+
+/** @brief sensor specific settings
+*/
+static void
+gl847_setup_sensor (Genesys_Device * dev, Genesys_Register_Set * regs, int dpi)
+{
+ Genesys_Register_Set *r;
+ Sensor_Profile *sensor;
+ int dpihw, i;
+ uint16_t exp;
+
+ DBGSTART;
+ dpihw=sanei_genesys_compute_dpihw(dev,dpi);
+
+ for (i = 0x06; i < 0x0e; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x10 + i);
+ if (r)
+ r->value = dev->sensor.regs_0x10_0x1d[i];
+ }
+
+ for (i = 0; i < 9; i++)
+ {
+ r = sanei_genesys_get_address (regs, 0x52 + i);
+ if (r)
+ r->value = dev->sensor.regs_0x52_0x5e[i];
+ }
+
+ /* set EXPDUMMY and CKxMAP */
+ dpihw=sanei_genesys_compute_dpihw(dev,dpi);
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw);
+
+ sanei_genesys_set_reg_from_set(regs,REG_EXPDMY,(uint8_t)((sensor->expdummy) & 0xff));
+
+ /* if no calibration has been done, set default values for exposures */
+ exp=dev->sensor.regs_0x10_0x1d[0]*256+dev->sensor.regs_0x10_0x1d[1];
+ if(exp==0)
+ {
+ exp=sensor->expr;
+ }
+ sanei_genesys_set_double(regs,REG_EXPR,exp);
+
+ exp=dev->sensor.regs_0x10_0x1d[2]*256+dev->sensor.regs_0x10_0x1d[3];
+ if(exp==0)
+ {
+ exp=sensor->expg;
+ }
+ sanei_genesys_set_double(regs,REG_EXPG,exp);
+
+ exp=dev->sensor.regs_0x10_0x1d[4]*256+dev->sensor.regs_0x10_0x1d[5];
+ if(exp==0)
+ {
+ exp=sensor->expb;
+ }
+ sanei_genesys_set_double(regs,REG_EXPB,exp);
+
+ sanei_genesys_set_triple(regs,REG_CK1MAP,sensor->ck1map);
+ sanei_genesys_set_triple(regs,REG_CK3MAP,sensor->ck3map);
+ sanei_genesys_set_triple(regs,REG_CK4MAP,sensor->ck4map);
+
+ /* order of the sub-segments */
+ dev->order=sensor->order;
+
+ r = sanei_genesys_get_address (regs, 0x17);
+ r->value = sensor->r17;
+
+ DBGCOMPLETED;
+}
+
+
+/* returns the max register bulk size */
+static int
+gl847_bulk_full_size (void)
+{
+ return GENESYS_GL847_MAX_REGS;
+}
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl847_init_registers (Genesys_Device * dev)
+{
+ int lide700=0;
+ uint8_t val;
+
+ DBGSTART;
+ /* 700F class needs some different initial settings */
+ if (strcmp (dev->model->name, "canon-lide-700f") == 0)
+ {
+ lide700 = 1;
+ }
+
+ memset (dev->reg, 0,
+ GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ SETREG (0x01, 0x82);
+ SETREG (0x02, 0x18);
+ SETREG (0x03, 0x50);
+ SETREG (0x04, 0x12);
+ SETREG (0x05, 0x80);
+ SETREG (0x06, 0x50); /* FASTMODE + POWERBIT */
+ SETREG (0x08, 0x10);
+ SETREG (0x09, 0x01);
+ SETREG (0x0a, 0x00);
+ SETREG (0x0b, 0x01);
+ SETREG (0x0c, 0x02);
+
+ /* LED exposures */
+ SETREG (0x10, 0x00);
+ SETREG (0x11, 0x00);
+ SETREG (0x12, 0x00);
+ SETREG (0x13, 0x00);
+ SETREG (0x14, 0x00);
+ SETREG (0x15, 0x00);
+
+ SETREG (0x16, 0x10);
+ SETREG (0x17, 0x08);
+ SETREG (0x18, 0x00);
+
+ /* EXPDMY */
+ SETREG (0x19, 0x50);
+
+ SETREG (0x1a, 0x34);
+ SETREG (0x1b, 0x00);
+ SETREG (0x1c, 0x02);
+ SETREG (0x1d, 0x04);
+ SETREG (0x1e, 0x10);
+ SETREG (0x1f, 0x04);
+ SETREG (0x20, 0x02);
+ SETREG (0x21, 0x10);
+ SETREG (0x22, 0x7f);
+ SETREG (0x23, 0x7f);
+ SETREG (0x24, 0x10);
+ SETREG (0x25, 0x00);
+ SETREG (0x26, 0x00);
+ SETREG (0x27, 0x00);
+ SETREG (0x2c, 0x09);
+ SETREG (0x2d, 0x60);
+ SETREG (0x2e, 0x80);
+ SETREG (0x2f, 0x80);
+ SETREG (0x30, 0x00);
+ SETREG (0x31, 0x10);
+ SETREG (0x32, 0x15);
+ SETREG (0x33, 0x0e);
+ SETREG (0x34, 0x40);
+ SETREG (0x35, 0x00);
+ SETREG (0x36, 0x2a);
+ SETREG (0x37, 0x30);
+ SETREG (0x38, 0x2a);
+ SETREG (0x39, 0xf8);
+ SETREG (0x3d, 0x00);
+ SETREG (0x3e, 0x00);
+ SETREG (0x3f, 0x00);
+ SETREG (0x52, 0x03);
+ SETREG (0x53, 0x07);
+ SETREG (0x54, 0x00);
+ SETREG (0x55, 0x00);
+ SETREG (0x56, 0x00);
+ SETREG (0x57, 0x00);
+ SETREG (0x58, 0x2a);
+ SETREG (0x59, 0xe1);
+ SETREG (0x5a, 0x55);
+ SETREG (0x5e, 0x41);
+ SETREG (0x5f, 0x40);
+ SETREG (0x60, 0x00);
+ SETREG (0x61, 0x21);
+ SETREG (0x62, 0x40);
+ SETREG (0x63, 0x00);
+ SETREG (0x64, 0x21);
+ SETREG (0x65, 0x40);
+ SETREG (0x67, 0x80);
+ SETREG (0x68, 0x80);
+ SETREG (0x69, 0x20);
+ SETREG (0x6a, 0x20);
+
+ /* CK1MAP */
+ SETREG (0x74, 0x00);
+ SETREG (0x75, 0x00);
+ SETREG (0x76, 0x3c);
+
+ /* CK3MAP */
+ SETREG (0x77, 0x00);
+ SETREG (0x78, 0x00);
+ SETREG (0x79, 0x9f);
+
+ /* CK4MAP */
+ SETREG (0x7a, 0x00);
+ SETREG (0x7b, 0x00);
+ SETREG (0x7c, 0x55);
+
+ SETREG (0x7d, 0x00);
+
+ /* NOTE: autoconf is a non working option */
+ SETREG (0x87, 0x02);
+ SETREG (0x9d, 0x06);
+ SETREG (0xa2, 0x0f);
+ SETREG (0xbd, 0x18);
+ SETREG (0xfe, 0x08);
+
+ /* gamma[0] and gamma[256] values */
+ SETREG (0xbe, 0x00);
+ SETREG (0xc5, 0x00);
+ SETREG (0xc6, 0x00);
+ SETREG (0xc7, 0x00);
+ SETREG (0xc8, 0x00);
+ SETREG (0xc9, 0x00);
+ SETREG (0xca, 0x00);
+
+ /* LiDE 700 fixups */
+ if(lide700)
+ {
+ SETREG (0x5f, 0x04);
+ SETREG (0x7d, 0x80);
+
+ /* we write to these registers only once */
+ val=0;
+ sanei_genesys_write_register (dev, REG7E, val);
+ sanei_genesys_write_register (dev, REG9E, val);
+ sanei_genesys_write_register (dev, REG9F, val);
+ sanei_genesys_write_register (dev, REGAB, val);
+ }
+
+ /* fine tune upon device description */
+ dev->reg[reg_0x05].value &= ~REG05_DPIHW;
+ switch (dev->sensor.optical_res)
+ {
+ case 600:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ dev->reg[reg_0x05].value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* initalize calibration reg */
+ memcpy (dev->calib_reg, dev->reg,
+ GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ DBGCOMPLETED;
+}
+
+/**@brief send slope table for motor movement
+ * Send slope_table in machine byte order
+ * @param dev device to send slope table
+ * @param table_nr index of the slope table in ASIC memory
+ * Must be in the [0-4] range.
+ * @param slope_table pointer to 16 bit values array of the slope table
+ * @param steps number of elements in the slope table
+ */
+static SANE_Status
+gl847_send_slope_table (Genesys_Device * dev, int table_nr,
+ uint16_t * slope_table, int steps)
+{
+ SANE_Status status;
+ uint8_t *table;
+ int i;
+ char msg[10000];
+
+ DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __FUNCTION__,
+ table_nr, steps);
+
+ /* sanity check */
+ if(table_nr<0 || table_nr>4)
+ {
+ DBG (DBG_error, "%s: invalid table number %d!\n", __FUNCTION__, table_nr);
+ return SANE_STATUS_INVAL;
+ }
+
+ table = (uint8_t *) malloc (steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sprintf (msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++)
+ {
+ sprintf (msg+strlen(msg), "%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __FUNCTION__, msg);
+ }
+
+ /* slope table addresses are fixed */
+ status =
+ sanei_genesys_write_ahb (dev->dn, dev->usb_mode, 0x10000000 + 0x4000 * table_nr, steps * 2, table);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: write to AHB failed writing slope table %d (%s)\n",
+ __FUNCTION__, table_nr, sane_strstatus (status));
+ }
+
+ free (table);
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * Set register values of Analog Device type frontend
+ * */
+static SANE_Status
+gl847_set_ad_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ uint16_t val;
+ uint8_t val8;
+
+ DBGSTART;
+
+ /* wait for FE to be ready */
+ status = sanei_genesys_get_status (dev, &val8);
+ while (val8 & REG41_FEBUSY);
+ {
+ usleep (10000);
+ status = sanei_genesys_get_status (dev, &val8);
+ };
+
+ if (set == AFE_INIT)
+ {
+ DBG (DBG_proc, "gl847_set_ad_fe(): setting DAC %u\n",
+ dev->model->dac_type);
+
+ /* sets to default values */
+ sanei_genesys_init_fe (dev);
+ }
+
+ /* reset DAC */
+ status = sanei_genesys_fe_write_data (dev, 0x00, 0x80);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl847_set_ad_fe: failed to write reg0: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* write them to analog frontend */
+ val = dev->frontend.reg[0];
+ status = sanei_genesys_fe_write_data (dev, 0x00, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl847_set_ad_fe: failed to write reg0: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ val = dev->frontend.reg[1];
+ status = sanei_genesys_fe_write_data (dev, 0x01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl847_set_ad_fe: failed to write reg1: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.gain[i];
+ status = sanei_genesys_fe_write_data (dev, 0x02 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_set_ad_fe: failed to write gain %d: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ for (i = 0; i < 3; i++)
+ {
+ val = dev->frontend.offset[i];
+ status = sanei_genesys_fe_write_data (dev, 0x05 + i, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_set_ad_fe: failed to write offset %d: %s\n", i,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+static SANE_Status
+gl847_homsnr_gpio(Genesys_Device *dev)
+{
+uint8_t val;
+SANE_Status status=SANE_STATUS_GOOD;
+
+ if (dev->model->gpo_type == GPO_CANONLIDE700)
+ {
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ val &= ~REG6C_GPIO10;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ }
+ else
+ {
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ val |= REG6C_GPIO10;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ }
+ return status;
+}
+
+/* Set values of analog frontend */
+static SANE_Status
+gl847_set_fe (Genesys_Device * dev, uint8_t set)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBG (DBG_proc, "gl847_set_fe (%s)\n",
+ set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
+ AFE_POWER_SAVE ? "powersave" : "huh?");
+
+ RIE (sanei_genesys_read_register (dev, REG04, &val));
+
+ /* route to AD devices */
+ if ((val & REG04_FESET) == 0x02)
+ {
+ return gl847_set_ad_fe (dev, set);
+ }
+
+ /* for now there is no support yet for wolfson fe */
+ DBG (DBG_proc, "gl847_set_fe(): unsupported frontend type %d\n",
+ dev->reg[reg_0x04].value & REG04_FESET);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/** @brief set up motor related register for scan
+ */
+static SANE_Status
+gl847_init_motor_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int scan_exposure_time,
+ float scan_yres,
+ int scan_step_type,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ int scan_power_mode,
+ unsigned int flags)
+{
+ SANE_Status status;
+ int use_fast_fed;
+ unsigned int fast_dpi;
+ uint16_t scan_table[SLOPE_TABLE_SIZE];
+ uint16_t fast_table[SLOPE_TABLE_SIZE];
+ int scan_steps, fast_steps, factor;
+ unsigned int feedl, dist;
+ Genesys_Register_Set *r;
+ uint32_t z1, z2;
+ unsigned int min_restep = 0x20;
+ uint8_t val, effective;
+ int fast_step_type;
+ unsigned int ccdlmt,tgtime;
+
+ DBGSTART;
+ DBG (DBG_proc, "gl847_init_motor_regs_scan : scan_exposure_time=%d, "
+ "scan_yres=%g, scan_step_type=%d, scan_lines=%d, scan_dummy=%d, "
+ "feed_steps=%d, scan_power_mode=%d, flags=%x\n",
+ scan_exposure_time,
+ scan_yres,
+ scan_step_type,
+ scan_lines, scan_dummy, feed_steps, scan_power_mode, flags);
+
+ /* get step multiplier */
+ factor = gl847_get_step_multiplier (reg);
+
+ use_fast_fed=0;
+ /* no fast fed since feed works well */
+ if(dev->settings.yres==4444 && feed_steps>100
+ && ((flags & MOTOR_FLAG_FEED)==0))
+ {
+ use_fast_fed=1;
+ }
+ DBG (DBG_io, "%s: use_fast_fed=%d\n", __FUNCTION__, use_fast_fed);
+
+ sanei_genesys_set_triple(reg, REG_LINCNT, scan_lines);
+ DBG (DBG_io, "%s: lincnt=%d\n", __FUNCTION__, scan_lines);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address (reg, REG02);
+ r->value = 0x00;
+ r->value |= REG02_MTRPWR;
+
+ if (use_fast_fed)
+ r->value |= REG02_FASTFED;
+ else
+ r->value &= ~REG02_FASTFED;
+
+ if (flags & MOTOR_FLAG_AUTO_GO_HOME)
+ r->value |= REG02_AGOHOME | REG02_NOTHOME;
+
+ if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ ||(scan_yres>=dev->sensor.optical_res))
+ {
+ r->value |= REG02_ACDCDIS;
+ }
+
+ /* scan and backtracking slope table */
+ sanei_genesys_slope_table(scan_table,
+ &scan_steps,
+ scan_yres,
+ scan_exposure_time,
+ dev->motor.base_ydpi,
+ scan_step_type,
+ factor,
+ dev->model->motor_type,
+ gl847_motors);
+ RIE(gl847_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps*factor));
+ RIE(gl847_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps*factor));
+
+ /* fast table */
+ fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
+ fast_step_type=scan_step_type;
+ if(scan_step_type>=2)
+ {
+ fast_step_type=2;
+ }
+
+ sanei_genesys_slope_table(fast_table,
+ &fast_steps,
+ fast_dpi,
+ scan_exposure_time,
+ dev->motor.base_ydpi,
+ fast_step_type,
+ factor,
+ dev->model->motor_type,
+ gl847_motors);
+
+ /* manual override of high start value */
+ fast_table[0]=fast_table[1];
+
+ RIE(gl847_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps*factor));
+ RIE(gl847_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps*factor));
+ RIE(gl847_send_slope_table (dev, HOME_TABLE, fast_table, fast_steps*factor));
+
+ /* correct move distance by acceleration and deceleration amounts */
+ feedl=feed_steps;
+ if (use_fast_fed)
+ {
+ feedl<<=fast_step_type;
+ dist=(scan_steps+2*fast_steps)*factor;
+ /* TODO read and decode REGAB */
+ r = sanei_genesys_get_address (reg, 0x5e);
+ dist += (r->value & 31);
+ /* FEDCNT */
+ r = sanei_genesys_get_address (reg, REG_FEDCNT);
+ dist += r->value;
+ }
+ else
+ {
+ feedl<<=scan_step_type;
+ dist=scan_steps*factor;
+ if (flags & MOTOR_FLAG_FEED)
+ dist *=2;
+ }
+ DBG (DBG_io2, "%s: scan steps=%d\n", __FUNCTION__, scan_steps);
+ DBG (DBG_io2, "%s: acceleration distance=%d\n", __FUNCTION__, dist);
+
+ /* check for overflow */
+ if(dist<feedl)
+ feedl -= dist;
+ else
+ feedl = 0;
+
+ sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
+ DBG (DBG_io ,"%s: feedl=%d\n",__FUNCTION__,feedl);
+
+ r = sanei_genesys_get_address (reg, REG0C);
+ ccdlmt=(r->value & REG0C_CCDLMT)+1;
+
+ r = sanei_genesys_get_address (reg, REG1C);
+ tgtime=1<<(r->value & REG1C_TGTIME);
+
+ /* hi res motor speed GPIO */
+ RIE (sanei_genesys_read_register (dev, REG6C, &effective));
+
+ /* if quarter step, bipolar Vref2 */
+ if (scan_step_type > 1)
+ {
+ if (scan_step_type < 3)
+ {
+ val = effective & ~REG6C_GPIO13;
+ }
+ else
+ {
+ val = effective | REG6C_GPIO13;
+ }
+ }
+ else
+ {
+ val = effective;
+ }
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+
+ /* effective scan */
+ RIE (sanei_genesys_read_register (dev, REG6C, &effective));
+ val = effective | REG6C_GPIO10;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+
+ min_restep=scan_steps/2-1;
+ if (min_restep < 1)
+ min_restep = 1;
+ r = sanei_genesys_get_address (reg, REG_FWDSTEP);
+ r->value = min_restep;
+ r = sanei_genesys_get_address (reg, REG_BWDSTEP);
+ r->value = min_restep;
+
+ sanei_genesys_calculate_zmode2(use_fast_fed,
+ scan_exposure_time*ccdlmt*tgtime,
+ scan_table,
+ scan_steps*factor,
+ feedl,
+ min_restep*factor,
+ &z1,
+ &z2);
+
+ DBG (DBG_info, "gl847_init_motor_regs_scan: z1 = %d\n", z1);
+ sanei_genesys_set_triple(reg, REG60, z1 | (scan_step_type << (16+REG60S_STEPSEL)));
+
+ DBG (DBG_info, "gl847_init_motor_regs_scan: z2 = %d\n", z2);
+ sanei_genesys_set_triple(reg, REG63, z2 | (scan_step_type << (16+REG63S_FSTPSEL)));
+
+ r = sanei_genesys_get_address (reg, 0x1e);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ r = sanei_genesys_get_address (reg, REG67);
+ r->value = REG67_MTRPWM;
+
+ r = sanei_genesys_get_address (reg, REG68);
+ r->value = REG68_FASTPWM;
+
+ r = sanei_genesys_get_address (reg, REG_STEPNO);
+ r->value = scan_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FASTNO);
+ r->value = scan_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FSHDEC);
+ r->value = scan_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FMOVNO);
+ r->value = fast_steps;
+
+ r = sanei_genesys_get_address (reg, REG_FMOVDEC);
+ r->value = fast_steps;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief set up registers related to sensor
+ * Set up the following registers
+ 0x01
+ 0x03
+ 0x10-0x015 R/G/B exposures
+ 0x19 EXPDMY
+ 0x2e BWHI
+ 0x2f BWLO
+ 0x04
+ 0x87
+ 0x05
+ 0x2c,0x2d DPISET
+ 0x30,0x31 STRPIXEL
+ 0x32,0x33 ENDPIXEL
+ 0x35,0x36,0x37 MAXWD [25:2] (>>2)
+ 0x38,0x39 LPERIOD
+ 0x34 DUMMY
+ */
+static SANE_Status
+gl847_init_optical_regs_scan (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ unsigned int exposure_time,
+ int used_res,
+ unsigned int start,
+ unsigned int pixels,
+ int channels,
+ int depth,
+ SANE_Bool half_ccd, int color_filter, int flags)
+{
+ unsigned int words_per_line;
+ unsigned int startx, endx, used_pixels;
+ unsigned int dpiset, dpihw,segnb,cksel,factor;
+ unsigned int bytes;
+ Genesys_Register_Set *r;
+ SANE_Status status;
+ Sensor_Profile *sensor;
+
+ DBG (DBG_proc, "gl847_init_optical_regs_scan : exposure_time=%d, "
+ "used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
+ "half_ccd=%d, flags=%x\n", exposure_time,
+ used_res, start, pixels, channels, depth, half_ccd, flags);
+
+ /* resolution is divided according to CKSEL */
+ r = sanei_genesys_get_address (reg, REG18);
+ cksel= (r->value & REG18_CKSEL)+1;
+ DBG (DBG_io2, "%s: cksel=%d\n", __FUNCTION__, cksel);
+
+ /* to manage high resolution device while keeping good
+ * low resolution scanning speed, we make hardware dpi vary */
+ dpihw=sanei_genesys_compute_dpihw(dev, used_res * cksel);
+ factor=dev->sensor.optical_res/dpihw;
+ DBG (DBG_io2, "%s: dpihw=%d (factor=%d)\n", __FUNCTION__, dpihw, factor);
+
+ /* sensor parameters */
+ sensor=get_sensor_profile(dev->model->ccd_type, dpihw);
+ gl847_setup_sensor (dev, reg, dpihw);
+ dpiset = used_res * cksel;
+
+ /* start and end coordinate in optical dpi coordinates */
+ startx = start/cksel+dev->sensor.CCD_start_xoffset;
+ used_pixels=pixels/cksel;
+
+ /* end of sensor window */
+ endx = startx + used_pixels;
+
+ /* sensors are built from 600 dpi segments for LiDE 100/200
+ * and 1200 dpi for the 700F */
+ if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR)
+ {
+ segnb=dpihw/600;
+ }
+ else
+ {
+ segnb=1;
+ }
+
+ /* compute pixel coordinate in the given dpihw space,
+ * taking segments into account */
+ startx/=factor*segnb;
+ endx/=factor*segnb;
+ dev->len=endx-startx;
+ dev->dist=0;
+ dev->skip=0;
+
+ /* in cas of multi-segments sensor, we have to add the witdh
+ * of the sensor crossed by the scan area */
+ if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR && segnb>1)
+ {
+ dev->dist = sensor->segcnt;
+ }
+
+ /* use a segcnt rounded to next even number */
+ endx += ((dev->dist+1)&0xfffe)*(segnb-1);
+ used_pixels=endx-startx;
+
+ status = gl847_set_fe (dev, AFE_SET);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_init_optical_regs_scan: failed to set frontend: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* enable shading */
+ r = sanei_genesys_get_address (reg, REG01);
+ r->value &= ~REG01_SCAN;
+ r->value |= REG01_SHDAREA;
+ if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG01_DVDSET;
+ }
+ else
+ {
+ r->value |= REG01_DVDSET;
+ }
+
+ r = sanei_genesys_get_address (reg, REG03);
+ r->value &= ~REG03_AVEENB;
+
+ if (flags & OPTICAL_FLAG_DISABLE_LAMP)
+ r->value &= ~REG03_LAMPPWR;
+ else
+ r->value |= REG03_LAMPPWR;
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, 0x2e);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, 0x2f);
+ r->value = dev->settings.threshold;
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, REG04);
+ switch (depth)
+ {
+ case 1:
+ r->value &= ~REG04_BITSET;
+ r->value |= REG04_LINEART;
+ break;
+ case 8:
+ r->value &= ~(REG04_LINEART | REG04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG04_LINEART;
+ r->value |= REG04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG04_FILTER | REG04_AFEMOD);
+ if (channels == 1)
+ {
+ switch (color_filter)
+ {
+ case 0:
+ r->value |= 0x14; /* red filter */
+ break;
+ case 2:
+ r->value |= 0x1c; /* blue filter */
+ break;
+ default:
+ r->value |= 0x18; /* green filter */
+ break;
+ }
+ }
+ else
+ r->value |= 0x10; /* mono */
+
+ /* register 05 */
+ r = sanei_genesys_get_address (reg, REG05);
+
+ /* set up dpihw */
+ r->value &= ~REG05_DPIHW;
+ switch(dpihw)
+ {
+ case 600:
+ r->value |= REG05_DPIHW_600;
+ break;
+ case 1200:
+ r->value |= REG05_DPIHW_1200;
+ break;
+ case 2400:
+ r->value |= REG05_DPIHW_2400;
+ break;
+ case 4800:
+ r->value |= REG05_DPIHW_4800;
+ break;
+ }
+
+ /* enable gamma tables */
+ if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
+ r->value &= ~REG05_GMMENB;
+ else
+ r->value |= REG05_GMMENB;
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ /* we set up LEDADD only when asked */
+ if (dev->model->is_cis == SANE_TRUE)
+ {
+ r = sanei_genesys_get_address (reg, 0x87);
+ r->value &= ~REG87_LEDADD;
+ if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG87_LEDADD;
+ }
+ /* RGB weighting
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG01_TRUEGRAY;
+ if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
+ {
+ r->value |= REG01_TRUEGRAY;
+ }*/
+ }
+
+ /* words(16bit) before gamma, conversion to 8 bit or lineart*/
+ words_per_line = (used_pixels * dpiset) / dpihw;
+ bytes=depth/8;
+ if (depth == 1)
+ {
+ words_per_line = (words_per_line+7)/8 ;
+ dev->len = (dev->len >> 3) + ((dev->len & 7) ? 1 : 0);
+ dev->dist = (dev->dist >> 3) + ((dev->dist & 7) ? 1 : 0);
+ }
+ else
+ {
+ words_per_line *= bytes;
+ dev->dist *= bytes;
+ dev->len *= bytes;
+ }
+
+ dev->bpl = words_per_line;
+ dev->cur=0;
+ dev->segnb=segnb;
+ dev->line_interp = 0;
+
+ sanei_genesys_set_double(reg,REG_DPISET,dpiset);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __FUNCTION__, dpiset);
+
+ sanei_genesys_set_double(reg,REG_STRPIXEL,startx);
+ sanei_genesys_set_double(reg,REG_ENDPIXEL,endx);
+ DBG (DBG_io2, "%s: startx=%d\n", __FUNCTION__, startx);
+ DBG (DBG_io2, "%s: endx =%d\n", __FUNCTION__, endx);
+
+ DBG (DBG_io2, "%s: used_pixels=%d\n", __FUNCTION__, used_pixels);
+ DBG (DBG_io2, "%s: pixels =%d\n", __FUNCTION__, pixels);
+ DBG (DBG_io2, "%s: depth =%d\n", __FUNCTION__, depth);
+ DBG (DBG_io2, "%s: dev->bpl =%lu\n", __FUNCTION__, (unsigned long)dev->bpl);
+ DBG (DBG_io2, "%s: dev->len =%lu\n", __FUNCTION__, (unsigned long)dev->len);
+ DBG (DBG_io2, "%s: dev->dist =%lu\n", __FUNCTION__, (unsigned long)dev->dist);
+ DBG (DBG_io2, "%s: dev->segnb =%lu\n", __FUNCTION__, (unsigned long)dev->segnb);
+
+ words_per_line *= channels;
+ dev->wpl = words_per_line;
+
+ if(dev->oe_buffer.buffer!=NULL)
+ {
+ sanei_genesys_buffer_free (&(dev->oe_buffer));
+ }
+ RIE (sanei_genesys_buffer_alloc (&(dev->oe_buffer), dev->wpl));
+
+ /* MAXWD is expressed in 4 words unit */
+ sanei_genesys_set_triple(reg, REG_MAXWD, (words_per_line >> 2));
+ DBG (DBG_io2, "%s: words_per_line used=%d\n", __FUNCTION__, words_per_line);
+
+ sanei_genesys_set_double(reg, REG_LPERIOD, exposure_time);
+ DBG (DBG_io2, "%s: exposure_time used=%d\n", __FUNCTION__, exposure_time);
+
+ r = sanei_genesys_get_address (reg, 0x34);
+ r->value = dev->sensor.dummy_pixel;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl847_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags)
+{
+ int used_res;
+ int start, used_pixels;
+ int bytes_per_line;
+ int move;
+ unsigned int lincnt;
+ unsigned int oflags; /**> optical flags */
+ unsigned int mflags; /**> motor flags */
+ int exposure_time;
+ int stagger;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+ int scan_step_type = 1;
+ int scan_power_mode = 0;
+ int max_shift;
+ size_t requested_buffer_size, read_buffer_size;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl847_init_scan_regs settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g/%g\n"
+ "Depth/Channels: %u/%u\n"
+ "Flags : %x\n\n",
+ xres, yres, lines, pixels, startx, starty, depth, channels, flags);
+
+ /* we may have 2 domains for ccd: xres below or above half ccd max dpi */
+ if (dev->sensor.optical_res < 2 * xres ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ half_ccd = SANE_FALSE;
+ }
+ else
+ {
+ half_ccd = SANE_TRUE;
+ }
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+ if (half_ccd)
+ optical_res /= 2;
+
+ /* stagger */
+ if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl847_init_scan_regs : stagger=%d lines\n", stagger);
+
+ /* used_res */
+ if (flags & SCAN_FLAG_USE_OPTICAL_RES)
+ {
+ used_res = optical_res;
+ }
+ else
+ {
+ /* resolution is choosen from a list */
+ used_res = xres;
+ }
+
+ /* compute scan parameters values */
+ /* pixels are allways given at full optical resolution */
+ /* use detected left margin and fixed value */
+ /* start */
+ /* add x coordinates */
+ start = startx;
+
+ if (stagger > 0)
+ start |= 1;
+
+ /* compute correct pixels number */
+ /* pixels */
+ used_pixels = (pixels * optical_res) / xres;
+
+ /* round up pixels number if needed */
+ if (used_pixels * xres < pixels * optical_res)
+ used_pixels++;
+
+ dummy = 3-channels;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres * channels;
+ else
+ slope_dpi = yres;
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ exposure_time = gl847_compute_exposure (dev, used_res);
+ scan_step_type = sanei_genesys_compute_step_type(gl847_motors, dev->model->motor_type, exposure_time);
+
+ DBG (DBG_info, "gl847_init_scan_regs : exposure_time=%d pixels\n", exposure_time);
+ DBG (DBG_info, "gl847_init_scan_regs : scan_step_type=%d\n", scan_step_type);
+
+/*** optical parameters ***/
+ /* in case of dynamic lineart, we use an internal 8 bit gray scan
+ * to generate 1 lineart data */
+ if ((flags & SCAN_FLAG_DYNAMIC_LINEART) && (dev->settings.scan_mode == SCAN_MODE_LINEART))
+ {
+ depth = 8;
+ }
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ oflags = 0;
+ if(flags & SCAN_FLAG_DISABLE_SHADING)
+ oflags |= OPTICAL_FLAG_DISABLE_SHADING;
+ if(flags & SCAN_FLAG_DISABLE_GAMMA)
+ oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
+ if(flags & SCAN_FLAG_DISABLE_LAMP)
+ oflags |= OPTICAL_FLAG_DISABLE_LAMP;
+
+ if (dev->model->is_cis && dev->settings.true_gray)
+ {
+ oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
+ }
+
+ status = gl847_init_optical_regs_scan (dev,
+ reg,
+ exposure_time,
+ used_res,
+ start,
+ used_pixels,
+ channels,
+ depth,
+ half_ccd,
+ color_filter,
+ oflags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+/*** motor parameters ***/
+
+ /* max_shift */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,flags);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ /* add tl_y to base movement */
+ move = starty;
+ DBG (DBG_info, "gl847_init_scan_regs: move=%d steps\n", move);
+
+ mflags=0;
+ if(flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
+ mflags |= MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
+ if(flags & SCAN_FLAG_FEEDING)
+ mflags |= MOTOR_FLAG_FEED;
+
+ status = gl847_init_motor_regs_scan (dev,
+ reg,
+ exposure_time,
+ slope_dpi,
+ scan_step_type,
+ dev->model->is_cis ? lincnt *
+ channels : lincnt, dummy, move,
+ scan_power_mode,
+ mflags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ /*** prepares data reordering ***/
+
+/* words_per_line */
+ bytes_per_line = (used_pixels * used_res) / optical_res;
+ bytes_per_line = (bytes_per_line * channels * depth) / 8;
+
+ requested_buffer_size = 8 * bytes_per_line;
+ /* we must use a round number of bytes_per_line */
+ /* XXX STEF XXX
+ if (requested_buffer_size > BULKIN_MAXSIZE)
+ requested_buffer_size =
+ (BULKIN_MAXSIZE / bytes_per_line) * bytes_per_line;
+ */
+
+ read_buffer_size =
+ 2 * requested_buffer_size +
+ ((max_shift + stagger) * used_pixels * channels * depth) / 8;
+
+ RIE (sanei_genesys_buffer_free (&(dev->read_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->read_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->lines_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->lines_buffer), read_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->shrink_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->shrink_buffer),
+ requested_buffer_size));
+
+ RIE (sanei_genesys_buffer_free (&(dev->out_buffer)));
+ RIE (sanei_genesys_buffer_alloc (&(dev->out_buffer),
+ (8 * dev->settings.pixels * channels *
+ depth) / 8));
+
+
+ dev->read_bytes_left = bytes_per_line * lincnt;
+
+ DBG (DBG_info,
+ "gl847_init_scan_regs: physical bytes to read = %lu\n",
+ (u_long) dev->read_bytes_left);
+ dev->read_active = SANE_TRUE;
+
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+/* TODO: should this be done elsewhere? */
+ /* scan bytes to send to the frontend */
+ /* theory :
+ target_size =
+ (dev->settings.pixels * dev->settings.lines * channels * depth) / 8;
+ but it suffers from integer overflow so we do the following:
+
+ 1 bit color images store color data byte-wise, eg byte 0 contains
+ 8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
+ 8 bits of blue.
+ This does not fix the overflow, though.
+ 644mp*16 = 10gp, leading to an overflow
+ -- pierre
+ */
+
+ dev->total_bytes_read = 0;
+ if (depth == 1)
+ dev->total_bytes_to_read =
+ ((dev->settings.pixels * dev->settings.lines) / 8 +
+ (((dev->settings.pixels * dev->settings.lines) % 8) ? 1 : 0)) *
+ channels;
+ else
+ dev->total_bytes_to_read =
+ dev->settings.pixels * dev->settings.lines * channels * (depth / 8);
+
+ DBG (DBG_info, "gl847_init_scan_regs: total bytes to send = %lu\n",
+ (u_long) dev->total_bytes_to_read);
+/* END TODO */
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl847_calculate_current_setup (Genesys_Device * dev)
+{
+ int channels;
+ int depth;
+ int start;
+
+ float xres; /*dpi */
+ float yres; /*dpi */
+ float startx; /*optical_res, from dummy_pixel+1 */
+ float pixels;
+ float lines;
+
+ int used_res;
+ int used_pixels;
+ unsigned int lincnt;
+ int exposure_time;
+ int stagger;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+ int max_shift;
+
+ SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
+ int optical_res;
+
+ DBG (DBG_info,
+ "gl847_calculate_current_setup settings:\n"
+ "Resolution: %uDPI\n"
+ "Lines : %u\n"
+ "PPL : %u\n"
+ "Startpos : %.3f/%.3f\n"
+ "Scan mode : %d\n\n",
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ /* channels */
+ if (dev->settings.scan_mode == 4) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == 0)
+ depth = 1;
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+
+ xres = dev->settings.xres; /*dpi */
+ yres = dev->settings.yres; /*dpi */
+ startx = start; /*optical_res, from dummy_pixel+1 */
+ pixels = dev->settings.pixels;
+ lines = dev->settings.lines;
+
+ DBG (DBG_info,
+ "gl847_calculate_current_setup settings:\n"
+ "Resolution : %gDPI/%gDPI\n"
+ "Lines : %g\n"
+ "PPL : %g\n"
+ "Startpos : %g\n"
+ "Depth/Channels: %u/%u\n\n",
+ xres, yres, lines, pixels, startx, depth, channels);
+
+/* half_ccd */
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ if ((dev->sensor.optical_res < 2 * xres) ||
+ !(dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE))
+ {
+ half_ccd = SANE_FALSE;
+ }
+ else
+ {
+ half_ccd = SANE_TRUE;
+ }
+
+ /* optical_res */
+ optical_res = dev->sensor.optical_res;
+
+ /* stagger */
+ if (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE)
+ stagger = (4 * yres) / dev->motor.base_ydpi;
+ else
+ stagger = 0;
+ DBG (DBG_info, "gl847_calculate_current_setup: stagger=%d lines\n",
+ stagger);
+
+ /* resolution is choosen from a fixed list */
+ used_res = xres;
+
+ /* compute scan parameters values */
+ /* pixels are allways given at half or full CCD optical resolution */
+ /* use detected left margin and fixed value */
+
+ /* compute correct pixels number */
+ used_pixels = (pixels * optical_res) / used_res;
+ dummy = 3-channels;
+
+ /* slope_dpi */
+ /* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = yres * channels;
+ else
+ slope_dpi = yres;
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ exposure_time = gl847_compute_exposure (dev, used_res);
+ DBG (DBG_info, "%s : exposure_time=%d pixels\n", __FUNCTION__, exposure_time);
+
+ /* max_shift */
+ max_shift=sanei_genesys_compute_max_shift(dev,channels,yres,0);
+
+ /* lincnt */
+ lincnt = lines + max_shift + stagger;
+
+ dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
+ dev->current_setup.lines = lincnt;
+ dev->current_setup.depth = depth;
+ dev->current_setup.channels = channels;
+ dev->current_setup.exposure_time = exposure_time;
+ dev->current_setup.xres = used_res;
+ dev->current_setup.yres = yres;
+ dev->current_setup.half_ccd = half_ccd;
+ dev->current_setup.stagger = stagger;
+ dev->current_setup.max_shift = max_shift + stagger;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gl847_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
+{
+
+ DBG (DBG_proc, "gl847_set_motor_power\n");
+
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ | REG02_MTRPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, REG02,
+ sanei_genesys_read_reg_from_set (regs,
+ REG02)
+ & ~REG02_MTRPWR);
+ }
+}
+
+static void
+gl847_set_lamp_power (Genesys_Device __sane_unused__ * dev,
+ Genesys_Register_Set * regs, SANE_Bool set)
+{
+ if (set)
+ {
+ sanei_genesys_set_reg_from_set (regs, REG03,
+ sanei_genesys_read_reg_from_set (regs, REG03)
+ | REG03_LAMPPWR);
+ }
+ else
+ {
+ sanei_genesys_set_reg_from_set (regs, REG03,
+ sanei_genesys_read_reg_from_set (regs, REG03)
+ & ~REG03_LAMPPWR);
+ }
+}
+
+/*for fast power saving methods only, like disabling certain amplifiers*/
+static SANE_Status
+gl847_save_power (Genesys_Device * dev, SANE_Bool enable)
+{
+ DBG (DBG_proc, "gl847_save_power: enable = %d\n", enable);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl847_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
+{
+ DBG (DBG_proc, "gl847_set_powersaving (delay = %d)\n", delay);
+ if (dev == NULL)
+ return SANE_STATUS_INVAL;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl847_start_action (Genesys_Device * dev)
+{
+ return sanei_genesys_write_register (dev, 0x0f, 0x01);
+}
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl847_stop_action (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val40, val;
+ unsigned int loop;
+
+ DBGSTART;
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl847_homsnr_gpio(dev);
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ status = sanei_genesys_read_register (dev, REG40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* only stop action if needed */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
+ {
+ DBG (DBG_info, "%s: already stopped\n", __FUNCTION__);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* ends scan */
+ val = sanei_genesys_read_reg_from_set (dev->reg, REG01);
+ val &= ~REG01_SCAN;
+ sanei_genesys_set_reg_from_set (dev->reg, REG01, val);
+ status = sanei_genesys_write_register (dev, REG01, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to write register 01: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (100 * 1000);
+
+ loop = 10;
+ while (loop > 0)
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ status = sanei_genesys_read_register (dev, REG40, &val40);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* if scanner is in command mode, we are done */
+ if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG)
+ && !(val & REG41_MOTORENB))
+ {
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ usleep (100 * 1000);
+ loop--;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Send the low-level scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl847_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool start_motor)
+{
+ SANE_Status status;
+ uint8_t val;
+ Genesys_Register_Set *r;
+
+ DBGSTART;
+
+ /* clear GPIO 10 */
+ if (dev->model->gpo_type != GPO_CANONLIDE700)
+ {
+ RIE (sanei_genesys_read_register (dev, REG6C, &val));
+ val &= ~REG6C_GPIO10;
+ RIE (sanei_genesys_write_register (dev, REG6C, val));
+ }
+
+ val = REG0D_CLRLNCNT;
+ RIE (sanei_genesys_write_register (dev, REG0D, val));
+ val = REG0D_CLRMCNT;
+ RIE (sanei_genesys_write_register (dev, REG0D, val));
+
+ RIE (sanei_genesys_read_register (dev, REG01, &val));
+ val |= REG01_SCAN;
+ RIE (sanei_genesys_write_register (dev, REG01, val));
+ r = sanei_genesys_get_address (reg, REG01);
+ r->value = val;
+
+ if (start_motor)
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 1));
+ }
+ else
+ {
+ RIE (sanei_genesys_write_register (dev, REG0F, 0));
+ }
+
+ DBGCOMPLETED;
+
+ return status;
+}
+
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+ SANE_Status
+gl847_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
+ SANE_Bool check_stop)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "gl847_end_scan (check_stop = %d)\n", check_stop);
+ if (reg == NULL)
+ return SANE_STATUS_INVAL;
+
+ if (dev->model->is_sheetfed == SANE_TRUE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else /* flat bed scanners */
+ {
+ status = gl847_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_end_scan: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* Moves the slider to the home (top) postion slowly */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl847_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL847_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ float resolution;
+ uint8_t val;
+ int loop = 0;
+ int scan_mode;
+
+ DBG (DBG_proc, "gl847_slow_back_home (wait_until_home = %d)\n",
+ wait_until_home);
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl847_homsnr_gpio(dev);
+
+ /* first read gives HOME_SENSOR true */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+ usleep (100000); /* sleep 100 ms */
+
+ /* second is reliable */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ sanei_genesys_print_status (val);
+ }
+
+ /* is sensor at home? */
+ if (val & HOMESNR)
+ {
+ DBG (DBG_info, "%s: already at home, completed\n", __FUNCTION__);
+ dev->scanhead_position_in_steps = 0;
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy (local_reg, dev->reg, GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+
+ /* TODO add scan_mode to the API */
+ scan_mode= dev->settings.scan_mode;
+ dev->settings.scan_mode=SCAN_MODE_LINEART;
+ gl847_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 100,
+ 30000,
+ 100,
+ 100,
+ 8,
+ 1,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ dev->settings.scan_mode=scan_mode;
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
+
+ /* set up for reverse */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ r->value |= REG02_MTRREV;
+
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL847_MAX_REGS));
+
+ status = gl847_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_slow_back_home: failed to start motor: %s\n",
+ sane_strstatus (status));
+ gl847_stop_action (dev);
+ /* send original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL847_MAX_REGS);
+ return status;
+ }
+
+ /* post scan gpio : without that HOMSNR is unreliable */
+ gl847_homsnr_gpio(dev);
+
+ if (wait_until_home)
+ {
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_slow_back_home: failed to read home sensor: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (val & HOMESNR) /* home sensor */
+ {
+ DBG (DBG_info, "gl847_slow_back_home: reached home position\n");
+ gl847_stop_action (dev);
+ dev->scanhead_position_in_steps = 0;
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+ }
+ usleep (100000); /* sleep 100 ms */
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl847_stop_action (dev);
+ DBG (DBG_error,
+ "gl847_slow_back_home: timeout while waiting for scanhead to go home\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_info, "gl847_slow_back_home: scanhead is still moving\n");
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
+ area at 600 dpi from very top of scanner */
+static SANE_Status
+gl847_search_start_position (Genesys_Device * dev)
+{
+ int size;
+ SANE_Status status;
+ uint8_t *data;
+ Genesys_Register_Set local_reg[GENESYS_GL847_MAX_REGS];
+ int steps;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ DBG (DBG_proc, "gl847_search_start_position\n");
+
+ memcpy (local_reg, dev->reg,
+ GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ status = gl847_init_scan_regs (dev, local_reg, dpi, dpi, 0, 0, /*we should give a small offset here~60 steps */
+ 600, dev->model->search_lines, 8, 1, 1, /*green */
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to set up registers: %s\n",
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* send to scanner */
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL847_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ DBGCOMPLETED;
+ return status;
+ }
+
+ size = pixels * dev->model->search_lines;
+
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = gl847_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("search_position.pnm", data, 8, 1, pixels,
+ dev->model->search_lines);
+
+ status = gl847_end_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to end scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* update regs to copy ASIC internal state */
+ memcpy (dev->reg, local_reg,
+ GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+/*TODO: find out where sanei_genesys_search_reference_point
+ stores information, and use that correctly*/
+ status =
+ sanei_genesys_search_reference_point (dev, data, 0, dpi, pixels,
+ dev->model->search_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to set search reference point: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ free (data);
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sets up register for coarse gain calibration
+ * todo: check it for scanners using it */
+static SANE_Status
+gl847_init_regs_for_coarse_calibration (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t channels;
+ uint8_t cksel;
+
+ DBG (DBG_proc, "gl847_init_regs_for_coarse_calibration\n");
+
+
+ cksel = (dev->calib_reg[reg_0x18].value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
+
+ /* set line size */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ status = gl847_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ 0,
+ 0,
+ dev->sensor.optical_res / cksel,
+ 20,
+ 16,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_init_register_for_coarse_calibration: Failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_info,
+ "gl847_init_register_for_coarse_calibration: optical sensor res: %d dpi, actual res: %d\n",
+ dev->sensor.optical_res / cksel, dev->settings.xres);
+
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_init_register_for_coarse_calibration: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief moves the slider to steps at motor base dpi
+ * @param dev device to work on
+ * @param steps number of steps to move in base_dpi line count
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl847_feed (Genesys_Device * dev, unsigned int steps)
+{
+ Genesys_Register_Set local_reg[GENESYS_GL847_MAX_REGS];
+ SANE_Status status;
+ Genesys_Register_Set *r;
+ float resolution;
+ uint8_t val;
+
+ DBGSTART;
+ DBG (DBG_io, "%s: steps=%d\n", __FUNCTION__, steps);
+
+ /* prepare local registers */
+ memcpy (local_reg, dev->reg, GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ resolution=sanei_genesys_get_lowest_ydpi(dev);
+ gl847_init_scan_regs (dev,
+ local_reg,
+ resolution,
+ resolution,
+ 0,
+ steps,
+ 100,
+ 3,
+ 8,
+ 3,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_FEEDING |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ /* set exposure to zero */
+ sanei_genesys_set_triple(local_reg,REG_EXPR,0);
+ sanei_genesys_set_triple(local_reg,REG_EXPG,0);
+ sanei_genesys_set_triple(local_reg,REG_EXPB,0);
+
+ /* clear scan and feed count */
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
+ RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
+
+ /* set up for no scan */
+ r = sanei_genesys_get_address (local_reg, REG01);
+ r->value &= ~REG01_SCAN;
+
+ /* send registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL847_MAX_REGS));
+
+ status = gl847_start_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to start motor: %s\n", __FUNCTION__, sane_strstatus (status));
+ gl847_stop_action (dev);
+
+ /* restore original registers */
+ dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL847_MAX_REGS);
+ return status;
+ }
+
+ /* wait until feed count reaches the required value, but do not
+ * exceed 30s */
+ do
+ {
+ status = sanei_genesys_get_status (dev, &val);
+ }
+ while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
+
+ /* then stop scanning */
+ RIE(gl847_stop_action (dev));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* init registers for shading calibration */
+static SANE_Status
+gl847_init_regs_for_shading (Genesys_Device * dev)
+{
+ SANE_Status status;
+ float move;
+
+ DBGSTART;
+ dev->calib_channels = 3;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ dev->calib_resolution = sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ dev->calib_lines = dev->model->shading_lines;
+ if(dev->calib_resolution==4800)
+ dev->calib_lines *= 2;
+ dev->calib_pixels = (dev->sensor.sensor_pixels*dev->calib_resolution)/dev->sensor.optical_res;
+ DBG (DBG_io, "%s: calib_lines = %d\n", __FUNCTION__, (int)dev->calib_lines);
+ DBG (DBG_io, "%s: calib_pixels = %d\n", __FUNCTION__, (int)dev->calib_pixels);
+
+ /* this is aworkaround insufficent distance for slope
+ * motor acceleration TODO special motor slope for shading */
+ move=1;
+ if(dev->calib_resolution<1200)
+ {
+ move=40;
+ }
+
+ status = gl847_init_scan_regs (dev,
+ dev->calib_reg,
+ dev->calib_resolution,
+ dev->calib_resolution,
+ 0,
+ move,
+ dev->calib_pixels,
+ dev->calib_lines,
+ 16,
+ dev->calib_channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ status = dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to bulk write registers: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ /* we use GENESYS_FLAG_SHADING_REPARK */
+ dev->scanhead_position_in_steps = 0;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief set up registers for the actual scan
+ */
+static SANE_Status
+gl847_init_regs_for_scan (Genesys_Device * dev)
+{
+ int channels;
+ int flags;
+ int depth;
+ float move;
+ int move_dpi;
+ float start;
+
+ SANE_Status status;
+
+ DBG (DBG_info,
+ "gl847_init_regs_for_scan settings:\nResolution: %uDPI\n"
+ "Lines : %u\nPPL : %u\nStartpos : %.3f/%.3f\nScan mode : %d\n\n",
+ dev->settings.yres, dev->settings.lines, dev->settings.pixels,
+ dev->settings.tl_x, dev->settings.tl_y, dev->settings.scan_mode);
+
+ /* channels */
+ if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */
+ channels = 3;
+ else
+ channels = 1;
+
+ /* depth */
+ depth = dev->settings.depth;
+ if (dev->settings.scan_mode == SCAN_MODE_LINEART)
+ depth = 1;
+
+
+ /* steps to move to reach scanning area:
+ - first we move to physical start of scanning
+ either by a fixed steps amount from the black strip
+ or by a fixed amount from parking position,
+ minus the steps done during shading calibration
+ - then we move by the needed offset whitin physical
+ scanning area
+
+ assumption: steps are expressed at maximum motor resolution
+
+ we need:
+ SANE_Fixed y_offset;
+ SANE_Fixed y_size;
+ SANE_Fixed y_offset_calib;
+ mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
+
+ /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
+ relative from origin, else, it is from parking position */
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = SANE_UNFIX (dev->model->y_offset);
+ move += dev->settings.tl_y;
+ move = (move * move_dpi) / MM_PER_INCH;
+ move -= dev->scanhead_position_in_steps;
+ DBG (DBG_info, "%s: move=%f steps\n",__FUNCTION__, move);
+
+ /* fast move to scan area */
+ /* we don't move fast the whole distance since it would involve
+ * computing acceleration/deceleration distance for scan
+ * resolution. So leave a remainder for it so scan makes the final
+ * move tuning */
+ if(channels*dev->settings.yres>=600 && move>700)
+ {
+ status = gl847_feed (dev, move-500);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to move to scan area\n",__FUNCTION__);
+ return status;
+ }
+ move=500;
+ }
+
+ DBG (DBG_info, "gl124_init_regs_for_scan: move=%f steps\n", move);
+ DBG (DBG_info, "%s: move=%f steps\n", __FUNCTION__, move);
+
+ /* start */
+ start = SANE_UNFIX (dev->model->x_offset);
+ start += dev->settings.tl_x;
+ start = (start * dev->sensor.optical_res) / MM_PER_INCH;
+
+ flags = 0;
+
+ /* emulated lineart from gray data is required for now */
+ if(dev->settings.scan_mode == SCAN_MODE_LINEART
+ && dev->settings.dynamic_lineart)
+ {
+ flags |= SCAN_FLAG_DYNAMIC_LINEART;
+ }
+
+ /* backtracking isn't handled well, so don't enable it */
+ flags |= SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
+
+ status = gl847_init_scan_regs (dev,
+ dev->reg,
+ dev->settings.xres,
+ dev->settings.yres,
+ start,
+ move,
+ dev->settings.pixels,
+ dev->settings.lines,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ flags);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+static SANE_Status
+gl847_send_shading_data (Genesys_Device * dev, uint8_t * data, int size)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint32_t addr, length, i, x, factor, pixels;
+ uint32_t dpiset, dpihw, strpixel, endpixel;
+ uint16_t tempo;
+ uint32_t lines, channels;
+ uint8_t val,*buffer,*ptr,*src;
+
+ DBGSTART;
+ DBG( DBG_io2, "%s: writing %d bytes of shading data\n",__FUNCTION__,size);
+
+ /* shading data is plit in 3 (up to 5 with IR) areas
+ write(0x10014000,0x00000dd8)
+ URB 23429 bulk_out len 3544 wrote 0x33 0x10 0x....
+ write(0x1003e000,0x00000dd8)
+ write(0x10068000,0x00000dd8)
+ */
+ length = (uint32_t) (size / 3);
+ sanei_genesys_get_double(dev->reg,REG_STRPIXEL,&tempo);
+ strpixel=tempo;
+ sanei_genesys_get_double(dev->reg,REG_ENDPIXEL,&tempo);
+ endpixel=tempo;
+
+ /* compute deletion factor */
+ sanei_genesys_get_double(dev->reg,REG_DPISET,&tempo);
+ dpiset=tempo;
+ DBG( DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d, DPISET=%d\n",__FUNCTION__,strpixel,endpixel,endpixel-strpixel,dpiset);
+ dpihw=sanei_genesys_compute_dpihw(dev,dpiset);
+ factor=dpihw/dpiset;
+ DBG( DBG_io2, "%s: factor=%d\n",__FUNCTION__,factor);
+
+ if(DBG_LEVEL>=DBG_data)
+ {
+ dev->binary=fopen("binary.pnm","wb");
+ sanei_genesys_get_triple(dev->reg, REG_LINCNT, &lines);
+ channels=dev->current_setup.channels;
+ if(dev->binary!=NULL)
+ {
+ fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels,lines/channels,255);
+ }
+ }
+
+ pixels=endpixel-strpixel;
+
+ /* since we're using SHDAREA, substract startx coordinate from shading */
+ strpixel-=((dev->sensor.CCD_start_xoffset*600)/dev->sensor.optical_res);
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2;
+ pixels*=2*2;
+
+ /* allocate temporary buffer */
+ buffer=(uint8_t *)malloc(pixels);
+ memset(buffer,0,pixels);
+ DBG( DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n",__FUNCTION__,pixels,pixels);
+
+ /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address
+ * is 8192*reg value */
+
+ /* write actual color channel data */
+ for(i=0;i<3;i++)
+ {
+ /* build up actual shading data by copying the part from the full width one
+ * to the one corresponding to SHDAREA */
+ ptr=buffer;
+
+ /* iterate on both sensor segment */
+ for(x=0;x<pixels;x+=4*factor)
+ {
+ /* coefficient source */
+ src=(data+strpixel+i*length)+x;
+
+ /* coefficient copy */
+ ptr[0]=src[0];
+ ptr[1]=src[1];
+ ptr[2]=src[2];
+ ptr[3]=src[3];
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+
+ RIE (sanei_genesys_read_register (dev, 0xd0+i, &val));
+ addr = val * 8192 + 0x10000000;
+ status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, addr, pixels, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gl847_send_shading_data; write to AHB failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ free(buffer);
+ DBGCOMPLETED;
+
+ return status;
+}
+
+/** @brief calibrates led exposure
+ * Calibrate exposure by scanning a white area until the used exposure gives
+ * data white enough.
+ * @param dev device to calibrate
+ */
+static SANE_Status
+gl847_led_calibration (Genesys_Device * dev)
+{
+ int num_pixels;
+ int total_size;
+ int used_res;
+ uint8_t *line;
+ int i, j;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int val;
+ int channels, depth;
+ int avg[3], top[3], bottom[3];
+ int turn;
+ char fn[20];
+ uint16_t exp[3];
+ Sensor_Profile *sensor;
+ float move;
+ SANE_Bool acceptable;
+
+ DBGSTART;
+
+ move = SANE_UNFIX (dev->model->y_offset_calib);
+ move = (move * (dev->motor.base_ydpi/4)) / MM_PER_INCH;
+ if(move>20)
+ {
+ RIE(gl847_feed (dev, move));
+ }
+ DBG (DBG_io, "%s: move=%f steps\n", __FUNCTION__, move);
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ depth=16;
+ used_res=sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ sensor=get_sensor_profile(dev->model->ccd_type, used_res);
+ num_pixels = (dev->sensor.sensor_pixels*used_res)/dev->sensor.optical_res;
+
+ /* initial calibration reg values */
+ memcpy (dev->calib_reg, dev->reg, GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ /* set up for the calibration scan */
+ status = gl847_init_scan_regs (dev,
+ dev->calib_reg,
+ used_res,
+ used_res,
+ 0,
+ 0,
+ num_pixels,
+ 1,
+ depth,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: failed to setup scan: %s\n", __FUNCTION__, sane_strstatus (status));
+ return status;
+ }
+
+ total_size = num_pixels * channels * (depth/8) * 1; /* colors * bytes_per_color * scan lines */
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ /* initial loop values and boundaries */
+ exp[0]=sensor->expr;
+ exp[1]=sensor->expg;
+ exp[2]=sensor->expb;
+
+ bottom[0]=29000;
+ bottom[1]=29000;
+ bottom[2]=29000;
+
+ top[0]=41000;
+ top[1]=51000;
+ top[2]=51000;
+
+ turn = 0;
+
+ /* no move during led calibration */
+ gl847_set_motor_power (dev->calib_reg, SANE_FALSE);
+ do
+ {
+ /* set up exposure */
+ sanei_genesys_set_double(dev->calib_reg,REG_EXPR,exp[0]);
+ sanei_genesys_set_double(dev->calib_reg,REG_EXPG,exp[1]);
+ sanei_genesys_set_double(dev->calib_reg,REG_EXPB,exp[2]);
+
+ /* write registers and scan data */
+ RIEF (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS), line);
+
+ DBG (DBG_info, "gl847_led_calibration: starting line reading\n");
+ RIEF (gl847_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ /* stop scanning */
+ RIEF (gl847_stop_action (dev), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf (fn, 20, "led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file (fn, line, depth, channels, num_pixels, 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG (DBG_info, "gl847_led_calibration: average: %d,%d,%d\n", avg[0], avg[1], avg[2]);
+
+ /* check if exposure gives average within the boundaries */
+ acceptable = SANE_TRUE;
+ for(i=0;i<3;i++)
+ {
+ if(avg[i]<bottom[i])
+ {
+ exp[i]=(exp[i]*bottom[i])/avg[i];
+ acceptable = SANE_FALSE;
+ }
+ if(avg[i]>top[i])
+ {
+ exp[i]=(exp[i]*top[i])/avg[i];
+ acceptable = SANE_FALSE;
+ }
+ }
+
+ turn++;
+ }
+ while (!acceptable && turn < 100);
+
+ DBG (DBG_info, "gl847_led_calibration: acceptable exposure: %d,%d,%d\n", exp[0], exp[1], exp[2]);
+
+ /* set these values as final ones for scan */
+ sanei_genesys_set_double(dev->reg,REG_EXPR,exp[0]);
+ sanei_genesys_set_double(dev->reg,REG_EXPG,exp[1]);
+ sanei_genesys_set_double(dev->reg,REG_EXPB,exp[2]);
+
+ /* store in this struct since it is the one used by cache calibration */
+ dev->sensor.regs_0x10_0x1d[0] = (exp[0] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[1] = exp[0] & 0xff;
+ dev->sensor.regs_0x10_0x1d[2] = (exp[1] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[3] = exp[1] & 0xff;
+ dev->sensor.regs_0x10_0x1d[4] = (exp[2] >> 8) & 0xff;
+ dev->sensor.regs_0x10_0x1d[5] = exp[2] & 0xff;
+
+ /* cleanup before return */
+ free (line);
+
+ /* go back home */
+ if(move>20)
+ {
+ status=gl847_slow_back_home (dev, SANE_TRUE);
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+ */
+static SANE_Status
+gl847_init_gpio (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx=0;
+
+ DBGSTART;
+
+ /* search GPIO profile */
+ while(gpios[idx].sensor_id!=0 && dev->model->gpo_type!=gpios[idx].sensor_id)
+ {
+ idx++;
+ }
+ if(gpios[idx].sensor_id==0)
+ {
+ DBG (DBG_error, "%s: failed to find GPIO profile for sensor_id=%d\n", __FUNCTION__, dev->model->ccd_type);
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (sanei_genesys_write_register (dev, REGA7, gpios[idx].ra7));
+ RIE (sanei_genesys_write_register (dev, REGA6, gpios[idx].ra6));
+
+ RIE (sanei_genesys_write_register (dev, REG6E, gpios[idx].r6e));
+ RIE (sanei_genesys_write_register (dev, REG6C, 0x00));
+
+ RIE (sanei_genesys_write_register (dev, REG6B, gpios[idx].r6b));
+ RIE (sanei_genesys_write_register (dev, REG6C, gpios[idx].r6c));
+ RIE (sanei_genesys_write_register (dev, REG6D, gpios[idx].r6d));
+ RIE (sanei_genesys_write_register (dev, REG6E, gpios[idx].r6e));
+ RIE (sanei_genesys_write_register (dev, REG6F, gpios[idx].r6f));
+
+ RIE (sanei_genesys_write_register (dev, REGA8, gpios[idx].ra8));
+ RIE (sanei_genesys_write_register (dev, REGA9, gpios[idx].ra9));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * set memory layout by filling values in dedicated registers
+ */
+static SANE_Status
+gl847_init_memory_layout (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int idx = 0;
+ uint8_t val;
+
+ DBG (DBG_proc, "gl847_init_memory_layout\n");
+
+ /* point to per model memory layout */
+ idx = 0;
+ if (strcmp (dev->model->name, "canon-lide-100") == 0)
+ {
+ idx = 0;
+ }
+ if (strcmp (dev->model->name, "canon-lide-200") == 0)
+ {
+ idx = 1;
+ }
+ if (strcmp (dev->model->name, "canon-5600f") == 0)
+ {
+ idx = 2;
+ }
+ if (strcmp (dev->model->name, "canon-lide-700f") == 0)
+ {
+ idx = 3;
+ }
+
+ /* CLKSET nd DRAMSEL */
+ val = layouts[idx].dramsel;
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].value = val;
+
+ /* prevent further writings by bulk write register */
+ dev->reg[reg_0x0b].address = 0x00;
+
+ /* setup base address for shading data. */
+ /* values must be multiplied by 8192=0x4000 to give address on AHB */
+ /* R-Channel shading bank0 address setting for CIS */
+ sanei_genesys_write_register (dev, 0xd0, layouts[idx].rd0);
+ /* G-Channel shading bank0 address setting for CIS */
+ sanei_genesys_write_register (dev, 0xd1, layouts[idx].rd1);
+ /* B-Channel shading bank0 address setting for CIS */
+ sanei_genesys_write_register (dev, 0xd2, layouts[idx].rd2);
+
+ /* setup base address for scanned data. */
+ /* values must be multiplied by 1024*2=0x0800 to give address on AHB */
+ /* R-Channel ODD image buffer 0x0124->0x92000 */
+ /* size for each buffer is 0x16d*1k word */
+ sanei_genesys_write_register (dev, 0xe0, layouts[idx].re0);
+ sanei_genesys_write_register (dev, 0xe1, layouts[idx].re1);
+ /* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
+ sanei_genesys_write_register (dev, 0xe2, layouts[idx].re2);
+ sanei_genesys_write_register (dev, 0xe3, layouts[idx].re3);
+
+ /* R-Channel EVEN image buffer 0x0292 */
+ sanei_genesys_write_register (dev, 0xe4, layouts[idx].re4);
+ sanei_genesys_write_register (dev, 0xe5, layouts[idx].re5);
+ /* R-Channel EVEN image buffer end-address 0x03ff*/
+ sanei_genesys_write_register (dev, 0xe6, layouts[idx].re6);
+ sanei_genesys_write_register (dev, 0xe7, layouts[idx].re7);
+
+ /* same for green, since CIS, same addresses */
+ sanei_genesys_write_register (dev, 0xe8, layouts[idx].re0);
+ sanei_genesys_write_register (dev, 0xe9, layouts[idx].re1);
+ sanei_genesys_write_register (dev, 0xea, layouts[idx].re2);
+ sanei_genesys_write_register (dev, 0xeb, layouts[idx].re3);
+ sanei_genesys_write_register (dev, 0xec, layouts[idx].re4);
+ sanei_genesys_write_register (dev, 0xed, layouts[idx].re5);
+ sanei_genesys_write_register (dev, 0xee, layouts[idx].re6);
+ sanei_genesys_write_register (dev, 0xef, layouts[idx].re7);
+
+/* same for blue, since CIS, same addresses */
+ sanei_genesys_write_register (dev, 0xf0, layouts[idx].re0);
+ sanei_genesys_write_register (dev, 0xf1, layouts[idx].re1);
+ sanei_genesys_write_register (dev, 0xf2, layouts[idx].re2);
+ sanei_genesys_write_register (dev, 0xf3, layouts[idx].re3);
+ sanei_genesys_write_register (dev, 0xf4, layouts[idx].re4);
+ sanei_genesys_write_register (dev, 0xf5, layouts[idx].re5);
+ sanei_genesys_write_register (dev, 0xf6, layouts[idx].re6);
+ sanei_genesys_write_register (dev, 0xf7, layouts[idx].re7);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/* *
+ * initialize ASIC from power on condition
+ */
+static SANE_Status
+gl847_boot (Genesys_Device * dev, SANE_Bool cold)
+{
+ SANE_Status status;
+ uint8_t val;
+
+ DBGSTART;
+
+ /* reset ASIC if cold boot */
+ if(cold)
+ {
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
+ RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
+ }
+
+ /* test CHKVER */
+ RIE (sanei_genesys_read_register (dev, REG40, &val));
+ if (val & REG40_CHKVER)
+ {
+ RIE (sanei_genesys_read_register (dev, 0x00, &val));
+ DBG (DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __FUNCTION__, val);
+ }
+
+ /* Set default values for registers */
+ gl847_init_registers (dev);
+
+ /* Write initial registers */
+ RIE (dev->model->cmd_set->bulk_write_register (dev, dev->reg, GENESYS_GL847_MAX_REGS));
+
+ /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
+ val = dev->reg[reg_0x0b].value & REG0B_DRAMSEL;
+ val = (val | REG0B_ENBDRAM);
+ RIE (sanei_genesys_write_register (dev, REG0B, val));
+ dev->reg[reg_0x0b].value = val;
+
+ /* CIS_LINE */
+ SETREG (0x08, REG08_CIS_LINE);
+ RIE (sanei_genesys_write_register (dev, 0x08, dev->reg[reg_0x08].value));
+
+ /* set up end access */
+ RIE (sanei_genesys_write_0x8c (dev, 0x10, 0x0b));
+ RIE (sanei_genesys_write_0x8c (dev, 0x13, 0x0e));
+
+ /* setup gpio */
+ RIE (gl847_init_gpio (dev));
+
+ /* setup internal memory layout */
+ RIE (gl847_init_memory_layout (dev));
+
+ SETREG (0xf8, 0x01);
+ RIE (sanei_genesys_write_register (dev, 0xf8, dev->reg[reg_0xf8].value));
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl847_init (Genesys_Device * dev)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+ DBGSTART;
+
+ status=sanei_genesys_asic_init(dev, GENESYS_GL847_MAX_REGS);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+static SANE_Status
+gl847_update_hardware_sensors (Genesys_Scanner * s)
+{
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t val;
+ uint8_t scan, file, email, copy;
+ switch(s->dev->model->gpo_type)
+ {
+ case GPO_CANONLIDE700:
+ scan=0x04;
+ file=0x02;
+ email=0x01;
+ copy=0x08;
+ break;
+ default:
+ scan=0x01;
+ file=0x02;
+ email=0x04;
+ copy=0x08;
+ }
+ RIE (sanei_genesys_read_register (s->dev, REG6D, &val));
+
+ if (s->val[OPT_SCAN_SW].b == s->last_val[OPT_SCAN_SW].b)
+ s->val[OPT_SCAN_SW].b = (val & scan) == 0;
+ if (s->val[OPT_FILE_SW].b == s->last_val[OPT_FILE_SW].b)
+ s->val[OPT_FILE_SW].b = (val & file) == 0;
+ if (s->val[OPT_EMAIL_SW].b == s->last_val[OPT_EMAIL_SW].b)
+ s->val[OPT_EMAIL_SW].b = (val & email) == 0;
+ if (s->val[OPT_COPY_SW].b == s->last_val[OPT_COPY_SW].b)
+ s->val[OPT_COPY_SW].b = (val & copy) == 0;
+
+ return status;
+}
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
+ * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
+ * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
+ */
+static SANE_Status
+gl847_search_strip (Genesys_Device * dev, SANE_Bool forward, SANE_Bool black)
+{
+ unsigned int pixels, lines, channels;
+ SANE_Status status;
+ Genesys_Register_Set local_reg[GENESYS_GL847_MAX_REGS];
+ size_t size;
+ uint8_t *data;
+ int steps, depth, dpi;
+ unsigned int pass, count, found, x, y;
+ char title[80];
+ Genesys_Register_Set *r;
+
+ DBG (DBG_proc, "gl847_search_strip %s %s\n", black ? "black" : "white",
+ forward ? "forward" : "reverse");
+
+ gl847_set_fe (dev, AFE_SET);
+ status = gl847_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_search_strip: failed to stop: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for a gray scan at lowest dpi */
+ dpi = 9600;
+ for (x = 0; x < MAX_RESOLUTIONS; x++)
+ {
+ if (dev->model->xdpi_values[x] > 0 && dev->model->xdpi_values[x] < dpi)
+ dpi = dev->model->xdpi_values[x];
+ }
+ channels = 1;
+ /* 10 MM */
+ /* lines = (10 * dpi) / MM_PER_INCH; */
+ /* shading calibation is done with dev->motor.base_ydpi */
+ lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
+ depth = 8;
+ pixels = (dev->sensor.sensor_pixels * dpi) / dev->sensor.optical_res;
+ size = pixels * channels * lines * (depth / 8);
+ data = malloc (size);
+ if (!data)
+ {
+ DBG (DBG_error, "gl847_search_strip: failed to allocate memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->scanhead_position_in_steps = 0;
+
+ memcpy (local_reg, dev->reg,
+ GENESYS_GL847_MAX_REGS * sizeof (Genesys_Register_Set));
+
+ status = gl847_init_scan_regs (dev,
+ local_reg,
+ dpi,
+ dpi,
+ 0,
+ 0,
+ pixels,
+ lines,
+ depth,
+ channels,
+ 0,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl847_search_strip: failed to setup for scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* set up for reverse or forward */
+ r = sanei_genesys_get_address (local_reg, REG02);
+ if (forward)
+ r->value &= ~REG02_MTRREV;
+ else
+ r->value |= REG02_MTRREV;
+
+
+ status = dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL847_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error,
+ "gl847_search_strip: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl847_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl847_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl847_search_strip: gl847_stop_action failed\n");
+ return status;
+ }
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels, pixels,
+ lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < 20 && !found)
+ {
+ status =
+ dev->model->cmd_set->bulk_write_register (dev, local_reg, GENESYS_GL847_MAX_REGS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_search_strip: Failed to bulk write registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* now start scan */
+ status = gl847_begin_scan (dev, local_reg, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_strip: failed to begin scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* waits for valid data */
+ do
+ sanei_genesys_test_buffer_empty (dev, &steps);
+ while (steps);
+
+ /* now we're on target, we can read data */
+ status = sanei_genesys_read_data_from_scanner (dev, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error,
+ "gl847_search_start_position: failed to read data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gl847_stop_action (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (data);
+ DBG (DBG_error, "gl847_search_strip: gl847_stop_action failed\n");
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
+ sanei_genesys_write_pnm_file (title, data, depth, channels,
+ pixels, lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl847_search_strip: strip found forward during pass %d at line %d\n",
+ pass, y);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl847_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG (DBG_data,
+ "gl847_search_strip: strip found backward during pass %d \n",
+ pass);
+ }
+ else
+ {
+ DBG (DBG_data,
+ "gl847_search_strip: pixels=%d, count=%d (%d%%)\n",
+ pixels, count, (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+ free (data);
+ if (found)
+ {
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_info, "gl847_search_strip: %s strip found\n",
+ black ? "black" : "white");
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ DBG (DBG_info, "gl847_search_strip: %s strip not found\n",
+ black ? "black" : "white");
+ }
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * average dark pixels of a 8 bits scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG (DBG_info, "dark_average: avg[%d] = %d\n", k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG (DBG_info, "dark_average: average = %d\n", average);
+ return average;
+}
+
+static SANE_Status
+gl847_offset_calibration (Genesys_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ uint8_t *first_line, *second_line, reg04;
+ unsigned int channels, bpp;
+ char title[32];
+ int pass = 0, avg, total_size;
+ int topavg, bottomavg, resolution, lines;
+ int top, bottom, black_pixels, pixels;
+
+ DBGSTART;
+
+ /* no gain nor offset for AKM AFE */
+ RIE (sanei_genesys_read_register (dev, REG04, &reg04));
+ if ((reg04 & REG04_FESET) == 0x02)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ resolution=dev->sensor.optical_res;
+ dev->calib_pixels = dev->sensor.sensor_pixels;
+ lines=1;
+ bpp=8;
+ pixels= (dev->sensor.sensor_pixels*resolution) / dev->sensor.optical_res;
+ black_pixels = (dev->sensor.black_pixels * resolution) / dev->sensor.optical_res;
+ DBG (DBG_io2, "gl847_offset_calibration: black_pixels=%d\n", black_pixels);
+
+ status = gl847_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_offset_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gl847_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ /* allocate memory for scans */
+ total_size = pixels * channels * lines * (bpp/8); /* colors * bytes_per_color * scan lines */
+
+ first_line = malloc (total_size);
+ if (!first_line)
+ return SANE_STATUS_NO_MEM;
+
+ second_line = malloc (total_size);
+ if (!second_line)
+ {
+ free (first_line);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* init gain */
+ dev->frontend.gain[0] = 0;
+ dev->frontend.gain[1] = 0;
+ dev->frontend.gain[2] = 0;
+
+ /* scan with no move */
+ bottom = 10;
+ dev->frontend.offset[0] = bottom;
+ dev->frontend.offset[1] = bottom;
+ dev->frontend.offset[2] = bottom;
+
+ RIEF2 (gl847_set_fe(dev, AFE_SET),first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS),first_line, second_line);
+ DBG (DBG_info, "gl847_offset_calibration: starting first line reading\n");
+ RIEF2 (gl847_begin_scan (dev, dev->calib_reg, SANE_TRUE),first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, first_line, total_size),first_line, second_line);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ snprintf(title,20,"offset%03d.pnm",bottom);
+ sanei_genesys_write_pnm_file (title, first_line, bpp, channels, pixels, lines);
+ }
+
+ bottomavg = dark_average (first_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_io2, "gl847_offset_calibration: bottom avg=%d\n", bottomavg);
+
+ /* now top value */
+ top = 255;
+ dev->frontend.offset[0] = top;
+ dev->frontend.offset[1] = top;
+ dev->frontend.offset[2] = top;
+ RIEF2 (gl847_set_fe(dev, AFE_SET),first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS),first_line, second_line);
+ DBG (DBG_info, "gl847_offset_calibration: starting second line reading\n");
+ RIEF2 (gl847_begin_scan (dev, dev->calib_reg, SANE_TRUE),first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size),first_line, second_line);
+
+ topavg = dark_average (second_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_io2, "gl847_offset_calibration: top avg=%d\n", topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.offset[0] = (top + bottom) / 2;
+ dev->frontend.offset[1] = (top + bottom) / 2;
+ dev->frontend.offset[2] = (top + bottom) / 2;
+
+ /* scan with no move */
+ RIEF2 (gl847_set_fe(dev, AFE_SET),first_line, second_line);
+ RIEF2 (dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS),first_line, second_line);
+ DBG (DBG_info, "gl847_offset_calibration: starting second line reading\n");
+ RIEF2 (gl847_begin_scan (dev, dev->calib_reg, SANE_TRUE),first_line, second_line);
+ RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size),first_line, second_line);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sprintf (title, "offset%03d.pnm", dev->frontend.offset[1]);
+ sanei_genesys_write_pnm_file (title, second_line, bpp, channels, pixels, lines);
+ }
+
+ avg = dark_average (second_line, pixels, lines, channels, black_pixels);
+ DBG (DBG_info, "gl847_offset_calibration: avg=%d offset=%d\n", avg,
+ dev->frontend.offset[1]);
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.offset[1];
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.offset[1];
+ }
+ }
+ DBG (DBG_info, "gl847_offset_calibration: offset=(%d,%d,%d)\n", dev->frontend.offset[0], dev->frontend.offset[1], dev->frontend.offset[2]);
+
+ /* cleanup before return */
+ free (first_line);
+ free (second_line);
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gl847_coarse_gain_calibration (Genesys_Device * dev, int dpi)
+{
+ int pixels;
+ int total_size;
+ uint8_t *line, reg04;
+ int i, j, channels;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+ int resolution;
+ int bpp;
+
+ DBG (DBG_proc, "gl847_coarse_gain_calibration: dpi = %d\n", dpi);
+
+ /* no gain nor offset for AKM AFE */
+ RIE (sanei_genesys_read_register (dev, REG04, &reg04));
+ if ((reg04 & REG04_FESET) == 0x02)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ /* follow CKSEL */
+ if(dev->settings.xres<dev->sensor.optical_res)
+ {
+ coeff=0.9;
+ /*resolution=dev->sensor.optical_res/2; */
+ resolution=dev->sensor.optical_res;
+ }
+ else
+ {
+ resolution=dev->sensor.optical_res;
+ coeff=1.0;
+ }
+ lines=10;
+ bpp=8;
+ pixels = (dev->sensor.sensor_pixels * resolution) / dev->sensor.optical_res;
+
+ status = gl847_init_scan_regs (dev,
+ dev->calib_reg,
+ resolution,
+ resolution,
+ 0,
+ 0,
+ pixels,
+ lines,
+ bpp,
+ channels,
+ dev->settings.color_filter,
+ SCAN_FLAG_DISABLE_SHADING |
+ SCAN_FLAG_DISABLE_GAMMA |
+ SCAN_FLAG_SINGLE_LINE |
+ SCAN_FLAG_IGNORE_LINE_DISTANCE);
+ gl847_set_motor_power (dev->calib_reg, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "gl847_coarse_calibration: failed to setup scan: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ RIE (dev->model->cmd_set->bulk_write_register
+ (dev, dev->calib_reg, GENESYS_GL847_MAX_REGS));
+
+ total_size = pixels * channels * (16/bpp) * lines;
+
+ line = malloc (total_size);
+ if (!line)
+ return SANE_STATUS_NO_MEM;
+
+ RIEF (gl847_set_fe(dev, AFE_SET), line);
+ RIEF (gl847_begin_scan (dev, dev->calib_reg, SANE_TRUE), line);
+ RIEF (sanei_genesys_read_data_from_scanner (dev, line, total_size), line);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file ("coarse.pnm", line, bpp, channels, pixels, lines);
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if(bpp==16)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * pixels + 1] * 256 +
+ line[i * 2 + j * 2 * pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ }
+ else
+ {
+ if (dev->model->is_cis)
+ val = line[i + j * pixels];
+ else
+ val = line[i * channels + j];
+ }
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = ((float) dev->sensor.gain_white_ref*coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = 283 - 208 / gain[j];
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.gain[j] = code;
+
+ DBG (DBG_proc,
+ "gl847_coarse_gain_calibration: channel %d, max=%d, gain = %f, setting:%d\n",
+ j, max[j], gain[j], dev->frontend.gain[j]);
+ }
+
+ if (dev->model->is_cis)
+ {
+ if (dev->frontend.gain[0] > dev->frontend.gain[1])
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ if (dev->frontend.gain[0] > dev->frontend.gain[2])
+ dev->frontend.gain[0] = dev->frontend.gain[2];
+ dev->frontend.gain[2] = dev->frontend.gain[1] = dev->frontend.gain[0];
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.gain[0] = dev->frontend.gain[1];
+ dev->frontend.gain[2] = dev->frontend.gain[1];
+ }
+
+ free (line);
+
+ RIE (gl847_stop_action (dev));
+
+ status=gl847_slow_back_home (dev, SANE_TRUE);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+
+/** the gl847 command set */
+static Genesys_Command_Set gl847_cmd_set = {
+ "gl847-generic", /* the name of this set */
+
+ gl847_init,
+ NULL, /*gl847_init_regs_for_warmup*/
+ gl847_init_regs_for_coarse_calibration,
+ gl847_init_regs_for_shading,
+ gl847_init_regs_for_scan,
+
+ gl847_get_filter_bit,
+ gl847_get_lineart_bit,
+ gl847_get_bitset_bit,
+ gl847_get_gain4_bit,
+ gl847_get_fast_feed_bit,
+ gl847_test_buffer_empty_bit,
+ gl847_test_motor_flag_bit,
+
+ gl847_bulk_full_size,
+
+ gl847_set_fe,
+ gl847_set_powersaving,
+ gl847_save_power,
+
+ gl847_set_motor_power,
+ gl847_set_lamp_power,
+
+ gl847_begin_scan,
+ gl847_end_scan,
+
+ sanei_genesys_send_gamma_table,
+
+ gl847_search_start_position,
+
+ gl847_offset_calibration,
+ gl847_coarse_gain_calibration,
+ gl847_led_calibration,
+
+ gl847_slow_back_home,
+
+ sanei_genesys_bulk_write_register,
+ NULL,
+ gl847_bulk_read_data,
+
+ gl847_update_hardware_sensors,
+
+ NULL, /* no known gl847 sheetfed scanner */
+ NULL, /* no known gl847 sheetfed scanner */
+ NULL, /* no known gl847 sheetfed scanner */
+ gl847_search_strip,
+
+ sanei_genesys_is_compatible_calibration,
+ NULL,
+ gl847_send_shading_data,
+ gl847_calculate_current_setup,
+ gl847_boot
+};
+
+SANE_Status
+sanei_gl847_init_cmd_set (Genesys_Device * dev)
+{
+ dev->model->cmd_set = &gl847_cmd_set;
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_gl847.h b/backend/genesys_gl847.h
new file mode 100644
index 0000000..d787f43
--- /dev/null
+++ b/backend/genesys_gl847.h
@@ -0,0 +1,695 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+
+
+#ifdef UNIT_TESTING
+SANE_Status gl847_stop_action (Genesys_Device * dev);
+SANE_Status gl847_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
+#endif
+
+#define REG01 0x01
+#define REG01_CISSET 0x80
+#define REG01_DOGENB 0x40
+#define REG01_DVDSET 0x20
+#define REG01_STAGGER 0x10
+#define REG01_COMPENB 0x08
+#define REG01_TRUEGRAY 0x04
+#define REG01_SHDAREA 0x02
+#define REG01_SCAN 0x01
+
+#define REG02 0x02
+#define REG02_NOTHOME 0x80
+#define REG02_ACDCDIS 0x40
+#define REG02_AGOHOME 0x20
+#define REG02_MTRPWR 0x10
+#define REG02_FASTFED 0x08
+#define REG02_MTRREV 0x04
+#define REG02_HOMENEG 0x02
+#define REG02_LONGCURV 0x01
+
+#define REG03 0x03
+#define REG03_LAMPDOG 0x80
+#define REG03_AVEENB 0x40
+#define REG03_XPASEL 0x20
+#define REG03_LAMPPWR 0x10
+#define REG03_LAMPTIM 0x0f
+
+#define REG04 0x04
+#define REG04_LINEART 0x80
+#define REG04_BITSET 0x40
+#define REG04_AFEMOD 0x30
+#define REG04_FILTER 0x0c
+#define REG04_FESET 0x03
+
+#define REG04S_AFEMOD 4
+
+#define REG05 0x05
+#define REG05_DPIHW 0xc0
+#define REG05_DPIHW_600 0x00
+#define REG05_DPIHW_1200 0x40
+#define REG05_DPIHW_2400 0x80
+#define REG05_DPIHW_4800 0xc0
+#define REG05_MTLLAMP 0x30
+#define REG05_GMMENB 0x08
+#define REG05_MTLBASE 0x03
+
+#define REG06_SCANMOD 0xe0
+#define REG06S_SCANMOD 5
+#define REG06_PWRBIT 0x10
+#define REG06_GAIN4 0x08
+#define REG06_OPTEST 0x07
+
+#define REG07_LAMPSIM 0x80
+
+#define REG08_DRAM2X 0x80
+#define REG08_MPENB 0x20
+#define REG08_CIS_LINE 0x10
+#define REG08_IR1ENB 0x08
+#define REG08_IR2ENB 0x04
+#define REG08_ENB24M 0x01
+
+#define REG09_MCNTSET 0xc0
+#define REG09_EVEN1ST 0x20
+#define REG09_BLINE1ST 0x10
+#define REG09_BACKSCAN 0x08
+#define REG09_ENHANCE 0x04
+#define REG09_SHORTTG 0x02
+#define REG09_NWAIT 0x01
+
+#define REG09S_MCNTSET 6
+#define REG09S_CLKSET 4
+
+
+#define REG0A_LPWMEN 0x10
+
+#define REG0B 0x0b
+#define REG0B_DRAMSEL 0x07
+#define REG0B_ENBDRAM 0x08
+#define REG0B_ENBDRAM 0x08
+#define REG0B_RFHDIS 0x10
+#define REG0B_CLKSET 0xe0
+#define REG0B_24MHZ 0x00
+#define REG0B_30MHZ 0x20
+#define REG0B_40MHZ 0x40
+#define REG0B_48MHZ 0x60
+#define REG0B_60MHZ 0x80
+
+#define REG0C 0x0c
+#define REG0C_CCDLMT 0x0f
+
+#define REG0D 0x0d
+#define REG0D_FULLSTP 0x10
+#define REG0D_SEND 0x80
+#define REG0D_CLRMCNT 0x04
+#define REG0D_CLRDOCJM 0x02
+#define REG0D_CLRLNCNT 0x01
+
+#define REG0F 0x0f
+
+#define REG16_CTRLHI 0x80
+#define REG16_TOSHIBA 0x40
+#define REG16_TGINV 0x20
+#define REG16_CK1INV 0x10
+#define REG16_CK2INV 0x08
+#define REG16_CTRLINV 0x04
+#define REG16_CKDIS 0x02
+#define REG16_CTRLDIS 0x01
+
+#define REG17_TGMODE 0xc0
+#define REG17_TGMODE_NO_DUMMY 0x00
+#define REG17_TGMODE_REF 0x40
+#define REG17_TGMODE_XPA 0x80
+#define REG17_TGW 0x3f
+#define REG17S_TGW 0
+
+#define REG18 0x18
+#define REG18_CNSET 0x80
+#define REG18_DCKSEL 0x60
+#define REG18_CKTOGGLE 0x10
+#define REG18_CKDELAY 0x0c
+#define REG18_CKSEL 0x03
+
+#define REG1A_SW2SET 0x80
+#define REG1A_SW1SET 0x40
+#define REG1A_MANUAL3 0x02
+#define REG1A_MANUAL1 0x01
+#define REG1A_CK4INV 0x08
+#define REG1A_CK3INV 0x04
+#define REG1A_LINECLP 0x02
+
+#define REG1C 0x1c
+#define REG1C_TGTIME 0x07
+
+#define REG1D_CK4LOW 0x80
+#define REG1D_CK3LOW 0x40
+#define REG1D_CK1LOW 0x20
+#define REG1D_TGSHLD 0x1f
+#define REG1DS_TGSHLD 0
+
+
+#define REG1E_WDTIME 0xf0
+#define REG1ES_WDTIME 4
+#define REG1E_LINESEL 0x0f
+#define REG1ES_LINESEL 0
+
+#define REG_FEDCNT 0x1f
+
+#define REG24 0x1c
+#define REG40 0x40
+#define REG40_CHKVER 0x10
+#define REG40_HISPDFLG 0x04
+#define REG40_MOTMFLG 0x02
+#define REG40_DATAENB 0x01
+
+#define REG41_PWRBIT 0x80
+#define REG41_BUFEMPTY 0x40
+#define REG41_FEEDFSH 0x20
+#define REG41_SCANFSH 0x10
+#define REG41_HOMESNR 0x08
+#define REG41_LAMPSTS 0x04
+#define REG41_FEBUSY 0x02
+#define REG41_MOTORENB 0x01
+
+#define REG58_VSMP 0xf8
+#define REG58S_VSMP 3
+#define REG58_VSMPW 0x07
+#define REG58S_VSMPW 0
+
+#define REG59_BSMP 0xf8
+#define REG59S_BSMP 3
+#define REG59_BSMPW 0x07
+#define REG59S_BSMPW 0
+
+#define REG5A_ADCLKINV 0x80
+#define REG5A_RLCSEL 0x40
+#define REG5A_CDSREF 0x30
+#define REG5AS_CDSREF 4
+#define REG5A_RLC 0x0f
+#define REG5AS_RLC 0
+
+#define REG5E_DECSEL 0xe0
+#define REG5ES_DECSEL 5
+#define REG5E_STOPTIM 0x1f
+#define REG5ES_STOPTIM 0
+
+#define REG60 0x60
+#define REG60_Z1MOD 0x1f
+#define REG61 0x61
+#define REG61_Z1MOD 0xff
+#define REG62 0x62
+#define REG62_Z1MOD 0xff
+
+#define REG63 0x63
+#define REG63_Z2MOD 0x1f
+#define REG64 0x64
+#define REG64_Z2MOD 0xff
+#define REG65 0x65
+#define REG65_Z2MOD 0xff
+
+#define REG60S_STEPSEL 5
+#define REG60_STEPSEL 0xe0
+#define REG60_FULLSTEP 0x00
+#define REG60_HALFSTEP 0x20
+#define REG60_EIGHTHSTEP 0x60
+#define REG60_16THSTEP 0x80
+
+#define REG63S_FSTPSEL 5
+#define REG63_FSTPSEL 0xe0
+#define REG63_FULLSTEP 0x00
+#define REG63_HALFSTEP 0x20
+#define REG63_EIGHTHSTEP 0x60
+#define REG63_16THSTEP 0x80
+
+#define REG67 0x67
+#define REG67_MTRPWM 0x80
+
+#define REG68 0x68
+#define REG68_FASTPWM 0x80
+
+#define REG6B 0x6b
+#define REG6B_MULTFILM 0x80
+#define REG6B_GPOM13 0x40
+#define REG6B_GPOM12 0x20
+#define REG6B_GPOM11 0x10
+#define REG6B_GPO18 0x02
+#define REG6B_GPO17 0x01
+
+#define REG6C 0x6c
+#define REG6C_GPIO16 0x80
+#define REG6C_GPIO15 0x40
+#define REG6C_GPIO14 0x20
+#define REG6C_GPIO13 0x10
+#define REG6C_GPIO12 0x08
+#define REG6C_GPIO11 0x04
+#define REG6C_GPIO10 0x02
+#define REG6C_GPIO9 0x01
+#define REG6C_GPIOH 0xff
+#define REG6C_GPIOL 0xff
+
+#define REG6D 0x6d
+#define REG6E 0x6e
+#define REG6F 0x6f
+#define REG7E 0x7e
+
+#define REG87_LEDADD 0x04
+
+#define REG9E 0x9e
+#define REG9F 0x9f
+
+#define REGA6 0xa6
+#define REGA7 0xa7
+#define REGA8 0xa8
+#define REGA9 0xa9
+#define REGAB 0xab
+
+#define REG_EXPR 0x10
+#define REG_EXPG 0x12
+#define REG_EXPB 0x14
+#define REG_EXPDMY 0x19
+#define REG_STEPNO 0x21
+#define REG_FWDSTEP 0x22
+#define REG_BWDSTEP 0x23
+#define REG_FASTNO 0x24
+#define REG_DPISET 0x2c
+#define REG_STRPIXEL 0x30
+#define REG_ENDPIXEL 0x32
+#define REG_LINCNT 0x25
+#define REG_MAXWD 0x35
+#define REG_LPERIOD 0x38
+#define REG_FEEDL 0x3d
+#define REG_FMOVDEC 0x5f
+#define REG_FSHDEC 0x69
+#define REG_FMOVNO 0x6a
+#define REG_CK1MAP 0x74
+#define REG_CK3MAP 0x77
+#define REG_CK4MAP 0x7a
+
+/**
+ * writable scanner registers */
+enum
+{
+ reg_0x01 = 0,
+ reg_0x02,
+ reg_0x03,
+ reg_0x04,
+ reg_0x05,
+ reg_0x06,
+ reg_0x08,
+ reg_0x09,
+ reg_0x0a,
+ reg_0x0b,
+ reg_0x0c,
+ reg_0x0d,
+ reg_0x0e,
+ reg_0x0f,
+ reg_0x10,
+ reg_0x11,
+ reg_0x12,
+ reg_0x13,
+ reg_0x14,
+ reg_0x15,
+ reg_0x16,
+ reg_0x17,
+ reg_0x18,
+ reg_0x19,
+ reg_0x1a,
+ reg_0x1b,
+ reg_0x1c,
+ reg_0x1d,
+ reg_0x1e,
+ reg_0x1f,
+ reg_0x20,
+ reg_0x21,
+ reg_0x22,
+ reg_0x23,
+ reg_0x24,
+ reg_0x25,
+ reg_0x26,
+ reg_0x27,
+ reg_0x2c,
+ reg_0x2d,
+ reg_0x2e,
+ reg_0x2f,
+ reg_0x30,
+ reg_0x31,
+ reg_0x32,
+ reg_0x33,
+ reg_0x34,
+ reg_0x35,
+ reg_0x36,
+ reg_0x37,
+ reg_0x38,
+ reg_0x39,
+ reg_0x3a,
+ reg_0x3b,
+ reg_0x3d,
+ reg_0x3e,
+ reg_0x3f,
+ reg_0x51,
+ reg_0x52,
+ reg_0x53,
+ reg_0x54,
+ reg_0x55,
+ reg_0x56,
+ reg_0x57,
+ reg_0x58,
+ reg_0x59,
+ reg_0x5a,
+ reg_0x5e,
+ reg_0x5f,
+ reg_0x60,
+ reg_0x61,
+ reg_0x62,
+ reg_0x63,
+ reg_0x64,
+ reg_0x65,
+ reg_0x67,
+ reg_0x68,
+ reg_0x69,
+ reg_0x6a,
+ reg_0x6b,
+ reg_0x6c,
+ reg_0x6d,
+ reg_0x6e,
+ reg_0x6f,
+ reg_0x74,
+ reg_0x75,
+ reg_0x76,
+ reg_0x77,
+ reg_0x78,
+ reg_0x79,
+ reg_0x7a,
+ reg_0x7b,
+ reg_0x7c,
+ reg_0x7d,
+ reg_0x87,
+ reg_0x9d,
+ reg_0xa2,
+ reg_0xa3,
+ reg_0xa4,
+ reg_0xa5,
+ reg_0xa6,
+ reg_0xa7,
+ reg_0xa8,
+ reg_0xa9,
+ reg_0xbd,
+ reg_0xbe,
+ reg_0xc5,
+ reg_0xc6,
+ reg_0xc7,
+ reg_0xc8,
+ reg_0xc9,
+ reg_0xca,
+ reg_0xd0,
+ reg_0xd1,
+ reg_0xd2,
+ reg_0xe0,
+ reg_0xe1,
+ reg_0xe2,
+ reg_0xe3,
+ reg_0xe4,
+ reg_0xe5,
+ reg_0xe6,
+ reg_0xe7,
+ reg_0xe8,
+ reg_0xe9,
+ reg_0xea,
+ reg_0xeb,
+ reg_0xec,
+ reg_0xed,
+ reg_0xee,
+ reg_0xef,
+ reg_0xf0,
+ reg_0xf1,
+ reg_0xf2,
+ reg_0xf3,
+ reg_0xf4,
+ reg_0xf5,
+ reg_0xf6,
+ reg_0xf7,
+ reg_0xf8,
+ reg_0xfe,
+ GENESYS_GL847_MAX_REGS
+};
+
+#define SETREG(adr,val) {dev->reg[reg_##adr].address=adr;dev->reg[reg_##adr].value=val;}
+
+/** set up registers for an actual scan
+ *
+ * this function sets up the scanner to scan in normal or single line mode
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl847_init_scan_regs (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ float xres, /*dpi */
+ float yres, /*dpi */
+ float startx, /*optical_res, from dummy_pixel+1 */
+ float starty, /*base_ydpi, from home! */
+ float pixels,
+ float lines,
+ unsigned int depth,
+ unsigned int channels,
+ int color_filter,
+ unsigned int flags);
+
+/* Send the low-level scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl847_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool start_motor);
+
+/* Send the stop scan command */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl847_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool check_stop);
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status gl847_init (Genesys_Device * dev);
+
+/** @brief moves the slider to steps at motor base dpi
+ * @param dev device to work on
+ * @param steps number of steps to move
+ * */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+gl847_feed (Genesys_Device * dev, unsigned int steps);
+
+typedef struct
+{
+ uint8_t sensor_id;
+ uint8_t r6b;
+ uint8_t r6c;
+ uint8_t r6d;
+ uint8_t r6e;
+ uint8_t r6f;
+ uint8_t ra6;
+ uint8_t ra7;
+ uint8_t ra8;
+ uint8_t ra9;
+} Gpio_Profile;
+
+static Gpio_Profile gpios[]={
+ { GPO_CANONLIDE200, 0x02, 0xf9, 0x20, 0xff, 0x00, 0x04, 0x04, 0x00, 0x00},
+ { GPO_CANONLIDE700, 0x06, 0xdb, 0xff, 0xff, 0x80, 0x15, 0x07, 0x20, 0x10},
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+typedef struct
+{
+ uint8_t dramsel;
+ uint8_t rd0;
+ uint8_t rd1;
+ uint8_t rd2;
+ uint8_t re0;
+ uint8_t re1;
+ uint8_t re2;
+ uint8_t re3;
+ uint8_t re4;
+ uint8_t re5;
+ uint8_t re6;
+ uint8_t re7;
+} Memory_layout;
+
+static Memory_layout layouts[]={
+ /* LIDE 100 */
+ {
+ 0x29,
+ 0x0a, 0x15, 0x20,
+ 0x00, 0xac, 0x02, 0x55, 0x02, 0x56, 0x03, 0xff
+ },
+ /* LIDE 200 */
+ {
+ 0x29,
+ 0x0a, 0x1f, 0x34,
+ 0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
+ },
+ /* 5600F */
+ {
+ 0x29,
+ 0x0a, 0x1f, 0x34,
+ 0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
+ },
+ /* LIDE 700F */
+ {
+ 0x2a,
+ 0x0a, 0x33, 0x5c,
+ 0x02, 0x14, 0x09, 0x09, 0x09, 0x0a, 0x0f, 0xff
+ }
+};
+
+/** @brief structure for sensor settings
+ * this structure describes the sensor settings to use for a given
+ * exposure.
+ */
+typedef struct {
+ int sensor_type; /**> sensor id */
+ int dpi; /**> maximum dpi for which data are valid */
+ int exposure; /**> exposure */
+ int ck1map; /**> CK1MAP */
+ int ck3map; /**> CK3MAP */
+ int ck4map; /**> CK4MAP */
+ int segcnt; /**> SEGCNT */
+ int expdummy; /**> exposure dummy */
+ int expr; /**> initial red exposure */
+ int expg; /**> initial green exposure */
+ int expb; /**> initial blue exposure */
+ size_t *order; /**> order of sub-segments */
+ uint8_t r17; /**> TG width */
+} Sensor_Profile;
+
+/* *INDENT-OFF* */
+
+static size_t order_01[]={0,1};
+static size_t order_0213[]={0,2,1,3};
+static size_t order_0246[]={0,2,4,6,1,3,5,7};
+
+static size_t new_order[]={0,1,2,3};
+static size_t order_0145[]={0,1,4,5,2,3,6,7};
+
+/**
+ * database of sensor profiles
+ */
+static Sensor_Profile sensors[]={
+ {CIS_CANONLIDE100, 200, 2848, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
+ {CIS_CANONLIDE100, 300, 1424, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
+ {CIS_CANONLIDE100, 600, 1432, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
+ {CIS_CANONLIDE100, 1200, 2712, 60, 159, 85, 5136, 255, 746, 478, 353, order_01 , 0x08},
+ {CIS_CANONLIDE100, 2400, 5280, 60, 159, 85, 5136, 255, 1417, 909, 643, order_0213, 0x06},
+ /*
+ {CIS_CANONLIDE200, 150, 2848, 240, 636, 340, 5144, 0, 255, 637, 637, 637},
+ {CIS_CANONLIDE200, 300, 1424, 240, 636, 340, 5144, 0, 255, 637, 637, 637},
+ */
+ {CIS_CANONLIDE200, 200, 2848, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
+ {CIS_CANONLIDE200, 300, 1424, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
+ {CIS_CANONLIDE200, 600, 1432, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
+ {CIS_CANONLIDE200, 1200, 2712, 60, 159, 85, 5136, 255, 746, 478, 353, order_01 , 0x08},
+ {CIS_CANONLIDE200, 2400, 5280, 60, 159, 85, 5136, 255, 1417, 909, 643, order_0213, 0x06},
+ {CIS_CANONLIDE200, 4800, 10416, 60, 159, 85, 5136, 255, 2692, 1728, 1221, order_0246, 0x04},
+
+ /* LiDE 700F */
+ {CIS_CANONLIDE700, 150, 2848, 135, 249, 85, 5187, 255, 465, 310, 239, NULL , 0x0c},
+ {CIS_CANONLIDE700, 300, 1424, 135, 249, 85, 5187, 255, 465, 310, 239, NULL , 0x0c},
+ {CIS_CANONLIDE700, 600, 1504, 135, 249, 85, 5187, 255, 465, 310, 239, NULL , 0x0c},
+ {CIS_CANONLIDE700, 1200, 2696, 135, 249, 85, 5187, 255, 1464, 844, 555, order_01 , 0x0a},
+ {CIS_CANONLIDE700, 2400, 10576, 135, 249, 85, 5187, 255, 2798, 1558, 972, new_order , 0x08},
+ {CIS_CANONLIDE700, 4800, 10576, 135, 249, 85, 5187, 255, 2798, 1558, 972, order_0145, 0x06},
+};
+/* *INDENT-ON* */
+
+/* base motor sopes in full step unit */
+/* target=((exposure * dpi) / base_dpi)>>step_type; */
+static uint32_t lide200_base[] = { 46876, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2336, 2329, 2322, 2314, 2307, 2300,2292,2285,2278,2271,2263,2256,2249,2241,2234,2227,2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
+static uint32_t lide200_medium[] = { 46876, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136,2343, 2336, 2329, 2322, 2314, 2307, 2300,2292,2285,2278,2271,2263,2256,2249,2241,2234,2227,2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
+static uint32_t lide200_high[] = { 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
+static uint32_t lide700_medium[] = { 46876,2342,2342,2342,2342,2342,2342,2342,2342,2302,2286,2274,2266,2258,2252,2244,2240,2234,2228,2224,2218,2216,2210,2208,2202,2200,2194,2192,2190,2186,2182,2180,2176,2174,2172,2170,2166,2162,2160,2156,2154,2152,2150,2150,2146,2144,2142,2140,2136,2134,2132,2130,2130,2128,2124,2122,2120,2120,2118,2116,2112,2112,2110,2108,2106,2106,2104,2102,2102,2098,2096,2094,2094,2092,2090,2090,2086,2084,2084,2082,2082,2080,2078,2078,2076,2074,2074,2070,2070,2068,2066,2066,2064,2064,2062,2062,2060,2058,2058,2054,2054,2052,2052,2050,2050,2048,2048,2046,2046,2044,2042,2042,2040,2040,2038,2038,2034,2034,2032,2032,2030,2030,2028,2028,2026,2026,2022,2022};
+static uint32_t lide700_high[] = { 46876,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864};
+/* 5190 trop
+ * 5186 pas assez
+ */
+/*
+static uint32_t lide200_max[] = { 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
+*/
+
+/**
+ * database of motor profiles
+ */
+
+/* *INDENT-OFF* */
+static Motor_Profile gl847_motors[]={
+ /* LiDE 100 */
+ {MOTOR_CANONLIDE100, 2848, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE100, 1424, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE100, 1432, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE100, 2712, QUARTER_STEP, lide200_medium},
+ {MOTOR_CANONLIDE100, 5280, EIGHTH_STEP , lide200_high},
+
+ /* LiDE 200 */
+ {MOTOR_CANONLIDE200, 2848, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE200, 1424, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE200, 1432, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE200, 2712, QUARTER_STEP, lide200_medium},
+ {MOTOR_CANONLIDE200, 5280, EIGHTH_STEP , lide200_high},
+ {MOTOR_CANONLIDE200, 10416, EIGHTH_STEP , lide200_high},
+
+ /* LiDE 700F */
+ {MOTOR_CANONLIDE700, 2848, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE700, 1424, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE700, 1504, HALF_STEP , lide200_base},
+ {MOTOR_CANONLIDE700, 2696, HALF_STEP , lide700_medium}, /* 2696 , 2838 */
+ {MOTOR_CANONLIDE700, 10576, EIGHTH_STEP, lide700_high},
+
+ /* end of database entry */
+ {0, 0, 0, NULL},
+};
+/* *INDENT-ON* */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_low.c b/backend/genesys_low.c
new file mode 100644
index 0000000..62563ae
--- /dev/null
+++ b/backend/genesys_low.c
@@ -0,0 +1,1981 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_low
+
+#include "genesys_low.h"
+
+/* ------------------------------------------------------------------------ */
+/* functions calling ASIC specific functions */
+/* ------------------------------------------------------------------------ */
+
+/**
+ * setup the hardware dependent functions
+ */
+SANE_Status
+sanei_genesys_init_cmd_set (Genesys_Device * dev)
+{
+ DBG_INIT ();
+ switch (dev->model->asic_type)
+ {
+ case GENESYS_GL646:
+ return sanei_gl646_init_cmd_set (dev);
+ case GENESYS_GL841:
+ return sanei_gl841_init_cmd_set (dev);
+ case GENESYS_GL843:
+ return sanei_gl843_init_cmd_set (dev);
+ case GENESYS_GL845: /* since only a few reg bits differs
+ we handle both together */
+ case GENESYS_GL846:
+ return sanei_gl846_init_cmd_set (dev);
+ case GENESYS_GL847:
+ return sanei_gl847_init_cmd_set (dev);
+ case GENESYS_GL124:
+ return sanei_gl124_init_cmd_set (dev);
+ default:
+ return SANE_STATUS_INVAL;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* General IO and debugging functions */
+/* ------------------------------------------------------------------------ */
+
+/* Write data to a pnm file (e.g. calibration). For debugging only */
+/* data is RGB or grey, with little endian byte order */
+SANE_Status
+sanei_genesys_write_pnm_file (char *filename, uint8_t * data, int depth,
+ int channels, int pixels_per_line, int lines)
+{
+ FILE *out;
+ int count;
+
+ DBG (DBG_info,
+ "sanei_genesys_write_pnm_file: depth=%d, channels=%d, ppl=%d, lines=%d\n",
+ depth, channels, pixels_per_line, lines);
+
+ out = fopen (filename, "w");
+ if (!out)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_pnm_file: could nor open %s for writing: %s\n",
+ filename, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ if(depth==1)
+ {
+ fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines);
+ }
+ else
+ {
+ fprintf (out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6',
+ pixels_per_line, lines, (int) pow (2, depth) - 1);
+ }
+ if (channels == 3)
+ {
+ for (count = 0; count < (pixels_per_line * lines * 3); count++)
+ {
+ if (depth == 16)
+ fputc (*(data + 1), out);
+ fputc (*(data++), out);
+ if (depth == 16)
+ data++;
+ }
+ }
+ else
+ {
+ if (depth==1)
+ {
+ pixels_per_line/=8;
+ }
+ for (count = 0; count < (pixels_per_line * lines); count++)
+ {
+ switch (depth)
+ {
+ case 8:
+ fputc (*(data + count), out);
+ break;
+ case 16:
+ fputc (*(data + 1), out);
+ fputc (*(data), out);
+ data += 2;
+ break;
+ default:
+ fputc(data[count], out);
+ break;
+ }
+ }
+ }
+ fclose (out);
+
+ DBG (DBG_proc, "sanei_genesys_write_pnm_file: finished\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* the following 2 functions are used to handle registers in a
+ way that doesn't depend on the actual ASIC type */
+
+/* Reads a register from a register set */
+SANE_Byte
+sanei_genesys_read_reg_from_set (Genesys_Register_Set * reg,
+ uint16_t address)
+{
+ SANE_Int i;
+
+ for (i = 0; i < GENESYS_MAX_REGS && reg[i].address; i++)
+ {
+ if (reg[i].address == address)
+ {
+ return reg[i].value;
+ }
+ }
+ return 0;
+}
+
+/* Reads a register from a register set */
+void
+sanei_genesys_set_reg_from_set (Genesys_Register_Set * reg, uint16_t address,
+ SANE_Byte value)
+{
+ SANE_Int i;
+
+ for (i = 0; i < GENESYS_MAX_REGS && reg[i].address; i++)
+ {
+ if (reg[i].address == address)
+ {
+ reg[i].value = value;
+ break;
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+/** @brief write to one high (addr >= 0x100) register
+ * write to a register which address is higher than 0xff.
+ * @param dev opened device to write to
+ * @param reg LSB of register address
+ * @param val value to write
+ */
+SANE_Status
+sanei_genesys_write_hregister (Genesys_Device * dev, uint16_t reg, uint8_t val)
+{
+ SANE_Status status;
+ uint8_t buffer[2];
+
+ buffer[0]=reg & 0xff;
+ buffer[1]=val;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ 0x100 | VALUE_SET_REGISTER, INDEX, 2, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_genesys_write_hregister (0x%02x, 0x%02x): failed : %s\n", reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_write_hregister (0x%02x, 0x%02x) completed\n",
+ reg, val);
+
+ return status;
+}
+
+/** @brief read from one high (addr >= 0x100) register
+ * Read to a register which address is higher than 0xff. Second byte is check to detect
+ * physical link errors.
+ * @param dev opened device to read from
+ * @param reg LSB of register address
+ * @param val value to write
+ */
+SANE_Status
+sanei_genesys_read_hregister (Genesys_Device * dev, uint16_t reg, uint8_t * val)
+{
+ SANE_Status status;
+ SANE_Byte value[2];
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_BUFFER,
+ 0x100 | VALUE_GET_REGISTER, 0x22+((reg & 0xff)<<8), 2, value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_hregister (0x%02x): failed while reading register: %s\n",
+ reg, sane_strstatus (status));
+ return status;
+ }
+ *val=value[0];
+ DBG( DBG_io2, "sanei_genesys_read_hregister(0x%02x)=0x%02x\n",reg,*val);
+
+ /* check usb link status */
+ if((value[1] & 0xff) != 0x55)
+ {
+ DBG (DBG_error,"sanei_genesys_read_hregister: invalid read, scanner unplugged ?\n");
+ status=SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
+
+/**
+ * Write to one GL847 ASIC register
+URB 10 control 0x40 0x04 0x83 0x00 len 2 wrote 0xa6 0x04
+ */
+static SANE_Status
+sanei_genesys_write_gl847_register (Genesys_Device * dev, uint8_t reg, uint8_t val)
+{
+ SANE_Status status;
+ uint8_t buffer[2];
+
+ buffer[0]=reg;
+ buffer[1]=val;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_SET_REGISTER, INDEX, 2, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_genesys_write_gl847_register (0x%02x, 0x%02x): failed : %s\n", reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_write_gl847_register (0x%02x, 0x%02x) completed\n",
+ reg, val);
+
+ return status;
+}
+
+/**
+ * Write to one ASIC register
+ */
+SANE_Status
+sanei_genesys_write_register (Genesys_Device * dev, uint16_t reg, uint8_t val)
+{
+ SANE_Status status;
+ SANE_Byte reg8;
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ /* 16 bit register address space */
+ if(reg>255)
+ {
+ return sanei_genesys_write_hregister(dev, reg, val);
+ }
+
+ /* route to gl847 function if needed */
+ if(dev->model->asic_type==GENESYS_GL847
+ || dev->model->asic_type==GENESYS_GL845
+ || dev->model->asic_type==GENESYS_GL846
+ || dev->model->asic_type==GENESYS_GL124)
+ {
+ return sanei_genesys_write_gl847_register(dev, reg, val);
+ }
+
+ reg8=reg & 0xff;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &reg8);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_register (0x%02x, 0x%02x): failed while setting register: %s\n",
+ reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_WRITE_REGISTER, INDEX, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_register (0x%02x, 0x%02x): failed while writing register value: %s\n",
+ reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_write_register (0x%02x, 0x%02x) completed\n",
+ reg, val);
+
+ return status;
+}
+
+/**
+ * @brief write command to 0x8c endpoint
+ * Write a value to 0x8c end point (end access), for USB firmware related operations
+ * Known values are 0x0f, 0x11 for USB 2.0 data transfer and 0x0f,0x14 for USB1.1
+ * @param dev device to write to
+ * @param index index of the command
+ * @param val value to write
+ */
+SANE_Status
+sanei_genesys_write_0x8c (Genesys_Device * dev, uint8_t index, uint8_t val)
+{
+ SANE_Status status;
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ DBG (DBG_io, "sanei_genesys_write_0x8c: 0x%02x,0x%02x\n", index, val);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_BUF_ENDACCESS, index, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_0x8c: failed %s\n", sane_strstatus (status));
+ }
+ return status;
+}
+
+/* read reg 0x41:
+ * URB 164 control 0xc0 0x04 0x8e 0x4122 len 2 read 0xfc 0x55
+ */
+static SANE_Status
+sanei_genesys_read_gl847_register (Genesys_Device * dev, uint16_t reg, uint8_t * val)
+{
+ SANE_Status status;
+ SANE_Byte value[2];
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_BUFFER,
+ VALUE_GET_REGISTER, 0x22+(reg<<8), 2, value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_gl847_register (0x%02x): failed while setting register: %s\n",
+ reg, sane_strstatus (status));
+ return status;
+ }
+ *val=value[0];
+ DBG( DBG_io2, "sanei_genesys_read_gl847_register(0x%02x)=0x%02x\n",reg,*val);
+
+ /* check usb link status */
+ if((value[1] & 0xff) != 0x55)
+ {
+ DBG (DBG_error,"sanei_genesys_read_gl847_register: invalid read, scanner unplugged ?\n");
+ status=SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
+
+/* Read from one register */
+SANE_Status
+sanei_genesys_read_register (Genesys_Device * dev, uint16_t reg, uint8_t * val)
+{
+ SANE_Status status;
+ SANE_Byte reg8;
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ *val=0;
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ /* 16 bit register address space */
+ if(reg>255)
+ {
+ return sanei_genesys_read_hregister(dev, reg, val);
+ }
+
+ /* route to gl847 function if needed */
+ if(dev->model->asic_type==GENESYS_GL847
+ || dev->model->asic_type==GENESYS_GL845
+ || dev->model->asic_type==GENESYS_GL846
+ || dev->model->asic_type==GENESYS_GL124)
+ return sanei_genesys_read_gl847_register(dev, reg, val);
+
+ /* 8 bit register address space */
+ reg8=(SANE_Byte)(reg& 0Xff);
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &reg8);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_register (0x%02x, 0x%02x): failed while setting register: %s\n",
+ reg, *val, sane_strstatus (status));
+ return status;
+ }
+
+ *val = 0;
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_REGISTER,
+ VALUE_READ_REGISTER, INDEX, 1, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_register (0x%02x, 0x%02x): failed while reading register value: %s\n",
+ reg, *val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_read_register (0x%02x, 0x%02x) completed\n",
+ reg, *val);
+
+ return status;
+}
+
+/* Set address for writing data */
+SANE_Status
+sanei_genesys_set_buffer_address (Genesys_Device * dev, uint32_t addr)
+{
+ SANE_Status status;
+
+ if(dev->model->asic_type==GENESYS_GL847
+ || dev->model->asic_type==GENESYS_GL845
+ || dev->model->asic_type==GENESYS_GL846
+ || dev->model->asic_type==GENESYS_GL124)
+ {
+ DBG (DBG_warn,
+ "sanei_genesys_set_buffer_address: shouldn't be used for GL846+ ASICs\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (DBG_io,
+ "sanei_genesys_set_buffer_address: setting address to 0x%05x\n",
+ addr & 0xfffffff0);
+
+ addr = addr >> 4;
+
+ status = sanei_genesys_write_register (dev, 0x2b, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_set_buffer_address: failed while writing low byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ addr = addr >> 8;
+ status = sanei_genesys_write_register (dev, 0x2a, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_set_buffer_address: failed while writing high byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_set_buffer_address: completed\n");
+
+ return status;
+}
+
+/** read data for analog frontend
+ * @param dev device owning the AFE
+ * @param addr register address to read
+ * @param data placeholder for the result
+ * @return SANE_STATUS_GOOD is OK, else the error code
+ */
+SANE_Status
+sanei_genesys_fe_read_data (Genesys_Device * dev, uint8_t addr,
+ uint16_t *data)
+{
+ SANE_Status status;
+ uint8_t value;
+ Genesys_Register_Set reg[1];
+
+
+ DBG (DBG_proc, "sanei_genesys_fe_read_data: start\n");
+
+ reg[0].address = 0x50;
+ reg[0].value = addr;
+
+ /* set up read address */
+ status = dev->model->cmd_set->bulk_write_register (dev, reg, 1);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_fe_read_data: failed while bulk writing registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* read data */
+ RIE (sanei_genesys_read_register (dev, 0x46, &value));
+ *data=256*value;
+ RIE (sanei_genesys_read_register (dev, 0x47, &value));
+ *data+=value;
+
+ DBG (DBG_io, "sanei_genesys_fe_read_data (0x%02x, 0x%04x)\n", addr, *data);
+ DBG (DBG_proc, "sanei_genesys_fe_read_data: completed\n");
+
+ return status;
+}
+
+/* Write data for analog frontend */
+SANE_Status
+sanei_genesys_fe_write_data (Genesys_Device * dev, uint8_t addr,
+ uint16_t data)
+{
+ SANE_Status status;
+ Genesys_Register_Set reg[3];
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ DBG (DBG_io, "sanei_genesys_fe_write_data (0x%02x, 0x%04x)\n", addr, data);
+
+ reg[0].address = 0x51;
+ reg[0].value = addr;
+ reg[1].address = 0x3a;
+ reg[1].value = (data / 256) & 0xff;
+ reg[2].address = 0x3b;
+ reg[2].value = data & 0xff;
+ if (dev->model->asic_type == GENESYS_GL124)
+ {
+ reg[1].address = 0x5d;
+ reg[2].address = 0x5e;
+ }
+
+ status = dev->model->cmd_set->bulk_write_register (dev, reg, 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_fe_write_data: failed while bulk writing registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_fe_write_data: completed\n");
+
+ return status;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Medium level functions */
+/* ------------------------------------------------------------------------ */
+
+/** read the status register
+ */
+SANE_Status
+sanei_genesys_get_status (Genesys_Device * dev, uint8_t * status)
+{
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ *status=0;
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ if(dev->model->asic_type==GENESYS_GL124)
+ return sanei_genesys_read_hregister(dev, 0x101, status);
+ return sanei_genesys_read_register (dev, 0x41, status);
+}
+
+/**
+ * decodes and prints content of status register
+ * @param val value read from status register
+ */
+void sanei_genesys_print_status (uint8_t val)
+{
+ char msg[80];
+
+ sprintf (msg, "%s%s%s%s%s%s%s%s",
+ val & PWRBIT ? "PWRBIT " : "",
+ val & BUFEMPTY ? "BUFEMPTY " : "",
+ val & FEEDFSH ? "FEEDFSH " : "",
+ val & SCANFSH ? "SCANFSH " : "",
+ val & HOMESNR ? "HOMESNR " : "",
+ val & LAMPSTS ? "LAMPSTS " : "",
+ val & FEBUSY ? "FEBUSY " : "",
+ val & MOTORENB ? "MOTORENB" : "");
+ DBG (DBG_info, "status=%s\n", msg);
+}
+
+#if 0
+/* returns pixels per line from register set */
+/*candidate for moving into chip specific files?*/
+static int
+genesys_pixels_per_line (Genesys_Register_Set * reg)
+{
+ int pixels_per_line;
+
+ pixels_per_line =
+ sanei_genesys_read_reg_from_set (reg,
+ 0x32) * 256 +
+ sanei_genesys_read_reg_from_set (reg, 0x33);
+ pixels_per_line -=
+ (sanei_genesys_read_reg_from_set (reg, 0x30) * 256 +
+ sanei_genesys_read_reg_from_set (reg, 0x31));
+
+ return pixels_per_line;
+}
+
+/* returns dpiset from register set */
+/*candidate for moving into chip specific files?*/
+static int
+genesys_dpiset (Genesys_Register_Set * reg)
+{
+ int dpiset;
+
+ dpiset =
+ sanei_genesys_read_reg_from_set (reg,
+ 0x2c) * 256 +
+ sanei_genesys_read_reg_from_set (reg, 0x2d);
+
+ return dpiset;
+}
+#endif
+
+/** read the number of valid words in scanner's RAM
+ * ie registers 42-43-44
+ */
+/*candidate for moving into chip specific files?*/
+SANE_Status
+sanei_genesys_read_valid_words (Genesys_Device * dev, unsigned int *words)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBGSTART;
+ switch (dev->model->asic_type)
+ {
+ case GENESYS_GL124:
+ RIE (sanei_genesys_read_hregister (dev, 0x102, &value));
+ *words = (value & 0x03);
+ RIE (sanei_genesys_read_hregister (dev, 0x103, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_hregister (dev, 0x104, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_hregister (dev, 0x105, &value));
+ *words = *words * 256 + value;
+ break;
+
+ case GENESYS_GL845:
+ case GENESYS_GL846:
+ RIE (sanei_genesys_read_register (dev, 0x42, &value));
+ *words = (value & 0x02);
+ RIE (sanei_genesys_read_register (dev, 0x43, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x44, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x45, &value));
+ *words = *words * 256 + value;
+ break;
+
+ case GENESYS_GL847:
+ RIE (sanei_genesys_read_register (dev, 0x42, &value));
+ *words = (value & 0x03);
+ RIE (sanei_genesys_read_register (dev, 0x43, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x44, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x45, &value));
+ *words = *words * 256 + value;
+ break;
+
+ default:
+ RIE (sanei_genesys_read_register (dev, 0x44, &value));
+ *words = value;
+ RIE (sanei_genesys_read_register (dev, 0x43, &value));
+ *words += (value * 256);
+ RIE (sanei_genesys_read_register (dev, 0x42, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *words += ((value & 0x03) * 256 * 256);
+ else
+ *words += ((value & 0x0f) * 256 * 256);
+ }
+
+ DBG (DBG_proc, "%s: %d words\n", __FUNCTION__, *words);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** read the number of lines scanned
+ * ie registers 4b-4c-4d
+ */
+SANE_Status
+sanei_genesys_read_scancnt (Genesys_Device * dev, unsigned int *words)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBG (DBG_proc, "sanei_genesys_read_scancnt: start\n");
+
+ if (dev->model->asic_type == GENESYS_GL124)
+ {
+ RIE (sanei_genesys_read_hregister (dev, 0x10b, &value));
+ *words = (value & 0x0f) << 16;
+ RIE (sanei_genesys_read_hregister (dev, 0x10c, &value));
+ *words += (value << 8);
+ RIE (sanei_genesys_read_hregister (dev, 0x10d, &value));
+ *words += value;
+ }
+ else
+ {
+ RIE (sanei_genesys_read_register (dev, 0x4d, &value));
+ *words = value;
+ RIE (sanei_genesys_read_register (dev, 0x4c, &value));
+ *words += (value * 256);
+ RIE (sanei_genesys_read_register (dev, 0x4b, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *words += ((value & 0x03) * 256 * 256);
+ else
+ *words += ((value & 0x0f) * 256 * 256);
+ }
+
+ DBG (DBG_proc, "sanei_genesys_read_scancnt: %d lines\n", *words);
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Find register in set
+ * @param regs register set to search
+ * @param addr addres of the searched register
+ * @return a Genesys_Register_Set pointer corresponding to the required
+ * address in ASIC space. Or NULL if not found.
+ */
+Genesys_Register_Set *
+sanei_genesys_get_address (Genesys_Register_Set * regs, uint16_t addr)
+{
+ int i;
+ for (i = 0; i < GENESYS_MAX_REGS; i++)
+ {
+ if (regs[i].address == addr)
+ return &regs[i];
+ }
+ DBG (DBG_error, "sanei_genesys_get_address: failed to find address for register 0x%02x, crash expected !\n",addr);
+ return NULL;
+}
+
+/**
+ * set a 16 bit value in the given register set.
+ * @param regs register set where to set values
+ * @param addr address of the first register index to set
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_set_double(Genesys_Register_Set *regs, uint16_t addr, uint16_t value)
+{
+ Genesys_Register_Set *r;
+
+ /* high byte */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = HIBYTE (value);
+
+ /* low byte */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = LOBYTE (value);
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * set a 24 bit value in the given register set.
+ * @param regs register set where to set values
+ * @param addr address of the first register index to set
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_set_triple(Genesys_Register_Set *regs, uint16_t addr, uint32_t value)
+{
+ Genesys_Register_Set *r;
+
+ /* low byte of highword */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = LOBYTE (HIWORD(value));
+
+ /* high byte of low word */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = HIBYTE (LOWORD(value));
+
+ /* low byte of low word */
+ r = sanei_genesys_get_address (regs, addr+2);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = LOBYTE (LOWORD(value));
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * get a 16 bit value in the given register set.
+ * @param regs register set where to read values
+ * @param addr address of the first register index to read
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_get_double(Genesys_Register_Set *regs, uint16_t addr, uint16_t *value)
+{
+ Genesys_Register_Set *r;
+ uint16_t result=0;
+
+ /* high byte */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result=r->value<<8;
+
+ /* low byte */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result+=r->value;
+
+ *value=result;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * get a 24 bit value in the given register set.
+ * @param regs register set where to read values
+ * @param addr address of the first register index to read
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_get_triple(Genesys_Register_Set *regs, uint16_t addr, uint32_t *value)
+{
+ Genesys_Register_Set *r;
+ uint32_t result=0;
+
+ /* low byte of highword */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result=r->value<<16;
+
+ /* high byte of low word */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result+=(r->value<<8);
+
+ /* low byte of low word */
+ r = sanei_genesys_get_address (regs, addr+2);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result+=r->value;
+
+ *value=result;
+ return SANE_STATUS_GOOD;
+}
+
+/* Checks if the scan buffer is empty */
+SANE_Status
+sanei_genesys_test_buffer_empty (Genesys_Device * dev, SANE_Bool * empty)
+{
+ uint8_t val = 0;
+ SANE_Status status;
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_test_buffer_empty: failed to read buffer status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (dev->model->cmd_set->test_buffer_empty_bit (val))
+ {
+ DBG (DBG_io2, "sanei_genesys_test_buffer_empty: buffer is empty\n");
+ *empty = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+ }
+
+ *empty = SANE_FALSE;
+
+ DBG (DBG_io, "sanei_genesys_test_buffer_empty: buffer is filled\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Read data (e.g scanned image) from scan buffer */
+SANE_Status
+sanei_genesys_read_data_from_scanner (Genesys_Device * dev, uint8_t * data,
+ size_t size)
+{
+ SANE_Status status;
+ int time_count = 0;
+ unsigned int words = 0;
+
+ DBG (DBG_proc, "sanei_genesys_read_data_from_scanner (size = %lu bytes)\n",
+ (u_long) size);
+
+ if (size & 1)
+ DBG (DBG_info,
+ "WARNING sanei_genesys_read_data_from_scanner: odd number of bytes\n");
+
+ /* wait until buffer not empty for up to 5 seconds */
+ do
+ {
+ status = sanei_genesys_read_valid_words (dev, &words);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_data_from_scanner: checking for empty buffer failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (words == 0)
+ {
+ usleep (10000); /* wait 10 msec */
+ time_count++;
+ }
+ }
+ while ((time_count < 2500*2) && (words == 0));
+
+ if (words == 0) /* timeout, buffer does not get filled */
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_data_from_scanner: timeout, buffer does not get filled\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status = dev->model->cmd_set->bulk_read_data (dev, 0x45, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_data_from_scanner: reading bulk data failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "sanei_genesys_read_data_from_scanner: completed\n");
+ return SANE_STATUS_GOOD;
+}
+SANE_Status
+sanei_genesys_read_feed_steps (Genesys_Device * dev, unsigned int *steps)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBG (DBG_proc, "sanei_genesys_read_feed_steps\n");
+
+ if (dev->model->asic_type == GENESYS_GL124)
+ {
+ RIE (sanei_genesys_read_hregister (dev, 0x108, &value));
+ *steps = (value & 0x1f) << 16;
+ RIE (sanei_genesys_read_hregister (dev, 0x109, &value));
+ *steps += (value << 8);
+ RIE (sanei_genesys_read_hregister (dev, 0x10a, &value));
+ *steps += value;
+ }
+ else
+ {
+ RIE (sanei_genesys_read_register (dev, 0x4a, &value));
+ *steps = value;
+ RIE (sanei_genesys_read_register (dev, 0x49, &value));
+ *steps += (value * 256);
+ RIE (sanei_genesys_read_register (dev, 0x48, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *steps += ((value & 0x03) * 256 * 256);
+ else if (dev->model->asic_type == GENESYS_GL841)
+ *steps += ((value & 0x0f) * 256 * 256);
+ else
+ *steps += ((value & 0x1f) * 256 * 256);
+ }
+
+ DBG (DBG_proc, "sanei_genesys_read_feed_steps: %d steps\n", *steps);
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Write to many registers at once
+ * Note: sequential call to write register, no effective
+ * bulk write implemented.
+ * @param dev device to write to
+ * @param reg pointer to an array of registers
+ * @param elems size of the array
+ */
+SANE_Status
+sanei_genesys_bulk_write_register (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ size_t elems)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t i;
+
+ for (i = 0; i < elems && status == SANE_STATUS_GOOD; i++)
+ {
+ if (reg[i].address != 0)
+ {
+ status =
+ sanei_genesys_write_register (dev, reg[i].address, reg[i].value);
+ }
+ }
+
+ DBG (DBG_io, "%s: wrote %lu registers\n", __FUNCTION__, (u_long) elems);
+ return status;
+}
+
+
+
+/**
+ * writes a block of data to AHB
+ * @param dn USB device index
+ * @param usb_mode usb mode : -1, fake usb, 1 usb 1.1, 2 usb 2.0
+ * @param addr AHB address to write to
+ * @param size size of the chunk of data
+ * @param data pointer to the data to write
+ */
+SANE_Status
+sanei_genesys_write_ahb (SANE_Int dn, int usb_mode, uint32_t addr, uint32_t size, uint8_t * data)
+{
+ uint8_t outdata[8];
+ size_t written,blksize;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ char msg[100]="AHB=";
+
+ outdata[0] = addr & 0xff;
+ outdata[1] = ((addr >> 8) & 0xff);
+ outdata[2] = ((addr >> 16) & 0xff);
+ outdata[3] = ((addr >> 24) & 0xff);
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ sprintf (msg+strlen(msg), " 0x%02x", outdata[i]);
+ }
+ DBG (DBG_io, "%s: write(0x%08x,0x%08x)\n", __FUNCTION__, addr,size);
+ DBG (DBG_io, "%s: %s\n", __FUNCTION__, msg);
+ }
+
+ /* no effective write if fake USB */
+ if(usb_mode<0)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* write addr and size for AHB */
+ status =
+ sanei_usb_control_msg (dn, REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER,
+ 0x01, 8, outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_genesys_write_ahb: failed while setting addr and size: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* write actual data */
+ written = 0;
+ do
+ {
+ if (size - written > BULKOUT_MAXSIZE)
+ {
+ blksize = BULKOUT_MAXSIZE;
+ }
+ else
+ {
+ blksize = size - written;
+ }
+ status = sanei_usb_write_bulk (dn, data + written, &blksize);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_ahb: failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ written += blksize;
+ }
+ while (written < size);
+
+ return status;
+}
+
+/** @brief generates gamma buffer to transfer
+ * Generates gamma table buffer to send to ASIC. Applies
+ * contrast and brightness if set.
+ * @param dev device to set up
+ * @param bits number of bits used by gamma
+ * @param max value for gamma
+ * @param size of the gamma table
+ * @param gamma allocated gamma buffer to fill
+ * @returns SANE_STATUS_GOOD or SANE_STATUS_NO_MEM
+ */
+SANE_Status sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
+ int bits,
+ int max,
+ int size,
+ uint8_t *gamma)
+{
+ int i;
+ uint16_t value, *lut=NULL;
+
+ if(dev->settings.contrast!=0 || dev->settings.brightness!=0)
+ {
+ lut=(uint16_t *)malloc(65536*2);
+ if(lut==NULL)
+ {
+ free(gamma);
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_genesys_load_lut((unsigned char *)lut,
+ bits,
+ bits,
+ 0,
+ max,
+ dev->settings.contrast,
+ dev->settings.brightness);
+ for (i = 0; i < size-1; i++)
+ {
+ value=dev->sensor.gamma_table[GENESYS_RED][i];
+ value=lut[value];
+ gamma[i * 2 + size * 0 + 0] = value & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_GREEN][i];
+ value=lut[value];
+ gamma[i * 2 + size * 2 + 0] = value & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_BLUE][i];
+ value=lut[value];
+ gamma[i * 2 + size * 4 + 0] = value & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
+ }
+ }
+ else
+ {
+ for (i = 0; i < size-1; i++)
+ {
+ value=dev->sensor.gamma_table[GENESYS_RED][i];
+ gamma[i * 2 + size * 0 + 0] = value & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_GREEN][i];
+ gamma[i * 2 + size * 2 + 0] = value & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_BLUE][i];
+ gamma[i * 2 + size * 4 + 0] = value & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
+ }
+ }
+
+
+ if(lut!=NULL)
+ {
+ free(lut);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief send gamma table to scanner
+ * This function sends generic gamma table (ie ones built with
+ * provided gamma) or the user defined one if provided by
+ * fontend. Used by gl846+ ASICs
+ * @param dev device to write to
+ */
+SANE_Status
+sanei_genesys_send_gamma_table (Genesys_Device * dev)
+{
+ int size;
+ int i;
+ uint8_t *gamma, val;
+ SANE_Status status;
+
+ DBGSTART;
+
+ size = 256 + 1;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ gamma = (uint8_t *) malloc (size * 2 * 3);
+ if (!gamma)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(gamma, 255, size*3*2);
+
+ RIE(sanei_genesys_generate_gamma_buffer(dev, 16, 65535, size, gamma));
+
+ /* loop sending gamma tables NOTE: 0x01000000 not 0x10000000 */
+ for (i = 0; i < 3; i++)
+ {
+ /* clear corresponding GMM_N bit */
+ RIEF (sanei_genesys_read_register (dev, 0xbd, &val), gamma);
+ val &= ~(0x01 << i);
+ RIEF (sanei_genesys_write_register (dev, 0xbd, val), gamma);
+
+ /* clear corresponding GMM_F bit */
+ RIEF (sanei_genesys_read_register (dev, 0xbe, &val), gamma);
+ val &= ~(0x01 << i);
+ RIEF (sanei_genesys_write_register (dev, 0xbe, val), gamma);
+
+ /* set GMM_Z */
+ RIEF (sanei_genesys_write_register (dev, 0xc5+2*i, gamma[size*2*i+1]), gamma);
+ RIEF (sanei_genesys_write_register (dev, 0xc6+2*i, gamma[size*2*i]), gamma);
+
+ status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, 0x01000000 + 0x200 * i, (size-1) * 2, gamma + i * size * 2+2);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "%s: write to AHB failed writing table %d (%s)\n", __FUNCTION__,
+ i, sane_strstatus (status));
+ }
+ }
+
+ free (gamma);
+ DBGCOMPLETED;
+ return status;
+}
+
+/** @brief initialize device
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home. Designed for gl846+ ASICs
+ * @param dev device to initialize
+ * @param max_regs umber of maximum used registers
+ */
+SANE_Status
+sanei_genesys_asic_init (Genesys_Device * dev, int max_regs)
+{
+ SANE_Status status;
+ uint8_t val;
+ SANE_Bool cold = SANE_TRUE;
+ int size, i;
+
+ DBGSTART;
+
+ /* URB 16 control 0xc0 0x0c 0x8e 0x0b len 1 read 0x00 */
+ if(dev->usb_mode>=0)
+ {
+ status = sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_GET_REGISTER, 0x00, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: request register failed %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_io2, "%s: value=0x%02x\n", __FUNCTION__, val);
+ DBG (DBG_info, "%s: device is %s\n", __FUNCTION__, (val & 0x08) ? "USB 1.0" : "USB2.0");
+ if (val & 0x08)
+ {
+ dev->usb_mode = 1;
+ }
+ else
+ {
+ dev->usb_mode = 2;
+ }
+ }
+
+ /* check if the device has already been initialized and powered up
+ * we read register 6 and check PWRBIT, if reset scanner has been
+ * freshly powered up. This bit will be set to later so that following
+ * reads can detect power down/up cycle*/
+ RIE (sanei_genesys_read_register (dev, 0x06, &val));
+ /* test for POWER bit */
+ if (val & 0x10)
+ {
+ cold = SANE_FALSE;
+ }
+ DBG (DBG_info, "%s: device is %s\n", __FUNCTION__, cold ? "cold" : "warm");
+
+ /* don't do anything if backend is initialized and hardware hasn't been
+ * replug */
+ if (dev->already_initialized && !cold)
+ {
+ DBG (DBG_info, "%s: already initialized, nothing to do\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* set up hardware and registers */
+ RIE (dev->model->cmd_set->asic_boot (dev, cold));
+
+ /* now hardware part is OK, set up device struct */
+ FREE_IFNOT_NULL (dev->white_average_data);
+ FREE_IFNOT_NULL (dev->dark_average_data);
+ FREE_IFNOT_NULL (dev->sensor.gamma_table[0]);
+ FREE_IFNOT_NULL (dev->sensor.gamma_table[1]);
+ FREE_IFNOT_NULL (dev->sensor.gamma_table[2]);
+
+ dev->settings.color_filter = 0;
+
+ /* duplicate initial values into calibration registers */
+ memcpy (dev->calib_reg, dev->reg, max_regs * sizeof (Genesys_Register_Set));
+
+ /* Set analog frontend */
+ RIE (dev->model->cmd_set->set_fe (dev, AFE_INIT));
+
+ /* init gamma tables */
+ size = 256;
+
+ for(i=0;i<3;i++)
+ {
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ dev->sensor.gamma_table[i] = (uint16_t *) malloc (2 * size);
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ DBG (DBG_error, "%s: could not allocate memory for gamma table %d\n",
+ __FUNCTION__, i);
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_genesys_create_gamma_table (dev->sensor.gamma_table[i],
+ size,
+ 65535,
+ 65535,
+ dev->sensor.gamma[i]);
+ }
+ }
+
+ dev->oe_buffer.buffer = NULL;
+ dev->already_initialized = SANE_TRUE;
+
+ /* Move to home if needed */
+ RIE (dev->model->cmd_set->slow_back_home (dev, SANE_TRUE));
+ dev->scanhead_position_in_steps = 0;
+
+ /* Set powersaving (default = 15 minutes) */
+ RIE (dev->model->cmd_set->set_powersaving (dev, 15));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * Wait for the scanning head to park
+ */
+SANE_Status
+sanei_genesys_wait_for_home (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val;
+ int loop;
+
+ DBGSTART;
+
+ /* clear the parking status whatever the outcome of the function */
+ dev->parking=SANE_FALSE;
+
+ /* read initial status, if head isn't at home and motor is on
+ * we are parking, so we wait.
+ * gl847/gl124 need 2 reads for reliable results */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (10000);
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* if at home, return */
+ if(val & HOMESNR)
+ {
+ DBG (DBG_info,
+ "%s: already at home\n", __FUNCTION__);
+ return status;
+ }
+
+ /* loop for 30 s max, polling home sensor */
+ loop = 0;
+ do
+ {
+ /* wait 100 ms */
+ usleep (100000);
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sanei_genesys_print_status (val);
+ }
+ ++loop;
+ }
+ while (loop < 300 && !(val & HOMESNR) && status == SANE_STATUS_GOOD);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**@brief compute hardware sensor dpi to use
+ * compute the sensor hardware dpi based on target resolution.
+ * A lower dpihw enable faster scans.
+ * @param dev device used for the scan
+ * @param xres x resolution of the scan
+ * @return the hardware dpi to use
+ */
+int sanei_genesys_compute_dpihw(Genesys_Device *dev, int xres)
+{
+ /* some scanners use alxways hardware dpi for sensor */
+ if (dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE)
+ {
+ return dev->sensor.optical_res;
+ }
+
+ /* can't be below 600 dpi */
+ if (xres <= 600)
+ {
+ return 600;
+ }
+ if (xres <= dev->sensor.optical_res / 4)
+ {
+ return dev->sensor.optical_res / 4;
+ }
+ if (xres <= dev->sensor.optical_res / 2)
+ {
+ return dev->sensor.optical_res / 2;
+ }
+ return dev->sensor.optical_res;
+}
+
+/** @brief motor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at full step and at a reference exposure. Use first entry
+ * by default.
+ * @param motors motor profile database
+ * @param motor_type motor id
+ * @param exposure exposure time
+ * @return a pointer to a Motor_Profile struct
+ */
+Motor_Profile *sanei_genesys_get_motor_profile(Motor_Profile *motors, int motor_type, int exposure)
+{
+ unsigned int i;
+ int idx;
+
+ i=0;
+ idx=-1;
+ while(motors[i].exposure!=0)
+ {
+ /* exact match */
+ if(motors[i].motor_type==motor_type && motors[i].exposure==exposure)
+ {
+ return &(motors[i]);
+ }
+
+ /* closest match */
+ if(motors[i].motor_type==motor_type)
+ {
+ /* if profile exposure is higher than the required one,
+ * the entry is a candidate for the closest match */
+ if(motors[i].exposure>=exposure)
+ {
+ if(idx<0)
+ {
+ /* no match found yet */
+ idx=i;
+ }
+ else
+ {
+ /* test for better match */
+ if(motors[i].exposure<motors[idx].exposure)
+ {
+ idx=i;
+ }
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default motor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(motors[idx]);
+}
+
+/**@brief compute motor step type to use
+ * compute the step type (full, half, quarter, ...) to use based
+ * on target resolution
+ * @param motors motor profile database
+ * @param motor_type motor id
+ * @param exposure sensor exposure
+ * @return 0 for full step
+ * 1 for half step
+ * 2 for quarter step
+ * 3 for eighth step
+ */
+int sanei_genesys_compute_step_type(Motor_Profile *motors,
+ int motor_type,
+ int exposure)
+{
+Motor_Profile *profile;
+
+ profile=sanei_genesys_get_motor_profile(motors, motor_type, exposure);
+ return profile->step_type;
+}
+
+/** @brief generate slope table
+ * Generate the slope table to use for the scan using a reference slope
+ * table.
+ * @param slope pointer to the slope table to fill
+ * @param steps pointer to return used step number
+ * @param dpi desired motor resolution
+ * @param exposure exposure used
+ * @param base_dpi base resolution of the motor
+ * @param step_type step type used for scan
+ * @param factor shrink factor for the slope
+ * @param motor_type motor id
+ * @param motors motor profile database
+ */
+int sanei_genesys_slope_table(uint16_t *slope,
+ int *steps,
+ int dpi,
+ int exposure,
+ int base_dpi,
+ int step_type,
+ int factor,
+ int motor_type,
+ Motor_Profile *motors)
+{
+int sum, i;
+uint16_t target,current;
+Motor_Profile *profile;
+
+ /* required speed */
+ target=((exposure * dpi) / base_dpi)>>step_type;
+ DBG (DBG_io2, "%s: target=%d\n", __FUNCTION__, target);
+
+ /* fill result with target speed */
+ for(i=0;i<SLOPE_TABLE_SIZE;i++)
+ slope[i]=target;
+
+ profile=sanei_genesys_get_motor_profile(motors, motor_type, exposure);
+
+ /* use profile to build table */
+ i=0;
+ sum=0;
+
+ /* first step is used unmodified */
+ current=profile->table[0];
+
+ /* loop on profile copying and apply step type */
+ while(profile->table[i]!=0 && current>=target)
+ {
+ slope[i]=current;
+ sum+=slope[i];
+ i++;
+ current=profile->table[i]>>step_type;
+ }
+
+ /* range checking */
+ if(profile->table[i]==0 && DBG_LEVEL >= DBG_warn && current>target)
+ {
+ DBG (DBG_warn,"%s: short slope table, failed to reach %d. target too low ?\n",__FUNCTION__,target);
+ }
+ if(i<3 && DBG_LEVEL >= DBG_warn)
+ {
+ DBG (DBG_warn,"%s: short slope table, failed to reach %d. target too high ?\n",__FUNCTION__,target);
+ }
+
+ /* align on factor */
+ while(i%factor!=0)
+ {
+ slope[i+1]=slope[i];
+ sum+=slope[i];
+ i++;
+ }
+
+ /* ensure minimal slope size */
+ while(i<2*factor)
+ {
+ slope[i+1]=slope[i];
+ sum+=slope[i];
+ i++;
+ }
+
+ /* return used steps and acceleration sum */
+ *steps=i/factor;
+ return sum;
+}
+
+/** @brief returns the lowest possible ydpi for the device
+ * Parses device entry to find lowest motor dpi.
+ * @param dev device description
+ * @return lowest motor resolution
+ */
+int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev)
+{
+ int min=20000;
+ int i=0;
+
+ while(dev->model->ydpi_values[i]!=0)
+ {
+ if(dev->model->ydpi_values[i]<min)
+ {
+ min=dev->model->ydpi_values[i];
+ }
+ i++;
+ }
+ return min;
+}
+
+/** @brief returns the lowest possible dpi for the device
+ * Parses device entry to find lowest motor or sensor dpi.
+ * @param dev device description
+ * @return lowest motor resolution
+ */
+int sanei_genesys_get_lowest_dpi(Genesys_Device *dev)
+{
+ int min=20000;
+ int i=0;
+
+ while(dev->model->ydpi_values[i]!=0)
+ {
+ if(dev->model->ydpi_values[i]<min)
+ {
+ min=dev->model->ydpi_values[i];
+ }
+ i++;
+ }
+ i=0;
+ while(dev->model->xdpi_values[i]!=0)
+ {
+ if(dev->model->xdpi_values[i]<min)
+ {
+ min=dev->model->xdpi_values[i];
+ }
+ i++;
+ }
+ return min;
+}
+
+/** @brief check is a cache entry may be used
+ * Compares current settings with the cache entry and return
+ * SANE_TRUE if they are compatible.
+ */
+SANE_Status
+sanei_genesys_is_compatible_calibration (Genesys_Device * dev,
+ Genesys_Calibration_Cache * cache,
+ int for_overwrite)
+{
+#ifdef HAVE_SYS_TIME_H
+ struct timeval time;
+#endif
+ int compatible = 1, resolution;
+ SANE_Status status;
+
+ DBGSTART;
+
+ if(dev->model->cmd_set->calculate_current_setup==NULL)
+ {
+ DBG (DBG_proc,
+ "sanei_genesys_is_compatible_calibration: no calculate_setup, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ status = dev->model->cmd_set->calculate_current_setup (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_is_compatible_calibration: failed to calculate current setup: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ dev->current_setup.scan_method = dev->settings.scan_method;
+
+ DBG (DBG_proc, "sanei_genesys_is_compatible_calibration: checking\n");
+
+ /* a calibration cache is compatible if color mode and x dpi match the user
+ * requested scan. In the case of CIS scanners, dpi isn't a criteria */
+ if (dev->model->is_cis == SANE_FALSE)
+ {
+ resolution = dev->settings.xres;
+ if(resolution>dev->sensor.optical_res)
+ {
+ resolution=dev->sensor.optical_res;
+ }
+ compatible = (resolution == ((int) cache->used_setup.xres));
+ }
+ else
+ {
+ resolution=sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ compatible = (resolution == ((int) sanei_genesys_compute_dpihw(dev,cache->used_setup.xres)));
+ }
+ if (dev->current_setup.half_ccd != cache->used_setup.half_ccd)
+ {
+ DBG (DBG_io,
+ "sanei_genesys_is_compatible_calibration: half_ccd=%d, used=%d\n",
+ dev->current_setup.half_ccd, cache->used_setup.half_ccd);
+ compatible = 0;
+ }
+ if (dev->current_setup.scan_method != cache->used_setup.scan_method)
+ {
+ DBG (DBG_io,
+ "sanei_genesys_is_compatible_calibration: current method=%d, used=%d\n",
+ dev->current_setup.scan_method, cache->used_setup.scan_method);
+ compatible = 0;
+ }
+ if (!compatible)
+ {
+ DBG (DBG_proc,
+ "sanei_genesys_is_compatible_calibration: completed, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* a cache entry expires after 60 minutes for non sheetfed scanners */
+ /* this is not taken into account when overwriting cache entries */
+#ifdef HAVE_SYS_TIME_H
+ if(for_overwrite == SANE_FALSE)
+ {
+ gettimeofday (&time, NULL);
+ if ((time.tv_sec - cache->last_calibration > 60 * 60)
+ && (dev->model->is_sheetfed == SANE_FALSE)
+ && (dev->settings.scan_method == SCAN_METHOD_FLATBED))
+ {
+ DBG (DBG_proc,
+ "sanei_genesys_is_compatible_calibration: expired entry, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+#endif
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief compute maximum line distance shift
+ * compute maximum line distance shift for the motor and sensor
+ * combination. Line distance shift is the distance between different
+ * color component of CCD sensors. Since these components aren't at
+ * the same physical place, they scan diffrent lines. Software must
+ * take this into account to accurately mix color data.
+ * @param dev device session to compute max_shift for
+ * @param channels number of color channels for the scan
+ * @param yres motor resolution used for the scan
+ * @param flags scan flags
+ * @return 0 or line distance shift
+ */
+int sanei_genesys_compute_max_shift(Genesys_Device *dev,
+ int channels,
+ int yres,
+ int flags)
+{
+ int max_shift;
+
+ max_shift=0;
+ if (channels > 1 && !(flags & SCAN_FLAG_IGNORE_LINE_DISTANCE))
+ {
+ max_shift = dev->ld_shift_r;
+ if (dev->ld_shift_b > max_shift)
+ max_shift = dev->ld_shift_b;
+ if (dev->ld_shift_g > max_shift)
+ max_shift = dev->ld_shift_g;
+ max_shift = (max_shift * yres) / dev->motor.base_ydpi;
+ }
+ return max_shift;
+}
+
+/** @brief build lookup table for digital enhancements
+ * Function to build a lookup table (LUT), often
+ used by scanners to implement brightness/contrast/gamma
+ or by backends to speed binarization/thresholding
+
+ offset and slope inputs are -127 to +127
+
+ slope rotates line around central input/output val,
+ 0 makes horizontal line
+
+ pos zero neg
+ . x . . x
+ . x . . x
+ out . x .xxxxxxxxxxx . x
+ . x . . x
+ ....x....... ............ .......x....
+ in in in
+
+ offset moves line vertically, and clamps to output range
+ 0 keeps the line crossing the center of the table
+
+ high low
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+
+ out_min/max provide bounds on output values,
+ useful when building thresholding lut.
+ 0 and 255 are good defaults otherwise.
+ * @param lut pointer where to store the generated lut
+ * @param in_bits number of bits for in values
+ * @param out_bits number of bits of out values
+ * @param out_min minimal out value
+ * @param out_max maximal out value
+ * @param slope slope of the generated data
+ * @param offset offset of the generated data
+ */
+SANE_Status
+sanei_genesys_load_lut (unsigned char * lut,
+ int in_bits,
+ int out_bits,
+ int out_min,
+ int out_max,
+ int slope,
+ int offset)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j;
+ double shift, rise;
+ int max_in_val = (1 << in_bits) - 1;
+ int max_out_val = (1 << out_bits) - 1;
+ uint8_t *lut_p8 = lut;
+ uint16_t *lut_p16 = (uint16_t *) lut;
+
+ DBGSTART;
+
+ /* slope is converted to rise per unit run:
+ * first [-127,127] to [-.999,.999]
+ * then to [-PI/4,PI/4] then [0,PI/2]
+ * then take the tangent (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ rise = tan ((double) slope / 128 * M_PI_4 + M_PI_4) * max_out_val / max_in_val;
+
+ /* line must stay vertically centered, so figure
+ * out vertical offset at central input value */
+ shift = (double) max_out_val / 2 - (rise * max_in_val / 2);
+
+ /* convert the user offset setting to scale of output
+ * first [-127,127] to [-1,1]
+ * then to [-max_out_val/2,max_out_val/2]*/
+ shift += (double) offset / 127 * max_out_val / 2;
+
+ for (i = 0; i <= max_in_val; i++)
+ {
+ j = rise * i + shift;
+
+ /* cap data to required range */
+ if (j < out_min)
+ {
+ j = out_min;
+ }
+ else if (j > out_max)
+ {
+ j = out_max;
+ }
+
+ /* copy result according to bit depth */
+ if (out_bits <= 8)
+ {
+ *lut_p8 = j;
+ lut_p8++;
+ }
+ else
+ {
+ *lut_p16 = j;
+ lut_p16++;
+ }
+ }
+
+ DBGCOMPLETED;
+ return ret;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/genesys_low.h b/backend/genesys_low.h
new file mode 100644
index 0000000..1d5ef22
--- /dev/null
+++ b/backend/genesys_low.h
@@ -0,0 +1,1206 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
+ Parts of the structs have been taken from the gt68xx backend by
+ Sergey Vlasov <vsu@altlinux.ru> et al.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GENESYS_LOW_H
+#define GENESYS_LOW_H
+
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+#include <stddef.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MKDIR
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+
+#include "../include/_stdint.h"
+
+#define DBG_error0 0 /* errors/warnings printed even with devuglevel 0 */
+#define DBG_error 1 /* fatal errors */
+#define DBG_init 2 /* initialization and scanning time messages */
+#define DBG_warn 3 /* warnings and non-fatal errors */
+#define DBG_info 4 /* informational messages */
+#define DBG_proc 5 /* starting/finishing functions */
+#define DBG_io 6 /* io functions */
+#define DBG_io2 7 /* io functions that are called very often */
+#define DBG_data 8 /* log image data */
+
+/**
+ * call a function and return on error
+ */
+#define RIE(function) \
+ do { status = function; \
+ if (status != SANE_STATUS_GOOD) \
+ { \
+ DBG(DBG_error, "%s: %s\n", __FUNCTION__, sane_strstatus (status)); \
+ return status; \
+ } \
+ } while (SANE_FALSE)
+
+#define RIEF(function, mem) \
+ do { status = function; \
+ if (status != SANE_STATUS_GOOD) \
+ { \
+ free(mem); \
+ DBG(DBG_error, "%s: %s\n", __FUNCTION__, sane_strstatus (status)); \
+ return status; \
+ } \
+ } while (SANE_FALSE)
+
+#define RIEF2(function, mem1, mem2) \
+ do { status = function; \
+ if (status != SANE_STATUS_GOOD) \
+ { \
+ free(mem1); \
+ free(mem2); \
+ return status; \
+ } \
+ } while (SANE_FALSE)
+
+#define DBGSTART DBG (DBG_proc, "%s start\n", __FUNCTION__);
+#define DBGCOMPLETED DBG (DBG_proc, "%s completed\n", __FUNCTION__);
+
+#define FREE_IFNOT_NULL(x) if(x!=NULL) { free(x); x=NULL;}
+
+#define GENESYS_RED 0
+#define GENESYS_GREEN 1
+#define GENESYS_BLUE 2
+
+/* Flags */
+#define GENESYS_FLAG_UNTESTED (1 << 0) /**< Print a warning for these scanners */
+#define GENESYS_FLAG_14BIT_GAMMA (1 << 1) /**< use 14bit Gamma table instead of 12 */
+#define GENESYS_FLAG_LAZY_INIT (1 << 2) /**< skip extensive ASIC test at init */
+#define GENESYS_FLAG_XPA (1 << 3)
+#define GENESYS_FLAG_SKIP_WARMUP (1 << 4) /**< skip genesys_warmup() */
+/** @brief offset calibration flag
+ * signals that the scanner does offset calibration. In this case off_calibration() and
+ * coarse_gain_calibration() functions must be implemented
+ */
+#define GENESYS_FLAG_OFFSET_CALIBRATION (1 << 5)
+#define GENESYS_FLAG_SEARCH_START (1 << 6) /**< do start search before scanning */
+#define GENESYS_FLAG_REPARK (1 << 7) /**< repark head (and check for lock) by
+ moving without scanning */
+#define GENESYS_FLAG_DARK_CALIBRATION (1 << 8) /**< do dark calibration */
+#define GENESYS_FLAG_STAGGERED_LINE (1 << 9) /**< pixel columns are shifted vertically for hi-res modes */
+
+#define GENESYS_FLAG_MUST_WAIT (1 << 10) /**< tells wether the scanner must wait for the head when parking */
+
+
+#define GENESYS_FLAG_HAS_UTA (1 << 11) /**< scanner has a transparency adapter */
+
+#define GENESYS_FLAG_DARK_WHITE_CALIBRATION (1 << 12) /**< yet another calibration method. does white and dark shading in one run, depending on a black and a white strip*/
+#define GENESYS_FLAG_CUSTOM_GAMMA (1 << 13) /**< allow custom gamma tables */
+#define GENESYS_FLAG_NO_CALIBRATION (1 << 14) /**< allow scanners to use skip the calibration, needed for sheetfed scanners */
+#define GENESYS_FLAG_HALF_CCD_MODE (1 << 15) /**< scanner has setting for half ccd mode */
+#define GENESYS_FLAG_SIS_SENSOR (1 << 16) /**< handling of multi-segments sensors in software */
+#define GENESYS_FLAG_SHADING_NO_MOVE (1 << 17) /**< scanner doesn't move sensor during shading calibration */
+#define GENESYS_FLAG_SHADING_REPARK (1 << 18) /**< repark head between shading scans */
+#define GENESYS_FLAG_FULL_HWDPI_MODE (1 << 19) /**< scanner always use maximum hw dpi to setup the sensor */
+
+#define GENESYS_HAS_NO_BUTTONS 0 /**< scanner has no supported button */
+#define GENESYS_HAS_SCAN_SW (1 << 0) /**< scanner has SCAN button */
+#define GENESYS_HAS_FILE_SW (1 << 1) /**< scanner has FILE button */
+#define GENESYS_HAS_COPY_SW (1 << 2) /**< scanner has COPY button */
+#define GENESYS_HAS_EMAIL_SW (1 << 3) /**< scanner has EMAIL button */
+#define GENESYS_HAS_PAGE_LOADED_SW (1 << 4) /**< scanner has paper in detection */
+#define GENESYS_HAS_OCR_SW (1 << 5) /**< scanner has OCR button */
+#define GENESYS_HAS_POWER_SW (1 << 6) /**< scanner has power button */
+#define GENESYS_HAS_CALIBRATE (1 << 7) /**< scanner has 'calibrate' software button to start calibration */
+#define GENESYS_HAS_EXTRA_SW (1 << 8) /**< scanner has extra function button */
+
+/* USB control message values */
+#define REQUEST_TYPE_IN (USB_TYPE_VENDOR | USB_DIR_IN)
+#define REQUEST_TYPE_OUT (USB_TYPE_VENDOR | USB_DIR_OUT)
+#define REQUEST_REGISTER 0x0c
+#define REQUEST_BUFFER 0x04
+#define VALUE_BUFFER 0x82
+#define VALUE_SET_REGISTER 0x83
+#define VALUE_READ_REGISTER 0x84
+#define VALUE_WRITE_REGISTER 0x85
+#define VALUE_INIT 0x87
+#define GPIO_OUTPUT_ENABLE 0x89
+#define GPIO_READ 0x8a
+#define GPIO_WRITE 0x8b
+#define VALUE_BUF_ENDACCESS 0x8c
+#define VALUE_GET_REGISTER 0x8e
+#define INDEX 0x00
+
+/* todo: used?
+#define VALUE_READ_STATUS 0x86
+*/
+
+/* Read/write bulk data/registers */
+#define BULK_OUT 0x01
+#define BULK_IN 0x00
+#define BULK_RAM 0x00
+#define BULK_REGISTER 0x11
+
+#define BULKIN_MAXSIZE 0xFE00
+#define GL646_BULKIN_MAXSIZE 0xFFC0
+#define GL646_BULKIN_MINSIZE 0x0800
+#define BULKOUT_MAXSIZE 0xF000
+
+/* AFE values */
+#define AFE_INIT 1
+#define AFE_SET 2
+#define AFE_POWER_SAVE 4
+
+#define LOWORD(x) ((uint16_t)((x) & 0xffff))
+#define HIWORD(x) ((uint16_t)((x) >> 16))
+#define LOBYTE(x) ((uint8_t)((x) & 0xFF))
+#define HIBYTE(x) ((uint8_t)((x) >> 8))
+
+/* Global constants */
+/* TODO: emove this leftover of early backend days */
+#define MOTOR_SPEED_MAX 350
+#define DARK_VALUE 0
+
+#define PWRBIT 0x80
+#define BUFEMPTY 0x40
+#define FEEDFSH 0x20
+#define SCANFSH 0x10
+#define HOMESNR 0x08
+#define LAMPSTS 0x04
+#define FEBUSY 0x02
+#define MOTORENB 0x01
+
+typedef struct Genesys_Register_Set
+{
+ uint16_t address;
+ uint8_t value;
+} Genesys_Register_Set;
+
+/** @brief Data structure to set up analog frontend.
+ * The analog frontend converts analog value from image sensor to
+ * digital value. It has its own control registers which are set up
+ * with this structure. The values are written using sanei_genesys_fe_write_data.
+ * The actual register addresses they map to depends on the frontend used.
+ * @see sanei_genesys_fe_write_data
+ */
+typedef struct
+{
+ uint8_t fe_id; /**< id of the frontend description */
+ uint8_t reg[4]; /**< values to set up frontend control register, they
+ usually map to analog register 0x00 to 0x03 */
+ uint8_t sign[3]; /**< sets the sign of the digital value */
+ uint8_t offset[3]; /**< offset correction to apply to signal, most often
+ maps to frontend register 0x20-0x22 */
+ uint8_t gain[3]; /**< amplification to apply to signal, most often
+ maps to frontend register 0x28-0x2a */
+ uint8_t reg2[3]; /**< extra control registers */
+} Genesys_Frontend;
+
+typedef struct
+{
+ uint8_t sensor_id; /**< id of the sensor description */
+ int optical_res;
+ int black_pixels;
+ int dummy_pixel; /* value of dummy register. */
+ int CCD_start_xoffset; /* last pixel of CCD margin at optical resolution */
+ int sensor_pixels; /* total pixels used by the sensor */
+ int fau_gain_white_ref; /* TA CCD target code (reference gain) */
+ int gain_white_ref; /* CCD target code (reference gain) */
+ uint8_t regs_0x08_0x0b[4];
+ uint8_t regs_0x10_0x1d[14];
+ uint8_t regs_0x52_0x5e[13];
+ float gamma[3]; /**< red, green and blue gamma coefficient for default gamma tables */
+ uint16_t *gamma_table[3]; /**< sensor specific gamma tables */
+} Genesys_Sensor;
+
+typedef struct
+{
+ uint8_t gpo_id; /**< id of the gpo description */
+ uint8_t value[2]; /**< registers 0x6c and 0x6d on gl843 */
+ uint8_t enable[2]; /**< registers 0x6e and 0x6F on gl843 */
+} Genesys_Gpo;
+
+typedef struct
+{
+ SANE_Int maximum_start_speed; /* maximum speed allowed when accelerating from standstill. Unit: pixeltime/step */
+ SANE_Int maximum_speed; /* maximum speed allowed. Unit: pixeltime/step */
+ SANE_Int minimum_steps; /* number of steps used for default curve */
+ float g; /* power for non-linear acceleration curves. */
+/* vs*(1-i^g)+ve*(i^g) where
+ vs = start speed, ve = end speed,
+ i = 0.0 for first entry and i = 1.0 for last entry in default table*/
+} Genesys_Motor_Slope;
+
+
+typedef struct
+{
+ uint8_t motor_id; /**< id of the motor description */
+ SANE_Int base_ydpi; /* motor base steps. Unit: 1/" */
+ SANE_Int optical_ydpi; /* maximum resolution in y-direction. Unit: 1/" */
+ SANE_Int max_step_type; /* maximum step type. 0-2 */
+ SANE_Int power_mode_count; /* number of power modes*/
+ Genesys_Motor_Slope slopes[2][3]; /* slopes to derive individual slopes from */
+} Genesys_Motor;
+
+typedef enum Genesys_Color_Order
+{
+ COLOR_ORDER_RGB,
+ COLOR_ORDER_BGR
+}
+Genesys_Color_Order;
+
+
+#define MAX_SCANNERS 50
+#define MAX_RESOLUTIONS 13
+#define MAX_DPI 4
+
+#define GENESYS_GL646 646
+#define GENESYS_GL841 841
+#define GENESYS_GL843 843
+#define GENESYS_GL845 845
+#define GENESYS_GL846 846
+#define GENESYS_GL847 847
+#define GENESYS_GL848 848
+#define GENESYS_GL123 123
+#define GENESYS_GL124 124
+
+#define GENESYS_MAX_REGS 256
+
+#define DAC_WOLFSON_UMAX 0
+#define DAC_WOLFSON_ST12 1
+#define DAC_WOLFSON_ST24 2
+#define DAC_WOLFSON_5345 3
+#define DAC_WOLFSON_HP2400 4
+#define DAC_WOLFSON_HP2300 5
+#define DAC_CANONLIDE35 6
+#define DAC_AD_XP200 7 /* Analog Device frontend */
+#define DAC_WOLFSON_XP300 8
+#define DAC_WOLFSON_HP3670 9
+#define DAC_WOLFSON_DSM600 10
+#define DAC_CANONLIDE200 11
+#define DAC_KVSS080 12
+#define DAC_G4050 13
+#define DAC_CANONLIDE110 14
+#define DAC_PLUSTEK_3600 15
+#define DAC_CANONLIDE700 16
+#define DAC_CS8400F 17
+#define DAC_IMG101 18
+#define DAC_PLUSTEK3800 19
+
+#define CCD_UMAX 0
+#define CCD_ST12 1 /* SONY ILX548: 5340 Pixel ??? */
+#define CCD_ST24 2 /* SONY ILX569: 10680 Pixel ??? */
+#define CCD_5345 3
+#define CCD_HP2400 4
+#define CCD_HP2300 5
+#define CCD_CANONLIDE35 6
+#define CIS_XP200 7 /* CIS sensor for Strobe XP200 */
+ /* 8 is unused currently */
+#define CCD_HP3670 9
+#define CCD_DP665 10
+#define CCD_ROADWARRIOR 11
+#define CCD_DSMOBILE600 12
+#define CCD_XP300 13
+#define CCD_DP685 14
+#define CIS_CANONLIDE200 15
+#define CIS_CANONLIDE100 16
+#define CCD_KVSS080 17
+#define CCD_G4050 18
+#define CIS_CANONLIDE110 19
+#define CCD_PLUSTEK_3600 20
+#define CCD_HP_N6310 21
+#define CIS_CANONLIDE700 22
+#define CCD_CS4400F 23
+#define CCD_CS8400F 24
+#define CCD_IMG101 25
+#define CCD_PLUSTEK3800 26
+#define CIS_CANONLIDE210 27
+
+#define GPO_UMAX 0
+#define GPO_ST12 1
+#define GPO_ST24 2
+#define GPO_5345 3
+#define GPO_HP2400 4
+#define GPO_HP2300 5
+#define GPO_CANONLIDE35 6
+#define GPO_XP200 7
+#define GPO_XP300 8
+#define GPO_HP3670 9
+#define GPO_DP665 10
+#define GPO_DP685 11
+#define GPO_CANONLIDE200 12
+#define GPO_KVSS080 13
+#define GPO_G4050 14
+#define GPO_CANONLIDE110 15
+#define GPO_PLUSTEK_3600 16
+#define GPO_CANONLIDE210 17
+#define GPO_HP_N6310 18
+#define GPO_CANONLIDE700 19
+#define GPO_CS4400F 20
+#define GPO_CS8400F 21
+#define GPO_IMG101 22
+#define GPO_PLUSTEK3800 23
+
+#define MOTOR_UMAX 0
+#define MOTOR_5345 1
+#define MOTOR_ST24 2
+#define MOTOR_HP2400 3
+#define MOTOR_HP2300 4
+#define MOTOR_CANONLIDE35 5
+#define MOTOR_XP200 6
+#define MOTOR_XP300 7
+#define MOTOR_HP3670 9
+#define MOTOR_DP665 10
+#define MOTOR_ROADWARRIOR 11
+#define MOTOR_DSMOBILE_600 12
+#define MOTOR_CANONLIDE200 13
+#define MOTOR_CANONLIDE100 14
+#define MOTOR_KVSS080 15
+#define MOTOR_G4050 16
+#define MOTOR_CANONLIDE110 17
+#define MOTOR_PLUSTEK_3600 18
+#define MOTOR_CANONLIDE700 19
+#define MOTOR_CS8400F 20
+#define MOTOR_IMG101 21
+#define MOTOR_PLUSTEK3800 22
+#define MOTOR_CANONLIDE210 23
+
+
+/* Forward typedefs */
+typedef struct Genesys_Device Genesys_Device;
+struct Genesys_Scanner;
+typedef struct Genesys_Calibration_Cache Genesys_Calibration_Cache;
+
+/**
+ * Scanner command set description.
+ *
+ * This description contains parts which are common to all scanners with the
+ * same command set, but may have different optical resolution and other
+ * parameters.
+ */
+typedef struct Genesys_Command_Set
+{
+ /** @name Identification */
+ /*@{ */
+
+ /** Name of this command set */
+ SANE_String_Const name;
+
+ /*@} */
+
+ /** For ASIC initialization */
+ SANE_Status (*init) (Genesys_Device * dev);
+
+ SANE_Status (*init_regs_for_warmup) (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ int *channels, int *total_size);
+ SANE_Status (*init_regs_for_coarse_calibration) (Genesys_Device * dev);
+ SANE_Status (*init_regs_for_shading) (Genesys_Device * dev);
+ SANE_Status (*init_regs_for_scan) (Genesys_Device * dev);
+
+ SANE_Bool (*get_filter_bit) (Genesys_Register_Set * reg);
+ SANE_Bool (*get_lineart_bit) (Genesys_Register_Set * reg);
+ SANE_Bool (*get_bitset_bit) (Genesys_Register_Set * reg);
+ SANE_Bool (*get_gain4_bit) (Genesys_Register_Set * reg);
+ SANE_Bool (*get_fast_feed_bit) (Genesys_Register_Set * reg);
+
+ SANE_Bool (*test_buffer_empty_bit) (SANE_Byte val);
+ SANE_Bool (*test_motor_flag_bit) (SANE_Byte val);
+
+ int (*bulk_full_size) (void);
+
+ SANE_Status (*set_fe) (Genesys_Device * dev, uint8_t set);
+ SANE_Status (*set_powersaving) (Genesys_Device * dev, int delay);
+ SANE_Status (*save_power) (Genesys_Device * dev, SANE_Bool enable);
+
+ void (*set_motor_power) (Genesys_Register_Set * regs, SANE_Bool set);
+ void (*set_lamp_power) (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ SANE_Bool set);
+
+ SANE_Status (*begin_scan) (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ SANE_Bool start_motor);
+ SANE_Status (*end_scan) (Genesys_Device * dev,
+ Genesys_Register_Set * regs,
+ SANE_Bool check_stop);
+
+ /**
+ * Send gamma tables to ASIC
+ */
+ SANE_Status (*send_gamma_table) (Genesys_Device * dev);
+
+ SANE_Status (*search_start_position) (Genesys_Device * dev);
+ SANE_Status (*offset_calibration) (Genesys_Device * dev);
+ SANE_Status (*coarse_gain_calibration) (Genesys_Device * dev, int dpi);
+ SANE_Status (*led_calibration) (Genesys_Device * dev);
+
+ SANE_Status (*slow_back_home) (Genesys_Device * dev,
+ SANE_Bool wait_until_home);
+
+ SANE_Status (*bulk_write_register) (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ size_t elems);
+ SANE_Status (*bulk_write_data) (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len);
+
+ SANE_Status (*bulk_read_data) (Genesys_Device * dev, uint8_t addr,
+ uint8_t * data, size_t len);
+
+ /* Updates hardware sensor information in Genesys_Scanner.val[].
+ If possible, just get information for given option.
+ The sensor state in Genesys_Scanner.val[] should be merged with the
+ new sensor state, using the information that was last read by the frontend
+ in Genesys_Scanner.last_val[], in such a way that a button up/down
+ relative to Genesys_Scanner.last_val[] is not lost.
+ */
+ SANE_Status (*update_hardware_sensors) (struct Genesys_Scanner * s);
+
+ /* functions for sheetfed scanners */
+ /**
+ * load document into scanner
+ */
+ SANE_Status (*load_document) (Genesys_Device * dev);
+ /**
+ * detects is the scanned document has left scanner. In this
+ * case it updates the amount of data to read and set up
+ * flags in the dev struct
+ */
+ SANE_Status (*detect_document_end) (Genesys_Device * dev);
+ /**
+ * eject document from scanner
+ */
+ SANE_Status (*eject_document) (Genesys_Device * dev);
+ /**
+ * search for an black or white area in forward or reverse
+ * direction */
+ SANE_Status (*search_strip) (Genesys_Device * dev, SANE_Bool forward, SANE_Bool black);
+
+ SANE_Status (*is_compatible_calibration) (
+ Genesys_Device * dev,
+ Genesys_Calibration_Cache *cache,
+ SANE_Bool for_overwrite);
+
+ /* functions for transparency adapter */
+ /**
+ * move scanning head to transparency adapter
+ */
+ SANE_Status (*move_to_ta) (Genesys_Device * dev);
+
+ /**
+ * write shading data calibration to ASIC
+ */
+ SANE_Status (*send_shading_data) (Genesys_Device * dev, uint8_t * data, int size);
+
+ /**
+ * calculate current scan setup
+ */
+ SANE_Status (*calculate_current_setup) (Genesys_Device * dev);
+
+ /**
+ * cold boot init function
+ */
+ SANE_Status (*asic_boot) (Genesys_Device * dev, SANE_Bool cold);
+
+} Genesys_Command_Set;
+
+/** @brief structure to describe a scanner model
+ * This structure describes a model. It is composed of information on the
+ * sensor, the motor, scanner geometry and flags to drive operation.
+ */
+typedef struct Genesys_Model
+{
+ SANE_String_Const name;
+ SANE_String_Const vendor;
+ SANE_String_Const model;
+
+ SANE_Int asic_type; /* ASIC type gl646 or gl841 */
+ Genesys_Command_Set *cmd_set; /* pointers to low level functions */
+
+ SANE_Int xdpi_values[MAX_RESOLUTIONS]; /* possible x resolutions */
+ SANE_Int ydpi_values[MAX_RESOLUTIONS]; /* possible y resolutions */
+ SANE_Int bpp_gray_values[MAX_DPI]; /* possible depths in gray mode */
+ SANE_Int bpp_color_values[MAX_DPI]; /* possible depths in color mode */
+
+ SANE_Fixed x_offset; /* Start of scan area in mm */
+ SANE_Fixed y_offset; /* Start of scan area in mm (Amount of
+ feeding needed to get to the medium) */
+ SANE_Fixed x_size; /* Size of scan area in mm */
+ SANE_Fixed y_size; /* Size of scan area in mm */
+
+ SANE_Fixed y_offset_calib; /* Start of white strip in mm */
+ SANE_Fixed x_offset_mark; /* Start of black mark in mm */
+
+ SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */
+ SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */
+
+ SANE_Fixed y_offset_calib_ta; /* Start of white strip in TA mode in mm */
+
+ SANE_Fixed post_scan; /* Size of scan area after paper sensor stops
+ sensing document in mm */
+ SANE_Fixed eject_feed; /* Amount of feeding needed to eject document
+ after finishing scanning in mm */
+
+ /* Line-distance correction (in pixel at optical_ydpi) for CCD scanners */
+ SANE_Int ld_shift_r; /* red */
+ SANE_Int ld_shift_g; /* green */
+ SANE_Int ld_shift_b; /* blue */
+
+ Genesys_Color_Order line_mode_color_order; /* Order of the CCD/CIS colors */
+
+ SANE_Bool is_cis; /* Is this a CIS or CCD scanner? */
+ SANE_Bool is_sheetfed; /* Is this sheetfed scanner? */
+
+ SANE_Int ccd_type; /* which SENSOR type do we have ? */
+ SANE_Int dac_type; /* which DAC do we have ? */
+ SANE_Int gpo_type; /* General purpose output type */
+ SANE_Int motor_type; /* stepper motor type */
+ SANE_Word flags; /* Which hacks are needed for this scanner? */
+ SANE_Word buttons; /* Button flags, described existing buttons for the model */
+ /*@} */
+ SANE_Int shading_lines; /* how many lines are used for shading calibration */
+ SANE_Int search_lines; /* how many lines are used to search start position */
+} Genesys_Model;
+
+#define SCAN_METHOD_FLATBED 0 /**< normal scan method */
+#define SCAN_METHOD_TRANSPARENCY 2 /**< scan using transparency adaptor */
+#define SCAN_METHOD_NEGATIVE 0x88 /**< scan using negative adaptor */
+
+#define SCAN_MODE_LINEART 0 /**< lineart scan mode */
+#define SCAN_MODE_HALFTONE 1 /**< halftone scan mode */
+#define SCAN_MODE_GRAY 2 /**< gray scan mode */
+#define SCAN_MODE_COLOR 4 /**< color scan mode */
+
+typedef struct
+{
+ int scan_method; /* todo: change >=2: Transparency, 0x88: negative film */
+ int scan_mode; /* todo: change 0,1 = lineart, halftone; 2 = gray, 3 = 3pass color, 4=single pass color */
+ int xres; /**< horizontal dpi */
+ int yres; /**< vertical dpi */
+
+ double tl_x; /* x start on scan table in mm */
+ double tl_y; /* y start on scan table in mm */
+
+ unsigned int lines; /**< number of lines at scan resolution */
+ unsigned int pixels; /**< number of pixels at scan resolution */
+
+ unsigned int depth;/* bit depth of the scan */
+
+ /* todo : remove these fields ? */
+ int exposure_time;
+
+ unsigned int color_filter;
+
+ /**< true if scan is true gray, false if monochrome scan */
+ int true_gray;
+
+ /**< lineart threshold */
+ int threshold;
+
+ /**< lineart threshold curve for dynamic rasterization */
+ int threshold_curve;
+
+ /**< Disable interpolation for xres<yres*/
+ int disable_interpolation;
+
+ /**< Use double x resolution internally to provide better
+ * quality */
+ int double_xres;
+
+ /**< true is lineart is generated from gray data by
+ * the dynamic rasterization algo */
+ int dynamic_lineart;
+
+ /**< value for contrast enhancement in the [-100..100] range */
+ int contrast;
+
+ /**< value for brightness enhancement in the [-100..100] range */
+ int brightness;
+} Genesys_Settings;
+
+typedef struct Genesys_Current_Setup
+{
+ int pixels; /* pixel count expected from scanner */
+ int lines; /* line count expected from scanner */
+ int depth; /* depth expected from scanner */
+ int channels; /* channel count expected from scanner */
+ int scan_method; /* scanning method: flatbed or XPA */
+ int exposure_time; /* used exposure time */
+ float xres; /* used xres */
+ float yres; /* used yres*/
+ SANE_Bool half_ccd; /* half ccd mode */
+ SANE_Int stagger;
+ SANE_Int max_shift; /* max shift of any ccd component, including staggered pixels*/
+} Genesys_Current_Setup;
+
+typedef struct Genesys_Buffer
+{
+ SANE_Byte *buffer;
+ size_t size;
+ size_t pos; /* current position in read buffer */
+ size_t avail; /* data bytes currently in buffer */
+} Genesys_Buffer;
+
+struct Genesys_Calibration_Cache
+{
+ Genesys_Current_Setup used_setup;/* used to check if entry is compatible */
+ time_t last_calibration;
+
+ Genesys_Frontend frontend;
+ Genesys_Sensor sensor;
+
+ size_t calib_pixels;
+ size_t calib_channels;
+ size_t average_size;
+ uint8_t *white_average_data;
+ uint8_t *dark_average_data;
+
+ struct Genesys_Calibration_Cache *next;
+};
+
+/**
+ * Describes the current device status for the backend
+ * session. This should be more accurately called
+ * Genesys_Session .
+ */
+struct Genesys_Device
+{
+ SANE_Int dn;
+ SANE_Word vendorId; /**< USB vendor identifier */
+ SANE_Word productId; /**< USB product identifier */
+ SANE_Int usb_mode; /**< USB mode: 1 for USB 1.1, 2 for USB 2.0,
+ 0 unset and -1 for fake USB device */
+ SANE_String file_name;
+ SANE_String calib_file;
+ Genesys_Model *model;
+
+ Genesys_Register_Set reg[256];
+ Genesys_Register_Set calib_reg[256];
+ Genesys_Settings settings;
+ Genesys_Frontend frontend;
+ Genesys_Sensor sensor;
+ Genesys_Gpo gpo;
+ Genesys_Motor motor;
+ uint16_t slope_table0[256];
+ uint16_t slope_table1[256];
+ uint8_t control[6];
+ time_t init_date;
+
+ size_t average_size;
+ size_t calib_pixels; /**< number of pixels used during shading calibration */
+ size_t calib_lines; /**< number of lines used during shading calibration */
+ size_t calib_channels;
+ size_t calib_resolution;
+ uint8_t *white_average_data;
+ uint8_t *dark_average_data;
+ uint16_t dark[3];
+
+ SANE_Bool already_initialized;
+ SANE_Int scanhead_position_in_steps;
+ SANE_Int lamp_off_time;
+
+ SANE_Bool read_active;
+ SANE_Bool parking; /**< signal wether the park command has been issued */
+ SANE_Bool document; /**< for sheetfed scanner's, is TRUE when there
+ is a document in the scanner */
+
+ Genesys_Buffer read_buffer;
+ Genesys_Buffer lines_buffer;
+ Genesys_Buffer shrink_buffer;
+ Genesys_Buffer out_buffer;
+ Genesys_Buffer binarize_buffer; /**< buffer for digital lineart from gray data */
+ Genesys_Buffer local_buffer; /**< local buffer for gray data during dynamix lineart */
+
+ size_t read_bytes_left; /**< bytes to read from scanner */
+
+ size_t total_bytes_read; /**< total bytes read sent to frontend */
+ size_t total_bytes_to_read; /**< total bytes read to be sent to frontend */
+ size_t wpl; /**< asic's word per line */
+
+ Genesys_Current_Setup current_setup; /* contains the real used values */
+
+ /**< look up table used in dynamic rasterization */
+ unsigned char lineart_lut[256];
+
+ Genesys_Calibration_Cache *calibration_cache;
+
+ struct Genesys_Device *next;
+
+ SANE_Int ld_shift_r; /**< used red line-distance shift*/
+ SANE_Int ld_shift_g; /**< used green line-distance shift*/
+ SANE_Int ld_shift_b; /**< used blue line-distance shift*/
+ int segnb; /**< number of segments composing the sensor */
+ int line_interp; /**< number of lines used in line interpolation */
+ int line_count; /**< number of scan lines used during scan */
+ size_t bpl; /**< bytes per full scan widthline */
+ size_t dist; /**< bytes distance between an odd and an even pixel */
+ size_t len; /**< number of even pixels */
+ size_t cur; /**< current pixel position within sub window */
+ size_t skip; /**< number of bytes to skip at start of line */
+ size_t *order; /**< array describing the order of the sub-segments of the sensor */
+ Genesys_Buffer oe_buffer; /**< buffer to handle even/odd data */
+
+ SANE_Bool buffer_image; /**< when true the scanned picture is first buffered
+ * to allow software image enhancements */
+ SANE_Byte *img_buffer; /**< image buffer where the scanned picture is stored */
+
+ FILE *binary; /**< binary logger file */
+};
+
+typedef struct Genesys_USB_Device_Entry
+{
+ SANE_Word vendor; /**< USB vendor identifier */
+ SANE_Word product; /**< USB product identifier */
+ Genesys_Model *model; /**< Scanner model information */
+} Genesys_USB_Device_Entry;
+
+/**
+ * structure for motor database
+ */
+typedef struct {
+ int motor_type; /**< motor id */
+ int exposure; /**< exposure for the slope table */
+ int step_type; /**< default step type for given exposure */
+ uint32_t *table; /**< 0 terminated slope table at full step */
+} Motor_Profile;
+
+#define FULL_STEP 0
+#define HALF_STEP 1
+#define QUARTER_STEP 2
+#define EIGHTH_STEP 3
+
+#define SLOPE_TABLE_SIZE 1024
+
+#define SCAN_TABLE 0 /* table 1 at 0x4000 for gl124 */
+#define BACKTRACK_TABLE 1 /* table 2 at 0x4800 for gl124 */
+#define STOP_TABLE 2 /* table 3 at 0x5000 for gl124 */
+#define FAST_TABLE 3 /* table 4 at 0x5800 for gl124 */
+#define HOME_TABLE 4 /* table 5 at 0x6000 for gl124 */
+
+#define SCAN_FLAG_SINGLE_LINE 0x001
+#define SCAN_FLAG_DISABLE_SHADING 0x002
+#define SCAN_FLAG_DISABLE_GAMMA 0x004
+#define SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE 0x008
+#define SCAN_FLAG_IGNORE_LINE_DISTANCE 0x010
+#define SCAN_FLAG_USE_OPTICAL_RES 0x020
+#define SCAN_FLAG_DISABLE_LAMP 0x040
+#define SCAN_FLAG_DYNAMIC_LINEART 0x080
+#define SCAN_FLAG_CALIBRATION 0x100
+#define SCAN_FLAG_FEEDING 0x200
+#define SCAN_FLAG_USE_XPA 0x400
+
+#define MOTOR_FLAG_AUTO_GO_HOME 0x01
+#define MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE 0x02
+#define MOTOR_FLAG_FEED 0x04
+#define MOTOR_FLAG_USE_XPA 0x08
+
+/** @name "Optical flags" */
+/*@{ optical flags available when setting up sensor for scan */
+
+#define OPTICAL_FLAG_DISABLE_GAMMA 0x01 /**< disable gamma correction */
+#define OPTICAL_FLAG_DISABLE_SHADING 0x02 /**< disable shading correction */
+#define OPTICAL_FLAG_DISABLE_LAMP 0x04 /**< turn off lamp */
+#define OPTICAL_FLAG_ENABLE_LEDADD 0x08 /**< enable true CIS gray by enabling LED addition */
+#define OPTICAL_FLAG_DISABLE_DOUBLE 0x10 /**< disable automatic x-direction double data expansion */
+#define OPTICAL_FLAG_STAGGER 0x20 /**< disable stagger correction */
+#define OPTICAL_FLAG_USE_XPA 0x40 /**< use XPA lamp rather than regular one */
+
+/*@} */
+
+/*--------------------------------------------------------------------------*/
+/* common functions needed by low level specific functions */
+/*--------------------------------------------------------------------------*/
+
+extern Genesys_Register_Set *sanei_genesys_get_address (Genesys_Register_Set * regs, uint16_t addr);
+
+extern SANE_Byte
+sanei_genesys_read_reg_from_set (Genesys_Register_Set * regs, uint16_t address);
+
+extern void
+sanei_genesys_set_reg_from_set (Genesys_Register_Set * regs, uint16_t address, SANE_Byte value);
+
+extern SANE_Status sanei_genesys_init_cmd_set (Genesys_Device * dev);
+
+extern SANE_Status
+sanei_genesys_read_register (Genesys_Device * dev, uint16_t reg, uint8_t * val);
+
+extern SANE_Status
+sanei_genesys_write_register (Genesys_Device * dev, uint16_t reg, uint8_t val);
+
+extern SANE_Status
+sanei_genesys_read_hregister (Genesys_Device * dev, uint16_t reg, uint8_t * val);
+
+extern SANE_Status
+sanei_genesys_write_hregister (Genesys_Device * dev, uint16_t reg, uint8_t val);
+
+extern SANE_Status
+sanei_genesys_bulk_write_register (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ size_t elems);
+
+extern SANE_Status sanei_genesys_write_0x8c (Genesys_Device * dev, uint8_t index, uint8_t val);
+
+extern SANE_Status sanei_genesys_get_status (Genesys_Device * dev, uint8_t * status);
+
+extern void sanei_genesys_print_status (uint8_t val);
+
+extern SANE_Status
+sanei_genesys_write_ahb (SANE_Int dn, int usb_mode, uint32_t addr, uint32_t size, uint8_t * data);
+
+extern void sanei_genesys_init_fe (Genesys_Device * dev);
+
+extern void sanei_genesys_init_structs (Genesys_Device * dev);
+
+extern SANE_Status
+sanei_genesys_init_shading_data (Genesys_Device * dev, int pixels_per_line);
+
+extern SANE_Status sanei_genesys_read_valid_words (Genesys_Device * dev,
+ unsigned int *steps);
+
+extern SANE_Status sanei_genesys_read_scancnt (Genesys_Device * dev,
+ unsigned int *steps);
+
+extern SANE_Status sanei_genesys_read_feed_steps (Genesys_Device * dev,
+ unsigned int *steps);
+
+extern void
+sanei_genesys_calculate_zmode2 (SANE_Bool two_table,
+ uint32_t exposure_time,
+ uint16_t * slope_table,
+ int reg21,
+ int move, int reg22, uint32_t * z1,
+ uint32_t * z2);
+
+extern void
+sanei_genesys_calculate_zmode (uint32_t exposure_time,
+ uint32_t steps_sum,
+ uint16_t last_speed, uint32_t feedl,
+ uint8_t fastfed, uint8_t scanfed,
+ uint8_t fwdstep, uint8_t tgtime,
+ uint32_t * z1, uint32_t * z2);
+
+extern SANE_Status
+sanei_genesys_set_buffer_address (Genesys_Device * dev, uint32_t addr);
+
+/** @brief Reads data from frontend register.
+ * Reads data from the given frontend register. May be used to query
+ * analog frontend status by reading the right register.
+ */
+extern SANE_Status
+sanei_genesys_fe_read_data (Genesys_Device * dev, uint8_t addr,
+ uint16_t *data);
+/** @brief Write data to frontend register.
+ * Writes data to analog frontend register at the given address.
+ * The use and address of registers change from model to model.
+ */
+extern SANE_Status
+sanei_genesys_fe_write_data (Genesys_Device * dev, uint8_t addr,
+ uint16_t data);
+
+extern SANE_Int
+sanei_genesys_exposure_time2 (Genesys_Device * dev,
+ float ydpi, int step_type, int endpixel,
+ int led_exposure, int power_mode);
+
+extern SANE_Int
+sanei_genesys_exposure_time (Genesys_Device * dev, Genesys_Register_Set * reg,
+ int xdpi);
+extern SANE_Int
+sanei_genesys_generate_slope_table (uint16_t * slope_table, unsigned int max_steps,
+ unsigned int use_steps, uint16_t stop_at,
+ uint16_t vstart, uint16_t vend,
+ unsigned int steps, double g,
+ unsigned int *used_steps, unsigned int *vfinal);
+
+extern SANE_Int
+sanei_genesys_create_slope_table (Genesys_Device * dev,
+ uint16_t * slope_table, int steps,
+ int step_type, int exposure_time,
+ SANE_Bool same_speed, double yres,
+ int power_mode);
+
+SANE_Int
+sanei_genesys_create_slope_table3 (Genesys_Device * dev,
+ uint16_t * slope_table, int max_step,
+ unsigned int use_steps,
+ int step_type, int exposure_time,
+ double yres,
+ unsigned int *used_steps,
+ unsigned int *final_exposure,
+ int power_mode);
+
+extern void
+sanei_genesys_create_gamma_table (uint16_t * gamma_table, int size,
+ float maximum, float gamma_max,
+ float gamma);
+
+extern SANE_Status sanei_genesys_send_gamma_table (Genesys_Device * dev);
+
+extern SANE_Status sanei_genesys_start_motor (Genesys_Device * dev);
+
+extern SANE_Status sanei_genesys_stop_motor (Genesys_Device * dev);
+
+extern SANE_Status
+sanei_genesys_search_reference_point (Genesys_Device * dev, uint8_t * data,
+ int start_pixel, int dpi, int width,
+ int height);
+
+extern SANE_Status
+sanei_genesys_write_pnm_file (char *filename, uint8_t * data, int depth,
+ int channels, int pixels_per_line, int lines);
+
+extern SANE_Status
+sanei_genesys_test_buffer_empty (Genesys_Device * dev, SANE_Bool * empty);
+
+extern SANE_Status
+sanei_genesys_read_data_from_scanner (Genesys_Device * dev, uint8_t * data,
+ size_t size);
+
+extern SANE_Status
+sanei_genesys_buffer_alloc(Genesys_Buffer * buf, size_t size);
+
+extern SANE_Status
+sanei_genesys_buffer_free(Genesys_Buffer * buf);
+
+extern SANE_Byte *
+sanei_genesys_buffer_get_write_pos(Genesys_Buffer * buf, size_t size);
+
+extern SANE_Byte *
+sanei_genesys_buffer_get_read_pos(Genesys_Buffer * buf);
+
+extern SANE_Status
+sanei_genesys_buffer_produce(Genesys_Buffer * buf, size_t size);
+
+extern SANE_Status
+sanei_genesys_buffer_consume(Genesys_Buffer * buf, size_t size);
+
+extern SANE_Status
+sanei_genesys_set_double(Genesys_Register_Set *regs, uint16_t addr, uint16_t value);
+
+extern SANE_Status
+sanei_genesys_set_triple(Genesys_Register_Set *regs, uint16_t addr, uint32_t value);
+
+extern SANE_Status
+sanei_genesys_get_double(Genesys_Register_Set *regs, uint16_t addr, uint16_t *value);
+
+extern SANE_Status
+sanei_genesys_get_triple(Genesys_Register_Set *regs, uint16_t addr, uint32_t *value);
+
+extern SANE_Status
+sanei_genesys_wait_for_home(Genesys_Device *dev);
+
+extern SANE_Status
+sanei_genesys_asic_init(Genesys_Device *dev, SANE_Bool cold);
+
+extern
+int sanei_genesys_compute_dpihw(Genesys_Device *dev, int xres);
+
+extern
+Motor_Profile *sanei_genesys_get_motor_profile(Motor_Profile *motors, int motor_type, int exposure);
+
+extern
+int sanei_genesys_compute_step_type(Motor_Profile *motors, int motor_type, int exposure);
+
+extern
+int sanei_genesys_slope_table(uint16_t *slope, int *steps, int dpi, int exposure, int base_dpi, int step_type, int factor, int motor_type, Motor_Profile *motors);
+
+/** @brief find lowest motor resolution for the device.
+ * Parses the resolution list for motor and
+ * returns the lowest value.
+ * @param dev for which to find the lowest motor resolution
+ * @return the lowest available motor resolution for the device
+ */
+extern
+int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev);
+
+/** @brief find lowest resolution for the device.
+ * Parses the resolution list for motor and sensor and
+ * returns the lowest value.
+ * @param dev for which to find the lowest resolution
+ * @return the lowest available resolution for the device
+ */
+extern
+int sanei_genesys_get_lowest_dpi(Genesys_Device *dev);
+
+/**
+ * reads previously cached calibration data
+ * from file
+ */
+extern SANE_Status
+sanei_genesys_read_calibration (Genesys_Device * dev);
+
+extern SANE_Status
+sanei_genesys_is_compatible_calibration (Genesys_Device * dev,
+ Genesys_Calibration_Cache * cache,
+ int for_overwrite);
+
+/** @brief compute maximum line distance shift
+ * compute maximum line distance shift for the motor and sensor
+ * combination. Line distance shift is the distance between different
+ * color component of CCD sensors. Since these components aren't at
+ * the same physical place, they scan diffrent lines. Software must
+ * take this into account to accurately mix color data.
+ * @param dev device session to compute max_shift for
+ * @param channels number of color channels for the scan
+ * @param yres motor resolution used for the scan
+ * @param flags scan flags
+ * @return 0 or line distance shift
+ */
+extern
+int sanei_genesys_compute_max_shift(Genesys_Device *dev,
+ int channels,
+ int yres,
+ int flags);
+
+extern SANE_Status
+sanei_genesys_load_lut (unsigned char * lut,
+ int in_bits,
+ int out_bits,
+ int out_min,
+ int out_max,
+ int slope,
+ int offset);
+
+extern SANE_Status
+sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
+ int bits,
+ int max,
+ int size,
+ uint8_t *gamma);
+
+#ifdef UNIT_TESTING
+SANE_Status
+genesys_send_offset_and_shading (Genesys_Device * dev,
+ uint8_t * data,
+ int size);
+
+void
+genesys_average_data (uint8_t * average_data,
+ uint8_t * calibration_data,
+ uint32_t lines,
+ uint32_t pixel_components_per_line);
+
+void
+compute_averaged_planar (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int pixels_per_line,
+ unsigned int words_per_color,
+ unsigned int channels,
+ unsigned int o,
+ unsigned int coeff,
+ unsigned int target_bright,
+ unsigned int target_dark);
+
+
+void
+compute_coefficients (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int pixels_per_line,
+ unsigned int channels,
+ unsigned int cmat[3],
+ int offset,
+ unsigned int coeff,
+ unsigned int target);
+
+void
+compute_planar_coefficients (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int factor,
+ unsigned int pixels_per_line,
+ unsigned int words_per_color,
+ unsigned int channels,
+ unsigned int cmat[3],
+ unsigned int offset,
+ unsigned int coeff,
+ unsigned int target);
+
+void
+compute_shifted_coefficients (Genesys_Device * dev,
+ uint8_t * shading_data,
+ unsigned int pixels_per_line,
+ unsigned int channels,
+ unsigned int cmat[3],
+ int offset,
+ unsigned int coeff,
+ unsigned int target_dark,
+ unsigned int target_bright,
+ unsigned int patch_size); /* contigous extent */
+
+SANE_Status
+probe_genesys_devices (void);
+#endif
+
+
+/*---------------------------------------------------------------------------*/
+/* ASIC specific functions declarations */
+/*---------------------------------------------------------------------------*/
+extern SANE_Status sanei_gl646_init_cmd_set (Genesys_Device * dev);
+extern SANE_Status sanei_gl841_init_cmd_set (Genesys_Device * dev);
+extern SANE_Status sanei_gl843_init_cmd_set (Genesys_Device * dev);
+extern SANE_Status sanei_gl846_init_cmd_set (Genesys_Device * dev);
+extern SANE_Status sanei_gl847_init_cmd_set (Genesys_Device * dev);
+extern SANE_Status sanei_gl124_init_cmd_set (Genesys_Device * dev);
+
+#endif /* not GENESYS_LOW_H */
diff --git a/backend/gphoto2.c b/backend/gphoto2.c
new file mode 100644
index 0000000..7b297c4
--- /dev/null
+++ b/backend/gphoto2.c
@@ -0,0 +1,1995 @@
+/* Please note! Although intended to support multiple camera types
+ * it's been tested with only cameras I have access to: the Kodak DC240
+ * and the Directory Browse "camera." I'm very interested
+ * in learning what it would take to support more cameras. In
+ * particular, the current incarnation will only support cameras
+ * that directly generate jpeg files.
+ *
+ * Please report sucesses or failures using this backend!
+ *
+ * However, having said that, I've already found it to be quite useful
+ * even in its current form - one reason is that gphoto2 provides access
+ * to the camera via USB which is not supported by the regular DC240
+ * backend and is dramatically faster than the serial port.
+ */
+
+/***************************************************************************
+ * _S_A_N_E - Scanner Access Now Easy.
+
+ gphoto2.c
+
+ 03/12/01 - Peter Fales
+
+ Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is
+ based on dc25 driver (C) 1998 by Peter Fales)
+
+ This file (C) 2001 by Peter Fales
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for digital cameras
+ supported by the gphoto2 libraries.
+
+ THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
+
+ (feedback to: gphoto2-devel@fales-lorenz.net)
+
+ This backend is based somewhat on the dc25 backend included in this
+ package by Peter Fales, and the dc210 backend by Brian J. Murrell
+
+ ***************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "../include/sane/sanei_jpeg.h"
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME gphoto2
+#include "../include/sane/sanei_backend.h"
+
+/* PSF 1/12/02 - gphoto2.h does a #include of config.h. We don't have
+ * config.h by that name (we call it sane/config.h), so the #undef of
+ * HAVE_CONFIG_H will cause it to skip that.
+ */
+#undef HAVE_CONFIG_H
+#include "gphoto2.h"
+
+
+#include <gphoto2-camera.h>
+#include <gphoto2-port-log.h>
+
+#define CHECK_RET(f) {int res = f; if (res < 0) {DBG (1,"ERROR: %s\n", gp_result_as_string (res)); return (SANE_STATUS_INVAL);}}
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MAGIC (void *)0xab730324
+#define GPHOTO2_CONFIG_FILE "gphoto2.conf"
+
+static SANE_Bool is_open = 0;
+
+/* Options selected by frontend: */
+static SANE_Bool gphoto2_opt_thumbnails; /* Read thumbnails */
+static SANE_Bool gphoto2_opt_snap; /* Take new picture */
+static SANE_Bool gphoto2_opt_lowres; /* Set low resolution */
+static SANE_Bool gphoto2_opt_erase; /* Erase after downloading */
+static SANE_Bool gphoto2_opt_autoinc; /* Increment image number */
+static SANE_Bool dumpinquiry; /* Dump status info */
+
+/* Used for jpeg decompression */
+static struct jpeg_decompress_struct cinfo;
+static djpeg_dest_ptr dest_mgr = NULL;
+
+static SANE_Int highres_height = 960, highres_width = 1280;
+static SANE_Int thumb_height = 120, thumb_width = 160;
+static SANE_String TopFolder; /* Fixed part of path strings */
+static SANE_Int SubDirs = 1; /* Search for Sub directories */
+
+static GPHOTO2 Cam_data; /* Other camera data */
+
+static SANE_Range image_range = {
+ 0,
+ 0,
+ 0
+};
+
+static SANE_String *folder_list;
+static SANE_Int current_folder = 0;
+
+static SANE_Option_Descriptor sod[] = {
+ {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_IMAGE_SELECTION 1
+ {
+ "",
+ "Image Selection",
+ "Selection of the image to load.",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_FOLDER 2
+ {
+ "folder",
+ "Folder",
+ "Select folder within camera",
+ SANE_TYPE_STRING,
+ SANE_UNIT_NONE,
+ 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_STRING_LIST,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_IMAGE_NUMBER 3
+ {
+ "image",
+ "Image Number",
+ "Select Image Number to load from camera",
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ 4,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & image_range} /* this is ANSI conformant! */
+ }
+ ,
+
+#define GPHOTO2_OPT_THUMBS 4
+ {
+ "thumbs",
+ "Load Thumbnail",
+ "Load the image as thumbnail.",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_SNAP 5
+ {
+ "snap",
+ "Snap new picture",
+ "Take new picture and download it",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ ,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_LOWRES 6
+ {
+ "lowres",
+ "Low Resolution",
+ "Resolution of new picture or selected image (must be manually specified)",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE /* Until we figure out how to support it */
+ /* | SANE_CAP_ADVANCED */ ,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_ERASE 7
+ {
+ "erase",
+ "Erase",
+ "Erase the picture after downloading",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_DEFAULT 8
+ {
+ "default-enhancements",
+ "Defaults",
+ "Set default values for enhancement controls.",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_INIT_GPHOTO2 9
+ {
+ "camera-init",
+ "Re-establish Communications",
+ "Re-establish communications with camera (in case of timeout, etc.)",
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+#define GPHOTO2_OPT_AUTOINC 10
+ {
+ "autoinc",
+ "Auto Increment",
+ "Increment image number after each scan",
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+
+
+};
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+
+CameraList *dir_list;
+Camera *camera;
+
+/* Buffer to hold line currently being processed by sane_read */
+static SANE_Byte *linebuffer = NULL;
+static SANE_Int linebuffer_size = 0;
+static SANE_Int linebuffer_index = 0;
+
+/* used for setting up commands */
+static SANE_Char cmdbuf[256];
+
+/* Structures used by gphoto2 API */
+static CameraAbilities abilities;
+static CameraFile *data_file;
+static const unsigned char *data_ptr;
+static unsigned long data_file_total_size, data_file_current_index;
+
+static SANE_Int hack_fd;
+
+#include <sys/time.h>
+#include <unistd.h>
+
+/* Device select/open/close */
+
+static SANE_Device dev[] = {
+ {
+ "0",
+ "Gphoto2",
+ "Supported",
+ "still camera"},
+};
+
+static const SANE_Device *devlist[] = {
+ dev + 0, 0
+};
+
+/*
+ * debug_func - called for gphoto2 debugging output (if enabled)
+ */
+static void
+debug_func (GPLogLevel level, const char *domain, const char *format,
+ va_list args, void UNUSEDARG * data)
+{
+ if (level == GP_LOG_ERROR)
+ DBG (0, "%s(ERROR): ", domain);
+ else
+ DBG (0, "%s(%i): ", domain, level);
+ sanei_debug_msg (0, DBG_LEVEL, STRINGIFY (BACKEND_NAME), format, args);
+ DBG (0, "\n");
+}
+
+/*
+ * init_gphoto2() - Initialize interface to camera using gphoto2 API
+ */
+static SANE_Int
+init_gphoto2 (void)
+{
+ CameraList *list;
+ GPPortInfoList *il;
+ GPPortInfo info;
+ SANE_Int n, m, port;
+ CameraAbilitiesList *al;
+
+ gp_log (GP_LOG_VERBOSE, "SANE", "Initializing\n");
+
+ if (!Cam_data.camera_name)
+ {
+ DBG (0, "init_gphoto2: Camera name not specified in config file\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (camera)
+ {
+ /*
+ * We get here if re-initializing the camera: either because
+ * the user clicked the "re-establish" button, or we need to
+ * recalculate the number of photos after taking a picture.
+ * We must release the old camera before starting over.
+ */
+ CHECK_RET (gp_camera_unref (camera));
+ }
+
+ CHECK_RET (gp_camera_new (&camera));
+
+ CHECK_RET (gp_abilities_list_new (&al));
+ CHECK_RET (gp_abilities_list_load (al, NULL));
+ CHECK_RET (m =
+ gp_abilities_list_lookup_model (al,
+ (char *) Cam_data.camera_name));
+ CHECK_RET (gp_abilities_list_get_abilities (al, m, &abilities));
+ CHECK_RET (gp_abilities_list_free (al));
+ CHECK_RET (gp_camera_set_abilities (camera, abilities));
+
+ if (!Cam_data.port)
+ {
+ DBG (0, "init_gphoto2: Camera port not specified in config file\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ CHECK_RET (gp_port_info_list_new (&il));
+ CHECK_RET (gp_port_info_list_load (il));
+
+
+ if (strcmp (Cam_data.port, "Browse") != 0)
+ {
+ CHECK_RET (port = gp_port_info_list_lookup_path (il, Cam_data.port));
+ CHECK_RET (gp_port_info_list_get_info (il, port, &info));
+ CHECK_RET (gp_camera_set_port_info (camera, info));
+ gp_port_info_list_free (il);
+ }
+
+ for (n = 0; abilities.speed[n]; n++)
+ {
+ if (abilities.speed[n] == Cam_data.speed)
+ {
+ break;
+ }
+ }
+
+ if (abilities.speed[n] == 0 && !strncmp (Cam_data.port, "serial:", 7))
+ {
+ DBG (0,
+ "%s: error: %d is not a valid speed for this camers. Use \"gphoto2 --camera \"%s\" --abilities\" for list.\n",
+ "init_gphoto2", Cam_data.speed, Cam_data.camera_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "init_gphoto2: about to initialize port\n");
+ /*
+ * Setting of speed only makes sense for serial ports. gphoto2
+ * knows that and will complain if we try to set the speed for
+ * ports other than serial ones. Because we are paranoid here and
+ * check every single error message returned by gphoto2, we need
+ * to make sure that we have a serial port.
+ */
+ if (Cam_data.speed && !strncmp (Cam_data.port, "serial:", 7))
+ {
+ /*
+ * Not sure why we need this hack. The API keeps opening/closing
+ * the port, and that seems to confuse the camera. Holding
+ * the port open seems to fix it.
+ */
+ if ((hack_fd = open (Cam_data.port + 7, O_RDONLY)) < 0)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+#ifdef HAVE_USLEEP
+ usleep (200);
+#else
+ sleep (1);
+#endif
+ CHECK_RET (gp_camera_set_port_speed (camera, Cam_data.speed));
+ }
+
+ CHECK_RET (gp_camera_init (camera, NULL));
+
+ if (!(abilities.operations & GP_OPERATION_CAPTURE_IMAGE))
+ {
+ DBG (20, "init_gphoto2: Camera does not support image capture\n");
+ sod[GPHOTO2_OPT_SNAP].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (!(abilities.file_operations & GP_FILE_OPERATION_PREVIEW))
+ {
+ DBG (20, "init_gphoto2: Camera does not support image preview\n");
+ sod[GPHOTO2_OPT_THUMBS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (!(abilities.file_operations & GP_FILE_OPERATION_DELETE))
+ {
+ DBG (20, "init_gphoto2: Camera does not support image deletion\n");
+ sod[GPHOTO2_OPT_ERASE].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ DBG (4, "init_gphoto2: about to get folders\n");
+
+ CHECK_RET (gp_list_new (&list));
+ CHECK_RET (gp_camera_folder_list_folders (camera, TopFolder, list, NULL));
+ n = gp_list_count (list);
+ if (n < 0)
+ {
+ DBG (0, "init_gphoto2: Unable to get file list\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * close_gphoto2() - Shutdown camera interface
+ */
+static void
+close_gphoto2 (void)
+{
+ /*
+ * Put the camera back to 9600 baud
+ */
+
+ if (gp_camera_unref (camera))
+ {
+ DBG (1, "close_gphoto2: error: could not close device\n");
+ }
+
+ camera = NULL;
+ close (hack_fd);
+}
+
+/*
+ * get_info() - Get overall information about camera: folder names,
+ * number of pictures, etc.
+ */
+SANE_Int
+get_info (void)
+{
+ SANE_String_Const val;
+ SANE_Int n;
+
+ if (Cam_data.pic_taken == 0)
+ {
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ image_range.min = 0;
+ image_range.max = 0;
+ }
+ else
+ {
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ image_range.min = 1;
+ image_range.max = Cam_data.pic_taken;
+ }
+
+ if (SubDirs)
+ {
+ n = read_dir (TopFolder, 0);
+ }
+ else
+ {
+ n = 1;
+ }
+
+ /* If we've already got a folder_list, free it up before starting
+ * the new one
+ */
+ if (folder_list != NULL)
+ {
+ int tmp;
+ for (tmp = 0; folder_list[tmp]; tmp++)
+ {
+ free (folder_list[tmp]);
+ }
+ free (folder_list);
+ }
+
+ folder_list =
+ (SANE_String *) malloc ((n + 1) * sizeof (SANE_String_Const *));
+
+ if (SubDirs)
+ {
+ for (n = 0; n < gp_list_count (dir_list); n++)
+ {
+ gp_list_get_name (dir_list, n, &val);
+ folder_list[n] = strdup (val);
+ if (strchr ((const char *) folder_list[n], ' '))
+ {
+ *strchr ((const char *) folder_list[n], ' ') = '\0';
+ }
+ }
+ if (n == 0)
+ {
+ folder_list[n++] = (SANE_String) strdup ("");
+ }
+ }
+ else
+ {
+ n = 0;
+ folder_list[n++] = "N/A";
+ }
+
+ folder_list[n] = NULL;
+ sod[GPHOTO2_OPT_FOLDER].constraint.string_list =
+ (SANE_String_Const *) folder_list;
+
+ Cam_data.pic_taken = 0;
+ Cam_data.pic_left = 1; /* Just a guess! */
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/*
+ * erase() - erase file from camera corresponding to
+ * current picture number. Does not update any of the other
+ * backend data structures.
+ */
+static SANE_Int
+erase (void)
+{
+ SANE_String_Const filename;
+
+ if (SubDirs)
+ {
+ sprintf (cmdbuf, "%s/%s", (char *) TopFolder,
+ (const char *) folder_list[current_folder]);
+ }
+ else
+ {
+ strcpy (cmdbuf, TopFolder);
+ }
+
+ CHECK_RET (gp_list_get_name
+ (dir_list, Cam_data.current_picture_number - 1, &filename));
+
+ CHECK_RET (gp_camera_file_delete (camera, cmdbuf, filename, NULL));
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * change_res() - FIXME: Would like to set resolution, but haven't figure
+ * out how to control that yet.
+ */
+static SANE_Int
+change_res (SANE_Byte res)
+{
+
+ return (res - res);
+
+}
+
+/*
+ * sane_init() - Initialization function from SANE API. Initialize some
+ * data structures, verify that all the necessary config information
+ * is present, and initialize gphoto2
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize)
+{
+ SANE_Int n, entries;
+ SANE_Char f[] = "sane_init";
+ SANE_Char dev_name[PATH_MAX], *p;
+ SANE_Char buf[256];
+ CameraAbilitiesList *al;
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT ();
+
+ DBG (1,
+ "GPHOTO2 Backend $Id$\n");
+
+ if (getenv ("GP_DEBUG"))
+ {
+ gp_log_add_func (atoi (getenv ("GP_DEBUG")), debug_func, NULL);
+ }
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (GPHOTO2_CONFIG_FILE);
+
+ if (!fp)
+ {
+ /* Earlier versions why would try to keep going with compiled in
+ * defaults if the config file is missing. But, now we have so
+ * options and combinations of options, that success without a config
+ * file is unlikely. So, give and return failure
+ */
+ DBG (0, "warning: %s: missing config file '%s'\n"
+ "If you aren't using gphoto2, you should disable it in dll.conf.\n"
+ "If you do want to use gphoto2, you'll need to install the config\n"
+ "file in %s.\n", f, GPHOTO2_CONFIG_FILE, GPHOTO2_CONFIG_FILE);
+
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ dev_name[sizeof (dev_name) - 1] = '\0';
+ DBG (20, "%s: config- %s\n", f, dev_name);
+
+ if (dev_name[0] == '#')
+ continue; /* ignore line comments */
+ len = strlen (dev_name);
+ if (!len)
+ continue; /* ignore empty lines */
+ if (strncmp (dev_name, "port=", 5) == 0)
+ {
+ GPPortInfoList *list;
+ GPPortInfo info;
+ int result;
+
+ p = dev_name + 5;
+ if (p)
+ Cam_data.port = strdup (p);
+ DBG (20, "Config file port=%s\n", Cam_data.port);
+
+ /* Validate port */
+ CHECK_RET (gp_port_info_list_new (&list));
+ result = gp_port_info_list_load (list);
+ if (result < 0)
+ {
+ gp_port_info_list_free (list);
+ return SANE_STATUS_INVAL;
+ }
+ entries = gp_port_info_list_count (list);
+ if (entries < 0)
+ {
+ gp_port_info_list_free (list);
+ return SANE_STATUS_INVAL;
+ }
+ for (n = 0; n < entries; n++)
+ {
+#ifdef HAVE_GP_PORT_INFO_GET_PATH
+ char *info_path = NULL;
+#endif
+ result = gp_port_info_list_get_info (list, n, &info);
+ if (result < 0)
+ {
+ gp_port_info_list_free (list);
+ return SANE_STATUS_INVAL;
+ }
+#ifdef HAVE_GP_PORT_INFO_GET_PATH
+ gp_port_info_get_path (info, &info_path);
+ if (strcmp (Cam_data.port, info_path) == 0)
+#else
+ if (strcmp (Cam_data.port, info.path) == 0)
+#endif
+ {
+ break;
+ }
+ }
+ if (n == entries)
+ {
+ DBG (0,
+ "%s: error: %s is not a valid gphoto2 port. Use \"gphoto2 --list-ports\" for list.\n",
+ "init_gphoto2", Cam_data.port);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (strncmp (dev_name, "camera=", 7) == 0)
+ {
+ Cam_data.camera_name = strdup (dev_name + 7);
+ DBG (20, "Config file camera=%s\n", Cam_data.camera_name);
+ sprintf (buf, "Image selection - %s", Cam_data.camera_name);
+
+ CHECK_RET (gp_abilities_list_new (&al));
+ CHECK_RET (gp_abilities_list_load (al, NULL));
+ CHECK_RET (entries = gp_abilities_list_count (al));
+
+ for (n = 0; n < entries; n++)
+ {
+ CHECK_RET (gp_abilities_list_get_abilities
+ (al, n, &abilities));
+ if (strcmp (Cam_data.camera_name, abilities.model) == 0)
+ {
+ break;
+ }
+ }
+ if (n == entries)
+ {
+ DBG (0,
+ "%s: error: %s is not a valid camera type. Use \"gphoto2 --list-cameras\" for list.\n",
+ f, Cam_data.camera_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Special case: Force port to special value for the
+ * "Directory Browse" camera - overriding anything in
+ * the config file - or more likely when not specified
+ * in the config file.
+ */
+
+ if (strcmp (Cam_data.camera_name, "Directory Browse") == 0)
+ {
+ Cam_data.port = "Browse";
+ }
+
+ sod[GPHOTO2_OPT_IMAGE_SELECTION].title = strdup (buf);
+ }
+ else if (strcmp (dev_name, "dumpinquiry") == 0)
+ {
+ dumpinquiry = SANE_TRUE;
+ }
+ else if (strncmp (dev_name, "speed=", 6) == 0)
+ {
+ sscanf (&dev_name[6], "%d", &Cam_data.speed);
+
+ DBG (20, "Config file speed=%u\n", Cam_data.speed);
+
+ }
+ else if (strncmp (dev_name, "resolution=", 11) == 0)
+ {
+ sscanf (&dev_name[11], "%dx%d", &highres_width,
+ &highres_height);
+ DBG (20, "Config file resolution=%ux%u\n", highres_width,
+ highres_height);
+ }
+ else if (strncmp (dev_name, "thumb_resolution=", 17) == 0)
+ {
+ sscanf (&dev_name[17], "%dx%d", &thumb_width, &thumb_height);
+ DBG (20, "Config file thumb_resolution=%ux%u\n", thumb_width,
+ thumb_height);
+ }
+ else if (strncmp (dev_name, "topfolder=", 10) == 0)
+ {
+ /* Make sure TopFolder is non-null */
+ if (strlen (dev_name) > 10)
+ {
+ TopFolder = strdup (&dev_name[10]);
+ DBG (20, "Config file topfolder=%s\n", TopFolder);
+ }
+ }
+ else if (strncmp (dev_name, "subdirs=", 8) == 0)
+ {
+ SubDirs = atoi (&dev_name[8]);
+ if (SubDirs == 0)
+ {
+ sod[GPHOTO2_OPT_FOLDER].cap |= SANE_CAP_INACTIVE;
+ }
+ DBG (20, "Config file subdirs=%d\n", SubDirs);
+ }
+ }
+ fclose (fp);
+ }
+
+ DBG (3, "sane_init: about to init_gphoto2\n");
+
+ if (init_gphoto2 () != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+ dev[0].name = strdup (Cam_data.port);
+
+ DBG (3, "sane_init: about to get_info\n");
+ if (get_info () != SANE_STATUS_GOOD)
+ {
+ DBG (1, "error: could not get info\n");
+ close_gphoto2 ();
+ return SANE_STATUS_INVAL;
+ }
+
+ /* load the current images array */
+ DBG (3, "sane_init: about to get_pictures_info\n");
+ get_pictures_info ();
+
+ if (Cam_data.pic_taken == 0)
+ {
+ Cam_data.current_picture_number = 0;
+ parms.bytes_per_line = 0;
+ parms.pixels_per_line = 0;
+ parms.lines = 0;
+ }
+ else
+ {
+ Cam_data.current_picture_number = 1;
+/* OLD:
+ set_res (Cam_data.Pictures[Cam_data.current_picture_number - 1].low_res);
+*/
+ set_res (gphoto2_opt_lowres);
+ }
+
+ if (dumpinquiry)
+ {
+ SANE_Int x = 0;
+ DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n");
+ DBG (0, "Model : %s\n", abilities.model);
+ DBG (0, "Pictures : %d\n", Cam_data.pic_taken);
+ DBG (0, "Serial port support : %s\n",
+ (abilities.port & GP_PORT_SERIAL) ? "yes" : "no");
+ DBG (0, "USB support : %s\n",
+ (abilities.port & GP_PORT_USB) ? "yes" : "no");
+
+ if (abilities.speed[0] != 0)
+ {
+ DBG (0, "Transfer speeds supported :\n");
+ do
+ {
+ DBG (0, " : %i\n",
+ abilities.speed[x]);
+ x++;
+ }
+ while (abilities.speed[x] != 0);
+ }
+ DBG (0, "Capture choices :\n");
+ if (abilities.operations & GP_OPERATION_CAPTURE_IMAGE)
+ DBG (0, " : Image\n");
+ if (abilities.operations & GP_OPERATION_CAPTURE_VIDEO)
+ DBG (0, " : Video\n");
+ if (abilities.operations & GP_OPERATION_CAPTURE_AUDIO)
+ DBG (0, " : Audio\n");
+ if (abilities.operations & GP_OPERATION_CAPTURE_PREVIEW)
+ DBG (0, " : Preview\n");
+ DBG (0, "Configuration support : %s\n",
+ abilities.operations & GP_OPERATION_CONFIG ? "yes" : "no");
+
+ DBG (0, "Delete files on camera support : %s\n",
+ abilities.
+ file_operations & GP_FILE_OPERATION_DELETE ? "yes" : "no");
+ DBG (0, "File preview (thumbnail) support : %s\n",
+ abilities.
+ file_operations & GP_FILE_OPERATION_PREVIEW ? "yes" : "no");
+ DBG (0, "File upload support : %s\n",
+ abilities.
+ folder_operations & GP_FOLDER_OPERATION_PUT_FILE ? "yes" : "no");
+
+
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_exit() - Required by SANE API.
+ */
+void
+sane_exit (void)
+{
+ close_gphoto2 ();
+}
+
+/*
+ * sane_get_devices() - From SANE API
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool
+ UNUSEDARG local_only)
+{
+ DBG (127, "sane_get_devices called\n");
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_open() - From SANE API
+ */
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Int i;
+
+ DBG (127, "sane_open for device %s\n", devicename);
+ if (!devicename[0])
+ {
+ i = 0;
+ }
+ else
+ {
+ for (i = 0; i < NELEMS (dev); ++i)
+ {
+ if (strcmp (devicename, dev[i].name) == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ if (i >= NELEMS (dev))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_open)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ is_open = 1;
+ *handle = MAGIC;
+
+ DBG (4, "sane_open: pictures taken=%d\n", Cam_data.pic_taken);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_close() - From SANE API
+ */
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (127, "sane_close called\n");
+ if (handle == MAGIC)
+ is_open = 0;
+
+ DBG (127, "sane_close returning\n");
+}
+
+/*
+ * sane_get_option_descriptor() - From SANE API
+ */
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ if (handle != MAGIC || !is_open)
+ return NULL; /* wrong device */
+ if (option < 0 || option >= NELEMS (sod))
+ return NULL;
+ return &sod[option];
+}
+
+static SANE_Int myinfo = 0;
+
+/*
+ * sane_control_option() - From SANE API
+ */
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Status status;
+
+ if (option < 0 || option >= NELEMS (sod))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ /* Need to put this DBG line after the range check on option */
+ DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n",
+ handle, sod[option].title,
+ (action ==
+ SANE_ACTION_SET_VALUE ? "SET" : (action ==
+ SANE_ACTION_GET_VALUE ? "GET" :
+ "SETAUTO")), value, (void *) info);
+
+ if (handle != MAGIC || !is_open)
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (option < 0 || option >= NELEMS (sod))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_VALUE:
+
+ /* Can't set disabled options */
+ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap))
+ {
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* initialize info to zero - we'll OR in various values later */
+ if (info)
+ *info = 0;
+
+ status = sanei_constrain_value (sod + option, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "Constraint error in control_option\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ case GPHOTO2_OPT_IMAGE_NUMBER:
+ if (*(SANE_Word *) value <= Cam_data.pic_taken)
+ Cam_data.current_picture_number = *(SANE_Word *) value;
+ else
+ Cam_data.current_picture_number = Cam_data.pic_taken;
+
+ /*
+ * Setting a new image number could change image size (if
+ * we supported that - which we hope to do someday!
+ */
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ /* get the image's resolution, unless the camera has no
+ * pictures yet
+ */
+ if (Cam_data.pic_taken != 0)
+ {
+/* OLD:
+ set_res (Cam_data.
+ Pictures[Cam_data.current_picture_number - 1].low_res);
+*/
+ set_res (gphoto2_opt_lowres);
+ }
+ break;
+
+ case GPHOTO2_OPT_THUMBS:
+ gphoto2_opt_thumbnails = !!*(SANE_Word *) value;
+
+ /* Thumbnail forces an image size change: */
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+ if (Cam_data.pic_taken != 0)
+ {
+/* OLD:
+ set_res (Cam_data.
+ Pictures[Cam_data.current_picture_number - 1].low_res);
+*/
+ set_res (gphoto2_opt_lowres);
+ }
+ break;
+
+ case GPHOTO2_OPT_SNAP:
+ switch (*(SANE_Bool *) value)
+ {
+ case SANE_TRUE:
+ gphoto2_opt_snap = SANE_TRUE;
+ break;
+ case SANE_FALSE:
+ gphoto2_opt_snap = SANE_FALSE;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Snap forces new image size and changes image range */
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ /* if we are snapping a new one */
+ if (gphoto2_opt_snap)
+ {
+ /* activate the resolution setting */
+/* Until we figure out how to do this
+ sod[GPHOTO2_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE;
+*/
+ /* and de-activate the image number selector */
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* deactivate the resolution setting */
+ sod[GPHOTO2_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
+ /* and activate the image number selector, if there are
+ * pictures available */
+ if (Cam_data.current_picture_number)
+ {
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ /* set params according to resolution settings */
+ set_res (gphoto2_opt_lowres);
+
+ break;
+
+ case GPHOTO2_OPT_LOWRES:
+ gphoto2_opt_lowres = !!*(SANE_Word *) value;
+
+ /* Lowres potentially changes image size */
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+
+/* FIXME - change the number of pictures left depending on resolution
+ perhaps just call get_info again?
+*/
+ set_res (gphoto2_opt_lowres);
+
+ break;
+
+ case GPHOTO2_OPT_ERASE:
+ gphoto2_opt_erase = !!*(SANE_Word *) value;
+ break;
+
+ case GPHOTO2_OPT_AUTOINC:
+ gphoto2_opt_autoinc = !!*(SANE_Word *) value;
+ break;
+
+ case GPHOTO2_OPT_FOLDER:
+ DBG (1, "FIXME set folder not implemented yet\n");
+ break;
+
+ case GPHOTO2_OPT_DEFAULT:
+ gphoto2_opt_thumbnails = 0;
+ gphoto2_opt_snap = 0;
+
+ /* deactivate the resolution setting */
+ sod[GPHOTO2_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
+ /* and activate the image number selector */
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ DBG (1, "FIXME: Set all defaults here!\n");
+ break;
+
+ case GPHOTO2_OPT_INIT_GPHOTO2:
+ if (init_gphoto2 () != SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (get_info () != SANE_STATUS_GOOD)
+ {
+ DBG (1, "error: could not get info\n");
+ close_gphoto2 ();
+ return SANE_STATUS_INVAL;
+ }
+
+ /* load the current images array */
+ get_pictures_info ();
+
+ myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_GET_VALUE:
+
+ /* Can't return status for disabled options */
+ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap))
+ {
+ return (SANE_STATUS_INVAL);
+ }
+
+ switch (option)
+ {
+ case 0:
+ *(SANE_Word *) value = NELEMS (sod);
+ break;
+
+ case GPHOTO2_OPT_IMAGE_NUMBER:
+ *(SANE_Word *) value = Cam_data.current_picture_number;
+ break;
+
+ case GPHOTO2_OPT_THUMBS:
+ *(SANE_Word *) value = gphoto2_opt_thumbnails;
+ break;
+
+ case GPHOTO2_OPT_SNAP:
+ *(SANE_Word *) value = gphoto2_opt_snap;
+ break;
+
+ case GPHOTO2_OPT_LOWRES:
+ *(SANE_Word *) value = gphoto2_opt_lowres;
+ break;
+
+ case GPHOTO2_OPT_ERASE:
+ *(SANE_Word *) value = gphoto2_opt_erase;
+ break;
+
+ case GPHOTO2_OPT_AUTOINC:
+ *(SANE_Word *) value = gphoto2_opt_autoinc;
+ break;
+
+ case GPHOTO2_OPT_FOLDER:
+ if (folder_list == NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ strncpy ((char *) value, (const char *) folder_list[current_folder],
+ 256);
+ break;
+
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ switch (option)
+ {
+ default:
+ return SANE_STATUS_UNSUPPORTED; /* We are DUMB */
+ }
+ }
+
+ if (info && action == SANE_ACTION_SET_VALUE)
+ {
+ *info = myinfo;
+ myinfo = 0;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_get_parameters() - From SANE API
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Int rc = SANE_STATUS_GOOD;
+
+ DBG (127, "sane_get_params called, wid=%d,height=%d\n",
+ parms.pixels_per_line, parms.lines);
+
+ if (handle != MAGIC || !is_open)
+ rc = SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ parms.last_frame = SANE_TRUE; /* Have no idea what this does */
+ *params = parms;
+ DBG (127, "sane_get_params return %d\n", rc);
+ return rc;
+}
+
+typedef struct
+{
+ struct jpeg_source_mgr pub;
+ JOCTET *buffer;
+}
+my_source_mgr;
+typedef my_source_mgr *my_src_ptr;
+
+METHODDEF (void)
+jpeg_init_source (j_decompress_ptr UNUSEDARG cinfo)
+{
+ /* nothing to do */
+}
+
+METHODDEF (boolean) jpeg_fill_input_buffer (j_decompress_ptr cinfo)
+{
+ int n;
+
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (data_file_current_index + 512 > data_file_total_size)
+ {
+ n = data_file_total_size - data_file_current_index;
+ }
+ else
+ {
+ n = 512;
+ }
+
+ memcpy (src->buffer, data_ptr + data_file_current_index, n);
+ data_file_current_index += n;
+
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = n;
+
+ return TRUE;
+}
+
+METHODDEF (void) jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (num_bytes > 0)
+ {
+ while (num_bytes > (long) src->pub.bytes_in_buffer)
+ {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ (void) jpeg_fill_input_buffer (cinfo);
+ }
+ }
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+}
+
+METHODDEF (void)
+jpeg_term_source (j_decompress_ptr UNUSEDARG cinfo)
+{
+ /* no work necessary here */
+}
+
+/*
+ * sane_start() - From SANE API
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ SANE_String_Const filename, mime_type;
+
+ DBG (127, "sane_start called\n");
+ if (handle != MAGIC || !is_open ||
+ (Cam_data.current_picture_number == 0
+ && gphoto2_opt_snap == SANE_FALSE))
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+ if (Cam_data.scanning)
+ return SANE_STATUS_EOF;
+
+/*
+ * This shouldn't normally happen, but we allow it as a special case
+ * when batch/autoinc are in effect. The first illegal picture number
+ * terminates the scan
+ */
+ if (Cam_data.current_picture_number > Cam_data.pic_taken)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (gphoto2_opt_snap)
+ {
+ /*
+ * Don't allow picture unless there is room in the
+ * camera.
+ */
+ if (Cam_data.pic_left == 0)
+ {
+ DBG (3, "No room to store new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ if (snap_pic () == SANE_STATUS_INVAL)
+ {
+ DBG (1, "Failed to snap new picture\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (4, "sane_start: about to get file\n");
+
+ CHECK_RET (gp_file_new (&data_file));
+
+ if (SubDirs)
+ {
+ sprintf (cmdbuf, "%s/%s", (char *) TopFolder,
+ (const char *) folder_list[current_folder]);
+ }
+ else
+ {
+ strcpy (cmdbuf, TopFolder);
+ }
+
+ CHECK_RET (gp_list_get_name
+ (dir_list, Cam_data.current_picture_number - 1, &filename));
+
+ CHECK_RET (gp_camera_file_get (camera, cmdbuf, filename,
+ gphoto2_opt_thumbnails ? GP_FILE_TYPE_PREVIEW
+ : GP_FILE_TYPE_NORMAL, data_file, NULL));
+
+ CHECK_RET (gp_file_get_mime_type (data_file, &mime_type));
+ if (strcmp (GP_MIME_JPEG, mime_type) != 0)
+ {
+ DBG (0,
+ "FIXME - Only jpeg files currently supported, can't do %s for file %s/%s\n",
+ mime_type, cmdbuf, filename);
+ return SANE_STATUS_INVAL;
+ }
+
+ CHECK_RET (gp_file_get_data_and_size
+ (data_file, (const char **)&data_ptr, &data_file_total_size));
+
+ if ( converter_init (handle) != SANE_STATUS_GOOD )
+ return SANE_STATUS_INVAL;
+
+ /* Check if a linebuffer has been allocated. If we had one
+ * previously, free it up and allocate one for (possibly) new
+ * size. parms.bytes_per_line is set by converter_init()
+ */
+ if (linebuffer == NULL)
+ {
+ linebuffer = malloc (parms.bytes_per_line);
+ }
+ else
+ {
+ free (linebuffer);
+ linebuffer = malloc (parms.bytes_per_line);
+ }
+ if (linebuffer == NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ Cam_data.scanning = SANE_TRUE; /* don't overlap scan requests */
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_read() - From SANE API
+ */
+SANE_Status
+sane_read (SANE_Handle UNUSEDARG handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ if (Cam_data.scanning == SANE_FALSE)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ /* If there is anything in the buffer, satisfy the read from there */
+ if (linebuffer_size && linebuffer_index < linebuffer_size)
+ {
+ *length = linebuffer_size - linebuffer_index;
+
+ if (*length > max_length)
+ {
+ *length = max_length;
+ }
+ memcpy (data, linebuffer + linebuffer_index, *length);
+ linebuffer_index += *length;
+
+ return SANE_STATUS_GOOD;
+ }
+
+ if (converter_scan_complete ())
+ {
+ SANE_Status retval;
+
+ *length = 0;
+ retval = converter_do_scan_complete_cleanup ();
+
+ if (retval != SANE_STATUS_GOOD)
+ {
+ return retval;
+ }
+ }
+
+ *length = converter_fill_buffer ();
+ linebuffer_size = *length;
+ linebuffer_index = 0;
+
+ if (*length > max_length)
+ {
+ *length = max_length;
+ }
+ memcpy (data, linebuffer + linebuffer_index, *length);
+ linebuffer_index += *length;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * sane_cancel() - From SANE API
+ */
+void
+sane_cancel (SANE_Handle UNUSEDARG handle)
+{
+ if (Cam_data.scanning)
+ {
+ Cam_data.scanning = SANE_FALSE; /* done with scan */
+ }
+ else
+ DBG (4, "sane_cancel: not scanning - nothing to do\n");
+}
+
+/*
+ * sane_set_io_mode() - From SANE API
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle UNUSEDARG handle, SANE_Bool
+ UNUSEDARG non_blocking)
+{
+ /* sane_set_io_mode() is only valid during a scan */
+ if (Cam_data.scanning)
+ {
+ if (non_blocking == SANE_FALSE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else
+ {
+ /* We aren't currently scanning */
+ return SANE_STATUS_INVAL;
+ }
+}
+
+/*
+ * sane_get_select_fd() - From SANE API
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle UNUSEDARG handle, SANE_Int UNUSEDARG * fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * get_pictures_info - load information about all pictures currently in
+ * camera: Mainly the mapping of picture number
+ * to picture name. We'ld like to get other
+ * information such as image size, but the API
+ * doesn't provide any support for that.
+ */
+static PictureInfo *
+get_pictures_info (void)
+{
+ SANE_Char f[] = "get_pictures_info";
+ SANE_Char path[256];
+ SANE_Int num_pictures;
+ SANE_Int p;
+ PictureInfo *pics;
+
+ if (Cam_data.Pictures)
+ {
+ free (Cam_data.Pictures);
+ Cam_data.Pictures = NULL;
+ }
+
+ strcpy (path, TopFolder);
+ if (SubDirs)
+ {
+ if (folder_list[current_folder] != NULL)
+ {
+ strcat (path, "/");
+ strcat (path, (const char *) folder_list[current_folder]);
+ }
+ }
+ num_pictures = read_dir (path, 1);
+ Cam_data.pic_taken = num_pictures;
+ if (num_pictures > 0)
+ {
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
+ image_range.min = 1;
+ image_range.max = num_pictures;
+ }
+
+ if ((pics = (PictureInfo *) malloc (Cam_data.pic_taken *
+ sizeof (PictureInfo))) == NULL)
+ {
+ DBG (1, "%s: error: allocate memory for pictures array\n", f);
+ return NULL;
+ }
+
+ for (p = 0; p < Cam_data.pic_taken; p++)
+ {
+ if (get_picture_info (pics + p, p) == -1)
+ {
+ free (pics);
+ return NULL;
+ }
+ }
+
+ Cam_data.Pictures = pics;
+ return pics;
+}
+
+/*
+ * get_picture_info() - get info about picture p. Currently we have no
+ * way to get information about a picture beyond it's name.
+ */
+static SANE_Int
+get_picture_info (PictureInfo * pic, SANE_Int p)
+{
+
+ SANE_Char f[] = "get_picture_info";
+ const char *name;
+
+ DBG (4, "%s: info for pic #%d\n", f, p);
+
+ gp_list_get_name (dir_list, p, &name);
+ DBG (4, "Name is %s\n", name);
+
+ read_info (name);
+
+ pic->low_res = SANE_FALSE;
+
+ return 0;
+}
+
+/*
+ * snap_pic - take a picture (and call get_pictures_info to re-create
+ * the directory related data structures)
+ */
+static SANE_Status
+snap_pic (void)
+{
+ SANE_Char f[] = "snap_pic";
+ CameraFilePath path;
+
+ /* make sure camera is set to our settings state */
+ if (change_res (gphoto2_opt_lowres) == -1)
+ {
+ DBG (1, "%s: Failed to set resolution\n", f);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * This is needed when the camera has no files and the first picture
+ * is taken. I guess it's because a folder needs to be created and
+ * the filesystem doesn't know about it.
+ */
+ if (Cam_data.pic_taken == 0)
+ {
+ gp_filesystem_reset (camera->fs);
+ }
+
+ CHECK_RET (gp_camera_capture (camera, GP_CAPTURE_IMAGE, &path, NULL));
+
+ /* Can't just increment picture count, because if the camera has
+ * zero pictures we may not know the folder name. Start over
+ * with get_info and get_pictures_info. (We didn't have the call
+ * to init_gphoto2() here before, but that was causing us to not
+ * see the new image - need to use a biggger hammer to get it to
+ * re-read the camera directory
+ */
+
+ if (init_gphoto2 () != SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (get_info () != SANE_STATUS_GOOD)
+ {
+ DBG (1, "error: could not get info\n");
+ close_gphoto2 ();
+ return SANE_STATUS_INVAL;
+ }
+
+ if (get_pictures_info () == NULL)
+ {
+ DBG (1, "%s: Failed to get new picture info\n", f);
+ /* FIXME - I guess we should try to erase the image here */
+ return SANE_STATUS_INVAL;
+ }
+
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ Cam_data.current_picture_number = Cam_data.pic_taken;
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * read_dir - read a list of file names from the specified directory
+ * and create a linked list of file name entries in
+ * alphabetical order. The first entry in the list will
+ * be "picture #1", etc.
+ */
+static SANE_Int
+read_dir (SANE_String dir, SANE_Bool read_files)
+{
+ SANE_Int retval = 0;
+ SANE_Char f[] = "read_dir";
+
+ /* Free up current list */
+ if (dir_list != NULL)
+ {
+ if (gp_list_free (dir_list) < 0)
+ {
+ DBG (0, "%s: errror: gp_list_free failed\n", f);
+ }
+ dir_list = NULL;
+ }
+ if (gp_list_new (&dir_list) < 0)
+ {
+ DBG (0, "%s: errror: gp_list_new failed\n", f);
+ }
+
+ if (read_files)
+ {
+ CHECK_RET (gp_camera_folder_list_files (camera, dir, dir_list, NULL));
+ }
+ else
+ {
+ CHECK_RET (gp_camera_folder_list_folders (camera, dir, dir_list, NULL));
+ }
+
+ retval = gp_list_count (dir_list);
+
+ return retval;
+}
+
+/*
+ * read_info - read the info block from camera for the specified file
+ * NOT YET SUPPORTED - If it were we could use it to do things
+ * like update the image size parameters displayed by the GUI
+ */
+static SANE_Int
+read_info (SANE_String_Const fname)
+{
+ SANE_Char path[256];
+
+ strcpy (path, "\\DCIM\\");
+ strcat (path, (const char *) folder_list[current_folder]);
+ strcat (path, "\\");
+ strcat (path, fname);
+
+ return 0;
+}
+
+/*
+ * set_res - set picture size depending on resolution settings
+ */
+static void
+set_res (SANE_Int UNUSEDARG lowres)
+{
+ if (gphoto2_opt_thumbnails)
+ {
+ parms.bytes_per_line = THUMB_WIDTH * 3;
+ parms.pixels_per_line = THUMB_WIDTH;
+ parms.lines = THUMB_HEIGHT;
+ }
+ else
+ {
+ parms.bytes_per_line = HIGHRES_WIDTH * 3;
+ parms.pixels_per_line = HIGHRES_WIDTH;
+ parms.lines = HIGHRES_HEIGHT;
+ }
+}
+
+/*
+ * converter_do_scan_complete_cleanup - do everything that needs to be
+ * once a "scan" has been completed: Unref the file, Erase the image,
+ * and increment image number to point to next picture.
+ */
+static SANE_Status
+converter_do_scan_complete_cleanup (void)
+{
+ CameraList *tmp_list;
+ SANE_Int i;
+ SANE_String_Const filename;
+
+ gp_file_unref (data_file);
+
+ if (gphoto2_opt_erase)
+ {
+ DBG (127, "sane_read bp%d, erase image\n", __LINE__);
+ if (erase () == -1)
+ {
+ DBG (1, "Failed to erase memory\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ if (SubDirs)
+ {
+ sprintf (cmdbuf, "%s/%s", (char *) TopFolder,
+ (const char *) folder_list[current_folder]);
+ }
+ else
+ {
+ strcpy (cmdbuf, TopFolder);
+ }
+
+ CHECK_RET (gp_list_get_name
+ (dir_list, Cam_data.current_picture_number - 1, &filename));
+
+ Cam_data.pic_taken--;
+ Cam_data.pic_left++;
+ if (Cam_data.current_picture_number > Cam_data.pic_taken)
+ {
+ Cam_data.current_picture_number = Cam_data.pic_taken;
+ }
+ image_range.max--;
+ if (image_range.max == 0)
+ {
+ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ /* Too bad we don't have an API function for deleting a
+ * list item. Instead, we copy all the entries in the
+ * current list, skipping over the deleted entry, and then
+ * replace the current list with the new list.
+ */
+ gp_list_new (&tmp_list);
+
+ for (i = 0; i < gp_list_count (dir_list); i++)
+ {
+ SANE_String_Const tfilename;
+
+ CHECK_RET (gp_list_get_name (dir_list, i, &tfilename));
+ /* If not the one to delete, copy to the new list */
+ if (strcmp (tfilename, filename) != 0)
+ {
+ CHECK_RET (gp_list_append (tmp_list, tfilename, NULL));
+ }
+ }
+ gp_list_free (dir_list);
+ dir_list = tmp_list;
+
+ }
+ if (gphoto2_opt_autoinc)
+ {
+ if (Cam_data.current_picture_number <= Cam_data.pic_taken)
+ {
+ Cam_data.current_picture_number++;
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ /* get the image's resolution */
+/* OLD:
+ set_res (Cam_data.Pictures[Cam_data.current_picture_number - 1].
+ low_res);
+*/
+ set_res (gphoto2_opt_lowres);
+ }
+ DBG (4, "Increment count to %d (total %d)\n",
+ Cam_data.current_picture_number, Cam_data.pic_taken);
+ }
+ return SANE_STATUS_EOF;
+}
+
+/*
+ * converter_fill_buffer - Fill line buffer with next input line from image.
+ * Currently assumes jpeg, but this is where we would put the switch
+ * to handle other image types.
+ */
+static SANE_Int
+converter_fill_buffer (void)
+{
+
+/*
+ * FIXME: Current implementation reads one scan line at a time. Part
+ * of the reason for this is in the original code is to give the frontend
+ * a chance to update * the progress marker periodically. Since the gphoto2
+ * driver sucks in the whole image before decoding it, perhaps we could
+ * come up with a simpler implementation.
+ */
+
+ SANE_Int lines = 1;
+
+ (void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines);
+ (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) linebuffer);
+
+ return cinfo.output_width * cinfo.output_components * lines;
+}
+
+/*
+ * converter_scan_complete - Check if all the data for the image has been read.
+ * Currently assumes jpeg, but this is where we would put the
+ * switch to handle other image types.
+ */
+static SANE_Bool
+converter_scan_complete (void)
+{
+ if (cinfo.output_scanline >= cinfo.output_height)
+ {
+ return SANE_TRUE;
+ }
+ else
+ {
+ return SANE_FALSE;
+ }
+}
+
+/*
+ * converter_init - Initialize image conversion data.
+ * Currently assumes jpeg, but this is where we would put the
+ * switch to handle other image types.
+ */
+static SANE_Status
+converter_init (SANE_Handle handle)
+{
+ SANE_Int row_stride;
+ struct jpeg_error_mgr jerr;
+ my_src_ptr src;
+
+ data_file_current_index = 0;
+
+ /* Basic check to see if this is really a jpeg file */
+ if ( data_ptr[0] != 0xff || data_ptr[1] != 0xd8 ) {
+ sane_cancel(handle);
+exit(1);
+ return SANE_STATUS_INVAL;
+ }
+
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_decompress (&cinfo);
+
+ cinfo.src =
+ (struct jpeg_source_mgr *) (*cinfo.mem->
+ alloc_small) ((j_common_ptr) & cinfo,
+ JPOOL_PERMANENT,
+ sizeof (my_source_mgr));
+ src = (my_src_ptr) cinfo.src;
+
+ src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &
+ cinfo,
+ JPOOL_PERMANENT,
+ 1024 * sizeof (JOCTET));
+ src->pub.init_source = jpeg_init_source;
+ src->pub.fill_input_buffer = jpeg_fill_input_buffer;
+ src->pub.skip_input_data = jpeg_skip_input_data;
+ src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */
+ src->pub.term_source = jpeg_term_source;
+ src->pub.bytes_in_buffer = 0;
+ src->pub.next_input_byte = NULL;
+
+ (void) jpeg_read_header (&cinfo, TRUE);
+ dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo);
+ (void) jpeg_start_decompress (&cinfo);
+
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ parms.bytes_per_line = cinfo.output_width * 3; /* 3 colors */
+ parms.pixels_per_line = cinfo.output_width;
+ parms.lines = cinfo.output_height;
+
+ linebuffer_size = 0;
+ linebuffer_index = 0;
+
+ return(SANE_STATUS_GOOD);
+}
diff --git a/backend/gphoto2.conf.in b/backend/gphoto2.conf.in
new file mode 100644
index 0000000..f5a660a
--- /dev/null
+++ b/backend/gphoto2.conf.in
@@ -0,0 +1,32 @@
+# Interface port where the camera is connected
+# This should be one of the values returned by "gphoto2 --list-ports",
+# such # as serial:/dev/ttyS6 or usb:
+port=serial:/dev/ttyd1
+
+# Port speed. This should be one of the values returned by
+# "gphoto2 --abilities"
+speed=115200
+
+# Name of camera. This should be one of the values returned by
+# "gphoto2 --list-cameras"
+camera=Kodak DC240
+
+# Prints some extra information during the init phase.
+dumpinquiry
+
+# The resolution should be the maximum resolution supported by the
+# camera. It's not really used for much, since the actual size will be
+# reported by the camera when the download starts. But it may be useful
+# for the frontend to have a clue prior to the download. (e.g. it
+# may want to create an image window, or report the maximum file size.
+# Width x Height.
+resolution=1280x960
+
+# Thumbnail resolutions - ditto
+thumb_resolution=160x120
+
+# top-level (fixed) folder directory in camera. Backend assumes
+# that there is one variable directory under this (e.g. 100DC240)
+# which will be read from the camera, and all the images in the
+# camera are under that.
+topfolder=/DCIM
diff --git a/backend/gphoto2.h b/backend/gphoto2.h
new file mode 100644
index 0000000..4ef953b
--- /dev/null
+++ b/backend/gphoto2.h
@@ -0,0 +1,200 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ gphoto2.h
+
+ 03/12/01 - Peter Fales
+
+ Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is
+ based on dc25 driver (C) 1998 by Peter Fales)
+
+ This file (C) 2001 by Peter Fales
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for the Kodak DC-240
+ digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
+
+ (feedback to: gphoto2-devel@fales-lorenz.net)
+
+ This backend is based somewhat on the dc25 backend included in this
+ package by Peter Fales, and the dc210 backend by Brian J. Murrell
+
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE (1==1)
+#endif
+
+#ifndef FALSE
+#define FALSE (!TRUE)
+#endif
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+typedef struct picture_info
+{
+ int low_res;
+ int size;
+}
+PictureInfo;
+
+typedef struct GPHOTO2_s
+{
+ SANE_String port; /* the port name it's on */
+ SANE_Int speed; /* current port speed */
+ SANE_String camera_name;
+ SANE_Bool scanning; /* currently scanning an image? */
+ SANE_Byte model;
+ SANE_Byte ver_major;
+ SANE_Byte ver_minor;
+ SANE_Int pic_taken;
+ SANE_Int pic_left;
+ struct
+ {
+ unsigned int low_res:1;
+ unsigned int low_batt:1;
+ }
+ flags;
+ PictureInfo *Pictures; /* array of pictures */
+ SANE_Int current_picture_number; /* picture being operated on */
+}
+GPHOTO2;
+
+typedef struct gphoto2_info_s
+{
+ SANE_Byte model;
+ SANE_Byte ver_major;
+ SANE_Byte ver_minor;
+ SANE_Int pic_taken;
+ SANE_Int pic_left;
+ struct
+ {
+ SANE_Int low_res:1;
+ SANE_Int low_batt:1;
+ }
+ flags;
+}
+Gphoto2Info, *Gphoto2InfoPtr;
+
+static SANE_Int get_info (void);
+
+#define HIGH_RES 0
+#define LOW_RES 1
+
+#define HIGHRES_WIDTH highres_width
+#define HIGHRES_HEIGHT highres_height
+
+#define LOWRES_WIDTH lowres_width
+#define LOWRES_HEIGHT lowres_height
+
+#define THUMB_WIDTH thumb_width
+#define THUMB_HEIGHT thumb_height
+
+/*
+ * External definitions
+ */
+
+extern char *__progname; /* Defined in /usr/lib/crt0.o */
+
+
+struct cam_dirent
+{
+ SANE_Char name[11];
+ SANE_Byte attr;
+ SANE_Byte create_time[2];
+ SANE_Byte creat_date[2];
+ long size;
+};
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+struct cam_dirlist
+{
+ SANE_Char name[48];
+ struct cam_dirlist *next;
+};
+
+
+
+#include <sys/types.h>
+
+FILE *sanei_config_open (const char *filename);
+
+static SANE_Int init_gphoto2 (void);
+
+static void close_gphoto2 (void);
+
+static PictureInfo *get_pictures_info (void);
+
+static SANE_Int get_picture_info (PictureInfo * pic, SANE_Int p);
+
+static SANE_Status snap_pic (void);
+
+char *sanei_config_read (char *str, int n, FILE * stream);
+
+static SANE_Int read_dir (SANE_String dir, SANE_Bool read_files);
+
+static void set_res (SANE_Int lowres);
+
+static SANE_Int read_info (SANE_String_Const fname);
+
+static SANE_Status converter_do_scan_complete_cleanup (void);
+
+static SANE_Int converter_fill_buffer (void);
+
+static SANE_Bool converter_scan_complete (void);
+
+static SANE_Status converter_init (SANE_Handle handle);
diff --git a/backend/gt68xx.c b/backend/gt68xx.c
new file mode 100644
index 0000000..ea71d91
--- /dev/null
+++ b/backend/gt68xx.c
@@ -0,0 +1,2389 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
+ Copyright (C) 2009 Stéphane Voltz <stef.dev@free.fr> for sheetfed
+ calibration code.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ * SANE backend for Grandtech GT-6801 and GT-6816 based scanners
+ */
+
+#include "../include/sane/config.h"
+
+#define BUILD 84
+#define MAX_DEBUG
+#define WARMUP_TIME 60
+#define CALIBRATION_HEIGHT 2.5
+#define SHORT_TIMEOUT (1 * 1000)
+#define LONG_TIMEOUT (30 * 1000)
+
+/* Use a reader process if possible (usually faster) */
+#if defined (HAVE_SYS_SHM_H) && (!defined (USE_PTHREAD)) && (!defined (HAVE_OS2_H))
+#define USE_FORK
+#define SHM_BUFFERS 10
+#endif
+
+#define TUNE_CALIBRATOR
+
+/* Send coarse white or black calibration to stdout */
+#if 0
+#define SAVE_WHITE_CALIBRATION
+#endif
+#if 0
+#define SAVE_BLACK_CALIBRATION
+#endif
+
+/* Debug calibration, print total brightness of the scanned image */
+#if 0
+#define DEBUG_BRIGHTNESS
+#endif
+
+/* Debug calibration, print black mark values */
+#if 0
+#define DEBUG_BLACK
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <dirent.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME gt68xx
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+#include "gt68xx.h"
+#include "gt68xx_high.c"
+#include "gt68xx_devices.c"
+
+static SANE_Int num_devices = 0;
+static GT68xx_Device *first_dev = 0;
+static GT68xx_Scanner *first_handle = 0;
+static const SANE_Device **devlist = 0;
+/* Array of newly attached devices */
+static GT68xx_Device **new_dev = 0;
+/* Length of new_dev array */
+static SANE_Int new_dev_len = 0;
+/* Number of entries alloced for new_dev */
+static SANE_Int new_dev_alloced = 0;
+/* Is this computer little-endian ?*/
+SANE_Bool little_endian;
+SANE_Bool debug_options = SANE_FALSE;
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_LINEART,
+ 0
+};
+
+static SANE_String_Const gray_mode_list[] = {
+ GT68XX_COLOR_RED,
+ GT68XX_COLOR_GREEN,
+ GT68XX_COLOR_BLUE,
+ 0
+};
+
+static SANE_String_Const source_list[] = {
+ SANE_I18N ("Flatbed"),
+ SANE_I18N ("Transparency Adapter"),
+ 0
+};
+
+static SANE_Range x_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (216.0), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range y_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (299.0), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+
+static const SANE_Range offset_range = {
+ -63, /* minimum */
+ 63, /* maximum */
+ 1 /* quantization */
+};
+
+static SANE_Range gamma_range = {
+ SANE_FIX (0.01), /* minimum */
+ SANE_FIX (5.0), /* maximum */
+ SANE_FIX (0.01) /* quantization */
+};
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/* Test if this machine is little endian (from coolscan.c) */
+static SANE_Bool
+calc_little_endian (void)
+{
+ SANE_Int testvalue = 255;
+ uint8_t *firstbyte = (uint8_t *) & testvalue;
+
+ if (*firstbyte == 255)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+get_afe_values (SANE_String_Const cp, GT68xx_AFE_Parameters * afe)
+{
+ SANE_Char *word, *end;
+ int i;
+
+ for (i = 0; i < 6; i++)
+ {
+ cp = sanei_config_get_string (cp, &word);
+ if (word && *word)
+ {
+ long int long_value;
+ errno = 0;
+ long_value = strtol (word, &end, 0);
+
+ if (end == word)
+ {
+ DBG (5, "get_afe_values: can't parse %d. parameter `%s'\n",
+ i + 1, word);
+ free (word);
+ word = 0;
+ return SANE_STATUS_INVAL;
+ }
+ else if (errno)
+ {
+ DBG (5, "get_afe_values: can't parse %d. parameter `%s' "
+ "(%s)\n", i + 1, word, strerror (errno));
+ free (word);
+ word = 0;
+ return SANE_STATUS_INVAL;
+ }
+ else if (long_value < 0)
+ {
+ DBG (5, "get_afe_values: %d. parameter < 0 (%d)\n", i + 1,
+ (int) long_value);
+ free (word);
+ word = 0;
+ return SANE_STATUS_INVAL;
+ }
+ else if (long_value > 0x3f)
+ {
+ DBG (5, "get_afe_values: %d. parameter > 0x3f (%d)\n", i + 1,
+ (int) long_value);
+ free (word);
+ word = 0;
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ DBG (5, "get_afe_values: %d. parameter set to 0x%02x\n", i + 1,
+ (int) long_value);
+ switch (i)
+ {
+ case 0:
+ afe->r_offset = (SANE_Byte) long_value;
+ break;
+ case 1:
+ afe->r_pga = (SANE_Byte) long_value;
+ break;
+ case 2:
+ afe->g_offset = (SANE_Byte) long_value;
+ break;
+ case 3:
+ afe->g_pga = (SANE_Byte) long_value;
+ break;
+ case 4:
+ afe->b_offset = (SANE_Byte) long_value;
+ break;
+ case 5:
+ afe->b_pga = (SANE_Byte) long_value;
+ break;
+ }
+ free (word);
+ word = 0;
+ }
+ }
+ else
+ {
+ DBG (5, "get_afe_values: option `afe' needs 6 parameters\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+setup_scan_request (GT68xx_Scanner * s, GT68xx_Scan_Request * scan_request)
+{
+
+ if (s->dev->model->flags & GT68XX_FLAG_MIRROR_X)
+ scan_request->x0 =
+ s->opt[OPT_TL_X].constraint.range->max - s->val[OPT_BR_X].w;
+ else
+ scan_request->x0 = s->val[OPT_TL_X].w;
+ scan_request->y0 = s->val[OPT_TL_Y].w;
+ scan_request->xs = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w;
+ scan_request->ys = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w;
+
+ if (s->val[OPT_FULL_SCAN].w == SANE_TRUE)
+ {
+ scan_request->x0 -= s->dev->model->x_offset;
+ scan_request->y0 -= (s->dev->model->y_offset);
+ scan_request->xs += s->dev->model->x_offset;
+ scan_request->ys += s->dev->model->y_offset;
+ }
+
+ scan_request->xdpi = s->val[OPT_RESOLUTION].w;
+ if (scan_request->xdpi > s->dev->model->optical_xdpi)
+ scan_request->xdpi = s->dev->model->optical_xdpi;
+ scan_request->ydpi = s->val[OPT_RESOLUTION].w;
+
+ if (IS_ACTIVE (OPT_BIT_DEPTH) && !s->val[OPT_PREVIEW].w)
+ scan_request->depth = s->val[OPT_BIT_DEPTH].w;
+ else
+ scan_request->depth = 8;
+
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ scan_request->color = SANE_TRUE;
+ else
+ scan_request->color = SANE_FALSE;
+
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ SANE_Int xs =
+ SANE_UNFIX (scan_request->xs) * scan_request->xdpi / MM_PER_INCH +
+ 0.5;
+
+ if (xs % 8)
+ {
+ scan_request->xs =
+ SANE_FIX ((xs - (xs % 8)) * MM_PER_INCH / scan_request->xdpi);
+ DBG (5, "setup_scan_request: lineart mode, %d pixels %% 8 = %d\n",
+ xs, xs % 8);
+ }
+ }
+
+ scan_request->calculate = SANE_FALSE;
+ scan_request->lamp = SANE_TRUE;
+ scan_request->mbs = SANE_FALSE;
+
+ if (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0)
+ scan_request->use_ta = SANE_TRUE;
+ else
+ scan_request->use_ta = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+calc_parameters (GT68xx_Scanner * s)
+{
+ SANE_String val;
+ SANE_Status status = SANE_STATUS_GOOD;
+ GT68xx_Scan_Request scan_request;
+ GT68xx_Scan_Parameters scan_params;
+
+ DBG (5, "calc_parameters: start\n");
+ val = s->val[OPT_MODE].s;
+
+ s->params.last_frame = SANE_TRUE;
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0
+ || strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->params.format = SANE_FRAME_GRAY;
+ else /* Color */
+ s->params.format = SANE_FRAME_RGB;
+
+ setup_scan_request (s, &scan_request);
+ scan_request.calculate = SANE_TRUE;
+
+ status = gt68xx_device_setup_scan (s->dev, &scan_request, SA_SCAN,
+ &scan_params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "calc_parameters: gt68xx_device_setup_scan returned: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->params.depth = 1;
+ else
+ s->params.depth = scan_params.depth;
+
+ s->params.lines = scan_params.pixel_ys;
+ s->params.pixels_per_line = scan_params.pixel_xs;
+ /* Inflate X if necessary */
+ if (s->val[OPT_RESOLUTION].w > s->dev->model->optical_xdpi)
+ s->params.pixels_per_line *=
+ (s->val[OPT_RESOLUTION].w / s->dev->model->optical_xdpi);
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ if (s->params.depth > 8)
+ {
+ s->params.depth = 16;
+ s->params.bytes_per_line *= 2;
+ }
+ else if (s->params.depth == 1)
+ s->params.bytes_per_line /= 8;
+
+ if (s->params.format == SANE_FRAME_RGB)
+ s->params.bytes_per_line *= 3;
+
+ DBG (5, "calc_parameters: exit\n");
+ return status;
+}
+
+static SANE_Status
+create_bpp_list (GT68xx_Scanner * s, SANE_Int * bpp)
+{
+ int count;
+
+ for (count = 0; bpp[count] != 0; count++)
+ ;
+ s->bpp_list[0] = count;
+ for (count = 0; bpp[count] != 0; count++)
+ {
+ s->bpp_list[s->bpp_list[0] - count] = bpp[count];
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (GT68xx_Scanner * s)
+{
+ SANE_Int option, count;
+ SANE_Status status;
+ SANE_Word *dpi_list;
+ GT68xx_Model *model = s->dev->model;
+ SANE_Bool has_ta = SANE_FALSE;
+
+ DBG (5, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->opt[option].size = sizeof (SANE_Word);
+ s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (SANE_VALUE_SCAN_MODE_GRAY);
+
+ /* scan mode */
+ s->opt[OPT_GRAY_MODE_COLOR].name = "gray-mode-color";
+ s->opt[OPT_GRAY_MODE_COLOR].title = SANE_I18N ("Gray mode color");
+ s->opt[OPT_GRAY_MODE_COLOR].desc =
+ SANE_I18N ("Selects which scan color is used "
+ "gray mode (default: green).");
+ s->opt[OPT_GRAY_MODE_COLOR].type = SANE_TYPE_STRING;
+ s->opt[OPT_GRAY_MODE_COLOR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GRAY_MODE_COLOR].size = max_string_size (gray_mode_list);
+ s->opt[OPT_GRAY_MODE_COLOR].constraint.string_list = gray_mode_list;
+ s->val[OPT_GRAY_MODE_COLOR].s = strdup (GT68XX_COLOR_GREEN);
+
+ /* scan source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].s = strdup ("Flatbed");
+ status = gt68xx_device_get_ta_status (s->dev, &has_ta);
+ if (status != SANE_STATUS_GOOD || !has_ta)
+ DISABLE (OPT_SOURCE);
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* lamp on */
+ s->opt[OPT_LAMP_OFF_AT_EXIT].name = SANE_NAME_LAMP_OFF_AT_EXIT;
+ s->opt[OPT_LAMP_OFF_AT_EXIT].title = SANE_TITLE_LAMP_OFF_AT_EXIT;
+ s->opt[OPT_LAMP_OFF_AT_EXIT].desc = SANE_DESC_LAMP_OFF_AT_EXIT;
+ s->opt[OPT_LAMP_OFF_AT_EXIT].type = SANE_TYPE_BOOL;
+ s->opt[OPT_LAMP_OFF_AT_EXIT].unit = SANE_UNIT_NONE;
+ s->opt[OPT_LAMP_OFF_AT_EXIT].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_LAMP_OFF_AT_EXIT].w = SANE_TRUE;
+ if (s->dev->model->is_cis && !(s->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
+ DISABLE (OPT_LAMP_OFF_AT_EXIT);
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = 0;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list;
+ RIE (create_bpp_list (s, s->dev->model->bpp_gray_values));
+ s->val[OPT_BIT_DEPTH].w = 8;
+ if (s->opt[OPT_BIT_DEPTH].constraint.word_list[0] < 2)
+ DISABLE (OPT_BIT_DEPTH);
+
+ /* resolution */
+ for (count = 0; model->ydpi_values[count] != 0; count++)
+ ;
+ dpi_list = malloc ((count + 1) * sizeof (SANE_Word));
+ if (!dpi_list)
+ return SANE_STATUS_NO_MEM;
+ dpi_list[0] = count;
+ for (count = 0; model->ydpi_values[count] != 0; count++)
+ dpi_list[dpi_list[0] - count] = model->ydpi_values[count];
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+ s->val[OPT_RESOLUTION].w = 300;
+
+ /* backtrack */
+ s->opt[OPT_BACKTRACK].name = SANE_NAME_BACKTRACK;
+ s->opt[OPT_BACKTRACK].title = SANE_TITLE_BACKTRACK;
+ s->opt[OPT_BACKTRACK].desc = SANE_DESC_BACKTRACK;
+ s->opt[OPT_BACKTRACK].type = SANE_TYPE_BOOL;
+ s->val[OPT_BACKTRACK].w = SANE_FALSE;
+
+ /* "Debug" group: */
+ s->opt[OPT_DEBUG_GROUP].title = SANE_I18N ("Debugging Options");
+ s->opt[OPT_DEBUG_GROUP].desc = "";
+ s->opt[OPT_DEBUG_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_DEBUG_GROUP].size = 0;
+ s->opt[OPT_DEBUG_GROUP].cap = 0;
+ s->opt[OPT_DEBUG_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ if (!debug_options)
+ DISABLE (OPT_DEBUG_GROUP);
+
+ /* auto warmup */
+ s->opt[OPT_AUTO_WARMUP].name = "auto-warmup";
+ s->opt[OPT_AUTO_WARMUP].title = SANE_I18N ("Automatic warmup");
+ s->opt[OPT_AUTO_WARMUP].desc =
+ SANE_I18N ("Warm-up until the lamp's brightness is constant "
+ "instead of insisting on 60 seconds warm-up time.");
+ s->opt[OPT_AUTO_WARMUP].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTO_WARMUP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_AUTO_WARMUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTO_WARMUP].w = SANE_TRUE;
+ if ((s->dev->model->is_cis
+ && !(s->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) || !debug_options)
+ DISABLE (OPT_AUTO_WARMUP);
+
+ /* full scan */
+ s->opt[OPT_FULL_SCAN].name = "full-scan";
+ s->opt[OPT_FULL_SCAN].title = SANE_I18N ("Full scan");
+ s->opt[OPT_FULL_SCAN].desc =
+ SANE_I18N ("Scan the complete scanning area including calibration strip. "
+ "Be careful. Don't select the full height. For testing only.");
+ s->opt[OPT_FULL_SCAN].type = SANE_TYPE_BOOL;
+ s->opt[OPT_FULL_SCAN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FULL_SCAN].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_FULL_SCAN].w = SANE_FALSE;
+ if (!debug_options)
+ DISABLE (OPT_FULL_SCAN);
+
+ /* coarse calibration */
+ s->opt[OPT_COARSE_CAL].name = "coarse-calibration";
+ s->opt[OPT_COARSE_CAL].title = SANE_I18N ("Coarse calibration");
+ s->opt[OPT_COARSE_CAL].desc =
+ SANE_I18N ("Setup gain and offset for scanning automatically. If this "
+ "option is disabled, options for setting the analog frontend "
+ "parameters manually are provided. This option is enabled "
+ "by default. For testing only.");
+ s->opt[OPT_COARSE_CAL].type = SANE_TYPE_BOOL;
+ s->opt[OPT_COARSE_CAL].unit = SANE_UNIT_NONE;
+ s->opt[OPT_COARSE_CAL].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_COARSE_CAL].w = SANE_TRUE;
+ if (!debug_options)
+ DISABLE (OPT_COARSE_CAL);
+ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED)
+ {
+ s->val[OPT_COARSE_CAL].w = SANE_FALSE;
+ DISABLE (OPT_COARSE_CAL);
+ }
+
+ /* coarse calibration only once */
+ s->opt[OPT_COARSE_CAL_ONCE].name = "coarse-calibration-once";
+ s->opt[OPT_COARSE_CAL_ONCE].title =
+ SANE_I18N ("Coarse calibration for first scan only");
+ s->opt[OPT_COARSE_CAL_ONCE].desc =
+ SANE_I18N ("Coarse calibration is only done for the first scan. Works "
+ "with most scanners and can save scanning time. If the image "
+ "brightness is different with each scan, disable this option. "
+ "For testing only.");
+ s->opt[OPT_COARSE_CAL_ONCE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_COARSE_CAL_ONCE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_COARSE_CAL_ONCE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_COARSE_CAL_ONCE].w = SANE_FALSE;
+ if (!debug_options)
+ DISABLE (OPT_COARSE_CAL_ONCE);
+ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED)
+ DISABLE (OPT_COARSE_CAL_ONCE);
+
+ /* calibration */
+ s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].desc = SANE_TITLE_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL;
+ s->opt[OPT_QUALITY_CAL].unit = SANE_UNIT_NONE;
+ s->opt[OPT_QUALITY_CAL].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_QUALITY_CAL].w = SANE_TRUE;
+ if (!debug_options)
+ DISABLE (OPT_QUALITY_CAL);
+ /* we disable image correction for scanners that can't calibrate */
+ if ((s->dev->model->flags & GT68XX_FLAG_SHEET_FED)
+ &&(!(s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)))
+ {
+ s->val[OPT_QUALITY_CAL].w = SANE_FALSE;
+ DISABLE (OPT_QUALITY_CAL);
+ }
+
+ /* backtrack lines */
+ s->opt[OPT_BACKTRACK_LINES].name = "backtrack-lines";
+ s->opt[OPT_BACKTRACK_LINES].title = SANE_I18N ("Backtrack lines");
+ s->opt[OPT_BACKTRACK_LINES].desc =
+ SANE_I18N ("Number of lines the scan slider moves back when backtracking "
+ "occurs. That happens when the scanner scans faster than the "
+ "computer can receive the data. Low values cause faster scans "
+ "but increase the risk of omitting lines.");
+ s->opt[OPT_BACKTRACK_LINES].type = SANE_TYPE_INT;
+ s->opt[OPT_BACKTRACK_LINES].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BACKTRACK_LINES].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BACKTRACK_LINES].constraint.range = &u8_range;
+ if (s->dev->model->is_cis && !(s->dev->model->flags & GT68XX_FLAG_SHEET_FED))
+ s->val[OPT_BACKTRACK_LINES].w = 0x10;
+ else
+ s->val[OPT_BACKTRACK_LINES].w = 0x3f;
+ if (!debug_options)
+ DISABLE (OPT_BACKTRACK_LINES);
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* internal gamma value */
+ s->opt[OPT_GAMMA_VALUE].name = "gamma-value";
+ s->opt[OPT_GAMMA_VALUE].title = SANE_I18N ("Gamma value");
+ s->opt[OPT_GAMMA_VALUE].desc =
+ SANE_I18N ("Sets the gamma value of all channels.");
+ s->opt[OPT_GAMMA_VALUE].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_VALUE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VALUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VALUE].constraint.range = &gamma_range;
+ s->opt[OPT_GAMMA_VALUE].cap |= SANE_CAP_EMULATED;
+ s->val[OPT_GAMMA_VALUE].w = s->dev->gamma_value;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->val[OPT_THRESHOLD].w = 128;
+ DISABLE (OPT_THRESHOLD);
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ x_range.max = model->x_size;
+ y_range.max = model->y_size;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y].w = y_range.max;
+
+ /* sensor group */
+ s->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS;
+ s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS;
+ s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS;
+ s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* calibration needed */
+ s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration";
+ s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Need calibration");
+ s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings");
+ s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE;
+ if (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)
+ s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_NEED_CALIBRATION_SW].b = 0;
+
+ /* document present sensor */
+ s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED;
+ s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED;
+ s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED;
+ s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE;
+ if (s->dev->model->command_set->document_present)
+ s->opt[OPT_PAGE_LOADED_SW].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ else
+ s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_PAGE_LOADED_SW].b = 0;
+
+ /* button group */
+ s->opt[OPT_BUTTON_GROUP].name = "Buttons";
+ s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons");
+ s->opt[OPT_BUTTON_GROUP].desc = SANE_I18N ("Buttons");
+ s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* calibrate button */
+ s->opt[OPT_CALIBRATE].name = "calibrate";
+ s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate");
+ s->opt[OPT_CALIBRATE].desc =
+ SANE_I18N ("Start calibration using special sheet");
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
+ if (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)
+ s->opt[OPT_CALIBRATE].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
+ SANE_CAP_AUTOMATIC;
+ else
+ s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_CALIBRATE].b = 0;
+
+ /* clear calibration cache button */
+ s->opt[OPT_CLEAR_CALIBRATION].name = "clear";
+ s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration");
+ s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache");
+ s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE;
+ if (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)
+ s->opt[OPT_CLEAR_CALIBRATION].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
+ SANE_CAP_AUTOMATIC;
+ else
+ s->opt[OPT_CLEAR_CALIBRATION].cap = SANE_CAP_INACTIVE;
+ s->val[OPT_CLEAR_CALIBRATION].b = 0;
+
+
+ RIE (calc_parameters (s));
+
+ DBG (5, "init_options: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, GT68xx_Device ** devp, SANE_Bool may_wait)
+{
+ GT68xx_Device *dev;
+ SANE_Status status;
+
+ DBG (5, "attach: start: devp %s NULL, may_wait = %d\n", devp ? "!=" : "==",
+ may_wait);
+ if (!devname)
+ {
+ DBG (1, "attach: devname == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->file_name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ dev->missing = SANE_FALSE;
+ DBG (4, "attach: device `%s' was already in device list\n",
+ devname);
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (4, "attach: trying to open device `%s'\n", devname);
+ RIE (gt68xx_device_new (&dev));
+ status = gt68xx_device_open (dev, devname);
+ if (status == SANE_STATUS_GOOD)
+ DBG (4, "attach: device `%s' successfully opened\n", devname);
+ else
+ {
+ DBG (4, "attach: couldn't open device `%s': %s\n", devname,
+ sane_strstatus (status));
+ gt68xx_device_free (dev);
+ if (devp)
+ *devp = 0;
+ return status;
+ }
+
+ if (!gt68xx_device_is_configured (dev))
+ {
+ GT68xx_Model *model = NULL;
+ DBG (2, "attach: Warning: device `%s' is not listed in device table\n",
+ devname);
+ DBG (2,
+ "attach: If you have manually added it, use override in gt68xx.conf\n");
+ gt68xx_device_get_model ("unknown-scanner", &model);
+ status = gt68xx_device_set_model (dev, model);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (4, "attach: couldn't set model: %s\n",
+ sane_strstatus (status));
+ gt68xx_device_free (dev);
+ if (devp)
+ *devp = 0;
+ return status;
+ }
+ dev->manual_selection = SANE_TRUE;
+ }
+
+ dev->file_name = strdup (devname);
+ dev->missing = SANE_FALSE;
+ if (!dev->file_name)
+ return SANE_STATUS_NO_MEM;
+ DBG (2, "attach: found %s flatbed scanner %s at %s\n", dev->model->vendor,
+ dev->model->model, dev->file_name);
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ gt68xx_device_close (dev);
+ DBG (5, "attach: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ GT68xx_Device *dev;
+ SANE_Status status;
+
+ RIE (attach (devname, &dev, SANE_FALSE));
+
+ if (dev)
+ {
+ /* Keep track of newly attached devices so we can set options as
+ necessary. */
+ if (new_dev_len >= new_dev_alloced)
+ {
+ new_dev_alloced += 4;
+ if (new_dev)
+ new_dev =
+ realloc (new_dev, new_dev_alloced * sizeof (new_dev[0]));
+ else
+ new_dev = malloc (new_dev_alloced * sizeof (new_dev[0]));
+ if (!new_dev)
+ {
+ DBG (1, "attach_one_device: out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ new_dev[new_dev_len++] = dev;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+#if defined(_WIN32) || defined(HAVE_OS2_H)
+# define PATH_SEP "\\"
+#else
+# define PATH_SEP "/"
+#endif
+
+static SANE_Status
+download_firmware_file (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte *buf = NULL;
+ int size = -1;
+ SANE_Char filename[PATH_MAX], dirname[PATH_MAX], basename[PATH_MAX];
+ FILE *f;
+
+ if (strncmp (dev->model->firmware_name, PATH_SEP, 1) != 0)
+ {
+ /* probably filename only */
+ snprintf (filename, PATH_MAX, "%s%s%s%s%s%s%s",
+ STRINGIFY (PATH_SANE_DATA_DIR),
+ PATH_SEP, "sane", PATH_SEP, "gt68xx", PATH_SEP,
+ dev->model->firmware_name);
+ snprintf (dirname, PATH_MAX, "%s%s%s%s%s",
+ STRINGIFY (PATH_SANE_DATA_DIR),
+ PATH_SEP, "sane", PATH_SEP, "gt68xx");
+ strncpy (basename, dev->model->firmware_name, PATH_MAX);
+ }
+ else
+ {
+ /* absolute path */
+ char *pos;
+ strncpy (filename, dev->model->firmware_name, PATH_MAX);
+ strncpy (dirname, dev->model->firmware_name, PATH_MAX);
+ pos = strrchr (dirname, PATH_SEP[0]);
+ if (pos)
+ pos[0] = '\0';
+ strncpy (basename, pos + 1, PATH_MAX);
+ }
+
+ /* first, try to open with exact case */
+ DBG (5, "download_firmware: trying %s\n", filename);
+ f = fopen (filename, "rb");
+ if (!f)
+ {
+ /* and now any case */
+ DIR *dir;
+ struct dirent *direntry;
+
+ DBG (5,
+ "download_firmware_file: Couldn't open firmware file `%s': %s\n",
+ filename, strerror (errno));
+
+ dir = opendir (dirname);
+ if (!dir)
+ {
+ DBG (5, "download_firmware: couldn't open directory `%s': %s\n",
+ dirname, strerror (errno));
+ status = SANE_STATUS_INVAL;
+ }
+ if (status == SANE_STATUS_GOOD)
+ {
+ do
+ {
+ direntry = readdir (dir);
+ if (direntry
+ && (strncasecmp (direntry->d_name, basename, PATH_MAX) ==
+ 0))
+ {
+ snprintf (filename, PATH_MAX, "%s%s%s",
+ dirname, PATH_SEP, direntry->d_name);
+ break;
+ }
+ }
+ while (direntry != 0);
+ if (direntry == 0)
+ {
+ DBG (5, "download_firmware: file `%s' not found\n", filename);
+ status = SANE_STATUS_INVAL;
+ }
+ closedir (dir);
+ }
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (5, "download_firmware: trying %s\n", filename);
+ f = fopen (filename, "rb");
+ if (!f)
+ {
+ DBG (5,
+ "download_firmware_file: Couldn't open firmware file `%s': %s\n",
+ filename, strerror (errno));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (0, "Couldn't open firmware file (`%s'): %s\n",
+ filename, strerror (errno));
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ fseek (f, 0, SEEK_END);
+ size = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ if (size == -1)
+ {
+ DBG (1, "download_firmware_file: error getting size of "
+ "firmware file \"%s\": %s\n", filename, strerror (errno));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (5, "firmware size: %d\n", size);
+ buf = (SANE_Byte *) malloc (size);
+ if (!buf)
+ {
+ DBG (1, "download_firmware_file: cannot allocate %d bytes "
+ "for firmware\n", size);
+ status = SANE_STATUS_NO_MEM;
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ int bytes_read = fread (buf, 1, size, f);
+ if (bytes_read != size)
+ {
+ DBG (1, "download_firmware_file: problem reading firmware "
+ "file \"%s\": %s\n", filename, strerror (errno));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (f)
+ fclose (f);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ status = gt68xx_device_download_firmware (dev, buf, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "download_firmware_file: firmware download failed: %s\n",
+ sane_strstatus (status));
+ }
+ }
+
+ if (buf)
+ free (buf);
+
+ return status;
+}
+
+/** probe for gt68xx devices
+ * This function scan usb and try to attached to scanner
+ * configured in gt68xx.conf .
+ */
+static SANE_Status probe_gt68xx_devices(void)
+{
+ SANE_Char line[PATH_MAX];
+ SANE_Char *word;
+ SANE_String_Const cp;
+ SANE_Int linenumber;
+ GT68xx_Device *dev;
+ FILE *fp;
+
+ /* set up for no new devices detected at first */
+ new_dev = 0;
+ new_dev_len = 0;
+ new_dev_alloced = 0;
+
+ /* mark already detected devices as missing, during device probe
+ * detected devices will clear this flag */
+ dev = first_dev;
+ while(dev!=NULL)
+ {
+ dev->missing = SANE_TRUE;
+ dev = dev->next;
+ }
+
+ fp = sanei_config_open (GT68XX_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/usb/scanner instead of insisting on config file */
+ DBG (3, "sane_init: couldn't open config file `%s': %s. Using "
+ "/dev/usb/scanner directly\n", GT68XX_CONFIG_FILE,
+ strerror (errno));
+ attach ("/dev/usb/scanner", 0, SANE_FALSE);
+ return SANE_STATUS_GOOD;
+ }
+
+ little_endian = calc_little_endian ();
+ DBG (5, "sane_init: %s endian machine\n", little_endian ? "little" : "big");
+
+ linenumber = 0;
+ DBG (4, "sane_init: reading config file `%s'\n", GT68XX_CONFIG_FILE);
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ word = 0;
+ linenumber++;
+
+ cp = sanei_config_get_string (line, &word);
+ if (!word || cp == line)
+ {
+ DBG (6, "sane_init: config file line %d: ignoring empty line\n",
+ linenumber);
+ if (word)
+ free (word);
+ continue;
+ }
+ if (word[0] == '#')
+ {
+ DBG (6, "sane_init: config file line %d: ignoring comment line\n",
+ linenumber);
+ free (word);
+ continue;
+ }
+
+ if (strcmp (word, "firmware") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (word)
+ {
+ int i;
+ for (i = 0; i < new_dev_len; i++)
+ {
+ new_dev[i]->model->firmware_name = word;
+ DBG (5, "sane_init: device %s: firmware will be loaded "
+ "from %s\n", new_dev[i]->model->name,
+ new_dev[i]->model->firmware_name);
+ }
+ if (i == 0)
+ DBG (5, "sane_init: firmware %s can't be loaded, set device "
+ "first\n", word);
+ }
+ else
+ {
+ DBG (3, "sane_init: option `firmware' needs a parameter\n");
+ }
+ }
+ else if (strcmp (word, "vendor") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (word)
+ {
+ int i;
+
+ for (i = 0; i < new_dev_len; i++)
+ {
+ new_dev[i]->model->vendor = word;
+ DBG (5, "sane_init: device %s: vendor name set to %s\n",
+ new_dev[i]->model->name, new_dev[i]->model->vendor);
+ }
+ if (i == 0)
+ DBG (5, "sane_init: can't set vendor name %s, set device "
+ "first\n", word);
+ }
+ else
+ {
+ DBG (3, "sane_init: option `vendor' needs a parameter\n");
+ }
+ }
+ else if (strcmp (word, "model") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (word)
+ {
+ int i;
+ for (i = 0; i < new_dev_len; i++)
+ {
+ new_dev[i]->model->model = word;
+ DBG (5, "sane_init: device %s: model name set to %s\n",
+ new_dev[i]->model->name, new_dev[i]->model->model);
+ }
+ if (i == 0)
+ DBG (5, "sane_init: can't set model name %s, set device "
+ "first\n", word);
+ free (word);
+ }
+ else
+ {
+ DBG (3, "sane_init: option `model' needs a parameter\n");
+ }
+ }
+ else if (strcmp (word, "override") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (word)
+ {
+ int i;
+ for (i = 0; i < new_dev_len; i++)
+ {
+ SANE_Status status;
+ GT68xx_Device *dev = new_dev[i];
+ GT68xx_Model *model;
+ if (gt68xx_device_get_model (word, &model) == SANE_TRUE)
+ {
+ status = gt68xx_device_set_model (dev, model);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "sane_init: couldn't override model: %s\n",
+ sane_strstatus (status));
+ else
+ DBG (5, "sane_init: new model set to %s\n",
+ dev->model->name);
+ }
+ else
+ {
+ DBG (1, "sane_init: override: model %s not found\n",
+ word);
+ }
+ }
+ if (i == 0)
+ DBG (5, "sane_init: can't override model to %s, set device "
+ "first\n", word);
+ free (word);
+ }
+ else
+ {
+ DBG (3, "sane_init: option `override' needs a parameter\n");
+ }
+ }
+ else if (strcmp (word, "afe") == 0)
+ {
+ GT68xx_AFE_Parameters afe = {0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ free (word);
+ word = 0;
+
+ status = get_afe_values (cp, &afe);
+ if (status == SANE_STATUS_GOOD)
+ {
+ int i;
+ for (i = 0; i < new_dev_len; i++)
+ {
+ new_dev[i]->model->afe_params = afe;
+ DBG (5, "sane_init: device %s: setting new afe values\n",
+ new_dev[i]->model->name);
+ }
+ if (i == 0)
+ DBG (5,
+ "sane_init: can't set afe values, set device first\n");
+ }
+ else
+ DBG (3, "sane_init: can't set afe values\n");
+ }
+ else
+ {
+ new_dev_len = 0;
+ DBG (4, "sane_init: config file line %d: trying to attach `%s'\n",
+ linenumber, line);
+ sanei_usb_attach_matching_devices (line, attach_one_device);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+
+ if (new_dev_alloced > 0)
+ {
+ new_dev_len = new_dev_alloced = 0;
+ free (new_dev);
+ }
+
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+/* -------------------------- SANE API functions ------------------------- */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+#ifdef DBG_LEVEL
+ if (DBG_LEVEL > 0)
+ {
+ DBG (5, "sane_init: debug options are enabled, handle with care\n");
+ debug_options = SANE_TRUE;
+ }
+#endif
+ DBG (2, "SANE GT68xx backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ sanei_usb_init ();
+
+ num_devices = 0;
+ first_dev = 0;
+ first_handle = 0;
+ devlist = 0;
+ new_dev = 0;
+ new_dev_len = 0;
+ new_dev_alloced = 0;
+
+ status = probe_gt68xx_devices ();
+ DBG (5, "sane_init: exit\n");
+
+ return status;
+}
+
+void
+sane_exit (void)
+{
+ GT68xx_Device *dev, *next;
+
+ DBG (5, "sane_exit: start\n");
+ sanei_usb_exit();
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ gt68xx_device_free (dev);
+ }
+ first_dev = 0;
+ first_handle = 0;
+ if (devlist)
+ free (devlist);
+ devlist = 0;
+
+ DBG (5, "sane_exit: exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ GT68xx_Device *dev;
+ SANE_Int dev_num;
+
+ DBG (5, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ /* hot-plug case : detection of newly connected scanners */
+ sanei_usb_scan_devices ();
+ probe_gt68xx_devices ();
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ dev_num = 0;
+ dev = first_dev;
+ while(dev!=NULL)
+ {
+ SANE_Device *sane_device;
+
+ /* don't return devices that have been unplugged */
+ if(dev->missing==SANE_FALSE)
+ {
+ sane_device = malloc (sizeof (*sane_device));
+ if (!sane_device)
+ return SANE_STATUS_NO_MEM;
+ sane_device->name = dev->file_name;
+ sane_device->vendor = dev->model->vendor;
+ sane_device->model = dev->model->model;
+ sane_device->type = strdup ("flatbed scanner");
+ devlist[dev_num] = sane_device;
+ dev_num++;
+ }
+
+ /* next device */
+ dev = dev->next;
+ }
+ devlist[dev_num] = 0;
+
+ *device_list = devlist;
+
+ DBG (5, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ GT68xx_Device *dev;
+ SANE_Status status;
+ GT68xx_Scanner *s;
+ SANE_Bool power_ok;
+
+ DBG (5, "sane_open: start (devicename = `%s')\n", devicename);
+
+ if (devicename[0])
+ {
+ /* test for gt68xx short hand name */
+ if(strcmp(devicename,"gt68xx")!=0)
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->file_name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ DBG (5, "sane_open: couldn't find `%s' in devlist, trying attach\n",
+ devicename);
+ RIE (attach (devicename, &dev, SANE_TRUE));
+ }
+ else
+ DBG (5, "sane_open: found `%s' in devlist\n", dev->model->name);
+ }
+ else
+ {
+ dev = first_dev;
+ if (dev)
+ {
+ devicename = dev->file_name;
+ DBG (5, "sane_open: default empty devicename, using first device `%s'\n", devicename);
+ }
+ }
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = first_dev;
+ if (dev)
+ {
+ devicename = dev->file_name;
+ DBG (5, "sane_open: empty devicename, trying `%s'\n", devicename);
+ }
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ RIE (gt68xx_device_open (dev, devicename));
+ RIE (gt68xx_device_activate (dev));
+
+ if (dev->model->flags & GT68XX_FLAG_UNTESTED)
+ {
+ DBG (0, "WARNING: Your scanner is not fully supported or at least \n");
+ DBG (0, " had only limited testing. Please be careful and \n");
+ DBG (0, " report any failure/success to \n");
+ DBG (0, " sane-devel@lists.alioth.debian.org. Please provide as many\n");
+ DBG (0, " details as possible, e.g. the exact name of your\n");
+ DBG (0, " scanner and what does (not) work.\n");
+ }
+
+ if (dev->manual_selection)
+ {
+ DBG (0, "WARNING: You have manually added the ids of your scanner \n");
+ DBG (0,
+ " to gt68xx.conf. Please use an appropriate override \n");
+ DBG (0,
+ " for your scanner. Use extreme care and switch off \n");
+ DBG (0,
+ " the scanner immediately if you hear unusual noise. \n");
+ DBG (0, " Please report any success to \n");
+ DBG (0, " sane-devel@lists.alioth.debian.org. Please provide as many\n");
+ DBG (0, " details as possible, e.g. the exact name of your\n");
+ DBG (0, " scanner, ids, settings etc.\n");
+
+ if (strcmp (dev->model->name, "unknown-scanner") == 0)
+ {
+ GT68xx_USB_Device_Entry *entry;
+
+ DBG (0,
+ "ERROR: You haven't chosen an override in gt68xx.conf. Please use \n");
+ DBG (0, " one of the following: \n");
+
+ for (entry = gt68xx_usb_device_list; entry->model; ++entry)
+ {
+ if (strcmp (entry->model->name, "unknown-scanner") != 0)
+ DBG (0, " %s\n", entry->model->name);
+ }
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* The firmware check is disabled by default because it may confuse
+ some scanners: So the firmware is loaded everytime. */
+#if 0
+ RIE (gt68xx_device_check_firmware (dev, &firmware_loaded));
+ firmware_loaded = SANE_FALSE;
+ if (firmware_loaded)
+ DBG (3, "sane_open: firmware already loaded, skipping load\n");
+ else
+ RIE (download_firmware_file (dev));
+ /* RIE (gt68xx_device_check_firmware (dev, &firmware_loaded)); */
+ if (!firmware_loaded)
+ {
+ DBG (1, "sane_open: firmware still not loaded? Proceeding anyway\n");
+ /* return SANE_STATUS_IO_ERROR; */
+ }
+#else
+ RIE (download_firmware_file (dev));
+#endif
+
+ RIE (gt68xx_device_get_id (dev));
+
+ if (!(dev->model->flags & GT68XX_FLAG_NO_STOP))
+ RIE (gt68xx_device_stop_scan (dev));
+
+ RIE (gt68xx_device_get_power_status (dev, &power_ok));
+ if (power_ok)
+ {
+ DBG (5, "sane_open: power ok\n");
+ }
+ else
+ {
+ DBG (0, "sane_open: power control failure: check power plug!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ RIE (gt68xx_scanner_new (dev, &s));
+ RIE (gt68xx_device_lamp_control (s->dev, SANE_TRUE, SANE_FALSE));
+ gettimeofday (&s->lamp_on_time, 0);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+ s->scanning = SANE_FALSE;
+ s->first_scan = SANE_TRUE;
+ s->gamma_table = 0;
+ s->calibrated = SANE_FALSE;
+ RIE (init_options (s));
+ dev->gray_mode_color = 0x02;
+
+ /* try to restore calibration from file */
+ if((s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE))
+ {
+ /* error restoring calibration is non blocking */
+ gt68xx_read_calibration(s);
+ }
+
+ DBG (5, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ GT68xx_Scanner *prev, *s;
+ GT68xx_Device *dev;
+
+ DBG (5, "sane_close: start\n");
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (5, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ if (s->val[OPT_LAMP_OFF_AT_EXIT].w == SANE_TRUE)
+ gt68xx_device_lamp_control (s->dev, SANE_FALSE, SANE_FALSE);
+
+ dev = s->dev;
+
+ free (s->val[OPT_MODE].s);
+ free (s->val[OPT_GRAY_MODE_COLOR].s);
+ free (s->val[OPT_SOURCE].s);
+ free (dev->file_name);
+ free ((void *)(size_t)s->opt[OPT_RESOLUTION].constraint.word_list);
+
+ gt68xx_scanner_free (s);
+
+ gt68xx_device_fix_descriptor (dev);
+
+ gt68xx_device_deactivate (dev);
+ gt68xx_device_close (dev);
+
+ DBG (5, "sane_close: exit\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ GT68xx_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ DBG (5, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option);
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ GT68xx_Scanner *s = handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (5, "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" :
+ (action == SANE_ACTION_SET_VALUE) ? "set" :
+ (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->opt[option].name, option);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ {
+ DBG (1, "sane_control_option: don't call this function while "
+ "scanning (option = %s (%d))\n", s->opt[option].name, option);
+
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (1, "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (2, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_FULL_SCAN:
+ case OPT_COARSE_CAL:
+ case OPT_COARSE_CAL_ONCE:
+ case OPT_QUALITY_CAL:
+ case OPT_BACKTRACK:
+ case OPT_BACKTRACK_LINES:
+ case OPT_PREVIEW:
+ case OPT_LAMP_OFF_AT_EXIT:
+ case OPT_AUTO_WARMUP:
+ case OPT_GAMMA_VALUE:
+ case OPT_THRESHOLD:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ /* string options: */
+ case OPT_MODE:
+ case OPT_GRAY_MODE_COLOR:
+ case OPT_SOURCE:
+ strcpy (val, s->val[option].s);
+ break;
+ case OPT_NEED_CALIBRATION_SW:
+ *(SANE_Bool *) val = !s->calibrated;
+ break;
+ case OPT_PAGE_LOADED_SW:
+ s->dev->model->command_set->document_present (s->dev, val);
+ break;
+ default:
+ DBG (2, "sane_control_option: can't get unknown option %d\n",
+ option);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (2, "sane_control_option: option %d is not settable\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_FULL_SCAN:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_LAMP_OFF_AT_EXIT:
+ case OPT_AUTO_WARMUP:
+ case OPT_COARSE_CAL_ONCE:
+ case OPT_BACKTRACK_LINES:
+ case OPT_QUALITY_CAL:
+ case OPT_GAMMA_VALUE:
+ case OPT_THRESHOLD:
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_GRAY_MODE_COLOR:
+ if (strcmp (s->val[option].s, val) != 0)
+ { /* something changed */
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ break;
+ case OPT_SOURCE:
+ if (strcmp (s->val[option].s, val) != 0)
+ { /* something changed */
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, "Transparency Adapter") == 0)
+ {
+ RIE (gt68xx_device_lamp_control
+ (s->dev, SANE_FALSE, SANE_TRUE));
+ x_range.max = s->dev->model->x_size_ta;
+ y_range.max = s->dev->model->y_size_ta;
+ }
+ else
+ {
+ RIE (gt68xx_device_lamp_control
+ (s->dev, SANE_TRUE, SANE_FALSE));
+ x_range.max = s->dev->model->x_size;
+ y_range.max = s->dev->model->y_size;
+ }
+ s->first_scan = SANE_TRUE;
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ gettimeofday (&s->lamp_on_time, 0);
+ }
+ break;
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ ENABLE (OPT_THRESHOLD);
+ DISABLE (OPT_BIT_DEPTH);
+ ENABLE (OPT_GRAY_MODE_COLOR);
+ }
+ else
+ {
+ DISABLE (OPT_THRESHOLD);
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ RIE (create_bpp_list (s, s->dev->model->bpp_gray_values));
+ ENABLE (OPT_GRAY_MODE_COLOR);
+ }
+ else
+ {
+ RIE (create_bpp_list (s, s->dev->model->bpp_color_values));
+ DISABLE (OPT_GRAY_MODE_COLOR);
+ }
+ if (s->bpp_list[0] < 2)
+ DISABLE (OPT_BIT_DEPTH);
+ else
+ ENABLE (OPT_BIT_DEPTH);
+ }
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_COARSE_CAL:
+ s->val[option].w = *(SANE_Word *) val;
+ if (s->val[option].w == SANE_TRUE)
+ {
+ ENABLE (OPT_COARSE_CAL_ONCE);
+ s->first_scan = SANE_TRUE;
+ }
+ else
+ {
+ DISABLE (OPT_COARSE_CAL_ONCE);
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_BACKTRACK:
+ s->val[option].w = *(SANE_Word *) val;
+ if (s->val[option].w == SANE_TRUE)
+ ENABLE (OPT_BACKTRACK_LINES);
+ else
+ DISABLE (OPT_BACKTRACK_LINES);
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_CALIBRATE:
+ status = gt68xx_sheetfed_scanner_calibrate (s);
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_CLEAR_CALIBRATION:
+ gt68xx_clear_calibration (s);
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ default:
+ DBG (2, "sane_control_option: can't set unknown option %d\n",
+ option);
+ }
+ }
+ else
+ {
+ DBG (2, "sane_control_option: unknown action %d for option %d\n",
+ action, option);
+ return SANE_STATUS_INVAL;
+ }
+ if (info)
+ *info = myinfo;
+
+ DBG (5, "sane_control_option: exit\n");
+ return status;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ GT68xx_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (5, "sane_get_parameters: start\n");
+
+ RIE (calc_parameters (s));
+ if (params)
+ *params = s->params;
+
+ DBG (4, "sane_get_parameters: format=%d, last_frame=%d, lines=%d\n",
+ s->params.format, s->params.last_frame, s->params.lines);
+ DBG (4, "sane_get_parameters: pixels_per_line=%d, bytes per line=%d\n",
+ s->params.pixels_per_line, s->params.bytes_per_line);
+ DBG (3, "sane_get_parameters: pixels %dx%dx%d\n",
+ s->params.pixels_per_line, s->params.lines, 1 << s->params.depth);
+
+ DBG (5, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ GT68xx_Scanner *s = handle;
+ GT68xx_Scan_Request scan_request;
+ GT68xx_Scan_Parameters scan_params;
+ SANE_Status status;
+ SANE_Int i, gamma_size;
+ unsigned int *buffer_pointers[3];
+ SANE_Bool document;
+
+ DBG (5, "sane_start: start\n");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ RIE (calc_parameters (s));
+
+ if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w)
+ {
+ DBG (0, "sane_start: top left x >= bottom right x --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w)
+ {
+ DBG (0, "sane_start: top left y >= bottom right y --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (strcmp (s->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_BLUE) == 0)
+ s->dev->gray_mode_color = 0x01;
+ else if (strcmp (s->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_GREEN) == 0)
+ s->dev->gray_mode_color = 0x02;
+ else
+ s->dev->gray_mode_color = 0x03;
+
+ setup_scan_request (s, &scan_request);
+ if (!s->first_scan && s->val[OPT_COARSE_CAL_ONCE].w == SANE_TRUE)
+ s->auto_afe = SANE_FALSE;
+ else
+ s->auto_afe = s->val[OPT_COARSE_CAL].w;
+
+ s->dev->gamma_value = s->val[OPT_GAMMA_VALUE].w;
+ gamma_size = s->params.depth == 16 ? 65536 : 256;
+ s->gamma_table = malloc (sizeof (SANE_Int) * gamma_size);
+ if (!s->gamma_table)
+ {
+ DBG (1, "sane_start: couldn't malloc %d bytes for gamma table\n",
+ gamma_size);
+ return SANE_STATUS_NO_MEM;
+ }
+ for (i = 0; i < gamma_size; i++)
+ {
+ s->gamma_table[i] =
+ (gamma_size - 1) * pow (((double) i + 1) / (gamma_size),
+ 1.0 / SANE_UNFIX (s->dev->gamma_value)) + 0.5;
+ if (s->gamma_table[i] > (gamma_size - 1))
+ s->gamma_table[i] = (gamma_size - 1);
+ if (s->gamma_table[i] < 0)
+ s->gamma_table[i] = 0;
+#if 0
+ printf ("%d %d\n", i, s->gamma_table[i]);
+#endif
+ }
+
+ if(!(s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE))
+ {
+ s->calib = s->val[OPT_QUALITY_CAL].w;
+ }
+
+ if (!(s->dev->model->flags & GT68XX_FLAG_NO_STOP))
+ RIE (gt68xx_device_stop_scan (s->dev));
+
+ if (!(s->dev->model->flags & GT68XX_FLAG_SHEET_FED))
+ RIE (gt68xx_device_carriage_home (s->dev));
+
+ gt68xx_scanner_wait_for_positioning (s);
+ gettimeofday (&s->start_time, 0);
+
+ if (s->val[OPT_BACKTRACK].w == SANE_TRUE)
+ scan_request.backtrack = SANE_TRUE;
+ else
+ {
+ if (s->val[OPT_RESOLUTION].w >= s->dev->model->ydpi_no_backtrack)
+ scan_request.backtrack = SANE_FALSE;
+ else
+ scan_request.backtrack = SANE_TRUE;
+ }
+
+ if (scan_request.backtrack)
+ scan_request.backtrack_lines = s->val[OPT_BACKTRACK_LINES].w;
+ else
+ scan_request.backtrack_lines = 0;
+
+ /* don't call calibration for scanners that use sheetfed_calibrate */
+ if(!(s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE))
+ {
+ RIE (gt68xx_scanner_calibrate (s, &scan_request));
+ }
+ else
+ {
+ s->calib = s->calibrated;
+ }
+
+ /* is possible, wait for document to be inserted before scanning */
+ /* wait for 5 secondes max */
+ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED
+ && s->dev->model->command_set->document_present)
+ {
+ i=0;
+ do
+ {
+ RIE(s->dev->model->command_set->document_present(s->dev,&document));
+ if(document==SANE_FALSE)
+ {
+ i++;
+ sleep(1);
+ }
+ } while ((i<5) && (document==SANE_FALSE));
+ if(document==SANE_FALSE)
+ {
+ DBG (4, "sane_start: no doucment detected after %d s\n",i);
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ /* some sheetfed scanners need a special operation to move
+ * paper before starting real scan */
+ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED)
+ {
+ RIE (gt68xx_sheetfed_move_to_scan_area (s, &scan_request));
+ }
+
+ /* restore calibration */
+ if( (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)
+ &&(s->calibrated == SANE_TRUE))
+ {
+ /* compute scan parameters */
+ scan_request.calculate = SANE_TRUE;
+ gt68xx_device_setup_scan (s->dev, &scan_request, SA_SCAN, &scan_params);
+
+ /* restore settings from calibration stored */
+ memcpy(s->dev->afe,&(s->afe_params), sizeof(GT68xx_AFE_Parameters));
+ RIE (gt68xx_assign_calibration (s, scan_params));
+ scan_request.calculate = SANE_FALSE;
+ }
+
+ /* send scan request to the scanner */
+ RIE (gt68xx_scanner_start_scan (s, &scan_request, &scan_params));
+
+ for (i = 0; i < scan_params.overscan_lines; ++i)
+ RIE (gt68xx_scanner_read_line (s, buffer_pointers));
+ DBG (4, "sane_start: wanted: dpi=%d, x=%.1f, y=%.1f, width=%.1f, "
+ "height=%.1f, color=%s\n", scan_request.xdpi,
+ SANE_UNFIX (scan_request.x0),
+ SANE_UNFIX (scan_request.y0), SANE_UNFIX (scan_request.xs),
+ SANE_UNFIX (scan_request.ys), scan_request.color ? "color" : "gray");
+
+ s->line = 0;
+ s->byte_count = s->reader->params.pixel_xs;
+ s->total_bytes = 0;
+ s->first_scan = SANE_FALSE;
+
+#ifdef DEBUG_BRIGHTNESS
+ s->average_white = 0;
+ s->max_white = 0;
+ s->min_black = 255;
+#endif
+
+ s->scanning = SANE_TRUE;
+
+ DBG (5, "sane_start: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ GT68xx_Scanner *s = handle;
+ SANE_Status status;
+ static unsigned int *buffer_pointers[3];
+ SANE_Int inflate_x;
+ SANE_Bool lineart;
+ SANE_Int i, color, colors;
+
+ if (!s)
+ {
+ DBG (1, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!buf)
+ {
+ DBG (1, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (1, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *len = 0;
+
+ if (!s->scanning)
+ {
+ DBG (3, "sane_read: scan was cancelled, is over or has not been "
+ "initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ DBG (5, "sane_read: start (line %d of %d, byte_count %d of %d)\n",
+ s->line, s->reader->params.pixel_ys, s->byte_count,
+ s->reader->params.pixel_xs);
+
+ if (s->line >= s->reader->params.pixel_ys
+ && s->byte_count >= s->reader->params.pixel_xs)
+ {
+ DBG (4, "sane_read: nothing more to scan: EOF\n");
+ return SANE_STATUS_EOF;
+ }
+
+ inflate_x = s->val[OPT_RESOLUTION].w / s->dev->model->optical_xdpi;
+ if (inflate_x > 1)
+ DBG (5, "sane_read: inflating x by factor %d\n", inflate_x);
+ else
+ inflate_x = 1;
+
+ lineart = (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ ? SANE_TRUE : SANE_FALSE;
+
+ if (s->reader->params.color)
+ colors = 3;
+ else
+ colors = 1;
+
+ while ((*len) < max_len)
+ {
+ if (s->byte_count >= s->reader->params.pixel_xs)
+ {
+ if (s->line >= s->reader->params.pixel_ys)
+ {
+ DBG (4, "sane_read: scan complete: %d bytes, %d total\n",
+ *len, s->total_bytes);
+ return SANE_STATUS_GOOD;
+ }
+ DBG (5, "sane_read: getting line %d of %d\n", s->line,
+ s->reader->params.pixel_ys);
+ RIE (gt68xx_scanner_read_line (s, buffer_pointers));
+ s->line++;
+ s->byte_count = 0;
+
+ /* Apply gamma */
+ for (color = 0; color < colors; color++)
+ for (i = 0; i < s->reader->pixels_per_line; i++)
+ {
+ if (s->reader->params.depth > 8)
+ buffer_pointers[color][i] =
+ s->gamma_table[buffer_pointers[color][i]];
+ else
+ buffer_pointers[color][i] =
+ (s->gamma_table[buffer_pointers[color][i] >> 8] << 8) +
+ (s->gamma_table[buffer_pointers[color][i] >> 8]);
+ }
+ /* mirror lines */
+ if (s->dev->model->flags & GT68XX_FLAG_MIRROR_X)
+ {
+ unsigned int swap;
+
+ for (color = 0; color < colors; color++)
+ {
+ for (i = 0; i < s->reader->pixels_per_line / 2; i++)
+ {
+ swap = buffer_pointers[color][i];
+ buffer_pointers[color][i] =
+ buffer_pointers[color][s->reader->pixels_per_line -
+ 1 - i];
+ buffer_pointers[color][s->reader->pixels_per_line - 1 -
+ i] = swap;
+ }
+ }
+ }
+ }
+ if (lineart)
+ {
+ SANE_Int bit;
+ SANE_Byte threshold = s->val[OPT_THRESHOLD].w;
+
+ buf[*len] = 0;
+ for (bit = 7; bit >= 0; bit--)
+ {
+ SANE_Byte is_black =
+ (((buffer_pointers[0][s->byte_count] >> 8) & 0xff) >
+ threshold) ? 0 : 1;
+ buf[*len] |= (is_black << bit);
+ if ((7 - bit) % inflate_x == (inflate_x - 1))
+ s->byte_count++;
+ }
+ }
+ else if (s->reader->params.color)
+ {
+ /* color */
+ if (s->reader->params.depth > 8)
+ {
+ SANE_Int color = (s->total_bytes / 2) % 3;
+ if ((s->total_bytes % 2) == 0)
+ {
+ if (little_endian)
+ buf[*len] = buffer_pointers[color][s->byte_count] & 0xff;
+ else
+ buf[*len] =
+ (buffer_pointers[color][s->byte_count] >> 8) & 0xff;
+ }
+ else
+ {
+ if (little_endian)
+ buf[*len] =
+ (buffer_pointers[color][s->byte_count] >> 8) & 0xff;
+ else
+ buf[*len] = buffer_pointers[color][s->byte_count] & 0xff;
+
+ if (s->total_bytes % (inflate_x * 6) == (inflate_x * 6 - 1))
+ s->byte_count++;
+ }
+ }
+ else
+ {
+ SANE_Int color = s->total_bytes % 3;
+ buf[*len] = (buffer_pointers[color][s->byte_count] >> 8) & 0xff;
+ if (s->total_bytes % (inflate_x * 3) == (inflate_x * 3 - 1))
+ s->byte_count++;
+#ifdef DEBUG_BRIGHTNESS
+ s->average_white += buf[*len];
+ s->max_white =
+ (buf[*len] > s->max_white) ? buf[*len] : s->max_white;
+ s->min_black =
+ (buf[*len] < s->min_black) ? buf[*len] : s->min_black;
+#endif
+ }
+ }
+ else
+ {
+ /* gray */
+ if (s->reader->params.depth > 8)
+ {
+ if ((s->total_bytes % 2) == 0)
+ {
+ if (little_endian)
+ buf[*len] = buffer_pointers[0][s->byte_count] & 0xff;
+ else
+ buf[*len] =
+ (buffer_pointers[0][s->byte_count] >> 8) & 0xff;
+ }
+ else
+ {
+ if (little_endian)
+ buf[*len] =
+ (buffer_pointers[0][s->byte_count] >> 8) & 0xff;
+ else
+ buf[*len] = buffer_pointers[0][s->byte_count] & 0xff;
+ if (s->total_bytes % (2 * inflate_x) == (2 * inflate_x - 1))
+ s->byte_count++;
+ }
+ }
+ else
+ {
+ buf[*len] = (buffer_pointers[0][s->byte_count] >> 8) & 0xff;
+ if (s->total_bytes % inflate_x == (inflate_x - 1))
+ s->byte_count++;
+ }
+ }
+ (*len)++;
+ s->total_bytes++;
+ }
+
+ DBG (4, "sane_read: exit (line %d of %d, byte_count %d of %d, %d bytes, "
+ "%d total)\n",
+ s->line, s->reader->params.pixel_ys, s->byte_count,
+ s->reader->params.pixel_xs, *len, s->total_bytes);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ GT68xx_Scanner *s = handle;
+
+ DBG (5, "sane_cancel: start\n");
+
+ if (s->scanning)
+ {
+ s->scanning = SANE_FALSE;
+ if (s->total_bytes != (s->params.bytes_per_line * s->params.lines))
+ DBG (1, "sane_cancel: warning: scanned %d bytes, expected %d "
+ "bytes\n", s->total_bytes,
+ s->params.bytes_per_line * s->params.lines);
+ else
+ {
+ struct timeval now;
+ int secs;
+
+ gettimeofday (&now, 0);
+ secs = now.tv_sec - s->start_time.tv_sec;
+
+ DBG (3,
+ "sane_cancel: scan finished, scanned %d bytes in %d seconds\n",
+ s->total_bytes, secs);
+#ifdef DEBUG_BRIGHTNESS
+ DBG (1,
+ "sane_cancel: average white: %d, max_white=%d, min_black=%d\n",
+ s->average_white / s->total_bytes, s->max_white, s->min_black);
+#endif
+
+ }
+ /* some scanners don't like this command when cancelling a scan */
+ sanei_usb_set_timeout (SHORT_TIMEOUT);
+ gt68xx_device_fix_descriptor (s->dev);
+ gt68xx_scanner_stop_scan (s);
+ sanei_usb_set_timeout (LONG_TIMEOUT);
+
+ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED)
+ {
+ gt68xx_device_paperfeed (s->dev);
+ }
+ else
+ {
+ sanei_usb_set_timeout (SHORT_TIMEOUT);
+ gt68xx_scanner_wait_for_positioning (s);
+ sanei_usb_set_timeout (LONG_TIMEOUT);
+ gt68xx_device_carriage_home (s->dev);
+ }
+ if (s->gamma_table)
+ free (s->gamma_table);
+ s->gamma_table = 0;
+ }
+ else
+ {
+ DBG (4, "sane_cancel: scan has not been initiated yet, "
+ "or it is allready aborted\n");
+ }
+
+ DBG (5, "sane_cancel: exit\n");
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ GT68xx_Scanner *s = handle;
+
+ DBG (5, "sane_set_io_mode: handle = %p, non_blocking = %s\n",
+ handle, non_blocking == SANE_TRUE ? "true" : "false");
+
+ if (!s->scanning)
+ {
+ DBG (1, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ GT68xx_Scanner *s = handle;
+
+ DBG (5, "sane_get_select_fd: handle = %p, fd = %p\n", handle, (void *) fd);
+
+ if (!s->scanning)
+ {
+ DBG (1, "sane_get_select_fd: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx.conf.in b/backend/gt68xx.conf.in
new file mode 100644
index 0000000..4affdcc
--- /dev/null
+++ b/backend/gt68xx.conf.in
@@ -0,0 +1,255 @@
+
+# gt68xx.conf: Configuration file for GT68XX based scanners (@PACKAGEVERSION@)
+# Read man sane-gt68xx for documentation
+
+# Put the firmware file into "@DATADIR@/sane/gt68xx/".
+
+# Manual configuration is necessary for some scanners. Please uncomment the
+# override line and optinally the vendor and product lines that apply to your
+# scanner. For some scanners it's also necessary to change the name of the
+# firmware file.
+
+#############################################################################
+# For testing scanners that are not yet supported by this backend add the
+# vendor and product ids in the usb line below. Also fill in the override
+# and firmware lines. For more details, see:
+# http://www.meier-geinitz.de/sane/gt68xx-backend/adding.html
+
+# usb vendor product
+# override "something"
+# firmware "path"
+
+##############################################################################
+# Autodetect Mustek BearPaw 1200 CU, 2400 CU, Mustek ScanExpress 1200 UB Plus,
+# Artec Ultima 2000 (e+), and several other GT-6801-based scanners
+usb 0x05d8 0x4002
+
+# Mustek BearPaw 1200 CU doesn't need any manual override
+
+# Mustek ScanExpress 1200 UB Plus:
+#override "mustek-scanexpress-1200-ub-plus"
+
+# Medion/Lifetec/Tevion LT 9452:
+#override "mustek-scanexpress-1200-ub-plus"
+#vendor "Lifetec"
+#model "LT 9452"
+
+# Trust Compact Scan USB 19200:
+#override "mustek-scanexpress-1200-ub-plus"
+#vendor "Trust"
+#model "Compact Scan USB 19200"
+
+# Mustek ScanExpress 2400 USB
+#override "mustek-scanexpress-2400-usb"
+
+# Artec Ultima 2000:
+#override "artec-ultima-2000"
+#firmware "ePlus2k.usb"
+
+# Artec Ultima 2000e+:
+#override "artec-ultima-2000"
+#firmware "ePlus2k.usb"
+#vendor "Artec"
+#model "Ultima 2000e+"
+
+# Boeder SmartScan Slim Edition:
+#override "artec-ultima-2000"
+#vendor "Boeder"
+#model "SmartScan Slim Edition"
+
+# Medion/Lifetec/Tevion/Cytron MD/LT 9385:
+#override "artec-ultima-2000"
+#vendor "Medion"
+#model "MD/LT 9385"
+
+# Medion/Lifetec/Tevion/Cytron MD 9458:
+#override "artec-ultima-2000"
+#vendor "Medion"
+#model "MD 9458"
+
+# Trust Flat Scan USB 19200:
+#override "artec-ultima-2000"
+#vendor "Trust"
+#model "Flat Scan USB 19200"
+
+# Mustek BearPaw 2400 CU:
+#override "mustek-bearpaw-2400-cu"
+
+# Fujitsu 1200CUS:
+#override "mustek-bearpaw-2400-cu"
+#vendor "Fujitsu"
+#model "1200CUS"
+
+##############################################################################
+# Autodetect Mustek BearPaw 1200 TA and Mustek BearPaw 1200 CS
+usb 0x055f 0x021e
+
+# Mustek BearPaw 1200 TA doesn't need any manual settings
+
+# Mustek BearPaw 1200 CS:
+#model "Bearpaw 1200 CS"
+
+##############################################################################
+# Autodetect Mustek BearPaw 2400 TA and Mustek BearPaw 2400 CS
+usb 0x055f 0x0218
+
+# Mustek BearPaw 2400 TA doesn't need any manual settings
+
+# Mustek BearPaw 2400 CS:
+#model "Bearpaw 2400 CS"
+
+##############################################################################
+# Autodetect Mustek BearPaw 2400 TA Plus, Packard Bell Diamond 2450, and
+# Trust 240TH Easy Webscan Gold
+usb 0x055f 0x0219
+
+# Mustek BearPaw 2400 TA doesn't need any manual settings
+
+# Trust 240TH Easy Webscan Gold:
+#vendor "Trust"
+#model "240TH Easy Webscan Gold"
+
+# Packard Bell Diamond 2450:
+#vendor "Packard Bell"
+#model "Diamond 2450"
+
+##############################################################################
+# Autodetect Mustek BearPaw 2448 TA Plus and Mustek BearPaw 2448 CS Plus
+usb 0x055f 0x021a
+
+# Mustek BearPaw 2448 CS Plus:
+#model "Bearpaw 2448 CS Plus"
+
+##############################################################################
+# Autodetect Mustek BearPaw 2400 CU Plus
+usb 0x055f 0x021d
+
+##############################################################################
+# Autodetect Mustek ScanExpress 1248 UB
+usb 0x055f 0x021f
+
+##############################################################################
+# Autodetect Mustek Bearpaw 1200 CU Plus and Packard Bell Diamond 1200
+usb 0x055f 0x021c
+
+# Mustek BearPaw 1200 CU Plus doesn't need any manual settings
+
+# Packard Bell Diamond 1200:
+#vendor "Packard Bell"
+#model "Diamond 1200"
+
+# Another Mustek BearPaw 1200 CU Plus version?
+usb 0x055f 0x021b
+
+##############################################################################
+# Autodetect Mustek ScanExpress A3 USB
+usb 0x055f 0x0210
+
+# Mustek ScanExpress A3 USB doesn't need any manual settings
+
+##############################################################################
+# Autodetect Lexmark X70/X73
+usb 0x043d 0x002d
+
+##############################################################################
+# Autodetect Plustek OpticPro 1248U and Revscan 19200i
+usb 0x07b3 0x0401
+usb 0x07b3 0x0400
+
+# Plustek OpticPro 1248U doesn't need any manual settings
+
+# RevScan 19200i
+#vendor "RevScan"
+#model "19200i"
+
+##############################################################################
+# Autodetect Plustek OpticPro U16B and UT16B
+usb 0x07b3 0x0402
+usb 0x07b3 0x0403
+
+# Plustek OpticPro U16B doesn't need any manual settings
+
+# Plustek OpticPro UT16B
+#model "UT16B"
+
+##############################################################################
+# Autodetect Plustek OpticPro S12 and Nortek MyScan1200
+usb 0x07b3 0x040b
+#vendor "Nortek"
+#model "MyScan 1200"
+
+##############################################################################
+# Autodetect Plustek OpticPro S24
+usb 0x07b3 0x040e
+
+##############################################################################
+# Autodetect Plustek OpticSlim M12 and NeatReceipts Scanalizer Professional 2.5
+usb 0x07b3 0x0412
+#vendor "NeatReceipts"
+#model "Scanalizer Professional 2.5"
+
+##############################################################################
+# Iriscan Express 2
+usb 0x07b3 0x045f
+
+##############################################################################
+# Autodetect NeatReceipts Mobile Scanner
+usb 0x07b3 0x0462
+
+##############################################################################
+# Autodetect Plustek OpticSlim 1200
+usb 0x07b3 0x0413
+
+##############################################################################
+# Autodetect Plustek OpticSlim 2400
+usb 0x07b3 0x0422
+
+##############################################################################
+# Autodetect Plustek OpticSlim 2400 plus
+usb 0x07b3 0x0454
+model "OpticSlim 2400 Plus"
+override "plustek-opticslim-2400"
+
+##############################################################################
+# Autodetect Genius Colorpage SF600
+usb 0x0458 0x2021
+
+##############################################################################
+# Autodetect Genius Colorpage Vivid3x
+usb 0x0458 0x2011
+
+# Genius Colorpage Vivid3x doesn't need any manual settings
+
+##############################################################################
+# Autodetect Genius Colorpage Vivid4x
+usb 0x0458 0x201b
+
+##############################################################################
+# Autodetect Genius Colorpage Vivid3xe
+usb 0x0458 0x2017
+
+##############################################################################
+# Autodetect Genius Colorpage Vivid4xe
+usb 0x0458 0x201a
+
+##############################################################################
+# Autodetect Genius Colorpage 1200 X
+usb 0x0458 0x201d
+
+##############################################################################
+# Autodetect Genius Colorpage 1200 XE
+usb 0x0458 0x201f
+
+##############################################################################
+# Autodetect Genius Colorpage Vivid 4
+usb 0x0458 0x2014
+
+##############################################################################
+# Autodetect Genius Color Slim 1200
+usb 0x0458 0x201E
+
+##############################################################################
+# Autodetect Visioneer OneTouch 7300
+usb 0x04a7 0x0444
+
+##############################################################################
diff --git a/backend/gt68xx.h b/backend/gt68xx.h
new file mode 100644
index 0000000..20f8c6c
--- /dev/null
+++ b/backend/gt68xx.h
@@ -0,0 +1,56 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_H
+#define GT68XX_H
+
+#include <sys/types.h>
+#include "gt68xx_high.h"
+
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+#define GT68XX_CONFIG_FILE "gt68xx.conf"
+
+#endif /* not GT68XX_H */
diff --git a/backend/gt68xx_devices.c b/backend/gt68xx_devices.c
new file mode 100644
index 0000000..1239190
--- /dev/null
+++ b/backend/gt68xx_devices.c
@@ -0,0 +1,1947 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* Scanner-specific data */
+
+#include "gt68xx_low.h"
+#include "gt68xx_generic.c"
+#include "gt68xx_gt6801.c"
+#include "gt68xx_gt6816.c"
+
+static GT68xx_Command_Set mustek_gt6816_command_set = {
+ "mustek-gt6816", /* Name of this command set */
+
+ 0x40, /* Request type */
+ 0x01, /* Request */
+
+ 0x200c, /* Memory read - wValue */
+ 0x200b, /* Memory write - wValue */
+
+ 0x2010, /* Send normal command - wValue */
+ 0x3f40, /* Send normal command - wIndex */
+ 0x2011, /* Receive normal result - wValue */
+ 0x3f00, /* Receive normal result - wIndex */
+
+ 0x2012, /* Send small command - wValue */
+ 0x3f40, /* Send small command - wIndex */
+ 0x2013, /* Receive small result - wValue */
+ 0x3f00, /* Receive small result - wIndex */
+
+ /* activate */ NULL,
+ /* deactivate */ NULL,
+ gt6816_check_firmware,
+ gt6816_download_firmware,
+ gt6816_get_power_status,
+ gt6816_get_ta_status,
+ gt6816_lamp_control,
+ gt6816_is_moving,
+ gt68xx_generic_move_relative,
+ gt6816_carriage_home,
+ /* gt68xx_generic_paperfeed */ NULL,
+ gt68xx_generic_start_scan,
+ gt68xx_generic_read_scanned_data,
+ gt6816_stop_scan,
+ gt68xx_generic_setup_scan,
+ gt68xx_generic_set_afe,
+ gt68xx_generic_set_exposure_time,
+ gt68xx_generic_get_id,
+ /* gt68xx_generic_move_paper */ NULL,
+ /* gt6816_document_present */ NULL
+};
+
+static GT68xx_Command_Set mustek_gt6816_sheetfed_command_set = {
+ "mustek-gt6816", /* Name of this command set */
+
+ 0x40, /* Request type */
+ 0x01, /* Request */
+
+ 0x200c, /* Memory read - wValue */
+ 0x200b, /* Memory write - wValue */
+
+ 0x2010, /* Send normal command - wValue */
+ 0x3f40, /* Send normal command - wIndex */
+ 0x2011, /* Receive normal result - wValue */
+ 0x3f00, /* Receive normal result - wIndex */
+
+ 0x2012, /* Send small command - wValue */
+ 0x3f40, /* Send small command - wIndex */
+ 0x2013, /* Receive small result - wValue */
+ 0x3f00, /* Receive small result - wIndex */
+
+ /* activate */ NULL,
+ /* deactivate */ NULL,
+ gt6816_check_firmware,
+ gt6816_download_firmware,
+ gt6816_get_power_status,
+ gt6816_get_ta_status,
+ gt6816_lamp_control,
+ gt6816_is_moving,
+ gt68xx_generic_move_relative,
+ /* gt6816_carriage_home */ NULL,
+ gt68xx_generic_paperfeed,
+ gt68xx_generic_start_scan,
+ gt68xx_generic_read_scanned_data,
+ gt6801_stop_scan,
+ gt68xx_generic_setup_scan,
+ gt68xx_generic_set_afe,
+ gt68xx_generic_set_exposure_time,
+ gt68xx_generic_get_id,
+ gt68xx_generic_move_paper,
+ gt6816_document_present
+};
+
+static GT68xx_Command_Set mustek_gt6801_command_set = {
+ "mustek-gt6801",
+
+
+ 0x40, /* Request type */
+ 0x01, /* Request */
+
+ 0x200a, /* Memory read - wValue */
+ 0x2009, /* Memory write - wValue */
+
+ 0x2010, /* Send normal command - wValue */
+ 0x3f40, /* Send normal command - wIndex */
+ 0x2011, /* Receive normal result - wValue */
+ 0x3f00, /* Receive normal result - wIndex */
+
+ 0x2012, /* Send small command - wValue */
+ 0x3f40, /* Send small command - wIndex */
+ 0x2013, /* Receive small result - wValue */
+ 0x3f00, /* Receive small result - wIndex */
+
+ /* activate */ NULL,
+ /* deactivate */ NULL,
+ gt6801_check_firmware,
+ gt6801_download_firmware,
+ gt6801_get_power_status,
+ /* get_ta_status (FIXME: implement this) */ NULL,
+ gt6801_lamp_control,
+ gt6801_is_moving,
+ /* gt68xx_generic_move_relative *** to be tested */ NULL,
+ gt6801_carriage_home,
+ /* gt68xx_generic_paperfeed */ NULL,
+ gt68xx_generic_start_scan,
+ gt68xx_generic_read_scanned_data,
+ gt6801_stop_scan,
+ gt68xx_generic_setup_scan,
+ gt68xx_generic_set_afe,
+ gt68xx_generic_set_exposure_time,
+ gt68xx_generic_get_id,
+ /* gt68xx_generic_move_paper */ NULL,
+ /* gt6816_document_present */ NULL
+};
+
+static GT68xx_Command_Set plustek_gt6801_command_set = {
+ "plustek-gt6801",
+
+ 0x40, /* Request type */
+ 0x04, /* Request */
+
+ 0x200a, /* Memory read - wValue */
+ 0x2009, /* Memory write - wValue */
+
+ 0x2010, /* Send normal command - wValue */
+ 0x3f40, /* Send normal command - wIndex */
+ 0x2011, /* Receive normal result - wValue */
+ 0x3f00, /* Receive normal result - wIndex */
+
+ 0x2012, /* Send small command - wValue */
+ 0x3f40, /* Send small command - wIndex */
+ 0x2013, /* Receive small result - wValue */
+ 0x3f00, /* Receive small result - wIndex */
+
+ /* activate */ NULL,
+ /* deactivate */ NULL,
+ gt6801_check_plustek_firmware,
+ gt6801_download_firmware,
+ gt6801_get_power_status,
+ /* get_ta_status (FIXME: implement this) */ NULL,
+ gt6801_lamp_control,
+ gt6801_is_moving,
+ /* gt68xx_generic_move_relative *** to be tested */ NULL,
+ gt6801_carriage_home,
+ /* gt68xx_generic_paperfeed */ NULL,
+ gt68xx_generic_start_scan,
+ gt68xx_generic_read_scanned_data,
+ gt6801_stop_scan,
+ gt68xx_generic_setup_scan,
+ gt68xx_generic_set_afe,
+ /* set_exposure_time */ NULL,
+ gt68xx_generic_get_id,
+ /* gt68xx_generic_move_paper */ NULL,
+ /* gt6816_document_present */ NULL
+};
+
+static GT68xx_Model unknown_model = {
+ "unknown-scanner", /* Name */
+ "unknown manufacturer", /* Device vendor string */
+ "unknown device -- use override to select", /* Device model name */
+ "unknown", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {8, 0}, /* possible depths in gray mode */
+ {8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.0), /* Start of scan area in mm (y) */
+ SANE_FIX (200.0), /* Size of scan area in mm (x) */
+ SANE_FIX (280.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (9.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x14, 0x07, 0x14, 0x07, 0x14, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */
+ /* Standard values for unknown scanner */
+};
+
+static GT68xx_Model mustek_2400ta_model = {
+ "mustek-bearpaw-2400-ta", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 2400 TA", /* Device model name */
+ "A2fw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 200, 100, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 200, 100, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.67), /* Start of scan area in mm (x) */
+ SANE_FIX (7.4), /* Start of scan area in mm (y) */
+ SANE_FIX (219.0), /* Size of scan area in mm (x) */
+ SANE_FIX (298.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.635), /* Start of black mark in mm (x) */
+
+ SANE_FIX (94.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (107.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (37.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (165.0), /* Size of scan area in TA mode in mm (y) */
+ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_SCAN_FROM_HOME /* Which flags are needed for this scanner? */
+ /* flatbed values tested */
+};
+
+static GT68xx_Model mustek_2400taplus_model = {
+ "mustek-bearpaw-2400-ta-plus", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 2400 TA Plus", /* Device model name */
+ "A2Dfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 100, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 100, 50, 0}, /* possible y-resolutions */
+ {8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.41), /* Start of scan area in mm (x) */
+ SANE_FIX (7.4), /* Start of scan area in mm (y) */
+ SANE_FIX (217.5), /* Size of scan area in mm (x) */
+ SANE_FIX (298.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (5.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (94.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (107.0) /* Start of scan area in TA mode in mm (y) */ ,
+ SANE_FIX (37.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (165.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 48, 96, /* RGB CCD Line-distance correction in pixel */
+ 8, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_USE_OPTICAL_X | GT68XX_FLAG_SCAN_FROM_HOME /* Which flags are needed for this scanner? */
+ /* Setup and tested */
+};
+
+static GT68xx_Model mustek_2448taplus_model = {
+ "mustek-bearpaw-2448-ta-plus", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 2448 TA Plus", /* Device model name */
+ "A2Nfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 200, 100, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 200, 100, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.41), /* Start of scan area in mm (x) */
+ SANE_FIX (7.4), /* Start of scan area in mm (y) */
+ SANE_FIX (216.3), /* Size of scan area in mm (x) */
+ SANE_FIX (297.5), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (4.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (94.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (105.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (36.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (165.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 48, 96, /* RGB CCD Line-distance correction in pixel */
+ 8, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */
+ /* Based on data from Jakub Dvo?ák <xdvorak@chello.cz>. */
+};
+
+static GT68xx_Model mustek_1200ta_model = {
+ "mustek-bearpaw-1200-ta", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 1200 TA", /* Device model name */
+ "A1fw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (8.4), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (6.9), /* Start of black mark in mm (x) */
+
+ SANE_FIX (98.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (108.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (37.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (163.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */
+
+ 32, 16, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* Setup for 1200 TA */
+};
+
+static GT68xx_Model mustek_1200cuplus_model = {
+ "mustek-bearpaw-1200-cu-plus", /* Name */
+ "Mustek", /* Device vendor string */
+ "Bearpaw 1200 CU Plus", /* Device model name */
+ "PS1Dfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.0), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (9.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x14, 0x07, 0x14, 0x07, 0x14, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */
+ /* Tested by Hamersky Robert r.hamersky at utanet.at */
+};
+
+static GT68xx_Model mustek_1200cuplus2_model = {
+ "mustek-bearpaw-1200-cu-plus-2", /* Name */
+ "Mustek", /* Device vendor string */
+ "Bearpaw 1200 CU Plus", /* Device model name */
+ "PS1Gfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.0), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (5.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x19, 0x03, 0x1b, 0x05, 0x19, 0x03}, /* Default offset/gain */
+ {0xb0, 0xb0, 0xb0}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */
+ /* Tested by hmg */
+};
+
+static GT68xx_Model mustek_2400cuplus_model = {
+ "mustek-bearpaw-2400-cu-plus", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 2400 CU Plus", /* Device model name */
+ "PS2Dfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 200, 150, 100, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 200, 150, 100, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (2.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.0), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (4.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x15, 0x08, 0x12, 0x05, 0x12, 0x05}, /* Default offset/gain */
+ {0x2a0, 0x1ab, 0x10d}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */
+ /* Setup and tested */
+};
+
+/* Seems that Mustek ScanExpress 1200 UB Plus, the Mustek BearPaw 1200 CU
+ * and lots of other scanners have the same USB identifier.
+ */
+static GT68xx_Model mustek_1200cu_model = {
+ "mustek-bearpaw-1200-cu", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 1200 CU", /* Device model name */
+ "PS1fw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 600, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.0), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (4.2), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x13, 0x04, 0x15, 0x06, 0x0f, 0x02}, /* Default offset/gain */
+ {0x150, 0x150, 0x150}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* Setup and tested */
+};
+
+static GT68xx_Model mustek_scanexpress1200ubplus_model = {
+ "mustek-scanexpress-1200-ub-plus", /* Name */
+ "Mustek", /* Device vendor string */
+ "ScanExpress 1200 UB Plus", /* Device model name */
+ "SBfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (15.5), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (6.5), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x0f, 0x01, 0x15, 0x06, 0x13, 0x04}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* Setup and tested */
+};
+
+static GT68xx_Model mustek_scanexpress1248ub_model = {
+ "mustek-scanexpress-1248-ub", /* Name */
+ "Mustek", /* Device vendor string */
+ "ScanExpress 1248 UB", /* Device model name */
+ "SBSfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.0), /* Start of scan area in mm (x) */
+ SANE_FIX (14.5), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (9.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x1e, 0x08, 0x21, 0x0d, 0x1b, 0x05}, /* Default offset/gain */
+ {0x50, 0x50, 0x50}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */
+ /* tested by hmg */
+};
+
+
+static GT68xx_Model artec_ultima2000_model = {
+ "artec-ultima-2000", /* Name */
+ "Artec", /* Device vendor string */
+ "Ultima 2000", /* Device model name */
+ "Gt680xfw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
+ {600, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (2.0), /* Start of scan area in mm (x) */
+ SANE_FIX (7.0), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x0f, 0x01, 0x15, 0x06, 0x13, 0x04}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_MIRROR_X | GT68XX_FLAG_MOTOR_HOME | GT68XX_FLAG_OFFSET_INV /* Which flags are needed for this scanner? */
+ /* Setup for Cytron TCM MD 9385 */
+};
+
+static GT68xx_Model mustek_2400cu_model = {
+ "mustek-bearpaw-2400-cu", /* Name */
+ "Mustek", /* Device vendor string */
+ "BearPaw 2400 CU", /* Device model name */
+ "PS2fw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
+ {2400, 1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (13.5), /* Start of scan area in mm (y) */
+ SANE_FIX (215.0), /* Size of scan area in mm (x) */
+ SANE_FIX (296.9), /* Size of scan area in mm (y) */
+
+ SANE_FIX (5.5), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.9), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x0f, 0x01, 0x15, 0x06, 0x13, 0x04}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* basically tested, details may need tweaking */
+};
+
+static GT68xx_Model mustek_scanexpress2400usb_model = {
+ "mustek-scanexpress-2400-usb", /* Name */
+ "Mustek", /* Device vendor string */
+ "ScanExpress 2400 USB", /* Device model name */
+ "P9fw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (5.0), /* Start of scan area in mm (x) */
+ SANE_FIX (12.0), /* Start of scan area in mm (y) */
+ SANE_FIX (224.0), /* Size of scan area in mm (x) */
+ SANE_FIX (300.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (7.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 24, 12, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x12, 0x06, 0x0e, 0x03, 0x19, 0x25}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_UNTESTED | GT68XX_FLAG_SE_2400 /* Which flags are needed for this scanner? */
+ /* only partly tested, from "Fan Dan" <dan_fancn@hotmail.com> */
+};
+
+static GT68xx_Model mustek_a3usb_model = {
+ "mustek-scanexpress-a3-usb", /* Name */
+ "Mustek", /* Device vendor string */
+ "ScanExpress A3 USB", /* Device model name */
+ "A32fw.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 300, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 300, /* base x-res used to calculate geometry */
+ 300, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (7.0), /* Start of scan area in mm (x) */
+ SANE_FIX (11.5), /* Start of scan area in mm (y) */
+ SANE_FIX (297.0), /* Size of scan area in mm (x) */
+ SANE_FIX (433.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (2.4), /* Start of white strip in mm (y) */
+ SANE_FIX (0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 5, 5, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x14, 0x05, 0x12, 0x05, 0x17, 0x0c}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_CIS_LAMP /* Which flags are needed for this scanner? */
+ /* Tested by hmg. This scanner is a bit strange as it uses a CIS sensor but
+ it also has a lamp. So the lamp needs to be heated but CIS mode must be
+ used for scanning and calibration. There is no TA for that scanner */
+};
+
+static GT68xx_Model lexmark_x73_model = {
+ "lexmark-x73", /* Name */
+ "Lexmark", /* Device vendor string */
+ "X73", /* Device model name */
+ "OSLO3071b2.usb", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (6.519), /* Start of scan area in mm (x) */
+ SANE_FIX (12.615), /* Start of scan area in mm (y) */
+ SANE_FIX (220.0), /* Size of scan area in mm (x) */
+ SANE_FIX (297.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (1.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 32, 16, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x14, 0x07, 0x14, 0x07, 0x14, 0x07}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* When using automatic gain pictures are too dark. Only some ad hoc tests for
+ lexmark x70 were done so far. WARNING: Don't use the Full scan option
+ with the above settings, otherwise the sensor may bump at the end of
+ the sledge and the scanner may be damaged! */
+};
+
+static GT68xx_Model plustek_op1248u_model = {
+ "plustek-op1248u", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticPro 1248U", /* Device model name */
+ "ccd548.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &plustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (216.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x1e, 0x24, 0x1d, 0x1f, 0x1d, 0x20}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* tested */
+};
+
+static GT68xx_Model plustek_u16b_model = {
+ "plustek-u16b", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticPro U16B", /* Device model name */
+ "ccd68861.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (5.5), /* Start of scan area in mm (x) */
+ SANE_FIX (8.5), /* Start of scan area in mm (y) */
+ SANE_FIX (216.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x18, 0x16, 0x16, 0x0f, 0x17, 0x11}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_NO_POWER_STATUS |
+ GT68XX_FLAG_NO_LINEMODE
+ /* Which flags are needed for this scanner? */
+ /* Tested with a U16B by Henning Meier-Geinitz. 600 dpi is maximum
+ vertically. Line mode does not work. That's a hardware/firmware
+ issue. */
+};
+
+static GT68xx_Model plustek_ops12_model = {
+ "plustek-opticpro-s12", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticPro S12", /* Device model name */
+ "ccd548.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x1c, 0x29, 0x1c, 0x2c, 0x1c, 0x2b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* Seems to work */
+};
+
+static GT68xx_Model plustek_ops24_model = {
+ "plustek-opticpro-s24", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticPro S24", /* Device model name */
+ "ccd569.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 200, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 200, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.5), /* Start of scan area in mm (x) */
+ SANE_FIX (8.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x18, 0x1c, 0x16, 0x12, 0x18, 0x1c}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE
+ /* Which flags are needed for this scanner? */
+ /* Works (tested by Filip Kaluza). Based on genius Colorpage Vivid 1200 X. */
+};
+
+
+static GT68xx_Model genius_vivid4_model = {
+ "genius-colorpage-vivid4", /* Name */
+ "Genius", /* Device vendor string */
+ "ColorPage Vivid 4", /* Device model name */
+ "ccd68861.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (5.5), /* Start of scan area in mm (x) */
+ SANE_FIX (8.5), /* Start of scan area in mm (y) */
+ SANE_FIX (216.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x18, 0x16, 0x16, 0x0f, 0x17, 0x11}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_NO_POWER_STATUS |
+ GT68XX_FLAG_NO_LINEMODE
+ /* Which flags are needed for this scanner? */
+ /* This scanner seems to be very similar to Plustelk U16B and is reported to work. */
+};
+
+
+static GT68xx_Model genius_vivid3x_model = {
+ "genius-colorpage-vivid3x", /* Name */
+ "Genius", /* Device vendor string */
+ "Colorpage Vivid3x", /* Device model name */
+ "ccd548.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &plustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x1c, 0x29, 0x1c, 0x2c, 0x1c, 0x2b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* Tested to some degree, based on the Plustek OpticPro 1248U */
+};
+
+static GT68xx_Model genius_vivid4x_model = {
+ "genius-colorpage-vivid4x", /* Name */
+ "Genius", /* Device vendor string */
+ "Colorpage Vivid4x", /* Device model name */
+ "ccd548.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x1c, 0x29, 0x1c, 0x2c, 0x1c, 0x2b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* Is reported to work, copied from 3x, some values from Claudio Filho <filhocf@openoffice.org> */
+};
+
+static GT68xx_Model genius_vivid4xe_model = {
+ "genius-colorpage-vivid4xe", /* Name */
+ "Genius", /* Device vendor string */
+ "Colorpage Vivid4xe", /* Device model name */
+ "ccd548.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x1c, 0x29, 0x1c, 0x2c, 0x1c, 0x2b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* tested a bit */
+};
+
+static GT68xx_Model genius_vivid3xe_model = {
+ "genius-colorpage-vivid3xe", /* Name */
+ "Genius", /* Device vendor string */
+ "Colorpage Vivid3xe", /* Device model name */
+ "ccd548.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &plustek_gt6801_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 600, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 75, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (3.5), /* Start of scan area in mm (x) */
+ SANE_FIX (7.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x1c, 0x29, 0x1c, 0x2c, 0x1c, 0x2b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* mostly untested, based on the Genius Vivid3x */
+};
+
+static GT68xx_Model genius_vivid1200x_model = {
+ "genius-colorpage-vivid-1200-x", /* Name */
+ "Genius", /* Device vendor string */
+ "Colorpage Vivid 1200 X", /* Device model name */
+ "ccd569.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 200, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 200, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.5), /* Start of scan area in mm (x) */
+ SANE_FIX (8.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x18, 0x1c, 0x16, 0x12, 0x18, 0x1c}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE
+ /* Which flags are needed for this scanner? */
+ /* Tested. */
+};
+
+
+static GT68xx_Model genius_vivid1200xe_model = {
+ "genius-colorpage-vivid-1200-xe", /* Name */
+ "Genius", /* Device vendor string */
+ "Colorpage Vivid 1200 XE", /* Device model name */
+ "ccd569.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_TRUE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 200, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 200, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (4.5), /* Start of scan area in mm (x) */
+ SANE_FIX (8.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (1.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x18, 0x1c, 0x16, 0x12, 0x18, 0x1c}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */
+ /* Tested by hmg */
+};
+
+static GT68xx_Model iriscan_express_2_model = {
+ "iriscan-express-2", /* Name */
+ "Iris", /* Device vendor string */
+ "Iriscan Express 2", /* Device model name */
+ "cism216.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_sheetfed_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ /* values based on analyze of the firmware */
+ {600, 400, 300, 200, 150, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 400, 300, 200, 150, 100, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (18.0), /* Start of white strip in mm (y) */
+ SANE_FIX (21.72), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x22, 0x0c, 0x22, 0x0a, 0x24, 0x09}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_POWER_STATUS | GT68XX_FLAG_SHEET_FED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_HAS_CALIBRATE
+};
+
+
+static GT68xx_Model plustek_opticslim_m12_model = {
+ "plustek-opticslim-m12", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticSlim M12", /* Device model name */
+ "cism216.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_sheetfed_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 400, 300, 200, 150, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 400, 300, 200, 150, 100, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (18.0), /* Start of white strip in mm (y) */
+ SANE_FIX (21.72), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x24, 0x0a, 0x23, 0x0f, 0x23, 0x0b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_POWER_STATUS | GT68XX_FLAG_SHEET_FED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_HAS_CALIBRATE
+};
+
+static GT68xx_Model genius_sf600_model = {
+ "genius-SF600", /* Name */
+ "Genius", /* Device vendor string */
+ "ColorPage SF600", /* Device model name */
+ "cism216.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_sheetfed_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 200, 150, 100, 0}, /* possible x-resolutions */
+ {600, 300, 200, 150, 100, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (1.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (18.0), /* Start of white strip in mm (y) */
+ SANE_FIX (21.72), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
+ {0x24, 0x0a, 0x23, 0x0f, 0x23, 0x0b}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_NO_POWER_STATUS | GT68XX_FLAG_UNTESTED | GT68XX_FLAG_SHEET_FED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_HAS_CALIBRATE | GT68XX_FLAG_NO_STOP
+};
+
+/* Untested but should work according to Ryan Reading <ryanr23@gmail.com>. Based on Plustek M12 */
+
+static GT68xx_Model plustek_opticslim1200_model = {
+ "plustek-opticslim-1200", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticSlim 1200", /* Device model name */
+ "cism216.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 600, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 600, /* base x-res used to calculate geometry */
+ 600, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */
+ {16, 8, 0}, /* possible depths in gray mode */
+ {16, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (1.0), /* Start of scan area in mm (x) */
+ SANE_FIX (9.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (140.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x15, 0x09, 0x18, 0x11, 0x16, 0x0c}, /* Default offset/gain */
+ {0x157, 0x157, 0x157}, /* Default exposure parameters */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ 0
+};
+
+static GT68xx_Model plustek_opticslim2400_model = {
+ "plustek-opticslim-2400", /* Name */
+ "Plustek", /* Device vendor string */
+ "OpticSlim 2400", /* Device model name */
+ "cis3R5B1.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (1.0), /* Start of scan area in mm (x) */
+ SANE_FIX (9.5), /* Start of scan area in mm (y) */
+ SANE_FIX (217.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (0.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x15, 0x09, 0x18, 0x11, 0x16, 0x0c}, /* Default offset/gain */
+ {0x300, 0x300, 0x300}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ 0
+/* By Detlef Gausepohl <detlef@psych.rwth-aachen.de>. Fixed and tested by hmg. */
+};
+
+static GT68xx_Model visioneer_onetouch_7300_model = {
+ "visioneer-onetouch-7300", /* Name */
+ "Visioneer", /* Device vendor string */
+ "OneTouch 7300", /* Device model name */
+ "Cis3r5b1.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 1200, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
+ {12, 8, 0}, /* possible depths in gray mode */
+ {12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (1.0), /* Start of scan area in mm (x) */
+ SANE_FIX (9.5), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (140.0), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x15, 0x09, 0x18, 0x11, 0x16, 0x0c}, /* Default offset/gain */
+ {0x80, 0x80, 0x80}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ 0
+};
+
+/* Tested by Jason Novek. Based on Plustek OpticSlim 2400. */
+
+
+static GT68xx_Model genius_colorpageslim_1200_model = {
+ "genius-colorpageslim-1200", /* Name */
+ "Genius", /* Device vendor string */
+ "ColorPage Slim 1200", /* Device model name */
+ "Cis3r5b1.fw", /* Name of the firmware file */
+ SANE_FALSE, /* Dynamic allocation flag */
+
+ &mustek_gt6816_command_set, /* Command set used by this scanner */
+
+ 1200, /* maximum optical sensor resolution */
+ 2400, /* maximum motor resolution */
+ 1200, /* base x-res used to calculate geometry */
+ 1200, /* base y-res used to calculate geometry */
+ 1200, /* if ydpi is equal or higher, disable backtracking */
+ SANE_FALSE, /* Use base_ydpi for all resolutions */
+
+
+
+ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
+ {2400,1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
+ {16, 12, 8, 0}, /* possible depths in gray mode */
+ {16, 12, 8, 0}, /* possible depths in color mode */
+
+ SANE_FIX (0.5), /* Start of scan area in mm (x) */
+ SANE_FIX (8.0), /* Start of scan area in mm (y) */
+ SANE_FIX (218.0), /* Size of scan area in mm (x) */
+ SANE_FIX (299.0), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in mm (y) */
+ SANE_FIX (9.5), /* Start of black mark in mm (x) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
+
+ SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
+
+ 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
+ 0, /* CCD distcance for CCD with 6 lines) */
+
+ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
+ {0x19, 0x1a, 0x18, 0x14, 0x18, 0x12}, /* Default offset/gain */
+ {0x548, 0x513, 0x48d}, /* Default exposure parameters */
+ SANE_FIX (1.5), /* Default gamma value */
+
+ SANE_TRUE, /* Is this a CIS scanner? */
+ GT68XX_FLAG_ALWAYS_LINEMODE | GT68XX_FLAG_SE_2400
+};
+/* tested by Aleksey Nedorezov <aleksey at nedorezov.com> */
+
+
+
+
+static GT68xx_USB_Device_Entry gt68xx_usb_device_list[] = {
+ {0x10000, 0x10000, &unknown_model}, /* used for yet unknown scanners */
+ {0x055f, 0x0218, &mustek_2400ta_model},
+ {0x055f, 0x0219, &mustek_2400taplus_model},
+ {0x055f, 0x021c, &mustek_1200cuplus_model},
+ {0x055f, 0x021b, &mustek_1200cuplus2_model},
+ {0x055f, 0x021d, &mustek_2400cuplus_model},
+ {0x055f, 0x021e, &mustek_1200ta_model},
+ {0x055f, 0x021f, &mustek_scanexpress1248ub_model},
+ {0x05d8, 0x4002, &mustek_1200cu_model},
+ {0x05d8, 0x4002, &mustek_scanexpress1200ubplus_model}, /* manual override */
+ {0x05d8, 0x4002, &artec_ultima2000_model}, /* manual override */
+ {0x05d8, 0x4002, &mustek_2400cu_model}, /* manual override */
+ {0x05d8, 0x4002, &mustek_scanexpress2400usb_model}, /* manual override */
+ {0x055f, 0x0210, &mustek_a3usb_model},
+ {0x055f, 0x021a, &mustek_2448taplus_model},
+ {0x043d, 0x002d, &lexmark_x73_model},
+ {0x07b3, 0x0400, &plustek_op1248u_model},
+ {0x07b3, 0x0401, &plustek_op1248u_model}, /* Same scanner, different id? */
+ {0x07b3, 0x0402, &plustek_u16b_model},
+ {0x07b3, 0x0403, &plustek_u16b_model}, /* two ids? 403 seems to be more common */
+ {0x07b3, 0x040b, &plustek_ops12_model},
+ {0x07b3, 0x040e, &plustek_ops24_model},
+ {0x07b3, 0x0412, &plustek_opticslim_m12_model},
+ {0x07b3, 0x0413, &plustek_opticslim1200_model},
+ {0x07b3, 0x0422, &plustek_opticslim2400_model},
+ {0x07b3, 0x045f, &iriscan_express_2_model},
+ {0x0458, 0x2011, &genius_vivid3x_model},
+ {0x0458, 0x2014, &genius_vivid4_model},
+ {0x0458, 0x2017, &genius_vivid3xe_model},
+ {0x0458, 0x201a, &genius_vivid4xe_model},
+ {0x0458, 0x201b, &genius_vivid4x_model},
+ {0x0458, 0x201d, &genius_vivid1200x_model},
+ {0x0458, 0x201f, &genius_vivid1200xe_model},
+ {0x0458, 0x2021, &genius_sf600_model},
+ {0x04a7, 0x0444, &visioneer_onetouch_7300_model},
+ {0x0458, 0x201E, &genius_colorpageslim_1200_model},
+ {0, 0, NULL}
+};
diff --git a/backend/gt68xx_generic.c b/backend/gt68xx_generic.c
new file mode 100644
index 0000000..f5e5bb6
--- /dev/null
+++ b/backend/gt68xx_generic.c
@@ -0,0 +1,679 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2005-2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ * @brief GT68xx commands common for most GT68xx-based scanners.
+ */
+
+#include "gt68xx_generic.h"
+
+
+SANE_Status
+gt68xx_generic_move_relative (GT68xx_Device * dev, SANE_Int distance)
+{
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ if (distance >= 0)
+ req[0] = 0x14;
+ else
+ {
+ req[0] = 0x15;
+ distance = -distance;
+ }
+ req[1] = 0x01;
+ req[2] = LOBYTE (distance);
+ req[3] = HIBYTE (distance);
+
+ return gt68xx_device_req (dev, req, req);
+}
+
+SANE_Status
+gt68xx_generic_start_scan (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x43;
+ req[1] = 0x01;
+ RIE (gt68xx_device_req (dev, req, req));
+ RIE (gt68xx_device_check_result (req, 0x43));
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_generic_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x35;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ *ready = SANE_FALSE;
+ if (req[0] == 0)
+ *ready = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Byte
+gt68xx_generic_fix_gain (SANE_Int gain)
+{
+ if (gain < 0)
+ gain = 0;
+ else if (gain > 31)
+ gain += 12;
+ else if (gain > 51)
+ gain = 63;
+
+ return gain;
+}
+
+static SANE_Byte
+gt68xx_generic_fix_offset (SANE_Int offset)
+{
+ if (offset < 0)
+ offset = 0;
+ else if (offset > 63)
+ offset = 63;
+ return offset;
+}
+
+SANE_Status
+gt68xx_generic_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params)
+{
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x22;
+ req[1] = 0x01;
+ req[2] = gt68xx_generic_fix_offset (params->r_offset);
+ req[3] = gt68xx_generic_fix_gain (params->r_pga);
+ req[4] = gt68xx_generic_fix_offset (params->g_offset);
+ req[5] = gt68xx_generic_fix_gain (params->g_pga);
+ req[6] = gt68xx_generic_fix_offset (params->b_offset);
+ req[7] = gt68xx_generic_fix_gain (params->b_pga);
+
+ DBG (6,
+ "gt68xx_generic_set_afe: real AFE: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ req[2], req[3], req[4], req[5], req[6], req[7]);
+ return gt68xx_device_req (dev, req, req);
+}
+
+SANE_Status
+gt68xx_generic_set_exposure_time (GT68xx_Device * dev,
+ GT68xx_Exposure_Parameters * params)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x76;
+ req[1] = 0x01;
+ req[2] = req[6] = req[10] = 0x04;
+ req[4] = LOBYTE (params->r_time);
+ req[5] = HIBYTE (params->r_time);
+ req[8] = LOBYTE (params->g_time);
+ req[9] = HIBYTE (params->g_time);
+ req[12] = LOBYTE (params->b_time);
+ req[13] = HIBYTE (params->b_time);
+
+ DBG (6, "gt68xx_generic_set_exposure_time: 0x%03x 0x%03x 0x%03x\n",
+ params->r_time, params->g_time, params->b_time);
+
+ RIE (gt68xx_device_req (dev, req, req));
+ RIE (gt68xx_device_check_result (req, 0x76));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_generic_get_id (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x2e;
+ req[1] = 0x01;
+ RIE (gt68xx_device_req (dev, req, req));
+ RIE (gt68xx_device_check_result (req, 0x2e));
+
+ DBG (2,
+ "get_id: vendor id=0x%04X, product id=0x%04X, DID=0x%08X, FID=0x%04X\n",
+ req[2] + (req[3] << 8), req[4] + (req[5] << 8),
+ req[6] + (req[7] << 8) + (req[8] << 16) + (req[9] << 24),
+ req[10] + (req[11] << 8));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_generic_paperfeed (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x83;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+ return SANE_STATUS_GOOD;
+}
+
+#define MAX_PIXEL_MODE 15600
+
+SANE_Status
+gt68xx_generic_setup_scan (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params)
+{
+ SANE_Status status;
+ GT68xx_Model *model;
+ SANE_Int xdpi, ydpi;
+ SANE_Bool color;
+ SANE_Int depth;
+ SANE_Int pixel_x0, pixel_y0, pixel_xs, pixel_ys;
+ SANE_Int pixel_align;
+
+ SANE_Int abs_x0, abs_y0, abs_xs, abs_ys, base_xdpi, base_ydpi;
+ SANE_Int scan_xs, scan_ys, scan_bpl;
+ SANE_Int bits_per_line;
+ SANE_Byte color_mode_code;
+ SANE_Bool line_mode;
+ SANE_Int overscan_lines;
+ SANE_Fixed x0, y0, xs, ys;
+ SANE_Bool backtrack = SANE_FALSE;
+
+ DBG (6, "gt6816_setup_scan: enter (action=%s)\n",
+ action == SA_CALIBRATE ? "calibrate" :
+ action == SA_CALIBRATE_ONE_LINE ? "calibrate one line" :
+ action == SA_SCAN ? "scan" : "calculate only");
+
+ model = dev->model;
+
+ xdpi = request->xdpi;
+ ydpi = request->ydpi;
+ color = request->color;
+ depth = request->depth;
+
+ base_xdpi = model->base_xdpi;
+ base_ydpi = model->base_ydpi;
+
+ if (xdpi > model->base_xdpi)
+ base_xdpi = model->optical_xdpi;
+
+ /* Special fixes */
+ if ((dev->model->flags & GT68XX_FLAG_USE_OPTICAL_X) && xdpi <= 50)
+ base_xdpi = model->optical_xdpi;
+
+ if ((dev->model->flags & GT68XX_FLAG_SCAN_FROM_HOME) &&
+ !request->use_ta && action == SA_SCAN)
+ request->mbs = SANE_TRUE;
+
+ if (!model->constant_ydpi)
+ {
+ if (ydpi > model->base_ydpi)
+ base_ydpi = model->optical_ydpi;
+ }
+
+ DBG (6, "gt68xx_generic_setup_scan: base_xdpi=%d, base_ydpi=%d\n",
+ base_xdpi, base_ydpi);
+
+ switch (action)
+ {
+ case SA_CALIBRATE_ONE_LINE:
+ {
+ x0 = request->x0;
+ if (request->use_ta)
+ y0 = model->y_offset_calib_ta;
+ else
+ y0 = model->y_offset_calib;
+ ys = SANE_FIX (1.0 * MM_PER_INCH / ydpi); /* one line */
+ xs = request->xs;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE:
+ {
+ if (request->use_ta)
+ {
+ if (dev->model->flags & GT68XX_FLAG_MIRROR_X)
+ x0 = request->x0 - model->x_offset_ta;
+ else
+ x0 = request->x0 + model->x_offset_ta;
+ if (request->mbs)
+ y0 = model->y_offset_calib_ta;
+ else
+ y0 = 0;
+ }
+ else
+ {
+ if (dev->model->flags & GT68XX_FLAG_MIRROR_X)
+ x0 = request->x0 - model->x_offset;
+ else
+ x0 = request->x0 + model->x_offset;
+ if (request->mbs)
+ y0 = model->y_offset_calib;
+ else
+ y0 = 0;
+ }
+ ys = SANE_FIX (CALIBRATION_HEIGHT);
+ xs = request->xs;
+ break;
+ }
+ case SA_SCAN:
+ {
+ SANE_Fixed x_offset, y_offset;
+
+ if (strcmp (dev->model->command_set->name, "mustek-gt6816") != 0)
+ request->mbs = SANE_TRUE; /* always go home for gt6801 scanners */
+ if (request->use_ta)
+ {
+ x_offset = model->x_offset_ta;
+ if (request->mbs)
+ y_offset = model->y_offset_ta;
+ else
+ {
+ y_offset = model->y_offset_ta - model->y_offset_calib_ta
+ - SANE_FIX (CALIBRATION_HEIGHT);
+ if ((request->y0 + y_offset) < 0)
+ {
+ y_offset = model->y_offset_ta;
+ request->mbs = SANE_TRUE;
+ }
+ }
+
+ }
+ else
+ {
+ x_offset = model->x_offset;
+ if (request->mbs)
+ y_offset = model->y_offset;
+ else
+ {
+ y_offset = model->y_offset - model->y_offset_calib
+ - SANE_FIX (CALIBRATION_HEIGHT);
+ if ((request->y0 + y_offset) < 0)
+ {
+ y_offset = model->y_offset;
+ request->mbs = SANE_TRUE;
+ }
+ }
+
+ }
+ if (dev->model->flags & GT68XX_FLAG_MIRROR_X)
+ x0 = request->x0 - x_offset;
+ else
+ x0 = request->x0 + x_offset;
+ y0 = request->y0 + y_offset;
+ if (y0 < 0)
+ y0 = 0;
+ ys = request->ys;
+ xs = request->xs;
+ backtrack = request->backtrack;
+ break;
+ }
+
+ default:
+ DBG (1, "gt68xx_generic_setup_scan: invalid action=%d\n", (int) action);
+ return SANE_STATUS_INVAL;
+ }
+
+ pixel_x0 = SANE_UNFIX (x0) * xdpi / MM_PER_INCH + 0.5;
+ pixel_y0 = SANE_UNFIX (y0) * ydpi / MM_PER_INCH + 0.5;
+ pixel_ys = SANE_UNFIX (ys) * ydpi / MM_PER_INCH + 0.5;
+ pixel_xs = SANE_UNFIX (xs) * xdpi / MM_PER_INCH + 0.5;
+
+
+ DBG (6, "gt68xx_generic_setup_scan: xdpi=%d, ydpi=%d\n", xdpi, ydpi);
+ DBG (6, "gt68xx_generic_setup_scan: color=%s, depth=%d\n",
+ color ? "TRUE" : "FALSE", depth);
+ DBG (6, "gt68xx_generic_setup_scan: pixel_x0=%d, pixel_y0=%d\n",
+ pixel_x0, pixel_y0);
+ DBG (6, "gt68xx_generic_setup_scan: pixel_xs=%d, pixel_ys=%d\n",
+ pixel_xs, pixel_ys);
+
+
+ color_mode_code = 0x80;
+ if (color)
+ color_mode_code |= (1 << 2);
+ else
+ color_mode_code |= dev->gray_mode_color;
+
+ if (depth > 12)
+ color_mode_code |= (1 << 5);
+ else if (depth > 8)
+ {
+ color_mode_code &= 0x7f;
+ color_mode_code |= (1 << 4);
+ }
+
+ DBG (6, "gt68xx_generic_setup_scan: color_mode_code = 0x%02X\n",
+ color_mode_code);
+
+ overscan_lines = 0;
+ params->ld_shift_r = params->ld_shift_g = params->ld_shift_b = 0;
+ params->ld_shift_double = 0;
+
+ /* Line distance correction is required for color scans. */
+ if (action == SA_SCAN && color)
+ {
+ SANE_Int optical_ydpi = model->optical_ydpi;
+ SANE_Int ld_shift_r = model->ld_shift_r;
+ SANE_Int ld_shift_g = model->ld_shift_g;
+ SANE_Int ld_shift_b = model->ld_shift_b;
+ SANE_Int max_ld = MAX (MAX (ld_shift_r, ld_shift_g), ld_shift_b);
+
+ overscan_lines = max_ld * ydpi / optical_ydpi;
+ params->ld_shift_r = ld_shift_r * ydpi / optical_ydpi;
+ params->ld_shift_g = ld_shift_g * ydpi / optical_ydpi;
+ params->ld_shift_b = ld_shift_b * ydpi / optical_ydpi;
+ params->ld_shift_double = 0;
+ DBG (6, "gt68xx_generic_setup_scan: overscan=%d, ld=%d/%d/%d\n",
+ overscan_lines, params->ld_shift_r, params->ld_shift_g,
+ params->ld_shift_b);
+ }
+
+ /* Used for CCD scanners with 6 instead of 3 CCD lines */
+ if (action == SA_SCAN && xdpi >= model->optical_xdpi
+ && model->ld_shift_double > 0)
+ {
+ params->ld_shift_double =
+ model->ld_shift_double * ydpi / model->optical_ydpi;
+ if (color)
+ overscan_lines += (params->ld_shift_double * 3);
+ else
+ overscan_lines += params->ld_shift_double;
+
+ DBG (6, "gt68xx_generic_setup_scan: overscan=%d, ld double=%d\n",
+ overscan_lines, params->ld_shift_double);
+ }
+
+ abs_x0 = pixel_x0 * base_xdpi / xdpi;
+ abs_y0 = pixel_y0 * base_ydpi / ydpi;
+ DBG (6, "gt68xx_generic_setup_scan: abs_x0=%d, abs_y0=%d\n", abs_x0,
+ abs_y0);
+
+ params->double_column = abs_x0 & 1;
+
+ /* Calculate minimum number of pixels which span an integral multiple of 64
+ * bytes. */
+ pixel_align = 32; /* best case for depth = 16 */
+ while ((depth * pixel_align) % (64 * 8) != 0)
+ pixel_align *= 2;
+ DBG (6, "gt68xx_generic_setup_scan: pixel_align=%d\n", pixel_align);
+
+ if (pixel_xs % pixel_align == 0)
+ scan_xs = pixel_xs;
+ else
+ scan_xs = (pixel_xs / pixel_align + 1) * pixel_align;
+ scan_ys = pixel_ys + overscan_lines;
+
+ if ((xdpi != base_xdpi)
+ && (strcmp (dev->model->command_set->name, "mustek-gt6816") != 0))
+ abs_xs = (scan_xs - 1) * base_xdpi / xdpi; /* gt6801 */
+ else
+ abs_xs = scan_xs * base_xdpi / xdpi; /* gt6816 */
+
+ if (action == SA_CALIBRATE_ONE_LINE)
+ abs_ys = 2;
+ else
+ abs_ys = scan_ys * base_ydpi / ydpi;
+ DBG (6, "gt68xx_generic_setup_scan: abs_xs=%d, abs_ys=%d\n", abs_xs,
+ abs_ys);
+
+ if (model->flags & GT68XX_FLAG_NO_LINEMODE)
+ {
+ line_mode = SANE_FALSE;
+ DBG (6,
+ "gt68xx_generic_setup_scan: using pixel mode (GT68XX_FLAG_NO_LINEMODE)\n");
+ }
+ else if (model->is_cis && !(model->flags & GT68XX_FLAG_CIS_LAMP))
+ {
+ line_mode = SANE_TRUE;
+ DBG (6, "gt68xx_generic_setup_scan: using line mode (CIS)\n");
+ }
+ else if (model->flags & GT68XX_FLAG_ALWAYS_LINEMODE)
+ {
+ line_mode = SANE_TRUE;
+ DBG (6,
+ "gt68xx_generic_setup_scan: using line mode (GT68XX_FLAG_ALWAYS_LINEMODE)\n");
+ }
+ else
+ {
+ SANE_Int max_bpl = xdpi * 3 * depth *
+ (SANE_UNFIX (model->x_size) -
+ SANE_UNFIX (model->x_offset)) / MM_PER_INCH / 8;
+
+ line_mode = SANE_FALSE;
+ if (!color)
+ {
+ DBG (6,
+ "gt68xx_generic_setup_scan: using line mode for monochrome scan\n");
+ line_mode = SANE_TRUE;
+ }
+ else if (max_bpl > MAX_PIXEL_MODE)
+ {
+ DBG (6,
+ "gt68xx_generic_setup_scan: max_bpl = %d > %d: forcing line mode\n",
+ max_bpl, MAX_PIXEL_MODE);
+ line_mode = SANE_TRUE;
+ }
+ else
+ DBG (6,
+ "gt68xx_generic_setup_scan: max_bpl = %d <= %d: using pixel mode\n",
+ max_bpl, MAX_PIXEL_MODE);
+ }
+
+ bits_per_line = depth * scan_xs;
+
+ if (color && !line_mode)
+ bits_per_line *= 3;
+
+ if (bits_per_line % 8) /* impossible */
+ {
+ DBG (0, "gt68xx_generic_setup_scan: BUG: unaligned bits_per_line=%d\n",
+ bits_per_line);
+ return SANE_STATUS_INVAL;
+ }
+ scan_bpl = bits_per_line / 8;
+
+ if (scan_bpl % 64) /* impossible */
+ {
+ DBG (0, "gt68xx_generic_setup_scan: BUG: unaligned scan_bpl=%d\n",
+ scan_bpl);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (color)
+ if (line_mode || dev->model->flags & GT68XX_FLAG_SE_2400)
+ scan_ys *= 3;
+
+ DBG (6, "gt68xx_generic_setup_scan: scan_xs=%d, scan_ys=%d\n", scan_xs,
+ scan_ys);
+
+ DBG (6, "gt68xx_generic_setup_scan: scan_bpl=%d\n", scan_bpl);
+
+ if (!request->calculate)
+ {
+ GT68xx_Packet req;
+ SANE_Byte motor_mode_1, motor_mode_2;
+
+ if (scan_bpl > (16 * 1024))
+ {
+ DBG (0, "gt68xx_generic_setup_scan: scan_bpl=%d, too large\n",
+ scan_bpl);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if ((dev->model->flags & GT68XX_FLAG_NO_LINEMODE) && line_mode && color)
+ {
+ DBG (0,
+ "gt68xx_generic_setup_scan: the scanner's memory is too small for "
+ "that combination of resolution, dpi and width\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (6, "gt68xx_generic_setup_scan: backtrack=%d\n", backtrack);
+
+ motor_mode_1 = (request->mbs ? 0 : 1) << 1;
+ motor_mode_1 |= (request->mds ? 0 : 1) << 2;
+ motor_mode_1 |= (request->mas ? 0 : 1) << 0;
+ motor_mode_1 |= (backtrack ? 1 : 0) << 3;
+
+ motor_mode_2 = (request->lamp ? 0 : 1) << 0;
+ motor_mode_2 |= (line_mode ? 0 : 1) << 2;
+
+ if ((action != SA_SCAN)
+ && (strcmp (dev->model->command_set->name, "mustek-gt6816") == 0))
+ motor_mode_2 |= 1 << 3;
+
+ DBG (6,
+ "gt68xx_generic_setup_scan: motor_mode_1 = 0x%02X, motor_mode_2 = 0x%02X\n",
+ motor_mode_1, motor_mode_2);
+
+ /* Fill in the setup command */
+ memset (req, 0, sizeof (req));
+ req[0x00] = 0x20;
+ req[0x01] = 0x01;
+ req[0x02] = LOBYTE (abs_y0);
+ req[0x03] = HIBYTE (abs_y0);
+ req[0x04] = LOBYTE (abs_ys);
+ req[0x05] = HIBYTE (abs_ys);
+ req[0x06] = LOBYTE (abs_x0);
+ req[0x07] = HIBYTE (abs_x0);
+ req[0x08] = LOBYTE (abs_xs);
+ req[0x09] = HIBYTE (abs_xs);
+ req[0x0a] = color_mode_code;
+ if (model->is_cis && !(model->flags & GT68XX_FLAG_CIS_LAMP))
+ req[0x0b] = 0x60;
+ else
+ req[0x0b] = 0x20;
+ req[0x0c] = LOBYTE (xdpi);
+ req[0x0d] = HIBYTE (xdpi);
+ req[0x0e] = 0x12; /* ??? 0x12 */
+ req[0x0f] = 0x00; /* ??? 0x00 */
+ req[0x10] = LOBYTE (scan_bpl);
+ req[0x11] = HIBYTE (scan_bpl);
+ req[0x12] = LOBYTE (scan_ys);
+ req[0x13] = HIBYTE (scan_ys);
+ req[0x14] = motor_mode_1;
+ req[0x15] = motor_mode_2;
+ req[0x16] = LOBYTE (ydpi);
+ req[0x17] = HIBYTE (ydpi);
+ if (backtrack)
+ req[0x18] = request->backtrack_lines;
+ else
+ req[0x18] = 0x00;
+
+ status = gt68xx_device_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_generic_setup_scan: setup request failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ RIE (gt68xx_device_check_result (req, 0x20));
+ }
+
+ /* Fill in calculated values */
+ params->xdpi = xdpi;
+ params->ydpi = ydpi;
+ params->depth = depth;
+ params->color = color;
+ params->pixel_xs = pixel_xs;
+ params->pixel_ys = pixel_ys;
+ params->scan_xs = scan_xs;
+ params->scan_ys = scan_ys;
+ params->scan_bpl = scan_bpl;
+ params->line_mode = line_mode;
+ params->overscan_lines = overscan_lines;
+ params->pixel_x0 = pixel_x0;
+
+ DBG (6, "gt68xx_generic_setup_scan: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_generic_move_paper (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+ SANE_Int ydpi;
+ SANE_Int pixel_y0;
+ SANE_Int abs_y0, base_ydpi;
+ GT68xx_Model *model = dev->model;
+
+ ydpi = request->ydpi;
+ base_ydpi = model->base_ydpi;
+
+ if (ydpi > model->base_ydpi)
+ ydpi = base_ydpi;
+
+ pixel_y0 =
+ SANE_UNFIX ((request->y0 + model->y_offset)) * ydpi / MM_PER_INCH + 0.5;
+ abs_y0 = pixel_y0 * base_ydpi / ydpi;
+
+ DBG (6, "gt68xx_generic_move_paper: base_ydpi=%d\n", base_ydpi);
+ DBG (6, "gt68xx_generic_move_paper: ydpi=%d\n", ydpi);
+ DBG (6, "gt68xx_generic_move_paper: abs_y0=%d\n", abs_y0);
+
+ /* paper move request */
+ memset (req, 0, sizeof (req));
+ req[0] = 0x82;
+ req[1] = 0x01;
+ req[2] = LOBYTE (abs_y0);
+ req[3] = HIBYTE (abs_y0);
+ RIE (gt68xx_device_req (dev, req, req));
+
+ DBG (6, "gt68xx_generic_move_paper: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/gt68xx_generic.h b/backend/gt68xx_generic.h
new file mode 100644
index 0000000..01ecff9
--- /dev/null
+++ b/backend/gt68xx_generic.h
@@ -0,0 +1,75 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_GENERIC_H
+#define GT68XX_GENERIC_H
+
+#include "gt68xx_low.h"
+
+static SANE_Status
+gt68xx_generic_move_relative (GT68xx_Device * dev, SANE_Int distance);
+
+static SANE_Status gt68xx_generic_start_scan (GT68xx_Device * dev);
+
+static SANE_Status
+gt68xx_generic_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready);
+
+static SANE_Status
+gt68xx_generic_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params);
+
+static SANE_Status
+gt68xx_generic_set_exposure_time (GT68xx_Device * dev,
+ GT68xx_Exposure_Parameters * params);
+static SANE_Status gt68xx_generic_get_id (GT68xx_Device * dev);
+static SANE_Status gt68xx_generic_paperfeed (GT68xx_Device * dev);
+
+static SANE_Status
+gt68xx_generic_setup_scan (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params);
+static SANE_Status
+gt68xx_generic_move_paper (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request);
+
+#endif /* not GT68XX_GENERIC_H */
diff --git a/backend/gt68xx_gt6801.c b/backend/gt68xx_gt6801.c
new file mode 100644
index 0000000..d61729c
--- /dev/null
+++ b/backend/gt68xx_gt6801.c
@@ -0,0 +1,278 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ GT6801 support by Andreas Nowack <nowack.andreas@gmx.de>
+ Copyright (C) 2002-2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ * @brief Implementation of the GT6801 specific functions.
+ */
+
+#include "gt68xx_gt6801.h"
+
+/* doesn't work with plustek scanner */
+SANE_Status
+gt6801_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x50;
+ req[1] = 0x01;
+ req[2] = 0x80;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ if (req[0] == 0x00 && req[1] == 0x50)
+ *loaded = SANE_TRUE;
+ else
+ *loaded = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* doesn't work with at least cytron scanner */
+SANE_Status
+gt6801_check_plustek_firmware (GT68xx_Device * dev, SANE_Bool * loaded)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+
+ req[0] = 0x73;
+ req[1] = 0x01;
+
+ status = gt68xx_device_small_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* Assume that firmware is not loaded */
+ *loaded = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* check for correct answer */
+ if ((req[0] == 0) && (req[1] == 0x12) && (req[3] == 0x80))
+ /* req[3] is 0 if fw is not loaded, if loaded it's 0x80
+ * at least on my 1284u (gerhard@gjaeger.de)
+ *
+ * With the Genius scanners req[3] is 0x02/0x82. (hmg)
+ */
+ *loaded = SANE_TRUE;
+ else
+ *loaded = SANE_FALSE;
+
+ /* Until I find out if testing for req[3] & 0x80 is save, load the firmware
+ everytime */
+ *loaded = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+#define MAX_DOWNLOAD_BLOCK_SIZE 64
+
+SANE_Status
+gt6801_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size)
+{
+ SANE_Status status;
+ SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte *block;
+ SANE_Word addr, bytes_left;
+ GT68xx_Packet boot_req;
+ SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE;
+
+ CHECK_DEV_ACTIVE (dev, "gt6801_download_firmware");
+
+ for (addr = 0; addr < size; addr += block_size)
+ {
+ bytes_left = size - addr;
+ if (bytes_left > block_size)
+ block = data + addr;
+ else
+ {
+ memset (download_buf, 0, block_size);
+ memcpy (download_buf, data + addr, bytes_left);
+ block = download_buf;
+ }
+ RIE (gt68xx_device_memory_write (dev, addr, block_size, block));
+ RIE (gt68xx_device_memory_read (dev, 0x3f00, block_size, check_buf));
+
+ /*
+ * For GT6816 this was:
+ * if (memcmp (block, check_buf, block_size) != 0) ...
+ * Apparently the GT6801 does something different...
+ *
+ * hmg: For my BP 1200 CU the result is 00 09 so maybe only the 0 is
+ * relevant?
+ */
+ if ((check_buf[0] != 0) && (check_buf[1] != 0x40))
+ {
+ DBG (3, "gt6801_download_firmware: mismatch at block 0x%0x\n",
+ addr);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ memset (boot_req, 0, sizeof (boot_req));
+ boot_req[0] = 0x69;
+ boot_req[1] = 0x01;
+ boot_req[2] = 0xc0;
+ boot_req[3] = 0x1c;
+ RIE (gt68xx_device_req (dev, boot_req, boot_req));
+
+#if 0
+ /* hmg: the following isn't in my log: */
+ memset (boot_req, 0, sizeof (boot_req)); /* I don't know if this is needed */
+ boot_req[0] = 0x01;
+ boot_req[1] = 0x01;
+ RIE (gt68xx_device_small_req (dev, boot_req, boot_req));
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt6801_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x10;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ /* I don't know what power_ok = SANE_FALSE looks like... */
+ /* hmg: let's assume it's different from the usual 00 10 */
+ if (gt68xx_device_check_result (req, 0x10) == SANE_STATUS_GOOD)
+ *power_ok = SANE_TRUE;
+ else
+ *power_ok = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+gt6801_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp)
+{
+ if (!dev->model->is_cis)
+ {
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x25;
+ req[1] = 0x01;
+ req[2] = 0;
+ if (fb_lamp)
+ req[2] |= 0x01;
+ if (ta_lamp)
+ req[2] |= 0x02;
+ return gt68xx_device_req (dev, req, req);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+gt6801_is_moving (GT68xx_Device * dev, SANE_Bool * moving)
+{
+ /* this seems not to be supported by the scanner */
+ (void) dev;
+
+ *moving = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+gt6801_carriage_home (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+
+ memset (req, 0, sizeof (req));
+
+ if (dev->model->flags & GT68XX_FLAG_MOTOR_HOME)
+ {
+ req[0] = 0x34;
+ req[1] = 0x01;
+ status = gt68xx_device_req (dev, req, req);
+ }
+ else
+ {
+ req[0] = 0x12;
+ req[1] = 0x01;
+ if ((status = gt68xx_device_req (dev, req, req)) == SANE_STATUS_GOOD)
+ {
+ RIE (gt68xx_device_check_result (req, 0x12));
+ memset (req, 0, sizeof (req));
+ req[0] = 0x24;
+ req[1] = 0x01;
+ status = gt68xx_device_req (dev, req, req);
+ RIE (gt68xx_device_check_result (req, 0x24));
+ }
+ }
+ return status;
+}
+
+
+SANE_Status
+gt6801_stop_scan (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+ SANE_Status status;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x42;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+ RIE (gt68xx_device_check_result (req, 0x42));
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/gt68xx_gt6801.h b/backend/gt68xx_gt6801.h
new file mode 100644
index 0000000..750ce39
--- /dev/null
+++ b/backend/gt68xx_gt6801.h
@@ -0,0 +1,71 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ GT6801 support by Andreas Nowack <nowack.andreas@gmx.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_GT6801_H
+#define GT68XX_GT6801_H
+
+static SANE_Status
+gt6801_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded);
+
+static SANE_Status
+gt6801_check_plustek_firmware (GT68xx_Device * dev, SANE_Bool * loaded);
+
+static SANE_Status
+gt6801_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size);
+
+static SANE_Status
+gt6801_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok);
+
+static SANE_Status
+gt6801_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp);
+
+static SANE_Status gt6801_is_moving (GT68xx_Device * dev, SANE_Bool * moving);
+
+static SANE_Status gt6801_carriage_home (GT68xx_Device * dev);
+
+static SANE_Status gt6801_stop_scan (GT68xx_Device * dev);
+
+#endif /* not GT68XX_GT6801_H */
diff --git a/backend/gt68xx_gt6816.c b/backend/gt68xx_gt6816.c
new file mode 100644
index 0000000..045194a
--- /dev/null
+++ b/backend/gt68xx_gt6816.c
@@ -0,0 +1,263 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002-2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ * @brief Implementation of the gt6816 specific functions.
+ */
+
+#include "gt68xx_gt6816.h"
+
+SANE_Status
+gt6816_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x70;
+ req[1] = 0x01;
+
+ status = gt68xx_device_small_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* Assume that firmware is not loaded because without firmware, we need
+ 64 bytes for the result, not 8 */
+ *loaded = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+ }
+ /* check anyway */
+ if (req[0] == 0x00 && req[1] == 0x70 && req[2] == 0xff)
+ *loaded = SANE_TRUE;
+ else
+ *loaded = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+#define MAX_DOWNLOAD_BLOCK_SIZE 64
+
+SANE_Status
+gt6816_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size)
+{
+ SANE_Status status;
+ SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte *block;
+ SANE_Word addr, bytes_left;
+ GT68xx_Packet boot_req;
+ SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE;
+
+ CHECK_DEV_ACTIVE (dev, "gt6816_download_firmware");
+
+ for (addr = 0; addr < size; addr += block_size)
+ {
+ bytes_left = size - addr;
+ if (bytes_left > block_size)
+ block = data + addr;
+ else
+ {
+ memset (download_buf, 0, block_size);
+ memcpy (download_buf, data + addr, bytes_left);
+ block = download_buf;
+ }
+ RIE (gt68xx_device_memory_write (dev, addr, block_size, block));
+ RIE (gt68xx_device_memory_read (dev, addr, block_size, check_buf));
+ if (memcmp (block, check_buf, block_size) != 0)
+ {
+ DBG (3, "gt6816_download_firmware: mismatch at block 0x%0x\n",
+ addr);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ memset (boot_req, 0, sizeof (boot_req));
+ boot_req[0] = 0x69;
+ boot_req[1] = 0x01;
+ boot_req[2] = LOBYTE (addr);
+ boot_req[3] = HIBYTE (addr);
+ RIE (gt68xx_device_req (dev, boot_req, boot_req));
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+gt6816_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x3f;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ if ((req[0] == 0x00 && req[1] == 0x3f && req[2] == 0x01)
+ || (dev->model->flags & GT68XX_FLAG_NO_POWER_STATUS))
+ *power_ok = SANE_TRUE;
+ else
+ *power_ok = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt6816_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x28;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ if (req[0] == 0x00 && req[1] == 0x28 && (req[8] & 0x01) != 0
+ && !dev->model->is_cis)
+ *ta_attached = SANE_TRUE;
+ else
+ *ta_attached = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+gt6816_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp)
+{
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x25;
+ req[1] = 0x01;
+ req[2] = 0;
+ if (fb_lamp)
+ req[2] |= 0x01;
+ if (ta_lamp)
+ req[2] |= 0x02;
+
+ return gt68xx_device_req (dev, req, req);
+}
+
+
+SANE_Status
+gt6816_is_moving (GT68xx_Device * dev, SANE_Bool * moving)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x17;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ if (req[0] == 0x00 && req[1] == 0x17)
+ {
+ if (req[2] == 0 && (req[3] == 0 || req[3] == 2))
+ *moving = SANE_FALSE;
+ else
+ *moving = SANE_TRUE;
+ }
+ else
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+gt6816_carriage_home (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x24;
+ req[1] = 0x01;
+
+ return gt68xx_device_req (dev, req, req);
+}
+
+
+SANE_Status
+gt6816_stop_scan (GT68xx_Device * dev)
+{
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x41;
+ req[1] = 0x01;
+
+ return gt68xx_device_small_req (dev, req, req);
+}
+
+SANE_Status
+gt6816_document_present (GT68xx_Device * dev, SANE_Bool * present)
+{
+ SANE_Status status;
+ GT68xx_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x59;
+ req[1] = 0x01;
+
+ RIE (gt68xx_device_req (dev, req, req));
+
+ if (req[0] == 0x00 && req[1] == 0x59)
+ {
+ if (req[2] == 0)
+ *present = SANE_FALSE;
+ else
+ *present = SANE_TRUE;
+ }
+ else
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/gt68xx_gt6816.h b/backend/gt68xx_gt6816.h
new file mode 100644
index 0000000..7b72071
--- /dev/null
+++ b/backend/gt68xx_gt6816.h
@@ -0,0 +1,72 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_GT6816_H
+#define GT68XX_GT6816_H
+
+static SANE_Status
+gt6816_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded);
+
+static SANE_Status
+gt6816_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size);
+
+static SANE_Status
+gt6816_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok);
+
+static SANE_Status
+gt6816_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached);
+
+static SANE_Status
+gt6816_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp);
+
+static SANE_Status gt6816_is_moving (GT68xx_Device * dev, SANE_Bool * moving);
+
+static SANE_Status gt6816_carriage_home (GT68xx_Device * dev);
+
+static SANE_Status gt6816_stop_scan (GT68xx_Device * dev);
+
+static SANE_Status gt6816_document_present (GT68xx_Device * dev, SANE_Bool * present);
+
+#endif /* not GT68XX_GT6816_H */
diff --git a/backend/gt68xx_high.c b/backend/gt68xx_high.c
new file mode 100644
index 0000000..25885b0
--- /dev/null
+++ b/backend/gt68xx_high.c
@@ -0,0 +1,2744 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ AFE offset/gain setting by David Stevenson <david.stevenson@zoom.co.uk>
+ Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
+ Copyright (C) 2009 Stéphane Voltz <stef.dev@free.fr> for sheetfed
+ calibration code.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "gt68xx_high.h"
+#include "gt68xx_mid.c"
+
+#include <unistd.h>
+#include <math.h>
+
+static SANE_Status
+gt68xx_afe_ccd_auto (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request);
+static SANE_Status gt68xx_afe_cis_auto (GT68xx_Scanner * scanner);
+
+SANE_Status
+gt68xx_calibrator_new (SANE_Int width,
+ SANE_Int white_level, GT68xx_Calibrator ** cal_return)
+{
+ GT68xx_Calibrator *cal;
+ SANE_Int i;
+
+ DBG (4, "gt68xx_calibrator_new: enter: width=%d, white_level=%d\n",
+ width, white_level);
+
+ *cal_return = 0;
+
+ if (width <= 0)
+ {
+ DBG (5, "gt68xx_calibrator_new: invalid width=%d\n", width);
+ return SANE_STATUS_INVAL;
+ }
+
+ cal = (GT68xx_Calibrator *) malloc (sizeof (GT68xx_Calibrator));
+ if (!cal)
+ {
+ DBG (5, "gt68xx_calibrator_new: no memory for GT68xx_Calibrator\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ cal->k_white = NULL;
+ cal->k_black = NULL;
+ cal->white_line = NULL;
+ cal->black_line = NULL;
+ cal->width = width;
+ cal->white_level = white_level;
+ cal->white_count = 0;
+ cal->black_count = 0;
+#ifdef TUNE_CALIBRATOR
+ cal->min_clip_count = cal->max_clip_count = 0;
+#endif /* TUNE_CALIBRATOR */
+
+ cal->k_white = (unsigned int *) malloc (width * sizeof (unsigned int));
+ cal->k_black = (unsigned int *) malloc (width * sizeof (unsigned int));
+ cal->white_line = (double *) malloc (width * sizeof (double));
+ cal->black_line = (double *) malloc (width * sizeof (double));
+
+ if (!cal->k_white || !cal->k_black | !cal->white_line || !cal->black_line)
+ {
+ DBG (5, "gt68xx_calibrator_new: no memory for calibration data\n");
+ gt68xx_calibrator_free (cal);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < width; ++i)
+ {
+ cal->k_white[i] = 0;
+ cal->k_black[i] = 0;
+ cal->white_line[i] = 0.0;
+ cal->black_line[i] = 0.0;
+ }
+
+ *cal_return = cal;
+ DBG (5, "gt68xx_calibrator_new: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_free (GT68xx_Calibrator * cal)
+{
+ DBG (5, "gt68xx_calibrator_free: enter\n");
+
+ if (!cal)
+ {
+ DBG (5, "gt68xx_calibrator_free: cal==NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+#ifdef TUNE_CALIBRATOR
+ DBG (4, "gt68xx_calibrator_free: min_clip_count=%d, max_clip_count=%d\n",
+ cal->min_clip_count, cal->max_clip_count);
+#endif /* TUNE_CALIBRATOR */
+
+ if (cal->k_white)
+ {
+ free (cal->k_white);
+ cal->k_white = NULL;
+ }
+
+ if (cal->k_black)
+ {
+ free (cal->k_black);
+ cal->k_black = NULL;
+ }
+
+ if (cal->white_line)
+ {
+ free (cal->white_line);
+ cal->white_line = NULL;
+ }
+
+ if (cal->black_line)
+ {
+ free (cal->black_line);
+ cal->black_line = NULL;
+ }
+
+ free (cal);
+
+ DBG (5, "gt68xx_calibrator_free: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_add_white_line (GT68xx_Calibrator * cal, unsigned int *line)
+{
+ SANE_Int i;
+ SANE_Int width = cal->width;
+
+ SANE_Int sum = 0;
+
+ cal->white_count++;
+
+ for (i = 0; i < width; ++i)
+ {
+ cal->white_line[i] += line[i];
+ sum += line[i];
+#ifdef SAVE_WHITE_CALIBRATION
+ printf ("%c", line[i] >> 8);
+#endif
+ }
+ if (sum / width / 256 < 0x50)
+ DBG (1,
+ "gt68xx_calibrator_add_white_line: WARNING: dark calibration line: "
+ "%2d medium white: 0x%02x\n", cal->white_count - 1,
+ sum / width / 256);
+ else
+ DBG (5,
+ "gt68xx_calibrator_add_white_line: line: %2d medium white: 0x%02x\n",
+ cal->white_count - 1, sum / width / 256);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_eval_white (GT68xx_Calibrator * cal, double factor)
+{
+ SANE_Int i;
+ SANE_Int width = cal->width;
+
+ for (i = 0; i < width; ++i)
+ {
+ cal->white_line[i] = cal->white_line[i] / cal->white_count * factor;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_add_black_line (GT68xx_Calibrator * cal, unsigned int *line)
+{
+ SANE_Int i;
+ SANE_Int width = cal->width;
+
+ SANE_Int sum = 0;
+
+ cal->black_count++;
+
+ for (i = 0; i < width; ++i)
+ {
+ cal->black_line[i] += line[i];
+ sum += line[i];
+#ifdef SAVE_BLACK_CALIBRATION
+ printf ("%c", line[i] >> 8);
+#endif
+ }
+
+ DBG (5,
+ "gt68xx_calibrator_add_black_line: line: %2d medium black: 0x%02x\n",
+ cal->black_count - 1, sum / width / 256);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_eval_black (GT68xx_Calibrator * cal, double factor)
+{
+ SANE_Int i;
+ SANE_Int width = cal->width;
+
+ for (i = 0; i < width; ++i)
+ {
+ cal->black_line[i] = cal->black_line[i] / cal->black_count - factor;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_finish_setup (GT68xx_Calibrator * cal)
+{
+#ifdef TUNE_CALIBRATOR
+ double ave_black = 0.0;
+ double ave_diff = 0.0;
+#endif /* TUNE_CALIBRATOR */
+ int i;
+ int width = cal->width;
+ unsigned int max_value = 65535;
+
+ for (i = 0; i < width; ++i)
+ {
+ unsigned int white = cal->white_line[i];
+ unsigned int black = cal->black_line[i];
+ unsigned int diff = (white > black) ? white - black : 1;
+ if (diff > max_value)
+ diff = max_value;
+ cal->k_white[i] = diff;
+ cal->k_black[i] = black;
+#ifdef TUNE_CALIBRATOR
+ ave_black += black;
+ ave_diff += diff;
+#endif /* TUNE_CALIBRATOR */
+ }
+
+#ifdef TUNE_CALIBRATOR
+ ave_black /= width;
+ ave_diff /= width;
+ DBG (4, "gt68xx_calibrator_finish_setup: ave_black=%f, ave_diff=%f\n",
+ ave_black, ave_diff);
+#endif /* TUNE_CALIBRATOR */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_calibrator_process_line (GT68xx_Calibrator * cal, unsigned int *line)
+{
+ int i;
+ int width = cal->width;
+ unsigned int white_level = cal->white_level;
+
+ for (i = 0; i < width; ++i)
+ {
+ unsigned int src_value = line[i];
+ unsigned int black = cal->k_black[i];
+ unsigned int value;
+
+ if (src_value > black)
+ {
+ value = (src_value - black) * white_level / cal->k_white[i];
+ if (value > 0xffff)
+ {
+ value = 0xffff;
+#ifdef TUNE_CALIBRATOR
+ cal->max_clip_count++;
+#endif /* TUNE_CALIBRATOR */
+ }
+ }
+ else
+ {
+ value = 0;
+#ifdef TUNE_CALIBRATOR
+ if (src_value < black)
+ cal->min_clip_count++;
+#endif /* TUNE_CALIBRATOR */
+ }
+
+ line[i] = value;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_scanner_new (GT68xx_Device * dev, GT68xx_Scanner ** scanner_return)
+{
+ GT68xx_Scanner *scanner;
+ int i;
+
+ *scanner_return = NULL;
+
+ scanner = (GT68xx_Scanner *) malloc (sizeof (GT68xx_Scanner));
+ if (!scanner)
+ {
+ DBG (5, "gt68xx_scanner_new: no memory for GT68xx_Scanner\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ scanner->dev = dev;
+ scanner->reader = NULL;
+ scanner->cal_gray = NULL;
+ scanner->cal_r = NULL;
+ scanner->cal_g = NULL;
+ scanner->cal_b = NULL;
+
+ for(i=0;i<MAX_RESOLUTIONS;i++)
+ {
+ scanner->calibrations[i].dpi = 0;
+ scanner->calibrations[i].red = NULL;
+ scanner->calibrations[i].green = NULL;
+ scanner->calibrations[i].blue = NULL;
+ scanner->calibrations[i].gray = NULL;
+ }
+
+ *scanner_return = scanner;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gt68xx_scanner_free_calibrators (GT68xx_Scanner * scanner)
+{
+ if (scanner->cal_gray)
+ {
+ gt68xx_calibrator_free (scanner->cal_gray);
+ scanner->cal_gray = NULL;
+ }
+
+ if (scanner->cal_r)
+ {
+ gt68xx_calibrator_free (scanner->cal_r);
+ scanner->cal_r = NULL;
+ }
+
+ if (scanner->cal_g)
+ {
+ gt68xx_calibrator_free (scanner->cal_g);
+ scanner->cal_g = NULL;
+ }
+
+ if (scanner->cal_b)
+ {
+ gt68xx_calibrator_free (scanner->cal_b);
+ scanner->cal_b = NULL;
+ }
+}
+
+SANE_Status
+gt68xx_scanner_free (GT68xx_Scanner * scanner)
+{
+ int i;
+
+ if (!scanner)
+ {
+ DBG (5, "gt68xx_scanner_free: scanner==NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scanner->reader)
+ {
+ gt68xx_line_reader_free (scanner->reader);
+ scanner->reader = NULL;
+ }
+
+ gt68xx_scanner_free_calibrators (scanner);
+
+ /* free in memory calibration data */
+ for (i = 0; i < MAX_RESOLUTIONS; i++)
+ {
+ scanner->calibrations[i].dpi = 0;
+ if (scanner->calibrations[i].red != NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].red);
+ }
+ if (scanner->calibrations[i].green != NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].green);
+ }
+ if (scanner->calibrations[i].blue != NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].blue);
+ }
+ if (scanner->calibrations[i].gray != NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].gray);
+ }
+ }
+
+ free (scanner);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_wait_for_positioning (GT68xx_Scanner * scanner)
+{
+ SANE_Status status;
+ SANE_Bool moving;
+ SANE_Int status_count = 0;
+
+ usleep (100000); /* needed by the BP 2400 CU Plus? */
+ while (SANE_TRUE)
+ {
+ status = gt68xx_device_is_moving (scanner->dev, &moving);
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (!moving)
+ break;
+ }
+ else
+ {
+ if (status_count > 9)
+ {
+ DBG (1, "gt68xx_scanner_wait_for_positioning: error count too high!\n");
+ return status;
+ }
+ status_count++;
+ DBG(3, "gt68xx_scanner_wait_for_positioning: ignored error\n");
+ }
+ usleep (100000);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+gt68xx_scanner_internal_start_scan (GT68xx_Scanner * scanner)
+{
+ SANE_Status status;
+ SANE_Bool ready;
+ SANE_Int repeat_count;
+
+ status = gt68xx_scanner_wait_for_positioning (scanner);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_internal_start_scan: gt68xx_scanner_wait_for_positioning error: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gt68xx_device_start_scan (scanner->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_internal_start_scan: gt68xx_device_start_scan error: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ for (repeat_count = 0; repeat_count < 30 * 100; ++repeat_count)
+ {
+ status = gt68xx_device_read_scanned_data (scanner->dev, &ready);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_internal_start_scan: gt68xx_device_read_scanned_data error: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (ready)
+ break;
+ usleep (10000);
+ }
+ if (!ready)
+ {
+ DBG (5,
+ "gt68xx_scanner_internal_start_scan: scanner still not ready - giving up\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = gt68xx_device_read_start (scanner->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_internal_start_scan: gt68xx_device_read_start error: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_start_scan_extended (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params)
+{
+ SANE_Status status;
+ GT68xx_AFE_Parameters afe = *scanner->dev->afe;
+
+ status = gt68xx_scanner_wait_for_positioning (scanner);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_start_scan_extended: gt68xx_scanner_wait_for_positioning error: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gt68xx_device_setup_scan (scanner->dev, request, action, params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_start_scan_extended: gt68xx_device_setup_scan failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gt68xx_line_reader_new (scanner->dev, params,
+ action == SA_SCAN ? SANE_TRUE : SANE_FALSE,
+ &scanner->reader);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_start_scan_extended: gt68xx_line_reader_new failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (scanner->dev->model->is_cis
+ && !((scanner->dev->model->flags & GT68XX_FLAG_SHEET_FED) && scanner->calibrated == SANE_FALSE))
+ {
+ status =
+ gt68xx_device_set_exposure_time (scanner->dev,
+ scanner->dev->exposure);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_start_scan_extended: gt68xx_device_set_exposure_time failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = gt68xx_device_set_afe (scanner->dev, &afe);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_start_scan_extended: gt68xx_device_set_afe failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gt68xx_scanner_internal_start_scan (scanner);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_start_scan_extended: gt68xx_scanner_internal_start_scan failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_create_calibrator (GT68xx_Scan_Parameters * params,
+ GT68xx_Calibrator ** cal_return)
+{
+ return gt68xx_calibrator_new (params->pixel_xs, 65535, cal_return);
+}
+
+static SANE_Status
+gt68xx_scanner_create_color_calibrators (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Parameters * params)
+{
+ SANE_Status status;
+
+ if (!scanner->cal_r)
+ {
+ status = gt68xx_scanner_create_calibrator (params, &scanner->cal_r);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ if (!scanner->cal_g)
+ {
+ status = gt68xx_scanner_create_calibrator (params, &scanner->cal_g);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ if (!scanner->cal_b)
+ {
+ status = gt68xx_scanner_create_calibrator (params, &scanner->cal_b);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_create_gray_calibrators (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Parameters * params)
+{
+ SANE_Status status;
+
+ if (!scanner->cal_gray)
+ {
+ status = gt68xx_scanner_create_calibrator (params, &scanner->cal_gray);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_calibrate_color_white_line (GT68xx_Scanner * scanner,
+ unsigned int **buffer_pointers)
+{
+
+ gt68xx_calibrator_add_white_line (scanner->cal_r, buffer_pointers[0]);
+ gt68xx_calibrator_add_white_line (scanner->cal_g, buffer_pointers[1]);
+ gt68xx_calibrator_add_white_line (scanner->cal_b, buffer_pointers[2]);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_calibrate_gray_white_line (GT68xx_Scanner * scanner,
+ unsigned int **buffer_pointers)
+{
+ gt68xx_calibrator_add_white_line (scanner->cal_gray, buffer_pointers[0]);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_calibrate_color_black_line (GT68xx_Scanner * scanner,
+ unsigned int **buffer_pointers)
+{
+ gt68xx_calibrator_add_black_line (scanner->cal_r, buffer_pointers[0]);
+ gt68xx_calibrator_add_black_line (scanner->cal_g, buffer_pointers[1]);
+ gt68xx_calibrator_add_black_line (scanner->cal_b, buffer_pointers[2]);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_scanner_calibrate_gray_black_line (GT68xx_Scanner * scanner,
+ unsigned int **buffer_pointers)
+{
+ gt68xx_calibrator_add_black_line (scanner->cal_gray, buffer_pointers[0]);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_scanner_calibrate (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * request)
+{
+ SANE_Status status;
+ GT68xx_Scan_Parameters params;
+ GT68xx_Scan_Request req;
+ SANE_Int i;
+ unsigned int *buffer_pointers[3];
+ GT68xx_AFE_Parameters *afe = scanner->dev->afe;
+ GT68xx_Exposure_Parameters *exposure = scanner->dev->exposure;
+
+ memcpy (&req, request, sizeof (req));
+
+ gt68xx_scanner_free_calibrators (scanner);
+
+ if (scanner->auto_afe)
+ {
+ if (scanner->dev->model->is_cis)
+ status = gt68xx_afe_cis_auto (scanner);
+ else
+ status = gt68xx_afe_ccd_auto (scanner, request);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "gt68xx_scanner_calibrate: gt68xx_afe_*_auto failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ req.mbs = SANE_FALSE;
+ }
+ else
+ req.mbs = SANE_TRUE;
+
+ DBG (3, "afe 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", afe->r_offset,
+ afe->r_pga, afe->g_offset, afe->g_pga, afe->b_offset, afe->b_pga);
+ DBG (3, "exposure 0x%02x 0x%02x 0x%02x\n", exposure->r_time,
+ exposure->g_time, exposure->b_time);
+
+ if (!scanner->calib)
+ return SANE_STATUS_GOOD;
+
+ req.mds = SANE_TRUE;
+ req.mas = SANE_FALSE;
+
+ if (scanner->dev->model->is_cis && !(scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
+ req.color = SANE_TRUE;
+
+ if (req.use_ta)
+ {
+ req.lamp = SANE_FALSE;
+ status =
+ gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
+ }
+ else
+ {
+ req.lamp = SANE_TRUE;
+ status =
+ gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
+ }
+
+ status = gt68xx_scanner_start_scan_extended (scanner, &req, SA_CALIBRATE,
+ &params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_calibrate: gt68xx_scanner_start_scan_extended failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (params.color)
+ {
+ status = gt68xx_scanner_create_color_calibrators (scanner, &params);
+ }
+ else
+ {
+ status = gt68xx_scanner_create_gray_calibrators (scanner, &params);
+ }
+
+#if defined(SAVE_WHITE_CALIBRATION) || defined(SAVE_BLACK_CALIBRATION)
+ printf ("P5\n%d %d\n255\n", params.pixel_xs, params.pixel_ys * 3);
+#endif
+ for (i = 0; i < params.pixel_ys; ++i)
+ {
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_calibrate: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (params.color)
+ status = gt68xx_scanner_calibrate_color_white_line (scanner,
+ buffer_pointers);
+ else
+ status = gt68xx_scanner_calibrate_gray_white_line (scanner,
+ buffer_pointers);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "gt68xx_scanner_calibrate: calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ gt68xx_scanner_stop_scan (scanner);
+
+ if (params.color)
+ {
+ gt68xx_calibrator_eval_white (scanner->cal_r, 1);
+ gt68xx_calibrator_eval_white (scanner->cal_g, 1);
+ gt68xx_calibrator_eval_white (scanner->cal_b, 1);
+ }
+ else
+ {
+ gt68xx_calibrator_eval_white (scanner->cal_gray, 1);
+ }
+
+ req.mbs = SANE_FALSE;
+ req.mds = SANE_FALSE;
+ req.mas = SANE_FALSE;
+ req.lamp = SANE_FALSE;
+
+ status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_calibrate: gt68xx_device_lamp_control failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (!scanner->dev->model->is_cis
+ || (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
+ usleep (500000);
+ status = gt68xx_scanner_start_scan_extended (scanner, &req, SA_CALIBRATE,
+ &params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_calibrate: gt68xx_scanner_start_scan_extended failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ for (i = 0; i < params.pixel_ys; ++i)
+ {
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_calibrate: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (params.color)
+ status = gt68xx_scanner_calibrate_color_black_line (scanner,
+ buffer_pointers);
+ else
+ status = gt68xx_scanner_calibrate_gray_black_line (scanner,
+ buffer_pointers);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "gt68xx_scanner_calibrate: calibration failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ gt68xx_scanner_stop_scan (scanner);
+
+ if (req.use_ta)
+ status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
+ else
+ status = gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_calibrate: gt68xx_device_lamp_control failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (!scanner->dev->model->is_cis)
+ usleep (500000);
+
+ if (params.color)
+ {
+ gt68xx_calibrator_eval_black (scanner->cal_r, 0.0);
+ gt68xx_calibrator_eval_black (scanner->cal_g, 0.0);
+ gt68xx_calibrator_eval_black (scanner->cal_b, 0.0);
+
+ gt68xx_calibrator_finish_setup (scanner->cal_r);
+ gt68xx_calibrator_finish_setup (scanner->cal_g);
+ gt68xx_calibrator_finish_setup (scanner->cal_b);
+ }
+ else
+ {
+ gt68xx_calibrator_eval_black (scanner->cal_gray, 0.0);
+ gt68xx_calibrator_finish_setup (scanner->cal_gray);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_scanner_start_scan (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Parameters * params)
+{
+ request->mbs = SANE_FALSE; /* don't go home before real scan */
+ request->mds = SANE_TRUE;
+ request->mas = SANE_FALSE;
+ if (request->use_ta)
+ {
+ gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
+ request->lamp = SANE_FALSE;
+ }
+ else
+ {
+ gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
+ request->lamp = SANE_TRUE;
+ }
+ if (!scanner->dev->model->is_cis)
+ sleep (2);
+
+ return gt68xx_scanner_start_scan_extended (scanner, request, SA_SCAN,
+ params);
+}
+
+SANE_Status
+gt68xx_scanner_read_line (GT68xx_Scanner * scanner,
+ unsigned int **buffer_pointers)
+{
+ SANE_Status status;
+
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_scanner_read_line: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (scanner->calib)
+ {
+ if (scanner->reader->params.color)
+ {
+ gt68xx_calibrator_process_line (scanner->cal_r, buffer_pointers[0]);
+ gt68xx_calibrator_process_line (scanner->cal_g, buffer_pointers[1]);
+ gt68xx_calibrator_process_line (scanner->cal_b, buffer_pointers[2]);
+ }
+ else
+ {
+ if (scanner->dev->model->is_cis && !(scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
+ {
+ if (strcmp
+ (scanner->val[OPT_GRAY_MODE_COLOR].s,
+ GT68XX_COLOR_BLUE) == 0)
+ gt68xx_calibrator_process_line (scanner->cal_b,
+ buffer_pointers[0]);
+ else
+ if (strcmp
+ (scanner->val[OPT_GRAY_MODE_COLOR].s,
+ GT68XX_COLOR_GREEN) == 0)
+ gt68xx_calibrator_process_line (scanner->cal_g,
+ buffer_pointers[0]);
+ else
+ gt68xx_calibrator_process_line (scanner->cal_r,
+ buffer_pointers[0]);
+ }
+ else
+ {
+ gt68xx_calibrator_process_line (scanner->cal_gray,
+ buffer_pointers[0]);
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_scanner_stop_scan (GT68xx_Scanner * scanner)
+{
+ gt68xx_line_reader_free (scanner->reader);
+ scanner->reader = NULL;
+
+ return gt68xx_device_stop_scan (scanner->dev);
+}
+
+/************************************************************************/
+/* */
+/* AFE offset/gain automatic configuration */
+/* */
+/************************************************************************/
+
+typedef struct GT68xx_Afe_Values GT68xx_Afe_Values;
+
+struct GT68xx_Afe_Values
+{
+ SANE_Int black; /* minimum black (0-255) */
+ SANE_Int white; /* maximum white (0-255) */
+ SANE_Int total_white; /* average white of the complete line (0-65536) */
+ SANE_Int calwidth;
+ SANE_Int callines;
+ SANE_Int max_width;
+ SANE_Int scan_dpi;
+ SANE_Fixed start_black;
+ SANE_Int offset_direction;
+ SANE_Int coarse_black;
+ SANE_Int coarse_white;
+};
+
+
+/************************************************************************/
+/* CCD scanners */
+/************************************************************************/
+
+/** Calculate average black and maximum white
+ *
+ * This function is used for CCD scanners. The black mark to the left ist used
+ * for the calculation of average black. The remaining calibration strip
+ * is used for searching the segment whose white average is the highest.
+ *
+ * @param values AFE values
+ * @param buffer scanned line
+ */
+static void
+gt68xx_afe_ccd_calc (GT68xx_Afe_Values * values, unsigned int *buffer)
+{
+ SANE_Int start_black;
+ SANE_Int end_black;
+ SANE_Int start_white;
+ SANE_Int end_white;
+ SANE_Int i;
+ SANE_Int max_black = 0;
+ SANE_Int min_black = 255;
+ SANE_Int max_white = 0;
+ SANE_Int total_white = 0;
+
+ /* set size of black mark and white segments */
+ start_black =
+ SANE_UNFIX (values->start_black) * values->scan_dpi / MM_PER_INCH;
+ end_black = start_black + 1.0 * values->scan_dpi / MM_PER_INCH; /* 1 mm */
+
+ /* 5mm after mark */
+ start_white = end_black + 5.0 * values->scan_dpi / MM_PER_INCH;
+ end_white = values->calwidth;
+
+ DBG (5,
+ "gt68xx_afe_ccd_calc: dpi=%d, start_black=%d, end_black=%d, start_white=%d, end_white=%d\n",
+ values->scan_dpi, start_black, end_black, start_white, end_white);
+
+ /* calc min and max black value */
+ for (i = start_black; i < end_black; i++)
+ {
+ if ((SANE_Int) (buffer[i] >> 8) < min_black)
+ min_black = (buffer[i] >> 8);
+ if ((SANE_Int) (buffer[i] >> 8) > max_black)
+ max_black = (buffer[i] >> 8);
+#ifdef DEBUG_BLACK
+ if ((buffer[i] >> 8) > 15)
+ fprintf (stderr, "+");
+ else if ((buffer[i] >> 8) < 5)
+ fprintf (stderr, "-");
+ else
+ fprintf (stderr, "_");
+#endif
+ }
+#ifdef DEBUG_BLACK
+ fprintf (stderr, "\n");
+#endif
+
+ for (i = start_white; i < end_white; ++i)
+ {
+ if ((SANE_Int) (buffer[i] >> 8) > max_white)
+ max_white = (buffer[i] >> 8);
+ total_white += buffer[i];
+ }
+ values->total_white = total_white / (end_white - start_white);
+
+ values->black = min_black;
+ values->white = max_white;
+ if (values->white < 50 || values->black > 150
+ || values->white - values->black < 30 || max_black - min_black > 15)
+ DBG (1,
+ "gt68xx_afe_ccd_calc: WARNING: max_white %3d min_black %3d max_black %3d\n",
+ values->white, values->black, max_black);
+ else
+ DBG (5,
+ "gt68xx_afe_ccd_calc: max_white %3d min_black %3d max_black %3d\n",
+ values->white, values->black, max_black);
+}
+
+static SANE_Bool
+gt68xx_afe_ccd_adjust_offset_gain (SANE_String_Const color_name,
+ GT68xx_Afe_Values * values,
+ unsigned int *buffer, SANE_Byte * offset,
+ SANE_Byte * pga, SANE_Byte * old_offset,
+ SANE_Byte * old_pga)
+{
+ SANE_Int black_low = values->coarse_black, black_high = black_low + 10;
+ SANE_Int white_high = values->coarse_white, white_low = white_high - 10;
+ SANE_Bool done = SANE_TRUE;
+ SANE_Byte local_pga = *pga;
+ SANE_Byte local_offset = *offset;
+
+ gt68xx_afe_ccd_calc (values, buffer);
+
+#if 0
+ /* test all offset values */
+ local_offset++;
+ done = SANE_FALSE;
+ goto finish;
+#endif
+
+ if (values->white > white_high)
+ {
+ if (values->black > black_high)
+ local_offset += values->offset_direction;
+ else if (values->black < black_low)
+ local_pga--;
+ else
+ {
+ local_offset += values->offset_direction;
+ local_pga--;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+ else if (values->white < white_low)
+ {
+ if (values->black < black_low)
+ local_offset -= values->offset_direction;
+ else if (values->black > black_high)
+ local_pga++;
+ else
+ {
+ local_offset -= values->offset_direction;
+ local_pga++;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+ if (values->black > black_high)
+ {
+ if (values->white > white_high)
+ local_offset += values->offset_direction;
+ else if (values->white < white_low)
+ local_pga++;
+ else
+ {
+ local_offset += values->offset_direction;
+ local_pga++;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+ else if (values->black < black_low)
+ {
+ if (values->white < white_low)
+ local_offset -= values->offset_direction;
+ else if (values->white > white_high)
+ local_pga--;
+ else
+ {
+ local_offset -= values->offset_direction;
+ local_pga--;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+finish:
+ if ((local_pga == *pga) && (local_offset == *offset))
+ done = SANE_TRUE;
+ if ((local_pga == *old_pga) && (local_offset == *old_offset))
+ done = SANE_TRUE;
+
+ *old_pga = *pga;
+ *old_offset = *offset;
+
+ DBG (4, "%5s white=%3d, black=%3d, offset=%2d, gain=%2d, old offs=%2d, "
+ "old gain=%2d, total_white=%5d %s\n", color_name, values->white,
+ values->black, local_offset, local_pga, *offset, *pga,
+ values->total_white, done ? "DONE " : "");
+
+ *pga = local_pga;
+ *offset = local_offset;
+
+ return done;
+}
+
+/* Wait for lamp to give stable brightness */
+static SANE_Status
+gt68xx_wait_lamp_stable (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Parameters * params,
+ GT68xx_Scan_Request *request,
+ unsigned int *buffer_pointers[3],
+ GT68xx_Afe_Values *values,
+ SANE_Bool dont_move)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int last_white = 0;
+ SANE_Bool first = SANE_TRUE;
+ SANE_Bool message_printed = SANE_FALSE;
+ struct timeval now, start_time;
+ int secs_lamp_on, secs_start;
+ int increase = -5;
+
+ gettimeofday (&start_time, 0);
+ do
+ {
+ usleep (200000);
+
+ if (!first && dont_move)
+ {
+ request->mbs = SANE_FALSE;
+ request->mds = SANE_FALSE;
+ }
+ first = SANE_FALSE;
+
+ /* read line */
+ status = gt68xx_scanner_start_scan_extended (scanner, request,
+ SA_CALIBRATE_ONE_LINE,
+ params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_wait_lamp_stable: gt68xx_scanner_start_scan_extended "
+ "failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_wait_lamp_stable: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gt68xx_scanner_stop_scan (scanner);
+
+ gt68xx_afe_ccd_calc (values, buffer_pointers[0]);
+
+ DBG (4,
+ "gt68xx_wait_lamp_stable: this white = %d, last white = %d\n",
+ values->total_white, last_white);
+
+ gettimeofday (&now, 0);
+ secs_lamp_on = now.tv_sec - scanner->lamp_on_time.tv_sec;
+ secs_start = now.tv_sec - start_time.tv_sec;
+
+ if (!message_printed && secs_start > 5)
+ {
+ DBG (0, "Please wait for lamp warm-up\n");
+ message_printed = SANE_TRUE;
+ }
+
+ if (scanner->val[OPT_AUTO_WARMUP].w == SANE_TRUE)
+ {
+ if (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP)
+ {
+ if (values->total_white <= (last_white - 20))
+ increase--;
+ if (values->total_white >= last_white)
+ increase++;
+ if (increase > 0 && (values->total_white <= (last_white + 20))
+ && values->total_white != 0)
+ break;
+ }
+ else
+ {
+ if ((values->total_white <= (last_white + 20))
+ && values->total_white != 0)
+ break; /* lamp is warmed up */
+ }
+ }
+ last_white = values->total_white;
+ }
+ while (secs_lamp_on <= WARMUP_TIME);
+
+ DBG (3, "gt68xx_wait_lamp_stable: Lamp is stable after %d secs (%d secs total)\n",
+ secs_start, secs_lamp_on);
+ return status;
+}
+
+/** Select best AFE gain and offset parameters.
+ *
+ * This function must be called before the main scan to choose the best values
+ * for the AFE gains and offsets. It performs several one-line scans of the
+ * calibration strip.
+ *
+ * @param scanner Scanner object.
+ * @param orig_request Scan parameters.
+ *
+ * @returns
+ * - #SANE_STATUS_GOOD - gain and offset setting completed successfully
+ * - other error value - failure of some internal function
+ */
+static SANE_Status
+gt68xx_afe_ccd_auto (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * orig_request)
+{
+ SANE_Status status;
+ GT68xx_Scan_Parameters params;
+ GT68xx_Scan_Request request;
+ int i;
+ GT68xx_Afe_Values values;
+ unsigned int *buffer_pointers[3];
+ GT68xx_AFE_Parameters *afe = scanner->dev->afe, old_afe;
+ SANE_Bool gray_done = SANE_FALSE;
+ SANE_Bool red_done = SANE_FALSE, green_done = SANE_FALSE, blue_done =
+ SANE_FALSE;
+
+ values.offset_direction = 1;
+ if (scanner->dev->model->flags & GT68XX_FLAG_OFFSET_INV)
+ values.offset_direction = -1;
+
+ memset (&old_afe, 255, sizeof (old_afe));
+
+ request.x0 = SANE_FIX (0.0);
+ request.xs = scanner->dev->model->x_size;
+ request.xdpi = 300;
+ request.ydpi = 300;
+ request.depth = 8;
+ request.color = orig_request->color;
+ /* request.color = SANE_TRUE; */
+ request.mas = SANE_FALSE;
+ request.mbs = SANE_FALSE;
+ request.mds = SANE_TRUE;
+ request.calculate = SANE_FALSE;
+ request.use_ta = orig_request->use_ta;
+
+ if (orig_request->use_ta)
+ {
+ gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
+ request.lamp = SANE_FALSE;
+ }
+ else
+ {
+ gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
+ request.lamp = SANE_TRUE;
+ }
+
+ /* read line */
+ status = gt68xx_scanner_start_scan_extended (scanner, &request,
+ SA_CALIBRATE_ONE_LINE,
+ &params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_afe_ccd_auto: gt68xx_scanner_start_scan_extended failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ values.scan_dpi = params.xdpi;
+ values.calwidth = params.pixel_xs;
+ values.max_width =
+ (params.pixel_xs * scanner->dev->model->optical_xdpi) / params.xdpi;
+ if (orig_request->use_ta)
+ values.start_black = SANE_FIX (20.0);
+ else
+ values.start_black = scanner->dev->model->x_offset_mark;
+ values.coarse_black = 1;
+ values.coarse_white = 254;
+
+ request.mds = SANE_FALSE;
+ DBG (5, "gt68xx_afe_ccd_auto: scan_dpi=%d, calwidth=%d, max_width=%d, "
+ "start_black=%.1f mm\n", values.scan_dpi,
+ values.calwidth, values.max_width, SANE_UNFIX (values.start_black));
+
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_afe_ccd_auto: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gt68xx_scanner_stop_scan (scanner);
+
+ status = gt68xx_wait_lamp_stable (scanner, &params, &request, buffer_pointers,
+ &values, SANE_FALSE);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "gt68xx_afe_ccd_auto: gt68xx_wait_lamp_stable failed %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ i = 0;
+ do
+ {
+ i++;
+ /* read line */
+ status = gt68xx_scanner_start_scan_extended (scanner, &request,
+ SA_CALIBRATE_ONE_LINE,
+ &params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_afe_ccd_auto: gt68xx_scanner_start_scan_extended failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_afe_ccd_auto: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (params.color)
+ {
+ /* red */
+ if (!red_done)
+ red_done =
+ gt68xx_afe_ccd_adjust_offset_gain ("red", &values,
+ buffer_pointers[0],
+ &afe->r_offset, &afe->r_pga,
+ &old_afe.r_offset,
+ &old_afe.r_pga);
+ /* green */
+ if (!green_done)
+ green_done =
+ gt68xx_afe_ccd_adjust_offset_gain ("green", &values,
+ buffer_pointers[1],
+ &afe->g_offset, &afe->g_pga,
+ &old_afe.g_offset,
+ &old_afe.g_pga);
+
+ /* blue */
+ if (!blue_done)
+ blue_done =
+ gt68xx_afe_ccd_adjust_offset_gain ("blue", &values,
+ buffer_pointers[2],
+ &afe->b_offset, &afe->b_pga,
+ &old_afe.b_offset,
+ &old_afe.b_pga);
+ }
+ else
+ {
+ if (strcmp (scanner->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_BLUE)
+ == 0)
+ {
+ gray_done =
+ gt68xx_afe_ccd_adjust_offset_gain ("gray", &values,
+ buffer_pointers[0],
+ &afe->b_offset,
+ &afe->b_pga,
+ &old_afe.b_offset,
+ &old_afe.b_pga);
+ }
+ else
+ if (strcmp
+ (scanner->val[OPT_GRAY_MODE_COLOR].s,
+ GT68XX_COLOR_GREEN) == 0)
+ {
+ gray_done =
+ gt68xx_afe_ccd_adjust_offset_gain ("gray", &values,
+ buffer_pointers[0],
+ &afe->g_offset,
+ &afe->g_pga,
+ &old_afe.g_offset,
+ &old_afe.g_pga);
+ afe->r_offset = afe->b_offset = 0x20;
+ afe->r_pga = afe->b_pga = 0x18;
+ }
+ else
+ {
+ gray_done =
+ gt68xx_afe_ccd_adjust_offset_gain ("gray", &values,
+ buffer_pointers[0],
+ &afe->r_offset,
+ &afe->r_pga,
+ &old_afe.r_offset,
+ &old_afe.r_pga);
+ }
+ }
+ gt68xx_scanner_stop_scan (scanner);
+ }
+ while (((params.color && (!red_done || !green_done || !blue_done))
+ || (!params.color && !gray_done)) && i < 100);
+
+ return status;
+}
+
+/************************************************************************/
+/* CIS scanners */
+/************************************************************************/
+
+
+static void
+gt68xx_afe_cis_calc_black (GT68xx_Afe_Values * values,
+ unsigned int *black_buffer)
+{
+ SANE_Int start_black;
+ SANE_Int end_black;
+ SANE_Int i, j;
+ SANE_Int min_black = 255;
+ SANE_Int average = 0;
+
+ start_black = 0;
+ end_black = values->calwidth;
+
+ /* find min average black value */
+ for (i = start_black; i < end_black; ++i)
+ {
+ SANE_Int avg_black = 0;
+ for (j = 0; j < values->callines; j++)
+ avg_black += (*(black_buffer + i + j * values->calwidth) >> 8);
+ avg_black /= values->callines;
+ average += avg_black;
+ if (avg_black < min_black)
+ min_black = avg_black;
+ }
+ values->black = min_black;
+ average /= (end_black - start_black);
+ DBG (5,
+ "gt68xx_afe_cis_calc_black: min_black=0x%02x, average_black=0x%02x\n",
+ values->black, average);
+}
+
+static void
+gt68xx_afe_cis_calc_white (GT68xx_Afe_Values * values,
+ unsigned int *white_buffer)
+{
+ SANE_Int start_white;
+ SANE_Int end_white;
+ SANE_Int i, j;
+ SANE_Int max_white = 0;
+
+ start_white = 0;
+ end_white = values->calwidth;
+ values->total_white = 0;
+
+ /* find max average white value */
+ for (i = start_white; i < end_white; ++i)
+ {
+ SANE_Int avg_white = 0;
+ for (j = 0; j < values->callines; j++)
+ {
+ avg_white += (*(white_buffer + i + j * values->calwidth) >> 8);
+ values->total_white += (*(white_buffer + i + j * values->calwidth));
+ }
+ avg_white /= values->callines;
+ if (avg_white > max_white)
+ max_white = avg_white;
+ }
+ values->white = max_white;
+ values->total_white /= (values->callines * (end_white - start_white));
+ DBG (5,
+ "gt68xx_afe_cis_calc_white: max_white=0x%02x, average_white=0x%02x\n",
+ values->white, values->total_white >> 8);
+}
+
+static SANE_Bool
+gt68xx_afe_cis_adjust_gain_offset (SANE_String_Const color_name,
+ GT68xx_Afe_Values * values,
+ unsigned int *black_buffer,
+ unsigned int *white_buffer,
+ GT68xx_AFE_Parameters * afe,
+ GT68xx_AFE_Parameters * old_afe)
+{
+ SANE_Byte *offset, *old_offset, *gain, *old_gain;
+ SANE_Int o, g;
+ SANE_Int black_low = values->coarse_black, black_high = black_low + 10;
+ SANE_Int white_high = values->coarse_white, white_low = white_high - 10;
+ SANE_Bool done = SANE_TRUE;
+
+ gt68xx_afe_cis_calc_black (values, black_buffer);
+ gt68xx_afe_cis_calc_white (values, white_buffer);
+
+ if (strcmp (color_name, "red") == 0)
+ {
+ offset = &(afe->r_offset);
+ old_offset = &old_afe->r_offset;
+ gain = &afe->r_pga;
+ old_gain = &old_afe->r_pga;
+ }
+ else if (strcmp (color_name, "green") == 0)
+ {
+ offset = &afe->g_offset;
+ old_offset = &old_afe->g_offset;
+ gain = &afe->g_pga;
+ old_gain = &old_afe->g_pga;
+ }
+ else
+ {
+ offset = &afe->b_offset;
+ old_offset = &old_afe->b_offset;
+ gain = &afe->b_pga;
+ old_gain = &old_afe->b_pga;
+ }
+
+ o = *offset;
+ g = *gain;
+
+ if (values->white > white_high)
+ {
+ if (values->black > black_high)
+ o -= values->offset_direction;
+ else if (values->black < black_low)
+ g--;
+ else
+ {
+ o -= values->offset_direction;
+ g--;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+ else if (values->white < white_low)
+ {
+ if (values->black < black_low)
+ o += values->offset_direction;
+ else if (values->black > black_high)
+ g++;
+ else
+ {
+ o += values->offset_direction;
+ g++;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+ if (values->black > black_high)
+ {
+ if (values->white > white_high)
+ o -= values->offset_direction;
+ else if (values->white < white_low)
+ g++;
+ else
+ {
+ o -= values->offset_direction;
+ g++;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+ else if (values->black < black_low)
+ {
+ if (values->white < white_low)
+ o += values->offset_direction;
+ else if (values->white > white_high)
+ g--;
+ else
+ {
+ o += values->offset_direction;
+ g--;
+ }
+ done = SANE_FALSE;
+ goto finish;
+ }
+finish:
+ if (g < 0)
+ g = 0;
+ if (g > 48)
+ g = 48;
+ if (o < 0)
+ o = 0;
+ if (o > 64)
+ o = 64;
+
+ if ((g == *gain) && (o == *offset))
+ done = SANE_TRUE;
+ if ((g == *old_gain) && (o == *old_offset))
+ done = SANE_TRUE;
+
+ *old_gain = *gain;
+ *old_offset = *offset;
+
+ DBG (4, "%5s white=%3d, black=%3d, offset=0x%02X, gain=0x%02X, old offs=0x%02X, "
+ "old gain=0x%02X, total_white=%5d %s\n", color_name, values->white,
+ values->black, o, g, *offset, *gain, values->total_white,
+ done ? "DONE " : "");
+
+ *gain = g;
+ *offset = o;
+
+ return done;
+}
+
+
+static SANE_Bool
+gt68xx_afe_cis_adjust_exposure (SANE_String_Const color_name,
+ GT68xx_Afe_Values * values,
+ unsigned int *white_buffer, SANE_Int border,
+ SANE_Int * exposure_time)
+{
+ SANE_Int exposure_change = 0;
+
+ gt68xx_afe_cis_calc_white (values, white_buffer);
+
+ if (values->white < border)
+ {
+ exposure_change = ((border - values->white) * 1);
+ (*exposure_time) += exposure_change;
+ DBG (4,
+ "%5s: white = %3d, total_white=%5d (exposure too low) --> exposure += %d (=0x%03x)\n",
+ color_name, values->white, values->total_white, exposure_change, *exposure_time);
+ return SANE_FALSE;
+ }
+ else if (values->white > border + 5)
+ {
+ exposure_change = -((values->white - (border + 5)) * 1);
+ (*exposure_time) += exposure_change;
+ DBG (4,
+ "%5s: white = %3d, total_white=%5d (exposure too high) --> exposure -= %d (=0x%03x)\n",
+ color_name, values->white, values->total_white, exposure_change, *exposure_time);
+ return SANE_FALSE;
+ }
+ else
+ {
+ DBG (4, "%5s: white = %3d, total_white=%5d (exposure ok=0x%03x)\n",
+ color_name, values->white, values->total_white, *exposure_time);
+ }
+ return SANE_TRUE;
+}
+
+static SANE_Status
+gt68xx_afe_cis_read_lines (GT68xx_Afe_Values * values,
+ GT68xx_Scanner * scanner, SANE_Bool lamp,
+ SANE_Bool first, unsigned int *r_buffer,
+ unsigned int *g_buffer, unsigned int *b_buffer)
+{
+ SANE_Status status;
+ int line;
+ unsigned int *buffer_pointers[3];
+ GT68xx_Scan_Request request;
+ GT68xx_Scan_Parameters params;
+
+ request.x0 = SANE_FIX (0.0);
+ request.xs = scanner->dev->model->x_size;
+ request.xdpi = 300;
+ request.ydpi = 300;
+ request.depth = 8;
+ request.color = SANE_TRUE;
+ request.mas = SANE_FALSE;
+ request.calculate = SANE_FALSE;
+ request.use_ta = SANE_FALSE;
+
+ if (first) /* go to start position */
+ {
+ request.mbs = SANE_TRUE;
+ request.mds = SANE_TRUE;
+ }
+ else
+ {
+ request.mbs = SANE_FALSE;
+ request.mds = SANE_FALSE;
+ }
+ request.lamp = lamp;
+
+ if (!r_buffer) /* First, set the size parameters */
+ {
+ request.calculate = SANE_TRUE;
+ RIE (gt68xx_device_setup_scan
+ (scanner->dev, &request, SA_CALIBRATE_ONE_LINE, &params));
+ values->scan_dpi = params.xdpi;
+ values->calwidth = params.pixel_xs;
+ values->callines = params.pixel_ys;
+ values->start_black = scanner->dev->model->x_offset_mark;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (first && (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP))
+ {
+ if (request.use_ta)
+ {
+ gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
+ request.lamp = SANE_FALSE;
+ }
+ else
+ {
+ gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE);
+ request.lamp = SANE_TRUE;
+ }
+ status = gt68xx_wait_lamp_stable (scanner, &params, &request,
+ buffer_pointers, values, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "gt68xx_afe_cis_read_lines: gt68xx_wait_lamp_stable failed %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ request.mbs = SANE_FALSE;
+ request.mds = SANE_FALSE;
+ }
+
+ status =
+ gt68xx_scanner_start_scan_extended (scanner, &request,
+ SA_CALIBRATE_ONE_LINE, &params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_afe_cis_read_lines: gt68xx_scanner_start_scan_extended failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ values->scan_dpi = params.xdpi;
+ values->calwidth = params.pixel_xs;
+ values->callines = params.pixel_ys;
+ values->coarse_black = 2;
+ values->coarse_white = 253;
+
+ if (r_buffer && g_buffer && b_buffer)
+ for (line = 0; line < values->callines; line++)
+ {
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_afe_cis_read_lines: gt68xx_line_reader_read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ memcpy (r_buffer + values->calwidth * line, buffer_pointers[0],
+ values->calwidth * sizeof (unsigned int));
+ memcpy (g_buffer + values->calwidth * line, buffer_pointers[1],
+ values->calwidth * sizeof (unsigned int));
+ memcpy (b_buffer + values->calwidth * line, buffer_pointers[2],
+ values->calwidth * sizeof (unsigned int));
+ }
+
+ status = gt68xx_scanner_stop_scan (scanner);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "gt68xx_afe_cis_read_lines: gt68xx_scanner_stop_scan failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_afe_cis_auto (GT68xx_Scanner * scanner)
+{
+ SANE_Status status;
+ int total_count, exposure_count;
+ GT68xx_Afe_Values values;
+ GT68xx_AFE_Parameters *afe = scanner->dev->afe, old_afe;
+ GT68xx_Exposure_Parameters *exposure = scanner->dev->exposure;
+ SANE_Int red_done, green_done, blue_done;
+ SANE_Bool first = SANE_TRUE;
+ unsigned int *r_gbuffer = 0, *g_gbuffer = 0, *b_gbuffer = 0;
+ unsigned int *r_obuffer = 0, *g_obuffer = 0, *b_obuffer = 0;
+
+ DBG (5, "gt68xx_afe_cis_auto: start\n");
+
+ memset (&old_afe, 255, sizeof (old_afe));
+
+ /* Start with the preset exposure settings */
+ memcpy (scanner->dev->exposure, &scanner->dev->model->exposure,
+ sizeof (*scanner->dev->exposure));
+
+ RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_FALSE, SANE_FALSE,
+ r_gbuffer, g_gbuffer, b_gbuffer));
+
+ r_gbuffer =
+ malloc (values.calwidth * values.callines * sizeof (unsigned int));
+ g_gbuffer =
+ malloc (values.calwidth * values.callines * sizeof (unsigned int));
+ b_gbuffer =
+ malloc (values.calwidth * values.callines * sizeof (unsigned int));
+ r_obuffer =
+ malloc (values.calwidth * values.callines * sizeof (unsigned int));
+ g_obuffer =
+ malloc (values.calwidth * values.callines * sizeof (unsigned int));
+ b_obuffer =
+ malloc (values.calwidth * values.callines * sizeof (unsigned int));
+ if (!r_gbuffer || !g_gbuffer || !b_gbuffer || !r_obuffer || !g_obuffer
+ || !b_obuffer)
+ return SANE_STATUS_NO_MEM;
+
+ total_count = 0;
+ red_done = green_done = blue_done = SANE_FALSE;
+ old_afe.r_offset = old_afe.g_offset = old_afe.b_offset = 255;
+ old_afe.r_pga = old_afe.g_pga = old_afe.b_pga = 255;
+ do
+ {
+ values.offset_direction = 1;
+ if (scanner->dev->model->flags & GT68XX_FLAG_OFFSET_INV)
+ values.offset_direction = -1;
+
+ RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_FALSE, first,
+ r_obuffer, g_obuffer, b_obuffer));
+ RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_TRUE, SANE_FALSE,
+ r_gbuffer, g_gbuffer, b_gbuffer));
+
+ if (!red_done)
+ red_done =
+ gt68xx_afe_cis_adjust_gain_offset ("red", &values, r_obuffer,
+ r_gbuffer, afe, &old_afe);
+ if (!green_done)
+ green_done =
+ gt68xx_afe_cis_adjust_gain_offset ("green", &values, g_obuffer,
+ g_gbuffer, afe, &old_afe);
+ if (!blue_done)
+ blue_done =
+ gt68xx_afe_cis_adjust_gain_offset ("blue", &values, b_obuffer,
+ b_gbuffer, afe, &old_afe);
+ total_count++;
+ first = SANE_FALSE;
+
+ }
+ while (total_count < 100 && (!red_done || !green_done || !blue_done));
+
+ if (!red_done || !green_done || !blue_done)
+ DBG (0, "gt68xx_afe_cis_auto: setting AFE reached limit\n");
+
+ /* Exposure time */
+ exposure_count = 0;
+ red_done = green_done = blue_done = SANE_FALSE;
+ do
+ {
+ /* read white line */
+ RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_TRUE, SANE_FALSE,
+ r_gbuffer, g_gbuffer, b_gbuffer));
+ if (!red_done)
+ red_done =
+ gt68xx_afe_cis_adjust_exposure ("red", &values, r_gbuffer, 245,
+ &exposure->r_time);
+ if (!green_done)
+ green_done =
+ gt68xx_afe_cis_adjust_exposure ("green", &values, g_gbuffer, 245,
+ &exposure->g_time);
+ if (!blue_done)
+ blue_done =
+ gt68xx_afe_cis_adjust_exposure ("blue", &values, b_gbuffer, 245,
+ &exposure->b_time);
+ exposure_count++;
+ total_count++;
+ }
+ while ((!red_done || !green_done || !blue_done) && exposure_count < 50);
+
+ if (!red_done || !green_done || !blue_done)
+ DBG (0, "gt68xx_afe_cis_auto: setting exposure reached limit\n");
+
+ /* store afe calibration when needed */
+ if(scanner->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)
+ {
+ memcpy(&(scanner->afe_params), afe, sizeof(GT68xx_AFE_Parameters));
+ scanner->exposure_params.r_time=exposure->r_time;
+ scanner->exposure_params.g_time=exposure->g_time;
+ scanner->exposure_params.b_time=exposure->b_time;
+ }
+
+ free (r_gbuffer);
+ free (g_gbuffer);
+ free (b_gbuffer);
+ free (r_obuffer);
+ free (g_obuffer);
+ free (b_obuffer);
+ DBG (4, "gt68xx_afe_cis_auto: total_count: %d\n", total_count);
+
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief create and copy calibrator
+ * Creates a calibrator of the given width and copy data from reference
+ * to initialize it
+ * @param calibator pointer to the calibrator to create
+ * @param reference calibrator with reference data to copy
+ * @param width the width in pixels of the calibrator
+ * @param offset offset in pixels when copying data from reference
+ * @return SANE_STATUS_GOOD and a filled calibrator if enough memory
+ */
+static SANE_Status
+gt68xx_calibrator_create_copy (GT68xx_Calibrator ** calibrator,
+ GT68xx_Calibrator * reference, int width,
+ int offset)
+{
+ SANE_Status status;
+ int i;
+
+ if (reference == NULL)
+ {
+ DBG (1, "gt68xx_calibrator_create_copy: NULL reference, skipping...\n");
+ *calibrator = NULL;
+ return SANE_STATUS_GOOD;
+ }
+ /* check for reference overflow */
+ if(width+offset>reference->width)
+ {
+ DBG (1, "gt68xx_calibrator_create_copy: required with and offset exceed reference width\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = gt68xx_calibrator_new (width, 65535, calibrator);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_calibrator_create_copy: failed to create calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ for(i=0;i<width;i++)
+ {
+ (*calibrator)->k_white[i]=reference->k_white[i+offset];
+ (*calibrator)->k_black[i]=reference->k_black[i+offset];
+ (*calibrator)->white_line[i]=reference->white_line[i+offset];
+ (*calibrator)->black_line[i]=reference->black_line[i+offset];
+ }
+
+ return status;
+}
+
+static SANE_Status
+gt68xx_sheetfed_move_to_scan_area (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * request)
+{
+ SANE_Status status;
+
+ if (!(scanner->dev->model->flags & GT68XX_FLAG_SHEET_FED)
+ || scanner->dev->model->command_set->move_paper == NULL)
+ return SANE_STATUS_GOOD;
+
+ /* send move paper command */
+ RIE (scanner->dev->model->command_set->move_paper (scanner->dev, request));
+
+ /* wait until paper is set to the desired position */
+ return gt68xx_scanner_wait_for_positioning (scanner);
+}
+
+/**< number of consecutive white line to detect a white area */
+#define WHITE_LINES 2
+
+/** @brief calibrate sheet fed scanner
+ * This function calibrates sheet fed scanner by scanning a calibration
+ * target (which may be a blank page). It first move to a white area then
+ * does afe and exposure calibration. Then it scans white lines to get data
+ * for shading correction.
+ * @param scanner structure describing the frontend session and the device
+ * @return SANE_STATUS_GOOD is everything goes right, SANE_STATUS_INVAL
+ * otherwise.
+ */
+static SANE_Status
+gt68xx_sheetfed_scanner_calibrate (GT68xx_Scanner * scanner)
+{
+ SANE_Status status;
+ GT68xx_Scan_Request request;
+ GT68xx_Scan_Parameters params;
+ int count, i, x, y, white;
+ unsigned int *buffer_pointers[3];
+#ifdef DEBUG_CALIBRATION
+ FILE *fcal;
+ char title[50];
+#endif
+
+ DBG (3, "gt68xx_sheetfed_scanner_calibrate: start.\n");
+
+ /* clear calibration if needed */
+ gt68xx_scanner_free_calibrators (scanner);
+ for (i = 0; i < MAX_RESOLUTIONS; i++)
+ {
+ if(scanner->calibrations[i].red!=NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].red);
+ }
+ if(scanner->calibrations[i].green!=NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].green);
+ }
+ if(scanner->calibrations[i].blue!=NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].blue);
+ }
+ if(scanner->calibrations[i].gray!=NULL)
+ {
+ gt68xx_calibrator_free (scanner->calibrations[i].gray);
+ }
+ }
+ scanner->calibrated = SANE_FALSE;
+
+ /* find minimum horizontal resolution */
+ request.xdpi = 9600;
+ for (i = 0; scanner->dev->model->xdpi_values[i] != 0; i++)
+ {
+ if (scanner->dev->model->xdpi_values[i] < request.xdpi)
+ {
+ request.xdpi = scanner->dev->model->xdpi_values[i];
+ request.ydpi = scanner->dev->model->xdpi_values[i];
+ }
+ }
+
+ /* move to white area SA_CALIBRATE uses its own y0/ys fixed values */
+ request.x0 = 0;
+ request.y0 = scanner->dev->model->y_offset_calib;
+ request.xs = scanner->dev->model->x_size;
+ request.depth = 8;
+
+ request.color = SANE_FALSE;
+ request.mbs = SANE_TRUE;
+ request.mds = SANE_TRUE;
+ request.mas = SANE_FALSE;
+ request.lamp = SANE_TRUE;
+ request.calculate = SANE_FALSE;
+ request.use_ta = SANE_FALSE;
+ request.backtrack = SANE_FALSE;
+ request.backtrack_lines = 0;
+
+ /* skip start of calibration sheet */
+ status = gt68xx_sheetfed_move_to_scan_area (scanner, &request);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: failed to skip start of calibration sheet %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: gt68xx_device_lamp_control returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* loop until we find a white area to calibrate on */
+ i = 0;
+ request.y0 = 0;
+ do
+ {
+ /* start scan */
+ status =
+ gt68xx_scanner_start_scan_extended (scanner, &request, SA_CALIBRATE,
+ &params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_start_scan_extended returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* loop until we find WHITE_LINES consecutive white lines or we reach and of area */
+ white = 0;
+ y = 0;
+ do
+ {
+ status = gt68xx_line_reader_read (scanner->reader, buffer_pointers);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: gt68xx_line_reader_read returned %s\n",
+ sane_strstatus (status));
+ gt68xx_scanner_stop_scan (scanner);
+ return status;
+ }
+
+ /* check for white line */
+ count = 0;
+ for (x = 0; x < params.pixel_xs; x++)
+ {
+ if (((buffer_pointers[0][x] >> 8) & 0xff) > 50)
+ {
+ count++;
+ }
+ }
+
+ /* line is white if 93% is above black level */
+ if ((100 * count) / params.pixel_xs < 93)
+ {
+ white = 0;
+ }
+ else
+ {
+ white++;
+ }
+ y++;
+ }
+ while ((white < WHITE_LINES) && (y < params.pixel_ys));
+
+ /* end scan */
+ gt68xx_scanner_stop_scan (scanner);
+
+ i++;
+ }
+ while (i < 20 && white < WHITE_LINES);
+
+ /* check if we found a white area */
+ if (white != WHITE_LINES)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: didn't find a white area\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now do calibration */
+ scanner->auto_afe = SANE_TRUE;
+ scanner->calib = SANE_TRUE;
+
+ /* loop at each possible xdpi to create calibrators */
+ i = 0;
+ while (scanner->dev->model->xdpi_values[i] > 0)
+ {
+ request.xdpi = scanner->dev->model->xdpi_values[i];
+ request.ydpi = scanner->dev->model->xdpi_values[i];
+ request.x0 = 0;
+ request.y0 = 0;
+ request.xs = scanner->dev->model->x_size;
+ request.color = SANE_FALSE;
+ request.mbs = SANE_FALSE;
+ request.mds = SANE_TRUE;
+ request.mas = SANE_FALSE;
+ request.lamp = SANE_TRUE;
+ request.calculate = SANE_FALSE;
+ request.use_ta = SANE_FALSE;
+ request.backtrack = SANE_FALSE;
+ request.backtrack_lines = 0;
+
+ /* calibrate in color */
+ request.color = SANE_TRUE;
+ status = gt68xx_scanner_calibrate (scanner, &request);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_calibrate returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* since auto afe is done at a fixed resolution, we don't need to
+ * do each each time, once is enough */
+ scanner->auto_afe = SANE_FALSE;
+
+ /* allocate and save per dpi calibrators */
+ scanner->calibrations[i].dpi = request.xdpi;
+
+ /* recompute params */
+ request.calculate = SANE_TRUE;
+ gt68xx_device_setup_scan (scanner->dev, &request, SA_SCAN, &params);
+
+ scanner->calibrations[i].pixel_x0 = params.pixel_x0;
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->calibrations[i].red),
+ scanner->cal_r, scanner->cal_r->width,
+ 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: failed to create red calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->calibrations[i].green),
+ scanner->cal_g, scanner->cal_g->width,
+ 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: failed to create green calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->calibrations[i].blue),
+ scanner->cal_b, scanner->cal_b->width,
+ 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: failed to create blue calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* calibrate in gray */
+ request.color = SANE_FALSE;
+ status = gt68xx_scanner_calibrate (scanner, &request);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_calibrate returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (scanner->cal_gray)
+ {
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->calibrations[i].gray),
+ scanner->cal_gray,
+ scanner->cal_gray->width, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_sheetfed_scanner_calibrate: failed to create gray calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+#ifdef DEBUG_CALIBRATION
+ sprintf (title, "cal-%03d-red.pnm", scanner->calibrations[i].dpi);
+ fcal = fopen (title, "wb");
+ if (fcal != NULL)
+ {
+ fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs);
+ for (x = 0; x < params.pixel_xs; x++)
+ fputc ((scanner->calibrations[i].red->k_white[x] >> 8) & 0xff,
+ fcal);
+ fclose (fcal);
+ }
+ sprintf (title, "cal-%03d-green.pnm", scanner->calibrations[i].dpi);
+ fcal = fopen (title, "wb");
+ if (fcal != NULL)
+ {
+ fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs);
+ for (x = 0; x < params.pixel_xs; x++)
+ fputc ((scanner->calibrations[i].green->k_white[x] >> 8) & 0xff,
+ fcal);
+ fclose (fcal);
+ }
+ sprintf (title, "cal-%03d-blue.pnm", scanner->calibrations[i].dpi);
+ fcal = fopen (title, "wb");
+ if (fcal != NULL)
+ {
+ fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs);
+ for (x = 0; x < params.pixel_xs; x++)
+ fputc ((scanner->calibrations[i].blue->k_white[x] >> 8) & 0xff,
+ fcal);
+ fclose (fcal);
+ }
+#endif
+
+ /* next resolution */
+ i++;
+ }
+
+ scanner->calibrated = SANE_TRUE;
+
+ /* eject calibration target from feeder */
+ gt68xx_device_paperfeed (scanner->dev);
+
+ /* save calibration to file */
+ gt68xx_write_calibration (scanner);
+
+ DBG (3, "gt68xx_sheetfed_scanner_calibrate: end.\n");
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief assign calibration for scan
+ * This function creates the calibrators and set up afe for the requested
+ * scan. It uses calibration data that has been created by
+ * gt68xx_sheetfed_scanner_calibrate.
+ * @param scanner structure describing the frontend session and the device
+ * @return SANE_STATUS_GOOD is everything goes right, SANE_STATUS_INVAL
+ * otherwise.
+ */
+static SANE_Status
+gt68xx_assign_calibration (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Parameters params)
+{
+ int i, dpi, offset;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (3, "gt68xx_assign_calibration: start.\n");
+
+ dpi = params.xdpi;
+ DBG (4, "gt68xx_assign_calibration: searching calibration for %d dpi\n",
+ dpi);
+
+ /* search matching dpi */
+ i = 0;
+ while (scanner->calibrations[i].dpi > 0
+ && scanner->calibrations[i].dpi != dpi)
+ {
+ i++;
+ }
+
+ /* check if found a match */
+ if (scanner->calibrations[i].dpi == 0)
+ {
+ DBG (4,
+ "gt68xx_assign_calibration: failed to find calibration for %d dpi\n",
+ dpi);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (4, "gt68xx_assign_calibration: using entry %d for %d dpi\n", i, dpi);
+
+ DBG (5,
+ "gt68xx_assign_calibration: using scan_parameters: pixel_x0=%d, pixel_xs=%d \n",
+ params.pixel_x0, params.pixel_xs);
+
+ /* AFE/exposure data copy */
+ memcpy (scanner->dev->afe, &(scanner->afe_params),
+ sizeof (GT68xx_AFE_Parameters));
+ scanner->dev->exposure->r_time = scanner->exposure_params.r_time;
+ scanner->dev->exposure->g_time = scanner->exposure_params.g_time;
+ scanner->dev->exposure->b_time = scanner->exposure_params.b_time;
+
+ /* free calibrators if needed */
+ gt68xx_scanner_free_calibrators (scanner);
+
+ /* TODO compute offset based on the x0 value from scan_request */
+ offset = params.pixel_x0 - scanner->calibrations[i].pixel_x0;
+
+ /* calibrator allocation and copy */
+ if (scanner->calibrations[i].red!=NULL)
+ {
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->cal_r),
+ scanner->calibrations[i].red,
+ params.pixel_xs,
+ offset);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_assign_calibration: failed to create calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (scanner->calibrations[i].green!=NULL)
+ {
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->cal_g),
+ scanner->calibrations[i].green,
+ params.pixel_xs,
+ offset);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_assign_calibration: failed to create calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (scanner->calibrations[i].blue!=NULL)
+ {
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->cal_b),
+ scanner->calibrations[i].blue,
+ params.pixel_xs,
+ offset);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_assign_calibration: failed to create calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (scanner->calibrations[i].gray!=NULL)
+ {
+ status =
+ gt68xx_calibrator_create_copy (&(scanner->cal_gray),
+ scanner->calibrations[i].gray,
+ params.pixel_xs,
+ offset);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "gt68xx_assign_calibration: failed to create calibrator: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (3, "gt68xx_assign_calibration: end.\n");
+ return status;
+}
+
+static char *gt68xx_calibration_file(GT68xx_Scanner * scanner)
+{
+ char *ptr=NULL;
+ char tmp_str[PATH_MAX];
+
+ ptr=getenv("HOME");
+ if(ptr!=NULL)
+ {
+ sprintf (tmp_str, "%s/.sane/gt68xx-%s.cal", ptr, scanner->dev->model->name);
+ }
+ else
+ {
+ ptr=getenv("TMPDIR");
+ if(ptr!=NULL)
+ {
+ sprintf (tmp_str, "%s/gt68xx-%s.cal", ptr, scanner->dev->model->name);
+ }
+ else
+ {
+ sprintf (tmp_str, "/tmp/gt68xx-%s.cal", scanner->dev->model->name);
+ }
+ }
+ DBG(5,"gt68xx_calibration_file: using >%s< for calibration file name\n",tmp_str);
+ return strdup(tmp_str);
+}
+
+static SANE_Status
+gt68xx_clear_calibration (GT68xx_Scanner * scanner)
+{
+ char *fname;
+ int i;
+
+ if (scanner->calibrated == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+
+ /* clear file */
+ fname = gt68xx_calibration_file (scanner);
+ unlink (fname);
+ free (fname);
+
+ /* free calibrators */
+ for (i = 0; i < MAX_RESOLUTIONS && scanner->calibrations[i].dpi > 0; i++)
+ {
+ scanner->calibrations[i].dpi = 0;
+ if (scanner->calibrations[i].red)
+ gt68xx_calibrator_free (scanner->calibrations[i].red);
+ if (scanner->calibrations[i].green)
+ gt68xx_calibrator_free (scanner->calibrations[i].green);
+ if (scanner->calibrations[i].blue)
+ gt68xx_calibrator_free (scanner->calibrations[i].blue);
+ if (scanner->calibrations[i].gray)
+ gt68xx_calibrator_free (scanner->calibrations[i].gray);
+ }
+
+ /* reset flags */
+ scanner->calibrated = SANE_FALSE;
+ scanner->val[OPT_QUALITY_CAL].w = SANE_FALSE;
+ scanner->val[OPT_NEED_CALIBRATION_SW].w = SANE_TRUE;
+ DBG (5, "gt68xx_clear_calibration: done\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_write_calibration (GT68xx_Scanner * scanner)
+{
+ char *fname;
+ FILE *fcal;
+ int i;
+ SANE_Int nullwidth = 0;
+
+ if (scanner->calibrated == SANE_FALSE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ /* open file */
+ fname = gt68xx_calibration_file (scanner);
+ fcal = fopen (fname, "wb");
+ free (fname);
+ if (fcal == NULL)
+ {
+ DBG (1,
+ "gt68xx_write_calibration: failed to open calibration file for writing %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* TODO we save check endianness and word alignment in case of a home
+ * directory used trough different archs */
+ fwrite (&(scanner->afe_params), sizeof (GT68xx_AFE_Parameters), 1, fcal);
+ fwrite (&(scanner->exposure_params), sizeof (GT68xx_Exposure_Parameters), 1,
+ fcal);
+ for (i = 0; i < MAX_RESOLUTIONS && scanner->calibrations[i].dpi > 0; i++)
+ {
+ DBG (1, "gt68xx_write_calibration: saving %d dpi calibration\n",
+ scanner->calibrations[i].dpi);
+ fwrite (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal);
+ fwrite (&(scanner->calibrations[i].pixel_x0), sizeof (SANE_Int), 1,
+ fcal);
+
+ fwrite (&(scanner->calibrations[i].red->width), sizeof (SANE_Int), 1,
+ fcal);
+ fwrite (&(scanner->calibrations[i].red->white_level), sizeof (SANE_Int),
+ 1, fcal);
+ fwrite (scanner->calibrations[i].red->k_white, sizeof (unsigned int),
+ scanner->calibrations[i].red->width, fcal);
+ fwrite (scanner->calibrations[i].red->k_black, sizeof (unsigned int),
+ scanner->calibrations[i].red->width, fcal);
+ fwrite (scanner->calibrations[i].red->white_line, sizeof (double),
+ scanner->calibrations[i].red->width, fcal);
+ fwrite (scanner->calibrations[i].red->black_line, sizeof (double),
+ scanner->calibrations[i].red->width, fcal);
+
+ fwrite (&(scanner->calibrations[i].green->width), sizeof (SANE_Int), 1,
+ fcal);
+ fwrite (&(scanner->calibrations[i].green->white_level),
+ sizeof (SANE_Int), 1, fcal);
+ fwrite (scanner->calibrations[i].green->k_white, sizeof (unsigned int),
+ scanner->calibrations[i].green->width, fcal);
+ fwrite (scanner->calibrations[i].green->k_black, sizeof (unsigned int),
+ scanner->calibrations[i].green->width, fcal);
+ fwrite (scanner->calibrations[i].green->white_line, sizeof (double),
+ scanner->calibrations[i].green->width, fcal);
+ fwrite (scanner->calibrations[i].green->black_line, sizeof (double),
+ scanner->calibrations[i].green->width, fcal);
+
+ fwrite (&(scanner->calibrations[i].blue->width), sizeof (SANE_Int), 1,
+ fcal);
+ fwrite (&(scanner->calibrations[i].blue->white_level),
+ sizeof (SANE_Int), 1, fcal);
+ fwrite (scanner->calibrations[i].blue->k_white, sizeof (unsigned int),
+ scanner->calibrations[i].blue->width, fcal);
+ fwrite (scanner->calibrations[i].blue->k_black, sizeof (unsigned int),
+ scanner->calibrations[i].blue->width, fcal);
+ fwrite (scanner->calibrations[i].blue->white_line, sizeof (double),
+ scanner->calibrations[i].blue->width, fcal);
+ fwrite (scanner->calibrations[i].blue->black_line, sizeof (double),
+ scanner->calibrations[i].blue->width, fcal);
+
+ if (scanner->calibrations[i].gray != NULL)
+ {
+ fwrite (&(scanner->calibrations[i].gray->width), sizeof (SANE_Int),
+ 1, fcal);
+ fwrite (&(scanner->calibrations[i].gray->white_level),
+ sizeof (SANE_Int), 1, fcal);
+ fwrite (scanner->calibrations[i].gray->k_white,
+ sizeof (unsigned int), scanner->calibrations[i].gray->width,
+ fcal);
+ fwrite (scanner->calibrations[i].gray->k_black,
+ sizeof (unsigned int), scanner->calibrations[i].gray->width,
+ fcal);
+ fwrite (scanner->calibrations[i].gray->white_line, sizeof (double),
+ scanner->calibrations[i].gray->width, fcal);
+ fwrite (scanner->calibrations[i].gray->black_line, sizeof (double),
+ scanner->calibrations[i].gray->width, fcal);
+ }
+ else
+ {
+ fwrite (&nullwidth, sizeof (SANE_Int), 1, fcal);
+ }
+ }
+ DBG (5, "gt68xx_write_calibration: wrote %d calibrations\n", i);
+
+ fclose (fcal);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_read_calibration (GT68xx_Scanner * scanner)
+{
+ char *fname;
+ FILE *fcal;
+ int i;
+ SANE_Int width, level;
+
+ scanner->calibrated = SANE_FALSE;
+ fname = gt68xx_calibration_file (scanner);
+ fcal = fopen (fname, "rb");
+ free (fname);
+ if (fcal == NULL)
+ {
+ DBG (1,
+ "gt68xx_read_calibration: failed to open calibration file for reading %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* TODO we should check endiannes and word alignment in case of a home
+ * directory used trough different archs */
+
+ /* TODO check for errors */
+ fread (&(scanner->afe_params), sizeof (GT68xx_AFE_Parameters), 1, fcal);
+ fread (&(scanner->exposure_params), sizeof (GT68xx_Exposure_Parameters), 1,
+ fcal);
+
+ /* loop on calibrators */
+ i = 0;
+ fread (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal);
+ while (!feof (fcal) && scanner->calibrations[i].dpi > 0)
+ {
+ fread (&(scanner->calibrations[i].pixel_x0), sizeof (SANE_Int), 1,
+ fcal);
+
+ fread (&width, sizeof (SANE_Int), 1, fcal);
+ fread (&level, sizeof (SANE_Int), 1, fcal);
+ gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].red));
+ fread (scanner->calibrations[i].red->k_white, sizeof (unsigned int),
+ width, fcal);
+ fread (scanner->calibrations[i].red->k_black, sizeof (unsigned int),
+ width, fcal);
+ fread (scanner->calibrations[i].red->white_line, sizeof (double), width,
+ fcal);
+ fread (scanner->calibrations[i].red->black_line, sizeof (double), width,
+ fcal);
+
+ fread (&width, sizeof (SANE_Int), 1, fcal);
+ fread (&level, sizeof (SANE_Int), 1, fcal);
+ gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].green));
+ fread (scanner->calibrations[i].green->k_white, sizeof (unsigned int),
+ width, fcal);
+ fread (scanner->calibrations[i].green->k_black, sizeof (unsigned int),
+ width, fcal);
+ fread (scanner->calibrations[i].green->white_line, sizeof (double),
+ width, fcal);
+ fread (scanner->calibrations[i].green->black_line, sizeof (double),
+ width, fcal);
+
+ fread (&width, sizeof (SANE_Int), 1, fcal);
+ fread (&level, sizeof (SANE_Int), 1, fcal);
+ gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].blue));
+ fread (scanner->calibrations[i].blue->k_white, sizeof (unsigned int),
+ width, fcal);
+ fread (scanner->calibrations[i].blue->k_black, sizeof (unsigned int),
+ width, fcal);
+ fread (scanner->calibrations[i].blue->white_line, sizeof (double),
+ width, fcal);
+ fread (scanner->calibrations[i].blue->black_line, sizeof (double),
+ width, fcal);
+
+ fread (&width, sizeof (SANE_Int), 1, fcal);
+ if (width > 0)
+ {
+ fread (&level, sizeof (SANE_Int), 1, fcal);
+ gt68xx_calibrator_new (width, level,
+ &(scanner->calibrations[i].gray));
+ fread (scanner->calibrations[i].gray->k_white,
+ sizeof (unsigned int), width, fcal);
+ fread (scanner->calibrations[i].gray->k_black,
+ sizeof (unsigned int), width, fcal);
+ fread (scanner->calibrations[i].gray->white_line, sizeof (double),
+ width, fcal);
+ fread (scanner->calibrations[i].gray->black_line, sizeof (double),
+ width, fcal);
+ }
+ /* prepare for nex resolution */
+ i++;
+ fread (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal);
+ }
+
+ DBG (5, "gt68xx_read_calibration: read %d calibrations\n", i);
+ fclose (fcal);
+
+ scanner->val[OPT_QUALITY_CAL].w = SANE_TRUE;
+ scanner->val[OPT_NEED_CALIBRATION_SW].w = SANE_FALSE;
+ scanner->calibrated = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_high.h b/backend/gt68xx_high.h
new file mode 100644
index 0000000..d830e96
--- /dev/null
+++ b/backend/gt68xx_high.h
@@ -0,0 +1,374 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002-2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_HIGH_H
+#define GT68XX_HIGH_H
+
+#include "gt68xx_mid.h"
+
+typedef struct GT68xx_Calibrator GT68xx_Calibrator;
+typedef struct GT68xx_Calibration GT68xx_Calibration;
+typedef struct GT68xx_Scanner GT68xx_Scanner;
+
+/** Calibration data for one channel.
+ */
+struct GT68xx_Calibrator
+{
+ unsigned int *k_white; /**< White point vector */
+ unsigned int *k_black; /**< Black point vector */
+
+ double *white_line; /**< White average */
+ double *black_line; /**< Black average */
+
+ SANE_Int width; /**< Image width */
+ SANE_Int white_level; /**< Desired white level */
+
+ SANE_Int white_count; /**< Number of white lines scanned */
+ SANE_Int black_count; /**< Number of black lines scanned */
+
+#ifdef TUNE_CALIBRATOR
+ SANE_Int min_clip_count; /**< Count of too low values */
+ SANE_Int max_clip_count; /**< Count of too high values */
+#endif /* TUNE_CALIBRATOR */
+};
+
+
+/** Calibration data for a given resolution
+ */
+struct GT68xx_Calibration
+{
+ SANE_Int dpi; /**< optical horizontal dpi used to
+ build the calibration data */
+ SANE_Int pixel_x0; /**< x start position used at calibration time */
+
+ GT68xx_Calibrator *gray; /**< Calibrator for grayscale data */
+ GT68xx_Calibrator *red; /**< Calibrator for the red channel */
+ GT68xx_Calibrator *green; /**< Calibrator for the green channel */
+ GT68xx_Calibrator *blue; /**< Calibrator for the blue channel */
+};
+
+/** Create a new calibrator for one (color or mono) channel.
+ *
+ * @param width Image width in pixels.
+ * @param white_level Desired white level (65535 max).
+ * @param cal_return Returned pointer to the created calibrator object.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - the calibrator object was created.
+ * - SANE_STATUS_INVAL - invalid parameters.
+ * - SANE_STATUS_NO_MEM - not enough memory to create the object.
+ */
+static SANE_Status
+gt68xx_calibrator_new (SANE_Int width,
+ SANE_Int white_level, GT68xx_Calibrator ** cal_return);
+
+/** Destroy the channel calibrator object.
+ *
+ * @param cal Calibrator object.
+ */
+static SANE_Status gt68xx_calibrator_free (GT68xx_Calibrator * cal);
+
+/** Add a white calibration line to the calibrator.
+ *
+ * This function should be called after scanning each white calibration line.
+ * The line width must be equal to the value passed to gt68xx_calibrator_new().
+ *
+ * @param cal Calibrator object.
+ * @param line Pointer to the line data.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the line data was processed successfully.
+ */
+static SANE_Status
+gt68xx_calibrator_add_white_line (GT68xx_Calibrator * cal,
+ unsigned int *line);
+
+/** Calculate the white point for the calibrator.
+ *
+ * This function should be called when all white calibration lines have been
+ * scanned. After doing this, gt68xx_calibrator_add_white_line() should not be
+ * called again for this calibrator.
+ *
+ * @param cal Calibrator object.
+ * @param factor White point correction factor.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the white point was calculated successfully.
+ */
+static SANE_Status
+gt68xx_calibrator_eval_white (GT68xx_Calibrator * cal, double factor);
+
+/** Add a black calibration line to the calibrator.
+ *
+ * This function should be called after scanning each black calibration line.
+ * The line width must be equal to the value passed to gt68xx_calibrator_new().
+ *
+ * @param cal Calibrator object.
+ * @param line Pointer to the line data.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the line data was processed successfully.
+ */
+static SANE_Status
+gt68xx_calibrator_add_black_line (GT68xx_Calibrator * cal,
+ unsigned int *line);
+
+/** Calculate the black point for the calibrator.
+ *
+ * This function should be called when all black calibration lines have been
+ * scanned. After doing this, gt68xx_calibrator_add_black_line() should not be
+ * called again for this calibrator.
+ *
+ * @param cal Calibrator object.
+ * @param factor Black point correction factor.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the white point was calculated successfully.
+ */
+static SANE_Status
+gt68xx_calibrator_eval_black (GT68xx_Calibrator * cal, double factor);
+
+/** Finish the calibrator setup and prepare for real scanning.
+ *
+ * This function must be called after gt68xx_calibrator_eval_white() and
+ * gt68xx_calibrator_eval_black().
+ *
+ * @param cal Calibrator object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the calibrator setup completed successfully.
+ */
+static SANE_Status gt68xx_calibrator_finish_setup (GT68xx_Calibrator * cal);
+
+/** Process the image line through the calibrator.
+ *
+ * This function must be called only after gt68xx_calibrator_finish_setup().
+ * The image line is modified in place.
+ *
+ * @param cal Calibrator object.
+ * @param line Pointer to the image line data.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the image line was processed successfully.
+ */
+static SANE_Status
+gt68xx_calibrator_process_line (GT68xx_Calibrator * cal, unsigned int *line);
+
+/** List of SANE options
+ */
+enum GT68xx_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_GRAY_MODE_COLOR,
+ OPT_SOURCE,
+ OPT_PREVIEW,
+ OPT_BIT_DEPTH,
+ OPT_RESOLUTION,
+ OPT_LAMP_OFF_AT_EXIT,
+ OPT_BACKTRACK,
+
+ OPT_DEBUG_GROUP,
+ OPT_AUTO_WARMUP,
+ OPT_FULL_SCAN,
+ OPT_COARSE_CAL,
+ OPT_COARSE_CAL_ONCE,
+ OPT_QUALITY_CAL,
+ OPT_BACKTRACK_LINES,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_GAMMA_VALUE,
+ OPT_THRESHOLD,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_SENSOR_GROUP,
+ OPT_NEED_CALIBRATION_SW, /* signals calibration is needed */
+ OPT_PAGE_LOADED_SW, /* signals that a document is inserted in feeder */
+
+ OPT_BUTTON_GROUP,
+ OPT_CALIBRATE, /* button option to trigger call
+ to sheetfed calibration */
+ OPT_CLEAR_CALIBRATION, /* clear calibration */
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+/** Scanner object.
+ */
+struct GT68xx_Scanner
+{
+ struct GT68xx_Scanner *next; /**< Next scanner in list */
+ GT68xx_Device *dev; /**< Low-level device object */
+
+ GT68xx_Line_Reader *reader; /**< Line reader object */
+
+ GT68xx_Calibrator *cal_gray; /**< Calibrator for grayscale data */
+ GT68xx_Calibrator *cal_r; /**< Calibrator for the red channel */
+ GT68xx_Calibrator *cal_g; /**< Calibrator for the green channel */
+ GT68xx_Calibrator *cal_b; /**< Calibrator for the blue channel */
+
+ /* SANE data */
+ SANE_Bool scanning; /**< We are currently scanning */
+ SANE_Option_Descriptor opt[NUM_OPTIONS]; /**< Option descriptors */
+ Option_Value val[NUM_OPTIONS]; /**< Option values */
+ SANE_Parameters params; /**< SANE Parameters */
+ SANE_Int line; /**< Current line */
+ SANE_Int total_bytes; /**< Bytes already transmitted */
+ SANE_Int byte_count; /**< Bytes transmitted in this line */
+ SANE_Bool calib; /**< Apply calibration data */
+ SANE_Bool auto_afe; /**< Use automatic gain/offset */
+ SANE_Bool first_scan; /**< Is this the first scan? */
+ struct timeval lamp_on_time; /**< Time when the lamp was turned on */
+ struct timeval start_time; /**< Time when the scan was started */
+ SANE_Int bpp_list[5]; /**< */
+ SANE_Int *gamma_table; /**< Gray gamma table */
+#ifdef DEBUG_BRIGHTNESS
+ SANE_Int average_white; /**< For debugging brightness problems */
+ SANE_Int max_white;
+ SANE_Int min_black;
+#endif
+
+ /** SANE_TRUE when the scanner has been calibrated */
+ SANE_Bool calibrated;
+
+ /** per horizontal resolution calibration data */
+ GT68xx_Calibration calibrations[MAX_RESOLUTIONS];
+
+ /* AFE and exposure settings */
+ GT68xx_AFE_Parameters afe_params;
+ GT68xx_Exposure_Parameters exposure_params;
+};
+
+
+/** Create a new scanner object.
+ *
+ * @param dev Low-level device object.
+ * @param scanner_return Returned pointer to the created scanner object.
+ */
+static SANE_Status
+gt68xx_scanner_new (GT68xx_Device * dev, GT68xx_Scanner ** scanner_return);
+
+/** Destroy the scanner object.
+ *
+ * The low-level device object is not destroyed.
+ *
+ * @param scanner Scanner object.
+ */
+static SANE_Status gt68xx_scanner_free (GT68xx_Scanner * scanner);
+
+/** Calibrate the scanner before the main scan.
+ *
+ * @param scanner Scanner object.
+ * @param request Scan request data.
+ * @param use_autogain Enable automatic offset/gain control
+ */
+static SANE_Status
+gt68xx_scanner_calibrate (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * request);
+
+/** Start scanning the image.
+ *
+ * This function does not perform calibration - it needs to be performed before
+ * by calling gt68xx_scanner_calibrate().
+ *
+ * @param scanner Scanner object.
+ * @param request Scan request data.
+ * @param params Returned scan parameters (calculated from the request).
+ */
+static SANE_Status
+gt68xx_scanner_start_scan (GT68xx_Scanner * scanner,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Parameters * params);
+
+/** Read one image line from the scanner.
+ *
+ * This function can be called only during the scan - after calling
+ * gt68xx_scanner_start_scan() and before calling gt68xx_scanner_stop_scan().
+ *
+ * @param scanner Scanner object.
+ * @param buffer_pointers Array of pointers to the image lines.
+ */
+static SANE_Status
+gt68xx_scanner_read_line (GT68xx_Scanner * scanner,
+ unsigned int **buffer_pointers);
+
+/** Stop scanning the image.
+ *
+ * This function must be called to finish the scan started by
+ * gt68xx_scanner_start_scan(). It may be called before all lines are read to
+ * cancel the scan prematurely.
+ *
+ * @param scanner Scanner object.
+ */
+static SANE_Status gt68xx_scanner_stop_scan (GT68xx_Scanner * scanner);
+
+/** Save calibration data to file
+ *
+ * This function stores in memory calibration data created at calibration
+ * time into file
+ * @param scanner Scanner object.
+ * @return SANE_STATUS_GOOD when succesfull
+ */
+static SANE_Status gt68xx_write_calibration (GT68xx_Scanner * scanner);
+
+/** Read calibration data from file
+ *
+ * This function sets in memory calibration data from data saved into file.
+ *
+ * @param scanner Scanner object.
+ * @return SANE_STATUS_GOOD when succesfull
+ */
+static SANE_Status gt68xx_read_calibration (GT68xx_Scanner * scanner);
+
+#endif /* not GT68XX_HIGH_H */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_low.c b/backend/gt68xx_low.c
new file mode 100644
index 0000000..6060c45
--- /dev/null
+++ b/backend/gt68xx_low.c
@@ -0,0 +1,1034 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ * @brief Implementation of the low-level scanner interface functions.
+ */
+
+#include "gt68xx_low.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_usb.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_FORK
+#include <sys/wait.h>
+#include <unistd.h>
+#include "gt68xx_shm_channel.c"
+#endif
+
+/** Check that the device pointer is not NULL.
+ *
+ * @param dev Pointer to the device object (GT68xx_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_NOT_NULL(dev, func_name) \
+ do { \
+ IF_DBG( \
+ if (!(dev)) \
+ { \
+ DBG (0, "BUG: NULL device\n"); \
+ return SANE_STATUS_INVAL; \
+ } \
+ ) \
+ } while (SANE_FALSE)
+
+/** Check that the device is open.
+ *
+ * @param dev Pointer to the device object (GT68xx_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_OPEN(dev, func_name) \
+ do { \
+ IF_DBG( \
+ CHECK_DEV_NOT_NULL ((dev), (func_name)); \
+ if ((dev)->fd == -1) \
+ { \
+ DBG (0, "%s: BUG: device %p not open\n", (func_name), \
+ ((void *) dev)); \
+ return SANE_STATUS_INVAL; \
+ } \
+ ) \
+ } while (SANE_FALSE)
+
+/** Check that the device is open and active.
+ *
+ * @param dev Pointer to the device (GT68xx_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_ACTIVE(dev, func_name) \
+ do { \
+ IF_DBG( \
+ CHECK_DEV_OPEN ((dev), (func_name)); \
+ if (!(dev)->active) \
+ { \
+ DBG (0, "%s: BUG: device %p not active\n", (func_name), \
+ ((void *) dev)); \
+ return SANE_STATUS_INVAL; \
+ } \
+ ) \
+ } while (SANE_FALSE)
+
+
+#ifndef NDEBUG
+
+/** Dump a request packet for debugging purposes.
+ *
+ * @param prefix String printed before the packet contents.
+ * @param req The request packet to be dumped.
+ */
+static void
+dump_req (SANE_String_Const prefix, GT68xx_Packet req)
+{
+ int i;
+ char buf[GT68XX_PACKET_SIZE * 3 + 1];
+
+ for (i = 0; i < GT68XX_PACKET_SIZE; ++i)
+ sprintf (buf + i * 3, " %02x", req[i]);
+ DBG (8, "%s%s\n", prefix, buf);
+}
+
+#endif /* not NDEBUG */
+
+/** Dump a request packet if the debug level is at 8 or above.
+ *
+ * @param prefix String printed before the packet contents.
+ * @param req The request packet to be dumped.
+ */
+#define DUMP_REQ(prefix, req) \
+ do { IF_DBG( if (DBG_LEVEL >= 8) dump_req ((prefix), (req)); ) } while (0)
+
+
+SANE_Status
+gt68xx_device_new (GT68xx_Device ** dev_return)
+{
+ GT68xx_Device *dev;
+
+ DBG (7, "gt68xx_device_new: enter\n");
+ if (!dev_return)
+ return SANE_STATUS_INVAL;
+
+ dev = (GT68xx_Device *) malloc (sizeof (GT68xx_Device));
+
+ if (!dev)
+ {
+ DBG (3, "gt68xx_device_new: couldn't malloc %lu bytes for device\n",
+ (u_long) sizeof (GT68xx_Device));
+ *dev_return = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ *dev_return = dev;
+
+ memset (dev, 0, sizeof (GT68xx_Device));
+
+ dev->fd = -1;
+ dev->active = SANE_FALSE;
+
+ dev->model = NULL;
+ dev->command_set_private = NULL;
+
+ dev->read_buffer = NULL;
+ dev->read_buffer_size = 32768;
+
+ dev->manual_selection = SANE_FALSE;
+
+#ifdef USE_FORK
+ dev->shm_channel = NULL;
+#endif /* USE_FORK */
+
+ DBG (7, "gt68xx_device_new:: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_free (GT68xx_Device * dev)
+{
+ DBG (7, "gt68xx_device_free: enter: dev=%p\n", (void *) dev);
+ if (dev)
+ {
+ if (dev->active)
+ gt68xx_device_deactivate (dev);
+
+ if (dev->fd != -1)
+ gt68xx_device_close (dev);
+
+ if (dev->model && dev->model->allocated)
+ {
+ DBG (7, "gt68xx_device_free: freeing model data %p\n",
+ (void *) dev->model);
+ free (dev->model);
+ }
+
+ DBG (7, "gt68xx_device_free: freeing dev\n");
+ free (dev);
+ }
+ DBG (7, "gt68xx_device_free: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+static GT68xx_USB_Device_Entry *
+gt68xx_find_usb_device_entry (SANE_Word vendor, SANE_Word product)
+{
+ GT68xx_USB_Device_Entry *entry;
+
+ for (entry = gt68xx_usb_device_list; entry->model; ++entry)
+ {
+ if (vendor == entry->vendor && product == entry->product)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static SANE_Status
+gt68xx_device_identify (GT68xx_Device * dev)
+{
+ SANE_Status status;
+ SANE_Word vendor, product;
+ GT68xx_USB_Device_Entry *entry;
+
+ CHECK_DEV_OPEN (dev, "gt68xx_device_identify");
+
+ status = sanei_usb_get_vendor_product (dev->fd, &vendor, &product);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_identify: error getting USB id: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ entry = gt68xx_find_usb_device_entry (vendor, product);
+
+ if (entry)
+ {
+ dev->model = entry->model;
+ }
+ else
+ {
+ dev->model = NULL;
+ DBG (3, "gt68xx_device_identify: unknown USB device (vendor 0x%04x, "
+ "product 0x%04x)\n", vendor, product);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_open (GT68xx_Device * dev, const char *dev_name)
+{
+ SANE_Status status;
+ SANE_Int fd;
+
+ DBG (7, "gt68xx_device_open: enter: dev=%p\n", (void *) dev);
+
+ CHECK_DEV_NOT_NULL (dev, "gt68xx_device_open");
+
+ if (dev->fd != -1)
+ {
+ DBG (3, "gt68xx_device_open: device already open\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_usb_open (dev_name, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_open: sanei_usb_open failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->fd = fd;
+
+ if (!dev->model)
+ gt68xx_device_identify (dev);
+
+ DBG (7, "gt68xx_device_open: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_close (GT68xx_Device * dev)
+{
+ DBG (7, "gt68xx_device_close: enter: dev=%p\n", (void *) dev);
+
+ CHECK_DEV_OPEN (dev, "gt68xx_device_close");
+
+ if (dev->active)
+ gt68xx_device_deactivate (dev);
+
+ sanei_usb_close (dev->fd);
+ dev->fd = -1;
+
+ DBG (7, "gt68xx_device_close: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Bool
+gt68xx_device_is_configured (GT68xx_Device * dev)
+{
+ if (dev && dev->model && dev->model->command_set)
+ return SANE_TRUE;
+ else
+ return SANE_FALSE;
+}
+
+SANE_Status
+gt68xx_device_set_model (GT68xx_Device * dev, GT68xx_Model * model)
+{
+ if (dev->active)
+ {
+ DBG (3, "gt68xx_device_set_model: device already active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->model && dev->model->allocated)
+ free (dev->model);
+
+ dev->model = model;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Bool
+gt68xx_device_get_model (SANE_String name, GT68xx_Model ** model)
+{
+ GT68xx_USB_Device_Entry *entry;
+
+ for (entry = gt68xx_usb_device_list; entry->model; ++entry)
+ {
+ if (strcmp (name, entry->model->name) == 0)
+ {
+ *model = entry->model;
+ return SANE_TRUE;
+ }
+ }
+ return SANE_FALSE;
+}
+
+
+SANE_Status
+gt68xx_device_activate (GT68xx_Device * dev)
+{
+ SANE_Status status;
+ CHECK_DEV_OPEN (dev, "gt68xx_device_activate");
+ if (dev->active)
+ {
+ DBG (3, "gt68xx_device_activate: device already active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!gt68xx_device_is_configured (dev))
+ {
+ DBG (3, "gt68xx_device_activate: device is not configured\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (7, "gt68xx_device_activate: model \"%s\"\n", dev->model->name);
+ if (dev->model->command_set->activate)
+ {
+ status = (*dev->model->command_set->activate) (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_activate: command-set-specific "
+ "activate failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ }
+ dev->afe = malloc (sizeof (*dev->afe));
+ dev->exposure = malloc (sizeof (*dev->exposure));
+ if (!dev->afe || !dev->exposure)
+ return SANE_STATUS_NO_MEM;
+ memcpy (dev->afe, &dev->model->afe_params, sizeof (*dev->afe));
+ memcpy (dev->exposure, &dev->model->exposure, sizeof (*dev->exposure));
+ dev->gamma_value = dev->model->default_gamma_value;
+ dev->active = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_deactivate (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_deactivate");
+ if (dev->read_active)
+ gt68xx_device_read_finish (dev);
+ if (dev->model->command_set->deactivate)
+ {
+ status = (*dev->model->command_set->deactivate) (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_deactivate: command set-specific deactivate failed: %s\n",
+ sane_strstatus (status));
+ /* proceed with deactivate anyway */
+ }
+ }
+ if (dev->afe)
+ free (dev->afe);
+ dev->afe = 0;
+ if (dev->exposure)
+ free (dev->exposure);
+ dev->exposure = 0;
+ dev->active = SANE_FALSE;
+ return status;
+}
+
+SANE_Status
+gt68xx_device_memory_write (GT68xx_Device * dev,
+ SANE_Word addr, SANE_Word size, SANE_Byte * data)
+{
+ SANE_Status status;
+ DBG (8,
+ "gt68xx_device_memory_write: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ (void *) dev, addr, size, data);
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_memory_write");
+ status =
+ sanei_usb_control_msg (dev->fd, 0x40,
+ dev->model->command_set->request,
+ dev->model->command_set->memory_write_value,
+ addr, size, data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_memory_write: sanei_usb_control_msg failed: %s\n",
+ sane_strstatus (status));
+ }
+ return status;
+}
+
+SANE_Status
+gt68xx_device_memory_read (GT68xx_Device * dev,
+ SANE_Word addr, SANE_Word size, SANE_Byte * data)
+{
+ SANE_Status status;
+ DBG (8,
+ "gt68xx_device_memory_read: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ (void *) dev, addr, size, data);
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_memory_read");
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc0,
+ dev->model->command_set->request,
+ dev->model->command_set->memory_read_value,
+ addr, size, data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_memory_read: sanei_usb_control_msg failed: %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+static SANE_Status
+gt68xx_device_generic_req (GT68xx_Device * dev,
+ SANE_Byte request_type, SANE_Word request,
+ SANE_Word cmd_value, SANE_Word cmd_index,
+ SANE_Word res_value, SANE_Word res_index,
+ GT68xx_Packet cmd, GT68xx_Packet res,
+ size_t res_size)
+{
+ SANE_Status status;
+ DBG (7, "gt68xx_device_generic_req: command=0x%02x\n", cmd[0]);
+ DUMP_REQ (">>", cmd);
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_generic_req");
+ status = sanei_usb_control_msg (dev->fd,
+ request_type, request, cmd_value,
+ cmd_index, GT68XX_PACKET_SIZE, cmd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_generic_req: writing command failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ memset (res, 0, sizeof (GT68xx_Packet));
+ status = sanei_usb_control_msg (dev->fd,
+ request_type | 0x80, request,
+ res_value, res_index, res_size, res);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_generic_req: reading response failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DUMP_REQ ("<<", res);
+ return status;
+}
+
+SANE_Status
+gt68xx_device_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res)
+{
+ GT68xx_Command_Set *command_set = dev->model->command_set;
+ return gt68xx_device_generic_req (dev,
+ command_set->request_type,
+ command_set->request,
+ command_set->send_cmd_value,
+ command_set->send_cmd_index,
+ command_set->recv_res_value,
+ command_set->recv_res_index, cmd,
+ res, GT68XX_PACKET_SIZE);
+}
+
+SANE_Status
+gt68xx_device_small_req (GT68xx_Device * dev, GT68xx_Packet cmd,
+ GT68xx_Packet res)
+{
+ GT68xx_Command_Set *command_set = dev->model->command_set;
+ GT68xx_Packet fixed_cmd;
+ int i;
+ for (i = 0; i < 8; ++i)
+ memcpy (fixed_cmd + i * 8, cmd, 8);
+ return gt68xx_device_generic_req (dev,
+ command_set->request_type,
+ command_set->request,
+ command_set->send_small_cmd_value,
+ command_set->send_small_cmd_index,
+ command_set->recv_small_res_value,
+ command_set->recv_small_res_index,
+ fixed_cmd, res, 0x08);
+}
+
+
+SANE_Status
+gt68xx_device_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_download_firmware");
+ if (dev->model->command_set->download_firmware)
+ return (*dev->model->command_set->download_firmware) (dev, data, size);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_power_status");
+ if (dev->model->command_set->get_power_status)
+ return (*dev->model->command_set->get_power_status) (dev, power_ok);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_ta_status");
+ if (dev->model->command_set->get_ta_status)
+ return (*dev->model->command_set->get_ta_status) (dev, ta_attached);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_lamp_control");
+ if (dev->model->command_set->lamp_control)
+ return (*dev->model->command_set->lamp_control) (dev, fb_lamp, ta_lamp);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_is_moving (GT68xx_Device * dev, SANE_Bool * moving)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_is_moving");
+ if (dev->model->command_set->is_moving)
+ return (*dev->model->command_set->is_moving) (dev, moving);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* currently not used */
+#if 0
+static SANE_Status
+gt68xx_device_move_relative (GT68xx_Device * dev, SANE_Int distance)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_move_relative");
+ if (dev->model->command_set->move_relative)
+ return (*dev->model->command_set->move_relative) (dev, distance);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+#endif
+
+SANE_Status
+gt68xx_device_carriage_home (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_carriage_home");
+ if (dev->model->command_set->carriage_home)
+ return (*dev->model->command_set->carriage_home) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_paperfeed (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_paperfeed");
+ if (dev->model->command_set->paperfeed)
+ return (*dev->model->command_set->paperfeed) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_start_scan (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_start_scan");
+ if (dev->model->command_set->start_scan)
+ return (*dev->model->command_set->start_scan) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_scanned_data");
+ if (dev->model->command_set->read_scanned_data)
+ return (*dev->model->command_set->read_scanned_data) (dev, ready);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_setup_scan (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_setup_scan");
+ if (dev->model->command_set->setup_scan)
+ return (*dev->model->command_set->setup_scan) (dev, request, action,
+ params);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_set_afe");
+ if (dev->model->command_set->set_afe)
+ return (*dev->model->command_set->set_afe) (dev, params);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_set_exposure_time (GT68xx_Device * dev,
+ GT68xx_Exposure_Parameters * params)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_set_exposure_time");
+ if (dev->model->command_set->set_exposure_time)
+ return (*dev->model->command_set->set_exposure_time) (dev, params);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_stop_scan (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_stop_scan");
+ if (dev->model->command_set->stop_scan)
+ return (*dev->model->command_set->stop_scan) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_read_raw (GT68xx_Device * dev, SANE_Byte * buffer,
+ size_t * size)
+{
+ SANE_Status status;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_raw");
+ DBG (7, "gt68xx_device_read_raw: enter: size=%lu\n", (unsigned long) *size);
+ status = sanei_usb_read_bulk (dev->fd, buffer, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_read_raw: bulk read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (7, "gt68xx_device_read_raw: leave: size=%lu\n", (unsigned long) *size);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_set_read_buffer_size (GT68xx_Device * dev, size_t buffer_size)
+{
+ CHECK_DEV_NOT_NULL (dev, "gt68xx_device_set_read_buffer_size");
+ if (dev->read_active)
+ {
+ DBG (3, "gt68xx_device_set_read_buffer_size: BUG: read already "
+ "active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ buffer_size = (buffer_size + 63UL) & ~63UL;
+ if (buffer_size > 0)
+ {
+ dev->requested_buffer_size = buffer_size;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "gt68xx_device_set_read_buffer_size: bad buffer size\n");
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+gt68xx_device_read_prepare (GT68xx_Device * dev,
+ size_t expected_count, SANE_Bool final_scan)
+{
+ size_t buffer_size;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_prepare");
+ if (dev->read_active)
+ {
+ DBG (3, "gt68xx_device_read_prepare: read already active\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "gt68xx_device_read_prepare: total size: %lu bytes\n",
+ (unsigned long) expected_count);
+ buffer_size = dev->requested_buffer_size;
+ DBG (5, "gt68xx_device_read_prepare: requested buffer size: %lu\n",
+ (unsigned long) buffer_size);
+ if (buffer_size > expected_count)
+ {
+ buffer_size = (expected_count + 63UL) & ~63UL;
+ }
+ DBG (5, "gt68xx_device_read_prepare: real size: %lu\n",
+ (unsigned long) buffer_size);
+ dev->read_buffer_size = buffer_size;
+ dev->read_buffer = (SANE_Byte *) malloc (buffer_size);
+ if (!dev->read_buffer)
+ {
+ DBG (3,
+ "gt68xx_device_read_prepare: not enough memory for the read buffer (%lu bytes)\n",
+ (unsigned long) buffer_size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->read_active = SANE_TRUE;
+ dev->final_scan = final_scan;
+ dev->read_pos = dev->read_bytes_in_buffer = 0;
+ dev->read_bytes_left = expected_count;
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef USE_FORK
+
+static SANE_Status
+gt68xx_reader_process (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int buffer_id;
+ SANE_Byte *buffer_addr;
+ size_t size;
+ SANE_Int line = 0;
+ size_t read_bytes_left = dev->read_bytes_left;
+ shm_channel_writer_init (dev->shm_channel);
+ while (read_bytes_left > 0)
+ {
+ status = shm_channel_writer_get_buffer (dev->shm_channel,
+ &buffer_id, &buffer_addr);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ DBG (9, "gt68xx_reader_process: buffer %d: get\n", buffer_id);
+ size = dev->read_buffer_size;
+ DBG (9, "gt68xx_reader_process: buffer %d: trying to read %lu bytes "
+ "(%lu bytes left, line %d)\n", buffer_id, (unsigned long) size,
+ (unsigned long) read_bytes_left, line);
+ status = gt68xx_device_read_raw (dev, buffer_addr, &size);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ DBG (9,
+ "gt68xx_reader_process: buffer %d: read %lu bytes (line %d)\n",
+ buffer_id, (unsigned long) size, line);
+ status =
+ shm_channel_writer_put_buffer (dev->shm_channel, buffer_id, size);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ DBG (9, "gt68xx_reader_process: buffer %d: put\n", buffer_id);
+ read_bytes_left -= size;
+ line++;
+ }
+ DBG (9, "gt68xx_reader_process: finished, now sleeping\n");
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ sleep (5 * 60); /* wait until we are killed (or timeout) */
+ shm_channel_writer_close (dev->shm_channel);
+ return status;
+}
+
+static SANE_Status
+gt68xx_device_read_start_fork (GT68xx_Device * dev)
+{
+ SANE_Status status;
+ int pid;
+ if (dev->shm_channel)
+ {
+ DBG (3,
+ "gt68xx_device_read_start_fork: BUG: shm_channel already created\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status =
+ shm_channel_new (dev->read_buffer_size, SHM_BUFFERS, &dev->shm_channel);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_read_start_fork: cannot create shared memory channel: "
+ "%s\n", sane_strstatus (status));
+ dev->shm_channel = NULL;
+ return status;
+ }
+
+ pid = fork ();
+ if (pid == -1)
+ {
+ DBG (3, "gt68xx_device_read_start_fork: cannot fork: %s\n",
+ strerror (errno));
+ shm_channel_free (dev->shm_channel);
+ dev->shm_channel = NULL;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (pid == 0)
+ {
+ /* Child process */
+ status = gt68xx_reader_process (dev);
+ _exit (status);
+ }
+ else
+ {
+ /* Parent process */
+ dev->reader_pid = pid;
+ shm_channel_reader_init (dev->shm_channel);
+ shm_channel_reader_start (dev->shm_channel);
+ return SANE_STATUS_GOOD;
+ }
+}
+
+#endif /* USE_FORK */
+
+
+static SANE_Status
+gt68xx_device_read_start (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_start");
+#ifdef USE_FORK
+ /* Don't fork a separate process for every calibration scan. */
+ if (dev->final_scan)
+ return gt68xx_device_read_start_fork (dev);
+#endif /* USE_FORK */
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_read (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size)
+{
+ SANE_Status status;
+ size_t byte_count = 0;
+ size_t left_to_read = *size;
+ size_t transfer_size, block_size, raw_block_size;
+#ifdef USE_FORK
+ SANE_Int buffer_id;
+ SANE_Byte *buffer_addr;
+ SANE_Int buffer_bytes;
+#endif /* USE_FORK */
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read");
+ if (!dev->read_active)
+ {
+ DBG (3, "gt68xx_device_read: read not active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ while (left_to_read > 0)
+ {
+ if (dev->read_bytes_in_buffer == 0)
+ {
+ block_size = dev->read_buffer_size;
+ if (block_size > dev->read_bytes_left)
+ block_size = dev->read_bytes_left;
+ if (block_size == 0)
+ break;
+ raw_block_size = (block_size + 63UL) & ~63UL;
+ DBG (7, "gt68xx_device_read: trying to read %ld bytes\n",
+ (long) raw_block_size);
+#ifdef USE_FORK
+ if (dev->shm_channel)
+ {
+ status = shm_channel_reader_get_buffer (dev->shm_channel,
+ &buffer_id,
+ &buffer_addr,
+ &buffer_bytes);
+ if (status == SANE_STATUS_GOOD && buffer_addr != NULL)
+ {
+ DBG (9, "gt68xx_device_read: buffer %d: get\n", buffer_id);
+ memcpy (dev->read_buffer, buffer_addr, buffer_bytes);
+ shm_channel_reader_put_buffer (dev->shm_channel, buffer_id);
+ DBG (9, "gt68xx_device_read: buffer %d: put\n", buffer_id);
+ }
+ }
+ else
+#endif /* USE_FORK */
+ status = gt68xx_device_read_raw (dev, dev->read_buffer,
+ &raw_block_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_read: read failed\n");
+ return status;
+ }
+ dev->read_pos = 0;
+ dev->read_bytes_in_buffer = block_size;
+ dev->read_bytes_left -= block_size;
+ }
+
+ transfer_size = left_to_read;
+ if (transfer_size > dev->read_bytes_in_buffer)
+ transfer_size = dev->read_bytes_in_buffer;
+ if (transfer_size > 0)
+ {
+ memcpy (buffer, dev->read_buffer + dev->read_pos, transfer_size);
+ dev->read_pos += transfer_size;
+ dev->read_bytes_in_buffer -= transfer_size;
+ byte_count += transfer_size;
+ left_to_read -= transfer_size;
+ buffer += transfer_size;
+ }
+ }
+
+ *size = byte_count;
+ if (byte_count == 0)
+ return SANE_STATUS_EOF;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_read_finish (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_finish");
+ if (!dev->read_active)
+ {
+ DBG (3, "gt68xx_device_read_finish: read not active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (7, "gt68xx_device_read_finish: read_bytes_left = %ld\n",
+ (long) dev->read_bytes_left);
+#ifdef USE_FORK
+ if (dev->reader_pid != 0)
+ {
+ int pid_status;
+ /* usleep (100000); */
+ DBG (7, "gt68xx_device_read_finish: trying to kill reader process\n");
+ kill (dev->reader_pid, SIGKILL);
+ waitpid (dev->reader_pid, &pid_status, 0);
+ if (WIFEXITED (pid_status))
+ status = WEXITSTATUS (pid_status);
+ DBG (7, "gt68xx_device_read_finish: reader process killed\n");
+ dev->reader_pid = 0;
+ }
+ if (dev->shm_channel)
+ {
+ shm_channel_free (dev->shm_channel);
+ dev->shm_channel = NULL;
+ }
+
+#endif /* USE_FORK */
+
+ free (dev->read_buffer);
+ dev->read_buffer = NULL;
+ dev->read_active = SANE_FALSE;
+ DBG (7, "gt68xx_device_read_finish: exit (%s)\n", sane_strstatus (status));
+ return status;
+}
+
+static SANE_Status
+gt68xx_device_check_result (GT68xx_Packet res, SANE_Byte command)
+{
+ if (res[0] != 0)
+ {
+ DBG (1, "gt68xx_device_check_result: result was %2X %2X "
+ "(expected: %2X %2X)\n", res[0], res[1], 0, command);
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* The Gt681xfw.usb firmware doesn't return the command byte
+ in the second byte, so we can't rely on that test */
+ if (res[1] != command)
+ DBG (5, "gt68xx_device_check_result: warning: result was %2X %2X "
+ "(expected: %2X %2X)\n", res[0], res[1], 0, command);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_get_id (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_id");
+ if (dev->model->command_set->get_id)
+ return (*dev->model->command_set->get_id) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+static void
+gt68xx_device_fix_descriptor (GT68xx_Device * dev)
+{
+ SANE_Byte data[8];
+ sanei_usb_control_msg (dev->fd, 0x80, 0x06, 0x01 << 8, 0, 8, data);
+}
+
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_low.h b/backend/gt68xx_low.h
new file mode 100644
index 0000000..68cd7c5
--- /dev/null
+++ b/backend/gt68xx_low.h
@@ -0,0 +1,1099 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_LOW_H
+#define GT68XX_LOW_H
+
+/** @file
+ * @brief Low-level scanner interface functions.
+ */
+
+#include "../include/sane/sane.h"
+
+#include <stddef.h>
+
+#ifdef USE_FORK
+#include <sys/types.h>
+#include "gt68xx_shm_channel.h"
+#endif
+
+#ifdef NDEBUG
+#undef MAX_DEBUG
+#endif
+
+/* calculate the minimum/maximum values */
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+/* return the lower/upper 8 bits of a 16 bit word */
+#define HIBYTE(w) ((SANE_Byte)(((SANE_Word)(w) >> 8) & 0xFF))
+#define LOBYTE(w) ((SANE_Byte)(w))
+
+
+/* return if an error occured while the function was called */
+#ifdef MAX_DEBUG
+# ifndef __FUNCTION__
+# define __FUNCTION__ "somewhere"
+# endif
+
+# define RIE(function) \
+ do \
+ { \
+ status = function; \
+ if (status != SANE_STATUS_GOOD) \
+ { \
+ DBG (7, "%s: %s: %s\n", __FUNCTION__, STRINGIFY(function), \
+ sane_strstatus (status)); \
+ return status; \
+ } \
+ } \
+ while (SANE_FALSE)
+
+#else
+
+# define RIE(function) \
+ do { status = function; \
+ if (status != SANE_STATUS_GOOD) return status; \
+ } while (SANE_FALSE)
+
+#endif
+
+/* Flags */
+#define GT68XX_FLAG_MIRROR_X (1 << 0) /* CIS unit mounted the other way round? */
+#define GT68XX_FLAG_MOTOR_HOME (1 << 1) /* Use motor_home command (0x34) */
+#define GT68XX_FLAG_OFFSET_INV (1 << 2) /* Offset control is inverted */
+#define GT68XX_FLAG_UNTESTED (1 << 3) /* Print a warning for these scanners */
+#define GT68XX_FLAG_SE_2400 (1 << 4) /* Special quirks for SE 2400USB */
+#define GT68XX_FLAG_NO_STOP (1 << 5) /* Don't call stop_scan before the scan */
+#define GT68XX_FLAG_CIS_LAMP (1 << 6) /* CIS sensor with lamp */
+#define GT68XX_FLAG_NO_POWER_STATUS (1 << 7) /* get_power_status_doesn't work */
+#define GT68XX_FLAG_NO_LINEMODE (1 << 8) /* Linemode does not work with this scanner */
+#define GT68XX_FLAG_SCAN_FROM_HOME (1 << 9) /* Move home after calibration */
+#define GT68XX_FLAG_USE_OPTICAL_X (1 << 10) /* Use optical xdpi for 50 dpi and below */
+#define GT68XX_FLAG_ALWAYS_LINEMODE (1 << 11) /* Linemode must be used for any resolution */
+#define GT68XX_FLAG_SHEET_FED (1 << 12) /* we have a sheet fed scanner */
+#define GT68XX_FLAG_HAS_CALIBRATE (1 << 13) /* for sheet fed scanners that be calibrated with
+ an calibration sheet */
+
+/* Forward typedefs */
+typedef struct GT68xx_USB_Device_Entry GT68xx_USB_Device_Entry;
+typedef struct GT68xx_Command_Set GT68xx_Command_Set;
+typedef struct GT68xx_Model GT68xx_Model;
+typedef struct GT68xx_Device GT68xx_Device;
+typedef struct GT68xx_Scan_Request GT68xx_Scan_Request;
+typedef struct GT68xx_Scan_Parameters GT68xx_Scan_Parameters;
+typedef struct GT68xx_AFE_Parameters GT68xx_AFE_Parameters;
+typedef struct GT68xx_Exposure_Parameters GT68xx_Exposure_Parameters;
+
+typedef enum GT68xx_Color_Order
+{
+ COLOR_ORDER_RGB,
+ COLOR_ORDER_BGR
+}
+GT68xx_Color_Order;
+
+#define GT68XX_COLOR_RED SANE_I18N ("Red")
+#define GT68XX_COLOR_GREEN SANE_I18N ("Green")
+#define GT68XX_COLOR_BLUE SANE_I18N ("Blue")
+
+/** Scan action code (purpose of the scan).
+ *
+ * The scan action code affects various scanning mode fields in the setup
+ * command.
+ */
+typedef enum GT68xx_Scan_Action
+{
+ SA_CALIBRATE, /**< Calibration scan, no offsets, no LD
+ correction */
+ SA_CALIBRATE_ONE_LINE, /**< Same, but only one line for auto AFE */
+ SA_SCAN /**< Normal scan */
+}
+GT68xx_Scan_Action;
+
+/** USB device list entry. */
+struct GT68xx_USB_Device_Entry
+{
+ SANE_Word vendor; /**< USB vendor identifier */
+ SANE_Word product; /**< USB product identifier */
+ GT68xx_Model *model; /**< Scanner model information */
+};
+
+#define MAX_SCANNERS 50
+/* Looks like gcc doesn't like declarations without specificating the number
+ of array elements at least when --enable-warnings is active */
+
+/** List of all supported devices.
+ *
+ * This is an array of GT68xx_USB_Device_Entry structures which describe
+ * USB devices supported by this backend. The array is terminated by an
+ * entry with model = NULL.
+ *
+ */
+static GT68xx_USB_Device_Entry gt68xx_usb_device_list[MAX_SCANNERS];
+
+/** GT68xx analog front-end (AFE) parameters.
+ */
+struct GT68xx_AFE_Parameters
+{
+ SANE_Byte r_offset; /**< Red channel offset */
+ SANE_Byte r_pga; /**< Red channel PGA gain */
+ SANE_Byte g_offset; /**< Green channel offset (also used for mono) */
+ SANE_Byte g_pga; /**< Green channel PGA gain (also used for mono) */
+ SANE_Byte b_offset; /**< Blue channel offset */
+ SANE_Byte b_pga; /**< Blue channel PGA gain */
+};
+
+/** GT68xx exposure time parameters.
+ */
+struct GT68xx_Exposure_Parameters
+{
+ SANE_Int r_time; /**< Red exposure time */
+ SANE_Int g_time; /**< Red exposure time */
+ SANE_Int b_time; /**< Red exposure time */
+};
+
+
+/**
+ * Scanner command set description.
+ *
+ * This description contains parts which are common to all scanners with the
+ * same command set, but may have different optical resolution and other
+ * parameters.
+ */
+struct GT68xx_Command_Set
+{
+ /** @name Identification */
+ /*@{ */
+
+ /** Name of this command set */
+ SANE_String_Const name;
+
+ /*@} */
+
+ /** @name USB request parameters
+ *
+ * These values are used in the USB control transfer parameters (wValue and
+ * wIndex fields, as in the USB specification).
+ */
+ /*@{ */
+
+ SANE_Byte request_type; /**< Request type (should be 0x40, vendor spec) */
+ SANE_Byte request; /**< Vendor spec resquest (0x01 or 0x04) */
+ SANE_Word memory_read_value; /**< Memory read - wValue */
+ SANE_Word memory_write_value; /**< Memory write - wValue */
+ SANE_Word send_cmd_value; /**< Send normal command - wValue */
+ SANE_Word send_cmd_index; /**< Send normal command - wIndex */
+ SANE_Word recv_res_value; /**< Receive normal result - wValue */
+ SANE_Word recv_res_index; /**< Receive normal result - wIndex */
+ SANE_Word send_small_cmd_value; /**< Send small command - wValue */
+ SANE_Word send_small_cmd_index; /**< Send small command - wIndex */
+ SANE_Word recv_small_res_value; /**< Receive small result - wValue */
+ SANE_Word recv_small_res_index; /**< Receive small result - wIndex */
+
+ /*@} */
+
+ /** @name Activation/deactivation hooks
+ *
+ * These hooks can be used to perform additional actions when the device is
+ * activated and deactivated.
+ */
+ /*@{ */
+
+ /** Activate the device.
+ *
+ * This function may allocate a command-set-specific data structure and place
+ * the pointer to it into the GT68xx_Device::command_set_private field.
+ */
+ SANE_Status (*activate) (GT68xx_Device * dev);
+
+ /** Deactivate the device.
+ *
+ * If the activate function has allocated a command-set-specific data
+ * structure, this function must free all corresponding resources and set the
+ * GT68xx_Device::command_set_private pointer to #NULL.
+ */
+ SANE_Status (*deactivate) (GT68xx_Device * dev);
+
+ /*@} */
+
+ /** @name Low-level command implementation functions
+ *
+ * These functions should implement the corresponding commands for the
+ * particular command set. If a function cannot be implemented because there
+ * is no corresponding command in the command set, the function pointer
+ * should be set to #NULL; the wrapper will return #SANE_STATUS_UNSUPPORTED
+ * in this case.
+ */
+ /*@{ */
+
+ /** Check whether the firmware is already downloaded.
+ *
+ * @param dev Device object.
+ * @param loaded Returned firmware status:
+ * - #SANE_TRUE - the firmware is already loaded.
+ * - #SANE_FALSE - the firmware is not loaded.
+ */
+ SANE_Status (*check_firmware) (GT68xx_Device * dev, SANE_Bool * loaded);
+
+ /** Download the firmware */
+ SANE_Status (*download_firmware) (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size);
+
+ /** Check whether the external power supply is connected.
+ *
+ * @param dev Device object.
+ * @param power_ok Returned power status:
+ * - #SANE_TRUE - the external power supply is connected, or the scanner does
+ * not need external power.
+ * - #SANE_FALSE - the external power supply is not connected, so the scanner
+ * will not work.
+ */
+ SANE_Status (*get_power_status) (GT68xx_Device * dev,
+ SANE_Bool * power_ok);
+
+ /** Check whether a transparency adapter is attached to the scanner.
+ *
+ * @param dev Device object.
+ * @param ta_attached Returned transparency adapter status:
+ * - #SANE_TRUE - the transparency adapter is connected.
+ * - #SANE_FALSE - the transparency adapter is not connected.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - transparency adapter status was checked
+ * successfully; check @a *ta_attached for the result.
+ * - #SANE_STATUS_UNSUPPORTED - this scanner model does not support the
+ * transparency adapter.
+ * */
+ SANE_Status (*get_ta_status) (GT68xx_Device * dev,
+ SANE_Bool * ta_attached);
+
+ /** Turn the lamps in the scanner and/or the transparency adapter on or off.
+ *
+ * @param dev Device object.
+ * @param fb_lamp #SANE_TRUE turns on the flatbed lamp.
+ * @param ta_lamp #SANE_TRUE turns on the transparency adapter lamp.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the command completed successfully.
+ * - #SANE_STATUS_UNSUPPORTED - unsupported request was made (like attempt to
+ * turn on the TA lamp on a scanner which does not support TA).
+ */
+ SANE_Status (*lamp_control) (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp);
+
+ /** Check whether the scanner carriage is still moving.
+ *
+ * @param dev Device object.
+ * @param moving Returned state of the scanner:
+ * - #SANE_TRUE - the scanner carriage is still moving.
+ * - #SANE_FALSE - the scanner carriage has stopped.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the command completed successfully, the status in @a
+ * *moving is valid.
+ */
+ SANE_Status (*is_moving) (GT68xx_Device * dev, SANE_Bool * moving);
+
+
+ /** Move the scanner carriage by the specified number of steps.
+ *
+ * @param dev Device object.
+ * @param distance Number of steps to move (positive to move forward,
+ * negative to move backward). The measurement unit is model-dependent;
+ * number of steps per inch is found in the GT68xx_Model::base_ydpi field.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the command completed successfully; the movement is
+ * started. Call gt68xx_device_is_moving() periodically to determine when
+ * the movement is complete.
+ */
+ SANE_Status (*move_relative) (GT68xx_Device * dev, SANE_Int distance);
+
+ /** Move the scanner carriage to the home position.
+ *
+ * @param dev Device object.
+ */
+ SANE_Status (*carriage_home) (GT68xx_Device * dev);
+
+ /** Eject the paper at the end of the scan.
+ *
+ * @param dev Device object.
+ */
+ SANE_Status (*paperfeed) (GT68xx_Device * dev);
+
+ /** Start scanning the image.
+ *
+ * @param dev Device object.
+ */
+ SANE_Status (*start_scan) (GT68xx_Device * dev);
+
+ /** Start reading the scanned image data from the scanner.
+ *
+ * @param dev Device object.
+ * */
+ SANE_Status (*read_scanned_data) (GT68xx_Device * dev, SANE_Bool * ready);
+
+ /** Stop scanning the image and reading the data. */
+ SANE_Status (*stop_scan) (GT68xx_Device * dev);
+
+ /** Set parameters for the next scan. */
+ SANE_Status (*setup_scan) (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params);
+
+ SANE_Status (*set_afe) (GT68xx_Device * dev,
+ GT68xx_AFE_Parameters * params);
+
+ SANE_Status (*set_exposure_time) (GT68xx_Device * dev,
+ GT68xx_Exposure_Parameters * params);
+
+ /** Get the vendor, product and some more ids from the scanner */
+ SANE_Status (*get_id) (GT68xx_Device * dev);
+
+ /** Move the paper by the amount of y offset needed to reach scan area
+ *
+ * @param dev Device object.
+ * @param request scan request used to compute move to reach scan area
+ */
+ SANE_Status (*move_paper) (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request);
+
+ /** Detect if a document is inserted in the feeder
+ *
+ * @param dev Device object.
+ * @param present
+ */
+ SANE_Status (*document_present) (GT68xx_Device * dev,
+ SANE_Bool *present);
+ /*@} */
+};
+
+#define MAX_RESOLUTIONS 12
+#define MAX_DPI 4
+
+/** Model-specific scanner data.
+ */
+struct GT68xx_Model
+{
+ /** @name Identification */
+ /*@{ */
+
+ /** A single lowercase word to be used in the configuration file. */
+ SANE_String_Const name;
+
+ /** Device vendor string. */
+ SANE_String_Const vendor;
+
+ /** Device model name. */
+ SANE_String_Const model;
+
+ /** Name of the firmware file. */
+ SANE_String_Const firmware_name;
+
+ /** Dynamic allocation flag.
+ *
+ * This flag must be set to SANE_TRUE if the structure is dynamically
+ * allocated; in this case the structure will be freed when the GT68xx_Device
+ * is freed.
+ */
+ SANE_Bool allocated;
+ /*@} */
+
+ /** @name Scanner model parameters */
+ /*@{ */
+
+ GT68xx_Command_Set *command_set;
+
+ SANE_Int optical_xdpi; /* maximum resolution in x-direction */
+ SANE_Int optical_ydpi; /* maximum resolution in y-direction */
+ SANE_Int base_xdpi; /* x-resolution used to calculate geometry */
+ SANE_Int base_ydpi; /* y-resolution used to calculate geometry */
+ SANE_Int ydpi_no_backtrack; /* if ydpi is equal or higher, disable backtracking */
+ SANE_Bool constant_ydpi; /* Use base_ydpi for all resolutions */
+
+ SANE_Int xdpi_values[MAX_RESOLUTIONS]; /* possible x resolutions */
+ SANE_Int ydpi_values[MAX_RESOLUTIONS]; /* possible y resolutions */
+ SANE_Int bpp_gray_values[MAX_DPI]; /* possible depths in gray mode */
+ SANE_Int bpp_color_values[MAX_DPI]; /* possible depths in color mode */
+
+ SANE_Fixed x_offset; /* Start of scan area in mm */
+ SANE_Fixed y_offset; /* Start of scan area in mm */
+ SANE_Fixed x_size; /* Size of scan area in mm */
+ SANE_Fixed y_size; /* Size of scan area in mm */
+
+ SANE_Fixed y_offset_calib; /* Start of white strip in mm */
+ SANE_Fixed x_offset_mark; /* Start of black mark in mm */
+
+ SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */
+ SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */
+
+ SANE_Fixed y_offset_calib_ta; /* Start of white strip in TA mode in mm */
+
+ /* Line-distance correction (in pixel at optical_ydpi) for CCD scanners */
+ SANE_Int ld_shift_r; /* red */
+ SANE_Int ld_shift_g; /* green */
+ SANE_Int ld_shift_b; /* blue */
+ SANE_Int ld_shift_double; /* distance between two CCD lines of one color
+ (only applicable for CCD with 6 lines) */
+
+ GT68xx_Color_Order line_mode_color_order; /* Order of the CCD/CIS colors */
+
+ GT68xx_AFE_Parameters afe_params; /* Default offset/gain */
+ GT68xx_Exposure_Parameters exposure; /* Default exposure parameters */
+ SANE_Fixed default_gamma_value; /* Default gamma value */
+
+ SANE_Bool is_cis; /* Is this a CIS or CCD scanner? */
+
+ SANE_Word flags; /* Which hacks are needed for this scanner? */
+ /*@} */
+};
+
+/** GT68xx device instance.
+ *
+ */
+struct GT68xx_Device
+{
+ /** Device file descriptor. */
+ int fd;
+
+ /** Device activation flag. */
+ SANE_Bool active;
+
+ /** Device missing to flag devices that are unplugged
+ * after sane_init and befor sane_exit */
+ SANE_Bool missing;
+
+ /** Scanner model data. */
+ GT68xx_Model *model;
+
+ /** Pointer to command-set-specific data. */
+ void *command_set_private;
+
+ GT68xx_AFE_Parameters *afe;
+ GT68xx_Exposure_Parameters *exposure;
+ SANE_Fixed gamma_value;
+
+ SANE_Bool read_active;
+ SANE_Bool final_scan;
+ SANE_Byte *read_buffer;
+ size_t requested_buffer_size;
+ size_t read_buffer_size;
+ size_t read_pos;
+ size_t read_bytes_in_buffer;
+ size_t read_bytes_left;
+ SANE_Byte gray_mode_color;
+ SANE_Bool manual_selection;
+#ifdef USE_FORK
+ Shm_Channel *shm_channel;
+ pid_t reader_pid;
+#endif /* USE_FORK */
+
+ /** Pointer to next device */
+ struct GT68xx_Device *next;
+
+ /** Device file name */
+ SANE_String file_name;
+};
+
+/** Parameters for the high-level scan request.
+ *
+ * These parameters describe the scan request sent by the SANE frontend.
+ */
+struct GT68xx_Scan_Request
+{
+ SANE_Fixed x0; /**< Left boundary */
+ SANE_Fixed y0; /**< Top boundary */
+ SANE_Fixed xs; /**< Width */
+ SANE_Fixed ys; /**< Height */
+ SANE_Int xdpi; /**< Horizontal resolution */
+ SANE_Int ydpi; /**< Vertical resolution */
+ SANE_Int depth; /**< Number of bits per channel */
+ SANE_Bool color; /**< Color mode flag */
+ SANE_Bool mbs; /**< Move before scan */
+ SANE_Bool mds; /**< Move during scan */
+ SANE_Bool mas; /**< Move after scan */
+ SANE_Bool lamp; /**< Lamp on/off */
+ SANE_Bool calculate; /**< Don't scan, only calculate parameters */
+ SANE_Bool use_ta; /**< Use the tansparency adapter */
+ SANE_Bool backtrack; /**< Enable backtracking */
+ SANE_Bool backtrack_lines; /**< How many lines to backtrack */
+};
+
+/** Scan parameters for gt68xx_device_setup_scan().
+ *
+ * These parameters describe a low-level scan request; many such requests are
+ * executed during calibration, and they need to have parameters separate from
+ * the main request (GT68xx_Scan_Request).
+ */
+struct GT68xx_Scan_Parameters
+{
+ SANE_Int xdpi; /**< Horizontal resolution */
+ SANE_Int ydpi; /**< Vertical resolution */
+ SANE_Int depth; /**< Number of bits per channel */
+ SANE_Bool color; /**< Color mode flag */
+
+ SANE_Int pixel_xs; /**< Logical width in pixels */
+ SANE_Int pixel_ys; /**< Logical height in pixels */
+ SANE_Int scan_xs; /**< Physical width in pixels */
+ SANE_Int scan_ys; /**< Physical height in pixels */
+ SANE_Int scan_bpl; /**< Number of bytes per scan line */
+ SANE_Bool line_mode; /**< Use line mode instead of pixel mode */
+ SANE_Int overscan_lines; /**< Number of extra scan lines */
+ SANE_Int ld_shift_r;
+ SANE_Int ld_shift_g;
+ SANE_Int ld_shift_b;
+ SANE_Int ld_shift_double;
+ SANE_Int double_column;
+ SANE_Int pixel_x0; /**< x start postion */
+};
+
+
+#define GT68XX_PACKET_SIZE 64
+
+typedef SANE_Byte GT68xx_Packet[GT68XX_PACKET_SIZE];
+
+/** Create a new GT68xx_Device object.
+ *
+ * The newly created device object is in the closed state.
+ *
+ * @param dev_return Returned pointer to the created device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the device object was created.
+ * - #SANE_STATUS_NO_MEM - not enough system resources to create the object.
+ */
+static SANE_Status gt68xx_device_new (GT68xx_Device ** dev_return);
+
+/** Destroy the device object and release all associated resources.
+ *
+ * If the device was active, it will be deactivated; if the device was open, it
+ * will be closed.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ */
+static SANE_Status gt68xx_device_free (GT68xx_Device * dev);
+
+/** Open the scanner device.
+ *
+ * This function opens the device special file @a dev_name and tries to detect
+ * the device model by its USB ID.
+ *
+ * If the device is detected successfully (its USB ID is found in the supported
+ * device list), this function sets the appropriate model parameters.
+ *
+ * If the USB ID is not recognized, the device remains unconfigured; an attempt
+ * to activate it will fail unless gt68xx_device_set_model() is used to force
+ * the parameter set. Note that the open is considered to be successful in
+ * this case.
+ *
+ * @param dev Device object.
+ * @param dev_name Scanner device name.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - the device was opened successfully (it still may be
+ * unconfigured).
+ */
+static SANE_Status
+gt68xx_device_open (GT68xx_Device * dev, const char *dev_name);
+
+/** Close the scanner device.
+ *
+ * @param dev Device object.
+ */
+static SANE_Status gt68xx_device_close (GT68xx_Device * dev);
+
+/** Check if the device is configured.
+ *
+ * A device is considered configured when it has a model parameters structure
+ * (GT68xx_Model) and a command set (GT68xx_Command_Set). Normally these
+ * parameters are assigned automatically by gt68xx_device_open(), if the device
+ * is known. If the USB ID of the device is not found in the list, or the OS
+ * does not support identification of USB devices, the device will be
+ * unconfigured after opening.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_TRUE - device is configured and can be activated.
+ * - #SANE_FALSE - device is not configured; attempt to activate it will fail.
+ */
+static SANE_Bool gt68xx_device_is_configured (GT68xx_Device * dev);
+
+/** Change the device model structure.
+ *
+ * This function can be used to change all model-dependent parameters at once
+ * by supplying a whole model data structure. The model may be changed only
+ * when the device is not active.
+ *
+ * If the device already had a model structure which was dynamically allocated,
+ * the old structure is freed.
+ *
+ * If the new model structure @a model is dynamically allocated, the device @a
+ * dev takes ownership of it: @a model will be freed when @a dev is destroyed
+ * or gt68xx_device_set_model() is called again.
+ *
+ * @param dev Device object.
+ * @param model Device model data.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - model successfully changed.
+ * - #SANE_STATUS_INVAL - invalid request (attempt to change model when the
+ * device is already active, or not yet opened).
+ */
+static SANE_Status
+gt68xx_device_set_model (GT68xx_Device * dev, GT68xx_Model * model);
+
+/** Get model by name.
+ *
+ * This function can be used to find a model by its name.
+ *
+ * @param name Device model name.
+ * @param model Device model data.
+ *
+ * @return
+ * - #SANE_TRUE - model successfully found.
+ * - #SANE_FALSE - model not found.
+ */
+static SANE_Bool
+gt68xx_device_get_model (SANE_String name, GT68xx_Model ** model);
+
+#if 0
+/** Create a new private copy of the model data for this device.
+ *
+ * Normally the model data structures can be shared between several devices.
+ * If the program needs to modify some model parameters for a device, it must
+ * call this function to make a private copy of parameters. This private copy
+ * will be automatically freed when the device is destroyed.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - a private copy was made successfully.
+ * - #SANE_STATUS_INVAL - invalid request (the device was already active, or
+ * not yet opened).
+ * - #SANE_STATUS_NO_MEM - not enough memory for copy of the model parameters.
+ */
+static SANE_Status gt68xx_device_unshare_model (GT68xx_Device * dev);
+#endif
+
+/** Activate the device.
+ *
+ * The device must be activated before performing any I/O operations with it.
+ * All device model parameters must be configured before activation; it is
+ * impossible to change them after the device is active.
+ *
+ * This function might need to acquire resources (it calls
+ * GT68xx_Command_Set::activate). These resources will be released when
+ * gt68xx_device_deactivate() is called.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - device activated successfully.
+ * - #SANE_STATUS_INVAL - invalid request (attempt to activate a closed or
+ * unconfigured device).
+ */
+static SANE_Status gt68xx_device_activate (GT68xx_Device * dev);
+
+/** Deactivate the device.
+ *
+ * This function reverses the action of gt68xx_device_activate().
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - device deactivated successfully.
+ * - #SANE_STATUS_INVAL - invalid request (the device was not activated).
+ */
+static SANE_Status gt68xx_device_deactivate (GT68xx_Device * dev);
+
+/** Write a data block to the GT68xx memory.
+ *
+ * @param dev Device object.
+ * @param addr Start address in the GT68xx memory.
+ * @param size Size of the data block in bytes.
+ * @param data Data block to write.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ *
+ * @warning
+ * @a size must be a multiple of 64 (at least with GT6816), otherwise the
+ * scanner (and possibly the entire USB bus) will lock up.
+ */
+static SANE_Status
+gt68xx_device_memory_write (GT68xx_Device * dev, SANE_Word addr,
+ SANE_Word size, SANE_Byte * data);
+
+/** Read a data block from the GT68xx memory.
+ *
+ * @param dev Device object.
+ * @param addr Start address in the GT68xx memory.
+ * @param size Size of the data block in bytes.
+ * @param data Buffer for the read data.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ *
+ * @warning
+ * @a size must be a multiple of 64 (at least with GT6816), otherwise the
+ * scanner (and possibly the entire USB bus) will lock up.
+ */
+static SANE_Status
+gt68xx_device_memory_read (GT68xx_Device * dev, SANE_Word addr,
+ SANE_Word size, SANE_Byte * data);
+
+/** Execute a control command.
+ *
+ * @param dev Device object.
+ * @param cmd Command packet.
+ * @param res Result packet (may point to the same buffer as @a cmd).
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res);
+
+/** Execute a "small" control command.
+ *
+ * @param dev Device object.
+ * @param cmd Command packet; only first 8 bytes are used.
+ * @param res Result packet (may point to the same buffer as @a cmd).
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_small_req (GT68xx_Device * dev, GT68xx_Packet cmd,
+ GT68xx_Packet res);
+
+#if 0
+/** Check whether the firmware is downloaded into the scanner.
+ *
+ * @param dev Device object.
+ * @param loaded Returned firmware status:
+ * - #SANE_TRUE - the firmware is already loaded.
+ * - #SANE_FALSE - the firmware is not loaded.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded);
+#endif
+
+static SANE_Status
+gt68xx_device_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size);
+
+/** Check whether the external power supply is connected.
+ *
+ * @param dev Device object.
+ * @param power_ok Returned power status:
+ * - #SANE_TRUE - the external power supply is connected, or the scanner does
+ * not need external power.
+ * - #SANE_FALSE - the external power supply is not connected, so the scanner
+ * will not work.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok);
+
+/** Check whether the transparency adapter is connected.
+ *
+ * @param dev Device object.
+ * @param ta_attached Returned TA status:
+ * - #SANE_TRUE - the TA is connected.
+ * - #SANE_FALSE - the TA is not connected.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_UNSUPPORTED - the scanner does not support TA connection.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached);
+
+/** Turn the lamps in the scanner and/or the transparency adapter on or off.
+ *
+ * @param dev Device object.
+ * @param fb_lamp #SANE_TRUE turns on the flatbed lamp.
+ * @param ta_lamp #SANE_TRUE turns on the transparency adapter lamp.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ * - #SANE_STATUS_UNSUPPORTED - unsupported request was made (like attempt to
+ * turn on the TA lamp on a scanner which does not support TA).
+ */
+static SANE_Status
+gt68xx_device_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp);
+
+/** Check whether the scanner carriage is still moving.
+ *
+ * @param dev Device object.
+ * @param moving Returned state of the scanner:
+ * - #SANE_TRUE - the scanner carriage is still moving.
+ * - #SANE_FALSE - the scanner carriage has stopped.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success; the status in @a *moving is valid.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_is_moving (GT68xx_Device * dev, SANE_Bool * moving);
+
+#if 0
+/** Move the scanner carriage by the specified number of steps.
+ *
+ * @param dev Device object.
+ * @param distance Number of steps to move (positive to move forward, negative
+ * to move backward). The measurement unit is model-dependent; number of steps
+ * per inch is found in the GT68xx_Model::base_ydpi field.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success; the movement is started. Call
+ * gt68xx_device_is_moving() periodically to determine when the movement is
+ * complete.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_move_relative (GT68xx_Device * dev, SANE_Int distance);
+#endif
+
+/** Move the scanner carriage to the home position.
+ *
+ * This function starts moving the scanner carriage to the home position, but
+ * deos not wait for finishing the movement. To determine when the carriage
+ * returned to the home position, the program should periodically call
+ * gt68xx_device_is_moving().
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success; the movement is started.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status gt68xx_device_carriage_home (GT68xx_Device * dev);
+
+/** Eject the paper after the end of scanning.
+ *
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success; the movement is started.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status gt68xx_device_paperfeed (GT68xx_Device * dev);
+
+/** Start scanning the image.
+ *
+ * This function initiates scanning with parameters set by
+ * gt68xx_device_setup_scan() (which should be called before). In particular,
+ * it starts the carriage movement to the start of the scanning window.
+ *
+ * After calling this function, gt68xx_device_read_scanned_data() should be
+ * called repeatedly until the scanner signals that it is ready to deliver the
+ * data.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status gt68xx_device_start_scan (GT68xx_Device * dev);
+
+/** Start reading the scanned image data.
+ *
+ * This function should be used only after gt68xx_device_start_scan(). It
+ * should be called repeatedly until @a *ready flag becomes true, at which
+ * point reading the image data should be started using
+ * gt68xx_device_read_prepare().
+ *
+ * @param dev Device object.
+ * @param ready Returned status of the scanner:
+ * - #SANE_TRUE - the scanner is ready to send data.
+ * - #SANE_FALSE - the scanner is not ready (e.g., the carriage has not reached
+ * the start of the scanning window).
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success; the value in @a *ready is valid.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready);
+
+/** Stop scanning the image.
+ *
+ * This function should be used after reading all image data from the scanner,
+ * or in the middle of scan to abort the scanning process.
+ *
+ * @param dev Device object.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status gt68xx_device_stop_scan (GT68xx_Device * dev);
+
+/** Set parameters for the next scan.
+ *
+ * This function calculates the hardware-dependent scanning parameters and,
+ * unless @a calculate_only is set, sends the command to prepare for scanning
+ * with the specified window and parameters.
+ *
+ * @param dev Device object.
+ * @param request High-level scanning request.
+ * @param action Action code describing the phase of calibration or scanning
+ * process.
+ * @param params Returned structure with hardware-dependent scanning
+ * parameters.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_UNSUPPORTED - the requested scanning parameters in @a request
+ * are not supported by hardware.
+ * - #SANE_STATUS_INVAL - some of the parameters in @a request, or the @a
+ * action code, are completely invalid.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_setup_scan (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params);
+
+/** Configure the analog front-end (AFE) of the GT68xx.
+ *
+ * @param dev Device object.
+ * @param params AFE parameters.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params);
+
+static SANE_Status
+gt68xx_device_set_exposure_time (GT68xx_Device * dev,
+ GT68xx_Exposure_Parameters * params);
+
+/** Read raw data from the bulk-in scanner pipe.
+ *
+ * @param dev Device object.
+ * @param buffer Buffer for the read data.
+ * @param size Pointer to the variable which must be set to the requested data
+ * size before call. After completion this variable will hold the number of
+ * bytes actually read.
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - a communication error occured.
+ */
+static SANE_Status
+gt68xx_device_read_raw (GT68xx_Device * dev, SANE_Byte * buffer,
+ size_t * size);
+
+static SANE_Status
+gt68xx_device_set_read_buffer_size (GT68xx_Device * dev, size_t buffer_size);
+
+static SANE_Status
+gt68xx_device_read_prepare (GT68xx_Device * dev, size_t expected_count,
+ SANE_Bool final_scan);
+
+static SANE_Status
+gt68xx_device_read (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size);
+
+static SANE_Status gt68xx_device_read_finish (GT68xx_Device * dev);
+
+/** Make sure that the result of a command is ok.
+ *
+ * @param res Result packet from the last command
+ * @param command Command
+ *
+ * @return
+ * - #SANE_STATUS_GOOD - success.
+ * - #SANE_STATUS_IO_ERROR - the command wasn't successful
+*/
+static SANE_Status
+gt68xx_device_check_result (GT68xx_Packet res, SANE_Byte command);
+
+
+static SANE_Status
+gt68xx_device_get_id (GT68xx_Device * dev);
+
+/** Read the device descriptor of the scanner.
+ *
+ * This function should be called before closing the device to make sure
+ * that the device descriptor is propperly stored in the scanner's memory.
+ * If that's not done, the next try to get the config descriptor will
+ * result in a corrupted descriptor.
+ *
+ * @param dev device
+*/
+static void
+gt68xx_device_fix_descriptor (GT68xx_Device * dev);
+
+#endif /* not GT68XX_LOW_H */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_mid.c b/backend/gt68xx_mid.c
new file mode 100644
index 0000000..1301139
--- /dev/null
+++ b/backend/gt68xx_mid.c
@@ -0,0 +1,1185 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002-2007 Henning Geinitz <sane@geinitz.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "gt68xx_mid.h"
+#include "gt68xx_low.c"
+
+/** @file
+ * @brief Image data unpacking.
+ */
+
+static SANE_Status
+gt68xx_delay_buffer_init (GT68xx_Delay_Buffer * delay,
+ SANE_Int pixels_per_line, SANE_Int delay_count)
+{
+ SANE_Int bytes_per_line;
+ SANE_Int line_count, i;
+
+ if (pixels_per_line <= 0)
+ {
+ DBG (3, "gt68xx_delay_buffer_init: BUG: pixels_per_line=%d\n",
+ pixels_per_line);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (delay_count < 0)
+ {
+ DBG (3, "gt68xx_delay_buffer_init: BUG: delay_count=%d\n", delay_count);
+ return SANE_STATUS_INVAL;
+ }
+
+ bytes_per_line = pixels_per_line * sizeof (unsigned int);
+
+ delay->line_count = line_count = delay_count + 1;
+ delay->read_index = 0;
+ delay->write_index = delay_count;
+
+ delay->mem_block = (SANE_Byte *) malloc (bytes_per_line * line_count);
+ if (!delay->mem_block)
+ {
+ DBG (3, "gt68xx_delay_buffer_init: no memory for delay block\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ /* make sure that we will see if one of the unitialized lines get displayed */
+ for (i = 0; i < bytes_per_line * line_count; i++)
+ delay->mem_block[i] = i % 256;
+
+ delay->lines =
+ (unsigned int **) malloc (sizeof (unsigned int *) * line_count);
+ if (!delay->lines)
+ {
+ free (delay->mem_block);
+ DBG (3,
+ "gt68xx_delay_buffer_init: no memory for delay line pointers\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < line_count; ++i)
+ delay->lines[i] =
+ (unsigned int *) (delay->mem_block + i * bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_delay_buffer_done (GT68xx_Delay_Buffer * delay)
+{
+ if (delay->lines)
+ {
+ free (delay->lines);
+ delay->lines = NULL;
+ }
+
+ if (delay->mem_block)
+ {
+ free (delay->mem_block);
+ delay->mem_block = NULL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#define DELAY_BUFFER_WRITE_PTR(delay) ( (delay)->lines[(delay)->write_index] )
+
+#define DELAY_BUFFER_SELECT_PTR(delay,dist) \
+ ((delay)->lines[((delay)->read_index + (dist)) % (delay)->line_count])
+
+#define DELAY_BUFFER_READ_PTR(delay) ( (delay)->lines[(delay)->read_index ] )
+
+#define DELAY_BUFFER_STEP(delay) \
+ do \
+ { \
+ (delay)->read_index = ((delay)->read_index + 1) % (delay)->line_count; \
+ (delay)->write_index = ((delay)->write_index + 1) % (delay)->line_count; \
+ } \
+ while (SANE_FALSE)
+
+
+static inline void
+unpack_8_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line)
+{
+ for (; pixels_per_line > 0; ++src, ++dst, --pixels_per_line)
+ {
+ *dst = (((unsigned int) *src) << 8) | *src;
+ }
+}
+
+static inline void
+unpack_8_rgb (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line)
+{
+ for (; pixels_per_line > 0; src += 3, ++dst, --pixels_per_line)
+ {
+ *dst = (((unsigned int) *src) << 8) | *src;
+ }
+}
+
+/* 12-bit routines use the fact that pixels_per_line is aligned */
+
+static inline void
+unpack_12_le_mono (SANE_Byte * src, unsigned int *dst,
+ SANE_Int pixels_per_line)
+{
+ for (; pixels_per_line > 0; src += 3, dst += 2, pixels_per_line -= 2)
+ {
+ dst[0] = ((((unsigned int) (src[1] & 0x0f)) << 12)
+ | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f));
+ dst[1] = ((((unsigned int) src[2]) << 8)
+ | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04));
+ }
+}
+
+static inline void
+unpack_12_le_rgb (SANE_Byte * src,
+ unsigned int *dst1,
+ unsigned int *dst2,
+ unsigned int *dst3, SANE_Int pixels_per_line)
+{
+ for (; pixels_per_line > 0; pixels_per_line -= 2)
+ {
+ *dst1++ = ((((unsigned int) (src[1] & 0x0f)) << 12)
+ | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f));
+ *dst2++ = ((((unsigned int) src[2]) << 8)
+ | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04));
+ src += 3;
+
+ *dst3++ = ((((unsigned int) (src[1] & 0x0f)) << 12)
+ | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f));
+ *dst1++ = ((((unsigned int) src[2]) << 8)
+ | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04));
+ src += 3;
+
+ *dst2++ = ((((unsigned int) (src[1] & 0x0f)) << 12)
+ | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f));
+ *dst3++ = ((((unsigned int) src[2]) << 8)
+ | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04));
+ src += 3;
+ }
+}
+
+static inline void
+unpack_16_le_mono (SANE_Byte * src, unsigned int *dst,
+ SANE_Int pixels_per_line)
+{
+ for (; pixels_per_line > 0; src += 2, dst++, --pixels_per_line)
+ {
+ *dst = (((unsigned int) src[1]) << 8) | src[0];
+ }
+}
+
+static inline void
+unpack_16_le_rgb (SANE_Byte * src, unsigned int *dst,
+ SANE_Int pixels_per_line)
+{
+ for (; pixels_per_line > 0; src += 6, ++dst, --pixels_per_line)
+ {
+ *dst = (((unsigned int) src[1]) << 8) | src[0];
+ }
+}
+
+
+static SANE_Status
+line_read_gray_8 (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+
+ size = reader->params.scan_bpl;
+
+ RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size));
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_8_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_double_8 (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+ int i;
+
+ size = reader->params.scan_bpl;
+
+ RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size));
+ unpack_8_mono (reader->pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ reader->pixels_per_line);
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+
+ for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2)
+ buffer[i] = DELAY_BUFFER_WRITE_PTR (&reader->g_delay)[i];
+
+ buffer_pointers_return[0] = buffer;
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_12 (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size));
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_12_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_double_12 (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+ int i;
+
+ size = reader->params.scan_bpl;
+
+ RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size));
+ unpack_12_le_mono (reader->pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ reader->pixels_per_line);
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+
+ for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2)
+ buffer[i] = DELAY_BUFFER_WRITE_PTR (&reader->g_delay)[i];
+
+ buffer_pointers_return[0] = buffer;
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_16 (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size));
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_16_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_double_16 (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+ int i;
+
+ size = reader->params.scan_bpl;
+
+ RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size));
+ unpack_16_le_mono (reader->pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ reader->pixels_per_line);
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+
+ for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2)
+ buffer[i] = DELAY_BUFFER_WRITE_PTR (&reader->g_delay)[i];
+
+ buffer_pointers_return[0] = buffer;
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ return SANE_STATUS_GOOD;
+
+}
+
+static SANE_Status
+line_read_rgb_8_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl * 3;
+
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_double_8_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+ int i;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2)
+ {
+ DELAY_BUFFER_READ_PTR (&reader->r_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->r_delay,
+ reader->params.ld_shift_double)[i];
+ DELAY_BUFFER_READ_PTR (&reader->g_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->g_delay,
+ reader->params.ld_shift_double)[i];
+ DELAY_BUFFER_READ_PTR (&reader->b_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->b_delay,
+ reader->params.ld_shift_double)[i];
+ }
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_8_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_12_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_double_12_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+ int i;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2)
+ {
+ DELAY_BUFFER_READ_PTR (&reader->r_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->r_delay,
+ reader->params.ld_shift_double)[i];
+ DELAY_BUFFER_READ_PTR (&reader->g_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->g_delay,
+ reader->params.ld_shift_double)[i];
+ DELAY_BUFFER_READ_PTR (&reader->b_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->b_delay,
+ reader->params.ld_shift_double)[i];
+ }
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_16_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_double_16_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+ int i;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2)
+ {
+ DELAY_BUFFER_READ_PTR (&reader->r_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->r_delay,
+ reader->params.ld_shift_double)[i];
+ DELAY_BUFFER_READ_PTR (&reader->g_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->g_delay,
+ reader->params.ld_shift_double)[i];
+ DELAY_BUFFER_READ_PTR (&reader->b_delay)[i] =
+ DELAY_BUFFER_SELECT_PTR (&reader->b_delay,
+ reader->params.ld_shift_double)[i];
+ }
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_12_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_12_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_16_line_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl * 3;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_8_pixel_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+ ++pixel_buffer;
+ unpack_8_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ ++pixel_buffer;
+ unpack_8_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+line_read_rgb_12_pixel_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ unpack_12_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ reader->pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_rgb_16_pixel_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+ pixel_buffer += 2;
+ unpack_16_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += 2;
+ unpack_16_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_8_pixel_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+ ++pixel_buffer;
+ unpack_8_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ ++pixel_buffer;
+ unpack_8_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+line_read_bgr_12_pixel_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ unpack_12_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ reader->pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_16_pixel_mode (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ size = reader->params.scan_bpl;
+ RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size));
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+ pixel_buffer += 2;
+ unpack_16_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += 2;
+ unpack_16_le_rgb (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+gt68xx_line_reader_init_delays (GT68xx_Line_Reader * reader)
+{
+ SANE_Status status;
+
+ if (reader->params.color)
+ {
+ status = gt68xx_delay_buffer_init (&reader->r_delay,
+ reader->params.scan_xs,
+ reader->params.ld_shift_r +
+ reader->params.ld_shift_double);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = gt68xx_delay_buffer_init (&reader->g_delay,
+ reader->params.scan_xs,
+ reader->params.ld_shift_g +
+ reader->params.ld_shift_double);
+ if (status != SANE_STATUS_GOOD)
+ {
+ gt68xx_delay_buffer_done (&reader->r_delay);
+ return status;
+ }
+
+ status = gt68xx_delay_buffer_init (&reader->b_delay,
+ reader->params.scan_xs,
+ reader->params.ld_shift_b +
+ reader->params.ld_shift_double);
+ if (status != SANE_STATUS_GOOD)
+ {
+ gt68xx_delay_buffer_done (&reader->g_delay);
+ gt68xx_delay_buffer_done (&reader->r_delay);
+ return status;
+ }
+ }
+ else
+ {
+ status = gt68xx_delay_buffer_init (&reader->g_delay,
+ reader->params.scan_xs,
+ reader->params.ld_shift_double);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ reader->delays_initialized = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+gt68xx_line_reader_free_delays (GT68xx_Line_Reader * reader)
+{
+ if (reader->delays_initialized)
+ {
+ if (reader->params.color)
+ {
+ gt68xx_delay_buffer_done (&reader->b_delay);
+ gt68xx_delay_buffer_done (&reader->g_delay);
+ gt68xx_delay_buffer_done (&reader->r_delay);
+ }
+ else
+ {
+ gt68xx_delay_buffer_done (&reader->g_delay);
+ }
+ reader->delays_initialized = SANE_FALSE;
+ }
+}
+
+SANE_Status
+gt68xx_line_reader_new (GT68xx_Device * dev,
+ GT68xx_Scan_Parameters * params,
+ SANE_Bool final_scan,
+ GT68xx_Line_Reader ** reader_return)
+{
+ SANE_Status status;
+ GT68xx_Line_Reader *reader;
+ SANE_Int image_size;
+ SANE_Int scan_bpl_full;
+
+ DBG (6, "gt68xx_line_reader_new: enter\n");
+
+ *reader_return = NULL;
+
+ reader = (GT68xx_Line_Reader *) malloc (sizeof (GT68xx_Line_Reader));
+ if (!reader)
+ {
+ DBG (3, "gt68xx_line_reader_new: cannot allocate GT68xx_Line_Reader\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (reader, 0, sizeof (GT68xx_Line_Reader));
+
+ reader->dev = dev;
+ memcpy (&reader->params, params, sizeof (GT68xx_Scan_Parameters));
+ reader->pixel_buffer = 0;
+ reader->delays_initialized = SANE_FALSE;
+
+ reader->read = NULL;
+
+ status = gt68xx_line_reader_init_delays (reader);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_line_reader_new: cannot allocate line buffers: %s\n",
+ sane_strstatus (status));
+ free (reader);
+ return status;
+ }
+
+ reader->pixels_per_line = reader->params.pixel_xs;
+
+ if (!reader->params.color)
+ {
+ if (reader->params.depth == 8)
+ {
+ if (reader->params.ld_shift_double > 0)
+ reader->read = line_read_gray_double_8;
+ else
+ reader->read = line_read_gray_8;
+ }
+ else if (reader->params.depth == 12)
+ {
+ if (reader->params.ld_shift_double > 0)
+ reader->read = line_read_gray_double_12;
+ else
+ reader->read = line_read_gray_12;
+ }
+ else if (reader->params.depth == 16)
+ {
+ if (reader->params.ld_shift_double > 0)
+ reader->read = line_read_gray_double_16;
+ else
+ reader->read = line_read_gray_16;
+ }
+ }
+ else if (reader->params.line_mode)
+ {
+ if (reader->params.depth == 8)
+ {
+ if (dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ {
+ if (reader->params.ld_shift_double > 0)
+ reader->read = line_read_rgb_double_8_line_mode;
+ else
+ reader->read = line_read_rgb_8_line_mode;
+ }
+ else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ reader->read = line_read_bgr_8_line_mode;
+ }
+ else if (reader->params.depth == 12)
+ {
+ if (dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ {
+ if (reader->params.ld_shift_double > 0)
+ reader->read = line_read_rgb_double_12_line_mode;
+ else
+ reader->read = line_read_rgb_12_line_mode;
+ }
+ else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ reader->read = line_read_bgr_12_line_mode;
+ }
+ else if (reader->params.depth == 16)
+ {
+ if (dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ {
+ if (reader->params.ld_shift_double > 0)
+ reader->read = line_read_rgb_double_16_line_mode;
+ else
+ reader->read = line_read_rgb_16_line_mode;
+ }
+ else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ reader->read = line_read_bgr_16_line_mode;
+ }
+ }
+ else
+ {
+ if (reader->params.depth == 8)
+ {
+ if (dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ reader->read = line_read_rgb_8_pixel_mode;
+ else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ reader->read = line_read_bgr_8_pixel_mode;
+ }
+ else if (reader->params.depth == 12)
+ {
+ if (dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ reader->read = line_read_rgb_12_pixel_mode;
+ else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ reader->read = line_read_bgr_12_pixel_mode;
+ }
+ else if (reader->params.depth == 16)
+ {
+ if (dev->model->line_mode_color_order == COLOR_ORDER_RGB)
+ reader->read = line_read_rgb_16_pixel_mode;
+ else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
+ reader->read = line_read_bgr_16_pixel_mode;
+ }
+ }
+
+ if (reader->read == NULL)
+ {
+ DBG (3, "gt68xx_line_reader_new: unsupported bit depth (%d)\n",
+ reader->params.depth);
+ gt68xx_line_reader_free_delays (reader);
+ free (reader);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ scan_bpl_full = reader->params.scan_bpl;
+
+ if (reader->params.color && reader->params.line_mode)
+ scan_bpl_full *= 3;
+
+ reader->pixel_buffer = malloc (scan_bpl_full);
+ if (!reader->pixel_buffer)
+ {
+ DBG (3, "gt68xx_line_reader_new: cannot allocate pixel buffer\n");
+ gt68xx_line_reader_free_delays (reader);
+ free (reader);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ gt68xx_device_set_read_buffer_size (reader->dev,
+ scan_bpl_full /* * 200 */ );
+
+ image_size = reader->params.scan_bpl * reader->params.scan_ys;
+ status = gt68xx_device_read_prepare (reader->dev, image_size, final_scan);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_line_reader_new: gt68xx_device_read_prepare failed: %s\n",
+ sane_strstatus (status));
+ free (reader->pixel_buffer);
+ gt68xx_line_reader_free_delays (reader);
+ free (reader);
+ return status;
+ }
+
+ DBG (6, "gt68xx_line_reader_new: leave: ok\n");
+ *reader_return = reader;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_line_reader_free (GT68xx_Line_Reader * reader)
+{
+ SANE_Status status;
+
+ DBG (6, "gt68xx_line_reader_free: enter\n");
+
+ gt68xx_line_reader_free_delays (reader);
+
+ if (reader->pixel_buffer)
+ {
+ free (reader->pixel_buffer);
+ reader->pixel_buffer = NULL;
+ }
+
+ status = gt68xx_device_read_finish (reader->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_line_reader_free: gt68xx_device_read_finish failed: %s\n",
+ sane_strstatus (status));
+ }
+
+ free (reader);
+
+ DBG (6, "gt68xx_line_reader_free: leave\n");
+ return status;
+}
+
+SANE_Status
+gt68xx_line_reader_read (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+
+ status = (*reader->read) (reader, buffer_pointers_return);
+ return status;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_mid.h b/backend/gt68xx_mid.h
new file mode 100644
index 0000000..4ba6c4c
--- /dev/null
+++ b/backend/gt68xx_mid.h
@@ -0,0 +1,154 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_MID_H
+#define GT68XX_MID_H
+
+/** @file
+ * @brief Image data unpacking.
+ */
+
+#include "gt68xx_low.h"
+#include "../include/sane/sane.h"
+
+typedef struct GT68xx_Delay_Buffer GT68xx_Delay_Buffer;
+typedef struct GT68xx_Line_Reader GT68xx_Line_Reader;
+
+struct GT68xx_Delay_Buffer
+{
+ SANE_Int line_count;
+ SANE_Int read_index;
+ SANE_Int write_index;
+ unsigned int **lines;
+ SANE_Byte *mem_block;
+};
+
+/**
+ * Object for reading image data line by line, with line distance correction.
+ *
+ * This object handles reading the image data from the scanner line by line and
+ * converting it to internal format. Internally each image sample is
+ * represented as <code>unsigned int</code> value, scaled to 16-bit range
+ * (0-65535). For color images the data for each primary color is stored as
+ * separate lines.
+ */
+struct GT68xx_Line_Reader
+{
+ GT68xx_Device *dev; /**< Low-level interface object */
+ GT68xx_Scan_Parameters params; /**< Scan parameters */
+
+#if 0
+ /** Number of bytes in the returned scanlines */
+ SANE_Int bytes_per_line;
+
+ /** Number of bytes per pixel in the returned scanlines */
+ SANE_Int bytes_per_pixel;
+#endif
+
+ /** Number of pixels in the returned scanlines */
+ SANE_Int pixels_per_line;
+
+ SANE_Byte *pixel_buffer;
+
+ GT68xx_Delay_Buffer r_delay;
+ GT68xx_Delay_Buffer g_delay;
+ GT68xx_Delay_Buffer b_delay;
+ SANE_Bool delays_initialized;
+
+ SANE_Status (*read) (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return);
+};
+
+/**
+ * Create a new GT68xx_Line_Reader object.
+ *
+ * @param dev The low-level scanner interface object.
+ * @param params Scan parameters prepared by gt68xx_device_setup_scan().
+ * @param final_scan SANE_TRUE for the final scan, SANE_FALSE for
+ * calibration scans.
+ * @param reader_return Location for the returned object.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_NO_MEM - cannot allocate memory for object or buffers
+ * - other error values - failure of some internal functions
+ */
+static SANE_Status
+gt68xx_line_reader_new (GT68xx_Device * dev,
+ GT68xx_Scan_Parameters * params,
+ SANE_Bool final_scan,
+ GT68xx_Line_Reader ** reader_return);
+
+/**
+ * Destroy the GT68xx_Line_Reader object.
+ *
+ * @param reader The GT68xx_Line_Reader object to destroy.
+ */
+static SANE_Status gt68xx_line_reader_free (GT68xx_Line_Reader * reader);
+
+/**
+ * Read a scanline from the GT68xx_Line_Reader object.
+ *
+ * @param reader The GT68xx_Line_Reader object.
+ * @param buffer_pointers_return Array of pointers to image lines (1 or 3
+ * elements)
+ *
+ * This function reads a full scanline from the device, unpacks it to internal
+ * buffers and returns pointer to these buffers in @a
+ * buffer_pointers_return[i]. For monochrome scan, only @a
+ * buffer_pointers_return[0] is filled; for color scan, elements 0, 1, 2 are
+ * filled with pointers to red, green, and blue data. The returned pointers
+ * are valid until the next call to gt68xx_line_reader_read(), or until @a
+ * reader is destroyed.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - read completed successfully
+ * - other error value - an error occured
+ */
+static SANE_Status
+gt68xx_line_reader_read (GT68xx_Line_Reader * reader,
+ unsigned int **buffer_pointers_return);
+
+#endif /* not GT68XX_MID_H */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_shm_channel.c b/backend/gt68xx_shm_channel.c
new file mode 100644
index 0000000..bca7425
--- /dev/null
+++ b/backend/gt68xx_shm_channel.c
@@ -0,0 +1,673 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ * @brief Shared memory channel implementation.
+ */
+
+#include "gt68xx_shm_channel.h"
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifndef SHM_R
+#define SHM_R 0
+#endif
+
+#ifndef SHM_W
+#define SHM_W 0
+#endif
+
+/** Shared memory channel.
+ *
+ */
+struct Shm_Channel
+{
+ SANE_Int buf_size; /**< Size of each buffer */
+ SANE_Int buf_count; /**< Number of buffers */
+ void *shm_area; /**< Address of shared memory area */
+ SANE_Byte **buffers; /**< Array of pointers to buffers */
+ SANE_Int *buffer_bytes; /**< Array of buffer byte counts */
+ int writer_put_pipe[2]; /**< Notification pipe from writer */
+ int reader_put_pipe[2]; /**< Notification pipe from reader */
+};
+
+/** Dummy union to find out the needed alignment */
+union Shm_Channel_Align
+{
+ int i;
+ long l;
+ void *ptr;
+ void (*func_ptr) (void);
+ double d;
+};
+
+/** Check if shm_channel is valid */
+#define SHM_CHANNEL_CHECK(shm_channel, func_name) \
+ do { \
+ if ((shm_channel) == NULL) \
+ { \
+ DBG (3, "%s: BUG: shm_channel==NULL\n", (func_name)); \
+ return SANE_STATUS_INVAL; \
+ } \
+ } while (SANE_FALSE)
+
+/** Alignment for shared memory contents */
+#define SHM_CHANNEL_ALIGNMENT (sizeof (union Shm_Channel_Align))
+
+/** Align the given size up to a multiple of the given alignment */
+#define SHM_CHANNEL_ROUND_UP(size, align) \
+ ( ((size) % (align)) ? ((size)/(align) + 1)*(align) : (size) )
+
+/** Align the size using SHM_CHANNEL_ALIGNMENT */
+#define SHM_CHANNEL_ALIGN(size) \
+ SHM_CHANNEL_ROUND_UP((size_t) (size), SHM_CHANNEL_ALIGNMENT)
+
+/** Close a file descriptor if it is currently open.
+ *
+ * This function checks if the file descriptor is not -1, and sets it to -1
+ * after close (so that it will not be closed twice).
+ *
+ * @param fd_var Pointer to a variable holding the file descriptor.
+ */
+static void
+shm_channel_fd_safe_close (int *fd_var)
+{
+ if (*fd_var != -1)
+ {
+ close (*fd_var);
+ *fd_var = -1;
+ }
+}
+
+static SANE_Status
+shm_channel_fd_set_close_on_exec (int fd)
+{
+ long value;
+
+ value = fcntl (fd, F_GETFD, 0L);
+ if (value == -1)
+ return SANE_STATUS_IO_ERROR;
+ if (fcntl (fd, F_SETFD, value | FD_CLOEXEC) == -1)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+#if 0
+static SANE_Status
+shm_channel_fd_set_non_blocking (int fd, SANE_Bool non_blocking)
+{
+ long value;
+
+ value = fcntl (fd, F_GETFL, 0L);
+ if (value == -1)
+ return SANE_STATUS_IO_ERROR;
+
+ if (non_blocking)
+ value |= O_NONBLOCK;
+ else
+ value &= ~O_NONBLOCK;
+
+ if (fcntl (fd, F_SETFL, value) == -1)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+/** Create a new shared memory channel.
+ *
+ * This function should be called before the fork to set up the shared memory.
+ *
+ * @param buf_size Size of each shared memory buffer in bytes.
+ * @param buf_count Number of shared memory buffers (up to 255).
+ * @param shm_channel_return Returned shared memory channel object.
+ */
+SANE_Status
+shm_channel_new (SANE_Int buf_size,
+ SANE_Int buf_count, Shm_Channel ** shm_channel_return)
+{
+ Shm_Channel *shm_channel;
+ void *shm_area;
+ SANE_Byte *shm_data;
+ int shm_buffer_bytes_size, shm_buffer_size;
+ int shm_size;
+ int shm_id;
+ int i;
+
+ if (buf_size <= 0)
+ {
+ DBG (3, "shm_channel_new: invalid buf_size=%d\n", buf_size);
+ return SANE_STATUS_INVAL;
+ }
+ if (buf_count <= 0 || buf_count > 255)
+ {
+ DBG (3, "shm_channel_new: invalid buf_count=%d\n", buf_count);
+ return SANE_STATUS_INVAL;
+ }
+ if (!shm_channel_return)
+ {
+ DBG (3, "shm_channel_new: BUG: shm_channel_return==NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *shm_channel_return = NULL;
+
+ shm_channel = (Shm_Channel *) malloc (sizeof (Shm_Channel));
+ if (!shm_channel)
+ {
+ DBG (3, "shm_channel_new: no memory for Shm_Channel\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shm_channel->buf_size = buf_size;
+ shm_channel->buf_count = buf_count;
+ shm_channel->shm_area = NULL;
+ shm_channel->buffers = NULL;
+ shm_channel->buffer_bytes = NULL;
+ shm_channel->writer_put_pipe[0] = shm_channel->writer_put_pipe[1] = -1;
+ shm_channel->reader_put_pipe[0] = shm_channel->reader_put_pipe[1] = -1;
+
+ shm_channel->buffers =
+ (SANE_Byte **) malloc (sizeof (SANE_Byte *) * buf_count);
+ if (!shm_channel->buffers)
+ {
+ DBG (3, "shm_channel_new: no memory for buffer pointers\n");
+ shm_channel_free (shm_channel);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (pipe (shm_channel->writer_put_pipe) == -1)
+ {
+ DBG (3, "shm_channel_new: cannot create writer put pipe: %s\n",
+ strerror (errno));
+ shm_channel_free (shm_channel);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (pipe (shm_channel->reader_put_pipe) == -1)
+ {
+ DBG (3, "shm_channel_new: cannot create reader put pipe: %s\n",
+ strerror (errno));
+ shm_channel_free (shm_channel);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[0]);
+ shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[1]);
+ shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[0]);
+ shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[1]);
+
+ shm_buffer_bytes_size = SHM_CHANNEL_ALIGN (sizeof (SANE_Int) * buf_count);
+ shm_buffer_size = SHM_CHANNEL_ALIGN (buf_size);
+ shm_size = shm_buffer_bytes_size + buf_count * shm_buffer_size;
+
+ shm_id = shmget (IPC_PRIVATE, shm_size, IPC_CREAT | SHM_R | SHM_W);
+ if (shm_id == -1)
+ {
+ DBG (3, "shm_channel_new: cannot create shared memory segment: %s\n",
+ strerror (errno));
+ shm_channel_free (shm_channel);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shm_area = shmat (shm_id, NULL, 0);
+ if (shm_area == (void *) -1)
+ {
+ DBG (3, "shm_channel_new: cannot attach to shared memory segment: %s\n",
+ strerror (errno));
+ shmctl (shm_id, IPC_RMID, NULL);
+ shm_channel_free (shm_channel);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (shmctl (shm_id, IPC_RMID, NULL) == -1)
+ {
+ DBG (3, "shm_channel_new: cannot remove shared memory segment id: %s\n",
+ strerror (errno));
+ shmdt (shm_area);
+ shmctl (shm_id, IPC_RMID, NULL);
+ shm_channel_free (shm_channel);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shm_channel->shm_area = shm_area;
+
+ shm_channel->buffer_bytes = (SANE_Int *) shm_area;
+ shm_data = ((SANE_Byte *) shm_area) + shm_buffer_bytes_size;
+ for (i = 0; i < shm_channel->buf_count; ++i)
+ {
+ shm_channel->buffers[i] = shm_data;
+ shm_data += shm_buffer_size;
+ }
+
+ *shm_channel_return = shm_channel;
+ return SANE_STATUS_GOOD;
+}
+
+/** Close the shared memory channel and release associated resources.
+ *
+ * @param shm_channel Shared memory channel object.
+ */
+SANE_Status
+shm_channel_free (Shm_Channel * shm_channel)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_free");
+
+ if (shm_channel->shm_area)
+ {
+ shmdt (shm_channel->shm_area);
+ shm_channel->shm_area = NULL;
+ }
+
+ if (shm_channel->buffers)
+ {
+ free (shm_channel->buffers);
+ shm_channel->buffers = NULL;
+ }
+
+ shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]);
+ shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]);
+ shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]);
+ shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Initialize the shared memory channel in the writer process.
+ *
+ * This function should be called after the fork in the process which will
+ * write data to the channel.
+ *
+ * @param shm_channel Shared memory channel object.
+ */
+SANE_Status
+shm_channel_writer_init (Shm_Channel * shm_channel)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_init");
+
+ shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]);
+ shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Get a free shared memory buffer for writing.
+ *
+ * This function may block waiting for a free buffer (if the reader process
+ * does not process the data fast enough).
+ *
+ * After successfull call to this function the writer process should fill the
+ * buffer with the data and pass the buffer identifier from @a buffer_id_return
+ * to shm_channel_writer_put_buffer() to give the buffer to the reader process.
+ *
+ * @param shm_channel Shared memory channel object.
+ * @param buffer_id_return Returned buffer identifier.
+ * @param buffer_addr_return Returned buffer address.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - a free buffer was available (or became available after
+ * waiting for it); @a buffer_id_return and @a buffer_addr_return are filled
+ * with valid values.
+ * - SANE_STATUS_EOF - the reader process has closed its half of the channel.
+ * - SANE_STATUS_IO_ERROR - an I/O error occured.
+ */
+SANE_Status
+shm_channel_writer_get_buffer (Shm_Channel * shm_channel,
+ SANE_Int * buffer_id_return,
+ SANE_Byte ** buffer_addr_return)
+{
+ SANE_Byte buf_index;
+ int bytes_read;
+
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_get_buffer");
+
+ do
+ bytes_read = read (shm_channel->reader_put_pipe[0], &buf_index, 1);
+ while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 1)
+ {
+ SANE_Int index = buf_index;
+ if (index < shm_channel->buf_count)
+ {
+ *buffer_id_return = index;
+ *buffer_addr_return = shm_channel->buffers[index];
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ *buffer_id_return = -1;
+ *buffer_addr_return = NULL;
+ if (bytes_read == 0)
+ return SANE_STATUS_EOF;
+ else
+ return SANE_STATUS_IO_ERROR;
+}
+
+/** Pass a filled shared memory buffer to the reader process.
+ *
+ * @param shm_channel Shared memory channel object.
+ * @param buffer_id Buffer identifier from shm_channel_writer_put_buffer().
+ * @param buffer_bytes Number of data bytes in the buffer.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - the buffer was successfully queued.
+ * - SANE_STATUS_IO_ERROR - the reader process has closed its half of the
+ * channel, or another I/O error occured.
+ */
+SANE_Status
+shm_channel_writer_put_buffer (Shm_Channel * shm_channel,
+ SANE_Int buffer_id, SANE_Int buffer_bytes)
+{
+ SANE_Byte buf_index;
+ int bytes_written;
+
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_put_buffer");
+
+ if (buffer_id < 0 || buffer_id >= shm_channel->buf_count)
+ {
+ DBG (3, "shm_channel_writer_put_buffer: BUG: buffer_id=%d\n",
+ buffer_id);
+ return SANE_STATUS_INVAL;
+ }
+
+ shm_channel->buffer_bytes[buffer_id] = buffer_bytes;
+
+ buf_index = (SANE_Byte) buffer_id;
+ do
+ bytes_written = write (shm_channel->writer_put_pipe[1], &buf_index, 1);
+ while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR));
+
+ if (bytes_written == 1)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_IO_ERROR;
+}
+
+/** Close the writing half of the shared memory channel.
+ *
+ * @param shm_channel Shared memory channel object.
+ */
+SANE_Status
+shm_channel_writer_close (Shm_Channel * shm_channel)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_close");
+
+ shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/** Initialize the shared memory channel in the reader process.
+ *
+ * This function should be called after the fork in the process which will
+ * read data from the channel.
+ *
+ * @param shm_channel Shared memory channel object.
+ */
+SANE_Status
+shm_channel_reader_init (Shm_Channel * shm_channel)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_init");
+
+ shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]);
+
+ /* Don't close reader_put_pipe[0] here. Otherwise, if the channel writer
+ * process dies early, this process might get SIGPIPE - and I don't want to
+ * mess with signals in the main process. */
+ /* shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]); */
+
+ return SANE_STATUS_GOOD;
+}
+
+#if 0
+/** Set non-blocking or blocking mode for the reading half of the shared memory
+ * channel.
+ *
+ * @param shm_channel Shared memory channel object.
+ * @param non_blocking SANE_TRUE to make the channel non-blocking, SANE_FALSE
+ * to set blocking mode.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - the requested mode was set successfully.
+ * - SANE_STATUS_IO_ERROR - error setting the requested mode.
+ */
+SANE_Status
+shm_channel_reader_set_io_mode (Shm_Channel * shm_channel,
+ SANE_Bool non_blocking)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_set_io_mode");
+
+ return shm_channel_fd_set_non_blocking (shm_channel->writer_put_pipe[0],
+ non_blocking);
+}
+
+/** Get the file descriptor which will signal when some data is available in
+ * the shared memory channel.
+ *
+ * The returned file descriptor can be used in select() or poll(). When one of
+ * these functions signals that the file descriptor is ready for reading,
+ * shm_channel_reader_get_buffer() should return some data without blocking.
+ *
+ * @param shm_channel Shared memory channel object.
+ * @param fd_return The returned file descriptor.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - the file descriptor was returned.
+ */
+SANE_Status
+shm_channel_reader_get_select_fd (Shm_Channel * shm_channel,
+ SANE_Int * fd_return)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_select_fd");
+
+ *fd_return = shm_channel->writer_put_pipe[0];
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+/** Start reading from the shared memory channel.
+ *
+ * A newly initialized shared memory channel is stopped - the writer process
+ * will block on shm_channel_writer_get_buffer(). This function will pass all
+ * available buffers to the writer process, starting the transfer through the
+ * channel.
+ *
+ * @param shm_channel Shared memory channel object.
+ */
+SANE_Status
+shm_channel_reader_start (Shm_Channel * shm_channel)
+{
+ int i, bytes_written;
+ SANE_Byte buffer_id;
+
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_start");
+
+ for (i = 0; i < shm_channel->buf_count; ++i)
+ {
+ buffer_id = i;
+ do
+ bytes_written =
+ write (shm_channel->reader_put_pipe[1], &buffer_id, 1);
+ while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR));
+
+ if (bytes_written == -1)
+ {
+ DBG (3, "shm_channel_reader_start: write error at buffer %d: %s\n",
+ i, strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Get the next shared memory buffer passed from the writer process.
+ *
+ * If the channel was not set to non-blocking mode, this function will block
+ * until the data buffer arrives from the writer process. In non-blocking mode
+ * this function will place NULL in @a *buffer_addr_return and return
+ * SANE_STATUS_GOOD if a buffer is not available immediately.
+ *
+ * After successful completion of this function (return value is
+ * SANE_STATUS_GOOD and @a *buffer_addr_return is not NULL) the reader process
+ * should process the data in the buffer and then call
+ * shm_channel_reader_put_buffer() to release the buffer.
+ *
+ * @param shm_channel Shared memory channel object.
+ * @param buffer_id_return Returned buffer identifier.
+ * @param buffer_addr_return Returned buffer address.
+ * @param buffer_bytes_return Returned number of data bytes in the buffer.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - no error. If the channel was in non-blocking mode, @a
+ * *buffer_id_return may be NULL, indicating that no data was available.
+ * Otherwise, @a *buffer_id_return, @a *buffer_addr_return and @a
+ * *buffer_bytes return are filled with valid values.
+ * - SANE_STATUS_EOF - the writer process has closed its half of the channel.
+ * - SANE_STATUS_IO_ERROR - an I/O error occured.
+ */
+SANE_Status
+shm_channel_reader_get_buffer (Shm_Channel * shm_channel,
+ SANE_Int * buffer_id_return,
+ SANE_Byte ** buffer_addr_return,
+ SANE_Int * buffer_bytes_return)
+{
+ SANE_Byte buf_index;
+ int bytes_read;
+
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_buffer");
+
+ do
+ bytes_read = read (shm_channel->writer_put_pipe[0], &buf_index, 1);
+ while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 1)
+ {
+ SANE_Int index = buf_index;
+ if (index < shm_channel->buf_count)
+ {
+ *buffer_id_return = index;
+ *buffer_addr_return = shm_channel->buffers[index];
+ *buffer_bytes_return = shm_channel->buffer_bytes[index];
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ *buffer_id_return = -1;
+ *buffer_addr_return = NULL;
+ *buffer_bytes_return = 0;
+ if (bytes_read == 0)
+ return SANE_STATUS_EOF;
+ else
+ return SANE_STATUS_IO_ERROR;
+}
+
+/** Release a shared memory buffer received by the reader process.
+ *
+ * This function must be called after shm_channel_reader_get_buffer() to
+ * release the buffer and make it available for transferring the next portion
+ * of data.
+ *
+ * After calling this function the reader process must not access the buffer
+ * contents; any data which may be needed later should be copied into some
+ * other place beforehand.
+ *
+ * @param shm_channel Shared memory channel object.
+ * @param buffer_id Buffer identifier from shm_channel_reader_get_buffer().
+ *
+ * @return
+ * - SANE_STATUS_GOOD - the buffer was successfully released.
+ * - SANE_STATUS_IO_ERROR - the writer process has closed its half of the
+ * channel, or an unexpected I/O error occured.
+ */
+SANE_Status
+shm_channel_reader_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id)
+{
+ SANE_Byte buf_index;
+ int bytes_written;
+
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_put_buffer");
+
+ if (buffer_id < 0 || buffer_id >= shm_channel->buf_count)
+ {
+ DBG (3, "shm_channel_reader_put_buffer: BUG: buffer_id=%d\n",
+ buffer_id);
+ return SANE_STATUS_INVAL;
+ }
+
+ buf_index = (SANE_Byte) buffer_id;
+ do
+ bytes_written = write (shm_channel->reader_put_pipe[1], &buf_index, 1);
+ while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR));
+
+ if (bytes_written == 1)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_IO_ERROR;
+}
+
+#if 0
+/** Close the reading half of the shared memory channel.
+ *
+ * @param shm_channel Shared memory channel object.
+ */
+SANE_Status
+shm_channel_reader_close (Shm_Channel * shm_channel)
+{
+ SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_close");
+
+ shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]);
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/gt68xx_shm_channel.h b/backend/gt68xx_shm_channel.h
new file mode 100644
index 0000000..c6cfdff
--- /dev/null
+++ b/backend/gt68xx_shm_channel.h
@@ -0,0 +1,106 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GT68XX_SHM_CHANNEL_H
+#define GT68XX_SHM_CHANNEL_H
+
+/** @file
+ * @brief Shared memory channel support.
+ */
+
+#include "../include/sane/sane.h"
+
+typedef struct Shm_Channel Shm_Channel;
+
+static SANE_Status
+shm_channel_new (SANE_Int buf_size,
+ SANE_Int buf_count, Shm_Channel ** shm_channel_return);
+
+static SANE_Status shm_channel_free (Shm_Channel * shm_channel);
+
+
+static SANE_Status shm_channel_writer_init (Shm_Channel * shm_channel);
+
+static SANE_Status
+shm_channel_writer_get_buffer (Shm_Channel * shm_channel,
+ SANE_Int * buffer_id_return,
+ SANE_Byte ** buffer_addr_return);
+
+static SANE_Status
+shm_channel_writer_put_buffer (Shm_Channel * shm_channel,
+ SANE_Int buffer_id, SANE_Int buffer_bytes);
+
+static SANE_Status shm_channel_writer_close (Shm_Channel * shm_channel);
+
+
+static SANE_Status shm_channel_reader_init (Shm_Channel * shm_channel);
+
+#if 0
+static SANE_Status
+shm_channel_reader_set_io_mode (Shm_Channel * shm_channel,
+ SANE_Bool non_blocking);
+
+static SANE_Status
+shm_channel_reader_get_select_fd (Shm_Channel * shm_channel,
+ SANE_Int * fd_return);
+
+#endif
+
+static SANE_Status shm_channel_reader_start (Shm_Channel * shm_channel);
+
+static SANE_Status
+shm_channel_reader_get_buffer (Shm_Channel * shm_channel,
+ SANE_Int * buffer_id_return,
+ SANE_Byte ** buffer_addr_return,
+ SANE_Int * buffer_bytes_return);
+
+static SANE_Status
+shm_channel_reader_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id);
+
+#if 0
+static SANE_Status shm_channel_reader_close (Shm_Channel * shm_channel);
+#endif
+
+#endif /* not GT68XX_SHM_CHANNEL_H */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/hp-accessor.c b/backend/hp-accessor.c
new file mode 100644
index 0000000..b1acd7c
--- /dev/null
+++ b/backend/hp-accessor.c
@@ -0,0 +1,902 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/* #define STUBS
+extern int sanei_debug_hp; */
+#define DEBUG_DECLARE_ONLY
+
+#include "../include/sane/config.h"
+#include "../include/sane/sanei_backend.h"
+
+#include "../include/lassert.h"
+#include <string.h>
+
+#include <sys/types.h>
+
+#include "hp.h"
+#include "hp-option.h"
+#include "hp-accessor.h"
+#include "hp-device.h"
+
+#define DATA_SIZE_INCREMENT (1024)
+
+/*
+ * class HpData
+ */
+struct hp_data_s
+{
+ hp_byte_t * buf;
+ size_t bufsiz;
+ size_t length;
+ hp_bool_t frozen;
+};
+
+
+static void
+hp_data_resize (HpData this, size_t newsize)
+{
+
+ if (this->bufsiz != newsize)
+ {
+ assert(!this->frozen);
+ this->buf = sanei_hp_realloc(this->buf, newsize);
+ assert(this->buf);
+ this->bufsiz = newsize;
+ }
+}
+
+static void
+hp_data_freeze (HpData this)
+{
+ hp_data_resize(this, this->length);
+ this->frozen = 1;
+}
+
+static size_t
+hp_data_alloc (HpData this, size_t sz)
+{
+ size_t newsize = this->bufsiz;
+ size_t offset = this->length;
+
+ /*
+ * mike@easysw.com:
+ *
+ * The following code is REQUIRED so that pointers, etc. aren't
+ * misaligned. This causes MAJOR problems on all SPARC, ALPHA,
+ * and MIPS processors, and possibly others.
+ *
+ * The workaround is to ensure that all allocations are in multiples
+ * of 8 bytes.
+ */
+ sz = (sz + sizeof (long) - 1) & ~(sizeof (long) - 1);
+
+ while (newsize < this->length + sz)
+ newsize += DATA_SIZE_INCREMENT;
+ hp_data_resize(this, newsize);
+
+ this->length += sz;
+ return offset;
+}
+
+static void *
+hp_data_data (HpData this, size_t offset)
+{
+ assert(offset < this->length);
+ return (char *)this->buf + offset;
+}
+
+HpData
+sanei_hp_data_new (void)
+{
+ return sanei_hp_allocz(sizeof(struct hp_data_s));
+}
+
+HpData
+sanei_hp_data_dup (HpData orig)
+{
+ HpData new;
+
+ hp_data_freeze(orig);
+ if (!( new = sanei_hp_memdup(orig, sizeof(*orig)) ))
+ return 0;
+ if (!(new->buf = sanei_hp_memdup(orig->buf, orig->bufsiz)))
+ {
+ sanei_hp_free(new);
+ return 0;
+ }
+ return new;
+}
+
+void
+sanei_hp_data_destroy (HpData this)
+{
+ sanei_hp_free(this->buf);
+ sanei_hp_free(this);
+}
+
+
+/*
+ * class HpAccessor
+ */
+
+typedef const struct hp_accessor_type_s * HpAccessorType;
+typedef struct hp_accessor_s * _HpAccessor;
+
+struct hp_accessor_s
+{
+ HpAccessorType type;
+ size_t data_offset;
+ size_t data_size;
+};
+
+struct hp_accessor_type_s
+{
+ SANE_Status (*get)(HpAccessor this, HpData data, void * valp);
+ SANE_Status (*set)(HpAccessor this, HpData data, void * valp);
+ int (*getint)(HpAccessor this, HpData data);
+ void (*setint)(HpAccessor this, HpData data, int val);
+};
+
+SANE_Status
+sanei_hp_accessor_get (HpAccessor this, HpData data, void * valp)
+{
+ if (!this->type->get)
+ return SANE_STATUS_INVAL;
+ return (*this->type->get)(this, data, valp);
+}
+
+SANE_Status
+sanei_hp_accessor_set (HpAccessor this, HpData data, void * valp)
+{
+ if (!this->type->set)
+ return SANE_STATUS_INVAL;
+ return (*this->type->set)(this, data, valp);
+}
+
+int
+sanei_hp_accessor_getint (HpAccessor this, HpData data)
+{
+ assert (this->type->getint);
+ return (*this->type->getint)(this, data);
+}
+
+void
+sanei_hp_accessor_setint (HpAccessor this, HpData data, int val)
+{
+ assert (this->type->setint);
+ (*this->type->setint)(this, data, val);
+}
+
+const void *
+sanei_hp_accessor_data (HpAccessor this, HpData data)
+{
+ return hp_data_data(data, this->data_offset);
+}
+
+void *
+sanei__hp_accessor_data (HpAccessor this, HpData data)
+{
+ return hp_data_data(data, this->data_offset);
+}
+
+size_t
+sanei_hp_accessor_size (HpAccessor this)
+{
+ return this->data_size;
+}
+
+HpAccessor
+sanei_hp_accessor_new (HpData data, size_t sz)
+{
+ static const struct hp_accessor_type_s type = {
+ 0, 0, 0, 0
+ };
+ _HpAccessor new = sanei_hp_alloc(sizeof(*new));
+ new->type = &type;
+ new->data_offset = hp_data_alloc(data, new->data_size = sz);
+ return new;
+}
+
+
+/*
+ * class HpAccessorInt
+ */
+
+#define hp_accessor_int_s hp_accessor_s
+
+typedef const struct hp_accessor_int_s * HpAccessorInt;
+typedef struct hp_accessor_int_s * _HpAccessorInt;
+
+static SANE_Status
+hp_accessor_int_get (HpAccessor this, HpData data, void * valp)
+{
+ *(SANE_Int*)valp = *(int *)hp_data_data(data, this->data_offset);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_accessor_int_set (HpAccessor this, HpData data, void * valp)
+{
+ *(int *)hp_data_data(data, this->data_offset) = *(SANE_Int*)valp;
+ return SANE_STATUS_GOOD;
+}
+
+static int
+hp_accessor_int_getint (HpAccessor this, HpData data)
+{
+ return *(int *)hp_data_data(data, this->data_offset);
+}
+
+static void
+hp_accessor_int_setint (HpAccessor this, HpData data, int val)
+{
+ *(int *)hp_data_data(data, this->data_offset) = val;
+}
+
+HpAccessor
+sanei_hp_accessor_int_new (HpData data)
+{
+ static const struct hp_accessor_type_s type = {
+ hp_accessor_int_get, hp_accessor_int_set,
+ hp_accessor_int_getint, hp_accessor_int_setint
+ };
+ _HpAccessorInt new = sanei_hp_alloc(sizeof(*new));
+ new->type = &type;
+ new->data_offset = hp_data_alloc(data, new->data_size = sizeof(int));
+ return (HpAccessor)new;
+}
+
+
+/*
+ * class HpAccessorBool
+ */
+
+#define hp_accessor_bool_s hp_accessor_s
+
+typedef const struct hp_accessor_bool_s * HpAccessorBool;
+typedef struct hp_accessor_bool_s * _HpAccessorBool;
+
+static SANE_Status
+hp_accessor_bool_get (HpAccessor this, HpData data, void * valp)
+{
+ int val = *(int *)hp_data_data(data, this->data_offset);
+ *(SANE_Bool*)valp = val ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_accessor_bool_set (HpAccessor this, HpData data, void * valp)
+{
+ int * datap = hp_data_data(data, this->data_offset);
+ *datap = *(SANE_Bool*)valp == SANE_FALSE ? 0 : 1;
+ return SANE_STATUS_GOOD;
+}
+
+HpAccessor
+sanei_hp_accessor_bool_new (HpData data)
+{
+ static const struct hp_accessor_type_s type = {
+ hp_accessor_bool_get, hp_accessor_bool_set,
+ hp_accessor_int_getint, hp_accessor_int_setint
+ };
+ _HpAccessorBool new = sanei_hp_alloc(sizeof(*new));
+ new->type = &type;
+ new->data_offset = hp_data_alloc(data, new->data_size = sizeof(int));
+ return (HpAccessor)new;
+}
+
+
+/*
+ * class HpAccessorFixed
+ */
+
+#define hp_accessor_fixed_s hp_accessor_s
+
+typedef const struct hp_accessor_fixed_s * HpAccessorFixed;
+typedef struct hp_accessor_fixed_s * _HpAccessorFixed;
+
+static SANE_Status
+hp_accessor_fixed_get (HpAccessor this, HpData data, void * valp)
+{
+ *(SANE_Fixed*)valp = *(SANE_Fixed *)hp_data_data(data, this->data_offset);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_accessor_fixed_set (HpAccessor this, HpData data, void * valp)
+{
+ *(SANE_Fixed *)hp_data_data(data, this->data_offset) = *(SANE_Fixed*)valp;
+ return SANE_STATUS_GOOD;
+}
+
+HpAccessor
+sanei_hp_accessor_fixed_new (HpData data)
+{
+ static const struct hp_accessor_type_s type = {
+ hp_accessor_fixed_get, hp_accessor_fixed_set, 0, 0
+ };
+ _HpAccessorFixed new = sanei_hp_alloc(sizeof(*new));
+ new->type = &type;
+ new->data_offset = hp_data_alloc(data, new->data_size = sizeof(SANE_Fixed));
+ return (HpAccessor)new;
+}
+
+
+/*
+ * class HpAccessorChoice
+ */
+
+typedef struct hp_accessor_choice_s * _HpAccessorChoice;
+
+struct hp_accessor_choice_s
+{
+ HpAccessorType type;
+ size_t data_offset;
+ size_t data_size;
+
+ HpChoice choices;
+ SANE_String_Const * strlist;
+};
+
+static SANE_Status
+hp_accessor_choice_get (HpAccessor this, HpData data, void * valp)
+{
+ HpChoice choice = *(HpChoice *)hp_data_data(data, this->data_offset);
+ strcpy(valp, choice->name);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_accessor_choice_set (HpAccessor _this, HpData data, void * valp)
+{
+ HpAccessorChoice this = (HpAccessorChoice)_this;
+ HpChoice choice;
+ SANE_String_Const * strlist = this->strlist;
+
+ for (choice = this->choices; choice; choice = choice->next)
+ {
+ /* Skip choices which aren't in strlist. */
+ if (!*strlist || strcmp(*strlist, choice->name) != 0)
+ continue;
+ strlist++;
+
+ if (strcmp((const char *)valp, choice->name) == 0)
+ {
+ *(HpChoice *)hp_data_data(data, this->data_offset) = choice;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+static int
+hp_accessor_choice_getint (HpAccessor this, HpData data)
+{
+ HpChoice choice = *(HpChoice *)hp_data_data(data, this->data_offset);
+ return choice->val;
+}
+
+static void
+hp_accessor_choice_setint (HpAccessor _this, HpData data, int val)
+{
+ HpAccessorChoice this = (HpAccessorChoice)_this;
+ HpChoice choice;
+ HpChoice first_choice = 0;
+ SANE_String_Const * strlist = this->strlist;
+
+ for (choice = this->choices; choice; choice = choice->next)
+ {
+ /* Skip choices which aren't in strlist. */
+ if (!*strlist || strcmp(*strlist, choice->name) != 0)
+ continue;
+ strlist++;
+
+ if (!first_choice)
+ first_choice = choice; /* First enabled choice */
+
+ if (choice->val == val)
+ {
+ *(HpChoice *)hp_data_data(data, this->data_offset) = choice;
+ return;
+ }
+ }
+
+ if (first_choice)
+ *(HpChoice *)hp_data_data(data, this->data_offset) = first_choice;
+ else
+ assert(!"No choices to choose from?");
+}
+
+SANE_Int
+sanei_hp_accessor_choice_maxsize (HpAccessorChoice this)
+{
+ HpChoice choice;
+ SANE_Int size = 0;
+
+ for (choice = this->choices; choice; choice = choice->next)
+ if ((SANE_Int)strlen(choice->name) >= size)
+ size = strlen(choice->name) + 1;
+ return size;
+}
+
+SANE_String_Const *
+sanei_hp_accessor_choice_strlist (HpAccessorChoice this,
+ HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ if (optset)
+ {
+ int old_val = hp_accessor_choice_getint((HpAccessor)this, data);
+ HpChoice choice;
+ size_t count = 0;
+
+ for (choice = this->choices; choice; choice = choice->next)
+ if (sanei_hp_choice_isEnabled(choice, optset, data, info))
+ this->strlist[count++] = choice->name;
+ this->strlist[count] = 0;
+
+ hp_accessor_choice_setint((HpAccessor)this, data, old_val);
+ }
+
+ return this->strlist;
+}
+
+HpAccessor
+sanei_hp_accessor_choice_new (HpData data, HpChoice choices,
+ hp_bool_t may_change)
+{
+ static const struct hp_accessor_type_s type = {
+ hp_accessor_choice_get, hp_accessor_choice_set,
+ hp_accessor_choice_getint, hp_accessor_choice_setint
+ };
+ HpChoice choice;
+ size_t count = 0;
+ _HpAccessorChoice this;
+
+ if ( may_change ) data->frozen = 0;
+
+ for (choice = choices; choice; choice = choice->next)
+ count++;
+ this = sanei_hp_alloc(sizeof(*this) + (count+1) * sizeof(*this->strlist));
+ if (!this)
+ return 0;
+
+ this->type = &type;
+ this->data_offset = hp_data_alloc(data, this->data_size = sizeof(HpChoice));
+ this->choices = choices;
+ this->strlist = (SANE_String_Const *)(this + 1);
+
+ count = 0;
+ for (choice = this->choices; choice; choice = choice->next)
+ this->strlist[count++] = choice->name;
+ this->strlist[count] = 0;
+
+ return (HpAccessor)this;
+}
+
+/*
+ * class HpAccessorVector
+ */
+
+typedef struct hp_accessor_vector_s * _HpAccessorVector;
+
+struct hp_accessor_vector_s
+{
+ HpAccessorType type;
+ size_t data_offset;
+ size_t data_size;
+
+ unsigned short mask;
+ unsigned short length;
+ unsigned short offset;
+ short stride;
+
+ unsigned short (*unscale)(HpAccessorVector this, SANE_Fixed fval);
+ SANE_Fixed (*scale)(HpAccessorVector this, unsigned short val);
+
+ SANE_Fixed fmin;
+ SANE_Fixed fmax;
+
+};
+
+unsigned
+sanei_hp_accessor_vector_length (HpAccessorVector this)
+{
+ return this->length;
+}
+
+SANE_Fixed
+sanei_hp_accessor_vector_minval (HpAccessorVector this)
+{
+ return this->fmin;
+}
+
+SANE_Fixed
+sanei_hp_accessor_vector_maxval (HpAccessorVector this)
+{
+ return this->fmax;
+}
+
+static unsigned short
+_v_get (HpAccessorVector this, const unsigned char * data)
+{
+ unsigned short val;
+
+ if (this->mask <= 255)
+ val = data[0];
+ else
+#ifndef NotOrig
+ val = (data[0] << 8) + data[1];
+#else
+ val = (data[1] << 8) + data[0];
+#endif
+
+ return val & this->mask;
+}
+
+static void
+_v_set (HpAccessorVector this, unsigned char * data, unsigned short val)
+{
+ val &= this->mask;
+
+ if (this->mask <= 255)
+ {
+ data[0] = (unsigned char)val;
+ }
+ else
+ {
+#ifndef NotOrig
+ data[1] = (unsigned char)val;
+ data[0] = (unsigned char)(val >> 8);
+#else
+ data[0] = (unsigned char)val;
+ data[1] = (unsigned char)(val >> 8);
+#endif
+ }
+}
+
+static SANE_Status
+hp_accessor_vector_get (HpAccessor _this, HpData d, void * valp)
+{
+ HpAccessorVector this = (HpAccessorVector)_this;
+ SANE_Fixed * ptr = valp;
+ const SANE_Fixed * end = ptr + this->length;
+ const unsigned char * data = hp_data_data(d, this->data_offset);
+
+ data += this->offset;
+
+ while (ptr < end)
+ {
+ *ptr++ = (*this->scale)(this, _v_get(this, data));
+ data += this->stride;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_accessor_vector_set (HpAccessor _this, HpData d, void * valp)
+{
+ HpAccessorVector this = (HpAccessorVector)_this;
+ SANE_Fixed * ptr = valp;
+ const SANE_Fixed * end = ptr + this->length;
+ unsigned char * data = hp_data_data(d, this->data_offset);
+
+ data += this->offset;
+
+ while (ptr < end)
+ {
+ if (*ptr < this->fmin)
+ *ptr = this->fmin;
+ if (*ptr > this->fmax)
+ *ptr = this->fmax;
+
+ _v_set(this, data, (*this->unscale)(this, *ptr++));
+
+ data += this->stride;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static unsigned short
+_vector_unscale (HpAccessorVector this, SANE_Fixed fval)
+{
+ unsigned short max_val = this->mask;
+ return (fval * max_val + SANE_FIX(0.5)) / SANE_FIX(1.0);
+}
+
+static SANE_Fixed
+_vector_scale (HpAccessorVector this, unsigned short val)
+{
+ unsigned short max_val = this->mask;
+ return (SANE_FIX(1.0) * val + max_val / 2) / max_val;
+}
+
+HpAccessor
+sanei_hp_accessor_vector_new (HpData data, unsigned length, unsigned depth)
+{
+ static const struct hp_accessor_type_s type = {
+ hp_accessor_vector_get, hp_accessor_vector_set, 0, 0
+ };
+ unsigned width = depth > 8 ? 2 : 1;
+ _HpAccessorVector new = sanei_hp_alloc(sizeof(*new));
+
+ if (!new)
+ return 0;
+
+ assert(depth > 0 && depth <= 16);
+ assert(length > 0);
+
+ new->type = &type;
+ new->data_size = length * width;
+ new->data_offset = hp_data_alloc(data, new->data_size);
+
+ new->mask = ((unsigned)1 << depth) - 1;
+ new->length = length;
+ new->offset = 0;
+ new->stride = width;
+
+ new->scale = _vector_scale;
+ new->unscale = _vector_unscale;
+
+ new->fmin = SANE_FIX(0.0);
+ new->fmax = SANE_FIX(1.0);
+
+ return (HpAccessor)new;
+}
+
+static unsigned short
+_gamma_vector_unscale (HpAccessorVector UNUSEDARG this, SANE_Fixed fval)
+{
+ unsigned short unscaled = fval / SANE_FIX(1.0);
+ if (unscaled > 255) unscaled = 255;
+ unscaled = 255 - unscaled; /* Dont know why. But this is how it works */
+
+ return unscaled;
+}
+
+static SANE_Fixed
+_gamma_vector_scale (HpAccessorVector UNUSEDARG this, unsigned short val)
+{
+ SANE_Fixed scaled;
+ val = 255-val; /* Dont know why. But this is how it works */
+ scaled = val * SANE_FIX(1.0);
+
+ return scaled;
+}
+
+HpAccessor
+sanei_hp_accessor_gamma_vector_new (HpData data, unsigned length,
+ unsigned depth)
+{
+ _HpAccessorVector this =
+ ( (_HpAccessorVector) sanei_hp_accessor_vector_new(data, length, depth) );
+
+
+ if (!this)
+ return 0;
+
+ this->offset += this->stride * (this->length - 1);
+ this->stride = -this->stride;
+
+ this->scale = _gamma_vector_scale;
+ this->unscale = _gamma_vector_unscale;
+
+ this->fmin = SANE_FIX(0.0);
+ this->fmax = SANE_FIX(255.0);
+
+ return (HpAccessor)this;
+}
+
+static unsigned short
+_matrix_vector_unscale (HpAccessorVector this, SANE_Fixed fval)
+{
+ unsigned short max_val = this->mask >> 1;
+ unsigned short sign_bit = this->mask & ~max_val;
+ unsigned short sign = 0;
+
+ if (fval == SANE_FIX(1.0))
+ return sign_bit;
+
+ if (fval < 0)
+ {
+ sign = sign_bit;
+ fval = -fval;
+ }
+ return sign | ((fval * max_val + this->fmax / 2) / this->fmax);
+}
+
+static SANE_Fixed
+_matrix_vector_scale (HpAccessorVector this, unsigned short val)
+{
+ unsigned short max_val = this->mask >> 1;
+ unsigned short sign_bit = this->mask & ~max_val;
+ SANE_Fixed fval;
+
+ if (val == sign_bit)
+ return SANE_FIX(1.0);
+
+ fval = (this->fmax * (val & max_val) + max_val / 2) / max_val;
+
+ if ((val & sign_bit) != 0)
+ fval = -fval;
+
+ return fval;
+}
+
+HpAccessor
+sanei_hp_accessor_matrix_vector_new (HpData data, unsigned length,
+ unsigned depth)
+{
+ _HpAccessorVector this =
+ ( (_HpAccessorVector) sanei_hp_accessor_vector_new(data, length, depth) );
+
+ if (!this)
+ return 0;
+
+ this->scale = _matrix_vector_scale;
+ this->unscale = _matrix_vector_unscale;
+
+ this->fmax = depth == 10 ? SANE_FIX(4.0) : SANE_FIX(2.0);
+ this->fmax *= (this->mask >> 1);
+ this->fmax >>= (depth - 1);
+ this->fmin = - this->fmax;
+
+ return (HpAccessor)this;
+}
+
+HpAccessor
+sanei_hp_accessor_subvector_new (HpAccessorVector super,
+ unsigned nchan, unsigned chan)
+{
+ _HpAccessorVector this = sanei_hp_memdup(super, sizeof(*this));
+
+ if (!this)
+ return 0;
+
+ assert(chan < nchan);
+ assert(this->length % nchan == 0);
+
+ this->length /= nchan;
+
+ if (this->stride < 0)
+ this->offset += (nchan - chan - 1) * this->stride;
+ else
+ this->offset += chan * this->stride;
+
+ this->stride *= nchan;
+
+ return (HpAccessor)this;
+}
+
+/*
+ * class HpAccessorGeometry
+ */
+
+typedef const struct hp_accessor_geometry_s * HpAccessorGeometry;
+typedef struct hp_accessor_geometry_s * _HpAccessorGeometry;
+
+struct hp_accessor_geometry_s
+{
+ HpAccessorType type;
+ size_t data_offset;
+ size_t data_size;
+
+ HpAccessor this;
+ HpAccessor other;
+ hp_bool_t is_br;
+ HpAccessor resolution;
+};
+
+
+static SANE_Status
+hp_accessor_geometry_set (HpAccessor _this, HpData data, void * _valp)
+{
+ HpAccessorGeometry this = (HpAccessorGeometry)_this;
+ SANE_Fixed * valp = _valp;
+ SANE_Fixed limit;
+
+ sanei_hp_accessor_get(this->other, data, &limit);
+ if (this->is_br ? *valp < limit : *valp > limit)
+ *valp = limit;
+ return sanei_hp_accessor_set(this->this, data, valp);
+}
+
+static int
+_to_devpixels (SANE_Fixed val_mm, SANE_Fixed mm_per_pix)
+{
+ assert(val_mm >= 0);
+ return (val_mm + mm_per_pix / 2) / mm_per_pix;
+}
+
+static int
+hp_accessor_geometry_getint (HpAccessor _this, HpData data)
+{
+ HpAccessorGeometry this = (HpAccessorGeometry)_this;
+ SANE_Fixed this_val, other_val;
+ int res = sanei_hp_accessor_getint(this->resolution,
+ data);
+ SANE_Fixed mm_per_pix = (SANE_FIX(MM_PER_INCH) + res / 2) / res;
+
+ assert(res > 0);
+ sanei_hp_accessor_get(this->this, data, &this_val);
+
+ if (this->is_br)
+ {
+ /* Convert to extent. */
+ sanei_hp_accessor_get(this->other, data, &other_val);
+ assert(this_val >= other_val && other_val >= 0);
+ return (_to_devpixels(this_val, mm_per_pix)
+ - _to_devpixels(other_val, mm_per_pix) + 1);
+ }
+ return _to_devpixels(this_val, mm_per_pix);
+}
+
+/*
+ * we should implement hp_accessor_geometry_setint, but we don't
+ * need it yet...
+ */
+
+
+HpAccessor
+sanei_hp_accessor_geometry_new (HpAccessor val, HpAccessor lim, hp_bool_t is_br,
+ HpAccessor resolution)
+{
+ static const struct hp_accessor_type_s type = {
+ hp_accessor_fixed_get, hp_accessor_geometry_set,
+ hp_accessor_geometry_getint, 0
+ };
+ _HpAccessorGeometry new = sanei_hp_alloc(sizeof(*new));
+
+ new->type = &type;
+ new->data_offset = val->data_offset;
+ new->data_size = val->data_size;
+
+ new->this = val;
+ new->other = lim;
+ new->is_br = is_br;
+ new->resolution = resolution;
+
+ return (HpAccessor)new;
+}
diff --git a/backend/hp-accessor.h b/backend/hp-accessor.h
new file mode 100644
index 0000000..f043c3f
--- /dev/null
+++ b/backend/hp-accessor.h
@@ -0,0 +1,88 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+#ifndef HP_ACCESSOR_H_INCLUDED
+#define HP_ACCESSOR_H_INCLUDED
+#include "hp.h"
+
+HpData sanei_hp_data_new (void);
+HpData sanei_hp_data_dup (HpData orig);
+void sanei_hp_data_destroy (HpData this);
+
+HpAccessor sanei_hp_accessor_new (HpData data, size_t size);
+HpAccessor sanei_hp_accessor_int_new (HpData data);
+HpAccessor sanei_hp_accessor_bool_new (HpData data);
+HpAccessor sanei_hp_accessor_fixed_new (HpData data);
+HpAccessor sanei_hp_accessor_choice_new(HpData data, HpChoice choices,
+ hp_bool_t may_change);
+HpAccessor sanei_hp_accessor_vector_new(HpData data,
+ unsigned length, unsigned depth);
+HpAccessor sanei_hp_accessor_gamma_vector_new(HpData data,
+ unsigned length, unsigned depth);
+HpAccessor sanei_hp_accessor_matrix_vector_new(HpData data,
+ unsigned length, unsigned depth);
+HpAccessor sanei_hp_accessor_subvector_new(HpAccessorVector super,
+ unsigned nchan, unsigned chan);
+
+HpAccessor sanei_hp_accessor_geometry_new (HpAccessor val, HpAccessor lim,
+ hp_bool_t is_br, HpAccessor res);
+
+SANE_Status sanei_hp_accessor_get (HpAccessor this, HpData data, void * valp);
+SANE_Status sanei_hp_accessor_set (HpAccessor this, HpData data, void * valp);
+int sanei_hp_accessor_getint(HpAccessor this, HpData data);
+void sanei_hp_accessor_setint(HpAccessor this, HpData data, int v);
+const void *sanei_hp_accessor_data (HpAccessor this, HpData data);
+void * sanei__hp_accessor_data (HpAccessor this, HpData data);
+size_t sanei_hp_accessor_size (HpAccessor this);
+
+unsigned sanei_hp_accessor_vector_length (HpAccessorVector this);
+SANE_Fixed sanei_hp_accessor_vector_minval (HpAccessorVector this);
+SANE_Fixed sanei_hp_accessor_vector_maxval (HpAccessorVector this);
+
+SANE_Int sanei_hp_accessor_choice_maxsize (HpAccessorChoice this);
+SANE_String_Const *
+ sanei_hp_accessor_choice_strlist (HpAccessorChoice this, HpOptSet optset,
+ HpData data, const HpDeviceInfo *info);
+
+#endif /* HP_ACCESSOR_H_INCLUDED */
diff --git a/backend/hp-device.c b/backend/hp-device.c
new file mode 100644
index 0000000..2b96ad6
--- /dev/null
+++ b/backend/hp-device.c
@@ -0,0 +1,476 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/*#define STUBS
+extern int sanei_debug_hp;*/
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "../include/lassert.h"
+#include "hp-device.h"
+#include "hp-accessor.h"
+#include "hp-option.h"
+#include "hp-scsi.h"
+#include "hp-scl.h"
+
+/* Mark an scl-command to be simulated */
+SANE_Status
+sanei_hp_device_simulate_set (const char *devname, HpScl scl, int flag)
+
+{HpDeviceInfo *info;
+ int inqid;
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return SANE_STATUS_INVAL;
+
+ inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN;
+ info->simulate.sclsimulate[inqid] = flag;
+
+ DBG(3, "hp_device_simulate_set: %d set to %ssimulated\n",
+ inqid+HP_SCL_INQID_MIN, flag ? "" : "not ");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Clear all simulation flags */
+void
+sanei_hp_device_simulate_clear (const char *devname)
+
+{HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return;
+
+ memset (&(info->simulate.sclsimulate[0]), 0,
+ sizeof (info->simulate.sclsimulate));
+
+ info->simulate.gamma_simulate = 0;
+}
+
+/* Get simulate flag for an scl-command */
+hp_bool_t
+sanei_hp_device_simulate_get (const char *devname, HpScl scl)
+
+{HpDeviceInfo *info;
+ int inqid;
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return 0;
+
+ inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN;
+ return info->simulate.sclsimulate[inqid];
+}
+
+SANE_Status
+sanei_hp_device_support_get (const char *devname, HpScl scl,
+ int *minval, int *maxval)
+
+{HpDeviceInfo *info;
+ HpSclSupport *sclsupport;
+ int inqid;
+
+/* #define HP_TEST_SIMULATE */
+#ifdef HP_TEST_SIMULATE
+ if (scl == SCL_BRIGHTNESS) return SANE_STATUS_UNSUPPORTED;
+ if (scl == SCL_CONTRAST) return SANE_STATUS_UNSUPPORTED;
+ if (scl == SCL_DOWNLOAD_TYPE)
+ {
+ *minval = 2; *maxval = 14;
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return SANE_STATUS_INVAL;
+
+ inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN;
+ sclsupport = &(info->sclsupport[inqid]);
+
+ if ( !(sclsupport->checked) ) return SANE_STATUS_INVAL;
+ if ( !(sclsupport->is_supported) ) return SANE_STATUS_UNSUPPORTED;
+
+ if (minval) *minval = sclsupport->minval;
+ if (maxval) *maxval = sclsupport->maxval;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Update the list of supported commands */
+SANE_Status
+sanei_hp_device_support_probe (HpScsi scsi)
+
+{HpDeviceInfo *info;
+ HpSclSupport *sclsupport;
+ SANE_Status status;
+ int k, val, inqid;
+ static HpScl sclprobe[] = /* The commands that should be probed */
+ {
+ SCL_AUTO_BKGRND,
+ SCL_COMPRESSION,
+ SCL_DOWNLOAD_TYPE,
+ SCL_X_SCALE,
+ SCL_Y_SCALE,
+ SCL_DATA_WIDTH,
+ SCL_INVERSE_IMAGE,
+ SCL_BW_DITHER,
+ SCL_CONTRAST,
+ SCL_BRIGHTNESS,
+#ifdef SCL_SHARPENING
+ SCL_SHARPENING,
+#endif
+ SCL_MIRROR_IMAGE,
+ SCL_X_RESOLUTION,
+ SCL_Y_RESOLUTION,
+ SCL_OUTPUT_DATA_TYPE,
+ SCL_PRELOAD_ADF,
+ SCL_MEDIA,
+ SCL_X_EXTENT,
+ SCL_Y_EXTENT,
+ SCL_X_POS,
+ SCL_Y_POS,
+ SCL_SPEED,
+ SCL_FILTER,
+ SCL_TONE_MAP,
+ SCL_MATRIX,
+ SCL_UNLOAD,
+ SCL_CHANGE_DOC,
+ SCL_ADF_BFEED
+ };
+ enum hp_device_compat_e compat;
+
+ DBG(1, "hp_device_support_probe: Check supported commands for %s\n",
+ sanei_hp_scsi_devicename (scsi) );
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ assert (info);
+
+ memset (&(info->sclsupport[0]), 0, sizeof (info->sclsupport));
+
+ for (k = 0; k < (int)(sizeof (sclprobe) / sizeof (sclprobe[0])); k++)
+ {
+ inqid = SCL_INQ_ID(sclprobe[k])-HP_SCL_INQID_MIN;
+ sclsupport = &(info->sclsupport[inqid]);
+ status = sanei_hp_scl_inquire (scsi, sclprobe[k], &val,
+ &(sclsupport->minval),
+ &(sclsupport->maxval));
+ sclsupport->is_supported = (status == SANE_STATUS_GOOD);
+ sclsupport->checked = 1;
+
+ /* The OfficeJets seem to ignore brightness and contrast settings,
+ * so we'll pretend they're not supported at all. */
+ if (((sclprobe[k]==SCL_BRIGHTNESS) || (sclprobe[k]==SCL_CONTRAST)) &&
+ (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) &&
+ (compat & HP_COMPAT_OJ_1150C)) {
+ sclsupport->is_supported=0;
+ }
+
+ if (sclsupport->is_supported)
+ {
+ DBG(1, "hp_device_support_probe: %d supported (%d..%d, %d)\n",
+ inqid+HP_SCL_INQID_MIN, sclsupport->minval, sclsupport->maxval, val);
+ }
+ else
+ {
+ DBG(1, "hp_device_support_probe: %d not supported\n",
+ inqid+HP_SCL_INQID_MIN);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_device_probe_model (enum hp_device_compat_e *compat, HpScsi scsi,
+ int *model_num, const char **model_name)
+{
+ static struct {
+ HpScl cmd;
+ int model_num;
+ const char * model;
+ enum hp_device_compat_e flag;
+ } probes[] = {
+ { SCL_HP_MODEL_1, 1, "ScanJet Plus", HP_COMPAT_PLUS },
+ { SCL_HP_MODEL_2, 2, "ScanJet IIc", HP_COMPAT_2C },
+ { SCL_HP_MODEL_3, 3, "ScanJet IIp", HP_COMPAT_2P },
+ { SCL_HP_MODEL_4, 4, "ScanJet IIcx", HP_COMPAT_2CX },
+ { SCL_HP_MODEL_5, 5, "ScanJet 3c/4c/6100C", HP_COMPAT_4C },
+ { SCL_HP_MODEL_6, 6, "ScanJet 3p", HP_COMPAT_3P },
+ { SCL_HP_MODEL_8, 8, "ScanJet 4p", HP_COMPAT_4P },
+ { SCL_HP_MODEL_9, 9, "ScanJet 5p/4100C/5100C", HP_COMPAT_5P },
+ { SCL_HP_MODEL_10,10,"PhotoSmart Photo Scanner", HP_COMPAT_PS },
+ { SCL_HP_MODEL_11,11,"OfficeJet 1150C", HP_COMPAT_OJ_1150C },
+ { SCL_HP_MODEL_12,12,"OfficeJet 1170C or later", HP_COMPAT_OJ_1170C },
+ { SCL_HP_MODEL_14,14,"ScanJet 62x0C", HP_COMPAT_6200C },
+ { SCL_HP_MODEL_16,15,"ScanJet 5200C", HP_COMPAT_5200C },
+ { SCL_HP_MODEL_17,17,"ScanJet 63x0C", HP_COMPAT_6300C }
+ };
+ int i;
+ char buf[8];
+ size_t len;
+ SANE_Status status;
+ static char *last_device = NULL;
+ static enum hp_device_compat_e last_compat;
+ static int last_model_num = -1;
+ static const char *last_model_name = "Model Unknown";
+
+ assert(scsi);
+ DBG(1, "probe_scanner: Probing %s\n", sanei_hp_scsi_devicename (scsi));
+
+ if (last_device != NULL) /* Look if we already probed the device */
+ {
+ if (strcmp (last_device, sanei_hp_scsi_devicename (scsi)) == 0)
+ {
+ DBG(3, "probe_scanner: use cached compatibility flags\n");
+ *compat = last_compat;
+ if (model_num) *model_num = last_model_num;
+ if (model_name) *model_name = last_model_name;
+ return SANE_STATUS_GOOD;
+ }
+ sanei_hp_free (last_device);
+ last_device = NULL;
+ }
+ *compat = 0;
+ last_model_num = -1;
+ last_model_name = "Model Unknown";
+ for (i = 0; i < (int)(sizeof(probes)/sizeof(probes[0])); i++)
+ {
+ DBG(1,"probing %s\n",probes[i].model);
+
+ len = sizeof(buf);
+ if (!FAILED( status = sanei_hp_scl_upload(scsi, probes[i].cmd,
+ buf, sizeof(buf)) ))
+ {
+ DBG(1, "probe_scanner: %s compatible (%5s)\n", probes[i].model, buf);
+ last_model_name = probes[i].model;
+ /* Some scanners have different responses */
+ if (probes[i].model_num == 9)
+ {
+ if (strncmp (buf, "5110A", 5) == 0)
+ last_model_name = "ScanJet 5p";
+ else if (strncmp (buf, "5190A", 5) == 0)
+ last_model_name = "ScanJet 5100C";
+ else if (strncmp (buf, "6290A", 5) == 0)
+ last_model_name = "ScanJet 4100C";
+ }
+ *compat |= probes[i].flag;
+ last_model_num = probes[i].model_num;
+ }
+ else if (!UNSUPPORTED( status ))
+ return status; /* SCL inquiry failed */
+ }
+ /* Save values for next call */
+ last_device = sanei_hp_strdup (sanei_hp_scsi_devicename (scsi));
+ last_compat = *compat;
+ if (model_num) *model_num = last_model_num;
+ if (model_name) *model_name = last_model_name;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_device_probe (enum hp_device_compat_e *compat, HpScsi scsi)
+{
+ return sanei_hp_device_probe_model (compat, scsi, 0, 0);
+}
+
+hp_bool_t
+sanei_hp_device_compat (HpDevice this, enum hp_device_compat_e which)
+{
+ return (this->compat & which) != 0;
+}
+
+static SANE_Status
+hp_nonscsi_device_new (HpDevice * newp, const char * devname, HpConnect connect)
+{
+ HpDevice this;
+ HpScsi scsi;
+ SANE_Status status;
+ const char * model_name = "ScanJet";
+
+ if (FAILED( sanei_hp_nonscsi_new(&scsi, devname, connect) ))
+ {
+ DBG(1, "%s: Can't open nonscsi device\n", devname);
+ return SANE_STATUS_INVAL; /* Can't open device */
+ }
+
+ /* reset scanner; returns all parameters to defaults */
+ if (FAILED( sanei_hp_scl_reset(scsi) ))
+ {
+ DBG(1, "hp_nonscsi_device_new: SCL reset failed\n");
+ sanei_hp_scsi_destroy(scsi,1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Things seem okay, allocate new device */
+ this = sanei_hp_allocz(sizeof(*this));
+ this->data = sanei_hp_data_new();
+
+ if (!this || !this->data)
+ return SANE_STATUS_NO_MEM;
+
+ this->sanedev.name = sanei_hp_strdup(devname);
+ if (!this->sanedev.name)
+ return SANE_STATUS_NO_MEM;
+ this->sanedev.vendor = "Hewlett-Packard";
+ this->sanedev.type = "flatbed scanner";
+
+ status = sanei_hp_device_probe_model (&(this->compat), scsi, 0, &model_name);
+ if (!FAILED(status))
+ {
+ sanei_hp_device_support_probe (scsi);
+ status = sanei_hp_optset_new(&(this->options), scsi, this);
+ }
+ sanei_hp_scsi_destroy(scsi,1);
+
+ if (!model_name) model_name = "ScanJet";
+ this->sanedev.model = sanei_hp_strdup (model_name);
+ if (!this->sanedev.model)
+ return SANE_STATUS_NO_MEM;
+
+ if (FAILED(status))
+ {
+ DBG(1, "hp_nonscsi_device_new: %s: probe failed (%s)\n",
+ devname, sane_strstatus(status));
+ sanei_hp_data_destroy(this->data);
+ sanei_hp_free((void *)this->sanedev.name);
+ sanei_hp_free((void *)this->sanedev.model);
+ sanei_hp_free(this);
+ return status;
+ }
+
+ DBG(1, "hp_nonscsi_device_new: %s: found HP ScanJet model %s\n",
+ devname, this->sanedev.model);
+
+ *newp = this;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_device_new (HpDevice * newp, const char * devname)
+{
+ HpDevice this;
+ HpScsi scsi;
+ HpConnect connect;
+ SANE_Status status;
+ char * str;
+
+ DBG(3, "sanei_hp_device_new: %s\n", devname);
+
+ connect = sanei_hp_get_connect (devname);
+ if ( connect != HP_CONNECT_SCSI )
+ return hp_nonscsi_device_new (newp, devname, connect);
+
+ if (FAILED( sanei_hp_scsi_new(&scsi, devname) ))
+ {
+ DBG(1, "%s: Can't open scsi device\n", devname);
+ return SANE_STATUS_INVAL; /* Can't open device */
+ }
+
+ if (sanei_hp_scsi_inq(scsi)[0] != 0x03
+ || memcmp(sanei_hp_scsi_vendor(scsi), "HP ", 8) != 0)
+ {
+ DBG(1, "%s: does not seem to be an HP scanner\n", devname);
+ sanei_hp_scsi_destroy(scsi,1);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* reset scanner; returns all parameters to defaults */
+ if (FAILED( sanei_hp_scl_reset(scsi) ))
+ {
+ DBG(1, "sanei_hp_device_new: SCL reset failed\n");
+ sanei_hp_scsi_destroy(scsi,1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Things seem okay, allocate new device */
+ this = sanei_hp_allocz(sizeof(*this));
+ this->data = sanei_hp_data_new();
+
+ if (!this || !this->data)
+ return SANE_STATUS_NO_MEM;
+
+ this->sanedev.name = sanei_hp_strdup(devname);
+ str = sanei_hp_strdup(sanei_hp_scsi_model(scsi));
+ if (!this->sanedev.name || !str)
+ return SANE_STATUS_NO_MEM;
+ this->sanedev.model = str;
+ if ((str = strchr(str, ' ')) != 0)
+ *str = '\0';
+ this->sanedev.vendor = "Hewlett-Packard";
+ this->sanedev.type = "flatbed scanner";
+
+ status = sanei_hp_device_probe(&(this->compat), scsi);
+ if (!FAILED(status))
+ {
+ sanei_hp_device_support_probe (scsi);
+ status = sanei_hp_optset_new(&this->options, scsi, this);
+ }
+ sanei_hp_scsi_destroy(scsi,1);
+
+ if (FAILED(status))
+ {
+ DBG(1, "sanei_hp_device_new: %s: probe failed (%s)\n",
+ devname, sane_strstatus(status));
+ sanei_hp_data_destroy(this->data);
+ sanei_hp_free((void *)this->sanedev.name);
+ sanei_hp_free((void *)this->sanedev.model);
+ sanei_hp_free(this);
+ return status;
+ }
+
+ DBG(1, "sanei_hp_device_new: %s: found HP ScanJet model %s\n",
+ devname, this->sanedev.model);
+
+ *newp = this;
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Device *
+sanei_hp_device_sanedevice (HpDevice this)
+{
+ return &this->sanedev;
+}
+
diff --git a/backend/hp-device.h b/backend/hp-device.h
new file mode 100644
index 0000000..272a78a
--- /dev/null
+++ b/backend/hp-device.h
@@ -0,0 +1,94 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+#ifndef HP_DEVICE_INCLUDED
+#define HP_DEVICE_INCLUDED
+#include "hp.h"
+
+enum hp_device_compat_e {
+ HP_COMPAT_PLUS = 1 << 0, /* HP ScanJet Plus */
+ HP_COMPAT_2C = 1 << 1,
+ HP_COMPAT_2P = 1 << 2,
+ HP_COMPAT_2CX = 1 << 3,
+ HP_COMPAT_4C = 1 << 4, /* also 3c, 6100C */
+ HP_COMPAT_3P = 1 << 5,
+ HP_COMPAT_4P = 1 << 6,
+ HP_COMPAT_5P = 1 << 7, /* also 4100C, 5100C */
+ HP_COMPAT_5100C = 1 << 8, /* redundant with 5p */
+ HP_COMPAT_PS = 1 << 9, /* HP PhotoSmart Photo Scanner */
+ HP_COMPAT_OJ_1150C = 1 << 10,
+ HP_COMPAT_OJ_1170C = 1 << 11, /* also later OfficeJets */
+ HP_COMPAT_6200C = 1 << 12,
+ HP_COMPAT_5200C = 1 << 13,
+ HP_COMPAT_6300C = 1 << 14
+};
+
+struct hp_device_s
+{
+ HpData data;
+ HpOptSet options;
+ SANE_Device sanedev;
+ enum hp_device_compat_e compat;
+};
+
+SANE_Status sanei_hp_device_new (HpDevice * new, const char * devname);
+
+const SANE_Device * sanei_hp_device_sanedevice (HpDevice this);
+
+void sanei_hp_device_simulate_clear (const char *devname);
+SANE_Status sanei_hp_device_simulate_set (const char *devname, HpScl scl,
+ int flag);
+SANE_Status sanei_hp_device_support_get (const char *devname, HpScl scl,
+ int *minval, int *maxval);
+SANE_Status sanei_hp_device_support_probe (HpScsi scsi);
+SANE_Status sanei_hp_device_probe_model (enum hp_device_compat_e *compat,
+ HpScsi scsi, int *model_num,
+ const char **model_name);
+SANE_Status sanei_hp_device_probe (enum hp_device_compat_e *compat,
+ HpScsi scsi);
+hp_bool_t sanei_hp_device_compat (HpDevice this,
+ enum hp_device_compat_e c);
+
+
+#endif /* HP_DEVICE_INCLUDED */
diff --git a/backend/hp-handle.c b/backend/hp-handle.c
new file mode 100644
index 0000000..d9be2d5
--- /dev/null
+++ b/backend/hp-handle.c
@@ -0,0 +1,790 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/* #define STUBS
+extern int sanei_debug_hp; */
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <string.h>
+#include <signal.h>
+#include "../include/lassert.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "hp-handle.h"
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_thread.h"
+
+#include "hp-device.h"
+#include "hp-option.h"
+#include "hp-accessor.h"
+#include "hp-scsi.h"
+#include "hp-scl.h"
+
+struct hp_handle_s
+{
+ HpData data;
+ HpDevice dev;
+ SANE_Parameters scan_params;
+
+ SANE_Pid reader_pid;
+ int child_forked; /* Flag if we used fork() or not */
+ size_t bytes_left;
+ int pipe_read_fd;
+ sigset_t sig_set;
+
+ sig_atomic_t cancelled;
+
+ /* These data are used by the child */
+ HpScsi scsi;
+ HpProcessData procdata;
+ int pipe_write_fd;
+};
+
+
+static hp_bool_t
+hp_handle_isScanning (HpHandle this)
+{
+ return this->reader_pid != 0;
+}
+
+/*
+ * reader thread. Used when threads are used
+ */
+static int
+reader_thread (void *data)
+{
+ struct hp_handle_s *this = (struct hp_handle_s *) data;
+ struct SIGACTION act;
+ SANE_Status status;
+
+ DBG (1, "reader_thread: thread started\n"
+ " parameters: scsi = 0x%08lx, pipe_write_fd = %d\n",
+ (long) this->scsi, this->pipe_write_fd);
+
+ memset(&act, 0, sizeof(act));
+ sigaction(SIGTERM, &act, 0);
+
+ DBG (1, "Starting sanei_hp_scsi_pipeout()\n");
+ status = sanei_hp_scsi_pipeout (this->scsi, this->pipe_write_fd,
+ &(this->procdata));
+ DBG (1, "sanei_hp_scsi_pipeout finished with %s\n", sane_strstatus (status));
+
+ close (this->pipe_write_fd);
+ this->pipe_write_fd = -1;
+ sanei_hp_scsi_destroy (this->scsi, 0);
+ return status;
+}
+
+/*
+ * reader process. Used when forking child.
+ */
+static int
+reader_process (void *data)
+{
+ struct hp_handle_s *this = (struct hp_handle_s *) data;
+ struct SIGACTION sa;
+ SANE_Status status;
+
+ /* Here we are in a forked child. The thread will not come up to here. */
+ /* Forked child must close read end of pipe */
+ close (this->pipe_read_fd);
+ this->pipe_read_fd = -1;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, 0);
+ sigdelset(&(this->sig_set), SIGTERM);
+ sigprocmask(SIG_SETMASK, &(this->sig_set), 0);
+
+ /* not closing writing end of pipe gives an infinite loop on Digital UNIX */
+ status = sanei_hp_scsi_pipeout (this->scsi, this->pipe_write_fd,
+ &(this->procdata));
+ close (this->pipe_write_fd);
+ this->pipe_write_fd = -1;
+ DBG(3,"reader_process: Exiting child (%s)\n",sane_strstatus(status));
+ return (status);
+}
+
+static SANE_Status
+hp_handle_startReader (HpHandle this, HpScsi scsi)
+{
+ int fds[2];
+ sigset_t old_set;
+
+ assert(this->reader_pid == 0);
+ this->cancelled = 0;
+ this->pipe_write_fd = this->pipe_read_fd = -1;
+
+ if (pipe(fds))
+ return SANE_STATUS_IO_ERROR;
+
+ sigfillset(&(this->sig_set));
+ sigprocmask(SIG_BLOCK, &(this->sig_set), &old_set);
+
+ this->scsi = scsi;
+ this->pipe_write_fd = fds[1];
+ this->pipe_read_fd = fds[0];
+
+ /* Will childs be forked ? */
+ this->child_forked = sanei_thread_is_forked ();
+
+ /* Start a thread or fork a child. None of them will return here. */
+ /* Returning means to be in the parent or thread/fork failed */
+ this->reader_pid = sanei_thread_begin (this->child_forked ? reader_process :
+ reader_thread, (void *) this);
+ if (this->reader_pid != 0)
+ {
+ /* Here we are in the parent */
+ sigprocmask(SIG_SETMASK, &old_set, 0);
+
+ if ( this->child_forked )
+ { /* After fork(), parent must close writing end of pipe */
+ DBG(3, "hp_handle_startReader: parent closes write end of pipe\n");
+ close (this->pipe_write_fd);
+ this->pipe_write_fd = -1;
+ }
+
+ if (this->reader_pid == -1) /* Creating child failed ? Clean up pipe */
+ {
+ if ( !this->child_forked )
+ {
+ close (this->pipe_write_fd);
+ this->pipe_write_fd = -1;
+ }
+ close (this->pipe_read_fd);
+ this->pipe_read_fd = -1;
+
+ DBG(1, "hp_handle_startReader: fork() failed\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(1, "start_reader: reader process %ld started\n", (long) this->reader_pid);
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(3, "Unexpected return from sanei_thread_begin()\n");
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+hp_handle_stopScan (HpHandle this)
+{
+ HpScsi scsi;
+
+ this->cancelled = 0;
+ this->bytes_left = 0;
+
+ if (this->reader_pid)
+ {
+ int info;
+ DBG(3, "hp_handle_stopScan: killing child (%ld)\n", (long) this->reader_pid);
+ if (this->child_forked)
+ {
+ kill(this->reader_pid, SIGTERM);
+ waitpid(this->reader_pid, &info, 0);
+ }
+ else
+ {
+ sanei_thread_kill (this->reader_pid);
+ sanei_thread_waitpid(this->reader_pid, &info);
+ }
+ DBG(1, "hp_handle_stopScan: child %s = %d\n",
+ WIFEXITED(info) ? "exited, status" : "signalled, signal",
+ WIFEXITED(info) ? WEXITSTATUS(info) : WTERMSIG(info));
+ close(this->pipe_read_fd);
+ this->reader_pid = 0;
+
+ if ( !FAILED( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name)) )
+ {
+ if (WIFSIGNALED(info))
+ {
+ /*
+ sanei_hp_scl_set(scsi, SCL_CLEAR_ERRORS, 0);
+ sanei_hp_scl_errcheck(scsi);
+ */
+ sanei_hp_scl_reset(scsi);
+ }
+ sanei_hp_scsi_destroy(scsi,0);
+ }
+ }
+ else
+ {
+ DBG(3, "hp_handle_stopScan: no pid for child\n");
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_handle_uploadParameters (HpHandle this, HpScsi scsi, int *scan_depth,
+ hp_bool_t *soft_invert, hp_bool_t *out8)
+{
+ SANE_Parameters * p = &this->scan_params;
+ int data_width;
+ enum hp_device_compat_e compat;
+
+ assert(scsi);
+
+ *soft_invert = 0;
+ *out8 = 0;
+
+ p->last_frame = SANE_TRUE;
+ /* inquire resulting size of image after setting it up */
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_PIXELS_PER_LINE,
+ &p->pixels_per_line,0,0) );
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_BYTES_PER_LINE,
+ &p->bytes_per_line,0,0) );
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_NUMBER_OF_LINES,
+ &p->lines,0,0));
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_DATA_WIDTH,
+ &data_width,0,0));
+
+ switch (sanei_hp_optset_scanmode(this->dev->options, this->data)) {
+ case HP_SCANMODE_LINEART: /* Lineart */
+ case HP_SCANMODE_HALFTONE: /* Halftone */
+ p->format = SANE_FRAME_GRAY;
+ p->depth = 1;
+ *scan_depth = 1;
+
+ /* The OfficeJets don't seem to handle SCL_INVERSE_IMAGE, so we'll
+ * have to invert in software. */
+ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_OJ_1150C)) {
+ *soft_invert=1;
+ }
+
+ break;
+ case HP_SCANMODE_GRAYSCALE: /* Grayscale */
+ p->format = SANE_FRAME_GRAY;
+ p->depth = (data_width > 8) ? 16 : 8;
+ *scan_depth = data_width;
+
+ /* 8 bit output forced ? */
+ if ( *scan_depth > 8 )
+ {
+ *out8 = sanei_hp_optset_output_8bit (this->dev->options, this->data);
+ DBG(1,"hp_handle_uploadParameters: out8=%d\n", (int)*out8);
+ if (*out8)
+ {
+ p->depth = 8;
+ p->bytes_per_line /= 2;
+ }
+ }
+ break;
+ case HP_SCANMODE_COLOR: /* RGB */
+ p->format = SANE_FRAME_RGB;
+ p->depth = (data_width > 24) ? 16 : 8;
+ *scan_depth = data_width / 3;
+
+ /* 8 bit output forced ? */
+ if ( *scan_depth > 8 )
+ {
+ *out8 = sanei_hp_optset_output_8bit (this->dev->options, this->data);
+ DBG(1,"hp_handle_uploadParameters: out8=%d\n", (int)*out8);
+ if (*out8)
+ {
+ p->depth = 8;
+ p->bytes_per_line /= 2;
+ }
+ }
+ /* HP PhotoSmart does not invert when depth > 8. Lets do it by software */
+ if ( (*scan_depth > 8)
+ && (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_PS) )
+ *soft_invert = 1;
+ DBG(1, "hp_handle_uploadParameters: data width %d\n", data_width);
+ break;
+ default:
+ assert(!"Aack");
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+HpHandle
+sanei_hp_handle_new (HpDevice dev)
+{
+ HpHandle new = sanei_hp_allocz(sizeof(*new));
+
+ if (!new)
+ return 0;
+
+ if (!(new->data = sanei_hp_data_dup(dev->data)))
+ {
+ sanei_hp_free(new);
+ return 0;
+ }
+
+ new->dev = dev;
+ return new;
+}
+
+void
+sanei_hp_handle_destroy (HpHandle this)
+{
+ HpScsi scsi=0;
+
+ DBG(3,"sanei_hp_handle_destroy: stop scan\n");
+
+ hp_handle_stopScan(this);
+
+ if (sanei_hp_scsi_new(&scsi,this->dev->sanedev.name)==SANE_STATUS_GOOD &&
+ scsi) {
+ sanei_hp_scsi_destroy(scsi,1);
+ }
+
+ sanei_hp_data_destroy(this->data);
+ sanei_hp_free(this);
+}
+
+const SANE_Option_Descriptor *
+sanei_hp_handle_saneoption (HpHandle this, SANE_Int optnum)
+{
+ if (this->cancelled)
+ {
+ DBG(1, "sanei_hp_handle_saneoption: cancelled. Stop scan\n");
+ hp_handle_stopScan(this);
+ }
+ return sanei_hp_optset_saneoption(this->dev->options, this->data, optnum);
+}
+
+SANE_Status
+sanei_hp_handle_control(HpHandle this, SANE_Int optnum,
+ SANE_Action action, void *valp, SANE_Int *info)
+{
+ SANE_Status status;
+ HpScsi scsi;
+ hp_bool_t immediate;
+
+ if (this->cancelled)
+ {
+ DBG(1, "sanei_hp_handle_control: cancelled. Stop scan\n");
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+ }
+
+ if (hp_handle_isScanning(this))
+ return SANE_STATUS_DEVICE_BUSY;
+
+ RETURN_IF_FAIL( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) );
+
+ immediate = sanei_hp_optset_isImmediate(this->dev->options, optnum);
+
+ status = sanei_hp_optset_control(this->dev->options, this->data,
+ optnum, action, valp, info, scsi,
+ immediate);
+ sanei_hp_scsi_destroy ( scsi,0 );
+
+ return status;
+}
+
+SANE_Status
+sanei_hp_handle_getParameters (HpHandle this, SANE_Parameters *params)
+{
+ SANE_Status status;
+
+ if (!params)
+ return SANE_STATUS_GOOD;
+
+ if (this->cancelled)
+ {
+ DBG(1, "sanei_hp_handle_getParameters: cancelled. Stop scan\n");
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+ }
+
+ if (hp_handle_isScanning(this))
+ {
+ *params = this->scan_params;
+ return SANE_STATUS_GOOD;
+ }
+
+ status = sanei_hp_optset_guessParameters(this->dev->options,
+ this->data, params);
+#ifdef INQUIRE_AFTER_SCAN
+ /* Photosmart: this gives the correct number of lines when doing
+ an update of the SANE parameters right after a preview */
+ if (!strcmp("C5100A", this->dev->sanedev.model)) {
+ HpScsi scsi;
+ SANE_Parameters * p = &this->scan_params;
+
+ if (!FAILED( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) )) {
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_NUMBER_OF_LINES,
+ &p->lines,0,0));
+ sanei_hp_scsi_destroy(scsi,0);
+ *params = this->scan_params;
+ }
+ }
+#endif
+ return status;
+}
+
+SANE_Status
+sanei_hp_handle_startScan (HpHandle this)
+{
+ SANE_Status status;
+ HpScsi scsi;
+ HpScl scl;
+ HpProcessData *procdata = &(this->procdata);
+ int adfscan;
+
+ /* FIXME: setup preview mode stuff? */
+
+ if (hp_handle_isScanning(this))
+ {
+ DBG(3,"sanei_hp_handle_startScan: Stop current scan\n");
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+ }
+
+ RETURN_IF_FAIL( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) );
+
+ status = sanei_hp_optset_download(this->dev->options, this->data, scsi);
+
+ if (!FAILED(status))
+ status = hp_handle_uploadParameters(this, scsi,
+ &(procdata->bits_per_channel),
+ &(procdata->invert),
+ &(procdata->out8));
+
+ if (FAILED(status))
+ {
+ sanei_hp_scsi_destroy(scsi,0);
+ return status;
+ }
+
+ procdata->mirror_vertical =
+ sanei_hp_optset_mirror_vert (this->dev->options, this->data, scsi);
+ DBG(1, "start: %s to mirror image vertically\n", procdata->mirror_vertical ?
+ "Request" : "No request" );
+
+ scl = sanei_hp_optset_scan_type (this->dev->options, this->data);
+ adfscan = (scl == SCL_ADF_SCAN);
+
+ /* For ADF scan we should check if there is paper available */
+ if ( adfscan )
+ {int adfstat = 0;
+ int can_check_paper = 0;
+ int is_flatbed = 0;
+ int minval, maxval;
+
+ /* For ADF-support, we have three different types of scanners:
+ * ScanJet, ScanJet+, IIp, 3p:
+ * scroll feed, no support for inquire paper in ADF, unload document
+ * and preload document
+ * IIc, IIcx, 3c, 4c, 6100C, 4p:
+ * flatbed, no support for preload document
+ * 5100C, 5200C, 6200C, 6300C:
+ * scroll feed.
+ * For all scroll feed types, we use the usual scan window command.
+ * For flatbed types, use a sequence of special commands.
+ */
+
+ /* Check the IIp group */
+ if ( (sanei_hp_device_support_get (this->dev->sanedev.name,
+ SCL_UNLOAD, &minval, &maxval)
+ != SANE_STATUS_GOOD )
+ && (sanei_hp_device_support_get (this->dev->sanedev.name,
+ SCL_CHANGE_DOC, &minval, &maxval)
+ != SANE_STATUS_GOOD ) )
+ {
+ DBG(3, "start: Request for ADF scan without support of unload doc\n");
+ DBG(3, " and change doc. Seems to be something like a IIp.\n");
+ DBG(3, " Use standard scan window command.\n");
+
+ scl = SCL_START_SCAN;
+ can_check_paper = 0;
+ is_flatbed = 0;
+ }
+/*
+ else if ( sanei_hp_device_support_get (this->dev->sanedev.name,
+ SCL_PRELOAD_ADF, &minval, &maxval)
+ != SANE_STATUS_GOOD )
+*/
+ else if ( sanei_hp_is_flatbed_adf (scsi) )
+ {
+ DBG(3, "start: Request for ADF scan without support of preload doc.\n");
+ DBG(3, " Seems to be a flatbed ADF.\n");
+ DBG(3, " Use ADF scan window command.\n");
+
+ can_check_paper = 1;
+ is_flatbed = 1;
+ }
+ else
+ {
+ DBG(3, "start: Request for ADF scan with support of preload doc.\n");
+ DBG(3, " Seems to be a scroll feed ADF.\n");
+ DBG(3, " Use standard scan window command.\n");
+
+ scl = SCL_START_SCAN;
+ can_check_paper = 1;
+ is_flatbed = 0;
+ }
+
+ /* Check if the ADF is ready */
+ if ( sanei_hp_scl_inquire(scsi, SCL_ADF_READY, &adfstat, 0, 0)
+ != SANE_STATUS_GOOD )
+ {
+ DBG(1, "start: Error checking if ADF is ready\n");
+ sanei_hp_scsi_destroy(scsi,0);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if ( adfstat != 1 )
+ {
+ DBG(1, "start: ADF is not ready. Finished.\n");
+ sanei_hp_scsi_destroy(scsi,0);
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ /* Check paper in ADF */
+ if ( can_check_paper )
+ {
+ if ( sanei_hp_scl_inquire(scsi, SCL_ADF_BIN, &adfstat, 0, 0)
+ != SANE_STATUS_GOOD )
+ {
+ DBG(1, "start: Error checking if paper in ADF\n");
+ sanei_hp_scsi_destroy(scsi,0);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if ( adfstat != 1 )
+ {
+ DBG(1, "start: No paper in ADF bin. Finished.\n");
+ sanei_hp_scsi_destroy(scsi,0);
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ if ( is_flatbed )
+ {
+ if ( sanei_hp_scl_set(scsi, SCL_CHANGE_DOC, 0) != SANE_STATUS_GOOD )
+ {
+ DBG(1, "start: Error changing document\n");
+ sanei_hp_scsi_destroy(scsi,0);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+ }
+
+ DBG(1, "start: %s to mirror image vertically\n", procdata->mirror_vertical ?
+ "Request" : "No request" );
+
+ this->bytes_left = ( this->scan_params.bytes_per_line
+ * this->scan_params.lines );
+
+ DBG(1, "start: %d pixels per line, %d bytes per line, %d lines high\n",
+ this->scan_params.pixels_per_line, this->scan_params.bytes_per_line,
+ this->scan_params.lines);
+ procdata->bytes_per_line = (int)this->scan_params.bytes_per_line;
+ if (procdata->out8)
+ {
+ procdata->bytes_per_line *= 2;
+ DBG(1,"(scanner will send %d bytes per line, 8 bit output forced)\n",
+ procdata->bytes_per_line);
+ }
+ procdata->lines = this->scan_params.lines;
+
+ /* Wait for front-panel button push ? */
+ status = sanei_hp_optset_start_wait(this->dev->options, this->data);
+
+ if (status) /* Wait for front button push ? Start scan in reader process */
+ {
+ procdata->startscan = scl;
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ procdata->startscan = 0;
+ status = sanei_hp_scl_startScan(scsi, scl);
+ }
+
+ if (!FAILED( status ))
+ {
+ status = hp_handle_startReader(this, scsi);
+ }
+
+ /* Close SCSI-connection in forked environment */
+ if (this->child_forked)
+ sanei_hp_scsi_destroy(scsi,0);
+
+ return status;
+}
+
+
+SANE_Status
+sanei_hp_handle_read (HpHandle this, void * buf, size_t *lengthp)
+{
+ ssize_t nread;
+ SANE_Status status;
+
+ DBG(3, "sanei_hp_handle_read: trying to read %lu bytes\n",
+ (unsigned long) *lengthp);
+
+ if (!hp_handle_isScanning(this))
+ {
+ DBG(1, "sanei_hp_handle_read: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (this->cancelled)
+ {
+ DBG(1, "sanei_hp_handle_read: cancelled. Stop scan\n");
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (*lengthp == 0)
+ return SANE_STATUS_GOOD;
+
+ if (*lengthp > this->bytes_left)
+ *lengthp = this->bytes_left;
+
+ if ((nread = read(this->pipe_read_fd, buf, *lengthp)) < 0)
+ {
+ *lengthp = 0;
+ if (errno == EAGAIN)
+ return SANE_STATUS_GOOD;
+ DBG(1, "sanei_hp_handle_read: read from pipe: %s. Stop scan\n",
+ strerror(errno));
+ hp_handle_stopScan(this);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ this->bytes_left -= (*lengthp = nread);
+
+ if (nread > 0)
+ {
+ DBG(3, "sanei_hp_handle_read: read %lu bytes\n", (unsigned long) nread);
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(1, "sanei_hp_handle_read: EOF from pipe. Stop scan\n");
+ status = this->bytes_left ? SANE_STATUS_IO_ERROR : SANE_STATUS_EOF;
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+
+ /* Switch off lamp and check unload after scan */
+ if (status == SANE_STATUS_EOF)
+ {
+ const HpDeviceInfo *hpinfo;
+ HpScsi scsi;
+
+ if ( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) == SANE_STATUS_GOOD )
+ {
+ hpinfo = sanei_hp_device_info_get ( this->dev->sanedev.name );
+
+ if ( hpinfo )
+ {
+ if ( hpinfo->unload_after_scan )
+ sanei_hp_scl_set(scsi, SCL_UNLOAD, 0);
+ }
+
+ sanei_hp_scsi_destroy(scsi,0);
+ }
+ }
+ return status;
+}
+
+void
+sanei_hp_handle_cancel (HpHandle this)
+{
+ this->cancelled = 1;
+ /* The OfficeJet K series may not deliver enough data. */
+ /* Therefore the read might not return until it is interrupted. */
+ DBG(3,"sanei_hp_handle_cancel: compat flags: 0x%04x\n",
+ (int)this->dev->compat);
+ if ( (this->reader_pid)
+ && (this->dev->compat & HP_COMPAT_OJ_1150C) )
+ {
+ DBG(3,"sanei_hp_handle_cancel: send SIGTERM to child (%ld)\n",
+ (long) this->reader_pid);
+ if (this->child_forked)
+ kill(this->reader_pid, SIGTERM);
+ else
+ sanei_thread_kill(this->reader_pid);
+ }
+}
+
+SANE_Status
+sanei_hp_handle_setNonblocking (HpHandle this, hp_bool_t non_blocking)
+{
+ if (!hp_handle_isScanning(this))
+ return SANE_STATUS_INVAL;
+
+ if (this->cancelled)
+ {
+ DBG(3,"sanei_hp_handle_setNonblocking: cancelled. Stop scan\n");
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (fcntl(this->pipe_read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_handle_getPipefd (HpHandle this, SANE_Int *fd)
+{
+ if (! hp_handle_isScanning(this))
+ return SANE_STATUS_INVAL;
+
+ if (this->cancelled)
+ {
+ DBG(3,"sanei_hp_handle_getPipefd: cancelled. Stop scan\n");
+ RETURN_IF_FAIL( hp_handle_stopScan(this) );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ *fd = this->pipe_read_fd;
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/hp-handle.h b/backend/hp-handle.h
new file mode 100644
index 0000000..f1fac9a
--- /dev/null
+++ b/backend/hp-handle.h
@@ -0,0 +1,65 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+#ifndef HP_HANDLE_INCLUDED
+#define HP_HANDLE_INCLUDED
+#include "hp.h"
+
+HpHandle sanei_hp_handle_new (HpDevice dev);
+
+void sanei_hp_handle_destroy (HpHandle this);
+const SANE_Option_Descriptor * sanei_hp_handle_saneoption (HpHandle this,
+ SANE_Int optnum);
+SANE_Status sanei_hp_handle_control(HpHandle this, SANE_Int optnum,
+ SANE_Action action, void *valp, SANE_Int *info);
+SANE_Status sanei_hp_handle_getParameters (HpHandle this,
+ SANE_Parameters *params);
+SANE_Status sanei_hp_handle_startScan (HpHandle this);
+SANE_Status sanei_hp_handle_read (HpHandle this, void * buf, size_t *lengthp);
+void sanei_hp_handle_cancel (HpHandle this);
+SANE_Status sanei_hp_handle_setNonblocking (HpHandle this,
+ hp_bool_t non_blocking);
+SANE_Status sanei_hp_handle_getPipefd (HpHandle this, SANE_Int *fd);
+
+#endif /* HP_HANDLE_INCLUDED */
diff --git a/backend/hp-hpmem.c b/backend/hp-hpmem.c
new file mode 100644
index 0000000..d082bda
--- /dev/null
+++ b/backend/hp-hpmem.c
@@ -0,0 +1,151 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+/*
+#define STUBS
+extern int sanei_debug_hp;*/
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "../include/lassert.h"
+
+#include "hp.h"
+
+typedef struct hp_alloc_s Alloc;
+
+struct hp_alloc_s
+{
+ Alloc * prev;
+ Alloc * next;
+ hp_byte_t buf[1];
+};
+
+static Alloc head[] = {{ head, head, {0} }};
+
+#define DATA_OFFSET (head->buf - (hp_byte_t *)head)
+#define VOID_TO_ALLOCP(p) ((Alloc *)((hp_byte_t *)(p) - DATA_OFFSET))
+#define ALLOCSIZE(sz) (sz + DATA_OFFSET)
+
+
+void *
+sanei_hp_alloc (size_t sz)
+{
+ Alloc * new = malloc(ALLOCSIZE(sz));
+
+ if (!new)
+ return 0;
+ (new->next = head->next)->prev = new;
+ (new->prev = head)->next = new;
+ return new->buf;
+}
+
+void *
+sanei_hp_allocz (size_t sz)
+{
+ void * new = sanei_hp_alloc(sz);
+
+ if (!new)
+ return 0;
+ memset(new, 0, sz);
+ return new;
+}
+
+void *
+sanei_hp_memdup (const void * src, size_t sz)
+{
+ char * new = sanei_hp_alloc(sz);
+ if (!new)
+ return 0;
+ return memcpy(new, src, sz);
+}
+
+char *
+sanei_hp_strdup (const char * str)
+{
+ return sanei_hp_memdup(str, strlen(str) + 1);
+}
+
+void *
+sanei_hp_realloc (void * ptr, size_t sz)
+{
+ if (ptr)
+ {
+ Alloc * old = VOID_TO_ALLOCP(ptr);
+ Alloc copy = *old;
+ Alloc * new = realloc(old, ALLOCSIZE(sz));
+ if (!new)
+ return 0;
+ if (new != old)
+ (new->prev = copy.prev)->next = (new->next = copy.next)->prev = new;
+ return new->buf;
+ }
+ else
+ return sanei_hp_alloc(sz);
+}
+
+void
+sanei_hp_free (void * ptr)
+{
+ Alloc * old = VOID_TO_ALLOCP(ptr);
+
+ assert(old && old != head);
+ (old->next->prev = old->prev)->next = old->next;
+ old->next = old->prev = 0; /* so we can puke on multiple free's */
+ free(old);
+}
+
+void
+sanei_hp_free_all (void)
+{
+ Alloc * ptr;
+ Alloc * next;
+
+ for (ptr = head->next; ptr != head; ptr = next)
+ {
+ next = ptr->next;
+ free(ptr);
+ }
+ head->next = head->prev = head;
+}
diff --git a/backend/hp-option.c b/backend/hp-option.c
new file mode 100644
index 0000000..10bcb3e
--- /dev/null
+++ b/backend/hp-option.c
@@ -0,0 +1,4087 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/*
+ $Log$
+ Revision 1.13 2005/04/13 12:50:07 ellert-guest
+ Add missing SANE_I18N, Regenerate .po files accordingly, Update Swedish translations
+
+ Revision 1.12 2003/10/09 19:32:50 kig-guest
+ Bug #300241: fix invers image on 3c/4c/6100C at 10 bit depth
+
+
+*/
+
+/* pwd.h not available ? */
+#if (defined(__IBMC__) || defined(__IBMCPP__))
+#ifndef _AIX
+# define SANE_HOME_HP "SANE_HOME_HP"
+#endif
+#endif
+
+/* To be done: dont reallocate choice accessors */
+/* #define HP_ALLOC_CHOICEACC_ONCE 1 */
+/*
+#define HP_EXPERIMENTAL
+*/ /*
+#define STUBS
+extern int sanei_debug_hp; */
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/lalloca.h"
+
+#include <stdio.h>
+#include <string.h>
+#include "../include/lassert.h"
+#ifndef SANE_HOME_HP
+#include <pwd.h>
+#endif
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "hp.h"
+#include "hp-option.h"
+#include "hp-accessor.h"
+#include "hp-scsi.h"
+#include "hp-scl.h"
+#include "hp-device.h"
+
+
+/* FIXME: descriptors should be static? */
+
+typedef SANE_Option_Descriptor * _HpSaneOption;
+typedef struct hp_option_descriptor_s * _HpOptionDescriptor;
+typedef struct hp_option_s * _HpOption;
+
+typedef struct hp_data_info_s * HpDataInfo;
+typedef struct hp_data_info_s * _HpDataInfo;
+
+typedef HpAccessor HpAccessorOptd;
+
+
+static hp_bool_t hp_optset_isEnabled (HpOptSet this, HpData data,
+ const char *name, const HpDeviceInfo *info);
+static HpOption hp_optset_get (HpOptSet this, HpOptionDescriptor optd);
+static HpOption hp_optset_getByName (HpOptSet this, const char * name);
+static SANE_Status hp_download_calib_file (HpScsi scsi);
+static SANE_Status hp_probe_parameter_support_table (enum hp_device_compat_e
+ compat, HpScl scl, int value);
+
+#define HP_EOL -9999
+
+/* Dont need requiries for commands that are probed */
+#define HP_PROBE_SCL_COMMAND 1
+
+/* Scale factor for vectors (gtk seems not to like vectors/curves
+ * in y-range 0.0,...,1.0)
+ */
+#define HP_VECTOR_SCALE (256.0)
+/*
+ *
+ */
+struct hp_option_s
+{
+ HpOptionDescriptor descriptor;
+ HpAccessorOptd optd_acsr;
+ HpAccessor data_acsr;
+ void * extra;
+};
+
+struct hp_option_descriptor_s
+{
+ const char * name;
+ const char * title;
+ const char * desc;
+ SANE_Value_Type type;
+ SANE_Unit unit;
+ SANE_Int cap;
+
+ enum hp_device_compat_e requires; /* model dependent support flags */
+
+ /* probe for option support */
+ SANE_Status (*probe) (_HpOption this, HpScsi scsi, HpOptSet optset,
+ HpData data);
+ SANE_Status (*program) (HpOption this, HpScsi scsi, HpOptSet optset,
+ HpData data);
+ hp_bool_t (*enable) (HpOption this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info);
+
+ hp_bool_t has_global_effect;
+ hp_bool_t affects_scan_params;
+ hp_bool_t program_immediate;
+ hp_bool_t suppress_for_scan;
+ hp_bool_t may_change;
+
+
+ /* This stuff should really be in a subclasses: */
+ HpScl scl_command;
+ int minval, maxval, startval; /* for simulation */
+ HpChoice choices;
+};
+
+struct hp_data_info_s
+{
+ HpScl scl;
+};
+
+static const struct hp_option_descriptor_s
+ NUM_OPTIONS[1], PREVIEW_MODE[1], SCAN_MODE[1], SCAN_RESOLUTION[1],
+
+ CUSTOM_GAMMA[1], GAMMA_VECTOR_8x8[1],
+#ifdef ENABLE_7x12_TONEMAPS
+ GAMMA_VECTOR_7x12[1],
+ RGB_TONEMAP[1], GAMMA_VECTOR_R[1], GAMMA_VECTOR_G[1], GAMMA_VECTOR_B[1],
+#endif
+ HALFTONE_PATTERN[1], MEDIA[1], OUT8[1], BIT_DEPTH[1], SCAN_SOURCE[1],
+#ifdef FAKE_COLORSEP_MATRIXES
+ SEPMATRIX[1],
+#endif
+ MATRIX_TYPE[1];
+
+
+/* Check if a certain scanner model supports a command with a given parameter
+ * value. The function returns SANE_STATUS_GOOD if the command and the
+ * value is found in the support table of that scanner.
+ * It returns SANE_STATUS_UNSUPPORTED if the command is found in the support
+ * table of that scanner, but the value is not included in the table.
+ * It returns SANE_STATUS_EOF if there is no information about that command
+ * and that scanner in the support table.
+ */
+static SANE_Status
+hp_probe_parameter_support_table (enum hp_device_compat_e compat,
+ HpScl scl, int value)
+
+{int k, j;
+ char *eptr;
+ static int photosmart_output_type[] =
+ /* HP Photosmart: only b/w, gray, color is supported */
+ { HP_COMPAT_PS, SCL_OUTPUT_DATA_TYPE, 0, 4, 5, HP_EOL };
+
+ static int *support_table[] =
+ {
+ photosmart_output_type
+ };
+
+ eptr = getenv ("SANE_HP_CHK_TABLE");
+ if ((eptr != NULL) && (*eptr == '0'))
+ return SANE_STATUS_EOF;
+
+ for (k = 0; k < (int)(sizeof (support_table)/sizeof (support_table[0])); k++)
+ {
+ if ((scl == support_table[k][1]) && (support_table[k][0] & compat))
+ {
+ for (j = 2; support_table[k][j] != HP_EOL; j++)
+ if (support_table[k][j] == value) return SANE_STATUS_GOOD;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ return SANE_STATUS_EOF;
+}
+
+
+/*
+ * class HpChoice
+ */
+typedef struct hp_choice_s * _HpChoice;
+
+static hp_bool_t
+hp_choice_isSupported (HpChoice choice, int minval, int maxval)
+{
+ return ( choice->is_emulated
+ || ( choice->val >= minval && choice->val <= maxval ) );
+}
+
+static hp_bool_t
+hp_probed_choice_isSupported (HpScsi scsi, HpScl scl,
+ HpChoice choice, int minval, int maxval)
+{
+ hp_bool_t isSupported;
+ SANE_Status status;
+ enum hp_device_compat_e compat;
+
+ if ( choice->is_emulated )
+ {
+ DBG(3, "probed_choice: value %d is emulated\n", choice->val);
+ return ( 1 );
+ }
+ if ( choice->val < minval || choice->val > maxval )
+ {
+ DBG(3, "probed_choice: value %d out of range (%d,%d)\n", choice->val,
+ minval, maxval);
+ return ( 0 );
+ }
+
+ if (sanei_hp_device_probe (&compat, scsi) != SANE_STATUS_GOOD)
+ {
+ DBG(1, "probed_choice: Could not get compatibilities for scanner\n");
+ return ( 0 );
+ }
+
+ status = hp_probe_parameter_support_table (compat, scl, choice->val);
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG(3, "probed_choice: command/value found in support table\n");
+ return ( 1 );
+ }
+ else if (status == SANE_STATUS_UNSUPPORTED)
+ {
+ DBG(3, "probed_choice: command found in support table, but value n.s.\n");
+ return ( 0 );
+ }
+
+ /* Not in the support table. Try to inquire */
+ /* Fix me: It seems that the scanner does not raise a parameter error */
+ /* after specifiying an unsupported command-value. */
+
+ sanei_hp_scl_clearErrors (scsi);
+ sanei_hp_scl_set (scsi, scl, choice->val);
+
+ isSupported = ( sanei_hp_scl_errcheck (scsi) == SANE_STATUS_GOOD );
+
+ DBG(3, "probed_choice: value %d %s\n", choice->val,
+ isSupported ? "supported" : "not supported");
+ return isSupported;
+}
+
+hp_bool_t
+sanei_hp_choice_isEnabled (HpChoice this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ if (!this->enable)
+ return 1;
+ return (*this->enable)(this, optset, data, info);
+}
+
+static hp_bool_t
+_cenable_incolor (HpChoice UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR;
+}
+
+static hp_bool_t
+_cenable_notcolor (HpChoice UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ return sanei_hp_optset_scanmode(optset, data) != HP_SCANMODE_COLOR;
+}
+
+/*
+ * class HpAccessorOptd
+ */
+static HpAccessorOptd
+hp_accessor_optd_new (HpData data)
+{
+ return sanei_hp_accessor_new(data, sizeof(SANE_Option_Descriptor));
+}
+
+static _HpSaneOption
+hp_accessor_optd_data (HpAccessorOptd this, HpData data)
+{
+ return sanei__hp_accessor_data(this, data);
+}
+
+
+
+/*
+ * class OptionDescriptor
+ */
+
+static SANE_Status
+hp_option_descriptor_probe (HpOptionDescriptor desc, HpScsi scsi,
+ HpOptSet optset, HpData data, HpOption * newoptp)
+{
+ _HpOption new;
+ SANE_Status status;
+ _HpSaneOption optd;
+
+ new = sanei_hp_alloc(sizeof(*new));
+ new->descriptor = desc;
+ if (!(new->optd_acsr = hp_accessor_optd_new(data)))
+ return SANE_STATUS_NO_MEM;
+ new->data_acsr = 0;
+ optd = hp_accessor_optd_data(new->optd_acsr, data);
+
+ memset(optd, 0, sizeof(*optd));
+ optd->name = desc->name;
+ optd->title = desc->title;
+ optd->desc = desc->desc;
+ optd->type = desc->type;
+ optd->unit = desc->unit;
+ optd->cap = desc->cap;
+
+ /*
+ * Probe function will set optd->size, optd->constraint_type,
+ * and optd->constraint.
+ * and also new->accessor
+ * and possibly new->extra
+ */
+ if (desc->probe)
+ {
+ if (FAILED( status = (*desc->probe)(new, scsi, optset, data) ))
+ {
+ /* hp_accessor_optd_destoy(new->optd_acsr) */
+ sanei_hp_free(new);
+ return status;
+ }
+ }
+
+ *newoptp = new;
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ * class Option
+ */
+static HpSaneOption
+hp_option_saneoption (HpOption this, HpData data)
+{
+ return hp_accessor_optd_data(this->optd_acsr, data);
+}
+
+static _HpSaneOption
+_hp_option_saneoption (HpOption this, HpData data)
+{
+ return hp_accessor_optd_data(this->optd_acsr, data);
+}
+
+static SANE_Status
+hp_option_download (HpOption this, HpData data, HpOptSet optset, HpScsi scsi)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int value;
+
+ if (IS_SCL_CONTROL(scl))
+ {
+ value = sanei_hp_accessor_getint(this->data_acsr, data);
+ if ( (scl == SCL_DATA_WIDTH)
+ && (sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR) )
+ {
+ value *= 3;
+ }
+ return sanei_hp_scl_set(scsi, scl, value);
+ }
+ else if (IS_SCL_DATA_TYPE(scl))
+ return sanei_hp_scl_download(scsi, scl,
+ sanei_hp_accessor_data(this->data_acsr, data),
+ sanei_hp_accessor_size(this->data_acsr));
+ assert(!scl);
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+hp_option_upload (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int val;
+
+ if (IS_SCL_CONTROL(scl))
+ {
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) );
+ if ( (scl == SCL_DATA_WIDTH)
+ && (sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR) )
+ {
+ val /= 3;
+ }
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ return SANE_STATUS_GOOD;
+ }
+ else if (IS_SCL_DATA_TYPE(scl))
+ return sanei_hp_scl_upload(scsi, scl,
+ sanei__hp_accessor_data(this->data_acsr, data),
+ sanei_hp_accessor_size(this->data_acsr));
+ assert(!scl);
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+hp_option_program (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ const HpDeviceInfo *info;
+
+ DBG(10, "hp_option_program: name=%s, enable=0x%08lx, program=0x%08lx\n",
+ this->descriptor->name, (long)this->descriptor->enable,
+ (long)this->descriptor->program);
+
+ /* Replaced by flag suppress_for_scan
+ * if (this->descriptor->program_immediate)
+ * {
+ * DBG(10, "hp_option_program: is program_immediate. Dont program now.\n");
+ * return SANE_STATUS_GOOD;
+ * }
+ */
+
+ if (!this->descriptor->program)
+ return SANE_STATUS_GOOD;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ if (this->descriptor->enable
+ && !(*this->descriptor->enable)(this, optset, data, info))
+ return SANE_STATUS_GOOD;
+
+ return (*this->descriptor->program)(this, scsi, optset, data);
+}
+
+static SANE_Status
+hp_option_get (HpOption this, HpData data, void * valp)
+{
+ if (!this->data_acsr)
+ return SANE_STATUS_INVAL;
+ return sanei_hp_accessor_get(this->data_acsr, data, valp);
+}
+
+static hp_bool_t
+_values_are_equal (HpOption this, HpData data,
+ const void * val1, const void * val2)
+{
+ HpSaneOption optd = hp_option_saneoption(this, data);
+
+ if (optd->type == SANE_TYPE_STRING)
+ return strncmp((const char *)val1, (const char *)val2, optd->size) == 0;
+ else
+ return memcmp(val1, val2, optd->size) == 0;
+}
+
+static hp_bool_t
+hp_option_isImmediate (HpOption this)
+{
+ return ( this->descriptor->program_immediate
+ && this->descriptor->program );
+}
+
+static SANE_Status
+hp_option_imm_set (HpOptSet optset, HpOption this, HpData data,
+ void * valp, SANE_Int * info, HpScsi scsi)
+{
+ HpSaneOption optd = hp_option_saneoption(this, data);
+ hp_byte_t * old_val = alloca(optd->size);
+ SANE_Status status;
+
+ assert (this->descriptor->program_immediate && this->descriptor->program);
+
+ if (!SANE_OPTION_IS_SETTABLE(optd->cap))
+ return SANE_STATUS_INVAL;
+
+ DBG(10,"hp_option_imm_set: %s\n", this->descriptor->name);
+
+ if ( this->descriptor->type == SANE_TYPE_BUTTON )
+ {
+ status = (*this->descriptor->program)(this, scsi, optset, data);
+ if ( !FAILED(status) && info )
+ {
+ if (this->descriptor->has_global_effect)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (this->descriptor->affects_scan_params)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ return status;
+ }
+
+ if ( !this->data_acsr )
+ return SANE_STATUS_INVAL;
+
+ if (!old_val)
+ return SANE_STATUS_NO_MEM;
+
+ if (FAILED( status = sanei_constrain_value(optd, valp, info) ))
+ {
+ DBG(1, "option_imm_set: %s: constrain_value failed :%s\n",
+ this->descriptor->name, sane_strstatus(status));
+ return status;
+ }
+
+ RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, old_val) );
+
+ if (_values_are_equal(this, data, old_val, valp))
+ {
+ DBG(3, "option_imm_set: value unchanged\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ if (info)
+ memcpy(old_val, valp, optd->size); /* Save requested value */
+
+ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, valp) );
+
+ if ( this->descriptor->type == SANE_TYPE_STRING )
+ RETURN_IF_FAIL( (*this->descriptor->program)(this, scsi, optset, data) );
+
+ if (info)
+ {
+ if (!_values_are_equal(this, data, old_val, valp))
+ *info |= SANE_INFO_INEXACT;
+ if (this->descriptor->has_global_effect)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (this->descriptor->affects_scan_params)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_option_set (HpOption this, HpData data, void * valp, SANE_Int * info)
+{
+ HpSaneOption optd = hp_option_saneoption(this, data);
+ hp_byte_t * old_val = alloca(optd->size);
+ SANE_Status status;
+ char sval[64];
+
+
+ if (!SANE_OPTION_IS_SETTABLE(optd->cap) || !this->data_acsr)
+ return SANE_STATUS_INVAL;
+ if (!old_val)
+ return SANE_STATUS_NO_MEM;
+
+ sval[0] = '\0';
+ if (this->descriptor->type == SANE_TYPE_INT)
+ sprintf (sval," value=%d", *(int*)valp);
+
+ DBG(10,"hp_option_set: %s%s\n", this->descriptor->name, sval);
+
+ if (FAILED( status = sanei_constrain_value(optd, valp, info) ))
+ {
+ DBG(1, "option_set: %s: constrain_value failed :%s\n",
+ this->descriptor->name, sane_strstatus(status));
+ return status;
+ }
+
+ RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, old_val) );
+
+ if (_values_are_equal(this, data, old_val, valp))
+ {
+ DBG(3, "option_set: %s: value unchanged\n",this->descriptor->name);
+ return SANE_STATUS_GOOD;
+ }
+
+ if (info)
+ memcpy(old_val, valp, optd->size); /* Save requested value */
+
+ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, valp) );
+
+ if (info)
+ {
+ if (!_values_are_equal(this, data, old_val, valp))
+ *info |= SANE_INFO_INEXACT;
+ if (this->descriptor->has_global_effect)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (this->descriptor->affects_scan_params)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ DBG(3, "option_set: %s: info=0x%lx\n",this->descriptor->name,
+ (long)*info);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static int
+hp_option_getint (HpOption this, HpData data)
+{
+ return sanei_hp_accessor_getint(this->data_acsr, data);
+}
+
+static SANE_Status
+hp_option_imm_control (HpOptSet optset, HpOption this, HpData data,
+ SANE_Action action, void * valp, SANE_Int *infop,
+ HpScsi scsi)
+{
+ HpSaneOption optd = hp_option_saneoption(this, data);
+
+ if (!SANE_OPTION_IS_ACTIVE(optd->cap))
+ return SANE_STATUS_INVAL;
+
+ switch (action) {
+ case SANE_ACTION_GET_VALUE:
+ return hp_option_get(this, data, valp);
+ case SANE_ACTION_SET_VALUE:
+ return hp_option_imm_set(optset, this, data, valp, infop, scsi);
+ case SANE_ACTION_SET_AUTO:
+ default:
+ return SANE_STATUS_INVAL;
+ }
+}
+
+static SANE_Status
+hp_option_control (HpOption this, HpData data,
+ SANE_Action action, void * valp, SANE_Int *infop)
+{
+ HpSaneOption optd = hp_option_saneoption(this, data);
+
+ if (!SANE_OPTION_IS_ACTIVE(optd->cap))
+ return SANE_STATUS_INVAL;
+
+ switch (action) {
+ case SANE_ACTION_GET_VALUE:
+ return hp_option_get(this, data, valp);
+ case SANE_ACTION_SET_VALUE:
+ return hp_option_set(this, data, valp, infop);
+ case SANE_ACTION_SET_AUTO:
+ default:
+ return SANE_STATUS_INVAL;
+ }
+}
+
+
+static void
+hp_option_reprogram (HpOption this, HpOptSet optset, HpData data, HpScsi scsi)
+{
+ if (this->descriptor->may_change)
+ {
+ DBG(5, "hp_option_reprogram: %s\n", this->descriptor->name);
+
+ hp_option_program (this, scsi, optset, data);
+ }
+}
+
+
+static void
+hp_option_reprobe (HpOption this, HpOptSet optset, HpData data, HpScsi scsi)
+{
+ if (this->descriptor->may_change)
+ {
+ DBG(5, "hp_option_reprobe: %s\n", this->descriptor->name);
+
+ (*this->descriptor->probe)((_HpOption)this, scsi, optset, data);
+ }
+}
+
+static void
+hp_option_updateEnable (HpOption this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ hp_bool_t (*f)(HpOption, HpOptSet, HpData, const HpDeviceInfo *)
+ = this->descriptor->enable;
+ _HpSaneOption optd = _hp_option_saneoption(this, data);
+
+ if (!f || (*f)(this, optset, data, info))
+ optd->cap &= ~SANE_CAP_INACTIVE;
+ else
+ optd->cap |= SANE_CAP_INACTIVE;
+}
+
+static hp_bool_t
+hp_option_isInternal (HpOption this)
+{
+ return this->descriptor->name[0] == '_';
+}
+
+
+/*
+ * Option probe functions
+ */
+
+static SANE_Status
+_set_range (HpOption opt, HpData data,
+ SANE_Word min, SANE_Word quant, SANE_Word max)
+{
+ _HpSaneOption optd = _hp_option_saneoption(opt, data);
+ SANE_Range * range = sanei_hp_alloc(sizeof(*range)); /* FIXME: leak? */
+
+ if (! range)
+ return SANE_STATUS_NO_MEM;
+
+ range->min = min;
+ range->max = max;
+ range->quant = quant;
+ optd->constraint.range = range;
+ optd->constraint_type = SANE_CONSTRAINT_RANGE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+_set_size (HpOption opt, HpData data, SANE_Int size)
+{
+ _hp_option_saneoption(opt, data)->size = size;
+}
+
+/* #ifdef HP_EXPERIMENTAL */
+static SANE_Status
+_probe_int (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset, HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int minval, maxval;
+ int val = 0;
+
+ assert(scl);
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) );
+
+ if (minval >= maxval)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_int_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ _set_size(this, data, sizeof(SANE_Int));
+ return _set_range(this, data, minval, 1, maxval);
+}
+/* #endif */
+
+static SANE_Status
+_probe_int_brightness (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int minval, maxval;
+ int val = 0;
+ hp_bool_t simulate;
+
+ assert(scl);
+
+ simulate = ( sanei_hp_device_support_get (
+ sanei_hp_scsi_devicename (scsi), scl, 0, 0)
+ != SANE_STATUS_GOOD );
+
+ if ( simulate )
+ {
+ val = this->descriptor->startval;
+ minval = this->descriptor->minval;
+ maxval = this->descriptor->maxval;
+ }
+ else
+ {
+ RETURN_IF_FAIL ( sanei_hp_scl_inquire(scsi,scl,&val,&minval,&maxval) );
+ }
+
+ if (minval >= maxval)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_int_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ _set_size(this, data, sizeof(SANE_Int));
+ return _set_range(this, data, minval, 1, maxval);
+}
+
+static SANE_Status
+_probe_resolution (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ int minval, maxval, min2, max2;
+ int val = 0, val2;
+ int quant = 1;
+ enum hp_device_compat_e compat;
+
+ /* Check for supported resolutions in both directions */
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_X_RESOLUTION, &val,
+ &minval, &maxval) );
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_Y_RESOLUTION, &val2, &min2, &max2));
+ if ( min2 > minval ) minval = min2;
+ if ( max2 < maxval ) maxval = max2;
+
+ if (minval >= maxval)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_int_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ _set_size(this, data, sizeof(SANE_Int));
+
+ /* The HP OfficeJet Pro 1150C crashes the scan head when scanning at
+ * resolutions less than 42 dpi. Set a safe minimum resolution.
+ * Hopefully 50 dpi is safe enough. */
+ if ((sanei_hp_device_probe(&compat,scsi)==SANE_STATUS_GOOD) &&
+ ((compat&(HP_COMPAT_OJ_1150C|HP_COMPAT_OJ_1170C))==HP_COMPAT_OJ_1150C)) {
+ if (minval<50) minval=50;
+ }
+
+ /* HP Photosmart scanner does not allow scanning at arbitrary resolutions */
+ /* for slides/negatives. Must be multiple of 300 dpi. Set quantization. */
+
+ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_PS) )
+ {int val, mi, ma;
+
+ if ( (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &val, &mi, &ma)
+ == SANE_STATUS_GOOD)
+ && ((val == HP_MEDIA_SLIDE) || (val == HP_MEDIA_NEGATIVE)) )
+ quant = 300;
+ minval = (minval+quant-1)/quant;
+ minval *= quant;
+ maxval = (maxval+quant-1)/quant;
+ maxval *= quant;
+ }
+ DBG(5, "_probe_resolution: set range %d..%d, quant=%d\n",minval,maxval,quant);
+
+ return _set_range(this, data, minval, quant, maxval);
+}
+
+static SANE_Status
+_probe_bool (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int val = 0;
+
+ if (scl)
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) );
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_bool_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ _set_size(this, data, sizeof(SANE_Bool));
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+_probe_change_doc (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+
+{SANE_Status status;
+ int cap = 0;
+
+ DBG(2, "probe_change_doc: inquire ADF capability\n");
+
+ status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &cap, 0, 0);
+ if ( (status != SANE_STATUS_GOOD) || (cap == 0))
+ return SANE_STATUS_UNSUPPORTED;
+
+ DBG(2, "probe_change_doc: check if change document is supported\n");
+
+ status = sanei_hp_scl_inquire(scsi, SCL_CHANGE_DOC, &cap, 0, 0);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_bool_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, cap);
+ _set_size(this, data, sizeof(SANE_Bool));
+
+ return SANE_STATUS_GOOD;
+}
+
+/* The OfficeJets support SCL_UNLOAD even when no ADF is installed, so
+ * this function was added to check for SCL_ADF_CAPABILITY, similar to
+ * _probe_change_doc(), to hide the unnecessary "Unload" button on
+ * non-ADF OfficeJets. */
+static SANE_Status
+_probe_unload (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+
+{SANE_Status status;
+ int cap = 0;
+
+ DBG(2, "probe_unload: inquire ADF capability\n");
+
+ status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &cap, 0, 0);
+ if ( (status != SANE_STATUS_GOOD) || (cap == 0))
+ return SANE_STATUS_UNSUPPORTED;
+
+ DBG(2, "probe_unload: check if unload is supported\n");
+
+ status = sanei_hp_scl_inquire(scsi, SCL_UNLOAD, &cap, 0, 0);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_bool_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, cap);
+ _set_size(this, data, sizeof(SANE_Bool));
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_probe_calibrate (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ int val = 0; /* Always false */
+ int minval, maxval;
+ int media;
+ int download_calib_file = 1;
+ enum hp_device_compat_e compat;
+
+ /* The OfficeJets don't seem to support calibration, so we'll
+ * remove it from the option list to reduce frontend clutter. */
+ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) &&
+ (compat & HP_COMPAT_OJ_1150C)) {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* If we have a Photosmart scanner, we only download the calibration file */
+ /* when medium is set to prints */
+ media = -1;
+ if (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &val, &minval, &maxval)
+ == SANE_STATUS_GOOD)
+ media = val; /* 3: prints, 2: slides, 1: negatives */
+
+ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_PS)
+ && (media != HP_MEDIA_PRINT))
+ download_calib_file = 0;
+
+ /* Recalibrate can not be probed, because it has no inquire ID. */
+ /* And the desired ID of 10963 does not work. So we have to trust */
+ /* the evaluated HP model number. */
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_bool_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ _set_size(this, data, sizeof(SANE_Bool));
+
+ /* Try to download calibration map */
+ if (download_calib_file)
+ hp_download_calib_file ( scsi );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static HpChoice
+_make_choice_list (HpChoice choice, int minval, int maxval)
+{
+ static struct hp_choice_s bad = { 0, 0, 0, 0, 0 }; /* FIXME: hack */
+
+ /* FIXME: Another memory leak */
+
+ if (!choice->name)
+ return 0;
+ else if (hp_choice_isSupported(choice, minval, maxval))
+ {
+ _HpChoice new = sanei_hp_memdup(choice, sizeof(*new));
+ if (!new)
+ return &bad;
+ new->next = _make_choice_list(choice + 1, minval, maxval);
+ return new;
+ }
+ else
+ return _make_choice_list(choice + 1, minval, maxval);
+}
+
+static HpChoice
+_make_probed_choice_list (HpScsi scsi, HpScl scl, HpChoice choice,
+ int minval, int maxval)
+{
+ static struct hp_choice_s bad = { 0, 0, 0, 0, 0 }; /* FIXME: hack */
+
+ /* FIXME: Another memory leak */
+
+ if (!choice->name)
+ return 0;
+ else if (hp_probed_choice_isSupported(scsi, scl, choice, minval, maxval))
+ {
+ _HpChoice new = sanei_hp_memdup(choice, sizeof(*new));
+ if (!new)
+ return &bad;
+ new->next = _make_probed_choice_list(scsi, scl, choice + 1, minval, maxval);
+ return new;
+ }
+ else
+ return _make_probed_choice_list(scsi, scl, choice + 1, minval, maxval);
+}
+
+static void
+_set_stringlist (HpOption this, HpData data, SANE_String_Const * strlist)
+{
+ _HpSaneOption optd = _hp_option_saneoption(this, data);
+ optd->constraint.string_list = strlist;
+ optd->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+}
+
+static SANE_Status
+_probe_choice (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int minval, maxval, val;
+ HpChoice choices;
+ const HpDeviceInfo *info;
+ enum hp_device_compat_e compat;
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) );
+ DBG(3, "choice_option_probe: '%s': val, min, max = %d, %d, %d\n",
+ this->descriptor->name, val, minval, maxval);
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+
+ /* Datawidth needs a special handling. The choicelist consists of */
+ /* values of bits per sample. But the minval/maxval uses bits per pixel */
+ if ( scl == SCL_DATA_WIDTH )
+ {
+ enum hp_scanmode_e scanmode = sanei_hp_optset_scanmode (optset, data);
+
+ /* The data width inquiries seem not to work properly on PhotoSmart */
+ /* Sometimes they report just 24 bits, but support 30 bits too. */
+ /* Sometimes they report min/max to be 24/8. Assume they all support */
+ /* at least 10 bits per channel for RGB. Grayscale is only supported */
+ /* with 8 bits. */
+ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_PS))
+ {
+ if (scanmode == HP_SCANMODE_GRAYSCALE)
+ {
+ minval = 8; if (maxval < 8) maxval = 8;
+ }
+ else if (scanmode == HP_SCANMODE_COLOR)
+ {
+ minval = 24; if (maxval < 30) maxval = 30;
+ }
+ DBG(1, "choice_option_probe: set max. datawidth to %d for photosmart\n",
+ maxval);
+ }
+
+ if ( scanmode == HP_SCANMODE_COLOR )
+ {
+ minval /= 3; if ( minval <= 0) minval = 1;
+ maxval /= 3; if ( maxval <= 0) maxval = 1;
+ val /= 3; if (val <= 0) val = 1;
+ }
+
+#if 0
+ /* The OfficeJets claim to support >8 bits per color, but it may not
+ * work on some models. This code (if not commented out) disables it. */
+ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) &&
+ (compat & HP_COMPAT_OJ_1150C)) {
+ if (maxval>8) maxval=8;
+ }
+#endif
+ }
+
+ choices = _make_choice_list(this->descriptor->choices, minval, maxval);
+ if (choices && !choices->name) /* FIXME: hack */
+ return SANE_STATUS_NO_MEM;
+ if (!choices)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If no accessor, create one here. */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!(this->data_acsr))
+#endif
+ this->data_acsr = sanei_hp_accessor_choice_new(data, choices,
+ this->descriptor->may_change);
+
+ if (!(this->data_acsr))
+ return SANE_STATUS_NO_MEM;
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+
+ _set_stringlist(this, data,
+ sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr,
+ 0, 0, info));
+ _set_size(this, data,
+ sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_probe_each_choice (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int minval, maxval, val;
+ HpChoice choices;
+ const HpDeviceInfo *info;
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) );
+ DBG(3, "choice_option_probe_each: '%s': val, min, max = %d, %d, %d\n",
+ this->descriptor->name, val, minval, maxval);
+ DBG(3, "choice_option_probe_each: test all values for '%s' separately\n",
+ this->descriptor->name);
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ choices = _make_probed_choice_list(scsi, scl, this->descriptor->choices,
+ minval, maxval);
+
+ DBG(3, "choice_option_probe_each: restore previous value %d for '%s'\n",
+ val, this->descriptor->name);
+ /* Restore current value */
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, scl, val) );
+
+ if (choices && !choices->name) /* FIXME: hack */
+ return SANE_STATUS_NO_MEM;
+ if (!choices)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices,
+ this->descriptor->may_change )))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+
+ _set_stringlist(this, data,
+ sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr,
+ 0, 0, info));
+ _set_size(this, data,
+ sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr));
+ return SANE_STATUS_GOOD;
+}
+
+/* pseudo probe for exposure times in Photosmart */
+static SANE_Status
+_probe_ps_exposure_time (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ int minval = 0, maxval = 9, val = 0;
+ HpChoice choices;
+ const HpDeviceInfo *info;
+
+ choices = _make_choice_list(this->descriptor->choices, minval, maxval);
+ if (choices && !choices->name) /* FIXME: hack */
+ return SANE_STATUS_NO_MEM;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices,
+ this->descriptor->may_change )))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+
+ _set_stringlist(this, data,
+ sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr,
+ 0, 0, info));
+ _set_size(this, data,
+ sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr));
+ return SANE_STATUS_GOOD;
+}
+
+/* probe scan type (normal, adf, xpa) */
+static SANE_Status
+_probe_scan_type (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ int val;
+ int numchoices = 0;
+ HpChoice choices;
+ SANE_Status status;
+ const HpDeviceInfo *info;
+ struct hp_choice_s scan_types[4];
+ struct hp_choice_s nch = { 0, 0, 0, 0, 0 };
+ enum hp_device_compat_e compat;
+
+ /* We always have normal scan mode */
+ scan_types[numchoices++] = this->descriptor->choices[0];
+
+ if ( sanei_hp_device_probe (&compat, scsi) != SANE_STATUS_GOOD )
+ compat = 0;
+
+ /* Inquire ADF Capability. PhotoSmart scanner reports ADF capability, */
+ /* but it makes no sense. */
+ if ((compat & HP_COMPAT_PS) == 0)
+ {
+ status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &val, 0, 0);
+ if ( (status == SANE_STATUS_GOOD) && (val == 1) )
+ {
+ scan_types[numchoices++] = this->descriptor->choices[1];
+ }
+ }
+
+ /* Inquire XPA capability is supported only by IIcx and 6100c/4c/3c. */
+ /* But more devices support XPA scan window. So dont inquire XPA cap. */
+ if ( compat & ( HP_COMPAT_2CX | HP_COMPAT_4C | HP_COMPAT_4P
+ | HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C) &&
+ !(compat&HP_COMPAT_OJ_1150C) )
+ {
+ scan_types[numchoices++] = this->descriptor->choices[2];
+ }
+
+ /* Only normal scan type available ? No need to display choice */
+ if (numchoices <= 1) return SANE_STATUS_UNSUPPORTED;
+
+ scan_types[numchoices] = nch;
+ val = 0;
+
+ choices = _make_choice_list(scan_types, 0, numchoices);
+ if (choices && !choices->name) /* FIXME: hack */
+ return SANE_STATUS_NO_MEM;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices,
+ this->descriptor->may_change )))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+
+ _set_stringlist(this, data,
+ sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr,
+ 0, 0, info));
+ _set_size(this, data,
+ sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_probe_mirror_horiz (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int minval, maxval, val, sec_dir;
+ HpChoice choices;
+ const HpDeviceInfo *info;
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) );
+ DBG(3, "probe_mirror_horiz: '%s': val, min, max = %d, %d, %d\n",
+ this->descriptor->name, val, minval, maxval);
+
+ /* Look if the device supports the (?) inquire secondary scan-direction */
+ if ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0)
+ == SANE_STATUS_GOOD )
+ minval = HP_MIRROR_HORIZ_CONDITIONAL;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ choices = _make_choice_list(this->descriptor->choices, minval, maxval);
+ if (choices && !choices->name) /* FIXME: hack */
+ return SANE_STATUS_NO_MEM;
+ if (!choices)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices,
+ this->descriptor->may_change )))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+
+ _set_stringlist(this, data,
+ sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr,
+ 0, 0, info));
+ _set_size(this, data,
+ sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_probe_mirror_vert (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ int minval = HP_MIRROR_VERT_OFF,
+ maxval = HP_MIRROR_VERT_ON,
+ val = HP_MIRROR_VERT_OFF;
+ int sec_dir;
+ HpChoice choices;
+ const HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+
+ /* Look if the device supports the (?) inquire secondary scan-direction */
+ if ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0)
+ == SANE_STATUS_GOOD )
+ maxval = HP_MIRROR_VERT_CONDITIONAL;
+
+ choices = _make_choice_list(this->descriptor->choices, minval, maxval);
+ if (choices && !choices->name) /* FIXME: hack */
+ return SANE_STATUS_NO_MEM;
+ if (!choices)
+ return SANE_STATUS_UNSUPPORTED;
+
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices,
+ this->descriptor->may_change )))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+
+ _set_stringlist(this, data,
+ sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr,
+ 0, 0, info));
+ _set_size(this, data,
+ sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr));
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status _probe_front_button(_HpOption this, HpScsi scsi,
+ HpOptSet UNUSEDARG optset, HpData data)
+{
+ int val = 0;
+
+ if ( sanei_hp_scl_inquire(scsi, SCL_FRONT_BUTTON, &val, 0, 0)
+ != SANE_STATUS_GOOD )
+ return SANE_STATUS_UNSUPPORTED;
+
+ _set_size(this, data, sizeof(SANE_Bool));
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if ( !(this->data_acsr = sanei_hp_accessor_bool_new(data)) )
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, 0);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+_probe_geometry (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ hp_bool_t is_tl = 0;
+ hp_bool_t active_xpa = sanei_hp_is_active_xpa ( scsi );
+ int minval, maxval;
+ SANE_Fixed fval;
+
+ /* There might have been a reason for inquiring the extent */
+ /* by using the maxval of the position. But this does not work */
+ /* when scanning from ADF. The Y-pos is then inquired with -1..0. */
+ /* First try to get the values with SCL_X/Y_POS. If this is not ok, */
+ /* use SCL_X/Y_EXTENT */
+ if (scl == SCL_X_EXTENT)
+ {
+ scl = SCL_X_POS;
+ }
+ else if (scl == SCL_Y_EXTENT)
+ {
+ scl = SCL_Y_POS;
+ }
+ else
+ is_tl = 1;
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, 0, &minval, &maxval) );
+ if (minval >= maxval)
+ return SANE_STATUS_INVAL;
+
+ /* Bad maximum value for extent-inquiry ? */
+ if ( (!is_tl) && (maxval <= 0) )
+ {
+ scl = (scl == SCL_X_POS) ? SCL_X_EXTENT : SCL_Y_EXTENT;
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, 0, &minval, &maxval) );
+ if (minval >= maxval)
+ return SANE_STATUS_INVAL;
+ }
+
+ if ((scl == SCL_X_EXTENT) || (scl == SCL_Y_EXTENT))
+ {
+ /* Max. extent is larger than max. position. Reduce extent */
+ maxval--;
+ DBG(3, "probe_geometry: Inquiry by extent. Reduced maxval to %lu\n",
+ (unsigned long)maxval);
+ }
+
+ /* Need a new accessor ? */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_fixed_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* The active xpa is only 5x5 inches */
+ if ( (!is_tl) && active_xpa
+ && (sanei_hp_optset_scan_type (optset, data) == SCL_XPA_SCAN) )
+ {
+ DBG(3,"Set maxval to 1500 because of active XPA\n");
+ maxval = 1500;
+ }
+
+ fval = is_tl ? SANE_FIX(0.0) : maxval * SANE_FIX(MM_PER_DEVPIX);
+ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, &fval) );
+
+ _set_size(this, data, sizeof(SANE_Fixed));
+ return _set_range(this, data,
+ minval * SANE_FIX(MM_PER_DEVPIX),
+ 1,
+ maxval * SANE_FIX(MM_PER_DEVPIX));
+}
+
+static SANE_Status
+_probe_download_type (HpScl scl, HpScsi scsi)
+{
+ SANE_Status status;
+
+ sanei_hp_scl_clearErrors (scsi);
+ sanei_hp_scl_set (scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl));
+
+ status = sanei_hp_scl_errcheck (scsi);
+
+ DBG(3, "probe_download_type: Download type %d %ssupported\n", SCL_INQ_ID(scl),
+ (status == SANE_STATUS_GOOD) ? "" : "not ");
+
+ return status;
+}
+
+static SANE_Status
+_probe_custom_gamma (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ HpScl scl_tonemap = SCL_8x8TONE_MAP;
+ SANE_Status status;
+ hp_bool_t simulate;
+ int val = 0, minval, maxval;
+ int id = SCL_INQ_ID(scl_tonemap);
+
+ /* Check if download type supported */
+ status = sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi),
+ SCL_DOWNLOAD_TYPE, &minval, &maxval);
+
+ simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval);
+
+ if (simulate)
+ {
+ DBG(3, "probe_custom_gamma: Download type 2 not supported. Simulate\n");
+ }
+ else
+ {
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) );
+ }
+
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_bool_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, val);
+ _set_size(this, data, sizeof(SANE_Bool));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_probe_vector (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ static struct vector_type_s {
+ HpScl scl;
+ unsigned length, depth;
+ HpAccessor (*creator)(HpData data, unsigned length, unsigned depth);
+ } types[] = {
+ { SCL_8x8TONE_MAP, 256, 8, sanei_hp_accessor_gamma_vector_new },
+#ifdef ENABLE_7x12_TONEMAPS
+ { SCL_BW7x12TONE_MAP, 129, 12, sanei_hp_accessor_gamma_vector_new },
+ { SCL_7x12TONE_MAP, 3 * 129, 12, sanei_hp_accessor_gamma_vector_new },
+#endif
+#ifdef ENABLE_16x16_DITHERS
+ { SCL_BW16x16DITHER, 256, 8, sanei_hp_accessor_vector_new },
+#endif
+ { SCL_BW8x8DITHER, 64, 8, sanei_hp_accessor_vector_new },
+
+ { SCL_8x9MATRIX_COEFF, 9, 8, sanei_hp_accessor_matrix_vector_new },
+#ifdef ENABLE_10BIT_MATRIXES
+ { SCL_10x9MATRIX_COEFF, 9, 10, sanei_hp_accessor_matrix_vector_new },
+ { SCL_10x3MATRIX_COEFF, 3, 10, sanei_hp_accessor_matrix_vector_new },
+#endif
+ { 0, 0, 0, 0 }
+ };
+ static struct subvector_type_s {
+ HpOptionDescriptor desc;
+ unsigned nchan, chan;
+ HpOptionDescriptor super;
+ } subvec_types[] = {
+#ifdef ENABLE_7x12_TONEMAPS
+ { GAMMA_VECTOR_R, 3, 0, RGB_TONEMAP },
+ { GAMMA_VECTOR_G, 3, 1, RGB_TONEMAP },
+ { GAMMA_VECTOR_B, 3, 2, RGB_TONEMAP },
+#endif
+ { 0, 0, 0, 0 },
+ };
+
+ HpScl scl = this->descriptor->scl_command;
+ HpAccessorVector vec;
+
+ if (scl)
+ {
+ struct vector_type_s *type;
+ for (type = types; type->scl; type++)
+ if (type->scl == scl)
+ break;
+ assert(type->scl);
+
+ RETURN_IF_FAIL ( _probe_download_type (scl, scsi) );
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ this->data_acsr = (*type->creator)(data, type->length, type->depth);
+ }
+ }
+ else
+ {
+ struct subvector_type_s *type;
+ HpOption super;
+
+ for (type = subvec_types; type->desc; type++)
+ if (type->desc == this->descriptor)
+ break;
+ assert(type->desc);
+
+ super = hp_optset_get(optset, type->super);
+ assert(super);
+
+ /* If we dont have an accessor, get one */
+#ifdef HP_ALLOC_CHOICEACC_ONCE
+ if (!this->data_acsr)
+#endif
+ {
+ this->data_acsr = sanei_hp_accessor_subvector_new(
+ (HpAccessorVector) super->data_acsr, type->nchan, type->chan);
+ }
+ }
+
+ if (!this->data_acsr)
+ return SANE_STATUS_NO_MEM;
+
+ vec = (HpAccessorVector)this->data_acsr;
+
+ _set_size(this, data, sizeof(SANE_Fixed)
+ * sanei_hp_accessor_vector_length(vec));
+
+ return _set_range(this, data,
+ sanei_hp_accessor_vector_minval(vec),
+ 1,
+ sanei_hp_accessor_vector_maxval(vec));
+}
+
+static SANE_Status
+_probe_gamma_vector (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ SANE_Fixed * buf;
+ int i;
+ size_t size, length;
+
+ RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) );
+
+ /* Initialize to linear map */
+ size = hp_option_saneoption(this, data)->size;
+ if (!(buf = alloca(size)))
+ return SANE_STATUS_NO_MEM;
+ length = size / sizeof(SANE_Fixed);
+ for (i = 0; i < (int)length; i++)
+ buf[i] = (SANE_FIX(HP_VECTOR_SCALE* 1.0) * i + (length-1) / 2) / length;
+ return sanei_hp_accessor_set(this->data_acsr, data, buf);
+}
+
+
+static SANE_Status
+_probe_horiz_dither (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ int dim = 8;
+ size_t size;
+ int i, j;
+ SANE_Fixed * buf;
+
+ if (this->descriptor->scl_command == SCL_BW16x16DITHER)
+ dim = 16;
+
+ RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) );
+
+ /* Select vertical dither pattern, and upload it */
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_BW_DITHER, HP_DITHER_VERTICAL) );
+ RETURN_IF_FAIL( hp_option_upload(this, scsi, optset, data) );
+
+ /* Flip it to get a horizontal dither pattern */
+ size = hp_option_saneoption(this, data)->size;
+ assert(size == dim * dim * sizeof(SANE_Fixed));
+ if (!(buf = alloca(size)))
+ return SANE_STATUS_NO_MEM;
+
+#define SWAP_FIXED(x,y) do { SANE_Fixed tmp = x; x = y; y = tmp; } while(0)
+ RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, buf) );
+ for (i = 0; i < dim; i++) for (j = i + 1; j < dim; j++)
+ SWAP_FIXED(buf[i * dim + j], buf[j * dim + i]);
+ return sanei_hp_accessor_set(this->data_acsr, data, buf);
+}
+
+static SANE_Status
+_probe_matrix (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) );
+
+ /* Initial value: select RGB matrix, and upload it. */
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_MATRIX, HP_MATRIX_RGB) );
+ return hp_option_upload(this, scsi, optset, data);
+}
+
+static SANE_Status
+_probe_num_options (_HpOption this, HpScsi UNUSEDARG scsi,
+ HpOptSet UNUSEDARG optset, HpData data)
+{
+ /* If we dont have an accessor, get one */
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_int_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+ _set_size(this, data, sizeof(SANE_Int));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_probe_devpix (_HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int resolution;
+
+ if (FAILED( sanei_hp_scl_inquire(scsi, scl, &resolution, 0, 0) ))
+ {
+ DBG(1, "probe_devpix: inquiry failed, assume 300 ppi\n");
+ resolution = 300;
+ }
+
+ if (!this->data_acsr)
+ {
+ if (!(this->data_acsr = sanei_hp_accessor_int_new(data)))
+ return SANE_STATUS_NO_MEM;
+ }
+
+ sanei_hp_accessor_setint(this->data_acsr, data, resolution);
+ _set_size(this, data, sizeof(SANE_Int));
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ * Simulate functions
+ */
+static SANE_Status
+_simulate_brightness (HpOption this, HpData data, HpScsi scsi)
+{
+ int k, val, newval;
+ unsigned char *brightness_map;
+ HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ assert (info);
+
+ val = sanei_hp_accessor_getint(this->data_acsr, data);
+
+ DBG(3, "simulate_brightness: value = %d\n", val);
+
+ /* Update brightness map in info structure */
+ brightness_map = &(info->simulate.brightness_map[0]);
+ val *= 2; /* A value of 127 should give a totally white image */
+ for (k = 0; k < 256; k++)
+ {
+ newval = k + val;
+ if (newval < 0) newval = 0; else if (newval > 255) newval = 255;
+ brightness_map[k] = (unsigned char)newval;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static int
+hp_contrast (int x, int g)
+
+{int y = 0;
+
+ if (g < -127) g = -127; else if (g > 127) g = 127;
+ if (x < 0) x = 0; else if (x > 255) x = 255;
+
+ if (g == 0)
+ {
+ y = x;
+ }
+ else if (g < 0)
+ {
+ g = -g;
+ y = x * (255 - 2*g);
+ y = y/255 + g;
+ }
+ else
+ {
+ if (x <= g) y = 0;
+ else if (x >= 255-g) y = 255;
+ else
+ {
+ y = (x - g)*255;
+ y /= (255 - 2*g);
+ }
+ }
+
+ return y;
+}
+
+static SANE_Status
+_simulate_contrast (HpOption this, HpData data, HpScsi scsi)
+{
+ int k, val, newval;
+ unsigned char *contrast_map;
+ HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ assert (info);
+
+ val = sanei_hp_accessor_getint(this->data_acsr, data);
+
+ DBG(3, "simulate_contrast: value = %d\n", val);
+
+ /* Update contrast map in info structure */
+ contrast_map = &(info->simulate.contrast_map[0]);
+
+ for (k = 0; k < 256; k++)
+ {
+ newval = hp_contrast (k, val);
+ if (newval < 0) newval = 0; else if (newval > 255) newval = 255;
+ contrast_map[k] = (unsigned char)newval;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Option download functions
+ */
+static SANE_Status
+_program_generic (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ return hp_option_download(this, data, optset, scsi);
+}
+
+static SANE_Status
+_program_geometry (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+/* #define HP_LIMIT_ADF_WINDOW */
+#ifndef HP_LIMIT_ADF_WINDOW
+
+ return hp_option_download(this, data, optset, scsi);
+
+#else
+
+ HpScl scl = this->descriptor->scl_command;
+ int value;
+ SANE_Status Status;
+
+ if (sanei_hp_optset_scan_type (optset, data) != SCL_ADF_SCAN)
+ return hp_option_download(this, data, optset, scsi);
+
+ /* ADF may crash when scanning only a window ? */
+ if ( (scl == SCL_X_POS) || (scl == SCL_Y_POS) )
+ {
+ value = 0;
+ DBG(3,"program_geometry: set %c-pos to %d\n",
+ (scl == SCL_X_POS) ? 'x' : 'y', value);
+ }
+ else if ( scl == SCL_X_EXTENT )
+ {
+ value = 2550;
+ DBG(3,"program_geometry: set x-extent to %d\n", value);
+ }
+ else
+ {
+ value = 4200;
+ DBG(3,"program_geometry: set y-extent to %d\n", value);
+ }
+
+ Status = sanei_hp_scl_set(scsi, scl, value);
+ return Status;
+
+#endif
+}
+
+static SANE_Status
+_program_data_width (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ int value = sanei_hp_accessor_getint(this->data_acsr, data);
+ SANE_Status status;
+
+ if ( sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR )
+ {
+ value *= 3;
+ if (value < 24)
+ {
+ DBG(3,"program_data_width: map datawith from %d to 24\n", (int)value);
+ value = 24;
+ }
+ }
+ status = sanei_hp_scl_set(scsi, scl, value);
+ return status;
+}
+
+static SANE_Status
+_program_generic_simulate (HpOption this, HpScsi scsi, HpOptSet optset,
+ HpData data)
+{
+ HpScl scl = this->descriptor->scl_command;
+ const char *devname = sanei_hp_scsi_devicename (scsi);
+ int simulate;
+
+ /* Check if command is supported */
+ simulate = ( sanei_hp_device_support_get (devname, scl, 0, 0)
+ != SANE_STATUS_GOOD );
+
+ /* Save simulate flag */
+ sanei_hp_device_simulate_set (devname, scl, simulate);
+
+ if ( !simulate ) /* Let the device do it */
+ return hp_option_download(this, data, optset, scsi);
+
+ DBG(3, "program_generic: %lu not programmed. Will be simulated\n",
+ (unsigned long)(SCL_INQ_ID(scl)));
+
+ switch (scl)
+ {
+ case SCL_BRIGHTNESS:
+ _simulate_brightness (this, data, scsi);
+ break;
+
+ case SCL_CONTRAST:
+ _simulate_contrast (this, data,scsi);
+ break;
+
+ default:
+ DBG(1, "program_generic: No simulation for %lu\n",
+ (unsigned long)(SCL_INQ_ID(scl)));
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_simulate_custom_gamma (HpOption gvector, HpScsi scsi, HpData data)
+{
+ size_t size = sanei_hp_accessor_size(gvector->data_acsr);
+ const unsigned char *vector_data =
+ (const unsigned char *)sanei_hp_accessor_data(gvector->data_acsr, data);
+ HpDeviceInfo *info;
+ int k, newval;
+
+ DBG(3,"program_custom_gamma_simulate: save gamma map\n");
+ if (size != 256)
+ {
+ DBG(1,"program_custom_gamma_simulate: size of vector is %d.\
+ Should be 256.\n", (int)size);
+ return SANE_STATUS_INVAL;
+ }
+
+ RETURN_IF_FAIL (sanei_hp_scl_set(scsi, SCL_TONE_MAP, 0));
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ info->simulate.gamma_simulate = 1;
+
+ for (k = 0; k < 256; k++)
+ {
+ newval = 255 - vector_data[255-k];
+ if (newval < 0) newval = 0; else if (newval > 255) newval = 255;
+ info->simulate.gamma_map[k] = newval;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_program_tonemap (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ hp_bool_t use_custom_map = hp_option_getint(this, data);
+ HpOption gvector = 0;
+ int type = 0;
+
+ if (!use_custom_map)
+ return sanei_hp_scl_set(scsi, SCL_TONE_MAP, 0);
+
+#ifdef ENABLE_7x12_TONEMAPS
+ /* Try to find the appropriate 5P style tonemap. */
+ if (sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR)
+ {
+ type = -1;
+ gvector = hp_optset_get(optset, RGB_TONEMAP);
+ }
+ else
+ {
+ type = -2;
+ gvector = hp_optset_get(optset, GAMMA_VECTOR_7x12);
+ }
+#endif
+
+ /* If that failed, just use 8x8 tonemap */
+ if (!gvector)
+ {
+ HpScl scl_tonemap = SCL_8x8TONE_MAP;
+ hp_bool_t simulate;
+ int id = SCL_INQ_ID(scl_tonemap);
+ int minval, maxval;
+ SANE_Status status;
+
+ type = -1;
+ gvector = hp_optset_get(optset, GAMMA_VECTOR_8x8);
+
+ /* Check if download type supported */
+ status = sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi),
+ SCL_DOWNLOAD_TYPE, &minval, &maxval);
+
+ simulate = (status != SANE_STATUS_GOOD) || (id < minval)
+ || (id > maxval);
+ if (simulate)
+ return _simulate_custom_gamma (gvector, scsi, data);
+ }
+
+ assert(gvector != 0);
+
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_TONE_MAP, type) );
+ return hp_option_download(gvector, data, optset, scsi);
+}
+
+
+static SANE_Status
+_program_dither (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ enum hp_dither_type_e type = hp_option_getint(this, data);
+ HpOption dither;
+
+ switch (type) {
+ case HP_DITHER_CUSTOM:
+ dither = hp_optset_getByName(optset, SANE_NAME_HALFTONE_PATTERN);
+ assert(dither != 0);
+ break;
+ case HP_DITHER_HORIZONTAL:
+ dither = hp_optset_getByName(optset, HP_NAME_HORIZONTAL_DITHER);
+ type = HP_DITHER_CUSTOM;
+ assert(dither != 0);
+ break;
+ default:
+ dither = 0;
+ break;
+ }
+
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_BW_DITHER, type) );
+ if (!dither)
+ return SANE_STATUS_GOOD;
+ return hp_option_download(dither, data, optset, scsi);
+}
+
+#ifdef FAKE_COLORSEP_MATRIXES
+static HpOption
+_get_sepmatrix (HpOptSet optset, HpData data, enum hp_matrix_type_e type)
+{
+ SANE_Fixed buf[9];
+ HpOption matrix = hp_optset_get(optset, SEPMATRIX);
+
+ memset(buf, 0, sizeof(buf));
+ if (type == HP_MATRIX_RED)
+ buf[1] = SANE_FIX(1.0);
+ else if (type == HP_MATRIX_GREEN)
+ buf[4] = SANE_FIX(1.0);
+ else if (type == HP_MATRIX_BLUE)
+ buf[7] = SANE_FIX(1.0);
+ else
+ {
+ assert(!"Bad colorsep type");
+ return 0;
+ }
+ sanei_hp_accessor_set(matrix->data_acsr, data, buf);
+ return matrix;
+}
+#endif
+
+static SANE_Status
+_program_matrix (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ enum hp_matrix_type_e type = hp_option_getint(this, data);
+ HpOption matrix = 0;
+
+ if (type == HP_MATRIX_AUTO)
+ return SANE_STATUS_GOOD; /* Default to matrix set by mode */
+
+ /* Download custom matrix, if we need it. */
+ if (type == HP_MATRIX_CUSTOM)
+ {
+ matrix = hp_optset_getByName(optset, SANE_NAME_MATRIX_RGB);
+ assert(matrix);
+ }
+#ifdef FAKE_COLORSEP_MATRIXES
+ else if (type == HP_MATRIX_RED
+ || type == HP_MATRIX_BLUE
+ || type == HP_MATRIX_GREEN)
+ {
+ matrix = _get_sepmatrix(optset, data, type);
+ type = HP_MATRIX_CUSTOM;
+ assert(matrix);
+ }
+#else
+ else if (type == HP_MATRIX_GREEN)
+ type = HP_MATRIX_PASS;
+#endif
+
+
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_MATRIX, type) );
+ if (matrix)
+ RETURN_IF_FAIL( hp_option_download(matrix, data, optset, scsi) );
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_program_resolution (HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+
+ int xresolution = hp_option_getint(this, data);
+ int yresolution = xresolution;
+ int xscale = 100, yscale = 100;
+
+#ifdef FIX_PHOTOSMART
+ int minval, maxval, media;
+ enum hp_device_compat_e compat;
+
+ /* HP Photosmart scanner has problems with scanning slides/negatives */
+ /* at arbitrary resolutions. The following tests did not work: */
+ /* xres = yres = next lower multiple of 300, xscale = yscale > 100: */
+ /* xres = yres = next higher multiple of 300, xscale = yscale < 100: */
+ /* xres = next lower multiple of 300, xscale > 100 */
+ /* xres = next higher multiple of 300, xscale < 100 */
+ /* yres = next lower multiple of 300, yscale > 100 */
+ /* yres = next higher multiple of 300, yscale < 100 */
+ /* The image extent was ok, but the content was streched in y-direction */
+
+ if (xresolution > 300)
+ {
+ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_PS)
+ && (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &media, &minval, &maxval)
+ == SANE_STATUS_GOOD)
+ && ((media == HP_MEDIA_SLIDE) || (media == HP_MEDIA_NEGATIVE)))
+ {int next_resolution;
+ next_resolution = (xresolution % 300) * 300;
+ if (next_resolution < 300) next_resolution = 300;
+ yresolution = next_resolution;
+ yscale = (int)(100.0 * xresolution / yresolution);
+ }
+ }
+#endif
+
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_X_SCALE, xscale) );
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_Y_SCALE, yscale) );
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_X_RESOLUTION, xresolution) );
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_Y_RESOLUTION, yresolution) );
+
+ return SANE_STATUS_GOOD;
+}
+
+static char *
+get_home_dir (void)
+{
+#ifdef SANE_HOME_HP
+
+ return getenv (SANE_HOME_HP);
+
+#else
+
+ struct passwd *pw;
+
+ pw = getpwuid (getuid ()); /* Look if we can find our home directory */
+ return pw ? pw->pw_dir : NULL;
+
+#endif
+}
+
+static char *
+get_calib_filename (HpScsi scsi)
+{
+ char *homedir;
+ char *calib_filename, *cf;
+ const char *devname = sanei_hp_scsi_devicename (scsi);
+ int name_len;
+
+ homedir = get_home_dir (); /* Look if we can find our home directory */
+ if (!homedir) return NULL;
+
+ name_len = strlen (homedir) + 33;
+ if ( devname ) name_len += strlen (devname);
+ calib_filename = sanei_hp_allocz (name_len);
+ if (!calib_filename) return NULL;
+
+ strcpy (calib_filename, homedir);
+ strcat (calib_filename, "/.sane/calib-hp");
+ if ( devname && devname[0] ) /* Replace '/' by "+-" */
+ {
+ cf = calib_filename + strlen (calib_filename);
+ *(cf++) = ':';
+ while (*devname)
+ {
+ if (*devname == '/') *(cf++) = '+', *(cf++) = '-';
+ else *(cf++) = *devname;
+ devname++;
+ }
+ }
+ strcat (calib_filename, ".dat");
+
+ return calib_filename;
+}
+
+static SANE_Status
+read_calib_file (int *nbytes, char **calib_data, HpScsi scsi)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ char *calib_filename;
+ FILE *calib_file;
+ int err, c1, c2, c3, c4;
+
+ *nbytes = 0;
+ *calib_data = NULL;
+
+ calib_filename = get_calib_filename ( scsi );
+ if (!calib_filename) return SANE_STATUS_NO_MEM;
+
+ calib_file = fopen (calib_filename, "rb");
+ if ( calib_file )
+ {
+ err = ((c1 = getc (calib_file)) == EOF);
+ err |= ((c2 = getc (calib_file)) == EOF);
+ err |= ((c3 = getc (calib_file)) == EOF);
+ err |= ((c4 = getc (calib_file)) == EOF);
+ *nbytes = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
+ if ( err )
+ {
+ DBG(1, "read_calib_file: Error reading calibration data size\n");
+ status = SANE_STATUS_EOF;
+ }
+ else
+ {
+ *calib_data = sanei_hp_alloc ( *nbytes );
+ if ( !*calib_data )
+ {
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ err |= ((int)fread (*calib_data,1,*nbytes,calib_file) != *nbytes);
+ if ( err )
+ {
+ DBG(1, "read_calib_file: Error reading calibration data\n");
+ sanei_hp_free ( *calib_data );
+ status = SANE_STATUS_EOF;
+ }
+ }
+ }
+ fclose ( calib_file );
+ }
+ else
+ {
+ DBG(1, "read_calib_file: Error opening calibration file %s\
+ for reading\n", calib_filename);
+ status = SANE_STATUS_EOF;
+ }
+
+ sanei_hp_free (calib_filename);
+
+ return ( status );
+}
+
+static SANE_Status
+write_calib_file (int nbytes, char *data, HpScsi scsi)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ char *calib_filename;
+ int err;
+ FILE *calib_file;
+
+ calib_filename = get_calib_filename ( scsi );
+ if (!calib_filename) return SANE_STATUS_NO_MEM;
+
+ calib_file = fopen (calib_filename, "wb");
+ if ( calib_file )
+ {
+ err = (putc ((nbytes >> 24) & 0xff, calib_file) == EOF);
+ err |= (putc ((nbytes >> 16) & 0xff, calib_file) == EOF);
+ err |= (putc ((nbytes >> 8) & 0xff, calib_file) == EOF);
+ err |= (putc (nbytes & 0xff, calib_file) == EOF);
+ err |= ((int)fwrite (data, 1, nbytes, calib_file) != nbytes);
+ fclose (calib_file);
+ if ( err )
+ {
+ DBG(1, "write_calib_file: Error writing calibration data\n");
+ unlink (calib_filename);
+ status = SANE_STATUS_EOF;
+ }
+ }
+ else
+ {
+ DBG(1, "write_calib_file: Error opening calibration file %s\
+ for writing\n", calib_filename);
+ status = SANE_STATUS_EOF;
+ }
+
+ sanei_hp_free (calib_filename);
+ return (status);
+}
+
+static SANE_Status
+_program_media (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ int req_media, minval, maxval, current_media;
+ HpScl scl = this->descriptor->scl_command;
+
+ req_media = sanei_hp_accessor_getint(this->data_acsr, data);
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &current_media,
+ &minval, &maxval) );
+ if (current_media == req_media)
+ return SANE_STATUS_GOOD;
+
+ /* Unload scanner */
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_UNLOAD, 0) );
+
+ /* Select new media */
+ RETURN_IF_FAIL( hp_option_download(this, data, optset, scsi));
+
+ /* Update support list */
+ sanei_hp_device_support_probe (scsi);
+
+ if (req_media == HP_MEDIA_PRINT)
+ hp_download_calib_file (scsi);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_program_unload_after_scan (HpOption this, HpScsi scsi,
+ HpOptSet UNUSEDARG optset, HpData data)
+{ HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ assert (info);
+ info->unload_after_scan = sanei_hp_accessor_getint(this->data_acsr, data);
+
+ DBG(3,"program_unload_after_scan: flag = %lu\n",
+ (unsigned long)info->unload_after_scan);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_program_lamp_off (HpOption UNUSEDARG this, HpScsi scsi,
+ HpOptSet UNUSEDARG optset, HpData UNUSEDARG data)
+{
+ DBG(3,"program_lamp_off: shut off lamp\n");
+
+ return sanei_hp_scl_set(scsi, SCL_LAMPTEST, 0);
+}
+
+static SANE_Status
+_program_scan_type (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+
+{ int req_scan_type;
+
+ req_scan_type = sanei_hp_accessor_getint(this->data_acsr, data);
+
+ if ( req_scan_type == HP_SCANTYPE_XPA )
+ {
+ enum hp_scanmode_e scan_mode = sanei_hp_optset_scanmode(optset, data);
+ static unsigned char xpa_matrix_coeff[] = {
+0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x80
+ };
+ static unsigned char xpa_tone_map[] = {
+0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xfe,0x0f,
+0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f,
+0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e,
+0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e,
+0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d,
+0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d,
+0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c,
+0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c,
+0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b,
+0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a,
+0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a,
+0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09,
+0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08,
+0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07,
+0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05,
+0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02,
+0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00,0x0f,0xfe,0x0f,
+0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f,
+0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e,
+0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e,
+0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d,
+0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d,
+0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c,
+0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c,
+0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b,
+0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a,
+0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a,
+0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09,
+0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08,
+0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07,
+0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05,
+0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02,
+0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00,0x0f,0xfe,0x0f,
+0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f,
+0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e,
+0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e,
+0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d,
+0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d,
+0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c,
+0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c,
+0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b,
+0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a,
+0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a,
+0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09,
+0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08,
+0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07,
+0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05,
+0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02,
+0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00
+ };
+
+ sanei_hp_scl_set(scsi, SCL_RESERVED1, 0); /* dont know */
+ sanei_hp_scl_set(scsi, SCL_10952, 0); /* Calibration mode */
+
+ if ( sanei_hp_is_active_xpa (scsi)
+ && ( (scan_mode==HP_SCANMODE_COLOR)
+ || (scan_mode==HP_SCANMODE_GRAYSCALE)) )
+ {
+ DBG (3,"program_scan_type: set tone map for active XPA\n");
+ sanei_hp_scl_download (scsi, SCL_10x9MATRIX_COEFF, xpa_matrix_coeff,
+ sizeof (xpa_matrix_coeff));
+ sanei_hp_scl_set(scsi, SCL_MATRIX, -1); /* Set matrix coefficient */
+
+ sanei_hp_scl_download (scsi, SCL_7x12TONE_MAP, xpa_tone_map,
+ sizeof (xpa_tone_map));
+ sanei_hp_scl_set(scsi, SCL_TONE_MAP, -1); /* Select tone map */
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+_program_change_doc (HpOption UNUSEDARG this, HpScsi scsi,
+ HpOptSet UNUSEDARG optset, HpData UNUSEDARG data)
+{
+ int istat;
+
+ DBG(2, "program_change_doc: inquire ADF ready\n");
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_ADF_READY, &istat, 0, 0) );
+ if ( istat != 1 ) /* ADF not ready */
+ {
+ DBG(2, "program_change_doc: ADF not ready\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(2, "program_change_doc: inquire paper in ADF\n");
+
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_ADF_BIN, &istat, 0, 0) );
+ if ( istat == 0 ) /* Nothing in ADF BIN */
+ {
+ DBG(2, "program_change_doc: nothing in ADF BIN. Just Unload.\n");
+ return sanei_hp_scl_set(scsi, SCL_UNLOAD, 0);
+ }
+
+ DBG(2, "program_change_doc: Clear errors and change document.\n");
+
+ RETURN_IF_FAIL( sanei_hp_scl_clearErrors (scsi) );
+
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_CHANGE_DOC, 0) );
+
+ return sanei_hp_scl_errcheck (scsi);
+}
+
+static SANE_Status
+_program_unload (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ hp_bool_t adfscan = ( sanei_hp_optset_scan_type (optset, data)
+ == SCL_ADF_SCAN );
+
+ /* If we have an ADF, try to see if it is ready to unload */
+ if (adfscan)
+ {int val;
+
+ if ( sanei_hp_scl_inquire(scsi, SCL_ADF_RDY_UNLOAD, &val, 0, 0)
+ == SANE_STATUS_GOOD )
+ {
+ DBG(3, "program_unload: ADF is%sready to unload\n", val ? " " : " not ");
+ }
+ else
+ {
+ DBG(3, "program_unload: Command 'Ready to unload' not supported\n");
+ }
+ }
+ return hp_option_download(this, data, optset, scsi);
+}
+
+static SANE_Status
+_program_calibrate (HpOption UNUSEDARG this, HpScsi scsi,
+ HpOptSet UNUSEDARG optset, HpData UNUSEDARG data)
+{
+ struct passwd *pw;
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t calib_size;
+ char *calib_buf;
+
+ RETURN_IF_FAIL ( sanei_hp_scl_calibrate(scsi) ); /* Start calibration */
+
+ pw = getpwuid (getuid ()); /* Look if we can find our home directory */
+ if (!pw) return SANE_STATUS_GOOD;
+
+ DBG(3, "_program_calibrate: Read calibration data\n");
+
+ RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP,
+ &calib_size, &calib_buf) );
+
+ DBG(3, "_program_calibrate: Got %lu bytes of calibration data\n",
+ (unsigned long) calib_size);
+
+ write_calib_file (calib_size, calib_buf, scsi);
+
+ sanei_hp_free (calib_buf);
+
+ return (status);
+}
+
+/* The exposure time of the HP Photosmart can be changed by overwriting
+ * some headers of the calibration data. The scanner uses a slower stepping
+ * speed for higher exposure times */
+static SANE_Status
+_program_ps_exposure_time (HpOption this, HpScsi scsi,
+ HpOptSet UNUSEDARG optset, HpData data)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t calib_size = 0;
+ char *calib_buf = NULL;
+ int i;
+ int option = hp_option_getint(this, data);
+ static char *exposure[] =
+ {"\x00\x64\x00\x64\x00\x64", /* 100% */
+ "\x00\x7d\x00\x7d\x00\x7d", /* 125% */
+ "\x00\x96\x00\x96\x00\x96", /* 150% */
+ "\x00\xaf\x00\xaf\x00\xaf", /* 175% */
+ "\x00\xc0\x00\xc0\x00\xc0", /* 200% */
+ "\x00\xe1\x00\xe1\x00\xe1", /* 225% */
+ "\x00\xfa\x00\xfa\x00\xfa", /* 250% */
+ "\x01\x13\x01\x13\x01\x13", /* 275% */
+ "\x01\x24\x01\x24\x01\x24", /* 300% */
+ "\x00\x64\x00\xc0\x01\x24"}; /* Negatives */
+ /* Negatives get some extra blue to penetrate the orange mask and less
+ red to not saturate the red channel; R:G:B = 100:200:300 */
+
+ /* We dont use the 100% case. It may cause mechanical problems */
+ if ((option < 1) || (option > 9)) return 0;
+ RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP,
+ &calib_size, &calib_buf) );
+
+ DBG(3, "_program_ps_exposure_time: Got %lu bytes of calibration data\n",
+ (unsigned long) calib_size);
+
+ for (i = 0; i < 6; i++)
+ calib_buf[24 + i] = exposure[option][i];
+
+ status = sanei_hp_scl_download ( scsi, SCL_CALIB_MAP, calib_buf,
+ (size_t)calib_size);
+
+ /* see what the scanner did to our alterations */
+ /*
+ * RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP,
+ * &calib_size, &calib_buf) );
+ *
+ * for (i = 0; i < 9; i++)
+ * DBG(1, ">%x ", (unsigned char) calib_buf[24 + i]);
+ */
+
+ sanei_hp_free (calib_buf);
+
+ return (status);
+}
+
+static SANE_Status
+_program_scanmode (HpOption this, HpScsi scsi, HpOptSet optset, HpData data)
+{
+ enum hp_scanmode_e new_mode = hp_option_getint(this, data);
+ int invert = 0;
+ int fw_invert = 0; /* Flag: does firmware do inversion ? */
+ int is_model_4c = 0;
+ enum hp_device_compat_e compat;
+ hp_bool_t disable_xpa = ( sanei_hp_optset_scan_type (optset, data)
+ != SCL_XPA_SCAN );
+
+ /* Seems that models 3c/4c/6100C invert image data at 10 bit by themself. */
+ /* So we must not invert it by the invert command. */
+ if ( (sanei_hp_device_probe (&compat,scsi) == SANE_STATUS_GOOD)
+ && (compat & HP_COMPAT_4C) )
+ {
+ is_model_4c = 1;
+ DBG(3, "program_scanmode: model 3c/4c/6100C recognized\n");
+ }
+
+ if (is_model_4c)
+ {
+ const HpDeviceInfo *info;
+ int data_width;
+ HpOption option;
+ int is_preview = 0;
+
+ /* Preview uses maximum 8 bit. So we don't need to check data width */
+ option = hp_optset_getByName (optset, SANE_NAME_PREVIEW);
+ if ( option )
+ is_preview = hp_option_getint (option, data);
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+
+ if ( (!is_preview)
+ && hp_optset_isEnabled (optset, data, SANE_NAME_BIT_DEPTH, info))
+ {
+ data_width = sanei_hp_optset_data_width (optset, data);
+ if ((data_width == 10) || (data_width == 30))
+ {
+ fw_invert = 1;
+ DBG(3, "program_scanmode: firmware is doing inversion\n");
+ }
+ }
+ }
+
+ /* Disabling XPA resets some settings in the scanner. */
+ /* Scanmode is the first we program. So set XPA prior to scanmode */
+ DBG(3, "program_scanmode: disable XPA = %d\n", (int)disable_xpa);
+ sanei_hp_scl_set(scsi, SCL_XPA_DISABLE, disable_xpa);
+
+ RETURN_IF_FAIL( hp_option_download(this, data, optset, scsi) );
+
+ switch (new_mode) {
+ case HP_SCANMODE_GRAYSCALE:
+ /* Make sure that it is not b/w. Correct data width will be set later */
+ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 8) );
+ invert = 1;
+ if (fw_invert) invert = 0;
+ /* For active XPA we use a tone map. Dont invert */
+ if ( (!disable_xpa) && sanei_hp_is_active_xpa (scsi) ) invert = 0;
+ break;
+ case HP_SCANMODE_COLOR:
+ invert = 1;
+ if (fw_invert) invert = 0;
+ /* For active XPA we use a tone map. Dont invert */
+ if ( (!disable_xpa) && sanei_hp_is_active_xpa (scsi) ) invert = 0;
+ break;
+ default:
+ break;
+ }
+
+ return sanei_hp_scl_set(scsi, SCL_INVERSE_IMAGE, invert);
+}
+
+static SANE_Status
+_program_mirror_horiz (HpOption this, HpScsi scsi, HpOptSet UNUSEDARG optset,
+ HpData data)
+{
+ int sec_dir, mirror = hp_option_getint(this, data);
+
+ if ( mirror == HP_MIRROR_HORIZ_CONDITIONAL )
+ {
+ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR,
+ &sec_dir, 0, 0) );
+ mirror = (sec_dir == 1);
+ }
+
+ return sanei_hp_scl_set(scsi, SCL_MIRROR_IMAGE, mirror);
+}
+
+/*
+ * Option enable predicates
+ */
+static hp_bool_t
+_enable_choice (HpOption this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ SANE_String_Const * strlist
+ = sanei_hp_accessor_choice_strlist((HpAccessorChoice) this->data_acsr,
+ optset, data, info);
+
+ _set_stringlist(this, data, strlist);
+
+ assert(strlist[0]);
+ return strlist[0] != 0;
+}
+
+#ifdef ENABLE_7x12_TONEMAPS
+static hp_bool_t
+_enable_rgb_maps (HpOption this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA);
+
+ return (cgam && hp_option_getint(cgam, data)
+ && sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR);
+}
+#endif
+
+static hp_bool_t
+_enable_mono_map (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA);
+
+ return (cgam && hp_option_getint(cgam, data)
+ && ( sanei_hp_optset_scanmode(optset, data) != HP_SCANMODE_COLOR
+ || ! hp_optset_getByName(optset, SANE_NAME_GAMMA_VECTOR_R) ));
+}
+
+static hp_bool_t
+_enable_rgb_matrix (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ HpOption type = hp_optset_get(optset, MATRIX_TYPE);
+
+ return type && hp_option_getint(type, data) == HP_MATRIX_CUSTOM;
+}
+
+static hp_bool_t
+_enable_brightness (HpOption this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA);
+ HpScl scl = this->descriptor->scl_command;
+ int simulate;
+
+ simulate = ( sanei_hp_device_support_get ( info->devname, scl, 0, 0 )
+ != SANE_STATUS_GOOD );
+ /* If brightness is simulated, we only do it for gray/color */
+ if ( simulate )
+ {HpOption mode = hp_optset_get(optset, SCAN_MODE);
+ int val = hp_option_getint (mode, data);
+ int disable;
+
+ disable = (val != HP_SCANMODE_GRAYSCALE) && (val != HP_SCANMODE_COLOR);
+ if (disable)
+ {
+ if ( cgam ) /* Disable custom gamma. */
+ {
+ val = 0;
+ hp_option_set (cgam, data, &val, 0);
+ }
+ return 0;
+ }
+ }
+
+ return !cgam || !hp_option_getint(cgam, data);
+}
+
+static hp_bool_t
+_enable_autoback (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_LINEART;
+}
+
+static hp_bool_t
+_enable_custom_gamma (HpOption this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ HpScl scl_tonemap = SCL_8x8TONE_MAP;
+ int id = SCL_INQ_ID(scl_tonemap);
+ int simulate, minval, maxval;
+ SANE_Status status;
+
+ /* Check if download type supported */
+ status = sanei_hp_device_support_get ( info->devname,
+ SCL_DOWNLOAD_TYPE, &minval, &maxval);
+
+ simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval);
+
+ /* If custom gamma is simulated, we only do it for gray/color */
+ if ( simulate )
+ {HpOption mode = hp_optset_get(optset, SCAN_MODE);
+ int val;
+
+ if ( mode )
+ {
+ val = hp_option_getint (mode, data);
+ if ((val != HP_SCANMODE_GRAYSCALE) && (val != HP_SCANMODE_COLOR))
+ {
+ val = 0;
+ hp_option_set (this, data, &val, 0);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static hp_bool_t
+_enable_halftone (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE;
+}
+
+static hp_bool_t
+_enable_halftonevec (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ if (sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE)
+ {
+ HpOption dither = hp_optset_get(optset, HALFTONE_PATTERN);
+
+ return dither && hp_option_getint(dither, data) == HP_DITHER_CUSTOM;
+ }
+ return 0;
+}
+
+static hp_bool_t
+_enable_data_width (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{enum hp_scanmode_e mode;
+
+ mode = sanei_hp_optset_scanmode (optset, data);
+ return ( (mode == HP_SCANMODE_GRAYSCALE) || (mode == HP_SCANMODE_COLOR) );
+}
+
+static hp_bool_t
+_enable_out8 (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info)
+{
+ if (hp_optset_isEnabled (optset, data, SANE_NAME_BIT_DEPTH, info))
+ {
+ int data_width = sanei_hp_optset_data_width (optset, data);
+ return (((data_width > 8) && (data_width <= 16)) || (data_width > 24));
+ }
+ return 0;
+}
+
+static hp_bool_t
+_enable_calibrate (HpOption UNUSEDARG this, HpOptSet optset, HpData data,
+ const HpDeviceInfo UNUSEDARG *info)
+{
+ HpOption media = hp_optset_get(optset, MEDIA);
+
+ /* If we dont have the media button, we should have calibrate */
+ if ( !media ) return 1;
+
+ return hp_option_getint(media, data) == HP_MEDIA_PRINT;
+}
+
+static SANE_Status
+hp_download_calib_file (HpScsi scsi)
+{
+ int nbytes;
+ char *calib_data;
+ SANE_Status status;
+
+ RETURN_IF_FAIL ( read_calib_file ( &nbytes, &calib_data, scsi ) );
+
+ DBG(3, "hp_download_calib_file: Got %d bytes calibration data\n", nbytes);
+
+ status = sanei_hp_scl_download ( scsi, SCL_CALIB_MAP, calib_data,
+ (size_t) nbytes);
+ sanei_hp_free ( calib_data );
+
+ DBG(3, "hp_download_calib_file: download %s\n", (status == SANE_STATUS_GOOD) ?
+ "successful" : "failed");
+
+ return status;
+}
+
+
+/*
+ * The actual option descriptors.
+ */
+
+#if (defined(__IBMC__) || defined(__IBMCPP__))
+#ifndef _AIX
+ #define INT INT
+#endif
+#endif
+
+#define SCANNER_OPTION(name,type,unit) \
+ PASTE(SANE_NAME_,name), \
+ PASTE(SANE_TITLE_,name), \
+ PASTE(SANE_DESC_,name), \
+ PASTE(SANE_TYPE_,type), \
+ PASTE(SANE_UNIT_,unit), \
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT
+
+#define CONSTANT_OPTION(name,type,unit) \
+ PASTE(SANE_NAME_,name), \
+ PASTE(SANE_TITLE_,name), \
+ PASTE(SANE_DESC_,name), \
+ PASTE(SANE_TYPE_,type), \
+ PASTE(SANE_UNIT_,unit), \
+ SANE_CAP_SOFT_DETECT
+
+#define INTERNAL_OPTION(name,type,unit) \
+ PASTE(HP_NAME_,name), "", "", \
+ PASTE(SANE_TYPE_,type), \
+ PASTE(SANE_UNIT_,unit), \
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT
+
+#define OPTION_GROUP(name) "", name, "", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0
+
+#define ADVANCED_GROUP(name) \
+ "", name, "", SANE_TYPE_GROUP, SANE_UNIT_NONE, SANE_CAP_ADVANCED
+
+
+#define REQUIRES(req) req
+#define NO_REQUIRES REQUIRES(0)
+
+static const struct hp_option_descriptor_s NUM_OPTIONS[1] = {{
+ CONSTANT_OPTION(NUM_OPTIONS, INT, NONE),
+ NO_REQUIRES,
+ _probe_num_options,
+ 0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+
+static const struct hp_option_descriptor_s SCAN_MODE_GROUP[1] = {{
+ OPTION_GROUP(SANE_I18N("Scan Mode")),
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+
+/* Preview stuff */
+static const struct hp_option_descriptor_s PREVIEW_MODE[1] = {{
+ SCANNER_OPTION(PREVIEW, BOOL, NONE),
+ NO_REQUIRES,
+ _probe_bool,
+ 0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+
+static const struct hp_choice_s _scanmode_choices[] = {
+ { HP_SCANMODE_LINEART, SANE_VALUE_SCAN_MODE_LINEART, 0, 0, 0 },
+ { HP_SCANMODE_HALFTONE, SANE_VALUE_SCAN_MODE_HALFTONE, 0, 0, 0 },
+ { HP_SCANMODE_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY, 0, 0, 0 },
+ { HP_SCANMODE_COLOR, SANE_VALUE_SCAN_MODE_COLOR, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s SCAN_MODE[1] = {{
+ SCANNER_OPTION(SCAN_MODE, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_scanmode, 0,
+ 1, 1, 1, 0, 0, SCL_OUTPUT_DATA_TYPE, 0, 0, 0, _scanmode_choices
+}};
+static const struct hp_option_descriptor_s SCAN_RESOLUTION[1] = {{
+ SCANNER_OPTION(SCAN_RESOLUTION, INT, DPI),
+ NO_REQUIRES,
+ _probe_resolution, _program_resolution, 0,
+ 0, 1, 0, 0, 1, SCL_X_RESOLUTION, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s DEVPIX_RESOLUTION[1] = {{
+ INTERNAL_OPTION(DEVPIX_RESOLUTION, INT, DPI),
+ NO_REQUIRES,
+ _probe_devpix, 0, 0,
+ 0, 0, 0, 0, 1, SCL_DEVPIX_RESOLUTION, 0, 0, 0, 0
+}};
+
+static const struct hp_option_descriptor_s ENHANCEMENT_GROUP[1] = {{
+ OPTION_GROUP(SANE_I18N("Enhancement")),
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+static const struct hp_option_descriptor_s BRIGHTNESS[1] = {{
+ SCANNER_OPTION(BRIGHTNESS, INT, NONE),
+ NO_REQUIRES,
+ _probe_int_brightness, _program_generic_simulate, _enable_brightness,
+ 0, 0, 0, 0, 0, SCL_BRIGHTNESS, -127, 127, 0, 0
+}};
+static const struct hp_option_descriptor_s CONTRAST[1] = {{
+ SCANNER_OPTION(CONTRAST, INT, NONE),
+ NO_REQUIRES,
+ _probe_int_brightness, _program_generic_simulate, _enable_brightness,
+ 0, 0, 0, 0, 0, SCL_CONTRAST, -127, 127, 0, 0
+}};
+#ifdef SCL_SHARPENING
+static const struct hp_option_descriptor_s SHARPENING[1] = {{
+ SCANNER_OPTION(SHARPENING, INT, NONE),
+ NO_REQUIRES,
+ _probe_int, _program_generic, 0,
+ 0, 0, 0, 0, 0, SCL_SHARPENING, -127, 127, 0, 0
+}};
+#endif
+static const struct hp_option_descriptor_s AUTO_THRESHOLD[1] = {{
+ SCANNER_OPTION(AUTO_THRESHOLD, BOOL, NONE),
+ NO_REQUIRES,
+ _probe_bool, _program_generic, _enable_autoback,
+ 0, 0, 0, 0, 0, SCL_AUTO_BKGRND, 0, 0, 0, 0
+}};
+
+static const struct hp_option_descriptor_s ADVANCED_GROUP[1] = {{
+ ADVANCED_GROUP(SANE_I18N("Advanced Options")),
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+/* FIXME: make this a choice? (BW or RGB custom) */
+static const struct hp_option_descriptor_s CUSTOM_GAMMA[1] = {{
+ SCANNER_OPTION(CUSTOM_GAMMA, BOOL, NONE),
+ NO_REQUIRES,
+ _probe_custom_gamma, _program_tonemap, _enable_custom_gamma,
+ 1, 0, 0, 0, 0, SCL_TONE_MAP, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s GAMMA_VECTOR_8x8[1] = {{
+ SCANNER_OPTION(GAMMA_VECTOR, FIXED, NONE),
+ NO_REQUIRES,
+ _probe_gamma_vector, 0, _enable_mono_map,
+ 0, 0, 0, 0, 0, SCL_8x8TONE_MAP, 0, 0, 0, 0
+}};
+
+#ifdef ENABLE_7x12_TONEMAPS
+static const struct hp_option_descriptor_s GAMMA_VECTOR_7x12[1] = {{
+ SCANNER_OPTION(GAMMA_VECTOR, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C
+ |HP_COMPAT_5200C|HP_COMPAT_6300C),
+ _probe_gamma_vector, 0, _enable_mono_map,
+ 0, 0, 0, 0, 0, SCL_BW7x12TONE_MAP, 0, 0, 0, 0
+}};
+
+static const struct hp_option_descriptor_s RGB_TONEMAP[1] = {{
+ INTERNAL_OPTION(RGB_TONEMAP, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C
+ |HP_COMPAT_5200C|HP_COMPAT_6300C),
+ _probe_gamma_vector, 0, 0,
+ 0, 0, 0, 0, 0, SCL_7x12TONE_MAP, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s GAMMA_VECTOR_R[1] = {{
+ SCANNER_OPTION(GAMMA_VECTOR_R, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C
+ |HP_COMPAT_5200C|HP_COMPAT_6300C),
+ _probe_gamma_vector, 0, _enable_rgb_maps,
+ 0,0,0,0,0,0,0,0,0,0
+}};
+static const struct hp_option_descriptor_s GAMMA_VECTOR_G[1] = {{
+ SCANNER_OPTION(GAMMA_VECTOR_G, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C
+ |HP_COMPAT_5200C|HP_COMPAT_6300C),
+ _probe_gamma_vector, 0, _enable_rgb_maps,
+ 0,0,0,0,0,0,0,0,0,0
+}};
+static const struct hp_option_descriptor_s GAMMA_VECTOR_B[1] = {{
+ SCANNER_OPTION(GAMMA_VECTOR_B, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C
+ |HP_COMPAT_5200C|HP_COMPAT_6300C),
+ _probe_gamma_vector, 0, _enable_rgb_maps,
+ 0,0,0,0,0,0,0,0,0,0
+}};
+#endif
+
+static const struct hp_choice_s _halftone_choices[] = {
+ { HP_DITHER_COARSE, SANE_I18N("Coarse"), 0, 0, 0 },
+ { HP_DITHER_FINE, SANE_I18N("Fine"), 0, 0, 0 },
+ { HP_DITHER_BAYER, SANE_I18N("Bayer"), 0, 0, 0 },
+ { HP_DITHER_VERTICAL, SANE_I18N("Vertical"), 0, 0, 0 },
+ { HP_DITHER_HORIZONTAL, SANE_I18N("Horizontal"), 0, 1, 0 },
+ { HP_DITHER_CUSTOM, SANE_I18N("Custom"), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s HALFTONE_PATTERN[1] = {{
+ SCANNER_OPTION(HALFTONE_PATTERN, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_dither, _enable_halftone,
+ 1, 0, 0, 0, 0, SCL_BW_DITHER, 0, 0, 0, _halftone_choices
+}};
+/* FIXME: Halftone dimension? */
+
+#ifdef ENABLE_16X16_DITHERS
+static const struct hp_option_descriptor_s HALFTONE_PATTERN_16x16[1] = {{
+ SCANNER_OPTION(HALFTONE_PATTERN, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_4P | HP_COMPAT_4C | HP_COMPAT_5100C
+ | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C),
+ _probe_horiz_dither, 0, _enable_halftonevec,
+ 0, 0, 0, 0, 0, SCL_BW16x16DITHER
+}};
+static const struct hp_option_descriptor_s HORIZONTAL_DITHER_16x16[1] = {{
+ INTERNAL_OPTION(HORIZONTAL_DITHER, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_4P | HP_COMPAT_4C | HP_COMPAT_5100C
+ | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C),
+ _probe_horiz_dither, 0, 0,
+ 0, 0, 0, 0, 0, SCL_BW16x16DITHER
+}};
+#endif
+static const struct hp_option_descriptor_s HALFTONE_PATTERN_8x8[1] = {{
+ SCANNER_OPTION(HALFTONE_PATTERN, FIXED, NONE),
+ NO_REQUIRES,
+ _probe_horiz_dither, 0, _enable_halftonevec,
+ 0, 0, 0, 0, 0, SCL_BW8x8DITHER, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s HORIZONTAL_DITHER_8x8[1] = {{
+ INTERNAL_OPTION(HORIZONTAL_DITHER, FIXED, NONE),
+ NO_REQUIRES,
+ _probe_horiz_dither, 0, 0,
+ 0, 0, 0, 0, 0, SCL_BW8x8DITHER, 0, 0, 0, 0
+}};
+
+static const struct hp_choice_s _matrix_choices[] = {
+ { HP_MATRIX_AUTO, SANE_I18N("Auto"), 0, 1, 0 },
+ { HP_MATRIX_RGB, SANE_I18N("NTSC RGB"), _cenable_incolor, 0, 0 },
+ { HP_MATRIX_XPA_RGB, SANE_I18N("XPA RGB"), _cenable_incolor, 0, 0 },
+ { HP_MATRIX_PASS, SANE_I18N("Pass-through"), _cenable_incolor, 0, 0 },
+ { HP_MATRIX_BW, SANE_I18N("NTSC Gray"), _cenable_notcolor, 0, 0 },
+ { HP_MATRIX_XPA_BW, SANE_I18N("XPA Gray"), _cenable_notcolor, 0, 0 },
+ { HP_MATRIX_RED, SANE_I18N("Red"), _cenable_notcolor, 0, 0 },
+ { HP_MATRIX_GREEN, SANE_I18N("Green"), _cenable_notcolor, 1, 0 },
+ { HP_MATRIX_BLUE, SANE_I18N("Blue"), _cenable_notcolor, 0, 0 },
+#ifdef ENABLE_CUSTOM_MATRIX
+ { HP_MATRIX_CUSTOM, SANE_I18N("Custom"), 0, 0, 0 },
+#endif
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct hp_option_descriptor_s MATRIX_TYPE[1] = {{
+ SCANNER_OPTION(MATRIX_TYPE, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_matrix, _enable_choice,
+ 1, 0, 0, 0, 0, SCL_MATRIX, 0, 0, 0, _matrix_choices
+}};
+
+static const struct hp_option_descriptor_s MATRIX_RGB[1] = {{
+ SCANNER_OPTION(MATRIX_RGB, FIXED, NONE),
+ NO_REQUIRES,
+ _probe_matrix, 0, _enable_rgb_matrix,
+ 0, 0, 0, 0, 0, SCL_8x9MATRIX_COEFF, 0, 0, 0, 0
+}};
+#ifdef FAKE_COLORSEP_MATRIXES
+static const struct hp_option_descriptor_s SEPMATRIX[1] = {{
+ INTERNAL_OPTION(SEPMATRIX, FIXED, NONE),
+ NO_REQUIRES,
+ _probe_vector, 0, 0,
+ 0, 0, 0, 0, 0, SCL_8x9MATRIX_COEFF, 0, 0, 0, 0
+}};
+#endif
+#ifdef ENABLE_10BIT_MATRIXES
+static const struct hp_option_descriptor_s MATRIX_RGB10[1] = {{
+ SCANNER_OPTION(MATRIX_RGB, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C
+ | HP_COMPAT_5200C | HP_COMPAT_6300C),
+ _probe_matrix, 0, _enable_rgb_matrix,
+ 0, 0, 0, 0, 0, SCL_10x9MATRIX_COEFF, 0, 0, 0, 0
+}};
+#endif
+#ifdef NotYetSupported
+static const struct hp_option_descriptor_s BWMATRIX_GRAY10[1] = {{
+ SCANNER_OPTION(MATRIX_GRAY, FIXED, NONE),
+ REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C
+ | HP_COMPAT_5200C | HP_COMPAT_6300C),
+ _probe_matrix, 0, _enable_gray_matrix,
+ 0, 0, 0, 0, 0, SCL_10x3MATRIX_COEFF, 0, 0, 0, 0
+}};
+#endif
+
+static const struct hp_choice_s _scan_speed_choices[] = {
+ { 0, SANE_I18N("Auto"), 0, 0, 0 },
+ { 1, SANE_I18N("Slow"), 0, 0, 0 },
+ { 2, SANE_I18N("Normal"), 0, 0, 0 },
+ { 3, SANE_I18N("Fast"), 0, 0, 0 },
+ { 4, SANE_I18N("Extra Fast"), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s SCAN_SPEED[1] = {{
+ SCANNER_OPTION(SCAN_SPEED, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_generic, 0,
+ 0, 0, 0, 0, 1, SCL_SPEED, 0, 0, 0, _scan_speed_choices
+}};
+
+static const struct hp_choice_s _smoothing_choices[] = {
+ { 0, SANE_I18N("Auto"), 0, 0, 0 },
+ { 3, SANE_I18N("Off"), 0, 0, 0 },
+ { 1, SANE_I18N("2-pixel"), 0, 0, 0 },
+ { 2, SANE_I18N("4-pixel"), 0, 0, 0 },
+ { 4, SANE_I18N("8-pixel"), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s SMOOTHING[1] = {{
+ SCANNER_OPTION(SMOOTHING, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_generic, 0,
+ 0, 0, 0, 0, 0, SCL_FILTER, 0, 0, 0, _smoothing_choices
+}};
+
+static const struct hp_choice_s _media_choices[] = {
+ { HP_MEDIA_PRINT, SANE_I18N("Print"), 0, 0, 0 },
+ { HP_MEDIA_SLIDE, SANE_I18N("Slide"), 0, 0, 0 },
+ { HP_MEDIA_NEGATIVE, SANE_I18N("Film-strip"), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s MEDIA[1] = {{
+ SCANNER_OPTION(MEDIA, STRING, NONE),
+ NO_REQUIRES,
+ _probe_choice, _program_media, 0,
+ 1, 1, 1, 1, 0, SCL_MEDIA, 0, 0, 0, _media_choices
+}};
+
+static const struct hp_choice_s _data_widths[] = {
+ {1, "1", 0, 0, 0},
+ {8, "8", 0, 0, 0},
+ {10, "10", 0, 0, 0},
+ {12, "12", 0, 0, 0},
+ {14, "14", 0, 0, 0},
+ {16, "16", 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static const struct hp_option_descriptor_s BIT_DEPTH[1] = {{
+ SCANNER_OPTION(BIT_DEPTH, STRING, NONE),
+ NO_REQUIRES,
+ _probe_choice, _program_data_width, _enable_data_width,
+ 1, 1, 1, 0, 1, SCL_DATA_WIDTH, 0, 0, 0, _data_widths
+}};
+
+static const struct hp_option_descriptor_s OUT8[1] =
+{
+ {
+ SCANNER_OPTION(OUTPUT_8BIT, BOOL, NONE),
+ NO_REQUIRES, /* enum hp_device_compat_e requires */
+ _probe_bool, /* SANE_Status (*probe)() */
+ 0, /* SANE_Status (*program)() */
+ _enable_out8, /* hp_bool_t (*enable)() */
+ 0, /* hp_bool_t has_global_effect */
+ 0, /* hp_bool_t affects_scan_params */
+ 0, /* hp_bool_t program_immediate */
+ 0, /* hp_bool_t suppress_for_scan */
+ 0, /* hp_bool_t may_change */
+ 0, /* HpScl scl_command */
+ 0, /* int minval */
+ 0, /* int maxval */
+ 0, /* int startval */
+ 0 /* HpChoice choices */
+ }
+};
+
+/* The 100% setting may cause problems within the scanner */
+static const struct hp_choice_s _ps_exposure_times[] = {
+ /* {0, "100%", 0, 0, 0}, */
+ { 0, SANE_I18N("Default"), 0, 0, 0 },
+ {1, "125%", 0, 0, 0},
+ {2, "150%", 0, 0, 0},
+ {3, "175%", 0, 0, 0},
+ {4, "200%", 0, 0, 0},
+ {5, "225%", 0, 0, 0},
+ {6, "250%", 0, 0, 0},
+ {7, "275%", 0, 0, 0},
+ {8, "300%", 0, 0, 0},
+ {9, SANE_I18N("Negative"), 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+/* Photosmart exposure time */
+static const struct hp_option_descriptor_s PS_EXPOSURE_TIME[1] = {{
+ SCANNER_OPTION(PS_EXPOSURE_TIME, STRING, NONE),
+ REQUIRES( HP_COMPAT_PS ),
+ _probe_ps_exposure_time, _program_ps_exposure_time, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, _ps_exposure_times
+}};
+
+/* Normal, ADF or XPA scanning. Because scanning from ADF can change */
+/* the extent of the scanning area, this option is marked to change */
+/* global settings. The user should switch to ADF scanning after */
+/* placing paper in the ADF. */
+static const struct hp_choice_s _scan_types[] = {
+ { HP_SCANTYPE_NORMAL, SANE_I18N("Normal"), 0, 0, 0 },
+ { HP_SCANTYPE_ADF, SANE_I18N("ADF"), 0, 0, 0 },
+ { HP_SCANTYPE_XPA, SANE_I18N("XPA"), 0, 0, 0 },
+ {0, 0, 0, 0, 0 }
+};
+
+static const struct hp_option_descriptor_s SCAN_SOURCE[1] = {{
+ SCANNER_OPTION(SCAN_SOURCE, STRING, NONE),
+ NO_REQUIRES,
+ _probe_scan_type, _program_scan_type, 0,
+ 1, 1, 1, 0, 0, SCL_START_SCAN, 0, 0, 0, _scan_types
+}};
+
+/* Unload after is only necessary for PhotoScanner */
+static const struct hp_option_descriptor_s UNLOAD_AFTER_SCAN[1] = {{
+ SCANNER_OPTION(UNLOAD_AFTER_SCAN, BOOL, NONE),
+ REQUIRES(HP_COMPAT_PS),
+ _probe_bool, _program_unload_after_scan, 0,
+ 0, 0, 0, 1, 0, SCL_UNLOAD, 0, 0, 0, 0
+}};
+
+static const struct hp_option_descriptor_s CHANGE_DOC[1] = {{
+ SCANNER_OPTION(CHANGE_DOC, BUTTON, NONE),
+ NO_REQUIRES,
+ _probe_change_doc, _program_change_doc, 0,
+ 1, 1, 1, 1, 0, SCL_CHANGE_DOC, 0, 0, 0, 0
+}};
+
+static const struct hp_option_descriptor_s UNLOAD[1] = {{
+ SCANNER_OPTION(UNLOAD, BUTTON, NONE),
+ NO_REQUIRES,
+ _probe_unload, _program_unload, 0,
+ 0, 0, 1, 1, 0, SCL_UNLOAD, 0, 0, 0, 0
+}};
+
+/* There is no inquire ID-for the calibrate command. */
+/* So here we need the requiries. */
+static const struct hp_option_descriptor_s CALIBRATE[1] = {{
+ SCANNER_OPTION(CALIBRATE, BUTTON, NONE),
+ REQUIRES(HP_COMPAT_PS),
+ _probe_calibrate, _program_calibrate, _enable_calibrate,
+ 0, 0, 1, 1, 0, SCL_CALIBRATE, 0, 0, 0, 0
+}};
+
+static const struct hp_option_descriptor_s GEOMETRY_GROUP[1] = {{
+ ADVANCED_GROUP(SANE_I18N("Geometry")),
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+static const struct hp_option_descriptor_s SCAN_TL_X[1] = {{
+ SCANNER_OPTION(SCAN_TL_X, FIXED, MM),
+ NO_REQUIRES,
+ _probe_geometry, _program_geometry, 0,
+ 0, 1, 0, 0, 1, SCL_X_POS, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s SCAN_TL_Y[1] = {{
+ SCANNER_OPTION(SCAN_TL_Y, FIXED, MM),
+ NO_REQUIRES,
+ _probe_geometry, _program_geometry, 0,
+ 0, 1, 0, 0, 1, SCL_Y_POS, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s SCAN_BR_X[1] = {{
+ SCANNER_OPTION(SCAN_BR_X, FIXED, MM),
+ NO_REQUIRES,
+ _probe_geometry, _program_geometry, 0,
+ 0, 1, 0, 0, 1, SCL_X_EXTENT, 0, 0, 0, 0
+}};
+static const struct hp_option_descriptor_s SCAN_BR_Y[1] = {{
+ SCANNER_OPTION(SCAN_BR_Y, FIXED, MM),
+ NO_REQUIRES,
+ _probe_geometry, _program_geometry, 0,
+ 0, 1, 0, 0, 1, SCL_Y_EXTENT, 0, 0, 0, 0
+}};
+
+static const struct hp_choice_s _mirror_horiz_choices[] = {
+ { HP_MIRROR_HORIZ_OFF, SANE_I18N("Off"), 0, 0, 0 },
+ { HP_MIRROR_HORIZ_ON, SANE_I18N("On"), 0, 0, 0 },
+ { HP_MIRROR_HORIZ_CONDITIONAL, SANE_I18N("Conditional"), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s MIRROR_HORIZ[1] = {{
+ SCANNER_OPTION(MIRROR_HORIZ, STRING, NONE),
+ NO_REQUIRES,
+ _probe_mirror_horiz, _program_mirror_horiz, 0,
+ 0, 0, 0, 0, 0, SCL_MIRROR_IMAGE, 0, 0, 0, _mirror_horiz_choices
+}};
+
+static const struct hp_choice_s _mirror_vert_choices[] = {
+ { HP_MIRROR_VERT_OFF, SANE_I18N("Off"), 0, 0, 0 },
+ { HP_MIRROR_VERT_ON, SANE_I18N("On"), 0, 0, 0 },
+ { HP_MIRROR_VERT_CONDITIONAL, SANE_I18N("Conditional"), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s MIRROR_VERT[1] = {{
+ SCANNER_OPTION(MIRROR_VERT, STRING, NONE),
+ NO_REQUIRES,
+ _probe_mirror_vert, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, _mirror_vert_choices
+}};
+
+static const struct hp_option_descriptor_s BUTTON_WAIT[1] =
+{
+ {
+ SCANNER_OPTION(BUTTON_WAIT, BOOL, NONE),
+ NO_REQUIRES, /* enum hp_device_compat_e requires */
+ _probe_front_button, /* SANE_Status (*probe)() */
+ 0, /* SANE_Status (*program)() */
+ 0, /* hp_bool_t (*enable)() */
+ 0, /* hp_bool_t has_global_effect */
+ 0, /* hp_bool_t affects_scan_params */
+ 0, /* hp_bool_t program_immediate */
+ 0, /* hp_bool_t suppress_for_scan */
+ 0, /* hp_bool_t may_change */
+ 0, /* HpScl scl_command */
+ 0, /* int minval */
+ 0, /* int maxval */
+ 0, /* int startval */
+ 0 /* HpChoice choices */
+ }
+};
+
+
+static const struct hp_option_descriptor_s LAMP_OFF[1] =
+{
+ {
+ SCANNER_OPTION(LAMP_OFF, BUTTON, NONE),
+ /* Lamp off instruction not supported by Photosmart */
+ REQUIRES( HP_COMPAT_PLUS | HP_COMPAT_2C | HP_COMPAT_2P | HP_COMPAT_2CX
+ | HP_COMPAT_4C | HP_COMPAT_3P | HP_COMPAT_4P | HP_COMPAT_5P
+ | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C
+ | HP_COMPAT_6300C), /* enum hp_device_compat_e requires */
+ _probe_bool, /* SANE_Status (*probe)() */
+ _program_lamp_off, /* SANE_Status (*program)() */
+ 0, /* hp_bool_t (*enable)() */
+ 0, /* hp_bool_t has_global_effect */
+ 0, /* hp_bool_t affects_scan_params */
+ 1, /* hp_bool_t program_immediate */
+ 1, /* hp_bool_t suppress_for_scan */
+ 0, /* hp_bool_t may_change */
+ SCL_LAMPTEST, /* HpScl scl_command */
+ 0, /* int minval */
+ 0, /* int maxval */
+ 0, /* int startval */
+ 0 /* HpChoice choices */
+ }
+};
+
+#ifdef HP_EXPERIMENTAL
+
+static const struct hp_choice_s _range_choices[] = {
+ { 0, "0", 0, 0, 0 },
+ { 1, "1", 0, 0, 0 },
+ { 2, "2", 0, 0, 0 },
+ { 3, "3", 0, 0, 0 },
+ { 4, "4", 0, 0, 0 },
+ { 5, "5", 0, 0, 0 },
+ { 6, "6", 0, 0, 0 },
+ { 7, "7", 0, 0, 0 },
+ { 8, "8", 0, 0, 0 },
+ { 9, "9", 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+static const struct hp_option_descriptor_s EXPERIMENT_GROUP[1] = {{
+ ADVANCED_GROUP(SANE_I18N("Experiment"))
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */
+}};
+static const struct hp_option_descriptor_s PROBE_10470[1] = {{
+ SCANNER_OPTION(10470, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_generic, 0,
+ 0, 0, 0, 0, 0, SCL_10470, 0, 0, 0, _range_choices
+}};
+static const struct hp_option_descriptor_s PROBE_10485[1] = {{
+ SCANNER_OPTION(10485, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_generic, 0,
+ 0, 0, 0, 0, 0, SCL_10485, 0, 0, 0, _range_choices
+}};
+static const struct hp_option_descriptor_s PROBE_10952[1] = {{
+ SCANNER_OPTION(10952, STRING, NONE),
+ NO_REQUIRES,
+ _probe_each_choice, _program_generic, 0,
+ 0, 0, 0, 0, 0, SCL_10952, 0, 0, 0, _range_choices
+}};
+static const struct hp_option_descriptor_s PROBE_10967[1] = {{
+ SCANNER_OPTION(10967, INT, NONE),
+ NO_REQUIRES,
+ _probe_int, _program_generic, 0,
+ 0, 0, 0, 0, 0, SCL_10967, 0, 0, 0, 0
+}};
+
+#endif
+
+static HpOptionDescriptor hp_options[] = {
+ NUM_OPTIONS,
+
+ SCAN_MODE_GROUP,
+ PREVIEW_MODE,
+ SCAN_MODE, SCAN_RESOLUTION, DEVPIX_RESOLUTION,
+
+ ENHANCEMENT_GROUP,
+ BRIGHTNESS, CONTRAST,
+#ifdef SCL_SHARPENING
+ SHARPENING,
+#endif
+ AUTO_THRESHOLD,
+
+ ADVANCED_GROUP,
+ CUSTOM_GAMMA,
+#ifdef ENABLE_7x12_TONEMAPS
+ GAMMA_VECTOR_7x12,
+ RGB_TONEMAP, GAMMA_VECTOR_R, GAMMA_VECTOR_G, GAMMA_VECTOR_B,
+#endif
+ GAMMA_VECTOR_8x8,
+
+ MATRIX_TYPE,
+#ifdef FAKE_COLORSEP_MATRIXES
+ SEPMATRIX,
+#endif
+#ifdef ENABLE_10BIT_MATRIXES
+ MATRIX_RGB10, /* FIXME: unsupported: MATRIX_GRAY10, */
+#endif
+ MATRIX_RGB,
+
+ HALFTONE_PATTERN,
+#ifdef ENABLE_16X16_DITHERS
+ HALFTONE_PATTERN_16x16, HORIZONTAL_DITHER_16x16,
+#endif
+ HALFTONE_PATTERN_8x8, HORIZONTAL_DITHER_8x8,
+
+ SCAN_SPEED, SMOOTHING, MEDIA, PS_EXPOSURE_TIME, BIT_DEPTH, OUT8,
+ SCAN_SOURCE, BUTTON_WAIT, LAMP_OFF, UNLOAD_AFTER_SCAN,
+ CHANGE_DOC, UNLOAD, CALIBRATE,
+
+ GEOMETRY_GROUP,
+ SCAN_TL_X, SCAN_TL_Y, SCAN_BR_X, SCAN_BR_Y,
+ MIRROR_HORIZ, MIRROR_VERT,
+
+#ifdef HP_EXPERIMENTAL
+
+ EXPERIMENT_GROUP,
+ PROBE_10470,
+ PROBE_10485,
+ PROBE_10952,
+ PROBE_10967,
+
+#endif
+
+ 0
+};
+
+
+
+/*
+ * class HpOptSet
+ */
+
+struct hp_optset_s
+{
+#define OPTION_LIST_MAX sizeof(hp_options)/sizeof(hp_options[0])
+ HpOption options[OPTION_LIST_MAX];
+ size_t num_sane_opts;
+ size_t num_opts;
+
+ /* Magic accessors to get coord in actual scan pixels: */
+ HpAccessor tl_x, tl_y, br_x, br_y;
+};
+
+static HpOption
+hp_optset_get (HpOptSet this, HpOptionDescriptor optd)
+{
+ HpOption * optp = this->options;
+ int i = this->num_opts;
+
+ while (i--)
+ {
+ if ((*optp)->descriptor == optd)
+ return *optp;
+ optp++;
+ }
+ return 0;
+}
+
+static HpOption
+hp_optset_getByIndex (HpOptSet this, int optnum)
+{
+ if ((optnum < 0) || (optnum >= (int)this->num_sane_opts))
+ return 0;
+ return this->options[optnum];
+}
+
+static HpOption
+hp_optset_getByName (HpOptSet this, const char * name)
+{
+ HpOption * optp = this->options;
+ int i = this->num_opts;
+
+ while (i--)
+ {
+ if (strcmp((*optp)->descriptor->name, name) == 0)
+ return *optp;
+ optp++;
+ }
+ return 0;
+}
+
+static _HpOption
+_hp_optset_get (HpOptSet this, HpOptionDescriptor opt)
+{
+ /* Cast away const-ness */
+ return (_HpOption) hp_optset_get(this, opt);
+}
+
+enum hp_scanmode_e
+sanei_hp_optset_scanmode (HpOptSet this, HpData data)
+{
+ HpOption mode = hp_optset_get(this, SCAN_MODE);
+ assert(mode);
+ return hp_option_getint(mode, data);
+}
+
+
+hp_bool_t
+sanei_hp_optset_output_8bit (HpOptSet this, HpData data)
+{
+ HpOption option_out8;
+ int out8;
+
+ option_out8 = hp_optset_get(this, OUT8);
+ if (option_out8)
+ {
+ out8 = hp_option_getint(option_out8, data);
+ return out8;
+ }
+ return 0;
+}
+
+
+/* Returns the data width that is send to the scanner, depending */
+/* on the scanmode. (b/w: 1, gray: 8..12, color: 24..36 */
+int
+sanei_hp_optset_data_width (HpOptSet this, HpData data)
+{
+ enum hp_scanmode_e mode = sanei_hp_optset_scanmode (this, data);
+ int datawidth = 0;
+ HpOption opt_dwidth;
+
+ switch (mode)
+ {
+ case HP_SCANMODE_LINEART:
+ case HP_SCANMODE_HALFTONE:
+ datawidth = 1;
+ break;
+
+ case HP_SCANMODE_GRAYSCALE:
+ opt_dwidth = hp_optset_get(this, BIT_DEPTH);
+ if (opt_dwidth)
+ datawidth = hp_option_getint (opt_dwidth, data);
+ else
+ datawidth = 8;
+ break;
+
+ case HP_SCANMODE_COLOR:
+ opt_dwidth = hp_optset_get(this, BIT_DEPTH);
+ if (opt_dwidth)
+ datawidth = 3 * hp_option_getint (opt_dwidth, data);
+ else
+ datawidth = 24;
+ break;
+ }
+ return datawidth;
+}
+
+hp_bool_t
+sanei_hp_optset_mirror_vert (HpOptSet this, HpData data, HpScsi scsi)
+{
+ HpOption mode;
+ int mirror, sec_dir;
+
+ mode = hp_optset_get(this, MIRROR_VERT);
+ assert(mode);
+ mirror = hp_option_getint(mode, data);
+
+ if (mirror == HP_MIRROR_VERT_CONDITIONAL)
+ {
+ mirror = HP_MIRROR_VERT_OFF;
+ if ( ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0)
+ == SANE_STATUS_GOOD ) && ( sec_dir == 1 ) )
+ mirror = HP_MIRROR_VERT_ON;
+ }
+ return mirror == HP_MIRROR_VERT_ON;
+}
+
+hp_bool_t sanei_hp_optset_start_wait(HpOptSet this, HpData data)
+{
+ HpOption mode;
+ int wait;
+
+ if ((mode = hp_optset_get(this, BUTTON_WAIT)) == 0)
+ return(0);
+
+ wait = hp_option_getint(mode, data);
+
+ return(wait);
+}
+
+HpScl
+sanei_hp_optset_scan_type (HpOptSet this, HpData data)
+{
+ HpOption mode;
+ HpScl scl = SCL_START_SCAN;
+ int scantype;
+
+ mode = hp_optset_get(this, SCAN_SOURCE);
+ if (mode)
+ {
+ scantype = hp_option_getint(mode, data);
+ DBG(5, "sanei_hp_optset_scan_type: scantype=%d\n", scantype);
+
+ switch (scantype)
+ {
+ case 1: scl = SCL_ADF_SCAN; break;
+ case 2: scl = SCL_XPA_SCAN; break;
+ default: scl = SCL_START_SCAN; break;
+ }
+ }
+ return scl;
+}
+
+static void
+hp_optset_add (HpOptSet this, HpOption opt)
+{
+ assert(this->num_opts < OPTION_LIST_MAX);
+
+ /*
+ * Keep internal options at the end of the list.
+ */
+ if (hp_option_isInternal(opt))
+ this->options[this->num_opts] = opt;
+ else
+ {
+ if (this->num_opts != this->num_sane_opts)
+ memmove(&this->options[this->num_sane_opts + 1],
+ &this->options[this->num_sane_opts],
+ ( (this->num_opts - this->num_sane_opts)
+ * sizeof(*this->options) ));
+ this->options[this->num_sane_opts++] = opt;
+ }
+ this->num_opts++;
+}
+
+static SANE_Status
+hp_optset_fix_geometry_options (HpOptSet this)
+{
+ _HpOption tl_x = _hp_optset_get(this, SCAN_TL_X);
+ _HpOption tl_y = _hp_optset_get(this, SCAN_TL_Y);
+ _HpOption br_x = _hp_optset_get(this, SCAN_BR_X);
+ _HpOption br_y = _hp_optset_get(this, SCAN_BR_Y);
+ HpOption scanres = hp_optset_get(this, SCAN_RESOLUTION);
+ HpOption devpix = hp_optset_get(this, DEVPIX_RESOLUTION);
+
+ HpAccessor tl_xa, tl_ya, br_xa, br_ya;
+
+ assert(tl_x && tl_y && br_x && br_y); /* Geometry options missing */
+
+ tl_xa = tl_x->data_acsr;
+ tl_ya = tl_y->data_acsr;
+ br_xa = br_x->data_acsr;
+ br_ya = br_y->data_acsr;
+
+ assert(tl_xa && tl_ya && br_xa && br_ya);
+ assert(scanres->data_acsr && devpix->data_acsr);
+
+ /* Magic accessors that will read out in device pixels */
+ tl_x->data_acsr = sanei_hp_accessor_geometry_new(tl_xa, br_xa, 0,
+ devpix->data_acsr);
+ tl_y->data_acsr = sanei_hp_accessor_geometry_new(tl_ya, br_ya, 0,
+ devpix->data_acsr);
+ br_x->data_acsr = sanei_hp_accessor_geometry_new(br_xa, tl_xa, 1,
+ devpix->data_acsr);
+ br_y->data_acsr = sanei_hp_accessor_geometry_new(br_ya, tl_ya, 1,
+ devpix->data_acsr);
+
+ if (!tl_x->data_acsr || !tl_y->data_acsr
+ || !br_x->data_acsr || !br_y->data_acsr)
+ return SANE_STATUS_NO_MEM;
+
+ /* Magic accessors that will read out in scan pixels */
+ this->tl_x = sanei_hp_accessor_geometry_new(tl_xa, br_xa, 0,
+ scanres->data_acsr);
+ this->tl_y = sanei_hp_accessor_geometry_new(tl_ya, br_ya, 0,
+ scanres->data_acsr);
+ this->br_x = sanei_hp_accessor_geometry_new(br_xa, tl_xa, 1,
+ scanres->data_acsr);
+ this->br_y = sanei_hp_accessor_geometry_new(br_ya, tl_ya, 1,
+ scanres->data_acsr);
+ if (!this->tl_x || !this->tl_y || !this->br_x || !this->br_y)
+ return SANE_STATUS_NO_MEM;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+hp_optset_reprogram (HpOptSet this, HpData data, HpScsi scsi)
+{
+ int i;
+
+ DBG(5, "hp_optset_reprogram: %lu options\n",
+ (unsigned long) this->num_opts);
+
+ for (i = 0; i < (int)this->num_opts; i++)
+ hp_option_reprogram(this->options[i], this, data, scsi);
+
+ DBG(5, "hp_optset_reprogram: finished\n");
+}
+
+static void
+hp_optset_reprobe (HpOptSet this, HpData data, HpScsi scsi)
+{
+ int i;
+
+ DBG(5, "hp_optset_reprobe: %lu options\n",
+ (unsigned long) this->num_opts);
+
+ for (i = 0; i < (int)this->num_opts; i++)
+ hp_option_reprobe(this->options[i], this, data, scsi);
+
+ DBG(5, "hp_optset_reprobe: finished\n");
+}
+
+static void
+hp_optset_updateEnables (HpOptSet this, HpData data, const HpDeviceInfo *info)
+{
+ int i;
+
+ DBG(5, "hp_optset_updateEnables: %lu options\n",
+ (unsigned long) this->num_opts);
+
+ for (i = 0; i < (int)this->num_opts; i++)
+ hp_option_updateEnable(this->options[i], this, data, info);
+}
+
+static hp_bool_t
+hp_optset_isEnabled (HpOptSet this, HpData data, const char *name,
+ const HpDeviceInfo *info)
+{
+ HpOption optpt;
+
+ optpt = hp_optset_getByName (this, name);
+
+ if (!optpt) /* Not found ? Not enabled */
+ return 0;
+
+ if (!(optpt->descriptor->enable)) /* No enable necessary ? Enabled */
+ return 1;
+
+ return (*optpt->descriptor->enable)(optpt, this, data, info);
+}
+
+/* This function is only called from sanei_hp_handle_startScan() */
+SANE_Status
+sanei_hp_optset_download (HpOptSet this, HpData data, HpScsi scsi)
+{
+ int i, errcount = 0;
+
+ DBG(3, "Start downloading parameters to scanner\n");
+
+ /* Reset scanner to wake it up */
+
+ /* Reset would switch off XPA lamp and switch on scanner lamp. */
+ /* Only do a reset if not in active XPA mode */
+ if ( (sanei_hp_optset_scan_type (this, data) != SCL_XPA_SCAN)
+ || (!sanei_hp_is_active_xpa (scsi)) )
+ {
+ RETURN_IF_FAIL(sanei_hp_scl_reset (scsi));
+ }
+ RETURN_IF_FAIL(sanei_hp_scl_clearErrors (scsi));
+
+ sanei_hp_device_simulate_clear ( sanei_hp_scsi_devicename (scsi) );
+
+ for (i = 0; i < (int)this->num_opts; i++)
+ {
+ if ( (this->options[i])->descriptor->suppress_for_scan )
+ {
+ DBG(3,"sanei_hp_optset_download: %s suppressed for scan\n",
+ (this->options[i])->descriptor->name);
+ }
+ else
+ {
+ RETURN_IF_FAIL( hp_option_program(this->options[i], scsi, this, data) );
+
+ if ( sanei_hp_scl_errcheck (scsi) != SANE_STATUS_GOOD )
+ {
+ errcount++;
+ DBG(3, "Option %s generated scanner error\n",
+ this->options[i]->descriptor->name);
+
+ RETURN_IF_FAIL(sanei_hp_scl_clearErrors (scsi));
+ }
+ }
+ }
+ DBG(3, "Downloading parameters finished.\n");
+
+ /* Check preview */
+ {HpOption option;
+ int is_preview, data_width;
+ const HpDeviceInfo *info;
+
+ option = hp_optset_getByName (this, SANE_NAME_PREVIEW);
+ if ( option )
+ {
+ is_preview = hp_option_getint (option, data);
+ if ( is_preview )
+ {
+ /* For preview we only use 8 bit per channel */
+
+ DBG(3, "sanei_hp_optset_download: Set up preview options\n");
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+
+ if (hp_optset_isEnabled (this, data, SANE_NAME_BIT_DEPTH, info))
+ {
+ data_width = sanei_hp_optset_data_width (this, data);
+ if (data_width > 24)
+ {
+ sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 24);
+ }
+ else if ((data_width > 8) && (data_width <= 16))
+ {
+ sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 8);
+ }
+ }
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_optset_new(HpOptSet * newp, HpScsi scsi, HpDevice dev)
+{
+ HpOptionDescriptor * ptr;
+ HpOptSet this = sanei_hp_allocz(sizeof(*this));
+ SANE_Status status;
+ HpOption option;
+ const HpDeviceInfo *info;
+
+ if (!this)
+ return SANE_STATUS_NO_MEM;
+
+ /* FIXME: more DBG's */
+ for (ptr = hp_options; *ptr; ptr++)
+ {
+ HpOptionDescriptor desc = *ptr;
+
+ DBG(8, "sanei_hp_optset_new: %s\n", desc->name);
+ if (desc->requires && !sanei_hp_device_compat(dev, desc->requires))
+ continue;
+ if (desc->type != SANE_TYPE_GROUP
+ && hp_optset_getByName(this, desc->name))
+ continue;
+
+ status = hp_option_descriptor_probe(desc, scsi, this,
+ dev->data, &option);
+ if (UNSUPPORTED(status))
+ continue;
+ if (FAILED(status))
+ {
+ DBG(1, "Option '%s': probe failed: %s\n", desc->name,
+ sane_strstatus(status));
+ sanei_hp_free(this);
+ return status;
+ }
+ hp_optset_add(this, option);
+ }
+
+ /* Set NUM_OPTIONS */
+ assert(this->options[0]->descriptor == NUM_OPTIONS);
+ sanei_hp_accessor_setint(this->options[0]->data_acsr, dev->data,
+ this->num_sane_opts);
+
+ /* Now for some kludges */
+ status = hp_optset_fix_geometry_options(this);
+ if (FAILED(status))
+ {
+ sanei_hp_free(this);
+ return status;
+ }
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ hp_optset_updateEnables(this, dev->data, info);
+
+ *newp = this;
+ return SANE_STATUS_GOOD;
+}
+
+hp_bool_t
+sanei_hp_optset_isImmediate (HpOptSet this, int optnum)
+{
+ HpOption opt = hp_optset_getByIndex(this, optnum);
+
+ if (!opt)
+ return 0;
+
+ return hp_option_isImmediate (opt);
+}
+
+SANE_Status
+sanei_hp_optset_control (HpOptSet this, HpData data,
+ int optnum, SANE_Action action,
+ void * valp, SANE_Int *infop, HpScsi scsi,
+ hp_bool_t immediate)
+{
+ HpOption opt = hp_optset_getByIndex(this, optnum);
+ SANE_Int my_info = 0, my_val = 0;
+
+ DBG(3,"sanei_hp_optset_control: %s\n", opt ? opt->descriptor->name : "");
+
+ if (infop)
+ *infop = 0;
+ else
+ infop = &my_info;
+
+ if (!opt)
+ return SANE_STATUS_INVAL;
+
+ /* There are problems with SANE_ACTION_GET_VALUE and valp == 0. */
+ /* Check if we really need valp. */
+ if ((action == SANE_ACTION_GET_VALUE) && (!valp))
+ {
+ /* Options without a value ? */
+ if ( (opt->descriptor->type == SANE_TYPE_BUTTON)
+ || (opt->descriptor->type == SANE_TYPE_GROUP))
+ {
+ valp = &my_val; /* Just simulate a return value locally. */
+ }
+ else /* Others must return a value. So this is invalid */
+ {
+ DBG(1, "sanei_hp_optset_control: get value, but valp == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (immediate)
+ RETURN_IF_FAIL( hp_option_imm_control(this, opt, data, action, valp, infop,
+ scsi) );
+ else
+ RETURN_IF_FAIL( hp_option_control(opt, data, action, valp, infop ) );
+
+ if ((*infop & SANE_INFO_RELOAD_OPTIONS) != 0)
+ {const HpDeviceInfo *info;
+
+ DBG(3,"sanei_hp_optset_control: reprobe\n");
+
+ /* At first we try to reprogram the parameters that may have changed */
+ /* by an option that had a global effect. This is necessary to */
+ /* specify options in an arbitrary order. Example: */
+ /* Changing scan mode resets scan resolution in the scanner. */
+ /* If resolution is set from the API before scan mode, we must */
+ /* reprogram the resolution afterwards. */
+ hp_optset_reprogram(this, data, scsi);
+ hp_optset_reprobe(this, data, scsi);
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ hp_optset_updateEnables(this, data, info);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_optset_guessParameters (HpOptSet this, HpData data,
+ SANE_Parameters * p)
+{
+ /* These are magic accessors which actually get the extent, not the
+ * absolute position... */
+ int xextent = sanei_hp_accessor_getint(this->br_x, data);
+ int yextent = sanei_hp_accessor_getint(this->br_y, data);
+ int data_width;
+
+ assert(xextent > 0 && yextent > 0);
+ p->last_frame = SANE_TRUE;
+ p->pixels_per_line = xextent;
+ p->lines = yextent;
+
+ switch (sanei_hp_optset_scanmode(this, data)) {
+ case HP_SCANMODE_LINEART: /* Lineart */
+ case HP_SCANMODE_HALFTONE: /* Halftone */
+ p->format = SANE_FRAME_GRAY;
+ p->depth = 1;
+ p->bytes_per_line = (p->pixels_per_line + 7) / 8;
+ break;
+ case HP_SCANMODE_GRAYSCALE: /* Grayscale */
+ p->format = SANE_FRAME_GRAY;
+ p->depth = 8;
+ p->bytes_per_line = p->pixels_per_line;
+ if ( !sanei_hp_optset_output_8bit (this, data) )
+ {
+ data_width = sanei_hp_optset_data_width (this, data);
+ if ( data_width > 8 )
+ {
+ p->depth *= 2;
+ p->bytes_per_line *= 2;
+ }
+ }
+ break;
+ case HP_SCANMODE_COLOR: /* RGB */
+ p->format = SANE_FRAME_RGB;
+ p->depth = 8;
+ p->bytes_per_line = 3 * p->pixels_per_line;
+ if ( !sanei_hp_optset_output_8bit (this, data) )
+ {
+ data_width = sanei_hp_optset_data_width (this, data);
+ if ( data_width > 24 )
+ {
+ p->depth *= 2;
+ p->bytes_per_line *= 2;
+ }
+ }
+ break;
+ default:
+ assert(!"Bad scan mode?");
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sanei_hp_optset_saneoption (HpOptSet this, HpData data, int optnum)
+{
+ HpOption opt = hp_optset_getByIndex(this, optnum);
+
+ if (!opt)
+ return 0;
+ return hp_option_saneoption(opt, data);
+}
diff --git a/backend/hp-option.h b/backend/hp-option.h
new file mode 100644
index 0000000..d1795e1
--- /dev/null
+++ b/backend/hp-option.h
@@ -0,0 +1,294 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/*
+ $Id$
+*/
+#ifndef HP_OPTION_H_INCLUDED
+#define HP_OPTION_H_INCLUDED
+#include "hp.h"
+
+/*
+ * Non-standard SANE options
+ *
+ * FIXME: this should become standard
+ */
+
+#ifndef SANE_NAME_SHARPENING
+# define SANE_NAME_SHARPENING "sharpening"
+# define SANE_TITLE_SHARPENING SANE_I18N("Sharpening")
+# define SANE_DESC_SHARPENING SANE_I18N("Set sharpening value.")
+#endif
+
+#ifndef SANE_NAME_AUTO_THRESHOLD
+# define SANE_NAME_AUTO_THRESHOLD "auto-threshold"
+# define SANE_TITLE_AUTO_THRESHOLD SANE_I18N("Auto Threshold")
+# define SANE_DESC_AUTO_THRESHOLD \
+ SANE_I18N("Enable automatic determination of threshold for line-art scans.")
+#endif
+
+#ifndef SANE_NAME_SMOOTHING
+# define SANE_NAME_SMOOTHING "smoothing"
+# define SANE_TITLE_SMOOTHING SANE_I18N("Smoothing")
+# define SANE_DESC_SMOOTHING SANE_I18N("Select smoothing filter.")
+#endif
+
+#ifndef SANE_NAME_UNLOAD_AFTER_SCAN
+# define SANE_NAME_UNLOAD_AFTER_SCAN "unload-after-scan"
+# define SANE_TITLE_UNLOAD_AFTER_SCAN SANE_I18N("Unload media after scan")
+# define SANE_DESC_UNLOAD_AFTER_SCAN SANE_I18N("Unloads the media after a scan.")
+#endif
+
+#ifndef SANE_NAME_CHANGE_DOC
+# define SANE_NAME_CHANGE_DOC "change-document"
+# define SANE_TITLE_CHANGE_DOC SANE_I18N("Change document")
+# define SANE_DESC_CHANGE_DOC SANE_I18N("Change Document.")
+#endif
+
+#ifndef SANE_NAME_UNLOAD
+# define SANE_NAME_UNLOAD "unload"
+# define SANE_TITLE_UNLOAD SANE_I18N("Unload")
+# define SANE_DESC_UNLOAD SANE_I18N("Unload Document.")
+#endif
+
+#ifndef SANE_NAME_CALIBRATE
+# define SANE_NAME_CALIBRATE "calibrate"
+# define SANE_TITLE_CALIBRATE SANE_I18N("Calibrate")
+# define SANE_DESC_CALIBRATE SANE_I18N("Start calibration process.")
+#endif
+
+#ifndef SANE_NAME_MEDIA
+# define SANE_NAME_MEDIA "media-type"
+# define SANE_TITLE_MEDIA SANE_I18N("Media")
+# define SANE_DESC_MEDIA SANE_I18N("Set type of media.")
+#endif
+
+#ifndef SANE_NAME_PS_EXPOSURE_TIME
+# define SANE_NAME_PS_EXPOSURE_TIME "ps-exposure-time"
+# define SANE_TITLE_PS_EXPOSURE_TIME SANE_I18N("Exposure time")
+# define SANE_DESC_PS_EXPOSURE_TIME \
+ SANE_I18N("A longer exposure time lets the scanner\
+ collect more light. Suggested use is 175% for prints,\
+ 150% for normal slides and \"Negative\" for\
+ negative film. For dark (underexposed) images you can increase this value.")
+#endif
+
+#ifndef SANE_NAME_MATRIX_TYPE
+# define SANE_NAME_MATRIX_TYPE "matrix-type"
+# define SANE_TITLE_MATRIX_TYPE SANE_I18N("Color Matrix")
+/* FIXME: better description */
+# define SANE_DESC_MATRIX_TYPE SANE_I18N("Set the scanners color matrix.")
+#endif
+
+#ifndef SANE_NAME_MATRIX_RGB
+# define SANE_NAME_MATRIX_RGB "matrix-rgb"
+# define SANE_TITLE_MATRIX_RGB SANE_I18N("Color Matrix")
+# define SANE_DESC_MATRIX_RGB SANE_I18N("Custom color matrix.")
+#endif
+
+#ifndef SANE_NAME_MATRIX_GRAY
+# define SANE_NAME_MATRIX_GRAY "matrix-gray"
+# define SANE_TITLE_MATRIX_GRAY SANE_I18N("Mono Color Matrix")
+# define SANE_DESC_MATRIX_GRAY SANE_I18N("Custom color matrix for grayscale scans.")
+#endif
+
+#ifndef SANE_NAME_MIRROR_HORIZ
+# define SANE_NAME_MIRROR_HORIZ "mirror-horizontal"
+# define SANE_TITLE_MIRROR_HORIZ SANE_I18N("Mirror horizontal")
+# define SANE_DESC_MIRROR_HORIZ SANE_I18N("Mirror image horizontally.")
+#endif
+
+#ifndef SANE_NAME_MIRROR_VERT
+# define SANE_NAME_MIRROR_VERT "mirror-vertical"
+# define SANE_TITLE_MIRROR_VERT SANE_I18N("Mirror vertical")
+# define SANE_DESC_MIRROR_VERT SANE_I18N("Mirror image vertically.")
+#endif
+
+#ifndef SANE_NAME_UPDATE
+# define SANE_NAME_UPDATE "update-options"
+# define SANE_TITLE_UPDATE SANE_I18N("Update options")
+# define SANE_DESC_UPDATE SANE_I18N("Update options.")
+#endif
+
+#ifndef SANE_NAME_OUTPUT_8BIT
+# define SANE_NAME_OUTPUT_8BIT "output-8bit"
+# define SANE_TITLE_OUTPUT_8BIT SANE_I18N("8 bit output")
+# define SANE_DESC_OUTPUT_8BIT \
+ SANE_I18N("Use bit depth greater eight internally,\
+ but output only eight bits.")
+#endif
+
+#ifndef SANE_NAME_BUTTON_WAIT
+# define SANE_NAME_BUTTON_WAIT "button-wait"
+# define SANE_TITLE_BUTTON_WAIT SANE_I18N("Front button wait")
+# define SANE_DESC_BUTTON_WAIT SANE_I18N("Wait to scan for front-panel button push.")
+# define HP_BUTTON_WAIT_NO 0
+# define HP_BUTTON_WAIT_YES 1
+#endif
+
+#ifndef SANE_NAME_LAMP_OFF
+# define SANE_NAME_LAMP_OFF "lamp-off"
+# define SANE_TITLE_LAMP_OFF SANE_I18N("Shut off lamp")
+# define SANE_DESC_LAMP_OFF SANE_I18N("Shut off scanner lamp.")
+# define HP_LAMP_OFF_NO 0
+# define HP_LAMP_OFF_YES 1
+#endif
+
+/* Some test stuff to see what undocumented SCL-commands do */
+# define SANE_NAME_10470 "10470"
+# define SANE_TITLE_10470 "10470"
+# define SANE_DESC_10470 "10470."
+
+# define SANE_NAME_10485 "10485"
+# define SANE_TITLE_10485 "10485"
+# define SANE_DESC_10485 "10485."
+
+# define SANE_NAME_10952 "10952"
+# define SANE_TITLE_10952 "10952"
+# define SANE_DESC_10952 "10952."
+
+# define SANE_NAME_10967 "10967"
+# define SANE_TITLE_10967 "10967"
+# define SANE_DESC_10967 "10967."
+
+/*
+ * Internal option names which are not presented to the SANE frontend.
+ */
+#define HP_NAME_HORIZONTAL_DITHER "__hdither__"
+#define HP_NAME_DEVPIX_RESOLUTION "__devpix_resolution__"
+#ifdef ENABLE_7x12_TONEMAPS
+# define HP_NAME_RGB_TONEMAP "__rgb_tonemap__"
+#endif
+#ifdef FAKE_COLORSEP_MATRIXES
+# define HP_NAME_SEPMATRIX "__sepmatrix__"
+#endif
+
+struct hp_choice_s
+{
+ int val;
+ const char *name;
+ hp_bool_t (*enable)(HpChoice this, HpOptSet optset, HpData data,
+ const HpDeviceInfo *info);
+ hp_bool_t is_emulated:1;
+ HpChoice next;
+};
+
+enum hp_scanmode_e
+{
+ HP_SCANMODE_LINEART = 0,
+ HP_SCANMODE_HALFTONE = 3,
+ HP_SCANMODE_GRAYSCALE = 4,
+ HP_SCANMODE_COLOR = 5
+};
+
+enum hp_scantype_e
+{
+ HP_SCANTYPE_NORMAL = 0,
+ HP_SCANTYPE_ADF = 1,
+ HP_SCANTYPE_XPA = 2
+};
+
+enum hp_dither_type_e {
+ HP_DITHER_CUSTOM = -1,
+ HP_DITHER_COARSE = 0,
+ HP_DITHER_FINE = 1,
+ HP_DITHER_BAYER = 2,
+ HP_DITHER_VERTICAL = 3,
+ HP_DITHER_HORIZONTAL
+};
+
+enum hp_matrix_type_e {
+ HP_MATRIX_AUTO = -256,
+ HP_MATRIX_GREEN = -257,
+ HP_MATRIX_CUSTOM_BW = -2,
+ HP_MATRIX_CUSTOM = -1,
+ HP_MATRIX_RGB = 0,
+ HP_MATRIX_BW = 1,
+ HP_MATRIX_PASS = 2,
+ HP_MATRIX_RED = 3,
+ HP_MATRIX_BLUE = 4,
+ HP_MATRIX_XPA_RGB = 5,
+ HP_MATRIX_XPA_BW = 6
+};
+
+enum hp_mirror_horiz_e {
+ HP_MIRROR_HORIZ_CONDITIONAL = -256,
+ HP_MIRROR_HORIZ_OFF = 0,
+ HP_MIRROR_HORIZ_ON = 1
+};
+
+enum hp_mirror_vert_e {
+ HP_MIRROR_VERT_OFF = -258,
+ HP_MIRROR_VERT_ON = -257,
+ HP_MIRROR_VERT_CONDITIONAL = -256
+};
+
+enum hp_media_e {
+ HP_MEDIA_NEGATIVE = 1,
+ HP_MEDIA_SLIDE = 2,
+ HP_MEDIA_PRINT = 3
+};
+
+hp_bool_t sanei_hp_choice_isEnabled (HpChoice this, HpOptSet optset,
+ HpData data, const HpDeviceInfo *info);
+
+SANE_Status sanei_hp_optset_new(HpOptSet * newp, HpScsi scsi, HpDevice dev);
+SANE_Status sanei_hp_optset_download (HpOptSet this, HpData data, HpScsi scsi);
+SANE_Status sanei_hp_optset_control (HpOptSet this, HpData data,
+ int optnum, SANE_Action action,
+ void * valp, SANE_Int *infop, HpScsi scsi,
+ hp_bool_t immediate);
+SANE_Status sanei_hp_optset_guessParameters (HpOptSet this, HpData data,
+ SANE_Parameters * p);
+enum hp_scanmode_e sanei_hp_optset_scanmode (HpOptSet this, HpData data);
+hp_bool_t sanei_hp_optset_output_8bit (HpOptSet this, HpData data);
+int sanei_hp_optset_data_width (HpOptSet this, HpData data);
+hp_bool_t sanei_hp_optset_isImmediate (HpOptSet this, int optnum);
+hp_bool_t sanei_hp_optset_mirror_vert (HpOptSet this, HpData data, HpScsi scsi);
+hp_bool_t sanei_hp_optset_start_wait(HpOptSet this, HpData data);
+HpScl sanei_hp_optset_scan_type (HpOptSet this, HpData data);
+const SANE_Option_Descriptor * sanei_hp_optset_saneoption (HpOptSet this,
+ HpData data, int optnum);
+
+#endif /* HP_OPTION_H_INCLUDED */
diff --git a/backend/hp-scl.c b/backend/hp-scl.c
new file mode 100644
index 0000000..e58508b
--- /dev/null
+++ b/backend/hp-scl.c
@@ -0,0 +1,2118 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/*
+ $Log$
+ Revision 1.15 2008/03/28 14:37:36 kitno-guest
+ add usleep to improve usb performance, from jim a t meyering d o t net
+
+ Revision 1.14 2004-10-04 18:09:05 kig-guest
+ Rename global function hp_init_openfd to sanei_hp_init_openfd
+
+ Revision 1.13 2004/03/27 13:52:39 kig-guest
+ Keep USB-connection open (was problem with Linux 2.6.x)
+
+ Revision 1.12 2003/10/09 19:34:57 kig-guest
+ Redo when TEST UNIT READY failed
+ Redo when read returns with 0 bytes (non-SCSI only)
+
+
+*/
+
+/*
+#define STUBS
+extern int sanei_debug_hp;*/
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+#include "../include/lalloca.h" /* Must be first */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "../include/lassert.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_pio.h"
+
+#include "hp.h"
+
+#include "../include/sane/sanei_backend.h"
+
+#include "hp-option.h"
+#include "hp-scsi.h"
+#include "hp-scl.h"
+#include "hp-device.h"
+
+#define HP_SCSI_INQ_LEN (36)
+#define HP_SCSI_CMD_LEN (6)
+#define HP_SCSI_BUFSIZ (HP_SCSI_MAX_WRITE + HP_SCSI_CMD_LEN)
+
+#define HP_MAX_OPEN_FD 16
+static struct hp_open_fd_s /* structure to save info about open file descriptor */
+{
+ char *devname;
+ HpConnect connect;
+ int fd;
+} asHpOpenFd[HP_MAX_OPEN_FD];
+
+
+/*
+ *
+ */
+struct hp_scsi_s
+{
+ int fd;
+ char * devname;
+
+ /* Output buffering */
+ hp_byte_t buf[HP_SCSI_BUFSIZ];
+ hp_byte_t * bufp;
+
+ hp_byte_t inq_data[HP_SCSI_INQ_LEN];
+};
+
+#define HP_TMP_BUF_SIZE (1024*4)
+#define HP_WR_BUF_SIZE (1024*4)
+
+typedef struct
+{
+ HpProcessData procdata;
+
+ int outfd;
+ const unsigned char *map;
+
+ unsigned char *image_buf; /* Buffer to store complete image (if req.) */
+ unsigned char *image_ptr;
+ int image_buf_size;
+
+ unsigned char *tmp_buf; /* Buffer for scan data to get even number of bytes */
+ int tmp_buf_size;
+ int tmp_buf_len;
+
+ unsigned char wr_buf[HP_WR_BUF_SIZE];
+ unsigned char *wr_ptr;
+ int wr_buf_size;
+ int wr_left;
+} PROCDATA_HANDLE;
+
+
+/* Initialize structure where we remember out open file descriptors */
+void
+sanei_hp_init_openfd ()
+{int iCount;
+ memset (asHpOpenFd, 0, sizeof (asHpOpenFd));
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ asHpOpenFd[iCount].fd = -1;
+}
+
+
+/* Look if the device is still open */
+static SANE_Status
+hp_GetOpenDevice (const char *devname, HpConnect connect, int *pfd)
+
+{int iCount;
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if (!asHpOpenFd[iCount].devname) continue;
+ if ( (strcmp (asHpOpenFd[iCount].devname, devname) == 0)
+ && (asHpOpenFd[iCount].connect == connect) )
+ {
+ if (pfd) *pfd = asHpOpenFd[iCount].fd;
+ DBG(3, "hp_GetOpenDevice: device %s is open with fd=%d\n", devname,
+ asHpOpenFd[iCount].fd);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_GetOpenDevice: device %s not open\n", devname);
+ return SANE_STATUS_INVAL;
+}
+
+/* Add an open file descriptor. This also decides */
+/* if we keep a connection open or not. */
+static SANE_Status
+hp_AddOpenDevice (const char *devname, HpConnect connect, int fd)
+
+{int iCount, iKeepOpen;
+ static int iInitKeepFlags = 1;
+
+ /* The default values which connections to keep open or not */
+ static int iKeepOpenSCSI = 0;
+ static int iKeepOpenUSB = 1;
+ static int iKeepOpenDevice = 0;
+ static int iKeepOpenPIO = 0;
+
+ if (iInitKeepFlags) /* Change the defaults by environment */
+ {char *eptr;
+
+ iInitKeepFlags = 0;
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_SCSI");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenSCSI = (*eptr == '1');
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_USB");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenUSB = (*eptr == '1');
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_DEVICE");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenDevice = (*eptr == '1');
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_PIO");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenPIO = (*eptr == '1');
+ }
+
+ /* Look if we should keep it open or not */
+ iKeepOpen = 0;
+ switch (connect)
+ {
+ case HP_CONNECT_SCSI: iKeepOpen = iKeepOpenSCSI;
+ break;
+ case HP_CONNECT_PIO : iKeepOpen = iKeepOpenPIO;
+ break;
+ case HP_CONNECT_USB : iKeepOpen = iKeepOpenUSB;
+ break;
+ case HP_CONNECT_DEVICE : iKeepOpen = iKeepOpenDevice;
+ break;
+ case HP_CONNECT_RESERVE:
+ break;
+ }
+ if (!iKeepOpen)
+ {
+ DBG(3, "hp_AddOpenDevice: %s should not be kept open\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if (!asHpOpenFd[iCount].devname) /* Is this entry free ? */
+ {
+ asHpOpenFd[iCount].devname = sanei_hp_strdup (devname);
+ if (!asHpOpenFd[iCount].devname) return SANE_STATUS_NO_MEM;
+ DBG(3, "hp_AddOpenDevice: added device %s with fd=%d\n", devname, fd);
+ asHpOpenFd[iCount].connect = connect;
+ asHpOpenFd[iCount].fd = fd;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_AddOpenDevice: %s not added\n", devname);
+ return SANE_STATUS_NO_MEM;
+}
+
+
+/* Check if we have remembered an open file descriptor */
+static SANE_Status
+hp_IsOpenFd (int fd, HpConnect connect)
+
+{int iCount;
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if ( (asHpOpenFd[iCount].devname != NULL)
+ && (asHpOpenFd[iCount].fd == fd)
+ && (asHpOpenFd[iCount].connect == connect) )
+ {
+ DBG(3, "hp_IsOpenFd: %d is open\n", fd);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_IsOpenFd: %d not open\n", fd);
+ return SANE_STATUS_INVAL;
+}
+
+
+static SANE_Status
+hp_RemoveOpenFd (int fd, HpConnect connect)
+
+{int iCount;
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if ( (asHpOpenFd[iCount].devname != NULL)
+ && (asHpOpenFd[iCount].fd == fd)
+ && (asHpOpenFd[iCount].connect == connect) )
+ {
+ sanei_hp_free (asHpOpenFd[iCount].devname);
+ asHpOpenFd[iCount].devname = NULL;
+ DBG(3, "hp_RemoveOpenFd: removed %d\n", asHpOpenFd[iCount].fd);
+ asHpOpenFd[iCount].fd = -1;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_RemoveOpenFd: %d not removed\n", fd);
+ return SANE_STATUS_INVAL;
+}
+
+
+static SANE_Status
+hp_nonscsi_write (HpScsi this, hp_byte_t *data, size_t len, HpConnect connect)
+
+{int n = -1;
+ size_t loc_len;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (len <= 0) return SANE_STATUS_GOOD;
+
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE: /* direct device-io */
+ n = write (this->fd, data, len);
+ break;
+
+ case HP_CONNECT_PIO: /* Use sanepio interface */
+ n = sanei_pio_write (this->fd, data, len);
+ break;
+
+ case HP_CONNECT_USB: /* Not supported */
+ loc_len = len;
+ status = sanei_usb_write_bulk ((SANE_Int)this->fd, data, &loc_len);
+ n = loc_len;
+ break;
+
+ case HP_CONNECT_RESERVE:
+ n = -1;
+ break;
+
+ default:
+ n = -1;
+ break;
+ }
+
+ if (n == 0) return SANE_STATUS_EOF;
+ else if (n < 0) return SANE_STATUS_IO_ERROR;
+
+ return status;
+}
+
+static SANE_Status
+hp_nonscsi_read (HpScsi this, hp_byte_t *data, size_t *len, HpConnect connect,
+ int UNUSEDARG isResponse)
+
+{int n = -1;
+ static int retries = -1;
+ size_t save_len = *len;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (*len <= 0) return SANE_STATUS_GOOD;
+
+ if (retries < 0) /* Read environment */
+ {char *eptr = getenv ("SANE_HP_RDREDO");
+
+ retries = 1; /* Set default value */
+ if (eptr != NULL)
+ {
+ if (sscanf (eptr, "%d", &retries) != 1) retries = 1; /* Restore default */
+ else if (retries < 0) retries = 0; /* Allow no retries here */
+ }
+ }
+
+ for (;;) /* Retry on EOF */
+ {
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE:
+ n = read (this->fd, data, *len);
+ break;
+
+ case HP_CONNECT_PIO:
+ n = sanei_pio_read (this->fd, data, *len);
+ break;
+
+ case HP_CONNECT_USB:
+ status = sanei_usb_read_bulk((SANE_Int)this->fd, (SANE_Byte *)data, len);
+ n = *len;
+ break;
+
+ case HP_CONNECT_RESERVE:
+ n = -1;
+ break;
+
+ default:
+ n = -1;
+ break;
+ }
+ if ((n != 0) || (retries <= 0)) break;
+ retries--;
+ usleep (100*1000); /* sleep 0.1 seconds */
+ *len = save_len; /* Restore value */
+ }
+
+ if (n == 0) return SANE_STATUS_EOF;
+ else if (n < 0) return SANE_STATUS_IO_ERROR;
+
+ *len = n;
+ return status;
+}
+
+static SANE_Status
+hp_nonscsi_open (const char *devname, int *fd, HpConnect connect)
+
+{int lfd, flags;
+ SANE_Int dn;
+ SANE_Status status = SANE_STATUS_INVAL;
+
+#ifdef _O_RDWR
+ flags = _O_RDWR;
+#else
+ flags = O_RDWR;
+#endif
+#ifdef _O_EXCL
+ flags |= _O_EXCL;
+#else
+ flags |= O_EXCL;
+#endif
+#ifdef _O_BINARY
+ flags |= _O_BINARY;
+#endif
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE:
+ lfd = open (devname, flags);
+ if (lfd < 0)
+ {
+ DBG(1, "hp_nonscsi_open: open device %s failed (%s)\n", devname,
+ strerror (errno) );
+ status = (errno == EACCES) ? SANE_STATUS_ACCESS_DENIED : SANE_STATUS_INVAL;
+ }
+ else
+ status = SANE_STATUS_GOOD;
+ break;
+
+ case HP_CONNECT_PIO:
+ status = sanei_pio_open (devname, &lfd);
+ break;
+
+ case HP_CONNECT_USB:
+ DBG(17, "hp_nonscsi_open: open usb with \"%s\"\n", devname);
+ status = sanei_usb_open (devname, &dn);
+ lfd = (int)dn;
+ break;
+
+ case HP_CONNECT_RESERVE:
+ status = SANE_STATUS_INVAL;
+ break;
+
+ default:
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "hp_nonscsi_open: open device %s failed\n", devname);
+ }
+ else
+ {
+ DBG(17,"hp_nonscsi_open: device %s opened, fd=%d\n", devname, lfd);
+ }
+
+ if (fd) *fd = lfd;
+
+ return status;
+}
+
+static void
+hp_nonscsi_close (int fd, HpConnect connect)
+
+{
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE:
+ close (fd);
+ break;
+
+ case HP_CONNECT_PIO:
+ sanei_pio_close (fd);
+ break;
+
+ case HP_CONNECT_USB:
+ sanei_usb_close (fd);
+ break;
+
+ case HP_CONNECT_RESERVE:
+ break;
+
+ default:
+ break;
+ }
+ DBG(17,"hp_nonscsi_close: closed fd=%d\n", fd);
+}
+
+SANE_Status
+sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect)
+{
+ HpScsi new;
+ SANE_Status status;
+ int iAlreadyOpen = 0;
+
+ new = sanei_hp_allocz(sizeof(*new));
+ if (!new)
+ return SANE_STATUS_NO_MEM;
+
+ /* Is the device already open ? */
+ if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD )
+ {
+ iAlreadyOpen = 1;
+ }
+ else
+ {
+ status = hp_nonscsi_open(devname, &new->fd, connect);
+ if (FAILED(status))
+ {
+ DBG(1, "nonscsi_new: open failed (%s)\n", sane_strstatus(status));
+ sanei_hp_free(new);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* For SCSI-devices we would have the inquire command here */
+ strncpy ((char *)new->inq_data, "\003zzzzzzzHP ------ R000",
+ sizeof (new->inq_data));
+
+ new->bufp = new->buf + HP_SCSI_CMD_LEN;
+ new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
+ if ( new->devname ) strcpy (new->devname, devname);
+
+ *newp = new;
+
+ /* Remember the open device */
+ if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+hp_scsi_close (HpScsi this, int completely)
+{HpConnect connect;
+
+ DBG(3, "scsi_close: closing fd %ld\n", (long)this->fd);
+
+ connect = sanei_hp_scsi_get_connect (this);
+
+ if (!completely) /* May we keep the device open ? */
+ {
+ if ( hp_IsOpenFd (this->fd, connect) == SANE_STATUS_GOOD )
+ {
+ DBG(3, "scsi_close: not closing. Keep open\n");
+ return;
+ }
+
+ }
+ assert(this->fd >= 0);
+
+ if (connect != HP_CONNECT_SCSI)
+ hp_nonscsi_close (this->fd, connect);
+ else
+ sanei_scsi_close (this->fd);
+
+ DBG(3,"scsi_close: really closed\n");
+
+ /* Remove a remembered open device */
+ hp_RemoveOpenFd (this->fd, connect);
+}
+
+
+SANE_Status
+sanei_hp_scsi_new (HpScsi * newp, const char * devname)
+{
+ static hp_byte_t inq_cmd[] = { 0x12, 0, 0, 0, HP_SCSI_INQ_LEN, 0};
+ static hp_byte_t tur_cmd[] = { 0x00, 0, 0, 0, 0, 0};
+ size_t inq_len = HP_SCSI_INQ_LEN;
+ HpScsi new;
+ HpConnect connect;
+ SANE_Status status;
+ int iAlreadyOpen = 0;
+
+ connect = sanei_hp_get_connect (devname);
+
+ if (connect != HP_CONNECT_SCSI)
+ return sanei_hp_nonscsi_new (newp, devname, connect);
+
+ new = sanei_hp_allocz(sizeof(*new));
+ if (!new)
+ return SANE_STATUS_NO_MEM;
+
+ /* Is the device still open ? */
+ if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD )
+ {
+ iAlreadyOpen = 1;
+ }
+ else
+ {
+ status = sanei_scsi_open(devname, &new->fd, 0, 0);
+ if (FAILED(status))
+ {
+ DBG(1, "scsi_new: open failed (%s)\n", sane_strstatus(status));
+ sanei_hp_free(new);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ DBG(3, "scsi_inquire: sending INQUIRE\n");
+ status = sanei_scsi_cmd(new->fd, inq_cmd, 6, new->inq_data, &inq_len);
+ if (FAILED(status))
+ {
+ DBG(1, "scsi_inquire: inquiry failed: %s\n", sane_strstatus(status));
+ sanei_scsi_close(new->fd);
+ sanei_hp_free(new);
+ return status;
+ }
+
+ {char vendor[9], model[17], rev[5];
+ memset (vendor, 0, sizeof (vendor));
+ memset (model, 0, sizeof (model));
+ memset (rev, 0, sizeof (rev));
+ memcpy (vendor, new->inq_data + 8, 8);
+ memcpy (model, new->inq_data + 16, 16);
+ memcpy (rev, new->inq_data + 32, 4);
+
+ DBG(3, "vendor=%s, model=%s, rev=%s\n", vendor, model, rev);
+ }
+
+ DBG(3, "scsi_new: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0);
+ if (FAILED(status))
+ {
+ DBG(1, "hp_scsi_open: test unit ready failed (%s)\n",
+ sane_strstatus(status));
+ usleep (500*1000); /* Wait 0.5 seconds */
+ DBG(3, "scsi_new: sending TEST_UNIT_READY second time\n");
+ status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0);
+ }
+
+ if (FAILED(status))
+ {
+ DBG(1, "hp_scsi_open: test unit ready failed (%s)\n",
+ sane_strstatus(status));
+
+ sanei_scsi_close(new->fd);
+ sanei_hp_free(new);
+ return status; /* Fix problem with non-scanner devices */
+ }
+
+ new->bufp = new->buf + HP_SCSI_CMD_LEN;
+ new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
+ if ( new->devname ) strcpy (new->devname, devname);
+
+ *newp = new;
+
+ /* Remember the open device */
+ if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* The "completely" parameter was added for OfficeJet support.
+ * For JetDirect connections, closing and re-opening the scan
+ * channel is very time consuming. Also, the OfficeJet G85
+ * unloads a loaded document in the ADF when the scan channel
+ * gets closed. The solution is to "completely" destroy the
+ * connection, including closing and deallocating the PTAL
+ * channel, when initially probing the device in hp-device.c,
+ * but leave it open while the frontend is actually using the
+ * device (from hp-handle.c), and "completely" destroy it when
+ * the frontend closes its handle. */
+void
+sanei_hp_scsi_destroy (HpScsi this,int completely)
+{
+ /* Moved to hp_scsi_close():
+ * assert(this->fd >= 0);
+ * DBG(3, "scsi_close: closing fd %d\n", this->fd);
+ */
+
+ hp_scsi_close (this, completely);
+ if ( this->devname ) sanei_hp_free (this->devname);
+ sanei_hp_free(this);
+}
+
+hp_byte_t *
+sanei_hp_scsi_inq (HpScsi this)
+{
+ return this->inq_data;
+}
+
+const char *
+sanei_hp_scsi_vendor (HpScsi this)
+{
+ static char buf[9];
+ memcpy(buf, sanei_hp_scsi_inq(this) + 8, 8);
+ buf[8] = '\0';
+ return buf;
+}
+
+const char *
+sanei_hp_scsi_model (HpScsi this)
+{
+
+ static char buf[17];
+ memcpy(buf, sanei_hp_scsi_inq(this) + 16, 16);
+ buf[16] = '\0';
+ return buf;
+}
+
+const char *
+sanei_hp_scsi_devicename (HpScsi this)
+{
+ return this->devname;
+}
+
+hp_bool_t
+sanei_hp_is_active_xpa (HpScsi scsi)
+{HpDeviceInfo *info;
+ int model_num;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ if (info->active_xpa < 0)
+ {
+ model_num = sanei_hp_get_max_model (scsi);
+ info->active_xpa = (model_num >= 17);
+ DBG(5,"sanei_hp_is_active_xpa: model=%d, active_xpa=%d\n",
+ model_num, info->active_xpa);
+ }
+ return info->active_xpa;
+}
+
+int
+sanei_hp_get_max_model (HpScsi scsi)
+
+{HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ if (info->max_model < 0)
+ {enum hp_device_compat_e compat;
+ int model_num;
+
+ if ( sanei_hp_device_probe_model ( &compat, scsi, &model_num, 0)
+ == SANE_STATUS_GOOD )
+ info->max_model = model_num;
+ }
+ return info->max_model;
+}
+
+
+int
+sanei_hp_is_flatbed_adf (HpScsi scsi)
+
+{int model = sanei_hp_get_max_model (scsi);
+
+ return ((model == 2) || (model == 4) || (model == 5) || (model == 8));
+}
+
+
+HpConnect
+sanei_hp_get_connect (const char *devname)
+
+{const HpDeviceInfo *info;
+ HpConnect connect = HP_CONNECT_SCSI;
+ int got_connect_type = 0;
+
+ info = sanei_hp_device_info_get (devname);
+ if (!info)
+ {
+ DBG(1, "sanei_hp_get_connect: Could not get info for %s. Assume SCSI\n",
+ devname);
+ connect = HP_CONNECT_SCSI;
+ }
+ else
+ if ( !(info->config_is_up) )
+ {
+ DBG(1, "sanei_hp_get_connect: Config not initialized for %s. Assume SCSI\n",
+ devname);
+ connect = HP_CONNECT_SCSI;
+ }
+ else
+ {
+ connect = info->config.connect;
+ got_connect_type = info->config.got_connect_type;
+ }
+
+ /* Beware of using a USB-device as a SCSI-device (not 100% perfect) */
+ if ((connect == HP_CONNECT_SCSI) && !got_connect_type)
+ {int maybe_usb;
+
+ maybe_usb = ( strstr (devname, "usb")
+ || strstr (devname, "uscanner")
+ || strstr (devname, "ugen"));
+ if (maybe_usb)
+ {static int print_warning = 1;
+
+ if (print_warning)
+ {
+ print_warning = 0;
+ DBG(1,"sanei_hp_get_connect: WARNING\n");
+ DBG(1," Device %s assumed to be SCSI, but device name\n",devname);
+ DBG(1," looks like USB. Will continue with USB.\n");
+ DBG(1," If you really want it as SCSI, add the following\n");
+ DBG(1," to your file .../etc/sane.d/hp.conf:\n");
+ DBG(1," %s\n", devname);
+ DBG(1," option connect-scsi\n");
+ DBG(1," The same warning applies to other device names containing\n");
+ DBG(1," \"usb\", \"uscanner\" or \"ugen\".\n");
+ }
+ connect = HP_CONNECT_DEVICE;
+ }
+ }
+ return connect;
+}
+
+HpConnect
+sanei_hp_scsi_get_connect (HpScsi this)
+
+{
+ return sanei_hp_get_connect (sanei_hp_scsi_devicename (this));
+}
+
+
+static SANE_Status
+hp_scsi_flush (HpScsi this)
+{
+ hp_byte_t * data = this->buf + HP_SCSI_CMD_LEN;
+ size_t len = this->bufp - data;
+ HpConnect connect;
+
+ assert(len < HP_SCSI_MAX_WRITE);
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ this->bufp = this->buf;
+
+ DBG(16, "scsi_flush: writing %lu bytes:\n", (unsigned long) len);
+ DBGDUMP(16, data, len);
+
+ *this->bufp++ = 0x0A;
+ *this->bufp++ = 0;
+ *this->bufp++ = len >> 16;
+ *this->bufp++ = len >> 8;
+ *this->bufp++ = len;
+ *this->bufp++ = 0;
+
+ connect = sanei_hp_scsi_get_connect (this);
+ if (connect == HP_CONNECT_SCSI)
+ return sanei_scsi_cmd (this->fd, this->buf, HP_SCSI_CMD_LEN + len, 0, 0);
+ else
+ return hp_nonscsi_write (this, this->buf+HP_SCSI_CMD_LEN, len, connect);
+}
+
+static size_t
+hp_scsi_room (HpScsi this)
+{
+ return this->buf + HP_SCSI_BUFSIZ - this->bufp;
+}
+
+static SANE_Status
+hp_scsi_need (HpScsi this, size_t need)
+{
+ assert(need < HP_SCSI_MAX_WRITE);
+
+ if (need > hp_scsi_room(this))
+ RETURN_IF_FAIL( hp_scsi_flush(this) );
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_scsi_write (HpScsi this, const void *data, size_t len)
+{
+ if ( len < HP_SCSI_MAX_WRITE )
+ {
+ RETURN_IF_FAIL( hp_scsi_need(this, len) );
+ memcpy(this->bufp, data, len);
+ this->bufp += len;
+ }
+ else
+ {size_t maxwrite = HP_SCSI_MAX_WRITE - 16;
+ const char *c_data = (const char *)data;
+
+ while ( len > 0 )
+ {
+ if ( maxwrite > len ) maxwrite = len;
+ RETURN_IF_FAIL( hp_scsi_write(this, c_data, maxwrite) );
+ c_data += maxwrite;
+ len -= maxwrite;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_scsi_scl(HpScsi this, HpScl scl, int val)
+{
+ char group = tolower(SCL_GROUP_CHAR(scl));
+ char param = toupper(SCL_PARAM_CHAR(scl));
+ int count;
+
+ assert(IS_SCL_CONTROL(scl) || IS_SCL_COMMAND(scl));
+ assert(isprint(group) && isprint(param));
+
+ RETURN_IF_FAIL( hp_scsi_need(this, 10) );
+
+ /* Dont try to optimize SCL-commands like using <ESC>*a1b0c5T */
+ /* Some scanners have problems with it (e.g. HP Photosmart Photoscanner */
+ /* with window position/extent, resolution) */
+ count = sprintf((char *)this->bufp, "\033*%c%d%c", group, val, param);
+ this->bufp += count;
+
+ assert(count > 0 && this->bufp < this->buf + HP_SCSI_BUFSIZ);
+
+ return hp_scsi_flush(this);
+}
+
+/* Read it bytewise */
+static SANE_Status
+hp_scsi_read_slow (HpScsi this, void * dest, size_t *len)
+{static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
+ size_t leftover = *len;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char *start_dest = (unsigned char *)dest;
+ unsigned char *next_dest = start_dest;
+
+ DBG(16, "hp_scsi_read_slow: Start reading %d bytes bytewise\n", (int)*len);
+
+ while (leftover > 0) /* Until we got all the bytes */
+ {size_t one = 1;
+
+ read_cmd[2] = 0;
+ read_cmd[3] = 0;
+ read_cmd[4] = 1; /* Read one byte */
+
+ status = sanei_scsi_cmd (this->fd, read_cmd, sizeof(read_cmd),
+ next_dest, &one);
+ if ((status != SANE_STATUS_GOOD) || (one != 1))
+ {
+ DBG(250,"hp_scsi_read_slow: Reading byte %d: status=%s, len=%d\n",
+ (int)(next_dest-start_dest), sane_strstatus(status), (int)one);
+ }
+
+ if (status != SANE_STATUS_GOOD) break; /* Finish on error */
+
+ next_dest++;
+ leftover--;
+ }
+
+ *len = next_dest-start_dest; /* This is the number of bytes we got */
+
+ DBG(16, "hp_scsi_read_slow: Got %d bytes\n", (int)*len);
+
+ if ((status != SANE_STATUS_GOOD) && (*len > 0))
+ {
+ DBG(16, "We got some data. Ignore the error \"%s\"\n",
+ sane_strstatus(status));
+ status = SANE_STATUS_GOOD;
+ }
+ return status;
+}
+
+/* The OfficeJets tend to return inquiry responses containing array
+ * data in two packets. The added "isResponse" parameter tells
+ * whether we should keep reading until we get
+ * a well-formed response. Naturally, this parameter would be zero
+ * when reading scan data. */
+static SANE_Status
+hp_scsi_read (HpScsi this, void * dest, size_t *len, int isResponse)
+{
+ HpConnect connect;
+
+ RETURN_IF_FAIL( hp_scsi_flush(this) );
+
+ connect = sanei_hp_scsi_get_connect (this);
+ if (connect == HP_CONNECT_SCSI)
+ {int read_bytewise = 0;
+
+ if (*len <= 32) /* Is it a candidate for reading bytewise ? */
+ {const HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get (sanei_hp_scsi_devicename (this));
+ if ((info != NULL) && (info->config_is_up) && info->config.dumb_read)
+ read_bytewise = 1;
+ }
+
+ if ( ! read_bytewise )
+ {static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
+ read_cmd[2] = *len >> 16;
+ read_cmd[3] = *len >> 8;
+ read_cmd[4] = *len;
+
+ RETURN_IF_FAIL( sanei_scsi_cmd (this->fd, read_cmd,
+ sizeof(read_cmd), dest, len) );
+ }
+ else
+ {
+ RETURN_IF_FAIL (hp_scsi_read_slow (this, dest, len));
+ }
+ }
+ else
+ {
+ RETURN_IF_FAIL( hp_nonscsi_read (this, dest, len, connect, isResponse) );
+ }
+ DBG(16, "scsi_read: %lu bytes:\n", (unsigned long) *len);
+ DBGDUMP(16, dest, *len);
+ return SANE_STATUS_GOOD;
+}
+
+
+static int signal_caught = 0;
+
+static RETSIGTYPE
+signal_catcher (int sig)
+{
+ DBG(1,"signal_catcher(sig=%d): old signal_caught=%d\n",sig,signal_caught);
+ if (!signal_caught)
+ signal_caught = sig;
+}
+
+static void
+hp_data_map (register const unsigned char *map, register int count,
+ register unsigned char *data)
+{
+ if (count <= 0) return;
+ while (count--)
+ {
+ *data = map[*data];
+ data++;
+ }
+}
+
+static const unsigned char *
+hp_get_simulation_map (const char *devname, const HpDeviceInfo *info)
+{
+ hp_bool_t sim_gamma, sim_brightness, sim_contrast;
+ int k, ind;
+ const unsigned char *map = NULL;
+ static unsigned char map8x8[256];
+
+ sim_gamma = info->simulate.gamma_simulate;
+ sim_brightness = sanei_hp_device_simulate_get (devname, SCL_BRIGHTNESS);
+ sim_contrast = sanei_hp_device_simulate_get (devname, SCL_CONTRAST);
+
+ if ( sim_gamma )
+ {
+ map = &(info->simulate.gamma_map[0]);
+ }
+ else if ( sim_brightness && sim_contrast )
+ {
+ for (k = 0; k < 256; k++)
+ {
+ ind = info->simulate.contrast_map[k];
+ map8x8[k] = info->simulate.brightness_map[ind];
+ }
+ map = &(map8x8[0]);
+ }
+ else if ( sim_brightness )
+ map = &(info->simulate.brightness_map[0]);
+ else if ( sim_contrast )
+ map = &(info->simulate.contrast_map[0]);
+
+ return map;
+}
+
+
+/* Check the native byte order on the local machine */
+static hp_bool_t
+is_lowbyte_first_byteorder (void)
+
+{unsigned short testvar = 1;
+ unsigned char *testptr = (unsigned char *)&testvar;
+
+ if (sizeof (unsigned short) == 2)
+ return (testptr[0] == 1);
+ else if (sizeof (unsigned short) == 4)
+ return ((testptr[0] == 1) || (testptr[2] == 1));
+ else
+ return ( (testptr[0] == 1) || (testptr[2] == 1)
+ || (testptr[4] == 1) || (testptr[6] == 1));
+}
+
+/* The SANE standard defines that 2-byte data must use the full 16 bit range.
+ * Byte order returned by the backend must be native byte order.
+ * Scaling to 16 bit and byte order is achived by hp_scale_to_16bit.
+ * for >8 bits data, take the two data bytes and scale their content
+ * to the full 16 bit range, using
+ * scaled = unscaled << (newlen - oldlen) +
+ * unscaled >> (oldlen - (newlen - oldlen)),
+ * with newlen=16 and oldlen the original bit depth.
+ */
+static void
+hp_scale_to_16bit(int count, register unsigned char *data, int depth,
+ hp_bool_t invert)
+{
+ register unsigned int tmp;
+ register unsigned int mask;
+ register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder ();
+ unsigned int shift1 = 16 - depth;
+ unsigned int shift2 = 2*depth - 16;
+ int k;
+
+ if (count <= 0) return;
+
+ mask = 1;
+ for (k = 1; k < depth; k++) mask |= (1 << k);
+
+ if (lowbyte_first)
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp = (tmp << shift1) + (tmp >> shift2);
+ if (invert) tmp = ~tmp;
+ *data++ = tmp & 255U;
+ *data++ = (tmp >> 8) & 255U;
+ }
+ }
+ else /* Highbyte first */
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp = (tmp << shift1) + (tmp >> shift2);
+ if (invert) tmp = ~tmp;
+ *data++ = (tmp >> 8) & 255U;
+ *data++ = tmp & 255U;
+ }
+ }
+}
+
+
+static void
+hp_scale_to_8bit(int count, register unsigned char *data, int depth,
+ hp_bool_t invert)
+{
+ register unsigned int tmp, mask;
+ register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder ();
+ unsigned int shift1 = depth-8;
+ int k;
+ unsigned char *dataout = data;
+
+ if ((count <= 0) || (shift1 <= 0)) return;
+
+ mask = 1;
+ for (k = 1; k < depth; k++) mask |= (1 << k);
+
+ if (lowbyte_first)
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp >>= shift1;
+ if (invert) tmp = ~tmp;
+ *(dataout++) = tmp & 255U;
+ data += 2;
+ }
+ }
+ else /* Highbyte first */
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp >>= shift1;
+ if (invert) tmp = ~tmp;
+ *(dataout++) = tmp & 255U;
+ data += 2;
+ }
+ }
+}
+
+static void
+hp_soft_invert(int count, register unsigned char *data) {
+ while (count>0) {
+ *data = ~(*data);
+ data++;
+ count--;
+ }
+}
+
+static PROCDATA_HANDLE *
+process_data_init (HpProcessData *procdata, const unsigned char *map,
+ int outfd, hp_bool_t use_imgbuf)
+
+{PROCDATA_HANDLE *ph = sanei_hp_alloc (sizeof (PROCDATA_HANDLE));
+ int tsz;
+
+ if (ph == NULL) return NULL;
+
+ memset (ph, 0, sizeof (*ph));
+ memcpy (&(ph->procdata), procdata, sizeof (*procdata));
+ procdata = &(ph->procdata);
+
+ tsz = (HP_TMP_BUF_SIZE <= 0) ? procdata->bytes_per_line : HP_TMP_BUF_SIZE;
+ ph->tmp_buf = sanei_hp_alloc (tsz);
+ if (ph->tmp_buf == NULL)
+ {
+ sanei_hp_free (ph);
+ return NULL;
+ }
+ ph->tmp_buf_size = tsz;
+ ph->tmp_buf_len = 0;
+
+ ph->map = map;
+ ph->outfd = outfd;
+
+ if ( procdata->mirror_vertical || use_imgbuf)
+ {
+ tsz = procdata->lines*procdata->bytes_per_line;
+ if (procdata->out8) tsz /= 2;
+ ph->image_ptr = ph->image_buf = sanei_hp_alloc (tsz);
+ if ( !ph->image_buf )
+ {
+ procdata->mirror_vertical = 0;
+ ph->image_buf_size = 0;
+ DBG(1, "process_scanline_init: Not enough memory to mirror image\n");
+ }
+ else
+ ph->image_buf_size = tsz;
+ }
+
+ ph->wr_ptr = ph->wr_buf;
+ ph->wr_buf_size = ph->wr_left = sizeof (ph->wr_buf);
+
+ return ph;
+}
+
+
+static SANE_Status
+process_data_write (PROCDATA_HANDLE *ph, unsigned char *data, int nbytes)
+
+{int ncopy;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+
+ /* Fill up write buffer */
+ ncopy = ph->wr_left;
+ if (ncopy > nbytes) ncopy = nbytes;
+
+ memcpy (ph->wr_ptr, data, ncopy);
+ ph->wr_ptr += ncopy;
+ ph->wr_left -= ncopy;
+ data += ncopy;
+ nbytes -= ncopy;
+
+ if ( ph->wr_left > 0 ) /* Did not fill up the write buffer ? Finished */
+ return SANE_STATUS_GOOD;
+
+ DBG(12, "process_data_write: write %d bytes\n", ph->wr_buf_size);
+ /* Don't write data if we got a signal in the meantime */
+ if ( signal_caught
+ || (write (ph->outfd, ph->wr_buf, ph->wr_buf_size) != ph->wr_buf_size))
+ {
+ DBG(1, "process_data_write: write failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ ph->wr_ptr = ph->wr_buf;
+ ph->wr_left = ph->wr_buf_size;
+
+ /* For large amount of data write it from data-buffer */
+ while ( nbytes > ph->wr_buf_size )
+ {
+ if ( signal_caught
+ || (write (ph->outfd, data, ph->wr_buf_size) != ph->wr_buf_size))
+ {
+ DBG(1, "process_data_write: write failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ nbytes -= ph->wr_buf_size;
+ data += ph->wr_buf_size;
+ }
+
+ if ( nbytes > 0 ) /* Something left ? Save it to (empty) write buffer */
+ {
+ memcpy (ph->wr_ptr, data, nbytes);
+ ph->wr_ptr += nbytes;
+ ph->wr_left -= nbytes;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+process_scanline (PROCDATA_HANDLE *ph, unsigned char *linebuf,
+ int bytes_per_line)
+
+{int out_bytes_per_line = bytes_per_line;
+ HpProcessData *procdata;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+ procdata = &(ph->procdata);
+
+ if ( ph->map )
+ hp_data_map (ph->map, bytes_per_line, linebuf);
+
+ if (procdata->bits_per_channel > 8)
+ {
+ if (procdata->out8)
+ {
+ hp_scale_to_8bit( bytes_per_line/2, linebuf,
+ procdata->bits_per_channel,
+ procdata->invert);
+ out_bytes_per_line /= 2;
+ }
+ else
+ {
+ hp_scale_to_16bit( bytes_per_line/2, linebuf,
+ procdata->bits_per_channel,
+ procdata->invert);
+ }
+ } else if (procdata->invert) {
+ hp_soft_invert(bytes_per_line,linebuf);
+ }
+
+ if ( ph->image_buf )
+ {
+ DBG(5, "process_scanline: save in memory\n");
+
+ if ( ph->image_ptr+out_bytes_per_line-1
+ <= ph->image_buf+ph->image_buf_size-1 )
+ {
+ memcpy(ph->image_ptr, linebuf, out_bytes_per_line);
+ ph->image_ptr += out_bytes_per_line;
+ }
+ else
+ {
+ DBG(1, "process_scanline: would exceed image buffer\n");
+ }
+ }
+ else /* Save scanlines in a bigger buffer. */
+ { /* Otherwise we will get performance problems */
+
+ RETURN_IF_FAIL ( process_data_write (ph, linebuf, out_bytes_per_line) );
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+process_data (PROCDATA_HANDLE *ph, unsigned char *read_ptr, int nread)
+
+{int bytes_left;
+ HpProcessData *procdata;
+
+ if (nread <= 0) return SANE_STATUS_GOOD;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+
+ procdata = &(ph->procdata);
+ if ( ph->tmp_buf_len > 0 ) /* Something left ? */
+ {
+ bytes_left = ph->tmp_buf_size - ph->tmp_buf_len;
+ if (nread < bytes_left) /* All to buffer ? */
+ {
+ memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, nread);
+ ph->tmp_buf_len += nread;
+ return SANE_STATUS_GOOD;
+ }
+ memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, bytes_left);
+ read_ptr += bytes_left;
+ nread -= bytes_left;
+ RETURN_IF_FAIL ( process_scanline (ph, ph->tmp_buf, ph->tmp_buf_size) );
+ ph->tmp_buf_len = 0;
+ }
+ while (nread > 0)
+ {
+ if (nread >= ph->tmp_buf_size)
+ {
+ RETURN_IF_FAIL ( process_scanline (ph, read_ptr, ph->tmp_buf_size) );
+ read_ptr += ph->tmp_buf_size;
+ nread -= ph->tmp_buf_size;
+ }
+ else
+ {
+ memcpy (ph->tmp_buf, read_ptr, nread);
+ ph->tmp_buf_len = nread;
+ nread = 0;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+process_data_flush (PROCDATA_HANDLE *ph)
+
+{SANE_Status status = SANE_STATUS_GOOD;
+ HpProcessData *procdata;
+ unsigned char *image_data;
+ size_t image_len;
+ int num_lines, bytes_per_line;
+ int nbytes;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+
+ if ( ph->tmp_buf_len > 0 )
+ process_scanline (ph, ph->tmp_buf, ph->tmp_buf_len);
+
+ if ( ph->wr_left != ph->wr_buf_size ) /* Something in write buffer ? */
+ {
+ nbytes = ph->wr_buf_size - ph->wr_left;
+ if ( signal_caught || (write (ph->outfd, ph->wr_buf, nbytes) != nbytes))
+ {
+ DBG(1, "process_data_flush: write failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ ph->wr_ptr = ph->wr_buf;
+ ph->wr_left = ph->wr_buf_size;
+ }
+
+ procdata = &(ph->procdata);
+ if ( ph->image_buf )
+ {
+ bytes_per_line = procdata->bytes_per_line;
+ if (procdata->out8) bytes_per_line /= 2;
+ image_len = (size_t) (ph->image_ptr - ph->image_buf);
+ num_lines = ((int)(image_len + bytes_per_line-1)) / bytes_per_line;
+
+ DBG(3, "process_data_finish: write %d bytes from memory...\n",
+ (int)image_len);
+
+ if ( procdata->mirror_vertical )
+ {
+ image_data = ph->image_buf + (num_lines-1) * bytes_per_line;
+ while (num_lines > 0 )
+ {
+ if ( signal_caught
+ || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line))
+ {
+ DBG(1,"process_data_finish: write from memory failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ num_lines--;
+ image_data -= bytes_per_line;
+ }
+ }
+ else
+ {
+ image_data = ph->image_buf;
+ while (num_lines > 0 )
+ {
+ if ( signal_caught
+ || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line))
+ {
+ DBG(1,"process_data_finish: write from memory failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ num_lines--;
+ image_data += bytes_per_line;
+ }
+ }
+ }
+ return status;
+}
+
+
+static void
+process_data_finish (PROCDATA_HANDLE *ph)
+
+{
+ DBG(12, "process_data_finish called\n");
+
+ if (ph == NULL) return;
+
+ if (ph->image_buf != NULL) sanei_hp_free (ph->image_buf);
+
+ sanei_hp_free (ph->tmp_buf);
+ sanei_hp_free (ph);
+}
+
+
+SANE_Status
+sanei_hp_scsi_pipeout (HpScsi this, int outfd, HpProcessData *procdata)
+{
+ /* We will catch these signals, and rethrow them after cleaning up,
+ * anything not in this list, we will ignore. */
+ static int kill_sig[] = {
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGPIPE, SIGALRM, SIGTERM,
+ SIGUSR1, SIGUSR2, SIGBUS,
+#ifdef SIGSTKFLT
+ SIGSTKFLT,
+#endif
+#ifdef SIGIO
+ SIGIO,
+#else
+# ifdef SIGPOLL
+ SIGPOLL,
+# endif
+#endif
+#ifdef SIGXCPU
+ SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ,
+#endif
+#ifdef SIGVTALRM
+ SIGVTALRM,
+#endif
+#ifdef SIGPWR
+ SIGPWR,
+#endif
+ };
+#define HP_NSIGS (sizeof(kill_sig)/sizeof(kill_sig[0]))
+ struct SIGACTION old_handler[HP_NSIGS];
+ struct SIGACTION sa;
+ sigset_t old_set, sig_set;
+ int i;
+ int bits_per_channel = procdata->bits_per_channel;
+
+#define HP_PIPEBUF 32768
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct {
+ size_t len;
+ void * id;
+ hp_byte_t cmd[6];
+ hp_byte_t data[HP_PIPEBUF];
+ } buf[2], *req = NULL;
+
+ int reqs_completed = 0;
+ int reqs_issued = 0;
+ char *image_buf = 0;
+ char *read_buf = 0;
+ const HpDeviceInfo *info;
+ const char *devname = sanei_hp_scsi_devicename (this);
+ int enable_requests = 1;
+ int enable_image_buffering = 0;
+ const unsigned char *map = NULL;
+ HpConnect connect;
+ PROCDATA_HANDLE *ph = NULL;
+ size_t count = procdata->lines * procdata->bytes_per_line;
+
+ RETURN_IF_FAIL( hp_scsi_flush(this) );
+
+ connect = sanei_hp_get_connect (devname);
+ info = sanei_hp_device_info_get (devname);
+
+ assert (info);
+
+ if ( info->config_is_up )
+ {
+ enable_requests = info->config.use_scsi_request;
+ enable_image_buffering = info->config.use_image_buffering;
+ }
+ else
+ {
+ enable_requests = 0;
+ }
+
+ if (connect != HP_CONNECT_SCSI)
+ enable_requests = 0;
+
+ /* Currently we can only simulate 8 bits mapping */
+ if (bits_per_channel == 8)
+ map = hp_get_simulation_map (devname, info);
+
+ sigfillset(&sig_set);
+ sigprocmask(SIG_BLOCK, &sig_set, &old_set);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_catcher;
+ sigfillset(&sa.sa_mask);
+
+ sigemptyset(&sig_set);
+ for (i = 0; i < (int)(HP_NSIGS); i++)
+ {
+ sigaction(kill_sig[i], &sa, &old_handler[i]);
+ sigaddset(&sig_set, kill_sig[i]);
+ }
+ signal_caught = 0;
+ sigprocmask(SIG_UNBLOCK, &sig_set, 0);
+
+ /* Wait for front button push ? */
+ if ( procdata->startscan )
+ {
+ for (;;)
+ {int val = 0;
+
+ if (signal_caught) goto quit;
+ sanei_hp_scl_inquire (this, SCL_FRONT_BUTTON, &val, 0, 0);
+ if (val) break;
+ usleep ((unsigned long)333*1000); /* Wait 1/3 second */
+ }
+ status = sanei_hp_scl_startScan (this, procdata->startscan);
+ if (status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "do_read: Error starting scan in reader process\n");
+ goto quit;
+ }
+ }
+ ph = process_data_init (procdata, map, outfd, enable_image_buffering);
+
+ if ( ph == NULL )
+ {
+ DBG(1, "do_read: Error with process_data_init()\n");
+ goto quit;
+ }
+
+ DBG(1, "do_read: Start reading data from scanner\n");
+
+ if (enable_requests) /* Issue SCSI-requests ? */
+ {
+ while (count > 0 || reqs_completed < reqs_issued)
+ {
+ while (count > 0 && reqs_issued < reqs_completed + 2)
+ {
+ req = buf + (reqs_issued++ % 2);
+
+ req->len = HP_PIPEBUF;
+ if (count < req->len)
+ req->len = count;
+ count -= req->len;
+
+ req->cmd[0] = 0x08;
+ req->cmd[1] = 0;
+ req->cmd[2] = req->len >> 16;
+ req->cmd[3] = req->len >> 8;
+ req->cmd[4] = req->len;
+ req->cmd[5] = 0;
+
+ DBG(3, "do_read: entering request to read %lu bytes\n",
+ (unsigned long) req->len);
+
+ status = sanei_scsi_req_enter(this->fd, req->cmd, 6,
+ req->data, &req->len, &req->id);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "do_read: Error from scsi_req_enter: %s\n",
+ sane_strstatus(status));
+ goto quit;
+ }
+ if (signal_caught)
+ goto quit;
+ }
+
+ if (signal_caught)
+ goto quit;
+
+ assert(reqs_completed < reqs_issued);
+ req = buf + (reqs_completed++ % 2);
+
+ DBG(3, "do_read: waiting for data\n");
+ status = sanei_scsi_req_wait(req->id);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "do_read: Error from scsi_req_wait: %s\n",
+ sane_strstatus(status));
+ goto quit;
+ }
+ if (signal_caught)
+ goto quit;
+
+ status = process_data (ph, (unsigned char *)req->data, (int)req->len);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1,"do_read: Error in process_data\n");
+ goto quit;
+ }
+ }
+ }
+ else /* Read directly */
+ {
+ read_buf = sanei_hp_alloc ( HP_PIPEBUF );
+ if (!read_buf)
+ {
+ DBG(1, "do_read: not enough memory for read buffer\n");
+ goto quit;
+ }
+
+ while (count > 0)
+ {size_t nread;
+
+ if (signal_caught)
+ goto quit;
+
+ DBG(5, "do_read: %lu bytes left to read\n", (unsigned long)count);
+
+ nread = HP_PIPEBUF;
+ if (nread > count) nread = count;
+
+ DBG(3, "do_read: try to read data (%lu bytes)\n", (unsigned long)nread);
+
+ status = hp_scsi_read (this, read_buf, &nread, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "do_read: Error from scsi_read: %s\n",sane_strstatus(status));
+ goto quit;
+ }
+
+ DBG(3, "do_read: got %lu bytes\n", (unsigned long)nread);
+
+ if (nread <= 0)
+ {
+ DBG(1, "do_read: Nothing read\n");
+ continue;
+ }
+
+ status = process_data (ph, (unsigned char *)read_buf, (int)nread);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1,"do_read: Error in process_data\n");
+ goto quit;
+ }
+ count -= nread;
+ }
+ }
+
+ process_data_flush (ph);
+
+quit:
+
+ process_data_finish (ph);
+
+ if ( image_buf ) sanei_hp_free ( image_buf );
+ if ( read_buf ) sanei_hp_free ( read_buf );
+
+ if (enable_requests && (reqs_completed < reqs_issued))
+ {
+ DBG(1, "do_read: cleaning up leftover requests\n");
+ while (reqs_completed < reqs_issued)
+ {
+ req = buf + (reqs_completed++ % 2);
+ sanei_scsi_req_wait(req->id);
+ }
+ }
+
+ sigfillset(&sig_set);
+ sigprocmask(SIG_BLOCK, &sig_set, 0);
+ for (i = 0; i < (int)(HP_NSIGS); i++)
+ sigaction(kill_sig[i], &old_handler[i], 0);
+ sigprocmask(SIG_SETMASK, &old_set, 0);
+
+ if (signal_caught)
+ {
+ DBG(1, "do_read: caught signal %d\n", signal_caught);
+ raise(signal_caught);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ return status;
+}
+
+
+
+/*
+ *
+ */
+
+static SANE_Status
+_hp_scl_inq (HpScsi scsi, HpScl scl, HpScl inq_cmnd,
+ void *valp, size_t *lengthp)
+{
+ size_t bufsize = 16 + (lengthp ? *lengthp: 0);
+ char * buf = alloca(bufsize);
+ char expect[16], expect_char;
+ int val, count;
+ SANE_Status status;
+
+ if (!buf)
+ return SANE_STATUS_NO_MEM;
+
+ /* Flush data before sending inquiry. */
+ /* Otherwise scanner might not generate a response. */
+ RETURN_IF_FAIL( hp_scsi_flush (scsi)) ;
+
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, inq_cmnd, SCL_INQ_ID(scl)) );
+ usleep (1000); /* 500 works, too, but not 100 */
+
+ status = hp_scsi_read(scsi, buf, &bufsize, 1);
+ if (FAILED(status))
+ {
+ DBG(1, "scl_inq: read failed (%s)\n", sane_strstatus(status));
+ return status;
+ }
+
+ if (SCL_PARAM_CHAR(inq_cmnd) == 'R')
+ expect_char = 'p';
+ else
+ expect_char = tolower(SCL_PARAM_CHAR(inq_cmnd) - 1);
+
+ count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char);
+ if (memcmp(buf, expect, count) != 0)
+ {
+ DBG(1, "scl_inq: malformed response: expected '%s', got '%.*s'\n",
+ expect, count, buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ if (buf[0] == 'N')
+ { /* null response */
+ DBG(3, "scl_inq: parameter %d unsupported\n", SCL_INQ_ID(scl));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (sscanf(buf, "%d%n", &val, &count) != 1)
+ {
+ DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ expect_char = lengthp ? 'W' : 'V';
+ if (*buf++ != expect_char)
+ {
+ DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n",
+ expect_char, buf - 1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (!lengthp)
+ *(int *)valp = val; /* Get integer value */
+ else
+ {
+ if (val > (int)*lengthp)
+ {
+ DBG(1, "scl_inq: inquiry returned %d bytes, expected <= %lu\n",
+ val, (unsigned long) *lengthp);
+ return SANE_STATUS_IO_ERROR;
+ }
+ *lengthp = val;
+ memcpy(valp, buf , *lengthp); /* Get binary data */
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sanei_hp_scl_upload_binary (HpScsi scsi, HpScl scl, size_t *lengthhp,
+ char **bufhp)
+{
+ size_t bufsize = 16, sv;
+ char * buf = alloca(bufsize);
+ char * bufstart = buf;
+ char * hpdata;
+ char expect[16], expect_char;
+ int n, val, count;
+ SANE_Status status;
+
+ if (!buf)
+ return SANE_STATUS_NO_MEM;
+
+ assert ( IS_SCL_DATA_TYPE (scl) );
+
+ /* Flush data before sending inquiry. */
+ /* Otherwise scanner might not generate a response. */
+ RETURN_IF_FAIL( hp_scsi_flush (scsi)) ;
+
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_UPLOAD_BINARY_DATA, SCL_INQ_ID(scl)) );
+
+ status = hp_scsi_read(scsi, buf, &bufsize, 0);
+ if (FAILED(status))
+ {
+ DBG(1, "scl_upload_binary: read failed (%s)\n", sane_strstatus(status));
+ return status;
+ }
+
+ expect_char = 't';
+ count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char);
+ if (memcmp(buf, expect, count) != 0)
+ {
+ DBG(1, "scl_upload_binary: malformed response: expected '%s', got '%.*s'\n",
+ expect, count, buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ if (buf[0] == 'N')
+ { /* null response */
+ DBG(1, "scl_upload_binary: parameter %d unsupported\n", SCL_INQ_ID(scl));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (sscanf(buf, "%d%n", &val, &count) != 1)
+ {
+ DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ expect_char = 'W';
+ if (*buf++ != expect_char)
+ {
+ DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n",
+ expect_char, buf - 1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ *lengthhp = val;
+ *bufhp = hpdata = sanei_hp_alloc ( val );
+ if (!hpdata)
+ return SANE_STATUS_NO_MEM;
+
+ if (buf < bufstart + bufsize)
+ {
+ n = bufsize - (buf - bufstart);
+ if (n > val) n = val;
+ memcpy (hpdata, buf, n);
+ hpdata += n;
+ val -= n;
+ }
+
+ status = SANE_STATUS_GOOD;
+ if ( val > 0 )
+ {
+ sv = val;
+ status = hp_scsi_read(scsi, hpdata, &sv, 0);
+ if (status != SANE_STATUS_GOOD)
+ sanei_hp_free ( *bufhp );
+ }
+
+ return status;
+}
+
+
+SANE_Status
+sanei_hp_scl_set(HpScsi scsi, HpScl scl, int val)
+{
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, val) );
+
+
+#ifdef PARANOID
+ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_scl_inquire(HpScsi scsi, HpScl scl, int * valp, int * minp, int * maxp)
+{
+ HpScl inquiry = ( IS_SCL_CONTROL(scl)
+ ? SCL_INQUIRE_PRESENT_VALUE
+ : SCL_INQUIRE_DEVICE_PARAMETER );
+
+ assert(IS_SCL_CONTROL(scl) || IS_SCL_PARAMETER(scl));
+ assert(IS_SCL_CONTROL(scl) || (!minp && !maxp));
+
+ if (valp)
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, 0) );
+ if (minp)
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl,
+ SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
+ if (maxp)
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl,
+ SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) );
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef _HP_NOT_USED
+static SANE_Status
+hp_scl_get_bounds(HpScsi scsi, HpScl scl, int * minp, int * maxp)
+{
+ assert(IS_SCL_CONTROL(scl));
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
+ return _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0);
+}
+#endif
+
+#ifdef _HP_NOT_USED
+static SANE_Status
+hp_scl_get_bounds_and_val(HpScsi scsi, HpScl scl,
+ int * minp, int * maxp, int * valp)
+{
+ assert(IS_SCL_CONTROL(scl));
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) );
+ return _hp_scl_inq(scsi, scl, SCL_INQUIRE_PRESENT_VALUE, valp, 0);
+}
+#endif
+
+SANE_Status
+sanei_hp_scl_download(HpScsi scsi, HpScl scl, const void * valp, size_t len)
+{
+ assert(IS_SCL_DATA_TYPE(scl));
+
+ sanei_hp_scl_clearErrors ( scsi );
+ RETURN_IF_FAIL( hp_scsi_need(scsi, 16) );
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl)) );
+ /* Download type not supported ? */
+ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_LENGTH, len) );
+ RETURN_IF_FAIL( hp_scsi_write(scsi, valp, len) );
+
+#ifdef PARANOID
+ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_scl_upload(HpScsi scsi, HpScl scl, void * valp, size_t len)
+{
+ size_t nread = len;
+ HpScl inquiry = ( IS_SCL_DATA_TYPE(scl)
+ ? SCL_UPLOAD_BINARY_DATA
+ : SCL_INQUIRE_DEVICE_PARAMETER );
+
+ assert(IS_SCL_DATA_TYPE(scl) || IS_SCL_PARAMETER(scl));
+
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, &nread) );
+ if (IS_SCL_PARAMETER(scl) && nread < len)
+ ((char *)valp)[nread] = '\0';
+ else if (len != nread)
+ {
+ DBG(1, "scl_upload: requested %lu bytes, got %lu\n",
+ (unsigned long) len, (unsigned long) nread);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_scl_calibrate(HpScsi scsi)
+{
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_CALIBRATE, 0) );
+ return hp_scsi_flush(scsi);
+}
+
+SANE_Status
+sanei_hp_scl_startScan(HpScsi scsi, HpScl scl)
+{
+ char *msg = "";
+
+ if (scl == SCL_ADF_SCAN) msg = " (ADF)";
+ else if (scl == SCL_XPA_SCAN) msg = " (XPA)";
+ else scl = SCL_START_SCAN;
+
+ DBG(1, "sanei_hp_scl_startScan: Start scan%s\n", msg);
+
+ /* For active XPA we must not use XPA scan */
+ if ((scl == SCL_XPA_SCAN) && sanei_hp_is_active_xpa (scsi))
+ {
+ DBG(3,"Map XPA scan to scan because of active XPA\n");
+ scl = SCL_START_SCAN;
+ }
+
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, 0) );
+ return hp_scsi_flush(scsi);
+}
+
+SANE_Status
+sanei_hp_scl_reset(HpScsi scsi)
+{
+ RETURN_IF_FAIL( hp_scsi_write(scsi, "\033E", 2) );
+ RETURN_IF_FAIL( hp_scsi_flush(scsi) );
+ return sanei_hp_scl_errcheck(scsi);
+}
+
+SANE_Status
+sanei_hp_scl_clearErrors(HpScsi scsi)
+{
+ RETURN_IF_FAIL( hp_scsi_flush(scsi) );
+ RETURN_IF_FAIL( hp_scsi_write(scsi, "\033*oE", 4) );
+ return hp_scsi_flush(scsi);
+}
+
+static const char *
+hp_scl_strerror (int errnum)
+{
+ static const char * errlist[] = {
+ "Command Format Error",
+ "Unrecognized Command",
+ "Parameter Error",
+ "Illegal Window",
+ "Scaling Error",
+ "Dither ID Error",
+ "Tone Map ID Error",
+ "Lamp Error",
+ "Matrix ID Error",
+ "Cal Strip Param Error",
+ "Gross Calibration Error"
+ };
+
+ if (errnum >= 0 && errnum < (int)(sizeof(errlist)/sizeof(errlist[0])))
+ return errlist[errnum];
+ else
+ switch(errnum) {
+ case 1024: return "ADF Paper Jam";
+ case 1025: return "Home Position Missing";
+ case 1026: return "Paper Not Loaded";
+ default: return "??Unkown Error??";
+ }
+}
+
+/* Check for SCL errors */
+SANE_Status
+sanei_hp_scl_errcheck (HpScsi scsi)
+{
+ int errnum;
+ int nerrors;
+ SANE_Status status;
+
+ status = sanei_hp_scl_inquire(scsi, SCL_CURRENT_ERROR_STACK, &nerrors,0,0);
+ if (!FAILED(status) && nerrors)
+ status = sanei_hp_scl_inquire(scsi, SCL_OLDEST_ERROR, &errnum,0,0);
+ if (FAILED(status))
+ {
+ DBG(1, "scl_errcheck: Can't read SCL error stack: %s\n",
+ sane_strstatus(status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (nerrors)
+ {
+ DBG(1, "Scanner issued SCL error: (%d) %s\n",
+ errnum, hp_scl_strerror(errnum));
+
+ sanei_hp_scl_clearErrors (scsi);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/hp-scl.h b/backend/hp-scl.h
new file mode 100644
index 0000000..7954492
--- /dev/null
+++ b/backend/hp-scl.h
@@ -0,0 +1,166 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+#ifndef HP_SCL_INCLUDED
+#define HP_SCL_INCLUDED
+
+#define HP_SCL_PACK(id, group, char) \
+ ((SANE_Word)(id) << 16 | ((group) & 0xFF) << 8 | ((char) & 0xFF))
+#define SCL_INQ_ID(code) ((code) >> 16)
+#define SCL_GROUP_CHAR(code) ((char)(((code) >> 8) & 0xFF))
+#define SCL_PARAM_CHAR(code) ((char)((code) & 0xFF))
+
+#define HP_SCL_CONTROL(id,g,c) HP_SCL_PACK(id,g,c)
+#define HP_SCL_COMMAND(g,c) HP_SCL_PACK(0,g,c)
+#define HP_SCL_PARAMETER(id) HP_SCL_PACK(id, 0, 0)
+#define HP_SCL_DATA_TYPE(id) HP_SCL_PACK(id, 1, 0)
+
+#define IS_SCL_CONTROL(scl) (SCL_INQ_ID(scl) && SCL_PARAM_CHAR(scl))
+#define IS_SCL_COMMAND(scl) (!SCL_INQ_ID(scl) && SCL_PARAM_CHAR(scl))
+#define IS_SCL_PARAMETER(scl) (SCL_INQ_ID(scl) && !SCL_PARAM_CHAR(scl))
+#define IS_SCL_DATA_TYPE(scl) (SCL_GROUP_CHAR(scl) == '\001')
+
+#define SCL_AUTO_BKGRND HP_SCL_CONTROL(10307, 'a', 'B')
+#define SCL_COMPRESSION HP_SCL_CONTROL(10308, 'a', 'C')
+#define SCL_DOWNLOAD_TYPE HP_SCL_CONTROL(10309, 'a', 'D')
+#define SCL_X_SCALE HP_SCL_CONTROL(10310, 'a', 'E')
+#define SCL_Y_SCALE HP_SCL_CONTROL(10311, 'a', 'F')
+#define SCL_DATA_WIDTH HP_SCL_CONTROL(10312, 'a', 'G')
+#define SCL_INVERSE_IMAGE HP_SCL_CONTROL(10314, 'a', 'I')
+#define SCL_BW_DITHER HP_SCL_CONTROL(10315, 'a', 'J')
+#define SCL_CONTRAST HP_SCL_CONTROL(10316, 'a', 'K')
+#define SCL_BRIGHTNESS HP_SCL_CONTROL(10317, 'a', 'L')
+#define SCL_MIRROR_IMAGE HP_SCL_CONTROL(10318, 'a', 'M')
+#define SCL_SHARPENING HP_SCL_CONTROL(10319, 'a', 'N')
+#define SCL_RESERVED1 HP_SCL_CONTROL(10320, 'a', 'O')
+#define SCL_X_RESOLUTION HP_SCL_CONTROL(10323, 'a', 'R')
+#define SCL_Y_RESOLUTION HP_SCL_CONTROL(10324, 'a', 'S')
+#define SCL_OUTPUT_DATA_TYPE HP_SCL_CONTROL(10325, 'a', 'T')
+#define SCL_DOWNLOAD_LENGTH HP_SCL_CONTROL(10328, 'a', 'W')
+#define SCL_PRELOAD_ADF HP_SCL_CONTROL(10468, 'f', 'C')
+#define SCL_MEDIA HP_SCL_CONTROL(10469, 'f', 'D')
+#define SCL_10470 HP_SCL_CONTROL(10470, 'f', 'E')
+#define SCL_LAMPTEST HP_SCL_CONTROL(10477, 'f', 'L')
+#define SCL_X_EXTENT HP_SCL_CONTROL(10481, 'f', 'P')
+#define SCL_Y_EXTENT HP_SCL_CONTROL(10482, 'f', 'Q')
+#define SCL_START_SCAN HP_SCL_COMMAND('f', 'S')
+#define SCL_10485 HP_SCL_CONTROL(10485, 'f', 'T')
+#define SCL_10488 HP_SCL_CONTROL(10488, 'f', 'W')
+#define SCL_X_POS HP_SCL_CONTROL(10489, 'f', 'X')
+#define SCL_Y_POS HP_SCL_CONTROL(10490, 'f', 'Y')
+#define SCL_XPA_SCAN HP_SCL_COMMAND('u', 'D')
+#define SCL_SPEED HP_SCL_CONTROL(10950, 'u', 'E')
+#define SCL_FILTER HP_SCL_CONTROL(10951, 'u', 'F')
+#define SCL_10952 HP_SCL_CONTROL(10952, 'u', 'G')
+#define SCL_XPA_DISABLE HP_SCL_CONTROL(10953, 'u', 'H')
+#define SCL_TONE_MAP HP_SCL_CONTROL(10956, 'u', 'K')
+#define SCL_CALIBRATE HP_SCL_COMMAND('u', 'R')
+#define SCL_ADF_SCAN HP_SCL_COMMAND('u', 'S')
+#define SCL_MATRIX HP_SCL_CONTROL(10965, 'u', 'T')
+#define SCL_UNLOAD HP_SCL_CONTROL(10966, 'u', 'U')
+#define SCL_10967 HP_SCL_CONTROL(10967, 'u', 'V')
+#define SCL_CHANGE_DOC HP_SCL_CONTROL(10969, 'u', 'X')
+#define SCL_ADF_BFEED HP_SCL_CONTROL(10970, 'u', 'Y')
+/* Clear Errors does not follow command syntax Esc*o0E, it is only Esc*oE */
+/* #define SCL_CLEAR_ERRORS HP_SCL_COMMAND('o', 'E') */
+
+#define SCL_INQUIRE_PRESENT_VALUE HP_SCL_COMMAND('s', 'R')
+#define SCL_INQUIRE_MINIMUM_VALUE HP_SCL_COMMAND('s', 'L')
+#define SCL_INQUIRE_MAXIMUM_VALUE HP_SCL_COMMAND('s', 'H')
+#define SCL_INQUIRE_DEVICE_PARAMETER HP_SCL_COMMAND('s', 'E')
+#define SCL_UPLOAD_BINARY_DATA HP_SCL_COMMAND('s', 'U')
+
+#define SCL_HP_MODEL_1 HP_SCL_PARAMETER(3)
+#define SCL_HP_MODEL_2 HP_SCL_PARAMETER(10)
+#define SCL_HP_MODEL_3 HP_SCL_PARAMETER(9)
+#define SCL_HP_MODEL_4 HP_SCL_PARAMETER(11)
+#define SCL_HP_MODEL_5 HP_SCL_PARAMETER(12)
+#define SCL_HP_MODEL_6 HP_SCL_PARAMETER(14)
+#define SCL_HP_MODEL_8 HP_SCL_PARAMETER(15)
+#define SCL_HP_MODEL_9 HP_SCL_PARAMETER(16)
+#define SCL_HP_MODEL_10 HP_SCL_PARAMETER(17)
+#define SCL_HP_MODEL_11 HP_SCL_PARAMETER(18)
+#define SCL_HP_MODEL_12 HP_SCL_PARAMETER(19)
+#define SCL_HP_MODEL_14 HP_SCL_PARAMETER(21)
+#define SCL_HP_MODEL_16 HP_SCL_PARAMETER(31)
+#define SCL_HP_MODEL_17 HP_SCL_PARAMETER(32)
+
+#define SCL_ADF_CAPABILITY HP_SCL_PARAMETER(24)
+#define SCL_ADF_BIN HP_SCL_PARAMETER(25)
+#define SCL_ADF_RDY_UNLOAD HP_SCL_PARAMETER(27)
+
+#define SCL_CURRENT_ERROR_STACK HP_SCL_PARAMETER(257)
+#define SCL_CURRENT_ERROR HP_SCL_PARAMETER(259)
+#define SCL_OLDEST_ERROR HP_SCL_PARAMETER(261)
+#define SCL_PIXELS_PER_LINE HP_SCL_PARAMETER(1024)
+#define SCL_BYTES_PER_LINE HP_SCL_PARAMETER(1025)
+#define SCL_NUMBER_OF_LINES HP_SCL_PARAMETER(1026)
+#define SCL_ADF_READY HP_SCL_PARAMETER(1027)
+
+#define SCL_DEVPIX_RESOLUTION HP_SCL_PARAMETER(1028)
+
+#define SCL_AUTO_SPEED HP_SCL_PARAMETER(1040)
+
+#define SCL_FRONT_BUTTON HP_SCL_PARAMETER(1044)
+
+#define SCL_PRELOADED HP_SCL_PARAMETER(1045)
+
+/* The following is not documented */
+#define SCL_SECONDARY_SCANDIR HP_SCL_PARAMETER(1047)
+
+#define SCL_BW8x8DITHER HP_SCL_DATA_TYPE(0)
+#define SCL_8x8TONE_MAP HP_SCL_DATA_TYPE(1)
+#define SCL_8x9MATRIX_COEFF HP_SCL_DATA_TYPE(2)
+#define SCL_8x8DITHER HP_SCL_DATA_TYPE(3)
+#define SCL_CAL_STRIP HP_SCL_DATA_TYPE(4)
+#define SCL_BW16x16DITHER HP_SCL_DATA_TYPE(5)
+#define SCL_10x8TONE_MAP HP_SCL_DATA_TYPE(6)
+#define SCL_10x3MATRIX_COEFF HP_SCL_DATA_TYPE(8)
+#define SCL_10x9MATRIX_COEFF HP_SCL_DATA_TYPE(9)
+#define SCL_7x12TONE_MAP HP_SCL_DATA_TYPE(10)
+#define SCL_BW7x12TONE_MAP HP_SCL_DATA_TYPE(11)
+#define SCL_RGB_GAINS HP_SCL_DATA_TYPE(11)
+#define SCL_CALIB_MAP HP_SCL_DATA_TYPE(14)
+
+#endif /* HP_SCL_INCLUDED */
diff --git a/backend/hp-scsi.h b/backend/hp-scsi.h
new file mode 100644
index 0000000..0e12185
--- /dev/null
+++ b/backend/hp-scsi.h
@@ -0,0 +1,78 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+#ifndef HP_SCSI_INCLUDED
+#define HP_SCSI_INCLUDED
+
+#define HP_SCSI_MAX_WRITE (2048)
+SANE_Status sanei_hp_nonscsi_new (HpScsi * newp, const char * devname,
+ HpConnect connect);
+SANE_Status sanei_hp_scsi_new (HpScsi * newp, const char * devname);
+void sanei_hp_scsi_destroy (HpScsi this,int completely);
+
+hp_byte_t * sanei_hp_scsi_inq (HpScsi this);
+const char *sanei_hp_scsi_model (HpScsi this);
+const char *sanei_hp_scsi_vendor (HpScsi this);
+const char *sanei_hp_scsi_devicename (HpScsi this);
+
+SANE_Status sanei_hp_scsi_pipeout (HpScsi this, int outfd,
+ HpProcessData *pdescr);
+
+SANE_Status sanei_hp_scl_calibrate (HpScsi scsi);
+SANE_Status sanei_hp_scl_startScan (HpScsi scsi, HpScl scl);
+SANE_Status sanei_hp_scl_reset (HpScsi scsi);
+SANE_Status sanei_hp_scl_clearErrors (HpScsi scsi);
+SANE_Status sanei_hp_scl_errcheck (HpScsi scsi);
+
+SANE_Status sanei_hp_scl_upload_binary (HpScsi scsi, HpScl scl,
+ size_t *lengthhp, char **bufhp);
+SANE_Status sanei_hp_scl_set (HpScsi scsi, HpScl scl, int val);
+SANE_Status sanei_hp_scl_inquire(HpScsi scsi, HpScl scl,
+ int * valp, int * minp, int * maxp);
+SANE_Status sanei_hp_scl_upload (HpScsi scsi, HpScl scl,
+ void * buf, size_t sz);
+SANE_Status sanei_hp_scl_download (HpScsi scsi, HpScl scl,
+ const void * buf, size_t sz);
+
+#endif /* HP_SCSI_INCLUDED */
diff --git a/backend/hp.README b/backend/hp.README
new file mode 100644
index 0000000..756ddca
--- /dev/null
+++ b/backend/hp.README
@@ -0,0 +1,70 @@
+Fri Dec 5 18:10:52 PST 1997
+Geoffrey T. Dairiki <dairiki@alumni.caltech.edu>
+
+Here is a new HP backend. It's name is, appropriately, ``newhp''.
+
+It is still in the pre-alpha stages but mostly works on my ScanJet 5P.
+It is completely untested on other scanners, but might hopefully work
+on ScanJet models IIp, IIc, IIcx, 3p, 3c, 4c, 4p, and 5p.
+
+I've only really tested this with xscanimage.
+
+Some features included in this backend which aren't in the old
+HP driver are:
+
+ Support for custom tonemaps (gamma-maps). For some reason I
+ can't get the 10-bit tonemaps working with my 5p, so for you
+ only get a single tonemap (it still works in color mode, but
+ you don't get individual control over each R/G/B channel.)
+
+ You can enable the scanjets "Auto Threshold" mode when scanning
+ line-art. I can't notice that it does much though....
+
+ Added an option to control the scan speed.
+
+ Added an option to select the dither pattern used in half-tone mode.
+ I don't think that custom dither patterns are working correctly,
+ and in any case, with the current front-ends, there is no convenient
+ way to set a custom dither pattern....
+
+ Added an option to select the color correction matrixes.
+ This also seems to be flaky. Again the current front-ends do not provide
+ a decent way to adjust the custom color matrix.
+
+
+Tue Feb 24 20:00:00 UT+1 1998
+Peter Kirchgessner <pkirchg@aol.com>
+
+Picked up the sources because they did not find their way to the
+SANE-package up to now. And because I want to have a better support
+for the HP Photosmart Photoscanner. Renamed the backend from newhp
+to xhp just to get a better acceptance.
+
+ Added an option to perform calibration. Upload and download calibration data
+
+ Added options for horizontal/vertical mirroring. Vertical mirroring
+ is performed by loading image completely into memory before
+ sending to frontend.
+
+ Uses SCSI-flushing more often because otherwise the scanner did not
+ accept all of the commands.
+
+ Test support of commands to be independant from inquired
+ model number.
+
+Wed Mar 28 10:00:00 UT+1 1998
+
+Bring it on its way to the SANE-project. Geoffrey accepted to add
+licence informations to the files as long as noone has to pay for the code.
+
+Sun Jun 07 15:00:00 UT+1 1998
+
+ Added unload button
+
+ Fixed problem with custom gamma table
+
+Sat Nov 14 14:00:00 UT+1 1998
+
+ Show calibrate button on Photosmart only for print media
+ Add HP 6200 C
+ Suppress halftone mode on photosmart (it is not supported)
diff --git a/backend/hp.TODO b/backend/hp.TODO
new file mode 100644
index 0000000..b9c1fd2
--- /dev/null
+++ b/backend/hp.TODO
@@ -0,0 +1,52 @@
+These are Geoffreys TODOs:
+
+ Fix ifdef ENABLE_'s
+ Add emulation for color separation matrixes?
+ Gamma plot initialization?
+
+ Dither pattern option not working right.
+
+ More use of FAILED macro , maybe SUCCESS macro ?
+
+ Convert to single .h file?
+
+
+/** FIXME: ToDo
+ *
+ * Split up hp_scsi_open -- don't need full probe every time?
+ * or if probing at least make sure answer is constant.
+ *
+ * Stale flage so don't download unchanged parameters?
+ *
+ * Fix or disable dither stuff.
+ *
+ * Add controls
+ *
+ * Preview mode: fast, grayscale instead of halftone.
+ * Separate X and Y resolutions + bind button.
+ * 10x8 tone maps?
+ * Sharpening?
+ * Matrixes - fix color separtion?
+ * Dither pattern? COlor dither pattern?
+ *
+ * check return values (of maybe others)?
+ * get rid of overly verbose DBG's
+ * add som DBG's
+ */
+
+New TODOs:
+
+ Find a way to set matrices in the frontend (gtk-table with sliders ?)
+
+ Find a way to set dither matrices in the frontend
+ (See the ENABLE_...-macros in hp.h to enable these features)
+
+ Fix problem with performance of reader-process. Communication through
+ the pipe might be too slow and make the scanner repeating several strips.
+
+ Fix various problems with PhotoScanner:
+ - rubbish on bottom of scanned image
+ - too few data that is received on bottom of scanned image for PhotoScanner
+ - scan time for slides at 2400 dpi is twice as long as under M$-Windows
+
+ Support 10/12-bits for greyscale
diff --git a/backend/hp.c b/backend/hp.c
new file mode 100644
index 0000000..c85e40c
--- /dev/null
+++ b/backend/hp.c
@@ -0,0 +1,1013 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ Support for HP PhotoSmart Photoscanner by Peter Kirchgessner
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+static char *hp_backend_version = "1.06";
+static char *hp_backend_revision = "$Revision$";
+/* Changes:
+
+ V 1.06:
+ $Log$
+ Revision 1.22 2008/11/26 21:21:25 kitno-guest
+ * backend/ *.[ch]: nearly every backend used V_MAJOR
+ instead of SANE_CURRENT_MAJOR in sane_init()
+ * backend/snapscan.c: remove EXPECTED_VERSION check
+ since new SANE standard is forward compatible
+
+ Revision 1.21 2004-10-04 18:09:05 kig-guest
+ Rename global function hp_init_openfd to sanei_hp_init_openfd
+
+ Revision 1.20 2004/03/27 13:52:39 kig-guest
+ Keep USB-connection open (was problem with Linux 2.6.x)
+
+
+ V 1.05:
+ Revision 1.19 2003/10/24 17:26:07 kig-guest
+ Use new sanei-thread-interface
+
+ Revision 1.18 2003/10/09 19:37:29 kig-guest
+ Redo when TEST UNIT READY failed
+ Redo when read returns with 0 bytes (non-SCSI only)
+ Bug #300241: fix invers image on 3c/4c/6100C at 10 bit depth
+
+ Revision 1.17 2003/10/06 19:54:07 kig-guest
+ Bug #300248: correct "Negatives" to "Negative" in option description
+
+
+ V 1.04, 24-Jul-2003, PK (peter@kirchgessner.net)
+ - Add internationalization
+
+ V 1.03, 14-Apr-2003, PK (peter@kirchgessner.net)
+ - check valp in call of sane_control_option()
+
+ V 1.02, 02-Feb-2003, PK (peter@kirchgessner.net)
+ - add OS/2-support by Franz Bakan
+
+ V 1.01, 06-Dec-2002, PK (peter@kirchgessner.net)
+ - add option dumb-read to work around problems
+ with BusLogic SCSI driver (error during device I/O)
+
+ V 1.00, 17-Nov-2002, PK (peter@kirchgessner.net)
+ - add libusb support
+
+ V 0.96, 05-Aug-2002, PK (peter@kirchgessner.net)
+ - check USB device names
+
+ V 0.95, 07-Jul-2001, PK (peter@kirchgessner.net)
+ - add support for active XPA
+ - check if paper in ADF for ADF scan
+ - add option lamp off
+ - remove some really unused parameters
+
+ V 0.94, 31-Dec-2000, PK (peter@kirchgessner.net)
+ - always switch off lamp after scan
+
+ V 0.93, 04-Dec-2000, PK (peter@kirchgessner.net)
+ - fix problem with ADF-support on ScanJet 6350 (and maybe others)
+
+ V 0.92, 03-Oct-2000, Rupert W. Curwen (rcurwen@uk.research.att.com):
+ - try to not allocate accessors twice (only for accessors
+ that have fixed length)
+ - fix problem with leaving connection open for some error conditions
+
+ V 0.91, 04-Sep-2000, David Paschal (paschal@rcsis.com):
+ - Added support for flatbed HP OfficeJets
+ - (PK) fix problem with cancel preview
+
+ V 0.90, 02-Sep-2000, PK:
+ - fix timing problem between killing child and writing to pipe
+ - change fprintf(stderr,...) to DBG
+ - change include <sane..> to "sane.." in hp.h
+ - change handling of options that have global effects.
+ i.e. if option scanmode is received (has global effect),
+ all options that "may change" are send to the scanner again.
+ This fixes a problem that --resolution specified infront of
+ --mode on command line of scanimage was ignored.
+ NOTE: This change does not allow to specify --depth 12 infront of
+ --mode color, because --depth is only enabled with --mode color.
+ - add depth greater 8 bits for mode grayscale
+ - add option for 8 bit output but 10/12 bit scanning
+ V 0.88, 25-Jul-2000, PK:
+ - remove inlines
+ V 0.88, 20-Jul-2000, PK:
+ - Use sanei_config_read()
+ - dont write chars < 32 to DBG
+ V 0.88, 09-Jul-2000, PK:
+ - Add front button support by Chris S. Cowles, Houston, Texas,
+ c_cowles@ieee.org
+ V 0.87, 28-Jun-2000, PK:
+ - ADF-support for ScanJet IIp
+ - Return error SANE_STATUS_NO_DOCS if no paper in ADF
+ V 0.86, 12-Feb-2000, PK:
+ - fix gcc warnings
+ - fix problems with bitdepths > 8
+ - allow hp_data_resize to be called with newsize==bufsiz
+ (Jens Heise, <heisbeee@calvados.zrz.TU-Berlin.DE>)
+ - add option enable-image-buffering
+ V 0.85, 30-Jan-2000, PK:
+ - correct and enhace data widths > 8 (Ewald de Wit <ewald@pobox.com>)
+ - enable data width for all scanners
+ - PhotoSmart: exposure "Off" changed to "Default"
+ - PhotoSmart: even if max. datawidth 24 is reported, allow 30 bits.
+ - change keyword -data-width to -depth and use value for bits per sample
+ - change keyword -halftone-type to -halftone-pattern
+ - change keyword -scantype to -source
+ - fix problem with multiple definition of sanei_debug_hp
+ V 0.83, 04-Jul-99, PK:
+ - reset scanner before downloading parameters (fixes problem
+ with sleep mode of scanners)
+ - fix problem with coredump if non-scanner HP SCSI devices
+ are connected (CDR)
+ - option scan-from-adf replaced by scantype normal/adf/xpa
+ - change value "Film strip" to "Film-strip" for option
+ --media-type
+ - PhotoScanner: allow only scanning at multiple of 300 dpi
+ for scanning slides/film strips. This also fixes a problem with the
+ preview which uses arbitrary resolutions.
+ - Marian Szebenyi: close pipe (endless loop on Digital UNIX)
+
+ V 0.82, 28-Feb-99, Ewald de Wit <ewald@pobox.com>:
+ - add options 'exposure time' and 'data width'
+
+ V 0.81, 11-Jan-99, PK:
+ - occasionally 'scan from ADF' was active for Photoscanner
+
+ V 0.80, 10-Jan-99, PK:
+ - fix problem with scan size for ADF-scan
+ (thanks to Christop Biardzki <cbi@allgaeu.org> for tests)
+ - add option "unload after scan" for HP PhotoScanner
+ - no blanks in command line options
+ - fix problem with segmentation fault for scanimage -d hp:/dev/sga
+ with /dev/sga not included in hp.conf
+
+ V 0.72, 25-Dec-98, PK:
+ - add patches from mike@easysw.com to fix problems:
+ - core dumps by memory alignment
+ - config file to accept matching devices (scsi HP)
+ - add simulation for brightness/contrast/custom gamma table
+ if not supported by scanner
+ - add configuration options for connect-...
+
+ V 0.72c, 04-Dec-98, PK:
+ - use sanei_pio
+ - try ADF support
+
+ V 0.72b, 29-Nov-98 James Carter <james@cs.york.ac.uk>, PK:
+ - try to add parallel scanner support
+
+ V 0.71, 14-Nov-98 PK:
+ - add HP 6200 C
+ - cleanup hp_scsi_s structure
+ - show calibrate button on photoscanner only for print media
+ - suppress halftone mode on photoscanner
+ - add media selection for photoscanner
+
+ V 0.70, 26-Jul-98 PK:
+ - Rename global symbols to sanei_...
+ Change filenames to hp-...
+ Use backend name hp
+
+ V 0.65, 18-Jul-98 PK:
+ - Dont use pwd.h for VACPP-Compiler to get home-directory,
+ check $SANE_HOME_XHP instead
+
+ V 0.64, 12-Jul-98 PK:
+ - only download calibration file for media = 1 (prints)
+ - Changes for VACPP-Compiler (check macros __IBMC__, __IBMCPP__)
+
+ V 0.63, 07-Jun-98 PK:
+ - fix problem with custom gamma table
+ - Add unload button
+
+ V 0.62, 25-May-98 PK:
+ - make it compilable under sane V 0.73
+
+ V 0.61, 28-Mar-98, Peter Kirchgessner <pkirchg@aol.com>:
+ - Add support for HP PhotoSmart Photoscanner
+ - Use more inquiries to see what the scanner supports
+ - Add options: calibrate/Mirror horizontal+vertical
+ - Upload/download calibration data
+*/
+
+#define VERSIO 8
+
+#include "../include/sane/config.h"
+#include "hp.h"
+
+#include <string.h>
+/* #include <sys/types.h> */
+/* #include "../include/sane/sane.h" */
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_thread.h"
+/* #include "../include/sane/sanei_debug.h" */
+#include "hp-device.h"
+#include "hp-handle.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#ifndef NDEBUG
+#include <ctype.h>
+void
+sanei_hp_dbgdump (const void * bufp, size_t len)
+{
+ const hp_byte_t *buf = bufp;
+ int offset = 0;
+ int i;
+ char line[128], pt[32];
+
+ for (offset = 0; offset < (int)len; offset += 16)
+ {
+ sprintf (line," 0x%04X ", offset);
+ for (i = offset; i < offset + 16 && i < (int)len; i++)
+ {
+ sprintf (pt," %02X", buf[i]);
+ strcat (line, pt);
+ }
+ while (i++ < offset + 16)
+ strcat (line, " ");
+ strcat (line, " ");
+ for (i = offset; i < offset + 16 && i < (int)len; i++)
+ {
+ sprintf (pt, "%c", isprint(buf[i]) ? buf[i] : '.');
+ strcat (line, pt);
+ }
+ DBG(16,"%s\n",line);
+ }
+}
+
+#endif
+
+typedef struct info_list_el_s * HpDeviceInfoList;
+struct info_list_el_s
+{
+ HpDeviceInfoList next;
+ HpDeviceInfo info;
+};
+
+typedef struct device_list_el_s * HpDeviceList;
+struct device_list_el_s
+{
+ HpDeviceList next;
+ HpDevice dev;
+};
+
+/* Global state */
+static struct hp_global_s {
+ hp_bool_t is_up;
+ hp_bool_t config_read;
+
+ const SANE_Device ** devlist;
+
+ HpDeviceList device_list;
+ HpDeviceList handle_list;
+ HpDeviceInfoList infolist;
+
+ HpDeviceConfig config;
+} global;
+
+
+/* Get the info structure for a device. If not available in global list */
+/* add new entry and return it */
+static HpDeviceInfo *
+hp_device_info_create (const char *devname)
+
+{
+ HpDeviceInfoList *infolist = &(global.infolist);
+ HpDeviceInfoList infolistelement;
+ HpDeviceInfo *info;
+ int k, found;
+
+ if (!global.is_up) return 0;
+
+ found = 0;
+ infolistelement = 0;
+ info = 0;
+ while (*infolist)
+ {
+ infolistelement = *infolist;
+ info = &(infolistelement->info);
+ if (strcmp (info->devname, devname) == 0) /* Already in list ? */
+ {
+ found = 1;
+ break;
+ }
+ infolist = &(infolistelement->next);
+ }
+
+ if (found) /* Clear old entry */
+ {
+ memset (infolistelement, 0, sizeof (*infolistelement));
+ }
+ else /* New element */
+ {
+ infolistelement = (HpDeviceInfoList)
+ sanei_hp_allocz (sizeof (*infolistelement));
+ if (!infolistelement) return 0;
+ info = &(infolistelement->info);
+ *infolist = infolistelement;
+ }
+
+ k = sizeof (info->devname);
+ strncpy (info->devname, devname, k);
+ info->devname[k-1] = '\0';
+ info->max_model = -1;
+ info->active_xpa = -1;
+
+ return info;
+}
+
+static void
+hp_init_config (HpDeviceConfig *config)
+
+{
+ if (config)
+ {
+ config->connect = HP_CONNECT_SCSI;
+ config->use_scsi_request = 1;
+ config->use_image_buffering = 0;
+ config->got_connect_type = 0;
+ config->dumb_read = 0;
+ }
+}
+
+static HpDeviceConfig *
+hp_global_config_get (void)
+
+{
+ if (!global.is_up) return 0;
+ return &(global.config);
+}
+
+static SANE_Status
+hp_device_config_add (const char *devname)
+
+{
+ HpDeviceInfo *info;
+ HpDeviceConfig *config;
+
+ info = hp_device_info_create (devname);
+ if (!info) return SANE_STATUS_INVAL;
+
+ config = hp_global_config_get ();
+
+ if (config)
+ {
+ memcpy (&(info->config), config, sizeof (info->config));
+ info->config_is_up = 1;
+ }
+ else /* Initialize with default configuration */
+ {
+ DBG(3, "hp_device_config_add: No configuration found for device %s.\n\tUseing default\n",
+ devname);
+ hp_init_config (&(info->config));
+ info->config_is_up = 1;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+HpDeviceInfo *
+sanei_hp_device_info_get (const char *devname)
+
+{
+ HpDeviceInfoList *infolist;
+ HpDeviceInfoList infolistelement;
+ HpDeviceInfo *info;
+ int retries = 1;
+
+ if (!global.is_up)
+ {
+ DBG(17, "sanei_hp_device_info_get: global.is_up = %d\n", (int)global.is_up);
+ return 0;
+ }
+
+ DBG(250, "sanei_hp_device_info_get: searching %s\n", devname);
+ do
+ {
+ infolist = &(global.infolist);
+ while (*infolist)
+ {
+ infolistelement = *infolist;
+ info = &(infolistelement->info);
+ DBG(250, "sanei_hp_device_info_get: check %s\n", info->devname);
+ if (strcmp (info->devname, devname) == 0) /* Found ? */
+ {
+ return info;
+ }
+ infolist = &(infolistelement->next);
+ }
+
+ /* No configuration found. Assume default */
+ DBG(1, "hp_device_info_get: device %s not configured. Using default\n",
+ devname);
+ if (hp_device_config_add (devname) != SANE_STATUS_GOOD)
+ return 0;
+ }
+ while (retries-- > 0);
+
+ return 0;
+}
+
+HpDevice
+sanei_hp_device_get (const char *devname)
+{
+ HpDeviceList ptr;
+
+ for (ptr = global.device_list; ptr; ptr = ptr->next)
+ if (strcmp(sanei_hp_device_sanedevice(ptr->dev)->name, devname) == 0)
+ return ptr->dev;
+
+ return 0;
+}
+
+static void
+hp_device_info_remove (void)
+{
+ HpDeviceInfoList next, infolistelement = global.infolist;
+ HpDeviceInfo *info;
+
+ if (!global.is_up) return;
+
+ while (infolistelement)
+ {
+ info = &(infolistelement->info);
+ next = infolistelement->next;
+ sanei_hp_free (infolistelement);
+ infolistelement = next;
+ }
+}
+
+static SANE_Status
+hp_device_list_add (HpDeviceList * list, HpDevice dev)
+{
+ HpDeviceList new = sanei_hp_alloc(sizeof(*new));
+
+ if (!new)
+ return SANE_STATUS_NO_MEM;
+ while (*list)
+ list = &(*list)->next;
+
+ *list = new;
+ new->next = 0;
+ new->dev = dev;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_device_list_remove (HpDeviceList * list, HpDevice dev)
+{
+ HpDeviceList old;
+
+ while (*list && (*list)->dev != dev)
+ list = &(*list)->next;
+
+ if (!*list)
+ return SANE_STATUS_INVAL;
+
+ old = *list;
+ *list = (*list)->next;
+ sanei_hp_free(old);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_handle_list_add (HpDeviceList * list, HpHandle h)
+{
+ return hp_device_list_add(list, (HpDevice)h);
+}
+
+static SANE_Status
+hp_handle_list_remove (HpDeviceList * list, HpHandle h)
+{
+ return hp_device_list_remove(list, (HpDevice)h);
+}
+
+
+
+
+static SANE_Status
+hp_init (void)
+{
+ memset(&global, 0, sizeof(global));
+ global.is_up++;
+ DBG(3, "hp_init: global.is_up = %d\n", (int)global.is_up);
+ return SANE_STATUS_GOOD;
+}
+
+static void
+hp_destroy (void)
+{
+ if (global.is_up)
+ {
+ /* Close open handles */
+ while (global.handle_list)
+ sane_close(global.handle_list->dev);
+
+ /* Remove device infos */
+ hp_device_info_remove ();
+
+ sanei_hp_free_all();
+ global.is_up = 0;
+ DBG(3, "hp_destroy: global.is_up = %d\n", (int)global.is_up);
+ }
+}
+
+static SANE_Status
+hp_get_dev (const char *devname, HpDevice* devp)
+{
+ HpDeviceList ptr;
+ HpDevice new;
+ const HpDeviceInfo *info;
+ char *connect;
+ HpConnect hp_connect;
+ SANE_Status status;
+
+ for (ptr = global.device_list; ptr; ptr = ptr->next)
+ if (strcmp(sanei_hp_device_sanedevice(ptr->dev)->name, devname) == 0)
+ {
+ if (devp)
+ *devp = ptr->dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ info = sanei_hp_device_info_get (devname);
+ hp_connect = info->config.connect;
+
+ if (hp_connect == HP_CONNECT_SCSI) connect = "scsi";
+ else if (hp_connect == HP_CONNECT_DEVICE) connect = "device";
+ else if (hp_connect == HP_CONNECT_PIO) connect = "pio";
+ else if (hp_connect == HP_CONNECT_USB) connect = "usb";
+ else if (hp_connect == HP_CONNECT_RESERVE) connect = "reserve";
+ else connect = "unknown";
+
+ DBG(3, "hp_get_dev: New device %s, connect-%s, scsi-request=%lu\n",
+ devname, connect, (unsigned long)info->config.use_scsi_request);
+
+ if (!ptr)
+ {
+ status = sanei_hp_device_new (&new, devname);
+
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ if (devp)
+ *devp = new;
+
+ RETURN_IF_FAIL( hp_device_list_add(&global.device_list, new) );
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_attach (const char *devname)
+{
+ DBG(7,"hp_attach: \"%s\"\n", devname);
+ hp_device_config_add (devname);
+ return hp_get_dev (devname, 0);
+}
+
+static void
+hp_attach_matching_devices (HpDeviceConfig *config, const char *devname)
+{
+ static int usb_initialized = 0;
+
+ if (strncmp (devname, "usb", 3) == 0)
+ {
+ config->connect = HP_CONNECT_USB;
+ config->use_scsi_request = 0;
+ DBG(1,"hp_attach_matching_devices: usb attach matching \"%s\"\n",devname);
+ if (!usb_initialized)
+ {
+ sanei_usb_init ();
+ usb_initialized = 1;
+ }
+ sanei_usb_attach_matching_devices (devname, hp_attach);
+ }
+ else
+ {
+ DBG(1, "hp_attach_matching_devices: attach matching %s\n", devname);
+ sanei_config_attach_matching_devices (devname, hp_attach);
+ }
+}
+
+static SANE_Status
+hp_read_config (void)
+{
+ FILE * fp;
+ char buf[PATH_MAX], arg1[PATH_MAX], arg2[PATH_MAX], arg3[PATH_MAX];
+ int nl, nargs;
+ HpDeviceConfig *config, df_config, dev_config;
+ hp_bool_t is_df_config;
+ char cu_device[PATH_MAX];
+
+ if (!global.is_up)
+ return SANE_STATUS_INVAL;
+ if (global.config_read)
+ return SANE_STATUS_GOOD;
+
+ /* The default config will keep options set up until the first device is specified */
+ hp_init_config (&df_config);
+ config = &df_config;
+ is_df_config = 1;
+ cu_device[0] = '\0';
+
+ DBG(1, "hp_read_config: hp backend v%s/%s starts reading config file\n",
+ hp_backend_version, hp_backend_revision);
+
+ if ((fp = sanei_config_open(HP_CONFIG_FILE)) != 0)
+ {
+ while (sanei_config_read(buf, sizeof(buf), fp))
+ {
+ char *dev_name;
+
+ nl = strlen (buf);
+ while (nl > 0)
+ {
+ nl--;
+ if ( (buf[nl] == ' ') || (buf[nl] == '\t')
+ || (buf[nl] == '\r') || (buf[nl] == '\n'))
+ buf[nl] = '\0';
+ else
+ break;
+ }
+
+ DBG(1, "hp_read_config: processing line <%s>\n", buf);
+
+ nargs = sscanf (buf, "%s%s%s", arg1, arg2, arg3);
+ if ((nargs <= 0) || (arg1[0] == '#')) continue;
+
+ /* Option to process ? */
+ if ((strcmp (arg1, "option") == 0) && (nargs >= 2))
+ {
+ if (strcmp (arg2, "connect-scsi") == 0)
+ {
+ config->connect = HP_CONNECT_SCSI;
+ config->got_connect_type = 1;
+ }
+ else if (strcmp (arg2, "connect-device") == 0)
+ {
+ config->connect = HP_CONNECT_DEVICE;
+ config->got_connect_type = 1;
+ config->use_scsi_request = 0;
+ }
+ else if (strcmp (arg2, "connect-pio") == 0)
+ {
+ config->connect = HP_CONNECT_PIO;
+ config->got_connect_type = 1;
+ config->use_scsi_request = 0;
+ }
+ else if (strcmp (arg2, "connect-usb") == 0)
+ {
+ config->connect = HP_CONNECT_USB;
+ config->got_connect_type = 1;
+ config->use_scsi_request = 0;
+ }
+ else if (strcmp (arg2, "connect-reserve") == 0)
+ {
+ config->connect = HP_CONNECT_RESERVE;
+ config->got_connect_type = 1;
+ config->use_scsi_request = 0;
+ }
+ else if (strcmp (arg2, "disable-scsi-request") == 0)
+ {
+ config->use_scsi_request = 0;
+ }
+ else if (strcmp (arg2, "enable-image-buffering") == 0)
+ {
+ config->use_image_buffering = 1;
+ }
+ else if (strcmp (arg2, "dumb-read") == 0)
+ {
+ config->dumb_read = 1;
+ }
+ else
+ {
+ DBG(1,"hp_read_config: Invalid option %s\n", arg2);
+ }
+ }
+ else /* No option. This is the start of a new device */
+ {
+ if (is_df_config) /* Did we only read default configurations ? */
+ {
+ is_df_config = 0; /* Stop reading default config */
+ /* Initialize device config with default-config */
+ memcpy (&dev_config, &df_config, sizeof (dev_config));
+ config = &dev_config; /* Start reading a device config */
+ }
+ if (cu_device[0] != '\0') /* Did we work on a device ? */
+ {
+ memcpy (hp_global_config_get(), &dev_config,sizeof (dev_config));
+ hp_attach_matching_devices (hp_global_config_get(), cu_device);
+ cu_device[0] = '\0';
+ }
+
+ /* Initialize new device with default config */
+ memcpy (&dev_config, &df_config, sizeof (dev_config));
+
+ /* Cut off leading blanks of device name */
+ dev_name = buf+strspn (buf, " \t\n\r");
+ strcpy (cu_device, dev_name); /* Save the device name */
+ }
+ }
+ if (cu_device[0] != '\0') /* Did we work on a device ? */
+ {
+ memcpy (hp_global_config_get (), &dev_config, sizeof (dev_config));
+ DBG(1, "hp_read_config: attach %s\n", cu_device);
+ hp_attach_matching_devices (hp_global_config_get (), cu_device);
+ cu_device[0] = '\0';
+ }
+ fclose (fp);
+ DBG(1, "hp_read_config: reset to default config\n");
+ memcpy (hp_global_config_get (), &df_config, sizeof (df_config));
+ }
+ else
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ char *dev_name = "/dev/scanner";
+
+ memcpy (hp_global_config_get (), &df_config, sizeof (df_config));
+ hp_attach_matching_devices (hp_global_config_get (), dev_name);
+ }
+
+ global.config_read++;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_update_devlist (void)
+{
+ HpDeviceList devp;
+ const SANE_Device **devlist;
+ int count = 0;
+
+ RETURN_IF_FAIL( hp_read_config() );
+
+ if (global.devlist)
+ sanei_hp_free(global.devlist);
+
+ for (devp = global.device_list; devp; devp = devp->next)
+ count++;
+
+ if (!(devlist = sanei_hp_alloc((count + 1) * sizeof(*devlist))))
+ return SANE_STATUS_NO_MEM;
+
+ global.devlist = devlist;
+
+ for (devp = global.device_list; devp; devp = devp->next)
+ *devlist++ = sanei_hp_device_sanedevice(devp->dev);
+ *devlist = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ *
+ */
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback UNUSEDARG authorize)
+{SANE_Status status;
+
+ DBG_INIT();
+ DBG(3, "sane_init called\n");
+ sanei_thread_init ();
+
+ sanei_hp_init_openfd ();
+ hp_destroy();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, VERSIO);
+
+ status = hp_init();
+ DBG(3, "sane_init will finish with %s\n", sane_strstatus (status));
+ return status;
+}
+
+void
+sane_exit (void)
+{
+ DBG(3, "sane_exit called\n");
+ hp_destroy();
+ DBG(3, "sane_exit will finish\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list,
+ SANE_Bool UNUSEDARG local_only)
+{
+ DBG(3, "sane_get_devices called\n");
+
+ RETURN_IF_FAIL( hp_update_devlist() );
+ *device_list = global.devlist;
+ DBG(3, "sane_get_devices will finish with %s\n",
+ sane_strstatus (SANE_STATUS_GOOD));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle *handle)
+{
+ HpDevice dev = 0;
+ HpHandle h;
+
+ DBG(3, "sane_open called\n");
+
+ RETURN_IF_FAIL( hp_read_config() );
+
+ if (devicename[0])
+ RETURN_IF_FAIL( hp_get_dev(devicename, &dev) );
+ else
+ {
+ /* empty devicname -> use first device */
+ if (global.device_list)
+ dev = global.device_list->dev;
+ }
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (!(h = sanei_hp_handle_new(dev)))
+ return SANE_STATUS_NO_MEM;
+
+ RETURN_IF_FAIL( hp_handle_list_add(&global.handle_list, h) );
+
+ *handle = h;
+ DBG(3, "sane_open will finish with %s\n", sane_strstatus (SANE_STATUS_GOOD));
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ HpHandle h = handle;
+
+ DBG(3, "sane_close called\n");
+
+ if (!FAILED( hp_handle_list_remove(&global.handle_list, h) ))
+ sanei_hp_handle_destroy(h);
+
+ DBG(3, "sane_close will finish\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int optnum)
+{
+ HpHandle h = handle;
+ const SANE_Option_Descriptor *optd;
+
+ DBG(10, "sane_get_option_descriptor called\n");
+
+ optd = sanei_hp_handle_saneoption(h, optnum);
+
+ DBG(10, "sane_get_option_descriptor will finish\n");
+
+ return optd;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int optnum,
+ SANE_Action action, void *valp, SANE_Int *info)
+{
+ HpHandle h = handle;
+ SANE_Status status;
+
+ DBG(10, "sane_control_option called\n");
+
+ status = sanei_hp_handle_control(h, optnum, action, valp, info);
+
+ DBG(10, "sane_control_option will finish with %s\n",
+ sane_strstatus (status));
+ return status;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
+{
+ HpHandle h = handle;
+ SANE_Status status;
+
+ DBG(10, "sane_get_parameters called\n");
+
+ status = sanei_hp_handle_getParameters(h, params);
+
+ DBG(10, "sane_get_parameters will finish with %s\n",
+ sane_strstatus (status));
+ return status;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ HpHandle h = handle;
+ SANE_Status status;
+
+ DBG(3, "sane_start called\n");
+
+ status = sanei_hp_handle_startScan(h);
+
+ DBG(3, "sane_start will finish with %s\n", sane_strstatus (status));
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
+{
+ HpHandle h = handle;
+ size_t length = max_len;
+ SANE_Status status;
+
+ DBG(16, "sane_read called\n");
+
+ status = sanei_hp_handle_read(h, buf, &length);
+ *len = length;
+
+ DBG(16, "sane_read will finish with %s\n", sane_strstatus (status));
+ return status;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ HpHandle h = handle;
+
+ DBG(3, "sane_cancel called\n");
+
+ sanei_hp_handle_cancel(h);
+
+ DBG(3, "sane_cancel will finish\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ HpHandle h = handle;
+ SANE_Status status;
+
+ DBG(3, "sane_set_io_mode called\n");
+
+ status = sanei_hp_handle_setNonblocking(h, non_blocking);
+
+ DBG(3, "sane_set_io_mode will finish with %s\n",
+ sane_strstatus (status));
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+ HpHandle h = handle;
+ SANE_Status status;
+
+ DBG(10, "sane_get_select_fd called\n");
+
+ status = sanei_hp_handle_getPipefd(h, fd);
+
+ DBG(10, "sane_get_select_fd will finish with %s\n",
+ sane_strstatus (status));
+ return status;
+}
diff --git a/backend/hp.conf.in b/backend/hp.conf.in
new file mode 100644
index 0000000..0db154f
--- /dev/null
+++ b/backend/hp.conf.in
@@ -0,0 +1,21 @@
+scsi HP
+# Uncomment the following if you have "Error during device I/O" on SCSI
+# option dumb-read
+#
+# The usual place for a SCSI-scanner on Linux
+/dev/scanner
+#
+# USB-scanners supported by the hp-backend
+# HP ScanJet 4100C
+usb 0x03f0 0x0101
+# HP ScanJet 5200C
+usb 0x03f0 0x0401
+# HP ScanJet 62X0C
+usb 0x03f0 0x0201
+# HP ScanJet 63X0C
+usb 0x03f0 0x0601
+#
+# Uncomment the following if your scanner is connected by USB,
+# but you are not using libusb
+# /dev/usb/scanner0
+# option connect-device
diff --git a/backend/hp.h b/backend/hp.h
new file mode 100644
index 0000000..b86ee04
--- /dev/null
+++ b/backend/hp.h
@@ -0,0 +1,226 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+#ifndef HP_H_INCLUDED
+#define HP_H_INCLUDED
+#include <limits.h>
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+
+#undef BACKEND_NAME
+#define BACKEND_NAME hp
+#define DEBUG_NOT_STATIC
+#include "../include/sane/sanei_debug.h"
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+/* FIXME: these should be options? */
+#undef ENABLE_7x12_TONEMAPS
+#define ENABLE_16x16_DITHERS
+#define ENABLE_10BIT_MATRIXES
+
+#define FAKE_COLORSEP_MATRIXES
+
+#undef ENABLE_CUSTOM_MATRIX
+
+#define HP_CONFIG_FILE STRINGIFY(BACKEND_NAME) ".conf"
+
+#define DEVPIX_PER_INCH 300.0
+#define MM_PER_DEVPIX (MM_PER_INCH / DEVPIX_PER_INCH)
+
+/*
+ * Macros for testing return values of functions which
+ * return SANE_Status types.
+ */
+#define FAILED(status) (status != SANE_STATUS_GOOD)
+#define UNSUPPORTED(status) (status == SANE_STATUS_UNSUPPORTED)
+#define RETURN_IF_FAIL(try) do { \
+ SANE_Status status = (try); \
+ if (FAILED(status)) \
+ return status; \
+} while (0)
+#define CATCH_RET_FAIL(try, catch) do { \
+ SANE_Status status = (try); \
+ if (FAILED(status)) { (catch); return status; } \
+} while (0)
+
+#ifndef DBG_LEVEL
+#define DBG_LEVEL PASTE(sanei_debug_, BACKEND_NAME)
+#endif
+#ifndef NDEBUG
+# define DBGDUMP(level, buf, size) \
+ do { if (DBG_LEVEL >= (level)) sanei_hp_dbgdump(buf, size); } while (0)
+#else
+# define DBGDUMP(level, buf, size)
+#endif
+
+
+ /*
+ *
+ */
+
+typedef unsigned int hp_bool_t;
+typedef unsigned char hp_byte_t;
+
+typedef enum { HP_CONNECT_SCSI, HP_CONNECT_DEVICE,
+ HP_CONNECT_PIO, HP_CONNECT_USB, HP_CONNECT_RESERVE } HpConnect;
+
+typedef struct
+{
+ HpConnect connect;
+ hp_bool_t got_connect_type;
+ hp_bool_t use_scsi_request;
+ hp_bool_t use_image_buffering;
+ hp_bool_t dumb_read;
+} HpDeviceConfig;
+
+#define HP_SCL_INQID_MIN 10306
+#define HP_SCL_INQID_MAX 10971
+
+typedef struct
+{
+ hp_bool_t checked, is_supported;
+ int minval, maxval;
+} HpSclSupport;
+
+typedef struct
+{ /* Flags for simulated commands */
+ hp_bool_t sclsimulate[HP_SCL_INQID_MAX - HP_SCL_INQID_MIN + 1];
+ hp_bool_t gamma_simulate;
+ unsigned char brightness_map[256]; /* Map to simulate brightness level */
+ unsigned char contrast_map[256]; /* Map to simulate contrast level */
+ unsigned char gamma_map[256]; /* Map to simulate custom gamma table */
+} HpSimulate;
+
+/* Information about a connected device */
+typedef struct
+{
+ char devname[64]; /* unique device name */
+
+ hp_bool_t config_is_up; /* flag if config-struct is valid */
+ HpDeviceConfig config; /* device configuration*/
+ /* List of command support */
+ HpSclSupport sclsupport[HP_SCL_INQID_MAX - HP_SCL_INQID_MIN + 1];
+
+ HpSimulate simulate; /* Info about simulations */
+
+ hp_bool_t unload_after_scan;
+ int active_xpa;
+ int max_model;
+} HpDeviceInfo;
+
+HpDeviceInfo *sanei_hp_device_info_get (const char *dev_name);
+
+/* hp-scl.c */
+#if INT_MAX > 30000
+typedef int HpScl;
+#else
+typedef long int HpScl;
+#endif
+
+void sanei_hp_init_openfd (void);
+
+typedef struct
+{
+ int lines;
+ int bytes_per_line; /* as received from scanner */
+ int bits_per_channel;
+ hp_bool_t out8; /* This flag is set and only set, when data with */
+ /* depths > 8 is to be mapped to 8 bit output. */
+ hp_bool_t mirror_vertical;
+ hp_bool_t invert;
+ HpScl startscan;
+} HpProcessData;
+
+/* hp-option.c */
+typedef SANE_Option_Descriptor * HpSaneOption;
+
+typedef const struct hp_choice_s * HpChoice;
+typedef struct hp_option_s * HpOption;
+typedef const struct hp_option_descriptor_s * HpOptionDescriptor;
+typedef struct hp_optset_s * HpOptSet;
+
+/* hp-accessor.c */
+typedef struct hp_data_s * HpData;
+typedef struct hp_accessor_s * HpAccessor;
+typedef struct hp_accessor_vector_s * HpAccessorVector;
+typedef struct hp_accessor_choice_s * HpAccessorChoice;
+
+/* hp-device.c */
+typedef struct hp_device_s * HpDevice;
+hp_bool_t sanei_hp_device_simulate_get (const char *devname, HpScl scl);
+HpDevice sanei_hp_device_get (const char *dev_name);
+
+/* hp-handle.c */
+typedef struct hp_handle_s * HpHandle;
+
+/* hp-scsi.c */
+typedef struct hp_scsi_s * HpScsi;
+
+/* hp-scl.c */
+hp_bool_t sanei_hp_is_active_xpa (HpScsi scsi);
+int sanei_hp_get_max_model (HpScsi scsi);
+int sanei_hp_is_flatbed_adf (HpScsi scsi);
+HpConnect sanei_hp_get_connect (const char *devname);
+HpConnect sanei_hp_scsi_get_connect (HpScsi this);
+
+/* hp.c */
+#ifndef NDEBUG
+void sanei_hp_dbgdump (const void * bufp, size_t len);
+#endif
+
+/* hp-hpmem.c */
+void * sanei_hp_alloc(size_t sz);
+void * sanei_hp_allocz(size_t sz);
+void * sanei_hp_memdup(const void * src, size_t sz);
+char * sanei_hp_strdup(const char * str);
+void * sanei_hp_realloc(void * ptr, size_t sz);
+void sanei_hp_free(void * ptr);
+void sanei_hp_free_all(void);
+
+#endif /* HP_H_INCLUDED */
diff --git a/backend/hp3500.c b/backend/hp3500.c
new file mode 100644
index 0000000..48a8035
--- /dev/null
+++ b/backend/hp3500.c
@@ -0,0 +1,3506 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ This file implements a SANE backend for HP ScanJet 3500 series scanners.
+ Currently supported:
+ - HP ScanJet 3500C
+ - HP ScanJet 3530C
+ - HP ScanJet 3570C
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+
+ There are some device specific routines in this file that are in "#if 0"
+ sections - these are left in place for documentation purposes in case
+ somebody wants to implement features that use those routines.
+
+*/
+
+/* ------------------------------------------------------------------------- */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_LIBC_H
+# include <libc.h> /* NeXTStep/OpenStep */
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+#include "../include/sane/sanei_backend.h"
+
+#define RTCMD_GETREG 0x80
+#define RTCMD_READSRAM 0x81
+
+#define RTCMD_SETREG 0x88
+#define RTCMD_WRITESRAM 0x89
+
+#define RTCMD_NVRAMCONTROL 0x8a
+
+#define RTCMD_BYTESAVAIL 0x90
+#define RTCMD_READBYTES 0x91
+
+#define RT_CHANNEL_ALL 0
+#define RT_CHANNEL_RED 1
+#define RT_CHANNEL_GREEN 2
+#define RT_CHANNEL_BLUE 3
+
+typedef int (*rts8801_callback) (void *param, unsigned bytes, void *data);
+
+#define DEBUG 1
+#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX(number * MM_PER_INCH / 1200)
+#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) * 1200 / MM_PER_INCH
+
+#define MSG_ERR 1
+#define MSG_USER 5
+#define MSG_INFO 6
+#define FLOW_CONTROL 10
+#define MSG_IO 15
+#define MSG_IO_READ 17
+#define IO_CMD 20
+#define IO_CMD_RES 20
+#define MSG_GET 25
+/* ------------------------------------------------------------------------- */
+
+enum hp3500_option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_RESOLUTION,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ NUM_OPTIONS
+};
+
+typedef struct
+{
+ int left;
+ int top;
+ int right;
+ int bottom;
+} hp3500_rect;
+
+struct hp3500_data
+{
+ struct hp3500_data *next;
+ char *devicename;
+
+ int sfd;
+ int pipe_r;
+ int pipe_w;
+ SANE_Pid reader_pid;
+
+ int resolution;
+ int mode;
+
+ time_t last_scan;
+
+ hp3500_rect request_mm;
+ hp3500_rect actual_mm;
+ hp3500_rect fullres_pixels;
+ hp3500_rect actres_pixels;
+
+ int rounded_left;
+ int rounded_top;
+ int rounded_right;
+ int rounded_bottom;
+
+ int bytes_per_scan_line;
+ int scan_width_pixels;
+ int scan_height_pixels;
+
+ int brightness;
+ int contrast;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ SANE_Device sane;
+};
+
+struct hp3500_write_info
+{
+ struct hp3500_data *scanner;
+ int bytesleft;
+};
+
+typedef struct detailed_calibration_data
+{
+ unsigned char const *channeldata[3];
+ unsigned resolution_divisor;
+} detailed_calibration_data;
+
+static struct hp3500_data *first_dev = 0;
+static struct hp3500_data **new_dev = &first_dev;
+static int num_devices = 0;
+static SANE_Int res_list[] =
+ { 9, 50, 75, 100, 150, 200, 300, 400, 600, 1200 };
+static const SANE_Range range_x =
+ { 0, SANE_FIX (215.9), SANE_FIX (MM_PER_INCH / 1200) };
+static const SANE_Range range_y =
+ { 0, SANE_FIX (298.7), SANE_FIX (MM_PER_INCH / 1200) };
+static const SANE_Range range_brightness =
+ { 0, 255, 0 };
+static const SANE_Range range_contrast =
+ { 0, 255, 0 };
+
+
+#define HP3500_COLOR_SCAN 0
+#define HP3500_GRAY_SCAN 1
+#define HP3500_LINEART_SCAN 2
+#define HP3500_TOTAL_SCANS 3
+
+static char const *scan_mode_list[HP3500_TOTAL_SCANS + 1] = { 0 };
+
+static SANE_Status attachScanner (const char *name);
+static SANE_Status init_options (struct hp3500_data *scanner);
+static int reader_process (void *);
+static void calculateDerivedValues (struct hp3500_data *scanner);
+static void do_reset (struct hp3500_data *scanner);
+static void do_cancel (struct hp3500_data *scanner);
+
+/*
+ * used by sane_get_devices
+ */
+static const SANE_Device **devlist = 0;
+
+/*
+ * SANE Interface
+ */
+
+
+/**
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init\n");
+
+ sanei_usb_init ();
+ sanei_thread_init ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ sanei_usb_find_devices (0x03f0, 0x2205, attachScanner);
+ sanei_usb_find_devices (0x03f0, 0x2005, attachScanner);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ int i;
+ struct hp3500_data *dev;
+
+ DBG (10, "sane_get_devices %d\n", local_only);
+
+ if (devlist)
+ free (devlist);
+ devlist = calloc (num_devices + 1, sizeof (SANE_Device *));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = first_dev, i = 0; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Called to establish connection with the scanner. This function will
+ * also establish meaningful defauls and initialize the options.
+ *
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct hp3500_data *dev = NULL;
+ struct hp3500_data *scanner = NULL;
+
+ if (name[0] == 0)
+ {
+ DBG (10, "sane_open: no device requested, using default\n");
+ if (first_dev)
+ {
+ scanner = (struct hp3500_data *) first_dev;
+ DBG (10, "sane_open: device %s found\n", first_dev->sane.name);
+ }
+ }
+ else
+ {
+ DBG (10, "sane_open: device %s requested\n", name);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, name) == 0)
+ {
+ DBG (10, "sane_open: device %s found\n", name);
+ scanner = (struct hp3500_data *) dev;
+ }
+ }
+ }
+
+ if (!scanner)
+ {
+ DBG (10, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *handle = scanner;
+
+ init_options (scanner);
+
+ scanner->resolution = 200;
+ scanner->request_mm.left = 0;
+ scanner->request_mm.top = 0;
+ scanner->request_mm.right = SCANNER_UNIT_TO_FIXED_MM (10200);
+ scanner->request_mm.bottom = SCANNER_UNIT_TO_FIXED_MM (14100);
+ scanner->mode = 0;
+ scanner->brightness = 128;
+ scanner->contrast = 64;
+ calculateDerivedValues (scanner);
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (99, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fdp)
+{
+ struct hp3500_data *scanner = (struct hp3500_data *) h;
+ DBG (10, "sane_get_select_fd\n");
+ *fdp = scanner->pipe_r;
+ DBG (99, "%p %d\n", h, *fdp);
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct hp3500_data *scanner = handle;
+
+ DBG (MSG_GET,
+ "sane_get_option_descriptor: \"%s\"\n", scanner->opt[option].name);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+ return &scanner->opt[option];
+}
+
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct hp3500_data *scanner = (struct hp3500_data *) handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Int dummy;
+ int i;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ *info = 0;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = scanner->opt[option].cap;
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (MSG_GET, "sane_control_option: get value \"%s\"\n",
+ scanner->opt[option].name);
+ DBG (11, "\tcap = %d\n", cap);
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (10, "\tinactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (option)
+ {
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ *(SANE_Word *) val = scanner->resolution;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ *(SANE_Word *) val = scanner->request_mm.left;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *(SANE_Word *) val = scanner->request_mm.top;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *(SANE_Word *) val = scanner->request_mm.right;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *(SANE_Word *) val = scanner->request_mm.bottom;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ strcpy ((SANE_Char *) val, scan_mode_list[scanner->mode]);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (10, "sane_control_option: set value \"%s\"\n",
+ scanner->opt[option].name);
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (10, "\tinactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (10, "\tnot settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (scanner->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (10, "\tbad value\n");
+ return status;
+ }
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ if (scanner->resolution == *(SANE_Word *) val)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ scanner->resolution = (*(SANE_Word *) val);
+ calculateDerivedValues (scanner);
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ if (scanner->request_mm.left == *(SANE_Word *) val)
+ return SANE_STATUS_GOOD;
+ scanner->request_mm.left = *(SANE_Word *) val;
+ calculateDerivedValues (scanner);
+ if (scanner->actual_mm.left != scanner->request_mm.left)
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ if (scanner->request_mm.top == *(SANE_Word *) val)
+ return SANE_STATUS_GOOD;
+ scanner->request_mm.top = *(SANE_Word *) val;
+ calculateDerivedValues (scanner);
+ if (scanner->actual_mm.top != scanner->request_mm.top)
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ if (scanner->request_mm.right == *(SANE_Word *) val)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ scanner->request_mm.right = *(SANE_Word *) val;
+ calculateDerivedValues (scanner);
+ if (scanner->actual_mm.right != scanner->request_mm.right)
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ if (scanner->request_mm.bottom == *(SANE_Word *) val)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ scanner->request_mm.bottom = *(SANE_Word *) val;
+ calculateDerivedValues (scanner);
+ if (scanner->actual_mm.bottom != scanner->request_mm.bottom)
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ for (i = 0; scan_mode_list[i]; ++i)
+ {
+ if (!strcmp ((SANE_Char const *) val, scan_mode_list[i]))
+ {
+ DBG (10, "Setting scan mode to %s (request: %s)\n",
+ scan_mode_list[i], (SANE_Char const *) val);
+ scanner->mode = i;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ /* Impossible */
+ return SANE_STATUS_INVAL;
+
+ case OPT_BRIGHTNESS:
+ scanner->brightness = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ scanner->contrast = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+ } /* switch */
+ } /* else */
+ return SANE_STATUS_INVAL;
+}
+
+/**
+ * Called by SANE when a page acquisition operation is to be started.
+ *
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct hp3500_data *scanner = handle;
+ int defaultFds[2];
+ int ret;
+
+ DBG (10, "sane_start\n");
+
+ if (scanner->sfd < 0)
+ {
+ /* first call */
+ DBG (10, "sane_start opening USB device\n");
+ if (sanei_usb_open (scanner->sane.name, &(scanner->sfd)) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (MSG_ERR,
+ "sane_start: open of %s failed:\n", scanner->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ calculateDerivedValues (scanner);
+
+ DBG (10, "\tbytes per line = %d\n", scanner->bytes_per_scan_line);
+ DBG (10, "\tpixels_per_line = %d\n", scanner->scan_width_pixels);
+ DBG (10, "\tlines = %d\n", scanner->scan_height_pixels);
+
+
+ /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
+ if (pipe (defaultFds) < 0)
+ {
+ DBG (MSG_ERR, "ERROR: could not create pipe\n");
+ do_cancel (scanner);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe_r = defaultFds[0];
+ scanner->pipe_w = defaultFds[1];
+
+ ret = SANE_STATUS_GOOD;
+
+ scanner->reader_pid = sanei_thread_begin (reader_process, scanner);
+ time (&scanner->last_scan);
+
+ if (scanner->reader_pid == -1)
+ {
+ DBG (MSG_ERR, "cannot fork reader process.\n");
+ DBG (MSG_ERR, "%s", strerror (errno));
+ ret = SANE_STATUS_IO_ERROR;
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ close (scanner->pipe_w);
+ }
+
+ if (ret == SANE_STATUS_GOOD)
+ {
+ DBG (10, "sane_start: ok\n");
+ }
+
+ return ret;
+}
+
+
+/**
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct hp3500_data *scanner = (struct hp3500_data *) handle;
+
+
+ DBG (10, "sane_get_parameters\n");
+
+ calculateDerivedValues (scanner);
+
+ params->format =
+ (scanner->mode == HP3500_COLOR_SCAN) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ params->depth = (scanner->mode == HP3500_LINEART_SCAN) ? 1 : 8;
+
+ params->pixels_per_line = scanner->scan_width_pixels;
+ params->lines = scanner->scan_height_pixels;
+
+ params->bytes_per_line = scanner->bytes_per_scan_line;
+
+ params->last_frame = 1;
+ DBG (10, "\tdepth %d\n", params->depth);
+ DBG (10, "\tlines %d\n", params->lines);
+ DBG (10, "\tpixels_per_line %d\n", params->pixels_per_line);
+ DBG (10, "\tbytes_per_line %d\n", params->bytes_per_line);
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Called by SANE to read data.
+ *
+ * In this implementation, sane_read does nothing much besides reading
+ * data from a pipe and handing it back. On the other end of the pipe
+ * there's the reader process which gets data from the scanner and
+ * stuffs it into the pipe.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ struct hp3500_data *scanner = (struct hp3500_data *) handle;
+ ssize_t nread;
+ int source = scanner->pipe_r;
+
+ *len = 0;
+
+ nread = read (source, buf, max_len);
+ DBG (30, "sane_read: read %ld bytes of %ld\n",
+ (long) nread, (long) max_len);
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel (scanner);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0)
+ {
+ close (source);
+ DBG (10, "sane_read: pipe closed\n");
+ return SANE_STATUS_EOF;
+ }
+
+ return SANE_STATUS_GOOD;
+} /* sane_read */
+
+
+/**
+ * Cancels a scan.
+ *
+ * It has been said on the mailing list that sane_cancel is a bit of a
+ * misnomer because it is routinely called to signal the end of a
+ * batch - quoting David Mosberger-Tang:
+ *
+ * > In other words, the idea is to have sane_start() be called, and
+ * > collect as many images as the frontend wants (which could in turn
+ * > consist of multiple frames each as indicated by frame-type) and
+ * > when the frontend is done, it should call sane_cancel().
+ * > Sometimes it's better to think of sane_cancel() as "sane_stop()"
+ * > but that name would have had some misleading connotations as
+ * > well, that's why we stuck with "cancel".
+ *
+ * The current consensus regarding duplex and ADF scans seems to be
+ * the following call sequence: sane_start; sane_read (repeat until
+ * EOF); sane_start; sane_read... and then call sane_cancel if the
+ * batch is at an end. I.e. do not call sane_cancel during the run but
+ * as soon as you get a SANE_STATUS_NO_DOCS.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle h)
+{
+ DBG (10, "sane_cancel\n");
+ do_cancel ((struct hp3500_data *) h);
+}
+
+
+/**
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close\n");
+ do_reset (handle);
+ do_cancel (handle);
+}
+
+
+/**
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_clo-se(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct hp3500_data *dev, *next;
+
+ DBG (10, "sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->devicename);
+ free (dev);
+ }
+
+ if (devlist)
+ free (devlist);
+}
+
+/*
+ * The scanning code
+ */
+
+static SANE_Status
+attachScanner (const char *devicename)
+{
+ struct hp3500_data *dev;
+
+ DBG (15, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+
+ if (NULL == (dev = malloc (sizeof (*dev))))
+ return SANE_STATUS_NO_MEM;
+ memset (dev, 0, sizeof (*dev));
+
+ dev->devicename = strdup (devicename);
+ dev->sfd = -1;
+ dev->last_scan = 0;
+ dev->reader_pid = -1;
+ dev->pipe_r = dev->pipe_w = -1;
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = "Hewlett-Packard";
+ dev->sane.model = "ScanJet 3500";
+ dev->sane.type = "scanner";
+
+ ++num_devices;
+ *new_dev = dev;
+
+ DBG (15, "attach_scanner: done\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (struct hp3500_data *scanner)
+{
+ int i;
+ SANE_Option_Descriptor *opt;
+
+ memset (scanner->opt, 0, sizeof (scanner->opt));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].name = "filler";
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ opt = scanner->opt + OPT_NUM_OPTS;
+ opt->title = SANE_TITLE_NUM_OPTIONS;
+ opt->desc = SANE_DESC_NUM_OPTIONS;
+ opt->type = SANE_TYPE_INT;
+ opt->cap = SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_RESOLUTION;
+ opt->name = SANE_NAME_SCAN_RESOLUTION;
+ opt->title = SANE_TITLE_SCAN_RESOLUTION;
+ opt->desc = SANE_DESC_SCAN_RESOLUTION;
+ opt->type = SANE_TYPE_INT;
+ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opt->constraint.word_list = res_list;
+ opt->unit = SANE_UNIT_DPI;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_GEOMETRY_GROUP;
+ opt->title = SANE_I18N ("Geometry");
+ opt->desc = SANE_I18N ("Geometry Group");
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ opt = scanner->opt + OPT_TL_X;
+ opt->name = SANE_NAME_SCAN_TL_X;
+ opt->title = SANE_TITLE_SCAN_TL_X;
+ opt->desc = SANE_DESC_SCAN_TL_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_x;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_TL_Y;
+ opt->name = SANE_NAME_SCAN_TL_Y;
+ opt->title = SANE_TITLE_SCAN_TL_Y;
+ opt->desc = SANE_DESC_SCAN_TL_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_y;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_BR_X;
+ opt->name = SANE_NAME_SCAN_BR_X;
+ opt->title = SANE_TITLE_SCAN_BR_X;
+ opt->desc = SANE_DESC_SCAN_BR_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_x;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_BR_Y;
+ opt->name = SANE_NAME_SCAN_BR_Y;
+ opt->title = SANE_TITLE_SCAN_BR_Y;
+ opt->desc = SANE_DESC_SCAN_BR_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_y;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ if (!scan_mode_list[0])
+ {
+ scan_mode_list[HP3500_COLOR_SCAN] = SANE_VALUE_SCAN_MODE_COLOR;
+ scan_mode_list[HP3500_GRAY_SCAN] = SANE_VALUE_SCAN_MODE_GRAY;
+ scan_mode_list[HP3500_LINEART_SCAN] = SANE_VALUE_SCAN_MODE_LINEART;
+ scan_mode_list[HP3500_TOTAL_SCANS] = 0;
+ }
+
+ opt = scanner->opt + OPT_MODE_GROUP;
+ opt->title = SANE_I18N ("Scan Mode Group");
+ opt->desc = SANE_I18N ("Scan Mode Group");
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+
+ opt = scanner->opt + OPT_MODE;
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = scan_mode_list;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_BRIGHTNESS;
+ opt->name = SANE_NAME_BRIGHTNESS;
+ opt->title = SANE_TITLE_BRIGHTNESS;
+ opt->desc = SANE_DESC_BRIGHTNESS;
+ opt->type = SANE_TYPE_INT;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_brightness;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ opt = scanner->opt + OPT_CONTRAST;
+ opt->name = SANE_NAME_CONTRAST;
+ opt->title = SANE_TITLE_CONTRAST;
+ opt->desc = SANE_DESC_CONTRAST;
+ opt->type = SANE_TYPE_INT;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &range_contrast;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+do_reset (struct hp3500_data *scanner)
+{
+ scanner = scanner; /* kill warning */
+}
+
+static void
+do_cancel (struct hp3500_data *scanner)
+{
+ if (scanner->reader_pid != -1)
+ {
+
+ if (sanei_thread_kill (scanner->reader_pid) == 0)
+ {
+ int exit_status;
+
+ sanei_thread_waitpid (scanner->reader_pid, &exit_status);
+ }
+ scanner->reader_pid = -1;
+ }
+ if (scanner->pipe_r >= 0)
+ {
+ close (scanner->pipe_r);
+ scanner->pipe_r = -1;
+ }
+}
+
+static void
+calculateDerivedValues (struct hp3500_data *scanner)
+{
+
+ DBG (12, "calculateDerivedValues\n");
+
+ /* Convert the SANE_FIXED values for the scan area into 1/1200 inch
+ * scanner units */
+
+ scanner->fullres_pixels.left =
+ FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.left);
+ scanner->fullres_pixels.top =
+ FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.top);
+ scanner->fullres_pixels.right =
+ FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.right);
+ scanner->fullres_pixels.bottom =
+ FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.bottom);
+
+ DBG (12, "\tleft margin: %u\n", scanner->fullres_pixels.left);
+ DBG (12, "\ttop margin: %u\n", scanner->fullres_pixels.top);
+ DBG (12, "\tright margin: %u\n", scanner->fullres_pixels.right);
+ DBG (12, "\tbottom margin: %u\n", scanner->fullres_pixels.bottom);
+
+
+ scanner->scan_width_pixels =
+ scanner->resolution * (scanner->fullres_pixels.right -
+ scanner->fullres_pixels.left) / 1200;
+ scanner->scan_height_pixels =
+ scanner->resolution * (scanner->fullres_pixels.bottom -
+ scanner->fullres_pixels.top) / 1200;
+ if (scanner->mode == HP3500_LINEART_SCAN)
+ scanner->bytes_per_scan_line = (scanner->scan_width_pixels + 7) / 8;
+ else if (scanner->mode == HP3500_GRAY_SCAN)
+ scanner->bytes_per_scan_line = scanner->scan_width_pixels;
+ else
+ scanner->bytes_per_scan_line = scanner->scan_width_pixels * 3;
+
+ if (scanner->scan_width_pixels < 1)
+ scanner->scan_width_pixels = 1;
+ if (scanner->scan_height_pixels < 1)
+ scanner->scan_height_pixels = 1;
+
+ scanner->actres_pixels.left =
+ scanner->fullres_pixels.left * scanner->resolution / 1200;
+ scanner->actres_pixels.top =
+ scanner->fullres_pixels.top * scanner->resolution / 1200;
+ scanner->actres_pixels.right =
+ scanner->actres_pixels.left + scanner->scan_width_pixels;
+ scanner->actres_pixels.bottom =
+ scanner->actres_pixels.top + scanner->scan_height_pixels;
+
+ scanner->actual_mm.left =
+ SCANNER_UNIT_TO_FIXED_MM (scanner->fullres_pixels.left);
+ scanner->actual_mm.top =
+ SCANNER_UNIT_TO_FIXED_MM (scanner->fullres_pixels.top);
+ scanner->actual_mm.bottom =
+ SCANNER_UNIT_TO_FIXED_MM (scanner->scan_width_pixels * 1200 /
+ scanner->resolution);
+ scanner->actual_mm.right =
+ SCANNER_UNIT_TO_FIXED_MM (scanner->scan_height_pixels * 1200 /
+ scanner->resolution);
+
+ DBG (12, "calculateDerivedValues: ok\n");
+}
+
+/* From here on in we have the original code written for the scanner demo */
+
+#define MAX_COMMANDS_BYTES 131072
+#define MAX_READ_COMMANDS 1 /* Issuing more than one register
+ * read command in a single request
+ * seems to put the device in an
+ * unpredictable state.
+ */
+#define MAX_READ_BYTES 0xffc0
+
+#define REG_DESTINATION_POSITION 0x60
+#define REG_MOVE_CONTROL_TEST 0xb3
+
+static int command_reads_outstanding = 0;
+static int command_bytes_outstanding = 0;
+static unsigned char command_buffer[MAX_COMMANDS_BYTES];
+static int receive_bytes_outstanding = 0;
+static char *command_readmem_outstanding[MAX_READ_COMMANDS];
+static int command_readbytes_outstanding[MAX_READ_COMMANDS];
+static unsigned char sram_access_method = 0;
+static unsigned sram_size = 0;
+static int udh;
+
+static int
+rt_execute_commands (void)
+{
+ SANE_Status result;
+ size_t bytes;
+
+ if (!command_bytes_outstanding)
+ return 0;
+
+ bytes = command_bytes_outstanding;
+
+ result = sanei_usb_write_bulk (udh, /* 0x02, */ command_buffer, &bytes);
+
+ if (result == SANE_STATUS_GOOD && receive_bytes_outstanding)
+ {
+ unsigned char readbuf[MAX_READ_BYTES];
+ int total_read = 0;
+
+ do
+ {
+ bytes = receive_bytes_outstanding - total_read;
+ result = sanei_usb_read_bulk (udh,
+ /* 0x81, */
+ readbuf + total_read, &bytes);
+ if (result == SANE_STATUS_GOOD)
+ total_read += bytes;
+ else
+ break;
+ }
+ while (total_read < receive_bytes_outstanding);
+ if (result == SANE_STATUS_GOOD)
+ {
+ unsigned char *readptr;
+ int i;
+
+ for (i = 0, readptr = readbuf;
+ i < command_reads_outstanding;
+ readptr += command_readbytes_outstanding[i++])
+ {
+ memcpy (command_readmem_outstanding[i],
+ readptr, command_readbytes_outstanding[i]);
+ }
+ }
+ }
+ receive_bytes_outstanding = command_reads_outstanding =
+ command_bytes_outstanding = 0;
+ return (result == SANE_STATUS_GOOD) ? 0 : -1;
+}
+
+static int
+rt_queue_command (int command,
+ int reg,
+ int count,
+ int bytes, void const *data_, int readbytes, void *readdata)
+{
+ int len = 4 + bytes;
+ unsigned char *buffer;
+ unsigned char const *data = data_;
+
+ /* We add "bytes" here to account for the possiblity that all of the
+ * data bytes are 0xaa and hence require a following 0x00 byte.
+ */
+ if (command_bytes_outstanding + len + bytes > MAX_COMMANDS_BYTES ||
+ (readbytes &&
+ ((command_reads_outstanding >= MAX_READ_COMMANDS) ||
+ (receive_bytes_outstanding >= MAX_READ_BYTES))))
+ {
+ if (rt_execute_commands () < 0)
+ return -1;
+ }
+
+ buffer = command_buffer + command_bytes_outstanding;
+
+ *buffer++ = command;
+ *buffer++ = reg;
+ *buffer++ = count >> 8;
+ *buffer++ = count;
+ while (bytes--)
+ {
+ *buffer++ = *data;
+ if (*data++ == 0xaa)
+ {
+ *buffer++ = 0;
+ ++len;
+ }
+ }
+ command_bytes_outstanding += len;
+ if (readbytes)
+ {
+ command_readbytes_outstanding[command_reads_outstanding] = readbytes;
+ command_readmem_outstanding[command_reads_outstanding] = readdata;
+ receive_bytes_outstanding += readbytes;
+ ++command_reads_outstanding;
+ }
+
+ return 0;
+}
+
+static int
+rt_send_command_immediate (int command,
+ int reg,
+ int count,
+ int bytes,
+ void *data, int readbytes, void *readdata)
+{
+ rt_queue_command (command, reg, count, bytes, data, readbytes, readdata);
+ return rt_execute_commands ();
+}
+
+static int
+rt_queue_read_register (int reg, int bytes, void *data)
+{
+ return rt_queue_command (RTCMD_GETREG, reg, bytes, 0, 0, bytes, data);
+}
+
+static int
+rt_read_register_immediate (int reg, int bytes, void *data)
+{
+ if (rt_queue_read_register (reg, bytes, data) < 0)
+ return -1;
+ return rt_execute_commands ();
+}
+
+static int
+rt_queue_set_register (int reg, int bytes, void *data)
+{
+ return rt_queue_command (RTCMD_SETREG, reg, bytes, bytes, data, 0, 0);
+}
+
+static int
+rt_set_register_immediate (int reg, int bytes, void *data)
+{
+ if (reg < 0xb3 && reg + bytes > 0xb3)
+ {
+ int bytes_in_first_block = 0xb3 - reg;
+
+ if (rt_set_register_immediate (reg, bytes_in_first_block, data) < 0 ||
+ rt_set_register_immediate (0xb4, bytes - bytes_in_first_block - 1,
+ (char *) data + bytes_in_first_block +
+ 1) < 0)
+ return -1;
+ return 0;
+ }
+ if (rt_queue_set_register (reg, bytes, data) < 0)
+ return -1;
+ return rt_execute_commands ();
+}
+
+static int
+rt_set_one_register (int reg, int val)
+{
+ char r = val;
+
+ return rt_set_register_immediate (reg, 1, &r);
+}
+
+static int
+rt_write_sram (int bytes, void *data_)
+{
+ unsigned char *data = (unsigned char *) data_;
+
+ /* The number of bytes passed in could be much larger than we can transmit
+ * (0xffc0) bytes. With 0xaa escapes it could be even larger. Accordingly
+ * we need to count the 0xaa escapes and write in chunks if the number of
+ * bytes would otherwise exceed a limit (I have used 0xf000 as the limit).
+ */
+ while (bytes > 0)
+ {
+ int now = 0;
+ int bufsize = 0;
+
+ while (now < bytes && bufsize < 0xf000)
+ {
+ int i;
+
+ /* Try to avoid writing part pages */
+ for (i = 0; i < 32 && now < bytes; ++i)
+ {
+ ++bufsize;
+ if (data[now++] == 0xaa)
+ ++bufsize;
+ }
+ }
+
+ if (rt_send_command_immediate (RTCMD_WRITESRAM, 0, now, now, data, 0,
+ 0) < 0)
+ return -1;
+ bytes -= now;
+ data += now;
+ }
+ return 0;
+}
+
+static int
+rt_read_sram (int bytes, void *data_)
+{
+ unsigned char *data = (unsigned char *) data_;
+
+ while (bytes > 0)
+ {
+ int now = (bytes > 0xf000) ? 0xf000 : bytes;
+ if (rt_send_command_immediate (RTCMD_READSRAM, 0, bytes, 0, 0, bytes,
+ data) < 0)
+ return -1;
+ bytes -= now;
+ data += now;
+ }
+ return 0;
+}
+
+static int
+rt_set_sram_page (int page)
+{
+ unsigned char regs[2];
+
+ regs[0] = page;
+ regs[1] = page >> 8;
+
+ return rt_set_register_immediate (0x91, 2, regs);
+}
+
+static int
+rt_detect_sram (unsigned *totalbytes, unsigned char *r93setting)
+{
+ char data[0x818];
+ char testbuf[0x818];
+ unsigned i;
+ int test_values[] = { 6, 2, 1, -1 };
+
+ for (i = 0; i < sizeof (data); ++i)
+ data[i] = i % 0x61;
+
+
+ for (i = 0; test_values[i] != -1; ++i)
+ {
+ if (rt_set_one_register (0x93, test_values[i]) ||
+ rt_set_sram_page (0x81) ||
+ rt_write_sram (0x818, data) ||
+ rt_set_sram_page (0x81) || rt_read_sram (0x818, testbuf))
+ return -1;
+ if (!memcmp (testbuf, data, 0x818))
+ {
+ sram_access_method = test_values[i];
+ if (r93setting)
+ *r93setting = sram_access_method;
+ break;
+ }
+ }
+ if (!sram_access_method)
+ return -1;
+
+ for (i = 0; i < 16; ++i)
+ {
+ int j;
+ char write_data[32];
+ char read_data[32];
+ int pagesetting;
+
+ for (j = 0; j < 16; j++)
+ {
+ write_data[j * 2] = j * 2;
+ write_data[j * 2 + 1] = i;
+ }
+
+ pagesetting = i * 4096;
+
+
+ if (rt_set_sram_page (pagesetting) < 0 ||
+ rt_write_sram (32, write_data) < 0)
+ return -1;
+ if (i)
+ {
+ if (rt_set_sram_page (0) < 0 || rt_read_sram (32, read_data) < 0)
+ return -1;
+ if (!memcmp (read_data, write_data, 32))
+ {
+ sram_size = i * 0x20000;
+ if (totalbytes)
+ *totalbytes = sram_size;
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+static int
+rt_get_available_bytes (void)
+{
+ unsigned char data[3];
+
+ if (rt_queue_command (RTCMD_BYTESAVAIL, 0, 3, 0, 0, 3, data) < 0 ||
+ rt_execute_commands () < 0)
+ return -1;
+ return ((unsigned) data[0]) |
+ ((unsigned) data[1] << 8) | ((unsigned) data[2] << 16);
+}
+
+static int
+rt_get_data (int bytes, void *data)
+{
+ int total = 0;
+
+ while (bytes)
+ {
+ int bytesnow = bytes;
+
+ if (bytesnow > 0xffc0)
+ bytesnow = 0xffc0;
+ if (rt_queue_command
+ (RTCMD_READBYTES, 0, bytesnow, 0, 0, bytesnow, data) < 0
+ || rt_execute_commands () < 0)
+ return -1;
+ total += bytesnow;
+ bytes -= bytesnow;
+ data = (char *) data + bytesnow;
+ }
+ return 0;
+}
+
+static int
+rt_is_moving (void)
+{
+ char r;
+
+ if (rt_read_register_immediate (REG_MOVE_CONTROL_TEST, 1, &r) < 0)
+ return -1;
+ if (r == 0x08)
+ return 1;
+ return 0;
+}
+
+static int
+rt_is_rewound (void)
+{
+ char r;
+
+ if (rt_read_register_immediate (0x1d, 1, &r) < 0)
+ return -1;
+ if (r & 0x02)
+ return 1;
+ return 0;
+}
+
+static int
+rt_set_direction_forwards (unsigned char *regs)
+{
+ regs[0xc6] |= 0x08;
+ return 0;
+}
+
+static int
+rt_set_direction_rewind (unsigned char *regs)
+{
+ regs[0xc6] &= 0xf7;
+ return 0;
+}
+
+static int
+rt_set_stop_when_rewound (unsigned char *regs, int stop)
+{
+ if (stop)
+ regs[0xb2] |= 0x10;
+ else
+ regs[0xb2] &= 0xef;
+ return 0;
+}
+
+static int
+rt_start_moving (void)
+{
+ if (rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 8) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 8) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+rt_stop_moving (void)
+{
+ if (rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+rt_set_powersave_mode (int enable)
+{
+ unsigned char r;
+
+ if (rt_read_register_immediate (REG_MOVE_CONTROL_TEST, 1, &r) < 0)
+ return -1;
+ if (r & 0x04)
+ {
+ if (enable == 1)
+ return 0;
+ r &= ~0x04;
+ }
+ else
+ {
+ if (enable == 0)
+ return 0;
+ r |= 0x04;
+ }
+ if (rt_set_one_register (REG_MOVE_CONTROL_TEST, r) < 0 ||
+ rt_set_one_register (REG_MOVE_CONTROL_TEST, r) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+rt_turn_off_lamp (void)
+{
+ return rt_set_one_register (0x3a, 0);
+}
+
+static int
+rt_turn_on_lamp (void)
+{
+ char r3ab[2];
+ char r10;
+ char r58;
+
+ if (rt_read_register_immediate (0x3a, 1, r3ab) < 0 ||
+ rt_read_register_immediate (0x10, 1, &r10) < 0 ||
+ rt_read_register_immediate (0x58, 1, &r58) < 0)
+ return -1;
+ r3ab[0] |= 0x80;
+ r3ab[1] = 0x40;
+ r10 |= 0x01;
+ r58 &= 0x0f;
+ if (rt_set_register_immediate (0x3a, 2, r3ab) < 0 ||
+ rt_set_one_register (0x10, r10) < 0 ||
+ rt_set_one_register (0x58, r58) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+rt_set_value_lsbfirst (unsigned char *regs,
+ int firstreg, int totalregs, unsigned value)
+{
+ while (totalregs--)
+ {
+ regs[firstreg++] = value & 0xff;
+ value >>= 8;
+ }
+ return 0;
+}
+
+#if 0
+static int
+rt_set_value_msbfirst (unsigned char *regs,
+ int firstreg, int totalregs, unsigned value)
+{
+ while (totalregs--)
+ {
+ regs[firstreg + totalregs] = value & 0xff;
+ value >>= 8;
+ }
+ return 0;
+}
+#endif
+
+static int
+rt_set_ccd_shift_clock_multiplier (unsigned char *regs, unsigned value)
+{
+ return rt_set_value_lsbfirst (regs, 0xf0, 3, value);
+}
+
+static int
+rt_set_ccd_clock_reset_interval (unsigned char *regs, unsigned value)
+{
+ return rt_set_value_lsbfirst (regs, 0xf9, 3, value);
+}
+
+static int
+rt_set_ccd_clamp_clock_multiplier (unsigned char *regs, unsigned value)
+{
+ return rt_set_value_lsbfirst (regs, 0xfc, 3, value);
+}
+
+static int
+rt_set_movement_pattern (unsigned char *regs, unsigned value)
+{
+ return rt_set_value_lsbfirst (regs, 0xc0, 3, value);
+}
+
+static int
+rt_set_motor_movement_clock_multiplier (unsigned char *regs, unsigned value)
+{
+ regs[0x40] = (regs[0x40] & ~0xc0) | (value << 6);
+ return 0;
+}
+
+static int
+rt_set_motor_type (unsigned char *regs, unsigned value)
+{
+ regs[0xc9] = (regs[0xc9] & 0xf8) | (value & 0x7);
+ return 0;
+}
+
+static int
+rt_set_noscan_distance (unsigned char *regs, unsigned value)
+{
+ DBG (10, "Setting distance without scanning to %d\n", value);
+ return rt_set_value_lsbfirst (regs, 0x60, 2, value);
+}
+
+static int
+rt_set_total_distance (unsigned char *regs, unsigned value)
+{
+ DBG (10, "Setting total distance to %d\n", value);
+ return rt_set_value_lsbfirst (regs, 0x62, 2, value);
+}
+
+static int
+rt_set_scanline_start (unsigned char *regs, unsigned value)
+{
+ return rt_set_value_lsbfirst (regs, 0x66, 2, value);
+}
+
+static int
+rt_set_scanline_end (unsigned char *regs, unsigned value)
+{
+ return rt_set_value_lsbfirst (regs, 0x6c, 2, value);
+}
+
+static int
+rt_set_basic_calibration (unsigned char *regs,
+ int redoffset1,
+ int redoffset2,
+ int redgain,
+ int greenoffset1,
+ int greenoffset2,
+ int greengain,
+ int blueoffset1, int blueoffset2, int bluegain)
+{
+ regs[0x05] = redoffset1;
+ regs[0x02] = redoffset2;
+ regs[0x08] = redgain;
+ regs[0x06] = greenoffset1;
+ regs[0x03] = greenoffset2;
+ regs[0x09] = greengain;
+ regs[0x07] = blueoffset1;
+ regs[0x04] = blueoffset2;
+ regs[0x0a] = bluegain;
+ return 0;
+}
+
+static int
+rt_set_calibration_addresses (unsigned char *regs,
+ unsigned redaddr,
+ unsigned greenaddr,
+ unsigned blueaddr, unsigned endaddr)
+{
+ regs[0x84] = redaddr;
+ regs[0x8e] = (regs[0x8e] & 0x0f) | ((redaddr >> 4) & 0xf0);
+ rt_set_value_lsbfirst (regs, 0x85, 2, greenaddr);
+ rt_set_value_lsbfirst (regs, 0x87, 2, blueaddr);
+ rt_set_value_lsbfirst (regs, 0x89, 2, (endaddr + 31) / 32);
+ return 0;
+}
+
+static int
+rt_set_lamp_duty_cycle (unsigned char *regs,
+ int enable, int frequency, int offduty)
+{
+ if (enable)
+ regs[0x3b] |= 0x80;
+ else
+ regs[0x3b] &= 0x7f;
+
+ regs[0x3b] =
+ (regs[0x3b] & 0x80) | ((frequency & 0x7) << 4) | (offduty & 0x0f);
+ regs[0x3d] = (regs[0x3d] & 0x7f) | ((frequency & 0x8) << 4);
+ return 0;
+}
+
+static int
+rt_set_data_feed_on (unsigned char *regs)
+{
+ regs[0xb2] &= ~0x04;
+ return 0;
+}
+
+static int
+rt_enable_ccd (unsigned char *regs, int enable)
+{
+ if (enable)
+ regs[0x00] &= ~0x10;
+ else
+ regs[0x00] |= 0x10;
+ return 0;
+}
+
+static int
+rt_set_cdss (unsigned char *regs, int val1, int val2)
+{
+ regs[0x28] = (regs[0x28] & 0xe0) | (val1 & 0x1f);
+ regs[0x2a] = (regs[0x2a] & 0xe0) | (val2 & 0x1f);
+ return 0;
+}
+
+static int
+rt_set_cdsc (unsigned char *regs, int val1, int val2)
+{
+ regs[0x29] = (regs[0x29] & 0xe0) | (val1 & 0x1f);
+ regs[0x2b] = (regs[0x2b] & 0xe0) | (val2 & 0x1f);
+ return 0;
+}
+
+static int
+rt_update_after_setting_cdss2 (unsigned char *regs)
+{
+ int fullcolour = (!(regs[0x2f] & 0xc0) && (regs[0x2f] & 0x04));
+ int value = regs[0x2a] & 0x1f;
+
+ regs[0x2a] = (regs[0x2a] & 0xe0) | (value & 0x1f);
+
+ if (fullcolour)
+ value *= 3;
+ if ((regs[0x40] & 0xc0) == 0x40)
+ value += 17;
+ else
+ value += 16;
+
+ regs[0x2c] = (regs[0x2c] & 0xe0) | (value % 24);
+ regs[0x2d] = (regs[0x2d] & 0xe0) | ((value + 2) % 24);
+ return 0;
+}
+
+static int
+rt_set_cph0s (unsigned char *regs, int on)
+{
+ if (on)
+ regs[0x2d] |= 0x20; /* 1200dpi horizontal coordinate space */
+ else
+ regs[0x2d] &= ~0x20; /* 600dpi horizontal coordinate space */
+ return 0;
+}
+
+static int
+rt_set_cvtr_lm (unsigned char *regs, int val1, int val2, int val3)
+{
+ regs[0x28] = (regs[0x28] & ~0xe0) | (val1 << 5);
+ regs[0x29] = (regs[0x29] & ~0xe0) | (val2 << 5);
+ regs[0x2a] = (regs[0x2a] & ~0xe0) | (val3 << 5);
+ return 0;
+}
+
+static int
+rt_set_cvtr_mpt (unsigned char *regs, int val1, int val2, int val3)
+{
+ regs[0x3c] = (val1 & 0x0f) | (val2 << 4);
+ regs[0x3d] = (regs[0x3d] & 0xf0) | (val3 & 0x0f);
+ return 0;
+}
+
+static int
+rt_set_cvtr_wparams (unsigned char *regs,
+ unsigned fpw, unsigned bpw, unsigned w)
+{
+ regs[0x31] = (w & 0x0f) | ((bpw << 4) & 0x30) | (fpw << 6);
+ return 0;
+}
+
+static int
+rt_enable_movement (unsigned char *regs, int enable)
+{
+ if (enable)
+ regs[0xc3] |= 0x80;
+ else
+ regs[0xc3] &= ~0x80;
+ return 0;
+}
+
+static int
+rt_set_scan_frequency (unsigned char *regs, int frequency)
+{
+ regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f);
+ return 0;
+}
+
+static int
+rt_set_merge_channels (unsigned char *regs, int on)
+{
+ /* RGBRGB instead of RRRRR...GGGGG...BBBB */
+ regs[0x2f] &= ~0x14;
+ regs[0x2f] |= on ? 0x04 : 0x10;
+ return 0;
+}
+
+static int
+rt_set_channel (unsigned char *regs, int channel)
+{
+ regs[0x2f] = (regs[0x2f] & ~0xc0) | (channel << 6);
+ return 0;
+}
+
+static int
+rt_set_single_channel_scanning (unsigned char *regs, int on)
+{
+ if (on)
+ regs[0x2f] |= 0x20;
+ else
+ regs[0x2f] &= ~0x20;
+ return 0;
+}
+
+static int
+rt_set_colour_mode (unsigned char *regs, int on)
+{
+ if (on)
+ regs[0x2f] |= 0x02;
+ else
+ regs[0x2f] &= ~0x02;
+ return 0;
+}
+
+static int
+rt_set_horizontal_resolution (unsigned char *regs, int resolution)
+{
+ int base_resolution = 300;
+
+ if (regs[0x2d] & 0x20)
+ base_resolution *= 2;
+ if (regs[0xd3] & 0x08)
+ base_resolution *= 2;
+ regs[0x7a] = base_resolution / resolution;
+ return 0;
+}
+
+static int
+rt_set_last_sram_page (unsigned char *regs, int pagenum)
+{
+ rt_set_value_lsbfirst (regs, 0x8b, 2, pagenum);
+ return 0;
+}
+
+static int
+rt_set_step_size (unsigned char *regs, int stepsize)
+{
+ rt_set_value_lsbfirst (regs, 0xe2, 2, stepsize);
+ rt_set_value_lsbfirst (regs, 0xe0, 2, 0);
+ return 0;
+}
+
+static int
+rt_set_all_registers (void const *regs_)
+{
+ char regs[255];
+
+ memcpy (regs, regs_, 255);
+ regs[0x32] &= ~0x40;
+
+ if (rt_set_one_register (0x32, regs[0x32]) < 0 ||
+ rt_set_register_immediate (0, 255, regs) < 0 ||
+ rt_set_one_register (0x32, regs[0x32] | 0x40) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+rt_adjust_misc_registers (unsigned char *regs)
+{
+ /* Mostly unknown purposes - probably no need to adjust */
+ regs[0xc6] = (regs[0xc6] & 0x0f) | 0x20; /* Purpose unknown - appears to do nothing */
+ regs[0x2e] = 0x86; /* ???? - Always has this value */
+ regs[0x30] = 2; /* CCPL = 1 */
+ regs[0xc9] |= 0x38; /* Doesn't have any obvious effect, but the Windows driver does this */
+ return 0;
+}
+
+
+#define NVR_MAX_ADDRESS_SIZE 11
+#define NVR_MAX_OPCODE_SIZE 3
+#define NVR_DATA_SIZE 8
+#define NVR_MAX_COMMAND_SIZE ((NVR_MAX_ADDRESS_SIZE + \
+ NVR_MAX_OPCODE_SIZE + \
+ NVR_DATA_SIZE) * 2 + 1)
+
+static int
+rt_nvram_enable_controller (int enable)
+{
+ unsigned char r;
+
+ if (rt_read_register_immediate (0x1d, 1, &r) < 0)
+ return -1;
+ if (enable)
+ r |= 1;
+ else
+ r &= ~1;
+ return rt_set_one_register (0x1d, r);
+
+}
+
+static int
+rt_nvram_init_command (void)
+{
+ unsigned char regs[13];
+
+ if (rt_read_register_immediate (0x10, 13, regs) < 0)
+ return -1;
+ regs[2] |= 0xf0;
+ regs[4] = (regs[4] & 0x1f) | 0x60;
+ return rt_set_register_immediate (0x10, 13, regs);
+}
+
+static int
+rt_nvram_init_stdvars (int block, int *addrbits, unsigned char *basereg)
+{
+ int bitsneeded;
+ int capacity;
+
+ switch (block)
+ {
+ case 0:
+ bitsneeded = 7;
+ break;
+
+ case 1:
+ bitsneeded = 9;
+ break;
+
+ case 2:
+ bitsneeded = 11;
+ break;
+
+ default:
+ bitsneeded = 0;
+ capacity = 1;
+ while (capacity < block)
+ capacity <<= 1, ++bitsneeded;
+ break;
+ }
+
+ *addrbits = bitsneeded;
+
+ if (rt_read_register_immediate (0x10, 1, basereg) < 0)
+ return -1;
+
+ *basereg &= ~0x60;
+ return 0;
+}
+
+static void
+rt_nvram_set_half_bit (unsigned char *buffer,
+ int value, unsigned char stdbits, int whichhalf)
+{
+ *buffer = stdbits | (value ? 0x40 : 0) | (whichhalf ? 0x20 : 0);
+}
+
+static void
+rt_nvram_set_command_bit (unsigned char *buffer,
+ int value, unsigned char stdbits)
+{
+ rt_nvram_set_half_bit (buffer, value, stdbits, 0);
+ rt_nvram_set_half_bit (buffer + 1, value, stdbits, 1);
+}
+
+static void
+rt_nvram_set_addressing_bits (unsigned char *buffer,
+ int location,
+ int addressingbits, unsigned char stdbits)
+{
+ int currentbit = 1 << (addressingbits - 1);
+
+ while (addressingbits--)
+ {
+ rt_nvram_set_command_bit (buffer,
+ (location & currentbit) ? 1 : 0, stdbits);
+ buffer += 2;
+ currentbit >>= 1;
+ }
+}
+
+#if 0
+static int
+rt_nvram_enable_write (int addressingbits, int enable, unsigned char stdbits)
+{
+ unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE];
+ int cmdsize = 6 + addressingbits * 2;
+
+ rt_nvram_set_command_bit (cmdbuffer, 1, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 2, 0, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 4, 0, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 6, enable, stdbits);
+ if (addressingbits > 1)
+ rt_nvram_set_addressing_bits (cmdbuffer + 8, 0, addressingbits - 1,
+ stdbits);
+
+ if (rt_nvram_enable_controller (1) < 0 ||
+ rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, cmdsize, cmdsize,
+ cmdbuffer, 0, 0) < 0
+ || rt_nvram_enable_controller (0) < 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+rt_nvram_write (int block, int location, char const *data, int bytes)
+{
+ int addressingbits;
+ unsigned char stdbits;
+ unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE];
+ unsigned char *address_bits;
+ unsigned char *data_bits;
+ int cmdsize;
+
+ /* This routine doesn't appear to work, but I can't see anything wrong with it */
+ if (rt_nvram_init_stdvars (block, &addressingbits, &stdbits) < 0)
+ return -1;
+
+ cmdsize = (addressingbits + 8) * 2 + 6;
+ address_bits = cmdbuffer + 6;
+ data_bits = address_bits + (addressingbits * 2);
+
+ rt_nvram_set_command_bit (cmdbuffer, 1, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 2, 0, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 4, 1, stdbits);
+
+ if (rt_nvram_init_command () < 0 ||
+ rt_nvram_enable_write (addressingbits, 1, stdbits) < 0)
+ return -1;
+
+ while (bytes--)
+ {
+ int i;
+
+ rt_nvram_set_addressing_bits (address_bits, location, addressingbits,
+ stdbits);
+ rt_nvram_set_addressing_bits (data_bits, *data++, 8, stdbits);
+
+ if (rt_nvram_enable_controller (1) < 0 ||
+ rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, cmdsize, cmdsize,
+ cmdbuffer, 0, 0) < 0
+ || rt_nvram_enable_controller (0) < 0)
+ return -1;
+
+ if (rt_nvram_enable_controller (1) < 0)
+ return -1;
+ for (i = 0; i < cmdsize; ++i)
+ {
+ unsigned char r;
+ unsigned char cmd;
+
+ rt_nvram_set_half_bit (&cmd, 0, stdbits, i & 1);
+ if (rt_send_command_immediate
+ (RTCMD_NVRAMCONTROL, 0, 1, 1, &cmd, 0, 0) < 0
+ || rt_read_register_immediate (0x10, 1, &r) < 0)
+ {
+ return -1;
+ }
+ else if (r & 0x80)
+ {
+ break;
+ }
+ }
+ if (rt_nvram_enable_controller (0) < 0)
+ return -1;
+
+ ++location;
+ }
+
+ if (rt_nvram_enable_write (addressingbits, 0, stdbits) < 0)
+ return -1;
+ return 0;
+}
+#endif
+
+static int
+rt_nvram_read (int block, int location, unsigned char *data, int bytes)
+{
+ int addressingbits;
+ unsigned char stdbits;
+ unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE];
+ unsigned char *address_bits;
+ unsigned char readbit_command[2];
+ int cmdsize;
+
+ if (rt_nvram_init_stdvars (block, &addressingbits, &stdbits) < 0)
+ return -1;
+
+ cmdsize = addressingbits * 2 + 7;
+ address_bits = cmdbuffer + 6;
+
+ rt_nvram_set_command_bit (cmdbuffer, 1, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 2, 1, stdbits);
+ rt_nvram_set_command_bit (cmdbuffer + 4, 0, stdbits);
+ rt_nvram_set_half_bit (cmdbuffer + cmdsize - 1, 0, stdbits, 0);
+
+ rt_nvram_set_half_bit (readbit_command, 0, stdbits, 1);
+ rt_nvram_set_half_bit (readbit_command + 1, 0, stdbits, 0);
+
+ if (rt_nvram_init_command () < 0)
+ return -1;
+
+ while (bytes--)
+ {
+ char c = 0;
+ unsigned char r;
+ int i;
+
+ rt_nvram_set_addressing_bits (address_bits, location, addressingbits,
+ stdbits);
+
+ if (rt_nvram_enable_controller (1) < 0 ||
+ rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0x1d, cmdsize,
+ cmdsize, cmdbuffer, 0, 0) < 0)
+ return -1;
+
+ for (i = 0; i < 8; ++i)
+ {
+ c <<= 1;
+
+ if (rt_send_command_immediate
+ (RTCMD_NVRAMCONTROL, 0x1d, 2, 2, readbit_command, 0, 0) < 0
+ || rt_read_register_immediate (0x10, 1, &r) < 0)
+ return -1;
+ if (r & 0x80)
+ c |= 1;
+ }
+ if (rt_nvram_enable_controller (0) < 0)
+ return -1;
+
+ *data++ = c;
+ ++location;
+ }
+ return 0;
+}
+
+static unsigned char initial_regs[] = {
+ /* 0x00 */ 0xf5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0xe1, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc,
+ /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x19,
+ /* 0x30 */ 0xd0, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0xa0, 0x37, 0xff, 0x0f, 0x00, 0x00,
+ /* 0x40 */ 0x80, 0x00, 0x00, 0x00, 0x8c, 0x76, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x50 */ 0x20, 0xbc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x58 */ 0x1d, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
+ /* 0x60 */ 0x5e, 0xea, 0x5f, 0xea, 0x00, 0x80, 0x64, 0x00,
+ /* 0x68 */ 0x00, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x00,
+ /* 0x70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x78 */ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x80 */ 0x0f, 0x02, 0x4b, 0x02, 0x00, 0xec, 0x19, 0xd8,
+ /* 0x88 */ 0x2d, 0x87, 0x02, 0xff, 0x3f, 0x78, 0x60, 0x00,
+ /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xa0 */ 0x00, 0x00, 0x00, 0x0c, 0x27, 0x64, 0x00, 0x00,
+ /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xb0 */ 0x12, 0x08, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00,
+ /* 0xb8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xc0 */ 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00,
+ /* 0xc8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xd0 */ 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
+ /* 0xd8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xe0 */ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xe8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xf0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xf8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define RT_NORMAL_TG 0
+#define RT_DOUBLE_TG 1
+#define RT_TRIPLE_TG 2
+#define RT_DDOUBLE_TG 3
+#define RT_300_TG 4
+#define RT_150_TG 5
+#define RT_TEST_TG 6
+static struct tg_info__
+{
+ int tg_cph0p;
+ int tg_crsp;
+ int tg_cclpp;
+ int tg_cph0s;
+ int tg_cdss1;
+ int tg_cdsc1;
+ int tg_cdss2;
+ int tg_cdsc2;
+} tg_info[] =
+{
+ /* CPH CCD Shifting Clock
+ * 0P ??? Perhaps CCD rising edge position
+ * 0S ???
+ * CRS Reset CCD Clock
+ * P ??? Perhaps CCD falling edge position
+ * CCLP CCD Clamp Clock
+ * P ???
+ * CDS ???
+ * S1 ???
+ * S2 ???
+ * C1 ???
+ * C2 ???
+ */
+ /*CPH0P CRSP CCLPP CPH0S CDSS1 CDSC1 CDSS2 CDSC2 */
+ {
+ 0x01FFE0, 0x3c0000, 0x003000, 1, 0xb, 0xd, 0x00, 0x01}, /* NORMAL */
+ {
+ 0x7ff800, 0xf00000, 0x01c000, 0, 0xb, 0xc, 0x14, 0x15}, /* DOUBLE */
+ {
+ 0x033fcc, 0x300000, 0x060000, 1, 0x8, 0xa, 0x00, 0x01}, /* TRIPLE */
+ {
+ 0x028028, 0x300000, 0x060000, 1, 0x8, 0xa, 0x00, 0x01}, /* DDOUBLE */
+ {
+ 0x7ff800, 0x030000, 0x060000, 0, 0xa, 0xc, 0x17, 0x01}, /* 300 */
+ {
+ 0x7fc700, 0x030000, 0x060000, 0, 0x7, 0x9, 0x17, 0x01}, /* 150 */
+ {
+ 0x7ff800, 0x300000, 0x060000, 0, 0xa, 0xc, 0x17, 0x01}, /* TEST */
+};
+
+struct resolution_parameters
+{
+ unsigned resolution;
+ int reg_39_value;
+ int reg_c3_value;
+ int reg_c6_value;
+ int scan_frequency;
+ int cph0s;
+ int red_green_offset;
+ int green_blue_offset;
+ int intra_channel_offset;
+ int motor_movement_clock_multiplier;
+ int d3_bit_3_value;
+ int tg;
+ int step_size;
+};
+
+/* The TG value sets seem to affect the exposure time:
+ * At 200dpi:
+ * NORMAL gets higher values than DOUBLE
+ * DDOUBLE gives a crazy spike in the data
+ * TRIPLE gives a black result
+ * TEST gives a black result
+ * 300 gives a black result
+ * 150 gives a black result
+ */
+
+static struct resolution_parameters resparms[] = {
+ /* Acceptable values for stepsz are:
+ * 0x157b 0xabd, 0x55e, 0x2af, 0x157, 0xab, 0x55
+ */
+ /* My values - all work */
+ /*res r39 rC3 rC6 freq cph0s rgo gbo intra mmcm d3 tg stepsz */
+ {1200, 3, 6, 4, 2, 1, 22, 22, 4, 2, 1, RT_NORMAL_TG, 0x157b},
+ {600, 15, 6, 4, 1, 0, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e},
+ {400, 3, 1, 4, 1, 1, 6, 6, 1, 2, 1, RT_NORMAL_TG, 0x157b},
+ {300, 15, 3, 4, 1, 0, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af},
+ {200, 7, 1, 4, 1, 0, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e},
+ {150, 15, 3, 1, 1, 0, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af},
+ {100, 3, 1, 3, 1, 0, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd},
+ {75, 15, 3, 3, 1, 0, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af},
+ {50, 15, 1, 1, 1, 0, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+struct dcalibdata
+{
+ unsigned char *buffers[3];
+ int pixelsperrow;
+ int pixelnow;
+ int channelnow;
+ int firstrowdone;
+};
+
+static void dump_registers (unsigned char const *);
+static int
+rts8801_rewind (void)
+{
+ unsigned char regs[255];
+ int n;
+ int tg_setting = RT_DOUBLE_TG;
+
+ rt_read_register_immediate (0, 255, regs);
+
+ rt_set_noscan_distance (regs, 59998);
+ rt_set_total_distance (regs, 59999);
+
+ rt_set_stop_when_rewound (regs, 0);
+
+ rt_set_one_register (0xc6, 0);
+ rt_set_one_register (0xc6, 0);
+
+
+ rt_set_direction_rewind (regs);
+
+ rt_set_step_size (regs, 0x55);
+ regs[0x39] = 3;
+ regs[0xc3] = (regs[0xc3] & 0xf8) | 0x86;
+ regs[0xc6] = (regs[0xc6] & 0xf8) | 4;
+
+ rt_set_horizontal_resolution (regs, 25);
+ rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p);
+ rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp);
+ rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp);
+ rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1,
+ tg_info[tg_setting].tg_cdss2);
+ rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1,
+ tg_info[tg_setting].tg_cdsc2);
+ rt_update_after_setting_cdss2 (regs);
+ rt_set_cvtr_wparams (regs, 3, 0, 6);
+ rt_set_cvtr_mpt (regs, 15, 15, 15);
+ rt_set_cvtr_lm (regs, 7, 7, 7);
+ rt_set_motor_type (regs, 2);
+
+ if (DBG_LEVEL >= 5)
+ dump_registers (regs);
+
+ rt_set_all_registers (regs);
+ rt_set_one_register (0x2c, regs[0x2c]);
+
+ rt_start_moving ();
+
+ while (!rt_is_rewound () &&
+ ((n = rt_get_available_bytes ()) > 0 || rt_is_moving () > 0))
+ {
+ if (n)
+ {
+ char buffer[0xffc0];
+
+ if (n > (int) sizeof (buffer))
+ n = sizeof (buffer);
+ rt_get_data (n, buffer);
+ }
+ else
+ {
+ usleep (10000);
+ }
+ }
+
+ rt_stop_moving ();
+ return 0;
+}
+
+static int cancelled_scan = 0;
+
+static unsigned
+get_lsbfirst_int (unsigned char const *p, int n)
+{
+ unsigned value = *p++;
+ int shift = 8;
+
+ while (--n)
+ {
+ unsigned now = *p++;
+ value |= now << shift;
+ shift += 8;
+ }
+ return value;
+}
+
+static int
+convert_c6 (int i)
+{
+ switch (i)
+ {
+ case 3:
+ return 1;
+
+ case 1:
+ return 2;
+
+ case 4:
+ return 4;
+ }
+ return -1;
+}
+
+static void
+dump_registers (unsigned char const *regs)
+{
+ int i = 0;
+ long pixels;
+
+ DBG (5, "Scan commencing with registers:\n");
+ while (i < 255)
+ {
+ int j = 0;
+ char buffer[80];
+
+ buffer[0] = 0;
+
+ sprintf (buffer + strlen (buffer), "%02x:", i);
+ while (j < 8)
+ {
+ sprintf (buffer + strlen (buffer), " %02x", regs[i++]);
+ j++;
+ }
+ sprintf (buffer + strlen (buffer), " -");
+ while (j++ < 16 && i < 255)
+ sprintf (buffer + strlen (buffer), " %02x", regs[i++]);
+ DBG (5, " %s\n", buffer);
+ }
+
+ DBG (5, " Position:\n");
+ DBG (5, " Distance without scanning: %u\n",
+ get_lsbfirst_int (regs + 0x60, 2));
+ DBG (5, " Total distance: %u\n",
+ get_lsbfirst_int (regs + 0x62, 2));
+ DBG (5, " Scanning distance: %u\n",
+ get_lsbfirst_int (regs + 0x62, 2) - get_lsbfirst_int (regs + 0x60, 2));
+ DBG (5, " Direction: %s\n",
+ (regs[0xc6] & 0x08) ? "forward" : "rewind");
+ DBG (5, " Motor: %s\n",
+ (regs[0xc3] & 0x80) ? "enabled" : "disabled");
+ if (regs[0x7a])
+ DBG (5, " X range: %u-%u\n",
+ get_lsbfirst_int (regs + 0x66, 2) / regs[0x7a],
+ get_lsbfirst_int (regs + 0x6c, 2) / regs[0x7a]);
+ DBG (5, " TG Info:\n");
+ DBG (5, " CPH0P: %06x\n",
+ get_lsbfirst_int (regs + 0xf0, 3));
+ DBG (5, " CRSP: %06x\n",
+ get_lsbfirst_int (regs + 0xf9, 3));
+ DBG (5, " CCLPP: %06x\n",
+ get_lsbfirst_int (regs + 0xfc, 3));
+ DBG (5, " CPH0S: %d\n",
+ (regs[0x2d] & 0x20) ? 1 : 0);
+ DBG (5, " CDSS1: %02x\n", regs[0x28] & 0x1f);
+ DBG (5, " CDSC1: %02x\n", regs[0x29] & 0x1f);
+ DBG (5, " CDSS2: %02x\n", regs[0x2a] & 0x1f);
+ DBG (5, " CDSC2: %02x\n", regs[0x2b] & 0x1f);
+
+ DBG (5, " Resolution specific:\n");
+ if (!regs[0x7a])
+ DBG (5, " Horizontal resolution: Denominator is zero!\n");
+ else
+ DBG (5, " Horizontal resolution: %u\n", 300
+ * ((regs[0x2d] & 0x20) ? 2 : 1)
+ * ((regs[0xd3] & 0x08) ? 2 : 1) / regs[0x7a]);
+ DBG (5, " Derived vertical resolution: %u\n",
+ 400 * (regs[0xc3] & 0x1f) * convert_c6 (regs[0xc6] & 0x7) /
+ (regs[0x39] + 1));
+ DBG (5, " Register D3:3 %u\n",
+ (regs[0xd3] & 0x08) ? 1 : 0);
+ DBG (5, " Register 39: %u\n", regs[0x39]);
+ DBG (5, " Register C3:0-5: %u\n", regs[0xc3] & 0x1f);
+ DBG (5, " Register C6:0-2: %u\n", regs[0xc6] & 0x7);
+ DBG (5, " Motor movement clock multiplier: %u\n", regs[0x40] >> 6);
+ DBG (5, " Step Size: %04x\n",
+ get_lsbfirst_int (regs + 0xe2, 2));
+ DBG (5, " Frequency: %u\n", regs[0x64] & 0xf);
+ DBG (5, " Colour registers\n");
+ DBG (5, " Register 2F: %02x\n", regs[0x2f]);
+ DBG (5, " Register 2C: %02x\n", regs[0x2c]);
+ if (regs[0x7a])
+ {
+ DBG (5, " Scan data estimates:\n");
+ pixels =
+ (long) (get_lsbfirst_int (regs + 0x62, 2) -
+ get_lsbfirst_int (regs + 0x60,
+ 2)) * (long) (get_lsbfirst_int (regs + 0x6c,
+ 2) -
+ get_lsbfirst_int (regs + 0x66,
+ 2)) /
+ regs[0x7a];
+ DBG (5, " Pixels: %ld\n", pixels);
+ DBG (5, " Bytes at 24BPP: %ld\n", pixels * 3);
+ DBG (5, " Bytes at 1BPP: %ld\n", pixels / 8);
+ }
+ DBG (5, "\n");
+}
+
+static int
+constrain (int val, int min, int max)
+{
+ if (val < min)
+ {
+ DBG (10, "Clipped %d to %d\n", val, min);
+ val = min;
+ }
+ else if (val > max)
+ {
+ DBG (10, "Clipped %d to %d\n", val, max);
+ val = max;
+ }
+ return val;
+}
+
+
+static int
+rts8801_doscan (unsigned width,
+ unsigned height,
+ unsigned colour,
+ unsigned red_green_offset,
+ unsigned green_blue_offset,
+ unsigned intra_channel_offset,
+ rts8801_callback cbfunc,
+ void *params,
+ int oddfirst,
+ unsigned char const *calib_info,
+ int merged_channels,
+ detailed_calibration_data const *detailed_calib_data)
+{
+ unsigned rowbytes = 0;
+ unsigned output_rowbytes = 0;
+ unsigned channels = 0;
+ unsigned total_rows = 0;
+ unsigned bytesperchannel;
+ unsigned char *row_buffer;
+ unsigned char *output_buffer;
+ unsigned buffered_rows;
+ int rows_to_begin;
+ int rowbuffer_bytes;
+ int n;
+ unsigned rownow = 0;
+ unsigned bytenow = 0;
+ unsigned char *channel_data[3][2];
+ unsigned i;
+ unsigned j;
+ int result = 0;
+ unsigned rows_supplied = 0;
+
+ calib_info = calib_info; /* Kill warning */
+ if (cancelled_scan)
+ return -1;
+ rt_start_moving ();
+
+ channels = 3;
+ rowbytes = width * 3;
+ bytesperchannel = width;
+ switch (colour)
+ {
+ case HP3500_GRAY_SCAN:
+ output_rowbytes = width;
+ break;
+
+ case HP3500_COLOR_SCAN:
+ output_rowbytes = rowbytes;
+ break;
+
+ case HP3500_LINEART_SCAN:
+ output_rowbytes = (width + 7) / 8;
+ break;
+ }
+
+ buffered_rows =
+ red_green_offset + green_blue_offset + intra_channel_offset + 1;
+ rows_to_begin = buffered_rows;
+ rowbuffer_bytes = buffered_rows * rowbytes;
+ row_buffer = (unsigned char *) malloc (rowbuffer_bytes);
+ output_buffer = (unsigned char *) malloc (rowbytes);
+
+ for (i = j = 0; i < channels; ++i)
+ {
+ if (i == 1)
+ j += red_green_offset;
+ else if (i == 2)
+ j += green_blue_offset;
+ if (merged_channels)
+ channel_data[i][1 - oddfirst] = row_buffer + rowbytes * j + i;
+ else
+ channel_data[i][1 - oddfirst] = row_buffer + rowbytes * j + width * i;
+ channel_data[i][oddfirst] =
+ channel_data[i][1 - oddfirst] + rowbytes * intra_channel_offset;
+ }
+
+ while (((n = rt_get_available_bytes ()) > 0 || rt_is_moving () > 0)
+ && !cancelled_scan)
+ {
+ if (n == 1 && (rt_is_moving () || rt_get_available_bytes () != 1))
+ n = 0;
+ if (n > 0)
+ {
+ unsigned char buffer[0xffc0];
+
+ if (n > 0xffc0)
+ n = 0xffc0;
+ else if ((n > 1) && (n & 1))
+ --n;
+ if (rt_get_data (n, buffer) >= 0)
+ {
+ unsigned char *bufnow = buffer;
+
+ while (n)
+ {
+ int numcopy = rowbytes - bytenow;
+
+ if (numcopy > n)
+ numcopy = n;
+
+ memcpy (row_buffer + rownow * rowbytes + bytenow,
+ bufnow, numcopy);
+ bytenow += numcopy;
+ bufnow += numcopy;
+ n -= numcopy;
+
+ if (bytenow == rowbytes)
+ {
+ if (!rows_to_begin || !--rows_to_begin)
+ {
+ unsigned char *outnow = output_buffer;
+
+ for (i = 0;
+ i < (merged_channels ? rowbytes : width);
+ i += merged_channels ? channels : 1)
+ {
+ for (j = 0; j < channels; ++j)
+ {
+ unsigned pix =
+ (unsigned char) channel_data[j][i & 1][i];
+
+ if (detailed_calib_data)
+ {
+ unsigned char const *calib_start =
+ detailed_calib_data->channeldata[j] +
+ 2 *
+ detailed_calib_data->
+ resolution_divisor * i /
+ (merged_channels ? channels : 1);
+ pix =
+ constrain ((int) pix -
+ (int) calib_start[0], 0,
+ 255);
+ pix =
+ constrain (pix * calib_start[1] /
+ 0x40, 0, 255);
+ }
+ *outnow++ = pix;
+ }
+ }
+
+ if (colour == HP3500_GRAY_SCAN || colour == HP3500_LINEART_SCAN)
+ {
+ unsigned char const *in_now = output_buffer;
+ int bit = 7;
+
+ outnow = output_buffer;
+ for (i = 0; i < width; ++i)
+ {
+
+ if (colour == HP3500_GRAY_SCAN)
+ {
+ *outnow++ = ((unsigned) in_now[0] * 2989 +
+ (unsigned) in_now[1] * 5870 +
+ (unsigned) in_now[2] * 1140) / 10000;
+ }
+ else
+ {
+ if (bit == 7)
+ *outnow = ((in_now[1] < 0x80) ? 0x80 : 0);
+ else if (in_now[1] < 0x80)
+ *outnow |= (1 << bit);
+ if (bit == 0)
+ {
+ ++outnow;
+ bit = 7;
+ }
+ else
+ {
+ --bit;
+ }
+ }
+ in_now += 3;
+ }
+ }
+ if (rows_supplied++ < height &&
+ !((*cbfunc) (params, output_rowbytes, output_buffer)))
+ break;
+
+ for (i = 0; i < channels; ++i)
+ {
+ for (j = 0; j < 2; ++j)
+ {
+ channel_data[i][j] += rowbytes;
+ if (channel_data[i][j] - row_buffer >=
+ rowbuffer_bytes)
+ channel_data[i][j] -= rowbuffer_bytes;
+ }
+ }
+ }
+ ++total_rows;
+ if (++rownow == buffered_rows)
+ rownow = 0;
+ bytenow = 0;
+ }
+ }
+ }
+ DBG (30, "total_rows = %d\r", total_rows);
+ }
+ else
+ {
+ usleep (10000);
+ }
+ }
+ DBG (10, "\n");
+ if (n < 0)
+ result = -1;
+
+ free (output_buffer);
+ free (row_buffer);
+
+ rt_stop_moving ();
+ return result;
+}
+
+static unsigned local_sram_size;
+static unsigned char r93setting;
+
+#define RTS8801_F_SUPPRESS_MOVEMENT 1
+
+static int
+find_resolution_index (unsigned resolution)
+{
+ int res = 0;
+
+ for (res = 0; resparms[res].resolution != resolution; ++res)
+ {
+ if (!resparms[res].resolution)
+ return -1;
+ }
+ return res;
+}
+
+static int
+rts8801_fullscan (unsigned x,
+ unsigned y,
+ unsigned w,
+ unsigned h,
+ unsigned xresolution,
+ unsigned yresolution,
+ unsigned colour,
+ rts8801_callback cbfunc,
+ void *param,
+ unsigned char *calib_info,
+ int flags,
+ int red_calib_offset,
+ int green_calib_offset,
+ int blue_calib_offset,
+ int end_calib_offset,
+ detailed_calibration_data const *detailed_calib_data)
+{
+ int ires, jres;
+ int tg_setting;
+ unsigned char regs[256];
+ unsigned char offdutytime;
+ int result;
+ int scan_frequency;
+
+ ires = find_resolution_index (xresolution);
+ jres = find_resolution_index (yresolution);
+
+ if (ires < 0 || jres < 0)
+ return -1;
+
+ /* Set scan parameters */
+
+ rt_read_register_immediate (0, 255, regs);
+ regs[255] = 0;
+
+ rt_enable_ccd (regs, 1);
+ rt_enable_movement (regs, 1);
+ rt_set_scan_frequency (regs, 1);
+
+ rt_adjust_misc_registers (regs);
+
+ rt_set_cvtr_wparams (regs, 3, 0, 6);
+ rt_set_cvtr_mpt (regs, 15, 15, 15);
+ rt_set_cvtr_lm (regs, 7, 7, 7);
+ rt_set_motor_type (regs, 2);
+
+ if (rt_nvram_read (0, 0x7b, &offdutytime, 1) < 0 || offdutytime >= 15)
+ {
+ offdutytime = 6;
+ }
+ rt_set_lamp_duty_cycle (regs, 1, /* On */
+ 10, /* Frequency */
+ offdutytime); /* Off duty time */
+
+ rt_set_movement_pattern (regs, 0x800000);
+
+
+ tg_setting = resparms[jres].tg;
+ rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p);
+ rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp);
+ rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp);
+
+
+ rt_set_one_register (0xc6, 0);
+ rt_set_one_register (0xc6, 0);
+
+ rt_set_step_size (regs, resparms[jres].step_size);
+
+ rt_set_direction_forwards (regs);
+
+ rt_set_stop_when_rewound (regs, 0);
+ rt_set_data_feed_on (regs);
+
+ rt_set_calibration_addresses (regs, 0, 0, 0, 0);
+
+ rt_set_basic_calibration (regs,
+ calib_info[0], calib_info[1], calib_info[2],
+ calib_info[3], calib_info[4], calib_info[5],
+ calib_info[6], calib_info[7], calib_info[8]);
+ regs[0x0b] = 0x70; /* If set to 0x71, the alternative, all values are low */
+
+ if (red_calib_offset >= 0
+ && green_calib_offset >= 0
+ && blue_calib_offset >= 0 &&
+ yresolution < 400)
+ {
+ rt_set_calibration_addresses (regs, red_calib_offset,
+ green_calib_offset, blue_calib_offset,
+ end_calib_offset);
+ regs[0x40] |= 0x2f;
+ detailed_calib_data = 0;
+ }
+ else if (end_calib_offset >= 0)
+ {
+ rt_set_calibration_addresses (regs, 0x600, 0x600, 0x600,
+ end_calib_offset);
+ regs[0x40] &= 0xc0;
+ }
+
+ rt_set_channel (regs, RT_CHANNEL_ALL);
+ rt_set_single_channel_scanning (regs, 0);
+ rt_set_merge_channels (regs, 1);
+ rt_set_colour_mode (regs, 1);
+
+ rt_set_motor_movement_clock_multiplier (regs,
+ resparms[jres].
+ motor_movement_clock_multiplier);
+
+ rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1,
+ tg_info[tg_setting].tg_cdss2);
+ rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1,
+ tg_info[tg_setting].tg_cdsc2);
+ rt_update_after_setting_cdss2 (regs);
+
+ rt_set_last_sram_page (regs, (local_sram_size - 1) >> 5);
+
+ regs[0x39] = resparms[jres].reg_39_value;
+ regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value;
+ regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value;
+ scan_frequency = resparms[jres].scan_frequency;
+ rt_set_scan_frequency (regs, scan_frequency);
+ rt_set_cph0s (regs, resparms[ires].cph0s);
+ if (resparms[ires].d3_bit_3_value)
+ regs[0xd3] |= 0x08;
+ else
+ regs[0xd3] &= 0xf7;
+
+ if (flags & RTS8801_F_SUPPRESS_MOVEMENT)
+ regs[0xc3] &= 0x7f;
+ rt_set_horizontal_resolution (regs, xresolution);
+
+ rt_set_noscan_distance (regs, y * scan_frequency - 1);
+ rt_set_total_distance (regs, scan_frequency *
+ (y +
+ h +
+ resparms[jres].red_green_offset +
+ resparms[jres].green_blue_offset +
+ resparms[jres].intra_channel_offset) - 1);
+
+ rt_set_scanline_start (regs,
+ x * (1200 / xresolution) /
+ (resparms[ires].cph0s ? 1 : 2) /
+ (resparms[ires].d3_bit_3_value ? 1 : 2));
+ rt_set_scanline_end (regs,
+ (x +
+ w) * (1200 / xresolution) /
+ (resparms[ires].cph0s ? 1 : 2) /
+ (resparms[ires].d3_bit_3_value ? 1 : 2));
+
+ rt_set_all_registers (regs);
+
+ rt_set_one_register (0x2c, regs[0x2c]);
+
+ if (DBG_LEVEL >= 5)
+ dump_registers (regs);
+
+ result = rts8801_doscan (w,
+ h,
+ colour,
+ resparms[jres].red_green_offset,
+ resparms[jres].green_blue_offset,
+ resparms[jres].intra_channel_offset,
+ cbfunc, param, (x & 1), calib_info,
+ (regs[0x2f] & 0x04) != 0, detailed_calib_data);
+
+ return result;
+}
+
+static int
+accumfunc (struct dcalibdata *dcd, int bytes, char *data)
+{
+ unsigned char *c = (unsigned char *) data;
+
+ while (bytes > 0)
+ {
+ if (dcd->firstrowdone)
+ dcd->buffers[dcd->channelnow][dcd->pixelnow - dcd->pixelsperrow] = *c;
+ if (++dcd->channelnow >= 3)
+ {
+ dcd->channelnow = 0;
+ if (++dcd->pixelnow == dcd->pixelsperrow)
+ ++dcd->firstrowdone;
+ }
+ c++;
+ bytes--;
+ }
+ return 1;
+}
+
+static int
+calcmedian (unsigned char const *data,
+ int pixel, int pixels_per_row, int elements)
+{
+ int tallies[256];
+ int i;
+ int elemstogo = elements / 2;
+
+ memset (tallies, 0, sizeof (tallies));
+ data += pixel;
+ for (i = 0; i < elements; ++i)
+ {
+ ++tallies[*data];
+ data += pixels_per_row;
+ }
+ i = 0;
+ while (elemstogo - tallies[i] > 0)
+ elemstogo -= tallies[i++];
+ return i;
+}
+
+struct calibdata
+{
+ unsigned char *buffer;
+ int space;
+};
+
+static int
+storefunc (struct calibdata *cd, int bytes, char *data)
+{
+ if (cd->space > 0)
+ {
+ if (bytes > cd->space)
+ bytes = cd->space;
+ memcpy (cd->buffer, data, bytes);
+ cd->buffer += bytes;
+ cd->space -= bytes;
+ }
+ return 1;
+}
+
+static unsigned
+sum_channel (unsigned char *p, int n, int bytwo)
+{
+ unsigned v = 0;
+
+ while (n-- > 0)
+ {
+ v += *p;
+ p += 3;
+ if (bytwo)
+ p += 3;
+ }
+ return v;
+}
+
+static int do_warmup = 1;
+
+static int
+rts8801_scan (unsigned x,
+ unsigned y,
+ unsigned w,
+ unsigned h,
+ unsigned resolution,
+ unsigned colour,
+ unsigned brightness,
+ unsigned contrast,
+ rts8801_callback cbfunc,
+ void *param)
+{
+ unsigned char calib_info[9];
+ unsigned char calibbuf[2400];
+ struct dcalibdata dcd;
+ struct calibdata cd;
+ unsigned char *detail_buffer = 0;
+ int iCalibOffset;
+ int iCalibX;
+ int iCalibY;
+ int iCalibWidth;
+ int iCalibTarget;
+ int iCalibPixels;
+ int iMoveFlags = 0;
+ unsigned int aiLow[3] = { 0, 0, 0 };
+ unsigned int aiHigh[3] = { 256, 256, 256 };
+ unsigned aiBestOffset[3];
+ int i;
+ unsigned j;
+ int anychanged;
+ int calibration_size;
+ unsigned char *pDetailedCalib;
+ int red_calibration_offset;
+ int green_calibration_offset;
+ int blue_calibration_offset;
+ int end_calibration_offset;
+ int base_resolution;
+ int resolution_divisor;
+ int resolution_index;
+ int detailed_calibration_rows = 50;
+ unsigned char *tdetail_buffer;
+ detailed_calibration_data detailed_calib_data;
+
+ /* Initialise and power up */
+
+ rt_set_all_registers (initial_regs);
+ rt_set_powersave_mode (0);
+
+ /* Initial rewind in case scanner is stuck away from home position */
+
+ rts8801_rewind ();
+
+ /* Detect SRAM */
+
+ rt_detect_sram (&local_sram_size, &r93setting);
+
+ /* Warm up the lamp */
+
+ DBG (10, "Warming up the lamp\n");
+
+ rt_turn_on_lamp ();
+ if (do_warmup)
+ sleep (25);
+
+ /* Basic calibration */
+
+ DBG (10, "Calibrating (stage 1)\n");
+
+ calib_info[2] = calib_info[5] = calib_info[8] = 1;
+
+ calib_info[0] = calib_info[1] = calib_info[3] = calib_info[4] =
+ calib_info[6] = calib_info[7] = 0xb4;
+
+ iCalibOffset = 0; /* Note that horizontal resolution is always 600dpi for calibration. 330 is 110 dots in (for R,G,B channels) */
+ iCalibX = 1;
+ iCalibPixels = 50;
+ iCalibY = (resolution == 25) ? 1 : 2; /* Was 1200 / resolution, which would take us past the calibration area for 50dpi */
+ iCalibWidth = 100;
+ iCalibTarget = 550;
+
+ for (i = 0; i < 3; ++i)
+ aiBestOffset[i] = 0xb4;
+
+ do
+ {
+ DBG (30, "Initial calibration pass commences\n");
+ anychanged = 0;
+
+ for (i = 0; i < 3; ++i)
+ {
+ aiBestOffset[i] = (aiHigh[i] + aiLow[i] + 1) / 2;
+ }
+
+ for (i = 0; i < 3; ++i)
+ calib_info[i * 3] = calib_info[i * 3 + 1] = aiBestOffset[i];
+
+ cd.buffer = calibbuf;
+ cd.space = sizeof (calibbuf);
+ DBG (30, "Commencing scan for initial calibration pass\n");
+ rts8801_fullscan (iCalibX, iCalibY, iCalibWidth, 2, 600, resolution,
+ HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd,
+ calib_info, iMoveFlags, -1, -1, -1, -1, 0);
+ DBG (30, "Completed scan for initial calibration pass\n");
+ iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT;
+
+ for (i = 0; i < 3; ++i)
+ {
+ int sum;
+
+ if (aiBestOffset[i] >= 255)
+ continue;
+ sum = sum_channel (calibbuf + iCalibOffset + i, iCalibPixels, 0);
+ DBG (20, "channel[%d] sum = %d (target %d)\n", i, sum,
+ iCalibTarget);
+
+ if (sum >= iCalibTarget)
+ aiHigh[i] = aiBestOffset[i];
+ else
+ aiLow[i] = aiBestOffset[i];
+ }
+ DBG (30, "Initial calibration pass completed\n");
+ }
+ while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1
+ && aiLow[1] < aiHigh[1] + 1);
+
+ DBG (20, "Offsets calculated\n");
+ cd.buffer = calibbuf;
+ cd.space = sizeof (calibbuf);
+ DBG (20, "Scanning for part 2 of initial calibration\n");
+ rts8801_fullscan (iCalibX + 2100, iCalibY, iCalibWidth, 2, 600, resolution,
+ HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd,
+ calib_info, RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1,
+ 0);
+ DBG (20, "Scan for part 2 of initial calibration completed\n");
+
+ DBG (20, "Initial calibration completed\n");
+
+ tdetail_buffer =
+ (unsigned char *) malloc (w * 3 * detailed_calibration_rows);
+ aiLow[0] = aiLow[1] = aiLow[2] = 1;
+ aiHigh[0] = aiHigh[1] = aiHigh[2] = 64;
+
+ do
+ {
+ struct dcalibdata dcdt;
+
+ for (i = 0; i < 3; ++i)
+ calib_info[i * 3 + 2] = (aiLow[i] + aiHigh[i]) / 2;
+
+ dcdt.buffers[0] = tdetail_buffer;
+ dcdt.buffers[1] = (tdetail_buffer + w * detailed_calibration_rows);
+ dcdt.buffers[2] = (dcdt.buffers[1] + w * detailed_calibration_rows);
+ dcdt.pixelsperrow = w;
+ dcdt.pixelnow = dcdt.channelnow = dcdt.firstrowdone = 0;
+ rts8801_fullscan (x, 4, w, detailed_calibration_rows + 1, resolution,
+ resolution, HP3500_COLOR_SCAN,
+ (rts8801_callback) accumfunc, &dcdt, calib_info,
+ RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, 0);
+ for (i = 0; i < 3; ++i)
+ {
+ int largest = 1;
+
+ for (j = 0; j < w; ++j)
+ {
+ int val =
+ calcmedian (dcdt.buffers[i], j, w, detailed_calibration_rows);
+
+ if (val > largest)
+ largest = val;
+ }
+
+ if (largest < 0xe0)
+ aiLow[i] = calib_info[i * 3 + 2];
+ else
+ aiHigh[i] = calib_info[i * 3 + 2];
+ }
+ }
+ while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1
+ && aiLow[1] < aiHigh[1] + 1);
+
+ for (i = 0; i < 3; ++i)
+ calib_info[i * 3 + 2] = aiLow[i];
+
+ for (i = 0; i < 3; ++i)
+ {
+ DBG (10, "Channel [%d] gain=%02x offset=%02x\n",
+ i, calib_info[i * 3] + 2, calib_info[i * 3]);
+ }
+
+ DBG (20, "Gain factors calculated\n");
+
+ /* Stage 2 calibration */
+
+ DBG (10, "Calibrating (stage 2)\n");
+
+ detail_buffer =
+ (unsigned char *) malloc (w * 3 * detailed_calibration_rows);
+
+ dcd.buffers[0] = detail_buffer;
+ dcd.buffers[1] = (detail_buffer + w * detailed_calibration_rows);
+ dcd.buffers[2] = (dcd.buffers[1] + w * detailed_calibration_rows);
+ dcd.pixelsperrow = w;
+ dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0;
+
+ DBG (10, "Performing detailed calibration scan\n");
+ rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1,
+ resolution, resolution, HP3500_COLOR_SCAN,
+ (rts8801_callback) accumfunc, &dcd, calib_info,
+ RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, -1, 0);
+
+ DBG (10, "Detailed calibration scan completed\n");
+
+ /* And now for the detailed calibration */
+ resolution_index = find_resolution_index (resolution);
+ base_resolution = 300;
+ if (resparms[resolution_index].cph0s)
+ base_resolution *= 2;
+ if (resparms[resolution_index].d3_bit_3_value)
+ base_resolution *= 2;
+ resolution_divisor = base_resolution / resolution;
+
+ calibration_size = w * resolution_divisor * 6 + 1536;
+ red_calibration_offset = 1536;
+ blue_calibration_offset =
+ red_calibration_offset + w * resolution_divisor * 2;
+ green_calibration_offset =
+ blue_calibration_offset + w * resolution_divisor * 2;
+ end_calibration_offset =
+ green_calibration_offset + w * resolution_divisor * 2;
+ pDetailedCalib = (unsigned char *) malloc (calibration_size);
+
+ memset (pDetailedCalib, 0, calibration_size);
+ for (i = 0; i < 3; ++i)
+ {
+ int idx =
+ (i == 0) ? red_calibration_offset : (i ==
+ 1) ? green_calibration_offset :
+ blue_calibration_offset;
+ double g = calib_info[i * 3 + 2];
+
+ for (j = 0; j < 256; j++)
+ {
+ int val = j;
+
+ if (val < 0)
+ val = 0;
+ if (val > 255)
+ val = 255;
+ pDetailedCalib[i * 512 + j * 2] = val;
+ pDetailedCalib[i * 512 + j * 2 + 1] = val;
+ }
+
+ for (j = 0; j < w; ++j)
+ {
+ int multnow;
+ int offnow;
+
+ /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed
+ * calibration to return either higher or lower values.
+ */
+ int k;
+
+ {
+ double denom1 =
+ calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows);
+ double f = 0xff / (denom1 - 2 * g);
+ double contrast_adjust = (double) (contrast + 1) / 64;
+
+ multnow = f * 64 * contrast_adjust;
+ offnow = 4 * g + 128 - brightness;
+ }
+ if (multnow < 0)
+ multnow = 0;
+ if (multnow > 255)
+ multnow = 255;
+ if (offnow < 0)
+ offnow = 0;
+ if (offnow > 255)
+ offnow = 255;
+
+ for (k = 0; k < resolution_divisor; ++k)
+ {
+ /*multnow = j * resolution_divisor + k; */
+ pDetailedCalib[idx++] = offnow; /* Subtract this value from the result */
+ pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x40 */
+ }
+ }
+ }
+
+ DBG (10, "\n");
+
+ rt_set_sram_page (0);
+ rt_set_one_register (0x93, r93setting);
+ rt_write_sram (calibration_size, pDetailedCalib);
+
+ /* And finally, perform the scan */
+
+ DBG (10, "Scanning\n");
+
+ rts8801_rewind ();
+
+ detailed_calib_data.channeldata[0] =
+ pDetailedCalib + red_calibration_offset;
+ detailed_calib_data.channeldata[1] =
+ pDetailedCalib + green_calibration_offset;
+ detailed_calib_data.channeldata[2] =
+ pDetailedCalib + blue_calibration_offset;
+ detailed_calib_data.resolution_divisor = resolution_divisor;
+
+ rts8801_fullscan (x, y, w, h, resolution, resolution, colour, cbfunc, param,
+ calib_info, 0,
+ red_calibration_offset, green_calibration_offset,
+ blue_calibration_offset, end_calibration_offset,
+ &detailed_calib_data);
+
+ rt_turn_off_lamp ();
+ rts8801_rewind ();
+ rt_set_powersave_mode (1);
+
+ if (pDetailedCalib)
+ free (pDetailedCalib);
+ if (detail_buffer)
+ free (detail_buffer);
+ return 0;
+}
+
+static int
+writefunc (struct hp3500_write_info *winfo, int bytes, char *data)
+{
+ static int warned = 0;
+
+ if (bytes > winfo->bytesleft)
+ {
+ if (!warned)
+ {
+ warned = 1;
+ DBG (1, "Overflow protection triggered\n");
+ rt_stop_moving ();
+ }
+ bytes = winfo->bytesleft;
+ if (!bytes)
+ return 0;
+ }
+ winfo->bytesleft -= bytes;
+ return write (winfo->scanner->pipe_w, data, bytes) == bytes;
+}
+
+static void
+sigtermHandler (int signal)
+{
+ signal = signal; /* get rid of compiler warning */
+ cancelled_scan = 1;
+}
+
+static int
+reader_process (void *pv)
+{
+ struct hp3500_data *scanner = pv;
+ time_t t;
+ sigset_t ignore_set;
+ sigset_t sigterm_set;
+ struct SIGACTION act;
+ struct hp3500_write_info winfo;
+ int status;
+
+ if (sanei_thread_is_forked ())
+ {
+ close (scanner->pipe_r);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ memset (&act, 0, sizeof (act));
+#ifdef _POSIX_SOURCE
+ act.sa_handler = sigtermHandler;
+#endif
+ sigaction (SIGTERM, &act, 0);
+ }
+
+
+ /* Warm up the lamp again if our last scan ended more than 5 minutes ago. */
+ time (&t);
+ do_warmup = (t - scanner->last_scan) > 300;
+
+ if (getenv ("HP3500_NOWARMUP") && atoi (getenv ("HP3500_NOWARMUP")) > 0)
+ do_warmup = 0;
+
+ udh = scanner->sfd;
+
+ cancelled_scan = 0;
+
+ winfo.scanner = scanner;
+ winfo.bytesleft =
+ scanner->bytes_per_scan_line * scanner->scan_height_pixels;
+
+ if (getenv ("HP3500_SLEEP"))
+ {
+ int seconds = atoi (getenv ("HP3500_SLEEP"));
+
+ DBG (1, "Backend process %d sleeping for %d seconds\n", getpid (),
+ seconds);
+ sleep (seconds);
+ }
+ DBG (10, "Scanning at %ddpi, mode=%s\n", scanner->resolution,
+ scan_mode_list[scanner->mode]);
+ if (rts8801_scan
+ (scanner->actres_pixels.left + 250 * scanner->resolution / 1200,
+ scanner->actres_pixels.top + 599 * scanner->resolution / 1200,
+ scanner->actres_pixels.right - scanner->actres_pixels.left,
+ scanner->actres_pixels.bottom - scanner->actres_pixels.top,
+ scanner->resolution, scanner->mode, scanner->brightness,
+ scanner->contrast, (rts8801_callback) writefunc, &winfo) >= 0)
+ status = SANE_STATUS_GOOD;
+ status = SANE_STATUS_IO_ERROR;
+ close (scanner->pipe_w);
+ return status;
+}
diff --git a/backend/hp3900.c b/backend/hp3900.c
new file mode 100644
index 0000000..46ec673
--- /dev/null
+++ b/backend/hp3900.c
@@ -0,0 +1,61 @@
+/* HP Scanjet 3900 series - Stand-alone/SANE Backend controller
+
+ Copyright (C) 2005-2008 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+
+/* Backend info */
+#define BACKEND_NAME hp3900
+#define BACKEND_VRSN "0.12"
+#define BACKEND_AUTHOR "Jonathan Bravo Lopez (JKD)"
+#define BACKEND_EMAIL "jkdsoft@gmail.com"
+#define BACKEND_URL "http://jkdsoftware.dyndns.org"
+#define BACKEND_LICENSE "General Public License (GPL)"
+
+/* if you want to compile this one as a sane backend then comment next line */
+/* Caution: Sources included in SANE project don't provide hp3900_stdalone.c */
+
+/*#define STANDALONE*/
+
+#ifdef STANDALONE
+#include "hp3900_stdalone.c"
+#else
+#include "hp3900_sane.c"
+#endif
diff --git a/backend/hp3900.conf.in b/backend/hp3900.conf.in
new file mode 100644
index 0000000..2de0001
--- /dev/null
+++ b/backend/hp3900.conf.in
@@ -0,0 +1,31 @@
+#
+# Configuration file for the hp3900 backend
+#
+
+# HP Scanjet 3800
+usb 0x03f0 0x2605
+
+# HP Scanjet 3970c
+usb 0x03f0 0x2305
+
+# HP Scanjet 4070 Photosmart
+usb 0x03f0 0x2405
+
+# HP Scanjet 4370
+usb 0x03f0 0x4105
+
+# HP Scanjet G2710
+usb 0x03f0 0x2805
+
+# HP Scanjet G3010
+usb 0x03f0 0x4205
+
+# HP Scanjet G3110
+usb 0x03f0 0x4305
+
+# UMAX Astra 4900/4950
+usb 0x06dc 0x0020
+
+# BenQ 5550
+usb 0x04a5 0x2211
+
diff --git a/backend/hp3900_config.c b/backend/hp3900_config.c
new file mode 100644
index 0000000..3c0ed77
--- /dev/null
+++ b/backend/hp3900_config.c
@@ -0,0 +1,6055 @@
+/* HP Scanjet 3900 series - RTS8822 internal config
+
+ Copyright (C) 2005-2009 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* returns device model according to given product and vendor id's */
+static SANE_Int cfg_device_get(SANE_Int product, SANE_Int vendor);
+
+/* returns information and capabilities about selected chipset model */
+static SANE_Int cfg_chipset_get(SANE_Int model, struct st_chip *chipset);
+
+/* returns the chipset model for each scanner */
+static SANE_Int cfg_chipset_model_get(SANE_Int device);
+
+/* buttons for each scanner */
+static SANE_Int cfg_buttons_get(struct st_buttons *reg);
+
+/* area constrains for each scanner */
+static SANE_Int cfg_constrains_get(struct st_constrains *constrain);
+
+/* spectrum clock generator for each scanner */
+static SANE_Int cfg_sscg_get(SANE_Int *enable, SANE_Int *mode, SANE_Int *clock);
+
+/* motor general configuration for each scanner */
+static SANE_Int cfg_motor_get(struct st_motorcfg *reg);
+
+/* motor resource for each scanner */
+static SANE_Byte *cfg_motor_resource_get(SANE_Byte *size);
+
+/* sensor general configuration for each scanner */
+static SANE_Int cfg_sensor_get(struct st_sensorcfg *reg);
+
+/* reference voltages for each scanner */
+static void cfg_refvoltages_get(SANE_Int sensortype, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs);
+static void hp3800_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs);
+static void hp3970_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs);
+
+/* offset calibration start and length */
+static void cfg_offset_get(SANE_Int sensortype, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width);
+static void ua4900_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width);
+static void hp3800_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width);
+static void hp3970_offset(SANE_Int sensor, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width);
+static void hp4370_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width);
+
+/* autoref configuration */
+static void cfg_autoref_get(struct st_autoref *reg);
+
+/* autoref start effective pixel */
+static SANE_Int cfg_effectivepixel_get(SANE_Int sensortype, SANE_Int resolution);
+static SANE_Int ua4900_effectivepixel(SANE_Int resolution);
+static SANE_Int hp3800_effectivepixel(SANE_Int resolution);
+static SANE_Int hp3970_effectivepixel(SANE_Int sensor, SANE_Int resolution);
+static SANE_Int hp4370_effectivepixel(SANE_Int resolution);
+
+/* default values for gain and offset */
+static SANE_Int cfg_gainoffset_get(SANE_Int sensortype, struct st_gain_offset *reg);
+static SANE_Int bq5550_gainoffset(SANE_Int usb, struct st_gain_offset *myreg);
+static SANE_Int ua4900_gainoffset(SANE_Int usb, struct st_gain_offset *myreg);
+static SANE_Int hp3800_gainoffset(SANE_Int usb, struct st_gain_offset *myreg);
+static SANE_Int hp3970_gainoffset(SANE_Int usb, SANE_Int sensor, struct st_gain_offset *myreg);
+static SANE_Int hp4370_gainoffset(SANE_Int usb, struct st_gain_offset *myreg);
+
+/* values to detect optimum pulse-width modulation */
+static SANE_Int cfg_checkstable_get(SANE_Int lamp, struct st_checkstable *check);
+static SANE_Int ua4900_checkstable(SANE_Int lamp, struct st_checkstable *check);
+static SANE_Int hp3800_checkstable(SANE_Int lamp, struct st_checkstable *check);
+static SANE_Int hp3970_checkstable(SANE_Int lamp, struct st_checkstable *check);
+static SANE_Int hp4370_checkstable(SANE_Int lamp, struct st_checkstable *check);
+
+/* fixed pulse-width modulation values */
+static SANE_Int cfg_fixedpwm_get(SANE_Int sensortype, SANE_Int scantype);
+static SANE_Int ua4900_fixedpwm(SANE_Int scantype, SANE_Int usb);
+static SANE_Int hp3800_fixedpwm(SANE_Int scantype, SANE_Int usb);
+static SANE_Int hp3970_fixedpwm(SANE_Int scantype, SANE_Int usb, SANE_Int sensor);
+static SANE_Int hp4370_fixedpwm(SANE_Int scantype, SANE_Int usb);
+
+/* virtual origin (ser and ler references) */
+static void cfg_vrefs_get(SANE_Int sensortype, SANE_Int res, SANE_Int *ser, SANE_Int *ler);
+static void hp3800_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler);
+static void hp3970_vrefs(SANE_Int usb, SANE_Int sensor, SANE_Int res, SANE_Int *ser, SANE_Int *ler);
+static void hp4370_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler);
+
+/* scanmodes supported by each scanner */
+static SANE_Int cfg_scanmode_get(SANE_Int sensortype, SANE_Int sm, struct st_scanmode *mymode);
+static SANE_Int bq5550_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode);
+static SANE_Int ua4900_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode);
+static SANE_Int hp3800_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode);
+static SANE_Int hp3970_scanmodes(SANE_Int usb, SANE_Int ccd, SANE_Int sm, struct st_scanmode *mymode);
+static SANE_Int hp4370_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode);
+
+/* timing values for ccd sensors */
+static SANE_Int cfg_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg);
+static SANE_Int bq5550_timing_get(SANE_Int tm, struct st_timing *reg);
+static SANE_Int ua4900_timing_get(SANE_Int tm, struct st_timing *reg);
+static SANE_Int hp3800_timing_get(SANE_Int tm, struct st_timing *reg);
+static SANE_Int hp3970_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg);
+static SANE_Int hp4370_timing_get(SANE_Int tm, struct st_timing *reg);
+
+/* motor movements */
+static SANE_Int cfg_motormove_get(SANE_Int sensortype, SANE_Int mm, struct st_motormove *reg);
+static SANE_Int bq5550_motormove(SANE_Int item, struct st_motormove *reg);
+static SANE_Int hp3800_motormove(SANE_Int item, struct st_motormove *reg);
+static SANE_Int hp3970_motormove(SANE_Int usb, SANE_Int ccd, SANE_Int item, struct st_motormove *reg);
+
+/* motor curves */
+static SANE_Int *cfg_motorcurve_get(void);
+static SANE_Int *bq5550_motor(void);
+static SANE_Int *hp3800_motor(void);
+static SANE_Int *hp3970_motor(void);
+static SANE_Int *hp4370_motor(void);
+
+/* shading cut values */
+static void cfg_shading_cut_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void ua4900_shading_cut(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void hp3800_shading_cut(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void hp3970_shading_cut(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void hp4370_shading_cut(SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+
+/* wrefs values */
+static void cfg_wrefs_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void ua4900_wrefs(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void hp3800_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void hp3970_wrefs(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+static void hp4370_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue);
+
+
+/* DEPRECATED FUNCTIONS !!!!!!!!!!!!!!!!!!! */
+
+
+static int get_value(int section, int option, int defvalue, int file);
+
+/* HP Scanjet 3800 */
+static int hp3800_calibreflective(int option, int defvalue);
+static int hp3800_calibtransparent(int option, int defvalue);
+static int hp3800_calibnegative(int option, int defvalue);
+static int srt_hp3800_scanparam_get(int option, int defvalue);
+
+/* UMAX Astra 4900 */
+static int ua4900_calibreflective(int option, int defvalue);
+static int ua4900_calibtransparent(int option, int defvalue);
+static int ua4900_calibnegative(int option, int defvalue);
+
+/* HP Scanjet 3970 */
+static int hp3970_calibreflective(int option, int defvalue);
+static int hp3970_calibtransparent(int option, int defvalue);
+static int hp3970_calibnegative(int option, int defvalue);
+static int srt_hp3970_scanparam_get(int file, int option, int defvalue);
+static int srt_hp3970_platform_get(int option, int defvalue);
+
+/* HP Scanjet 4370 */
+static int hp4370_calibreflective(int option, int defvalue);
+static int hp4370_calibtransparent(int option, int defvalue);
+static int hp4370_calibnegative(int option, int defvalue);
+static int srt_hp4370_scanparam_get(int file, int option, int defvalue);
+
+/* HP Scanjet g3110 */
+static int hpg3110_calibnegative(int option, int defvalue);
+static int hpg3110_calibtransparent(int option, int defvalue);
+
+/* ----- Implementation ----- */
+
+/* DEPRECATED enumerations */
+
+enum ConfigFiles
+{
+ FITCALIBRATE=0,
+
+ T_RTINIFILE, T_USB1INIFILE,
+ S_RTINIFILE, S_USB1INIFILE
+};
+
+enum fcsec6
+{
+ CALIBREFLECTIVE = 0, CALIBTRANSPARENT, CALIBNEGATIVEFILM,
+
+ SCANINFO,
+ SCAN_CALI,
+
+ WSTRIPXPOS, WSTRIPYPOS,
+ BSTRIPXPOS, BSTRIPYPOS,
+
+ BREFR, BREFG, BREFB,
+
+ REFBITDEPTH,
+ OFFSETHEIGHT,
+
+ OFFSETNSIGMA, OFFSETTARGETMAX, OFFSETTARGETMIN,
+ OFFSETAVGTARGETR, OFFSETAVGTARGETG, OFFSETAVGTARGETB,
+
+ ADCOFFEVENODD,
+ CALIBOFFSET1ON,
+
+ ADCOFFQUICKWAY, ADCOFFPREDICTSTART, ADCOFFPREDICTEND,
+
+ OFFSETTUNESTEP1, OFFSETBOUNDARYRATIO1, OFFSETAVGRATIO1,
+
+ OFFSETEVEN1R, OFFSETEVEN1G, OFFSETEVEN1B,
+ OFFSETODD1R, OFFSETODD1G, OFFSETODD1B,
+
+ ADCOFFPREDICTR, ADCOFFPREDICTG, ADCOFFPREDICTB,
+ ADCOFFEVEN1R_1ST, ADCOFFEVEN1G_1ST, ADCOFFEVEN1B_1ST,
+ ADCOFFODD1R_1ST, ADCOFFODD1G_1ST, ADCOFFODD1B_1ST,
+
+ PEAKR, PEAKG, PEAKB,
+ MINR, MING, MINB,
+
+ CALIBOFFSET2ON,
+ OFFSETTUNESTEP2, OFFSETBOUNDARYRATIO2, OFFSETAVGRATIO2,
+
+ OFFSETEVEN2R, OFFSETEVEN2G, OFFSETEVEN2B,
+ OFFSETODD2R, OFFSETODD2G, OFFSETODD2B,
+
+ GAINHEIGHT, GAINTARGETFACTOR,
+
+ CALIBPAGON,
+
+ HIPAGR, HIPAGG, HIPAGB,
+ LOPAGR, LOPAGG, LOPAGB,
+ PAGR, PAGG, PAGB,
+
+ CALIBGAIN1ON, GAIN1R, GAIN1G, GAIN1B,
+ CALIBGAIN2ON, GAIN2R, GAIN2G, GAIN2B,
+
+ TOTSHADING,
+
+ BSHADINGON, BSHADINGHEIGHT, BSHADINGPREDIFFR, BSHADINGPREDIFFG, BSHADINGPREDIFFB,
+ BSHADINGDEFCUTOFF,
+ WSHADINGON, WSHADINGHEIGHT, WSHADINGPREDIFFR, WSHADINGPREDIFFG, WSHADINGPREDIFFB,
+
+ PARKHOMEAFTERCALIB,
+
+ SHADINGTIME_16BIT, SHADOWTIME_16BIT, SHADINGTIME_8BIT, SHADOWTIME_8BIT,
+ PREVIEWDPI,
+
+ FIRSTDCOFFSETEVEN0, FIRSTDCOFFSETODD0, FIRSTDCOFFSETEVEN1,
+ FIRSTDCOFFSETODD1, FIRSTDCOFFSETEVEN2, FIRSTDCOFFSETODD2,
+
+ CALIBOFFSET10N, CALIBOFFSET20N,
+ CALIBGAIN10N, CALIBGAIN20N,
+ ARRANGELINE,
+ COMPRESSION,
+
+ TA_X_START, TA_Y_START,
+
+ DPIGAINCONTROL600,
+ DPIGAINCONTROL_TA600,
+ DPIGAINCONTROL_NEG600,
+
+ CRVS, MLOCK,
+ ENABLEWARMUP,
+
+ NMAXTARGET, NMINTARGET,
+ NMAXTARGETTA, NMINTARGETTA,
+ NMAXTARGETNEG, NMINTARGETNEG,
+
+ STABLEDIFF,
+ DELTAPWM,
+
+ PWMLAMPLEVEL,
+
+ TMAPWMDUTY,
+
+ PAG1, PAG2, PAG3,
+
+ LEFTLEADING,
+
+ WAVE_XSTART,
+
+ WAVE_S575_XDUMMY_2400, WAVE_S575_XDUMMY_1200, WAVE_S575_XDUMMY_600,
+
+ ODD_DCOFFSET11, ODD_DCOFFSET12, ODD_DCOFFSET13,
+ ODD_DCOFFSET21, ODD_DCOFFSET22, ODD_DCOFFSET23,
+
+ EVEN_DCOFFSET11, EVEN_DCOFFSET12, EVEN_DCOFFSET13,
+ EVEN_DCOFFSET21, EVEN_DCOFFSET22, EVEN_DCOFFSET23,
+
+ DCGAIN11, DCGAIN12, DCGAIN13,
+ DCGAIN21, DCGAIN22, DCGAIN23,
+
+ CRYSTALFREQ,
+
+ PGA1, PGA2, PGA3,
+
+ VGAGAIN11, VGAGAIN12, VGAGAIN13,
+
+ DCSTEPEVEN1, DCSTEPODD1,
+ DCSTEPEVEN2, DCSTEPODD2,
+ DCSTEPEVEN3, DCSTEPODD3,
+
+ FIRSTDCOFFSETEVEN11, FIRSTDCOFFSETODD11,
+ FIRSTDCOFFSETEVEN12, FIRSTDCOFFSETODD12,
+ FIRSTDCOFFSETEVEN13, FIRSTDCOFFSETODD13,
+
+ DCOFFSETEVEN11, DCOFFSETODD11,
+ DCOFFSETEVEN12, DCOFFSETODD12,
+ DCOFFSETEVEN13, DCOFFSETODD13,
+
+ SHADINGBASE, SHADINGFACT1, SHADINGFACT2, SHADINGFACT3,
+
+ PIXELDARKLEVEL,
+
+ EXPOSURETIME,
+ SCANYSTART, SCANYLINES,
+
+ BINARYTHRESHOLDH, BINARYTHRESHOLDL,
+ CLOSETIME,
+ PLATFORM,
+ SCAN_PARAM,
+ USB1_PWM, USB2_PWM,
+ WAVETEST,
+ DMA_PARAM,
+ TRUE_GRAY_PARAM,
+ CALI_PARAM,
+ DUMMYLINE,
+
+ MEXPT1, MEXPT2, MEXPT3,
+ EXPT1, EXPT2, EXPT3,
+
+ STARTPOS,
+ LINEDARLAMPOFF,
+ MCLKIOC
+};
+
+static SANE_Int cfg_device_get(SANE_Int product, SANE_Int vendor)
+{
+ struct st_myreg
+ {
+ SANE_Int vendor, product, device;
+ };
+
+ struct st_myreg myreg[] =
+ {
+ /*vendor, prodct, device */
+ { 0x4a5, 0x2211, BQ5550 }, /* BenQ 5550 */
+ { 0x6dc, 0x0020, UA4900 }, /* UMAX Astra 4900 */
+ { 0x3f0, 0x2605, HP3800 }, /* HP Scanjet 3800 */
+ { 0x3f0, 0x2805, HPG2710}, /* HP Scanjet G2710 */
+ { 0x3f0, 0x2305, HP3970 }, /* HP Scanjet 3970c */
+ { 0x3f0, 0x2405, HP4070 }, /* HP Scanjet 4070 Photosmart */
+ { 0x3f0, 0x4105, HP4370 }, /* HP Scanjet 4370 */
+ { 0x3f0, 0x4205, HPG3010}, /* HP Scanjet G3010 */
+ { 0x3f0, 0x4305, HPG3110} /* HP Scanjet G3010 */
+ };
+
+ SANE_Int rst = -1; /* default */
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg);
+
+ for (a = 0; a < count; a++)
+ {
+ if ((vendor == myreg[a].vendor)&&(product == myreg[a].product))
+ {
+ rst = myreg[a].device;
+
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_chipset_model_get(SANE_Int device)
+{
+ /* returns the chipset model for each scanner */
+ struct st_myreg
+ {
+ SANE_Int device, chipset;
+ };
+
+ struct st_myreg myreg[] =
+ {
+ /*device , chipset */
+ { HP3800 , RTS8822BL_03A },
+ { HPG2710, RTS8822BL_03A },
+ { BQ5550 , RTS8823L_01E },
+ { UA4900 , RTS8822L_01H },
+ { HP3970 , RTS8822L_01H },
+ { HP4070 , RTS8822L_01H },
+ { HP4370 , RTS8822L_02A },
+ { HPG3010, RTS8822L_02A },
+ { HPG3110, RTS8822L_02A }
+ };
+
+ SANE_Int rst = RTS8822L_01H; /* default */
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (device == myreg[a].device)
+ {
+ rst = myreg[a].chipset;
+
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_chipset_get(SANE_Int model, struct st_chip *chipset)
+{
+ /* returns info and capabilities of selected chipset */
+ SANE_Int rst = ERROR;
+
+ if (chipset != NULL)
+ {
+ struct st_chip data[] =
+ {
+ /* model , capabilities, name */
+ {RTS8823L_01E , 0 , "RTS8823L-01E" },
+ {RTS8822BL_03A, CAP_EEPROM , "RTS8822BL-03A"},
+ {RTS8822L_02A , CAP_EEPROM , "RTS8822L-02A" },
+ {RTS8822L_01H , CAP_EEPROM , "RTS8822L-01H" }
+ };
+
+ SANE_Int a;
+
+ for (a = 0; a < 4; a++)
+ {
+ if (model == data[a].model)
+ {
+ /* model found, fill information */
+ chipset->model = data[a].model;
+ chipset->capabilities = data[a].capabilities;
+ chipset->name = strdup(data[a].name);
+
+ if (chipset->name != NULL)
+ rst = OK;
+
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Device's Buttons ---------- */
+
+static SANE_Int cfg_buttons_get(struct st_buttons *reg)
+{
+ /* buttons for each scanner */
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ struct st_myreg
+ {
+ SANE_Int device;
+ struct st_buttons value;
+ };
+
+ struct st_myreg myreg[] =
+ {
+ /*device, {count, {btn1, btn2, btn3, btn4, btn5, btn6)} */
+ { BQ5550 , {3 , {0x01, 0x02, 0x08, -1, -1, -1}}},
+ { UA4900 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}},
+ { HP3800 , {3 , {0x01, 0x02, 0x04, -1, -1, -1}}},
+ { HPG2710, {3 , {0x01, 0x02, 0x04, -1, -1, -1}}},
+ { HP3970 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}},
+ { HP4070 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}},
+ { HP4370 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}},
+ { HPG3010, {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}},
+ { HPG3110, {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (RTS_Debug->dev_model == myreg[a].device)
+ {
+ memcpy(reg, &myreg[a].value, sizeof(struct st_buttons));
+ rst = OK;
+
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Spectrum clock generator ---------- */
+
+static SANE_Int cfg_sscg_get(SANE_Int *enable, SANE_Int *mode, SANE_Int *clock)
+{
+ SANE_Int rst = ERROR;
+
+ if ((enable != NULL)&&(mode != NULL)&&(clock != NULL))
+ {
+ struct st_myreg
+ {
+ SANE_Int device;
+ SANE_Int value[3];
+ };
+
+ struct st_myreg myreg[] =
+ {
+ /*device, {enable, mode, clock} */
+ { BQ5550, {1 , 1, 1}},
+ { UA4900, {1 , 1, 0}},
+ { HP3800, {1 , 1, 0}},
+ {HPG2710, {1 , 1, 0}},
+ { HP3970, {1 , 1, 0}},
+ { HP4070, {1 , 1, 0}},
+ { HP4370, {1 , 1, 0}},
+ {HPG3010, {1 , 1, 0}},
+ {HPG3110, {1 , 1, 0}}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg);
+
+ /* default values */
+ *enable = 0;
+ *mode = 0;
+ *clock = 3;
+
+ for (a = 0; a < count; a++)
+ {
+ if (RTS_Debug->dev_model == myreg[a].device)
+ {
+ *enable = myreg[a].value[0];
+ *mode = myreg[a].value[1];
+ *clock = myreg[a].value[2];
+ rst = OK;
+
+ break;
+ }
+ }
+ }
+ return rst;
+}
+
+/** SEC: Motors ---------- */
+
+static SANE_Int cfg_motor_get(struct st_motorcfg *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ struct st_myreg
+ {
+ SANE_Int device;
+ struct st_motorcfg motor;
+ };
+
+ struct st_myreg myreg[] =
+ {
+ /*device, {type , res, freq, speed, basemove, highmove, parkmove, change}} */
+ { BQ5550, {MT_OUTPUTSTATE, 1200, 30, 800, 1, 0, 0, TRUE}},
+ { UA4900, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}},
+ { HP3800, {MT_OUTPUTSTATE, 1200, 30, 800, 1, 0, 0, TRUE}},
+ {HPG2710, {MT_OUTPUTSTATE, 1200, 30, 800, 1, 0, 0, TRUE}},
+ { HP3970, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}},
+ { HP4070, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}},
+ { HP4370, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}},
+ {HPG3010, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}},
+ {HPG3110, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg);
+
+ /* default values */
+ memset(reg, 0, sizeof(struct st_motorcfg));
+ reg->type = -1;
+
+ for (a = 0; a < count; a++)
+ {
+ if (RTS_Debug->dev_model == myreg[a].device)
+ {
+ memcpy(reg, &myreg[a].motor, sizeof(struct st_motorcfg));
+ rst = OK;
+
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Sensors ---------- */
+
+static SANE_Int cfg_sensor_get(struct st_sensorcfg *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ struct st_myreg
+ {
+ SANE_Int device;
+ struct st_sensorcfg sensor;
+ };
+
+ struct st_myreg myreg[] =
+ {
+ /*device, {type , name , resolution, {chnl_colors }, {chnl_gray }, {rgb_order }, line_dist, evenodd_dist} */
+ { BQ5550, {CCD_SENSOR, -1, 1200 , {CL_BLUE, CL_GREEN, CL_RED }, {CL_GREEN, 0}, {CL_BLUE, CL_GREEN, CL_RED }, 24 , 4 }},
+ { UA4900, {CIS_SENSOR, SNYS575, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 24 , 0 }},
+ { HP3800, {CCD_SENSOR, TCD2905, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 64 , 8 }},
+ {HPG2710, {CCD_SENSOR, TCD2905, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 64 , 8 }},
+ { HP3970, {CCD_SENSOR, TCD2952, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 24 , 4 }},
+ { HP4070, {CCD_SENSOR, TCD2952, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 24 , 4 }},
+ { HP4370, {CCD_SENSOR, TCD2958, 4800 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 128 , 6 }},
+ {HPG3010, {CCD_SENSOR, TCD2958, 4800 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 128 , 6 }},
+ {HPG3110, {CCD_SENSOR, TCD2958, 4800 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 128 , 6 }}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg);
+
+ /* default values */
+ memset(reg, 0, sizeof(struct st_sensorcfg));
+ reg->type = -1;
+
+ for (a = 0; a < count; a++)
+ {
+ if (RTS_Debug->dev_model == myreg[a].device)
+ {
+ memcpy(reg, &myreg[a].sensor, sizeof(struct st_sensorcfg));
+ rst = OK;
+
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Reference voltages ---------- */
+
+static void hp3800_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs)
+{
+ /* this function returns top, middle and bottom reference voltages for each scanner */
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ SANE_Byte values[3];
+ };
+
+ struct st_reg myreg[] =
+ {
+ /* usb, sensor , {vrts, vrms, vrbs} */
+ {USB20, CCD_SENSOR, { 2, 3, 2}},
+ {USB11, CCD_SENSOR, { 2, 3, 2}},
+ };
+
+ if ((vrts != NULL)&&(vrms != NULL)&&(vrbs != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ *vrts = *vrms = *vrbs = 0;
+
+ for (a = 0; a < count; a++)
+ {
+ if ((myreg[a].usb == usb)&&(myreg[a].sensor == sensor))
+ {
+ *vrts = myreg[a].values[0];
+ *vrms = myreg[a].values[1];
+ *vrbs = myreg[a].values[2];
+ }
+ }
+ }
+}
+
+static void hp3970_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs)
+{
+ /* this function returns top, middle and bottom reference voltages for each scanner */
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ SANE_Byte values[3];
+ };
+
+ struct st_reg myreg[] =
+ {
+ /* usb, sensor , {vrts, vrms, vrbs} */
+ {USB20, CCD_SENSOR, { 0, 0, 0}},
+ {USB11, CCD_SENSOR, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, { 0, 0, 0}}
+ };
+
+ if ((vrts != NULL)&&(vrms != NULL)&&(vrbs != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ *vrts = *vrms = *vrbs = 0;
+
+ for (a = 0; a < count; a++)
+ {
+ if ((myreg[a].usb == usb)&&(myreg[a].sensor == sensor))
+ {
+ *vrts = myreg[a].values[0];
+ *vrms = myreg[a].values[1];
+ *vrbs = myreg[a].values[2];
+ }
+ }
+ }
+}
+
+static void cfg_refvoltages_get(SANE_Int sensortype, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs)
+{
+ /* this function returns top, middle and bottom reference voltages for each scanner */
+ switch(RTS_Debug->dev_model)
+ {
+ case HP3800:
+ case HPG2710:
+ hp3800_refvoltages(RTS_Debug->usbtype, sensortype, vrts, vrms, vrbs);
+ break;
+ default:
+ /* at this momment all analyzed scanners have the same values */
+ hp3970_refvoltages(RTS_Debug->usbtype, sensortype, vrts, vrms, vrbs);
+ break;
+ }
+}
+
+/** SEC: Calibration Offset ---------- */
+
+static void hp3800_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width)
+{
+ /* this function provides left coordinate and width to calculate offset
+ Sensor = Toshiba T2905
+ */
+
+ struct st_ofst
+ {
+ SANE_Int left;
+ SANE_Int width;
+ };
+
+ struct st_reg
+ {
+ SANE_Int resolution;
+ struct st_ofst values[3];
+ };
+
+ struct st_reg myreg[] =
+ {
+ /*res , {ref(L,W), tma(L,W), neg(L,W)} */
+ { 2400, {{15, 20}, {15, 20}, {15, 20}}},
+ { 1200, {{10, 10}, {10, 10}, {10, 10}}},
+ { 600, {{ 2, 10}, { 5, 10}, { 5, 10}}},
+ { 300, {{ 1, 5}, { 1, 5}, { 1, 5}}},
+ { 150, {{ 0, 3}, { 0, 3}, { 0, 3}}}
+ };
+
+ if ((left != NULL)&&(width != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (myreg[a].resolution == resolution)
+ {
+ scantype--;
+
+ *left = myreg[a].values[scantype].left;
+ *width = myreg[a].values[scantype].width;
+
+ break;
+ }
+ }
+ }
+}
+
+static void hp3970_offset(SANE_Int sensor, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width)
+{
+ /* this function provides left coordinate and width to calculate offset */
+
+ struct st_ofst
+ {
+ SANE_Int left;
+ SANE_Int width;
+ };
+
+ struct st_reg
+ {
+ SANE_Int sensor;
+ SANE_Int resolution;
+ struct st_ofst values[3];
+ };
+
+ struct st_reg myreg[] =
+ {
+ /* sensor , res , {ref(L,W), tma(L,W), neg(L,W)} */
+ {CCD_SENSOR, 2400, {{16, 20}, {16, 20}, {16, 20}}},
+ {CCD_SENSOR, 1200, {{16, 10}, {16, 10}, {16, 10}}},
+ {CCD_SENSOR, 600, {{15, 10}, {15, 10}, {15, 10}}},
+ {CCD_SENSOR, 300, {{ 7, 5}, { 7, 5}, { 7, 5}}},
+ {CCD_SENSOR, 200, {{ 7, 3}, { 7, 3}, { 7, 3}}},
+ {CCD_SENSOR, 100, {{ 3, 3}, { 3, 3}, { 3, 3}}},
+
+ /* sensor , res , {ref(L,W), tma(L,W), neg(L,W)} */
+ {CIS_SENSOR, 2400, {{84, 20}, {84, 20}, {84, 20}}},
+ {CIS_SENSOR, 1200, {{54, 10}, {54, 10}, {54, 10}}},
+ {CIS_SENSOR, 600, {{28, 10}, {28, 10}, {28, 10}}},
+ {CIS_SENSOR, 300, {{15, 5}, {15, 5}, {15, 5}}},
+ {CIS_SENSOR, 200, {{ 5, 3}, { 5, 3}, { 5, 3}}},
+ {CIS_SENSOR, 100, {{ 2, 3}, { 2, 3}, { 2, 3}}}
+ };
+
+ if ((left != NULL)&&(width != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if ((myreg[a].sensor == sensor)&&(myreg[a].resolution == resolution))
+ {
+ scantype--;
+
+ *left = myreg[a].values[scantype].left;
+ *width = myreg[a].values[scantype].width;
+
+ break;
+ }
+ }
+ }
+}
+
+static void hp4370_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width)
+{
+ /* this function provides left coordinate and width to calculate offset */
+
+ struct st_ofst
+ {
+ SANE_Int left;
+ SANE_Int width;
+ };
+
+ struct st_reg
+ {
+ SANE_Int resolution;
+ struct st_ofst values[3];
+ };
+
+ struct st_reg myreg[] =
+ {
+ /* res, {ref(L,W), tma(L,W), neg(L,W)} */
+ { 4800, {{42, 20}, {42, 20}, {52, 26}}},
+ { 2400, {{14, 20}, {14, 20}, {14, 26}}},
+ { 1200, {{ 8, 14}, { 8, 14}, { 8, 14}}},
+ { 600, {{ 4, 8}, { 4, 8}, { 4, 8}}},
+ { 300, {{ 2, 4}, { 2, 4}, { 2, 4}}},
+ { 150, {{ 1, 2}, { 1, 2}, { 1, 2}}}
+ };
+
+ if ((left != NULL)&&(width != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (myreg[a].resolution == resolution)
+ {
+ scantype--;
+
+ *left = myreg[a].values[scantype].left;
+ *width = myreg[a].values[scantype].width;
+
+ break;
+ }
+ }
+ }
+}
+
+static void ua4900_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width)
+{
+ /* this function provides left coordinate and width to calculate offset */
+
+ struct st_ofst
+ {
+ SANE_Int left;
+ SANE_Int width;
+ };
+
+ struct st_reg
+ {
+ SANE_Int resolution;
+ struct st_ofst values[3];
+ };
+
+ struct st_reg myreg[] =
+ {
+ /* res , {ref(L,W), tma(L,W), neg(L,W)} */
+ { 2400, {{20, 20}, {16, 20}, {16, 20}}},
+ { 1200, {{20, 10}, {10, 10}, {10, 10}}},
+ { 600, {{ 7, 10}, {15, 10}, {15, 10}}},
+ { 300, {{ 5, 10}, { 7, 8}, { 7, 8}}},
+ { 200, {{ 2, 10}, { 7, 6}, { 7, 6}}},
+ { 100, {{ 0, 10}, { 3, 4}, { 3, 4}}}
+ };
+
+ if ((left != NULL)&&(width != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (myreg[a].resolution == resolution)
+ {
+ scantype--;
+
+ *left = myreg[a].values[scantype].left;
+ *width = myreg[a].values[scantype].width;
+
+ break;
+ }
+ }
+ }
+}
+
+static void cfg_offset_get(SANE_Int sensortype, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width)
+{
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900:
+ ua4900_offset(resolution, scantype, left, width);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ hp3800_offset(resolution, scantype, left, width);
+ break;
+
+ case HPG3010:
+ case HPG3110:
+ case HP4370:
+ hp4370_offset(resolution, scantype, left, width);
+ break;
+
+ default:
+ hp3970_offset(sensortype, resolution, scantype, left, width);
+ break;
+ }
+}
+
+/** SEC: Device constrains ---------- */
+
+static SANE_Int cfg_constrains_get(struct st_constrains *constrain)
+{
+ SANE_Int rst = ERROR;
+
+ struct st_reg
+ {
+ SANE_Int device;
+ struct st_constrains constrain;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* constrains are set in milimeters */
+ /*device , reflective , negative , transparent */
+ /* , {{left, width, top, height}, {left, width, top, height}, {left, width, top, height}}}, */
+ { BQ5550 , {{ 0, 220, 0, 300}, { 88, 42, 0, 83}, { 88, 42, 0, 83}}},
+ { HP3800 , {{ 0, 220, 0, 300}, { 89, 45, 0, 85}, { 89, 45, 0, 100}}},
+ {HPG2710 , {{ 0, 220, 0, 300}, { 89, 45, 0, 85}, { 89, 45, 0, 100}}},
+ { HP3970 , {{ 0, 220, 0, 300}, { 88, 42, 0, 83}, { 88, 42, 0, 83}}},
+ { HP4070 , {{ 0, 220, 0, 300}, { 58, 99, 0, 197}, { 58, 99, 0, 197}}},
+ { HP4370 , {{ 0, 220, 0, 300}, { 90, 45, 0, 85}, { 90, 45, 0, 100}}},
+ { UA4900 , {{ 0, 220, 0, 300}, { 88, 42, 0, 83}, { 88, 42, 0, 83}}},
+ {HPG3010 , {{ 0, 220, 0, 300}, { 90, 45, 0, 85}, { 89, 45, 0, 100}}},
+ {HPG3110 , {{ 0, 220, 0, 300}, { 92, 28, 0, 190}, { 85, 44, 0, 190}}}
+ };
+
+ if (constrain != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].device == RTS_Debug->dev_model)
+ {
+ memcpy(constrain, &reg[a].constrain, sizeof(struct st_constrains));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Motor resource ------------------- */
+static SANE_Byte *cfg_motor_resource_get(SANE_Byte *size)
+{
+ /* this function returns the proper motor resources for a given device */
+ SANE_Byte *rst = NULL;
+
+ /* Until now, resource size is always 32 bytes */
+ rst = (SANE_Byte *)malloc(sizeof(SANE_Byte) * 32);
+ if (size != NULL)
+ *size = 0;
+
+ if (rst != NULL)
+ {
+ bzero(rst, sizeof(SANE_Byte) * 32);
+
+ switch(RTS_Debug->dev_model)
+ {
+ case BQ5550:
+ {
+ SANE_Byte Resource[] = {0xff, 0xb4, 0xb0, 0xd4, 0xd0, 0x70, 0x50, 0x54, 0x30, 0x34, 0x14, 0x38, 0x18, 0x0c, 0x08, 0x28, 0x04, 0x24, 0x20, 0x44, 0x40, 0xe0, 0xc0, 0xc4, 0xa0, 0xa4, 0x84, 0xa8, 0x88, 0x9c, 0x98, 0xb8};
+ memcpy(rst, &Resource, sizeof(SANE_Byte) * 32);
+ if (size != NULL)
+ *size = 32;
+ }
+ break;
+ default:
+ {
+ SANE_Byte Resource[] = {0xff, 0x90, 0xb0, 0xd4, 0xd0, 0x70, 0x50, 0x54, 0x30, 0x10, 0x14, 0x38, 0x18, 0x0c, 0x08, 0x28, 0x04, 0x00, 0x20, 0x44, 0x40, 0xe0, 0xc0, 0xc4, 0xa0, 0x80, 0x84, 0xa8, 0x88, 0x9c, 0x98, 0xb8};
+ memcpy(rst, &Resource, sizeof(SANE_Byte) * 32);
+ if (size != NULL)
+ *size = 32;
+ }
+ break;
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Auto reference position ---------- */
+
+static void cfg_autoref_get(struct st_autoref *reg)
+{
+ if (reg != NULL)
+ {
+ struct st_reg
+ {
+ SANE_Int device;
+ struct st_autoref value;
+ };
+
+ struct st_reg myreg[] =
+ {
+ /* x and y offsets are based on 2400 dpi */
+ /* device, { type , x , y , resolution, extern_boundary}*/
+ { BQ5550 , {REF_NONE , -40, -40, 600 , 40}},
+ { UA4900 , {REF_NONE , -40, -40, 600 , 40}},
+ { HP3800 , {REF_TAKEFROMSCANNER, 88, 624, 600 , 40}},
+ {HPG2710 , {REF_TAKEFROMSCANNER, 88, 624, 600 , 40}},
+ { HP3970 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}},
+ { HP4070 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}},
+ { HP4370 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}},
+ {HPG3010 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}},
+ {HPG3110 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(myreg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (myreg[a].device == RTS_Debug->dev_model)
+ {
+ memcpy(reg, &myreg[a].value, sizeof(struct st_autoref));
+ break;
+ }
+ }
+ }
+}
+
+static SANE_Int hp3800_effectivepixel(SANE_Int resolution)
+{
+ struct st_reg
+ {
+ SANE_Int resolution;
+ SANE_Int pixel;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* res , pixel */
+ { 2400, 134 },
+ { 1200, 134 },
+ { 600, 230 },
+ { 300, 172 },
+ { 200, 230 },
+ { 100, 230 }
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+ SANE_Int rst = 230; /* default */
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].resolution == resolution)
+ {
+ rst = reg[a].pixel;
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3970_effectivepixel(SANE_Int sensor, SANE_Int resolution)
+{
+ struct st_reg
+ {
+ SANE_Int resolution;
+ SANE_Int pixel[2];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* res , {Toshiba, sony}} */
+ { 2400, {134 , 218 }},
+ { 1200, {134 , 240 }},
+ { 600, {230 , 242 }},
+ { 300, {160 , 172 }},
+ { 200, {230 , 242 }},
+ { 100, {230 , 242 }}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+ SANE_Int rst = 230; /* default */
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].resolution == resolution)
+ {
+ rst = (sensor == CCD_SENSOR)? reg[a].pixel[0] : reg[a].pixel[1];
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp4370_effectivepixel(SANE_Int resolution)
+{
+ struct st_reg
+ {
+ SANE_Int resolution;
+ SANE_Int pixel;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* res , pxl */
+ { 4800, 134},
+ { 2400, 134},
+ { 1200, 134},
+ { 600, 230},
+ { 300, 230},
+ { 150, 230}
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+ SANE_Int rst = 230; /* default */
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].resolution == resolution)
+ {
+ rst = reg[a].pixel;
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int ua4900_effectivepixel(SANE_Int resolution)
+{
+ struct st_reg
+ {
+ SANE_Int resolution;
+ SANE_Int pixel;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* res , pixel */
+ { 2400, 134 },
+ { 1200, 134 },
+ { 600, 230 },
+ { 300, 172 },
+ { 200, 230 },
+ { 100, 230 }
+ };
+
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+ SANE_Int rst = 230; /* default */
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].resolution == resolution)
+ {
+ rst = reg[a].pixel;
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_effectivepixel_get(SANE_Int sensortype, SANE_Int resolution)
+{
+ SANE_Int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900:
+ rst = ua4900_effectivepixel(resolution);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_effectivepixel(resolution);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_effectivepixel(resolution);
+ break;
+
+ default:
+ rst = hp3970_effectivepixel(sensortype, resolution);
+ break;
+ }
+
+ return rst;
+}
+
+/** SEC: Gain and offset values ---------- */
+
+static SANE_Int bq5550_gainoffset(SANE_Int usb, struct st_gain_offset *myreg)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ struct st_gain_offset values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */
+ { USB20, {{264, 264, 264}, {0, 0, 0}, {262, 262, 262}, {0, 0, 0}, {3, 3, 3}, {27, 27, 27}, {4, 4, 4}}},
+ { USB11, {{264, 264, 264}, {0, 0, 0}, {262, 262, 262}, {0, 0, 0}, {3, 3, 3}, {27, 27, 27}, {4, 4, 4}}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (myreg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ memcpy(myreg, &reg[a].values, sizeof(struct st_gain_offset));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3800_gainoffset(SANE_Int usb, struct st_gain_offset *myreg)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ struct st_gain_offset values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */
+ { USB20, {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {2, 2, 2}, {4, 4, 4}, {4, 4, 4}}},
+ { USB11, {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {2, 2, 2}, {4, 4, 4}, {4, 4, 4}}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (myreg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ memcpy(myreg, &reg[a].values, sizeof(struct st_gain_offset));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3970_gainoffset(SANE_Int usb, SANE_Int sensor, struct st_gain_offset *myreg)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ struct st_gain_offset values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , sensor , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */
+ { USB20, CCD_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}},
+ { USB11, CCD_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}},
+
+ { USB20, CIS_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}},
+ { USB11, CIS_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (myreg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; count < 4; a++)
+ {
+ if ((reg[a].usb == usb)&&(reg[a].sensor == sensor))
+ {
+ memcpy(myreg, &reg[a].values, sizeof(struct st_gain_offset));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp4370_gainoffset(SANE_Int usb, struct st_gain_offset *myreg)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ struct st_gain_offset values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }} */
+ { USB20, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}},
+ { USB11, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (myreg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ memcpy(myreg, &reg[a].values, sizeof(struct st_gain_offset));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int ua4900_gainoffset(SANE_Int usb, struct st_gain_offset *myreg)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ struct st_gain_offset values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */
+ { USB20, {{321, 321, 321}, {0, 0, 0}, {321, 321, 321}, {0, 0, 0}, {0, 0, 0}, {24, 21, 19}, {8, 8, 8}}},
+ { USB11, {{321, 321, 321}, {0, 0, 0}, {321, 321, 321}, {0, 0, 0}, {0, 0, 0}, {16, 16, 16}, {4, 4, 4}}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (myreg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ memcpy(myreg, &reg[a].values, sizeof(struct st_gain_offset));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_gainoffset_get(SANE_Int sensortype, struct st_gain_offset *reg)
+{
+ SANE_Int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case BQ5550:
+ rst = bq5550_gainoffset(RTS_Debug->usbtype, reg);
+ break;
+
+ case UA4900:
+ rst = ua4900_gainoffset(RTS_Debug->usbtype, reg);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_gainoffset(RTS_Debug->usbtype, reg);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_gainoffset(RTS_Debug->usbtype, reg);
+ break;
+
+ default:
+ rst = hp3970_gainoffset(RTS_Debug->usbtype, sensortype, reg);
+ break;
+ }
+
+ return rst;
+}
+
+/** SEC: Pulse-width modulation check stable ---------- */
+
+static SANE_Int hp3800_checkstable(SANE_Int lamp, struct st_checkstable *check)
+{
+ struct st_reg
+ {
+ SANE_Int lamp;
+ struct st_checkstable values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* lamp , { diff, interval, tottime } */
+ { 0 , { 100., 200, 10000}},
+ { FLB_LAMP, { 100., 200, 10000}},
+ { TMA_LAMP, { 100., 200, 10000}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].lamp == lamp)
+ {
+ memcpy(check, &reg[a].values, sizeof(struct st_checkstable));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3970_checkstable(SANE_Int lamp, struct st_checkstable *check)
+{
+ struct st_reg
+ {
+ SANE_Int lamp;
+ struct st_checkstable values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* lamp , { diff, interval, tottime } */
+ { 0 , {1000., 200, 5000}},
+ { FLB_LAMP, { 500., 200, 5000}},
+ { TMA_LAMP, { 500., 200, 5000}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].lamp == lamp)
+ {
+ memcpy(check, &reg[a].values, sizeof(struct st_checkstable));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp4370_checkstable(SANE_Int lamp, struct st_checkstable *check)
+{
+ struct st_reg
+ {
+ SANE_Int lamp;
+ struct st_checkstable values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* lamp , { diff, interval, tottime } */
+ { 0 , { 100., 200, 5000}},
+ { FLB_LAMP, { 300., 200, 5000}},
+ { TMA_LAMP, { 0., 200, 25000}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].lamp == lamp)
+ {
+ memcpy(check, &reg[a].values, sizeof(struct st_checkstable));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int ua4900_checkstable(SANE_Int lamp, struct st_checkstable *check)
+{
+ struct st_reg
+ {
+ SANE_Int lamp;
+ struct st_checkstable values;
+ };
+
+ struct st_reg reg[] =
+ {
+ /* lamp , { diff, interval, tottime } */
+ { 0 , { 100., 200, 5000}},
+ { FLB_LAMP, { 10., 200, 5000}},
+ { TMA_LAMP, { 10., 200, 25000}}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (reg != NULL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].lamp == lamp)
+ {
+ memcpy(check, &reg[a].values, sizeof(struct st_checkstable));
+ rst = OK;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_checkstable_get(SANE_Int lamp, struct st_checkstable *check)
+{
+ SANE_Int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900:
+ rst = ua4900_checkstable(lamp, check);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_checkstable(lamp, check);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_checkstable(lamp, check);
+ break;
+
+ default:
+ rst = hp3970_checkstable(lamp, check);
+ break;
+ }
+
+ return rst;
+}
+
+/** SEC: Fixed pulse-width modulation values ---------- */
+
+static SANE_Int hp3800_fixedpwm(SANE_Int scantype, SANE_Int usb)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int pwm[3];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , { ST_NORMAL, ST_TA, ST_NEG} */
+ { USB20, { 0, 0, 0}},
+ { USB11, { 0, 0, 0}}
+ };
+
+ SANE_Int a, rst = 0x16;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ if ((scantype < ST_NORMAL)||(scantype > ST_NEG))
+ scantype = ST_NORMAL;
+
+ rst = reg[a].pwm[scantype - 1];
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3970_fixedpwm(SANE_Int scantype, SANE_Int usb, SANE_Int sensor)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ SANE_Int pwm[3];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , sensor , { ST_NORMAL, ST_TA, ST_NEG} */
+ { USB20, CCD_SENSOR, { 22, 22, 22}},
+ { USB11, CCD_SENSOR, { 22, 22, 22}},
+
+ { USB20, CIS_SENSOR, { 22, 22, 22}},
+ { USB11, CIS_SENSOR, { 22, 22, 22}}
+ };
+
+ SANE_Int a, rst = 0x16;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if ((reg[a].usb == usb)&&(reg[a].sensor == sensor))
+ {
+ if ((scantype < ST_NORMAL)||(scantype > ST_NEG))
+ scantype = ST_NORMAL;
+
+ rst = reg[a].pwm[scantype - 1];
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp4370_fixedpwm(SANE_Int scantype, SANE_Int usb)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int pwm[3];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , { ST_NORMAL, ST_TA, ST_NEG} */
+ { USB20, { 20, 28, 28}},
+ { USB11, { 20, 28, 28}}
+ };
+
+ SANE_Int a, rst = 0x16;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ if ((scantype < ST_NORMAL)||(scantype > ST_NEG))
+ scantype = ST_NORMAL;
+
+ rst = reg[a].pwm[scantype - 1];
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int ua4900_fixedpwm(SANE_Int scantype, SANE_Int usb)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int pwm[3];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* usb , { ST_NORMAL, ST_TA, ST_NEG} */
+ { USB20, { 20, 28, 28}},
+ { USB11, { 20, 28, 28}}
+ };
+
+ SANE_Int a, rst = 0x16;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].usb == usb)
+ {
+ if ((scantype < ST_NORMAL)||(scantype > ST_NEG))
+ scantype = ST_NORMAL;
+
+ rst = reg[a].pwm[scantype - 1];
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_fixedpwm_get(SANE_Int sensortype, SANE_Int scantype)
+{
+ SANE_Int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900:
+ rst = ua4900_fixedpwm(scantype, RTS_Debug->usbtype);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_fixedpwm(scantype, RTS_Debug->usbtype);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_fixedpwm(scantype, RTS_Debug->usbtype);
+ break;
+
+ default:
+ rst = hp3970_fixedpwm(scantype, RTS_Debug->usbtype, sensortype);
+ break;
+ }
+
+ return rst;
+}
+
+/** SEC: Fixed reference positions ---------- */
+
+static void cfg_vrefs_get(SANE_Int sensortype, SANE_Int res, SANE_Int *ser, SANE_Int *ler)
+{
+ switch(RTS_Debug->dev_model)
+ {
+ case HP3800:
+ case HPG2710:
+ hp3800_vrefs(res, ser, ler);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ hp4370_vrefs(res, ser, ler);
+ break;
+
+ default:
+ hp3970_vrefs(RTS_Debug->usbtype, sensortype, res, ser, ler);
+ break;
+ }
+}
+
+static void hp3800_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler)
+{
+ struct st_reg
+ {
+ SANE_Int resolution;
+ SANE_Int vref[2];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* res, { ser, ler} */
+ { 150, { 25, 50}},
+ { 300, { 50, 101}},
+ { 600, { 102, 202}},
+ { 1200, { 204, 404}},
+ { 2400, { 408, 808}}
+ };
+
+ if ((ser != NULL)&&(ler != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ /* values by default */
+ *ser = *ler = 0;
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].resolution == res)
+ {
+ *ser = reg[a].vref[0];
+ *ler = reg[a].vref[1];
+ break;
+ }
+ }
+ }
+}
+
+static void hp3970_vrefs(SANE_Int usb, SANE_Int sensor, SANE_Int res, SANE_Int *ser, SANE_Int *ler)
+{
+ struct st_reg
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ SANE_Int resolution;
+ SANE_Int vref[2];
+ };
+
+ /* I think these references should be the same in all usb versions and in
+ all sensor types but windows driver has some different values in some cases.
+ */
+
+ struct st_reg reg[] =
+ {
+ /* usb , sensor , res, { ser, ler} */
+ { USB20, CCD_SENSOR, 100, { 28, 60}},
+ { USB20, CCD_SENSOR, 200, { 37, 117}},
+ { USB20, CCD_SENSOR, 300, { 52, 162}},
+ { USB20, CCD_SENSOR, 600, { 103, 344}},
+ { USB20, CCD_SENSOR, 1200, { 156, 660}},
+ { USB20, CCD_SENSOR, 2400, { 309, 1262}},
+
+ /* usb , sensor , res, { ser, ler} */
+ { USB11, CCD_SENSOR, 100, { 28, 60}},
+ { USB11, CCD_SENSOR, 200, { 37, 117}},
+ { USB11, CCD_SENSOR, 300, { 52, 162}},
+ { USB11, CCD_SENSOR, 600, { 103, 344}},
+ { USB11, CCD_SENSOR, 1200, { 156, 660}},
+ { USB11, CCD_SENSOR, 2400, { 309, 1262}},
+
+ /* usb , sensor , res, { ser, ler} */
+ { USB20, CIS_SENSOR, 100, { 15, 60}},
+ { USB20, CIS_SENSOR, 200, { 39, 117}},
+ { USB20, CIS_SENSOR, 300, { 56, 161}},
+ { USB20, CIS_SENSOR, 600, { 108, 342}},
+ { USB20, CIS_SENSOR, 1200, { 221, 642}},
+ { USB20, CIS_SENSOR, 2400, { 407, 1285}},
+
+ /* usb , sensor , res, { ser, ler} */
+ { USB11, CIS_SENSOR, 100, { 15, 60}},
+ { USB11, CIS_SENSOR, 200, { 39, 117}},
+ { USB11, CIS_SENSOR, 300, { 56, 161}},
+ { USB11, CIS_SENSOR, 600, { 108, 342}},
+ { USB11, CIS_SENSOR, 1200, { 221, 642}},
+ { USB11, CIS_SENSOR, 2400, { 407, 1285}}
+ };
+
+ if ((ser != NULL)&&(ler != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ /* values by default */
+ *ser = *ler = 0;
+
+ for (a = 0; a < count; a++)
+ {
+ if ((reg[a].usb == usb)&&(reg[a].sensor == sensor)&&(reg[a].resolution == res))
+ {
+ *ser = reg[a].vref[0];
+ *ler = reg[a].vref[1];
+ break;
+ }
+ }
+ }
+}
+
+static void hp4370_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler)
+{
+ struct st_reg
+ {
+ SANE_Int resolution;
+ SANE_Int vref[2];
+ };
+
+ struct st_reg reg[] =
+ {
+ /* res, { ser, ler} */
+ { 150, { 31, 81}},
+ { 300, { 61, 162}},
+ { 600, { 122, 324}},
+ { 1200, { 244, 648}},
+ { 2400, { 488, 1256}},
+ { 4800, { 976, 2512}}
+ };
+
+ if ((ser != NULL)&&(ler != NULL))
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(reg) / sizeof(struct st_reg);
+
+ /* values by default */
+ *ser = *ler = 0;
+
+ for (a = 0; a < count; a++)
+ {
+ if (reg[a].resolution == res)
+ {
+ *ser = reg[a].vref[0];
+ *ler = reg[a].vref[1];
+ break;
+ }
+ }
+ }
+}
+
+/** SEC: Motor movements ---------- */
+
+static SANE_Int cfg_motormove_get(SANE_Int sensortype, SANE_Int item, struct st_motormove *reg)
+{
+ SANE_Int rst = ERROR;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case BQ5550:
+ rst = bq5550_motormove(item, reg);
+ break;
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_motormove(item, reg);
+ break;
+
+ default:
+ rst = hp3970_motormove(RTS_Debug->usbtype, sensortype, item, reg);
+ break;
+ }
+
+ return rst;
+}
+
+static SANE_Int bq5550_motormove(SANE_Int item, struct st_motormove *reg)
+{
+ SANE_Int rst = ERROR;
+
+ /* data is the same in all usb types and sensors so those args aren't needed */
+
+ if (reg != NULL)
+ {
+ struct st_motormove mv[] =
+ {
+ /* systemclock, ctpc, steptype , motorcurve } */
+ { 0x05 , 4059, STT_HALF , 0 },
+ { 0x02 , 1200, STT_QUART, -1 }
+ };
+
+ rst = OK;
+
+ if ((item < 2)&&(item > -1))
+ memcpy(reg, &mv[item], sizeof(struct st_motormove));
+ else rst = ERROR;
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3800_motormove(SANE_Int item, struct st_motormove *reg)
+{
+ SANE_Int rst = ERROR;
+
+ /* data is the same in all usb types and sensors so those args aren't needed */
+
+ if (reg != NULL)
+ {
+ struct st_motormove mv[] =
+ {
+ /* systemclock, ctpc, steptype, motorcurve } */
+ { 0x04 , 1991, STT_HALF, 2 },
+ { 0x02 , 1991, STT_HALF, -1 }
+ };
+
+ rst = OK;
+
+ if ((item < 2)&&(item > -1))
+ memcpy(reg, &mv[item], sizeof(struct st_motormove));
+ else rst = ERROR;
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3970_motormove(SANE_Int usb, SANE_Int ccd, SANE_Int item, struct st_motormove *reg)
+{
+ SANE_Int rst = ERROR;
+
+ struct st_mtmove
+ {
+ SANE_Int usbtype;
+ SANE_Int sensor;
+ struct st_motormove move;
+ };
+
+ if (reg != NULL)
+ {
+ struct st_mtmove mv[] =
+ {
+ /* usb, sensor , {systemclock, ctpc, steptype, motorcurve } */
+ {USB20, CCD_SENSOR, {0x02 , 6431, STT_HALF, 1 }},
+ {USB20, CCD_SENSOR, {0x02 , 2000, STT_HALF, -1 }},
+
+ {USB20, CIS_SENSOR, {0x02 , 6431, STT_HALF, 1 }},
+ {USB20, CIS_SENSOR, {0x02 , 2000, STT_HALF, -1 }},
+
+ {USB11, CCD_SENSOR, {0x02 , 6431, STT_HALF, 1 }},
+ {USB11, CCD_SENSOR, {0x02 , 2000, STT_HALF, -1 }},
+
+ {USB11, CIS_SENSOR, {0x02 , 6431, STT_HALF, 1 }},
+ {USB11, CIS_SENSOR, {0x02 , 2000, STT_HALF, -1 }}
+ };
+
+ if (item < 2)
+ {
+ SANE_Int a, count = 0;
+ SANE_Int total = sizeof(mv) / sizeof(struct st_mtmove);
+
+ for (a = 0; a < total; a++)
+ {
+ if ((mv[a].usbtype == usb)&&(mv[a].sensor == ccd))
+ {
+ if (item == count)
+ {
+ memcpy(reg, &mv[a].move, sizeof(struct st_motormove));
+ rst = OK;
+ break;
+ } else count++;
+ }
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Scanning modes ---------- */
+
+static SANE_Int cfg_scanmode_get(SANE_Int sensortype, SANE_Int sm, struct st_scanmode *mymode)
+{
+ SANE_Int rst = ERROR;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case BQ5550:
+ rst = bq5550_scanmodes(RTS_Debug->usbtype, sm, mymode);
+ break;
+
+ case UA4900:
+ rst = ua4900_scanmodes(RTS_Debug->usbtype, sm, mymode);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_scanmodes(RTS_Debug->usbtype, sm, mymode);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_scanmodes(RTS_Debug->usbtype, sm, mymode);
+ break;
+
+ default: /* hp3970 hp4070 */
+ rst = hp3970_scanmodes(RTS_Debug->usbtype, sensortype, sm, mymode);
+ break;
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3970_scanmodes(SANE_Int usb, SANE_Int ccd, SANE_Int sm, struct st_scanmode *mymode)
+{
+ struct st_modes
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ struct st_scanmode mode;
+ };
+
+ struct st_modes reg[] =
+ {
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , 2 , PIXEL_RATE, 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , 2 , PIXEL_RATE, 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x03 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x04 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 21999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 10727, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 5591, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x04 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 6999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 4671, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 21999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 10727, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 5591, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x04 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 6999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 4671, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, CIS_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CIS_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (mymode != NULL)
+ {
+ SANE_Int a;
+ SANE_Int total = sizeof(reg) / sizeof(struct st_modes);
+ SANE_Int count = 0;
+ struct st_modes *md;
+
+ for (a = 0; a < total; a++)
+ {
+ md = &reg[a];
+ if ((md->usb == usb)&&(md->sensor == ccd))
+ {
+ if (count == sm)
+ {
+ memcpy(mymode, &md->mode, sizeof(struct st_scanmode));
+ rst = OK;
+ break;
+ }
+
+ count++;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp4370_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode)
+{
+ struct st_modes
+ {
+ SANE_Int usb;
+ struct st_scanmode mode;
+ };
+
+ struct st_modes reg[] =
+ {
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NORMAL, CM_COLOR , 4800, 0x00 , -1 , PIXEL_RATE, 0x05 , 47799, 256 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NORMAL, CM_GRAY , 4800, 0x05 , -1 , LINE_RATE , 0x05 , 47799, 256 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 600, 0x08 , 2 , LINE_RATE , 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 300, 0x09 , 3 , LINE_RATE , 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_TA , CM_COLOR , 4800, 0x0A , -1 , PIXEL_RATE, 0x05 , 53999, 256 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 8999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2959, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_TA , CM_GRAY , 4800, 0x0B , -1 , LINE_RATE , 0x05 , 53999, 256 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 600, 0x08 , 3 , LINE_RATE , 0x05 , 8999, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 300, 0x09 , 4 , LINE_RATE , 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x05 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NEG , CM_COLOR , 4800, 0x0C , -1 , PIXEL_RATE, 0x05 , 60599, 256 , STT_HALF, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599, 145799}, 0 , 1 , -1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 89999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 600, 0x03 , -1 , PIXEL_RATE, 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 300, 0x04 , -1 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 150, 0x04 , 3 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NEG , CM_GRAY , 4800, 0x0D , -1 , LINE_RATE , 0x05 , 60599, 256 , STT_FULL, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 ,145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599, 145799}, 0 , 1 , -1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 89999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 600, 0x08 , -1 , LINE_RATE , 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 300, 0x09 , -1 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 150, 0x09 , 3 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, {ST_NORMAL, CM_COLOR , 4800, 0x00 , -1 , PIXEL_RATE, 0x05 , 47799, 64 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, {ST_NORMAL, CM_GRAY , 4800, 0x05 , -1 , LINE_RATE , 0x05 , 47799, 64 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 600, 0x08 , 2 , LINE_RATE , 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 300, 0x09 , 3 , LINE_RATE , 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, {ST_TA , CM_COLOR , 4800, 0x0A , -1 , PIXEL_RATE, 0x05 , 53999, 64 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 8999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2959, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, {ST_TA , CM_GRAY , 4800, 0x0B , -1 , LINE_RATE , 0x05 , 53999, 64 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 600, 0x08 , 3 , LINE_RATE , 0x05 , 8999, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 300, 0x09 , 4 , LINE_RATE , 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x05 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, {ST_NEG , CM_COLOR , 4800, 0x0C , -1 , PIXEL_RATE, 0x05 , 60599, 64 , STT_HALF, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 ,145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599,145799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 89999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 600, 0x03 , -1 , PIXEL_RATE, 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 300, 0x04 , -1 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 150, 0x04 , 3 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, {ST_NEG , CM_GRAY , 4800, 0x0D , -1 , LINE_RATE , 0x05 , 60599, 64 , STT_FULL, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 ,145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599,145799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 59999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 600, 0x08 , -1 , LINE_RATE , 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 300, 0x09 , -1 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 150, 0x09 , 3 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (mymode != NULL)
+ {
+ SANE_Int a;
+ SANE_Int total = sizeof(reg) / sizeof(struct st_modes);
+ SANE_Int count = 0;
+ struct st_modes *md;
+
+ for (a = 0; a < total; a++)
+ {
+ md = &reg[a];
+ if (md->usb == usb)
+ {
+ if (count == sm)
+ {
+ memcpy(mymode, &md->mode, sizeof(struct st_scanmode));
+ rst = OK;
+ break;
+ }
+
+ count++;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int hp3800_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode)
+{
+ struct st_modes
+ {
+ SANE_Int usb;
+ struct st_scanmode mode;
+ };
+
+ struct st_modes reg[] =
+ {
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 300, 0x03 , 9 , PIXEL_RATE, 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 300, 0x08 , 9 , LINE_RATE , 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 600, 0x09 , -1 , PIXEL_RATE, 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NEG , CM_COLOR , 2400, 0x0A , -1 , PIXEL_RATE, 0x04 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 1200, 0x0B , -1 , PIXEL_RATE, 0x05 ,127999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 600, 0x0C , -1 , PIXEL_RATE, 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 300, 0x0D , -1 , PIXEL_RATE, 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 150, 0x0D , 10 , PIXEL_RATE, 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NEG , CM_GRAY , 2400, 0x0F , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 1200, 0x10 , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 600, 0x11 , -1 , LINE_RATE , 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 300, 0x12 , -1 , LINE_RATE , 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 150, 0x12 , 10 , LINE_RATE , 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 5 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 300, 0x03 , 9 , PIXEL_RATE, 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 300, 0x08 , 9 , LINE_RATE , 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NORMAL, CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 600, 0x09 , -1 , PIXEL_RATE, 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_TA , CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB11, {ST_NEG , CM_COLOR , 2400, 0x0A , -1 , PIXEL_RATE, 0x04 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 1200, 0x0B , -1 , PIXEL_RATE, 0x05 ,127999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 600, 0x0C , -1 , PIXEL_RATE, 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 300, 0x0D , -1 , PIXEL_RATE, 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_COLOR , 150, 0x0D , 10 , PIXEL_RATE, 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB11, {ST_NEG , CM_GRAY , 2400, 0x0F , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 1200, 0x10 , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 600, 0x11 , -1 , LINE_RATE , 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 300, 0x12 , -1 , LINE_RATE , 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB11, {ST_NEG , CM_GRAY , 150, 0x12 , 10 , LINE_RATE , 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (mymode != NULL)
+ {
+ SANE_Int a;
+ SANE_Int total = sizeof(reg) / sizeof(struct st_modes);
+ SANE_Int count = 0;
+ struct st_modes *md;
+
+ for (a = 0; a < total; a++)
+ {
+ md = &reg[a];
+ if (md->usb == usb)
+ {
+ if (count == sm)
+ {
+ memcpy(mymode, &md->mode, sizeof(struct st_scanmode));
+ rst = OK;
+ break;
+ }
+
+ count++;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int bq5550_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode)
+{
+ struct st_modes
+ {
+ SANE_Int usb;
+ struct st_scanmode mode;
+ };
+
+ struct st_modes reg[] =
+ {
+ /* usb, {scantype , colormode , res , timing, curve, samplerate, clock, ctpc , backstp, steptype , dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 12999, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x05 , 6999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 5 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 300, 0x02 , 0 , PIXEL_RATE, 0x05 , 5599, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 150, 0x02 , 0 , PIXEL_RATE, 0x05 , 6439, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 100, 0x02 , 0 , PIXEL_RATE, 0x05 , 5759, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x03 , -1 , LINE_RATE , 0x05 , 12999, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 600, 0x04 , -1 , LINE_RATE , 0x05 , 7199, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 300, 0x05 , 0 , LINE_RATE , 0x05 , 5599, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 150, 0x05 , 0 , LINE_RATE , 0x05 , 6239, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 100, 0x05 , 0 , LINE_RATE , 0x05 , 5759, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NORMAL, CM_LINEART, 1200, 0x03 , -1 , LINE_RATE , 0x05 , 12999, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_LINEART, 600, 0x04 , -1 , LINE_RATE , 0x05 , 7199, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_LINEART, 300, 0x05 , 0 , LINE_RATE , 0x05 , 5599, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_LINEART, 150, 0x05 , 0 , LINE_RATE , 0x05 , 6239, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_LINEART, 100, 0x05 , 0 , LINE_RATE , 0x05 , 5759, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode , res , timing, curve, samplerate, clock, ctpc , backstp, steptype , dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_TA , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 9899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x05 , 9999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 300, 0x02 , 0 , PIXEL_RATE, 0x05 , 4799, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 150, 0x02 , 0 , PIXEL_RATE, 0x05 , 4959, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 100, 0x02 , 0 , PIXEL_RATE, 0x05 , 5059, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_TA , CM_GRAY , 1200, 0x03 , -1 , LINE_RATE , 0x05 , 9899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 600, 0x04 , -1 , LINE_RATE , 0x05 , 9999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 300, 0x05 , 0 , LINE_RATE , 0x05 , 4799, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 150, 0x05 , 0 , LINE_RATE , 0x05 , 4959, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 100, 0x05 , 0 , LINE_RATE , 0x05 , 5059, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_TA , CM_LINEART, 1200, 0x03 , -1 , LINE_RATE , 0x05 , 9899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_LINEART, 600, 0x04 , -1 , LINE_RATE , 0x05 , 9999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_LINEART, 300, 0x05 , 0 , LINE_RATE , 0x05 , 4799, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_LINEART, 150, 0x05 , 0 , LINE_RATE , 0x05 , 4959, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_LINEART, 100, 0x05 , 0 , LINE_RATE , 0x05 , 5059, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode , res , timing, curve, samplerate, clock, ctpc , backstp, steptype , dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NEG , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 51899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x05 , 51799, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 300, 0x02 , 0 , PIXEL_RATE, 0x05 , 25899, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 150, 0x02 , 0 , PIXEL_RATE, 0x05 , 25899, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 100, 0x02 , 0 , PIXEL_RATE, 0x05 , 25499, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NEG , CM_GRAY , 1200, 0x03 , -1 , LINE_RATE , 0x05 , 51899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 600, 0x04 , -1 , LINE_RATE , 0x05 , 51799, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 300, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 150, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 100, 0x05 , 0 , LINE_RATE , 0x05 , 25499, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NEG , CM_LINEART, 1200, 0x03 , -1 , LINE_RATE , 0x05 , 51899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_LINEART, 600, 0x04 , -1 , LINE_RATE , 0x05 , 51799, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_LINEART, 300, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_LINEART, 150, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_LINEART, 100, 0x05 , 0 , LINE_RATE , 0x05 , 25499, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}
+ };
+
+ SANE_Int rst = ERROR;
+
+ /* silence compiler */
+ usb = usb;
+
+ if (mymode != NULL)
+ {
+ SANE_Int a;
+ SANE_Int total = sizeof(reg) / sizeof(struct st_modes);
+ SANE_Int count = 0;
+ struct st_modes *md;
+
+ for (a = 0; a < total; a++)
+ {
+ md = &reg[a];
+ if (count == sm)
+ {
+ memcpy(mymode, &md->mode, sizeof(struct st_scanmode));
+ rst = OK;
+ break;
+ }
+
+ count++;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int ua4900_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode)
+{
+ struct st_modes
+ {
+ SANE_Int usb;
+ struct st_scanmode mode;
+ };
+
+ struct st_modes reg[] =
+ {
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 600, 0x01 , 2 , PIXEL_RATE, 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 300, 0x02 , 3 , PIXEL_RATE, 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 200, 0x03 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x05 , -1 , LINE_RATE , 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 600, 0x06 , 2 , LINE_RATE , 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 300, 0x07 , 3 , LINE_RATE , 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 200, 0x08 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_TA , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 300, 0x02 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 200, 0x03 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_TA , CM_GRAY , 1200, 0x05 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 600, 0x06 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 300, 0x07 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 200, 0x08 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */
+ {USB20, {ST_NEG , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 300, 0x02 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 200, 0x03 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+
+ {USB20, {ST_NEG , CM_GRAY , 1200, 0x05 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 600, 0x06 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 300, 0x07 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 200, 0x08 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }},
+ {USB20, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}
+ };
+
+ SANE_Int rst = ERROR;
+
+ if (mymode != NULL)
+ {
+ SANE_Int a;
+ SANE_Int total = sizeof(reg) / sizeof(struct st_modes);
+ SANE_Int count = 0;
+ struct st_modes *md;
+
+ for (a = 0; a < total; a++)
+ {
+ md = &reg[a];
+ if (md->usb == usb)
+ {
+ if (count == sm)
+ {
+ memcpy(mymode, &md->mode, sizeof(struct st_scanmode));
+ rst = OK;
+ break;
+ }
+
+ count++;
+ }
+ }
+ }
+
+ return rst;
+}
+
+/** SEC: Calibration wreferences ---------- */
+
+static void hp3970_wrefs(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ struct st_wref
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ SANE_Int depth;
+ SANE_Int res;
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ struct st_wref wrefs[] =
+ {
+ /*usb , sensor , depth, res , {transparent }, {negative} */
+ {USB20, CCD_SENSOR, 8 , 2400, { 78, 78, 68}, {120, 136, 157}},
+ {USB20, CCD_SENSOR, 8 , 1200, { 78, 78, 68}, {120, 136, 157}},
+ {USB20, CCD_SENSOR, 8 , 600 , { 79, 80, 70}, {119, 136, 157}},
+ {USB20, CCD_SENSOR, 8 , 300 , { 79, 80, 70}, {119, 136, 157}},
+ {USB20, CCD_SENSOR, 8 , 200 , { 79, 80, 70}, {119, 136, 157}},
+ {USB20, CCD_SENSOR, 8 , 100 , { 79, 80, 70}, {119, 136, 157}},
+
+ {USB20, CCD_SENSOR, 16 , 2400, { 81, 81, 71}, {119, 137, 158}},
+ {USB20, CCD_SENSOR, 16 , 1200, { 81, 81, 71}, {119, 137, 158}},
+ {USB20, CCD_SENSOR, 16 , 600 , { 81, 82, 72}, {119, 137, 158}},
+ {USB20, CCD_SENSOR, 16 , 300 , { 81, 82, 72}, {119, 137, 158}},
+ {USB20, CCD_SENSOR, 16 , 200 , { 81, 82, 72}, {119, 137, 158}},
+ {USB20, CCD_SENSOR, 16 , 100 , { 81, 82, 72}, {119, 137, 158}},
+
+ /*usb , sensor , depth, res , {transparent }, {negative } */
+ {USB20, CIS_SENSOR, 8 , 2400, { 94, 85, 78}, { 94, 85, 78}},
+ {USB20, CIS_SENSOR, 8 , 1200, { 83, 82, 75}, {140, 155, 155}},
+ {USB20, CIS_SENSOR, 8 , 600 , { 84, 84, 76}, {145, 155, 165}},
+ {USB20, CIS_SENSOR, 8 , 300 , {146, 166, 166}, {146, 166, 166}},
+ {USB20, CIS_SENSOR, 8 , 200 , {145, 310, 160}, {145, 310, 160}},
+ {USB20, CIS_SENSOR, 8 , 100 , {140, 300, 155}, {140, 300, 155}},
+
+ {USB20, CIS_SENSOR, 16 , 2400, { 94, 85, 78}, { 94, 85, 78}},
+ {USB20, CIS_SENSOR, 16 , 1200, { 83, 82, 75}, {140, 155, 155}},
+ {USB20, CIS_SENSOR, 16 , 600 , { 84, 84, 76}, {145, 155, 165}},
+ {USB20, CIS_SENSOR, 16 , 300 , {146, 166, 166}, {146, 166, 166}},
+ {USB20, CIS_SENSOR, 16 , 200 , {145, 310, 160}, {145, 310, 160}},
+ {USB20, CIS_SENSOR, 16 , 100 , {140, 300, 155}, {140, 300, 155}},
+
+ /*usb , sensor , depth, res , {transparent }, {negative } */
+ {USB11, CCD_SENSOR, 8 , 2400, { 78, 78, 68}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 8 , 1200, { 78, 78, 68}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 8 , 600 , { 79, 79, 70}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 8 , 300 , { 79, 79, 70}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 8 , 200 , { 79, 79, 70}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 8 , 100 , { 79, 79, 70}, {120, 137, 158}},
+
+ {USB11, CCD_SENSOR, 16 , 2400, { 80, 80, 70}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 16 , 1200, { 80, 80, 70}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 16 , 600 , { 80, 81, 71}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 16 , 300 , { 80, 81, 71}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 16 , 200 , { 80, 81, 71}, {120, 137, 158}},
+ {USB11, CCD_SENSOR, 16 , 100 , { 80, 81, 71}, {120, 137, 158}},
+
+ /*usb , sensor , depth, res , {transparent }, {negative} */
+ {USB11, CIS_SENSOR, 8 , 2400, { 94, 85, 78}, {94 , 85 , 78}},
+ {USB11, CIS_SENSOR, 8 , 1200, { 83, 82, 75}, {140, 155, 155}},
+ {USB11, CIS_SENSOR, 8 , 600 , { 84, 84, 76}, {145, 155, 165}},
+ {USB11, CIS_SENSOR, 8 , 300 , {146, 166, 166}, {146, 166, 166}},
+ {USB11, CIS_SENSOR, 8 , 200 , {145, 310, 160}, {145, 310, 160}},
+ {USB11, CIS_SENSOR, 8 , 100 , {140, 300, 155}, {140, 300, 155}},
+
+ {USB11, CIS_SENSOR, 16 , 2400, { 94, 85, 78}, { 94, 85, 78}},
+ {USB11, CIS_SENSOR, 16 , 1200, { 83, 82, 75}, {140, 155, 155}},
+ {USB11, CIS_SENSOR, 16 , 600 , { 84, 84, 76}, {145, 155, 165}},
+ {USB11, CIS_SENSOR, 16 , 300 , {146, 166, 166}, {146, 166, 166}},
+ {USB11, CIS_SENSOR, 16 , 200 , {145, 310, 160}, {145, 310, 160}},
+ {USB11, CIS_SENSOR, 16 , 100 , {140, 300, 155}, {140, 300, 155}}
+ };
+
+ struct st_wref *rf;
+
+ *red = *green = *blue = 0x50;
+
+ if (res <= 100)
+ res = 100;
+ else if (res <= 200)
+ res = 200;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else if (res <= 1200)
+ res = 1200;
+ else res = 2400;
+
+ if (scantype != ST_NORMAL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref);
+
+ for (a = 0; a < count; a++)
+ {
+ rf = &wrefs[a];
+ if ((rf->usb == usb)&&(rf->sensor == ccd)&&(rf->depth == depth)&&(rf->res == res))
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = rf->negative[CL_RED];
+ *green = rf->negative[CL_GREEN];
+ *blue = rf->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = rf->transparent[CL_RED];
+ *green = rf->transparent[CL_GREEN];
+ *blue = rf->transparent[CL_BLUE];
+ break;
+ }
+
+ break;
+ }
+ }
+ } else
+ {
+ /* reflective mode */
+ *red = 233;
+ *green = 230;
+ *blue = 222;
+ }
+}
+
+static void ua4900_wrefs(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ struct st_wref
+ {
+ SANE_Int usb;
+ SANE_Int depth;
+ SANE_Int res;
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ struct st_wref wrefs[] =
+ {
+ /*usb , depth, res , {transparent }, {negative } */
+ {USB20, 8 , 1200, {136, 131, 121}, {215, 210, 260}},
+ {USB20, 8 , 600 , {133, 129, 118}, {219, 215, 264}},
+ {USB20, 8 , 300 , {133, 129, 119}, {218, 215, 261}},
+ {USB20, 8 , 200 , {133, 130, 119}, {218, 215, 261}},
+ {USB20, 8 , 100 , {132, 128, 118}, {218, 215, 261}},
+
+ {USB20, 16 , 1200, {264, 263, 266}, { 0, 0, 0}},
+ {USB20, 16 , 600 , {264, 265, 268}, { 0, 0, 0}},
+ {USB20, 16 , 300 , {264, 260, 263}, { 0, 0, 0}},
+ {USB20, 16 , 200 , {257, 255, 253}, { 0, 0, 0}},
+ {USB20, 16 , 100 , {258, 259, 256}, { 0, 0, 0}},
+
+ /*usb , depth, res , {transparent }, {negative } */
+ {USB11, 8 , 1200, {135, 130, 119}, {220, 215, 264}},
+ {USB11, 8 , 600 , {132, 128, 117}, {217, 213, 259}},
+ {USB11, 8 , 300 , {132, 128, 117}, {216, 211, 259}},
+ {USB11, 8 , 200 , {132, 128, 118}, {215, 352, 259}},
+ {USB11, 8 , 100 , {266, 264, 264}, {215, 352, 259}},
+
+ {USB11, 16 , 1200, {264, 263, 266}, { 0, 0, 0}},
+ {USB11, 16 , 600 , {257, 256, 253}, { 0, 0, 0}},
+ {USB11, 16 , 300 , {259, 252, 254}, { 0, 0, 0}},
+ {USB11, 16 , 200 , {257, 255, 253}, { 0, 0, 0}},
+ {USB11, 16 , 100 , {258, 259, 256}, { 0, 0, 0}}
+ };
+
+ struct st_wref *rf;
+
+ *red = *green = *blue = 0x50;
+
+ if (res <= 100)
+ res = 100;
+ else if (res <= 200)
+ res = 200;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else res = 1200;
+
+ if (scantype != ST_NORMAL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref);
+
+ for (a = 0; a < count; a++)
+ {
+ rf = &wrefs[a];
+ if ((rf->usb == usb)&&(rf->depth == depth)&&(rf->res == res))
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = rf->negative[CL_RED];
+ *green = rf->negative[CL_GREEN];
+ *blue = rf->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = rf->transparent[CL_RED];
+ *green = rf->transparent[CL_GREEN];
+ *blue = rf->transparent[CL_BLUE];
+ break;
+ }
+
+ break;
+ }
+ }
+ } else
+ {
+ /* reflective mode */
+ *red = 233;
+ *green = 230;
+ *blue = 222;
+ }
+}
+
+static void hp3800_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ /* in this scanner, values are the same in all usb versions and depths */
+ struct st_wref
+ {
+ SANE_Int res;
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ struct st_wref wrefs[] =
+ {
+ /* res , {transparent }, {negative } */
+ { 2400, {276, 279, 243}, { 98, 162, 229}},
+ { 1200, {276, 279, 243}, { 98, 162, 229}},
+ { 600, {276, 279, 243}, {100, 162, 229}},
+ { 300, {276, 279, 243}, {100, 162, 229}},
+ { 150, {276, 279, 243}, {100, 162, 229}},
+ };
+
+ struct st_wref *rf;
+
+ *red = *green = *blue = 0x50;
+
+ if (res <= 150)
+ res = 150;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else if (res <= 1200)
+ res = 1200;
+ else res = 2400;
+
+ if (scantype != ST_NORMAL)
+ {
+ SANE_Int a;
+ SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref);
+
+ for (a = 0; a < count; a++)
+ {
+ rf = &wrefs[a];
+ if (rf->res == res)
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = rf->negative[CL_RED];
+ *green = rf->negative[CL_GREEN];
+ *blue = rf->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = rf->transparent[CL_RED];
+ *green = rf->transparent[CL_GREEN];
+ *blue = rf->transparent[CL_BLUE];
+ break;
+ }
+
+ break;
+ }
+ }
+ } else
+ {
+ /* reflective mode */
+ *red = 248;
+ *green = 250;
+ *blue = 248;
+ }
+}
+
+static void hp4370_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ struct st_wref
+ {
+ SANE_Int res;
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ /* values are the same in all usb versions and depths */
+
+ struct st_wref wrefs[] =
+ {
+ /* res, {transparent }, {negative} */
+ { 4800, { 93, 93, 82}, {156, 308, 454}},
+ { 2400, { 93, 93, 82}, {156, 156, 153}},
+ { 1200, { 93, 93, 82}, {156, 156, 153}},
+ { 600, { 93, 93, 82}, {155, 155, 152}},
+ { 300, { 94, 94, 83}, {155, 155, 152}},
+ { 150, { 86, 87, 77}, {148, 145, 138}}
+ };
+
+ struct st_wref *rf;
+
+ SANE_Int a;
+
+ *red = *green = *blue = 0x50;
+
+ if (res <= 150)
+ res = 150;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else if (res <= 1200)
+ res = 1200;
+ else if (res <= 2400)
+ res = 2400;
+ else res = 4800;
+
+ if (scantype != ST_NORMAL)
+ {
+ SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref);
+
+ for (a = 0; a < count; a++)
+ {
+ rf = &wrefs[a];
+ if (rf->res == res)
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = rf->negative[CL_RED];
+ *green = rf->negative[CL_GREEN];
+ *blue = rf->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = rf->transparent[CL_RED];
+ *green = rf->transparent[CL_GREEN];
+ *blue = rf->transparent[CL_BLUE];
+ break;
+ }
+
+ break;
+ }
+ }
+ } else
+ {
+ /* reflective mode */
+ *red = 233;
+ *green = 232;
+ *blue = 223;
+ }
+}
+
+static void cfg_wrefs_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900:
+ ua4900_wrefs(RTS_Debug->usbtype, depth, res, scantype, red, green, blue);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ hp3800_wrefs(res, scantype, red, green, blue);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ hp4370_wrefs(res, scantype, red, green, blue);
+ break;
+
+ default:
+ /* hp3970 and hp4070 */
+ hp3970_wrefs(RTS_Debug->usbtype, sensortype, depth, res, scantype, red, green, blue);
+ break;
+ }
+}
+
+static void hp3800_shading_cut(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ /* values are the same in all usb versions and depths */
+ struct st_cut
+ {
+ SANE_Int res;
+ SANE_Int reflective[3];
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ struct st_cut cuts[] =
+ {
+ /* res, {reflective }, {transparent }, {negative } */
+ { 2400, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}},
+ { 1200, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}},
+ { 600, { 10, 9, 12}, {-75, -75, -75}, {440, -300, -250}},
+ { 300, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}},
+ { 150, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}}
+ };
+
+ struct st_cut *ct;
+
+ SANE_Int a;
+ SANE_Int count = sizeof(cuts) / sizeof(struct st_cut);
+
+ *red = *green = *blue = 0;
+
+ if (res <= 150)
+ res = 150;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else if (res <= 1200)
+ res = 1200;
+ else res = 2400;
+
+ for (a = 0; a < count; a++)
+ {
+ ct = &cuts[a];
+ if (ct->res == res)
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = ct->negative[CL_RED];
+ *green = ct->negative[CL_GREEN];
+ *blue = ct->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = ct->transparent[CL_RED];
+ *green = ct->transparent[CL_GREEN];
+ *blue = ct->transparent[CL_BLUE];
+ break;
+ default: /* reflective */
+ *red = ct->reflective[CL_RED];
+ *green = ct->reflective[CL_GREEN];
+ *blue = ct->reflective[CL_BLUE];
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void hp3970_shading_cut(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ struct st_cut
+ {
+ SANE_Int usb;
+ SANE_Int sensor;
+ SANE_Int depth;
+ SANE_Int res;
+ SANE_Int reflective[3];
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ struct st_cut cuts[] =
+ {
+ /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */
+ {USB20, CCD_SENSOR, 8 , 2400, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 3}},
+ {USB20, CCD_SENSOR, 8 , 1200, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 5}},
+ {USB20, CCD_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}},
+ {USB20, CCD_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}},
+ {USB20, CCD_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}},
+ {USB20, CCD_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}},
+
+ {USB20, CCD_SENSOR, 16 , 2400, {-15, -15, -15}, { -7, -7, -7}, {43, 0, 3}},
+ {USB20, CCD_SENSOR, 16 , 1200, {-15, -15, -15}, {-15, -15, -15}, {63, 0, 5}},
+ {USB20, CCD_SENSOR, 16 , 600 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}},
+ {USB20, CCD_SENSOR, 16 , 300 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}},
+ {USB20, CCD_SENSOR, 16 , 200 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}},
+ {USB20, CCD_SENSOR, 16 , 100 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}},
+
+ /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */
+ {USB20, CIS_SENSOR, 8 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 8 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+
+ {USB20, CIS_SENSOR, 16 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 16 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 16 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 16 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 16 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB20, CIS_SENSOR, 16 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+
+ /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */
+ {USB11, CCD_SENSOR, 8 , 2400, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 3}},
+ {USB11, CCD_SENSOR, 8 , 1200, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 5}},
+ {USB11, CCD_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}},
+ {USB11, CCD_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}},
+ {USB11, CCD_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}},
+ {USB11, CCD_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}},
+
+ {USB11, CCD_SENSOR, 16 , 2400, {-15, -15, -15}, { -7, -7, -7}, {43, 0, 3}},
+ {USB11, CCD_SENSOR, 16 , 1200, {-15, -15, -15}, {-15, -15, -15}, {63, 0, 5}},
+ {USB11, CCD_SENSOR, 16 , 600 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}},
+ {USB11, CCD_SENSOR, 16 , 300 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}},
+ {USB11, CCD_SENSOR, 16 , 200 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}},
+ {USB11, CCD_SENSOR, 16 , 100 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}},
+
+ /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */
+ {USB11, CIS_SENSOR, 8 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 8 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+
+ {USB11, CIS_SENSOR, 16 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 16 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 16 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 16 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 16 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}},
+ {USB11, CIS_SENSOR, 16 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}
+ };
+
+ struct st_cut *ct;
+
+ SANE_Int a;
+ SANE_Int count = sizeof(cuts) / sizeof(struct st_cut);
+
+ *red = *green = *blue = 0;
+
+ if (res <= 100)
+ res = 100;
+ else if (res <= 200)
+ res = 200;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else if (res <= 1200)
+ res = 1200;
+ else res = 2400;
+
+ for (a = 0; a < count; a++)
+ {
+ ct = &cuts[a];
+ if ((ct->usb == usb)&&(ct->sensor == ccd)&&(ct->depth == depth)&&(ct->res == res))
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = ct->negative[CL_RED];
+ *green = ct->negative[CL_GREEN];
+ *blue = ct->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = ct->transparent[CL_RED];
+ *green = ct->transparent[CL_GREEN];
+ *blue = ct->transparent[CL_BLUE];
+ break;
+ default: /* reflective */
+ *red = ct->reflective[CL_RED];
+ *green = ct->reflective[CL_GREEN];
+ *blue = ct->reflective[CL_BLUE];
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void hp4370_shading_cut(SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ struct st_cut
+ {
+ SANE_Int depth;
+ SANE_Int res;
+ SANE_Int reflective[3];
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ /* values are the same in all usb versions for each depth */
+ struct st_cut cuts[] =
+ {
+ /* depth, res , {reflective }, {transparent }, {negative } */
+ { 8 , 4800, { 0, 0, 0}, { -3, -3, -3}, {35, -10, -7}},
+ { 8 , 2400, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -7}},
+ { 8 , 1200, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -22}},
+ { 8 , 600 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}},
+ { 8 , 300 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}},
+ { 8 , 150 , {-15, -15, -15}, { 0, 0, 0}, {50, -23, -14}},
+
+ { 16 , 4800, { 0, 0, 0}, { -3, -3, -3}, {35, -10, -7}},
+ { 16 , 2400, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -7}},
+ { 16 , 1200, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -22}},
+ { 16 , 600 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}},
+ { 16 , 300 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}},
+ { 16 , 150 , {-15, -15, -15}, {-15, -15, -15}, {40, -15, -5}}
+ };
+
+ struct st_cut *ct;
+
+ SANE_Int a;
+ SANE_Int count = sizeof(cuts) / sizeof(struct st_cut);
+
+ *red = *green = *blue = 0;
+
+ if (res <= 150)
+ res = 150;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else if (res <= 1200)
+ res = 1200;
+ else if (res <= 2400)
+ res = 2400;
+ else res = 4800;
+
+ for (a = 0; a < count; a++)
+ {
+ ct = &cuts[a];
+ if ((ct->depth == depth)&&(ct->res == res))
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = ct->negative[CL_RED];
+ *green = ct->negative[CL_GREEN];
+ *blue = ct->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = ct->transparent[CL_RED];
+ *green = ct->transparent[CL_GREEN];
+ *blue = ct->transparent[CL_BLUE];
+ break;
+ default: /* reflective */
+ *red = ct->reflective[CL_RED];
+ *green = ct->reflective[CL_GREEN];
+ *blue = ct->reflective[CL_BLUE];
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void ua4900_shading_cut(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ struct st_cut
+ {
+ SANE_Int usb;
+ SANE_Int depth;
+ SANE_Int res;
+ SANE_Int reflective[3];
+ SANE_Int transparent[3];
+ SANE_Int negative[3];
+ };
+
+ struct st_cut cuts[] =
+ {
+ /*usb , depth, res , {reflective }, {transparent }, {negative } */
+ {USB20, 8 , 1200, {-15, -17, -13}, { 6, 0, 0}, {110, 15, 250}},
+ {USB20, 8 , 600 , {-16, -15, -15}, { 6, 7, 5}, {105, 11, 20}},
+ {USB20, 8 , 300 , {-15, -15, -15}, { 6, 7, 5}, {105, 10, 25}},
+ {USB20, 8 , 200 , {-15, -15, -15}, { 5, 5, 5}, {110, 15, 32}},
+ {USB20, 8 , 100 , {-15, -15, -15}, { 5, 5, 5}, {110, 15, 32}},
+
+ {USB20, 16 , 1200, {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB20, 16 , 600 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB20, 16 , 300 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB20, 16 , 200 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB20, 16 , 100 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+
+ /*usb , depth, res , {reflective }, {transparent }, {negative } */
+ {USB11, 8 , 1200, {-15, -17, -13}, { 5, 5, 5}, {105, 12, 23}},
+ {USB11, 8 , 600 , {-17, -15, -16}, { 8, 8, 8}, {105, 12, 20}},
+ {USB11, 8 , 300 , {-15, -15, -15}, { 8, 8, 8}, {105, 15, 25}},
+ {USB11, 8 , 200 , {-15, -15, -15}, { 8, 8, 8}, {105, 40, 25}},
+ {USB11, 8 , 100 , {-15, -15, -15}, { 8, 8, 8}, {-20, 50, 23}},
+
+ {USB11, 16 , 1200, {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB11, 16 , 600 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB11, 16 , 300 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB11, 16 , 200 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}},
+ {USB11, 16 , 100 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}
+ };
+
+ struct st_cut *ct;
+
+ SANE_Int a;
+ SANE_Int count = sizeof(cuts) / sizeof(struct st_cut);
+
+ *red = *green = *blue = 0;
+
+ if (res <= 100)
+ res = 100;
+ else if (res <= 200)
+ res = 200;
+ else if (res <= 300)
+ res = 300;
+ else if (res <= 600)
+ res = 600;
+ else res = 1200;
+
+ for (a = 0; a < count; a++)
+ {
+ ct = &cuts[a];
+ if ((ct->usb == usb)&&(ct->depth == depth)&&(ct->res == res))
+ {
+ switch(scantype)
+ {
+ case ST_NEG:
+ *red = ct->negative[CL_RED];
+ *green = ct->negative[CL_GREEN];
+ *blue = ct->negative[CL_BLUE];
+ break;
+ case ST_TA:
+ *red = ct->transparent[CL_RED];
+ *green = ct->transparent[CL_GREEN];
+ *blue = ct->transparent[CL_BLUE];
+ break;
+ default: /* reflective */
+ *red = ct->reflective[CL_RED];
+ *green = ct->reflective[CL_GREEN];
+ *blue = ct->reflective[CL_BLUE];
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void cfg_shading_cut_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue)
+{
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900:
+ ua4900_shading_cut(RTS_Debug->usbtype, depth, res, scantype, red, green, blue);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ hp3800_shading_cut(res, scantype, red, green, blue);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ hp4370_shading_cut(depth, res, scantype, red, green, blue);
+ break;
+
+ default:
+ /* hp3970 and hp4070 */
+ hp3970_shading_cut(RTS_Debug->usbtype, sensortype, depth, res, scantype, red, green, blue);
+ break;
+ }
+}
+
+/** SEC: Sensor Timings ---------- */
+
+/* ccd timing values for bq5550 scanner */
+static SANE_Int bq5550_timing_get(SANE_Int tm, struct st_timing *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if ((tm < 7)&&(reg != NULL))
+ {
+ /* bq5550 sensor timing values for each resolution and color mode */
+
+ struct st_timing data[] =
+ {
+ /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0F , 0x1F }, {0x12 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{66571993091., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {125829120. , 0. , 0x00 , 0x01 , 0x00 }, {67654057987., 0. , 0x00 , 0x01 , 0x00 }, {1065418748. , 0. , 0x00 , 0x01 , 0x01 }, {16383. , 0. , 0x00 , 0x01 , 0x00 }, {68182605824., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0F , 0x1F }, {0x12 , 0x22 }, {0x00 , 0x00 }, {262140. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{66571993091., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {262140. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {125829120. , 0. , 0x00 , 0x01 , 0x00 }, {67654057987., 0. , 0x00 , 0x01 , 0x00 }, {1065418748. , 0. , 0x00 , 0x01 , 0x01 }, {16383. , 0. , 0x00 , 0x01 , 0x00 }, {68182605824., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {524284. , 0. }, 0x00}
+ };
+
+ memcpy(reg, &data[tm], sizeof(struct st_timing));
+ rst = OK;
+ }
+
+ return rst;
+}
+
+/* ccd timing values for hp3800 scanner */
+static SANE_Int hp3800_timing_get(SANE_Int tm, struct st_timing *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if ((tm < 20)&&(reg != NULL))
+ {
+ /* Toshiba T2905 sensor timing values for each resolution and color mode */
+
+ struct st_timing data[] =
+ {
+ /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */
+ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 0. , 0x00 , 0x01 , 0x01 }, {68719476732., 0. , 0x00 , 0x01 , 0x01 }, {134217216. , 0. , 0x01 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 0. , 0x00 , 0x01 , 0x01 }, {66571993087., 0. , 0x00 , 0x01 , 0x01 }, {524287. , 0. , 0x00 , 0x01 , 0x00 }, {68718952448., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {1069563840. , 0. , 0x00 , 0x01 , 0x00 }, {67649912895., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x03 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {477225440. , 0. , 0x00 , 0x01 , 0x00 }, {68242251295., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x0096, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {13743895344., 0. , 0x00 , 0x01 , 0x00 }, {54975581391., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x01 , 0x21 }, {0x02 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+
+ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 0. , 0x00 , 0x01 , 0x01 }, {68719476732., 0. , 0x00 , 0x01 , 0x01 }, {134217216. , 0. , 0x01 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 0. , 0x00 , 0x01 , 0x01 }, {66571993087., 0. , 0x00 , 0x01 , 0x01 }, {524287. , 0. , 0x00 , 0x01 , 0x00 }, {68718952448., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {1069563840. , 0. , 0x00 , 0x01 , 0x00 }, {67649912895., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x03 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {477225440. , 0. , 0x00 , 0x01 , 0x00 }, {68242251295., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {266346464. , 0. , 0x00 , 0x01 , 0x00 }, {68453130271., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x04 , 0x21 }, {0x06 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+
+ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 8589934588. , 0x00 , 0x01 , 0x01 }, {68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 64692944895., 0x00 , 0x01 , 0x01 }, {66571993087., 66571993087., 0x00 , 0x01 , 0x01 }, {524287. , 524287. , 0x00 , 0x01 , 0x00 }, {68718952448., 68718952448., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {534781920. , 534781920. , 0x00 , 0x01 , 0x00 }, {68184694815., 68184694815., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x20FF , 0x01 , -1 , {0x05 , 0x21 }, {0x07 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {477225440. , 477225440. , 0x00 , 0x01 , 0x00 }, {68242251295., 68242251295., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x11FF , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x0096, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {13743895344., 13743895344., 0x00 , 0x01 , 0x00 }, {54975581391., 54975581391., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x110F , 0x01 , -1 , {0x01 , 0x21 }, {0x02 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+
+ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 8589934588. , 0x00 , 0x01 , 0x01 }, {68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 64692944895., 0x00 , 0x01 , 0x01 }, {66571993087., 66571993087., 0x00 , 0x01 , 0x01 }, {524287. , 524287. , 0x00 , 0x01 , 0x00 }, {68718952448., 68718952448., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {534781920. , 534781920. , 0x00 , 0x01 , 0x00 }, {68184694815., 68184694815., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x20FF , 0x01 , -1 , {0x05 , 0x21 }, {0x07 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {477225440. , 477225440. , 0x00 , 0x01 , 0x00 }, {68242251295., 68242251295., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x110F , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x0096, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {13743895344., 13743895344., 0x00 , 0x01 , 0x00 }, {54975581391., 54975581391., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x110F , 0x01 , -1 , {0x01 , 0x21 }, {0x02 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ };
+
+ memcpy(reg, &data[tm], sizeof(struct st_timing));
+ rst = OK;
+ }
+
+ return rst;
+}
+
+/* ccd timing values for hp3970 scanner */
+static SANE_Int hp3970_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if ((tm < 12)&&(reg != NULL))
+ {
+ rst = OK;
+ if (sensortype == CCD_SENSOR)
+ {
+ /* Toshiba T2952 sensor timing values for each resolution and color mode */
+ struct st_timing data[] =
+ {
+ /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */
+ {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34376515584., 0. , 0x00 , 0x01 , 0x00 }, {8455716864. , 0. , 0x01 , 0x01 , 0x00 }, {60246982656., 0. , 0x01 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x04 , 0x09 }, {0x06 , 0x0B }, {0x00 , 0x00 }, {27481079808., 0. }, 0x00},
+ {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {64424511232., 0. , 0x00 , 0x01 , 0x00 }, {68711088128., 0. , 0x00 , 0x01 , 0x00 }, {8388352. , 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x15 }, {0x0C , 0x18 }, {0x00 , 0x00 }, {33351135232., 33351135232.}, 0x01},
+ {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {60129570816., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x12 }, {0x0C , 0x14 }, {0x00 , 0x00 }, {64677150720., 0. }, 0x00},
+ {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51539611648., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {1057222656. , 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {8084643840. , 0. }, 0x00},
+ {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68182605888., 0. , 0x00 , 0x01 , 0x00 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1B }, {0x0C , 0x1D }, {0x00 , 0x00 }, {4164816768. , 0. }, 0x00},
+ {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34376515584., 0. , 0x00 , 0x01 , 0x00 }, {8455716864. , 0. , 0x01 , 0x01 , 0x00 }, {60246982656., 0. , 0x01 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x04 , 0x09 }, {0x06 , 0x0B }, {0x00 , 0x00 }, {2113929216. , 0. }, 0x00},
+ {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {64424511232., 0. , 0x00 , 0x01 , 0x00 }, {68711088128., 0. , 0x00 , 0x01 , 0x00 }, {8388352. , 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x15 }, {0x0C , 0x18 }, {0x00 , 0x00 }, {67104768. , 67104768. }, 0x01},
+ {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {60129570816., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x12 }, {0x0C , 0x14 }, {0x00 , 0x00 }, {268369920. , 0. }, 0x00},
+ {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51539611648., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {1057222656. , 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {33546240. , 0. }, 0x00},
+ {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68585258944., 0. , 0x00 , 0x01 , 0x00 }, {536870784. , 0. , 0x00 , 0x01 , 0x00 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1B }, {0x0C , 0x1C }, {0x00 , 0x00 }, {4194176. , 0. }, 0x00},
+ {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34359738368., 0. , 0x00 , 0x01 , 0x00 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {2114445438. , 0. }, 0x00},
+ {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34359738368., 0. , 0x00 , 0x01 , 0x00 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {524286. , 0. }, 0x00}
+ };
+
+ memcpy(reg, &data[tm], sizeof(struct st_timing));
+ } else
+ {
+ /* Sony S575 sensor timing values for each resolution and color mode
+ I haven't found any hp3970 scanner using sony sensor but windows drivers support this case */
+ struct st_timing data[] =
+ {
+ /* res , cnpp, {cvtrp1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */
+ {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60112764928., 0. , 0x00 , 0x01 , 0x00 }, {34326183936., 0. , 0x00 , 0x01 , 0x00 }, {1056964608. , 0. , 0x01 , 0x01 , 0x00 }, {67645734912., 0. , 0x01 , 0x01 , 0x01 }, {1056964608. , 0. , 0x00 , 0x01 , 0x00 }, {67645734912., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x05 , 0x09 }, {0x07 , 0x0b }, {0x00 , 0x00 }, {27481079808., 0. }, 0x00},
+ {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68316823296., 0. , 0x00 , 0x01 , 0x00 }, {42949672704., 0. , 0x00 , 0x01 , 0x00 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0B , 0x17 }, {0x0D , 0x19 }, {0x00 , 0x00 }, {16675567616., 16675567616.}, 0x01},
+ {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{67914166272., 0. , 0x00 , 0x01 , 0x00 }, {42949668864., 0. , 0x00 , 0x01 , 0x00 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x15 }, {0x0B , 0x17 }, {0x00 , 0x00 }, {8084643840. , 0. }, 0x00},
+ {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60129538048., 0. , 0x00 , 0x01 , 0x00 }, {34359730176., 0. , 0x00 , 0x01 , 0x00 }, {1057222656. , 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {8084643840. , 0. }, 0x00},
+ {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68585258944., 0. , 0x00 , 0x01 , 0x00 }, {536870784. , 0. , 0x00 , 0x01 , 0x00 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1A }, {0x0C , 0x1C }, {0x00 , 0x00 }, {8329633536. , 0. }, 0x00},
+ {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60112764928., 0. , 0x00 , 0x01 , 0x00 }, {34326183936., 0. , 0x00 , 0x01 , 0x00 }, {1056964608. , 0. , 0x01 , 0x01 , 0x00 }, {67645734912., 0. , 0x01 , 0x01 , 0x01 }, {1056964608. , 0. , 0x00 , 0x01 , 0x00 }, {67645734912., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x05 , 0x09 }, {0x07 , 0x0B }, {0x00 , 0x00 }, {2113929216. , 0. }, 0x00},
+ {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68316823296., 0. , 0x00 , 0x01 , 0x00 }, {42949672704., 0. , 0x00 , 0x01 , 0x00 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0B , 0x17 }, {0x0D , 0x19 }, {0x00 , 0x00 }, {33552384. , 33552384. }, 0x01},
+ {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{67914166272., 0. , 0x00 , 0x01 , 0x00 }, {42949668864., 0. , 0x00 , 0x01 , 0x00 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x15 }, {0x0B , 0x17 }, {0x00 , 0x00 }, {33546240. , 0. }, 0x00},
+ {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60129538048., 0. , 0x00 , 0x01 , 0x00 }, {34359730176., 0. , 0x00 , 0x01 , 0x00 }, {34359730176., 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {33546240. , 0. }, 0x00},
+ {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68182605888., 0. , 0x00 , 0x01 , 0x00 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1A }, {0x0C , 0x1D }, {0x00 , 0x00 }, {4194176. , 0. }, 0x00},
+ {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{51539607551., 0. , 0x00 , 0x01 , 0x00 }, {34359738366., 0. , 0x00 , 0x01 , 0x00 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {2114445438. , 0. }, 0x00},
+ {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{51539607551., 0. , 0x00 , 0x01 , 0x00 }, {34359738366., 0. , 0x00 , 0x01 , 0x00 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {524286. , 0. }, 0x00}
+ };
+
+ memcpy(reg, &data[tm], sizeof(struct st_timing));
+ }
+ }
+
+ return rst;
+}
+
+/* ccd timing values for hp4370 scanner */
+static SANE_Int hp4370_timing_get(SANE_Int tm, struct st_timing *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if ((reg != NULL)&&(tm < 14))
+ {
+ /* Toshiba T2958 sensor timing values for each resolution and color mode */
+
+ struct st_timing data[] =
+ {
+ /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */
+ {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x5D5B , 0xBAB7 , 0x1A , -1 , {0x08 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {8084644321. , 8084644321. }, 0x00},
+ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869182., 0. , 0x00 , 0x01 , 0x01 }, {268434432. , 0. , 0x01 , 0x01 , 0x00 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , -1 , {0x07 , 0x18 }, {0x0B , 0x1E }, {0x00 , 0x00 }, {67662254016., 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719475967., 0. , 0x00 , 0x01 , 0x01 }, {1048572. , 0. , 0x00 , 0x01 , 0x01 }, {68718428163., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x1F , 0x17 }, {0x21 , 0x19 }, {0x01 , 0x01 }, {34888349727., 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719476731., 0. , 0x00 , 0x01 , 0x01 }, {4261672960. , 0. , 0x00 , 0x01 , 0x00 }, {64457803775., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x00 , 0x1F }, {0x03 , 0x21 }, {0x00 , 0x00 }, {8457781752. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51808043007., 0. , 0x00 , 0x01 , 0x01 }, {8084644320. , 0. , 0x00 , 0x01 , 0x00 }, {60634832415., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x09 , 0x20 }, {0x0A , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00},
+ {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x5D5B , 0xBAB7 , 0x1A , -1 , {0x08 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {33546240. , 33546240. }, 0x00},
+ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869182., 0. , 0x00 , 0x01 , 0x01 }, {268434432. , 0. , 0x01 , 0x01 , 0x00 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , -1 , {0x07 , 0x18 }, {0x0B , 0x1E }, {0x00 , 0x00 }, {16777152. , 0. }, 0x00},
+ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719475967., 0. , 0x00 , 0x01 , 0x01 }, {1048572. , 0. , 0x00 , 0x01 , 0x01 }, {68718428163., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x1F , 0x17 }, {0x21 , 0x19 }, {0x01 , 0x01 }, {536868864. , 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719476731., 0. , 0x00 , 0x01 , 0x01 }, {4261672960. , 0. , 0x00 , 0x01 , 0x00 }, {64457803775., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x00 , 0x1F }, {0x03 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00},
+ {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51808043007., 0. , 0x00 , 0x01 , 0x01 }, {8084644320. , 0. , 0x00 , 0x01 , 0x00 }, {60634832415., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x09 , 0x20 }, {0x0A , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00},
+ {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x6977 , 0xD2EF , 0x1A , -1 , {0x07 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {8084644321. , 8084644321. }, 0x00},
+ {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x6977 , 0xD2EF , 0x1A , -1 , {0x07 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {33546240. , 33546240. }, 0x00},
+ {0x12C0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {8589934591. , 8589934591. , 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x765B , 0xECB7 , 0x1A , -1 , {0x07 , 0x1B }, {0x0D , 0x21 }, {0x00 , 0x00 }, {8457781752. , 8457781752. }, 0x00},
+ {0x12C0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {8589934591. , 8589934591. , 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x765B , 0xECB7 , 0x1A , -1 , {0x07 , 0x1B }, {0x0D , 0x21 }, {0x00 , 0x00 }, {2097144. , 2097144. }, 0x00}
+ };
+
+ memcpy(reg, &data[tm], sizeof(struct st_timing));
+ rst = OK;
+ }
+
+ return rst;
+}
+
+/* ccd timing values for ua4900 scanner */
+static SANE_Int ua4900_timing_get(SANE_Int tm, struct st_timing *reg)
+{
+ SANE_Int rst = ERROR;
+
+ if ((tm < 10)&&(reg != NULL))
+ {
+ /* Sony S575 sensor timing values for each resolution and color mode */
+ struct st_timing data[] =
+ {
+ /* res , cnpp, {cvtrp1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */
+ {0x04b0, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{ 1073737728., 0. , 0x01 , 0x01 , 0x00 }, {67645739007., 0. , 0x01 , 0x01 , 0x01 }, {67645739007., 0. , 0x01 , 0x01 , 0x01 }, { 1073737728., 0. , 0x01 , 0x01 , 0x00 }, {25769803776., 0. , 0x00 , 0x01 , 0x00 }, { 62., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x1c }, {0x0A , 0x1e }, {0x00 , 0x00 }, {67662254016., 0. }, 0x00},
+ {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x03 , 0x03 , {{ 262143., 0. , 0x00 , 0x01 , 0x00 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, { 262143., 3. , 0x00 , 0x01 , 0x00 }, { 2080374784., 0. , 0x00 , 0x01 , 0x00 }, {64424509455., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x10 , 0x1d }, {0x12 , 0x1f }, {0x00 , 0x00 }, {33831127008., 0. }, 0x00},
+ {0x012c, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{ 32767., 0. , 0x00 , 0x01 , 0x00 }, {68182605824., 0. , 0x00 , 0x01 , 0x01 }, { 2130837500., 0. , 0x00 , 0x01 , 0x01 }, {66588639235., 0. , 0x00 , 0x01 , 0x00 }, { 117440512., 0. , 0x00 , 0x01 , 0x00 }, {68182605825., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x12 , 0x1f }, {0x14 , 0x21 }, {0x00 , 0x00 }, { 8457781752., 0. }, 0x00},
+ {0x00c8, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x10 , 0x1f }, {0x12 , 0x21 }, {0x00 , 0x00 }, { 8457781752., 0. }, 0x00},
+ {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x10 , 0x1f }, {0x12 , 0x21 }, {0x00 , 0x00 }, { 8457781752., 0. }, 0x00},
+ {0x04b0, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{ 268434432., 0. , 0x01 , 0x01 , 0x00 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, { 268434432., 0. , 0x01 , 0x01 , 0x00 }, {51539607552., 0. , 0x00 , 0x01 , 0x00 }, {30. , 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x1d }, {0x09 , 0x1e }, {0x00 , 0x00 }, {16777152. , 0. }, 0x00},
+ {0x0258, 0x1d, {0x00, 0x00, 0x00}, 0x0f , 0x03 , 0x03 , {{ 2097088., 0. , 0x00 , 0x01 , 0x00 }, {68717379632., 0. , 0x00 , 0x01 , 0x01 }, {68717379632., 0. , 0x00 , 0x01 , 0x01 }, { 2097088., 3. , 0x00 , 0x01 , 0x00 }, { 1879048192., 0. , 0x00 , 0x01 , 0x00 }, {60129542592., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0d , 0x16 }, {0x0f , 0x18 }, {0x00 , 0x00 }, {134213632. , 0. }, 0x00},
+ {0x012c, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{64424510463., 0. , 0x00 , 0x01 , 0x00 }, { 4278190080., 0. , 0x00 , 0x01 , 0x01 }, { 267390975., 0. , 0x00 , 0x01 , 0x01 }, {68452085760., 0. , 0x00 , 0x01 , 0x00 }, { 6291456., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x15 , 0x1f }, {0x17 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00},
+ {0x00C8, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x17 , 0x1f }, {0x19 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00},
+ {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x17 , 0x1f }, {0x19 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00}
+ };
+
+ memcpy(reg, &data[tm], sizeof(struct st_timing));
+ rst = OK;
+ }
+
+ return rst;
+}
+
+static SANE_Int cfg_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg)
+{
+ /* return timing values for current device */
+
+ SANE_Int rst = ERROR;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case BQ5550:
+ rst = bq5550_timing_get(tm, reg);
+ break;
+
+ case UA4900:
+ rst = ua4900_timing_get(tm, reg);
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_timing_get(tm, reg);
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_timing_get(tm, reg);
+ break;
+
+ default:
+ /* hp3970 and hp4070 */
+ rst = hp3970_timing_get(sensortype, tm, reg);
+ break;
+ }
+
+ return rst;
+}
+
+/** SEC: Motor curves ---------- */
+
+static SANE_Int *bq5550_motor()
+{
+ SANE_Int *rst = NULL;
+ SANE_Int steps[] =
+ {
+ /* motorcurve 1 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 20000 ,6323 ,4095 ,3270 ,2823 ,2533 ,2318 ,2151 ,2016 ,1905 ,1811 ,1730 ,1660 ,1599 ,1544 ,1494 ,1450 ,1409 ,1373 ,1339 ,1307 ,1278 ,1251 ,1225 ,1201 ,1179 ,1158 ,1138 ,1119 ,1101 ,1084 ,1067 ,1052 ,1037 ,1023 ,1009 ,996 ,984 ,972 ,960 ,949 ,938 ,928 ,918 ,909 ,899 ,890 ,882 ,873 ,865 ,857 ,849 ,842 ,835 ,828 ,821 ,814 ,807 ,801 ,795 ,789 ,783 ,777 ,772 ,766 ,761 ,756 ,751 ,746 ,741 ,736 ,731 ,727 ,722 ,718 ,714 ,709 ,705 ,701 ,697 ,693 ,690 ,686 ,682 ,678 ,675 ,671 ,668 ,665 ,661 ,658 ,655 ,652 ,649 ,645 ,642 ,640 ,637 ,634 ,631 ,628 ,625 ,623 ,620 ,617 ,615 ,612 ,610 ,607 ,605 ,602 ,600 ,597 ,595 ,593 ,591 ,588 ,586 ,584 ,582 ,580 ,577 ,575 ,573 ,571 ,569 ,567 ,565 ,563 ,562 ,560 ,558 ,556 ,554 ,552 ,550 ,549 ,547 ,545 ,543 ,542 ,540 ,538 ,537 ,535 ,534 ,532 ,530 ,529 ,527 ,526 ,524 ,523 ,521 ,520 ,518 ,517 ,515 ,514 ,513 ,511 ,510 ,508 ,507 ,506 ,504 ,503 ,502 ,501 ,499 ,498 ,497 ,495 ,494 ,493 ,492 ,491 ,489 ,488 ,487 ,486 ,485 ,484 ,482 ,481 ,480 ,479 ,478 ,477 ,476 ,475 ,474 ,473 ,472 ,470 ,469 ,468 ,467 ,466 ,465 ,464 ,463 ,462 ,461 ,460 ,460 ,459 ,458 ,457 ,456 ,455 ,454 ,453 ,452 ,451 ,450 ,449 ,449 ,448 ,447 ,446 ,445 ,444 ,443 ,443 ,442 ,441 ,440 ,439 ,438 ,438 ,437 ,436 ,435 ,434 ,434 ,433 ,432 ,431 ,431 ,430 ,429 ,428 ,428 ,427 ,426 ,425 ,425 ,424 ,423 ,422 ,422 ,421 ,420 ,420 ,419 ,418 ,418 ,417 ,416 ,416 ,415 ,414 ,414 ,413 ,412 ,412 ,411 ,410 ,410 ,409 ,408 ,408 ,407 ,407 ,406 ,405 ,405 ,404 ,404 ,403 ,402 ,402 ,401 ,401 ,400 ,399 ,399 ,398 ,398 ,397 ,397 ,396 ,395 ,395 ,394 ,394 ,393 ,393 ,392 ,392 ,391 ,390 ,390 ,389 ,389 ,388 ,388 ,387 ,387 ,386 ,386 ,385 ,385 ,384 ,384 ,383 ,383 ,382 ,382 ,381 ,381 ,380 ,380 ,379 ,379 ,378 ,378 ,377 ,377 ,377 ,376 ,376 ,375 ,375 ,374 ,374 ,373 ,373 ,372 ,372 ,372 ,371 ,371 ,370 ,370 ,369 ,369 ,368 ,368 ,368 ,367 ,367 ,366 ,366 ,365 ,365 ,365 ,364 ,364 ,363 ,363 ,363 ,362 ,362 ,361 ,361 ,361 ,360 ,360 ,359 ,359 ,359 ,358 ,358 ,357 ,357 ,357 ,356 ,356 ,356 ,355 ,355 ,354 ,354 ,354 ,353 ,353 ,353 ,352 ,352 ,351 ,351 ,351 ,350 ,350 ,350 ,349 ,349 ,349 ,348 ,348 ,348 ,347 ,347 ,347 ,346 ,346 ,346 ,345 ,345 ,345 ,344 ,344 ,344 ,343 ,343 ,343 ,342 ,342 ,342 ,341 ,341 ,341 ,340 ,340 ,340 ,339 ,339 ,339 ,338 ,338 ,338 ,337 ,337 ,337 ,337 ,336 ,336 ,336 ,335 ,335 ,335 ,334 ,334 ,334 ,334 ,333 ,333 ,333 ,332 ,332 ,332 ,332 ,331 ,331 ,331 ,330 ,330 ,330 ,330 ,329 ,329 ,329 ,328 ,328 ,328 ,328 ,327 ,327 ,327 ,326 ,326 ,326 ,326 ,325 ,325 ,325 ,325 ,324 ,324 ,324 ,324 ,323 ,323 ,323 ,323 ,322 ,322 ,322 ,321 ,321 ,321 ,321 ,320 ,320 ,320 ,320 ,319 ,319 ,319 ,319 ,318 ,318 ,318 ,318 ,317 ,317 ,317 ,317 ,317 ,316 ,316 ,316 ,316 ,315 ,315 ,315 ,315 ,314 ,314 ,314 ,314 ,313 ,313 ,313 ,313 ,313 ,312 ,312 ,312 ,312 ,311 ,311 ,311 ,311 ,311 ,310 ,310 ,310 ,310 ,309 ,309 ,309 ,309 ,309 ,308 ,308 ,308 ,308 ,307 ,307 ,307 ,307 ,307 ,306 ,306 ,306 ,306 ,306 ,305 ,305 ,305 ,305 ,305 ,304 ,304 ,304 ,304 ,303 ,303 ,303 ,303 ,303 ,302 ,302 ,302 ,302 ,302 ,301 ,301 ,301 ,301 ,301 ,300 ,300 ,300 ,300 ,300 ,300 ,299 ,299 ,299 ,299 ,299 ,298 ,298 ,298 ,298 ,298 ,297 ,297 ,297 ,297 ,297 ,297 ,296 ,296 ,296 ,296 ,296 ,295 ,295 ,295 ,295 ,295 ,295 ,294 ,294 ,294 ,294 ,294 ,293 ,293 ,293 ,293 ,293 ,293 ,292 ,292 ,292 ,292 ,292 ,292 ,291 ,291 ,291 ,291 ,291 ,290 ,290 ,290 ,290 ,290 ,290 ,289 ,289 ,289 ,289 ,289 ,289 ,288 ,288 ,288 ,288 ,288 ,288 ,288 ,287 ,287 ,287 ,287 ,287 ,287 ,286 ,286 ,286 ,286 ,286 ,286 ,285 ,285 ,285 ,285 ,285 ,285, 0,
+ ACC_CURVE,CRV_SMEARING , 4000, 3877, 3377, 2777, 2127, 1724, 1449, 1250, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 479 ,500 ,547 ,607 ,682 ,766 ,865 ,950 ,1040 ,1200 ,1369 ,1580 ,1810 ,2087 ,2500 ,3048 ,3877 ,4818 ,5822 ,6896, 0,
+ DEC_CURVE,CRV_SMEARING , 1250, 1449, 1724, 2127, 2777, 3377, 3877, 4000, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 479 ,500 ,547 ,607 ,682 ,766 ,865 ,950 ,1040 ,1200 ,1369 ,1580 ,1810 ,2087 ,2500 ,3048 ,3877 ,4818 ,5822 ,6896, 0,
+ -1
+ };
+
+ rst = (SANE_Int *)malloc(sizeof(steps));
+ if (rst != NULL)
+ memcpy(rst, &steps, sizeof(steps));
+
+ return rst;
+}
+
+static SANE_Int *hp4370_motor()
+{
+ SANE_Int *rst = NULL;
+ SANE_Int steps[] =
+ {
+ /* motorcurve 1 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 2000, 1984, 1968, 1953, 1937, 1921, 1906, 1890, 1874, 1859, 1843, 1827, 1812, 1796, 1781, 1765, 1749, 1734, 1715, 1700, 1684, 1669, 1653, 1637, 1622, 1606, 1590, 1572, 1556, 1541, 1525, 1510, 1494, 1478, 1463, 1447, 1431, 1416, 1400, 1384, 1366, 1351, 1335, 1319, 1304, 1288, 1272, 1257, 1241, 1225, 1210, 1194, 1179, 1160, 1145, 1129, 1113, 1098, 1082, 1066, 1051, 1035, 1017, 1001, 986, 970, 954, 939, 923, 907, 892, 876, 861, 845, 829, 814, 798, 782, 767, 749, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0,
+ ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 749, 1166, 1583, 2000, 0,
+ DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0,
+ -2,
+
+ /* motorcurve 2 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0,
+ ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0,
+ -2,
+
+ /* motorcurve 3 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 5360, 3655, 2855, 2422, 2142, 1944, 1795, 1678, 1582, 1503, 1434, 1374, 0,
+ ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 684, 677, 669, 662, 655, 648, 642, 636, 629, 624, 618, 612, 607, 602, 596, 591, 587, 582, 577, 573, 568, 564, 560, 556, 552, 548, 544, 540, 537, 533, 530, 526, 523, 520, 516, 513, 510, 507, 504, 501, 498, 496, 493, 490, 488, 485, 482, 480, 477, 475, 472, 470, 468, 466, 463, 461, 459, 457, 455, 453, 450, 448, 446, 444, 443, 441, 439, 437, 435, 433, 431, 430, 428, 426, 425, 423, 421, 420, 418, 416, 415, 413, 412, 410, 409, 407, 406, 405, 403, 402, 400, 399, 398, 396, 395, 394, 392, 391, 390, 389, 387, 386, 385, 384, 382, 381, 380, 379, 378, 377, 376, 374, 373, 372, 371, 370, 369, 368, 367, 366, 365, 364, 363, 362, 361, 360, 359, 358, 357, 356, 355, 354, 353, 353, 352, 351, 350, 349, 348, 0,
+ ACC_CURVE,CRV_SMEARING , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0,
+ DEC_CURVE,CRV_PARKHOME , 348, 351, 353, 356, 359, 362, 365, 368, 371, 374, 378, 381, 385, 389, 392, 396, 400, 405, 409, 413, 418, 423, 428, 433, 439, 444, 450, 457, 463, 470, 477, 485, 493, 501, 510, 520, 530, 540, 552, 564, 577, 591, 607, 624, 642, 662, 684, 709, 737, 769, 805, 847, 897, 958, 1034, 1131, 1264, 1458, 1791, 2628, 5360, 0,
+ DEC_CURVE,CRV_SMEARING , 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0,
+ -2,
+
+ /* motorcurve 4 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2888, 2234, 1878, 1648, 1485, 1362, 1265, 1186, 1120, 1064, 1016, 973, 936, 903, 873, 845, 821, 798, 777, 758, 740, 723, 708, 693, 679, 666, 654, 643, 632, 621, 612, 602, 593, 585, 576, 569, 561, 554, 547, 540, 534, 528, 522, 516, 510, 505, 499, 494, 490, 485, 480, 476, 471, 467, 463, 459, 455, 451, 448, 444, 440, 437, 434, 430, 427, 424, 421, 418, 415, 412, 409, 407, 404, 401, 399, 396, 394, 391, 389, 387, 384, 382, 380, 378, 376, 374, 371, 369, 367, 366, 364, 362, 360, 358, 356, 354, 353, 351, 349, 348, 346, 344, 343, 341, 340, 338, 337, 335, 334, 332, 331, 329, 328, 327, 325, 324, 323, 321, 320, 319, 318, 316, 315, 314, 313, 312, 311, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 285, 284, 283, 282, 281, 280, 279, 279, 278, 277, 276, 275, 275, 274, 273, 272, 272, 271, 270, 269, 269, 268, 267, 267, 266, 265, 264, 264, 263, 262, 262, 261, 260, 260, 259, 259, 258, 257, 257, 256, 255, 255, 254, 254, 253, 252, 252, 251, 251, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 223, 223, 222, 222, 222, 221, 221, 220, 220, 220, 219, 219, 219, 218, 218, 217, 217, 217, 216, 216, 216, 215, 215, 215, 214, 214, 214, 213, 213, 213, 212, 212, 212, 211, 211, 211, 210, 210, 210, 209, 209, 209, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 0,
+ ACC_CURVE,CRV_SMEARING , 4705, 3056, 2724, 2497, 1498, 1498, 1374, 1276, 1196, 1130, 1073, 1025, 982, 944, 911, 880, 853, 828, 805, 784, 764, 746, 730, 714, 699, 685, 675, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ DEC_CURVE,CRV_PARKHOME , 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 182, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 230, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 251, 253, 254, 256, 258, 260, 262, 264, 266, 268, 271, 273, 275, 278, 280, 282, 285, 288, 290, 293, 296, 299, 302, 305, 309, 312, 316, 319, 323, 327, 331, 336, 340, 345, 350, 355, 360, 365, 371, 377, 384, 391, 398, 406, 414, 422, 432, 441, 452, 463, 476, 489, 504, 520, 538, 558, 580, 605, 633, 667, 706, 752, 810, 883, 979, 1116, 1326, 1714, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 675, 685, 699, 714, 730, 746, 764, 784, 805, 828, 853, 880, 911, 944, 982, 1025, 1073, 1130, 1196, 1276, 1374, 1498, 1498, 2497, 2724, 3056, 4705, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ -2,
+
+ /* motorcurve 5 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 3763, 3763, 3763, 3763, 3763, 3763, 2444, 2178, 1997, 1198, 1198, 1098, 1020, 956, 903, 858, 819, 785, 754, 727, 703, 681, 662, 644, 626, 610, 596, 582, 571, 558, 547, 537, 527, 518, 509, 500, 492, 485, 478, 471, 464, 458, 452, 446, 440, 435, 430, 425, 420, 415, 410, 407, 402, 398, 394, 391, 386, 383, 380, 376, 373, 369, 366, 364, 360, 357, 355, 352, 349, 347, 344, 341, 338, 337, 334, 332, 329, 328, 325, 323, 321, 319, 317, 315, 313, 311, 310, 308, 306, 304, 302, 301, 299, 297, 295, 294, 293, 291, 290, 288, 286, 285, 284, 283, 281, 280, 278, 277, 275, 275, 274, 272, 271, 270, 268, 267, 266, 265, 264, 263, 262, 261, 259, 258, 257, 257, 256, 255, 254, 253, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 243, 242, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 230, 230, 229, 228, 228, 227, 226, 225, 225,224, 223, 222, 222, 221, 221, 221, 220, 219, 219, 218, 217, 217, 216, 215, 215, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 185, 185, 185, 185, 185, 185, 184, 184, 183, 183, 183, 182, 182, 182, 181, 181, 180, 180, 180, 179, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101,101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0,
+ ACC_CURVE,CRV_PARKHOME , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0,
+ ACC_CURVE,CRV_SMEARING , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 340, 342, 345, 348, 351, 354, 357, 360, 363, 366, 369, 372, 375, 378, 381, 384, 387, 391, 395, 399, 403, 407, 411, 415, 419, 422, 425, 429, 433, 437, 441, 445, 449, 453, 458, 463, 468, 473, 478, 483, 489, 495, 501, 507, 514, 521, 528, 536, 544, 553, 562, 572, 582, 593, 604, 616, 629, 643, 658, 674, 692, 711, 732, 755, 781, 810, 843, 880, 923, 974,1035, 1110, 1205, 1331, 1510, 1796, 2368, 3400, 4922, 0,
+ DEC_CURVE,CRV_PARKHOME , 138, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_SMEARING , 138, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 184, 184, 184, 185, 185, 185, 185, 185, 186, 186, 186, 187, 187, 188, 188, 188, 189, 189, 190, 190, 191, 191, 191, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 203, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 221, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 230, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 239, 239, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 266, 267, 269, 270, 271, 273, 274, 275, 276, 277, 279, 280, 282, 283, 284, 285, 287, 289, 290, 292, 293, 294, 296, 298, 300, 301, 302, 304, 306, 308, 310, 311, 313, 315, 318, 320, 321, 323, 326, 328, 330, 332, 335, 337, 339, 342, 344, 347, 349, 352, 355, 358, 361, 364, 366, 370, 374, 376, 380, 383, 387, 391, 394, 399, 402, 407, 411, 416, 420, 425, 430, 436, 441, 446, 453, 458, 464, 472, 478, 485, 493, 501, 509, 518, 527, 537, 548, 559, 571, 583, 597, 611, 626, 644, 662, 682, 704, 728, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ -2,
+
+ /* motorcurve 6 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2888, 2234, 1878, 1648, 1485, 1362, 1265, 1186, 1120, 1064, 1016, 973, 936, 903, 873, 845, 821, 798, 777, 758, 740, 723, 708, 693, 679, 666, 654, 643, 632, 621, 612, 602, 593, 585, 576, 569, 561, 554, 547, 540, 534, 528, 522, 516, 510, 505, 499, 494, 490, 485, 480, 476, 471, 467, 463, 459, 455, 451, 448, 444, 440, 437, 434, 430, 427, 424, 421, 418, 415, 412, 409, 407, 404, 401, 399, 396, 394, 391, 389, 387, 384, 382, 380, 378, 376, 374, 371, 369, 367, 366, 364, 362, 360, 358, 356, 354, 353, 351, 349, 348, 346, 344, 343, 341, 340, 338, 337, 335, 334, 332, 331, 329, 328, 327, 325, 324, 323, 321, 320, 319, 318, 316, 315, 314, 313, 312, 311, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 285, 284, 283, 282, 281, 280, 279, 279, 278, 277, 276, 275, 275, 274, 273, 272, 272, 271, 270, 269, 269, 268, 267, 267, 266, 265, 264, 264, 263, 262, 262, 261, 260, 260, 259, 259, 258, 257, 257, 256, 255, 255, 254, 254, 253, 252, 252, 251, 251, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 223, 223, 222, 222, 222, 221, 221, 220, 220, 220, 219, 219, 219, 218, 218, 217, 217, 217, 216, 216, 216, 215, 215, 215, 214, 214, 214, 213, 213, 213, 212, 212, 212, 211, 211, 211, 210, 210, 210, 209, 209, 209, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 0,
+ ACC_CURVE,CRV_SMEARING , 4705, 3056, 2724, 2497, 1498, 1498, 1374, 1276, 1196, 1130, 1073, 1025, 982, 944, 911, 880, 853, 828, 805, 784, 764, 746, 730, 714, 699, 685, 675, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ DEC_CURVE,CRV_PARKHOME , 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 182, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 230, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 251, 253, 254, 256, 258, 260, 262, 264, 266, 268, 271, 273, 275, 278, 280, 282, 285, 288, 290, 293, 296, 299, 302, 305, 309, 312, 316, 319, 323, 327, 331, 336, 340, 345, 350, 355, 360, 365, 371, 377, 384, 391, 398, 406, 414, 422, 432, 441, 452, 463, 476, 489, 504, 520, 538, 558, 580, 605, 633, 667, 706, 752, 810, 883, 979, 1116, 1326, 1714, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 675, 685, 699, 714, 730, 746, 764, 784, 805, 828, 853, 880, 911, 944, 982, 1025, 1073, 1130, 1196, 1276, 1374, 1498, 1498, 2497, 2724, 3056, 4705, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ -2,
+
+ /* motorcurve 7 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 3763, 3763, 3763, 3763, 3763, 3763, 2444, 2178, 1997, 1198, 1198, 1098, 1020, 956, 903, 858, 819, 785, 754, 727, 703, 681, 662, 644, 626, 610, 596, 582, 571, 558, 547, 537, 527, 518, 509, 500, 492, 485, 478, 471, 464, 458, 452, 446, 440, 435, 430, 425, 420, 415, 410, 407, 402, 398, 394, 391, 386, 383, 380, 376, 373, 369, 366, 364, 360, 357, 355, 352, 349, 347, 344, 341, 338, 337, 334, 332, 329, 328, 325, 323, 321, 319, 317, 315, 313, 311, 310, 308, 306, 304, 302, 301, 299, 297, 295, 294, 293, 291, 290, 288, 286, 285, 284, 283, 281, 280, 278, 277, 275, 275, 274, 272, 271, 270, 268, 267, 266, 265, 264, 263, 262, 261, 259, 258, 257, 257, 256, 255, 254, 253, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 243, 242, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 230, 230, 229, 228, 228, 227, 226, 225, 225,224, 223, 222, 222, 221, 221, 221, 220, 219, 219, 218, 217, 217, 216, 215, 215, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 185, 185, 185, 185, 185, 185, 184, 184, 183, 183, 183, 182, 182, 182, 181, 181, 180, 180, 180, 179, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101,101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0,
+ ACC_CURVE,CRV_PARKHOME , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0,
+ ACC_CURVE,CRV_SMEARING , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 522, 522, 528, 536, 544, 553, 562, 572, 582, 593, 604, 616, 629, 643, 658, 674, 692, 711, 732, 755, 781, 810, 843, 880, 923, 974, 1035, 1110, 1205, 1331, 1510, 1796, 2368, 3400, 4922, 0,
+ DEC_CURVE,CRV_PARKHOME , 138, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_SMEARING , 138, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 184, 184, 184, 185, 185, 185, 185, 185, 186, 186, 186, 187, 187, 188, 188, 188, 189, 189, 190, 190, 191, 191, 191, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 203, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 221, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 230, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 239, 239, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 266, 267, 269, 270, 271, 273, 274, 275, 276, 277, 279, 280, 282, 283, 284, 285, 287, 289, 290, 292, 293, 294, 296, 298, 300, 301, 302, 304, 306, 308, 310, 311, 313, 315, 318, 320, 321, 323, 326, 328, 330, 332, 335, 337, 339, 342, 344, 347, 349, 352, 355, 358, 361, 364, 366, 370, 374, 376, 380, 383, 387, 391, 394, 399, 402, 407, 411, 416, 420, 425, 430, 436, 441, 446, 453, 458, 464, 472, 478, 485, 493, 501, 509, 518, 527, 537, 548, 559, 571, 583, 597, 611, 626, 644, 662, 682, 704, 728, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ -2,
+
+ /* motorcurve 8 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 1046, 1046, 1046, 1046, 1046, 1046, 647, 501, 421, 370, 333, 305, 284, 266, 251, 239, 228, 218, 210, 202, 196, 190, 184, 179, 174, 170, 166, 162, 159, 155, 152, 149, 147, 144, 142, 139, 137, 135, 133, 131, 129, 127, 126, 124, 123, 121, 120, 118, 117, 116, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 100, 99, 98, 97, 96, 96, 95, 94, 94, 93, 92, 92, 91, 91, 90, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 82, 81, 81, 80, 80, 79, 79, 79, 78, 78, 78, 77, 77, 76, 76, 76, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 23, 0,
+ ACC_CURVE,CRV_PARKHOME , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0,
+ ACC_CURVE,CRV_SMEARING , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ DEC_CURVE,CRV_PARKHOME , 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ DEC_CURVE,CRV_SMEARING , 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 79, 80, 80, 81, 81, 81, 82, 82, 83, 83, 84, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 98, 99, 99, 100, 101, 102, 103, 104, 105, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 118, 119, 120, 122, 123, 125, 127, 128, 130, 132, 134, 136, 138, 140, 142, 145, 147, 150, 153, 156, 159, 163, 167, 171, 175, 180, 185, 190, 196, 203, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ -1
+ };
+
+ rst = (SANE_Int *)malloc(sizeof(steps));
+ if (rst != NULL)
+ memcpy(rst, &steps, sizeof(steps));
+
+ return rst;
+}
+
+static SANE_Int *hp3970_motor()
+{
+ SANE_Int *rst = NULL;
+ SANE_Int steps[] =
+ {
+ /* motorcurve 1 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 2000, 1984, 1968, 1953, 1937, 1921, 1906, 1890, 1874, 1859, 1843, 1827, 1812, 1796, 1781, 1765, 1749, 1734, 1715, 1700, 1684, 1669, 1653, 1637, 1622, 1606, 1590, 1572, 1556, 1541, 1525, 1510, 1494, 1478, 1463, 1447, 1431, 1416, 1400, 1384, 1366, 1351, 1335, 1319, 1304, 1288, 1272, 1257, 1241, 1225, 1210, 1194, 1179, 1160, 1145, 1129, 1113, 1098, 1082, 1066, 1051, 1035, 1017, 1001, 986, 970, 954, 939, 923, 907, 892, 876, 861, 845, 829, 814, 798, 782, 767, 749, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0,
+ ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 749, 1166, 1583, 2000, 0,
+ DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0,
+ -2,
+
+ /* motorcurve 2 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0,
+ ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0,
+ -2,
+
+ /* motorcurve 3 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 5360, 3655, 2855, 2422, 2142, 1944, 1795, 1678, 1582, 1503, 1434, 1374, 0,
+ ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 684, 677, 669, 662, 655, 648, 642, 636, 629, 624, 618, 612, 607, 602, 596, 591, 587, 582, 577, 573, 568, 564, 560, 556, 552, 548, 544, 540, 537, 533, 530, 526, 523, 520, 516, 513, 510, 507, 504, 501, 498, 496, 493, 490, 488, 485, 482, 480, 477, 475, 472, 470, 468, 466, 463, 461, 459, 457, 455, 453, 450, 448, 446, 444, 443, 441, 439, 437, 435, 433, 431, 430, 428, 426, 425, 423, 421, 420, 418, 416, 415, 413, 412, 410, 409, 407, 406, 405, 403, 402, 400, 399, 398, 396, 395, 394, 392, 391, 390, 389, 387, 386, 385, 384, 382, 381, 380, 379, 378, 377, 376, 374, 373, 372, 371, 370, 369, 368, 367, 366, 365, 364, 363, 362, 361, 360, 359, 358, 357, 356, 355, 354, 353, 353, 352, 351, 350, 349, 348, 0,
+ ACC_CURVE,CRV_SMEARING , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0,
+ DEC_CURVE,CRV_PARKHOME , 348, 351, 353, 356, 359, 362, 365, 368, 371, 374, 378, 381, 385, 389, 392, 396, 400, 405, 409, 413, 418, 423, 428, 433, 439, 444, 450, 457, 463, 470, 477, 485, 493, 501, 510, 520, 530, 540, 552, 564, 577, 591, 607, 624, 642, 662, 684, 709, 737, 769, 805, 847, 897, 958, 1034, 1131, 1264, 1458, 1791, 2628, 5360, 0,
+ DEC_CURVE,CRV_SMEARING , 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0,
+ -2,
+
+ /* motorcurve 4 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0,
+ ACC_CURVE,CRV_PARKHOME , 4705, 2888, 2234, 1878, 1648, 1485, 1362, 1265, 1186, 1120, 1064, 1016, 973, 936, 903, 873, 845, 821, 798, 777, 758, 740, 723, 708, 693, 679, 666, 654, 643, 632, 621, 612, 602, 593, 585, 576, 569, 561, 554, 547, 540, 534, 528, 522, 516, 510, 505, 499, 494, 490, 485, 480, 476, 471, 467, 463, 459, 455, 451, 448, 444, 440, 437, 434, 430, 427, 424, 421, 418, 415, 412, 409, 407, 404, 401, 399, 396, 394, 391, 389, 387, 384, 382, 380, 378, 376, 374, 371, 369, 367, 366, 364, 362, 360, 358, 356, 354, 353, 351, 349, 348, 346, 344, 343, 341, 340, 338, 337, 335, 334, 332, 331, 329, 328, 327, 325, 324, 323, 321, 320, 319, 318, 316, 315, 314, 313, 312, 311, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 285, 284, 283, 282, 281, 280, 279, 279, 278, 277, 276, 275, 275, 274, 273, 272, 272, 271, 270, 269, 269, 268, 267, 267, 266, 265, 264, 264, 263, 262, 262, 261, 260, 260, 259, 259, 258, 257, 257, 256, 255, 255, 254, 254, 253, 252, 252, 251, 251, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 223, 223, 222, 222, 222, 221, 221, 220, 220, 220, 219, 219, 219, 218, 218, 217, 217, 217, 216, 216, 216, 215, 215, 215, 214, 214, 214, 213, 213, 213, 212, 212, 212, 211, 211, 211, 210, 210, 210, 209, 209, 209, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 0,
+ ACC_CURVE,CRV_SMEARING , 4705, 3056, 2724, 2497, 1498, 1498, 1374, 1276, 1196, 1130, 1073, 1025, 982, 944, 911, 880, 853, 828, 805, 784, 764, 746, 730, 714, 699, 685, 675, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ DEC_CURVE,CRV_PARKHOME , 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 182, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 230, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 251, 253, 254, 256, 258, 260, 262, 264, 266, 268, 271, 273, 275, 278, 280, 282, 285, 288, 290, 293, 296, 299, 302, 305, 309, 312, 316, 319, 323, 327, 331, 336, 340, 345, 350, 355, 360, 365, 371, 377, 384, 391, 398, 406, 414, 422, 432, 441, 452, 463, 476, 489, 504, 520, 538, 558, 580, 605, 633, 667, 706, 752, 810, 883, 979, 1116, 1326, 1714, 4705, 0,
+ DEC_CURVE,CRV_SMEARING , 675, 685, 699, 714, 730, 746, 764, 784, 805, 828, 853, 880, 911, 944, 982, 1025, 1073, 1130, 1196, 1276, 1374, 1498, 1498, 2497, 2724, 3056, 4705, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0,
+ -2,
+
+ /* motorcurve 5 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 3763, 3763, 3763, 3763, 3763, 3763, 2444, 2178, 1997, 1198, 1198, 1098, 1020, 956, 903, 858, 819, 785, 754, 727, 703, 681, 662, 644, 626, 610, 596, 582, 571, 558, 547, 537, 527, 518, 509, 500, 492, 485, 478, 471, 464, 458, 452, 446, 440, 435, 430, 425, 420, 415, 410, 407, 402, 398, 394, 391, 386, 383, 380, 376, 373, 369, 366, 364, 360, 357, 355, 352, 349, 347, 344, 341, 338, 337, 334, 332, 329, 328, 325, 323, 321, 319, 317, 315, 313, 311, 310, 308, 306, 304, 302, 301, 299, 297, 295, 294, 293, 291, 290, 288, 286, 285, 284, 283, 281, 280, 278, 277, 275, 275, 274, 272, 271, 270, 268, 267, 266, 265, 264, 263, 262, 261, 259, 258, 257, 257, 256, 255, 254, 253, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 243, 242, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 230, 230, 229, 228, 228, 227, 226, 225, 225,224, 223, 222, 222, 221, 221, 221, 220, 219, 219, 218, 217, 217, 216, 215, 215, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 185, 185, 185, 185, 185, 185, 184, 184, 183, 183, 183, 182, 182, 182, 181, 181, 180, 180, 180, 179, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101,101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0,
+ ACC_CURVE,CRV_PARKHOME , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0,
+ ACC_CURVE,CRV_SMEARING , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_PARKHOME , 138, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_SMEARING , 138, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 184, 184, 184, 185, 185, 185, 185, 185, 186, 186, 186, 187, 187, 188, 188, 188, 189, 189, 190, 190, 191, 191, 191, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 203, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 221, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 230, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 239, 239, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 266, 267, 269, 270, 271, 273, 274, 275, 276, 277, 279, 280, 282, 283, 284, 285, 287, 289, 290, 292, 293, 294, 296, 298, 300, 301, 302, 304, 306, 308, 310, 311, 313, 315, 318, 320, 321, 323, 326, 328, 330, 332, 335, 337, 339, 342, 344, 347, 349, 352, 355, 358, 361, 364, 366, 370, 374, 376, 380, 383, 387, 391, 394, 399, 402, 407, 411, 416, 420, 425, 430, 436, 441, 446, 453, 458, 464, 472, 478, 485, 493, 501, 509, 518, 527, 537, 548, 559, 571, 583, 597, 611, 626, 644, 662, 682, 704, 728, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0,
+ -2,
+
+ /* motorcurve 6 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 23999, 0,
+ ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 0,
+ ACC_CURVE,CRV_SMEARING , 23999, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 23999, 0,
+ DEC_CURVE,CRV_PARKHOME , 692, 700, 709, 718, 727, 737, 747, 758, 769, 780, 792, 805, 818, 832, 847, 863, 880, 897, 916, 937, 958, 981, 1006, 1034, 1063, 1096, 1131, 1170, 1214, 1264, 1319, 1384, 1458, 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0,
+ DEC_CURVE,CRV_SMEARING , 23999, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 23999, 0,
+ -2,
+
+ /* motorcurve 7 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 6667, 0,
+ ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 0,
+ ACC_CURVE,CRV_SMEARING , 6667, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 6667, 0,
+ DEC_CURVE,CRV_PARKHOME , 692, 700, 709, 718, 727, 737, 747, 758, 769, 780, 792, 805, 818, 832, 847, 863, 880, 897, 916, 937, 958, 981, 1006, 1034, 1063, 1096, 1131, 1170, 1214, 1264, 1319, 1384, 1458, 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0,
+ DEC_CURVE,CRV_SMEARING , 6667, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 6667, 0,
+ -2,
+
+ /* motorcurve 8 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN, 1046, 1046, 1046, 1046, 1046, 1046, 647, 501, 421, 370, 333, 305, 284, 266, 251, 239, 228, 218, 210, 202, 196, 190, 184, 179, 174, 170, 166, 162, 159, 155, 152, 149, 147, 144, 142, 139, 137, 135, 133, 131, 129, 127, 126, 124, 123, 121, 120, 118, 117, 116, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 100, 99, 98, 97, 96, 96, 95, 94, 94, 93, 92, 92, 91, 91, 90, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 82, 81, 81, 80, 80, 79, 79, 79, 78, 78, 78, 77, 77, 76, 76, 76, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 23, 0,
+ ACC_CURVE,CRV_PARKHOME , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0,
+ ACC_CURVE,CRV_SMEARING , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0,
+ DEC_CURVE,CRV_NORMALSCAN, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ DEC_CURVE,CRV_PARKHOME , 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ DEC_CURVE,CRV_SMEARING , 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 79, 80, 80, 81, 81, 81, 82, 82, 83, 83, 84, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 98, 99, 99, 100, 101, 102, 103, 104, 105, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 118, 119, 120, 122, 123, 125, 127, 128, 130, 132, 134, 136, 138, 140, 142, 145, 147, 150, 153, 156, 159, 163, 167, 171, 175, 180, 185, 190, 196, 203, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ DEC_CURVE,CRV_BUFFERFULL, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0,
+ -1
+ };
+
+ rst = (SANE_Int *)malloc(sizeof(steps));
+ if (rst != NULL)
+ memcpy(rst, &steps, sizeof(steps));
+
+ return rst;
+}
+
+static SANE_Int *hp3800_motor()
+{
+ SANE_Int *rst = NULL;
+ SANE_Int steps[] =
+ {
+ /* motorcurve 1 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,2000,1984,1968,1953,1937,1921,1906,1890,1874,1859,1843,1827,1812,1796,1781,1765,1749,1734,1715,1700,1684,1669,1653,1637,1622,1606,1590,1572,1556,1541,1525,1510,1494,1478,1463,1447,1431,1416,1400,1384,1366,1351,1335,1319,1304,1288,1272,1257,1241,1225,1210,1194,1179,1160,1145,1129,1113,1098,1082,1066,1051,1035,1017,1001,986,970,954,939,923,907,892,876,861,845,829,814,798,782,767,749, 0,
+ ACC_CURVE,CRV_PARKHOME ,4705,2913,2253,1894,1662,1498,1374,1276,1196,1129,1073,1024,981,944,910,880,852,827,804,783,764,746,729,713,699,685,672,659,648,637,626,616,607,598,589,581,573,565,558,551,544,538,532,526,520,514,509,503,500, 0,
+ ACC_CURVE,CRV_SMEARING ,200,12,14,16, 0,
+ DEC_CURVE,CRV_NORMALSCAN,749,1166,1583,2000, 0,
+ DEC_CURVE,CRV_PARKHOME ,500,503,509,514,520,526,532,538,544,551,558,565,573,581,589,598,607,616,626,637,648,659,672,685,699,713,729,746,764,783,804,827,852,880,910,944,981,1024,1073,1129,1196,1276,1374,1498,1662,1894,2253,2913,4705, 0,
+ DEC_CURVE,CRV_SMEARING ,300,234,167,100, 0,
+ DEC_CURVE,CRV_BUFFERFULL,1100,867,633,400, 0,
+ -2,
+
+ /* motorcurve 2 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,4705,2664,2061,1732,1521,1370,1257,1167,1094,1033,982,937,898,864,833,805,780,757,736,717,699,683,667,653,640,627,615,604,593,583,574,564,556,547,540,532,525,518,511,505,499,493,487,481,476,471,466,461,456,452,447,443,439,435,431,427,424,420,417,413,410,407,403,400,397,394,391,389,386,383,381,378,375,373,371,368,366,364,361,359,357,355,353,351,349,347,345,343,341,339,338,336,334,332,331,329,327,326,324,323,321,320,318,317,315,314,312,311,310,308,307,306,304,303,302,301,299,298,297,296,295,293,292,291,290,289,288,287,286,285,284,283,282,281,280,279,278,277,276,275,274,273,272,271,270,270,269,268,267,266,265,264,264,263,262,261,260,260,259,258,257,257,256,255,255,254,253,252,252,251,250,250,249,248,248,247,246,246,245,244,244,243,242,242,241,241,240,239,239,238,238,237,237,236,235,235,234,234,233,233,232,232,231,231,230,230,229,229,228,227,227,227,226,226,225,225,224,224,223,223,222,222,221,221,220,220,219,219,219,218,218,217,217,216,216,216,215,215,214,214,214,213,213,212,212,212,211,211,210,210,210,209,209,208,208,208,207,207,207,206,206,206,205,205,204,204,204,203,203,203,202,202,202,201,201,201,200,200,200,199,199,199,198,198,198,197,197,197,197,196,196,196,195,195,195,194,194,194,194,193,193,193,192,192,192,192,191,191,191,190,190,190,190,189,189,189,188,188,188,188,187,187,187,187,186,186,186,186,185,185,185,185,184,184,184,184,183,183,183,183,182,182,182,182,181,181,181,181,181,180,180,180,180,179,179,179,179,178,178,178,178,178,177,177,177,177,177,176,176,176,176,175,175,175,175,175,174,174,174,174,174,173,173,173,173,173,172,172,172,172,172,171,171,171, 0,
+ ACC_CURVE,CRV_PARKHOME ,4705,2913,2253,1894,1662,1498,1374,1276,1196,1129,1073,1024,981,944,910,880,852,827,804,783,764,746,729,713,699,685,672,659,648,637,626,616,607,598,589,581,573,565,558,551,544,538,532,526,520,514,509,503,500, 0,
+ ACC_CURVE,CRV_SMEARING ,200,12,14,16, 0,
+ DEC_CURVE,CRV_NORMALSCAN,171,172,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,184,184,185,186,187,188,189,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,213,214,215,217,218,219,221,222,224,225,227,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255,258,260,262,265,268,270,273,276,279,282,285,289,292,296,299,303,307,311,316,320,325,330,335,341,347,353,359,366,373,381,390,398,408,418,429,441,455,469,485,503,523,545,571,601,636,678,730,796,883,1005,1195,1544,4705, 0,
+ DEC_CURVE,CRV_PARKHOME ,500,503,509,514,520,526,532,538,544,551,558,565,573,581,589,598,607,616,626,637,648,659,672,685,699,713,729,746,764,783,804,827,852,880,910,944,981,1024,1073,1129,1196,1276,1374,1498,1662,1894,2253,2913,4705, 0,
+ DEC_CURVE,CRV_SMEARING ,300,234,167,100, 0,
+ DEC_CURVE,CRV_BUFFERFULL,1100,867,633,400, 0,
+ -2,
+
+ /* motorcurve 3 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,5360,3655,2855,2422,2142,1944,1795,1678,1582,1503,1434,1374, 0,
+ ACC_CURVE,CRV_PARKHOME ,5360,3362,2628,2229,1973,1791,1654,1547,1458,1384,1319,1264,1214,1170,1131,1096,1063,1034,1006,981,958,937,916,897,880,863,847,832,818,805,792,780,769,758,747,737,727,718,709,700,692,684,677,669,662,655,648,642,636,629,624,618,612,607,602,596,591,587,582,577,573,568,564,560,556,552,548,544,540,537,533,530,526,523,520,516,513,510,507,504,501,498,496,493,490,488,485,482,480,477,475,472,470,468,466,463,461,459,457,455,453,450,448,446,444,443,441,439,437,435,433,431,430,428,426,425,423,421,420,418,416,415,413,412,410,409,407,406,405,403,402,400,399,398,396,395,394,392,391,390,389,387,386,385,384,382,381,380,379,378,377,376,374,373,372,371,370,369,368,367,366,365,364,363,362,361,360,359,358,357,356,355,354,353,353,352,351,350,349,348, 0,
+ ACC_CURVE,CRV_SMEARING ,5360,3362,2628,2229,1973,1791,1654,1547, 0,
+ DEC_CURVE,CRV_NORMALSCAN,1374,1434,1503,1582,1678,1795,1944,2142,2422,2855,3655,5360, 0,
+ DEC_CURVE,CRV_PARKHOME ,348,351,353,356,359,362,365,368,371,374,378,381,385,389,392,396,400,405,409,413,418,423,428,433,439,444,450,457,463,470,477,485,493,501,510,520,530,540,552,564,577,591,607,624,642,662,684,709,737,769,805,847,897,958,1034,1131,1264,1458,1791,2628,5360, 0,
+ DEC_CURVE,CRV_SMEARING ,1547,1654,1791,1973,2229,2628,3362,5360, 0,
+ DEC_CURVE,CRV_BUFFERFULL,1374,1434,1503,1582,1678,1795,1944,2142,2422,2855,3655,5360, 0,
+ -2,
+
+ /* motorcurve 4 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,4705,2664,2061,1732,1521,1370,1257,1167,1094,1033,982,937,898,864,833,805,780,757,736,717,699,683,667,653,640,627,615,604,593,583,574,564,556,547,540,532,525,518,511,505,499,493,487,481,476,471,466,461,456,452,447,443,439,435,431,427,424,420,417,413,410,407,403,400,397,394,391,389,386,383,381,378,375,373,371,368,366,364,361,359,357,355,353,351,349,347,345,343,341,339,338,336,334,332,331,329,327,326,324,323,321,320,318,317,315,314,312,311,310,308,307,306,304,303,302,301,299,298,297,296,295,293,292,291,290,289,288,287,286,285,284,283,282,281,280,279,278,277,276,275,274,273,272,271,270,270,269,268,267,266,265,264,264,263,262,261,260,260,259,258,257,257,256,255,255,254,253,252,252,251,250,250,249,248,248,247,246,246,245,244,244,243,242,242,241,241,240,239,239,238,238,237,237,236,235,235,234,234,233,233,232,232,231,231,230,230,229,229,228,227,227,227,226,226,225,225,224,224,223,223,222,222,221,221,220,220,219,219,219,218,218,217,217,216,216,216,215,215,214,214,214,213,213,212,212,212,211,211,210,210,210,209,209,208,208,208,207,207,207,206,206,206,205,205,204,204,204,203,203,203,202,202,202,201,201,201,200,200,200,199,199,199,198,198,198,197,197,197,197,196,196,196,195,195,195,194,194,194,194,193,193,193,192,192,192,192,191,191,191,190,190,190,190,189,189,189,188,188,188,188,187,187,187,187,186,186,186,186,185,185,185,185,184,184,184,184,183,183,183,183,182,182,182,182,181,181,181,181,181,180,180,180,180,179,179,179,179,178,178,178,178,178,177,177,177,177,177,176,176,176,176,175,175,175,175,175,174,174,174,174,174,173,173,173,173,173,172,172,172,172,172,171,171,171, 0,
+ ACC_CURVE,CRV_PARKHOME ,4705,2888,2234,1878,1648,1485,1362,1265,1186,1120,1064,1016,973,936,903,873,845,821,798,777,758,740,723,708,693,679,666,654,643,632,621,612,602,593,585,576,569,561,554,547,540,534,528,522,516,510,505,499,494,490,485,480,476,471,467,463,459,455,451,448,444,440,437,434,430,427,424,421,418,415,412,409,407,404,401,399,396,394,391,389,387,384,382,380,378,376,374,371,369,367,366,364,362,360,358,356,354,353,351,349,348,346,344,343,341,340,338,337,335,334,332,331,329,328,327,325,324,323,321,320,319,318,316,315,314,313,312,311,309,308,307,306,305,304,303,302,301,300,299,298,297,296,295,294,293,292,291,290,289,288,287,286,285,285,284,283,282,281,280,279,279,278,277,276,275,275,274,273,272,272,271,270,269,269,268,267,267,266,265,264,264,263,262,262,261,260,260,259,259,258,257,257,256,255,255,254,254,253,252,252,251,251,250,249,249,248,248,247,247,246,246,245,245,244,243,243,242,242,241,241,240,240,239,239,238,238,237,237,237,236,236,235,235,234,234,233,233,232,232,231,231,231,230,230,229,229,228,228,228,227,227,226,226,225,225,225,224,224,223,223,223,222,222,222,221,221,220,220,220,219,219,219,218,218,217,217,217,216,216,216,215,215,215,214,214,214,213,213,213,212,212,212,211,211,211,210,210,210,209,209,209,208,208,208,207,207,207,207,206,206,206,205,205,205,204,204,204,204,203,203,203,202,202,202,202,201,201,201,200,200,200,200,199,199,199,199,198,198,198,198,197,197,197,196,196,196,196,195,195,195,195,194,194,194,194,193,193,193,193,192,192,192,192,192,191,191,191,191,190,190,190,190,189,189,189,189,189,188,188,188,188,187,187,187,187,187,186,186,186,186,186,185,185,185,185,184,184,184,184,184,183,183,183,183,183,182,182,182,182,182,181,181,181,181,181,180,180,180,180,180,180,179,179,179,179,179,178,178,178,178,178,177,177,177,177,177,177,176,176,176,176,176,176,175,175,175,175,175,174,174,174,174,174,174,173,173,173,173,173,173,172,172,172,172,172,172,171,171,171,171, 0,
+ ACC_CURVE,CRV_SMEARING ,4705,3056,2724,2497,1498,1498,1374,1276,1196,1130,1073,1025,982,944,911,880,853,828,805,784,764,746,730,714,699,685,675, 0,
+ DEC_CURVE,CRV_NORMALSCAN,171,172,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,184,184,185,186,187,188,189,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,213,214,215,217,218,219,221,222,224,225,227,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255,258,260,262,265,268,270,273,276,279,282,285,289,292,296,299,303,307,311,316,320,325,330,335,341,347,353,359,366,373,381,390,398,408,418,429,441,455,469,485,503,523,545,571,601,636,678,730,796,883,1005,1195,1544,4705, 0,
+ DEC_CURVE,CRV_PARKHOME ,171,172,172,173,173,174,174,175,175,176,176,177,177,178,179,179,180,180,181,182,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,194,195,195,196,197,198,199,199,200,201,202,203,204,205,206,206,207,208,209,210,211,212,213,214,215,217,218,219,220,221,222,223,225,226,227,228,230,231,232,234,235,237,238,240,241,243,244,246,247,249,251,253,254,256,258,260,262,264,266,268,271,273,275,278,280,282,285,288,290,293,296,299,302,305,309,312,316,319,323,327,331,336,340,345,350,355,360,365,371,377,384,391,398,406,414,422,432,441,452,463,476,489,504,520,538,558,580,605,633,667,706,752,810,883,979,1116,1326,1714,4705, 0,
+ DEC_CURVE,CRV_SMEARING ,675,685,699,714,730,746,764,784,805,828,853,880,911,944,982,1025,1073,1130,1196,1276,1374,1498,1498,2497,2724,3056,4705, 0,
+ DEC_CURVE,CRV_BUFFERFULL,171,172,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,184,184,185,186,187,188,189,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,213,214,215,217,218,219,221,222,224,225,227,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255,258,260,262,265,268,270,273,276,279,282,285,289,292,296,299,303,307,311,316,320,325,330,335,341,347,353,359,366,373,381,390,398,408,418,429,441,455,469,485,503,523,545,571,601,636,678,730,796,883,1005,1195,1544,4705, 0,
+ -2,
+
+ /* motorcurve 5 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,3763,3763,3763,3763,3763,3763,2444,2178,1997,1198,1198,1098,1020,956,903,858,819,785,754,727,703,681,662,644,626,610,596,582,571,558,547,537,527,518,509,500,492,485,478,471,464,458,452,446,440,435,430,425,420,415,410,407,402,398,394,391,386,383,380,376,373,369,366,364,360,357,355,352,349,347,344,341,338,337,334,332,329,328,325,323,321,319,317,315,313,311,310,308,306,304,302,301,299,297,295,294,293,291,290,288,286,285,284,283,281,280,278,277,275,275,274,272,271,270,268,267,266,265,264,263,262,261,259,258,257,257,256,255,254,253,251,250,249,248,248,247,246,245,244,243,243,242,241,240,239,239,238,237,236,235,235,234,233,232,231,230,230,230,229,228,228,227,226,225,225,224,223,222,222,221,221,221,220,219,219,218,217,217,216,215,215,214,213,213,212,212,212,211,211,210,209,209,208,208,207,207,206,206,205,204,204,203,203,203,203,202,202,201,201,200,200,199,199,198,198,197,197,196,196,195,195,194,194,194,194,194,193,193,192,192,191,191,190,190,190,189,189,188,188,188,187,187,186,186,185,185,185,185,185,185,184,184,183,183,183,182,182,182,181,181,180,180,180,179,179,179,178,178,178,177,177,177,176,176,176,176,176,176,175,175,175,174,174,174,173,173,173,172,172,172,171,171,171,171,170,170,170,169,169,169,169,168,168,168,167,167,167,167,167,167,167,166,166,166,166,165,165,165,165,164,164,164,163,163,163,163,162,162,162,162,161,161,161,161,160,160,160,160,159,159,159,159,159,158,158,158,158,158,158,158,158,157,157,157,157,157,156,156,156,156,155,155,155,155,155,154,154,154,154,154,153,153,153,153,152,152,152,152,152,151,151,151,151,151,150,150,150,150,150,149,149,149,149,149,149,149,149,149,149,149,148,148,148,148,148,147,147,147,147,147,147,146,146,146,146,146,145,145,145,145,145,145,144,144,144,144,144,144,143,143,143,143,143,143,142,142,142,142,142,142,141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140,140,140,140,139,139,139,139,139,139,139,138,138,138,138,138,138,138,137,137,137,137,137,137,137,136,136,136,136,136,136,136,135,135,135,135,135,135,135,134,134,134,134,134,134,134,134,133,133,133,133,133,133,133,133,132,132,132,132,132,132,132,132,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,130,130,130,130,130,130,130,130,129,129,129,129,129,129,129,129,129,128,128,128,128,128,128,128,128,128,127,127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,120,120,120,119,119,119,119,119,119,119,119,119,119,119,119,118,118,118,118,118,118,118,118,118,118,118,118,117,117,117,117,117,117,117,117,117,117,117,117,116,116,116,116,116,116,116,116,116,116,116,116,115,115,115,115,115,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93, 0,
+ ACC_CURVE,CRV_PARKHOME ,3763,2330,1803,1515,1330,1198,1099,1021,957,904,859,819,785,755,728,704,682,662,644,626,611,597,583,571,559,548,537,527,518,509,501,493,485,478,472,464,458,453,446,441,436,430,425,420,416,411,407,402,399,394,391,387,383,380,376,374,370,366,364,361,358,355,352,349,347,344,342,339,337,335,332,330,328,326,323,321,320,318,315,313,311,310,308,306,304,302,301,300,298,296,294,293,292,290,289,287,285,284,283,282,280,279,277,276,275,274,273,271,270,269,267,266,266,265,263,262,261,260,259,258,257,256,255,254,253,252,251,250,249,248,248,247,246,245,244,243,242,241,240,239,239,239,238,237,236,235,234,233,233,232,231,230,230,230,229,228,227,227,226,225,224,224,223,222,221,221,221,220,220,219,218,218,217,216,216,215,214,214,213,213,212,212,212,211,211,210,209,209,208,208,207,207,206,205,205,204,204,203,203,203,203,202,202,201,201,200,200,199,199,198,198,197,197,196,196,195,195,194,194,194,194,194,193,193,192,192,191,191,191,190,190,189,189,188,188,188,187,187,186,186,186,185,185,185,185,185,184,184,184,183,183,182,182,182,181,181,181,180,180,180,179,179,178,178,178,177,177,177,176,176,176,176,176,176,175,175,175,174,174,174,174,173,173,173,172,172,172,171,171,171,170,170,170,170,169,169,169,168,168,168,168,167,167,167,167,167,167,167,166,166,166,166,165,165,165,165,164,164,164,163,163,163,163,162,162,162,162,161,161,161,161,160,160,160,160,160,159,159,159,159,158,158,158,158,158,158,158,158,157,157,157,157,157,156,156,156,156,155,155,155,155,155,154,154,154,154,154,153,153,153,153,153,152,152,152,152,152,151,151,151,151,151,150,150,150,150,150,149,149,149,149,149,149,149,149,149,149,148,148,148,148,148,148,147,147,147,147,147,147,146,146,146,146,146,145,145,145,145,145,145,144,144,144,144,144,144,143,143,143,143,143,143,142,142,142,142,142,142,141,141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140,140,140,140,139,139,139,139,139,139,139,138, 0,
+ ACC_CURVE,CRV_SMEARING ,3763,2330,1803,1515,1330,1198,1099,1021,957,904,859,819,785,755,728,704,682,662,644,626,611,597,583,571,559,548,540, 0,
+ DEC_CURVE,CRV_NORMALSCAN,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,90,90,90,90,90,90,91,91,91,91,91,92,92,92,92,93,93,93,93,94,94,94,95,95,95,95,95,96,96,97,97,98,98,99,99,100,100,101,101,102,102,103,104,104,105,106,107,108,109,110,111,112,113,113,115,117,119,121,122,124,127,130,132,135,139,142,146,149,153,158,162,167,171,176,180,186,193,202,209,216,223,232,243,254,266,279,292,306,320,335,337,351,367,380,396,414,437,464,493,520,549,583,611,644,675,711,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0,
+ DEC_CURVE,CRV_PARKHOME ,138,142,146,149,153,158,162,167,171,176,180,186,193,202,209,216,223,232,243,254,266,279,292,306,320,335,337,351,367,380,396,414,437,464,493,520,549,583,611,644,675,711,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0,
+ DEC_CURVE,CRV_SMEARING ,138,139,139,139,139,139,139,139,140,140,140,140,140,140,140,140,140,140,140,140,140,141,141,141,141,141,141,141,142,142,142,142,142,142,143,143,143,143,143,143,144,144,144,144,144,144,145,145,145,145,145,145,146,146,146,146,146,147,147,147,147,147,147,148,148,148,148,148,148,149,149,149,149,149,149,149,149,149,149,150,150,150,150,150,151,151,151,151,151,152,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,155,156,156,156,156,157,157,157,157,157,158,158,158,158,158,158,158,158,159,159,159,159,160,160,160,160,160,161,161,161,161,162,162,162,162,163,163,163,163,164,164,164,165,165,165,165,166,166,166,166,167,167,167,167,167,167,167,168,168,168,168,169,169,169,170,170,170,170,171,171,171,172,172,172,173,173,173,174,174,174,174,175,175,175,176,176,176,176,176,176,177,177,177,178,178,178,179,179,180,180,180,181,181,181,182,182,182,183,183,184,184,184,185,185,185,185,185,186,186,186,187,187,188,188,188,189,189,190,190,191,191,191,192,192,193,193,194,194,194,194,194,195,195,196,196,197,197,198,198,199,199,200,200,201,201,202,202,203,203,203,203,204,204,205,205,206,207,207,208,208,209,209,210,211,211,212,212,212,213,213,214,214,215,216,216,217,218,218,219,220,220,221,221,221,222,223,224,224,225,226,227,227,228,229,230,230,230,231,232,233,233,234,235,236,237,238,239,239,239,240,241,242,243,244,245,246,247,248,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,265,266,266,267,269,270,271,273,274,275,276,277,279,280,282,283,284,285,287,289,290,292,293,294,296,298,300,301,302,304,306,308,310,311,313,315,318,320,321,323,326,328,330,332,335,337,339,342,344,347,349,352,355,358,361,364,366,370,374,376,380,383,387,391,394,399,402,407,411,416,420,425,430,436,441,446,453,458,464,472,478,485,493,501,509,518,527,537,548,559,571,583,597,611,626,644,662,682,704,728,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0,
+ DEC_CURVE,CRV_BUFFERFULL,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,95,95,95,95,95,96,96,97,97,98,98,99,99,100,100,101,101,102,102,103,104,104,105,106,107,108,109,110,111,112,113,113,115,117,119,121,122,124,127,130,132,135,139,142,146,149,153,158,162,167,171,176,180,186,193,202,209,216,223,232,243,254,266,279,292,306,320,335,337,351,367,380,396,414,437,464,493,520,549,583,611,644,675,711,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0,
+ -2,
+
+ /* motorcurve 6 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,23999, 0,
+ ACC_CURVE,CRV_PARKHOME ,5360,3362,2628,2229,1973,1791,1654,1547,1458,1384,1319,1264,1214,1170,1131,1096,1063,1034,1006,981,958,937,916,897,880,863,847,832,818,805,792,780,769,758,747,737,727,718,709,700,692, 0,
+ ACC_CURVE,CRV_SMEARING ,23999, 0,
+ DEC_CURVE,CRV_NORMALSCAN,23999, 0,
+ DEC_CURVE,CRV_PARKHOME ,692,700,709,718,727,737,747,758,769,780,792,805,818,832,847,863,880,897,916,937,958,981,1006,1034,1063,1096,1131,1170,1214,1264,1319,1384,1458,1547,1654,1791,1973,2229,2628,3362,5360, 0,
+ DEC_CURVE,CRV_SMEARING ,23999, 0,
+ DEC_CURVE,CRV_BUFFERFULL,23999, 0,
+ -2,
+
+ /* motorcurve 7 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,6667, 0,
+ ACC_CURVE,CRV_PARKHOME ,5360,3362,2628,2229,1973,1791,1654,1547,1458,1384,1319,1264,1214,1170,1131,1096,1063,1034,1006,981,958,937,916,897,880,863,847,832,818,805,792,780,769,758,747,737,727,718,709,700,692, 0,
+ ACC_CURVE,CRV_SMEARING ,6667, 0,
+ DEC_CURVE,CRV_NORMALSCAN,6667, 0,
+ DEC_CURVE,CRV_PARKHOME ,692,700,709,718,727,737,747,758,769,780,792,805,818,832,847,863,880,897,916,937,958,981,1006,1034,1063,1096,1131,1170,1214,1264,1319,1384,1458,1547,1654,1791,1973,2229,2628,3362,5360, 0,
+ DEC_CURVE,CRV_SMEARING ,6667, 0,
+ DEC_CURVE,CRV_BUFFERFULL,6667, 0,
+ -2,
+
+ /* motorcurve 8 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,1046,1046,1046,1046,1046,1046,647,501,421,370,333,305,284,266,251,239,228,218,210,202,196,190,184,179,174,170,166,162,159,155,152,149,147,144,142,139,137,135,133,131,129,127,126,124,123,121,120,118,117,116,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,100,99,98,97,96,96,95,94,94,93,92,92,91,91,90,89,89,88,88,87,87,86,86,85,85,84,84,83,83,82,82,82,81,81,80,80,79,79,79,78,78,78,77,77,76,76,76,75,75,75,74,74,74,74,73,73,73,72,72,72,71,71,71,71,70,70,70,70,69,69,69,69,68,68,68,68,67,67,67,67,66,66,66,66,65,65,65,65,64,64,64,64,63,63,63,63,62,62,62,62,61,61,61,61,61,60,60,60,60,60,60,59,59,59,59,59,59,59,58,58,58,58,58,58,57,57,57,57,57,57,57,56,56,56,56,56,56,56,56,55,55,55,55,55,55,55,55,54,54,54,54,54,54,54,54,53,53,53,53,53,53,53,53,52,52,52,52,52,52,52,52,51,51,51,51,51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,23,23, 0,
+ ACC_CURVE,CRV_PARKHOME ,1045,678,604,554,332,332,304,283,265,250,238,227,217,209,201,195,189,183,178,173,169,165,161,158,154,151,148,146,143,141,138,136,134,132,130,128,126,125,123,122,120,119,117,116,115,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,99,98,97,96,95,95,94,93,93,92,91,91,90,90,89,88,88,87,87,86,86,85,85,84,84,83,83,82,82,81,81,81,80,80,79,79,78,78,78,77,77,77,76,76,75,75,75,74,74,74,73,73,73,73,72,72,72,71,71,71,70,70,70,70,69,69,69,69,68,68,68,68,67,67,67,67,66,66,66,66,65,65,65,65,65,64,64,64,64,64,63,63,63,63,63,62,62,62,62,62,61,61,61,61,61,61,60,60,60,60,60,60,59,59,59,59,59,59,58,58,58,58,58,58,58,57,57,57,57,57,57,57,56,56,56,56,56,56,56,55,55,55,55,55,55,55,55,54,54,54,54,54,54,54,54,53,53,53,53,53,53,53,53,53,52,52,52,52,52,52,52,52,52,51,51,51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39, 0,
+ ACC_CURVE,CRV_SMEARING ,1045,678,604,554,332,332,304,283,265,250,238,227,217,209,201,195,189,183,178,173,169,165,161,158,154,151,148,146,143,141,138,136,134,132,130,128,126,125,123,122,120,119,117,116,115,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,99,98,97,96,95,95,94,93,93,92,91,91,90,90,89,88,88,87,87,86,86,85,85,84,84,83,83,82,82,81,81,81,80,80,79,79,78,78,78,77,77,77,76,76,75,75,75,74,74,74,73,73,73,73,72,72,72,71,71,71,70,70,70,70,69,69,69,69,68,68,68,68,67,67,67,67,66,66,66,66,65,65,65,65,65,64,64,64,64,64,63,63,63,63,63,62,62,62,62,62,61,61,61,61,61,61,60,60,60,60,60,60,59,59,59,59,59,59,58,58,58,58,58,58,58,57,57,57,57,57,57,57,56,56,56,56,56,56,56,55,55,55,55,55,55,55,55,54,54,54,54,54,54,54,54,53,53,53,53,53,53,53,53,53,52,52,52,52,52,52,52,52,52,51,51,51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39, 0,
+ DEC_CURVE,CRV_NORMALSCAN,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,30,30,30,30,31,31,31,31,32,32,32,32,33,33,34,34,35,35,36,37,38,38,39,40,41,42,43,45,46,47,48,50,51,53,54,57,59,61,63,65,68,71,75,78,82,86,90,94,94,98,103,106,111,116,122,130,138,145,153,163,171,180,188,198,211,219,228,239,252,267,284,306,334,370,422,502,648,1045, 0,
+ DEC_CURVE,CRV_PARKHOME ,39,40,41,42,43,45,46,47,48,50,51,53,54,57,59,61,63,65,68,71,75,78,82,86,90,94,94,98,103,106,111,116,122,130,138,145,153,163,171,180,188,198,211,219,228,239,252,267,284,306,334,370,422,502,648,1045, 0,
+ DEC_CURVE,CRV_SMEARING ,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,59,59,59,59,59,59,59,60,60,60,60,60,60,60,61,61,61,61,61,61,62,62,62,62,62,62,63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,67,68,68,68,68,69,69,69,69,70,70,70,70,71,71,71,71,72,72,72,73,73,73,73,74,74,74,75,75,75,76,76,76,77,77,77,78,78,78,79,79,79,80,80,81,81,81,82,82,83,83,84,84,84,85,85,86,86,87,87,88,88,89,90,90,91,91,92,93,93,94,94,95,96,96,97,98,99,99,100,101,102,103,104,105,105,106,107,108,109,110,112,113,114,115,116,118,119,120,122,123,125,127,128,130,132,134,136,138,140,142,145,147,150,153,156,159,163,167,171,175,180,185,190,196,203,211,219,228,239,252,267,284,306,334,370,422,502,648,1045, 0,
+ DEC_CURVE,CRV_BUFFERFULL,1045,648,502,422,370,334,306,284,267,252,239,228,219,211,198,188,180,171,163,153,145,138,130,122,116,111,106,103,98,94,94,90,86,82,78,75,71,68,65,63,61,59,57,54,53,51,50,48,47,46,45,43,42,41,40,39,38,38,37,36,35,35,34,34,33,33,32,32,32,32,31,31,31,31,30,30,30,30,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, 0,
+ -2,
+
+ /* motorcurve 9 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0,
+ ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166,166,166, 0,
+ ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,455, 0,
+ DEC_CURVE,CRV_NORMALSCAN,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136,3136,3136,3136,3136,3136, 0,
+ DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136,3136,3136,3136,3136,3136, 0,
+ DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136,3136,3136,3136,3136,3136, 0,
+ DEC_CURVE,CRV_BUFFERFULL,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,127,127,128,128,129,129,130,131,132,133,133,134,135,136,137,138,139,140,141,143,144,145,146,147,149,150,151,153,154,156,157,159,161,162,164,166,168,170,172,174,176,179,181,184,186,189,192,195,198,202,206,209,213,218,222,227,233,239,245,252,259,267,276,282,286,291,298,305,310,319,325,331,342,354,362,376,387,404,417,431,456,475,509,551,586,654,715,850,998,1664,3136,3136, 0,
+ -2,
+
+ /* motorcurve 10 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376, 0,
+ ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0,
+ ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,455, 0,
+ DEC_CURVE,CRV_NORMALSCAN,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0,
+ DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0,
+ DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0,
+ DEC_CURVE,CRV_BUFFERFULL,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0,
+ -2,
+
+ /* motorcurve 11 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0,
+ ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0,
+ ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0,
+ DEC_CURVE,CRV_NORMALSCAN,167,168,169,170,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,191,192,193,194,196,197,198,200,201,203,204,206,207,209,211,212,214,216,218,219,221,223,225,227,230,232,234,236,239,241,244,247,249,252,255,258,261,265,268,272,275,279,283,288,292,297,302,307,313,318,325,331,338,346,353,362,371,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0,
+ DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0,
+ DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0,
+ DEC_CURVE,CRV_BUFFERFULL,167,168,169,170,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,191,192,193,194,196,197,198,200,201,203,204,206,207,209,211,212,214,216,218,219,221,223,225,227,230,232,234,236,239,241,244,247,249,252,255,258,261,265,268,272,275,279,283,288,292,297,302,307,313,318,325,331,338,346,353,362,371,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0,
+ -2,
+
+ /* motorcurve 12 */
+ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */
+ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166,166,166,165,165,165,164,164,163,163,163,162,162,162,161,161,161,160,160,160,159,159,159,158,158,158,157,157,157,156,156,156,156,155,155,155,154,154,154,153,153,153,153,152,152,152,151,151,151,151,150,150,150,150,149,149,149,148,148,148,148,147,147,147,147,146,146,146,146,145,145,145,145,144,144,144,144,144,143,143,143,143,142,142,142,142,141,141,141,141,141,140,140,140,140,140,139,139,139,139,138,138,138,138,138,137,137,137,137,137,136,136,136,136,136,135,135,135,135,135,135,134,134,134,134,134,133,133,133,133,133,133,132,132,132,132,132,131,131,131,131,131,131,130,130,130,130,130,130,129,129,129,129,129,129,128,128,128,128,128,128,127,127,127,127,127,127,127,126,126,126,126,126,126,125, 0,
+ ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0,
+ ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,455, 0,
+ DEC_CURVE,CRV_NORMALSCAN,110,110,110,110,110,110,110,110,111,111,111,111,111,112,112,112,112,112,113,113,113,113,114,114,114,114,115,115,115,115,115,116,116,116,116,117,117,117,117,118,118,118,118,119,119,119,120,120,120,120,121,121,121,121,122,122,122,123,123,123,124,124,124,124,125,125,125,126,126,126,127,127,127,128,128,128,129,129,129,130,130,130,131,131,132,132,132,133,133,133,134,134,135,135,135,136,136,137,137,138,138,138,139,139,140,140,141,141,142,142,142,143,143,144,144,145,145,146,146,147,148,148,149,149,150,150,151,151,152,153,153,154,154,155,156,156,157,158,158,159,160,160,161,162,163,163,164,165,166,166,167,168,169,170,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,191,192,193,194,196,197,198,200,201,203,204,206,207,209,211,212,214,216,218,219,221,223,225,227,230,232,234,236,239,241,244,247,249,252,255,258,261,265,268,272,275,279,283,288,292,297,302,307,313,318,325,331,338,346,353,362,371,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0,
+ DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0,
+ DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0,
+ DEC_CURVE,CRV_BUFFERFULL,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,127,127,128,128,129,129,130,131,132,133,133,134,135,136,137,138,139,140,141,143,144,145,146,147,149,150,151,153,154,156,157,159,161,162,164,166,168,170,172,174,176,179,181,184,186,189,192,195,198,202,206,209,213,218,222,227,233,239,245,252,259,267,276,282,286,291,298,305,310,319,325,331,342,354,362,376,387,404,417,431,456,475,509,551,586,654,715,850,998,1664,3136, 0,
+ -1
+ };
+
+ rst = (SANE_Int *)malloc(sizeof(steps));
+ if (rst != NULL)
+ memcpy(rst, &steps, sizeof(steps));
+
+ return rst;
+}
+
+static SANE_Int *cfg_motorcurve_get()
+{
+ /* returns motor setting buffer for a device */
+
+ SANE_Int *rst = NULL;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case BQ5550:
+ rst = bq5550_motor();
+ break;
+
+ case HP3800:
+ case HPG2710:
+ rst = hp3800_motor();
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ rst = hp4370_motor();
+ break;
+
+ default:
+ rst = hp3970_motor();
+ break;
+ }
+
+ return rst;
+}
+
+/* DEPRECATED functions */
+
+static int ua4900_calibreflective(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 0; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 300; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 310; break;
+ case OFFSETODD1R: rst = 310; break;
+ case OFFSETEVEN1G: rst = 313; break;
+ case OFFSETODD1G: rst = 313; break;
+ case OFFSETEVEN1B: rst = 319; break;
+ case OFFSETODD1B: rst = 319; break;
+ case ADCOFFPREDICTR: rst = 321; break;
+ case ADCOFFPREDICTG: rst = 321; break;
+ case ADCOFFPREDICTB: rst = 321; break;
+ case ADCOFFEVEN1R_1ST: rst = 344; break;
+ case ADCOFFODD1R_1ST: rst = 344; break;
+ case ADCOFFEVEN1G_1ST: rst = 328; break;
+ case ADCOFFODD1G_1ST: rst = 328; break;
+ case ADCOFFEVEN1B_1ST: rst = 341; break;
+ case ADCOFFODD1B_1ST: rst = 341; break;
+ case PEAKR: rst = 122; break;
+ case PEAKG: rst = 122; break;
+ case PEAKB: rst = 122; break;
+ case MINR: rst = 50; break;
+ case MING: rst = 50; break;
+ case MINB: rst = 50; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 10; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 8; break;
+ case GAIN1G: rst = 8; break;
+ case GAIN1B: rst = 8; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 8; break;
+ case GAIN2G: rst = 8; break;
+ case GAIN2B: rst = 8; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 10; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 1; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 15; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp3800_calibreflective(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 0; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 300; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 310; break;
+ case OFFSETODD1R: rst = 310; break;
+ case OFFSETEVEN1G: rst = 317; break;
+ case OFFSETODD1G: rst = 317; break;
+ case OFFSETEVEN1B: rst = 293; break;
+ case OFFSETODD1B: rst = 293; break;
+ case ADCOFFPREDICTR: rst = 500; break;
+ case ADCOFFPREDICTG: rst = 500; break;
+ case ADCOFFPREDICTB: rst = 500; break;
+ case ADCOFFEVEN1R_1ST: rst = 128; break;
+ case ADCOFFODD1R_1ST: rst = 128; break;
+ case ADCOFFEVEN1G_1ST: rst = 128; break;
+ case ADCOFFODD1G_1ST: rst = 128; break;
+ case ADCOFFEVEN1B_1ST: rst = 128; break;
+ case ADCOFFODD1B_1ST: rst = 128; break;
+ case PEAKR: rst = 104; break;
+ case PEAKG: rst = 111; break;
+ case PEAKB: rst = 105; break;
+ case MINR: rst = 50; break;
+ case MING: rst = 56; break;
+ case MINB: rst = 57; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 10; break;
+ case GAINTARGETFACTOR: rst = 80; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 23; break;
+ case GAIN1G: rst = 19; break;
+ case GAIN1B: rst = 0; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -3; break;
+ case BSHADINGHEIGHT: rst = 20; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp3970_calibreflective(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 0; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 300; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 327; break;
+ case OFFSETODD1R: rst = 327; break;
+ case OFFSETEVEN1G: rst = 315; break;
+ case OFFSETODD1G: rst = 315; break;
+ case OFFSETEVEN1B: rst = 322; break;
+ case OFFSETODD1B: rst = 322; break;
+ case ADCOFFPREDICTR: rst = 322; break;
+ case ADCOFFPREDICTG: rst = 310; break;
+ case ADCOFFPREDICTB: rst = 322; break;
+ case ADCOFFEVEN1R_1ST: rst = 344; break;
+ case ADCOFFODD1R_1ST: rst = 344; break;
+ case ADCOFFEVEN1G_1ST: rst = 328; break;
+ case ADCOFFODD1G_1ST: rst = 328; break;
+ case ADCOFFEVEN1B_1ST: rst = 341; break;
+ case ADCOFFODD1B_1ST: rst = 341; break;
+ case PEAKR: rst = 82; break;
+ case PEAKG: rst = 117; break;
+ case PEAKB: rst = 116; break;
+ case MINR: rst = 37; break;
+ case MING: rst = 51; break;
+ case MINB: rst = 53; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 10; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 28; break;
+ case GAIN1G: rst = 22; break;
+ case GAIN1B: rst = 21; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 10; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp4370_calibreflective(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 0; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 300; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 305; break;
+ case OFFSETODD1R: rst = 3305; break;
+ case OFFSETEVEN1G: rst = 313; break;
+ case OFFSETODD1G: rst = 313; break;
+ case OFFSETEVEN1B: rst = 317; break;
+ case OFFSETODD1B: rst = 317; break;
+ case ADCOFFPREDICTR: rst = 500; break;
+ case ADCOFFPREDICTG: rst = 500; break;
+ case ADCOFFPREDICTB: rst = 500; break;
+ case ADCOFFEVEN1R_1ST: rst = 344; break;
+ case ADCOFFODD1R_1ST: rst = 344; break;
+ case ADCOFFEVEN1G_1ST: rst = 328; break;
+ case ADCOFFODD1G_1ST: rst = 328; break;
+ case ADCOFFEVEN1B_1ST: rst = 341; break;
+ case ADCOFFODD1B_1ST: rst = 341; break;
+ case PEAKR: rst = 159; break;
+ case PEAKG: rst = 191; break;
+ case PEAKB: rst = 191; break;
+ case MINR: rst = 146; break;
+ case MING: rst = 180; break;
+ case MINB: rst = 179; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 10; break;
+ case GAINTARGETFACTOR: rst = 80; break;
+ case CALIBPAGON: rst = 0; break;
+ case HIPAGR: rst = 3; break;
+ case HIPAGG: rst = 0; break;
+ case HIPAGB: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case LOPAGR: rst = 3; break;
+ case LOPAGG: rst = 3; break;
+ case LOPAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 10; break;
+ case GAIN1G: rst = 2; break;
+ case GAIN1B: rst = 1; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 10; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int fc_calibreflective(int option, int defvalue)
+{
+ int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900: rst = ua4900_calibreflective(option, defvalue); break;
+ case HPG2710:
+ case HP3800: rst = hp3800_calibreflective(option, defvalue); break;
+ case HPG3010:
+ case HPG3110:
+ case HP4370: rst = hp4370_calibreflective(option, defvalue); break;
+ default : rst = hp3970_calibreflective(option, defvalue); break;
+ }
+
+ return rst;
+}
+
+static int ua4900_calibtransparent(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 12100; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 2; break;
+ case BREFG: rst = 2; break;
+ case BREFB: rst = 2; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 13; break;
+ case OFFSETAVGTARGETG: rst = 13; break;
+ case OFFSETAVGTARGETB: rst = 13; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 321; break;
+ case OFFSETODD1R: rst = 321; break;
+ case OFFSETEVEN1G: rst = 321; break;
+ case OFFSETODD1G: rst = 321; break;
+ case OFFSETEVEN1B: rst = 321; break;
+ case OFFSETODD1B: rst = 321; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 177; break;
+ case PEAKG: rst = 177; break;
+ case PEAKB: rst = 177; break;
+ case MINR: rst = 136; break;
+ case MING: rst = 136; break;
+ case MINB: rst = 136; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 0; break;
+ case PAGG: rst = 0; break;
+ case PAGB: rst = 0; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 24; break;
+ case GAIN1G: rst = 21; break;
+ case GAIN1B: rst = 19; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 8; break;
+ case GAIN2G: rst = 8; break;
+ case GAIN2B: rst = 8; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = 2; break;
+ case BSHADINGHEIGHT: rst = 10; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp3800_calibtransparent(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 4155; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 2; break;
+ case BREFG: rst = 2; break;
+ case BREFB: rst = 2; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 310; break;
+ case OFFSETODD1R: rst = 310; break;
+ case OFFSETEVEN1G: rst = 299; break;
+ case OFFSETODD1G: rst = 299; break;
+ case OFFSETEVEN1B: rst = 309; break;
+ case OFFSETODD1B: rst = 309; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 67; break;
+ case PEAKG: rst = 61; break;
+ case PEAKB: rst = 57; break;
+ case MINR: rst = 10; break;
+ case MING: rst = 10; break;
+ case MINB: rst = 10; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 2; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 9; break;
+ case GAIN1G: rst = 12; break;
+ case GAIN1B: rst = 10; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -3; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp3970_calibtransparent(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 7500; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 2; break;
+ case BREFG: rst = 2; break;
+ case BREFB: rst = 2; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 323; break;
+ case OFFSETODD1R: rst = 323; break;
+ case OFFSETEVEN1G: rst = 327; break;
+ case OFFSETODD1G: rst = 327; break;
+ case OFFSETEVEN1B: rst = 327; break;
+ case OFFSETODD1B: rst = 327; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 42; break;
+ case PEAKG: rst = 55; break;
+ case PEAKB: rst = 53; break;
+ case MINR: rst = 31; break;
+ case MING: rst = 39; break;
+ case MINB: rst = 38; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 2; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 21; break;
+ case GAIN1G: rst = 14; break;
+ case GAIN1B: rst = 12; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = 1; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp4370_calibtransparent(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 6580; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 2; break;
+ case BREFG: rst = 2; break;
+ case BREFB: rst = 2; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 315; break;
+ case OFFSETODD1R: rst = 321; break;
+ case OFFSETEVEN1G: rst = 321; break;
+ case OFFSETODD1G: rst = 324; break;
+ case OFFSETEVEN1B: rst = 324; break;
+ case OFFSETODD1B: rst = 327; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 62; break;
+ case PEAKG: rst = 66; break;
+ case PEAKB: rst = 54; break;
+ case MINR: rst = 42; break;
+ case MING: rst = 45; break;
+ case MINB: rst = 45; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 80; break;
+ case CALIBPAGON: rst = 0; break;
+ case HIPAGR: rst = 3; break;
+ case HIPAGG: rst = 3; break;
+ case HIPAGB: rst = 2; break;
+ case LOPAGR: rst = 3; break;
+ case LOPAGG: rst = 3; break;
+ case LOPAGB: rst = 2; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 2; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 11; break;
+ case GAIN1G: rst = 9; break;
+ case GAIN1B: rst = 12; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hpg3110_calibtransparent(int option, int defvalue)
+{
+ int rst = defvalue;
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 5100; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 2; break;
+ case BREFG: rst = 2; break;
+ case BREFB: rst = 2; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 315; break;
+ case OFFSETODD1R: rst = 321; break;
+ case OFFSETEVEN1G: rst = 321; break;
+ case OFFSETODD1G: rst = 324; break;
+ case OFFSETEVEN1B: rst = 324; break;
+ case OFFSETODD1B: rst = 327; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 62; break;
+ case PEAKG: rst = 66; break;
+ case PEAKB: rst = 54; break;
+ case MINR: rst = 42; break;
+ case MING: rst = 45; break;
+ case MINB: rst = 45; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 80; break;
+ case CALIBPAGON: rst = 0; break;
+ case HIPAGR: rst = 3; break;
+ case HIPAGG: rst = 3; break;
+ case HIPAGB: rst = 2; break;
+ case LOPAGR: rst = 3; break;
+ case LOPAGG: rst = 3; break;
+ case LOPAGB: rst = 2; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 2; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 11; break;
+ case GAIN1G: rst = 9; break;
+ case GAIN1B: rst = 12; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int fc_calibtransparent(int option, int defvalue)
+{
+ int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900: rst = ua4900_calibtransparent(option, defvalue); break;
+ case HPG2710:
+ case HP3800: rst = hp3800_calibtransparent(option, defvalue); break;
+ case HPG3010:
+ case HP4370: rst = hp4370_calibtransparent(option, defvalue); break;
+ case HPG3110: rst = hpg3110_calibtransparent(option, defvalue); break;
+ default : rst = hp3970_calibtransparent(option, defvalue); break;
+ }
+
+ return rst;
+}
+
+static int ua4900_calibnegative(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 12100; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 2; break;
+ case BREFG: rst = 2; break;
+ case BREFB: rst = 2; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 5; break;
+ case OFFSETAVGTARGETG: rst = 5; break;
+ case OFFSETAVGTARGETB: rst = 5; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 283; break;
+ case OFFSETODD1R: rst = 283; break;
+ case OFFSETEVEN1G: rst = 279; break;
+ case OFFSETODD1G: rst = 279; break;
+ case OFFSETEVEN1B: rst = 295; break;
+ case OFFSETODD1B: rst = 295; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 113; break;
+ case PEAKG: rst = 145; break;
+ case PEAKB: rst = 126; break;
+ case MINR: rst = 80; break;
+ case MING: rst = 105; break;
+ case MINB: rst = 96; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 10; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 0; break;
+ case PAGG: rst = 0; break;
+ case PAGB: rst = 0; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 8; break;
+ case GAIN1G: rst = 8; break;
+ case GAIN1B: rst = 8; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 8; break;
+ case GAIN2G: rst = 8; break;
+ case GAIN2B: rst = 8; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = 2; break;
+ case BSHADINGHEIGHT: rst = 10; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp3800_calibnegative(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 4155; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 315; break;
+ case OFFSETODD1R: rst = 315; break;
+ case OFFSETEVEN1G: rst = 307; break;
+ case OFFSETODD1G: rst = 304; break;
+ case OFFSETEVEN1B: rst = 309; break;
+ case OFFSETODD1B: rst = 309; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 37; break;
+ case PEAKG: rst = 234; break;
+ case PEAKB: rst = 202; break;
+ case MINR: rst = 32; break;
+ case MING: rst = 195; break;
+ case MINB: rst = 166; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 30; break;
+ case GAIN1G: rst = 3; break;
+ case GAIN1B: rst = 0; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp3970_calibnegative(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 7500; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 294; break;
+ case OFFSETODD1R: rst = 294; break;
+ case OFFSETEVEN1G: rst = 276; break;
+ case OFFSETODD1G: rst = 276; break;
+ case OFFSETEVEN1B: rst = 266; break;
+ case OFFSETODD1B: rst = 266; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 33; break;
+ case PEAKG: rst = 75; break;
+ case PEAKB: rst = 105; break;
+ case MINR: rst = 19; break;
+ case MING: rst = 34; break;
+ case MINB: rst = 42; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 90; break;
+ case CALIBPAGON: rst = 0; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 23; break;
+ case GAIN1G: rst = 18; break;
+ case GAIN1B: rst = 23; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = 1; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hpg3110_calibnegative(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 5100; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 308; break;
+ case OFFSETODD1R: rst = 308; break;
+ case OFFSETEVEN1G: rst = 317; break;
+ case OFFSETODD1G: rst = 317; break;
+ case OFFSETEVEN1B: rst = 319; break;
+ case OFFSETODD1B: rst = 319; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 116; break;
+ case PEAKG: rst = 126; break;
+ case PEAKB: rst = 102; break;
+ case MINR: rst = 103; break;
+ case MING: rst = 112; break;
+ case MINB: rst = 80; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 80; break;
+ case CALIBPAGON: rst = 0; break;
+ case HIPAGR: rst = 3; break;
+ case HIPAGG: rst = 3; break;
+ case HIPAGB: rst = 3; break;
+ case LOPAGR: rst = 3; break;
+ case LOPAGG: rst = 3; break;
+ case LOPAGB: rst = 3; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 6; break;
+ case GAIN1G: rst = 1; break;
+ case GAIN1B: rst = 7; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int hp4370_calibnegative(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(option)
+ {
+ case WSTRIPXPOS: rst = 0; break;
+ case WSTRIPYPOS: rst = 6580; break;
+ case BSTRIPXPOS: rst = 0; break;
+ case BSTRIPYPOS: rst = 0; break;
+ case BREFR: rst = 10; break;
+ case BREFG: rst = 10; break;
+ case BREFB: rst = 10; break;
+ case REFBITDEPTH: rst = 8; break;
+ case OFFSETHEIGHT: rst = 10; break;
+ case OFFSETNSIGMA: rst = 2; break;
+ case OFFSETTARGETMAX: rst = 50; break;
+ case OFFSETTARGETMIN: rst = 2; break;
+ case OFFSETAVGTARGETR: rst = 10; break;
+ case OFFSETAVGTARGETG: rst = 10; break;
+ case OFFSETAVGTARGETB: rst = 10; break;
+ case ADCOFFEVENODD: rst = 1; break;
+ case CALIBOFFSET1ON: rst = 2; break;
+ case ADCOFFQUICKWAY: rst = 1; break;
+ case ADCOFFPREDICTSTART: rst = 200; break;
+ case ADCOFFPREDICTEND: rst = 500; break;
+ case OFFSETTUNESTEP1: rst = 5; break;
+ case OFFSETBOUNDARYRATIO1: rst = 100; break;
+ case OFFSETAVGRATIO1: rst = 100; break;
+ case OFFSETEVEN1R: rst = 308; break;
+ case OFFSETODD1R: rst = 308; break;
+ case OFFSETEVEN1G: rst = 317; break;
+ case OFFSETODD1G: rst = 317; break;
+ case OFFSETEVEN1B: rst = 319; break;
+ case OFFSETODD1B: rst = 319; break;
+ case ADCOFFPREDICTR: rst = 333; break;
+ case ADCOFFPREDICTG: rst = 313; break;
+ case ADCOFFPREDICTB: rst = 317; break;
+ case ADCOFFEVEN1R_1ST: rst = 69; break;
+ case ADCOFFODD1R_1ST: rst = 69; break;
+ case ADCOFFEVEN1G_1ST: rst = 87; break;
+ case ADCOFFODD1G_1ST: rst = 87; break;
+ case ADCOFFEVEN1B_1ST: rst = 106; break;
+ case ADCOFFODD1B_1ST: rst = 106; break;
+ case PEAKR: rst = 116; break;
+ case PEAKG: rst = 126; break;
+ case PEAKB: rst = 102; break;
+ case MINR: rst = 103; break;
+ case MING: rst = 112; break;
+ case MINB: rst = 80; break;
+ case CALIBOFFSET2ON: rst = 0; break;
+ case OFFSETTUNESTEP2: rst = 1; break;
+ case OFFSETBOUNDARYRATIO2: rst = 100; break;
+ case OFFSETAVGRATIO2: rst = 100; break;
+ case OFFSETEVEN2R: rst = 0; break;
+ case OFFSETODD2R: rst = 0; break;
+ case OFFSETEVEN2G: rst = 0; break;
+ case OFFSETODD2G: rst = 0; break;
+ case OFFSETEVEN2B: rst = 0; break;
+ case OFFSETODD2B: rst = 0; break;
+ case GAINHEIGHT: rst = 30; break;
+ case GAINTARGETFACTOR: rst = 80; break;
+ case CALIBPAGON: rst = 0; break;
+ case HIPAGR: rst = 3; break;
+ case HIPAGG: rst = 3; break;
+ case HIPAGB: rst = 3; break;
+ case LOPAGR: rst = 3; break;
+ case LOPAGG: rst = 3; break;
+ case LOPAGB: rst = 3; break;
+ case PAGR: rst = 3; break;
+ case PAGG: rst = 3; break;
+ case PAGB: rst = 3; break;
+ case CALIBGAIN1ON: rst = 1; break;
+ case GAIN1R: rst = 6; break;
+ case GAIN1G: rst = 1; break;
+ case GAIN1B: rst = 7; break;
+ case CALIBGAIN2ON: rst = 0; break;
+ case GAIN2R: rst = 4; break;
+ case GAIN2G: rst = 4; break;
+ case GAIN2B: rst = 4; break;
+ case TOTSHADING: rst = 0; break;
+ case BSHADINGON: rst = -2; break;
+ case BSHADINGHEIGHT: rst = 30; break;
+ case BSHADINGPREDIFFR: rst = 2; break;
+ case BSHADINGPREDIFFG: rst = 2; break;
+ case BSHADINGPREDIFFB: rst = 2; break;
+ case BSHADINGDEFCUTOFF: rst = 0; break;
+ case WSHADINGON: rst = 3; break;
+ case WSHADINGHEIGHT: rst = 24; break;
+ case WSHADINGPREDIFFR: rst = -1; break;
+ case WSHADINGPREDIFFG: rst = -1; break;
+ case WSHADINGPREDIFFB: rst = -1; break;
+ }
+
+ return rst;
+}
+
+static int fc_calibnegative(int option, int defvalue)
+{
+ int rst;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900: rst = ua4900_calibnegative(option, defvalue); break;
+ case HPG2710:
+ case HP3800: rst = hp3800_calibnegative(option, defvalue); break;
+ case HPG3010:
+ case HP4370: rst = hp4370_calibnegative(option, defvalue); break;
+ case HPG3110: rst = hpg3110_calibnegative(option, defvalue); break;
+ default : rst = hp3970_calibnegative(option, defvalue); break;
+ }
+
+ return rst;
+}
+
+static int fc_scaninfo_get(int option, int defvalue)
+{
+ int value[] = {1, 0, 0, 0, 0, 100};
+ int ua4900_value[] = {1, 0xcdcdcdcd, 0xcdcdcdcd, 0xcdcdcdcd, 0xcdcdcdcd, 100};
+
+ int rst = defvalue;
+ int *myvalue = NULL;
+
+ switch(RTS_Debug->dev_model)
+ {
+ case UA4900: myvalue = ua4900_value; break;
+ default: myvalue = value; break;
+ }
+
+ switch(option)
+ {
+ case PARKHOMEAFTERCALIB: rst = myvalue[0]; break;
+ case SHADINGTIME_16BIT: rst = myvalue[1]; break;
+ case SHADOWTIME_16BIT: rst = myvalue[2]; break;
+ case SHADINGTIME_8BIT: rst = myvalue[3]; break;
+ case SHADOWTIME_8BIT: rst = myvalue[4]; break;
+ case PREVIEWDPI: rst = myvalue[5]; break;
+ }
+
+ return rst;
+}
+
+/* fitcalibrate */
+static int fitcalibrate_get(int section, int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(section)
+ {
+ case CALIBREFLECTIVE:
+ rst = fc_calibreflective(option, defvalue); break;
+ case CALIBTRANSPARENT:
+ rst = fc_calibtransparent(option, defvalue); break;
+ case CALIBNEGATIVEFILM:
+ rst = fc_calibnegative(option, defvalue); break;
+ case SCANINFO:
+ rst = fc_scaninfo_get(option, defvalue); break;
+ }
+
+ return rst;
+}
+
+static int srt_hp3800_scanparam_get(int option, int defvalue)
+{
+ int rst = defvalue;
+
+ /* t_rtinifile */
+ int value3[] = {1, 0, 0, 0, 1, 12, 0, 1, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0};
+
+ int *value = value3;
+
+ if (value != NULL)
+ switch(option)
+ {
+ case ARRANGELINE: rst = value[0]; break;
+ case COMPRESSION: rst = value[1]; break;
+ case TA_X_START: rst = value[2]; break;
+ case TA_Y_START: rst = value[3]; break;
+ case DPIGAINCONTROL600: rst = value[4]; break;
+ case CRVS: rst = value[5]; break;
+ case MLOCK: rst = value[6]; break;
+ case ENABLEWARMUP: rst = value[7]; break;
+ case NMAXTARGET: rst = value[8]; break;
+ case NMINTARGET: rst = value[9]; break;
+ case NMAXTARGETTA: rst = value[10]; break;
+ case NMINTARGETTA: rst = value[11]; break;
+ case NMAXTARGETNEG: rst = value[12]; break;
+ case NMINTARGETNEG: rst = value[13]; break;
+ case STABLEDIFF: rst = value[14]; break;
+ case DELTAPWM: rst = value[15]; break;
+ case PWMLAMPLEVEL: rst = value[16]; break;
+ case TMAPWMDUTY: rst = value[17]; break;
+ case LEFTLEADING: rst = value[18]; break;
+ }
+
+ return rst;
+}
+
+static int srt_hp3970_scanparam_get(int file, int option, int defvalue)
+{
+ int rst = defvalue;
+ /* s_rtinifile */
+ int value1[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360};
+ /* s_usb1inifile */
+ int value2[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360};
+ /* t_rtinifile */
+ int value3[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0};
+ /* t_usb1inifile */
+ int value4[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0};
+
+ int *value = NULL;
+
+ switch(file)
+ {
+ case S_RTINIFILE: value = value1; break;
+ case S_USB1INIFILE: value = value2; break;
+ case T_RTINIFILE: value = value3; break;
+ case T_USB1INIFILE: value = value4; break;
+ }
+
+ if (value != NULL)
+ switch(option)
+ {
+ case ARRANGELINE: rst = value[0]; break;
+ case COMPRESSION: rst = value[1]; break;
+ case TA_X_START: rst = value[2]; break;
+ case TA_Y_START: rst = value[3]; break;
+ case DPIGAINCONTROL600: rst = value[4]; break;
+ case CRVS: rst = value[5]; break;
+ case MLOCK: rst = value[6]; break;
+ case ENABLEWARMUP: rst = value[7]; break;
+ case NMAXTARGET: rst = value[8]; break;
+ case NMINTARGET: rst = value[9]; break;
+ case NMAXTARGETTA: rst = value[10]; break;
+ case NMINTARGETTA: rst = value[11]; break;
+ case NMAXTARGETNEG: rst = value[12]; break;
+ case NMINTARGETNEG: rst = value[13]; break;
+ case STABLEDIFF: rst = value[14]; break;
+ case DELTAPWM: rst = value[15]; break;
+ case PWMLAMPLEVEL: rst = value[16]; break;
+ case TMAPWMDUTY: rst = value[17]; break;
+ case LEFTLEADING: rst = value[18]; break;
+ }
+
+ return rst;
+}
+
+static int srt_hp4370_scanparam_get(int file, int option, int defvalue)
+{
+ /* s_rtinifile */
+ int value1[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360};
+ /* s_usb1inifile */
+ int value2[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360};
+ /* t_rtinifile */
+ int value3[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0};
+ /* t_usb1inifile */
+ int value4[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0};
+ int *value = NULL;
+
+ int rst = defvalue;
+
+ switch(file)
+ {
+ case S_RTINIFILE: value = value1; break;
+ case S_USB1INIFILE: value = value2; break;
+ case T_RTINIFILE: value = value3; break;
+ case T_USB1INIFILE: value = value4; break;
+ }
+
+ if (value != NULL)
+ switch(option)
+ {
+ case ARRANGELINE: rst = value[0]; break;
+ case COMPRESSION: rst = value[1]; break;
+ case TA_X_START: rst = value[2]; break;
+ case TA_Y_START: rst = value[3]; break;
+ case DPIGAINCONTROL600: rst = value[4]; break;
+ case CRVS: rst = value[5]; break;
+ case MLOCK: rst = value[6]; break;
+ case ENABLEWARMUP: rst = value[7]; break;
+ case NMAXTARGET: rst = value[8]; break;
+ case NMINTARGET: rst = value[9]; break;
+ case NMAXTARGETTA: rst = value[10]; break;
+ case NMINTARGETTA: rst = value[11]; break;
+ case NMAXTARGETNEG: rst = value[12]; break;
+ case NMINTARGETNEG: rst = value[13]; break;
+ case STABLEDIFF: rst = value[14]; break;
+ case DELTAPWM: rst = value[15]; break;
+ case PWMLAMPLEVEL: rst = value[16]; break;
+ case TMAPWMDUTY: rst = value[17]; break;
+ case LEFTLEADING: rst = value[18]; break;
+ }
+
+ return rst;
+}
+
+static int srt_scancali_get(int file, int option, int defvalue)
+{
+ int rst = defvalue;
+ /* s_rtinifile */
+ int value1[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 91, 91,
+ 53, 53, 48, 48, 104, 104, 59, 59, 64,64};
+ /* s_usb1inifile */
+ int value2[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 91, 91,
+ 53, 53, 48, 48, 104, 104, 59, 59, 64, 64};
+ /* t_rtinifile*/
+ int value3[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 270, 270,
+ 511, 511, 511, 511, 270, 270, 511, 511, 511, 511};
+ /* t_usb1inifile*/
+ int value4[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 270, 270,
+ 511, 511, 511, 511, 270, 270, 511, 511, 511, 511};
+ int *value = NULL;
+
+ switch(file)
+ {
+ case S_RTINIFILE: value = value1; break;
+ case S_USB1INIFILE: value = value2; break;
+ case T_RTINIFILE: value = value3; break;
+ case T_USB1INIFILE: value = value4; break;
+ }
+
+ if (value != NULL)
+ switch(option)
+ {
+ case PGA1: rst = value[0]; break;
+ case PGA2: rst = value[1]; break;
+ case PGA3: rst = value[2]; break;
+ case VGAGAIN11: rst = value[3]; break;
+ case VGAGAIN12: rst = value[4]; break;
+ case VGAGAIN13: rst = value[5]; break;
+ case DCSTEPEVEN1: rst = value[6]; break;
+ case DCSTEPODD1: rst = value[7]; break;
+ case DCSTEPEVEN2: rst = value[8]; break;
+ case DCSTEPODD2: rst = value[9]; break;
+ case DCSTEPEVEN3: rst = value[10]; break;
+ case DCSTEPODD3: rst = value[11]; break;
+ case FIRSTDCOFFSETEVEN11: rst = value[12]; break;
+ case FIRSTDCOFFSETODD11: rst = value[13]; break;
+ case FIRSTDCOFFSETEVEN12: rst = value[14]; break;
+ case FIRSTDCOFFSETODD12: rst = value[15]; break;
+ case FIRSTDCOFFSETEVEN13: rst = value[16]; break;
+ case FIRSTDCOFFSETODD13: rst = value[17]; break;
+ case DCOFFSETEVEN11: rst = value[18]; break;
+ case DCOFFSETODD11: rst = value[19]; break;
+ case DCOFFSETEVEN12: rst = value[20]; break;
+ case DCOFFSETODD12: rst = value[21]; break;
+ case DCOFFSETEVEN13: rst = value[22]; break;
+ case DCOFFSETODD13: rst = value[23]; break;
+ }
+
+ return rst;
+}
+
+static int srt_truegrayparam_get(int file, int option, int defvalue)
+{
+ int rst = defvalue;
+ /* s_rtinifile */
+ int value1[] = {100, 30, 59, 11};
+ /* s_usb1inifile */
+ int value2[] = {100, 30, 59, 11};
+ /* t_rtinifile */
+ int value3[] = {100, 30, 59, 11};
+ /* t_usb1inifile */
+ int value4[] = {100, 30, 59, 11};
+ int *value = NULL;
+
+ switch(file)
+ {
+ case S_RTINIFILE: value = value1; break;
+ case S_USB1INIFILE: value = value2; break;
+ case T_RTINIFILE: value = value3; break;
+ case T_USB1INIFILE: value = value4; break;
+ }
+
+ if (value != NULL)
+ switch(option)
+ {
+ case SHADINGBASE: rst = value[0]; break;
+ case SHADINGFACT1: rst = value[1]; break;
+ case SHADINGFACT2: rst = value[2]; break;
+ case SHADINGFACT3: rst = value[3]; break;
+ }
+
+ return rst;
+}
+
+static int srt_caliparam_get(int file, int option, int defvalue)
+{
+ int rst = defvalue;
+ /* s_rtinifile */
+ int value1[] = {0xffff};
+ /* s_usb1inifile */
+ int value2[] = {0xffff};
+ /* t_rtinifile */
+ int value3[] = {0xffff};
+ /* t_usb1inifile */
+ int value4[] = {0xffff};
+ int *value = NULL;
+
+ switch(file)
+ {
+ case S_RTINIFILE: value = value1; break;
+ case S_USB1INIFILE: value = value2; break;
+ case T_RTINIFILE: value = value3; break;
+ case T_USB1INIFILE: value = value4; break;
+ }
+
+ if (value != NULL)
+ switch(option)
+ {
+ case PIXELDARKLEVEL: rst = value[0]; break;
+ }
+
+ return rst;
+}
+
+static int srt_hp3800_platform_get(int option, int defvalue)
+{
+ /* s_rtinifile*/
+ int value1[] = {100, 99, 1214636};
+
+ int *value = value1;
+ int rst = defvalue;
+
+ if (value != NULL)
+ {
+ switch(option)
+ {
+ case BINARYTHRESHOLDH: rst = value[0]; break;
+ case BINARYTHRESHOLDL: rst = value[1]; break;
+ case CLOSETIME: rst = value[2]; break;
+ }
+ }
+
+ return rst;
+}
+
+static int srt_hp3970_platform_get(int option, int defvalue)
+{
+ /* s_rtinifile*/
+ int value1[] = {128, 127, 1214636};
+
+ int *value = value1;
+ int rst = defvalue;
+
+ if (value != NULL)
+ {
+ switch(option)
+ {
+ case BINARYTHRESHOLDH: rst = value[0]; break;
+ case BINARYTHRESHOLDL: rst = value[1]; break;
+ case CLOSETIME: rst = value[2]; break;
+ }
+ }
+
+ return rst;
+}
+
+
+static int srt_ua4900_platform_get(int option, int defvalue)
+{
+ int value1[] = {128, 127, 1214636};
+ int *value = value1;
+ int rst = defvalue;
+
+ if (value != NULL)
+ {
+ switch(option)
+ {
+ case BINARYTHRESHOLDH: rst = value[0]; break;
+ case BINARYTHRESHOLDL: rst = value[1]; break;
+ case CLOSETIME: rst = value[2]; break;
+ }
+ }
+
+ return rst;
+}
+
+static int srt_hp4370_platform_get(int option, int defvalue)
+{
+ /* t_rtinifile */
+ int value3[] = {128, 127, 1214636};
+
+ int *value = value3;
+ int rst = defvalue;
+
+ if (value != NULL)
+ {
+ switch(option)
+ {
+ case BINARYTHRESHOLDH: rst = value[0]; break;
+ case BINARYTHRESHOLDL: rst = value[1]; break;
+ case CLOSETIME: rst = value[2]; break;
+ }
+ }
+
+ return rst;
+}
+
+static int srt_scaninfo_get(int file, int option, int defvalue)
+{
+ int rst = defvalue;
+ int value1[] = {0, 0, 0, 0};
+ int value2[] = {0, 0, 0, 0};
+ int value3[] = {0, 0, 0, 0};
+ int value4[] = {0, 0, 0, 0};
+ int *value = NULL;
+
+ switch(file)
+ {
+ case S_RTINIFILE: value = value1; break;
+ case S_USB1INIFILE: value = value2; break;
+ case T_RTINIFILE: value = value3; break;
+ case T_USB1INIFILE: value = value4; break;
+ }
+
+ if (value != NULL)
+ {
+ switch(option)
+ {
+ case SHADINGTIME_16BIT: rst = value[0]; break;
+ case SHADOWTIME_16BIT: rst = value[1]; break;
+ case SHADINGTIME_8BIT: rst = value[2]; break;
+ case SHADOWTIME_8BIT: rst = value[3]; break;
+ }
+ }
+
+ return rst;
+}
+
+static int srt_sec_get(int file, int section, int option, int defvalue)
+{
+ int rst = defvalue;
+
+ switch(section)
+ {
+ case SCAN_PARAM:
+ switch(RTS_Debug->dev_model)
+ {
+ case HPG2710:
+ case HP3800: rst = srt_hp3800_scanparam_get(option, defvalue); break;
+ case HPG3010:
+ case HPG3110:
+ case HP4370: rst = srt_hp4370_scanparam_get(file, option, defvalue); break;
+ default : rst = srt_hp3970_scanparam_get(file, option, defvalue); break;
+ }
+ break;
+ case SCAN_CALI:
+ rst = srt_scancali_get(file, option, defvalue); break;
+ case TRUE_GRAY_PARAM:
+ rst = srt_truegrayparam_get(file, option, defvalue); break;
+ case CALI_PARAM:
+ rst = srt_caliparam_get(file, option, defvalue); break;
+ case PLATFORM:
+ switch(RTS_Debug->dev_model)
+ {
+ case HPG2710:
+ case HP3800: rst = srt_hp3800_platform_get(option, defvalue); break;
+ case UA4900: rst = srt_ua4900_platform_get(option, defvalue); break;
+ case HPG3010:
+ case HPG3110:
+ case HP4370: rst = srt_hp4370_platform_get(option, defvalue); break;
+ default : rst = srt_hp3970_platform_get(option, defvalue); break;
+ }
+ break;
+ case SCANINFO:
+ rst = srt_scaninfo_get(file, option, defvalue); break;
+ }
+
+ return rst;
+}
+
+static int get_value(int section, int option, int defvalue, int file)
+{
+ int rst = defvalue;
+
+ switch(file)
+ {
+ case FITCALIBRATE:
+ rst = fitcalibrate_get(section, option, defvalue); break;
+ case S_RTINIFILE:
+ case S_USB1INIFILE:
+ case T_RTINIFILE:
+ case T_USB1INIFILE:
+ rst = srt_sec_get(file, section, option, defvalue); break;
+ }
+
+ return rst;
+}
diff --git a/backend/hp3900_debug.c b/backend/hp3900_debug.c
new file mode 100644
index 0000000..18f6136
--- /dev/null
+++ b/backend/hp3900_debug.c
@@ -0,0 +1,1581 @@
+/* HP Scanjet 3900 series - Debugging functions for standalone
+
+ Copyright (C) 2005-2008 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* debugging level messages */
+#define DBG_ERR 0x00 /* Only important errors */
+#define DBG_VRB 0x01 /* verbose messages */
+#define DBG_FNC 0x02 /* Function names and parameters */
+#define DBG_CTL 0x03 /* USB Ctl data */
+#define DBG_BLK 0x04 /* USB Bulk data */
+
+#include <stdarg.h>
+#ifdef HAVE_TIFFIO_H
+#include <tiffio.h> /* dbg_tiff_save */
+#endif
+
+/* headers */
+
+static void dump_shading (struct st_calibration *myCalib);
+static char *dbg_scantype (SANE_Int type);
+static void dbg_scanmodes (struct st_device *dev);
+static void dbg_motorcurves (struct st_device *dev);
+static void dbg_motormoves (struct st_device *dev);
+static void dbg_hwdcfg (struct st_hwdconfig *params);
+static void dbg_ScanParams (struct st_scanparams *params);
+static void dbg_calibtable (struct st_gain_offset *params);
+static char *dbg_colour (SANE_Int colour);
+static void dbg_motorcfg (struct st_motorcfg *motorcfg);
+static void dbg_buttons (struct st_buttons *buttons);
+static void dbg_sensor (struct st_sensorcfg *sensor);
+static void dbg_timing (struct st_timing *mt);
+static void dbg_sensorclock (struct st_cph *cph);
+static void dbg_tiff_save (char *sFile, SANE_Int width, SANE_Int height,
+ SANE_Int depth, SANE_Int colortype, SANE_Int res_x,
+ SANE_Int res_y, SANE_Byte * buffer, SANE_Int size);
+static void dbg_autoref (struct st_scanparams *scancfg, SANE_Byte * pattern,
+ SANE_Int ser1, SANE_Int ser2, SANE_Int ler);
+
+#ifdef developing
+static void dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer,
+ SANE_Int size, SANE_Int start);
+static void dbg_registers (SANE_Byte * buffer);
+#endif
+
+#ifdef STANDALONE
+
+/* implementation */
+
+int DBG_LEVEL = 0;
+
+static void
+DBG (int level, const char *msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+
+ if (level <= DBG_LEVEL)
+ vfprintf (stderr, msg, ap);
+
+ va_end (ap);
+}
+
+#endif
+
+/* debugging functions */
+
+static void
+dump_shading (struct st_calibration *myCalib)
+{
+ if (myCalib != NULL)
+ {
+ SANE_Int colour, a;
+ FILE *shadingfile[3];
+
+ shadingfile[0] = fopen ("RShading.txt", "w");
+ shadingfile[1] = fopen ("GShading.txt", "w");
+ shadingfile[2] = fopen ("BShading.txt", "w");
+
+ for (colour = 0; colour < 3; colour++)
+ {
+ if (shadingfile[colour] != NULL)
+ {
+ for (a = 0; a < myCalib->shadinglength; a++)
+ fprintf (shadingfile[colour], "%04i: %04x %04x\n", a,
+ (unsigned int) myCalib->white_shading[colour][a],
+ (unsigned int) myCalib->black_shading[colour][a]);
+ fclose (shadingfile[colour]);
+ }
+ }
+ }
+}
+
+static char *
+dbg_scantype (SANE_Int type)
+{
+ switch (type)
+ {
+ case ST_NORMAL:
+ return "ST_NORMAL";
+ break;
+ case ST_TA:
+ return "ST_TA";
+ break;
+ case ST_NEG:
+ return "ST_NEG";
+ break;
+ default:
+ return "Unknown";
+ break;
+ }
+}
+
+static void
+dbg_sensorclock (struct st_cph *cph)
+{
+ if (cph != NULL)
+ {
+ DBG (DBG_FNC, " -> cph->p1 = %f\n", cph->p1);
+ DBG (DBG_FNC, " -> cph->p2 = %f\n", cph->p2);
+ DBG (DBG_FNC, " -> cph->ps = %i\n", cph->ps);
+ DBG (DBG_FNC, " -> cph->ge = %i\n", cph->ge);
+ DBG (DBG_FNC, " -> cph->go = %i\n", cph->go);
+ }
+ else
+ DBG (DBG_FNC, " -> cph is NULL\n");
+}
+
+static void
+dbg_timing (struct st_timing *mt)
+{
+ if (mt != NULL)
+ {
+ DBG (DBG_FNC, " -> mt->cdss[0] = %i\n", _B0 (mt->cdss[0]));
+ DBG (DBG_FNC, " -> mt->cdsc[0] = %i\n", _B0 (mt->cdsc[0]));
+ DBG (DBG_FNC, " -> mt->cdss[1] = %i\n", _B0 (mt->cdss[1]));
+ DBG (DBG_FNC, " -> mt->cdsc[1] = %i\n", _B0 (mt->cdsc[1]));
+ DBG (DBG_FNC, " -> mt->cnpp = %i\n", _B0 (mt->cnpp));
+ DBG (DBG_FNC, " -> mt->cvtrp0 = %i\n", _B0 (mt->cvtrp[0]));
+ DBG (DBG_FNC, " -> mt->cvtrp1 = %i\n", _B0 (mt->cvtrp[1]));
+ DBG (DBG_FNC, " -> mt->cvtrp2 = %i\n", _B0 (mt->cvtrp[2]));
+ DBG (DBG_FNC, " -> mt->cvtrfpw = %i\n", _B0 (mt->cvtrfpw));
+ DBG (DBG_FNC, " -> mt->cvtrbpw = %i\n", _B0 (mt->cvtrbpw));
+ DBG (DBG_FNC, " -> mt->cvtrw = %i\n", _B0 (mt->cvtrw));
+ DBG (DBG_FNC, " -> mt->clamps = 0x%08x\n", mt->clamps);
+ DBG (DBG_FNC, " -> mt->clampe = 0x%08x\n", mt->clampe);
+ DBG (DBG_FNC, " -> mt->adcclkp0 = %f\n", mt->adcclkp[0]);
+ DBG (DBG_FNC, " -> mt->adcclkp1 = %f\n", mt->adcclkp[1]);
+ DBG (DBG_FNC, " -> mt->adcclkp2e = %i\n", mt->adcclkp2e);
+ DBG (DBG_FNC, " -> mt->cphbp2s = %i\n", mt->cphbp2s);
+ DBG (DBG_FNC, " -> mt->cphbp2e = %i\n", mt->cphbp2e);
+ }
+ else
+ DBG (DBG_FNC, " -> mt is NULL\n");
+}
+
+static void
+dbg_sensor (struct st_sensorcfg *sensor)
+{
+ if (sensor != NULL)
+ {
+ DBG (DBG_FNC,
+ " -> type, name, res , {chn_color }, {chn_gray}, {rgb_order }, line_dist, evnodd_dist\n");
+ DBG (DBG_FNC,
+ " -> ----, ----, --- , {--, --, --}, {--, -- }, {--, --, --}, ---------, -----------\n");
+ DBG (DBG_FNC,
+ " -> %4i, %4i, %4i, {%2i, %2i, %2i}, {%2i, %2i }, {%2i, %2i, %2i}, %9i, %11i\n",
+ sensor->type, sensor->name, sensor->resolution,
+ sensor->channel_color[0], sensor->channel_color[1],
+ sensor->channel_color[2], sensor->channel_gray[0],
+ sensor->channel_gray[1], sensor->rgb_order[0],
+ sensor->rgb_order[1], sensor->rgb_order[2], sensor->line_distance,
+ sensor->evenodd_distance);
+ }
+ else
+ DBG (DBG_FNC, " -> sensor is NULL\n");
+}
+
+static void
+dbg_buttons (struct st_buttons *buttons)
+{
+ if (buttons != NULL)
+ {
+ DBG (DBG_FNC, " -> count, btn1, btn2, btn3, btn4, btn5, btn6\n");
+ DBG (DBG_FNC, " -> -----, ----, ----, ----, ----, ----, ----\n");
+ DBG (DBG_FNC, " -> %5i, %4i, %4i, %4i, %4i, %4i, %4i\n",
+ buttons->count, buttons->mask[0], buttons->mask[1],
+ buttons->mask[2], buttons->mask[3], buttons->mask[4],
+ buttons->mask[5]);
+ }
+ else
+ DBG (DBG_FNC, " -> buttons is NULL\n");
+}
+
+static void
+dbg_scanmodes (struct st_device *dev)
+{
+ if (dev->scanmodes_count > 0)
+ {
+ SANE_Int a;
+ struct st_scanmode *reg;
+
+ DBG (DBG_FNC,
+ " -> ##, ST , CM , RES , TM, CV, SR, CLK, CTPC , BKS , STT, DML, { Exposure times }, { Max exposure times }, MP , MExp16, MExpF, MExp, MRI, MSI, MMTIR, MMTIRH, SK\n");
+ DBG (DBG_FNC,
+ " -> --, ---------, ----------, --- , --, --, --, ---, ------, ----, ---, ---, {------ ------ ------}, {------ ------ ------}, ---, ------, -----, ----, ---, ---, -----, ------, --\n");
+ for (a = 0; a < dev->scanmodes_count; a++)
+ {
+ reg = dev->scanmodes[a];
+ if (reg != NULL)
+ {
+ DBG (DBG_FNC,
+ " -> %2i, %9s, %10s, %4i, %2i, %2i, %2i, %3i, %6i, %4i, %3i, %3i, {%6i, %6i, %6i}, {%6i, %6i, %6i}, %3i, %6i, %5i, %4i, %3i, %3i, %5i, %6i, %2i\n",
+ a, dbg_scantype (reg->scantype),
+ dbg_colour (reg->colormode), reg->resolution, reg->timing,
+ reg->motorcurve, reg->samplerate, reg->systemclock,
+ reg->ctpc, reg->motorbackstep, reg->scanmotorsteptype,
+ reg->dummyline, reg->expt[0], reg->expt[1], reg->expt[2],
+ reg->mexpt[0], reg->mexpt[1], reg->mexpt[2],
+ reg->motorplus, reg->multiexposurefor16bitmode,
+ reg->multiexposureforfullspeed, reg->multiexposure,
+ reg->mri, reg->msi, reg->mmtir, reg->mmtirh,
+ reg->skiplinecount);
+ }
+ }
+ }
+}
+
+static void
+dbg_motorcurves (struct st_device *dev)
+{
+ if (dev->mtrsetting != NULL)
+ {
+ struct st_motorcurve *mtc;
+ SANE_Int a = 0;
+
+ while (a < dev->mtrsetting_count)
+ {
+ DBG (DBG_FNC, " -> Motorcurve %2i: ", a);
+ mtc = dev->mtrsetting[a];
+ if (mtc != NULL)
+ {
+ DBG (DBG_FNC, "mri=%i msi=%i skip=%i bckstp=%i\n", mtc->mri,
+ mtc->msi, mtc->skiplinecount, mtc->motorbackstep);
+ if (mtc->curve_count > 0)
+ {
+ char *sdata = (char *) malloc (256);
+ if (sdata != NULL)
+ {
+ char *sline = (char *) malloc (256);
+ if (sline != NULL)
+ {
+ SANE_Int count;
+ struct st_curve *crv;
+
+ DBG (DBG_FNC,
+ " -> ##, dir, type , count, from, to , steps\n");
+ DBG (DBG_FNC,
+ " -> --, ---, ----------, -----, ----, ----, -----\n");
+
+ count = 0;
+ while (count < mtc->curve_count)
+ {
+ memset (sline, 0, 256);
+
+ snprintf (sdata, 256, " -> %02i, ", count);
+ strcat (sline, sdata);
+
+ crv = mtc->curve[count];
+ if (crv != NULL)
+ {
+ if (crv->crv_speed == ACC_CURVE)
+ strcat (sline, "ACC, ");
+ else
+ strcat (sline, "DEC, ");
+
+ switch (crv->crv_type)
+ {
+ case CRV_NORMALSCAN:
+ strcat (sline, "NORMALSCAN, ");
+ break;
+ case CRV_PARKHOME:
+ strcat (sline, "PARKHOME , ");
+ break;
+ case CRV_SMEARING:
+ strcat (sline, "SMEARING , ");
+ break;
+ case CRV_BUFFERFULL:
+ strcat (sline, "BUFFERFULL, ");
+ break;
+ default:
+ snprintf (sdata, 256, "unknown %2i, ",
+ crv->crv_type);
+ strcat (sline, sdata);
+ break;
+ }
+
+ snprintf (sdata, 256, "%5i, ",
+ crv->step_count);
+ strcat (sline, sdata);
+ if (crv->step_count > 0)
+ {
+ SANE_Int stpcount = 0;
+
+ snprintf (sdata, 256, "%4i, %4i| ",
+ crv->step[0],
+ crv->step[crv->step_count -
+ 1]);
+ strcat (sline, sdata);
+
+ while (stpcount < crv->step_count)
+ {
+ if (stpcount == 10)
+ {
+ strcat (sline, "...");
+ break;
+ }
+ if (stpcount > 0)
+ strcat (sline, ", ");
+
+ snprintf (sdata, 256, "%4i",
+ crv->step[stpcount]);
+ strcat (sline, sdata);
+
+ stpcount++;
+ }
+ strcat (sline, "\n");
+ }
+ else
+ strcat (sline, "NONE\n");
+ }
+ else
+ strcat (sline, "NULL ...\n");
+
+ DBG (DBG_FNC, "%s", sline);
+
+ count++;
+ }
+
+ free (sline);
+ }
+ free (sdata);
+ }
+ }
+ }
+ else
+ DBG (DBG_FNC, "NULL\n");
+ a++;
+ }
+ }
+}
+
+static void
+dbg_motormoves (struct st_device *dev)
+{
+ if (dev->motormove_count > 0)
+ {
+ SANE_Int a;
+ struct st_motormove *reg;
+
+ DBG (DBG_FNC, " -> ##, CLK, CTPC, STT, CV\n");
+ DBG (DBG_FNC, " -> --, ---, ----, ---, --\n");
+ for (a = 0; a < dev->motormove_count; a++)
+ {
+ reg = dev->motormove[a];
+ if (reg != NULL)
+ {
+ DBG (DBG_FNC, " -> %2i, %3i, %4i, %3i, %2i\n",
+ a, reg->systemclock, reg->ctpc,
+ reg->scanmotorsteptype, reg->motorcurve);
+ }
+ }
+ }
+}
+
+static void
+dbg_hwdcfg (struct st_hwdconfig *params)
+{
+ if (params != NULL)
+ {
+ DBG (DBG_FNC, " -> Low level config:\n");
+ DBG (DBG_FNC, " -> startpos = %i\n", params->startpos);
+ DBG (DBG_FNC, " -> arrangeline = %s\n",
+ (params->arrangeline ==
+ FIX_BY_SOFT) ? "FIX_BY_SOFT" : (params->arrangeline ==
+ FIX_BY_HARD) ? "FIX_BY_HARD" :
+ "FIX_BY_NONE");
+ DBG (DBG_FNC, " -> scantype = %s\n",
+ dbg_scantype (params->scantype));
+ DBG (DBG_FNC, " -> compression = %i\n", params->compression);
+ DBG (DBG_FNC, " -> use_gamma_tables = %i\n",
+ params->use_gamma_tables);
+ DBG (DBG_FNC, " -> gamma_tablesize = %i\n",
+ params->gamma_tablesize);
+ DBG (DBG_FNC, " -> white_shading = %i\n",
+ params->white_shading);
+ DBG (DBG_FNC, " -> black_shading = %i\n",
+ params->black_shading);
+ DBG (DBG_FNC, " -> unk3 = %i\n", params->unk3);
+ DBG (DBG_FNC, " -> motorplus = %i\n", params->motorplus);
+ DBG (DBG_FNC, " -> static_head = %i\n", params->static_head);
+ DBG (DBG_FNC, " -> motor_direction = %s\n",
+ (params->motor_direction == MTR_FORWARD) ? "FORWARD" : "BACKWARD");
+ DBG (DBG_FNC, " -> dummy_scan = %i\n", params->dummy_scan);
+ DBG (DBG_FNC, " -> highresolution = %i\n",
+ params->highresolution);
+ DBG (DBG_FNC, " -> sensorevenodddistance = %i\n",
+ params->sensorevenodddistance);
+ DBG (DBG_FNC, " -> calibrate = %i\n", params->calibrate);
+ }
+}
+
+static void
+dbg_ScanParams (struct st_scanparams *params)
+{
+ if (params != NULL)
+ {
+ DBG (DBG_FNC, " -> Scan params:\n");
+ DBG (DBG_FNC, " -> colormode = %s\n",
+ dbg_colour (params->colormode));
+ DBG (DBG_FNC, " -> depth = %i\n", params->depth);
+ DBG (DBG_FNC, " -> samplerate = %i\n", params->samplerate);
+ DBG (DBG_FNC, " -> timing = %i\n", params->timing);
+ DBG (DBG_FNC, " -> channel = %i\n", params->channel);
+ DBG (DBG_FNC, " -> sensorresolution = %i\n", params->sensorresolution);
+ DBG (DBG_FNC, " -> resolution_x = %i\n", params->resolution_x);
+ DBG (DBG_FNC, " -> resolution_y = %i\n", params->resolution_y);
+ DBG (DBG_FNC, " -> left = %i\n", params->coord.left);
+ DBG (DBG_FNC, " -> width = %i\n", params->coord.width);
+ DBG (DBG_FNC, " -> top = %i\n", params->coord.top);
+ DBG (DBG_FNC, " -> height = %i\n", params->coord.height);
+ DBG (DBG_FNC, " -> shadinglength = %i\n", params->shadinglength);
+ DBG (DBG_FNC, " -> v157c = %i\n", params->v157c);
+ DBG (DBG_FNC, " -> bytesperline = %i\n", params->bytesperline);
+ DBG (DBG_FNC, " -> expt = %i\n", params->expt);
+ DBG (DBG_FNC, " *> startpos = %i\n", params->startpos);
+ DBG (DBG_FNC, " *> leftleading = %i\n", params->leftleading);
+ DBG (DBG_FNC, " *> ser = %i\n", params->ser);
+ DBG (DBG_FNC, " *> ler = %i\n", params->ler);
+ DBG (DBG_FNC, " *> scantype = %s\n",
+ dbg_scantype (params->scantype));
+ }
+}
+
+static void
+dbg_calibtable (struct st_gain_offset *params)
+{
+ if (params != NULL)
+ {
+ DBG (DBG_FNC, " -> Calib table:\n");
+ DBG (DBG_FNC, " -> type R G B\n");
+ DBG (DBG_FNC, " -> ----- --- --- ---B\n");
+ DBG (DBG_FNC, " -> edcg1 = %3i , %3i , %3i\n", params->edcg1[0],
+ params->edcg1[1], params->edcg1[2]);
+ DBG (DBG_FNC, " -> edcg2 = %3i , %3i , %3i\n", params->edcg2[0],
+ params->edcg2[1], params->edcg2[2]);
+ DBG (DBG_FNC, " -> odcg1 = %3i , %3i , %3i\n", params->odcg1[0],
+ params->odcg1[1], params->odcg1[2]);
+ DBG (DBG_FNC, " -> odcg2 = %3i , %3i , %3i\n", params->odcg2[0],
+ params->odcg2[1], params->odcg2[2]);
+ DBG (DBG_FNC, " -> pag = %3i , %3i , %3i\n", params->pag[0],
+ params->pag[1], params->pag[2]);
+ DBG (DBG_FNC, " -> vgag1 = %3i , %3i , %3i\n", params->vgag1[0],
+ params->vgag1[1], params->vgag1[2]);
+ DBG (DBG_FNC, " -> vgag2 = %3i , %3i , %3i\n", params->vgag2[0],
+ params->vgag2[1], params->vgag2[2]);
+ }
+}
+
+static char *
+dbg_colour (SANE_Int colour)
+{
+ switch (colour)
+ {
+ case CM_COLOR:
+ return "CM_COLOR";
+ break;
+ case CM_GRAY:
+ return "CM_GRAY";
+ break;
+ case CM_LINEART:
+ return "CM_LINEART";
+ break;
+ default:
+ return "Unknown";
+ break;
+ }
+}
+
+static void
+dbg_motorcfg (struct st_motorcfg *motorcfg)
+{
+ if (motorcfg != NULL)
+ {
+ DBG (DBG_FNC,
+ " -> type, res , freq, speed, base, high, park, change\n");
+ DBG (DBG_FNC,
+ " -> ----, --- , ----, -----, ----, ----, ----, ------\n");
+ DBG (DBG_FNC, " -> %4i, %4i, %4i, %5i, %4i, %4i, %4i, %6i\n",
+ motorcfg->type, motorcfg->resolution, motorcfg->pwmfrequency,
+ motorcfg->basespeedpps, motorcfg->basespeedmotormove,
+ motorcfg->highspeedmotormove, motorcfg->parkhomemotormove,
+ motorcfg->changemotorcurrent);
+ }
+}
+
+static void
+dbg_tiff_save (char *sFile, SANE_Int width, SANE_Int height, SANE_Int depth,
+ SANE_Int colortype, SANE_Int res_x, SANE_Int res_y,
+ SANE_Byte * buffer, SANE_Int size)
+{
+#ifdef HAVE_TIFFIO_H
+ if (buffer != NULL)
+ {
+ char *path = getenv ("HOME");
+
+ if (path != NULL)
+ {
+ char filename[512];
+ TIFF *image;
+
+ if (snprintf (filename, 512, "%s/%s", path, sFile) > 0)
+ {
+ /* Open the TIFF file */
+ if ((image = TIFFOpen (filename, "w")) != NULL)
+ {
+ char desc[256];
+
+ SANE_Int spp = (colortype == CM_GRAY) ? 1 : 3;
+ SANE_Int ct =
+ (colortype ==
+ CM_GRAY) ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB;
+
+ snprintf (desc, 256, "Created with hp3900 %s",
+ BACKEND_VRSN);
+
+ /* We need to set some values for basic tags before we can add any data */
+ TIFFSetField (image, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField (image, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField (image, TIFFTAG_BITSPERSAMPLE, depth);
+ TIFFSetField (image, TIFFTAG_SAMPLESPERPIXEL, spp);
+
+ TIFFSetField (image, TIFFTAG_PHOTOMETRIC, ct);
+ TIFFSetField (image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField (image, TIFFTAG_PLANARCONFIG,
+ PLANARCONFIG_CONTIG);
+
+ TIFFSetField (image, TIFFTAG_XRESOLUTION, (double) res_x);
+ TIFFSetField (image, TIFFTAG_YRESOLUTION, (double) res_y);
+ TIFFSetField (image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+ TIFFSetField (image, TIFFTAG_IMAGEDESCRIPTION, desc);
+
+ /* Write the information to the file */
+ TIFFWriteRawStrip (image, 0, buffer, size);
+ TIFFClose (image);
+ }
+ }
+ else
+ DBG (DBG_ERR, "- dbg_tiff_save: Error generating filename\n");
+ }
+ else
+ DBG (DBG_ERR,
+ "- dbg_tiff_save: Enviroment HOME variable does not exist\n");
+ }
+#else
+ /* silent gcc */
+ sFile = sFile;
+ width = width;
+ height = height;
+ depth = depth;
+ colortype = colortype;
+ res_x = res_x;
+ res_y = res_y;
+ buffer = buffer;
+ size = size;
+
+ DBG (DBG_ERR, "- dbg_tiff_save: tiffio not supported\n");
+#endif
+}
+
+static void
+dbg_autoref (struct st_scanparams *scancfg, SANE_Byte * pattern,
+ SANE_Int ser1, SANE_Int ser2, SANE_Int ler)
+{
+ /* this function generates post-autoref.tiff */
+ SANE_Byte *img =
+ malloc (sizeof (SANE_Byte) *
+ (scancfg->coord.width * scancfg->coord.height * 3));
+
+ if (img != NULL)
+ {
+ SANE_Int c, value;
+
+ /* generate image from 1 gray channel to 3 color channels */
+ for (c = 0; c < (scancfg->coord.width * scancfg->coord.height); c++)
+ {
+ value = *(pattern + c);
+ *(img + (3 * c)) = value;
+ *(img + (3 * c) + 1) = value;
+ *(img + (3 * c) + 2) = value;
+ }
+
+ for (c = 0; c < scancfg->coord.height; c++)
+ {
+ /* line for first SER */
+ if (c < (ler + 5))
+ {
+ *(img + (scancfg->coord.width * c * 3) + (3 * ser1)) = 0;
+ *(img + (scancfg->coord.width * c * 3) + (3 * ser1) + 1) = 255;
+ *(img + (scancfg->coord.width * c * 3) + (3 * ser1) + 2) = 0;
+ }
+
+ /* line for second SER */
+ if (c > (ler - 5))
+ {
+ *(img + (scancfg->coord.width * c * 3) + (3 * ser2)) = 90;
+ *(img + (scancfg->coord.width * c * 3) + (3 * ser2) + 1) = 90;
+ *(img + (scancfg->coord.width * c * 3) + (3 * ser2) + 2) = 255;
+ }
+
+ /* vertical lines of the pointer */
+ if ((c > (ler - 5)) && (c < (ler + 5)))
+ {
+ if ((ser2 - 5) >= 0)
+ {
+ *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 - 5))) =
+ 255;
+ *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 - 5)) +
+ 1) = 255;
+ *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 - 5)) +
+ 2) = 0;
+ }
+
+ if ((ser2 + 5) < scancfg->coord.width)
+ {
+ *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 + 5))) =
+ 255;
+ *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 + 5)) +
+ 1) = 255;
+ *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 + 5)) +
+ 2) = 0;
+ }
+ }
+ }
+
+ /* line for first LER */
+ for (c = 0; c < scancfg->coord.width; c++)
+ {
+ if ((c > (ser1 - 5)) && (c < (ser2 + 5)))
+ {
+ if (c != (ser2 - 5))
+ {
+ *(img + (scancfg->coord.width * ler * 3) + (3 * c)) = 255;
+ *(img + (scancfg->coord.width * ler * 3) + (3 * c) + 1) =
+ 90;
+ *(img + (scancfg->coord.width * ler * 3) + (3 * c) + 2) =
+ 90;
+ }
+
+ /* horizontal lines of the pointer */
+ if ((c > (ser2 - 5)) && (c < (ser2 + 5)))
+ {
+ if ((ler - 5) >= 0)
+ {
+ *(img + (scancfg->coord.width * (ler - 5) * 3) +
+ (3 * c)) = 255;
+ *(img + (scancfg->coord.width * (ler - 5) * 3) +
+ (3 * c) + 1) = 255;
+ *(img + (scancfg->coord.width * (ler - 5) * 3) +
+ (3 * c) + 2) = 0;
+ }
+
+ if ((ler + 5) < scancfg->coord.height)
+ {
+ *(img + (scancfg->coord.width * (ler + 5) * 3) +
+ (3 * c)) = 255;
+ *(img + (scancfg->coord.width * (ler + 5) * 3) +
+ (3 * c) + 1) = 255;
+ *(img + (scancfg->coord.width * (ler + 5) * 3) +
+ (3 * c) + 2) = 0;
+ }
+ }
+ }
+ }
+
+ dbg_tiff_save ("post-autoref.tiff", scancfg->coord.width,
+ scancfg->coord.height, 8, CM_COLOR,
+ scancfg->resolution_x, scancfg->resolution_y, img,
+ scancfg->coord.height * scancfg->coord.width * 3);
+
+ /* free generated image */
+ free (img);
+ }
+}
+
+#ifdef developing
+
+static void
+dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer, SANE_Int size,
+ SANE_Int start)
+{
+ if (level <= DBG_LEVEL)
+ {
+ DBG (level, "%s ", title);
+ if ((size > 0) && (buffer != NULL))
+ {
+ SANE_Int cont, data, offset = 0;
+ SANE_Int col = 0;
+ char text[9];
+ char *sline = NULL;
+ char *sdata = NULL;
+
+ sline = (char *) malloc (81);
+ if (sline != NULL)
+ {
+ sdata = (char *) malloc (81);
+ if (sdata != NULL)
+ {
+ for (cont = 0; cont < size; cont++)
+ {
+ if (col == 0)
+ {
+ if (cont == 0)
+ snprintf (sline, 80, " BF: ");
+ else
+ snprintf (sline, 80, " ");
+ bzero (&text, sizeof (text));
+ }
+ data = _B0 (buffer[cont]);
+ text[col] = (data > 31) ? data : '·';
+ snprintf (sdata, 80, "%02x ", data);
+ sline = strcat (sline, sdata);
+ col++;
+ offset++;
+ if (col == 8)
+ {
+ col = 0;
+ snprintf (sdata, 80, " : %s : 0x%04x\n", text,
+ start + offset - 8);
+ sline = strcat (sline, sdata);
+ DBG (level, "%s", sline);
+ bzero (sline, 81);
+ }
+ }
+ if (col > 0)
+ {
+ for (cont = col; cont < 8; cont++)
+ {
+ snprintf (sdata, 80, "-- ");
+ sline = strcat (sline, sdata);
+ offset++;
+ }
+ snprintf (sdata, 80, " : %s : 0x%04x\n", text,
+ start + offset - 8);
+ sline = strcat (sline, sdata);
+ DBG (level, "%s", sline);
+ bzero (sline, 81);
+ }
+ free (sdata);
+ }
+ free (sline);
+ }
+ }
+ else
+ DBG (level, " BF: Empty buffer\n");
+ }
+}
+
+static void
+dbg_registers (SANE_Byte * buffer)
+{
+ /* buffer size must be RT_BUFFER_LEN bytes */
+ /*SANE_Int iValue, iValue2;
+ double dValue;
+
+ DBG(DBG_FNC, "\n----------------------------------------------------\n");
+ DBG(DBG_FNC, """RTS8822 Control Registers Info""\nAddress Info\n------- ----\n");
+ iValue = data_lsb_get(&buffer[0x000], 1);
+ DBG(DBG_FNC, "\n0x0000");
+ DBG(DBG_FNC, " bit[0..3] = systemclock: 0x%02x\n", iValue & 0x0f);
+ DBG(DBG_FNC, " bit[4] = 0x%02x : MLOCK\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[5] = 0x%02x : Bit to reset scanner\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[6] = 0x%02x : ?\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = 0x%02x : RTS_IsExecuting\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x001], 1);
+ DBG(DBG_FNC, "0x0001 bit[0] = 0x%02x : ?\n", iValue & 1);
+ DBG(DBG_FNC, " bit[1] = 0x%02x : (is 1 if has motorcurves)\n", (iValue >> 1) & 1);
+ DBG(DBG_FNC, " bit[2] = 0x%02x : ?\n", (iValue >> 2) & 1);
+ DBG(DBG_FNC, " bit[3] = 0x%02x : ?\n", (iValue >> 3) & 1);
+ DBG(DBG_FNC, " bit[4] = 0x%02x : dummy scan\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[5..7] = 0x%02x : ?\n", (iValue >> 5) & 7);
+
+ dbg_buffer(DBG_FNC, "\n0x0002", &buffer[0x002], 0x0e, 0x02);
+
+ iValue = data_lsb_get(&buffer[0x010], 1);
+ DBG(DBG_FNC, "\n0x0010 bit[0..4] = 0x%02x : cvrs\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5] = 0x%02x : Enable CCD\n", ((iValue >> 5) & 1));
+ DBG(DBG_FNC, " bit[6] = 0x%02x : Enable CCD channel 1\n", ((iValue >> 6) & 1));
+ DBG(DBG_FNC, " bit[7] = 0x%02x : Enable CCD channel 2\n", ((iValue >> 7) & 1));
+
+ iValue = data_lsb_get(&buffer[0x011], 1);
+ DBG(DBG_FNC, "\n0x0011 bit[0..6] = ?: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[7] = 0x%02x : sensor type (CCD=0|CIS=1)\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x012], 1);
+ DBG(DBG_FNC, "0x0012 bit[0..5] = 0x%02x [0x%02x,0x%02x,0x%02x] rgb channel order\n", (iValue & 0x3f), (iValue >> 4) & 3, (iValue >> 2) & 3, iValue & 3);
+ DBG(DBG_FNC, " bit[6..7] = channels_per_dot : 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x013], 1);
+ DBG(DBG_FNC, "\n0x0013");
+ DBG(DBG_FNC, " bit[0..1] = Pre-Amplifier Gain[RED] : 0x%02x\n", iValue & 3);
+ DBG(DBG_FNC, " bit[2..3] = Pre-Amplifier Gain[GREEN] : 0x%02x\n", (iValue >> 2) & 3);
+ DBG(DBG_FNC, " bit[4..5] = Pre-Amplifier Gain[BLUE] : 0x%02x\n", (iValue >> 4) & 3);
+ DBG(DBG_FNC, " bit[6] = ? : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = Enable CCD channel 3: : 0x%02x\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x014], 1);
+ DBG(DBG_FNC, "\n0x0014");
+ DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 1 [RED] : 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = Top Reference Voltage: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x015], 1);
+ DBG(DBG_FNC, "0x0015");
+ DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 1 [GREEN] : 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = Middle Reference Voltage: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x016], 1);
+ DBG(DBG_FNC, "0x0016");
+ DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 1 [BLUE] : 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = Bottom Reference Voltage: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x017], 1);
+ DBG(DBG_FNC, "0x0017");
+ DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 2 [RED] : 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = Top Reference Voltage: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x018], 1);
+ DBG(DBG_FNC, "0x0018");
+ DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 2 [GREEN] : 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = Middle Reference Voltage: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x019], 1);
+ DBG(DBG_FNC, "0x0019");
+ DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 2 [BLUE] : 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = Bottom Reference Voltage: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x01a], 1);
+ iValue2 = data_lsb_get(&buffer[0x01b], 1);
+ DBG(DBG_FNC, "\n0x001a-0x001b\n");
+ DBG(DBG_FNC, " Red Even offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue);
+ DBG(DBG_FNC, " Red Even offset 2: 0x%02x\n", iValue2 & 0x3f);
+
+ iValue = data_lsb_get(&buffer[0x01c], 1);
+ iValue2 = data_lsb_get(&buffer[0x01d], 1);
+ DBG(DBG_FNC, "0x001c-0x001d\n");
+ DBG(DBG_FNC, " Red Odd offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue);
+ DBG(DBG_FNC, " Red Odd offset 2: 0x%02x\n", iValue2 & 0x3f);
+
+ iValue = data_lsb_get(&buffer[0x01e], 1);
+ iValue2 = data_lsb_get(&buffer[0x01f], 1);
+ DBG(DBG_FNC, "0x001e-0x001f\n");
+ DBG(DBG_FNC, " Green Even offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue);
+ DBG(DBG_FNC, " Green Even offset 2: 0x%02x\n", iValue2 & 0x3f);
+
+ iValue = data_lsb_get(&buffer[0x020], 1);
+ iValue2 = data_lsb_get(&buffer[0x021], 1);
+ DBG(DBG_FNC, "0x0020-0x0021\n");
+ DBG(DBG_FNC, " Green Odd offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue);
+ DBG(DBG_FNC, " Green Odd offset 2: 0x%02x\n", iValue2 & 0x3f);
+
+ iValue = data_lsb_get(&buffer[0x022], 1);
+ iValue2 = data_lsb_get(&buffer[0x023], 1);
+ DBG(DBG_FNC, "0x0022-0x0023\n");
+ DBG(DBG_FNC, " Blue Even offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue);
+ DBG(DBG_FNC, " Blue Even offset 2: 0x%02x\n", iValue2 & 0x3f);
+
+ iValue = data_lsb_get(&buffer[0x024], 1);
+ iValue2 = data_lsb_get(&buffer[0x025], 1);
+ DBG(DBG_FNC, "0x0024-0x0025\n");
+ DBG(DBG_FNC, " Blue Odd offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue);
+ DBG(DBG_FNC, " Blue Odd offset 2: 0x%02x\n", iValue2 & 0x3f);
+
+ dbg_buffer(DBG_FNC, "\n0x0026", &buffer[0x026], 0x03, 0x26);
+
+ iValue = data_lsb_get(&buffer[0x029], 1);
+ DBG(DBG_FNC, "\n0x0029");
+ DBG(DBG_FNC, " First connection to scanner? : 0x%02x\n", iValue);
+
+ dbg_buffer(DBG_FNC, "\n0x002a", &buffer[0x02a], 0x06, 0x2a);
+
+ DBG(DBG_FNC, "\nExposure times:\n");
+ iValue = data_lsb_get(&buffer[0x030], 3);
+ DBG(DBG_FNC, "0x0030 Line exposure time : %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x033], 3);
+ DBG(DBG_FNC, "\n0x0033 mexpts[RED] : %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x036], 3);
+ DBG(DBG_FNC, "0x0036 expts[RED] : %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x039], 3);
+ DBG(DBG_FNC, "0x0039 mexpts[GREEN]: %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x03c], 3);
+ DBG(DBG_FNC, "0x003c expts[GREEN]: %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x03f], 3);
+ DBG(DBG_FNC, "0x003f mexpts[BLUE] : %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x042], 3);
+ DBG(DBG_FNC, "0x0042 expts[BLUE] : %i us\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x045], 1);
+ DBG(DBG_FNC, "\n0x0045 bit[0..4] = timing.cvtrfpw: 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5] = timing.cvtrp[2]: 0x%02x\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[6] = timing.cvtrp[1]: 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = timing.cvtrp[0]: 0x%02x\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x046], 1);
+ DBG(DBG_FNC, "0x0046");
+ DBG(DBG_FNC, " bit[0..4] = timing.cvtrbpw: 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 3);
+
+ iValue = data_lsb_get(&buffer[0x047], 1);
+ DBG(DBG_FNC, "0x0047");
+ DBG(DBG_FNC, " timing.cvtrw: 0x%02x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x04c], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x04a], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x048], 0x02);
+ DBG(DBG_FNC, "\n0x0048 Linear image sensor clock 1\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph0p1: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x04c], 0x01);
+ DBG(DBG_FNC, " bit[36] = timing.cph0go: 0x%02x\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[37] = timing.cph0ge: 0x%02x\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[38] = timing.cph0ps: 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x051], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x04f], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x04d], 0x02);
+ DBG(DBG_FNC, "0x004d");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph0p2: %.0f.\n", dValue);
+
+ iValue = data_lsb_get(&buffer[0x056], 1) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x054], 2);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x052], 2);
+ DBG(DBG_FNC, "\n0x0052 Linear image sensor clock 2\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph1p1: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x056], 1);
+ DBG(DBG_FNC, " bit[36] = timing.cph1go: 0x%02x\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[37] = timing.cph1ge: 0x%02x\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[38] = timing.cph1ps: 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 7) & 1);
+
+
+ iValue = data_lsb_get(&buffer[0x05b], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x059], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x057], 0x02);
+ DBG(DBG_FNC, "0x0057");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph1p2: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x05b], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = %02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: %02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: %02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: %02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: %02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x060], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x05e], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x05c], 0x02);
+ DBG(DBG_FNC, "\n0x005c Linear Image Sensor Clock 3\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph2p1: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x060], 0x01);
+ DBG(DBG_FNC, " bit[36] = timing.cph2go: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = timing.cph2ge: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = timing.cph2ps: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x065], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x063], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x061], 0x02);
+ DBG(DBG_FNC, "0x0061");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph2p2: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x065], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x06a], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x068], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x066], 0x02);
+ DBG(DBG_FNC, "\n0x0066 Linear Image Sensor Clock 4\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph3p1: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x06a], 0x01);
+ DBG(DBG_FNC, " bit[36] = timing.cph3go: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = timing.cph3ge: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = timing.cph3ps: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x06f], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x06d], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x06b], 0x02);
+ DBG(DBG_FNC, "0x006b");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph3p2: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x06f], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x074], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x072], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x070], 0x02);
+ DBG(DBG_FNC, "\n0x0070 Linear Image Sensor Clock 5\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph4p1: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x074], 0x01);
+ DBG(DBG_FNC, " bit[36] = timing.cph4go: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = timing.cph4ge: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = timing.cph4ps: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x079], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x077], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x075], 0x02);
+ DBG(DBG_FNC, "0x0075");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph4p2: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x079], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x07e], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x07c], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x07a], 0x02);
+ DBG(DBG_FNC, "\n0x007a Linear Image Sensor Clock 6\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph5p1: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x07e], 0x01);
+ DBG(DBG_FNC, " bit[36] = timing.cph5go: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = timing.cph5ge: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = timing.cph5ps: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x083], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x081], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x07f], 0x02);
+ DBG(DBG_FNC, "0x007f");
+ DBG(DBG_FNC, " bit[0..35] = timing.cph5p2: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x083], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ iValue = data_lsb_get(&buffer[0x084], 3);
+ DBG(DBG_FNC, "\n0x0084");
+ DBG(DBG_FNC, " timing.cphbp2s : 0x%06x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x087], 3);
+ DBG(DBG_FNC, "0x0087");
+ DBG(DBG_FNC, " timing.cphbp2e : 0x%06x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x08a], 3);
+ DBG(DBG_FNC, "0x008a");
+ DBG(DBG_FNC, " timing.clamps : 0x%08x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x08d], 3);
+ DBG(DBG_FNC, "0x008d");
+ DBG(DBG_FNC, " timing.clampe or cphbp2e : 0x%08x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x092], 0x01);
+ DBG(DBG_FNC, "\n0x0092 Correlated-Double-Sample 1\n");
+ DBG(DBG_FNC, " bit[0..5] = timing.cdss[0]: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x093], 0x01);
+ DBG(DBG_FNC, "0x0093");
+ DBG(DBG_FNC, " bit[0..5] = timing.cdsc[0]: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x094], 0x01);
+ DBG(DBG_FNC, "\n0x0094 Correlated-Double-Sample 2\n");
+ DBG(DBG_FNC, " bit[0..5] = timing.cdss[1]: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x095], 0x01);
+ DBG(DBG_FNC, "0x0095");
+ DBG(DBG_FNC, " bit[0..5] = timing.cdsc[1]: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x096], 0x01);
+ DBG(DBG_FNC, "0x0096");
+ DBG(DBG_FNC, " bit[0..5] = timing.cnpp: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x09b], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x099], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x097], 0x02);
+ DBG(DBG_FNC, "\n0x0097 Analog to Digital Converter clock 1\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.adcclkp[0]: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x09b], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ dbg_buffer(DBG_FNC, "\n0x009c CIS sensor 1", &buffer[0x09c], 0x06, 0x9c);
+ dbg_buffer(DBG_FNC, "0x00a2 CIS sensor 2", &buffer[0x0a2], 0x06, 0xa2);
+ dbg_buffer(DBG_FNC, "0x00a8 CIS sensor 3", &buffer[0x0a8], 0x06, 0xa8);
+
+ iValue = data_lsb_get(&buffer[0x0ae], 0x01);
+ DBG(DBG_FNC, "\n0x00ae");
+ DBG(DBG_FNC, " bit[0..5] = ?: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x0af], 0x01);
+ DBG(DBG_FNC, "0x00af");
+ DBG(DBG_FNC, " bit[0..2] = ?: 0x%02x\n", iValue & 7);
+ DBG(DBG_FNC, " bit[3..7] = ?: 0x%02x\n", (iValue >> 3) & 0x1f);
+
+ iValue = data_lsb_get(&buffer[0x0b0], 2);
+ DBG(DBG_FNC, "\n0x00b0");
+ DBG(DBG_FNC, " Left : 0x%04x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0b2], 2);
+ DBG(DBG_FNC, "0x00b2");
+ DBG(DBG_FNC, " Right: 0x%04x\n", iValue);
+
+ dbg_buffer(DBG_FNC, "\n0x00b4", &buffer[0x0b4], 12, 0xb4);
+
+ iValue = data_lsb_get(&buffer[0x0c0], 0x01);
+ DBG(DBG_FNC, "\n0x00c0");
+ DBG(DBG_FNC, " bit[0..4] = resolution ratio: 0x%02x\n", iValue & 0x1f);
+ DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 7);
+
+ iValue = data_lsb_get(&buffer[0x0c5], 0x01) & 0x0f;
+ dValue = iValue * pow(2, 32);
+ iValue = data_lsb_get(&buffer[0x0c3], 0x02);
+ dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x0c1], 2);
+ DBG(DBG_FNC, "\n0x00c1 Analog to Digital Converter clock 2\n");
+ DBG(DBG_FNC, " bit[0..35] = timing.adcclkp[1]: %.0f.\n", dValue);
+ iValue = data_lsb_get(&buffer[0x0c5], 0x01);
+ DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f);
+ DBG(DBG_FNC, " bit[36] = ?: 0x%02x (equal to bit[32])\n", (iValue >> 0x04) & 0x01);
+ DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01);
+ DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01);
+ DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01);
+
+ dbg_buffer(DBG_FNC, "\n0x00c6", &buffer[0x0c6], 0x0a, 0xc6);
+
+ iValue = ((buffer[0x0d4] & 0x0f) << 0x10) + data_lsb_get(&buffer[0x0d0], 0x02);
+ DBG(DBG_FNC, "\n0x00d0");
+ DBG(DBG_FNC, " Top : 0x%04x\n", iValue);
+
+ iValue = ((buffer[0x0d4] & 0xf0) << 0x06)+ data_lsb_get(&buffer[0x0d2], 0x02);
+ DBG(DBG_FNC, "x00d2");
+ DBG(DBG_FNC, " Down: 0x%04x\n", iValue);
+
+ iValue = _B0(buffer[0x0d5]);
+ DBG(DBG_FNC, "0x00d5");
+ DBG(DBG_FNC, " ?: 0x%04x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0d6], 1);
+ DBG(DBG_FNC, "\n0x00d6");
+ DBG(DBG_FNC, " bit[0..3] = ? : 0x%02x\n", iValue & 0xf);
+ DBG(DBG_FNC, " bit[4..7] = dummyline: 0x%02x\n", (iValue >> 4) & 0xf);
+
+ iValue = data_lsb_get(&buffer[0x0d7], 0x01);
+ DBG(DBG_FNC, "\n0x00d7");
+ DBG(DBG_FNC, " bit[0..5] = motor pwm frequency: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6] = ?: 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = motor type: 0x%02x ", (iValue >> 7) & 1);
+ if (((iValue >> 7) & 1) == MT_OUTPUTSTATE)
+ DBG(DBG_FNC, ": Output state machine\n");
+ else DBG(DBG_FNC, "On-Chip PWM\n");
+
+ iValue = data_lsb_get(&buffer[0x0d8], 0x01);
+ DBG(DBG_FNC, "\n0x00d8");
+ DBG(DBG_FNC, " bit[0..5] = ?: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6] = scantype (0=Normal|1=TMA) : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = enable head movement : 0x%02x :", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x0d9], 0x01);
+ DBG(DBG_FNC, "\n0x00d9");
+ DBG(DBG_FNC, " bit[0..2] = ?: 0x%02x\n", iValue & 7);
+ DBG(DBG_FNC, " bit[3] = ?: 0x%02x\n", (iValue >> 3) & 1);
+ DBG(DBG_FNC, " bit[4..6] = motor step type: 0x%02x: ", (iValue >> 4) & 7);
+ switch((iValue >> 4) & 7)
+ {
+ case 0: DBG(DBG_FNC, "full (1)\n"); break;
+ case 1: DBG(DBG_FNC, "half (1/2)\n"); break;
+ case 2: DBG(DBG_FNC, "quart (1/4)\n"); break;
+ case 3: DBG(DBG_FNC, "(1/8)\n"); break;
+ default: DBG(DBG_FNC, "unknown\n"); break;
+ }
+ DBG(DBG_FNC, " bit[7] = Motor direction: 0x%02x = ", (iValue >> 7) & 1);
+ if (((iValue >> 7) & 1) == 0)
+ DBG(DBG_FNC, "Backward\n");
+ else DBG(DBG_FNC, "Forward\n");
+
+ iValue = data_lsb_get(&buffer[0x0dd], 0x01);
+ DBG(DBG_FNC, "\n0x00da");
+ DBG(DBG_FNC, " msi = 0x%03x\n", ((iValue & 3) << 8) + data_lsb_get(&buffer[0x0da], 1));
+
+ DBG(DBG_FNC, "0x00db");
+ DBG(DBG_FNC, " motorbackstep1 = 0x%03x\n", ((iValue & 0x0c) << 6) + data_lsb_get(&buffer[0x0db], 1));
+
+ DBG(DBG_FNC, "0x00dc");
+ DBG(DBG_FNC, " motorbackstep2 = 0x%03x\n", ((iValue & 0x30) << 4) + data_lsb_get(&buffer[0x0dc], 1));
+
+ iValue = data_lsb_get(&buffer[0x0dd], 0x01);
+ DBG(DBG_FNC, "0x00dd");
+ DBG(DBG_FNC, " bit[7] = Motor enabled?: 0x%02x = ", (iValue >> 7) & 1);
+ if (((iValue >> 7) & 1) == 0)
+ DBG(DBG_FNC, "Yes\n");
+ else DBG(DBG_FNC, "No\n");
+
+ iValue = data_lsb_get(&buffer[0x0de], 0x02);
+ DBG(DBG_FNC, "\n0x00de");
+ DBG(DBG_FNC, " bit[00..11] = ?: 0x%02x\n", iValue & 0xfff);
+ DBG(DBG_FNC, " bit[12..15] = ?: 0x%02x\n", (iValue >> 12) & 0x0f);
+
+ iValue = data_lsb_get(&buffer[0x0df], 0x01);
+ DBG(DBG_FNC, "\n0x00df");
+ DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0x0f);
+ DBG(DBG_FNC, " bit[4] = has_motorcurves?: 0x%02x\n", (iValue >> 4) & 0x01);
+ DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 7);
+
+ iValue = data_lsb_get(&buffer[0x0e0], 1);
+ DBG(DBG_FNC, "\n0x00e0 step size - 1 : 0x%02x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0e1], 3);
+ DBG(DBG_FNC, "\n0x00e1 0x%06x : last step of accurve.normalscan table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0e4], 3);
+ DBG(DBG_FNC, "0x00e4 0x%06x : last step of accurve.smearing table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0e7], 3);
+ DBG(DBG_FNC, "0x00e7 0x%06x : last step of accurve.parkhome table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0ea], 3);
+ DBG(DBG_FNC, "0x00ea 0x%06x : last step of deccurve.scanbufferfull table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0ed], 3);
+ DBG(DBG_FNC, "0x00ed 0x%06x : last step of deccurve.normalscan table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0f0], 3);
+ DBG(DBG_FNC, "0x00f0 0x%06x : last step of deccurve.smearing table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0f3], 3);
+ DBG(DBG_FNC, "0x00f3 0x%06x : last step of deccurve.parkhome table\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x0f6], 2);
+ DBG(DBG_FNC, "\n0x00f6 bit[00..13] = 0x%04x : ptr to accurve.normalscan step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x0f8], 2);
+ DBG(DBG_FNC, "0x00f8");
+ DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.scanbufferfull step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x0fa], 2);
+ DBG(DBG_FNC, "0x00fa");
+ DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to accurve.smearing step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x0fc], 2);
+ DBG(DBG_FNC, "0x00fc");
+ DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.smearing step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x0fe], 2);
+ DBG(DBG_FNC, "0x00fe");
+ DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.normalscan step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x100], 2);
+ DBG(DBG_FNC, "0x0100");
+ DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to accurve.parkhome step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x102], 2);
+ DBG(DBG_FNC, "0x0102");
+ DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.parkhome step table\n", iValue & 0x3fff);
+ DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3);
+
+ dbg_buffer(DBG_FNC, "\n0x0104 Motor resource", &buffer[0x104], 0x20, 0x104);
+
+ dbg_buffer(DBG_FNC, "\n0x0124", &buffer[0x124], 0x22, 0x124);
+
+ iValue = data_lsb_get(&buffer[0x146], 1);
+ DBG(DBG_FNC, "\n0x0146");
+ DBG(DBG_FNC, " bit[0..3] = Lamp pulse-width modulation frequency : 0x%02x\n", iValue & 0xf);
+ DBG(DBG_FNC, " bit[4] = timer enabled? : 0x%02x\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[5] = ? : 0x%02x\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[6] = lamp turned on? : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = sensor type : 0x%02x ", (iValue >> 7) & 1);
+ if (((iValue >> 7) & 1) != 0)
+ DBG(DBG_FNC, "CCD\n");
+ else DBG(DBG_FNC, "CIS\n");
+
+ iValue = data_lsb_get(&buffer[0x147], 1);
+ DBG(DBG_FNC, "\n0x0147");
+ DBG(DBG_FNC, " time to turn off lamp = 0x%02x (minutes * 2.682163611980331)\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x148], 1);
+ DBG(DBG_FNC, "\n0x0148");
+ DBG(DBG_FNC, " bit[0..5] = Lamp pulse-width modulation duty cycle : 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ? : 0x%02x\n",(iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x149], 1);
+ DBG(DBG_FNC, "\n0x0149");
+ DBG(DBG_FNC, " bit[0..5] = even_odd_distance : 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ? : 0x%02x\n",(iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x14a], 1);
+ DBG(DBG_FNC, "0x014a");
+ DBG(DBG_FNC, " bit[0..5] = sensor line distance : 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n",(iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x14b], 1);
+ DBG(DBG_FNC, "0x014b");
+ DBG(DBG_FNC, " bit[0..5] = sensor line distance + even_odd_distance: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n",(iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x14c], 1);
+ DBG(DBG_FNC, "0x014c");
+ DBG(DBG_FNC, " bit[0..5] = sensor line distance * 2: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x14d], 1);
+ DBG(DBG_FNC, "0x014d");
+ DBG(DBG_FNC, " bit[0..5] = (sensor line distance * 2) + even_odd_distance: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3);
+
+ iValue = data_lsb_get(&buffer[0x14e], 1);
+ DBG(DBG_FNC, "\n0x014e");
+ DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0xf);
+ DBG(DBG_FNC, " bit[4] = ?: 0x%02x\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 7);
+
+ dbg_buffer(DBG_FNC, "\n0x014f", &buffer[0x14f], 0x05, 0x14f);
+
+ iValue = data_lsb_get(&buffer[0x154], 1);
+ DBG(DBG_FNC, "\n0x0154");
+ DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0xf);
+ DBG(DBG_FNC, " bit[4..5] = ?: 0x%02x\n", (iValue >> 4) & 3);
+ DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 7);
+
+ iValue = data_lsb_get(&buffer[0x155], 1);
+ DBG(DBG_FNC, "\n0x0155");
+ DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0x0f);
+ DBG(DBG_FNC, " bit[4] = 0x%02x : ", (iValue >> 4) & 1);
+ if (((iValue >> 4) & 1) == 0)
+ DBG(DBG_FNC, "flb lamp\n");
+ else DBG(DBG_FNC, "tma lamp\n");
+ DBG(DBG_FNC, " bit[5..7] = ? : 0x%02x\n", (iValue >> 5) & 7);
+
+ dbg_buffer(DBG_FNC, "\n0x0156", &buffer[0x156], 0x02, 0x156);
+
+ iValue = data_lsb_get(&buffer[0x158], 1);
+ DBG(DBG_FNC, "\n0x0158");
+ DBG(DBG_FNC, " bit[0..3] = %02x : Scanner buttons ", iValue & 0x0f);
+ if ((iValue & 0x0f) == 0x0f)
+ DBG(DBG_FNC, "enabled\n");
+ else DBG(DBG_FNC, "dissabled\n");
+ DBG(DBG_FNC, " bit[4..7] = ? : 0x%02x\n", (iValue >> 4) & 0x0f);
+
+ dbg_buffer(DBG_FNC, "\n0x0159", &buffer[0x159], 11, 0x159);
+
+ iValue = data_lsb_get(&buffer[0x164], 1);
+ DBG(DBG_FNC, "\n0x0164");
+ DBG(DBG_FNC, " bit[0..6] = ?: 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1);
+
+ dbg_buffer(DBG_FNC, "\n0x0165", &buffer[0x165], 3, 0x165);
+
+ iValue = data_lsb_get(&buffer[0x168], 1);
+ DBG(DBG_FNC, "\n0x0168 Buttons status : 0x%02x\n", iValue);
+ DBG(DBG_FNC, " bit[0] = button 1 : 0x%02x\n", iValue & 1);
+ DBG(DBG_FNC, " bit[1] = button 2 : 0x%02x\n", (iValue >> 1) & 1);
+ DBG(DBG_FNC, " bit[2] = button 4 : 0x%02x\n", (iValue >> 2) & 1);
+ DBG(DBG_FNC, " bit[3] = button 3 : 0x%02x\n", (iValue >> 3) & 1);
+ DBG(DBG_FNC, " bit[4] = button ? : 0x%02x\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[5] = button ? : 0x%02x\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[6] = ? : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x169], 1);
+ DBG(DBG_FNC, "\n0x0169", iValue);
+ DBG(DBG_FNC, " bit[0] = ? : 0x%02x\n", iValue & 1);
+ DBG(DBG_FNC, " bit[1] = tma attached? : 0x%02x\n", (iValue >> 1) & 1);
+ DBG(DBG_FNC, " bit[2..7] = ? : 0x%02x\n", (iValue >> 2) & 0x3f);
+
+ iValue = data_lsb_get(&buffer[0x16a], 1);
+ DBG(DBG_FNC, "\n0x016a Buttons status 2: 0x%02x\n", iValue);
+ DBG(DBG_FNC, " bit[0] = button 1 : 0x%02x\n", iValue & 1);
+ DBG(DBG_FNC, " bit[1] = button 2 : 0x%02x\n", (iValue >> 1) & 1);
+ DBG(DBG_FNC, " bit[2] = button 4 : 0x%02x\n", (iValue >> 2) & 1);
+ DBG(DBG_FNC, " bit[3] = button 3 : 0x%02x\n", (iValue >> 3) & 1);
+ DBG(DBG_FNC, " bit[4] = button ? : 0x%02x\n", (iValue >> 4) & 1);
+ DBG(DBG_FNC, " bit[5] = button ? : 0x%02x\n", (iValue >> 5) & 1);
+ DBG(DBG_FNC, " bit[6] = ? : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1);
+
+ dbg_buffer(DBG_FNC, "\n0x016b", &buffer[0x16b], 4, 0x16b);
+
+ iValue = data_lsb_get(&buffer[0x16f], 1);
+ DBG(DBG_FNC, "\n0x016f");
+ DBG(DBG_FNC, " bit[0..5] = ? : 0x%02x\n", iValue & 0x3f);
+ DBG(DBG_FNC, " bit[6] = is lamp at home? : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = ?: %02x\n", (iValue >> 7) & 1);
+
+ dbg_buffer(DBG_FNC, "\n0x0170", &buffer[0x170], 0x17, 0x170);
+
+ iValue = data_lsb_get(&buffer[0x187], 1);
+ DBG(DBG_FNC, "\n0x0187");
+ DBG(DBG_FNC, " bit[0..3] = ? : 0x%02x\n", iValue & 0xf);
+ DBG(DBG_FNC, " bit[4..7] = mclkioc : 0x%02x\n", (iValue >> 4) & 0xf);
+
+ dbg_buffer(DBG_FNC, "\n0x0188", &buffer[0x188], 0x16, 0x188);
+
+ iValue = data_lsb_get(&buffer[0x19e], 2);
+ DBG(DBG_FNC, "\n0x019e");
+ DBG(DBG_FNC, " binary threshold low : 0x%04x\n", (iValue >> 8) + ((iValue << 8) & 0xff00));
+
+ iValue = data_lsb_get(&buffer[0x1a0], 2);
+ DBG(DBG_FNC, "\n0x01a0");
+ DBG(DBG_FNC, " binary threshold high : 0x%04x\n", (iValue >> 8) + ((iValue << 8) & 0xff00));
+
+ dbg_buffer(DBG_FNC, "\n0x01a2", &buffer[0x1a2], 0x12, 0x1a2);
+
+ iValue = data_lsb_get(&buffer[0x1b4], 2);
+ DBG(DBG_FNC, "\n0x01b4");
+ DBG(DBG_FNC, " bit[00..13] = Ptr to red gamma table (table_size * 0) : 0x%04x\n", (iValue & 0x3fff));
+ DBG(DBG_FNC, " bit[14..15] = ? : 0x%02x\n", (iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x1b6], 2);
+ DBG(DBG_FNC, "0x01b6");
+ DBG(DBG_FNC, " bit[00..13] = Ptr to green gamma table (table_size * 1) : 0x%04x\n", (iValue & 0x3fff));
+ DBG(DBG_FNC, " bit[14..15] = ? : 0x%02x\n", (iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x1b8], 2);
+ DBG(DBG_FNC, "0x01b8");
+ DBG(DBG_FNC, " bit[00..13] = Ptr to blue gamma table (table_size * 2) : 0x%04x\n", (iValue & 0x3fff));
+ DBG(DBG_FNC, " bit[14..15] = ? : 0x%02x\n", (iValue >> 14) & 3);
+
+ iValue = data_lsb_get(&buffer[0x1ba], 1);
+ DBG(DBG_FNC, "\n0x01ba");
+ DBG(DBG_FNC, " ? : 0x%02x\n", iValue);
+
+ iValue = data_lsb_get(&buffer[0x1bb], 2);
+ DBG(DBG_FNC, "0x01bb");
+ DBG(DBG_FNC, " ? : 0x%04x\n", iValue + ((data_lsb_get(&buffer[0x1bf], 1) & 1) << 16));
+
+ iValue = data_lsb_get(&buffer[0x1bd], 2);
+ DBG(DBG_FNC, "0x01bd");
+ DBG(DBG_FNC, " ? : 0x%04x\n", iValue + (((data_lsb_get(&buffer[0x1bf], 1) >> 1) & 3) << 16));
+
+ iValue = data_lsb_get(&buffer[0x1c0], 3);
+ DBG(DBG_FNC, "0x01c0");
+ DBG(DBG_FNC, " bit[0..19] = ? : 0x%06x\n", iValue & 0xfffff);
+
+ iValue = data_lsb_get(&buffer[0x1bf], 2);
+ DBG(DBG_FNC, "\n0x01bf");
+ DBG(DBG_FNC, " bit[3..4] = ? : 0x%02x\n", (iValue >> 3) & 3);
+ DBG(DBG_FNC, " bit[5..7] = ? : 0x%02x\n", (iValue >> 5) & 7);
+
+ iValue = data_lsb_get(&buffer[0x1c2], 3);
+ DBG(DBG_FNC, "\n0x01c2");
+ DBG(DBG_FNC, " bit[4..23] = ? : 0x%06x\n", ((iValue >> 8) & 0xffff) + (((iValue >> 4) & 0xf) << 16));
+
+ iValue = data_lsb_get(&buffer[0x1c5], 3);
+ DBG(DBG_FNC, "0x01c5");
+ DBG(DBG_FNC, " bit[00..19] = ? : 0x%06x\n", iValue & 0xfffff);
+ DBG(DBG_FNC, " bit[20..23] = ? : 0x%02x\n", (iValue >> 20) & 0xf);
+
+ dbg_buffer(DBG_FNC, "\n0x01c8", &buffer[0x1c8], 7, 0x1c8);
+
+ iValue = data_lsb_get(&buffer[0x1cf], 3);
+ DBG(DBG_FNC, "\n0x01cf");
+ DBG(DBG_FNC, " bit[0] = ? : 0x%02x\n", iValue & 1);
+ DBG(DBG_FNC, " bit[1] = shading base (0 = 0x4000|1= 0x2000) : 0x%02x\n", (iValue >> 1) & 1);
+ DBG(DBG_FNC, " bit[2] = white shading correction : 0x%02x\n", (iValue >> 2) & 1);
+ DBG(DBG_FNC, " bit[3] = black shading correction : 0x%02x\n", (iValue >> 3) & 1);
+ DBG(DBG_FNC, " bit[4..5] = 0x%02x : ", (iValue >> 4) & 3);
+ switch ((iValue >> 4) & 3)
+ {
+ case 0: DBG(DBG_FNC, "8 bits per channel"); break;
+ case 1: DBG(DBG_FNC, "12 bits per channel"); break;
+ case 2: DBG(DBG_FNC, "16 bits per channel"); break;
+ case 3: DBG(DBG_FNC, "lineart mode"); break;
+ }
+ DBG(DBG_FNC, "\n");
+ DBG(DBG_FNC, " bit[6] = samplerate: 0x%02x ", (iValue >> 6) & 1);
+ if (((iValue >> 6) & 1) == PIXEL_RATE)
+ DBG(DBG_FNC, "PIXEL_RATE\n");
+ else DBG(DBG_FNC, "LINE_RATE\n");
+ DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1);
+
+ iValue = data_lsb_get(&buffer[0x1d0], 1);
+ DBG(DBG_FNC, "\n0x01d0");
+ DBG(DBG_FNC, " bit[0] = 0x%02x\n", iValue & 1);
+ DBG(DBG_FNC, " bit[1] = 0x%02x\n", (iValue >> 1) & 1);
+ DBG(DBG_FNC, " bit[2..3] = gamma table size : 0x%02x ", (iValue >> 2) & 3);
+ switch ((iValue >> 2) & 3)
+ {
+ case 0: DBG(DBG_FNC, "bit[0] + 0x100") ;break;
+ case 1: DBG(DBG_FNC, "bit[0] + 0x400") ;break;
+ case 2: DBG(DBG_FNC, "bit[0] + 0x1000") ;break;
+ }
+ DBG(DBG_FNC, "\n");
+ DBG(DBG_FNC, " bit[4..5] = ? : 0x%02x\n", (iValue >> 4) & 3);
+ DBG(DBG_FNC, " bit[6] = use gamma tables? : 0x%02x\n", (iValue >> 6) & 1);
+ DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1);
+
+ dbg_buffer(DBG_FNC, "\n0x01d1", &buffer[0x1d1], 0x430, 0x1d1);
+
+ DBG(DBG_FNC, "----------------------------------------------------\n\n");
+ */
+ /*exit(0); */
+}
+#endif
diff --git a/backend/hp3900_rts8822.c b/backend/hp3900_rts8822.c
new file mode 100644
index 0000000..9c8deaa
--- /dev/null
+++ b/backend/hp3900_rts8822.c
@@ -0,0 +1,15123 @@
+/* HP Scanjet 3900 series - RTS8822 Core
+
+ Copyright (C) 2005-2013 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+
+/*
+ This code is still a bit ugly due to it's the result of applying
+ reverse engineering techniques to windows driver. So at this
+ moment what you see is exactly what windows driver does.
+ And so many global vars exist that will be erased when driver
+ is entirely debugged. There are some variables with unknown
+ purpose. So they have no meaning name in form v+address. I
+ hope to change their names when driver is debugged completely.
+*/
+
+#ifndef RTS8822_CORE
+
+#define RTS8822_CORE
+
+#define GetTickCount() (time(0) * 1000)
+#define min(A,B) (((A)<(B)) ? (A) : (B))
+#define max(A,B) (((A)>(B)) ? (A) : (B))
+#define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_))
+#define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* bzero() */
+#include <time.h> /* clock() */
+#include <math.h> /* truncf() */
+#include <ctype.h> /* tolower() */
+#include <unistd.h> /* usleep() */
+#include <sys/types.h>
+
+#include "hp3900_types.c"
+#include "hp3900_debug.c"
+#include "hp3900_config.c"
+#include "hp3900_usb.c"
+
+/*-------------------- Exported function headers --------------------*/
+
+#ifdef developing
+static SANE_Int hp4370_prueba (struct st_device *dev);
+static void prueba (SANE_Byte * a);
+void shadingtest1 (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib);
+static SANE_Int Calib_test (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib,
+ struct st_scanparams *scancfg);
+static SANE_Int Calib_BlackShading_jkd (struct st_device *dev,
+ SANE_Byte * Regs,
+ struct st_calibration *myCalib,
+ struct st_scanparams *scancfg);
+#endif
+
+/*static void show_diff(struct st_device *dev, SANE_Byte *original);*/
+
+/* functions to allocate and free space for a device */
+static struct st_device *RTS_Alloc (void);
+static void RTS_Free (struct st_device *dev);
+
+/* Scanner level commands */
+static SANE_Int RTS_Scanner_Init (struct st_device *dev);
+static SANE_Int RTS_Scanner_SetParams (struct st_device *dev,
+ struct params *param);
+static SANE_Int RTS_Scanner_StartScan (struct st_device *dev);
+static void RTS_Scanner_StopScan (struct st_device *dev, SANE_Int wait);
+static void RTS_Scanner_End (struct st_device *dev);
+
+/* loading configuration functions */
+static SANE_Int Load_Buttons (struct st_device *dev);
+static SANE_Int Load_Chipset (struct st_device *dev);
+static SANE_Int Load_Config (struct st_device *dev);
+static SANE_Int Load_Constrains (struct st_device *dev);
+static SANE_Int Load_Motor (struct st_device *dev);
+static SANE_Int Load_MotorCurves (struct st_device *dev);
+static SANE_Int Load_Motormoves (struct st_device *dev);
+static SANE_Int Load_Scanmodes (struct st_device *dev);
+static SANE_Int Load_Sensor (struct st_device *dev);
+static SANE_Int Load_Timings (struct st_device *dev);
+
+/* freeing configuration functions */
+static void Free_Buttons (struct st_device *dev);
+static void Free_Chipset (struct st_device *dev);
+static void Free_Config (struct st_device *dev);
+static void Free_Constrains (struct st_device *dev);
+static void Free_Motor (struct st_device *dev);
+static void Free_MotorCurves (struct st_device *dev);
+static void Free_Motormoves (struct st_device *dev);
+static void Free_Scanmodes (struct st_device *dev);
+static void Free_Sensor (struct st_device *dev);
+static void Free_Timings (struct st_device *dev);
+static void Free_Vars (void);
+
+/* Functions to manage data */
+static SANE_Byte data_bitget (SANE_Byte * address, SANE_Int mask);
+static void data_bitset (SANE_Byte * address, SANE_Int mask, SANE_Byte data);
+static SANE_Int data_lsb_get (SANE_Byte * address, SANE_Int size);
+static void data_lsb_set (SANE_Byte * address, SANE_Int data, SANE_Int size);
+static void data_msb_set (SANE_Byte * address, SANE_Int data, SANE_Int size);
+static void data_wide_bitset (SANE_Byte * address, SANE_Int mask,
+ SANE_Int data);
+static SANE_Int data_swap_endianess (SANE_Int address, SANE_Int size);
+
+static SANE_Int Device_get (SANE_Int product, SANE_Int vendor);
+
+/* Chipset related commands */
+static SANE_Int Chipset_ID (struct st_device *dev);
+static SANE_Int Chipset_Name (struct st_device *dev, char *name,
+ SANE_Int size);
+static SANE_Int Chipset_Reset (struct st_device *dev);
+
+/* Initializing functions */
+static SANE_Int Init_Registers (struct st_device *dev);
+static SANE_Int Init_USBData (struct st_device *dev);
+static SANE_Int Init_Vars (void);
+
+/* scanmode functions */
+static SANE_Int Scanmode_fitres (struct st_device *dev, SANE_Int scantype,
+ SANE_Int colormode, SANE_Int resolution);
+static SANE_Int Scanmode_maxres (struct st_device *dev, SANE_Int scantype,
+ SANE_Int colormode);
+static SANE_Int Scanmode_minres (struct st_device *dev, SANE_Int scantype,
+ SANE_Int colormode);
+
+/* Chipset management useful commands*/
+static SANE_Int RTS_USBType (struct st_device *dev);
+static SANE_Byte RTS_Sensor_Type (USB_Handle usb_handle);
+static void RTS_DebugInit (void);
+static SANE_Int RTS_Enable_CCD (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int channels);
+
+/* DMA management commands */
+static SANE_Int RTS_DMA_Cancel (struct st_device *dev);
+static SANE_Int RTS_DMA_CheckType (struct st_device *dev, SANE_Byte * Regs);
+static SANE_Int RTS_DMA_Enable_Read (struct st_device *dev, SANE_Int dmacs,
+ SANE_Int size, SANE_Int options);
+static SANE_Int RTS_DMA_Enable_Write (struct st_device *dev, SANE_Int dmacs,
+ SANE_Int size, SANE_Int options);
+static SANE_Int RTS_DMA_Read (struct st_device *dev, SANE_Int dmacs,
+ SANE_Int options, SANE_Int size,
+ SANE_Byte * buffer);
+static SANE_Int RTS_DMA_Reset (struct st_device *dev);
+static SANE_Int RTS_DMA_SetType (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Byte ramtype);
+static SANE_Int RTS_DMA_WaitReady (struct st_device *dev, SANE_Int msecs);
+static SANE_Int RTS_DMA_Write (struct st_device *dev, SANE_Int dmacs,
+ SANE_Int options, SANE_Int size,
+ SANE_Byte * buffer);
+
+/* EEPROM management commands */
+static SANE_Int RTS_EEPROM_ReadByte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * data);
+static SANE_Int RTS_EEPROM_ReadInteger (USB_Handle usb_handle,
+ SANE_Int address, SANE_Int * data);
+static SANE_Int RTS_EEPROM_ReadWord (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int * data);
+static SANE_Int RTS_EEPROM_WriteBuffer (USB_Handle usb_handle,
+ SANE_Int address, SANE_Byte * data,
+ SANE_Int size);
+static SANE_Int RTS_EEPROM_WriteByte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte data);
+static SANE_Int RTS_EEPROM_WriteInteger (USB_Handle usb_handle,
+ SANE_Int address, SANE_Int data);
+static SANE_Int RTS_EEPROM_WriteWord (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int data);
+
+static SANE_Int RTS_Execute (struct st_device *dev);
+static SANE_Int RTS_Warm_Reset (struct st_device *dev);
+static SANE_Byte RTS_IsExecuting (struct st_device *dev, SANE_Byte * Regs);
+
+static SANE_Int RTS_GetScanmode (struct st_device *dev, SANE_Int scantype,
+ SANE_Int colormode, SANE_Int resolution);
+static SANE_Int RTS_GetImage (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_gain_offset *gain_offset,
+ SANE_Byte * buffer,
+ struct st_calibration *myCalib,
+ SANE_Int options, SANE_Int gainmode);
+static SANE_Int RTS_GetImage_GetBuffer (struct st_device *dev, double dSize,
+ SANE_Byte * buffer,
+ double *transferred);
+static SANE_Int RTS_GetImage_Read (struct st_device *dev, SANE_Byte * buffer,
+ struct st_scanparams *myvar,
+ struct st_hwdconfig *hwdcfg);
+
+static SANE_Int RTS_isTmaAttached (struct st_device *dev);
+
+/* functions to wait for a process tp finish */
+static SANE_Int RTS_WaitInitEnd (struct st_device *dev, SANE_Int msecs);
+static SANE_Int RTS_WaitScanEnd (struct st_device *dev, SANE_Int msecs);
+
+/* functions to read/write control registers */
+static SANE_Int RTS_ReadRegs (USB_Handle usb_handle, SANE_Byte * buffer);
+static SANE_Int RTS_WriteRegs (USB_Handle usb_handle, SANE_Byte * buffer);
+
+/* functions to manage the scan counter */
+static SANE_Int RTS_ScanCounter_Inc (struct st_device *dev);
+static SANE_Int RTS_ScanCounter_Get (struct st_device *dev);
+
+/* functions to setup control registers */
+static SANE_Int RTS_Setup (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *myvar,
+ struct st_hwdconfig *hwdcfg,
+ struct st_gain_offset *gain_offset);
+static void RTS_Setup_Arrangeline (struct st_device *dev,
+ struct st_hwdconfig *hwdcfg,
+ SANE_Int colormode);
+static void RTS_Setup_Channels (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ SANE_Int mycolormode);
+static void RTS_Setup_Coords (SANE_Byte * Regs, SANE_Int iLeft, SANE_Int iTop,
+ SANE_Int width, SANE_Int height);
+static SANE_Int RTS_Setup_Depth (SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ SANE_Int mycolormode);
+static void RTS_Setup_Exposure_Times (SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_scanmode *sm);
+static void RTS_Setup_GainOffset (SANE_Byte * Regs,
+ struct st_gain_offset *gain_offset);
+static void RTS_Setup_Gamma (SANE_Byte * Regs, struct st_hwdconfig *lowcfg);
+static SANE_Int RTS_Setup_Line_Distances (struct st_device *dev,
+ SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_hwdconfig *hwdcfg,
+ SANE_Int mycolormode,
+ SANE_Int arrangeline);
+static SANE_Int RTS_Setup_Motor (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *myvar,
+ SANE_Int somevalue);
+static void RTS_Setup_RefVoltages (struct st_device *dev, SANE_Byte * Regs);
+static void RTS_Setup_SensorTiming (struct st_device *dev, SANE_Int mytiming,
+ SANE_Byte * Regs);
+static void RTS_Setup_Shading (SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_hwdconfig *hwdcfg,
+ SANE_Int bytes_per_line);
+
+static SANE_Int Scan_Start (struct st_device *dev);
+
+static void SetLock (USB_Handle usb_handle, SANE_Byte * Regs,
+ SANE_Byte Enable);
+static SANE_Int fn3330 (struct st_device *dev, SANE_Byte * Regs,
+ struct st_cal2 *calbuffers,
+ SANE_Int sensorchannelcolor, SANE_Int * tablepos,
+ SANE_Int data);
+static SANE_Int fn3560 (USHORT * table, struct st_cal2 *calbuffers,
+ SANE_Int * tablepos);
+static SANE_Int fn3730 (struct st_device *dev, struct st_cal2 *calbuffers,
+ SANE_Byte * Regs, USHORT * table,
+ SANE_Int sensorchannelcolor, SANE_Int data);
+
+static SANE_Int Reading_CreateBuffers (struct st_device *dev);
+static SANE_Int Reading_DestroyBuffers (struct st_device *dev);
+static SANE_Int Reading_BufferSize_Get (struct st_device *dev,
+ SANE_Byte channels_per_dot,
+ SANE_Int channel_size);
+static SANE_Int Reading_BufferSize_Notify (struct st_device *dev,
+ SANE_Int data, SANE_Int size);
+static SANE_Int Reading_Wait (struct st_device *dev,
+ SANE_Byte Channels_per_dot,
+ SANE_Byte Channel_size, SANE_Int size,
+ SANE_Int * last_amount, SANE_Int seconds,
+ SANE_Byte op);
+
+static SANE_Int Read_Image (struct st_device *dev, SANE_Int buffer_size,
+ SANE_Byte * buffer, SANE_Int * transferred);
+static SANE_Int Read_ResizeBlock (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size,
+ SANE_Int * transferred);
+static SANE_Int Read_Block (struct st_device *dev, SANE_Int buffer_size,
+ SANE_Byte * buffer, SANE_Int * transferred);
+static SANE_Int Read_NonColor_Block (struct st_device *dev,
+ SANE_Byte * buffer, SANE_Int buffer_size,
+ SANE_Byte ColorMode,
+ SANE_Int * transferred);
+
+/* Ref functions */
+static SANE_Int Refs_Analyze_Pattern (struct st_scanparams *scancfg,
+ SANE_Byte * scanned_pattern,
+ SANE_Int * ler1, SANE_Int ler1order,
+ SANE_Int * ser1, SANE_Int ser1order);
+static SANE_Int Refs_Counter_Inc (struct st_device *dev);
+static SANE_Byte Refs_Counter_Load (struct st_device *dev);
+static SANE_Int Refs_Counter_Save (struct st_device *dev, SANE_Byte data);
+static SANE_Int Refs_Detect (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int resolution_x, SANE_Int resolution_y,
+ SANE_Int * x, SANE_Int * y);
+static SANE_Int Refs_Load (struct st_device *dev, SANE_Int * x, SANE_Int * y);
+static SANE_Int Refs_Save (struct st_device *dev, SANE_Int left_leading,
+ SANE_Int start_pos);
+static SANE_Int Refs_Set (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *myscan);
+
+/* Coordinates' constrains functions */
+static SANE_Int Constrains_Check (struct st_device *dev, SANE_Int Resolution,
+ SANE_Int scantype,
+ struct st_coords *mycoords);
+static struct st_coords *Constrains_Get (struct st_device *dev,
+ SANE_Byte scantype);
+
+/* Gain and offset functions */
+static SANE_Int GainOffset_Clear (struct st_device *dev);
+static SANE_Int GainOffset_Get (struct st_device *dev);
+static SANE_Int GainOffset_Save (struct st_device *dev, SANE_Int * offset,
+ SANE_Byte * gain);
+static SANE_Int GainOffset_Counter_Inc (struct st_device *dev,
+ SANE_Int * arg1);
+static SANE_Byte GainOffset_Counter_Load (struct st_device *dev);
+static SANE_Int GainOffset_Counter_Save (struct st_device *dev,
+ SANE_Byte data);
+
+/* Gamma functions*/
+static SANE_Int Gamma_AllocTable (SANE_Byte * table);
+static SANE_Int Gamma_Apply (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_hwdconfig *hwdcfg,
+ struct st_gammatables *mygamma);
+static void Gamma_FreeTables (void);
+static SANE_Int Gamma_SendTables (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Byte * gammatable, SANE_Int size);
+static SANE_Int Gamma_GetTables (struct st_device *dev,
+ SANE_Byte * Gamma_buffer);
+
+/* Lamp functions */
+static SANE_Byte Lamp_GetGainMode (struct st_device *dev, SANE_Int resolution,
+ SANE_Byte scantype);
+static void Lamp_SetGainMode (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int resolution, SANE_Byte gainmode);
+static SANE_Int Lamp_PWM_DutyCycle_Get (struct st_device *dev,
+ SANE_Int * data);
+static SANE_Int Lamp_PWM_DutyCycle_Set (struct st_device *dev,
+ SANE_Int duty_cycle);
+static SANE_Int Lamp_PWM_Setup (struct st_device *dev, SANE_Int lamp);
+static SANE_Int Lamp_PWM_use (struct st_device *dev, SANE_Int enable);
+static SANE_Int Lamp_PWM_CheckStable (struct st_device *dev,
+ SANE_Int resolution, SANE_Int lamp);
+static SANE_Int Lamp_PWM_Save (struct st_device *dev, SANE_Int fixedpwm);
+static SANE_Int Lamp_PWM_SaveStatus (struct st_device *dev, SANE_Byte status);
+static SANE_Int Lamp_Status_Get (struct st_device *dev, SANE_Byte * flb_lamp,
+ SANE_Byte * tma_lamp);
+static SANE_Int Lamp_Status_Set (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int turn_on, SANE_Int lamp);
+static SANE_Int Lamp_Status_Timer_Set (struct st_device *dev,
+ SANE_Int minutes);
+static SANE_Int Lamp_Warmup (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int lamp, SANE_Int resolution);
+
+/* Head related functions */
+static SANE_Int Head_IsAtHome (struct st_device *dev, SANE_Byte * Regs);
+static SANE_Int Head_ParkHome (struct st_device *dev, SANE_Int bWait,
+ SANE_Int movement);
+static SANE_Int Head_Relocate (struct st_device *dev, SANE_Int speed,
+ SANE_Int direction, SANE_Int ypos);
+
+/* Motor functions */
+static SANE_Byte *Motor_AddStep (SANE_Byte * steps, SANE_Int * bwriten,
+ SANE_Int step);
+static SANE_Int Motor_Change (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Byte value);
+static SANE_Int Motor_GetFromResolution (SANE_Int resolution);
+static SANE_Int Motor_Move (struct st_device *dev, SANE_Byte * Regs,
+ struct st_motormove *mymotor,
+ struct st_motorpos *mtrpos);
+static void Motor_Release (struct st_device *dev);
+static SANE_Int Motor_Setup_Steps (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int mysetting);
+static SANE_Int Motor_Curve_Equal (struct st_device *dev,
+ SANE_Int motorsetting, SANE_Int direction,
+ SANE_Int curve1, SANE_Int curve2);
+static void Motor_Curve_Free (struct st_motorcurve **motorcurves,
+ SANE_Int * mtc_count);
+static struct st_curve *Motor_Curve_Get (struct st_device *dev,
+ SANE_Int motorcurve,
+ SANE_Int direction, SANE_Int itype);
+static struct st_motorcurve **Motor_Curve_Parse (SANE_Int * mtc_count,
+ SANE_Int * buffer);
+
+/* Functions to arrange scanned lines */
+static SANE_Int Arrange_Colour (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size, SANE_Int * transferred);
+static SANE_Int Arrange_Compose (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size,
+ SANE_Int * transferred);
+static SANE_Int Arrange_NonColour (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size,
+ SANE_Int * transferred);
+
+/* Composing RGB triplet functions */
+static void Triplet_Gray (SANE_Byte * pPointer1, SANE_Byte * pPointer2,
+ SANE_Byte * buffer, SANE_Int channels_count);
+static void Triplet_Lineart (SANE_Byte * pPointer1, SANE_Byte * pPointer2,
+ SANE_Byte * buffer, SANE_Int channels_count);
+static void Triplet_Compose_Order (struct st_device *dev, SANE_Byte * pRed,
+ SANE_Byte * pGreen, SANE_Byte * pBlue,
+ SANE_Byte * buffer, SANE_Int dots);
+static void Triplet_Compose_HRes (SANE_Byte * pPointer1,
+ SANE_Byte * pPointer2,
+ SANE_Byte * pPointer3,
+ SANE_Byte * pPointer4,
+ SANE_Byte * pPointer5,
+ SANE_Byte * pPointer6, SANE_Byte * buffer,
+ SANE_Int Width);
+static void Triplet_Compose_LRes (SANE_Byte * pRed, SANE_Byte * pGreen,
+ SANE_Byte * pBlue, SANE_Byte * buffer,
+ SANE_Int dots);
+static void Triplet_Colour_Order (struct st_device *dev, SANE_Byte * pRed,
+ SANE_Byte * pGreen, SANE_Byte * pBlue,
+ SANE_Byte * buffer, SANE_Int Width);
+static void Triplet_Colour_HRes (SANE_Byte * pRed1, SANE_Byte * pGreen1,
+ SANE_Byte * pBlue1, SANE_Byte * pRed2,
+ SANE_Byte * pGreen2, SANE_Byte * pBlue2,
+ SANE_Byte * buffer, SANE_Int Width);
+static void Triplet_Colour_LRes (SANE_Int Width, SANE_Byte * Buffer,
+ SANE_Byte * pChannel1, SANE_Byte * pChannel2,
+ SANE_Byte * pChannel3);
+
+/* Timing functions */
+static SANE_Int Timing_SetLinearImageSensorClock (SANE_Byte * Regs,
+ struct st_cph *cph);
+
+/* Functions used to resize retrieved image */
+static SANE_Int Resize_Start (struct st_device *dev, SANE_Int * transferred);
+static SANE_Int Resize_CreateBuffers (struct st_device *dev, SANE_Int size1,
+ SANE_Int size2, SANE_Int size3);
+static SANE_Int Resize_DestroyBuffers (struct st_device *dev);
+static SANE_Int Resize_Increase (SANE_Byte * to_buffer,
+ SANE_Int to_resolution, SANE_Int to_width,
+ SANE_Byte * from_buffer,
+ SANE_Int from_resolution,
+ SANE_Int from_width, SANE_Int myresize_mode);
+static SANE_Int Resize_Decrease (SANE_Byte * to_buffer,
+ SANE_Int to_resolution, SANE_Int to_width,
+ SANE_Byte * from_buffer,
+ SANE_Int from_resolution,
+ SANE_Int from_width, SANE_Int myresize_mode);
+
+/* Scanner buttons support */
+static SANE_Int Buttons_Count (struct st_device *dev);
+static SANE_Int Buttons_Enable (struct st_device *dev);
+static SANE_Int Buttons_Order (struct st_device *dev, SANE_Int mask);
+static SANE_Int Buttons_Status (struct st_device *dev);
+static SANE_Int Buttons_Released (struct st_device *dev);
+
+/* Calibration functions */
+static SANE_Int Calib_CreateBuffers (struct st_device *dev,
+ struct st_calibration *buffer,
+ SANE_Int my14b4);
+static SANE_Int Calib_CreateFixedBuffers (void);
+static void Calib_FreeBuffers (struct st_calibration *caltables);
+static void Calib_LoadCut (struct st_device *dev,
+ struct st_scanparams *scancfg, SANE_Int scantype,
+ struct st_calibration_config *calibcfg);
+static SANE_Int Calib_AdcGain (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ SANE_Int arg2, SANE_Int gainmode);
+static SANE_Int Calib_AdcOffsetRT (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ SANE_Int value);
+static SANE_Int Calib_BlackShading (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ struct st_calibration *myCalib,
+ SANE_Int gainmode);
+static SANE_Int Calib_BWShading (struct st_calibration_config *calibcfg,
+ struct st_calibration *myCalib,
+ SANE_Int gainmode);
+static SANE_Int Calib_WhiteShading_3 (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ struct st_calibration *myCalib,
+ SANE_Int gainmode);
+static void Calibrate_Free (struct st_cal2 *calbuffers);
+static SANE_Int Calibrate_Malloc (struct st_cal2 *calbuffers,
+ SANE_Byte * Regs,
+ struct st_calibration *myCalib,
+ SANE_Int somelength);
+static SANE_Int Calib_ReadTable (struct st_device *dev, SANE_Byte * table,
+ SANE_Int size, SANE_Int data);
+static SANE_Int Calib_WriteTable (struct st_device *dev, SANE_Byte * table,
+ SANE_Int size, SANE_Int data);
+static SANE_Int Calib_LoadConfig (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ SANE_Int scantype, SANE_Int resolution,
+ SANE_Int bitmode);
+static SANE_Int Calib_PAGain (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ SANE_Int gainmode);
+static SANE_Int Calibration (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_calibration *myCalib, SANE_Int value);
+
+/* function for white shading correction */
+static SANE_Int WShading_Calibrate (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib,
+ struct st_scanparams *scancfg);
+static void WShading_Emulate (SANE_Byte * buffer, SANE_Int * chnptr,
+ SANE_Int size, SANE_Int depth);
+
+/* functions for shading calibration */
+static SANE_Int Shading_apply (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *myvar,
+ struct st_calibration *myCalib);
+static SANE_Int Shading_black_apply (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int channels,
+ struct st_calibration *myCalib,
+ struct st_cal2 *calbuffers);
+static SANE_Int Shading_white_apply (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int channels,
+ struct st_calibration *myCalib,
+ struct st_cal2 *calbuffers);
+
+/* Spread-Spectrum Clock Generator functions */
+static SANE_Int SSCG_Enable (struct st_device *dev);
+
+static void Split_into_12bit_channels (SANE_Byte * destino,
+ SANE_Byte * fuente, SANE_Int size);
+static SANE_Int Scan_Read_BufferA (struct st_device *dev,
+ SANE_Int buffer_size, SANE_Int arg2,
+ SANE_Byte * pBuffer,
+ SANE_Int * bytes_transfered);
+
+static SANE_Int Bulk_Operation (struct st_device *dev, SANE_Byte op,
+ SANE_Int buffer_size, SANE_Byte * buffer,
+ SANE_Int * transfered);
+static SANE_Int Get_PAG_Value (SANE_Byte scantype, SANE_Byte color);
+static SANE_Int GetOneLineInfo (struct st_device *dev, SANE_Int resolution,
+ SANE_Int * maximus, SANE_Int * minimus,
+ double *average);
+
+static SANE_Int Load_StripCoords (SANE_Int scantype, SANE_Int * ypos,
+ SANE_Int * xpos);
+
+/*static SANE_Int Free_Fixed_CalBuffer(void);*/
+static SANE_Int SetMultiExposure (struct st_device *dev, SANE_Byte * Regs);
+
+static void Set_E950_Mode (struct st_device *dev, SANE_Byte mode);
+
+static SANE_Int LoadImagingParams (struct st_device *dev, SANE_Int inifile);
+
+static SANE_Int SetScanParams (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_hwdconfig *hwdcfg);
+static SANE_Int IsScannerLinked (struct st_device *dev);
+
+static SANE_Int Read_FE3E (struct st_device *dev, SANE_Byte * destino);
+
+static double get_shrd (double value, SANE_Int desp);
+static char get_byte (double value);
+/*static SANE_Int RTS8822_GetRegisters(SANE_Byte *buffer);*/
+
+/* ----------------- Implementation ------------------*/
+
+static void
+RTS_Free (struct st_device *dev)
+{
+ /* this function frees space of devices's variable */
+
+ if (dev != NULL)
+ {
+ /* next function shouldn't be necessary but I can NOT assure that other
+ programmers will call Free_Config before this function */
+ Free_Config (dev);
+
+ if (dev->init_regs != NULL)
+ free (dev->init_regs);
+
+ if (dev->Resize != NULL)
+ free (dev->Resize);
+
+ if (dev->Reading != NULL)
+ free (dev->Reading);
+
+ if (dev->scanning != NULL)
+ free (dev->scanning);
+
+ if (dev->status != NULL)
+ free (dev->status);
+
+ free (dev);
+ }
+}
+
+static struct st_device *
+RTS_Alloc ()
+{
+ /* this function allocates space for device's variable */
+
+ struct st_device *dev = NULL;
+
+ dev = malloc (sizeof (struct st_device));
+ if (dev != NULL)
+ {
+ SANE_Int rst = OK;
+
+ bzero (dev, sizeof (struct st_device));
+
+ /* initial registers */
+ dev->init_regs = malloc (sizeof (SANE_Byte) * RT_BUFFER_LEN);
+ if (dev->init_regs != NULL)
+ bzero (dev->init_regs, sizeof (SANE_Byte) * RT_BUFFER_LEN);
+ else
+ rst = ERROR;
+
+ if (rst == OK)
+ {
+ dev->scanning = malloc (sizeof (struct st_scanning));
+ if (dev->scanning != NULL)
+ bzero (dev->scanning, sizeof (struct st_scanning));
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ dev->Reading = malloc (sizeof (struct st_readimage));
+ if (dev->Reading != NULL)
+ bzero (dev->Reading, sizeof (struct st_readimage));
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ dev->Resize = malloc (sizeof (struct st_resize));
+ if (dev->Resize != NULL)
+ bzero (dev->Resize, sizeof (struct st_resize));
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ dev->status = malloc (sizeof (struct st_status));
+ if (dev->status != NULL)
+ bzero (dev->status, sizeof (struct st_status));
+ else
+ rst = ERROR;
+ }
+
+ /* if something fails, free space */
+ if (rst != OK)
+ {
+ RTS_Free (dev);
+ dev = NULL;
+ }
+ }
+
+ return dev;
+}
+
+static void
+RTS_Scanner_End (struct st_device *dev)
+{
+ Gamma_FreeTables ();
+ Free_Config (dev);
+ Free_Vars ();
+}
+
+static SANE_Int
+Device_get (SANE_Int product, SANE_Int vendor)
+{
+ return cfg_device_get (product, vendor);
+}
+
+static SANE_Int
+RTS_Scanner_Init (struct st_device *dev)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "> RTS_Scanner_Init:\n");
+ DBG (DBG_FNC, "> Backend version: %s\n", BACKEND_VRSN);
+
+ rst = ERROR;
+
+ /* gets usb type of this scanner if it's not already set by user */
+ if (RTS_Debug->usbtype == -1)
+ RTS_Debug->usbtype = RTS_USBType (dev);
+
+ if (RTS_Debug->usbtype != ERROR)
+ {
+ DBG (DBG_FNC, " -> Chipset model ID: %i\n", Chipset_ID (dev));
+
+ Chipset_Reset (dev);
+
+ if (Load_Config (dev) == OK)
+ {
+ if (IsScannerLinked (dev) == OK)
+ {
+ Set_E950_Mode (dev, 0);
+ Gamma_AllocTable (NULL);
+ rst = OK;
+ }
+ else
+ Free_Config (dev);
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int
+RTS_WriteRegs (USB_Handle usb_handle, SANE_Byte * buffer)
+{
+ SANE_Int rst = ERROR;
+
+ if (buffer != NULL)
+ rst =
+ Write_Buffer (usb_handle, 0xe800, buffer,
+ RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ return rst;
+}
+
+static SANE_Int
+RTS_ReadRegs (USB_Handle usb_handle, SANE_Byte * buffer)
+{
+ SANE_Int rst = ERROR;
+
+ if (buffer != NULL)
+ rst =
+ Read_Buffer (usb_handle, 0xe800, buffer,
+ RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ return rst;
+}
+
+static void
+SetLock (USB_Handle usb_handle, SANE_Byte * Regs, SANE_Byte Enable)
+{
+ SANE_Byte lock;
+
+ DBG (DBG_FNC, "+ SetLock(*Regs, Enable=%i):\n", Enable);
+
+ if (Regs == NULL)
+ {
+ if (Read_Byte (usb_handle, 0xee00, &lock) != OK)
+ lock = 0;
+ }
+ else
+ lock = Regs[0x600];
+
+ if (Enable == FALSE)
+ lock &= 0xfb;
+ else
+ lock |= 4;
+
+ if (Regs != NULL)
+ Regs[0x600] = lock;
+
+ Write_Byte (usb_handle, 0xee00, lock);
+
+ DBG (DBG_FNC, "- SetLock\n");
+}
+
+static void
+Set_E950_Mode (struct st_device *dev, SANE_Byte mode)
+{
+ SANE_Int data;
+
+ DBG (DBG_FNC, "+ Set_E950_Mode(mode=%i):\n", mode);
+
+ if (Read_Word (dev->usb_handle, 0xe950, &data) == OK)
+ {
+ data = (mode == 0) ? data & 0xffbf : data | 0x40;
+ Write_Word (dev->usb_handle, 0xe950, data);
+ }
+
+ DBG (DBG_FNC, "- Set_E950_Mode\n");
+}
+
+static struct st_curve *
+Motor_Curve_Get (struct st_device *dev, SANE_Int motorcurve,
+ SANE_Int direction, SANE_Int itype)
+{
+ struct st_curve *rst = NULL;
+
+ if (dev != NULL)
+ {
+ if ((dev->mtrsetting != NULL) && (motorcurve < dev->mtrsetting_count))
+ {
+ struct st_motorcurve *mtc = dev->mtrsetting[motorcurve];
+
+ if (mtc != NULL)
+ {
+ if ((mtc->curve != NULL) && (mtc->curve_count > 0))
+ {
+ struct st_curve *crv;
+ SANE_Int a = 0;
+
+ while (a < mtc->curve_count)
+ {
+ /* get each curve */
+ crv = mtc->curve[a];
+ if (crv != NULL)
+ {
+ /* check direction and type */
+ if ((crv->crv_speed == direction)
+ && (crv->crv_type == itype))
+ {
+ /* found ! */
+ rst = crv;
+ break;
+ }
+ }
+ a++;
+ }
+ }
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int
+Motor_Curve_Equal (struct st_device *dev, SANE_Int motorsetting,
+ SANE_Int direction, SANE_Int curve1, SANE_Int curve2)
+{
+ /* compares two curves of the same direction
+ returns TRUE if both buffers are equal */
+
+ SANE_Int rst = FALSE;
+ struct st_curve *crv1 =
+ Motor_Curve_Get (dev, motorsetting, direction, curve1);
+ struct st_curve *crv2 =
+ Motor_Curve_Get (dev, motorsetting, direction, curve2);
+
+ if ((crv1 != NULL) && (crv2 != NULL))
+ {
+ if (crv1->step_count == crv2->step_count)
+ {
+ rst = TRUE;
+
+ if (crv1->step_count > 0)
+ {
+ SANE_Int a = 0;
+
+ while ((a < crv1->step_count) && (rst == TRUE))
+ {
+ rst = (crv1->step[a] == crv2->step[a]) ? TRUE : FALSE;
+ a++;
+ }
+ }
+ }
+ }
+
+ return rst;
+}
+
+static struct st_motorcurve **
+Motor_Curve_Parse (SANE_Int * mtc_count, SANE_Int * buffer)
+{
+ /* this function parses motorcurve buffer to get all motor settings */
+ struct st_motorcurve **rst = NULL;
+
+ *mtc_count = 0;
+
+ if (buffer != NULL)
+ {
+ /* phases:
+ -1 : null phase
+ 0 :
+ -3 : initial config
+ */
+ struct st_motorcurve *mtc = NULL;
+ SANE_Int phase;
+
+ phase = -1;
+ while (*buffer != -1)
+ {
+ if (*buffer == -2)
+ {
+ /* end of motorcurve */
+ /* complete any openned phase */
+ /* close phase */
+ phase = -1;
+ }
+ else
+ {
+ /* step */
+ if (phase == -1)
+ {
+ /* new motorcurve */
+ phase = 0;
+ mtc =
+ (struct st_motorcurve *)
+ malloc (sizeof (struct st_motorcurve));
+ if (mtc != NULL)
+ {
+ *mtc_count += 1;
+ rst =
+ realloc (rst,
+ sizeof (struct st_motorcurve **) *
+ *mtc_count);
+ if (rst != NULL)
+ {
+ rst[*mtc_count - 1] = mtc;
+ memset (mtc, 0, sizeof (struct st_motorcurve));
+ phase = -3; /* initial config */
+ }
+ else
+ {
+ /* memory error */
+ *mtc_count = 0;
+ break;
+ }
+ }
+ else
+ break; /* some error */
+ }
+
+ if (mtc != NULL)
+ {
+ switch (phase)
+ {
+ case -3: /* initial config */
+ mtc->mri = *(buffer);
+ mtc->msi = *(buffer + 1);
+ mtc->skiplinecount = *(buffer + 2);
+ mtc->motorbackstep = *(buffer + 3);
+ buffer += 3;
+
+ phase = -4;
+ break;
+
+ case -4:
+ /**/
+ {
+ /* create new step curve */
+ struct st_curve *curve =
+ malloc (sizeof (struct st_curve));
+ if (curve != NULL)
+ {
+ /* add to step curve list */
+ mtc->curve =
+ (struct st_curve **) realloc (mtc->curve,
+ sizeof (struct
+ st_curve
+ **) *
+ (mtc->
+ curve_count +
+ 1));
+ if (mtc->curve != NULL)
+ {
+ mtc->curve_count++;
+ mtc->curve[mtc->curve_count - 1] = curve;
+
+ memset (curve, 0, sizeof (struct st_curve));
+ /* read crv speed and type */
+ curve->crv_speed = *buffer;
+ curve->crv_type = *(buffer + 1);
+ buffer += 2;
+
+ /* get length of step buffer */
+ while (*(buffer + curve->step_count) != 0)
+ curve->step_count++;
+
+ if (curve->step_count > 0)
+ {
+ /* allocate step buffer */
+ curve->step =
+ (SANE_Int *) malloc (sizeof (SANE_Int) *
+ curve->step_count);
+ if (curve->step != NULL)
+ {
+ memcpy (curve->step, buffer,
+ sizeof (SANE_Int) *
+ curve->step_count);
+ buffer += curve->step_count;
+ }
+ else
+ curve->step_count = 0;
+ }
+ }
+ else
+ {
+ mtc->curve_count = 0;
+ free (curve);
+ }
+ }
+ else
+ break;
+ }
+ break;
+ }
+ }
+ }
+ buffer++;
+ }
+ }
+
+ return rst;
+}
+
+static void
+Motor_Curve_Free (struct st_motorcurve **motorcurves, SANE_Int * mtc_count)
+{
+ if ((motorcurves != NULL) && (mtc_count != NULL))
+ {
+ struct st_motorcurve *mtc;
+ struct st_curve *curve;
+
+ while (*mtc_count > 0)
+ {
+ mtc = motorcurves[*mtc_count - 1];
+ if (mtc != NULL)
+ {
+ if (mtc->curve != NULL)
+ {
+ while (mtc->curve_count > 0)
+ {
+ curve = mtc->curve[mtc->curve_count - 1];
+ if (curve != NULL)
+ {
+ if (curve->step != NULL)
+ free (curve->step);
+
+ free (curve);
+ }
+ mtc->curve_count--;
+ }
+ }
+ free (mtc);
+ }
+ *mtc_count -= 1;
+ }
+
+ free (motorcurves);
+ }
+}
+
+static SANE_Byte
+RTS_Sensor_Type (USB_Handle usb_handle)
+{
+ /*
+ Returns sensor type
+ 01 = CCD
+ 00 = CIS
+ */
+
+ SANE_Int a, b, c;
+ SANE_Byte rst;
+
+ DBG (DBG_FNC, "+ RTS_Sensor_Type:\n");
+
+ a = b = c = 0;
+
+ /* Save data first */
+ Read_Word (usb_handle, 0xe950, &a);
+ Read_Word (usb_handle, 0xe956, &b);
+
+ /* Enables GPIO 0xe950 writing directly 0x13ff */
+ Write_Word (usb_handle, 0xe950, 0x13ff);
+ /* Sets GPIO 0xe956 writing 0xfcf0 */
+ Write_Word (usb_handle, 0xe956, 0xfcf0);
+ /* Makes a sleep of 200 ms */
+ usleep (1000 * 200);
+ /* Get GPIO 0xe968 */
+ Read_Word (usb_handle, 0xe968, &c);
+ /* Restore data */
+ Write_Word (usb_handle, 0xe950, a);
+ Write_Word (usb_handle, 0xe956, b);
+
+ rst = ((_B1 (c) & 1) == 0) ? CCD_SENSOR : CIS_SENSOR;
+
+ DBG (DBG_FNC, "- RTS_Sensor_Type: %s\n",
+ (rst == CCD_SENSOR) ? "CCD" : "CIS");
+
+ return rst;
+}
+
+static void
+Free_Scanmodes (struct st_device *dev)
+{
+ DBG (DBG_FNC, "> Free_Scanmodes\n");
+
+ if (dev->scanmodes != NULL)
+ {
+ if (dev->scanmodes_count > 0)
+ {
+ SANE_Int a;
+ for (a = 0; a < dev->scanmodes_count; a++)
+ if (dev->scanmodes[a] != NULL)
+ free (dev->scanmodes[a]);
+ }
+
+ free (dev->scanmodes);
+ dev->scanmodes = NULL;
+ }
+
+ dev->scanmodes_count = 0;
+}
+
+static SANE_Int
+Load_Scanmodes (struct st_device *dev)
+{
+ SANE_Int rst = OK;
+ SANE_Int a, b;
+ struct st_scanmode reg, *mode;
+
+ DBG (DBG_FNC, "> Load_Scanmodes\n");
+
+ if ((dev->scanmodes != NULL) || (dev->scanmodes_count > 0))
+ Free_Scanmodes (dev);
+
+ a = 0;
+ while ((cfg_scanmode_get (dev->sensorcfg->type, a, &reg) == OK)
+ && (rst == OK))
+ {
+ mode = (struct st_scanmode *) malloc (sizeof (struct st_scanmode));
+ if (mode != NULL)
+ {
+ memcpy (mode, &reg, sizeof (struct st_scanmode));
+
+ for (b = 0; b < 3; b++)
+ {
+ if (mode->mexpt[b] == 0)
+ {
+ mode->mexpt[b] = mode->ctpc;
+ if (mode->multiexposure != 1)
+ mode->expt[b] = mode->ctpc;
+ }
+ }
+
+ mode->ctpc = ((mode->ctpc + 1) * mode->multiexposure) - 1;
+
+ dev->scanmodes =
+ (struct st_scanmode **) realloc (dev->scanmodes,
+ (dev->scanmodes_count +
+ 1) *
+ sizeof (struct st_scanmode **));
+ if (dev->scanmodes != NULL)
+ {
+ dev->scanmodes[dev->scanmodes_count] = mode;
+ dev->scanmodes_count++;
+ }
+ else
+ rst = ERROR;
+ }
+ else
+ rst = ERROR;
+
+ a++;
+ }
+
+ if (rst == ERROR)
+ Free_Scanmodes (dev);
+
+ DBG (DBG_FNC, " -> Found %i scanmodes\n", dev->scanmodes_count);
+ dbg_scanmodes (dev);
+
+ return rst;
+}
+
+static void
+Free_Config (struct st_device *dev)
+{
+ DBG (DBG_FNC, "+ Free_Config\n");
+
+ /* free buttons */
+ Free_Buttons (dev);
+
+ /* free motor general configuration */
+ Free_Motor (dev);
+
+ /* free sensor main configuration */
+ Free_Sensor (dev);
+
+ /* free ccd sensor timing tables */
+ Free_Timings (dev);
+
+ /* free motor curves */
+ Free_MotorCurves (dev);
+
+ /* free motor movements */
+ Free_Motormoves (dev);
+
+ /* free scan modes */
+ Free_Scanmodes (dev);
+
+ /* free constrains */
+ Free_Constrains (dev);
+
+ /* free chipset configuration */
+ Free_Chipset (dev);
+
+ DBG (DBG_FNC, "- Free_Config\n");
+}
+
+static void
+Free_Chipset (struct st_device *dev)
+{
+ DBG (DBG_FNC, "> Free_Chipset\n");
+
+ if (dev->chipset != NULL)
+ {
+ if (dev->chipset->name != NULL)
+ free (dev->chipset->name);
+
+ free (dev->chipset);
+ dev->chipset = NULL;
+ }
+}
+
+static SANE_Int
+Load_Chipset (struct st_device *dev)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "> Load_Chipset\n");
+
+ if (dev->chipset != NULL)
+ Free_Chipset (dev);
+
+ dev->chipset = malloc (sizeof (struct st_chip));
+ if (dev->chipset != NULL)
+ {
+ SANE_Int model;
+
+ bzero (dev->chipset, sizeof (struct st_chip));
+
+ /* get chipset model of selected scanner */
+ model = cfg_chipset_model_get (RTS_Debug->dev_model);
+
+ /* get configuration for selected chipset */
+ rst = cfg_chipset_get (model, dev->chipset);
+ }
+
+ /* if rst == ERROR may be related to allocating space for chipset name */
+
+ return rst;
+}
+
+static SANE_Int
+Load_Config (struct st_device *dev)
+{
+ DBG (DBG_FNC, "+ Load_Config\n");
+
+ /* load chipset configuration */
+ Load_Chipset (dev);
+
+ /* load scanner's buttons */
+ Load_Buttons (dev);
+
+ /* load scanner area constrains */
+ Load_Constrains (dev);
+
+ /* load motor general configuration */
+ Load_Motor (dev);
+
+ /* load sensor main configuration */
+ Load_Sensor (dev);
+
+ if (dev->sensorcfg->type == -1)
+ /* get sensor from gpio */
+ dev->sensorcfg->type = RTS_Sensor_Type (dev->usb_handle);
+
+ /* load ccd sensor timing tables */
+ Load_Timings (dev);
+
+ /* load motor curves */
+ Load_MotorCurves (dev);
+
+ /* load motor movements */
+ Load_Motormoves (dev);
+
+ /* load scan modes */
+ Load_Scanmodes (dev);
+
+ /* deprecated */
+ if (dev->sensorcfg->type == CCD_SENSOR)
+ /* ccd */ usbfile =
+ (RTS_Debug->usbtype == USB20) ? T_RTINIFILE : T_USB1INIFILE;
+ else /* cis */
+ usbfile = (RTS_Debug->usbtype == USB20) ? S_RTINIFILE : S_USB1INIFILE;
+
+ scantype = ST_NORMAL;
+
+ pwmlamplevel = get_value (SCAN_PARAM, PWMLAMPLEVEL, 1, usbfile);
+
+ arrangeline2 = get_value (SCAN_PARAM, ARRANGELINE, FIX_BY_HARD, usbfile);
+
+ shadingbase = get_value (TRUE_GRAY_PARAM, SHADINGBASE, 3, usbfile);
+ shadingfact[0] = get_value (TRUE_GRAY_PARAM, SHADINGFACT1, 1, usbfile);
+ shadingfact[1] = get_value (TRUE_GRAY_PARAM, SHADINGFACT2, 1, usbfile);
+ shadingfact[2] = get_value (TRUE_GRAY_PARAM, SHADINGFACT3, 1, usbfile);
+
+ LoadImagingParams (dev, usbfile);
+
+ DBG (DBG_FNC, "- Load_Config\n");
+
+ return OK;
+}
+
+static SANE_Int
+LoadImagingParams (struct st_device *dev, SANE_Int inifile)
+{
+ DBG (DBG_FNC, "> LoadImagingParams(inifile='%i'):\n", inifile);
+
+ scan.startpos = get_value (SCAN_PARAM, STARTPOS, 0, inifile);
+ scan.leftleading = get_value (SCAN_PARAM, LEFTLEADING, 0, inifile);
+ arrangeline = get_value (SCAN_PARAM, ARRANGELINE, FIX_BY_HARD, inifile);
+ compression = get_value (SCAN_PARAM, COMPRESSION, 0, inifile);
+
+ /* get default gain and offset values */
+ cfg_gainoffset_get (dev->sensorcfg->type, default_gain_offset);
+
+ linedarlampoff = get_value (CALI_PARAM, LINEDARLAMPOFF, 0, inifile);
+
+ pixeldarklevel = get_value (CALI_PARAM, PIXELDARKLEVEL, 0x00ffff, inifile);
+
+ binarythresholdh = get_value (PLATFORM, BINARYTHRESHOLDH, 0x80, inifile);
+ binarythresholdl = get_value (PLATFORM, BINARYTHRESHOLDL, 0x7f, inifile);
+
+ return OK;
+}
+
+static SANE_Int
+Arrange_Colour (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size, SANE_Int * transferred)
+{
+ /*
+ 05F0FA78 04EC00D8 /CALL to Assumed StdFunc2 from hpgt3970.04EC00D3
+ 05F0FA7C 05D10048 |Arg1 = 05D10048
+ 05F0FA80 0000F906 \Arg2 = 0000F906
+ */
+ SANE_Int mydistance;
+ SANE_Int Lines_Count;
+ SANE_Int space;
+ SANE_Int rst = OK;
+ SANE_Int c;
+ struct st_scanning *scn;
+
+ DBG (DBG_FNC, "> Arrange_Colour(*buffer, buffer_size=%i, *transferred)\n",
+ buffer_size);
+
+ /* this is just to make code more legible */
+ scn = dev->scanning;
+
+ if (scn->imagebuffer == NULL)
+ {
+ if (dev->sensorcfg->type == CCD_SENSOR)
+ mydistance =
+ (dev->sensorcfg->line_distance * scan2.resolution_y) /
+ dev->sensorcfg->resolution;
+ else
+ mydistance = 0;
+
+ /*aafa */
+ if (mydistance != 0)
+ {
+ scn->bfsize =
+ (scn->arrange_hres ==
+ TRUE) ? scn->arrange_sensor_evenodd_dist : 0;
+ scn->bfsize = (scn->bfsize + (mydistance * 2) + 1) * line_size;
+ }
+ else
+ scn->bfsize = line_size * 2;
+
+ /*ab3c */
+ space =
+ (((scn->bfsize / line_size) * bytesperline) >
+ scn->bfsize) ? (scn->bfsize / line_size) *
+ bytesperline : scn->bfsize;
+
+ scn->imagebuffer = (SANE_Byte *) malloc (space * sizeof (SANE_Byte));
+ if (scn->imagebuffer == NULL)
+ return ERROR;
+
+ scn->imagepointer = scn->imagebuffer;
+
+ if (Read_Block (dev, scn->bfsize, scn->imagebuffer, transferred) != OK)
+ return ERROR;
+
+ scn->arrange_orderchannel = FALSE;
+ scn->channel_size = (scan2.depth == 8) ? 1 : 2;
+
+ /* Calculate channel displacements */
+ for (c = CL_RED; c <= CL_BLUE; c++)
+ {
+ if (mydistance == 0)
+ {
+ /*ab9b */
+ if (scn->arrange_hres == FALSE)
+ {
+ if ((((dev->sensorcfg->line_distance * scan2.resolution_y) *
+ 2) / dev->sensorcfg->resolution) == 1)
+ scn->arrange_orderchannel = TRUE;
+
+ if (scn->arrange_orderchannel == TRUE)
+ scn->desp[c] =
+ ((dev->sensorcfg->rgb_order[c] / 2) * line_size) +
+ (scn->channel_size * c);
+ else
+ scn->desp[c] = scn->channel_size * c;
+ }
+ }
+ else
+ {
+ /*ac32 */
+ scn->desp[c] =
+ (dev->sensorcfg->rgb_order[c] * (mydistance * line_size)) +
+ (scn->channel_size * c);
+
+ if (scn->arrange_hres == TRUE)
+ {
+ scn->desp1[c] = scn->desp[c];
+ scn->desp2[c] =
+ ((scn->channel_size * 3) + scn->desp[c]) +
+ (scn->arrange_sensor_evenodd_dist * line_size);
+ }
+ }
+ }
+
+ /*ace2 */
+ for (c = CL_RED; c <= CL_BLUE; c++)
+ {
+ if (scn->arrange_hres == TRUE)
+ {
+ scn->pColour2[c] = scn->imagebuffer + scn->desp2[c];
+ scn->pColour1[c] = scn->imagebuffer + scn->desp1[c];
+ }
+ else
+ scn->pColour[c] = scn->imagebuffer + scn->desp[c];
+ }
+ }
+
+ /*ad91 */
+ Lines_Count = buffer_size / line_size;
+ while (Lines_Count > 0)
+ {
+ if (scn->arrange_orderchannel == FALSE)
+ {
+ if (scn->arrange_hres == TRUE)
+ Triplet_Colour_HRes (scn->pColour1[CL_RED],
+ scn->pColour1[CL_GREEN],
+ scn->pColour1[CL_BLUE],
+ scn->pColour2[CL_RED],
+ scn->pColour2[CL_GREEN],
+ scn->pColour2[CL_BLUE], buffer,
+ line_size / (scn->channel_size * 3));
+ else
+ Triplet_Colour_LRes (line_size / (scn->channel_size * 3), buffer,
+ scn->pColour[CL_RED], scn->pColour[CL_GREEN],
+ scn->pColour[CL_BLUE]);
+ }
+ else
+ Triplet_Colour_Order (dev, scn->pColour[CL_RED],
+ scn->pColour[CL_GREEN], scn->pColour[CL_BLUE],
+ buffer, line_size / (scn->channel_size * 3));
+
+ scn->arrange_size -= bytesperline;
+ if (scn->arrange_size < 0)
+ v15bc--;
+
+ buffer += line_size;
+
+ Lines_Count--;
+ if (Lines_Count == 0)
+ {
+ if ((scn->arrange_size | v15bc) == 0)
+ return OK;
+ }
+
+ if (Read_Block (dev, line_size, scn->imagepointer, transferred) ==
+ ERROR)
+ return ERROR;
+
+ /* Update displacements */
+ for (c = CL_RED; c <= CL_BLUE; c++)
+ {
+ if (scn->arrange_hres == TRUE)
+ {
+ /*aeb7 */
+ scn->desp2[c] = (scn->desp2[c] + line_size) % scn->bfsize;
+ scn->desp1[c] = (scn->desp1[c] + line_size) % scn->bfsize;
+
+ scn->pColour2[c] = scn->imagebuffer + scn->desp2[c];
+ scn->pColour1[c] = scn->imagebuffer + scn->desp1[c];
+ }
+ else
+ {
+ /*af86 */
+ scn->desp[c] = (scn->desp[c] + line_size) % scn->bfsize;
+ scn->pColour[c] = scn->imagebuffer + scn->desp[c];
+ }
+ }
+
+ /*aff3 */
+ scn->imagepointer += line_size;
+ if (scn->imagepointer >= (scn->imagebuffer + scn->bfsize))
+ scn->imagepointer = scn->imagebuffer;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+RTS_Scanner_SetParams (struct st_device *dev, struct params *param)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ RTS_Scanner_SetParams:\n");
+ DBG (DBG_FNC, "-> param->resolution_x=%i\n", param->resolution_x);
+ DBG (DBG_FNC, "-> param->resolution_y=%i\n", param->resolution_y);
+ DBG (DBG_FNC, "-> param->left =%i\n", param->coords.left);
+ DBG (DBG_FNC, "-> param->width =%i\n", param->coords.width);
+ DBG (DBG_FNC, "-> param->top =%i\n", param->coords.top);
+ DBG (DBG_FNC, "-> param->height =%i\n", param->coords.height);
+ DBG (DBG_FNC, "-> param->colormode =%s\n",
+ dbg_colour (param->colormode));
+ DBG (DBG_FNC, "-> param->scantype =%s\n",
+ dbg_scantype (param->scantype));
+ DBG (DBG_FNC, "-> param->depth =%i\n", param->depth);
+ DBG (DBG_FNC, "-> param->channel =%i\n", param->channel);
+
+ /* validate area size to scan */
+ if ((param->coords.width != 0) && (param->coords.height != 0))
+ {
+ SANE_Byte mybuffer[1];
+ struct st_hwdconfig hwdcfg;
+
+ /* setting coordinates */
+ memcpy (&scan.coord, &param->coords, sizeof (struct st_coords));
+
+ /* setting resolution */
+ scan.resolution_x = param->resolution_x;
+ scan.resolution_y = param->resolution_y;
+
+ /* setting colormode and depth */
+ scan.colormode = param->colormode;
+ scan.depth = (param->colormode == CM_LINEART) ? 8 : param->depth;
+
+ /* setting color channel for non color scans */
+ scan.channel = _B0 (param->channel);
+
+ arrangeline = FIX_BY_HARD;
+ if ((scan.resolution_x == 2400) || ((scan.resolution_x == 4800)))
+ {
+ if (scan.colormode != CM_COLOR)
+ {
+ if (scan.colormode == CM_GRAY)
+ {
+ if (scan.channel == 3)
+ arrangeline = FIX_BY_SOFT;
+ }
+ }
+ else
+ arrangeline = FIX_BY_SOFT;
+ }
+
+ /* setting scan type */
+ if ((param->scantype > 0) && (param->scantype < 4))
+ scan.scantype = param->scantype;
+ else
+ scan.scantype = ST_NORMAL;
+
+ /* setting scanner lamp */
+ data_bitset (&dev->init_regs[0x146], 0x40,
+ ((dev->sensorcfg->type == CIS_SENSOR) ? 0 : 1));
+
+ /* turn on appropiate lamp */
+ if (scan.scantype == ST_NORMAL)
+ Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP);
+ else
+ Lamp_Status_Set (dev, NULL, TRUE, TMA_LAMP);
+
+ mybuffer[0] = 0;
+ if (RTS_IsExecuting (dev, mybuffer) == FALSE)
+ RTS_WriteRegs (dev->usb_handle, dev->init_regs);
+
+ if (scan.depth == 16)
+ compression = FALSE;
+
+ /* resetting low level config */
+ bzero (&hwdcfg, sizeof (struct st_hwdconfig));
+
+ /* setting low level config */
+ hwdcfg.scantype = scan.scantype;
+ hwdcfg.calibrate = mybuffer[0];
+ hwdcfg.arrangeline = arrangeline; /*1 */
+ hwdcfg.highresolution = (scan.resolution_x > 1200) ? TRUE : FALSE;
+ hwdcfg.sensorevenodddistance = dev->sensorcfg->evenodd_distance;
+
+ SetScanParams (dev, dev->init_regs, &scan, &hwdcfg);
+
+ scan.shadinglength =
+ (((scan.sensorresolution * 17) / 2) + 3) & 0xfffffffc;
+
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- RTS_Scanner_SetParams: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+SetScanParams (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg)
+{
+ struct st_coords mycoords;
+ SANE_Int mycolormode;
+ SANE_Int myvalue;
+ SANE_Int mymode;
+ SANE_Int channel_size;
+ SANE_Int channel_count;
+ SANE_Int dots_per_block;
+ SANE_Int aditional_dots;
+
+ DBG (DBG_FNC, "+ SetScanParams:\n");
+ dbg_ScanParams (scancfg);
+ dbg_hwdcfg (hwdcfg);
+
+ bzero (&mycoords, sizeof (struct st_coords));
+ /* Copy scancfg to scan2 */
+ memcpy (&scan2, scancfg, sizeof (struct st_scanparams));
+
+ mycolormode = scancfg->colormode;
+ myvalue = scancfg->colormode;
+ scantype = hwdcfg->scantype;
+
+ if (scancfg->colormode == CM_LINEART)
+ scan2.depth = 8;
+
+ if ((scancfg->colormode != CM_COLOR) && (scancfg->channel == 3)) /*channel = 0x00 */
+ {
+ if (scancfg->colormode == CM_GRAY)
+ {
+ mycolormode = (hwdcfg->arrangeline != FIX_BY_SOFT) ? 3 : CM_COLOR;
+ }
+ else
+ mycolormode = 3;
+ myvalue = mycolormode;
+ }
+
+ dev->Resize->resolution_x = scancfg->resolution_x;
+ dev->Resize->resolution_y = scancfg->resolution_y;
+
+ mymode = RTS_GetScanmode (dev, hwdcfg->scantype, myvalue, scancfg->resolution_x); /*0x0b */
+ if (mymode == -1)
+ {
+ /* Non supported resolution. We will resize image after scanning */
+ SANE_Int fitres;
+
+ fitres =
+ Scanmode_fitres (dev, hwdcfg->scantype, scancfg->colormode,
+ scancfg->resolution_x);
+ if (fitres != -1)
+ {
+ /* supported resolution found */
+ dev->Resize->type = RSZ_DECREASE;
+ }
+ else
+ {
+ dev->Resize->type = RSZ_INCREASE;
+ fitres =
+ Scanmode_maxres (dev, hwdcfg->scantype, scancfg->colormode);
+ }
+
+ scan2.resolution_x = fitres;
+ scan2.resolution_y = fitres;
+
+ mymode =
+ RTS_GetScanmode (dev, hwdcfg->scantype, myvalue, scan2.resolution_x);
+ if (mymode == -1)
+ return ERROR;
+
+ imageheight = scancfg->coord.height;
+ dev->Resize->towidth = scancfg->coord.width;
+
+ /* Calculate coords for new resolution */
+ mycoords.left =
+ (scan2.resolution_x * scancfg->coord.left) /
+ dev->Resize->resolution_x;
+ mycoords.width =
+ (scan2.resolution_x * scancfg->coord.width) /
+ dev->Resize->resolution_x;
+ mycoords.top =
+ (scan2.resolution_y * scancfg->coord.top) / dev->Resize->resolution_y;
+ mycoords.height =
+ ((scan2.resolution_y * scancfg->coord.height) /
+ dev->Resize->resolution_y) + 2;
+
+ switch (scan2.colormode)
+ {
+ case CM_GRAY:
+ if ((dev->scanmodes[mymode]->samplerate == PIXEL_RATE)
+ && (mycolormode != 3))
+ dev->Resize->towidth *= 2;
+
+ channel_size = (scan2.depth == 8) ? 1 : 2;
+ dev->Resize->mode = (scan2.depth == 8) ? RSZ_GRAYL : RSZ_GRAYH;
+ dev->Resize->bytesperline = dev->Resize->towidth * channel_size;
+ break;
+ case CM_LINEART:
+ if (dev->scanmodes[mymode]->samplerate == PIXEL_RATE)
+ dev->Resize->towidth *= 2;
+
+ dev->Resize->mode = RSZ_LINEART;
+ dev->Resize->bytesperline = (dev->Resize->towidth + 7) / 8;
+ break;
+ default: /*CM_COLOR */
+ channel_count = 3;
+ channel_size = (scan2.depth == 8) ? 1 : 2;
+ dev->Resize->mode = (scan2.depth == 8) ? RSZ_COLOURL : RSZ_COLOURH;
+ dev->Resize->bytesperline =
+ scancfg->coord.width * (channel_count * channel_size);
+ break;
+ }
+ }
+ else
+ {
+ /* Supported scanmode */
+ dev->Resize->type = RSZ_NONE;
+ scan2.resolution_x = scancfg->resolution_x;
+ scan2.resolution_y = scancfg->resolution_y;
+ mycoords.left = scancfg->coord.left;
+ mycoords.top = scancfg->coord.top;
+ mycoords.width = scancfg->coord.width;
+ mycoords.height = scancfg->coord.height;
+ }
+
+ scancfg->timing = dev->scanmodes[mymode]->timing;
+
+ scan2.sensorresolution = dev->timings[scancfg->timing]->sensorresolution;
+ if ((scantype > 0) && (scantype < 5))
+ scan2.shadinglength =
+ (((scan2.sensorresolution * 17) / 2) + 3) & 0xfffffffc;
+
+ scancfg->sensorresolution = scan2.sensorresolution;
+ scancfg->shadinglength = scan2.shadinglength;
+
+ dev->scanning->arrange_compression = ((mycolormode != CM_LINEART)
+ && (scan2.depth <=
+ 8)) ? hwdcfg->compression : FALSE;
+
+ if ((arrangeline2 == FIX_BY_HARD) || (mycolormode == CM_LINEART))
+ arrangeline2 = mycolormode; /*¿? */
+ else if ((mycolormode == CM_GRAY) && (hwdcfg->highresolution == FALSE))
+ arrangeline2 = 0;
+
+ if (hwdcfg->highresolution == FALSE)
+ {
+ /* resolution < 1200 dpi */
+ dev->scanning->arrange_hres = FALSE;
+ dev->scanning->arrange_sensor_evenodd_dist = 0;
+ }
+ else
+ {
+ /* resolution > 1200 dpi */
+ dev->scanning->arrange_hres = TRUE;
+ dev->scanning->arrange_sensor_evenodd_dist =
+ hwdcfg->sensorevenodddistance;
+ }
+
+ /* with must be adjusted to fit in the dots count per block */
+ aditional_dots = 0;
+ if (mycolormode != CM_LINEART)
+ {
+ dots_per_block = ((scan2.resolution_x > 2400)
+ && (scancfg->samplerate == PIXEL_RATE)) ? 8 : 4;
+
+ /* fit width */
+ if ((mycoords.width % dots_per_block) != 0)
+ {
+ aditional_dots = dots_per_block - (mycoords.width % dots_per_block);
+ mycoords.width += aditional_dots;
+ }
+ }
+ else
+ {
+ /* Lineart */
+ dots_per_block = 32 - (mycoords.width & 0x1f);
+ if (dots_per_block < 32)
+ {
+ mycoords.width += dots_per_block;
+ aditional_dots = (dots_per_block / 8);
+ }
+ }
+
+ DBG (DBG_FNC, " -> dots_per_block: %i\n", dots_per_block);
+ DBG (DBG_FNC, " -> aditional_dots: %i\n", aditional_dots);
+
+ if (mycolormode == CM_LINEART)
+ {
+ bytesperline =
+ (dev->scanmodes[mymode]->samplerate ==
+ PIXEL_RATE) ? mycoords.width / 4 : mycoords.width / 8;
+ imagewidth3 = bytesperline;
+ lineart_width = bytesperline * 8;
+ line_size = bytesperline - aditional_dots;
+ dev->Resize->fromwidth = line_size * 8;
+ }
+ else
+ {
+ /*4510 */
+ switch (mycolormode)
+ {
+ case CM_COLOR:
+ channel_count = 3;
+ break;
+ case 3:
+ channel_count = 1;
+ break;
+ case CM_GRAY:
+ channel_count = (dev->scanmodes[mymode]->samplerate == PIXEL_RATE) ? 2 : 1; /*1 */
+ break;
+ }
+
+ channel_size = (scan2.depth == 8) ? 1 : 2;
+ bytesperline = mycoords.width * (channel_count * channel_size);
+ imagewidth3 = bytesperline / channel_count;
+ lineart_width = imagewidth3 / channel_size;
+ line_size =
+ bytesperline - (aditional_dots * (channel_count * channel_size));
+ dev->Resize->fromwidth = line_size / (channel_count * channel_size);
+ }
+
+ imagesize = mycoords.height * bytesperline;
+ v15b4 = 0;
+ dev->scanning->arrange_size = imagesize;
+ v15bc = 0;
+
+ /* set resolution ratio */
+ data_bitset (&Regs[0xc0], 0x1f,
+ scancfg->sensorresolution / scancfg->resolution_x);
+
+ scancfg->coord.left = mycoords.left;
+ scancfg->coord.top = mycoords.top;
+ scancfg->coord.width = mycoords.width;
+ scancfg->coord.height = mycoords.height;
+ scancfg->resolution_x = scan2.resolution_x;
+ scancfg->resolution_y = scan2.resolution_y;
+
+ myvalue =
+ (dev->Resize->type == RSZ_NONE) ? line_size : dev->Resize->bytesperline;
+ scancfg->bytesperline = bytesperline;
+
+ scancfg->v157c = myvalue;
+
+ if (scan.colormode != CM_COLOR)
+ {
+ if (mycolormode == CM_COLOR)
+ scancfg->v157c = (scancfg->v157c / 3);
+ }
+
+ if (scan.colormode == CM_LINEART)
+ {
+ if (mycolormode == 3)
+ {
+ scancfg->v157c = (scancfg->v157c + 7) / 8;
+ scancfg->bytesperline = (scancfg->bytesperline + 7) / 8;
+ }
+ }
+
+ DBG (DBG_FNC, "- SetScanParams:\n");
+
+ return OK;
+}
+
+static SANE_Int
+GainOffset_Counter_Save (struct st_device *dev, SANE_Byte data)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "> GainOffset_Counter_Save(data=%i):\n", data);
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ data = min (data, 0x0f);
+ rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x0077, data);
+ }
+
+ return rst;
+}
+
+static SANE_Int
+GainOffset_Counter_Inc (struct st_device *dev, SANE_Int * arg1)
+{
+ SANE_Byte count;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ GainOffset_Counter_Inc:\n");
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ count = GainOffset_Counter_Load (dev);
+ if ((count >= 0x0f) || (GainOffset_Get (dev) != OK))
+ {
+ offset[CL_BLUE] = offset[CL_GREEN] = offset[CL_RED] = 0;
+ gain[CL_BLUE] = gain[CL_GREEN] = gain[CL_RED] = 0;
+ count = 0;
+ }
+ else
+ {
+ count++;
+ if (arg1 != NULL)
+ *arg1 = 1;
+ }
+
+ rst = GainOffset_Counter_Save (dev, count);
+ }
+ else
+ rst = OK;
+
+ DBG (DBG_FNC, "- GainOffset_Counter_Inc: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+GainOffset_Get (struct st_device *dev)
+{
+ SANE_Int a, data, rst;
+ SANE_Byte checksum;
+
+ DBG (DBG_FNC, "+ GainOffset_Get:\n");
+
+ checksum = 0;
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ /* get current checksum */
+ if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x76, &checksum) == OK)
+ {
+ rst = OK;
+
+ /* read gain and offset values from EEPROM */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x70 + (2 * a), &data)
+ == ERROR)
+ {
+ rst = ERROR;
+ break;
+ }
+ else
+ offset[a] = data;
+ }
+
+ /* check checksum */
+ checksum =
+ _B0 (checksum + offset[CL_GREEN] + offset[CL_BLUE] +
+ offset[CL_RED]);
+ }
+ else
+ rst = ERROR;
+ }
+ else
+ rst = ERROR;
+
+ /* extract gain and offset values */
+ if ((rst == OK) && (checksum == 0x5b))
+ {
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ gain[a] = (offset[a] >> 9) & 0x1f;
+ offset[a] &= 0x01ff;
+ }
+ }
+ else
+ {
+ /* null values, let's reset them */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ gain[a] = 0;
+ offset[a] = 0;
+ }
+
+ rst = ERROR;
+ }
+
+ DBG (DBG_FNC,
+ "-> Preview gainR=%i, gainG=%i, gainB=%i, offsetR=%i, offsetG=%i, offsetB=%i\n",
+ gain[CL_RED], gain[CL_GREEN], gain[CL_BLUE], offset[CL_RED],
+ offset[CL_GREEN], offset[CL_BLUE]);
+
+ DBG (DBG_FNC, "- GainOffset_Get: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Scanmode_maxres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode)
+{
+ /* returns position in scanmodes table where data fits with given arguments */
+ SANE_Int rst = 0;
+ SANE_Int a;
+ struct st_scanmode *reg;
+
+ for (a = 0; a < dev->scanmodes_count; a++)
+ {
+ reg = dev->scanmodes[a];
+ if (reg != NULL)
+ {
+ if ((reg->scantype == scantype) && (reg->colormode == colormode))
+ rst = max (rst, reg->resolution); /* found ! */
+ }
+ }
+
+ if (rst == 0)
+ {
+ /* There isn't any mode for these arguments.
+ Most devices doesn't support specific setup to scan in lineart mode
+ so they use gray colormode. Lets check this case */
+ if (colormode == CM_LINEART)
+ rst = Scanmode_maxres (dev, scantype, CM_GRAY);
+ }
+
+ DBG (DBG_FNC, "> Scanmode_maxres(scantype=%s, colormode=%s): %i\n",
+ dbg_scantype (scantype), dbg_colour (colormode), rst);
+
+ return rst;
+}
+
+static SANE_Int
+Scanmode_minres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode)
+{
+ /* returns position in scanmodes table where data fits with given arguments */
+ SANE_Int rst, a;
+ struct st_scanmode *reg;
+
+ rst = Scanmode_maxres (dev, scantype, colormode);
+
+ for (a = 0; a < dev->scanmodes_count; a++)
+ {
+ reg = dev->scanmodes[a];
+ if (reg != NULL)
+ {
+ if ((reg->scantype == scantype) && (reg->colormode == colormode))
+ rst = min (rst, reg->resolution); /* found ! */
+ }
+ }
+
+ if (rst == 0)
+ {
+ /* There isn't any mode for these arguments.
+ Most devices doesn't support specific setup to scan in lineart mode
+ so they use gray colormode. Lets check this case */
+ if (colormode == CM_LINEART)
+ rst = Scanmode_minres (dev, scantype, CM_GRAY);
+ }
+
+ DBG (DBG_FNC, "> Scanmode_minres(scantype=%s, colormode=%s): %i\n",
+ dbg_scantype (scantype), dbg_colour (colormode), rst);
+
+ return rst;
+}
+
+static SANE_Int
+Scanmode_fitres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode,
+ SANE_Int resolution)
+{
+ /* returns a supported resolution */
+ SANE_Int rst;
+ SANE_Int a, nullres;
+ struct st_scanmode *reg;
+
+ nullres = Scanmode_maxres (dev, scantype, colormode) + 1;
+ rst = nullres;
+
+ for (a = 0; a < dev->scanmodes_count; a++)
+ {
+ reg = dev->scanmodes[a];
+ if (reg != NULL)
+ {
+ if ((reg->scantype == scantype) && (reg->colormode == colormode))
+ {
+ if ((reg->resolution < rst) && (resolution <= reg->resolution))
+ rst = reg->resolution;
+ }
+ }
+ }
+
+ if (rst == nullres)
+ {
+ /* There isn't any mode for these arguments.
+ Most devices doesn't support specific setup to scan in lineart mode
+ so they use gray colormode. Lets check this case */
+ if (colormode != CM_LINEART)
+ {
+ /* at this point, given resolution is bigger than maximum supported resolution */
+ rst = -1;
+ }
+ else
+ rst = Scanmode_minres (dev, scantype, CM_GRAY);
+ }
+
+ DBG (DBG_FNC,
+ "> Scanmode_fitres(scantype=%s, colormode=%s, resolution=%i): %i\n",
+ dbg_scantype (scantype), dbg_colour (colormode), resolution, rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_GetScanmode (struct st_device *dev, SANE_Int scantype, SANE_Int colormode,
+ SANE_Int resolution)
+{
+ /* returns position in scanmodes table where data fits with given arguments */
+ SANE_Int rst = -1;
+ SANE_Int a;
+ struct st_scanmode *reg;
+
+ for (a = 0; a < dev->scanmodes_count; a++)
+ {
+ reg = dev->scanmodes[a];
+ if (reg != NULL)
+ {
+ if ((reg->scantype == scantype) && (reg->colormode == colormode)
+ && (reg->resolution == resolution))
+ {
+ /* found ! */
+ rst = a;
+ break;
+ }
+ }
+ }
+
+ if (rst == -1)
+ {
+ /* There isn't any mode for these arguments.
+ May be given resolution isn't supported by chipset.
+ Most devices doesn't support specific setup to scan in lineart mode
+ so they use gray colormode. Lets check this case */
+ if ((colormode == CM_LINEART) || (colormode == 3))
+ rst = RTS_GetScanmode (dev, scantype, CM_GRAY, resolution);
+ }
+
+ DBG (DBG_FNC,
+ "> RTS_GetScanmode(scantype=%s, colormode=%s, resolution=%i): %i\n",
+ dbg_scantype (scantype), dbg_colour (colormode), resolution, rst);
+
+ return rst;
+}
+
+static void
+Free_Motor (struct st_device *dev)
+{
+ /* this function releases space for stepper motor */
+
+ DBG (DBG_FNC, "> Free_Motor\n");
+
+ if (dev->motorcfg != NULL)
+ {
+ free (dev->motorcfg);
+ dev->motorcfg = NULL;
+ }
+}
+
+static SANE_Int
+Load_Motor (struct st_device *dev)
+{
+ /* this function loads general configuration for motor */
+
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "> Load_Motor\n");
+
+ if (dev->motorcfg != NULL)
+ Free_Motor (dev);
+
+ dev->motorcfg = malloc (sizeof (struct st_motorcfg));
+ if (dev->motorcfg != NULL)
+ {
+ rst = cfg_motor_get (dev->motorcfg);
+ dbg_motorcfg (dev->motorcfg);
+ }
+
+ return rst;
+}
+
+static void
+Free_Sensor (struct st_device *dev)
+{
+ /* this function releases space for ccd sensor */
+
+ DBG (DBG_FNC, "> Free_Sensor\n");
+
+ if (dev->sensorcfg != NULL)
+ {
+ free (dev->sensorcfg);
+ dev->sensorcfg = NULL;
+ }
+}
+
+static void
+Free_Buttons (struct st_device *dev)
+{
+ /* this function releases space for buttons */
+
+ DBG (DBG_FNC, "> Free_Buttons\n");
+
+ if (dev->buttons != NULL)
+ {
+ free (dev->buttons);
+ dev->buttons = NULL;
+ }
+}
+
+static SANE_Int
+Load_Buttons (struct st_device *dev)
+{
+ /* this function loads configuration for ccd sensor */
+
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "> Load_Buttons\n");
+
+ if (dev->buttons != NULL)
+ Free_Buttons (dev);
+
+ dev->buttons = malloc (sizeof (struct st_buttons));
+ if (dev->buttons != NULL)
+ {
+ rst = cfg_buttons_get (dev->buttons);
+ dbg_buttons (dev->buttons);
+ }
+
+ return rst;
+}
+
+static SANE_Int
+Load_Sensor (struct st_device *dev)
+{
+ /* this function loads configuration for ccd sensor */
+
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "> Load_Sensor\n");
+
+ if (dev->sensorcfg != NULL)
+ Free_Sensor (dev);
+
+ dev->sensorcfg = malloc (sizeof (struct st_sensorcfg));
+ if (dev->sensorcfg != NULL)
+ {
+ rst = cfg_sensor_get (dev->sensorcfg);
+ dbg_sensor (dev->sensorcfg);
+ }
+
+ return rst;
+}
+
+static void
+Free_Timings (struct st_device *dev)
+{
+ /* this function frees all ccd sensor timing tables */
+ DBG (DBG_FNC, "> Free_Timings\n");
+
+ if (dev->timings != NULL)
+ {
+ if (dev->timings_count > 0)
+ {
+ SANE_Int a;
+ for (a = 0; a < dev->timings_count; a++)
+ if (dev->timings[a] != NULL)
+ free (dev->timings[a]);
+
+ dev->timings_count = 0;
+ }
+
+ free (dev->timings);
+ dev->timings = NULL;
+ }
+}
+
+static SANE_Int
+Load_Timings (struct st_device *dev)
+{
+ SANE_Int rst = OK;
+ SANE_Int a;
+ struct st_timing reg, *tmg;
+
+ DBG (DBG_FNC, "> Load_Timings\n");
+
+ if (dev->timings != NULL)
+ Free_Timings (dev);
+
+ a = 0;
+
+ while ((cfg_timing_get (dev->sensorcfg->type, a, &reg) == OK)
+ && (rst == OK))
+ {
+ tmg = (struct st_timing *) malloc (sizeof (struct st_timing));
+ if (tmg != NULL)
+ {
+ memcpy (tmg, &reg, sizeof (struct st_timing));
+
+ dev->timings_count++;
+ dev->timings =
+ (struct st_timing **) realloc (dev->timings,
+ sizeof (struct st_timing **) *
+ dev->timings_count);
+ if (dev->timings == NULL)
+ {
+ rst = ERROR;
+ dev->timings_count = 0;
+ }
+ else
+ dev->timings[dev->timings_count - 1] = tmg;
+ }
+ else
+ rst = ERROR;
+
+ a++;
+ }
+
+ if (rst == ERROR)
+ Free_Timings (dev);
+
+ DBG (DBG_FNC, " -> Found %i timing registers\n", dev->timings_count);
+
+ return rst;
+}
+
+static SANE_Int
+IsScannerLinked (struct st_device *dev)
+{
+ SANE_Int var2;
+ SANE_Byte lamp;
+
+ DBG (DBG_FNC, "+ IsScannerLinked:\n");
+
+ Read_FE3E (dev, &v1619);
+ Init_USBData (dev);
+ scan.scantype = ST_NORMAL;
+
+ RTS_WaitInitEnd (dev, 0x30000);
+
+ lamp = FLB_LAMP;
+
+ /* Comprobar si es la primera conexión con el escaner */
+ if (Read_Word (dev->usb_handle, 0xe829, &var2) == OK)
+ {
+ SANE_Int firstconnection;
+
+#ifdef STANDALONE
+ firstconnection = TRUE;
+#else
+ firstconnection = (var2 == 0) ? TRUE : FALSE;
+#endif
+
+ if (firstconnection == TRUE)
+ {
+ /* primera conexión */
+ SANE_Byte flb_lamp, tma_lamp;
+
+ flb_lamp = 0;
+ tma_lamp = 0;
+ Lamp_Status_Get (dev, &flb_lamp, &tma_lamp);
+
+ if ((flb_lamp == 0) && (tma_lamp != 0))
+ lamp = TMA_LAMP;
+
+ /*Clear GainOffset count */
+ GainOffset_Clear (dev);
+ GainOffset_Counter_Save (dev, 0);
+
+ /* Clear AutoRef count */
+ Refs_Counter_Save (dev, 0);
+
+ Buttons_Enable (dev);
+ Lamp_Status_Timer_Set (dev, 13);
+ }
+ else
+ lamp = (_B0 (var2) == 0) ? FLB_LAMP : TMA_LAMP;
+ }
+
+ if (RTS_Warm_Reset (dev) != OK)
+ return ERROR;
+
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+
+ Lamp_Status_Timer_Set (dev, 13);
+
+ /* Use fixed pwm? */
+ if (RTS_Debug->use_fixed_pwm != FALSE)
+ {
+ Lamp_PWM_Save (dev, cfg_fixedpwm_get (dev->sensorcfg->type, ST_NORMAL));
+ /* Lets enable using fixed pwm */
+ Lamp_PWM_SaveStatus (dev, TRUE);
+ }
+
+ Lamp_PWM_Setup (dev, lamp);
+
+ DBG (DBG_FNC, "- IsScannerLinked:\n");
+
+ return OK;
+}
+
+static SANE_Int
+Lamp_PWM_SaveStatus (struct st_device *dev, SANE_Byte status)
+{
+ SANE_Byte mypwm;
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_SaveStatus(status=%i):\n", status);
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ rst = ERROR;
+
+ if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x007b, &mypwm) == OK)
+ {
+ mypwm = (status == FALSE) ? mypwm & 0x7f : mypwm | 0x80;
+
+ if (RTS_EEPROM_WriteByte (dev->usb_handle, 0x007b, mypwm) == OK)
+ rst = OK;
+ }
+ }
+
+ DBG (DBG_FNC, "- Lamp_PWM_SaveStatus: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_PWM_Save (struct st_device *dev, SANE_Int fixedpwm)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_Save(fixedpwm=%i):\n", fixedpwm);
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ rst =
+ RTS_EEPROM_WriteByte (dev->usb_handle, 0x7b, ((fixedpwm << 2) | 0x80));
+ else
+ rst = OK;
+
+ DBG (DBG_FNC, "- Lamp_PWM_Save: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_PWM_Setup (struct st_device *dev, SANE_Int lamp)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_Setup(lamp=%s):\n",
+ (lamp == FLB_LAMP) ? "FLB_LAMP" : "TMA_LAMP");
+
+ if (Lamp_PWM_use (dev, 1) == OK)
+ {
+ SANE_Int fixedpwm, currentpwd;
+
+ currentpwd = 0;
+ fixedpwm =
+ cfg_fixedpwm_get (dev->sensorcfg->type,
+ (lamp == FLB_LAMP) ? ST_NORMAL : ST_TA);
+
+ if (Lamp_PWM_DutyCycle_Get (dev, &currentpwd) == OK)
+ {
+ /* set duty cycle if current one is different */
+ if (currentpwd != fixedpwm)
+ rst = Lamp_PWM_DutyCycle_Set (dev, fixedpwm);
+ }
+ else
+ rst = Lamp_PWM_DutyCycle_Set (dev, fixedpwm);
+ }
+
+ DBG (DBG_FNC, "- Lamp_PWM_Setup: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_PWM_DutyCycle_Get (struct st_device *dev, SANE_Int * data)
+{
+ SANE_Byte a;
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_DutyCycle_Get:\n");
+
+ if (Read_Byte (dev->usb_handle, 0xe948, &a) == OK)
+ {
+ *data = a & 0x3f;
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- Lamp_PWM_DutyCycle_Get = %i: %i\n", *data, rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_PWM_DutyCycle_Set (struct st_device *dev, SANE_Int duty_cycle)
+{
+ SANE_Byte *Regs;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_DutyCycle_Set(duty_cycle=%i):\n", duty_cycle);
+
+ rst = ERROR;
+
+ Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ if (Regs != NULL)
+ {
+ if (RTS_ReadRegs (dev->usb_handle, Regs) == OK)
+ {
+ data_bitset (&Regs[0x148], 0x3f, duty_cycle);
+
+ if (pwmlamplevel == 0)
+ {
+ data_bitset (&Regs[0x148], 0x40, 0);
+ Regs[0x1e0] |= ((duty_cycle >> 1) & 0x40);
+ }
+
+ data_bitset (&dev->init_regs[0x148], 0x7f, Regs[0x148]);
+ data_bitset (&dev->init_regs[0x1e0], 0x3f, Regs[0x1e0]);
+
+ Write_Byte (dev->usb_handle, 0xe948, Regs[0x0148]);
+
+ rst = Write_Byte (dev->usb_handle, 0xe9e0, Regs[0x01e0]);
+ }
+
+ free (Regs);
+ }
+
+ DBG (DBG_FNC, "- Lamp_PWM_DutyCycle_Set: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Head_ParkHome (struct st_device *dev, SANE_Int bWait, SANE_Int movement)
+{
+ SANE_Int rst = ERROR;
+ SANE_Byte *Regs;
+
+ DBG (DBG_FNC, "+ Head_ParkHome(bWait=%i, movement=%i):\n", bWait, movement);
+
+ Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ if (Regs != NULL)
+ {
+ rst = OK;
+
+ memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ /* Lets wait if it's required when is already executing */
+ if (bWait != FALSE)
+ {
+ if (RTS_WaitScanEnd (dev, 0x3a98) != OK)
+ {
+ DBG (DBG_FNC, " -> Head_ParkHome: RTS_WaitScanEnd Timeout\n");
+ rst = ERROR; /* timeout */
+ }
+ }
+ else
+ {
+ if (RTS_IsExecuting (dev, Regs) == FALSE)
+ {
+ DBG (DBG_FNC,
+ " -> Head_ParkHome: RTS_IsExecuting = 0, exiting function\n");
+ rst = ERROR; /* if NOT executing */
+ }
+ }
+
+ /* Check if lamp is at home */
+ if ((rst == OK) && (Head_IsAtHome (dev, Regs) == FALSE))
+ {
+ struct st_motormove mymotor;
+ struct st_motorpos mtrpos;
+
+ DBG (DBG_FNC,
+ "-> Head_ParkHome: Lamp is not at home, lets move\n");
+
+ /* it isn't */
+ dev->status->parkhome = TRUE;
+
+ if ((movement != -1) && (movement < dev->motormove_count))
+ {
+ memcpy (&mymotor, dev->motormove[movement],
+ sizeof (struct st_motormove));
+ }
+ else
+ {
+ /* debug this code. Shouldn't have any relationship with offsets */
+ if (default_gain_offset->edcg2[CL_BLUE] < 4)
+ mymotor.scanmotorsteptype =
+ default_gain_offset->edcg2[CL_BLUE];
+
+ mymotor.ctpc = default_gain_offset->odcg2[1];
+ mymotor.systemclock = default_gain_offset->edcg2[1]; /*? */
+ }
+
+ mtrpos.options = MTR_ENABLED | MTR_BACKWARD;
+ mtrpos.v12e448 = 0x01;
+ mtrpos.v12e44c = 0x00;
+ mtrpos.coord_y = 0x4e20;
+
+ Motor_Move (dev, Regs, &mymotor, &mtrpos);
+
+ /* Should we wait? */
+ if (bWait != FALSE)
+ rst = RTS_WaitScanEnd (dev, 15000);
+
+ dev->status->parkhome = FALSE;
+ }
+
+ free (Regs);
+ }
+
+ DBG (DBG_FNC, "- Head_ParkHome: %i:\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Motor_Move (struct st_device *dev, SANE_Byte * Regs,
+ struct st_motormove *mymotor, struct st_motorpos *mtrpos)
+{
+ SANE_Byte *cpRegs;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Motor_Move:\n");
+
+ rst = ERROR;
+
+ cpRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ if (cpRegs != NULL)
+ {
+ SANE_Int data, v12dcf8, coord_y, step_type;
+
+ memcpy (cpRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ v12dcf8 = 0;
+
+ /* resolution = 1 dpi */
+ data_bitset (&cpRegs[0xc0], 0x1f, 1); /*---xxxxx*/
+
+ /* set motor step type */
+ data_bitset (&cpRegs[0xd9], 0x70, mymotor->scanmotorsteptype); /*-xxx----*/
+
+ /* set motor direction (polarity) */
+ data_bitset (&cpRegs[0xd9], 0x80, mtrpos->options >> 3); /*e------- */
+
+ /* next value doesn't seem to have any effect */
+ data_bitset (&cpRegs[0xd9], 0x0f, mtrpos->options); /*----efgh*/
+
+ /* 0 enable/1 disable motor */
+ data_bitset (&cpRegs[0xdd], 0x80, mtrpos->options >> 4); /*d------- */
+
+ /* next value doesn't seem to have any effect */
+ data_bitset (&cpRegs[0xdd], 0x40, mtrpos->options >> 4); /*-d------*/
+
+ switch (mymotor->scanmotorsteptype)
+ {
+ case STT_OCT:
+ step_type = 8;
+ break;
+ case STT_QUART:
+ step_type = 4;
+ break;
+ case STT_HALF:
+ step_type = 2;
+ break;
+ case STT_FULL:
+ step_type = 1;
+ break;
+ default:
+ step_type = 0;
+ break; /* shouldn't be used */
+ }
+
+ coord_y = (mtrpos->coord_y * step_type) & 0xffff;
+ if (coord_y < 2)
+ coord_y = 2;
+
+ /* Sets dummyline to 1 */
+ data_bitset (&cpRegs[0xd6], 0xf0, 1);
+
+ /* set step_size - 1 */
+ cpRegs[0xe0] = 0;
+
+ cpRegs[0x01] &= 0xf9;
+ cpRegs[0x01] |= (mtrpos->v12e448 & 1) << 2;
+
+ /* set dummy scan */
+ data_bitset (&cpRegs[0x01], 0x10, 1); /*---x----*/
+
+ /* set samplerate */
+ data_bitset (&cpRegs[0x1cf], 0x40, PIXEL_RATE); /*-x------*/
+
+ /* unknown data */
+ data_bitset (&cpRegs[0x1cf], 0x80, 1); /*x------- */
+
+ /* sets one chanel per color */
+ data_bitset (&cpRegs[0x12], 0x3f, 0); /* channel */
+ data_bitset (&cpRegs[0x12], 0xc0, 1); /* 1 channel */
+
+ /* timing cnpp */
+ data_bitset (&cpRegs[0x96], 0x3f, 0x0b); /*--001011*/
+
+ /* set systemclock */
+ data_bitset (&cpRegs[0x00], 0x0f, mymotor->systemclock); /*----xxxx*/
+
+ /* set last step of accurve.smearing table to 2 */
+ data_lsb_set (&cpRegs[0xe4], 2, 3);
+
+ /* set last step of deccurve.scanbufferfull table to 16 */
+ data_lsb_set (&Regs[0xea], 0x10, 3);
+
+ /* set last step of deccurve.normalscan table to 16 */
+ data_lsb_set (&Regs[0xed], 0x10, 3);
+
+ /* set last step of deccurve.smearing table to 16 */
+ data_lsb_set (&Regs[0xf0], 0x10, 3);
+
+ /* set last step of deccurve.parkhome table to 16 */
+ data_lsb_set (&Regs[0xf3], 0x10, 3);
+
+ /* set msi */
+ cpRegs[0xda] = 2;
+ cpRegs[0xdd] &= 0xfc;
+
+ /* set if motor has motorcurves */
+ data_bitset (&cpRegs[0xdf], 0x10,
+ ((mymotor->motorcurve != -1) ? 1 : 0));
+
+ if (mymotor->motorcurve != -1)
+ {
+ struct st_curve *crv;
+
+ /* set last step of accurve.normalscan table */
+ crv =
+ Motor_Curve_Get (dev, mymotor->motorcurve, ACC_CURVE,
+ CRV_NORMALSCAN);
+ if (crv != NULL)
+ data_lsb_set (&cpRegs[0xe1], crv->step[crv->step_count - 1], 3);
+
+ DBG (DBG_FNC, " -> Setting up stepper motor using motorcurve %i\n",
+ mymotor->motorcurve);
+ v12dcf8 = Motor_Setup_Steps (dev, cpRegs, mymotor->motorcurve);
+
+ /* set step_size - 1 */
+ cpRegs[0xe0] = 0;
+
+ crv =
+ Motor_Curve_Get (dev, mymotor->motorcurve, DEC_CURVE,
+ CRV_NORMALSCAN);
+ if (crv != NULL)
+ coord_y -= (v12dcf8 + crv->step_count);
+
+ /* set line exposure time */
+ data_lsb_set (&cpRegs[0x30], mymotor->ctpc, 3);
+
+ /* set last step of accurve.smearing table */
+ data_lsb_set (&cpRegs[0xe4], 0, 3);
+ }
+ else
+ {
+ /* Setting some motor step */
+ SANE_Int some_step;
+
+ switch (Regs[0x00] & 0x0f)
+ {
+ case 0x00:
+ some_step = 0x00895440;
+ break; /* 3 x 0x2DC6C0 */
+ case 0x08:
+ case 0x01:
+ some_step = 0x00b71b00;
+ break; /* 4 x 0x2DC6C0 */
+ case 0x02:
+ some_step = 0x0112a880;
+ break; /* 6 x 0x2DC6C0 */
+ case 0x0a:
+ case 0x03:
+ some_step = 0x016e3600;
+ break; /* 8 x 0x2DC6C0 */
+ case 0x04:
+ some_step = 0x02255100;
+ break; /* 12 x 0x2DC6C0 */
+ case 0x0c:
+ some_step = 0x02dc6c00;
+ break; /* 16 x 0x2DC6C0 */
+ case 0x05:
+ some_step = 0x044aa200;
+ break; /* 24 x 0x2DC6C0 */
+ case 0x0d:
+ some_step = 0x05b8d800;
+ break; /* 32 x 0x2DC6C0 */
+
+ case 0x09:
+ some_step = 0x00f42400;
+ break;
+ case 0x0b:
+ some_step = 0x01e84800;
+ break; /* = case 9 * 2 */
+ default:
+ some_step = 0x0478f7f8;
+ break;
+ }
+
+ /* divide by timing.cnpp */
+ some_step /= ((cpRegs[0x96] & 0x3f) + 1);
+ if (mymotor->ctpc > 0)
+ some_step /= mymotor->ctpc;
+
+ /* set line exposure time */
+ data_lsb_set (&cpRegs[0x30], some_step, 3);
+
+ /* set last step of accurve.normalscan table */
+ data_lsb_set (&cpRegs[0xe1], some_step, 3);
+ }
+
+ /* Setting coords */
+ RTS_Setup_Coords (cpRegs, 100, coord_y - 1, 800, 1);
+
+ /* enable head movement */
+ data_bitset (&cpRegs[0xd8], 0x80, 1);
+
+ /* release motor before executing */
+ Motor_Release (dev);
+
+ RTS_Warm_Reset (dev);
+
+ /* action! */
+ data = RTS_WriteRegs (dev->usb_handle, cpRegs);
+ if (data == OK)
+ RTS_Execute (dev);
+
+ /* wait 10 seconds */
+ RTS_WaitScanEnd (dev, 10000);
+
+ rst = (data != OK) ? v12dcf8 : RTS_WaitScanEnd (dev, 20000);
+
+ free (cpRegs);
+ }
+
+ DBG (DBG_FNC, "- Motor_Move: %i\n", rst);
+
+ return rst;
+}
+
+static void
+Free_Motormoves (struct st_device *dev)
+{
+ DBG (DBG_FNC, "> Free_Motormoves\n");
+
+ if (dev->motormove != NULL)
+ {
+ SANE_Int a;
+ struct st_motormove *ms;
+
+ for (a = 0; a < dev->motormove_count; a++)
+ {
+ ms = dev->motormove[a];
+ if (ms != NULL)
+ free (ms);
+ }
+
+ free (dev->motormove);
+ dev->motormove = NULL;
+ }
+
+ dev->motormove_count = 0;
+}
+
+static void
+Free_MotorCurves (struct st_device *dev)
+{
+ DBG (DBG_FNC, "> Free_MotorCurves\n");
+ if (dev->mtrsetting != NULL)
+ Motor_Curve_Free (dev->mtrsetting, &dev->mtrsetting_count);
+
+ dev->mtrsetting = NULL;
+ dev->mtrsetting_count = 0;
+}
+
+static SANE_Int
+Load_MotorCurves (struct st_device *dev)
+{
+ SANE_Int rst = ERROR;
+ SANE_Int *mtc = NULL;
+
+ if (dev->mtrsetting != NULL)
+ Free_MotorCurves (dev);
+
+ DBG (DBG_FNC, "> Load_MotorCurves\n");
+
+ /* get motor setttings buffer for this device */
+ mtc = cfg_motorcurve_get ();
+ if (mtc != NULL)
+ {
+ /* parse buffer to get all motorcurves */
+ dev->mtrsetting = Motor_Curve_Parse (&dev->mtrsetting_count, mtc);
+ if (dev->mtrsetting != NULL)
+ rst = OK;
+ }
+
+ if (rst != ERROR)
+ {
+ DBG (DBG_FNC, " -> Found %i motor settings\n", dev->mtrsetting_count);
+ dbg_motorcurves (dev);
+ }
+ else
+ DBG (DBG_ERR, "- Load_MotorCurves error!!\n");
+
+ return rst;
+}
+
+static SANE_Int
+Load_Motormoves (struct st_device *dev)
+{
+ SANE_Int rst = OK;
+ SANE_Int a;
+ struct st_motormove reg, *mm;
+
+ DBG (DBG_FNC, "> Load_Motormoves\n");
+
+ /* if there is already any movement loaded let's free it */
+ if (dev->motormove != NULL)
+ Free_Motormoves (dev);
+
+ a = 0;
+ while ((cfg_motormove_get (dev->sensorcfg->type, a, &reg) != ERROR)
+ && (rst == OK))
+ {
+ dev->motormove_count++;
+ dev->motormove =
+ (struct st_motormove **) realloc (dev->motormove,
+ sizeof (struct st_motormove **) *
+ dev->motormove_count);
+ if (dev->motormove != NULL)
+ {
+ mm = (struct st_motormove *) malloc (sizeof (struct st_motormove));
+ if (mm != NULL)
+ {
+ memcpy (mm, &reg, sizeof (struct st_motormove));
+ dev->motormove[dev->motormove_count - 1] = mm;
+ }
+ else
+ rst = ERROR;
+ }
+ else
+ rst = ERROR;
+
+ a++;
+ }
+
+ if (rst == ERROR)
+ Free_Motormoves (dev);
+
+ DBG (DBG_FNC, " -> Found %i motormoves\n", dev->motormove_count);
+ dbg_motormoves (dev);
+
+ return rst;
+}
+
+static SANE_Byte *
+Motor_AddStep (SANE_Byte * steps, SANE_Int * bwriten, SANE_Int step)
+{
+ steps = (SANE_Byte *) realloc (steps, sizeof (SANE_Byte) * (*bwriten + 3));
+ if (steps != NULL)
+ {
+ data_msb_set (&steps[*bwriten], step, 3);
+ *bwriten += 3;
+ }
+ else
+ *bwriten = 0;
+
+ return steps;
+}
+
+static SANE_Int
+Motor_Setup_Steps (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int mysetting)
+{
+ SANE_Int varx10, cont, last_acc_step, varx20, stepbuffer_size,
+ mystep, bwriten;
+ SANE_Int myvar, var1, myvalor, mybwriten;
+ struct st_curve *mycurve;
+ SANE_Byte *steps;
+
+ DBG (DBG_FNC, "+ Motor_Setup_Steps(*Regs, motorsetting=%i):\n", mysetting);
+
+ varx10 = 0;
+ cont = 0;
+ varx20 = 0;
+ stepbuffer_size = (v15f8 << 4) & 0xffff;
+ steps = NULL;
+ bwriten = 0;
+ deccurvecount = 0;
+ acccurvecount = 0;
+ last_acc_step = 0;
+
+ /* mycurve points to acc normalscan steps table */
+ mycurve = Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_NORMALSCAN);
+
+ if (mycurve != NULL)
+ {
+ /* acccurvecount has the number of steps in acc normalscan table */
+ acccurvecount = mycurve->step_count;
+
+ /* get last acccurve step from acc.normalscan step table */
+ last_acc_step = data_lsb_get (&Regs[0xe1], 3);
+
+ /* sets pointer to acc.normalscan step table */
+ data_wide_bitset (&Regs[0xf6], 0x3fff, stepbuffer_size);
+
+ /* Separate each step in three bytes */
+ if (mycurve->step_count > 0)
+ for (cont = 0; cont < mycurve->step_count; cont++)
+ {
+ mystep = mycurve->step[cont];
+ if (mystep <= last_acc_step)
+ {
+ acccurvecount = cont;
+ break;
+ }
+ varx20 += mystep + 1;
+ steps = Motor_AddStep (steps, &bwriten, mystep);
+ }
+ }
+
+ if (acccurvecount == 0)
+ {
+ /* Write one step (last_acc_step + 0x01) to buffer */
+ acccurvecount++;
+ varx20 += (last_acc_step + 1) + 1;
+ steps = Motor_AddStep (steps, &bwriten, last_acc_step + 1);
+ }
+
+ /* write another step (last_acc_step) */
+ acccurvecount++;
+ varx20 += last_acc_step + 1;
+ steps = Motor_AddStep (steps, &bwriten, last_acc_step);
+
+ /* get line exposure time */
+ myvar = data_lsb_get (&Regs[0x30], 3) + 1;
+
+ var1 = (varx20 + myvar - 1) / myvar;
+ var1 = ((var1 * myvar) + mycurve->step[0] - varx20) - 0x0d;
+ if (steps != NULL)
+ data_msb_set (&steps[0], var1, 3);
+
+ /* dec.scanbufferfull step table */
+ /* set pointer to next table */
+ stepbuffer_size += (acccurvecount * 3);
+ data_wide_bitset (&Regs[0xf8], 0x3fff, stepbuffer_size);
+
+ /* set last step of deccurve.scanbufferfull table */
+ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_BUFFERFULL);
+ deccurvecount = mycurve->step_count;
+ data_lsb_set (&Regs[0xea], mycurve->step[mycurve->step_count - 1], 3);
+
+ /* write another step mycurve->step_count,cont,last_acc_step */
+ deccurvecount++;
+ steps = Motor_AddStep (steps, &bwriten, last_acc_step);
+
+ /* Separate each step in three bytes */
+ if (mycurve->step_count > 1)
+ for (cont = 0; cont < (mycurve->step_count - 1); cont++)
+ {
+ mystep = mycurve->step[cont];
+ if (mystep > last_acc_step)
+ steps = Motor_AddStep (steps, &bwriten, mystep);
+ else
+ deccurvecount--;
+ }
+
+ myvalor = dev->mtrsetting[mysetting]->motorbackstep;
+ if (myvalor > 0)
+ {
+ SANE_Int step_size = _B0 (Regs[0xe0]) + 1;
+
+ myvalor = ((myvalor - deccurvecount) - acccurvecount) + 2;
+ varx10 = myvalor;
+ myvalor /= step_size;
+ myvalor *= step_size;
+ var1 = mycurve->step[mycurve->step_count - 1]; /* last deccurve step */
+ if (last_acc_step >= var1)
+ var1 = last_acc_step + 1;
+ deccurvecount += (varx10 - myvalor);
+ myvalor = varx10 - myvalor;
+ }
+ else
+ myvalor = varx10;
+
+ if (myvalor > 0)
+ for (cont = myvalor; cont > 0; cont--)
+ steps = Motor_AddStep (steps, &bwriten, var1 - 1);
+
+ /* write another step , bwriten tiene 4b */
+ steps = Motor_AddStep (steps, &bwriten, var1);
+
+ /* acc.smearing step table */
+ if (Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_SMEARING) != NULL)
+ {
+ /* acc.smearing curve enabled */
+ if (Motor_Curve_Equal
+ (dev, mysetting, ACC_CURVE, CRV_SMEARING, CRV_NORMALSCAN) == TRUE)
+ {
+ /* acc.smearing pointer points to acc.normalscan table */
+ data_wide_bitset (&Regs[0xfa], 0x3fff,
+ data_lsb_get (&Regs[0xf6], 2));
+ /* last step of acc.smearing table is the same as acc.normalscan */
+ data_lsb_set (&Regs[0xe4], data_lsb_get (&Regs[0xe1], 3), 3);
+ }
+ else
+ {
+ /* set pointer to next step table */
+ stepbuffer_size += (deccurvecount * 3);
+ data_wide_bitset (&Regs[0xfa], 0x3fff, stepbuffer_size);
+
+ /* set last step of acc.smearing table */
+ mycurve = Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_SMEARING);
+ if (mycurve != NULL)
+ {
+ smearacccurvecount = mycurve->step_count;
+ data_lsb_set (&Regs[0xe4],
+ mycurve->step[mycurve->step_count - 1], 3);
+
+ /* generate acc.smearing table */
+ if (mycurve->step_count > 0)
+ for (cont = 0; cont < mycurve->step_count; cont++)
+ steps =
+ Motor_AddStep (steps, &bwriten, mycurve->step[cont]);
+ }
+ }
+ }
+ else
+ {
+ /* acc.smearing curve disabled */
+ data_wide_bitset (&Regs[0xfa], 0x3fff, 0);
+ }
+
+ /* dec.smearing */
+ if (Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_SMEARING) != NULL)
+ {
+ /* dec.smearing curve enabled */
+ if (Motor_Curve_Equal
+ (dev, mysetting, DEC_CURVE, CRV_SMEARING, CRV_BUFFERFULL) == TRUE)
+ {
+ /* dec.smearing pointer points to dec.scanbufferfull table */
+ data_wide_bitset (&Regs[0x00fc], 0x3fff,
+ data_lsb_get (&Regs[0x00f8], 2));
+ /* last step of dec.smearing table is the same as dec.scanbufferfull */
+ data_lsb_set (&Regs[0x00f0], data_lsb_get (&Regs[0x00ea], 3), 3);
+ }
+ else
+ {
+ /* set pointer to next step table */
+ if (mycurve != NULL)
+ stepbuffer_size += (mycurve->step_count * 3);
+ data_wide_bitset (&Regs[0xfc], 0x3fff, stepbuffer_size);
+
+ /* set last step of dec.smearing table */
+ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_SMEARING);
+ if (mycurve != NULL)
+ {
+ smeardeccurvecount = mycurve->step_count;
+ data_lsb_set (&Regs[0xf0],
+ mycurve->step[mycurve->step_count - 1], 3);
+
+ /* generate dec.smearing table */
+ if (mycurve->step_count > 0)
+ for (cont = 0; cont < mycurve->step_count; cont++)
+ steps =
+ Motor_AddStep (steps, &bwriten, mycurve->step[cont]);
+ }
+ }
+ }
+ else
+ {
+ /* dec.smearing curve disabled */
+ data_wide_bitset (&Regs[0x00fc], 0x3fff, 0);
+ }
+
+ /* dec.normalscan */
+ if (Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN) != NULL)
+ {
+ /* dec.normalscan enabled */
+ if (Motor_Curve_Equal
+ (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN, CRV_BUFFERFULL) == TRUE)
+ {
+ /* dec.normalscan pointer points to dec.scanbufferfull table */
+ data_wide_bitset (&Regs[0xfe], 0x3fff,
+ data_lsb_get (&Regs[0xf8], 2));
+ /* last step of dec.normalscan table is the same as dec.scanbufferfull */
+ data_lsb_set (&Regs[0xed], data_lsb_get (&Regs[0xea], 3), 3);
+ }
+ else
+ if (Motor_Curve_Equal
+ (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN, CRV_SMEARING) == TRUE)
+ {
+ /* dec.normalscan pointer points to dec.smearing table */
+ data_wide_bitset (&Regs[0xfe], 0x3fff,
+ data_lsb_get (&Regs[0xfc], 2));
+ /* last step of dec.normalscan table is the same as dec.smearing */
+ data_lsb_set (&Regs[0xed], data_lsb_get (&Regs[0xf0], 3), 3);
+ }
+ else
+ {
+ /* set pointer to next step table */
+ if (mycurve != NULL)
+ stepbuffer_size += (mycurve->step_count * 3);
+ data_wide_bitset (&Regs[0xfe], 0x3fff, stepbuffer_size);
+
+ /* set last step of dec.normalscan table */
+ mycurve =
+ Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN);
+ if (mycurve != NULL)
+ {
+ data_lsb_set (&Regs[0xed],
+ mycurve->step[mycurve->step_count - 1], 3);
+
+ /* generate dec.normalscan table */
+ if (mycurve->step_count > 0)
+ for (cont = 0; cont < mycurve->step_count; cont++)
+ steps =
+ Motor_AddStep (steps, &bwriten, mycurve->step[cont]);
+ }
+ }
+ }
+ else
+ {
+ /* dec.normalscan disabled */
+ data_wide_bitset (&Regs[0xfe], 0x3fff, 0);
+ }
+
+ /* acc.parkhome */
+ if (Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_PARKHOME) != NULL)
+ {
+ /* parkhome curve enabled */
+
+ if (Motor_Curve_Equal
+ (dev, mysetting, ACC_CURVE, CRV_PARKHOME, CRV_NORMALSCAN) == TRUE)
+ {
+ /* acc.parkhome pointer points to acc.normalscan table */
+ data_wide_bitset (&Regs[0x100], 0x3fff,
+ data_lsb_get (&Regs[0xf6], 2));
+
+ /* last step of acc.parkhome table is the same as acc.normalscan */
+ data_lsb_set (&Regs[0xe7], data_lsb_get (&Regs[0xe1], 3), 3);
+ }
+ else
+ if (Motor_Curve_Equal
+ (dev, mysetting, ACC_CURVE, CRV_PARKHOME, CRV_SMEARING) == TRUE)
+ {
+ /* acc.parkhome pointer points to acc.smearing table */
+ data_wide_bitset (&Regs[0x100], 0x3fff,
+ data_lsb_get (&Regs[0xfa], 2));
+ /* last step of acc.parkhome table is the same as acc.smearing */
+ data_lsb_set (&Regs[0xe7], data_lsb_get (&Regs[0xe4], 3), 3);
+ }
+ else
+ {
+ /* set pointer to next step table */
+ if (mycurve != NULL)
+ stepbuffer_size += (mycurve->step_count * 3);
+ data_wide_bitset (&Regs[0x100], 0x3fff, stepbuffer_size);
+
+ /* set last step of acc.parkhome table */
+ mycurve = Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_PARKHOME);
+ if (mycurve != NULL)
+ {
+ data_lsb_set (&Regs[0xe7],
+ mycurve->step[mycurve->step_count - 1], 3);
+
+ /* generate acc.parkhome table */
+ if (mycurve->step_count > 0)
+ for (cont = 0; cont < mycurve->step_count; cont++)
+ steps =
+ Motor_AddStep (steps, &bwriten, mycurve->step[cont]);
+ }
+ }
+ }
+ else
+ {
+ /* parkhome curve is disabled */
+ /* acc.parkhome pointer points to 0 */
+ data_wide_bitset (&Regs[0x100], 0x3fff, 0);
+ data_lsb_set (&Regs[0xe7], 16, 3);
+ }
+
+ /* dec.parkhome */
+ if (Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_PARKHOME) != NULL)
+ {
+ /* parkhome curve enabled */
+ if (Motor_Curve_Equal
+ (dev, mysetting, DEC_CURVE, CRV_PARKHOME, CRV_BUFFERFULL) == TRUE)
+ {
+ /* dec.parkhome pointer points to dec.scanbufferfull table */
+ data_wide_bitset (&Regs[0x102], 0x3fff,
+ data_lsb_get (&Regs[0xf8], 2));
+ /* last step of dec.parkhome table is the same as dec.scanbufferfull */
+ data_lsb_set (&Regs[0xf3], data_lsb_get (&Regs[0xe4], 3), 3);
+ }
+ else
+ if (Motor_Curve_Equal
+ (dev, mysetting, DEC_CURVE, CRV_PARKHOME, CRV_SMEARING) == TRUE)
+ {
+ /* dec.parkhome pointer points to dec.smearing table */
+ data_wide_bitset (&Regs[0x102], 0x3fff,
+ data_lsb_get (&Regs[0xfe], 2));
+ /* last step of dec.parkhome table is the same as dec.smearing */
+ data_lsb_set (&Regs[0xf3], data_lsb_get (&Regs[0xf0], 3), 3);
+ }
+ else
+ if (Motor_Curve_Equal
+ (dev, mysetting, DEC_CURVE, CRV_PARKHOME, CRV_NORMALSCAN) == TRUE)
+ {
+ /* dec.parkhome pointer points to dec.normalscan table */
+ data_wide_bitset (&Regs[0x102], 0x3fff,
+ data_lsb_get (&Regs[0xfe], 2));
+ /* last step of dec.parkhome table is the same as dec.normalscan */
+ data_lsb_set (&Regs[0xf3], data_lsb_get (&Regs[0xed], 3), 3);
+ }
+ else
+ {
+ /* set pointer to next step table */
+ if (mycurve != NULL)
+ stepbuffer_size += (mycurve->step_count * 3);
+ data_wide_bitset (&Regs[0x102], 0x3fff, stepbuffer_size);
+
+ /* set last step of dec.parkhome table */
+ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_PARKHOME);
+ if (mycurve != NULL)
+ {
+ data_lsb_set (&Regs[0xf3],
+ mycurve->step[mycurve->step_count - 1], 3);
+
+ /* generate dec.parkhome table */
+ if (mycurve->step_count > 0)
+ for (cont = 0; cont < mycurve->step_count; cont++)
+ steps =
+ Motor_AddStep (steps, &bwriten, mycurve->step[cont]);
+ }
+ }
+ }
+ else
+ {
+ /* parkhome curve is disabled */
+
+ /* dec.parkhome pointer points to 0 */
+ data_wide_bitset (&Regs[0x102], 0x3fff, 0);
+ data_lsb_set (&Regs[0xf3], 16, 3);
+ }
+
+ mybwriten = bwriten & 0x8000000f;
+ if (mybwriten < 0)
+ mybwriten = ((mybwriten - 1) | 0xfffffff0) + 1;
+
+ if (mybwriten != 0)
+ bwriten = (((bwriten & 0xffff) >> 0x04) + 1) << 0x04;
+ bwriten = bwriten & 0xffff;
+
+ /* display table */
+ DBG (DBG_FNC, " -> Direction Type Offset Last step\n");
+ DBG (DBG_FNC, " -> --------- -------------- ------ ---------\n");
+ DBG (DBG_FNC, " -> ACC_CURVE CRV_NORMALSCAN %6i %6i\n",
+ data_lsb_get (&Regs[0x0f6], 2) & 0x3fff, data_lsb_get (&Regs[0x0e1],
+ 3));
+ DBG (DBG_FNC, " -> ACC_CURVE CRV_SMEARING %6i %6i\n",
+ data_lsb_get (&Regs[0x0fa], 2) & 0x3fff, data_lsb_get (&Regs[0x0e4],
+ 3));
+ DBG (DBG_FNC, " -> ACC_CURVE CRV_PARKHOME %6i %6i\n",
+ data_lsb_get (&Regs[0x100], 2) & 0x3fff, data_lsb_get (&Regs[0x0e7],
+ 3));
+ DBG (DBG_FNC, " -> DEC_CURVE CRV_NORMALSCAN %6i %6i\n",
+ data_lsb_get (&Regs[0x0fe], 2) & 0x3fff, data_lsb_get (&Regs[0x0ed],
+ 3));
+ DBG (DBG_FNC, " -> DEC_CURVE CRV_SMEARING %6i %6i\n",
+ data_lsb_get (&Regs[0x0fc], 2) & 0x3fff, data_lsb_get (&Regs[0x0f0],
+ 3));
+ DBG (DBG_FNC, " -> DEC_CURVE CRV_PARKHOME %6i %6i\n",
+ data_lsb_get (&Regs[0x102], 2) & 0x3fff, data_lsb_get (&Regs[0x0f3],
+ 3));
+ DBG (DBG_FNC, " -> DEC_CURVE CRV_BUFFERFULL %6i %6i\n",
+ data_lsb_get (&Regs[0x0f8], 2) & 0x3fff, data_lsb_get (&Regs[0x0ea],
+ 3));
+
+ RTS_Warm_Reset (dev);
+
+ /* send motor steps */
+ if (steps != NULL)
+ {
+ if (bwriten > 0)
+ {
+ /* lock */
+ SetLock (dev->usb_handle, Regs, TRUE);
+
+ /* send steps */
+ RTS_DMA_Write (dev, 0x0000, v15f8, bwriten, steps);
+
+ /* unlock */
+ SetLock (dev->usb_handle, Regs, FALSE);
+ }
+
+ free (steps);
+ }
+
+ DBG (DBG_FNC, "- Motor_Setup_Steps: %i\n", acccurvecount);
+
+ return acccurvecount;
+}
+
+static SANE_Int
+Lamp_PWM_use (struct st_device *dev, SANE_Int enable)
+{
+ SANE_Byte a, b;
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_use(enable=%i):\n", enable);
+
+ if (Read_Byte (dev->usb_handle, 0xe948, &a) == OK)
+ {
+ if (Read_Byte (dev->usb_handle, 0xe9e0, &b) == OK)
+ {
+ if (enable != 0)
+ {
+ if (pwmlamplevel != 0x00)
+ {
+ b |= 0x80;
+ dev->init_regs[0x01e0] &= 0x3f;
+ dev->init_regs[0x01e0] |= 0x80;
+ }
+ else
+ {
+ a |= 0x40;
+ b &= 0x3f;
+ dev->init_regs[0x0148] |= 0x40;
+ dev->init_regs[0x01e0] &= 0x3f;
+ }
+ }
+ else
+ {
+ b &= 0x7f;
+ a &= 0xbf;
+ }
+
+ if (Write_Byte (dev->usb_handle, 0xe948, a) == OK)
+ rst = Write_Byte (dev->usb_handle, 0xe9e0, b);
+ }
+ }
+
+ DBG (DBG_FNC, "- Lamp_PWM_use: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+SSCG_Enable (struct st_device *dev)
+{
+ SANE_Int rst;
+ SANE_Int sscg;
+ SANE_Byte data1, data2;
+ SANE_Int enable, mode, clock;
+
+ DBG (DBG_FNC, "+ SSCG_Enable:\n");
+
+ rst = cfg_sscg_get (&enable, &mode, &clock);
+
+ if (rst == OK)
+ {
+ if ((Read_Byte (dev->usb_handle, 0xfe3a, &data1) == OK)
+ && (Read_Byte (dev->usb_handle, 0xfe39, &data2) == OK))
+ {
+ if (enable != FALSE)
+ {
+ /* clock values: 0=0.25%; 1=0.50%; 2=0.75%; 3=1.00% */
+ data2 = (mode == 0) ? data2 & 0x7f : data2 | 0x80;
+
+ sscg = (data1 & 0xf3) | (((clock & 0x03) | 0x04) << 0x02);
+ sscg = (sscg << 8) | data2;
+ }
+ else
+ sscg = ((data1 & 0xef) << 8) | _B0 (data2);
+
+ rst = Write_Word (dev->usb_handle, 0xfe39, sscg);
+ }
+ else
+ rst = ERROR;
+ }
+
+ DBG (DBG_FNC, "- SSCG_Enable: %i\n", rst);
+
+ return rst;
+}
+
+static void
+RTS_Setup_RefVoltages (struct st_device *dev, SANE_Byte * Regs)
+{
+ /* this function sets top, midle and bottom reference voltages */
+
+ DBG (DBG_FNC, "> RTS_Setup_RefVoltages\n");
+
+ if (Regs != NULL)
+ {
+ SANE_Byte vrts, vrms, vrbs;
+
+ cfg_refvoltages_get (dev->sensorcfg->type, &vrts, &vrms, &vrbs);
+
+ /* Top Reference Voltage */
+ data_bitset (&Regs[0x14], 0xe0, vrts); /*xxx----- */
+
+ /* Middle Reference Voltage */
+ data_bitset (&Regs[0x15], 0xe0, vrms); /*xxx----- */
+
+ /* Bottom Reference Voltage */
+ data_bitset (&Regs[0x16], 0xe0, vrbs); /*xxx----- */
+ }
+}
+
+static SANE_Int
+Init_USBData (struct st_device *dev)
+{
+ SANE_Byte data;
+ SANE_Byte *resource;
+
+ DBG (DBG_FNC, "+ Init_USBData:\n");
+
+ if (Read_Byte (dev->usb_handle, 0xf8ff, &data) != OK)
+ return ERROR;
+
+ data = (data | 1);
+ if (Write_Byte (dev->usb_handle, 0xf8ff, data) != OK)
+ return ERROR;
+
+ if (SSCG_Enable (dev) != OK)
+ return ERROR;
+
+ Init_Registers (dev);
+
+ /* Gamma table size = 0x100 */
+ data_bitset (&dev->init_regs[0x1d0], 0x30, 0x00); /*--00----*/
+
+ /* Set 3 channels_per_dot */
+ data_bitset (&dev->init_regs[0x12], 0xc0, 0x03); /*xx------ */
+
+ /* systemclock */
+ data_bitset (&dev->init_regs[0x00], 0x0f, 0x05); /*----xxxx*/
+
+ /* timing cnpp */
+ data_bitset (&dev->init_regs[0x96], 0x3f, 0x17); /*--xxxxxx*/
+
+ /* set sensor_channel_color_order */
+ data_bitset (&dev->init_regs[0x60a], 0x7f, 0x24); /*-xxxxxxx*/
+
+ /* set crvs */
+ data_bitset (&dev->init_regs[0x10], 0x1f, get_value (SCAN_PARAM, CRVS, 7, usbfile)); /*---xxxxx*/
+
+ /* set reference voltages */
+ RTS_Setup_RefVoltages (dev, dev->init_regs);
+
+ dev->init_regs[0x11] |= 0x10;
+
+ data_bitset (&dev->init_regs[0x26], 0x70, 5); /*-101----*/
+
+ dev->init_regs[0x185] = 0x88;
+ dev->init_regs[0x186] = 0x88;
+
+ /* SDRAM clock */
+ data = get_value (SCAN_PARAM, MCLKIOC, 8, usbfile);
+ data_bitset (&dev->init_regs[0x187], 0x0f, 0x08); /*----xxxx*/
+ data_bitset (&dev->init_regs[0x187], 0xf0, data); /*xxxx---- */
+
+ data--;
+
+ if (data < 7)
+ {
+ switch (data)
+ {
+ case 0:
+ data |= 0xc0;
+ break;
+ case 1:
+ data |= 0xa0;
+ break;
+ case 2:
+ data |= 0xe0;
+ break;
+ case 3:
+ data |= 0x90;
+ break;
+ case 4:
+ data |= 0xd0;
+ break;
+ case 5:
+ data |= 0xb0;
+ break;
+ case 6:
+ data = (data & 0x0f);
+ break;
+ }
+ dev->init_regs[0x187] = _B0 (data);
+ }
+
+ data_bitset (&dev->init_regs[0x26], 0x0f, 0); /*----0000*/
+
+ dev->init_regs[0x27] &= 0x3f;
+ dev->init_regs[0x29] = 0x24;
+ dev->init_regs[0x2a] = 0x10;
+ dev->init_regs[0x150] = 0xff;
+ dev->init_regs[0x151] = 0x13;
+ dev->init_regs[0x156] = 0xf0;
+ dev->init_regs[0x157] = 0xfd;
+
+ if (dev->motorcfg->changemotorcurrent != FALSE)
+ Motor_Change (dev, dev->init_regs, 3);
+
+ dev->init_regs[0xde] = 0;
+ data_bitset (&dev->init_regs[0xdf], 0x0f, 0);
+
+ /* loads motor resource for this dev */
+ resource = cfg_motor_resource_get (&data);
+ if ((resource != NULL) && (data > 1))
+ memcpy (&dev->init_regs[0x104], resource, data);
+
+ /* this bit is set but I don't know its purpose */
+ dev->init_regs[0x01] |= 0x40; /*-1------*/
+
+ dev->init_regs[0x124] = 0x94;
+
+ /* release motor */
+ Motor_Release (dev);
+
+ DBG (DBG_FNC, "- Init_USBData:\n");
+
+ return OK;
+}
+
+static SANE_Int
+Init_Registers (struct st_device *dev)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Init_Registers:\n");
+
+ /* Lee dev->init_regs */
+ bzero (dev->init_regs, RT_BUFFER_LEN);
+ RTS_ReadRegs (dev->usb_handle, dev->init_regs);
+ Read_FE3E (dev, &v1619);
+
+ if (dev->sensorcfg->type == CCD_SENSOR)
+ {
+ /* CCD sensor */
+ data_bitset (&dev->init_regs[0x11], 0xc0, 0); /*xx------ */
+ data_bitset (&dev->init_regs[0x146], 0x80, 1); /*x------- */
+ data_bitset (&dev->init_regs[0x146], 0x40, 1); /*-x------*/
+
+ }
+ else
+ {
+ /* CIS sensor */
+ data_bitset (&dev->init_regs[0x146], 0x80, 0); /*0------- */
+ data_bitset (&dev->init_regs[0x146], 0x40, 0); /*-0------*/
+ data_bitset (&dev->init_regs[0x11], 0xc0, 2); /*xx------ */
+ data_bitset (&dev->init_regs[0xae], 0x3f, 0x14); /*--xxxxxx*/
+ data_bitset (&dev->init_regs[0xaf], 0x07, 1); /*-----xxx*/
+
+ dev->init_regs[0x9c] = dev->init_regs[0xa2] = dev->init_regs[0xa8] =
+ (RTS_Debug->dev_model != UA4900) ? 1 : 0;
+ dev->init_regs[0x9d] = dev->init_regs[0xa3] = dev->init_regs[0xa9] = 0;
+ dev->init_regs[0x9e] = dev->init_regs[0xa4] = dev->init_regs[0xaa] = 0;
+ dev->init_regs[0x9f] = dev->init_regs[0xa5] = dev->init_regs[0xab] = 0;
+ dev->init_regs[0xa0] = dev->init_regs[0xa6] = dev->init_regs[0xac] = 0;
+ dev->init_regs[0xa1] = dev->init_regs[0xa7] = dev->init_regs[0xad] =
+ (RTS_Debug->dev_model != UA4900) ? 0x80 : 0;
+ }
+
+ /* disable CCD channels */
+ data_bitset (&dev->init_regs[0x10], 0xe0, 0); /*xxx----- */
+ data_bitset (&dev->init_regs[0x13], 0x80, 0); /*x------- */
+
+ /* enable timer to switch off lamp */
+ data_bitset (&dev->init_regs[0x146], 0x10, 1); /*---x----*/
+
+ /* set time to switch off lamp */
+ dev->init_regs[0x147] = 0xff;
+
+ /* set last acccurve step */
+ data_lsb_set (&dev->init_regs[0xe1], 0x2af8, 3);
+
+ /* set msi 0x02 */
+ dev->init_regs[0xda] = 0x02;
+ data_bitset (&dev->init_regs[0xdd], 0x03, 0); /*------xx*/
+
+ /* set binary threshold high and low in little endian */
+ data_lsb_set (&dev->init_regs[0x19e], binarythresholdl, 2);
+ data_lsb_set (&dev->init_regs[0x1a0], binarythresholdh, 2);
+
+
+ data_bitset (&dev->init_regs[0x01], 0x08, 0); /*----x---*/
+ data_bitset (&dev->init_regs[0x16f], 0x40, 0); /*-x------*/
+ dev->init_regs[0x0bf] = (dev->init_regs[0x00bf] & 0xe0) | 0x20;
+ dev->init_regs[0x163] = (dev->init_regs[0x0163] & 0x3f) | 0x40;
+
+ data_bitset (&dev->init_regs[0xd6], 0x0f, 8); /*----xxxx*/
+ data_bitset (&dev->init_regs[0x164], 0x80, 1); /*x------- */
+
+ dev->init_regs[0x0bc] = 0x00;
+ dev->init_regs[0x0bd] = 0x00;
+
+ dev->init_regs[0x165] = (dev->init_regs[0x0165] & 0x3f) | 0x80;
+ dev->init_regs[0x0ed] = 0x10;
+ dev->init_regs[0x0be] = 0x00;
+ dev->init_regs[0x0d5] = 0x00;
+
+ dev->init_regs[0xee] = 0x00;
+ dev->init_regs[0xef] = 0x00;
+ dev->init_regs[0xde] = 0xff;
+
+ /* set bit[4] has_curves = 0 | bit[0..3] unknown = 0 */
+ data_bitset (&dev->init_regs[0xdf], 0x10, 0); /*---x----*/
+ data_bitset (&dev->init_regs[0xdf], 0x0f, 0); /*----xxxx*/
+
+ /* Set motor type */
+ data_bitset (&dev->init_regs[0xd7], 0x80, dev->motorcfg->type); /*x------- */
+
+ if (dev->motorcfg->type == MT_ONCHIP_PWM)
+ {
+ data_bitset (&dev->init_regs[0x14e], 0x10, 1); /*---x----*/
+
+ /* set motorpwmfrequency */
+ data_bitset (&dev->init_regs[0xd7], 0x3f, dev->motorcfg->pwmfrequency); /*--xxxxxx*/
+ }
+
+ dev->init_regs[0x600] &= 0xfb;
+ dev->init_regs[0x1d8] |= 0x08;
+
+ v160c_block_size = 0x04;
+ mem_total = 0x80000;
+
+ /* check and setup installed ram */
+ RTS_DMA_CheckType (dev, dev->init_regs);
+ rst = RTS_DMA_WaitReady (dev, 1500);
+
+ DBG (DBG_FNC, "- Init_Registers: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Read_FE3E (struct st_device *dev, SANE_Byte * destino)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Read_FE3E:\n");
+
+ rst = ERROR;
+ if (destino != NULL)
+ {
+ SANE_Byte data;
+ if (Read_Byte (dev->usb_handle, 0xfe3e, &data) == 0)
+ {
+ *destino = data;
+ rst = OK;
+ DBG (DBG_FNC, " -> %02x\n", _B0 (data));
+ }
+ }
+
+ DBG (DBG_FNC, "- Read_FE3E: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Head_IsAtHome (struct st_device *dev, SANE_Byte * Regs)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Head_IsAtHome:\n");
+
+ /* if returns TRUE, lamp is at home. Otherwise it returns FALSE */
+ rst = 0;
+
+ if (Regs != NULL)
+ {
+ SANE_Byte data;
+ if (Read_Byte (dev->usb_handle, 0xe96f, &data) == OK)
+ {
+ Regs[0x16f] = _B0 (data);
+ rst = (data >> 6) & 1;
+ }
+ }
+
+ rst = (rst == 1) ? TRUE : FALSE;
+
+ DBG (DBG_FNC, "- Head_IsAtHome: %s\n", (rst == TRUE) ? "Yes" : "No");
+
+ return rst;
+}
+
+static SANE_Byte
+RTS_IsExecuting (struct st_device *dev, SANE_Byte * Regs)
+{
+ SANE_Byte rst;
+
+ DBG (DBG_FNC, "+ RTS_IsExecuting:\n");
+
+ rst = 0;
+
+ if (Regs != NULL)
+ {
+ SANE_Byte data;
+ if (Read_Byte (dev->usb_handle, 0xe800, &data) == OK)
+ {
+ Regs[0x00] = data;
+ rst = (data >> 7) & 1;
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_IsExecuting: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_WaitScanEnd (struct st_device *dev, SANE_Int msecs)
+{
+ SANE_Byte data;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_WaitScanEnd(msecs=%i):\n", msecs);
+
+ /* returns 0 if ok or timeout
+ returns -1 if fails */
+
+ rst = ERROR;
+
+ if (Read_Byte (dev->usb_handle, 0xe800, &data) == OK)
+ {
+ long ticks = GetTickCount () + msecs;
+ rst = OK;
+ while (((data & 0x80) != 0) && (ticks > GetTickCount ()) && (rst == OK))
+ {
+ rst = Read_Byte (dev->usb_handle, 0xe800, &data);
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_WaitScanEnd: Ending with rst=%i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_Enable_CCD (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ RTS_Enable_CCD(*Regs, arg2=%i):\n", channels);
+
+ if (Read_Buffer (dev->usb_handle, 0xe810, &Regs[0x10], 4) == OK)
+ {
+ data_bitset (&Regs[0x10], 0xe0, channels); /*xxx----- */
+ data_bitset (&Regs[0x13], 0x80, channels >> 3); /*x------- */
+
+ Write_Buffer (dev->usb_handle, 0xe810, &Regs[0x10], 4);
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- RTS_Enable_CCD: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_Warm_Reset (struct st_device *dev)
+{
+ SANE_Byte data;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_Warm_Reset:\n");
+
+ rst = ERROR;
+ if (Read_Byte (dev->usb_handle, 0xe800, &data) == OK)
+ {
+ data = (data & 0x3f) | 0x40; /*01------ */
+ if (Write_Byte (dev->usb_handle, 0xe800, data) == OK)
+ {
+ data &= 0xbf; /*-0------*/
+ rst = Write_Byte (dev->usb_handle, 0xe800, data);
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_Warm_Reset: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_Status_Timer_Set (struct st_device *dev, SANE_Int minutes)
+{
+ SANE_Byte MyBuffer[2];
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Lamp_Status_Timer_Set(minutes=%i):\n", minutes);
+
+ MyBuffer[0] = dev->init_regs[0x0146] & 0xef;
+ MyBuffer[1] = dev->init_regs[0x0147];
+
+ if (minutes > 0)
+ {
+ double rst, op2;
+
+ minutes = _B0 (minutes);
+ op2 = 2.682163611980331;
+ MyBuffer[0x00] |= 0x10;
+ rst = (minutes * op2);
+ MyBuffer[0x01] = (SANE_Byte) floor (rst);
+ }
+
+ dev->init_regs[0x147] = MyBuffer[1];
+ dev->init_regs[0x146] =
+ (dev->init_regs[0x146] & 0xef) | (MyBuffer[0] & 0x10);
+
+ rst =
+ Write_Word (dev->usb_handle, 0xe946,
+ (SANE_Int) ((MyBuffer[1] << 8) + MyBuffer[0]));
+
+ DBG (DBG_FNC, "- Lamp_Status_Timer_Set: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Buttons_Enable (struct st_device *dev)
+{
+ SANE_Int data, rst;
+
+ DBG (DBG_FNC, "+ Buttons_Enable:\n");
+
+ if (Read_Word (dev->usb_handle, 0xe958, &data) == OK)
+ {
+ data |= 0x0f;
+ rst = Write_Word (dev->usb_handle, 0xe958, data);
+ }
+ else
+ rst = ERROR;
+
+ DBG (DBG_FNC, "- Buttons_Enable: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Buttons_Count (struct st_device *dev)
+{
+ SANE_Int rst = 0;
+
+ /* This chipset supports up to six buttons */
+
+ if (dev->buttons != NULL)
+ rst = dev->buttons->count;
+
+ DBG (DBG_FNC, "> Buttons_Count: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Buttons_Status (struct st_device *dev)
+{
+ SANE_Int rst = -1;
+ SANE_Byte data;
+
+ DBG (DBG_FNC, "+ Buttons_Status\n");
+
+ /* Each bit is 1 if button is not pressed, and 0 if it is pressed
+ This chipset supports up to six buttons */
+
+ if (Read_Byte (dev->usb_handle, 0xe968, &data) == OK)
+ rst = _B0 (data);
+
+ DBG (DBG_FNC, "- Buttons_Status: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Buttons_Released (struct st_device *dev)
+{
+ SANE_Int rst = -1;
+ SANE_Byte data;
+
+ DBG (DBG_FNC, "+ Buttons_Released\n");
+
+ /* Each bit is 1 if button is released, until reading this register. Then
+ entire register is cleared. This chipset supports up to six buttons */
+
+ if (Read_Byte (dev->usb_handle, 0xe96a, &data) == OK)
+ rst = _B0 (data);
+
+ DBG (DBG_FNC, "- Buttons_Released: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Buttons_Order (struct st_device *dev, SANE_Int mask)
+{
+ /* this is a way to order each button according to its bit in register 0xe968 */
+ SANE_Int rst = -1;
+
+ if (dev->buttons != NULL)
+ {
+ SANE_Int a;
+
+ for (a = 0; a < 6; a++)
+ {
+ if (dev->buttons->mask[a] == mask)
+ {
+ rst = a;
+ break;
+ }
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Int
+GainOffset_Clear (struct st_device *dev)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ GainOffset_Clear:\n");
+
+ /* clear offsets */
+ offset[CL_RED] = offset[CL_GREEN] = offset[CL_BLUE] = 0;
+
+ /* save offsets */
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ SANE_Int a;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ RTS_EEPROM_WriteWord (dev->usb_handle, 0x70 + (2 * a), 0);
+
+ /* update checksum */
+ rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x76, 0);
+ }
+
+ DBG (DBG_FNC, "- GainOffset_Clear: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_Status_Get (struct st_device *dev, SANE_Byte * flb_lamp,
+ SANE_Byte * tma_lamp)
+{
+ /* The only reason that I think windows driver uses two variables to get
+ which lamp is switched on instead of one variable is that some chipset
+ model could have both lamps switched on, so I'm maintaining such var */
+
+ SANE_Int rst = ERROR; /* default */
+
+ DBG (DBG_FNC, "+ Lamp_Status_Get:\n");
+
+ if ((flb_lamp != NULL) && (tma_lamp != NULL))
+ {
+ SANE_Int data1;
+ SANE_Byte data2;
+
+ if (Read_Byte (dev->usb_handle, 0xe946, &data2) == OK)
+ {
+ if (Read_Word (dev->usb_handle, 0xe954, &data1) == OK)
+ {
+ rst = OK;
+
+ *flb_lamp = 0;
+ *tma_lamp = 0;
+
+ switch (dev->chipset->model)
+ {
+ case RTS8822BL_03A:
+ *flb_lamp = ((data2 & 0x40) != 0) ? 1 : 0;
+ *tma_lamp = (((data2 & 0x20) != 0)
+ && ((data1 & 0x10) == 1)) ? 1 : 0;
+ break;
+ default:
+ if ((_B1 (data1) & 0x10) == 0)
+ *flb_lamp = (data2 >> 6) & 1;
+ else
+ *tma_lamp = (data2 >> 6) & 1;
+ break;
+ }
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- Lamp_Status_Get: rst=%i flb=%i tma=%i\n", rst,
+ _B0 (*flb_lamp), _B0 (*tma_lamp));
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_WaitReady (struct st_device *dev, SANE_Int msecs)
+{
+ SANE_Byte data;
+ SANE_Int rst;
+ long mytime;
+
+ DBG (DBG_FNC, "+ RTS_DMA_WaitReady(msecs=%i):\n", msecs);
+
+ rst = OK;
+ mytime = GetTickCount () + msecs;
+
+ while ((mytime > GetTickCount ()) && (rst == OK))
+ {
+ if (Read_Byte (dev->usb_handle, 0xef09, &data) == OK)
+ {
+ if ((data & 1) == 0)
+ usleep (1000 * 100);
+ else
+ break;
+ }
+ else
+ rst = ERROR;
+ }
+
+ DBG (DBG_FNC, "- RTS_DMA_WaitReady: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_WaitInitEnd (struct st_device *dev, SANE_Int msecs)
+{
+ SANE_Byte data;
+ SANE_Int rst;
+ long mytime;
+
+ DBG (DBG_FNC, "+ RTS_WaitInitEnd(msecs=%i):\n", msecs);
+
+ rst = OK;
+ mytime = GetTickCount () + msecs;
+
+ while ((mytime > GetTickCount ()) && (rst == OK))
+ {
+ if (Read_Byte (dev->usb_handle, 0xf910, &data) == OK)
+ {
+ if ((data & 8) == 0)
+ usleep (1000 * 100);
+ else
+ break;
+ }
+ else
+ rst = ERROR;
+ }
+
+ DBG (DBG_FNC, "- RTS_WaitInitEnd: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Motor_Change (struct st_device *dev, SANE_Byte * buffer, SANE_Byte value)
+{
+ SANE_Int data, rst;
+
+ DBG (DBG_FNC, "+ Motor_Change(*buffer, value=%i):\n", value);
+
+ if (Read_Word (dev->usb_handle, 0xe954, &data) == OK)
+ {
+ data &= 0xcf; /*--00----*/
+ value--;
+ switch (value)
+ {
+ case 2:
+ data |= 0x30;
+ break; /*--11----*/
+ case 1:
+ data |= 0x20;
+ break; /*--10----*/
+ case 0:
+ data |= 0x10;
+ break; /*--01----*/
+ }
+
+ buffer[0x154] = _B0 (data);
+
+ rst = Write_Byte (dev->usb_handle, 0xe954, buffer[0x154]);
+ }
+ else
+ rst = ERROR;
+
+ DBG (DBG_FNC, "- Motor_Change: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_Read (struct st_device *dev, SANE_Int dmacs, SANE_Int options,
+ SANE_Int size, SANE_Byte * buffer)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC,
+ "+ RTS_DMA_Read(dmacs=%04x, options=%04x, size=%i., *buffer):\n",
+ dmacs, options, size);
+
+ /* is there any buffer to send? */
+ if ((buffer != NULL) && (size > 0))
+ {
+ /* reset dma */
+ if (RTS_DMA_Reset (dev) == OK)
+ {
+ /* prepare dma to read */
+ if (RTS_DMA_Enable_Read (dev, dmacs, size, options) == OK)
+ {
+ SANE_Int transferred;
+
+ rst =
+ Bulk_Operation (dev, BLK_READ, size, buffer, &transferred);
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_DMA_Read(): %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_Write (struct st_device *dev, SANE_Int dmacs, SANE_Int options,
+ SANE_Int size, SANE_Byte * buffer)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC,
+ "+ RTS_DMA_Write(dmacs=%04x, options=%04x, size=%i., *buffer):\n",
+ dmacs, options, size);
+
+ /* is there any buffer to send? */
+ if ((buffer != NULL) && (size > 0))
+ {
+ /* reset dma */
+ if (RTS_DMA_Reset (dev) == OK)
+ {
+ /* prepare dma to write */
+ if (RTS_DMA_Enable_Write (dev, dmacs, size, options) == OK)
+ {
+ SANE_Int transferred;
+ SANE_Byte *check_buffer;
+
+ check_buffer = (SANE_Byte *) malloc (size);
+ if (check_buffer != NULL)
+ {
+ /* if some transfer fails we try again until ten times */
+ SANE_Int a;
+ for (a = 10; a > 0; a--)
+ {
+ /* send buffer */
+ Bulk_Operation (dev, BLK_WRITE, size, buffer,
+ &transferred);
+
+ /* prepare dma to read */
+ if (RTS_DMA_Enable_Read (dev, dmacs, size, options) ==
+ OK)
+ {
+ SANE_Int b = 0, diff = FALSE;
+
+ /* read buffer */
+ Bulk_Operation (dev, BLK_READ, size, check_buffer,
+ &transferred);
+
+ /* check buffers */
+ while ((b < size) && (diff == FALSE))
+ {
+ if (buffer[b] == check_buffer[b])
+ b++;
+ else
+ diff = TRUE;
+ }
+
+ /* if buffers are equal we can break loop */
+ if (diff == TRUE)
+ {
+ /* cancel dma */
+ RTS_DMA_Cancel (dev);
+
+ /* prepare dma to write buffer again */
+ if (RTS_DMA_Enable_Write
+ (dev, dmacs, size, options) != OK)
+ break;
+ }
+ else
+ {
+ /* everything went ok */
+ rst = OK;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ /* free check buffer */
+ free (check_buffer);
+ }
+ else
+ {
+ /* for some reason it's not posible to allocate space to check
+ sent buffer so we just write data */
+ Bulk_Operation (dev, BLK_WRITE, size, buffer, &transferred);
+ rst = OK;
+ }
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_DMA_Write(): %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_CheckType (struct st_device *dev, SANE_Byte * Regs)
+{
+ /* This function tries to detect what kind of RAM supports chipset */
+ /* Returns a value between 0 and 4. -1 means error */
+
+ SANE_Int rst = -1;
+
+ DBG (DBG_FNC, "+ RTS_DMA_CheckType(*Regs):\n");
+
+ if (Regs != NULL)
+ {
+ SANE_Byte *out_buffer;
+
+ /* define buffer to send */
+ out_buffer = (SANE_Byte *) malloc (sizeof (SANE_Byte) * 2072);
+ if (out_buffer != NULL)
+ {
+ SANE_Byte *in_buffer;
+
+ /* define incoming buffer */
+ in_buffer = (SANE_Byte *) malloc (sizeof (SANE_Byte) * 2072);
+ if (in_buffer != NULL)
+ {
+ SANE_Int a, b, diff;
+
+ /* fill outgoing buffer with a known pattern */
+ b = 0;
+ for (a = 0; a < 2072; a++)
+ {
+ out_buffer[a] = b;
+ b++;
+ if (b == 0x61)
+ b = 0;
+ }
+
+ /* let's send buffer with different ram types and compare
+ incoming buffer until getting the right type */
+
+ for (a = 4; a >= 0; a--)
+ {
+ /* set ram type */
+ if (RTS_DMA_SetType (dev, Regs, a) != OK)
+ break;
+
+ /* wait 1500 miliseconds */
+ if (RTS_DMA_WaitReady (dev, 1500) == OK)
+ {
+ /* reset dma */
+ RTS_DMA_Reset (dev);
+
+ /* write buffer */
+ RTS_DMA_Write (dev, 0x0004, 0x102, 2072, out_buffer);
+
+ /* now read buffer */
+ RTS_DMA_Read (dev, 0x0004, 0x102, 2072, in_buffer);
+
+ /* check buffers */
+ b = 0;
+ diff = FALSE;
+ while ((b < 2072) && (diff == FALSE))
+ {
+ if (out_buffer[b] == in_buffer[b])
+ b++;
+ else
+ diff = TRUE;
+ }
+
+ /* if buffers are equal */
+ if (diff == FALSE)
+ {
+ SANE_Int data = 0;
+
+ /* buffers are equal so we've found the right ram type */
+ memset (out_buffer, 0, 0x20);
+ for (b = 0; b < 0x20; b += 2)
+ out_buffer[b] = b;
+
+ /* write buffer */
+ if (RTS_DMA_Write
+ (dev, 0x0004, 0x0000, 0x20, out_buffer) == OK)
+ {
+ SANE_Int c = 0;
+ diff = TRUE;
+
+ do
+ {
+ c++;
+ for (b = 1; b < 0x20; b += 2)
+ out_buffer[b] = c;
+
+ if (RTS_DMA_Write
+ (dev, 0x0004, (_B0 (c) << 0x11) >> 0x04,
+ 0x20, out_buffer) == OK)
+ {
+ if (RTS_DMA_Read
+ (dev, 0x0004, 0x0000, 0x20,
+ in_buffer) == OK)
+ {
+ b = 0;
+ diff = FALSE;
+ while ((b < 0x20)
+ && (diff == FALSE))
+ {
+ if (out_buffer[b] ==
+ in_buffer[b])
+ b++;
+ else
+ diff = TRUE;
+ }
+
+ if (diff == FALSE)
+ data = c << 7;
+ }
+ }
+ }
+ while ((c < 0x80) && (diff == TRUE));
+ }
+
+ switch (data)
+ {
+ case 16384:
+ Regs[0x708] &= 0x1f;
+ Regs[0x708] |= 0x80;
+ break;
+ case 8192:
+ Regs[0x708] &= 0x1f;
+ Regs[0x708] |= 0x60;
+ break;
+ case 4096:
+ Regs[0x708] &= 0x1f;
+ Regs[0x708] |= 0x40;
+ break;
+ case 2048:
+ Regs[0x708] &= 0x1f;
+ Regs[0x708] |= 0x20;
+ break;
+ case 1024:
+ Regs[0x708] &= 0x1f;
+ data = 0x200;
+ break;
+ case 128:
+ Regs[0x708] &= 0x1f;
+ break;
+ }
+
+ DBG (DBG_FNC, " -> data1 = 0x%08x\n",
+ (data * 4) * 1024);
+ DBG (DBG_FNC, " -> data2 = 0x%08x\n", data * 1024);
+ DBG (DBG_FNC, " -> type = 0x%04x\n",
+ Regs[0x708] >> 5);
+
+ RTS_DMA_SetType (dev, Regs, Regs[0x708] >> 5);
+
+ rst = OK;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ free (in_buffer);
+ }
+
+ free (out_buffer);
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_DMA_CheckType(): %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_SetType (struct st_device *dev, SANE_Byte * Regs, SANE_Byte ramtype)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ RTS_DMA_SetType(*Regs, ramtype=%i):\n", ramtype);
+
+ if (Regs != NULL)
+ {
+ data_bitset (&Regs[0x708], 0x08, 0); /*----0---*/
+
+ if (Write_Byte (dev->usb_handle, 0xef08, Regs[0x708]) == OK)
+ {
+ data_bitset (&Regs[0x708], 0xe0, ramtype);
+
+ if (Write_Byte (dev->usb_handle, 0xef08, Regs[0x708]) == OK)
+ {
+ data_bitset (&Regs[0x708], 0x08, 1); /*----1---*/
+ rst = Write_Byte (dev->usb_handle, 0xef08, Regs[0x708]);
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_DMA_SetType: %i\n", rst);
+
+ return rst;
+}
+
+static void
+Motor_Release (struct st_device *dev)
+{
+ SANE_Byte data = 0;
+
+ DBG (DBG_FNC, "+ Motor_Release:\n");
+
+ if (Read_Byte (dev->usb_handle, 0xe8d9, &data) == OK)
+ {
+ data |= 4;
+ Write_Byte (dev->usb_handle, 0xe8d9, data);
+ }
+
+ DBG (DBG_FNC, "- Motor_Release:\n");
+}
+
+static SANE_Byte
+GainOffset_Counter_Load (struct st_device *dev)
+{
+ SANE_Byte data = 0x0f;
+
+ DBG (DBG_FNC, "+ GainOffset_Counter_Load:\n");
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x77, &data) != OK)
+ data = 0x0f;
+
+ DBG (DBG_FNC, "- GainOffset_Counter_Load: %i\n", _B0 (data));
+
+ return data;
+}
+
+static SANE_Int
+RTS_Execute (struct st_device *dev)
+{
+ SANE_Byte e813, e800;
+ SANE_Int ret;
+
+ DBG (DBG_FNC, "+ RTS_Execute:\n");
+
+ e813 = 0;
+ e800 = 0;
+ ret = ERROR;
+
+ if (Read_Byte (dev->usb_handle, 0xe800, &e800) == OK)
+ {
+ if (Read_Byte (dev->usb_handle, 0xe813, &e813) == OK)
+ {
+ e813 &= 0xbf;
+ if (Write_Byte (dev->usb_handle, 0xe813, e813) == OK)
+ {
+ e800 |= 0x40;
+ if (Write_Byte (dev->usb_handle, 0xe800, e800) == OK)
+ {
+ e813 |= 0x40;
+ if (Write_Byte (dev->usb_handle, 0xe813, e813) == OK)
+ {
+ e800 &= 0xbf;
+ if (Write_Byte (dev->usb_handle, 0xe800, e800) == OK)
+ {
+ usleep (1000 * 100);
+ e800 |= 0x80;
+ ret = Write_Byte (dev->usb_handle, 0xe800, e800);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_Execute: %i\n", ret);
+
+ return ret;
+}
+
+static SANE_Int
+RTS_isTmaAttached (struct st_device *dev)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_isTmaAttached:\n");
+
+ /* returns 0 if Tma is attached. Otherwise 1 */
+ if (Read_Word (dev->usb_handle, 0xe968, &rst) == OK)
+ {
+ rst = ((_B1 (rst) & 2) != 0) ? FALSE : TRUE;
+ }
+ else
+ rst = TRUE;
+
+ DBG (DBG_FNC, "- RTS_isTmaAttached: %s\n", (rst == TRUE) ? "Yes" : "No");
+
+ return rst;
+}
+
+static SANE_Int
+Gamma_AllocTable (SANE_Byte * table)
+{
+ SANE_Int C;
+ SANE_Int rst = OK;
+
+ hp_gamma->depth = 8;
+
+ for (C = 0; C < 3; C++)
+ if (hp_gamma->table[C] == NULL)
+ hp_gamma->table[C] = malloc (sizeof (SANE_Byte) * 256);
+
+ if ((hp_gamma->table[CL_RED] != NULL) &&
+ (hp_gamma->table[CL_GREEN] != NULL) &&
+ (hp_gamma->table[CL_BLUE] != NULL))
+ {
+ /* All tables allocated */
+ for (C = 0; C < 256; C++)
+ {
+ if ((table != NULL) && (RTS_Debug->EnableGamma == TRUE))
+ {
+ /* fill gamma tables with user defined values */
+ hp_gamma->table[CL_RED][C] = table[C];
+ hp_gamma->table[CL_GREEN][C] = table[0x100 + C];
+ hp_gamma->table[CL_BLUE][C] = table[0x200 + C];
+ }
+ else
+ {
+ hp_gamma->table[CL_RED][C] = C;
+ hp_gamma->table[CL_GREEN][C] = C;
+ hp_gamma->table[CL_BLUE][C] = C;
+ }
+ }
+
+ /* Locate threshold of bw */
+ for (C = 0; C < 256; C++)
+ if (hp_gamma->table[CL_RED][C] != 0)
+ break;
+
+ bw_threshold = C - 1;
+ }
+ else
+ {
+ /* Some alloc failed */
+ rst = ERROR;
+
+ Gamma_FreeTables ();
+ }
+
+ DBG (DBG_FNC, "> Gamma_AllocTable: %i >> bw_threshold = %i\n", rst,
+ bw_threshold);
+
+ return rst;
+}
+
+static SANE_Int
+Gamma_Apply (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg,
+ struct st_gammatables *mygamma)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Gamma_Apply(*Regs, *scancfg, *hwdcfg, *mygamma):\n");
+ dbg_ScanParams (scancfg);
+
+ if (hwdcfg->use_gamma_tables == FALSE)
+ {
+ DBG (DBG_FNC, "-> Gamma tables are not used\n");
+
+ v1600 = NULL;
+ v1604 = NULL;
+ v1608 = NULL;
+ }
+ else
+ {
+ /*390b */
+ SANE_Int table_size, buffersize, c;
+ SANE_Byte channels, *gammabuffer;
+
+ DBG (DBG_FNC, "-> Using gamma tables\n");
+
+ /* get channels count */
+ channels = 3; /* default */
+
+ if (scancfg->colormode != CM_COLOR)
+ {
+ if (scancfg->channel != 3)
+ {
+ if (scancfg->colormode != 3)
+ channels = (scancfg->samplerate == PIXEL_RATE) ? 2 : 1;
+ }
+ }
+
+ /* get size for gamma tables */
+ switch (mygamma->depth & 0x0c)
+ {
+ case 0:
+ table_size = 0x100 + (mygamma->depth & 1);
+ break;
+ case 4:
+ table_size = 0x400 + (mygamma->depth & 1);
+ break;
+ case 8:
+ table_size = 0x1000 + (mygamma->depth & 1);
+ break;
+ default:
+ table_size = 2;
+ break;
+ }
+
+ /* allocate space for gamma buffer */
+ buffersize = table_size * channels;
+ gammabuffer = (SANE_Byte *) malloc (buffersize * sizeof (SANE_Byte));
+ if (gammabuffer != NULL)
+ {
+ /* update gamma pointers for each channel */
+ v1600 = (SANE_Byte *) & mygamma->table[CL_RED];
+ v1604 = (SANE_Byte *) & mygamma->table[CL_GREEN];
+ v1608 = (SANE_Byte *) & mygamma->table[CL_BLUE];
+
+ /* include gamma tables into one buffer */
+ for (c = 0; c < channels; c++)
+ memcpy (gammabuffer + (c * table_size), mygamma->table[c],
+ table_size);
+
+ /* send gamma buffer to scanner */
+ Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b] & 0xaf);
+ rst = Gamma_SendTables (dev, Regs, gammabuffer, buffersize);
+ Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]);
+
+ /* free gamma buffer */
+ free (gammabuffer);
+ }
+ else
+ rst = ERROR;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+Refs_Analyze_Pattern (struct st_scanparams *scancfg,
+ SANE_Byte * scanned_pattern, SANE_Int * ler1,
+ SANE_Int ler1order, SANE_Int * ser1, SANE_Int ser1order)
+{
+ SANE_Int buffersize, xpos, ypos, coord, cnt, chn_size, dist, rst;
+ double *color_sum, *color_dif, diff_max;
+ SANE_Int vector[3];
+
+ DBG (DBG_FNC,
+ "+ Refs_Analyze_Pattern(depth=%i, width=%i, height=%i, *scanned_pattern, *ler1, ler1order=%i, *ser1, ser1order=%i)\n",
+ scancfg->depth, scancfg->coord.width, scancfg->coord.height, ler1order,
+ ser1order);
+
+ rst = ERROR; /* by default */
+ dist = 5; /* distance to compare */
+ chn_size = (scancfg->depth > 8) ? 2 : 1;
+ buffersize = max (scancfg->coord.width, scancfg->coord.height);
+
+ color_sum = (double *) malloc (sizeof (double) * buffersize);
+ if (color_sum != NULL)
+ {
+ color_dif = (double *) malloc (sizeof (double) * buffersize);
+ if (color_dif != NULL)
+ {
+ /*-------- 1st SER -------- */
+ coord = 1;
+
+ if ((scancfg->coord.width - dist) > 1)
+ {
+ /* clear buffers */
+ bzero (color_sum, sizeof (double) * buffersize);
+ bzero (color_dif, sizeof (double) * buffersize);
+
+ for (xpos = 0; xpos < scancfg->coord.width; xpos++)
+ {
+ for (ypos = 0; ypos < 20; ypos++)
+ color_sum[xpos] +=
+ data_lsb_get (scanned_pattern +
+ (scancfg->coord.width * ypos) + xpos,
+ chn_size);
+ }
+
+ diff_max =
+ (ser1order !=
+ 0) ? color_sum[0] - color_sum[1] : color_sum[1] -
+ color_sum[0];
+ color_dif[0] = diff_max;
+ cnt = 1;
+
+ do
+ {
+ color_dif[cnt] =
+ (ser1order !=
+ 0) ? color_sum[cnt] - color_sum[cnt +
+ dist] : color_sum[cnt +
+ dist] -
+ color_sum[cnt];
+
+ if ((color_dif[cnt] >= 0) && (color_dif[cnt] > diff_max))
+ {
+ /*d4df */
+ diff_max = color_dif[cnt];
+ if (abs (color_dif[cnt] - color_dif[cnt - 1]) >
+ abs (color_dif[coord] - color_dif[coord - 1]))
+ coord = cnt;
+ }
+
+ cnt++;
+ }
+ while (cnt < (scancfg->coord.width - dist));
+ }
+
+ vector[0] = coord + dist;
+
+ /*-------- 1st LER -------- */
+ coord = 1;
+
+ if ((scancfg->coord.height - dist) > 1)
+ {
+ /* clear buffers */
+ bzero (color_sum, sizeof (double) * buffersize);
+ bzero (color_dif, sizeof (double) * buffersize);
+
+ for (ypos = 0; ypos < scancfg->coord.height; ypos++)
+ {
+ for (xpos = vector[0]; xpos < scancfg->coord.width - dist;
+ xpos++)
+ color_sum[ypos] +=
+ data_lsb_get (scanned_pattern +
+ (scancfg->coord.width * ypos) + xpos,
+ chn_size);
+ }
+
+ diff_max =
+ (ler1order !=
+ 0) ? color_sum[0] - color_sum[1] : color_sum[1] -
+ color_sum[0];
+ color_dif[0] = diff_max;
+
+ cnt = 1;
+
+ do
+ {
+ color_dif[cnt] =
+ (ler1order !=
+ 0) ? color_sum[cnt] - color_sum[cnt +
+ dist] : color_sum[cnt +
+ dist] -
+ color_sum[cnt];
+
+ if ((color_dif[cnt] >= 0) && (color_dif[cnt] > diff_max))
+ {
+ diff_max = color_dif[cnt];
+ if (abs (color_dif[cnt] - color_dif[cnt - 1]) >
+ abs (color_dif[coord] - color_dif[coord - 1]))
+ coord = cnt;
+ }
+
+ cnt++;
+ }
+ while (cnt < (scancfg->coord.height - dist));
+ }
+
+ vector[1] = coord + dist;
+
+ /*-------- 1st LER -------- */
+ if ((scancfg->coord.width - dist) > 1)
+ {
+ /* clear buffers */
+ bzero (color_sum, sizeof (double) * buffersize);
+ bzero (color_dif, sizeof (double) * buffersize);
+
+ for (xpos = 0; xpos < scancfg->coord.width; xpos++)
+ {
+ for (ypos = coord + 4; ypos < scancfg->coord.height; ypos++)
+ color_sum[xpos] +=
+ data_lsb_get (scanned_pattern +
+ (scancfg->coord.width * ypos) + xpos,
+ chn_size);
+ }
+
+ diff_max =
+ (ser1order !=
+ 0) ? color_sum[0] - color_sum[1] : color_sum[1] -
+ color_sum[0];
+ color_dif[0] = diff_max;
+ cnt = 1;
+
+ do
+ {
+ color_dif[cnt] =
+ (ser1order !=
+ 0) ? color_sum[cnt] - color_sum[cnt +
+ dist] : color_sum[cnt +
+ dist] -
+ color_sum[cnt];
+
+ if ((color_dif[cnt] >= 0) && (color_dif[cnt] > diff_max))
+ {
+ diff_max = color_dif[cnt];
+ if (abs (color_dif[cnt] - color_dif[cnt - 1]) >
+ abs (color_dif[coord] - color_dif[coord - 1]))
+ coord = cnt;
+ }
+
+ cnt++;
+ }
+ while (cnt < (scancfg->coord.width - dist));
+ }
+
+ vector[2] = coord + dist;
+
+ /* save image */
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ dbg_autoref (scancfg, scanned_pattern, vector[0], vector[2],
+ vector[1]);
+
+ /* assign values detected */
+ if (ser1 != NULL)
+ *ser1 = vector[2];
+
+ if (ler1 != NULL)
+ *ler1 = vector[1];
+
+ /* show values */
+ DBG (DBG_FNC, " -> Vectors found: x1=%i, x2=%i, y=%i\n", vector[0],
+ vector[2], vector[1]);
+
+ rst = OK;
+
+ free (color_dif);
+ }
+
+ free (color_sum);
+ }
+
+ DBG (DBG_FNC, "- Refs_Analyze_Pattern: %i\n", rst);
+
+ return rst;
+}
+
+static double
+get_shrd (double value, SANE_Int desp)
+{
+ if (desp <= 0x40)
+ return value / pow (2, desp);
+ else
+ return 0;
+}
+
+static char
+get_byte (double value)
+{
+ unsigned int data;
+ double temp;
+
+ if (value > 0xffffffff)
+ {
+ temp = floor (get_shrd (value, 0x20));
+ temp *= pow (2, 32);
+ value -= temp;
+ }
+
+ data = (unsigned int) value;
+
+ data = _B0 (data);
+
+ return data;
+}
+
+static SANE_Int
+Timing_SetLinearImageSensorClock (SANE_Byte * Regs, struct st_cph *cph)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC,
+ "+ Timing_SetLinearImageSensorClock(SANE_Byte *Regs, struct st_cph *cph)\n");
+
+ dbg_sensorclock (cph);
+
+ if ((Regs != NULL) && (cph != NULL))
+ {
+ Regs[0x00] = get_byte (cph->p1);
+ Regs[0x01] = get_byte (get_shrd (cph->p1, 0x08));
+ Regs[0x02] = get_byte (get_shrd (cph->p1, 0x10));
+ Regs[0x03] = get_byte (get_shrd (cph->p1, 0x18));
+
+ Regs[0x04] &= 0x80;
+ Regs[0x04] |= ((get_byte (get_shrd (cph->p1, 0x20))) & 0x0f);
+ Regs[0x04] |= ((cph->ps & 1) << 6);
+ Regs[0x04] |= ((cph->ge & 1) << 5);
+ Regs[0x04] |= ((cph->go & 1) << 4);
+
+ Regs[0x05] = get_byte (cph->p2);
+ Regs[0x06] = get_byte (get_shrd (cph->p2, 0x08));
+ Regs[0x07] = get_byte (get_shrd (cph->p2, 0x10));
+ Regs[0x08] = get_byte (get_shrd (cph->p2, 0x18));
+ Regs[0x09] &= 0xf0;
+ Regs[0x09] |= ((get_byte (get_shrd (cph->p2, 0x20))) & 0x0f);
+
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- Timing_SetLinearImageSensorClock: %i\n", rst);
+
+ return rst;
+}
+
+static void
+RTS_Setup_SensorTiming (struct st_device *dev, SANE_Int mytiming,
+ SANE_Byte * Regs)
+{
+ DBG (DBG_FNC, "+ RTS_Setup_SensorTiming(mytiming=%i, *Regs):\n", mytiming);
+
+ if ((Regs != NULL) && (mytiming < dev->timings_count))
+ {
+ struct st_timing *mt = dev->timings[mytiming];
+
+ if (mt != NULL)
+ {
+ dbg_timing (mt);
+
+ /* Correlated-Double-Sample 1 & 2 */
+ data_bitset (&Regs[0x92], 0x3f, mt->cdss[0]);
+ data_bitset (&Regs[0x93], 0x3f, mt->cdsc[0]);
+ data_bitset (&Regs[0x94], 0x3f, mt->cdss[1]);
+ data_bitset (&Regs[0x95], 0x3f, mt->cdsc[1]);
+
+ data_bitset (&Regs[0x96], 0x3f, mt->cnpp);
+
+ /* Linear image sensor transfer gates */
+ data_bitset (&Regs[0x45], 0x80, mt->cvtrp[0]);
+ data_bitset (&Regs[0x45], 0x40, mt->cvtrp[1]);
+ data_bitset (&Regs[0x45], 0x20, mt->cvtrp[2]);
+
+ data_bitset (&Regs[0x45], 0x1f, mt->cvtrfpw);
+ data_bitset (&Regs[0x46], 0x1f, mt->cvtrbpw);
+
+ data_lsb_set (&Regs[0x47], mt->cvtrw, 1);
+
+ data_lsb_set (&Regs[0x84], mt->cphbp2s, 3);
+ data_lsb_set (&Regs[0x87], mt->cphbp2e, 3);
+
+ data_lsb_set (&Regs[0x8a], mt->clamps, 3);
+ data_lsb_set (&Regs[0x8d], mt->clampe, 3);
+
+ if (dev->chipset->model == RTS8822L_02A)
+ {
+ if (mt->clampe == -1)
+ data_lsb_set (&Regs[0x8d], mt->cphbp2e, 3);
+ }
+
+ Regs[0x97] = get_byte (mt->adcclkp[0]);
+ Regs[0x98] = get_byte (get_shrd (mt->adcclkp[0], 0x08));
+ Regs[0x99] = get_byte (get_shrd (mt->adcclkp[0], 0x10));
+ Regs[0x9a] = get_byte (get_shrd (mt->adcclkp[0], 0x18));
+ Regs[0x9b] &= 0xf0;
+ Regs[0x9b] |= ((get_byte (get_shrd (mt->adcclkp[0], 0x20))) & 0x0f);
+
+ Regs[0xc1] = get_byte (mt->adcclkp[1]);
+ Regs[0xc2] = get_byte (get_shrd (mt->adcclkp[1], 0x08));
+ Regs[0xc3] = get_byte (get_shrd (mt->adcclkp[1], 0x10));
+ Regs[0xc4] = get_byte (get_shrd (mt->adcclkp[1], 0x18));
+ Regs[0xc5] &= 0xe0;
+ Regs[0xc5] |= ((get_byte (get_shrd (mt->adcclkp[1], 0x20))) & 0x0f);
+
+ /* bit(4) = bit(0) */
+ Regs[0xc5] |= ((mt->adcclkp2e & 1) << 4);
+
+ Timing_SetLinearImageSensorClock (&Regs[0x48], &mt->cph[0]);
+ Timing_SetLinearImageSensorClock (&Regs[0x52], &mt->cph[1]);
+ Timing_SetLinearImageSensorClock (&Regs[0x5c], &mt->cph[2]);
+ Timing_SetLinearImageSensorClock (&Regs[0x66], &mt->cph[3]);
+ Timing_SetLinearImageSensorClock (&Regs[0x70], &mt->cph[4]);
+ Timing_SetLinearImageSensorClock (&Regs[0x7a], &mt->cph[5]);
+ }
+ }
+}
+
+static SANE_Int
+Motor_GetFromResolution (SANE_Int resolution)
+{
+ SANE_Int ret;
+
+ ret = 3;
+ if (RTS_Debug->usbtype != USB11)
+ {
+ if (scan.scantype != ST_NORMAL)
+ {
+ /* scantype is ST_NEG or ST_TA */
+ if (resolution >= 600)
+ ret = 0;
+ }
+ else if (resolution >= 1200)
+ ret = 0;
+ }
+ else if (resolution >= 600)
+ ret = 0;
+
+ DBG (DBG_FNC, "> Motor_GetFromResolution(resolution=%i): %i\n", resolution,
+ ret);
+
+ return ret;
+}
+
+static SANE_Int
+SetMultiExposure (struct st_device *dev, SANE_Byte * Regs)
+{
+ SANE_Int iValue, myctpc;
+
+ DBG (DBG_FNC, "> SetMultiExposure:\n");
+
+ /* set motor has no curves */
+ data_bitset (&Regs[0xdf], 0x10, 0); /*---0----*/
+
+ /* select case systemclock */
+ switch (Regs[0x00] & 0x0f)
+ {
+ case 0x00:
+ iValue = 0x00895440;
+ break; /* 3 x 0x2DC6C0 */
+ case 0x08:
+ case 0x01:
+ iValue = 0x00b71b00;
+ break; /* 4 x 0x2DC6C0 */
+ case 0x02:
+ iValue = 0x0112a880;
+ break; /* 6 x 0x2DC6C0 */
+ case 0x0a:
+ case 0x03:
+ iValue = 0x016e3600;
+ break; /* 8 x 0x2DC6C0 */
+ case 0x04:
+ iValue = 0x02255100;
+ break; /* 12 x 0x2DC6C0 */
+ case 0x0c:
+ iValue = 0x02dc6c00;
+ break; /* 16 x 0x2DC6C0 */
+ case 0x05:
+ iValue = 0x044aa200;
+ break; /* 24 x 0x2DC6C0 */
+ case 0x0d:
+ iValue = 0x05b8d800;
+ break; /* 32 x 0x2DC6C0 */
+
+ case 0x09:
+ iValue = 0x00f42400;
+ break;
+ case 0x0b:
+ iValue = 0x01e84800;
+ break; /* = case 9 * 2 */
+ default:
+ iValue = 0x0478f7f8;
+ break;
+ }
+
+ /* divide by timing.cnpp */
+ iValue /= ((Regs[0x96] & 0x3f) + 1);
+ iValue /= dev->motorcfg->basespeedpps;
+
+ /* get line exposure time */
+ myctpc = data_lsb_get (&Regs[0x30], 3) + 1;
+
+ DBG (DBG_FNC, "CTPC -- SetMultiExposure -- 1 =%i\n", myctpc);
+
+ /* if last step of accurve.normalscan table is lower than iValue ... */
+ if (data_lsb_get (&Regs[0xe1], 3) < iValue)
+ {
+ SANE_Int traget;
+ SANE_Int step_size = _B0 (Regs[0xe0]) + 1;
+
+ /* set exposure time [RED] if zero */
+ if (data_lsb_get (&Regs[0x36], 3) == 0)
+ data_lsb_set (&Regs[0x36], myctpc - 1, 3);
+
+ /* set exposure time [GREEN] if zero */
+ if (data_lsb_get (&Regs[0x3c], 3) == 0)
+ data_lsb_set (&Regs[0x3c], myctpc - 1, 3);
+
+ /* set exposure time [BLUE] if zero */
+ if (data_lsb_get (&Regs[0x42], 3) == 0)
+ data_lsb_set (&Regs[0x42], myctpc - 1, 3);
+
+ iValue = (iValue + 1) * step_size;
+
+ /* update line exposure time */
+ traget = (((myctpc + iValue - 1) / myctpc) * myctpc);
+ data_lsb_set (&Regs[0x30], traget - 1, 3);
+
+ traget = (traget / step_size) - 1;
+ data_lsb_set (&Regs[0x00e1], traget, 3);
+ }
+
+ /* 8300 */
+ return OK;
+}
+
+static SANE_Int
+data_lsb_get (SANE_Byte * address, SANE_Int size)
+{
+ SANE_Int ret = 0;
+ if ((address != NULL) && (size > 0) && (size < 5))
+ {
+ SANE_Int a;
+ SANE_Byte b;
+ size--;
+ for (a = size; a >= 0; a--)
+ {
+ b = address[a];
+ ret = (ret << 8) + b;
+ }
+ }
+ return ret;
+}
+
+static SANE_Byte
+data_bitget (SANE_Byte * address, SANE_Int mask)
+{
+ SANE_Int desp = 0;
+
+ if (mask & 1);
+ else if (mask & 2)
+ desp = 1;
+ else if (mask & 4)
+ desp = 2;
+ else if (mask & 8)
+ desp = 3;
+ else if (mask & 16)
+ desp = 4;
+ else if (mask & 32)
+ desp = 5;
+ else if (mask & 64)
+ desp = 6;
+ else if (mask & 128)
+ desp = 7;
+
+ return (*address & mask) >> desp;
+}
+
+static void
+data_bitset (SANE_Byte * address, SANE_Int mask, SANE_Byte data)
+{
+ /* This function fills mask bits of just a byte with bits given in data */
+ if (mask & 1);
+ else if (mask & 2)
+ data <<= 1;
+ else if (mask & 4)
+ data <<= 2;
+ else if (mask & 8)
+ data <<= 3;
+ else if (mask & 16)
+ data <<= 4;
+ else if (mask & 32)
+ data <<= 5;
+ else if (mask & 64)
+ data <<= 6;
+ else if (mask & 128)
+ data <<= 7;
+
+ *address = (*address & (0xff - mask)) | (data & mask);
+}
+
+static void
+data_wide_bitset (SANE_Byte * address, SANE_Int mask, SANE_Int data)
+{
+ /* Setting bytes bit per bit
+ mask is 4 bytes size
+ Example:
+ data = 0111010111
+ mask = 00000000 11111111 11000000 00000000
+ rst = 00000000 01110101 11000000 00000000 */
+
+ SANE_Int mymask, started = FALSE;
+
+ if ((address != NULL) && (mask != 0))
+ {
+ while (mask != 0)
+ {
+ mymask = _B0 (mask);
+
+ if (started == FALSE)
+ {
+ if (mymask != 0)
+ {
+ SANE_Int a, myvalue;
+
+ for (a = 0; a < 8; a++)
+ if ((mymask & (1 << a)) != 0)
+ break;
+
+ myvalue = _B0 (data << a);
+ myvalue >>= a;
+ data_bitset (address, mymask, myvalue);
+ data >>= (8 - a);
+ started = TRUE;
+ }
+ }
+ else
+ {
+ data_bitset (address, mymask, _B0 (data));
+ data >>= 8;
+ }
+
+ address++;
+ mask >>= 8;
+ }
+ }
+}
+
+
+static void
+data_lsb_set (SANE_Byte * address, SANE_Int data, SANE_Int size)
+{
+ if ((address != NULL) && (size > 0) && (size < 5))
+ {
+ SANE_Int a;
+ for (a = 0; a < size; a++)
+ {
+ address[a] = _B0 (data);
+ data >>= 8;
+ }
+ }
+}
+
+static void
+data_msb_set (SANE_Byte * address, SANE_Int data, SANE_Int size)
+{
+ if ((address != NULL) && (size > 0) && (size < 5))
+ {
+ SANE_Int a;
+
+ for (a = size - 1; a >= 0; a--)
+ {
+ address[a] = _B0 (data);
+ data >>= 8;
+ }
+ }
+}
+
+static SANE_Int
+data_swap_endianess (SANE_Int address, SANE_Int size)
+{
+ SANE_Int rst = 0;
+
+ if ((size > 0) && (size < 5))
+ {
+ SANE_Int a;
+
+ for (a = 0; a < size; a++)
+ {
+ rst = (rst << 8) | _B0 (address);
+ address >>= 8;
+ }
+ }
+
+ return rst;
+}
+
+static void
+Lamp_SetGainMode (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int resolution, SANE_Byte gainmode)
+{
+ DBG (DBG_FNC, "> Lamp_SetGainMode(*Regs, resolution=%i, gainmode=%i):\n",
+ resolution, gainmode);
+
+ if (dev->chipset->model == RTS8822L_02A)
+ {
+ /* hp4370 */
+ SANE_Int data1, data2;
+
+ data1 = data_lsb_get (&Regs[0x154], 2) & 0xfe7f;
+ data2 = data_lsb_get (&Regs[0x156], 2);
+
+ switch (resolution)
+ {
+ case 4800:
+ data2 |= 0x40;
+ data1 &= 0xffbf;
+ break;
+ case 100:
+ case 150:
+ case 200:
+ case 300:
+ case 600:
+ case 1200:
+ case 2400:
+ data1 |= 0x40;
+ data2 &= 0xffbf;
+ break;
+ }
+
+ data_lsb_set (&Regs[0x154], data1, 2);
+ data_lsb_set (&Regs[0x156], data2, 2);
+ }
+ else
+ {
+ /* hp3970 hp4070 ua4900 */
+ SANE_Int data;
+
+ data = data_lsb_get (&Regs[0x154], 2) & 0xfe7f;
+ data = (gainmode == FALSE) ? data | 0x0040 : data & 0xffbf;
+
+ switch (resolution)
+ {
+ case 100:
+ case 200:
+ case 300:
+ case 600:
+ data |= 0x0100;
+ break;
+ case 2400:
+ data |= 0x0180;
+ break;
+ case 1200:
+ if (dev->sensorcfg->type == CIS_SENSOR)
+ data |= 0x80;
+ else if (dev->sensorcfg->type == CCD_SENSOR)
+ data |= 0x0180;
+ break;
+ }
+
+ data_lsb_set (&Regs[0x0154], data, 2);
+ }
+}
+
+static SANE_Int
+RTS_Scanner_StartScan (struct st_device *dev)
+{
+ SANE_Int rst = ERROR; /* default */
+ SANE_Int data;
+
+ DBG (DBG_FNC, "+ RTS_Scanner_StartScan():\n");
+
+ v14b4 = 1; /* TEMPORAL */
+ data = 0;
+ Lamp_PWM_DutyCycle_Get (dev, &data);
+ data = _B0 (data);
+
+ DBG (DBG_FNC, "-> Pwm used = %i\n", data);
+
+ /*
+ windows driver saves pwm used, in file usbfile
+ Section [SCAN_PARAM], field PwmUsed
+ */
+
+ dev->status->cancel = FALSE;
+
+ if (Scan_Start (dev) == OK)
+ {
+ SANE_Int transferred;
+
+ rst = OK;
+
+ if (dev->scanning->imagebuffer != NULL)
+ {
+ free (dev->scanning->imagebuffer);
+ dev->scanning->imagebuffer = NULL;
+ }
+
+ SetLock (dev->usb_handle, NULL, (scan2.depth == 16) ? FALSE : TRUE);
+
+ /* Reservamos los buffers necesarios para leer la imagen */
+ Reading_CreateBuffers (dev);
+
+ if (dev->Resize->type != RSZ_NONE)
+ Resize_Start (dev, &transferred); /* 6729 */
+
+ RTS_ScanCounter_Inc (dev);
+ }
+
+ DBG (DBG_FNC, "- RTS_Scanner_StartScan: %i\n", rst);
+
+ return rst;
+}
+
+static void
+Triplet_Gray (SANE_Byte * pPointer1, SANE_Byte * pPointer2,
+ SANE_Byte * buffer, SANE_Int channels_count)
+{
+ /*
+ pPointer1 = FAB8
+ pPointer2 = FABC
+ buffer = FAC0
+ channels_count = FAC4
+ */
+
+ SANE_Int value;
+ SANE_Int channel_size;
+
+ DBG (DBG_FNC,
+ "> Triplet_Gray(*pPointer1, *pPointer2, *buffer, channels_count=%i)\n",
+ channels_count);
+
+ channel_size = (scan2.depth > 8) ? 2 : 1;
+ channels_count = channels_count / 2;
+
+ while (channels_count > 0)
+ {
+ value = data_lsb_get (pPointer1, channel_size);
+ data_lsb_set (buffer, value, channel_size);
+
+ value = data_lsb_get (pPointer2, channel_size);
+ data_lsb_set (buffer + channel_size, value, channel_size);
+
+ pPointer1 += 2 * channel_size;
+ pPointer2 += 2 * channel_size;
+ buffer += 2 * channel_size;
+
+ channels_count--;
+ }
+}
+
+static void
+Triplet_Lineart (SANE_Byte * pPointer1, SANE_Byte * pPointer2,
+ SANE_Byte * buffer, SANE_Int channels_count)
+{
+ /* Composing colour in lineart mode */
+
+ SANE_Int dots_count = 0;
+ SANE_Int channel;
+ SANE_Byte mask;
+ SANE_Byte value;
+ SANE_Int C;
+
+ DBG (DBG_FNC,
+ "> Triplet_Lineart(*pPointer1, *pPointer2, *buffer, channels_count=%i)\n",
+ channels_count);
+
+ if (channels_count > 0)
+ {
+ dots_count = (channels_count + 1) / 2;
+ while (dots_count > 0)
+ {
+ mask = 0x80;
+ channel = 2;
+ do
+ {
+ value = 0;
+ for (C = 4; C > 0; C--)
+ {
+ value =
+ (value << 2) +
+ (((*pPointer2 & mask) << 1) | (*pPointer1 & mask));
+ mask = mask >> 1;
+ }
+ *buffer = value;
+ buffer++;
+ channel--;
+ }
+ while (channel > 0);
+ pPointer2 += 2;
+ pPointer1 += 2;
+ dots_count--;
+ }
+ }
+}
+
+static SANE_Int
+Arrange_NonColour (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size, SANE_Int * transferred)
+{
+ /*
+ buffer : fadc
+ buffer_size : fae0
+ */
+
+ SANE_Int lines_count = 0; /* ebp */
+ SANE_Int channels_count = 0; /* fadc pisa buffer */
+ SANE_Int rst = ERROR;
+ struct st_scanning *scn;
+
+ DBG (DBG_FNC,
+ "+ Arrange_NonColour(*buffer, buffer_size=%i, *transferred):\n",
+ buffer_size);
+
+ /* this is just to make code more legible */
+ scn = dev->scanning;
+
+ if (scn->imagebuffer == NULL)
+ {
+ if ((scn->arrange_hres == TRUE) || (scan2.colormode == CM_LINEART))
+ {
+ scn->bfsize = (scn->arrange_sensor_evenodd_dist + 1) * line_size;
+ scn->imagebuffer =
+ (SANE_Byte *) malloc (scn->bfsize * sizeof (SANE_Byte));
+ if (scn->imagebuffer != NULL)
+ {
+ if (Read_Block (dev, scn->bfsize, scn->imagebuffer, transferred)
+ == OK)
+ {
+ scn->channel_size = (scan2.depth == 8) ? 1 : 2;
+ scn->desp1[CL_RED] = 0;
+ scn->desp2[CL_RED] =
+ scn->channel_size +
+ (scn->arrange_sensor_evenodd_dist * line_size);
+ scn->pColour2[CL_RED] =
+ scn->imagebuffer + scn->desp2[CL_RED];
+ scn->pColour1[CL_RED] =
+ scn->imagebuffer + scn->desp1[CL_RED];
+ rst = OK;
+ }
+ }
+ }
+ }
+ else
+ rst = OK;
+
+ /* b0f4 */
+ if (rst == OK)
+ {
+ scn->imagepointer = scn->imagebuffer;
+ lines_count = buffer_size / line_size;
+ channels_count = line_size / scn->channel_size;
+ while (lines_count > 0)
+ {
+ if (scan2.colormode == CM_LINEART)
+ Triplet_Lineart (scn->pColour1[CL_RED], scn->pColour2[CL_RED],
+ buffer, channels_count);
+ else
+ Triplet_Gray (scn->pColour1[CL_RED], scn->pColour2[CL_RED],
+ buffer, channels_count);
+
+ buffer += line_size;
+ scn->arrange_size -= bytesperline;
+
+ lines_count--;
+ if (lines_count == 0)
+ {
+ if ((scn->arrange_size | v15bc) == 0)
+ break;
+ }
+
+ rst = Read_Block (dev, line_size, scn->imagepointer, transferred);
+ if (rst != OK)
+ break;
+
+ if (scn->arrange_hres == TRUE)
+ {
+ scn->desp2[CL_RED] =
+ (line_size + scn->desp2[CL_RED]) % scn->bfsize;
+ scn->desp1[CL_RED] =
+ (line_size + scn->desp1[CL_RED]) % scn->bfsize;
+
+ scn->pColour2[CL_RED] = scn->imagebuffer + scn->desp2[CL_RED];
+ scn->pColour1[CL_RED] = scn->imagebuffer + scn->desp1[CL_RED];
+ }
+
+ /* b21d */
+ scn->imagepointer += line_size;
+ if (scn->imagepointer >= (scn->imagebuffer + scn->bfsize))
+ scn->imagepointer = scn->imagebuffer;
+ }
+ }
+
+ /* 2246 */
+
+ DBG (DBG_FNC, "- Arrange_NonColour(*transferred=%i): %i\n", *transferred,
+ rst);
+
+ return rst;
+}
+
+static SANE_Int
+Resize_Decrease (SANE_Byte * to_buffer, SANE_Int to_resolution,
+ SANE_Int to_width, SANE_Byte * from_buffer,
+ SANE_Int from_resolution, SANE_Int from_width,
+ SANE_Int myresize_mode)
+{
+ /*
+ to_buffer = FAC8 = 0x236200
+ to_resolution = FACC = 0x4b
+ to_width = FAD0 = 0x352
+ from_buffer = FAD4 = 0x235460
+ from_resolution = FAD8 = 0x64
+ from_width = FADC = 0x46d
+ myresize_mode = FAE0 = 1
+ */
+
+ SANE_Int rst = ERROR;
+ SANE_Int channels = 0; /* fac8 */
+ SANE_Int depth = 0; /* fae0 */
+ SANE_Int color[3] = { 0, 0, 0 }; /* fab8 | fabc | fac0 */
+ SANE_Int to_pos = 0; /* fad4 */
+ SANE_Int rescont = 0;
+ SANE_Int from_pos = 0; /* fab4 */
+ SANE_Int C;
+ SANE_Int smres = 0; /* fab0 */
+ SANE_Int value;
+ SANE_Int channel_size;
+
+ to_resolution = to_resolution & 0xffff;
+ from_resolution = from_resolution & 0xffff;
+
+ DBG (DBG_FNC,
+ "+ Resize_Decrease(*to_buffer, to_resolution=%i, to_width=%i, *from_buffer, from_resolution=%i, from_width=%i, myresize_mode=%i):\n",
+ to_resolution, to_width, from_resolution, from_width, myresize_mode);
+
+ if (myresize_mode != RSZ_LINEART)
+ {
+ switch (myresize_mode)
+ {
+ case RSZ_GRAYL:
+ channels = 1;
+ depth = 8;
+ break;
+ case RSZ_COLOURL:
+ channels = 3;
+ depth = 8;
+ break;
+ case RSZ_COLOURH:
+ channels = 3;
+ depth = 16;
+ break;
+ case RSZ_GRAYH:
+ channels = 1;
+ depth = 16;
+ break;
+ }
+
+ channel_size = (depth > 8) ? 2 : 1;
+ to_pos = 0;
+ rescont = 0;
+
+ while (to_pos < to_width)
+ {
+ from_pos++;
+ if (from_pos > from_width)
+ from_buffer -= (((depth + 7) / 8) * channels);
+
+ rescont += to_resolution;
+ if (rescont < from_resolution)
+ {
+ /* Adds 3 color channel values */
+ for (C = 0; C < channels; C++)
+ {
+ color[C] +=
+ data_lsb_get (from_buffer, channel_size) * to_resolution;
+ from_buffer += channel_size;
+ }
+ }
+ else
+ {
+ /* fc3c */
+ to_pos++;
+ smres = to_resolution - (rescont - from_resolution);
+ for (C = 0; C < channels; C++)
+ {
+ value =
+ ((data_lsb_get (from_buffer, channel_size) * smres) +
+ color[C]) / from_resolution;
+ data_lsb_set (to_buffer, value, channel_size);
+ color[C] =
+ data_lsb_get (from_buffer,
+ channel_size) * (rescont - from_resolution);
+
+ to_buffer += channel_size;
+ from_buffer += channel_size;
+ }
+ rescont -= from_resolution;
+ }
+ }
+
+ rst = OK;
+ }
+ else
+ {
+ /* fd60 */
+ SANE_Int bit, pos, desp, rescont2;
+
+ *to_buffer = 0;
+ bit = 0;
+ pos = 0;
+ desp = 0;
+ rescont = 0;
+ rescont2 = 0;
+ if (to_width > 0)
+ {
+ do
+ {
+ if (bit == 8)
+ {
+ /* fda6 */
+ bit = 0;
+ to_buffer++;
+ *to_buffer = 0;
+ }
+
+ rescont += to_resolution;
+ if (rescont < from_resolution)
+ {
+ if ((*from_buffer & (0x80 >> desp)) != 0)
+ rescont2 += to_resolution;
+ }
+ else
+ {
+ /*fdd5 */
+ pos++;
+ rescont -= from_resolution;
+ if ((*from_buffer & (0x80 >> desp)) != 0)
+ /*fdee */
+ rescont2 += (to_resolution - rescont);
+ if (rescont2 > (to_resolution / 2))
+ /* fe00 */
+ *to_buffer = _B0 (*to_buffer | (0x80 >> bit));
+ rescont2 =
+ ((*from_buffer & (0x80 >> desp)) != 0) ? rescont : 0;
+ bit++;
+ }
+
+ /* fe2f */
+ desp++;
+ if (desp == 8)
+ {
+ desp = 0;
+ from_buffer++;
+ }
+ }
+ while (pos < to_width);
+ }
+ else
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- Resize_Decrease: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Resize_Increase (SANE_Byte * to_buffer, SANE_Int to_resolution,
+ SANE_Int to_width, SANE_Byte * from_buffer,
+ SANE_Int from_resolution, SANE_Int from_width,
+ SANE_Int myresize_mode)
+{
+ /*
+
+ to_buffer = FAC8 = 0x2353f0
+ to_resolution = FACC = 0x4b
+ to_width = FAD0 = 0x352
+ from_buffer = FAD4 = 0x234650
+ from_resolution = FAD8 = 0x64
+ from_width = FADC = 0x46d
+ myresize_mode = FAE0 = 1
+ */
+
+ SANE_Int rst = ERROR;
+ SANE_Int desp; /* fac0 */
+ SANE_Byte *myp2; /* faac */
+ SANE_Int mywidth; /* fab4 fab8 */
+ SANE_Int mychannels; /* fabc */
+ SANE_Int channels = 0; /* faa4 */
+ SANE_Int depth = 0; /* faa8 */
+ SANE_Int pos = 0; /* fae0 */
+ SANE_Int rescount;
+ SANE_Int val6 = 0;
+ SANE_Int val7 = 0;
+ SANE_Int value;
+ /**/
+ DBG (DBG_FNC,
+ "+ Resize_Increase(*to_buffer, to_resolution=%i, to_width=%i, *from_buffer, from_resolution=%i, from_width=%i, myresize_mode=%i):\n",
+ to_resolution, to_width, from_resolution, from_width, myresize_mode);
+
+ if (myresize_mode != RSZ_LINEART)
+ {
+ switch (myresize_mode)
+ {
+ case RSZ_GRAYL:
+ channels = 1;
+ depth = 8;
+ break;
+ case RSZ_COLOURL:
+ channels = 3;
+ depth = 8;
+ break;
+ case RSZ_COLOURH:
+ channels = 3;
+ depth = 16;
+ break;
+ case RSZ_GRAYH:
+ channels = 1;
+ depth = 16;
+ break;
+ }
+
+ if (channels > 0)
+ {
+ SANE_Byte channel_size;
+ SANE_Byte *p_dst; /* fac8 */
+ SANE_Byte *p_src; /* fad4 */
+
+ desp = to_buffer - from_buffer;
+ myp2 = from_buffer;
+ channel_size = (depth == 8) ? 1 : 2;
+
+ for (mychannels = 0; mychannels < channels; mychannels++)
+ {
+ pos = 0;
+ rescount = (from_resolution / 2) + to_resolution;
+
+ p_src = myp2;
+ p_dst = myp2 + desp;
+
+ /* f938 */
+ val7 = data_lsb_get (p_src, channel_size);
+
+ if (to_width > 0)
+ {
+ for (mywidth = 0; mywidth < to_width; mywidth++)
+ {
+ if (rescount >= to_resolution)
+ {
+ rescount -= to_resolution;
+ val6 = val7;
+ pos++;
+ if (pos < from_width)
+ {
+ p_src += (channels * channel_size);
+ val7 = data_lsb_get (p_src, channel_size);
+ }
+ }
+
+ /*f9a5 */
+ data_lsb_set (p_dst,
+ ((((to_resolution - rescount) * val6) +
+ (val7 * rescount)) / to_resolution),
+ channel_size);
+ rescount += from_resolution;
+ p_dst += (channels * channel_size);
+ }
+ }
+
+ myp2 += channel_size;
+ }
+
+ rst = OK;
+ }
+ else
+ rst = OK;
+ }
+ else
+ {
+ /* RSZ_LINEART mode */
+ /* fa02 */
+ /*
+ to_buffer = FAC8 = 0x2353f0
+ to_resolution = FACC = 0x4b
+ to_width = FAD0 = 0x352
+ from_buffer = FAD4 = 0x234650
+ from_resolution = FAD8 = 0x64
+ from_width = FADC = 0x46d
+ myresize_mode = FAE0 = 1
+ */
+ SANE_Int myres2; /* fac8 */
+ SANE_Int sres;
+ SANE_Int lfae0;
+ SANE_Int lfad8;
+ SANE_Int myres;
+ SANE_Int cont = 1;
+ SANE_Int someval;
+ SANE_Int bit; /*lfaa8 */
+
+ myres2 = from_resolution;
+ sres = (myres2 / 2) + to_resolution;
+ value = _B0 (*from_buffer);
+ bit = 0;
+ lfae0 = 0;
+ lfad8 = value >> 7;
+ someval = lfad8;
+ *to_buffer = 0;
+
+ if (to_width > 0)
+ {
+ myres = to_resolution;
+ to_resolution = myres / 2;
+ do
+ {
+ if (sres >= myres)
+ {
+ sres -= myres;
+ lfae0++;
+ cont++;
+ lfad8 = someval;
+ if (lfae0 < from_width)
+ {
+ if (cont == 8)
+ {
+ cont = 0;
+ from_buffer++;
+ }
+ bit = (((0x80 >> cont) & *from_buffer) != 0) ? 1 : 0;
+ }
+ }
+ /*faa6 */
+ if ((((myres - sres) * lfad8) + (bit * sres)) > to_resolution)
+ *to_buffer |= (0x80 >> bit);
+
+ bit++;
+ if (bit == 8)
+ {
+ bit = 0;
+ to_buffer++;
+ *to_buffer = 0;
+ }
+ to_width--;
+ sres += myres2;
+ }
+ while (to_width > 0);
+ rst = OK;
+ }
+ }
+
+ DBG (DBG_FNC, "- Resize_Increase: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Resize_Start (struct st_device *dev, SANE_Int * transferred)
+{
+ SANE_Int rst = ERROR;
+ struct st_resize *rz = dev->Resize;
+
+ DBG (DBG_FNC, "+ Resize_Start(*transferred):\n");
+
+ if (Resize_CreateBuffers
+ (dev, line_size, rz->bytesperline, rz->bytesperline) == ERROR)
+ return ERROR;
+
+ if (arrangeline2 == FIX_BY_SOFT)
+ {
+ /* fee0 */
+ if (scan2.colormode == CM_COLOR)
+ rst = Arrange_Colour (dev, rz->v3624, line_size, transferred);
+ else
+ rst = Arrange_NonColour (dev, rz->v3624, line_size, transferred);
+ }
+ else
+ rst = Read_Block (dev, line_size, rz->v3624, transferred); /* ff03 */
+
+ /* Redimensionado */
+ switch (rz->type)
+ {
+ case RSZ_DECREASE:
+ /* ff1b */
+ Resize_Decrease (rz->v3628, rz->resolution_x, rz->towidth, rz->v3624,
+ scan2.resolution_x, rz->fromwidth, rz->mode);
+ break;
+ case RSZ_INCREASE:
+ /* ff69 */
+ rz->rescount = 0;
+ Resize_Increase (rz->v3628, rz->resolution_x, rz->towidth, rz->v3624,
+ scan2.resolution_x, rz->fromwidth, rz->mode);
+ if (arrangeline2 == FIX_BY_SOFT)
+ {
+ /* ffb1 */
+ if (scan2.colormode == CM_COLOR)
+ rst = Arrange_Colour (dev, rz->v3624, line_size, transferred);
+ else
+ rst = Arrange_NonColour (dev, rz->v3624, line_size, transferred);
+ }
+ else
+ rst = Read_Block (dev, line_size, rz->v3624, transferred); /* ffe0 */
+
+ /* fff2 */
+ Resize_Increase (rz->v362c, rz->resolution_x, rz->towidth, rz->v3624,
+ scan2.resolution_x, rz->fromwidth, rz->mode);
+ break;
+ }
+
+ /* 002a */
+
+ DBG (DBG_FNC, "- Resize_Start(*transferred=%i): %i\n", *transferred, rst);
+
+ return rst;
+}
+
+static SANE_Int
+Resize_CreateBuffers (struct st_device *dev, SANE_Int size1, SANE_Int size2,
+ SANE_Int size3)
+{
+ SANE_Int rst = ERROR;
+ struct st_resize *rz = dev->Resize;
+
+ rz->v3624 = (SANE_Byte *) malloc ((size1 + 0x40) * sizeof (SANE_Byte));
+ rz->v3628 = (SANE_Byte *) malloc ((size2 + 0x40) * sizeof (SANE_Byte));
+ rz->v362c = (SANE_Byte *) malloc ((size3 + 0x40) * sizeof (SANE_Byte));
+
+ if ((rz->v3624 == NULL) || (rz->v3628 == NULL) || (rz->v362c == NULL))
+ Resize_DestroyBuffers (dev);
+ else
+ rst = OK;
+
+ DBG (DBG_FNC, "> Resize_CreateBuffers(size1=%i, size2=%i, size3=%i): %i\n",
+ size1, size2, size3, rst);
+
+ return rst;
+}
+
+static SANE_Int
+Resize_DestroyBuffers (struct st_device *dev)
+{
+ struct st_resize *rz = dev->Resize;
+
+ if (rz->v3624 != NULL)
+ free (rz->v3624);
+
+ if (rz->v3628 != NULL)
+ free (rz->v3628);
+
+ if (rz->v362c != NULL)
+ free (rz->v362c);
+
+ rz->v3624 = NULL;
+ rz->v3628 = NULL;
+ rz->v362c = NULL;
+
+ return OK;
+}
+
+static SANE_Int
+Reading_DestroyBuffers (struct st_device *dev)
+{
+ DBG (DBG_FNC, "> Reading_DestroyBuffers():\n");
+
+ if (dev->Reading->DMABuffer != NULL)
+ free (dev->Reading->DMABuffer);
+
+ if (dev->scanning->imagebuffer != NULL)
+ {
+ free (dev->scanning->imagebuffer);
+ dev->scanning->imagebuffer = NULL;
+ }
+
+ bzero (dev->Reading, sizeof (struct st_readimage));
+
+ return OK;
+}
+
+static SANE_Int
+Gamma_SendTables (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Byte * gammatable, SANE_Int size)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ Gamma_SendTables(*Regs, *gammatable, size=%i):\n", size);
+
+ if ((gammatable != NULL) && (size > 0))
+ {
+ SANE_Int transferred;
+ SANE_Int first_table;
+ SANE_Int cont = 0;
+ SANE_Int retry = TRUE;
+ SANE_Byte *mybuffer;
+
+ /* lock */
+ SetLock (dev->usb_handle, Regs, TRUE);
+
+ first_table = (data_lsb_get (&Regs[0x1b4], 2) & 0x3fff) >> 4;
+
+ mybuffer = (SANE_Byte *) malloc (sizeof (SANE_Byte) * size);
+ if (mybuffer != NULL)
+ {
+ /* Try to send buffer during 10 seconds */
+ long tick = GetTickCount () + 10000;
+ while ((retry == TRUE) && (tick > GetTickCount ()))
+ {
+ retry = FALSE;
+
+ /* Operation type 0x14 */
+ if (IWrite_Word (dev->usb_handle, 0x0000, 0x0014, 0x0800) == OK)
+ {
+ /* Send size to write */
+ if (RTS_DMA_Enable_Write (dev, 0x0000, size, first_table) ==
+ OK)
+ {
+ /* Send data */
+ if (Bulk_Operation
+ (dev, BLK_WRITE, size, gammatable,
+ &transferred) == OK)
+ {
+ /* Send size to read */
+ if (RTS_DMA_Enable_Read
+ (dev, 0x0000, size, first_table) == OK)
+ {
+ /* Retrieve data */
+ if (Bulk_Operation
+ (dev, BLK_READ, size, mybuffer,
+ &transferred) == OK)
+ {
+ /* Check data */
+ while ((cont < size) && (retry == FALSE))
+ {
+ if (mybuffer[cont] != gammatable[cont])
+ retry = TRUE;
+ cont++;
+ }
+
+ if (retry == FALSE)
+ rst = OK;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ free (mybuffer);
+ }
+
+ /* unlock */
+ SetLock (dev->usb_handle, Regs, FALSE);
+ }
+
+ DBG (DBG_FNC, "- Gamma_SendTables: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Gamma_GetTables (struct st_device *dev, SANE_Byte * Gamma_buffer)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ Gamma_GetTables(SANE_Byte *Gamma_buffer):\n");
+
+ if (Gamma_buffer == NULL)
+ return ERROR;
+
+ /* Operation type 0x14 */
+ if (IWrite_Word (dev->usb_handle, 0x0000, 0x0014, 0x0800) == 0x00)
+ {
+ SANE_Int size = 768;
+
+ if (RTS_DMA_Enable_Read (dev, 0x0000, size, 0) == OK)
+ {
+ SANE_Int transferred = 0;
+ usleep (1000 * 500);
+
+ /* Read buffer */
+ rst =
+ Bulk_Operation (dev, BLK_READ, size, Gamma_buffer, &transferred);
+ }
+ }
+
+ DBG (DBG_FNC, "- Gamma_GetTables: %i\n", rst);
+
+ return rst;
+}
+
+static void
+Gamma_FreeTables ()
+{
+ SANE_Int c;
+
+ DBG (DBG_FNC, "> Gamma_FreeTables()\n");
+
+ for (c = 0; c < 3; c++)
+ {
+ if (hp_gamma->table[c] != NULL)
+ {
+ free (hp_gamma->table[c]);
+ hp_gamma->table[c] = NULL;
+ }
+ }
+ use_gamma_tables = FALSE;
+}
+
+static void
+RTS_Scanner_StopScan (struct st_device *dev, SANE_Int wait)
+{
+ SANE_Byte data;
+
+ DBG (DBG_FNC, "+ RTS_Scanner_StopScan():\n");
+
+ data = 0;
+
+ Reading_DestroyBuffers (dev);
+ Resize_DestroyBuffers (dev);
+
+ RTS_DMA_Reset (dev);
+
+ data_bitset (&dev->init_regs[0x60b], 0x10, 0);
+ data_bitset (&dev->init_regs[0x60a], 0x40, 0);
+
+ if (Write_Buffer (dev->usb_handle, 0xee0a, &dev->init_regs[0x60a], 2) == OK)
+ Motor_Change (dev, dev->init_regs, 3);
+
+ usleep (1000 * 200);
+
+ if (wait == FALSE)
+ {
+ Read_Byte (dev->usb_handle, 0xe801, &data);
+ if ((data & 0x02) == 0)
+ {
+ if (Head_IsAtHome (dev, dev->init_regs) == FALSE)
+ {
+ /* clear execution bit */
+ data_bitset (&dev->init_regs[0x00], 0x80, 0);
+
+ Write_Byte (dev->usb_handle, 0x00, dev->init_regs[0x00]);
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+ }
+ }
+ }
+ else
+ {
+ /*66a1 */
+ /* clear execution bit */
+ data_bitset (&dev->init_regs[0x00], 0x80, 0);
+
+ Write_Byte (dev->usb_handle, 0x00, dev->init_regs[0x00]);
+ if (Head_IsAtHome (dev, dev->init_regs) == FALSE)
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+ }
+
+ /*66e0 */
+ RTS_Enable_CCD (dev, dev->init_regs, 0);
+
+ Lamp_Status_Timer_Set (dev, 13);
+
+ DBG (DBG_FNC, "- RTS_Scanner_StopScan()\n");
+}
+
+static SANE_Int
+Reading_CreateBuffers (struct st_device *dev)
+{
+ SANE_Byte data;
+ SANE_Int mybytesperline;
+ SANE_Int mybuffersize, a, b;
+
+ DBG (DBG_FNC, "+ Reading_CreateBuffers():\n");
+
+ data = 0;
+
+ /* Gets BinarythresholdH */
+ if (Read_Byte (dev->usb_handle, 0xe9a1, &data) == OK)
+ binarythresholdh = data;
+
+ mybytesperline =
+ (scan2.depth == 12) ? (bytesperline * 3) / 4 : bytesperline;
+
+ dev->Reading->Max_Size = 0xfc00;
+ dev->Reading->DMAAmount = 0;
+
+ a = (RTS_Debug->dmabuffersize / 63);
+ b = (((RTS_Debug->dmabuffersize - a) / 2) + a) >> 0x0f;
+ mybuffersize = ((b << 6) - b) << 10;
+ if (mybuffersize < 0x1f800)
+ mybuffersize = 0x1f800;
+
+ dev->Reading->DMABufferSize = mybuffersize; /*3FFC00 4193280 */
+
+ do
+ {
+ dev->Reading->DMABuffer =
+ (SANE_Byte *) malloc (dev->Reading->DMABufferSize *
+ sizeof (SANE_Byte));
+ if (dev->Reading->DMABuffer != NULL)
+ break;
+ dev->Reading->DMABufferSize -= dev->Reading->Max_Size;
+ }
+ while (dev->Reading->DMABufferSize >= dev->Reading->Max_Size);
+
+ /* 6003 */
+ dev->Reading->Starting = TRUE;
+
+ dev->Reading->Size4Lines = (mybytesperline > dev->Reading->Max_Size) ?
+ mybytesperline : (dev->Reading->Max_Size / mybytesperline) *
+ mybytesperline;
+
+ dev->Reading->ImageSize = imagesize;
+ read_v15b4 = v15b4;
+
+ DBG (DBG_FNC, "- Reading_CreateBuffers():\n");
+
+ return OK;
+}
+
+static SANE_Int
+RTS_ScanCounter_Inc (struct st_device *dev)
+{
+ /* Keep a count of the number of scans done by this scanner */
+
+ SANE_Int idata;
+
+ DBG (DBG_FNC, "+ RTS_ScanCounter_Inc():\n");
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ SANE_Byte cdata = 0;
+ SANE_Byte somebuffer[26];
+
+ switch (dev->chipset->model)
+ {
+ case RTS8822L_02A:
+ case RTS8822BL_03A:
+ /* value is 4 bytes size starting from address 0x21 in msb format */
+ if (RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata) == OK)
+ {
+ idata = data_swap_endianess (idata, 4) + 1;
+ idata = data_swap_endianess (idata, 4);
+ RTS_EEPROM_WriteInteger (dev->usb_handle, 0x21, idata);
+ }
+ break;
+ default:
+ /* value is 4 bytes size starting from address 0x21 in lsb format */
+ bzero (&somebuffer, sizeof (somebuffer));
+ somebuffer[4] = 0x0c;
+
+ RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata);
+ data_lsb_set (&somebuffer[0], idata + 1, 4);
+
+ RTS_EEPROM_ReadByte (dev->usb_handle, 0x003a, &cdata);
+ somebuffer[25] = cdata;
+ RTS_EEPROM_WriteBuffer (dev->usb_handle, 0x21, somebuffer, 0x1a);
+ break;
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_ScanCounter_Inc()\n");
+
+ return OK;
+}
+
+static SANE_Int
+RTS_ScanCounter_Get (struct st_device *dev)
+{
+ /* Returns the number of scans done by this scanner */
+
+ SANE_Int idata = 0;
+
+ DBG (DBG_FNC, "+ RTS_ScanCounter_Get():\n");
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata);
+
+ switch (dev->chipset->model)
+ {
+ case RTS8822L_02A:
+ case RTS8822BL_03A:
+ /* value is 4 bytes size starting from address 0x21 in msb format */
+ idata = data_swap_endianess (idata, 4);
+ break;
+ default: /* RTS8822L_01H */
+ /* value is 4 bytes size starting from address 0x21 in lsb format */
+ idata &= 0xffffffff;
+ break;
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_ScanCounter_Get(): %i\n", idata);
+
+ return idata;
+}
+
+static SANE_Int
+Read_Image (struct st_device *dev, SANE_Int buffer_size, SANE_Byte * buffer,
+ SANE_Int * transferred)
+{
+ SANE_Int rst;
+ SANE_Byte mycolormode;
+
+ DBG (DBG_FNC, "+ Read_Image(buffer_size=%i, *buffer, *transferred):\n",
+ buffer_size);
+
+ *transferred = 0;
+ mycolormode = scan2.colormode;
+ rst = ERROR;
+ if ((scan2.colormode != CM_COLOR) && (scan2.channel == 3))
+ mycolormode = 3;
+
+ if (dev->Resize->type == RSZ_NONE)
+ {
+ if (arrangeline == FIX_BY_SOFT)
+ {
+ switch (mycolormode)
+ {
+ case CM_COLOR:
+ rst = Arrange_Colour (dev, buffer, buffer_size, transferred);
+ break;
+ case 3:
+ rst = Arrange_Compose (dev, buffer, buffer_size, transferred);
+ break;
+ default:
+ rst = Arrange_NonColour (dev, buffer, buffer_size, transferred);
+ break;
+ }
+ }
+ else
+ rst = Read_Block (dev, buffer_size, buffer, transferred); /*00fe */
+ }
+ else
+ rst = Read_ResizeBlock (dev, buffer, buffer_size, transferred); /*010d */
+
+ DBG (DBG_FNC, "- Read_Image(*transferred=%i): %i\n", *transferred, rst);
+
+ return rst;
+}
+
+static SANE_Int
+Arrange_Compose (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size, SANE_Int * transferred)
+{
+ /*
+ fnb250
+
+ 0600FA7C 05E10048 buffer
+ 0600FA80 0000F906 buffer_size
+ */
+ SANE_Byte *mybuffer = buffer; /* fa7c */
+ SANE_Int mydistance; /*ebp */
+ SANE_Int mydots; /*fa74 */
+ SANE_Int channel_size;
+ SANE_Int c;
+ struct st_scanning *scn;
+
+ /*mywidth = fa70 */
+
+ DBG (DBG_FNC, "+ Arrange_Compose(*buffer, buffer_size=%i, *transferred):\n",
+ buffer_size);
+
+ channel_size = (scan2.depth == 8) ? 1 : 2;
+
+ /* this is just to make code more legible */
+ scn = dev->scanning;
+
+ if (scn->imagebuffer == NULL)
+ {
+ if (dev->sensorcfg->type == CCD_SENSOR)
+ mydistance =
+ (dev->sensorcfg->line_distance * scan2.resolution_y) /
+ dev->sensorcfg->resolution;
+ else
+ mydistance = 0;
+
+ if (mydistance != 0)
+ {
+ scn->bfsize =
+ (scn->arrange_hres ==
+ TRUE) ? scn->arrange_sensor_evenodd_dist : 0;
+ scn->bfsize = line_size * (scn->bfsize + (mydistance * 2) + 1);
+ }
+ else
+ scn->bfsize = line_size * 2;
+
+ /*b2f0 */
+ scn->imagebuffer =
+ (SANE_Byte *) malloc (scn->bfsize * sizeof (SANE_Byte));
+ if (scn->imagebuffer == NULL)
+ return ERROR;
+
+ scn->imagepointer = scn->imagebuffer;
+ if (Read_Block (dev, scn->bfsize, scn->imagebuffer, transferred) ==
+ ERROR)
+ return ERROR;
+
+ /* Calculate channel displacements */
+ scn->arrange_orderchannel = FALSE;
+ for (c = CL_RED; c <= CL_BLUE; c++)
+ {
+ if (mydistance == 0)
+ {
+ /*b34e */
+ if (scn->arrange_hres == FALSE)
+ {
+ if ((((dev->sensorcfg->line_distance * scan2.resolution_y) *
+ 2) / dev->sensorcfg->resolution) == 1)
+ scn->arrange_orderchannel = TRUE;
+
+ if (scn->arrange_orderchannel == TRUE)
+ scn->desp[c] =
+ ((dev->sensorcfg->rgb_order[c] / 2) * line_size) +
+ (channel_size * c);
+ else
+ scn->desp[c] = channel_size * c;
+ }
+ }
+ else
+ {
+ /*b3e3 */
+ scn->desp[c] =
+ (dev->sensorcfg->rgb_order[c] * (mydistance * line_size)) +
+ (channel_size * c);
+
+ if (scn->arrange_hres == TRUE)
+ {
+ /*b43b */
+ scn->desp1[c] = scn->desp[c];
+ scn->desp2[c] =
+ ((channel_size * 3) + scn->desp1[c]) +
+ (scn->arrange_sensor_evenodd_dist * line_size);
+ };
+ }
+ }
+
+ for (c = CL_RED; c <= CL_BLUE; c++)
+ {
+ if (scn->arrange_hres == TRUE)
+ {
+ scn->pColour2[c] = scn->imagebuffer + scn->desp2[c];
+ scn->pColour1[c] = scn->imagebuffer + scn->desp1[c];
+ }
+ else
+ scn->pColour[c] = scn->imagebuffer + scn->desp[c];
+ }
+ }
+
+ /*b545 */
+ buffer_size /= line_size;
+ mydots = line_size / (channel_size * 3);
+
+ while (buffer_size > 0)
+ {
+ if (scn->arrange_orderchannel == FALSE)
+ {
+ /*b5aa */
+ if (scn->arrange_hres == TRUE)
+ Triplet_Compose_HRes (scn->pColour1[CL_RED],
+ scn->pColour1[CL_GREEN],
+ scn->pColour1[CL_BLUE],
+ scn->pColour2[CL_RED],
+ scn->pColour2[CL_GREEN],
+ scn->pColour2[CL_BLUE], mybuffer, mydots);
+ else
+ Triplet_Compose_LRes (scn->pColour[CL_RED],
+ scn->pColour[CL_GREEN],
+ scn->pColour[CL_BLUE], mybuffer, mydots);
+ }
+ else
+ Triplet_Compose_Order (dev, scn->pColour[CL_RED],
+ scn->pColour[CL_GREEN], scn->pColour[CL_BLUE],
+ mybuffer, mydots);
+
+ /*b5f8 */
+ mybuffer += line_size;
+ scn->arrange_size -= bytesperline;
+ if (scn->arrange_size < 0)
+ v15bc--;
+
+ buffer_size--;
+ if (buffer_size == 0)
+ {
+ if ((scn->arrange_size | v15bc) == 0)
+ return OK;
+ }
+
+ /*b63f */
+ if (Read_Block (dev, line_size, scn->imagepointer, transferred) ==
+ ERROR)
+ return ERROR;
+
+ for (c = CL_RED; c <= CL_BLUE; c++)
+ {
+ if (scn->arrange_hres == TRUE)
+ {
+ /*b663 */
+ scn->desp2[c] = (scn->desp2[c] + line_size) % scn->bfsize;
+ scn->desp1[c] = (scn->desp1[c] + line_size) % scn->bfsize;
+
+ scn->pColour2[c] = scn->imagebuffer + scn->desp2[c];
+ scn->pColour1[c] = scn->imagebuffer + scn->desp1[c];
+ }
+ else
+ {
+ /*b74a */
+ scn->desp[c] = (scn->desp[c] + line_size) % scn->bfsize;
+ scn->pColour[c] = scn->imagebuffer + scn->desp[c];
+ }
+ }
+
+ /*b7be */
+ scn->imagepointer += line_size;
+ if (scn->imagepointer >= (scn->imagebuffer + scn->bfsize))
+ scn->imagepointer = scn->imagebuffer;
+ }
+
+ return OK;
+}
+
+static void
+Triplet_Compose_HRes (SANE_Byte * pRed1, SANE_Byte * pGreen1,
+ SANE_Byte * pBlue1, SANE_Byte * pRed2,
+ SANE_Byte * pGreen2, SANE_Byte * pBlue2,
+ SANE_Byte * buffer, SANE_Int Width)
+{
+ SANE_Int Value;
+ SANE_Int Channel_size;
+ SANE_Int max_value;
+
+ DBG (DBG_FNC,
+ "> Triplet_Compose_HRes(*pRed1, *pGreen1, *pBlue1, *pRed2 *pGreen2, *pBlue2, *buffer, Width=%i):\n",
+ Width);
+
+ Width /= 2;
+ Channel_size = (scan2.depth > 8) ? 2 : 1;
+ max_value = (1 << scan2.depth) - 1;
+
+ while (Width > 0)
+ {
+ Value =
+ data_lsb_get (pRed1, Channel_size) + data_lsb_get (pGreen1,
+ Channel_size) +
+ data_lsb_get (pBlue1, Channel_size);
+
+ Value = min (Value, max_value);
+
+ if (v1600 != NULL)
+ {
+ if (scan2.depth > 8)
+ Value = *(v1600 + (Value >> 8)) | _B0 (Value);
+ else
+ Value = *(v1600 + Value);
+ }
+
+ data_lsb_set (buffer, Value, Channel_size);
+ buffer += Channel_size;
+
+ Value =
+ data_lsb_get (pRed2, Channel_size) + data_lsb_get (pGreen2,
+ Channel_size) +
+ data_lsb_get (pBlue2, Channel_size);
+
+ Value = min (Value, max_value);
+
+ if (v1600 != NULL)
+ {
+ if (scan2.depth > 8)
+ Value = *(v1600 + (Value >> 8)) | _B0 (Value);
+ else
+ Value = *(v1600 + Value);
+ }
+
+ data_lsb_set (buffer, Value, Channel_size);
+ buffer += Channel_size;
+
+ pRed1 += 6 * Channel_size;
+ pGreen1 += 6 * Channel_size;
+ pBlue1 += 6 * Channel_size;
+
+ pRed2 += 6 * Channel_size;
+ pGreen2 += 6 * Channel_size;
+ pBlue2 += 6 * Channel_size;
+
+ Width--;
+ }
+}
+
+static void
+Triplet_Compose_Order (struct st_device *dev, SANE_Byte * pRed,
+ SANE_Byte * pGreen, SANE_Byte * pBlue,
+ SANE_Byte * buffer, SANE_Int dots)
+{
+ SANE_Int Value;
+
+ DBG (DBG_FNC,
+ "> Triplet_Compose_Order(*pRed, *pGreen, *pBlue, *buffer, dots=%i):\n",
+ dots);
+
+ if (scan2.depth > 8)
+ {
+ /* c0fe */
+ dots = dots / 2;
+ while (dots > 0)
+ {
+ Value =
+ min (data_lsb_get (pRed, 2) + data_lsb_get (pGreen, 2) +
+ data_lsb_get (pBlue, 2), 0xffff);
+
+ if (v1600 != NULL)
+ Value = (*(v1600 + (Value >> 8)) << 8) | _B0 (Value);
+
+ data_lsb_set (buffer, Value, 2);
+
+ buffer += 2;
+ pRed += 6;
+ pGreen += 6;
+ pBlue += 6;
+ dots--;
+ }
+ }
+ else
+ {
+ SANE_Byte *myp1, *myp2, *myp3;
+
+ if (dev->sensorcfg->rgb_order[CL_RED] == 1)
+ {
+ myp1 = pRed;
+ myp2 = pGreen;
+ myp3 = pBlue;
+ }
+ else if (dev->sensorcfg->rgb_order[CL_GREEN] == 1)
+ {
+ myp1 = pGreen;
+ myp2 = pRed;
+ myp3 = pBlue;
+ }
+ else
+ {
+ myp1 = pBlue;
+ myp2 = pRed;
+ myp3 = pGreen;
+ }
+
+ while (dots > 0)
+ {
+ Value =
+ min (((*myp1 + *(line_size + myp1)) / 2) + *myp2 + *myp3, 0xff);
+
+ *buffer = (v1600 == NULL) ? _B0 (Value) : *(v1600 + Value);
+
+ buffer++;
+ myp1 += 3;
+ myp2 += 3;
+ myp3 += 3;
+ dots--;
+ }
+ }
+}
+
+static void
+Triplet_Compose_LRes (SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue,
+ SANE_Byte * buffer, SANE_Int dots)
+{
+ SANE_Int Value;
+ SANE_Int Channel_size;
+ SANE_Int max_value;
+
+ DBG (DBG_FNC,
+ "> Triplet_Compose_LRes(*pRed, *pGreen, *pBlue, *buffer, dots=%i):\n",
+ dots);
+
+ Channel_size = (scan2.depth > 8) ? 2 : 1;
+ max_value = (1 << scan2.depth) - 1;
+
+ /*bf59 */
+ while (dots > 0)
+ {
+ Value =
+ data_lsb_get (pRed, Channel_size) + data_lsb_get (pGreen,
+ Channel_size) +
+ data_lsb_get (pBlue, Channel_size);
+
+ Value = min (Value, max_value);
+
+ if (v1600 != NULL)
+ {
+ if (scan2.depth > 8)
+ Value = (*(v1600 + (Value >> 8)) << 8) | _B0 (Value);
+ else
+ Value = _B0 (*(v1600 + Value));
+ }
+
+ data_lsb_set (buffer, Value, Channel_size);
+
+ buffer += Channel_size;
+ pRed += Channel_size * 3;
+ pGreen += Channel_size * 3;
+ pBlue += Channel_size * 3;
+ dots--;
+ }
+}
+
+static void
+Triplet_Colour_Order (struct st_device *dev, SANE_Byte * pRed,
+ SANE_Byte * pGreen, SANE_Byte * pBlue,
+ SANE_Byte * buffer, SANE_Int Width)
+{
+ SANE_Int Value;
+
+ DBG (DBG_FNC,
+ "> Triplet_Colour_Order(*pRed, *pGreen, *pBlue, *buffer, Width=%i):\n",
+ Width);
+
+ if (scan2.depth > 8)
+ {
+ Width = Width / 2;
+ while (Width > 0)
+ {
+ Value = data_lsb_get (pRed, 2);
+ data_lsb_set (buffer, Value, 2);
+
+ Value = data_lsb_get (pGreen, 2);
+ data_lsb_set (buffer + 2, Value, 2);
+
+ Value = data_lsb_get (pBlue, 2);
+ data_lsb_set (buffer + 4, Value, 2);
+
+ pRed += 6;
+ pGreen += 6;
+ pBlue += 6;
+ buffer += 6;
+ Width--;
+ }
+ }
+ else
+ {
+ SANE_Int Colour;
+
+ if (dev->sensorcfg->rgb_order[CL_RED] == 1)
+ Colour = CL_RED;
+ else if (dev->sensorcfg->rgb_order[CL_GREEN] == 1)
+ Colour = CL_GREEN;
+ else
+ Colour = CL_BLUE;
+
+ while (Width > 0)
+ {
+ switch (Colour)
+ {
+ case CL_RED:
+ *buffer = (*pRed + *(pRed + line_size)) / 2;
+ *(buffer + 1) = *pGreen;
+ *(buffer + 2) = *pBlue;
+ break;
+ case CL_GREEN:
+ *buffer = *pRed;
+ *(buffer + 1) = ((*pGreen + *(pGreen + line_size)) / 2);
+ *(buffer + 2) = *pBlue;
+ break;
+ case CL_BLUE:
+ *buffer = *pRed;
+ *(buffer + 1) = *pGreen;
+ *(buffer + 2) = ((*pBlue + *(pBlue + line_size)) / 2);
+ break;
+ }
+
+ pRed += 3;
+ pGreen += 3;
+ pBlue += 3;
+ buffer += 3;
+
+ Width--;
+ }
+ }
+}
+
+static void
+Triplet_Colour_HRes (SANE_Byte * pRed1, SANE_Byte * pGreen1,
+ SANE_Byte * pBlue1, SANE_Byte * pRed2,
+ SANE_Byte * pGreen2, SANE_Byte * pBlue2,
+ SANE_Byte * buffer, SANE_Int Width)
+{
+ SANE_Int Value;
+ SANE_Int channel_size;
+ SANE_Int c;
+ SANE_Byte *pPointers[6];
+
+ pPointers[0] = pRed1;
+ pPointers[1] = pGreen1;
+ pPointers[2] = pBlue1;
+
+ pPointers[3] = pRed2;
+ pPointers[4] = pGreen2;
+ pPointers[5] = pBlue2;
+
+ DBG (DBG_FNC,
+ "> Triplet_Colour_HRes(*pRed1, *pGreen1, *pBlue1, *pRed2, *pGreen2, *pBlue2, *buffer, Width=%i):\n",
+ Width);
+
+ channel_size = (scan2.depth > 8) ? 2 : 1;
+
+ Width = Width / 2;
+ while (Width > 0)
+ {
+ for (c = 0; c < 6; c++)
+ {
+ Value = data_lsb_get (pPointers[c], channel_size);
+ data_lsb_set (buffer, Value, channel_size);
+
+ pPointers[c] += (6 * channel_size);
+ buffer += (channel_size);
+ }
+ Width--;
+ }
+}
+
+static void
+Triplet_Colour_LRes (SANE_Int Width, SANE_Byte * Buffer,
+ SANE_Byte * pChannel1, SANE_Byte * pChannel2,
+ SANE_Byte * pChannel3)
+{
+ /*
+ 05F0FA4C 04EBAE4A /CALL to Assumed StdFunc6 from hpgt3970.04EBAE45
+ 05F0FA50 00234FF8 |Arg1 = 00234FF8 pChannel3
+ 05F0FA54 002359EF |Arg2 = 002359EF pChannel2
+ 05F0FA58 002363E6 |Arg3 = 002363E6 pChannel1
+ 05F0FA5C 05D10048 |Arg4 = 05D10048 Buffer
+ 05F0FA60 00000352 |Arg5 = 00000352 Width
+ */
+
+ /* Esta funcion une los tres canales de color en un triplete
+ Inicialmente cada color está separado en 3 buffers apuntados
+ por pChannel1 ,2 y 3
+ */
+ SANE_Int Value;
+ SANE_Int channel_size;
+ SANE_Int c;
+ SANE_Byte *pChannels[3];
+
+ pChannels[0] = pChannel3;
+ pChannels[1] = pChannel2;
+ pChannels[2] = pChannel1;
+
+ DBG (DBG_FNC, "> Triplet_Colour_LRes(Width=%i, *Buffer2, *p1, *p2, *p3):\n",
+ Width);
+
+ channel_size = (scan2.depth > 8) ? 2 : 1;
+ while (Width > 0)
+ {
+ /* ba74 */
+ for (c = 0; c < 3; c++)
+ {
+ Value = data_lsb_get (pChannels[c], channel_size);
+ data_lsb_set (Buffer, Value, channel_size);
+
+ pChannels[c] += channel_size;
+ Buffer += channel_size;
+ }
+ Width--;
+ }
+}
+
+static SANE_Int
+Read_ResizeBlock (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size, SANE_Int * transferred)
+{
+ /*The Beach
+ buffer = FA7C 05E30048
+ buffer_size = FA80 0000F906
+ */
+
+ SANE_Int rst = ERROR; /* fa68 */
+ SANE_Int lfa54;
+ SANE_Int lfa58;
+ SANE_Byte *pP1; /* fa5c */
+ SANE_Byte *pP2; /* fa60 */
+ SANE_Int bOk;
+ struct st_resize *rz = dev->Resize;
+
+ /* fa74 = Resize->resolution_y */
+ /* fa70 = Resize->resolution_x */
+ /* fa64 = scan2.resolution_y */
+ /* fa6c = scan2.resolution_x */
+
+ DBG (DBG_FNC,
+ "+ Read_ResizeBlock(*buffer, buffer_size=%i, *transferred):\n",
+ buffer_size);
+
+ if (rz->type == RSZ_DECREASE)
+ {
+ lfa58 = 0;
+ do
+ {
+ bOk = 1;
+ if (arrangeline2 == FIX_BY_SOFT)
+ {
+ if (scan2.colormode == CM_COLOR)
+ rst = Arrange_Colour (dev, rz->v3624, line_size, transferred);
+ else
+ rst =
+ Arrange_NonColour (dev, rz->v3624, line_size, transferred);
+ }
+ else
+ rst = Read_Block (dev, line_size, rz->v3624, transferred);
+
+ /*f2df */
+ Resize_Decrease (rz->v362c, rz->resolution_x, rz->towidth,
+ rz->v3624, scan2.resolution_x, rz->fromwidth,
+ rz->mode);
+ rz->rescount += rz->resolution_y;
+
+ if (rz->rescount > scan2.resolution_y)
+ {
+ /*f331 */
+ rz->rescount -= scan2.resolution_y;
+ if (scan2.depth == 8)
+ {
+ /* f345 */
+ pP1 = rz->v3628;
+ pP2 = rz->v362c;
+ if (rz->mode == RSZ_LINEART)
+ {
+ /* f36b */
+ SANE_Int bit = 0;
+ SANE_Byte *pP3 = rz->v362c;
+ SANE_Int value;
+
+ *buffer = 0;
+ lfa54 = 0;
+ while (lfa54 < rz->towidth)
+ {
+ if (bit == 8)
+ {
+ buffer++;
+ *buffer = 0;
+ pP1++;
+ bit = 0;
+ pP3++;
+ }
+
+ value =
+ ((*pP1 & (0x80 >> bit)) != 0) ? rz->rescount : 0;
+
+ if ((*pP3 & (0x80 >> bit)) != 0)
+ value += (scan2.resolution_y - rz->rescount);
+
+ if (value > rz->resolution_y)
+ *buffer |= (0x80 >> bit);
+
+ bit++;
+ lfa54++;
+ }
+ }
+ else
+ {
+ /* f414 */
+ lfa54 = 0;
+ while (lfa54 < rz->bytesperline)
+ {
+ *buffer =
+ _B0 ((((scan2.resolution_y -
+ rz->rescount) * *pP2) +
+ (*pP1 * rz->rescount)) /
+ scan2.resolution_y);
+ pP1++;
+ pP2++;
+ buffer++;
+ lfa54++;
+ }
+ }
+ }
+ else
+ {
+ /* f47d */
+ lfa54 = 0;
+ pP1 = rz->v3628;
+ pP2 = rz->v362c;
+
+ if ((rz->bytesperline & 0xfffffffe) > 0)
+ {
+ SANE_Int value;
+ do
+ {
+ value =
+ (((scan2.resolution_y -
+ rz->rescount) * data_lsb_get (pP2,
+ 2)) +
+ (data_lsb_get (pP1, 2) * rz->rescount)) /
+ scan2.resolution_y;
+ data_lsb_set (buffer, value, 2);
+
+ buffer += 2;
+ pP1 += 2;
+ pP2 += 2;
+ lfa54++;
+ }
+ while (lfa54 < (rz->bytesperline / 2));
+ }
+ }
+ }
+ else
+ bOk = 0;
+ /* f4fd f502 */
+ pP1 = rz->v3628;
+ /* swap pointers */
+ rz->v3628 = rz->v362c;
+ rz->v362c = pP1;
+ }
+ while (bOk == 0);
+ }
+ else
+ {
+ /*f530 */
+ SANE_Int lfa68;
+ SANE_Int transferred;
+ SANE_Int channel_size;
+
+ rz->rescount += scan2.resolution_y;
+ lfa58 = 0;
+ if (rz->rescount > rz->resolution_y)
+ {
+ lfa68 = 1;
+ rz->rescount -= rz->resolution_y;
+ }
+ else
+ lfa68 = 0;
+
+ pP1 = rz->v3628;
+ pP2 = rz->v362c;
+
+ if (rz->mode == RSZ_LINEART)
+ {
+ /*f592 */
+ *buffer = 0;
+
+ if (rz->towidth > 0)
+ {
+ SANE_Int mask, mres;
+ /* lfa60 = rz->resolution_y */
+ /* lfa7c = rz->resolution_y / 2 */
+
+ for (lfa54 = 0; lfa54 < rz->towidth; lfa54++)
+ {
+ mask = 0x80 >> lfa58;
+
+ mres = ((mask & *pP1) != 0) ? rz->rescount : 0;
+
+ if ((mask & *pP2) != 0)
+ mres += (rz->resolution_y - rz->rescount);
+
+ if (mres > (rz->resolution_y / 2))
+ *buffer = *buffer | mask;
+
+ lfa58++;
+ if (lfa58 == 8)
+ {
+ lfa58 = 0;
+ buffer++;
+ pP1++;
+ pP2++;
+ *buffer = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*f633 */
+ channel_size = (scan2.depth > 8) ? 2 : 1;
+
+ if (rz->rescount < scan2.resolution_y)
+ {
+ if (rz->bytesperline != 0)
+ {
+ SANE_Int value;
+
+ for (lfa54 = 0; lfa54 < rz->bytesperline; lfa54++)
+ {
+ value =
+ (((scan2.resolution_y -
+ rz->rescount) * data_lsb_get (pP2,
+ channel_size)) +
+ (rz->rescount * data_lsb_get (pP1, channel_size))) /
+ scan2.resolution_y;
+ data_lsb_set (buffer, value, channel_size);
+
+ pP1 += channel_size;
+ pP2 += channel_size;
+ buffer += channel_size;
+ }
+ }
+ }
+ else
+ memcpy (buffer, rz->v3628, rz->bytesperline); /*f6a8 */
+ }
+
+ /*f736 */
+ if (lfa68 != 0)
+ {
+ SANE_Byte *temp;
+
+ if (arrangeline2 == FIX_BY_SOFT)
+ {
+ /*f74b */
+ if (scan2.colormode == CM_COLOR)
+ rst =
+ Arrange_Colour (dev, rz->v3624, line_size, &transferred);
+ else
+ rst =
+ Arrange_NonColour (dev, rz->v3624, line_size, &transferred);
+ }
+ else
+ rst = Read_Block (dev, line_size, rz->v3624, &transferred); /*f77a */
+
+ /*f78c */
+ /* swap buffers */
+ temp = rz->v3628;
+ rz->v3628 = rz->v362c;
+ rz->v362c = temp;
+
+ Resize_Increase (temp, rz->resolution_x, rz->towidth, rz->v3624,
+ scan2.resolution_x, rz->fromwidth, rz->mode);
+ }
+ else
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- Read_ResizeBlock(*transferred=%i): %i\n", *transferred,
+ rst);
+
+ return rst;
+}
+
+static void
+Split_into_12bit_channels (SANE_Byte * destino, SANE_Byte * fuente,
+ SANE_Int size)
+{
+ /*
+ Each letter represents a bit
+ abcdefgh 12345678 lmnopqrs << before splitting
+ [efgh1234 0000abcd] [lmnopqrs 00005678] << after splitting, in memory
+ [0000abcd efgh1234] [00005678 lmnopqrs] << resulting channels
+ */
+
+ DBG (DBG_FNC, "> Split_into_12bit_channels(*destino, *fuente, size=%i\n",
+ size);
+
+ if ((destino != NULL) && (fuente != NULL))
+ {
+ if ((size - (size & 0x03)) != 0)
+ {
+ SANE_Int C;
+
+ C = (size - (size & 0x03) + 3) / 4;
+ do
+ {
+ *destino = _B0 ((*(fuente + 1) >> 4) + (*fuente << 4));
+ *(destino + 1) = _B0 (*fuente >> 4);
+ *(destino + 2) = _B0 (*(fuente + 2));
+ *(destino + 3) = *(fuente + 1) & 0x0f;
+ destino += 4;
+ fuente += 3;
+ C--;
+ }
+ while (C > 0);
+ }
+
+ /**/ if ((size & 0x03) != 0)
+ {
+ *destino = _B0 ((*(fuente + 1) >> 4) + (*fuente << 4));
+ *(destino + 1) = _B0 (*fuente >> 4);
+ }
+ }
+}
+
+static SANE_Int
+Read_NonColor_Block (struct st_device *dev, SANE_Byte * buffer,
+ SANE_Int buffer_size, SANE_Byte ColorMode,
+ SANE_Int * transferred)
+{
+ /* FA50 05DA0048 buffer
+ FA54 0000F906 buffer_size
+ FA58 00 ColorMode
+ */
+
+ SANE_Int rst = OK;
+ SANE_Int lfa38 = 0;
+ SANE_Byte *gamma = v1600;
+ SANE_Int block_bytes_per_line;
+ SANE_Int mysize;
+ SANE_Byte *mybuffer;
+
+ DBG (DBG_FNC,
+ "+ Read_NonColor_Block(*buffer, buffer_size=%i, ColorMode=%s):\n",
+ buffer_size, dbg_colour (ColorMode));
+
+ if (ColorMode != CM_GRAY)
+ {
+ /* Lineart mode */
+ if ((lineart_width & 7) != 0)
+ lfa38 = 8 - (lineart_width & 7);
+ block_bytes_per_line = (lineart_width + 7) / 8;
+ }
+ else
+ block_bytes_per_line = line_size;
+ /*61b2 */
+
+ mysize = (buffer_size / block_bytes_per_line) * bytesperline;
+ mybuffer = (SANE_Byte *) malloc (mysize * sizeof (SANE_Byte)); /*fa40 */
+
+ if (mybuffer != NULL)
+ {
+ SANE_Int LinesCount;
+ SANE_Int mysize4lines;
+ SANE_Byte *pBuffer = buffer;
+ SANE_Byte *pImage = NULL; /* fa30 */
+ SANE_Int puntero;
+ SANE_Int value;
+
+ do
+ {
+ mysize4lines =
+ (mysize <=
+ dev->Reading->Size4Lines) ? mysize : dev->Reading->Size4Lines;
+ LinesCount = mysize4lines / bytesperline;
+
+ if (ColorMode == CM_GRAY)
+ {
+ if (scan2.depth == 12)
+ {
+ /* 633b */
+ /*GRAY Bit mode 12 */
+ rst =
+ Scan_Read_BufferA (dev, (mysize4lines * 3) / 4, 0,
+ mybuffer, transferred);
+ if (rst == OK)
+ {
+ pImage = mybuffer;
+ pBuffer += LinesCount * block_bytes_per_line;
+ while (LinesCount > 0)
+ {
+ Split_into_12bit_channels (mybuffer, pImage,
+ line_size);
+ pImage += (bytesperline * 3) / 4;
+ LinesCount--;
+ }
+ }
+ else
+ break;
+ }
+ else
+ {
+ /* grayscale 8 and 16 bits */
+
+ SANE_Int channel_size;
+
+ rst =
+ Scan_Read_BufferA (dev, mysize4lines, 0, mybuffer,
+ transferred);
+
+ if (rst == OK)
+ {
+ channel_size = (scan2.depth > 8) ? 2 : 1;
+
+ pImage = mybuffer;
+
+ /* No gamma tables */
+ while (LinesCount > 0)
+ {
+ if (line_size > 0)
+ {
+ puntero = 0;
+ do
+ {
+ value =
+ data_lsb_get (pImage + puntero,
+ channel_size);
+
+ if (gamma != NULL)
+ value +=
+ *gamma << (8 * (channel_size - 1));
+
+ data_lsb_set (pBuffer, value, channel_size);
+
+ pBuffer += channel_size;
+ puntero += channel_size;
+ }
+ while (puntero < line_size);
+ }
+ pImage += bytesperline;
+ LinesCount--;
+ }
+ }
+ else
+ break;
+ }
+ }
+ else
+ {
+ /*6429 */
+ /* LINEART */
+ SANE_Int desp;
+ rst =
+ Scan_Read_BufferA (dev, mysize4lines, 0, mybuffer,
+ transferred);
+ if (rst == OK)
+ {
+ pImage = mybuffer;
+ while (LinesCount > 0)
+ {
+ if (lineart_width > 0)
+ {
+ desp = 0;
+ do
+ {
+ if ((desp % 7) == 0)
+ *pBuffer = 0;
+
+ /* making a byte bit per bit */
+ *pBuffer = *pBuffer << 1;
+
+ /* bit 1 if data is under thresholdh value */
+ if (*(pImage + desp) >= binarythresholdh) /* binarythresholdh = 0x0c */
+ *pBuffer = *pBuffer | 1;
+
+ desp++;
+ if ((desp % 7) == 0)
+ pBuffer++;
+
+ }
+ while (desp < lineart_width);
+ }
+
+ if (lfa38 != 0)
+ {
+ *pBuffer = (*pBuffer << lfa38);
+ pBuffer++;
+ }
+ /* 64b0 */
+ pImage += bytesperline;
+ LinesCount--;
+ }
+ }
+ else
+ break;
+ }
+ /* 64c0 */
+ mysize -= mysize4lines;
+ }
+ while ((mysize > 0) && (dev->status->cancel == FALSE));
+
+ free (mybuffer);
+ }
+ else
+ rst = ERROR;
+
+ DBG (DBG_FNC, "- Read_NonColor_Block(*transferred=%i): %i\n", *transferred,
+ rst);
+
+ return rst;
+}
+
+static SANE_Int
+Read_Block (struct st_device *dev, SANE_Int buffer_size, SANE_Byte * buffer,
+ SANE_Int * transferred)
+{
+ /*
+ SANE_Int buffer_size fa80
+ SANE_Byte *buffer fa7c
+ */
+/*
+scan2:
+04F0155C 01 08 00 02 03 00 58 02 ..X
+04F01564 58 02 58 02 C5 00 00 00 XXÅ...
+04F0156C B4 07 00 00 8B 01 00 00 ´..‹..
+04F01574 10 06 00 00 EC 13 00 00 ..ì..
+04F0157C B2 07 00 00 B4 07 00 00 ²..´..
+04F01584 CF 08 00 00 Ï..
+
+arrangeline2 = 1
+*/
+ SANE_Int rst, LinesCount;
+ SANE_Int mysize;
+ SANE_Byte *readbuffer = NULL;
+ SANE_Byte *pImage = NULL;
+
+ DBG (DBG_FNC, "+ Read_Block(buffer_size=%i, *buffer):\n", buffer_size);
+
+ rst = ERROR;
+ *transferred = 0;
+
+ if ((scan2.colormode != CM_COLOR) && (scan2.channel == 3)
+ && (arrangeline2 != FIX_BY_SOFT))
+ {
+ /*6510 */
+ return Read_NonColor_Block (dev, buffer, buffer_size, scan2.colormode,
+ transferred);
+ }
+
+ /*6544 */
+ mysize = (buffer_size / line_size) * bytesperline;
+ readbuffer = (SANE_Byte *) malloc (mysize * sizeof (SANE_Byte));
+ pImage = buffer;
+
+ if (readbuffer != NULL)
+ {
+ do
+ {
+ buffer_size =
+ (dev->Reading->Size4Lines <
+ mysize) ? dev->Reading->Size4Lines : mysize;
+ LinesCount = buffer_size / bytesperline;
+
+ if (scan2.depth == 12)
+ {
+ rst =
+ Scan_Read_BufferA (dev, buffer_size, 0, readbuffer,
+ transferred);
+ if (rst == OK)
+ {
+ if (LinesCount > 0)
+ {
+ SANE_Byte *destino, *fuente;
+ destino = buffer;
+ fuente = readbuffer;
+ do
+ {
+ Split_into_12bit_channels (destino, fuente,
+ line_size);
+ destino += line_size;
+ fuente += (bytesperline * 3) / 4;
+ LinesCount--;
+ }
+ while (LinesCount > 0);
+ }
+ }
+ else
+ break;
+ }
+ else
+ {
+ /*65d9 */
+ rst =
+ Scan_Read_BufferA (dev, buffer_size, 0, readbuffer,
+ transferred);
+ if (rst == OK)
+ {
+ memcpy (pImage, readbuffer, *transferred);
+
+ /* apply white shading correction */
+ if ((RTS_Debug->wshading == TRUE)
+ && (scan2.scantype == ST_NORMAL))
+ WShading_Emulate (pImage, &wshading->ptr, *transferred,
+ scan2.depth);
+
+ pImage += *transferred;
+ }
+ else
+ break;
+ }
+ /*6629 */
+ mysize -= buffer_size;
+ }
+ while ((mysize > 0) && (dev->status->cancel == FALSE));
+
+ free (readbuffer);
+ }
+
+ DBG (DBG_FNC, "- Read_Block(*transferred=%i): %i\n", *transferred, rst);
+
+ return rst;
+}
+
+static SANE_Int
+Scan_Read_BufferA (struct st_device *dev, SANE_Int buffer_size, SANE_Int arg2,
+ SANE_Byte * pBuffer, SANE_Int * bytes_transfered)
+{
+ SANE_Int rst = OK;
+ SANE_Byte *ptBuffer = NULL;
+ SANE_Byte *ptImg = NULL;
+ struct st_readimage *rd = dev->Reading;
+
+ DBG (DBG_FNC,
+ "+ Scan_Read_BufferA(buffer_size=%i, arg2, *pBuffer, *bytes_transfered):\n",
+ buffer_size);
+
+ arg2 = arg2; /* silence gcc */
+ *bytes_transfered = 0;
+
+ if (pBuffer != NULL)
+ {
+ ptBuffer = pBuffer;
+
+ while ((buffer_size > 0) && (rst == OK)
+ && (dev->status->cancel == FALSE))
+ {
+ /* Check if we've already started */
+ if (rd->Starting == TRUE)
+ {
+ /* Get channels per dot and channel's size in bytes */
+ SANE_Byte data;
+
+ rd->Channels_per_dot = 1;
+ if (Read_Byte (dev->usb_handle, 0xe812, &data) == OK)
+ {
+ data = data >> 6;
+ if (data != 0)
+ rd->Channels_per_dot = data;
+ }
+
+ rd->Channel_size = 1;
+ if (Read_Byte (dev->usb_handle, 0xee0b, &data) == OK)
+ if (((data & 0x40) != 0) && ((data & 0x08) == 0))
+ rd->Channel_size = 2;
+
+ rd->RDStart = rd->DMABuffer;
+ rd->RDSize = 0;
+ rd->DMAAmount = 0;
+ rd->Starting = FALSE;
+ }
+
+ /* Is there any data to read from scanner? */
+ if ((rd->ImageSize > 0) && (rd->RDSize == 0))
+ {
+ /* Try to read from scanner all possible data to fill DMABuffer */
+ if (rd->RDSize < rd->DMABufferSize)
+ {
+ SANE_Int iAmount, dofree;
+
+ /* Check if we have already notify buffer size */
+ if (rd->DMAAmount <= 0)
+ {
+ /* Initially I suppose that I can read all image */
+ iAmount = min (rd->ImageSize, rd->Max_Size);
+ rd->DMAAmount =
+ ((RTS_Debug->dmasetlength * 2) / iAmount) * iAmount;
+ rd->DMAAmount = min (rd->DMAAmount, rd->ImageSize);
+ Reading_BufferSize_Notify (dev, 0, rd->DMAAmount);
+ iAmount = min (iAmount, rd->DMABufferSize - rd->RDSize);
+ }
+ else
+ {
+ iAmount = min (rd->DMAAmount, rd->ImageSize);
+ iAmount = min (iAmount, rd->Max_Size);
+ }
+
+ /* Allocate buffer to read image if it's necessary */
+ if ((rd->RDSize == 0) && (iAmount <= buffer_size))
+ {
+ ptImg = ptBuffer;
+ dofree = FALSE;
+ }
+ else
+ {
+ ptImg =
+ (SANE_Byte *) malloc (iAmount * sizeof (SANE_Byte));
+ dofree = TRUE;
+ }
+
+ if (ptImg != NULL)
+ {
+ /* We must wait for scanner to get data */
+ SANE_Int opStatus, sc;
+
+ sc = (iAmount < rd->Max_Size) ? TRUE : FALSE;
+ opStatus = Reading_Wait (dev, rd->Channels_per_dot,
+ rd->Channel_size,
+ iAmount,
+ &rd->Bytes_Available, 10, sc);
+
+ /* If something fails, perhaps we can read some bytes... */
+ if (opStatus != OK)
+ {
+ if (rd->Bytes_Available > 0)
+ iAmount = rd->Bytes_Available;
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ /* Try to read from scanner */
+ SANE_Int transferred = 0;
+ opStatus =
+ Bulk_Operation (dev, BLK_READ, iAmount, ptImg,
+ &transferred);
+
+ DBG (DBG_FNC,
+ "> Scan_Read_BufferA: Bulk read %i bytes\n",
+ transferred);
+
+ /*if something fails may be we can read some bytes */
+ iAmount = (SANE_Int) transferred;
+ if (iAmount != 0)
+ {
+ /* Lets copy data into DMABuffer if it's necessary */
+ if (ptImg != ptBuffer)
+ {
+ SANE_Byte *ptDMABuffer;
+
+ ptDMABuffer = rd->RDStart + rd->RDSize;
+ if ((ptDMABuffer - rd->DMABuffer) >=
+ rd->DMABufferSize)
+ ptDMABuffer -= rd->DMABufferSize;
+
+ if ((ptDMABuffer + iAmount) >=
+ (rd->DMABuffer + rd->DMABufferSize))
+ {
+ SANE_Int rest =
+ iAmount - (rd->DMABufferSize -
+ (ptDMABuffer -
+ rd->DMABuffer));
+ memcpy (ptDMABuffer, ptImg,
+ iAmount - rest);
+ memcpy (rd->DMABuffer,
+ ptImg + (iAmount - rest), rest);
+ }
+ else
+ memcpy (ptDMABuffer, ptImg, iAmount);
+ rd->RDSize += iAmount;
+ }
+ else
+ {
+ *bytes_transfered += iAmount;
+ buffer_size -= iAmount;
+ }
+
+ rd->DMAAmount -= iAmount;
+ rd->ImageSize -= iAmount;
+ }
+ else
+ rst = ERROR;
+ }
+
+ /* Lets free buffer */
+ if (dofree == TRUE)
+ {
+ free (ptImg);
+ ptImg = NULL;
+ }
+ }
+ else
+ rst = ERROR;
+ }
+ }
+
+ /* is there any data read from scanner? */
+ if (rd->RDSize > 0)
+ {
+ /* Add to the given buffer so many bytes as posible */
+ SANE_Int iAmount;
+
+ iAmount = min (buffer_size, rd->RDSize);
+ if ((rd->RDStart + iAmount) >=
+ (rd->DMABuffer + rd->DMABufferSize))
+ {
+ SANE_Int rest =
+ rd->DMABufferSize - (rd->RDStart - rd->DMABuffer);
+ memcpy (ptBuffer, rd->RDStart, rest);
+ memcpy (ptBuffer + rest, rd->DMABuffer, iAmount - rest);
+ rd->RDStart = rd->DMABuffer + (iAmount - rest);
+ }
+ else
+ {
+ memcpy (ptBuffer, rd->RDStart, iAmount);
+ rd->RDStart += iAmount;
+ }
+
+ ptBuffer += iAmount;
+ rd->RDSize -= iAmount;
+ buffer_size -= iAmount;
+ *bytes_transfered += iAmount;
+
+ /* if there isn't any data in DMABuffer we can point RDStart
+ to the begining of DMABuffer */
+ if (rd->RDSize == 0)
+ rd->RDStart = rd->DMABuffer;
+ }
+
+ /* in case of all data is read we return OK with bytes_transfered = 0 */
+ if ((*bytes_transfered == 0)
+ || ((rd->RDSize == 0) && (rd->ImageSize == 0)))
+ break;
+ }
+
+ if (rst == ERROR)
+ RTS_DMA_Cancel (dev);
+ }
+
+ DBG (DBG_FNC, "-> *bytes_transfered=%i\n", *bytes_transfered);
+ DBG (DBG_FNC, "-> Reading->ImageSize=%i\n", rd->ImageSize);
+ DBG (DBG_FNC, "-> Reading->DMAAmount=%i\n", rd->DMAAmount);
+ DBG (DBG_FNC, "-> Reading->RDSize =%i\n", rd->RDSize);
+
+ DBG (DBG_FNC, "- Scan_Read_BufferA: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Reading_BufferSize_Get (struct st_device *dev, SANE_Byte channels_per_dot,
+ SANE_Int channel_size)
+{
+ /* returns the ammount of bytes in scanner's buffer ready to be read */
+
+ SANE_Int rst;
+
+ DBG (DBG_FNC,
+ "+ Reading_BufferSize_Get(channels_per_dot=%i, channel_size=%i):\n",
+ channels_per_dot, channel_size);
+
+ rst = 0;
+
+ if (channel_size > 0)
+ {
+ SANE_Int myAmount;
+
+ if (channels_per_dot < 1)
+ {
+ /* read channels per dot from registers */
+ if (Read_Byte (dev->usb_handle, 0xe812, &channels_per_dot) == OK)
+ channels_per_dot = _B0 (channels_per_dot >> 6);
+
+ if (channels_per_dot == 0)
+ channels_per_dot++;
+ }
+
+ if (Read_Integer (dev->usb_handle, 0xef16, &myAmount) == OK)
+ rst = ((channels_per_dot * 32) / channel_size) * myAmount;
+ }
+
+ DBG (DBG_FNC, "- Reading_BufferSize_Get: %i bytes\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_Warmup (struct st_device *dev, SANE_Byte * Regs, SANE_Int lamp,
+ SANE_Int resolution)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Lamp_Warmup(*Regs, lamp=%i, resolution=%i)\n", lamp,
+ resolution);
+
+ if (Regs != NULL)
+ {
+ SANE_Byte flb_lamp, tma_lamp;
+ SANE_Int overdrivetime;
+
+ Lamp_Status_Get (dev, &flb_lamp, &tma_lamp);
+
+ /* ensure that selected lamp is switched on */
+ if (lamp == FLB_LAMP)
+ {
+ overdrivetime = RTS_Debug->overdrive_flb;
+
+ if (flb_lamp == 0)
+ {
+ /* FLB-Lamp is turned off, lets turn on */
+ Lamp_Status_Set (dev, Regs, TRUE, FLB_LAMP);
+ waitforpwm = TRUE;
+ }
+ }
+ else
+ {
+ /* is tma device attached to scanner ? */
+ if (RTS_isTmaAttached (dev) == TRUE)
+ {
+ overdrivetime = RTS_Debug->overdrive_ta;
+
+ if (tma_lamp == 0)
+ {
+ /* tma lamp is turned off */
+ Lamp_Status_Set (dev, Regs, FALSE, TMA_LAMP);
+ waitforpwm = TRUE;
+ }
+ }
+ else
+ rst = ERROR;
+ }
+
+ /* perform warmup process */
+ if (rst == OK)
+ {
+ Lamp_PWM_Setup (dev, lamp);
+
+ if (waitforpwm == TRUE)
+ {
+ /*Lamp_PWM_DutyCycle_Set(dev, (lamp == TMA_LAMP)? 0x0e : 0x00); */
+
+ if (RTS_Debug->warmup == TRUE)
+ {
+ long ticks = GetTickCount () + overdrivetime;
+
+ DBG (DBG_VRB, "- Lamp Warmup process. Please wait...\n");
+
+ dev->status->warmup = TRUE;
+
+ while (GetTickCount () <= ticks)
+ usleep (1000 * 200);
+
+ Lamp_PWM_CheckStable (dev, resolution, lamp);
+
+ }
+ else
+ DBG (DBG_VRB, "- Lamp Warmup process disabled.\n");
+ }
+
+ /*Lamp_PWM_Setup(dev, lamp);
+
+ if (waitforpwm == TRUE)
+ {
+ if (RTS_Debug->warmup == TRUE)
+ Lamp_PWM_CheckStable(dev, resolution, lamp);
+
+ waitforpwm = FALSE;
+ } */
+ }
+
+ }
+ else
+ rst = ERROR;
+
+ dev->status->warmup = FALSE;
+
+ DBG (DBG_FNC, "- Lamp_Warmup: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Scan_Start (struct st_device *dev)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Scan_Start:\n");
+
+ rst = ERROR;
+ if (RTS_Enable_CCD (dev, dev->init_regs, 0x0f) == OK)
+ {
+ SANE_Byte Regs[RT_BUFFER_LEN], mlock;
+ SANE_Int ypos, xpos, runb1;
+ struct st_scanparams scancfg;
+ struct st_hwdconfig hwdcfg;
+ struct st_calibration myCalib;
+ long tick;
+
+ memcpy (&Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (&scancfg, &scan, sizeof (struct st_scanparams));
+
+ dbg_ScanParams (&scancfg);
+
+ /* reserva buffer 6 dwords en fa84-fa9f */
+ bzero (&hwdcfg, sizeof (struct st_hwdconfig));
+
+ /* wait till lamp is at home (should use timeout
+ windows driver doesn't use it)
+ */
+ tick = GetTickCount () + 10000;
+ while ((Head_IsAtHome (dev, Regs) == FALSE)
+ && (tick > GetTickCount ()));
+
+ if (v14b4 != 0)
+ {
+ SANE_Int lfaa0 = 0;
+
+ if (GainOffset_Counter_Inc (dev, &lfaa0) != OK)
+ return 0x02;
+ }
+
+ tick = GetTickCount ();
+
+ /* set margin references */
+ Refs_Set (dev, Regs, &scancfg);
+
+ /* locate head to right position */
+ Load_StripCoords (scantype, &ypos, &xpos);
+ if (ypos != 0)
+ Head_Relocate (dev, dev->motorcfg->parkhomemotormove, MTR_FORWARD,
+ ypos);
+
+ /* perform lamp warmup */
+ if (Lamp_Warmup
+ (dev, Regs, (scancfg.scantype == ST_NORMAL) ? FLB_LAMP : TMA_LAMP,
+ scan.resolution_x) == ERROR)
+ return ERROR;
+
+ /* Calibration process */
+
+ /*592c */
+ if (Calib_CreateBuffers (dev, &myCalib, v14b4) != OK)
+ return ERROR;
+
+ /*5947 */
+
+/*
+if (Calib_BlackShading_jkd(dev, Regs, &myCalib, &scancfg) == OK)
+ Head_ParkHome(dev, TRUE, dev->motorcfg->parkhomemotormove);
+*/
+
+/*
+if (Calib_test(dev, Regs, &myCalib, &scancfg) == OK )
+ Head_ParkHome(dev, TRUE, dev->motorcfg->parkhomemotormove);
+*/
+
+/* Calibrate White shading correction */
+ if ((RTS_Debug->wshading == TRUE) && (scan.scantype == ST_NORMAL))
+ if (WShading_Calibrate (dev, Regs, &myCalib, &scancfg) == OK)
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+
+ hwdcfg.calibrate = RTS_Debug->calibrate;
+
+ if (RTS_Debug->calibrate != 0)
+ {
+ /* Let's calibrate */
+ if ((scancfg.colormode != CM_COLOR) && (scancfg.channel == 3))
+ scancfg.colormode = CM_COLOR;
+
+ hwdcfg.arrangeline = 0;
+
+ if (scan.scantype == ST_NORMAL)
+ {
+ /* Calibration for reflective type */
+
+ /*59e3 */
+ memcpy (&Regs, dev->init_regs,
+ RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ if (Calibration (dev, Regs, &scancfg, &myCalib, 0) != OK)
+ {
+ if (v14b4 == 0)
+ Calib_FreeBuffers (&myCalib);
+ return ERROR;
+ }
+ }
+ else
+ {
+ /*59ed */
+ /* Calibration for negative/slide type */
+
+ }
+
+ /*5af1 */
+ if (RTS_Debug->ScanWhiteBoard != FALSE)
+ {
+ Head_ParkHome (dev, TRUE, dev->motorcfg->basespeedmotormove);
+ scan.ler = 1;
+ }
+
+ scancfg.colormode = scan.colormode;
+ }
+ else
+ {
+ /*5b1e */
+ /*Don't calibrate */
+ if (scan.scantype == ST_NORMAL)
+ {
+ Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP);
+ }
+ else
+ {
+ if ((scan.scantype == ST_TA) || (scan.scantype == ST_NEG))
+ {
+ /*SANE_Int ta_y_start; */
+ Lamp_Status_Set (dev, NULL, FALSE, TMA_LAMP);
+ /*ta_y_start =
+ get_value(SCAN_PARAM, TA_Y_START, 0x2508, usbfile);
+ ta_y_start += (((((scan.coord.top * 3) * 5) * 5) * 32) / scancfg.resolution_x);
+ if (ta_y_start >= 500)
+ {
+ Head_Relocate(dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, ta_y_start);
+ scancfg.coord.top = 1;
+ scan.ler = 1;
+ } else
+ {
+ / *5ba9* /
+ if (ta_y_start > 0)
+ {
+ Head_Relocate(dev, dev->motorcfg->basespeedmotormove, MTR_FORWARD, ta_y_start);
+ scancfg.coord.top = 1;
+ scan.ler = 1;
+ }
+ } */
+ }
+ }
+ }
+
+ /*5bd0 */
+ usleep (1000 * 200);
+
+ hwdcfg.scantype = scan.scantype;
+ hwdcfg.motor_direction = MTR_FORWARD;
+
+ /* Set Origin */
+ if ((scan.scantype >= ST_NORMAL) || (scan.scantype <= ST_NEG))
+ {
+ scancfg.coord.left += scan.ser;
+ scancfg.coord.top += scan.ler;
+ }
+
+ hwdcfg.sensorevenodddistance = dev->sensorcfg->evenodd_distance;
+ hwdcfg.highresolution = (scancfg.resolution_x <= 1200) ? FALSE : TRUE;
+
+ /*5c55 */
+ /*
+ if (RTS_Debug->calibrate == FALSE)
+ {
+ SANE_Int mytop = (((scancfg.coord.top * 5) * 5) * 16) / scancfg.resolution_y;
+ if ((scancfg.resolution_y <= 150)&&(mytop < 300))
+ {
+ scancfg.coord.top = scancfg.resolution_y / 4;
+ } else
+ {
+ if (mytop < 100)
+ scancfg.coord.top = scancfg.resolution_y / 12;
+ }
+ }
+ */
+
+ /*5cd9 */
+ if (compression != FALSE)
+ hwdcfg.compression = TRUE;
+
+ /* setting arrangeline option */
+ hwdcfg.arrangeline = arrangeline;
+ if (scancfg.resolution_x == 2400)
+ {
+ /* 5cfa */
+ if (scancfg.colormode != CM_COLOR)
+ {
+ if ((scancfg.colormode == CM_GRAY) && (scancfg.channel == 3))
+ hwdcfg.arrangeline = FIX_BY_SOFT;
+ }
+ else
+ hwdcfg.arrangeline = FIX_BY_SOFT;
+ }
+
+ /*5d12 */
+ if (dev->sensorcfg->type == CCD_SENSOR)
+ {
+ /*5d3a */
+ scancfg.coord.left += 24;
+ switch (scancfg.resolution_x)
+ {
+ case 1200:
+ scancfg.coord.left -= 63;
+ break;
+ case 2400:
+ scancfg.coord.left -= 127;
+ break;
+ }
+ }
+ else
+ {
+ /*5d5a */
+ /* CIS sensor */
+ /*5d6d */
+ scancfg.coord.left += 50;
+ switch (scancfg.resolution_x)
+ {
+ case 1200:
+ scancfg.coord.left -= 63;
+ break;
+ case 2400:
+ scancfg.coord.left -= 127;
+ break;
+ }
+ }
+
+ /* 5d92 */
+ DBG (DBG_FNC, " ->Scan_Start xStart=%i, xExtent=%i\n",
+ scancfg.coord.left, scancfg.coord.width);
+
+ runb1 = 1;
+ if (scan.scantype == ST_NORMAL)
+ {
+ /*5db7 */
+ if ((scancfg.resolution_x == 1200)
+ || (scancfg.resolution_x == 2400))
+ {
+ /*5e41 */
+ if ((scancfg.resolution_y / 10) > scancfg.coord.top)
+ runb1 = 0;
+ }
+ else
+ {
+ if ((scancfg.resolution_x == 600)
+ && (RTS_Debug->usbtype == USB11)
+ && (scancfg.colormode == CM_COLOR))
+ {
+ /*5ded */
+ if ((scancfg.resolution_y / 10) > scancfg.coord.top)
+ runb1 = 0;
+ }
+ else
+ {
+ if ((scancfg.resolution_x == 600)
+ || (scancfg.resolution_x == 300))
+ {
+ /*5e11 */
+ if (scancfg.resolution_y > scancfg.coord.top)
+ runb1 = 0;
+ }
+ else
+ runb1 = 0;
+ }
+ }
+ }
+ else
+ {
+ /*5e7c *//* entra aquí */
+ if ((scancfg.resolution_y / 10) > scancfg.coord.top)
+ runb1 = 0;
+ }
+ /*5eb1 */
+ if (runb1 == 1) /*entra */
+ {
+ SANE_Int val1 = scancfg.coord.top - (scancfg.resolution_y / 10);
+ scancfg.coord.top -= val1;
+ Head_Relocate (dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, (dev->motorcfg->resolution / scancfg.resolution_y) * val1); /*x168 */
+ }
+
+ /*5efe */
+ if (RTS_Debug->calibrate != FALSE)
+ {
+ if (use_gamma_tables != FALSE)
+ {
+ hwdcfg.use_gamma_tables = TRUE;
+ hp_gamma->depth = 0;
+ }
+
+ /*5f24 */
+ hwdcfg.white_shading = TRUE;
+ hwdcfg.black_shading = TRUE;
+ hwdcfg.unk3 = 0;
+ RTS_Setup (dev, Regs, &scancfg, &hwdcfg, &calibdata->gain_offset);
+
+ myCalib.shading_type = 0;
+ myCalib.shadinglength =
+ min (myCalib.shadinglength, scan.shadinglength);
+
+ if (scancfg.colormode != CM_COLOR)
+ {
+ if ((scancfg.channel > 0) && (scancfg.channel < 3))
+ myCalib.WRef[0] = myCalib.WRef[scancfg.channel];
+ }
+
+ RTS_WriteRegs (dev->usb_handle, Regs);
+
+ /* apply gamma if required */
+ Gamma_Apply (dev, Regs, &scancfg, &hwdcfg, hp_gamma);
+
+ Shading_apply (dev, Regs, &scancfg, &myCalib);
+
+ /* Save to file? */
+ if (RTS_Debug->DumpShadingData != FALSE)
+ dump_shading (&myCalib); /*5ff9 */
+ }
+ else
+ RTS_Setup (dev, Regs, &scancfg, &hwdcfg, default_gain_offset);
+
+ /*602a */
+ RTS_Debug->calibrate = hwdcfg.calibrate;
+ binarythresholdh = bw_threshold;
+ binarythresholdl = bw_threshold;
+ DBG (DBG_FNC, "> bw threshold -- hi=%i, lo=%i\n", binarythresholdh,
+ binarythresholdl);
+
+ /* set threshold high */
+ data_lsb_set (&Regs[0x1a0], binarythresholdh, 2);
+
+ /* set threshold low */
+ data_lsb_set (&Regs[0x19e], binarythresholdl, 2);
+
+ /* if has motorcurves... */
+ if ((Regs[0xdf] & 0x10) != 0)
+ data_bitset (&Regs[0x01], 0x02, 1);
+
+ /* Set MLOCK */
+ mlock = get_value (SCAN_PARAM, MLOCK, 0, usbfile) & 1;
+ data_bitset (&Regs[0x00], 0x10, mlock); /*---x----*/
+
+ if (dev->motorcfg->changemotorcurrent != FALSE)
+ Motor_Change (dev, Regs,
+ Motor_GetFromResolution (scancfg.resolution_x));
+
+ /* set gain control mode */
+ Lamp_SetGainMode (dev, Regs, scancfg.resolution_x,
+ Lamp_GetGainMode (dev, scancfg.resolution_x,
+ scan.scantype));
+
+ RTS_WaitScanEnd (dev, 15000);
+ if (v14b4 == 0)
+ Calib_FreeBuffers (&myCalib);
+
+ /* release motor */
+ Motor_Release (dev);
+
+
+#ifdef developing
+/* prueba(Regs);
+ dbg_registers(Regs);*/
+ /*WShading_Calibrate(dev, Regs, &myCalib, &scancfg); */
+ /*shadingtest1(dev, Regs, &myCalib); */
+#endif
+
+ if (RTS_Warm_Reset (dev) == OK)
+ {
+ RTS_WriteRegs (dev->usb_handle, Regs);
+ usleep (1000 * 500);
+
+ if (RTS_Execute (dev) == OK)
+ {
+ Lamp_Status_Timer_Set (dev, 0);
+
+ /* Let scanner some time to store some data */
+ if ((dev->chipset->model == RTS8822L_02A)
+ && (scancfg.resolution_x > 2400))
+ usleep (1000 * 5000);
+
+ rst = OK;
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- Scan_Start: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_Setup_Motor (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg, SANE_Int somevalue)
+{
+ SANE_Int rst = ERROR; /* default */
+
+ DBG (DBG_FNC, "+ RTS_Setup_Motor(*Regs, *scancfg, somevalue=%i):\n",
+ somevalue);
+ dbg_ScanParams (scancfg);
+
+ if ((Regs != NULL) && (scancfg != NULL))
+ {
+ SANE_Int colormode, mymode;
+
+ colormode = ((scancfg->colormode != CM_COLOR)
+ && (scancfg->channel == 3)) ? 3 : scancfg->colormode;
+ mymode =
+ RTS_GetScanmode (dev, scantype, colormode, scancfg->resolution_x);
+
+ if (mymode != -1)
+ {
+ SANE_Int mbs[2] = { 0 }; /* motor back steps */
+ SANE_Int step_size, step_type, dummyline, myvalue, lf02c;
+ struct st_scanmode *sm;
+
+ sm = dev->scanmodes[mymode];
+
+ /* set motor step type */
+ data_bitset (&Regs[0xd9], 0x70, sm->scanmotorsteptype); /*-xxx----*/
+
+ /* set motor direction (polarity) */
+ data_bitset (&Regs[0xd9], 0x80, somevalue >> 3); /*e------- */
+
+ /* next value doesn't seem to have any effect */
+ data_bitset (&Regs[0xd9], 0x0f, somevalue); /*----efgh*/
+
+ /* 0 enable/1 disable motor */
+ data_bitset (&Regs[0xdd], 0x80, somevalue >> 4); /*d------- */
+
+ /* next value doesn't seem to have any effect */
+ data_bitset (&Regs[0xdd], 0x40, somevalue >> 4); /*-d------*/
+
+ switch (sm->scanmotorsteptype)
+ {
+ case STT_OCT:
+ step_type = 8;
+ break;
+ case STT_QUART:
+ step_type = 4;
+ break;
+ case STT_HALF:
+ step_type = 2;
+ break;
+ default:
+ step_type = 1;
+ break; /* STT_FULL */
+ }
+
+ /* set dummy lines */
+ dummyline = sm->dummyline;
+ if (dummyline == 0)
+ dummyline++;
+
+ data_bitset (&Regs[0xd6], 0xf0, dummyline); /*xxxx---- */
+
+ /* Set if motor has curves */
+ data_bitset (&Regs[0xdf], 0x10, ((sm->motorcurve != -1) ? 1 : 0)); /*---x----*/
+
+ /* set last step of deccurve.scanbufferfull table to 16 */
+ data_lsb_set (&Regs[0xea], 0x10, 3);
+
+ /* set last step of deccurve.normalscan table to 16 */
+ data_lsb_set (&Regs[0xed], 0x10, 3);
+
+ /* set last step of deccurve.smearing table to 16 */
+ data_lsb_set (&Regs[0xf0], 0x10, 3);
+
+ /* set last step of deccurve.parkhome table to 16 */
+ data_lsb_set (&Regs[0xf3], 0x10, 3);
+
+ /* set step size */
+ step_size =
+ _B0 ((dev->motorcfg->resolution * step_type) /
+ (dummyline * scancfg->resolution_y));
+ data_lsb_set (&Regs[0xe0], step_size - 1, 1);
+
+ /* set line exposure time */
+ myvalue = data_lsb_get (&Regs[0x30], 3);
+ myvalue += ((myvalue + 1) % step_size);
+ data_lsb_set (&Regs[0x30], myvalue, 3);
+
+ /* set last step of accurve.normalscan table */
+ myvalue = ((myvalue + 1) / step_size) - 1;
+ data_lsb_set (&Regs[0xe1], myvalue, 3);
+
+ /* 42b30eb */
+ lf02c = 0;
+ if (sm->motorcurve != -1)
+ {
+ if (sm->motorcurve < dev->mtrsetting_count)
+ {
+ struct st_motorcurve *ms = dev->mtrsetting[sm->motorcurve];
+ ms->motorbackstep = sm->motorbackstep;
+ }
+
+ DBG (DBG_FNC, " -> Setting up step motor using motorcurve %i\n",
+ sm->motorcurve);
+ lf02c = Motor_Setup_Steps (dev, Regs, sm->motorcurve);
+
+ /* set motor back steps */
+ mbs[1] = sm->motorbackstep;
+ if (mbs[1] >= (smeardeccurvecount + smearacccurvecount))
+ mbs[0] =
+ mbs[1] - (smeardeccurvecount + smearacccurvecount) + 2;
+ else
+ mbs[0] = 0;
+
+ if (mbs[1] >= (deccurvecount + acccurvecount))
+ mbs[1] -= (deccurvecount + acccurvecount) + 2;
+ else
+ mbs[1] = 0;
+ }
+ else
+ {
+ /* this scanner hasn't got any motorcurve */
+
+ /* set last step of accurve.smearing table (same as accurve.normalscan) */
+ data_lsb_set (&Regs[0xe4], myvalue, 3);
+
+ /* set last step of accurve.parkhome table (same as accurve.normalscan) */
+ data_lsb_set (&Regs[0xe7], myvalue, 3);
+
+ /* both motorbacksteps are equal */
+ mbs[0] = sm->motorbackstep;
+ mbs[1] = sm->motorbackstep;
+ }
+
+ /* show msi and motorbacksteps */
+ DBG (DBG_FNC, " -> msi = %i\n", sm->msi);
+ DBG (DBG_FNC, " -> motorbackstep1 = %i\n", mbs[0]);
+ DBG (DBG_FNC, " -> motorbackstep2 = %i\n", mbs[1]);
+
+ /* set msi */
+ data_bitset (&Regs[0xda], 0xff, _B0 (sm->msi)); /*xxxxxxxx */
+ data_bitset (&Regs[0xdd], 0x03, _B1 (sm->msi)); /*------xx*/
+
+ /* set motorbackstep (a) */
+ data_bitset (&Regs[0xdb], 0xff, _B0 (mbs[0])); /*xxxxxxxx */
+ data_bitset (&Regs[0xdd], 0x0c, _B1 (mbs[0])); /*----xx--*/
+
+ /* set motorbackstep (b) */
+ data_bitset (&Regs[0xdc], 0xff, _B0 (mbs[1])); /*xxxxxxxx */
+ data_bitset (&Regs[0xdd], 0x30, _B1 (mbs[1])); /*--xx----*/
+
+ /* 328b */
+
+ /* get dummy lines count */
+ dummyline = data_bitget (&Regs[0xd6], 0xf0);
+
+ myvalue = scancfg->coord.top * (dummyline * step_size);
+
+ if (lf02c >= myvalue)
+ scancfg->coord.top = 1;
+ else
+ scancfg->coord.top -= (lf02c / (dummyline * step_size)) - 1;
+
+ rst = lf02c; /* Result from Motor_Setup_Steps */
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_Setup_Motor: %i\n", rst);
+
+ return rst;
+}
+
+static void
+RTS_Setup_Exposure_Times (SANE_Byte * Regs, struct st_scanparams *scancfg,
+ struct st_scanmode *sm)
+{
+ DBG (DBG_FNC, "> RTS_Setup_Exposure_Times\n");
+
+ if ((sm != NULL) && (Regs != NULL) && (scancfg != NULL))
+ {
+ SANE_Int myexpt[3], linexpt, a;
+
+ /* calculate line exposure time */
+ linexpt = sm->ctpc + 1;
+ if (RTS_Debug->usbtype == USB11)
+ linexpt *= sm->multiexposureforfullspeed;
+
+ if (scancfg->depth > 8)
+ linexpt *= sm->multiexposurefor16bitmode;
+
+ linexpt--;
+
+ /* generate exposure times for each channel color */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ if ((linexpt > sm->mexpt[a]) && (sm->expt[a] == 0))
+ sm->expt[a] = sm->mexpt[a];
+
+ myexpt[a] = (sm->expt[a] == 0) ? sm->mexpt[a] : sm->expt[a];
+ }
+
+ /* save exposure times */
+ DBG (DBG_FNC, "-> Exposure times : %04x, %04x, %04x\n", sm->expt[0],
+ sm->expt[1], sm->expt[2]);
+ data_lsb_set (&Regs[0x36], sm->expt[CL_RED], 3);
+ data_lsb_set (&Regs[0x3c], sm->expt[CL_GREEN], 3);
+ data_lsb_set (&Regs[0x42], sm->expt[CL_BLUE], 3);
+
+ /* save maximum exposure times */
+ DBG (DBG_FNC, "-> Maximum exposure times: %04x, %04x, %04x\n",
+ sm->mexpt[0], sm->mexpt[1], sm->mexpt[2]);
+ data_lsb_set (&Regs[0x33], sm->mexpt[CL_RED], 3);
+ data_lsb_set (&Regs[0x39], sm->mexpt[CL_GREEN], 3);
+ data_lsb_set (&Regs[0x3f], sm->mexpt[CL_BLUE], 3);
+
+ /* save line exposure time */
+ data_lsb_set (&Regs[0x30], linexpt, 3);
+
+ /* scancfg->expt = lowest value */
+ scancfg->expt = min (min (myexpt[1], myexpt[2]), myexpt[0]);
+ }
+}
+
+static SANE_Int
+RTS_Setup_Line_Distances (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_hwdconfig *hwdcfg, SANE_Int mycolormode,
+ SANE_Int arrangeline)
+{
+ SANE_Int iLineDistance = 0;
+
+ if (arrangeline == FIX_BY_HARD)
+ {
+ /* we don't need to arrange retrieved line */
+ SANE_Int mylinedistance, myevenodddist;
+
+ mylinedistance =
+ (dev->sensorcfg->line_distance * scancfg->resolution_y) /
+ dev->sensorcfg->resolution;
+
+ if (hwdcfg->highresolution == TRUE)
+ myevenodddist =
+ (hwdcfg->sensorevenodddistance * scancfg->resolution_y) /
+ dev->sensorcfg->resolution;
+ else
+ myevenodddist = 0;
+
+ data_bitset (&Regs[0x149], 0x3f, myevenodddist);
+ data_bitset (&Regs[0x14a], 0x3f, mylinedistance);
+ data_bitset (&Regs[0x14b], 0x3f, mylinedistance + myevenodddist);
+ data_bitset (&Regs[0x14c], 0x3f, mylinedistance * 2);
+ data_bitset (&Regs[0x14d], 0x3f, (mylinedistance * 2) + myevenodddist);
+ }
+ else
+ {
+ /* arrange retrieved line */
+ data_bitset (&Regs[0x149], 0x3f, 0);
+ data_bitset (&Regs[0x14a], 0x3f, 0);
+ data_bitset (&Regs[0x14b], 0x3f, 0);
+ data_bitset (&Regs[0x14c], 0x3f, 0);
+ data_bitset (&Regs[0x14d], 0x3f, 0);
+
+ if (arrangeline == FIX_BY_SOFT)
+ {
+ if (hwdcfg->highresolution == FALSE)
+ {
+ if (mycolormode == CM_COLOR)
+ {
+ iLineDistance =
+ (dev->sensorcfg->line_distance * scan2.resolution_y) * 2;
+ iLineDistance =
+ (iLineDistance / dev->sensorcfg->resolution) + 1;
+ if (iLineDistance < 2)
+ iLineDistance = 2;
+ }
+ }
+ else
+ {
+ /* bcc */
+ if (mycolormode == CM_COLOR)
+ iLineDistance =
+ ((dev->sensorcfg->line_distance * 2) +
+ hwdcfg->sensorevenodddistance) * scan2.resolution_y;
+ else
+ iLineDistance =
+ dev->sensorcfg->line_distance * scan2.resolution_y;
+
+ iLineDistance =
+ (iLineDistance / dev->sensorcfg->resolution) + 1;
+ if (iLineDistance < 2)
+ iLineDistance = 2;
+ }
+
+ /* c25 */
+ iLineDistance &= 0xffff;
+ v15b4 = (iLineDistance > 0) ? 1 : 0;
+ imagesize += iLineDistance * bytesperline;
+ }
+ }
+
+ DBG (DBG_FNC,
+ "> RTS_Setup_Line_Distances(*Regs, *scancfg, *hwdcfg, mycolormode=%i, arrangeline=%i): %i\n",
+ mycolormode, arrangeline, iLineDistance);
+
+ return iLineDistance;
+}
+
+static SANE_Int
+RTS_Setup_Depth (SANE_Byte * Regs, struct st_scanparams *scancfg,
+ SANE_Int mycolormode)
+{
+ /* channels_per_line = channels_per_dot * scan.width
+ bytes_per_line = channels_per_line * bits_per_channel
+ */
+
+ SANE_Int bytes_per_line = 0;
+
+ if ((scancfg != NULL) && (Regs != NULL))
+ {
+ SANE_Int channels_per_line =
+ data_bitget (&Regs[0x12], 0xc0) * scancfg->coord.width;
+
+ bytes_per_line = channels_per_line;
+
+ /* set bits per channel in shading correction's register (0x1cf) */
+ if (mycolormode == CM_LINEART)
+ {
+ /* lineart mode */
+ bytes_per_line = (bytes_per_line + 7) / 8;
+ data_bitset (&Regs[0x1cf], 0x30, 3); /*--11----*/
+ }
+ else
+ {
+ /*f0c */
+ switch (scancfg->depth)
+ {
+ case 16:
+ /* 16 bits per channel */
+ bytes_per_line *= 2;
+ data_bitset (&Regs[0x1cf], 0x30, 2); /*--10----*/
+ break;
+ case 12:
+ /* 12 bits per channel */
+ bytes_per_line *= 2;
+ data_bitset (&Regs[0x1cf], 0x30, 1); /*--01----*/
+ break;
+ default:
+ /* 8 bits per channel */
+ data_bitset (&Regs[0x1cf], 0x30, 0); /*--00----*/
+ break;
+ }
+ }
+ }
+
+ return bytes_per_line;
+}
+
+static void
+RTS_Setup_Shading (SANE_Byte * Regs, struct st_scanparams *scancfg,
+ struct st_hwdconfig *hwdcfg, SANE_Int bytes_per_line)
+{
+ DBG (DBG_FNC,
+ "> RTS_Setup_Shading(*Regs, *scancfg, *hwdcfg, bytes_per_line=%i)\n",
+ bytes_per_line);
+
+ if ((Regs != NULL) && (hwdcfg != NULL))
+ {
+ SANE_Int dots_count, myvalue, myvalue2, mem_available, resolution_ratio,
+ sensor_line_distance;
+ SANE_Int channels, table_size;
+
+ resolution_ratio = Regs[0x0c0] & 0x1f;
+
+ /* 50de */
+ data_bitset (&Regs[0x1bf], 0x18, hwdcfg->unk3); /*---xx---*/
+
+ /* Enable black shading correction ? */
+ data_bitset (&Regs[0x1cf], 0x08, hwdcfg->black_shading); /*----x---*/
+
+ /* Enable white shading correction ? */
+ data_bitset (&Regs[0x1cf], 0x04, hwdcfg->white_shading); /*-----x--*/
+
+ if ((hwdcfg->white_shading != FALSE) && (hwdcfg->black_shading != FALSE)
+ && (hwdcfg->unk3 != 0))
+ data_bitset (&Regs[0x1cf], 0x04, 0); /*-----x--*/
+
+ table_size = 0;
+
+ /* if hwdcfg->black_shading */
+ if ((Regs[0x1cf] & 8) != 0)
+ table_size = (resolution_ratio * scancfg->coord.width) * 2; /* black shading buffer size? */
+
+ /* if hwdcfg->white_shading */
+ if ((Regs[0x1cf] & 4) != 0)
+ table_size += (resolution_ratio * scancfg->coord.width) * 2; /* white shading buffer size? */
+
+ /* Regs 0x1ba, 0x1bb, 0x1bd, 0x1c0 seem to be 4 pointers
+ to some buffer related to shading correction */
+
+ Regs[0x1ba] = 0x00;
+ table_size = (table_size + v160c_block_size - 1) / v160c_block_size;
+ table_size = ((table_size + 15) / 16) + 16;
+
+ Regs[0x1bf] &= 0xfe;
+ Regs[0x1bb] = _B0 (table_size);
+ Regs[0x1bc] = _B1 (table_size);
+ Regs[0x1bf] |= _B2 (table_size) & 1; /*-------x*/
+
+ Regs[0x1bf] &= 0xf9;
+ Regs[0x1bd] = _B0 (table_size * 2);
+ Regs[0x1be] = _B1 (table_size * 2);
+ Regs[0x1bf] |= (_B2 (table_size * 2) & 3) << 1; /*-----xx-*/
+
+ data_wide_bitset (&Regs[0x1c0], 0xfffff, table_size * 3);
+
+ mem_available = mem_total - ((table_size * 3) * 16);
+ sensor_line_distance = Regs[0x14a] & 0x3f;
+
+ /* select case channels_per_dot */
+ channels = data_lsb_get (&Regs[0x12], 1) >> 6;
+
+ switch (channels)
+ {
+ case 3: /* 3 channels per dot */
+ /* 528d */
+ dots_count = bytes_per_line / 3; /* 882 */
+ myvalue =
+ (((sensor_line_distance + 1) * dots_count) + v160c_block_size -
+ 1) / v160c_block_size;
+ myvalue2 = myvalue;
+ mem_available = (mem_available - (myvalue * 3) + 2) / 3;
+
+ myvalue += (table_size * 3) * 8;
+ myvalue = ((myvalue * 2) + mem_available);
+
+ data_bitset (&Regs[0x1c2], 0xf0, _B2 ((myvalue / 16) + 1)); /* 4 higher bits xxxx---- */
+ data_wide_bitset (&Regs[0x1c3], 0xffff, (myvalue / 16) + 1); /* 16 lower bits */
+
+ myvalue = myvalue + myvalue2 + mem_available;
+ data_wide_bitset (&Regs[0x1c5], 0xfffff, (myvalue / 16) + 1);
+ break;
+ case 2: /* 2 channels per dot */
+ dots_count = bytes_per_line / 2;
+ myvalue =
+ (((sensor_line_distance + 1) * dots_count) + v160c_block_size -
+ 1) / v160c_block_size;
+ mem_available = ((mem_available - myvalue) + 1) / 2;
+ myvalue += (((table_size * 3) + mem_available) / 16) + 1;
+
+ data_bitset (&Regs[0x1c2], 0xf0, _B2 (myvalue)); /* 4 higher bits xxxx---- */
+ data_wide_bitset (&Regs[0x1c3], 0xffff, myvalue); /* 16 lower bits */
+ break;
+ default:
+ dots_count = bytes_per_line;
+ break;
+ }
+
+ Regs[0x01c7] &= 0x0f;
+ Regs[0x01c8] = _B0 ((mem_total - 1) / 16);
+ Regs[0x01c9] = _B1 ((mem_total - 1) / 16);
+ Regs[0x01c7] |= (_B2 ((mem_total - 1) / 16) & 0x0f) << 4;
+
+ mem_available -= (dots_count + v160c_block_size - 1) / v160c_block_size;
+ mem_available /= 16;
+ Regs[0x0712] &= 0x0f;
+ Regs[0x0710] = _B0 (mem_available);
+ Regs[0x0711] = _B1 (mem_available);
+ Regs[0x0712] |= _B0 (_B2 (mem_available) << 4); /*xxxx---- */
+
+ Regs[0x0713] = 0x00;
+ Regs[0x0714] = 0x10;
+ Regs[0x0715] &= 0xf0;
+ }
+}
+
+static void
+RTS_Setup_Arrangeline (struct st_device *dev, struct st_hwdconfig *hwdcfg,
+ SANE_Int colormode)
+{
+ dev->scanning->arrange_compression =
+ (colormode == CM_LINEART) ? FALSE : hwdcfg->compression;
+
+ if ((colormode == CM_LINEART)
+ || ((colormode == CM_GRAY) && (hwdcfg->highresolution == FALSE)))
+ arrangeline2 = 0;
+ else
+ arrangeline2 = hwdcfg->arrangeline;
+
+ dev->scanning->arrange_hres = hwdcfg->highresolution;
+ dev->scanning->arrange_sensor_evenodd_dist =
+ (hwdcfg->highresolution == FALSE) ? 0 : hwdcfg->sensorevenodddistance;
+}
+
+static void
+RTS_Setup_Channels (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg, SANE_Int mycolormode)
+{
+ DBG (DBG_FNC, "> RTS_Setup_Channels(colormode=%i)\n", mycolormode);
+
+ if ((scancfg != NULL) && (Regs != NULL))
+ {
+ if ((mycolormode != CM_COLOR) && (mycolormode != 3))
+ {
+ /* CM_GRAY || CM_LINEART */
+ if (scancfg->samplerate == LINE_RATE)
+ {
+ /* Setting channels_per_dot to 1 */
+ data_bitset (&Regs[0x12], 0xc0, 1); /*01------ */
+
+ /* setting one rgb_channel_order */
+ data_bitset (&Regs[0x12], 0x03, dev->sensorcfg->rgb_order[scancfg->channel]); /*------xx*/
+
+ /* set sensor_channel_color_order */
+ data_bitset (&Regs[0x60a], 0x3f, 6); /*--xxxxxx*/
+
+ /* set samplerate */
+ data_bitset (&Regs[0x1cf], 0x40, PIXEL_RATE); /*-x------*/
+
+ /* set unknown data */
+ data_bitset (&Regs[0x1cf], 0x80, 1); /*x------- */
+
+ if (scancfg->channel == dev->sensorcfg->rgb_order[1])
+ {
+ /* mexpts[CL_RED] = mexpts[CL_GREEN] */
+ data_lsb_set (&Regs[0x33], data_lsb_get (&Regs[0x39], 3),
+ 3);
+
+ /* expts[CL_RED] = expts[CL_GREEN] */
+ data_lsb_set (&Regs[0x36], data_lsb_get (&Regs[0x3c], 3),
+ 3);
+ }
+ else if (scancfg->channel == dev->sensorcfg->rgb_order[2])
+ {
+ /* mexpts[CL_RED] = mexpts[CL_BLUE] */
+ data_lsb_set (&Regs[0x33], data_lsb_get (&Regs[0x3f], 3),
+ 3);
+
+ /* expts[CL_RED] = expts[CL_BLUE] */
+ data_lsb_set (&Regs[0x36], data_lsb_get (&Regs[0x42], 3),
+ 3);
+ }
+ }
+ else
+ {
+ /* e01 */
+ /* setting channels_per_dot to 2 */
+ data_bitset (&Regs[0x12], 0xc0, 2);
+
+ /* set two channel color order */
+ data_bitset (&Regs[0x12], 0x03, dev->sensorcfg->channel_gray[0]); /*------xx*/
+ data_bitset (&Regs[0x12], 0x0c, dev->sensorcfg->channel_gray[1]); /*----xx--*/
+
+ /* set samplerate */
+ data_bitset (&Regs[0x1cf], 0x40, LINE_RATE);
+
+ /* set unknown data */
+ data_bitset (&Regs[0x1cf], 0x80, 1);
+ }
+ }
+ else
+ {
+ /* CM_COLOR || 3 */
+ /* e42 */
+
+ /* setting channels_per_dot to 3 */
+ data_bitset (&Regs[0x12], 0xc0, 3);
+
+ /* setting samplerate */
+ data_bitset (&Regs[0x1cf], 0x40, scancfg->samplerate);
+
+ /* set unknown data */
+ data_bitset (&Regs[0x1cf], 0x80, 0);
+
+ /* set sensor chanel_color_order */
+ data_bitset (&Regs[0x60a], 0x03, dev->sensorcfg->channel_color[2]); /*------xx*/
+ data_bitset (&Regs[0x60a], 0x0c, dev->sensorcfg->channel_color[1]); /*----xx--*/
+ data_bitset (&Regs[0x60a], 0x30, dev->sensorcfg->channel_color[0]); /*--xx----*/
+
+ /* set rgb_channel_order */
+ data_bitset (&Regs[0x12], 0x03, dev->sensorcfg->rgb_order[0]); /*------xx*/
+ data_bitset (&Regs[0x12], 0x0c, dev->sensorcfg->rgb_order[1]); /*----xx--*/
+ data_bitset (&Regs[0x12], 0x30, dev->sensorcfg->rgb_order[2]); /*--xx----*/
+ }
+ }
+}
+
+static SANE_Int
+RTS_Setup (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg,
+ struct st_gain_offset *gain_offset)
+{
+ SANE_Int rst = ERROR; /* default */
+ SANE_Int lSMode;
+ SANE_Byte mycolormode;
+
+ DBG (DBG_FNC, "+ RTS_Setup:\n");
+ dbg_ScanParams (scancfg);
+ dbg_hwdcfg (hwdcfg);
+
+ mycolormode = scancfg->colormode;
+ if (scancfg->colormode != CM_COLOR)
+ {
+ if (scancfg->colormode == CM_LINEART)
+ scancfg->depth = 8;
+
+ if (scancfg->channel == 3)
+ {
+ if (scancfg->colormode == CM_GRAY)
+ mycolormode = (hwdcfg->arrangeline != FIX_BY_SOFT) ? 3 : CM_COLOR;
+ else
+ mycolormode = 3;
+ }
+ }
+
+ /* 42b47d6 */
+ memcpy (&scan2, scancfg, sizeof (struct st_scanparams));
+
+ scantype = hwdcfg->scantype;
+ lSMode =
+ RTS_GetScanmode (dev, scantype, mycolormode, scancfg->resolution_x);
+ if (lSMode >= 0)
+ {
+ struct st_scanmode *sm = dev->scanmodes[lSMode];
+
+ if (sm != NULL)
+ {
+ SANE_Int dummyline, iLineDistance, resolution_ratio, bytes_per_line;
+ struct st_coords rts_coords;
+
+ iLineDistance = 0;
+
+ scancfg->timing = sm->timing;
+ scancfg->sensorresolution =
+ dev->timings[scancfg->timing]->sensorresolution;
+ scancfg->shadinglength =
+ (((scancfg->sensorresolution * 17) / 2) + 3) & 0xfffffffc;
+ scancfg->samplerate = sm->samplerate;
+
+ hwdcfg->motorplus = sm->motorplus;
+
+ /* set systemclock */
+ data_bitset (&Regs[0x00], 0x0f, sm->systemclock);
+
+ /* setting exposure times */
+ RTS_Setup_Exposure_Times (Regs, scancfg, sm);
+
+ /* setting arranges */
+ RTS_Setup_Arrangeline (dev, hwdcfg, mycolormode);
+
+ /* set up line distances */
+ iLineDistance =
+ RTS_Setup_Line_Distances (dev, Regs, scancfg, hwdcfg, mycolormode,
+ arrangeline);
+
+ /* 4c67 */
+
+ /* setup channel colors */
+ RTS_Setup_Channels (dev, Regs, scancfg, mycolormode);
+
+ /* setup depth */
+ bytes_per_line = RTS_Setup_Depth (Regs, scancfg, mycolormode);
+
+ /* f61 */
+
+ /* Set resolution ratio */
+ resolution_ratio =
+ (scancfg->sensorresolution / scancfg->resolution_x) & 0x1f;
+ data_bitset (&Regs[0xc0], 0x1f, resolution_ratio);
+
+ /* set sensor timing values */
+ RTS_Setup_SensorTiming (dev, scancfg->timing, Regs);
+
+ data_bitset (&Regs[0xd8], 0x40, ((scantype == ST_NORMAL) ? 0 : 1)); /*-x------*/
+
+ /* Use static head ? */
+ data_bitset (&Regs[0xd8], 0x80, ((hwdcfg->static_head == FALSE) ? 1 : 0)); /*x------- */
+
+ /* Setting up gamma */
+ RTS_Setup_Gamma (Regs, hwdcfg);
+
+ /* setup shading correction */
+ RTS_Setup_Shading (Regs, scancfg, hwdcfg, bytes_per_line);
+
+ /* setup stepper motor */
+ hwdcfg->startpos =
+ RTS_Setup_Motor (dev, Regs, scancfg,
+ hwdcfg->motor_direction | MTR_ENABLED);
+
+ /* set coordinates */
+ dummyline = data_bitget (&Regs[0xd6], 0xf0);
+
+ if (scancfg->coord.left == 0)
+ scancfg->coord.left++;
+ if (scancfg->coord.top == 0)
+ scancfg->coord.top++;
+
+ rts_coords.left = scancfg->coord.left * resolution_ratio;
+ rts_coords.width = scancfg->coord.width * resolution_ratio;
+ rts_coords.top = scancfg->coord.top * dummyline;
+ rts_coords.height =
+ ((Regs[0x14d] & 0x3f) + scancfg->coord.height +
+ iLineDistance) * dummyline;
+
+ if ((rts_coords.left & 1) == 0)
+ rts_coords.left++;
+
+ RTS_Setup_Coords (Regs, rts_coords.left, rts_coords.top,
+ rts_coords.width, rts_coords.height);
+
+ data_bitset (&Regs[0x01], 0x06, 0); /*-----xx-*/
+
+ /* dummy_scan? */
+ data_bitset (&Regs[0x01], 0x10, hwdcfg->dummy_scan); /*---x----*/
+
+ data_bitset (&Regs[0x163], 0xc0, 1); /*xx------ */
+
+ if (dev->scanning->arrange_compression != FALSE)
+ {
+ Regs[0x60b] &= 0x8f;
+ data_bitset (&Regs[0x60b], 0x10, 1); /*-001----*/
+ }
+ else
+ data_bitset (&Regs[0x60b], 0x7f, 0); /*-0000000*/
+
+ if (mycolormode == 3)
+ {
+ SANE_Int channels_per_line;
+
+ /* Set channels_per_line = channels_per_dot * scan_width */
+ channels_per_line =
+ data_bitget (&Regs[0x12], 0xc0) * scancfg->coord.width;
+ data_wide_bitset (&Regs[0x060c], 0x3ffff, channels_per_line);
+
+ /* Sets 16 bits per channel */
+ data_bitset (&Regs[0x1cf], 0x30, 2); /*--10----*/
+
+ Regs[0x60b] |= 0x40;
+ if (v1619 == 0x21)
+ {
+ dev->scanning->arrange_compression = FALSE;
+ data_bitset (&Regs[0x60b], 0x10, 0); /*---0----*/
+ }
+
+ switch (scancfg->depth)
+ {
+ case 8:
+ case 16:
+ Regs[0x060b] &= 0xf3;
+ break;
+ case 12:
+ Regs[0x060b] = (Regs[0x060b] & 0xfb) | 0x08;
+ break;
+ }
+
+ if (scancfg->colormode == CM_LINEART)
+ data_bitset (&Regs[0x60b], 0x0c, 0);
+
+ /* disable gamma correction ¿? */
+ data_bitset (&Regs[0x1d0], 0x40, 0);
+ }
+
+ /* 5683 */
+ /* Set calibration table */
+ RTS_Setup_GainOffset (Regs, gain_offset);
+
+ rst = OK;
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_Setup: %i\n", rst);
+
+ return rst;
+}
+
+static void
+RTS_Setup_Coords (SANE_Byte * Regs, SANE_Int iLeft, SANE_Int iTop,
+ SANE_Int width, SANE_Int height)
+{
+ DBG (DBG_FNC,
+ "> RTS_Setup_Coords(*Regs, iLeft=%i, iTop=%i, width=%i, height=%i)\n",
+ iLeft, iTop, width, height);
+
+ if (Regs != NULL)
+ {
+ /* Set Left coord */
+ data_lsb_set (&Regs[0xb0], iLeft, 2);
+
+ /* Set Right coord */
+ data_lsb_set (&Regs[0xb2], iLeft + width, 2);
+
+ /* Set Top coord */
+ data_lsb_set (&Regs[0xd0], iTop, 2);
+ data_bitset (&Regs[0xd4], 0x0f, _B2 (iTop));
+
+ /* Set Down coord */
+ data_lsb_set (&Regs[0xd2], iTop + height, 2);
+ data_bitset (&Regs[0xd4], 0xf0, _B2 (iTop + height));
+ }
+}
+
+static void
+RTS_Setup_GainOffset (SANE_Byte * Regs, struct st_gain_offset *gain_offset)
+{
+ SANE_Byte fake[] =
+ { 0x19, 0x15, 0x19, 0x64, 0x64, 0x64, 0x74, 0xc0, 0x74, 0xc0, 0x6d,
+ 0xc0, 0x6d, 0xc0, 0x5f, 0xc0, 0x5f, 0xc0
+ };
+
+ DBG (DBG_FNC, "> RTS_Setup_GainOffset(*Regs, *gain_offset)\n");
+ dbg_calibtable (gain_offset);
+
+ if ((Regs != NULL) && (gain_offset != NULL))
+ {
+ if (RTS_Debug->calibrate == FALSE)
+ {
+ data_bitset (&Regs[0x13], 0x03, gain_offset->pag[CL_RED]); /*------xx*/
+ data_bitset (&Regs[0x13], 0x0c, gain_offset->pag[CL_GREEN]); /*----xx--*/
+ data_bitset (&Regs[0x13], 0x30, gain_offset->pag[CL_BLUE]); /*--xx----*/
+
+ memcpy (&Regs[0x14], &fake, 18);
+ }
+ else
+ {
+ SANE_Int a;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ /* Offsets */
+ Regs[0x1a + (a * 4)] = _B0 (gain_offset->edcg1[a]);
+ Regs[0x1b + (a * 4)] =
+ ((gain_offset->edcg1[a] >> 1) & 0x80) | (gain_offset->
+ edcg2[a] & 0x7f);
+ Regs[0x1c + (a * 4)] = _B0 (gain_offset->odcg1[a]);
+ Regs[0x1d + (a * 4)] =
+ ((gain_offset->odcg1[a] >> 1) & 0x80) | (gain_offset->
+ odcg2[a] & 0x7f);
+
+ /* Variable Gain Amplifier */
+ data_bitset (&Regs[0x14 + a], 0x1f, gain_offset->vgag1[a]);
+ data_bitset (&Regs[0x17 + a], 0x1f, gain_offset->vgag2[a]);
+ }
+
+ data_bitset (&Regs[0x13], 0x03, gain_offset->pag[CL_RED]); /*------xx*/
+ data_bitset (&Regs[0x13], 0x0c, gain_offset->pag[CL_GREEN]); /*----xx--*/
+ data_bitset (&Regs[0x13], 0x30, gain_offset->pag[CL_BLUE]); /*--xx----*/
+ }
+ }
+}
+
+static void
+Calibrate_Free (struct st_cal2 *calbuffers)
+{
+ DBG (DBG_FNC, "> Calibrate_Free(*calbuffers)\n");
+
+ if (calbuffers != NULL)
+ {
+ SANE_Int c;
+
+ if (calbuffers->table2 != NULL)
+ {
+ free (calbuffers->table2);
+ calbuffers->table2 = NULL;
+ }
+
+ for (c = 0; c < 4; c++)
+ {
+ if (calbuffers->tables[c] != NULL)
+ {
+ free (calbuffers->tables[c]);
+ calbuffers->tables[c] = NULL;
+ }
+ }
+
+ calbuffers->shadinglength1 = 0;
+ calbuffers->tables_size = 0;
+ calbuffers->shadinglength3 = 0;
+ }
+}
+
+static SANE_Int
+Calibrate_Malloc (struct st_cal2 *calbuffers, SANE_Byte * Regs,
+ struct st_calibration *myCalib, SANE_Int somelength)
+{
+ SANE_Int myshadinglength, pos;
+ SANE_Int rst;
+
+ if ((calbuffers != NULL) && (Regs != NULL) && (myCalib != NULL))
+ {
+ if ((Regs[0x1bf] & 0x18) == 0)
+ {
+ if ((((Regs[0x1cf] >> 1) & Regs[0x1cf]) & 0x04) != 0)
+ calbuffers->table_count = 2;
+ else
+ calbuffers->table_count = 4;
+ }
+ else
+ calbuffers->table_count = 4;
+
+ /*365d */
+ myshadinglength = myCalib->shadinglength * 2;
+ calbuffers->shadinglength1 = min (myshadinglength, somelength);
+
+ if ((myshadinglength % somelength) != 0)
+ calbuffers->tables_size =
+ (myshadinglength >= somelength) ? somelength * 2 : somelength;
+ else
+ calbuffers->tables_size = somelength;
+
+ if (myshadinglength >= somelength)
+ {
+ calbuffers->shadinglength1 =
+ (myshadinglength % calbuffers->shadinglength1) +
+ calbuffers->shadinglength1;
+ calbuffers->shadinglength3 =
+ ((myCalib->shadinglength * 2) / somelength) - 1;
+ }
+ else
+ calbuffers->shadinglength3 = 0;
+
+ calbuffers->shadinglength3 =
+ (somelength / 16) * calbuffers->shadinglength3;
+
+ rst = OK;
+ for (pos = 0; pos < calbuffers->table_count; pos++)
+ {
+ calbuffers->tables[pos] =
+ (USHORT *) malloc (calbuffers->tables_size * sizeof (USHORT));
+ if (calbuffers->tables[pos] == NULL)
+ {
+ rst = ERROR;
+ break;
+ }
+ }
+
+ if (rst == OK)
+ {
+ calbuffers->table2 =
+ (USHORT *) malloc (calbuffers->tables_size * sizeof (USHORT));
+ if (calbuffers->table2 == NULL)
+ rst = ERROR;
+ }
+
+ if (rst != OK)
+ Calibrate_Free (calbuffers);
+ }
+ else
+ rst = ERROR;
+
+ DBG (DBG_FNC,
+ "> Calibrate_Malloc(*calbuffers, *Regs, *myCalib, somelength=%i): %i\n",
+ somelength, rst);
+
+ return rst;
+}
+
+static SANE_Int
+fn3560 (USHORT * table, struct st_cal2 *calbuffers, SANE_Int * tablepos)
+{
+ /*05FEF974 001F99B0 |table = 001F99B0
+ 05FEF978 05FEFA08 |calbuffers->tables[0] = 05FEFA08
+ 05FEF97C 000000A0 |calbuffers->shadinglength3 = 000000A0
+ 05FEF980 00000348 |calbuffers->shadinglength1 = 00000348
+ 05FEF984 04F01502 |calbuffers->table_count = 04F01502
+ 05FEF988 05FEF998 \Arg6 = 05FEF998
+ */
+
+ if (table != NULL)
+ {
+ SANE_Int pos[4] = { 0, 0, 0, 0 }; /*f960 f964 f968 f96c */
+ SANE_Int usetable = 0;
+ SANE_Int a;
+
+ SANE_Int mylength3 = calbuffers->shadinglength1; /*f97c */
+ SANE_Byte *pPointer =
+ (SANE_Byte *) (table + (calbuffers->shadinglength3 * 16));
+
+ DBG (DBG_FNC, "> fn3560(*table, *calbuffers, *tablepos)\n");
+
+ if (mylength3 > 0)
+ {
+ do
+ {
+ if (calbuffers->tables[usetable] != NULL)
+ {
+ if (mylength3 <= 16)
+ {
+ if (mylength3 > 0)
+ {
+ do
+ {
+ *(calbuffers->tables[usetable] +
+ pos[usetable]) = _B0 (*pPointer);
+ pPointer++;
+ pos[usetable]++;
+ mylength3--;
+ }
+ while (mylength3 > 0);
+ }
+ break;
+ }
+
+ for (a = 0; a < 16; a++)
+ {
+ *(calbuffers->tables[usetable] + pos[usetable]) =
+ _B0 (*pPointer);
+ pPointer++;
+ pos[usetable]++;
+ }
+ }
+
+ mylength3 -= 16;
+ usetable++;
+ if (usetable == calbuffers->table_count)
+ usetable = 0;
+ }
+ while (mylength3 > 0);
+ }
+
+ /*35f8 */
+ if (calbuffers->table_count > 0)
+ {
+ /* Return position of each table */
+ memcpy (tablepos, pos, sizeof (SANE_Int) * 4);
+ }
+ }
+
+ return OK;
+}
+
+static SANE_Int
+Calib_WriteTable (struct st_device *dev, SANE_Byte * table, SANE_Int size,
+ SANE_Int data)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ Calib_WriteTable(*table, size=%i):\n", size);
+
+ if ((table != NULL) && (size > 0))
+ {
+ SANE_Int transferred;
+
+ if (RTS_DMA_Reset (dev) == OK)
+ {
+ /* Send size to write */
+ if (RTS_DMA_Enable_Write (dev, 0x0004, size, data) == OK)
+ /* Send data */
+ rst = Bulk_Operation (dev, BLK_WRITE, size, table, &transferred);
+ }
+ }
+
+ DBG (DBG_FNC, "- Calib_WriteTable: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Calib_ReadTable (struct st_device *dev, SANE_Byte * table, SANE_Int size,
+ SANE_Int data)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ Calib_ReadTable(*table, size=%i):\n", size);
+
+ if ((table != NULL) && (size > 0))
+ {
+ SANE_Int transferred;
+
+ if (RTS_DMA_Reset (dev) == OK)
+ {
+ /* Send size to read */
+ if (RTS_DMA_Enable_Read (dev, 0x0004, size, data) == OK)
+ /* Retrieve data */
+ rst = Bulk_Operation (dev, BLK_READ, size, table, &transferred);
+ }
+ }
+
+ DBG (DBG_FNC, "- Calib_ReadTable: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+fn3330 (struct st_device *dev, SANE_Byte * Regs, struct st_cal2 *calbuffers,
+ SANE_Int sensorchannelcolor, SANE_Int * tablepos, SANE_Int data)
+{
+ /*05EEF968 04F0F7F8 |Regs = 04F0F7F8
+ 05EEF96C 02DEC838 |calbuffers->table2 = 02DEC838
+ 05EEF970 05EEFA08 |calbuffers->tables[] = 05EEFA08
+ 05EEF974 00000000 |sensorchannelcolor = 00000000
+ 05EEF978 000000A0 |calbuffers->shadinglength3 = 000000A0
+ 05EEF97C 00000400 |calbuffers->tables_size = 00000400
+ 05EEF980 05EEF998 |&pos = 05EEF998
+ 05EEF984 00221502 |calbuffers->table_count = 00221502
+ 05EEF988 00000000 \data = 00000000
+ */
+
+ SANE_Int table_count = calbuffers->table_count; /*f960 */
+ SANE_Int schcolor = _B0 (sensorchannelcolor);
+ SANE_Int a = 0;
+ SANE_Int tablelength = calbuffers->shadinglength3 / table_count; /*f954 */
+ SANE_Int val_color = 0; /*f974 */
+ SANE_Int val_lineart = 0; /*f978 */
+ SANE_Int val_gray = 0; /*ebx */
+ SANE_Int value4 = 0; /*ebp */
+ SANE_Int size;
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC,
+ "+ fn3330(*Regs, *calbuffers, sensorchannelcolor=%i, *tablepos, data=%i):\n",
+ sensorchannelcolor, data);
+
+ if (calbuffers->table_count > 0)
+ {
+ do
+ {
+ if (calbuffers->table_count == 2)
+ {
+ /*338c */
+ if (a != 0)
+ {
+ /*3394 */
+ if (_B0 (data) == 0)
+ {
+ val_color = 0x100000;
+ val_lineart = 0x100000;
+ val_gray = 0x200000;
+ }
+ else
+ {
+ /*343a */
+ val_color = 0x300000;
+ val_lineart = 0x300000;
+ val_gray = 0;
+ }
+ }
+ else
+ {
+ /*33be */
+ if (_B0 (data) == 0)
+ {
+ val_color = 0;
+ val_lineart = 0;
+ val_gray = 0x300000;
+ }
+ else
+ {
+ /*342a */
+ val_color = 0x200000;
+ val_lineart = 0x200000;
+ val_gray = 0x100000;
+ }
+ }
+ }
+ else
+ {
+ /*33d5 */
+ switch (a)
+ {
+ case 0:
+ val_color = 0;
+ val_lineart = 0;
+ val_gray = 0x300000;
+ break;
+ case 1:
+ val_color = 0x200000;
+ val_lineart = 0x200000;
+ val_gray = 0x100000;
+ break;
+ case 2:
+ val_color = 0x100000;
+ val_lineart = 0x100000;
+ val_gray = 0x200000;
+ break;
+ case 3:
+ val_color = 0x300000;
+ val_lineart = 0x300000;
+ val_gray = 0;
+ break;
+ }
+ }
+
+ /*3449 */
+ switch (schcolor)
+ {
+ case CM_LINEART:
+ size =
+ (((Regs[0x1bf] >> 1) & 3) << 0x10) | (Regs[0x1be] << 0x08) |
+ Regs[0x1bd];
+ value4 = (tablelength + size) | val_lineart;
+ break;
+ case CM_GRAY:
+ size =
+ ((Regs[0x1bf] & 1) << 0x10) | (Regs[0x1bc] << 0x08) |
+ Regs[0x1bb];
+ value4 = (tablelength + size) | val_gray;
+ break;
+ default:
+ size = _B0 (Regs[0x1ba]);
+ value4 = (tablelength + size) | val_color;
+ break;
+ }
+
+ if (Calib_ReadTable
+ (dev, (SANE_Byte *) calbuffers->table2, calbuffers->tables_size,
+ value4) != OK)
+ {
+ rst = ERROR;
+ break;
+ }
+
+ memcpy (calbuffers->tables[a], calbuffers->table2, tablepos[a]);
+
+ if (tablepos[a + 1] == 0)
+ break;
+
+ a++;
+ }
+ while (a < calbuffers->table_count);
+ }
+
+ DBG (DBG_FNC, "- fn3330: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+fn3730 (struct st_device *dev, struct st_cal2 *calbuffers, SANE_Byte * Regs,
+ USHORT * table, SANE_Int sensorchannelcolor, SANE_Int data)
+{
+ /*05FEF9AC |calbuffers = 05FEF9F8
+ 05FEF9B0 |Regs = 04EFF7F8
+ 05FEF9B4 |table = 001F99B0
+ 05FEF9B8 |sensorchannelcolor = 00000000
+ 05FEF9BC |data = 00000000
+ */
+
+ SANE_Int pos[4] = { 0, 0, 0, 0 }; /*f998 f99c f9a0 f9a4 */
+ SANE_Int rst;
+
+ DBG (DBG_FNC,
+ "+ fn3730(*calbuffers, *Regs, *table, sensorchannelcolor=%i, data=%i):\n",
+ sensorchannelcolor, data);
+
+ fn3560 (table, calbuffers, pos);
+ rst = fn3330 (dev, Regs, calbuffers, sensorchannelcolor, pos, data);
+
+ DBG (DBG_FNC, "- fn3730: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Shading_white_apply (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int channels, struct st_calibration *myCalib,
+ struct st_cal2 *calbuffers)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Shading_white_apply(channels=%i)\n", channels);
+
+ /*3e7f */
+ Calibrate_Malloc (calbuffers, Regs, myCalib,
+ (RTS_Debug->usbtype == USB20) ? 0x200 : 0x40);
+
+ if (channels > 0)
+ {
+ /*int a; */
+ SANE_Int chnl;
+ SANE_Int pos; /*fa2c */
+ SANE_Int transferred;
+
+ rst = ERROR;
+
+ for (chnl = 0; chnl < channels; chnl++)
+ {
+ /*for (a = 0; a < myCalib->shadinglength; a++)
+ myCalib->black_shading[chnl][a] = 0x2000; */
+ /* 11 tries */
+ for (pos = 0; pos <= 10; pos++)
+ {
+ /* Send size to write */
+ if (RTS_DMA_Enable_Write
+ (dev, dev->sensorcfg->channel_color[chnl] | 0x14,
+ myCalib->shadinglength, 0) == OK)
+ /* Send data */
+ Bulk_Operation (dev, BLK_WRITE,
+ myCalib->shadinglength * sizeof (USHORT),
+ (SANE_Byte *) & myCalib->
+ white_shading[chnl][myCalib->first_position -
+ 1], &transferred);
+
+ /*3df7 */
+ if (fn3730
+ (dev, calbuffers, Regs,
+ &myCalib->white_shading[chnl][myCalib->first_position - 1],
+ dev->sensorcfg->channel_color[chnl], 1) == OK)
+ {
+ rst = OK;
+ break;
+ }
+
+ RTS_DMA_Cancel (dev);
+ }
+ }
+ }
+
+ Calibrate_Free (calbuffers);
+
+ DBG (DBG_FNC, "- Shading_white_apply: %i\n", rst);
+
+ return OK;
+}
+
+static SANE_Int
+Shading_black_apply (struct st_device *dev, SANE_Byte * Regs,
+ SANE_Int channels, struct st_calibration *myCalib,
+ struct st_cal2 *calbuffers)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Shading_black_apply(channels=%i)\n", channels);
+
+ /* 3d79 */
+ Calibrate_Malloc (calbuffers, Regs, myCalib,
+ (RTS_Debug->usbtype == USB20) ? 0x200 : 0x40);
+
+ if (channels > 0)
+ {
+ /*int a; */
+ SANE_Int chnl;
+ SANE_Int pos; /*fa2c */
+ SANE_Int transferred;
+
+ rst = ERROR;
+
+ for (chnl = 0; chnl < channels; chnl++)
+ {
+ /* 11 tries */
+ /*for (a = 0; a < myCalib->shadinglength; a++)
+ myCalib->black_shading[chnl][a] = 0x2000; */
+
+ for (pos = 0; pos <= 10; pos++)
+ {
+ /* Send size to write */
+ if (RTS_DMA_Enable_Write
+ (dev, dev->sensorcfg->channel_color[chnl] | 0x10,
+ myCalib->shadinglength, 0) == OK)
+ /* Send data */
+ Bulk_Operation (dev, BLK_WRITE,
+ myCalib->shadinglength * sizeof (USHORT),
+ (SANE_Byte *) & myCalib->
+ black_shading[chnl][myCalib->first_position -
+ 1], &transferred);
+
+ /*3df7 */
+ if (fn3730
+ (dev, calbuffers, Regs,
+ &myCalib->black_shading[chnl][myCalib->first_position - 1],
+ dev->sensorcfg->channel_color[chnl], 0) == OK)
+ {
+ rst = OK;
+ break;
+ }
+
+ RTS_DMA_Cancel (dev);
+ }
+ }
+ }
+
+ /*3e62 */
+ Calibrate_Free (calbuffers);
+
+ DBG (DBG_FNC, "- Shading_black_apply: %i\n", rst);
+
+ return OK;
+}
+
+static SANE_Int
+Shading_apply (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *myvar, struct st_calibration *myCalib)
+{
+ /*
+ Regs f1bc
+ myvar f020
+ hwdcfg e838
+ arg4 e81c
+ myCalib e820
+ */
+
+ SANE_Int rst; /* lf9e0 */
+ SANE_Int myfact; /* e820 */
+ SANE_Int shadata;
+ SANE_Byte channels; /* f9d4 */
+ SANE_Int myShadingBase; /* e818 */
+
+ char lf9d1;
+ char lf9d0;
+
+ DBG (DBG_FNC, "+ Shading_apply(*Regs, *myvar, *mygamma, *myCalib):\n");
+ dbg_ScanParams (myvar);
+
+ lf9d0 = (Regs[0x60b] >> 6) & 1;
+ lf9d1 = (Regs[0x60b] >> 4) & 1;
+ Regs[0x060b] &= 0xaf;
+ rst = Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]);
+ if (rst == OK)
+ {
+ SANE_Byte colormode = myvar->colormode; /*fa24 */
+ SANE_Int le7cc, le7d8;
+ struct st_cal2 calbuffers; /* f9f8 */
+
+ if (colormode != CM_COLOR)
+ {
+ if (myvar->channel != 3)
+ {
+ if (colormode != 3)
+ channels = (myvar->samplerate == PIXEL_RATE) ? 2 : 1;
+ else
+ channels = 3;
+ }
+ else
+ {
+ colormode = 3;
+ channels = 3;
+ }
+ }
+ else
+ channels = 3;
+
+ /*
+ White shading formula : 2000H x Target / (Wn-Dn) = White Gain data ----- for 8 times system
+ White shading formula : 4000H x Target / (Wn-Dn) = White Gain data ----- for 4 times system
+ For example : Target = 3FFFH Wn = 2FFFH Dn = 0040H and 8 times system operation
+ then White Gain = 2000H x 3FFFH / (2FFFH-0040H) = 2AE4H (1.34033 times)
+ */
+ /* 3aad */
+ if (colormode == 3)
+ {
+ /*
+ SANE_Int pos;
+ SANE_Int colour;
+
+ myShadingBase = shadingbase;
+
+ for (colour = 0; colour < channels; colour++)
+ {
+ if (myCalib->white_shading[colour] != NULL)
+ {
+ myfact = shadingfact[colour];
+ if (myCalib->shadinglength > 0)
+ {
+ for (pos = myCalib->first_position - 1; pos < myCalib->shadinglength; pos++)
+ myCalib->white_shading[colour][pos] = (myCalib->white_shading[colour][pos] * myfact) / myShadingBase;
+ }
+ } else break;
+ }
+ */
+ }
+
+ /* 3b3b */
+ if (myCalib->shading_enabled != FALSE)
+ {
+ /* 3b46 */
+ SANE_Int colour, pos;
+ le7cc = shadingbase;
+ le7d8 = shadingbase;
+
+ DBG (DBG_FNC, "-> Shading type: %i\n", myCalib->shading_type);
+
+ for (colour = 0; colour < channels; colour++)
+ {
+ if (colormode == 3)
+ le7cc = shadingfact[colour];
+
+ myShadingBase = ((Regs[0x1cf] & 2) != 0) ? 0x2000 : 0x4000;
+
+ myfact = myCalib->WRef[colour] * myShadingBase;
+
+ if (myCalib->shading_type == 2)
+ {
+ /*3bd8 */
+ if ((myCalib->black_shading[colour] != NULL)
+ && (myCalib->white_shading[colour] != NULL))
+ {
+ for (pos = myCalib->first_position - 1;
+ pos < myCalib->shadinglength; pos++)
+ {
+ if (myCalib->white_shading[colour][pos] == 0)
+ shadata = myShadingBase;
+ else
+ shadata =
+ myfact / myCalib->white_shading[colour][pos];
+
+ shadata = min ((shadata * le7cc) / le7d8, 0xff00);
+ myCalib->black_shading[colour][pos] &= 0xff;
+ myCalib->black_shading[colour][pos] |=
+ shadata & 0xff00;
+ }
+ }
+ else
+ break;
+ }
+ else
+ {
+ /*3c63 */
+ if (myCalib->shading_type == 3)
+ {
+ /*3c68 */
+ if (myCalib->black_shading[colour] != NULL)
+ {
+ for (pos = myCalib->first_position - 1;
+ pos < myCalib->shadinglength; pos++)
+ {
+ if (myCalib->black_shading[colour][pos] == 0)
+ shadata = myShadingBase;
+ else
+ shadata =
+ myfact /
+ myCalib->black_shading[colour][pos];
+
+ shadata =
+ min ((shadata * le7cc) / le7d8, 0xffc0);
+ myCalib->black_shading[colour][pos] &= 0x3f;
+ myCalib->black_shading[colour][pos] |=
+ shadata & 0xffc0;
+ }
+ }
+ else
+ break;
+ }
+ else
+ {
+ /*3ce3 */
+ if (myCalib->white_shading[colour] != NULL)
+ {
+ for (pos = 0; pos < myCalib->shadinglength; pos++)
+ {
+ if (myCalib->white_shading[colour][pos] == 0)
+ shadata = myShadingBase;
+ else
+ shadata =
+ myfact /
+ myCalib->white_shading[colour][pos];
+
+ shadata =
+ min ((shadata * le7cc) / le7d8, 0xffff);
+ myCalib->white_shading[colour][pos] = shadata;
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+
+ /*3d4c */
+ bzero (&calbuffers, sizeof (struct st_cal2));
+
+ /* If black shading correction is enabled ... */
+ if ((Regs[0x1cf] & 8) != 0)
+ Shading_black_apply (dev, Regs, channels, myCalib, &calbuffers);
+
+ /*3e6e */
+
+ /* If white shading correction is enabled ... */
+ if ((Regs[0x1cf] & 4) != 0)
+ Shading_white_apply (dev, Regs, channels, myCalib, &calbuffers);
+
+ /* 3f74 */
+ if (rst == 0)
+ {
+ data_bitset (&Regs[0x60b], 0x40, lf9d0); /*-x------*/
+ data_bitset (&Regs[0x60b], 0x10, lf9d1); /*---x----*/
+
+ rst = Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]);
+ }
+ }
+ /*3fb5 */
+
+ DBG (DBG_FNC, "- Shading_apply: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Bulk_Operation (struct st_device *dev, SANE_Byte op, SANE_Int buffer_size,
+ SANE_Byte * buffer, SANE_Int * transfered)
+{
+ SANE_Int iTransferSize, iBytesToTransfer, iPos, rst, iBytesTransfered;
+
+ DBG (DBG_FNC, "+ Bulk_Operation(op=%s, buffer_size=%i, buffer):\n",
+ ((op & 0x01) != 0) ? "READ" : "WRITE", buffer_size);
+
+ iBytesToTransfer = buffer_size;
+ iPos = 0;
+ rst = OK;
+ iBytesTransfered = 0;
+
+ if (transfered != NULL)
+ *transfered = 0;
+
+ iTransferSize = min (buffer_size, RTS_Debug->dmatransfersize);
+
+ if (op != 0)
+ {
+ /* Lectura */
+ do
+ {
+ iTransferSize = min (iTransferSize, iBytesToTransfer);
+
+ iBytesTransfered =
+ Read_Bulk (dev->usb_handle, &buffer[iPos], iTransferSize);
+ if (iBytesTransfered < 0)
+ {
+ rst = ERROR;
+ break;
+ }
+ else
+ {
+ if (transfered != NULL)
+ *transfered += iBytesTransfered;
+ }
+ iPos += iTransferSize;
+ iBytesToTransfer -= iTransferSize;
+ }
+ while (iBytesToTransfer > 0);
+ }
+ else
+ {
+ /* Escritura */
+ do
+ {
+ iTransferSize = min (iTransferSize, iBytesToTransfer);
+
+ if (Write_Bulk (dev->usb_handle, &buffer[iPos], iTransferSize) !=
+ OK)
+ {
+ rst = ERROR;
+ break;
+ }
+ else
+ {
+ if (transfered != NULL)
+ *transfered += iTransferSize;
+ }
+ iPos += iTransferSize;
+ iBytesToTransfer -= iTransferSize;
+ }
+ while (iBytesToTransfer > 0);
+ }
+
+ DBG (DBG_FNC, "- Bulk_Operation: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Reading_BufferSize_Notify (struct st_device *dev, SANE_Int data,
+ SANE_Int size)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Reading_BufferSize_Notify(data=%i, size=%i):\n", data,
+ size);
+
+ rst = RTS_DMA_Enable_Read (dev, 0x0008, size, data);
+
+ DBG (DBG_FNC, "- Reading_BufferSize_Notify: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Reading_Wait (struct st_device *dev, SANE_Byte Channels_per_dot,
+ SANE_Byte Channel_size, SANE_Int size, SANE_Int * last_amount,
+ SANE_Int seconds, SANE_Byte op)
+{
+ SANE_Int rst;
+ SANE_Byte cTimeout, executing;
+ SANE_Int lastAmount, myAmount;
+ long tick;
+
+ DBG (DBG_FNC,
+ "+ Reading_Wait(Channels_per_dot=%i, Channel_size=%i, size=%i, *last_amount, seconds=%i, op=%i):\n",
+ Channels_per_dot, Channel_size, size, seconds, op);
+
+ rst = OK;
+ cTimeout = FALSE;
+ lastAmount = 0;
+
+ myAmount = Reading_BufferSize_Get (dev, Channels_per_dot, Channel_size);
+ if (myAmount < size)
+ {
+ /* Wait until scanner fills its buffer */
+ if (seconds == 0)
+ seconds = 10;
+ tick = GetTickCount () + (seconds * 1000);
+
+ while (cTimeout == FALSE)
+ {
+ myAmount =
+ Reading_BufferSize_Get (dev, Channels_per_dot, Channel_size);
+
+ /* check special case */
+ if (op == TRUE)
+ {
+ if (((myAmount + 0x450) > size)
+ || (RTS_IsExecuting (dev, &executing) == FALSE))
+ break;
+ }
+
+ if (myAmount < size)
+ {
+ /* Check timeout */
+ if (myAmount == lastAmount)
+ {
+ /* we are in timeout? */
+ if (tick < GetTickCount ())
+ {
+ /* TIMEOUT */
+ rst = ERROR;
+ cTimeout = TRUE;
+ }
+ else
+ usleep (100 * 1000);
+ }
+ else
+ {
+ /* Amount increased, update tick */
+ lastAmount = myAmount;
+ tick = GetTickCount () + (seconds * 1000);
+ }
+ }
+ else
+ {
+ lastAmount = myAmount;
+ break; /* buffer full */
+ }
+ }
+ }
+
+ if (last_amount != NULL)
+ *last_amount = myAmount;
+
+ DBG (DBG_FNC, "- Reading_Wait: %i , last_amount=%i\n", rst, myAmount);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_GetImage_GetBuffer (struct st_device *dev, double dSize,
+ char unsigned *buffer, double *transferred)
+{
+ SANE_Int rst = ERROR;
+ SANE_Int itransferred;
+ double dtransferred = 0;
+
+ DBG (DBG_FNC, "+ RTS_GetImage_GetBuffer(dSize=%f, buffer, transferred):\n",
+ dSize);
+
+ rst = OK;
+ dSize /= 2;
+
+ if (dSize > 0)
+ {
+ SANE_Int myLength;
+ SANE_Int iPos = 0;
+
+ do
+ {
+ itransferred = 0;
+ myLength =
+ (dSize <=
+ RTS_Debug->dmasetlength) ? dSize : RTS_Debug->dmasetlength;
+
+ if (myLength > 0x1ffe0)
+ myLength = 0x1ffe0;
+
+ rst = ERROR;
+ if (Reading_Wait (dev, 0, 1, myLength * 2, NULL, 5, FALSE) == OK)
+ {
+ if (Reading_BufferSize_Notify (dev, 0, myLength * 2) == OK)
+ rst =
+ Bulk_Operation (dev, BLK_READ, myLength * 2, &buffer[iPos],
+ &itransferred);
+ }
+
+ if (rst != OK)
+ break;
+
+ iPos += itransferred;
+ dSize -= itransferred;
+ dtransferred += itransferred * 2;
+ }
+ while (dSize > 0);
+ }
+
+ /* Return bytes transferred */
+ if (transferred != NULL)
+ *transferred = dtransferred;
+
+ if (rst != OK)
+ RTS_DMA_Cancel (dev);
+
+ DBG (DBG_FNC, "- RTS_GetImage_GetBuffer: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_GetImage_Read (struct st_device *dev, SANE_Byte * buffer,
+ struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg)
+{
+ /*buffer f80c = esp+14
+ scancfg f850 = esp+18
+ hwdcfg faac = */
+
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC, "+ RTS_GetImage_Read(buffer, scancfg, hwdcfg):\n");
+
+ if (buffer != NULL)
+ {
+ double dSize = scancfg->bytesperline * scancfg->coord.height;
+ SANE_Byte exfn;
+
+ if (scancfg->depth == 12)
+ dSize = (dSize * 3) / 4;
+
+ /*3ff6 */
+ exfn = 1;
+ if (hwdcfg != NULL)
+ if (hwdcfg->compression != FALSE)
+ exfn = 0;
+
+ if (exfn != 0)
+ {
+ double transferred;
+ rst = RTS_GetImage_GetBuffer (dev, dSize, buffer, &transferred);
+ }
+
+ if (rst == OK)
+ RTS_WaitScanEnd (dev, 1500);
+ }
+
+ DBG (DBG_FNC, "- RTS_GetImage_Read: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_GetImage (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg,
+ struct st_gain_offset *gain_offset, SANE_Byte * buffer,
+ struct st_calibration *myCalib, SANE_Int options,
+ SANE_Int gaincontrol)
+{
+ /* 42b8e10 */
+
+ SANE_Int rst = ERROR; /* default */
+
+ DBG (DBG_FNC,
+ "+ RTS_GetImage(*Regs, *scancfg, *gain_offset, *buffer, myCalib, options=0x%08x, gaincontrol=%i):\n",
+ options, gaincontrol);
+ dbg_ScanParams (scancfg);
+
+ /* validate arguments */
+ if ((Regs != NULL) && (scancfg != NULL))
+ {
+ if ((scancfg->coord.width != 0) && (scancfg->coord.height != 0))
+ {
+ struct st_scanparams *myscancfg;
+
+ /* let's make a copy of scan config */
+ myscancfg =
+ (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ if (myscancfg != NULL)
+ {
+ struct st_hwdconfig *hwdcfg;
+
+ memcpy (myscancfg, scancfg, sizeof (struct st_scanparams));
+
+ /* Allocate space for low level config */
+ hwdcfg =
+ (struct st_hwdconfig *) malloc (sizeof (struct st_hwdconfig));
+ if (hwdcfg != NULL)
+ {
+ bzero (hwdcfg, sizeof (struct st_hwdconfig));
+
+ if (((options & 2) != 0) || ((_B1 (options) & 1) != 0))
+ {
+ /* switch off lamp */
+ data_bitset (&Regs[0x146], 0x40, 0);
+
+ Write_Byte (dev->usb_handle, 0xe946, Regs[0x146]);
+ usleep (1000 * ((v14b4 == 0) ? 500 : 300));
+ }
+
+ hwdcfg->scantype = scan.scantype;
+ hwdcfg->use_gamma_tables =
+ ((options & OP_USE_GAMMA) != 0) ? 1 : 0;
+ hwdcfg->white_shading =
+ ((options & OP_WHITE_SHAD) != 0) ? 1 : 0;
+ hwdcfg->black_shading =
+ ((options & OP_BLACK_SHAD) != 0) ? 1 : 0;
+ hwdcfg->motor_direction =
+ ((options & OP_BACKWARD) !=
+ 0) ? MTR_BACKWARD : MTR_FORWARD;
+ hwdcfg->compression =
+ ((options & OP_COMPRESSION) != 0) ? 1 : 0;
+ hwdcfg->static_head =
+ ((options & OP_STATIC_HEAD) != 0) ? 1 : 0;
+ hwdcfg->dummy_scan = (buffer == NULL) ? TRUE : FALSE;
+ hwdcfg->arrangeline = 0;
+ hwdcfg->highresolution =
+ (myscancfg->resolution_x > 1200) ? TRUE : FALSE;
+ hwdcfg->unk3 = 0;
+
+ /* Set Left coord */
+ myscancfg->coord.left +=
+ ((dev->sensorcfg->type == CCD_SENSOR) ? 24 : 50);
+
+ switch (myscancfg->resolution_x)
+ {
+ case 1200:
+ myscancfg->coord.left -= 63;
+ break;
+ case 2400:
+ myscancfg->coord.left -= 126;
+ break;
+ }
+
+ if (myscancfg->coord.left < 0)
+ myscancfg->coord.left = 0;
+
+ RTS_Setup (dev, Regs, myscancfg, hwdcfg, gain_offset);
+
+ /* Setting exposure time */
+ switch (scan.scantype)
+ {
+ case ST_NORMAL:
+ if (scan.resolution_x == 100)
+ {
+ SANE_Int iValue;
+ SANE_Byte *myRegs;
+
+ myRegs =
+ (SANE_Byte *) malloc (RT_BUFFER_LEN *
+ sizeof (SANE_Byte));
+ if (myRegs != NULL)
+ {
+ bzero (myRegs,
+ RT_BUFFER_LEN * sizeof (SANE_Byte));
+ RTS_Setup (dev, myRegs, &scan, hwdcfg,
+ gain_offset);
+
+ iValue = data_lsb_get (&myRegs[0x30], 3);
+ data_lsb_set (&Regs[0x30], iValue, 3);
+
+ /*Copy myregisters mexpts to Regs mexpts */
+ iValue = data_lsb_get (&myRegs[0x33], 3);
+ data_lsb_set (&Regs[0x33], iValue, 3);
+
+ iValue = data_lsb_get (&myRegs[0x39], 3);
+ data_lsb_set (&Regs[0x39], iValue, 3);
+
+ iValue = data_lsb_get (&myRegs[0x3f], 3);
+ data_lsb_set (&Regs[0x3f], iValue, 3);
+
+ free (myRegs);
+ }
+ }
+ break;
+ case ST_NEG:
+ {
+ SANE_Int myvalue;
+
+ /* Setting exposure times for Negative scans */
+ data_lsb_set (&Regs[0x30], myscancfg->expt, 3);
+ data_lsb_set (&Regs[0x33], myscancfg->expt, 3);
+ data_lsb_set (&Regs[0x39], myscancfg->expt, 3);
+ data_lsb_set (&Regs[0x3f], myscancfg->expt, 3);
+
+ data_lsb_set (&Regs[0x36], 0, 3);
+ data_lsb_set (&Regs[0x3c], 0, 3);
+ data_lsb_set (&Regs[0x42], 0, 3);
+
+ myvalue =
+ ((myscancfg->expt +
+ 1) / (data_lsb_get (&Regs[0xe0], 1) + 1)) - 1;
+ data_lsb_set (&Regs[0xe1], myvalue, 3);
+ }
+ break;
+ }
+
+ /* 91a0 */
+ if (myscancfg->resolution_y > 600)
+ {
+ options |= 0x20000000;
+ if (options != 0) /* Always true ... */
+ SetMultiExposure (dev, Regs);
+ else
+ myscancfg->coord.top += hwdcfg->startpos;
+ }
+ else
+ SetMultiExposure (dev, Regs);
+
+ /* 91e2 */
+ RTS_WriteRegs (dev->usb_handle, Regs);
+ if (myCalib != NULL)
+ Shading_apply (dev, Regs, myscancfg, myCalib);
+
+ if (dev->motorcfg->changemotorcurrent != FALSE)
+ Motor_Change (dev, Regs,
+ Motor_GetFromResolution (myscancfg->
+ resolution_x));
+
+ /* mlock = 0 */
+ data_bitset (&Regs[0x00], 0x10, 0);
+
+ data_wide_bitset (&Regs[0xde], 0xfff, 0);
+
+ /* release motor */
+ Motor_Release (dev);
+
+ if (RTS_Warm_Reset (dev) == OK)
+ {
+ rst = OK;
+
+ SetLock (dev->usb_handle, Regs,
+ (myscancfg->depth == 16) ? FALSE : TRUE);
+
+ /* set gain control */
+ Lamp_SetGainMode (dev, Regs, myscancfg->resolution_x,
+ gaincontrol);
+
+ /* send registers to scanner */
+ if (RTS_WriteRegs (dev->usb_handle, Regs) == OK)
+ {
+ /* execute! */
+ if (RTS_Execute (dev) == OK)
+ RTS_GetImage_Read (dev, buffer, myscancfg, hwdcfg); /*92e7 */
+ }
+
+ /*92fc */
+ SetLock (dev->usb_handle, Regs, FALSE);
+
+ if ((options & 0x200) != 0)
+ {
+ /* switch on lamp */
+ data_bitset (&Regs[0x146], 0x40, 1);
+
+ Write_Byte (dev->usb_handle, 0xe946, Regs[0x146]);
+ /* Wait 3 seconds */
+ usleep (1000 * 3000);
+ }
+
+ /*9351 */
+ if (dev->motorcfg->changemotorcurrent == TRUE)
+ Motor_Change (dev, dev->init_regs, 3);
+ }
+
+ /* free low level configuration */
+ free (hwdcfg);
+ }
+
+ /* free scanning configuration */
+ free (myscancfg);
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- RTS_GetImage: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Refs_Detect (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution_x,
+ SANE_Int resolution_y, SANE_Int * x, SANE_Int * y)
+{
+ SANE_Int rst = ERROR; /* default */
+
+ DBG (DBG_FNC, "+ Refs_Detect(*Regs, resolution_x=%i, resolution_y=%i):\n",
+ resolution_x, resolution_y);
+
+ if ((x != NULL) && (y != NULL))
+ {
+ SANE_Byte *image;
+ struct st_scanparams scancfg;
+
+ *x = *y = 0; /* default */
+
+ /* set configuration to scan a little area at the top-left corner */
+ bzero (&scancfg, sizeof (struct st_scanparams));
+ scancfg.depth = 8;
+ scancfg.colormode = CM_GRAY;
+ scancfg.channel = CL_RED;
+ scancfg.resolution_x = resolution_x;
+ scancfg.resolution_y = resolution_y;
+ scancfg.coord.left = 4;
+ scancfg.coord.width = (resolution_x * 3) / 10;
+ scancfg.coord.top = 1;
+ scancfg.coord.height = (resolution_y * 4) / 10;
+ scancfg.shadinglength = (resolution_x * 17) / 2;
+ scancfg.bytesperline = scancfg.coord.width;
+
+ /* allocate space to store image */
+ image =
+ (SANE_Byte *) malloc ((scancfg.coord.height * scancfg.coord.width) *
+ sizeof (SANE_Byte));
+ if (image != NULL)
+ {
+ struct st_gain_offset gain_offset;
+ SANE_Int gaincontrol, pwmlamplevel_backup, C;
+
+ gaincontrol = 0;
+ if (RTS_Debug->use_fixed_pwm == FALSE)
+ {
+ /* 3877 */
+ gaincontrol = Lamp_GetGainMode (dev, resolution_x, ST_NORMAL); /* scan.scantype */
+ pwmlamplevel = 0;
+ Lamp_PWM_use (dev, 1);
+ Lamp_PWM_DutyCycle_Set (dev, (gaincontrol == 0) ? 0x12 : 0x26);
+
+ /* Enciende flb lamp */
+ Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP);
+ usleep (1000 * 2000);
+ }
+
+ /* 38d6 */
+ pwmlamplevel_backup = pwmlamplevel;
+ pwmlamplevel = 0;
+ Lamp_PWM_use (dev, 1);
+
+ bzero (&gain_offset, sizeof (struct st_gain_offset));
+ for (C = CL_RED; C <= CL_BLUE; C++)
+ {
+ gain_offset.pag[C] = 3;
+ gain_offset.vgag1[C] = 4;
+ gain_offset.vgag2[C] = 4;
+ }
+
+ /* perform lamp warmup */
+ Lamp_Warmup (dev, Regs, FLB_LAMP, resolution_x);
+
+ /* retrieve image from scanner */
+ if (RTS_GetImage
+ (dev, Regs, &scancfg, &gain_offset, image, 0, 0x20000000,
+ gaincontrol) == OK)
+ {
+ SANE_Int ser1, ler1;
+
+ /* same image to disk if required by user */
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ {
+ dbg_tiff_save ("pre-autoref.tiff",
+ scancfg.coord.width,
+ scancfg.coord.height,
+ scancfg.depth,
+ CM_GRAY,
+ scancfg.resolution_x,
+ scancfg.resolution_y,
+ image,
+ scancfg.coord.height * scancfg.coord.width);
+ }
+
+ /* calculate reference position */
+ if (Refs_Analyze_Pattern (&scancfg, image, &ler1, 1, &ser1, 0)
+ == OK)
+ {
+ *y = scancfg.coord.top + ler1;
+ *x = scancfg.coord.left + ser1;
+
+ rst = OK;
+ }
+ }
+
+ free (image);
+
+ pwmlamplevel = pwmlamplevel_backup;
+ }
+
+ DBG (DBG_FNC, " -> Detected refs: x=%i , y=%i\n", *x, *y);
+ }
+
+ DBG (DBG_FNC, "- Refs_Detect: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Refs_Set (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg)
+{
+ SANE_Int rst;
+ SANE_Int y, x;
+ struct st_autoref refcfg;
+
+ DBG (DBG_FNC, "+ Refs_Set(*Regs, *scancfg):\n");
+ dbg_ScanParams (scancfg);
+
+ rst = OK;
+
+ /* get fixed references for given resolution */
+ cfg_vrefs_get (dev->sensorcfg->type, scancfg->resolution_x, &scan.ler,
+ &scan.ser);
+ scan.leftleading = scan.ser;
+ scan.startpos = scan.ler;
+
+ /* get auto reference configuration */
+ cfg_autoref_get (&refcfg);
+
+ if (refcfg.type != REF_NONE)
+ {
+ /* if reference counter is == 0 perform auto detection */
+ if (Refs_Counter_Load (dev) == 0)
+ {
+ DBG (DBG_FNC,
+ " -> Refs_Set - Autodetection mandatory (counter == 0)\n");
+
+ refcfg.type = REF_AUTODETECT;
+ }
+
+ switch (refcfg.type)
+ {
+ case REF_AUTODETECT:
+ /* try to autodetect references scanning a little area */
+ if (Refs_Detect
+ (dev, Regs, refcfg.resolution, refcfg.resolution, &x, &y) == OK)
+ Refs_Save (dev, x, y);
+ else
+ rst = ERROR;
+
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+ break;
+
+ case REF_TAKEFROMSCANNER:
+ /* Try to get values from scanner */
+ if (Refs_Load (dev, &x, &y) == ERROR)
+ {
+ if (Refs_Detect
+ (dev, Regs, refcfg.resolution, refcfg.resolution, &x,
+ &y) == OK)
+ Refs_Save (dev, x, y);
+ else
+ rst = ERROR;
+
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+ }
+ break;
+ }
+
+ if (rst == OK)
+ {
+ /* values are based on resolution given by refcfg.resolution.
+
+ offset_x and y are based on 2400 dpi so convert values to that dpi
+ before adding offsets and then return to resolution given by user */
+
+ x *= (2400 / refcfg.resolution);
+ y *= (2400 / refcfg.resolution);
+
+ scan.leftleading = x;
+ scan.startpos = y;
+ scan.ser = ((x + refcfg.offset_x) * scancfg->resolution_x) / 2400;
+ scan.ler = ((y + refcfg.offset_y) * scancfg->resolution_y) / 2400;
+
+ DBG (DBG_FNC,
+ " -> After SEROffset and LEROffset, xoffset = %i, yoffset =%i\n",
+ scan.ser, scan.ler);
+ }
+
+ /* increase refs counter */
+ Refs_Counter_Inc (dev);
+ }
+
+ DBG (DBG_FNC, "- Refs_Set: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Lamp_Status_Set (struct st_device *dev, SANE_Byte * Regs, SANE_Int turn_on,
+ SANE_Int lamp)
+{
+ SANE_Int rst = ERROR; /* default */
+ SANE_Byte freevar = FALSE;
+
+ DBG (DBG_FNC, "+ Lamp_Status_Set(*Regs, turn_on=%i->%s, lamp=%s)\n",
+ turn_on,
+ ((((lamp - 1) | turn_on) & 1) == 1) ? "Yes" : "No",
+ (lamp == FLB_LAMP) ? "FLB_LAMP" : "TMA_LAMP");
+
+ if (Regs == NULL)
+ {
+ Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ if (Regs != NULL)
+ freevar = TRUE;
+ }
+
+ if (Regs != NULL)
+ {
+ RTS_ReadRegs (dev->usb_handle, Regs);
+
+ /* next op depends on chipset */
+ switch (dev->chipset->model)
+ {
+ case RTS8822BL_03A:
+ /* register 0xe946 has 2 bits and each one referres one lamp
+ 0x40: FLB_LAMP | 0x20 : TMA_LAMP
+ if both were enabled both lamps would be switched on */
+ data_bitset (&Regs[0x146], 0x20, ((lamp == TMA_LAMP) && (turn_on == TRUE)) ? 1 : 0); /* TMA */
+ data_bitset (&Regs[0x146], 0x40, ((lamp == FLB_LAMP) && (turn_on == TRUE)) ? 1 : 0); /* FLB */
+
+ data_bitset (&Regs[0x155], 0x10, (lamp != FLB_LAMP) ? 1 : 0);
+ break;
+ default:
+ /* the other chipsets only use one bit to indicate when a lamp is
+ switched on or not being bit 0x10 in 0xe955 who decides which lamp
+ is affected */
+ /* switch on lamp? yes if TMA_LAMP, else whatever turn_on says */
+ data_bitset (&Regs[0x146], 0x40, ((lamp - 1) | turn_on));
+ /* what lamp must be switched on? */
+ if ((Regs[0x146] & 0x40) != 0)
+ data_bitset (&Regs[0x155], 0x10, (lamp != FLB_LAMP) ? 1 : 0);
+ break;
+ }
+
+ /*42b8cd1 */
+ /* switch on/off lamp */
+ /*dev->init_regs[0x0146] = (dev->init_regs[0x146] & 0xbf) | (Regs[0x146] & 0x40); */
+ dev->init_regs[0x0146] = (dev->init_regs[0x146] & 0x9f) | (Regs[0x146] & 0x60); /*-xx-----*/
+
+ /* Which lamp */
+ dev->init_regs[0x0155] = Regs[0x0155];
+ Write_Byte (dev->usb_handle, 0xe946, Regs[0x0146]);
+ usleep (1000 * 200);
+ Write_Buffer (dev->usb_handle, 0xe954, &Regs[0x0154], 2);
+ }
+
+ if (freevar != FALSE)
+ {
+ free (Regs);
+ Regs = NULL;
+ }
+
+ DBG (DBG_FNC, "- Lamp_Status_Set: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Get_PAG_Value (SANE_Byte scantype, SANE_Byte color)
+{
+ SANE_Int rst, iType, iColor;
+
+ switch (scantype)
+ {
+ case ST_NEG:
+ iType = CALIBNEGATIVEFILM;
+ break;
+ case ST_TA:
+ iType = CALIBTRANSPARENT;
+ break;
+ case ST_NORMAL:
+ iType = CALIBREFLECTIVE;
+ break;
+ default:
+ iType = CALIBREFLECTIVE;
+ break;
+ }
+
+ switch (color)
+ {
+ case CL_BLUE:
+ iColor = PAGB;
+ break;
+ case CL_GREEN:
+ iColor = PAGG;
+ break;
+ case CL_RED:
+ iColor = PAGR;
+ break;
+ default:
+ iColor = PAGR;
+ break;
+ }
+
+ rst = get_value (iType, iColor, 1, FITCALIBRATE);
+
+ DBG (DBG_FNC, "> Get_PAG_Value(scantype=%s, color=%i): %i\n",
+ dbg_scantype (scantype), color, rst);
+
+ return rst;
+}
+
+static SANE_Byte
+Lamp_GetGainMode (struct st_device *dev, SANE_Int resolution,
+ SANE_Byte scantype)
+{
+ SANE_Byte ret;
+ SANE_Int mygain, iValue;
+
+ switch (scantype)
+ {
+ case ST_TA:
+ ret = 0;
+ iValue = DPIGAINCONTROL_TA600;
+ break;
+ case ST_NEG:
+ ret = 1;
+ iValue = DPIGAINCONTROL_NEG600;
+ break;
+ default: /* Reflective */
+ ret = 1;
+ iValue = DPIGAINCONTROL600;
+ break;
+ }
+
+ mygain = get_value (SCAN_PARAM, iValue, ret, usbfile);
+ ret = 0;
+
+/*
+
+*/
+ if (scantype == ST_NORMAL)
+ {
+ if (dev->chipset->model == RTS8822L_02A)
+ {
+ switch (resolution)
+ {
+ case 100:
+ case 150:
+ case 300:
+ case 600:
+ case 1200:
+ case 2400:
+ case 4800:
+ ret = ((RTS_Debug->usbtype != USB11) && (mygain != 0)) ? 1 : 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (resolution)
+ {
+ case 100:
+ case 200:
+ case 300:
+ case 600:
+ if (RTS_Debug->usbtype != USB11)
+ ret = (mygain != 0) ? 1 : 0;
+ else
+ ret = (resolution == 100) ? 1 : 0;
+ break;
+ case 1200:
+ case 2400:
+ ret = 0;
+ break;
+ }
+ }
+ }
+ else if (scantype == ST_TA)
+ {
+ switch (resolution)
+ {
+ /*hp3970 */
+ case 100:
+ case 200:
+ /*common */
+ case 300:
+ case 600:
+ case 1200:
+ case 2400:
+ /*hp4370 */
+ case 150:
+ case 4800:
+ ret = ((RTS_Debug->usbtype != USB11) && (mygain != 0)) ? 1 : 0;
+ break;
+ }
+ }
+ else
+ {
+ /* ST_NEG */
+ switch (resolution)
+ {
+ case 100:
+ case 200:
+ case 300:
+ case 600:
+ ret = ((RTS_Debug->usbtype != USB11) && (mygain != 0)) ? 1 : 0;
+ break;
+ case 1200:
+ case 2400:
+ case 4800: /*hp4370 */
+ ret = 0;
+ break;
+ }
+ }
+
+ DBG (DBG_FNC, "> Lamp_GetGainMode(resolution=%i, scantype=%s): %i\n",
+ resolution, dbg_scantype (scantype), ret);
+
+ return ret;
+}
+
+static SANE_Int
+GetOneLineInfo (struct st_device *dev, SANE_Int resolution,
+ SANE_Int * maximus, SANE_Int * minimus, double *average)
+{
+ SANE_Int rst = ERROR;
+
+ DBG (DBG_FNC,
+ "+ GetOneLineInfo(resolution=%i, *maximus, *minimus, *average):\n",
+ resolution);
+
+ /* Check parameters */
+ if ((maximus != NULL) && (minimus != NULL) && (average != NULL))
+ {
+ SANE_Byte *Regs, *image;
+ SANE_Int a, gainmode;
+ struct st_gain_offset gain_offset;
+ struct st_scanparams scancfg;
+
+ Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ if (Regs != NULL)
+ {
+ /* Copy scanner registers */
+ memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ /* Setting some registers */
+ for (a = 0x192; a <= 0x19d; a++)
+ Regs[a] = 0;
+
+ /* Create calibration table */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ gain_offset.edcg1[a] = 256;
+ gain_offset.edcg2[a] = 0;
+ gain_offset.odcg1[a] = 256;
+ gain_offset.odcg2[a] = 0;
+ gain_offset.vgag1[a] = 4;
+ gain_offset.vgag2[a] = 4;
+ gain_offset.pag[a] = Get_PAG_Value (scan.scantype, a);
+ }
+
+ RTS_GetScanmode (dev, scantype, 0, resolution);
+
+ /* Setting scanning params */
+ memset (&scancfg, 0, sizeof (struct st_scanparams));
+ scancfg.colormode = CM_COLOR;
+ scancfg.resolution_x = resolution;
+ scancfg.resolution_y = resolution;
+ scancfg.coord.left = 100;
+ scancfg.coord.width = (resolution * 8.5) - 100;
+ scancfg.coord.top = 1;
+ scancfg.coord.height = 1;
+ scancfg.depth = 8;
+ scancfg.shadinglength = resolution * 8.5;
+ scancfg.v157c = scancfg.coord.width * 3;
+ scancfg.bytesperline = scancfg.v157c;
+
+ /* Reserve buffer for line */
+ image =
+ (SANE_Byte *) malloc (((scancfg.coord.width * 0x21) * 3) *
+ sizeof (SANE_Byte));
+ if (image != NULL)
+ {
+ gainmode =
+ Lamp_GetGainMode (dev, resolution & 0xffff, scan.scantype);
+ if (RTS_GetImage
+ (dev, Regs, &scancfg, &gain_offset, image, 0,
+ OP_STATIC_HEAD, gainmode) != ERROR)
+ {
+ /* Read all image to take max min and average colours */
+ SANE_Byte *pointer1 = image;
+ SANE_Byte *pointer2;
+ SANE_Byte *pointer3;
+ SANE_Int cmin[3]; /* min values */
+ SANE_Int cmax[3]; /* max values */
+ double cave[3]; /* average values */
+ SANE_Int mysize;
+
+ if (scancfg.colormode != CM_GRAY)
+ {
+ pointer2 = image;
+ pointer3 = image;
+ }
+ else
+ {
+ pointer2 = image + 1;
+ pointer3 = image + 2;
+ }
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ cmin[a] = 255;
+ cmax[a] = 0;
+ cave[a] = 0;
+ }
+
+ if (scancfg.coord.height > 0)
+ {
+ SANE_Int y, x;
+ SANE_Byte *mypointer;
+ SANE_Byte color;
+ SANE_Int desp[3];
+
+ desp[CL_RED] = pointer1 - pointer3;
+ desp[CL_GREEN] = pointer2 - pointer3;
+ desp[CL_BLUE] = 0;
+
+ for (y = 0; y < scancfg.coord.height; y++)
+ {
+ if (scancfg.coord.width > 0)
+ {
+ mypointer = pointer3;
+
+ for (x = 0; x < scancfg.coord.width; x++)
+ {
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ /* Take colour values */
+ color = *(mypointer + desp[a]);
+
+ /* Take max values for each color */
+ cmax[a] = max (cmax[a], color);
+
+ /* Take min values for each color */
+ cmin[a] = min (cmin[a], color);
+
+ /* Average */
+ cave[a] += color;
+ }
+
+ mypointer += 3;
+ }
+ }
+
+ /* point to the pixel that is below */
+ pointer1 += scancfg.coord.width * 3;
+ pointer2 += scancfg.coord.width * 3;
+ pointer3 += scancfg.coord.width * 3;
+ }
+ }
+
+ mysize = scancfg.coord.height * scancfg.coord.width;
+ if (mysize < 1)
+ mysize = 1;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ maximus[a] = cmax[a];
+ minimus[a] = cmin[a];
+ average[a] = cave[a] / mysize;
+ }
+
+ DBG (DBG_FNC, " -> GetOneLineInfo: max r=%3i g=%3i b=%3i\n",
+ maximus[CL_RED], maximus[CL_GREEN], maximus[CL_BLUE]);
+ DBG (DBG_FNC, " -> min r=%3i g=%3i b=%3i\n",
+ minimus[CL_RED], minimus[CL_GREEN], minimus[CL_BLUE]);
+ DBG (DBG_FNC,
+ " -> avg r=%3.0f g=%3.0f b=%3.0f\n",
+ average[CL_RED], average[CL_GREEN], average[CL_BLUE]);
+
+ rst = OK;
+ }
+
+ free (image);
+ }
+
+ free (Regs);
+ }
+ }
+
+ DBG (DBG_FNC, "- GetOneLineInfo: %i\n", rst);
+
+ return OK;
+}
+
+static SANE_Int
+Lamp_PWM_CheckStable (struct st_device *dev, SANE_Int resolution,
+ SANE_Int lamp)
+{
+ struct st_checkstable check;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Lamp_PWM_CheckStable(resolution=%i, lamp=%i):\n",
+ resolution, lamp);
+
+ rst = cfg_checkstable_get (lamp, &check);
+
+ if (rst == OK)
+ {
+ SANE_Int maximus[3] = { 0 };
+ SANE_Int minimus[3] = { 0 };
+ double average[3] = { 0 };
+ SANE_Int maxbigger;
+ SANE_Int last_colour = 0;
+
+ double diff = check.diff * 0.01;
+ long tottime = GetTickCount () + check.tottime;
+
+ while (GetTickCount () <= tottime)
+ {
+ rst = GetOneLineInfo (dev, resolution, maximus, minimus, average);
+ if (rst == OK)
+ {
+ /* Takes maximal colour value */
+ maxbigger =
+ max (maximus[CL_GREEN],
+ max (maximus[CL_BLUE], maximus[CL_RED]));
+
+ /*breaks when colour intensity increases 'diff' or lower */
+ if (abs (maxbigger - last_colour) < diff)
+ {
+ DBG (DBG_FNC, " -> PWM is ready\n");
+ break;
+ }
+
+ last_colour = maxbigger;
+ }
+
+ usleep (1000 * check.interval);
+ }
+
+ }
+
+ DBG (DBG_FNC, "- Lamp_PWM_CheckStable: %i\n", rst);
+
+ return OK;
+}
+
+static SANE_Byte
+Refs_Counter_Load (struct st_device *dev)
+{
+ SANE_Byte data = 15;
+
+ DBG (DBG_FNC, "+ Refs_Counter_Load:\n");
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x78, &data) != OK)
+ data = 15;
+
+ DBG (DBG_FNC, "- Refs_Counter_Load: %i\n", _B0 (data));
+
+ return data;
+}
+
+static SANE_Int
+Refs_Counter_Save (struct st_device *dev, SANE_Byte data)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ Refs_Counter_Save(data=%i):\n", data);
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ if (data > 15)
+ data = 15;
+
+ rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x78, data);
+ }
+
+ DBG (DBG_FNC, "- Refs_Counter_Save: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Refs_Counter_Inc (struct st_device *dev)
+{
+ SANE_Byte data;
+
+ DBG (DBG_FNC, "+ Refs_Counter_Inc:\n");
+
+ data = Refs_Counter_Load (dev) + 1;
+
+ if (data >= 15)
+ data = 0;
+
+ Refs_Counter_Save (dev, data);
+
+ DBG (DBG_FNC, "- Refs_Counter_Inc() : Count=%i\n", data);
+
+ return OK;
+}
+
+static SANE_Int
+Load_StripCoords (SANE_Int scantype, SANE_Int * ypos, SANE_Int * xpos)
+{
+ SANE_Int iType;
+
+ switch (scantype)
+ {
+ case 3:
+ iType = CALIBNEGATIVEFILM;
+ break;
+ case 2:
+ iType = CALIBTRANSPARENT;
+ break;
+ default:
+ iType = CALIBREFLECTIVE;
+ break;
+ }
+
+ *xpos = get_value (iType, WSTRIPXPOS, 0, FITCALIBRATE);
+ *ypos = get_value (iType, WSTRIPYPOS, 0, FITCALIBRATE);
+
+ DBG (DBG_FNC, "> Load_StripCoords(scantype=%s): ypos=%i, xpos=%i\n",
+ dbg_scantype (scantype), *ypos, *xpos);
+
+ return OK;
+}
+
+static SANE_Int
+Head_Relocate (struct st_device *dev, SANE_Int speed, SANE_Int direction,
+ SANE_Int ypos)
+{
+ SANE_Int rst;
+ SANE_Byte *Regs;
+
+ DBG (DBG_FNC, "+ Head_Relocate(speed=%i, direction=%i, ypos=%i):\n", speed,
+ direction, ypos);
+
+ rst = ERROR;
+
+ Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ if (Regs != NULL)
+ {
+ struct st_motormove mymotor;
+ struct st_motorpos mtrpos;
+
+ bzero (&mymotor, sizeof (struct st_motormove));
+ memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ if (speed < dev->motormove_count)
+ memcpy (&mymotor, dev->motormove[speed],
+ sizeof (struct st_motormove));
+
+ /*83fe */
+ mtrpos.coord_y = ypos;
+ mtrpos.options =
+ MTR_ENABLED | ((direction == MTR_BACKWARD) ? MTR_BACKWARD :
+ MTR_FORWARD);
+ mtrpos.v12e448 = 0;
+ mtrpos.v12e44c = 1;
+
+ Motor_Move (dev, Regs, &mymotor, &mtrpos);
+
+ /* waits 15 seconds */
+ RTS_WaitScanEnd (dev, 15000);
+
+ free (Regs);
+ rst = OK;
+ }
+
+ DBG (DBG_FNC, "- Head_Relocate: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Calib_CreateFixedBuffers ()
+{
+ SANE_Byte channel;
+ SANE_Int ret;
+
+ DBG (DBG_FNC, "> Calib_CreateFixedBuffers()\n");
+
+ ret = OK;
+ channel = 0;
+
+ while ((channel < 3) && (ret == OK))
+ {
+ /* First table */
+ if (fixed_black_shading[channel] == NULL)
+ fixed_black_shading[channel] =
+ (USHORT *) malloc (0x7f8 * sizeof (USHORT));
+
+ if (fixed_black_shading[channel] != NULL)
+ bzero (fixed_black_shading[channel], 0x7f8 * sizeof (USHORT));
+ else
+ ret = ERROR;
+
+ /* Second table */
+ if (fixed_white_shading[channel] == NULL)
+ fixed_white_shading[channel] =
+ (USHORT *) malloc (0x7f8 * sizeof (USHORT));
+
+ if (fixed_white_shading[channel] != NULL)
+ bzero (fixed_white_shading[channel], 0x7f8 * sizeof (USHORT));
+ else
+ ret = ERROR;
+
+ channel++;
+ }
+
+ return ret;
+}
+
+static SANE_Int
+Calib_CreateBuffers (struct st_device *dev, struct st_calibration *buffer,
+ SANE_Int my14b4)
+{
+ SANE_Int ebp, ret, channel;
+
+ ret = ERROR;
+ dev = dev;
+
+ buffer->shadinglength = scan.coord.width;
+ ebp = 0x14;
+
+ if (my14b4 != 0)
+ {
+ /* 673d */
+ if (Calib_CreateFixedBuffers () == OK)
+ {
+ for (channel = 0; channel < 3; channel++)
+ {
+ buffer->white_shading[channel] = fixed_white_shading[channel];
+ buffer->black_shading[channel] = fixed_black_shading[channel];
+ }
+ ret = OK;
+ }
+ }
+ else
+ {
+ /* 677f */
+ SANE_Int pos;
+ channel = 0;
+ while ((channel < 3) && (ret == OK))
+ {
+ buffer->black_shading[channel] =
+ (USHORT *) malloc (ebp +
+ (buffer->shadinglength * sizeof (USHORT)));
+ buffer->white_shading[channel] =
+ (USHORT *) malloc (ebp +
+ (buffer->shadinglength * sizeof (USHORT)));
+ if ((buffer->black_shading[channel] != NULL)
+ && (buffer->white_shading[channel] != NULL))
+ {
+ for (pos = 0; pos < buffer->shadinglength; pos++)
+ {
+ buffer->black_shading[channel][pos] = 0x00;
+ buffer->white_shading[channel][pos] = 0x4000;
+ }
+ ret = OK;
+ }
+ else
+ Calib_FreeBuffers (buffer);
+
+ channel++;
+ }
+ }
+
+ DBG (DBG_FNC, "> Calib_CreateBuffers: *buffer, my14b4=%i): %i\n", my14b4,
+ ret);
+
+ return ret;
+}
+
+static void
+Calib_FreeBuffers (struct st_calibration *caltables)
+{
+ DBG (DBG_FNC, "> Calib_FreeBuffers(*caltables)\n");
+
+ if (caltables != NULL)
+ {
+ SANE_Int channel;
+
+ for (channel = 0; channel < 3; channel++)
+ {
+ if (caltables->black_shading[channel] != NULL)
+ {
+ free (caltables->black_shading[channel]);
+ caltables->black_shading[channel] = NULL;
+ }
+
+ if (caltables->white_shading[channel] != NULL)
+ {
+ free (caltables->white_shading[channel]);
+ caltables->white_shading[channel] = NULL;
+ }
+ }
+ }
+}
+
+static SANE_Int
+Calib_LoadConfig (struct st_device *dev,
+ struct st_calibration_config *calibcfg, SANE_Int scantype,
+ SANE_Int resolution, SANE_Int bitmode)
+{
+ SANE_Int section, a;
+ struct st_autoref refcfg;
+
+ DBG (DBG_FNC,
+ "> Calib_LoadConfig(*calibcfg, scantype=%s, resolution=%i, bitmode=%i)\n",
+ dbg_scantype (scantype), resolution, bitmode);
+
+ switch (scantype)
+ {
+ case ST_NEG:
+ section = CALIBNEGATIVEFILM;
+ break;
+ case ST_TA:
+ section = CALIBTRANSPARENT;
+ break;
+ default:
+ section = CALIBREFLECTIVE;
+ break;
+ }
+
+ calibcfg->WStripXPos = get_value (section, WSTRIPXPOS, 0, FITCALIBRATE);
+ calibcfg->WStripYPos = get_value (section, WSTRIPYPOS, 0, FITCALIBRATE);
+ calibcfg->BStripXPos = get_value (section, BSTRIPXPOS, 0, FITCALIBRATE);
+ calibcfg->BStripYPos = get_value (section, WSTRIPYPOS, 0, FITCALIBRATE);
+
+ /* get calibration wrefs */
+ cfg_wrefs_get (dev->sensorcfg->type, bitmode, resolution, scantype,
+ &calibcfg->WRef[CL_RED], &calibcfg->WRef[CL_GREEN],
+ &calibcfg->WRef[CL_BLUE]);
+
+ /* 4913 */
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ WRef[a] = _B0 (calibcfg->WRef[a]);
+
+ calibcfg->BRef[a] = get_value (section, BREFR + a, 10, FITCALIBRATE);
+ calibcfg->OffsetEven1[a] =
+ get_value (section, OFFSETEVEN1R + a, 256, FITCALIBRATE);
+ calibcfg->OffsetEven2[a] =
+ get_value (section, OFFSETEVEN2R + a, 0, FITCALIBRATE);
+ calibcfg->OffsetOdd1[a] =
+ get_value (section, OFFSETODD1R + a, 256, FITCALIBRATE);
+ calibcfg->OffsetOdd2[a] =
+ get_value (section, OFFSETODD2R + a, 0, FITCALIBRATE);
+ }
+
+ calibcfg->RefBitDepth =
+ _B0 (get_value (section, REFBITDEPTH, 8, FITCALIBRATE));
+ calibcfg->CalibOffset10n =
+ _B0 (get_value (section, CALIBOFFSET10N, 3, FITCALIBRATE));
+ calibcfg->CalibOffset20n =
+ _B0 (get_value (section, CALIBOFFSET20N, 0, FITCALIBRATE));
+ calibcfg->OffsetHeight =
+ get_value (section, OFFSETHEIGHT, 10, FITCALIBRATE);
+
+ /* 4ae9 */
+
+ /* get left coordinate and length to calibrate offset */
+ cfg_offset_get (dev->sensorcfg->type, resolution, scantype,
+ &calibcfg->OffsetPixelStart, &calibcfg->OffsetNPixel);
+
+ /*4c49 */
+ calibcfg->OffsetNSigma = get_value (section, OFFSETNSIGMA, 2, FITCALIBRATE);
+ calibcfg->OffsetTargetMax =
+ get_value (section, OFFSETTARGETMAX, 0x32, FITCALIBRATE) * 0.01;
+ calibcfg->OffsetTargetMin =
+ get_value (section, OFFSETTARGETMIN, 2, FITCALIBRATE) * 0.01;
+ calibcfg->OffsetBoundaryRatio1 =
+ get_value (section, OFFSETBOUNDARYRATIO1, 0x64, FITCALIBRATE) * 0.01;
+ calibcfg->OffsetBoundaryRatio2 =
+ get_value (section, OFFSETBOUNDARYRATIO2, 0x64, FITCALIBRATE) * 0.01;
+
+ calibcfg->OffsetAvgRatio1 =
+ get_value (section, OFFSETAVGRATIO1, 0x64, FITCALIBRATE) * 0.01;
+ calibcfg->OffsetAvgRatio2 =
+ get_value (section, OFFSETAVGRATIO2, 0x64, FITCALIBRATE) * 0.01;
+ calibcfg->AdcOffQuickWay =
+ get_value (section, ADCOFFQUICKWAY, 1, FITCALIBRATE);
+ calibcfg->AdcOffPredictStart =
+ get_value (section, ADCOFFPREDICTSTART, 0xc8, FITCALIBRATE);
+ calibcfg->AdcOffPredictEnd =
+ get_value (section, ADCOFFPREDICTEND, 0x1f4, FITCALIBRATE);
+ calibcfg->AdcOffEvenOdd =
+ get_value (section, ADCOFFEVENODD, 1, FITCALIBRATE);
+ calibcfg->OffsetTuneStep1 =
+ _B0 (get_value (section, OFFSETTUNESTEP1, 1, FITCALIBRATE));
+ calibcfg->OffsetTuneStep2 =
+ _B0 (get_value (section, OFFSETTUNESTEP2, 1, FITCALIBRATE));
+ calibcfg->CalibGain10n = get_value (section, CALIBGAIN10N, 1, FITCALIBRATE);
+ calibcfg->CalibGain20n = get_value (section, CALIBGAIN20N, 0, FITCALIBRATE);
+ calibcfg->CalibPAGOn = get_value (section, CALIBPAGON, 0, FITCALIBRATE);
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ calibcfg->OffsetAvgTarget[a] =
+ _B0 (get_value (section, OFFSETAVGTARGETR + a, 0x0d, FITCALIBRATE));
+ calibcfg->PAG[a] = get_value (section, PAGR + a, 3, FITCALIBRATE);
+ calibcfg->Gain1[a] = get_value (section, GAIN1R + a, 4, FITCALIBRATE);
+ calibcfg->Gain2[a] = get_value (section, GAIN2R + a, 4, FITCALIBRATE);
+ calibcfg->WShadingPreDiff[a] =
+ get_value (section, WSHADINGPREDIFFR + a, -1, FITCALIBRATE);
+ calibcfg->BShadingPreDiff[a] =
+ get_value (section, BSHADINGPREDIFFR + a, 2, FITCALIBRATE);
+ }
+
+ calibcfg->GainHeight = get_value (section, GAINHEIGHT, 0x1e, FITCALIBRATE);
+ calibcfg->GainTargetFactor =
+ get_value (section, GAINTARGETFACTOR, 0x5a, FITCALIBRATE) * 0.01;
+ calibcfg->TotShading = get_value (section, TOTSHADING, 0, FITCALIBRATE);
+
+ /* White shading */
+ calibcfg->WShadingOn = get_value (section, WSHADINGON, 3, FITCALIBRATE);
+ calibcfg->WShadingHeight =
+ get_value (section, WSHADINGHEIGHT, 0x18, FITCALIBRATE);
+
+ /* Black shading */
+ calibcfg->BShadingOn = get_value (section, BSHADINGON, 2, FITCALIBRATE);
+ calibcfg->BShadingHeight =
+ get_value (section, BSHADINGHEIGHT, 0x1e, FITCALIBRATE);
+
+ calibcfg->BShadingDefCutOff =
+ get_value (section, BSHADINGDEFCUTOFF, 0, FITCALIBRATE);
+
+ refcfg.extern_boundary = 0;
+ cfg_autoref_get (&refcfg);
+ calibcfg->ExternBoundary = refcfg.extern_boundary * 0.01;
+
+ calibcfg->EffectivePixel =
+ cfg_effectivepixel_get (dev->sensorcfg->type, resolution);
+
+ return OK;
+}
+
+static SANE_Int
+Calib_AdcGain (struct st_device *dev, struct st_calibration_config *calibcfg,
+ SANE_Int arg2, SANE_Int gaincontrol)
+{
+ /*
+ 0606F8E0 04F60738 |Arg1 = 04F60738
+ 0606F8E4 0606F90C |Arg2 = 0606F90C calibcfg
+ 0606F8E8 00000001 |Arg3 = 00000001 arg2
+ 0606F8EC 00000001 \Arg4 = 00000001 gaincontrol
+ */
+
+ SANE_Int rst = ERROR;
+ SANE_Byte *myRegs; /*f1c0 */
+
+ DBG (DBG_FNC, "+ Calib_AdcGain(*calibcfg, arg2=%i, gaincontrol=%i)\n", arg2,
+ gaincontrol);
+
+ myRegs = (SANE_Byte *) malloc (sizeof (SANE_Byte) * RT_BUFFER_LEN);
+ if (myRegs != NULL)
+ {
+ struct st_scanparams *scancfg; /*f17c */
+ SANE_Int bytes_per_line, bytes_to_next_colour, bytes_per_pixel;
+
+ /* get register values to perform adc gain calibration */
+ memcpy (myRegs, &calibdata->Regs, sizeof (SANE_Byte) * RT_BUFFER_LEN);
+
+ scancfg =
+ (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ if (scancfg != NULL)
+ {
+ SANE_Byte *image, *pgain, *pcalgain;
+
+ /* get proper scan configuration */
+ memcpy (scancfg, &calibdata->scancfg,
+ sizeof (struct st_scanparams));
+
+ /* set gain control type */
+ Lamp_SetGainMode (dev, myRegs, scancfg->resolution_x, gaincontrol);
+
+ /* 8-bit depth */
+ scancfg->depth = 8;
+
+ /* set coordinates */
+ if ((scan.scantype > 0) && (scan.scantype < 4))
+ scancfg->coord.left += scan.ser;
+
+ if ((scancfg->coord.width & 1) == 0)
+ scancfg->coord.width++;
+
+ scancfg->coord.top = 1;
+ scancfg->coord.height = calibcfg->OffsetHeight;
+
+ /* three more values to read image data after getting image from scanner */
+ switch (scancfg->colormode)
+ {
+ case CM_GRAY:
+ case CM_LINEART:
+ bytes_to_next_colour = 0;
+ bytes_per_pixel = 1;
+ bytes_per_line = scancfg->coord.width;
+ break;
+ default: /* CM_COLOR */
+ /* c027 */
+ bytes_to_next_colour = 1;
+ bytes_per_line = scancfg->coord.width * 3;
+ if (scancfg->samplerate == LINE_RATE)
+ {
+ bytes_to_next_colour = scancfg->coord.width;
+ bytes_per_pixel = 1;
+ }
+ else
+ bytes_per_pixel = 3;
+ break;
+ }
+
+ /*7fc7 */
+ scancfg->v157c = bytes_per_line;
+ scancfg->bytesperline = bytes_per_line;
+
+ /* select type of gain parameters to set */
+ if (arg2 != 0)
+ {
+ pgain = calibdata->gain_offset.vgag1;
+ pcalgain = calibcfg->Gain1;
+ }
+ else
+ {
+ /*7ff2 */
+ pgain = calibdata->gain_offset.vgag2;
+ pcalgain = calibcfg->Gain2;
+ }
+
+ /*8002 */
+ /* Allocate space for image | size = 132912 */
+ image =
+ (SANE_Byte *) malloc (sizeof (SANE_Byte) *
+ ((scancfg->coord.height +
+ 16) * bytes_per_line));
+ if (image != NULL)
+ {
+ /* Lets read image */
+ if (RTS_GetImage
+ (dev, myRegs, scancfg, &calibdata->gain_offset, image, NULL,
+ OP_STATIC_HEAD, gaincontrol) == OK)
+ {
+ SANE_Int a;
+ SANE_Int vmin[3], vmax[3];
+ double dval[3] = { 0.0 }; /*f1a8 f1b0 f1b8 */
+ SANE_Byte *pimage = image;
+
+ /* initialize values */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ calibcfg->unk1[a] = 0;
+ calibcfg->unk2[a] = 0xff;
+
+ vmin[a] = 0xff;
+ vmax[a] = 0;
+ }
+
+ /* process image data */
+ if (scancfg->coord.width > 0)
+ {
+ /*8104 */
+ SANE_Int pos, myheight /*f164 */ ;
+ SANE_Int chn_sum[3];
+
+ for (pos = scancfg->coord.width; pos > 0; pos--)
+ {
+ chn_sum[CL_RED] = chn_sum[CL_GREEN] =
+ chn_sum[CL_BLUE] = 0;
+
+ if (scancfg->coord.height > 0)
+ for (myheight = 0;
+ myheight < scancfg->coord.height; myheight++)
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ chn_sum[a] +=
+ *(pimage + (bytes_per_line * myheight) +
+ (bytes_to_next_colour * a));
+
+ /*816e */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ vmin[a] =
+ min (vmin[a],
+ chn_sum[a] / scancfg->coord.height);
+ vmax[a] =
+ max (vmax[a],
+ chn_sum[a] / scancfg->coord.height);
+
+ calibcfg->unk1[a] =
+ max (calibcfg->unk1[a], vmax[a]);
+ calibcfg->unk2[a] =
+ min (calibcfg->unk1[a], vmin[a]);
+
+ dval[a] += vmax[a] & 0xffff;
+ }
+
+ pimage += bytes_per_pixel;
+ }
+ }
+
+ /*82b0 */
+ dval[CL_RED] /= scancfg->coord.width;
+ dval[CL_GREEN] /= scancfg->coord.width;
+ dval[CL_BLUE] /= scancfg->coord.width;
+
+ DBG (DBG_FNC, " -> adcgain (av/l): r=%f, g=%f, b=%f\n",
+ dval[CL_RED], dval[CL_GREEN], dval[CL_BLUE]);
+ DBG (DBG_FNC, " -> (max ): R=%i, G=%i, B=%i\n",
+ calibcfg->unk1[CL_RED], calibcfg->unk1[CL_GREEN],
+ calibcfg->unk1[CL_BLUE]);
+ DBG (DBG_FNC, " -> (min ): r=%i, g=%i, b=%i\n",
+ calibcfg->unk2[CL_RED], calibcfg->unk2[CL_GREEN],
+ calibcfg->unk2[CL_BLUE]);
+
+ if (scancfg->colormode == CM_COLOR)
+ {
+ /*8353 */
+ double dvalue;
+ SANE_Int ival;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ dvalue =
+ ((((calibcfg->WRef[a] * (1 << scancfg->depth)) *
+ calibcfg->GainTargetFactor) * 0.00390625) /
+ dval[a]) * ((44 - pgain[a]) / 40);
+ if (dvalue > 0.9090909090909091)
+ {
+ /*83d7 */
+ dvalue = min (44 - (40 / dvalue), 31);
+ ival = dvalue;
+ pgain[a] = _B0 (ival);
+ pcalgain[a] = _B0 (ival);
+ }
+ else
+ {
+ pgain[a] = 0;
+ pcalgain[a] = 0;
+ }
+ }
+ }
+ else
+ {
+ /*843c */
+ /*falta codigo */
+ double dvalue;
+ SANE_Int ival;
+
+ dvalue =
+ ((44 -
+ pgain[CL_RED]) / 40) * ((((1 << scancfg->depth) *
+ calibcfg->WRef[scancfg->
+ channel]) *
+ 0.9) * 0.00390625) /
+ 17.08509389671362;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ if (dvalue > 0.9090909090909091)
+ {
+ dvalue = min (44 - (40 / dvalue), 31);
+ ival = dvalue;
+ pgain[a] = _B0 (ival);
+ pcalgain[a] = _B0 (ival);
+ }
+ else
+ {
+ /*84e3 */
+ pgain[a] = 0;
+ pcalgain[a] = 0;
+ }
+ }
+ }
+
+ /*84fa */
+ /* Save buffer */
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ {
+ dbg_tiff_save ("adcgain.tiff",
+ scancfg->coord.width,
+ scancfg->coord.height,
+ scancfg->depth,
+ CM_COLOR,
+ scancfg->resolution_x,
+ scancfg->resolution_y,
+ image,
+ (scancfg->coord.height +
+ 16) * bytes_per_line);
+ }
+
+ /* check if peak values are above offset average target + 5 */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ if (calibcfg->unk1[a] >= calibcfg->OffsetAvgTarget[a] + 5)
+ {
+ rst = OK;
+ break;
+ }
+ }
+
+ free (image);
+ }
+
+ free (scancfg);
+ }
+
+ free (myRegs);
+ }
+
+ /* v14b8 = (rst == OK)? 0: 1; */
+
+ /* show */
+ dbg_calibtable (&calibdata->gain_offset);
+
+ DBG (DBG_FNC, "- Calib_AdcGain: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+GainOffset_Save (struct st_device *dev, SANE_Int * offset, SANE_Byte * gain)
+{
+ SANE_Int rst = OK;
+
+ DBG (DBG_FNC, "+ GainOffset_Save(*offset, *gain):\n");
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ if ((offset != NULL) && (gain != NULL))
+ {
+ SANE_Int a, crc, value;
+
+ crc = 0x5B;
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ value = (*gain << 9) | *offset;
+ crc = _B0 (abs (crc - _B0 (value)));
+ rst =
+ RTS_EEPROM_WriteWord (dev->usb_handle, 0x70 + (a * 2), value);
+ }
+
+ if (rst == OK)
+ rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x76, crc);
+ }
+ else
+ rst = ERROR;
+ }
+
+ DBG (DBG_FNC, "- GainOffset_Save: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+Calib_PAGain (struct st_device *dev, struct st_calibration_config *calibcfg,
+ SANE_Int gainmode)
+{
+ SANE_Byte *Regs;
+ struct st_scanparams *scancfg;
+ SANE_Int channel_size;
+ SANE_Int bytes_to_next_colour = 0;
+ SANE_Int bytes_per_pixel = 0;
+ SANE_Int length = 0;
+ SANE_Byte *image;
+ double rst;
+ SANE_Int ret = ERROR;
+
+ DBG (DBG_FNC, "+ Calib_PAGain(*calibcfg, gainmode=%i)\n", gainmode);
+
+ Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ if (Regs != NULL)
+ {
+ scancfg =
+ (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ if (scancfg != NULL)
+ {
+ memcpy (Regs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (scancfg, &calibdata->scancfg,
+ sizeof (struct st_scanparams));
+
+ if (scan.scantype == ST_NORMAL)
+ {
+ /* bfa5 */
+ scancfg->coord.left = scan.ser;
+ scancfg->coord.width = (scancfg->sensorresolution * 17) / 2;
+ }
+ else
+ {
+ scancfg->coord.left = scan.ser + v0750;
+ scancfg->coord.width = (scancfg->sensorresolution * 3) / 2;
+ }
+
+ /* bfca */
+ if ((scancfg->coord.width & 1) == 1)
+ scancfg->coord.width++;
+
+ scancfg->coord.top = 1;
+ scancfg->coord.height = calibcfg->OffsetHeight;
+
+ channel_size = (scancfg->depth > 8) ? 2 : 1;
+
+ switch (scancfg->colormode)
+ {
+ case CM_GRAY:
+ case CM_LINEART:
+ bytes_to_next_colour = 0;
+ bytes_per_pixel = 1;
+ length = channel_size * scancfg->coord.width;
+ break;
+ default: /* CM_COLOR */
+ /* c027 */
+ bytes_to_next_colour = 1;
+ length = (channel_size * scancfg->coord.width) * 3;
+ if (scancfg->samplerate == LINE_RATE)
+ {
+ bytes_to_next_colour = scancfg->coord.width;
+ bytes_per_pixel = 1;
+ }
+ else
+ bytes_per_pixel = 3;
+ break;
+ }
+
+ /* c070 */
+ scancfg->v157c = length;
+
+ image =
+ (SANE_Byte *) malloc ((scancfg->coord.height * length) *
+ sizeof (SANE_Byte));
+ if (image != NULL)
+ {
+ ret =
+ RTS_GetImage (dev, Regs, scancfg, &calibdata->gain_offset,
+ image, 0, OP_STATIC_HEAD, gainmode);
+ if (ret == OK)
+ {
+ /* 429c105 */
+ SANE_Int a;
+ SANE_Byte *ptr[3];
+ SANE_Int vmin[3] = { 255, 255, 255 }; /* f16c|f16e|f170 */
+ SANE_Int vmax[3] = { 0, 0, 0 }; /* f164|f166|f168 */
+ SANE_Int total[3];
+
+ ptr[CL_RED] = image;
+ ptr[CL_GREEN] = image + bytes_to_next_colour;
+ ptr[CL_BLUE] = image + (bytes_to_next_colour * 2);
+
+ if (scancfg->coord.width > 0)
+ {
+ SANE_Int pos, b;
+
+ for (pos = 0; pos < scancfg->coord.width; pos++)
+ {
+ total[CL_BLUE] = 0;
+ total[CL_GREEN] = 0;
+ total[CL_RED] = 0;
+
+ for (a = 0; a < scancfg->coord.height; a++)
+ {
+ for (b = CL_RED; b <= CL_BLUE; b++)
+ total[b] +=
+ *(ptr[b] +
+ ((scancfg->coord.height - a) * length));
+ }
+
+ /* c1a5 */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ total[a] /= scancfg->coord.height;
+ vmin[a] = min (vmin[a], total[a]);
+ vmax[a] = max (vmax[a], total[a]);
+
+ ptr[a] += bytes_per_pixel;
+ }
+ }
+ }
+
+ /* 429c234 */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ rst =
+ (calibcfg->WRef[a] * calibcfg->GainTargetFactor) /
+ vmax[a];
+ if (rst <= 1.5)
+ {
+ if (rst <= 1.286)
+ {
+ if (rst <= 1.125)
+ calibdata->gain_offset.pag[a] = 0;
+ else
+ calibdata->gain_offset.pag[a] = 1;
+ }
+ else
+ calibdata->gain_offset.pag[a] = 2;
+ }
+ else
+ calibdata->gain_offset.pag[a] = 3;
+ }
+ }
+ free (image);
+ }
+ free (scancfg);
+ }
+ free (Regs);
+ }
+
+ DBG (DBG_FNC, "- Calib_PAGain: %i\n", ret);
+
+ return ret;
+}
+
+static SANE_Int
+Chipset_ID (struct st_device *dev)
+{
+ SANE_Int ret;
+
+ if (Read_Word (dev->usb_handle, 0xfe3c, &ret) == OK)
+ ret = _B0 (ret);
+ else
+ ret = 0;
+
+ DBG (DBG_FNC, "> Chipset_ID(): %i\n", ret);
+
+ return ret;
+}
+
+static SANE_Int
+Chipset_Name (struct st_device *dev, char *name, SANE_Int size)
+{
+ SANE_Int rst = ERROR;
+
+ if (name != NULL)
+ {
+ strncpy (name, dev->chipset->name, size);
+ rst = OK;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+Refs_Load (struct st_device *dev, SANE_Int * x, SANE_Int * y)
+{
+ SANE_Int ret = OK;
+
+ DBG (DBG_FNC, "+ Refs_Load:\n");
+
+ *y = *x = 0;
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ SANE_Int data;
+
+ ret = ERROR;
+
+ if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x6a, &data) == OK)
+ {
+ *x = data;
+ if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x6c, &data) == OK)
+ {
+ *y = data;
+ if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x6e, &data) == OK)
+ {
+ if ((_B0 (*y + *x + data)) == 0x5a)
+ ret = OK;
+ }
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- Refs_Load(y=%i, x=%i) : %i\n", *y, *x, ret);
+
+ return ret;
+}
+
+static SANE_Int
+Refs_Save (struct st_device *dev, SANE_Int left_leading, SANE_Int start_pos)
+{
+ SANE_Int ret = OK;
+
+ DBG (DBG_FNC, "+ Refs_Save(left_leading=%i, start_pos=%i)\n", left_leading,
+ start_pos);
+
+ /* check if chipset supports accessing eeprom */
+ if ((dev->chipset->capabilities & CAP_EEPROM) != 0)
+ {
+ ret = ERROR;
+
+ if (RTS_EEPROM_WriteWord (dev->usb_handle, 0x6a, left_leading) == OK)
+ {
+ if (RTS_EEPROM_WriteWord (dev->usb_handle, 0x6c, start_pos) == OK)
+ {
+ SANE_Byte data = _B0 (0x5a - (start_pos + left_leading));
+ ret = RTS_EEPROM_WriteByte (dev->usb_handle, 0x6e, data);
+ }
+ }
+ }
+
+ DBG (DBG_FNC, "- Refs_Save: %i\n", ret);
+
+ return ret;
+}
+
+static SANE_Int
+Calib_AdcOffsetRT (struct st_device *dev,
+ struct st_calibration_config *calibcfg, SANE_Int value)
+{
+/*
+05EFF8E4 04F10738 |Arg1 = 04F10738
+05EFF8E8 05EFF90C |Arg2 = 05EFF90C calibcfg
+05EFF8EC 00000001 \Arg3 = 00000001 value
+*/
+ SANE_Byte Regs[RT_BUFFER_LEN]; /*f1c4 */
+ SANE_Int channels_per_dot; /*f108 */
+ struct st_scanparams scancfg; /*f18c */
+ SANE_Int *pedcg; /*f114 */
+ SANE_Int *podcg; /*f118 */
+ SANE_Int *poffseteven; /*f130 */
+ SANE_Int *poffsetodd; /*f128 */
+ SANE_Int channel;
+ SANE_Int avgtarget[3]; /*f1b8 f1bc f1c0 */
+ SANE_Byte *scanbuffer; /*f0f8 */
+ SANE_Int scan_options; /*f12c */
+ SANE_Int highresolution; /*f144 */
+ double dbValues[3] = { 0, 0, 0 }; /*f148 f14c f150 */
+ SANE_Int do_loop; /*f0ec */
+ SANE_Int gainmode;
+ SANE_Byte *ptr; /*f0f4 */
+ SANE_Byte *mvgag; /*f0e4 *//*f10c */
+ USHORT wvalues[9]; /*0856 0858 085a 085c 085e 0860 0862 0864 0866 */
+ SANE_Int imgcount = 0;
+ /* myoffsetnpixel = f120 */
+ /* desp f0e8 & f140 */
+
+ DBG (DBG_FNC, "+ Calib_AdcOffsetRT(*calibcfg, value=%i)\n", value);
+
+ memcpy (&Regs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (&scancfg, &calibdata->scancfg, sizeof (struct st_scanparams));
+
+ channels_per_dot = (calibdata->scancfg.colormode == CM_COLOR) ? 3 : 1;
+
+ if (value != 0)
+ {
+ pedcg = &calibdata->gain_offset.edcg1[CL_RED];
+ podcg = &calibdata->gain_offset.odcg1[CL_RED];
+ poffseteven = &calibcfg->OffsetEven1[CL_RED];
+ poffsetodd = &calibcfg->OffsetOdd1[CL_RED];
+ }
+ else
+ {
+ /*c37c */
+ pedcg = &calibdata->gain_offset.edcg2[CL_RED];
+ podcg = &calibdata->gain_offset.odcg2[CL_RED];
+ poffseteven = &calibcfg->OffsetEven2[CL_RED];
+ poffsetodd = &calibcfg->OffsetOdd2[CL_RED];
+ }
+
+ /*c3a4 */
+ scancfg.coord.left = calibcfg->OffsetPixelStart;
+
+ if (channels_per_dot > 0)
+ {
+ for (channel = 0; channel < channels_per_dot; channel++)
+ {
+ avgtarget[channel] = calibcfg->OffsetAvgTarget[channel] << 8;
+ if (avgtarget[channel] == 0)
+ avgtarget[channel] = 0x80;
+ }
+ }
+
+ /* set image coordinates to scan */
+ scancfg.coord.width = calibcfg->OffsetNPixel;
+ if ((scancfg.coord.width & 1) == 0)
+ scancfg.coord.width++;
+
+ scancfg.bytesperline = channels_per_dot * scancfg.coord.width;
+ scancfg.coord.top = 1;
+ scancfg.coord.height = calibcfg->OffsetHeight;
+ scancfg.depth = 8;
+
+ /* allocate memory to store image */
+ scanbuffer =
+ (SANE_Byte *) malloc ((scancfg.bytesperline * calibcfg->OffsetHeight) *
+ sizeof (SANE_Byte));
+ if (scanbuffer == NULL)
+ return ERROR;
+
+ /*42ac477 */
+ scan_options = (linedarlampoff == 1) ? 1 : 0x101;
+ highresolution = (scancfg.sensorresolution >= 1200) ? TRUE : FALSE;
+
+ do
+ {
+ if (channels_per_dot > 0)
+ {
+ for (channel = 0; channel < channels_per_dot; channel++)
+ dbValues[channel] =
+ (40 / (44 - calibdata->gain_offset.vgag2[channel])) * (40 /
+ (44 -
+ calibdata->
+ gain_offset.
+ vgag1
+ [channel]));
+ }
+
+ /*429c50f */
+ /* Get Image */
+ gainmode = Lamp_GetGainMode (dev, scancfg.resolution_x, scan.scantype);
+ if (RTS_GetImage
+ (dev, Regs, &scancfg, &calibdata->gain_offset, scanbuffer, 0,
+ scan_options, gainmode) != OK)
+ {
+ free (scanbuffer);
+ return ERROR;
+ }
+
+ /*429c55f */
+ /* Save retrieved image */
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ {
+ char fname[30];
+
+ imgcount++;
+ if (snprintf (fname, 30, "adcoffset_rt%i.tiff", imgcount) > 0)
+ dbg_tiff_save (fname,
+ scancfg.coord.width,
+ scancfg.coord.height,
+ scancfg.depth,
+ CM_COLOR,
+ scancfg.resolution_x,
+ scancfg.resolution_y,
+ scanbuffer,
+ scancfg.bytesperline * calibcfg->OffsetHeight);
+ }
+
+ /*429c5a5 */
+ do_loop = FALSE;
+
+ if (highresolution == TRUE)
+ {
+ /* f0fc = f0e4 = channel */
+ SANE_Int lf104;
+ SANE_Int *mydcg; /*f0f4 */
+ USHORT *mywvalue; /*ebp */
+
+ SANE_Byte is_ready[6]; /*f174 f178 f17c f180 f184 f18c */
+ SANE_Byte *ptr_ready; /*f11c */
+ SANE_Int colour;
+
+ for (channel = 0; channel < 6; channel++)
+ is_ready[channel] = FALSE;
+
+ if (channels_per_dot <= 0)
+ break;
+
+ ptr = scanbuffer;
+ mvgag = (SANE_Byte *) calibdata->gain_offset.vgag1;
+
+ for (channel = 0; channel < channels_per_dot; channel++)
+ {
+ for (lf104 = 0; lf104 < 2; lf104++)
+ {
+ if (lf104 == 0)
+ {
+ mywvalue = &wvalues[channel];
+ mydcg = pedcg;
+ ptr_ready = &is_ready[0];
+ }
+ else
+ {
+ /*1645 */
+ mywvalue = &wvalues[3];
+ mydcg = podcg;
+ ptr_ready = &is_ready[3];
+ }
+
+ /*1658 */
+ if (ptr_ready[channel] == FALSE)
+ {
+ colour = 0;
+ if (lf104 < calibcfg->OffsetNPixel)
+ {
+ SANE_Int dot;
+
+ for (dot = 0;
+ dot < (calibcfg->OffsetNPixel - lf104 + 1) / 2;
+ dot++)
+ colour +=
+ scanbuffer[mvgag[(lf104 * channels_per_dot)] +
+ (dot * (channels_per_dot * 2))];
+ }
+
+ /*c6b2 */
+ colour = colour << 8;
+ if (colour == 0)
+ {
+ /*c6b9 */
+ if (mydcg[channel] != 0x1ff)
+ {
+ /*c6d5 */
+ mydcg[channel] = 0x1ff;
+ do_loop = TRUE;
+ }
+ else
+ ptr_ready[channel] = TRUE;
+ }
+ else
+ {
+ SANE_Int myesi;
+ SANE_Int d;
+
+ /*c6e8 */
+ if (*mywvalue == 0)
+ mywvalue += 2;
+
+ colour /= (calibcfg->OffsetNPixel / 2);
+ if (colour >= avgtarget[channel])
+ {
+ colour -= avgtarget[channel];
+ myesi = 0;
+ }
+ else
+ {
+ colour = avgtarget[channel] - colour;
+ myesi = 1;
+ }
+
+ d = mydcg[channel];
+ if (d < 0x100)
+ d = 0xff - d;
+
+ if (myesi != 0)
+ {
+ /*c76e */
+ if ((d + colour) > 0x1ff)
+ {
+ if (*mvgag > 0)
+ {
+ *mvgag = *mvgag - 1;
+ do_loop = TRUE;
+ }
+ else
+ ptr_ready[channel] = TRUE; /*c7a0 */
+ }
+ else
+ d += colour;
+ }
+ else
+ {
+ /*c7ad */
+ if (colour > d)
+ {
+ if (*mvgag > 0)
+ {
+ *mvgag = *mvgag - 1;
+ do_loop = TRUE;
+ }
+ else
+ ptr_ready[channel] = TRUE;
+ }
+ else
+ d -= colour;
+ }
+
+ /*c7dd */
+ mydcg[channel] = (d < 0x100) ? 0x100 - d : d;
+ }
+
+ dbg_calibtable (&calibdata->gain_offset);
+ }
+ }
+
+ /*c804 */
+ mvgag++;
+ }
+ }
+ else
+ {
+ /* Low resolution */
+
+ SANE_Byte is_ready[3];
+ SANE_Int colour;
+
+ /*429c845 */
+ for (channel = 0; channel < channels_per_dot; channel++)
+ is_ready[channel] = FALSE;
+
+ if (channels_per_dot <= 0)
+ break;
+
+ ptr = scanbuffer;
+ mvgag = (SANE_Byte *) calibdata->gain_offset.vgag1;
+
+ for (channel = 0; channel < channels_per_dot; channel++)
+ {
+ if (is_ready[channel] == FALSE)
+ {
+ colour = 0;
+ if (calibcfg->OffsetNPixel > 0)
+ {
+ SANE_Int dot;
+
+ /* Take one channel colour values from offsetnpixel pixels */
+ for (dot = 0; dot < calibcfg->OffsetNPixel; dot++)
+ colour += *(ptr + (dot * channels_per_dot));
+ }
+
+ colour <<= 8;
+ if (colour == 0)
+ {
+ if (pedcg[channel] != 0x1ff)
+ {
+ do_loop = TRUE;
+ podcg[channel] = 0x1ff;
+ pedcg[channel] = 0x1ff;
+ }
+ else
+ is_ready[channel] = TRUE;
+ }
+ else
+ {
+ /*c8f7 */
+ SANE_Int myesi;
+ SANE_Int op1, op2, op3;
+
+ /* Get colour average */
+ colour /= calibcfg->OffsetNPixel;
+
+ /* get absolute difference with avgtarget */
+ myesi = (colour > avgtarget[channel]) ? 0 : 1;
+ colour = abs (avgtarget[channel] - colour);
+
+ if (scancfg.resolution_x > 600)
+ {
+ /*c923 */
+ if (wvalues[channel + 3] == 0)
+ wvalues[channel + 3]++;
+
+ if (wvalues[channel] == 0)
+ wvalues[channel]++;
+
+ op3 = max (wvalues[channel], wvalues[channel + 3]);
+ }
+ else
+ {
+ if (wvalues[channel + 6] == 0)
+ wvalues[channel + 6]++;
+
+ op3 = wvalues[channel + 6];
+ }
+
+ /*c9d3 */
+ op1 = (SANE_Int) (colour / (dbValues[channel] * op3));
+ op2 =
+ (pedcg[channel] <
+ 0x100) ? pedcg[channel] - 0xff : pedcg[channel];
+
+ if (myesi != 0)
+ {
+ /*c9f5 */
+ if (((op2 + op1) & 0xffff) > 0x1ff)
+ {
+ if (*mvgag != 0)
+ {
+ do_loop = TRUE;
+ *mvgag = *mvgag - 1;
+ }
+ else
+ is_ready[channel] = TRUE;
+ }
+ else
+ op2 += op1;
+ }
+ else
+ {
+ /*ca31 */
+ if (op1 > op2)
+ {
+ if (*mvgag > 0)
+ {
+ do_loop = TRUE;
+ *mvgag = *mvgag - 1;
+ }
+ else
+ is_ready[channel] = TRUE;
+ }
+ else
+ op2 -= op1;
+ }
+
+ /*ca54 */
+ if (op2 < 0x100)
+ op2 = 0x100 - op2;
+
+ pedcg[channel] = op2;
+ podcg[channel] = op2;
+ }
+ }
+ /*ca6f */
+ ptr++;
+ mvgag++;
+ dbg_calibtable (&calibdata->gain_offset);
+ }
+ }
+ }
+ while (do_loop != FALSE);
+
+ /*429cad1 */
+ for (channel = 0; channel < 3; channel++)
+ {
+ poffseteven[channel] =
+ (pedcg[channel] < 0x100) ? 0xff - pedcg[channel] : pedcg[channel];
+ poffsetodd[channel] =
+ (podcg[channel] < 0x100) ? 0xff - podcg[channel] : podcg[channel];
+ }
+
+ free (scanbuffer);
+
+ return OK;
+}
+
+static void
+Calib_LoadCut (struct st_device *dev, struct st_scanparams *scancfg,
+ SANE_Int scantype, struct st_calibration_config *calibcfg)
+{
+ double mylong; /*ee78 */
+ double mylong2;
+ /**/ SANE_Int channel[3];
+ SANE_Int a;
+
+ cfg_shading_cut_get (dev->sensorcfg->type, scancfg->depth,
+ scancfg->resolution_x, scantype, &channel[0],
+ &channel[1], &channel[2]);
+
+ mylong = 1 << scancfg->depth;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ mylong2 = channel[a];
+ calibcfg->ShadingCut[a] = (mylong * mylong2) * 0.000390625;
+ }
+}
+
+static SANE_Int
+Calib_BWShading (struct st_calibration_config *calibcfg,
+ struct st_calibration *myCalib, SANE_Int gainmode)
+{
+ /*
+ 0603F8E4 0603F90C |Arg1 = 0603F90C calibcfg
+ 0603F8E8 0603FAAC |Arg2 = 0603FAAC myCalib
+ 0603F8EC 00000001 \Arg3 = 00000001 gainmode
+ */
+
+ /*falta codigo */
+
+ /*silence gcc */
+ calibcfg = calibcfg;
+ myCalib = myCalib;
+ gainmode = gainmode;
+
+ return OK;
+}
+
+static SANE_Int
+Calib_WhiteShading_3 (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ struct st_calibration *myCalib, SANE_Int gainmode)
+{
+/*
+05EDF8E0 04F00738 |Arg1 = 04F00738
+05EDF8E4 05EDF90C |Arg2 = 05EDF90C calibcfg
+05EDF8E8 05EDFAAC |Arg3 = 05EDFAAC myCalib
+05EDF8EC 00000001 \Arg4 = 00000001 gainmode
+*/
+ SANE_Byte *myRegs; /*f1bc */
+ struct st_scanparams scancfg; /*f170 */
+ SANE_Int myWidth; /*f14c */
+ SANE_Int lf168, bytes_per_pixel;
+ SANE_Int bytes_per_line;
+ /**/ SANE_Int a;
+ double lf1a4[3];
+ SANE_Int otherheight; /*f150 */
+ SANE_Int otherheight2;
+ SANE_Int lf12c;
+ SANE_Int lf130;
+ double *buffer1; /*f138 */
+ double *buffer2; /*f144 */
+ SANE_Byte *scanbuffer; /*f164 */
+ SANE_Byte *ptr; /*f148 */
+ SANE_Int position; /*f140 */
+ SANE_Int lf13c, myHeight;
+ SANE_Int myESI, myEDI;
+ SANE_Int channel; /*f134 */
+ double myst;
+ double sumatorio;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "> Calib_WhiteShading3(*calibcfg, *myCalib, gainmode=%i)\n",
+ gainmode);
+
+ myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (myRegs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (&scancfg, &calibdata->scancfg, sizeof (struct st_scanparams));
+
+ Lamp_SetGainMode (dev, myRegs, scancfg.resolution_x, gainmode);
+
+ rst = OK;
+ scancfg.resolution_y = 200;
+ switch (scan.scantype)
+ {
+ case ST_NORMAL:
+ /*a184 */
+ scancfg.coord.left += scan.ser;
+ scancfg.coord.width &= 0xffff;
+ break;
+ case ST_TA:
+ case ST_NEG:
+ scancfg.coord.left += scan.ser;
+ break;
+ }
+
+ /*a11b */
+ if ((scancfg.coord.width & 1) != 0)
+ scancfg.coord.width++;
+
+ scancfg.coord.top = 1;
+ scancfg.coord.height = calibcfg->WShadingHeight;
+
+ switch (scancfg.colormode)
+ {
+ case CM_GRAY:
+ case CM_LINEART:
+ myWidth = scancfg.coord.width;
+ lf168 = 0;
+ bytes_per_line = ((scancfg.depth + 7) / 8) * myWidth;
+ bytes_per_pixel = 1;
+ break;
+ default: /* CM_COLOR */
+ myWidth = scancfg.coord.width * 3;
+ bytes_per_line = ((scancfg.depth + 7) / 8) * myWidth;
+ lf168 = (scancfg.samplerate == LINE_RATE) ? scancfg.coord.width : 1;
+ bytes_per_pixel = (scancfg.samplerate == PIXEL_RATE) ? 3 : 1;
+ break;
+ }
+
+ /*a1e8 */
+ scancfg.v157c = bytes_per_line;
+ scancfg.bytesperline = bytes_per_line;
+
+ for (a = 0; a < 3; a++)
+ lf1a4[a] = (calibcfg->WRef[a] * (1 << scancfg.depth)) >> 8;
+
+ /* debug this code because if it's correct, lf130 and lf12c are always 2 */
+ otherheight = calibcfg->WShadingHeight - 3;
+ otherheight -= (otherheight - 4);
+ otherheight2 = otherheight / 2;
+ otherheight -= otherheight2;
+ lf130 = otherheight2;
+ lf12c = otherheight;
+
+ buffer1 = (double *) malloc (otherheight * sizeof (double));
+ if (buffer1 == NULL)
+ return ERROR;
+
+ buffer2 = (double *) malloc (otherheight * sizeof (double));
+ if (buffer2 == NULL)
+ {
+ free (buffer1);
+ return ERROR;
+ }
+
+ scanbuffer =
+ (SANE_Byte *) malloc (((scancfg.coord.height + 16) * bytes_per_line) *
+ sizeof (SANE_Byte));
+ if (scanbuffer == NULL)
+ {
+ free (buffer1);
+ free (buffer2);
+ return ERROR;
+ }
+
+ /* Scan image */
+ myCalib->shading_enabled = FALSE;
+ rst =
+ RTS_GetImage (dev, myRegs, &scancfg, &calibdata->gain_offset, scanbuffer,
+ myCalib, 0x20000080, gainmode);
+
+ for (a = 0; a < 3; a++)
+ myCalib->WRef[a] *= ((1 << scancfg.depth) >> 8);
+
+ if (rst == ERROR)
+ {
+ free (buffer1);
+ free (buffer2);
+ free (scanbuffer);
+ return ERROR;
+ }
+
+ if (scancfg.depth > 8)
+ {
+ /*a6d9 */
+ position = 0;
+ sumatorio = 0;
+ if (myWidth > 0)
+ {
+ do
+ {
+ switch (scancfg.colormode)
+ {
+ case CM_GRAY:
+ case CM_LINEART:
+ channel = 0;
+ lf13c = position;
+ break;
+ default: /*CM_COLOR */
+ if (scancfg.samplerate == PIXEL_RATE)
+ {
+ /* pixel rate */
+ channel = position % bytes_per_pixel;
+ lf13c = position / bytes_per_pixel;
+ }
+ else
+ {
+ /* line rate */
+ channel = position / lf168;
+ lf13c = position % lf168;
+ }
+ break;
+ }
+
+ /*a743 */
+ if (lf130 > 0)
+ bzero (buffer1, lf130 * sizeof (double));
+
+ /*a761 */
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ buffer2[a] = (1 << scancfg.depth) - 1.0;
+ }
+
+ /*a78f */
+ myESI = 0;
+ myEDI = 0;
+ ptr = scanbuffer + (position * 2);
+ myHeight = 0;
+
+ if (otherheight > 0)
+ {
+ do
+ {
+ myst = 0;
+ for (a = 0; a < 4; a++)
+ myst += data_lsb_get (ptr + (a * (myWidth * 2)), 2);
+
+ myEDI = 0;
+ myst = myst * 0.25;
+ if (myHeight < (otherheight - 4))
+ {
+ if (myst < buffer2[myESI])
+ {
+ buffer2[myESI] = myst;
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ if (buffer2[myESI] < buffer2[a])
+ myESI = a;
+ }
+ }
+
+ /*a820 */
+ if (myst >= buffer1[myEDI])
+ {
+ buffer1[myEDI] = myst;
+ if (lf130 > 0)
+ {
+ for (a = 0; a < lf130; a++)
+ if (buffer1[myEDI] >= buffer1[a])
+ myEDI = a;
+ }
+ }
+ sumatorio += myst;
+ }
+ else
+ {
+ /*a853 */
+ if (myHeight == (otherheight - 4))
+ {
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ if (buffer2[myESI] >= buffer2[a])
+ myESI = a;
+ }
+
+ if (lf130 > 0)
+ {
+ for (a = 0; a < lf130; a++)
+ if (buffer1[myEDI] < buffer1[a])
+ myEDI = a;
+ }
+ }
+
+ /*a895 */
+ if (myst >= buffer2[myESI])
+ {
+ /*a89c */
+ sumatorio -= buffer2[myESI];
+ sumatorio += myst;
+ buffer2[myESI] = myst;
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ if (buffer2[myESI] >= buffer2[a])
+ myESI = a;
+ }
+ }
+ else
+ {
+ if (myst < buffer1[myEDI])
+ {
+ sumatorio -= buffer1[myEDI];
+ sumatorio += myst;
+ buffer1[myEDI] = myst;
+
+ if (lf130 > 0)
+ {
+ for (a = 0; a < lf130; a++)
+ if (buffer1[myEDI] < buffer1[a])
+ myEDI = a;
+ }
+ }
+ }
+ }
+
+ /*a901 */
+ ptr += (myWidth * 2);
+ myHeight++;
+ }
+ while (myHeight < otherheight);
+ }
+
+ /*a924 */
+ scancfg.ser = 0;
+ scancfg.startpos = otherheight - 4;
+
+ sumatorio = sumatorio / scancfg.startpos;
+ if (myCalib->shading_enabled != FALSE)
+ {
+ /*a94a */
+ myCalib->white_shading[channel][lf13c] =
+ (unsigned short) sumatorio;
+ }
+ else
+ {
+ /*a967 */
+ if ((scancfg.colormode != CM_GRAY)
+ && (scancfg.colormode != CM_LINEART))
+ sumatorio /= lf1a4[channel];
+ else
+ sumatorio /= lf1a4[scancfg.channel];
+
+ sumatorio = min (sumatorio * 0x4000, 65535);
+
+ if (myRegs[0x1bf] != 0x18)
+ myCalib->black_shading[channel][lf13c] |=
+ (0x140 -
+ ((((myRegs[0x1bf] >> 3) & 3) *
+ 3) << 6)) & ((int) sumatorio);
+ else
+ myCalib->white_shading[channel][lf13c] =
+ (unsigned short) sumatorio;
+ }
+
+ /*a9fd */
+ position++;
+ }
+ while (position < myWidth);
+ }
+ }
+ else
+ {
+ /*a6d9 */
+ position = 0;
+ sumatorio = 0;
+ if (myWidth > 0)
+ {
+ do
+ {
+ switch (scancfg.colormode)
+ {
+ case CM_GRAY:
+ case CM_LINEART:
+ channel = 0;
+ lf13c = position;
+ break;
+ default: /*CM_COLOR */
+ if (scancfg.samplerate == PIXEL_RATE)
+ {
+ channel = position % bytes_per_pixel;
+ lf13c = position / bytes_per_pixel;
+ }
+ else
+ {
+ channel = position / lf168;
+ lf13c = position % lf168;
+ }
+ break;
+ }
+
+ /*a743 */
+ if (lf130 > 0)
+ bzero (buffer1, lf130 * sizeof (double));
+
+ /*a761 */
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ buffer2[a] = (1 << scancfg.depth) - 1.0;
+ }
+
+ /*a78f */
+ myESI = 0;
+ myEDI = 0;
+ ptr = scanbuffer + position;
+ myHeight = 0;
+
+ if (otherheight > 0)
+ {
+ do
+ {
+ myst = 0;
+ for (a = 0; a < 4; a++)
+ myst += *(ptr + (a * myWidth));
+
+ myEDI = 0;
+ myst *= 0.25;
+ if (myHeight < (otherheight - 4))
+ {
+ if (myst < buffer2[myESI])
+ {
+ buffer2[myESI] = myst;
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ if (buffer2[myESI] < buffer2[a])
+ myESI = a;
+ }
+ }
+ /*a820 */
+ if (myst >= buffer1[myEDI])
+ {
+ buffer1[myEDI] = myst;
+ if (lf130 > 0)
+ {
+ for (a = 0; a < lf130; a++)
+ if (buffer1[myEDI] >= buffer1[a])
+ myEDI = a;
+ }
+ }
+ sumatorio += myst;
+ }
+ else
+ {
+ /*a853 */
+ if (myHeight == (otherheight - 4))
+ {
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ if (buffer2[myESI] >= buffer2[a])
+ myESI = a;
+ }
+
+ if (lf130 > 0)
+ {
+ for (a = 0; a < lf130; a++)
+ if (buffer1[myEDI] < buffer1[a])
+ myEDI = a;
+ }
+ }
+
+ /*a895 */
+ if (myst >= buffer2[myESI])
+ {
+ /*a89c */
+ sumatorio -= buffer2[myESI];
+ sumatorio += myst;
+ buffer2[myESI] = myst;
+ if (lf12c > 0)
+ {
+ for (a = 0; a < lf12c; a++)
+ if (buffer2[myESI] >= buffer2[a])
+ myESI = a;
+ }
+ }
+ else
+ {
+ if (myst < buffer1[myEDI])
+ {
+ sumatorio -= buffer1[myEDI];
+ sumatorio += myst;
+ buffer1[myEDI] = myst;
+
+ if (lf130 > 0)
+ {
+ for (a = 0; a < lf130; a++)
+ if (buffer1[myEDI] < buffer1[a])
+ myEDI = a;
+ }
+ }
+ }
+ }
+ /*a901 */
+ ptr += myWidth;
+ myHeight++;
+ }
+ while (myHeight < otherheight);
+ }
+ /*a924 */
+ scancfg.ser = 0;
+ scancfg.startpos = otherheight - 4;
+
+ sumatorio /= scancfg.startpos;
+ if (myCalib->shading_enabled != FALSE)
+ {
+ /*a94a */
+ myCalib->white_shading[channel][lf13c] =
+ (unsigned short) sumatorio;
+ }
+ else
+ {
+ /*a967 */
+ if ((scancfg.colormode != CM_GRAY)
+ && (scancfg.colormode != CM_LINEART))
+ sumatorio /= lf1a4[channel];
+ else
+ sumatorio /= lf1a4[scancfg.channel];
+
+ sumatorio = min (sumatorio * 0x4000, 65535);
+
+ if (myRegs[0x1bf] != 0x18)
+ myCalib->black_shading[channel][lf13c] |=
+ (0x140 -
+ ((((myRegs[0x1bf] >> 3) & 0x03) *
+ 3) << 6)) & ((int) sumatorio);
+ else
+ myCalib->white_shading[channel][lf13c] =
+ (unsigned short) sumatorio;
+ }
+ /*a9fd */
+ position++;
+ }
+ while (position < myWidth);
+ }
+ }
+
+ /*aa12 */
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ {
+ dbg_tiff_save ("whiteshading3.tiff",
+ scancfg.coord.width,
+ scancfg.coord.height,
+ scancfg.depth,
+ CM_COLOR,
+ scancfg.resolution_x,
+ scancfg.resolution_y,
+ scanbuffer,
+ (scancfg.coord.height + 16) * bytes_per_line);
+ }
+
+ free (buffer1);
+ free (buffer2);
+ free (scanbuffer);
+
+ return OK;
+}
+
+static SANE_Int
+Calib_BlackShading (struct st_device *dev,
+ struct st_calibration_config *calibcfg,
+ struct st_calibration *myCalib, SANE_Int gainmode)
+{
+ /*
+ gainmode f8ec
+ myCalib f8e8
+ calibcfg f8e4
+ */
+ SANE_Byte *myRegs; /*f1bc */
+ struct st_scanparams scancfg; /*f020 */
+ double shadingprediff[6]; /*f08c f094 f09c f0a4 f0ac f0b4 */
+ double mylong; /*f018 */
+ double maxvalue; /*eff8 */
+ double sumatorio = 0.0;
+ double myst;
+ SANE_Int rst;
+ SANE_Int a;
+ SANE_Int mheight; /*efe0 */
+ SANE_Int current_line; /*efe4 */
+ SANE_Int bytes_per_line; /*efd8 */
+ SANE_Int position; /*lefcc */
+ SANE_Int leff0, lf010, lefd0;
+ SANE_Byte *buffer; /*efd4 */
+ SANE_Byte buff2[256]; /*F0BC */
+ SANE_Int buff3[0x8000];
+ SANE_Byte *ptr; /*f008 */
+ SANE_Int my14b4; /*f008 pisa ptr */
+ SANE_Int biggest; /*bx */
+ SANE_Int lowest; /*dx */
+ SANE_Int lefdc;
+ SANE_Int channel;
+ SANE_Int smvalues[3]; /*f04c f04e f050 */
+ double dbvalue[6]; /*lf05c lf060, lf064 lf068, lf06c lf070,
+ lf074 lf078, lf07c lf080, lf084 lf088 */
+
+ DBG (DBG_FNC, "> Calib_BlackShading(*calibcfg, *myCalib, gainmode=%i)\n",
+ gainmode);
+
+ rst = OK;
+ myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (myRegs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (&scancfg, &calibdata->scancfg, sizeof (struct st_scanparams));
+
+ Lamp_SetGainMode (dev, myRegs, scancfg.resolution_x, gainmode);
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ shadingprediff[a + 3] = calibcfg->BShadingPreDiff[a];
+
+ if ((scan.scantype >= ST_NORMAL) && (scan.scantype <= ST_NEG))
+ scancfg.coord.left += scan.ser;
+
+ if ((scancfg.coord.width & 1) != 0)
+ scancfg.coord.width++;
+
+ scancfg.coord.top = 1;
+ scancfg.depth = 8;
+ scancfg.coord.height = calibcfg->BShadingHeight;
+
+ if (scancfg.colormode != CM_COLOR)
+ {
+ bytes_per_line = scancfg.coord.width;
+ leff0 = 0;
+ lf010 = 1;
+ }
+ else
+ {
+ /*876c */
+ bytes_per_line = scancfg.coord.width * 3;
+ if (scancfg.samplerate == LINE_RATE)
+ {
+ leff0 = scancfg.coord.width;
+ lf010 = 1;
+ }
+ else
+ {
+ leff0 = 1;
+ lf010 = 3;
+ }
+ }
+
+ scancfg.v157c = bytes_per_line;
+ scancfg.bytesperline = bytes_per_line;
+
+ mylong = 1 << (16 - scancfg.depth);
+ if ((myRegs[0x1bf] & 0x18) != 0)
+ mylong /= 1 << (((myRegs[0x1bf] >> 5) & 3) + 4);
+
+ lefd0 =
+ ((((myRegs[0x1bf] >> 3) & 2) << 8) -
+ ((((myRegs[0x1bf] >> 3) & 1) * 3) << 6)) - 1;
+
+ if (scancfg.depth >= 8)
+ maxvalue = ((1 << (scancfg.depth - 8)) << 8) - 1;
+ else
+ maxvalue = (256 / (1 << (8 - scancfg.depth))) - 1;
+
+ Calib_LoadCut (dev, &scancfg, scan.scantype, calibcfg);
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ shadingprediff[a] = calibcfg->ShadingCut[a];
+
+ if (calibcfg->BShadingOn == -1)
+ {
+ SANE_Int b, d;
+ double e;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ myst = max (shadingprediff[a], 0);
+ myst = (maxvalue >= myst) ? shadingprediff[a] : maxvalue;
+ shadingprediff[a] = max (myst, 0);
+ }
+
+ b = 0;
+
+ while (b < bytes_per_line)
+ {
+ if (scancfg.colormode != CM_COLOR)
+ {
+ channel = 0;
+ d = b;
+ }
+ else
+ {
+ if (scancfg.samplerate == PIXEL_RATE)
+ {
+ channel = (b % lf010) & 0xffff;
+ d = (b / lf010) & 0xffff;
+ }
+ else
+ {
+ channel = (b / leff0) & 0xffff;
+ d = (b % leff0) & 0xffff;
+ }
+ }
+ /*89d0 */
+ e = min (lefd0, mylong * shadingprediff[channel]);
+ myCalib->black_shading[channel][d] |= (unsigned short) e & 0xffff;
+ b++;
+ }
+
+ return OK;
+ }
+
+ /* Allocate buffer to read image */
+ mheight = scancfg.coord.height;
+ buffer =
+ (SANE_Byte *) malloc (((scancfg.coord.height + 16) * bytes_per_line) *
+ sizeof (SANE_Byte));
+ if (buffer == NULL)
+ return ERROR;
+
+ /* Turn off lamp */
+ Lamp_Status_Set (dev, NULL, FALSE, FLB_LAMP);
+ usleep (200 * 1000);
+
+ /* Scan image */
+ myCalib->shading_enabled = FALSE;
+ rst =
+ RTS_GetImage (dev, myRegs, &scancfg, &calibdata->gain_offset, buffer,
+ myCalib, 0x101, gainmode);
+ if (rst == ERROR)
+ {
+ /*8ac2 */
+ free (buffer);
+ memcpy (&calibdata->Regs, myRegs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+ free (myRegs);
+ return ERROR;
+ }
+
+ /* myRegs isn't going to be used anymore */
+ free (myRegs);
+ myRegs = NULL;
+
+ /* Turn on lamp again */
+ if (scan.scantype != ST_NORMAL)
+ {
+ Lamp_Status_Set (dev, NULL, FALSE, TMA_LAMP);
+ usleep (1000 * 1000);
+ }
+ else
+ Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP);
+
+ /* Save buffer */
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ {
+ dbg_tiff_save ("blackshading.tiff",
+ scancfg.coord.width,
+ scancfg.coord.height,
+ scancfg.depth,
+ CM_COLOR,
+ scancfg.resolution_x,
+ scancfg.resolution_y,
+ buffer, (scancfg.coord.height + 16) * bytes_per_line);
+ }
+
+ if (scancfg.depth > 8)
+ {
+ /*8bb2 */
+ bzero (&dbvalue, 6 * sizeof (double));
+ position = 0;
+
+ if (bytes_per_line > 0)
+ {
+ do
+ {
+ bzero (&buff3, 0x8000 * sizeof (SANE_Int));
+ sumatorio = 0;
+ ptr = buffer + position;
+ current_line = 0;
+ biggest = 0;
+ lowest = (int) maxvalue;
+ /* Toma los valores de una columna */
+ if (mheight > 0)
+ {
+ SANE_Int value;
+ do
+ {
+ value = data_lsb_get (ptr, 2);
+ biggest = max (biggest, value);
+ if (current_line < mheight)
+ {
+ sumatorio += value;
+ lowest = min (lowest, value);
+ biggest = max (biggest, value);
+
+ buff3[value]++;
+ }
+ else
+ {
+ /*8cab */
+ if (value > lowest)
+ {
+ buff3[lowest]--;
+ buff3[value]++;
+ sumatorio += value;
+ sumatorio -= lowest;
+
+ if (buff3[lowest] != 0)
+ {
+ do
+ {
+ lowest++;
+ }
+ while (buff3[lowest] == 0);
+ }
+ }
+ }
+ /*8d0b */
+ ptr += (bytes_per_line * 2);
+ current_line++;
+ }
+ while (current_line < mheight);
+ }
+ /*8d27 */
+ sumatorio /= mheight;
+
+ if (scancfg.colormode != CM_COLOR)
+ {
+ channel = 0;
+ lefdc = position;
+ }
+ else
+ {
+ /*8d5f */
+ if (scancfg.samplerate == PIXEL_RATE)
+ {
+ channel = position % lf010;
+ lefdc = (position / lf010) & 0xffff;
+ }
+ else
+ {
+ channel = position / leff0;
+ lefdc = position % leff0;
+ }
+ }
+
+ dbvalue[channel] += sumatorio;
+ if ((scancfg.colormode == CM_GRAY)
+ || (scancfg.colormode == CM_LINEART))
+ sumatorio += shadingprediff[scancfg.channel];
+ else
+ sumatorio += shadingprediff[channel];
+
+ myst = min (max (0, sumatorio), maxvalue);
+
+ dbvalue[channel + 3] = myst;
+
+ if ((calibcfg->BShadingOn == 1) || (calibcfg->BShadingOn == 2))
+ {
+ if (calibcfg->BShadingOn == 2)
+ {
+ myst -=
+ calibcfg->BRef[channel] * (1 << (scancfg.depth - 8));
+ myst = max (myst, 0);
+ }
+ /*8e6d */
+ myst *= mylong;
+ myCalib->black_shading[channel][lefdc] = min (myst, lefd0);
+ }
+
+ position++;
+ }
+ while (position < bytes_per_line);
+ }
+ }
+ else
+ {
+ /*8eb6 */
+ bzero (&dbvalue, 6 * sizeof (double));
+ position = 0;
+
+ if (bytes_per_line > 0)
+ {
+ do
+ {
+ bzero (&buff2, 256 * sizeof (SANE_Byte));
+ sumatorio = 0;
+ /* ptr points to the next position of the first line */
+ ptr = buffer + position;
+ biggest = 0;
+ lowest = (int) maxvalue;
+ current_line = 0;
+ /* Toma los valores de una columna */
+ if (mheight > 0)
+ {
+ my14b4 = v14b4;
+ do
+ {
+ biggest = max (biggest, *ptr);
+
+ if (my14b4 == 0)
+ {
+ /*8fd7 */
+ if (current_line < mheight)
+ {
+ sumatorio += *ptr;
+
+ lowest = min (lowest, *ptr);
+ biggest = max (biggest, *ptr);
+
+ buff2[*ptr]++;
+ }
+ else
+ {
+ /*9011 */
+ if (*ptr > lowest)
+ {
+ buff2[lowest]--;
+ buff2[*ptr]++;
+ sumatorio += *ptr;
+ sumatorio -= lowest;
+
+ if (buff2[lowest] != 0)
+ {
+ do
+ {
+ lowest++;
+ }
+ while (buff2[lowest] == 0);
+ }
+ }
+ }
+ }
+ else
+ sumatorio += *ptr;
+ /*9067 */
+ /* Point to the next pixel under current line */
+ ptr += bytes_per_line;
+ current_line++;
+ }
+ while (current_line < mheight);
+ }
+
+ /*908a */
+ /* Calculates average of each column */
+ sumatorio = sumatorio / mheight;
+
+ if (scancfg.colormode != CM_COLOR)
+ {
+ channel = 0;
+ lefdc = position;
+ }
+ else
+ {
+ /*90c5 */
+ if (scancfg.samplerate == PIXEL_RATE)
+ {
+ channel = position % lf010;
+ lefdc = (position / lf010) & 0xffff;
+ }
+ else
+ {
+ /*90fb */
+ channel = position / leff0;
+ lefdc = position % leff0;
+ }
+ }
+
+ /*911f */
+ dbvalue[channel] += sumatorio;
+ if ((scancfg.colormode == CM_GRAY)
+ || (scancfg.colormode == CM_LINEART))
+ sumatorio += shadingprediff[scancfg.channel];
+ else
+ sumatorio += shadingprediff[channel];
+
+ /*9151 */
+ myst = min (max (0, sumatorio), maxvalue);
+
+ /*9198 */
+ if (position >= 3)
+ {
+ double myst2;
+
+ myst -= dbvalue[channel + 3];
+ myst2 = myst;
+ myst = min (myst, shadingprediff[channel + 3]);
+
+ my14b4 = -shadingprediff[channel + 3];
+ if (myst >= my14b4)
+ myst = min (myst2, shadingprediff[channel + 3]);
+ else
+ myst = my14b4;
+
+ myst += dbvalue[channel + 3];
+ }
+
+ /*9203 */
+ dbvalue[channel + 3] = myst;
+
+ switch (calibcfg->BShadingOn)
+ {
+ case 1:
+ myCalib->black_shading[channel][lefdc] |=
+ (unsigned short) (((int) myst & 0xff) << 8) & 0xffff;
+ break;
+ case 2:
+ /*9268 */
+ my14b4 =
+ calibcfg->BRef[channel] / (1 << (8 - scancfg.depth));
+ myst -= my14b4;
+ myst = max (myst, 0);
+ myst *= mylong;
+ myCalib->black_shading[channel][lefdc] = min (myst, lefd0);
+ break;
+ }
+
+ /*92d8 */
+ position++;
+ }
+ while (position < bytes_per_line);
+ }
+ }
+
+ /*9306 */
+ if (calibcfg->BShadingOn == -2)
+ {
+ for (a = 0; a < 3; a++)
+ {
+ dbvalue[a] =
+ (dbvalue[a] / scancfg.coord.width) + calibcfg->ShadingCut[a];
+ if (dbvalue[a] < 0)
+ dbvalue[a] = 0;
+ smvalues[a] = min ((int) (dbvalue[a] + 0.5) & 0xffff, maxvalue);
+ }
+
+ if (scancfg.coord.width > 0)
+ {
+ SANE_Int b, c;
+
+ for (c = 0; c < scancfg.coord.width; c++)
+ for (b = 0; b < 3; b++)
+ myCalib->black_shading[b][c] |=
+ (unsigned short) min (smvalues[b] * mylong, lefd0);
+ }
+ }
+ /*9425 */
+ free (buffer);
+
+ return OK;
+}
+
+static SANE_Int
+Calibration (struct st_device *dev, SANE_Byte * Regs,
+ struct st_scanparams *scancfg, struct st_calibration *myCalib,
+ SANE_Int value)
+{
+ /*//SANE_Int Calibration([fa20]char *Regs, [fa24]struct st_scanparams *scancfg, [fa28]struct st_calibration myCalib, [fa2c]SANE_Int value)
+ */
+
+ struct st_calibration_config calibcfg; /* f90c */
+ SANE_Int a;
+ SANE_Byte gainmode;
+ SANE_Int lf900;
+
+ DBG (DBG_FNC, "> Calibration\n");
+ dbg_ScanParams (scancfg);
+
+ value = value; /*silence gcc */
+
+ memcpy (&calibdata->Regs, Regs, sizeof (SANE_Byte) * RT_BUFFER_LEN);
+
+ /*4213be8 */
+ memset (&calibcfg, 0x30, sizeof (struct st_calibration_config));
+ Calib_LoadConfig (dev, &calibcfg, scan.scantype, scancfg->resolution_x,
+ scancfg->depth);
+
+ bzero (&calibdata->gain_offset, sizeof (struct st_gain_offset)); /*[42b3654] */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ myCalib->WRef[a] = calibcfg.WRef[a];
+
+ calibdata->gain_offset.edcg1[a] = 256;
+ calibdata->gain_offset.odcg1[a] = 256;
+ calibdata->gain_offset.vgag1[a] = 4;
+ calibdata->gain_offset.vgag2[a] = 4;
+
+ /*3654|3656|3658
+ 365a|365c|365e
+ 3660|3662|3664
+ 3666|3668|366a
+ 366c|366d|366e
+ 366f|3670|3671
+ 3672|3673|3674 */
+ }
+
+ memcpy (&calibdata->scancfg, scancfg, sizeof (struct st_scanparams));
+ gainmode = Lamp_GetGainMode (dev, scancfg->resolution_x, scan.scantype); /* [lf904] = 1 */
+
+ /* 3cf3 */
+ myCalib->first_position = 1;
+ myCalib->shading_type = 0;
+ if (calibdata->scancfg.colormode == CM_LINEART)
+ {
+ calibdata->scancfg.colormode = CM_GRAY;
+ calibcfg.GainTargetFactor = 1.3;
+ }
+
+ lf900 = OK;
+ if (calibcfg.CalibPAGOn != 0)
+ {
+ if (Calib_PAGain (dev, &calibcfg, gainmode) != 0)
+ lf900 = ERROR;
+ /*ERROR*/}
+ else
+ {
+ /*3da7 */
+ if ((calibdata->scancfg.colormode != CM_GRAY)
+ && (calibdata->scancfg.colormode != CM_LINEART))
+ {
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ calibdata->gain_offset.pag[a] = calibcfg.PAG[a];
+ }
+ else
+ {
+ /* 3dd3 */
+ /* Default PAGain */
+ if (calibdata->scancfg.channel > 2)
+ calibdata->scancfg.channel = 0;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ calibdata->gain_offset.pag[a] =
+ calibcfg.PAG[calibdata->scancfg.channel];
+ }
+ }
+
+ /* 3e01 */
+ if (calibcfg.CalibOffset10n != 0) /*==2*/
+ {
+ /*v14b4=1 offset[CL_RED]=0x174 offset[CL_GREEN]=0x16d offset[CL_BLUE]=0x160 */
+ if ((v14b4 != 0) && (offset[CL_RED] != 0) && (offset[CL_GREEN] != 0)
+ && (offset[CL_BLUE] != 0))
+ {
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ calibdata->gain_offset.edcg1[a] = offset[a];
+ calibdata->gain_offset.odcg1[a] = offset[a];
+ }
+ }
+ else
+ {
+ /* 3e84 */
+ if ((calibcfg.CalibOffset10n > 0) && (calibcfg.CalibOffset10n < 4))
+ {
+ /*if (calibcfg.CalibOffset10n != 0) */
+ if (calibcfg.CalibOffset10n == 3)
+ {
+ lf900 = Calib_AdcOffsetRT (dev, &calibcfg, 1);
+ }
+ else
+ {
+ /* 3eb2 */
+ /*falta codigo */
+ }
+ }
+ }
+ }
+ else
+ {
+ /* 3faf */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ calibdata->gain_offset.edcg1[a] =
+ abs (calibcfg.OffsetEven1[a] - 0x100);
+ calibdata->gain_offset.odcg1[a] =
+ abs (calibcfg.OffsetOdd1[a] - 0x100);
+ }
+ }
+
+ /* 3f13 3f0b */
+ if ((gainmode != 0) && (calibcfg.CalibGain10n != 0))
+ {
+ /*gain[CL_RED]=0x17 gain[CL_GREEN]=0x12 gain[CL_BLUE]=0x17 */
+ if ((v14b4 != 0) && (gain[CL_RED] != 0) && (gain[CL_GREEN] != 0)
+ && (gain[CL_BLUE] != 0))
+ {
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ calibdata->gain_offset.vgag1[a] = gain[a];
+ }
+ else
+ {
+ /*4025 */
+ lf900 = Calib_AdcGain (dev, &calibcfg, 1, gainmode);
+
+ if ((v14b4 != 0) && (lf900 == OK))
+ GainOffset_Save (dev, &calibdata->gain_offset.edcg1[0],
+ &calibdata->gain_offset.vgag1[0]);
+ }
+ }
+ else
+ {
+ /*4089 */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ calibdata->gain_offset.vgag1[a] = calibcfg.Gain1[a];
+ }
+
+ /*40a5 */
+ if ((gainmode != 0) && (calibcfg.CalibOffset20n != 0))
+ {
+ switch (calibcfg.CalibOffset20n)
+ {
+ case 3:
+ lf900 = Calib_AdcOffsetRT (dev, &calibcfg, 2);
+ break;
+ }
+ /*4140 */
+ /*falta codigo */
+ }
+ else
+ {
+ /*4162 */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ calibdata->gain_offset.edcg2[a] =
+ abs (calibcfg.OffsetEven2[a] - 0x40);
+ calibdata->gain_offset.odcg2[a] =
+ abs (calibcfg.OffsetOdd2[a] - 0x40);
+ }
+ }
+
+ /*41d6 */
+ if ((gainmode != 0) && (calibcfg.CalibGain20n != 0))
+ {
+ lf900 = Calib_AdcGain (dev, &calibcfg, 0, gainmode);
+ }
+ else
+ {
+ /*423c */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ calibdata->gain_offset.vgag2[a] = calibcfg.Gain2[a];
+ }
+
+ /*4258 */
+ if (calibcfg.TotShading != 0)
+ {
+ lf900 = Calib_BWShading (&calibcfg, myCalib, gainmode);
+ /*falta codigo */
+ }
+ else
+ {
+ /*428f */
+ if (gainmode != 0)
+ {
+ if (calibcfg.BShadingOn != 0)
+ lf900 = Calib_BlackShading (dev, &calibcfg, myCalib, gainmode);
+
+ /*42fd */
+ if ((lf900 != ERROR) && (calibcfg.WShadingOn != 0))
+ {
+ switch (calibcfg.WShadingOn)
+ {
+ default:
+ break;
+ case 3:
+ lf900 =
+ Calib_WhiteShading_3 (dev, &calibcfg, myCalib, gainmode);
+ break;
+ case 2:
+ break;
+ }
+ }
+ else
+ myCalib->shading_enabled = FALSE;
+ }
+ else
+ myCalib->shading_enabled = FALSE;
+ }
+
+ /*43ca */
+ memcpy (&myCalib->gain_offset, &calibdata->gain_offset,
+ sizeof (struct st_gain_offset));
+ memcpy (&mitabla2, &calibdata->gain_offset, sizeof (struct st_gain_offset));
+
+ /*4424 */
+ /* Park home after calibration */
+ if (get_value (SCANINFO, PARKHOMEAFTERCALIB, TRUE, FITCALIBRATE) == FALSE)
+ scan.ler -= calibcfg.WShadingHeight;
+ else
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+
+ return OK;
+}
+
+/*static void show_diff(struct st_device *dev, SANE_Byte *original)
+{
+ SANE_Byte *buffer = (SANE_Byte *)malloc(RT_BUFFER_LEN * sizeof(SANE_Byte));
+ SANE_Int a;
+
+ if ((buffer == NULL)||(original == NULL))
+ return;
+
+ if (RTS_ReadRegs(dev->usb_handle, buffer) != OK)
+ {
+ free(buffer);
+ return;
+ }
+
+ for (a = 0; a < RT_BUFFER_LEN; a++)
+ {
+ if ((original[a] & 0xff) != (buffer[a] & 0xff))
+ {
+ printf("%5i: %i -> %i\n", a, original[a] & 0xff, buffer[a] & 0xff);
+ original[a] = buffer[a] & 0xff;
+ }
+ }
+
+ free(buffer);
+} */
+
+static SANE_Int
+Load_Constrains (struct st_device *dev)
+{
+ SANE_Int rst = ERROR;
+
+ if (dev->constrains != NULL)
+ Free_Constrains (dev);
+
+ DBG (DBG_FNC, "> Load_Constrains\n");
+
+ dev->constrains =
+ (struct st_constrains *) malloc (sizeof (struct st_constrains));
+ if (dev->constrains != NULL)
+ {
+ cfg_constrains_get (dev->constrains);
+ rst = OK;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+Constrains_Check (struct st_device *dev, SANE_Int Resolution,
+ SANE_Int scantype, struct st_coords *mycoords)
+{
+ /*
+ Constrains:
+ 100 dpi 850 x 1170 | 164 x 327
+ 300 dpi 2550 x 3510
+ 600 dpi 5100 x 7020
+ 1200 dpi 10200 x 14040
+ */
+
+ SANE_Int rst = ERROR;
+
+ if (dev->constrains != NULL)
+ {
+ struct st_coords coords;
+ struct st_coords *mc;
+
+ if ((scantype < ST_NORMAL) || (scantype > ST_NEG))
+ scantype = ST_NORMAL;
+
+ switch (scantype)
+ {
+ case ST_TA:
+ mc = &dev->constrains->slide;
+ break;
+ case ST_NEG:
+ mc = &dev->constrains->negative;
+ break;
+ default:
+ mc = &dev->constrains->reflective;
+ break;
+ }
+
+ coords.left = MM_TO_PIXEL (mc->left, Resolution);
+ coords.width = MM_TO_PIXEL (mc->width, Resolution);
+ coords.top = MM_TO_PIXEL (mc->top, Resolution);
+ coords.height = MM_TO_PIXEL (mc->height, Resolution);
+
+ /* Check left and top */
+ if (mycoords->left < 0)
+ mycoords->left = 0;
+
+ mycoords->left += coords.left;
+
+ if (mycoords->top < 0)
+ mycoords->top = 0;
+
+ mycoords->top += coords.top;
+
+ /* Check width and height */
+ if ((mycoords->width < 0) || (mycoords->width > coords.width))
+ mycoords->width = coords.width;
+
+ if ((mycoords->height < 0) || (mycoords->height > coords.height))
+ mycoords->height = coords.height;
+
+ rst = OK;
+ }
+
+ DBG (DBG_FNC,
+ "> Constrains_Check: Source=%s, Res=%i, LW=(%i,%i), TH=(%i,%i): %i\n",
+ dbg_scantype (scantype), Resolution, mycoords->left, mycoords->width,
+ mycoords->top, mycoords->height, rst);
+
+ return rst;
+}
+
+static struct st_coords *
+Constrains_Get (struct st_device *dev, SANE_Byte scantype)
+{
+ static struct st_coords *rst = NULL;
+
+ if (dev->constrains != NULL)
+ {
+ switch (scantype)
+ {
+ case ST_TA:
+ rst = &dev->constrains->slide;
+ break;
+ case ST_NEG:
+ rst = &dev->constrains->negative;
+ break;
+ default:
+ rst = &dev->constrains->reflective;
+ break;
+ }
+ }
+
+ return rst;
+}
+
+static void
+Free_Constrains (struct st_device *dev)
+{
+ DBG (DBG_FNC, "> Free_Constrains\n");
+
+ if (dev->constrains != NULL)
+ {
+ free (dev->constrains);
+ dev->constrains = NULL;
+ }
+}
+
+static void
+RTS_DebugInit ()
+{
+ /* Default vaules */
+ RTS_Debug->dev_model = HP3970;
+
+ RTS_Debug->DumpShadingData = FALSE;
+ RTS_Debug->SaveCalibFile = FALSE;
+ RTS_Debug->ScanWhiteBoard = FALSE;
+ RTS_Debug->EnableGamma = TRUE;
+ RTS_Debug->use_fixed_pwm = TRUE;
+ RTS_Debug->dmatransfersize = 0x80000;
+ RTS_Debug->dmasetlength = 0x7c0000;
+ RTS_Debug->dmabuffersize = 0x400000;
+ RTS_Debug->usbtype = -1;
+
+ /* Lamp settings */
+ RTS_Debug->overdrive_flb = 10000; /* msecs */
+ RTS_Debug->overdrive_ta = 10000; /* msecs */
+
+ RTS_Debug->warmup = TRUE;
+
+ /* Calibration settings */
+ RTS_Debug->calibrate = FALSE;
+ RTS_Debug->wshading = TRUE;
+}
+
+static void
+RTS_Setup_Gamma (SANE_Byte * Regs, struct st_hwdconfig *hwdcfg)
+{
+ DBG (DBG_FNC, "> RTS_Setup_Gamma(*Regs, *hwdcfg)\n");
+
+ if ((hwdcfg != NULL) && (Regs != NULL))
+ {
+ if (hwdcfg->use_gamma_tables != FALSE)
+ {
+ SANE_Int table_size;
+
+ /* set set table size */
+ data_bitset (&Regs[0x1d0], 0x0f, hwdcfg->gamma_tablesize);
+
+ /* enable gamma correction */
+ data_bitset (&Regs[0x1d0], 0x40, 1);
+
+
+ switch (Regs[0x1d0] & 0x0c)
+ {
+ case 0:
+ table_size = (Regs[0x1d0] & 1) | 0x0100;
+ break;
+ case 4:
+ table_size = (Regs[0x1d0] & 1) | 0x0400;
+ break;
+ case 8:
+ table_size = (Regs[0x1d0] & 1) | 0x1000;
+ break;
+ default:
+ table_size = hwdcfg->startpos & 0xffff;
+ break;
+ }
+
+ /* 5073 */
+ /* points to red gamma table */
+ data_wide_bitset (&Regs[0x1b4], 0x3fff, 0);
+
+ /* points to green gamma table */
+ data_wide_bitset (&Regs[0x1b6], 0x3fff, table_size);
+
+ /* points to blue gamma table */
+ data_wide_bitset (&Regs[0x1b8], 0x3fff, table_size * 2);
+
+ v15f8 = (((table_size * 3) + 15) / 16) & 0xffff;
+ }
+ else
+ {
+ /* disable gamma correction */
+ data_bitset (&Regs[0x1d0], 0x40, 0);
+ v15f8 = 0;
+ }
+ }
+}
+
+static SANE_Int
+RTS_USBType (struct st_device *dev)
+{
+ /* Gets USB type of this scanner */
+
+ SANE_Int rst = ERROR;
+ SANE_Byte data;
+
+ DBG (DBG_FNC, "+ RTS_USBType\n");
+
+ if (Read_Byte (dev->usb_handle, 0xfe11, &data) == OK)
+ rst = (data & 1);
+
+ DBG (DBG_FNC, "- RTS_USBType(void): %s\n",
+ (rst == USB11) ? "USB1.1" : "USB2.0");
+
+ return rst;
+}
+
+static SANE_Int
+Init_Vars (void)
+{
+ SANE_Int rst = OK;
+
+ hp_gamma = malloc (sizeof (struct st_gammatables));
+ if (hp_gamma != NULL)
+ bzero (hp_gamma, sizeof (struct st_gammatables));
+ else
+ rst = ERROR;
+
+ if (rst == OK)
+ {
+ RTS_Debug = malloc (sizeof (struct st_debug_opts));
+ if (RTS_Debug != NULL)
+ bzero (RTS_Debug, sizeof (struct st_debug_opts));
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ default_gain_offset = malloc (sizeof (struct st_gain_offset));
+ if (default_gain_offset != NULL)
+ bzero (default_gain_offset, sizeof (struct st_gain_offset));
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ calibdata = malloc (sizeof (struct st_calibration_data));
+ if (calibdata != NULL)
+ bzero (calibdata, sizeof (struct st_calibration_data));
+ else
+ rst = ERROR;
+ }
+
+ if (rst == OK)
+ {
+ wshading = malloc (sizeof (struct st_shading));
+ if (wshading != NULL)
+ bzero (wshading, sizeof (struct st_shading));
+ else
+ rst = ERROR;
+ }
+
+ waitforpwm = TRUE;
+
+ use_gamma_tables = TRUE;
+
+ if (rst == OK)
+ RTS_DebugInit ();
+ else
+ Free_Vars ();
+
+ return rst;
+}
+
+static void
+Free_Vars (void)
+{
+ if (RTS_Debug != NULL)
+ {
+ free (RTS_Debug);
+ RTS_Debug = NULL;
+ }
+
+ if (hp_gamma != NULL)
+ {
+ free (hp_gamma);
+ hp_gamma = NULL;
+ }
+
+ if (calibdata != NULL)
+ {
+ free (calibdata);
+ calibdata = NULL;
+ }
+
+ if (wshading != NULL)
+ {
+ if (wshading->rates != NULL)
+ free (wshading->rates);
+
+ free (wshading);
+ wshading = NULL;
+ }
+
+ if (default_gain_offset != NULL)
+ {
+ free (default_gain_offset);
+ default_gain_offset = NULL;
+ }
+
+}
+
+static SANE_Int
+Chipset_Reset (struct st_device *dev)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ Chipset_Reset:\n");
+
+ /* I've found two ways to reset chipset. Next one will stay commented
+ rst = ERROR;
+ if (Read_Byte(dev->usb_handle, 0xe800, &data) == OK)
+ {
+ data |= 0x20;
+ if (Write_Byte(dev->usb_handle, 0xe800, data) == OK)
+ {
+ data &= 0xdf;
+ rst = Write_Byte(dev->usb_handle, 0xe800, data);
+ }
+ }
+ */
+
+ rst = IWrite_Buffer (dev->usb_handle, 0x0000, NULL, 0, 0x0801);
+
+ DBG (DBG_FNC, "- Chipset_Reset: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_Enable_Read (struct st_device *dev, SANE_Int dmacs, SANE_Int size,
+ SANE_Int options)
+{
+ SANE_Int rst = ERROR;
+ SANE_Byte buffer[6];
+
+ DBG (DBG_FNC,
+ "+ RTS_DMA_Enable_Read(dmacs=0x%04x, size=%i, options=0x%06x)\n",
+ dmacs, size, options);
+
+ data_msb_set (&buffer[0], options, 3);
+
+ /* buffer size divided by 2 (words count) */
+ data_lsb_set (&buffer[3], size / 2, 3);
+
+ rst = IWrite_Buffer (dev->usb_handle, dmacs, buffer, 6, 0x0400);
+
+ DBG (DBG_FNC, "- RTS_DMA_Enable_Read: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_Enable_Write (struct st_device *dev, SANE_Int dmacs, SANE_Int size,
+ SANE_Int options)
+{
+ SANE_Int rst = ERROR;
+ SANE_Byte buffer[6];
+
+ DBG (DBG_FNC,
+ "+ RTS_DMA_Enable_Write(dmacs=0x%04x, size=%i, options=0x%06x)\n",
+ dmacs, size, options);
+
+ data_msb_set (&buffer[0], options, 3);
+
+ /* buffer size divided by 2 (words count) */
+ data_lsb_set (&buffer[3], size / 2, 3);
+
+ rst = IWrite_Buffer (dev->usb_handle, dmacs, buffer, 6, 0x0401);
+
+ DBG (DBG_FNC, "- RTS_DMA_Enable_Write: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_Cancel (struct st_device *dev)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_DMA_Cancel:\n");
+
+ rst = IWrite_Word (dev->usb_handle, 0x0000, 0, 0x0600);
+
+ DBG (DBG_FNC, "- RTS_DMA_Cancel: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_DMA_Reset (struct st_device *dev)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_DMA_Reset:\n");
+
+ rst = IWrite_Word (dev->usb_handle, 0x0000, 0x0000, 0x0800);
+
+ DBG (DBG_FNC, "- RTS_DMA_Reset: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_WriteByte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_WriteByte(address=%04x, data=%i):\n", address,
+ data);
+
+ rst = IWrite_Byte (usb_handle, address, data, 0x200, 0x200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_WriteByte: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_ReadWord (USB_Handle usb_handle, SANE_Int address, SANE_Int * data)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_ReadWord(address=%04x, data):\n", address);
+
+ rst = IRead_Word (usb_handle, address, data, 0x200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_ReadWord: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_ReadByte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * data)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_ReadByte(address=%04x, data):\n", address);
+
+ rst = IRead_Byte (usb_handle, address, data, 0x200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_ReadByte: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_WriteWord (USB_Handle usb_handle, SANE_Int address, SANE_Int data)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_WriteWord(address=%04x, data=%i):\n", address,
+ data);
+
+ rst = IWrite_Word (usb_handle, address, data, 0x0200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_WriteWord: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_ReadInteger (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int * data)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_ReadInteger(address=%04x, data):\n", address);
+
+ rst = IRead_Integer (usb_handle, address, data, 0x200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_ReadInteger: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_WriteInteger (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int data)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_WriteInteger(address=%04x, data):\n", address);
+
+ rst = IWrite_Integer (usb_handle, address, data, 0x200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_WriteInteger: %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+RTS_EEPROM_WriteBuffer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * data, SANE_Int size)
+{
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "+ RTS_EEPROM_WriteBuffer(address=%04x, data, size=%i):\n",
+ address, size);
+
+ rst = IWrite_Buffer (usb_handle, address, data, size, 0x200);
+
+ DBG (DBG_FNC, "- RTS_EEPROM_WriteBuffer: %i\n", rst);
+
+ return rst;
+}
+
+static void
+WShading_Emulate (SANE_Byte * buffer, SANE_Int * chnptr, SANE_Int size,
+ SANE_Int depth)
+{
+ if ((wshading->rates != NULL) && (chnptr != NULL))
+ {
+ if (*chnptr < wshading->count)
+ {
+ double maxvalue, chncolor;
+ SANE_Int chnsize;
+ SANE_Int pos;
+ SANE_Int icolor;
+
+ maxvalue = (1 << depth) - 1;
+ chnsize = (depth > 8) ? 2 : 1;
+
+ pos = 0;
+ while (pos < size)
+ {
+ /* get channel color */
+ chncolor = data_lsb_get (buffer + pos, chnsize);
+
+ /* apply shading coeficient */
+ chncolor *= wshading->rates[*chnptr];
+
+ /* care about limits */
+ chncolor = min (chncolor, maxvalue);
+
+ /* save color */
+ icolor = chncolor;
+ data_lsb_set (buffer + pos, icolor, chnsize);
+
+ *chnptr = *chnptr + 1;
+ if (*chnptr >= wshading->count)
+ *chnptr = 0;
+
+ pos += chnsize;
+ }
+ }
+ }
+}
+
+static SANE_Int
+WShading_Calibrate (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib,
+ struct st_scanparams *scancfg)
+{
+ struct st_calibration_config *calibcfg;
+ struct st_gain_offset myCalibTable;
+ struct st_scanparams *myscancfg;
+ SANE_Byte *myRegs; /*f1bc */
+ SANE_Int bytes_per_line;
+ /**/ SANE_Int x, y, a, C;
+ SANE_Byte *pattern; /*f164 */
+ double sumatorio;
+ SANE_Int gainmode;
+ SANE_Int rst;
+ SANE_Byte *avg_colors;
+
+ DBG (DBG_FNC, "> WShading_Calibrate(*myCalib)\n");
+
+ bzero (&myCalibTable, sizeof (struct st_gain_offset));
+ for (C = CL_RED; C <= CL_BLUE; C++)
+ {
+ myCalibTable.pag[C] = 3;
+ myCalibTable.vgag1[C] = 4;
+ myCalibTable.vgag2[C] = 4;
+ }
+
+ calibcfg =
+ (struct st_calibration_config *)
+ malloc (sizeof (struct st_calibration_config));
+ memset (calibcfg, 0x30, sizeof (struct st_calibration_config));
+
+ myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ memcpy (myscancfg, scancfg, sizeof (struct st_scanparams));
+
+ myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x,
+ myscancfg->depth);
+ gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype);
+
+ Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode);
+
+ rst = OK;
+
+ switch (scan.scantype)
+ {
+ case ST_NORMAL:
+ /*a184 */
+ myscancfg->coord.left += scan.ser;
+ myscancfg->coord.width &= 0xffff;
+ break;
+ case ST_TA:
+ case ST_NEG:
+ myscancfg->coord.left += scan.ser;
+ break;
+ }
+
+ /*a11b */
+ if ((myscancfg->coord.width & 1) != 0)
+ myscancfg->coord.width++;
+
+ myscancfg->coord.top = 1;
+ myscancfg->coord.height = calibcfg->WShadingHeight;
+
+ myscancfg->sensorresolution = 0;
+
+ bytes_per_line =
+ myscancfg->coord.width * (((myscancfg->colormode == CM_COLOR) ? 3 : 1) *
+ ((myscancfg->depth > 8) ? 2 : 1));
+
+ /*a1e8 */
+ myscancfg->v157c = bytes_per_line;
+ myscancfg->bytesperline = bytes_per_line;
+
+ /* allocate space for pattern */
+ pattern =
+ (SANE_Byte *) malloc (((myscancfg->coord.height) * bytes_per_line) *
+ sizeof (SANE_Byte));
+ if (pattern == NULL)
+ return ERROR;
+
+ /* Scan image */
+ myCalib->shading_enabled = FALSE;
+ rst =
+ RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, pattern, myCalib,
+ 0x20000000, gainmode);
+
+ if (rst != ERROR)
+ {
+ SANE_Int chn;
+ double colors[3] = { 0, 0, 0 };
+ double prueba;
+ SANE_Int data;
+ SANE_Int bytes_per_channel;
+
+ bytes_per_channel = (myscancfg->depth > 8) ? 2 : 1;
+
+ avg_colors = (SANE_Byte *) malloc (sizeof (SANE_Byte) * bytes_per_line);
+
+ if (avg_colors != NULL)
+ {
+ wshading->ptr = 0;
+ wshading->count = bytes_per_line / bytes_per_channel;
+
+ if (wshading->rates != NULL)
+ {
+ free (wshading->rates);
+ wshading->rates = NULL;
+ }
+ wshading->rates =
+ (double *) malloc (sizeof (double) * wshading->count);
+
+ chn = 0;
+ for (x = 0; x < wshading->count; x++)
+ {
+ sumatorio = 0;
+
+ for (y = 0; y < myscancfg->coord.height; y++)
+ {
+ data =
+ data_lsb_get (pattern +
+ ((x * bytes_per_channel) +
+ (bytes_per_line * y)), bytes_per_channel);
+ sumatorio += data;
+ }
+
+ sumatorio /= myscancfg->coord.height;
+ a = sumatorio;
+ colors[chn] = max (colors[chn], sumatorio);
+ chn++;
+ if (chn > 2)
+ chn = 0;
+
+ data_lsb_set (avg_colors + (x * bytes_per_channel), a,
+ bytes_per_channel);
+ }
+
+ DBG (DBG_FNC, " -> max colors RGB= %f %f %f\n", colors[0],
+ colors[1], colors[2]);
+
+ chn = 0;
+ for (x = 0; x < wshading->count; x++)
+ {
+ data =
+ data_lsb_get (avg_colors + (x * bytes_per_channel),
+ bytes_per_channel);
+ prueba = data;
+ *(wshading->rates + x) = colors[chn] / prueba;
+ chn++;
+ if (chn > 2)
+ chn = 0;
+ }
+ }
+
+ if (RTS_Debug->SaveCalibFile != FALSE)
+ {
+ dbg_tiff_save ("whiteshading_jkd.tiff",
+ myscancfg->coord.width,
+ myscancfg->coord.height,
+ myscancfg->depth,
+ CM_COLOR,
+ scancfg->resolution_x,
+ scancfg->resolution_y,
+ pattern, (myscancfg->coord.height) * bytes_per_line);
+ }
+
+#ifdef developing
+ {
+ FILE *archivo;
+ char texto[1024];
+
+ /* apply correction to the pattern to see the result */
+ chn = 0;
+ for (x = 0; x < myscancfg->coord.height * wshading->count; x++)
+ {
+ data =
+ data_lsb_get (pattern + (x * bytes_per_channel),
+ bytes_per_channel);
+ sumatorio = data;
+ sumatorio *= wshading->rates[chn];
+ if (sumatorio > ((1 << myscancfg->depth) - 1))
+ sumatorio = (1 << myscancfg->depth) - 1;
+
+ a = sumatorio;
+ data_lsb_set (pattern + (x * bytes_per_channel), a,
+ bytes_per_channel);
+
+ chn++;
+ if (chn == wshading->count)
+ chn = 0;
+ }
+
+ /* save corrected pattern */
+ dbg_tiff_save ("onwhiteshading_jkd.tiff",
+ myscancfg->coord.width,
+ myscancfg->coord.height,
+ myscancfg->depth,
+ CM_COLOR,
+ scancfg->resolution_x,
+ scancfg->resolution_y,
+ pattern, (myscancfg->coord.height) * bytes_per_line);
+
+ /* export coefficients */
+ archivo = fopen ("wShading.txt", "w");
+ for (x = 0; x < wshading->count; x++)
+ {
+ snprintf (texto, 1024, "%f", wshading->rates[x]);
+ fprintf (archivo, "%s\n", texto);
+ }
+
+ fclose (archivo);
+ }
+#endif
+ }
+
+ free (pattern);
+
+ return OK;
+}
+
+#ifdef developing
+static SANE_Int
+motor_pos (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib, struct st_scanparams *scancfg)
+{
+ struct st_calibration_config *calibcfg;
+ struct st_gain_offset myCalibTable;
+ struct st_scanparams *myscancfg;
+ SANE_Byte *myRegs; /*f1bc */
+ SANE_Int bytes_per_line;
+ /**/ SANE_Int a, C;
+ SANE_Byte *scanbuffer; /*f164 */
+ SANE_Int gainmode;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "> Calib_test(*myCalib)\n");
+
+ bzero (&myCalibTable, sizeof (struct st_gain_offset));
+
+ calibcfg =
+ (struct st_calibration_config *)
+ malloc (sizeof (struct st_calibration_config));
+ memset (calibcfg, 0x30, sizeof (struct st_calibration_config));
+
+ myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ memcpy (myscancfg, scancfg, sizeof (struct st_scanparams));
+
+ myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x,
+ myscancfg->depth);
+ gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype);
+
+ Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode);
+
+ rst = OK;
+
+ switch (scan.scantype)
+ {
+ case ST_NORMAL:
+ /*a184 */
+ myscancfg->coord.left += scan.ser;
+ myscancfg->coord.width &= 0xffff;
+ break;
+ case ST_TA:
+ case ST_NEG:
+ myscancfg->coord.left += scan.ser;
+ break;
+ }
+
+ /*a11b */
+ if ((myscancfg->coord.width & 1) != 0)
+ myscancfg->coord.width++;
+
+ myscancfg->coord.top = 100;
+ myscancfg->coord.height = 30;
+
+ bytes_per_line = myscancfg->coord.width * 3;
+
+ /*a1e8 */
+ myscancfg->v157c = bytes_per_line;
+ myscancfg->bytesperline = bytes_per_line;
+
+ scanbuffer =
+ (SANE_Byte *) malloc (((myscancfg->coord.height + 16) * bytes_per_line) *
+ sizeof (SANE_Byte));
+ if (scanbuffer == NULL)
+ return ERROR;
+
+ /* Scan image */
+ myCalib->shading_enabled = FALSE;
+ /*Head_Relocate(dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, 5500); */
+
+ for (a = 0; a < 10; a++)
+ {
+ for (C = CL_RED; C <= CL_BLUE; C++)
+ {
+ myCalibTable.pag[C] = 3;
+ myCalibTable.vgag1[C] = 4;
+ myCalibTable.vgag2[C] = 4;
+ myCalibTable.edcg1[C] = a * 20;
+ }
+
+ dbg_ScanParams (myscancfg);
+
+ Head_Relocate (dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD,
+ 5000);
+ rst =
+ RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, scanbuffer,
+ myCalib, 0x20000000, gainmode);
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+
+ if (rst != ERROR)
+ {
+ char name[30];
+ snprintf (name, 30, "calibtest-%i.tiff", a);
+ dbg_tiff_save (name,
+ myscancfg->coord.width,
+ myscancfg->coord.height,
+ myscancfg->depth,
+ CM_COLOR,
+ myscancfg->resolution_x,
+ myscancfg->resolution_y,
+ scanbuffer,
+ (myscancfg->coord.height + 16) * bytes_per_line);
+ }
+ }
+
+ free (scanbuffer);
+
+ exit (0);
+ return OK;
+}
+
+static SANE_Int
+hp4370_prueba (struct st_device *dev)
+{
+ SANE_Int rst;
+ SANE_Int data = 0x0530, a;
+ SANE_Int transferred;
+ SANE_Byte buffer[512];
+
+ for (a = 0; a < 256; a++)
+ data_lsb_set (buffer + (a * 2), 0x9d7, 2);
+
+ rst = IWrite_Word (dev->usb_handle, 0x0000, data, 0x0800);
+ RTS_DMA_Enable_Write (dev, 0x4, 512, 0);
+ Bulk_Operation (dev, BLK_WRITE, 512, buffer, &transferred);
+
+ return rst;
+}
+
+static SANE_Int
+Calib_BlackShading_jkd (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib,
+ struct st_scanparams *scancfg)
+{
+ struct st_calibration_config *calibcfg;
+ struct st_gain_offset myCalibTable;
+ struct st_scanparams *myscancfg;
+ SANE_Byte *myRegs; /*f1bc */
+ SANE_Int bytes_per_line;
+ /**/ SANE_Int x, y, a, C;
+ SANE_Byte *scanbuffer; /*f164 */
+ double sumatorio;
+ SANE_Int gainmode;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "> Calib_BlackShading_jkd(*myCalib)\n");
+
+ bzero (&myCalibTable, sizeof (struct st_gain_offset));
+ for (C = CL_RED; C <= CL_BLUE; C++)
+ {
+ myCalibTable.pag[C] = 3;
+ myCalibTable.vgag1[C] = 4;
+ myCalibTable.vgag2[C] = 4;
+ }
+
+ calibcfg =
+ (struct st_calibration_config *)
+ malloc (sizeof (struct st_calibration_config));
+ memset (calibcfg, 0x30, sizeof (struct st_calibration_config));
+
+ myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ memcpy (myscancfg, scancfg, sizeof (struct st_scanparams));
+
+ myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x,
+ myscancfg->depth);
+ gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype);
+
+ Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode);
+
+ rst = OK;
+
+ switch (scan.scantype)
+ {
+ case ST_NORMAL:
+ /*a184 */
+ myscancfg->coord.left += scan.ser;
+ myscancfg->coord.width &= 0xffff;
+ break;
+ case ST_TA:
+ case ST_NEG:
+ myscancfg->coord.left += scan.ser;
+ break;
+ }
+
+ /*a11b */
+ if ((myscancfg->coord.width & 1) != 0)
+ myscancfg->coord.width++;
+
+ myscancfg->coord.top = 1;
+ myscancfg->coord.height = calibcfg->BShadingHeight;
+
+ bytes_per_line = myscancfg->coord.width * 3;
+
+ /*a1e8 */
+ myscancfg->v157c = bytes_per_line;
+ myscancfg->bytesperline = bytes_per_line;
+
+ scanbuffer =
+ (SANE_Byte *) malloc (((myscancfg->coord.height + 16) * bytes_per_line) *
+ sizeof (SANE_Byte));
+ if (scanbuffer == NULL)
+ return ERROR;
+
+ /* Turn off lamp */
+ Lamp_Status_Set (dev, NULL, FALSE, FLB_LAMP);
+ usleep (200 * 1000);
+
+ /* Scan image */
+ myCalib->shading_enabled = FALSE;
+ rst =
+ RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, scanbuffer, myCalib,
+ 0x101, gainmode);
+
+ /* Turn on lamp again */
+ if (scan.scantype != ST_NORMAL)
+ {
+ Lamp_Status_Set (dev, NULL, FALSE, TMA_LAMP);
+ usleep (1000 * 1000);
+ }
+ else
+ Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP);
+
+ if (rst != ERROR)
+ {
+ jkd_black = (SANE_Byte *) malloc (bytes_per_line);
+
+ if (jkd_black != NULL)
+ {
+ jkd_blackbpl = bytes_per_line;
+
+ for (x = 0; x < bytes_per_line; x++)
+ {
+ sumatorio = 0;
+
+ for (y = 0; y < myscancfg->coord.height + 16; y++)
+ sumatorio += scanbuffer[x + (bytes_per_line * y)];
+
+ sumatorio /= myscancfg->coord.height + 16;
+ a = sumatorio;
+ *(jkd_black + x) = _B0 (a);
+ }
+ }
+
+ /*if (RTS_Debug->SaveCalibFile != FALSE) */
+ {
+ dbg_tiff_save ("blackshading_jkd.tiff",
+ myscancfg->coord.width,
+ myscancfg->coord.height,
+ myscancfg->depth,
+ CM_COLOR,
+ myscancfg->resolution_x,
+ myscancfg->resolution_y,
+ scanbuffer,
+ (myscancfg->coord.height + 16) * bytes_per_line);
+ }
+ }
+
+ free (scanbuffer);
+
+ return OK;
+}
+
+static SANE_Int
+Calib_test (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib, struct st_scanparams *scancfg)
+{
+ struct st_calibration_config *calibcfg;
+ struct st_gain_offset myCalibTable;
+ struct st_scanparams *myscancfg;
+ SANE_Byte *myRegs; /*f1bc */
+ SANE_Int bytes_per_line;
+ /**/ SANE_Int a, C;
+ SANE_Byte *scanbuffer; /*f164 */
+ SANE_Int gainmode;
+ SANE_Int rst;
+
+ DBG (DBG_FNC, "> Calib_test(*myCalib)\n");
+
+ bzero (&myCalibTable, sizeof (struct st_gain_offset));
+
+ calibcfg =
+ (struct st_calibration_config *)
+ malloc (sizeof (struct st_calibration_config));
+ memset (calibcfg, 0x30, sizeof (struct st_calibration_config));
+
+ myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams));
+ memcpy (myscancfg, scancfg, sizeof (struct st_scanparams));
+
+ myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte));
+ memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
+
+ Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x,
+ myscancfg->depth);
+ gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype);
+
+ Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode);
+
+ rst = OK;
+
+ switch (scan.scantype)
+ {
+ case ST_NORMAL:
+ /*a184 */
+ myscancfg->coord.left += scan.ser;
+ myscancfg->coord.width &= 0xffff;
+ break;
+ case ST_TA:
+ case ST_NEG:
+ myscancfg->coord.left += scan.ser;
+ break;
+ }
+
+ /*a11b */
+ if ((myscancfg->coord.width & 1) != 0)
+ myscancfg->coord.width++;
+
+ myscancfg->coord.top = 100;
+ myscancfg->coord.height = 30;
+
+ bytes_per_line = myscancfg->coord.width * 3;
+
+ /*a1e8 */
+ myscancfg->v157c = bytes_per_line;
+ myscancfg->bytesperline = bytes_per_line;
+
+ scanbuffer =
+ (SANE_Byte *) malloc (((myscancfg->coord.height + 16) * bytes_per_line) *
+ sizeof (SANE_Byte));
+ if (scanbuffer == NULL)
+ return ERROR;
+
+ /* Scan image */
+ myCalib->shading_enabled = FALSE;
+ /*Head_Relocate(dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, 5500); */
+
+ for (a = 0; a < 10; a++)
+ {
+ for (C = CL_RED; C <= CL_BLUE; C++)
+ {
+ myCalibTable.pag[C] = 3;
+ myCalibTable.vgag1[C] = 4;
+ myCalibTable.vgag2[C] = 4;
+ myCalibTable.edcg1[C] = a * 20;
+ }
+
+ Head_Relocate (dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD,
+ 5000);
+ rst =
+ RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, scanbuffer,
+ myCalib, 0x20000000, gainmode);
+ Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove);
+
+ if (rst != ERROR)
+ {
+ char name[30];
+ snprintf (name, 30, "calibtest-%i.tiff", a);
+ dbg_tiff_save (name,
+ myscancfg->coord.width,
+ myscancfg->coord.height,
+ myscancfg->depth,
+ CM_COLOR,
+ myscancfg->resolution_x,
+ myscancfg->resolution_y,
+ scanbuffer,
+ (myscancfg->coord.height + 16) * bytes_per_line);
+ }
+ }
+
+ free (scanbuffer);
+
+ exit (0);
+ return OK;
+}
+
+static void
+prueba (SANE_Byte * a)
+{
+ /* SANE_Byte p[] = {}; */
+ /*int z = 69; */
+
+ /*(a + 11) = 0x0; */
+ /*a[1] = a[1] | 0x40; */
+
+ /*memcpy(a, &p, sizeof(p)); */
+
+ /*memcpy(a + 0x12, p, 10); */
+ /*a[0x146] &= 0xdf; */
+
+}
+
+void
+shadingtest1 (struct st_device *dev, SANE_Byte * Regs,
+ struct st_calibration *myCalib)
+{
+ USHORT *buffer;
+ int a;
+ int bit[2];
+
+ DBG (DBG_FNC, "+ shadingtest1(*Regs, *myCalib):\n");
+
+ if ((Regs == NULL) || (myCalib == NULL))
+ return;
+
+ RTS_DMA_Reset (dev);
+
+ bit[0] = (Regs[0x60b] >> 6) & 1;
+ bit[1] = (Regs[0x60b] >> 4) & 1;
+ Regs[0x060b] &= 0xaf;
+
+ Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]);
+
+ Regs[0x1cf] = 0; /* reset config. By default black shading disabled and pixel-rate */
+ /*Regs[0x1cf] |= 2; shadingbase 0x2000 */
+ Regs[0x1cf] |= 4; /* White shading enabled */
+ Regs[0x1cf] |= 0x20; /* 16 bits per channel */
+
+ Write_Byte (dev->usb_handle, 0xe9cf, Regs[0x01cf]);
+
+ buffer = (USHORT *) malloc (sizeof (USHORT) * myCalib->shadinglength);
+
+ DBG (DBG_FNC, " -> shading length = %i\n", myCalib->shadinglength);
+
+ /* fill buffer */
+ for (a = 0; a < myCalib->shadinglength; a++)
+ buffer[a] = RTS_Debug->shd + (a * 500);
+
+ for (a = 0; a < 3; a++)
+ {
+ RTS_DMA_Write (dev, a | 0x14, 0,
+ sizeof (USHORT) * myCalib->shadinglength,
+ (SANE_Byte *) buffer);
+ }
+
+ data_bitset (&Regs[0x60b], 0x40, bit[0]); /*-x------*/
+ data_bitset (&Regs[0x60b], 0x10, bit[1]); /*---x----*/
+
+ Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]);
+
+ DBG (DBG_FNC, "- shadingtest1\n");
+}
+
+#endif
+
+#endif /* RTS8822_CORE */
diff --git a/backend/hp3900_sane.c b/backend/hp3900_sane.c
new file mode 100644
index 0000000..a1c381c
--- /dev/null
+++ b/backend/hp3900_sane.c
@@ -0,0 +1,2734 @@
+/* HP Scanjet 3900 series - SANE Backend controller
+ Copyright (C) 2005-2009 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* Backend Code for SANE*/
+#define HP3900_CONFIG_FILE "hp3900.conf"
+#define GAMMA_DEFAULT 1.0
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_debug.h"
+
+#include "hp3900_rts8822.c"
+
+struct st_convert
+{
+ SANE_Int colormode;
+ SANE_Int depth;
+ SANE_Int threshold;
+ SANE_Int negative;
+ SANE_Int real_depth;
+};
+
+/* options enumerator */
+typedef enum
+{
+ opt_begin = 0,
+
+ grp_geometry,
+ opt_tlx, opt_tly, opt_brx, opt_bry,
+ opt_resolution,
+
+ /* gamma tables */
+ opt_gamma_red,
+ opt_gamma_green,
+ opt_gamma_blue,
+
+ opt_scantype,
+ opt_colormode,
+ opt_depth,
+ opt_threshold,
+
+ /* debugging options */
+ grp_debug,
+ opt_model,
+ opt_negative,
+ opt_nogamma,
+ opt_nowshading,
+ opt_realdepth,
+ opt_emulategray,
+ opt_nowarmup,
+ opt_dbgimages,
+ opt_reset,
+
+ /* device information */
+ grp_info,
+ opt_chipname,
+ opt_chipid,
+ opt_scancount,
+ opt_infoupdate,
+
+ /* supported buttons. RTS8822 supports up to 6 buttons */
+ grp_sensors,
+ opt_button_0,
+ opt_button_1,
+ opt_button_2,
+ opt_button_3,
+ opt_button_4,
+ opt_button_5,
+
+ opt_count
+} EOptionIndex;
+
+/* linked list of SANE_Device structures */
+typedef struct TDevListEntry
+{
+ struct TDevListEntry *pNext;
+ SANE_Device dev;
+ char *devname;
+} TDevListEntry;
+
+typedef struct
+{
+ char *pszVendor;
+ char *pszName;
+} TScannerModel;
+
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+} TOptionValue;
+
+typedef struct
+{
+ SANE_Int model;
+ SANE_Option_Descriptor aOptions[opt_count];
+ TOptionValue aValues[opt_count];
+ struct params ScanParams;
+
+ /* lists */
+ SANE_String_Const *list_colormodes;
+ SANE_Int *list_depths;
+ SANE_String_Const *list_models;
+ SANE_Int *list_resolutions;
+ SANE_String_Const *list_sources;
+
+ SANE_Word *aGammaTable[3]; /* a 16-to-16 bit color lookup table */
+ SANE_Range rng_gamma;
+
+ /* reading image */
+ SANE_Byte *image;
+ SANE_Byte *rest;
+ SANE_Int rest_amount;
+ SANE_Int mylin;
+
+ /* convertion settings */
+ struct st_convert cnv;
+
+ /* ranges */
+ SANE_Range rng_threshold;
+ SANE_Range rng_horizontal;
+ SANE_Range rng_vertical;
+
+ SANE_Int scan_count;
+ SANE_Int fScanning; /* TRUE if actively scanning */
+} TScanner;
+
+/* functions to manage backend's options */
+static void options_init (TScanner * scanner);
+static void options_free (TScanner * scanner);
+
+/* devices listing */
+static SANE_Int _ReportDevice (TScannerModel * pModel,
+ const char *pszDeviceName);
+static SANE_Status attach_one_device (SANE_String_Const devname);
+
+/* capabilities */
+static SANE_Status bknd_colormodes (TScanner * scanner, SANE_Int model);
+static void bknd_constrains (TScanner * scanner, SANE_Int source,
+ SANE_Int type);
+static SANE_Status bknd_depths (TScanner * scanner, SANE_Int model);
+static SANE_Status bknd_info (TScanner * scanner);
+static SANE_Status bknd_models (TScanner * scanner);
+static SANE_Status bknd_resolutions (TScanner * scanner, SANE_Int model);
+static SANE_Status bknd_sources (TScanner * scanner, SANE_Int model);
+
+/* convertions */
+static void Color_Negative (SANE_Byte * buffer, SANE_Int size,
+ SANE_Int depth);
+static void Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth);
+static void Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size,
+ SANE_Int threshold);
+static void Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size,
+ SANE_Byte * to_buffer);
+
+/* gamma functions */
+static void gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size,
+ SANE_Int depth);
+static SANE_Int gamma_create (TScanner * s, double gamma);
+static void gamma_free (TScanner * s);
+
+static SANE_Int Get_Colormode (SANE_String colormode);
+static SANE_Int Get_Model (SANE_String model);
+static SANE_Int Get_Source (SANE_String source);
+static SANE_Int GetUSB_device_model (SANE_String_Const name);
+static size_t max_string_size (const SANE_String_Const strings[]);
+
+static SANE_Status get_button_status (TScanner * s);
+
+/* reading buffers */
+static SANE_Status img_buffers_alloc (TScanner * scanner, SANE_Int size);
+static SANE_Status img_buffers_free (TScanner * scanner);
+
+static SANE_Status option_get (TScanner * scanner, SANE_Int optid,
+ void *result);
+static SANE_Status option_set (TScanner * scanner, SANE_Int optid,
+ void *value, SANE_Int * pInfo);
+
+static void Set_Coordinates (SANE_Int scantype, SANE_Int resolution,
+ struct st_coords *coords);
+static SANE_Int set_ScannerModel (SANE_Int proposed, SANE_Int product,
+ SANE_Int vendor);
+static void Silent_Compile (void);
+static SANE_Status Translate_coords (struct st_coords *coords);
+
+/* SANE functions */
+void sane_cancel (SANE_Handle h);
+void sane_close (SANE_Handle h);
+SANE_Status sane_control_option (SANE_Handle h, SANE_Int n,
+ SANE_Action Action, void *pVal,
+ SANE_Int * pInfo);
+void sane_exit (void);
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle h,
+ SANE_Int n);
+SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p);
+SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd);
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h);
+SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen,
+ SANE_Int * len);
+SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking);
+SANE_Status sane_start (SANE_Handle h);
+
+/* variables */
+static struct st_device *device = NULL;
+static TDevListEntry *_pFirstSaneDev = 0;
+static SANE_Int iNumSaneDev = 0;
+static const SANE_Device **_pSaneDevList = 0;
+
+/* Own functions */
+
+static SANE_Status
+bknd_resolutions (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_resolutions(*scanner, model=%i)\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_Int *res = NULL;
+
+ switch (model)
+ {
+ case BQ5550:
+ case UA4900:
+ {
+ SANE_Int myres[] = { 8, 50, 75, 100, 150, 200, 300, 600, 1200 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+
+ case HPG2710:
+ case HP3800:
+ {
+ /* 1200 and 2400 dpi are disabled until problems are solved */
+ SANE_Int myres[] = { 7, 50, 75, 100, 150, 200, 300, 600 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ {
+ SANE_Int myres[] =
+ { 10, 50, 75, 100, 150, 200, 300, 600, 1200, 2400, 4800 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+
+ default: /* HP3970 & HP4070 & UA4900 */
+ {
+ SANE_Int myres[] =
+ { 9, 50, 75, 100, 150, 200, 300, 600, 1200, 2400 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+ }
+
+ if (res != NULL)
+ {
+ if (scanner->list_resolutions != NULL)
+ free (scanner->list_resolutions);
+
+ scanner->list_resolutions = res;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_models (TScanner * scanner)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_models:\n");
+
+ if (scanner != NULL)
+ {
+ SANE_String_Const *model = NULL;
+
+ /* at this moment all devices use the same list */
+ SANE_String_Const mymodel[] =
+ { "HP3800", "HP3970", "HP4070", "HP4370", "UA4900", "HPG3010",
+"BQ5550", "HPG2710", "HPG3110", 0 };
+
+ /* allocate space to save list */
+ model = (SANE_String_Const *) malloc (sizeof (mymodel));
+ if (model != NULL)
+ memcpy (model, &mymodel, sizeof (mymodel));
+
+ if (model != NULL)
+ {
+ /* free previous list */
+ if (scanner->list_models != NULL)
+ free (scanner->list_models);
+
+ /* set new list */
+ scanner->list_models = model;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_colormodes (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_colormodes(*scanner, model=%i)\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_String_Const *colormode = NULL;
+
+ /* at this moment all devices use the same list */
+ SANE_String_Const mycolormode[] =
+ { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, 0 };
+
+ /* silence gcc */
+ model = model;
+
+ colormode = (SANE_String_Const *) malloc (sizeof (mycolormode));
+ if (colormode != NULL)
+ memcpy (colormode, &mycolormode, sizeof (mycolormode));
+
+ if (colormode != NULL)
+ {
+ if (scanner->list_colormodes != NULL)
+ free (scanner->list_colormodes);
+
+ scanner->list_colormodes = colormode;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_sources (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_sources(*scanner, model=%i)\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_String_Const *source = NULL;
+
+ switch (model)
+ {
+ case UA4900:
+ {
+ SANE_String_Const mysource[] = { SANE_I18N ("Flatbed"), 0 };
+ source = (SANE_String_Const *) malloc (sizeof (mysource));
+ if (source != NULL)
+ memcpy (source, &mysource, sizeof (mysource));
+ }
+ break;
+ default: /* hp3970, hp4070, hp4370 and others */
+ {
+ SANE_String_Const mysource[] =
+ { SANE_I18N ("Flatbed"), SANE_I18N ("Slide"),
+SANE_I18N ("Negative"), 0 };
+ source = (SANE_String_Const *) malloc (sizeof (mysource));
+ if (source != NULL)
+ memcpy (source, &mysource, sizeof (mysource));
+ }
+ break;
+ }
+
+ if (source != NULL)
+ {
+ if (scanner->list_sources != NULL)
+ free (scanner->list_sources);
+
+ scanner->list_sources = source;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_depths (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_depths(*scanner, model=%i\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_Int *depth = NULL;
+
+ /* at this moment all devices use the same list */
+ SANE_Int mydepth[] = { 2, 8, 16 }; /*{3, 8, 12, 16}; */
+
+ /* silence gcc */
+ model = model;
+
+ depth = (SANE_Int *) malloc (sizeof (mydepth));
+ if (depth != NULL)
+ memcpy (depth, &mydepth, sizeof (mydepth));
+
+ if (depth != NULL)
+ {
+ if (scanner->list_depths != NULL)
+ free (scanner->list_depths);
+
+ scanner->list_depths = depth;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_info (TScanner * scanner)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_info(*scanner)");
+
+ if (scanner != NULL)
+ {
+ char data[256];
+
+ /* update chipset name */
+ Chipset_Name (device, data, 255);
+ if (scanner->aValues[opt_chipname].s != NULL)
+ {
+ free (scanner->aValues[opt_chipname].s);
+ scanner->aValues[opt_chipname].s = NULL;
+ }
+
+ scanner->aValues[opt_chipname].s = strdup (data);
+ scanner->aOptions[opt_chipname].size = strlen (data) + 1;
+
+ /* update chipset id */
+ scanner->aValues[opt_chipid].w = Chipset_ID (device);
+
+ /* update scans counter */
+ scanner->aValues[opt_scancount].w = RTS_ScanCounter_Get (device);
+
+ rst = SANE_STATUS_GOOD;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+GetUSB_device_model (SANE_String_Const name)
+{
+ SANE_Int usbid, model;
+
+ /* default model is unknown */
+ model = -1;
+
+ /* open usb device */
+ if (sanei_usb_open (name, &usbid) == SANE_STATUS_GOOD)
+ {
+ SANE_Int vendor, product;
+
+ if (sanei_usb_get_vendor_product (usbid, &vendor, &product) ==
+ SANE_STATUS_GOOD)
+ model = Device_get (product, vendor);
+
+ sanei_usb_close (usbid);
+ }
+
+ return model;
+}
+
+static void
+Silent_Compile (void)
+{
+ /*
+ There are some functions in hp3900_rts8822.c that aren't used yet.
+ To avoid compilation warnings we will use them here
+ */
+
+ SANE_Byte a = 1;
+
+ if (a == 0)
+ {
+ Buttons_Status (device);
+ Calib_WriteTable (device, NULL, 0, 0);
+ Gamma_GetTables (device, NULL);
+ }
+}
+
+static void
+bknd_constrains (TScanner * scanner, SANE_Int source, SANE_Int type)
+{
+ struct st_coords *coords = Constrains_Get (device, source);
+
+ if ((coords != NULL) && (scanner != NULL))
+ {
+ switch (type)
+ {
+ case 1: /* Y */
+ scanner->rng_vertical.max = coords->height;
+ break;
+ default: /* X */
+ scanner->rng_horizontal.max = coords->width;
+ break;
+ }
+ }
+}
+
+static SANE_Status
+img_buffers_free (TScanner * scanner)
+{
+ if (scanner != NULL)
+ {
+ if (scanner->image != NULL)
+ {
+ free (scanner->image);
+ scanner->image = NULL;
+ }
+
+ if (scanner->rest != NULL)
+ {
+ free (scanner->rest);
+ scanner->rest = NULL;
+ }
+
+ scanner->rest_amount = 0;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+img_buffers_alloc (TScanner * scanner, SANE_Int size)
+{
+ SANE_Status rst;
+
+ /* default result at this point */
+ rst = SANE_STATUS_INVAL;
+
+ if (scanner != NULL)
+ {
+ /* default result at this point */
+ rst = SANE_STATUS_NO_MEM;
+
+ /* free previous allocs */
+ img_buffers_free (scanner);
+
+ scanner->image = (SANE_Byte *) malloc (size * sizeof (SANE_Byte));
+ if (scanner->image != NULL)
+ {
+ scanner->rest = (SANE_Byte *) malloc (size * sizeof (SANE_Byte));
+ if (scanner->rest != NULL)
+ rst = SANE_STATUS_GOOD; /* ok !! */
+ }
+
+ if (rst != SANE_STATUS_GOOD)
+ img_buffers_free (scanner);
+ }
+
+ return rst;
+}
+
+static SANE_Int
+set_ScannerModel (SANE_Int proposed, SANE_Int product, SANE_Int vendor)
+{
+ /* This function will set the device behaviour */
+
+ SANE_Int current = Device_get (product, vendor);
+ char *sdevname[10] =
+ { "Unknown", "HP3970", "HP4070", "HP4370", "UA4900", "HP3800", "HPG3010",
+"BQ5550", "HPG2710", "HPG3110" };
+
+ DBG (DBG_FNC,
+ "> set_ScannerModel(proposed=%i, product=%04x, vendor=%04x)\n",
+ proposed, product, vendor);
+
+ if (proposed < 0)
+ {
+ if ((current < 0) || (current >= DEVSCOUNT))
+ {
+ DBG (DBG_VRB, " -> Unknown device. Defaulting to HP3970...\n");
+ RTS_Debug->dev_model = HP3970;
+ }
+ else
+ {
+ RTS_Debug->dev_model = current;
+ DBG (DBG_VRB, " -> Device model is %s\n", sdevname[current + 1]);
+ }
+ }
+ else
+ {
+ if (proposed < DEVSCOUNT)
+ {
+ RTS_Debug->dev_model = proposed;
+ DBG (DBG_VRB, " -> Device %s , treating as %s ...\n",
+ sdevname[current + 1], sdevname[proposed + 1]);
+ }
+ else
+ {
+ if ((current >= 0) && (current < DEVSCOUNT))
+ {
+ RTS_Debug->dev_model = current;
+ DBG (DBG_VRB,
+ " -> Device not supported. Defaulting to %s ...\n",
+ sdevname[current + 1]);
+ }
+ else
+ {
+ RTS_Debug->dev_model = HP3970;
+ DBG (DBG_VRB,
+ "-> Device not supported. Defaulting to HP3970...\n");
+ }
+ }
+ }
+
+ return OK;
+}
+
+static void
+Set_Coordinates (SANE_Int scantype, SANE_Int resolution,
+ struct st_coords *coords)
+{
+ struct st_coords *limits = Constrains_Get (device, scantype);
+
+ DBG (DBG_FNC, "> Set_Coordinates(res=%i, *coords):\n", resolution);
+
+ if (coords->left == -1)
+ coords->left = 0;
+
+ if (coords->width == -1)
+ coords->width = limits->width;
+
+ if (coords->top == -1)
+ coords->top = 0;
+
+ if (coords->height == -1)
+ coords->height = limits->height;
+
+ DBG (DBG_FNC, " -> Coords [MM] : xy(%i, %i) wh(%i, %i)\n", coords->left,
+ coords->top, coords->width, coords->height);
+
+ coords->left = MM_TO_PIXEL (coords->left, resolution);
+ coords->width = MM_TO_PIXEL (coords->width, resolution);
+ coords->top = MM_TO_PIXEL (coords->top, resolution);
+ coords->height = MM_TO_PIXEL (coords->height, resolution);
+
+ DBG (DBG_FNC, " -> Coords [px] : xy(%i, %i) wh(%i, %i)\n", coords->left,
+ coords->top, coords->width, coords->height);
+
+ Constrains_Check (device, resolution, scantype, coords);
+
+ DBG (DBG_FNC, " -> Coords [check]: xy(%i, %i) wh(%i, %i)\n", coords->left,
+ coords->top, coords->width, coords->height);
+}
+
+static void
+Color_Negative (SANE_Byte * buffer, SANE_Int size, SANE_Int depth)
+{
+ if (buffer != NULL)
+ {
+ SANE_Int a;
+ SANE_Int max_value = (1 << depth) - 1;
+
+ if (depth > 8)
+ {
+ USHORT *sColor = (void *) buffer;
+ for (a = 0; a < size / 2; a++)
+ {
+ *sColor = max_value - *sColor;
+ sColor++;
+ }
+ }
+ else
+ {
+ for (a = 0; a < size; a++)
+ *(buffer + a) = max_value - *(buffer + a);
+ }
+ }
+}
+
+static SANE_Status
+get_button_status (TScanner * s)
+{
+ if (s != NULL)
+ {
+ SANE_Int a, b, status, btn;
+
+ b = 1;
+ status = Buttons_Released (device) & 63;
+ for (a = 0; a < 6; a++)
+ {
+ if ((status & b) != 0)
+ {
+ btn = Buttons_Order (device, b);
+ if (btn != -1)
+ s->aValues[opt_button_0 + btn].w = SANE_TRUE;
+ }
+
+ b <<= 1;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size, SANE_Byte * to_buffer)
+{
+ if ((from_buffer != NULL) && (to_buffer != NULL))
+ {
+ SANE_Int a, b;
+
+ a = 1;
+ b = 0;
+
+ while (a < size)
+ {
+ *(to_buffer + b) = *(from_buffer + a);
+ a += 2;
+ b++;
+ }
+ }
+}
+
+static void
+Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size, SANE_Int threshold)
+{
+ /* code provided by tobias leutwein */
+
+ if (buffer != NULL)
+ {
+ SANE_Byte toBufferByte;
+ SANE_Int fromBufferPos_i = 0;
+ SANE_Int toBufferPos_i = 0;
+ SANE_Int bitPos_i;
+
+ while (fromBufferPos_i < size)
+ {
+ toBufferByte = 0;
+
+ for (bitPos_i = 7; bitPos_i != (-1); bitPos_i--)
+ {
+ if ((fromBufferPos_i < size)
+ && (buffer[fromBufferPos_i] < threshold))
+ toBufferByte |= (1u << bitPos_i);
+
+ fromBufferPos_i++;
+ }
+
+ buffer[toBufferPos_i] = toBufferByte;
+ toBufferPos_i++;
+ }
+ }
+}
+
+static void
+Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth)
+{
+ /* converts 3 color channel into 1 gray channel of specified bit depth */
+
+ if (buffer != NULL)
+ {
+ SANE_Int c, chn, chn_size;
+ SANE_Byte *ptr_src = NULL;
+ SANE_Byte *ptr_dst = NULL;
+ float data, chn_data;
+ float coef[3] = { 0.299, 0.587, 0.114 }; /* coefficients per channel */
+
+ chn_size = (depth > 8) ? 2 : 1;
+ ptr_src = (void *) buffer;
+ ptr_dst = (void *) buffer;
+
+ for (c = 0; c < size / (3 * chn_size); c++)
+ {
+ data = 0.;
+
+ /* get, apply coeffs and sum channels */
+ for (chn = 0; chn < 3; chn++)
+ {
+ chn_data = data_lsb_get (ptr_src + (chn * chn_size), chn_size);
+ data += (chn_data * coef[chn]);
+ }
+
+ /* save result */
+ data_lsb_set (ptr_dst, (SANE_Int) data, chn_size);
+
+ ptr_src += 3 * chn_size; /* next triplet */
+ ptr_dst += chn_size;
+ }
+ }
+}
+
+static void
+gamma_free (TScanner * s)
+{
+ DBG (DBG_FNC, "> gamma_free()\n");
+
+ if (s != NULL)
+ {
+ /* Destroy gamma tables */
+ SANE_Int a;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ if (s->aGammaTable[a] != NULL)
+ {
+ free (s->aGammaTable[a]);
+ s->aGammaTable[a] = NULL;
+ }
+ }
+ }
+}
+
+static SANE_Int
+gamma_create (TScanner * s, double gamma)
+{
+ SANE_Int rst = ERROR; /* by default */
+
+ DBG (DBG_FNC, "> gamma_create(*s)\n");
+
+ if (s != NULL)
+ {
+ SANE_Int a;
+ double value, c;
+
+ /* default result */
+ rst = OK;
+
+ /* destroy previus gamma tables */
+ gamma_free (s);
+
+ /* check gamma value */
+ if (gamma < 0)
+ gamma = GAMMA_DEFAULT;
+
+ /* allocate space for 16 bit gamma tables */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ s->aGammaTable[a] = malloc (65536 * sizeof (SANE_Word));
+ if (s->aGammaTable[a] == NULL)
+ {
+ rst = ERROR;
+ break;
+ }
+ }
+
+ if (rst == OK)
+ {
+ /* fill tables */
+ for (a = 0; a < 65536; a++)
+ {
+ value = (a / (65536. - 1));
+ value = pow (value, (1. / gamma));
+ value = value * (65536. - 1);
+
+ c = (SANE_Int) value;
+ if (c > (65536. - 1))
+ c = (65536. - 1);
+ else if (c < 0)
+ c = 0;
+
+ s->aGammaTable[CL_RED][a] = c;
+ s->aGammaTable[CL_GREEN][a] = c;
+ s->aGammaTable[CL_BLUE][a] = c;
+ }
+ }
+ else
+ gamma_free (s);
+ }
+
+ return rst;
+}
+
+static void
+gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size, SANE_Int depth)
+{
+ if ((s != NULL) && (buffer != NULL))
+ {
+ SANE_Int c;
+ SANE_Int dot_size = 3 * ((depth > 8) ? 2 : 1);
+ SANE_Byte *pColor = buffer;
+ USHORT *sColor = (void *) buffer;
+
+ if ((s->aGammaTable[CL_RED] != NULL)
+ && (s->aGammaTable[CL_GREEN] != NULL)
+ && (s->aGammaTable[CL_BLUE] != NULL))
+ {
+ for (c = 0; c < size / dot_size; c++)
+ {
+ if (depth > 8)
+ {
+ *sColor = s->aGammaTable[CL_RED][*sColor];
+ *(sColor + 1) = s->aGammaTable[CL_GREEN][*(sColor + 1)];
+ *(sColor + 2) = s->aGammaTable[CL_BLUE][*(sColor + 2)];
+ sColor += 3;
+ }
+ else
+ {
+ /* 8 bits gamma */
+ *pColor =
+ (s->aGammaTable[CL_RED][*pColor * 256] >> 8) & 0xff;
+ *(pColor + 1) =
+ (s->
+ aGammaTable[CL_GREEN][*(pColor + 1) * 256] >> 8) & 0xff;
+ *(pColor + 2) =
+ (s->
+ aGammaTable[CL_BLUE][*(pColor + 2) * 256] >> 8) & 0xff;
+ pColor += 3;
+ }
+ }
+ }
+ }
+}
+
+static SANE_Int
+Get_Model (SANE_String model)
+{
+ SANE_Int rst;
+
+ if (strcmp (model, "HP3800") == 0)
+ rst = HP3800;
+ else if (strcmp (model, "HPG2710") == 0)
+ rst = HPG2710;
+ else if (strcmp (model, "HP3970") == 0)
+ rst = HP3970;
+ else if (strcmp (model, "HP4070") == 0)
+ rst = HP4070;
+ else if (strcmp (model, "HP4370") == 0)
+ rst = HP4370;
+ else if (strcmp (model, "HPG3010") == 0)
+ rst = HPG3010;
+ else if (strcmp (model, "HPG3110") == 0)
+ rst = HPG3110;
+ else if (strcmp (model, "UA4900") == 0)
+ rst = UA4900;
+ else if (strcmp (model, "BQ5550") == 0)
+ rst = BQ5550;
+ else
+ rst = HP3970; /* default */
+
+ return rst;
+}
+
+static SANE_Int
+Get_Source (SANE_String source)
+{
+ SANE_Int rst;
+
+ if (strcmp (source, SANE_I18N ("Flatbed")) == 0)
+ rst = ST_NORMAL;
+ else if (strcmp (source, SANE_I18N ("Slide")) == 0)
+ rst = ST_TA;
+ else if (strcmp (source, SANE_I18N ("Negative")) == 0)
+ rst = ST_NEG;
+ else
+ rst = ST_NORMAL; /* default */
+
+ return rst;
+}
+
+static SANE_Int
+Get_Colormode (SANE_String colormode)
+{
+ SANE_Int rst;
+
+ if (strcmp (colormode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ rst = CM_COLOR;
+ else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ rst = CM_GRAY;
+ else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ rst = CM_LINEART;
+ else
+ rst = CM_COLOR; /* default */
+
+ return rst;
+}
+
+static SANE_Status
+Translate_coords (struct st_coords *coords)
+{
+ SANE_Int data;
+
+ DBG (DBG_FNC, "> Translate_coords(*coords)\n");
+
+ if ((coords->left < 0) || (coords->top < 0) ||
+ (coords->width < 0) || (coords->height < 0))
+ return SANE_STATUS_INVAL;
+
+ if (coords->width < coords->left)
+ {
+ data = coords->left;
+ coords->left = coords->width;
+ coords->width = data;
+ }
+
+ if (coords->height < coords->top)
+ {
+ data = coords->top;
+ coords->top = coords->height;
+ coords->height = data;
+ }
+
+ coords->width -= coords->left;
+ coords->height -= coords->top;
+
+ if (coords->width == 0)
+ coords->width++;
+
+ if (coords->height == 0)
+ coords->height++;
+
+ return SANE_STATUS_GOOD;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ DBG (DBG_FNC, "> max_string_size:\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+static void
+options_free (TScanner * scanner)
+{
+ /* frees all information contained in controls */
+
+ DBG (DBG_FNC, "> options_free\n");
+
+ if (scanner != NULL)
+ {
+ SANE_Int i;
+ SANE_Option_Descriptor *pDesc;
+ TOptionValue *pVal;
+
+ /* free gamma tables */
+ gamma_free (scanner);
+
+ /* free lists */
+ if (scanner->list_resolutions != NULL)
+ free (scanner->list_resolutions);
+
+ if (scanner->list_depths != NULL)
+ free (scanner->list_depths);
+
+ if (scanner->list_sources != NULL)
+ free (scanner->list_sources);
+
+ if (scanner->list_colormodes != NULL)
+ free (scanner->list_colormodes);
+
+ if (scanner->list_models != NULL)
+ free (scanner->list_models);
+
+ /* free values in certain controls */
+ for (i = opt_begin; i < opt_count; i++)
+ {
+ pDesc = &scanner->aOptions[i];
+ pVal = &scanner->aValues[i];
+
+ if (pDesc->type == SANE_TYPE_STRING)
+ {
+ if (pVal->s != NULL)
+ free (pVal->s);
+ }
+ }
+ }
+}
+
+static void
+options_init (TScanner * scanner)
+{
+ /* initializes all controls */
+
+ DBG (DBG_FNC, "> options_init\n");
+
+ if (scanner != NULL)
+ {
+ SANE_Int i;
+ SANE_Option_Descriptor *pDesc;
+ TOptionValue *pVal;
+
+ /* set gamma */
+ gamma_create (scanner, 2.2);
+
+ /* color convertion */
+ scanner->cnv.colormode = -1;
+ scanner->cnv.negative = FALSE;
+ scanner->cnv.threshold = 40;
+ scanner->cnv.real_depth = FALSE;
+ scanner->cnv.depth = -1;
+
+ /* setting threshold */
+ scanner->rng_threshold.min = 0;
+ scanner->rng_threshold.max = 255;
+ scanner->rng_threshold.quant = 0;
+
+ /* setting gamma range (16 bits depth) */
+ scanner->rng_gamma.min = 0;
+ scanner->rng_gamma.max = 65535;
+ scanner->rng_gamma.quant = 0;
+
+ /* setting default horizontal constrain in milimeters */
+ scanner->rng_horizontal.min = 0;
+ scanner->rng_horizontal.max = 220;
+ scanner->rng_horizontal.quant = 1;
+
+ /* setting default vertical constrain in milimeters */
+ scanner->rng_vertical.min = 0;
+ scanner->rng_vertical.max = 300;
+ scanner->rng_vertical.quant = 1;
+
+ /* allocate option lists */
+ bknd_info (scanner);
+ bknd_colormodes (scanner, RTS_Debug->dev_model);
+ bknd_depths (scanner, RTS_Debug->dev_model);
+ bknd_models (scanner);
+ bknd_resolutions (scanner, RTS_Debug->dev_model);
+ bknd_sources (scanner, RTS_Debug->dev_model);
+
+ /* By default preview scan */
+ scanner->ScanParams.scantype = ST_NORMAL;
+ scanner->ScanParams.colormode = CM_COLOR;
+ scanner->ScanParams.resolution_x = 75;
+ scanner->ScanParams.resolution_y = 75;
+ scanner->ScanParams.coords.left = 0;
+ scanner->ScanParams.coords.top = 0;
+ scanner->ScanParams.coords.width = 220;
+ scanner->ScanParams.coords.height = 300;
+ scanner->ScanParams.depth = 8;
+ scanner->ScanParams.channel = 0;
+
+ for (i = opt_begin; i < opt_count; i++)
+ {
+ pDesc = &scanner->aOptions[i];
+ pVal = &scanner->aValues[i];
+
+ /* defaults */
+ pDesc->name = "";
+ pDesc->title = "";
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = 0;
+
+ switch (i)
+ {
+ case opt_begin:
+ pDesc->title = SANE_TITLE_NUM_OPTIONS;
+ pDesc->desc = SANE_DESC_NUM_OPTIONS;
+ pDesc->cap = SANE_CAP_SOFT_DETECT;
+ pVal->w = (SANE_Word) opt_count;
+ break;
+
+ case grp_geometry:
+ pDesc->name = SANE_NAME_GEOMETRY;
+ pDesc->title = SANE_TITLE_GEOMETRY;
+ pDesc->desc = SANE_DESC_GEOMETRY;
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_tlx:
+ pDesc->name = SANE_NAME_SCAN_TL_X;
+ pDesc->title = SANE_TITLE_SCAN_TL_X;
+ pDesc->desc = SANE_DESC_SCAN_TL_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_horizontal;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = 0;
+ break;
+
+ case opt_tly:
+ pDesc->name = SANE_NAME_SCAN_TL_Y;
+ pDesc->title = SANE_TITLE_SCAN_TL_Y;
+ pDesc->desc = SANE_DESC_SCAN_TL_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_vertical;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = 0;
+ break;
+
+ case opt_brx:
+ pDesc->name = SANE_NAME_SCAN_BR_X;
+ pDesc->title = SANE_TITLE_SCAN_BR_X;
+ pDesc->desc = SANE_DESC_SCAN_BR_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_horizontal;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->rng_horizontal.max;
+ break;
+
+ case opt_bry:
+ pDesc->name = SANE_NAME_SCAN_BR_Y;
+ pDesc->title = SANE_TITLE_SCAN_BR_Y;
+ pDesc->desc = SANE_DESC_SCAN_BR_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_vertical;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->rng_vertical.max;
+ break;
+
+ case opt_resolution:
+ pDesc->name = SANE_NAME_SCAN_RESOLUTION;
+ pDesc->title = SANE_TITLE_SCAN_RESOLUTION;
+ pDesc->desc = SANE_DESC_SCAN_RESOLUTION;
+ pDesc->unit = SANE_UNIT_DPI;
+ pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pDesc->constraint.word_list = scanner->list_resolutions;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->list_resolutions[1];
+ break;
+
+ case opt_gamma_red:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_R;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_R;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_R;
+ pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word);
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_gamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = scanner->aGammaTable[CL_RED];
+ break;
+
+ case opt_gamma_green:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_G;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_G;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_G;
+ pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word);
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_gamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = scanner->aGammaTable[CL_GREEN];
+ break;
+
+ case opt_gamma_blue:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_B;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_B;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_B;
+ pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word);
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_gamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = scanner->aGammaTable[CL_BLUE];
+ break;
+
+ case opt_scantype:
+ pDesc->name = SANE_NAME_SCAN_SOURCE;
+ pDesc->title = SANE_TITLE_SCAN_SOURCE;
+ pDesc->desc = SANE_DESC_SCAN_SOURCE;
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = max_string_size (scanner->list_sources);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = scanner->list_sources;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (scanner->list_sources[0]);
+ break;
+
+ case opt_colormode:
+ pDesc->name = SANE_NAME_SCAN_MODE;
+ pDesc->title = SANE_TITLE_SCAN_MODE;
+ pDesc->desc = SANE_DESC_SCAN_MODE;
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = max_string_size (scanner->list_colormodes);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = scanner->list_colormodes;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (scanner->list_colormodes[0]);
+ break;
+
+ case opt_depth:
+ pDesc->name = SANE_NAME_BIT_DEPTH;
+ pDesc->title = SANE_TITLE_BIT_DEPTH;
+ pDesc->desc = SANE_DESC_BIT_DEPTH;
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_BIT;
+ pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pDesc->constraint.word_list = scanner->list_depths;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->list_depths[1];
+ break;
+
+ case opt_threshold:
+ pDesc->name = SANE_NAME_THRESHOLD;
+ pDesc->title = SANE_TITLE_THRESHOLD;
+ pDesc->desc = SANE_DESC_THRESHOLD;
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_threshold;
+ pDesc->cap |=
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_INACTIVE;
+ pVal->w = 0x80;
+ break;
+
+ /* debugging options */
+ case grp_debug:
+ pDesc->name = "grp_debug";
+ pDesc->title = SANE_I18N ("Debugging Options");
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = SANE_CAP_ADVANCED;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_model:
+ pDesc->name = "opt_model";
+ pDesc->title = SANE_I18N ("Scanner model");
+ pDesc->desc =
+ SANE_I18N
+ ("Allows to test device behaviour with other supported models");
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = max_string_size (scanner->list_models);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = scanner->list_models;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT |
+ SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (scanner->list_models[0]);
+ break;
+
+ case opt_negative:
+ pDesc->name = "opt_negative";
+ pDesc->title = SANE_I18N ("Negative");
+ pDesc->desc = SANE_I18N ("Image colours will be inverted");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_nogamma:
+ pDesc->name = "opt_nogamma";
+ pDesc->title = SANE_I18N ("Disable gamma correction");
+ pDesc->desc = SANE_I18N ("Gamma correction will be disabled");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_nowshading:
+ pDesc->name = "opt_nowshading";
+ pDesc->title = SANE_I18N ("Disable white shading correction");
+ pDesc->desc =
+ SANE_I18N ("White shading correction will be disabled");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_nowarmup:
+ pDesc->name = "opt_nowarmup";
+ pDesc->title = SANE_I18N ("Skip warmup process");
+ pDesc->desc = SANE_I18N ("Warmup process will be disabled");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_realdepth:
+ pDesc->name = "opt_realdepth";
+ pDesc->title = SANE_I18N ("Force real depth");
+ pDesc->desc =
+ SANE_I18N
+ ("If gamma is enabled, scans are always made in 16 bits depth to improve image quality and then converted to the selected depth. This option avoids depth emulation.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_emulategray:
+ pDesc->name = "opt_emulategray";
+ pDesc->title = SANE_I18N ("Emulate Grayscale");
+ pDesc->desc =
+ SANE_I18N
+ ("If enabled, image will be scanned in color mode and then converted to grayscale by software. This may improve image quality in some circumstances.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_dbgimages:
+ pDesc->name = "opt_dbgimages";
+ pDesc->title = SANE_I18N ("Save debugging images");
+ pDesc->desc =
+ SANE_I18N
+ ("If enabled, some images involved in scanner processing are saved to analyze them.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_reset:
+ pDesc->name = "opt_reset";
+ pDesc->title = SANE_I18N ("Reset chipset");
+ pDesc->desc = SANE_I18N ("Resets chipset data");
+ pDesc->type = SANE_TYPE_BUTTON;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.string_list = 0;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT;
+ pVal->w = 0;
+ break;
+
+ /* device information */
+ case grp_info:
+ pDesc->name = "grp_info";
+ pDesc->title = SANE_I18N ("Information");
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_chipname:
+ pDesc->name = "opt_chipname";
+ pDesc->title = SANE_I18N ("Chipset name");
+ pDesc->desc = SANE_I18N ("Shows chipset name used in device.");
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (SANE_I18N ("Unknown"));
+ pDesc->size = strlen(pVal->s) + 1;
+ break;
+
+ case opt_chipid:
+ pDesc->name = "opt_chipid";
+ pDesc->title = SANE_I18N ("Chipset ID");
+ pDesc->desc = SANE_I18N ("Shows the chipset ID");
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT;
+ pVal->w = -1;
+ break;
+
+ case opt_scancount:
+ pDesc->name = "opt_scancount";
+ pDesc->title = SANE_I18N ("Scan counter");
+ pDesc->desc =
+ SANE_I18N ("Shows the number of scans made by scanner");
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT;
+ pVal->w = -1;
+ break;
+
+ case opt_infoupdate:
+ pDesc->name = "opt_infoupdate";
+ pDesc->title = SANE_I18N ("Update information");
+ pDesc->desc = SANE_I18N ("Updates information about device");
+ pDesc->type = SANE_TYPE_BUTTON;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.string_list = 0;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT;
+ pVal->w = 0;
+ break;
+
+ /* buttons support */
+ case grp_sensors:
+ pDesc->name = SANE_NAME_SENSORS;
+ pDesc->title = SANE_TITLE_SENSORS;
+ pDesc->desc = SANE_DESC_SENSORS;
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_button_0:
+ case opt_button_1:
+ case opt_button_2:
+ case opt_button_3:
+ case opt_button_4:
+ case opt_button_5:
+ {
+ char name[12];
+ char title[128];
+
+ sprintf (name, "button %d", i - opt_button_0);
+ sprintf (title, "Scanner button %d", i - opt_button_0);
+ pDesc->name = strdup (name);
+ pDesc->title = strdup (title);
+ pDesc->desc =
+ SANE_I18N
+ ("This option reflects a front panel scanner button");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+
+ if (i - opt_button_0 >= Buttons_Count (device))
+ pDesc->cap |= SANE_CAP_INACTIVE;
+
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pVal->w = SANE_FALSE;
+ }
+ break;
+ }
+ }
+ }
+}
+
+static SANE_Int
+_ReportDevice (TScannerModel * pModel, const char *pszDeviceName)
+{
+ SANE_Int rst = ERROR;
+ TDevListEntry *pNew, *pDev;
+
+ DBG (DBG_FNC, "> _ReportDevice:\n");
+
+ pNew = malloc (sizeof (TDevListEntry));
+ if (pNew != NULL)
+ {
+ rst = OK;
+
+ /* add new element to the end of the list */
+ if (_pFirstSaneDev != NULL)
+ {
+ /* Add at the end of existing list */
+ for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext);
+
+ pDev->pNext = pNew;
+ }
+ else
+ _pFirstSaneDev = pNew;
+
+ /* fill in new element */
+ pNew->pNext = NULL;
+ pNew->devname = (char *) strdup (pszDeviceName);
+ pNew->dev.name = pNew->devname;
+ pNew->dev.vendor = pModel->pszVendor;
+ pNew->dev.model = pModel->pszName;
+ pNew->dev.type = SANE_I18N ("flatbed scanner");
+
+ iNumSaneDev++;
+ }
+
+ return rst;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ static TScannerModel sModel;
+
+ DBG (DBG_FNC, "> attach_one_device(devname=%s)\n", devname);
+
+ switch (GetUSB_device_model (devname))
+ {
+ case HP3800:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 3800");
+ break;
+ case HPG2710:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet G2710");
+ break;
+ case HP3970:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 3970");
+ break;
+ case HP4070:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 4070 Photosmart");
+ break;
+ case HP4370:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 4370");
+ break;
+ case HPG3010:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet G3010");
+ break;
+ case HPG3110:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet G3110");
+ break;
+ case UA4900:
+ sModel.pszVendor = (char *) strdup ("UMAX");
+ sModel.pszName = (char *) strdup ("Astra 4900");
+ break;
+ case BQ5550:
+ sModel.pszVendor = (char *) strdup ("BenQ");
+ sModel.pszName = (char *) strdup ("5550");
+ break;
+ default:
+ sModel.pszVendor = (char *) strdup ("Unknown");
+ sModel.pszName = (char *) strdup ("RTS8822 chipset based");
+ break;
+ }
+
+ _ReportDevice (&sModel, devname);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Sane default functions */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ FILE *conf_fp; /* Config file stream */
+ SANE_Char line[PATH_MAX];
+ SANE_Char *str = NULL;
+ SANE_String_Const proper_str;
+ SANE_Int nline = 0;
+
+ /* Initialize debug */
+ DBG_INIT ();
+
+ DBG (DBG_FNC, "> sane_init\n");
+
+ /* silence gcc */
+ authorize = authorize;
+
+ /* Initialize usb */
+ sanei_usb_init ();
+
+ /* Parse config file */
+ conf_fp = sanei_config_open (HP3900_CONFIG_FILE);
+ if (conf_fp)
+ {
+ while (sanei_config_read (line, sizeof (line), conf_fp))
+ {
+ nline++;
+ if (str)
+ free (str);
+
+ proper_str = sanei_config_get_string (line, &str);
+
+ /* Discards white lines and comments */
+ if ((str != NULL) && (proper_str != line) && (str[0] != '#'))
+ {
+ /* If line's not blank or a comment, then it's the device
+ * filename or a usb directive. */
+ sanei_usb_attach_matching_devices (line, attach_one_device);
+ }
+ }
+ fclose (conf_fp);
+ }
+ else
+ {
+ /* default */
+ DBG (DBG_VRB, "- %s not found. Looking for hardcoded usb ids ...\n",
+ HP3900_CONFIG_FILE);
+
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2605", attach_one_device); /* HP3800 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2805", attach_one_device); /* HPG2710 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2305", attach_one_device); /* HP3970 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2405", attach_one_device); /* HP4070 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4105", attach_one_device); /* HP4370 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4205", attach_one_device); /* HPG3010 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4305", attach_one_device); /* HPG3110 */
+ sanei_usb_attach_matching_devices ("usb 0x06dc 0x0020", attach_one_device); /* UA4900 */
+ sanei_usb_attach_matching_devices ("usb 0x04a5 0x2211", attach_one_device); /* BQ5550 */
+ }
+
+ /* Return backend version */
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Status rst = SANE_STATUS_GOOD;
+
+ local_only = local_only;
+
+ if (_pSaneDevList)
+ free (_pSaneDevList);
+
+ _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1));
+ if (_pSaneDevList != NULL)
+ {
+ TDevListEntry *pDev;
+ SANE_Int i = 0;
+
+ for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext)
+ _pSaneDevList[i++] = &pDev->dev;
+
+ _pSaneDevList[i++] = 0; /* last entry is 0 */
+ *device_list = _pSaneDevList;
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+
+ DBG (DBG_FNC, "> sane_get_devices: %i\n", rst);
+
+ return rst;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ TScanner *s;
+ SANE_Status rst;
+
+ /* check the name */
+ if (strlen (name) == 0)
+ /* default to first available device */
+ name = _pFirstSaneDev->dev.name;
+
+ /* allocate space for RTS environment */
+ device = RTS_Alloc ();
+ if (device != NULL)
+ {
+ /* Open device */
+ rst = sanei_usb_open (name, &device->usb_handle);
+ if (rst == SANE_STATUS_GOOD)
+ {
+ /* Allocating memory for device */
+ s = malloc (sizeof (TScanner));
+ if (s != NULL)
+ {
+ memset (s, 0, sizeof (TScanner));
+
+ /* Initializing RTS */
+ if (Init_Vars () == OK)
+ {
+ SANE_Int vendor, product;
+
+ /* Setting device model */
+ if (sanei_usb_get_vendor_product
+ (device->usb_handle, &vendor,
+ &product) == SANE_STATUS_GOOD)
+ s->model = Device_get (product, vendor);
+ else
+ s->model = HP3970;
+
+ set_ScannerModel (s->model, product, vendor);
+
+ /* Initialize device */
+ if (RTS_Scanner_Init (device) == OK)
+ {
+ /* silencing unused functions */
+ Silent_Compile ();
+
+ /* initialize backend options */
+ options_init (s);
+ *h = s;
+
+ /* everything went ok */
+ rst = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ free ((void *) s);
+ rst = SANE_STATUS_INVAL;
+ }
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+ }
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+
+ DBG (DBG_FNC, "> sane_open(name=%s): %i\n", name, rst);
+
+ return rst;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ SANE_Option_Descriptor *rst = NULL;
+
+ if ((n >= opt_begin) && (n < opt_count))
+ {
+ TScanner *s = (TScanner *) h;
+ rst = &s->aOptions[n];
+ }
+
+ DBG (DBG_FNC, "> SANE_Option_Descriptor(handle, n=%i): %i\n", n,
+ (rst == NULL) ? -1 : 0);
+
+ return rst;
+}
+
+static SANE_Status
+option_get (TScanner * scanner, SANE_Int optid, void *result)
+{
+ /* This function returns value contained in selected option */
+
+ DBG (DBG_FNC, "> option_get(optid=%i)\n", optid);
+
+ if ((scanner != NULL) && (result != NULL))
+ {
+ switch (optid)
+ {
+ /* SANE_Word */
+ case opt_begin: /* null */
+ case opt_reset: /* null */
+ case opt_negative:
+ case opt_nogamma:
+ case opt_nowshading:
+ case opt_emulategray:
+ case opt_dbgimages:
+ case opt_nowarmup:
+ case opt_realdepth:
+ case opt_depth:
+ case opt_resolution:
+ case opt_threshold:
+ case opt_brx:
+ case opt_tlx:
+ case opt_bry:
+ case opt_tly:
+ *(SANE_Word *) result = scanner->aValues[optid].w;
+ break;
+
+ /* SANE_Int */
+ case opt_chipid:
+ case opt_scancount:
+ *(SANE_Int *) result = scanner->aValues[optid].w;
+ break;
+
+ /* SANE_Word array */
+ case opt_gamma_red:
+ case opt_gamma_green:
+ case opt_gamma_blue:
+ memcpy (result, scanner->aValues[optid].wa,
+ scanner->aOptions[optid].size);
+ break;
+
+ /* String */
+ case opt_colormode:
+ case opt_scantype:
+ case opt_model:
+ case opt_chipname:
+ strncpy (result, scanner->aValues[optid].s, scanner->aOptions[optid].size);
+ ((char*)result)[scanner->aOptions[optid].size-1] = '\0';
+
+ break;
+
+ /* scanner buttons */
+ case opt_button_0:
+ get_button_status (scanner);
+ case opt_button_1:
+ case opt_button_2:
+ case opt_button_3:
+ case opt_button_4:
+ case opt_button_5:
+ /* copy the button state */
+ *(SANE_Word *) result = scanner->aValues[optid].w;
+ /* clear the button state */
+ scanner->aValues[optid].w = SANE_FALSE;
+ break;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+option_set (TScanner * scanner, SANE_Int optid, void *value, SANE_Int * pInfo)
+{
+ SANE_Status rst;
+
+ DBG (DBG_FNC, "> option_set(optid=%i)\n", optid);
+
+ rst = SANE_STATUS_INVAL;
+
+ if (scanner != NULL)
+ {
+ if (scanner->fScanning == FALSE)
+ {
+ SANE_Int info = 0;
+
+ rst = SANE_STATUS_GOOD;
+
+ switch (optid)
+ {
+ case opt_brx:
+ case opt_tlx:
+ case opt_bry:
+ case opt_tly:
+ case opt_depth:
+ case opt_nogamma:
+ case opt_nowshading:
+ case opt_nowarmup:
+ case opt_negative:
+ case opt_emulategray:
+ case opt_dbgimages:
+ case opt_threshold:
+ case opt_resolution:
+ info |= SANE_INFO_RELOAD_PARAMS;
+ scanner->aValues[optid].w = *(SANE_Word *) value;
+ break;
+
+ case opt_gamma_red:
+ case opt_gamma_green:
+ case opt_gamma_blue:
+ memcpy (scanner->aValues[optid].wa, value,
+ scanner->aOptions[optid].size);
+ break;
+
+ case opt_scantype:
+ if (strcmp (scanner->aValues[optid].s, value) != 0)
+ {
+ struct st_coords *coords;
+ SANE_Int source;
+
+ if (scanner->aValues[optid].s)
+ free (scanner->aValues[optid].s);
+
+ scanner->aValues[optid].s = strdup (value);
+
+ source = Get_Source (scanner->aValues[opt_scantype].s);
+ coords = Constrains_Get (device, source);
+ if (coords != NULL)
+ {
+ bknd_constrains (scanner, source, 0);
+ bknd_constrains (scanner, source, 1);
+ scanner->aValues[opt_tlx].w = 0;
+ scanner->aValues[opt_tly].w = 0;
+ scanner->aValues[opt_brx].w = coords->width;
+ scanner->aValues[opt_bry].w = coords->height;
+ }
+
+ info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case opt_colormode:
+ if (strcmp (scanner->aValues[optid].s, value) != 0)
+ {
+ if (scanner->aValues[optid].s)
+ free (scanner->aValues[optid].s);
+ scanner->aValues[optid].s = strdup (value);
+ if (Get_Colormode (scanner->aValues[optid].s) == CM_LINEART)
+ scanner->aOptions[opt_threshold].cap &=
+ ~SANE_CAP_INACTIVE;
+ else
+ scanner->aOptions[opt_threshold].cap |= SANE_CAP_INACTIVE;
+ info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case opt_model:
+ if (strcmp (scanner->aValues[optid].s, value) != 0)
+ {
+ SANE_Int model;
+
+ if (scanner->aValues[optid].s)
+ free (scanner->aValues[optid].s);
+ scanner->aValues[optid].s = strdup (value);
+
+ model = Get_Model (scanner->aValues[optid].s);
+ if (model != RTS_Debug->dev_model)
+ {
+ SANE_Int source;
+ struct st_coords *coords;
+
+ /* free configuration of last model */
+ Free_Config (device);
+
+ /* set new model */
+ RTS_Debug->dev_model = model;
+
+ /* and load configuration of current model */
+ Load_Config (device);
+
+ /* update options according to selected device */
+ bknd_info (scanner);
+ bknd_colormodes (scanner, model);
+ bknd_depths (scanner, model);
+ bknd_resolutions (scanner, model);
+ bknd_sources (scanner, model);
+
+ /* updating lists */
+ scanner->aOptions[opt_colormode].size =
+ max_string_size (scanner->list_colormodes);
+ scanner->aOptions[opt_colormode].constraint.
+ string_list = scanner->list_colormodes;
+ scanner->aOptions[opt_depth].constraint.word_list =
+ scanner->list_depths;
+ scanner->aOptions[opt_resolution].constraint.word_list =
+ scanner->list_resolutions;
+ scanner->aOptions[opt_scantype].size =
+ max_string_size (scanner->list_sources);
+ scanner->aOptions[opt_scantype].constraint.string_list =
+ scanner->list_sources;
+
+ /* default values */
+ if (scanner->aValues[opt_colormode].s != NULL)
+ free (scanner->aValues[opt_colormode].s);
+
+ if (scanner->aValues[opt_scantype].s != NULL)
+ free (scanner->aValues[opt_scantype].s);
+
+ scanner->aValues[opt_colormode].s =
+ strdup (scanner->list_colormodes[0]);
+ scanner->aValues[opt_scantype].s =
+ strdup (scanner->list_sources[0]);
+ scanner->aValues[opt_resolution].w =
+ scanner->list_resolutions[1];
+ scanner->aValues[opt_depth].w = scanner->list_depths[1];
+
+ source = Get_Source (scanner->aValues[opt_scantype].s);
+ coords = Constrains_Get (device, source);
+ if (coords != NULL)
+ {
+ bknd_constrains (scanner, source, 0);
+ bknd_constrains (scanner, source, 1);
+ scanner->aValues[opt_tlx].w = 0;
+ scanner->aValues[opt_tly].w = 0;
+ scanner->aValues[opt_brx].w = coords->width;
+ scanner->aValues[opt_bry].w = coords->height;
+ }
+ }
+
+ info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case opt_reset:
+ Chipset_Reset (device);
+ break;
+
+ case opt_realdepth:
+ scanner->aValues[optid].w =
+ (scanner->cnv.real_depth == TRUE) ? SANE_TRUE : SANE_FALSE;
+ break;
+
+ case opt_infoupdate:
+ if (bknd_info (scanner) == SANE_STATUS_GOOD)
+ info |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ default:
+ rst = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (pInfo != NULL)
+ *pInfo = info;
+ }
+ }
+
+ return rst;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action,
+ void *pVal, SANE_Int * pInfo)
+{
+ TScanner *scanner;
+ SANE_Status rst;
+
+ DBG (DBG_FNC, "> sane_control_option\n");
+
+ scanner = (TScanner *) h;
+
+ switch (Action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ rst = option_get (scanner, n, pVal);
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ rst = option_set (scanner, n, pVal, pInfo);
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ rst = SANE_STATUS_UNSUPPORTED;
+ break;
+
+ default:
+ rst = SANE_STATUS_INVAL;
+ break;
+ }
+
+ return rst;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+ TScanner *s = (TScanner *) h;
+
+ DBG (DBG_FNC, "+ sane_get_parameters:");
+
+ if (s != NULL)
+ {
+ struct st_coords coords;
+ SANE_Int res, source, depth, colormode, frameformat, bpl;
+
+ /* first do some checks */
+
+ /* colormode */
+ colormode = Get_Colormode (s->aValues[opt_colormode].s);
+
+ /* frameformat */
+ frameformat =
+ (colormode == CM_COLOR) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+
+ /* depth */
+ depth = (colormode == CM_LINEART) ? 1 : s->aValues[opt_depth].w;
+
+ /* scan type */
+ source = Get_Source (s->aValues[opt_scantype].s);
+
+ /* resolution */
+ res = s->aValues[opt_resolution].w;
+
+ /* image coordinates in milimeters */
+ coords.left = s->aValues[opt_tlx].w;
+ coords.top = s->aValues[opt_tly].w;
+ coords.width = s->aValues[opt_brx].w;
+ coords.height = s->aValues[opt_bry].w;
+
+ /* validate coords */
+ if (Translate_coords (&coords) == SANE_STATUS_GOOD)
+ {
+ Set_Coordinates (source, res, &coords);
+
+ if (colormode != CM_LINEART)
+ {
+ bpl = coords.width * ((depth > 8) ? 2 : 1);
+ if (colormode == CM_COLOR)
+ bpl *= 3; /* three channels */
+ }
+ else
+ bpl = (coords.width + 7) / 8;
+
+ /* return the data */
+ p->format = frameformat;
+ p->last_frame = SANE_TRUE;
+ p->depth = depth;
+ p->lines = coords.height;
+ p->pixels_per_line = coords.width;
+ p->bytes_per_line = bpl;
+
+ DBG (DBG_FNC, " -> Depth : %i\n", depth);
+ DBG (DBG_FNC, " -> Height: %i\n", coords.height);
+ DBG (DBG_FNC, " -> Width : %i\n", coords.width);
+ DBG (DBG_FNC, " -> BPL : %i\n", bpl);
+
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (DBG_FNC, "- sane_get_parameters: %i\n", rst);
+
+ return rst;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+ TScanner *s;
+
+ DBG (DBG_FNC, "+ sane_start\n");
+
+ s = (TScanner *) h;
+ if (s != NULL)
+ {
+ struct st_coords coords;
+ SANE_Int res, source, colormode, depth, channel;
+
+ /* first do some checks */
+ /* Get Scan type */
+ source = Get_Source (s->aValues[opt_scantype].s);
+
+ /* Check if scanner supports slides and negatives in case selected source is tma */
+ if (!((source != ST_NORMAL) && (RTS_isTmaAttached (device) == FALSE)))
+ {
+ /* Get depth */
+ depth = s->aValues[opt_depth].w;
+
+ /* Get color mode */
+ colormode = Get_Colormode (s->aValues[opt_colormode].s);
+
+ /* Emulating certain color modes */
+ if (colormode == CM_LINEART)
+ {
+ /* emulate lineart */
+ s->cnv.colormode = CM_LINEART;
+ colormode = CM_GRAY;
+ depth = 8;
+ }
+ else if ((colormode == CM_GRAY)
+ && (s->aValues[opt_emulategray].w == SANE_TRUE))
+ {
+ /* emulate grayscale */
+ s->cnv.colormode = CM_GRAY;
+ colormode = CM_COLOR;
+ }
+ else
+ s->cnv.colormode = -1;
+
+ /* setting channel for colormodes different than CM_COLOR */
+ channel = (colormode != CM_COLOR) ? 1 : 0;
+
+ /* negative colors */
+ s->cnv.negative =
+ (s->aValues[opt_negative].w == SANE_TRUE) ? TRUE : FALSE;
+
+ /* Get threshold */
+ s->cnv.threshold = s->aValues[opt_threshold].w;
+
+ /* Get resolution */
+ res = s->aValues[opt_resolution].w;
+
+ /* set depth emulation */
+ if (s->cnv.colormode == CM_LINEART)
+ s->cnv.real_depth = TRUE;
+ else
+ s->cnv.real_depth =
+ (s->aValues[opt_realdepth].w == SANE_TRUE) ? TRUE : FALSE;
+
+ /* use gamma? */
+ RTS_Debug->EnableGamma =
+ (s->aValues[opt_nogamma].w == SANE_TRUE) ? FALSE : TRUE;
+
+ /* disable white shading correction? */
+ RTS_Debug->wshading =
+ (s->aValues[opt_nowshading].w == SANE_TRUE) ? FALSE : TRUE;
+
+ /* skip warmup process? */
+ RTS_Debug->warmup =
+ (s->aValues[opt_nowarmup].w == SANE_TRUE) ? FALSE : TRUE;
+
+ /* save debugging images? */
+ RTS_Debug->SaveCalibFile =
+ (s->aValues[opt_dbgimages].w == SANE_TRUE) ? TRUE : FALSE;
+
+ /* Get image coordinates in milimeters */
+ coords.left = s->aValues[opt_tlx].w;
+ coords.top = s->aValues[opt_tly].w;
+ coords.width = s->aValues[opt_brx].w;
+ coords.height = s->aValues[opt_bry].w;
+
+ /* Validate coords */
+ if (Translate_coords (&coords) == SANE_STATUS_GOOD)
+ {
+
+ /* Stop previusly started scan */
+ RTS_Scanner_StopScan (device, TRUE);
+
+ s->ScanParams.scantype = source;
+ s->ScanParams.colormode = colormode;
+ s->ScanParams.resolution_x = res;
+ s->ScanParams.resolution_y = res;
+ s->ScanParams.channel = channel;
+
+ memcpy (&s->ScanParams.coords, &coords,
+ sizeof (struct st_coords));
+ Set_Coordinates (source, res, &s->ScanParams.coords);
+
+ /* emulating depth? */
+ if ((s->cnv.real_depth == FALSE) && (depth < 16)
+ && (RTS_Debug->EnableGamma == TRUE))
+ {
+ /* In order to improve image quality, we will scan at 16bits if
+ we are using gamma correction */
+ s->cnv.depth = depth;
+ s->ScanParams.depth = 16;
+ }
+ else
+ {
+ s->ScanParams.depth = depth;
+ s->cnv.depth = -1;
+ }
+
+ /* set scanning parameters */
+ if (RTS_Scanner_SetParams (device, &s->ScanParams) == OK)
+ {
+ /* Start scanning process */
+ if (RTS_Scanner_StartScan (device) == OK)
+ {
+ /* Allocate buffer to read one line */
+ s->mylin = 0;
+ rst = img_buffers_alloc (s, bytesperline);
+ }
+ }
+ }
+ }
+ else
+ rst = SANE_STATUS_COVER_OPEN;
+ }
+
+ DBG (DBG_FNC, "- sane_start: %i\n", rst);
+
+ return rst;
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ SANE_Status rst = SANE_STATUS_GOOD;
+ TScanner *s = (TScanner *) h;
+
+ DBG (DBG_FNC, "+ sane_read\n");
+
+ if ((s != NULL) && (buf != NULL) && (len != NULL))
+ {
+ /* nothing has been read at the moment */
+ *len = 0;
+
+ /* if we read all the lines return EOF */
+ if ((s->mylin == s->ScanParams.coords.height)
+ || (device->status->cancel == TRUE))
+ {
+ rst =
+ (device->status->cancel ==
+ TRUE) ? SANE_STATUS_CANCELLED : SANE_STATUS_EOF;
+
+ RTS_Scanner_StopScan (device, FALSE);
+ img_buffers_free (s);
+ }
+ else
+ {
+ SANE_Int emul_len, emul_maxlen;
+ SANE_Int thwidth, transferred, bufflength;
+ SANE_Byte *buffer, *pbuffer;
+
+ emul_len = 0;
+ if (s->cnv.depth != -1)
+ emul_maxlen = maxlen * (s->ScanParams.depth / s->cnv.depth);
+ else
+ emul_maxlen = maxlen;
+
+ /* if grayscale emulation is enabled check that retrieved data is multiple of three */
+ if (s->cnv.colormode == CM_GRAY)
+ {
+ SANE_Int chn_size, rest;
+
+ chn_size = (s->ScanParams.depth > 8) ? 2 : 1;
+ rest = emul_maxlen % (3 * chn_size);
+
+ if (rest != 0)
+ emul_maxlen -= rest;
+ }
+
+ /* this is important to keep lines alignment in lineart mode */
+ if (s->cnv.colormode == CM_LINEART)
+ emul_maxlen = s->ScanParams.coords.width;
+
+ /* if we are emulating depth, we scan at 16bit when frontend waits
+ for 8bit data. Next buffer will be used to retrieve data from
+ scanner prior to convert to 8 bits depth */
+ buffer = (SANE_Byte *) malloc (emul_maxlen * sizeof (SANE_Byte));
+
+ if (buffer != NULL)
+ {
+ pbuffer = buffer;
+
+ /* get bytes per line */
+ if (s->ScanParams.colormode != CM_LINEART)
+ {
+ thwidth =
+ s->ScanParams.coords.width *
+ ((s->ScanParams.depth > 8) ? 2 : 1);
+
+ if (s->ScanParams.colormode == CM_COLOR)
+ thwidth *= 3; /* three channels */
+ }
+ else
+ thwidth = (s->ScanParams.coords.width + 7) / 8;
+
+ /* read as many lines the buffer may contain and while there are lines to be read */
+ while ((emul_len < emul_maxlen)
+ && (s->mylin < s->ScanParams.coords.height))
+ {
+ /* Is there any data waiting for being passed ? */
+ if (s->rest_amount != 0)
+ {
+ /* copy to buffer as many bytes as we can */
+ bufflength =
+ min (emul_maxlen - emul_len, s->rest_amount);
+ memcpy (pbuffer, s->rest, bufflength);
+ emul_len += bufflength;
+ pbuffer += bufflength;
+ s->rest_amount -= bufflength;
+ if (s->rest_amount == 0)
+ s->mylin++;
+ }
+ else
+ {
+ /* read from scanner up to one line */
+ if (Read_Image
+ (device, bytesperline, s->image,
+ &transferred) != OK)
+ {
+ /* error, exit function */
+ rst = SANE_STATUS_EOF;
+ break;
+ }
+
+ /* is there any data? */
+ if (transferred != 0)
+ {
+ /* copy to buffer as many bytes as we can */
+ bufflength = min (emul_maxlen - emul_len, thwidth);
+
+ memcpy (pbuffer, s->image, bufflength);
+ emul_len += bufflength;
+ pbuffer += bufflength;
+
+ /* the rest will be copied to s->rest buffer */
+ if (bufflength < thwidth)
+ {
+ s->rest_amount = thwidth - bufflength;
+ memcpy (s->rest, s->image + bufflength,
+ s->rest_amount);
+ }
+ else
+ s->mylin++;
+ }
+ else
+ break;
+ }
+ } /* while */
+
+ /* process buffer before sending to frontend */
+ if ((emul_len > 0) && (rst != SANE_STATUS_EOF))
+ {
+ /* at this point ...
+ buffer : contains retrieved image
+ emul_len: contains size in bytes of retrieved image
+
+ after this code ...
+ buf : will contain postprocessed image
+ len : will contain size in bytes of postprocessed image */
+
+ /* apply gamma if neccesary */
+ if (RTS_Debug->EnableGamma == TRUE)
+ gamma_apply (s, buffer, emul_len, s->ScanParams.depth);
+
+ /* if we are scanning negatives, let's invert colors */
+ if (s->ScanParams.scantype == ST_NEG)
+ {
+ if (s->cnv.negative == FALSE)
+ Color_Negative (buffer, emul_len,
+ s->ScanParams.depth);
+ }
+ else if (s->cnv.negative != FALSE)
+ Color_Negative (buffer, emul_len, s->ScanParams.depth);
+
+ /* emulating grayscale ? */
+ if (s->cnv.colormode == CM_GRAY)
+ {
+ Color_to_Gray (buffer, emul_len, s->ScanParams.depth);
+ emul_len /= 3;
+ }
+
+ /* emulating depth */
+ if (s->cnv.depth != -1)
+ {
+ switch (s->cnv.depth)
+ {
+ /* case 1: treated separately as lineart */
+ /*case 12: in the future */
+ case 8:
+ Depth_16_to_8 (buffer, emul_len, buffer);
+ emul_len /= 2;
+ break;
+ }
+ }
+
+ /* lineart mode ? */
+ if (s->cnv.colormode == CM_LINEART)
+ {
+ /* I didn't see any scanner supporting lineart mode.
+ Windows drivers scan in grayscale and then convert image to lineart
+ so let's perform convertion */
+ SANE_Int rest = emul_len % 8;
+
+ Gray_to_Lineart (buffer, emul_len, s->cnv.threshold);
+ emul_len /= 8;
+ if (rest > 0)
+ emul_len++;
+ }
+
+ /* copy postprocessed image */
+ *len = emul_len;
+ memcpy (buf, buffer, *len);
+ }
+
+ free (buffer);
+ }
+ }
+ }
+ else
+ rst = SANE_STATUS_EOF;
+
+ DBG (DBG_FNC, "- sane_read: %s\n", sane_strstatus (rst));
+
+ return rst;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ DBG (DBG_FNC, "> sane_cancel\n");
+
+ /* silence gcc */
+ h = h;
+
+ device->status->cancel = TRUE;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (DBG_FNC, "> sane_set_io_mode\n");
+
+ /* silence gcc */
+ handle = handle;
+ non_blocking = non_blocking;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (DBG_FNC, "> sane_get_select_fd\n");
+
+ /* silence gcc */
+ handle = handle;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ TScanner *scanner = (TScanner *) h;
+
+ DBG (DBG_FNC, "- sane_close...\n");
+
+ /* stop previus scans */
+ RTS_Scanner_StopScan (device, TRUE);
+
+ /* close usb */
+ sanei_usb_close (device->usb_handle);
+
+ /* free scanner internal variables */
+ RTS_Scanner_End (device);
+
+ /* free RTS enviroment */
+ RTS_Free (device);
+
+ /* free backend variables */
+ if (scanner != NULL)
+ {
+ options_free (scanner);
+
+ img_buffers_free (scanner);
+ }
+}
+
+void
+sane_exit (void)
+{
+ /* free device list memory */
+ if (_pSaneDevList)
+ {
+ TDevListEntry *pDev, *pNext;
+
+ for (pDev = _pFirstSaneDev; pDev; pDev = pNext)
+ {
+ pNext = pDev->pNext;
+ /* pDev->dev.name is the same pointer that pDev->devname */
+ free (pDev->devname);
+ free (pDev);
+ }
+
+ _pFirstSaneDev = NULL;
+ free (_pSaneDevList);
+ _pSaneDevList = NULL;
+ }
+}
diff --git a/backend/hp3900_types.c b/backend/hp3900_types.c
new file mode 100644
index 0000000..4a1f7e9
--- /dev/null
+++ b/backend/hp3900_types.c
@@ -0,0 +1,783 @@
+/* HP Scanjet 3900 series - Structures and global variables
+
+ Copyright (C) 2005-2009 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* devices */
+#define DEVSCOUNT 0x09 /* Number of scanners supported by this backend */
+
+#define HP3970 0x00 /* rts8822l-01H HP Scanjet 3970 */
+#define HP4070 0x01 /* rts8822l-01H HP Scanjet 4070 */
+#define HP4370 0x02 /* rts8822l-02A HP Scanjet 4370 */
+#define UA4900 0x03 /* rts8822l-01H UMAX Astra 4900 */
+#define HP3800 0x04 /* rts8822bl-03A HP Scanjet 3800 */
+#define HPG3010 0x05 /* rts8822l-02A HP Scanjet G3010 */
+#define BQ5550 0x06 /* rts8823l-01E BenQ 5550 */
+#define HPG2710 0x07 /* rts8822bl-03A HP Scanjet G2710 */
+#define HPG3110 0x08 /* rts8822l-02A HP Scanjet G3110 */
+
+/* chipset models */
+#define RTS8822L_01H 0x00
+#define RTS8822L_02A 0x01
+#define RTS8822BL_03A 0x02
+#define RTS8823L_01E 0x03
+
+/* chipset capabilities */
+#define CAP_EEPROM 0x01
+
+/* acceleration types */
+#define ACC_CURVE 0x00
+#define DEC_CURVE 0x01
+
+/* curve types */
+#define CRV_NORMALSCAN 0x00
+#define CRV_PARKHOME 0x01
+#define CRV_SMEARING 0x02
+#define CRV_BUFFERFULL 0x03
+
+/* Sample rates */
+#define PIXEL_RATE 0x00
+#define LINE_RATE 0x01
+
+/* motor types */
+#define MT_OUTPUTSTATE 0x00
+#define MT_ONCHIP_PWM 0x01
+
+/* motor step types */
+#define STT_FULL 0x00 /* 90 degrees */
+#define STT_HALF 0x01 /* 45 degrees */
+#define STT_QUART 0x02 /* 22.5 degrees */
+#define STT_OCT 0x03 /* 11.25 degrees */
+
+/* motor options */
+#define MTR_BACKWARD 0x00
+#define MTR_FORWARD 0x08
+#define MTR_ENABLED 0x00
+#define MTR_DISABLED 0x10
+
+/* sensors */
+#define CCD_SENSOR 0x01
+#define CIS_SENSOR 0x00
+
+/* sony sensor models */
+#define SNYS575 0x00
+
+/* toshiba sensor models */
+#define TCD2952 0x01
+#define TCD2958 0x02
+#define TCD2905 0x03
+
+/* usb types */
+#define USB20 0x01
+#define USB11 0x00
+
+/* scan types */
+#define ST_NEG 0x03
+#define ST_TA 0x02
+#define ST_NORMAL 0x01
+
+/* colour modes */
+#define CM_COLOR 0x00
+#define CM_GRAY 0x01
+#define CM_LINEART 0x02
+
+/* colour channels */
+#define CL_RED 0x00
+#define CL_GREEN 0x01
+#define CL_BLUE 0x02
+
+/* lamp types */
+#define FLB_LAMP 0x01
+#define TMA_LAMP 0x02
+
+#define IST_NORMAL 0x00
+#define IST_TA 0x01
+#define IST_NEG 0x02
+
+#define ICM_GRAY 0x00
+#define ICM_LINEART 0x01
+#define ICM_COLOR 0x02
+
+#define TRUE 0x01
+#define FALSE 0x00
+
+/* function results */
+#define OK 0x00
+#define ERROR -1
+
+#define RT_BUFFER_LEN 0x71a
+
+#define FIX_BY_HARD 0x01
+#define FIX_BY_SOFT 0x02
+
+#define REF_AUTODETECT 0x02
+#define REF_TAKEFROMSCANNER 0x01
+#define REF_NONE 0x00
+
+/* bulk operations */
+#define BLK_WRITE 0x00
+#define BLK_READ 0x01
+
+/* constants for resizing functions */
+#define RSZ_NONE 0x00
+#define RSZ_DECREASE 0x01
+#define RSZ_INCREASE 0x02
+
+#define RSZ_GRAYL 0x00
+#define RSZ_COLOURL 0x01
+#define RSZ_COLOURH 0x02
+#define RSZ_LINEART 0x03
+#define RSZ_GRAYH 0x04
+
+/* Macros for managing data */
+#define _B0(x) ((SANE_Byte)((x) & 0xFF))
+#define _B1(x) ((SANE_Byte)((x) >> 0x08))
+#define _B2(x) ((SANE_Byte)((x) >> 0x10))
+#define _B3(x) ((SANE_Byte)((x) >> 0x18))
+
+/* operation constants used in RTS_GetImage */
+#define OP_STATIC_HEAD 0x00000001
+#define OP_COMPRESSION 0x00000004
+#define OP_BACKWARD 0x00000010
+#define OP_WHITE_SHAD 0x00000020
+#define OP_USE_GAMMA 0x00000040
+#define OP_BLACK_SHAD 0x00000080
+#define OP_LAMP_ON 0x00000200
+
+/* data types */
+
+typedef unsigned short USHORT;
+
+#ifdef STANDALONE
+/* Stand-alone*/
+#define SANE_STATUS_GOOD 0x00
+
+typedef unsigned char SANE_Byte;
+typedef int SANE_Int;
+typedef usb_dev_handle *USB_Handle;
+
+#else
+
+/* SANE backend */
+typedef SANE_Int USB_Handle;
+
+#endif
+
+/* structures */
+
+struct st_debug_opts
+{
+ /* device capabilities */
+ SANE_Int dev_model;
+
+ SANE_Byte SaveCalibFile;
+ SANE_Byte DumpShadingData;
+ SANE_Byte ScanWhiteBoard;
+ SANE_Byte EnableGamma;
+ SANE_Byte use_fixed_pwm;
+ SANE_Int dmabuffersize;
+ SANE_Int dmatransfersize;
+ SANE_Int dmasetlength;
+ SANE_Int usbtype;
+
+ SANE_Int calibrate;
+ SANE_Int wshading;
+
+ SANE_Int overdrive_flb;
+ SANE_Int overdrive_ta;
+ SANE_Byte warmup;
+
+ SANE_Int shd;
+};
+
+struct st_chip
+{
+ SANE_Int model;
+ SANE_Int capabilities;
+ char *name;
+};
+
+struct st_shading
+{
+ double *rates;
+ SANE_Int count;
+ SANE_Int ptr;
+};
+
+struct st_scanning
+{
+ SANE_Byte *imagebuffer;
+ SANE_Byte *imagepointer;
+ SANE_Int bfsize;
+ SANE_Int channel_size;
+
+ /* arrange line related variables */
+ SANE_Int arrange_hres;
+ SANE_Int arrange_compression;
+ SANE_Int arrange_sensor_evenodd_dist;
+ SANE_Int arrange_orderchannel;
+ SANE_Int arrange_size;
+
+ /* Pointers to each channel colour */
+ SANE_Byte *pColour[3];
+ SANE_Byte *pColour1[3];
+ SANE_Byte *pColour2[3];
+
+ /* Channel displacements */
+ SANE_Int desp[3];
+ SANE_Int desp1[3];
+ SANE_Int desp2[3];
+};
+
+struct st_resize
+{
+ SANE_Byte mode;
+ SANE_Int type;
+ SANE_Int fromwidth;
+ SANE_Int towidth;
+ SANE_Int bytesperline;
+ SANE_Int rescount;
+ SANE_Int resolution_x;
+ SANE_Int resolution_y;
+
+ SANE_Byte *v3624;
+ SANE_Byte *v3628;
+ SANE_Byte *v362c;
+};
+
+struct st_gammatables
+{
+ SANE_Int depth; /*0=0x100| 4=0x400 |8=0x1000 */
+ SANE_Byte *table[3];
+};
+
+struct st_readimage
+{
+ SANE_Int Size4Lines;
+
+ SANE_Byte Starting;
+ SANE_Byte *DMABuffer;
+ SANE_Int DMABufferSize;
+ SANE_Byte *RDStart;
+ SANE_Int RDSize;
+ SANE_Int DMAAmount;
+ SANE_Int Channel_size;
+ SANE_Byte Channels_per_dot;
+ SANE_Int ImageSize;
+ SANE_Int Bytes_Available;
+ SANE_Int Max_Size;
+ SANE_Byte Cancel;
+};
+
+struct st_gain_offset
+{
+ /* 32 bytes 08be|08e0|3654
+ red green blue */
+ SANE_Int edcg1[3]; /* 08e0|08e2|08e4 *//*Even offset 1 */
+ SANE_Int edcg2[3]; /* 08e6|08e8|08ea *//*Even offset 2 */
+ SANE_Int odcg1[3]; /* 08ec|08ee|08f0 *//*Odd offset 1 */
+ SANE_Int odcg2[3]; /* 08f2|08f4|08f6 *//*Odd offset 2 */
+ SANE_Byte pag[3]; /* 08f8|08f9|08fa */
+ SANE_Byte vgag1[3]; /* 08fb|08fc|08fd */
+ SANE_Byte vgag2[3]; /* 08fe|08ff|0900 */
+};
+
+struct st_calibration_config
+{
+ SANE_Int WStripXPos;
+ SANE_Int WStripYPos;
+ SANE_Int BStripXPos;
+ SANE_Int BStripYPos;
+ SANE_Int WRef[3];
+ SANE_Int BRef[3];
+ SANE_Byte RefBitDepth;
+ double OffsetTargetMax;
+ double OffsetTargetMin;
+ double OffsetBoundaryRatio1;
+ double OffsetBoundaryRatio2;
+ double OffsetAvgRatio1;
+ double OffsetAvgRatio2;
+ SANE_Int CalibOffset10n;
+ SANE_Int CalibOffset20n;
+ SANE_Int AdcOffEvenOdd;
+ SANE_Int AdcOffQuickWay;
+ SANE_Int OffsetEven1[3];
+ SANE_Int OffsetOdd1[3];
+ SANE_Int OffsetEven2[3];
+ SANE_Int OffsetOdd2[3];
+ SANE_Byte OffsetHeight;
+ SANE_Int OffsetPixelStart;
+ SANE_Int OffsetNPixel;
+ SANE_Byte OffsetNSigma;
+ SANE_Int AdcOffPredictStart;
+ SANE_Int AdcOffPredictEnd;
+ SANE_Byte OffsetAvgTarget[3];
+ SANE_Byte OffsetTuneStep1;
+ SANE_Byte OffsetTuneStep2;
+ double GainTargetFactor;
+ SANE_Int CalibGain10n;
+ SANE_Int CalibGain20n;
+ SANE_Int CalibPAGOn;
+ SANE_Int GainHeight;
+ SANE_Int unk1[3];
+ SANE_Int unk2[3];
+ SANE_Byte PAG[3];
+ SANE_Byte Gain1[3];
+ SANE_Byte Gain2[3];
+ /* White Shading */
+ SANE_Int WShadingOn;
+ SANE_Int WShadingHeight;
+ SANE_Int WShadingPreDiff[3];
+ SANE_Int unknown; /*?? */
+ double ShadingCut[3];
+ /* Black Shading */
+ SANE_Int BShadingOn;
+ SANE_Int BShadingHeight;
+ SANE_Int BShadingDefCutOff;
+ SANE_Int BShadingPreDiff[3];
+ double ExternBoundary;
+ SANE_Int EffectivePixel;
+ SANE_Byte TotShading;
+};
+
+struct st_calibration
+{
+ /* faac */
+ struct st_gain_offset gain_offset; /* 0..35 */
+ USHORT *white_shading[3]; /* +36 +40 +44 */
+ USHORT *black_shading[3]; /* +48 +52 +56 */
+ SANE_Int WRef[3]; /* +60 +62 +64 */
+ SANE_Byte shading_type; /* +66 */
+ SANE_Byte shading_enabled; /* +67 */
+ SANE_Int first_position; /* +68 */
+ SANE_Int shadinglength; /* +72 */
+};
+
+struct st_cal2
+{
+ /* f9f8 35 bytes */
+ SANE_Int table_count; /* +0 f9f8 */
+ SANE_Int shadinglength1; /* +4 f9fc */
+ SANE_Int tables_size; /* +8 fa00 */
+ SANE_Int shadinglength3; /* +12 fa04 */
+ USHORT *tables[4]; /* +16+20+24+28 fa08 fa0c fa10 fa14 */
+ USHORT *table2; /* +32 fa18 */
+};
+
+struct st_coords
+{
+ SANE_Int left;
+ SANE_Int width;
+ SANE_Int top;
+ SANE_Int height;
+};
+
+struct params
+{
+ SANE_Int scantype;
+ SANE_Int colormode;
+ SANE_Int resolution_x;
+ SANE_Int resolution_y;
+ struct st_coords coords;
+ SANE_Int depth;
+ SANE_Int channel;
+};
+
+struct st_constrains
+{
+ struct st_coords reflective;
+ struct st_coords negative;
+ struct st_coords slide;
+};
+
+struct st_scanparams /* 44 bytes size */
+{
+ /* 760-78b|155c-1587|fa58-fa83|f0c4 */
+ SANE_Byte colormode; /* [+00] 760 */
+ SANE_Byte depth; /* [+01] 761 */
+ SANE_Byte samplerate; /* [+02] 762 */
+ SANE_Byte timing; /* [+03] 763 */
+ SANE_Int channel; /* [+04] 764 */
+ SANE_Int sensorresolution; /* [+06] 766 */
+ SANE_Int resolution_x; /* [+08] 768 */
+ SANE_Int resolution_y; /* [+10] 76a */
+ struct st_coords coord; /* [+12] left */
+ /* [+16] width */
+ /* [+20] top */
+ /* [+24] height */
+ SANE_Int shadinglength; /* [+28] 77c */
+ SANE_Int v157c; /* [+32] 780 */
+ SANE_Int bytesperline; /* [+36] 784 */
+ SANE_Int expt; /* [+40] 788 */
+
+ SANE_Int startpos; /* [+44] 78c */
+ SANE_Int leftleading; /* [+46] 78e */
+ SANE_Int ser; /* [+48] 790 */
+ SANE_Int ler; /* [+52] 794 */
+ SANE_Int scantype; /* [+58] 79a */
+};
+
+struct st_hwdconfig /* 28 bytes size */
+{
+ /* fa84-fa9f|f0ac-f0c7|e838-e853|f3a4-f3bf */
+ SANE_Int startpos; /* +0 */
+ /* +1..7 */
+ SANE_Byte arrangeline; /* +8 */
+ SANE_Byte scantype; /* +9 */
+ SANE_Byte compression; /* +10 */
+ SANE_Byte use_gamma_tables; /* +11 */
+ SANE_Byte gamma_tablesize; /* +12 */
+ SANE_Byte white_shading; /* +13 */
+ SANE_Byte black_shading; /* +14 */
+ SANE_Byte unk3; /* +15 */
+ SANE_Byte motorplus; /* +16 */
+ SANE_Byte static_head; /* +17 */
+ SANE_Byte motor_direction; /* +18 */
+ SANE_Byte dummy_scan; /* +19 */
+ SANE_Byte highresolution; /* +20 */
+ SANE_Byte sensorevenodddistance; /* +21 */
+ /* +22..23 */
+ SANE_Int calibrate; /* +24 */
+};
+
+struct st_calibration_data
+{
+ SANE_Byte Regs[RT_BUFFER_LEN];
+ struct st_scanparams scancfg;
+ struct st_gain_offset gain_offset;
+};
+
+struct st_cph
+{
+ double p1;
+ double p2;
+ SANE_Byte ps;
+ SANE_Byte ge;
+ SANE_Byte go;
+};
+
+struct st_timing
+{
+ SANE_Int sensorresolution;
+ SANE_Byte cnpp;
+ SANE_Byte cvtrp[3]; /* 3 transfer gates */
+ SANE_Byte cvtrw;
+ SANE_Byte cvtrfpw;
+ SANE_Byte cvtrbpw;
+ struct st_cph cph[6]; /* Linear Image Sensor Clocks */
+ SANE_Int cphbp2s;
+ SANE_Int cphbp2e;
+ SANE_Int clamps;
+ SANE_Int clampe;
+ SANE_Byte cdss[2];
+ SANE_Byte cdsc[2];
+ SANE_Byte cdscs[2]; /* Toshiba T958 ccd from hp4370 */
+ double adcclkp[2];
+ SANE_Int adcclkp2e;
+};
+
+struct st_scanmode
+{
+ SANE_Int scantype;
+ SANE_Int colormode;
+ SANE_Int resolution;
+
+ SANE_Byte timing;
+ SANE_Int motorcurve;
+ SANE_Byte samplerate;
+ SANE_Byte systemclock;
+ SANE_Int ctpc;
+ SANE_Int motorbackstep;
+ SANE_Byte scanmotorsteptype;
+
+ SANE_Byte dummyline;
+ SANE_Int expt[3];
+ SANE_Int mexpt[3];
+ SANE_Int motorplus;
+ SANE_Int multiexposurefor16bitmode;
+ SANE_Int multiexposureforfullspeed;
+ SANE_Int multiexposure;
+ SANE_Int mri;
+ SANE_Int msi;
+ SANE_Int mmtir;
+ SANE_Int mmtirh;
+ SANE_Int skiplinecount;
+};
+
+struct st_motormove
+{
+ SANE_Byte systemclock;
+ SANE_Int ctpc;
+ SANE_Byte scanmotorsteptype;
+ SANE_Int motorcurve;
+};
+
+struct st_motorpos
+{
+ SANE_Int coord_y;
+ SANE_Byte options;
+ SANE_Int v12e448;
+ SANE_Int v12e44c;
+};
+
+struct st_find_edge
+{
+ SANE_Int exposuretime;
+ SANE_Int scanystart;
+ SANE_Int scanylines;
+ SANE_Int findlermethod;
+ SANE_Int findlerstart;
+ SANE_Int findlerend;
+ SANE_Int checkoffsetser;
+ SANE_Int findserchecklines;
+ SANE_Int findserstart;
+ SANE_Int findserend;
+ SANE_Int findsermethod;
+ SANE_Int offsettoser;
+ SANE_Int offsettoler;
+};
+
+struct st_curve
+{
+ SANE_Int crv_speed; /* acceleration or deceleration */
+ SANE_Int crv_type;
+ SANE_Int step_count;
+ SANE_Int *step;
+};
+
+struct st_motorcurve
+{
+ SANE_Int mri;
+ SANE_Int msi;
+ SANE_Int skiplinecount;
+ SANE_Int motorbackstep;
+ SANE_Int curve_count;
+ struct st_curve **curve;
+};
+
+struct st_checkstable
+{
+ double diff;
+ SANE_Int interval;
+ long tottime;
+};
+
+struct st_sensorcfg
+{
+ SANE_Int type;
+ SANE_Int name;
+ SANE_Int resolution;
+
+ SANE_Int channel_color[3];
+ SANE_Int channel_gray[2];
+ SANE_Int rgb_order[3];
+
+ SANE_Int line_distance;
+ SANE_Int evenodd_distance;
+};
+
+struct st_autoref
+{
+ SANE_Byte type;
+ SANE_Int offset_x;
+ SANE_Int offset_y;
+ SANE_Int resolution;
+ SANE_Int extern_boundary;
+};
+
+struct st_motorcfg
+{
+ SANE_Byte type;
+ SANE_Int resolution;
+ SANE_Byte pwmfrequency;
+ SANE_Int basespeedpps;
+ SANE_Int basespeedmotormove;
+ SANE_Int highspeedmotormove;
+ SANE_Int parkhomemotormove;
+ SANE_Byte changemotorcurrent;
+};
+
+struct st_buttons
+{
+ SANE_Int count;
+ SANE_Int mask[6]; /* up to 6 buttons */
+};
+
+struct st_status
+{
+ SANE_Byte warmup;
+ SANE_Byte parkhome;
+ SANE_Byte cancel;
+};
+
+struct st_device
+{
+ /* next var handles usb device, used for every usb operations */
+ USB_Handle usb_handle;
+
+ /* next buffer will contain initial state registers of the chipset */
+ SANE_Byte *init_regs;
+
+ /* next structure will contain information and capabilities about chipset */
+ struct st_chip *chipset;
+
+ /* next structure will contain general configuration of stepper motor */
+ struct st_motorcfg *motorcfg;
+
+ /* next structure will contain general configuration of ccd sensor */
+ struct st_sensorcfg *sensorcfg;
+
+ /* next structure will contain all ccd timing values */
+ SANE_Int timings_count;
+ struct st_timing **timings;
+
+ /* next structure will contain all possible motor movements */
+ SANE_Int motormove_count;
+ struct st_motormove **motormove;
+
+ /* next structure will contain all motorcurve values */
+ SANE_Int mtrsetting_count;
+ struct st_motorcurve **mtrsetting;
+
+ /* next structure will contain all possible scanning modes for one scanner */
+ SANE_Int scanmodes_count;
+ struct st_scanmode **scanmodes;
+
+ /* next structure contains constrain values for one scanner */
+ struct st_constrains *constrains;
+
+ /* next structure contains supported buttons and their order */
+ struct st_buttons *buttons;
+
+ /* next structure will be used to resize scanned image */
+ struct st_resize *Resize;
+
+ /* next structure will be used while reading image from device */
+ struct st_readimage *Reading;
+
+ /* next structure will be used to arrange color channels while scanning */
+ struct st_scanning *scanning;
+
+ /* next structure will contain some status which can be requested */
+ struct st_status *status;
+};
+
+/* Unknown vars */
+SANE_Int v14b4 = 0;
+SANE_Byte *v1600 = NULL; /* tabla */
+SANE_Byte *v1604 = NULL; /* tabla */
+SANE_Byte *v1608 = NULL; /* tabla */
+SANE_Byte v160c_block_size;
+SANE_Int mem_total;
+SANE_Byte v1619;
+SANE_Int v15f8;
+
+SANE_Int acccurvecount; /* counter used y MotorSetup */
+SANE_Int deccurvecount; /* counter used y MotorSetup */
+SANE_Int smearacccurvecount; /* counter used y MotorSetup */
+SANE_Int smeardeccurvecount; /* counter used y MotorSetup */
+
+/* Known vars */
+SANE_Int offset[3];
+SANE_Byte gain[3];
+
+static SANE_Int usbfile = -1;
+SANE_Int scantype;
+
+SANE_Byte pwmlamplevel;
+
+SANE_Byte arrangeline;
+SANE_Byte binarythresholdh;
+SANE_Byte binarythresholdl;
+
+SANE_Byte shadingbase;
+SANE_Byte shadingfact[3];
+SANE_Byte arrangeline;
+SANE_Int compression;
+
+SANE_Byte linedarlampoff;
+SANE_Int pixeldarklevel;
+
+SANE_Int bw_threshold = 0x00;
+
+/* SetScanParams */
+struct st_scanparams scan;
+struct st_scanparams scan2;
+
+SANE_Int bytesperline; /* width * (3 colors [RGB]) */
+SANE_Int imagewidth3;
+SANE_Int lineart_width;
+SANE_Int imagesize; /* bytesperline * coords.height */
+SANE_Int imageheight;
+SANE_Int line_size;
+SANE_Int v15b4;
+SANE_Int v15bc;
+SANE_Int waitforpwm;
+
+SANE_Byte WRef[3];
+
+USHORT *fixed_black_shading[3] = { NULL, NULL, NULL };
+USHORT *fixed_white_shading[3] = { NULL, NULL, NULL };
+
+/* Calibration */
+struct st_gain_offset mitabla2; /* calibration table */
+SANE_Int v0750;
+
+static SANE_Byte use_gamma_tables;
+
+SANE_Int read_v15b4 = 0;
+
+SANE_Int v35b8 = 0;
+SANE_Int arrangeline2;
+
+SANE_Int v07c0 = 0;
+
+/* next structure contains coefficients for white shading correction */
+struct st_shading *wshading;
+
+struct st_gammatables *hp_gamma;
+struct st_gain_offset *default_gain_offset;
+struct st_calibration_data *calibdata;
+
+struct st_debug_opts *RTS_Debug;
+
+/* testing */
+SANE_Byte *jkd_black = NULL;
+SANE_Int jkd_blackbpl;
diff --git a/backend/hp3900_usb.c b/backend/hp3900_usb.c
new file mode 100644
index 0000000..440c963
--- /dev/null
+++ b/backend/hp3900_usb.c
@@ -0,0 +1,509 @@
+/* HP Scanjet 3900 series - USB layer
+
+ Copyright (C) 2005-2008 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef USBLAYER
+
+#define USBLAYER
+
+#define TIMEOUT 1000
+#define BLK_READ_EP 0x81
+#define BLK_WRITE_EP 0x02
+
+SANE_Int dataline_count = 0;
+
+/* USB layer commands */
+static SANE_Int usb_ctl_write (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * buffer, SANE_Int size,
+ SANE_Int index);
+static SANE_Int usb_ctl_read (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * buffer, SANE_Int size,
+ SANE_Int index);
+
+/* Higher level commands*/
+
+static SANE_Int IRead_Byte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * data, SANE_Int index);
+static SANE_Int IRead_Word (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int * data, SANE_Int index);
+static SANE_Int IRead_Integer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int * data, SANE_Int index);
+static SANE_Int IRead_Buffer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * buffer, SANE_Int size,
+ SANE_Int index);
+static SANE_Int IWrite_Byte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte data, SANE_Int index1,
+ SANE_Int index2);
+static SANE_Int IWrite_Word (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int data, SANE_Int index);
+static SANE_Int IWrite_Integer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int data, SANE_Int index);
+static SANE_Int IWrite_Buffer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * buffer, SANE_Int size,
+ SANE_Int index);
+
+static SANE_Int Read_Byte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * data);
+static SANE_Int Read_Word (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int * data);
+static SANE_Int Read_Integer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int * data);
+static SANE_Int Read_Buffer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * buffer, SANE_Int size);
+static SANE_Int Read_Bulk (USB_Handle usb_handle, SANE_Byte * buffer,
+ size_t size);
+static SANE_Int Write_Byte (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte data);
+static SANE_Int Write_Word (USB_Handle usb_handle, SANE_Int address,
+ SANE_Int data);
+/*static SANE_Int Write_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int data);*/
+static SANE_Int Write_Buffer (USB_Handle usb_handle, SANE_Int address,
+ SANE_Byte * buffer, SANE_Int size);
+static SANE_Int Write_Bulk (USB_Handle usb_handle, SANE_Byte * buffer,
+ SANE_Int size);
+
+static SANE_Int show_buffer (SANE_Int level, SANE_Byte * buffer,
+ SANE_Int size);
+
+/* Implementation */
+
+static SANE_Int
+IWrite_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data,
+ SANE_Int index1, SANE_Int index2)
+{
+ SANE_Int rst = ERROR;
+ SANE_Byte buffer[2] = { 0x00, 0x00 };
+
+ if (usb_ctl_read (usb_handle, address + 1, buffer, 0x02, index1) == 2)
+ {
+ buffer[1] = (buffer[0] & 0xff);
+ buffer[0] = (data & 0xff);
+
+ if (usb_ctl_write (usb_handle, address, buffer, 0x02, index2) == 2)
+ rst = OK;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+IWrite_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int data,
+ SANE_Int index)
+{
+ SANE_Int rst = ERROR;
+ SANE_Byte buffer[2];
+
+ buffer[0] = (data & 0xff);
+ buffer[1] = ((data >> 8) & 0xff);
+
+ if (usb_ctl_write (usb_handle, address, buffer, 0x02, index) == 2)
+ rst = OK;
+
+ return rst;
+}
+
+static SANE_Int
+IWrite_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int data,
+ SANE_Int index)
+{
+ SANE_Int rst = ERROR;
+ SANE_Byte buffer[4];
+
+ buffer[0] = (data & 0xff);
+ buffer[1] = ((data >> 8) & 0xff);
+ buffer[2] = ((data >> 16) & 0xff);
+ buffer[3] = ((data >> 24) & 0xff);
+
+ if (usb_ctl_write (usb_handle, address, buffer, 0x04, index) == 4)
+ rst = OK;
+
+ return rst;
+}
+
+static SANE_Int
+IWrite_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer,
+ SANE_Int size, SANE_Int index)
+{
+ SANE_Int ret = ERROR;
+
+ if (!((buffer == NULL) && (size > 0)))
+ if (usb_ctl_write (usb_handle, address, buffer, size, index) == size)
+ ret = OK;
+
+ return ret;
+}
+
+static SANE_Int
+IRead_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data,
+ SANE_Int index)
+{
+ SANE_Byte buffer[2] = { 0x00, 0x00 };
+ SANE_Int ret = ERROR;
+
+ if (data != NULL)
+ if (usb_ctl_read (usb_handle, address, buffer, 0x02, index) == 2)
+ {
+ *data = (SANE_Byte) (buffer[0] & 0xff);
+ ret = OK;
+ }
+
+ return ret;
+}
+
+static SANE_Int
+IRead_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int * data,
+ SANE_Int index)
+{
+ SANE_Byte buffer[2] = { 0x00, 0x00 };
+ SANE_Int ret = ERROR;
+
+ if (data != NULL)
+ if (usb_ctl_read (usb_handle, address, buffer, 0x02, index) == 2)
+ {
+ *data = ((buffer[1] << 8) & 0xffff) + (buffer[0] & 0xff);
+ ret = OK;
+ }
+
+ return ret;
+}
+
+static SANE_Int
+IRead_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int * data,
+ SANE_Int index)
+{
+ SANE_Byte buffer[4] = { 0x00, 0x00, 0x00, 0x00 };
+ SANE_Int ret = ERROR;
+
+ if (data != NULL)
+ {
+ *data = 0;
+ if (usb_ctl_read (usb_handle, address, buffer, 0x04, index) == 4)
+ {
+ SANE_Int C;
+ for (C = 3; C >= 0; C--)
+ *data = ((*data << 8) + (buffer[C] & 0xff)) & 0xffffffff;
+ ret = OK;
+ }
+ }
+
+ return ret;
+}
+
+static SANE_Int
+IRead_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer,
+ SANE_Int size, SANE_Int index)
+{
+ SANE_Int ret = ERROR;
+
+ if (buffer != NULL)
+ if (usb_ctl_read (usb_handle, address, buffer, size, index) == size)
+ ret = OK;
+
+ return ret;
+}
+
+static SANE_Int
+Write_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data)
+{
+ return IWrite_Byte (usb_handle, address, data, 0x100, 0);
+}
+
+static SANE_Int
+Write_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int data)
+{
+ return IWrite_Word (usb_handle, address, data, 0);
+}
+
+/*static SANE_Int Write_Integer(USB_Handle usb_handle, SANE_Int address, SANE_Int data)
+{
+ return IWrite_Integer(usb_handle, address, data, 0);
+}*/
+
+static SANE_Int
+Write_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer,
+ SANE_Int size)
+{
+ return IWrite_Buffer (usb_handle, address, buffer, size, 0);
+}
+
+static SANE_Int
+Read_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data)
+{
+ return IRead_Byte (usb_handle, address, data, 0x100);
+}
+
+static SANE_Int
+Read_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int * data)
+{
+ return IRead_Word (usb_handle, address, data, 0x100);
+}
+
+static SANE_Int
+Read_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int * data)
+{
+ return IRead_Integer (usb_handle, address, data, 0x100);
+}
+
+static SANE_Int
+Read_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer,
+ SANE_Int size)
+{
+ return IRead_Buffer (usb_handle, address, buffer, size, 0x100);
+}
+
+static SANE_Int
+Write_Bulk (USB_Handle usb_handle, SANE_Byte * buffer, SANE_Int size)
+{
+ SANE_Int rst = ERROR;
+
+ if (buffer != NULL)
+ {
+ dataline_count++;
+ DBG (DBG_CTL, "%06i BLK DO: %i. bytes\n", dataline_count, size);
+ show_buffer (4, buffer, size);
+
+#ifdef STANDALONE
+ if (usb_handle != NULL)
+ if (usb_bulk_write
+ (usb_handle, BLK_WRITE_EP, (char *) buffer, size,
+ TIMEOUT) == size)
+ rst = OK;
+#else
+ if (usb_handle != -1)
+ {
+ size_t mysize = size;
+ if (sanei_usb_write_bulk (usb_handle, buffer, &mysize) ==
+ SANE_STATUS_GOOD)
+ rst = OK;
+ }
+#endif
+ }
+
+ if (rst != OK)
+ DBG (DBG_CTL, " : Write_Bulk error\n");
+
+ return rst;
+}
+
+static SANE_Int
+Read_Bulk (USB_Handle usb_handle, SANE_Byte * buffer, size_t size)
+{
+ SANE_Int rst = ERROR;
+
+ if (buffer != NULL)
+ {
+ dataline_count++;
+ DBG (DBG_CTL, "%06i BLK DI: Buffer length = %lu. bytes\n",
+ dataline_count, (u_long) size);
+
+#ifdef STANDALONE
+ if (usb_handle != NULL)
+ rst =
+ usb_bulk_read (usb_handle, BLK_READ_EP, (char *) buffer, size,
+ TIMEOUT);
+#else
+ if (usb_handle != -1)
+ if (sanei_usb_read_bulk (usb_handle, buffer, &size) ==
+ SANE_STATUS_GOOD)
+ rst = size;
+#endif
+ }
+
+ if (rst < 0)
+ DBG (DBG_CTL, " : Read_Bulk error\n");
+ else
+ show_buffer (4, buffer, rst);
+
+ return rst;
+}
+
+static SANE_Int
+usb_ctl_write (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer,
+ SANE_Int size, SANE_Int index)
+{
+ SANE_Int rst = ERROR;
+
+ dataline_count++;
+ DBG (DBG_CTL, "%06i CTL DO: 40 04 %04x %04x %04x\n",
+ dataline_count, address & 0xffff, index, size);
+ show_buffer (DBG_CTL, buffer, size);
+
+#ifdef STANDALONE
+ if (usb_handle != NULL)
+ rst = usb_control_msg (usb_handle, 0x40, /* Request type */
+ 0x04, /* Request */
+ address, /* Value */
+ index, /* Index */
+ (char *) buffer, /* Buffer */
+ size, /* Size */
+ TIMEOUT);
+#else
+ if (usb_handle != -1)
+ {
+ if (sanei_usb_control_msg (usb_handle, 0x40, /* Request type */
+ 0x04, /* Request */
+ address, /* Value */
+ index, /* Index */
+ size, /* Size */
+ buffer) /* Buffer */
+ == SANE_STATUS_GOOD)
+ rst = size;
+ else
+ rst = -1;
+ }
+#endif
+
+ if (rst < 0)
+ DBG (DBG_CTL, " : Error, returned %i\n", rst);
+
+ return rst;
+}
+
+static SANE_Int
+usb_ctl_read (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer,
+ SANE_Int size, SANE_Int index)
+{
+ SANE_Int rst;
+
+ rst = ERROR;
+
+ dataline_count++;
+ DBG (DBG_CTL, "%06i CTL DI: c0 04 %04x %04x %04x\n",
+ dataline_count, address & 0xffff, index, size);
+
+#ifdef STANDALONE
+ if (usb_handle != NULL)
+ rst = usb_control_msg (usb_handle, 0xc0, /* Request type */
+ 0x04, /* Request */
+ address, /* Value */
+ index, /* Index */
+ (char *) buffer, /* Buffer */
+ size, /* Size */
+ TIMEOUT);
+#else
+ if (usb_handle != -1)
+ {
+ if (sanei_usb_control_msg (usb_handle, 0xc0, /* Request type */
+ 0x04, /* Request */
+ address, /* Value */
+ index, /* Index */
+ size, /* Size */
+ buffer) /* Buffer */
+ == SANE_STATUS_GOOD)
+ rst = size;
+ else
+ rst = -1;
+ }
+#endif
+
+ if (rst < 0)
+ DBG (DBG_CTL, " : Error, returned %i\n", rst);
+ else
+ show_buffer (DBG_CTL, buffer, rst);
+
+ return rst;
+}
+
+static SANE_Int
+show_buffer (SANE_Int level, SANE_Byte * buffer, SANE_Int size)
+{
+ if (DBG_LEVEL >= level)
+ {
+ char *sline = NULL;
+ char *sdata = NULL;
+ SANE_Int cont, data, offset = 0, col = 0;
+
+ if ((size > 0) && (buffer != NULL))
+ {
+ sline = (char *) malloc (256);
+ if (sline != NULL)
+ {
+ sdata = (char *) malloc (256);
+ if (sdata != NULL)
+ {
+ bzero (sline, 256);
+ for (cont = 0; cont < size; cont++)
+ {
+ if (col == 0)
+ {
+ if (cont == 0)
+ snprintf (sline, 255, " BF: ");
+ else
+ snprintf (sline, 255, " ");
+ }
+ data = (buffer[cont] & 0xff);
+ snprintf (sdata, 255, "%02x ", data);
+ sline = strcat (sline, sdata);
+ col++;
+ offset++;
+ if (col == 8)
+ {
+ col = 0;
+ snprintf (sdata, 255, " : %i\n", offset - 8);
+ sline = strcat (sline, sdata);
+ DBG (level, "%s", sline);
+ bzero (sline, 256);
+ }
+ }
+ if (col > 0)
+ {
+ for (cont = col; cont < 8; cont++)
+ {
+ snprintf (sdata, 255, "-- ");
+ sline = strcat (sline, sdata);
+ offset++;
+ }
+ snprintf (sdata, 255, " : %i\n", offset - 8);
+ sline = strcat (sline, sdata);
+ DBG (level, "%s", sline);
+ bzero (sline, 256);
+ }
+ free (sdata);
+ }
+ free (sline);
+ }
+ }
+ else
+ DBG (level, " BF: Empty buffer\n");
+ }
+ return OK;
+}
+#endif /*USBLAYER*/
diff --git a/backend/hp4200.c b/backend/hp4200.c
new file mode 100644
index 0000000..a069be6
--- /dev/null
+++ b/backend/hp4200.c
@@ -0,0 +1,2979 @@
+/*
+ Copyright (C) 2000 by Adrian Perez Jorge
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Developers:
+
+ Adrian Perez Jorge (APJ) -
+ Creator of the original HP4200C backend code.
+ adrianpj@easynews.com
+
+ Andrew John Lewis (AJL) -
+ lewi0235@tc.umn.edu
+
+ Arnar Mar Hrafnkelsson (AMH) -
+ addi@umich.edu
+
+ Frank Zago
+ some cleanups and integration into SANE
+
+ Henning Meier-Geinitz <henning@meier-geinitz.de>
+ more cleanups, bug fixes
+
+TODO:
+
+ - support more scanning resolutions.
+ - support different color depths.
+ - support gray and lineart.
+ - improve scanning speed. Compute scanning parameters based on the
+ image size and the scanner-to-host bandwidth.
+ - improve image quality.
+ - fix problem concerning mangled images
+
+*/
+
+#define BUILD 2
+#define BACKEND_NAME hp4200
+
+#include "../include/sane/config.h"
+
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_pv8630.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_backend.h"
+#include "hp4200.h"
+
+#include "hp4200_lm9830.c"
+
+#define HP4200_CONFIG_FILE "hp4200.conf"
+
+/*--------------------------------------------------------------------------*/
+
+#if 0
+/* Some of these resolution need work in color shifting. */
+static const SANE_Int dpi_list[] =
+ { 8, 50, 75, 100, 150, 200, 300, 400, 600 };
+#else
+static const SANE_Int dpi_list[] = { 4, 75, 150, 300, 600 };
+#endif
+static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 };
+static SANE_Range y_range =
+ { SANE_FIX (0), SANE_FIX (11.75 * MM_PER_INCH), 0 };
+static const SANE_Range u8_range = { 0, 255, 0 };
+
+struct coarse_t
+{
+ int min_red;
+ int min_green;
+ int min_blue;
+ int max_red;
+ int max_green;
+ int max_blue;
+ int red_gain;
+ int red_offset;
+ int green_gain;
+ int green_offset;
+ int blue_gain;
+ int blue_offset;
+};
+
+static const double hdpi_mapping[8] = { 1, 1.5, 2, 3, 4, 6, 8, 12 };
+
+static HP4200_Device *first_device = NULL; /* device list head */
+static int n_devices = 0; /* the device count */
+static const SANE_Device **devlist = NULL;
+
+static unsigned char
+getreg (HP4200_Scanner * s, unsigned char reg)
+{
+ unsigned char reg_value;
+
+ if ((reg > 0x08) && (reg < 0x5b))
+ return (unsigned char) LOBYTE (s->regs[reg]);
+ else
+ {
+ lm9830_read_register (s->fd, reg, &reg_value);
+ return reg_value;
+ }
+}
+
+static void
+setreg (HP4200_Scanner * s, unsigned char reg, unsigned char reg_value)
+{
+ s->regs[reg] = reg_value; /* dirty bit should be clear with this */
+ if ((reg < 0x08) || (reg > 0x5b))
+ {
+ lm9830_write_register (s->fd, reg, reg_value);
+ }
+}
+
+static void
+setbits (HP4200_Scanner * s, unsigned char reg, unsigned char bitmap)
+{
+ s->regs[reg] = (s->regs[reg] & 0xff) | bitmap;
+ if ((reg < 0x08) || (reg > 0x5b))
+ {
+ lm9830_write_register (s->fd, reg, LOBYTE (s->regs[reg]));
+ }
+}
+
+static void
+clearbits (HP4200_Scanner * s, unsigned char reg, unsigned char mask)
+{
+ s->regs[reg] = (s->regs[reg] & ~mask) & 0xff;
+ if ((reg < 0x08) || (reg > 0x5b))
+ {
+ lm9830_write_register (s->fd, reg, LOBYTE (s->regs[reg]));
+ }
+}
+
+static int
+cache_write (HP4200_Scanner * s)
+{
+ int i;
+#ifdef DEBUG_REG_CACHE
+ int counter = 0;
+#endif
+
+ DBG (DBG_proc, "Writing registers\n");
+
+ for (i = 0; i < 0x80; i++)
+ if (!(s->regs[i] & 0x100))
+ { /* modified register */
+#ifdef DEBUG_REG_CACHE
+ fprintf (stderr, "%.2x", i);
+ if (counter == 8)
+ fprintf (stderr, "\n");
+ else
+ fprintf (stderr, ", ");
+ counter = (counter + 1) % 9;
+#endif
+ lm9830_write_register (s->fd, i, s->regs[i]);
+ s->regs[i] |= 0x100; /* register is updated */
+ }
+ return 0;
+}
+
+/*
+ * HP4200-dependent register initialization.
+ */
+
+static int
+hp4200_init_registers (HP4200_Scanner * s)
+{
+ /* set up hardware parameters */
+
+ s->hw_parms.crystal_frequency = 48000000;
+ s->hw_parms.SRAM_size = 128; /* Kb */
+ s->hw_parms.scan_area_width = 5100; /* pixels */
+ s->hw_parms.scan_area_length = 11; /* inches */
+ s->hw_parms.min_pixel_data_buffer_limit = 1024; /* bytes */
+ s->hw_parms.sensor_line_separation = 4; /* lines */
+ s->hw_parms.sensor_max_integration_time = 12; /* milliseconds */
+ s->hw_parms.home_sensor = 2;
+ s->hw_parms.sensor_resolution = 1; /* 600 dpi */
+ s->hw_parms.motor_full_steps_per_inch = 300;
+ s->hw_parms.motor_max_speed = 1.4; /* inches/second */
+ s->hw_parms.num_tr_pulses = 1;
+ s->hw_parms.guard_band_duration = 1;
+ s->hw_parms.pulse_duration = 3;
+ s->hw_parms.fsteps_25_speed = 3;
+ s->hw_parms.fsteps_50_speed = 3;
+ s->hw_parms.target_value.red = 1000;
+ s->hw_parms.target_value.green = 1000;
+ s->hw_parms.target_value.blue = 1000;
+
+ {
+ int i;
+
+ /*
+ * we are using a cache-like data structure so registers whose
+ * values were written to the lm9830 and aren't volatile, have
+ * bit 0x100 activated. This bit must be cleared if you want the
+ * value to be written to the chip once cache_write() is called.
+ */
+
+ /* clears the registers cache */
+ memset (s->regs, 0, sizeof (s->regs));
+
+ /*
+ * registers 0x00 - 0x07 are non-cacheable/volatile, so don't
+ * read the values using the cache. Instead use direct functions
+ * to read/write registers.
+ */
+
+ for (i = 0; i < 0x08; i++)
+ s->regs[i] = 0x100;
+ }
+
+ setreg (s, 0x70, 0x70); /* noise filter */
+
+ setreg (s, 0x0b,
+ INPUT_SIGNAL_POLARITY_NEGATIVE |
+ CDS_ON |
+ SENSOR_STANDARD |
+ SENSOR_RESOLUTION_600 | LINE_SKIPPING_COLOR_PHASE_DELAY (0));
+
+ setreg (s, 0x0c,
+ PHI1_POLARITY_POSITIVE |
+ PHI2_POLARITY_POSITIVE |
+ RS_POLARITY_POSITIVE |
+ CP1_POLARITY_POSITIVE |
+ CP2_POLARITY_POSITIVE |
+ TR1_POLARITY_NEGATIVE | TR2_POLARITY_NEGATIVE);
+
+ setreg (s, 0x0d,
+ PHI1_ACTIVE |
+ PHI2_ACTIVE |
+ RS_ACTIVE |
+ CP1_ACTIVE |
+ CP2_OFF |
+ TR1_ACTIVE |
+ TR2_OFF | NUMBER_OF_TR_PULSES (s->hw_parms.num_tr_pulses));
+
+ setreg (s, 0x0e,
+ TR_PULSE_DURATION (s->hw_parms.pulse_duration) |
+ TR_PHI1_GUARDBAND_DURATION (s->hw_parms.guard_band_duration));
+
+ /* for pixel rate timing */
+ setreg (s, 0x0f, 6);
+ setreg (s, 0x10, 23);
+ setreg (s, 0x11, 1);
+ setreg (s, 0x12, 3);
+ setreg (s, 0x13, 3); /* 0 */
+ setreg (s, 0x14, 5); /* 0 */
+ setreg (s, 0x15, 0);
+ setreg (s, 0x16, 0);
+ setreg (s, 0x17, 11);
+ setreg (s, 0x18, 2); /* 1 */
+
+ setreg (s, 0x19, CIS_TR1_TIMING_OFF | FAKE_OPTICAL_BLACK_PIXELS_OFF);
+
+ setreg (s, 0x1a, 0);
+ setreg (s, 0x1b, 0);
+
+ setreg (s, 0x1c, 0x0d);
+ setreg (s, 0x1d, 0x21);
+
+ setreg (s, 0x27, TR_RED_DROP (0) | TR_GREEN_DROP (0) | TR_BLUE_DROP (0));
+
+ setreg (s, 0x28, 0x00);
+
+ setreg (s, 0x29, ILLUMINATION_MODE (1));
+ setreg (s, 0x2a, HIBYTE (0)); /* 0 */
+ setreg (s, 0x2b, LOBYTE (0)); /* 0 */
+
+ setreg (s, 0x2c, HIBYTE (16383));
+ setreg (s, 0x2d, LOBYTE (16383));
+
+ setreg (s, 0x2e, HIBYTE (2)); /* 2 */
+ setreg (s, 0x2f, LOBYTE (2)); /* 1 */
+
+ setreg (s, 0x30, HIBYTE (0));
+ setreg (s, 0x31, LOBYTE (0));
+
+ setreg (s, 0x32, HIBYTE (0));
+ setreg (s, 0x33, LOBYTE (0));
+
+ setreg (s, 0x34, HIBYTE (32));
+ setreg (s, 0x35, LOBYTE (32));
+
+ setreg (s, 0x36, HIBYTE (48));
+ setreg (s, 0x37, LOBYTE (48));
+
+ setreg (s, 0x42, EPP_MODE | PPORT_DRIVE_CURRENT (3));
+
+ setreg (s, 0x43,
+ RAM_SIZE_128 |
+ SRAM_DRIVER_CURRENT (3) | SRAM_BANDWIDTH_8 | SCANNING_FULL_DUPLEX);
+
+ setreg (s, 0x45,
+ MICRO_STEPPING |
+ CURRENT_SENSING_PHASES (2) |
+ PHASE_A_POLARITY_POSITIVE |
+ PHASE_B_POLARITY_POSITIVE | STEPPER_MOTOR_OUTPUT);
+
+ setreg (s, 0x4a, HIBYTE (100));
+ setreg (s, 0x4b, LOBYTE (100));
+
+ setreg (s, 0x4c, HIBYTE (0));
+ setreg (s, 0x4d, LOBYTE (0));
+
+ /* resume scan threshold */
+ setreg (s, 0x4f, 64);
+ /* steps to reverse */
+ setreg (s, 0x50, 40);
+ setreg (s, 0x51,
+ ACCELERATION_PROFILE_STOPPED (3) |
+ ACCELERATION_PROFILE_25P (s->hw_parms.fsteps_25_speed) |
+ ACCELERATION_PROFILE_50P (s->hw_parms.fsteps_50_speed));
+ setreg (s, 0x54, NON_REVERSING_EXTRA_LINES (0) | FIRST_LINE_TO_PROCESS (0));
+ setreg (s, 0x55, KICKSTART_STEPS (0) | HOLD_CURRENT_TIMEOUT (2));
+
+ /* stepper PWM frequency */
+ setreg (s, 0x56, 8);
+ /* stepper pwm duty cycle */
+ setreg (s, 0x57, 23);
+
+ setreg (s, 0x58,
+ PAPER_SENSOR_1_POLARITY_HIGH |
+ PAPER_SENSOR_1_TRIGGER_EDGE |
+ PAPER_SENSOR_1_NO_STOP_SCAN |
+ PAPER_SENSOR_2_POLARITY_HIGH |
+ PAPER_SENSOR_2_TRIGGER_EDGE | PAPER_SENSOR_2_STOP_SCAN);
+ setreg (s, 0x59,
+ MISCIO_1_TYPE_OUTPUT |
+ MISCIO_1_POLARITY_HIGH |
+ MISCIO_1_TRIGGER_EDGE |
+ MISCIO_1_OUTPUT_STATE_HIGH |
+ MISCIO_2_TYPE_OUTPUT |
+ MISCIO_2_POLARITY_HIGH |
+ MISCIO_2_TRIGGER_EDGE | MISCIO_2_OUTPUT_STATE_HIGH);
+
+ return 0;
+}
+
+#ifdef DEBUG
+static int
+dump_register_cache (HP4200_Scanner * s)
+{
+ int i;
+
+ for (i = 0; i < 0x80; i++)
+ {
+ fprintf (stderr, "%.2x:0x%.2x", i, s->regs[i]);
+ if ((i + 1) % 8)
+ fprintf (stderr, ", ");
+ else
+ fprintf (stderr, "\n");
+ }
+ fputs ("", stderr);
+ return 0;
+}
+#endif
+
+/*
+ * returns the scanner head to home position
+ */
+
+static int
+hp4200_goto_home (HP4200_Scanner * s)
+{
+ unsigned char cmd_reg;
+ unsigned char status_reg;
+ unsigned char old_paper_sensor_reg;
+
+ cmd_reg = getreg (s, 0x07);
+ if (cmd_reg != 2)
+ {
+ unsigned char paper_sensor_reg;
+ unsigned char sensor_bit[2] = { 0x02, 0x10 };
+ /* sensor head is not returning */
+
+ /* let's see if it's already at home */
+ /* first put paper (head) sensor level sensitive */
+ paper_sensor_reg = getreg (s, 0x58);
+ old_paper_sensor_reg = paper_sensor_reg;
+ paper_sensor_reg &= ~sensor_bit[s->hw_parms.home_sensor - 1];
+ setreg (s, 0x58, paper_sensor_reg);
+ cache_write (s);
+
+ /* if the scan head is not at home then move motor backwards */
+ status_reg = getreg (s, 0x02);
+ setreg (s, 0x58, old_paper_sensor_reg);
+ cache_write (s);
+ if (!(status_reg & s->hw_parms.home_sensor))
+ {
+ setreg (s, 0x07, 0x08);
+ usleep (10 * 1000);
+ setreg (s, 0x07, 0x00);
+ usleep (10 * 1000);
+ setreg (s, 0x07, 0x02);
+ }
+ }
+ return 0;
+}
+
+#define HP4200_CHECK_INTERVAL 1000 /* usecs between status checks */
+static int
+hp4200_wait_homed (HP4200_Scanner * s)
+{
+ unsigned char cmd_reg;
+
+ cmd_reg = getreg (s, 0x07);
+ while (cmd_reg != 0)
+ {
+ usleep (HP4200_CHECK_INTERVAL);
+ cmd_reg = getreg (s, 0x07);
+ }
+ return 0;
+}
+
+static int
+compute_fastfeed_step_size (unsigned long crystal_freq, int mclk,
+ float max_speed, int steps_per_inch,
+ int color_mode)
+{
+ int aux;
+ int r;
+
+ if (color_mode == 0)
+ r = 24;
+ else
+ r = 8;
+
+ aux = floor (crystal_freq / ((double) mclk * max_speed * 4.0 *
+ steps_per_inch * r));
+
+
+ if (aux < 2)
+ aux = 2;
+ return aux;
+}
+
+static SANE_Status
+read_available_data (HP4200_Scanner * s, SANE_Byte * buffer,
+ size_t * byte_count)
+{
+ SANE_Status status;
+ unsigned char scankb1;
+ unsigned char scankb2;
+ size_t to_read;
+ size_t really_read;
+ size_t chunk;
+
+ assert (buffer != NULL);
+
+ *byte_count = 0;
+ do
+ {
+ scankb1 = getreg (s, 0x01);
+ scankb2 = getreg (s, 0x01);
+ if (s->aborted_by_user)
+ return SANE_STATUS_CANCELLED;
+ }
+ while ((scankb1 != scankb2) || (scankb1 < 12));
+
+ to_read = scankb1 * 1024;
+
+ while (to_read)
+ {
+ if (s->aborted_by_user)
+ return SANE_STATUS_CANCELLED;
+ chunk = (to_read > 0xffff) ? 0xffff : to_read;
+
+ sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x00);
+ sanei_pv8630_prep_bulkread (s->fd, chunk);
+ really_read = chunk;
+ if ((status = sanei_usb_read_bulk (s->fd, buffer, &really_read)) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_usb_read_bulk failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (really_read > to_read)
+ {
+ DBG (DBG_error, "USB stack read more bytes than requested!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ *byte_count += really_read;
+ buffer += really_read;
+ to_read -= really_read;
+#ifdef DEBUG
+ fprintf (stderr, "read %d bytes\n", really_read);
+#endif
+ }
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef unused
+static int
+compute_datalink_bandwidth (HP4200_Scanner * s)
+{
+ int line_size;
+ int pause_limit;
+ unsigned int color_mode;
+
+ /*
+ * Line size for 8 bpp, the entire scan area width (plus the
+ * status byte) at optical resolution.
+ */
+
+ if (s->user_parms.color)
+ {
+ line_size = 3 * s->hw_parms.scan_area_width + 1;
+ color_mode = 0;
+ setreg (s, 0x26, color_mode); /* 3 channel pixel rate color */
+ }
+ else
+ {
+ line_size = s->hw_parms.scan_area_width + 1;
+ color_mode = 4;
+ setreg (s, 0x26, 0x08 | color_mode); /* 1 channel mode A (green) */
+ }
+ setreg (s, 0x09, (3 << 3)); /* h-divider = 1, 8 bpp */
+
+ {
+ int first_white_pixel;
+ unsigned int line_end;
+
+ first_white_pixel = s->hw_parms.sensor_pixel_end - 10;
+ line_end = first_white_pixel + s->hw_parms.scan_area_width;
+ if (line_end > (s->hw_parms.sensor_num_pixels - 20))
+ line_end = s->hw_parms.sensor_num_pixels - 20;
+
+ setreg (s, 0x1c, HIBYTE (s->hw_parms.sensor_pixel_start));
+ setreg (s, 0x1d, LOBYTE (s->hw_parms.sensor_pixel_end));
+ setreg (s, 0x1e, HIBYTE (first_white_pixel));
+ setreg (s, 0x1f, LOBYTE (first_white_pixel));
+ setreg (s, 0x20, HIBYTE (s->hw_parms.sensor_num_pixels));
+ setreg (s, 0x21, LOBYTE (s->hw_parms.sensor_num_pixels));
+ setreg (s, 0x22, getreg (s, 0x1e));
+ setreg (s, 0x23, getreg (s, 0x1f));
+ setreg (s, 0x24, HIBYTE (line_end));
+ setreg (s, 0x25, LOBYTE (line_end));
+ }
+
+ /*
+ * During transfer rate calculation don't forward scanner sensor.
+ * Stay in the calibration region.
+ */
+
+ setreg (s, 0x4f, 0);
+ clearbits (s, 0x45, 0x10);
+
+ /*
+ * Pause the scan when memory is full.
+ */
+
+ pause_limit = s->hw_parms.SRAM_size - (line_size / 1024) - 1;
+ setreg (s, 0x4e, pause_limit & 0xff);
+
+ s->mclk = compute_min_mclk (s->hw_parms.SRAM_bandwidth,
+ s->hw_parms.crystal_frequency);
+
+
+ /*
+ * Set step size to fast speed.
+ */
+
+ {
+ int step_size;
+
+ step_size =
+ compute_fastfeed_step_size (s->hw_parms.crystal_frequency,
+ s->mclk,
+ s->hw_parms.scan_bar_max_speed,
+ s->hw_parms.motor_full_steps_per_inch,
+ color_mode);
+
+ setreg (s, 0x46, HIBYTE (step_size));
+ setreg (s, 0x47, LOBYTE (step_size));
+ setreg (s, 0x48, HIBYTE (step_size));
+ setreg (s, 0x49, LOBYTE (step_size));
+ }
+
+ cache_write (s);
+
+ /* dump_register_cache (s); */
+
+ /*
+ * scan during 1 sec. aprox.
+ */
+
+ setreg (s, 0x07, 0x08);
+ setreg (s, 0x07, 0x03);
+
+ {
+ struct timeval tv_before;
+ struct timeval tv_after;
+ int elapsed_time_ms = 0;
+ long bytes_read_total;
+ SANE_Byte *buffer;
+
+ buffer = malloc (2 * 98304); /* check this */
+ if (!buffer)
+ {
+ DBG (DBG_error, "compute_datalink_bandwidth: malloc failed\n");
+ return 0;
+ }
+ bytes_read_total = 0;
+ gettimeofday (&tv_before, NULL);
+ do
+ {
+ size_t bytes_read;
+ SANE_Status status;
+
+ status = read_available_data (s, buffer, &bytes_read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_available_data failed (%s)\n",
+ sane_strstatus (status));
+ return 0;
+ }
+ bytes_read_total += bytes_read;
+ gettimeofday (&tv_after, NULL);
+ elapsed_time_ms = (tv_after.tv_sec - tv_before.tv_sec) * 1000;
+ elapsed_time_ms += (tv_after.tv_usec - tv_before.tv_usec) / 1000;
+ }
+ while (elapsed_time_ms < 1000);
+
+ setreg (s, 0x07, 0x00);
+ free (buffer);
+
+ s->msrd_parms.datalink_bandwidth = bytes_read_total /
+ (elapsed_time_ms / 1000);
+
+#ifdef DEBUG
+ fprintf (stderr, "PC Transfer rate = %d bytes/sec. (%ld/%d)\n",
+ s->msrd_parms.datalink_bandwidth, bytes_read_total,
+ elapsed_time_ms);
+#endif
+ }
+ return 0;
+}
+#endif
+
+static void
+compute_first_gain_offset (int target, int max, int min, int *gain,
+ int *offset, int *max_gain, int *min_offset)
+{
+ *gain = (int) 15.0 *(target / (max - min) - 0.933);
+ *offset = (int) (-1.0 * min / (512.0 * 0.0195));
+ if (*gain >= 32)
+ {
+ *gain = (int) 15.0 *(target / 3.0 / (max - min) - 0.933);
+ *offset = (int) -3.0 * min / (512.0 * 0.0195);
+ }
+ if (*gain < 0)
+ *gain = 0;
+ else if (*gain > 63)
+ *gain = 63;
+
+ if (*offset < -31)
+ *offset = -31;
+ else if (*offset > 31)
+ *offset = 31;
+
+ *max_gain = 63;
+ *min_offset = -31;
+}
+
+#define DATA_PORT_READ (1 << 5)
+#define DATA_PORT_WRITE 0
+
+static int
+write_gamma (HP4200_Scanner * s)
+{
+ SANE_Status status;
+ int color;
+ int i;
+ unsigned char gamma[1024];
+ unsigned char read_gamma[1024];
+ int retval;
+ size_t to_read;
+ size_t to_write;
+
+ for (color = 0; color < 3; color++)
+ {
+ for (i = 0; i < 1024; i++)
+ gamma[i] = s->user_parms.gamma[color][i];
+
+ setreg (s, 0x03, color << 1);
+ setreg (s, 0x04, DATA_PORT_WRITE);
+ setreg (s, 0x05, 0x00);
+ sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
+ sanei_pv8630_prep_bulkwrite (s->fd, sizeof (gamma));
+ to_write = sizeof (gamma);
+ sanei_usb_write_bulk (s->fd, gamma, &to_write);
+
+ /* check if gamma vector was correctly written */
+
+ setreg (s, 0x03, color << 1);
+ setreg (s, 0x04, DATA_PORT_READ);
+ setreg (s, 0x05, 0x00);
+ sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
+ sanei_pv8630_prep_bulkread (s->fd, sizeof (read_gamma));
+ to_read = sizeof (read_gamma);
+ status = sanei_usb_read_bulk (s->fd, read_gamma, &to_read);
+ retval = memcmp (read_gamma, gamma, sizeof (read_gamma));
+ if (retval != 0)
+ {
+ DBG (DBG_error, "error: color %d has bad gamma table\n", color);
+ }
+#ifdef DEBUG
+ else
+ fprintf (stderr, "color %d gamma table is good\n", color);
+#endif
+ }
+
+ return 0;
+}
+
+static int
+write_default_offset_gain (HP4200_Scanner * s, SANE_Byte * gain_offset,
+ int size, int color)
+{
+ SANE_Byte *check_data;
+ int retval;
+ size_t to_read;
+ size_t to_write;
+
+ setreg (s, 0x03, (color << 1) | 1);
+ setreg (s, 0x04, DATA_PORT_WRITE);
+ setreg (s, 0x05, 0x00);
+ sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
+ sanei_pv8630_prep_bulkwrite (s->fd, size);
+ to_write = size;
+ sanei_usb_write_bulk (s->fd, gain_offset, &to_write);
+
+ check_data = malloc (size);
+ setreg (s, 0x03, (color << 1) | 1);
+ setreg (s, 0x04, DATA_PORT_READ);
+ setreg (s, 0x05, 0x00);
+ sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
+ sanei_pv8630_prep_bulkread (s->fd, size);
+ to_read = size;
+ sanei_usb_read_bulk (s->fd, check_data, &to_read);
+ retval = memcmp (gain_offset, check_data, size);
+ free (check_data);
+ if (retval != 0)
+ {
+ DBG (DBG_error, "error: color %d has bad gain/offset table\n", color);
+ }
+#ifdef DEBUG
+ else
+ fprintf (stderr, "color %d gain/offset table is good\n", color);
+#endif
+
+ return 0;
+}
+
+static int
+compute_gain_offset (int target, int max, int min, int *gain,
+ int *offset, int *max_gain, int *min_offset)
+{
+ int gain_stable;
+ int is_unstable;
+
+ gain_stable = 1; /* unless the opposite is said */
+ is_unstable = 0;
+
+ if (max > target)
+ {
+ if (*gain > 0)
+ {
+ (*gain)--;
+ *max_gain = *gain;
+ gain_stable = 0;
+ is_unstable |= 1;
+ }
+ else
+ {
+ DBG (DBG_error, "error: integration time too long.\n");
+ return -1;
+ }
+ }
+ else
+ {
+ if (*gain < *max_gain)
+ {
+ (*gain)++;
+ gain_stable = 0;
+ is_unstable |= 1;
+ }
+ }
+
+ if (min == 0)
+ {
+ if (*offset < 31)
+ {
+ (*offset)++;
+ if (gain_stable)
+ *min_offset = *offset;
+ is_unstable |= 1;
+ }
+ else
+ {
+ DBG (DBG_error, "error: max static has pixel value == 0\n");
+ return -1;
+ }
+ }
+ else
+ {
+ if (*offset > *min_offset)
+ {
+ (*offset)--;
+ is_unstable |= 1;
+ }
+ }
+ return is_unstable;
+}
+
+static int
+compute_bytes_per_line (int width_in_pixels, unsigned char hdpi_code,
+ unsigned char pixel_packing,
+ unsigned char data_mode,
+ unsigned char AFE_operation, int m)
+{
+ const int dpi_qot_mul[] = { 1, 2, 1, 1, 1, 1, 1, 1 };
+ const int dpi_qot_div[] = { 1, 3, 2, 3, 4, 6, 8, 12 };
+ int pixels_per_line;
+ int bytes_per_line;
+ int pixels_per_byte;
+ int status_bytes;
+ const int pixels_per_byte_mapping[] = { 8, 4, 2, 1 };
+
+ assert (hdpi_code <= 7);
+ pixels_per_line = (width_in_pixels * dpi_qot_mul[hdpi_code]) /
+ dpi_qot_div[hdpi_code];
+ if ((width_in_pixels * dpi_qot_mul[hdpi_code]) % dpi_qot_div[hdpi_code])
+ pixels_per_line++;
+
+
+ status_bytes = (m == 0) ? 1 : m;
+
+ if (data_mode == 1)
+ pixels_per_byte = 1; /* should be 0.5 but later
+ bytes_per_line will be multiplied
+ by 2, and also the number of status
+ bytes, that in this case should be
+ 2.
+ umm.. maybe this should be done in
+ the cleaner way.
+ */
+ else
+ {
+ assert (pixel_packing <= 3);
+ pixels_per_byte = pixels_per_byte_mapping[pixel_packing];
+ }
+
+ switch (AFE_operation)
+ {
+ case PIXEL_RATE_3_CHANNELS:
+ bytes_per_line = ((pixels_per_line * 3) / pixels_per_byte) +
+ status_bytes;
+ break;
+ case MODEA_1_CHANNEL:
+ bytes_per_line = (pixels_per_line / pixels_per_byte) + status_bytes;
+ break;
+ default:
+ /* Not implemented! (yet?) and not used.
+ * This case should not happen. */
+ assert (0);
+ }
+
+ if (data_mode == 1) /* see big note above */
+ bytes_per_line *= 2;
+
+ return bytes_per_line;
+}
+
+static int
+compute_pause_limit (hardware_parameters_t * hw_parms, int bytes_per_line)
+{
+ int coef_size;
+ const int coef_mapping[] = { 16, 32 };
+ int pause_limit;
+
+ coef_size = coef_mapping[hw_parms->sensor_resolution & 0x01];
+ pause_limit = hw_parms->SRAM_size - coef_size - (bytes_per_line / 1024) - 1;
+
+ if (pause_limit > 2)
+ pause_limit -= 2;
+
+ return pause_limit;
+}
+
+static int
+compute_dpd (HP4200_Scanner * s, int step_size, int line_end)
+{
+ int tr, dpd;
+
+ tr = 1 /* color mode */ *
+ (line_end + ((s->hw_parms.num_tr_pulses + 1) *
+ (2 * s->hw_parms.guard_band_duration +
+ s->hw_parms.pulse_duration + 1) +
+ 3 - s->hw_parms.num_tr_pulses));
+
+ if (tr == 0)
+ return 0;
+
+ dpd = (((s->hw_parms.fsteps_25_speed * 4) +
+ (s->hw_parms.fsteps_50_speed * 2) +
+ s->hw_parms.steps_to_reverse) * 4 * step_size) % tr;
+ dpd = tr - dpd;
+
+ return dpd;
+}
+
+static SANE_Status
+read_required_bytes (HP4200_Scanner * s, int required, SANE_Byte * buffer)
+{
+ int read_count = 0;
+ unsigned char scankb1;
+ unsigned char scankb2;
+ size_t to_read;
+ size_t really_read;
+ size_t chunk;
+ SANE_Status status;
+
+ assert (buffer != NULL);
+
+ while (required)
+ {
+ do
+ {
+ scankb1 = getreg (s, 0x01);
+ scankb2 = getreg (s, 0x01);
+ if (s->aborted_by_user)
+ return SANE_STATUS_CANCELLED;
+ }
+ while ((scankb1 != scankb2) || (scankb1 < 12));
+
+ to_read = min (required, (scankb1 * 1024));
+ while (to_read)
+ {
+ if (s->aborted_by_user)
+ return SANE_STATUS_CANCELLED;
+ chunk = (to_read > 0xffff) ? 0xffff : to_read;
+
+ sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x00);
+ sanei_pv8630_prep_bulkread (s->fd, chunk);
+ really_read = chunk;
+ if ((status = sanei_usb_read_bulk (s->fd, buffer, &really_read)) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_usb_read_bulk failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (really_read > chunk)
+ {
+ DBG (DBG_error, "USB stack read more bytes than requested!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ buffer += really_read;
+ required -= really_read;
+ to_read -= really_read;
+ read_count += really_read;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+scanner_buffer_init (scanner_buffer_t * sb, int size_in_kb)
+{
+
+ sb->size = size_in_kb * 1024 + 3;
+ sb->buffer = malloc (sb->size);
+ if (!sb->buffer)
+ return SANE_STATUS_NO_MEM;
+ sb->num_bytes = 0;
+ sb->data_ptr = sb->buffer;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+scanner_buffer_read (HP4200_Scanner * s)
+{
+ SANE_Status status;
+ size_t num_bytes_read_now;
+
+ assert (s->scanner_buffer.num_bytes <= 3);
+
+ memcpy (s->scanner_buffer.buffer, s->scanner_buffer.data_ptr, 3);
+
+ status = read_available_data (s, s->scanner_buffer.buffer +
+ s->scanner_buffer.num_bytes,
+ &num_bytes_read_now);
+ s->scanner_buffer.data_ptr = s->scanner_buffer.buffer;
+ s->scanner_buffer.num_bytes += num_bytes_read_now;
+ return status;
+}
+
+#define OFFSET_CODE_SIGN(off) (((off) < 0) ? (-(off) & 0x1f) | 0x20 : (off))
+#define OFFSET_DECODE_SIGN(off) (((off) & 0x20) ? -(off & 0x1f) : (off))
+
+static SANE_Status
+do_coarse_calibration (HP4200_Scanner * s, struct coarse_t *coarse)
+{
+ SANE_Status status;
+ unsigned char *cal_line = NULL;
+ unsigned char *cal_line_ptr;
+ int cal_line_size;
+ /* local scanning params */
+ int active_pixels_start;
+ int line_end;
+ int data_pixels_start;
+ int data_pixels_end;
+ int dpd;
+ int step_size;
+ int ff_step_size;
+ char steps_to_reverse;
+ char hdpi_div;
+ char line_rate_color;
+ int vdpi; /* vertical dots per inch */
+ int hdpi_code;
+ int calibrated;
+ int first_time;
+
+ int red_offset = 0;
+ int green_offset = 0;
+ int blue_offset = 0;
+
+ int red_gain = 1;
+ int green_gain = 1;
+ int blue_gain = 1;
+
+ int min_red_offset = -31;
+ int min_green_offset = -31;
+ int min_blue_offset = -31;
+
+ int max_red_gain = 63;
+ int max_green_gain = 63;
+ int max_blue_gain = 63;
+
+ int max_red;
+ int min_red;
+ int max_green;
+ int min_green;
+ int max_blue;
+ int min_blue;
+ static char me[] = "do_coarse_calibration";
+
+ DBG (DBG_proc, "%s\n", me);
+
+ setreg (s, 0x07, 0x00);
+ usleep (10 * 1000);
+
+ vdpi = 150;
+ hdpi_code = 0;
+ hdpi_div = hdpi_mapping[hdpi_code];
+ active_pixels_start = 0x40;
+ line_end = 0x2ee0;
+ s->mclk_div = 2;
+ data_pixels_start = 0x40;
+ data_pixels_end = (int) (data_pixels_start + s->hw_parms.scan_area_width);
+ data_pixels_end = min (data_pixels_end, line_end - 20);
+
+ cal_line_size = s->hw_parms.scan_area_width * 3 * 2 + 2;
+
+ setreg (s, 0x1e, HIBYTE (active_pixels_start));
+ setreg (s, 0x1f, LOBYTE (active_pixels_start));
+ setreg (s, 0x20, HIBYTE (line_end));
+ setreg (s, 0x21, LOBYTE (line_end));
+ setreg (s, 0x22, HIBYTE (data_pixels_start));
+ setreg (s, 0x23, LOBYTE (data_pixels_start));
+ setreg (s, 0x24, HIBYTE (data_pixels_end));
+ setreg (s, 0x25, LOBYTE (data_pixels_end));
+
+ setreg (s, 0x26,
+ PIXEL_RATE_3_CHANNELS |
+ GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0));
+
+
+ setreg (s, 0x08, (s->mclk_div - 1) * 2);
+ setreg (s, 0x09, hdpi_code | PIXEL_PACKING (3) | DATAMODE (1));
+ setreg (s, 0x0a, 0); /* reserved and strange register */
+
+ setreg (s, 0x38, red_offset);
+ setreg (s, 0x39, green_offset);
+ setreg (s, 0x3a, blue_offset);
+ setreg (s, 0x3b, red_gain);
+ setreg (s, 0x3c, green_gain);
+ setreg (s, 0x3d, blue_gain);
+
+ setreg (s, 0x5e, 0x80);
+
+ setreg (s, 0x3e, 0x00); /* 1.5:1, 6/10 bits, 2*fixed */
+ setreg (s, 0x3f, 0x00);
+ setreg (s, 0x40, 0x00);
+ setreg (s, 0x41, 0x00);
+
+ setreg (s, 0x4e, 0x5b - 0x3c); /* max Kb to pause */
+ setreg (s, 0x4f, 0x02); /* min Kb to resume */
+
+ line_rate_color = 1;
+ step_size = (vdpi * line_end * line_rate_color) /
+ (4 * s->hw_parms.motor_full_steps_per_inch);
+
+ dpd = compute_dpd (s, step_size, line_end); /* 0x0ada; */
+#ifdef DEBUG
+ fprintf (stderr, "dpd = %d\n", dpd);
+#endif
+ setreg (s, 0x52, HIBYTE (dpd));
+ setreg (s, 0x53, LOBYTE (dpd));
+
+ setreg (s, 0x46, HIBYTE (step_size));
+ setreg (s, 0x47, LOBYTE (step_size));
+
+ ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0); /* 0x0190; */
+ setreg (s, 0x48, HIBYTE (ff_step_size));
+ setreg (s, 0x49, LOBYTE (ff_step_size));
+ setreg (s, 0x4b, 0x15);
+ steps_to_reverse = 0x3f;
+ setreg (s, 0x50, steps_to_reverse);
+ setreg (s, 0x51, 0x15); /* accel profile */
+
+ /* this is to stay the motor stopped */
+ clearbits (s, 0x45, (1 << 4));
+
+ cache_write (s);
+
+ calibrated = 0;
+ first_time = 1;
+ cal_line = malloc (cal_line_size + 1024);
+
+ do
+ {
+ unsigned char cmd_reg;
+
+ /* resets the lm9830 before start scanning */
+ setreg (s, 0x07, 0x08);
+ do
+ {
+ setreg (s, 0x07, 0x03);
+ cmd_reg = getreg (s, 0x07);
+ }
+ while (cmd_reg != 0x03);
+
+ cal_line_ptr = cal_line;
+ status = read_required_bytes (s, cal_line_size, cal_line_ptr);
+ if (status != SANE_STATUS_GOOD)
+ goto done;
+
+ setreg (s, 0x07, 0x00);
+ {
+ unsigned int i;
+ min_red = max_red = (cal_line[0] * 256 + cal_line[1]) >> 2;
+ min_green = max_green = (cal_line[2] * 256 + cal_line[3]) >> 2;
+ min_blue = max_blue = (cal_line[4] * 256 + cal_line[5]) >> 2;
+ for (i = 6; i < (s->hw_parms.scan_area_width * 3 * 2); i += 6)
+ {
+ int value;
+
+ value = cal_line[i] * 256 + cal_line[i + 1];
+ value >>= 2;
+ if (value > max_red)
+ max_red = value;
+ value = cal_line[i + 2] * 256 + cal_line[i + 3];
+ value >>= 2;
+ if (value > max_green)
+ max_green = value;
+ value = cal_line[i + 4] * 256 + cal_line[i + 5];
+ value >>= 2;
+ if (value > max_blue)
+ max_blue = value;
+ value = cal_line[i] * 256 + cal_line[i + 1];
+ value >>= 2;
+ if (value < min_red)
+ min_red = value;
+ value = cal_line[i + 2] * 256 + cal_line[i + 3];
+ value >>= 2;
+ if (value < min_green)
+ min_green = value;
+ value = cal_line[i + 4] * 256 + cal_line[i + 5];
+ value >>= 2;
+ if (value < min_blue)
+ min_blue = value;
+ }
+#ifdef DEBUG
+ fprintf (stderr, "max_red:%d max_green:%d max_blue:%d\n",
+ max_red, max_green, max_blue);
+ fprintf (stderr, "min_red:%d min_green:%d min_blue:%d\n",
+ min_red, min_green, min_blue);
+#endif
+
+ if (first_time)
+ {
+ first_time = 0;
+ compute_first_gain_offset (s->hw_parms.target_value.red,
+ max_red, min_red,
+ &red_gain, &red_offset,
+ &max_red_gain, &min_red_offset);
+ compute_first_gain_offset (s->hw_parms.target_value.green,
+ max_green, min_green,
+ &green_gain, &green_offset,
+ &max_green_gain, &min_green_offset);
+ compute_first_gain_offset (s->hw_parms.target_value.blue,
+ max_blue, min_blue, &blue_gain,
+ &blue_offset, &max_blue_gain,
+ &min_blue_offset);
+ }
+ else
+ {
+ int retval;
+
+ /* this code should check return value -1 for error */
+
+ retval = compute_gain_offset (s->hw_parms.target_value.red,
+ max_red, min_red,
+ &red_gain, &red_offset,
+ &max_red_gain, &min_red_offset);
+ if (retval < 0)
+ break;
+ retval |= compute_gain_offset (s->hw_parms.target_value.green,
+ max_green, min_green,
+ &green_gain, &green_offset,
+ &max_green_gain,
+ &min_green_offset);
+ if (retval < 0)
+ break;
+ retval |= compute_gain_offset (s->hw_parms.target_value.blue,
+ max_blue, min_blue,
+ &blue_gain, &blue_offset,
+ &max_blue_gain, &min_blue_offset);
+ if (retval < 0)
+ break;
+ calibrated = !retval;
+ }
+
+ setreg (s, 0x3b, red_gain);
+ setreg (s, 0x3c, green_gain);
+ setreg (s, 0x3d, blue_gain);
+
+ setreg (s, 0x38, OFFSET_CODE_SIGN (red_offset));
+ setreg (s, 0x39, OFFSET_CODE_SIGN (green_offset));
+ setreg (s, 0x3a, OFFSET_CODE_SIGN (blue_offset));
+
+#ifdef DEBUG
+ fprintf (stderr, "%d, %d, %d %d, %d, %d\n", red_gain,
+ green_gain, blue_gain, red_offset, green_offset,
+ blue_offset);
+#endif
+ cache_write (s);
+ }
+ }
+ while (!calibrated);
+ coarse->min_red = min_red;
+ coarse->min_green = min_green;
+ coarse->min_blue = min_blue;
+ coarse->max_red = max_red;
+ coarse->max_green = max_green;
+ coarse->max_blue = max_blue;
+ coarse->red_gain = red_gain;
+ coarse->green_gain = green_gain;
+ coarse->blue_gain = blue_gain;
+ coarse->red_offset = red_offset;
+ coarse->green_offset = green_offset;
+ coarse->blue_offset = blue_offset;
+
+ status = SANE_STATUS_GOOD;
+
+done:
+ if (cal_line)
+ free (cal_line);
+
+ return status;
+}
+
+static int
+compute_corr_code (int average, int min_color, int range, int target)
+{
+ int value;
+ int corr_code;
+
+ value = average - min_color;
+ if (value > 0)
+ corr_code =
+ (int) (range * ((double) target / (double) value - 1.0) + 0.5);
+ else
+ corr_code = 0;
+ if (corr_code < 0)
+ corr_code = 0;
+ else if (corr_code > 2048)
+ corr_code = 0;
+ else if (corr_code > 1023)
+ corr_code = 1023;
+ return corr_code;
+}
+
+static int
+compute_hdpi_code (int hres)
+{
+ int hdpi_code;
+
+ /* Calculate the horizontal DPI code based on the requested
+ horizontal resolution. Defaults to 150dpi. */
+ switch (hres)
+ {
+ case 600:
+ hdpi_code = 0;
+ break;
+ case 400:
+ hdpi_code = 1;
+ break;
+ case 300:
+ hdpi_code = 2;
+ break;
+ case 200:
+ hdpi_code = 3;
+ break;
+ case 150:
+ hdpi_code = 4;
+ break;
+ case 100:
+ hdpi_code = 5;
+ break;
+ case 75:
+ hdpi_code = 6;
+ break;
+ case 50:
+ hdpi_code = 7;
+ break;
+ default:
+ hdpi_code = 4;
+ }
+ return hdpi_code;
+}
+
+
+static SANE_Status
+do_fine_calibration (HP4200_Scanner * s, struct coarse_t *coarse)
+{
+ SANE_Status status;
+ unsigned char *cal_line;
+ unsigned char *cal_line_ptr;
+ int *average;
+ SANE_Byte red_gain_offset[5460 * 2];
+ SANE_Byte green_gain_offset[5460 * 2];
+ SANE_Byte blue_gain_offset[5460 * 2];
+ int *corr_red = NULL;
+ int *corr_green = NULL;
+ int *corr_blue = NULL;
+ int registro[30][5460 * 3];
+ int cal_line_size;
+ /* local scanning params */
+ int active_pixels_start;
+ int line_end;
+ int line_length;
+ int data_pixels_start;
+ int data_pixels_end;
+ int dpd;
+ int step_size;
+ int ff_step_size;
+ char steps_to_reverse;
+ char hdpi_div;
+ char line_rate_color;
+ int vdpi; /* vertical dots per inch */
+ int hdpi_code;
+ int calibrated;
+ int first_time;
+ int lines_to_process;
+
+ static char me[] = "do_fine_calibration";
+
+ DBG (DBG_proc, "%s\n", me);
+
+ setreg (s, 0x07, 0x00);
+ usleep (10 * 1000);
+
+ vdpi = 150;
+ hdpi_code = compute_hdpi_code (s->user_parms.horizontal_resolution);
+
+ /* figure out which horizontal divider to use based on the
+ calculated horizontal dpi code */
+ hdpi_div = hdpi_mapping[hdpi_code];
+ active_pixels_start = 0x40;
+ line_end = 0x2ee0;
+ line_length = s->user_parms.image_width * hdpi_div;
+ s->mclk_div = 2;
+ data_pixels_start = 0x72 + s->runtime_parms.first_pixel * hdpi_div;
+ data_pixels_end =
+ (int) (data_pixels_start + s->user_parms.image_width * hdpi_div);
+ data_pixels_end = min (data_pixels_end, line_end - 20);
+
+ cal_line_size = line_length * 3 * 2 + 2;
+
+ setreg (s, 0x1e, HIBYTE (active_pixels_start));
+ setreg (s, 0x1f, LOBYTE (active_pixels_start));
+ setreg (s, 0x20, HIBYTE (line_end));
+ setreg (s, 0x21, LOBYTE (line_end));
+ setreg (s, 0x22, HIBYTE (data_pixels_start));
+ setreg (s, 0x23, LOBYTE (data_pixels_start));
+ setreg (s, 0x24, HIBYTE (data_pixels_end));
+ setreg (s, 0x25, LOBYTE (data_pixels_end));
+
+ setreg (s, 0x26,
+ PIXEL_RATE_3_CHANNELS |
+ GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0));
+
+
+ setreg (s, 0x08, (s->mclk_div - 1) * 2);
+ setreg (s, 0x09, 0 | PIXEL_PACKING (3) | DATAMODE (1));
+ setreg (s, 0x0a, 0); /* reserved and strange register */
+
+ setreg (s, 0x38, 1);
+ setreg (s, 0x39, 1);
+ setreg (s, 0x3a, 1);
+ setreg (s, 0x3b, coarse->red_gain);
+ setreg (s, 0x3c, coarse->green_gain);
+ setreg (s, 0x3d, coarse->blue_gain);
+
+ setreg (s, 0x5e, 0x80);
+
+ setreg (s, 0x3e, 0x00); /* 1.5:1, 6/10 bits, 2*fixed */
+ setreg (s, 0x3f, 0x00);
+ setreg (s, 0x40, 0x00);
+ setreg (s, 0x41, 0x00);
+
+ setreg (s, 0x4e, 0x5b - 0x3c); /* max Kb to pause */
+ setreg (s, 0x4f, 0x02); /* min Kb to resume */
+
+ line_rate_color = 1;
+ step_size = (vdpi * line_end * line_rate_color) /
+ (4 * s->hw_parms.motor_full_steps_per_inch);
+
+ dpd = compute_dpd (s, step_size, line_end); /* 0x0ada; */
+#ifdef DEBUG
+ fprintf (stderr, "dpd = %d\n", dpd);
+#endif
+ setreg (s, 0x52, HIBYTE (dpd));
+ setreg (s, 0x53, LOBYTE (dpd));
+
+ setreg (s, 0x46, HIBYTE (step_size));
+ setreg (s, 0x47, LOBYTE (step_size));
+
+ ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0); /* 0x0190; */
+ setreg (s, 0x48, HIBYTE (ff_step_size));
+ setreg (s, 0x49, LOBYTE (ff_step_size));
+ setreg (s, 0x4b, 0x15);
+ steps_to_reverse = 0x3f;
+ setreg (s, 0x50, steps_to_reverse);
+ setreg (s, 0x51, 0x15); /* accel profile */
+
+ /* this is to activate the motor */
+ setbits (s, 0x45, (1 << 4));
+
+ lines_to_process = 8 * step_size * 4 / line_end;
+ if (lines_to_process < 1)
+ lines_to_process = 1;
+
+#ifdef DEBUG
+ fprintf (stderr, "lines to process = %d\n", lines_to_process);
+#endif
+
+ setreg (s, 0x58, 0);
+
+ cache_write (s);
+
+ calibrated = 0;
+ first_time = 1;
+ cal_line = malloc (cal_line_size + 1024);
+ average = malloc (sizeof (int) * line_length * 3);
+ memset (average, 0, sizeof (int) * line_length * 3);
+ {
+ int i;
+ for (i = 0; i < 12; i++)
+ {
+ memset (registro[i], 0, 5460 * 3);
+ }
+ }
+
+ /* resets the lm9830 before start scanning */
+ setreg (s, 0x07, 0x08);
+ setreg (s, 0x07, 0x03);
+
+ usleep (100);
+
+ do
+ {
+
+ cal_line_ptr = cal_line;
+
+ status = read_required_bytes (s, cal_line_size, cal_line_ptr);
+ if (status != SANE_STATUS_GOOD)
+ goto done;
+ {
+ int i, j;
+
+ if (calibrated == 0)
+ for (j = 0, i = 0; i < (line_length * 3); i++, j += 2)
+ {
+ average[i] = (cal_line[j] * 256 + cal_line[j + 1]) >> 2;
+ registro[calibrated][i] = average[i];
+ }
+ else
+ for (j = 0, i = 0; i < (line_length * 3); i++, j += 2)
+ {
+ int value;
+ value = (cal_line[j] * 256 + cal_line[j + 1]) >> 2;
+ average[i] += value;
+ average[i] /= 2;
+ registro[calibrated][i] = value;
+ }
+ }
+ calibrated++;
+ }
+ while (calibrated < lines_to_process);
+ lm9830_write_register (s->fd, 0x07, 0x00);
+ usleep (10 * 1000);
+
+#if 0
+ {
+ int i;
+ int j = 0;
+ do
+ {
+ for (i = 3; (i + 6) < (line_length * 3); i += 3)
+ {
+ average[i] =
+ (2 * average[i - 3] + average[i] + 2 * average[i + 3]) / 5;
+ average[i + 1] =
+ (2 * average[i - 2] + average[i + 1] + 2 * average[i + 4]) / 5;
+ average[i + 2] =
+ (2 * average[i - 1] + average[i + 2] + 2 * average[i + 5]) / 5;
+ }
+ j++;
+ }
+ while (j < 3);
+ }
+#endif
+ {
+ int i;
+ int max_red;
+ int min_red;
+ int max_green;
+ int min_green;
+ int max_blue;
+ int min_blue;
+ min_red = max_red = average[0];
+ min_green = max_green = average[1];
+ min_blue = max_blue = average[2];
+ for (i = 3; i < (line_length * 3); i += 3)
+ {
+ int value;
+
+ value = average[i];
+ if (value > max_red)
+ max_red = value;
+ value = average[i + 1];
+ if (value > max_green)
+ max_green = value;
+ value = average[i + 2];
+ if (value > max_blue)
+ max_blue = value;
+ value = average[i];
+ if (value < min_red)
+ min_red = value;
+ value = average[i + 1];
+ if (value < min_green)
+ min_green = value;
+ value = average[i + 2];
+ if (value < min_blue)
+ min_blue = value;
+ }
+#ifdef DEBUG
+ fprintf (stderr, "max_red:%d max_green:%d max_blue:%d\n",
+ max_red, max_green, max_blue);
+ fprintf (stderr, "min_red:%d min_green:%d min_blue:%d\n",
+ min_red, min_green, min_blue);
+#endif
+
+ /* do fine calibration */
+ {
+ int min_white_red;
+ int min_white_green;
+ int min_white_blue;
+ double ratio;
+ int range;
+ double aux;
+ int min_white_err;
+ int j;
+
+ min_white_red = min_white_green = min_white_blue = 0x3ff;
+ for (i = 0; i < (line_length * 3); i += 3)
+ {
+ int value;
+
+ value = average[i] - coarse->min_red;
+ if ((value > 0) && (value < min_white_red))
+ min_white_red = value;
+ value = average[i + 1] - coarse->min_green;
+ if ((value > 0) && (value < min_white_green))
+ min_white_green = value;
+ value = average[i + 2] - coarse->min_blue;
+ if ((value > 0) && (value < min_white_blue))
+ min_white_blue = value;
+ }
+
+ ratio = 0;
+ min_white_err = 0x3ff;
+
+ aux = (double) s->hw_parms.target_value.red / min_white_red;
+ if (aux > ratio)
+ ratio = aux;
+ if (min_white_err > min_white_red)
+ min_white_err = min_white_red;
+ aux = (double) s->hw_parms.target_value.green / min_white_green;
+ if (aux > ratio)
+ ratio = aux;
+ if (min_white_err > min_white_green)
+ min_white_err = min_white_green;
+ aux = (double) s->hw_parms.target_value.blue / min_white_blue;
+ if (aux > ratio)
+ ratio = aux;
+ if (min_white_err > min_white_blue)
+ min_white_err = min_white_blue;
+
+#ifdef DEBUG
+ fprintf (stderr, "min_white_err = %d, ratio = %f\n",
+ min_white_err, ratio);
+#endif
+ if (ratio <= 1.5)
+ range = 2048;
+ else if (ratio <= 2.0)
+ range = 1024;
+ else
+ range = 512;
+
+ corr_red = malloc (sizeof (int) * line_length);
+ corr_green = malloc (sizeof (int) * line_length);
+ corr_blue = malloc (sizeof (int) * line_length);
+
+ for (i = 0, j = 0; i < (line_length * 3); i += 3, j++)
+ {
+ corr_red[j] = compute_corr_code (average[i],
+ coarse->min_red,
+ range,
+ s->hw_parms.target_value.red);
+ corr_green[j] =
+ compute_corr_code (average[i + 1], coarse->min_green,
+ range, s->hw_parms.target_value.green);
+ corr_blue[j] =
+ compute_corr_code (average[i + 2], coarse->min_blue,
+ range, s->hw_parms.target_value.blue);
+ }
+#ifdef DEBUG
+ {
+ FILE *kaka;
+ int i;
+ kaka = fopen ("corr.raw", "w");
+ for (i = 0; i < line_length; i++)
+ {
+ fprintf (kaka, "%d %d %d %d %d %d ",
+ corr_red[i], corr_green[i], corr_blue[i],
+ average[3 * i], average[3 * i + 1], average[3 * i + 2]);
+ fprintf (kaka, "%d %d %d %d %d %d %d %d %d ",
+ registro[0][3 * i], registro[0][3 * i + 1],
+ registro[0][3 * i + 2], registro[1][3 * i],
+ registro[1][3 * i + 1], registro[1][3 * i + 2],
+ registro[2][3 * i], registro[2][3 * i + 1],
+ registro[2][3 * i + 2]);
+ fprintf (kaka, "%d %d %d %d %d %d %d %d %d\n",
+ registro[3][3 * i], registro[3][3 * i + 1],
+ registro[3][3 * i + 2], registro[4][3 * i],
+ registro[4][3 * i + 1], registro[4][3 * i + 2],
+ registro[5][3 * i], registro[5][3 * i + 1],
+ registro[5][3 * i + 2]);
+ }
+ fclose (kaka);
+ }
+#endif
+ {
+ int max_black;
+ int use_six_eight_bits;
+
+ max_black = max (coarse->min_red, coarse->min_green);
+ max_black = max (max_black, coarse->min_blue);
+ use_six_eight_bits = (max_black < 64);
+
+ if (use_six_eight_bits)
+ {
+ setreg (s, 0x3e, (1 << 4) | (1 << 3) | (1024 / range));
+ }
+ else
+ {
+ setreg (s, 0x3e, (1 << 4) | (1 << 3) | (1 << 2) | (1024 / range));
+ }
+ memset (red_gain_offset, 0, sizeof (red_gain_offset));
+ memset (green_gain_offset, 0, sizeof (green_gain_offset));
+ memset (blue_gain_offset, 0, sizeof (blue_gain_offset));
+ for (i = 0, j = (data_pixels_start - active_pixels_start) * 2;
+ i < line_length; i++, j += 2)
+ {
+ if (use_six_eight_bits)
+ {
+ red_gain_offset[j] = (coarse->min_red << 2) |
+ ((corr_red[i] >> 8) & 0x03);
+ red_gain_offset[j + 1] = corr_red[i] & 0xff;
+ green_gain_offset[j] = (coarse->min_green << 2) |
+ ((corr_green[i] >> 8) & 0x03);
+ green_gain_offset[j + 1] = corr_green[i] & 0xff;
+ blue_gain_offset[j] = (coarse->min_blue << 2) |
+ ((corr_blue[i] >> 8) & 0x03);
+ blue_gain_offset[j + 1] = corr_blue[i] & 0xff;
+ }
+ else
+ {
+ red_gain_offset[j] = coarse->min_red;
+ red_gain_offset[j + 1] = corr_red[j] >> 2;
+ green_gain_offset[j] = coarse->min_green;
+ green_gain_offset[j + 1] = corr_green[j] >> 2;
+ blue_gain_offset[j] = coarse->min_blue;
+ blue_gain_offset[j + 1] = corr_blue[j] >> 2;
+ }
+ }
+ write_default_offset_gain (s, red_gain_offset, 5460 * 2, 0);
+ write_default_offset_gain (s, green_gain_offset, 5460 * 2, 1);
+ write_default_offset_gain (s, blue_gain_offset, 5460 * 2, 2);
+ }
+ }
+ }
+
+ status = SANE_STATUS_GOOD;
+
+done:
+ if (corr_red)
+ free (corr_red);
+ if (corr_green)
+ free (corr_green);
+ if (corr_blue)
+ free (corr_blue);
+ if (cal_line)
+ free (cal_line);
+ if (average)
+ free (average);
+
+ return status;
+}
+
+static void
+ciclic_buffer_init_offset_correction (ciclic_buffer_t * cb, int vres)
+{
+ cb->blue_idx = 0;
+ switch (vres)
+ {
+ case 600:
+ cb->green_idx = 4;
+ cb->red_idx = 8;
+ cb->first_good_line = 8;
+ break;
+ case 400:
+ cb->green_idx = 3;
+ cb->red_idx = 6;
+ cb->first_good_line = 6;
+ break;
+ case 300:
+ cb->green_idx = 2;
+ cb->red_idx = 4;
+ cb->first_good_line = 4;
+ break;
+ case 200:
+ cb->blue_idx = 0;
+ cb->green_idx = 1;
+ cb->red_idx = 2;
+ cb->first_good_line = 4;
+ break;
+ case 150:
+ cb->green_idx = 1;
+ cb->red_idx = 2;
+ cb->first_good_line = 2;
+ break;
+ case 75:
+ cb->green_idx = 1;
+ cb->red_idx = 2;
+ cb->first_good_line = 2;
+ break;
+ default:
+ cb->green_idx = 0;
+ cb->red_idx = 0;
+ cb->first_good_line = 0;
+ break;
+ }
+
+ cb->buffer_position = cb->buffer_ptrs[cb->first_good_line];
+}
+
+static SANE_Status
+ciclic_buffer_init (ciclic_buffer_t * cb, SANE_Int bytes_per_line,
+ int vres, int status_bytes)
+{
+ cb->good_bytes = 0;
+ cb->num_lines = 12;
+ cb->size = bytes_per_line * cb->num_lines;
+ cb->can_consume = cb->size + cb->num_lines * status_bytes;
+
+ cb->buffer = malloc (cb->size);
+ if (!cb->buffer)
+ return SANE_STATUS_NO_MEM;
+
+ {
+ int i;
+ unsigned char *buffer;
+ unsigned char **ptrs;
+
+ ptrs = cb->buffer_ptrs = (unsigned char **)
+ malloc (sizeof (unsigned char *) * cb->num_lines);
+ if (!cb->buffer_ptrs)
+ return SANE_STATUS_NO_MEM;
+
+ buffer = cb->buffer;
+ for (i = 0; i < cb->num_lines; i++)
+ {
+ ptrs[i] = buffer;
+ buffer += bytes_per_line;
+ }
+ }
+
+ cb->current_line = 0;
+ cb->pixel_position = 0;
+ ciclic_buffer_init_offset_correction (cb, vres);
+
+ return SANE_STATUS_GOOD;
+}
+
+static int
+prepare_for_a_scan (HP4200_Scanner * s)
+{
+ /* local scanning params */
+ int active_pixels_start;
+ int line_end;
+ int data_pixels_start;
+ int data_pixels_end;
+ int ff_step_size;
+ int dpd;
+ int step_size;
+ char steps_to_reverse;
+ char hdpi_div;
+ char line_rate_color;
+ int hdpi_code;
+ unsigned char pixel_packing;
+ unsigned char data_mode;
+ unsigned char AFE_operation;
+ int pause_limit;
+ int n = 0, m = 0;
+
+ setreg (s, 0x07, 0x00);
+ usleep (10 * 1000);
+
+ hdpi_code = compute_hdpi_code (s->user_parms.horizontal_resolution);
+ /* figure out which horizontal divider to use based on the
+ calculated horizontal dpi code */
+ hdpi_div = hdpi_mapping[hdpi_code];
+
+ /* image_width is set to the correct number of pixels by calling
+ fxn. This might be the reason we can't do high res full width
+ scans though...not sure. */
+ /*s->user_parms.image_width /= 4; */
+ active_pixels_start = 0x40;
+ line_end = 0x2ee0; /* 2ee0 */
+ s->mclk_div = 2;
+ data_pixels_start = 0x72 + s->runtime_parms.first_pixel * hdpi_div;
+ data_pixels_end =
+ (int) (data_pixels_start + s->user_parms.image_width * hdpi_div);
+ data_pixels_end = min (data_pixels_end, line_end - 20);
+ setreg (s, 0x1e, HIBYTE (active_pixels_start));
+ setreg (s, 0x1f, LOBYTE (active_pixels_start));
+ setreg (s, 0x20, HIBYTE (line_end));
+ setreg (s, 0x21, LOBYTE (line_end));
+ setreg (s, 0x22, HIBYTE (data_pixels_start));
+ setreg (s, 0x23, LOBYTE (data_pixels_start));
+ setreg (s, 0x24, HIBYTE (data_pixels_end));
+ setreg (s, 0x25, LOBYTE (data_pixels_end));
+
+ AFE_operation = PIXEL_RATE_3_CHANNELS;
+ setreg (s, 0x26,
+ AFE_operation |
+ GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0));
+
+ setreg (s, 0x08, (s->mclk_div - 1) * 2);
+ pixel_packing = 3;
+ data_mode = 0;
+ setreg (s, 0x09, hdpi_code | PIXEL_PACKING (pixel_packing) |
+ DATAMODE (data_mode));
+ setreg (s, 0x0a, 0); /* reserved and strange register */
+
+ setreg (s, 0x5c, 0x00);
+ setreg (s, 0x5d, 0x00);
+ setreg (s, 0x5e, 0x00);
+
+ if (s->user_parms.vertical_resolution == 1200)
+ {
+ /* 1 out of 2 */
+ n = 1;
+ m = 2;
+ }
+ setreg (s, 0x44, (256 - n) & 0xff);
+ setreg (s, 0x5a, m);
+ s->runtime_parms.status_bytes = (m == 0) ? 1 : m;
+ if (data_mode == 1)
+ s->runtime_parms.status_bytes *= 2;
+
+ s->runtime_parms.scanner_line_size =
+ compute_bytes_per_line (data_pixels_end - data_pixels_start,
+ hdpi_code, pixel_packing, data_mode,
+ AFE_operation, m);
+ pause_limit = compute_pause_limit (&(s->hw_parms),
+ s->runtime_parms.scanner_line_size);
+
+#ifdef DEBUG
+ fprintf (stderr, "scanner_line_size = %d\npause_limit = %d\n",
+ s->runtime_parms.scanner_line_size, pause_limit);
+#endif
+
+ setreg (s, 0x4e, pause_limit); /* max Kb to pause */
+ setreg (s, 0x4f, 0x02); /* min Kb to resume */
+
+ line_rate_color = 1;
+ step_size =
+ (s->user_parms.vertical_resolution * line_end * line_rate_color) /
+ (4 * s->hw_parms.motor_full_steps_per_inch);
+
+ if (s->val[OPT_BACKTRACK].b)
+ {
+ steps_to_reverse = 0x3f;
+ setreg (s, 0x50, steps_to_reverse);
+ setreg (s, 0x51, 0x15); /* accel profile */
+ }
+ else
+ {
+ s->hw_parms.steps_to_reverse = 0;
+ setreg (s, 0x50, s->hw_parms.steps_to_reverse);
+ setreg (s, 0x51, 0); /* accel profile */
+ s->hw_parms.fsteps_25_speed = 0;
+ s->hw_parms.fsteps_50_speed = 0;
+ }
+
+ dpd = compute_dpd (s, step_size, line_end); /* 0x0ada; */
+#ifdef DEBUG
+ fprintf (stderr, "dpd = %d\n", dpd);
+#endif
+ setreg (s, 0x52, HIBYTE (dpd));
+ setreg (s, 0x53, LOBYTE (dpd));
+
+ setreg (s, 0x46, HIBYTE (step_size));
+ setreg (s, 0x47, LOBYTE (step_size));
+
+ ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency,
+ s->mclk_div,
+ s->hw_parms.motor_max_speed,
+ s->hw_parms.
+ motor_full_steps_per_inch, 0);
+ setreg (s, 0x48, HIBYTE (ff_step_size));
+ setreg (s, 0x49, LOBYTE (ff_step_size));
+ setreg (s, 0x4b, 0x15);
+ /* this is to stay the motor running */
+ setbits (s, 0x45, (1 << 4));
+
+ setreg (s, 0x4a, HIBYTE (47 + s->runtime_parms.steps_to_skip));
+ setreg (s, 0x4b, LOBYTE (47 + s->runtime_parms.steps_to_skip));
+
+ setreg (s, 0x58, 0);
+
+ ciclic_buffer_init (&(s->ciclic_buffer),
+ s->runtime_parms.image_line_size,
+ s->user_parms.vertical_resolution,
+ s->runtime_parms.status_bytes);
+
+ s->runtime_parms.num_bytes_left_to_scan =
+ s->user_parms.lines_to_scan * s->runtime_parms.image_line_size;
+
+#ifdef DEBUG
+ fprintf (stderr, "bytes to scan = %ld\n",
+ s->runtime_parms.num_bytes_left_to_scan);
+#endif
+
+ cache_write (s);
+
+#ifdef DEBUG
+ lm9830_dump_registers (s->fd);
+#endif
+
+ lm9830_reset (s->fd);
+
+ setreg (s, 0x07, 0x03);
+ usleep (100);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+end_scan (HP4200_Scanner * s)
+{
+ s->scanning = SANE_FALSE;
+ setreg (s, 0x07, 0x00);
+ lm9830_reset (s->fd);
+ setbits (s, 0x58, PAPER_SENSOR_2_STOP_SCAN);
+ cache_write (s);
+ setreg (s, 0x07, 0x02);
+
+ /* Free some buffers */
+ if (s->ciclic_buffer.buffer)
+ {
+ free (s->ciclic_buffer.buffer);
+ s->ciclic_buffer.buffer = NULL;
+ }
+ if (s->ciclic_buffer.buffer_ptrs)
+ {
+ free (s->ciclic_buffer.buffer_ptrs);
+ s->ciclic_buffer.buffer_ptrs = NULL;
+ }
+ if (s->scanner_buffer.buffer)
+ {
+ free (s->scanner_buffer.buffer);
+ s->scanner_buffer.buffer = NULL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static int
+hp4200_init_scanner (HP4200_Scanner * s)
+{
+ int ff_step_size;
+ int mclk_div;
+
+ lm9830_ini_scanner (s->fd, NULL);
+ hp4200_init_registers (s);
+ scanner_buffer_init (&(s->scanner_buffer), s->hw_parms.SRAM_size);
+ setreg (s, 0x07, 0x08);
+ usleep (10 * 1000);
+ setreg (s, 0x07, 0x00);
+ usleep (10 * 1000);
+ mclk_div = 2;
+
+ setreg (s, 0x08, (mclk_div - 1) * 2);
+ ff_step_size =
+ compute_fastfeed_step_size (s->hw_parms.crystal_frequency,
+ mclk_div,
+ s->hw_parms.motor_max_speed,
+ s->hw_parms.motor_full_steps_per_inch, 0);
+ setreg (s, 0x48, HIBYTE (ff_step_size));
+ setreg (s, 0x49, LOBYTE (ff_step_size));
+ setbits (s, 0x45, (1 << 4));
+ cache_write (s);
+ return 0;
+}
+
+static void
+ciclic_buffer_copy (ciclic_buffer_t * cb, SANE_Byte * buf,
+ SANE_Int num_bytes, int image_line_size, int status_bytes)
+{
+ int biggest_upper_block_size;
+ int upper_block_size;
+ int lower_block_size;
+ int bytes_to_be_a_entire_line;
+
+ /* copy the upper block */
+ biggest_upper_block_size = cb->size - (cb->buffer_position - cb->buffer);
+ upper_block_size = min (biggest_upper_block_size, num_bytes);
+ memcpy (buf, cb->buffer_position, upper_block_size);
+ cb->good_bytes -= upper_block_size;
+
+ bytes_to_be_a_entire_line = (cb->buffer_position - cb->buffer) %
+ image_line_size;
+ cb->can_consume += upper_block_size +
+ status_bytes * (((bytes_to_be_a_entire_line + upper_block_size) /
+ image_line_size) - 1);
+
+ if (num_bytes < biggest_upper_block_size)
+ {
+ cb->buffer_position += num_bytes;
+ return;
+ }
+
+ /* copy the lower block */
+ lower_block_size = num_bytes - biggest_upper_block_size;
+ if (lower_block_size > 0)
+ {
+ memcpy (buf + biggest_upper_block_size, cb->buffer, lower_block_size);
+ cb->good_bytes -= lower_block_size;
+ cb->can_consume += lower_block_size + status_bytes *
+ (lower_block_size / image_line_size);
+ cb->buffer_position = cb->buffer + lower_block_size;
+ }
+ else
+ {
+ cb->buffer_position = cb->buffer;
+ }
+ assert (cb->good_bytes >= 0);
+ assert (lower_block_size >= 0);
+}
+
+static void
+ciclic_buffer_consume (ciclic_buffer_t * cb,
+ scanner_buffer_t * scanner_buffer,
+ int image_width, int status_bytes)
+{
+ int to_consume;
+ int to_consume_now;
+ int i;
+ int processed;
+
+ to_consume = min (cb->can_consume, scanner_buffer->num_bytes);
+
+ while (to_consume)
+ {
+
+ if (cb->pixel_position == image_width)
+ {
+ if (scanner_buffer->num_bytes >= status_bytes)
+ {
+ /* forget status bytes */
+ scanner_buffer->data_ptr += status_bytes;
+ scanner_buffer->num_bytes -= status_bytes;
+ cb->can_consume -= status_bytes;
+ to_consume -= status_bytes;
+
+ cb->pixel_position = 0; /* back to the start pixel */
+
+ cb->red_idx = (cb->red_idx + 1) % cb->num_lines;
+ cb->green_idx = (cb->green_idx + 1) % cb->num_lines;
+ cb->blue_idx = (cb->blue_idx + 1) % cb->num_lines;
+ cb->current_line++;
+ }
+ else
+ break;
+ }
+
+ to_consume_now = min ((image_width - cb->pixel_position) * 3,
+ to_consume);
+
+ if (to_consume_now < 3)
+ break;
+
+ for (i = cb->pixel_position * 3; to_consume_now >= 3;
+ i += 3, to_consume_now -= 3)
+ {
+ cb->buffer_ptrs[cb->red_idx][i] = scanner_buffer->data_ptr[0];
+ cb->buffer_ptrs[cb->green_idx][i + 1] = scanner_buffer->data_ptr[1];
+ cb->buffer_ptrs[cb->blue_idx][i + 2] = scanner_buffer->data_ptr[2];
+ scanner_buffer->data_ptr += 3;
+ }
+ processed = i - (cb->pixel_position * 3);
+ cb->pixel_position = i / 3;
+ to_consume -= processed;
+ cb->can_consume -= processed;
+ scanner_buffer->num_bytes -= processed;
+ if (cb->current_line > cb->first_good_line)
+ cb->good_bytes += processed;
+ }
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ SANE_Status status;
+ int to_copy_now;
+ int bytes_to_copy_to_frontend;
+ HP4200_Scanner *s = h;
+
+ static char me[] = "sane_read";
+ DBG (DBG_proc, "%s\n", me);
+
+ if (!(s->scanning))
+ {
+ /* OOPS, not scanning */
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (!buf || !len)
+ return SANE_STATUS_INVAL;
+
+ *len = 0;
+
+ if (s->runtime_parms.num_bytes_left_to_scan == 0)
+ {
+ end_scan (s);
+ return SANE_STATUS_EOF;
+ }
+
+ bytes_to_copy_to_frontend = min (s->runtime_parms.num_bytes_left_to_scan,
+ maxlen);
+
+ /* first copy available data from the ciclic buffer */
+ to_copy_now = min (s->ciclic_buffer.good_bytes, bytes_to_copy_to_frontend);
+
+ if (to_copy_now > 0)
+ {
+ ciclic_buffer_copy (&(s->ciclic_buffer), buf, to_copy_now,
+ s->runtime_parms.image_line_size,
+ s->runtime_parms.status_bytes);
+ buf += to_copy_now;
+ bytes_to_copy_to_frontend -= to_copy_now;
+ *len += to_copy_now;
+ }
+
+ /* if not enough bytes, get data from the scanner */
+ while (bytes_to_copy_to_frontend)
+ {
+ if (s->scanner_buffer.num_bytes < 3)
+ { /* cicl buf consumes modulo 3
+ bytes at least now for rgb
+ color 8 bpp fixme: but this
+ is ugly and not generic
+ */
+ status = scanner_buffer_read (s);
+
+ if (status == SANE_STATUS_CANCELLED)
+ {
+ end_scan (s);
+ s->aborted_by_user = SANE_FALSE;
+ return status;
+ }
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ while ((s->scanner_buffer.num_bytes > 3) && bytes_to_copy_to_frontend)
+ {
+ ciclic_buffer_consume (&(s->ciclic_buffer), &(s->scanner_buffer),
+ s->user_parms.image_width,
+ s->runtime_parms.status_bytes);
+ to_copy_now = min (s->ciclic_buffer.good_bytes,
+ bytes_to_copy_to_frontend);
+
+ if (to_copy_now > 0)
+ {
+ ciclic_buffer_copy (&(s->ciclic_buffer), buf, to_copy_now,
+ s->runtime_parms.image_line_size,
+ s->runtime_parms.status_bytes);
+ buf += to_copy_now;
+ bytes_to_copy_to_frontend -= to_copy_now;
+ *len += to_copy_now;
+ }
+ }
+ }
+
+ s->runtime_parms.num_bytes_left_to_scan -= *len;
+
+ if (s->runtime_parms.num_bytes_left_to_scan < 0)
+ *len += s->runtime_parms.num_bytes_left_to_scan;
+
+ return SANE_STATUS_GOOD;
+}
+
+static HP4200_Device *
+find_device (SANE_String_Const name)
+{
+ static char me[] = "find_device";
+ HP4200_Device *dev;
+
+ DBG (DBG_proc, "%s\n", me);
+
+ for (dev = first_device; dev; dev = dev->next)
+ {
+ if (strcmp (dev->dev.name, name) == 0)
+ {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+static SANE_Status
+add_device (SANE_String_Const name, HP4200_Device ** argpd)
+{
+ int fd;
+ HP4200_Device *pd;
+ static const char me[] = "add_device";
+ SANE_Status status;
+
+ DBG (DBG_proc, "%s(%s)\n", me, name);
+
+ /* Avoid adding the same device more than once */
+ if ((pd = find_device (name)))
+ {
+ if (argpd)
+ *argpd = pd;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* open the device file, but read only or read/write to perform
+ ioctl's ? */
+ if ((status = sanei_usb_open (name, &fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: open(%s) failed: %s\n", me, name,
+ sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ /* put here some code to probe that the device attached to the
+ device file is a supported scanner. Maybe some ioctl */
+ sanei_usb_close (fd);
+
+ pd = (HP4200_Device *) calloc (1, sizeof (HP4200_Device));
+ if (!pd)
+ {
+ DBG (DBG_error, "%s: out of memory allocating device.\n", me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ pd->dev.name = strdup (name);
+ pd->dev.vendor = "Hewlett-Packard";
+ pd->dev.model = "HP-4200";
+ pd->dev.type = "flatbed scanner";
+
+ if (!pd->dev.name || !pd->dev.vendor || !pd->dev.model || !pd->dev.type)
+ {
+ DBG (DBG_error,
+ "%s: out of memory allocating device descriptor strings.\n", me);
+ free (pd);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ pd->handle = NULL;
+ pd->next = first_device;
+ first_device = pd;
+ n_devices++;
+ if (argpd)
+ *argpd = pd;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (SANE_String_Const name)
+{
+ static char me[] = "attach";
+ DBG (DBG_proc, "%s\n", me);
+ return add_device (name, NULL);
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ static const char me[] = "sane_hp4200_init";
+ char dev_name[PATH_MAX];
+ FILE *fp;
+
+ authorize = authorize; /* keep gcc quiet */
+
+ DBG_INIT ();
+
+ DBG (DBG_proc, "%s\n", me);
+ DBG (DBG_error, "SANE hp4200 backend version %d.%d build %d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+ /* put some version_code checks here */
+
+ if (NULL != version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+ }
+
+ sanei_usb_init ();
+ sanei_pv8630_init ();
+
+ fp = sanei_config_open (HP4200_CONFIG_FILE);
+ if (!fp)
+ {
+ DBG (DBG_error, "%s: configuration file not found!\n", me);
+
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+
+ if (strlen (dev_name) == 0)
+ continue; /* ignore empty lines */
+
+ DBG (DBG_info, "%s: looking for devices matching %s\n",
+ me, dev_name);
+
+ sanei_usb_attach_matching_devices (dev_name, attach);
+ }
+
+ fclose (fp);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ HP4200_Device *device, *next;
+
+ DBG (DBG_proc, "sane_hp4200_exit\n");
+
+ for (device = first_device; device; device = next)
+ {
+ next = device->next;
+ if (device->handle)
+ {
+ sane_close (device->handle);
+ }
+ if (device->dev.name)
+ {
+ free (device->dev.name);
+ }
+ free (device);
+ }
+ first_device = NULL;
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ n_devices = 0;
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ int i;
+ HP4200_Device *pdev;
+
+ DBG (DBG_proc, "sane_get_devices (%p, %d)\n", (void *) device_list,
+ local_only);
+
+ /* Waste the last list returned from this function */
+ if (devlist)
+ free (devlist);
+
+ devlist = (const SANE_Device **)
+ malloc ((n_devices + 1) * sizeof (SANE_Device *));
+
+ if (!devlist)
+ {
+ DBG (DBG_error, "sane_get_devices: out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0, pdev = first_device; pdev; i++, pdev = pdev->next)
+ {
+ devlist[i] = &(pdev->dev);
+ }
+ devlist[i] = NULL;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+init_options (HP4200_Scanner * s)
+{
+ s->opt[OPT_NUM_OPTS].name = "";
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NUM_OPTS].size = sizeof (SANE_Word);
+ s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ s->opt[OPT_RES].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RES].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RES].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_RES].type = SANE_TYPE_INT;
+ s->opt[OPT_RES].size = sizeof (SANE_Word);
+ s->opt[OPT_RES].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RES].constraint.word_list = dpi_list;
+ s->val[OPT_RES].w = 150;
+
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].size = sizeof (SANE_Fixed);
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+ s->val[OPT_TL_X].w = x_range.min;
+
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].size = sizeof (SANE_Fixed);
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = y_range.min;
+
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].size = sizeof (SANE_Fixed);
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X].w = x_range.max;
+
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].size = sizeof (SANE_Fixed);
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y].w = y_range.max;
+
+ s->opt[OPT_BACKTRACK].name = SANE_NAME_BACKTRACK;
+ s->opt[OPT_BACKTRACK].title = SANE_TITLE_BACKTRACK;
+ s->opt[OPT_BACKTRACK].desc = SANE_DESC_BACKTRACK;
+ s->opt[OPT_BACKTRACK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BACKTRACK].type = SANE_TYPE_BOOL;
+ s->opt[OPT_BACKTRACK].size = sizeof (SANE_Bool);
+ s->opt[OPT_BACKTRACK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BACKTRACK].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_BACKTRACK].b = SANE_TRUE;
+
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 1024 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = s->user_parms.gamma[0];
+
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 1024 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = s->user_parms.gamma[1];
+
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 1024 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = s->user_parms.gamma[2];
+
+ {
+ int i;
+ double gamma = 2.0;
+ for (i = 0; i < 1024; i++)
+ {
+ s->user_parms.gamma[0][i] =
+ 255 * pow (((double) i + 1) / 1024, 1.0 / gamma);
+ s->user_parms.gamma[1][i] = s->user_parms.gamma[0][i];
+ s->user_parms.gamma[2][i] = s->user_parms.gamma[0][i];
+#ifdef DEBUG
+ printf ("%d %d\n", i, s->user_parms.gamma[0][i]);
+#endif
+ }
+ }
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].size = sizeof (SANE_Word);
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ static const char me[] = "sane_hp4200_open";
+ SANE_Status status;
+ HP4200_Device *dev;
+ HP4200_Scanner *s;
+
+ DBG (DBG_proc, "%s (%s, %p)\n", me, name, (void *) h);
+
+ if (name && name[0])
+ {
+ dev = find_device (name);
+ if (!dev)
+ {
+ status = add_device (name, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ {
+ dev = first_device;
+ }
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (!h)
+ return SANE_STATUS_INVAL;
+
+ s = *h = (HP4200_Scanner *) calloc (1, sizeof (HP4200_Scanner));
+ if (!s)
+ {
+ DBG (DBG_error, "%s: out of memory creating scanner structure.\n", me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->handle = s;
+ s->aborted_by_user = SANE_FALSE;
+ s->ciclic_buffer.buffer = NULL;
+ s->scanner_buffer.buffer = NULL;
+ s->dev = dev;
+ s->user_parms.image_width = 0;
+ s->user_parms.lines_to_scan = 0;
+ s->user_parms.vertical_resolution = 0;
+ s->scanning = SANE_FALSE;
+ s->fd = -1;
+
+ init_options (s);
+
+ if ((sanei_usb_open (dev->dev.name, &s->fd) != SANE_STATUS_GOOD))
+ {
+ DBG (DBG_error, "%s: Can't open %s.\n", me, dev->dev.name);
+ return SANE_STATUS_IO_ERROR; /* fixme: return busy when file is
+ being accessed already */
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ HP4200_Scanner *s = (HP4200_Scanner *) h;
+ DBG (DBG_proc, "sane_hp4200_close (%p)\n", (void *) h);
+
+ if (s)
+ {
+ s->dev->handle = NULL;
+ if (s->fd != -1)
+ {
+ sanei_usb_close (s->fd);
+ }
+ free (s);
+ }
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ static char me[] = "sane_get_option_descriptor";
+ HP4200_Scanner *s = (HP4200_Scanner *) h;
+
+ DBG (DBG_proc, "%s\n", me);
+
+ if ((n < 0) || (n >= NUM_OPTIONS))
+ return NULL;
+
+ return s->opt + n;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ HP4200_Scanner *s = (HP4200_Scanner *) handle;
+ SANE_Status status;
+ SANE_Int myinfo = 0;
+ SANE_Word cap;
+
+ DBG (DBG_proc, "sane_control_option\n");
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ case OPT_NUM_OPTS:
+ case OPT_RES:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+
+ case OPT_BACKTRACK:
+ *(SANE_Bool *) val = s->val[option].b;
+ break;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+
+ /* Numeric side-effect free options */
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Numeric side-effect options */
+ case OPT_RES:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+
+ case OPT_BACKTRACK:
+ s->val[option].b = *(SANE_Bool *) val;
+ break;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (info)
+ *info = myinfo;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+compute_parameters (HP4200_Scanner * s)
+{
+ int resolution;
+ int opt_tl_x;
+ int opt_br_x;
+ int opt_tl_y;
+ int opt_br_y;
+
+ if (s->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ resolution = 50;
+ opt_tl_x = SANE_UNFIX (x_range.min);
+ opt_tl_y = SANE_UNFIX (y_range.min);
+ opt_br_x = SANE_UNFIX (x_range.max);
+ opt_br_y = SANE_UNFIX (y_range.max);
+ }
+ else
+ {
+ resolution = s->val[OPT_RES].w;
+ opt_tl_x = SANE_UNFIX (s->val[OPT_TL_X].w);
+ opt_tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w);
+ opt_br_x = SANE_UNFIX (s->val[OPT_BR_X].w);
+ opt_br_y = SANE_UNFIX (s->val[OPT_BR_Y].w);
+ }
+
+ s->user_parms.horizontal_resolution = resolution;
+ s->user_parms.vertical_resolution = resolution;
+
+ s->runtime_parms.steps_to_skip = floor (300.0 / MM_PER_INCH * opt_tl_y);
+ s->user_parms.lines_to_scan =
+ floor ((opt_br_y - opt_tl_y) / MM_PER_INCH * resolution);
+ s->user_parms.image_width =
+ floor ((opt_br_x - opt_tl_x) / MM_PER_INCH * resolution);
+ s->runtime_parms.first_pixel = floor (opt_tl_x / MM_PER_INCH * resolution);
+
+ /* fixme: add support for more depth's and bpp's. */
+ s->runtime_parms.image_line_size = s->user_parms.image_width * 3;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ static char me[] = "sane_get_parameters";
+ HP4200_Scanner *s = (HP4200_Scanner *) h;
+
+ DBG (DBG_proc, "%s\n", me);
+ if (!p)
+ return SANE_STATUS_INVAL;
+
+ p->format = SANE_FRAME_RGB;
+ p->last_frame = SANE_TRUE;
+ p->depth = 8;
+ if (!s->scanning)
+ {
+ compute_parameters (s);
+ }
+
+ p->lines = s->user_parms.lines_to_scan;
+ p->pixels_per_line = s->user_parms.image_width;
+ p->bytes_per_line = s->runtime_parms.image_line_size;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ HP4200_Scanner *s = (HP4200_Scanner *) h;
+ struct coarse_t coarse;
+
+ static char me[] = "sane_start";
+ DBG (DBG_proc, "%s\n", me);
+
+ s->scanning = SANE_TRUE;
+ s->aborted_by_user = SANE_FALSE;
+ s->user_parms.color = SANE_TRUE;
+
+ compute_parameters (s);
+
+ hp4200_init_scanner (s);
+ hp4200_goto_home (s);
+ hp4200_wait_homed (s);
+ /* restore default register values here... */
+ write_gamma (s);
+ hp4200_init_registers (s);
+ lm9830_ini_scanner (s->fd, NULL);
+ /* um... do not call cache_write() here, don't know why :( */
+ do_coarse_calibration (s, &coarse);
+ do_fine_calibration (s, &coarse);
+ prepare_for_a_scan (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ static char me[] = "sane_cancel";
+ HP4200_Scanner *s = (HP4200_Scanner *) h;
+ DBG (DBG_proc, "%s\n", me);
+
+ s->aborted_by_user = SANE_TRUE;
+
+ end_scan (s);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ HP4200_Scanner *dev = handle;
+ SANE_Status status;
+
+ non_blocking = non_blocking; /* silence gcc */
+
+ if (dev->scanning == SANE_FALSE)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (non_blocking == SANE_FALSE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ static char me[] = "sane_get_select_fd";
+
+ h = h; /* keep gcc quiet */
+ fd = fd; /* keep gcc quiet */
+
+ DBG (DBG_proc, "%s\n", me);
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/hp4200.conf.in b/backend/hp4200.conf.in
new file mode 100644
index 0000000..e3e13b9
--- /dev/null
+++ b/backend/hp4200.conf.in
@@ -0,0 +1,6 @@
+#
+# Configuration file for the hp4200 backend
+#
+
+# HP4200
+usb 0x03f0 0x0105
diff --git a/backend/hp4200.h b/backend/hp4200.h
new file mode 100644
index 0000000..13c0d7f
--- /dev/null
+++ b/backend/hp4200.h
@@ -0,0 +1,266 @@
+/*
+ Copyright (C) 2000 by Adrian Perez Jorge
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _HP4200_H
+#define _HP4200_H
+
+#include <sys/types.h>
+
+#define MCLKDIV_SCALING 2
+#define NUM_REGISTERS 0x80
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+
+/*--------------------------------------------------------------------------*/
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+enum HP4200_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_RES,
+
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_BACKTRACK,
+
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_PREVIEW,
+
+ NUM_OPTIONS /* must come last */
+};
+
+/* already declared in the sane includes...
+typedef union
+{
+ SANE_Word w;
+ SANE_Bool b;
+ SANE_Fixed f;
+ SANE_Word *wa;
+}
+Option_Value;
+*/
+
+enum ScannerModels
+{
+ HP4200
+};
+
+typedef struct HP4200_Device
+{
+ struct HP4200_Device *next;
+ SANE_Device dev;
+ SANE_Handle handle;
+}
+HP4200_Device;
+
+struct _scanner_buffer_t
+{
+ unsigned char *buffer; /* buffer memory space */
+ int size; /* size of the buffer */
+ int num_bytes; /* number of bytes left (to read) */
+ unsigned char *data_ptr; /* cursor in buffer */
+};
+
+typedef struct _scanner_buffer_t scanner_buffer_t;
+
+struct _ciclic_buffer_t
+{
+ int good_bytes; /* number of valid bytes of the image */
+ int num_lines; /* number of lines of the ciclic buffer */
+ int size; /* size in bytes of the buffer space */
+ unsigned char *buffer; /* pointer to the buffer space */
+ unsigned char **buffer_ptrs; /* pointers to the beginning of each
+ line in the buffer space */
+ int can_consume; /* num of bytes the ciclic buf can consume */
+ int current_line; /* current scanned line */
+ int first_good_line; /* number of lines to fill the ``pipeline'' */
+ unsigned char *buffer_position; /* pointer to the first byte that
+ can be copied */
+ int pixel_position; /* pixel position in current line */
+
+ /* color indexes for the proper line in the ciclic buffer */
+ int red_idx;
+ int green_idx;
+ int blue_idx;
+};
+
+typedef struct _ciclic_buffer_t ciclic_buffer_t;
+
+struct _hardware_parameters_t
+{
+ unsigned int SRAM_size;
+ unsigned char SRAM_bandwidth;
+ unsigned long crystal_frequency;
+ unsigned int min_pixel_data_buffer_limit;
+ unsigned int motor_full_steps_per_inch;
+ float motor_max_speed;
+ unsigned int scan_bar_max_speed;
+ unsigned int start_of_scanning_area;
+ unsigned int calibration_strip_height;
+ unsigned int scan_area_width;
+ double scan_area_length; /* in inches */
+ unsigned int sensor_num_pixels;
+ unsigned int sensor_pixel_start;
+ unsigned int sensor_pixel_end;
+ int sensor_cds_state; /* 0 == off, 1 == on */
+ int sensor_signal_polarity; /* 0 == ??, 1 == ?? */
+ int sensor_max_integration_time;
+ int sensor_line_separation;
+ int sensor_type;
+ unsigned int sensor_resolution;
+ int sensor_control_signals_polarity;
+ int sensor_control_signals_state;
+ int sensor_control_pixel_rate_timing;
+ int sensor_control_line_rate_timing;
+ unsigned int sensor_black_clamp_timing; /* ??? */
+ unsigned int sensor_CIS_timing;
+ int sensor_toshiba_timing;
+ int sensor_color_modes; /* bitmask telling color modes supported */
+ int illumination_mode;
+ int motor_control_mode;
+ int motor_paper_sense_mode;
+ int motor_pause_reverse_mode;
+ int misc_io_mode;
+ int num_tr_pulses;
+ int guard_band_duration;
+ int pulse_duration;
+ int fsteps_25_speed;
+ int fsteps_50_speed;
+ int steps_to_reverse;
+ struct
+ {
+ int red;
+ int green;
+ int blue;
+ }
+ target_value;
+ unsigned short home_sensor;
+};
+
+typedef struct _hardware_parameters_t hardware_parameters_t;
+
+struct _user_parameters_t
+{
+ unsigned int image_width;
+ unsigned int lines_to_scan;
+ unsigned int horizontal_resolution;
+ unsigned int vertical_resolution;
+ int hres_reduction_method; /* interpolation/??? */
+ int vres_reduction_method;
+ SANE_Bool color; /* color/grayscale */
+ int bpp;
+ int scan_mode; /* preview/full scan */
+ SANE_Bool no_reverse;
+ SANE_Word gamma[3][1024]; /* gamma table for rgb */
+};
+
+typedef struct _user_parameters_t user_parameters_t;
+
+struct _measured_parameters_t
+{
+ unsigned int datalink_bandwidth;
+ struct
+ {
+ int red;
+ int green;
+ int blue;
+ }
+ coarse_calibration_data;
+ struct
+ {
+ int *pRedOffset;
+ int *pGreenOffset;
+ int *pBlueOffset;
+ int *pRedGain;
+ int *pGreenGain;
+ int *pBlueGain;
+ }
+ fine_calibration_data;
+ int max_integration_time;
+ int color_mode;
+};
+
+typedef struct _measured_parameters_t measured_parameters_t;
+
+struct _runtime_parameters_t
+{
+ long num_bytes_left_to_scan;
+ int status_bytes; /* number of status bytes per line */
+ int image_line_size; /* line size in bytes without status bytes */
+ int scanner_line_size; /* line size in bytes including the
+ status bytes */
+ int first_pixel; /* first pixel in the line to be scanned */
+ int steps_to_skip;
+};
+
+typedef struct _runtime_parameters_t runtime_parameters_t;
+
+struct _HP4200_Scanner
+{
+ struct _HP4200_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Bool scanning;
+ SANE_Bool aborted_by_user;
+
+ SANE_Parameters params;
+
+ HP4200_Device *dev;
+ hardware_parameters_t hw_parms;
+ user_parameters_t user_parms;
+ measured_parameters_t msrd_parms;
+ unsigned int regs[NUM_REGISTERS];
+ int mclk;
+ float mclk_div;
+
+ int fd;
+
+ ciclic_buffer_t ciclic_buffer;
+ scanner_buffer_t scanner_buffer;
+ runtime_parameters_t runtime_parms;
+};
+
+typedef struct _HP4200_Scanner HP4200_Scanner;
+
+#endif /* !_HP4200_H */
diff --git a/backend/hp4200_lm9830.c b/backend/hp4200_lm9830.c
new file mode 100644
index 0000000..f2d9475
--- /dev/null
+++ b/backend/hp4200_lm9830.c
@@ -0,0 +1,219 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Adrian Perez Jorge
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a backend for the HP4200C flatbed scanner
+*/
+
+
+#include "hp4200_lm9830.h"
+
+static SANE_Status
+lm9830_read_register (int fd, unsigned char reg, unsigned char *data)
+{
+ SANE_Status retval;
+
+ if (!data)
+ return -SANE_STATUS_INVAL;
+ retval = sanei_pv8630_write_byte (fd, PV8630_REPPADDRESS, reg);
+ if (retval != SANE_STATUS_GOOD)
+ return retval;
+ return sanei_pv8630_read_byte (fd, PV8630_RDATA, data);
+}
+
+static SANE_Status
+lm9830_write_register (int fd, unsigned char reg, unsigned char value)
+{
+ SANE_Status retval;
+
+ retval = sanei_pv8630_write_byte (fd, PV8630_REPPADDRESS, reg);
+ if (retval != SANE_STATUS_GOOD)
+ return retval;
+
+ return sanei_pv8630_write_byte (fd, PV8630_RDATA, value);
+}
+
+#ifdef DEBUG
+static int
+lm9830_dump_registers (int fd)
+{
+ int i;
+ unsigned char value = 0;
+
+ for (i = 0; i < 0x80; i++)
+ {
+ lm9830_read_register (fd, i, &value);
+ printf ("%.2x:0x%.2x", i, value);
+ if ((i + 1) % 8)
+ printf (", ");
+ else
+ printf ("\n");
+ }
+ puts ("");
+ return 0;
+}
+#endif
+
+#if 0
+static int
+pv8630_reset_buttons (int fd)
+{
+ lm9830_write_register (fd, 0x59, 0x10);
+ lm9830_write_register (fd, 0x59, 0x90);
+ return 0;
+}
+#endif
+
+#if 0
+static int
+lm9830_lamp_off (int fd)
+{
+ lm9830_write_register (fd, 0x07, 0x00);
+ lm9830_write_register (fd, 0x2c, 0x00);
+ lm9830_write_register (fd, 0x2d, 0x01);
+ lm9830_write_register (fd, 0x2e, 0x3f);
+ lm9830_write_register (fd, 0x2f, 0xff);
+ return 0;
+}
+
+static int
+lm9830_lamp_on (int fd)
+{
+ lm9830_write_register (fd, 0x07, 0x00);
+ lm9830_write_register (fd, 0x2c, 0x3f);
+ lm9830_write_register (fd, 0x2d, 0xff);
+ lm9830_write_register (fd, 0x2e, 0x00);
+ lm9830_write_register (fd, 0x2f, 0x01);
+ return 0;
+}
+#endif
+
+#if 0
+/*
+ * This function prints what button was pressed (the time before this
+ * code was executed).
+ */
+
+static int
+hp4200c_what_button (int fd)
+{
+ unsigned char button;
+
+ pv8630_read_buttons (fd, &button);
+
+ if (button & 0x08)
+ puts ("Scan");
+ if (button & 0x10)
+ puts ("Copy");
+ if (button & 0x20)
+ puts ("E-mail");
+ if ((button & 0x38) == 0)
+ puts ("None");
+
+ pv8630_reset_buttons (fd);
+ return 0;
+}
+#endif
+
+static int
+lm9830_ini_scanner (int fd, unsigned char *regs)
+{
+#ifdef unused
+ unsigned char inittable[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x1A, 0x00, 0x0A, 0x60, 0x2F, 0x13, 0x06,
+ 0x17, 0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x0B,
+ 0x00, 0x00, 0x00, 0x00, 0x0D, 0x21, 0x00, 0x40,
+ 0x15, 0x18, 0x00, 0x40, 0x02, 0x98, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30,
+ 0x25, 0x25, 0x24, 0x28, 0x24, 0x28, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x1D, 0x00, 0x13, 0x05, 0x48,
+ 0x01, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x02,
+ 0x00, 0x15, 0x00, 0x45, 0x00, 0x10, 0x08, 0x17,
+ 0x2B, 0x90, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00
+ };
+#endif
+
+ unsigned char daisy[] = { 0x99, 0x66, 0xcc, 0x33 };
+ unsigned char *regdata;
+ unsigned int i;
+
+ sanei_pv8630_write_byte (fd, PV8630_RMODE, 0x02);
+ for (i = 0; i < sizeof (daisy); i++)
+ {
+ sanei_pv8630_write_byte (fd, PV8630_RDATA, daisy[i]);
+ }
+ sanei_pv8630_write_byte (fd, PV8630_RMODE, 0x16);
+ lm9830_write_register (fd, 0x42, 0x06);
+
+ if (!regs)
+ return 0;
+ /* regdata = inittable; */
+ else
+ regdata = regs;
+
+ for (i = 8; i < 0x60; i++)
+ {
+ lm9830_write_register (fd, i, regdata[i]);
+ }
+ for (i = 0x60; i < 0x70; i++)
+ {
+ lm9830_write_register (fd, i, 0);
+ }
+ lm9830_write_register (fd, 0x70, 0x70);
+ for (i = 0x71; i < 0x80; i++)
+ {
+ lm9830_write_register (fd, i, 0);
+ }
+ return 0;
+}
+
+static int
+lm9830_reset (int fd)
+{
+ lm9830_write_register (fd, 0x07, 0x08);
+ usleep (100);
+ lm9830_write_register (fd, 0x07, 0x00);
+ usleep (100);
+
+ return 0;
+}
diff --git a/backend/hp4200_lm9830.h b/backend/hp4200_lm9830.h
new file mode 100644
index 0000000..4cf44eb
--- /dev/null
+++ b/backend/hp4200_lm9830.h
@@ -0,0 +1,184 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Adrian Perez Jorge
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a backend for the HP4200C flatbed scanner
+*/
+
+
+#define INPUT_SIGNAL_POLARITY_NEGATIVE 0
+#define INPUT_SIGNAL_POLARITY_POSITIVE 1
+#define CDS_OFF 0
+#define CDS_ON (1 << 1)
+#define SENSOR_EVENODD (1 << 2)
+#define SENSOR_STANDARD 0
+#define SENSOR_RESOLUTION_300 0
+#define SENSOR_RESOLUTION_600 (1 << 3)
+#define LINE_SKIPPING_COLOR_PHASE_DELAY(n) (((n) & 0x0f) << 4)
+
+#define PHI1_POLARITY_POSITIVE 0
+#define PHI1_POLARITY_NEGATIVE 1
+#define PHI2_POLARITY_POSITIVE 0
+#define PHI2_POLARITY_NEGATIVE (1 << 1)
+#define RS_POLARITY_POSITIVE 0
+#define RS_POLARITY_NEGATIVE (1 << 2)
+#define CP1_POLARITY_POSITIVE 0
+#define CP1_POLARITY_NEGATIVE (1 << 3)
+#define CP2_POLARITY_POSITIVE 0
+#define CP2_POLARITY_NEGATIVE (1 << 4)
+#define TR1_POLARITY_POSITIVE 0
+#define TR1_POLARITY_NEGATIVE (1 << 5)
+#define TR2_POLARITY_POSITIVE 0
+#define TR2_POLARITY_NEGATIVE (1 << 6)
+
+#define PHI1_OFF 0
+#define PHI1_ACTIVE 1
+#define PHI2_OFF 0
+#define PHI2_ACTIVE (1 << 1)
+#define RS_OFF 0
+#define RS_ACTIVE (1 << 2)
+#define CP1_OFF 0
+#define CP1_ACTIVE (1 << 3)
+#define CP2_OFF 0
+#define CP2_ACTIVE (1 << 4)
+#define TR1_OFF 0
+#define TR1_ACTIVE (1 << 5)
+#define TR2_OFF 0
+#define TR2_ACTIVE (1 << 6)
+#define NUMBER_OF_TR_PULSES(n) (((n) - 1) << 7)
+
+#define TR_PULSE_DURATION(n) ((n) & 0x0f)
+#define TR_PHI1_GUARDBAND_DURATION(n) (((n) & 0x0f) << 4)
+
+#define CIS_TR1_TIMING_OFF 0
+#define CIS_TR1_TIMING_MODE1 1
+#define CIS_TR1_TIMING_MODE2 2
+#define FAKE_OPTICAL_BLACK_PIXELS_OFF 0
+#define FAKE_OPTICAL_BLACK_PIXELS_ON (1 << 2)
+
+#define PIXEL_RATE_3_CHANNELS 0
+#define LINE_RATE_3_CHANNELS 1
+#define MODEA_1_CHANNEL 4
+#define MODEB_1_CHANNEL 5
+#define GRAY_CHANNEL_RED 0
+#define GRAY_CHANNEL_GREEN (1 << 3)
+#define GRAY_CHANNEL_BLU (2 << 3)
+
+#define TR_RED(n) ((n) << 5)
+#define TR_GREEN(n) ((n) << 6)
+#define TR_BLUE(n) ((n) << 7)
+
+#define TR_RED_DROP(n) (n)
+#define TR_GREEN_DROP(n) ((n) << 2)
+#define TR_BLUE_DROP(n) ((n) << 4)
+
+#define ILLUMINATION_MODE(n) (n)
+
+#define HIBYTE(w) ((SANE_Byte)(((w) >> 8) & 0xff))
+#define LOBYTE(w) ((SANE_Byte)((w) & 0xff))
+
+#define EPP_MODE 0
+#define NIBBLE_MODE 1
+
+#define PPORT_DRIVE_CURRENT(n) ((n) << 1)
+
+#define RAM_SIZE_64 0
+#define RAM_SIZE_128 1
+#define RAM_SIZE_256 2
+
+#define SRAM_DRIVER_CURRENT(n) ((n) << 2)
+#define SRAM_BANDWIDTH_4 0
+#define SRAM_BANDWIDTH_8 (1 << 4)
+#define SCANNING_FULL_DUPLEX 0
+#define SCANNING_HALF_DUPLEX (1 << 5)
+
+#define FULL_STEPPING 0
+#define MICRO_STEPPING 1
+#define CURRENT_SENSING_PHASES(n) (((n) - 1) << 1)
+#define PHASE_A_POLARITY_POSITIVE 0
+#define PHASE_A_POLARITY_NEGATIVE (1 << 2)
+#define PHASE_B_POLARITY_POSITIVE 0
+#define PHASE_B_POLARITY_NEGATIVE (1 << 3)
+#define STEPPER_MOTOR_TRISTATE 0
+#define STEPPER_MOTOR_OUTPUT (1 << 4)
+
+#define ACCELERATION_PROFILE_STOPPED(n) (n)
+#define ACCELERATION_PROFILE_25P(n) ((n) << 2)
+#define ACCELERATION_PROFILE_50P(n) ((n) << 4)
+
+#define NON_REVERSING_EXTRA_LINES(n) (n)
+#define FIRST_LINE_TO_PROCESS(n) ((n) << 3)
+
+#define KICKSTART_STEPS(n) (n)
+#define HOLD_CURRENT_TIMEOUT(n) ((n) << 3)
+
+#define PAPER_SENSOR_1_POLARITY_LOW 0
+#define PAPER_SENSOR_1_POLARITY_HIGH 1
+#define PAPER_SENSOR_1_TRIGGER_LEVEL 0
+#define PAPER_SENSOR_1_TRIGGER_EDGE (1 << 1)
+#define PAPER_SENSOR_1_NO_STOP_SCAN 0
+#define PAPER_SENSOR_1_STOP_SCAN (1 << 2)
+#define PAPER_SENSOR_2_POLARITY_LOW 0
+#define PAPER_SENSOR_2_POLARITY_HIGH (1 << 3)
+#define PAPER_SENSOR_2_TRIGGER_LEVEL 0
+#define PAPER_SENSOR_2_TRIGGER_EDGE (1 << 4)
+#define PAPER_SENSOR_2_NO_STOP_SCAN 0
+#define PAPER_SENSOR_2_STOP_SCAN (1 << 5)
+
+#define MISCIO_1_TYPE_INPUT 0
+#define MISCIO_1_TYPE_OUTPUT 1
+#define MISCIO_1_POLARITY_LOW 0
+#define MISCIO_1_POLARITY_HIGH (1 << 1)
+#define MISCIO_1_TRIGGER_LEVEL 0
+#define MISCIO_1_TRIGGER_EDGE (1 << 2)
+#define MISCIO_1_OUTPUT_STATE_LOW 0
+#define MISCIO_1_OUTPUT_STATE_HIGH (1 << 3)
+#define MISCIO_2_TYPE_INPUT 0
+#define MISCIO_2_TYPE_OUTPUT (1 << 4)
+#define MISCIO_2_POLARITY_LOW 0
+#define MISCIO_2_POLARITY_HIGH (1 << 5)
+#define MISCIO_2_TRIGGER_LEVEL 0
+#define MISCIO_2_TRIGGER_EDGE (1 << 6)
+#define MISCIO_2_OUTPUT_STATE_LOW 0
+#define MISCIO_2_OUTPUT_STATE_HIGH (1 << 7)
+
+#define PIXEL_PACKING(n) ((n) << 3)
+#define DATAMODE(n) ((n) << 5)
diff --git a/backend/hp5400.c b/backend/hp5400.c
new file mode 100644
index 0000000..61de3db
--- /dev/null
+++ b/backend/hp5400.c
@@ -0,0 +1,82 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+
+ This file was initially copied from the hp3300 testools and adjusted to
+ suit. Original copyright notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+
+/*
+ SANE interface for hp54xx scanners. Prototype.
+ Parts of this source were inspired by other backends.
+*/
+
+#include "../include/sane/config.h"
+
+#include "hp5400_debug.h"
+#include "hp5400.h"
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+
+
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memcpy */
+#include <stdio.h>
+
+
+#define HP5400_CONFIG_FILE "hp5400.conf"
+
+#define BUILD 3
+
+/* (source) includes for data transfer methods */
+#include "hp5400_debug.c"
+#include "hp5400_internal.c"
+#include "hp5400_sane.c"
+#include "hp5400_sanei.c"
+
+
diff --git a/backend/hp5400.conf.in b/backend/hp5400.conf.in
new file mode 100644
index 0000000..98c97dd
--- /dev/null
+++ b/backend/hp5400.conf.in
@@ -0,0 +1,14 @@
+# hp5400.conf
+# See man sane-hp5400 for a description.
+#
+# HP 5400C
+usb 0x03F0 0x1005
+#
+# HP 5470C
+usb 0x03F0 0x1105
+#
+# Device filename to use for scanner access
+#
+# Uncomment the following line if autodetection fails
+#
+#/dev/usbscanner
diff --git a/backend/hp5400.h b/backend/hp5400.h
new file mode 100644
index 0000000..b0efb4f
--- /dev/null
+++ b/backend/hp5400.h
@@ -0,0 +1,143 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ $Id$
+*/
+
+
+/*
+ Core HP5400 functions.
+*/
+
+
+#ifndef _HP5400_H_
+#define _HP5400_H_
+
+#include <unistd.h>
+
+#include "hp5400_xfer.h" /* for EScannerModel */
+
+#define HW_DPI 300 /* horizontal resolution of hardware */
+#define HW_LPI 300 /* vertical resolution of hardware */
+
+enum ScanType
+{
+ SCAN_TYPE_CALIBRATION,
+ SCAN_TYPE_PREVIEW,
+ SCAN_TYPE_NORMAL
+};
+
+/* In case we ever need to track multiple models */
+typedef struct
+{
+ char *pszVendor;
+ char *pszName;
+}
+TScannerModel;
+
+typedef struct
+{
+ /* transfer buffer */
+ void *buffer; /* Pointer to memory allocated for buffer */
+ int roff, goff, boff; /* Offset into buffer of rows to be copied *next* */
+ int bufstart, bufend; /* What is currently the valid buffer */
+ int bpp; /* Bytes per pixel per colour (1 or 2) */
+ int linelength, pixels; /* Bytes per line from scanner */
+ int transfersize; /* Number of bytes to transfer resulting image */
+ int blksize; /* Size of blocks to pull from scanner */
+ int buffersize; /* Size of the buffer */
+}
+TDataPipe;
+
+typedef struct
+{
+ int iXferHandle; /* handle used for data transfer to HW */
+ TDataPipe pipe; /* Pipe for data */
+
+ int iTopLeftX; /* in mm */
+ int iTopLeftY; /* in mm */
+ /* int iSensorSkew; *//* in units of 1/1200 inch */
+ /* int iSkipLines; *//* lines of garbage to skip */
+ /* int fReg07; *//* NIASH00019 */
+ /* int fGamma16; *//* if TRUE, gamma entries are 16 bit */
+/* int iExpTime; */
+ /* int iReversedHead; *//* Head is reversed */
+ /* int iBufferSize; *//* Size of internal scan buffer */
+/* EScannerModel eModel; */
+}
+THWParams;
+
+/* The scanner needs a Base DPI off which all it's calibration and
+ * offset/size parameters are based. For the time being this is the same as
+ * the iDpi but maybe we want it seperate. This is because while this field
+ * would have limited values (300,600,1200,2400) the x/y dpi can vary. The
+ * windows interface seems to allow 200dpi (though I've never tried it). We
+ * need to decide how these values are related to the HW coordinates. */
+
+
+typedef struct
+{
+ int iDpi; /* horizontal resolution */
+ int iLpi; /* vertical resolution */
+ int iTop; /* in HW coordinates (units HW_LPI) */
+ int iLeft; /* in HW coordinates (units HW_LPI) */
+ int iWidth; /* in HW coordinates (units HW_LPI) */
+ int iHeight; /* in HW coordinates (units HW_LPI) */
+
+ int iBytesPerLine; /* Resulting bytes per line */
+ int iLines; /* Resulting lines of image */
+ int iLinesRead; /* Lines of image already read */
+
+ int iColourOffset; /* How far the colours are offset. Currently this is
+ * set by the caller. This doesn't seem to be
+ * necessary anymore since the scanner is doing it
+ * internally. Leave it for the time being as it
+ * may be needed later. */
+}
+TScanParams;
+
+
+
+#endif /* NO _HP5400_H_ */
diff --git a/backend/hp5400_debug.c b/backend/hp5400_debug.c
new file mode 100644
index 0000000..0460e8b
--- /dev/null
+++ b/backend/hp5400_debug.c
@@ -0,0 +1,73 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "hp5400_debug.h"
+
+
+#ifdef STANDALONE
+
+#include <stdio.h>
+
+FILE *DBG_ASSERT = NULL;
+FILE *DBG_ERR = NULL;
+FILE *DBG_MSG = NULL;
+
+void hp5400_dbg_start() {
+ DBG_MSG = stdout;
+ DBG_ERR = stderr;
+ DBG_ASSERT = stderr;
+}
+
+#else
+
+/* #define DEBUG_NOT_STATIC */
+#undef DEBUG_DECLARE_ONLY
+#undef _SANEI_DEBUG_H
+#include "../include/sane/sanei_debug.h"
+
+#endif
+
+
diff --git a/backend/hp5400_debug.h b/backend/hp5400_debug.h
new file mode 100644
index 0000000..c743045
--- /dev/null
+++ b/backend/hp5400_debug.h
@@ -0,0 +1,81 @@
+#ifndef __HP5400_DEBUG_H_
+#define __HP5400_DEBUG_H_
+
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+#ifndef STANDALONE
+
+#define DEBUG_NOT_STATIC
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/sanei_debug.h"
+
+#define DBG_ASSERT 1
+#define DBG_ERR 16
+#define DBG_MSG 32
+#define HP5400_DBG DBG
+#define HP5400_SANE_STATIC static
+
+
+#else
+
+#include <stdio.h>
+#define LOCAL_DBG
+#define HP5400_DBG fprintf
+extern FILE *DBG_ASSERT;
+extern FILE *DBG_ERR;
+extern FILE *DBG_MSG;
+
+void hp5400_dbg_start();
+
+#define HP5400_SANE_STATIC
+
+#endif
+
+
+
+#endif
diff --git a/backend/hp5400_internal.c b/backend/hp5400_internal.c
new file mode 100644
index 0000000..d7e2325
--- /dev/null
+++ b/backend/hp5400_internal.c
@@ -0,0 +1,1455 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+ Copyright (c) 2003 Henning Meier-Geinitz, <henning@meier-geinitz.de>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ HP5400/5470 Test util.
+ Currently is only able to read back the scanner version string,
+ but this basically demonstrates ability to communicate with the scanner.
+
+ Massively expanded. Can do calibration scan, upload gamma and calibration
+ tables and stores the results of a scan. - 19/02/2003 Martijn
+*/
+
+#include <stdio.h> /* for printf */
+#include <stdlib.h> /* for exit */
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <netinet/in.h> /* for htons */
+#include <string.h>
+
+
+#include "hp5400.h"
+#include "hp5400_xfer.h"
+#include "hp5400_internal.h"
+#include "hp5400_debug.h" /* debug functions */
+
+
+#ifndef min
+#define min(A,B) (((A)<(B)) ? (A) : (B))
+#endif
+#ifndef max
+#define max(A,B) (((A)>(B)) ? (A) : (B))
+#endif
+
+
+#ifndef STRING_VERSION_MATCH
+#define NO_STRING_VERSION_MATCH 1
+#endif
+
+#ifdef __GNUC__
+#define PACKED __attribute__ ((packed))
+#else
+#define PACKED
+#endif
+
+/* If this is enabled, a copy of the raw data from the scanner will be saved to
+ imagedebug.dat and the attempted conversion to imagedebug.ppm */
+/* #define IMAGE_DEBUG */
+
+/* If this is defined you get extra info on the calibration */
+/* #define CALIB_DEBUG */
+
+
+typedef struct versionString {
+ char strVersion[128];
+} versionString;
+
+const int numVersions = 3;
+versionString *MatchVersions;
+
+
+static TScannerModel Model_HP54xx =
+ { "Hewlett-Packard", "HP54xx Flatbed Scanner" };
+
+
+HP5400_SANE_STATIC
+int
+InitHp5400_internal() {
+
+ MatchVersions = malloc( sizeof(versionString) * numVersions );
+ strcpy( MatchVersions[0].strVersion, "SilitekIBlizd C3 ScannerV0.84");
+ strcpy( MatchVersions[1].strVersion, "SilitekIBlizd C3 ScannerV0.86");
+ strcpy( MatchVersions[2].strVersion, "SilitekIBlizd C3 ScannerV0.87");
+
+ return 1;
+}
+
+HP5400_SANE_STATIC
+int
+FreeHp5400_internal() {
+
+ free(MatchVersions);
+ MatchVersions = NULL;
+
+ return 1;
+}
+
+
+HP5400_SANE_STATIC
+int
+WriteByte (int iHandle, int cmd, char data)
+{
+ if (hp5400_command_write (iHandle, cmd, 1, &data) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to send byte (cmd=%04X)\n", cmd);
+ return -1;
+ }
+ return 0;
+}
+
+HP5400_SANE_STATIC
+int
+SetLamp (THWParams * pHWParams, int fLampOn)
+{
+ if (fLampOn)
+ {
+ if (WriteByte (pHWParams->iXferHandle, 0x0000, 0x01) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+HP5400_SANE_STATIC
+int
+WarmupLamp (int iHandle)
+{
+ int i = 30; /* Max 30 seconds, 15 is typical for cold start? */
+ int couldRead;
+ unsigned char dataVerify[0x02];
+
+ /* Keep writing 01 to 0000 until no error... */
+ unsigned char data0000[] = {
+ 0x01
+ };
+ unsigned char data0300[3];
+
+
+ hp5400_command_write_noverify (iHandle, 0x0000, data0000, 1);
+
+ do
+ {
+ hp5400_command_read_noverify (iHandle, 0x0300, 3, data0300);
+
+ hp5400_command_write_noverify (iHandle, 0x0000, data0000, 1);
+
+ couldRead =
+ hp5400_command_read_noverify (iHandle, 0xc500, 0x02, dataVerify);
+ if ((dataVerify[0] != 0) || (dataVerify[1] != 0))
+ sleep (1);
+ }
+ while ((i-- > 0) && (couldRead >= 0)
+ && ((dataVerify[0] != 0) || (dataVerify[1] != 0)));
+
+ if (i > 0)
+ return 0;
+
+ /*
+
+ while( i > 0 )
+ {
+ if( WriteByte( iHandle, 0x0000, 0x01 ) == 0 )
+ return 0;
+ sleep(1);
+ i--;
+ }
+ */
+
+ HP5400_DBG (DBG_MSG, "***WARNING*** Warmup lamp failed...\n");
+ return -1;
+}
+
+#define CALPIXBYBLOCK 42
+
+HP5400_SANE_STATIC
+int
+SetCalibration (int iHandle, int numPixels, unsigned int *low_vals[3],
+ unsigned int *high_vals[3], int dpi)
+{
+ char cmd[8];
+ /* unsigned char cmd[8]; */ /* should fix the compilation warning
+ but I don't have a scanner right now
+ to check that the fix does not break
+ calibration */
+
+ int i, j, k;
+ struct CalPixel
+ {
+ char highr[2], highg[2], highb[2];
+ char lowr[2], lowg[2], lowb[2];
+ };
+ struct CalBlock
+ {
+ struct CalPixel pixels[CALPIXBYBLOCK];
+ char pad[8];
+ }
+ PACKED;
+
+ struct CalBlock *calinfo;
+
+ /**
+ we did scan test at 300 dpi, so we don't have all the needed pixels.
+ To fill the gap, we loop
+ */
+ int numLoop = max (1, dpi / 300);
+ int calBlockSize = CALPIXBYBLOCK * (6 * sizeof (short)) + 8 * sizeof (char); /* = sizeof(calBlock) */
+ int numCalBlock =
+ ((numPixels / CALPIXBYBLOCK) +
+ ((numPixels % CALPIXBYBLOCK != 0) ? 1 : 0));
+ int calSize = numLoop * calBlockSize * numCalBlock;
+
+ calinfo = malloc (calSize);
+ memset (calinfo, 0, calSize);
+
+ for (j = 0; j < numLoop * numCalBlock * CALPIXBYBLOCK; j++)
+ {
+ struct CalPixel *pixel =
+ &calinfo[j / CALPIXBYBLOCK].pixels[j % CALPIXBYBLOCK];
+
+ /*
+ i = ( j % (int)( 0.80 * numPixels ) ) + (int)(0.10 * numPixels );
+ */
+ /* better solution : stretch the actual scan size to the calibration size */
+ i = j / numLoop;
+
+ /* This is obviously not quite right. The values on
+ * the right are approximatly what windows sends */
+ k = (high_vals[0][i] > 0x4000) ? 1000000000 / high_vals[0][i] : 0; /* 0x6700 */
+ pixel->highr[0] = k;
+ pixel->highr[1] = k >> 8;
+ k = (high_vals[1][i] > 0x4000) ? 1000000000 / high_vals[1][i] : 0; /* 0x5e00 */
+ pixel->highg[0] = k;
+ pixel->highg[1] = k >> 8;
+ k = (high_vals[2][i] > 0x4000) ? 1000000000 / high_vals[2][i] : 0; /* 0x6000 */
+ pixel->highb[0] = k;
+ pixel->highb[1] = k >> 8;
+
+ pixel->lowr[0] = low_vals[0][i]; /* 0x0530 */
+ pixel->lowr[1] = low_vals[0][i] >> 8;
+ pixel->lowg[0] = low_vals[1][i]; /* 0x0530 */
+ pixel->lowg[1] = low_vals[1][i] >> 8;
+ pixel->lowb[0] = low_vals[2][i]; /* 0x0530 */
+ pixel->lowb[1] = low_vals[2][i] >> 8;
+ }
+
+ cmd[0] = 0xff & (calSize >> 16);
+ cmd[1] = 0xff & (calSize >> 8);
+ cmd[2] = 0xff & (calSize >> 0);
+ cmd[3] = 0x00;
+ cmd[4] = 0x54;
+ cmd[5] = 0x02;
+ cmd[6] = 0x80;
+ cmd[7] = 0x00;
+
+ hp5400_bulk_command_write (iHandle, 0xE603, cmd, 8, calSize, calSize,
+ (void *) calinfo);
+
+ free (calinfo);
+ return 0;
+}
+
+/* Write a gamma table */
+HP5400_SANE_STATIC
+void
+WriteGammaCalibTable (int iHandle, const int *pabGammaR, const int *pabGammaG,
+ const int *pabGammaB)
+{
+ char cmd[3];
+ char *buffer;
+ int i, j;
+
+ /* Setup dummy gamma correction table */
+ buffer = malloc (2 * 65536);
+
+ cmd[0] = 2;
+ cmd[1] = 0;
+ cmd[2] = 0;
+
+ for (i = 0; i < 3; i++)
+ {
+ const int *ptr = (i == 0) ? pabGammaR :
+ (i == 1) ? pabGammaG : pabGammaB;
+
+ for (j = 0; j < 65536; j++) {
+ buffer[2 * j] = ptr[j];
+ buffer[2 * j + 1] = ptr[j] >> 8;
+ }
+
+ hp5400_bulk_command_write (iHandle, 0x2A01 + i, cmd, 3, 2 * 65536,
+ 65536, (void *) buffer);
+ }
+ free (buffer);
+
+ return;
+}
+
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+void
+SetDefaultGamma (int iHandle)
+{
+ int *buffer = malloc (sizeof (int) * 65536);
+ int i;
+
+ for (i = 0; i < 65336; i++)
+ buffer[i] = i;
+
+ WriteGammaCalibTable (iHandle, buffer, buffer, buffer);
+}
+#endif
+
+#define BUFFER_SIZE (6*65536)
+
+#ifdef IMAGE_DEBUG
+FILE *temp;
+#endif
+
+/* Bytes per line is the number of pixels. The actual bytes is one more */
+HP5400_SANE_STATIC
+void
+CircBufferInit (int iHandle, TDataPipe * p, int iBytesPerLine,
+ int bpp, int iMisAlignment, int blksize, int iTransferSize)
+{
+ iHandle = iHandle; /* to avoid compilation warning */
+ p->buffersize = max (BUFFER_SIZE, 3 * blksize);
+
+ if (p->buffer)
+ {
+ free (p->buffer);
+ }
+
+ /* Allocate a large enough buffer for transfer */
+ p->buffer = malloc (p->buffersize);
+
+ p->pixels = (iBytesPerLine / 3) / bpp;
+
+ /* These three must always be positive */
+ p->roff = 0;
+ p->goff = p->pixels * bpp + 1;
+ p->boff = 2 * p->pixels * bpp + 2;;
+
+ p->linelength = iBytesPerLine + 3; /* NUL at end of each row */
+ p->bpp = bpp;
+ p->bufstart = p->bufend = 0;
+
+ if (iMisAlignment > 0)
+ {
+ p->roff += 0;
+ p->goff += p->linelength * iMisAlignment;
+ p->boff += p->linelength * iMisAlignment * 2;
+ }
+
+ if (iMisAlignment < 0)
+ {
+ p->roff -= p->linelength * iMisAlignment * 2;
+ p->goff -= p->linelength * iMisAlignment;
+ p->boff -= 0;
+ }
+
+ p->blksize = blksize;
+ p->transfersize = iTransferSize;
+
+#ifdef IMAGE_DEBUG
+ temp = fopen ("imagedebug.dat", "w+b");
+#endif
+
+ HP5400_DBG (DBG_MSG,
+ "Begin: line=%d (%X), pixels=%d (%X), r=%d (%X), g=%d (%X), b=%d (%X), bpp=%d, step=%d\n",
+ p->linelength, p->linelength, p->pixels, p->pixels, p->roff, p->roff,
+ p->goff, p->goff, p->boff, p->boff, bpp, iMisAlignment);
+}
+
+
+HP5400_SANE_STATIC
+int
+CircBufferGetLine (int iHandle, TDataPipe * p, void *pabLine)
+{
+ int i;
+ int maxoff = 0;
+ char* buftmp = (char*) (p->buffer);
+
+/* HP5400_DBG(DBG_MSG, "CircBufferGetLine:\n"); */
+
+ if (p->roff > maxoff)
+ maxoff = p->roff;
+ if (p->goff > maxoff)
+ maxoff = p->goff;
+ if (p->boff > maxoff)
+ maxoff = p->boff;
+
+ maxoff += p->pixels * p->bpp;
+
+ if (maxoff < p->linelength)
+ maxoff = p->linelength;
+
+
+ /* resize buffer if needed */
+ if (p->bufstart + maxoff >= p->buffersize + p->blksize)
+ {
+ /* store actual buffer pointer */
+ void *tmpBuf = p->buffer;
+ /* calculate new size for buffer (oversize a bit) */
+ int newsize = p->bufstart + maxoff + 2 * p->blksize;
+
+ p->buffer = malloc (newsize);
+ memcpy (p->buffer, tmpBuf, p->buffersize);
+ p->buffersize = newsize;
+
+ /* free old buffer */
+ free (tmpBuf);
+ buftmp = (char*)(p->buffer);
+ }
+
+ while (p->bufstart + maxoff >= p->bufend) /* Not enough data in buffer */
+ {
+ int res;
+ unsigned char cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ cmd[4] = p->blksize;
+ cmd[5] = p->blksize >> 8;
+
+ assert ((p->bufend + p->blksize) <= p->buffersize);
+
+ HP5400_DBG (DBG_MSG, "Reading block, %d bytes remain\n", p->transfersize);
+ p->transfersize -= p->blksize;
+
+ res =
+ hp5400_bulk_read_block (iHandle, CMD_INITBULK3, cmd, sizeof (cmd),
+ buftmp + p->bufend, p->blksize);
+ if (res != p->blksize)
+ {
+ HP5400_DBG (DBG_ERR, "*** ERROR: Read returned %d. FATAL.\n", res);
+ return -1;
+ }
+#ifdef IMAGE_DEBUG
+ fwrite (
+ buftmp + p->bufend
+ ,p->blksize
+ ,1
+ ,temp
+ );
+#endif
+ p->bufend += p->blksize;
+ }
+
+ assert (p->bufstart + maxoff < p->bufend);
+
+ /* Copy a line into the result buffer */
+ if (p->bpp == 1)
+ {
+ char *itPix = (char *) pabLine;
+ char *itR = (char *) (buftmp + p->bufstart + p->roff);
+ char *itG = (char *) (buftmp + p->bufstart + p->goff);
+ char *itB = (char *) (buftmp + p->bufstart + p->boff);
+ for (i = 0; i < p->pixels; i++)
+ {
+ /* pointer move goes a little bit faster than vector access */
+ /* Although I wouldn't be surprised if the compiler worked that out anyway.
+ * No matter, this is easier to read :) */
+ *(itPix++) = *(itR++);
+ *(itPix++) = *(itG++);
+ *(itPix++) = *(itB++);
+ }
+ }
+ else
+ {
+ short *itPix = (short *) pabLine;
+ short *itR = (short *) (buftmp + p->bufstart + p->roff);
+ short *itG = (short *) (buftmp + p->bufstart + p->goff);
+ short *itB = (short *) (buftmp + p->bufstart + p->boff);
+ for (i = 0; i < p->pixels; i++)
+ {
+#if 0
+ /* This code, while correct for PBM is not correct for the host and
+ * since we need it correct for calibration, it has to go */
+ ((short *) pabLine)[3 * i + 0] =
+ *((short *) (p->buffer + p->bufstart + p->roff + 2 * i));
+ ((short *) pabLine)[3 * i + 1] =
+ *((short *) (p->buffer + p->bufstart + p->goff + 2 * i));
+ ((short *) pabLine)[3 * i + 2] =
+ *((short *) (p->buffer + p->bufstart + p->boff + 2 * i));
+#else
+ /* pointer move goes a little bit faster than vector access */
+ *(itPix++) = htons (*(itR++));
+ *(itPix++) = htons (*(itG++));
+ *(itPix++) = htons (*(itB++));
+#endif
+ }
+ }
+
+ p->bufstart += p->linelength;
+
+ assert (p->bufstart <= p->bufend);
+
+ /* If we've used a whole block at the beginning, move it */
+ if (p->bufstart > p->blksize)
+ {
+ memmove (
+ buftmp
+ ,buftmp + p->bufstart
+ ,p->bufend - p->bufstart
+ );
+
+ p->bufend -= p->bufstart;
+ p->bufstart = 0;
+ }
+
+ return 0;
+}
+
+HP5400_SANE_STATIC
+void
+CircBufferExit (TDataPipe * p)
+{
+ free (p->buffer);
+ p->buffer = NULL;
+#ifdef IMAGE_DEBUG
+ fclose (temp);
+ temp = NULL;
+#endif
+ return;
+}
+
+
+#ifdef STANDALONE
+/* bpp is BYTES per pixel */
+HP5400_SANE_STATIC
+void
+DecodeImage (FILE * file, int planes, int bpp, int xsize, int ysize,
+ const char *filename)
+{
+ char *in, *buf;
+ char *p[3];
+ FILE *output;
+ int i, j, k;
+
+ /* xsize is byte width, not pixel width */
+ xsize /= planes * bpp;
+
+ HP5400_DBG (DBG_MSG,
+ "DecodeImage(planes=%d,bpp=%d,xsize=%d,ysize=%d) => %d (file=%s)\n",
+ planes, bpp, xsize, ysize, planes * bpp * xsize * ysize, filename);
+
+ in = malloc (planes * (xsize * bpp + 1));
+
+ for (i = 0; i < planes; i++)
+ p[i] = in + i * (xsize * bpp + 1);
+
+ buf = malloc (3 * xsize * bpp);
+
+ output = fopen (filename, "wb");
+
+ fprintf (output, "P%d\n%d %d\n", (planes == 3) ? 6 : 5, xsize, ysize);
+ fprintf (output, "%d\n", (bpp == 1) ? 255 : 0xb000);
+
+ for (i = 0; i < ysize; i++)
+ {
+ fread (in, planes * (xsize * bpp + 1), 1, file);
+
+ for (j = 0; j < xsize; j++)
+ {
+ for (k = 0; k < planes; k++)
+ {
+ if (bpp == 1)
+ {
+ buf[j * planes + k] = p[k][j];
+ }
+ else if (bpp == 2)
+ {
+ buf[j * planes * 2 + k * 2 + 0] = p[k][2 * j + 0];
+ buf[j * planes * 2 + k * 2 + 1] = p[k][2 * j + 1];
+ }
+ }
+ }
+ fwrite (buf, planes * xsize * bpp, 1, output);
+ }
+
+ fclose (output);
+
+ free (in);
+ free (buf);
+}
+
+HP5400_SANE_STATIC
+int
+hp5400_test_scan_response (struct ScanResponse *resp, struct ScanRequest *req)
+{
+ req = req; /* to avoid compilation warning */
+ HP5400_DBG (DBG_MSG, "Scan response:\n");
+ HP5400_DBG (DBG_MSG, " transfersize=%d htonl-> %d\n", resp->transfersize,
+ htonl (resp->transfersize));
+ HP5400_DBG (DBG_MSG, " xsize=%d htonl-> %d\n", resp->xsize,
+ htonl (resp->xsize));
+ HP5400_DBG (DBG_MSG, " ysize=%d htons-> %d\n", resp->ysize,
+ htons (resp->ysize));
+ return 1;
+}
+#endif
+
+/* This is a very specialised scanning function. It does the scan request as
+ * usual but instead of producing an image it returns three arrays of ints.
+ * These are averages of the R,G,B values for each column.
+ *
+ * Note the array argument should point to an array of three NULL. These
+ * will be overwritten with allocated pointers. */
+
+HP5400_SANE_STATIC
+int
+DoAverageScan (int iHandle, struct ScanRequest *req, int code,
+ unsigned int **array)
+{
+ THWParams HWParams;
+ struct ScanResponse res;
+ unsigned short *buffer;
+ int i, j, k, length;
+
+ memset (&HWParams, 0, sizeof (HWParams));
+
+ HWParams.iXferHandle = iHandle;
+
+ if (InitScan2 (SCAN_TYPE_CALIBRATION, req, &HWParams, &res, 0, code) != 0)
+ return -1; /* No colour offseting, we want raw */
+
+ length = htonl (res.xsize) / 6;
+
+ HP5400_DBG (DBG_MSG, "Calibration scan: %d pixels wide\n", length);
+
+ for (j = 0; j < 3; j++)
+ {
+ array[j] = malloc (sizeof (int) * length);
+ memset (array[j], 0, sizeof (int) * length); /* Clear array */
+ }
+
+ buffer = malloc (htonl (res.xsize) + 1);
+
+ /* First we just sum them all */
+ for (i = 0; i < htons (res.ysize); i++)
+ {
+ CircBufferGetLine (iHandle, &HWParams.pipe, buffer);
+
+ for (j = 0; j < length; j++)
+ for (k = 0; k < 3; k++)
+ array[k][j] += buffer[3 * j + k];
+ }
+
+ free (buffer);
+ FinishScan (&HWParams);
+
+ /* Now divide by the height to get the average */
+ for (j = 0; j < length; j++)
+ for (k = 0; k < 3; k++)
+ array[k][j] /= htons (res.ysize);
+
+ return 0;
+}
+
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+int
+DoScan (int iHandle, struct ScanRequest *req, const char *filename, int code,
+ struct ScanResponse *res)
+{
+ FILE *file;
+ THWParams HWParams;
+ struct ScanResponse res_temp;
+ void *buffer;
+/* int bpp, planes; */
+ int i;
+
+ code = code; /*to avoid compilation warning*/
+
+ if (res == NULL)
+ res = &res_temp;
+
+ memset (&HWParams, 0, sizeof (HWParams));
+
+ file = fopen (filename, "w+b");
+ if (!file)
+ {
+ HP5400_DBG (DBG_MSG, "Couldn't open outputfile (%s)\n", strerror (errno));
+ return -1;
+ }
+
+ HWParams.iXferHandle = iHandle;
+
+ if (InitScan2 (SCAN_TYPE_NORMAL, req, &HWParams, res, 1, 0x40) != 0)
+ return -1;
+
+ fprintf (file, "P%d\n%d %d\n", 6, htonl (res->xsize) / 3,
+ htons (res->ysize));
+ fprintf (file, "%d\n", 255);
+
+ buffer = malloc (htonl (res->xsize) + 1);
+ for (i = 0; i < htons (res->ysize); i++)
+ {
+ CircBufferGetLine (iHandle, &HWParams.pipe, buffer);
+
+ fwrite (buffer, htonl (res->xsize), 1, file);
+ }
+ free (buffer);
+
+ FinishScan (&HWParams);
+
+ fclose (file);
+ return 0;
+}
+#endif
+
+HP5400_SANE_STATIC
+int
+Calibrate (int iHandle, int dpi)
+{
+ struct ScanRequest req;
+
+ unsigned int *low_array[3];
+ unsigned int *high_array[3];
+#ifdef CALIB_DEBUG
+ char buffer[512];
+ int i, j;
+#endif
+
+/* The first calibration scan. Finds maximum of each CCD */
+ memset(&req, 0, sizeof(req));
+
+ req.x1 = 0x08;
+ req.dpix = htons (300); /* = 300 dpi */
+ req.dpiy = htons (300); /* = 300 dpi */
+ req.offx = htons (0); /* = 0cm */
+ req.offy = htons (0); /* = 0cm */
+ req.lenx = htons (2690); /* = 22.78cm */
+ req.leny = htons (50); /* = 0.42cm */
+
+ req.flags1 = htons (0x0000);
+ req.flags2 = htons (0x0010);
+ req.flags3 = htons (0x3020); /* First calibration scan, 48bpp */
+
+ req.gamma[0] = htons (100);
+ req.gamma[1] = htons (100);
+ req.gamma[2] = htons (100);
+
+ if (DoAverageScan (iHandle, &req, 0x40, high_array) != 0)
+ return -1;
+
+#ifdef CALIB_DEBUG
+ for (i = 0; i < 3; i++)
+ {
+ int len;
+ buffer[0] = 0;
+ sprintf (buffer, "Average %d: \n", i);
+ len = strlen (buffer);
+
+ for (j = 0; j < 24; j++)
+ {
+ sprintf (buffer + len, "%04X ", high_array[i][j]);
+ len += 5;
+ }
+ strcat (buffer, " ... \n");
+ len += 6;
+
+ for (j = 1000; j < 1024; j++)
+ {
+ sprintf (buffer + len, "%04X ", high_array[i][j]);
+ len += 5;
+ }
+ strcat (buffer, " ... \n");
+ len += 6;
+
+ for (j = 2000; j < 2024; j++)
+ {
+ sprintf (buffer + len, "%04X ", high_array[i][j]);
+ len += 5;
+ }
+ strcat (buffer, " ... \n");
+ len += 6;
+
+ HP5400_DBG (DBG_MSG, buffer);
+ }
+#endif
+
+/* The second calibration scan. Finds minimum of each CCD */
+ memset(&req, 0, sizeof(req));
+
+ req.x1 = 0x08;
+ req.dpix = htons (300); /* = 300 dpi */
+ req.dpiy = htons (300); /* = 300 dpi */
+ req.offx = htons (0); /* = 0cm */
+ req.offy = htons (0); /* = 0cm */
+ req.lenx = htons (2690); /* = 22.78cm */
+ req.leny = htons (16); /* = 0.14cm */
+
+ req.flags1 = htons (0x0000);
+ req.flags2 = htons (0x0010);
+ req.flags3 = htons (0x3024); /* Second calibration scan, 48bpp */
+
+ req.gamma[0] = htons (100);
+ req.gamma[1] = htons (100);
+ req.gamma[2] = htons (100);
+
+ if (DoAverageScan (iHandle, &req, 0x00, low_array) != 0)
+ return -1;
+
+#ifdef CALIB_DEBUG
+ for (i = 0; i < 3; i++)
+ {
+ int len;
+ buffer[0] = 0;
+ sprintf (buffer, "Average %d: \n", i);
+ len = strlen (buffer);
+
+ for (j = 0; j < 24; j++)
+ {
+ sprintf (buffer + len, "%04X ", low_array[i][j]);
+ len += 5;
+ }
+ strcat (buffer, " ... \n");
+ len += 6;
+
+ for (j = 1000; j < 1024; j++)
+ {
+ sprintf (buffer + len, "%04X ", low_array[i][j]);
+ len += 5;
+ }
+ strcat (buffer, " ... \n");
+ len += 6;
+
+ for (j = 2000; j < 2024; j++)
+ {
+ sprintf (buffer + len, "%04X ", low_array[i][j]);
+ len += 5;
+ }
+ strcat (buffer, " ... \n");
+ len += 6;
+
+ HP5400_DBG (DBG_MSG, buffer);
+ }
+#endif
+
+ SetCalibration (iHandle, 2690, low_array, high_array, dpi);
+
+ return 0;
+}
+
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+int
+hp5400_scan (int iHandle, TScanParams * params, THWParams * pHWParams,
+ const char *filename)
+{
+ struct ScanRequest req;
+ struct ScanResponse res;
+ int result;
+
+ pHWParams = pHWParams; /*to avoid compilation warning*/
+
+ HP5400_DBG (DBG_MSG, "\n");
+ HP5400_DBG (DBG_MSG, "Scanning :\n");
+ HP5400_DBG (DBG_MSG, " dpi(x) : %d\n", params->iDpi);
+ HP5400_DBG (DBG_MSG, " dpi(y) : %d\n", params->iLpi);
+ HP5400_DBG (DBG_MSG, " x0 : %d\n", params->iLeft);
+ HP5400_DBG (DBG_MSG, " y0 : %d\n", params->iTop);
+ HP5400_DBG (DBG_MSG, " width : %d\n", params->iWidth);
+ HP5400_DBG (DBG_MSG, " height : %d\n", params->iHeight);
+ HP5400_DBG (DBG_MSG, "\n");
+
+ memset(&req, 0, sizeof(req));
+
+ req.x1 = 0x08;
+ req.dpix = htons (params->iDpi);
+ req.dpiy = htons (params->iLpi);
+
+ /* These offsets and lengths should all be in the reference DPI which
+ * is set to HW_LPI */
+ req.offx = htons (params->iLeft);
+ req.offy = htons (params->iTop);
+ req.lenx = htons (params->iWidth);
+ req.leny = htons (params->iHeight);
+
+ req.flags1 = htons (0x0080);
+ req.flags2 = htons (0x0000);
+ req.flags3 = htons ((params->iDpi < 2400) ? 0x18E8 : 0x18C0); /* Try preview scan */
+
+ req.gamma[0] = htons (100);
+ req.gamma[1] = htons (100);
+ req.gamma[2] = htons (100);
+
+ if (Calibrate (iHandle, params->iDpi) != 0)
+ return -1;
+ SetDefaultGamma (iHandle);
+
+ result = DoScan (iHandle, &req, filename, 0x40, &res);
+
+ /* Pass the results back to the parent */
+ params->iBytesPerLine = htonl (res.xsize);
+ params->iLines = htons (res.ysize);
+
+#if 0
+ imageFile = fopen ("output.dat", "r+b");
+ if (imageFile)
+ {
+ int planes = 3;
+ int bpp = 1;
+ fseek (imageFile, 0, SEEK_SET);
+ DecodeImage (imageFile, planes, bpp, planes * bpp * params->iWidth,
+ params->iHeight, filename);
+ fclose (imageFile);
+ }
+#endif
+ return result;
+}
+
+HP5400_SANE_STATIC
+int
+PreviewScan (int iHandle)
+{
+ TScanParams params;
+ THWParams pHWParams;
+
+ /* Reference LPI is 300dpi, remember */
+ params.iDpi = 75;
+ params.iLpi = 75;
+ params.iLeft = 0;
+ params.iTop = 0;
+ params.iWidth = 2552; /* = 21.61cm * 300dpi */
+ params.iHeight = 3510; /* = 29.72cm * 300dpi */
+ params.iColourOffset = 1;
+
+ return hp5400_scan (iHandle, &params, &pHWParams, "output.ppm");
+}
+
+
+static char UISetup1[] = {
+/* Offset 40 */
+ 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x14, 0x00,
+ 0x52, 0x65, 0x61, 0x64,
+ 0x79, 0x00, 0x53, 0x63, 0x61, 0x6E, 0x6E, 0x65, 0x72, 0x20, 0x4C, 0x6F,
+ 0x63, 0x6B, 0x65, 0x64,
+ 0x00, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x00, 0x43, 0x61, 0x6E, 0x63, 0x65,
+ 0x6C, 0x69, 0x6E, 0x67,
+ 0x14, 0x00, 0x50, 0x6F, 0x77, 0x65, 0x72, 0x53, 0x61, 0x76, 0x65, 0x20,
+ 0x4F, 0x6E, 0x00, 0x53,
+};
+
+static char UISetup2[] = {
+
+ 0x63, 0x61, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x14, 0x00, 0x41, 0x44, 0x46,
+ 0x20, 0x70, 0x61, 0x70,
+ 0x65, 0x72, 0x20, 0x6A, 0x61, 0x6D, 0x00, 0x43, 0x6F, 0x70, 0x69, 0x65,
+ 0x73, 0x00, 0x00,
+};
+
+HP5400_SANE_STATIC
+int
+InitScanner (int iHandle)
+{
+ WarmupLamp (iHandle);
+
+ if (WriteByte (iHandle, 0xF200, 0x40) < 0)
+ return -1;
+
+ if (hp5400_command_write (iHandle, 0xF10B, sizeof (UISetup1), UISetup1) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to send UISetup1 (%lu)\n", (u_long) sizeof (UISetup1));
+ return -1;
+ }
+
+ if (WriteByte (iHandle, 0xF200, 0x00) < 0)
+ return -1;
+
+ if (hp5400_command_write (iHandle, 0xF10C, sizeof (UISetup2), UISetup2) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to send UISetup2\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/* Warning! The caller must have configured the gamma tables at this stage */
+HP5400_SANE_STATIC
+int
+InitScan (enum ScanType scantype, TScanParams * pParams,
+ THWParams * pHWParams)
+{
+ struct ScanRequest req;
+ struct ScanResponse res;
+ int ret;
+
+ memset(&req, 0, sizeof(req));
+
+ req.x1 = 0x08;
+ req.dpix = htons (pParams->iDpi); /* = 300 dpi */
+ req.dpiy = htons (pParams->iLpi); /* = 300 dpi */
+ req.offx = htons (pParams->iLeft); /* = 0cm */
+ req.offy = htons (pParams->iTop); /* = 0cm */
+ req.lenx = htons (pParams->iWidth); /* = 22.78cm */
+
+ /* Scan a few extra rows so we can colour offset properly. At 300dpi the
+ * difference is 4 lines */
+
+ req.leny = htons (pParams->iHeight); /* = 0.42cm */
+
+ req.flags1 = htons ((scantype == SCAN_TYPE_CALIBRATION) ? 0x0000 : 0x0080);
+ req.flags2 = htons ((scantype == SCAN_TYPE_CALIBRATION) ? 0x0010 :
+ (scantype == SCAN_TYPE_PREVIEW) ? 0x0000 :
+ /* SCAN_TYPE_NORMAL */ 0x0040);
+ req.flags3 = htons (0x18E8);
+
+ req.gamma[0] = htons (100);
+ req.gamma[1] = htons (100);
+ req.gamma[2] = htons (100);
+
+ if (Calibrate (pHWParams->iXferHandle, pParams->iDpi) != 0)
+ return -1;
+/* SetDefaultGamma( pHWParams->iXferHandle ); ** Must be done by caller */
+
+ HP5400_DBG (DBG_MSG, "Calibration complete\n");
+ ret =
+ InitScan2 (scantype, &req, pHWParams, &res, pParams->iColourOffset, 0x40);
+
+ HP5400_DBG (DBG_MSG, "InitScan2 returned %d\n", ret);
+
+ /* Pass the results back to the parent */
+ pParams->iBytesPerLine = htonl (res.xsize);
+
+ /* Hide the extra lines we're scanning */
+ pParams->iLines = htons (res.ysize);
+
+ return ret; /* 0 is good, -1 is bad */
+}
+
+/* For want of a better name ... */
+HP5400_SANE_STATIC
+int
+InitScan2 (enum ScanType scantype, struct ScanRequest *req,
+ THWParams * pHWParams, struct ScanResponse *result,
+ int iColourOffset, int code)
+{
+ struct ScanResponse res;
+ int iHandle = pHWParams->iXferHandle;
+
+ memset(&res, 0, sizeof(res));
+
+ /* Protect scanner from damage. This stops stpuid errors. It basically
+ * limits you to the scanner glass. Stuff like calibrations which need
+ * more access do it safely by fiddling other paramters. Note you can
+ * still break things by fiddling the ScanOffset, but that is not yet
+ * under user control */
+
+ if (scantype != SCAN_TYPE_CALIBRATION)
+ {
+ HP5400_DBG (DBG_MSG, "Off(%d,%d) : Len(%d,%d)\n", htons (req->offx),
+ htons (req->offy), htons (req->lenx), htons (req->leny));
+ /* Yes, all the htons() is silly but we want this check as late as possible */
+ if (htons (req->offx) > 0x09F8)
+ req->offx = htons (0x09F8);
+ if (htons (req->offy) > 0x0DB6)
+ req->offy = htons (0x0DB6);
+ /* These tests are meaningless as htons() returns unsigned
+ if( htons( req->offx ) < 0 ) req->offx = 0;
+ if( htons( req->offy ) < 0 ) req->offy = 0;
+ */
+ if (htons (req->offx) + htons (req->lenx) > 0x09F8)
+ req->lenx = htons (0x09F8 - htons (req->offx));
+ if (htons (req->offy) + htons (req->leny) > 0x0DB6)
+ req->leny = htons (0x0DB6 - htons (req->offy));
+ if (htons (req->lenx) <= 1)
+ return -1;
+ if (htons (req->leny) <= 1)
+ return -1;
+ }
+
+ WarmupLamp (iHandle);
+
+ { /* Try to set it to make scan succeed, URB 53 *//* 0x1B01 => 0x40 */
+ /* I think this tries to cancel any existing scan */
+ char flag = 0x40;
+ if (hp5400_command_write (iHandle, CMD_STOPSCAN, sizeof (flag), &flag) <
+ 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to cancel scan flag\n");
+ return -1;
+ }
+ }
+
+ { /* Try to set it to make scan succeed, URB 55 */
+ char data[4] = { 0x02, 0x03, 0x03, 0x3C };
+ if (hp5400_command_write (iHandle, CMD_UNKNOWN3, sizeof (data), data) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to set unknown1\n");
+ return -1;
+ }
+ }
+
+ { /* Try to set it to make scan succeed, URB 59 */
+ char flag = 0x04;
+ if (hp5400_command_write (iHandle, CMD_UNKNOWN2, sizeof (flag), &flag) <
+ 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to set unknown2\n");
+ return -1;
+ }
+ }
+
+ { /* URB 67 *//* Reference DPI = 300 currently */
+ short dpi = htons (HW_LPI);
+ if (hp5400_command_write (iHandle, CMD_SETDPI, sizeof (dpi), &dpi) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to set dpi\n");
+ return -1;
+ }
+ }
+
+ if (scantype != SCAN_TYPE_CALIBRATION)
+ { /* Setup scan offsets - Should only apply to non-calibration scans */
+ short offsets[2];
+
+ offsets [0] = htons (0x0054);
+ offsets [1] = htons (0x0282);
+
+ if (hp5400_command_write
+ (iHandle, CMD_SETOFFSET, sizeof (offsets), offsets) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to set offsets\n");
+ return -1;
+ }
+ }
+
+ HP5400_DBG (DBG_MSG, "Scan request: \n ");
+ {
+ size_t i;
+ for (i = 0; i < sizeof (*req); i++)
+ {
+ HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) req)[i]);
+ }
+ HP5400_DBG (DBG_MSG, "\n");
+ }
+
+ if (hp5400_command_write
+ (iHandle,
+ (scantype !=
+ SCAN_TYPE_CALIBRATION) ? CMD_SCANREQUEST2 : CMD_SCANREQUEST,
+ sizeof (*req), req) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to send scan request\n");
+ return -1;
+ }
+
+ { /* Try to set it to make scan succeed, URB 71 */
+ char flag = code; /* Start scan with light on or off as requested */
+ if (hp5400_command_write (iHandle, CMD_STARTSCAN, sizeof (flag), &flag) <
+ 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to set gamma flag\n");
+ return -1;
+ }
+ }
+
+ if (hp5400_command_read (iHandle, CMD_SCANRESPONSE, sizeof (res), &res) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to read scan response\n");
+ return -1;
+ }
+
+ HP5400_DBG (DBG_MSG, "Scan response: \n ");
+ {
+ size_t i;
+ for (i = 0; i < sizeof (res); i++)
+ {
+ HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) &res)[i]);
+ }
+ HP5400_DBG (DBG_MSG, "\n");
+ }
+
+ HP5400_DBG (DBG_MSG, "Bytes to transfer: %d\nBitmap resolution: %d x %d\n",
+ htonl (res.transfersize), htonl (res.xsize), htons (res.ysize));
+
+ HP5400_DBG (DBG_MSG, "Proceeding to scan\n");
+
+ if (htonl (res.transfersize) == 0)
+ {
+ HP5400_DBG (DBG_MSG, "Hmm, size is zero. Obviously a problem. Aborting...\n");
+ return -1;
+ }
+
+ {
+ char x1 = 0x14, x2 = 0x24;
+
+ float pixels = ((float) htons (req->lenx) * (float) htons (req->leny)) *
+ ((float) htons (req->dpix) * (float) htons (req->dpiy)) /
+ ((float) HW_LPI * (float) HW_LPI);
+ int bpp = (int) ((float) htonl (res.transfersize) / pixels + 0.5);
+ int planes = (bpp == 1) ? 1 : 3;
+ bpp /= planes;
+
+ HP5400_DBG (DBG_MSG, "bpp = %d / ( (%d * %d) * (%d * %d) / (%d * %d) ) = %d\n",
+ htonl (res.transfersize),
+ htons (req->lenx), htons (req->leny),
+ htons (req->dpix), htons (req->dpiy), HW_LPI, HW_LPI, bpp);
+
+ hp5400_command_write_noverify (iHandle, CMD_INITBULK1, &x1, 1);
+ hp5400_command_write_noverify (iHandle, CMD_INITBULK2, &x2, 1);
+
+ if (bpp > 2) /* Bug!! */
+ bpp = 2;
+
+ CircBufferInit (pHWParams->iXferHandle, &pHWParams->pipe,
+ htonl (res.xsize), bpp, iColourOffset, 0xF000,
+ htonl (res.transfersize) + 3 * htons (res.ysize));
+ }
+
+ if (result) /* copy ScanResult to parent if they asked for it */
+ memcpy (result, &res, sizeof (*result));
+
+ return 0;
+}
+
+HP5400_SANE_STATIC
+void
+FinishScan (THWParams * pHWParams)
+{
+ int iHandle = pHWParams->iXferHandle;
+
+ CircBufferExit (&pHWParams->pipe);
+
+ { /* Finish scan request */
+ char flag = 0x40;
+ if (hp5400_command_write (iHandle, CMD_STOPSCAN, sizeof (flag), &flag) <
+ 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to set gamma flag\n");
+ return;
+ }
+ }
+}
+
+HP5400_SANE_STATIC
+int
+HP5400Open (THWParams * params, const char *filename)
+{
+ int iHandle = hp5400_open (filename);
+ char szVersion[32];
+ int i;
+#ifndef NO_STRING_VERSION_MATCH
+ int versionMatched;
+#endif
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_MSG, "hp5400_open failed\n");
+ return -1;
+ }
+
+ params->iXferHandle = 0;
+
+ /* read version info */
+ if (hp5400_command_read
+ (iHandle, CMD_GETVERSION, sizeof (szVersion), szVersion) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to read version string\n");
+ goto hp5400_close_exit;
+ }
+
+ HP5400_DBG (DBG_MSG, "version String :\n");
+ for (i=0; i < 32; i++) {
+ HP5400_DBG (DBG_MSG, "%c\n", szVersion[i]);
+ }
+ HP5400_DBG (DBG_MSG, "\n");
+
+#ifndef NO_STRING_VERSION_MATCH
+ i = 0;
+ versionMatched = 0;
+ while ( !versionMatched && (i < numVersions) ) {
+ if (!strncmp (szVersion + 1, MatchVersions[i] .strVersion, strlen(MatchVersions[i] .strVersion) - 4)) {
+ versionMatched = 1;
+ }
+ i++;
+ }
+ if ( !versionMatched ) {
+ HP5400_DBG (DBG_MSG,
+ "Sorry, unknown scanner version. Attempted match on :\n");
+ i = 0;
+ while ( i < numVersions ) {
+ HP5400_DBG (DBG_MSG, "* '%s'\n", MatchVersions[i] .strVersion);
+ i++;
+ }
+ HP5400_DBG (DBG_MSG, "Version is '%s'\n", szVersion);
+ goto hp5400_close_exit;
+ }
+#else
+ HP5400_DBG (DBG_MSG, "Warning, Version match is disabled. Version is '%s'\n",
+ szVersion);
+#endif /* NO_STRING_VERSION_MATCH */
+
+ params->iXferHandle = iHandle;
+
+ /* Start the lamp warming up */
+ /* No point checking the return value, we don't care anyway */
+ WriteByte (iHandle, 0x0000, 0x01);
+
+ /* Success */
+ return 0;
+
+hp5400_close_exit:
+ hp5400_close (iHandle);
+ return -1;
+}
+
+HP5400_SANE_STATIC
+void
+HP5400Close (THWParams * params)
+{
+ hp5400_close (params->iXferHandle);
+}
+
+HP5400_SANE_STATIC
+int
+HP5400Detect (const char *filename,
+ int (*_ReportDevice) (TScannerModel * pModel,
+ const char *pszDeviceName))
+{
+ int iHandle = hp5400_open (filename);
+
+ char szVersion[32];
+ int ret = 0;
+#ifndef NO_STRING_VERSION_MATCH
+ int versionMatched = 0;
+ int i = 0;
+#endif
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_MSG, "hp5400_open failed\n");
+ return -1;
+ }
+
+ /* read version info */
+ if (hp5400_command_read
+ (iHandle, CMD_GETVERSION, sizeof (szVersion), szVersion) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "failed to read version string\n");
+ ret = -1;
+ goto hp5400_close_exit;
+ }
+
+#ifndef NO_STRING_VERSION_MATCH
+ i = 0;
+ versionMatched = 0;
+ while ( !versionMatched && (i < numVersions) ) {
+ if (!memcmp (szVersion + 1, MatchVersions[i] .strVersion, strlen (MatchVersions[i] .strVersion) - 4)) {
+ versionMatched = 1;
+ }
+ i++;
+ }
+ if ( !versionMatched ) {
+ HP5400_DBG (DBG_MSG,
+ "Sorry, unknown scanner version. Attempted match on :\n");
+ i = 0;
+ while ( i < numVersions ) {
+ HP5400_DBG (DBG_MSG, "* '%s'\n", MatchVersions[i] .strVersion);
+ i++;
+ }
+ HP5400_DBG (DBG_MSG, "Version is '%s'\n", szVersion);
+ goto hp5400_close_exit;
+ }
+#else
+ HP5400_DBG (DBG_MSG, "Warning, Version match is disabled. Version is '%s'\n",
+ szVersion);
+#endif /* NO_STRING_VERSION_MATCH */
+
+ if (_ReportDevice)
+ _ReportDevice (&Model_HP54xx, filename);
+
+hp5400_close_exit:
+ hp5400_close (iHandle);
+ return ret;
+}
+
+#ifdef STANDALONE
+int
+main (int argc, char *argv[])
+{
+ THWParams scanner;
+
+ assert (sizeof (struct ScanRequest) == 32);
+ assert (sizeof (struct ScanResponse) == 16);
+
+ hp5400_dbg_start();
+
+ HP5400_DBG (DBG_MSG,
+ "HP5400/5470C sample scan utility, by Martijn van Oosterhout <kleptog@svana.org>\n");
+ HP5400_DBG (DBG_MSG,
+ "Based on the testutils by Bertrik Sikken (bertrik@zonnet.nl)\n");
+
+ if ((argc == 6) && (!strcmp (argv[1], "-decode")))
+ {
+ int width = atoi (argv[3]);
+ int height = atoi (argv[4]);
+ FILE *temp = fopen (argv[2], "r+b");
+ if (temp)
+ {
+ int planes = 3;
+ int bpp = 1;
+ fseek (temp, 0, SEEK_SET);
+ DecodeImage (temp, planes, bpp, planes * bpp * width, height,
+ argv[5]);
+ fclose (temp);
+ }
+ return 1;
+ }
+
+ if (HP5400Open (&scanner, NULL) < 0)
+ {
+ return 1;
+ }
+
+ PreviewScan (scanner.iXferHandle);
+
+ HP5400Close (&scanner);
+ fprintf (stderr, "Note: output is in output.ppm\n");
+ return 0;
+}
+
+#endif
diff --git a/backend/hp5400_internal.h b/backend/hp5400_internal.h
new file mode 100644
index 0000000..981ce0b
--- /dev/null
+++ b/backend/hp5400_internal.h
@@ -0,0 +1,276 @@
+#ifndef _HP5400_INTERNAL_H_
+#define _HP5400_INTERNAL_H_
+
+/* sane - Scanner Access Now Easy.
+ (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+ (c) 2003 Martijn van Oosterhout, kleptog@svana.org
+ (c) 2002 Bertrik Sikken, bertrik@zonnet.nl
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ HP5400/5470 Test util.
+ Currently is only able to read back the scanner version string,
+ but this basically demonstrates ability to communicate with the scanner.
+
+ Massively expanded. Can do calibration scan, upload gamma and calibration
+ tables and stores the results of a scan. - 19/02/2003 Martijn
+*/
+
+#include "../include/_stdint.h"
+
+#ifdef __GNUC__
+#define PACKED __attribute__ ((packed))
+#else
+#define PACKED
+#endif
+
+/* If this is enabled, a copy of the raw data from the scanner will be saved to
+ imagedebug.dat and the attempted conversion to imagedebug.ppm */
+/* #define IMAGE_DEBUG */
+
+/* If this is defined you get extra info on the calibration */
+/* #define CALIB_DEBUG */
+
+#define CMD_GETVERSION 0x1200
+#define CMD_GETUITEXT 0xf00b
+#define CMD_GETCMDID 0xc500
+#define CMD_SCANREQUEST 0x2505 /* This is for previews */
+#define CMD_SCANREQUEST2 0x2500 /* This is for real scans */
+#define CMD_SCANRESPONSE 0x3400
+
+/* Testing stuff to make it work */
+#define CMD_SETDPI 0x1500 /* ??? */
+#define CMD_STOPSCAN 0x1B01 /* 0x40 = lamp in on, 0x00 = lamp is off */
+#define CMD_STARTSCAN 0x1B05 /* 0x40 = lamp in on, 0x00 = lamp is off */
+#define CMD_UNKNOWN 0x2300 /* Send fixed string */
+#define CMD_UNKNOWN2 0xD600 /* ??? Set to 0x04 */
+#define CMD_UNKNOWN3 0xC000 /* ??? Set to 02 03 03 3C */
+#define CMD_SETOFFSET 0xE700 /* two ints in network order. X-offset, Y-offset of full scan */
+ /* Given values seem to be 0x0054 (=4.57mm) and 0x0282 (=54.36mm) */
+
+#define CMD_INITBULK1 0x0087 /* send 0x14 */
+#define CMD_INITBULK2 0x0083 /* send 0x24 */
+#define CMD_INITBULK3 0x0082 /* transfer length 0xf000 */
+
+
+struct ScanRequest
+{
+ uint8_t x1; /* Set to 0x08 */
+ uint16_t dpix, dpiy; /* Set to 75, 150 or 300 in network order */
+ uint16_t offx, offy; /* Offset to scan, in 1/300th of dpi, in network order */
+ uint16_t lenx, leny; /* Size of scan, in 1/300th of dpi, in network order */
+ uint16_t flags1, flags2, flags3; /* Undetermined flag info */
+ /* Known combinations are:
+ 1st calibration scan: 0x0000, 0x0010, 0x1820 = 24bpp
+ 2nd calibration scan: 0x0000, 0x0010, 0x3020 = 48bpp ???
+ 3rd calibration scan: 0x0000, 0x0010, 0x3024 = 48bpp ???
+ Preview scan: 0x0080, 0x0000, 0x18E8 = 8bpp
+ 4th & 5th like 2nd and 3rd
+ B&W scan: 0x0080, 0x0040, 0x08E8 = 8bpp
+ 6th & 7th like 2nd and 3rd
+ True colour scan 0x0080, 0x0040, 0x18E8 = 24bpp
+ */
+ uint8_t zero; /* Seems to always be zero */
+ uint16_t gamma[3]; /* Set to 100 in network order. Gamma? */
+ uint16_t pad[3]; /* Zero padding ot 32 bytes??? */
+}
+PACKED;
+
+ /* More known combos (All 24-bit):
+ 300 x 300 light calibration: 0x0000, 0x0010, 0x1820
+ 300 x 300 dark calibration: 0x0000, 0x0010, 0x3024
+ 75 x 75 preview scan: 0x0080, 0x0000, 0x18E8
+ 300 x 300 full scan: 0x0080, 0x0000, 0x18E8
+ 600 x 300 light calibration: 0x0000, 0x0010, 0x3000
+ 600 x 300 dark calibration: 0x0000, 0x0010, 0x3004
+ 600 x 600 full scan: 0x0080, 0x0000, 0x18C8
+ 1200 x 300 light calibration: 0x0000, 0x0010, 0x3000
+ 1200 x 300 dark calibration: 0x0000, 0x0010, 0x3004
+ 1200 x 1200 full scan: 0x0080, 0x0000, 0x18C8
+ 2400 x 300 light calibration: 0x0000, 0x0010, 0x3000
+ 2400 x 300 dark calibration: 0x0000, 0x0010, 0x3004
+ 2400 x 2400 full scan: 0x0080, 0x0000, 0x18C0
+ */
+
+struct ScanResponse
+{
+ uint16_t x1; /* Usually 0x0000 or 0x4000 */
+ uint32_t transfersize; /* Number of bytes to be transferred */
+ uint32_t xsize; /* Shape of returned bitmap */
+ uint16_t ysize; /* Why does the X get more bytes? */
+ uint16_t pad[2]; /* Zero padding to 16 bytes??? */
+}
+PACKED;
+
+HP5400_SANE_STATIC
+int
+InitScan2 (enum ScanType type, struct ScanRequest *req,
+ THWParams * pHWParams, struct ScanResponse *res,
+ int iColourOffset, int code);
+
+HP5400_SANE_STATIC
+void
+FinishScan (THWParams * pHWParams);
+
+HP5400_SANE_STATIC
+int
+WriteByte (int iHandle, int cmd, char data);
+
+HP5400_SANE_STATIC
+int
+SetLamp (THWParams * pHWParams, int fLampOn);
+
+HP5400_SANE_STATIC
+int
+WarmupLamp (int iHandle);
+
+HP5400_SANE_STATIC
+int
+SetCalibration (int iHandle, int numPixels,
+ unsigned int *low_vals[3],
+ unsigned int *high_vals[3], int dpi);
+
+HP5400_SANE_STATIC
+void
+WriteGammaCalibTable (int iHandle, const int *pabGammaR,
+ const int *pabGammaG,
+ const int *pabGammaB);
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+void
+SetDefaultGamma (int iHandle);
+#endif
+
+HP5400_SANE_STATIC
+void
+CircBufferInit (int iHandle, TDataPipe * p, int iBytesPerLine,
+ int bpp, int iMisAlignment, int blksize,
+ int iTransferSize);
+
+HP5400_SANE_STATIC
+int
+CircBufferGetLine (int iHandle, TDataPipe * p, void *pabLine);
+
+HP5400_SANE_STATIC
+void
+CircBufferExit (TDataPipe * p);
+
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+void
+DecodeImage (FILE * file, int planes, int bpp, int xsize, int ysize,
+ const char *filename);
+
+HP5400_SANE_STATIC
+int
+hp5400_test_scan_response (struct ScanResponse *resp,
+ struct ScanRequest *req);
+#endif
+
+
+HP5400_SANE_STATIC
+int
+DoAverageScan (int iHandle, struct ScanRequest *req, int code,
+ unsigned int **array);
+
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+int
+DoScan (int iHandle, struct ScanRequest *req, const char *filename, int code,
+ struct ScanResponse *res);
+#endif
+
+HP5400_SANE_STATIC
+int
+Calibrate (int iHandle, int dpi);
+
+#ifdef STANDALONE
+HP5400_SANE_STATIC
+int
+hp5400_scan (int iHandle, TScanParams * params, THWParams * pHWParams,
+ const char *filename);
+
+HP5400_SANE_STATIC
+int
+PreviewScan (int iHandle);
+
+HP5400_SANE_STATIC
+int
+InitScanner (int iHandle);
+#endif
+
+HP5400_SANE_STATIC
+int
+InitScan (enum ScanType scantype, TScanParams * pParams,
+ THWParams * pHWParams);
+
+HP5400_SANE_STATIC
+void
+FinishScan (THWParams * pHWParams);
+
+HP5400_SANE_STATIC
+int
+HP5400Open (THWParams * params, const char *filename);
+
+HP5400_SANE_STATIC
+void
+HP5400Close (THWParams * params);
+
+HP5400_SANE_STATIC
+int
+HP5400Detect (const char *filename,
+ int (*_ReportDevice) (TScannerModel * pModel,
+ const char *pszDeviceName));
+
+
+HP5400_SANE_STATIC
+int
+InitHp5400_internal( void );
+
+HP5400_SANE_STATIC
+int
+FreeHp5400_internal( void );
+
+
+#ifdef STANDALONE
+int
+main (int argc, char *argv[]);
+#endif
+
+#endif
diff --git a/backend/hp5400_sane.c b/backend/hp5400_sane.c
new file mode 100644
index 0000000..e5fdf43
--- /dev/null
+++ b/backend/hp5400_sane.c
@@ -0,0 +1,1022 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+
+/*
+ SANE interface for hp54xx scanners. Prototype.
+ Parts of this source were inspired by other backends.
+*/
+
+#include "../include/sane/config.h"
+
+/* definitions for debug */
+#include "hp5400_debug.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memcpy */
+#include <stdio.h>
+#include <errno.h>
+
+#define HP5400_CONFIG_FILE "hp5400.conf"
+
+#include "hp5400.h"
+
+/* includes for data transfer methods */
+#include "hp5400.h"
+
+#ifdef STANDALONE
+#include "hp5400_scanner.h"
+#endif
+
+#if defined(LINUX_USB_SUPPORT)
+ #include "hp5400_linux.c"
+#endif
+#if defined(USCANNER_SUPPORT)
+ #include "hp5400_uscanner.c"
+#endif
+#if defined(LIBUSB_SUPPORT)
+ #include "hp5400_libusb.c"
+#endif
+#if defined(LIBIEEE1284_SUPPORT)
+ #include "hp5400_ieee1284.c"
+#endif
+
+
+
+/* other definitions */
+#ifndef min
+#define min(A,B) (((A)<(B)) ? (A) : (B))
+#endif
+#ifndef max
+#define max(A,B) (((A)>(B)) ? (A) : (B))
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4)
+#define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_))
+
+#define NUM_GAMMA_ENTRIES 65536
+
+
+/* options enumerator */
+typedef enum
+{
+ optCount = 0,
+
+ optGroupGeometry,
+ optTLX, optTLY, optBRX, optBRY,
+ optDPI,
+
+ optGroupImage,
+
+ optGammaTableRed, /* Gamma Tables */
+ optGammaTableGreen,
+ optGammaTableBlue,
+
+ optLast, /* Disable the offset code */
+
+ optGroupMisc,
+ optOffsetX, optOffsetY
+
+
+/* put temporarily disabled options here after optLast */
+/*
+ optLamp,
+*/
+
+}
+EOptionIndex;
+
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+}
+TOptionValue;
+
+
+typedef struct
+{
+ SANE_Option_Descriptor aOptions[optLast];
+ TOptionValue aValues[optLast];
+
+ TScanParams ScanParams;
+ THWParams HWParams;
+
+ TDataPipe DataPipe;
+ int iLinesLeft;
+
+ SANE_Int *aGammaTableR; /* a 16-to-16 bit color lookup table */
+ SANE_Int *aGammaTableG; /* a 16-to-16 bit color lookup table */
+ SANE_Int *aGammaTableB; /* a 16-to-16 bit color lookup table */
+
+ int fScanning; /* TRUE if actively scanning */
+ int fCanceled;
+}
+TScanner;
+
+
+/* linked list of SANE_Device structures */
+typedef struct TDevListEntry
+{
+ struct TDevListEntry *pNext;
+ SANE_Device dev;
+ char* devname;
+}
+TDevListEntry;
+
+
+
+/* Device filename for USB access */
+char usb_devfile[128];
+
+static TDevListEntry *_pFirstSaneDev = 0;
+static int iNumSaneDev = 0;
+
+
+static const SANE_Device **_pSaneDevList = 0;
+
+/* option constraints */
+static const SANE_Range rangeGammaTable = {0, 65535, 1};
+#ifdef SUPPORT_2400_DPI
+static const SANE_Int setResolutions[] = {6, 75, 150, 300, 600, 1200, 2400};
+#else
+static const SANE_Int setResolutions[] = {5, 75, 150, 300, 600, 1200};
+#endif
+static const SANE_Range rangeXmm = {0, 220, 1};
+static const SANE_Range rangeYmm = {0, 300, 1};
+static const SANE_Range rangeXoffset = {0, 20, 1};
+static const SANE_Range rangeYoffset = {0, 70, 1};
+static const SANE_Int offsetX = 5;
+static const SANE_Int offsetY = 52;
+
+
+static void _InitOptions(TScanner *s)
+{
+ int i, j;
+ SANE_Option_Descriptor *pDesc;
+ TOptionValue *pVal;
+
+ /* set a neutral gamma */
+ if( s->aGammaTableR == NULL ) /* Not yet allocated */
+ {
+ s->aGammaTableR = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) );
+ s->aGammaTableG = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) );
+ s->aGammaTableB = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) );
+
+ for (j = 0; j < NUM_GAMMA_ENTRIES; j++) {
+ s->aGammaTableR[j] = j;
+ s->aGammaTableG[j] = j;
+ s->aGammaTableB[j] = j;
+ }
+ }
+
+ for (i = optCount; i < optLast; i++) {
+
+ pDesc = &s->aOptions[i];
+ pVal = &s->aValues[i];
+
+ /* defaults */
+ pDesc->name = "";
+ pDesc->title = "";
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof(SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = 0;
+
+ switch (i) {
+
+ case optCount:
+ pDesc->title = SANE_TITLE_NUM_OPTIONS;
+ pDesc->desc = SANE_DESC_NUM_OPTIONS;
+ pDesc->cap = SANE_CAP_SOFT_DETECT;
+ pVal->w = (SANE_Word)optLast;
+ break;
+
+ case optGroupGeometry:
+ pDesc->title = "Geometry";
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->size = 0;
+ break;
+
+ case optTLX:
+ pDesc->name = SANE_NAME_SCAN_TL_X;
+ pDesc->title = SANE_TITLE_SCAN_TL_X;
+ pDesc->desc = SANE_DESC_SCAN_TL_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeXmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = rangeXmm.min + offsetX;
+ break;
+
+ case optTLY:
+ pDesc->name = SANE_NAME_SCAN_TL_Y;
+ pDesc->title = SANE_TITLE_SCAN_TL_Y;
+ pDesc->desc = SANE_DESC_SCAN_TL_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeYmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = rangeYmm.min + offsetY;
+ break;
+
+ case optBRX:
+ pDesc->name = SANE_NAME_SCAN_BR_X;
+ pDesc->title = SANE_TITLE_SCAN_BR_X;
+ pDesc->desc = SANE_DESC_SCAN_BR_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeXmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = rangeXmm.max + offsetX;
+ break;
+
+ case optBRY:
+ pDesc->name = SANE_NAME_SCAN_BR_Y;
+ pDesc->title = SANE_TITLE_SCAN_BR_Y;
+ pDesc->desc = SANE_DESC_SCAN_BR_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeYmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = rangeYmm.max + offsetY;
+ break;
+
+ case optDPI:
+ pDesc->name = SANE_NAME_SCAN_RESOLUTION;
+ pDesc->title = SANE_TITLE_SCAN_RESOLUTION;
+ pDesc->desc = SANE_DESC_SCAN_RESOLUTION;
+ pDesc->unit = SANE_UNIT_DPI;
+ pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pDesc->constraint.word_list = setResolutions;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = setResolutions[1];
+ break;
+
+ case optGroupImage:
+ pDesc->title = SANE_I18N("Image");
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->size = 0;
+ break;
+
+ case optGammaTableRed:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_R;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_R;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_R;
+ pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int );
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeGammaTable;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = s->aGammaTableR;
+ break;
+
+ case optGammaTableGreen:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_G;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_G;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_G;
+ pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int );
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeGammaTable;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = s->aGammaTableG;
+ break;
+
+ case optGammaTableBlue:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_B;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_B;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_B;
+ pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int );
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeGammaTable;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = s->aGammaTableB;
+ break;
+
+ case optGroupMisc:
+ pDesc->title = SANE_I18N("Miscellaneous");
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->size = 0;
+ break;
+
+ case optOffsetX:
+ pDesc->title = SANE_I18N("offset X");
+ pDesc->desc = SANE_I18N("Hardware internal X position of the scanning area.");
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeXoffset;
+ pDesc->cap = SANE_CAP_SOFT_SELECT;
+ pVal->w = offsetX;
+ break;
+
+ case optOffsetY:
+ pDesc->title = SANE_I18N("offset Y");
+ pDesc->desc = SANE_I18N("Hardware internal Y position of the scanning area.");
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeYoffset;
+ pDesc->cap = SANE_CAP_SOFT_SELECT;
+ pVal->w = offsetY;
+ break;
+
+
+#if 0
+ case optLamp:
+ pDesc->name = "lamp";
+ pDesc->title = SANE_I18N("Lamp status");
+ pDesc->desc = SANE_I18N("Switches the lamp on or off.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ /* switch the lamp on when starting for first the time */
+ pVal->w = SANE_TRUE;
+ break;
+#endif
+#if 0
+ case optCalibrate:
+ pDesc->name = "calibrate";
+ pDesc->title = SANE_I18N("Calibrate");
+ pDesc->desc = SANE_I18N("Calibrates for black and white level.");
+ pDesc->type = SANE_TYPE_BUTTON;
+ pDesc->cap = SANE_CAP_SOFT_SELECT;
+ pDesc->size = 0;
+ break;
+#endif
+ default:
+ HP5400_DBG(DBG_ERR, "Uninitialised option %d\n", i);
+ break;
+ }
+ }
+}
+
+
+static int _ReportDevice(TScannerModel *pModel, const char *pszDeviceName)
+{
+ TDevListEntry *pNew, *pDev;
+
+ HP5400_DBG(DBG_MSG, "hp5400: _ReportDevice '%s'\n", pszDeviceName);
+
+ pNew = malloc(sizeof(TDevListEntry));
+ if (!pNew) {
+ HP5400_DBG(DBG_ERR, "no mem\n");
+ return -1;
+ }
+
+ /* add new element to the end of the list */
+ if (_pFirstSaneDev == NULL) {
+ _pFirstSaneDev = pNew;
+ }
+ else {
+ for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext) {
+ ;
+ }
+ pDev->pNext = pNew;
+ }
+
+ /* fill in new element */
+ pNew->pNext = 0;
+ /* we use devname to avoid having to free a const
+ * pointer */
+ pNew->devname = (char*)strdup(pszDeviceName);
+ pNew->dev.name = pNew->devname;
+ pNew->dev.vendor = pModel->pszVendor;
+ pNew->dev.model = pModel->pszName;
+ pNew->dev.type = "flatbed scanner";
+
+ iNumSaneDev++;
+
+ return 0;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ const char * filename = (const char*) devname;
+ if (HP5400Detect (filename, _ReportDevice) < 0)
+ {
+ HP5400_DBG (DBG_MSG, "attach_one_device: couldn't attach %s\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ HP5400_DBG (DBG_MSG, "attach_one_device: attached %s successfully\n", devname);
+ return SANE_STATUS_GOOD;
+}
+
+
+/*****************************************************************************/
+
+SANE_Status
+sane_init (SANE_Int * piVersion, SANE_Auth_Callback pfnAuth)
+{
+ FILE *conf_fp; /* Config file stream */
+ SANE_Char line[PATH_MAX];
+ SANE_Char *str = NULL;
+ SANE_String_Const proper_str;
+ int nline = 0;
+
+ /* prevent compiler from complaing about unused parameters */
+ pfnAuth = pfnAuth;
+
+ strcpy(usb_devfile, "/dev/usb/scanner0");
+ _pFirstSaneDev = 0;
+ iNumSaneDev = 0;
+
+ InitHp5400_internal();
+
+
+ DBG_INIT ();
+
+ HP5400_DBG (DBG_MSG, "sane_init: SANE hp5400 backend version %d.%d-%d (from %s)\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ sanei_usb_init ();
+
+ conf_fp = sanei_config_open (HP5400_CONFIG_FILE);
+
+ iNumSaneDev = 0;
+
+ if (conf_fp)
+ {
+ HP5400_DBG (DBG_MSG, "Reading config file\n");
+
+ while (sanei_config_read (line, sizeof (line), conf_fp))
+ {
+ ++nline;
+
+ if (str)
+ {
+ free (str);
+ }
+
+ proper_str = sanei_config_get_string (line, &str);
+
+ /* Discards white lines and comments */
+ if (!str || proper_str == line || str[0] == '#')
+ {
+ HP5400_DBG (DBG_MSG, "Discarding line %d\n", nline);
+ }
+ else
+ {
+ /* If line's not blank or a comment, then it's the device
+ * filename or a usb directive. */
+ HP5400_DBG (DBG_MSG, "Trying to attach %s\n", line);
+ sanei_usb_attach_matching_devices (line, attach_one_device);
+ }
+ } /* while */
+ fclose (conf_fp);
+ }
+ else
+ {
+ HP5400_DBG (DBG_ERR, "Unable to read config file \"%s\": %s\n",
+ HP5400_CONFIG_FILE, strerror (errno));
+ HP5400_DBG (DBG_MSG, "Using default built-in values\n");
+ attach_one_device (usb_devfile);
+ }
+
+ if (piVersion != NULL)
+ {
+ *piVersion = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_exit (void)
+{
+ TDevListEntry *pDev, *pNext;
+ HP5400_DBG (DBG_MSG, "sane_exit\n");
+
+ /* free device list memory */
+ if (_pSaneDevList)
+ {
+ for (pDev = _pFirstSaneDev; pDev; pDev = pNext)
+ {
+ pNext = pDev->pNext;
+ free (pDev->devname);
+ /* pDev->dev.name is the same pointer that pDev->devname */
+ free (pDev);
+ }
+ _pFirstSaneDev = 0;
+ free (_pSaneDevList);
+ _pSaneDevList = 0;
+ }
+
+
+ FreeHp5400_internal();
+}
+
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ TDevListEntry *pDev;
+ int i;
+
+ HP5400_DBG (DBG_MSG, "sane_get_devices\n");
+
+ local_only = local_only;
+
+ if (_pSaneDevList)
+ {
+ free (_pSaneDevList);
+ }
+
+ _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1));
+ if (!_pSaneDevList)
+ {
+ HP5400_DBG (DBG_MSG, "no mem\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ i = 0;
+ for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext)
+ {
+ _pSaneDevList[i++] = &pDev->dev;
+ }
+ _pSaneDevList[i++] = 0; /* last entry is 0 */
+
+ *device_list = _pSaneDevList;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ TScanner *s;
+
+ HP5400_DBG (DBG_MSG, "sane_open: %s\n", name);
+
+ /* check the name */
+ if (strlen (name) == 0)
+ {
+ /* default to first available device */
+ name = _pFirstSaneDev->dev.name;
+ }
+
+ s = malloc (sizeof (TScanner));
+ if (!s)
+ {
+ HP5400_DBG (DBG_MSG, "malloc failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (s, 0, sizeof (TScanner)); /* Clear everything to zero */
+ if (HP5400Open (&s->HWParams, name) < 0)
+ {
+ /* is this OK ? */
+ HP5400_DBG (DBG_ERR, "HP5400Open failed\n");
+ free ((void *) s);
+ return SANE_STATUS_INVAL; /* is this OK? */
+ }
+ HP5400_DBG (DBG_MSG, "Handle=%d\n", s->HWParams.iXferHandle);
+ _InitOptions (s);
+ *h = s;
+
+ /* Turn on lamp by default at startup */
+/* SetLamp(&s->HWParams, TRUE); */
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle h)
+{
+ TScanner *s;
+
+ HP5400_DBG (DBG_MSG, "sane_close\n");
+
+ s = (TScanner *) h;
+
+ /* turn of scanner lamp */
+ SetLamp (&s->HWParams, FALSE);
+
+ /* close scanner */
+ HP5400Close (&s->HWParams);
+
+ /* free scanner object memory */
+ free ((void *) s);
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ TScanner *s;
+
+ HP5400_DBG (DBG_MSG, "sane_get_option_descriptor %d\n", n);
+
+ if ((n < optCount) || (n >= optLast))
+ {
+ return NULL;
+ }
+
+ s = (TScanner *) h;
+ return &s->aOptions[n];
+}
+
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action,
+ void *pVal, SANE_Int * pInfo)
+{
+ TScanner *s;
+ SANE_Int info;
+
+ HP5400_DBG (DBG_MSG, "sane_control_option: option %d, action %d\n", n, Action);
+
+ s = (TScanner *) h;
+ info = 0;
+
+ switch (Action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ switch (n)
+ {
+
+ /* Get options of type SANE_Word */
+ case optBRX:
+ case optTLX:
+ *(SANE_Word *) pVal = s->aValues[n].w; /* Not needed anymore - s->aValues[optOffsetX].w; */
+ HP5400_DBG (DBG_MSG,
+ "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
+ *(SANE_Word *) pVal);
+ break;
+
+ case optBRY:
+ case optTLY:
+ *(SANE_Word *) pVal = s->aValues[n].w; /* Not needed anymore - - s->aValues[optOffsetY].w; */
+ HP5400_DBG (DBG_MSG,
+ "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
+ *(SANE_Word *) pVal);
+ break;
+
+ case optOffsetX:
+ case optOffsetY:
+ case optCount:
+ case optDPI:
+ HP5400_DBG (DBG_MSG,
+ "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
+ (int) s->aValues[n].w);
+ *(SANE_Word *) pVal = s->aValues[n].w;
+ break;
+
+ /* Get options of type SANE_Word array */
+ case optGammaTableRed:
+ case optGammaTableGreen:
+ case optGammaTableBlue:
+ HP5400_DBG (DBG_MSG, "Reading gamma table\n");
+ memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size);
+ break;
+
+#if 0
+ /* Get options of type SANE_Bool */
+ case optLamp:
+ GetLamp (&s->HWParams, &fLampIsOn);
+ *(SANE_Bool *) pVal = fLampIsOn;
+ break;
+#endif
+#if 0
+ case optCalibrate:
+ /* although this option has nothing to read,
+ it's added here to avoid a warning when running scanimage --help */
+ break;
+#endif
+ default:
+ HP5400_DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n);
+ }
+ break;
+
+
+ case SANE_ACTION_SET_VALUE:
+ if (s->fScanning)
+ {
+ HP5400_DBG (DBG_ERR,
+ "sane_control_option: SANE_ACTION_SET_VALUE not allowed during scan\n");
+ return SANE_STATUS_INVAL;
+ }
+ switch (n)
+ {
+
+ case optCount:
+ return SANE_STATUS_INVAL;
+ break;
+
+ case optBRX:
+ case optTLX:
+ info |= SANE_INFO_RELOAD_PARAMS;
+ s->ScanParams.iLines = 0; /* Forget actual image settings */
+ s->aValues[n].w = *(SANE_Word *) pVal; /* Not needed anymore - + s->aValues[optOffsetX].w; */
+ break;
+
+ case optBRY:
+ case optTLY:
+ info |= SANE_INFO_RELOAD_PARAMS;
+ s->ScanParams.iLines = 0; /* Forget actual image settings */
+ s->aValues[n].w = *(SANE_Word *) pVal; /* Not needed anymore - + s->aValues[optOffsetY].w; */
+ break;
+ case optDPI:
+ info |= SANE_INFO_RELOAD_PARAMS;
+ s->ScanParams.iLines = 0; /* Forget actual image settings */
+#ifdef SUPPORT_2400_DPI
+ (s->aValues[n].w) = *(SANE_Word *) pVal;
+#else
+ (s->aValues[n].w) = min (1200, *(SANE_Word *) pVal);
+#endif
+ break;
+
+ case optGammaTableRed:
+ case optGammaTableGreen:
+ case optGammaTableBlue:
+ HP5400_DBG (DBG_MSG, "Writing gamma table\n");
+ memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size);
+ break;
+/*
+ case optLamp:
+ fVal = *(SANE_Bool *)pVal;
+ HP5400_DBG(DBG_MSG, "lamp %s\n", fVal ? "on" : "off");
+ SetLamp(&s->HWParams, fVal);
+ break;
+*/
+#if 0
+ case optCalibrate:
+/* SimpleCalib(&s->HWParams); */
+ break;
+#endif
+ default:
+ HP5400_DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n);
+ }
+ if (pInfo != NULL)
+ {
+ *pInfo = info;
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_UNSUPPORTED;
+
+
+ default:
+ HP5400_DBG (DBG_ERR, "Invalid action (%d)\n", Action);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ TScanner *s;
+ HP5400_DBG (DBG_MSG, "sane_get_parameters\n");
+
+ s = (TScanner *) h;
+
+ /* first do some checks */
+ if (s->aValues[optTLX].w >= s->aValues[optBRX].w)
+ {
+ HP5400_DBG (DBG_ERR, "TLX should be smaller than BRX\n");
+ return SANE_STATUS_INVAL; /* proper error code? */
+ }
+ if (s->aValues[optTLY].w >= s->aValues[optBRY].w)
+ {
+ HP5400_DBG (DBG_ERR, "TLY should be smaller than BRY\n");
+ return SANE_STATUS_INVAL; /* proper error code? */
+ }
+
+ /* return the data */
+ p->format = SANE_FRAME_RGB;
+ p->last_frame = SANE_TRUE;
+
+ p->depth = 8;
+ if (s->ScanParams.iLines) /* Initialised by doing a scan */
+ {
+ p->pixels_per_line = s->ScanParams.iBytesPerLine / 3;
+ p->lines = s->ScanParams.iLines;
+ p->bytes_per_line = s->ScanParams.iBytesPerLine;
+ }
+ else
+ {
+ p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w,
+ s->aValues[optDPI].w);
+ p->pixels_per_line =
+ MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w,
+ s->aValues[optDPI].w);
+ p->bytes_per_line = p->pixels_per_line * 3;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#define BUFFER_READ_HEADER_SIZE 32
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ TScanner *s;
+ SANE_Parameters par;
+
+ HP5400_DBG (DBG_MSG, "sane_start\n");
+
+ s = (TScanner *) h;
+
+ if (sane_get_parameters (h, &par) != SANE_STATUS_GOOD)
+ {
+ HP5400_DBG (DBG_MSG, "Invalid scan parameters (sane_get_parameters)\n");
+ return SANE_STATUS_INVAL;
+ }
+ s->iLinesLeft = par.lines;
+
+ /* fill in the scanparams using the option values */
+ s->ScanParams.iDpi = s->aValues[optDPI].w;
+ s->ScanParams.iLpi = s->aValues[optDPI].w;
+
+ /* Guessing here. 75dpi => 1, 2400dpi => 32 */
+ /* s->ScanParams.iColourOffset = s->aValues[optDPI].w / 75; */
+ /* now we don't need correction => corrected by scan request type ? */
+ s->ScanParams.iColourOffset = 0;
+
+ s->ScanParams.iTop =
+ MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, HW_LPI);
+ s->ScanParams.iLeft =
+ MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI);
+
+ /* Note: All measurements passed to the scanning routines must be in HW_LPI */
+ s->ScanParams.iWidth =
+ MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w, HW_LPI);
+ s->ScanParams.iHeight =
+ MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w, HW_LPI);
+
+ /* After the scanning, the iLines and iBytesPerLine will be filled in */
+
+ /* copy gamma table */
+ WriteGammaCalibTable (s->HWParams.iXferHandle, s->aGammaTableR,
+ s->aGammaTableG, s->aGammaTableB);
+
+ /* prepare the actual scan */
+ /* We say normal here. In future we should have a preview flag to set preview mode */
+ if (InitScan (SCAN_TYPE_NORMAL, &s->ScanParams, &s->HWParams) != 0)
+ {
+ HP5400_DBG (DBG_MSG, "Invalid scan parameters (InitScan)\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* for the moment no lines has been read */
+ s->ScanParams.iLinesRead = 0;
+
+ s->fScanning = TRUE;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+
+ /* Read actual scan from the circular buffer */
+ /* Note: this is already color corrected, though some work still needs to be done
+ to deal with the colour offsetting */
+ TScanner *s;
+ char *buffer = (char*)buf;
+
+ HP5400_DBG (DBG_MSG, "sane_read: request %d bytes \n", maxlen);
+
+ s = (TScanner *) h;
+
+ /* nothing has been read for the moment */
+ *len = 0;
+
+
+ /* if we read all the lines return EOF */
+ if (s->ScanParams.iLinesRead == s->ScanParams.iLines)
+ {
+/* FinishScan( &s->HWParams ); *** FinishScan called in sane_cancel */
+ HP5400_DBG (DBG_MSG, "sane_read: EOF\n");
+ return SANE_STATUS_EOF;
+ }
+
+ /* read as many lines the buffer may contain and while there are lines to be read */
+ while ((*len + s->ScanParams.iBytesPerLine <= maxlen)
+ && (s->ScanParams.iLinesRead < s->ScanParams.iLines))
+ {
+
+ /* get one more line from the circular buffer */
+ CircBufferGetLine (s->HWParams.iXferHandle, &s->HWParams.pipe, buffer);
+
+ /* increment pointer, size and line number */
+ buffer += s->ScanParams.iBytesPerLine;
+ *len += s->ScanParams.iBytesPerLine;
+ s->ScanParams.iLinesRead++;
+ }
+
+ HP5400_DBG (DBG_MSG, "sane_read: %d bytes read\n", *len);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_cancel (SANE_Handle h)
+{
+ TScanner *s;
+
+ HP5400_DBG (DBG_MSG, "sane_cancel\n");
+
+ s = (TScanner *) h;
+
+ /* to be implemented more thoroughly */
+
+ /* Make sure the scanner head returns home */
+ FinishScan (&s->HWParams);
+
+ s->fCanceled = TRUE;
+ s->fScanning = FALSE;
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ HP5400_DBG (DBG_MSG, "sane_set_io_mode %s\n", m ? "non-blocking" : "blocking");
+
+ /* prevent compiler from complaining about unused parameters */
+ h = h;
+
+ if (m)
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ HP5400_DBG (DBG_MSG, "sane_select_fd\n");
+
+ /* prevent compiler from complaining about unused parameters */
+ h = h;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/hp5400_sanei.c b/backend/hp5400_sanei.c
new file mode 100644
index 0000000..1396cb1
--- /dev/null
+++ b/backend/hp5400_sanei.c
@@ -0,0 +1,387 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+ Copyright (c) 2003 Henning Meier-Geinitz, <henning@meier-geinitz.de>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Transport layer for communication with HP5400/5470 scanner.
+
+ Implementation using sanei_usb
+
+ Additions to support bulk data transport. Added debugging info - 19/02/2003 Martijn
+ Changed to use sanei_usb instead of direct /dev/usb/scanner access - 15/04/2003 Henning
+*/
+
+
+#include "hp5400_xfer.h"
+#include "hp5400_debug.h"
+#include <stdio.h>
+#include "../include/sane/sanei_usb.h"
+
+#define CMD_INITBULK1 0x0087 /* send 0x14 */
+#define CMD_INITBULK2 0x0083 /* send 0x24 */
+#define CMD_INITBULK3 0x0082 /* transfer length 0xf000 */
+
+
+
+static void
+_UsbWriteControl (int fd, int iValue, int iIndex, void *pabData, int iSize)
+{
+ int requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT;
+ int request = (iSize > 1) ? 0x04 : 0x0C;
+
+ HP5400_DBG (DBG_MSG,
+ "Write: reqtype = 0x%02X, req = 0x%02X, value = %04X, len = %d\n",
+ requesttype, request, iValue, iSize);
+
+ if (iSize > 0)
+ {
+ int i;
+ HP5400_DBG (DBG_MSG, " Data: ");
+ for (i = 0; i < iSize && i < 8; i++)
+ HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) pabData)[i]);
+ if (iSize > 8)
+ HP5400_DBG (DBG_MSG, "...");
+ HP5400_DBG (DBG_MSG, "\n");
+ }
+
+ if (fd != -1)
+ {
+ sanei_usb_control_msg (fd, requesttype, request, iValue, iIndex, iSize,
+ pabData);
+ }
+ /* No error checking? */
+
+}
+
+void
+hp5400_command_write_noverify (int fd, int iValue, void *pabData, int iSize)
+{
+ _UsbWriteControl (fd, iValue, 0, pabData, iSize);
+}
+
+static void
+_UsbReadControl (int fd, int iValue, int iIndex, void *pabData, int iSize)
+{
+ int requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN;
+ int request = (iSize > 1) ? 0x04 : 0x0C;
+
+ HP5400_DBG (DBG_MSG, "Read: reqtype = 0x%02X, req = 0x%02X, value = %04X\n",
+ requesttype, request, iValue);
+
+ if (fd != -1)
+ {
+ sanei_usb_control_msg (fd, requesttype, request, iValue, iIndex, iSize,
+ pabData);
+ }
+}
+
+
+HP5400_SANE_STATIC int
+hp5400_open (const char *filename)
+{
+ int fd, iVendor, iProduct;
+ SANE_Status status;
+
+ if (!filename)
+ filename = "/dev/usb/scanner0";
+
+ status = sanei_usb_open (filename, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ HP5400_DBG (DBG_MSG, "hp5400_open: open returned %s\n",
+ sane_strstatus (status));
+ return -1;
+ }
+
+ status = sanei_usb_get_vendor_product (fd, &iVendor, &iProduct);
+ if (status != SANE_STATUS_GOOD)
+ {
+ HP5400_DBG (DBG_MSG, "hp5400_open: can't get vendor/product ids: %s\n",
+ sane_strstatus (status));
+ sanei_usb_close (fd);
+ return -1;
+ }
+
+ if ((iVendor != 0x03F0) || ((iProduct != 0x1005) && (iProduct != 0x1105)))
+ {
+ HP5400_DBG (DBG_MSG, "hp5400_open: vendor/product 0x%04X-0x%04X is not "
+ "supported\n", iVendor, iProduct);
+ sanei_usb_close (fd);
+ return -1;
+ }
+
+ HP5400_DBG (DBG_MSG, "vendor/product 0x%04X-0x%04X opened\n", iVendor, iProduct);
+
+ return fd;
+}
+
+
+void
+hp5400_close (int iHandle)
+{
+ sanei_usb_close (iHandle);
+}
+
+
+/* returns value > 0 if verify ok */
+int
+hp5400_command_verify (int iHandle, int iCmd)
+{
+ unsigned char abData[4];
+ int fd;
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_ERR, "hp5400_command_verify: invalid handle\n");
+ return -1;
+ }
+ fd = iHandle;
+
+ /* command 0xc500: read back previous command */
+ _UsbReadControl (fd, 0xc500, 0, (char *) abData, 2);
+
+ if (abData[0] != (iCmd >> 8))
+ {
+ HP5400_DBG (DBG_ERR,
+ "hp5400_command_verify failed, expected 0x%02X%02X, got 0x%02X%02X\n",
+ (int) (iCmd >> 8), (int) (iCmd & 0xff), (int) abData[0],
+ (int) abData[1]);
+
+ return -1;
+ }
+
+ if (abData[1] != 0) /* Error code non-zero */
+ {
+ _UsbReadControl (fd, 0x0300, 0, (char *) abData, 3);
+ HP5400_DBG (DBG_ERR, " error response is: %02X %02X %02X\n", abData[0],
+ abData[1], abData[2]);
+
+ return -1;
+ }
+
+ HP5400_DBG (DBG_MSG, "Command %02X verified\n", abData[0]);
+ return 1;
+}
+
+
+/* returns > 0 if command OK */
+int
+hp5400_command_read_noverify (int iHandle, int iCmd, int iLen, void *pbData)
+{
+ int fd;
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_ERR, "hp5400_command_read: invalid handle\n");
+ return -1;
+ }
+ fd = iHandle;
+
+ _UsbReadControl (fd, iCmd, 0, pbData, iLen);
+
+ return 1;
+}
+
+/* returns > 0 if command OK */
+int
+hp5400_command_read (int iHandle, int iCmd, int iLen, void *pbData)
+{
+ hp5400_command_read_noverify (iHandle, iCmd, iLen, pbData);
+
+ return hp5400_command_verify (iHandle, iCmd);
+}
+
+
+/* returns >0 if command OK */
+int
+hp5400_command_write (int iHandle, int iCmd, int iLen, void *pbData)
+{
+ int fd;
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_ERR, "hp5400_command_write: invalid handle\n");
+ return -1;
+ }
+ fd = iHandle;
+
+ _UsbWriteControl (fd, iCmd, 0, (char *) pbData, iLen);
+
+ return hp5400_command_verify (iHandle, iCmd);
+}
+
+#ifdef STANDALONE
+/* returns >0 if command OK */
+int
+hp5400_bulk_read (int iHandle, size_t len, int block, FILE * file)
+{
+ SANE_Int fd;
+ char x1 = 0x14, x2 = 0x24;
+ short buf[4] = { 0, 0, 0, 0 };
+ SANE_Byte *buffer;
+ size_t res = 0;
+
+ buf[2] = block;
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_ERR, "hp5400_command_read: invalid handle\n");
+ return -1;
+ }
+ fd = iHandle;
+
+ buffer = (unsigned char*) malloc (block);
+
+ _UsbWriteControl (fd, CMD_INITBULK1, 0, &x1, 1);
+ _UsbWriteControl (fd, CMD_INITBULK2, 0, &x2, 1);
+
+ while (len > 0)
+ {
+ _UsbWriteControl (fd, CMD_INITBULK3, 0, (unsigned char *) &buf,
+ sizeof (buf));
+ res = block;
+ sanei_usb_read_bulk (fd, buffer, &res);
+ HP5400_DBG (DBG_MSG, "Read bulk returned %lu, %lu remain\n",
+ (u_long) res, (u_long) len);
+ if (res > 0)
+ {
+ fwrite (buffer, (len < res) ? len : res, 1, file);
+ }
+ len -= block;
+ }
+ /* error handling? */
+ return 0;
+}
+#endif
+
+/* returns >0 if command OK */
+int
+hp5400_bulk_read_block (int iHandle, int iCmd, void *cmd, int cmdlen,
+ void *buffer, int len)
+{
+ SANE_Int fd;
+ size_t res = 0;
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_ERR, "hp5400_command_read_block: invalid handle\n");
+ return -1;
+ }
+ fd = iHandle;
+
+ _UsbWriteControl (fd, iCmd, 0, cmd, cmdlen);
+ res = len;
+ sanei_usb_read_bulk (fd, (SANE_Byte *) buffer, &res);
+ HP5400_DBG (DBG_MSG, "Read block returned %lu when reading %d\n",
+ (u_long) res, len);
+ return res;
+}
+
+/* returns >0 if command OK */
+int
+hp5400_bulk_command_write (int iHandle, int iCmd, void *cmd, int cmdlen,
+ int datalen, int block, char *data)
+{
+ SANE_Int fd;
+ size_t res = 0, offset = 0;
+
+ if (iHandle < 0)
+ {
+ HP5400_DBG (DBG_ERR, "hp5400_bulk_command_write: invalid handle\n");
+ return -1;
+ }
+ fd = iHandle;
+
+ HP5400_DBG (DBG_MSG, "bulk_command_write(%04X,<%d bytes>,<%d bytes>)\n", iCmd,
+ cmdlen, datalen);
+
+ _UsbWriteControl (fd, iCmd, 0, cmd, cmdlen);
+
+ while (datalen > 0)
+ {
+ {
+ int i;
+ HP5400_DBG (DBG_MSG, " Data: ");
+ for (i = 0; i < datalen && i < block && i < 8; i++)
+ HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) data + offset)[i]);
+ if (i >= 8)
+ HP5400_DBG (DBG_MSG, "...");
+ HP5400_DBG (DBG_MSG, "\n");
+ }
+ res = (datalen < block) ? datalen : block;
+ sanei_usb_write_bulk (fd, (SANE_Byte *) (data + offset), &res);
+ HP5400_DBG (DBG_MSG, "Write returned %lu, %d remain\n", (u_long) res, datalen);
+ datalen -= block;
+ offset += block;
+ }
+
+ return hp5400_command_verify (iHandle, iCmd);
+}
+
+#ifdef STANDALONE
+/**
+ ScannerIsOn
+ retrieve on/off status from scanner
+ @return 1 if is on 0 if is off -1 if is not reachable
+*/
+int
+hp5400_isOn (int iHandle)
+{
+ unsigned char text2400[3];
+
+ hp5400_command_read (iHandle, 0x2400, 0x03, text2400);
+
+ /* byte 0 indicates if is on or off if 0x02 */
+ /* byte 1 indicates time since is on */
+ /* byte 2 indicates time since is power plugged */
+
+ if (text2400[0] & 0x02)
+ {
+ return 1; /* is on */
+ }
+
+ return 0; /* is off */
+}
+#endif
+
diff --git a/backend/hp5400_sanei.h b/backend/hp5400_sanei.h
new file mode 100644
index 0000000..3c8e337
--- /dev/null
+++ b/backend/hp5400_sanei.h
@@ -0,0 +1,108 @@
+#ifndef _HP5400_SANEI_H_
+#define _HP5400_SANEI_H_
+
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+ Copyright (c) 2003 Henning Meier-Geinitz, <henning@meier-geinitz.de>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Transport layer for communication with HP5400/5470 scanner.
+
+ Implementation using sanei_usb
+
+ Additions to support bulk data transport. Added debugging info - 19/02/2003 Martijn
+ Changed to use sanei_usb instead of direct /dev/usb/scanner access - 15/04/2003 Henning
+*/
+
+
+
+#define CMD_INITBULK1 0x0087 /* send 0x14 */
+#define CMD_INITBULK2 0x0083 /* send 0x24 */
+#define CMD_INITBULK3 0x0082 /* transfer length 0xf000 */
+
+
+
+HP5400_SANE_STATIC void _UsbWriteControl (int fd, int iValue, int iIndex, void *pabData, int iSize);
+
+HP5400_SANE_STATIC void hp5400_command_write_noverify (int fd, int iValue, void *pabData, int iSize);
+
+HP5400_SANE_STATIC void _UsbReadControl (int fd, int iValue, int iIndex, void *pabData, int iSize);
+
+HP5400_SANE_STATIC int hp5400_open (const char *filename);
+
+HP5400_SANE_STATIC void hp5400_close (int iHandle);
+
+/* returns value > 0 if verify ok */
+HP5400_SANE_STATIC int hp5400_command_verify (int iHandle, int iCmd);
+
+/* returns > 0 if command OK */
+HP5400_SANE_STATIC int hp5400_command_read_noverify (int iHandle, int iCmd, int iLen, void *pbData);
+
+/* returns > 0 if command OK */
+HP5400_SANE_STATIC int hp5400_command_read (int iHandle, int iCmd, int iLen, void *pbData);
+
+/* returns >0 if command OK */
+HP5400_SANE_STATIC int hp5400_command_write (int iHandle, int iCmd, int iLen, void *pbData);
+
+#ifdef STANDALONE
+/* returns >0 if command OK */
+HP5400_SANE_STATIC int hp5400_bulk_read (int iHandle, size_t len, int block, FILE * file);
+#endif
+
+/* returns >0 if command OK */
+HP5400_SANE_STATIC int hp5400_bulk_read_block (int iHandle, int iCmd, void *cmd, int cmdlen,
+ void *buffer, int len);
+
+/* returns >0 if command OK */
+HP5400_SANE_STATIC int hp5400_bulk_command_write (int iHandle, int iCmd, void *cmd, int cmdlen,
+ int datalen, int block, char *data);
+
+/**
+ ScannerIsOn
+ retrieve on/off status from scanner
+ @return 1 if is on 0 if is off -1 if is not reachable
+*/
+HP5400_SANE_STATIC int hp5400_isOn (int iHandle);
+
+#endif
diff --git a/backend/hp5400_xfer.h b/backend/hp5400_xfer.h
new file mode 100644
index 0000000..e3e823d
--- /dev/null
+++ b/backend/hp5400_xfer.h
@@ -0,0 +1,77 @@
+#ifndef _HP5400_XFER_H_
+#define _HP5400_XFER_H_
+
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
+ Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
+
+ Originally copied from HP3300 testtools. Original notice follows:
+
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Transport layer for communication with HP5400/5470 scanner.
+
+ Add support for bulk transport of data - 19/02/2003 Martijn
+*/
+
+#include <stdio.h>
+
+HP5400_SANE_STATIC int hp5400_open (const char *filename);
+HP5400_SANE_STATIC void hp5400_close (int iHandle);
+
+HP5400_SANE_STATIC int hp5400_command_verify (int iHandle, int iCmd);
+HP5400_SANE_STATIC int hp5400_command_read (int iHandle, int iCmd, int iLen, void *pbData);
+HP5400_SANE_STATIC int hp5400_command_read_noverify (int iHandle, int iCmd, int iLen,
+ void *pbData);
+HP5400_SANE_STATIC int hp5400_command_write (int iHandle, int iCmd, int iLen, void *pbData);
+HP5400_SANE_STATIC void hp5400_command_write_noverify (int fd, int iValue, void *pabData,
+ int iSize);
+#ifdef STANDALONE
+HP5400_SANE_STATIC int hp5400_bulk_read (int iHandle, size_t size, int block, FILE * file);
+#endif
+HP5400_SANE_STATIC int hp5400_bulk_read_block (int iHandle, int iCmd, void *cmd, int cmdlen,
+ void *buffer, int len);
+HP5400_SANE_STATIC int hp5400_bulk_command_write (int iHandle, int iCmd, void *cmd, int cmdlen,
+ int len, int block, char *data);
+#ifdef STANDALONE
+HP5400_SANE_STATIC int hp5400_isOn (int iHandle);
+#endif
+
+#endif
diff --git a/backend/hp5590.c b/backend/hp5590.c
new file mode 100644
index 0000000..8db3d5e
--- /dev/null
+++ b/backend/hp5590.c
@@ -0,0 +1,1381 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
+ HP ScanJet 4570c support by Markham Thomas
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for
+ HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners
+*/
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#define BACKEND_NAME hp5590
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "hp5590_cmds.c"
+#include "hp5590_low.c"
+
+/* Debug levels */
+#define DBG_err 0
+#define DBG_proc 10
+#define DBG_verbose 20
+
+#define hp5590_assert(exp) if(!(exp)) { \
+ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\
+ return SANE_STATUS_INVAL; \
+}
+
+#define hp5590_assert_void_return(exp) if(!(exp)) { \
+ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\
+ return; \
+}
+
+/* #define HAS_WORKING_COLOR_48 */
+#define BUILD 7
+#define USB_TIMEOUT 30 * 1000
+
+static SANE_Word
+res_list[] = { 6, 100, 200, 300, 600, 1200, 2400 };
+
+#define SANE_VALUE_SCAN_SOURCE_FLATBED SANE_I18N("Flatbed")
+#define SANE_VALUE_SCAN_SOURCE_ADF SANE_I18N("ADF")
+#define SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX SANE_I18N("ADF Duplex")
+#define SANE_VALUE_SCAN_SOURCE_TMA_SLIDES SANE_I18N("TMA Slides")
+#define SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES SANE_I18N("TMA Negatives")
+
+#define SANE_VALUE_SCAN_MODE_COLOR_24 SANE_VALUE_SCAN_MODE_COLOR
+#define SANE_VALUE_SCAN_MODE_COLOR_48 SANE_I18N("Color (48 bits)")
+
+#define SANE_NAME_LAMP_TIMEOUT "extend-lamp-timeout"
+#define SANE_TITLE_LAMP_TIMEOUT SANE_I18N("Extend lamp timeout")
+#define SANE_DESC_LAMP_TIMEOUT SANE_I18N("Extends lamp timeout (from 15 minutes to 1 hour)")
+#define SANE_NAME_WAIT_FOR_BUTTON "wait-for-button"
+#define SANE_TITLE_WAIT_FOR_BUTTON SANE_I18N("Wait for button")
+#define SANE_DESC_WAIT_FOR_BUTTON SANE_I18N("Waits for button before scanning")
+
+#define MAX_SCAN_SOURCE_VALUE_LEN 24
+#define MAX_SCAN_MODE_VALUE_LEN 24
+
+static SANE_Range
+range_x, range_y, range_qual;
+
+static SANE_String_Const
+mode_list[] = {
+ SANE_VALUE_SCAN_MODE_COLOR_24,
+#ifdef HAS_WORKING_COLOR_48
+ SANE_VALUE_SCAN_MODE_COLOR_48,
+#endif /* HAS_WORKING_COLOR_48 */
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_LINEART,
+ NULL
+};
+
+enum hp5590_opt_idx {
+ HP5590_OPT_NUM = 0,
+ HP5590_OPT_TL_X,
+ HP5590_OPT_TL_Y,
+ HP5590_OPT_BR_X,
+ HP5590_OPT_BR_Y,
+ HP5590_OPT_MODE,
+ HP5590_OPT_SOURCE,
+ HP5590_OPT_RESOLUTION,
+ HP5590_OPT_LAMP_TIMEOUT,
+ HP5590_OPT_WAIT_FOR_BUTTON,
+ HP5590_OPT_PREVIEW,
+ HP5590_OPT_LAST
+};
+
+struct hp5590_scanner {
+ struct scanner_info *info;
+ enum proto_flags proto_flags;
+ SANE_Device sane;
+ SANE_Int dn;
+ float br_x, br_y, tl_x, tl_y;
+ unsigned int dpi;
+ enum color_depths depth;
+ enum scan_sources source;
+ SANE_Bool extend_lamp_timeout;
+ SANE_Bool wait_for_button;
+ SANE_Bool preview;
+ unsigned int quality;
+ SANE_Option_Descriptor *opts;
+ struct hp5590_scanner *next;
+ unsigned int image_size;
+ SANE_Int transferred_image_size;
+ void *bulk_read_state;
+ SANE_Bool scanning;
+};
+
+static
+struct hp5590_scanner *scanners_list;
+
+/******************************************************************************/
+static SANE_Status
+calc_image_params (struct hp5590_scanner *scanner,
+ unsigned int *pixel_bits,
+ unsigned int *pixels_per_line,
+ unsigned int *bytes_per_line,
+ unsigned int *lines,
+ unsigned int *image_size)
+{
+ unsigned int _pixel_bits;
+ SANE_Status ret;
+ unsigned int _pixels_per_line;
+ unsigned int _bytes_per_line;
+ unsigned int _lines;
+ unsigned int _image_size;
+ float var;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ if (!scanner)
+ return SANE_STATUS_INVAL;
+
+ ret = hp5590_calc_pixel_bits (scanner->dpi, scanner->depth, &_pixel_bits);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ var = (float) (1.0 * (scanner->br_x - scanner->tl_x) * scanner->dpi);
+ _pixels_per_line = var;
+ if (var > _pixels_per_line)
+ _pixels_per_line++;
+
+ var = (float) (1.0 * (scanner->br_y - scanner->tl_y) * scanner->dpi);
+ _lines = var;
+ if (var > _lines)
+ _lines++;
+
+ var = (float) (1.0 * _pixels_per_line / 8 * _pixel_bits);
+ _bytes_per_line = var;
+ if (var > _bytes_per_line)
+ _bytes_per_line++;
+
+ _image_size = _lines * _bytes_per_line;
+
+ DBG (DBG_verbose, "%s: pixel_bits: %u, pixels_per_line: %u, "
+ "bytes_per_line: %u, lines: %u, image_size: %u\n",
+ __FUNCTION__,
+ _pixel_bits, _pixels_per_line, _bytes_per_line, _lines, _image_size);
+
+ if (pixel_bits)
+ *pixel_bits = _pixel_bits;
+
+ if (pixels_per_line)
+ *pixels_per_line = _pixels_per_line;
+
+ if (bytes_per_line)
+ *bytes_per_line = _bytes_per_line;
+
+ if (lines)
+ *lines = _lines;
+
+ if (image_size)
+ *image_size = _image_size;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+attach_usb_device (SANE_String_Const devname,
+ enum hp_scanner_types hp_scanner_type)
+{
+ struct scanner_info *info;
+ struct hp5590_scanner *scanner, *ptr;
+ unsigned int max_count, count;
+ SANE_Int dn;
+ SANE_Status ret;
+ const struct hp5590_model *hp5590_model;
+
+ DBG (DBG_proc, "%s: Opening USB device\n", __FUNCTION__);
+ if (sanei_usb_open (devname, &dn) != SANE_STATUS_GOOD)
+ return SANE_STATUS_IO_ERROR;
+ DBG (DBG_proc, "%s: USB device opened\n", __FUNCTION__);
+
+ ret = hp5590_model_def (hp_scanner_type, &hp5590_model);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (hp5590_init_scanner (dn, hp5590_model->proto_flags,
+ &info, hp_scanner_type) != 0)
+ return SANE_STATUS_IO_ERROR;
+
+ DBG (1, "%s: found HP%s scanner at '%s'\n",
+ __FUNCTION__, info->model, devname);
+
+ DBG (DBG_verbose, "%s: Reading max scan count\n", __FUNCTION__);
+ if (hp5590_read_max_scan_count (dn, hp5590_model->proto_flags,
+ &max_count) != 0)
+ return SANE_STATUS_IO_ERROR;
+ DBG (DBG_verbose, "%s: Max Scanning count %u\n", __FUNCTION__, max_count);
+
+ DBG (DBG_verbose, "%s: Reading scan count\n", __FUNCTION__);
+ if (hp5590_read_scan_count (dn, hp5590_model->proto_flags,
+ &count) != 0)
+ return SANE_STATUS_IO_ERROR;
+ DBG (DBG_verbose, "%s: Scanning count %u\n", __FUNCTION__, count);
+
+ ret = hp5590_read_part_number (dn, hp5590_model->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_stop_scan (dn, hp5590_model->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ scanner = malloc (sizeof(struct hp5590_scanner));
+ if (!scanner)
+ return SANE_STATUS_NO_MEM;
+ memset (scanner, 0, sizeof(struct hp5590_scanner));
+
+ scanner->sane.model = info->model;
+ scanner->sane.vendor = "HP";
+ scanner->sane.type = info->kind;
+ scanner->sane.name = devname;
+ scanner->dn = dn;
+ scanner->proto_flags = hp5590_model->proto_flags;
+ scanner->info = info;
+ scanner->bulk_read_state = NULL;
+ scanner->opts = NULL;
+
+ if (!scanners_list)
+ scanners_list = scanner;
+ else
+ {
+ for (ptr = scanners_list; ptr->next; ptr = ptr->next);
+ ptr->next = scanner;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+attach_hp4570 (SANE_String_Const devname)
+{
+ return attach_usb_device (devname, SCANNER_HP4570);
+}
+
+/******************************************************************************/
+static SANE_Status
+attach_hp5550 (SANE_String_Const devname)
+{
+ return attach_usb_device (devname, SCANNER_HP5550);
+}
+
+/******************************************************************************/
+static SANE_Status
+attach_hp5590 (SANE_String_Const devname)
+{
+ return attach_usb_device (devname, SCANNER_HP5590);
+}
+
+/******************************************************************************/
+static SANE_Status
+attach_hp7650 (SANE_String_Const devname)
+{
+ return attach_usb_device (devname, SCANNER_HP7650);
+}
+
+/******************************************************************************/
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ SANE_Status ret;
+ SANE_Word vendor_id, product_id;
+
+ DBG_INIT();
+
+ DBG (1, "SANE backed for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 %u.%u.%u\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ DBG (1, "(c) Ilia Sotnikov <hostcc@gmail.com>\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ sanei_usb_init();
+
+ sanei_usb_set_timeout (USB_TIMEOUT);
+
+ scanners_list = NULL;
+
+ ret = hp5590_vendor_product_id (SCANNER_HP4570, &vendor_id, &product_id);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp4570);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_vendor_product_id (SCANNER_HP5550, &vendor_id, &product_id);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp5550);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_vendor_product_id (SCANNER_HP5590, &vendor_id, &product_id);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp5590);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_vendor_product_id (SCANNER_HP7650, &vendor_id, &product_id);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp7650);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+void sane_exit (void)
+{
+ struct hp5590_scanner *ptr, *pnext;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ for (ptr = scanners_list; ptr; ptr = pnext)
+ {
+ if (ptr->opts != NULL)
+ free (ptr->opts);
+ pnext = ptr->next;
+ free (ptr);
+ }
+}
+
+/******************************************************************************/
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ struct hp5590_scanner *ptr;
+ unsigned int found, i;
+
+ DBG (DBG_proc, "%s, local only: %u\n", __FUNCTION__, local_only);
+
+ if (!device_list)
+ return SANE_STATUS_INVAL;
+
+ for (found = 0, ptr = scanners_list; ptr; found++, ptr = ptr->next);
+ DBG (1, "Found %u devices\n", found);
+
+ found++;
+ *device_list = malloc (found * sizeof (SANE_Device));
+ if (!*device_list)
+ return SANE_STATUS_NO_MEM;
+ memset (*device_list, 0, found * sizeof(SANE_Device));
+
+ for (i = 0, ptr = scanners_list; ptr; i++, ptr = ptr->next)
+ {
+ (*device_list)[i] = &(ptr->sane);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ struct hp5590_scanner *ptr;
+ SANE_Option_Descriptor *opts;
+ unsigned int available_sources;
+ SANE_String_Const *sources_list;
+ unsigned int source_idx;
+
+ DBG (DBG_proc, "%s: device name: %s\n", __FUNCTION__, devicename);
+
+ if (!handle)
+ return SANE_STATUS_INVAL;
+
+ /* Allow to open the first available device by specifying zero-length name */
+ if (!devicename || !devicename[0]) {
+ ptr = scanners_list;
+ } else {
+ for (ptr = scanners_list;
+ ptr && strcmp (ptr->sane.name, devicename) != 0;
+ ptr = ptr->next);
+ }
+
+ if (!ptr)
+ return SANE_STATUS_INVAL;
+
+ ptr->tl_x = 0;
+ ptr->tl_y = 0;
+ ptr->br_x = ptr->info->max_size_x;
+ ptr->br_y = ptr->info->max_size_y;
+ ptr->dpi = res_list[1];
+ ptr->depth = DEPTH_BW;
+ ptr->source = SOURCE_FLATBED;
+ ptr->extend_lamp_timeout = SANE_FALSE;
+ ptr->wait_for_button = SANE_FALSE;
+ ptr->preview = SANE_FALSE;
+ ptr->quality = 4;
+ ptr->image_size = 0;
+ ptr->scanning = SANE_FALSE;
+
+ *handle = ptr;
+
+ opts = malloc (sizeof (SANE_Option_Descriptor) * HP5590_OPT_LAST);
+ if (!opts)
+ return SANE_STATUS_NO_MEM;
+
+ opts[HP5590_OPT_NUM].name = SANE_NAME_NUM_OPTIONS;
+ opts[HP5590_OPT_NUM].title = SANE_TITLE_NUM_OPTIONS;
+ opts[HP5590_OPT_NUM].desc = SANE_DESC_NUM_OPTIONS;
+ opts[HP5590_OPT_NUM].type = SANE_TYPE_INT;
+ opts[HP5590_OPT_NUM].unit = SANE_UNIT_NONE;
+ opts[HP5590_OPT_NUM].size = sizeof(SANE_Word);
+ opts[HP5590_OPT_NUM].cap = SANE_CAP_INACTIVE | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_NUM].constraint_type = SANE_CONSTRAINT_NONE;
+ opts[HP5590_OPT_NUM].constraint.string_list = NULL;
+
+ range_x.min = SANE_FIX(0);
+ range_x.max = SANE_FIX(ptr->info->max_size_x * 25.4);
+ range_x.quant = SANE_FIX(0.1);
+ range_y.min = SANE_FIX(0);
+ range_y.max = SANE_FIX(ptr->info->max_size_y * 25.4);
+ range_y.quant = SANE_FIX(0.1);
+
+ range_qual.min = SANE_FIX(4);
+ range_qual.max = SANE_FIX(16);
+ range_qual.quant = SANE_FIX(1);
+
+ opts[HP5590_OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ opts[HP5590_OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ opts[HP5590_OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ opts[HP5590_OPT_TL_X].type = SANE_TYPE_FIXED;
+ opts[HP5590_OPT_TL_X].unit = SANE_UNIT_MM;
+ opts[HP5590_OPT_TL_X].size = sizeof(SANE_Fixed);
+ opts[HP5590_OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ opts[HP5590_OPT_TL_X].constraint.range = &range_x;
+
+ opts[HP5590_OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ opts[HP5590_OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ opts[HP5590_OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ opts[HP5590_OPT_TL_Y].type = SANE_TYPE_FIXED;
+ opts[HP5590_OPT_TL_Y].unit = SANE_UNIT_MM;
+ opts[HP5590_OPT_TL_Y].size = sizeof(SANE_Fixed);
+ opts[HP5590_OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ opts[HP5590_OPT_TL_Y].constraint.range = &range_y;
+
+ opts[HP5590_OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ opts[HP5590_OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ opts[HP5590_OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ opts[HP5590_OPT_BR_X].type = SANE_TYPE_FIXED;
+ opts[HP5590_OPT_BR_X].unit = SANE_UNIT_MM;
+ opts[HP5590_OPT_BR_X].size = sizeof(SANE_Fixed);
+ opts[HP5590_OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ opts[HP5590_OPT_BR_X].constraint.range = &range_x;
+
+ opts[HP5590_OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ opts[HP5590_OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ opts[HP5590_OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ opts[HP5590_OPT_BR_Y].type = SANE_TYPE_FIXED;
+ opts[HP5590_OPT_BR_Y].unit = SANE_UNIT_MM;
+ opts[HP5590_OPT_BR_Y].size = sizeof(SANE_Fixed);
+ opts[HP5590_OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ opts[HP5590_OPT_BR_Y].constraint.range = &range_y;
+
+ opts[HP5590_OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ opts[HP5590_OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ opts[HP5590_OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ opts[HP5590_OPT_MODE].type = SANE_TYPE_STRING;
+ opts[HP5590_OPT_MODE].unit = SANE_UNIT_NONE;
+ opts[HP5590_OPT_MODE].size = MAX_SCAN_MODE_VALUE_LEN;
+ opts[HP5590_OPT_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opts[HP5590_OPT_MODE].constraint.string_list = mode_list;
+
+ available_sources = 1; /* Flatbed is always available */
+ if (ptr->info->features & FEATURE_ADF)
+ available_sources += 2;
+ if (ptr->info->features & FEATURE_TMA)
+ available_sources += 2;
+ available_sources++; /* Count terminating NULL */
+ sources_list = malloc (available_sources * sizeof (SANE_String_Const));
+ if (!sources_list)
+ return SANE_STATUS_NO_MEM;
+ source_idx = 0;
+ sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_FLATBED;
+ if (ptr->info->features & FEATURE_ADF)
+ {
+ sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_ADF;
+ sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX;
+ }
+ if (ptr->info->features & FEATURE_TMA)
+ {
+ sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_TMA_SLIDES;
+ sources_list[source_idx++] = SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES;
+ }
+ sources_list[source_idx] = NULL;
+
+ opts[HP5590_OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ opts[HP5590_OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ opts[HP5590_OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ opts[HP5590_OPT_SOURCE].type = SANE_TYPE_STRING;
+ opts[HP5590_OPT_SOURCE].unit = SANE_UNIT_NONE;
+ opts[HP5590_OPT_SOURCE].size = MAX_SCAN_SOURCE_VALUE_LEN;
+ opts[HP5590_OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opts[HP5590_OPT_SOURCE].constraint.string_list = sources_list;
+
+ opts[HP5590_OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ opts[HP5590_OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ opts[HP5590_OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ opts[HP5590_OPT_RESOLUTION].type = SANE_TYPE_INT;
+ opts[HP5590_OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ opts[HP5590_OPT_RESOLUTION].size = sizeof(SANE_Int);
+ opts[HP5590_OPT_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opts[HP5590_OPT_RESOLUTION].constraint.word_list = res_list;
+
+ opts[HP5590_OPT_LAMP_TIMEOUT].name = SANE_NAME_LAMP_TIMEOUT;
+ opts[HP5590_OPT_LAMP_TIMEOUT].title = SANE_TITLE_LAMP_TIMEOUT;
+ opts[HP5590_OPT_LAMP_TIMEOUT].desc = SANE_DESC_LAMP_TIMEOUT;
+ opts[HP5590_OPT_LAMP_TIMEOUT].type = SANE_TYPE_BOOL;
+ opts[HP5590_OPT_LAMP_TIMEOUT].unit = SANE_UNIT_NONE;
+ opts[HP5590_OPT_LAMP_TIMEOUT].size = sizeof(SANE_Bool);
+ opts[HP5590_OPT_LAMP_TIMEOUT].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ opts[HP5590_OPT_LAMP_TIMEOUT].constraint_type = SANE_CONSTRAINT_NONE;
+ opts[HP5590_OPT_LAMP_TIMEOUT].constraint.string_list = NULL;
+
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].name = SANE_NAME_WAIT_FOR_BUTTON;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].title = SANE_TITLE_WAIT_FOR_BUTTON;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].desc = SANE_DESC_WAIT_FOR_BUTTON;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].size = sizeof(SANE_Bool);
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
+ opts[HP5590_OPT_WAIT_FOR_BUTTON].constraint.string_list = NULL;
+
+ opts[HP5590_OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ opts[HP5590_OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ opts[HP5590_OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ opts[HP5590_OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ opts[HP5590_OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ opts[HP5590_OPT_PREVIEW].size = sizeof(SANE_Bool);
+ opts[HP5590_OPT_PREVIEW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opts[HP5590_OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ opts[HP5590_OPT_PREVIEW].constraint.string_list = NULL;
+
+ ptr->opts = opts;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+void
+sane_close (SANE_Handle handle)
+{
+ struct hp5590_scanner *scanner = handle;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ sanei_usb_close (scanner->dn);
+ scanner->dn = -1;
+}
+
+/******************************************************************************/
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct hp5590_scanner *scanner = handle;
+
+ DBG (DBG_proc, "%s, option: %u\n", __FUNCTION__, option);
+
+ if (option >= HP5590_OPT_LAST)
+ return NULL;
+
+ return &scanner->opts[option];
+}
+
+/******************************************************************************/
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value,
+ SANE_Int * info)
+{
+ struct hp5590_scanner *scanner = handle;
+
+ if (!value)
+ return SANE_STATUS_INVAL;
+
+ if (!handle)
+ return SANE_STATUS_INVAL;
+
+ if (option >= HP5590_OPT_LAST)
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ if (option == HP5590_OPT_NUM)
+ {
+ DBG(3, "%s: get total number of options - %u\n", __FUNCTION__, HP5590_OPT_LAST);
+ *((SANE_Int *) value) = HP5590_OPT_LAST;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (!scanner->opts)
+ return SANE_STATUS_INVAL;
+
+ DBG (DBG_proc, "%s: get option '%s' value\n", __FUNCTION__, scanner->opts[option].name);
+
+ if (option == HP5590_OPT_BR_X)
+ {
+ *(SANE_Fixed *) value = SANE_FIX (scanner->br_x * 25.4);
+ }
+
+ if (option == HP5590_OPT_BR_Y)
+ {
+ *(SANE_Fixed *) value = SANE_FIX (scanner->br_y * 25.4);
+ }
+
+ if (option == HP5590_OPT_TL_X)
+ {
+ *(SANE_Fixed *) value = SANE_FIX ((scanner->tl_x) * 25.4);
+ }
+
+ if (option == HP5590_OPT_TL_Y)
+ {
+ *(SANE_Fixed *) value = SANE_FIX (scanner->tl_y * 25.4);
+ }
+
+ if (option == HP5590_OPT_MODE)
+ {
+ switch (scanner->depth) {
+ case DEPTH_BW:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_MODE_LINEART, strlen (SANE_VALUE_SCAN_MODE_LINEART));
+ break;
+ case DEPTH_GRAY:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_MODE_GRAY, strlen (SANE_VALUE_SCAN_MODE_GRAY));
+ break;
+ case DEPTH_COLOR_24:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_24, strlen (SANE_VALUE_SCAN_MODE_COLOR_24));
+ break;
+ case DEPTH_COLOR_48:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_48, strlen (SANE_VALUE_SCAN_MODE_COLOR_48));
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (option == HP5590_OPT_SOURCE)
+ {
+ switch (scanner->source) {
+ case SOURCE_FLATBED:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_SOURCE_FLATBED, strlen (SANE_VALUE_SCAN_SOURCE_FLATBED));
+ break;
+ case SOURCE_ADF:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF, strlen (SANE_VALUE_SCAN_SOURCE_ADF));
+ break;
+ case SOURCE_ADF_DUPLEX:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX, strlen (SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX));
+ break;
+ case SOURCE_TMA_SLIDES:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_SLIDES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_SLIDES));
+ break;
+ case SOURCE_TMA_NEGATIVES:
+ memset (value , 0, scanner->opts[option].size);
+ memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES));
+ break;
+ case SOURCE_NONE:
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (option == HP5590_OPT_RESOLUTION)
+ {
+ *(SANE_Int *) value = scanner->dpi;
+ }
+
+ if (option == HP5590_OPT_LAMP_TIMEOUT)
+ {
+ *(SANE_Bool *) value = scanner->extend_lamp_timeout;
+ }
+
+ if (option == HP5590_OPT_WAIT_FOR_BUTTON)
+ {
+ *(SANE_Bool *) value = scanner->wait_for_button;
+ }
+
+ if (option == HP5590_OPT_PREVIEW)
+ {
+ *(SANE_Bool *) value = scanner->preview;
+ }
+ }
+
+ if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (option == HP5590_OPT_NUM)
+ return SANE_STATUS_INVAL;
+
+ if (option == HP5590_OPT_BR_X)
+ {
+ float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4;
+ if (val <= scanner->tl_x)
+ return SANE_STATUS_GOOD;
+ scanner->br_x = val;
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS;
+ }
+
+ if (option == HP5590_OPT_BR_Y)
+ {
+ float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4;
+ if (val <= scanner->tl_y)
+ return SANE_STATUS_GOOD;
+ scanner->br_y = val;
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS;
+ }
+
+ if (option == HP5590_OPT_TL_X)
+ {
+ float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4;
+ if (val >= scanner->br_x)
+ return SANE_STATUS_GOOD;
+ scanner->tl_x = val;
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS;
+ }
+
+ if (option == HP5590_OPT_TL_Y)
+ {
+ float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4;
+ if (val >= scanner->br_y)
+ return SANE_STATUS_GOOD;
+ scanner->tl_y = val;
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS;
+ }
+
+ if (option == HP5590_OPT_MODE)
+ {
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ scanner->depth = DEPTH_BW;
+ }
+
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ scanner->depth = DEPTH_GRAY;
+ }
+
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_24) == 0)
+ {
+ scanner->depth = DEPTH_COLOR_24;
+ }
+
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_48) == 0)
+ {
+ scanner->depth = DEPTH_COLOR_48;
+ }
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ if (option == HP5590_OPT_SOURCE)
+ {
+ range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4);
+
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_FLATBED) == 0)
+ {
+ scanner->source = SOURCE_FLATBED;
+ range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4);
+ range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4);
+ scanner->br_x = scanner->info->max_size_x;
+ scanner->br_y = scanner->info->max_size_y;
+ }
+ /* In ADF modes the device can scan up to ADF_MAX_Y_INCHES, which is usually
+ * bigger than what scanner reports back during initialization
+ */
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF) == 0)
+ {
+ scanner->source = SOURCE_ADF;
+ range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4);
+ range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4);
+ scanner->br_x = scanner->info->max_size_x;
+ scanner->br_y = ADF_MAX_Y_INCHES * 25.4;
+ }
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX) == 0)
+ {
+ scanner->source = SOURCE_ADF_DUPLEX;
+ range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4);
+ range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4 * 2);
+ scanner->br_y = ADF_MAX_Y_INCHES * 25.4 * 2;
+ scanner->br_x = scanner->info->max_size_x;
+ }
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_SLIDES) == 0)
+ {
+ scanner->source = SOURCE_TMA_SLIDES;
+ range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4);
+ range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4);
+ scanner->br_x = TMA_MAX_X_INCHES * 25.4;
+ scanner->br_y = TMA_MAX_Y_INCHES * 25.4;
+ }
+ if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES) == 0)
+ {
+ scanner->source = SOURCE_TMA_NEGATIVES;
+ range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4);
+ range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4);
+ scanner->br_x = TMA_MAX_X_INCHES * 25.4;
+ scanner->br_y = TMA_MAX_Y_INCHES * 25.4;
+ }
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ if (option == HP5590_OPT_RESOLUTION)
+ {
+ scanner->dpi = *(SANE_Int *) value;
+ if (info)
+ *info = SANE_INFO_RELOAD_PARAMS;
+ }
+
+ if (option == HP5590_OPT_LAMP_TIMEOUT)
+ {
+ scanner->extend_lamp_timeout = *(SANE_Bool *) value;
+ }
+
+ if (option == HP5590_OPT_WAIT_FOR_BUTTON)
+ {
+ scanner->wait_for_button = *(SANE_Bool *) value;
+ }
+
+ if (option == HP5590_OPT_PREVIEW)
+ {
+ scanner->preview = *(SANE_Bool *) value;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params)
+{
+ struct hp5590_scanner *scanner = handle;
+ SANE_Status ret;
+ unsigned int pixel_bits;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ if (!params)
+ return SANE_STATUS_INVAL;
+
+ if (!handle)
+ return SANE_STATUS_INVAL;
+
+ ret = calc_image_params (scanner,
+ (unsigned int *) &pixel_bits,
+ (unsigned int *) &params->pixels_per_line,
+ (unsigned int *) &params->bytes_per_line,
+ (unsigned int *) &params->lines, NULL);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ switch (scanner->depth) {
+ case DEPTH_BW:
+ params->depth = pixel_bits;
+ params->format = SANE_FRAME_GRAY;
+ params->last_frame = SANE_TRUE;
+ break;
+ case DEPTH_GRAY:
+ params->depth = pixel_bits;
+ params->format = SANE_FRAME_GRAY;
+ params->last_frame = SANE_TRUE;
+ break;
+ case DEPTH_COLOR_24:
+ params->depth = pixel_bits / 3;
+ params->last_frame = SANE_TRUE;
+ params->format = SANE_FRAME_RGB;
+ break;
+ case DEPTH_COLOR_48:
+ params->depth = pixel_bits / 3;
+ params->last_frame = SANE_TRUE;
+ params->format = SANE_FRAME_RGB;
+ break;
+ default:
+ DBG(0, "%s: Unknown depth\n", __FUNCTION__);
+ return SANE_STATUS_INVAL;
+ }
+
+
+ DBG (DBG_proc, "format: %u, last_frame: %u, bytes_per_line: %u, "
+ "pixels_per_line: %u, lines: %u, depth: %u\n",
+ params->format, params->last_frame,
+ params->bytes_per_line, params->pixels_per_line,
+ params->lines, params->depth);
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct hp5590_scanner *scanner = handle;
+ SANE_Status ret;
+ unsigned int bytes_per_line;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ if (!scanner)
+ return SANE_STATUS_INVAL;
+
+ if ( scanner->scanning == SANE_TRUE
+ && ( scanner->source == SOURCE_ADF
+ || scanner->source == SOURCE_ADF_DUPLEX))
+ {
+ DBG (DBG_verbose, "%s: Scanner is scanning, check if more data is available\n",
+ __FUNCTION__);
+ ret = hp5590_is_data_available (scanner->dn, scanner->proto_flags);
+ if (ret == SANE_STATUS_GOOD)
+ {
+ DBG (DBG_verbose, "%s: More data is available\n", __FUNCTION__);
+ scanner->transferred_image_size = scanner->image_size;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (ret != SANE_STATUS_NO_DOCS)
+ return ret;
+ }
+
+ sane_cancel (handle);
+
+ if (scanner->wait_for_button)
+ {
+ enum button_status status;
+ for (;;)
+ {
+ ret = hp5590_read_buttons (scanner->dn,
+ scanner->proto_flags,
+ &status);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (status == BUTTON_CANCEL)
+ return SANE_STATUS_CANCELLED;
+
+ if (status != BUTTON_NONE && status != BUTTON_POWER)
+ break;
+ sleep (1);
+ }
+ }
+
+ DBG (DBG_verbose, "Init scanner\n");
+ ret = hp5590_init_scanner (scanner->dn, scanner->proto_flags,
+ NULL, SCANNER_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_power_status (scanner->dn, scanner->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (DBG_verbose, "Wakeup\n");
+ ret = hp5590_select_source_and_wakeup (scanner->dn, scanner->proto_flags,
+ scanner->source,
+ scanner->extend_lamp_timeout);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_set_scan_params (scanner->dn,
+ scanner->proto_flags,
+ scanner->info,
+ scanner->tl_x * scanner->dpi,
+ scanner->tl_y * scanner->dpi,
+ (scanner->br_x - scanner->tl_x) * scanner->dpi,
+ (scanner->br_y - scanner->tl_y) * scanner->dpi,
+ scanner->dpi,
+ scanner->depth, scanner->preview ? MODE_PREVIEW : MODE_NORMAL,
+ scanner->source);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return ret;
+ }
+
+ ret = calc_image_params (scanner, NULL, NULL,
+ &bytes_per_line, NULL,
+ &scanner->image_size);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return ret;
+ }
+
+ scanner->transferred_image_size = scanner->image_size;
+
+ if ( scanner->depth == DEPTH_COLOR_24
+ || scanner->depth == DEPTH_COLOR_48)
+ {
+ DBG (1, "Color 24/48 bits: checking if image size is correctly "
+ "aligned on number of colors\n");
+ if (bytes_per_line % 3)
+ {
+ DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on number of colors (3) "
+ "(image size: %u, bytes per line %u)\n",
+ scanner->image_size, bytes_per_line);
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (1, "Color 24/48 bits: image size is correctly aligned on number of colors "
+ "(image size: %u, bytes per line %u)\n",
+ scanner->image_size, bytes_per_line);
+
+ DBG (1, "Color 24/48 bits: checking if image size is correctly "
+ "aligned on bytes per line\n");
+ if (scanner->image_size % bytes_per_line)
+ {
+ DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on bytes per line "
+ "(image size: %u, bytes per line %u)\n",
+ scanner->image_size, bytes_per_line);
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (1, "Color 24/48 bits: image size correctly aligned on bytes per line "
+ "(images size: %u, bytes per line: %u)\n",
+ scanner->image_size, bytes_per_line);
+ }
+
+ DBG (DBG_verbose, "Final image size: %u\n", scanner->image_size);
+
+ DBG (DBG_verbose, "Reverse calibration maps\n");
+ ret = hp5590_send_reverse_calibration_map (scanner->dn, scanner->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return ret;
+ }
+
+ DBG (DBG_verbose, "Forward calibration maps\n");
+ ret = hp5590_send_forward_calibration_maps (scanner->dn, scanner->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return ret;
+ }
+
+ scanner->scanning = SANE_TRUE;
+
+ DBG (DBG_verbose, "Starting scan\n");
+ ret = hp5590_start_scan (scanner->dn, scanner->proto_flags);
+ /* Check for paper jam */
+ if ( ret == SANE_STATUS_DEVICE_BUSY
+ && ( scanner->source == SOURCE_ADF
+ || scanner->source == SOURCE_ADF_DUPLEX))
+ return SANE_STATUS_JAMMED;
+
+ if (ret != SANE_STATUS_GOOD)
+ {
+ hp5590_reset_scan_head (scanner->dn, scanner->proto_flags);
+ return ret;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+convert_lineart (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size)
+{
+ SANE_Int i;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_assert (scanner != NULL);
+ hp5590_assert (data != NULL);
+
+ /* Invert lineart */
+ if (scanner->depth == DEPTH_BW)
+ {
+ for (i = 0; i < size; i++)
+ data[i] ^= 0xff;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+convert_to_rgb (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size)
+{
+ unsigned int pixels_per_line;
+ unsigned int bytes_per_color;
+ unsigned int bytes_per_line;
+ unsigned int lines;
+ unsigned int i, j;
+ unsigned char *buf;
+ unsigned char *ptr;
+ SANE_Status ret;
+
+ hp5590_assert (scanner != NULL);
+ hp5590_assert (data != NULL);
+
+ if ( scanner->depth == DEPTH_BW
+ || scanner->depth == DEPTH_GRAY)
+ return SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+#ifndef HAS_WORKING_COLOR_48
+ if (scanner->depth == DEPTH_COLOR_48)
+ return SANE_STATUS_UNSUPPORTED;
+#endif
+
+ ret = calc_image_params (scanner,
+ NULL,
+ &pixels_per_line, &bytes_per_line,
+ NULL, NULL);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ lines = size / bytes_per_line;
+ bytes_per_color = bytes_per_line / 3;
+
+ DBG (DBG_verbose, "Length : %u\n", size);
+
+ DBG (DBG_verbose, "Converting row RGB to normal RGB\n");
+
+ DBG (DBG_verbose, "Bytes per line %u\n", bytes_per_line);
+ DBG (DBG_verbose, "Bytes per color %u\n", bytes_per_color);
+ DBG (DBG_verbose, "Lines %u\n", lines);
+
+ buf = malloc (bytes_per_line);
+ if (!buf)
+ return SANE_STATUS_NO_MEM;
+
+ ptr = data;
+ for (j = 0; j < lines; ptr += bytes_per_line, j++)
+ {
+ memset (buf, 0, bytes_per_line);
+ for (i = 0; i < pixels_per_line; i++)
+ {
+ if (scanner->depth == DEPTH_COLOR_24)
+ {
+ /* R */
+ buf[i*3] = ptr[i];
+ /* G */
+ buf[i*3+1] = ptr[i+bytes_per_color];
+ /* B */
+ buf[i*3+2] = ptr[i+bytes_per_color*2];
+ }
+ else
+ {
+ /* R */
+ buf[i*6] = ptr[2*i+1];
+ buf[i*6+1] = ptr[2*i];
+ /* G */
+ buf[i*6+2] = ptr[2*i+bytes_per_color+1];
+ buf[i*6+3] = ptr[2*i+bytes_per_color];
+ /* B */
+ buf[i*6+4] = ptr[2*i+bytes_per_color*2+1];
+ buf[i*6+5] = ptr[2*i+bytes_per_color*2];
+ }
+ }
+ memcpy (ptr, buf, bytes_per_line);
+ }
+ free (buf);
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ struct hp5590_scanner *scanner = handle;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s, length %u, left %u\n",
+ __FUNCTION__,
+ max_length,
+ scanner->transferred_image_size);
+
+ if (!length)
+ {
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scanner->transferred_image_size == 0)
+ {
+ *length = 0;
+ DBG (DBG_verbose, "Setting scan count\n");
+
+ ret = hp5590_inc_scan_count (scanner->dn, scanner->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Dont free bulk read state, some bytes could be left
+ * for the next images from ADF
+ */
+ return SANE_STATUS_EOF;
+ }
+
+ if (!scanner->bulk_read_state)
+ {
+ ret = hp5590_low_init_bulk_read_state (&scanner->bulk_read_state);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ scanner->scanning = SANE_FALSE;
+ return ret;
+ }
+ }
+
+ *length = max_length;
+ if (*length > scanner->transferred_image_size)
+ *length = scanner->transferred_image_size;
+
+ if ( scanner->depth == DEPTH_COLOR_24
+ || scanner->depth == DEPTH_COLOR_48)
+ {
+ unsigned int bytes_per_line;
+ ret = calc_image_params (scanner,
+ NULL, NULL,
+ &bytes_per_line,
+ NULL, NULL);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ *length -= *length % bytes_per_line;
+ DBG (2, "Aligning requested size to bytes per line "
+ "(requested: %u, aligned: %u)\n",
+ max_length, *length);
+ }
+
+ ret = hp5590_read (scanner->dn, scanner->proto_flags,
+ data, *length, scanner->bulk_read_state);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ scanner->scanning = SANE_FALSE;
+ return ret;
+ }
+
+ scanner->transferred_image_size -= *length;
+
+ ret = convert_to_rgb (scanner, data, *length);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ scanner->scanning = SANE_FALSE;
+ return ret;
+ }
+
+ ret = convert_lineart (scanner, data, *length);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+void
+sane_cancel (SANE_Handle handle)
+{
+ struct hp5590_scanner *scanner = handle;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ scanner->scanning = SANE_FALSE;
+
+ if (scanner->dn < 0)
+ return;
+
+ hp5590_low_free_bulk_read_state (&scanner->bulk_read_state);
+
+ ret = hp5590_stop_scan (scanner->dn, scanner->proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return;
+}
+
+/******************************************************************************/
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/******************************************************************************/
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ * fd)
+{
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* vim: sw=2 ts=8
+ */
diff --git a/backend/hp5590_cmds.c b/backend/hp5590_cmds.c
new file mode 100644
index 0000000..894101b
--- /dev/null
+++ b/backend/hp5590_cmds.c
@@ -0,0 +1,2274 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
+ HP ScanJet 4570c support by Markham Thomas
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for
+ HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners
+*/
+
+#include "../include/sane/config.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#include <errno.h>
+#include <string.h>
+
+#include "../include/sane/sanei_debug.h"
+#include "../include/_stdint.h"
+#include "hp5590_low.h"
+#include "hp5590_cmds.h"
+
+static const struct hp5590_model
+hp5590_models[] = {
+ {
+ SCANNER_HP4570,
+ 0x03f0, 0x1305, "SILITEKIElwood",
+ "4570C/5500C", "Workgroup scanner",
+ PF_NONE
+ },
+ {
+ SCANNER_HP5550,
+ 0x03f0, 0x1205, "SILITEKIPenguin",
+ "4500C/5550C", "Workgroup scanner",
+ PF_NO_USB_IN_USB_ACK /* These devices need no
+ * acknowledgement after USB-in-USB
+ * commands */
+ },
+ {
+ SCANNER_HP5590,
+ 0x03f0, 0x1705, "SILITEKIPenguin",
+ "5590", "Workgroup scanner",
+ PF_NONE
+ },
+ {
+ SCANNER_HP7650,
+ 0x03f0, 0x1805, "SILITEKIArnold",
+ "7650", "Document scanner",
+ PF_NONE
+ }
+};
+
+/* Debug levels */
+#define DBG_err 0
+#define DBG_proc 10
+#define DBG_cmds 40
+
+#define hp5590_cmds_assert(exp) if(!(exp)) { \
+ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\
+ return SANE_STATUS_INVAL; \
+}
+
+#define WAKEUP_TIMEOUT 90
+
+/* First byte of init (0x12 cmd) response */
+#define INIT_FLAG_TMA 1 << 0
+#define INIT_FLAG_ADF 1 << 1
+#define INIT_FLAG_LCD 1 << 3
+
+/* Power (0x24 cmd) */
+#define POWER_FLAG_ON 1 << 1
+
+/* ADF (0x03 cmd) */
+#define FLAG_ADF_EMPTY 1 << 1
+
+#define PART_NUMBER_LEN 10
+
+#define REVERSE_MAP_LEN 128 * 1024 / sizeof(uint16_t)
+#define FORWARD_MAP_LEN 128 * 1024 / sizeof(uint16_t)
+
+/* Button flags */
+/* From left to rigth */
+/* 1: Power
+ * 1: Scan
+ * 2: Collect
+ * 3: File
+ * 4: Email
+ * 5: Copy
+ * 6,7: Up/down
+ * 8: Mode
+ * 9: Cancel
+ */
+#define BUTTON_FLAG_EMAIL 1 << 15
+#define BUTTON_FLAG_COPY 1 << 14
+#define BUTTON_FLAG_DOWN 1 << 13
+#define BUTTON_FLAG_MODE 1 << 12
+#define BUTTON_FLAG_UP 1 << 11
+#define BUTTON_FLAG_FILE 1 << 9
+#define BUTTON_FLAG_POWER 1 << 5
+#define BUTTON_FLAG_SCAN 1 << 2
+#define BUTTON_FLAG_COLLECT 1 << 1
+#define BUTTON_FLAG_CANCEL 1 << 0
+
+#define CMD_INIT 0x0012
+#define CMD_EEPROM_ADDR 0x00f2
+#define CMD_EEPROM_READ 0x0bf0
+#define CMD_EEPROM_WRITE 0x0bf1
+#define CMD_DATA_STATUS 0x0001
+#define CMD_STOP_SCAN 0x011b
+#define CMD_CONTROL_LAMP 0x00c0
+#define CMD_POWER_STATUS 0x0024
+#define CMD_SELECT_SOURCE 0x00d6
+#define CMD_MISC_STATUS 0x0003
+#define CMD_LOCK_UNLOCK 0x0000
+#define CMD_SET_BASE_DPI 0x0015
+#define CMD_SET_COLOR_MAP 0x0240
+#define CMD_SET_SCAN_PARAMS 0x0025
+#define CMD_GET_IMAGE_PARAMS 0x0034
+#define CMD_START_SCAN 0x051b
+#define CMD_BUTTON_STATUS 0x0020
+
+struct init_resp
+{
+ uint8_t flags; /* bit 0 - TMA, bit 1 - ADF, bit 3 - LCD present */
+ uint8_t id[15]; /* SILITEKPenguin */
+ uint8_t pad1[9]; /* 00 00 00 00 00 00 00 00 00 */
+ uint8_t version[5]; /* 0.0.67 */
+ uint16_t max_dpi_x; /* 09 60 = 2400 */
+ uint16_t max_dpi_y; /* 09 60 = 2400 */
+ uint16_t max_pixels_x; /* 4F B0 = 20400 (20400 / 2400 = 8.5") */
+ uint16_t max_pixels_y; /* 6D E0 = 28128 (28128 / 2400 = 11.72") */
+ uint8_t pad2[8]; /* 00 00 00 00 00 00 00 00 */
+ uint16_t motor_param_normal; /* 00 64 = 100 */
+ uint16_t motor_param_max; /* 03 E8 = 1000 */
+} __attribute__ ((packed));
+
+struct power_resp
+{
+ uint8_t flags;
+ uint16_t unk1;
+} __attribute__ ((packed));
+
+/*
+ * 215.9 mm x 297.2 mm
+ * 8.5" x 11.72"
+ *
+ * 50 : 425.00 x 586.00
+ * 75 : 637.50 x 879.50
+ * 100 : 850.00 x 1172.00
+ * 150 : 1275.00 x 1758.00 (base DPI)
+ * 200 : 1700.00 x 2344.00
+ * 300 : 2550.00 x 3516.00 (base DPI)
+ * 400 : 3400.00 x 4688.00
+ * 600 : 5100.00 x 7032.00 (base DPI)
+ */
+
+#define SCAN_PARAMS_SOURCE_TMA_NEGATIVES 1 << 0
+#define SCAN_PARAMS_SOURCE_TMA_SLIDES 1 << 1
+#define SCAN_PARAMS_SOURCE_ADF 1 << 2
+#define SCAN_PARAMS_SOURCE_FLATBED 1 << 3
+#define SCAN_PARAMS_SOURCE_SIMPLEX 1 << 4
+#define SCAN_PARAMS_SOURCE_DUPLEX 1 << 6
+
+struct scan_params
+{
+ uint8_t source; /*
+ * TMA Negatives : 11 = 17
+ * TMA Slides : 12 = 18
+ * ADF : 14 = 20
+ * Flatbed : 18 = 24
+ * ADF Duplex : 54 = 84
+ */
+
+ uint16_t dpi_x; /*
+ * 50 : 00 64 = 100
+ * 75 : 00 64 = 100
+ * 100 : 00 64 = 100
+ * 150 : 00 c8 = 200
+ * 200 : 00 c8 = 200
+ * 300 : 01 2c = 300
+ * 400 : 02 58 = 600
+ * 600 : 02 58 = 600
+ * 1200 : 04 b0 = 1200
+ */
+
+ uint16_t dpi_y; /*
+ * 50 : 00 64 = 100
+ * 75 : 00 64 = 100
+ * 100 : 00 64 = 100
+ * 150 : 00 c8 = 200
+ * 200 : 00 c8 = 200
+ * 300 : 01 2c = 300
+ * 400 : 02 58 = 600
+ * 600 : 02 58 = 600
+ * 1200 : 04 b0 = 1200
+ */
+
+ uint16_t top_x; /*
+ * pixels * (Base DPI / current DPI)
+ * 00 00, 01 6e = 366 (x = 425 - 302 = 123)
+ * 04 b0 = 1200 (x = 425 - 24 = 401)
+ */
+
+ uint16_t top_y; /*
+ * pixels * (Base DPI / current DPI)
+ * 00 00, 06 99 = 1689 (y = 585 - 21 = 564)
+ */
+
+ uint16_t size_x; /* X pixels in Base DPI (CMD 15)
+ * 50 : 04f8 = 1272 ; 150
+ * 75 : 04f8 = 1272 ; 150
+ * 100 : 04f8 = 1272 ; 150
+ * 100 TMA : 00fc = 252 ; 150
+ * 150 : 09f6 = 2550, 09f0 = 2544, 09f4 = 2548 ; 300
+ * 200 : 09f0 = 2544, 09f6 = 2550, 09f6 = 2550 ; 300
+ * 300 : 09f6 = 2550, 09f0 = 2544, 09f4 = 2548 ; 300
+ * 300 TMA : 01fc = 508 ; 300
+ * 400 : 13ec = 5100 ; 600
+ * 600 : 13e8 = 5096, 13ec = 5100 ,13ec = 5100 ; 600
+ * 1200 : 27a8 = 10152 ; 1200
+ */
+
+ uint16_t size_y; /* Y pixels in Base DPI (CMD 15)
+ * 50 : 06db = 1755 ; 150
+ * 75 : 06da = 1754 ; 150
+ * 100 : 06db = 1755 ; 150
+ * 100 TMA : 0384 = 900 ; 150
+ * 150 : 0db6 = 3510 ; 300
+ * 200 : 0db6 = 3510 ; 300
+ * 300 : 0db6 = 3510 ; 300
+ * 300 TMA : 0708 = 1800 ; 300
+ * 400 : 1b6c = 7020 ; 600
+ * 600 : 1b6c = 7020 ; 600
+ * 1200 : 36d8 = 14040 ; 1200
+ */
+
+ uint16_t unk1; /* 00 80 */
+
+ uint16_t bw_gray_flag; /*
+ * 00 40 - bw (ntsc gray)/gray,
+ * 00 20 - bw (by green band),
+ * 00 10 - bw (by red band),
+ * 00 30 - bw (by blue band),
+ * 00 00 - color
+ */
+
+ uint8_t pixel_bits; /*
+ * bw 50/75/150/400 : 08 = 8
+ * bw 100/200/300/600/1200 : 01 = 1
+ * gray 50/75/100/150/200/400/600 : 08 = 8
+ * color 24 bit 50/75/100/150/200/400/600 : 18 = 24
+ * color 48 bit 100/200 : 30 = 48
+ */
+
+ uint16_t flags; /*
+ * 50/75/100/150/200/300 : e8 40 = 59456
+ * 400/600/1200 : c8 40 = 51264
+ */
+
+ uint16_t motor_param1; /*
+ * 00 64 = 100
+ */
+ uint16_t motor_param2; /*
+ * 00 64 = 100 - ADF, Flatbed, TMA slides
+ * 00 c8 = 200 - TMA Negatives
+ */
+ uint16_t motor_param3; /*
+ * 00 64 = 100 - ADF, Flatbed, TMA slides
+ * 01 90 = 400 - TMA negatives
+ */
+ uint32_t pad1; /* 00 00 00 00 */
+ uint16_t pad2; /* 00 00 */
+ uint8_t mode; /* 00 - normal scan, 04 - preview scan */
+ uint16_t pad3; /* 00 00 */
+
+ uint16_t line_width; /* Based on current .dpi_x
+ * bw 50 : 03 50 = 848
+ * gray 50 : 03 50 = 848
+ * color 50 : 09 f0 = 2544 (3 * gray)
+ *
+ * bw 75 : 03 50 = 848
+ * gray 75 : 03 50 = 848
+ * color 75 : 09 f0 = 2544 (3 * gray)
+ *
+ * bw 100 : 00 6a = 106
+ * gray 100 : 03 50 = 848 (8 * bw)
+ * color 100(24) : 09 f0 = 2544 (3 * gray)
+ * color 100(48) : 13 e0 = 5088 (2 * color 24)
+ * color 100(48) TMA : 03 f0 = 1008
+ *
+ * bw 150 : 06 a4 = 1700
+ * gray 150 : 06 a4 = 1700
+ * color 150 : 13 ec = 5100 (3 * gray)
+ *
+ * bw 200 : 00 d4 = 212
+ * gray 200 : 06 a4 = 1700 (8 * bw)
+ * color 200(24) : 13 ec = 5100 (3 * gray)
+ * color 200(48) : 27 a8 = 10152
+ *
+ * bw 300 : 01 3e = 318
+ * gray 300 : 09 f4 = 2548 (8 * bw)
+ * color 300 : 1d dc = 7644 (3 * gray)
+ * color 300(48) TMA : 0b e8 = 3048
+ *
+ * bw 400 : 13 ec = 5100
+ * gray 400 : 13 ec = 5100
+ * color 400 : 3b c4 = 15300 (3 * gray)
+ *
+ * bw 600 : 02 7d = 637
+ * gray 600 : 13 ec = 5100 (8 * bw)
+ * color 600 : 3b c4 = 15300 (3 * gray)
+ *
+ * bw 1200 : 04 f5 = 1269
+ */
+} __attribute__ ((packed));
+
+struct image_params
+{
+ uint8_t signature; /* c0 */
+ uint8_t pad1; /* 00 */
+ uint32_t image_size; /*
+ * bw 50 : 00 0f 23 a0 = 992 160
+ * gray 50 : 00 0f 23 a0 = 992 160
+ * color 50 : 00 2d 6a e0 = 2 976 480
+ *
+ * bw 75 : 00 0f 20 50 = 991 312
+ * gray 75 : 00 0f 20 50 = 991 312
+ * color 75 : 00 2d 60 f0 = 2 973 936
+ * color 75(48) : 00 5a 86 40 = 5 932 608
+ *
+ * bw 100 : 00 01 e4 74 = 124 020
+ * gray 100 : 00 0f 23 a0 = 992 160
+ * color 100 : 00 2d 6a e0 = 2 976 480
+ * color 100(48) : 00 5a 68 10 = 5 924 880
+ * color 100(48), preview: 00 5a d5 c0 = 5 952 960
+ *
+ * bw 150 : 00 3c b3 10 = 3 978 000
+ * gray 150 : 00 3c b3 10 = 3 978 000
+ * color 150 : 00 b6 19 30 = 11 934 000
+ * color 150(48) : 01 6a 7b a0 = 23 755 680
+ *
+ * bw 200 : 00 07 91 d0 = 496 080
+ * gray 200 : 00 3c b3 10 = 3 978 000
+ * color 200 : 00 b6 19 30 = 11 934 000
+ * color 200(48) : 01 6a f3 a0 = 23 786 400
+ *
+ * bw 300 : 00 11 08 14 = 1 116 180
+ * gray 300 : 00 88 77 78 = 8 943 480
+ * color 300 : 01 99 66 68 = 26 830 440
+ *
+ * bw 400 : 02 22 4b 90 = 35 802 000
+ * gray 400 : 02 22 4b 90 = 35 802 000
+ * color 400 : 06 66 e2 b0 = 107 406 000
+ *
+ * bw 600 : 00 44 3b bc = 4 471 740
+ * gray 600 : 02 22 4b 90 = 35 802 000
+ * color 600 : 06 66 e2 b0 = 107 406 000
+ */
+ uint16_t pad2; /* 00 00 */
+ uint16_t line_width;
+ uint16_t real_size_y;
+ uint32_t pad3; /* 00 00 00 00 */
+} __attribute__ ((packed));
+
+struct lamp_state
+{
+ uint8_t unk1; /* 02 */
+ uint8_t flag; /* 01 on start, 02 - TMA, 03 - all other */
+ uint16_t turnoff_time; /* 0a 0a, 03 36, 0f 36 */
+} __attribute__ ((packed));
+
+struct color_map
+{
+ uint8_t color1[6]; /* 00 00 00 00 01 00 */
+ uint8_t color2[6]; /* 00 00 00 00 01 00 */
+ uint8_t color3[6]; /* 00 00 00 00 01 00 */
+} __attribute__ ((packed));
+
+struct reg_03
+{
+ uint8_t unk1; /* 0x0b - ADF ready, 0x03 - not */
+ uint8_t unk2; /* 0x80 */
+ uint8_t adf_flags; /* 0x01 - ADF ready when selected, 0x02 - not */
+} __attribute__ ((packed));
+
+/******************************************************************************/
+static SANE_Status
+hp5590_model_def (enum hp_scanner_types scanner_type,
+ const struct hp5590_model ** model)
+{
+ unsigned int i;
+
+ hp5590_cmds_assert (model != NULL);
+
+ for (i = 0; i < sizeof (hp5590_models) / sizeof (struct hp5590_model); i++)
+ {
+ if (hp5590_models[i].scanner_type == scanner_type)
+ {
+ *model = &hp5590_models[i];
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_vendor_product_id (enum hp_scanner_types scanner_type,
+ SANE_Word * vendor_id,
+ SANE_Word * product_id)
+{
+ const struct hp5590_model *model;
+ SANE_Status ret;
+
+ hp5590_cmds_assert (vendor_id != NULL);
+ hp5590_cmds_assert (product_id != NULL);
+
+ ret = hp5590_model_def (scanner_type, &model);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ *vendor_id = model->usb_vendor_id;
+ *product_id = model->usb_product_id;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_init_scanner (SANE_Int dn,
+ enum proto_flags proto_flags,
+ struct scanner_info ** info,
+ enum hp_scanner_types scanner_type)
+{
+ struct init_resp init_resp;
+ char id_buf[sizeof (init_resp.id) + 1];
+ char ver_buf[sizeof (init_resp.version) + 1];
+ SANE_Status ret;
+ const struct hp5590_model *scanner_model;
+
+ /*
+ * 0A 53 49 4C 49 54 45 4B 49 50 65 6E 67 75 69 6E .SILITEKIPenguin
+ * 00 00 00 00 00 00 00 00 00 30 2E 30 36 37 09 60 .........0.067..
+ * 09 60 4F B0 6D E0 00 00 00 00 00 00 00 00 00 64 ..O.m..........d
+ * 03 E8 ..
+ */
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (init_resp) == 50);
+
+ /* Init scanner */
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN | CMD_VERIFY,
+ CMD_INIT,
+ (unsigned char *) &init_resp,
+ sizeof (init_resp), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ memset (id_buf, 0, sizeof (id_buf));
+ memcpy (id_buf, init_resp.id, sizeof (id_buf) - 1);
+ scanner_model = NULL;
+ if (scanner_type != SCANNER_NONE)
+ {
+ unsigned int i;
+ for (i = 0; i < sizeof (hp5590_models) / sizeof (struct hp5590_model);
+ i++)
+ {
+ if (hp5590_models[i].scanner_type == scanner_type)
+ {
+ if (strcmp (id_buf, hp5590_models[i].vendor_id) != 0)
+ {
+ DBG (DBG_err, "%s: Vendor id mismatch for scanner HP%s - "
+ "required '%s', got '%s'\n",
+ __FUNCTION__,
+ hp5590_models[i].model,
+ hp5590_models[i].vendor_id, id_buf);
+ return SANE_STATUS_INVAL;
+ }
+ scanner_model = &hp5590_models[i];
+ break;
+ }
+ }
+ hp5590_cmds_assert (scanner_model != NULL);
+ }
+
+ if (scanner_model)
+ {
+ DBG (DBG_cmds, "HP%s flags (0x%02x)\n", scanner_model->model, init_resp.flags);
+
+ DBG (DBG_cmds, "HP%s flags: ADF %s, TMA %s, LCD %s\n",
+ scanner_model->model,
+ init_resp.flags & INIT_FLAG_ADF ? "yes" : "no",
+ init_resp.flags & INIT_FLAG_TMA ? "yes" : "no",
+ init_resp.flags & INIT_FLAG_LCD ? "yes" : "no");
+
+
+ memset (ver_buf, 0, sizeof (ver_buf));
+ memcpy (ver_buf, init_resp.version, sizeof (ver_buf) - 1);
+ DBG (DBG_cmds, "HP%s firmware version: %s\n", scanner_model->model, ver_buf);
+
+ DBG (DBG_cmds, "HP%s max resolution X: %u DPI\n",
+ scanner_model->model, ntohs (init_resp.max_dpi_x));
+ DBG (DBG_cmds, "HP%s max resolution Y: %u DPI\n",
+ scanner_model->model, ntohs (init_resp.max_dpi_y));
+ DBG (DBG_cmds, "HP%s max pixels X: %u\n",
+ scanner_model->model, ntohs (init_resp.max_pixels_x));
+ DBG (DBG_cmds, "HP%s max pixels Y: %u\n",
+ scanner_model->model, ntohs (init_resp.max_pixels_y));
+ DBG (DBG_cmds, "HP%s max size X: %.3f inches\n",
+ scanner_model->model,
+ ntohs (init_resp.max_pixels_x) * 1.0 /
+ ntohs (init_resp.max_dpi_x));
+ DBG (DBG_cmds, "HP%s max size Y: %.3f inches\n", scanner_model->model,
+ ntohs (init_resp.max_pixels_y) * 1.0 /
+ ntohs (init_resp.max_dpi_y));
+ DBG (DBG_cmds, "HP%s normal motor param: %u, max motor param: %u\n",
+ scanner_model->model, ntohs (init_resp.motor_param_normal),
+ ntohs (init_resp.motor_param_max));
+ }
+
+ if (info)
+ {
+ *info = malloc (sizeof (struct scanner_info));
+ if (!*info)
+ {
+ DBG (DBG_err, "Memory allocation failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (*info, 0, sizeof (struct scanner_info));
+
+ (*info)->max_dpi_x = ntohs (init_resp.max_dpi_x);
+ (*info)->max_dpi_y = ntohs (init_resp.max_dpi_y);
+ (*info)->max_pixels_x = ntohs (init_resp.max_pixels_x) - 1;
+ (*info)->max_pixels_y = ntohs (init_resp.max_pixels_y) + 1;
+ (*info)->max_size_x = (*info)->max_pixels_x * 1.0 / (*info)->max_dpi_x;
+ (*info)->max_size_y = (*info)->max_pixels_y * 1.0 / (*info)->max_dpi_y;
+ (*info)->features = FEATURE_NONE;
+ if (init_resp.flags & INIT_FLAG_LCD)
+ (*info)->features |= FEATURE_LCD;
+ if (init_resp.flags & INIT_FLAG_ADF)
+ (*info)->features |= FEATURE_ADF;
+ if (init_resp.flags & INIT_FLAG_TMA)
+ (*info)->features |= FEATURE_TMA;
+ if (scanner_model)
+ {
+ (*info)->model = scanner_model->model;
+ (*info)->kind = scanner_model->kind;
+ }
+ }
+
+ ret = hp5590_get_status (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: scanner reports non-zero status: %s\n",
+ __FUNCTION__, sane_strstatus (ret));
+ return ret;
+ }
+ DBG (DBG_cmds, "%s: scanner status OK\n", __FUNCTION__);
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_eeprom (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int addr,
+ unsigned char *data, unsigned int size)
+{
+ uint8_t eeprom_addr = addr;
+ SANE_Status ret;
+
+ hp5590_cmds_assert (data != NULL);
+ hp5590_cmds_assert (sizeof (eeprom_addr) == 1);
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ DBG (DBG_proc, "Reading EEPROM: addr %04x, size %u\n", addr, size);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_EEPROM_ADDR,
+ (unsigned char *) &eeprom_addr,
+ sizeof (eeprom_addr), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN | CMD_VERIFY,
+ CMD_EEPROM_READ, data, size, CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_write_eeprom (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int addr,
+ unsigned char *data, unsigned int size)
+{
+ uint8_t eeprom_addr = addr;
+ SANE_Status ret;
+
+ hp5590_cmds_assert (data != NULL);
+ hp5590_cmds_assert (sizeof (eeprom_addr) == 1);
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ DBG (DBG_proc, "Writing EEPROM: addr %04x, size: %u\n", addr, size);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_EEPROM_ADDR,
+ (unsigned char *) &eeprom_addr,
+ sizeof (eeprom_addr), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_EEPROM_WRITE, data, size, CORE_DATA);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_scan_count (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int *count)
+{
+ uint32_t scan_count;
+ SANE_Status ret;
+
+ hp5590_cmds_assert (count != NULL);
+ hp5590_cmds_assert (sizeof (scan_count) == 4);
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ DBG (DBG_proc, "Reading scan count\n");
+
+ ret = hp5590_read_eeprom (dn,
+ proto_flags,
+ 0x00,
+ (unsigned char *) &scan_count,
+ sizeof (scan_count));
+
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Host order */
+ *count = scan_count;
+
+ DBG (DBG_proc, "Scan count %u\n", *count);
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_inc_scan_count (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint32_t scan_count;
+ unsigned int count;
+ unsigned int new_count;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ hp5590_cmds_assert (sizeof (scan_count) == 4);
+
+ ret = hp5590_read_scan_count (dn, proto_flags, &count);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ scan_count = ++count;
+
+ ret = hp5590_write_eeprom (dn,
+ proto_flags,
+ 0x00,
+ (unsigned char *) &scan_count,
+ sizeof (scan_count));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Verify its setting */
+ ret = hp5590_read_scan_count (dn, proto_flags, &new_count);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (count != new_count)
+ {
+ DBG (DBG_err, "Scan count wasn't set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_max_scan_count (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int *max_count)
+{
+ uint8_t max_scan_count[3];
+ SANE_Status ret;
+
+ hp5590_cmds_assert (max_count != NULL);
+ hp5590_cmds_assert (sizeof (max_scan_count) == 3);
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ DBG (DBG_proc, "Reading max scan count\n");
+
+ ret = hp5590_read_eeprom (dn,
+ proto_flags,
+ 0x10,
+ (unsigned char *) max_scan_count,
+ sizeof (max_scan_count));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Host order */
+ *max_count = 0;
+ memcpy (max_count, max_scan_count, sizeof (max_scan_count));
+
+ DBG (DBG_proc, "Max scan count %u\n", *max_count);
+
+ return SANE_STATUS_GOOD;
+}
+
+/***************************************************************************
+ *
+ * EEPROM contents:
+ *
+ * 0000: 6A 11 00 00 FF FF FF FF FF FF FF FF 09 0E 0F 00 j...............
+ * 0010: 0C 13 0F 00 00 3A 00 FF FF FF 4E 35 39 45 54 52 ..........N59ETR
+ * 0020: 31 52 4D 00 FF FF 00 16 00 0A 00 0D 00 11 00 10 1RM.............
+ * 0030: FF FF FF FF FF FF FF FF FF 00 FF FF FF FF FF FF ................
+ * 0040: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 0050: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 0060: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 0070: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 0080: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 0090: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 00A0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 00B0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 00C0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 00D0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 00E0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+ * 00F0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ...............
+ *
+ * Addr 0x00, len: 0x04 - scan count (little-endian)
+ * Addr 0x1A, len: 0x0A - part number (w/o first 'CN' letters)
+ * Addr 0x10, len: 0x03 - max scan count (little-endian) (0C 13 0F)
+ *
+ */
+
+/******************************************************************************/
+static __sane_unused__ SANE_Status
+hp5590_read_eeprom_all_cmd (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint8_t eeprom[255];
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ ret = hp5590_read_eeprom (dn,
+ proto_flags,
+ 0x00,
+ (unsigned char *) eeprom,
+ sizeof (eeprom));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* FIXME: Debug output */
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_part_number (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ unsigned int part_number_len = PART_NUMBER_LEN;
+ unsigned char part_number[PART_NUMBER_LEN + 1];
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ memset (part_number, 0, sizeof (part_number));
+ ret = hp5590_read_eeprom (dn,
+ proto_flags,
+ 0x1a,
+ part_number, part_number_len);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (DBG_cmds, "Part number: '%s'\n", part_number);
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_is_data_available (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint8_t data_status;
+ SANE_Status ret;
+ SANE_Bool data_available;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (data_status) == 1);
+ data_available = SANE_FALSE;
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN | CMD_VERIFY,
+ CMD_DATA_STATUS,
+ (unsigned char *) &data_status,
+ sizeof (data_status), CORE_DATA);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (DBG_cmds, "%s: Data status: %02x\n", __FUNCTION__, data_status);
+
+ if (data_status == 0x40)
+ data_available = SANE_TRUE;
+
+ DBG (DBG_cmds, "%s: Data is %s\n",
+ __FUNCTION__,
+ data_available == SANE_TRUE ? "available" : "not available");
+
+ return data_available == SANE_TRUE ? SANE_STATUS_GOOD : SANE_STATUS_NO_DOCS;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_stop_scan (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint8_t reg_011b = 0x40;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (reg_011b) == 1);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_STOP_SCAN,
+ (unsigned char *) &reg_011b,
+ sizeof (reg_011b), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* FIXME */
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_turnon_lamp (SANE_Int dn,
+ enum proto_flags proto_flags,
+ enum hp5590_lamp_state state)
+{
+ struct lamp_state lamp_state;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (lamp_state) == 4);
+
+ if (state == LAMP_STATE_TURNON)
+ {
+ /* Turn on lamp */
+ lamp_state.unk1 = 0x02;
+ lamp_state.flag = 0x01;
+ lamp_state.turnoff_time = htons (0x0a0a);
+ DBG (DBG_cmds, "%s: turning lamp on\n", __FUNCTION__);
+ }
+
+ if (state == LAMP_STATE_TURNOFF)
+ {
+ /* Turn off lamp */
+ lamp_state.unk1 = 0x02;
+ lamp_state.flag = 0x02;
+ lamp_state.turnoff_time = htons (0x0a0a);
+ DBG (DBG_cmds, "%s: turning lamp off\n", __FUNCTION__);
+ }
+
+ if (state == LAMP_STATE_SET_TURNOFF_TIME)
+ {
+ /* Turn on lamp */
+ lamp_state.unk1 = 0x02;
+ lamp_state.flag = 0x03;
+ lamp_state.turnoff_time = htons (0x0336);
+ DBG (DBG_cmds, "%s: setting turnoff time\n", __FUNCTION__);
+ }
+
+ if (state == LAMP_STATE_SET_TURNOFF_TIME_LONG)
+ {
+ /* Turn on lamp */
+ lamp_state.unk1 = 0x02;
+ lamp_state.flag = 0x03;
+ lamp_state.turnoff_time = htons (0x0f36);
+ DBG (DBG_cmds, "%s: setting long turnoff time\n", __FUNCTION__);
+ }
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_CONTROL_LAMP,
+ (unsigned char *) &lamp_state,
+ sizeof (lamp_state), CORE_DATA);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (state == LAMP_STATE_TURNON)
+ {
+ ret = hp5590_init_scanner (dn, proto_flags, NULL, SCANNER_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_power_status (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ struct power_resp power_resp;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (power_resp) == 3);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN | CMD_VERIFY,
+ CMD_POWER_STATUS,
+ (unsigned char *) &power_resp,
+ sizeof (power_resp), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (DBG_cmds, "Power status: %s (%02x)\n",
+ power_resp.flags & POWER_FLAG_ON ? "on" : "off", power_resp.flags);
+
+ if (!(power_resp.flags & POWER_FLAG_ON))
+ {
+ DBG (DBG_cmds, "Turning lamp on\n");
+ ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNON);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_error_code (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int *adf_flags)
+{
+ struct reg_03 reg_03;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (reg_03) == 3);
+ hp5590_cmds_assert (adf_flags != NULL);
+
+ memset (&reg_03, 0, sizeof (reg_03));
+ *adf_flags = 0;
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN,
+ CMD_MISC_STATUS,
+ (unsigned char *) &reg_03,
+ sizeof (reg_03), CORE_NONE);
+
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (DBG_cmds, "%s: adf_flags: %04x\n", __FUNCTION__, reg_03.adf_flags);
+ DBG (DBG_cmds, "%s: unk1 : %04x\n", __FUNCTION__, reg_03.unk1);
+ DBG (DBG_cmds, "%s: unk2 : %04x\n", __FUNCTION__, reg_03.unk2);
+
+ *adf_flags = reg_03.adf_flags;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_reset_scan_head (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNOFF);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ usleep (100 * 1000);
+
+ ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNON);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_select_source_and_wakeup (SANE_Int dn,
+ enum proto_flags proto_flags,
+ enum scan_sources source,
+ SANE_Bool extend_lamp_timeout)
+{
+ uint8_t reg_d6 = 0x04;
+ SANE_Status ret;
+ unsigned int adf_flags;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (reg_d6) == 1);
+
+ if (source == SOURCE_TMA_SLIDES || source == SOURCE_TMA_NEGATIVES)
+ {
+ ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNOFF);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+ else
+ {
+ ret = hp5590_turnon_lamp (dn,
+ proto_flags,
+ extend_lamp_timeout == SANE_TRUE ?
+ LAMP_STATE_SET_TURNOFF_TIME_LONG :
+ LAMP_STATE_SET_TURNOFF_TIME);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ switch (source)
+ {
+ case SOURCE_ADF:
+ case SOURCE_ADF_DUPLEX:
+ reg_d6 = 0x03;
+ break;
+ case SOURCE_FLATBED:
+ reg_d6 = 0x04;
+ break;
+ case SOURCE_TMA_SLIDES:
+ reg_d6 = 0x02;
+ break;
+ case SOURCE_TMA_NEGATIVES:
+ reg_d6 = 0x01;
+ break;
+ case SOURCE_NONE:
+ DBG (DBG_err, "Scan source not selected\n");
+ return SANE_STATUS_INVAL;
+ default:
+ DBG (DBG_err, "Unknown scan source: %u\n", source);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (DBG_cmds, "Scan source: %u\n", reg_d6);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_SELECT_SOURCE,
+ (unsigned char *) &reg_d6,
+ sizeof (reg_d6), CORE_NONE);
+
+ if (ret != SANE_STATUS_GOOD && ret != SANE_STATUS_DEVICE_BUSY)
+ return ret;
+
+ ret = hp5590_read_error_code (dn, proto_flags, &adf_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (adf_flags & FLAG_ADF_EMPTY)
+ {
+ DBG (DBG_cmds, "ADF empty\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_lock_unlock_scanner (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint8_t reg_00 = 0x01;
+ SANE_Status ret;
+ unsigned int adf_flags;
+ unsigned int waiting;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ hp5590_cmds_assert (sizeof (reg_00) == 1);
+
+ for (waiting = 0; waiting < WAKEUP_TIMEOUT; waiting++, sleep (1))
+ {
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_LOCK_UNLOCK,
+ (unsigned char *) &reg_00,
+ sizeof (reg_00), CORE_NONE);
+ if (ret == SANE_STATUS_GOOD)
+ break;
+
+ if (ret != SANE_STATUS_DEVICE_BUSY)
+ return ret;
+
+ DBG (DBG_cmds, "Waiting for scanner...\n");
+ ret = hp5590_read_error_code (dn, proto_flags, &adf_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (adf_flags & FLAG_ADF_EMPTY)
+ {
+ DBG (DBG_cmds, "ADF empty\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ if (waiting == WAKEUP_TIMEOUT)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_set_base_dpi (SANE_Int dn,
+ enum proto_flags proto_flags,
+ struct scanner_info *scanner_info,
+ unsigned int base_dpi)
+{
+ uint16_t _base_dpi;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (scanner_info != NULL);
+ hp5590_cmds_assert (base_dpi != 0);
+ hp5590_cmds_assert (sizeof (_base_dpi) == 2);
+
+ if (base_dpi > scanner_info->max_dpi_x
+ || base_dpi > scanner_info->max_dpi_y)
+ {
+ DBG (DBG_err, "Base DPI too large "
+ "(given: %u, max X DPI: %u, max Y DPI: %u)\n",
+ base_dpi,
+ scanner_info->max_dpi_x,
+ scanner_info->max_dpi_y);
+ return SANE_STATUS_INVAL;
+ }
+
+ _base_dpi = htons (base_dpi);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_SET_BASE_DPI,
+ (unsigned char *) &_base_dpi,
+ sizeof (_base_dpi), CORE_DATA);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_set_color_map (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int base_dpi)
+{
+ struct color_map color_map;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (color_map) == 18);
+ hp5590_cmds_assert (base_dpi != 0);
+
+ memset (&color_map, 0, sizeof (color_map));
+ if (base_dpi < 2400)
+ {
+ color_map.color1[4] = 0x01;
+ color_map.color2[4] = 0x01;
+ color_map.color3[4] = 0x01;
+ }
+ else
+ {
+ color_map.color1[2] = 0xff;
+ color_map.color1[3] = 0x01;
+ color_map.color1[4] = 0x04;
+ color_map.color1[5] = 0x02;
+
+ color_map.color2[2] = 0xff;
+ color_map.color2[3] = 0x01;
+ color_map.color2[4] = 0x04;
+ color_map.color2[5] = 0x02;
+
+ color_map.color3[2] = 0xff;
+ color_map.color3[3] = 0x01;
+ color_map.color3[4] = 0x04;
+ color_map.color3[5] = 0x02;
+ }
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_SET_COLOR_MAP,
+ (unsigned char *) &color_map,
+ sizeof (color_map), CORE_DATA);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************
+ * Calculate base DPI
+ * Base DPI is what image dimensions are calculated on (top X,Y; bottom X,Y)
+ * Calculated according the following rules:
+ * Base DPI is 150 when 0 < DPI < 150;
+ * Base DPI is 300 when 150 <= DPI <= 300;
+ * Base DPI is 600 when 300 < DPI <= 600;
+ * Base DPI is 1200 when 600 < DPI;
+ */
+static SANE_Status
+calc_base_dpi (unsigned int dpi, unsigned int *base_dpi)
+{
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (base_dpi != NULL);
+ hp5590_cmds_assert (dpi != 0);
+
+ *base_dpi = 0;
+
+ if (dpi < 150)
+ {
+ *base_dpi = 150;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi >= 150 && dpi <= 300)
+ {
+ *base_dpi = 300;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 300 && dpi <= 600)
+ {
+ *base_dpi = 600;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 600 && dpi <= 1200)
+ {
+ *base_dpi = 1200;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 1200 && dpi <= 2400)
+ {
+ *base_dpi = 2400;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (DBG_err, "Error calculating base DPI (given DPI: %u)\n", dpi);
+ return SANE_STATUS_INVAL;
+}
+
+/******************************************************************************/
+static SANE_Status
+calc_scanner_dpi (unsigned int dpi, unsigned int *scanner_dpi)
+{
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (scanner_dpi != NULL);
+ hp5590_cmds_assert (dpi != 0);
+
+ if (dpi <= 100)
+ {
+ *scanner_dpi = 100;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 100 && dpi <= 200)
+ {
+ *scanner_dpi = 200;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi == 300)
+ {
+ *scanner_dpi = 300;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 300 && dpi <= 600)
+ {
+ *scanner_dpi = 600;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 600 && dpi <= 1200)
+ {
+ *scanner_dpi = 1200;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (dpi > 1200 && dpi <= 2400)
+ {
+ *scanner_dpi = 2400;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (DBG_err, "Error calculating scanner DPI (given DPI: %u)\n", dpi);
+ return SANE_STATUS_INVAL;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_calc_pixel_bits (unsigned int dpi, enum color_depths color_depth,
+ unsigned int *pixel_bits)
+{
+ unsigned int scanner_dpi;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (pixel_bits != NULL);
+ hp5590_cmds_assert (dpi != 0);
+
+ ret = calc_scanner_dpi (dpi, &scanner_dpi);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (color_depth == DEPTH_COLOR_48)
+ {
+ *pixel_bits = 48;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (color_depth == DEPTH_COLOR_24)
+ {
+ *pixel_bits = 24;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (color_depth == DEPTH_GRAY)
+ {
+ *pixel_bits = 8;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (color_depth == DEPTH_BW)
+ {
+ if (dpi == scanner_dpi)
+ *pixel_bits = 1;
+ else
+ *pixel_bits = 8;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (DBG_err, "Error calculating pixel bits (given DPI: %u)\n", dpi);
+ return SANE_STATUS_INVAL;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_set_scan_area (SANE_Int dn,
+ enum proto_flags proto_flags,
+ struct scanner_info *scanner_info,
+ unsigned int top_x, unsigned int top_y,
+ unsigned int width, unsigned int height,
+ unsigned int dpi, enum color_depths color_depth,
+ enum scan_modes scan_mode,
+ enum scan_sources scan_source)
+{
+ struct scan_params scan_params;
+ unsigned int scanner_top_x;
+ unsigned int scanner_top_y;
+ unsigned int scanner_pixels_x;
+ unsigned int scanner_pixels_y;
+ unsigned int base_dpi;
+ unsigned int scanner_dpi;
+ unsigned int pixel_bits;
+ unsigned int scanner_line_width;
+ unsigned int max_pixels_x_current_dpi;
+ unsigned int max_pixels_y_current_dpi;
+ unsigned int pixels_x;
+ unsigned int pixels_y;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (scan_params) == 37);
+ hp5590_cmds_assert (dpi != 0);
+ hp5590_cmds_assert (scanner_info != NULL);
+
+ memset (&scan_params, 0, sizeof (scan_params));
+
+ scan_params.source = SCAN_PARAMS_SOURCE_SIMPLEX;
+ if (scan_source == SOURCE_ADF)
+ scan_params.source |= SCAN_PARAMS_SOURCE_ADF;
+ if (scan_source == SOURCE_ADF_DUPLEX)
+ scan_params.source |= SCAN_PARAMS_SOURCE_ADF | SCAN_PARAMS_SOURCE_DUPLEX;
+ if (scan_source == SOURCE_FLATBED)
+ scan_params.source |= SCAN_PARAMS_SOURCE_FLATBED;
+ if (scan_source == SOURCE_TMA_SLIDES)
+ scan_params.source |= SCAN_PARAMS_SOURCE_TMA_SLIDES;
+ if (scan_source == SOURCE_TMA_NEGATIVES)
+ scan_params.source |= SCAN_PARAMS_SOURCE_TMA_NEGATIVES;
+
+ DBG (DBG_cmds, "Scan params. source : 0x%04x\n", scan_params.source);
+
+ DBG (DBG_cmds, "DPI: %u\n", dpi);
+ if (dpi > scanner_info->max_dpi_x || dpi > scanner_info->max_dpi_y)
+ {
+ DBG (DBG_err, "DPI too large "
+ "(given: %u, max X DPI: %u, max Y DPI: %u)\n",
+ dpi, scanner_info->max_dpi_x, scanner_info->max_dpi_y);
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = calc_base_dpi (dpi, &base_dpi);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ DBG (DBG_cmds, "Base DPI: %u\n", base_dpi);
+
+ ret = calc_scanner_dpi (dpi, &scanner_dpi);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ DBG (DBG_cmds, "Scanner DPI: %u\n", scanner_dpi);
+
+ scan_params.dpi_x = htons (scanner_dpi);
+ scan_params.dpi_y = htons (scanner_dpi);
+
+ DBG (DBG_cmds, "DPI X: 0x%04x\n", scanner_dpi);
+ DBG (DBG_cmds, "DPI Y: 0x%04x\n", scanner_dpi);
+
+ ret = hp5590_calc_pixel_bits (dpi, color_depth, &pixel_bits);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ DBG (DBG_cmds, "Pixel bits: %u\n", pixel_bits);
+
+ scan_params.pixel_bits = pixel_bits;
+
+ scan_params.bw_gray_flag = 0;
+ if (color_depth == DEPTH_BW || color_depth == DEPTH_GRAY)
+ scan_params.bw_gray_flag = htons (0x40);
+
+ scan_params.flags = htons (0xe840);
+ if (dpi > 300 && dpi <= 1200)
+ scan_params.flags = htons (0xc840);
+ if (dpi > 1200)
+ scan_params.flags = htons (0xc040);
+
+ scan_params.motor_param1 = htons (100);
+ scan_params.motor_param2 = htons (100);
+ scan_params.motor_param3 = htons (100);
+ if (scan_source == SOURCE_TMA_NEGATIVES)
+ {
+ scan_params.motor_param2 = htons (200);
+ scan_params.motor_param3 = htons (400);
+ }
+
+ scan_params.unk1 = htons (0x80);
+
+ scan_params.mode = 0;
+ if (scan_mode == MODE_PREVIEW)
+ scan_params.mode = 0x04;
+
+ max_pixels_x_current_dpi = (float) scanner_info->max_size_x * dpi;
+ max_pixels_y_current_dpi = (float) scanner_info->max_size_y * dpi;
+ if ( scan_source == SOURCE_TMA_NEGATIVES
+ || scan_source == SOURCE_TMA_SLIDES)
+ {
+ max_pixels_x_current_dpi = (float) (TMA_MAX_X_INCHES * dpi);
+ max_pixels_y_current_dpi = (float) (TMA_MAX_Y_INCHES * dpi);
+ }
+ /* In ADF mode the device can scan up to ADF_MAX_Y_INCHES, which is usually
+ * bigger than what scanner reports back during initialization
+ */
+ if ( scan_source == SOURCE_ADF )
+ max_pixels_y_current_dpi = (float) (ADF_MAX_Y_INCHES * dpi);
+
+ /* Allow two times of max pixels for ADF Duplex mode */
+ if (scan_source == SOURCE_ADF_DUPLEX)
+ max_pixels_y_current_dpi *= 2;
+
+ pixels_x = width;
+ pixels_y = height;
+
+ scanner_top_x = (float) (top_x * (1.0 * base_dpi / dpi));
+ scanner_top_y = (float) (top_y * (1.0 * base_dpi / dpi));
+ scanner_pixels_x = (float) (pixels_x * (1.0 * base_dpi / dpi));
+ scanner_pixels_y = (float) (pixels_y * (1.0 * base_dpi / dpi));
+
+ DBG (DBG_cmds, "Top X: %u, top Y: %u, size X: %u, size Y: %u\n",
+ top_x, top_y, pixels_x, pixels_y);
+ DBG (DBG_cmds, "Scanner top X: %u, top Y: %u, size X: %u, size Y: %u\n",
+ scanner_top_x, scanner_top_y,
+ scanner_pixels_x, scanner_pixels_y);
+
+ if (top_x + pixels_x > max_pixels_x_current_dpi)
+ {
+ DBG (DBG_err, "Top X (%u) + pixels X (%u) exceedes max X %u\n",
+ top_x, pixels_x, max_pixels_x_current_dpi);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (top_y + pixels_y > max_pixels_y_current_dpi)
+ {
+ DBG (DBG_err, "Top Y (%u) + pixels Y (%u) exceedes max Y %u\n",
+ top_y, pixels_y, max_pixels_y_current_dpi);
+ return SANE_STATUS_INVAL;
+ }
+
+ scan_params.top_x = htons (scanner_top_x);
+ scan_params.top_y = htons (scanner_top_y);
+ scan_params.size_x = htons (scanner_pixels_x);
+ scan_params.size_y = htons (scanner_pixels_y);
+
+ scanner_line_width = (float) (pixels_x
+ * (1.0 * scanner_dpi / dpi) / 8 * pixel_bits);
+
+ /* Scanner hangs at scan command if line width less than 18 */
+ if (scanner_line_width < 18)
+ {
+ DBG (DBG_err, "Line width too smal, extending to minimum\n");
+ scanner_line_width = 18;
+ }
+ scan_params.line_width = htons (scanner_line_width);
+ DBG (DBG_cmds, "Line width: %u\n", scanner_line_width);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_SET_SCAN_PARAMS,
+ (unsigned char *) &scan_params,
+ sizeof (scan_params), CORE_DATA);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_image_params (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ struct image_params image_params;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (image_params) == 16);
+
+ memset (&image_params, 0, sizeof (image_params));
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN | CMD_VERIFY,
+ CMD_GET_IMAGE_PARAMS,
+ (unsigned char *) &image_params,
+ sizeof (image_params), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ if (image_params.signature != 0xc0)
+ {
+ DBG (DBG_err, "Wrong signature for image parameters structure "
+ "received (needed 0xc0, got %02x)\n", image_params.signature);
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_cmds, "Received image params:\n");
+ DBG (DBG_cmds, "Signature %02x\n", image_params.signature);
+ DBG (DBG_cmds, "Image size %lu (%04lx)\n",
+ (unsigned long) ntohl (image_params.image_size),
+ (unsigned long) ntohl (image_params.image_size));
+ DBG (DBG_cmds, "Line width: %u (%02x)\n", ntohs (image_params.line_width),
+ ntohs (image_params.line_width));
+ DBG (DBG_cmds, "Actual size Y: %u (%02x)\n",
+ ntohs (image_params.real_size_y), ntohs (image_params.real_size_y));
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_set_scan_params (SANE_Int dn,
+ enum proto_flags proto_flags,
+ struct scanner_info * scanner_info,
+ unsigned int top_x, unsigned int top_y,
+ unsigned int width, unsigned int height,
+ unsigned int dpi, enum color_depths color_depth,
+ enum scan_modes scan_mode,
+ enum scan_sources scan_source)
+{
+ unsigned int base_dpi;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (scanner_info != NULL);
+ hp5590_cmds_assert (dpi != 0);
+
+ /* Lock scanner */
+ ret = hp5590_lock_unlock_scanner (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Set base DPI */
+ ret = calc_base_dpi (dpi, &base_dpi);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ /* Unlock scanner */
+ hp5590_lock_unlock_scanner (dn, proto_flags);
+ return ret;
+ }
+
+ DBG (DBG_cmds, "Set base DPI: %u\n", base_dpi);
+ ret = hp5590_set_base_dpi (dn, proto_flags, scanner_info, base_dpi);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ /* Unlock scanner */
+ hp5590_lock_unlock_scanner (dn, proto_flags);
+ return ret;
+ }
+
+ /* Set color map */
+ ret = hp5590_set_color_map (dn, proto_flags, base_dpi);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ /* Unlock scanner */
+ hp5590_lock_unlock_scanner (dn, proto_flags);
+ return ret;
+ }
+
+ ret = hp5590_set_scan_area (dn,
+ proto_flags,
+ scanner_info,
+ top_x, top_y,
+ width, height,
+ dpi, color_depth, scan_mode, scan_source);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ /* Unlock scanner */
+ hp5590_lock_unlock_scanner (dn, proto_flags);
+ return ret;
+ }
+
+ ret = hp5590_read_image_params (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ /* Unlock scanner */
+ hp5590_lock_unlock_scanner (dn, proto_flags);
+ return ret;
+ }
+
+ /* Unlock scanner */
+ ret = hp5590_lock_unlock_scanner (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_send_reverse_calibration_map (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ unsigned int reverse_map_size = REVERSE_MAP_LEN;
+ uint16_t reverse_map[REVERSE_MAP_LEN];
+ unsigned int i;
+ uint16_t val;
+ unsigned int len;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ DBG (DBG_proc, "Preparing reverse calibration map\n");
+ val = 0xffff;
+ len = reverse_map_size / 4;
+ for (i = 0; i < len; i++)
+ {
+ reverse_map[i] = htons (val);
+ val -= 1;
+ }
+
+ for (i = len; i < len * 2; i++)
+ {
+ reverse_map[i] = htons (val);
+ val -= 1;
+ }
+
+ for (i = len * 2; i < len * 3; i++)
+ {
+ reverse_map[i] = htons (val);
+ val -= 1;
+ }
+
+ for (i = len * 3; i < len * 4; i++)
+ {
+ reverse_map[i] = htons (0xffff);
+ }
+
+ DBG (DBG_proc, "Done preparing reverse calibration map\n");
+
+ ret = hp5590_bulk_write (dn,
+ proto_flags,
+ 0x2b,
+ (unsigned char *) reverse_map,
+ reverse_map_size * sizeof (uint16_t));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_send_forward_calibration_maps (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ unsigned int forward_map_size = FORWARD_MAP_LEN;
+ uint16_t forward_map[FORWARD_MAP_LEN];
+ SANE_Status ret;
+ unsigned int i;
+ uint16_t val;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+ DBG (DBG_proc, "Preparing forward calibration map\n");
+ val = 0x0000;
+ for (i = 0; i < forward_map_size; i++)
+ {
+ forward_map[i] = htons (val);
+ if (val < 0xffff)
+ val += 1;
+ }
+ DBG (DBG_proc, "Done preparing forward calibration map\n");
+
+ ret = hp5590_bulk_write (dn,
+ proto_flags,
+ 0x012a,
+ (unsigned char *) forward_map,
+ forward_map_size * sizeof (uint16_t));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_bulk_write (dn,
+ proto_flags,
+ 0x022a,
+ (unsigned char *) forward_map,
+ forward_map_size * sizeof (uint16_t));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = hp5590_bulk_write (dn,
+ proto_flags,
+ 0x032a,
+ (unsigned char *) forward_map,
+ forward_map_size * sizeof (uint16_t));
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned char *bytes, unsigned int size,
+ void *state)
+{
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (bytes != NULL);
+ hp5590_cmds_assert (state != NULL);
+
+ ret = hp5590_bulk_read (dn, proto_flags, bytes, size, state);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_start_scan (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint8_t reg_051b = 0x40;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (sizeof (reg_051b) == 1);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_VERIFY,
+ CMD_START_SCAN,
+ (unsigned char *) &reg_051b,
+ sizeof (reg_051b), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************/
+static SANE_Status
+hp5590_read_buttons (SANE_Int dn,
+ enum proto_flags proto_flags,
+ enum button_status * status)
+{
+ uint16_t button_status;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ hp5590_cmds_assert (status != NULL);
+ hp5590_cmds_assert (sizeof (button_status) == 2);
+
+ ret = hp5590_cmd (dn,
+ proto_flags,
+ CMD_IN | CMD_VERIFY,
+ CMD_BUTTON_STATUS,
+ (unsigned char *) &button_status,
+ sizeof (button_status), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ *status = BUTTON_NONE;
+
+ /* Network order */
+ button_status = ntohs (button_status);
+ DBG (DBG_cmds, "Button status: %04x\n", button_status);
+ DBG (DBG_cmds, "Power: %s, Scan: %s, Collect: %s, File: %s, Email: %s, Copy: %s, "
+ "Up: %s, Down: %s, Mode: %s, Cancel: %s\n",
+ button_status & BUTTON_FLAG_POWER ? " on" : "off",
+ button_status & BUTTON_FLAG_SCAN ? " on" : "off",
+ button_status & BUTTON_FLAG_COLLECT ? " on" : "off",
+ button_status & BUTTON_FLAG_FILE ? " on" : "off",
+ button_status & BUTTON_FLAG_EMAIL ? " on" : "off",
+ button_status & BUTTON_FLAG_COPY ? " on" : "off",
+ button_status & BUTTON_FLAG_UP ? " on" : "off",
+ button_status & BUTTON_FLAG_DOWN ? " on" : "off",
+ button_status & BUTTON_FLAG_MODE ? " on" : "off",
+ button_status & BUTTON_FLAG_CANCEL ? " on" : "off");
+
+ if (button_status & BUTTON_FLAG_POWER)
+ *status = BUTTON_POWER;
+
+ if (button_status & BUTTON_FLAG_SCAN)
+ *status = BUTTON_SCAN;
+
+ if (button_status & BUTTON_FLAG_COLLECT)
+ *status = BUTTON_COLLECT;
+
+ if (button_status & BUTTON_FLAG_FILE)
+ *status = BUTTON_FILE;
+
+ if (button_status & BUTTON_FLAG_EMAIL)
+ *status = BUTTON_EMAIL;
+
+ if (button_status & BUTTON_FLAG_COPY)
+ *status = BUTTON_COPY;
+
+ if (button_status & BUTTON_FLAG_UP)
+ *status = BUTTON_UP;
+
+ if (button_status & BUTTON_FLAG_DOWN)
+ *status = BUTTON_DOWN;
+
+ if (button_status & BUTTON_FLAG_MODE)
+ *status = BUTTON_MODE;
+
+ if (button_status & BUTTON_FLAG_CANCEL)
+ *status = BUTTON_CANCEL;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* SET SCAN PARAMETERS
+==================== 50 =======================
+BW 50 (425 x 585)
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+BW 50 reduced top left (261 x 469)
+18 00 64 00 64 00 00 00 00 03 0c 05 7f 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 02 08
+
+BW 50 reduced top right (302 x 498)
+18 00 64 00 64 01 6e 00 00 03 8a 05 d6 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 02 5c
+
+BW 50 X 0, small width (16 x 585)
+18 00 64 00 64 00 00 00 00 00 30 06 db 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 00 20
+
+BW 50 X large, small width (24 x 585)
+18 00 64 00 64 04 b0 00 00 00 48 06 db 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 00 30
+
+BW 50 Y 0, small height (425 x 21)
+18 00 64 00 64 00 00 00 00 04 f8 00 3f 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+BW 50 Y large, small height (425 x 21)
+18 00 64 00 64 00 00 06 99 04 f8 00 3f 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+GRAY 50
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+COLOR 50
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 09 f0
+
+==================== 75 =======================
+BW 75
+18 00 64 00 64 00 00 00 00 04 f8 06 da 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+GRAY 75
+18 00 64 00 64 00 00 00 00 04 f8 06 da 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+COLOR 75
+18 00 64 00 64 00 00 00 00 04 f8 06 da 00 80 00
+00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 09 f0
+
+COLOR 75 48 bit
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+04 00 00 13 e0
+
+=================== 100 =======================
+BW 100
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+40 01 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 00 6a
+
+GRAY 100
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 50
+
+COLOR 100
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 09 f0
+
+COLOR 100 48bit, preview
+18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+04 00 00 13 e0
+
+COLOR 100 48bit
+18 00 64 00 64 00 00 00 00 04 f2 06 db 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 13 c8
+
+COLOR 100 48bit, TMA negatives
+11 00 64 00 64 00 00 00 00 00 fc 03 84 00 80 00
+00 30 e8 40 00 64 00 c8 01 90 00 00 00 00 00 00
+00 00 00 03 f0
+
+COLOR 100 48bit, TMA slides
+12 00 64 00 64 00 00 00 00 00 fc 03 84 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 03 f0
+
+=================== 150 =======================
+BW 150
+18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 06 a4
+
+GRAY 150
+18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 06 a4
+
+COLOR 150
+18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00
+00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 13 ec
+
+COLOR 150 48 bit
+18 00 c8 00 c8 00 00 00 00 09 ea 0d b6 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 27 a8
+
+=================== 200 =======================
+BW 200
+18 00 c8 00 c8 00 00 00 00 09 f0 0d b6 00 80 00
+40 01 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 00 d4
+
+GRAY 200
+18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 06 a4
+
+COLOR 200
+18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00
+00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 13 ec
+
+COLOR 200 48 bit
+18 00 c8 00 c8 00 00 00 00 09 f6 0d aa 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 27 d8
+
+=================== 300 =======================
+BW 300
+18 01 2c 01 2c 00 00 00 00 09 f0 0d b6 00 80 00
+40 01 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 01 3e
+
+GRAY 300
+18 01 2c 01 2c 00 00 00 00 09 f4 0d b6 00 80 00
+40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 09 f4
+
+COLOR 300
+18 01 2c 01 2c 00 00 00 00 09 f4 0d b6 00 80 00
+00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 1d dc
+
+COLOR 300 48bit, TMA negatives
+11 01 2c 01 2c 00 00 00 06 01 fc 07 02 00 80 00
+00 30 e8 40 00 64 00 c8 01 90 00 00 00 00 00 00
+00 00 00 0b e8
+
+COLOR 300 48bit, TMA slides
+12 01 2c 01 2c 00 00 00 00 01 fc 07 08 00 80 00
+00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 0b e8
+
+==================== 400 ======================
+BW 400
+18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00
+40 08 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 13 ec
+
+GRAY 400
+18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00
+40 08 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 13 ec
+
+COLOR 400
+18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00
+00 18 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 3b c4
+
+==================== 600 ======================
+BW 600
+18 02 58 02 58 00 00 00 00 13 e8 1b 6c 00 80 00
+40 01 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 02 7d
+
+GRAY 600
+18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00
+40 08 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 13 ec
+
+COLOR 600
+18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00
+00 18 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 3b c4
+
+==================== 1200 =====================
+BW 1200
+
+18 04 b0 04 b0 00 00 00 00 27 a8 36 d8 00 80 00
+40 01 c8 40 00 64 00 64 00 64 00 00 00 00 00 00
+00 00 00 04 f5
+*/
+
+/* READ SCAN PARAMETERS
+====================== 50 =====================
+BW 50
+c0 00 00 0f 23 a0 00 00 03 50 04 92 00 00 00 00
+
+GRAY 50
+c0 00 00 0f 23 a0 00 00 03 50 04 92 00 00 00 00
+
+COLOR 50
+c0 00 00 2d 6a e0 00 00 09 f0 04 92 00 00 00 00
+
+====================== 75 =====================
+BW 75
+c0 00 00 0f 20 50 00 00 03 50 04 91 00 00 00 00
+
+GRAY 75
+c0 00 00 0f 20 50 00 00 03 50 04 91 00 00 00 00
+
+COLOR 75
+c0 00 00 2d 60 f0 00 00 09 f0 04 91 00 00 00 00
+
+COLOR 75 48 bit
+c0 00 00 5a 86 40 00 00 13 e0 04 8e 00 00 00 00
+
+===================== 100 =====================
+BW 100
+c0 00 00 01 e4 74 00 00 00 6a 04 92 00 00 00 00
+
+GRAY 100
+c0 00 00 0f 23 a0 00 00 03 50 04 92 00 00 00 00
+
+COLOR 100
+c0 00 00 2d 6a e0 00 00 09 f0 04 92 00 00 00 00
+
+COLOR 100, 48 bit preview
+c0 00 00 5a d5 c0 00 00 13 e0 04 92 00 00 00 00
+
+COLOR 100, 48 bit
+c0 00 00 5a 68 10 00 00 13 c8 04 92 00 00 00 00
+
+===================== 150 =====================
+BW 150
+c0 00 00 3c b3 10 00 00 06 a4 09 24 00 00 00 00
+
+GRAY 150
+c0 00 00 3c b3 10 00 00 06 a4 09 24 00 00 00 00
+
+COLOR 150
+c0 00 00 b6 19 30 00 00 13 ec 09 24 00 00 00 00
+
+COLOR 150 48bit
+c0 00 01 6a 7b a0 00 00 27 a8 09 24 00 00 00 00
+
+===================== 200 =====================
+BW 200
+c0 00 00 07 91 d0 00 00 00 d4 09 24 00 00 00 00
+
+GRAY 200
+c0 00 00 3c b3 10 00 00 06 a4 09 24 00 00 00 00
+
+COLOR 200
+c0 00 00 b6 19 30 00 00 13 ec 09 24 00 00 00 00
+
+COLOR 200 48 bit
+c0 00 01 6a f3 a0 00 00 27 d8 09 1c 00 00 00 00
+
+===================== 300 =====================
+BW 300
+c0 00 00 11 08 14 00 00 01 3e 0d b6 00 00 00 00
+
+GRAY 300
+c0 00 00 88 77 78 00 00 09 f4 0d b6 00 00 00 00
+
+COLOR 300
+c0 00 01 99 66 68 00 00 1d dc 0d b6 00 00 00 00
+
+===================== 400 =====================
+BW 400
+c0 00 02 22 4b 90 00 00 13 ec 1b 6c 00 00 00 00
+
+GRAY 400
+c0 00 02 22 4b 90 00 00 13 ec 1b 6c 00 00 00 00
+
+COLOR 400
+c0 00 06 66 e2 b0 00 00 3b c4 1b 6c 00 00 00 00
+
+===================== 600 =====================
+BW 600
+c0 00 00 44 3b bc 00 00 02 7d 1b 6c 00 00 00 00
+
+GRAY 600
+c0 00 02 22 4b 90 00 00 13 ec 1b 6c 00 00 00 00
+
+COLOR 600
+c0 00 06 66 e2 b0 00 00 3b c4 1b 6c 00 00 00 00
+*/
+/* vim: sw=2 ts=8
+ */
diff --git a/backend/hp5590_cmds.h b/backend/hp5590_cmds.h
new file mode 100644
index 0000000..c8da0f2
--- /dev/null
+++ b/backend/hp5590_cmds.h
@@ -0,0 +1,202 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
+ HP ScanJet 4570c support by Markham Thomas
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for
+ HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners
+*/
+
+#ifndef HP5590_H
+#define HP5590_H
+
+#include "hp5590_low.h"
+
+#define TMA_MAX_X_INCHES 1.69
+#define TMA_MAX_Y_INCHES 6
+
+#define ADF_MAX_Y_INCHES 14
+
+enum hp_scanner_types
+{
+ SCANNER_NONE = 0,
+ SCANNER_HP4570,
+ SCANNER_HP5550,
+ SCANNER_HP5590,
+ SCANNER_HP7650
+};
+
+enum scan_sources
+{
+ SOURCE_NONE = 1,
+ SOURCE_FLATBED,
+ SOURCE_ADF,
+ SOURCE_ADF_DUPLEX,
+ SOURCE_TMA_NEGATIVES,
+ SOURCE_TMA_SLIDES
+};
+
+enum scan_modes
+{
+ MODE_NORMAL = 1,
+ MODE_PREVIEW
+};
+
+enum color_depths
+{
+ DEPTH_BW = 1,
+ DEPTH_GRAY,
+ DEPTH_COLOR_24,
+ DEPTH_COLOR_48
+};
+
+enum button_status
+{
+ BUTTON_NONE = 1,
+ BUTTON_POWER,
+ BUTTON_SCAN,
+ BUTTON_COLLECT,
+ BUTTON_FILE,
+ BUTTON_EMAIL,
+ BUTTON_COPY,
+ BUTTON_UP,
+ BUTTON_DOWN,
+ BUTTON_MODE,
+ BUTTON_CANCEL
+};
+
+enum hp5590_lamp_state
+{
+ LAMP_STATE_TURNOFF = 1,
+ LAMP_STATE_TURNON,
+ LAMP_STATE_SET_TURNOFF_TIME,
+ LAMP_STATE_SET_TURNOFF_TIME_LONG
+};
+
+struct hp5590_model
+{
+ enum hp_scanner_types scanner_type;
+ unsigned int usb_vendor_id;
+ unsigned int usb_product_id;
+ const char *vendor_id;
+ const char *model;
+ const char *kind;
+ enum proto_flags proto_flags;
+};
+
+#define FEATURE_NONE 0
+#define FEATURE_ADF 1 << 0
+#define FEATURE_TMA 1 << 1
+#define FEATURE_LCD 1 << 2
+
+struct scanner_info
+{
+ const char *model;
+ const char *kind;
+ unsigned int features;
+ const char *fw_version;
+ unsigned int max_dpi_x;
+ unsigned int max_dpi_y;
+ unsigned int max_pixels_x;
+ unsigned int max_pixels_y;
+ float max_size_x;
+ float max_size_y;
+ unsigned int max_motor_param;
+ unsigned int normal_motor_param;
+};
+
+static SANE_Status hp5590_model_def (enum hp_scanner_types scanner_type,
+ const struct hp5590_model ** model);
+static SANE_Status hp5590_vendor_product_id (enum hp_scanner_types scanner_type,
+ SANE_Word * vendor_id,
+ SANE_Word * product_id);
+static SANE_Status hp5590_init_scanner (SANE_Int dn,
+ enum proto_flags proto_flags,
+ struct scanner_info **info,
+ enum hp_scanner_types scanner_type);
+static SANE_Status hp5590_power_status (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_read_max_scan_count (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int *max_count);
+static SANE_Status hp5590_select_source_and_wakeup (SANE_Int dn,
+ enum proto_flags proto_flags,
+ enum scan_sources source,
+ SANE_Bool extend_lamp_timeout);
+static SANE_Status hp5590_stop_scan (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_read_scan_count (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int *count);
+static SANE_Status hp5590_set_scan_params (SANE_Int dn,
+ enum proto_flags proto_flags,
+ struct scanner_info *scanner_info,
+ unsigned int top_x, unsigned int top_y,
+ unsigned int width, unsigned int height,
+ unsigned int dpi,
+ enum color_depths color_depth,
+ enum scan_modes scan_mode,
+ enum scan_sources scan_source);
+static SANE_Status hp5590_send_forward_calibration_maps (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_send_reverse_calibration_map (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_inc_scan_count (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_start_scan (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_read (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned char *bytes,
+ unsigned int size, void *state);
+static SANE_Status hp5590_read_buttons (SANE_Int dn,
+ enum proto_flags proto_flags,
+ enum button_status *status);
+static SANE_Status hp5590_read_part_number (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_calc_pixel_bits (unsigned int dpi,
+ enum color_depths color_depth,
+ unsigned int *pixel_bits);
+static SANE_Status hp5590_is_data_available (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_reset_scan_head (SANE_Int dn,
+ enum proto_flags proto_flags);
+#endif /* HP5590_H */
+/* vim: sw=2 ts=8
+ */
diff --git a/backend/hp5590_low.c b/backend/hp5590_low.c
new file mode 100644
index 0000000..51da01a
--- /dev/null
+++ b/backend/hp5590_low.c
@@ -0,0 +1,965 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for
+ HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners
+*/
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#include "byteorder.h"
+
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/_stdint.h"
+#include "hp5590_low.h"
+
+/* Debug levels */
+#define DBG_err 0
+#define DBG_proc 10
+#define DBG_usb 50
+
+/* Custom assert() macro */
+#define hp5590_low_assert(exp) if(!(exp)) { \
+ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\
+ return SANE_STATUS_INVAL; \
+}
+
+/* Structure describing bulk transfer size */
+struct bulk_size
+{
+ uint16_t size;
+ uint8_t unused;
+} __attribute__ ((packed));
+
+/* Structure describing bulk URB */
+/* FIXME: Verify according to USB standard */
+struct usb_in_usb_bulk_setup
+{
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint8_t bEndpoint;
+ uint16_t unknown;
+ uint16_t wLength; /* MSB first */
+ uint8_t pad;
+} __attribute__ ((packed));
+
+/* Structure describing control URB */
+struct usb_in_usb_ctrl_setup {
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint16_t wValue; /* MSB first */
+ uint16_t wIndex; /* MSB first */
+ uint16_t wLength; /* LSB first */
+} __attribute__ ((packed));
+
+/* CORE status flag - ready or not */
+#define CORE_FLAG_NOT_READY 1 << 1
+
+/* Bulk transfers are done in pages, below their respective sizes */
+#define BULK_WRITE_PAGE_SIZE 0x0f000
+#define BULK_READ_PAGE_SIZE 0x10000
+#define ALLOCATE_BULK_READ_PAGES 16 /* 16 * 65536 = 1Mb */
+
+/* Structure describing bulk read state, because bulk reads will be done in
+ * pages, but function caller uses its own buffer, whose size is certainly
+ * different. Also, each bulk read page is ACK'ed by special command
+ * so total pages received should be tracked as well
+ */
+struct bulk_read_state
+{
+ unsigned char *buffer;
+ unsigned int buffer_size;
+ unsigned int bytes_available;
+ unsigned char *buffer_out_ptr;
+ unsigned char *buffer_in_ptr;
+ unsigned int total_pages;
+ unsigned char *buffer_end_ptr;
+ unsigned int initialized;
+};
+
+/*******************************************************************************
+ * USB-in-USB: get acknowledge for last USB-in-USB operation
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ *
+ * Returns
+ * SANE_STATUS_GOOD - if correct acknowledge was received
+ * SANE_STATUS_DEVICE_BUSY - otherwise
+ */
+static SANE_Status
+hp5590_get_ack (SANE_Int dn,
+ enum proto_flags proto_flags)
+{
+ uint8_t status;
+ SANE_Status ret;
+
+ /* Bypass reading acknowledge if the device doesn't need it */
+ if (proto_flags & PF_NO_USB_IN_USB_ACK)
+ return SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ /* Check if USB-in-USB operation was accepted */
+ ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
+ 0x0c, 0x8e, 0x20,
+ sizeof (status), &status);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error getting acknowledge\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ DBG (DBG_usb, "%s: USB-in-USB: accepted\n", __FUNCTION__);
+
+ /* Check if we received correct acknowledgement */
+ if (status != 0x01)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: not accepted (status %u)\n",
+ __FUNCTION__, status);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*******************************************************************************
+ * USB-in-USB: get device status
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ *
+ * Returns
+ * SANE_STATUS_GOOD - if correct status was received
+ * SANE_STATUS_DEVICE_BUSY - otherwise
+ */
+static SANE_Status
+hp5590_get_status (SANE_Int dn,
+ __sane_unused__ enum proto_flags proto_flags)
+{
+ uint8_t status;
+ SANE_Status ret;
+
+ DBG (DBG_proc, "%s\n", __FUNCTION__);
+
+ ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
+ 0x0c, 0x8e, 0x00,
+ sizeof (status), &status);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error getting device status\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ /* Check if we received correct status */
+ if (status != 0x00)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: got non-zero device status (status %u)\n",
+ __FUNCTION__, status);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*******************************************************************************
+ * USB-in-USB: sends control message for IN or OUT operation
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ * requesttype, request, value, index - their meaninings are similar to
+ * sanei_control_msg()
+ * bytes - pointer to data buffer
+ * size - size of data
+ * core_flags -
+ * CORE_NONE - no CORE operation will be performed
+ * CORE_DATA - operation on CORE data will be performed
+ * CORE_BULK_IN - preparation for bulk IN transfer (not used yet)
+ * CORE_BULK_OUT - preparation for bulk OUT trasfer
+ *
+ * Returns
+ * SANE_STATUS_GOOD - control message was sent w/o any errors
+ * all other SANE_Status values - otherwise
+ */
+static SANE_Status
+hp5590_control_msg (SANE_Int dn,
+ enum proto_flags proto_flags,
+ int requesttype, int request,
+ int value, int index, unsigned char *bytes,
+ int size, int core_flags)
+{
+ struct usb_in_usb_ctrl_setup ctrl;
+ SANE_Status ret;
+ unsigned int len;
+ unsigned char *ptr;
+ uint8_t ack;
+ uint8_t response;
+ unsigned int needed_response;
+
+ DBG (DBG_proc, "%s: USB-in-USB: core data: %s\n",
+ __FUNCTION__, core_flags & CORE_DATA ? "yes" : "no");
+
+ hp5590_low_assert (bytes != NULL);
+
+ /* IN (read) operation will be performed */
+ if (requesttype & USB_DIR_IN)
+ {
+ /* Prepare USB-in-USB control message */
+ memset (&ctrl, 0, sizeof (ctrl));
+ ctrl.bRequestType = 0xc0;
+ ctrl.bRequest = request;
+ ctrl.wValue = htons (value);
+ ctrl.wIndex = htons (index);
+ ctrl.wLength = htole16 (size);
+
+ DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __FUNCTION__);
+ /* Send USB-in-USB control message */
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x04, 0x8f, 0x00,
+ sizeof (ctrl), (unsigned char *) &ctrl);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error sending control message\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ /* USB-in-USB: checking acknowledge for control message */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ len = size;
+ ptr = bytes;
+ /* Data is read in 8 byte portions */
+ while (len)
+ {
+ unsigned int next_packet_size;
+ next_packet_size = 8;
+ if (len < 8)
+ next_packet_size = len;
+
+ /* Read USB-in-USB data */
+ ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
+ core_flags & CORE_DATA ? 0x0c : 0x04,
+ 0x90, 0x00, next_packet_size, ptr);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error reading data\n", __FUNCTION__);
+ return ret;
+ }
+
+ ptr += next_packet_size;
+ len -= next_packet_size;
+ }
+
+ /* Confirm data reception */
+ ack = 0;
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x0c, 0x8f, 0x00,
+ sizeof (ack), &ack);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error confirming data reception\n",
+ __FUNCTION__);
+ return -1;
+ }
+
+ /* USB-in-USB: checking if confirmation was acknowledged */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ /* OUT (write) operation will be performed */
+ if (!(requesttype & USB_DIR_IN))
+ {
+ /* Prepare USB-in-USB control message */
+ memset (&ctrl, 0, sizeof (ctrl));
+ ctrl.bRequestType = 0x40;
+ ctrl.bRequest = request;
+ ctrl.wValue = htons (value);
+ ctrl.wIndex = htons (index);
+ ctrl.wLength = htole16 (size);
+
+ DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __FUNCTION__);
+ /* Send USB-in-USB control message */
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x04, 0x8f, 0x00,
+ sizeof (ctrl), (unsigned char *) &ctrl);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error sending control message\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ /* USB-in-USB: checking acknowledge for control message */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ len = size;
+ ptr = bytes;
+ /* Data is sent in 8 byte portions */
+ while (len)
+ {
+ unsigned int next_packet_size;
+ next_packet_size = 8;
+ if (len < 8)
+ next_packet_size = len;
+
+ /* Send USB-in-USB data */
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ core_flags & CORE_DATA ? 0x04 : 0x0c,
+ 0x8f, 0x00, next_packet_size, ptr);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error sending data\n", __FUNCTION__);
+ return ret;
+ }
+
+ /* CORE data is acknowledged packet by packet */
+ if (core_flags & CORE_DATA)
+ {
+ /* USB-in-USB: checking if data was accepted */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ ptr += next_packet_size;
+ len -= next_packet_size;
+ }
+
+ /* Normal (non-CORE) data is acknowledged after its full transmission */
+ if (!(core_flags & CORE_DATA))
+ {
+ /* USB-in-USB: checking if data was accepted */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ /* Getting response after data transmission */
+ DBG (DBG_usb, "%s: USB-in-USB: getting response\n", __FUNCTION__);
+ ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
+ 0x0c, 0x90, 0x00,
+ sizeof (response), &response);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error getting response\n", __FUNCTION__);
+ return ret;
+ }
+
+ /* Necessary response after normal (non-CORE) data is 0x00,
+ * after bulk OUT preparation - 0x24
+ */
+ needed_response = core_flags & CORE_BULK_OUT ? 0x24 : 0x00;
+ if (response == needed_response)
+ DBG (DBG_usb, "%s: USB-in-USB: got correct response\n",
+ __FUNCTION__);
+
+ if (response != needed_response)
+ {
+ DBG (DBG_err,
+ "%s: USB-in-USB: invalid response received "
+ "(needed %04x, got %04x)\n",
+ __FUNCTION__, needed_response, response);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Send bulk OUT flags is bulk OUT preparation is performed */
+ if (core_flags & CORE_BULK_OUT)
+ {
+ uint8_t bulk_flags = 0x24;
+ DBG (DBG_usb, "%s: USB-in-USB: sending bulk flags\n",
+ __FUNCTION__);
+
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x0c, 0x83, 0x00,
+ sizeof (bulk_flags), &bulk_flags);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ /* USB-in-USB: checking confirmation for bulk flags */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*******************************************************************************
+ * USB-in-USB: verifies last command
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ * cmd - command to verify
+ *
+ * Returns
+ * SANE_STATUS_GOOD - command verified successfully and CORE is ready
+ * SANE_STATUS_IO_ERROR - command verification failed
+ * SANE_STATUS_DEVICE_BUSY - command verified successfully but CORE isn't ready
+ * all other SANE_Status values - otherwise
+ */
+static SANE_Status
+hp5590_verify_last_cmd (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int cmd)
+{
+ uint16_t verify_cmd;
+ unsigned int last_cmd;
+ unsigned int core_status;
+ SANE_Status ret;
+
+ DBG (3, "%s: USB-in-USB: command verification requested\n",
+ __FUNCTION__);
+
+ /* Read last command along with CORE status */
+ ret = hp5590_control_msg (dn,
+ proto_flags,
+ USB_DIR_IN,
+ 0x04, 0xc5, 0x00,
+ (unsigned char *) &verify_cmd,
+ sizeof (verify_cmd), CORE_NONE);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ verify_cmd = le16toh (verify_cmd); /* Response is LSB first */
+
+ /* Last command - minor byte */
+ last_cmd = verify_cmd & 0xff;
+ /* CORE status - major byte */
+ core_status = (verify_cmd & 0xff00) >> 8;
+
+ /* Verify last command */
+ DBG (DBG_usb, "%s: USB-in-USB: command verification %04x, "
+ "last command: %04x, core status: %04x\n",
+ __FUNCTION__, verify_cmd, last_cmd, core_status);
+ if ((cmd & 0x00ff) != last_cmd)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: command verification failed: "
+ "expected 0x%04x, got 0x%04x\n",
+ __FUNCTION__, cmd, last_cmd);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_usb, "%s: USB-in-USB: command verified successfully\n",
+ __FUNCTION__);
+
+ /* Return value depends on CORE status */
+ return core_status & CORE_FLAG_NOT_READY ?
+ SANE_STATUS_DEVICE_BUSY : SANE_STATUS_GOOD;
+}
+
+/*******************************************************************************
+ * USB-in-USB: send command (convenience wrapper around hp5590_control_msg())
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ * requesttype, request, value, index - their meaninings are similar to
+ * sanei_control_msg()
+ * bytes - pointer to data buffer
+ * size - size of data
+ * core_flags -
+ * CORE_NONE - no CORE operation will be performed
+ * CORE_DATA - operation on CORE data will be performed
+ * CORE_BULK_IN - preparation for bulk IN transfer (not used yet)
+ * CORE_BULK_OUT - preparation for bulk OUT trasfer
+ *
+ * Returns
+ * SANE_STATUS_GOOD - command was sent (and possible verified) w/o any errors
+ * all other SANE_Status values - otherwise
+ */
+static SANE_Status
+hp5590_cmd (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int flags,
+ unsigned int cmd, unsigned char *data, unsigned int size,
+ unsigned int core_flags)
+{
+ SANE_Status ret;
+
+ DBG (3, "%s: USB-in-USB: command : %04x\n", __FUNCTION__, cmd);
+
+ ret = hp5590_control_msg (dn,
+ proto_flags,
+ flags & CMD_IN ? USB_DIR_IN : USB_DIR_OUT,
+ 0x04, cmd, 0x00, data, size, core_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ ret = SANE_STATUS_GOOD;
+ /* Verify last command if requested */
+ if (flags & CMD_VERIFY)
+ {
+ ret = hp5590_verify_last_cmd (dn, proto_flags, cmd);
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+ * USB-in-USB: initialized bulk read state
+ *
+ * Parameters
+ * state - pointer to a pointer for initialized state
+ *
+ * Returns
+ * SANE_STATUS_GOOD - if state was initialized successfully
+ * SANE_STATUS_NO_MEM - memory allocation failed
+ */
+static SANE_Status
+hp5590_low_init_bulk_read_state (void **state)
+{
+ struct bulk_read_state *bulk_read_state;
+
+ DBG (3, "%s: USB-in-USB: initializing bulk read state\n", __FUNCTION__);
+
+ hp5590_low_assert (state != NULL);
+
+ bulk_read_state = malloc (sizeof (struct bulk_read_state));
+ if (!bulk_read_state)
+ return SANE_STATUS_NO_MEM;
+ memset (bulk_read_state, 0, sizeof (struct bulk_read_state));
+
+ bulk_read_state->buffer = malloc (ALLOCATE_BULK_READ_PAGES
+ * BULK_READ_PAGE_SIZE);
+ if (!bulk_read_state->buffer)
+ {
+ DBG (DBG_err, "%s: Memory allocation failed for %u bytes\n",
+ __FUNCTION__, ALLOCATE_BULK_READ_PAGES * BULK_READ_PAGE_SIZE);
+ return SANE_STATUS_NO_MEM;
+ }
+ bulk_read_state->buffer_size = ALLOCATE_BULK_READ_PAGES
+ * BULK_READ_PAGE_SIZE;
+ bulk_read_state->bytes_available = 0;
+ bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
+ bulk_read_state->buffer_in_ptr = bulk_read_state->buffer;
+ bulk_read_state->total_pages = 0;
+ bulk_read_state->buffer_end_ptr = bulk_read_state->buffer
+ + bulk_read_state->buffer_size;
+ bulk_read_state->initialized = 1;
+
+ *state = bulk_read_state;
+ return SANE_STATUS_GOOD;
+}
+
+/*******************************************************************************
+ * USB-in-USB: free bulk read state
+ *
+ * Parameters
+ * state - pointer to a pointer to bulk read state
+ *
+ * Returns
+ * SANE_STATUS_GOOD - bulk read state freed successfully
+ */
+static SANE_Status
+hp5590_low_free_bulk_read_state (void **state)
+{
+ struct bulk_read_state *bulk_read_state;
+
+ DBG (3, "%s\n", __FUNCTION__);
+
+ hp5590_low_assert (state != NULL);
+ /* Just return if NULL bulk read state was given */
+ if (*state == NULL)
+ return SANE_STATUS_GOOD;
+
+ bulk_read_state = *state;
+
+ DBG (3, "%s: USB-in-USB: freeing bulk read state\n", __FUNCTION__);
+
+ free (bulk_read_state->buffer);
+ bulk_read_state->buffer = NULL;
+ free (bulk_read_state);
+ *state = NULL;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* FIXME: perhaps needs to be converted to use hp5590_control_msg() */
+/*******************************************************************************
+ * USB-in-USB: bulk read
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ * bytes - pointer to data buffer
+ * size - size of data to read
+ * state - pointer to initialized bulk read state structure
+ */
+static SANE_Status
+hp5590_bulk_read (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned char *bytes, unsigned int size,
+ void *state)
+{
+ struct usb_in_usb_bulk_setup ctrl;
+ SANE_Status ret;
+ unsigned int next_pages;
+ uint8_t bulk_flags;
+ size_t next_portion;
+ struct bulk_read_state *bulk_read_state;
+ unsigned int bytes_until_buffer_end;
+
+ DBG (3, "%s\n", __FUNCTION__);
+
+ hp5590_low_assert (state != NULL);
+ hp5590_low_assert (bytes != NULL);
+
+ bulk_read_state = state;
+ if (bulk_read_state->initialized == 0)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: bulk read state not initialized\n",
+ __FUNCTION__);
+ return SANE_STATUS_INVAL;
+ }
+
+ memset (bytes, 0, size);
+
+ /* Check if requested data would fit into the buffer */
+ if (size > bulk_read_state->buffer_size)
+ {
+ DBG (DBG_err, "Data requested won't fit in the bulk read buffer "
+ "(requested: %u, buffer size: %u\n", size,
+ bulk_read_state->buffer_size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Read data until requested size of data will be received */
+ while (bulk_read_state->bytes_available < size)
+ {
+ DBG (DBG_usb, "%s: USB-in-USB: not enough data in buffer available "
+ "(available: %u, requested: %u)\n",
+ __FUNCTION__, bulk_read_state->bytes_available, size);
+
+ /* IMPORTANT! 'next_pages' means 'request and receive next_pages pages in
+ * one bulk transfer request '. Windows driver uses 4 pages between each
+ * request. The more pages are received between requests the less the
+ * scanner does scan head re-positioning thus improving scanning speed.
+ * On the other hand, scanner expects that all of the requested pages
+ * will be received immediately after the request. In case when a
+ * frontend will have a delay between reads we will get bulk transfer
+ * timeout sooner or later.
+ * Having next_pages = 1 is the most safe case.
+ */
+ next_pages = 1;
+ /* Count all received pages to calculate when we will need to send
+ * another bulk request
+ */
+ bulk_read_state->total_pages++;
+ DBG (DBG_usb, "%s: USB-in-USB: total pages done: %u\n",
+ __FUNCTION__, bulk_read_state->total_pages);
+
+ /* Send another bulk request for 'next_pages' before first
+ * page or next necessary one
+ */
+ if ( bulk_read_state->total_pages == 1
+ || bulk_read_state->total_pages % next_pages == 0)
+ {
+ /* Send bulk flags */
+ DBG (DBG_usb, "%s: USB-in-USB: sending USB-in-USB bulk flags\n",
+ __FUNCTION__);
+ bulk_flags = 0x24;
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x0c, 0x83, 0x00,
+ sizeof (bulk_flags), &bulk_flags);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ /* USB-in-USB: checking confirmation for bulk flags\n" */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Prepare bulk read request */
+ memset (&ctrl, 0, sizeof (ctrl));
+ ctrl.bRequestType = 0x00;
+ ctrl.bEndpoint = 0x82;
+ ctrl.wLength = htons (next_pages);
+
+ /* Send bulk read request */
+ DBG (DBG_usb, "%s: USB-in-USB: sending control msg for bulk\n",
+ __FUNCTION__);
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x04, 0x82, 0x00,
+ sizeof (ctrl),
+ (unsigned char *) &ctrl);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: error sending control msg\n",
+ __FUNCTION__);
+ return ret;
+ }
+
+ /* USB-in-USB: checking if control msg was accepted */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+ }
+
+ next_portion = BULK_READ_PAGE_SIZE;
+ /* Check if next page will fit into the buffer */
+ if (bulk_read_state->buffer_size
+ - bulk_read_state->bytes_available < next_portion)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: buffer too small\n", __FUNCTION__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Bulk read next page */
+ DBG (DBG_usb, "%s: USB-in-USB: bulk reading %lu bytes\n",
+ __FUNCTION__, (u_long) next_portion);
+ ret = sanei_usb_read_bulk (dn,
+ bulk_read_state->buffer_in_ptr,
+ &next_portion);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ if (ret == SANE_STATUS_EOF)
+ return ret;
+ DBG (DBG_err, "%s: USB-in-USB: error during bulk read: %s\n",
+ __FUNCTION__, sane_strstatus (ret));
+ return ret;
+ }
+
+ /* Check if we received the same amount of data as requsted */
+ if (next_portion != BULK_READ_PAGE_SIZE)
+ {
+ DBG (DBG_err, "%s: USB-in-USB: incomplete bulk read "
+ "(requested %u bytes, got %lu bytes)\n",
+ __FUNCTION__, BULK_READ_PAGE_SIZE, (u_long) next_portion);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Move pointers to the next position */
+ bulk_read_state->buffer_in_ptr += next_portion;
+
+ /* Check for the end of the buffer */
+ if (bulk_read_state->buffer_in_ptr > bulk_read_state->buffer_end_ptr)
+ {
+ DBG (DBG_err,
+ "%s: USB-in-USB: attempted to access over the end of buffer "
+ "(in_ptr: %p, end_ptr: %p, ptr: %p, buffer size: %u\n",
+ __FUNCTION__, bulk_read_state->buffer_in_ptr,
+ bulk_read_state->buffer_end_ptr, bulk_read_state->buffer,
+ bulk_read_state->buffer_size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Check for buffer pointer wrapping */
+ if (bulk_read_state->buffer_in_ptr == bulk_read_state->buffer_end_ptr)
+ {
+ DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while writing\n",
+ __FUNCTION__);
+ bulk_read_state->buffer_in_ptr = bulk_read_state->buffer;
+ }
+
+ /* Count the amount of data we read */
+ bulk_read_state->bytes_available += next_portion;
+ }
+
+ /* Transfer requested amount of data to the caller */
+ DBG (DBG_usb, "%s: USB-in-USB: data in bulk buffer is available "
+ "(requested %u bytes, available %u bytes)\n",
+ __FUNCTION__, size, bulk_read_state->bytes_available);
+
+ /* Check for buffer pointer wrapping */
+ bytes_until_buffer_end = bulk_read_state->buffer_end_ptr
+ - bulk_read_state->buffer_out_ptr;
+ if (bytes_until_buffer_end <= size)
+ {
+ /* First buffer part */
+ DBG (DBG_usb, "%s: USB-in-USB: reached bulk read buffer end\n", __FUNCTION__);
+ memcpy (bytes, bulk_read_state->buffer_out_ptr, bytes_until_buffer_end);
+ bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
+ /* And second part (if any) */
+ if (bytes_until_buffer_end < size)
+ {
+ DBG (DBG_usb, "%s: USB-in-USB: giving 2nd buffer part\n", __FUNCTION__);
+ memcpy (bytes + bytes_until_buffer_end,
+ bulk_read_state->buffer_out_ptr,
+ size - bytes_until_buffer_end);
+ bulk_read_state->buffer_out_ptr += size - bytes_until_buffer_end;
+ }
+ }
+ else
+ {
+ /* The data is in one buffer part (w/o wrapping) */
+ memcpy (bytes, bulk_read_state->buffer_out_ptr, size);
+ bulk_read_state->buffer_out_ptr += size;
+ if (bulk_read_state->buffer_out_ptr == bulk_read_state->buffer_end_ptr)
+ {
+ DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while reading\n",
+ __FUNCTION__);
+ bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
+ }
+ }
+
+ /* Count the amount of data transferred to the caller */
+ bulk_read_state->bytes_available -= size;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*******************************************************************************
+ * USB-in-USB: bulk write
+ *
+ * Parameters
+ * dn - sanei_usb device descriptor
+ * cmd - command for bulk write operation
+ * bytes - pointer to data buffer
+ * size - size of data
+ *
+ * Returns
+ * SANE_STATUS_GOOD - all data transferred successfully
+ * all other SANE_Status value - otherwise
+ */
+static SANE_Status
+hp5590_bulk_write (SANE_Int dn,
+ enum proto_flags proto_flags,
+ int cmd, unsigned char *bytes,
+ unsigned int size)
+{
+ struct usb_in_usb_bulk_setup ctrl;
+ SANE_Status ret;
+ struct bulk_size bulk_size;
+
+ unsigned int len;
+ unsigned char *ptr;
+ size_t next_portion;
+
+ DBG (3, "%s: USB-in-USB: command: %04x, size %u\n", __FUNCTION__, cmd,
+ size);
+
+ hp5590_low_assert (bytes != NULL);
+
+ /* Prepare bulk write request */
+ memset (&bulk_size, 0, sizeof (bulk_size));
+ /* Counted in page size */
+ bulk_size.size = size / BULK_WRITE_PAGE_SIZE;
+
+ /* Send bulk write request */
+ DBG (3, "%s: USB-in-USB: total %u pages (each of %u bytes)\n",
+ __FUNCTION__, bulk_size.size, BULK_WRITE_PAGE_SIZE);
+ ret = hp5590_control_msg (dn,
+ proto_flags,
+ USB_DIR_OUT,
+ 0x04, cmd, 0,
+ (unsigned char *) &bulk_size, sizeof (bulk_size),
+ CORE_DATA | CORE_BULK_OUT);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ len = size;
+ ptr = bytes;
+
+ /* Send all data in pages */
+ while (len)
+ {
+ next_portion = BULK_WRITE_PAGE_SIZE;
+ if (len < next_portion)
+ next_portion = len;
+
+ DBG (3, "%s: USB-in-USB: next portion %lu bytes\n",
+ __FUNCTION__, (u_long) next_portion);
+
+ /* Prepare bulk write request */
+ memset (&ctrl, 0, sizeof (ctrl));
+ ctrl.bRequestType = 0x01;
+ ctrl.bEndpoint = 0x82;
+ ctrl.wLength = htons (next_portion);
+
+ /* Send bulk write request */
+ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
+ 0x04, 0x82, 0,
+ sizeof (ctrl), (unsigned char *) &ctrl);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* USB-in-USB: checking if command was accepted */
+ ret = hp5590_get_ack (dn, proto_flags);
+ if (ret != SANE_STATUS_GOOD)
+ return ret;
+
+ /* Write bulk data */
+ DBG (3, "%s: USB-in-USB: bulk writing %lu bytes\n",
+ __FUNCTION__, (u_long) next_portion);
+ ret = sanei_usb_write_bulk (dn, ptr, &next_portion);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ /* Treast EOF as successful result */
+ if (ret == SANE_STATUS_EOF)
+ break;
+ DBG (DBG_err, "%s: USB-in-USB: error during bulk write: %s\n",
+ __FUNCTION__, sane_strstatus (ret));
+ return ret;
+ }
+
+ /* Move to the next page */
+ len -= next_portion;
+ ptr += next_portion;
+ }
+
+ /* Verify bulk command */
+ return hp5590_verify_last_cmd (dn, proto_flags, cmd);
+}
+/* vim: sw=2 ts=8
+ */
diff --git a/backend/hp5590_low.h b/backend/hp5590_low.h
new file mode 100644
index 0000000..5ca191d
--- /dev/null
+++ b/backend/hp5590_low.h
@@ -0,0 +1,89 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is part of a SANE backend for
+ HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners
+*/
+
+#ifndef HP5590_LOW_H
+#define HP5590_LOW_H
+
+#include "../include/sane/sane.h"
+
+enum proto_flags {
+ PF_NONE = 0,
+ PF_NO_USB_IN_USB_ACK = 1 << 0 /* Getting acknowledge after USB-in-USB command
+ * will be skipped */
+};
+
+/* Flags for hp5590_cmd() */
+#define CMD_IN 1 << 0 /* Indicates IN direction, otherwise - OUT */
+#define CMD_VERIFY 1 << 1 /* Requests last command verification */
+
+/* Core flags for hp5590_cmd() - they indicate so called CORE commands */
+#define CORE_NONE 0 /* No CORE operation */
+#define CORE_DATA 1 << 0 /* Operate on CORE data */
+#define CORE_BULK_IN 1 << 1 /* CORE bulk operation - prepare for bulk IN
+ * transfer (not used yet)
+ */
+#define CORE_BULK_OUT 1 << 2 /* CORE bulk operation - prepare for bulk OUT
+ * transfer
+ */
+static SANE_Status hp5590_cmd (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned int flags,
+ unsigned int cmd, unsigned char *data,
+ unsigned int size, unsigned int core_flags);
+static SANE_Status hp5590_bulk_read (SANE_Int dn,
+ enum proto_flags proto_flags,
+ unsigned char *bytes,
+ unsigned int size, void *state);
+static SANE_Status hp5590_bulk_write (SANE_Int dn,
+ enum proto_flags proto_flags,
+ int cmd,
+ unsigned char *bytes,
+ unsigned int size);
+static SANE_Status hp5590_get_status (SANE_Int dn,
+ enum proto_flags proto_flags);
+static SANE_Status hp5590_low_init_bulk_read_state (void **state);
+static SANE_Status hp5590_low_free_bulk_read_state (void **state);
+#endif /* HP5590_LOW_H */
+/* vim: sw=2 ts=8
+ */
diff --git a/backend/hpljm1005.c b/backend/hpljm1005.c
new file mode 100644
index 0000000..9460c48
--- /dev/null
+++ b/backend/hpljm1005.c
@@ -0,0 +1,1104 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2008 Philippe Rétornaz
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This backend is for HP LaserJet M1005 MFP
+
+ Highly inspired from the epson backend
+*/
+
+#define BUILD 1
+
+#include "../include/sane/config.h"
+#include <math.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <netinet/in.h>
+#define BACKEND_NAME hpljm1005
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+
+#define MAGIC_NUMBER 0x41535001
+#define PKT_READ_STATUS 0x0
+#define PKT_UNKNOW_1 0x1
+#define PKT_START_SCAN 0x2
+#define PKT_GO_IDLE 0x3
+#define PKT_DATA 0x5
+#define PKT_READCONF 0x6
+#define PKT_SETCONF 0x7
+#define PKT_END_DATA 0xe
+#define PKT_RESET 0x15
+
+#define RED_LAYER 0x3
+#define GREEN_LAYER 0x4
+#define BLUE_LAYER 0x5
+#define GRAY_LAYER 0x6
+
+#define MIN_SCAN_ZONE 101
+
+struct usbdev_s
+{
+ SANE_Int vendor_id;
+ SANE_Int product_id;
+ SANE_String_Const vendor_s;
+ SANE_String_Const model_s;
+ SANE_String_Const type_s;
+};
+
+/* Zero-terminated USB VID/PID array */
+static struct usbdev_s usbid[] = {
+ {0x03f0, 0x3b17, "Hewlett-Packard", "LaserJet M1005",
+ "multi-function peripheral"},
+ {0x03f0, 0x5617, "Hewlett-Packard", "LaserJet M1120",
+ "multi-function peripheral"},
+ {0x03f0, 0x5717, "Hewlett-Packard", "LaserJet M1120n",
+ "multi-function peripheral"},
+ {0, 0, NULL, NULL, NULL},
+ {0, 0, NULL, NULL, NULL}
+};
+
+static int cur_idx;
+
+#define BR_CONT_MIN 0x1
+#define BR_CONT_MAX 0xb
+
+#define RGB 1
+#define GRAY 0
+
+#define MAX_X_H 0x350
+#define MAX_Y_H 0x490
+#define MAX_X_S 220
+#define MAX_Y_S 330
+
+#define OPTION_MAX 9
+
+static SANE_Word resolution_list[] = {
+ 7, 75, 100, 150, 200, 300, 600, 1200
+};
+static SANE_Range range_x = { 0, MAX_X_S, 0 };
+static SANE_Range range_y = { 0, MAX_Y_S, 0 };
+
+static SANE_Range range_br_cont = { BR_CONT_MIN, BR_CONT_MAX, 0 };
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+#define X1_OFFSET 2
+#define X2_OFFSET 4
+#define Y1_OFFSET 3
+#define Y2_OFFSET 5
+#define RES_OFFSET 1
+#define COLOR_OFFSET 8
+#define BRIGH_OFFSET 6
+#define CONTR_OFFSET 7
+
+#define STATUS_IDLE 0
+#define STATUS_SCANNING 1
+#define STATUS_CANCELING 2
+
+struct device_s
+{
+ struct device_s *next;
+ SANE_String_Const devname;
+ int idx; /* Index in the usbid array */
+ int dn; /* Usb "Handle" */
+ SANE_Option_Descriptor optiond[OPTION_MAX];
+ char *buffer;
+ int bufs;
+ int read_offset;
+ int write_offset_r;
+ int write_offset_g;
+ int write_offset_b;
+ int status;
+ int width;
+ int height;
+ SANE_Word optionw[OPTION_MAX];
+ uint32_t conf_data[512];
+ uint32_t packet_data[512];
+};
+
+
+static void
+do_cancel(struct device_s *dev);
+
+
+static struct device_s *devlist_head;
+static int devlist_count; /* Number of element in the list */
+
+/*
+ * List of pointers to devices - will be dynamically allocated depending
+ * on the number of devices found.
+ */
+static SANE_Device **devlist = NULL;
+
+/* round() is c99, so we provide our own, though this version wont return -0 */
+static double
+round2(double x)
+{
+ return (double)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5);
+}
+
+static void
+update_img_size (struct device_s *dev)
+{
+ int dx, dy;
+
+ /* Only update the width when not scanning,
+ * otherwise the scanner give us the correct width */
+ if (dev->status == STATUS_SCANNING)
+ {
+ dev->height = -1;
+ return;
+ }
+
+ dx = dev->optionw[X2_OFFSET] - dev->optionw[X1_OFFSET];
+ dy = dev->optionw[Y2_OFFSET] - dev->optionw[Y1_OFFSET];
+
+ switch (dev->optionw[RES_OFFSET])
+ {
+ case 75:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 640);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 880);
+ break;
+ case 100:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 848);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 1180);
+ break;
+ case 150:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 1264);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 1775);
+ break;
+ case 200:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 1696);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 2351);
+ break;
+ case 300:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 2528);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 3510);
+ break;
+ case 600:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 5088);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 7020);
+ break;
+ case 1200:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 10208);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 14025);
+ break;
+ }
+
+ DBG(2,"New image size: %dx%d\n",dev->width, dev->height);
+
+}
+
+/* This function is copy/pasted from the Epson backend */
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static SANE_Status
+attach (SANE_String_Const devname)
+{
+ struct device_s *dev;
+
+ dev = malloc (sizeof (struct device_s));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, 0, sizeof (struct device_s));
+
+ dev->devname = devname;
+ DBG(1,"New device found: %s\n",dev->devname);
+
+/* Init the whole structure with default values */
+ /* Number of options */
+ dev->optiond[0].name = "";
+ dev->optiond[0].title = NULL;
+ dev->optiond[0].desc = NULL;
+ dev->optiond[0].type = SANE_TYPE_INT;
+ dev->optiond[0].unit = SANE_UNIT_NONE;
+ dev->optiond[0].size = sizeof (SANE_Word);
+ dev->optionw[0] = OPTION_MAX;
+
+ /* resolution */
+ dev->optiond[RES_OFFSET].name = "resolution";
+ dev->optiond[RES_OFFSET].title = "resolution";
+ dev->optiond[RES_OFFSET].desc = "resolution";
+ dev->optiond[RES_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[RES_OFFSET].unit = SANE_UNIT_DPI;
+ dev->optiond[RES_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[RES_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[RES_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[RES_OFFSET].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->optiond[RES_OFFSET].constraint.word_list = resolution_list;
+ dev->optionw[RES_OFFSET] = 75;
+
+ /* scan area */
+ dev->optiond[X1_OFFSET].name = "tl-x";
+ dev->optiond[X1_OFFSET].title = "tl-x";
+ dev->optiond[X1_OFFSET].desc = "tl-x";
+ dev->optiond[X1_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[X1_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[X1_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[X1_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[X1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[X1_OFFSET].constraint.range = &range_x;
+ dev->optionw[X1_OFFSET] = 0;
+
+ dev->optiond[Y1_OFFSET].name = "tl-y";
+ dev->optiond[Y1_OFFSET].title = "tl-y";
+ dev->optiond[Y1_OFFSET].desc = "tl-y";
+ dev->optiond[Y1_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[Y1_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[Y1_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[Y1_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[Y1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[Y1_OFFSET].constraint.range = &range_y;
+ dev->optionw[Y1_OFFSET] = 0;
+
+ dev->optiond[X2_OFFSET].name = "br-x";
+ dev->optiond[X2_OFFSET].title = "br-x";
+ dev->optiond[X2_OFFSET].desc = "br-x";
+ dev->optiond[X2_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[X2_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[X2_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[X2_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[X2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[X2_OFFSET].constraint.range = &range_x;
+ dev->optionw[X2_OFFSET] = MAX_X_S;
+
+ dev->optiond[Y2_OFFSET].name = "br-y";
+ dev->optiond[Y2_OFFSET].title = "br-y";
+ dev->optiond[Y2_OFFSET].desc = "br-y";
+ dev->optiond[Y2_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[Y2_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[Y2_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[Y2_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[Y2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[Y2_OFFSET].constraint.range = &range_y;
+ dev->optionw[Y2_OFFSET] = MAX_Y_S;
+
+ /* brightness */
+ dev->optiond[BRIGH_OFFSET].name = "brightness";
+ dev->optiond[BRIGH_OFFSET].title = "Brightness";
+ dev->optiond[BRIGH_OFFSET].desc = "Set the brightness";
+ dev->optiond[BRIGH_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[BRIGH_OFFSET].unit = SANE_UNIT_NONE;
+ dev->optiond[BRIGH_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[BRIGH_OFFSET].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[BRIGH_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[BRIGH_OFFSET].constraint.range = &range_br_cont;
+ dev->optionw[BRIGH_OFFSET] = 0x6;
+
+ /* contrast */
+ dev->optiond[CONTR_OFFSET].name = "contrast";
+ dev->optiond[CONTR_OFFSET].title = "Contrast";
+ dev->optiond[CONTR_OFFSET].desc = "Set the contrast";
+ dev->optiond[CONTR_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[CONTR_OFFSET].unit = SANE_UNIT_NONE;
+ dev->optiond[CONTR_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[CONTR_OFFSET].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[CONTR_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[CONTR_OFFSET].constraint.range = &range_br_cont;
+ dev->optionw[CONTR_OFFSET] = 0x6;
+
+ /* Color */
+ dev->optiond[COLOR_OFFSET].name = SANE_NAME_SCAN_MODE;
+ dev->optiond[COLOR_OFFSET].title = SANE_TITLE_SCAN_MODE;
+ dev->optiond[COLOR_OFFSET].desc = SANE_DESC_SCAN_MODE;
+ dev->optiond[COLOR_OFFSET].type = SANE_TYPE_STRING;
+ dev->optiond[COLOR_OFFSET].size = max_string_size (mode_list);
+ dev->optiond[COLOR_OFFSET].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[COLOR_OFFSET].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->optiond[COLOR_OFFSET].constraint.string_list = mode_list;
+ dev->optionw[COLOR_OFFSET] = RGB;
+ dev->dn = 0;
+ dev->idx = cur_idx;
+ dev->status = STATUS_IDLE;
+
+ dev->next = devlist_head;
+ devlist_head = dev;
+ devlist_count++;
+
+
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG_INIT();
+
+ sanei_usb_init ();
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ /* free everything */
+ struct device_s *iter;
+
+ if (devlist)
+ {
+ int i;
+ for (i = 0; devlist[i]; i++)
+ free (devlist[i]);
+ free (devlist);
+ devlist = NULL;
+ }
+ if (devlist_head)
+ {
+ iter = devlist_head->next;
+ free (devlist_head);
+ devlist_head = NULL;
+ while (iter)
+ {
+ struct device_s *tmp = iter;
+ iter = iter->next;
+ free (tmp);
+ }
+ }
+ devlist_count = 0;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device * **device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ struct device_s *iter;
+ int i;
+
+ devlist_count = 0;
+
+ if (devlist_head)
+ {
+ iter = devlist_head->next;
+ free (devlist_head);
+ devlist_head = NULL;
+ while (iter)
+ {
+ struct device_s *tmp = iter;
+ iter = iter->next;
+ free (tmp);
+ }
+ }
+
+ /* Rebuild our internal scanner list */
+ for (cur_idx = 0; usbid[cur_idx].vendor_id; cur_idx++)
+ sanei_usb_find_devices (usbid[cur_idx].vendor_id,
+ usbid[cur_idx].product_id, attach);
+
+ if (devlist)
+ {
+ for (i = 0; devlist[i]; i++)
+ free (devlist[i]);
+ free (devlist);
+ }
+
+ /* rebuild the sane-API scanner list array */
+ devlist = malloc (sizeof (devlist[0]) * (devlist_count + 1));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ memset (devlist, 0, sizeof (devlist[0]) * (devlist_count + 1));
+
+ for (i = 0, iter = devlist_head; i < devlist_count; i++, iter = iter->next)
+ {
+ devlist[i] = malloc (sizeof (SANE_Device));
+ if (!devlist[i])
+ {
+ int j;
+ for (j = 0; j < i; j++)
+ free (devlist[j]);
+ free (devlist);
+ devlist = NULL;
+ return SANE_STATUS_NO_MEM;
+ }
+ devlist[i]->name = iter->devname;
+ devlist[i]->vendor = usbid[iter->idx].vendor_s;
+ devlist[i]->model = usbid[iter->idx].model_s;
+ devlist[i]->type = usbid[iter->idx].type_s;
+ }
+ if (device_list)
+ *device_list = (const SANE_Device **) devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ struct device_s *dev;
+ int ret;
+
+ if(!devlist_head)
+ sane_get_devices(NULL,(SANE_Bool)0);
+
+ dev = devlist_head;
+
+ if (strlen (name))
+ for (; dev; dev = dev->next)
+ if (!strcmp (name, dev->devname))
+ break;
+
+ if (!dev) {
+ DBG(1,"Unable to find device %s\n",name);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(1,"Found device %s\n",name);
+
+ /* Now open the usb device */
+ ret = sanei_usb_open (name, &(dev->dn));
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(1,"Unable to open device %s\n",name);
+ return ret;
+ }
+
+ /* Claim the first interface */
+ ret = sanei_usb_claim_interface (dev->dn, 0);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ sanei_usb_close (dev->dn);
+ /* if we cannot claim the interface, this is because
+ someone else is using it */
+ DBG(1,"Unable to claim scanner interface on device %s\n",name);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+#ifdef HAVE_SANEI_USB_SET_TIMEOUT
+ sanei_usb_set_timeout (30000); /* 30s timeout */
+#endif
+
+ *h = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ /* Just in case if sane_cancel() is called
+ * after starting a scan but not while a sane_read
+ */
+ if (dev->status == STATUS_CANCELING)
+ {
+ do_cancel(dev);
+ }
+
+ sanei_usb_release_interface (dev->dn, 0);
+ sanei_usb_close (dev->dn);
+
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int option)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ if (option >= OPTION_MAX || option < 0)
+ return NULL;
+ return &(dev->optiond[option]);
+}
+
+static SANE_Status
+getvalue (SANE_Handle h, SANE_Int option, void *v)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ if (option != COLOR_OFFSET)
+ *((SANE_Word *) v) = dev->optionw[option];
+ else
+ {
+ strcpy ((char *) v,
+ dev->optiond[option].constraint.string_list[dev->
+ optionw[option]]);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+setvalue (SANE_Handle h, SANE_Int option, void *value, SANE_Int * info)
+{
+ struct device_s *dev = (struct device_s *) h;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int s_unit;
+ int s_unit_2;
+
+ if (option == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+
+ status = sanei_constrain_value (&(dev->optiond[option]), value, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ switch (option)
+ {
+ case X1_OFFSET:
+ dev->optionw[option] = *((SANE_Word *) value);
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_X_S))
+ * MAX_X_H);
+ s_unit_2 = (int) round2 ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S))
+ * MAX_X_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 - MIN_SCAN_ZONE;
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_X_H)) * MAX_X_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+
+ case X2_OFFSET:
+ /* X units */
+ /* convert into "scanner" unit, then back into mm */
+ dev->optionw[option] = *((SANE_Word *) value);
+
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_X_S))
+ * MAX_X_H);
+ s_unit_2 = (int) round2 ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S))
+ * MAX_X_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 + MIN_SCAN_ZONE;
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_X_H)) * MAX_X_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+ case Y1_OFFSET:
+ /* Y units */
+ dev->optionw[option] = *((SANE_Word *) value);
+
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+
+ s_unit_2 = (int) round2 ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 - MIN_SCAN_ZONE;
+
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_Y_H)) * MAX_Y_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+ case Y2_OFFSET:
+ /* Y units */
+ dev->optionw[option] = *((SANE_Word *) value);
+
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+
+ s_unit_2 = (int) round2 ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 + MIN_SCAN_ZONE;
+
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_Y_H)) * MAX_Y_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+ case COLOR_OFFSET:
+ if (!strcmp ((char *) value, mode_list[0]))
+ dev->optionw[option] = GRAY; /* Gray */
+ else if (!strcmp ((char *) value, mode_list[1]))
+ dev->optionw[option] = RGB; /* RGB */
+ else
+ return SANE_STATUS_INVAL;
+ break;
+ default:
+ dev->optionw[option] = *((SANE_Word *) value);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int option,
+ SANE_Action a, void *v, SANE_Int * i)
+{
+
+ if (option < 0 || option >= OPTION_MAX)
+ return SANE_STATUS_INVAL;
+
+ if (i)
+ *i = 0;
+
+
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+ return getvalue (h, option, v);
+
+ case SANE_ACTION_SET_VALUE:
+ return setvalue (h, option, v, i);
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ if (!p)
+ return SANE_STATUS_INVAL;
+
+ p->format =
+ dev->optionw[COLOR_OFFSET] == RGB ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ p->last_frame = SANE_TRUE;
+ p->depth = 8;
+
+ update_img_size (dev);
+ p->pixels_per_line = dev->width;
+ p->lines = dev->height;
+ p->bytes_per_line = p->pixels_per_line;
+ if (p->format == SANE_FRAME_RGB)
+ p->bytes_per_line *= 3;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+send_pkt (int command, int data_size, struct device_s *dev)
+{
+ size_t size = 32;
+
+ DBG(100,"Sending packet %d, next data size %d, device %s\n", command, data_size, dev->devname);
+
+ memset (dev->packet_data, 0, size);
+ dev->packet_data[0] = htonl (MAGIC_NUMBER);
+ dev->packet_data[1] = htonl (command);
+ dev->packet_data[5] = htonl (data_size);
+ sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->packet_data, &size);
+}
+
+
+/* s: printer status */
+/* Return the next packet size */
+static int
+wait_ack (struct device_s *dev, int *s)
+{
+ SANE_Status ret;
+ size_t size;
+ DBG(100, "Waiting scanner answer on device %s\n",dev->devname);
+ do
+ {
+ size = 32;
+ ret =
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->packet_data,
+ &size);
+ }
+ while (SANE_STATUS_EOF == ret || size == 0);
+ if (s)
+ *s = ntohl (dev->packet_data[4]);
+ return ntohl (dev->packet_data[5]);
+}
+
+static void
+send_conf (struct device_s *dev)
+{
+ int y1, y2, x1, x2;
+ size_t size = 100;
+ DBG(100,"Sending configuration packet on device %s\n",dev->devname);
+ y1 = (int) round2 ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H);
+ y2 = (int) round2 ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H);
+ x1 = (int) round2 ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S)) * MAX_X_H);
+ x2 = (int) round2 ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S)) * MAX_X_H);
+
+ DBG(100,"\t x1: %d, x2: %d, y1: %d, y2: %d\n",x1, x2, y1, y2);
+ DBG(100,"\t brightness: %d, contrast: %d\n", dev->optionw[BRIGH_OFFSET], dev->optionw[CONTR_OFFSET]);
+ DBG(100,"\t resolution: %d\n",dev->optionw[RES_OFFSET]);
+
+ dev->conf_data[0] = htonl (0x15);
+ dev->conf_data[1] = htonl (dev->optionw[BRIGH_OFFSET]);
+ dev->conf_data[2] = htonl (dev->optionw[CONTR_OFFSET]);
+ dev->conf_data[3] = htonl (dev->optionw[RES_OFFSET]);
+ dev->conf_data[4] = htonl (0x1);
+ dev->conf_data[5] = htonl (0x1);
+ dev->conf_data[6] = htonl (0x1);
+ dev->conf_data[7] = htonl (0x1);
+ dev->conf_data[8] = 0;
+ dev->conf_data[9] = 0;
+ dev->conf_data[10] = htonl (0x8);
+ dev->conf_data[11] = 0;
+ dev->conf_data[12] = 0;
+ dev->conf_data[13] = 0;
+ dev->conf_data[14] = 0;
+ dev->conf_data[16] = htonl (y1);
+ dev->conf_data[17] = htonl (x1);
+ dev->conf_data[18] = htonl (y2);
+ dev->conf_data[19] = htonl (x2);
+ dev->conf_data[20] = 0;
+ dev->conf_data[21] = 0;
+ dev->conf_data[22] = htonl (0x491);
+ dev->conf_data[23] = htonl (0x352);
+
+ if (dev->optionw[COLOR_OFFSET] == RGB)
+ {
+ dev->conf_data[15] = htonl (0x2);
+ dev->conf_data[24] = htonl (0x1);
+ DBG(100,"\t Scanning in RGB format\n");
+ }
+ else
+ {
+ dev->conf_data[15] = htonl (0x6);
+ dev->conf_data[24] = htonl (0x0);
+ DBG(100,"\t Scanning in Grayscale format\n");
+ }
+ sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+}
+
+static SANE_Status
+get_data (struct device_s *dev)
+{
+ int color;
+ size_t size;
+ int packet_size;
+ unsigned char *buffer = (unsigned char *) dev->packet_data;
+ if (dev->status == STATUS_IDLE)
+ return SANE_STATUS_IO_ERROR;
+ /* first wait a standard data pkt */
+ do
+ {
+ size = 32;
+ sanei_usb_read_bulk (dev->dn, buffer, &size);
+ if (size)
+ {
+ if (ntohl (dev->packet_data[0]) == MAGIC_NUMBER)
+ {
+ if (ntohl (dev->packet_data[1]) == PKT_DATA)
+ break;
+ if (ntohl (dev->packet_data[1]) == PKT_END_DATA)
+ {
+ dev->status = STATUS_IDLE;
+ DBG(100,"End of scan encountered on device %s\n",dev->devname);
+ send_pkt (PKT_GO_IDLE, 0, dev);
+ wait_ack (dev, NULL);
+ wait_ack (dev, NULL);
+ send_pkt (PKT_UNKNOW_1, 0, dev);
+ wait_ack (dev, NULL);
+ send_pkt (PKT_RESET, 0, dev);
+ sleep (2); /* Time for the scanning head to go back home */
+ return SANE_STATUS_EOF;
+ }
+ }
+ }
+ }
+ while (1);
+ packet_size = ntohl (dev->packet_data[5]);
+ if (!dev->buffer)
+ {
+ dev->bufs = packet_size - 24 /* size of header */ ;
+ if (dev->optionw[COLOR_OFFSET] == RGB)
+ dev->bufs *= 3;
+ dev->buffer = malloc (dev->bufs);
+ if (!dev->buffer)
+ return SANE_STATUS_NO_MEM;
+ dev->write_offset_r = 0;
+ dev->write_offset_g = 1;
+ dev->write_offset_b = 2;
+
+ }
+ /* Get the "data header" */
+ do
+ {
+ size = 24;
+ sanei_usb_read_bulk (dev->dn, buffer, &size);
+ }
+ while (!size);
+ color = ntohl (dev->packet_data[0]);
+ packet_size -= size;
+ dev->width = ntohl (dev->packet_data[5]);
+ DBG(100,"Got data size %d on device %s. Scan width: %d\n",packet_size, dev->devname, dev->width);
+ /* Now, read the data */
+ do
+ {
+ int j;
+ int i;
+ int ret;
+ do
+ {
+ size = packet_size > 512 ? 512 : packet_size;
+ ret = sanei_usb_read_bulk (dev->dn, buffer, &size);
+ }
+ while (!size || ret != SANE_STATUS_GOOD);
+ packet_size -= size;
+ switch (color)
+ {
+ case RED_LAYER:
+ DBG(101,"Got red layer data on device %s\n",dev->devname);
+ i = dev->write_offset_r + 3 * size;
+ if (i > dev->bufs)
+ i = dev->bufs;
+ for (j = 0; dev->write_offset_r < i; dev->write_offset_r += 3)
+ dev->buffer[dev->write_offset_r] = buffer[j++];
+ break;
+ case GREEN_LAYER:
+ DBG(101,"Got green layer data on device %s\n",dev->devname);
+ i = dev->write_offset_g + 3 * size;
+ if (i > dev->bufs)
+ i = dev->bufs;
+ for (j = 0; dev->write_offset_g < i; dev->write_offset_g += 3)
+ dev->buffer[dev->write_offset_g] = buffer[j++];
+ break;
+ case BLUE_LAYER:
+ DBG(101,"Got blue layer data on device %s\n",dev->devname);
+ i = dev->write_offset_b + 3 * size;
+ if (i > dev->bufs)
+ i = dev->bufs;
+ for (j = 0; dev->write_offset_b < i; dev->write_offset_b += 3)
+ dev->buffer[dev->write_offset_b] = buffer[j++];
+ break;
+ case GRAY_LAYER:
+ DBG(101,"Got gray layer data on device %s\n",dev->devname);
+ if (dev->write_offset_r + (int)size >= dev->bufs)
+ size = dev->bufs - dev->write_offset_r;
+ memcpy (dev->buffer + dev->write_offset_r, buffer, size);
+ dev->write_offset_r += size;
+ break;
+ }
+ }
+ while (packet_size > 0);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ struct device_s *dev = (struct device_s *) h;
+ int status;
+ size_t size;
+
+ dev->read_offset = 0;
+ dev->write_offset_r = 0;
+ dev->write_offset_g = 1;
+ dev->write_offset_b = 2;
+
+ free (dev->buffer);
+ dev->buffer = NULL;
+
+
+ send_pkt (PKT_RESET, 0, dev);
+ send_pkt (PKT_READ_STATUS, 0, dev);
+ wait_ack (dev, &status);
+ if (status)
+ return SANE_STATUS_IO_ERROR;
+
+ send_pkt (PKT_READCONF, 0, dev);
+
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+ send_pkt (PKT_SETCONF, 100, dev);
+ send_conf (dev);
+ wait_ack (dev, NULL);
+
+ send_pkt (PKT_START_SCAN, 0, dev);
+ wait_ack (dev, NULL);
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+
+ dev->status = STATUS_SCANNING;
+ /* Get the first data */
+ return get_data (dev);
+}
+
+
+static void
+do_cancel(struct device_s *dev)
+{
+ while (get_data (dev) == SANE_STATUS_GOOD);
+ free (dev->buffer);
+ dev->buffer = NULL;
+}
+
+static int
+min3 (int r, int g, int b)
+{
+ /* Optimize me ! */
+ g--;
+ b -= 2;
+ if (r < g && r < b)
+ return r;
+ if (b < r && b < g)
+ return b;
+ return g;
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ struct device_s *dev = (struct device_s *) h;
+ int available;
+ int ret;
+ *len = 0;
+ if (dev->status == STATUS_IDLE)
+ return SANE_STATUS_IO_ERROR;
+ if (dev->optionw[COLOR_OFFSET] == RGB)
+ {
+ while (min3 (dev->write_offset_r, dev->write_offset_g,
+ dev->write_offset_b) <= dev->read_offset)
+ {
+ ret = get_data (dev);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ if (min3 (dev->write_offset_r,
+ dev->write_offset_g,
+ dev->write_offset_b) <= dev->read_offset)
+ return ret;
+ }
+ }
+ available = min3 (dev->write_offset_r, dev->write_offset_g,
+ dev->write_offset_b);
+ }
+ else
+ {
+ while (dev->write_offset_r <= dev->read_offset)
+ {
+ ret = get_data (dev);
+ if (ret != SANE_STATUS_GOOD)
+ if (dev->write_offset_r <= dev->read_offset)
+ return ret;
+ }
+ available = dev->write_offset_r;
+ }
+ *len = available - dev->read_offset;
+ if (*len > maxlen)
+ *len = maxlen;
+ memcpy (buf, dev->buffer + dev->read_offset, *len);
+ dev->read_offset += *len;
+ if (dev->read_offset == dev->bufs)
+ {
+ free (dev->buffer);
+ dev->buffer = NULL;
+ dev->read_offset = 0;
+ dev->write_offset_r = 0;
+ dev->write_offset_g = 1;
+ dev->write_offset_b = 2;
+ }
+
+ /* Special case where sane_cancel is called while scanning */
+ if (dev->status == STATUS_CANCELING)
+ {
+ do_cancel(dev);
+ return SANE_STATUS_CANCELLED;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+
+ if (dev->status == STATUS_SCANNING)
+ {
+ dev->status = STATUS_CANCELING;
+ return;
+ }
+
+ free (dev->buffer);
+ dev->buffer = NULL;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ * fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
diff --git a/backend/hpsj5s.c b/backend/hpsj5s.c
new file mode 100644
index 0000000..75f3526
--- /dev/null
+++ b/backend/hpsj5s.c
@@ -0,0 +1,1568 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2002 Max Vorobiev <pcwizard@telecoms.sins.ru>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#define BUILD 3
+
+#define BACKEND_NAME hpsj5s
+#define HPSJ5S_CONFIG_FILE "hpsj5s.conf"
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+#include "hpsj5s.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+#define LINES_TO_FEED 480 /*Default feed length */
+
+static int scanner_d = -1; /*This is handler to the only-supported. Will be fixed. */
+static char scanner_path[PATH_MAX] = ""; /*String for device-file */
+static SANE_Byte bLastCalibration; /*Here we store calibration result */
+static SANE_Byte bCalibration; /*Here we store new calibration value */
+static SANE_Byte bHardwareState; /*Here we store copy of hardware flags register */
+
+/*Here we store Parameters:*/
+static SANE_Word wWidth = 2570; /*Scan area width */
+static SANE_Word wResolution = 300; /*Resolution in DPI */
+static SANE_Frame wCurrentFormat = SANE_FRAME_GRAY; /*Type of colors in image */
+static SANE_Int wCurrentDepth = 8; /*Bits per pixel in image */
+
+/*Here we count lines of every new image...*/
+static SANE_Word wVerticalResolution;
+
+/*Limits for resolution control*/
+static const SANE_Range ImageWidthRange = {
+ 0, /*minimal */
+ 2570, /*maximum */
+ 2 /*quant */
+};
+
+static const SANE_Word ImageResolutionsList[] = {
+ 6, /*Number of resolutions */
+ 75,
+ 100,
+ 150,
+ 200,
+ 250,
+ 300
+};
+
+static SANE_Option_Descriptor sod[] = {
+ { /*Number of options */
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL} /*No constraints required */
+ }
+ ,
+ { /*Width of scaned area */
+ "width",
+ "Width",
+ "Width of area to scan",
+ SANE_TYPE_INT,
+ SANE_UNIT_PIXEL,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {NULL} /*Range constrain setted in sane_init */
+ }
+ ,
+ { /*Resolution for scan */
+ "resolution",
+ "Resolution",
+ "Image resolution",
+ SANE_TYPE_INT,
+ SANE_UNIT_DPI,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_WORD_LIST,
+ {NULL} /*Word list constrain setted in sane_init */
+ }
+};
+
+static SANE_Parameters parms;
+
+/*Recalculate Lenght in dependace of resolution*/
+static SANE_Word
+LengthForRes (SANE_Word Resolution, SANE_Word Length)
+{
+ switch (Resolution)
+ {
+ case 75:
+ return Length / 4;
+ case 100:
+ return Length / 3;
+ case 150:
+ return Length / 2;
+ case 200:
+ return Length * 2 / 3;
+ case 250:
+ return Length * 5 / 6;
+ case 300:
+ default:
+ return Length;
+ }
+}
+
+static struct parport_list pl; /*List of detected parallel ports. */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char line[PATH_MAX]; /*Line from config file */
+ int len; /*Length of string from config file */
+ FILE *config_file; /*Handle to config file of this backend */
+
+ DBG_INIT ();
+ DBG (1, ">>sane_init");
+ DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+ version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+ DBG (1, "sane_init: SANE hpsj5s backend version %d.%d.%d\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ /*Inform about supported version */
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ /*Open configuration file for this backend */
+ config_file = sanei_config_open (HPSJ5S_CONFIG_FILE);
+
+ if (!config_file) /*Failed to open config file */
+ {
+ DBG (1, "sane_init: no config file found.");
+ return SANE_STATUS_GOOD;
+ }
+
+ /*Read line by line */
+ while (sanei_config_read (line, PATH_MAX, config_file))
+ {
+ if ((line[0] == '#') || (line[0] == '\0')) /*comment line or empty line */
+ continue;
+ len = strlen (line); /*sanei_config_read guaranty, it's not more then PATH_MAX-1 */
+ strcpy (scanner_path, line); /*so, we choose last in file (uncommented) */
+ }
+
+ fclose (config_file); /*We don't need config file any more */
+
+ /*sanei_config_attach_matching_devices(devname, attach_one); To do latter */
+
+ scanner_d = -1; /*scanner device not opened yet. */
+ DBG (1, "<<sane_init");
+
+ /*Init params structure with defaults values: */
+ wCurrentFormat = SANE_FRAME_GRAY;
+ wCurrentDepth = 8;
+ wWidth = 2570;
+ wResolution = 300;
+
+ /*Setup some option descriptors */
+ sod[1].constraint.range = &ImageWidthRange; /*Width option */
+ sod[2].constraint.word_list = &ImageResolutionsList[0]; /*Resolution option */
+
+ /*Search for ports in system: */
+ ieee1284_find_ports (&pl, 0);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ if (scanner_d != -1)
+ {
+ CloseScanner (scanner_d);
+ scanner_d = -1;
+ }
+
+ /*Free alocated ports information: */
+ ieee1284_free_ports (&pl);
+
+ DBG (2, "sane_exit\n");
+ return;
+}
+
+/* Device select/open/close */
+static const SANE_Device dev[] = {
+ {
+ "hpsj5s",
+ "Hewlett-Packard",
+ "ScanJet 5S",
+ "sheetfed scanner"}
+};
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ /*One device is supported and currently present */
+ static const SANE_Device *devlist[] = {
+ dev + 0, 0
+ };
+
+ /*No scanners presents */
+ static const SANE_Device *void_devlist[] = { 0 };
+
+ DBG (2, "sane_get_devices: local_only = %d\n", local_only);
+
+ if (scanner_d != -1) /*Device is opened, so it's present. */
+ {
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+ };
+
+ /*Device was not opened. */
+ scanner_d = OpenScanner (scanner_path);
+
+ if (scanner_d == -1) /*No devices present */
+ {
+ DBG (1, "failed to open scanner.\n");
+ *device_list = void_devlist;
+ return SANE_STATUS_GOOD;
+ }
+ DBG (1, "port opened.\n");
+
+ /*Check device. */
+ DBG (1, "sane_get_devices: check scanner started.");
+ if (DetectScanner () == 0)
+ { /*Device malfunction! */
+ DBG (1, "sane_get_devices: Device malfunction.");
+ *device_list = void_devlist;
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG (1, "sane_get_devices: Device works OK.");
+ *device_list = devlist;
+ }
+
+ /*We do not need it any more */
+ CloseScanner (scanner_d);
+ scanner_d = -1;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ int i;
+
+ if (!devicename)
+ {
+ DBG (1, "sane_open: devicename is NULL!");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (2, "sane_open: devicename = \"%s\"\n", devicename);
+
+ if (!devicename[0])
+ i = 0;
+ else
+ for (i = 0; i < NELEMS (dev); ++i) /*Search for device in list */
+ if (strcmp (devicename, dev[i].name) == 0)
+ break;
+
+ if (i >= NELEMS (dev)) /*No such device */
+ return SANE_STATUS_INVAL;
+
+ if (scanner_d != -1) /*scanner opened already! */
+ return SANE_STATUS_DEVICE_BUSY;
+
+ DBG (1, "sane_open: scanner device path name is \'%s\'\n", scanner_path);
+
+ scanner_d = OpenScanner (scanner_path);
+ if (scanner_d == -1)
+ return SANE_STATUS_DEVICE_BUSY; /*This should be done more carefully */
+
+ /*Check device. */
+ DBG (1, "sane_open: check scanner started.");
+ if (DetectScanner () == 0)
+ { /*Device malfunction! */
+ DBG (1, "sane_open: Device malfunction.");
+ CloseScanner (scanner_d);
+ scanner_d = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (1, "sane_open: Device found.All are green.");
+ *handle = (SANE_Handle) (unsigned long)scanner_d;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (2, "sane_close\n");
+ /*We support only single device - so ignore handle (FIX IT LATER) */
+ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
+ return; /* wrong device */
+ StandByScanner ();
+ CloseScanner (scanner_d);
+ scanner_d = -1;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ DBG (2, "sane_get_option_descriptor: option = %d\n", option);
+ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
+ return NULL; /* wrong device */
+
+ if (option < 0 || option >= NELEMS (sod)) /*No real options supported */
+ return NULL;
+
+ return &sod[option]; /*Return demanded option */
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
+ return SANE_STATUS_INVAL; /* wrong device */
+
+ if ((option >= NELEMS (sod)) || (option < 0)) /*Supported only this option */
+ return SANE_STATUS_INVAL;
+
+ switch (option)
+ {
+ case 0: /*Number of options */
+ if (action != SANE_ACTION_GET_VALUE) /*It can be only read */
+ return SANE_STATUS_INVAL;
+
+ *((SANE_Int *) value) = NELEMS (sod);
+ return SANE_STATUS_GOOD;
+ case 1: /*Scan area width */
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ *((SANE_Word *) value) = wWidth;
+ return SANE_STATUS_GOOD;
+ case SANE_ACTION_SET_VALUE: /*info should be setted */
+ wWidth = *((SANE_Word *) value);
+ if (info != NULL)
+ *info = SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ case 2: /*Resolution */
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ *((SANE_Word *) value) = wResolution;
+ return SANE_STATUS_GOOD;
+ case SANE_ACTION_SET_VALUE: /*info should be setted */
+ wResolution = *((SANE_Word *) value);
+ if (info != NULL)
+ *info = 0;
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD; /*For now we have no options to control */
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ DBG (2, "sane_get_parameters\n");
+
+ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
+ return SANE_STATUS_INVAL; /* wrong device */
+
+ /*Ignore handle parameter for now. FIX it latter. */
+ /*These parameters are OK for gray scale mode. */
+ parms.depth = /*wCurrentDepth */ 8;
+ parms.format = /*wCurrentFormat */ SANE_FRAME_GRAY;
+ parms.last_frame = SANE_TRUE; /*For grayscale... */
+ parms.lines = -1; /*Unknown a priory */
+ parms.pixels_per_line = LengthForRes (wResolution, wWidth); /*For grayscale... */
+ parms.bytes_per_line = parms.pixels_per_line; /*For grayscale... */
+ *params = parms;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int i;
+ DBG (2, "sane_start\n");
+
+ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
+ return SANE_STATUS_IO_ERROR;
+
+ CallFunctionWithParameter (0x93, 2);
+ bLastCalibration = CallFunctionWithRetVal (0xA9);
+ if (bLastCalibration == 0)
+ bLastCalibration = -1;
+
+ /*Turn on the lamp: */
+ CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, FLAGS_HW_LAMP_ON);
+ bHardwareState = FLAGS_HW_LAMP_ON;
+ /*Get average white point */
+ bCalibration = GetCalibration ();
+
+ if (bLastCalibration - bCalibration > 16)
+ { /*Lamp is not warm enouth */
+ DBG (1, "sane_start: warming lamp for 30 sec.\n");
+ for (i = 0; i < 30; i++)
+ sleep (1);
+ }
+
+ /*Check paper presents */
+ if (CheckPaperPresent () == 0)
+ {
+ DBG (1, "sane_start: no paper detected.");
+ return SANE_STATUS_NO_DOCS;
+ }
+ CalibrateScanElements ();
+ TransferScanParameters (GrayScale, wResolution, wWidth);
+ /*Turn on indicator and prepare engine. */
+ SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 1);
+ /*Feed paper */
+ if (PaperFeed (LINES_TO_FEED) == 0) /*Feed only for fixel lenght. Change it */
+ {
+ DBG (1, "sane_start: paper feed failed.");
+ SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 0);
+ return SANE_STATUS_JAMMED;
+ }
+ /*Set paper moving speed */
+ TurnOnPaperPulling (GrayScale, wResolution);
+
+ wVerticalResolution = 0; /*Reset counter */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ SANE_Byte bFuncResult, bTest;
+ int timeout;
+
+ if (!length)
+ {
+ DBG (1, "sane_read: length == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+ *length = 0;
+ if (!data)
+ {
+ DBG (1, "sane_read: data == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
+ {
+ DBG (1, "sane_read: unknown handle\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*While end of paper sheet was not reached */
+ /*Wait for scaned line ready */
+ timeout = 0;
+ while (((bFuncResult = CallFunctionWithRetVal (0xB2)) & 0x20) == 0)
+ {
+ bTest = CallFunctionWithRetVal (0xB5);
+ usleep (1);
+ timeout++;
+ if ((timeout < 1000) &&
+ (((bTest & 0x80) && ((bTest & 0x3F) <= 2)) ||
+ (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))))
+ continue;
+
+ if (timeout >= 1000)
+ continue; /*do it again! */
+
+ /*Data ready state! */
+
+ if ((bFuncResult & 0x20) != 0) /*End of paper reached! */
+ {
+ *length = 0;
+ return SANE_STATUS_EOF;
+ }
+
+ /*Data ready */
+ *length = LengthForRes (wResolution, wWidth);
+ if (*length >= max_length)
+ *length = max_length;
+
+ CallFunctionWithParameter (0xCD, 0);
+ CallFunctionWithRetVal (0xC8);
+ WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8);
+ WriteAddress (ADDRESS_RESULT);
+ /*Test if we need this line for current resolution
+ (scanner doesn't control vertical resolution in hardware) */
+ wVerticalResolution -= wResolution;
+ if (wVerticalResolution > 0)
+ {
+ timeout = 0;
+ continue;
+ }
+ else
+ wVerticalResolution = 300; /*Reset counter */
+
+ ReadDataBlock (data, *length);
+
+ /*switch indicator */
+ bHardwareState ^= FLAGS_HW_INDICATOR_OFF;
+ CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_EOF;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (2, "sane_cancel: handle = %p\n", handle);
+ /*Stop motor */
+ TurnOffPaperPulling ();
+
+ /*Indicator turn off */
+ bHardwareState |= FLAGS_HW_INDICATOR_OFF;
+ CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);
+
+ /*Get out of paper */
+ ReleasePaper ();
+
+ /*Restore indicator */
+ bHardwareState &= ~FLAGS_HW_INDICATOR_OFF;
+ CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);
+
+ bLastCalibration = CallFunctionWithRetVal (0xA9);
+ CallFunctionWithParameter (0xA9, bLastCalibration);
+ CallFunctionWithParameter (0x93, 4);
+
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
+ non_blocking);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
+ fd ? "!=" : "=");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ Middle-level API:
+*/
+
+/*
+ Detect if scanner present and works correctly.
+ Ret Val: 0 = detection failed, 1 = detection OK.
+*/
+static int
+DetectScanner (void)
+{
+ int Result1, Result2;
+ int Successful, Total;
+
+ Result1 = OutputCheck ();
+ Result2 = InputCheck ();
+
+ if (!(Result1 || Result2)) /*If all are 0 - it's error */
+ {
+ return 0;
+ }
+
+ WriteScannerRegister (0x7C, 0x80);
+ WriteScannerRegister (0x7F, 0x1);
+ WriteScannerRegister (0x72, 0x10);
+ WriteScannerRegister (0x72, 0x90);
+ WriteScannerRegister (0x7C, 0x24);
+ WriteScannerRegister (0x75, 0x0C);
+ WriteScannerRegister (0x78, 0x0);
+ WriteScannerRegister (0x79, 0x10);
+ WriteScannerRegister (0x71, 0x10);
+ WriteScannerRegister (0x71, 0x1);
+ WriteScannerRegister (0x72, 0x1);
+
+ for (Successful = 0, Total = 0; Total < 5; Total++)
+ {
+ if (CallCheck ())
+ Successful++;
+ if (Successful >= 3)
+ return 1; /*Correct and Stable */
+ }
+ return 0;
+}
+
+static void
+StandByScanner ()
+{
+ WriteScannerRegister (0x74, 0x80);
+ WriteScannerRegister (0x75, 0x0C);
+ WriteScannerRegister (0x77, 0x0);
+ WriteScannerRegister (0x78, 0x0);
+ WriteScannerRegister (0x79, 0x0);
+ WriteScannerRegister (0x7A, 0x0);
+ WriteScannerRegister (0x7B, 0x0);
+ WriteScannerRegister (0x7C, 0x4);
+ WriteScannerRegister (0x70, 0x0);
+ WriteScannerRegister (0x72, 0x90);
+ WriteScannerRegister (0x70, 0x0);
+}
+
+static void
+SwitchHardwareState (SANE_Byte mask, SANE_Byte invert_mask)
+{
+ if (!invert_mask)
+ {
+ bHardwareState &= ~mask;
+ }
+ else
+ bHardwareState |= mask;
+
+ CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);
+}
+
+/*return value: 0 - no paper, 1 - paper loaded.*/
+static int
+CheckPaperPresent ()
+{
+ if ((CallFunctionWithRetVal (0xB2) & 0x10) == 0)
+ return 1; /*Ok - paper present. */
+ return 0; /*No paper present */
+}
+
+static int
+ReleasePaper ()
+{
+ int i;
+
+ if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0)
+ { /*End of paper was not reached */
+ CallFunctionWithParameter (0xA7, 0xF);
+ CallFunctionWithParameter (0xA8, 0xFF);
+ CallFunctionWithParameter (0xC2, 0);
+
+ for (i = 0; i < 90000; i++)
+ {
+ if (CallFunctionWithRetVal (0xB2) & 0x80)
+ break;
+ usleep (1);
+ }
+ if (i >= 90000)
+ return 0; /*Fail. */
+
+ for (i = 0; i < 90000; i++)
+ {
+ if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0)
+ break;
+ else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
+ {
+ i = 90000;
+ break;
+ }
+ usleep (1);
+ }
+
+ CallFunctionWithParameter (0xC5, 0);
+
+ if (i >= 90000)
+ return 0; /*Fail. */
+
+ while (CallFunctionWithRetVal (0xB2) & 0x80); /*Wait bit dismiss */
+
+ CallFunctionWithParameter (0xA7, 1);
+ CallFunctionWithParameter (0xA8, 0x25);
+ CallFunctionWithParameter (0xC2, 0);
+
+ for (i = 0; i < 90000; i++)
+ {
+ if (CallFunctionWithRetVal (0xB2) & 0x80)
+ break;
+ usleep (1);
+ }
+ if (i >= 90000)
+ return 0; /*Fail. */
+
+ for (i = 0; i < 90000; i++)
+ {
+ if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
+ break;
+ usleep (1);
+ }
+ if (i >= 90000)
+ return 0; /*Fail. */
+ }
+
+ if (CallFunctionWithRetVal (0xB2) & 0x10)
+ {
+ CallFunctionWithParameter (0xA7, 1);
+ CallFunctionWithParameter (0xA8, 0x40);
+ }
+ else
+ {
+ CallFunctionWithParameter (0xA7, 0);
+ CallFunctionWithParameter (0xA8, 0xFA);
+ }
+ CallFunctionWithParameter (0xC2, 0);
+
+ for (i = 0; i < 9000; i++)
+ {
+ if (CallFunctionWithRetVal (0xB2) & 0x80)
+ break;
+ usleep (1);
+ }
+ if (i >= 9000)
+ return 0; /*Fail. */
+
+ while (CallFunctionWithRetVal (0xB2) & 0x80)
+ usleep (1);
+
+ return 1;
+}
+
+static void
+TransferScanParameters (enumColorDepth enColor, SANE_Word wResolution,
+ SANE_Word wPixelsLength)
+{
+ SANE_Word wRightBourder = (2570 + wPixelsLength) / 2 + 65;
+ SANE_Word wLeftBourder = (2570 - wPixelsLength) / 2 + 65;
+
+ switch (enColor)
+ {
+ case Drawing:
+ CallFunctionWithParameter (0x90, 2); /*Not supported correctle. FIX ME!!! */
+ break;
+ case Halftone:
+ CallFunctionWithParameter (0x90, 0xE3); /*Not supported correctly. FIX ME!!! */
+ CallFunctionWithParameter (0x92, 3);
+ break;
+ case GrayScale:
+ case TrueColor:
+ CallFunctionWithParameter (0x90, 0); /*Not suppoted correctly. FIX ME!!! */
+ break;
+ };
+ CallFunctionWithParameter (0xA1, 2);
+ CallFunctionWithParameter (0xA2, 1);
+ CallFunctionWithParameter (0xA3, 0x98);
+ /*Resolution: */
+ CallFunctionWithParameter (0x9A, (SANE_Byte) (wResolution >> 8)); /*High byte */
+ CallFunctionWithParameter (0x9B, (SANE_Byte) wResolution); /*Low byte */
+
+ LoadingPaletteToScanner ();
+
+ CallFunctionWithParameter (0xA4, 31); /*Some sort of constant parameter */
+ /*Left bourder */
+ CallFunctionWithParameter (0xA5, wLeftBourder / 256);
+ CallFunctionWithParameter (0xA6, wLeftBourder % 256);
+ /*Right bourder */
+ CallFunctionWithParameter (0xAA, wRightBourder / 256);
+ CallFunctionWithParameter (0xAB, wRightBourder % 256);
+
+ CallFunctionWithParameter (0xD0, 0);
+ CallFunctionWithParameter (0xD1, 0);
+ CallFunctionWithParameter (0xD2, 0);
+ CallFunctionWithParameter (0xD3, 0);
+ CallFunctionWithParameter (0xD4, 0);
+ CallFunctionWithParameter (0xD5, 0);
+
+ CallFunctionWithParameter (0x9D, 5);
+}
+
+static void
+TurnOnPaperPulling (enumColorDepth enColor, SANE_Word wResolution)
+{
+ switch (enColor)
+ {
+ case Drawing:
+ case Halftone:
+ CallFunctionWithParameter (0x91, 0xF7);
+ return;
+ case GrayScale:
+ switch (wResolution)
+ {
+ case 50:
+ case 75:
+ case 100:
+ CallFunctionWithParameter (0x91, 0xB7);
+ return;
+ case 150:
+ case 200:
+ CallFunctionWithParameter (0x91, 0x77);
+ return;
+ case 250:
+ case 300:
+ CallFunctionWithParameter (0x91, 0x37);
+ return;
+ default:
+ return;
+ }
+ case TrueColor:
+ switch (wResolution)
+ {
+ case 75:
+ case 100:
+ CallFunctionWithParameter (0x91, 0xA3);
+ return;
+ case 150:
+ case 200:
+ CallFunctionWithParameter (0x91, 0x53);
+ return;
+ case 250:
+ case 300:
+ CallFunctionWithParameter (0x91, 0x3);
+ return;
+ default:
+ return;
+ }
+ default:
+ return;
+ }
+}
+
+static void
+TurnOffPaperPulling ()
+{
+ CallFunctionWithParameter (0x91, 0);
+}
+
+/*
+ Returns avarage value of scaned row.
+ While paper not loaded this is base "white point".
+*/
+static SANE_Byte
+GetCalibration ()
+{
+ int i;
+ int Result;
+ SANE_Byte Buffer[2600];
+ SANE_Byte bTest;
+
+ CallFunctionWithParameter (0xA1, 2);
+ CallFunctionWithParameter (0xA2, 1);
+ CallFunctionWithParameter (0xA3, 0x98);
+
+ /*Resolution to 300 DPI */
+ CallFunctionWithParameter (0x9A, 1);
+ CallFunctionWithParameter (0x9B, 0x2C);
+
+ CallFunctionWithParameter (0x92, 0);
+ CallFunctionWithParameter (0xC6, 0);
+ CallFunctionWithParameter (0x92, 0x80);
+
+ for (i = 1; i < 256; i++)
+ CallFunctionWithParameter (0xC6, i);
+
+ for (i = 0; i < 256; i++)
+ CallFunctionWithParameter (0xC6, i);
+
+ for (i = 0; i < 256; i++)
+ CallFunctionWithParameter (0xC6, i);
+
+ CallFunctionWithParameter (0xA4, 31); /*Some sort of constant */
+
+ /*Left bourder */
+ CallFunctionWithParameter (0xA5, 0);
+ CallFunctionWithParameter (0xA6, 0x41);
+
+ /*Right bourder */
+ CallFunctionWithParameter (0xAA, 0xA);
+ CallFunctionWithParameter (0xAB, 0x39);
+
+ CallFunctionWithParameter (0xD0, 0);
+ CallFunctionWithParameter (0xD1, 0);
+ CallFunctionWithParameter (0xD2, 0);
+ CallFunctionWithParameter (0xD3, 0);
+ CallFunctionWithParameter (0xD4, 0);
+ CallFunctionWithParameter (0xD5, 0);
+
+ CallFunctionWithParameter (0x9C, 0x1B);
+ CallFunctionWithParameter (0x9D, 5);
+
+ CallFunctionWithParameter (0x92, 0x10);
+ CallFunctionWithParameter (0xC6, 0xFF);
+ CallFunctionWithParameter (0x92, 0x90);
+
+ for (i = 0; i < 2999; i++)
+ CallFunctionWithParameter (0xC6, 0xFF);
+
+ CallFunctionWithParameter (0x92, 0x50);
+ CallFunctionWithParameter (0xC6, 0);
+ CallFunctionWithParameter (0x92, 0xD0);
+
+ for (i = 0; i < 2999; i++)
+ CallFunctionWithParameter (0xC6, 0);
+
+ CallFunctionWithParameter (0x98, 0xFF); /*Up limit */
+ CallFunctionWithParameter (0x95, 0); /*Low limit */
+
+ CallFunctionWithParameter (0x90, 0); /*Gray scale... */
+
+ CallFunctionWithParameter (0x91, 0x3B); /*Turn motor on. */
+
+ for (i = 0; i < 5; i++)
+ {
+ do
+ { /*WARNING!!! Deadlock possible! */
+ bTest = CallFunctionWithRetVal (0xB5);
+ }
+ while ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
+ (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)));
+
+ CallFunctionWithParameter (0xCD, 0);
+ /*Skip this line for ECP: */
+ CallFunctionWithRetVal (0xC8);
+
+ WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8);
+ WriteAddress (0x20);
+ ReadDataBlock (Buffer, 2552);
+ };
+ CallFunctionWithParameter (0x91, 0); /*Turn off motor. */
+ usleep (10);
+ for (Result = 0, i = 0; i < 2552; i++)
+ Result += Buffer[i];
+ return Result / 2552;
+}
+
+static int
+PaperFeed (SANE_Word wLinesToFeed)
+{
+ int i;
+
+ CallFunctionWithParameter (0xA7, 0xF);
+ CallFunctionWithParameter (0xA8, 0xFF);
+ CallFunctionWithParameter (0xC2, 0);
+
+ for (i = 0; i < 9000; i++)
+ {
+ if (CallFunctionWithRetVal (0xB2) & 0x80)
+ break;
+ usleep (1);
+ }
+ if (i >= 9000)
+ return 0; /*Fail. */
+
+ for (i = 0; i < 9000; i += 5)
+ {
+ if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0)
+ break;
+ else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
+ {
+ i = 9000;
+ break;
+ }
+ usleep (5);
+ }
+
+ CallFunctionWithParameter (0xC5, 0);
+
+ if (i >= 9000)
+ return 0; /*Fail. */
+
+ /*Potential deadlock */
+ while (CallFunctionWithRetVal (0xB2) & 0x80); /*Wait bit dismiss */
+
+ CallFunctionWithParameter (0xA7, wLinesToFeed / 256);
+ CallFunctionWithParameter (0xA8, wLinesToFeed % 256);
+ CallFunctionWithParameter (0xC2, 0);
+
+ for (i = 0; i < 9000; i++)
+ {
+ if (CallFunctionWithRetVal (0xB2) & 0x80)
+ break;
+ usleep (1);
+ }
+ if (i >= 9000)
+ return 0; /*Fail. */
+
+ for (i = 0; i < 9000; i++)
+ {
+ if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
+ break;
+ usleep (1);
+ }
+ if (i >= 9000)
+ return 0; /*Fail. */
+
+ return 1;
+}
+
+/*For now we do no calibrate elements - just set maximum limits. FIX ME?*/
+static void
+CalibrateScanElements ()
+{
+ /*Those arrays will be used in future for correct calibration. */
+ /*Then we need to transfer UP brightness border, we use these registers */
+ SANE_Byte arUpTransferBorders[] = { 0x10, 0x20, 0x30 };
+ /*Then we need to transfer LOW brightness border, we use these registers */
+ SANE_Byte arLowTransferBorders[] = { 0x50, 0x60, 0x70 };
+ /*Then we need to save UP brightness border, we use these registers */
+ SANE_Byte arUpSaveBorders[] = { 0x98, 0x97, 0x99 };
+ /*Then we need to save LOW brightness border, we use these registers */
+ SANE_Byte arLowSaveBorders[] = { 0x95, 0x94, 0x96 };
+ /*Speeds, used for calibration */
+ SANE_Byte arSpeeds[] = { 0x3B, 0x37, 0x3F };
+ int j, Average, Temp, Index, /* Line, */ timeout,Calibration;
+ SANE_Byte bTest /*, Min, Max, Result */ ;
+ /*For current color component: (values from arrays). Next two lines - starting and terminating. */
+
+ SANE_Byte CurrentUpTransferBorder;
+ SANE_Byte CurrentLowTransferBorder;
+ SANE_Byte CurrentUpSaveBorder;
+ SANE_Byte CurrentLowSaveBorder;
+ SANE_Byte CurrentSpeed1, CurrentSpeed2;
+ SANE_Byte CorrectionValue;
+ SANE_Byte FilteredBuffer[2570];
+
+ CallFunctionWithParameter (0xA1, 2);
+ CallFunctionWithParameter (0xA2, 0);
+ CallFunctionWithParameter (0xA3, 0x98);
+
+ /*DPI = 300 */
+ CallFunctionWithParameter (0x9A, 1); /*High byte */
+ CallFunctionWithParameter (0x9B, 0x2C); /*Low byte */
+
+ /*Paletter settings. */
+ CallFunctionWithParameter (0x92, 0);
+ CallFunctionWithParameter (0xC6, 0);
+ CallFunctionWithParameter (0x92, 0x80);
+
+ /*First color component */
+ for (j = 1; j < 256; j++)
+ CallFunctionWithParameter (0xC6, j);
+
+ /*Second color component */
+ for (j = 0; j < 256; j++)
+ CallFunctionWithParameter (0xC6, j);
+
+ /*Third color component */
+ for (j = 0; j < 256; j++)
+ CallFunctionWithParameter (0xC6, j);
+
+ CallFunctionWithParameter (0xA4, 31);
+
+ /*Left border */
+ CallFunctionWithParameter (0xA5, 0); /*High byte */
+ CallFunctionWithParameter (0xA6, 0x41); /*Low byte */
+
+ /*Right border */
+ CallFunctionWithParameter (0xAA, 0xA); /*High byte */
+ CallFunctionWithParameter (0xAB, 0x4B); /*Low byte */
+
+ /*Zero these registers... */
+ CallFunctionWithParameter (0xD0, 0);
+ CallFunctionWithParameter (0xD1, 0);
+ CallFunctionWithParameter (0xD2, 0);
+ CallFunctionWithParameter (0xD3, 0);
+ CallFunctionWithParameter (0xD4, 0);
+ CallFunctionWithParameter (0xD5, 0);
+
+ CallFunctionWithParameter (0x9C, 0x1B);
+ CallFunctionWithParameter (0x9D, 0x5);
+
+ Average = 0;
+ for (Index = 0; Index < 3; Index++) /*For theree color components */
+ {
+ /*Up border = 0xFF */
+ CallFunctionWithParameter (0x92, arUpTransferBorders[Index]);
+ CallFunctionWithParameter (0xC6, 0xFF);
+ CallFunctionWithParameter (0x92, arUpTransferBorders[Index] | 0x80);
+
+ for (j = 2999; j > 0; j--)
+ CallFunctionWithParameter (0xC6, 0xFF);
+
+ /*Low border = 0x0 */
+ CallFunctionWithParameter (0x92, arLowTransferBorders[Index]);
+ CallFunctionWithParameter (0xC6, 0x0);
+ CallFunctionWithParameter (0x92, arLowTransferBorders[Index] | 0x80);
+
+ for (j = 2999; j > 0; j--)
+ CallFunctionWithParameter (0xC6, 0x0);
+
+ /*Save borders */
+ CallFunctionWithParameter (arUpSaveBorders[Index], 0xFF);
+ CallFunctionWithParameter (arLowSaveBorders[Index], 0x0);
+ CallFunctionWithParameter (0x90, 0); /*Gray Scale or True color sign :) */
+
+ CallFunctionWithParameter (0x91, arSpeeds[Index]);
+
+ /*waiting for scaned line... */
+ timeout = 0;
+ do
+ {
+ bTest = CallFunctionWithRetVal (0xB5);
+ timeout++;
+ usleep (1);
+ }
+ while ((timeout < 1000) &&
+ ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
+ (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))));
+
+ /*Let's read it... */
+ if(timeout < 1000)
+ {
+ CallFunctionWithParameter (0xCD, 0);
+ CallFunctionWithRetVal (0xC8);
+ WriteScannerRegister (0x70, 0xC8);
+ WriteAddress (0x20);
+
+ ReadDataBlock (FilteredBuffer, 2570);
+ }
+
+ CallFunctionWithParameter (0x91, 0); /*Stop engine. */
+
+ /*Note: if first read failed, junk would be calculated, but if previous
+ read was succeded, but last one failed, previous data'ld be used.
+ */
+ for(Temp = 0, j = 0; j < 2570; j++)
+ Temp += FilteredBuffer[j];
+ Temp /= 2570;
+
+ if((Average == 0)||(Average > Temp))
+ Average = Temp;
+ }
+
+ for(Index = 0; Index < 3; Index++) /*Three color components*/
+ {
+ CurrentUpTransferBorder = arUpTransferBorders[Index];
+ CallFunctionWithParameter (0xC6, 0xFF);
+ CallFunctionWithParameter (0x92, CurrentUpTransferBorder|0x80);
+ for(j=2999; j>0; j--)
+ CallFunctionWithParameter (0xC6, 0xFF);
+
+ CurrentLowTransferBorder = arLowTransferBorders[Index];
+ CallFunctionWithParameter (0xC6, 0x0);
+ CallFunctionWithParameter (0x92, CurrentLowTransferBorder|0x80);
+ for(j=2999; j>0; j--)
+ CallFunctionWithParameter (0xC6, 0);
+
+ CurrentUpSaveBorder = arUpSaveBorders[Index];
+ CallFunctionWithParameter (CurrentUpSaveBorder, 0xFF);
+
+ CurrentLowSaveBorder = arLowSaveBorders[Index];
+ CallFunctionWithParameter (CurrentLowSaveBorder, 0x0);
+ CallFunctionWithParameter (0x90,0);
+ Calibration = 0x80;
+ CallFunctionWithParameter (CurrentUpSaveBorder, 0x80);
+
+ CurrentSpeed1 = CurrentSpeed2 = arSpeeds[Index];
+
+ for(CorrectionValue = 0x40; CorrectionValue != 0;CorrectionValue >>= 2)
+ {
+ CallFunctionWithParameter (0x91, CurrentSpeed2);
+ usleep(10);
+
+ /*waiting for scaned line... */
+ for(j = 0; j < 5; j++)
+ {
+ timeout = 0;
+ do
+ {
+ bTest = CallFunctionWithRetVal (0xB5);
+ timeout++;
+ usleep (1);
+ }
+ while ((timeout < 1000) &&
+ ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
+ (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))));
+
+ /*Let's read it... */
+ if(timeout < 1000)
+ {
+ CallFunctionWithParameter (0xCD, 0);
+ CallFunctionWithRetVal (0xC8);
+ WriteScannerRegister (0x70, 0xC8);
+ WriteAddress (0x20);
+
+ ReadDataBlock (FilteredBuffer, 2570);
+ }
+ }/*5 times we read. I don't understand what for, but so does HP's driver.
+ Perhaps, we can optimize it in future.*/
+ WriteScannerRegister (0x91, 0);
+ usleep(10);
+
+ for(Temp = 0,j = 0; j < 16;j++)
+ Temp += FilteredBuffer[509+j]; /*At this offset calcalates HP's driver.*/
+ Temp /= 16;
+
+ if(Average > Temp)
+ {
+ Calibration += CorrectionValue;
+ Calibration = 0xFF < Calibration ? 0xFF : Calibration; /*min*/
+ }
+ else
+ Calibration -= CorrectionValue;
+
+ WriteScannerRegister (CurrentUpSaveBorder, Calibration);
+ }/*By CorrectionValue we tune UpSaveBorder*/
+
+ WriteScannerRegister (0x90, 8);
+ WriteScannerRegister (0x91, CurrentSpeed1);
+ usleep(10);
+ }/*By color components*/
+
+ return;
+}
+
+/*
+ Internal use functions:
+*/
+
+/*Returns 0 in case of fail and 1 in success.*/
+static int
+OutputCheck ()
+{
+ int i;
+
+ WriteScannerRegister (0x7F, 0x1);
+ WriteAddress (0x7E);
+ for (i = 0; i < 256; i++)
+ WriteData ((SANE_Byte) i);
+
+ WriteAddress (0x3F);
+ if (ReadDataByte () & 0x80)
+ return 0;
+
+ return 1;
+}
+
+static int
+InputCheck ()
+{
+ int i;
+ SANE_Byte Buffer[256];
+
+ WriteAddress (0x3E);
+ for (i = 0; i < 256; i++)
+ {
+ Buffer[i] = ReadDataByte ();
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ if (Buffer[i] != i)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+CallCheck ()
+{
+ int i;
+ SANE_Byte Buffer[256];
+
+ CallFunctionWithParameter (0x92, 0x10);
+ CallFunctionWithParameter (0xC6, 0x0);
+ CallFunctionWithParameter (0x92, 0x90);
+ WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6);
+
+ WriteAddress (0x60);
+
+ for (i = 1; i < 256; i++)
+ WriteData ((SANE_Byte) i);
+
+ CallFunctionWithParameter (0x92, 0x10);
+ CallFunctionWithRetVal (0xC6);
+ CallFunctionWithParameter (0x92, 0x90);
+ WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6);
+
+ WriteAddress (ADDRESS_RESULT);
+
+ ReadDataBlock (Buffer, 256);
+
+ for (i = 0; i < 255; i++)
+ {
+ if (Buffer[i + 1] != (SANE_Byte) i)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+LoadingPaletteToScanner ()
+{
+ /*For now we have statical gamma. */
+ SANE_Byte Gamma[256];
+ int i;
+ for (i = 0; i < 256; i++)
+ Gamma[i] = i;
+
+ CallFunctionWithParameter (0x92, 0);
+ CallFunctionWithParameter (0xC6, Gamma[0]);
+ CallFunctionWithParameter (0x92, 0x80);
+ for (i = 1; i < 256; i++)
+ CallFunctionWithParameter (0xC6, Gamma[i]);
+
+ for (i = 0; i < 256; i++)
+ CallFunctionWithParameter (0xC6, Gamma[i]);
+
+ for (i = 0; i < 256; i++)
+ CallFunctionWithParameter (0xC6, Gamma[i]);
+}
+
+/*
+ Low level warappers:
+*/
+static void
+WriteAddress (SANE_Byte Address)
+{
+ ieee1284_data_dir (pl.portv[scanner_d], 0); /*Forward mode */
+ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
+ ieee1284_epp_write_addr (pl.portv[scanner_d], 0, (char *) &Address, 1);
+}
+
+static void
+WriteData (SANE_Byte Data)
+{
+ ieee1284_data_dir (pl.portv[scanner_d], 0); /*Forward mode */
+ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
+ ieee1284_epp_write_data (pl.portv[scanner_d], 0, (char *) &Data, 1);
+}
+
+static void
+WriteScannerRegister (SANE_Byte Address, SANE_Byte Data)
+{
+ WriteAddress (Address);
+ WriteData (Data);
+}
+
+static void
+CallFunctionWithParameter (SANE_Byte Function, SANE_Byte Parameter)
+{
+ WriteScannerRegister (REGISTER_FUNCTION_CODE, Function);
+ WriteScannerRegister (REGISTER_FUNCTION_PARAMETER, Parameter);
+}
+
+static SANE_Byte
+CallFunctionWithRetVal (SANE_Byte Function)
+{
+ WriteScannerRegister (REGISTER_FUNCTION_CODE, Function);
+ WriteAddress (ADDRESS_RESULT);
+ return ReadDataByte ();
+}
+
+static SANE_Byte
+ReadDataByte ()
+{
+ SANE_Byte Result;
+
+ ieee1284_data_dir (pl.portv[scanner_d], 1); /*Reverse mode */
+ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
+ ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) &Result, 1);
+ return Result;
+}
+
+static void
+ReadDataBlock (SANE_Byte * Buffer, int length)
+{
+
+ ieee1284_data_dir (pl.portv[scanner_d], 1); /*Reverse mode */
+ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
+ ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) Buffer, length);
+}
+
+/* Send a daisy-chain-style CPP command packet. */
+int
+cpp_daisy (struct parport *port, int cmd)
+{
+ unsigned char s;
+
+ ieee1284_data_dir (port, 0); /*forward direction */
+ ieee1284_write_control (port, C1284_NINIT);
+ ieee1284_write_data (port, 0xaa);
+ usleep (2);
+ ieee1284_write_data (port, 0x55);
+ usleep (2);
+ ieee1284_write_data (port, 0x00);
+ usleep (2);
+ ieee1284_write_data (port, 0xff);
+ usleep (2);
+ s = ieee1284_read_status (port) ^ S1284_INVERTED; /*Converted for PC-style */
+
+ s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT);
+
+ if (s != (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT))
+ {
+ DBG (1, "%s: cpp_daisy: aa5500ff(%02x)\n", port->name, s);
+ return -1;
+ }
+
+ ieee1284_write_data (port, 0x87);
+ usleep (2);
+ s = ieee1284_read_status (port) ^ S1284_INVERTED; /*Convert to PC-style */
+
+ s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT);
+
+ if (s != (S1284_SELECT | S1284_NFAULT))
+ {
+ DBG (1, "%s: cpp_daisy: aa5500ff87(%02x)\n", port->name, s);
+ return -1;
+ }
+
+ ieee1284_write_data (port, 0x78);
+ usleep (2);
+ ieee1284_write_control (port, C1284_NINIT);
+ ieee1284_write_data (port, cmd);
+ usleep (2);
+ ieee1284_frob_control (port, C1284_NSTROBE, C1284_NSTROBE);
+ usleep (1);
+ ieee1284_frob_control (port, C1284_NSTROBE, 0);
+ usleep (1);
+ s = ieee1284_read_status (port);
+ ieee1284_write_data (port, 0xff);
+ usleep (2);
+
+ return s;
+}
+
+/*Daisy chain deselect operation.*/
+void
+daisy_deselect_all (struct parport *port)
+{
+ cpp_daisy (port, 0x30);
+}
+
+/*Daisy chain select operation*/
+int
+daisy_select (struct parport *port, int daisy, int mode)
+{
+ switch (mode)
+ {
+ /*For these modes we should switch to EPP mode: */
+ case M1284_EPP:
+ case M1284_EPPSL:
+ case M1284_EPPSWE:
+ return cpp_daisy (port, 0x20 + daisy) & S1284_NFAULT;
+ /*For these modes we should switch to ECP mode: */
+ case M1284_ECP:
+ case M1284_ECPRLE:
+ case M1284_ECPSWE:
+ return cpp_daisy (port, 0xd0 + daisy) & S1284_NFAULT;
+ /*Nothing was told for BECP in Daisy chain specification.
+ May be it's wise to use ECP? */
+ case M1284_BECP:
+ /*Others use compat mode */
+ case M1284_NIBBLE:
+ case M1284_BYTE:
+ case M1284_COMPAT:
+ default:
+ return cpp_daisy (port, 0xe0 + daisy) & S1284_NFAULT;
+ }
+}
+
+/*Daisy chain assign address operation.*/
+int
+assign_addr (struct parport *port, int daisy)
+{
+ return cpp_daisy (port, daisy);
+}
+
+static int
+OpenScanner (const char *scanner_path)
+{
+ int handle;
+ int caps;
+
+ /*Scaner name was specified in config file?*/
+ if (strlen(scanner_path) == 0)
+ return -1;
+
+ for (handle = 0; handle < pl.portc; handle++)
+ {
+ if (strcmp (scanner_path, pl.portv[handle]->name) == 0)
+ break;
+ }
+ if (handle == pl.portc) /*No match found */
+ return -1;
+
+ /*Open port */
+ if (ieee1284_open (pl.portv[handle], 0, &caps) != E1284_OK)
+ return -1;
+
+ /*Claim port */
+ if (ieee1284_claim (pl.portv[handle]) != E1284_OK)
+ return -1;
+
+ /*Total chain reset. */
+ daisy_deselect_all (pl.portv[handle]);
+
+ /*Assign addresses. */
+ assign_addr (pl.portv[handle], 0); /*Assume we have device first in chain. */
+
+ /*Select required device. For now - first in chain. */
+ daisy_select (pl.portv[handle], 0, M1284_EPP);
+
+ return handle;
+}
+
+static void
+CloseScanner (int handle)
+{
+ if (handle == -1)
+ return;
+
+ daisy_deselect_all (pl.portv[handle]);
+
+ ieee1284_release (pl.portv[handle]);
+
+ ieee1284_close (pl.portv[handle]);
+}
diff --git a/backend/hpsj5s.conf.in b/backend/hpsj5s.conf.in
new file mode 100644
index 0000000..77fb4e4
--- /dev/null
+++ b/backend/hpsj5s.conf.in
@@ -0,0 +1,2 @@
+#hpsj5s.conf
+#parport0 \ No newline at end of file
diff --git a/backend/hpsj5s.h b/backend/hpsj5s.h
new file mode 100644
index 0000000..0361acd
--- /dev/null
+++ b/backend/hpsj5s.h
@@ -0,0 +1,102 @@
+#ifndef __HPSJ5S_MIDDLE_LEVEL_API_HEADER__
+#define __HPSJ5S_MIDDLE_LEVEL_API_HEADER__
+
+
+#include <ieee1284.h>
+
+/*Scanner hardware registers*/
+#define REGISTER_FUNCTION_CODE 0x70 /*Here goes function code */
+#define REGISTER_FUNCTION_PARAMETER 0x60 /*Here goes function param */
+
+#define ADDRESS_RESULT 0x20 /*Here we get result */
+
+/*Scanner functions (not all - some of them I cann't identify)*/
+#define FUNCTION_SETUP_HARDWARE 0xA0
+
+/*Scanner hardware control flags:*/
+/*Set this flag and non-zero speed to start rotation*/
+#define FLAGS_HW_MOTOR_READY 0x1
+/*Set this flag to turn on lamp*/
+#define FLAGS_HW_LAMP_ON 0x2
+/*Set this flag to turn indicator lamp off*/
+#define FLAGS_HW_INDICATOR_OFF 0x4
+
+
+/*
+ Types:
+*/
+/*Color modes we support: 1-bit Drawing, 2-bit Halftone, 8-bit Gray Scale, 24-bt True Color*/
+typedef enum
+{ Drawing, Halftone, GrayScale, TrueColor }
+enumColorDepth;
+
+/*Middle-level API:*/
+
+static int OpenScanner (const char *scanner_path);
+
+static void CloseScanner (int handle);
+
+static int DetectScanner (void);
+
+static void StandByScanner (void);
+
+static void SwitchHardwareState (SANE_Byte mask, SANE_Byte invert_mask);
+
+static int CheckPaperPresent (void);
+
+static int ReleasePaper (void);
+
+static int PaperFeed (SANE_Word wLinesToFeed);
+
+static void TransferScanParameters (enumColorDepth enColor,
+ SANE_Word wResolution,
+ SANE_Word wCorrectedLength);
+
+static void TurnOnPaperPulling (enumColorDepth enColor,
+ SANE_Word wResolution);
+
+static void TurnOffPaperPulling (void);
+
+static SANE_Byte GetCalibration (void);
+
+static void CalibrateScanElements (void);
+
+/*Internal-use functions:*/
+
+static int OutputCheck (void);
+static int InputCheck (void);
+static int CallCheck (void);
+static void LoadingPaletteToScanner (void);
+
+/*Low level warappers:*/
+
+static void WriteAddress (SANE_Byte Address);
+
+static void WriteData (SANE_Byte Data);
+
+static void WriteScannerRegister (SANE_Byte Address, SANE_Byte Data);
+
+static void CallFunctionWithParameter (SANE_Byte Function,
+ SANE_Byte Parameter);
+
+static SANE_Byte CallFunctionWithRetVal (SANE_Byte Function);
+
+static SANE_Byte ReadDataByte (void);
+
+static void ReadDataBlock (SANE_Byte * Buffer, int lenght);
+
+/*Daisy chaining API: (should be moved to ieee1284 library in future)*/
+
+/*Deselect all devices in chain on this port.*/
+static void daisy_deselect_all (struct parport *port);
+
+/*Select device with number 'daisy' in 'mode'.*/
+static int daisy_select (struct parport *port, int daisy, int mode);
+
+/*Setup address for device in chain on this port*/
+static int assign_addr (struct parport *port, int daisy);
+
+/* Send a daisy-chain-style CPP command packet. */
+static int cpp_daisy (struct parport *port, int cmd);
+
+#endif
diff --git a/backend/hs2p-saneopts.h b/backend/hs2p-saneopts.h
new file mode 100644
index 0000000..7ea50c2
--- /dev/null
+++ b/backend/hs2p-saneopts.h
@@ -0,0 +1,365 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+
+#define SANE_NAME_INQUIRY "inquiry"
+#define SANE_TITLE_INQUIRY "Inquiry Data"
+#define SANE_DESC_INQUIRY "Displays scanner inquiry data"
+
+#define SANE_TITLE_SCAN_MODE_GROUP "Scan Mode"
+#define SANE_TITLE_GEOMETRY_GROUP "Geometry"
+#define SANE_TITLE_FEEDER_GROUP "Feeder"
+
+#define SANE_TITLE_ENHANCEMENT_GROUP "Enhancement"
+#define SANE_TITLE_ICON_GROUP "Icon"
+#define SANE_TITLE_BARCODE_GROUP "Barcode"
+
+#define SANE_TITLE_MISCELLANEOUS_GROUP "Miscellaneous"
+
+#define SANE_NAME_AUTOBORDER "autoborder"
+#define SANE_TITLE_AUTOBORDER "Autoborder"
+#define SANE_DESC_AUTOBORDER "Enable Automatic Border Detection"
+
+#define SANE_NAME_COMPRESSION "compression"
+#define SANE_TITLE_COMPRESSION "Data Compression"
+#define SANE_DESC_COMPRESSION "Sets the compression mode of the scanner"
+
+#define SANE_NAME_ROTATION "rotation"
+#define SANE_TITLE_ROTATION "Page Rotation"
+#define SANE_DESC_ROTATION "Sets the page rotation mode of the scanner"
+
+#define SANE_NAME_DESKEW "deskew"
+#define SANE_TITLE_DESKEW "Page Deskew"
+#define SANE_DESC_DESKEW "Enable Deskew Mode"
+
+#define SANE_NAME_TIMEOUT_ADF "timeout-adf"
+#define SANE_TITLE_TIMEOUT_ADF "ADF Timeout"
+#define SANE_DESC_TIMEOUT_ADF "Sets the timeout in seconds for the ADF"
+
+#define SANE_NAME_TIMEOUT_MANUAL "timeout-manual"
+#define SANE_TITLE_TIMEOUT_MANUAL "Manual Timeout"
+#define SANE_DESC_TIMEOUT_MANUAL "Sets the timeout in seconds for manual feeder"
+
+#define SANE_NAME_BATCH "batch"
+#define SANE_TITLE_BATCH "Batch"
+#define SANE_DESC_BATCH "Enable Batch Mode"
+
+#define SANE_NAME_CHECK_ADF "check-adf"
+#define SANE_TITLE_CHECK_ADF "Check ADF"
+#define SANE_DESC_CHECK_ADF "Check ADF Status prior to starting scan"
+
+#define SANE_NAME_PREFEED "prefeed"
+#define SANE_TITLE_PREFEED "Prefeed"
+#define SANE_DESC_PREFEED "Prefeed"
+
+#define SANE_NAME_DUPLEX "duplex"
+#define SANE_TITLE_DUPLEX "Duplex"
+#define SANE_DESC_DUPLEX "Enable Duplex (Dual-Sided) Scanning"
+
+#define SANE_NAME_ENDORSER "endorser"
+#define SANE_TITLE_ENDORSER "Endorser"
+#define SANE_DESC_ENDORSER "Print up to 19 character string on each sheet"
+
+#define SANE_NAME_ENDORSER_STRING "endorser-string"
+#define SANE_TITLE_ENDORSER_STRING "Endorser String"
+#define SANE_DESC_ENDORSER_STRING "valid characters: [0-9][ :#`'-./][A-Z][a-z]"
+
+#define SANE_NAME_BARCODE_SEARCH_COUNT "barcode-search-count"
+#define SANE_TITLE_BARCODE_SEARCH_COUNT "Barcode Search Count"
+#define SANE_DESC_BARCODE_SEARCH_COUNT "Number of barcodes to search for in the scanned image"
+
+#define SANE_NAME_BARCODE_HMIN "barcode-hmin"
+#define SANE_TITLE_BARCODE_HMIN "Barcode Minimum Height"
+#define SANE_DESC_BARCODE_HMIN "Sets the Barcode Minimun Height (larger values increase recognition speed)"
+
+#define SANE_NAME_BARCODE_SEARCH_MODE "barcode-search-mode"
+#define SANE_TITLE_BARCODE_SEARCH_MODE "Barcode Search Mode"
+#define SANE_DESC_BARCODE_SEARCH_MODE "Chooses the orientation of barcodes to be searched"
+
+#define SANE_NAME_BARCODE_SEARCH_TIMEOUT "barcode-search-timeout"
+#define SANE_TITLE_BARCODE_SEARCH_TIMEOUT "Barcode Search Timeout"
+#define SANE_DESC_BARCODE_SEARCH_TIMEOUT "Sets the timeout for barcode searching"
+
+#define SANE_NAME_BARCODE_SEARCH_BAR "barcode-search-bar"
+#define SANE_TITLE_BARCODE_SEARCH_BAR "Barcode Search Bar"
+#define SANE_DESC_BARCODE_SEARCH_BAR "Specifies the barcode type to search for"
+
+#define SANE_NAME_SECTION "section"
+#define SANE_TITLE_SECTION "Image/Barcode Search Sections"
+#define SANE_DESC_SECTION "Specifies an image section and/or a barcode search region"
+
+#define SANE_NAME_BARCODE_RELMAX "barcode-relmax"
+#define SANE_TITLE_BARCODE_RELMAX "Barcode RelMax"
+#define SANE_DESC_BARCODE_RELMAX "Specifies the maximum relation from the widest to the smallest bar"
+
+#define SANE_NAME_BARCODE_BARMIN "barcode-barmin"
+#define SANE_TITLE_BARCODE_BARMIN "Barcode Bar Minimum"
+#define SANE_DESC_BARCODE_BARMIN "Specifies the minimum number of bars in Bar/Patch code"
+
+#define SANE_NAME_BARCODE_BARMAX "barcode-barmax"
+#define SANE_TITLE_BARCODE_BARMAX "Barcode Bar Maximum"
+#define SANE_DESC_BARCODE_BARMAX "Specifies the maximum number of bars in a Bar/Patch code"
+
+#define SANE_NAME_BARCODE_CONTRAST "barcode-contrast"
+#define SANE_TITLE_BARCODE_CONTRAST "Barcode Contrast"
+#define SANE_DESC_BARCODE_CONTRAST "Specifies the image contrast used in decoding. Use higher values when " \
+"there are more white pixels in the code"
+
+#define SANE_NAME_BARCODE_PATCHMODE "barcode-patchmode"
+#define SANE_TITLE_BARCODE_PATCHMODE "Barcode Patch Mode"
+#define SANE_DESC_BARCODE_PATCHMODE "Controls Patch Code detection."
+
+#define SANE_NAME_SCAN_WAIT_MODE "scan-wait-mode"
+#define SANE_TITLE_SCAN_WAIT_MODE "Scan Wait Mode "
+#define SANE_DESC_SCAN_WAIT_MODE "Enables the scanner's start button"
+
+#define SANE_NAME_ACE_FUNCTION "ace-function"
+#define SANE_TITLE_ACE_FUNCTION "ACE Function"
+#define SANE_DESC_ACE_FUNCTION "ACE Function"
+
+#define SANE_NAME_ACE_SENSITIVITY "ace-sensitivity"
+#define SANE_TITLE_ACE_SENSITIVITY "ACE Sensitivity"
+#define SANE_DESC_ACE_SENSITIVITY "ACE Sensitivity"
+
+#define SANE_NAME_ICON_WIDTH "icon-width"
+#define SANE_TITLE_ICON_WIDTH "Icon Width"
+#define SANE_DESC_ICON_WIDTH "Width of icon (thumbnail) image in pixels"
+
+#define SANE_NAME_ICON_LENGTH "icon-length"
+#define SANE_TITLE_ICON_LENGTH "Icon Length"
+#define SANE_DESC_ICON_LENGTH "Length of icon (thumbnail) image in pixels"
+
+#define SANE_NAME_ORIENTATION "orientation"
+#define SANE_TITLE_ORIENTATION "Paper Orientation"
+#define SANE_DESC_ORIENTATION "[Portrait]/Landscape" \
+
+#define SANE_NAME_PAPER_SIZE "paper-size"
+#define SANE_TITLE_PAPER_SIZE "Paper Size"
+#define SANE_DESC_PAPER_SIZE "Specify the scan window geometry by specifying the paper size " \
+"of the documents to be scanned"
+
+#define SANE_NAME_PADDING "padding"
+#define SANE_TITLE_PADDING "Padding"
+#define SANE_DESC_PADDING "Pad if media length is less than requested"
+
+#define SANE_NAME_AUTO_SIZE "auto-size"
+#define SANE_TITLE_AUTO_SIZE "Auto Size"
+#define SANE_DESC_AUTO_SIZE "Automatic Paper Size Determination"
+
+#define SANE_NAME_BINARYFILTER "binary-filter"
+#define SANE_TITLE_BINARYFILTER "Binary Filter"
+#define SANE_DESC_BINARYFILTER "Binary Filter"
+
+#define SANE_NAME_SMOOTHING "smoothing"
+#define SANE_TITLE_SMOOTHING "Smoothing"
+#define SANE_DESC_SMOOTHING "Binary Smoothing Filter"
+
+#define SANE_NAME_NOISEREMOVAL "noise-removal"
+#define SANE_TITLE_NOISEREMOVAL "Noise Removal"
+#define SANE_DESC_NOISEREMOVAL "Binary Noise Removal Filter"
+
+#define SANE_NAME_NOISEMATRIX "noise-removal-matrix"
+#define SANE_TITLE_NOISEMATRIX "Noise Removal Matrix"
+#define SANE_DESC_NOISEMATRIX "Noise Removal Matrix"
+
+#define SANE_NAME_GRAYFILTER "gray-filter"
+#define SANE_TITLE_GRAYFILTER "Gray Filter"
+#define SANE_DESC_GRAYFILTER "Gray Filter"
+
+#define SANE_NAME_HALFTONE_CODE "halftone-type"
+#define SANE_TITLE_HALFTONE_CODE "Halftone Type"
+#define SANE_DESC_HALFTONE_CODE "Dither or Error Diffusion"
+
+/*
+#define SANE_NAME_HALFTONE_PATTERN "pattern"
+#define SANE_TITLE_HALFTONE_PATTERN "Pattern"
+#define SANE_DESC_HALFTONE_PATTERN "10 built-in halftone patterns + 2 user patterns"
+*/
+
+#define SANE_NAME_ERRORDIFFUSION "error-diffusion"
+#define SANE_TITLE_ERRORDIFFUSION "Error Diffusion"
+#define SANE_DESC_ERRORDIFFUSION "Useful for documents with both text and images"
+
+/*
+#define SANE_NAME_HALFTONE "halftone"
+#define SANE_TITLE_HALFTONE "Halftone"
+#define SANE_DESC_HALFTONE "Choose a dither pattern or error diffusion"
+
+#define SANE_NAME_NEGATIVE "negative image"
+#define SANE_TITLE_NEGATIVE "Negative Image"
+#define SANE_DESC_NEGATIVE "Reverse Image Format"
+
+#define SANE_NAME_BRIGHTNESS "brightness"
+#define SANE_TITLE_BRIGHTNESS "Brightness"
+#define SANE_DESC_BRIGHTNESS "Brightness"
+
+#define SANE_NAME_THRESHOLD "threshold"
+#define SANE_TITLE_THRESHOLD "Threshold"
+#define SANE_DESC_THRESHOLD "Threshold"
+*/
+
+#define SANE_NAME_GAMMA "gamma"
+#define SANE_TITLE_GAMMA "Gamma"
+#define SANE_DESC_GAMMA "Gamma Correction"
+
+#define SANE_NAME_AUTOSEP "auto-separation"
+#define SANE_TITLE_AUTOSEP "Automatic Separation"
+#define SANE_DESC_AUTOSEP "Automatic Separation"
+
+#define SANE_NAME_AUTOBIN "auto-binarization"
+#define SANE_TITLE_AUTOBIN "Automatic Binarization"
+#define SANE_DESC_AUTOBIN "Automatic Binarization"
+
+#define SANE_NAME_WHITE_BALANCE "white-balance"
+#define SANE_TITLE_WHITE_BALANCE "White Balance"
+#define SANE_DESC_WHITE_BALANCE "White Balance"
+
+#define SANE_NAME_PADDING_TYPE "padding-type"
+#define SANE_TITLE_PADDING_TYPE "Padding Type"
+#define SANE_DESC_PADDING_TYPE "Padding Type"
+
+#define SANE_NAME_BITORDER "bit-order"
+#define SANE_TITLE_BITORDER "Bit Order"
+#define SANE_DESC_BITORDER "Bit Order"
+
+#define SANE_NAME_SELF_DIAGNOSTICS "self-diagnostics"
+#define SANE_TITLE_SELF_DIAGNOSTICS "Self Diagnostics"
+#define SANE_DESC_SELF_DIAGNOSTICS "Self Diagnostics"
+
+#define SANE_NAME_OPTICAL_ADJUSTMENT "optical-adjustment"
+#define SANE_TITLE_OPTICAL_ADJUSTMENT "Optical Adjustment"
+#define SANE_DESC_OPTICAL_ADJUSTMENT "Optical Adjustment"
+
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_INQUIRY, /* inquiry string */
+ OPT_PREVIEW,
+ OPT_SCAN_MODE, /* scan mode */
+ OPT_RESOLUTION,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+ OPT_COMPRESSION, /* hardware compression */
+
+ OPT_GEOMETRY_GROUP,
+ /*OPT_AUTOBORDER, automatic border detection */
+ /*OPT_ROTATION, hardware rotation */
+ /*OPT_DESKEW, hardware deskew */
+ OPT_PAGE_ORIENTATION, /* portrait, landscape */
+ OPT_PAPER_SIZE, /* paper size */
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+ OPT_PADDING, /* Pad to requested length */
+ OPT_AUTO_SIZE, /* Automatic Size Recognition */
+
+ OPT_FEEDER_GROUP,
+ OPT_SCAN_SOURCE, /* scan source (eg. Flatbed, ADF) */
+ OPT_DUPLEX, /* scan both sides of the page */
+ OPT_SCAN_WAIT_MODE, /* Enables the scanner's Start Button */
+ OPT_PREFEED,
+ OPT_ENDORSER, /* Endorser (off,on) */
+ OPT_ENDORSER_STRING, /* Endorser String */
+ /*OPT_BATCH, scan in batch mode */
+ /*OPT_TIMEOUT_MANUAL, timeout in seconds with manual feed */
+ /*OPT_TIMEOUT_ADF, timeout in seconds with ADF */
+ /*OPT_CHECK_ADF, check for page in ADF before scanning */
+
+ OPT_ENHANCEMENT_GROUP,
+ /* OPT_ACE_FUNCTION,
+ OPT_ACE_SENSITIVITY, */
+ OPT_BRIGHTNESS, /* Brightness */
+ OPT_THRESHOLD, /* Threshold */
+ OPT_CONTRAST, /* Contrast */
+ OPT_NEGATIVE, /* Negative (reverse image) */
+ OPT_GAMMA, /* Gamma Correction */
+ OPT_CUSTOM_GAMMA,
+ OPT_GAMMA_VECTOR_GRAY,
+ OPT_HALFTONE_CODE, /* Halftone Code */
+ OPT_HALFTONE_PATTERN, /* Halftone Pattern */
+ OPT_GRAYFILTER, /* MRIF */
+ OPT_SMOOTHING, /* Smoothing */
+ OPT_NOISEREMOVAL, /* Noise Removal */
+ OPT_AUTOSEP, /* Auto Separation */
+ OPT_AUTOBIN, /* Auto Binarization */
+ OPT_WHITE_BALANCE,
+
+ OPT_MISCELLANEOUS_GROUP,
+ OPT_PADDING_TYPE,
+ /*OPT_BITORDER, */
+ OPT_SELF_DIAGNOSTICS,
+ OPT_OPTICAL_ADJUSTMENT,
+ /*
+ OPT_PARITION_FUNCTION
+ OPT_SECTION
+ */
+
+ OPT_DATA_GROUP,
+ OPT_UPDATE,
+ OPT_NREGX_ADF,
+ OPT_NREGY_ADF,
+ OPT_NREGX_BOOK,
+ OPT_NREGY_BOOK,
+ OPT_NSCANS_ADF,
+ OPT_NSCANS_BOOK,
+ OPT_LAMP_TIME,
+ OPT_EO_ODD,
+ OPT_EO_EVEN,
+ OPT_BLACK_LEVEL_ODD,
+ OPT_BLACK_LEVEL_EVEN,
+ OPT_WHITE_LEVEL_ODD,
+ OPT_WHITE_LEVEL_EVEN,
+ OPT_DENSITY,
+ OPT_FIRST_ADJ_WHITE_ODD,
+ OPT_FIRST_ADJ_WHITE_EVEN,
+ OPT_NREGX_REVERSE,
+ OPT_NREGY_REVERSE,
+ OPT_NSCANS_REVERSE_ADF,
+ OPT_REVERSE_TIME,
+ OPT_NCHARS,
+
+ NUM_OPTIONS /* must come last: */
+} HS2P_Option;
diff --git a/backend/hs2p-scsi.c b/backend/hs2p-scsi.c
new file mode 100644
index 0000000..82640e9
--- /dev/null
+++ b/backend/hs2p-scsi.c
@@ -0,0 +1,2198 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include <time.h>
+#include "hs2p.h"
+
+
+static SANE_String_Const
+print_devtype (SANE_Byte devtype)
+{
+ int i = devtype;
+ static SANE_String devtypes[] = {
+ "disk",
+ "tape",
+ "printer",
+ "processor",
+ "CD-writer",
+ "CD-drive",
+ "scanner",
+ "optical-drive",
+ "jukebox",
+ "communicator"
+ };
+
+ return (i >= 0 && i < NELEMS (devtypes)) ? devtypes[i] : "unknown-device";
+}
+static void
+print_bytes (const void *buf, size_t bufsize)
+{
+ const SANE_Byte *bp;
+ unsigned i;
+
+ for (i = 0, bp = buf; i < bufsize; i++, bp++)
+ DBG (DBG_error, "%3d: 0x%02x %d\n", i, *bp, *bp);
+}
+
+static void
+ScannerDump (HS2P_Scanner * s)
+{
+ int i;
+ HS2P_Info *info;
+ SANE_Device *sdev;
+
+ info = &s->hw->info;
+ sdev = &s->hw->sane;
+
+ DBG (DBG_info, "\n\n");
+ DBG (DBG_info, ">> ScannerDump:\n");
+ DBG (DBG_info, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n",
+ sdev->name, sdev->vendor, sdev->model, sdev->type);
+
+ DBG (DBG_info, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n",
+ print_devtype (info->devtype), info->vendor, info->product,
+ info->revision);
+
+ DBG (DBG_info, "Automatic Document Feeder: %s%s%s%s\n",
+ info->hasADF ? "Installed " : "Not Installed ",
+ info->hasSimplex ? "simplex" : "",
+ info->hasDuplex ? "duplex" : "",
+ info->hasARDF ? "reverse double-sided" : "");
+ DBG (DBG_info, "Endorser :%s\n",
+ info->hasEndorser ? " <Installed>" : " <Not Installed>");
+ DBG (DBG_info, "Image Processing Unit:%s\n",
+ info->hasIPU ? " <Installed>" : " <Not Installed>");
+ DBG (DBG_info, "Extended Board :%s\n",
+ info->hasXBD ? " <Installed>" : " <Not Installed>");
+
+ DBG (DBG_info, "\n");
+ DBG (DBG_info, "Image Composition Support\n");
+ DBG (DBG_info, "Line Art (B/W) Support : %s\n",
+ info->supports_lineart ? "Yes" : "No");
+ DBG (DBG_info, "Dithering (Halftone) Support: %s\n",
+ info->supports_dithering ? "Yes" : "No");
+ DBG (DBG_info, "Error Diffusion Support : %s\n",
+ info->supports_errordiffusion ? "Yes" : "No");
+ DBG (DBG_info, "Color Support : %s\n",
+ info->supports_color ? "Yes" : "No");
+ DBG (DBG_info, "4 Bit Gray Support : %s\n",
+ info->supports_4bitgray ? "Yes" : "No");
+ DBG (DBG_info, "5-8 Bit Gray Support : %s\n",
+ info->supports_8bitgray ? "Yes" : "No");
+
+ DBG (DBG_info, "Image Data processing:%s%s%s%s%s%s\n",
+ info->supports_whiteframing ? " <White Frame>" : "",
+ info->supports_blackframing ? " <Black Frame>" : "",
+ info->supports_edgeextraction ? " <Edge Extraction>" : "",
+ info->supports_noiseremoval ? " <Noise Filter>" : "",
+ info->supports_smoothing ? " <Smooth>" : "",
+ info->supports_linebolding ? " <Line Bolding>" : "");
+
+ DBG (DBG_info, "Image Compression:%s%s%s%s\n",
+ info->supports_MH ? " <MH support>" : "",
+ info->supports_MR ? " <MR support>" : "",
+ info->supports_MMR ? " <MMR support>" : "",
+ info->supports_MHB ? " <MH byte boundary support>" : "");
+ DBG (DBG_info, "Marker Recognition: %s\n",
+ info->supports_markerrecognition ? "<supported>" : "<not supported>");
+ DBG (DBG_info, "Size Recognition : %s\n",
+ info->supports_sizerecognition ? "<supported>" : "<not supported>");
+ DBG (DBG_info, "X Maximum Output Pixels = %d\n", info->xmaxoutputpixels);
+
+ /*
+ DBG (DBG_info, "Optional Features:%s%s%s%s\n",
+ info->canBorderRecog ? " <Border Recognition>" : "",
+ info->canBarCode ? " <BarCode Decoding>" : "",
+ info->canIcon ? " <Icon Generation>" : "",
+ info->canSection ? " <Section Support>" : "");
+ */
+
+ DBG (DBG_info, "Max bytes per scan-line: %d (%d pixels)\n",
+ info->xmaxoutputpixels / 8, info->xmaxoutputpixels);
+
+ DBG (DBG_info, "Basic resolution (X/Y) : %d/%d\n", info->resBasicX,
+ info->resBasicY);
+ DBG (DBG_info, "Maximum resolution (X/Y) : %d/%d\n", info->resMaxX,
+ info->resMaxY);
+ DBG (DBG_info, "Minimum resolution (X/Y) : %d/%d\n", info->resMinX,
+ info->resMinY);
+ DBG (DBG_info, "Standard Resolutions:\n");
+ for (i = 1; i <= info->resStdList[0]; i++)
+ DBG (DBG_info, " %d\n", info->resStdList[i]);
+
+ DBG (DBG_info,
+ "Window Width/Height (in basic res) %d/%d (%.2f/%.2f inches)\n",
+ info->winWidth, info->winHeight,
+ (info->resBasicX !=
+ 0) ? ((float) info->winWidth) / info->resBasicX : 0.0,
+ (info->resBasicY) ? ((float) info->winHeight) / info->resBasicY : 0.0);
+
+ /*
+ DBG (DBG_info, "Summary:%s%s%s\n",
+ info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
+ info->canACE ? " (ACE capable)" : "",
+ info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
+ */
+
+ DBG (DBG_info, "Buffer Full Ratio = %#02x\n",
+ info->cxn.buffer_full_ratio);
+ DBG (DBG_info, "Buffer Empty Ratio = %#02x\n",
+ info->cxn.buffer_empty_ratio);
+ DBG (DBG_info, "Bus Inactive Limit = %#02x\n",
+ info->cxn.bus_inactive_limit[0] << 8 | info->cxn.
+ bus_inactive_limit[1]);
+ DBG (DBG_info, "Disconnect Time Limit = %#04x\n",
+ info->cxn.disconnect_time_limit[0] << 8 | info->cxn.
+ disconnect_time_limit[1]);
+ DBG (DBG_info, "Connect Time Limit = %#02x\n",
+ info->cxn.connect_time_limit[0] << 8 | info->cxn.
+ connect_time_limit[1]);
+ DBG (DBG_info, "Maximum Burst Size = %#04x\n",
+ info->cxn.maximum_burst_size[0] << 8 | info->cxn.
+ maximum_burst_size[1]);
+ DBG (DBG_info, "DTDC = %#02x\n", info->cxn.dtdc & 0x03);
+
+ DBG (DBG_info, "White Balance is %s\n",
+ info->white_balance == 1 ? "Absolute" : "Relative");
+ DBG (DBG_info, "Medium Wait Timer is <not supported>\n"); /* get_medium_wait_timer(fd) */
+ DBG (DBG_info, "Scan Wait Mode is %s\n",
+ info->scan_wait_mode == 0 ? "OFF" : "ON");
+ DBG (DBG_info, "Service Mode is in Select %s Mode\n",
+ info->service_mode == 0 ? "Self-Diagnostics" : "Optical Adjustment");
+
+ sprintf (info->inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s\n",
+ info->vendor, info->product, info->revision,
+ info->hasADF && info->hasDuplex ? "Duplex Scanner" : "",
+ info->hasADF && info->hasSimplex ? "Simplex Scanner" : "");
+
+ DBG (DBG_info, "duplex_default=%d\n", info->default_duplex);
+ /*
+ DBG (DBG_info, "autoborder_default=%d\n", info->autoborder_default);
+ DBG (DBG_info, "batch_default=%d\n", info->batch_default);
+ DBG (DBG_info, "deskew_default=%d\n", info->deskew_default);
+ DBG (DBG_info, "check_adf_default=%d\n", info->check_adf_default);
+ DBG (DBG_info, "timeout_adf_default=%d\n", info->timeout_adf_default);
+ DBG (DBG_info, "timeout_manual_default=%d\n", info->timeout_manual_default);
+ DBG (DBG_info, "control_panel_default=%d\n", info->control_panel_default);
+ */
+
+ DBG (DBG_info, "bmu = %d\n", info->bmu);
+ DBG (DBG_info, "mud = %d\n", info->mud);
+ DBG (DBG_info, "white balance = %#0x\n", info->white_balance);
+ DBG (DBG_info, "adf control = %#0x\n", info->adf_control);
+ DBG (DBG_info, "adf mode control = %#0x\n", info->adf_mode_control);
+ DBG (DBG_info, "endorser control = %#0x\n", info->endorser_control);
+ DBG (DBG_info, "endorser string = %s\n", info->endorser_string);
+ DBG (DBG_info, "scan wait mode = %#0x\n", info->scan_wait_mode);
+ DBG (DBG_info, "service mode = %#0x\n", info->service_mode);
+
+ DBG (DBG_info, "BasicXRes = %d\n", info->resBasicX);
+ DBG (DBG_info, "BasicYRes = %d\n", info->resBasicY);
+
+ DBG (DBG_info, "XResStep = %d\n", info->resXstep);
+ DBG (DBG_info, "YResStep = %d\n", info->resYstep);
+
+ DBG (DBG_info, "MaxXres = %d\n", info->resMaxX);
+ DBG (DBG_info, "MaxYres = %d\n", info->resMaxY);
+
+ DBG (DBG_info, "MinXres = %d\n", info->resMinX);
+ DBG (DBG_info, "MinYres = %d\n", info->resMinY);
+
+ DBG (DBG_info, "Width = %d\n", info->winWidth);
+ DBG (DBG_info, "Height = %d\n", info->winHeight);
+
+ DBG (DBG_info, "<< ScannerDump\n");
+}
+static void
+print_vpd_info (struct inquiry_vpd_data *vbuf)
+{
+ DBG (DBG_info, "VPD IDENTIFIER C0H\n");
+ DBG (DBG_info, "[00] Peripheral %#02x\n", vbuf->devtype);
+ DBG (DBG_info, "[01] Page Code %#02x\n", vbuf->pagecode);
+ DBG (DBG_info, "[02] reserved %#02x\n", vbuf->byte2);
+ DBG (DBG_info, "[03] Page Length %#02x\n", vbuf->pagelength);
+ DBG (DBG_info, "[04] ADF ID %#02x\n", vbuf->adf_id);
+ DBG (DBG_info, "[05] Endorser ID %#02x\n", vbuf->end_id);
+ DBG (DBG_info, "[06] Image Processing Unit %#02x\n", vbuf->ipu_id);
+ DBG (DBG_info, "[07] Image Composition %#02x\n",
+ vbuf->imagecomposition);
+ DBG (DBG_info, "[08] Image Data Processing %lu\n",
+ _2btol (&vbuf->imagedataprocessing[0]));
+ DBG (DBG_info, "[10] Compression %#02x\n", vbuf->compression);
+ DBG (DBG_info, "[11] Marker Recognition %#02x\n",
+ vbuf->markerrecognition);
+ DBG (DBG_info, "[12] Size Recognition %#02x\n",
+ vbuf->sizerecognition);
+ DBG (DBG_info, "[13] reserved %#02x\n", vbuf->byte13);
+ DBG (DBG_info, "[14] X Maximum Output Pixel %lu\n",
+ _2btol (&vbuf->xmaxoutputpixels[0]));
+}
+static void
+print_jis_info (struct inquiry_jis_data *jbuf)
+{
+ DBG (DBG_info, "JIS IDENTIFIER F0H\n");
+ DBG (DBG_info, "[00] devtype %#02x\n", jbuf->devtype);
+ DBG (DBG_info, "[01] Page Code %#02x\n", jbuf->pagecode);
+ DBG (DBG_info, "[02] JIS Ver %#02x\n", jbuf->jisversion);
+ DBG (DBG_info, "[03] reserved1 %#02x\n", jbuf->reserved1);
+ DBG (DBG_info, "[04] Page Len %#02x\n", jbuf->alloclen);
+ DBG (DBG_info, "[05] BasicXRes %lu\n", _2btol (&jbuf->BasicRes.x[0]));
+ DBG (DBG_info, "[07] BasicYRes %lu\n", _2btol (&jbuf->BasicRes.y[0]));
+ DBG (DBG_info, "[09] Resolution step %#02x\n", jbuf->resolutionstep);
+ DBG (DBG_info, "[10] MaxXRes %lu\n", _2btol (&jbuf->MaxRes.x[0]));
+ DBG (DBG_info, "[12] MaxYRes %lu\n", _2btol (&jbuf->MaxRes.y[0]));
+ DBG (DBG_info, "[14] MinXRes %lu\n", _2btol (&jbuf->MinRes.x[0]));
+ DBG (DBG_info, "[16] MinYRes %lu\n", _2btol (&jbuf->MinRes.y[0]));
+ DBG (DBG_info, "[18] Std Res %#0x\n",
+ (jbuf->standardres[0] << 8) | jbuf->standardres[1]);
+ DBG (DBG_info, "[20] Win Width %lu\n", _4btol (&jbuf->Window.width[0])); /* Manual says 4787/12B3H pixels @400dpi = 12in */
+ DBG (DBG_info, "[24] Win Len %lu\n", _4btol (&jbuf->Window.length[0])); /* Manual says 6803/1A93H pixels @400dpi = 17in) */
+ DBG (DBG_info, "[28] function %#02x\n", jbuf->functions);
+ DBG (DBG_info, "[29] reserved %#02x\n", jbuf->reserved2);
+}
+
+/* 1-3-1 TEST UNIT READY
+ Byte0: | 0x00 |
+ Byte1: | 7-5 Logical Unit Number | Reserved |
+ Byte2: | Reserved |
+ Byte3: | Reserved |
+ Byte4: | Reserved |
+ Byte5: | 7-6 Vendor Unique | 5-2 Reserved | 1 Flag | 0 Link |
+*/
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> test_unit_ready\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = HS2P_SCSI_TEST_UNIT_READY;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< test_unit_ready\n");
+ return (status);
+}
+
+/* 1-3-2 REQUEST SENSE
+ Byte0: | 0x00 |
+ Byte1: | 7-5 Logical Unit Number | Reserved |
+ Byte2: | Reserved |
+ Byte3: | Reserved |
+ Byte4: | Allocation Length |
+ Byte5: | 7-6 Vendor Unique | 5-2 Reserved | 1 Flag | 0 Link |
+*/
+
+#if 0
+static SANE_Status
+get_sense_data (int fd, SENSE_DATA * sense_data)
+{
+ SANE_Status status;
+ DBG (DBG_sane_proc, ">> get_sense_data\n");
+
+ static SANE_Byte cmd[6];
+ size_t len;
+
+ len = sizeof (*sense_data);
+ memset (sense_data, 0, len);
+ memset (cmd, 0, sizeof (cmd));
+
+ cmd[0] = HS2P_SCSI_REQUEST_SENSE;
+ cmd[4] = len;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_data, &len);
+
+ DBG (DBG_proc, "<< get_sense_data\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+print_sense_data (int dbg_level, SENSE_DATA * data)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte *bp, *end;
+ SANE_Int i;
+
+ DBG (DBG_sane_proc, ">> print_sense_data\n");
+
+ bp = (SANE_Byte *) data;
+ end = bp + (SANE_Byte) sizeof (SENSE_DATA);
+ for (i = 0; bp < end; bp++, i++)
+ {
+ DBG (dbg_level, "Byte #%2d is %3d, 0x%02x\n", i, *bp, *bp);
+ }
+
+ DBG (dbg_level, "Valid=%1d, ErrorCode=%#x\n",
+ (data->error_code & 0x80) >> 7, data->error_code & 0x7F);
+ DBG (dbg_level, "Segment number = %d\n", data->segment_number);
+ DBG (dbg_level,
+ "F-mark=%1d, EOM=%1d, ILI=%1d, Reserved=%1d, SenseKey=%#x\n",
+ (data->sense_key & 0x80) >> 7, (data->sense_key & 0x40) >> 6,
+ (data->sense_key & 0x20) >> 5, (data->sense_key & 0x10) >> 4,
+ (data->sense_key & 0x0F));
+ DBG (dbg_level, "Information Byte = %lu\n", _4btol (data->information));
+ DBG (dbg_level, "Additional Sense Length = %d\n", data->sense_length);
+ DBG (dbg_level, "Command Specific Infomation = %lu\n",
+ _4btol (data->command_specific_information));
+ DBG (dbg_level, "Additional Sense Code = %#x\n", data->sense_code);
+ DBG (dbg_level, "Additional Sense Code Qualifier = %#x\n",
+ data->sense_code_qualifier);
+
+ DBG (DBG_proc, "<< print_sense_data\n");
+ return (status);
+}
+
+static struct sense_key *
+lookup_sensekey_errmsg (int code)
+{
+ int i;
+ struct sense_key *k = &sensekey_errmsg[0];
+
+ for (i = 0; i < 16; i++, k++)
+ if (k->key == code)
+ return k;
+ return NULL;
+}
+static struct ASCQ *
+lookup_ascq_errmsg (unsigned int code)
+{
+ unsigned int i;
+ struct ASCQ *k = &ascq_errmsg[0];
+
+ for (i = 0; i < 74; i++, k++)
+ if (k->codequalifier == code)
+ return k;
+ return NULL;
+}
+
+/* a sensible sense handler
+ arg is a pointer to the associated HS2P_Scanner structure
+
+ SENSE DATA FORMAT: 14 bytes bits[7-0]
+ Byte 0: [7]:valid [6-0]:Error Code
+ Byte 1: Segment Number
+ Byte 2: [7]: F-mark; [6]:EOM; [5]:ILI; [4]:reserved; [3-0]:Sense Key
+ Byte 3: Information Byte
+ Byte 4: Information Byte
+ Byte 5: Information Byte
+ Byte 6: Information Byte
+ Byte 7: Additional Sense Length (n-7)
+ Byte 8: Command Specific Information
+ Byte 9: Command Specific Information
+ Byte 10: Command Specific Information
+ Byte 11: Command Specific Information
+ Byte 12: Additional Sense Code
+ Byte 13: Additional Sense Code Qualifier
+*/
+static SANE_Status
+sense_handler (int __sane_unused__ scsi_fd, u_char * sense_buffer, void *sd)
+{
+ u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData;
+ u_long MissingBytes;
+ char *sense_str = "";
+
+ struct sense_key *skey;
+ struct ASCQ *ascq_key;
+ SENSE_DATA *sdp = (SENSE_DATA *) sd;
+ SANE_Int i;
+ SANE_Status status = SANE_STATUS_INVAL;
+ SANE_Char print_sense[(16 * 3) + 1];
+
+ DBG (DBG_proc, ">> sense_handler\n");
+ if (DBG_LEVEL >= DBG_info)
+ print_sense_data (DBG_LEVEL, (SENSE_DATA *) sense_buffer);
+
+ /* store sense_buffer */
+ DBG (DBG_info, ">> copying %lu bytes from sense_buffer[] to sense_data\n",
+ (u_long) sizeof (SENSE_DATA));
+ memcpy (sdp, sense_buffer, sizeof (SENSE_DATA));
+ if (DBG_LEVEL >= DBG_info)
+ print_sense_data (DBG_LEVEL, sdp);
+
+ ErrorCode = sense_buffer[0] & 0x7F;
+ ValidData = (sense_buffer[0] & 0x80) != 0;
+ sense = sense_buffer[2] & 0x0f; /* Sense Key */
+ asc = sense_buffer[12]; /* Additional Sense Code */
+ ascq = sense_buffer[13]; /* Additional Sense Code Qualifier */
+ EOM = (sense_buffer[2] & 0x40) != 0; /* End Of Media */
+ ILI = (sense_buffer[2] & 0x20) != 0; /* Invalid Length Indicator */
+ MissingBytes = ValidData ? _4btol (&sense_buffer[3]) : 0;
+
+ DBG (DBG_sense,
+ "sense_handler: sense_buffer=%#x, sense=%#x, asc=%#x, ascq=%#x\n",
+ sense_buffer[0], sense, asc, ascq);
+ DBG (DBG_sense,
+ "sense_handler: ErrorCode %02x ValidData: %d "
+ "EOM: %d ILI: %d MissingBytes: %lu\n", ErrorCode, ValidData, EOM,
+ ILI, MissingBytes);
+
+ memset (print_sense, '\0', sizeof (print_sense));
+ for (i = 0; i < 16; i++)
+ sprintf (print_sense + strlen (print_sense), "%02x ", sense_buffer[i]);
+ DBG (DBG_sense, "sense_handler: sense=%s\n", print_sense);
+
+ if (ErrorCode != 0x70 && ErrorCode != 0x71)
+ {
+ DBG (DBG_error, "sense_handler: error code is invalid.\n");
+ return SANE_STATUS_IO_ERROR; /* error code is invalid */
+ }
+
+ skey = lookup_sensekey_errmsg (sense); /* simple sequential search */
+ DBG (DBG_sense, "sense_handler: sense_key=%#x '%s - %s'\n", skey->key,
+ skey->meaning, skey->description);
+
+ DBG (DBG_sense, "Looking up ascq=(%#x,%#x)=%#x\n", asc, ascq,
+ (asc << 8) | ascq);
+ ascq_key = lookup_ascq_errmsg ((asc << 8) | ascq); /* simple sequential search */
+ DBG (DBG_sense, "sense_handler: ascq=(%#x,%#x): %#x '%s'\n", asc, ascq,
+ ascq_key->codequalifier, ascq_key->description);
+
+ /* handle each sense key: Translate from HS2P message to SANE_STATUS_ message
+ * SANE_STATUS_GOOD, _ACCESS_DEINIED, _NO_MEM, _INVAL, _IO_ERROR, _DEVICE_BUSY,
+ * _EOF, _UNSUPPORTED, _CANCELLED, _JAMMED, _NO_DOCS, _COVER_OPEN
+ */
+ switch (sense)
+ {
+ case 0x00: /* no sense */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x01: /* recovered error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x02: /* not ready */
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x03: /* medium error */
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x04: /* hardware error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x05: /* illegal request */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x06: /* unit attention */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x07: /* data protect */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x08: /* blank check */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x09: /* vendor specific */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0A: /* copy aborted */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ case 0x0B: /* aborted command */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ case 0x0C: /* equal */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0D: /* volume overflow */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0E: /* miscompare */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0F: /* reserved */
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+ if (ErrorCode == 0x70) /* Additional Sense Codes available */
+ switch ((asc << 8) | ascq)
+ {
+ case 0x0000: /* No additional Information */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x0002: /* End of Medium */
+ status = SANE_STATUS_NO_DOCS;
+ break;
+ case 0x0005: /* End of Data */
+ status = SANE_STATUS_EOF;
+ break;
+ case 0x0400: /* LUN not ready */
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x0401: /* LUN becoming ready */
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x0403: /* LUN not ready. Manual intervention needed */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0500: /* LUN doesn't respond to selection */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0700: /* Multiple peripheral devices selected */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x1100: /* Unrecovered read error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x1101: /* Read retries exhausted */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x1501: /* Mechanical positioning error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x1A00: /* Parameter list length error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2000: /* Invalid command operation code */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2400: /* Invalid field in CDB (check field pointer) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2500: /* LUN not supported */
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x2600: /* Invalid field in parameter list (check field pointer) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2900: /* Power on, reset, or BUS DEVICE RESET occurred */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x2A01: /* (MODE parameter changed) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2C00: /* Command sequence error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2C01: /* Too many windows specified */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2C02: /* Invalid combination of windows specified */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x3700: /* (Rounded paramter) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x3900: /* (Saving parameters not supported) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x3A00: /* Medium not present */
+ status = SANE_STATUS_NO_DOCS;
+ break;
+ case 0x3B09: /* Read past end of medium */
+ status = SANE_STATUS_EOF;
+ break;
+ case 0x3B0B: /* Position past end of medium */
+ status = SANE_STATUS_EOF;
+ break;
+ case 0x3D00: /* Invalid bits in IDENTIFY message */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x4300: /* Message error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x4500: /* Select/Reselect failure */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4700: /* (SCSI parity error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4800: /* Initiator detected error message received */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4900: /* Invalid message error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x4B00: /* Data phase error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x5300: /* (Media Load/Eject failed) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6000: /* Lamp failure */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6001: /* Shading error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6002: /* White adjustment error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6010: /* Reverse Side Lamp Failure */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6200: /* Scan head positioning error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6300: /* Document Waiting Cancel */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ case 0x8000: /* (PSU over heate) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8001: /* (PSU 24V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8002: /* (ADF 24V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8003: /* (5V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8004: /* (-12V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8100: /* (ADF 24V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8102: /* (Base 12V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8103: /* Lamp cover open (Lamp 24V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8104: /* (-12V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8105: /* (Endorser 6V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8106: /* SCU 3.3V power down error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8107: /* RCU 3.3V power down error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8108: /* OIPU 3.3V power down error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8200: /* Memory Error (Bus error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8210: /* Reverse-side memory error (Bus error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8300: /* (Image data processing LSI error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8301: /* (Interface LSI error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8302: /* (SCSI controller error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8303: /* (Compression unit error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8304: /* (Marker detect unit error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8400: /* Endorser error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8500: /* (Origin Positioning error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8600: /* Mechanical Time Out error (Pick Up Roller error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8700: /* (Heater error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8800: /* (Thermistor error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8900: /* ADF cover open */
+ status = SANE_STATUS_COVER_OPEN;
+ break;
+ case 0x8901: /* (ADF lift up) */
+ status = SANE_STATUS_COVER_OPEN;
+ break;
+ case 0x8902: /* Document jam error for ADF */
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x8903: /* Document misfeed for ADF */
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x8A00: /* (Interlock open) */
+ status = SANE_STATUS_COVER_OPEN;
+ break;
+ case 0x8B00: /* (Not enough memory) */
+ status = SANE_STATUS_NO_MEM;
+ break;
+ case 0x8C00: /* Size Detection failed */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default: /* Should never get here */
+ status = SANE_STATUS_INVAL;
+ DBG (DBG_sense,
+ "sense_handler: 'Undocumented code': ascq=(%#x,%#x)\n",
+ asc & 0xFF00, ascq & 0x00FF);
+ break;
+ }
+
+
+ DBG (DBG_proc, "sense_handler %s: '%s'-'%s' '%s' return:%d\n", sense_str,
+ skey->meaning, skey->description, ascq_key->description, status);
+ return status;
+}
+
+/* VPD IDENTIFIER Page Code 0x00
+ * A list of all Page Codes supported by scanner is returned as data
+ * Byte0 => bit7-5: Peripheral Qualifier, bits4-0: Peripheral Device Type
+ * Byte1 => Page Code of CDB is set as Page Code 0
+ * Byte2 => Reserved
+ * Byte3 => Page Length is 2 because scanner supports just two page codes: C0H and F0H
+ * Byte4 => First Support Page Code
+ * Byte5 => Second Support Page Code
+*/
+#if 0
+static SANE_Status
+vpd_indentifier_00H (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> vpd_identifier_00H\n");
+
+ cmd[0] = HS2P_SCSI_REQUEST_SENSE;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< vpd_identifier_00H\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+vpd_identifier_C0H (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> vpd_identifier_C0H\n");
+
+ cmd[0] = HS2P_SCSI_REQUEST_SENSE;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< vpd_identifier_C0H\n");
+ return (status);
+}
+#endif
+
+/* 1-3-3 INQUIRY : 6 bytes:
+ * Byte0 => 0x12
+ * Byte1 => bits7-5: Logical Unit number
+ * bits4-1: Reserved
+ * bit0: EVPD
+ * Byte2 => Page Code
+ * Byte3 => Reserved
+ * Byte4 => Allocation Length
+ * Byte5 => bits7-6: Vendor Unique
+ * bits5-2: Reserved
+ * bit1: Flag
+ * bit0: Link
+*/
+static SANE_Status
+inquiry (int fd, void *buf, size_t * buf_size, SANE_Byte evpd,
+ SANE_Byte page_code)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> inquiry\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = HS2P_SCSI_INQUIRY;
+ cmd[1] = evpd;
+ cmd[2] = page_code;
+/*cmd[3] Reserved */
+ cmd[4] = *buf_size;
+/*cmd[5] vendorunique+reserved+flag+link */
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< inquiry\n");
+ return (status);
+}
+
+/* 1-3-6 MODE SELECT -- sets various operation mode parameters for scanner */
+static SANE_Status
+mode_select (int fd, MP * settings)
+{
+ static struct
+ {
+ SELECT cmd; /* Mode page Select command */
+ MP mp; /* Hdr + Parameters */
+ } msc; /* Mode Select Command */
+ SANE_Status status;
+ size_t npages;
+
+ DBG (DBG_proc, ">> mode_select\n");
+
+ memset (&msc, 0, sizeof (msc)); /* Fill struct with zeros */
+ msc.cmd.opcode = HS2P_SCSI_MODE_SELECT; /* choose Mode Select Command */
+ msc.cmd.byte1 &= ~SMS_SP; /* unset bit0 SavePage to 0 */
+ msc.cmd.byte1 |= SMS_PF; /* set bit4 PageFormat to 1 */
+ npages = (settings->page.code == 2) ? 16 : 8;
+ msc.cmd.len = sizeof (msc.mp.hdr) + npages; /* either 4+8 or 4+20 */
+
+ memcpy (&msc.mp, settings, msc.cmd.len); /* Copy hdr+pages from Settings to msc.mp */
+ memset (&msc.mp.hdr, 0, sizeof (msc.mp.hdr)); /* make sure the hdr is all zeros */
+ /*
+ msc.hdr.data_len = 0x00;
+ msc.hdr.medium_type = 0x00;
+ msc.hdr.dev_spec = 0x00;
+ msc.hdr.blk_desc_len = 0x00;
+ */
+
+ /* Now execute the whole command */
+ if ((status =
+ sanei_scsi_cmd (fd, &msc, sizeof (msc.cmd) + msc.cmd.len, 0,
+ 0)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "ERROR: mode_select: %s\n", sane_strstatus (status));
+ DBG (DBG_error, "PRINTING CMD BLOCK:\n");
+ print_bytes (&msc.cmd, sizeof (msc.cmd));
+ DBG (DBG_error, "PRINTING MP HEADER:\n");
+ print_bytes (&msc.mp.hdr, sizeof (msc.mp.hdr));
+ DBG (DBG_error, "PRINTING MP PAGES:\n");
+ print_bytes (&msc.mp.page, msc.cmd.len);
+ }
+
+ DBG (DBG_proc, "<< mode_select\n");
+ return (status);
+}
+
+/* 1-3-7 MODE SENSE -- gets various operation mode parameters from scanner */
+static SANE_Status
+mode_sense (int fd, MP * buf, SANE_Byte page_code)
+{
+ SANE_Status status;
+ SENSE cmd; /* 6byte cmd */
+ MP msp; /* Mode Sense Page
+ * 4byte hdr + {2bytes +14 bytes}
+ * buffer to hold mode sense data gotten from scanner */
+
+ size_t nbytes;
+
+ DBG (DBG_proc, ">>>>> mode_sense: fd=%d, page_code=%#02x\n", fd, page_code);
+ nbytes = sizeof (msp);
+
+ DBG (DBG_info,
+ ">>>>> mode_sense: Zero'ing ModeSenseCommand msc and msp structures\n");
+
+ memset (&cmd, 0, sizeof (cmd)); /* Fill cmd struct with zeros */
+ memset (&msp, 0, sizeof (msp)); /* Fill msp struct with zeros */
+
+ /* set up Mode Sense Command */
+ DBG (DBG_info, ">>>>> mode_sense: Initializing Mode Sense cmd\n");
+ cmd.opcode = HS2P_SCSI_MODE_SENSE;
+ cmd.dbd &= ~(1 << 3); /* Disable Block Description (bit3) is set to 0 */
+ cmd.pc = (page_code & 0x3F); /* bits 5-0 */
+ cmd.pc &= ~(0x03 << 6); /* unset PC Field (bits7-6)
+ * 00 Curent Value is the only effective value
+ * 01 Changeable Value
+ * 10 Default Value
+ * 11 Saved Value */
+ /* cmd.len = ??? Allocation Length */
+
+ /* Now execute the whole command and store results in msc */
+ DBG (DBG_info, ">>>>> mode_sense: sanei_scsi_cmd\n");
+ DBG (DBG_info, ">>>>> cmd.opcode=%#0x cmd.dbd=%#02x, cmd.pc=%#02x\n",
+ cmd.opcode, cmd.dbd, cmd.pc);
+
+ nbytes = (page_code == 2) ? 20 : 12;
+ DBG (DBG_info,
+ ">>>>> sizeof(cmd)=%lu sizeof(msp)=%lu sizeof(hdr)=%lu sizeof(page)=%lu requesting %lu bytes\n",
+ (u_long) sizeof (cmd), (u_long) sizeof (msp),
+ (u_long) sizeof (msp.hdr), (u_long) sizeof (msp.page),
+ (u_long) nbytes);
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &msp, &nbytes);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "ERROR mode_sense: sanei_scsi_cmd error \"%s\"\n",
+ sane_strstatus (status));
+ DBG (DBG_error,
+ ">>>>> mode sense: number of bytes received from scanner: %lu\n",
+ (u_long) nbytes);
+ DBG (DBG_error, "PRINTING CMD BLOCK:\n");
+ print_bytes (&cmd, sizeof (cmd));
+ DBG (DBG_error, "PRINTING MP HEADER:\n");
+ print_bytes (&msp.hdr, sizeof (msp.hdr));
+ DBG (DBG_error, "PRINTING MP PAGES:\n");
+ print_bytes (&msp.page, sizeof (msp.page));
+ }
+ else
+ {
+ /* nbytes = (page_code==2)? 14 : 6; */
+ DBG (DBG_info, ">> >> got %lu bytes from scanner\n", (u_long) nbytes);
+ nbytes -= 4; /* we won't copy 4 byte hdr */
+ DBG (DBG_info, ">>>>> copying from msp to calling function's buf\n"
+ ">>>>> msp.page_size=%lu bytes=%lu buf_size=%lu\n",
+ (u_long) sizeof (msp.page), (u_long) nbytes,
+ (u_long) sizeof (*buf));
+ memcpy (buf, &(msp.page), nbytes);
+ }
+
+ DBG (DBG_proc, "<<<<< mode_sense\n");
+ return (status);
+}
+
+static SANE_Status
+set_window (int fd, SWD * swd)
+{
+ static struct
+ {
+ struct set_window_cmd cmd;
+ struct set_window_data swd;
+ } win;
+ SANE_Status status;
+ static size_t wdl, tl; /*window descriptor length, transfer length */
+ DBG (DBG_proc, ">> set_window\n");
+
+ /* initialize our struct with zeros */
+ memset (&win, 0, sizeof (win));
+
+ /* fill in struct with opcode */
+ win.cmd.opcode = HS2P_SCSI_SET_WINDOW;
+
+ /* bytes 1-5 are reserved */
+
+ /* Transfer length is header + window data */
+ tl = sizeof (*swd);
+ _lto3b (tl, &win.cmd.len[0]); /* 8 + (2*320) = 648 */
+ DBG (DBG_info,
+ "set_window: SET WINDOW COMMAND Transfer Length = %lu (should be 648)\n",
+ (unsigned long) tl);
+
+ /* Copy data from swd (including 8-byte header) to win.swd */
+ DBG (DBG_info,
+ "set_window: COPYING %lu bytes from settings to Set Window Command (%lu)\n",
+ (u_long) sizeof (*swd), (u_long) sizeof (win.swd));
+ if (!memcpy (&(win.swd), swd, sizeof (*swd)))
+ {
+ DBG (DBG_error, "set_window: error with memcpy\n");
+ }
+
+ /* Set Window Data Header: 0-5:reserved; 6-7:Window Descriptor Lenght=640 */
+ wdl = sizeof (win.swd) - sizeof (win.swd.hdr);
+ _lto2b (wdl, &win.swd.hdr.len[0]);
+ DBG (DBG_info,
+ "set_window: SET WINDOW COMMAND Window Descriptor Length = %lu (should be 640)\n",
+ (unsigned long) wdl);
+
+ /* Now execute command */
+ DBG (DBG_info,
+ "set_window: calling sanei_scsi_cmd(%d,&win,%lu, NULL, NULL)\n", fd,
+ (u_long) sizeof (win));
+ status = sanei_scsi_cmd (fd, &win, sizeof (win), NULL, NULL);
+ /*
+ status = sanei_scsi_cmd2 (fd, &win.cmd, sizeof(win.cmd), &win.swd, sizeof(win.swd), NULL, NULL);
+ */
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "*********************\n");
+ DBG (DBG_error, "ERROR: set_window: %s\n", sane_strstatus (status));
+ DBG (DBG_error, "PRINTING SWD CMD BLK:\n");
+ print_bytes (&win.cmd, sizeof (win.cmd));
+ DBG (DBG_error, "PRINTING SWD HEADER:\n");
+ print_bytes (&win.swd.hdr, sizeof (win.swd.hdr));
+ DBG (DBG_error, "PRINTING SWD DATA[0]:\n");
+ print_bytes (&win.swd.data[0], sizeof (win.swd.data[0]));
+ DBG (DBG_error, "PRINTING SWD DATA[1]:\n");
+ print_bytes (&win.swd.data[1], sizeof (win.swd.data[1]));
+ DBG (DBG_error, "*********************\n");
+ }
+
+ DBG (DBG_proc, "<< set_window\n");
+ return (status);
+}
+
+static SANE_Status
+get_window (int fd, GWD * gwd)
+{
+ struct get_window_cmd cmd;
+ SANE_Status status;
+ static size_t gwd_size;
+
+ DBG (DBG_proc, ">> get_window\n");
+
+ gwd_size = sizeof (*gwd);
+ DBG (DBG_info, ">> get_window datalen = %lu\n", (unsigned long) gwd_size);
+
+ /* fill in get_window_cmd */
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR cmd */
+ cmd.opcode = HS2P_SCSI_GET_WINDOW;
+ cmd.byte1 &= ~0x01; /* unset single bit 0 */
+ cmd.win_id = 0x00; /* either 0 or 1 */
+ _lto3b (gwd_size, cmd.len); /* Transfer Length is byte length of DATA to be returned */
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), gwd, &gwd_size);
+
+ DBG (DBG_proc, "<< get_window, datalen = %lu\n", (unsigned long) gwd_size);
+ return (status);
+}
+static void
+print_window_data (SWD * buf)
+{
+ int i, j, k;
+ struct hs2p_window_data *data;
+ struct window_section *ws;
+
+ DBG (DBG_proc, ">> print_window_data\n");
+ DBG (DBG_info, "HEADER\n");
+ for (i = 0; i < 6; i++)
+ DBG (DBG_info, "%#02x\n", buf->hdr.reserved[i]);
+ DBG (DBG_info, "Window Descriptor Length=%lu\n\n", _2btol (buf->hdr.len));
+
+ for (i = 0; i < 2; i++)
+ {
+ data = &buf->data[i];
+ DBG (DBG_info, "Window Identifier = %d\n", data->window_id);
+ DBG (DBG_info, "AutoBit = %#x\n", data->auto_bit);
+ DBG (DBG_info, "X-Axis Resolution = %lu\n", _2btol (data->xres));
+ DBG (DBG_info, "Y-Axis Resolution = %lu\n", _2btol (data->yres));
+ DBG (DBG_info, "X-Axis Upper Left = %lu\n", _4btol (data->ulx));
+ DBG (DBG_info, "Y-Axis Upper Left = %lu\n", _4btol (data->uly));
+ DBG (DBG_info, "Window Width = %lu\n", _4btol (data->width));
+ DBG (DBG_info, "Window Length = %lu\n", _4btol (data->length));
+ DBG (DBG_info, "Brightness = %d\n", data->brightness);
+ DBG (DBG_info, "Threshold = %d\n", data->threshold);
+ DBG (DBG_info, "Contrast = %d\n", data->contrast);
+ DBG (DBG_info, "Image Composition = %#0x\n", data->image_composition);
+ DBG (DBG_info, "Bits per Pixel = %d\n", data->bpp);
+ DBG (DBG_info, "Halftone Code = %#0x\n", data->halftone_code);
+ DBG (DBG_info, "Halftone Id = %#0x\n", data->halftone_id);
+ DBG (DBG_info, "Byte29 = %#0x RIF=%d PaddingType=%d\n", data->byte29,
+ data->byte29 & 0x80, data->byte29 & 0x7);
+ DBG (DBG_info, "Bit Ordering = %lu\n", _2btol (data->bit_ordering));
+ DBG (DBG_info, "Compression Type = %#x\n", data->compression_type);
+ DBG (DBG_info, "Compression Arg = %#x\n", data->compression_arg);
+ for (j = 0; j < 6; j++)
+ DBG (DBG_info, "Reserved=%#x\n", data->reserved2[j]);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored1);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored2);
+ DBG (DBG_info, "Byte42 = %#x MRIF=%d Filtering=%d GammaID=%d\n",
+ data->byte42, data->byte42 & 0x80, data->byte42 & 0x70,
+ data->byte42 & 0x0F);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored3);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored4);
+ DBG (DBG_info, "Binary Filtering = %#x\n", data->binary_filtering);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored5);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored6);
+ DBG (DBG_info, "Automatic Separation = %#x\n",
+ data->automatic_separation);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored7);
+ DBG (DBG_info, "Automatic Binarization = %#x\n",
+ data->automatic_binarization);
+ for (j = 0; j < 13; j++)
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored8[j]);
+
+ for (k = 0; k < 8; k++)
+ {
+ ws = &data->sec[k];
+ DBG (DBG_info, "\n\n");
+ DBG (DBG_info, "SECTION %d\n", k);
+ DBG (DBG_info, "Section Enable Flat (sef bit) = %#x\n", ws->sef);
+ DBG (DBG_info, "ignored = %d\n", ws->ignored0);
+ DBG (DBG_info, "Upper Left X = %lu\n", _4btol (ws->ulx));
+ DBG (DBG_info, "Upper Left Y = %lu\n", _4btol (ws->uly));
+ DBG (DBG_info, "Width = %lu\n", _4btol (ws->width));
+ DBG (DBG_info, "Length = %lu\n", _4btol (ws->length));
+ DBG (DBG_info, "Binary Filtering = %#x\n", ws->binary_filtering);
+ DBG (DBG_info, "ignored = %d\n", ws->ignored1);
+ DBG (DBG_info, "Threshold = %#x\n", ws->threshold);
+ DBG (DBG_info, "ignored = %d\n", ws->ignored2);
+ DBG (DBG_info, "Image Composition = %#x\n", ws->image_composition);
+ DBG (DBG_info, "Halftone Id = %#x\n", ws->halftone_id);
+ DBG (DBG_info, "Halftone Code = %#x\n", ws->halftone_code);
+ for (j = 0; j < 7; j++)
+ DBG (DBG_info, "ignored = %d\n", ws->ignored3[j]);
+ }
+ }
+ DBG (DBG_proc, "<< print_window_data\n");
+}
+
+static SANE_Status
+read_data (int fd, void *buf, size_t * buf_size, SANE_Byte dtc, u_long dtq)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> read_data buf_size=%lu dtc=0x%2.2x dtq=%lu\n",
+ (unsigned long) *buf_size, (int) dtc, dtq);
+ if (fd < 0)
+ {
+ DBG (DBG_error, "read_data: scanner is closed!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = dtc;
+ _lto2b (dtq, cmd.dtq);
+ _lto3b (*buf_size, cmd.len);
+
+ DBG (DBG_info, "read_data ready to send scsi cmd\n");
+ DBG (DBG_info, "opcode=0x%2.2x, dtc=0x%2.2x, dtq=%lu, transfer len =%d\n",
+ cmd.opcode, cmd.dtc, _2btol (cmd.dtq), _3btol (cmd.len));
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "read_data: %s\n", sane_strstatus (status));
+ DBG (DBG_proc, "<< read_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+
+#if 0
+static SANE_Status
+send_data (int fd, void *buf, size_t * buf_size)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> send_data %lu\n", (unsigned long) *buf_size);
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ memcpy (&cmd, buf, sizeof (*buf)); /* Fill in our struct with set values */
+ cmd.opcode = HS2P_SCSI_SEND_DATA;
+ _lto3b (*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< send_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+#endif
+
+static SANE_Bool
+is_valid_endorser_character (char c)
+{
+ int i = (int) c;
+ /* 44 characters can be printed by endorser */
+
+ if (i >= 0x30 && i <= 0x3A)
+ return SANE_TRUE; /* 0123456789: */
+ if (i == 0x23)
+ return SANE_TRUE; /* # */
+ if (i == 0x27)
+ return SANE_TRUE; /* ` */
+ if (i >= 0x2C && i <= 0x2F)
+ return SANE_TRUE; /* '-./ */
+ if (i == 0x20)
+ return SANE_TRUE; /* space */
+ if (i >= 0x41 && i <= 0x5A)
+ return SANE_TRUE; /* ABCDEFGHIJKLMNOPQRSTUVWXYZ <spaces> */
+ if (i >= 0x61 && i <= 0x7A)
+ return SANE_TRUE; /* abcdefghijklmnopqrstuvwxyz <spaces> */
+
+ return SANE_FALSE;
+}
+
+static SANE_Status
+set_endorser_string (int fd, SANE_String s)
+{
+ struct
+ {
+ struct scsi_rs_scanner_cmd cmd;
+ SANE_Byte endorser[19];
+ } out;
+ char *t;
+ int i, len;
+
+ SANE_Status status;
+ DBG (DBG_proc, ">> set_endorser_string %s\n", s);
+
+ for (i = 0, t = s; *t != '\0' && i < 19; i++)
+ {
+ DBG (DBG_info, "CHAR=%c\n", *t);
+ if (!is_valid_endorser_character (*t++))
+ return SANE_STATUS_INVAL;
+ }
+ len = strlen (s);
+
+ memset (&out, 0, sizeof (out)); /* CLEAR */
+ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */
+ out.cmd.dtc = 0x80; /* Endorser Data */
+ _lto3b (len, &out.cmd.len[0]); /* 19 bytes max */
+ memset (&out.endorser[0], ' ', 19); /* fill with spaces */
+ memcpy (&out.endorser[0], s, len);
+
+ status = sanei_scsi_cmd (fd, &out, sizeof (out), NULL, NULL);
+
+
+ DBG (DBG_proc, "<< set_endorser_string s=\"%s\" len=%d\n", s, len);
+ return (status);
+}
+
+static SANE_Status
+hs2p_send_gamma (HS2P_Scanner * s)
+{
+ SANE_Status status;
+ struct
+ {
+ struct scsi_rs_scanner_cmd cmd;
+ SANE_Byte gamma[2 + GAMMA_LENGTH];
+ } out;
+ int i;
+ size_t len = sizeof (out.gamma);
+
+ DBG (DBG_proc, ">> teco_send_gamma\n");
+
+ memset (&out, 0, sizeof (out)); /* CLEAR */
+ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */
+ out.cmd.dtc = 0x03; /* Gamma Function Data */
+ _lto3b (len, &out.cmd.len[0]); /* 19 bytes max */
+ out.gamma[0] = 0x08; /* Gamma ID for Download table */
+ out.gamma[1] = 0x08; /* The Number of gray scale (M) = 8 */
+ for (i = 2; i < 2 + GAMMA_LENGTH; i++)
+ {
+ out.gamma[i] = s->gamma_table[i];
+ }
+ status = sanei_scsi_cmd (s->fd, &out, sizeof (out), NULL, NULL);
+
+ DBG (DBG_proc, "<< teco_send_gamma\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+clear_maintenance_data (int fd, int code, char XorY, int number)
+{
+ struct
+ {
+ struct scsi_rs_scanner_cmd cmd;
+ char string[20];
+ } out;
+
+ SANE_Status status;
+ DBG (DBG_proc, ">> set_maintenance data\n");
+
+ memset (&out, 0, sizeof (out)); /* CLEAR */
+ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */
+ out.cmd.dtc = 0x85; /* Maintenance Data */
+ _lto3b (20, out.cmd.len); /* 20 bytes */
+ switch (code)
+ {
+ case 1:
+ strcpy (out.string, "EEPROM ALL ALL RESET");
+ break;
+ case 2:
+ strcpy (out.string, "EEPROM ALL RESET");
+ break;
+ case 3:
+ strcpy (out.string, "ADF RESET");
+ break;
+ case 4:
+ strcpy (out.string, "FLATBED RESET");
+ break;
+ case 5:
+ strcpy (out.string, "LAMP RESET");
+ break;
+ case 6:
+ sprintf (out.string, "EEPROM ADF %c %+4.1d", XorY, number);
+ break;
+ case 7:
+ sprintf (out.string, "EEPROM BOOK %c %4.1d", XorY, number);
+ break;
+ case 8:
+ sprintf (out.string, "WHITE ADJUST DATA %3d", number);
+ break;
+ case 9:
+ strcpy (out.string, "EEPROM FIRST WHITE ODD");
+ break;
+ case 10:
+ strcpy (out.string, "EEPROM FIRST WHITE EVEN");
+ break;
+ case 11:
+ strcpy (out.string, "R ADF RESET");
+ break;
+ case 12:
+ strcpy (out.string, "R LAMP RESET");
+ break;
+ case 13:
+ sprintf (out.string, "EEPROM R ADF %c %4.1d", XorY, number);
+ break;
+ case 14:
+ strcpy (out.string, "ENDORSER RESET");
+ break;
+ }
+ status = sanei_scsi_cmd (fd, &out, sizeof (out), NULL, NULL);
+
+ DBG (DBG_proc, "<< set_maintenance data\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+read_halftone_mask (int fd, SANE_Byte halftone_id, void *buf,
+ size_t * buf_size)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ SANE_Int len;
+ DBG (DBG_proc, ">> read_halftone_mask\n");
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = DATA_TYPE_HALFTONE;
+ _lto2b (halftone_id, cmd.dtq);
+
+ /* Each cell of an NxM dither pattern is 1 byte from the set {2,3,4,6,8,16} */
+ switch (halftone_id)
+ {
+ case 0x01:
+ len = 32;
+ break; /* 8x4, 45 degree */
+ case 0x02:
+ len = 36;
+ break; /* 6x6, spiral */
+ case 0x03:
+ len = 16;
+ break; /* 4x4, spiral */
+ case 0x04:
+ len = 64;
+ break; /* 8x8, 90 degree */
+ case 0x05:
+ len = 70;
+ break; /* 70 lines */
+ case 0x06:
+ len = 95;
+ break; /* 95 lines */
+ case 0x07:
+ len = 180;
+ break; /* 180 lines */
+ case 0x08:
+ len = 128;
+ break; /* 16x8, 45 degree */
+ case 0x09:
+ len = 256;
+ break; /* 16x16, 90 degree */
+ case 0x0A:
+ len = 64;
+ break; /* 8x8, Bayer */
+ default:
+ return SANE_STATUS_INVAL; /* Reserved */
+ }
+
+ _lto3b (len, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< read_halftone_mask\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+set_halftone_mask (int fd, SANE_Byte halftone_id, void *buf,
+ size_t * buf_size)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> set_halftone_mask\n");
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = DATA_TYPE_HALFTONE;
+ _lto2b (halftone_id, cmd.dtq);
+
+ /* Each cell of an NxM dither pattern is 1 byte from the set {2,3,4,6,8,16}
+ * 0x80, 0x81 are User definable custom dither patterns
+ */
+ if (halftone_id != 0x80 && halftone_id != 0x81)
+ return SANE_STATUS_INVAL;
+
+ _lto3b (*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< set_halftone_mask\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+read_gamma_function (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+
+static SANE_Status
+read_endorser_data (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+
+static SANE_Status
+read_size_data (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+read_maintenance_data (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+#endif
+
+/* Bit0: is 0 if document on ADF; else 1
+ * Bit1: is 0 if ADF cover is closed; else 1
+ * Bit2: reserved
+ * Bits7-3: reserved
+*/
+
+
+#if 0
+static SANE_Status
+read_adf_status (int fd, SANE_Byte * adf_status_byte)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct scsi_rs_scanner_cmd cmd;
+ static size_t len = 1;
+
+ DBG (DBG_proc, ">> read_adf_status\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = DATA_TYPE_ADF_STATUS;
+ _lto3b (0x01, cmd.len); /* convert 0x01 into 3-byte Transfer Length */
+ if ((status =
+ sanei_scsi_cmd (fd, &cmd, sizeof (cmd), adf_status_byte,
+ &len)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_adf_status ERROR: %s\n", sane_strstatus (status));
+ }
+ DBG (DBG_proc, "<< read_adf_status\n");
+ return (status);
+}
+#endif
+
+/*
+ * read_ipu_photoletter_parameters
+ * read_ipu_threshold_parameters
+ * read_sensor_data (WHAT DATA TYPE CODE?)
+*/
+/* SEND CMD */
+/*
+ * send_halftone_mask
+ * send_gamma_function
+ * send_endorser_data
+ * send_maintenance_data
+ * EPROM All Clear
+ * EPROM Counter Clear
+ * ADF Counter Clear
+ * Flatbed Counter Clear
+ * Lamp Counter Clear
+ * ADF Register Data
+ * Flatbed Register Data
+ * White Adjustment Data
+ * White level first Data (ODD)
+ * White level first Data (EVEN)
+ * Reverse side ADF Counter Clear
+ * Reverse side Lamp Counter Clear
+ * Reverse side ADF Register Data
+ * Endorser Character Counter Clear
+ * send_ipu_parameters
+*/
+
+/* OBJECT POSITION */
+/* GET DATA BUFFER STATUS */
+
+/* 1-3-4 MODE SELECT */
+
+/* 1-3-5 Reserve Unit: 0x16
+ * 1-3-6 Release Unit: 0x17
+*/
+static SANE_Status
+unit_cmd (int fd, SANE_Byte opcode)
+{
+ static struct
+ {
+ SANE_Byte opcode; /* 16H: Reserve Unit 17H: Release Unit */
+ SANE_Byte byte1; /* 7-5: LUN; 4: 3rd Party; 3-1: 3rd Party Device; 0: Reserved */
+ SANE_Byte reserved[3];
+ SANE_Byte control; /* 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link */
+ } cmd;
+
+ SANE_Byte LUN = (0x00 & 0x07) << 5;
+ SANE_Status status;
+ DBG (DBG_proc, ">> unit_cmd\n");
+
+ cmd.opcode = opcode;
+ cmd.byte1 = LUN & 0xE1; /* Mask=11100001 3rd Party and 3rd Party Device must be 0 */
+ memset (&cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< unit_cmd\n");
+ return (status);
+
+}
+
+/* The OBJECT POSITION command is used for carriage control or
+ * document feed and eject with ADF
+ *
+ * Position Function: Byte1 bits2-0
+ * 000 Unload instructs document eject
+ * 001 Load instructs document feed to scan start position
+ * 010 Absolute Positioning - instructs carriage to move to carriage lock position
+ * The carriage moves in the Y-axis direction as the amount set in Count when
+ * count>0
+ * (Not supported in IS420)
+ *
+*/
+static SANE_Status
+object_position (int fd, int load)
+{
+ static struct scsi_object_position_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> object_position\n");
+
+ /* byte 0 opcode
+ * byte 1 position function
+ * bytes 2-4 reserved
+ * bytes 5-8 reserved
+ * byte 9 control
+ */
+
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = HS2P_SCSI_OBJECT_POSITION;
+ if (load)
+ cmd.position_func = OBJECT_POSITION_LOAD;
+ else
+ cmd.position_func = OBJECT_POSITION_UNLOAD;
+
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< object_position\n");
+ return (status);
+}
+
+static SANE_Status
+get_data_status (int fd, STATUS_DATA * dbs)
+{
+ static GET_DBS_CMD cmd;
+ static STATUS_BUFFER buf; /* hdr + data */
+ size_t bufsize = sizeof (buf);
+ SANE_Status status;
+ DBG (DBG_proc, ">> get_data_status %lu\n", (unsigned long) bufsize);
+
+ /* Set up GET DATA BUFFER STATUS cmd */
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR cmd */
+ cmd.opcode = HS2P_SCSI_GET_BUFFER_STATUS;
+ cmd.wait &= ~0x01; /* unset Wait bit0 */
+ _lto2b (bufsize, cmd.len);
+
+ /* Now execute cmd, and put returned results in buf */
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &buf, &bufsize);
+
+ /* Now copy from buf.data to dbs */
+ memcpy (dbs, &buf.data, sizeof (*dbs));
+
+ if (status == SANE_STATUS_GOOD &&
+ ((unsigned int) _3btol (buf.hdr.len) <= sizeof (*dbs)
+ || _3btol (buf.data.filled) == 0))
+ {
+ DBG (DBG_info, "get_data_status: busy\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ DBG (DBG_proc, "<< get_data_status %lu\n", (unsigned long) bufsize);
+ return (status);
+}
+
+/* 1-3-7 MODE SENSE */
+/* 1-3-8 SCAN */
+
+/* 1-3-9 Receive Diagnostic
+ * Byte0: 1CH
+ * Byte1: 7-5 LUN; 4-0: reserved
+ * Byte2: Reserved
+ * Byte3-4: Allocation Length
+ * Byte5: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link
+ *
+ * This command is treated as a dummy command
+ * Return GOOD unless there is an error in command in which case it returns CHECK
+*/
+
+/*
+* The IS450 performs 7 self-diagnostics tests
+* 1) Home position error check
+* 2) Exposure lamp error check
+* 3) White level error check
+* 4) Document table error check
+* 5) SCU error check
+* 6) RCU error check
+* 7) Memory error check
+*
+* and uses the lights on the scanner to indicate the result
+*
+* PowerOn MachineBusy DocumentInPlace Error
+* (green) (green) (green) (red)
+*
+* SCU error check Blinking Blinking
+* RCU error check Blinking On Blinking
+* Home position error check Blinking Blinking Blinking On
+* Exposure lamp error check Blinking Blinking On On
+* White level error check Blinking Blinking
+* Memory Error (Simplex) Blinking
+* Memory Error (Duplex) Blinking
+*
+*/
+#if 0
+static SANE_Status
+receive_diagnostic (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> receive_diagnostic\n");
+
+ cmd[0] = HS2P_SCSI_RECEIVE_DIAGNOSTICS;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< receive_diagnostic\n");
+ return (status);
+}
+#endif
+
+/* 1-3-10 Send Diagnostic
+ * Byte0: 1DH
+ * Byte1: 7-5 LUN; 4: PF; 3: Reserved; 2: S-Test; 1: DevOfl; 0: U-Ofl
+ * Byte2: Reserved
+ * Byte3-4: Parameter List Length
+ * Byte5: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link
+ * This command executes self-diagnostic and optical-adjustment
+ * PF, DevOfl, and Parameter List Length must be 0 or CHECK condition is returned.
+*/
+#if 0
+static SANE_Status
+send_diagnostic (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> send_diagnostic\n");
+
+ cmd[0] = HS2P_SCSI_SEND_DIAGNOSTICS;
+ cmd[1] = 0x00 & (1 << 2) & 0xED; /* Set Self-Test bit and clear PF, DevOfl bits */
+ cmd[3] = 0x00;
+ cmd[4] = 0x00; /* Parameter list (bytes3-4) must be 0x00 */
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< send_diagnostic\n");
+ return (status);
+}
+#endif
+
+
+/* 1-3-8 SCAN command is used to instruct scanner to start scanning */
+static SANE_Status
+trigger_scan (HS2P_Scanner * s)
+{
+ static struct
+ {
+ START_SCAN cmd;
+ SANE_Byte wid[2]; /* scanner supports up to 2 windows */
+ } scan;
+ SANE_Status status;
+ DBG (DBG_proc, ">> trigger scan\n");
+
+ memset (&scan, 0, sizeof (scan)); /* CLEAR scan */
+ scan.cmd.opcode = HS2P_SCSI_START_SCAN;
+ /* Transfer length is the byte length of Window List transferred
+ * Window List is a list of Window Identifier created by SET WINDOW command
+ * Since only 1 Window is supported by SCAN command, 0 or 1 is used for Window Identifier
+ * and 1 or 2 for length
+ status = sanei_scsi_cmd (s->fd, &trigger, sizeof (trigger), &window_id_list[0], &wl_size);
+ */
+ scan.cmd.len = (s->val[OPT_DUPLEX].w == SANE_TRUE) ? 2 : 1;
+
+ DBG (DBG_info, "trigger_scan: sending %d Window Id to scanner\n",
+ scan.cmd.len);
+ status =
+ sanei_scsi_cmd (s->fd, &scan, sizeof (scan.cmd) + scan.cmd.len, NULL,
+ NULL);
+
+ DBG (DBG_proc, "<< trigger scan\n");
+ return (status);
+}
+
+#define MAX_WAITING_TIME 15
+static SANE_Status
+hs2p_wait_ready (HS2P_Scanner * s)
+{
+ STATUS_DATA dbs; /* Status Buffer Status DATA */
+ time_t now, start;
+ SANE_Status status;
+
+ start = time (NULL);
+
+ while (1)
+ {
+ status = get_data_status (s->fd, &dbs);
+
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (DBG_error, "scsi_wait_ready: get datat status failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ now = time (NULL);
+ if (now - start >= MAX_WAITING_TIME)
+ {
+ DBG (DBG_error,
+ "hs2p_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now - start));
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_STATUS_GOOD:
+ DBG (DBG_proc, "hs2p_wait_ready: %d bytes ready\n",
+ _3btol (dbs.filled));
+ return status;
+ break;
+ }
+ usleep (1000000); /* retry after 100ms */
+ }
+ return SANE_STATUS_INVAL;
+}
+
+
+/* MODE PAGES GET/SET */
+static SANE_Status
+connection_parameters (int fd, MP_CXN * settings, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_CXN buf;
+ size_t nbytes;
+ DBG (DBG_proc, ">> connection_parameters\n");
+ nbytes = sizeof (buf);
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET connection_parameters >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_CONNECTION);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_connection_parameters: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ memcpy (settings, &buf, nbytes);
+ }
+ else
+ { /* SET */
+ DBG (DBG_info, ">> SET connection_parameters >> calling mode_select\n");
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ memcpy (&buf, settings, nbytes);
+ /* Make sure calling function didn't change these bytes */
+ memset (&buf.hdr, 0, sizeof (buf.hdr)); /* Make sure 4bytes are 0 */
+ buf.code = PAGE_CODE_CONNECTION; /* bits5-0: Page Code 02H */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x0E; /* This is the only page with 14 bytes */
+
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_connection_parameters: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ DBG (DBG_proc, "<< connection_parameters\n");
+ return (status);
+}
+
+static SANE_Status
+get_basic_measurement_unit (int fd, SANE_Int * bmu, SANE_Int * mud)
+{
+ SANE_Status status;
+ MP_SMU buf;
+
+ DBG (DBG_proc, ">> get_basic_measurement_unit: fd=\"%d\"\n", fd);
+
+ status =
+ mode_sense (fd, (MP *) & buf,
+ (SANE_Byte) PAGE_CODE_SCANNING_MEASUREMENTS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_basic_measurement_unit: MODE_SELECT failed with status=%d\n",
+ status);
+ return (SANE_STATUS_INVAL);
+ }
+ *bmu = buf.bmu;
+ *mud = ((buf.mud[0] << 8) | buf.mud[1]);
+
+ DBG (DBG_proc, "<< get_basic_measurement_unit: bmu=%d mud=%d\n", *bmu,
+ *mud);
+ return (status);
+}
+
+static SANE_Status
+set_basic_measurement_unit (int fd, SANE_Byte bmu)
+{
+ MP_SMU buf; /* Mode Page Scanning Measurements Page Code */
+ SANE_Status status;
+ SANE_Int mud;
+ size_t bufsize = sizeof (buf);
+
+ DBG (DBG_proc, ">> set_basic_measurement_unit: %d\n", bmu);
+
+ /* Set up buf */
+ memset (&buf, 0, bufsize); /* CLEAR buf */
+ buf.code = PAGE_CODE_SCANNING_MEASUREMENTS; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+
+ buf.bmu = bmu; /* Power on default is POINTS */
+ mud = (bmu == INCHES) ? DEFAULT_MUD : 1;
+ DBG (DBG_info, "SET_BASIC_MEASUREMENT_UNIT: bmu=%d mud=%d\n", bmu, mud);
+ _lto2b (mud, &buf.mud[0]); /* buf.mud[0] = (mud >> 8) & 0xff; buf.mud[1] = (mud & 0xff); */
+
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_basic_measurement_unit: MODE_SELECT failed with status=%d\n",
+ status);
+ status = SANE_STATUS_INVAL;
+ }
+
+ DBG (DBG_proc,
+ "<< set_basic_measurement_unit: opcode=%d len=%d bmu=%d mud=%ld\n",
+ buf.code, buf.len, buf.bmu, _2btol (&buf.mud[0]));
+ return (status);
+}
+
+static SANE_Status
+adf_control (int fd, SANE_Bool flag, SANE_Byte * adf_control,
+ SANE_Byte * adf_mode, SANE_Byte * mwt)
+{
+ SANE_Status status;
+ MP_ADF buf;
+ size_t bufsize = sizeof (buf);
+
+ DBG (DBG_proc, ">> adf_control\n");
+
+ memset (&buf, 0, bufsize); /* Fill struct with zeros */
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET ADF_control>> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_ADF_CONTROL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_adf_control: MODE_SELECT failed\n");
+ return (status);
+ }
+ *adf_control = buf.adf_control;
+ *adf_mode = buf.adf_mode_control;
+ *mwt = buf.medium_wait_timer;
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ buf.code = PAGE_CODE_ADF_CONTROL; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ /* Byte2: adf_control: 7-2:reserved; 1-0:adf_control: Default 00H Flatbed, 01H Simplex, 02H Duplex */
+ /* Byte3: adf_mode_control: 7-3:reserved; 2: Prefeed Mode: 0 invalid, 1 valid; 1-0: ignored */
+ /* Byte4: medium_wait_timer: timeout period. Not supported */
+ buf.adf_control = (*adf_control & 0x03);
+ buf.adf_mode_control = (*adf_mode & 0x04);
+ buf.medium_wait_timer = *mwt;
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_adf_control: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ }
+ DBG (DBG_proc, ">> adf_control\n");
+ return (status);
+}
+
+static SANE_Status
+white_balance (int fd, int *val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_WhiteBal buf; /* White Balance Page Code */
+ size_t bufsize = sizeof (buf);
+
+ memset (&buf, 0, bufsize);
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_proc, ">> GET white_balance>> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_WHITE_BALANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_white_balance: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ *val = buf.white_balance;
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_WHITE_BALANCE; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ buf.white_balance = *val; /* Power on default is RELATIVE_WHITE */
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_white_balance: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ }
+ DBG (DBG_proc, "<< white balance: buf.white_balance=%#02x\n",
+ buf.white_balance);
+ return (status);
+}
+
+#if 0
+static SANE_Int
+lamp_timer (int fd, int val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_LampTimer buf; /* Lamp Timer Page Code */
+
+ DBG (DBG_proc, ">> lamp timer\n");
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET lamp_timer>> calling mode_sense\n");
+ if ((status =
+ mode_sense (fd, (MP *) & buf,
+ (SANE_Byte) PAGE_CODE_LAMP_TIMER_SET)) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_lamp_timer: MODE_SELECT failed\n");
+ return (-1);
+ }
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_LAMP_TIMER_SET; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ buf.time_on = val; /* time lamp has been on */
+ if ((status = mode_select (fd, (MP *) & buf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_lamp_timer: MODE_SELECT failed with status=%d\n", status);
+ return (-1);
+ }
+ }
+ DBG (DBG_proc, "<< lamp timer\n");
+ return (buf.time_on);
+}
+#endif
+
+static SANE_Status
+endorser_control (int fd, int *val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_EndCtrl buf; /* MPHdr (4bytes) + MPP (8bytes) */
+ SANE_Byte mask = 0x7; /* 7-3:reserved; 2-0: Endorser Control */
+ size_t bufsize = sizeof (buf);
+
+ DBG (DBG_proc, ">> endorser_control: fd=%d val=%d flag=%d\n", fd, *val,
+ flag);
+
+ memset (&buf, 0, bufsize); /* Fill struct with zeros */
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET endorser control >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_ENDORSER_CONTROL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_endorser_control: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ *val = buf.endorser_control & mask;
+ }
+ else
+ { /* SET */
+ DBG (DBG_info, ">> SET endorser control >> calling mode_select\n");
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_ENDORSER_CONTROL; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+
+ buf.endorser_control = *val & mask; /* Power on default is OFF */
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_endorser_control: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ }
+ DBG (DBG_proc, "<< endorser_control: endorser_control=%#02x\n",
+ buf.endorser_control);
+ return (status);
+}
+
+/* When SCAN, READ, or LOAD (in ADF mode) is issued, scanner waits until operator panel start button is pressed */
+static SANE_Status
+scan_wait_mode (int fd, int val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_SWM buf; /* Scan Wait Mode Page Code */
+ DBG (DBG_proc, ">> scan_wait_mode\n");
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET scan_wait_mode >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_SCAN_WAIT_MODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_scan_wait_mode: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_SCAN_WAIT_MODE; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ buf.swm = 0x00;
+ if (val == 1)
+ buf.swm |= 1; /* set bit 1 if scan_wait_mode ON */
+ else
+ buf.swm &= ~1; /* unset bit 1 if scan_wait_mode OFF */
+
+ DBG (DBG_info, ">> SET scan_wait_mode >> calling mode_sense\n");
+ if ((status = mode_select (fd, (MP *) & buf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "mode_select ERROR %s\n", sane_strstatus (status));
+ }
+ }
+ DBG (DBG_proc, "<< scan_wait_mode: buf.swm=%#02x\n", buf.swm);
+ return (status);
+}
+
+/* Selectable when Send Diagnostics command is performed */
+static SANE_Int
+service_mode (int fd, int val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_SRV buf; /* Service Mode Page Code */
+ DBG (DBG_proc, ">> service_mode\n");
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET service_mode >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf,
+ (SANE_Byte) PAGE_CODE_SERVICE_MODE_SELECT);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_service_mode: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_SERVICE_MODE_SELECT; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ /* 0H: Self-Diagnostics Mode, 1H: Optical Adjustment Mode */
+ buf.service = val & 0x01;
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_service_mode: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ DBG (DBG_proc, "<< service_mode\n");
+ return (buf.service & 0x01);
+}
diff --git a/backend/hs2p-scsi.h b/backend/hs2p-scsi.h
new file mode 100644
index 0000000..eecb60c
--- /dev/null
+++ b/backend/hs2p-scsi.h
@@ -0,0 +1,1290 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include <time.h>
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+/* 1-2 SCSI STATUS BYTE KEYS */
+#define HS2P_SCSI_STATUS_GOOD 0x00
+#define HS2P_SCSI_STATUS_CHECK 0x02
+#define HS2P_SCSI_STATUS_BUSY 0x08
+#define HS2P_SCSI_STATUS_RESERVATION CONFLICT 0x18
+/* All other status byte keys are reserved */
+
+/*
+ * SCSI Command List for Command Descriptor Block
+ * All reserved bit and fields in the CDB must be zero
+ * Values in the CDB described as "Reserved" must no be specified
+ * The FLAG and LINK bits in the CONTROL byte must be zero
+ * Any values in the Vendor Unique field are ignored
+ * The Logical Unit Number in the CDB must always be zero
+ * All Reserved bit and fields in the data fields must be zero
+ * Values of parameters in the data fields described as
+ * "Reserved" or "Not supported" must not be specified
+*/
+
+/* 1-3 SCSI COMMANDS */
+#define HS2P_SCSI_TEST_UNIT_READY 0x00
+#define HS2P_SCSI_REQUEST_SENSE 0x03
+#define HS2P_SCSI_INQUIRY 0x12
+#define HS2P_SCSI_MODE_SELECT 0x15
+#define HS2P_SCSI_RESERVE_UNIT 0x16
+#define HS2P_SCSI_RELEASE_UNIT 0x17
+#define HS2P_SCSI_MODE_SENSE 0x1a
+#define HS2P_SCSI_START_SCAN 0x1b
+#define HS2P_SCSI_RECEIVE_DIAGNOSTICS 0x1c
+#define HS2P_SCSI_SEND_DIAGNOSTICS 0x1d
+#define HS2P_SCSI_SET_WINDOW 0x24
+#define HS2P_SCSI_GET_WINDOW 0x25
+#define HS2P_SCSI_READ_DATA 0x28
+#define HS2P_SCSI_SEND_DATA 0x2a
+#define HS2P_SCSI_OBJECT_POSITION 0x31
+#define HS2P_SCSI_GET_BUFFER_STATUS 0x34
+
+/* Sense Key Defines */
+#define HS2P_SK_NO_SENSE 0x00
+#define HS2P_SK_RECOVERED_ERROR 0x01
+#define HS2P_SK_NOT_READY 0x02
+#define HS2P_SK_MEDIUM_ERROR 0x03
+#define HS2P_SK_HARDWARE_ERROR 0x04
+#define HS2P_SK_ILLEGAL_REQUEST 0x05
+#define HS2P_SK_UNIT_ATTENTION 0x06
+#define HS2P_SK_DATA_PROJECT 0x07
+#define HS2P_SK_BLANK_CHECK 0x08
+#define HS2P_SK_VENDOR_UNIQUE 0x09
+#define HS2P_SK_COPY_ABORTED 0x0a
+#define HS2P_SK_ABORTED_COMMAND 0x0b
+#define HS2P_SK_EQUAL 0x0c
+#define HS2P_SK_VOLUME_OVERFLOW 0x0d
+#define HS2P_SK_MISCOMPARE 0x0e
+#define HS2P_SK_RESERVED 0x0f
+
+struct sense_key
+{
+ int key;
+ char *meaning;
+ char *description;
+};
+static struct sense_key sensekey_errmsg[16] = {
+ {0x00, "NO SENSE", "Indicates that there is no Sense Key information"},
+ {0x01, "RECOVERED ERROR", "Invalid"},
+ {0x02, "NOT READY",
+ "Indicates that the scanner is not ready, e.g. ADF cover not closed"},
+ {0x03, "MEDIUM ERROR", "Error regarding document such as paper jam"},
+ {0x04, "HARDWARE ERROR",
+ "Error relating to hardware, e.g. CCD line clock error"},
+ {0x05, "ILLEGAL REQUEST",
+ "Used such as when illegal parameter exists in data or command"},
+ {0x06, "UNIT ATTENTION",
+ "Used when power on, BUS DEVICE RESET message or hardware reset"},
+ {0x07, "DATA PROJECT", "Invalid"},
+ {0x08, "BLANK CHECK", "Invalid"},
+ {0x09, "VENDOR UNIQUE", "Invalid"},
+ {0x0a, "COPY ABORTED", "Invalid"},
+ {0x0b, "ABORTED COMMAND", "Used when scanner aborts a command execution"},
+ {0x0c, "EQUAL", "Invalid"},
+ {0x0d, "VOLUME OVERFLOW", "Invalid"},
+ {0x0e, "MISCOMPARE", "Invalid"},
+ {0x0f, "RESERVED", "Invalid"}
+};
+
+/* When Error_Code = 0x70 more detailed information is available:
+ * code, qualifier, description
+*/
+struct ASCQ
+{ /* ADDITIONAL SENSE CODE QUALIFIER */
+ unsigned int codequalifier;
+ char *description;
+};
+static struct ASCQ ascq_errmsg[74] = {
+ {0x0000, "No additional sense information"},
+ {0x0002, "End of Medium detected"},
+ {0x0005, "End of Data detected"},
+ {0x0400, "Logical unit not ready. Don't know why."},
+ {0x0401, "Logical unit is in process of becoming ready."},
+ {0x0403, "Logical unit not ready. Manual intervention required."},
+ {0x0500, "Logical unit does not respond to selection."},
+ {0x0700, "Multiple peripheral devices selected."},
+ {0x1100, "Unrecovered read error."},
+ {0x1101, "Read retries exhausted."},
+ {0x1501, "Mechanical positioning error."},
+ {0x1a00, "Parameter list length error."},
+ {0x2000, "Invalid command operation mode."},
+ {0x2400, "Invalid field in CDB (check field pointer)."},
+ {0x2500, "Logical unit not supported."},
+ {0x2600, "Invalid field in parameter list (check field pointer)."},
+ {0x2900, "Power on, reset, or BUS DEVICE RESET occurred."},
+ {0x2a01, "(MODE parameter changed.)"},
+ {0x2c00, "Command sequence error."},
+ {0x2c01, "(Too many windows specified."},
+ {0x2c02, "(Invalid combination of windows specified."},
+ {0x3700, "(Rounded parameter.)"},
+ {0x3900, "(Saving parameters not supported.)"},
+ {0x3a00, "Medium not present."},
+ {0x3b09, "(Read past end of medium.)"},
+ {0x3b0b, "(Position past end of medium.)"},
+ {0x3d00, "Invalid bits in IDENTIFY message."},
+ {0x4300, "Message error."},
+ {0x4500, "Select/Reselect failure."},
+ {0x4700, "(SCSI parity error)"},
+ {0x4800, "Initiator detected error message received."},
+ {0x4900, "Invalid message error."},
+ {0x4a00, "Command phase error."},
+ {0x4b00, "Data phase error."},
+ {0x5300, "(Media Load/Eject failed)"},
+ {0x6000, "Lamp failure"},
+ {0x6001, "(Shading Error)"},
+ {0x6002, "White adjustment error"},
+ {0x6010, "Reverse Side Lamp Failure"},
+ {0x6200, "Scan head positioning error"},
+ {0x6300, "Document Waiting Cancel"},
+ {0x8000, "(PSU overheat)"},
+ {0x8001, "(PSU 24V fuse down)"},
+ {0x8002, "(ADF 24V fuse down)"},
+ {0x8003, "(5V fuse down)"},
+ {0x8004, "(-12V fuse down)"},
+ {0x8100, "(ADF 24V power off)"},
+ {0x8101, "(Base 12V power off)"},
+ {0x8102, "(SCSI 5V power off)"},
+ {0x8103, "Lamp cover open (Lamp 24V power off)"},
+ {0x8104, "(-12V power off)"},
+ {0x8105, "(Endorser 6V power off)"},
+ {0x8106, "SCU 3.3V power down error"},
+ {0x8107, "RCU 3.3V power down error"},
+ {0x8108, "OIPU 3.3V power down error"},
+ {0x8200, "Memory Error (Bus error)"},
+ {0x8210, "Reverse-side memory error (Bus error)"},
+ {0x8300, "(Image data processing LSI error)"},
+ {0x8301, "(Interfac LSI error)"},
+ {0x8302, "(SCSI controller error)"},
+ {0x8303, "(Compression unit error)"},
+ {0x8304, "(Marker detect unit error)"},
+ {0x8400, "Endorser error"},
+ {0x8500, "(Origin Positioning error)"},
+ {0x8600, "Mechanical Time Out error (Pick Up Roller error)"},
+ {0x8700, "(Heater error)"},
+ {0x8800, "(Thermistor error)"},
+ {0x8900, "ADF cover open"},
+ {0x8901, "(ADF lift up)"},
+ {0x8902, "Document jam error for ADF"},
+ {0x8903, "Document misfeed for ADF"},
+ {0x8a00, "(Interlock open)"},
+ {0x8b00, "(Not enough memory)"},
+ {0x8c00, "Size detection failed"}
+};
+
+typedef struct sense_data
+{ /* HS2P_REQUEST_SENSE_DATA */
+ /* bit7:valid is 1 if information byte is valid,
+ bits6:0 error_code */
+ SANE_Byte error_code;
+
+ /* not used, set to 0 */
+ SANE_Byte segment_number;
+
+ /* bit7 file-mark (unused, set to 0),
+ bit6 EOM is 1 if end of document detected before completing scan
+ bit5 ILI (incorrect length indicator) is 1 when data length mismatch occurs on READ
+ bits3:0 sense_key indicates error conditions. */
+ SANE_Byte sense_key;
+
+ SANE_Byte information[4];
+
+ /* fixed at 6 */
+ SANE_Byte sense_length;
+
+ /* not used and set to 0 */
+ SANE_Byte command_specific_information[4];
+ SANE_Byte sense_code;
+ SANE_Byte sense_code_qualifier;
+} SENSE_DATA;
+
+/* page codes used with HS2P_SCSI_INQUIRY */
+#define HS2P_INQUIRY_STANDARD_PAGE_CODE 0x00
+#define HS2P_INQUIRY_VPD_PAGE_CODE 0xC0
+#define HS2P_INQUIRY_JIS_PAGE_CODE 0xF0
+
+/*
+ * The EVPD and Page Code are used in pair. When the EVPD bit is 0, INQUIRY data
+ * in the standard format is returned to the initiator. When the EVPD bit is 1,
+ * the EVPD information specified by each Page Code is returned in each Page Code
+ * data format.
+ *
+ * EVPD=0x00, Page_Code=0x00 => Standard Data Format
+ *
+ * EVPD=0x01, PAGE_CODE=0x00 => Return list of supported Page Codes
+ * EVPD=0x01, PAGE_CODE=0x01~0x7F => Not Supported
+ * EVPD=0x01, PAGE_CODE=0x80~0x82 => Not Supported
+ * EVPD=0x01, PAGE_CODE=0x83~0xBF => Reserved
+ * EVPD=0x01, PAGE_CODE=0xC0 => RICOH Scanner VPD information
+ * EVPD=0x01, PAGE_CODE=0xF0 => JIS Version VPD information
+*/
+struct inquiry_standard_data
+{
+ /* bits7-5 peripheral qualifier
+ * bits4-0 peripheral device
+ * Peripheral Qualifier and Peripheral Devide Type are not supported on logical unit
+ * Therefore LUN=0 and this field indicates scanner and is set to 0x06
+ * When LUN!=0 this field becomes 0x1F and means undefined data
+ */
+ SANE_Byte devtype; /* must be 0x06 */
+
+ /* bit7: repaceable media bit is set to 0
+ * bits6-1: reserved
+ * bit0: EVPD
+ */
+ SANE_Byte rmb_evpd;
+
+ /* bits7-6: ISO Version is set to 0
+ * bits5-3: ECMA Version is set to 0
+ * bits2-0: ANSI Version is set to 2
+ */
+ SANE_Byte version;
+
+ /* bit7: AENC (asynchronous event notification capability) is set to 0
+ * bit6: TrmIOP (terminate I/O process) is set to 0
+ * bits5-4: reserved
+ * bits3-0: Response Data Format is set to 2
+ */
+ SANE_Byte response_data_format;
+
+ /* Additional Length indicate number of bytes which follows, set to 31
+ */
+ SANE_Byte length;
+
+ SANE_Byte reserved[2];
+
+ /* bit7: RelAdr (relative addressing) is set to 0
+ * bit6: Wbus32 is set to 0
+ * bit5: Wbus16 is set to 0
+ * bit4: Sync is set to 0
+ * bit3: Linked is set to 0
+ * bit2: reserved
+ * bit1: CmdQue is set to 0
+ * bit0: SftRe is set to 0
+ * Sync is set to 1 with this scanner to support synchronous data transfer
+ * When DIPSW2 is on, Sync is set to 0 for asynchronous data transfer
+ */
+ SANE_Byte byte7;
+
+ SANE_Byte vendor[8]; /* vendor_id="RICOH " */
+ SANE_Byte product[16]; /* product_id="IS450 " */
+ SANE_Byte revision[4]; /* product_revision_level="xRxx" where x indicate firmware version number */
+};
+
+/* VPD Information [EVPD=0x01, PageCode=0xC0] */
+struct inquiry_vpd_data
+{
+ SANE_Byte devtype; /* bits7-5: Peripheral Qualifier
+ * bits4-0: Peripheral Device Type */
+ SANE_Byte pagecode; /* Page Code => 0xC0 */
+ SANE_Byte byte2; /* Reserved */
+ SANE_Byte pagelength; /* Page Length => 12 (0x0C) */
+ SANE_Byte adf_id; /* ADF Identification
+ * 0: No ADF is mounted
+ * 1: Single sided ADF is mounted
+ * 2: Double sided ADF is mounted
+ * 3: ARDF is mounted. (Reverse double side scanning available)
+ * 4: Reserved
+ * It should be 1 or 2 with this scanner.
+ */
+ SANE_Byte end_id; /* Endorser Identification
+ * 0: No endorser
+ * 1: Endorser mounted
+ * 2: Reserved
+ * It should be 0 or 1 with this scanner
+ */
+ SANE_Byte ipu_id; /* Image Processing Unit Identification
+ * bits 7:2 Reserved
+ * bit 1 0:Extended board not mounted
+ * 1:Extended board is mounted
+ * bit 0 0:IPU is not mounted
+ * 1:IPU is mounted
+ * It should always be 0 with this scanner
+ */
+ SANE_Byte imagecomposition; /* indicates supported image data type.
+ * This is set to 0x37
+ * bit0 => Line art supported ? 1:0
+ * bit1 => Dither supported ? 1:0
+ * bit2 => Error Diffusion supported ? 1:0
+ * bit3 => Color supported ? 1:0
+ * bit4 => 4bits gray scale supported ? 1:0
+ * bit5 => 5-8bit gray scale supported ? 1:0
+ * bit6 => 5-8bit gray scale supported ? 1:0
+ * bit7 => Reserved
+ */
+ SANE_Byte imagedataprocessing[2]; /* Image Data Processing Method
+ * IPU installed ? 0x18 : 0x00
+ * Byte8 => White Framing ? 1:0
+ * Byte9 => Black Framing ? 1:0
+ * Byte10 => Edge Extraction ? 1:0
+ * Byte11 => Noise Removal ? 1:0
+ * Byte12 => Smoothing ? 1:0
+ * Byte13 => Line Bolding ? 0:1
+ * Byte14 => Reserved
+ * Byte15 => Reserved
+ */
+ SANE_Byte compression; /* Compression Method is set to 0x00
+ * bit0 => MH supported ? 1:0
+ * bit1 => MR supported ? 1:0
+ * bit2 => MMR supported ? 1:0
+ * bit3 => MH (byte boundary) supported ? 1:0
+ * bit4 => Reserved
+ */
+ SANE_Byte markerrecognition; /* Marker Recognition Method is set to 0x00
+ * bit0 => Marker Recognition supported ? 1:0
+ * bits1-7 => Reserved
+ */
+ SANE_Byte sizerecognition; /* Size Detection
+ * bit0 => Size Detection Supported ? 1:0
+ * bits1-7 => Reserved
+ */
+ SANE_Byte byte13; /* Reserved */
+ SANE_Byte xmaxoutputpixels[2]; /* X Maximum Output Pixel is set to 4960 (0x1360)
+ * indicates maximum number of pixels in the main
+ * scanning direction that can be output by scanner
+ */
+
+};
+
+struct inquiry_jis_data
+{ /* JIS INFORMATION VPD_IDENTIFIER_F0H */
+ SANE_Byte devtype; /* 7-5: peripheral qualifier, 4-0: peripheral device type */
+ SANE_Byte pagecode;
+ SANE_Byte jisversion;
+ SANE_Byte reserved1;
+ SANE_Byte alloclen; /* page length: Set to 25 (19H) */
+ struct
+ {
+ SANE_Byte x[2]; /* Basic X Resolution: Set to 400 (01H,90H) */
+ SANE_Byte y[2]; /* Basic Y Resolution: Set to 400 (01H,90H) */
+ } BasicRes;
+ SANE_Byte resolutionstep; /* 7-4: xstep, 3-0 ystep: Both set to 1 (11H) */
+ struct
+ {
+ SANE_Byte x[2]; /* Maximum X resolution: Set to 800 (03H,20H) */
+ SANE_Byte y[2]; /* Maximum Y resolution: Set to 800 (03H,20H) */
+ } MaxRes;
+ struct
+ {
+ SANE_Byte x[2]; /* Minimum X resolution: Set to 100 (00H,64H) */
+ SANE_Byte y[2]; /* Minimum Y resolution */
+ } MinRes;
+ SANE_Byte standardres[2]; /* Standard Resolution: bits 7-0:
+ * byte18: 60, 75,100,120,150,160,180, 200
+ * byte19: 240,300,320,400,480,600,800,1200
+ */
+ struct
+ {
+ SANE_Byte width[4]; /* in pixels based on basic resolution. Set to 4787 (12B3H) */
+ SANE_Byte length[4]; /* maximum number of scan lines based on basic resolution. Set to 6803 (1A93H) */
+ } Window;
+ SANE_Byte functions; /* This is set to 0EH: 0001110
+ * bit0: data overflow possible
+ * bit1: line art support
+ * bit2: dither support
+ * bit3: gray scale support
+ * bits7-4: reserved
+ */
+ SANE_Byte reserved2;
+};
+
+
+
+#define SMS_SP 0x01 /* Mask for Bit0 */
+#define SMS_PF 0x10 /* Mask for Bit4 */
+typedef struct scsi_mode_select_cmd
+{
+ SANE_Byte opcode; /* 15H */
+ SANE_Byte byte1; /* 7-5:LUN; 4:PF; 2:Reserved; 1:SP
+ * Save Page Bit must be 0 since pages cannot be saved
+ * Page Format Bit must be 1 */
+ SANE_Byte reserved[2];
+ SANE_Byte len; /* Parameter List Length */
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+} SELECT;
+
+/* MODE SELECT PARAMETERS:
+ * 0-n Mode Parameter Header
+ * 0-n mode Block Descriptor (not used)
+ * 0-n mode Page
+*/
+typedef struct scsi_mode_parameter_header
+{
+ SANE_Byte data_len; /* Mode Data Length NOT USED so must be 0 */
+ SANE_Byte medium_type; /* Medium Type NOT USED so must be 0 */
+ SANE_Byte dev_spec; /* Device Specific Parameter NOT USED so must be 0 */
+ SANE_Byte blk_desc_len; /* Block Descriptor Length is set to 0 */
+} MPHdr;
+
+typedef struct page
+{
+ SANE_Byte code; /* 7:PS; 6:Reserved; 5-0:Page Code */
+ SANE_Byte len; /* set to 14 when MPC=02H and 6 otherwise */
+ SANE_Byte parameter[14]; /* either 14 or 6, so let's allow room for 14 */
+} MPP; /* Mode Page Parameters */
+typedef struct mode_pages
+{
+ MPHdr hdr; /* Mode Page Header */
+ MPP page; /* Mode Page Parameters */
+} MP;
+ /* MODE PAGE CODES (MPC) */
+ /* 00H Reserved (Vendor Unique) */
+ /* 01H Reserved */
+#define PAGE_CODE_CONNECTION 0x02 /* 02H Disconnect/Reconnect Parameters */
+#define PAGE_CODE_SCANNING_MEASUREMENTS 0x03 /* 03H Scanning Measurement Parameters */
+ /* 04H-08H Reserved */
+ /* 09H-0AH Reserved (Not supported) */
+ /* 0BH-1FH Reserved */
+#define PAGE_CODE_WHITE_BALANCE 0x20 /* 20H White Balance */
+ /* 21H Reserved (Vendor Unique) */
+#define PAGE_CODE_LAMP_TIMER_SET 0x22 /* 22H Lamp Timer Set */
+#define PAGE_CODE_SCANNING_SPEED 0x23 /* 23H Reserved (Scanning speed select) */
+ /* 24H Reserved (Vendor Unique) */
+ /* 25H Reserved (Vendor Unique) */
+#define PAGE_CODE_ADF_CONTROL 0x26 /* 26H ADF Control */
+#define PAGE_CODE_ENDORSER_CONTROL 0x27 /* 27H Endorser Control */
+ /* 28H Reserved (Marker Area Data Processing) */
+ /* 29H-2AH Reserved (Vendor Unique) */
+#define PAGE_CODE_SCAN_WAIT_MODE 0x2B /* 2BH Scan Wait Mode (Medium Wait Mode) */
+ /* 2CH-3DH Reserved (Vendor Unique) */
+#define PAGE_CODE_SERVICE_MODE_SELECT 0x3E /* 3EH Service Mode Select */
+ /* 3FH Reserved (Not Supported) */
+
+typedef struct mode_page_connect
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0: 02H */
+ SANE_Byte len; /* Parameter Length 0EH */
+ SANE_Byte buffer_full_ratio; /* Ignored */
+ SANE_Byte buffer_empty_ratio; /* Ignored */
+ SANE_Byte bus_inactive_limit[2]; /* Ignored */
+ SANE_Byte disconnect_time_limit[2]; /* indicates minimum time to disconnect SCSI bus until reconnection.
+ * It is expressed in 100msec increments; i.e. "1" for 100msec, "2" for 200msec
+ * The maximum time is 2sec */
+ SANE_Byte connect_time_limit[2]; /* Ignored */
+ SANE_Byte maximum_burst_size[2]; /* expressed in 512 increments, i.e. "1" for 512 bytes, "2" for 1024 bytes
+ * "0" indicates unlimited amount of data */
+ SANE_Byte dtdc; /* 7-2:Reserved; 1-0:DTDC indicates limitations of disconnection (bit1,bit0):
+ * 00 (DEFAULT) Controlled by the other field in this page
+ * 01 Once the command data transfer starts, the target never disconnects until
+ * the whole data transfer completes
+ * 10 Reserved
+ * 11 Once the command data transfer starts, the target never disconnects until
+ * the completion of the command
+ */
+ SANE_Byte reserved[3];
+} MP_CXN;
+
+/* 1 inch = 6 picas = 72 points = 25.4 mm */
+#define DEFAULT_MUD 1200 /* WHY ? */
+/* BASIC MEASUREMENT UNIT
+ * 00H INCH
+ * 01H MILLIMETER
+ * 02H POINT
+ * 03H-FFH Reserved
+*/
+enum BMU
+{ INCHES = 0, MILLIMETERS, POINTS }; /* Basic Measurement Unit */
+
+typedef struct mode_page_scanning_measurement
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (03H) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte bmu; /* Basic Measurement Unit */
+ SANE_Byte reserved0;
+ SANE_Byte mud[2]; /* Measurement Unit Divisor
+ * produces an error if 0
+ * mud is fixed to 1 for millimeter or point
+ * point is default when scanner powers on */
+ SANE_Byte reserved1[2];
+} MP_SMU; /* Scanning Measurement Units */
+
+typedef struct mode_page_white_balance
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (03H) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte white_balance; /* "0" selects relative white mode (DEFAULT when power on)
+ * "1" selects absolute white mode */
+ SANE_Byte reserved[5];
+} MP_WhiteBal; /* White Balance */
+
+typedef struct mode_page_lamp_timer_set
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (22H) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte time_on; /* indicates the time of lamp turned on */
+ SANE_Byte ignored[5];
+} MP_LampTimer; /* Lamp Timer Set (Not supported ) */
+
+typedef struct mode_page_adf_control
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (26H) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte adf_control; /* 7-2:Reserved; 1-0:ADF selection:
+ * 00H Book Mode (DEFAULT when power on)
+ * 01H Simplex ADF
+ * 02H Duplex ADF
+ * 03H-FFH Reserved */
+ SANE_Byte adf_mode_control; /* 7-3:Reserved; 2:Prefeed Mode Validity 1-0:Ignored
+ * Prefeed Mode "0" means invalid, "1" means valid */
+ SANE_Byte medium_wait_timer; /* indicates time for scanner to wait for media. Scanner
+ * will send CHECK on timeout. NOT SUPPORTED */
+ SANE_Byte ignored[3];
+} MP_ADF; /* ADF Control */
+
+typedef struct mode_page_endorser_control
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (27H) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte endorser_control; /* 7-3:Reserved; 2-0:Endorser Control:
+ * 0H Disable Endorser (DEFAULT)
+ * 1H Enable Endorser
+ * 3H-7H Reserved */
+ SANE_Byte ignored[5];
+} MP_EndCtrl; /* Endorser Control */
+
+typedef struct mode_page_scan_wait
+{
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (2BH) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte swm; /* 7-1:Reserved; 0:Scan Wait Mode
+ * 0H Disable Medium wait mode
+ * 1H Enable Medium wait mode
+ * In Medium wait mode, when SCAN, READ, or LOAD (in ADF mode) is issued,
+ * the scanner waits until start button is pressed on operation panel
+ * When abort button is pressed, the command is cancelled
+ * In ADF mode, when there are no originals on ADF, CHECK condition is
+ * not given unless start button is pressed. */
+ SANE_Byte ignored[5];
+} MP_SWM; /* Scan Wait */
+
+typedef struct mode_page_service
+{ /* Selectable when Send Diagnostic command is performed */
+ MPHdr hdr; /* Mode Page Header: 4 bytes */
+ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (3EH) */
+ SANE_Byte len; /* Parameter Length (06H) */
+ SANE_Byte service; /* 7-1:Reserved; 0:Service Mode
+ * "0" selects Self Diagnostics mode (DEFAULT when power on )
+ * "1" selects Optical Adjustment mode */
+ SANE_Byte ignored[5];
+} MP_SRV; /* Service */
+
+typedef struct scsi_mode_sense_cmd
+{
+ SANE_Byte opcode; /* 1AH */
+ SANE_Byte dbd; /* 7-5:LUN; 4:Reserved; 3:DBD (Disable Block Desciption) set to "0"; 2-0:Reserved */
+ SANE_Byte pc; /* 7-6:PC; 5-0:Page Code
+ * PC field indicates the type of data to be returned (bit7,bit6):
+ * 00 Current Value (THIS IS THE ONLY VALUE WHICH WORKS!)
+ * 01 Changeable Value
+ * 10 Default Value
+ * 11 Saved Value
+ *
+ * Page Code indicates requested page. (See PAGE_CODE defines) */
+ SANE_Byte reserved;
+ SANE_Byte len; /* Allocation length */
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+} SENSE;
+/* MODE SENSE DATA FORMAT --
+ * The format of Sense Data to be returned is Mode Parameter Header + Page
+ * see struct scsi_mode_parameter_header
+ * struct mode_pages
+*/
+
+/* 1-3-8 SCAN command */
+typedef struct scsi_start_scan_cmd
+{
+ SANE_Byte opcode; /* 1BH */
+ SANE_Byte byte1; /* 7-5:LUN; 4-0:Reserved */
+ SANE_Byte page_code;
+ SANE_Byte reserved;
+ SANE_Byte len; /* Transfer Length
+ * Length of Window List in bytes
+ * Since scanner supports up to 2 windows, len is 1 or 2
+ */
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+} START_SCAN;
+
+/* 1-3-9 RECEIVE DIAGNOSTIC
+ * 1-3-10 SEND DIAGNOSTIC */
+
+/* BinaryFilter Byte
+ * bit7: Noise Removal '1':removal
+ * bit6: Smoothing '1':smoothing
+ * bits5-2: ignored
+ * bits1-0: Noise Removal Matrix
+ * 00:3x3 01:4x4
+ * 10:5x5 11:Reserved
+*/
+struct val_id
+{
+ SANE_Byte val;
+ SANE_Byte id;
+};
+static SANE_String_Const noisematrix_list[] = {
+ "None", "3x3", "4x4", "5x5", NULL
+};
+struct val_id noisematrix[] = {
+ {0x03, 0}, /* dummy <reserved> value for "None" */
+ {0x00, 1},
+ {0x01, 2},
+ {0x02, 3}
+};
+static SANE_String_Const grayfilter_list[] = {
+ "none", "averaging", "MTF correction", NULL
+};
+struct val_id grayfilter[] = {
+ {0x00, 0},
+ {0x01, 1},
+ {0x03, 2}
+};
+
+static SANE_String_Const paddingtype_list[] = {
+ "Pad with 0's to byte boundary",
+ "Pad with 1's to byte boundary",
+ "Truncate to byte boundary",
+ NULL
+};
+enum paddingtypes
+{ PAD_WITH_ZEROS = 0x01, PAD_WITH_ONES, TRUNCATE };
+struct val_id paddingtype[] = {
+ {PAD_WITH_ZEROS, 0},
+ {PAD_WITH_ONES, 1},
+ {TRUNCATE, 2}
+};
+
+#define NPADDINGTYPES 3
+#define PADDINGTYPE_DEFAULT 2
+static SANE_String_Const auto_separation_list[] = {
+ "Off", "On", "User", NULL
+};
+struct val_id auto_separation[] = {
+ {0x00, 0},
+ {0x01, 1},
+ {0x80, 2}
+};
+static SANE_String_Const auto_binarization_list[] = {
+ "Off",
+ "On",
+ "Enhancement of light characters",
+ "Removal of background color",
+ "User",
+ NULL
+};
+struct val_id auto_binarization[] = {
+ {0x00, 0},
+ {0x01, 1},
+ {0x02, 2},
+ {0x03, 3},
+ {0x80, 4}
+};
+enum imagecomposition
+{ LINEART = 0x00, HALFTONE, GRAYSCALE };
+enum halftonecode
+{ DITHER = 0x02, ERROR_DIFFUSION };
+static SANE_String_Const halftone_code[] = {
+ "Dither", "Error Diffusion", NULL
+};
+static SANE_String_Const halftone_pattern_list[] = {
+ "8x4, 45 degree",
+ "6x6, 90 degree",
+ "4x4, spiral",
+ "8x8, 90 degree",
+ "70 lines",
+ "95 lines",
+ "180 lines",
+ "16x8, 45 degree",
+ "16x16, 90 degree",
+ "8x8, Bayer",
+ "User #1",
+ "User #2",
+ NULL
+};
+struct val_id halftone[] = {
+ {0x01, 1},
+ {0x02, 2},
+ {0x03, 3},
+ {0x04, 4},
+ {0x05, 5},
+ {0x06, 6},
+ {0x07, 7},
+ {0x08, 9},
+ {0x09, 9},
+ {0x0A, 10},
+ {0x80, 11},
+ {0x81, 12}
+};
+
+#if 0
+static struct
+{
+ SANE_Byte code;
+ char *type;
+} compression_types[] =
+{
+ {
+ 0x00, "No compression"},
+ {
+ 0x01, "CCITT G3, 1-dimensional (MH)"},
+ {
+ 0x02, "CCITT G3, 2-dimensional (MR)"},
+ {
+ 0x03, "CCITT G4, 2-dimensional (MMR)"},
+ /* 04H-0FH Reserved
+ * 10H Reserved (not supported)
+ * 11H-7FH Reserved
+ */
+ {
+ 0x80, "CCITT G3, 1-dimensional (MH) Padding with 0's to byte boundary"}
+ /* 80H-FFH Reserved (Vendor Unique) */
+};
+static struct
+{
+ SANE_Byte code;
+ char *argument;
+} compression_argument[] =
+{
+ /* 00H Reserved */
+ /* 01H Reserved */
+ {
+ 0x02, "K factor-0~255"}
+ /* 03H Reserved */
+ /* 04H-0FH Reserved */
+ /* 10H Reserved */
+ /* 11H-7FH Reserved */
+ /* 80H Reserved */
+ /* 80H-FFH Reserved */
+};
+#endif
+#define GAMMA_NORMAL 0x00
+#define GAMMA_SOFT 0x01
+#define GAMMA_SHARP 0x02
+#define GAMMA_LINEAR 0x03
+#define GAMMA_USER 0x08
+/* 04H-07H Reserved */
+/* 09H-0FH Reserved */
+static SANE_String gamma_list[6] = {
+ "Normal", "Soft", "Sharp", "Linear", "User", NULL
+};
+
+/* 1-3-11 SET WINDOW command */
+
+struct window_section
+{ /* 32 bytes */
+ SANE_Byte sef; /*byte1 7-2:ignored 1:SEF '0'-invalid section; '1'-valid section */
+ SANE_Byte ignored0;
+ SANE_Byte ulx[4];
+ SANE_Byte uly[4];
+ SANE_Byte width[4];
+ SANE_Byte length[4];
+ SANE_Byte binary_filtering;
+ SANE_Byte ignored1;
+ SANE_Byte threshold;
+ SANE_Byte ignored2;
+ SANE_Byte image_composition;
+ SANE_Byte halftone_id;
+ SANE_Byte halftone_code;
+ SANE_Byte ignored3[7];
+};
+/* 1-3-11 SET WINDOW COMMAND
+ * Byte0: 24H
+ * Byte1: 7-5: LUN; 4-0: Reserved
+ * Byte2-5: Reserved
+ * Byte6-8: Transfer Length
+ * Byte9: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link
+ *
+ * Transfer length indicates the byte length of Window Parameters (Set Window Data Header +
+ * Window Descriptor Bytes transferred from the initiator in the DATA OUT PHASE
+ * The scanner supports 2 windows, so Transfer Length is 648 bytes:
+ * Set Window Header 8 bytes + Window Descriptor Bytes 640 (320*2) bytes).
+ * If data length is longer than 648 bytes only the first 648 bytes are valid, The remainng data is ignored.
+ * If data length is shorter than 648 only the specified byte length is valid data.
+ *
+ *
+ * WINDOW DATA HEADER
+ * Byte0-5: Reserved
+ * Byte6-7: Window Descriptor Length (WDL)
+ * WDL indicates the number of bytes of one Window Descriptor Bytes which follows.
+ * In this scanner, this value is 640 since it supports 2 windows.
+ *
+ * WINDOW DESCRIPTOR BYTES
+*/
+#define HS2P_WINDOW_DATA_SIZE 640
+struct hs2p_window_data
+{ /* HS2P_WINDOW_DATA_FORMAT */
+ SANE_Byte window_id; /* 0: Window Identifier */
+ SANE_Byte auto_bit; /* 1: 1-1:Reserved; 0:Auto */
+ SANE_Byte xres[2]; /* 2-3: X-Axis Resolution 100-800dpi in 1dpi steps */
+ SANE_Byte yres[2]; /* 4-5: Y-Axis Resolution 100-800dpi in 1dpi steps */
+ SANE_Byte ulx[4]; /* 6-9: X-Axis Upper Left */
+ SANE_Byte uly[4]; /* 10-13: Y-Axis Upper Left */
+ SANE_Byte width[4]; /* 14-17: Window Width */
+ SANE_Byte length[4]; /* 18-21: Window Length */
+ SANE_Byte brightness; /* 22: Brightness [0-255] dark-light 0 means default value of 128 */
+ SANE_Byte threshold; /* 23: Threshold [0-255] 0 means default value of 128 */
+ SANE_Byte contrast; /* 24: Contrast [0-255] low-high 0 means default value of 128 */
+ SANE_Byte image_composition; /* 25: Image Composition
+ * 00H Lineart
+ * 01H Dithered Halftone
+ * 02H Gray scale
+ */
+ SANE_Byte bpp; /* 26: Bits Per Pixel */
+ SANE_Byte halftone_code; /* 27: Halftone Code
+ * 00H-01H Reserved
+ * 02H Dither (partial Dot)
+ * 03H Error Diffusion
+ * 04H-07H Reserved
+ */
+ SANE_Byte halftone_id; /* 28: Halftone ID
+ * 00H Reserved
+ * 01H 8x4, 45 degree
+ * 02H 6x6, 90 degree
+ * 03H 4x4, Spiral
+ * 04H 8x8, 90 degree
+ * 05H 70 lines
+ * 06H 95 lines
+ * 07H 180 lines
+ * 08H 16x8, 45 degree
+ * 09H 16x16, 90 degree
+ * 0AH 8x8, Bayer
+ * 0Bh-7FH Reserved
+ * 80H Download #1
+ * 81H Download #2
+ * 82H-FFH Reserved
+ */
+ SANE_Byte byte29; /* 29: 7: RIF (Reverse Image Format) bit inversion
+ * Image Composition field must be lineart or dithered halftone
+ * RIF=0: White=0 Black=1
+ * RIF=1: White=1 Black=0
+ * 6-3: Reserved;
+ * 2-0: Padding Type:
+ * 00H Reserved
+ * 01H Pad with 0's to byte boundary
+ * 02H Pad with 1's to byte boundary
+ * 03H Truncate to byte boundary
+ * 04H-FFH Reserved
+ */
+ SANE_Byte bit_ordering[2]; /* 30-31: Bit Ordering: Default 0xF8
+ * 0: 0=>output from bit0 of each byte; 1=>output from bit7
+ * 1: 0=>output from LSB; 1=>output from MSB
+ * 2: 0=>unpacked 4 bits gray; 1=>Packed 4 bits gray
+ * 3: 1=>Bits arrangment from LSB in grayscale; 0=>from MSB
+ * 4-6: reserved
+ * 7: 1=>Mirroring; 0=>Normal output
+ * 8-15: reserved
+ */
+ SANE_Byte compression_type; /* 32: Compression Type: Unsupported in IS450 */
+ SANE_Byte compression_arg; /* 33: Compression Argument: Unsupported in IS450 */
+ SANE_Byte reserved2[6]; /* 34-39: Reserved */
+ SANE_Byte ignored1; /* 40: Ignored */
+ SANE_Byte ignored2; /* 41: Ignored */
+ SANE_Byte byte42; /* 42: 7: MRIF: Grayscale Reverse Image Format
+ * MRIF=0: White=0 Black=1
+ * MRIF=1: White=1 Black=0
+ * 6-4: Filtering: for Grayscale
+ * 000 No filter
+ * 001 Averaging
+ * 010 Reserved
+ * 011 MTF Correction
+ * 100 Reserved
+ * 110 Reserved
+ * 111 Reserved
+ * 3-0: Gamma ID
+ * 00H Normal
+ * 01H Soft
+ * 02H Sharp
+ * 03H Linear
+ * 04H-07H Reserved
+ * 08H Download table
+ * 09H-0FH Reserved
+ */
+ SANE_Byte ignored3; /* 43: Ignored */
+ SANE_Byte ignored4; /* 44: Ignored */
+ SANE_Byte binary_filtering; /* 45: Binary Filtering
+ * 0-1: Noise Removal Matrix:
+ * 00: 3x3
+ * 01: 4x4
+ * 10: 5x5
+ * 11: Reserved
+ * 5-2: Ignored
+ * 6: Smoothing Flag
+ * 7: Noise Removal Flag
+ *
+ * Smoothing and Noise removal can be set when option IPU is installed
+ * Setting is ignored for reverse side because optional IPU is not valid
+ * for reverse side scanning
+ */
+ /*
+ * The following is only available when IPU is installed:
+ * SECTION, Automatic Separation, Automatic Binarization
+ * 46-319 is ignored for Window 2
+ */
+ SANE_Byte ignored5; /* 46: Ignored */
+ SANE_Byte ignored6; /* 47: Ignored */
+ SANE_Byte automatic_separation; /* 48: Automatic Separation
+ * 00H OFF
+ * 01H Default
+ * 02H-7FH Reserved
+ * 80H Download table
+ * 91H-FFH Reserved
+ */
+ SANE_Byte ignored7; /* 49: Ignored */
+ SANE_Byte automatic_binarization; /* 50: Automatic Binarization
+ * 00H OFF
+ * 01H Default
+ * 02H Enhancement of light characters
+ * 03H Removal of background color
+ * 04H-7FH Reserved
+ * 80H Download table
+ * 81H-FFH Reserved
+ */
+ SANE_Byte ignored8[13]; /* 51-63: Ignored */
+ struct window_section sec[8]; /* Each window can have multiple sections, each of 32 bytes long
+ * 53-319: = 256 bytes = 8 sections of 32 bytes
+ * IS450 supports up to 4 sections,
+ * IS420 supports up to 6 sections
+ */
+};
+struct set_window_cmd
+{
+ SANE_Byte opcode; /* 24H */
+ SANE_Byte byte2; /* 7-5:LUN 4-0:Reserve */
+ SANE_Byte reserved[4]; /* Reserved */
+ SANE_Byte len[3]; /* Transfer Length */
+ SANE_Byte control; /* 76543210
+ * XX Vendor Unique
+ * XXXX Reserved
+ * X Flag
+ * X Link
+ */
+};
+struct set_window_data_hdr
+{
+ SANE_Byte reserved[6];
+ SANE_Byte len[2];
+};
+typedef struct set_window_data
+{
+ struct set_window_data_hdr hdr;
+ struct hs2p_window_data data[2];
+} SWD;
+
+/* 1-3-12 GET WINDOW command */
+struct get_window_cmd
+{
+ SANE_Byte opcode;
+ SANE_Byte byte1; /* 7-5: LUN; * 4-1:Reserved; * 0:Single bit is 0 */
+ SANE_Byte reserved[3];
+ SANE_Byte win_id; /* Window ID is either 0 or 1 */
+ SANE_Byte len[3]; /* Transfer Length */
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+};
+/* The data format to be returned is Get Window Data header + Window Descriptor Bytes
+ * The format of Window Descriptor Bytes is the same as that for SET WINDOW
+*/
+struct get_window_data_hdr
+{
+ SANE_Byte data_len[2]; /* Window Data Length indicates byte len of data which follows less its own 2 bytes */
+ SANE_Byte reserved[4];
+ SANE_Byte desc_len[2]; /* Window Descriptor Length indicates byte length of one Window Descriptor which is 640 */
+};
+typedef struct get_window_data
+{
+ struct get_window_data_hdr hdr;
+ struct hs2p_window_data data[2];
+} GWD;
+
+/* READ/SEND DATA TYPE CODES */
+/* DATA TYPE CODES (DTC): */
+#define DATA_TYPE_IMAGE 0x00
+/* 01H Reserved (Vendor Unique) */
+#define DATA_TYPE_HALFTONE 0x02
+#define DATA_TYPE_GAMMA 0x03
+/*04H-7FH Reserved */
+#define DATA_TYPE_ENDORSER 0x80
+#define DATA_TYPE_SIZE 0x81
+/* 82H Reserved */
+/* 83H Reserved (Vendor Unique) */
+#define DATA_TYPE_PAGE_LEN 0x84
+#define DATA_TYPE_MAINTENANCE 0x85
+#define DATA_TYPE_ADF_STATUS 0x86
+/* 87H Reserved (Skew Data) */
+/* 88H-91H Reserved (Vendor Unique) */
+/* 92H Reserved (Scanner Extension I/O Access) */
+/* 93H Reserved (Vendor Unique) */
+/* 94H-FFH Reserved (Vendor Unique) */
+#define DATA_TYPE_EOL -1 /* va_end */
+
+/* DATA TYPE QUALIFIER CODES when DTC=93H */
+#define DTQ 0x00 /* ignored */
+#define DTQ_AUTO_PHOTOLETTER 0x00 /* default */
+#define DTQ_DYNAMIC_THRESHOLDING 0x01 /* default */
+#define DTQ_LIGHT_CHARS_ENHANCEMENT 0x02
+#define DTQ_BACKGROUND_REMOVAL 0x03
+/* 04H-7FH Reserved */
+#define DTQ_AUTO_PHOTOLETTER_DOWNLOAD_TABLE 0x80
+#define DTQ_DYNAMIC_THRESHOLD_DOWNLOAD_TABLE 0x81
+/* 82H-FFH Reserved */
+
+/* 1-3-13 READ command */
+/* 1-3-14 SEND command */
+struct scsi_rs_scanner_cmd
+{
+ SANE_Byte opcode; /* READ=28H SEND=2AH */
+ SANE_Byte byte1; /* 7-5:LUN; 4-0:Reserved */
+ SANE_Byte dtc; /* Data Type Code: See DTC DEFINES above */
+ SANE_Byte reserved;
+ SANE_Byte dtq[2]; /* Data Type Qualifier valid only for DTC 02H,03H,93H */
+ SANE_Byte len[3]; /* Transfer Length */
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+};
+/*
+ * Data Format for Image Data
+ * Non-compressed: {first_line, second_line, ... nth_line}
+ * MH/MR Compression: {EOL, 1st_line_compressed, EOL, 2nd_line_compressed,..., EOL, last_line_compressed, EOL,EOL,EOL,EOL,EOL,EOL
+ * where EOL = 000000000001
+ * MMR Compression: = {1st_line_compressed, 2nd_line_compressed,...,last_line_compressed, EOL,EOL}
+ *
+ * Normal Binary Output: MSB-LSB 1stbytes,2nd,3rd...Last
+ * Mirror Binary Output: MSB-LSB Last,...2nd,1st
+ *
+ * Normal Gray Output MSB-LSB: 1st,2nd,3rd...Last
+ * 4 bit/pixel gray: [32103210]
+ * 8 bit/pixel gray: [76543210]
+ * Mirror Gray Output MSB-LSB Last,...2nd,1st
+ *
+ *
+ * HALFTONE MASK DATA: 1byte(row,col) ={2,3,4,6,8,16}
+ * (r0,c0), (r0,c1), (r0,c2)...(r1,c0),(r1,c2)...(rn,cn)
+ *
+ * GAMMA FUNCTION TABLE Output (D) vs. Input (I)(0,0)=(Black,Black) (255,255)=(White,White)
+ * The number of gray scale M = 8
+ * 2^8 = 256 total table data
+ * D0 = D(I=0), D1=D(I=1)...D255=D(I=255)
+ * DATA= [1st byte ID],[2nd byte M],[D0],[D1],...[D255]
+ *
+ * ENDORSER DATA: 1st_char, 2nd_char,...last_char
+ *
+ * SIZE DATA: 1byte: 4bits-Start Position; 4bits-Width Info
+ *
+ * PAGE LENGTH: 5bytes: 1st byte is MSB, Last byte is LSB
+*/
+
+typedef struct maintenance_data
+{
+ SANE_Byte nregx_adf; /* number of registers of main-scanning in ADF mode */
+ SANE_Byte nregy_adf; /* number of registers of sub-scanning in ADF mode */
+ SANE_Byte nregx_book; /* number of registers of main-scanning in Book mode */
+ SANE_Byte nregy_book; /* number of registers of sub-scanning in Book mode */
+ SANE_Byte nscans_adf[4]; /* Number of scanned pages in ADF mode */
+ SANE_Byte nscans_book[4]; /* Number of scanned pages in Book mode */
+ SANE_Byte lamp_time[4]; /* Lamp Time */
+ SANE_Byte eo_odd; /* Adjustment data of E/O balance in black level (ODD) */
+ SANE_Byte eo_even; /* Adjustment data of E/O balance in black level (EVEN) */
+ SANE_Byte black_level_odd; /* The adjustment data in black level (ODD) */
+ SANE_Byte black_level_even; /* The adjustment data in black level (EVEN) */
+ SANE_Byte white_level_odd[2]; /* The adjustment data in white level (ODD) */
+ SANE_Byte white_level_even[2]; /* The adjustment data in white level (EVEN) */
+ SANE_Byte first_adj_white_odd[2]; /* First adjustment data in white level (ODD) */
+ SANE_Byte first_adj_white_even[2]; /* First adjustment data in white level (EVEN) */
+ SANE_Byte density_adj; /* Density adjustment */
+ SANE_Byte nregx_reverse; /* The number of registers of main-scanning of the reverse-side ADF */
+ SANE_Byte nregy_reverse; /* The number of registers of sub-scanning of the reverse-side ADF */
+ SANE_Byte nscans_reverse_adf[4]; /* Number of scanned pages of the reverse side ADF */
+ SANE_Byte reverse_time[4]; /* The period of lamp turn on of the reverse side */
+ SANE_Byte nchars[4]; /* The number of endorser characters */
+ SANE_Byte reserved0;
+ SANE_Byte reserved1;
+ SANE_Byte reserved2;
+ SANE_Byte zero[2]; /* All set as 0 */
+} MAINTENANCE_DATA;
+/* ADF status 1byte:
+ * 7-3:Reserved;
+ * 2:Reserved;
+ * 1: '0'-ADF cover closed; '1'-ADF cover open
+ * 0: '0'-Document on ADF; '1'-No document on ADF
+ *
+*/
+
+struct IPU
+{
+ SANE_Byte byte0; /* 7-4:Reserved; 3:White mode; 2:Reserved; 1-0: Gamma Table Select */
+ SANE_Byte byte1; /* 7-2:Reserved; 1-0: MTF Filter Select */
+};
+struct IPU_Auto_PhotoLetter
+{
+ /* Halftone Separations for each level
+ * 256 steps of relative value with 0 the sharpest and 255 the softest
+ * The relation of strength is Strength2 > Strength3 > Strength4 ...
+ */
+ struct
+ {
+ SANE_Byte level[6];
+ } halftone_separation[2];
+
+ /* 7-2:Reversed 1-0:Halftone
+ * 00 Default
+ * 01 Peak Detection Soft
+ * 10 Peak Detection Sharp
+ * 11 Don't Use
+ */
+ SANE_Byte byte12;
+
+ SANE_Byte black_correction; /* Black correction strength: 0-255 sharpest-softest */
+ SANE_Byte edge_sep[4]; /* Edge Separation strengths: 0-255 sharpest-softest 1-4 */
+ SANE_Byte white_background_sep_strength; /* 0-255 sharpest-softest */
+ SANE_Byte byte19; /* 7-1:Reversed; 0:White mode '0'-Default; '1'-Sharp */
+ SANE_Byte byte20; /* 7-1:Reversed; 0:Halftone mode '0'-widen dots; '1'-Default */
+ SANE_Byte halftone_sep_levela;
+ SANE_Byte halftone_sep_levelb;
+ SANE_Byte byte23; /* 7-4:Reversed; 3-0:Adjustment of separation level: usually fixed to 0 */
+
+ /* 7-4:Reversed; 3-0:Judge Conditions Select
+ * 0XXX Black Correction OFF 1XXX Black Correction ON
+ * X0XX Halftone Separation OFF X1XX Halftone Separation ON
+ * XX0X White Separation OFF XX1X White Separation ON
+ * XXX0 Edge Separation OFF XXX1 Edge Separation ON
+ */
+ SANE_Byte byte24;
+
+ /* 7-4:Filter A; 3-0:Filter B
+ * FilterA: 16 types are valid from 0000 to 1111
+ * FilterB: 0000 to 1110 are valid; 1111 is not valid
+ */
+ SANE_Byte MTF_correction;
+
+ /* 7-4:Filter A; 3-0:Filter B
+ * 0000(soft) to 0111(sharp) are valid; 1000 to 1111 are invalid
+ */
+ SANE_Byte MTF_strength;
+
+ /* 7-4:Filter A; 3-0:Filter B
+ * slightly adjusts the strength of the filters
+ */
+ SANE_Byte MTF_adjustment;
+
+ /* 7-4:Reserved; 3-0: smoothing filter select
+ * 14 kinds are valid from 0000 to 1101; 1110 to 1111 are invalid
+ */
+ SANE_Byte smoothing;
+
+ /* 7-2:Reversed; 1-0: Filter Select
+ * 10 MTF Correction Select
+ * 11 Smoothing Select
+ * from 00 to 01 are not valid and basically it is set as 10
+ */
+ SANE_Byte byte29;
+
+ /* 7-4:Reserved; 3-0: MTF Correction Filter C
+ * 16 kinds are valid from 0000 to 1111
+ */
+ SANE_Byte MTF_correction_c;
+
+ /* 7-3:Reserved; 2-0: MTF Correction Filter strength C
+ * 000(soft) to 111(sharp) are valid
+ */
+ SANE_Byte MTF_strength_c;
+};
+/*
+struct IPU_Dynamic {
+ to be implemented
+};
+sensor data
+*/
+
+/* for object_position command */
+#define OBJECT_POSITION_UNLOAD 0
+#define OBJECT_POSITION_LOAD 1
+
+/* 1-3-15 OBJECT POSITION */
+typedef struct scsi_object_position_cmd
+{
+ SANE_Byte opcode; /* 31H */
+ SANE_Byte position_func; /* 7-5:LUN; 4-3:Reserved; 2-0:Position Function (bit2,bit1,bit0):
+ * 000 Unload Object (NO CHECK ERROR even though no document on ADF)
+ * 001 Load Object (NO CHECK ERROR even though document already fed to start position)
+ * 010 Absolute Positioning in Y-axis. Not Supported in this scanner
+ * 3H-7H Reserved */
+ SANE_Byte count[3]; /* Reserved */
+ SANE_Byte reserved[4];
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+} POSITION;
+
+/* 1-3-16 GET DATA BUFFER STATUS */
+typedef struct scsi_get_data_buffer_status_cmd
+{
+ SANE_Byte opcode; /* 34H */
+ SANE_Byte wait; /* 7-5:LUN; 4-1:Reserved; 0: Wait bit is "0" */
+ SANE_Byte reserved[5];
+ SANE_Byte len[2]; /* Allocation Length */
+ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */
+} GET_DBS_CMD;
+typedef struct scsi_status_hdr
+{
+ SANE_Byte len[3]; /* Data Buffer Status Length */
+ SANE_Byte block; /* 7-1:Reserved; 0:Block bit is 0 */
+} STATUS_HDR;
+typedef struct scsi_status_data
+{
+ SANE_Byte wid; /* window identifier is 0 or 1 */
+ SANE_Byte reserved;
+ SANE_Byte free[3]; /* Available Space Data `Buffer */
+ SANE_Byte filled[3]; /* Scan Data Available (Filled Data Bufferj) */
+} STATUS_DATA;
+/* BUFFER STATUS DATA FORMAT */
+typedef struct scsi_buffer_status
+{
+ STATUS_HDR hdr;
+ STATUS_DATA data;
+} STATUS_BUFFER;
diff --git a/backend/hs2p.c b/backend/hs2p.c
new file mode 100644
index 0000000..de3be42
--- /dev/null
+++ b/backend/hs2p.c
@@ -0,0 +1,3332 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+/* SANE-FLOW-DIAGRAM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device
+ . . - attach : to the device
+ . . . init_options : initialize SANE_OPTIONS array
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner-device
+ - sane_exit() : terminate use of backend
+*/
+#define BUILD 1
+
+/* Begin includes */
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#define BACKEND_NAME hs2p
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "hs2p-scsi.c"
+
+/* Begin macros */
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+
+/* Begin static constants */
+static int num_devices = 0;
+static HS2P_Device *first_dev = NULL;
+static HS2P_Scanner *first_handle = NULL;
+
+static SANE_Char inquiry_data[255] = "HS2P scanner";
+/*
+static SANE_Int disable_optional_frames = 0;
+static SANE_Int fake_inquiry = 0;
+*/
+
+static HS2P_HWEntry HS2P_Device_List[] = {
+ {"RICOH", "IS450"},
+ {"RICOH", "IS430"}, /*untested */
+ {"RICOH", "IS420"}, /*untested */
+ {"RICOH", "IS01"}, /*untested */
+ {"RICOH", "IS02"}, /*untested */
+ {NULL, NULL} /*sentinel */
+};
+
+#if 0
+static int
+allblank (const char *s)
+{
+ while (s && *s)
+ if (!isspace (*s++))
+ return 0;
+
+ return 1;
+}
+#endif
+
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (DBG_proc, ">> max_string_size\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (DBG_proc, "<< max_string_size\n");
+ return max_size;
+}
+
+static void
+trim_spaces (char *s, size_t n)
+{
+ for (s += (n - 1); n > 0; n--, s--)
+ {
+ if (*s && !isspace (*s))
+ break;
+ *s = '\0';
+ }
+}
+static SANE_Bool
+is_device_supported (char *device)
+{
+ HS2P_HWEntry *hw;
+
+ for (hw = &HS2P_Device_List[0]; hw->mfg != NULL; hw++)
+ if (strncmp (device, hw->model, strlen (hw->model)) == 0)
+ break; /* found a match */
+
+ return (hw == NULL) ? SANE_FALSE : SANE_TRUE;
+}
+
+static SANE_Int
+get_list_index (const char *list[], char *s) /* sequential search */
+{
+ SANE_Int i;
+
+ for (i = 0; list[i]; i++)
+ if (strcmp (s, list[i]) == 0)
+ return i; /* FOUND */
+
+ /* unknown paper_list strings are treated as 'custom' */
+ /* unknown compression_list strings are treated as 'none' */
+ /* unknown scan_source_list strings are treated as 'ADF' */
+ return 0;
+}
+
+static SANE_Int
+get_val_id_strndx (struct val_id *vi, int len, SANE_Int val)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ if (vi[i].val == val)
+ return vi[i].id; /* FOUND */
+ return vi[0].id; /* NOT FOUND so let's default to first */
+}
+
+static SANE_Status
+init_options (HS2P_Scanner * s)
+{
+ SANE_Int i;
+ DBG (DBG_proc, ">> init_options\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+
+ /*
+ * "Scan Mode" GROUP:
+ */
+ s->opt[OPT_MODE_GROUP].name = "";
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP;
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Preview: */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Inquiry */
+ s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY;
+ s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY;
+ s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY;
+ s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING;
+ s->opt[OPT_INQUIRY].size = sizeof (inquiry_data);
+ s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_INQUIRY].s = strdup (inquiry_data);
+ s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT; /* Display Only */
+
+ /* Scan mode */
+ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SCAN_MODE].size =
+ max_string_size ((SANE_String_Const *) scan_mode_list);
+ s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_MODE].constraint.string_list =
+ (SANE_String_Const *) & scan_mode_list[0];
+ s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]);
+ s->image_composition = LINEART;
+
+ /* Standard resolutions */
+ s->opt[OPT_RESOLUTION].name = "std-" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = "Std-" SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = "Std " SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList;
+ s->val[OPT_RESOLUTION].w = s->hw->info.default_res;
+
+ /* X Resolution */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = "X " SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &(s->hw->info.xres_range);
+ s->val[OPT_X_RESOLUTION].w = s->hw->info.resBasicX;
+
+ /* Y Resolution */
+ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = "Y " SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &(s->hw->info.yres_range);
+ s->val[OPT_Y_RESOLUTION].w = s->hw->info.resBasicY;
+
+ /* Compression */
+ s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION;
+ s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION;
+ s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION;
+ s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING;
+ s->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_COMPRESSION].size =
+ max_string_size ((SANE_String_Const *) compression_list);
+ s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_COMPRESSION].constraint.string_list =
+ (SANE_String_Const *) & compression_list[0];
+ s->val[OPT_COMPRESSION].s = strdup (compression_list[0]);
+ if (s->hw->info.supports_MH == SANE_FALSE || /* MH G3 1-D */
+ s->hw->info.supports_MR == SANE_FALSE || /* MR G3 2-D */
+ s->hw->info.supports_MMR == SANE_FALSE || /* MMR G4 2-D */
+ s->hw->info.supports_MHB == SANE_FALSE) /* MH byte boundary */
+ {
+ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+
+ /*
+ * "Geometry" GROUP:
+ */
+ s->opt[OPT_GEOMETRY_GROUP].name = "";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Auto Size Recognition available if IPU installed */
+ s->opt[OPT_AUTO_SIZE].name = SANE_NAME_AUTO_SIZE;
+ s->opt[OPT_AUTO_SIZE].title = SANE_TITLE_AUTO_SIZE;
+ s->opt[OPT_AUTO_SIZE].desc = SANE_DESC_AUTO_SIZE;
+ s->opt[OPT_AUTO_SIZE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTO_SIZE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_AUTO_SIZE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTO_SIZE].w = SANE_FALSE;
+ if (!s->hw->info.supports_sizerecognition)
+ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE;
+
+ /* Pad short documents to requested length with white space */
+ s->opt[OPT_PADDING].name = SANE_NAME_PADDING;
+ s->opt[OPT_PADDING].title = SANE_TITLE_PADDING;
+ s->opt[OPT_PADDING].desc = SANE_DESC_PADDING;
+ s->opt[OPT_PADDING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PADDING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_PADDING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PADDING].w = SANE_TRUE;
+ /*if (!s->hw->info.hasADF)
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+ FIXME: compare to user setting, not the existence of FB?
+ if (!strcmp (scan_source_list, "FB"))
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; */
+ /* Permanently disable OPT_PADDING */
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+
+ /* Paper Orientation */
+ s->opt[OPT_PAGE_ORIENTATION].name = SANE_NAME_ORIENTATION;
+ s->opt[OPT_PAGE_ORIENTATION].title = SANE_TITLE_ORIENTATION;
+ s->opt[OPT_PAGE_ORIENTATION].desc = SANE_DESC_ORIENTATION;
+ s->opt[OPT_PAGE_ORIENTATION].type = SANE_TYPE_STRING;
+ s->opt[OPT_PAGE_ORIENTATION].size = max_string_size (orientation_list);
+ s->opt[OPT_PAGE_ORIENTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAGE_ORIENTATION].constraint.string_list = &orientation_list[0];
+ s->val[OPT_PAGE_ORIENTATION].s = strdup (orientation_list[0]);
+
+ /* Paper Size */
+ s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
+ s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list);
+ s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER_SIZE].constraint.string_list = &paper_list[0];
+ s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]);
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &(s->hw->info.x_range);
+ s->val[OPT_TL_X].w = SANE_FIX (0.0);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &(s->hw->info.y_range);
+ s->val[OPT_TL_Y].w = SANE_FIX (0.0);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &(s->hw->info.x_range);
+ s->val[OPT_BR_X].w = s->hw->info.x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &(s->hw->info.y_range);
+ s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
+
+ DBG (DBG_info, "INIT_OPTIONS: ul(x,y) = (%d,%d) br(x,y) = (%d,%d)\n",
+ (unsigned) SANE_UNFIX (s->val[OPT_TL_X].w),
+ (unsigned) SANE_UNFIX (s->val[OPT_TL_Y].w),
+ (unsigned) SANE_UNFIX (s->val[OPT_BR_X].w),
+ (unsigned) SANE_UNFIX (s->val[OPT_BR_Y].w));
+ /* Autoborder */
+ /* Rotation */
+ /* Deskew */
+
+
+
+ /*
+ * "Feeder" GROUP:
+ */
+ s->opt[OPT_FEEDER_GROUP].name = "";
+ s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP;
+ s->opt[OPT_FEEDER_GROUP].desc = "";
+ s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scan Source */
+ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list);
+ s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_SOURCE].constraint.string_list =
+ (SANE_String_Const *) & scan_source_list[0];
+ s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]);
+ if (!s->hw->info.hasADF)
+ s->opt[OPT_SCAN_SOURCE].cap |= SANE_CAP_INACTIVE;
+
+ /* Duplex: */
+ s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
+ s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
+ s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
+ s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DUPLEX].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DUPLEX].w = s->hw->info.default_duplex;
+ if (!s->hw->info.hasDuplex)
+ s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+
+ /* Prefeed: */
+ s->opt[OPT_PREFEED].name = SANE_NAME_PREFEED;
+ s->opt[OPT_PREFEED].title = SANE_TITLE_PREFEED;
+ s->opt[OPT_PREFEED].desc = SANE_DESC_PREFEED;
+ s->opt[OPT_PREFEED].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREFEED].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_PREFEED].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREFEED].w = SANE_FALSE;
+ s->opt[OPT_PREFEED].cap |= SANE_CAP_INACTIVE;
+
+ /* Endorser: */
+ s->opt[OPT_ENDORSER].name = SANE_NAME_ENDORSER;
+ s->opt[OPT_ENDORSER].title = SANE_TITLE_ENDORSER;
+ s->opt[OPT_ENDORSER].desc = SANE_DESC_ENDORSER;
+ s->opt[OPT_ENDORSER].type = SANE_TYPE_BOOL;
+ s->opt[OPT_ENDORSER].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_ENDORSER].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_ENDORSER].w = s->hw->info.endorser_control;
+ if (!s->hw->info.hasEndorser)
+ s->opt[OPT_ENDORSER].cap |= SANE_CAP_INACTIVE;
+
+ /* Endorser String: */
+ s->opt[OPT_ENDORSER_STRING].name = SANE_NAME_ENDORSER_STRING;
+ s->opt[OPT_ENDORSER_STRING].title = SANE_TITLE_ENDORSER_STRING;
+ s->opt[OPT_ENDORSER_STRING].desc = SANE_DESC_ENDORSER_STRING;
+ s->opt[OPT_ENDORSER_STRING].type = SANE_TYPE_STRING;
+ s->opt[OPT_ENDORSER_STRING].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_ENDORSER_STRING].size = sizeof (s->hw->info.endorser_string);
+ s->opt[OPT_ENDORSER_STRING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_ENDORSER_STRING].s = strdup (s->hw->info.endorser_string);
+ if (!s->hw->info.hasEndorser)
+ s->opt[OPT_ENDORSER_STRING].cap |= SANE_CAP_INACTIVE;
+
+ /* Batch */
+ /* Check ADF */
+ /* timeout ADF */
+ /* timeout Manual */
+
+ /*
+ * "Enhancement" GROUP:
+ */
+ s->opt[OPT_ENHANCEMENT_GROUP].name = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Halftone Type */
+ s->opt[OPT_HALFTONE_CODE].name = SANE_NAME_HALFTONE_CODE;
+ s->opt[OPT_HALFTONE_CODE].title = SANE_TITLE_HALFTONE_CODE;
+ s->opt[OPT_HALFTONE_CODE].desc = SANE_DESC_HALFTONE_CODE;
+ s->opt[OPT_HALFTONE_CODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_CODE].size = max_string_size (halftone_code);
+ s->opt[OPT_HALFTONE_CODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_HALFTONE_CODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_CODE].constraint.string_list =
+ (SANE_String_Const *) & halftone_code[0];
+ s->val[OPT_HALFTONE_CODE].s = strdup (halftone_code[0]);
+ if (s->image_composition == LINEART)
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE;
+
+ /* Halftone patterns */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_PATTERN].size =
+ max_string_size ((SANE_String_Const *) halftone_pattern_list);
+ s->opt[OPT_HALFTONE_PATTERN].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
+ (SANE_String_Const *) & halftone_pattern_list[0];
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]);
+ if (s->image_composition == LINEART)
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE;
+
+ /* Gray Filter */
+ s->opt[OPT_GRAYFILTER].name = SANE_NAME_GRAYFILTER;
+ s->opt[OPT_GRAYFILTER].title = SANE_TITLE_GRAYFILTER;
+ s->opt[OPT_GRAYFILTER].desc = SANE_DESC_GRAYFILTER;
+ s->opt[OPT_GRAYFILTER].type = SANE_TYPE_STRING;
+ s->opt[OPT_GRAYFILTER].size = max_string_size (grayfilter_list);
+ s->opt[OPT_GRAYFILTER].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GRAYFILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GRAYFILTER].constraint.string_list =
+ (SANE_String_Const *) & grayfilter_list[0];
+ s->val[OPT_GRAYFILTER].s = strdup (grayfilter_list[0]);
+
+ /* Scan Wait Mode */
+ s->opt[OPT_SCAN_WAIT_MODE].name = SANE_NAME_SCAN_WAIT_MODE;
+ s->opt[OPT_SCAN_WAIT_MODE].title = SANE_TITLE_SCAN_WAIT_MODE;
+ s->opt[OPT_SCAN_WAIT_MODE].desc = SANE_DESC_SCAN_WAIT_MODE;
+ s->opt[OPT_SCAN_WAIT_MODE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SCAN_WAIT_MODE].unit = SANE_UNIT_NONE;
+ s->val[OPT_SCAN_WAIT_MODE].w =
+ (s->hw->info.scan_wait_mode) ? SANE_TRUE : SANE_FALSE;
+
+ /* Brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->info.brightness_range;
+ s->val[OPT_BRIGHTNESS].w = 128;
+
+ /* Threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &s->hw->info.threshold_range;
+ s->val[OPT_THRESHOLD].w = 128;
+
+ /* Contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &s->hw->info.contrast_range;
+ s->val[OPT_CONTRAST].w = 128;
+
+ /* Gamma */
+ s->opt[OPT_GAMMA].name = SANE_NAME_GAMMA;
+ s->opt[OPT_GAMMA].title = SANE_TITLE_GAMMA;
+ s->opt[OPT_GAMMA].desc = SANE_DESC_GAMMA;
+ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA].size = max_string_size ((SANE_String_Const *) gamma_list);
+ /*
+ s->opt[OPT_GAMMA].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA].constraint.range = &u8_range;
+ s->val[OPT_GAMMA].w = 0;
+ */
+ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GAMMA].constraint.string_list =
+ (SANE_String_Const *) & gamma_list[0];
+ s->val[OPT_GAMMA].s = strdup (gamma_list[0]);
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |=
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_GRAY].wa = s->gamma_table;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+
+
+ /* Control Panel */
+ /* ACE Function */
+ /* ACE Sensitivity */
+
+ /* Binary Smoothing Filter */
+ s->opt[OPT_SMOOTHING].name = SANE_NAME_SMOOTHING;
+ s->opt[OPT_SMOOTHING].title = SANE_TITLE_SMOOTHING;
+ s->opt[OPT_SMOOTHING].desc = SANE_DESC_SMOOTHING;
+ s->opt[OPT_SMOOTHING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SMOOTHING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SMOOTHING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_SMOOTHING].w = SANE_FALSE;
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_SMOOTHING].cap |= SANE_CAP_INACTIVE;
+
+ /* Binary Noise Removal Filter */
+ s->opt[OPT_NOISEREMOVAL].name = SANE_NAME_NOISEREMOVAL;
+ s->opt[OPT_NOISEREMOVAL].title = SANE_TITLE_NOISEREMOVAL;
+ s->opt[OPT_NOISEREMOVAL].desc = SANE_DESC_NOISEREMOVAL;
+ s->opt[OPT_NOISEREMOVAL].type = SANE_TYPE_STRING;
+ s->opt[OPT_NOISEREMOVAL].size = max_string_size (noisematrix_list);
+ s->opt[OPT_NOISEREMOVAL].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NOISEREMOVAL].constraint.string_list =
+ (SANE_String_Const *) & noisematrix_list[0];
+ s->val[OPT_NOISEREMOVAL].s = strdup (noisematrix_list[0]);
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_NOISEREMOVAL].cap |= SANE_CAP_INACTIVE;
+
+ /* Automatic Separation */
+ s->opt[OPT_AUTOSEP].name = SANE_NAME_AUTOSEP;
+ s->opt[OPT_AUTOSEP].title = SANE_TITLE_AUTOSEP;
+ s->opt[OPT_AUTOSEP].desc = SANE_DESC_AUTOSEP;
+ s->opt[OPT_AUTOSEP].type = SANE_TYPE_STRING;
+ s->opt[OPT_AUTOSEP].size = max_string_size (auto_separation_list);
+ s->opt[OPT_AUTOSEP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_AUTOSEP].constraint.string_list =
+ (SANE_String_Const *) & auto_separation_list[0];
+ s->val[OPT_AUTOSEP].s = strdup (auto_separation_list[0]);
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_AUTOSEP].cap |= SANE_CAP_INACTIVE;
+
+ /* Automatic Binarization */
+ s->opt[OPT_AUTOBIN].name = SANE_NAME_AUTOBIN;
+ s->opt[OPT_AUTOBIN].title = SANE_TITLE_AUTOBIN;
+ s->opt[OPT_AUTOBIN].desc = SANE_DESC_AUTOBIN;
+ s->opt[OPT_AUTOBIN].type = SANE_TYPE_STRING;
+ s->opt[OPT_AUTOBIN].size = max_string_size (auto_binarization_list);
+ s->opt[OPT_AUTOBIN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_AUTOBIN].constraint.string_list =
+ (SANE_String_Const *) & auto_binarization_list[0];
+ s->val[OPT_AUTOBIN].s = strdup (auto_binarization_list[0]);
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_AUTOBIN].cap |= SANE_CAP_INACTIVE;
+
+ /* SECTION
+ * The IS450 supports up to 4 Section; The IS420 supports up to 6 Sections
+ * For each struct window_section[i] we need to fill in ulx,uly,width,height,etc
+ * NOT YET IMPLEMENTED
+ */
+
+ /* Negative */
+ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEGATIVE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ /* White Balance */
+ s->opt[OPT_WHITE_BALANCE].name = SANE_NAME_WHITE_BALANCE;
+ s->opt[OPT_WHITE_BALANCE].title = SANE_TITLE_WHITE_BALANCE;
+ s->opt[OPT_WHITE_BALANCE].desc = SANE_DESC_WHITE_BALANCE;
+ s->opt[OPT_WHITE_BALANCE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_WHITE_BALANCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_BALANCE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_WHITE_BALANCE].w = SANE_FALSE; /* F/T = Relative/Absolute White */
+
+ /*
+ * "Miscellaneous" GROUP:
+ */
+ s->opt[OPT_MISCELLANEOUS_GROUP].name = "";
+ s->opt[OPT_MISCELLANEOUS_GROUP].title = SANE_TITLE_MISCELLANEOUS_GROUP;
+ s->opt[OPT_MISCELLANEOUS_GROUP].desc = "";
+ s->opt[OPT_MISCELLANEOUS_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MISCELLANEOUS_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_MISCELLANEOUS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Padding Type: */
+ s->opt[OPT_PADDING_TYPE].name = SANE_NAME_PADDING_TYPE;
+ s->opt[OPT_PADDING_TYPE].title = SANE_TITLE_PADDING_TYPE;
+ s->opt[OPT_PADDING_TYPE].desc = SANE_DESC_PADDING_TYPE;
+ s->opt[OPT_PADDING_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_PADDING_TYPE].cap = SANE_CAP_SOFT_DETECT; /* Display only */
+ s->opt[OPT_PADDING_TYPE].size = max_string_size (paddingtype_list);
+ /*
+ s->opt[OPT_PADDING_TYPE].size = sizeof((paddingtype_list[ get_paddingtype_strndx(TRUNCATE) ]));
+ s->opt[OPT_PADDING_TYPE].constraint_type = SANE_CONSTRAINT_NONE;
+ */
+ s->opt[OPT_PADDING_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PADDING_TYPE].constraint.string_list =
+ (SANE_String_Const *) & paddingtype_list[0];
+ s->val[OPT_PADDING_TYPE].s =
+ strdup (paddingtype_list[get_paddingtype_strndx (TRUNCATE)]);
+ DBG (DBG_info, "PADDINGTYPE =%s size=%d\n", s->val[OPT_PADDING_TYPE].s,
+ s->opt[OPT_PADDING_TYPE].size);
+
+ /* Bit Order
+ s->opt[OPT_BITORDER].name = SANE_NAME_BITORDER;
+ s->opt[OPT_BITORDER].title = SANE_TITLE_BITORDER;
+ s->opt[OPT_BITORDER].desc = SANE_DESC_BITORDER;
+ s->opt[OPT_BITORDER].type = SANE_TYPE_WORD;
+ s->opt[OPT_BITORDER].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BITORDER].constraint_type = SANE_CONSTRAINT_NONE
+ s->val[OPT_BITORDER].w = 0x7;
+ */
+
+ /* Self Diagnostics */
+ s->opt[OPT_SELF_DIAGNOSTICS].name = SANE_NAME_SELF_DIAGNOSTICS;
+ s->opt[OPT_SELF_DIAGNOSTICS].title = SANE_TITLE_SELF_DIAGNOSTICS;
+ s->opt[OPT_SELF_DIAGNOSTICS].desc = SANE_DESC_SELF_DIAGNOSTICS;
+ s->opt[OPT_SELF_DIAGNOSTICS].type = SANE_TYPE_BUTTON;
+
+ /* Optical Diagnostics */
+ s->opt[OPT_OPTICAL_ADJUSTMENT].name = SANE_NAME_OPTICAL_ADJUSTMENT;
+ s->opt[OPT_OPTICAL_ADJUSTMENT].title = SANE_TITLE_OPTICAL_ADJUSTMENT;
+ s->opt[OPT_OPTICAL_ADJUSTMENT].desc = SANE_DESC_OPTICAL_ADJUSTMENT;
+ s->opt[OPT_OPTICAL_ADJUSTMENT].type = SANE_TYPE_BUTTON;
+
+ /* MAINTENANCE DATA */
+ s->opt[OPT_DATA_GROUP].name = "";
+ s->opt[OPT_DATA_GROUP].title = "Maintenance Data";
+ s->opt[OPT_DATA_GROUP].desc = "";
+ s->opt[OPT_DATA_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_DATA_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_DATA_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_UPDATE].name = "Update";
+ s->opt[OPT_UPDATE].title = "Update";
+ s->opt[OPT_UPDATE].desc = "Update scanner data";
+ s->opt[OPT_UPDATE].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_NREGX_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGX_ADF].name = "# registers in main-scanning in ADF mode";
+ s->opt[OPT_NREGX_ADF].title = "# registers in main-scanning in ADF mode";
+ s->opt[OPT_NREGX_ADF].desc = "# registers in main-scanning in ADF mode";
+ s->opt[OPT_NREGX_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGX_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGX_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGY_ADF].name = "# registers in sub-scanning in ADF mode";
+ s->opt[OPT_NREGY_ADF].title = "# registers in sub-scanning in ADF mode";
+ s->opt[OPT_NREGY_ADF].desc = "# registers in sub-scanning in ADF mode";
+ s->opt[OPT_NREGY_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGY_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGY_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGY_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGX_BOOK].name = "# registers in main-scanning in book mode";
+ s->opt[OPT_NREGX_BOOK].title = "# registers in main-scanning in book mode";
+ s->opt[OPT_NREGX_BOOK].desc = "# registers in main-scanning in book mode";
+ s->opt[OPT_NREGX_BOOK].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGX_BOOK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGX_BOOK].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_BOOK].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGY_BOOK].name = "# registers in sub-scanning in book mode";
+ s->opt[OPT_NREGY_BOOK].title = "# registers in sub-scanning in book mode";
+ s->opt[OPT_NREGY_BOOK].desc = "# registers in sub-scanning in book mode";
+ s->opt[OPT_NREGY_BOOK].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGY_BOOK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGY_BOOK].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGY_BOOK].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NSCANS_ADF].name = "# ADF Scans";
+ s->opt[OPT_NSCANS_ADF].title = "# ADF Scans";
+ s->opt[OPT_NSCANS_ADF].desc = "# ADF Scans";
+ s->opt[OPT_NSCANS_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NSCANS_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NSCANS_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NSCANS_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NSCANS_BOOK].name = "# BOOK Scans";
+ s->opt[OPT_NSCANS_BOOK].title = "# BOOK Scans";
+ s->opt[OPT_NSCANS_BOOK].desc = "# BOOK Scans";
+ s->opt[OPT_NSCANS_BOOK].type = SANE_TYPE_INT;
+ s->opt[OPT_NSCANS_BOOK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NSCANS_BOOK].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NSCANS_BOOK].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_LAMP_TIME].name = "LAMP TIME";
+ s->opt[OPT_LAMP_TIME].title = "LAMP TIME";
+ s->opt[OPT_LAMP_TIME].desc = "LAMP TIME";
+ s->opt[OPT_LAMP_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_LAMP_TIME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_LAMP_TIME].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_LAMP_TIME].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_EO_ODD].name = "E/O Balance ODD";
+ s->opt[OPT_EO_ODD].title = "E/O Balance ODD";
+ s->opt[OPT_EO_ODD].desc = "Adj. of E/O Balance in black level ODD";
+ s->opt[OPT_EO_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_EO_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_EO_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_EO_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_EO_EVEN].name = "E/O Balance EVEN";
+ s->opt[OPT_EO_EVEN].title = "E/O Balance EVEN";
+ s->opt[OPT_EO_EVEN].desc = "Adj. of E/O Balance in black level EVEN";
+ s->opt[OPT_EO_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_EO_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_EO_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_EO_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_BLACK_LEVEL_ODD].name = "Black Level ODD";
+ s->opt[OPT_BLACK_LEVEL_ODD].title = "Black Level ODD";
+ s->opt[OPT_BLACK_LEVEL_ODD].desc = "Adj. data in black level (ODD)";
+ s->opt[OPT_BLACK_LEVEL_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BLACK_LEVEL_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BLACK_LEVEL_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_BLACK_LEVEL_EVEN].name = "Black Level EVEN";
+ s->opt[OPT_BLACK_LEVEL_EVEN].title = "Black Level EVEN";
+ s->opt[OPT_BLACK_LEVEL_EVEN].desc = "Adj. data in black level (EVEN)";
+ s->opt[OPT_BLACK_LEVEL_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BLACK_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BLACK_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_WHITE_LEVEL_ODD].name = "White Level ODD";
+ s->opt[OPT_WHITE_LEVEL_ODD].title = "White Level ODD";
+ s->opt[OPT_WHITE_LEVEL_ODD].desc = "Adj. data in White level (ODD)";
+ s->opt[OPT_WHITE_LEVEL_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WHITE_LEVEL_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_LEVEL_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_WHITE_LEVEL_EVEN].name = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].title = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].desc = "Adj. data in White level (EVEN)";
+ s->opt[OPT_WHITE_LEVEL_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WHITE_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_WHITE_LEVEL_EVEN].name = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].title = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].desc = "Adj. data in White level (EVEN)";
+ s->opt[OPT_WHITE_LEVEL_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WHITE_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_DENSITY].name = "Density Adjustment";
+ s->opt[OPT_DENSITY].title = "Density Adjustment";
+ s->opt[OPT_DENSITY].desc = "Density adjustment of std. white board";
+ s->opt[OPT_DENSITY].type = SANE_TYPE_INT;
+ s->opt[OPT_DENSITY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DENSITY].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_DENSITY].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].name = "1st adj. in white level (ODD)";
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].title = "1st adj. in white level (ODD)";
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].desc = "1st adj. in white level (ODD)";
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].name = "1st adj. in white level (EVEN)";
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].title = "1st adj. in white level (EVEN)";
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].desc = "1st adj. in white level (EVEN)";
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGX_REVERSE].name = "# registers of main-scanning of backside";
+ s->opt[OPT_NREGX_REVERSE].title =
+ "# registers of main-scanning of backside";
+ s->opt[OPT_NREGX_REVERSE].desc =
+ "# registers of main-scanning of ADF backside";
+ s->opt[OPT_NREGX_REVERSE].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGX_REVERSE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGX_REVERSE].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_REVERSE].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGY_REVERSE].name = "# registers of sub-scanning of backside";
+ s->opt[OPT_NREGY_REVERSE].title = "# registers of sub-scanning of backside";
+ s->opt[OPT_NREGY_REVERSE].desc =
+ "# registers of sub-scanning of ADF backside";
+ s->opt[OPT_NREGY_REVERSE].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGY_REVERSE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGY_REVERSE].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGY_REVERSE].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NSCANS_REVERSE_ADF].name = "# of scans of reverse side in ADF";
+ s->opt[OPT_NSCANS_REVERSE_ADF].title = "# of scans of reverse side in ADF";
+ s->opt[OPT_NSCANS_REVERSE_ADF].desc = "# of scans of reverse side in ADF";
+ s->opt[OPT_NSCANS_REVERSE_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NSCANS_REVERSE_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NSCANS_REVERSE_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NSCANS_REVERSE_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_REVERSE_TIME].name = "LAMP TIME (reverse)";
+ s->opt[OPT_REVERSE_TIME].title = "LAMP TIME (reverse)";
+ s->opt[OPT_REVERSE_TIME].desc = "LAMP TIME (reverse)";
+ s->opt[OPT_REVERSE_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_REVERSE_TIME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_REVERSE_TIME].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_REVERSE_TIME].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NCHARS].name = "# of endorser characters";
+ s->opt[OPT_NCHARS].title = "# of endorser characters";
+ s->opt[OPT_NCHARS].desc = "# of endorser characters";
+ s->opt[OPT_NCHARS].type = SANE_TYPE_INT;
+ s->opt[OPT_NCHARS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NCHARS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NCHARS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ DBG (DBG_proc, "<< init_options\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, int __sane_unused__ connType,
+ HS2P_Device ** devp)
+{
+ SANE_Status status;
+ HS2P_Device *dev;
+ struct inquiry_standard_data ibuf;
+ struct inquiry_vpd_data vbuf;
+ struct inquiry_jis_data jbuf;
+ size_t buf_size;
+ int fd = -1;
+ double mm;
+
+ char device_string[60];
+
+ unsigned int i;
+ SANE_String *str;
+
+
+ DBG (DBG_sane_proc, ">>> attach:\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG (DBG_sane_proc, ">>> attach: opening \"%s\"\n", devname);
+
+ /* sanei_scsi_open takes an option bufsize argument */
+ status = sanei_scsi_open (devname, &fd, &sense_handler, &(dev->sense_data));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: open failed: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (DBG_sane_proc, ">>> attach: opened %s fd=%d\n", devname, fd);
+
+ DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (standard data)\n");
+ memset (&ibuf, 0, sizeof (ibuf));
+ buf_size = sizeof (ibuf);
+ status = inquiry (fd, &ibuf, &buf_size, 0, HS2P_INQUIRY_STANDARD_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: inquiry failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ DBG (DBG_info,
+ ">>> attach: reported devtype='%d', vendor='%.8s', product='%.16s', revision='%.4s'\n",
+ ibuf.devtype, ibuf.vendor, ibuf.product, ibuf.revision);
+ DBG (DBG_info,
+ ">>> attach: reported RMB=%#x Ver=%#x ResponseDataFormat=%#x Length=%#x Byte7=%#x\n",
+ ibuf.rmb_evpd, ibuf.version, ibuf.response_data_format, ibuf.length,
+ ibuf.byte7);
+
+ if (ibuf.devtype != 6 || strncmp ((char *) ibuf.vendor, "RICOH ", 8) != 0)
+ {
+ DBG (DBG_warning, ">>> attach: device is not a RICOH scanner\n");
+ sanei_scsi_close (fd);
+ return SANE_STATUS_INVAL;
+ }
+ else if (!is_device_supported ((char *) ibuf.product))
+ {
+ DBG (DBG_warning,
+ ">>> attach: device %s is not yet a supported RICOH scanner\n",
+ ibuf.product);
+ sanei_scsi_close (fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* We should now have an open file descriptor to a supported hs2p scanner */
+ DBG (DBG_sane_proc, ">>> attach: sending TEST_UNIT_READY\n");
+ do
+ {
+ status = test_unit_ready (fd);
+ }
+ while (status == HS2P_SK_UNIT_ATTENTION);
+ if (status != HS2P_SCSI_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (vpd data)\n");
+ memset (&vbuf, 0, sizeof (vbuf));
+ buf_size = sizeof (vbuf);
+ status = inquiry (fd, &vbuf, &buf_size, 1, HS2P_INQUIRY_VPD_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: inquiry (vpd data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+ print_vpd_info (&vbuf);
+
+ DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (jis data)\n");
+ memset (&jbuf, 0, sizeof (jbuf));
+ buf_size = sizeof (jbuf);
+ status = inquiry (fd, &jbuf, &buf_size, 1, HS2P_INQUIRY_JIS_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: inquiry (jis data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+ print_jis_info (&jbuf);
+
+
+ /* Fill in HS2P_Device {sane;info} */
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, 0, sizeof (*dev));
+
+ /* Maximum Number of Sub-Sections of Main Scanning Window */
+ if (strncmp ((char *) ibuf.product, "IS450", 5) == 0)
+ {
+ dev->info.max_win_sections = 4;
+ }
+ else if (strncmp ((char *) ibuf.product, "IS420", 5) == 0)
+ {
+ dev->info.max_win_sections = 6;
+ }
+
+ /* Some MODE SELECT scanner options */
+ DBG (DBG_proc, ">>> attach: get_basic_measurement_unit\n");
+ status =
+ get_basic_measurement_unit (fd, &(dev->info.bmu), &(dev->info.mud));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: get_basic_measurement_unit failed (%s)\n",
+ sane_strstatus (status));
+ DBG (DBG_error, ">>> attach: setting to defaults\n");
+ status = set_basic_measurement_unit (fd, MILLIMETERS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ ">>> attach: set_basic_measurement_unit failed (%s)\n",
+ sane_strstatus (status));
+ }
+ }
+ if ((status =
+ get_connection_parameters (fd, &(dev->info.cxn))) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: get_connection_parameters failed\n");
+ }
+ status = get_endorser_control (fd, &dev->info.endorser_control);
+ if (status != SANE_STATUS_GOOD || dev->info.endorser_control != 0x01)
+ {
+ DBG (DBG_error,
+ ">>> attach: get_endorser_control failed: return value=%#02x\n",
+ dev->info.endorser_control);
+ dev->info.endorser_control = 0x00;
+ }
+ if ((dev->info.service_mode = get_service_mode (fd)) != 0x00
+ && dev->info.service_mode != 0x01)
+ {
+ DBG (DBG_error, ">>> attach: get_service_mode failed %#02x\n",
+ dev->info.service_mode);
+ dev->info.service_mode = 0x00;
+ }
+ if ((dev->info.scan_wait_mode = get_scan_wait_mode (fd)) != 0x00
+ && dev->info.scan_wait_mode != 0x01)
+ {
+ DBG (DBG_error,
+ ">>> attach: get_scan_wait_mode failed: return value=%#02x\n",
+ dev->info.scan_wait_mode);
+ dev->info.scan_wait_mode = 0x00;
+ }
+ status = get_white_balance (fd, &dev->info.white_balance);
+ if (status != SANE_STATUS_GOOD && dev->info.white_balance != 0x01)
+ {
+ DBG (DBG_error,
+ ">>> attach: get_white_balance failed: return value=%#02x\n",
+ dev->info.white_balance);
+ dev->info.white_balance = RELATIVE_WHITE;
+ }
+
+ DBG (DBG_info, ">>> attach: flushing and closing fd=%d\n", fd);
+ sanei_scsi_req_flush_all ();
+ sanei_scsi_close (fd);
+
+ dev->info.devtype = ibuf.devtype;
+ snprintf (dev->info.vendor, 9, "%-.5s", ibuf.vendor); /* RICOH */
+ trim_spaces (dev->info.vendor, sizeof (dev->info.vendor));
+ snprintf (dev->info.product, 16, "%-.16s", ibuf.product); /* IS450 */
+ trim_spaces (dev->info.product, sizeof (dev->info.product));
+ snprintf (dev->info.revision, 5, "%-.4s", ibuf.revision); /* 1R04 */
+ trim_spaces (dev->info.revision, sizeof (dev->info.revision));
+
+ /* SANE_Device sane information */
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor =
+ (strcmp (dev->info.vendor, "RICOH") ==
+ 0) ? strdup ("Ricoh") : strdup (dev->info.vendor);
+ dev->sane.model = strdup (dev->info.product);
+ dev->sane.type = strdup ("sheetfed scanner");
+ /*
+ dev->sane.email_backend_author = strdup("<Jeremy Johnson> jeremy@acjlaw.net");
+ dev->sane.backend_website = strdup("http://www.acjlaw.net:8080/~jeremy/Ricoh");
+ */
+ /* read these values from backend configuration file using parse_configuration
+ dev->sane.location = strdup();
+ dev->sane.comment = strdup();
+ dev->sane.backend_version_code = strdup();
+ */
+ /* NOT YET USED */
+ /* dev->sane.backend_capablity_flags = 0x00; */
+
+ /* set capabilities from vpd */
+ /* adf_id: 0=none,1=simplex,2=duplex,3=ARDF,4=reserved; should be 1 or 2 for IS450 family */
+ dev->info.hasADF = vbuf.adf_id == 0 ? SANE_FALSE : SANE_TRUE;
+ dev->info.hasSimplex = vbuf.adf_id == 1 ? SANE_TRUE : SANE_FALSE;
+ dev->info.hasDuplex = vbuf.adf_id == 2 ? SANE_TRUE : SANE_FALSE;
+ dev->info.hasARDF = vbuf.adf_id == 3 ? SANE_TRUE : SANE_FALSE;
+
+ /* end_id 0=none,1=Yes,2=reserved; should always be 0 or 1 */
+ dev->info.hasEndorser = vbuf.end_id == 1 ? SANE_TRUE : SANE_FALSE;
+ for (i = 0; i < 20; i++)
+ dev->info.endorser_string[i] = '\0';
+
+ /* ipu_id: Bit0: '0'-no IPU, '1'-has IPU
+ * Bit1: '0'-no extended board, '1'-has extended board;
+ * should always be 0
+ */
+ dev->info.hasIPU = (vbuf.ipu_id & 0x01) == 0x01 ? SANE_TRUE : SANE_FALSE;
+ dev->info.hasXBD = (vbuf.ipu_id & 0x02) == 0x02 ? SANE_TRUE : SANE_FALSE;
+
+
+ /* Image Composition Byte is set to 0x37 (0011 0111) */
+ dev->info.supports_lineart = (vbuf.imagecomposition & 0x01) == 0x01 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_dithering = (vbuf.imagecomposition & 0x02) == 0x02 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_errordiffusion = (vbuf.imagecomposition & 0x04) == 0x04 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_color = (vbuf.imagecomposition & 0x08) == 0x08 ? SANE_TRUE : SANE_FALSE; /* FALSE */
+ dev->info.supports_4bitgray = (vbuf.imagecomposition & 0x10) == 0x10 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_8bitgray = (vbuf.imagecomposition & 0x20) == 0x20 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ /* vbuf.imagecomposition & 0x40; FALSE */
+ /* vbuf.imagecomposition & 0x80 FALSE reserved */
+ str = &scan_mode_list[0]; /* array of string pointers */
+ if (dev->info.supports_lineart)
+ *str++ = strdup (SM_LINEART);
+ if (dev->info.supports_dithering || dev->info.supports_errordiffusion)
+ *str++ = strdup (SM_HALFTONE);
+ if (dev->info.supports_color)
+ *str++ = strdup (SM_COLOR);
+ if (dev->info.supports_4bitgray)
+ *str++ = strdup (SM_4BITGRAY);
+ if (dev->info.supports_8bitgray)
+ *str++ = strdup (SM_8BITGRAY);
+ *str = NULL;
+
+ snprintf (device_string, 60, "Flatbed%s%s%s%s%s%s",
+ dev->info.hasADF ? "/ADF" : "",
+ dev->info.hasDuplex ? "/Duplex" : "",
+ dev->info.hasEndorser ? "/Endorser" : "",
+ dev->info.hasIPU ? "/IPU" : "",
+ dev->info.supports_color ? " Color" : " B&W", " Scanner");
+ dev->sane.type = strdup (device_string);
+
+ /* ACE Image Data Processing Binary Filters
+ * For IS450 this is set to 0x18 (0001 1000) if IPU installed, else 0x00
+ * For IS420 this is set to 0x3C (0011 1100) if IPU installed, else 0x00
+ */
+ dev->info.supports_whiteframing =
+ ((vbuf.imagedataprocessing[0] & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_blackframing =
+ ((vbuf.imagedataprocessing[0] & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_edgeextraction =
+ ((vbuf.imagedataprocessing[0] & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_noiseremoval =
+ ((vbuf.imagedataprocessing[0] & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_smoothing =
+ ((vbuf.imagedataprocessing[0] & 0x10) == 0x10) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_linebolding =
+ ((vbuf.imagedataprocessing[0] & 0x20) == 0x20) ? SANE_TRUE : SANE_FALSE;
+
+ /* Compression Method is not supported for IS450
+ * is supported for IS420 */
+ dev->info.supports_MH =
+ ((vbuf.compression & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_MR =
+ ((vbuf.compression & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_MMR =
+ ((vbuf.compression & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_MHB = ((vbuf.compression & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE; /* MH Byte Boundary */
+
+ /* compression_list[] will have variable number of elements, but the order will be fixed as follows: */
+ str = &compression_list[0];
+ *str++ = strdup ("none");
+ if (dev->info.supports_MH)
+ *str++ = strdup ("G3 1-D MH");
+ if (dev->info.supports_MR)
+ *str++ = strdup ("G3 2-D MR");
+ if (dev->info.supports_MMR)
+ *str++ = strdup ("G4 2-D MMR");
+ if (dev->info.supports_MHB)
+ *str++ = strdup ("MH Byte Boundary");
+ *str = NULL;
+
+ /* Marker Recognition is set to 0x00 */
+ dev->info.supports_markerrecognition =
+ ((vbuf.markerrecognition & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+
+ /* Size Recognition
+ * For IS450 this is set to 0x01 when IPU installed; else 0x00
+ * For IS420 this is set to 0x01
+ */
+ dev->info.supports_sizerecognition =
+ ((vbuf.sizerecognition & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+
+ /* X Maximum Output Pixel in main scanning direction
+ * For IS450 this is set to 0x1360 (4960)
+ * For IS420 this is set to (4880)
+ * [MostSignificantByte LeastSignificantByte]
+ */
+ dev->info.xmaxoutputpixels =
+ (vbuf.xmaxoutputpixels[0] << 8) | vbuf.xmaxoutputpixels[1];
+
+ /* Set capabilities from jis VPD IDENTIFIER Page Code F0H */
+ dev->info.resBasicX = _2btol (&jbuf.BasicRes.x[0]); /* set to 400 */
+ dev->info.resBasicY = _2btol (&jbuf.BasicRes.y[0]); /* set to 400 */
+
+ dev->info.resXstep = (jbuf.resolutionstep >> 4) & 0x0F; /* set to 1 */
+ dev->info.resYstep = jbuf.resolutionstep & 0x0F; /* set to 1 */
+ dev->info.resMaxX = _2btol (&jbuf.MaxRes.x[0]); /* set to 800 */
+ dev->info.resMaxY = _2btol (&jbuf.MaxRes.y[0]); /* set to 800 */
+ dev->info.resMinX = _2btol (&jbuf.MinRes.x[0]); /* set to 100 for IS450 and 60 for IS420 */
+ dev->info.resMinY = _2btol (&jbuf.MinRes.y[0]); /* set to 100 for IS450 and 60 for IS420 */
+
+ dev->info.xres_range.min = _2btol (&jbuf.MinRes.x[0]); /* set to 100 for IS450 and 60 for IS420 */
+ dev->info.xres_range.max = _2btol (&jbuf.MaxRes.x[0]); /* set to 800 */
+ dev->info.resXstep = (jbuf.resolutionstep >> 4) & 0x0F; /* set to 1 */
+ dev->info.xres_range.quant = dev->info.resXstep;
+
+ dev->info.yres_range.min = _2btol (&jbuf.MinRes.y[0]); /* set to 100 for IS450 and 60 for IS420 */
+ dev->info.yres_range.max = _2btol (&jbuf.MaxRes.y[0]); /* set to 800 */
+ dev->info.resYstep = jbuf.resolutionstep & 0x0F; /* set to 1 */
+ dev->info.yres_range.quant = dev->info.resYstep;
+
+ /* set the length of the list to zero first, then append standard resolutions */
+ i = 0;
+ if ((jbuf.standardres[0] & 0x80) == 0x80)
+ dev->info.resStdList[++i] = 60;
+ if ((jbuf.standardres[0] & 0x40) == 0x40)
+ dev->info.resStdList[++i] = 75;
+ if ((jbuf.standardres[0] & 0x20) == 0x20)
+ dev->info.resStdList[++i] = 100;
+ if ((jbuf.standardres[0] & 0x10) == 0x10)
+ dev->info.resStdList[++i] = 120;
+ if ((jbuf.standardres[0] & 0x08) == 0x08)
+ dev->info.resStdList[++i] = 150;
+ if ((jbuf.standardres[0] & 0x04) == 0x04)
+ dev->info.resStdList[++i] = 160;
+ if ((jbuf.standardres[0] & 0x02) == 0x02)
+ dev->info.resStdList[++i] = 180;
+ if ((jbuf.standardres[0] & 0x01) == 0x01)
+ dev->info.resStdList[++i] = 200;
+ if ((jbuf.standardres[1] & 0x80) == 0x80)
+ dev->info.resStdList[++i] = 240;
+ if ((jbuf.standardres[1] & 0x40) == 0x40)
+ dev->info.resStdList[++i] = 300;
+ if ((jbuf.standardres[1] & 0x20) == 0x20)
+ dev->info.resStdList[++i] = 320;
+ if ((jbuf.standardres[1] & 0x10) == 0x10)
+ dev->info.resStdList[++i] = 400;
+ if ((jbuf.standardres[1] & 0x08) == 0x08)
+ dev->info.resStdList[++i] = 480;
+ if ((jbuf.standardres[1] & 0x04) == 0x04)
+ dev->info.resStdList[++i] = 600;
+ if ((jbuf.standardres[1] & 0x02) == 0x02)
+ dev->info.resStdList[++i] = 800;
+ if ((jbuf.standardres[1] & 0x01) == 0x01)
+ dev->info.resStdList[++i] = 1200;
+ dev->info.resStdList[0] = i; /* number of resolutions */
+ if (dev->info.resStdList[0] == 0)
+ { /* make a default standard resolutions for 200 and 300dpi */
+ DBG (DBG_warning, "attach: no standard resolutions reported\n");
+ dev->info.resStdList[0] = 2;
+ dev->info.resStdList[1] = 200;
+ dev->info.resStdList[2] = 300;
+ dev->info.resBasicX = dev->info.resBasicY = 300;
+ }
+ DBG (DBG_info, "attach: Window(W/L) = (%lu/%lu)\n",
+ _4btol (&jbuf.Window.width[0]), _4btol (&jbuf.Window.length[0]));
+ dev->info.winWidth = _4btol (&jbuf.Window.width[0]);
+ dev->info.winHeight = _4btol (&jbuf.Window.length[0]);
+ if (dev->info.winWidth <= 0)
+ {
+ dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5);
+ DBG (DBG_warning, "attach: invalid window width reported, using %d\n",
+ dev->info.winWidth);
+ }
+ if (dev->info.winHeight <= 0)
+ {
+ dev->info.winHeight = dev->info.resBasicY * 14;
+ DBG (DBG_warning, "attach: invalid window height reported, using %d\n",
+ dev->info.winHeight);
+ }
+ /* 4692 / 400 * 25.4 = 297 */
+ mm = (dev->info.resBasicX > 0) ?
+ ((double) dev->info.winWidth / (double) dev->info.resBasicX *
+ MM_PER_INCH) : 0.0;
+ dev->info.x_range.min = SANE_FIX (0.0);
+ dev->info.x_range.max = SANE_FIX (mm);
+ dev->info.x_range.quant = SANE_FIX (0.0);
+ DBG (DBG_info, "attach: winWidth=%d resBasicX=%d mm/in=%f mm=%f\n",
+ dev->info.winWidth, dev->info.resBasicX, MM_PER_INCH, mm);
+
+ mm = (dev->info.resBasicY > 0) ?
+ ((double) dev->info.winHeight / (double) dev->info.resBasicY *
+ MM_PER_INCH) : 0.0;
+ dev->info.y_range.min = SANE_FIX (0.0);
+ dev->info.y_range.max = SANE_FIX (mm);
+ dev->info.y_range.quant = SANE_FIX (0.0);
+
+ DBG (DBG_info, "attach: RANGE x_range.max=%f, y_range.max=%f\n",
+ SANE_UNFIX (dev->info.x_range.max),
+ SANE_UNFIX (dev->info.y_range.max));
+
+ /* min, max, quantization light-dark 1-255, 0 means default 128 */
+ dev->info.brightness_range.min = 1;
+ dev->info.brightness_range.max = 255;
+ dev->info.brightness_range.quant = 1;
+ /* min, max, quantization white-black 1-255, 0 means default 128 */
+ dev->info.contrast_range.min = 1;
+ dev->info.contrast_range.max = 255;
+ dev->info.contrast_range.quant = 1;
+ /* min, max, quantization low-high 1-255, 0 means default 128 */
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 1;
+
+ /* jbuf.functions */
+ dev->info.overflow_support =
+ ((jbuf.functions & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+ dev->info.lineart_support =
+ ((jbuf.functions & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE;
+ dev->info.dither_support =
+ ((jbuf.functions & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE;
+ dev->info.grayscale_support =
+ ((jbuf.functions & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE;
+
+ /* set option defaults */
+ dev->info.default_res = dev->info.resBasicX;
+ dev->info.default_xres = dev->info.resBasicX;
+ dev->info.default_yres = dev->info.resBasicY;
+ dev->info.default_imagecomposition = LINEART;
+ dev->info.default_media = FLATBED;
+ dev->info.default_duplex = SANE_FALSE;
+
+ /* dev->info.autoborder_default = dev->info.canBorderRecog; */
+ /*
+ dev->info.batch_default = SANE_FALSE;
+ dev->info.deskew_default = SANE_FALSE;
+ dev->info.check_adf_default = SANE_FALSE;
+ dev->info.timeout_adf_default = 0;
+ dev->info.timeout_manual_default = 0;
+ */
+ /* dev->info.control_panel_default = dev->info.canACE; Image Data Processing */
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (DBG_sane_proc, "<<< attach:\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/* SANE callback to attach a SCSI device */
+static SANE_Status
+attach_one_scsi (const char *devname)
+{
+ return attach (devname, CONNECTION_SCSI, NULL);
+ /* return SANE_STATUS_GOOD; */
+}
+
+static void
+parse_configuration_file (FILE * fp)
+{
+ char line[PATH_MAX], *s, *t;
+ int linenumber;
+
+ DBG (DBG_proc, ">> parse_configuration_file\n");
+
+ if (fp == NULL)
+ {
+ DBG (DBG_proc,
+ ">> parse_configuration_file: No config file present!\n");
+ }
+ else
+ { /*parse configuration file */
+ for (linenumber = 0; sanei_config_read (line, sizeof (line), fp);
+ linenumber++)
+ {
+ DBG (DBG_proc,
+ ">> parse_configuration_file: parsing config line \"%s\"\n",
+ line);
+ if (line[0] == '#')
+ continue; /* ignore line comments */
+ for (s = line; isspace (*s); ++s); /* skip white space: */
+ for (t = s; *t != '\0'; t++);
+ for (--t; t > s && isspace (*t); t--);
+ *(++t) = '\0'; /*trim trailing space */
+ if (!strlen (s))
+ continue; /* ignore empty lines */
+ if ((t = strstr (s, "scsi ")) != NULL)
+ {
+ /* scsi VENDOR MODEL TYPE BUS CHANNEL ID LUN */
+ DBG (DBG_proc,
+ ">> parse_configuration_file: config file line %d: trying to attach SCSI: %s'\n",
+ linenumber, line);
+ sanei_config_attach_matching_devices (t, attach_one_scsi);
+ }
+ else if ((t = strstr (s, "/dev/")) != NULL)
+ {
+ /* /dev/scanner /dev/sg0 */
+ DBG (DBG_proc,
+ ">> parse_configuration_file: config file line %d: trying to attach SCSI: %s'\n",
+ linenumber, line);
+ sanei_config_attach_matching_devices (t, attach_one_scsi);
+ }
+ else if ((t = strstr (s, "option")) != NULL)
+ {
+ for (t += 6; isspace (*t); t++); /* skip to flag */
+ /* if(strstr(t,"FLAG_VALUE")!=NULL) FLAG_VALUE=SANE_TRUE; */
+ }
+ else
+ {
+ DBG (DBG_proc,
+ ">> parse_configuration_file: config file line %d: OBSOLETE !! use the scsi keyword!\n",
+ linenumber);
+ DBG (DBG_proc,
+ ">> parse_configuration_file: (see man sane-avision for details): trying to attach SCSI: %s'\n",
+ line);
+ }
+ }
+ fclose (fp);
+ }
+ DBG (DBG_proc, "<< parse_configuration_file\n");
+ return;
+}
+
+static SANE_Status
+do_cancel (HS2P_Scanner * s)
+{
+ SANE_Status status;
+ DBG (DBG_sane_proc, ">> do_cancel\n");
+
+ DBG (DBG_proc, "cancel: sending OBJECT POSITION\n");
+
+ s->scanning = SANE_FALSE;
+ s->cancelled = SANE_TRUE;
+ s->EOM = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ if ((status =
+ object_position (s->fd,
+ OBJECT_POSITION_UNLOAD)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "cancel: OBJECT POSTITION failed\n");
+ }
+ sanei_scsi_req_flush_all ();
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+ /*
+ if (s->reader_pid > 0){
+ int exit_status;
+ sanei_thread_kill (s->reader_pid);
+ sanei_thread_waitpid (s->reader_pid, &exit_status);
+ s->reader_pid = 0;
+ }
+ */
+
+ DBG (DBG_sane_proc, "<< do_cancel\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+
+ DBG_INIT (); /* initialize SANE DEBUG */
+
+ /*DBG (DBG_sane_init, "> sane_init (authorize = %p)\n", (void *) authorize); */
+#if defined PACKAGE && defined VERSION
+ DBG (DBG_sane_init, "> sane_init: hs2p backend version %d.%d-%d ("
+ PACKAGE " " VERSION ")\n", SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+#endif
+ /*
+ sanei_thread_init ();
+ */
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+
+ if ((fp = sanei_config_open (HS2P_CONFIG_FILE)) != NULL)
+ {
+ parse_configuration_file (fp);
+ }
+ else
+ {
+ DBG (DBG_sane_init, "> sane_init: No config file \"%s\" present!\n",
+ HS2P_CONFIG_FILE);
+ }
+
+#if 0
+ /* avision.c: search for all supported scanners on all scsi busses & channels */
+ for (hw = &HS2P_Device_List[0]; hw->mfg != NULL; hw++)
+ {
+ sanei_scsi_find_devices (hw->mfg, /*vendor */
+ hw->model, /*model */
+ NULL, /*all types */
+ -1, /*all bus */
+ -1, /*all channel */
+ -1, /*all id */
+ -1, /*all lun */
+ attach_one_scsi); /*callback */
+ DBG (2, "sane_init: %s %s\n", hw->mfg, hw->model);
+ }
+#endif
+
+ DBG (DBG_sane_init, "< sane_init\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ HS2P_Device *dev, *next;
+ DBG (DBG_proc, ">> sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) (SANE_String_Const *) dev->sane.name);
+ free ((SANE_String_Const *) dev->sane.model);
+ free (dev);
+ }
+
+ DBG (DBG_proc, "<< sane_exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ HS2P_Device *dev;
+ int i;
+ DBG (DBG_proc, ">> sane_get_devices (local_only = %d)\n", local_only);
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "<< sane_get_devices\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ HS2P_Device *dev;
+ HS2P_Scanner *s;
+ DBG (DBG_proc, "> sane_open\n");
+
+ if (devnam[0] == '\0')
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+ if (!dev)
+ {
+ status = attach (devnam, CONNECTION_SCSI, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s)); /* initialize */
+
+ s->fd = -1;
+ s->hw = dev;
+ s->hw->info.bmu = s->bmu = MILLIMETERS; /* 01H */
+ s->hw->info.mud = s->mud = 1; /* If the scale is MM or POINT, mud is fixed to 1 */
+ s->bpp = 1; /* supports 1,4,6,8 so we set to LINEART 1bpp */
+ /*
+ s->scanning = SANE_FALSE;
+ s->cancelled = SANE_FALSE;
+ */
+ /*
+ */
+
+ ScannerDump (s);
+ init_options (s);
+
+ s->next = first_handle; /* insert newly opened handle into list of open handles: */
+ first_handle = s;
+
+ /* initialize our parameters here AND in sane_start?
+ get_parameters(s, 0);
+ */
+
+ *handle = s;
+
+ DBG (DBG_proc, "< sane_open\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ HS2P_Scanner *s = (HS2P_Scanner *) handle;
+ char **str;
+ DBG (DBG_proc, ">> sane_close\n");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+ free (s);
+
+ for (str = &compression_list[0]; *str; str++);
+ free (*str);
+ for (str = &scan_mode_list[0]; *str; str++);
+ free (*str);
+
+ DBG (DBG_proc, "<< sane_close\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ HS2P_Scanner *s = handle;
+ DBG (DBG_proc, ">> sane_get_option_descriptor: %d name=%s\n", option,
+ s->opt[option].name);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (DBG_info, "<< sane_get_option_descriptor: name=%s\n",
+ s->opt[option].name);
+ return (s->opt + option);
+}
+
+#if 0
+static SANE_Int
+get_scan_mode_id (char *s) /* sequential search */
+{
+ SANE_Int i;
+
+ for (i = 0; scan_mode_list[i]; i++)
+ if (strcmp (s, scan_mode_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as 'lineart' */
+ return scan_mode_list[i] ? i : 0;
+}
+#endif
+static SANE_Status
+update_hs2p_data (HS2P_Scanner * s)
+{
+
+ DBG (DBG_proc, ">> update_hs2p_data\n");
+ /* OPT_NREGX_ADF: */
+ DBG (DBG_sane_option, "OPT_NREGX_ADF\n");
+ s->val[OPT_NREGX_ADF].w = (SANE_Word) s->data.maintenance.nregx_adf;
+
+ /* OPT_NREGY_ADF: */
+ DBG (DBG_sane_option, "OPT_NREGY_ADF\n");
+ s->val[OPT_NREGY_ADF].w = (SANE_Word) s->data.maintenance.nregx_book;
+
+ /* OPT_NREGX_BOOK: */
+ DBG (DBG_sane_option, "OPT_NREGX_BOOK\n");
+ s->val[OPT_NREGX_BOOK].w = (SANE_Word) s->data.maintenance.nregx_book;
+
+ /* OPT_NREGY_BOOK: */
+ DBG (DBG_sane_option, "OPT_NREGY_BOOK\n");
+ s->val[OPT_NREGY_BOOK].w = (SANE_Word) s->data.maintenance.nregy_book;
+
+ /* OPT_NSCANS_ADF: */
+ DBG (DBG_sane_option, "OPT_NSCANS_ADF\n");
+ s->val[OPT_NSCANS_ADF].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_adf[0]));
+
+ /* OPT_NSCANS_BOOK: */
+ DBG (DBG_sane_option, "OPT_NSCANS_BOOK\n");
+ s->val[OPT_NSCANS_BOOK].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_book[0]));
+
+ /* OPT_LAMP_TIME: */
+ DBG (DBG_sane_option, "OPT_LAMP_TIME\n");
+ s->val[OPT_LAMP_TIME].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.lamp_time[0]));
+
+ /* OPT_EO_ODD: */
+ DBG (DBG_sane_option, "OPT_EO_ODD\n");
+ s->val[OPT_EO_ODD].w = (SANE_Word) s->data.maintenance.eo_odd;
+
+ /* OPT_EO_EVEN: */
+ DBG (DBG_sane_option, "OPT_EO_EVEN\n");
+ s->val[OPT_EO_EVEN].w = (SANE_Word) s->data.maintenance.eo_even;
+
+ /* OPT_BLACK_LEVEL_ODD: */
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_ODD\n");
+ s->val[OPT_BLACK_LEVEL_ODD].w =
+ (SANE_Word) s->data.maintenance.black_level_odd;
+
+ /* OPT_BLACK_LEVEL_EVEN: */
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_EVEN\n");
+ s->val[OPT_BLACK_LEVEL_EVEN].w =
+ (SANE_Word) s->data.maintenance.black_level_even;
+
+ /* OPT_WHITE_LEVEL_ODD: */
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_ODD\n");
+ s->val[OPT_WHITE_LEVEL_ODD].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_odd[0]));
+
+ /* OPT_WHITE_LEVEL_EVEN: */
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_EVEN\n");
+ s->val[OPT_WHITE_LEVEL_EVEN].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_even[0]));
+
+ /* OPT_FIRST_ADJ_WHITE_ODD: */
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_ODD\n");
+ s->val[OPT_FIRST_ADJ_WHITE_ODD].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_odd[0]));
+
+ /* OPT_FIRST_ADJ_WHITE_EVEN: */
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_EVEN\n");
+ s->val[OPT_FIRST_ADJ_WHITE_EVEN].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_even[0]));
+
+ /* OPT_DENSITY: */
+ DBG (DBG_sane_option, "OPT_DENSITY\n");
+ s->val[OPT_DENSITY].w = (SANE_Word) s->data.maintenance.density_adj;
+
+ /* OPT_NREGX_REVERSE: */
+ DBG (DBG_sane_option, "OPT_NREGX_REVERSE\n");
+ s->val[OPT_NREGX_REVERSE].w = (SANE_Word) s->data.maintenance.nregx_reverse;
+
+ /* OPT_NREGY_REVERSE: */
+ DBG (DBG_sane_option, "OPT_NREGY_REVERSE\n");
+ s->val[OPT_NREGY_REVERSE].w = (SANE_Word) s->data.maintenance.nregy_reverse;
+
+ /* OPT_NSCANS_REVERSE_ADF: */
+ DBG (DBG_sane_option, "OPT_NSCANS_REVERSE_ADF\n");
+ s->val[OPT_NSCANS_REVERSE_ADF].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_reverse_adf[0]));
+
+ /* OPT_REVERSE_TIME: */
+ DBG (DBG_sane_option, "OPT_REVERSE_TIME\n");
+ s->val[OPT_REVERSE_TIME].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.reverse_time[0]));
+
+ /* OPT_NCHARS: */
+ DBG (DBG_sane_option, "OPT_NCHARS\n");
+ s->val[OPT_NCHARS].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nchars[0]));
+
+ DBG (DBG_proc, "<< update_hs2p_data\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hs2p_open (HS2P_Scanner * s)
+{
+ SANE_Status status;
+ DBG (DBG_proc, ">> hs2p_open\n");
+ DBG (DBG_info, ">> hs2p_open: trying to open: name=\"%s\" fd=%d\n",
+ s->hw->sane.name, s->fd);
+ if ((status =
+ sanei_scsi_open (s->hw->sane.name, &s->fd, &sense_handler,
+ &(s->hw->sense_data))) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: open of %s failed: %d %s\n",
+ s->hw->sane.name, status, sane_strstatus (status));
+ return (status);
+ }
+ DBG (DBG_info, ">>hs2p_open: OPENED \"%s\" fd=%d\n", s->hw->sane.name,
+ s->fd);
+
+ if ((status = test_unit_ready (s->fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "hs2p_open: test_unit_ready() failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+ DBG (DBG_proc, "<< hs2p_open\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hs2p_close (HS2P_Scanner * s)
+{
+
+ DBG (DBG_proc, ">> hs2p_close\n");
+
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+
+ DBG (DBG_proc, "<< hs2p_close\n");
+ return SANE_STATUS_GOOD;
+}
+
+#include <stdarg.h>
+static SANE_Status
+get_hs2p_data (HS2P_Scanner * s, ...)
+{
+ SANE_Status status;
+ SANE_Byte *buf;
+ size_t *len = &(s->data.bufsize);
+ int dtc, fd = s->fd;
+ u_long dtq = 0; /* two bytes */
+ va_list ap;
+
+ DBG (DBG_proc, ">> get_hs2p_data\n");
+ if (fd < 0)
+ {
+ status = hs2p_open (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_hs2p_data: error opening scanner: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ for (va_start (ap, s), dtc = va_arg (ap, int); dtc != DATA_TYPE_EOL;
+ dtc = va_arg (ap, int))
+ {
+ DBG (DBG_proc, ">> get_hs2p_data 0x%2.2x\n", (int) dtc);
+ switch (dtc)
+ {
+ case DATA_TYPE_GAMMA:
+ buf = &(s->data.gamma[0]);
+ *len = sizeof (s->data.gamma);
+ break;
+ case DATA_TYPE_ENDORSER:
+ buf = &(s->data.endorser[0]);
+ *len = sizeof (s->data.endorser);
+ break;
+ case DATA_TYPE_SIZE:
+ buf = &(s->data.size);
+ *len = sizeof (s->data.size);
+ break;
+ case DATA_TYPE_PAGE_LEN:
+ buf = s->data.nlines;
+ *len = sizeof (s->data.nlines);
+ break;
+ case DATA_TYPE_MAINTENANCE:
+ buf = (SANE_Byte *) & (s->data.maintenance);
+ *len = sizeof (s->data.maintenance);
+ break;
+ case DATA_TYPE_ADF_STATUS:
+ buf = &(s->data.adf_status);
+ *len = sizeof (s->data.adf_status);
+ break;
+ case DATA_TYPE_IMAGE:
+ case DATA_TYPE_HALFTONE:
+ default:
+ DBG (DBG_info, "Data Type Code %2.2x not handled.\n", dtc);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (DBG_info,
+ "get_hs2p_data calling read_data for dtc=%2.2x and bufsize=%lu\n",
+ (int) dtc, (u_long) * len);
+ status = read_data (s->fd, buf, len, (SANE_Byte) dtc, dtq);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_scanner_data: ERROR %s\n",
+ sane_strstatus (status));
+ }
+ }
+ va_end (ap);
+
+ if (fd < 0)
+ { /* need to return fd to original state */
+ status = hs2p_close (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_hs2p_data: error closing fd: %s\n",
+ sane_strstatus (status));
+ }
+ }
+ DBG (DBG_proc, "<< get_hs2p_data: %d\n", status);
+ return (status);
+}
+
+static SANE_Status
+print_maintenance_data (MAINTENANCE_DATA * d)
+{
+ DBG (DBG_proc, ">> print_maintenance_data: \n");
+
+ DBG (DBG_LEVEL, "nregx_adf = %d\n", d->nregx_adf);
+ DBG (DBG_LEVEL, "nregy_adf = %d\n", d->nregy_adf);
+
+ DBG (DBG_LEVEL, "nregx_book = %d\n", d->nregx_book);
+ DBG (DBG_LEVEL, "nregy_book = %d\n", d->nregy_book);
+
+ DBG (DBG_LEVEL, "nscans_adf = %lu\n", _4btol (&(d->nscans_adf[0])));
+ DBG (DBG_LEVEL, "nscans_adf = %lu\n", _4btol (&(d->nscans_adf[0])));
+
+ DBG (DBG_LEVEL, "lamp time = %lu\n", _4btol (&(d->lamp_time[0])));
+
+ DBG (DBG_LEVEL, "eo_odd = %d\n", d->eo_odd);
+ DBG (DBG_LEVEL, "eo_even = %d\n", d->eo_even);
+
+ DBG (DBG_LEVEL, "black_level_odd = %d\n", d->black_level_odd);
+ DBG (DBG_LEVEL, "black_level_even = %d\n", d->black_level_even);
+
+ DBG (DBG_LEVEL, "white_level_odd = %lu\n",
+ _2btol (&(d->white_level_odd[0])));
+ DBG (DBG_LEVEL, "white_level_even = %lu\n",
+ _2btol (&(d->white_level_even[0])));
+
+ DBG (DBG_LEVEL, "first_adj_white_odd = %lu\n",
+ _2btol (&(d->first_adj_white_odd[0])));
+ DBG (DBG_LEVEL, "first_adj_white_even = %lu\n",
+ _2btol (&(d->first_adj_white_even[0])));
+
+ DBG (DBG_LEVEL, "density_adj = %d\n", d->density_adj);
+
+ DBG (DBG_LEVEL, "nregx_reverse = %d\n", d->nregx_reverse);
+ DBG (DBG_LEVEL, "nregy_reverse = %d\n", d->nregy_reverse);
+
+ DBG (DBG_LEVEL, "nscans_reverse_adf = %lu\n",
+ _4btol (&(d->nscans_reverse_adf[0])));
+
+ DBG (DBG_LEVEL, "reverse_time = %lu\n", _4btol (&(d->reverse_time[0])));
+
+ DBG (DBG_LEVEL, "nchars = %lu\n", _4btol (&(d->nchars[0])));
+
+ DBG (DBG_proc, "<< print_maintenance_data: \n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ HS2P_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+ SANE_Int paper_id;
+
+
+
+ name = s->opt[option].name ? s->opt[option].name : "(nil)";
+ if (info)
+ *info = 0;
+ DBG (DBG_proc, ">> sane_control_option: %s option=%d name=%s\n",
+ action == SANE_ACTION_GET_VALUE ? "SET" : "GET", option, name);
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (DBG_proc, "sane_control_option get_value option=%d\n", option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* bool options: */
+ /*case OPT_AUTOBORDER: case OPT_DESKEW: case OPT_CHECK_ADF: case OPT_BATCH: */
+ case OPT_PREVIEW:
+ case OPT_SCAN_WAIT_MODE:
+ case OPT_DUPLEX:
+ case OPT_AUTO_SIZE:
+ case OPT_NEGATIVE:
+ case OPT_ENDORSER:
+ case OPT_SMOOTHING:
+ case OPT_WHITE_BALANCE:
+ case OPT_PREFEED:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_PADDING:
+ *(SANE_Bool *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* string options: */
+ /* case OPT_ADF: */
+ /* case OPT_BITORDER: */
+ /* case OPT_ROTATION */
+ /* case OPT_SECTION: */
+ case OPT_INQUIRY:
+ case OPT_SCAN_SOURCE:
+ case OPT_PAGE_ORIENTATION:
+ case OPT_PAPER_SIZE:
+ case OPT_SCAN_MODE:
+ case OPT_ENDORSER_STRING:
+ case OPT_COMPRESSION:
+ case OPT_NOISEREMOVAL:
+ case OPT_GRAYFILTER:
+ case OPT_HALFTONE_CODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_GAMMA:
+ case OPT_AUTOSEP:
+ case OPT_AUTOBIN:
+ case OPT_PADDING_TYPE:
+ DBG (DBG_proc, "STRING=%s\n", s->val[option].s);
+ strcpy (val, s->val[option].s);
+ return (SANE_STATUS_GOOD);
+ DBG (DBG_proc, "sizeof(val)=%lu sizeof(s)=%lu\n",
+ (u_long) sizeof (val), (u_long) sizeof (s->val[option].s));
+ return (SANE_STATUS_GOOD);
+
+ /* gamma */
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* MAINTENANCE DATA */
+ case OPT_DATA_GROUP:
+ case OPT_UPDATE:
+ return SANE_STATUS_GOOD;
+ case OPT_NREGX_ADF:
+ DBG (DBG_sane_option, "OPT_NREGX_ADF\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_adf;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGY_ADF:
+ DBG (DBG_sane_option, "OPT_NREGY_ADF\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_book;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGX_BOOK:
+ DBG (DBG_sane_option, "OPT_NREGX_BOOK\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_book;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGY_BOOK:
+ DBG (DBG_sane_option, "OPT_NREGY_BOOK\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregy_book;
+ return SANE_STATUS_GOOD;
+ case OPT_NSCANS_ADF:
+ DBG (DBG_sane_option, "OPT_NSCANS_ADF\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_adf[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_NSCANS_BOOK:
+ DBG (DBG_sane_option, "OPT_NSCANS_BOOK\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_book[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_LAMP_TIME:
+ DBG (DBG_sane_option, "OPT_LAMP_TIME\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.lamp_time[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_EO_ODD:
+ DBG (DBG_sane_option, "OPT_EO_ODD\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.eo_odd;
+ return SANE_STATUS_GOOD;
+ case OPT_EO_EVEN:
+ DBG (DBG_sane_option, "OPT_EO_EVEN\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.eo_even;
+ return SANE_STATUS_GOOD;
+ case OPT_BLACK_LEVEL_ODD:
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_ODD\n");
+ *(SANE_Word *) val =
+ (SANE_Word) s->data.maintenance.black_level_odd;
+ return SANE_STATUS_GOOD;
+ case OPT_BLACK_LEVEL_EVEN:
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_EVEN\n");
+ *(SANE_Word *) val =
+ (SANE_Word) s->data.maintenance.black_level_even;
+ return SANE_STATUS_GOOD;
+ case OPT_WHITE_LEVEL_ODD:
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_ODD\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_odd[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_WHITE_LEVEL_EVEN:
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_EVEN\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_even[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_FIRST_ADJ_WHITE_ODD:
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_ODD\n");
+ *(SANE_Word *) val =
+ (SANE_Word)
+ _2btol (&(s->data.maintenance.first_adj_white_odd[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_FIRST_ADJ_WHITE_EVEN:
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_EVEN\n");
+ *(SANE_Word *) val =
+ (SANE_Word)
+ _2btol (&(s->data.maintenance.first_adj_white_even[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_DENSITY:
+ DBG (DBG_sane_option, "OPT_DENSITY\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.density_adj;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGX_REVERSE:
+ DBG (DBG_sane_option, "OPT_NREGX_REVERSE\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_reverse;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGY_REVERSE:
+ DBG (DBG_sane_option, "OPT_NREGY_REVERSE\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregy_reverse;
+ return SANE_STATUS_GOOD;
+ case OPT_NSCANS_REVERSE_ADF:
+ DBG (DBG_sane_option, "OPT_NSCANS_REVERSE_ADF\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_reverse_adf[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_REVERSE_TIME:
+ DBG (DBG_sane_option, "OPT_REVERSE_TIME\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.reverse_time[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_NCHARS:
+ DBG (DBG_sane_option, "OPT_NCHARS\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nchars[0]));
+ return (SANE_STATUS_GOOD);
+
+ default:
+ DBG (DBG_proc, "sane_control_option:invalid option number %d\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (DBG_proc, "sane_control_option set_value\n");
+ switch (s->opt[option].type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %d\n",
+ name, option, *(SANE_Word *) val);
+ break;
+ case SANE_TYPE_FIXED:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %f\n",
+ name, option, SANE_UNFIX (*(SANE_Word *) val));
+ break;
+ case SANE_TYPE_STRING:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %s\n",
+ name, option, (char *) val);
+ break;
+ case SANE_TYPE_BUTTON:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d]\n",
+ name, option);
+ update_hs2p_data (s);
+ break;
+ default:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d]\n", name,
+ option);
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+ if ((status =
+ sanei_constrain_value (s->opt + option, val,
+ info)) != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* disable auto size */
+ /* make sure that paper-size is set to custom */
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ s->val[option].w = *(SANE_Word *) val;
+ /* resets the paper format to user defined */
+ if (strcmp (s->val[OPT_PAPER_SIZE].s, paper_list[0]) != 0) /* CUSTOM PAPER SIZE */
+ {
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[OPT_PAPER_SIZE].s)
+ free (s->val[OPT_PAPER_SIZE].s);
+ s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]); /* CUSTOM PAPER SIZE */
+ }
+ /* fall through */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ /* fall through */
+ /*case OPT_ACE_FUNCTION: case OPT_ACE_SENSITIVITY: */
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_NUM_OPTS:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ /* string options */
+ case OPT_NOISEREMOVAL:
+ case OPT_AUTOSEP:
+ case OPT_AUTOBIN:
+ case OPT_COMPRESSION:
+ case OPT_PADDING_TYPE:
+ case OPT_GRAYFILTER:
+ case OPT_HALFTONE_CODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_ENDORSER_STRING:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* boolean options: */
+ case OPT_PREVIEW:
+ case OPT_DUPLEX:
+ case OPT_NEGATIVE:
+ case OPT_SCAN_WAIT_MODE:
+ case OPT_ENDORSER:
+ case OPT_SMOOTHING:
+ case OPT_WHITE_BALANCE:
+ case OPT_PREFEED:
+ case OPT_PADDING:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* options with side effect */
+ case OPT_GAMMA:
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ if (!strcmp ((SANE_String) val, "User"))
+ {
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_TRUE;
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ /* Brightness and Contrast do not work when downloading Gamma Table */
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ s->val[option].w = *(SANE_Word *) val;
+ s->val[OPT_X_RESOLUTION].w = *(SANE_Word *) val;
+ s->val[OPT_Y_RESOLUTION].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+ case OPT_SCAN_SOURCE:
+ /* a string option */
+ /* Since the scanner ejects the sheet in ADF mode
+ * it is impossible to scan multiple sections in one document
+ * In ADF mode, because of mechanical limitations:
+ * the minimum document size is (x,y)=(69mm x 120mm)
+ */
+ if (info && strcmp ((char *) s->val[option].s, (char *) val))
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!strcmp ("ADF", (SANE_String) val))
+ {
+ s->opt[OPT_ENDORSER].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ENDORSER_STRING].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_PREFEED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_DUPLEX].cap &= ~SANE_CAP_INACTIVE;
+ /*s->opt[OPT_PADDING].cap &= ~SANE_CAP_INACTIVE; */
+ }
+ else
+ { /* Flatbed */
+ s->opt[OPT_ENDORSER].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ENDORSER_STRING].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_PREFEED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_SCAN_MODE:
+ /* a string option */
+ /* scan mode != lineart disables compression, setting it to 'none' */
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (!strcmp (SM_LINEART, (SANE_String) val))
+ {
+ s->image_composition = LINEART;
+ s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE; /* enable compression control */
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; /* enable threshold control */
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; /* disable brightness control */
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* disable contrast control */
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; /* disable gamma */
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; /* disable gamma */
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; /* disable gamma */
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* disable halftone code */
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* disable halftone pattern */
+ }
+ else
+ {
+ if (!strcmp (SM_HALFTONE, (SANE_String) val))
+ {
+ s->image_composition = HALFTONE;
+ s->opt[OPT_HALFTONE_CODE].cap &= ~SANE_CAP_INACTIVE; /* enable halftone code */
+ s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; /* enable halftone pattern */
+ }
+ else if (!strcmp (SM_4BITGRAY, (SANE_String) val) ||
+ !strcmp (SM_6BITGRAY, (SANE_String) val) ||
+ !strcmp (SM_8BITGRAY, (SANE_String) val))
+ {
+ s->image_composition = GRAYSCALE;
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; /* enable gamma */
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; /* enable brightness */
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; /* enable contrast */
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* disable threshold */
+ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE; /* disable compression */
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* disable halftone code */
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* disable halftone pattern */
+ if (s->val[OPT_COMPRESSION].s
+ && get_compression_id (s->val[OPT_COMPRESSION].s) !=
+ 0)
+ {
+ free (s->val[OPT_COMPRESSION].s);
+ s->val[OPT_COMPRESSION].s =
+ strdup (compression_list[0]);
+ }
+ }
+ }
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_ORIENTATION:
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ /* set val to current selected paper size */
+ paper_id = get_paper_id ((SANE_String) s->val[OPT_PAPER_SIZE].s);
+ goto paper_id;
+ case OPT_PAPER_SIZE:
+ /* a string option */
+ /* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */
+ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* disable auto size */
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ paper_id = get_paper_id ((SANE_String) val);
+
+ /* paper_id 0 is a special case (custom) that
+ * disables the paper size control of geometry
+ */
+ paper_id:
+ if (paper_id != 0)
+ {
+ double x_max, y_max, x, y, temp;
+
+ x_max = SANE_UNFIX (s->hw->info.x_range.max);
+ y_max = SANE_UNFIX (s->hw->info.y_range.max);
+
+ /* a dimension of 0.0 (or less) is replaced with the max value */
+ x = (paper_sizes[paper_id].width <= 0.0) ? x_max :
+ paper_sizes[paper_id].width;
+ y = (paper_sizes[paper_id].length <= 0.0) ? y_max :
+ paper_sizes[paper_id].length;
+
+ if (info)
+ *info |=
+ SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ if (!strcmp (s->val[OPT_PAGE_ORIENTATION].s, LANDSCAPE)) /* swap */
+ {
+ temp = y_max;
+ y_max = x_max;
+ x_max = temp;
+ temp = y;
+ y = x;
+ x = temp;
+ }
+
+ s->val[OPT_TL_X].w = SANE_FIX (0.0);
+ s->val[OPT_TL_Y].w = SANE_FIX (0.0);
+ s->val[OPT_BR_X].w = SANE_FIX (MIN (x, x_max));
+ s->val[OPT_BR_Y].w = SANE_FIX (MIN (y, y_max));
+ }
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_UPDATE: /* SANE_TYPE_BUTTON */
+ DBG (DBG_info,
+ "OPT_UPDATE: ready to call get_hs2p_data: fd=%d\n", s->fd);
+ get_hs2p_data (s,
+ /* DATA_TYPE_GAMMA, */
+ /* DATA_TYPE_ENDORSER, */
+ /* DATA_TYPE_SIZE, */
+ /* DATA_TYPE_PAGE_LEN, */
+ DATA_TYPE_MAINTENANCE,
+ /* DATA_TYPE_ADF_STATUS, */
+ /* DATA_TYPE_IMAGE, */
+ /* DATA_TYPE_HALFTONE, */
+ DATA_TYPE_EOL); /* va_list end */
+ update_hs2p_data (s);
+ if (DBG_LEVEL >= DBG_info)
+ print_maintenance_data (&(s->data.maintenance));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+ }
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_proc, "<< sane_control_option\n");
+ return (SANE_STATUS_INVAL);
+
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ HS2P_Scanner *s = handle;
+ DBG (DBG_proc, ">> sane_get_parameters\n");
+
+ if (!s->scanning)
+ {
+ int width, length, xres, yres;
+ const char *mode;
+
+ memset (&s->params, 0, sizeof (s->params)); /* CLEAR SANE_Parameters */
+
+ width =
+ (int) (SANE_UNFIX (s->val[OPT_BR_X].w) -
+ SANE_UNFIX (s->val[OPT_TL_X].w));
+ length =
+ (int) (SANE_UNFIX (s->val[OPT_BR_Y].w) -
+ SANE_UNFIX (s->val[OPT_TL_Y].w));
+ xres = s->val[OPT_X_RESOLUTION].w;
+ yres = s->val[OPT_Y_RESOLUTION].w;
+ DBG (DBG_proc,
+ ">>sane_get_parameters: (W/L)=(%d/%d) (xres/yres)=(%d/%d) mud=%d\n",
+ width, length, xres, yres, s->hw->info.mud);
+
+ /* make best-effort guess at what parameters will look like once scanning starts. */
+ if (xres > 0 && yres > 0 && width > 0 && length > 0)
+ { /* convert from mm to pixels */
+ s->params.pixels_per_line =
+ width * xres / s->hw->info.mud / MM_PER_INCH;
+ s->params.lines = length * yres / s->hw->info.mud / MM_PER_INCH;
+ }
+
+ mode = s->val[OPT_SCAN_MODE].s;
+ if ((strcmp (mode, SM_LINEART) == 0) ||
+ (strcmp (mode, SM_HALFTONE)) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ /* if the scanner truncates to the byte boundary, so: chop! */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ s->params.depth = 1;
+ }
+ else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ s->params.last_frame = SANE_TRUE;
+ }
+ else
+ DBG (DBG_proc, "sane_get_parameters: scanning, so can't get params\n");
+
+ if (params)
+ *params = s->params;
+
+ DBG (DBG_proc,
+ "%d pixels per line, %d bytes per line, %d lines high, total %lu bytes, "
+ "dpi=%ld\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read,
+ (long) SANE_UNFIX (s->val[OPT_Y_RESOLUTION].w));
+
+ DBG (DBG_proc, "<< sane_get_parameters\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+set_window_data (HS2P_Scanner * s, SWD * wbuf)
+{
+ struct hs2p_window_data *data;
+ int i, nwin, id, xres, yres, xmax, ymax;
+ long ulx, uly, width, length, number, bytes;
+ double offset;
+
+ DBG (DBG_proc, ">> set_window_data: sizeof(*wbuf)=%lu; window len=%lu\n",
+ (u_long) sizeof (*wbuf), (u_long) sizeof (wbuf->data));
+
+ /* initialize our window buffer with zeros */
+ DBG (DBG_proc, ">> set_window_data: CLEARING wbuf\n");
+ memset (wbuf, 0, sizeof (*wbuf));
+
+ /* Header */
+ DBG (DBG_proc,
+ ">> set_window_data: writing Window Descriptor Length =%lu\n",
+ (u_long) sizeof (wbuf->data));
+ _lto2b (sizeof (wbuf->data), &wbuf->hdr.len[0]);
+
+ /* X-Axis Resolution 100-800dpi in 1 dpi steps */
+ xres = s->val[OPT_X_RESOLUTION].w;
+ if (xres < s->hw->info.resMinX || xres > s->hw->info.resMaxX)
+ {
+ DBG (DBG_error, "XRESOLUTION %d IS NOT WITHIN [%d, %d]\n", xres,
+ s->hw->info.resMinX, s->hw->info.resMaxX);
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* Y-Axis Resolution 100-800dpi in 1 dpi steps */
+ yres = s->val[OPT_Y_RESOLUTION].w;
+ if (yres < s->hw->info.resMinY || yres > s->hw->info.resMaxY)
+ {
+ DBG (DBG_error, "YRESOLUTION %d IS NOT WITHIN [%d, %d]\n", yres,
+ s->hw->info.resMinY, s->hw->info.resMaxY);
+ return (SANE_STATUS_INVAL);
+ }
+
+ ulx = (long) SANE_UNFIX (s->val[OPT_TL_X].w);
+ uly = (long) SANE_UNFIX (s->val[OPT_TL_Y].w);
+ DBG (DBG_info, "set_window_data: upperleft=(%ld,%ld)\n", ulx, uly);
+
+ width = (long) SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); /* Window Width */
+ length = (long) SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); /* Window Length */
+ DBG (DBG_info, "set_window_data: WxL= %ld x %ld\n", width, length);
+
+ /* NOTE: the width in inches converted to byte unit must be the following values or less
+ * Binary: 620 bytes
+ * 4-bits gray: 2480 bytes
+ * 8-bits gray: 4960 bytes
+ */
+ if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_LINEART))
+ {
+ bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w / 8.0);
+ if (bytes > 620)
+ {
+ DBG (DBG_error,
+ "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n",
+ width, s->val[OPT_X_RESOLUTION].w, bytes);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_4BITGRAY))
+ {
+ bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w / 2.0);
+ if (bytes > 2480)
+ {
+ DBG (DBG_error,
+ "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n",
+ width, s->val[OPT_X_RESOLUTION].w, bytes);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_8BITGRAY))
+ {
+ bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w);
+ if (bytes > 4960)
+ {
+ DBG (DBG_error,
+ "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n",
+ width, s->val[OPT_X_RESOLUTION].w, bytes);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+
+
+ if (strcmp (s->val[OPT_SCAN_SOURCE].s, scan_source_list[ADF]) == 0)
+ {
+ offset = (SANE_UNFIX (s->hw->info.x_range.max) - width) / 2.0;
+ DBG (DBG_info, "set_window_data: ADF origin offset=%f\n", offset);
+
+ ulx += (long) offset;
+ }
+
+
+ if (strcmp (s->val[OPT_SCAN_SOURCE].s, scan_source_list[FB]) == 0)
+ { /* FB */
+ xmax = 298; /*mm */
+ ymax = 432;
+ }
+ else
+ { /* ADF */
+ xmax = 298;
+ ymax = 2000;
+ }
+
+ /* Boundary Conditions when BMU = MM */
+ number = ulx + width;
+ if (number <= 0 || number > xmax)
+ {
+ DBG (DBG_error, "NOT WITHIN BOUNDS: ulx=%ld width=%ld sum=%ld\n",
+ ulx, width, number);
+ return (SANE_STATUS_INVAL);
+ }
+ number = uly + length;
+ if (number <= 0 || number > ymax)
+ {
+ DBG (DBG_error, "NOT WITHIN BOUNDS: uly=%ld length=%ld sum=%ld\n",
+ uly, length, number);
+ return (SANE_STATUS_INVAL);
+ }
+
+
+
+ /* For each window (up to 2 if we're duplexing) */
+ nwin = (s->val[OPT_DUPLEX].w == SANE_TRUE) ? 2 : 1;
+ for (i = 0; i < nwin; i++)
+ {
+ data = &(wbuf->data[i]);
+ data->window_id = i;
+ data->auto_bit &= 0xFE; /* Auto bit set to 0 since auto function isn't supported */
+
+ _lto2b (xres, &data->xres[0]); /* Set X resolution */
+ _lto2b (yres, &data->yres[0]); /* Set Y resolution */
+
+ _lto4b (ulx, &data->ulx[0]); /* X-Axis Upper Left */
+ _lto4b (uly, &data->uly[0]); /* Y-Axis Upper Left */
+
+ _lto4b (width, &data->width[0]); /* Window Width */
+ _lto4b (length, &data->length[0]); /* Window Length */
+
+
+
+
+
+
+ data->brightness = s->val[OPT_BRIGHTNESS].w; /* black-white: 1-255; 0 is default 128 */
+ data->threshold = s->val[OPT_THRESHOLD].w; /* light-dark: 1-255; 0 is default 128 */
+ data->contrast = s->val[OPT_CONTRAST].w; /* low-high: 1-255: 0 is default 128 */
+ if (data->brightness == 128)
+ data->brightness = 0;
+ if (data->threshold == 128)
+ data->threshold = 0;
+ if (data->contrast == 128)
+ data->contrast = 0;
+
+ data->image_composition = s->image_composition;
+ data->bpp = s->bpp = s->params.depth;
+
+ /* Byte 27, 347 Halftone Code: if HALFTONE, then either DITHER or ERROR_DIFFUSION */
+ if (s->image_composition == HALFTONE)
+ { /* Then let's use pattern selected by user */
+ data->halftone_code =
+ (get_halftone_code_id (s->val[OPT_HALFTONE_CODE].s) ==
+ 0) ? DITHER : ERROR_DIFFUSION;
+ data->halftone_id =
+ get_halftone_pattern_val (s->val[OPT_HALFTONE_PATTERN].s);
+ }
+ else
+ {
+ data->halftone_code = DITHER; /* 00H reserved */
+ data->halftone_id = 0x01; /* 00H reserved */
+ }
+
+
+
+ /* Byte 29, 349: RIF:reserved:padding type */
+ if (data->image_composition == LINEART
+ || data->image_composition == HALFTONE)
+ {
+ if (s->val[OPT_NEGATIVE].w)
+ data->byte29 |= (1 << 7); /* set bit 7 */
+ else
+ data->byte29 &= ~(1 << 7); /* unset bit 7 */
+ }
+ /* Padding Type */
+ data->byte29 |=
+ (paddingtype[get_paddingtype_id (s->val[OPT_PADDING_TYPE].s)].
+ val & 0x07);
+
+ /* Bit Ordering:
+ * Manual Says DEFAULT: [1111 1111][1111 1000]
+ * Bits15-8 reserved;
+ * Bit7: '0'-Normal '1'-Mirroring
+ * Bit6-4: Reserved
+ * Bit3: '0'-arrangement from MSB in grayscale mode
+ * '1'-arrangement from LSB in grayscale mode
+ * 2: '0'-unpacked 4-bits grayscale [DEFAULT]
+ * '1'-packed 4-bits grayscale
+ * 1: '0'-output from LSB of each word [DEFAULT]
+ * '1'-output from MSB of each word
+ * 0: '0'-output from bit 0 of each byte [DEFAULT]
+ * '1'-output from bit 7 of each byte
+ */
+ _lto2b (0x007, &data->bit_ordering[0]); /* Set to Packed4bitGray, MSB, MSbit */
+
+ /* Compression Type and Argument NOT SUPPORTED in this scanner */
+ data->compression_type = 0x00;
+ data->compression_arg = 0x02;
+
+ /* Byte42: MRIF:Filtering:GammaID */
+ if (data->image_composition == GRAYSCALE)
+ {
+ if (s->val[OPT_NEGATIVE].w)
+ data->byte42 &= ~(1 << 7); /* unset bit 7 */
+ else
+ data->byte42 |= (1 << 7); /* set bit 7 */
+ data->byte42 |= (get_grayfilter_val (s->val[OPT_GRAYFILTER].s) & (7 << 4)); /* set bits 6-4 to GRAYFILTER */
+ }
+ else
+ {
+ data->byte42 &= ~(1 << 7); /* unset bit 7 */
+ data->byte42 &= ~(7 << 4); /* unset bits 6-4 */
+ }
+ /* Bytes 45, 365 Binary Filtering for lineart and halftone can be set when option IPU is installed */
+ if ((id = get_noisematrix_id (s->val[OPT_NOISEREMOVAL].s)) != 0)
+ {
+ data->binary_filtering |= (1 << 7); /* set bit 7 */
+ data->binary_filtering |= noisematrix[id].val; /* 00H, 01H, 02H; 03H:Reserved */
+ }
+ if (s->val[OPT_SMOOTHING].w == SANE_TRUE)
+ data->binary_filtering |= (1 << 6); /* set bit 6 */
+
+ /* Automatic separation, automatic binarization, and SECTION is available if Image Processing Unit is installed */
+ if (s->hw->info.hasIPU)
+ {
+ /* Byte 48: Automatic Separation */
+ data->automatic_separation =
+ get_auto_separation_val (s->val[OPT_AUTOSEP].s);
+ /* Byte 50: Automatic Binarization */
+ data->automatic_binarization =
+ get_auto_binarization_val (s->val[OPT_AUTOBIN].s);
+ /* fill in values for each section
+ for(j=0; j<NumSec; j++){
+ wbuf[i].winsec[j].ulx
+ wbuf[i].winsec[j].uly
+ wbuf[i].winsec[j].width
+ wbuf[i].winsec[j].length
+ wbuf[i].winsec[j].binary_filtering
+ wbuf[i].winsec[j].threshold
+ wbuf[i].winsec[j].image_composition
+ wbuf[i].winsec[j].halftone_id
+ wbuf[i].winsec[j].halftone_arg
+ }
+ */
+ }
+ }
+ DBG (DBG_proc, "<< set_window_data\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_start (SANE_Handle handle) /* begin scanning */
+{
+ HS2P_Scanner *s = handle;
+ SANE_Status status;
+ SWD wbuf; /* Set Window Data: hdr + data */
+ GWD gbuf; /* Get Window Data: hdr + data */
+ SANE_Byte mode, prefeed, mwt = 0;
+
+ DBG (DBG_proc, ">> sane_start\n");
+ s->cancelled = SANE_FALSE;
+
+ if (s->another_side)
+ {
+ /* Number of bytes to read for one side of sheet */
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ DBG (DBG_info,
+ "SIDE#2 %d pixels per line, %d bytes, %d lines high, dpi=%d\n",
+ s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (int) s->val[OPT_Y_RESOLUTION].w);
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+ s->another_side = SANE_FALSE; /* This is side 2, so no more sides */
+ DBG (DBG_proc, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+ }
+
+ if (s->scanning)
+ {
+ DBG (DBG_info, "sane_start: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* Let's start a new scan */
+
+ if ((status = sane_get_parameters (s, 0)) != SANE_STATUS_GOOD)
+ { /* get preliminary parameters */
+ DBG (DBG_error, "sane_start: sane_get_parameters failed: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (DBG_info, ">> sane_start: trying to open: name=\"%s\" fd=%d\n",
+ s->hw->sane.name, s->fd);
+ if ((status =
+ sanei_scsi_open (s->hw->sane.name, &s->fd, &sense_handler,
+ &(s->hw->sense_data))) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: open of %s failed: %d %s\n",
+ s->hw->sane.name, status, sane_strstatus (status));
+ return (status);
+ }
+ DBG (DBG_info, ">>sane_start: OPENED \"%s\" fd=%d\n", s->hw->sane.name,
+ s->fd);
+
+ if ((status = test_unit_ready (s->fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: test_unit_ready() failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+
+ if ((status = reserve_unit (s->fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: reserve_unit() failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ /* NOW SET UP SCANNER ONCE PER BATCH */
+
+ DBG (DBG_info, "sane_start: setting basic measurement unit to mm\n");
+ if ((status = set_basic_measurement_unit (s->fd, s->hw->info.bmu)))
+ {
+ DBG (DBG_error, "set_basic_measurment_unit failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ if (get_scan_source_id (s->val[OPT_SCAN_SOURCE].s) == 0)
+ {
+ mode = FLATBED;
+ }
+ else
+ {
+ mode = (s->val[OPT_DUPLEX].w) ? DUPLEX : SIMPLEX;
+ }
+
+ prefeed = s->val[OPT_PREFEED].w ? 0x04 : 0x00;
+ DBG (DBG_info, "sane_start: setting scan source to %d %s\n", mode,
+ (SANE_String) s->val[OPT_SCAN_SOURCE].s);
+ DBG (DBG_info, "sane_start: setting prefeed to %d\n", prefeed);
+ if ((status =
+ set_adf_control (s->fd, &mode, &prefeed, &mwt)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: error set_adf_control: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+
+ DBG (DBG_info, "sane_start: setting endorser control to %d\n",
+ s->val[OPT_ENDORSER].w);
+ if ((status =
+ set_endorser_control (s->fd,
+ &s->val[OPT_ENDORSER].w)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_endorser_control failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ if (s->val[OPT_ENDORSER].w)
+ {
+ DBG (DBG_info, "sane_start: setting endorser string to %s\n",
+ s->val[OPT_ENDORSER_STRING].s);
+ if ((status =
+ set_endorser_string (s->fd,
+ (SANE_String) s->val[OPT_ENDORSER_STRING].
+ s)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_endorser_string failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ }
+
+ DBG (DBG_info, "sane_start: setting scan_wait_mode to %d\n",
+ s->val[OPT_SCAN_WAIT_MODE].w);
+ if ((status =
+ set_scan_wait_mode (s->fd,
+ s->val[OPT_SCAN_WAIT_MODE].w)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_scan_wait_mode failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (DBG_info, "sane_start: setting white_balance to %d\n",
+ s->val[OPT_WHITE_BALANCE].w);
+ if ((status =
+ set_white_balance (s->fd,
+ &s->val[OPT_WHITE_BALANCE].w)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_white_balance failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ if (s->val[OPT_CUSTOM_GAMMA].b)
+ { /* Custom Gamma needs to be sent to scanner */
+ DBG (DBG_info, "sane_start: setting custom gamma\n");
+ if ((status = hs2p_send_gamma (s)))
+ {
+ DBG (DBG_error, "hs2p_send_gamma failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ /* We succeeded, so we don't need to upload this vector again (unless user modifies gamma table) */
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE;
+ }
+
+
+ DBG (DBG_info, "sane_start: filling in window data buffer \n");
+ if ((status = set_window_data (s, &wbuf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_window_data failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (DBG_info, "sane_start: sending SET WINDOW DATA\n");
+ if ((status = set_window (s->fd, &wbuf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "SET WINDOW DATA failed: %s\n",
+ sane_strstatus (status));
+ print_window_data (&wbuf);
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (DBG_info, "sane_start: sending GET WINDOW\n");
+ memset (&gbuf, 0, sizeof (gbuf)); /* CLEAR wbuf */
+ if ((status = get_window (s->fd, &gbuf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ /* DONE WITH SETTING UP SCANNER ONCE PER BATCH */
+
+ s->EOM = SANE_FALSE;
+ if (mode != FLATBED)
+ {
+ if ((status =
+ get_hs2p_data (s, DATA_TYPE_ADF_STATUS,
+ DATA_TYPE_EOL)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: error reading adf_status: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+ if ((s->data.adf_status & 0x00) == 0x01)
+ {
+ DBG (DBG_warning, "sane_start: No document on ADF\n");
+ return (SANE_STATUS_NO_DOCS);
+ }
+ else if ((s->data.adf_status & 0x02) == 0x02)
+ {
+ DBG (DBG_warning, "sane_start: ADF cover open!\n");
+ return (SANE_STATUS_COVER_OPEN);
+ }
+ }
+
+
+ status = trigger_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "start of scan failed: %s\n", sane_strstatus (status));
+ print_window_data (&wbuf);
+ /* this line introduced not to freeze xscanimage */
+ /*do_cancel (s); */
+ return status;
+ }
+ /* Wait for scanner to become ready to transmit data */
+ status = hs2p_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "GET DATA STATUS failed: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ s->another_side = (mode == DUPLEX) ? SANE_TRUE : SANE_FALSE;
+ /* Number of bytes to read for one side of sheet */
+ DBG (DBG_info, "ANOTHER SIDE = %s\n", (s->another_side) ? "TRUE" : "FALSE");
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ DBG (DBG_info, "%d pixels per line, %d bytes, %d lines high, dpi=%d\n",
+ s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (int) s->val[OPT_Y_RESOLUTION].w);
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+
+ DBG (DBG_proc, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ HS2P_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread, bytes_requested, i, start;
+ SANE_Byte color;
+ DBG (DBG_proc, ">> sane_read\n");
+
+ *len = 0;
+
+ DBG (DBG_info, "sane_read: bytes left to read: %ld\n",
+ (u_long) s->bytes_to_read);
+
+ if (s->bytes_to_read == 0)
+ { /* We've reached the end of one side of sheet */
+ if (!s->another_side)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+ else
+ {
+ /* let frontend call sane_start again to reset bytes_to_read */
+ DBG (DBG_proc, "<< sane_read: getting another side\n");
+ return (SANE_STATUS_EOF);
+ }
+ }
+
+ if (s->cancelled)
+ {
+ DBG (DBG_info, "sane_read: cancelled!\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ if (!s->scanning)
+ {
+ DBG (DBG_info, "sane_read: scanning is false!\n");
+ return (do_cancel (s));
+ }
+
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ bytes_requested = nread;
+ start = 0;
+
+pad:
+ if (s->EOM)
+ {
+ if (s->val[OPT_PADDING].w)
+ {
+ DBG (DBG_info, "sane_read s->EOM padding from %ld to %ld\n",
+ (u_long) start, (u_long) bytes_requested);
+ color = (s->val[OPT_NEGATIVE].w) ? 0 : 255;
+ /* pad to requested length */
+ for (i = start; i < bytes_requested; i++)
+ buf[i] = color;
+ nread = bytes_requested; /* we've padded to bytes_requested */
+ *len = nread;
+ s->bytes_to_read -= nread;
+ }
+ else /* TRUNCATE: should never reach here */
+ {
+ *len = nread;
+ s->bytes_to_read = 0; /* EOM */
+ }
+ }
+ else
+ {
+ DBG (DBG_info, "sane_read: trying to read %ld bytes\n", (u_long) nread);
+ status = read_data (s->fd, buf, &nread, DATA_TYPE_IMAGE, DTQ);
+ switch (status)
+ {
+ case SANE_STATUS_NO_DOCS:
+ DBG (DBG_error, "sane_read: End-Of-Medium detected\n");
+ s->EOM = SANE_TRUE;
+ /*
+ * If status != SANE_STATUS_GOOD, then sense_handler() has already
+ * been called and the sanei_* functions have already gotten the
+ * sense data buffer (which apparently clears the error conditionn)
+ * so the following doesn't work:
+ get_sense_data (s->fd, &(s->hw->sense_data));
+ print_sense_data (&(s->hw->sense_data));
+ */
+ start = (isset_ILI (s->hw->sense_data)) ? /* Invalid Length Indicator */
+ bytes_requested - _4btol (s->hw->sense_data.information) : nread;
+ goto pad;
+ break;
+ case SANE_STATUS_GOOD:
+ *len = nread;
+ s->bytes_to_read -= nread;
+ break;
+ default:
+ DBG (DBG_error, "sane_read: read error\n");
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ }
+ DBG (DBG_proc, "<< sane_read\n");
+ return (SANE_STATUS_GOOD);
+}
+
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ HS2P_Scanner *s = handle;
+ DBG (DBG_proc, ">> sane_cancel\n");
+
+ if (s->scanning)
+ { /* if batchmode is enabled, then call set_window to abort the batch
+ if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE) {
+ DBG(5, "sane_cancel: calling set_window to abort batch\n");
+ set_window(s, BH_BATCH_ABORT);
+ } */
+ do_cancel (s);
+ }
+
+
+
+ DBG (DBG_proc, "<< sane_cancel\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (DBG_proc, ">> sane_set_io_mode (handle = %p, non_blocking = %d)\n",
+ handle, non_blocking);
+ DBG (DBG_proc, "<< sane_set_io_mode\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+#ifdef NONBLOCKSUPPORTED
+ HS2P_Scanner *s = handle;
+#endif
+ DBG (DBG_proc, ">> sane_get_select_fd (handle = %p, fd = %p)\n", handle,
+ (void *) fd);
+
+#ifdef NONBLOCKSUPPORTED
+ if (s->fd < 0)
+ {
+ DBG (DBG_proc, "<< sane_get_select_fd\n");
+ return SANE_STATUS_INVAL;
+ }
+ *fd = s->fd;
+ return SANE_STATUS_GOOD;
+#else
+ handle = handle;
+ fd = fd; /* get rid of compiler warning */
+ DBG (DBG_proc, "<< sane_get_select_fd\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif
+}
diff --git a/backend/hs2p.conf.in b/backend/hs2p.conf.in
new file mode 100644
index 0000000..1f7cb8c
--- /dev/null
+++ b/backend/hs2p.conf.in
@@ -0,0 +1,2 @@
+scsi RICOH
+/dev/scanner
diff --git a/backend/hs2p.h b/backend/hs2p.h
new file mode 100644
index 0000000..091fce2
--- /dev/null
+++ b/backend/hs2p.h
@@ -0,0 +1,516 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef HS2P_H
+#define HS2P_H 1
+
+#include <sys/types.h>
+#include "../include/sane/config.h"
+
+#include "hs2p-scsi.h"
+#include "hs2p-saneopts.h"
+
+#define HS2P_CONFIG_FILE "hs2p.conf"
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+typedef struct
+{
+ const char *mfg;
+ const char *model;
+} HS2P_HWEntry;
+
+enum CONNECTION_TYPES
+{ CONNECTION_SCSI = 0, CONNECTION_USB };
+
+enum media
+{ FLATBED = 0x00, SIMPLEX, DUPLEX };
+
+
+typedef struct data
+{
+ size_t bufsize;
+ /* 00H IMAGE */
+ /* 01H RESERVED */
+ /* 02H Halftone Mask */
+ SANE_Byte gamma[256]; /* 03H Gamma Function */
+ /* 04H - 7FH Reserved */
+ SANE_Byte endorser[19]; /* 80H Endorser */
+ SANE_Byte size; /* 81H startpos(4bits) + width(4bits) */
+ /* 82H Reserved */
+ /* 83H Reserved (Vendor Unique) */
+ SANE_Byte nlines[5]; /* 84H Page Length */
+ MAINTENANCE_DATA maintenance; /* 85H */
+ SANE_Byte adf_status; /* 86H */
+ /* 87H Reserved (Skew Data) */
+ /* 88H-91H Reserved (Vendor Unique) */
+ /* 92H Reserved (Scanner Extension I/O Access) */
+ /* 93H Reserved (Vendor Unique) */
+ /* 94H-FFH Reserved (Vendor Unique) */
+} HS2P_DATA;
+
+typedef struct
+{
+ SANE_Range xres_range;
+ SANE_Range yres_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+
+ SANE_Int window_width;
+ SANE_Int window_height;
+
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+ SANE_Range threshold_range;
+
+ char inquiry_data[256];
+
+ SANE_Byte max_win_sections; /* Number of supported window subsections
+ IS450 supports max of 4 sections
+ IS420 supports max of 6 sections
+ */
+
+ /* Defaults */
+ SANE_Int default_res;
+ SANE_Int default_xres;
+ SANE_Int default_yres;
+ SANE_Int default_imagecomposition; /* [lineart], halftone, grayscale, color */
+ SANE_Int default_media; /* [flatbed], simplex, duplex */
+ SANE_Int default_paper_size; /* [letter], legal, ledger, ... */
+ SANE_Int default_brightness;
+ SANE_Int default_contrast;
+ SANE_Int default_gamma; /* Normal, Soft, Sharp, Linear, User */
+ SANE_Bool default_adf;
+ SANE_Bool default_duplex;
+ /*
+ SANE_Bool default_border;
+ SANE_Bool default_batch;
+ SANE_Bool default_deskew;
+ SANE_Bool default_check_adf;
+ SANE_Int default_timeout_adf;
+ SANE_Int default_timeout_manual;
+ SANE_Bool default_control_panel;
+ */
+
+ /* Mode Page Parameters */
+ MP_CXN cxn; /* hdr + Connection Parameters */
+
+ SANE_Int bmu;
+ SANE_Int mud;
+ SANE_Int white_balance; /* 00H Relative, 01H Absolute; power on default is relative */
+ /* Lamp Timer not supported */
+ SANE_Int adf_control; /* 00H None, 01H Book, 01H Simplex, 02H Duplex */
+ SANE_Int adf_mode_control; /* bit2: prefeed mode invalid: "0" : valid "1" */
+ /* Medium Wait Timer not supported */
+ SANE_Int endorser_control; /* Default Off when power on */
+ SANE_Char endorser_string[20];
+ SANE_Bool scan_wait_mode; /* wait for operator panel start button to be pressed */
+ SANE_Bool service_mode; /* power on default self_diagnostics 00H; 01H optical_adjustment */
+
+ /* standard information: EVPD bit is 0 */
+ SANE_Byte devtype; /* devtype[6]="scanner" */
+ SANE_Char vendor[9]; /* model name 8+1 */
+ SANE_Char product[17]; /* product name 16+1 */
+ SANE_Char revision[5]; /* revision 4+1 */
+
+ /* VPD information: EVPD bit is 1, Page Code=C0H */
+ /* adf_id: 0: No ADF
+ * 1: Single-sided ADF
+ * 2: Double-sided ADF
+ * 3: ARDF (Reverse double-sided ADF)
+ * 4: Reserved
+ */
+
+ SANE_Bool hasADF; /* If YES; can either be one of Simplex,Duplex,ARDF */
+ SANE_Bool hasSimplex;
+ SANE_Bool hasDuplex;
+ SANE_Bool hasARDF;
+
+ SANE_Bool hasEndorser;
+
+ SANE_Bool hasIPU;
+ SANE_Bool hasXBD;
+
+ /* VPD Image Composition */
+ SANE_Bool supports_lineart;
+ SANE_Bool supports_dithering;
+ SANE_Bool supports_errordiffusion;
+ SANE_Bool supports_color;
+ SANE_Bool supports_4bitgray;
+ SANE_Bool supports_8bitgray;
+
+ /* VPD Image Data Processing ACE (supported for IS420) */
+ SANE_Bool supports_whiteframing;
+ SANE_Bool supports_blackframing;
+ SANE_Bool supports_edgeextraction;
+ SANE_Bool supports_noiseremoval; /* supported for IS450 if IPU installed */
+ SANE_Bool supports_smoothing; /* supported for IS450 if IPU installed */
+ SANE_Bool supports_linebolding;
+
+ /* VPD Compression (not supported for IS450) */
+ SANE_Bool supports_MH;
+ SANE_Bool supports_MR;
+ SANE_Bool supports_MMR;
+ SANE_Bool supports_MHB;
+
+ /* VPD Marker Recognition (not supported for IS450) */
+ SANE_Bool supports_markerrecognition;
+
+ /* VPD Size Recognition (supported for IS450 if IPU installed) */
+ SANE_Bool supports_sizerecognition;
+
+ /* VPD X Maximum Output Pixel: IS450:4960 IS420:4880 */
+ SANE_Int xmaxoutputpixels;
+
+ /* jis information VPD IDENTIFIER Page Code F0H */
+ SANE_Int resBasicX; /* basic X resolution */
+ SANE_Int resBasicY; /* basic Y resolution */
+ SANE_Int resXstep; /* resolution step in main scan direction */
+ SANE_Int resYstep; /* resolution step in sub scan direction */
+ SANE_Int resMaxX; /* maximum X resolution */
+ SANE_Int resMaxY; /* maximum Y resolution */
+ SANE_Int resMinX; /* minimum X resolution */
+ SANE_Int resMinY; /* minimum Y resolution */
+ SANE_Int resStdList[16 + 1]; /* list of available standard resolutions (first slot is the length) */
+ SANE_Int winWidth; /* length of window (in BasicX res DPI) */
+ SANE_Int winHeight; /* height of window (in BasicY res DPI) */
+ /* jis.functions duplicates vpd.imagecomposition lineart/dither/grayscale */
+ SANE_Bool overflow_support;
+ SANE_Bool lineart_support;
+ SANE_Bool dither_support;
+ SANE_Bool grayscale_support;
+
+} HS2P_Info;
+
+typedef struct HS2P_Device
+{
+ struct HS2P_Device *next;
+ /*
+ * struct with pointers to device/vendor/model names, and a type value
+ * used to inform sane frontend about the device
+ */
+ SANE_Device sane;
+ HS2P_Info info;
+ SENSE_DATA sense_data;
+} HS2P_Device;
+
+#define GAMMA_LENGTH 256
+typedef struct HS2P_Scanner
+{
+ /* all the state needed to define a scan request: */
+ struct HS2P_Scanner *next; /* linked list for housekeeping */
+ int fd; /* SCSI filedescriptor */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during reading of config file. */
+ int buffer_size; /* for sanei_open */
+ int connection; /* hardware interface type */
+
+
+ /* SANE option descriptors and values */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params; /* SANE image parameters */
+ /* additional values that don't fit into Option_Value representation */
+ SANE_Word gamma_table[GAMMA_LENGTH]; /* Custom Gray Gamma Table */
+
+ /* state information - not options */
+
+ /* scanner dependent/low-level state: */
+ HS2P_Device *hw;
+
+ SANE_Int bmu; /* Basic Measurement Unit */
+ SANE_Int mud; /* Measurement Unit Divisor */
+ SANE_Byte image_composition; /* LINEART, HALFTONE, GRAYSCALE */
+ SANE_Byte bpp; /* 1,4,6,or 8 Bits Per Pixel */
+
+
+ u_long InvalidBytes;
+ size_t bytes_to_read;
+ SANE_Bool cancelled;
+ /*SANE_Bool backpage; */
+ SANE_Bool scanning;
+ SANE_Bool another_side;
+ SANE_Bool EOM;
+
+ HS2P_DATA data;
+} HS2P_Scanner;
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+static const SANE_Range u16_range = {
+ 0, /* minimum */
+ 65535, /* maximum */
+ 0 /* quantization */
+};
+
+#define SM_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define SM_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define SM_DITHER "Dither"
+#define SM_ERRORDIFFUSION "Error Diffusion"
+#define SM_COLOR SANE_VALUE_SCAN_MODE_COLOR
+#define SM_4BITGRAY "4-Bit Gray"
+#define SM_6BITGRAY "6-Bit Gray"
+#define SM_8BITGRAY "8-Bit Gray"
+static SANE_String scan_mode_list[9];
+enum
+{ FB, ADF };
+static SANE_String_Const scan_source_list[] = {
+ "FB", /* Flatbed */
+ "ADF", /* Automatic Document Feeder */
+ NULL
+};
+static SANE_String compression_list[6]; /* "none", "g31d MH", "g32d MR", "g42d MMR", "MH byte boundary", NULL} */
+
+typedef struct
+{
+ SANE_String name;
+ double width, length; /* paper dimensions in mm */
+} HS2P_Paper;
+/* list of support paper sizes */
+/* 'custom' MUST be item 0; otherwise a width or length of 0 indicates
+ * the maximum value supported by the scanner
+ */
+static const HS2P_Paper paper_sizes[] = { /* Name, Width, Height in mm */
+ {"Custom", 0.0, 0.0},
+ {"Letter", 215.9, 279.4},
+ {"Legal", 215.9, 355.6},
+ {"Ledger", 279.4, 431.8},
+ {"A3", 297, 420},
+ {"A4", 210, 297},
+ {"A4R", 297, 210},
+ {"A5", 148.5, 210},
+ {"A5R", 210, 148.5},
+ {"A6", 105, 148.5},
+ {"B4", 250, 353},
+ {"B5", 182, 257},
+ {"Full", 0.0, 0.0},
+};
+
+#define PORTRAIT "Portait"
+#define LANDSCAPE "Landscape"
+static SANE_String_Const orientation_list[] = {
+ PORTRAIT,
+ LANDSCAPE,
+ NULL /* sentinel */
+};
+
+/* MUST be kept in sync with paper_sizes */
+static SANE_String_Const paper_list[] = {
+ "Custom",
+ "Letter",
+ "Legal",
+ "Ledger",
+ "A3",
+ "A4", "A4R",
+ "A5", "A5R",
+ "A6",
+ "B4",
+ "B5",
+ "Full",
+ NULL /* (not the same as "") sentinel */
+};
+
+#if 0
+static /* inline */ int _is_host_little_endian (void);
+static /* inline */ int
+_is_host_little_endian ()
+{
+ SANE_Int val = 255;
+ unsigned char *firstbyte = (unsigned char *) &val;
+
+ return (*firstbyte == 255) ? SANE_TRUE : SANE_FALSE;
+}
+#endif
+
+static /* inline */ void
+_lto2b (u_long val, SANE_Byte * bytes)
+{
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static /* inline */ void
+_lto3b (u_long val, SANE_Byte * bytes)
+{
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static /* inline */ void
+_lto4b (u_long val, SANE_Byte * bytes)
+{
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static /* inline */ u_long
+_2btol (SANE_Byte * bytes)
+{
+ u_long rv;
+
+ rv = (bytes[0] << 8) | bytes[1];
+
+ return rv;
+}
+
+static /* inline */ u_long
+_4btol (SANE_Byte * bytes)
+{
+ u_long rv;
+
+ rv = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
+
+ return rv;
+}
+
+/*
+static inline SANE_Int
+_2btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 8) | bytes[1];
+ return (rv);
+}
+*/
+static inline SANE_Int
+_3btol (SANE_Byte * bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
+ return (rv);
+}
+
+/*
+static inline SANE_Int
+_4btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+*/
+enum adf_ret_bytes
+{ ADF_SELECTION = 2, ADF_MODE_CONTROL, MEDIUM_WAIT_TIMER };
+
+#define get_paddingtype_id(s) (get_list_index( paddingtype_list, (char *)(s) ))
+#define get_paddingtype_val(i) (paddingtype[ get_paddingtype_id( (i) ) ].val)
+#define get_paddingtype_strndx(v) (get_val_id_strndx(&paddingtype[0], NELEMS(paddingtype), (v)))
+
+#define get_halftone_code_id(s) (get_list_index( halftone_code, (char *)(s) ))
+#define get_halftone_code_val(i) (halftone[get_halftone_code_id( (i) ) ].val)
+
+#define get_halftone_pattern_id(s) (get_list_index( halftone_pattern_list, (char *)(s) ))
+#define get_halftone_pattern_val(i) (halftone[get_halftone_pattern_id( (i) ) ].val)
+
+#define get_auto_binarization_id(s) (get_list_index( auto_binarization_list, (char *)(s) ))
+#define get_auto_binarization_val(i) (auto_binarization[ get_auto_binarization_id( (i) ) ].val)
+
+#define get_auto_separation_id(s) (get_list_index( auto_separation_list, (char *)(s) ))
+#define get_auto_separation_val(i) (auto_separation[ get_auto_separation_id( (i) ) ].val)
+
+#define get_noisematrix_id(s) (get_list_index( noisematrix_list, (char *)(s) ))
+#define get_noisematrix_val(i) (noisematrix[ get_noisematrix_id( (i) ) ].val)
+
+#define get_grayfilter_id(s) (get_list_index( grayfilter_list, (char *)(s) ))
+#define get_grayfilter_val(i) (grayfilter[ get_grayfilter_id( (i) ) ].val)
+
+#define get_paper_id(s) (get_list_index( paper_list, (char *)(s) ))
+#define get_compression_id(s) (get_list_index( (const char **)compression_list, (char *)(s) ))
+#define get_scan_source_id(s) (get_list_index( (const char **)scan_source_list, (char *)(s) ))
+
+#define reserve_unit(fd) (unit_cmd((fd),HS2P_SCSI_RESERVE_UNIT))
+#define release_unit(fd) (unit_cmd((fd),HS2P_SCSI_RELEASE_UNIT))
+
+#define GET SANE_TRUE
+#define SET SANE_FALSE
+
+#define get_endorser_control(fd,val) (endorser_control( (fd), (val), GET ))
+#define set_endorser_control(fd,val) (endorser_control( (fd), (val), SET ))
+
+#define get_connection_parameters(fd,parm) (connection_parameters( (fd), (parm), GET ))
+#define set_connection_parameters(fd,parm) (connection_parameters( (fd), (parm), SET ))
+
+#define get_adf_control(fd, a, b, c) (adf_control( (fd), GET, (a), (b), (c) ))
+#define set_adf_control(fd, a, b, c) (adf_control( (fd), SET, (a), (b), (c) ))
+
+#define RELATIVE_WHITE 0x00
+#define ABSOLUTE_WHITE 0x01
+#define get_white_balance(fd,val) (white_balance( (fd), (val), GET ))
+#define set_white_balance(fd,val) (white_balance( (fd), (val), SET ))
+
+#define get_scan_wait_mode(fd) (scan_wait_mode( (fd), 0, GET ))
+#define set_scan_wait_mode(fd,val) (scan_wait_mode( (fd), (val), SET ))
+
+#define get_service_mode(fd) (service_mode( (fd), 0, GET ))
+#define set_service_mode(fd,val) (service_mode( (fd), (val), SET ))
+
+#define isset_ILI(sd) ( ((sd).sense_key & 0x20) != 0)
+#define isset_EOM(sd) ( ((sd).sense_key & 0x40) != 0)
+
+
+#endif /* HS2P_H */
diff --git a/backend/ibm-scsi.c b/backend/ibm-scsi.c
new file mode 100644
index 0000000..59b42cb
--- /dev/null
+++ b/backend/ibm-scsi.c
@@ -0,0 +1,439 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/*
+*/
+
+#include <time.h>
+
+
+/* SCSI commands that the Ibm scanners understand: */
+#define IBM_SCSI_TEST_UNIT_READY 0x00
+#define IBM_SCSI_SET_WINDOW 0x24
+#define IBM_SCSI_GET_WINDOW 0x25
+#define IBM_SCSI_READ_SCANNED_DATA 0x28
+#define IBM_SCSI_INQUIRY 0x12
+#define IBM_SCSI_MODE_SELECT 0x15
+#define IBM_SCSI_START_SCAN 0x1b
+#define IBM_SCSI_MODE_SENSE 0x1a
+#define IBM_SCSI_GET_BUFFER_STATUS 0x34
+#define IBM_SCSI_OBJECT_POSITION 0x31
+
+/* How long do we wait for scanner to have data for us */
+#define MAX_WAITING_TIME 15
+
+/* for object_position command */
+#define OBJECT_POSITION_UNLOAD 0
+#define OBJECT_POSITION_LOAD 1
+
+struct scsi_window_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte reserved[4];
+ SANE_Byte len[3];
+ SANE_Byte control;
+};
+
+struct scsi_mode_select_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ SANE_Byte page_code; /* for mode_sense, reserved for mode_select */
+ SANE_Byte unused[1];
+ SANE_Byte len;
+ SANE_Byte control;
+};
+
+struct scsi_mode_header {
+ SANE_Byte data_length; /* Sense data length */
+ SANE_Byte medium_type;
+ SANE_Byte dev_spec;
+ SANE_Byte blk_desc_len;
+};
+
+/* next struct introduced by mf */
+struct scsi_object_position_cmd {
+ SANE_Byte opcode;
+ SANE_Byte position_func;
+ SANE_Byte count[3];
+ SANE_Byte res[3];
+ SANE_Byte control;
+ SANE_Byte res2;
+};
+
+struct scsi_get_buffer_status_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte res[5];
+ SANE_Byte len[2];
+ SANE_Byte control;
+};
+
+struct scsi_status_desc {
+ SANE_Byte window_id;
+ SANE_Byte byte2;
+ SANE_Byte available[3];
+ SANE_Byte filled[3];
+};
+
+struct scsi_status_data {
+ SANE_Byte len[3];
+ SANE_Byte byte4;
+ struct scsi_status_desc desc;
+};
+
+struct scsi_start_scan_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte unused[2];
+ SANE_Byte len;
+ SANE_Byte control;
+};
+
+struct scsi_read_scanner_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte data_type;
+ SANE_Byte byte3;
+ SANE_Byte data_type_qualifier[2];
+ SANE_Byte len[3];
+ SANE_Byte control;
+};
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (11, ">> test_unit_ready\n");
+
+ cmd[0] = IBM_SCSI_TEST_UNIT_READY;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< test_unit_ready\n");
+ return (status);
+}
+
+static SANE_Status
+inquiry (int fd, void *buf, size_t * buf_size)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (11, ">> inquiry\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = IBM_SCSI_INQUIRY;
+ cmd[4] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, "<< inquiry\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select (int fd, struct mode_pages *mp)
+{
+ static struct {
+ struct scsi_mode_select_cmd cmd;
+ struct scsi_mode_header smh;
+ struct mode_pages mp;
+ } select_cmd;
+ SANE_Status status;
+ DBG (11, ">> mode_select\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd.opcode = IBM_SCSI_MODE_SELECT;
+ select_cmd.cmd.byte2 |= SMS_PF;
+ select_cmd.cmd.len = sizeof(select_cmd.smh) + sizeof(select_cmd.mp);
+/* next line by mf */
+/* select_cmd.cmd.page_code= 20; */
+ memcpy (&select_cmd.mp, mp, sizeof(*mp));
+ status = sanei_scsi_cmd (fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ DBG (11, "<< mode_select\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+mode_sense (int fd, struct mode_pages *mp, SANE_Byte page_code)
+{
+ static struct scsi_mode_select_cmd cmd; /* no type, we can reuse it for sensing */
+ static struct {
+ struct scsi_mode_header smh;
+ struct mode_pages mp;
+ } select_data;
+ static size_t select_size = sizeof(select_data);
+ SANE_Status status;
+ DBG (11, ">> mode_sense\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_MODE_SENSE;
+ cmd.page_code = page_code;
+ cmd.len = sizeof(select_data);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &select_data, &select_size);
+ memcpy (mp, &select_data.mp, sizeof(*mp));
+
+ DBG (11, "<< mode_sense\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+trigger_scan (int fd)
+{
+ static struct scsi_start_scan_cmd cmd;
+ static char window_id_list[1] = { '\0' }; /* scan start data out */
+ static size_t wl_size = 1;
+ SANE_Status status;
+ DBG (11, ">> trigger scan\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_START_SCAN;
+ cmd.len = wl_size;
+/* next line by mf */
+/* cmd.unused[0] = 1; */
+ if (wl_size)
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &window_id_list, &wl_size);
+ else
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< trigger scan\n");
+ return (status);
+}
+
+static SANE_Status
+set_window (int fd, struct ibm_window_data *iwd)
+{
+
+ static struct {
+ struct scsi_window_cmd cmd;
+ struct ibm_window_data iwd;
+ } win;
+
+ SANE_Status status;
+ DBG (11, ">> set_window\n");
+
+ memset (&win, 0, sizeof (win));
+ win.cmd.opcode = IBM_SCSI_SET_WINDOW;
+ _lto3b(sizeof(*iwd), win.cmd.len);
+ memcpy (&win.iwd, iwd, sizeof(*iwd));
+ status = sanei_scsi_cmd (fd, &win, sizeof (win), 0, 0);
+
+ DBG (11, "<< set_window\n");
+ return (status);
+}
+
+static SANE_Status
+get_window (int fd, struct ibm_window_data *iwd)
+{
+
+ static struct scsi_window_cmd cmd;
+ static size_t iwd_size;
+ SANE_Status status;
+
+ iwd_size = sizeof(*iwd);
+ DBG (11, ">> get_window datalen = %lu\n", (unsigned long) iwd_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_GET_WINDOW;
+#if 1 /* it was if 0 */
+ cmd.byte2 |= (SANE_Byte)0x01; /* set Single bit to get one window desc. */
+#endif
+ _lto3b(iwd_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), iwd, &iwd_size);
+
+ DBG (11, "<< get_window, datalen = %lu\n", (unsigned long) iwd_size);
+ return (status);
+}
+
+static SANE_Status
+read_data (int fd, void *buf, size_t * buf_size)
+{
+ static struct scsi_read_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (11, ">> read_data %lu\n", (unsigned long) *buf_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_READ_SCANNED_DATA;
+ _lto3b(*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, "<< read_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+
+static SANE_Status
+object_position (int fd, int load)
+{
+ static struct scsi_object_position_cmd cmd;
+ SANE_Status status;
+ DBG (11, ">> object_position\n");
+
+#if 0
+ /* At least the Ricoh 420 doesn't like that command */
+ DBG (11, "object_position: ignored\n");
+ return SANE_STATUS_GOOD;
+#endif
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_OBJECT_POSITION;
+ if (load)
+ cmd.position_func = OBJECT_POSITION_LOAD;
+ else
+ cmd.position_func = OBJECT_POSITION_UNLOAD;
+ _lto3b(1, cmd.count);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< object_position\n");
+ return (status);
+}
+
+static SANE_Status
+get_data_status (int fd, struct scsi_status_desc *dbs)
+{
+ static struct scsi_get_buffer_status_cmd cmd;
+ static struct scsi_status_data ssd;
+ size_t ssd_size = sizeof(ssd);
+ SANE_Status status;
+ DBG (11, ">> get_data_status %lu\n", (unsigned long) ssd_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_GET_BUFFER_STATUS;
+ _lto2b(ssd_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &ssd, &ssd_size);
+
+ memcpy (dbs, &ssd.desc, sizeof(*dbs));
+ if (status == SANE_STATUS_GOOD &&
+ ((unsigned int) _3btol(ssd.len) <= sizeof(*dbs) || _3btol(ssd.desc.filled) == 0)) {
+ DBG (11, "get_data_status: busy\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ DBG (11, "<< get_data_status %lu\n", (unsigned long) ssd_size);
+ return (status);
+}
+
+#if 0
+static SANE_Status
+ibm_wait_ready_tur (int fd)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG(3, "scsi_wait_ready: sending TEST_UNIT_READY\n");
+
+ status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0);
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG(1, "scsi_wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG(1, "ibm_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+#endif
+
+static SANE_Status
+ibm_wait_ready (Ibm_Scanner * s)
+{
+ struct scsi_status_desc dbs;
+ time_t now, start;
+ SANE_Status status;
+
+ start = time(NULL);
+
+ while (1)
+ {
+ status = get_data_status (s->fd, &dbs);
+
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG(1, "scsi_wait_ready: get datat status failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ now = time(NULL);
+ if (now - start >= MAX_WAITING_TIME)
+ {
+ DBG(1, "ibm_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now - start));
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_STATUS_GOOD:
+ DBG(11, "ibm_wait_ready: %d bytes ready\n", _3btol(dbs.filled));
+ return status;
+ break;
+ }
+ usleep (1000000); /* retry after 100ms */
+ }
+ return SANE_STATUS_INVAL;
+}
diff --git a/backend/ibm.c b/backend/ibm.c
new file mode 100644
index 0000000..729c140
--- /dev/null
+++ b/backend/ibm.c
@@ -0,0 +1,1183 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ This file implements a SANE backend for the Ibm 2456 flatbed scanner,
+ written by mf <massifr@tiscalinet.it>. It derives from the backend for
+ Ricoh flatbed scanners written by Feico W. Dillema.
+
+ Currently maintained by Henning Meier-Geinitz <henning@meier-geinitz.de>.
+*/
+
+#define BUILD 5
+
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+#define BACKEND_NAME ibm
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define IBM_CONFIG_FILE "ibm.conf"
+
+#include "ibm.h"
+#include "ibm-scsi.c"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+static int num_devices = 0;
+static Ibm_Device *first_dev = NULL;
+static Ibm_Scanner *first_handle = NULL;
+/* static int is50 = 0; */
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (11, ">> max_string_size\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (11, "<< max_string_size\n");
+ return max_size;
+}
+
+static SANE_Status
+attach (const char *devnam, Ibm_Device ** devp)
+{
+ SANE_Status status;
+ Ibm_Device *dev;
+
+ int fd;
+ struct inquiry_data ibuf;
+ struct measurements_units_page mup;
+ struct ibm_window_data wbuf;
+ size_t buf_size;
+ char *str;
+ DBG (11, ">> attach\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ DBG (3, "attach: opening %s\n", devnam);
+ status = sanei_scsi_open (devnam, &fd, NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (3, "attach: sending INQUIRY\n");
+ memset (&ibuf, 0, sizeof (ibuf));
+ buf_size = sizeof(ibuf);
+/* next line by mf */
+ ibuf.byte2 = 2;
+ status = inquiry (fd, &ibuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ if (ibuf.devtype != 6)
+ {
+ DBG (1, "attach: device \"%s\" is not a scanner\n", devnam);
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ if (!(
+ (strncmp ((char *)ibuf.vendor, "IBM", 3) ==0
+ && strncmp ((char *)ibuf.product, "2456", 4) == 0)
+ || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0
+ && strncmp ((char *)ibuf.product, "IS420", 5) == 0)
+ || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0
+ && strncmp ((char *)ibuf.product, "IS410", 5) == 0)
+ || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0
+ && strncmp ((char *)ibuf.product, "IS430", 5) == 0)
+ ))
+ {
+ DBG (1, "attach: device \"%s\" doesn't look like a scanner I know\n",
+ devnam);
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+ /*
+ * Causes a problem with RICOH IS420
+ * Ignore this function ... seems to work ok
+ * Suggested to George Murphy george@topfloor.ie by henning
+ */
+ if (strncmp((char *)ibuf.vendor, "RICOH", 5) != 0
+ && strncmp((char *)ibuf.product, "IS420", 5) != 0)
+ {
+ DBG (3, "attach: sending OBJECT POSITION\n");
+ status = object_position (fd, OBJECT_POSITION_UNLOAD);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: OBJECT POSTITION failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+
+ memset (&mup, 0, sizeof (mup));
+ mup.page_code = MEASUREMENTS_PAGE;
+ mup.parameter_length = 0x06;
+ mup.bmu = INCHES;
+ mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff;
+ mup.mud[1] = (DEFAULT_MUD & 0xff);
+
+#if 0
+ DBG (3, "attach: sending MODE SELECT\n");
+ status = mode_select (fd, (struct mode_pages *) &mup);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+#endif
+
+#if 0
+ DBG (3, "attach: sending MODE SENSE\n");
+ memset (&mup, 0, sizeof (mup));
+ status = mode_sense (fd, (struct mode_pages *) &mup, PC_CURRENT | MEASUREMENTS_PAGE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+#endif
+
+ DBG (3, "attach: sending GET WINDOW\n");
+ memset (&wbuf, 0, sizeof (wbuf));
+ status = get_window (fd, &wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET_WINDOW failed %d\n", status);
+ sanei_scsi_close (fd);
+ DBG (11, "<< attach\n");
+ return (SANE_STATUS_INVAL);
+ }
+
+ sanei_scsi_close (fd);
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devnam);
+ dev->sane.vendor = "IBM";
+ str = malloc (16 + 1);
+ memset (str, 0, sizeof (str));
+ strncpy (str, (char *)ibuf.product, sizeof(ibuf.product));
+ strncpy (str + sizeof(ibuf.revision), (char *)ibuf.revision, sizeof(ibuf.revision));
+ str[sizeof(ibuf.product) + sizeof(ibuf.revision)] = '\0';
+ dev->sane.model = str;
+ dev->sane.type = "flatbed scanner";
+
+ DBG (5, "dev->sane.name = %s\n", dev->sane.name);
+ DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor);
+ DBG (5, "dev->sane.model = %s\n", dev->sane.model);
+ DBG (5, "dev->sane.type = %s\n", dev->sane.type);
+
+ dev->info.xres_default = _2btol(wbuf.x_res);
+ dev->info.yres_default = _2btol(wbuf.y_res);
+ dev->info.image_mode_default = wbuf.image_comp;
+
+ /* if you throw the MRIF bit the brighness control reverses too */
+ /* so I reverse the reversal in software for symmetry's sake */
+ /* I should make this into an option */
+
+ if (wbuf.image_comp == IBM_GRAYSCALE || wbuf.image_comp == IBM_DITHERED_MONOCHROME)
+ {
+ dev->info.brightness_default = 256 - wbuf.brightness;
+/*
+ if (is50)
+ dev->info.contrast_default = wbuf.contrast;
+ else
+*/
+ dev->info.contrast_default = 256 - wbuf.contrast;
+ }
+ else /* wbuf.image_comp == IBM_BINARY_MONOCHROME */
+ {
+ dev->info.brightness_default = wbuf.brightness;
+ dev->info.contrast_default = wbuf.contrast;
+ }
+
+/* da rivedere
+ dev->info.adf_default = wbuf.adf_state;
+*/
+ dev->info.adf_default = ADF_UNUSED;
+ dev->info.adf_default = IBM_PAPER_USER_DEFINED;
+
+#if 1
+ dev->info.bmu = mup.bmu;
+ dev->info.mud = _2btol(mup.mud);
+ if (dev->info.mud == 0) {
+ /* The Ricoh says it uses points as default Basic Measurement Unit */
+ /* but gives a Measurement Unit Divisor of zero */
+ /* So, we set it to the default (SCSI-standard) of 1200 */
+ /* with BMU in inches, i.e. 1200 points equal 1 inch */
+ dev->info.bmu = INCHES;
+ dev->info.mud = DEFAULT_MUD;
+ }
+#else
+ dev->info.bmu = INCHES;
+ dev->info.mud = DEFAULT_MUD;
+#endif
+
+ DBG (5, "xres_default=%d\n", dev->info.xres_default);
+ DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max);
+ DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min);
+
+ DBG (5, "yres_default=%d\n", dev->info.yres_default);
+ DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max);
+ DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min);
+
+ DBG (5, "x_range.max=%d\n", dev->info.x_range.max);
+ DBG (5, "y_range.max=%d\n", dev->info.y_range.max);
+
+ DBG (5, "image_mode=%d\n", dev->info.image_mode_default);
+
+ DBG (5, "brightness=%d\n", dev->info.brightness_default);
+ DBG (5, "contrast=%d\n", dev->info.contrast_default);
+
+ DBG (5, "adf_state=%d\n", dev->info.adf_default);
+
+ DBG (5, "bmu=%d\n", dev->info.bmu);
+ DBG (5, "mud=%d\n", dev->info.mud);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (11, "<< attach\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+attach_one(const char *devnam)
+{
+ attach (devnam, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (Ibm_Scanner * s)
+{
+ int i;
+ DBG (11, ">> init_options\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[s->hw->info.image_mode_default]);
+
+ /* x resolution */
+ s->opt[OPT_X_RESOLUTION].name = "X" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = "X " SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &ibm2456_res_range;
+ s->val[OPT_X_RESOLUTION].w = s->hw->info.xres_default;
+/*
+ if (is50)
+ s->opt[OPT_X_RESOLUTION].constraint.range = &is50_res_range;
+ else
+*/
+ s->opt[OPT_X_RESOLUTION].constraint.range = &ibm2456_res_range;
+
+ /* y resolution */
+ s->opt[OPT_Y_RESOLUTION].name = "Y" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = "Y " SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_Y_RESOLUTION].w = s->hw->info.yres_default;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &ibm2456_res_range;
+
+ /* adf */
+ s->opt[OPT_ADF].name = "adf";
+ s->opt[OPT_ADF].title = "Use ADF";
+ s->opt[OPT_ADF].desc = "Uses the automatic document feeder.";
+ s->opt[OPT_ADF].type = SANE_TYPE_BOOL;
+ s->opt[OPT_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_ADF].b = s->hw->info.adf_default;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* paper */
+ s->opt[OPT_PAPER].name = "paper";
+ s->opt[OPT_PAPER].title = "Paper format";
+ s->opt[OPT_PAPER].desc = "Sets the paper format.";
+ s->opt[OPT_PAPER].type = SANE_TYPE_STRING;
+ s->opt[OPT_PAPER].size = max_string_size (paper_list);
+ s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER].constraint.string_list = paper_list;
+ s->val[OPT_PAPER].s = strdup (paper_list[s->hw->info.paper_default]);
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &default_x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &default_y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &default_x_range;
+ s->val[OPT_BR_X].w = default_x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &default_y_range;
+ s->val[OPT_BR_Y].w = default_y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range;
+ s->val[OPT_BRIGHTNESS].w = s->hw->info.brightness_default;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &u8_range;
+ s->val[OPT_CONTRAST].w = s->hw->info.contrast_default;
+
+ DBG (11, "<< init_options\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (Ibm_Scanner * s)
+{
+ SANE_Status status;
+ DBG (11, ">> do_cancel\n");
+
+ DBG (3, "cancel: sending OBJECT POSITION\n");
+ status = object_position (s->fd, OBJECT_POSITION_UNLOAD);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "cancel: OBJECT POSTITION failed\n");
+ }
+
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (11, "<< do_cancel\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ FILE *fp;
+
+ DBG_INIT ();
+ DBG (11, ">> sane_init (authorize %s null)\n", (authorize) ? "!=" : "==");
+
+#if defined PACKAGE && defined VERSION
+ DBG (2, "sane_init: ibm backend version %d.%d-%d ("
+ PACKAGE " " VERSION ")\n", SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+#endif
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open(IBM_CONFIG_FILE);
+ if (fp)
+ {
+ char line[PATH_MAX], *lp;
+ size_t len;
+
+ /* read config file */
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ /* skip white space: */
+ for (lp = line; isspace(*lp); ++lp);
+ strcpy (devnam, lp);
+ }
+ fclose (fp);
+ }
+ sanei_config_attach_matching_devices (devnam, attach_one);
+ DBG (11, "<< sane_init\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Ibm_Device *dev, *next;
+ DBG (11, ">> sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+
+ DBG (11, "<< sane_exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Ibm_Device *dev;
+ int i;
+ DBG (11, ">> sane_get_devices (local_only = %d)\n", local_only);
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (11, "<< sane_get_devices\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ Ibm_Device *dev;
+ Ibm_Scanner *s;
+ DBG (11, ">> sane_open\n");
+
+ if (devnam[0] == '\0')
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ init_options (s);
+
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (11, "<< sane_open\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Ibm_Scanner *s = (Ibm_Scanner *) handle;
+ DBG (11, ">> sane_close\n");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+ free (s);
+
+ DBG (11, ">> sane_close\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Ibm_Scanner *s = handle;
+ DBG (11, ">> sane_get_option_descriptor\n");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (11, "<< sane_get_option_descriptor\n");
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Ibm_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ DBG (11, ">> sane_control_option\n");
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (11, "sane_control_option get_value\n");
+ switch (option)
+ {
+ /* word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ *(SANE_Word *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* bool options: */
+ case OPT_ADF:
+ *(SANE_Bool *) val = s->val[option].b;
+ return (SANE_STATUS_GOOD);
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_PAPER:
+ strcpy (val, s->val[option].s);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else {
+ DBG (11, "sane_control_option set_value\n");
+ if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ s->val[option].w = *(SANE_Word *) val;
+ /* resets the paper format to user defined */
+ if (strcmp(s->val[OPT_PAPER].s, paper_list[IBM_PAPER_USER_DEFINED]) != 0)
+ {
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[OPT_PAPER].s)
+ free (s->val[OPT_PAPER].s);
+ s->val[OPT_PAPER].s = strdup (paper_list[IBM_PAPER_USER_DEFINED]);
+ }
+ return (SANE_STATUS_GOOD);
+
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_MODE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_ADF:
+ s->val[option].b = *(SANE_Bool *) val;
+ if (*(SANE_Bool *) val)
+ s->adf_state = ADF_ARMED;
+ else
+ s->adf_state = ADF_UNUSED;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_PAPER:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[OPT_PAPER].s, "User") != 0)
+ {
+ s->val[OPT_TL_X].w = 0;
+ s->val[OPT_TL_Y].w = 0;
+ if (strcmp (s->val[OPT_PAPER].s, "A3") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_A3_W;
+ s->val[OPT_BR_Y].w = PAPER_A3_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "A4") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_A4_W;
+ s->val[OPT_BR_Y].w = PAPER_A4_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "A4R") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_A4R_W;
+ s->val[OPT_BR_Y].w = PAPER_A4R_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "A5") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_A5_W;
+ s->val[OPT_BR_Y].w = PAPER_A5_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "A5R") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_A5R_W;
+ s->val[OPT_BR_Y].w = PAPER_A5R_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "A6") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_A6_W;
+ s->val[OPT_BR_Y].w = PAPER_A6_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "B4") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_B4_W;
+ s->val[OPT_BR_Y].w = PAPER_B4_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "Legal") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_LEGAL_W;
+ s->val[OPT_BR_Y].w = PAPER_LEGAL_H;
+ }
+ else if (strcmp (s->val[OPT_PAPER].s, "Letter") == 0)
+ {
+ s->val[OPT_BR_X].w = PAPER_LETTER_W;
+ s->val[OPT_BR_Y].w = PAPER_LETTER_H;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+ }
+
+ }
+ }
+
+ DBG (11, "<< sane_control_option\n");
+ return (SANE_STATUS_INVAL);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Ibm_Scanner *s = handle;
+ DBG (11, ">> sane_get_parameters\n");
+
+ if (!s->scanning)
+ {
+ int width, length, xres, yres;
+ const char *mode;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w;
+ length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w;
+ xres = s->val[OPT_X_RESOLUTION].w;
+ yres = s->val[OPT_Y_RESOLUTION].w;
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (xres > 0 && yres > 0 && width > 0 && length > 0)
+ {
+ s->params.pixels_per_line = width * xres / s->hw->info.mud;
+ s->params.lines = length * yres / s->hw->info.mud;
+ }
+
+ mode = s->val[OPT_MODE].s;
+ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) ||
+ (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE)) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ /* the Ibm truncates to the byte boundary, so: chop! */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ s->params.depth = 1;
+ }
+ else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ s->params.last_frame = SANE_TRUE;
+ }
+ else
+ DBG (5, "sane_get_parameters: scanning, so can't get params\n");
+
+ if (params)
+ *params = s->params;
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w);
+
+ DBG (11, "<< sane_get_parameters\n");
+ return (SANE_STATUS_GOOD);
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char *mode_str;
+ Ibm_Scanner *s = handle;
+ SANE_Status status;
+ struct ibm_window_data wbuf;
+ struct measurements_units_page mup;
+
+ DBG (11, ">> sane_start\n");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return (status);
+ }
+
+ mode_str = s->val[OPT_MODE].s;
+ s->xres = s->val[OPT_X_RESOLUTION].w;
+ s->yres = s->val[OPT_Y_RESOLUTION].w;
+ s->ulx = s->val[OPT_TL_X].w;
+ s->uly = s->val[OPT_TL_Y].w;
+ s->width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w;
+ s->length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w;
+ s->brightness = s->val[OPT_BRIGHTNESS].w;
+ s->contrast = s->val[OPT_CONTRAST].w;
+ s->bpp = s->params.depth;
+ if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->image_composition = IBM_BINARY_MONOCHROME;
+ }
+ else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ {
+ s->image_composition = IBM_DITHERED_MONOCHROME;
+ }
+ else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ s->image_composition = IBM_GRAYSCALE;
+ }
+
+ memset (&wbuf, 0, sizeof (wbuf));
+/* next line commented out by mf */
+/* _lto2b(sizeof(wbuf) - 8, wbuf.len); */
+/* next line by mf */
+ _lto2b(IBM_WINDOW_DATA_SIZE, wbuf.len); /* size=320 */
+ _lto2b(s->xres, wbuf.x_res);
+ _lto2b(s->yres, wbuf.y_res);
+ _lto4b(s->ulx, wbuf.x_org);
+ _lto4b(s->uly, wbuf.y_org);
+ _lto4b(s->width, wbuf.width);
+ _lto4b(s->length, wbuf.length);
+
+ wbuf.image_comp = s->image_composition;
+ /* if you throw the MRIF bit the brighness control reverses too */
+ /* so I reverse the reversal in software for symmetry's sake */
+ if (wbuf.image_comp == IBM_GRAYSCALE || wbuf.image_comp == IBM_DITHERED_MONOCHROME)
+ {
+ if (wbuf.image_comp == IBM_GRAYSCALE)
+ wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x80; /* it was 0x90 */
+ if (wbuf.image_comp == IBM_DITHERED_MONOCHROME)
+ wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x10;
+ wbuf.brightness = 256 - (SANE_Byte) s->brightness;
+/*
+ if (is50)
+ wbuf.contrast = (SANE_Byte) s->contrast;
+ else
+*/
+ wbuf.contrast = 256 - (SANE_Byte) s->contrast;
+ }
+ else /* wbuf.image_comp == IBM_BINARY_MONOCHROME */
+ {
+ wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x00;
+ wbuf.brightness = (SANE_Byte) s->brightness;
+ wbuf.contrast = (SANE_Byte) s->contrast;
+ }
+
+ wbuf.threshold = 0;
+ wbuf.bits_per_pixel = s->bpp;
+
+ wbuf.halftone_code = 2; /* diithering */
+ wbuf.halftone_id = 0x0A; /* 8x8 Bayer pattenr */
+ wbuf.pad_type = 3;
+ wbuf.bit_ordering[0] = 0;
+ wbuf.bit_ordering[1] = 7; /* modified by mf (it was 3) */
+
+ DBG (5, "xres=%d\n", _2btol(wbuf.x_res));
+ DBG (5, "yres=%d\n", _2btol(wbuf.y_res));
+ DBG (5, "ulx=%d\n", _4btol(wbuf.x_org));
+ DBG (5, "uly=%d\n", _4btol(wbuf.y_org));
+ DBG (5, "width=%d\n", _4btol(wbuf.width));
+ DBG (5, "length=%d\n", _4btol(wbuf.length));
+ DBG (5, "image_comp=%d\n", wbuf.image_comp);
+
+ DBG (11, "sane_start: sending SET WINDOW\n");
+ status = set_window (s->fd, &wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (11, "sane_start: sending GET WINDOW\n");
+ memset (&wbuf, 0, sizeof (wbuf));
+ status = get_window (s->fd, &wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ DBG (5, "xres=%d\n", _2btol(wbuf.x_res));
+ DBG (5, "yres=%d\n", _2btol(wbuf.y_res));
+ DBG (5, "ulx=%d\n", _4btol(wbuf.x_org));
+ DBG (5, "uly=%d\n", _4btol(wbuf.y_org));
+ DBG (5, "width=%d\n", _4btol(wbuf.width));
+ DBG (5, "length=%d\n", _4btol(wbuf.length));
+ DBG (5, "image_comp=%d\n", wbuf.image_comp);
+
+ DBG (11, "sane_start: sending MODE SELECT\n");
+ memset (&mup, 0, sizeof (mup));
+ mup.page_code = MEASUREMENTS_PAGE;
+ mup.parameter_length = 0x06;
+ mup.bmu = INCHES;
+ mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff;
+ mup.mud[1] = (DEFAULT_MUD & 0xff);
+/* next lines by mf */
+ mup.adf_page_code = 0x26;
+ mup.adf_parameter_length = 6;
+ if (s->adf_state == ADF_ARMED)
+ mup.adf_control = 1;
+ else
+ mup.adf_control = 0;
+/* end lines by mf */
+
+ status = mode_select (s->fd, (struct mode_pages *) &mup);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT failed\n");
+ return (SANE_STATUS_INVAL);
+ }
+
+ status = trigger_scan (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
+ /* next line introduced not to freeze xscanimage */
+ do_cancel(s);
+ return status;
+ }
+
+ /* Wait for scanner to become ready to transmit data */
+ status = ibm_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w);
+
+ s->scanning = SANE_TRUE;
+
+ DBG (11, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Ibm_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ DBG (11, ">> sane_read\n");
+
+ *len = 0;
+
+ DBG (11, "sane_read: bytes left to read: %ld\n", (u_long) s->bytes_to_read);
+
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning) {
+ DBG (11, "sane_read: scanning is false!\n");
+ return (do_cancel (s));
+ }
+
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+
+ DBG (11, "sane_read: read %ld bytes\n", (u_long) nread);
+ status = read_data (s->fd, buf, &nread);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (11, "sane_read: read error\n");
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+
+ DBG (11, "<< sane_read\n");
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Ibm_Scanner *s = handle;
+ DBG (11, ">> sane_cancel\n");
+
+ s->scanning = SANE_FALSE;
+
+ DBG (11, "<< sane_cancel\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (5, ">> sane_set_io_mode (handle = %p, non_blocking = %d)\n",
+ handle, non_blocking);
+ DBG (5, "<< sane_set_io_mode\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (5, ">> sane_get_select_fd (handle = %p, fd = %p)\n",
+ handle, (void *) fd);
+ DBG (5, "<< sane_get_select_fd\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/ibm.conf.in b/backend/ibm.conf.in
new file mode 100644
index 0000000..e9d4d21
--- /dev/null
+++ b/backend/ibm.conf.in
@@ -0,0 +1,3 @@
+scsi IBM 2456
+scsi RICOH
+/dev/scanner
diff --git a/backend/ibm.h b/backend/ibm.h
new file mode 100644
index 0000000..a10c923
--- /dev/null
+++ b/backend/ibm.h
@@ -0,0 +1,386 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+
+
+#ifndef ibm_h
+#define ibm_h 1
+
+#include <sys/types.h>
+
+#include "../include/sane/config.h"
+
+/* defines for scan_image_mode field */
+#define IBM_BINARY_MONOCHROME 0
+#define IBM_DITHERED_MONOCHROME 1
+#define IBM_GRAYSCALE 2
+
+/* defines for paper field */
+#define IBM_PAPER_USER_DEFINED 0
+#define IBM_PAPER_A3 1
+#define IBM_PAPER_A4 2
+#define IBM_PAPER_A4L 3
+#define IBM_PAPER_A5 4
+#define IBM_PAPER_A5L 5
+#define IBM_PAPER_A6 6
+#define IBM_PAPER_B4 7
+#define IBM_PAPER_B5 8
+#define IBM_PAPER_LEGAL 9
+#define IBM_PAPER_LETTER 10
+
+/* sizes for mode parameter's base_measurement_unit */
+#define INCHES 0
+#define MILLIMETERS 1
+#define POINTS 2
+#define DEFAULT_MUD 1200
+#define MEASUREMENTS_PAGE (SANE_Byte)(0x03)
+
+/* Mode Page Control */
+#define PC_CURRENT 0x00
+#define PC_CHANGE 0x40
+#define PC_DEFAULT 0x80
+#define PC_SAVED 0xc0
+
+static const SANE_String_Const mode_list[] =
+ {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ 0
+ };
+
+static const SANE_String_Const paper_list[] =
+ {
+ "User",
+ "A3",
+ "A4", "A4R",
+ "A5", "A5R",
+ "A6",
+ "B4", "B5",
+ "Legal", "Letter",
+ 0
+ };
+
+#define PAPER_A3_W 14032
+#define PAPER_A3_H 19842
+#define PAPER_A4_W 9921
+#define PAPER_A4_H 14032
+#define PAPER_A4R_W 14032
+#define PAPER_A4R_H 9921
+#define PAPER_A5_W 7016
+#define PAPER_A5_H 9921
+#define PAPER_A5R_W 9921
+#define PAPER_A5R_H 7016
+#define PAPER_A6_W 4960
+#define PAPER_A6_H 7016
+#define PAPER_B4_W 11811
+#define PAPER_B4_H 16677
+#define PAPER_B5_W 8598
+#define PAPER_B5_H 12142
+#define PAPER_LEGAL_W 10200
+#define PAPER_LEGAL_H 16800
+#define PAPER_LETTER_W 10200
+#define PAPER_LETTER_H 13200
+
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+
+static const SANE_Range ibm2456_res_range =
+ {
+ 100, /* minimum */
+ 600, /* maximum */
+ 0 /* quantization */
+ };
+
+static const SANE_Range default_x_range =
+ {
+ 0, /* minimum */
+/* (SANE_Word) ( * DEFAULT_MUD), maximum */
+ 14032, /* maximum (found empirically for Gray mode) */
+ /* in Lineart mode it works till 14062 */
+ 2 /* quantization */
+ };
+
+static const SANE_Range default_y_range =
+ {
+ 0, /* minimum */
+/* (SANE_Word) (14 * DEFAULT_MUD), maximum */
+ 20410, /* maximum (found empirically) */
+ 2 /* quantization */
+ };
+
+
+
+static inline void
+_lto2b(SANE_Int val, SANE_Byte *bytes)
+
+{
+
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static inline void
+_lto3b(SANE_Int val, SANE_Byte *bytes)
+
+{
+
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static inline void
+_lto4b(SANE_Int val, SANE_Byte *bytes)
+{
+
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static inline SANE_Int
+_2btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 8) |
+ bytes[1];
+ return (rv);
+}
+
+static inline SANE_Int
+_3btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 16) |
+ (bytes[1] << 8) |
+ bytes[2];
+ return (rv);
+}
+
+static inline SANE_Int
+_4btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+ OPT_ADF,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_PAPER, /* predefined formats */
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ /* must come last: */
+ NUM_OPTIONS
+ }
+Ibm_Option;
+
+typedef struct Ibm_Info
+ {
+ SANE_Range xres_range;
+ SANE_Range yres_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+
+ SANE_Int xres_default;
+ SANE_Int yres_default;
+ SANE_Int image_mode_default;
+ SANE_Int paper_default;
+ SANE_Int brightness_default;
+ SANE_Int contrast_default;
+ SANE_Int adf_default;
+
+ SANE_Int bmu;
+ SANE_Int mud;
+ }
+Ibm_Info;
+
+typedef struct Ibm_Device
+ {
+ struct Ibm_Device *next;
+ SANE_Device sane;
+ Ibm_Info info;
+ }
+Ibm_Device;
+
+typedef struct Ibm_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct Ibm_Scanner *next;
+ int fd; /* SCSI filedescriptor */
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+ /* scanner dependent/low-level state: */
+ Ibm_Device *hw;
+
+ SANE_Int xres;
+ SANE_Int yres;
+ SANE_Int ulx;
+ SANE_Int uly;
+ SANE_Int width;
+ SANE_Int length;
+ SANE_Int brightness;
+ SANE_Int contrast;
+ SANE_Int image_composition;
+ SANE_Int bpp;
+ SANE_Bool reverse;
+/* next lines by mf */
+ SANE_Int adf_state;
+#define ADF_UNUSED 0 /* scan from flatbed, not ADF */
+#define ADF_ARMED 1 /* scan from ADF, everything's set up */
+#define ADF_CLEANUP 2 /* eject paper from ADF on close */
+/* end lines by mf */
+ size_t bytes_to_read;
+ int scanning;
+ }
+Ibm_Scanner;
+
+struct inquiry_data {
+ SANE_Byte devtype;
+ SANE_Byte byte2;
+ SANE_Byte byte3;
+ SANE_Byte byte4;
+ SANE_Byte byte5;
+ SANE_Byte res1[2];
+ SANE_Byte flags;
+ SANE_Byte vendor[8];
+ SANE_Byte product[8];
+ SANE_Byte revision[4];
+ SANE_Byte byte[60];
+};
+
+#define IBM_WINDOW_DATA_SIZE 320
+struct ibm_window_data {
+ /* header */
+ SANE_Byte reserved[6];
+ SANE_Byte len[2];
+ /* data */
+ SANE_Byte window_id; /* must be zero */
+ SANE_Byte reserved0;
+ SANE_Byte x_res[2];
+ SANE_Byte y_res[2];
+ SANE_Byte x_org[4];
+ SANE_Byte y_org[4];
+ SANE_Byte width[4];
+ SANE_Byte length[4];
+ SANE_Byte brightness;
+ SANE_Byte threshold;
+ SANE_Byte contrast;
+ SANE_Byte image_comp; /* image composition (data type) */
+ SANE_Byte bits_per_pixel;
+ SANE_Byte halftone_code; /* halftone_pattern[0] in ricoh.h */
+ SANE_Byte halftone_id; /* halftone_pattern[1] in ricoh.h */
+ SANE_Byte pad_type;
+ SANE_Byte bit_ordering[2];
+ SANE_Byte compression_type;
+ SANE_Byte compression_arg;
+ SANE_Byte res3[6];
+
+ /* Vendor Specific parameter byte(s) */
+ /* Ricoh specific, follow the scsi2 standard ones */
+ SANE_Byte byte1;
+ SANE_Byte byte2;
+ SANE_Byte mrif_filtering_gamma_id;
+ SANE_Byte byte3;
+ SANE_Byte byte4;
+ SANE_Byte binary_filter;
+ SANE_Byte reserved2[18];
+
+ SANE_Byte reserved3[256];
+
+};
+
+struct measurements_units_page {
+ SANE_Byte page_code; /* 0x03 */
+ SANE_Byte parameter_length; /* 0x06 */
+ SANE_Byte bmu;
+ SANE_Byte res1;
+ SANE_Byte mud[2];
+ SANE_Byte res2[2]; /* anybody know what `COH' may mean ??? */
+/* next 4 lines by mf */
+ SANE_Byte adf_page_code;
+ SANE_Byte adf_parameter_length;
+ SANE_Byte adf_control;
+ SANE_Byte res3[5];
+};
+
+struct mode_pages {
+ SANE_Byte page_code;
+ SANE_Byte parameter_length;
+ SANE_Byte rest[14]; /* modified by mf; it was 6; see above */
+#if 0
+ SANE_Byte more_pages[243]; /* maximum size 255 bytes (incl header) */
+#endif
+};
+
+
+#endif /* ibm_h */
diff --git a/backend/kodak-cmd.h b/backend/kodak-cmd.h
new file mode 100644
index 0000000..9f9880e
--- /dev/null
+++ b/backend/kodak-cmd.h
@@ -0,0 +1,645 @@
+#ifndef KODAK_CMD_H
+#define KODAK_CMD_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ *
+ * Please see to opening comments in kodak.c
+ */
+
+/* ==================================================================== */
+/* helper char array manipulation functions */
+
+static void
+setbitfield (unsigned char *pageaddr, int mask, int shift, int val)
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift);
+}
+
+static int
+getbitfield (unsigned char *pageaddr, int shift, int mask)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+static int
+getnbyte (unsigned char *pnt, int nbytes)
+{
+ unsigned int result = 0;
+ int i;
+
+ for (i = 0; i < nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+static void
+putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes)
+{
+ int i;
+
+ for (i = nbytes - 1; i >= 0; i--) {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+/* ==================================================================== */
+/* SCSI commands */
+
+#define set_SCSI_opcode(out, val) out[0]=val
+#define set_SCSI_lun(out, val) setbitfield(out + 1, 7, 5, val)
+
+/* ==================================================================== */
+/* TEST_UNIT_READY */
+
+#define TEST_UNIT_READY_code 0x00
+#define TEST_UNIT_READY_len 6
+
+/* ==================================================================== */
+/* REQUEST_SENSE */
+
+/* out */
+#define REQUEST_SENSE_code 0x03
+#define REQUEST_SENSE_len 6
+
+#define RS_return_size 0x12
+
+/* in */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 7, 1)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0, 0x7f)
+#define RS_error_code_current 0x70
+#define RS_error_code_deferred 0x71
+
+#define get_RS_segment_num(b) b[1]
+
+#define get_RS_filemark(b) getbitfield(b + 0x02, 7, 1)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 6, 1)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 5, 1)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0, 0x0f)
+
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+
+#define get_RS_additional_length(b) b[0x07]
+
+#define get_RS_cmd_info(b) getnbyte(b+8, 4)
+
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_FRUC(b) b[0x0e]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,7,1)
+#define get_RS_SKSB(b) getnbyte(b+0x0f, 3)
+
+/* ==================================================================== */
+/* INQUIRY */
+
+/* out */
+#define INQUIRY_code 0x12
+#define INQUIRY_len 6
+
+#define set_I_evpd(out, val) setbitfield(out + 1, 1, 0, val)
+#define set_I_page_code(out, val) out[0x02]=val
+#define I_page_code_default 0
+#define set_I_data_length(out,n) out[0x04]=n
+#define I_data_len 128
+
+/* in */
+#define get_I_periph_qual(in) getbitfield(in, 5, 7)
+#define I_periph_qual_valid 0x00
+#define I_periph_qual_nolun 0x03
+#define get_I_periph_devtype(in) getbitfield(in, 0, 0x1f)
+#define I_periph_devtype_scanner 0x06
+#define I_periph_devtype_unknown 0x1f
+
+/* dont use these, until vendor */
+#define get_I_rmb(in) getbitfield(in + 1, 7, 1)
+#define get_I_devtype_qual(in) getbitfield(in + 1, 0, 0x7f)
+
+#define get_I_iso_version(in) getbitfield(in + 2, 6, 3)
+#define get_I_ecma_version(in) getbitfield(in + 2, 3, 7)
+#define get_I_ansi_version(in) getbitfield(in + 2, 0, 7)
+
+#define get_I_aenc(in) getbitfield(in + 3, 7, 1)
+#define get_I_trmiop(in) getbitfield(in + 3, 6, 1)
+#define get_I_resonse_format(in) getbitfield(in + 3, 0, 0x0f)
+
+#define get_I_length(in) getnbyte(in + 4, 1)
+
+#define get_I_reladr(in) getbitfield(in + 7, 7, 1)
+#define get_I_wbus32(in) getbitfield(in + 7, 6, 1)
+#define get_I_wbus16(in) getbitfield(in + 7, 5, 1)
+#define get_I_sync(in) getbitfield(in + 7, 4, 1)
+#define get_I_linked(in) getbitfield(in + 7, 3, 1)
+#define get_I_cmdque(in) getbitfield(in + 7, 1, 1)
+#define get_I_sftre(in) getbitfield(in + 7, 0, 1)
+
+#define get_I_vendor(in, buf) strncpy(buf,(char *)in + 0x08, 0x08)
+#define get_I_product(in, buf) strncpy(buf,(char *)in + 0x10, 0x10)
+#define get_I_version(in, buf) strncpy(buf,(char *)in + 0x20, 0x04)
+#define get_I_build(in, buf) strncpy(buf,(char *)in + 0x24, 0x02)
+
+#define get_I_mf_disable(in) getbitfield(in + 38, 7, 1)
+#define get_I_checkdigit(in) getbitfield(in + 38, 6, 1)
+#define get_I_front_prism(in) getbitfield(in + 38, 5, 1)
+#define get_I_compressed_gray(in) getbitfield(in + 38, 4, 1)
+#define get_I_front_toggle(in) getbitfield(in + 38, 3, 1)
+#define get_I_front_dp1(in) getbitfield(in + 38, 2, 1)
+#define get_I_front_color(in) getbitfield(in + 38, 1, 1)
+#define get_I_front_atp(in) getbitfield(in + 38, 0, 1)
+
+#define get_I_dp1_180(in) getbitfield(in + 39, 7, 1)
+#define get_I_mf_pause(in) getbitfield(in + 39, 6, 1)
+#define get_I_rear_prism(in) getbitfield(in + 39, 5, 1)
+#define get_I_uncompressed_gray(in) getbitfield(in + 39, 4, 1)
+#define get_I_rear_toggle(in) getbitfield(in + 39, 3, 1)
+#define get_I_rear_dp1(in) getbitfield(in + 39, 2, 1)
+#define get_I_rear_color(in) getbitfield(in + 39, 1, 1)
+#define get_I_rear_atp(in) getbitfield(in + 39, 0, 1)
+
+#define get_I_min_bin_res(in) getnbyte(in + 40, 2)
+#define get_I_max_bin_res(in) getnbyte(in + 42, 2)
+#define get_I_min_col_res(in) getnbyte(in + 44, 2)
+#define get_I_max_col_res(in) getnbyte(in + 46, 2)
+
+#define get_I_max_image_width(in) getnbyte(in + 48, 4)
+#define get_I_max_image_length(in) getnbyte(in + 52, 4)
+
+/* 56-95 reserved */
+
+#define get_I_finecrop(in) getbitfield(in + 96, 7, 1)
+#define get_I_ithresh(in) getbitfield(in + 96, 6, 1)
+#define get_I_ecd(in) getbitfield(in + 96, 3, 1)
+#define get_I_vblr(in) getbitfield(in + 96, 2, 1)
+#define get_I_elevator(in) getbitfield(in + 96, 1, 1)
+#define get_I_relcrop(in) getbitfield(in + 96, 0, 1)
+
+#define get_I_cdeskew(in) getbitfield(in + 97, 7, 1)
+#define get_I_ia(in) getbitfield(in + 97, 6, 1)
+#define get_I_patch(in) getbitfield(in + 97, 5, 1)
+#define get_I_nullmode(in) getbitfield(in + 97, 4, 1)
+#define get_I_sabre(in) getbitfield(in + 97, 3, 1)
+#define get_I_lddds(in) getbitfield(in + 97, 2, 1)
+#define get_I_uddds(in) getbitfield(in + 97, 1, 1)
+#define get_I_fixedgap(in) getbitfield(in + 97, 0, 1)
+
+#define get_I_hr_printer(in) getbitfield(in + 98, 6, 1)
+#define get_I_elev_100_250(in) getbitfield(in + 98, 5, 1)
+#define get_I_udds_individual(in) getbitfield(in + 98, 4, 1)
+#define get_I_auto_color(in) getbitfield(in + 98, 3, 1)
+#define get_I_wb(in) getbitfield(in + 98, 2, 1)
+#define get_I_es(in) getbitfield(in + 98, 1, 1)
+#define get_I_fc(in) getbitfield(in + 98, 0, 1)
+
+#define get_I_max_rate(in) getnbyte(in + 99, 2)
+#define get_I_buffer_size(in) getnbyte(in + 101, 4)
+
+/* ==================================================================== */
+/* RESERVE UNIT */
+
+#define RESERVE_UNIT_code 0x16
+#define RESERVE_UNIT_len 6
+
+/* ==================================================================== */
+/* RELEASE UNIT */
+
+#define RELEASE_UNIT_code 0x17
+#define RELEASE_UNIT_len 6
+
+/* ==================================================================== */
+/* SCAN */
+
+#define SCAN_code 0x1b
+#define SCAN_len 10
+
+#define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val
+
+/* ==================================================================== */
+/* SEND DIAGNOSTIC */
+
+#define SEND_DIAGNOSTIC_code 0x1d
+#define SEND_DIAGNOSTIC_len 6
+
+/* ==================================================================== */
+/* SET WINDOW */
+
+#define SET_WINDOW_code 0x24
+#define SET_WINDOW_len 10
+
+/* transfer length is header+descriptor (8+64) */
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+/* ==================================================================== */
+/* GET WINDOW */
+
+#define GET_WINDOW_code 0x25
+#define GET_WINDOW_len 10
+
+#define set_GW_single(sb, val) setbitfield(sb + 1, 1, 0, val)
+#define set_GW_wid(sb, len) sb[5] = len
+
+/* window transfer length is for following header+descriptor (8+64) */
+#define set_GW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+/* ==================================================================== */
+/* WINDOW HEADER, used by get and set */
+
+#define WINDOW_HEADER_len 8
+
+/* header transfer length is for following descriptor (64) */
+#define set_WH_data_len(sb, len) putnbyte(sb, len, 2)
+#define set_WH_desc_len(sb, len) putnbyte(sb + 0x06, len, 2)
+
+/* ==================================================================== */
+/* WINDOW descriptor, used by get and set */
+
+#define WINDOW_DESCRIPTOR_len 64
+
+#define set_WD_wid(sb, val) sb[0]=val
+#define WD_wid_front_binary 0x01
+#define WD_wid_back_binary 0x02
+#define WD_wid_front_color 0x03
+#define WD_wid_back_color 0x04
+
+/* color/gray = 100,150,200,240,300 */
+/* binary = 200,240,300,400 */
+#define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2)
+#define get_WD_Xres(sb) getnbyte(sb + 0x02, 2)
+
+/* color/gray = 100,150,200,240,300 */
+/* binary = 200,240,300,400 */
+#define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2)
+#define get_WD_Yres(sb) getnbyte(sb + 0x04, 2)
+
+#define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4)
+#define get_WD_ULX(sb) getnbyte(sb + 0x06, 4)
+
+#define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4)
+#define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4)
+
+#define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4)
+#define get_WD_width(sb) getnbyte(sb + 0x0e, 4)
+
+#define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4)
+#define get_WD_length(sb) getnbyte(sb + 0x12, 4)
+
+#define set_WD_brightness(sb, val) sb[0x16] = val
+#define get_WD_brightness(sb) sb[0x16]
+
+#define set_WD_threshold(sb, val) sb[0x17] = val
+#define get_WD_threshold(sb) sb[0x17]
+
+#define set_WD_contrast(sb, val) sb[0x18] = val
+#define get_WD_contrast(sb) sb[0x18]
+
+#define set_WD_composition(sb, val) sb[0x19] = val
+#define get_WD_composition(sb) sb[0x19]
+#define WD_compo_LINEART 0
+#define WD_compo_HALFTONE 1
+#define WD_compo_MULTILEVEL 5
+
+/* 1, 8, or 24 */
+#define set_WD_bitsperpixel(sb, val) sb[0x1a] = val
+#define get_WD_bitsperpixel(sb) sb[0x1a]
+
+/* not used? */
+#define set_WD_halftone(sb, val) putnbyte(sb + 0x1b, val, 2)
+#define get_WD_halftone(sb) getnbyte(sb + 0x1b, 2)
+
+#define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val)
+#define get_WD_rif(sb) getbitfield(sb + 0x1d, 7, 1)
+
+/* always set to 1? */
+#define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2)
+#define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2)
+
+#define set_WD_compress_type(sb, val) sb[0x20] = val
+#define get_WD_compress_type(sb) sb[0x20]
+#define WD_compr_NONE 0
+#define WD_compr_FAXG4 3
+#define WD_compr_JPEG 0x80
+
+/* not used? */
+#define set_WD_compress_arg(sb, val) sb[0x21] = val
+#define get_WD_compress_arg(sb) sb[0x21]
+
+/* kodak specific stuff starts here */
+#define set_WD_noise_filter(sb, val) setbitfield(sb + 40, 7, 2, val)
+#define WD_nf_NONE 0
+#define WD_nf_LONE 1
+#define WD_nf_MAJORITY 2
+#define get_WD_noise_filter(sb) getbitfield(sb + 40, 7, 1)
+
+#define set_WD_allow_zero(sb, val) setbitfield(sb + 40, 1, 1, val)
+#define get_WD_allow_zero(sb, val) getbitfield(sb + 40, 1, 1)
+
+#define set_WD_vblr(sb, val) setbitfield(sb + 40, 1, 0, val)
+#define get_WD_vblr(sb, val) getbitfield(sb + 40, 0, 1)
+
+#define set_WD_image_overscan(sb, val) putnbyte(sb + 41, val, 2)
+
+#define set_WD_IS(sb, val) setbitfield(sb + 43, 1, 7, val)
+
+#define set_WD_deskew(sb, val) setbitfield(sb + 43, 1, 6, val)
+#define WD_deskew_DISABLE 0
+#define WD_deskew_ENABLE 1
+
+#define set_WD_BR(sb, val) setbitfield(sb + 43, 1, 5, val)
+#define set_WD_BF(sb, val) setbitfield(sb + 43, 1, 4, val)
+#define set_WD_ED(sb, val) setbitfield(sb + 43, 1, 3, val)
+#define set_WD_HR(sb, val) setbitfield(sb + 43, 1, 2, val)
+
+#define set_WD_cropping(sb, val) setbitfield(sb + 43, 3, 0, val)
+#define WD_crop_AUTO 0
+#define WD_crop_FIXED 1
+#define WD_crop_RELATIVE 2
+#define WD_crop_FINE 3
+
+#define set_WD_ithresh(sb, val) setbitfield(sb + 44, 1, 7, val)
+#define WD_ithresh_DISABLE 0
+#define WD_ithresh_ENABLE 1
+
+#define set_WD_dropout_color(sb, val) setbitfield(sb + 44, 7, 0, val)
+
+#define set_WD_dropout_bg(sb, val) sb[45] = val
+
+#define set_WD_dropout_thresh(sb, val) sb[46] = val
+
+#define set_WD_dropout_source(sb, val) sb[47] = val
+
+#define set_WD_disable_gamma(sb, val) sb[48] = val
+
+#define set_WD_ortho_rotation(sb, val) sb[49] = val
+
+#define set_WD_fine_crop_max(sb, val) putnbyte(sb + 50, val, 2)
+
+#define set_WD_color_detect_thresh(sb, val) sb[52] = val
+
+#define set_WD_color_detect_area(sb, val) sb[53] = val
+
+#define set_WD_border_add(sb, val) sb[54] = val
+
+#define max_WDB_size 0xc8
+
+/* ==================================================================== */
+/* READ */
+
+#define READ_code 0x28
+#define READ_len 10
+
+/* ==================================================================== */
+/* SEND */
+
+#define SEND_code 0x2a
+#define SEND_len 10
+
+/* ==================================================================== */
+/* READ and SEND are basically the same, so the following 'SR' macros */
+
+/* output */
+#define set_SR_datatype_code(sb, val) sb[0x02] = val
+#define SR_datatype_imagedata 0x00
+#define SR_datatype_random 0x80
+#define SR_datatype_imageheader 0x81
+#define SR_len_imageheader 1088
+
+#define set_SR_datatype_qual(sb, val) memcpy(sb + 4, val, 2)
+#define set_SR_xfer_length(sb, val) putnbyte(sb + 6, val, 3)
+
+/* if the data type is 'imageheader': */
+#define get_SR_ih_header_length(in) getnbyte(in + 0x00, 4)
+#define get_SR_ih_image_length(in) getnbyte(in + 0x04, 4)
+#define get_SR_ih_image_id(in) in[8]
+#define get_SR_ih_resolution(in) getnbyte(in + 9, 2)
+#define get_SR_ih_ulx(in) getnbyte(in + 11, 4)
+#define get_SR_ih_uly(in) getnbyte(in + 15, 4)
+#define get_SR_ih_width(in) getnbyte(in + 19, 4)
+#define get_SR_ih_length(in) getnbyte(in + 23, 4)
+#define get_SR_ih_bpp(in) in[27]
+#define get_SR_ih_comp_type(in) in[28]
+#define get_SR_ih_ACD(in) getbit(in + 29, 1, 5)
+#define get_SR_ih_MF(in) getbit(in + 29, 1, 4)
+#define get_SR_ih_RIF(in) getbit(in + 29, 1, 2)
+#define get_SR_ih_ID(in) getbit(in + 29, 1, 1)
+#define get_SR_ih_DE(in) getbit(in + 29, 1, 0)
+#define get_SR_ih_skew_angle(in) getnbyte(in + 30, 2)
+#define get_SR_ih_ia_level(in) in[32]
+#define get_SR_ih_ia(in) getnbyte(in + 33, 60)
+#define get_SR_ih_print_string(in) getnbyte(in + 93, 60)
+#define get_SR_ih_seqence_counter(in) getnbyte(in + 173, 4)
+#define get_SR_ih_ia_f1_definition(in) in[177]
+#define get_SR_ih_ia_f1_value(in) getnbyte(in + 178, 18)
+#define get_SR_ih_ia_f2_definition(in) in[196]
+#define get_SR_ih_ia_f2_value(in) getnbyte(in + 197, 18)
+#define get_SR_ih_ia_f3_definition(in) in[215]
+#define get_SR_ih_ia_f3_value(in) getnbyte(in + 216, 18)
+#define get_SR_ih_ia_f4_definition(in) in[234]
+#define get_SR_ih_ia_f5_value(in) getnbyte(in + 235, 18)
+#define get_SR_ih_PD(in) getbit(in + 253, 1, 7)
+#define get_SR_ih_patch_type(in) getbit(in + 253, 0x7f, 0)
+#define get_SR_ih_deskew_confidence(in) in[255]
+#define get_SR_ih_bt_contrast_perc(in) in[256]
+#define get_SR_ih_bt_contrast(in) getnbyte(in + 257, 2)
+#define get_SR_ih_bt_threshold(in) in[259]
+#define get_SR_ih_sum_histogram(in) getnbyte(in + 260, 256)
+#define get_SR_ih_diff_histogram(in) getnbyte(in + 516, 256)
+#define get_SR_ih_gamma_table(in) getnbyte(in + 772, 256)
+#define get_SR_ih_auto_color_area(in) in[1028]
+#define get_SR_ih_auto_color_thresh(in) in[1029]
+
+/* ==================================================================== */
+/* if the data type is 'random', we have all kinds of 2 byte */
+/* qualifiers and oddly sized commands. some are bidirectional */
+/* while others are only send or read */
+
+/* all payloads for these seem to start with 4 byte inclusive length? */
+#define set_SR_payload_len(sb, val) putnbyte(sb, val, 4)
+
+/* */
+#define SR_qual_jpeg_quant "JQ" /*both*/
+#define SR_len_jpeg_quant 262 /*front and back tables*/
+
+/* */
+#define SR_qual_config "SC" /*both*/
+#define SR_len_config 512 /*lots of stuff*/
+#define set_SR_sc_length(in,val) putnbyte(in, val, 4)
+#define get_SR_sc_length(in) getnbyte(in, 4)
+
+#define set_SR_sc_io1(in,val) putnbyte(in + 4, val, 1)
+#define get_SR_sc_io1(in) getnbyte(in + 4, 1)
+#define set_SR_sc_io2(in,val) putnbyte(in + 5, val, 1)
+#define get_SR_sc_io2(in) getnbyte(in + 5, 1)
+#define set_SR_sc_io3(in,val) putnbyte(in + 6, val, 1)
+#define get_SR_sc_io3(in) getnbyte(in + 6, 1)
+#define set_SR_sc_io4(in,val) putnbyte(in + 7, val, 1)
+#define get_SR_sc_io4(in) getnbyte(in + 7, 1)
+#define SR_sc_io_none 0
+#define SR_sc_io_front_binary 1
+#define SR_sc_io_rear_binary 2
+#define SR_sc_io_front_color 3
+#define SR_sc_io_rear_color 4
+
+#define set_SR_sc_trans_to(in,val) putnbyte(in + 13, val, 2)
+#define get_SR_sc_trans_to(in) getnbyte(in + 13, 2)
+#define set_SR_sc_trans_to_resp(in,val) putnbyte(in + 15, val, 1)
+#define get_SR_sc_trans_to_resp(in) getnbyte(in + 15, 1)
+
+#define get_SR_sc_DLS(in) getbitfield(in + 16, 7, 1)
+#define set_SR_sc_DLS(in, val) setbitfield(in + 16, 1, 7)
+#define get_SR_sc_DMS(in) getbitfield(in + 16, 6, 1)
+#define set_SR_sc_DMS(in, val) setbitfield(in + 16, 1, 6)
+#define get_SR_sc_DRS(in) getbitfield(in + 16, 5, 1)
+#define set_SR_sc_DRS(in, val) setbitfield(in + 16, 1, 5)
+#define get_SR_sc_UD_mode(in) getbitfield(in + 16, 0, 0x1f)
+#define set_SR_sc_UD_mode(in, val) setbitfield(in + 16, 0x1f, 0)
+#define get_SR_sc_LD_length(in) getnbyte(in + 17, 2)
+#define set_SR_sc_LD_length(in, val) setnbyte(in + 17, 2)
+#define get_SR_sc_DF_resp(in) getnbyte(in + 19, 1)
+#define set_SR_sc_DF_resp(in, val) setnbyte(in + 19, 1)
+
+#define get_SR_sc_TP_mode(in) getnbyte(in + 20, 1)
+#define get_SR_sc_CPT(in) getbitfield(in + 21, 0, 1)
+#define get_SR_sc_POD_mode(in) getnbyte(in + 22, 2)
+#define get_SR_sc_batch_mode(in) getnbyte(in + 24, 2)
+#define get_SR_sc_batch_level(in) getnbyte(in + 26, 1)
+#define get_SR_sc_start_batch(in) getnbyte(in + 27, 1)
+#define get_SR_sc_end_batch(in) getnbyte(in + 28, 1)
+#define get_SR_sc_patch_conf_tone(in) getnbyte(in + 29, 1)
+
+#define get_SR_sc_T6_patch(in) getbitfield(in + 30, 7, 1)
+#define get_SR_sc_T5_patch(in) getbitfield(in + 30, 6, 1)
+#define get_SR_sc_T4_patch(in) getbitfield(in + 30, 5, 1)
+#define get_SR_sc_T3_patch(in) getbitfield(in + 30, 4, 1)
+#define get_SR_sc_T2_patch(in) getbitfield(in + 30, 3, 1)
+#define get_SR_sc_T1_patch(in) getbitfield(in + 30, 2, 1)
+#define get_SR_sc_trans_patch(in) getbitfield(in + 30, 0, 3)
+
+#define get_SR_sc_IA_A_start(in) getnbyte(in + 31, 18)
+#define get_SR_sc_IA_B_start(in) getnbyte(in + 49, 18)
+#define get_SR_sc_IA_C_start(in) getnbyte(in + 67, 18)
+#define get_SR_sc_IA_D_start(in) getnbyte(in + 85, 18)
+
+#define get_SR_sc_IA_A_def(in) getnbyte(in + 103, 1)
+#define get_SR_sc_IA_B_def(in) getnbyte(in + 104, 1)
+#define get_SR_sc_IA_C_def(in) getnbyte(in + 105, 1)
+#define get_SR_sc_IA_D_def(in) getnbyte(in + 106, 1)
+
+#define get_SR_sc_IA_ltf_3(in) getnbyte(in + 107, 1)
+#define get_SR_sc_IA_ltf_2(in) getnbyte(in + 108, 1)
+#define get_SR_sc_IA_ltf_1(in) getnbyte(in + 109, 1)
+
+#define get_SR_sc_DP1_pos(in) getnbyte(in + 110, 4)
+#define get_SR_sc_hires_mode(in) getbitfield(in + 114, 4, 0xf)
+#define get_SR_sc_DP1_font(in) getbitfield(in + 114, 0, 0xf)
+#define get_SR_sc_DP1_orient(in) getnbyte(in + 115, 1)
+#define get_SR_sc_DP1_IA_format(in) getnbyte(in + 116, 1)
+#define get_SR_sc_DP1_date_format(in) getnbyte(in + 117, 1)
+#define get_SR_sc_DP1_date_delim(in) getnbyte(in + 118, 1)
+#define get_SR_sc_DP1_start_seq(in) getnbyte(in + 119, 4)
+#define get_SR_sc_DP1_seq_date_format(in) getnbyte(in + 123, 1)
+#define get_SR_sc_DP1_seq_print_width(in) getnbyte(in + 124, 1)
+
+#define get_SR_sc_DP1_msg1(in) getnbyte(in + 125, 40)
+#define get_SR_sc_DP1_msg2(in) getnbyte(in + 165, 40)
+#define get_SR_sc_DP1_msg3(in) getnbyte(in + 205, 40)
+#define get_SR_sc_DP1_msg4(in) getnbyte(in + 245, 40)
+#define get_SR_sc_DP1_msg5(in) getnbyte(in + 285, 40)
+#define get_SR_sc_DP1_msg6(in) getnbyte(in + 325, 40)
+#define get_SR_sc_DP1_l1_template(in) getnbyte(in + 365, 20)
+#define get_SR_sc_DP1_l2_template(in) getnbyte(in + 385, 20)
+#define get_SR_sc_DP1_l3_template(in) getnbyte(in + 405, 20)
+
+#define get_SR_sc_UDFK1(in) getnbyte(in + 425, 1)
+#define get_SR_sc_UDFK2(in) getnbyte(in + 426, 1)
+#define get_SR_sc_UDFK3(in) getnbyte(in + 427, 1)
+#define get_SR_sc_MPASS(in) getbitfield(in + 428, 1, 1)
+#define get_SR_sc_CE(in) getbitfield(in + 428, 0, 1)
+#define get_SR_sc_elevator_mode(in) getnbyte(in + 429, 1)
+#define get_SR_sc_stacking_mode(in) getnbyte(in + 430, 1)
+
+#define get_SR_sc_energy_star_to(in) getnbyte(in + 433, 1)
+
+#define get_SR_sc_PR1(in) getbitfield(in + 435, 3, 1)
+#define get_SR_sc_PR2(in) getbitfield(in + 435, 2, 1)
+#define get_SR_sc_PR3(in) getbitfield(in + 435, 1, 1)
+#define get_SR_sc_PR4(in) getbitfield(in + 435, 0, 1)
+
+/* */
+#define SR_qual_color_table "CT" /*read*/
+#define SR_len_color_table 52
+
+/* */
+#define SR_qual_color_table "CT" /*read*/
+#define SR_len_color_table 52
+
+/* */
+#define SR_qual_clear "CB" /*send*/
+#define SR_len_clear 0
+
+/* */
+#define SR_qual_end "GX" /*send*/
+#define SR_len_end 0
+
+/* local and gmt time commands, same format */
+#define SR_qual_clock "LC" /*both*/
+#define SR_qual_gmt "GT" /*send*/
+#define SR_len_time 10
+#define set_SR_time_hour(sb, val) putnbyte(sb+4, val, 1)
+#define set_SR_time_min(sb, val) putnbyte(sb+5, val, 1)
+#define set_SR_time_mon(sb, val) putnbyte(sb+6, val, 1)
+#define set_SR_time_day(sb, val) putnbyte(sb+7, val, 1)
+#define set_SR_time_year(sb, val) putnbyte(sb+8, val, 2)
+
+/* */
+#define SR_qual_lamp "LD" /*????*/
+#define SR_len_lamp 0 /*????*/
+
+/* */
+#define SR_qual_log_en "L0" /*read*/
+#define SR_qual_log_fr "L1" /*read*/
+#define SR_qual_log_it "L2" /*read*/
+#define SR_qual_log_de "L3" /*read*/
+#define SR_qual_log_br "L4" /*read*/
+#define SR_qual_log_jp "L5" /*read*/
+#define SR_qual_log_nl "L6" /*read*/
+#define SR_qual_log_es "L7" /*read*/
+#define SR_qual_log_cns "L8" /*read*/
+#define SR_qual_log_cnt "L9" /*read*/
+#define SR_qual_log_ko "LA" /*read*/
+#define SR_qual_log_ru "LB" /*read*/
+#define SR_qual_log_cz "LE" /*read*/
+#define SR_qual_log_tr "LF" /*read*/
+
+/* */
+#define SR_qual_startstop "SS" /*send*/
+#define SR_len_startstop 5
+#define set_SR_startstop_cmd(sb, val) putnbyte(sb+4, val, 1)
+
+/* */
+#define SR_qual_media "CM" /*read*/
+#define SR_len_media 5
+#define get_SR_media_status(sb, val) getnbyte(sb+4, 1)
+
+/* */
+#define SR_qual_img_cal "IC" /*send*/
+#define SR_len_img_cal 0
+
+/* */
+#define SR_qual_udds_cal "UC" /*send*/
+#define SR_len_udds_cal 0
+
+/* ==================================================================== */
+/* WRITE BUFFER */
+
+#define WRITE_BUFFER_code 0x3b
+#define WRITE_BUFFER_len 10
+
+#define set_WB_mode(sb, val) setbitfield(sb + 0x01, 7, 0, val)
+#define WB_mode_front 6
+#define WB_mode_back 7
+#define set_WB_buffer_id(sb, val) sb[0x02] = (unsigned char)val
+#define WB_buffer_id_block(sb, val) 0
+#define WB_buffer_id_last(sb, val) 1
+#define set_WB_buffer_offset(sb, val) putnbyte(sb + 3, val, 3)
+#define set_WB_list_length(sb, val) putnbyte(sb + 6, val, 3)
+
+#endif
diff --git a/backend/kodak.c b/backend/kodak.c
new file mode 100644
index 0000000..80a5700
--- /dev/null
+++ b/backend/kodak.c
@@ -0,0 +1,2913 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ This file implements a SANE backend for various large Kodak scanners.
+
+ The source code is divided in sections which you can easily find by
+ searching for the tag "@@".
+
+ Section 1 - Init & static stuff
+ Section 2 - sane_init, _get_devices, _open & friends
+ Section 3 - sane_*_option functions
+ Section 4 - sane_start, _get_param, _read & friends
+ Section 5 - sane_close functions
+ Section 6 - misc functions
+
+ Changes:
+ v0 through v5 2008-01-15, MAN
+ - development versions
+ v6 2009-06-22, MAN
+ - improved set_window() to build desciptor from scratch
+ - initial release
+ v7 2010-02-10, MAN
+ - add SANE_I18N to static strings
+ - don't fail if scsi buffer is too small
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . .
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . . - sane_get_parameters() : returns estimated scan parameters
+ . . - (repeat previous 3 functions)
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times; after sane_read returns EOF,
+ . . loop may continue with sane_start which may return a 2nd page
+ . . when doing duplex scans, or load the next page from the ADF)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+*/
+
+/*
+ * @@ Section 1 - Init
+ */
+
+#include "sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_LIBC_H
+# include <libc.h> /* NeXTStep/OpenStep */
+#endif
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#include "kodak-cmd.h"
+#include "kodak.h"
+
+#define DEBUG 1
+#define BUILD 7
+
+/* values for SANE_DEBUG_KODAK env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - scsi cmd trace 25
+ - scsi cmd detail 30
+ - useless noise 35
+*/
+
+/* ------------------------------------------------------------------------- */
+#define STRING_ADFFRONT SANE_I18N("ADF Front")
+#define STRING_ADFBACK SANE_I18N("ADF Back")
+#define STRING_ADFDUPLEX SANE_I18N("ADF Duplex")
+
+#define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+/* Also set via config file. */
+static int global_buffer_size = DEFAULT_BUFFER_SIZE;
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ * a ptr to a single-linked list of scanner structs
+ */
+static const SANE_Device **sane_devArray = NULL;
+static struct scanner *scanner_devList = NULL;
+
+/*
+ * @@ Section 2 - SANE & scanner init code
+ */
+
+/*
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init: start\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: kodak backend %d.%d.%d, from %s\n",
+ V_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ DBG (10, "sane_init: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ */
+/* Read the config file, find scanners with help from sanei_*
+ * store in two global lists of device structs
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ struct scanner *dev;
+ char line[PATH_MAX];
+ const char *lp;
+ FILE *fp;
+ int num_devices=0;
+ int i=0;
+
+ local_only = local_only; /* get rid of compiler warning */
+
+ DBG (10, "sane_get_devices: start\n");
+
+ /* set this to default before reading the file */
+ global_buffer_size = DEFAULT_BUFFER_SIZE;
+
+ fp = sanei_config_open (KODAK_CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n", KODAK_CONFIG_FILE);
+
+ while (sanei_config_read (line, PATH_MAX, fp)) {
+
+ lp = line;
+
+ /* ignore comments */
+ if (*lp == '#')
+ continue;
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if ((strncmp ("option", lp, 6) == 0) && isspace (lp[6])) {
+
+ lp += 6;
+ lp = sanei_config_skip_whitespace (lp);
+
+ /* we allow setting buffersize too big */
+ if ((strncmp (lp, "buffer-size", 11) == 0) && isspace (lp[11])) {
+
+ int buf;
+ lp += 11;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if (buf < 4096) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" \
+ (%d) is < 4096, ignoring!\n", buf);
+ continue;
+ }
+
+ if (buf > DEFAULT_BUFFER_SIZE) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" \
+ (%d) is > %d, warning!\n", buf, DEFAULT_BUFFER_SIZE);
+ }
+
+ DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n",
+ buf);
+ global_buffer_size = buf;
+ }
+ else {
+ DBG (5, "sane_get_devices: config option \"%s\" \
+ unrecognized\n", lp);
+ }
+ }
+ else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_config_attach_matching_devices (lp, attach_one);
+ }
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" unrecognized\n",
+ lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: no config file '%s', using defaults\n",
+ KODAK_CONFIG_FILE);
+ DBG (15, "sane_get_devices: looking for 'scsi KODAK'\n");
+ sanei_config_attach_matching_devices ("scsi KODAK", attach_one);
+ }
+
+ for (dev = scanner_devList; dev; dev=dev->next) {
+ DBG (15, "sane_get_devices: found scanner %s\n",dev->device_name);
+ num_devices++;
+ }
+
+ DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
+
+ sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
+ if (!sane_devArray)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = scanner_devList; dev; dev=dev->next) {
+ sane_devArray[i++] = (SANE_Device *)&dev->sane;
+ }
+
+ sane_devArray[i] = 0;
+
+ if(device_list){
+ *device_list = sane_devArray;
+ }
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* build the scanner struct and link to global list
+ * unless struct is already loaded, then pretend
+ */
+static SANE_Status
+attach_one (const char *device_name)
+{
+ struct scanner *s;
+ int ret;
+
+ DBG (10, "attach_one: start\n");
+ DBG (15, "attach_one: looking for '%s'\n", device_name);
+
+ for (s = scanner_devList; s; s = s->next) {
+ if (strcmp (s->sane.name, device_name) == 0) {
+ DBG (10, "attach_one: already attached!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* build a struct to hold it */
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* scsi command/data buffer */
+ s->buffer_size = global_buffer_size;
+
+ /* copy the device name */
+ s->device_name = strdup (device_name);
+ if (!s->device_name){
+ free (s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* connect the fd */
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ free (s->device_name);
+ free (s);
+ return ret;
+ }
+
+ /* Now query the device to load its vendor/model/version */
+ ret = init_inquire (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: inquiry failed\n");
+ return ret;
+ }
+
+ /* clean up the scanner struct based on model */
+ /* this is the only piece of model specific code */
+ ret = init_model (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: model failed\n");
+ return ret;
+ }
+
+ /* sets user 'values' to good defaults */
+ ret = init_user (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: user failed\n");
+ return ret;
+ }
+
+ /* sets SANE option 'values' to good defaults */
+ ret = init_options (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: options failed\n");
+ return ret;
+ }
+
+ /* we close the connection, so that another backend can talk to scanner */
+ disconnect_fd(s);
+
+ /* load info into sane_device struct */
+ s->sane.name = s->device_name;
+ s->sane.vendor = s->vendor_name;
+ s->sane.model = s->product_name;
+ s->sane.type = "scanner";
+
+ s->next = scanner_devList;
+ scanner_devList = s;
+
+ DBG (10, "attach_one: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * connect the fd in the scanner struct
+ */
+static SANE_Status
+connect_fd (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int buffer_size = s->buffer_size;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler,
+ s, &s->buffer_size);
+ if(!ret && buffer_size != s->buffer_size){
+ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n",
+ buffer_size, s->buffer_size);
+ }
+ else{
+ DBG (15, "connect_fd: opened SCSI device\n");
+ }
+ }
+
+ DBG (10, "connect_fd: finish %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * This routine will check if a certain device is a Kodak scanner
+ * It also copies interesting data from INQUIRY into the handle structure
+ */
+static SANE_Status
+init_inquire (struct scanner *s)
+{
+ int i;
+ SANE_Status ret;
+
+ unsigned char cmd[INQUIRY_len];
+ size_t cmdLen = INQUIRY_len;
+
+ unsigned char in[I_data_len];
+ size_t inLen = I_data_len;
+
+ DBG (10, "init_inquire: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, INQUIRY_code);
+ set_I_evpd (cmd, 0);
+ set_I_page_code (cmd, I_page_code_default);
+ set_I_data_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ if (get_I_periph_qual(in) != I_periph_qual_valid){
+ DBG (5, "The device at '%s' has invalid periph_qual.\n", s->device_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (get_I_periph_devtype(in) != I_periph_devtype_scanner){
+ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ get_I_vendor (in, s->vendor_name);
+ get_I_product (in, s->product_name);
+ get_I_version (in, s->version_name);
+ get_I_build (in, s->build_name);
+
+ s->vendor_name[8] = 0;
+ s->product_name[16] = 0;
+ s->version_name[4] = 0;
+ s->build_name[2] = 0;
+
+ /* gobble trailing spaces */
+ for (i = 7; s->vendor_name[i] == ' ' && i >= 0; i--)
+ s->vendor_name[i] = 0;
+ for (i = 15; s->product_name[i] == ' ' && i >= 0; i--)
+ s->product_name[i] = 0;
+ for (i = 3; s->version_name[i] == ' ' && i >= 0; i--)
+ s->version_name[i] = 0;
+ for (i = 2; s->build_name[i] == ' ' && i >= 0; i--)
+ s->build_name[i] = 0;
+
+ if (strcmp ("KODAK", s->vendor_name)) {
+ DBG (5, "The device at '%s' is reported to be made by '%s'\n", s->device_name, s->vendor_name);
+ DBG (5, "This backend only supports Kodak products.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "init_inquire: Found '%s' '%s' '%s' '%s' at '%s'\n",
+ s->vendor_name, s->product_name, s->version_name, s->build_name,
+ s->device_name);
+
+ /*defined in SCSI spec*/
+ DBG (15, "standard inquiry options\n");
+
+ /*FIXME: do we need to save these?*/
+ DBG (15, " PQ: %d\n",get_I_periph_qual(in));
+ DBG (15, " PDT: %d\n",get_I_periph_devtype(in));
+
+ DBG (15, " RMB: %d\n",get_I_rmb(in));
+ DBG (15, " DTQ: %d\n",get_I_devtype_qual(in));
+
+ DBG (15, " ISO: %d\n",get_I_iso_version(in));
+ DBG (15, " ECMA: %d\n",get_I_ecma_version(in));
+ DBG (15, " ANSI: %d\n",get_I_ansi_version(in));
+
+ DBG (15, " AENC: %d\n",get_I_aenc(in));
+ DBG (15, " TrmIOP: %d\n",get_I_trmiop(in));
+ DBG (15, " RDF: %d\n",get_I_resonse_format(in));
+
+ DBG (15, " Length: %d\n",get_I_length(in));
+
+ DBG (15, " RelAdr: %d\n",get_I_reladr(in));
+ DBG (15, " WBus32: %d\n",get_I_wbus32(in));
+ DBG (15, " WBus16: %d\n",get_I_wbus16(in));
+ DBG (15, " Sync: %d\n",get_I_sync(in));
+ DBG (15, " Linked: %d\n",get_I_linked(in));
+ DBG (15, " CmdQue: %d\n",get_I_cmdque(in));
+ DBG (15, " SftRe: %d\n",get_I_sftre(in));
+
+ /*kodak specific*/
+ DBG (15, "vendor inquiry options\n");
+
+ DBG (15, " MF Disable: %d\n",get_I_mf_disable(in));
+ DBG (15, " Checkdigit: %d\n",get_I_checkdigit(in));
+ DBG (15, " Front Prism: %d\n",get_I_front_prism(in));
+ DBG (15, " Comp Gray: %d\n",get_I_compressed_gray(in));
+ DBG (15, " Front Toggle: %d\n",get_I_front_toggle(in));
+ DBG (15, " Front DP1: %d\n",get_I_front_dp1(in));
+ DBG (15, " Front Color: %d\n",get_I_front_color(in));
+ DBG (15, " Front ATP: %d\n",get_I_front_atp(in));
+
+ DBG (15, " DP1 180: %d\n",get_I_dp1_180(in));
+ DBG (15, " MF Pause: %d\n",get_I_mf_pause(in));
+ DBG (15, " Rear Prism: %d\n",get_I_rear_prism(in));
+ DBG (15, " Uncomp Gray: %d\n",get_I_uncompressed_gray(in));
+ DBG (15, " Rear Toggle: %d\n",get_I_rear_toggle(in));
+ DBG (15, " Rear DP1: %d\n",get_I_rear_dp1(in));
+ DBG (15, " Rear Color: %d\n",get_I_rear_color(in));
+ DBG (15, " Rear ATP: %d\n",get_I_rear_atp(in));
+
+ /* we actually care about these */
+ DBG (15, " Min Binary Res: %d\n",get_I_min_bin_res(in));
+ s->s_res_min[MODE_LINEART] = get_I_min_bin_res(in);
+ DBG (15, " Max Binary Res: %d\n",get_I_max_bin_res(in));
+ s->s_res_max[MODE_LINEART] = get_I_max_bin_res(in);
+ DBG (15, " Min Color Res: %d\n",get_I_min_col_res(in));
+ s->s_res_min[MODE_COLOR] = get_I_min_col_res(in);
+ DBG (15, " Max Color Res: %d\n",get_I_max_col_res(in));
+ s->s_res_max[MODE_COLOR] = get_I_max_col_res(in);
+
+ DBG (15, " Max Width: %d\n",get_I_max_image_width(in));
+ s->s_width_max = get_I_max_image_width(in);
+ DBG (15, " Max Length: %d\n",get_I_max_image_length(in));
+ s->s_length_max = get_I_max_image_length(in);
+
+ /*FIXME: do we need to save these?*/
+ DBG (15, " Finecrop: %d\n",get_I_finecrop(in));
+ DBG (15, " iThresh: %d\n",get_I_ithresh(in));
+ DBG (15, " ECD: %d\n",get_I_ecd(in));
+ DBG (15, " VBLR: %d\n",get_I_vblr(in));
+ DBG (15, " Elevator: %d\n",get_I_elevator(in));
+ DBG (15, " RelCrop: %d\n",get_I_relcrop(in));
+
+ DBG (15, " CDeskew: %d\n",get_I_cdeskew(in));
+ DBG (15, " IA: %d\n",get_I_ia(in));
+ DBG (15, " Patch: %d\n",get_I_patch(in));
+ DBG (15, " Null Mode: %d\n",get_I_nullmode(in));
+ DBG (15, " SABRE: %d\n",get_I_sabre(in));
+ DBG (15, " LDDDS: %d\n",get_I_lddds(in));
+ DBG (15, " UDDDS: %d\n",get_I_uddds(in));
+ DBG (15, " Fixed Gap: %d\n",get_I_fixedgap(in));
+
+ DBG (15, " HR Printer: %d\n",get_I_hr_printer(in));
+ DBG (15, " Elev 100/250: %d\n",get_I_elev_100_250(in));
+ DBG (15, " UDDS Individual: %d\n",get_I_udds_individual(in));
+ DBG (15, " Auto Color: %d\n",get_I_auto_color(in));
+ DBG (15, " WB: %d\n",get_I_wb(in));
+ DBG (15, " ES: %d\n",get_I_es(in));
+ DBG (15, " FC: %d\n",get_I_fc(in));
+
+ DBG (15, " Max Rate: %d\n",get_I_max_rate(in));
+ DBG (15, " Buffer Size: %d\n",get_I_buffer_size(in));
+
+ DBG (10, "init_inquire: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * get model specific info that is not in vpd, and correct
+ * errors in vpd data. struct is already initialized to 0.
+ */
+static SANE_Status
+init_model (struct scanner *s)
+{
+
+ DBG (10, "init_model: start\n");
+
+ s->s_mode[MODE_LINEART] = 1;
+ s->s_mode[MODE_HALFTONE] = 1;
+ s->s_mode[MODE_GRAYSCALE] = 1;
+ s->s_mode[MODE_COLOR] = 1;
+
+ /* scanner did not tell us these */
+ s->s_res_min[MODE_HALFTONE] = s->s_res_min[MODE_LINEART];
+ s->s_res_max[MODE_HALFTONE] = s->s_res_max[MODE_LINEART];
+
+ s->s_res_min[MODE_GRAYSCALE] = s->s_res_min[MODE_COLOR];
+ s->s_res_max[MODE_GRAYSCALE] = s->s_res_max[MODE_COLOR];
+
+ s->s_width_min = 96;
+ s->s_length_min = 96;
+
+ s->s_brightness_steps = 0;
+ s->s_contrast_steps = 255;
+ s->s_threshold_steps = 255;
+ s->s_rif = 1;
+
+ DBG (10, "init_model: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * set good default user values.
+ * struct is already initialized to 0.
+ */
+static SANE_Status
+init_user (struct scanner *s)
+{
+
+ DBG (10, "init_user: start\n");
+
+ /* source */
+ s->u_source = SOURCE_ADF_FRONT;
+
+ /* scan mode */
+ s->u_mode = MODE_LINEART;
+
+ /*res, minimum for this mode*/
+ s->u_res = s->s_res_min[s->u_mode];
+
+ /* page width US-Letter */
+ s->u_page_width = 8.5 * 1200;
+ if(s->u_page_width > s->s_width_max){
+ s->u_page_width = s->s_width_max;
+ }
+
+ /* page height US-Letter */
+ s->u_page_height = 11 * 1200;
+ if(s->u_page_height > s->s_length_max){
+ s->u_page_height = s->s_length_max;
+ }
+
+ /* bottom-right x */
+ s->u_br_x = s->u_page_width;
+
+ /* bottom-right y */
+ s->u_br_y = s->u_page_height;
+
+ DBG (10, "init_user: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function presets the "option" array to blank
+ */
+static SANE_Status
+init_options (struct scanner *s)
+{
+ int i;
+
+ DBG (10, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].name = "filler";
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ DBG (10, "init_options: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct scanner *dev = NULL;
+ struct scanner *s = NULL;
+ SANE_Status ret;
+ unsigned char cmd[SEND_len];
+ size_t cmdLen = SEND_len;
+ unsigned char out[SR_len_time]; /*longest used in this function*/
+ int try=0;
+ time_t gmt_tt;
+ struct tm * gmt_tm_p;
+ struct tm * local_tm_p;
+
+ DBG (10, "sane_open: start\n");
+
+ if(scanner_devList){
+ DBG (15, "sane_open: searching currently attached scanners\n");
+ }
+ else{
+ DBG (15, "sane_open: no scanners currently attached, attaching\n");
+
+ ret = sane_get_devices(NULL,0);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+ }
+
+ if(name[0] == 0){
+ DBG (15, "sane_open: no device requested, using default\n");
+ s = scanner_devList;
+ }
+ else{
+ DBG (15, "sane_open: device %s requested\n", name);
+
+ for (dev = scanner_devList; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, name) == 0) {
+ s = dev;
+ break;
+ }
+ }
+ }
+
+ if (!s) {
+ DBG (5, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "sane_open: device %s found\n", s->sane.name);
+
+ *handle = s;
+
+ /* connect the fd so we can talk to scanner */
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ /*send the end batch (GX) command*/
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_end);
+ set_SR_xfer_length(cmd,SR_len_end);
+
+ /*start the following loop*/
+ ret = SANE_STATUS_DEVICE_BUSY;
+ s->rs_info = 0;
+
+ /*loop until scanner is ready*/
+ while(ret == SANE_STATUS_DEVICE_BUSY){
+ DBG (15, "sane_open: GX, try %d, sleep %lu\n", try, (unsigned long)s->rs_info);
+ try++;
+ sleep(s->rs_info);
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if(try > 5){
+ break;
+ }
+ }
+ if(ret){
+ DBG (5, "sane_open: GX error %d\n",ret);
+ return ret;
+ }
+
+ /*send the clear buffer (CB) command*/
+ DBG (15, "sane_open: CB\n");
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_clear);
+ set_SR_xfer_length(cmd,SR_len_clear);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: CB error %d\n",ret);
+ return ret;
+ }
+
+ /*send the GT command*/
+ DBG (15, "sane_open: GT\n");
+ gmt_tt = time(NULL);
+ gmt_tm_p = gmtime(&gmt_tt);
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_gmt);
+ set_SR_xfer_length(cmd,SR_len_time);
+
+ memset(out,0,SR_len_time);
+ set_SR_payload_len(out,SR_len_time);
+ set_SR_time_hour(out,gmt_tm_p->tm_hour);
+ set_SR_time_min(out,gmt_tm_p->tm_min);
+ set_SR_time_mon(out,gmt_tm_p->tm_mon);
+ set_SR_time_day(out,gmt_tm_p->tm_mday);
+ set_SR_time_year(out,gmt_tm_p->tm_year+1900);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, SR_len_time,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: GT error %d\n",ret);
+ return ret;
+ }
+
+ /*FIXME: read the LC command? */
+
+ /*send the LC command*/
+ DBG (15, "sane_open: LC\n");
+ gmt_tt = time(NULL);
+ local_tm_p = localtime(&gmt_tt);
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_clock);
+ set_SR_xfer_length(cmd,SR_len_time);
+
+ memset(out,0,SR_len_time);
+ set_SR_payload_len(out,SR_len_time);
+ set_SR_time_hour(out,local_tm_p->tm_hour);
+ set_SR_time_min(out,local_tm_p->tm_min);
+ set_SR_time_mon(out,local_tm_p->tm_mon);
+ set_SR_time_day(out,local_tm_p->tm_mday);
+ set_SR_time_year(out,local_tm_p->tm_year+1900);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, SR_len_time,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: LC error %d\n",ret);
+ return ret;
+ }
+
+ DBG (10, "sane_open: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * @@ Section 3 - SANE Options functions
+ */
+
+/*
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+ int i;
+ SANE_Option_Descriptor *opt = &s->opt[option];
+
+ DBG (20, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ /* "Mode" group -------------------------------------------------------- */
+ if(option==OPT_MODE_GROUP){
+ opt->title = "Scan Mode";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* source */
+ if(option==OPT_SOURCE){
+ i=0;
+ s->o_source_list[i++]=STRING_ADFFRONT;
+ s->o_source_list[i++]=STRING_ADFBACK;
+ s->o_source_list[i++]=STRING_ADFDUPLEX;
+ s->o_source_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_SOURCE;
+ opt->title = SANE_TITLE_SCAN_SOURCE;
+ opt->desc = SANE_DESC_SCAN_SOURCE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->o_source_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* scan mode */
+ if(option==OPT_MODE){
+ i=0;
+ if(s->s_mode[MODE_LINEART]){
+ s->o_mode_list[i++]=STRING_LINEART;
+ }
+ if(s->s_mode[MODE_HALFTONE]){
+ s->o_mode_list[i++]=STRING_HALFTONE;
+ }
+ if(s->s_mode[MODE_GRAYSCALE]){
+ s->o_mode_list[i++]=STRING_GRAYSCALE;
+ }
+ if(s->s_mode[MODE_COLOR]){
+ s->o_mode_list[i++]=STRING_COLOR;
+ }
+ s->o_mode_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->o_mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* resolution */
+ /* build a list of possible choices for current mode */
+ if(option==OPT_RES){
+ int reslist[]={100,150,200,240,300,400};
+ int j;
+
+ i=0;
+ for(j=0;j<6;j++){
+ if(reslist[j] >= s->s_res_min[s->u_mode]
+ && reslist[j] <= s->s_res_max[s->u_mode]){
+ s->o_res_list[s->u_mode][++i] = reslist[j];
+ }
+ }
+ s->o_res_list[s->u_mode][0] = i;
+
+ opt->name = SANE_NAME_SCAN_RESOLUTION;
+ opt->title = SANE_TITLE_SCAN_RESOLUTION;
+ opt->desc = SANE_DESC_SCAN_RESOLUTION;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_DPI;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opt->constraint.word_list = s->o_res_list[s->u_mode];
+ }
+
+ /* "Geometry" group ---------------------------------------------------- */
+ if(option==OPT_GEOMETRY_GROUP){
+ opt->title = "Geometry";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* top-left x */
+ if(option==OPT_TL_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min);
+ s->o_tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max);
+ s->o_tl_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_X;
+ opt->title = SANE_TITLE_SCAN_TL_X;
+ opt->desc = SANE_DESC_SCAN_TL_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_tl_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* top-left y */
+ if(option==OPT_TL_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min);
+ s->o_tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max);
+ s->o_tl_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_Y;
+ opt->title = SANE_TITLE_SCAN_TL_Y;
+ opt->desc = SANE_DESC_SCAN_TL_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_tl_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right x */
+ if(option==OPT_BR_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min);
+ s->o_br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max);
+ s->o_br_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_X;
+ opt->title = SANE_TITLE_SCAN_BR_X;
+ opt->desc = SANE_DESC_SCAN_BR_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_br_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right y */
+ if(option==OPT_BR_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min);
+ s->o_br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max);
+ s->o_br_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_Y;
+ opt->title = SANE_TITLE_SCAN_BR_Y;
+ opt->desc = SANE_DESC_SCAN_BR_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_br_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* page width */
+ if(option==OPT_PAGE_WIDTH){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_page_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min);
+ s->o_page_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max);
+ s->o_page_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = "pagewidth";
+ opt->title = "ADF paper width";
+ opt->desc = "Must be set properly to align scanning window";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_page_x_range;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* page height */
+ if(option==OPT_PAGE_HEIGHT){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_page_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min);
+ s->o_page_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max);
+ s->o_page_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = "pageheight";
+ opt->title = "ADF paper length";
+ opt->desc = "Must be set properly to eject pages";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_page_y_range;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* "Enhancement" group ------------------------------------------------- */
+ if(option==OPT_ENHANCEMENT_GROUP){
+ opt->title = "Enhancement";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* brightness */
+ if(option==OPT_BRIGHTNESS){
+ opt->name = SANE_NAME_BRIGHTNESS;
+ opt->title = SANE_TITLE_BRIGHTNESS;
+ opt->desc = SANE_DESC_BRIGHTNESS;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_brightness_range;
+ s->o_brightness_range.quant=1;
+ s->o_brightness_range.min=-(s->s_brightness_steps/2);
+ s->o_brightness_range.max=s->s_brightness_steps/2;
+ if(opt->constraint.range->max > opt->constraint.range->min){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* contrast */
+ if(option==OPT_CONTRAST){
+ opt->name = SANE_NAME_CONTRAST;
+ opt->title = SANE_TITLE_CONTRAST;
+ opt->desc = SANE_DESC_CONTRAST;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_contrast_range;
+ s->o_contrast_range.quant=1;
+ s->o_contrast_range.min=-(s->s_contrast_steps/2);
+ s->o_contrast_range.max=s->s_contrast_steps/2;
+ if(opt->constraint.range->max > opt->constraint.range->min){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /*threshold*/
+ if(option==OPT_THRESHOLD){
+ opt->name = SANE_NAME_THRESHOLD;
+ opt->title = SANE_TITLE_THRESHOLD;
+ opt->desc = SANE_DESC_THRESHOLD;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_threshold_range;
+ s->o_threshold_range.min=0;
+ s->o_threshold_range.max=s->s_threshold_steps;
+ s->o_threshold_range.quant=1;
+ if(opt->constraint.range->max > opt->constraint.range->min){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /*rif*/
+ if(option==OPT_RIF){
+ opt->name = "rif";
+ opt->title = "RIF";
+ opt->desc = "Reverse image format";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->s_rif)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ return opt;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Int dummy = 0;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ if (option >= NUM_OPTIONS) {
+ DBG (5, "sane_control_option: %d too big\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: %d inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE) {
+ SANE_Word * val_p = (SANE_Word *) val;
+
+ DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ *val_p = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if(s->u_source == SOURCE_ADF_FRONT){
+ strcpy (val, STRING_ADFFRONT);
+ }
+ else if(s->u_source == SOURCE_ADF_BACK){
+ strcpy (val, STRING_ADFBACK);
+ }
+ else if(s->u_source == SOURCE_ADF_DUPLEX){
+ strcpy (val, STRING_ADFDUPLEX);
+ }
+ else{
+ DBG(5,"missing option val for source\n");
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if(s->u_mode == MODE_LINEART){
+ strcpy (val, STRING_LINEART);
+ }
+ else if(s->u_mode == MODE_HALFTONE){
+ strcpy (val, STRING_HALFTONE);
+ }
+ else if(s->u_mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->u_mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+ *val_p = s->u_res;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_tl_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_tl_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_br_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_br_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_page_width);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_page_height);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *val_p = s->u_brightness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *val_p = s->u_contrast;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ *val_p = s->u_threshold;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RIF:
+ *val_p = s->u_rif;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE) {
+ int tmp;
+ SANE_Word val_c;
+ SANE_Status status;
+
+ DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
+
+ if ( s->started ) {
+ DBG (5, "sane_control_option: cant set, device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (5, "sane_control_option: bad value\n");
+ return status;
+ }
+
+ /* may have been changed by constrain, so dont copy until now */
+ val_c = *(SANE_Word *)val;
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option) {
+
+ /* Mode Group */
+ case OPT_SOURCE:
+ if (!strcmp (val, STRING_ADFFRONT)) {
+ tmp = SOURCE_ADF_FRONT;
+ }
+ else if (!strcmp (val, STRING_ADFBACK)) {
+ tmp = SOURCE_ADF_BACK;
+ }
+ else{
+ tmp = SOURCE_ADF_DUPLEX;
+ }
+
+ if (s->u_source != tmp) {
+ s->u_source = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (!strcmp (val, STRING_LINEART)) {
+ tmp = MODE_LINEART;
+ }
+ else if (!strcmp (val, STRING_HALFTONE)) {
+ tmp = MODE_HALFTONE;
+ }
+ else if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp != s->u_mode){
+ s->u_mode = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+
+ if (s->u_res != val_c) {
+ s->u_res = val_c;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ /* Geometry Group */
+ case OPT_TL_X:
+ if (s->u_tl_x != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ if (s->u_tl_y != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ if (s->u_br_x != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_br_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ if (s->u_br_y != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_br_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ if (s->u_page_width != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_page_width = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ if (s->u_page_height != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_page_height = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ /* Enhancement Group */
+ case OPT_BRIGHTNESS:
+ if (s->u_brightness != val_c){
+ s->u_brightness = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ if (s->u_contrast != val_c){
+ s->u_contrast = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ if (s->u_threshold != val_c){
+ s->u_threshold = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RIF:
+ if (s->u_rif != val_c){
+ s->u_rif = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+
+ return SANE_STATUS_INVAL;
+}
+
+/*
+ * @@ Section 4 - SANE scanning functions
+ */
+/*
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+/* SANE_Parameters is defined as a struct containing:
+ SANE_Frame format;
+ SANE_Bool last_frame;
+ SANE_Int lines;
+ SANE_Int depth; ( binary=1, gray=8, color=8 (!24) )
+ SANE_Int pixels_per_line;
+ SANE_Int bytes_per_line;
+*/
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct scanner *s = (struct scanner *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ /* started? get param data from image header */
+ if(s->started){
+ DBG (15, "sane_get_parameters: image settings:\n");
+
+ DBG (15, " tlx=%d, brx=%d, iw=%d, maxx=%d\n",
+ s->i_tlx, (s->i_tlx+s->i_width), s->i_width, s->s_width_max/1200);
+ DBG (15, " tly=%d, bry=%d, il=%d, maxy=%d\n",
+ s->i_tly, (s->i_tly+s->i_length), s->i_length, s->s_length_max/1200);
+ DBG (15, " res=%d, id=%d, bytes=%d\n",
+ s->i_dpi, s->i_id, s->i_bytes);
+
+ params->last_frame = 1;
+ params->lines = s->i_length;
+ params->pixels_per_line = s->i_width;
+
+ /* bitonal */
+ if (s->i_bpp == 1) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 1;
+ params->bytes_per_line = params->pixels_per_line / 8;
+
+#ifdef SANE_FRAME_G42D
+ /*G4 fax compression*/
+ if (s->i_compr) {
+ params->format = SANE_FRAME_G42D;
+ }
+#endif
+ }
+ /* gray */
+ else if (s->i_bpp == 8) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line;
+
+#ifdef SANE_FRAME_JPEG
+ /*jpeg compression*/
+ if (s->i_compr) {
+ params->format = SANE_FRAME_JPEG;
+ }
+#endif
+ }
+ /* color */
+ else if (s->i_bpp == 24 || s->i_bpp == 96) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line * 3;
+
+#ifdef SANE_FRAME_JPEG
+ /*jpeg compression*/
+ if (s->i_compr) {
+ params->format = SANE_FRAME_JPEG;
+ }
+#endif
+ }
+ else{
+ DBG(5,"sane_get_parameters: unsupported depth %d\n", s->i_bpp);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /* not started? get param data from user input */
+ else{
+
+ DBG (15, "sane_get_parameters: user settings:\n");
+
+ DBG (15, " tlx=%d, brx=%d, pw=%d, maxx=%d\n",
+ s->u_tl_x, s->u_br_x, s->u_page_width, s->s_width_max);
+ DBG (15, " tly=%d, bry=%d, ph=%d, maxy=%d\n",
+ s->u_tl_y, s->u_br_y, s->u_page_height, s->s_length_max);
+ DBG (15, " res=%d, user_x=%d, user_y=%d\n",
+ s->u_res, (s->u_res * (s->u_br_x - s->u_tl_x) / 1200),
+ (s->u_res * (s->u_br_y - s->u_tl_y) / 1200));
+
+ if (s->u_mode == MODE_COLOR) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ }
+ else if (s->u_mode == MODE_GRAYSCALE) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ }
+ else {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 1;
+ }
+
+ params->last_frame = 1;
+ params->lines = s->u_res * (s->u_br_y - s->u_tl_y) / 1200;
+ params->pixels_per_line = s->u_res * (s->u_br_x - s->u_tl_x) / 1200;
+
+ /* bytes per line differs by mode */
+ if (s->u_mode == MODE_COLOR) {
+ params->bytes_per_line = params->pixels_per_line * 3;
+ }
+ else if (s->u_mode == MODE_GRAYSCALE) {
+ params->bytes_per_line = params->pixels_per_line;
+ }
+ else {
+ params->bytes_per_line = params->pixels_per_line / 8;
+ }
+
+ }
+
+ DBG (15, "sane_get_parameters: returning:\n");
+ DBG (15, " scan_x=%d, Bpl=%d, depth=%d\n",
+ params->pixels_per_line, params->bytes_per_line, params->depth );
+
+ DBG (15, " scan_y=%d, frame=%d, last=%d\n",
+ params->lines, params->format, params->last_frame );
+
+ DBG (10, "sane_get_parameters: finish\n");
+
+ return ret;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ * commands: scanner control (lampon), send (lut), send (dither),
+ * set window, object pos, and scan
+ *
+ * this will be called before each image, including duplex backsides,
+ * and at the start of adf batch.
+ * hence, we spend alot of time playing with s->started, etc.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = handle;
+ SANE_Status ret;
+
+ DBG (10, "sane_start: start\n");
+
+ DBG (15, "started=%d, source=%d\n", s->started, s->u_source);
+
+ /* batch already running */
+ if(s->started){
+ /* not finished with current image, error */
+ if (s->bytes_tx != s->i_bytes) {
+ DBG(5,"sane_start: previous transfer not finished?");
+ return do_cancel(s);
+ }
+ }
+
+ /* first page of batch */
+ else{
+
+ unsigned char cmd[SCAN_len];
+ unsigned char pay[SR_len_startstop];
+
+ /* set window command */
+ ret = set_window(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot set window\n");
+ do_cancel(s);
+ return ret;
+ }
+
+ /* read/send JQ command */
+
+ /* read/send SC command */
+ ret = send_sc(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot send SC\n");
+ do_cancel(s);
+ return ret;
+ }
+
+ /* read/send CT command */
+
+ DBG (15, "sane_start: send SCAN\n");
+ memset(cmd, 0, SCAN_len);
+ set_SCSI_opcode(cmd, SCAN_code);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, SCAN_len,
+ NULL, 0,
+ NULL, NULL
+ );
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR sending SCAN\n");
+ do_cancel(s);
+ return ret;
+ }
+
+ /* send SS command */
+ DBG (15, "sane_start: send SS\n");
+ memset(cmd,0,SEND_len);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_startstop);
+ set_SR_xfer_length(cmd,SR_len_startstop);
+
+ memset(pay,0,SR_len_startstop);
+ set_SR_payload_len(pay,SR_len_startstop);
+ set_SR_startstop_cmd(pay,1);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, SEND_len,
+ pay, SR_len_startstop,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: SS error %d\n",ret);
+ return ret;
+ }
+
+ DBG (15, "sane_start: sleeping\n");
+ sleep(2);
+
+ s->started=1;
+ }
+
+ ret = read_imageheader(s);
+ if(ret){
+ DBG (5, "sane_open: error reading imageheader %d\n",ret);
+ return ret;
+ }
+
+ /* set clean defaults */
+ s->bytes_rx = 0;
+ s->bytes_tx = 0;
+
+ /* make large buffer to hold the images */
+ DBG (15, "sane_start: setup buffer\n");
+
+ /* free current buffer if too small */
+ if (s->buffer && s->bytes_buf < s->i_bytes) {
+ DBG (15, "sane_start: free buffer.\n");
+ free(s->buffer);
+ s->buffer = NULL;
+ s->bytes_buf = 0;
+ }
+
+ /* grab new buffer if dont have one */
+ if (!s->buffer) {
+ DBG (15, "sane_start: calloc buffer.\n");
+ s->buffer = calloc (1,s->i_bytes);
+ if (!s->buffer) {
+ DBG (5, "sane_start: Error, no buffer\n");
+ do_cancel(s);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ DBG (15, "started=%d, source=%d\n", s->started, s->u_source);
+
+ DBG (10, "sane_start: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This routine issues a SCSI SET WINDOW command to the scanner, using the
+ * values currently in the scanner data structure.
+ * the scanner has 4 separate windows, and all must be set similarly,
+ * even if you dont intend to aquire images from all of them.
+ */
+static SANE_Status
+set_window (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SET_WINDOW_len];
+ size_t cmdLen = SET_WINDOW_len;
+
+ /* the data phase has a header, followed by a window desc block
+ * the header specifies the number of bytes in 1 window desc block */
+ unsigned char pay[WINDOW_HEADER_len + WINDOW_DESCRIPTOR_len];
+ size_t payLen = WINDOW_HEADER_len + WINDOW_DESCRIPTOR_len;
+
+ unsigned char * desc = pay + WINDOW_HEADER_len;
+
+ int width = (s->u_br_x - s->u_tl_x) * s->u_res/1200;
+ int length = (s->u_br_y - s->u_tl_y) * s->u_res/1200;
+
+ DBG (10, "set_window: start\n");
+
+ /* binary window settings */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SET_WINDOW_code);
+ set_SW_xferlen(cmd,payLen);
+
+ memset(pay,0,payLen);
+ set_WH_desc_len(pay,WINDOW_DESCRIPTOR_len);
+
+ set_WD_wid(desc,WD_wid_front_binary);
+
+ /* common settings */
+ set_WD_Xres (desc, s->u_res);
+ set_WD_Yres (desc, s->u_res);
+
+ set_WD_ULX (desc, s->u_tl_x);
+ set_WD_ULY (desc, s->u_tl_y);
+
+ /* width % 32 == 0 && length % 1 == 0 */
+ width -= width % 32;
+ width = width*1200/s->u_res;
+
+ length = length*1200/s->u_res;
+
+ set_WD_width (desc, width);
+ set_WD_length (desc, length);
+
+ /* brightness not supported? */
+ set_WD_brightness (desc, 0);
+ set_WD_threshold (desc, s->u_threshold);
+ set_WD_contrast (desc, 0);
+ if(s->s_contrast_steps){
+ /*convert our common -127 to +127 range into HW's range
+ *FIXME: this code assumes hardware range of 1-255 */
+ set_WD_contrast (desc, s->u_contrast+128);
+ }
+
+ if(s->u_mode == MODE_HALFTONE){
+ set_WD_composition (desc, WD_compo_HALFTONE);
+ set_WD_bitsperpixel (desc, 1);
+ }
+ else{
+ set_WD_composition (desc, WD_compo_LINEART);
+ set_WD_bitsperpixel (desc, 1);
+ }
+
+ /* FIXME ht pattern */
+
+ set_WD_rif (desc, s->u_rif);
+
+ set_WD_bitorder (desc, 1);
+
+ /* compression options */
+ if(s->u_compr)
+ set_WD_compress_type (desc, WD_compr_FAXG4);
+
+ /*FIXME: noise filter */
+
+ set_WD_allow_zero(desc,1);
+ set_WD_cropping (desc, WD_crop_RELATIVE);
+
+ /*FIXME: more settings here*/
+
+ hexdump(15, "front binary window:", desc, WINDOW_DESCRIPTOR_len);
+
+ DBG (15, "set_window: set window binary back\n");
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting binary front window %d\n",ret);
+ return ret;
+ }
+
+ /*send the window for backside too*/
+ set_WD_wid(desc,WD_wid_back_binary);
+
+ DBG (15, "set_window: set window binary back\n");
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting binary back window %d\n",ret);
+ return ret;
+ }
+
+#if 0
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,GET_WINDOW_code);
+ set_GW_single(cmd,1);
+ set_GW_wid(cmd,WD_wid_front_color);
+ set_GW_xferlen(cmd,payLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ pay, &payLen
+ );
+ if(ret){
+ DBG (5, "set_window: error getting window %d\n",ret);
+ return ret;
+ }
+ hexdump(15,"foo",pay,payLen);
+#endif
+
+ /* color window settings */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SET_WINDOW_code);
+ set_SW_xferlen(cmd,payLen);
+
+ memset(pay,0,payLen);
+ set_WH_desc_len(pay,WINDOW_DESCRIPTOR_len);
+
+ set_WD_wid(desc,WD_wid_front_color);
+
+ /* common settings */
+ set_WD_Xres (desc, s->u_res);
+ set_WD_Yres (desc, s->u_res);
+
+ set_WD_ULX (desc, s->u_tl_x);
+ set_WD_ULY (desc, s->u_tl_y);
+
+ set_WD_width (desc, width);
+ set_WD_length (desc, length);
+
+ /*gray mode*/
+ if(s->u_mode == MODE_GRAYSCALE){
+ /*
+ gamma
+ width % 8 == 0 && length % 8 == 0
+ */
+ set_WD_composition (desc, WD_compo_MULTILEVEL);
+ set_WD_bitsperpixel (desc, 8);
+ }
+ /*color mode or color window in binary mode*/
+ else{
+ /*
+ width % 16 == 0 && length % 8 == 0
+ */
+ set_WD_composition (desc, WD_compo_MULTILEVEL);
+ set_WD_bitsperpixel (desc, 24);
+
+ /* compression options */
+ if(s->u_compr)
+ set_WD_compress_type (desc, WD_compr_JPEG);
+ }
+
+ set_WD_bitorder (desc, 1);
+
+ /*FIXME: noise filter */
+
+ set_WD_allow_zero(desc,1);
+ set_WD_cropping (desc, WD_crop_RELATIVE);
+
+ /*FIXME: more settings here*/
+
+ DBG (15, "set_window: set window color front\n");
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting color front window %d\n",ret);
+ return ret;
+ }
+
+ /*send the window for backside too*/
+ set_WD_wid(desc,WD_wid_back_color);
+
+ DBG (15, "set_window: set window color back\n");
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting color back window %d\n",ret);
+ return ret;
+ }
+
+ DBG (10, "set_window: finish\n");
+
+ return ret;
+}
+
+/*
+ * This routine reads the SC (scanner config) data from the scanner
+ * modifies a few params based on user data, and sends it back
+ */
+static SANE_Status
+send_sc(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+ unsigned char pay[SR_len_config];
+ size_t payLen = SR_len_config;
+
+ /* send SC command */
+ DBG (10, "send_sc: start\n");
+
+ DBG (15, "send_sc: reading config\n");
+ memset(cmd,0,READ_len);
+ set_SCSI_opcode(cmd,READ_code);
+
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_config);
+ set_SR_xfer_length(cmd,SR_len_config);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ pay, &payLen
+ );
+ if(ret || !payLen){
+ DBG (5, "send_sc: error reading: %d\n",ret);
+ return ret;
+ }
+
+ memset(cmd,0,SEND_len);
+ set_SCSI_opcode(cmd,SEND_code);
+
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_config);
+ set_SR_xfer_length(cmd,payLen);
+
+ if(s->u_source == SOURCE_ADF_FRONT){
+ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){
+ set_SR_sc_io1(pay,SR_sc_io_front_color);
+ }
+ else{
+ set_SR_sc_io1(pay,SR_sc_io_front_binary);
+ }
+ set_SR_sc_io2(pay,SR_sc_io_none);
+ set_SR_sc_io3(pay,SR_sc_io_none);
+ set_SR_sc_io4(pay,SR_sc_io_none);
+ }
+ else if(s->u_source == SOURCE_ADF_BACK){
+ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){
+ set_SR_sc_io1(pay,SR_sc_io_rear_color);
+ }
+ else{
+ set_SR_sc_io1(pay,SR_sc_io_rear_binary);
+ }
+ set_SR_sc_io2(pay,SR_sc_io_none);
+ set_SR_sc_io3(pay,SR_sc_io_none);
+ set_SR_sc_io4(pay,SR_sc_io_none);
+ }
+ else{
+ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){
+ set_SR_sc_io1(pay,SR_sc_io_front_color);
+ set_SR_sc_io2(pay,SR_sc_io_rear_color);
+ }
+ else{
+ set_SR_sc_io1(pay,SR_sc_io_front_binary);
+ set_SR_sc_io2(pay,SR_sc_io_rear_binary);
+ }
+ set_SR_sc_io3(pay,SR_sc_io_none);
+ set_SR_sc_io4(pay,SR_sc_io_none);
+ }
+
+ /*FIXME: there are hundreds of other settings in this payload*/
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+
+ DBG (10, "send_sc: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * This routine reads the image header from the scanner, and updates
+ * values currently in the scanner data structure.
+ */
+static SANE_Status
+read_imageheader (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ unsigned char pay[SR_len_imageheader];
+ size_t payLen = SR_len_imageheader;
+ int pass = 0;
+
+ /* read img header */
+ DBG (10, "read_imageheader: start\n");
+
+ memset(cmd,0,READ_len);
+ set_SCSI_opcode(cmd,READ_code);
+ set_SR_datatype_code(cmd,SR_datatype_imageheader);
+ set_SR_xfer_length(cmd,SR_len_imageheader);
+
+ while (pass++ < 1000){
+
+ DBG (15, "read_imageheader: pass %d\n", pass);
+
+ payLen = SR_len_imageheader;
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, READ_len,
+ NULL, 0,
+ pay, &payLen
+ );
+
+ DBG (15, "read_imageheader: pass status %d\n", ret);
+
+ if(ret != SANE_STATUS_DEVICE_BUSY){
+ break;
+ }
+
+ usleep(50000);
+ }
+
+ if (ret == SANE_STATUS_GOOD){
+
+ DBG (15, "image header:\n");
+
+ DBG (15, " bytes: %d\n",get_SR_ih_image_length(pay));
+ s->i_bytes = get_SR_ih_image_length(pay);
+
+ DBG (15, " id: %d\n",get_SR_ih_image_id(pay));
+ s->i_id = get_SR_ih_image_id(pay);
+
+ DBG (15, " dpi: %d\n",get_SR_ih_resolution(pay));
+ s->i_dpi = get_SR_ih_resolution(pay);
+
+ DBG (15, " tlx: %d\n",get_SR_ih_ulx(pay));
+ s->i_tlx = get_SR_ih_ulx(pay);
+
+ DBG (15, " tly: %d\n",get_SR_ih_uly(pay));
+ s->i_tly = get_SR_ih_uly(pay);
+
+ DBG (15, " width: %d\n",get_SR_ih_width(pay));
+ s->i_width = get_SR_ih_width(pay);
+
+ DBG (15, " length: %d\n",get_SR_ih_length(pay));
+ s->i_length = get_SR_ih_length(pay);
+
+ DBG (15, " bpp: %d\n",get_SR_ih_bpp(pay));
+ s->i_bpp = get_SR_ih_bpp(pay);
+
+ DBG (15, " comp: %d\n",get_SR_ih_comp_type(pay));
+ s->i_compr = get_SR_ih_comp_type(pay);
+
+ /*FIXME: there are alot more of these?*/
+ }
+
+ DBG (10, "read_imageheader: finish %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status ret=0;
+
+ DBG (10, "sane_read: start\n");
+
+ *len=0;
+
+ /* maybe cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: not started, call sane_start\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* sane_start required between images */
+ if(s->bytes_tx == s->i_bytes){
+ DBG (15, "sane_read: returning eof\n");
+ return SANE_STATUS_EOF;
+ }
+
+ if(s->i_bytes > s->bytes_rx ){
+ ret = read_from_scanner(s);
+ if(ret){
+ DBG(5,"sane_read: returning %d\n",ret);
+ return ret;
+ }
+ }
+
+ /* copy a block from buffer to frontend */
+ ret = read_from_buffer(s,buf,max_len,len);
+
+ DBG (10, "sane_read: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int bytes = s->buffer_size;
+ int remain = s->i_bytes - s->bytes_rx;
+ unsigned char * buf;
+ size_t inLen = 0;
+
+ unsigned char cmd[READ_len];
+ int cmdLen=READ_len;
+
+ DBG (10, "read_from_scanner: start\n");
+
+ memset(cmd, 0, cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+
+ /* figure out the max amount to transfer */
+ if(bytes > remain){
+ bytes = remain;
+ }
+
+ DBG(15, "read_from_scanner: to:%d rx:%d re:%d bu:%d pa:%d\n",
+ s->i_bytes, s->bytes_rx, remain, s->buffer_size, bytes);
+
+ if(ret){
+ return ret;
+ }
+
+ inLen = bytes;
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ set_SR_datatype_code (cmd, SR_datatype_imagedata);
+ set_SR_xfer_length (cmd, bytes);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ buf, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+ DBG(15, "read_from_scanner: got GOOD, returning GOOD\n");
+ }
+ else if (ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_scanner: got EOF, finishing\n");
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_scanner: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_scanner: error reading data block status = %d\n",ret);
+ inLen = 0;
+ }
+
+ if(inLen){
+ copy_buffer (s, buf, inLen);
+ }
+
+ free(buf);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG (5, "read_from_scanner: unexpected EOF, shortening image\n");
+ s->i_bytes = s->bytes_rx;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "read_from_scanner: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+copy_buffer(struct scanner *s, unsigned char * buf, int len)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "copy_buffer: start\n");
+
+ memcpy(s->buffer+s->bytes_rx,buf,len);
+ s->bytes_rx += len;
+
+ DBG (10, "copy_buffer: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_buffer(struct scanner *s, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int bytes = max_len;
+ int remain = s->bytes_rx - s->bytes_tx;
+
+ DBG (10, "read_from_buffer: start\n");
+
+ /* figure out the max amount to transfer */
+ if(bytes > remain){
+ bytes = remain;
+ }
+
+ *len = bytes;
+
+ DBG(15, "read_from_buffer: to:%d tx:%d re:%d bu:%d pa:%d\n",
+ s->i_bytes, s->bytes_tx, remain, max_len, bytes);
+
+ /*FIXME this needs to timeout eventually */
+ if(!bytes){
+ DBG(5,"read_from_buffer: nothing to do\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy(buf,s->buffer+s->bytes_tx,bytes);
+
+ s->bytes_tx += *len;
+
+ DBG (10, "read_from_buffer: finish\n");
+
+ return ret;
+}
+
+
+/*
+ * @@ Section 4 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * It has been said on the mailing list that sane_cancel is a bit of a
+ * misnomer because it is routinely called to signal the end of a
+ * batch - quoting David Mosberger-Tang:
+ *
+ * > In other words, the idea is to have sane_start() be called, and
+ * > collect as many images as the frontend wants (which could in turn
+ * > consist of multiple frames each as indicated by frame-type) and
+ * > when the frontend is done, it should call sane_cancel().
+ * > Sometimes it's better to think of sane_cancel() as "sane_stop()"
+ * > but that name would have had some misleading connotations as
+ * > well, that's why we stuck with "cancel".
+ *
+ * The current consensus regarding duplex and ADF scans seems to be
+ * the following call sequence: sane_start; sane_read (repeat until
+ * EOF); sane_start; sane_read... and then call sane_cancel if the
+ * batch is at an end. I.e. do not call sane_cancel during the run but
+ * as soon as you get a SANE_STATUS_NO_DOCS.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (10, "sane_cancel: start\n");
+ do_cancel ((struct scanner *) handle);
+ DBG (10, "sane_cancel: finish\n");
+}
+
+/*
+ * Performs cleanup.
+ * FIXME: do better cleanup if scanning is ongoing...
+ */
+static SANE_Status
+do_cancel (struct scanner *s)
+{
+ DBG (10, "do_cancel: start\n");
+
+ s->started = 0;
+
+ DBG (10, "do_cancel: finish\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/*
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close: start\n");
+
+ do_cancel((struct scanner *) handle);
+ disconnect_fd((struct scanner *) handle);
+
+ DBG (10, "sane_close: finish\n");
+}
+
+static SANE_Status
+disconnect_fd (struct scanner *s)
+{
+ DBG (10, "disconnect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (15, "disconnecting scsi device\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (10, "disconnect_fd: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct scanner *dev, *next;
+
+ DBG (10, "sane_exit: start\n");
+
+ for (dev = scanner_devList; dev; dev = next) {
+ disconnect_fd(dev);
+ next = dev->next;
+ free (dev->device_name);
+ free (dev);
+ }
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ scanner_devList = NULL;
+ sane_devArray = NULL;
+
+ DBG (10, "sane_exit: finish\n");
+}
+
+
+/*
+ * @@ Section 5 - misc helper functions
+ */
+/*
+ * Called by the SANE SCSI core on device errors
+ * parses the request sense return data buffer,
+ * decides the best SANE_Status for the problem
+ * and produces debug msgs
+ */
+static SANE_Status
+sense_handler (int fd, unsigned char * sensed_data, void *arg)
+{
+ struct scanner *s = arg;
+ unsigned int ili = get_RS_ILI (sensed_data);
+ unsigned int sk = get_RS_sense_key (sensed_data);
+ unsigned int asc = get_RS_ASC (sensed_data);
+ unsigned int ascq = get_RS_ASCQ (sensed_data);
+
+ DBG (5, "sense_handler: start\n");
+
+ /* kill compiler warning */
+ fd = fd;
+
+ /* save for later */
+ s->rs_info = get_RS_information (sensed_data);
+
+ DBG (5, "SK=%#02x, ASC=%#02x, ASCQ=%#02x, ILI=%d, info=%#08lx\n",
+ sk, asc, ascq, ili, (unsigned long)s->rs_info);
+
+ switch (sk) {
+
+ /* no sense */
+ case 0x0:
+ if (0x00 != asc) {
+ DBG (5, "No sense: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x00 != ascq) {
+ DBG (5, "No sense: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (ili) {
+ DBG (5, "No sense: ILI set\n");
+ return SANE_STATUS_EOF;
+ }
+ DBG (5, "No sense: ready\n");
+ return SANE_STATUS_GOOD;
+
+ /* not ready */
+ case 0x2:
+ if (0x80 != asc) {
+ DBG (5, "Not ready: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x00 != ascq) {
+ DBG (5, "Not ready: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Not ready: end of job\n");
+ return SANE_STATUS_NO_DOCS;
+ break;
+
+ /* hardware error */
+ case 0x4:
+ if (0x3b != asc) {
+ DBG (5, "Hardware error: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x05 == ascq) {
+ DBG (5, "Hardware error: paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (0x80 == ascq) {
+ DBG (5, "Hardware error: multi-feed\n");
+ return SANE_STATUS_JAMMED;
+ }
+ DBG (5, "Hardware error: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* illegal request */
+ case 0x5:
+ if (asc != 0x20 && asc != 0x24 && asc != 0x25 && asc != 0x26
+ && asc != 0x83 && asc != 0x8f) {
+ DBG (5, "Illegal request: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x20 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid opcode\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x24 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid field in CDB\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x25 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid LUN\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x26 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid field in params\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x83 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: command failed, check log\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x83 == asc && 0x01 == ascq) {
+ DBG (5, "Illegal request: command failed, invalid state\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x83 == asc && 0x02 == ascq) {
+ DBG (5, "Illegal request: command failed, critical error\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x8f == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: no image\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ DBG (5, "Illegal request: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* unit attention */
+ case 0x6:
+ if (asc != 0x29 && asc != 0x80) {
+ DBG (5, "Unit attention: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x29 == asc && 0x60 == ascq) {
+ DBG (5, "Unit attention: device reset\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (0x80 == asc && 0x00 == ascq) {
+ DBG (5, "Unit attention: Energy Star warm up\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x80 == asc && 0x01 == ascq) {
+ DBG (5, "Unit attention: lamp warm up for scan\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x80 == asc && 0x02 == ascq) {
+ DBG (5, "Unit attention: lamp warm up for cal\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x80 == asc && 0x04 == ascq) {
+ DBG (5, "Unit attention: calibration failed\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "Unit attention: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* ia overflow */
+ case 0x9:
+ if (0x80 == asc && 0x00 == ascq) {
+ DBG (5, "IA overflow: IA field overflow\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "IA overflow: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* volume overflow */
+ case 0xd:
+ if (0x80 == asc && 0x00 == ascq) {
+ DBG (5, "Volume overflow: Image buffer full\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Volume overflow: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ default:
+ DBG (5, "Unknown Sense Code\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "sense_handler: should never happen!\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/*
+SANE_Status
+do_rs(scanner * s)
+{
+ SANE_Status ret;
+ unsigned char cmd[REQUEST_SENSE_len];
+ size_t cmdLen = REQUEST_SENSE_len;
+
+ DBG (10, "do_rs: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,REQUEST_SENSE_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_end);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ while(ret == SANE_STATUS_DEVICE_BUSY){
+ ret = run_rs(s);
+ }
+
+ DBG (10, "do_rs: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+*/
+
+SANE_Status
+do_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ size_t actLen = 0;
+
+ /*shut up compiler*/
+ runRS=runRS;
+ shortTime=shortTime;
+
+ DBG(10, "do_cmd: start\n");
+
+ DBG(25, "cmd: writing %d bytes\n", (int)cmdLen);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+
+ if(outBuff && outLen){
+ DBG(25, "out: writing %d bytes\n", (int)outLen);
+ hexdump(30, "out: >>", outBuff, outLen);
+ }
+ if (inBuff && inLen){
+ DBG(25, "in: reading %d bytes\n", (int)*inLen);
+ actLen = *inLen;
+ }
+
+ ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen);
+
+ if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){
+ DBG(5,"do_cmd: return '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ /* FIXME: should we look at s->rs_info here? */
+ if (inBuff && inLen){
+ hexdump(30, "in: <<", inBuff, *inLen);
+ DBG(25, "in: read %d bytes\n", (int)*inLen);
+ }
+
+ DBG(10, "do_cmd: finish\n");
+
+ return ret;
+}
+
+#if 0 /* unused */
+static SANE_Status
+wait_scanner(struct scanner *s)
+{
+ int ret;
+
+ unsigned char cmd[TEST_UNIT_READY_len];
+ size_t cmdLen = TEST_UNIT_READY_len;
+
+ DBG (10, "wait_scanner: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,TEST_UNIT_READY_code);
+
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret));
+ }
+
+ DBG (10, "wait_scanner: finish\n");
+
+ return ret;
+}
+#endif /* 0 - unused */
+
+/**
+ * Convenience method to determine longest string size in a list.
+ */
+static size_t
+maxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+/**
+ * Prints a hex dump of the given buffer onto the debug output stream.
+ */
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ if(DBG_LEVEL < level)
+ return;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3x:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (15, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG (10, "sane_get_select_fd\n");
+ DBG (15, "%p %d\n", h, *fdp);
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/kodak.conf.in b/backend/kodak.conf.in
new file mode 100644
index 0000000..2ac339e
--- /dev/null
+++ b/backend/kodak.conf.in
@@ -0,0 +1,14 @@
+# NOTE: any 'option' lines only apply to
+# scanners discovered later in this file
+
+# to set data buffer size, in bytes
+# the value ranges from 4096 - infinity
+# but you may have scanning problems with
+# a value larger than 32768 (the default)
+option buffer-size 32768
+
+# To search for all KODAK scsi devices
+scsi KODAK
+
+# To use a specific scsi device
+#scsi /dev/sg1
diff --git a/backend/kodak.h b/backend/kodak.h
new file mode 100644
index 0000000..ea2a3e4
--- /dev/null
+++ b/backend/kodak.h
@@ -0,0 +1,272 @@
+#ifndef KODAK_H
+#define KODAK_H
+
+/*
+ * Part of SANE - Scanner Access Now Easy.
+ * Please see opening comment in kodak.c
+ */
+
+/* -------------------------------------------------------------------------
+ * This option list has to contain all options for all scanners supported by
+ * this driver. If a certain scanner cannot handle a certain option, there's
+ * still the possibility to say so, later.
+ */
+enum kodak_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_SOURCE, /*front/back/duplex*/
+ OPT_MODE, /*mono/ht/gray/color*/
+ OPT_RES,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_PAGE_WIDTH,
+ OPT_PAGE_HEIGHT,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+ OPT_RIF,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+struct scanner
+{
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during init of scanner. */
+ struct scanner *next;
+ char *device_name; /* The name of the scanner device for sane */
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during reading of config file. */
+ int buffer_size;
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during inquiry probing of the scanner. */
+ /* members in order found in scsi data... */
+ SANE_Device sane;
+
+ char vendor_name[9]; /* null-term data returned by SCSI inquiry.*/
+ char product_name[17]; /* null-term data returned by SCSI inquiry.*/
+ char version_name[5]; /* null-term data returned by SCSI inquiry.*/
+ char build_name[3]; /* null-term data returned by SCSI inquiry.*/
+
+ /* --------------------------------------------------------------------- */
+ /* immutable values which are set during INQUIRY probing of the scanner, */
+ /* or in the init_model cleanup routine... */
+
+ /* which modes scanner has */
+ int s_mode[4];
+
+ /* min and max resolution for each mode */
+ int s_res_min[4];
+ int s_res_max[4];
+
+ /* in 1/1200th inches, NOT sane units */
+ int s_width_min;
+ int s_width_max;
+ int s_length_min;
+ int s_length_max;
+
+ int s_brightness_steps;
+ int s_contrast_steps;
+ int s_threshold_steps;
+ int s_rif;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable SANE_Option structs provide our interface to frontend. */
+ /* some options require lists of strings or numbers, we keep them here */
+ /* instead of in global vars so that they can differ for each scanner */
+
+ /* long array of option structs */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+ /*mode group*/
+ SANE_String_Const o_source_list[4];
+ SANE_String_Const o_mode_list[5];
+ SANE_Int o_res_list[4][6];
+
+ /*geometry group*/
+ SANE_Range o_tl_x_range;
+ SANE_Range o_tl_y_range;
+ SANE_Range o_br_x_range;
+ SANE_Range o_br_y_range;
+ SANE_Range o_page_x_range;
+ SANE_Range o_page_y_range;
+
+ /*enhancement group*/
+ SANE_Range o_brightness_range;
+ SANE_Range o_contrast_range;
+ SANE_Range o_threshold_range;
+
+ /* --------------------------------------------------------------------- */
+ /* changeable vars to hold user input. modified by SANE_Options above */
+
+ /*mode group*/
+ int u_mode; /* color,lineart,etc */
+ int u_source; /* adf front,adf duplex,etc */
+ int u_res; /* resolution in dpi */
+
+ /*geometry group*/
+ /* The desired size of the scan, all in 1/1200 inch */
+ int u_tl_x;
+ int u_tl_y;
+ int u_br_x;
+ int u_br_y;
+ int u_page_width;
+ int u_page_height;
+
+ /*enhancement group*/
+ int u_brightness;
+ int u_contrast;
+ int u_threshold;
+ int u_rif;
+ int u_compr;
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by the scanner's post-scan image header */
+ int i_bytes;
+ int i_id;
+ int i_dpi;
+ int i_tlx;
+ int i_tly;
+ int i_width;
+ int i_length;
+ int i_bpp;
+ int i_compr;
+
+ /* --------------------------------------------------------------------- */
+ /* values which are set by scanning functions to keep track of pages, etc */
+ int started;
+
+ /* total to read/write */
+ int bytes_tot;
+
+ /* how far we have read */
+ int bytes_rx;
+
+ /* how far we have written */
+ int bytes_tx;
+
+ /* size of buffer */
+ int bytes_buf;
+
+ /* the buffer */
+ unsigned char * buffer;
+
+ /* --------------------------------------------------------------------- */
+ /* values which used by the command and data sending functions (scsi/usb)*/
+ int fd; /* The scanner device file descriptor. */
+ size_t rs_info;
+
+};
+
+#define DEFAULT_BUFFER_SIZE 32768
+
+#define SIDE_FRONT 0
+#define SIDE_BACK 1
+
+#define SOURCE_ADF_FRONT 0
+#define SOURCE_ADF_BACK 1
+#define SOURCE_ADF_DUPLEX 2
+
+#define MODE_LINEART 0
+#define MODE_HALFTONE 1
+#define MODE_GRAYSCALE 2
+#define MODE_COLOR 3
+
+/* ------------------------------------------------------------------------- */
+
+#define MM_PER_INCH 25.4
+#define MM_PER_UNIT_UNFIX SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))
+#define MM_PER_UNIT_FIX SANE_FIX(SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)))
+
+#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX((number) * MM_PER_UNIT_UNFIX)
+#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) / MM_PER_UNIT_UNFIX
+
+#define KODAK_CONFIG_FILE "kodak.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle);
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking);
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp);
+
+const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option);
+
+SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+
+SANE_Status sane_start (SANE_Handle handle);
+
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params);
+
+SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len);
+
+void sane_cancel (SANE_Handle h);
+
+void sane_close (SANE_Handle h);
+
+void sane_exit (void);
+
+/* ------------------------------------------------------------------------- */
+
+static SANE_Status attach_one (const char *name);
+static SANE_Status connect_fd (struct scanner *s);
+static SANE_Status disconnect_fd (struct scanner *s);
+static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg);
+
+static SANE_Status init_inquire (struct scanner *s);
+static SANE_Status init_model (struct scanner *s);
+static SANE_Status init_user (struct scanner *s);
+static SANE_Status init_options (struct scanner *s);
+
+static SANE_Status
+do_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+);
+
+#if 0 /* unused */
+static SANE_Status wait_scanner (struct scanner *s);
+#endif
+
+static SANE_Status do_cancel (struct scanner *scanner);
+
+static SANE_Status set_window (struct scanner *s);
+static SANE_Status read_imageheader(struct scanner *s);
+static SANE_Status send_sc(struct scanner *s);
+
+static SANE_Status read_from_scanner(struct scanner *s);
+static SANE_Status copy_buffer(struct scanner *s, unsigned char * buf, int len);
+static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len);
+
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+
+static size_t maxStringSize (const SANE_String_Const strings[]);
+
+#endif /* KODAK_H */
diff --git a/backend/kodakaio.c b/backend/kodakaio.c
new file mode 100644
index 0000000..8c4583a
--- /dev/null
+++ b/backend/kodakaio.c
@@ -0,0 +1,3346 @@
+/*
+ * kodakaio.c - SANE library for Kodak ESP Aio scanners.
+ *
+ * Copyright (C) 2011-2013 Paul Newall
+ *
+ * Based on the Magicolor sane backend:
+ * Based on the epson2 sane backend:
+ * Based on Kazuhiro Sasayama previous
+ * work on epson.[ch] file from the SANE package.
+ * Please see those files for additional copyrights.
+ * Author: Paul Newall
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+
+ * Using avahi now 25/11/12 for net autodiscovery. Use configure option --enable-avahi
+ * 01/01/13 Now with adf, the scan can be padded to make up the full page length,
+ * or the page can terminate at the end of the paper. This is a selectable option.
+ */
+
+/* convenient lines to paste
+export SANE_DEBUG_KODAKAIO=10
+
+for ubuntu prior to 12.10
+./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-avahi --disable-latex BACKENDS=kodakaio
+
+for ubuntu 12.10
+./configure --prefix=/usr --libdir=/usr/lib/i386-linux-gnu --sysconfdir=/etc --localstatedir=/var --enable-avahi --disable-latex BACKENDS=kodakaio
+
+*/
+
+/* SANE-FLOW-DIAGRAM Kodakaio commands in [] brackets
+
+ - sane_init() : initialize backend, attach scanners(devicename,0)
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev)
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition [V,L,F,S,C,D,O,Z] first time or after cancel. [(F),E,G] every time
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . . - sane_cancel() : cancel operation, kill reader_process [(F), U]
+
+ . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
+ - sane_exit() : terminate use of backend, free devicename and device-struture
+*/
+/* FUNCTION-TREE
+ sane_init
+ sane_start
+ k_init_parametersta
+ k_lock_scanner
+ k_set_scanning_parameters
+ print_params
+ k_start_scan
+ cmd_start_scan
+ print_status
+ k_send
+ kodakaio_txrxack
+ sane_open
+ device_detect
+ sane_get_devices
+ init_options
+ sane_control_option
+ getvalue
+ setvalue
+ search_string_list
+ change_source
+ activateOption
+ deactivateOption
+ sane_get_parameters
+ print_params
+ sane_read
+ k_read
+ cmd_read_data (reads one block)
+ k_recv
+ cmp_array
+ sane_exit
+ free_devices
+ k_recv
+ kodakaio_net_read
+ dump_hex_buffer_dense
+ k_send
+ sanei_kodakaio_net_write_raw
+ dump_hex_buffer_dense
+ open_scanner
+ sanei_kodakaio_net_open
+ close_scanner
+ sanei_kodakaio_net_close
+ detect_usb
+ kodakaio_getNumberOfUSBProductIds
+ attach_one_config - (Passed to sanei_configure_attach)
+ kodakaio_getNumberOfUSBProductIds
+ kodak_network_discovery
+ client_callback
+ browse_callback
+ resolve_callback
+ ProcessAvahiDevice
+ attach_one_net
+ attach_one_net
+ attach
+ device_detect
+ attach_one_usb - (passed to sanei_usb_find_devices)
+ attach
+ device_detect
+ k_lock_scanner
+ kodakaio_txrx
+ k_send
+ k_recv
+ kodakaio_txrxack
+ k_send
+ k_recv
+ cmd_set_color_curve
+ kodakaio_expect_ack
+ k_recv
+ cmd_cancel_scan
+ kodakaio_txrxack
+ cmd_set_scanning_parameters
+ kodakaio_txrxack
+ device_detect
+ k_dev_init
+*/
+
+
+#define KODAKAIO_VERSION 02
+#define KODAKAIO_REVISION 4
+#define KODAKAIO_BUILD 6
+
+/* for usb (but also used for net though it's not required). */
+#define MAX_BLOCK_SIZE 32768
+
+#define SCANNER_READ_TIMEOUT 15
+/* POLL_ITN_MS sets the individual poll timeout for network discovery */
+#define POLL_ITN_MS 20
+
+
+/* debugging levels:
+In terminal use: export SANE_DEBUG_KODAKAIO=40 to set the level to 40 or whatever
+level you want.
+Then you can scan with scanimage or simple-scan from terminal and see debug info
+
+use these defines to promote certain functions that you are interested in
+define low values to make detail of a section appear when DBG level is low
+define a high value eg 99 to get normal behaviour. */
+#define DBG_READ 99
+#define DBG_AUTO 99 /* for autodiscovery */
+
+/*
+normal levels. This system is a plan rather than a reality
+ *
+ * 127 recv buffer
+ * 125 send buffer
+ * 35 fine-grained status and progress
+ * 30 sane_read
+ * 25 setvalue, getvalue, control_option
+ * 20 low-level (I/O) functions
+ * 15 mid-level functions
+ * 10 high-level functions
+ * 7 open/close/attach
+ * 6 print_params
+ * 5 basic functions
+ * 3 status info and progress
+ * 2 sane api
+ * 1 errors & warnings
+ */
+
+#include "sane/config.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <math.h>
+#include <poll.h>
+#include <time.h>
+
+#if WITH_AVAHI
+/* used for auto detecting network printers */
+#include <assert.h>
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+static AvahiSimplePoll *simple_poll = NULL; /* global because called by several functions */
+
+#endif
+
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_tcp.h"
+#include "../include/sane/sanei_udp.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+#include "kodakaio.h"
+
+/* vendor and product ids that are allowed */
+#define SANE_KODAKAIO_VENDOR_ID (0x040a)
+
+#define min(x,y) (((x)<(y))?(x):(y))
+
+/* I think these timeouts (ms) are defaults, overridden by any timeouts in the kodakaio.conf file */
+static int K_SNMP_Timeout = 3000; /* used for any auto detection method */
+static int K_Scan_Data_Timeout = 10000;
+static int K_Request_Timeout = 5000;
+
+/* This file is used to store directly the raster returned by the scanner for debugging
+If RawScanPath has no length it will not be created */
+FILE *RawScan = NULL;
+/* example: unsigned char RawScanPath[] = "TestRawScan.pgm"; */
+char RawScanPath[] = ""; /* empty path means no raw scan file is made */
+
+/*
+ * Devices supported by this backend
+*/
+
+/* kodak command strings */
+static unsigned char KodakEsp_V[] = {0x1b,'S','V',0,0,0,0,0}; /* version?? */
+static unsigned char KodakEsp_v[] = {0x1b,'s','v',0,0,0,0,0}; /* reply to version?? */
+static unsigned char KodakEsp_Lock[] = {0x1b,'S','L',0,0,0,0,0}; /* Locks scanner */
+static unsigned char KodakEsp_UnLock[] = {0x1b,'S','U',0,0,0,0,0}; /* Unlocks scanner */
+static unsigned char KodakEsp_Ack[] = {0x1b,'S','S',0,0,0,0,0}; /* Acknowledge for all commands */
+/* the bytes after esc S S 0 may indicate status: S S 0 1 = docs in adf */
+static unsigned char KodakEsp_F[] = {0x1b,'S','F',0,0,0,0,0}; /* Purpose not known? colour balance?*/
+static unsigned char KodakEsp_Comp[] = {0x1b,'S','C',3,8,3,0,0}; /* 3,8,3,1,0 does compression. */
+/* The compression method is unknown */
+/* static unsigned char KodakEsp_E[] = {0x1b,'S','E',1,0,0,0,0}; NET Purpose not known */
+/* the extra 1 below could be despeckle option? maybe only for Hero 9.1 but no errors with ESP5250 */
+static unsigned char KodakEsp_E[] = {0x1b,'S','E',1,1,0,0,0};
+static unsigned char KodakEsp_Go[] = {0x1b,'S','G',0,0,0,0,0}; /* Starts the scan */
+/* Other commands are: D (resolution), O (top left), Z (bottom right), R, G, B (curves) */
+
+/* What is the relationship between these and the ranges in cap? */
+static SANE_Int kodakaio_resolution_list[] = {75, 150, 300, 600, 1200};
+static SANE_Int kodakaio_depth_list[] = {1,8}; /* The first value is the number of following entries */
+
+/* strings to try and match the model ';' separator
+static unsigned char SupportedMatchString[] = "KODAK ESP;KODAK HERO;KODAK OFFICE HERO;ADVENT WiFi AIO;"; */
+
+static struct KodakaioCap kodakaio_cap[] = {
+/* usbid,commandtype, modelname, USBoutEP, USBinEP,
+ opticalres, {dpi range}, pointer to res list, res list size
+ max depth, pointer to depth list,
+ flatbed x range, flatbed y range,
+ adf present, adf duplex,
+ adf x range, adf y range (y range should be set a little shorter than the paper being scanned)
+
+The following are not used but may be in future
+commandtype, max depth, pointer to depth list
+*/
+
+/* list of cap data the first scanner is the default
+*/
+ /* KODAK AIO DEFAULT, */
+ {
+ 0x9999, "esp", "KODAK AIO DEFAULT",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(100), 0}, {0, SANE_FIX(100), 0} /* ADF x/y ranges (TODO!) */
+ },
+ /* KODAK ESP 5100, */
+ {
+ 0x4025, "esp", "KODAK ESP 5100 AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP 5300, */
+ {
+ 0x4026, "esp", "KODAK ESP 5300 AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP 5500, */
+ {
+ 0x4027, "esp", "KODAK ESP 5500 AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP 5000, */
+ {
+ 0x4028, "esp", "KODAK ESP 5000 Series AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP 3300, */
+ {
+ 0x4031, "esp", "KODAK ESP 3300 Series AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP5, */
+ {
+ 0x4032, "esp", "KODAK ESP 5 AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP7, */
+ {
+ 0x403E, "esp", "KODAK ESP 7 AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP9, */
+ {
+ 0x403F, "esp", "KODAK ESP 9 AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP5210 or 5250, */
+ {
+ 0x4041, "esp", "KODAK ESP 5200 Series AiO",
+ -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP3200 , */
+ {
+ 0x4043, "esp", "KODAK ESP 3200 Series AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP4100 , */
+ {
+ 0x4053, "esp", "KODAK ESP Office 4100 Series AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP6100 , */
+ {
+ 0x4054, "esp", "KODAK ESP Office 6100 Series AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP7200 , */
+ {
+ 0x4056, "esp", "KODAK ESP 7200 Series AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP C110 , */
+ {
+ 0x4057, "esp", "KODAK ESP C110 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP C115 , */
+ {
+ 0x4058, "esp", "KODAK ESP C115 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP 2150 , */
+ {
+ 0x4059, "esp", "KODAK ESP Office 2150 Series",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP C310 , */
+ {
+ 0x405D, "esp", "KODAK ESP C310 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP C315 , */
+ {
+ 0x405E, "esp", "KODAK ESP C315 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* ADVENT AW10, */
+ {
+ 0x4060, "esp", "ADVENT WiFi AIO AW10",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex. */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK HERO 6.1, */
+ {
+ 0x4062, "esp", "KODAK OFFICE HERO 6.1 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex. */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK HERO 7.1, */
+ {
+ 0x4063, "esp", "KODAK HERO 7.1 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_TRUE, /* ADF, duplex. */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK HERO 5.1, */
+ {
+ 0x4064, "esp", "KODAK HERO 5.1 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_TRUE, /* ADF, duplex.*/
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP9200 , */
+ {
+ 0x4065, "esp", "KODAK ESP 9200 Series AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK ESP2170 , */
+ {
+ 0x4066, "esp", "KODAK ESP Office 2170 Series",
+ -1, 0x82,
+ 1200, {75, 1200, 0}, kodakaio_resolution_list, 5, /* 1200 dpi optical, {from, to, 0} 5 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK HERO 9.1, */
+ {
+ 0x4067, "esp", "KODAK HERO 9.1 AiO",
+ -1, 0x82,
+ 1200, {75, 1200, 0}, kodakaio_resolution_list, 5, /* 1200 dpi optical, {from, to, 0} 5 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex. */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* KODAK HERO 4.1, */
+ {
+ 0x4069, "esp", "KODAK HERO 4.1 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_TRUE, SANE_FALSE, /* ADF, duplex.*/
+
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+
+ /* KODAK HERO 3.1, */
+ {
+ 0x406D, "esp", "KODAK HERO 3.1 AiO",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_TRUE, /* ADF, duplex. */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ },
+ /* spare use for specified usbid */
+ {
+ 0, "esp", "specified",
+ -1, 0x82,
+ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */
+ 8, kodakaio_depth_list, /* color depth max 8, list above */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */
+ SANE_FALSE, SANE_TRUE, /* ADF, duplex. */
+ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */
+ }
+};
+
+/****************************************************************************
+ * General configuration parameter definitions ****************************************************************************/
+
+
+/*
+ * Definition of the mode_param struct, that is used to
+ * specify the valid parameters for the different scan modes.
+ *
+ * The depth variable gets updated when the bit depth is modified.
+ */
+
+static struct mode_param mode_params[] = {
+ /* {0x00, 1, 1}, // Lineart, 1 color, 1 bit */
+ {0x02, 1, 8}, /* Grayscale, 1 color, 8 bit */
+ {0x03, 3, 24} /* Color, 3 colors, 24 bit */
+};
+
+static SANE_String_Const mode_list[] = {
+ /* SANE_VALUE_SCAN_MODE_LINEART, */
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static const SANE_String_Const adf_mode_list[] = {
+ SANE_I18N("Simplex"),
+ SANE_I18N("Duplex"),
+ NULL
+};
+
+/* Define the different scan sources */
+#define FBF_STR SANE_I18N("Flatbed")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+/*
+ * source list need one dummy entry (save device settings is crashing).
+ * NOTE: no const - this list gets created while exploring the capabilities
+ * of the scanner. Here space is reserved for 3 entries + NULL ?
+ */
+
+static SANE_String_Const source_list[] = {
+ FBF_STR,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* prototypes */
+static SANE_Status attach_one_usb(SANE_String_Const devname);
+static SANE_Status attach_one_net(SANE_String_Const devname, unsigned int device);
+void kodakaio_com_str(unsigned char *buf, char *fmt_buf);
+int cmparray (unsigned char *array1, unsigned char *array2, size_t len);
+static struct KodakaioCap *get_device_from_identification (const char *ident, const char *vid, const char *pid);
+void ProcessAvahiDevice(const char *device_id, const char *vid, const char *pid, const char *ip_addr);
+
+
+/* Some utility functions */
+
+static size_t
+max_string_size(const SANE_String_Const strings[])
+{
+/* returns the length of the longest string in an array of strings */
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++) {
+ size = strlen(strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+
+static void
+print_params(const SANE_Parameters params, int level)
+{
+ DBG(level, "formats: binary=?, grey=%d, colour=%d\n",SANE_FRAME_GRAY, SANE_FRAME_RGB );
+ DBG(level, "params.format = %d\n", params.format);
+ DBG(level, "params.last_frame = %d\n", params.last_frame);
+ DBG(level, "params.bytes_per_line = %d\n", params.bytes_per_line);
+ DBG(level, "params.pixels_per_line = %d\n", params.pixels_per_line);
+ DBG(level, "params.lines = %d\n", params.lines);
+ DBG(level, "params.depth = %d\n", params.depth);
+}
+
+static void
+print_status(KodakAio_Scanner *s,int level)
+{
+ DBG(level, "print_status with level %d\n", level);
+ DBG(level, "s->bytes_unread = %d\n", s->bytes_unread);
+/*
+ DBG(level, "params.last_frame = %d\n", params.last_frame);
+ DBG(level, "params.bytes_per_line = %d\n", params.bytes_per_line);
+ DBG(level, "params.pixels_per_line = %d\n", params.pixels_per_line);
+ DBG(level, "params.lines = %d\n", params.lines);
+ DBG(level, "params.depth = %d\n", params.depth);
+*/
+}
+
+/****************************************************************************
+ * Low-level Network communication functions ****************************************************************************/
+
+/* We don't have a packet wrapper, which holds packet size etc., so we
+ don't have to use a *read_raw and a *_read function... */
+static int
+kodakaio_net_read(struct KodakAio_Scanner *s, unsigned char *buf, size_t wanted,
+ SANE_Status * status)
+{
+ size_t size, read = 0;
+ struct pollfd fds[1];
+ int pollreply;
+
+ *status = SANE_STATUS_GOOD;
+
+ /* poll for data-to-be-read (using K_Request_Timeout) */
+ fds[0].fd = s->fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ if ((pollreply = poll (fds, 1, K_Request_Timeout)) <= 0) {
+ if (pollreply ==0)
+ DBG(1, "net poll timeout\n");
+ else
+ /* pollreply is -ve */
+ DBG(1, "net poll error\n");
+ *status = SANE_STATUS_IO_ERROR;
+ return read;
+ }
+ else if(fds[0].revents & POLLIN) {
+ while (read < wanted) {
+ size = sanei_tcp_read(s->fd, buf + read, wanted - read);
+
+ if (size == 0)
+ break;
+
+ read += size;
+ }
+
+/* this error removed 28/12/12 because adf scans end with less data than wanted
+ if (read < wanted)
+ *status = SANE_STATUS_IO_ERROR;
+ */
+ DBG(32, "net read %d bytes:%x,%x,%x,%x,%x,%x,%x,%x\n",read,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]);
+ return read;
+ }
+ else
+ DBG(1, "Unknown problem with poll\n");
+ return read;
+}
+
+/* kodak does not pad commands like magicolor, so there's only a write_raw function */
+static int
+sanei_kodakaio_net_write_raw(struct KodakAio_Scanner *s,
+ const unsigned char *buf, size_t buf_size,
+ SANE_Status *status)
+{
+ DBG(32, "net write:%x,%x,%x,%x,%x,%x,%x,%x\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]);
+
+ sanei_tcp_write(s->fd, buf, buf_size);
+ /* TODO: Check whether sending failed... */
+
+ *status = SANE_STATUS_GOOD;
+ return buf_size;
+}
+
+static SANE_Status
+sanei_kodakaio_net_open(struct KodakAio_Scanner *s)
+{
+ struct timeval tv;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ DBG(5, "%s\n", __func__);
+
+ setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+sanei_kodakaio_net_close(struct KodakAio_Scanner *s)
+{
+ NOT_USED(s);
+ /* Does nothing - maybe should close the socket ? */
+ return SANE_STATUS_GOOD;
+}
+
+
+/****************************************************************************
+ * Low-level USB communication functions ****************************************************************************/
+
+static int
+kodakaio_getNumberOfUSBProductIds (void)
+{
+ return sizeof (kodakaio_cap) / sizeof (struct KodakaioCap);
+}
+
+/****************************************************************************
+ * low-level communication commands ****************************************************************************/
+
+static void dump_hex_buffer_dense (int level, const unsigned char *buf, size_t buf_size)
+{
+ size_t k;
+ char msg[1024], fmt_buf[1024];
+ memset (&msg[0], 0x00, 1024);
+ memset (&fmt_buf[0], 0x00, 1024);
+ for (k = 0; k < min(buf_size, 80); k++) {
+ if (k % 16 == 0) {
+ if (k>0) {
+ DBG (level, "%s\n", msg);
+ memset (&msg[0], 0x00, 1024);
+ }
+ sprintf (fmt_buf, " 0x%04lx ", (unsigned long)k);
+ strcat (msg, fmt_buf);
+ }
+ if (k % 8 == 0) {
+ strcat (msg, " ");
+ }
+ sprintf (fmt_buf, " %02x" , buf[k]);
+ strcat (msg, fmt_buf);
+ }
+ if (msg[0] != 0 ) {
+ DBG (level, "%s\n", msg);
+ }
+}
+
+/* changing params to char seems to cause a stack problem */
+void kodakaio_com_str(unsigned char *buf, char *fmt_buf)
+{
+/* returns a printable string version of the first 8 bytes assuming they are a kodakaio command*/
+ if(buf[0] == 0x1b) {
+ sprintf (fmt_buf, "esc %c %c %02x %02x %02x %02x %02x",
+ buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ }
+ else {
+ sprintf (fmt_buf, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ buf[0],buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ }
+}
+
+static int
+k_send(KodakAio_Scanner * s, void *buf, size_t buf_size, SANE_Status * status)
+{
+ char fmt_buf[25];
+
+ kodakaio_com_str(buf, fmt_buf);
+ DBG(15, "%s: size = %lu :%s\n", __func__, (u_long) buf_size, fmt_buf);
+
+ if (DBG_LEVEL >= 125) {
+ const unsigned char *s = buf;
+ DBG(125, "complete buffer:\n");
+ dump_hex_buffer_dense (125, s, buf_size);
+ }
+
+ if (s->hw->connection == SANE_KODAKAIO_NET) {
+ return sanei_kodakaio_net_write_raw(s, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_KODAKAIO_USB) {
+ size_t n;
+ n = buf_size;
+ *status = sanei_usb_write_bulk(s->fd, buf, &n);
+ DBG(50, "USB: wrote %lu bytes, status: %s\n", (unsigned long)n, sane_strstatus(*status));
+ return n;
+ }
+
+ *status = SANE_STATUS_INVAL;
+ return 0;
+}
+
+static ssize_t
+k_recv(KodakAio_Scanner * s, void *buf, ssize_t buf_size,
+ SANE_Status * status)
+{
+/* requests and receives data this function makes the split between USB and NET
+this function called by a number of others
+
+In USB mode, this function will wait until data is available for a maximum of SCANNER_READ_TIMEOUT seconds.
+*/
+ ssize_t n = 0;
+ char fmt_buf[25];
+ time_t time_start;
+ time_t time_now;
+ struct timespec usb_delay, usb_rem;
+ usb_delay.tv_sec = 0;
+ usb_delay.tv_nsec = 300000000; /* 0.3 sec */
+
+ if (s->hw->connection == SANE_KODAKAIO_NET) {
+
+ time(&time_start);
+ DBG(min(16,DBG_READ), "[%ld] %s: net req size = %ld ", (long) time_start, __func__, (long) buf_size);
+ n = kodakaio_net_read(s, buf, buf_size, status);
+ DBG(min(16,DBG_READ), "returned %d\n", n);
+
+ } else if (s->hw->connection == SANE_KODAKAIO_USB) {
+ /* Start the clock for USB timeout */
+ time(&time_start);
+
+ /* Loop until we have data */
+ while (n == 0) {
+ n = buf_size;
+/* but what if the data is an exact number of blocks? */
+ DBG(min(16,DBG_READ), "[%ld] %s: usb req size = %ld ", (long) time_start, __func__, (long) n);
+ *status = sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf, (size_t *) & n);
+ DBG(min(16,DBG_READ), "returned %ld\n", (long) n);
+
+ if(*status != SANE_STATUS_GOOD) {
+
+ DBG(min(16,DBG_READ), "sanei_usb_read_bulk gave %s\n", sane_strstatus(*status));
+
+ if (*status == SANE_STATUS_EOF) {
+ /* If we have EOF status, wait for more data */
+ time(&time_now);
+ if (difftime(time_now, time_start) < SCANNER_READ_TIMEOUT) {
+ nanosleep(&usb_delay, &usb_rem);
+ }
+ else {
+ /* Timeout */
+ return n;
+ }
+ }
+ else {
+ /* If we've encountered another type of error, return */
+ return n;
+ }
+ }
+ }
+ }
+
+ if (n == 8) {
+ kodakaio_com_str(buf, fmt_buf);
+ DBG(min(14,DBG_READ), "%s: size = %ld, got %s\n", __func__, (long int)n, fmt_buf);
+ }
+ /* dump buffer if appropriate */
+ if (DBG_LEVEL >= 127 && n > 0) {
+ const unsigned char* b=buf;
+ dump_hex_buffer_dense (125, b, buf_size);
+ }
+ return n;
+}
+
+
+static SANE_Status
+kodakaio_expect_ack(KodakAio_Scanner *s, unsigned char *rxbuf)
+/* gets 8 byte reply, checks reply is an Ack and returns appropriate status */
+{
+ SANE_Status status;
+
+ k_recv(s, rxbuf, 8, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+ /* strncmp ignores diffent possible responses like escSS00000 and escSS02000 */
+ if (strncmp((char *)KodakEsp_Ack,(char *)rxbuf,4)!=0) {
+ DBG (1, "No Ack received, Expected 0x%2x %2x %2x %2x... got 0x%2x %2x %2x %2x...\n",
+ KodakEsp_Ack[0], KodakEsp_Ack[1], KodakEsp_Ack[2], KodakEsp_Ack[3],rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return status;
+}
+
+static SANE_Status
+kodakaio_txrx(KodakAio_Scanner *s, unsigned char *txbuf, unsigned char *rxbuf)
+/* Sends 8 byte data to scanner and returns reply and appropriate status. */
+{
+ SANE_Status status;
+
+ k_send(s, txbuf, 8, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+ k_recv(s, rxbuf, 8, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status));
+ return status;
+ }
+ return status;
+}
+
+static SANE_Status
+kodakaio_txrxack(KodakAio_Scanner *s, unsigned char *txbuf, unsigned char *rxbuf)
+/*
+Sends 8 byte data to scanner, gets 8 byte reply, checks reply is an Ack
+and returns appropriate status
+*/
+{
+ SANE_Status status;
+
+ k_send(s, txbuf, 8, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ k_recv(s, rxbuf, 8, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status));
+ return status;
+ }
+ /* strncmp ignores different possible responses like escSS00000 and escSS02000 */
+ if (strncmp((char *)KodakEsp_Ack,(char *)rxbuf,3) == 0) { /* was 4 byte comp */
+ if (rxbuf[4] == 0x01 && s->adf_loaded == SANE_FALSE) {
+ s->adf_loaded = SANE_TRUE;
+ DBG(5, "%s: News - docs in ADF\n", __func__);
+ }
+ else if (rxbuf[4] != 0x01 && s->adf_loaded == SANE_TRUE) {
+ s->adf_loaded = SANE_FALSE;
+ DBG(5, "%s: News - ADF is empty\n", __func__);
+ }
+ }
+ else {
+ DBG (1, "No Ack received, Sent 0x%2x %2x %2x %2x... got 0x%2x %2x %2x %2x...\n",
+ txbuf[0], txbuf[1], txbuf[2], txbuf[3],rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return status;
+}
+
+/*
+ * high-level communication commands
+*/
+
+static SANE_Status
+k_hello (KodakAio_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char reply[8];
+ char fmt_buf[25];
+
+ DBG(5, "%s\n", __func__);
+ if((status = kodakaio_txrx(s, KodakEsp_V, reply))!= SANE_STATUS_GOOD) {
+ DBG(1, "%s: KodakEsp_V failure, %s\n", __func__, sane_strstatus(status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (strncmp((char *) reply, (char *) KodakEsp_v, 3)!=0) {
+ kodakaio_com_str(reply, fmt_buf);
+ DBG(1, "%s: KodakEsp_v err, got %s\n", __func__, fmt_buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG(5, "%s: OK %s\n", __func__, sane_strstatus(status));
+ return status;
+}
+
+/* Start scan command */
+static SANE_Status
+cmd_start_scan (SANE_Handle handle, size_t expect_total)
+/* expect_total is the expected total no of bytes just for info -ve value not a problem? or is it for size_t? */
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char reply[8];
+/*send the start command here */
+ print_status(s, 5);
+/*adf added 20/2/12, apparently an extra KodakEsp_F is sent when the adf is used */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* adf is in use */
+ if (! s->adf_loaded) return SANE_STATUS_CANCELLED; /* was SANE_STATUS_NO_DOCS; */
+ if (kodakaio_txrxack(s, KodakEsp_F, reply)!= SANE_STATUS_GOOD) {
+ DBG(1, "%s: Did not get a good reply to KodakEsp_F\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (kodakaio_txrxack(s, KodakEsp_E, reply)!= SANE_STATUS_GOOD) {
+ DBG(1, "%s: Did not get a good reply to KodakEsp_E\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(20, "starting the scan, expected total bytes %d\n",expect_total);
+ k_send(s, KodakEsp_Go, 8, &status);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG(1, "%s: KodakEsp_Go command NOT successfully sent\n", __func__);
+ else {
+ DBG(30, "%s: KodakEsp_Go command successfully sent\n", __func__);
+ s->scanning = SANE_TRUE;
+ }
+ return status;
+}
+
+static SANE_Status
+cmd_cancel_scan (SANE_Handle handle)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ unsigned char reply[8];
+/* adf added 20/2/12 should it be adf? or adf with paper in? */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* adf */
+ if (kodakaio_txrxack(s, KodakEsp_F, reply)!= SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR;
+ if (kodakaio_txrxack(s, KodakEsp_UnLock, reply)!= SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR;
+ DBG(5, "%s unlocked the scanner with adf F U\n", __func__);
+ }
+ else { /* no adf */
+ if (kodakaio_txrxack(s, KodakEsp_UnLock, reply)!= SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR;
+ DBG(5, "%s unlocked the scanner U\n", __func__);
+ }
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+cmd_get_scanning_parameters(SANE_Handle handle,
+ SANE_Frame *format, SANE_Int *depth,
+ SANE_Int *data_pixels, SANE_Int *pixels_per_line,
+ SANE_Int *lines)
+{
+/* data_pixels is per line.
+Old mc cmd read this stuff from the scanner. I don't think kodak can do that easily */
+
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ NOT_USED (format);
+ NOT_USED (depth);
+
+ DBG(10, "%s\n", __func__);
+
+ /* Calculate returned values */
+ *lines = s->params.lines;
+ *pixels_per_line = s->params.pixels_per_line;
+ *data_pixels = s->params.pixels_per_line;
+
+ DBG (20, "%s: data_pixels = %u, lines = %u, "
+ "pixels_per_line = %u)\n", __func__,
+ *data_pixels, *lines, *pixels_per_line);
+ return status;
+}
+
+static SANE_Status
+cmd_set_color_curve(SANE_Handle handle, unsigned char col)
+{
+/* sends the color curve data for one color*/
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char tx_col[8];
+ unsigned char rx[8];
+ unsigned char tx_curve[256];
+ unsigned char i;
+ DBG(32, "%s: start\n", __func__);
+ tx_col[0]=0x1b; tx_col[1]='S'; tx_col[2]='K'; tx_col[3]=col; tx_col[4]=0; tx_col[5]=0; tx_col[6]=0; tx_col[7]=0;
+/* linear curve now but could send tailor made curves in future */
+ for(i=0;i<255;++i) tx_curve[i]=i;
+ k_send(s, tx_col, 8, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, "curve command");
+ return status;
+ }
+ k_send(s, tx_curve, 256, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, "curve data");
+ return status;
+ }
+ if (kodakaio_expect_ack(s, rx) != SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR;
+ DBG(10, "%s: sent curve OK, \n", __func__);
+ return status;
+}
+
+/* Set scanning parameters command low level */
+static SANE_Status
+cmd_set_scanning_parameters(SANE_Handle handle,
+ int resolution,
+ int tl_x, int tl_y, int width, int height, unsigned char source)
+
+/* NB. here int tl_x, int tl_y, int width, int height are in DPI units, not optres units! */
+/* sends params to scanner, but should we store them too? */
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char tx_S[8];
+ unsigned char tx_dpi[8];
+ unsigned char tx_topleft[8];
+ unsigned char tx_widthheight[8];
+ unsigned char bufread[8];
+ int i;
+
+/*don't know the purpose of F yet. windows USB repeated 4 x why? does it affect the colour balance?*/
+ DBG(8, "%s\n", __func__);
+ for(i=0;i<4;++i) {
+ kodakaio_txrxack(s, KodakEsp_F, bufread);
+ sleep(1);
+ }
+/* kodakaio_txrxack(s, KodakEsp_F, bufread); for net, just once */
+
+/* Source? bed /ADF */
+ tx_S[0]=0x1b; tx_S[1]='S'; tx_S[2]='S'; tx_S[3]=source; tx_S[4]=0; tx_S[5]=0; tx_S[6]=0; tx_S[7]=0;
+ kodakaio_txrxack(s, tx_S, bufread);
+
+/* Compression */
+ kodakaio_txrxack(s, KodakEsp_Comp, bufread);
+
+/* DPI resolution */
+ tx_dpi[0]=0x1b;
+ tx_dpi[1]='S';
+ tx_dpi[2]='D';
+ tx_dpi[3]=resolution & 0xff;
+ tx_dpi[4]=(resolution >> 8) & 0xff;
+ tx_dpi[5]=resolution & 0xff;
+ tx_dpi[6]=(resolution >> 8) & 0xff;
+ tx_dpi[7]=0;
+ kodakaio_txrxack(s, tx_dpi, bufread);
+
+/* colour curves don't seem to be sent for usb preview
+but it seems to do no harm to send them */
+ cmd_set_color_curve(s, 'R');
+ cmd_set_color_curve(s, 'G');
+ cmd_set_color_curve(s, 'B');
+
+
+/* Origin top left s->tl_x and s->tl_y are in optres units
+this command needs actual DPI units*/
+ DBG(20, "%s: left (DPI)=%d, top (DPI)=%d\n", __func__, tl_x , tl_y);
+
+ tx_topleft[0]=0x1b;
+ tx_topleft[1]='S';
+ tx_topleft[2]='O';
+ tx_topleft[3]=(tl_x) & 0xff;
+ tx_topleft[4]=((tl_x) >> 8) & 0xff;
+ tx_topleft[5]=(tl_y) & 0xff;
+ tx_topleft[6]=((tl_y) >> 8) & 0xff;
+ tx_topleft[7]=0;
+ kodakaio_txrxack(s, tx_topleft, bufread);
+
+/* Z width height note the s->width and s->height are in optres units
+this command needs actual DPI units*/
+ tx_widthheight[0]=0x1b;
+ tx_widthheight[1]='S';
+ tx_widthheight[2]='Z';
+ tx_widthheight[3]=(width) & 0xff;
+ tx_widthheight[4]=((width) >> 8) & 0xff;
+ tx_widthheight[5]=(height) & 0xff;
+ tx_widthheight[6]=((height) >> 8) & 0xff;
+ tx_widthheight[7]=0;
+ kodakaio_txrxack(s, tx_widthheight, bufread);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG(1, "%s: Data NOT successfully sent\n", __func__);
+ else
+ DBG(20, "%s: Data successfully sent\n", __func__);
+ return status;
+}
+
+int
+cmparray (unsigned char *array1, unsigned char *array2, size_t len)
+{
+/* compares len bytes of the arrays returns 0 if they match
+returns the first missmatch position if they don't match */
+unsigned int i;
+ for(i=0; i<len; ++i)
+ {
+ if(array1[i] != array2[i]) return -1;
+ }
+ return 0;
+}
+
+
+
+static SANE_Status
+cmd_read_data (SANE_Handle handle, unsigned char *buf, size_t *len)
+{
+/*
+cmd_read_data is only used in k_read. It reads one block of data
+read data using k_recv until you get the ackstring
+when you get the ackstring return s->ack and do padding if the padding option is selected
+if no padding option return EOF
+*/
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Status status;
+ int oldtimeout = K_Request_Timeout;
+ size_t bytecount;
+ unsigned char *Last8; /* will point to the last 8 chars in buf */
+ int i, line, lines;
+
+ if (s->ack && s->val[OPT_PADDING].w) {
+ /* do padding of whole block*/
+ /* memset(buf, 0x80, *len); need to work out the background colour for this */
+ lines = *len / s->params.bytes_per_line;
+ for (line=0; line < lines; ++line) {
+ for (i=0; i< s->params.pixels_per_line; ++i) {
+ buf[line * s->params.bytes_per_line + i] = s->background[0]; /*red */
+ buf[line * s->params.bytes_per_line + s->params.pixels_per_line + i] = s->background[1]; /*green */
+ buf[line * s->params.bytes_per_line + 2 * s->params.pixels_per_line + i] = s->background[2]; /*blue */
+ }
+ }
+ s->bytes_unread -= *len;
+ if (s->bytes_unread < 0)
+ s->bytes_unread = 0;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (s->ack && !s->val[OPT_PADDING].w) {
+ s->bytes_unread = 0;
+ s->eof = SANE_TRUE;
+ return SANE_STATUS_EOF;
+ }
+
+ /* Temporarily set the poll timeout long instead of short,
+ * because a color scan needs >5 seconds to initialize. Is this needed for kodak? maybe */
+ K_Request_Timeout = K_Scan_Data_Timeout;
+ sanei_usb_set_timeout (K_Scan_Data_Timeout);
+ bytecount = k_recv(s, buf, *len, &status);
+ K_Request_Timeout = oldtimeout;
+ sanei_usb_set_timeout (oldtimeout);
+
+/* We may need to do some special thing with the block request size to cope with usb blocks of any length
+in order to keep the ack bytes, when using adf, at the end of one block ie not split between blocks.
+But it seems that the scanner takes care of that, and gives you the ack as a separate 8 byte block */
+
+ if (bytecount >= 8) {
+ /* it may be the last block from the scanner so look for Ack response in last 8 bytes */
+ Last8 = buf + bytecount - 8;
+
+ /* only compare 4 bytes because we sometimes get escSS02.. or escSS00..
+ is 4 the right number ? */
+ if (cmparray(Last8,KodakEsp_Ack,4) == 0) {
+ DBG(min(10,DBG_READ), "%s: found KodakEsp_Ack at %d bytes of %d\n", __func__, bytecount, *len);
+ s->ack = SANE_TRUE;
+ *len = bytecount - 8; /* discard the Ack response */
+ s->bytes_unread -= *len; /* return a short block */
+ }
+ else {
+ /* a not full buffer is returned usb does this */
+ DBG(min(10,DBG_READ), "%s: buffer not full, got %d bytes of %d\n", __func__, bytecount, *len);
+ *len = bytecount;
+ s->bytes_unread -= bytecount;
+ }
+ }
+ else {
+ DBG(min(1,DBG_READ), "%s: tiny read, got %d bytes of %d\n", __func__, bytecount, *len);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (*len > s->params.bytes_per_line) {
+ /* store average colour as background. That's not the ideal method but it's easy to implement. */
+ lines = *len / s->params.bytes_per_line;
+ s->background[0] = 0;
+ s->background[1] = 0;
+ s->background[2] = 0;
+
+ for (line=0; line < lines; ++line) {
+ for (i=0; i< s->params.pixels_per_line; ++i) {
+ s->background[0] += buf[line * s->params.bytes_per_line + i]; /*red */
+ s->background[1] += buf[line * s->params.bytes_per_line + s->params.pixels_per_line + i]; /*green */
+ s->background[2] += buf[line * s->params.bytes_per_line + 2 * s->params.pixels_per_line + i]; /*blue */
+ }
+ }
+ s->background[0] = s->background[0] / (lines * s->params.pixels_per_line);
+ s->background[1] = s->background[1] / (lines * s->params.pixels_per_line);
+ s->background[2] = s->background[2] / (lines * s->params.pixels_per_line);
+
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ if (s->bytes_unread <= 0)
+ DBG(min(2,DBG_READ), "%s: Page fully read %d blocks, %ld bytes unread\n", __func__, s->counter, (long) s->bytes_unread);
+ else
+ DBG(min(20,DBG_READ), "%s: Image data successfully read %ld bytes, %ld bytes unread\n", __func__, (long) bytecount, (long) s->bytes_unread);
+ else if (s->ack) /* was (status == SANE_STATUS_EOF) */
+ DBG(min(2,DBG_READ), "%s: scanner data read ended %d blocks %ld bytes, %ld bytes unread\n", __func__, s->counter, (long) bytecount, (long) s->bytes_unread);
+ else
+ DBG(min(1,DBG_READ), "%s: Image data read stopped with %s after %d blocks %ld bytes, %ld bytes unread\n", __func__, sane_strstatus(status), s->counter, (long) bytecount, (long) s->bytes_unread);
+
+ return status;
+}
+
+
+
+/****************************************************************************
+ * kodakaio backend high-level operations ****************************************************************************/
+
+static void
+k_dev_init(Kodak_Device *dev, const char *devname, int conntype)
+{
+ DBG(5, "%s for %s\n", __func__,devname);
+
+ dev->name = NULL;
+ dev->model = NULL;
+ dev->connection = conntype;
+ dev->sane.name = devname;
+ dev->sane.model = NULL;
+ dev->sane.type = "flatbed scanner";
+ dev->sane.vendor = "Kodak";
+ dev->cap = &kodakaio_cap[CAP_DEFAULT];
+}
+
+
+static SANE_Status
+k_set_model(KodakAio_Scanner * s, const char *model, size_t len)
+{
+ unsigned char *buf;
+ unsigned char *p;
+ struct Kodak_Device *dev = s->hw;
+
+ if (len<1) return SANE_STATUS_INVAL; /* to handle missing model */
+
+ buf = malloc(len + 1);
+ if (buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy(buf, model, len);
+ buf[len] = '\0';
+
+ p = &buf[len - 1];
+
+ while (*p == ' ') {
+ *p = '\0';
+ p--;
+ }
+
+ if (dev->model)
+ free(dev->model);
+
+ dev->model = strndup((const char *) buf, len);
+ dev->sane.model = dev->model;
+ DBG(10, "%s: model is '%s'\n", __func__, dev->model);
+
+ free(buf);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+k_set_device (SANE_Handle handle, SANE_Word device)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ Kodak_Device *dev = s->hw;
+ int n;
+
+ DBG(10, "%s: 0x%x\n", __func__, device);
+
+ for (n = 0; n < NELEMS (kodakaio_cap); n++) {
+ if (kodakaio_cap[n].id == device)
+ break;
+ }
+ if (n < NELEMS(kodakaio_cap)) {
+ dev->cap = &kodakaio_cap[n];
+ } else {
+ dev->cap = &kodakaio_cap[CAP_DEFAULT];
+ DBG(1, " unknown device 0x%x, using default %s\n",
+ device, dev->cap->model);
+ }
+ k_set_model (s, dev->cap->model, strlen (dev->cap->model));
+
+}
+
+static SANE_Status
+k_discover_capabilities(KodakAio_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Kodak_Device *dev = s->hw;
+ SANE_String_Const *source_list_add = source_list;
+
+ DBG(10, "%s\n", __func__);
+
+ /* always add flatbed */
+ *source_list_add++ = FBF_STR;
+ /* TODO: How can I check for existence of an ADF??? */
+ if (dev->cap->ADF == SANE_TRUE) {
+ *source_list_add++ = ADF_STR;
+ DBG(10, "%s: added adf to list\n", __func__);
+ }
+
+ /* TODO: Is there any capability that we can extract from the
+ * device by some scanner command? So far, it looks like
+ * the device does not support any reporting.
+ */
+
+ dev->x_range = &dev->cap->fbf_x_range;
+ dev->y_range = &dev->cap->fbf_y_range;
+
+ DBG(10, " x-range: %f %f\n", SANE_UNFIX(dev->x_range->min), SANE_UNFIX(dev->x_range->max));
+ DBG(10, " y-range: %f %f\n", SANE_UNFIX(dev->y_range->min), SANE_UNFIX(dev->y_range->max));
+
+ DBG(5, "End of %s, status:%s\n", __func__, sane_strstatus(status));
+ *source_list_add = NULL; /* add end marker to source list */
+ return status;
+}
+
+static SANE_Status
+k_setup_block_mode (KodakAio_Scanner *s)
+{
+/* works for USB and for net changing to make block size = a number of complete lines 28/12/12 */
+ s->block_len = MAX_BLOCK_SIZE / s->scan_bytes_per_line * s->scan_bytes_per_line;
+ s->bytes_unread = s->data_len;
+ s->counter = 0;
+ s->bytes_read_in_line = 0;
+ if (s->line_buffer)
+ free(s->line_buffer);
+ s->line_buffer = malloc(s->scan_bytes_per_line);
+ if (s->line_buffer == NULL) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (10, " %s: Setup block mode - scan_bytes_per_line=%d, pixels_per_line=%d, depth=%d, data_len=%d, block_len=%d, blocks=%d, last_len=%d\n",
+ __func__, s->scan_bytes_per_line, s->params.pixels_per_line, s->params.depth, s->data_len, s->block_len, s->blocks, s->last_len);
+ return SANE_STATUS_GOOD;
+}
+
+/* Call the commands to set scanning parameters
+In the Kodak Aio the parameters are:
+(x1b,"S","F",0,0,0,0,0)
+(x1b,"S","S",1,0,0,0,0)
+#It looks like the 4th param byte of the C command is compression 1=compress, 0=no compression
+#1st (or 3rd) param could be channels, 2nd could be bits per channel
+(x1b,"S","C",3,8,3,0,0) #3,8,3,1,0) was what the kodak software used with compression
+(x1b,"S","D",LowByte(Res),HighByte(Res),LowByte(Res),HighByte(Res),0) #resolution in DPI
+SendColour(tcpCliSock,"R")
+SendColour(tcpCliSock,"G")
+SendColour(tcpCliSock,"B")
+(x1b,"S","O",LowByte(x0*Res),HighByte(x0*Res),LowByte(y0*Res),HighByte(y0*Res),0) #top left in pixels
+(x1b,"S","Z",LowByte(x1*Res),HighByte(x1*Res),LowByte(y1*Res),HighByte(y1*Res),0) #bot right in pixels
+(x1b,"S","E",1,0,0,0,0)
+
+*/
+
+static SANE_Status
+k_lock_scanner (KodakAio_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char reply[8];
+
+ status = k_hello(s);
+ if(status != SANE_STATUS_GOOD) {
+ DBG(1, "%s k_hello failed with %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ if (kodakaio_txrxack(s, KodakEsp_Lock, reply)!= SANE_STATUS_GOOD) {
+ DBG(1, "%s Could not lock scanner\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (s->adf_loaded) DBG(5, "%s scanner locked, with docs in adf\n", __func__);
+ else DBG(5, "%s scanner locked, with no docs in adf\n", __func__);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+k_set_scanning_parameters(KodakAio_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char rs, source;
+ SANE_Int scan_pixels_per_line = 0;
+ int dpi, optres;
+
+ dpi = s->val[OPT_RESOLUTION].w;
+ optres = s->hw->cap->optical_res;
+
+ /* Find the resolution in the res list and assign the index (rs) */
+ for (rs=0; rs < s->hw->cap->res_list_size; rs++ ) {
+ if ( dpi == s->hw->cap->res_list[rs] )
+ break;
+ }
+
+ /* ADF used? */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) {
+ source = 0x00;
+ } else {
+ source = 0x01;
+ }
+
+
+ /* TODO: Any way to set PREVIEW??? */
+
+ /* Remaining bytes unused */
+ status = cmd_set_scanning_parameters(s, dpi,
+ s->left * dpi / optres, s->top * dpi / optres, /* top/left start (dpi units)*/
+ s->params.pixels_per_line, s->params.lines, /* extent was s->width, s->height*/
+ source); /* source */
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "%s: Command cmd_set_scanning_parameters failed, %s\n",
+ __func__, sane_strstatus(status));
+
+ /* Now query the scanner for the current image parameters */
+ status = cmd_get_scanning_parameters (s,
+ &s->params.format, &s->params.depth,
+ &scan_pixels_per_line,
+ &s->params.pixels_per_line, &s->params.lines);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: Command cmd_get_scanning_parameters failed, %s\n",
+ __func__, sane_strstatus(status));
+ return status;
+ }
+
+ /* Calculate how many bytes are really used per line */
+ s->params.bytes_per_line = ceil (s->params.pixels_per_line * s->params.depth / 8.0);
+ if (s->val[OPT_MODE].w == MODE_COLOR)
+ s->params.bytes_per_line *= 3;
+
+ /* Calculate how many bytes per line will be returned by the scanner.
+ magicolor needed this because it uses padding. Scan bytes per line != image bytes per line
+ * The values needed for this are returned by get_scanning_parameters */
+ s->scan_bytes_per_line = 3 * ceil (scan_pixels_per_line * s->params.depth / 8.0);
+ s->data_len = s->scan_bytes_per_line * floor (s->height * dpi / optres + 0.5); /* NB this is the length for a full scan */
+ DBG (1, "Check: scan_bytes_per_line = %d s->params.bytes_per_line = %d \n", s->scan_bytes_per_line, s->params.bytes_per_line);
+
+/* k_setup_block_mode at the start of each page for adf to work */
+ status = k_setup_block_mode (s);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "%s: Command k_setup_block_mode failed, %s\n",
+ __func__, sane_strstatus(status));
+
+ DBG (18, "%s: bytes_read_in_line: %d\n", __func__, s->bytes_read_in_line);
+ return status;
+}
+
+static SANE_Status
+k_check_adf(KodakAio_Scanner * s)
+{
+/* 20/2/12 detect paper in the adf? acknowledge esc S S 00 01.. ?*/
+
+ if (! s->adf_loaded) {
+ DBG(5, "%s: NO DOCS\n", __func__);
+ return SANE_STATUS_NO_DOCS;
+ }
+ else {
+
+ /* TODO: Check for jam in ADF */
+ DBG(5, "%s: DOCS IN ADF\n", __func__);
+ return SANE_STATUS_GOOD;
+ }
+}
+
+static SANE_Status
+k_scan_finish(KodakAio_Scanner * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ DBG(10, "%s called\n", __func__);
+
+ /* If we have not yet read all data, cancel the scan */
+ if (s->buf && !s->eof)
+ status = cmd_cancel_scan (s);
+
+ if (s->line_buffer)
+ free (s->line_buffer);
+ s->line_buffer = NULL;
+ free(s->buf);
+ s->buf = s->end = s->ptr = NULL;
+
+ return status;
+}
+
+static void
+k_copy_image_data(KodakAio_Scanner * s, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+/* copies the read data from s->line_buffer to the position in data pointer to by s->ptr
+uncompressed data is RRRR...GGGG...BBBB per line */
+{
+ SANE_Int bytes_available;
+
+ DBG (min(18,DBG_READ), "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line);
+ *length = 0;
+
+ while ((max_length >= s->params.bytes_per_line) && (s->ptr < s->end)) {
+ SANE_Int bytes_to_copy = s->scan_bytes_per_line - s->bytes_read_in_line;
+ /* First, fill the line buffer for the current line: */
+ bytes_available = (s->end - s->ptr);
+ /* Don't copy more than we have buffer and available */
+ if (bytes_to_copy > bytes_available)
+ bytes_to_copy = bytes_available;
+
+ if (bytes_to_copy > 0) {
+ memcpy (s->line_buffer + s->bytes_read_in_line, s->ptr, bytes_to_copy);
+ s->ptr += bytes_to_copy;
+ s->bytes_read_in_line += bytes_to_copy;
+ }
+
+ /* We have filled as much as possible of the current line
+ * with data from the scanner. If we have a complete line,
+ * copy it over.
+ line points to the current byte in the input s->line_buffer
+ data points to the output buffer*/
+ if ((s->bytes_read_in_line >= s->scan_bytes_per_line) &&
+ (s->params.bytes_per_line <= max_length))
+ {
+ SANE_Int i;
+ SANE_Byte *line = s->line_buffer;
+ *length += s->params.bytes_per_line;
+
+ for (i=0; i< s->params.pixels_per_line; ++i) {
+
+ if (s->val[OPT_MODE].w == MODE_COLOR){
+ /*interlace */
+ *data++ = 255-line[0]; /*red */
+ *data++ = 255-line[s->params.pixels_per_line]; /*green */
+ *data++ = 255-line[2 * s->params.pixels_per_line]; /*blue */
+
+ }
+
+ else { /* grey */
+ /*Average*/
+ *data++ = (255-line[0]
+ +255-line[s->params.pixels_per_line]
+ +255-line[2 * s->params.pixels_per_line])
+ / 3;
+ }
+ line++;
+ }
+/*debug file The same for color or grey because the scan is colour */
+ if (RawScan != NULL) {
+ for (i=0; i< s->scan_bytes_per_line; ++i) fputc(s->line_buffer[i],RawScan);
+ }
+ max_length -= s->params.bytes_per_line;
+ s->bytes_read_in_line -= s->scan_bytes_per_line;
+ }
+ }
+}
+
+static SANE_Status
+k_init_parametersta(KodakAio_Scanner * s)
+{
+ int dpi, optres;
+ /* struct mode_param *mparam; */
+
+ DBG(10, "%s\n", __func__);
+
+ memset(&s->params, 0, sizeof(SANE_Parameters));
+
+ dpi = s->val[OPT_RESOLUTION].w;
+ optres = s->hw->cap->optical_res;
+
+ /* mparam = &mode_params[s->val[OPT_MODE].w];does this get used? */
+
+ if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 ||
+ SANE_UNFIX(s->val[OPT_BR_X].w) == 0)
+ return SANE_STATUS_INVAL;
+
+ /* TODO: Use OPT_RESOLUTION or fixed 600dpi for left/top/width/height? */
+ s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5;
+
+ s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5;
+
+ /* width in pixels */
+ s->width =
+ ((SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5;
+
+ s->height = ((SANE_UNFIX(s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5;
+
+ DBG(20, "%s: s->width = %d, s->height = %d optres units\n",
+ __func__, s->width, s->height);
+
+ s->params.pixels_per_line = s->width * dpi / optres + 0.5;
+
+ /* ADF used without padding? added 30/12/12 */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0 && !s->val[OPT_PADDING].w)
+ s->params.lines = -1;
+ else
+ s->params.lines = s->height * dpi / optres + 0.5;
+
+
+ DBG(20, "%s: resolution = %d, preview = %d\n",
+ __func__, dpi, s->val[OPT_PREVIEW].w);
+
+ DBG(20, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n",
+ __func__, (void *) s, (void *) s->val,
+ SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w),
+ SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w));
+
+ /*
+ * The default color depth is stored in mode_params.depth:
+ */
+ if (mode_params[s->val[OPT_MODE].w].depth == 1)
+ s->params.depth = 1;
+ else {
+ DBG(20, "%s: setting depth = s->val[OPT_BIT_DEPTH].w = %d\n", __func__,s->val[OPT_BIT_DEPTH].w);
+ s->params.depth = s->val[OPT_BIT_DEPTH].w;
+ }
+ s->params.last_frame = SANE_TRUE;
+ s->params.bytes_per_line = 3 * ceil (s->params.depth * s->params.pixels_per_line / 8.0);
+
+/* kodak only scans in color and conversion to grey is done in the driver
+ s->params.format = SANE_FRAME_RGB; */
+ DBG(20, "%s: s->val[OPT_MODE].w = %d (color is %d)\n", __func__,s->val[OPT_MODE].w, MODE_COLOR);
+ if (s->val[OPT_MODE].w == MODE_COLOR) s->params.format = SANE_FRAME_RGB;
+ else s->params.format = SANE_FRAME_GRAY;
+
+ DBG(20, "%s: format=%d, bytes_per_line=%d, lines=%d\n", __func__, s->params.format, s->params.bytes_per_line, s->params.lines);
+ return (s->params.lines >= -1) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+k_start_scan(KodakAio_Scanner * s)
+{
+ SANE_Status status;
+
+ status = cmd_start_scan (s, s->data_len);
+ if (status != SANE_STATUS_GOOD ) {
+ DBG (1, "%s: starting the scan failed (%s)\n", __func__, sane_strstatus(status));
+ }
+ return status;
+}
+
+
+
+static SANE_Status
+k_read(struct KodakAio_Scanner *s)
+{
+ unsigned char rx[8];
+
+/* monitors progress of blocks and calls cmd_read_data to get each block
+you don't know how many blocks there will be in advance because their size may be determined by the scanner*/
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t buf_len = 0;
+
+ /* did we passed everything we read to sane? */
+ if (s->ptr == s->end) {
+
+ if (s->eof)
+ return SANE_STATUS_EOF;
+
+ s->counter++;
+
+ if (s->bytes_unread >= s->block_len)
+ buf_len = s->block_len;
+ else
+ buf_len = s->bytes_unread;
+
+ DBG(min(20,DBG_READ), "%s: block %d, size %lu\n", __func__,
+ s->counter, (unsigned long) buf_len);
+
+ /* receive image data + error code */
+ status = cmd_read_data (s, s->buf, &buf_len);
+ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) { /* was just GOOD 20/2/12 */
+ DBG (1, "%s: Receiving image data failed (%s)\n",
+ __func__, sane_strstatus(status));
+ cmd_cancel_scan(s);
+ return status;
+ }
+
+ DBG(min(14,DBG_READ), "%s: success %lu bytes of block %d, %d remain\n", __func__, (unsigned long) buf_len, s->counter, s->bytes_unread);
+
+ if (s->bytes_unread > 0) {
+ if (s->canceling) {
+ cmd_cancel_scan(s);
+ return SANE_STATUS_CANCELLED;
+ }
+ if (status == SANE_STATUS_EOF) {
+ /* page ended prematurely. */
+ }
+ }
+ else { /* s->bytes_unread <=0 This is the end of a page */
+ s->eof = SANE_TRUE;
+ DBG(min(10,DBG_READ), "%s: set EOF after %d blocks\n=============\n", __func__, s->counter);
+/* look for the terminating ack if required */
+ if (!s->ack) {
+ if (kodakaio_expect_ack(s, rx) == SANE_STATUS_GOOD) {
+ s->ack = SANE_TRUE;
+ }
+ else {
+ DBG(min(1,DBG_READ), "%s: Did not get expected ack at end of page\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ }
+
+ s->end = s->buf + buf_len;
+ s->ptr = s->buf;
+ }
+ else {
+ DBG(min(20,DBG_READ), "%s: data left in buffer\n", __func__);
+ }
+ return status;
+}
+
+/*
+ * SANE API implementation (high-level functions)
+*/
+
+static struct KodakaioCap *
+get_device_from_identification (const char *ident, const char *vid, const char *pid)
+{
+ int n;
+ SANE_Word pidnum, vidnum;
+
+ if(sscanf(vid, "%x", &vidnum) == EOF) {
+ DBG(5, "could not convert hex vid <%s>\n", vid);
+ return NULL;
+ }
+ if(sscanf(pid, "%x", &pidnum) == EOF) {
+ DBG(5, "could not convert hex pid <%s>\n", pid);
+ return NULL;
+ }
+ for (n = 0; n < NELEMS (kodakaio_cap); n++) {
+
+ if (strcmp (kodakaio_cap[n].model, ident)==0) {
+ DBG(20, "matched <%s> & <%s>\n", kodakaio_cap[n].model, ident);
+ return &kodakaio_cap[n];
+ }
+ else
+ if (kodakaio_cap[n].id == pidnum && 0x040A == vidnum) {
+ DBG(20, "matched <%s> & <%s:%s>\n", kodakaio_cap[n].model, vid, pid);
+ return &kodakaio_cap[n];
+ }
+ else {
+ DBG(20, "not found <%s> & <%s>\n", kodakaio_cap[n].model, pid);
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * close_scanner()
+ *
+ * Close the open scanner. Depending on the connection method, a different
+ * close function is called.
+ */
+static void
+close_scanner(KodakAio_Scanner *s)
+{
+ DBG(7, "%s: fd = %d\n", __func__, s->fd);
+
+ if (s->fd == -1)
+ return;
+
+ k_scan_finish(s);
+ if (s->hw->connection == SANE_KODAKAIO_NET) {
+ sanei_kodakaio_net_close(s);
+ sanei_tcp_close(s->fd);
+ } else if (s->hw->connection == SANE_KODAKAIO_USB) {
+ sanei_usb_close(s->fd);
+ }
+
+ s->fd = -1;
+}
+
+static SANE_Bool
+split_scanner_name (const char *name, char * IP, unsigned int *model)
+{
+ const char *device = name;
+ const char *qm;
+ *model = 0;
+ /* cut off leading net: */
+ if (strncmp(device, "net:", 4) == 0)
+ device = &device[4];
+
+ qm = strchr(device, '?');
+ if (qm != NULL) {
+ size_t len = qm-device;
+ strncpy (IP, device, len);
+ IP[len] = '\0';
+ qm++;
+ if (strncmp(qm, "model=", 6) == 0) {
+ qm += 6;
+ if (!sscanf(qm, "0x%x", model))
+ sscanf(qm, "%x", model);
+ }
+ } else {
+ strcpy (IP, device);
+ }
+ return SANE_TRUE;
+}
+
+/*
+ * open_scanner()
+ *
+ * Open the scanner device. Depending on the connection method,
+ * different open functions are called.
+ */
+
+static SANE_Status
+open_scanner(KodakAio_Scanner *s)
+{
+ SANE_Status status = 0;
+
+ DBG(7, "%s: %s\n", __func__, s->hw->sane.name);
+
+ if (s->fd != -1) {
+ DBG(10, "scanner is already open: fd = %d\n", s->fd);
+ return SANE_STATUS_GOOD; /* no need to open the scanner */
+ }
+
+ if (s->hw->connection == SANE_KODAKAIO_NET) {
+ /* device name has the form net:ipaddr?model=... */
+ char IP[1024];
+ unsigned int model = 0;
+ if (!split_scanner_name (s->hw->sane.name, IP, &model))
+ return SANE_STATUS_INVAL;
+ DBG(10, "split_scanner_name OK model=0x%x\n",model);
+/* normal with IP */
+ status = sanei_tcp_open(IP, 9101, &s->fd); /* (host,port,file pointer) */
+
+ if (status != SANE_STATUS_GOOD ) DBG(1, "Is network scanner switched on?\n");
+
+ if (model>0)
+ k_set_device (s, model);
+ if (status == SANE_STATUS_GOOD) {
+ status = sanei_kodakaio_net_open (s);
+ }
+else DBG(1, "status was not good at net open\n");
+
+
+ } else if (s->hw->connection == SANE_KODAKAIO_USB) {
+ DBG(7, "trying to open usb\n");
+ status = sanei_usb_open(s->hw->sane.name, &s->fd);
+ if (s->hw->cap->out_ep>0)
+ sanei_usb_set_endpoint (s->fd,
+ USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, s->hw->cap->out_ep);
+ if (s->hw->cap->in_ep>0)
+ sanei_usb_set_endpoint (s->fd,
+ USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, s->hw->cap->in_ep);
+ }
+
+ if (status == SANE_STATUS_ACCESS_DENIED) {
+ DBG(1, "please check that you have permissions on the device.\n");
+ DBG(1, "if this is a multi-function device with a printer,\n");
+ DBG(1, "disable any conflicting driver (like usblp).\n");
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ DBG(1, "%s open failed: %s\n", s->hw->sane.name,
+ sane_strstatus(status));
+ else
+ DBG(3, "scanner opened\n");
+/* add check here of usb properties? */
+/*sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc );*/
+
+
+ return status;
+}
+
+static SANE_Status
+detect_usb(struct KodakAio_Scanner *s)
+{
+ SANE_Status status;
+ SANE_Word vendor, product;
+ int i, numIds;
+ SANE_Bool is_valid;
+
+ /* if the sanei_usb_get_vendor_product call is not supported,
+ * then we just ignore this and rely on the user to config
+ * the correct device.
+ */
+
+ status = sanei_usb_get_vendor_product(s->fd, &vendor, &product);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "the device cannot be verified - will continue\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* check the vendor ID to see if we are dealing with a kodak device */
+ if (vendor != SANE_KODAKAIO_VENDOR_ID) {
+ /* this is not a supported vendor ID */
+ DBG(1, "not a Kodak Aio device at %s (vendor id=0x%x)\n",
+ s->hw->sane.name, vendor);
+ return SANE_STATUS_INVAL;
+ }
+
+ numIds = kodakaio_getNumberOfUSBProductIds();
+ is_valid = SANE_FALSE;
+ i = 0;
+
+ /* check all known product IDs to verify that we know
+ * about the device */
+ while (i != numIds && !is_valid) {
+ /* if (product == kodakaio_usb_product_ids[i]) */
+ if (product == kodakaio_cap[i].id)
+ is_valid = SANE_TRUE;
+ i++;
+ }
+
+ if (is_valid == SANE_FALSE) {
+ DBG(1, "the device at %s is not a supported (product id=0x%x)\n",
+ s->hw->sane.name, product);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(2, "found valid usb Kodak Aio scanner: 0x%x/0x%x (vendorID/productID)\n",
+ vendor, product);
+ k_set_device(s, product); /* added 21/12/11 to try and get a name for the device */
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a single-linked list of Kodak_Device structs
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ */
+static int num_devices; /* number of scanners attached to backend */
+static Kodak_Device *first_dev; /* first scanner in list */
+static const SANE_Device **devlist = NULL;
+
+static struct KodakAio_Scanner *
+scanner_create(struct Kodak_Device *dev, SANE_Status *status)
+{
+ struct KodakAio_Scanner *s;
+
+ s = malloc(sizeof(struct KodakAio_Scanner));
+ if (s == NULL) {
+ *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ memset(s, 0x00, sizeof(struct KodakAio_Scanner));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ return s;
+}
+
+static struct KodakAio_Scanner *
+device_detect(const char *name, int type, SANE_Status *status)
+{
+ struct KodakAio_Scanner *s;
+ struct Kodak_Device *dev;
+
+ /* try to find the device in our list */
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, name) == 0) {
+ dev->missing = 0;
+ DBG (10, "%s: Device %s already attached!\n", __func__,
+ name);
+ return scanner_create(dev, status);
+ }
+ }
+
+ if (type == SANE_KODAKAIO_NODEV) {
+ *status = SANE_STATUS_INVAL;
+ return NULL;
+ }
+
+ /* alloc and clear our device structure */
+ dev = malloc(sizeof(*dev));
+ if (!dev) {
+ *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+ memset(dev, 0x00, sizeof(struct Kodak_Device));
+
+ s = scanner_create(dev, status);
+ if (s == NULL)
+ return NULL;
+
+ k_dev_init(dev, name, type);
+
+ *status = open_scanner(s);
+ if (*status != SANE_STATUS_GOOD) {
+ free(s);
+ free(dev);
+ return NULL;
+ }
+
+ /* from now on, close_scanner() must be called */
+
+ /* USB requires special care */
+ if (dev->connection == SANE_KODAKAIO_USB) {
+ *status = detect_usb(s);
+ }
+
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ /* set name and model (if not already set) */
+ if (dev->model == NULL)
+ k_set_model(s, "generic", 7);
+
+ dev->name = strdup(name);
+ dev->sane.name = dev->name;
+
+ /* do we need to discover capabilities here? */
+ *status = k_discover_capabilities(s);
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ if (source_list[0] == NULL || dev->cap->dpi_range.min == 0) {
+ DBG(1, "something is wrong in the discovery process, aborting.\n");
+ *status = SANE_STATUS_IO_ERROR;
+ goto close;
+ }
+
+ /* add this scanner to the device list */
+ num_devices++;
+ dev->missing = 0;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ return s;
+
+ close:
+ close_scanner(s);
+ free(dev);
+ free(s);
+ return NULL;
+}
+
+
+
+#if WITH_AVAHI
+/* ProcessAvahiDevice is called to process each discovered device in turn */
+void
+ProcessAvahiDevice(const char *device_id, const char *vid, const char *pid, const char *ip_addr)
+{
+ struct KodakaioCap *cap;
+
+ DBG(min(10,DBG_AUTO),"device_id = <%s> vid:pid = <%s:%s>\n", device_id,vid,pid);
+
+/* check if it is a model likely to be supported: "KODAK ESP" or "KODAK HERO"
+
+ DBG(min(10,DBG_AUTO),"look up model <%s>\n", device_model); */
+ cap = get_device_from_identification("", vid, pid);
+ if (cap == NULL) {
+ return;
+ }
+
+ DBG(min(10,DBG_AUTO), "%s: Found autodiscovered device: %s (type 0x%x)\n", __func__, cap->model, cap->id);
+ attach_one_net (ip_addr, cap->id);
+}
+
+
+static void resolve_callback(
+ AvahiServiceResolver *r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+
+ char *pidkey, *pidvalue;
+ char *vidkey, *vidvalue;
+ size_t valuesize;
+ NOT_USED (flags);
+
+ assert(r);
+
+ /* Called whenever a service has been resolved successfully or timed out */
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ DBG(min(1,DBG_AUTO), "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
+ break;
+
+ case AVAHI_RESOLVER_FOUND: {
+ char a[AVAHI_ADDRESS_STR_MAX];
+
+ avahi_address_snprint(a, sizeof(a), address);
+
+/* Output short for Kodak ESP */
+ DBG(min(10,DBG_AUTO), "%s:%u %s ", a,port,host_name);
+ avahi_string_list_get_pair(avahi_string_list_find(txt, "vid"),
+ &vidkey, &vidvalue, &valuesize);
+ DBG(min(10,DBG_AUTO), "%s=%s ", vidkey, vidvalue);
+ avahi_string_list_get_pair(avahi_string_list_find(txt, "pid"),
+ &pidkey, &pidvalue, &valuesize);
+ DBG(min(10,DBG_AUTO), "%s=%s\n", pidkey, pidvalue);
+
+ ProcessAvahiDevice(name, vidvalue, pidvalue, a);
+ avahi_free(vidkey); avahi_free(vidvalue);
+ avahi_free(pidkey); avahi_free(pidvalue);
+ }
+ }
+
+ avahi_service_resolver_free(r);
+}
+
+static void browse_callback(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiClient *c = userdata;
+ assert(b);
+
+ /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
+ switch (event) {
+ case AVAHI_BROWSER_FAILURE:
+
+ DBG(min(1,DBG_AUTO), "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
+ avahi_simple_poll_quit(simple_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ DBG(min(5,DBG_AUTO), "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+
+ /* We ignore the returned resolver object. In the callback
+ function we free it. If the server is terminated before
+ the callback function is called the server will free
+ the resolver for us. */
+
+ if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c)))
+ DBG(min(1,DBG_AUTO), "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
+
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ DBG(min(1,DBG_AUTO), "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ DBG(min(5,DBG_AUTO), "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+ assert(c);
+
+ /* Called whenever the client or server state changes */
+
+ if (state == AVAHI_CLIENT_FAILURE) {
+ DBG(min(1,DBG_AUTO), "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c)));
+ avahi_simple_poll_quit(simple_poll);
+ }
+}
+
+
+static int
+kodak_network_discovery(const char*host)
+/* If host = NULL do autodiscovery. If host != NULL try to verify the model
+First version only does autodiscovery */
+{
+ AvahiClient *client = NULL;
+ AvahiServiceBrowser *sb = NULL;
+ int error;
+ int i, ret = 1;
+ NOT_USED(host);
+
+ DBG(2, "%s: called\n", __func__);
+
+ /* Allocate main loop object */
+ if (!(simple_poll = avahi_simple_poll_new())) {
+ DBG(min(1,DBG_AUTO), "Failed to create simple poll object.\n");
+ goto fail;
+ }
+
+ /* Allocate a new client */
+ client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error);
+
+ /* Check wether creating the client object succeeded */
+ if (!client) {
+ DBG(min(1,DBG_AUTO), "Failed to create client: %s\n", avahi_strerror(error));
+ goto fail;
+ }
+
+ /* Create the service browser */
+ if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_scanner._tcp", NULL, 0, browse_callback, client))) {
+ DBG(min(1,DBG_AUTO), "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
+ goto fail;
+ }
+
+ /* Run the main loop */
+ for(i=1;i<K_SNMP_Timeout/POLL_ITN_MS;++i) {
+ avahi_simple_poll_iterate(simple_poll,POLL_ITN_MS);
+ }
+ ret = 0;
+
+fail:
+
+
+ /* Cleanup things */
+ DBG(min(10,DBG_AUTO), "Cleaning up avahi.\n");
+ if (sb)
+ avahi_service_browser_free(sb);
+
+ if (client)
+ avahi_client_free(client);
+
+ if (simple_poll)
+ avahi_simple_poll_free(simple_poll);
+
+ return ret;
+}
+
+#endif
+
+
+static SANE_Status
+attach(const char *name, int type)
+{
+ SANE_Status status;
+ KodakAio_Scanner *s;
+
+ DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type);
+
+ s = device_detect(name, type, &status);
+ if(s == NULL)
+ return status;
+
+ close_scanner(s);
+ free(s);
+ return status;
+}
+
+SANE_Status
+attach_one_usb(const char *dev)
+{
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ return attach(dev, SANE_KODAKAIO_USB);
+}
+
+static SANE_Status
+attach_one_net(const char *dev, unsigned int model)
+{
+ char name[1024];
+
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ if (model > 0) {
+ snprintf(name, 1024, "net:%s?model=0x%x", dev, model);
+ } else {
+ snprintf(name, 1024, "net:%s", dev);
+ }
+
+ return attach(name, SANE_KODAKAIO_NET);
+}
+
+static SANE_Status
+attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
+{
+ int vendor, product, timeout;
+
+ int len = strlen(line);
+
+ DBG(7, "%s: len = %d, line = %s\n", __func__, len, line);
+
+ if (sscanf(line, "usb %i %i", &vendor, &product) == 2) {
+ /* add the vendor and product IDs to the list of
+ * known devices before we call the attach function */
+
+ int numIds = kodakaio_getNumberOfUSBProductIds();
+
+ if (vendor != SANE_KODAKAIO_VENDOR_ID) {
+ DBG(7, "Wrong vendor: numIds = %d, vendor = %d\n", numIds, vendor);
+ return SANE_STATUS_INVAL; /* this is not a Kodak device */
+ }
+ /* kodakaio_usb_product_ids[numIds - 1] = product; */
+ kodakaio_cap[numIds - 1].id = product;
+
+ sanei_usb_attach_matching_devices(line, attach_one_usb);
+
+ } else if (strncmp(line, "usb", 3) == 0 && len == 3) {
+ int i, numIds;
+ /* auto detect ? */
+ numIds = kodakaio_getNumberOfUSBProductIds();
+
+ for (i = 0; i < numIds; i++) {
+/* sanei_usb_find_devices(SANE_KODAKAIO_VENDOR_ID,
+ kodakaio_usb_product_ids[i], attach_one_usb); */
+ sanei_usb_find_devices(SANE_KODAKAIO_VENDOR_ID,
+ kodakaio_cap[i].id, attach_one_usb);
+ }
+
+ } else if (strncmp(line, "net", 3) == 0) {
+
+ /* remove the "net" sub string */
+ const char *name = sanei_config_skip_whitespace(line + 3);
+ char IP[1024];
+ unsigned int model = 0;
+
+ if (strncmp(name, "autodiscovery", 13) == 0) {
+
+#if WITH_AVAHI
+ DBG (30, "%s: Initiating network autodiscovery via avahi\n", __func__);
+ kodak_network_discovery(NULL);
+#else
+ DBG (20, "%s: Network autodiscovery not done because not configured with avahi.\n", __func__);
+#endif
+
+ } else if (sscanf(name, "%s %x", IP, &model) == 2) {
+ DBG(30, "%s: Using network device on IP %s, forcing model 0x%x\n", __func__, IP, model);
+ attach_one_net(IP, model);
+ } else {
+ DBG(1, "%s: net entry %s may be a host name?\n", __func__, name);
+ attach_one_net(name, 0);
+ }
+
+ } else if (sscanf(line, "snmp-timeout %i\n", &timeout)) {
+ /* Timeout for auto network discovery */
+ DBG(50, "%s: network auto-discovery timeout set to %d\n", __func__, timeout);
+ K_SNMP_Timeout = timeout;
+
+ } else if (sscanf(line, "scan-data-timeout %i\n", &timeout)) {
+ /* Timeout for scan data requests */
+ DBG(50, "%s: Scan data timeout set to %d\n", __func__, timeout);
+ K_Scan_Data_Timeout = timeout;
+
+ } else if (sscanf(line, "request-timeout %i\n", &timeout)) {
+ /* Timeout for all other read requests */
+ DBG(50, "%s: Request timeout set to %d\n", __func__, timeout);
+ K_Request_Timeout = timeout;
+
+ } else {
+ /* TODO: Warning about unparsable line! */
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+free_devices(void)
+{
+ Kodak_Device *dev, *next;
+
+ DBG(5, "%s\n", __func__);
+
+ for (dev = first_dev; dev; dev = next) {
+ next = dev->next;
+ free(dev->name);
+ free(dev->model);
+ free(dev);
+ }
+
+ if (devlist)
+ free(devlist);
+ devlist = NULL;
+ first_dev = NULL;
+}
+
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ DBG_INIT();
+ DBG(1, "========================================== \n");
+ DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__);
+
+ DBG(1, "kodakaio backend, version %i.%i.%i\n",
+ KODAKAIO_VERSION, KODAKAIO_REVISION, KODAKAIO_BUILD);
+ DBG(2, "%s: called\n", __func__);
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR,
+ KODAKAIO_BUILD);
+ sanei_usb_init();
+
+#if WITH_AVAHI
+ DBG(min(3,DBG_AUTO), "avahi detected\n");
+#else
+ DBG(min(3,DBG_AUTO), "avahi not detected\n");
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+/* Clean up the list of attached scanners. */
+void
+sane_exit(void)
+{
+ DBG(5, "%s\n", __func__);
+ free_devices();
+}
+
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Kodak_Device *dev, *s, *prev=0;
+ int i;
+
+ DBG(2, "%s: called\n", __func__);
+
+ sanei_usb_init();
+
+ /* mark all existing scanners as missing, attach_one will remove mark */
+ for (s = first_dev; s; s = s->next) {
+ s->missing = 1;
+ }
+
+ /* Read the config, mark each device as found, possibly add new devs */
+ sanei_configure_attach(KODAKAIO_CONFIG_FILE, NULL,
+ attach_one_config);
+
+ /*delete missing scanners from list*/
+ for (s = first_dev; s;) {
+ if (s->missing) {
+ DBG (5, "%s: missing scanner %s\n", __func__, s->name);
+
+ /*splice s out of list by changing pointer in prev to next*/
+ if (prev) {
+ prev->next = s->next;
+ free (s);
+ s = prev->next;
+ num_devices--;
+ } else {
+ /*remove s from head of list */
+ first_dev = s->next;
+ free(s);
+ s = first_dev;
+ prev=NULL;
+ num_devices--;
+ }
+ } else {
+ prev = s;
+ s = prev->next;
+ }
+ }
+
+ DBG (15, "%s: found %d scanner(s)\n", __func__, num_devices);
+ for (s = first_dev; s; s=s->next) {
+ DBG (15, "%s: found scanner %s\n", __func__, s->name);
+ }
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
+ if (!devlist) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(5, "%s - results:\n", __func__);
+
+ for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) {
+ DBG(5, " %d (%d): %s\n", i, dev->connection, dev->model);
+ devlist[i] = &dev->sane;
+ }
+
+ devlist[i] = NULL;
+
+ if(device_list){
+ *device_list = devlist;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options(KodakAio_Scanner *s)
+{
+ int i;
+ SANE_Word *res_list;
+ DBG(5, "%s: called\n", __func__);
+
+ for (i = 0; i < NUM_OPTIONS; i++) {
+ s->opt[i].size = sizeof(SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].name = SANE_NAME_STANDARD;
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_STANDARD;
+ s->opt[OPT_MODE_GROUP].desc = SANE_DESC_STANDARD;
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size(mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 0; /* Binary */
+ DBG(20, "%s: mode_list has first entry %s\n", __func__, mode_list[0]);
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->cap->depth_list;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1]; /* the first "real" element is the default */
+
+ DBG(20, "%s: depth list has depth_list[0] = %d entries\n", __func__, s->hw->cap->depth_list[0]);
+ if (s->hw->cap->depth_list[0] == 1) { /* only one element in the list -> hide the option */
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ DBG(20, "%s: Only one depth in list so inactive option\n", __func__);
+ }
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ res_list = malloc((s->hw->cap->res_list_size + 1) * sizeof(SANE_Word));
+ if (res_list == NULL) {
+ return SANE_STATUS_NO_MEM;
+ }
+ *(res_list) = s->hw->cap->res_list_size;
+ memcpy(&(res_list[1]), s->hw->cap->res_list, s->hw->cap->res_list_size * sizeof(SANE_Word));
+ s->opt[OPT_RESOLUTION].constraint.word_list = res_list;
+ s->val[OPT_RESOLUTION].w = s->hw->cap->dpi_range.min;
+
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ for(i=0;source_list[i]!=NULL;++i)
+ DBG(18, "source_list: %s\n",source_list[i]);
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size(source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */
+
+ if ((!s->hw->cap->ADF)) {
+ DBG(9, "device with no adf detected source option inactive\n");
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ }
+
+/* Are there any ESP scanners that are duplex? */
+ s->opt[OPT_ADF_MODE].name = "adf-mode";
+ s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode");
+ s->opt[OPT_ADF_MODE].desc =
+ SANE_I18N("Selects the ADF mode (simplex/duplex)");
+ s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list);
+ s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
+ s->val[OPT_ADF_MODE].w = 0; /* simplex */
+ if ((!s->hw->cap->ADF) || (!s->hw->cap->adf_duplex))
+ s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
+
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ /* padding short pages in adf */
+ s->opt[OPT_PADDING].name = "adf-padding";
+ s->opt[OPT_PADDING].title = "pad short adf pages";
+ s->opt[OPT_PADDING].desc = "Selects whether to make short pages up to full length";
+ s->opt[OPT_PADDING].type = SANE_TYPE_BOOL;
+ s->val[OPT_PADDING].w = SANE_FALSE;
+ if ((!s->hw->cap->ADF) || (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) != 0))
+ {
+ DBG(9, "adf not source so padding option off and inactive\n");
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *handle)
+{
+ SANE_Status status;
+ KodakAio_Scanner *s = NULL;
+
+ int l = strlen(name);
+
+ DBG(2, "%s: name = %s\n", __func__, name);
+
+ /* probe if empty device name provided */
+ if (l == 0) {
+
+ status = sane_get_devices(NULL,0);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ if (first_dev == NULL) {
+ DBG(1, "no device detected\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ s = device_detect(first_dev->sane.name, first_dev->connection,
+ &status);
+ if (s == NULL) {
+ DBG(1, "cannot open a perfectly valid device (%s),"
+ " please report to the authors\n", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ } else {
+
+ if (strncmp(name, "net:", 4) == 0) {
+ s = device_detect(name, SANE_KODAKAIO_NET, &status);
+ if (s == NULL)
+ return status;
+ } else if (strncmp(name, "libusb:", 7) == 0) {
+ s = device_detect(name, SANE_KODAKAIO_USB, &status);
+ if (s == NULL)
+ return status;
+ } else {
+
+ /* as a last resort, check for a match
+ * in the device list. This should handle platforms without libusb.
+ */
+ if (first_dev == NULL) {
+ status = sane_get_devices(NULL,0);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ }
+
+ s = device_detect(name, SANE_KODAKAIO_NODEV, &status);
+ if (s == NULL) {
+ DBG(1, "invalid device name: %s\n", name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ }
+
+
+ /* s is always valid here */
+
+ DBG(10, "handle obtained\n");
+ status = k_discover_capabilities(s); /* added 27/12/11 to fix source list problem
+maybe we should only be rebuilding the source list here? */
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ init_options(s);
+
+ *handle = (SANE_Handle) s;
+
+ status = open_scanner(s);
+ if (status != SANE_STATUS_GOOD) {
+ free(s);
+ return status;
+ }
+
+ return status;
+}
+
+void
+sane_close(SANE_Handle handle)
+{
+ KodakAio_Scanner *s;
+
+ /*
+ * XXX Test if there is still data pending from
+ * the scanner. If so, then do a cancel
+ */
+
+ s = (KodakAio_Scanner *) handle;
+ DBG(2, "%s: called\n", __func__);
+
+ if (s->fd != -1)
+ close_scanner(s);
+ if(RawScan != NULL)
+ fclose(RawScan);
+ RawScan = NULL;
+ free(s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+/* this may be a sane call, but it happens way too often to have DBG level 2 */
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+
+ DBG(20, "%s: called for option %d\n", __func__, option);
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return NULL;
+
+ return s->opt + option;
+}
+
+static const SANE_String_Const *
+search_string_list(const SANE_String_Const *list, SANE_String value)
+{
+ while (*list != NULL && strcmp(value, *list) != 0)
+ list++;
+
+ return ((*list == NULL) ? NULL : list);
+}
+
+/*
+ Activate, deactivate an option. Subroutines so we can add
+ debugging info if we want. The change flag is set to TRUE
+ if we changed an option. If we did not change an option,
+ then the value of the changed flag is not modified.
+*/
+
+static void
+activateOption(KodakAio_Scanner *s, SANE_Int option, SANE_Bool *change)
+{
+ if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
+ s->opt[option].cap &= ~SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+deactivateOption(KodakAio_Scanner *s, SANE_Int option, SANE_Bool *change)
+{
+ if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
+ s->opt[option].cap |= SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static SANE_Status
+getvalue(SANE_Handle handle, SANE_Int option, void *value)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ DBG(17, "%s: option = %d\n", __func__, option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ case OPT_BIT_DEPTH:
+/* case OPT_BRIGHTNESS: */
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *((SANE_Word *) value) = sval->w;
+ break;
+
+ case OPT_MODE:
+ case OPT_SOURCE:
+ case OPT_ADF_MODE:
+ strcpy((char *) value, sopt->constraint.string_list[sval->w]);
+ break;
+ case OPT_PADDING:
+ *((SANE_Bool *) value) = sval->w;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ * Handles setting the source (flatbed, or auto document feeder (ADF)).
+ *
+ */
+
+static void
+change_source(KodakAio_Scanner *s, SANE_Int optindex, char *value)
+{
+ int force_max = SANE_FALSE;
+ SANE_Bool dummy;
+
+ DBG(5, "%s: optindex = %d, source = '%s'\n", __func__, optindex,
+ value);
+
+ if (s->val[OPT_SOURCE].w == optindex)
+ return;
+
+ s->val[OPT_SOURCE].w = optindex;
+
+ if (s->val[OPT_TL_X].w == s->hw->x_range->min
+ && s->val[OPT_TL_Y].w == s->hw->y_range->min
+ && s->val[OPT_BR_X].w == s->hw->x_range->max
+ && s->val[OPT_BR_Y].w == s->hw->y_range->max) {
+ force_max = SANE_TRUE;
+ }
+
+ if (strcmp(ADF_STR, value) == 0) {
+ s->hw->x_range = &s->hw->cap->adf_x_range;
+ s->hw->y_range = &s->hw->cap->adf_y_range;
+ if (s->hw->cap->adf_duplex) {
+ activateOption(s, OPT_ADF_MODE, &dummy);
+ } else {
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ s->val[OPT_ADF_MODE].w = 0;
+ }
+ activateOption(s, OPT_PADDING, &dummy);
+
+ DBG(5, "adf activated flag = %d\n",s->hw->cap->adf_duplex);
+
+ } else {
+ /* ADF not active */
+ s->hw->x_range = &s->hw->cap->fbf_x_range;
+ s->hw->y_range = &s->hw->cap->fbf_y_range;
+
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ deactivateOption(s, OPT_PADDING, &dummy);
+ }
+
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+
+ if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
+ s->val[OPT_TL_X].w = s->hw->x_range->min;
+
+ if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
+ s->val[OPT_TL_Y].w = s->hw->y_range->min;
+
+ if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+}
+
+static SANE_Status
+setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ SANE_Status status;
+ const SANE_String_Const *optval = NULL;
+ int optindex = 0;
+ SANE_Bool reload = SANE_FALSE;
+
+ DBG(17, "%s: option = %d, value = %p, as word: %d\n", __func__, option, value, *(SANE_Word *) value);
+
+ status = sanei_constrain_value(sopt, value, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (info && value && (*info & SANE_INFO_INEXACT)
+ && sopt->type == SANE_TYPE_INT)
+ DBG(17, "%s: constrained val = %d\n", __func__,
+ *(SANE_Word *) value);
+
+ if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) {
+ optval = search_string_list(sopt->constraint.string_list,
+ (char *) value);
+ if (optval == NULL)
+ return SANE_STATUS_INVAL;
+ optindex = optval - sopt->constraint.string_list;
+ }
+
+ switch (option) {
+
+ case OPT_MODE:
+ {
+ sval->w = optindex;
+ /* if binary, then disable the bit depth selection */
+ if (optindex == 0) {
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ } else {
+ if (s->hw->cap->depth_list[0] == 1)
+ s->opt[OPT_BIT_DEPTH].cap |=
+ SANE_CAP_INACTIVE;
+ else {
+ s->opt[OPT_BIT_DEPTH].cap &=
+ ~SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w =
+ mode_params[optindex].depth;
+ }
+ }
+ reload = SANE_TRUE;
+ break;
+ }
+
+ case OPT_BIT_DEPTH:
+ sval->w = *((SANE_Word *) value);
+ mode_params[s->val[OPT_MODE].w].depth = sval->w;
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_RESOLUTION:
+ sval->w = *((SANE_Word *) value);
+ DBG(17, "setting resolution to %d\n", sval->w);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ sval->w = *((SANE_Word *) value);
+ if (SANE_UNFIX(sval->w) == 0) {
+ DBG(17, "invalid br-x or br-y\n");
+ return SANE_STATUS_INVAL;
+ }
+ /* passthru */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ sval->w = *((SANE_Word *) value);
+ DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w));
+ if (NULL != info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_SOURCE:
+ change_source(s, optindex, (char *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_ADF_MODE:
+ sval->w = optindex; /* Simple lists */
+ break;
+
+ case OPT_PADDING:
+ sval->w = *((SANE_Word *) value);
+ break;
+
+/* case OPT_BRIGHTNESS: */
+ case OPT_PREVIEW: /* needed? */
+ sval->w = *((SANE_Word *) value);
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ if (reload && info != NULL)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ DBG(17, "%s: end\n", __func__);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *value, SANE_Int *info)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ DBG(2, "%s: action = %x, option = %d %s\n", __func__, action, option, s->opt[option].name);
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ {
+ DBG(1, "%s: option num = %d (%s) out of range\n", __func__, option, s->opt[option].name);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (info != NULL)
+ *info = 0;
+
+ switch (action) {
+ case SANE_ACTION_GET_VALUE:
+ return getvalue(handle, option, value);
+
+ case SANE_ACTION_SET_VALUE:
+ return setvalue(handle, option, value, info);
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+
+ DBG(2, "%s: called\n", __func__);
+
+ if (params == NULL)
+ DBG(1, "%s: params is NULL\n", __func__);
+
+ /*
+ * If sane_start was already called, then just retrieve the parameters
+ * from the scanner data structure
+ */
+
+ if (!s->eof && s->ptr != NULL) {
+ DBG(5, "scan in progress, returning saved params structure\n");
+ } else {
+ /* otherwise initialize the params structure and gather the data */
+ k_init_parametersta(s);
+ }
+
+ if (params != NULL)
+ *params = s->params;
+
+ print_params(s->params,20);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function is part of the SANE API and gets called from the front end to
+ * start the scan process.
+ */
+
+SANE_Status
+sane_start(SANE_Handle handle)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ SANE_Status status;
+
+ DBG(2, "%s: called\n", __func__);
+ if(! s->scanning) {
+ /* calc scanning parameters */
+ status = k_init_parametersta(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* set scanning parameters; also query the current image
+ * parameters from the sanner and save
+ * them to s->params
+Only set scanning params the first time, or after a cancel
+try change 22/2/12 take lock scanner out of k_set_scanning_parameters */
+ status = k_lock_scanner(s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "could not lock scanner\n");
+ return status;
+ }
+
+ }
+
+ status = k_set_scanning_parameters(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ print_params(s->params, 5);
+ /* if we scan from ADF, check if it is loaded */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) {
+ status = k_check_adf(s);
+ if (status != SANE_STATUS_GOOD) {
+/* returning SANE_STATUS_NO_DOCS seems not to cause simple-scan to end the adf scan, so we cancel */
+ status = SANE_STATUS_CANCELLED;
+ DBG(10, "%s: returning %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+ }
+
+ /* prepare buffer here so that a memory allocation failure
+ * will leave the scanner in a sane state.
+ */
+ s->buf = realloc(s->buf, s->block_len);
+ if (s->buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ s->eof = SANE_FALSE; /* page not finished */
+ s->ack = SANE_FALSE; /* page from scanner not finished */
+ s->ptr = s->end = s->buf;
+ s->canceling = SANE_FALSE;
+
+ if (strlen(RawScanPath) > 0 && s->params.lines > 0)
+ RawScan = fopen(RawScanPath, "wb");/* open the debug file if it has a name */
+ if(RawScan) fprintf(RawScan, "P5\n%d %d\n%d\n",s->scan_bytes_per_line, s->params.lines, 255);
+
+ /* start scanning */
+ DBG(2, "%s: scanning...\n", __func__);
+ status = k_start_scan(s);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: start failed: %s\n", __func__,
+ sane_strstatus(status));
+
+ return status;
+ }
+
+ return status;
+}
+
+/* this moves data from our buffers to SANE */
+
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length,
+ SANE_Int *length)
+{
+ SANE_Status status;
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+
+ if (s->buf == NULL || s->canceling)
+ return SANE_STATUS_CANCELLED;
+
+ *length = 0;
+ DBG(18, "sane-read, bytes unread %d\n",s->bytes_unread);
+
+ status = k_read(s);
+
+ if (status == SANE_STATUS_CANCELLED) {
+ k_scan_finish(s);
+ return status;
+ }
+
+ k_copy_image_data(s, data, max_length, length);
+
+ DBG(18, "%d lines read, status: %s\n",
+ *length / s->params.bytes_per_line, sane_strstatus(status));
+
+ /* continue reading if appropriate */
+ if (status == SANE_STATUS_GOOD)
+ return status;
+
+/* not sure if we want to finish (unlock scanner) here or in sane_close */
+ /* k_scan_finish(s); */
+ return status;
+}
+
+/*
+ * void sane_cancel(SANE_Handle handle)
+ *
+ * Set the cancel flag to true. The next time the backend requests data
+ * from the scanner the CAN message will be sent.
+ */
+
+void
+sane_cancel(SANE_Handle handle)
+{
+ KodakAio_Scanner *s = (KodakAio_Scanner *) handle;
+ DBG(2, "%s: called\n", __func__);
+
+/* used to set cancelling flag to tell sane_read to cancel
+changed 20/2/12
+ s->canceling = SANE_TRUE;
+*/
+ cmd_cancel_scan(s);
+}
+
+/*
+ * SANE_Status sane_set_io_mode()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_set_io_mode(SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * SANE_Status sane_get_select_fd()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_get_select_fd(SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ *fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/kodakaio.conf.in b/backend/kodakaio.conf.in
new file mode 100644
index 0000000..2ad3ed4
--- /dev/null
+++ b/backend/kodakaio.conf.in
@@ -0,0 +1,80 @@
+### kodakaio.conf
+###
+### here are some examples for how to configure the kodakaio backend
+
+### Timeout settings: SNMP autodetection, Scan data read requests and other
+### read requests. All values are given in milliseconds,
+### i.e. 1000 is 1 second.
+
+# snmp-timeout controls auto-detection timeout in ms (1500=1.5s).
+snmp-timeout 2000
+
+# scan-data-timeout controls the timeout for scan data
+# (scans may take several seconds to initialize, so we need to wait longer)
+scan-data-timeout 10000
+
+# request-timeout controls all other data requests
+request-timeout 5000
+
+
+### Network: Format is "net IP_ADDRESS [USB_ID]" or "net autodiscovery"
+### if USB_ID is left out, SNMP is used to detect the device type
+### Currently autodiscovery seems to not work
+### So always use "net IP_ADDRESS [USB_ID]" as shown below
+### You can find the printer's IP address on its control panel
+### There is a list of USB_IDs at the end of this file
+
+net autodiscovery
+
+### The following is a kodak HERO 9.1 with explicit IP-Address
+#net 10.0.0.5 0x4067
+
+# kodak ESP5250 is usb 0x040a 0x4041
+#net 192.168.1.2 0x4041
+# kodak HERO 9.1 is usb 0x040a 0x4067
+#net 192.168.1.17 0x4067
+
+### USB: format is "usb" for automatic (libusb) discovery, based on USB IDs,
+### or "usb <vendor ID> <device ID> to force the use of a particular
+### device (the backend has some additional checks and will not use
+### non-kodak devices, though)
+
+usb
+
+### For libusb support for unknown scanners use the following command
+### usb <product ID> <device ID>
+### e.g.:
+
+# kodak ESP5250 is usb 0x040a 0x4041
+#usb 0x040a 0x4041
+# kodak HERO 9.1 is usb 0x040a 0x4067
+#usb 0x040a 0x4067
+
+### List of USB device IDs
+# 0x4059, /* kodak ESP 2150 */
+# 0x4066, /* kodak ESP 2170 */
+# 0x4043, /* kodak ESP 3200 */
+# 0x4031, /* kodak ESP 3300 */
+# 0x4053, /* kodak ESP 4100 */
+# 0x4028, /* kodak ESP 5000 */
+# 0x4025, /* kodak ESP 5100 */
+# 0x4041, /* kodak ESP 5200 */
+# 0x4026, /* kodak ESP 5300 */
+# 0x4027, /* kodak ESP 5500 */
+# 0x4054, /* kodak ESP 6100 */
+# 0x4056, /* kodak ESP 7200 */
+# 0x4065, /* kodak ESP 9200 */
+# 0x4032, /* kodak ESP 5 */
+# 0x403E, /* kodak ESP 7 */
+# 0x403F, /* kodak ESP 9 */
+# 0x4057, /* kodak ESP C110 */
+# 0x4058, /* kodak ESP C115 */
+# 0x405D, /* kodak ESP C310 */
+# 0x405E, /* kodak ESP C315 */
+# 0x4060, /* ADVENT WiFi AIO AW10 */
+# 0x406D, /* kodak Hero 3.1 */
+# 0x4064, /* kodak Hero 5.1 */
+# 0x4062, /* kodak Office Hero 6.1 */
+# 0x4063, /* kodak Hero 7.1 */
+# 0x4067, /* kodak Hero 9.1 */
+
diff --git a/backend/kodakaio.h b/backend/kodakaio.h
new file mode 100644
index 0000000..f2265f5
--- /dev/null
+++ b/backend/kodakaio.h
@@ -0,0 +1,198 @@
+/*
+ * kodakaio.c - SANE library for Kodak ESP Aio scanners.
+ *
+ * Copyright (C) 2011-2013 Paul Newall
+ *
+ * Based on the Magicolor sane backend:
+ * Based on the epson2 sane backend:
+ * Based on Kazuhiro Sasayama previous
+ * work on epson.[ch] file from the SANE package.
+ * Please see those files for additional copyrights.
+ * Author: Paul Newall
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+
+ 29/12/12 added KodakAio_Scanner.ack
+ 2/1/13 added KodakAio_Scanner.background[]
+ */
+
+#ifndef kodakaio_h
+#define kodakaio_h
+
+#undef BACKEND_NAME
+#define BACKEND_NAME kodakaio
+#define DEBUG_NOT_STATIC
+
+#include <sys/ioctl.h>
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+
+#ifdef __GNUC__
+#define __func__ __FUNCTION__
+#else
+#define __func__ "(undef)"
+#endif
+
+/* Silence the compiler for unused arguments */
+#define NOT_USED(x) ( (void)(x) )
+
+#define KODAKAIO_CONFIG_FILE "kodakaio.conf"
+
+#define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */
+#define DEVICE_NAME_LEN (16) /* length of device name in extended status */
+
+#define CAP_DEFAULT 0
+
+/* Structure holding the device capabilities */
+struct KodakaioCap
+{
+ SANE_Word id; /* USB pid */
+ const char *cmds; /* may be used for different command sets in future */
+ const char *model;
+ SANE_Int out_ep, in_ep; /* USB bulk out/in endpoints */
+
+ SANE_Int optical_res; /* optical resolution */
+ SANE_Range dpi_range; /* max/min resolutions */
+
+ SANE_Int *res_list; /* list of resolutions */
+ SANE_Int res_list_size; /* number of entries in this list */
+
+ SANE_Int maxDepth; /* max. color depth */
+ SANE_Word *depth_list; /* list of color depths */
+
+ /* SANE_Range brightness; brightness range */
+
+ SANE_Range fbf_x_range; /* flattbed x range */
+ SANE_Range fbf_y_range; /* flattbed y range */
+
+ SANE_Bool ADF; /* ADF is installed */
+ SANE_Bool adf_duplex; /* does the ADF handle duplex scanning */
+ SANE_Range adf_x_range; /* autom. document feeder x range */
+ SANE_Range adf_y_range; /* autom. document feeder y range */
+};
+
+/*
+Options:OPT_BRIGHTNESS, used to be after BIT_DEPTH
+*/
+enum {
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_BIT_DEPTH,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_SOURCE,
+ OPT_ADF_MODE,
+ OPT_PADDING, /* Selects padding of adf pages to the specified length */
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ NUM_OPTIONS
+};
+
+typedef enum
+{ /* hardware connection to the scanner */
+ SANE_KODAKAIO_NODEV, /* default, no HW specified yet */
+ SANE_KODAKAIO_USB, /* USB interface */
+ SANE_KODAKAIO_NET /* network interface */
+} Kodakaio_Connection_Type;
+
+
+/* Structure holding the hardware description */
+
+struct Kodak_Device
+{
+ struct Kodak_Device *next;
+ int missing;
+
+ char *name;
+ char *model;
+
+ SANE_Device sane;
+
+ SANE_Range *x_range; /* x range w/out extension */
+ SANE_Range *y_range; /* y range w/out extension */
+
+ Kodakaio_Connection_Type connection;
+
+ struct KodakaioCap *cap;
+};
+
+typedef struct Kodak_Device Kodak_Device;
+
+/* Structure holding an instance of a scanner (i.e. scanner has been opened) */
+struct KodakAio_Scanner
+{
+ struct KodakAio_Scanner *next;
+ struct Kodak_Device *hw;
+
+ int fd;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+
+ SANE_Bool ack; /* scanner has finished a page (happens early with adf and padding) */
+ SANE_Bool eof; /* backend has finished a page (after padding with adf) */
+ SANE_Byte *buf, *end, *ptr;
+ SANE_Bool canceling;
+ SANE_Bool scanning; /* scan in progress */
+ SANE_Bool adf_loaded; /* paper in adf */
+ SANE_Int background[3]; /* stores background RGB components for padding */
+
+ SANE_Int left, top; /* in optres units? */
+ SANE_Int width, height; /* in optres units? */
+
+ /* image block data */
+ SANE_Int data_len;
+ SANE_Int block_len;
+ SANE_Int last_len; /* to be phased out */
+ SANE_Int blocks; /* to be phased out */
+ SANE_Int counter;
+ SANE_Int bytes_unread; /* to track when to stop */
+
+ /* Used to store how many bytes of the current pixel line we have already
+ * read in previous read attempts. Since each line will be padded
+ * to multiples of 512 bytes, this is needed to know which bytes
+ * to ignore. NOT NEEDED FOR KODAKAIO */
+ SANE_Int bytes_read_in_line;
+ SANE_Byte *line_buffer;
+ /* How many bytes are scanned per line */
+ SANE_Int scan_bytes_per_line;
+};
+
+typedef struct KodakAio_Scanner KodakAio_Scanner;
+
+struct mode_param
+{
+ int flags;
+ int colors;
+ int depth;
+};
+
+enum {
+ MODE_GRAY, MODE_COLOR
+};
+
+#endif
diff --git a/backend/kvs1025.c b/backend/kvs1025.c
new file mode 100644
index 0000000..c0e1fa3
--- /dev/null
+++ b/backend/kvs1025.c
@@ -0,0 +1,456 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+ Copyright (C) 2010-2011, m. allan noah
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#define DEBUG_NOT_STATIC
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "kvs1025.h"
+#include "kvs1025_low.h"
+
+#include "../include/sane/sanei_debug.h"
+
+/* SANE backend operations, see Sane standard 1.04 documents (sane_dev.pdf)
+ for details */
+
+/* Init the KV-S1025 SANE backend. This function must be called before any other
+ SANE function can be called. */
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ DBG (DBG_error,
+ "This is panasonic KV-S1020C / KV-S1025C version %d.%d build %d\n",
+ V_MAJOR, V_MINOR, V_BUILD);
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, V_BUILD);
+ }
+
+ /* Initialize USB */
+ sanei_usb_init ();
+
+ status = kv_enum_devices ();
+ if (status)
+ return status;
+
+ DBG (DBG_proc, "sane_init: leave\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Terminate the KV-S1025 SANE backend */
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ kv_exit ();
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
+
+/* Get device list */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+ kv_get_devices_list (device_list);
+ DBG (DBG_proc, "sane_get_devices: leave\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Open device, return the device handle */
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ return kv_open_by_name (devicename, handle);
+}
+
+/* Close device */
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (DBG_proc, "sane_close: enter\n");
+ kv_close ((PKV_DEV) handle);
+ DBG (DBG_proc, "sane_close: leave\n");
+}
+
+/* Get option descriptor */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ return kv_get_option_descriptor ((PKV_DEV) handle, option);
+}
+
+/* Control option */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ return kv_control_option ((PKV_DEV) handle, option, action, val, info);
+}
+
+/* Get scan parameters */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ PKV_DEV dev = (PKV_DEV) handle;
+
+ int side = dev->current_side == SIDE_FRONT ? 0 : 1;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+ /* Setup the parameters for the scan. (guessed value) */
+ int resolution = dev->val[OPT_RESOLUTION].w;
+ int width, length, depth = kv_get_depth (kv_get_mode (dev));;
+
+ DBG (DBG_proc, "sane_get_parameters: initial settings\n");
+ kv_calc_paper_size (dev, &width, &length);
+
+ DBG (DBG_error, "Resolution = %d\n", resolution);
+ DBG (DBG_error, "Paper width = %d, height = %d\n", width, length);
+
+ /* Prepare the parameters for the caller. */
+ dev->params[0].format = kv_get_mode (dev) == SM_COLOR ?
+ SANE_FRAME_RGB : SANE_FRAME_GRAY;
+
+ dev->params[0].last_frame = SANE_TRUE;
+ dev->params[0].pixels_per_line = ((width * resolution) / 1200) & (~0xf);
+
+ dev->params[0].depth = depth > 8 ? 8 : depth;
+
+ dev->params[0].bytes_per_line =
+ (dev->params[0].pixels_per_line / 8) * depth;
+ dev->params[0].lines = (length * resolution) / 1200;
+
+ memcpy (&dev->params[1], &dev->params[0], sizeof (SANE_Parameters));
+ }
+
+ /* Return the current values. */
+ if (params)
+ *params = (dev->params[side]);
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Start scanning */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ SANE_Status status;
+ PKV_DEV dev = (PKV_DEV) handle;
+ SANE_Bool dev_ready;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+ if (!dev->scanning)
+ {
+ /* open device */
+ if (!kv_already_open (dev))
+ {
+ DBG (DBG_proc, "sane_start: need to open device\n");
+ status = kv_open (dev);
+ if (status)
+ {
+ return status;
+ }
+ }
+ /* Begin scan */
+ DBG (DBG_proc, "sane_start: begin scan\n");
+
+ /* Get necessary parameters */
+ sane_get_parameters (dev, NULL);
+
+ dev->current_page = 0;
+ dev->current_side = SIDE_FRONT;
+
+ /* The scanner must be ready. */
+ status = CMD_test_unit_ready (dev, &dev_ready);
+ if (status || !dev_ready)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!strcmp (dev->val[OPT_MANUALFEED].s, "off"))
+ {
+ status = CMD_get_document_existanse (dev);
+ if (status)
+ {
+ DBG (DBG_proc, "sane_start: exit with no more docs\n");
+ return status;
+ }
+ }
+
+ /* Set window */
+ status = CMD_reset_window (dev);
+ if (status)
+ {
+ return status;
+ }
+
+ status = CMD_set_window (dev, SIDE_FRONT, &rs);
+ if (status)
+ {
+ DBG (DBG_proc, "sane_start: error setting window\n");
+ return status;
+ }
+
+ if (rs.status)
+ {
+ DBG (DBG_proc, "sane_start: error setting window\n");
+ DBG (DBG_proc,
+ "sane_start: sense_key=0x%x, ASC=0x%x, ASCQ=0x%x\n",
+ get_RS_sense_key (rs.sense),
+ get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (IS_DUPLEX (dev))
+ {
+ status = CMD_set_window (dev, SIDE_BACK, &rs);
+
+ if (status)
+ {
+ DBG (DBG_proc, "sane_start: error setting window\n");
+ return status;
+ }
+ if (rs.status)
+ {
+ DBG (DBG_proc, "sane_start: error setting window\n");
+ DBG (DBG_proc,
+ "sane_start: sense_key=0x%x, "
+ "ASC=0x%x, ASCQ=0x%x\n",
+ get_RS_sense_key (rs.sense),
+ get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /* Scan */
+ status = CMD_scan (dev);
+ if (status)
+ {
+ return status;
+ }
+
+ status = AllocateImageBuffer (dev);
+ if (status)
+ {
+ return status;
+ }
+ dev->scanning = 1;
+ }
+ else
+ {
+ /* renew page */
+ if (IS_DUPLEX (dev))
+ {
+ if (dev->current_side == SIDE_FRONT)
+ {
+ /* back image data already read, so just return */
+ dev->current_side = SIDE_BACK;
+ DBG (DBG_proc, "sane_start: duplex back\n");
+ status = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+ else
+ {
+ dev->current_side = SIDE_FRONT;
+ dev->current_page++;
+ }
+ }
+ else
+ {
+ dev->current_page++;
+ }
+ }
+ DBG (DBG_proc, "sane_start: NOW SCANNING page\n");
+
+ /* Read image data */
+ status = ReadImageData (dev, dev->current_page);
+ if (status)
+ {
+ dev->scanning = 0;
+ return status;
+ }
+
+ /* Get picture element size */
+ {
+ int width, height;
+ status = CMD_read_pic_elements (dev, dev->current_page,
+ SIDE_FRONT, &width, &height);
+ if (status)
+ return status;
+ }
+
+ if (IS_DUPLEX (dev))
+ {
+ int width, height;
+ status = CMD_read_pic_elements (dev, dev->current_page,
+ SIDE_BACK, &width, &height);
+ if (status)
+ return status;
+ }
+
+ /* software based enhancement functions from sanei_magic */
+ /* these will modify the image, and adjust the params */
+ /* at this point, we are only looking at the front image */
+ /* of simplex or duplex data, back side has already exited */
+ /* so, we do both sides now, if required */
+ if (dev->val[OPT_SWDESKEW].w){
+ buffer_deskew(dev,SIDE_FRONT);
+ }
+ if (dev->val[OPT_SWCROP].w){
+ buffer_crop(dev,SIDE_FRONT);
+ }
+ if (dev->val[OPT_SWDESPECK].w){
+ buffer_despeck(dev,SIDE_FRONT);
+ }
+ if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){
+ buffer_rotate(dev,SIDE_FRONT);
+ }
+
+ if (IS_DUPLEX (dev)){
+ if (dev->val[OPT_SWDESKEW].w){
+ buffer_deskew(dev,SIDE_BACK);
+ }
+ if (dev->val[OPT_SWCROP].w){
+ buffer_crop(dev,SIDE_BACK);
+ }
+ if (dev->val[OPT_SWDESPECK].w){
+ buffer_despeck(dev,SIDE_BACK);
+ }
+ if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){
+ buffer_rotate(dev,SIDE_BACK);
+ }
+ }
+
+ cleanup:
+
+ /* check if we need to skip this page */
+ if (dev->val[OPT_SWSKIP].w && buffer_isblank(dev,dev->current_side)){
+ DBG (DBG_proc, "sane_start: blank page, recurse\n");
+ return sane_start(handle);
+ }
+
+ DBG (DBG_proc, "sane_start: exit\n");
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ PKV_DEV dev = (PKV_DEV) handle;
+ int side = dev->current_side == SIDE_FRONT ? 0 : 1;
+
+ int size = max_len;
+ if (!dev->scanning)
+ return SANE_STATUS_EOF;
+
+ if (size > dev->img_size[side])
+ size = dev->img_size[side];
+
+ if (size == 0)
+ {
+ *len = size;
+ return SANE_STATUS_EOF;
+ }
+
+ if (dev->val[OPT_INVERSE].w &&
+ (kv_get_mode (dev) == SM_BINARY || kv_get_mode (dev) == SM_DITHER))
+ {
+ int i;
+ unsigned char *p = dev->img_pt[side];
+ for (i = 0; i < size; i++)
+ {
+ buf[i] = ~p[i];
+ }
+ }
+ else
+ {
+ memcpy (buf, dev->img_pt[side], size);
+ }
+
+ /*hexdump(DBG_error, "img data", buf, 128); */
+
+ dev->img_pt[side] += size;
+ dev->img_size[side] -= size;
+
+ DBG (DBG_proc, "sane_read: %d bytes to read, "
+ "%d bytes read, EOF=%s %d\n",
+ max_len, size, dev->img_size[side] == 0 ? "True" : "False", side);
+
+ if (len)
+ {
+ *len = size;
+ }
+ if (dev->img_size[side] == 0)
+ {
+ if (!strcmp (dev->val[OPT_FEEDER_MODE].s, "single"))
+ if ((IS_DUPLEX (dev) && side) || !IS_DUPLEX (dev))
+ dev->scanning = 0;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ PKV_DEV dev = (PKV_DEV) handle;
+ DBG (DBG_proc, "sane_cancel: scan canceled.\n");
+ dev->scanning = 0;
+
+ kv_close (dev);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ h=h;
+ m=m;
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ h=h;
+ fd=fd;
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/kvs1025.h b/backend/kvs1025.h
new file mode 100644
index 0000000..cc44b85
--- /dev/null
+++ b/backend/kvs1025.h
@@ -0,0 +1,127 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#ifndef __KVS1025_H
+#define __KVS1025_H
+
+/* SANE backend name */
+#ifdef BACKEND_NAME
+#undef BACKEND_NAME
+#endif
+
+#define BACKEND_NAME kvs1025
+
+/* Build version */
+#define V_BUILD 5
+
+/* Paper range supported -- MAX scanner limits */
+#define KV_MAX_X_RANGE 216
+#define KV_MAX_Y_RANGE 2540
+
+/* Round ULX, ULY, Width and Height to 16 Pixels */
+#define KV_PIXEL_ROUND 19200
+/* (XR * W / 1200) % 16 == 0 i.e. (XR * W) % 19200 == 0 */
+
+/* MAX IULs per LINE */
+#define KV_PIXEL_MAX 14064
+/* Max 14064 pixels per line, 1/1200 inch each */
+
+#define MM_PER_INCH 25.4
+#define mmToIlu(mm) (((mm) * 1200) / MM_PER_INCH)
+#define iluToMm(ilu) (((ilu) * MM_PER_INCH) / 1200)
+
+/* Vendor defined options */
+#define SANE_NAME_DUPLEX "duplex"
+#define SANE_NAME_PAPER_SIZE "paper-size"
+#define SANE_NAME_AUTOSEP "autoseparation"
+#define SANE_NAME_LANDSCAPE "landscape"
+#define SANE_NAME_INVERSE "inverse"
+#define SANE_NAME_MIRROR "mirror"
+#define SANE_NAME_LONGPAPER "longpaper"
+#define SANE_NAME_LENGTHCTL "length-control"
+#define SANE_NAME_MANUALFEED "manual-feed"
+#define SANE_NAME_FEED_TIMEOUT "feed-timeout"
+#define SANE_NAME_DBLFEED "double-feed"
+
+#define SANE_TITLE_DUPLEX SANE_I18N("Duplex")
+#define SANE_TITLE_PAPER_SIZE SANE_I18N("Paper size")
+#define SANE_TITLE_AUTOSEP SANE_I18N("Automatic separation")
+#define SANE_TITLE_LANDSCAPE SANE_I18N("Landscape")
+#define SANE_TITLE_INVERSE SANE_I18N("Inverse Image")
+#define SANE_TITLE_MIRROR SANE_I18N("Mirror image")
+#define SANE_TITLE_LONGPAPER SANE_I18N("Long paper mode")
+#define SANE_TITLE_LENGTHCTL SANE_I18N("Length control mode")
+#define SANE_TITLE_MANUALFEED SANE_I18N("Manual feed mode")
+#define SANE_TITLE_FEED_TIMEOUT SANE_I18N("Manual feed timeout")
+#define SANE_TITLE_DBLFEED SANE_I18N("Double feed detection")
+
+#define SANE_DESC_DUPLEX \
+SANE_I18N("Enable Duplex (Dual-Sided) Scanning")
+#define SANE_DESC_PAPER_SIZE \
+SANE_I18N("Physical size of the paper in the ADF");
+#define SANE_DESC_AUTOSEP \
+SANE_I18N("Automatic separation")
+
+#define SIDE_FRONT 0x00
+#define SIDE_BACK 0x80
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+#define DBG_shortread 101
+
+/* Prototypes of SANE backend functions, see kvs1025.c */
+
+SANE_Status sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback /* __sane_unused__ authorize */ );
+
+void sane_exit (void);
+
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool /*__sane_unused__ local_only*/ );
+
+SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle);
+
+void sane_close (SANE_Handle handle);
+
+const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle
+ handle,
+ SANE_Int option);
+
+SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+SANE_Status sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters * params);
+
+SANE_Status sane_start (SANE_Handle handle);
+
+SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len);
+
+void sane_cancel (SANE_Handle handle);
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m);
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd);
+
+SANE_String_Const sane_strstatus (SANE_Status status);
+
+#endif /* #ifndef __KVS1025_H */
diff --git a/backend/kvs1025_cmds.h b/backend/kvs1025_cmds.h
new file mode 100644
index 0000000..c201ff8
--- /dev/null
+++ b/backend/kvs1025_cmds.h
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#ifndef KVS1025_CMDS_H
+#define KVS1025_CMDS_H
+
+/* Commands supported by the KV-S1020C / KV-S1025C scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_INQUIRY 0x12
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_SCAN 0x1B
+#define SCSI_SEND_10 0x2A
+#define SCSI_READ_10 0x28
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_GET_BUFFER_STATUS 0x34
+#define SCSI_SET_TIMEOUT 0xE1
+
+typedef enum
+{
+ KV_CMD_NONE = 0,
+ KV_CMD_IN = 0x81, /* scanner to pc */
+ KV_CMD_OUT = 0x02 /* pc to scanner */
+} KV_CMD_DIRECTION; /* equals to endpoint address */
+
+typedef struct
+{
+ KV_CMD_DIRECTION direction;
+ unsigned char cdb[12];
+ int cdb_size;
+ int data_size;
+ void *data;
+} KV_CMD_HEADER, *PKV_CMD_HEADER;
+
+#define KV_CMD_TIMEOUT 10000
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7)
+
+typedef enum
+{
+ KV_SUCCESS = 0,
+ KV_FAILED = 1,
+ KV_CHK_CONDITION = 2
+} KV_STATUS;
+
+typedef struct
+{
+ KV_STATUS status;
+ unsigned char reserved[16];
+ unsigned char sense[18];
+} KV_CMD_RESPONSE, *PKV_CMD_RESPONSE;
+
+
+#endif /*#ifndef KVS1025_CMDS_H */
diff --git a/backend/kvs1025_low.c b/backend/kvs1025_low.c
new file mode 100644
index 0000000..d834f61
--- /dev/null
+++ b/backend/kvs1025_low.c
@@ -0,0 +1,1178 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+#include "../include/sane/sanei_magic.h"
+
+#include "kvs1025.h"
+#include "kvs1025_low.h"
+#include "kvs1025_usb.h"
+
+#include "../include/sane/sanei_debug.h"
+
+/* Global storage */
+
+PKV_DEV g_devices = NULL; /* Chain of devices */
+const SANE_Device **g_devlist = NULL;
+
+/* Static functions */
+
+/* Free one device */
+static void
+kv_free (KV_DEV ** pdev)
+{
+ KV_DEV *dev;
+
+ dev = *pdev;
+
+ if (dev == NULL)
+ return;
+
+ DBG (DBG_proc, "kv_free : enter\n");
+
+ kv_close (dev);
+
+ DBG (DBG_proc, "kv_free : free image buffer 0 \n");
+ if (dev->img_buffers[0])
+ free (dev->img_buffers[0]);
+ DBG (DBG_proc, "kv_free : free image buffer 1 \n");
+ if (dev->img_buffers[1])
+ free (dev->img_buffers[1]);
+ DBG (DBG_proc, "kv_free : free scsi device name\n");
+ if (dev->scsi_device_name)
+ free (dev->scsi_device_name);
+
+ DBG (DBG_proc, "kv_free : free SCSI buffer\n");
+ if (dev->buffer0)
+ free (dev->buffer0);
+
+ DBG (DBG_proc, "kv_free : free dev \n");
+ free (dev);
+
+ *pdev = NULL;
+
+ DBG (DBG_proc, "kv_free : exit\n");
+}
+
+/* Free all devices */
+static void
+kv_free_devices (void)
+{
+ PKV_DEV dev;
+ while (g_devices)
+ {
+ dev = g_devices;
+ g_devices = dev->next;
+ kv_free (&dev);
+ }
+ if (g_devlist)
+ {
+ free (g_devlist);
+ g_devlist = NULL;
+ }
+}
+
+/* Get all supported scanners, and store into g_scanners_supported */
+SANE_Status
+kv_enum_devices (void)
+{
+ SANE_Status status;
+ kv_free_devices ();
+ status = kv_usb_enum_devices ();
+ if (status)
+ {
+ kv_free_devices ();
+ }
+
+ return status;
+}
+
+/* Return devices list to the front end */
+void
+kv_get_devices_list (const SANE_Device *** devices_list)
+{
+ *devices_list = g_devlist;
+}
+
+/* Close all open handles and clean up global storage */
+void
+kv_exit (void)
+{
+ kv_free_devices (); /* Free all devices */
+ kv_usb_cleanup (); /* Clean USB bus */
+}
+
+/* Open device by name */
+SANE_Status
+kv_open_by_name (SANE_String_Const devicename, SANE_Handle * handle)
+{
+
+ PKV_DEV pd = g_devices;
+ DBG (DBG_proc, "sane_open: enter (dev_name=%s)\n", devicename);
+ while (pd)
+ {
+ if (strcmp (pd->sane.name, devicename) == 0)
+ {
+ if (kv_open (pd) == 0)
+ {
+ *handle = (SANE_Handle) pd;
+ DBG (DBG_proc, "sane_open: leave\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ pd = pd->next;
+ }
+ DBG (DBG_proc, "sane_open: leave -- no device found\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* Open a device */
+SANE_Status
+kv_open (PKV_DEV dev)
+{
+ SANE_Status status = SANE_STATUS_UNSUPPORTED;
+ int i;
+#define RETRAY_NUM 3
+
+
+ if (dev->bus_mode == KV_USB_BUS)
+ {
+ status = kv_usb_open (dev);
+ }
+ if (status)
+ return status;
+ for (i = 0; i < RETRAY_NUM; i++)
+ {
+ SANE_Bool dev_ready;
+ status = CMD_test_unit_ready (dev, &dev_ready);
+ if (!status && dev_ready)
+ break;
+ }
+
+ if (status == 0)
+ {
+ /* Read device support info */
+ status = CMD_read_support_info (dev);
+
+ if (status == 0)
+ {
+ /* Init options */
+ kv_init_options (dev);
+ status = CMD_set_timeout (dev, dev->val[OPT_FEED_TIMEOUT].w);
+ }
+ }
+ dev->scanning = 0;
+ return status;
+}
+
+/* Check if device is already open */
+
+SANE_Bool
+kv_already_open (PKV_DEV dev)
+{
+ SANE_Bool status = 0;
+
+ if (dev->bus_mode == KV_USB_BUS)
+ {
+ status = kv_usb_already_open (dev);
+ }
+
+ return status;
+}
+
+/* Close a device */
+void
+kv_close (PKV_DEV dev)
+{
+ if (dev->bus_mode == KV_USB_BUS)
+ {
+ kv_usb_close (dev);
+ }
+ dev->scanning = 0;
+}
+
+/* Send command to a device */
+SANE_Status
+kv_send_command (PKV_DEV dev,
+ PKV_CMD_HEADER header, PKV_CMD_RESPONSE response)
+{
+ SANE_Status status = SANE_STATUS_UNSUPPORTED;
+ if (dev->bus_mode == KV_USB_BUS)
+ {
+ if (!kv_usb_already_open(dev))
+ {
+ DBG (DBG_error, "kv_send_command error: device not open.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status = kv_usb_send_command (dev, header, response);
+ }
+
+ return status;
+}
+
+/* Commands */
+
+SANE_Status
+CMD_test_unit_ready (PKV_DEV dev, SANE_Bool * ready)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_test_unit_ready\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_NONE;
+ hdr.cdb[0] = SCSI_TEST_UNIT_READY;
+ hdr.cdb_size = 6;
+
+ status = kv_send_command (dev, &hdr, &rs);
+
+ if (status == 0)
+ {
+ *ready = (rs.status == KV_SUCCESS ? 1 : 0);
+ }
+
+ return status;
+}
+
+SANE_Status
+CMD_set_timeout (PKV_DEV dev, SANE_Word timeout)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_set_timeout\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_OUT;
+ hdr.cdb[0] = SCSI_SET_TIMEOUT;
+ hdr.cdb[2] = 0x8D;
+ hdr.cdb[8] = 0x2;
+ hdr.cdb_size = 10;
+ hdr.data = dev->buffer;
+ dev->buffer[0] = 0;
+ dev->buffer[1] = (SANE_Byte) timeout;
+ hdr.data_size = 2;
+
+ status = kv_send_command (dev, &hdr, &rs);
+
+ return status;
+}
+
+SANE_Status
+CMD_read_support_info (PKV_DEV dev)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_read_support_info\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_READ_10;
+ hdr.cdb[2] = 0x93;
+ Ito24 (32, &hdr.cdb[6]);
+ hdr.data = dev->buffer;
+ hdr.data_size = 32;
+
+ status = kv_send_command (dev, &hdr, &rs);
+
+ DBG (DBG_error, "test.\n");
+
+ if (status == 0)
+ {
+ if (rs.status == 0)
+ {
+ int min_x_res, min_y_res, max_x_res, max_y_res;
+ int step_x_res, step_y_res;
+
+ dev->support_info.memory_size
+ = (dev->buffer[2] << 8 | dev->buffer[3]);
+ min_x_res = (dev->buffer[4] << 8) | dev->buffer[5];
+ min_y_res = (dev->buffer[6] << 8) | dev->buffer[7];
+ max_x_res = (dev->buffer[8] << 8) | dev->buffer[9];
+ max_y_res = (dev->buffer[10] << 8) | dev->buffer[11];
+ step_x_res = (dev->buffer[12] << 8) | dev->buffer[13];
+ step_y_res = (dev->buffer[14] << 8) | dev->buffer[15];
+
+ dev->support_info.min_resolution =
+ min_x_res > min_y_res ? min_x_res : min_y_res;
+ dev->support_info.max_resolution =
+ max_x_res < max_y_res ? max_x_res : max_y_res;
+ dev->support_info.step_resolution =
+ step_x_res > step_y_res ? step_x_res : step_y_res;
+ dev->support_info.support_duplex =
+ ((dev->buffer[0] & 0x08) == 0) ? 1 : 0;
+ dev->support_info.support_lamp =
+ ((dev->buffer[23] & 0x80) != 0) ? 1 : 0;
+
+ dev->support_info.max_x_range = KV_MAX_X_RANGE;
+ dev->support_info.max_y_range = KV_MAX_Y_RANGE;
+
+ dev->x_range.min = dev->y_range.min = 0;
+ dev->x_range.max = SANE_FIX (dev->support_info.max_x_range);
+ dev->y_range.max = SANE_FIX (dev->support_info.max_y_range);
+ dev->x_range.quant = dev->y_range.quant = 0;
+
+ DBG (DBG_error,
+ "support_info.memory_size = %d (MB)\n",
+ dev->support_info.memory_size);
+ DBG (DBG_error,
+ "support_info.min_resolution = %d (DPI)\n",
+ dev->support_info.min_resolution);
+ DBG (DBG_error,
+ "support_info.max_resolution = %d (DPI)\n",
+ dev->support_info.max_resolution);
+ DBG (DBG_error,
+ "support_info.step_resolution = %d (DPI)\n",
+ dev->support_info.step_resolution);
+ DBG (DBG_error,
+ "support_info.support_duplex = %s\n",
+ dev->support_info.support_duplex ? "TRUE" : "FALSE");
+ DBG (DBG_error, "support_info.support_lamp = %s\n",
+ dev->support_info.support_lamp ? "TRUE" : "FALSE");
+ }
+ else
+ {
+ DBG (DBG_error, "Error in CMD_get_support_info, "
+ "sense_key=%d, ASC=%d, ASCQ=%d\n",
+ get_RS_sense_key (rs.sense),
+ get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
+
+ }
+ }
+
+ return status;
+}
+
+SANE_Status
+CMD_scan (PKV_DEV dev)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_scan\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_NONE;
+ hdr.cdb[0] = SCSI_SCAN;
+ hdr.cdb_size = 6;
+
+ status = kv_send_command (dev, &hdr, &rs);
+
+ if (status == 0 && rs.status != 0)
+ {
+ DBG (DBG_error,
+ "Error in CMD_scan, sense_key=%d, ASC=%d, ASCQ=%d\n",
+ get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense),
+ get_RS_ASCQ (rs.sense));
+ }
+
+ return status;
+}
+
+SANE_Status
+CMD_set_window (PKV_DEV dev, int side, PKV_CMD_RESPONSE rs)
+{
+ unsigned char *window;
+ unsigned char *windowdata;
+ int size = 74;
+ KV_SCAN_MODE scan_mode;
+ KV_CMD_HEADER hdr;
+
+ DBG (DBG_proc, "CMD_set_window\n");
+
+ window = (unsigned char *) dev->buffer;
+ windowdata = window + 8;
+
+ memset (&hdr, 0, sizeof (hdr));
+ memset (window, 0, size);
+
+ Ito16 (66, &window[6]); /* Window descriptor block length */
+
+ /* Set window data */
+
+ scan_mode = kv_get_mode (dev);
+
+ kv_set_window_data (dev, scan_mode, side, windowdata);
+
+ hdr.direction = KV_CMD_OUT;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_SET_WINDOW;
+ Ito24 (size, &hdr.cdb[6]);
+ hdr.data = window;
+ hdr.data_size = size;
+
+ hexdump (DBG_error, "window", window, size);
+
+ return kv_send_command (dev, &hdr, rs);
+}
+
+SANE_Status
+CMD_reset_window (PKV_DEV dev)
+{
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+ SANE_Status status;
+
+ DBG (DBG_proc, "CMD_reset_window\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_NONE;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_SET_WINDOW;
+
+ status = kv_send_command (dev, &hdr, &rs);
+ if (rs.status != 0)
+ status = SANE_STATUS_INVAL;
+
+ return status;
+}
+
+SANE_Status
+CMD_get_buff_status (PKV_DEV dev, int *front_size, int *back_size)
+{
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+ SANE_Status status;
+ unsigned char *data = (unsigned char *) dev->buffer;
+ int size = 12;
+ memset (&hdr, 0, sizeof (hdr));
+ memset (data, 0, size);
+
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_GET_BUFFER_STATUS;
+ hdr.cdb[8] = size;
+ hdr.data = data;
+ hdr.data_size = size;
+
+ status = kv_send_command (dev, &hdr, &rs);
+ if (status == 0)
+ {
+ if (rs.status == KV_CHK_CONDITION)
+ return SANE_STATUS_NO_DOCS;
+ else
+ {
+ unsigned char *p = data + 4;
+ if (p[0] == SIDE_FRONT)
+ {
+ *front_size = (p[5] << 16) | (p[6] << 8) | p[7];
+ }
+ else
+ {
+ *back_size = (p[5] << 16) | (p[6] << 8) | p[7];
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+ return status;
+}
+
+SANE_Status
+CMD_wait_buff_status (PKV_DEV dev, int *front_size, int *back_size)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int cnt = 0;
+ *front_size = 0;
+ *back_size = 0;
+
+ DBG (DBG_proc, "CMD_wait_buff_status: enter feed %s\n",
+ dev->val[OPT_MANUALFEED].s);
+
+ do
+ {
+ DBG (DBG_proc, "CMD_wait_buff_status: tray #%d of %d\n", cnt,
+ dev->val[OPT_FEED_TIMEOUT].w);
+ status = CMD_get_buff_status (dev, front_size, back_size);
+ sleep (1);
+ }
+ while (status == SANE_STATUS_GOOD && (*front_size == 0)
+ && (*back_size == 0) && cnt++ < dev->val[OPT_FEED_TIMEOUT].w);
+
+ if (cnt > dev->val[OPT_FEED_TIMEOUT].w)
+ status = SANE_STATUS_NO_DOCS;
+
+ if (status == 0)
+ DBG (DBG_proc, "CMD_wait_buff_status: exit "
+ "front_size %d, back_size %d\n", *front_size, *back_size);
+ else
+ DBG (DBG_proc, "CMD_wait_buff_status: exit with no docs\n");
+ return status;
+}
+
+
+SANE_Status
+CMD_read_pic_elements (PKV_DEV dev, int page, int side,
+ int *width, int *height)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_read_pic_elements\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_READ_10;
+ hdr.cdb[2] = 0x80;
+ hdr.cdb[4] = page;
+ hdr.cdb[5] = side;
+ Ito24 (16, &hdr.cdb[6]);
+ hdr.data = dev->buffer;
+ hdr.data_size = 16;
+
+ status = kv_send_command (dev, &hdr, &rs);
+ if (status == 0)
+ {
+ if (rs.status == 0)
+ {
+ int s = side == SIDE_FRONT ? 0 : 1;
+ int depth = kv_get_depth (kv_get_mode (dev));
+ *width = B32TOI (dev->buffer);
+ *height = B32TOI (&dev->buffer[4]);
+
+ assert ((*width) % 8 == 0);
+
+ DBG (DBG_proc, "CMD_read_pic_elements: "
+ "Page %d, Side %s, W=%d, H=%d\n",
+ page, side == SIDE_FRONT ? "F" : "B", *width, *height);
+
+ dev->params[s].format = kv_get_mode (dev) == SM_COLOR ?
+ SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ dev->params[s].last_frame = SANE_TRUE;
+ dev->params[s].depth = depth > 8 ? 8 : depth;
+ dev->params[s].lines = *height ? *height
+ : dev->val[OPT_LANDSCAPE].w ? (*width * 3) / 4 : (*width * 4) / 3;
+ dev->params[s].pixels_per_line = *width;
+ dev->params[s].bytes_per_line =
+ (dev->params[s].pixels_per_line / 8) * depth;
+ }
+ else
+ {
+ DBG (DBG_proc, "CMD_read_pic_elements: failed\n");
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ return status;
+}
+
+SANE_Status
+CMD_read_image (PKV_DEV dev, int page, int side,
+ unsigned char *buffer, int *psize, KV_CMD_RESPONSE * rs)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ int size = *psize;
+
+ DBG (DBG_proc, "CMD_read_image\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_READ_10;
+ hdr.cdb[4] = page;
+ hdr.cdb[5] = side;
+ Ito24 (size, &hdr.cdb[6]);
+ hdr.data = buffer;
+ hdr.data_size = size;
+
+ *psize = 0;
+
+ status = kv_send_command (dev, &hdr, rs);
+
+ if (status)
+ return status;
+
+ *psize = size;
+
+ if (rs->status == KV_CHK_CONDITION && get_RS_ILI (rs->sense))
+ {
+ int delta = B32TOI (&rs->sense[3]);
+ DBG (DBG_error, "size=%d, delta=0x%x (%d)\n", size, delta, delta);
+ *psize = size - delta;
+ }
+
+ DBG (DBG_error, "CMD_read_image: bytes requested=%d, read=%d\n",
+ size, *psize);
+ DBG (DBG_error, "CMD_read_image: ILI=%d, EOM=%d\n",
+ get_RS_ILI (rs->sense), get_RS_EOM (rs->sense));
+
+ return status;
+}
+
+SANE_Status
+CMD_get_document_existanse (PKV_DEV dev)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_get_document_existanse\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_READ_10;
+ hdr.cdb[2] = 0x81;
+ Ito24 (6, &hdr.cdb[6]);
+ hdr.data = dev->buffer;
+ hdr.data_size = 6;
+
+ status = kv_send_command (dev, &hdr, &rs);
+ if (status)
+ return status;
+ if (rs.status)
+ return SANE_STATUS_NO_DOCS;
+ if ((dev->buffer[0] & 0x20) != 0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+SANE_Status
+CMD_wait_document_existanse (PKV_DEV dev)
+{
+ SANE_Status status;
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+ int cnt;
+
+ DBG (DBG_proc, "CMD_wait_document_existanse\n");
+
+ memset (&hdr, 0, sizeof (hdr));
+
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb_size = 10;
+ hdr.cdb[0] = SCSI_READ_10;
+ hdr.cdb[2] = 0x81;
+ Ito24 (6, &hdr.cdb[6]);
+ hdr.data = dev->buffer;
+ hdr.data_size = 6;
+
+ for (cnt = 0; cnt < dev->val[OPT_FEED_TIMEOUT].w; cnt++)
+ {
+ DBG (DBG_proc, "CMD_wait_document_existanse: tray #%d of %d\n", cnt,
+ dev->val[OPT_FEED_TIMEOUT].w);
+ status = kv_send_command (dev, &hdr, &rs);
+ if (status)
+ return status;
+ if (rs.status)
+ return SANE_STATUS_NO_DOCS;
+ if ((dev->buffer[0] & 0x20) != 0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else if (strcmp (dev->val[OPT_MANUALFEED].s, "off") == 0)
+ {
+ return SANE_STATUS_NO_DOCS;
+ }
+ sleep (1);
+ }
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+SANE_Status
+CMD_request_sense (PKV_DEV dev)
+{
+ KV_CMD_HEADER hdr;
+ KV_CMD_RESPONSE rs;
+
+ DBG (DBG_proc, "CMD_request_sense\n");
+ memset (&hdr, 0, sizeof (hdr));
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb[0] = SCSI_REQUEST_SENSE;
+ hdr.cdb[4] = 0x12;
+ hdr.cdb_size = 6;
+ hdr.data_size = 0x12;
+ hdr.data = dev->buffer;
+
+ return kv_send_command (dev, &hdr, &rs);
+}
+
+/* Scan routines */
+
+/* Allocate image buffer for one page (1 or 2 sides) */
+
+SANE_Status
+AllocateImageBuffer (PKV_DEV dev)
+{
+ int *size = dev->bytes_to_read;
+ int sides = IS_DUPLEX (dev) ? 2 : 1;
+ int i;
+ size[0] = dev->params[0].bytes_per_line * dev->params[0].lines;
+ size[1] = dev->params[1].bytes_per_line * dev->params[1].lines;
+
+ DBG (DBG_proc, "AllocateImageBuffer: enter\n");
+
+ for (i = 0; i < sides; i++)
+ {
+ SANE_Byte *p;
+ DBG (DBG_proc, "AllocateImageBuffer: size(%c)=%d\n",
+ i ? 'B' : 'F', size[i]);
+
+ if (dev->img_buffers[i] == NULL)
+ {
+ p = (SANE_Byte *) malloc (size[i]);
+ if (p == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->img_buffers[i] = p;
+ }
+ else
+ {
+ p = (SANE_Byte *) realloc (dev->img_buffers[i], size[i]);
+ if (p == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ dev->img_buffers[i] = p;
+ }
+ }
+ }
+ DBG (DBG_proc, "AllocateImageBuffer: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Read image data from scanner dev->img_buffers[0],
+ for the simplex page */
+SANE_Status
+ReadImageDataSimplex (PKV_DEV dev, int page)
+{
+ int bytes_to_read = dev->bytes_to_read[0];
+ SANE_Byte *buffer = (SANE_Byte *) dev->buffer;
+ int buff_size = SCSI_BUFFER_SIZE;
+ SANE_Byte *pt = dev->img_buffers[0];
+ KV_CMD_RESPONSE rs;
+ dev->img_size[0] = 0;
+ dev->img_size[1] = 0;
+
+ /* read loop */
+ do
+ {
+ int size = buff_size;
+ SANE_Status status;
+ DBG (DBG_error, "Bytes left = %d\n", bytes_to_read);
+ status = CMD_read_image (dev, page, SIDE_FRONT, buffer, &size, &rs);
+ if (status)
+ {
+ return status;
+ }
+ if (rs.status)
+ {
+ if (get_RS_sense_key (rs.sense))
+ {
+ DBG (DBG_error, "Error reading image data, "
+ "sense_key=%d, ASC=%d, ASCQ=%d",
+ get_RS_sense_key (rs.sense),
+ get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
+
+ if (get_RS_sense_key (rs.sense) == 3)
+ {
+ if (!get_RS_ASCQ (rs.sense))
+ return SANE_STATUS_NO_DOCS;
+ return SANE_STATUS_JAMMED;
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ }
+ /* copy data to image buffer */
+ if (size > bytes_to_read)
+ {
+ size = bytes_to_read;
+ }
+ if (size > 0)
+ {
+ memcpy (pt, buffer, size);
+ bytes_to_read -= size;
+ pt += size;
+ dev->img_size[0] += size;
+ }
+ }
+ while (!get_RS_EOM (rs.sense));
+
+ assert (pt == dev->img_buffers[0] + dev->img_size[0]);
+ DBG (DBG_error, "Image size = %d\n", dev->img_size[0]);
+ return SANE_STATUS_GOOD;
+}
+
+/* Read image data from scanner dev->img_buffers[0],
+ for the duplex page */
+SANE_Status
+ReadImageDataDuplex (PKV_DEV dev, int page)
+{
+ int bytes_to_read[2];
+ SANE_Byte *buffer = (SANE_Byte *) dev->buffer;
+ int buff_size[2];
+ SANE_Byte *pt[2];
+ KV_CMD_RESPONSE rs;
+ int sides[2];
+ SANE_Bool eoms[2];
+ int current_side = 1;
+
+ bytes_to_read[0] = dev->bytes_to_read[0];
+ bytes_to_read[1] = dev->bytes_to_read[1];
+
+ pt[0] = dev->img_buffers[0];
+ pt[1] = dev->img_buffers[1];
+
+ sides[0] = SIDE_FRONT;
+ sides[1] = SIDE_BACK;
+ eoms[0] = eoms[1] = 0;
+
+ buff_size[0] = SCSI_BUFFER_SIZE;
+ buff_size[1] = SCSI_BUFFER_SIZE;
+ dev->img_size[0] = 0;
+ dev->img_size[1] = 0;
+
+ /* read loop */
+ do
+ {
+ int size = buff_size[current_side];
+ SANE_Status status;
+ DBG (DBG_error, "Bytes left (F) = %d\n", bytes_to_read[0]);
+ DBG (DBG_error, "Bytes left (B) = %d\n", bytes_to_read[1]);
+
+ status = CMD_read_image (dev, page, sides[current_side],
+ buffer, &size, &rs);
+ if (status)
+ {
+ return status;
+ }
+ if (rs.status)
+ {
+ if (get_RS_sense_key (rs.sense))
+ {
+ DBG (DBG_error, "Error reading image data, "
+ "sense_key=%d, ASC=%d, ASCQ=%d",
+ get_RS_sense_key (rs.sense),
+ get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
+
+ if (get_RS_sense_key (rs.sense) == 3)
+ {
+ if (!get_RS_ASCQ (rs.sense))
+ return SANE_STATUS_NO_DOCS;
+ return SANE_STATUS_JAMMED;
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* copy data to image buffer */
+ if (size > bytes_to_read[current_side])
+ {
+ size = bytes_to_read[current_side];
+ }
+ if (size > 0)
+ {
+ memcpy (pt[current_side], buffer, size);
+ bytes_to_read[current_side] -= size;
+ pt[current_side] += size;
+ dev->img_size[current_side] += size;
+ }
+ if (rs.status)
+ {
+ if (get_RS_EOM (rs.sense))
+ {
+ eoms[current_side] = 1;
+ }
+ if (get_RS_ILI (rs.sense))
+ {
+ current_side++;
+ current_side &= 1;
+ }
+ }
+ }
+ while (eoms[0] == 0 || eoms[1] == 0);
+
+ DBG (DBG_error, "Image size (F) = %d\n", dev->img_size[0]);
+ DBG (DBG_error, "Image size (B) = %d\n", dev->img_size[1]);
+
+ assert (pt[0] == dev->img_buffers[0] + dev->img_size[0]);
+ assert (pt[1] == dev->img_buffers[1] + dev->img_size[1]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Read image data for one page */
+SANE_Status
+ReadImageData (PKV_DEV dev, int page)
+{
+ SANE_Status status;
+ DBG (DBG_proc, "Reading image data for page %d\n", page);
+
+ if (IS_DUPLEX (dev))
+ {
+ DBG (DBG_proc, "ReadImageData: Duplex %d\n", page);
+ status = ReadImageDataDuplex (dev, page);
+ }
+ else
+ {
+ DBG (DBG_proc, "ReadImageData: Simplex %d\n", page);
+ status = ReadImageDataSimplex (dev, page);
+ }
+ dev->img_pt[0] = dev->img_buffers[0];
+ dev->img_pt[1] = dev->img_buffers[1];
+
+ DBG (DBG_proc, "Reading image data for page %d, finished\n", page);
+
+ return status;
+}
+
+/* Look in image for likely upper and left paper edges, then rotate
+ * image so that upper left corner of paper is upper left of image.
+ * FIXME: should we do this before we binarize instead of after? */
+SANE_Status
+buffer_deskew(PKV_DEV s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int bg_color = 0xd6;
+ int side_index = (side == SIDE_FRONT)?0:1;
+ int resolution = s->val[OPT_RESOLUTION].w;
+
+ DBG (10, "buffer_deskew: start\n");
+
+ /*only find skew on first image from a page, or if first image had error */
+ if(side == SIDE_FRONT || s->deskew_stat){
+
+ s->deskew_stat = sanei_magic_findSkew(
+ &s->params[side_index],s->img_buffers[side_index],
+ resolution,resolution,
+ &s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope);
+
+ if(s->deskew_stat){
+ DBG (5, "buffer_despeck: bad findSkew, bailing\n");
+ goto cleanup;
+ }
+ }
+ /* backside images can use a 'flipped' version of frontside data */
+ else{
+ s->deskew_slope *= -1;
+ s->deskew_vals[0]
+ = s->params[side_index].pixels_per_line - s->deskew_vals[0];
+ }
+
+ ret = sanei_magic_rotate(&s->params[side_index],s->img_buffers[side_index],
+ s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color);
+
+ if(ret){
+ DBG(5,"buffer_deskew: rotate error: %d",ret);
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ cleanup:
+ DBG (10, "buffer_deskew: finish\n");
+ return ret;
+}
+
+/* Look in image for likely left/right/bottom paper edges, then crop image.
+ * Does not attempt to rotate the image, that should be done first.
+ * FIXME: should we do this before we binarize instead of after? */
+SANE_Status
+buffer_crop(PKV_DEV s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side_index = (side == SIDE_FRONT)?0:1;
+ int resolution = s->val[OPT_RESOLUTION].w;
+
+ DBG (10, "buffer_crop: start\n");
+
+ /*only find edges on first image from a page, or if first image had error */
+ if(side == SIDE_FRONT || s->crop_stat){
+
+ s->crop_stat = sanei_magic_findEdges(
+ &s->params[side_index],s->img_buffers[side_index],
+ resolution,resolution,
+ &s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]);
+
+ if(s->crop_stat){
+ DBG (5, "buffer_crop: bad edges, bailing\n");
+ goto cleanup;
+ }
+
+ DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n",
+ s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
+
+ /* we dont listen to the 'top' value, since the top is not padded */
+ /*s->crop_vals[0] = 0;*/
+ }
+ /* backside images can use a 'flipped' version of frontside data */
+ else{
+ int left = s->crop_vals[2];
+ int right = s->crop_vals[3];
+
+ s->crop_vals[2] = s->params[side_index].pixels_per_line - right;
+ s->crop_vals[3] = s->params[side_index].pixels_per_line - left;
+ }
+
+ /* now crop the image */
+ ret = sanei_magic_crop(&s->params[side_index],s->img_buffers[side_index],
+ s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
+
+ if(ret){
+ DBG (5, "buffer_crop: bad crop, bailing\n");
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ /* update image size counter to new, smaller size */
+ s->img_size[side_index]
+ = s->params[side_index].lines * s->params[side_index].bytes_per_line;
+
+ cleanup:
+ DBG (10, "buffer_crop: finish\n");
+ return ret;
+}
+
+/* Look in image for disconnected 'spots' of the requested size.
+ * Replace the spots with the average color of the surrounding pixels.
+ * FIXME: should we do this before we binarize instead of after? */
+SANE_Status
+buffer_despeck(PKV_DEV s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side_index = (side == SIDE_FRONT)?0:1;
+
+ DBG (10, "buffer_despeck: start\n");
+
+ ret = sanei_magic_despeck(
+ &s->params[side_index],s->img_buffers[side_index],s->val[OPT_SWDESPECK].w
+ );
+ if(ret){
+ DBG (5, "buffer_despeck: bad despeck, bailing\n");
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ cleanup:
+ DBG (10, "buffer_despeck: finish\n");
+ return ret;
+}
+
+/* Look if image has too few dark pixels.
+ * FIXME: should we do this before we binarize instead of after? */
+int
+buffer_isblank(PKV_DEV s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int side_index = (side == SIDE_FRONT)?0:1;
+ int status = 0;
+
+ DBG (10, "buffer_isblank: start\n");
+
+ ret = sanei_magic_isBlank(
+ &s->params[side_index],s->img_buffers[side_index],
+ SANE_UNFIX(s->val[OPT_SWSKIP].w)
+ );
+
+ if(ret == SANE_STATUS_NO_DOCS){
+ DBG (5, "buffer_isblank: blank!\n");
+ status = 1;
+ }
+ else if(ret){
+ DBG (5, "buffer_isblank: error %d\n",ret);
+ }
+
+ DBG (10, "buffer_isblank: finished\n");
+ return status;
+}
+
+/* Look if image needs rotation
+ * FIXME: should we do this before we binarize instead of after? */
+SANE_Status
+buffer_rotate(PKV_DEV s, int side)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int angle = 0;
+ int side_index = (side == SIDE_FRONT)?0:1;
+ int resolution = s->val[OPT_RESOLUTION].w;
+
+ DBG (10, "buffer_rotate: start\n");
+
+ if(s->val[OPT_SWDEROTATE].w){
+ ret = sanei_magic_findTurn(
+ &s->params[side_index],s->img_buffers[side_index],
+ resolution,resolution,&angle);
+
+ if(ret){
+ DBG (5, "buffer_rotate: error %d\n",ret);
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+ }
+
+ angle += s->val[OPT_ROTATE].w;
+
+ /*90 or 270 degree rotations are reversed on back side*/
+ if(side == SIDE_BACK && s->val[OPT_ROTATE].w % 180){
+ angle += 180;
+ }
+
+ ret = sanei_magic_turn(
+ &s->params[side_index],s->img_buffers[side_index],
+ angle);
+
+ if(ret){
+ DBG (5, "buffer_rotate: error %d\n",ret);
+ ret = SANE_STATUS_GOOD;
+ goto cleanup;
+ }
+
+ /* update image size counter to new, smaller size */
+ s->img_size[side_index]
+ = s->params[side_index].lines * s->params[side_index].bytes_per_line;
+
+ cleanup:
+ DBG (10, "buffer_rotate: finished\n");
+ return ret;
+}
diff --git a/backend/kvs1025_low.h b/backend/kvs1025_low.h
new file mode 100644
index 0000000..9c92832
--- /dev/null
+++ b/backend/kvs1025_low.h
@@ -0,0 +1,290 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#ifndef __KVS1025_LOW_H
+#define __KVS1025_LOW_H
+
+#include "kvs1025_cmds.h"
+
+#define VENDOR_ID 0x04DA
+
+typedef enum
+{
+ KV_S1020C = 0x1007,
+ KV_S1025C = 0x1006,
+ KV_S1045C = 0x1010
+} KV_MODEL_TYPE;
+
+/* Store an integer in 2, 3 or 4 byte in a big-endian array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
+
+/* 24 bits from an array to an integer. */
+#define B24TOI(buf) \
+ (((unsigned char *)buf)[0] << 16) | \
+ (((unsigned char *)buf)[1] << 8) | \
+ (((unsigned char *)buf)[2] << 0))
+
+#define SCSI_FD int
+#define SCSI_BUFFER_SIZE (0x40000-12)
+
+typedef enum
+{
+ KV_SCSI_BUS = 0x01,
+ KV_USB_BUS = 0x02
+} KV_BUS_MODE;
+
+typedef enum
+{
+ SM_BINARY = 0x00,
+ SM_DITHER = 0x01,
+ SM_GRAYSCALE = 0x02,
+ SM_COLOR = 0x05
+} KV_SCAN_MODE;
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+} CDB;
+
+typedef struct
+{
+ int width;
+ int height;
+} KV_PAPER_SIZE;
+
+/* remarked -- KV-S1020C / KV-S1025C supports ADF only
+typedef enum
+{
+ TRUPER_ADF = 0,
+ TRUPER_FLATBED = 1
+} KV_SCAN_SOURCE;
+*/
+
+/* options */
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+
+ /* General options */
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+ OPT_DUPLEX, /* Duplex mode */
+ OPT_SCAN_SOURCE, /* Scan source, fixed to ADF */
+ OPT_FEEDER_MODE, /* Feeder mode, fixed to Continous */
+ OPT_LONGPAPER, /* Long paper mode */
+ OPT_LENGTHCTL, /* Length control mode */
+ OPT_MANUALFEED, /* Manual feed mode */
+ OPT_FEED_TIMEOUT, /* Feed timeout */
+ OPT_DBLFEED, /* Double feed detection mode */
+ OPT_FIT_TO_PAGE, /* Scanner shrinks image to fit scanned page */
+
+ /* Geometry group */
+ OPT_GEOMETRY_GROUP,
+ OPT_PAPER_SIZE, /* Paper size */
+ OPT_LANDSCAPE, /* true if landscape; new for Truper 3200/3600 */
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS, /* Brightness */
+ OPT_CONTRAST, /* Contrast */
+ OPT_AUTOMATIC_THRESHOLD, /* Binary threshold */
+ OPT_HALFTONE_PATTERN, /* Halftone pattern */
+ OPT_AUTOMATIC_SEPARATION, /* Automatic separation */
+ OPT_WHITE_LEVEL, /* White level */
+ OPT_NOISE_REDUCTION, /* Noise reduction */
+ OPT_IMAGE_EMPHASIS, /* Image emphasis */
+ OPT_GAMMA, /* Gamma */
+ OPT_LAMP, /* Lamp -- color drop out */
+ OPT_INVERSE, /* Inverse image */
+ OPT_MIRROR, /* Mirror image */
+ OPT_JPEG, /* JPEG Compression */
+ OPT_ROTATE, /* Rotate image */
+
+ OPT_SWDESKEW, /* Software deskew */
+ OPT_SWDESPECK, /* Software despeckle */
+ OPT_SWDEROTATE, /* Software detect/correct 90 deg. rotation */
+ OPT_SWCROP, /* Software autocrop */
+ OPT_SWSKIP, /* Software blank page skip */
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+} KV_OPTION;
+
+typedef struct
+{
+ int memory_size; /* in MB */
+ int min_resolution; /* in DPI */
+ int max_resolution; /* in DPI */
+ int step_resolution; /* in DPI */
+ int support_duplex; /* 1 if true */
+ int support_lamp; /* 1 if true */
+ int max_x_range; /* in mm */
+ int max_y_range; /* in mm */
+} KV_SUPPORT_INFO;
+
+typedef struct kv_scanner_dev
+{
+ struct kv_scanner_dev *next;
+
+ SANE_Device sane;
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_type_str[32];
+ char scsi_vendor[12];
+ char scsi_product[20];
+ char scsi_version[8];
+
+ /* Bus info */
+ KV_BUS_MODE bus_mode;
+ SANE_Int usb_fd;
+ char device_name[100];
+ char *scsi_device_name;
+ SCSI_FD scsi_fd;
+
+ KV_MODEL_TYPE model_type;
+
+ SANE_Parameters params[2];
+
+ /* SCSI handling */
+ SANE_Byte *buffer0;
+ SANE_Byte *buffer; /* buffer = buffer0 + 12 */
+ /* for USB bulk transfer, a 12 bytes container
+ is required for each block */
+ /* Scanning handling. */
+ int scanning; /* TRUE if a scan is running. */
+ int current_page; /* the current page number, 0 is page 1 */
+ int current_side; /* the current side */
+ int bytes_to_read[2]; /* bytes to read */
+
+ /* --------------------------------------------------------------------- */
+ /* values used by the software enhancment code (deskew, crop, etc) */
+ SANE_Status deskew_stat;
+ int deskew_vals[2];
+ double deskew_slope;
+
+ SANE_Status crop_stat;
+ int crop_vals[4];
+
+ /* Support info */
+ KV_SUPPORT_INFO support_info;
+
+ SANE_Range x_range, y_range;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+ SANE_Bool option_set;
+
+ /* Image buffer */
+ SANE_Byte *img_buffers[2];
+ SANE_Byte *img_pt[2];
+ int img_size[2];
+} KV_DEV, *PKV_DEV;
+
+#define GET_OPT_VAL_W(dev, idx) ((dev)->val[idx].w)
+#define GET_OPT_VAL_L(dev, idx, token) get_optval_list(dev, idx, \
+ go_##token##_list, go_##token##_val)
+
+#define IS_DUPLEX(dev) GET_OPT_VAL_W(dev, OPT_DUPLEX)
+
+/* Prototypes in kvs1025_opt.c */
+
+int get_optval_list (const PKV_DEV dev, int idx,
+ const SANE_String_Const * str_list, const int *val_list);
+KV_SCAN_MODE kv_get_mode (const PKV_DEV dev);
+int kv_get_depth (KV_SCAN_MODE mode);
+
+void kv_calc_paper_size (const PKV_DEV dev, int *w, int *h);
+
+const SANE_Option_Descriptor *kv_get_option_descriptor (PKV_DEV dev,
+ SANE_Int option);
+void kv_init_options (PKV_DEV dev);
+SANE_Status kv_control_option (PKV_DEV dev, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info);
+void hexdump (int level, const char *comment, unsigned char *p, int l);
+void kv_set_window_data (PKV_DEV dev,
+ KV_SCAN_MODE scan_mode,
+ int side, unsigned char *windowdata);
+
+/* Prototypes in kvs1025_low.c */
+
+SANE_Status kv_enum_devices (void);
+void kv_get_devices_list (const SANE_Device *** devices_list);
+void kv_exit (void);
+SANE_Status kv_open (PKV_DEV dev);
+SANE_Bool kv_already_open (PKV_DEV dev);
+SANE_Status kv_open_by_name (SANE_String_Const devicename,
+ SANE_Handle * handle);
+void kv_close (PKV_DEV dev);
+SANE_Status kv_send_command (PKV_DEV dev,
+ PKV_CMD_HEADER header,
+ PKV_CMD_RESPONSE response);
+
+/* Commands */
+
+SANE_Status CMD_test_unit_ready (PKV_DEV dev, SANE_Bool * ready);
+SANE_Status CMD_read_support_info (PKV_DEV dev);
+SANE_Status CMD_scan (PKV_DEV dev);
+SANE_Status CMD_set_window (PKV_DEV dev, int side, PKV_CMD_RESPONSE rs);
+SANE_Status CMD_reset_window (PKV_DEV dev);
+SANE_Status CMD_get_buff_status (PKV_DEV dev, int *front_size,
+ int *back_size);
+SANE_Status CMD_wait_buff_status (PKV_DEV dev, int *front_size,
+ int *back_size);
+SANE_Status CMD_read_pic_elements (PKV_DEV dev, int page, int side,
+ int *width, int *height);
+SANE_Status CMD_read_image (PKV_DEV dev, int page, int side,
+ unsigned char *buffer, int *psize,
+ KV_CMD_RESPONSE * rs);
+SANE_Status CMD_wait_document_existanse (PKV_DEV dev);
+SANE_Status CMD_get_document_existanse (PKV_DEV dev);
+SANE_Status CMD_set_timeout (PKV_DEV dev, SANE_Word timeout);
+SANE_Status CMD_request_sense (PKV_DEV dev);
+/* Scan routines */
+
+SANE_Status AllocateImageBuffer (PKV_DEV dev);
+SANE_Status ReadImageDataSimplex (PKV_DEV dev, int page);
+SANE_Status ReadImageDataDuplex (PKV_DEV dev, int page);
+SANE_Status ReadImageData (PKV_DEV dev, int page);
+
+SANE_Status buffer_deskew (PKV_DEV dev, int side);
+SANE_Status buffer_crop (PKV_DEV dev, int side);
+SANE_Status buffer_despeck (PKV_DEV dev, int side);
+int buffer_isblank (PKV_DEV dev, int side);
+SANE_Status buffer_rotate(PKV_DEV dev, int side);
+
+#endif /* #ifndef __KVS1025_LOW_H */
diff --git a/backend/kvs1025_opt.c b/backend/kvs1025_opt.c
new file mode 100644
index 0000000..b7581f9
--- /dev/null
+++ b/backend/kvs1025_opt.c
@@ -0,0 +1,1581 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "kvs1025.h"
+#include "kvs1025_low.h"
+
+#include "../include/sane/sanei_debug.h"
+
+/* Option lists */
+
+static SANE_String_Const go_scan_mode_list[] = {
+ SANE_I18N ("bw"),
+ SANE_I18N ("halftone"),
+ SANE_I18N ("gray"),
+ SANE_I18N ("color"),
+ NULL
+};
+
+/*
+static int go_scan_mode_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x05
+};*/
+
+static const SANE_Word go_resolutions_list[] = {
+ 11, /* list size */
+ 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600
+};
+
+/* List of scan sources */
+static SANE_String_Const go_scan_source_list[] = {
+ SANE_I18N ("adf"),
+ SANE_I18N ("fb"),
+ NULL
+};
+static const int go_scan_source_val[] = {
+ 0,
+ 0x1
+};
+
+/* List of feeder modes */
+static SANE_String_Const go_feeder_mode_list[] = {
+ SANE_I18N ("single"),
+ SANE_I18N ("continuous"),
+ NULL
+};
+static const int go_feeder_mode_val[] = {
+ 0x00,
+ 0xff
+};
+
+/* List of manual feed mode */
+static SANE_String_Const go_manual_feed_list[] = {
+ SANE_I18N ("off"),
+ SANE_I18N ("wait_doc"),
+ SANE_I18N ("wait_key"),
+ NULL
+};
+static const int go_manual_feed_val[] = {
+ 0x00,
+ 0x01,
+ 0x02
+};
+
+/* List of paper sizes */
+static SANE_String_Const go_paper_list[] = {
+ SANE_I18N ("user_def"),
+ SANE_I18N ("business_card"),
+ SANE_I18N ("Check"),
+ /*SANE_I18N ("A3"), */
+ SANE_I18N ("A4"),
+ SANE_I18N ("A5"),
+ SANE_I18N ("A6"),
+ SANE_I18N ("Letter"),
+ /*SANE_I18N ("Double letter 11x17 in"),
+ SANE_I18N ("B4"), */
+ SANE_I18N ("B5"),
+ SANE_I18N ("B6"),
+ SANE_I18N ("Legal"),
+ NULL
+};
+static const int go_paper_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ /*0x03, *//* A3 : not supported */
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ /*0x09,
+ 0x0C, *//* Dbl letter and B4 : not supported */
+ 0x0D,
+ 0x0E,
+ 0x0F
+};
+
+static const KV_PAPER_SIZE go_paper_sizes[] = {
+ {210, 297}, /* User defined, default=A4 */
+ {54, 90}, /* Business card */
+ {80, 170}, /* Check (China business) */
+ /*{297, 420}, *//* A3 */
+ {210, 297}, /* A4 */
+ {148, 210}, /* A5 */
+ {105, 148}, /* A6 */
+ {216, 280}, /* US Letter 8.5 x 11 in */
+ /*{280, 432}, *//* Double Letter 11 x 17 in */
+ /*{250, 353}, *//* B4 */
+ {176, 250}, /* B5 */
+ {125, 176}, /* B6 */
+ {216, 356} /* US Legal */
+};
+
+static const int default_paper_size_idx = 3; /* A4 */
+static const int go_paper_max_width = 216; /* US letter */
+
+/* Lists of supported halftone. They are only valid with
+ * for the Black&White mode. */
+static SANE_String_Const go_halftone_pattern_list[] = {
+ SANE_I18N ("bayer_64"),
+ SANE_I18N ("bayer_16"),
+ SANE_I18N ("halftone_32"),
+ SANE_I18N ("halftone_64"),
+ SANE_I18N ("diffusion"),
+ NULL
+};
+static const int go_halftone_pattern_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04
+};
+
+/* List of automatic threshold options */
+static SANE_String_Const go_automatic_threshold_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("light"),
+ SANE_I18N ("dark"),
+ NULL
+};
+static const int go_automatic_threshold_val[] = {
+ 0,
+ 0x11,
+ 0x1f
+};
+
+/* List of white level base. */
+static SANE_String_Const go_white_level_list[] = {
+ SANE_I18N ("From scanner"),
+ SANE_I18N ("From paper"),
+ SANE_I18N ("Automatic"),
+ NULL
+};
+static const int go_white_level_val[] = {
+ 0x00,
+ 0x80,
+ 0x81
+};
+
+/* List of noise reduction options. */
+static SANE_String_Const go_noise_reduction_list[] = {
+ SANE_I18N ("default"),
+ "1x1",
+ "2x2",
+ "3x3",
+ "4x4",
+ "5x5",
+ NULL
+};
+static const int go_noise_reduction_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05
+};
+
+/* List of image emphasis options, 5 steps */
+static SANE_String_Const go_image_emphasis_list[] = {
+ SANE_I18N ("smooth"),
+ SANE_I18N ("none"),
+ SANE_I18N ("low"),
+ SANE_I18N ("medium"), /* default */
+ SANE_I18N ("high"),
+ NULL
+};
+static const int go_image_emphasis_val[] = {
+ 0x14,
+ 0x00,
+ 0x11,
+ 0x12,
+ 0x13
+};
+
+/* List of gamma */
+static SANE_String_Const go_gamma_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("crt"),
+ SANE_I18N ("linier"),
+ NULL
+};
+static const int go_gamma_val[] = {
+ 0x00,
+ 0x01,
+ 0x02
+};
+
+/* List of lamp color dropout */
+static SANE_String_Const go_lamp_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("red"),
+ SANE_I18N ("green"),
+ SANE_I18N ("blue"),
+ NULL
+};
+static const int go_lamp_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03
+};
+
+static SANE_Range go_value_range = { 0, 255, 0 };
+
+static SANE_Range go_jpeg_compression_range = { 0, 0x64, 0 };
+
+static SANE_Range go_rotate_range = { 0, 270, 90 };
+
+static SANE_Range go_swdespeck_range = { 0, 9, 1 };
+
+static SANE_Range go_swskip_range = { SANE_FIX(0), SANE_FIX(100), 1 };
+
+static const char *go_option_name[] = {
+ "OPT_NUM_OPTS",
+
+ /* General options */
+ "OPT_MODE_GROUP",
+ "OPT_MODE", /* scanner modes */
+ "OPT_RESOLUTION", /* X and Y resolution */
+ "OPT_DUPLEX", /* Duplex mode */
+ "OPT_SCAN_SOURCE", /* Scan source, fixed to ADF */
+ "OPT_FEEDER_MODE", /* Feeder mode, fixed to Continous */
+ "OPT_LONGPAPER", /* Long paper mode */
+ "OPT_LENGTHCTL", /* Length control mode */
+ "OPT_MANUALFEED", /* Manual feed mode */
+ "OPT_FEED_TIMEOUT", /* Feed timeout */
+ "OPT_DBLFEED", /* Double feed detection mode */
+ "OPT_FIT_TO_PAGE", /* Scanner shrinks image to fit scanned page */
+
+ /* Geometry group */
+ "OPT_GEOMETRY_GROUP",
+ "OPT_PAPER_SIZE", /* Paper size */
+ "OPT_LANDSCAPE", /* true if landscape */
+ "OPT_TL_X", /* upper left X */
+ "OPT_TL_Y", /* upper left Y */
+ "OPT_BR_X", /* bottom right X */
+ "OPT_BR_Y", /* bottom right Y */
+
+ "OPT_ENHANCEMENT_GROUP",
+ "OPT_BRIGHTNESS", /* Brightness */
+ "OPT_CONTRAST", /* Contrast */
+ "OPT_AUTOMATIC_THRESHOLD", /* Binary threshold */
+ "OPT_HALFTONE_PATTERN", /* Halftone pattern */
+ "OPT_AUTOMATIC_SEPARATION", /* Automatic separation */
+ "OPT_WHITE_LEVEL", /* White level */
+ "OPT_NOISE_REDUCTION", /* Noise reduction */
+ "OPT_IMAGE_EMPHASIS", /* Image emphasis */
+ "OPT_GAMMA", /* Gamma */
+ "OPT_LAMP", /* Lamp -- color drop out */
+ "OPT_INVERSE", /* Inverse image */
+ "OPT_MIRROR", /* Mirror image */
+ "OPT_JPEG", /* JPEG Compression */
+ "OPT_ROTATE", /* Rotate image */
+
+ "OPT_SWDESKEW", /* Software deskew */
+ "OPT_SWDESPECK", /* Software despeckle */
+ "OPT_SWDEROTATE", /* Software detect/correct 90 deg. rotation */
+ "OPT_SWCROP", /* Software autocrop */
+ "OPT_SWSKIP", /* Software blank page skip */
+
+ /* must come last: */
+ "OPT_NUM_OPTIONS"
+};
+
+
+/* Round to boundry, return 1 if value modified */
+static int
+round_to_boundry (SANE_Word * pval, SANE_Word boundry,
+ SANE_Word minv, SANE_Word maxv)
+{
+ SANE_Word lower, upper, k, v;
+
+ v = *pval;
+ k = v / boundry;
+ lower = k * boundry;
+ upper = (k + 1) * boundry;
+
+ if (v - lower <= upper - v)
+ {
+ *pval = lower;
+ }
+ else
+ {
+ *pval = upper;
+ }
+
+ if ((*pval) < minv)
+ *pval = minv;
+ if ((*pval) > maxv)
+ *pval = maxv;
+
+ return ((*pval) != v);
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const * strings)
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+get_string_list_index (const SANE_String_Const * list, SANE_String_Const name)
+{
+ int index;
+
+ index = 0;
+ while (list[index] != NULL)
+ {
+ if (strcmp (list[index], name) == 0)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "System bug: option %s not found in list\n", name);
+
+ return (-1); /* not found */
+}
+
+
+/* Lookup a string list from one array and return the correnpond value. */
+int
+get_optval_list (const PKV_DEV dev, int idx,
+ const SANE_String_Const * str_list, const int *val_list)
+{
+ int index;
+
+ index = get_string_list_index (str_list, dev->val[idx].s);
+
+ if (index < 0)
+ index = 0;
+
+ return val_list[index];
+}
+
+
+/* Get device mode from device options */
+KV_SCAN_MODE
+kv_get_mode (const PKV_DEV dev)
+{
+ int i;
+
+ i = get_string_list_index (go_scan_mode_list, dev->val[OPT_MODE].s);
+
+ switch (i)
+ {
+ case 0:
+ return SM_BINARY;
+ case 1:
+ return SM_DITHER;
+ case 2:
+ return SM_GRAYSCALE;
+ case 3:
+ return SM_COLOR;
+ default:
+ assert (0 == 1);
+ return 0;
+ }
+}
+
+void
+kv_calc_paper_size (const PKV_DEV dev, int *w, int *h)
+{
+ int i = get_string_list_index (go_paper_list,
+ dev->val[OPT_PAPER_SIZE].s);
+ if (i == 0)
+ { /* Non-standard document */
+ int x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ int y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ int x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ int y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ *w = x_br - x_tl;
+ *h = y_br - y_tl;
+ }
+ else
+ {
+ if (dev->val[OPT_LANDSCAPE].s)
+ {
+ *h = mmToIlu (go_paper_sizes[i].width);
+ *w = mmToIlu (go_paper_sizes[i].height);
+ }
+ else
+ {
+ *w = mmToIlu (go_paper_sizes[i].width);
+ *h = mmToIlu (go_paper_sizes[i].height);
+ }
+ }
+}
+
+/* Get bit depth from scan mode */
+int
+kv_get_depth (KV_SCAN_MODE mode)
+{
+ switch (mode)
+ {
+ case SM_BINARY:
+ case SM_DITHER:
+ return 1;
+ case SM_GRAYSCALE:
+ return 8;
+ case SM_COLOR:
+ return 24;
+ default:
+ assert (0 == 1);
+ return 0;
+ }
+}
+
+const SANE_Option_Descriptor *
+kv_get_option_descriptor (PKV_DEV dev, SANE_Int option)
+{
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %s\n",
+ go_option_name[option]);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+/* Reset the options for that scanner. */
+void
+kv_init_options (PKV_DEV dev)
+{
+ int i;
+
+ if (dev->option_set)
+ return;
+
+ DBG (DBG_proc, "kv_init_options: enter\n");
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (go_scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = go_scan_mode_list;
+ dev->val[OPT_MODE].s = strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list = go_resolutions_list;
+ dev->val[OPT_RESOLUTION].w = go_resolutions_list[3];
+
+ /* Duplex */
+ dev->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
+ dev->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
+ dev->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
+ dev->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_DUPLEX].unit = SANE_UNIT_NONE;
+ dev->val[OPT_DUPLEX].w = SANE_FALSE;
+ if (!dev->support_info.support_duplex)
+ dev->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+
+ /* Scan source */
+ dev->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ dev->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ dev->opt[OPT_SCAN_SOURCE].desc = SANE_I18N ("Sets the scan source");
+ dev->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_SCAN_SOURCE].size = max_string_size (go_scan_source_list);
+ dev->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_SCAN_SOURCE].constraint.string_list = go_scan_source_list;
+ dev->val[OPT_SCAN_SOURCE].s = strdup (go_scan_source_list[0]);
+ dev->opt[OPT_SCAN_SOURCE].cap &= ~SANE_CAP_SOFT_SELECT;
+ /* for KV-S1020C / KV-S1025C, scan source is fixed to ADF */
+
+ /* Feeder mode */
+ dev->opt[OPT_FEEDER_MODE].name = "feeder-mode";
+ dev->opt[OPT_FEEDER_MODE].title = SANE_I18N ("Feeder mode");
+ dev->opt[OPT_FEEDER_MODE].desc = SANE_I18N ("Sets the feeding mode");
+ dev->opt[OPT_FEEDER_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_FEEDER_MODE].size = max_string_size (go_feeder_mode_list);
+ dev->opt[OPT_FEEDER_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_FEEDER_MODE].constraint.string_list = go_feeder_mode_list;
+ dev->val[OPT_FEEDER_MODE].s = strdup (go_feeder_mode_list[1]);
+
+ /* Long paper */
+ dev->opt[OPT_LONGPAPER].name = SANE_NAME_LONGPAPER;
+ dev->opt[OPT_LONGPAPER].title = SANE_TITLE_LONGPAPER;
+ dev->opt[OPT_LONGPAPER].desc = SANE_I18N ("Enable/Disable long paper mode");
+ dev->opt[OPT_LONGPAPER].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_LONGPAPER].unit = SANE_UNIT_NONE;
+ dev->val[OPT_LONGPAPER].w = SANE_FALSE;
+
+ /* Length control */
+ dev->opt[OPT_LENGTHCTL].name = SANE_NAME_LENGTHCTL;
+ dev->opt[OPT_LENGTHCTL].title = SANE_TITLE_LENGTHCTL;
+ dev->opt[OPT_LENGTHCTL].desc =
+ SANE_I18N ("Enable/Disable length control mode");
+ dev->opt[OPT_LENGTHCTL].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_LENGTHCTL].unit = SANE_UNIT_NONE;
+ dev->val[OPT_LENGTHCTL].w = SANE_TRUE;
+
+ /* Manual feed */
+ dev->opt[OPT_MANUALFEED].name = SANE_NAME_MANUALFEED;
+ dev->opt[OPT_MANUALFEED].title = SANE_TITLE_MANUALFEED;
+ dev->opt[OPT_MANUALFEED].desc = SANE_I18N ("Sets the manual feed mode");
+ dev->opt[OPT_MANUALFEED].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MANUALFEED].size = max_string_size (go_manual_feed_list);
+ dev->opt[OPT_MANUALFEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MANUALFEED].constraint.string_list = go_manual_feed_list;
+ dev->val[OPT_MANUALFEED].s = strdup (go_manual_feed_list[0]);
+
+ /*Manual feed timeout */
+ dev->opt[OPT_FEED_TIMEOUT].name = SANE_NAME_FEED_TIMEOUT;
+ dev->opt[OPT_FEED_TIMEOUT].title = SANE_TITLE_FEED_TIMEOUT;
+ dev->opt[OPT_FEED_TIMEOUT].desc =
+ SANE_I18N ("Sets the manual feed timeout in seconds");
+ dev->opt[OPT_FEED_TIMEOUT].type = SANE_TYPE_INT;
+ dev->opt[OPT_FEED_TIMEOUT].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_FEED_TIMEOUT].size = sizeof (SANE_Int);
+ dev->opt[OPT_FEED_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_FEED_TIMEOUT].constraint.range = &(go_value_range);
+ dev->opt[OPT_FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_FEED_TIMEOUT].w = 30;
+
+ /* Double feed */
+ dev->opt[OPT_DBLFEED].name = SANE_NAME_DBLFEED;
+ dev->opt[OPT_DBLFEED].title = SANE_TITLE_DBLFEED;
+ dev->opt[OPT_DBLFEED].desc =
+ SANE_I18N ("Enable/Disable double feed detection");
+ dev->opt[OPT_DBLFEED].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_DBLFEED].unit = SANE_UNIT_NONE;
+ dev->val[OPT_DBLFEED].w = SANE_FALSE;
+
+ /* Fit to page */
+ dev->opt[OPT_FIT_TO_PAGE].name = SANE_I18N ("fit-to-page");
+ dev->opt[OPT_FIT_TO_PAGE].title = SANE_I18N ("Fit to page");
+ dev->opt[OPT_FIT_TO_PAGE].desc =
+ SANE_I18N ("Scanner shrinks image to fit scanned page");
+ dev->opt[OPT_FIT_TO_PAGE].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_FIT_TO_PAGE].unit = SANE_UNIT_NONE;
+ dev->val[OPT_FIT_TO_PAGE].w = SANE_FALSE;
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Paper sizes list */
+ dev->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
+ dev->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
+ dev->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
+ dev->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_PAPER_SIZE].size = max_string_size (go_paper_list);
+ dev->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_PAPER_SIZE].constraint.string_list = go_paper_list;
+ dev->val[OPT_PAPER_SIZE].s = strdup (""); /* will be set later */
+
+ /* Landscape */
+ dev->opt[OPT_LANDSCAPE].name = SANE_NAME_LANDSCAPE;
+ dev->opt[OPT_LANDSCAPE].title = SANE_TITLE_LANDSCAPE;
+ dev->opt[OPT_LANDSCAPE].desc =
+ SANE_I18N ("Set paper position : "
+ "true for landscape, false for portrait");
+ dev->opt[OPT_LANDSCAPE].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_LANDSCAPE].unit = SANE_UNIT_NONE;
+ dev->val[OPT_LANDSCAPE].w = SANE_FALSE;
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &(dev->x_range);
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &(dev->y_range);
+
+ /* Bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &(dev->x_range);
+
+ /* Bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &(dev->y_range);
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Brightness */
+ dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int);
+ dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BRIGHTNESS].constraint.range = &(go_value_range);
+ dev->val[OPT_BRIGHTNESS].w = 128;
+
+ /* Contrast */
+ dev->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ dev->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ dev->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ dev->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ dev->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_CONTRAST].size = sizeof (SANE_Int);
+ dev->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_CONTRAST].constraint.range = &(go_value_range);
+ dev->val[OPT_CONTRAST].w = 128;
+
+ /* Automatic threshold */
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].name = "automatic-threshold";
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].title = SANE_I18N ("Automatic threshold");
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].desc =
+ SANE_I18N
+ ("Automatically sets brightness, contrast, white level, "
+ "gamma, noise reduction and image emphasis");
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].type = SANE_TYPE_STRING;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].size =
+ max_string_size (go_automatic_threshold_list);
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint.string_list =
+ go_automatic_threshold_list;
+ dev->val[OPT_AUTOMATIC_THRESHOLD].s =
+ strdup (go_automatic_threshold_list[0]);
+
+ /* Halftone pattern */
+ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ dev->opt[OPT_HALFTONE_PATTERN].size =
+ max_string_size (go_halftone_pattern_list);
+ dev->opt[OPT_HALFTONE_PATTERN].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
+ go_halftone_pattern_list;
+ dev->val[OPT_HALFTONE_PATTERN].s = strdup (go_halftone_pattern_list[0]);
+
+ /* Automatic separation */
+ dev->opt[OPT_AUTOMATIC_SEPARATION].name = SANE_NAME_AUTOSEP;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].title = SANE_TITLE_AUTOSEP;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].desc = SANE_DESC_AUTOSEP;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].unit = SANE_UNIT_NONE;
+ dev->val[OPT_AUTOMATIC_SEPARATION].w = SANE_FALSE;
+
+ /* White level base */
+ dev->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
+ dev->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
+ dev->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL;
+ dev->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_STRING;
+ dev->opt[OPT_WHITE_LEVEL].size = max_string_size (go_white_level_list);
+ dev->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_WHITE_LEVEL].constraint.string_list = go_white_level_list;
+ dev->val[OPT_WHITE_LEVEL].s = strdup (go_white_level_list[0]);
+
+ /* Noise reduction */
+ dev->opt[OPT_NOISE_REDUCTION].name = "noise-reduction";
+ dev->opt[OPT_NOISE_REDUCTION].title = SANE_I18N ("Noise reduction");
+ dev->opt[OPT_NOISE_REDUCTION].desc =
+ SANE_I18N ("Reduce the isolated dot noise");
+ dev->opt[OPT_NOISE_REDUCTION].type = SANE_TYPE_STRING;
+ dev->opt[OPT_NOISE_REDUCTION].size =
+ max_string_size (go_noise_reduction_list);
+ dev->opt[OPT_NOISE_REDUCTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_NOISE_REDUCTION].constraint.string_list =
+ go_noise_reduction_list;
+ dev->val[OPT_NOISE_REDUCTION].s = strdup (go_noise_reduction_list[0]);
+
+ /* Image emphasis */
+ dev->opt[OPT_IMAGE_EMPHASIS].name = "image-emphasis";
+ dev->opt[OPT_IMAGE_EMPHASIS].title = SANE_I18N ("Image emphasis");
+ dev->opt[OPT_IMAGE_EMPHASIS].desc = SANE_I18N ("Sets the image emphasis");
+ dev->opt[OPT_IMAGE_EMPHASIS].type = SANE_TYPE_STRING;
+ dev->opt[OPT_IMAGE_EMPHASIS].size =
+ max_string_size (go_image_emphasis_list);
+ dev->opt[OPT_IMAGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_IMAGE_EMPHASIS].constraint.string_list =
+ go_image_emphasis_list;
+ dev->val[OPT_IMAGE_EMPHASIS].s = strdup (SANE_I18N ("medium"));
+
+ /* Gamma */
+ dev->opt[OPT_GAMMA].name = "gamma";
+ dev->opt[OPT_GAMMA].title = SANE_I18N ("Gamma");
+ dev->opt[OPT_GAMMA].desc = SANE_I18N ("Gamma");
+ dev->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ dev->opt[OPT_GAMMA].size = max_string_size (go_gamma_list);
+ dev->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_GAMMA].constraint.string_list = go_gamma_list;
+ dev->val[OPT_GAMMA].s = strdup (go_gamma_list[0]);
+
+ /* Lamp color dropout */
+ dev->opt[OPT_LAMP].name = "lamp-color";
+ dev->opt[OPT_LAMP].title = SANE_I18N ("Lamp color");
+ dev->opt[OPT_LAMP].desc = SANE_I18N ("Sets the lamp color (color dropout)");
+ dev->opt[OPT_LAMP].type = SANE_TYPE_STRING;
+ dev->opt[OPT_LAMP].size = max_string_size (go_lamp_list);
+ dev->opt[OPT_LAMP].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_LAMP].constraint.string_list = go_lamp_list;
+ dev->val[OPT_LAMP].s = strdup (go_lamp_list[0]);
+ if (!dev->support_info.support_lamp)
+ dev->opt[OPT_LAMP].cap |= SANE_CAP_INACTIVE;
+
+ /* Inverse image */
+ dev->opt[OPT_INVERSE].name = SANE_NAME_INVERSE;
+ dev->opt[OPT_INVERSE].title = SANE_TITLE_INVERSE;
+ dev->opt[OPT_INVERSE].desc =
+ SANE_I18N ("Inverse image in B/W or halftone mode");
+ dev->opt[OPT_INVERSE].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_INVERSE].unit = SANE_UNIT_NONE;
+ dev->val[OPT_INVERSE].w = SANE_FALSE;
+
+ /* Mirror image (left/right flip) */
+ dev->opt[OPT_MIRROR].name = SANE_NAME_MIRROR;
+ dev->opt[OPT_MIRROR].title = SANE_TITLE_MIRROR;
+ dev->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror image (left/right flip)");
+ dev->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_MIRROR].unit = SANE_UNIT_NONE;
+ dev->val[OPT_MIRROR].w = SANE_FALSE;
+
+ /* JPEG Image Compression */
+ dev->opt[OPT_JPEG].name = "jpeg";
+ dev->opt[OPT_JPEG].title = SANE_I18N ("jpeg compression");
+ dev->opt[OPT_JPEG].desc =
+ SANE_I18N
+ ("JPEG Image Compression with Q parameter, '0' - no compression");
+ dev->opt[OPT_JPEG].type = SANE_TYPE_INT;
+ dev->opt[OPT_JPEG].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_JPEG].size = sizeof (SANE_Int);
+ dev->opt[OPT_JPEG].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_JPEG].constraint.range = &(go_jpeg_compression_range);
+ dev->val[OPT_JPEG].w = 0;
+
+ /* Image Rotation */
+ dev->opt[OPT_ROTATE].name = "rotate";
+ dev->opt[OPT_ROTATE].title = SANE_I18N ("Rotate image clockwise");
+ dev->opt[OPT_ROTATE].desc =
+ SANE_I18N("Request driver to rotate pages by a fixed amount");
+ dev->opt[OPT_ROTATE].type = SANE_TYPE_INT;
+ dev->opt[OPT_ROTATE].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_ROTATE].size = sizeof (SANE_Int);
+ dev->opt[OPT_ROTATE].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_ROTATE].constraint.range = &(go_rotate_range);
+ dev->val[OPT_ROTATE].w = 0;
+
+ /* Software Deskew */
+ dev->opt[OPT_SWDESKEW].name = "swdeskew";
+ dev->opt[OPT_SWDESKEW].title = SANE_I18N ("Software deskew");
+ dev->opt[OPT_SWDESKEW].desc =
+ SANE_I18N("Request driver to rotate skewed pages digitally");
+ dev->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_SWDESKEW].unit = SANE_UNIT_NONE;
+ dev->val[OPT_SWDESKEW].w = SANE_FALSE;
+
+ /* Software Despeckle */
+ dev->opt[OPT_SWDESPECK].name = "swdespeck";
+ dev->opt[OPT_SWDESPECK].title = SANE_I18N ("Software despeckle diameter");
+ dev->opt[OPT_SWDESPECK].desc =
+ SANE_I18N("Maximum diameter of lone dots to remove from scan");
+ dev->opt[OPT_SWDESPECK].type = SANE_TYPE_INT;
+ dev->opt[OPT_SWDESPECK].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_SWDESPECK].size = sizeof (SANE_Int);
+ dev->opt[OPT_SWDESPECK].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_SWDESPECK].constraint.range = &(go_swdespeck_range);
+ dev->val[OPT_SWDESPECK].w = 0;
+
+ /* Software Derotate */
+ dev->opt[OPT_SWDEROTATE].name = "swderotate";
+ dev->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate");
+ dev->opt[OPT_SWDEROTATE].desc =
+ SANE_I18N("Request driver to detect and correct 90 degree image rotation");
+ dev->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE;
+ dev->val[OPT_SWDEROTATE].w = SANE_FALSE;
+
+ /* Software Autocrop*/
+ dev->opt[OPT_SWCROP].name = "swcrop";
+ dev->opt[OPT_SWCROP].title = SANE_I18N ("Software automatic cropping");
+ dev->opt[OPT_SWCROP].desc =
+ SANE_I18N("Request driver to remove border from pages digitally");
+ dev->opt[OPT_SWCROP].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_SWCROP].unit = SANE_UNIT_NONE;
+ dev->val[OPT_SWCROP].w = SANE_FALSE;
+
+ /* Software blank page skip */
+ dev->opt[OPT_SWSKIP].name = "swskip";
+ dev->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage");
+ dev->opt[OPT_SWSKIP].desc
+ = SANE_I18N("Request driver to discard pages with low numbers of dark pixels");
+ dev->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT;
+ dev->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_SWSKIP].constraint.range = &(go_swskip_range);
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+ sane_control_option (dev, OPT_PAPER_SIZE, SANE_ACTION_SET_VALUE,
+ (void *) go_paper_list[default_paper_size_idx], NULL);
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (void *) go_scan_mode_list[0], NULL);
+
+ DBG (DBG_proc, "kv_init_options: exit\n");
+
+ dev->option_set = 1;
+}
+
+
+SANE_Status
+kv_control_option (PKV_DEV dev, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+ int i;
+ SANE_Word value;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %s, action %s\n",
+ go_option_name[option], action == SANE_ACTION_GET_VALUE ? "R" : "W");
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ name = dev->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_LONGPAPER:
+ case OPT_LENGTHCTL:
+ case OPT_DBLFEED:
+ case OPT_RESOLUTION:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_DUPLEX:
+ case OPT_LANDSCAPE:
+ case OPT_AUTOMATIC_SEPARATION:
+ case OPT_INVERSE:
+ case OPT_MIRROR:
+ case OPT_FEED_TIMEOUT:
+ case OPT_JPEG:
+ case OPT_ROTATE:
+ case OPT_SWDESKEW:
+ case OPT_SWDESPECK:
+ case OPT_SWDEROTATE:
+ case OPT_SWCROP:
+ case OPT_SWSKIP:
+ case OPT_FIT_TO_PAGE:
+ *(SANE_Word *) val = dev->val[option].w;
+ DBG (DBG_error, "opt value = %d\n", *(SANE_Word *) val);
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ case OPT_FEEDER_MODE:
+ case OPT_SCAN_SOURCE:
+ case OPT_MANUALFEED:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_PAPER_SIZE:
+ case OPT_AUTOMATIC_THRESHOLD:
+ case OPT_WHITE_LEVEL:
+ case OPT_NOISE_REDUCTION:
+ case OPT_IMAGE_EMPHASIS:
+ case OPT_GAMMA:
+ case OPT_LAMP:
+
+ strcpy (val, dev->val[option].s);
+ DBG (DBG_error, "opt value = %s\n", (char *) val);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error,
+ "could not set option %s, not settable\n",
+ go_option_name[option]);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ /* Side-effect options */
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_RESOLUTION:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+
+ dev->val[option].w = *(SANE_Word *) val;
+
+ if (option == OPT_RESOLUTION)
+ {
+ if (round_to_boundry (&(dev->val[option].w),
+ dev->support_info.
+ step_resolution, 100, 600))
+ {
+ if (info)
+ {
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+ }
+ else if (option == OPT_TL_Y)
+ {
+ if (dev->val[option].w > dev->val[OPT_BR_Y].w)
+ {
+ dev->val[option].w = dev->val[OPT_BR_Y].w;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ }
+ }
+ }
+ else
+ {
+ if (dev->val[option].w < dev->val[OPT_TL_Y].w)
+ {
+ dev->val[option].w = dev->val[OPT_TL_Y].w;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ }
+ }
+ }
+
+ DBG (DBG_error,
+ "option %s, input = %d, value = %d\n",
+ go_option_name[option], (*(SANE_Word *) val),
+ dev->val[option].w);
+
+ return SANE_STATUS_GOOD;
+
+ /* The length of X must be rounded (up). */
+ case OPT_TL_X:
+ case OPT_BR_X:
+ {
+ SANE_Word xr = dev->val[OPT_RESOLUTION].w;
+ SANE_Word tl_x = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)) * xr;
+ SANE_Word br_x = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)) * xr;
+ value = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)) * xr; /* XR * W */
+
+ if (option == OPT_TL_X)
+ {
+ SANE_Word max = KV_PIXEL_MAX * xr - KV_PIXEL_ROUND;
+ if (br_x < max)
+ max = br_x;
+ if (round_to_boundry (&value, KV_PIXEL_ROUND, 0, max))
+ {
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ }
+ }
+ }
+ else
+ {
+ if (round_to_boundry
+ (&value, KV_PIXEL_ROUND, tl_x, KV_PIXEL_MAX * xr))
+ {
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ }
+ }
+ }
+
+ dev->val[option].w = SANE_FIX (iluToMm ((double) value / xr));
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+
+ DBG (DBG_error,
+ "option %s, input = %d, value = %d\n",
+ go_option_name[option], (*(SANE_Word *) val),
+ dev->val[option].w);
+ return SANE_STATUS_GOOD;
+ }
+ case OPT_LANDSCAPE:
+ dev->val[option].w = *(SANE_Word *) val;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ /* Side-effect free options */
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ case OPT_DUPLEX:
+ case OPT_LONGPAPER:
+ case OPT_LENGTHCTL:
+ case OPT_DBLFEED:
+ case OPT_INVERSE:
+ case OPT_MIRROR:
+ case OPT_AUTOMATIC_SEPARATION:
+ case OPT_JPEG:
+ case OPT_ROTATE:
+ case OPT_SWDESKEW:
+ case OPT_SWDESPECK:
+ case OPT_SWDEROTATE:
+ case OPT_SWCROP:
+ case OPT_SWSKIP:
+ case OPT_FIT_TO_PAGE:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_FEED_TIMEOUT:
+ dev->val[option].w = *(SANE_Word *) val;
+ return CMD_set_timeout (dev, *(SANE_Word *) val);
+
+ /* String mode */
+ case OPT_SCAN_SOURCE:
+ case OPT_WHITE_LEVEL:
+ case OPT_NOISE_REDUCTION:
+ case OPT_IMAGE_EMPHASIS:
+ case OPT_GAMMA:
+ case OPT_LAMP:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_FEEDER_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+
+ if (option == OPT_FEEDER_MODE &&
+ get_string_list_index (go_feeder_mode_list,
+ dev->val[option].s) == 1)
+ /* continuous mode */
+ {
+ free (dev->val[OPT_SCAN_SOURCE].s);
+ dev->val[OPT_SCAN_SOURCE].s = strdup (go_scan_source_list[0]);
+ dev->opt[OPT_LONGPAPER].cap &= ~SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ else
+ {
+ dev->opt[OPT_LONGPAPER].cap |= SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ if (option == OPT_SCAN_SOURCE &&
+ get_string_list_index (go_scan_source_list,
+ dev->val[option].s) == 1)
+ /* flatbed */
+ {
+ free (dev->val[OPT_FEEDER_MODE].s);
+ dev->val[OPT_FEEDER_MODE].s = strdup (go_feeder_mode_list[0]);
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_String) strdup (val);
+
+ /* Set default options for the scan modes. */
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_INVERSE].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_JPEG].cap &= ~SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[0]) == 0)
+ /* binary */
+ {
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_INVERSE].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[1]) == 0)
+ /* halftone */
+ {
+ dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_INVERSE].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[2]) == 0)
+ /* grayscale */
+ {
+ dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MANUALFEED:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+
+ if (strcmp (dev->val[option].s, go_manual_feed_list[0]) == 0) /* off */
+ dev->opt[OPT_FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE;
+ else
+ dev->opt[OPT_FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAPER_SIZE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_PAPER_SIZE].s);
+ dev->val[OPT_PAPER_SIZE].s = (SANE_Char *) strdup (val);
+
+ i = get_string_list_index (go_paper_list,
+ dev->val[OPT_PAPER_SIZE].s);
+ if (i == 0)
+ { /*user def */
+ dev->opt[OPT_TL_X].cap &=
+ dev->opt[OPT_TL_Y].cap &=
+ dev->opt[OPT_BR_X].cap &=
+ dev->opt[OPT_BR_Y].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_LANDSCAPE].w = 0;
+ }
+ else
+ {
+ dev->opt[OPT_TL_X].cap |=
+ dev->opt[OPT_TL_Y].cap |=
+ dev->opt[OPT_BR_X].cap |=
+ dev->opt[OPT_BR_Y].cap |= SANE_CAP_INACTIVE;
+ if (i == 4 || i == 5 || i == 7)
+ { /*A5, A6 or B6 */
+ dev->opt[OPT_LANDSCAPE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ dev->opt[OPT_LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_LANDSCAPE].w = 0;
+ }
+ }
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_AUTOMATIC_THRESHOLD:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_Char *) strdup (val);
+
+ /* If the threshold is not set to none, some option must
+ * disappear. */
+
+ dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_IMAGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (val, go_automatic_threshold_list[0]) == 0)
+ {
+ dev->opt[OPT_WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_IMAGE_EMPHASIS].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE;
+ if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[1]) == 0)
+ {
+ dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* Display a buffer in the log. */
+void
+hexdump (int level, const char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/* Set window data */
+void
+kv_set_window_data (PKV_DEV dev,
+ KV_SCAN_MODE scan_mode,
+ int side, unsigned char *windowdata)
+{
+ int paper = go_paper_val[get_string_list_index (go_paper_list,
+ dev->val[OPT_PAPER_SIZE].
+ s)];
+
+ /* Page side */
+ windowdata[0] = side;
+
+ /* X and Y resolution */
+ Ito16 (dev->val[OPT_RESOLUTION].w, &windowdata[2]);
+ Ito16 (dev->val[OPT_RESOLUTION].w, &windowdata[4]);
+
+ /* Width and length */
+ if (paper == 0)
+ { /* Non-standard document */
+ int x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ int y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ int x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ int y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ int width = x_br - x_tl;
+ int length = y_br - y_tl;
+ /* Upper Left (X,Y) */
+ Ito32 (x_tl, &windowdata[6]);
+ Ito32 (y_tl, &windowdata[10]);
+
+ Ito32 (width, &windowdata[14]);
+ Ito32 (length, &windowdata[18]);
+ Ito32 (width, &windowdata[48]); /* device specific */
+ Ito32 (length, &windowdata[52]); /* device specific */
+ }
+
+ /* Brightness */
+ windowdata[22] = 255 - GET_OPT_VAL_W (dev, OPT_BRIGHTNESS);
+ windowdata[23] = windowdata[22]; /* threshold, same as brightness. */
+
+ /* Contrast */
+ windowdata[24] = GET_OPT_VAL_W (dev, OPT_CONTRAST);
+
+ /* Image Composition */
+ windowdata[25] = (unsigned char) scan_mode;
+
+ /* Depth */
+ windowdata[26] = kv_get_depth (scan_mode);
+
+ /* Halftone pattern. */
+ if (scan_mode == SM_DITHER)
+ {
+ windowdata[28] = GET_OPT_VAL_L (dev, OPT_HALFTONE_PATTERN,
+ halftone_pattern);
+ }
+
+ /* Inverse */
+ if (scan_mode == SM_BINARY || scan_mode == SM_DITHER)
+ {
+ windowdata[29] = GET_OPT_VAL_W (dev, OPT_INVERSE);
+ }
+
+ /* Bit ordering */
+ windowdata[31] = 1;
+
+ /*Compression Type */
+ if (!(dev->opt[OPT_JPEG].cap & SANE_CAP_INACTIVE)
+ && GET_OPT_VAL_W (dev, OPT_JPEG))
+ {
+ windowdata[32] = 0x81; /*jpeg */
+ /*Compression Argument */
+ windowdata[33] = GET_OPT_VAL_W (dev, OPT_JPEG);
+ }
+
+ /* Gamma */
+ if (scan_mode == SM_DITHER || scan_mode == SM_GRAYSCALE)
+ {
+ windowdata[44] = GET_OPT_VAL_L (dev, OPT_GAMMA, gamma);
+ }
+
+ /* Feeder mode */
+ windowdata[57] = GET_OPT_VAL_L (dev, OPT_FEEDER_MODE, feeder_mode);
+
+ /* Stop skew -- disabled */
+ windowdata[41] = 0;
+
+ /* Scan source */
+ if (GET_OPT_VAL_L (dev, OPT_SCAN_SOURCE, scan_source))
+ { /* flatbed */
+ windowdata[41] |= 0x80;
+ }
+ else
+ {
+ windowdata[41] &= 0x7f;
+ }
+
+ /* Paper size */
+ windowdata[47] = paper;
+
+ if (paper) /* Standard Document */
+ windowdata[47] |= 1 << 7;
+
+ /* Long paper */
+ if (GET_OPT_VAL_W (dev, OPT_LONGPAPER))
+ {
+ windowdata[47] |= 0x20;
+ }
+
+ /* Length control */
+ if (GET_OPT_VAL_W (dev, OPT_LENGTHCTL))
+ {
+ windowdata[47] |= 0x40;
+ }
+
+ /* Landscape */
+ if (GET_OPT_VAL_W (dev, OPT_LANDSCAPE))
+ {
+ windowdata[47] |= 1 << 4;
+ }
+ /* Double feed */
+ if (GET_OPT_VAL_W (dev, OPT_DBLFEED))
+ {
+ windowdata[56] = 0x10;
+ }
+
+ /* Fit to page */
+ if (GET_OPT_VAL_W (dev, OPT_FIT_TO_PAGE))
+ {
+ windowdata[56] |= 1 << 2;
+ }
+
+ /* Manual feed */
+ windowdata[62] = GET_OPT_VAL_L (dev, OPT_MANUALFEED, manual_feed) << 6;
+
+ /* Mirror image */
+ if (GET_OPT_VAL_W (dev, OPT_MIRROR))
+ {
+ windowdata[42] = 0x80;
+ }
+
+ /* Image emphasis */
+ windowdata[43] = GET_OPT_VAL_L (dev, OPT_IMAGE_EMPHASIS, image_emphasis);
+
+ /* White level */
+ windowdata[60] = GET_OPT_VAL_L (dev, OPT_WHITE_LEVEL, white_level);
+
+ if (scan_mode == SM_BINARY || scan_mode == SM_DITHER)
+ {
+ /* Noise reduction */
+ windowdata[61] = GET_OPT_VAL_L (dev, OPT_NOISE_REDUCTION,
+ noise_reduction);
+
+ /* Automatic separation */
+ if (scan_mode == SM_DITHER && GET_OPT_VAL_W (dev,
+ OPT_AUTOMATIC_SEPARATION))
+ {
+ windowdata[59] = 0x80;
+ }
+ }
+
+ /* Automatic threshold. Must be last because it may override
+ * some previous options. */
+ if (scan_mode == SM_BINARY)
+ {
+ windowdata[58] =
+ GET_OPT_VAL_L (dev, OPT_AUTOMATIC_THRESHOLD, automatic_threshold);
+ }
+
+ if (windowdata[58] != 0)
+ {
+ /* Automatic threshold is enabled. */
+ windowdata[22] = 0; /* brightness. */
+ windowdata[23] = 0; /* threshold, same as brightness. */
+ windowdata[24] = 0; /* contrast */
+ windowdata[27] = windowdata[28] = 0; /* Halftone pattern. */
+ windowdata[43] = 0; /* Image emphasis */
+ windowdata[59] = 0; /* Automatic separation */
+ windowdata[60] = 0; /* White level */
+ windowdata[61] = 0; /* Noise reduction */
+ }
+
+ /* lamp -- color dropout */
+ windowdata[45] = GET_OPT_VAL_L (dev, OPT_LAMP, lamp) << 4;
+
+ /*Stop Mode: After 1 page */
+ windowdata[63] = 1;
+}
diff --git a/backend/kvs1025_usb.c b/backend/kvs1025_usb.c
new file mode 100644
index 0000000..90ce4d0
--- /dev/null
+++ b/backend/kvs1025_usb.c
@@ -0,0 +1,370 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "kvs1025.h"
+#include "kvs1025_low.h"
+#include "kvs1025_usb.h"
+#include "kvs1025_cmds.h"
+
+#include "../include/sane/sanei_debug.h"
+
+extern PKV_DEV g_devices; /* Chain of devices */
+extern const SANE_Device **g_devlist;
+
+/* static functions */
+
+/* Attach USB scanner */
+static SANE_Status
+attach_scanner_usb (const char *device_name)
+{
+ PKV_DEV dev;
+ SANE_Word vendor, product;
+
+ DBG (DBG_error, "attaching USB scanner %s\n", device_name);
+
+ sanei_usb_get_vendor_product_byname(device_name,&vendor,&product);
+
+ dev = (PKV_DEV) malloc (sizeof (KV_DEV));
+
+ if (dev == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (KV_DEV));
+
+ dev->bus_mode = KV_USB_BUS;
+ dev->usb_fd = -1;
+ dev->scsi_fd = -1;
+ strcpy (dev->device_name, device_name);
+
+ dev->buffer0 = (unsigned char *) malloc (SCSI_BUFFER_SIZE + 12);
+ dev->buffer = dev->buffer0 + 12;
+
+ if (dev->buffer0 == NULL)
+ {
+ free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->scsi_type = 6;
+ strcpy (dev->scsi_type_str, "ADF Scanner");
+ strcpy (dev->scsi_vendor, "Panasonic");
+ strcpy (dev->scsi_product,
+ product == (int) KV_S1020C ? "KV-S1020C" :
+ product == (int) KV_S1025C ? "KV-S1025C" :
+ product == (int) KV_S1045C ? "KV-S1045C" :
+ "KV-S10xxC");
+ strcpy (dev->scsi_version, "1.00");
+
+ /* Set SANE_Device */
+ dev->sane.name = dev->device_name;
+ dev->sane.vendor = dev->scsi_vendor;
+ dev->sane.model = dev->scsi_product;
+ dev->sane.type = dev->scsi_type_str;
+
+ /* Add into g_devices chain */
+ dev->next = g_devices;
+ g_devices = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Get all supported scanners, and store into g_devlist */
+SANE_Status
+kv_usb_enum_devices (void)
+{
+ int cnt = 0;
+ int i;
+ PKV_DEV pd;
+ char usb_str[18];
+
+ DBG (DBG_proc, "kv_usb_enum_devices: enter\n");
+
+ sanei_usb_init();
+
+ sprintf(usb_str,"usb %#04x %#04x",VENDOR_ID,KV_S1020C);
+ sanei_usb_attach_matching_devices(usb_str, attach_scanner_usb);
+
+ sprintf(usb_str,"usb %#04x %#04x",VENDOR_ID,KV_S1025C);
+ sanei_usb_attach_matching_devices(usb_str, attach_scanner_usb);
+
+ sprintf(usb_str,"usb %#04x %#04x",VENDOR_ID,KV_S1045C);
+ sanei_usb_attach_matching_devices(usb_str, attach_scanner_usb);
+
+ for (pd = g_devices; pd; pd=pd->next) {
+ cnt++;
+ }
+
+ g_devlist =
+ (const SANE_Device **) malloc (sizeof (SANE_Device *) * (cnt + 1));
+ if (g_devlist == NULL)
+ {
+ DBG (DBG_proc,
+ "kv_usb_enum_devices: leave on error " " --out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ pd = g_devices;
+ for (i = 0; i < cnt; i++)
+ {
+ g_devlist[i] = (const SANE_Device *) &pd->sane;
+ pd = pd->next;
+ }
+ g_devlist[cnt] = 0;
+
+ DBG (DBG_proc, "kv_usb_enum_devices: leave with %d devices.\n", cnt);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Check if device is already open */
+SANE_Bool
+kv_usb_already_open (PKV_DEV dev)
+{
+ return (dev->usb_fd > -1);
+}
+
+/* Open an USB device */
+SANE_Status
+kv_usb_open (PKV_DEV dev)
+{
+ SANE_Status ret;
+
+ DBG (DBG_proc, "kv_usb_open: enter\n");
+ if (kv_usb_already_open(dev))
+ {
+ DBG (DBG_proc, "kv_usb_open: leave -- already open\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ ret = sanei_usb_open (dev->device_name, &(dev->usb_fd));
+ if (ret)
+ {
+ DBG (DBG_error, "kv_usb_open: leave -- cannot open device\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ sanei_usb_clear_halt (dev->usb_fd);
+
+ DBG (DBG_proc, "kv_usb_open: leave\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Close an USB device */
+void
+kv_usb_close (PKV_DEV dev)
+{
+ DBG (DBG_proc, "kv_usb_close: enter\n");
+ if (kv_usb_already_open(dev))
+ {
+ sanei_usb_close(dev->usb_fd);
+ dev->usb_fd = -1;
+ }
+ DBG (DBG_proc, "kv_usb_close: leave\n");
+}
+
+/* Clean up the USB bus and release all resources allocated to devices */
+void
+kv_usb_cleanup (void)
+{
+}
+
+/* Send command via USB, and get response data */
+SANE_Status
+kv_usb_escape (PKV_DEV dev,
+ PKV_CMD_HEADER header, unsigned char *status_byte)
+{
+ int got_response = 0;
+ size_t len;
+ unsigned char cmd_buff[24];
+ memset (cmd_buff, 0, 24);
+ cmd_buff[3] = 0x18; /* container length */
+ cmd_buff[5] = 1; /* container type: command block */
+ cmd_buff[6] = 0x90; /* code */
+
+ if (!kv_usb_already_open(dev))
+ {
+ DBG (DBG_error, "kv_usb_escape: error, device not open.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ memcpy (cmd_buff + 12, header->cdb, header->cdb_size);
+
+ /* change timeout */
+ sanei_usb_set_timeout(KV_CMD_TIMEOUT);
+
+ /* Send command */
+ len = 24;
+ if (sanei_usb_write_bulk (dev->usb_fd, (SANE_Byte *) cmd_buff, &len))
+ {
+ DBG (DBG_error, "usb_bulk_write: Error writing command.\n");
+ hexdump (DBG_error, "cmd block", cmd_buff, 24);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Send / Read data */
+ if (header->direction == KV_CMD_IN)
+ {
+ size_t size = header->data_size + 12;
+ size_t size_read = size;
+ unsigned char *data = ((unsigned char *) header->data) - 12;
+ SANE_Status ret;
+
+ ret = sanei_usb_read_bulk (dev->usb_fd, (SANE_Byte *) data, &size_read);
+
+ /*empty read is ok?*/
+ if (ret == SANE_STATUS_EOF){
+ sanei_usb_clear_halt (dev->usb_fd);
+ ret = SANE_STATUS_GOOD;
+ }
+
+ if (ret) {
+ sanei_usb_clear_halt (dev->usb_fd);
+ DBG (DBG_error, "usb_bulk_read: Error reading data.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (size_read != size)
+ {
+ DBG (DBG_shortread, "usb_bulk_read: Warning - short read\n");
+ DBG (DBG_shortread, "usb_bulk_read: bytes to read = %lu\n", (unsigned long)size);
+ DBG (DBG_shortread,
+ "usb_bulk_read: bytes actual read = %lu\n", (unsigned long)size_read);
+ /*hexdump (DBG_shortread, "data", data, size_read); */
+ }
+ }
+
+ if (header->direction == KV_CMD_OUT)
+ {
+ size_t size = header->data_size + 12;
+ size_t size_written = size;
+ unsigned char *data = ((unsigned char *) header->data) - 12;
+ SANE_Status ret;
+
+ memset (data, 0, 12);
+ Ito32 (size, data);
+ data[5] = 0x02; /* container type: data block */
+ data[6] = 0xb0; /* code */
+
+ ret = sanei_usb_write_bulk (dev->usb_fd, (SANE_Byte *) data, &size_written);
+
+ /*empty write is ok?*/
+ if (ret == SANE_STATUS_EOF){
+ sanei_usb_clear_halt (dev->usb_fd);
+ ret = SANE_STATUS_GOOD;
+ }
+
+ if (ret) {
+ sanei_usb_clear_halt (dev->usb_fd);
+ DBG (DBG_error, "usb_bulk_write: Error writing data.\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (size_written != size)
+ {
+ DBG (DBG_shortread, "usb_bulk_write: Warning - short written\n");
+ DBG (DBG_shortread, "usb_bulk_write: bytes to write = %lu\n", (unsigned long)size);
+ DBG (DBG_shortread,
+ "usb_bulk_write: bytes actual written = %lu\n", (unsigned long)size_written);
+ hexdump (DBG_shortread, "data", data, size_written);
+ }
+ }
+
+ /* Get response */
+ if (!got_response)
+ {
+ SANE_Status ret;
+ size_t len = 16;
+
+ ret = sanei_usb_read_bulk (dev->usb_fd, (SANE_Byte *) cmd_buff, &len);
+
+ if (ret || len != 16)
+ {
+ DBG (DBG_error, "usb_bulk_read: Error reading response."
+ " read %lu bytes\n", (unsigned long)len);
+ sanei_usb_clear_halt (dev->usb_fd);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (cmd_buff[5] != 3)
+ {
+ DBG (DBG_error, "usb_bulk_read: Invalid response block.\n");
+ hexdump (DBG_error, "response", cmd_buff, 16);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ *status_byte = cmd_buff[15] & 0x3E;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Send command via USB, and request sense on CHECK CONDITION status */
+SANE_Status
+kv_usb_send_command (PKV_DEV dev,
+ PKV_CMD_HEADER header, PKV_CMD_RESPONSE response)
+{
+ unsigned char status = 0;
+ SANE_Status s;
+ memset (response, 0, sizeof (KV_CMD_RESPONSE));
+ response->status = KV_FAILED;
+
+ s = kv_usb_escape (dev, header, &status);
+
+ if (s)
+ {
+ status = 0x02;
+ }
+
+ if (status == 0x02)
+ { /* check condition */
+ /* request sense */
+ KV_CMD_HEADER hdr;
+ memset (&hdr, 0, sizeof (hdr));
+ hdr.direction = KV_CMD_IN;
+ hdr.cdb[0] = SCSI_REQUEST_SENSE;
+ hdr.cdb[4] = 0x12;
+ hdr.cdb_size = 6;
+ hdr.data_size = 0x12;
+ hdr.data = &response->sense;
+
+ if (kv_usb_escape (dev, &hdr, &status) != 0)
+ return SANE_STATUS_IO_ERROR;
+
+ hexdump (DBG_error, "sense data", (unsigned char *) &response->sense,
+ 0x12);
+
+ response->status = KV_CHK_CONDITION;
+ }
+ else
+ {
+ response->status = KV_SUCCESS;
+ }
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/kvs1025_usb.h b/backend/kvs1025_usb.h
new file mode 100644
index 0000000..1a193ea
--- /dev/null
+++ b/backend/kvs1025_usb.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+*/
+/* sane - Scanner Access Now Easy.
+ Panasonic KV-S1020C / KV-S1025C USB scanners.
+*/
+
+#ifndef __KVS1025_USB_H
+#define __KVS1025_USB_H
+
+#include "kvs1025_cmds.h"
+
+SANE_Status kv_usb_enum_devices (void);
+SANE_Status kv_usb_open (PKV_DEV dev);
+SANE_Bool kv_usb_already_open (PKV_DEV dev);
+void kv_usb_close (PKV_DEV dev);
+void kv_usb_cleanup (void);
+
+SANE_Status
+kv_usb_escape (PKV_DEV dev,
+ PKV_CMD_HEADER header,
+ unsigned char *status_byte);
+
+SANE_Status kv_usb_send_command (PKV_DEV dev,
+ PKV_CMD_HEADER header,
+ PKV_CMD_RESPONSE response);
+
+#endif /* #ifndef __KVS1025_USB_H */
diff --git a/backend/kvs20xx.c b/backend/kvs20xx.c
new file mode 100644
index 0000000..955252a
--- /dev/null
+++ b/backend/kvs20xx.c
@@ -0,0 +1,535 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+ Copyright (C) 2010, m. allan noah
+*/
+/*
+ Panasonic KV-S20xx USB-SCSI scanners.
+*/
+
+#define DEBUG_NOT_STATIC
+#define BUILD 2
+
+#include "../include/sane/config.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "kvs20xx.h"
+#include "kvs20xx_cmd.h"
+
+struct known_device
+{
+ const SANE_Int id;
+ const SANE_Device scanner;
+};
+
+static const struct known_device known_devices[] = {
+ {
+ KV_S2025C,
+ { "", "MATSHITA", "KV-S2025C", "sheetfed scanner" },
+ },
+ {
+ KV_S2045C,
+ { "", "MATSHITA", "KV-S2045C", "sheetfed scanner" },
+ },
+ {
+ KV_S2026C,
+ { "", "MATSHITA", "KV-S2026C", "sheetfed scanner" },
+ },
+ {
+ KV_S2046C,
+ { "", "MATSHITA", "KV-S2046C", "sheetfed scanner" },
+ },
+ {
+ KV_S2028C,
+ { "", "MATSHITA", "KV-S2028C", "sheetfed scanner" },
+ },
+ {
+ KV_S2048C,
+ { "", "MATSHITA", "KV-S2048C", "sheetfed scanner" },
+ },
+};
+
+SANE_Status
+sane_init (SANE_Int __sane_unused__ * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ DBG_INIT ();
+ DBG (DBG_INFO, "This is panasonic kvs20xx driver\n");
+
+ *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
+
+ /* Initialize USB */
+ sanei_usb_init ();
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * List of available devices, allocated by sane_get_devices, released
+ * by sane_exit()
+ */
+static SANE_Device **devlist = NULL;
+static unsigned curr_scan_dev = 0;
+
+void
+sane_exit (void)
+{
+ if (devlist)
+ {
+ int i;
+ for (i = 0; devlist[i]; i++)
+ {
+ free ((void *) devlist[i]->name);
+ free ((void *) devlist[i]);
+ }
+ free ((void *) devlist);
+ devlist = NULL;
+ }
+}
+
+static SANE_Status
+attach (SANE_String_Const devname)
+{
+ int i = 0;
+ if (devlist)
+ {
+ for (; devlist[i]; i++);
+ devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ devlist = malloc (sizeof (SANE_Device *) * 2);
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ }
+ devlist[i] = malloc (sizeof (SANE_Device));
+ if (!devlist[i])
+ return SANE_STATUS_NO_MEM;
+ memcpy (devlist[i], &known_devices[curr_scan_dev].scanner,
+ sizeof (SANE_Device));
+ devlist[i]->name = strdup (devname);
+ /* terminate device list with NULL entry: */
+ devlist[i + 1] = 0;
+ DBG (DBG_INFO, "%s device attached\n", devname);
+ return SANE_STATUS_GOOD;
+}
+
+/* Get device list */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ if (devlist)
+ {
+ int i;
+ for (i = 0; devlist[i]; i++)
+ {
+ free ((void *) devlist[i]->name);
+ free ((void *) devlist[i]);
+ }
+ free ((void *) devlist);
+ devlist = NULL;
+ }
+
+ for (curr_scan_dev = 0;
+ curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]);
+ curr_scan_dev++)
+ {
+ sanei_usb_find_devices (PANASONIC_ID,
+ known_devices[curr_scan_dev].id, attach);
+ }
+ for (curr_scan_dev = 0;
+ curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]);
+ curr_scan_dev++)
+ {
+ sanei_scsi_find_devices (known_devices[curr_scan_dev].scanner.vendor,
+ known_devices[curr_scan_dev].scanner.model,
+ NULL, -1, -1, -1, -1, attach);
+ }
+ *device_list = (const SANE_Device **) devlist;
+ return SANE_STATUS_GOOD;
+}
+
+/* Open device, return the device handle */
+SANE_Status
+sane_open (SANE_String_Const devname, SANE_Handle * handle)
+{
+ unsigned i, j, id = 0;
+ struct scanner *s;
+ SANE_Int h, bus;
+ SANE_Status st;
+ for (i = 0; devlist[i]; i++)
+ {
+ if (!strcmp (devlist[i]->name, devname))
+ break;
+ }
+ if (!devlist[i])
+ return SANE_STATUS_INVAL;
+ for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++)
+ {
+ if (!strcmp (devlist[i]->model, known_devices[j].scanner.model))
+ {
+ id = known_devices[j].id;
+ break;
+ }
+ }
+
+ st = sanei_usb_open (devname, &h);
+ if (st == SANE_STATUS_ACCESS_DENIED)
+ return st;
+ if (st)
+ {
+ st = sanei_scsi_open (devname, &h, kvs20xx_sense_handler, NULL);
+ if (st)
+ {
+ return st;
+ }
+ bus = SCSI;
+ }
+ else
+ {
+ bus = USB;
+ st = sanei_usb_claim_interface (h, 0);
+ if (st)
+ {
+ sanei_usb_close (h);
+ return st;
+ }
+ }
+
+ s = malloc (sizeof (struct scanner));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (struct scanner));
+ s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE);
+ if (!s->buffer)
+ return SANE_STATUS_NO_MEM;
+ s->file = h;
+ s->bus = bus;
+ s->id = id;
+ kvs20xx_init_options (s);
+ *handle = s;
+ for (i = 0; i < 3; i++)
+ {
+ st = kvs20xx_test_unit_ready (s);
+ if (st)
+ {
+ if (s->bus == SCSI)
+ {
+ sanei_scsi_close (s->file);
+ st = sanei_scsi_open (devname, &h, kvs20xx_sense_handler, NULL);
+ if (st)
+ return st;
+ }
+ else
+ {
+ sanei_usb_release_interface (s->file, 0);
+ sanei_usb_close (s->file);
+ st = sanei_usb_open (devname, &h);
+ if (st)
+ return st;
+ st = sanei_usb_claim_interface (h, 0);
+ if (st)
+ {
+ sanei_usb_close (h);
+ return st;
+ }
+ }
+ s->file = h;
+ }
+ else
+ break;
+ }
+ if (i == 3)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ st = kvs20xx_set_timeout (s, s->val[FEED_TIMEOUT].w);
+ if (st)
+ {
+ sane_close (s);
+ return st;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Close device */
+void
+sane_close (SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ int i;
+ if (s->bus == USB)
+ {
+ sanei_usb_release_interface (s->file, 0);
+ sanei_usb_close (s->file);
+ }
+ else
+ sanei_scsi_close (s->file);
+
+ for (i = 1; i < NUM_OPTIONS; i++)
+ {
+ if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s)
+ free (s->val[i].s);
+ }
+ if (s->data)
+ free (s->data);
+ free (s->buffer);
+ free (s);
+
+}
+
+/* Get option descriptor */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS || option < 0)
+ return NULL;
+ return s->opt + option;
+}
+
+static SANE_Status
+wait_document (struct scanner *s)
+{
+ SANE_Status st;
+ int i;
+ if (!strcmp ("off", s->val[MANUALFEED].s))
+ return kvs20xx_document_exist (s);
+
+ for (i = 0; i < s->val[FEED_TIMEOUT].w; i++)
+ {
+ st = kvs20xx_document_exist (s);
+ if (st != SANE_STATUS_NO_DOCS)
+ return st;
+ sleep (1);
+ }
+ return SANE_STATUS_NO_DOCS;
+}
+
+/* Start scanning */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status st;
+ int duplex = s->val[DUPLEX].w;
+
+ if (!s->scanning)
+ {
+ unsigned dummy_length;
+ st = kvs20xx_test_unit_ready (s);
+ if (st)
+ return st;
+
+ st = wait_document (s);
+ if (st)
+ return st;
+
+ st = kvs20xx_reset_window (s);
+ if (st)
+ return st;
+ st = kvs20xx_set_window (s, SIDE_FRONT);
+ if (st)
+ return st;
+ if (duplex)
+ {
+ st = kvs20xx_set_window (s, SIDE_BACK);
+ if (st)
+ return st;
+ }
+ st = kvs20xx_scan (s);
+ if (st)
+ return st;
+
+ st = kvs20xx_read_picture_element (s, SIDE_FRONT, &s->params);
+ if (st)
+ return st;
+ if (duplex)
+ {
+ st = get_adjust_data (s, &dummy_length);
+ if (st)
+ return st;
+ }
+ else
+ {
+ dummy_length = 0;
+ }
+ s->scanning = 1;
+ s->page = 0;
+ s->read = 0;
+ s->side = SIDE_FRONT;
+ sane_get_parameters (s, NULL);
+ s->saved_dummy_size = s->dummy_size = dummy_length
+ ? (dummy_length * s->val[RESOLUTION].w / 1200 - 1)
+ * s->params.bytes_per_line : 0;
+ s->side_size = s->params.lines * s->params.bytes_per_line;
+
+ s->data = realloc (s->data, duplex ? s->side_size * 2 : s->side_size);
+ if (!s->data)
+ {
+ s->scanning = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ if (duplex)
+ {
+ unsigned side = SIDE_FRONT;
+ unsigned read, mx;
+ if (s->side == SIDE_FRONT && s->read == s->side_size - s->dummy_size)
+ {
+ s->side = SIDE_BACK;
+ s->read = s->dummy_size;
+ s->dummy_size = 0;
+ return SANE_STATUS_GOOD;
+ }
+ s->read = 0;
+ s->dummy_size = s->saved_dummy_size;
+ s->side = SIDE_FRONT;
+ st = kvs20xx_document_exist (s);
+ if (st)
+ return st;
+ for (mx = s->side_size * 2; !st; mx -= read, side ^= SIDE_BACK)
+ st = kvs20xx_read_image_data (s, s->page, side,
+ &s->data[s->side_size * 2 - mx], mx,
+ &read);
+ }
+ else
+ {
+ unsigned read, mx;
+ s->read = 0;
+ st = kvs20xx_document_exist (s);
+ if (st)
+ return st;
+ DBG (DBG_INFO, "start: %d\n", s->page);
+
+ for (mx = s->side_size; !st; mx -= read)
+ st = kvs20xx_read_image_data (s, s->page, SIDE_FRONT,
+ &s->data[s->side_size - mx], mx, &read);
+ }
+ if (st && st != SANE_STATUS_EOF)
+ {
+ s->scanning = 0;
+ return st;
+ }
+ s->page++;
+ return SANE_STATUS_GOOD;
+}
+
+inline static void
+memcpy24 (u8 * dest, u8 * src, unsigned size, unsigned ls)
+{
+ unsigned i;
+ for (i = 0; i < size; i++)
+ {
+ dest[i * 3] = src[i];
+ dest[i * 3 + 1] = src[i + ls];
+ dest[i * 3 + 2] = src[i + 2 * ls];
+ }
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ int duplex = s->val[DUPLEX].w;
+ int color = !strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_COLOR);
+ int rest = s->side_size - s->read - s->dummy_size;
+ *len = 0;
+
+ if (!s->scanning || !rest)
+ {
+ if (strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous")))
+ {
+ if (!duplex || s->side == SIDE_BACK)
+ s->scanning = 0;
+ }
+ return SANE_STATUS_EOF;
+ }
+
+ *len = max_len < rest ? max_len : rest;
+ if (duplex && (s->id == KV_S2025C
+ || s->id == KV_S2026C || s->id == KV_S2028C))
+ {
+ if (color)
+ {
+ unsigned ls = s->params.bytes_per_line;
+ unsigned i, a = s->side == SIDE_FRONT ? 0 : ls / 3;
+ u8 *data;
+ *len = (*len / ls) * ls;
+ for (i = 0, data = s->data + s->read * 2 + a;
+ i < *len / ls; buf += ls, data += 2 * ls, i++)
+ memcpy24 (buf, data, ls / 3, ls * 2 / 3);
+ }
+ else
+ {
+ unsigned ls = s->params.bytes_per_line;
+ unsigned i = s->side == SIDE_FRONT ? 0 : ls;
+ unsigned head = ls - (s->read % ls);
+ unsigned tail = (*len - head) % ls;
+ unsigned lines = (*len - head) / ls;
+ u8 *data = s->data + (s->read / ls) * ls * 2 + i + s->read % ls;
+ assert (data <= s->data + s->side_size * 2);
+ memcpy (buf, data, head);
+ for (i = 0, buf += head, data += head + (head ? ls : 0);
+ i < lines; buf += ls, data += ls * 2, i++)
+ {
+ assert (data <= s->data + s->side_size * 2);
+ memcpy (buf, data, ls);
+ }
+ assert ((data <= s->data + s->side_size * 2) || !tail);
+ memcpy (buf, data, tail);
+ }
+ s->read += *len;
+ }
+ else
+ {
+ if (color)
+ {
+ unsigned i, ls = s->params.bytes_per_line;
+ u8 *data = s->data + s->read;
+ *len = (*len / ls) * ls;
+ for (i = 0; i < *len / ls; buf += ls, data += ls, i++)
+ memcpy24 (buf, data, ls / 3, ls / 3);
+ }
+ else
+ {
+ memcpy (buf, s->data + s->read, *len);
+ }
+ s->read += *len;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ s->scanning = 0;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ h,
+ SANE_Int __sane_unused__ * fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/kvs20xx.h b/backend/kvs20xx.h
new file mode 100644
index 0000000..9bc833d
--- /dev/null
+++ b/backend/kvs20xx.h
@@ -0,0 +1,211 @@
+#ifndef __KVS20XX_H
+#define __KVS20XX_H
+
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+ Copyright (C) 2010, m. allan noah
+*/
+/*
+ Panasonic KV-S20xx USB-SCSI scanners.
+*/
+
+#include <sys/param.h>
+
+#undef BACKEND_NAME
+#define BACKEND_NAME kvs20xx
+
+#define DBG_ERR 1
+#define DBG_WARN 2
+#define DBG_MSG 3
+#define DBG_INFO 4
+#define DBG_DBG 5
+
+#define PANASONIC_ID 0x04da
+#define KV_S2025C 0xdeadbeef
+#define KV_S2045C 0xdeadbeee
+#define KV_S2026C 0x1000
+#define KV_S2046C 0x1001
+#define KV_S2048C 0x1009
+#define KV_S2028C 0x100a
+#define USB 1
+#define SCSI 2
+#define MAX_READ_DATA_SIZE 0x10000
+#define BULK_HEADER_SIZE 12
+
+typedef unsigned char u8;
+typedef unsigned u32;
+typedef unsigned short u16;
+
+#define SIDE_FRONT 0x00
+#define SIDE_BACK 0x80
+
+/* options */
+typedef enum
+{
+ NUM_OPTS = 0,
+
+ /* General options */
+ MODE_GROUP,
+ MODE, /* scanner modes */
+ RESOLUTION, /* X and Y resolution */
+
+ DUPLEX, /* Duplex mode */
+ FEEDER_MODE, /* Feeder mode, fixed to Continous */
+ LENGTHCTL, /* Length control mode */
+ MANUALFEED, /* Manual feed mode */
+ FEED_TIMEOUT, /* Feed timeout */
+ DBLFEED, /* Double feed detection mode */
+ FIT_TO_PAGE, /* Scanner shrinks image to fit scanned page */
+
+ /* Geometry group */
+ GEOMETRY_GROUP,
+ PAPER_SIZE, /* Paper size */
+ LANDSCAPE, /* true if landscape */
+ TL_X, /* upper left X */
+ TL_Y, /* upper left Y */
+ BR_X, /* bottom right X */
+ BR_Y, /* bottom right Y */
+
+ ADVANCED_GROUP,
+ BRIGHTNESS, /* Brightness */
+ CONTRAST, /* Contrast */
+ THRESHOLD, /* Binary threshold */
+ IMAGE_EMPHASIS, /* Image emphasis */
+ GAMMA_CORRECTION, /* Gamma correction */
+ LAMP, /* Lamp -- color drop out */
+ /* must come last: */
+ NUM_OPTIONS
+} KV_OPTION;
+
+#ifndef SANE_OPTION
+typedef union
+{
+ SANE_Bool b; /**< bool */
+ SANE_Word w; /**< word */
+ SANE_Word *wa; /**< word array */
+ SANE_String s; /**< string */
+}
+Option_Value;
+#define SANE_OPTION 1
+#endif
+
+struct scanner
+{
+ unsigned id;
+ int scanning;
+ int page;
+ int side;
+ int bus;
+ SANE_Int file;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+ u8 *buffer;
+ u8 *data;
+ unsigned side_size;
+ unsigned read;
+ unsigned dummy_size;
+ unsigned saved_dummy_size;
+};
+
+struct window
+{
+ u8 reserved[6];
+ u16 window_descriptor_block_length;
+
+ u8 window_identifier;
+ u8 reserved2;
+ u16 x_resolution;
+ u16 y_resolution;
+ u32 upper_left_x;
+ u32 upper_left_y;
+ u32 width;
+ u32 length;
+ u8 brightness;
+ u8 threshold;
+ u8 contrast;
+ u8 image_composition;
+ u8 bit_per_pixel;
+ u16 halftone_pattern;
+ u8 reserved3;
+ u16 bit_ordering;
+ u8 compression_type;
+ u8 compression_argument;
+ u8 reserved4[6];
+
+ u8 vendor_unique_identifier;
+ u8 nobuf_fstspeed_dfstop;
+ u8 mirror_image;
+ u8 image_emphasis;
+ u8 gamma_correction;
+ u8 mcd_lamp_dfeed_sens;
+ u8 reserved5;
+ u8 document_size;
+ u32 document_width;
+ u32 document_length;
+ u8 ahead_deskew_dfeed_scan_area_fspeed_rshad;
+ u8 continuous_scanning_pages;
+ u8 automatic_threshold_mode;
+ u8 automatic_separation_mode;
+ u8 standard_white_level_mode;
+ u8 b_wnr_noise_reduction;
+ u8 mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr;
+ u8 stop_mode;
+} __attribute__((__packed__));
+
+void kvs20xx_init_options (struct scanner *);
+void kvs20xx_init_window (struct scanner *s, struct window *wnd, int wnd_id);
+
+static inline u16
+swap_bytes16 (u16 x)
+{
+ return x << 8 | x >> 8;
+}
+static inline u32
+swap_bytes32 (u32 x)
+{
+ return x << 24 | x >> 24 |
+ (x & (u32) 0x0000ff00UL) << 8 | (x & (u32) 0x00ff0000UL) >> 8;
+}
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static inline void
+set24 (u8 * p, u32 x)
+{
+ p[2] = x >> 16;
+ p[1] = x >> 8;
+ p[0] = x >> 0;
+}
+
+#define cpu2be16(x) (x)
+#define cpu2be32(x) (x)
+#define cpu2le16(x) swap_bytes16(x)
+#define cpu2le32(x) swap_bytes32(x)
+#define le2cpu16(x) swap_bytes16(x)
+#define le2cpu32(x) swap_bytes32(x)
+#define be2cpu16(x) (x)
+#define be2cpu32(x) (x)
+#define BIT_ORDERING 0
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+static inline void
+set24 (u8 * p, u32 x)
+{
+ p[0] = x >> 16;
+ p[1] = x >> 8;
+ p[2] = x >> 0;
+}
+
+#define cpu2le16(x) (x)
+#define cpu2le32(x) (x)
+#define cpu2be16(x) swap_bytes16(x)
+#define cpu2be32(x) swap_bytes32(x)
+#define le2cpu16(x) (x)
+#define le2cpu32(x) (x)
+#define be2cpu16(x) swap_bytes16(x)
+#define be2cpu32(x) swap_bytes32(x)
+#define BIT_ORDERING 1
+#else
+#error __BYTE_ORDER not defined
+#endif
+
+#endif /*__KVS20XX_H*/
diff --git a/backend/kvs20xx_cmd.c b/backend/kvs20xx_cmd.c
new file mode 100644
index 0000000..7579701
--- /dev/null
+++ b/backend/kvs20xx_cmd.c
@@ -0,0 +1,379 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+ Copyright (C) 2010, m. allan noah
+*/
+/*
+ Panasonic KV-S20xx USB-SCSI scanners.
+*/
+
+#include "../include/sane/config.h"
+
+#include <string.h>
+/*#include <unistd.h>*/
+
+#define DEBUG_DECLARE_ONLY
+#define BACKEND_NAME kvs20xx
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+
+#include "kvs20xx.h"
+#include "kvs20xx_cmd.h"
+
+static SANE_Status
+usb_send_command (struct scanner *s, struct cmd *c, struct response *r,
+ void *buf)
+{
+ SANE_Status st;
+ struct bulk_header *h = (struct bulk_header *) buf;
+ u8 resp[sizeof (*h) + STATUS_SIZE];
+ size_t sz = sizeof (*h) + MAX_CMD_SIZE;
+ memset (h, 0, sz);
+ h->length = cpu2be32 (sz);
+ h->type = cpu2be16 (COMMAND_BLOCK);
+ h->code = cpu2be16 (COMMAND_CODE);
+ memcpy (h + 1, c->cmd, c->cmd_size);
+
+ st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz);
+ if (st)
+ return st;
+ if (sz != sizeof (*h) + MAX_CMD_SIZE)
+ return SANE_STATUS_IO_ERROR;
+ if (c->dir == CMD_IN)
+ {
+ sz = sizeof (*h) + c->data_size;
+ st = sanei_usb_read_bulk (s->file, (SANE_Byte *) h, &sz);
+ c->data = h + 1;
+ c->data_size = sz - sizeof (*h);
+
+ if (st || sz < sizeof (*h))
+ {
+ st = sanei_usb_release_interface (s->file, 0);
+ if (st)
+ return st;
+ st = sanei_usb_claim_interface (s->file, 0);
+ if (st)
+ return st;
+ r->status = CHECK_CONDITION;
+ return SANE_STATUS_GOOD;
+ }
+
+ }
+ else if (c->dir == CMD_OUT)
+ {
+ sz = sizeof (*h) + c->data_size;
+ memset (h, 0, sizeof (*h));
+ h->length = cpu2be32 (sizeof (*h) + c->data_size);
+ h->type = cpu2be16 (DATA_BLOCK);
+ h->code = cpu2be16 (DATA_CODE);
+ memcpy (h + 1, c->data, c->data_size);
+ st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz);
+ if (st)
+ return st;
+ }
+ sz = sizeof (resp);
+ st = sanei_usb_read_bulk (s->file, resp, &sz);
+ if (st || sz != sizeof (resp))
+ return SANE_STATUS_IO_ERROR;
+ r->status = be2cpu32 (*((u32 *) (resp + sizeof (*h))));
+ return st;
+}
+
+SANE_Status
+kvs20xx_sense_handler (int __sane_unused__ fd,
+ u_char * sense_buffer, void __sane_unused__ * arg)
+{
+ unsigned i;
+ SANE_Status st = SANE_STATUS_GOOD;
+ for (i = 0; i < sizeof (s_errors) / sizeof (s_errors[0]); i++)
+ if ((sense_buffer[2] & 0xf) == s_errors[i].sense
+ && sense_buffer[12] == s_errors[i].asc
+ && sense_buffer[13] == s_errors[i].ascq)
+ {
+ st = s_errors[i].st;
+ break;
+ }
+ if (st == SANE_STATUS_GOOD && sense_buffer[2] & END_OF_MEDIUM)
+ st = SANE_STATUS_EOF;
+ if (i == sizeof (s_errors) / sizeof (s_errors[0]))
+ st = SANE_STATUS_IO_ERROR;
+ DBG (DBG_ERR,
+ "send_command: CHECK_CONDITION: sence:0x%x ASC:0x%x ASCQ:0x%x\n",
+ sense_buffer[2], sense_buffer[12], sense_buffer[13]);
+
+ return st;
+}
+
+static SANE_Status
+send_command (struct scanner * s, struct cmd * c)
+{
+ SANE_Status st = SANE_STATUS_GOOD;
+ if (s->bus == USB)
+ {
+ struct response r;
+ memset (&r, 0, sizeof (r));
+ st = usb_send_command (s, c, &r, s->buffer);
+ if (st)
+ return st;
+ if (r.status)
+ {
+ u8 b[sizeof (struct bulk_header) + RESPONSE_SIZE];
+ struct cmd c2 = {
+ {0},
+ 6,
+ 0,
+ RESPONSE_SIZE,
+ CMD_IN
+ };
+ c2.cmd[0] = REQUEST_SENSE;
+ c2.cmd[4] = RESPONSE_SIZE;
+ st = usb_send_command (s, &c2, &r, b);
+ if (st)
+ return st;
+ st = kvs20xx_sense_handler (0, b + sizeof (struct bulk_header), NULL);
+ }
+ }
+ else
+ {
+ if (c->dir == CMD_OUT)
+ {
+ memcpy (s->buffer, c->cmd, c->cmd_size);
+ memcpy (s->buffer + c->cmd_size, c->data, c->data_size);
+ st = sanei_scsi_cmd (s->file, s->buffer, c->cmd_size + c->data_size,
+ NULL, NULL);
+ }
+ else if (c->dir == CMD_IN)
+ {
+ c->data = s->buffer;
+ st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size,
+ c->data, (size_t *) & c->data_size);
+ }
+ else
+ {
+ st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, NULL, NULL);
+ }
+ }
+ return st;
+}
+
+SANE_Status
+kvs20xx_test_unit_ready (struct scanner * s)
+{
+ struct cmd c = {
+ {0},
+ 6,
+ 0,
+ 0,
+ CMD_NONE
+ };
+ c.cmd[0] = TEST_UNIT_READY;
+ if (send_command (s, &c))
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+kvs20xx_set_timeout (struct scanner * s, int timeout)
+{
+ u16 t = cpu2be16 ((u16) timeout);
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 0,
+ CMD_OUT
+ };
+ c.cmd[0] = SET_TIMEOUT;
+ c.cmd[2] = 0x8d;
+ *((u16 *) (c.cmd + 7)) = cpu2be16 (sizeof (t));
+
+ c.data = &t;
+ c.data_size = sizeof (t);
+
+ if (s->bus == USB)
+ sanei_usb_set_timeout (timeout * 1000);
+
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs20xx_set_window (struct scanner * s, int wnd_id)
+{
+ struct window wnd;
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 0,
+ CMD_OUT
+ };
+ c.cmd[0] = SET_WINDOW;
+ *((u16 *) (c.cmd + 7)) = cpu2be16 (sizeof (wnd));
+
+ c.data = &wnd;
+ c.data_size = sizeof (wnd);
+
+ kvs20xx_init_window (s, &wnd, wnd_id);
+
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs20xx_reset_window (struct scanner * s)
+{
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 0,
+ CMD_NONE
+ };
+ c.cmd[0] = SET_WINDOW;
+
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs20xx_scan (struct scanner * s)
+{
+ struct cmd c = {
+ {0},
+ 6,
+ 0,
+ 0,
+ CMD_NONE
+ };
+ c.cmd[0] = SCAN;
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs20xx_document_exist (struct scanner * s)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 6,
+ CMD_IN,
+ };
+ u8 *d;
+ c.cmd[0] = READ_10;
+ c.cmd[2] = 0x81;
+ set24 (c.cmd + 6, c.data_size);
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ d = c.data;
+ if (d[0] & 0x20)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+SANE_Status
+kvs20xx_read_picture_element (struct scanner * s, unsigned side,
+ SANE_Parameters * p)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 16,
+ CMD_IN
+ };
+ u32 *data;
+ c.cmd[0] = READ_10;
+ c.cmd[2] = 0x80;
+ c.cmd[5] = side;
+ set24 (c.cmd + 6, c.data_size);
+
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ data = (u32 *) c.data;
+ p->pixels_per_line = be2cpu32 (data[0]);
+ p->lines = be2cpu32 (data[1]);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_buffer_status (struct scanner * s, unsigned *data_avalible)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 12,
+ CMD_IN
+ };
+ u32 *data;
+ c.cmd[0] = GET_BUFFER_STATUS;
+ c.cmd[7] = 12;
+
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ data = (u32 *) c.data;
+ *data_avalible = be2cpu32 (data[3]);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+kvs20xx_read_image_data (struct scanner * s, unsigned page, unsigned side,
+ void *buf, unsigned max_size, unsigned *size)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 0,
+ CMD_IN
+ };
+ c.cmd[0] = READ_10;
+ c.cmd[4] = page;
+ c.cmd[5] = side;
+
+ c.data_size = max_size < MAX_READ_DATA_SIZE ? max_size : MAX_READ_DATA_SIZE;
+
+ set24 (c.cmd + 6, c.data_size);
+ status = send_command (s, &c);
+
+ if (status && status != SANE_STATUS_EOF)
+ return status;
+
+ *size = c.data_size;
+ DBG (DBG_INFO, "kvs20xx_read_image_data: read %d, status %d\n", *size, status);
+ memcpy (buf, c.data, *size);
+ return status;
+}
+
+SANE_Status
+get_adjust_data (struct scanner * s, unsigned *dummy_length)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0},
+ 10,
+ 0,
+ 40,
+ CMD_IN
+ };
+ u16 *data;
+
+ c.cmd[0] = GET_ADJUST_DATA;
+ c.cmd[2] = 0x9b;
+ c.cmd[8] = 40;
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ data = (u16 *) c.data;
+ *dummy_length = be2cpu16 (data[0]);
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/kvs20xx_cmd.h b/backend/kvs20xx_cmd.h
new file mode 100644
index 0000000..c18b754
--- /dev/null
+++ b/backend/kvs20xx_cmd.h
@@ -0,0 +1,128 @@
+#ifndef __KVS20XX_CMD_H
+#define __KVS20XX_CMD_H
+
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+ Copyright (C) 2010, m. allan noah
+*/
+/*
+ Panasonic KV-S20xx USB-SCSI scanners.
+*/
+
+#define COMMAND_BLOCK 1
+#define DATA_BLOCK 2
+#define RESPONSE_BLOCK 3
+
+#define COMMAND_CODE 0x9000
+#define DATA_CODE 0xb000
+#define RESPONSE_CODE 0xa000
+#define STATUS_SIZE 4
+
+struct bulk_header
+{
+ u32 length;
+ u16 type;
+ u16 code;
+ u32 transaction_id;
+};
+
+#define TEST_UNIT_READY 0x00
+#define INQUIRY 0x12
+#define SET_WINDOW 0x24
+#define SCAN 0x1B
+#define SEND_10 0x2A
+#define READ_10 0x28
+#define REQUEST_SENSE 0x03
+#define GET_BUFFER_STATUS 0x34
+#define SET_TIMEOUT 0xE1
+#define GET_ADJUST_DATA 0xE0
+#define GOOD 0
+#define CHECK_CONDITION 2
+
+typedef enum
+{
+ CMD_NONE = 0,
+ CMD_IN = 0x81, /* scanner to pc */
+ CMD_OUT = 0x02 /* pc to scanner */
+} CMD_DIRECTION; /* equals to endpoint address */
+
+#define RESPONSE_SIZE 0x12
+#define MAX_CMD_SIZE 12
+struct cmd
+{
+ unsigned char cmd[MAX_CMD_SIZE];
+ int cmd_size;
+ void *data;
+ int data_size;
+ int dir;
+};
+struct response
+{
+ int status;
+ unsigned char data[RESPONSE_SIZE];
+};
+
+#define END_OF_MEDIUM (1<<6)
+#define INCORRECT_LENGTH_INDICATOR (1<<5)
+static const struct
+{
+ unsigned sense, asc, ascq;
+ SANE_Status st;
+} s_errors[] =
+{
+ {
+ 0, 0, 0, SANE_STATUS_GOOD},
+ {
+ 2, 0, 0, SANE_STATUS_DEVICE_BUSY},
+ {
+ 2, 4, 1, SANE_STATUS_DEVICE_BUSY},
+ {
+ 2, 4, 0x80, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x81, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x82, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x83, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x84, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 0x80, 1, SANE_STATUS_CANCELLED},
+ {
+ 2, 0x80, 2, SANE_STATUS_CANCELLED},
+ {
+ 3, 0x3a, 0, SANE_STATUS_NO_DOCS},
+ {
+ 3, 0x80, 1, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 2, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 3, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 4, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 5, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 6, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 7, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 8, SANE_STATUS_JAMMED},
+ {
+3, 0x80, 9, SANE_STATUS_JAMMED},};
+
+SANE_Status kvs20xx_scan (struct scanner *s);
+SANE_Status kvs20xx_test_unit_ready (struct scanner *s);
+SANE_Status kvs20xx_set_timeout (struct scanner *s, int timeout);
+SANE_Status kvs20xx_set_window (struct scanner *s, int wnd_id);
+SANE_Status kvs20xx_reset_window (struct scanner *s);
+SANE_Status kvs20xx_read_picture_element (struct scanner *s, unsigned side,
+ SANE_Parameters * p);
+SANE_Status kvs20xx_read_image_data (struct scanner *s, unsigned page,
+ unsigned side, void *buf,
+ unsigned max_size, unsigned *size);
+SANE_Status kvs20xx_document_exist (struct scanner *s);
+SANE_Status get_adjust_data (struct scanner *s, unsigned *dummy_length);
+SANE_Status kvs20xx_sense_handler (int fd, u_char * sense_buffer, void *arg);
+
+#endif /*__KVS20XX_CMD_H*/
diff --git a/backend/kvs20xx_opt.c b/backend/kvs20xx_opt.c
new file mode 100644
index 0000000..83d3385
--- /dev/null
+++ b/backend/kvs20xx_opt.c
@@ -0,0 +1,801 @@
+/*
+ Copyright (C) 2008, Panasonic Russia Ltd.
+ Copyright (C) 2010, m. allan noah
+*/
+/*
+ Panasonic KV-S20xx USB-SCSI scanners.
+*/
+
+#include "../include/sane/config.h"
+
+#include <string.h>
+
+#define DEBUG_DECLARE_ONLY
+#define BACKEND_NAME kvs20xx
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "kvs20xx.h"
+#include "kvs20xx_cmd.h"
+
+
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+static const unsigned mode_val[] = { 0, 2, 5 };
+static const unsigned bps_val[] = { 1, 8, 24 };
+
+static const SANE_Range resolutions_range = {100,600,10};
+
+/* List of feeder modes */
+static SANE_String_Const feeder_mode_list[] = {
+ SANE_I18N ("single"),
+ SANE_I18N ("continuous"),
+ NULL
+};
+
+/* List of manual feed mode */
+static SANE_String_Const manual_feed_list[] = {
+ SANE_I18N ("off"),
+ SANE_I18N ("wait_doc"),
+ SANE_I18N ("wait_key"),
+ NULL
+};
+
+/* List of paper sizes */
+static SANE_String_Const paper_list[] = {
+ SANE_I18N ("user_def"),
+ SANE_I18N ("business_card"),
+ /*SANE_I18N("Check"), */
+ /*SANE_I18N ("A3"), */
+ SANE_I18N ("A4"),
+ SANE_I18N ("A5"),
+ SANE_I18N ("A6"),
+ SANE_I18N ("Letter"),
+ /*SANE_I18N ("Double letter 11x17 in"),
+ SANE_I18N ("B4"), */
+ SANE_I18N ("B5"),
+ SANE_I18N ("B6"),
+ SANE_I18N ("Legal"),
+ NULL
+};
+static const unsigned paper_val[] = { 0, 1, 4, 5, 6, 7, 13, 14, 15 };
+struct paper_size
+{
+ int width;
+ int height;
+};
+static const struct paper_size paper_sizes[] = {
+ {210, 297}, /* User defined, default=A4 */
+ {54, 90}, /* Business card */
+ /*{80, 170}, *//* Check (China business) */
+ /*{297, 420}, *//* A3 */
+ {210, 297}, /* A4 */
+ {148, 210}, /* A5 */
+ {105, 148}, /* A6 */
+ {215, 280}, /* US Letter 8.5 x 11 in */
+ /*{280, 432}, *//* Double Letter 11 x 17 in */
+ /*{250, 353}, *//* B4 */
+ {176, 250}, /* B5 */
+ {125, 176}, /* B6 */
+ {215, 355} /* US Legal */
+};
+
+#define MIN_WIDTH 51
+#define MAX_WIDTH 215
+#define MIN_LENGTH 70
+#define MAX_LENGTH 355
+static SANE_Range tl_x_range = { 0, MAX_WIDTH - MIN_WIDTH, 0 };
+static SANE_Range tl_y_range = { 0, MAX_LENGTH - MIN_LENGTH, 0 };
+static SANE_Range br_x_range = { MIN_WIDTH, MAX_WIDTH, 0 };
+static SANE_Range br_y_range = { MIN_LENGTH, MAX_LENGTH, 0 };
+static SANE_Range byte_value_range = { 0, 255, 0 };
+
+/* List of image emphasis options, 5 steps */
+static SANE_String_Const image_emphasis_list[] = {
+ SANE_I18N ("none"),
+ SANE_I18N ("low"),
+ SANE_I18N ("medium"),
+ SANE_I18N ("high"),
+ SANE_I18N ("smooth"),
+ NULL
+};
+
+/* List of gamma */
+static SANE_String_Const gamma_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("crt"),
+ NULL
+};
+static unsigned gamma_val[] = { 0, 1 };
+
+/* List of lamp color dropout */
+static SANE_String_Const lamp_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("red"),
+ SANE_I18N ("green"),
+ SANE_I18N ("blue"),
+ NULL
+};
+
+/* Reset the options for that scanner. */
+void
+kvs20xx_init_options (struct scanner *s)
+{
+ int i;
+ SANE_Option_Descriptor *o;
+ /* Pre-initialize the options. */
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; i++)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ o = &s->opt[NUM_OPTS];
+ o->name = "";
+ o->title = SANE_TITLE_NUM_OPTIONS;
+ o->desc = SANE_DESC_NUM_OPTIONS;
+ o->type = SANE_TYPE_INT;
+ o->cap = SANE_CAP_SOFT_DETECT;
+ s->val[NUM_OPTS].w = NUM_OPTIONS;
+
+ /* Mode group */
+ o = &s->opt[MODE_GROUP];
+ o->title = SANE_I18N ("Scan Mode");
+ o->desc = ""; /* not valid for a group */
+ o->type = SANE_TYPE_GROUP;
+ o->cap = 0;
+ o->size = 0;
+ o->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ o = &s->opt[MODE];
+ o->name = SANE_NAME_SCAN_MODE;
+ o->title = SANE_TITLE_SCAN_MODE;
+ o->desc = SANE_DESC_SCAN_MODE;
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (mode_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = mode_list;
+ s->val[MODE].s = malloc (o->size);
+ strcpy (s->val[MODE].s, mode_list[0]);
+
+ /* X and Y resolution */
+ o = &s->opt[RESOLUTION];
+ o->name = SANE_NAME_SCAN_RESOLUTION;
+ o->title = SANE_TITLE_SCAN_RESOLUTION;
+ o->desc = SANE_DESC_SCAN_RESOLUTION;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_DPI;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &resolutions_range;
+ s->val[RESOLUTION].w = 100;
+
+ /* Duplex */
+ o = &s->opt[DUPLEX];
+ o->name = "duplex";
+ o->title = SANE_I18N ("Duplex");
+ o->desc = SANE_I18N ("Enable Duplex (Dual-Sided) Scanning");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DUPLEX].w = SANE_FALSE;
+
+ /*FIXME
+ if (!s->support_info.support_duplex)
+ o->cap |= SANE_CAP_INACTIVE;
+ */
+
+ /* Feeder mode */
+ o = &s->opt[FEEDER_MODE];
+ o->name = "feeder-mode";
+ o->title = SANE_I18N ("Feeder mode");
+ o->desc = SANE_I18N ("Sets the feeding mode");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (feeder_mode_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = feeder_mode_list;
+ s->val[FEEDER_MODE].s = malloc (o->size);
+ strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]);
+
+ /* Length control */
+ o = &s->opt[LENGTHCTL];
+ o->name = "length-control";
+ o->title = SANE_I18N ("Length control mode");
+ o->desc =
+ SANE_I18N
+ ("Length Control Mode is a mode that the scanner reads up to the shorter length of actual"
+ " paper or logical document length.");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[LENGTHCTL].w = SANE_FALSE;
+
+ /* Manual feed */
+ o = &s->opt[MANUALFEED];
+ o->name = "manual-feed";
+ o->title = SANE_I18N ("Manual feed mode");
+ o->desc = SANE_I18N ("Sets the manual feed mode");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (manual_feed_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = manual_feed_list;
+ s->val[MANUALFEED].s = malloc (o->size);
+ strcpy (s->val[MANUALFEED].s, manual_feed_list[0]);
+
+ /*Manual feed timeout */
+ o = &s->opt[FEED_TIMEOUT];
+ o->name = "feed-timeout";
+ o->title = SANE_I18N ("Manual feed timeout");
+ o->desc = SANE_I18N ("Sets the manual feed timeout in seconds");
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[FEED_TIMEOUT].w = 30;
+
+ /* Double feed */
+ o = &s->opt[DBLFEED];
+ o->name = "double-feed";
+ o->title = SANE_I18N ("Double feed detection");
+ o->desc = SANE_I18N ("Enable/Disable double feed detection");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DBLFEED].w = SANE_FALSE;
+
+
+ /* Fit to page */
+ o = &s->opt[FIT_TO_PAGE];
+ o->name = SANE_I18N ("fit-to-page");
+ o->title = SANE_I18N ("Fit to page");
+ o->desc = SANE_I18N ("Scanner shrinks image to fit scanned page");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[FIT_TO_PAGE].w = SANE_FALSE;
+
+ /* Geometry group */
+ o = &s->opt[GEOMETRY_GROUP];
+ o->title = SANE_I18N ("Geometry");
+ o->desc = ""; /* not valid for a group */
+ o->type = SANE_TYPE_GROUP;
+ o->cap = 0;
+ o->size = 0;
+ o->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Paper sizes list */
+ o = &s->opt[PAPER_SIZE];
+ o->name = "paper-size";
+ o->title = SANE_I18N ("Paper size");
+ o->desc = SANE_I18N ("Physical size of the paper in the ADF");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (paper_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = paper_list;
+ s->val[PAPER_SIZE].s = malloc (o->size);
+ strcpy (s->val[PAPER_SIZE].s, SANE_I18N ("A4"));
+
+ /* Landscape */
+ o = &s->opt[LANDSCAPE];
+ o->name = "landscape";
+ o->title = SANE_I18N ("Landscape");
+ o->desc =
+ SANE_I18N ("Set paper position : "
+ "true for landscape, false for portrait");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[LANDSCAPE].w = SANE_FALSE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Upper left X */
+ o = &s->opt[TL_X];
+ o->name = SANE_NAME_SCAN_TL_X;
+ o->title = SANE_TITLE_SCAN_TL_X;
+ o->desc = SANE_DESC_SCAN_TL_X;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &tl_x_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[TL_X].w = 0;
+
+ /* Upper left Y */
+ o = &s->opt[TL_Y];
+ o->name = SANE_NAME_SCAN_TL_Y;
+ o->title = SANE_TITLE_SCAN_TL_Y;
+ o->desc = SANE_DESC_SCAN_TL_Y;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &tl_y_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[TL_Y].w = 0;
+
+ /* Bottom-right x */
+ o = &s->opt[BR_X];
+ o->name = SANE_NAME_SCAN_BR_X;
+ o->title = SANE_TITLE_SCAN_BR_X;
+ o->desc = SANE_DESC_SCAN_BR_X;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &br_x_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[BR_X].w = 210;
+
+ /* Bottom-right y */
+ o = &s->opt[BR_Y];
+ o->name = SANE_NAME_SCAN_BR_Y;
+ o->title = SANE_TITLE_SCAN_BR_Y;
+ o->desc = SANE_DESC_SCAN_BR_Y;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &br_y_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[BR_Y].w = 297;
+
+ /* Enhancement group */
+ o = &s->opt[ADVANCED_GROUP];
+ o->title = SANE_I18N ("Advanced");
+ o->desc = ""; /* not valid for a group */
+ o->type = SANE_TYPE_GROUP;
+ o->cap = SANE_CAP_ADVANCED;
+ o->size = 0;
+ o->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Brightness */
+ o = &s->opt[BRIGHTNESS];
+ o->name = SANE_NAME_BRIGHTNESS;
+ o->title = SANE_TITLE_BRIGHTNESS;
+ o->desc = SANE_DESC_BRIGHTNESS;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[BRIGHTNESS].w = 128;
+
+ /* Contrast */
+ o = &s->opt[CONTRAST];
+ o->name = SANE_NAME_CONTRAST;
+ o->title = SANE_TITLE_CONTRAST;
+ o->desc = SANE_DESC_CONTRAST;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[CONTRAST].w = 128;
+
+ /* threshold */
+ o = &s->opt[THRESHOLD];
+ o->name = SANE_NAME_THRESHOLD;
+ o->title = SANE_TITLE_THRESHOLD;
+ o->desc = SANE_DESC_THRESHOLD;
+ o->type = SANE_TYPE_INT;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[THRESHOLD].w = 128;
+
+
+ /* Image emphasis */
+ o = &s->opt[IMAGE_EMPHASIS];
+ o->name = "image-emphasis";
+ o->title = SANE_I18N ("Image emphasis");
+ o->desc = SANE_I18N ("Sets the image emphasis");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (image_emphasis_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = image_emphasis_list;
+ s->val[IMAGE_EMPHASIS].s = malloc (o->size);
+ strcpy (s->val[IMAGE_EMPHASIS].s, image_emphasis_list[0]);
+
+ /* Gamma */
+ o = &s->opt[GAMMA_CORRECTION];
+ o->name = "gamma-cor";
+ o->title = SANE_I18N ("Gamma correction");
+ o->desc = SANE_I18N ("Gamma correction");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (gamma_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = gamma_list;
+ s->val[GAMMA_CORRECTION].s = malloc (o->size);
+ strcpy (s->val[GAMMA_CORRECTION].s, gamma_list[0]);
+
+ /* Lamp color dropout */
+ o = &s->opt[LAMP];
+ o->name = "lamp-color";
+ o->title = SANE_I18N ("Lamp color");
+ o->desc = SANE_I18N ("Sets the lamp color (color dropout)");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (lamp_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = lamp_list;
+ s->val[LAMP].s = malloc (o->size);
+ strcpy (s->val[LAMP].s, lamp_list[0]);
+
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+str_index (const SANE_String_Const * list, SANE_String_Const name)
+{
+ int index;
+ index = 0;
+ while (list[index])
+ {
+ if (!strcmp (list[index], name))
+ return (index);
+ index++;
+ }
+ return (-1); /* not found */
+}
+
+/* Control option */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ int i;
+ SANE_Status status;
+ SANE_Word cap;
+ struct scanner *s = (struct scanner *) handle;
+
+ if (info)
+ *info = 0;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_UNSUPPORTED;
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ if (s->opt[option].type == SANE_TYPE_STRING)
+ {
+ DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %s\n",
+ option, s->val[option].s);
+ strcpy (val, s->val[option].s);
+ }
+ else
+ {
+ *(SANE_Word *) val = s->val[option].w;
+ DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %d\n",
+ option, s->val[option].w);
+ }
+ return SANE_STATUS_GOOD;
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (s->opt[option].type == SANE_TYPE_STRING)
+ {
+ if (!strcmp (val, s->val[option].s))
+ return SANE_STATUS_GOOD;
+ DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %s\n",
+ option, (SANE_String_Const) val);
+ }
+ else
+ {
+ if (*(SANE_Word *) val == s->val[option].w)
+ return SANE_STATUS_GOOD;
+ DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %d\n",
+ option, *(SANE_Word *) val);
+ }
+
+ switch (option)
+ {
+ /* Side-effect options */
+ case RESOLUTION:
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case TL_Y:
+ if ((*(SANE_Word *) val) + MIN_LENGTH <= s->val[BR_Y].w)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT;
+ return SANE_STATUS_GOOD;
+ case BR_Y:
+ if ((*(SANE_Word *) val) >= s->val[TL_Y].w + MIN_LENGTH)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT;
+ return SANE_STATUS_GOOD;
+
+ case TL_X:
+ if ((*(SANE_Word *) val) + MIN_WIDTH <= s->val[BR_X].w)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT;
+ return SANE_STATUS_GOOD;
+
+ case BR_X:
+ if (*(SANE_Word *) val >= s->val[TL_X].w + MIN_WIDTH)
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT;
+ return SANE_STATUS_GOOD;
+
+ case LANDSCAPE:
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ /* Side-effect free options */
+ case CONTRAST:
+ case BRIGHTNESS:
+ case DUPLEX:
+ case LENGTHCTL:
+ case DBLFEED:
+ case FIT_TO_PAGE:
+ case THRESHOLD:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case FEED_TIMEOUT:
+ s->val[option].w = *(SANE_Word *) val;
+ return kvs20xx_set_timeout (s, s->val[option].w);
+
+ /* String mode */
+ case IMAGE_EMPHASIS:
+ case GAMMA_CORRECTION:
+ case LAMP:
+ case FEEDER_MODE:
+ strcpy (s->val[option].s, val);
+ return SANE_STATUS_GOOD;
+
+ case MODE:
+ strcpy (s->val[MODE].s, val);
+ if (!strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ s->opt[THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[GAMMA_CORRECTION].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ case MANUALFEED:
+ strcpy (s->val[option].s, val);
+ if (strcmp (s->val[option].s, manual_feed_list[0]) == 0) /* off */
+ s->opt[FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE;
+ else
+ s->opt[FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case PAPER_SIZE:
+ strcpy (s->val[PAPER_SIZE].s, val);
+ i = str_index (paper_list, s->val[PAPER_SIZE].s);
+ if (i == 0)
+ { /*user def */
+ s->opt[TL_X].cap &=
+ s->opt[TL_Y].cap &=
+ s->opt[BR_X].cap &= s->opt[BR_Y].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ s->val[LANDSCAPE].w = 0;
+ }
+ else
+ {
+ s->opt[TL_X].cap |=
+ s->opt[TL_Y].cap |=
+ s->opt[BR_X].cap |= s->opt[BR_Y].cap |= SANE_CAP_INACTIVE;
+ if (i == 3 || i == 4 || i == 7)
+ { /*A5, A6 or B6 */
+ s->opt[LANDSCAPE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ s->val[LANDSCAPE].w = 0;
+ }
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+static inline unsigned
+mm2scanner_units (unsigned mm)
+{
+ return mm * 12000 / 254;
+}
+static inline unsigned
+scanner_units2mm (unsigned u)
+{
+ return u * 254 / 12000;
+}
+
+void
+kvs20xx_init_window (struct scanner *s, struct window *wnd, int wnd_id)
+{
+ int paper = str_index (paper_list, s->val[PAPER_SIZE].s);
+ memset (wnd, 0, sizeof (struct window));
+ wnd->window_descriptor_block_length = cpu2be16 (64);
+
+ wnd->window_identifier = wnd_id;
+ wnd->x_resolution = cpu2be16 (s->val[RESOLUTION].w);
+ wnd->y_resolution = cpu2be16 (s->val[RESOLUTION].w);
+ if (!paper)
+ {
+ wnd->upper_left_x =
+ cpu2be32 (mm2scanner_units (s->val[TL_X].w));
+ wnd->upper_left_y =
+ cpu2be32 (mm2scanner_units (s->val[TL_Y].w));
+ wnd->width =
+ cpu2be32 (mm2scanner_units (s->val[BR_X].w - s->val[TL_X].w));
+ wnd->length =
+ cpu2be32 (mm2scanner_units (s->val[BR_Y].w - s->val[TL_Y].w));
+ }
+ else
+ {
+ u32 w = cpu2be32 (mm2scanner_units (paper_sizes[paper].width));
+ u32 h = cpu2be32 (mm2scanner_units (paper_sizes[paper].height));
+ wnd->upper_left_x = cpu2be32 (mm2scanner_units (0));
+ wnd->upper_left_y = cpu2be32 (mm2scanner_units (0));
+ if (!s->val[LANDSCAPE].b)
+ {
+ wnd->document_width = wnd->width = w;
+ wnd->document_length = wnd->length = h;
+ }
+ else
+ {
+ wnd->document_width = wnd->width = h;
+ wnd->document_length = wnd->length = w;
+ }
+ }
+ wnd->brightness = s->val[BRIGHTNESS].w;
+ wnd->threshold = s->val[THRESHOLD].w;
+ wnd->contrast = s->val[CONTRAST].w;
+ wnd->image_composition = mode_val[str_index (mode_list, s->val[MODE].s)];
+ wnd->bit_per_pixel = bps_val[str_index (mode_list, s->val[MODE].s)];
+ wnd->halftone_pattern = 0; /*Does not supported */
+ wnd->bit_ordering = cpu2be16 (BIT_ORDERING);
+ wnd->compression_type = 0; /*Does not supported */
+ wnd->compression_argument = 0; /*Does not supported */
+
+ wnd->vendor_unique_identifier = 0;
+ wnd->nobuf_fstspeed_dfstop = 0;
+ wnd->mirror_image = 0;
+ wnd->image_emphasis = str_index (image_emphasis_list,
+ s->val[IMAGE_EMPHASIS].s);
+ wnd->gamma_correction = gamma_val[str_index (gamma_list,
+ s->val[GAMMA_CORRECTION].s)];
+ wnd->mcd_lamp_dfeed_sens = str_index (lamp_list, s->val[LAMP].s) << 4 | 2;
+
+ wnd->document_size = (paper != 0) << 7
+ | s->val[LENGTHCTL].b << 6 | s->val[LANDSCAPE].b << 4 | paper_val[paper];
+
+ wnd->ahead_deskew_dfeed_scan_area_fspeed_rshad = s->val[DBLFEED].b << 4
+ | s->val[FIT_TO_PAGE].b << 2;
+ wnd->continuous_scanning_pages = str_index (feeder_mode_list,
+ s->val[FEEDER_MODE].
+ s) ? 0xff : 0;
+ wnd->automatic_threshold_mode = 0; /*Does not supported */
+ wnd->automatic_separation_mode = 0; /*Does not supported */
+ wnd->standard_white_level_mode = 0; /*Does not supported */
+ wnd->b_wnr_noise_reduction = 0; /*Does not supported */
+ if (str_index (manual_feed_list, s->val[MANUALFEED].s) == 2)
+ wnd->mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr = 2 << 6;
+
+ wnd->stop_mode = 1;
+}
+
+
+/* Get scan parameters */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Parameters *p = &s->params;
+
+ if (!s->scanning)
+ {
+ unsigned w, h, res = s->val[RESOLUTION].w;
+ unsigned i = str_index (paper_list,
+ s->val[PAPER_SIZE].s);
+ if (i)
+ {
+ if (s->val[LANDSCAPE].b)
+ {
+ w = paper_sizes[i].height;
+ h = paper_sizes[i].width;
+ }
+ else
+ {
+ w = paper_sizes[i].width;
+ h = paper_sizes[i].height;
+ }
+ }
+ else
+ {
+ w = s->val[BR_X].w - s->val[TL_X].w;
+ h = s->val[BR_Y].w - s->val[TL_Y].w;
+ }
+ p->pixels_per_line = w * res / 25.4;
+ p->lines = h * res / 25.4;
+ }
+
+ p->format = (!strcmp(s->val[MODE].s,SANE_VALUE_SCAN_MODE_COLOR)) ?
+ SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ p->last_frame = SANE_TRUE;
+ p->depth = bps_val[str_index (mode_list, s->val[MODE].s)];
+ p->bytes_per_line = p->depth * p->pixels_per_line / 8;
+ if (p->depth > 8)
+ p->depth = 8;
+ if (params)
+ memcpy (params, p, sizeof (SANE_Parameters));
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/kvs40xx.c b/backend/kvs40xx.c
new file mode 100644
index 0000000..6c19e55
--- /dev/null
+++ b/backend/kvs40xx.c
@@ -0,0 +1,748 @@
+/*
+ Copyright (C) 2009, Panasonic Russia Ltd.
+ Copyright (C) 2010,2011, m. allan noah
+*/
+/*
+ Panasonic KV-S40xx USB-SCSI scanner driver.
+*/
+
+#include "../include/sane/config.h"
+
+#include <ctype.h> /*isspace*/
+#include <math.h> /*tan*/
+
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#define DEBUG_NOT_STATIC
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_scsi.h"
+#include "lassert.h"
+
+#include "kvs40xx.h"
+
+#include "sane/sanei_debug.h"
+
+#define DATA_TAIL 0x200
+
+struct known_device
+{
+ const SANE_Int id;
+ const SANE_Device scanner;
+};
+
+static const struct known_device known_devices[] = {
+ {
+ KV_S4085C,
+ {
+ "MATSHITA",
+ "KV-S4085C",
+ "High Speed Color ADF Scanner",
+ "scanner"
+ },
+ },
+ {
+ KV_S4065C,
+ {
+ "MATSHITA",
+ "KV-S4065C",
+ "High Speed Color ADF Scanner",
+ "scanner"
+ },
+ },
+ {
+ KV_S7075C,
+ {
+ "MATSHITA",
+ "KV-S7075C",
+ "High Speed Color ADF Scanner",
+ "scanner"
+ },
+ },
+};
+
+static inline SANE_Status buf_init(struct buf *b, SANE_Int sz)
+{
+ const int num = sz / BUF_SIZE + 1;
+ b->buf = (u8 **) realloc(b->buf, num * sizeof(u8 *));
+ if (!b->buf)
+ return SANE_STATUS_NO_MEM;
+ memset(b->buf, 0, num * sizeof(void *));
+ b->size = b->head = b->tail = 0;
+ b->sem = 0;
+ b->st = SANE_STATUS_GOOD;
+ pthread_cond_init(&b->cond, NULL);
+ pthread_mutex_init(&b->mu, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+static inline void buf_deinit(struct buf *b)
+{
+ int i;
+ if (!b->buf)
+ return;
+ for (i = b->head; i < b->tail; i++)
+ if (b->buf[i])
+ free(b->buf[i]);
+ free(b->buf);
+ b->buf = NULL;
+ b->head = b->tail = 0;
+}
+
+static inline SANE_Status new_buf(struct buf *b, u8 ** p)
+{
+ b->buf[b->tail] = (u8 *) malloc(BUF_SIZE);
+ if (!b->buf[b->tail])
+ return SANE_STATUS_NO_MEM;
+ *p = b->buf[b->tail];
+ ++b->tail;
+ return SANE_STATUS_GOOD;
+}
+
+static inline SANE_Status buf_get_err(struct buf *b)
+{
+ return b->size ? SANE_STATUS_GOOD : b->st;
+}
+
+static inline void buf_set_st(struct buf *b, SANE_Status st)
+{
+ pthread_mutex_lock(&b->mu);
+ b->st = st;
+ if (buf_get_err(b))
+ pthread_cond_signal(&b->cond);
+ pthread_mutex_unlock(&b->mu);
+}
+
+static inline void buf_cancel(struct buf *b)
+{
+ buf_set_st(b, SANE_STATUS_CANCELLED);
+}
+
+static inline void push_buf(struct buf *b, SANE_Int sz)
+{
+ pthread_mutex_lock(&b->mu);
+ b->sem++;
+ b->size += sz;
+ pthread_cond_signal(&b->cond);
+ pthread_mutex_unlock(&b->mu);
+}
+
+static inline u8 *get_buf(struct buf *b, SANE_Int * sz)
+{
+ SANE_Status err = buf_get_err(b);
+ if (err)
+ return NULL;
+
+ pthread_mutex_lock(&b->mu);
+ while (!b->sem && !buf_get_err(b))
+ pthread_cond_wait(&b->cond, &b->mu);
+ b->sem--;
+ err = buf_get_err(b);
+ if (!err) {
+ *sz = b->size < BUF_SIZE ? b->size : BUF_SIZE;
+ b->size -= *sz;
+ }
+ pthread_mutex_unlock(&b->mu);
+ return err ? NULL : b->buf[b->head];
+}
+
+static inline void pop_buf(struct buf *b)
+{
+ free(b->buf[b->head]);
+ b->buf[b->head] = NULL;
+ ++b->head;
+}
+
+SANE_Status
+sane_init (SANE_Int __sane_unused__ * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ DBG_INIT ();
+ DBG (DBG_INFO, "This is panasonic kvs40xx driver\n");
+
+ *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 1);
+
+ /* Initialize USB */
+ sanei_usb_init ();
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * List of available devices, allocated by sane_get_devices, released
+ * by sane_exit()
+ */
+static SANE_Device **devlist = NULL;
+static unsigned curr_scan_dev = 0;
+
+void
+sane_exit (void)
+{
+ if (devlist)
+ {
+ int i;
+ for (i = 0; devlist[i]; i++)
+ {
+ free ((void *) devlist[i]);
+ }
+ free ((void *) devlist);
+ devlist = NULL;
+ }
+}
+
+SANE_Status
+attach (SANE_String_Const devname);
+
+SANE_Status
+attach (SANE_String_Const devname)
+{
+ int i = 0;
+ if (devlist)
+ {
+ for (; devlist[i]; i++);
+ devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ devlist = malloc (sizeof (SANE_Device *) * 2);
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ }
+ devlist[i] = malloc (sizeof (SANE_Device));
+ if (!devlist[i])
+ return SANE_STATUS_NO_MEM;
+ memcpy (devlist[i], &known_devices[curr_scan_dev].scanner,
+ sizeof (SANE_Device));
+ devlist[i]->name = strdup (devname);
+ /* terminate device list with NULL entry: */
+ devlist[i + 1] = 0;
+ DBG (DBG_INFO, "%s device attached\n", devname);
+ return SANE_STATUS_GOOD;
+}
+
+/* Get device list */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ if (devlist)
+ {
+ int i;
+ for (i = 0; devlist[i]; i++)
+ {
+ free ((void *) devlist[i]);
+ }
+ free ((void *) devlist);
+ devlist = NULL;
+ }
+
+ for (curr_scan_dev = 0;
+ curr_scan_dev <
+ sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++)
+ {
+ sanei_usb_find_devices (PANASONIC_ID,
+ known_devices[curr_scan_dev].id, attach);
+ }
+
+ for (curr_scan_dev = 0;
+ curr_scan_dev <
+ sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++)
+ {
+ sanei_scsi_find_devices (known_devices[curr_scan_dev].
+ scanner.vendor,
+ known_devices[curr_scan_dev].
+ scanner.model, NULL, -1, -1, -1, -1, attach);
+ }
+ if(device_list)
+ *device_list = (const SANE_Device **) devlist;
+ return SANE_STATUS_GOOD;
+}
+
+/* Open device, return the device handle */
+SANE_Status
+sane_open (SANE_String_Const devname, SANE_Handle * handle)
+{
+ unsigned i, j, id = 0;
+ struct scanner *s;
+ SANE_Int h, bus;
+ SANE_Status st = SANE_STATUS_GOOD;
+ if (!devlist)
+ {
+ st = sane_get_devices (NULL, 0);
+ if (st)
+ return st;
+ }
+ for (i = 0; devlist[i]; i++)
+ {
+ if (!strcmp (devlist[i]->name, devname))
+ break;
+ }
+ if (!devlist[i])
+ return SANE_STATUS_INVAL;
+ for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++)
+ {
+ if (!strcmp (devlist[i]->model, known_devices[j].scanner.model))
+ {
+ id = known_devices[j].id;
+ break;
+ }
+ }
+
+ st = sanei_usb_open (devname, &h);
+
+ if (st == SANE_STATUS_ACCESS_DENIED)
+ return st;
+ if (st)
+ {
+ st = sanei_scsi_open (devname, &h, kvs40xx_sense_handler, NULL);
+ if (st)
+ {
+ return st;
+ }
+ bus = SCSI;
+ }
+ else
+ {
+ bus = USB;
+ st = sanei_usb_claim_interface (h, 0);
+ if (st)
+ {
+ sanei_usb_close (h);
+ return st;
+ }
+ }
+
+ s = malloc (sizeof (struct scanner));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (struct scanner));
+ s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE);
+ if (!s->buffer)
+ return SANE_STATUS_NO_MEM;
+
+ s->file = h;
+ s->bus = bus;
+ s->id = id;
+ strcpy (s->name, devname);
+ *handle = s;
+ for (i = 0; i < 3; i++)
+ {
+ st = kvs40xx_test_unit_ready (s);
+ if (st)
+ {
+ if (s->bus == SCSI)
+ {
+ sanei_scsi_close (s->file);
+ st = sanei_scsi_open (devname, &h, kvs40xx_sense_handler, NULL);
+ if (st)
+ return st;
+ }
+ else
+ {
+ sanei_usb_release_interface (s->file, 0);
+ sanei_usb_close (s->file);
+ st = sanei_usb_open (devname, &h);
+ if (st)
+ return st;
+ st = sanei_usb_claim_interface (h, 0);
+ if (st)
+ {
+ sanei_usb_close (h);
+ return st;
+ }
+ }
+ s->file = h;
+ }
+ else
+ break;
+ }
+ if (i == 3)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (id == KV_S4085C || id == KV_S4065C)
+ {
+ char str[16];
+ st = inquiry (s, str);
+ if (st)
+ goto err;
+ if (id == KV_S4085C)
+ s->id = !strcmp (str, "KV-S4085CL") ? KV_S4085CL : KV_S4085CW;
+ else
+ s->id = !strcmp (str, "KV-S4065CL") ? KV_S4065CL : KV_S4065CW;
+ }
+ kvs40xx_init_options (s);
+ st = kvs40xx_set_timeout (s, s->val[FEED_TIMEOUT].w);
+ if (st)
+ goto err;
+
+ return SANE_STATUS_GOOD;
+err:
+ sane_close (s);
+ return st;
+}
+
+/* Close device */
+void
+sane_close (SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ unsigned i;
+ hopper_down (s);
+ if (s->bus == USB)
+ {
+ sanei_usb_release_interface (s->file, 0);
+ sanei_usb_close (s->file);
+ }
+ else
+ sanei_scsi_close (s->file);
+
+ for (i = 1; i < NUM_OPTIONS; i++)
+ {
+ if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s)
+ free (s->val[i].s);
+ }
+
+ for (i = 0; i < sizeof (s->buf) / sizeof (s->buf[0]); i++)
+ buf_deinit (&s->buf[i]);
+
+ free (s->buffer);
+ free (s);
+
+}
+
+/* Get option descriptor */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS || option < 0)
+ return NULL;
+ return s->opt + option;
+}
+
+static SANE_Status
+wait_document (struct scanner *s)
+{
+ SANE_Status st;
+ int i;
+ if (!strcmp ("fb", s->val[SOURCE].s))
+ return SANE_STATUS_GOOD;
+ if (!strcmp ("off", s->val[MANUALFEED].s))
+ return kvs40xx_document_exist (s);
+
+ for (i = 0; i < s->val[FEED_TIMEOUT].w; i++)
+ {
+ st = kvs40xx_document_exist (s);
+ if (st != SANE_STATUS_NO_DOCS)
+ return st;
+ sleep (1);
+ }
+ return SANE_STATUS_NO_DOCS;
+}
+
+static SANE_Status read_image_duplex(SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status st = SANE_STATUS_GOOD;
+ unsigned read, side;
+ int i;
+ struct side {
+ unsigned mx, eof;
+ u8 *p;
+ struct buf *buf;
+ } a[2], *b;
+
+ for (i = 0; i < 2; i++) {
+ a[i].mx = BUF_SIZE;
+ a[i].eof = 0;
+ a[i].buf = &s->buf[i];
+ st = new_buf(&s->buf[i], &a[i].p);
+ if (st)
+ goto err;
+ }
+ for (b = &a[0], side = SIDE_FRONT; (!a[0].eof || !a[1].eof);) {
+ pthread_testcancel();
+ if (b->mx == 0) {
+ push_buf(b->buf, BUF_SIZE);
+ st = new_buf(b->buf, &b->p);
+ if (st)
+ goto err;
+ b->mx = BUF_SIZE;
+ }
+
+ st = kvs40xx_read_image_data(s, s->page, side,
+ b->p + BUF_SIZE - b->mx, b->mx,
+ &read);
+ b->mx -= read;
+ if (st) {
+ if (st != INCORRECT_LENGTH
+ && st != SANE_STATUS_EOF)
+ goto err;
+
+ if (st == SANE_STATUS_EOF) {
+ b->eof = 1;
+ push_buf(b->buf, BUF_SIZE - b->mx);
+ }
+ side ^= SIDE_BACK;
+ b = &a[side == SIDE_FRONT ? 0 : 1];
+ }
+ }
+
+ err:
+ for (i = 0; i < 2; i++)
+ buf_set_st(&s->buf[i], st);
+ return st;
+}
+
+static SANE_Status read_image_simplex(SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status st = SANE_STATUS_GOOD;
+
+ for (; (!st || st == INCORRECT_LENGTH);) {
+ unsigned read, mx;
+ unsigned char *p = NULL;
+ st = new_buf(&s->buf[0], &p);
+ for (read = 0, mx = BUF_SIZE; mx &&
+ (!st || st == INCORRECT_LENGTH); mx -= read) {
+ pthread_testcancel();
+ st = kvs40xx_read_image_data(s, s->page, SIDE_FRONT,
+ p + BUF_SIZE - mx, mx,
+ &read);
+ }
+ push_buf(&s->buf[0], BUF_SIZE - mx);
+ }
+ buf_set_st(&s->buf[0], st);
+ return st;
+}
+
+static SANE_Status read_data(struct scanner *s)
+{
+ SANE_Status st;
+ int duplex = s->val[DUPLEX].w;
+ s->read = 0;
+ s->side = SIDE_FRONT;
+
+ st = duplex ? read_image_duplex(s) : read_image_simplex(s);
+ if (st && (st != SANE_STATUS_EOF))
+ goto err;
+
+ st = kvs40xx_read_picture_element(s, SIDE_FRONT, &s->params);
+ if (st)
+ goto err;
+ if (!s->params.lines) {
+ st = SANE_STATUS_INVAL;
+ goto err;
+ }
+
+ sane_get_parameters(s, NULL);
+
+ s->page++;
+ return SANE_STATUS_GOOD;
+ err:
+ s->scanning = 0;
+ return st;
+}
+
+/* Start scanning */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status st = SANE_STATUS_GOOD;
+ int duplex = s->val[DUPLEX].w, i;
+ unsigned data_avalible;
+ int start = 0;
+
+ if (s->thread)
+ {
+ pthread_join (s->thread, NULL);
+ s->thread = 0;
+ }
+ if (!s->scanning)
+ {
+ st = kvs40xx_test_unit_ready (s);
+ if (st)
+ return st;
+
+ st = wait_document (s);
+ if (st)
+ return st;
+
+ st = kvs40xx_reset_window (s);
+ if (st)
+ return st;
+ st = kvs40xx_set_window (s, SIDE_FRONT);
+
+ if (st)
+ return st;
+
+ if (duplex)
+ {
+ st = kvs40xx_set_window (s, SIDE_BACK);
+ if (st)
+ return st;
+ }
+
+ st = kvs40xx_scan (s);
+ if (st)
+ return st;
+
+ if (s->val[CROP].b || s->val[LENGTHCTL].b || s->val[LONG_PAPER].b)
+ {
+ unsigned w, h, res = s->val[RESOLUTION].w;
+ SANE_Parameters *p = &s->params;
+ w = 297; /*A3 */
+ h = 420;
+ p->pixels_per_line = w * res / 25.4 + .5;
+ p->lines = h * res / 25.4 + .5;
+ }
+ else
+ {
+ st = kvs40xx_read_picture_element (s, SIDE_FRONT, &s->params);
+ if (st)
+ return st;
+ }
+
+ start = 1;
+ s->scanning = 1;
+ s->page = 0;
+ s->read = 0;
+ s->side = SIDE_FRONT;
+ sane_get_parameters (s, NULL);
+ }
+
+ if (duplex && s->side == SIDE_FRONT && !start)
+ {
+ s->side = SIDE_BACK;
+ s->read = 0;
+ return SANE_STATUS_GOOD;
+ }
+ do {
+ st = get_buffer_status(s, &data_avalible);
+ if (st)
+ goto err;
+
+ } while (!data_avalible);
+
+ for (i = 0; i < (duplex ? 2 : 1); i++)
+ {
+ st = buf_init (&s->buf[i], s->side_size);
+ if (st)
+ goto err;
+ }
+
+ if (pthread_create (&s->thread, NULL, (void *(*)(void *)) read_data, s))
+ {
+ st = SANE_STATUS_IO_ERROR;
+ goto err;
+ }
+
+ if (s->val[CROP].b || s->val[LENGTHCTL].b || s->val[LONG_PAPER].b)
+ {
+ pthread_join (s->thread, NULL);
+ s->thread = 0;
+ }
+
+ return SANE_STATUS_GOOD;
+err:
+ s->scanning = 0;
+ return st;
+}
+
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ int duplex = s->val[DUPLEX].w;
+ struct buf *b = s->side == SIDE_FRONT ? &s->buf[0] : &s->buf[1];
+ SANE_Status err = buf_get_err(b);
+ SANE_Int inbuf = 0;
+ *len = 0;
+
+ if (!s->scanning)
+ return SANE_STATUS_EOF;
+ if (err)
+ goto out;
+
+ if (s->read) {
+ *len =
+ max_len <
+ (SANE_Int) s->read ? max_len : (SANE_Int) s->read;
+ memcpy(buf, s->data + BUF_SIZE - s->read, *len);
+ s->read -= *len;
+
+ if (!s->read)
+ pop_buf(b);
+ goto out;
+ }
+
+ s->data = get_buf(b, &inbuf);
+ if (!s->data)
+ goto out;
+
+ *len = max_len < inbuf ? max_len : inbuf;
+ if (*len > BUF_SIZE)
+ *len = BUF_SIZE;
+ memcpy(buf, s->data, *len);
+ s->read = inbuf > BUF_SIZE ? BUF_SIZE - *len : inbuf - *len;
+
+ if (!s->read)
+ pop_buf(b);
+ out:
+ err = *len ? SANE_STATUS_GOOD : buf_get_err(b);
+ if (err == SANE_STATUS_EOF) {
+ if (strcmp(s->val[FEEDER_MODE].s, SANE_I18N("continuous"))) {
+ if (!duplex || s->side == SIDE_BACK)
+ s->scanning = 0;
+ }
+ buf_deinit(b);
+ } else if (err) {
+ unsigned i;
+ for (i = 0; i < sizeof(s->buf) / sizeof(s->buf[0]); i++)
+ buf_deinit(&s->buf[i]);
+ }
+ return err;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ unsigned i;
+ struct scanner *s = (struct scanner *) handle;
+ if (s->scanning && !strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous")))
+ {
+ stop_adf (s);
+ }
+ if (s->thread)
+ {
+ pthread_cancel (s->thread);
+ pthread_join (s->thread, NULL);
+ s->thread = 0;
+ }
+ for (i = 0; i < sizeof (s->buf) / sizeof (s->buf[0]); i++)
+ buf_deinit (&s->buf[i]);
+ s->scanning = 0;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ h,
+ SANE_Int __sane_unused__ * fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/kvs40xx.h b/backend/kvs40xx.h
new file mode 100644
index 0000000..fa17163
--- /dev/null
+++ b/backend/kvs40xx.h
@@ -0,0 +1,275 @@
+#ifndef __KVS40XX_H
+#define __KVS40XX_H
+
+/*
+ Copyright (C) 2009, Panasonic Russia Ltd.
+*/
+/*
+ Panasonic KV-S40xx USB-SCSI scanner driver.
+*/
+
+#include "../include/sane/config.h"
+#include <semaphore.h>
+
+#undef BACKEND_NAME
+#define BACKEND_NAME kvs40xx
+
+#define DBG_ERR 1
+#define DBG_WARN 2
+#define DBG_MSG 3
+#define DBG_INFO 4
+#define DBG_DBG 5
+
+#define PANASONIC_ID 0x04da
+#define KV_S4085C 0x100c
+#define KV_S4065C 0x100d
+#define KV_S7075C 0x100e
+
+#define KV_S4085CL (KV_S4085C|0x10000)
+#define KV_S4085CW (KV_S4085C|0x20000)
+#define KV_S4065CL (KV_S4065C|0x10000)
+#define KV_S4065CW (KV_S4065C|0x20000)
+
+#define USB 1
+#define SCSI 2
+#define BULK_HEADER_SIZE 12
+#define MAX_READ_DATA_SIZE (0x10000-0x100)
+#define BUF_SIZE MAX_READ_DATA_SIZE
+
+#define INCORRECT_LENGTH 0xfafafafa
+
+typedef unsigned char u8;
+typedef unsigned u32;
+typedef unsigned short u16;
+
+#define SIDE_FRONT 0x00
+#define SIDE_BACK 0x80
+
+/* options */
+typedef enum
+{
+ NUM_OPTS = 0,
+
+ /* General options */
+ MODE_GROUP,
+ MODE, /* scanner modes */
+ RESOLUTION, /* X and Y resolution */
+ SOURCE,
+
+ DUPLEX, /* Duplex mode */
+ FEEDER_MODE, /* Feeder mode, fixed to Continous */
+ LENGTHCTL, /* Length control mode */
+ LONG_PAPER,
+ MANUALFEED, /* Manual feed mode */
+ FEED_TIMEOUT, /* Feed timeout */
+ DBLFEED, /* Double feed detection mode */
+ DFEED_SENCE,
+ DFSTOP,
+ DFEED_L,
+ DFEED_C,
+ DFEED_R,
+ STAPELED_DOC, /* Detect stapled document */
+ FIT_TO_PAGE, /* Scanner shrinks image to fit scanned page */
+
+ /* Geometry group */
+ GEOMETRY_GROUP,
+ PAPER_SIZE, /* Paper size */
+ LANDSCAPE, /* true if landscape */
+ TL_X, /* upper left X */
+ TL_Y, /* upper left Y */
+ BR_X, /* bottom right X */
+ BR_Y, /* bottom right Y */
+
+ ADVANCED_GROUP,
+ BRIGHTNESS, /* Brightness */
+ CONTRAST, /* Contrast */
+ THRESHOLD, /* Binary threshold */
+ AUTOMATIC_THRESHOLD,
+ WHITE_LEVEL,
+ NOISE_REDUCTION,
+ INVERSE, /* Monochrome reversing */
+ IMAGE_EMPHASIS, /* Image emphasis */
+ GAMMA_CORRECTION, /* Gamma correction */
+ LAMP, /* Lamp -- color drop out */
+ RED_CHROMA,
+ BLUE_CHROMA,
+ HALFTONE_PATTERN, /* Halftone pattern */
+ COMPRESSION, /* JPEG Compression */
+ COMPRESSION_PAR, /* Compression parameter */
+ DESKEW,
+ STOP_SKEW,
+ CROP,
+ MIRROR,
+ BTMPOS,
+ TOPPOS,
+
+ /* must come last: */
+ NUM_OPTIONS
+} KV_OPTION;
+
+
+struct buf
+{
+ u8 **buf;
+ volatile int head;
+ volatile int tail;
+ volatile unsigned size;
+ volatile int sem;
+ volatile SANE_Status st;
+ pthread_mutex_t mu;
+ pthread_cond_t cond;
+};
+
+struct scanner
+{
+ char name[128];
+ unsigned id;
+ volatile int scanning;
+ int page;
+ int side;
+ int bus;
+ SANE_Int file;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+ u8 *buffer;
+ struct buf buf[2];
+ u8 *data;
+ unsigned side_size;
+ unsigned read;
+ pthread_t thread;
+};
+
+struct window
+{
+ u8 reserved[6];
+ u8 window_descriptor_block_length[2];
+
+ u8 window_identifier;
+ u8 reserved2;
+ u8 x_resolution[2];
+ u8 y_resolution[2];
+ u8 upper_left_x[4];
+ u8 upper_left_y[4];
+ u8 width[4];
+ u8 length[4];
+ u8 brightness;
+ u8 threshold;
+ u8 contrast;
+ u8 image_composition;
+ u8 bit_per_pixel;
+ u8 halftone_pattern[2];
+ u8 rif_padding; /*RIF*/
+ u8 bit_ordering[2];
+ u8 compression_type;
+ u8 compression_argument;
+ u8 reserved4[6];
+
+ u8 vendor_unique_identifier;
+ u8 nobuf_fstspeed_dfstop;
+ u8 mirror_image;
+ u8 image_emphasis;
+ u8 gamma_correction;
+ u8 mcd_lamp_dfeed_sens;
+ u8 reserved5; /*rmoir*/
+ u8 document_size;
+ u8 document_width[4];
+ u8 document_length[4];
+ u8 ahead_deskew_dfeed_scan_area_fspeed_rshad;
+ u8 continuous_scanning_pages;
+ u8 automatic_threshold_mode;
+ u8 automatic_separation_mode;
+ u8 standard_white_level_mode;
+ u8 b_wnr_noise_reduction;
+ u8 mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr;
+ u8 stop_mode;
+ u8 red_chroma;
+ u8 blue_chroma;
+};
+
+struct support_info
+{
+ /*TODO: */
+ unsigned char data[32];
+};
+
+void kvs40xx_init_options (struct scanner *);
+SANE_Status kvs40xx_test_unit_ready (struct scanner *s);
+SANE_Status kvs40xx_set_timeout (struct scanner *s, int timeout);
+void kvs40xx_init_window (struct scanner *s, struct window *wnd, int wnd_id);
+SANE_Status kvs40xx_set_window (struct scanner *s, int wnd_id);
+SANE_Status kvs40xx_reset_window (struct scanner *s);
+SANE_Status kvs40xx_read_picture_element (struct scanner *s, unsigned side,
+ SANE_Parameters * p);
+SANE_Status read_support_info (struct scanner *s, struct support_info *inf);
+SANE_Status kvs40xx_read_image_data (struct scanner *s, unsigned page,
+ unsigned side, void *buf,
+ unsigned max_size, unsigned *size);
+SANE_Status kvs40xx_document_exist (struct scanner *s);
+SANE_Status get_buffer_status (struct scanner *s, unsigned *data_avalible);
+SANE_Status kvs40xx_scan (struct scanner *s);
+SANE_Status kvs40xx_sense_handler (int fd, u_char * sense_buffer, void *arg);
+SANE_Status stop_adf (struct scanner *s);
+SANE_Status hopper_down (struct scanner *s);
+SANE_Status inquiry (struct scanner *s, char *id);
+
+static inline u16
+swap_bytes16 (u16 x)
+{
+ return x << 8 | x >> 8;
+}
+static inline u32
+swap_bytes32 (u32 x)
+{
+ return x << 24 | x >> 24 |
+ (x & (u32) 0x0000ff00UL) << 8 | (x & (u32) 0x00ff0000UL) >> 8;
+}
+
+#if WORDS_BIGENDIAN
+static inline void
+set24 (u8 * p, u32 x)
+{
+ p[2] = x >> 16;
+ p[1] = x >> 8;
+ p[0] = x >> 0;
+}
+
+#define cpu2be16(x) (x)
+#define cpu2be32(x) (x)
+#define cpu2le16(x) swap_bytes16(x)
+#define cpu2le32(x) swap_bytes32(x)
+#define le2cpu16(x) swap_bytes16(x)
+#define le2cpu32(x) swap_bytes32(x)
+#define be2cpu16(x) (x)
+#define be2cpu32(x) (x)
+#define BIT_ORDERING 0
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+static inline void
+set24 (u8 * p, u32 x)
+{
+ p[0] = x >> 16;
+ p[1] = x >> 8;
+ p[2] = x >> 0;
+}
+
+#define cpu2le16(x) (x)
+#define cpu2le32(x) (x)
+#define cpu2be16(x) swap_bytes16(x)
+#define cpu2be32(x) swap_bytes32(x)
+#define le2cpu16(x) (x)
+#define le2cpu32(x) (x)
+#define be2cpu16(x) swap_bytes16(x)
+#define be2cpu32(x) swap_bytes32(x)
+#define BIT_ORDERING 1
+#else
+#error __BYTE_ORDER not defined
+#endif
+
+
+static inline u32
+get24 (u8 * p)
+{
+ u32 x = (((u32) p[0]) << 16) | (((u32) p[1]) << 8) | (((u32) p[0]) << 0);
+ return x;
+}
+#endif /*__KVS40XX_H*/
diff --git a/backend/kvs40xx_cmd.c b/backend/kvs40xx_cmd.c
new file mode 100644
index 0000000..ade2014
--- /dev/null
+++ b/backend/kvs40xx_cmd.c
@@ -0,0 +1,601 @@
+/*
+ Copyright (C) 2009, Panasonic Russia Ltd.
+ Copyright (C) 2010,2011, m. allan noah
+*/
+/*
+ Panasonic KV-S40xx USB-SCSI scanner driver.
+*/
+#include "../include/sane/config.h"
+#include <string.h>
+
+#define DEBUG_DECLARE_ONLY
+#define BACKEND_NAME kvs40xx
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_config.h"
+
+#include "kvs40xx.h"
+
+#include "../include/sane/sanei_debug.h"
+
+#define COMMAND_BLOCK 1
+#define DATA_BLOCK 2
+#define RESPONSE_BLOCK 3
+
+#define COMMAND_CODE 0x9000
+#define DATA_CODE 0xb000
+#define RESPONSE_CODE 0xa000
+#define STATUS_SIZE 4
+
+struct bulk_header
+{
+ u32 length;
+ u16 type;
+ u16 code;
+ u32 transaction_id;
+};
+
+#define TEST_UNIT_READY 0x00
+#define INQUIRY 0x12
+#define SET_WINDOW 0x24
+#define SCAN 0x1B
+#define SEND_10 0x2A
+#define READ_10 0x28
+#define REQUEST_SENSE 0x03
+#define GET_BUFFER_STATUS 0x34
+#define SET_TIMEOUT 0xE1
+#define GET_ADJUST_DATA 0xE0
+#define HOPPER_DOWN 0xE1
+#define STOP_ADF 0xE1
+
+
+
+#define SUPPORT_INFO 0x93
+
+
+#define GOOD 0
+#define CHECK_CONDITION 2
+
+typedef enum
+{
+ CMD_NONE = 0,
+ CMD_IN = 0x81, /* scanner to pc */
+ CMD_OUT = 0x02 /* pc to scanner */
+} CMD_DIRECTION; /* equals to endpoint address */
+
+#define RESPONSE_SIZE 0x12
+#define MAX_CMD_SIZE 12
+struct cmd
+{
+ unsigned char cmd[MAX_CMD_SIZE];
+ int cmd_size;
+ void *data;
+ int data_size;
+ int dir;
+};
+struct response
+{
+ int status;
+ unsigned char data[RESPONSE_SIZE];
+};
+
+static SANE_Status
+usb_send_command (struct scanner *s, struct cmd *c, struct response *r,
+ void *buf)
+{
+ SANE_Status st;
+ struct bulk_header *h = (struct bulk_header *) buf;
+ u8 resp[sizeof (*h) + STATUS_SIZE];
+ size_t sz = sizeof (*h) + MAX_CMD_SIZE;
+ memset (h, 0, sz);
+ h->length = cpu2be32 (sz);
+ h->type = cpu2be16 (COMMAND_BLOCK);
+ h->code = cpu2be16 (COMMAND_CODE);
+ memcpy (h + 1, c->cmd, c->cmd_size);
+
+ st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz);
+ if (st)
+ return st;
+
+ if (sz != sizeof (*h) + MAX_CMD_SIZE)
+ return SANE_STATUS_IO_ERROR;
+
+ if (c->dir == CMD_IN)
+ {
+ unsigned l;
+ sz = sizeof (*h) + c->data_size;
+ c->data_size = 0;
+ st = sanei_usb_read_bulk (s->file, (SANE_Byte *) h, &sz);
+ for (l = sz; !st && l != be2cpu32 (h->length); l += sz)
+ {
+ DBG (DBG_WARN, "usb wrong read (%d instead %d)\n",
+ c->data_size, be2cpu32 (h->length));
+ sz = be2cpu32 (h->length) - l;
+ st = sanei_usb_read_bulk (s->file, ((SANE_Byte *) h) + l, &sz);
+
+ }
+
+ c->data = h + 1;
+
+ if (st)
+ {
+ st = sanei_usb_release_interface (s->file, 0);
+ if (st)
+ return st;
+ st = sanei_usb_claim_interface (s->file, 0);
+
+ if (st)
+ return st;
+
+ r->status = CHECK_CONDITION;
+ return SANE_STATUS_GOOD;
+ }
+
+ c->data_size = sz - sizeof (*h);
+
+
+ }
+ else if (c->dir == CMD_OUT)
+ {
+ sz = sizeof (*h) + c->data_size;
+ memset (h, 0, sizeof (*h));
+ h->length = cpu2be32 (sizeof (*h) + c->data_size);
+ h->type = cpu2be16 (DATA_BLOCK);
+ h->code = cpu2be16 (DATA_CODE);
+ memcpy (h + 1, c->data, c->data_size);
+ st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz);
+ if (st)
+ return st;
+ }
+ sz = sizeof (resp);
+ st = sanei_usb_read_bulk (s->file, resp, &sz);
+ if (st || sz != sizeof (resp))
+ return SANE_STATUS_IO_ERROR;
+
+ r->status = be2cpu32 (*((u32 *) (resp + sizeof (*h))));
+ return st;
+}
+
+#define END_OF_MEDIUM (1<<6)
+#define INCORRECT_LENGTH_INDICATOR (1<<5)
+static const struct
+{
+ unsigned sense, asc, ascq;
+ SANE_Status st;
+} s_errors[] =
+{
+ {
+ 2, 0, 0, SANE_STATUS_DEVICE_BUSY},
+ {
+ 2, 4, 1, SANE_STATUS_DEVICE_BUSY},
+ {
+ 2, 4, 0x80, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x81, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x82, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x83, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 4, 0x84, SANE_STATUS_COVER_OPEN},
+ {
+ 2, 0x80, 1, SANE_STATUS_CANCELLED},
+ {
+ 2, 0x80, 2, SANE_STATUS_CANCELLED},
+ {
+ 3, 0x3a, 0, SANE_STATUS_NO_DOCS},
+ {
+ 3, 0x80, 1, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 2, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 3, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 4, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 5, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 6, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 7, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 8, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 9, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0xa, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0xb, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0xc, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0xd, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0xe, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0xf, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0x10, SANE_STATUS_JAMMED},
+ {
+ 3, 0x80, 0x11, SANE_STATUS_JAMMED},
+ {
+ 5, 0x1a, 0x0, SANE_STATUS_INVAL},
+ {
+ 5, 0x20, 0x0, SANE_STATUS_INVAL},
+ {
+ 5, 0x24, 0x0, SANE_STATUS_INVAL},
+ {
+ 5, 0x25, 0x0, SANE_STATUS_INVAL},
+ {
+ 5, 0x26, 0x0, SANE_STATUS_INVAL},
+ {
+ 5, 0x2c, 0x01, SANE_STATUS_INVAL},
+ {
+ 5, 0x2c, 0x02, SANE_STATUS_INVAL},
+ {
+ 5, 0x2c, 0x80, SANE_STATUS_INVAL},
+ {
+ 5, 0x2c, 0x81, SANE_STATUS_INVAL},
+ {
+ 5, 0x2c, 0x82, SANE_STATUS_INVAL},
+ {
+5, 0x2c, 0x83, SANE_STATUS_INVAL},};
+
+SANE_Status
+kvs40xx_sense_handler (int __sane_unused__ fd,
+ u_char * sense_buffer, void __sane_unused__ * arg)
+{
+ unsigned i;
+ SANE_Status st = SANE_STATUS_GOOD;
+ if (sense_buffer[2] & 0xf)
+ { /*error */
+ for (i = 0; i < sizeof (s_errors) / sizeof (s_errors[0]); i++)
+ {
+ if ((sense_buffer[2] & 0xf) == s_errors[i].sense
+ && sense_buffer[12] == s_errors[i].asc
+ && sense_buffer[13] == s_errors[i].ascq)
+ {
+ st = s_errors[i].st;
+ break;
+ }
+ }
+ if (i == sizeof (s_errors) / sizeof (s_errors[0]))
+ st = SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ if (sense_buffer[2] & END_OF_MEDIUM)
+ st = SANE_STATUS_EOF;
+ else if (sense_buffer[2] & INCORRECT_LENGTH_INDICATOR)
+ st = INCORRECT_LENGTH;
+ }
+
+ DBG (DBG_ERR,
+ "send_command: CHECK_CONDITION: sence:0x%x ASC:0x%x ASCQ:0x%x\n",
+ sense_buffer[2], sense_buffer[12], sense_buffer[13]);
+
+ return st;
+}
+
+static SANE_Status
+send_command (struct scanner * s, struct cmd * c)
+{
+ SANE_Status st = SANE_STATUS_GOOD;
+ if (s->bus == USB)
+ {
+ struct response r;
+ memset (&r, 0, sizeof (r));
+ st = usb_send_command (s, c, &r, s->buffer);
+ if (st)
+ return st;
+ if (r.status)
+ {
+ u8 b[sizeof (struct bulk_header) + RESPONSE_SIZE];
+ struct cmd c2 = {
+ {0}, 6,
+ NULL, RESPONSE_SIZE,
+ CMD_IN
+ };
+ c2.cmd[0] = REQUEST_SENSE;
+ c2.cmd[4] = RESPONSE_SIZE;
+
+ st = usb_send_command (s, &c2, &r, b);
+ if (st)
+ return st;
+ st = kvs40xx_sense_handler (0, b + sizeof (struct bulk_header), NULL);
+ }
+ }
+ else
+ {
+ if (c->dir == CMD_OUT)
+ {
+ memcpy (s->buffer, c->cmd, c->cmd_size);
+ memcpy (s->buffer + c->cmd_size, c->data, c->data_size);
+ st = sanei_scsi_cmd (s->file, s->buffer,
+ c->cmd_size + c->data_size, NULL, NULL);
+ }
+ else if (c->dir == CMD_IN)
+ {
+ c->data = s->buffer;
+ st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size,
+ c->data, (size_t *) & c->data_size);
+ }
+ else
+ {
+ st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, NULL, NULL);
+ }
+ }
+ return st;
+}
+
+SANE_Status
+kvs40xx_test_unit_ready (struct scanner * s)
+{
+ struct cmd c = {
+ {0}, 6,
+ NULL, 0,
+ CMD_NONE
+ };
+ c.cmd[0] = TEST_UNIT_READY;
+ if (send_command (s, &c))
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+kvs40xx_set_timeout (struct scanner * s, int timeout)
+{
+ u16 t = cpu2be16 ((u16) timeout);
+ struct cmd c = {
+ {0}, 10,
+ NULL, 0,
+ CMD_OUT
+ };
+ c.data = &t;
+ c.data_size = sizeof (t);
+ c.cmd[0] = SET_TIMEOUT;
+ c.cmd[2] = 0x8d;
+ *((u16 *) (c.cmd + 7)) = cpu2be16 (sizeof (t));
+ if (s->bus == USB)
+ sanei_usb_set_timeout (timeout * 1000);
+
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs40xx_set_window (struct scanner * s, int wnd_id)
+{
+ struct window wnd;
+ struct cmd c = {
+ {0}, 10,
+ NULL, 0,
+ CMD_OUT
+ };
+ c.data = &wnd;
+ c.data_size = sizeof (wnd);
+ c.cmd[0] = SET_WINDOW;
+ *((u16 *) (c.cmd + 7)) = cpu2be16 (sizeof (wnd));
+ kvs40xx_init_window (s, &wnd, wnd_id);
+
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs40xx_reset_window (struct scanner * s)
+{
+ struct cmd c = {
+ {0}, 10,
+ NULL, 0,
+ CMD_NONE
+ };
+ c.cmd[0] = SET_WINDOW;
+
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs40xx_scan (struct scanner * s)
+{
+ struct cmd c = {
+ {0}, 6,
+ NULL, 0,
+ CMD_NONE
+ };
+ c.cmd[0] = SCAN;
+ return send_command (s, &c);
+}
+
+SANE_Status
+hopper_down (struct scanner * s)
+{
+ struct cmd c = {
+ {0}, 10,
+ NULL, 0,
+ CMD_NONE
+ };
+ c.cmd[0] = HOPPER_DOWN;
+ c.cmd[2] = 5;
+
+ if (s->id == KV_S7075C)
+ return SANE_STATUS_GOOD;
+ return send_command (s, &c);
+}
+
+SANE_Status
+stop_adf (struct scanner * s)
+{
+ struct cmd c = {
+ {0}, 10,
+ NULL, 0,
+ CMD_NONE
+ };
+ c.cmd[0] = STOP_ADF;
+ c.cmd[2] = 0x8b;
+ return send_command (s, &c);
+}
+
+SANE_Status
+kvs40xx_document_exist (struct scanner * s)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0}, 10,
+ NULL, 6,
+ CMD_IN
+ };
+ u8 *d;
+ c.cmd[0] = READ_10;
+ c.cmd[2] = 0x81;
+ set24 (c.cmd + 6, c.data_size);
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ d = c.data;
+ if (d[0] & 0x20)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+SANE_Status
+kvs40xx_read_picture_element (struct scanner * s, unsigned side,
+ SANE_Parameters * p)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0}, 10,
+ NULL, 16,
+ CMD_IN
+ };
+ u32 *data;
+ c.cmd[0] = READ_10;
+ c.cmd[2] = 0x80;
+ c.cmd[5] = side;
+ set24 (c.cmd + 6, c.data_size);
+
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ data = (u32 *) c.data;
+ p->pixels_per_line = be2cpu32 (data[0]);
+ p->lines = be2cpu32 (data[1]);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+get_buffer_status (struct scanner * s, unsigned *data_avalible)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0}, 10,
+ NULL, 12,
+ CMD_IN
+ };
+ c.cmd[0] = GET_BUFFER_STATUS;
+ c.cmd[7] = 12;
+
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ *data_avalible = get24 ((unsigned char *)c.data + 9);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+kvs40xx_read_image_data (struct scanner * s, unsigned page, unsigned side,
+ void *buf, unsigned max_size, unsigned *size)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0}, 10,
+ NULL, 0,
+ CMD_IN
+ };
+ c.data_size = max_size < MAX_READ_DATA_SIZE ? max_size : MAX_READ_DATA_SIZE;
+ c.cmd[0] = READ_10;
+ c.cmd[4] = page;
+ c.cmd[5] = side;
+
+ set24 (c.cmd + 6, c.data_size);
+ *size = 0;
+ status = send_command (s, &c);
+
+ if (status && status != SANE_STATUS_EOF && status != INCORRECT_LENGTH)
+ return status;
+
+ *size = c.data_size;
+ memcpy (buf, c.data, *size);
+ return status;
+}
+
+static SANE_Status
+get_adjust_data (struct scanner * s, unsigned *dummy_length)
+{
+ SANE_Status status;
+ struct cmd c = {
+ {0}, 10,
+ NULL, 40,
+ CMD_IN
+ };
+ u16 *data;
+
+ c.cmd[0] = GET_ADJUST_DATA;
+ c.cmd[2] = 0x9b;
+ c.cmd[8] = 40;
+ status = send_command (s, &c);
+ if (status)
+ return status;
+ data = (u16 *) c.data;
+ *dummy_length = be2cpu16 (data[0]);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+read_support_info (struct scanner * s, struct support_info * inf)
+{
+ SANE_Status st;
+ struct cmd c = {
+ {0}, 10,
+ NULL, sizeof (*inf),
+ CMD_IN
+ };
+
+ c.cmd[0] = READ_10;
+ c.cmd[2] = SUPPORT_INFO;
+ set24 (c.cmd + 6, c.data_size);
+
+ st = send_command (s, &c);
+ if (st)
+ return st;
+ memcpy (inf, c.data, sizeof (*inf));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+inquiry (struct scanner * s, char *id)
+{
+ int i;
+ SANE_Status st;
+ struct cmd c = {
+ {0}, 5,
+ NULL, 0x60,
+ CMD_IN
+ };
+
+ c.cmd[0] = INQUIRY;
+ c.cmd[4] = c.data_size;
+
+ st = send_command (s, &c);
+ if (st)
+ return st;
+ memcpy (id, (unsigned char *)c.data + 16, 16);
+ for (i = 0; i < 15 && id[i] != ' '; i++);
+ id[i] = 0;
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/kvs40xx_opt.c b/backend/kvs40xx_opt.c
new file mode 100644
index 0000000..c4f478b
--- /dev/null
+++ b/backend/kvs40xx_opt.c
@@ -0,0 +1,1414 @@
+/*
+ Copyright (C) 2009, Panasonic Russia Ltd.
+ Copyright (C) 2010,2011, m. allan noah
+*/
+/*
+ Panasonic KV-S40xx USB-SCSI scanner driver.
+*/
+
+#include "../include/sane/config.h"
+
+#include <string.h>
+#define DEBUG_DECLARE_ONLY
+#define BACKEND_NAME kvs40xx
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "lassert.h"
+
+#include "kvs40xx.h"
+
+#include "../include/sane/sanei_debug.h"
+
+
+static inline unsigned
+mm2scanner_units (unsigned mm)
+{
+ return (mm * 12000 / 254.0 + .5);
+}
+static inline unsigned
+scanner_units2mm (unsigned u)
+{
+ return (u * 254.0 / 12000 + .5);
+}
+struct restriction
+{
+ unsigned ux, uy, ux_pix, uy_pix;
+};
+
+static struct restriction flatbad = { 14064, 20400, 7031, 63999 };
+static struct restriction cw = { 14268, 128000, 7133, 63999 };
+static struct restriction cl = { 10724, 128000, 5361, 63999 };
+
+static inline int
+check_area (struct scanner *s, unsigned ux,
+ unsigned uy, unsigned bx, unsigned by)
+{
+ int fb = !strcmp (s->val[SOURCE].s, SANE_I18N ("fb"));
+ struct restriction *r = fb ? &flatbad
+ : (s->id == KV_S4085CL || s->id == KV_S4065CL) ? &cl : &cw;
+ unsigned res = s->val[RESOLUTION].w;
+ unsigned w = bx - ux;
+ unsigned h = by - uy;
+ unsigned c1 = mm2scanner_units (ux + w);
+ unsigned c2 = mm2scanner_units (uy + h);
+ int c = c1 <= r->ux && c1 >= 16 && c2 >= 1 && c2 <= r->uy ? 0 : -1;
+ if (c)
+ return c;
+ if (mm2scanner_units (ux) > r->ux)
+ return -1;
+ if (res * mm2scanner_units (ux) / 1200 > r->ux_pix)
+ return -1;
+
+ if (res * mm2scanner_units (uy) / 1200 > r->uy_pix)
+ return -1;
+ return 0;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+static const unsigned mode_val[] = { 0, 2, 5 };
+static const unsigned bps_val[] = { 1, 8, 24 };
+
+static const SANE_Range resolutions_range = {
+ 100,600,1
+};
+
+/* List of feeder modes */
+static SANE_String_Const feeder_mode_list[] = {
+ SANE_I18N ("single"),
+ SANE_I18N ("continuous"),
+ NULL
+};
+
+/* List of scan sources */
+static SANE_String_Const source_list[] = {
+ SANE_I18N ("adf"),
+ SANE_I18N ("fb"),
+ NULL
+};
+
+/* List of manual feed mode */
+static SANE_String_Const manual_feed_list[] = {
+ SANE_I18N ("off"),
+ SANE_I18N ("wait_doc"),
+ SANE_I18N ("wait_doc_hopper_up"),
+ SANE_I18N ("wait_key"),
+ NULL
+};
+
+/* List of paper sizes */
+static SANE_String_Const paper_list[] = {
+ SANE_I18N ("user_def"),
+ SANE_I18N ("business_card"),
+ SANE_I18N ("Check"),
+ SANE_I18N ("A3"),
+ SANE_I18N ("A4"),
+ SANE_I18N ("A5"),
+ SANE_I18N ("A6"),
+ SANE_I18N ("Letter"),
+ SANE_I18N ("Double letter 11x17 in"),
+ SANE_I18N ("B4"),
+ SANE_I18N ("B5"),
+ SANE_I18N ("B6"),
+ SANE_I18N ("Legal"),
+ NULL
+};
+
+static SANE_String_Const paper_list_woA3[] = {
+ SANE_I18N ("user_def"),
+ SANE_I18N ("business_card"),
+ SANE_I18N ("Check"),
+ /*SANE_I18N ("A3"), */
+ SANE_I18N ("A4"),
+ SANE_I18N ("A5"),
+ SANE_I18N ("A6"),
+ SANE_I18N ("Letter"),
+ /*SANE_I18N ("Double letter 11x17 in"), */
+ /*SANE_I18N ("B4"), */
+ SANE_I18N ("B5"),
+ SANE_I18N ("B6"),
+ SANE_I18N ("Legal"),
+ NULL
+};
+
+static const unsigned paper_val[] = { 0, 1, 2, 3, 4, 5, 6, 7,
+ 9, 12, 13, 14, 15
+};
+
+struct paper_size
+{
+ int width;
+ int height;
+};
+static const struct paper_size paper_sizes[] = {
+ {210, 297}, /* User defined, default=A4 */
+ {54, 90}, /* Business card */
+ {80, 170}, /* Check (China business) */
+ {297, 420}, /* A3 */
+ {210, 297}, /* A4 */
+ {148, 210}, /* A5 */
+ {105, 148}, /* A6 */
+ {215, 280}, /* US Letter 8.5 x 11 in */
+ {280, 432}, /* Double Letter 11 x 17 in */
+ {250, 353}, /* B4 */
+ {176, 250}, /* B5 */
+ {125, 176}, /* B6 */
+ {215, 355} /* US Legal */
+};
+
+#define MIN_WIDTH 48
+#define MIN_LENGTH 70
+#define MAX_WIDTH 297
+#define MAX_LENGTH 432
+
+#define MAX_WIDTH_A4 227
+#define MAX_LENGTH_A4 432
+
+static SANE_Range tl_x_range = { 0, MAX_WIDTH - MIN_WIDTH, 0 };
+static SANE_Range tl_y_range = { 0, MAX_LENGTH - MIN_LENGTH, 0 };
+static SANE_Range br_x_range = { MIN_WIDTH, MAX_WIDTH, 0 };
+static SANE_Range br_y_range = { MIN_LENGTH, MAX_LENGTH, 0 };
+
+static SANE_Range tl_x_range_A4 = { 0, MAX_WIDTH_A4 - MIN_WIDTH, 0 };
+static SANE_Range tl_y_range_A4 = { 0, MAX_LENGTH_A4 - MIN_LENGTH, 0 };
+static SANE_Range br_x_range_A4 = { MIN_WIDTH, MAX_WIDTH_A4, 0 };
+static SANE_Range br_y_range_A4 = { MIN_LENGTH, MAX_LENGTH_A4, 0 };
+
+static SANE_Range byte_value_range = { 0, 255, 0 };
+static SANE_Range compression_value_range = { 1, 0x64, 0 };
+
+/* List of image emphasis options, 5 steps */
+static SANE_String_Const image_emphasis_list[] = {
+ SANE_I18N ("none"),
+ SANE_I18N ("low"),
+ SANE_I18N ("medium"),
+ SANE_I18N ("high"),
+ SANE_I18N ("smooth"),
+ NULL
+};
+
+/* List of gamma */
+static SANE_String_Const gamma_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("crt"),
+ NULL
+};
+static unsigned gamma_val[] = { 0, 1 };
+
+/* List of lamp color dropout */
+static SANE_String_Const lamp_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("red"),
+ SANE_I18N ("green"),
+ SANE_I18N ("blue"),
+ NULL
+};
+static SANE_String_Const dfeed_sence_list[] = {
+ SANE_I18N ("Normal"),
+ SANE_I18N ("High sensivity"),
+ SANE_I18N ("Low sensivity"),
+ NULL
+};
+
+/* Lists of supported halftone. They are only valid with
+ * for the Black&White mode. */
+static SANE_String_Const halftone_pattern[] = {
+ SANE_I18N ("bayer_64"),
+ SANE_I18N ("bayer_16"),
+ SANE_I18N ("halftone_32"),
+ SANE_I18N ("halftone_64"),
+ SANE_I18N ("err_diffusion"),
+ NULL
+};
+
+/* Stapled document */
+static SANE_String_Const stapeled_list[] = {
+ SANE_I18N ("No detection"),
+ SANE_I18N ("Normal mode"),
+ SANE_I18N ("Enhanced mode"),
+ NULL
+};
+
+
+/* List of automatic threshold options */
+static SANE_String_Const automatic_threshold_list[] = {
+ SANE_I18N ("normal"),
+ SANE_I18N ("light"),
+ SANE_I18N ("dark"),
+ NULL
+};
+static const int automatic_threshold_val[] = {
+ 0,
+ 0x11,
+ 0x1f
+};
+
+/* List of white level base. */
+static SANE_String_Const white_level_list[] = {
+ SANE_I18N ("From scanner"),
+ SANE_I18N ("From paper"),
+ SANE_I18N ("Automatic"),
+ NULL
+};
+static const int white_level_val[] = {
+ 0x00,
+ 0x80,
+ 0x81
+};
+
+/* List of noise reduction options. */
+static SANE_String_Const noise_reduction_list[] = {
+ SANE_I18N ("default"),
+ "1x1",
+ "2x2",
+ "3x3",
+ "4x4",
+ "5x5",
+ NULL
+};
+
+/* Reset the options for that scanner. */
+void
+kvs40xx_init_options (struct scanner *s)
+{
+ int i;
+ SANE_Option_Descriptor *o;
+ /* Pre-initialize the options. */
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; i++)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ o = &s->opt[NUM_OPTS];
+ o->name = "";
+ o->title = SANE_TITLE_NUM_OPTIONS;
+ o->desc = SANE_DESC_NUM_OPTIONS;
+ o->type = SANE_TYPE_INT;
+ o->cap = SANE_CAP_SOFT_DETECT;
+ s->val[NUM_OPTS].w = NUM_OPTIONS;
+
+ /* Mode group */
+ o = &s->opt[MODE_GROUP];
+ o->title = SANE_I18N ("Scan Mode");
+ o->desc = ""; /* not valid for a group */
+ o->type = SANE_TYPE_GROUP;
+ o->cap = 0;
+ o->size = 0;
+ o->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ o = &s->opt[MODE];
+ o->name = SANE_NAME_SCAN_MODE;
+ o->title = SANE_TITLE_SCAN_MODE;
+ o->desc = SANE_DESC_SCAN_MODE;
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (mode_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = mode_list;
+ s->val[MODE].s = malloc (o->size);
+ strcpy (s->val[MODE].s, mode_list[2]);
+
+ /* X and Y resolution */
+ o = &s->opt[RESOLUTION];
+ o->name = SANE_NAME_SCAN_RESOLUTION;
+ o->title = SANE_TITLE_SCAN_RESOLUTION;
+ o->desc = SANE_DESC_SCAN_RESOLUTION;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_DPI;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &resolutions_range;
+ s->val[RESOLUTION].w = 100;
+
+ /* Duplex */
+ o = &s->opt[DUPLEX];
+ o->name = "duplex";
+ o->title = SANE_I18N ("Duplex");
+ o->desc = SANE_I18N ("Enable Duplex (Dual-Sided) Scanning");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DUPLEX].w = SANE_FALSE;
+
+ /*FIXME
+ if (!s->support_info.support_duplex)
+ o->cap |= SANE_CAP_INACTIVE;
+ */
+
+ /* Feeder mode */
+ o = &s->opt[FEEDER_MODE];
+ o->name = "feeder-mode";
+ o->title = SANE_I18N ("Feeder mode");
+ o->desc = SANE_I18N ("Sets the feeding mode");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (feeder_mode_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = feeder_mode_list;
+ s->val[FEEDER_MODE].s = malloc (o->size);
+ strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]);
+
+ /* Scan source */
+ o = &s->opt[SOURCE];
+ o->name = SANE_NAME_SCAN_SOURCE;
+ o->title = SANE_TITLE_SCAN_SOURCE;
+ o->desc = SANE_DESC_SCAN_SOURCE;
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (source_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = source_list;
+ s->val[SOURCE].s = malloc (o->size);
+ strcpy (s->val[SOURCE].s, source_list[0]);
+ if (s->id != KV_S7075C)
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Length control */
+ o = &s->opt[LENGTHCTL];
+ o->name = "length-control";
+ o->title = SANE_I18N ("Length control mode");
+ o->desc =
+ SANE_I18N
+ ("Length Control Mode is a mode that the scanner reads up to the shorter length of actual"
+ " paper or logical document length.");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[LENGTHCTL].w = SANE_FALSE;
+
+ o = &s->opt[LONG_PAPER];
+ o->name = "long-paper";
+ o->title = SANE_I18N ("Long paper mode");
+ o->desc = SANE_I18N ("Long Paper Mode is a mode that the scanner "
+ "reads the image after it divides long paper "
+ "by the length which is set in Document Size option.");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[LONG_PAPER].w = SANE_FALSE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Manual feed */
+ o = &s->opt[MANUALFEED];
+ o->name = "manual-feed";
+ o->title = SANE_I18N ("Manual feed mode");
+ o->desc = SANE_I18N ("Sets the manual feed mode");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (manual_feed_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = manual_feed_list;
+ s->val[MANUALFEED].s = malloc (o->size);
+ strcpy (s->val[MANUALFEED].s, manual_feed_list[0]);
+
+ /*Manual feed timeout */
+ o = &s->opt[FEED_TIMEOUT];
+ o->name = "feed-timeout";
+ o->title = SANE_I18N ("Manual feed timeout");
+ o->desc = SANE_I18N ("Sets the manual feed timeout in seconds");
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[FEED_TIMEOUT].w = 30;
+
+ /* Double feed */
+ o = &s->opt[DBLFEED];
+ o->name = "dfeed";
+ o->title = SANE_I18N ("Double feed detection");
+ o->desc = SANE_I18N ("Enable/Disable double feed detection");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DBLFEED].w = SANE_FALSE;
+
+ o = &s->opt[DFEED_SENCE];
+ o->name = "dfeed-sense";
+ o->title = SANE_I18N ("Double feed detector sensitivity");
+ o->desc = SANE_I18N ("Set the double feed detector sensitivity");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (dfeed_sence_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = dfeed_sence_list;
+ s->val[DFEED_SENCE].s = malloc (o->size);
+ strcpy (s->val[DFEED_SENCE].s, dfeed_sence_list[0]);
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[DFSTOP];
+ o->name = "dfstop";
+ o->title = SANE_I18N ("Do not stop after double feed detection");
+ o->desc = SANE_I18N ("Do not stop after double feed detection");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DFSTOP].w = SANE_FALSE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[DFEED_L];
+ o->name = "dfeed_l";
+ o->title = SANE_I18N ("Ignore left double feed sensor");
+ o->desc = SANE_I18N ("Ignore left double feed sensor");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DFEED_L].w = SANE_FALSE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[DFEED_C];
+ o->name = "dfeed_c";
+ o->title = SANE_I18N ("Ignore center double feed sensor");
+ o->desc = SANE_I18N ("Ignore center double feed sensor");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DFEED_C].w = SANE_FALSE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[DFEED_R];
+ o->name = "dfeed_r";
+ o->title = SANE_I18N ("Ignore right double feed sensor");
+ o->desc = SANE_I18N ("Ignore right double feed sensor");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DFEED_R].w = SANE_FALSE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Fit to page */
+ o = &s->opt[FIT_TO_PAGE];
+ o->name = SANE_I18N ("fit-to-page");
+ o->title = SANE_I18N ("Fit to page");
+ o->desc = SANE_I18N ("Scanner shrinks image to fit scanned page");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[FIT_TO_PAGE].w = SANE_FALSE;
+
+ /* Geometry group */
+ o = &s->opt[GEOMETRY_GROUP];
+ o->title = SANE_I18N ("Geometry");
+ o->desc = ""; /* not valid for a group */
+ o->type = SANE_TYPE_GROUP;
+ o->cap = 0;
+ o->size = 0;
+ o->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Paper sizes list */
+ o = &s->opt[PAPER_SIZE];
+ o->name = "paper-size";
+ o->title = SANE_I18N ("Paper size");
+ o->desc = SANE_I18N ("Physical size of the paper in the ADF");
+ o->type = SANE_TYPE_STRING;
+ o->constraint.string_list =
+ s->id == KV_S4085CL || s->id == KV_S4065CL ? paper_list_woA3 : paper_list;
+
+
+ o->size = max_string_size (o->constraint.string_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->val[PAPER_SIZE].s = malloc (o->size);
+ strcpy (s->val[PAPER_SIZE].s, SANE_I18N ("A4"));
+
+ /* Landscape */
+ o = &s->opt[LANDSCAPE];
+ o->name = "landscape";
+ o->title = SANE_I18N ("Landscape");
+ o->desc =
+ SANE_I18N ("Set paper position : "
+ "true for landscape, false for portrait");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[LANDSCAPE].w = SANE_FALSE;
+
+ /* Upper left X */
+ o = &s->opt[TL_X];
+ o->name = SANE_NAME_SCAN_TL_X;
+ o->title = SANE_TITLE_SCAN_TL_X;
+ o->desc = SANE_DESC_SCAN_TL_X;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range =
+ (s->id == KV_S4085CL || s->id == KV_S4065CL)
+ ? &tl_x_range_A4 : &tl_x_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[TL_X].w = 0;
+
+ /* Upper left Y */
+ o = &s->opt[TL_Y];
+ o->name = SANE_NAME_SCAN_TL_Y;
+ o->title = SANE_TITLE_SCAN_TL_Y;
+ o->desc = SANE_DESC_SCAN_TL_Y;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range =
+ (s->id == KV_S4085CL || s->id == KV_S4065CL)
+ ? &tl_y_range_A4 : &tl_y_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[TL_Y].w = 0;
+
+ /* Bottom-right x */
+ o = &s->opt[BR_X];
+ o->name = SANE_NAME_SCAN_BR_X;
+ o->title = SANE_TITLE_SCAN_BR_X;
+ o->desc = SANE_DESC_SCAN_BR_X;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range =
+ (s->id == KV_S4085CL || s->id == KV_S4065CL)
+ ? &br_x_range_A4 : &br_x_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[BR_X].w = 210;
+
+ /* Bottom-right y */
+ o = &s->opt[BR_Y];
+ o->name = SANE_NAME_SCAN_BR_Y;
+ o->title = SANE_TITLE_SCAN_BR_Y;
+ o->desc = SANE_DESC_SCAN_BR_Y;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_MM;
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range =
+ (s->id == KV_S4085CL || s->id == KV_S4065CL)
+ ? &br_y_range_A4 : &br_y_range;
+ o->cap |= SANE_CAP_INACTIVE;
+ s->val[BR_Y].w = 297;
+
+ /* Enhancement group */
+ o = &s->opt[ADVANCED_GROUP];
+ o->title = SANE_I18N ("Advanced");
+ o->desc = ""; /* not valid for a group */
+ o->type = SANE_TYPE_GROUP;
+ o->cap = SANE_CAP_ADVANCED;
+ o->size = 0;
+ o->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Brightness */
+ o = &s->opt[BRIGHTNESS];
+ o->name = SANE_NAME_BRIGHTNESS;
+ o->title = SANE_TITLE_BRIGHTNESS;
+ o->desc = SANE_DESC_BRIGHTNESS;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[BRIGHTNESS].w = 128;
+
+ /* Contrast */
+ o = &s->opt[CONTRAST];
+ o->name = SANE_NAME_CONTRAST;
+ o->title = SANE_TITLE_CONTRAST;
+ o->desc = SANE_DESC_CONTRAST;
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[CONTRAST].w = 128;
+
+ /* threshold */
+ o = &s->opt[THRESHOLD];
+ o->name = SANE_NAME_THRESHOLD;
+ o->title = SANE_TITLE_THRESHOLD;
+ o->desc = SANE_DESC_THRESHOLD;
+ o->type = SANE_TYPE_INT;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[THRESHOLD].w = 128;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[AUTOMATIC_THRESHOLD];
+ o->name = "athreshold";
+ o->title = SANE_I18N ("Automatic threshold mode");
+ o->desc = SANE_I18N ("Sets the automatic threshold mode");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (automatic_threshold_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = automatic_threshold_list;
+ s->val[AUTOMATIC_THRESHOLD].s = malloc (o->size);
+ strcpy (s->val[AUTOMATIC_THRESHOLD].s, automatic_threshold_list[0]);
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Image emphasis */
+ o = &s->opt[IMAGE_EMPHASIS];
+ o->name = "image-emphasis";
+ o->title = SANE_I18N ("Image emphasis");
+ o->desc = SANE_I18N ("Sets the image emphasis");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (image_emphasis_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = image_emphasis_list;
+ s->val[IMAGE_EMPHASIS].s = malloc (o->size);
+ strcpy (s->val[IMAGE_EMPHASIS].s, image_emphasis_list[0]);;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Gamma */
+ o = &s->opt[GAMMA_CORRECTION];
+ o->name = "gamma-cor";
+ o->title = SANE_I18N ("Gamma correction");
+ o->desc = SANE_I18N ("Gamma correction");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (gamma_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = gamma_list;
+ s->val[GAMMA_CORRECTION].s = malloc (o->size);
+ strcpy (s->val[GAMMA_CORRECTION].s, gamma_list[0]);
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Lamp color dropout */
+ o = &s->opt[LAMP];
+ o->name = "lamp-color";
+ o->title = SANE_I18N ("Lamp color");
+ o->desc = SANE_I18N ("Sets the lamp color (color dropout)");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (lamp_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = lamp_list;
+ s->val[LAMP].s = malloc (o->size);
+ strcpy (s->val[LAMP].s, lamp_list[0]);
+
+ /* Inverse image */
+ o = &s->opt[INVERSE];
+ o->name = "inverse";
+ o->title = SANE_I18N ("Inverse Image");
+ o->desc = SANE_I18N ("Inverse image in B/W mode");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Halftone pattern */
+ o = &s->opt[HALFTONE_PATTERN];
+ o->name = SANE_NAME_HALFTONE_PATTERN;
+ o->title = SANE_TITLE_HALFTONE_PATTERN;
+ o->desc = SANE_DESC_HALFTONE_PATTERN;
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (halftone_pattern);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = halftone_pattern;
+ s->val[HALFTONE_PATTERN].s = malloc (o->size);
+ strcpy (s->val[HALFTONE_PATTERN].s, halftone_pattern[0]);
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* JPEG Compression */
+ o = &s->opt[COMPRESSION];
+ o->name = "jpeg";
+ o->title = SANE_I18N ("JPEG compression");
+ o->desc =
+ SANE_I18N
+ ("JPEG compression (yours application must be able to uncompress)");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+
+ /* Compression parameter */
+ o = &s->opt[COMPRESSION_PAR];
+ o->name = "comp_arg";
+ o->title = "Compression Argument";
+ o->desc = "Compression Argument (Q parameter for JPEG)";
+ o->type = SANE_TYPE_INT;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(compression_value_range);
+ s->val[COMPRESSION_PAR].w = 0x4b;
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Stapled document */
+ o = &s->opt[STAPELED_DOC];
+ o->name = "stapeled_doc";
+ o->title = SANE_I18N ("Detect stapled document");
+ o->desc = SANE_I18N ("Detect stapled document");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (stapeled_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = stapeled_list;
+ s->val[STAPELED_DOC].s = malloc (o->size);
+ strcpy (s->val[STAPELED_DOC].s, stapeled_list[0]);
+ if (s->id == KV_S7075C)
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* White level base */
+ o = &s->opt[WHITE_LEVEL];
+ o->name = SANE_NAME_WHITE_LEVEL;
+ o->title = SANE_TITLE_WHITE_LEVEL;
+ o->desc = SANE_DESC_WHITE_LEVEL;
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (white_level_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = white_level_list;
+ s->val[WHITE_LEVEL].s = malloc (o->size);
+ strcpy (s->val[WHITE_LEVEL].s, white_level_list[0]);
+ o->cap |= SANE_CAP_INACTIVE;
+
+ /* Noise reduction */
+ o = &s->opt[NOISE_REDUCTION];
+ o->name = "noise-reduction";
+ o->title = SANE_I18N ("Noise reduction");
+ o->desc = SANE_I18N ("Reduce the isolated dot noise");
+ o->type = SANE_TYPE_STRING;
+ o->size = max_string_size (noise_reduction_list);
+ o->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ o->constraint.string_list = noise_reduction_list;
+ s->val[NOISE_REDUCTION].s = malloc (o->size);
+ strcpy (s->val[NOISE_REDUCTION].s, noise_reduction_list[0]);
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[RED_CHROMA];
+ o->name = "red-chroma";
+ o->title = SANE_I18N ("chroma of red");
+ o->desc = SANE_I18N ("Set chroma of red");
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[RED_CHROMA].w = 0;
+
+ o = &s->opt[BLUE_CHROMA];
+ o->name = "blue chroma";
+ o->title = SANE_I18N ("chroma of blue");
+ o->desc = SANE_I18N ("Set chroma of blue");
+ o->type = SANE_TYPE_INT;
+ o->unit = SANE_UNIT_NONE;
+ o->size = sizeof (SANE_Int);
+ o->constraint_type = SANE_CONSTRAINT_RANGE;
+ o->constraint.range = &(byte_value_range);
+ s->val[BLUE_CHROMA].w = 0;
+
+ o = &s->opt[DESKEW];
+ o->name = "deskew";
+ o->title = SANE_I18N ("Skew adjustment");
+ o->desc = SANE_I18N ("Skew adjustment");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[DESKEW].w = SANE_FALSE;
+ if (s->id != KV_S4085CL && s->id != KV_S4085CW)
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[STOP_SKEW];
+ o->name = "stop-skew";
+ o->title = SANE_I18N ("Stop scanner when a paper have been skewed");
+ o->desc = SANE_I18N ("Scanner will be stop when a paper have been skewed");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[STOP_SKEW].w = SANE_FALSE;
+
+ o = &s->opt[CROP];
+ o->name = "crop";
+ o->title = SANE_I18N ("Crop actual image area");
+ o->desc = SANE_I18N ("Scanner automatically detect image area and crop it");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[CROP].w = SANE_FALSE;
+ if (s->id != KV_S4085CL && s->id != KV_S4085CW)
+ o->cap |= SANE_CAP_INACTIVE;
+
+ o = &s->opt[MIRROR];
+ o->name = "mirror";
+ o->title = SANE_I18N ("Mirror image");
+ o->desc = SANE_I18N ("It is right and left reversing");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[MIRROR].w = SANE_FALSE;
+
+ o = &s->opt[TOPPOS];
+ o->name = "toppos";
+ o->title = SANE_I18N ("Addition of space in top position");
+ o->desc = SANE_I18N ("Addition of space in top position");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[TOPPOS].w = SANE_FALSE;
+
+ o = &s->opt[BTMPOS];
+ o->name = "btmpos";
+ o->title = SANE_I18N ("Addition of space in bottom position");
+ o->desc = SANE_I18N ("Addition of space in bottom position");
+ o->type = SANE_TYPE_BOOL;
+ o->unit = SANE_UNIT_NONE;
+ s->val[BTMPOS].w = SANE_FALSE;
+}
+
+
+/* Lookup a string list from one array and return its index. */
+static int
+str_index (const SANE_String_Const * list, SANE_String_Const name)
+{
+ int index;
+ index = 0;
+ while (list[index])
+ {
+ if (!strcmp (list[index], name))
+ return (index);
+ index++;
+ }
+ return (-1); /* not found */
+}
+
+/* Control option */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ int i;
+ SANE_Status status;
+ SANE_Word cap;
+ struct scanner *s = (struct scanner *) handle;
+
+ if (info)
+ *info = 0;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_UNSUPPORTED;
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ if (s->opt[option].type == SANE_TYPE_STRING)
+ {
+ DBG (DBG_INFO,
+ "sane_control_option: reading opt[%d] = %s\n",
+ option, s->val[option].s);
+ strcpy (val, s->val[option].s);
+ }
+ else
+ {
+ *(SANE_Word *) val = s->val[option].w;
+ DBG (DBG_INFO,
+ "sane_control_option: reading opt[%d] = %d\n",
+ option, s->val[option].w);
+ }
+ return SANE_STATUS_GOOD;
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (s->opt[option].type == SANE_TYPE_STRING)
+ {
+ if (!strcmp (val, s->val[option].s))
+ return SANE_STATUS_GOOD;
+ DBG (DBG_INFO,
+ "sane_control_option: writing opt[%d] = %s\n",
+ option, (SANE_String_Const) val);
+ }
+ else
+ {
+ if (*(SANE_Word *) val == s->val[option].w)
+ return SANE_STATUS_GOOD;
+ DBG (DBG_INFO,
+ "sane_control_option: writing opt[%d] = %d\n",
+ option, *(SANE_Word *) val);
+ }
+
+ switch (option)
+ {
+ /* Side-effect options */
+ case RESOLUTION:
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case TL_Y:
+ if ((*(SANE_Word *) val) + MIN_LENGTH <=
+ s->val[BR_Y].w &&
+ !check_area (s, s->val[TL_X].w, *(SANE_Word *) val,
+ s->val[BR_X].w, s->val[BR_Y].w))
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT |
+ SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ case BR_Y:
+ if ((*(SANE_Word *) val) >=
+ s->val[TL_Y].w + MIN_LENGTH
+ && !check_area (s, s->val[TL_X].w, s->val[TL_Y].w,
+ s->val[BR_X].w, *(SANE_Word *) val))
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT |
+ SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case TL_X:
+ if ((*(SANE_Word *) val) + MIN_WIDTH <=
+ s->val[BR_X].w &&
+ !check_area (s, *(SANE_Word *) val, s->val[TL_Y].w,
+ s->val[BR_X].w, s->val[BR_Y].w))
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT |
+ SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case BR_X:
+ if (*(SANE_Word *) val >=
+ s->val[TL_X].w + MIN_WIDTH
+ && !check_area (s, s->val[TL_X].w, s->val[TL_Y].w,
+ *(SANE_Word *) val, s->val[BR_Y].w))
+ {
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ else if (info)
+ *info |= SANE_INFO_INEXACT |
+ SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case LANDSCAPE:
+ s->val[option].w = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ /* Side-effect free options */
+ case CONTRAST:
+ case BRIGHTNESS:
+ case DUPLEX:
+ case LENGTHCTL:
+ case LONG_PAPER:
+ case FIT_TO_PAGE:
+ case THRESHOLD:
+ case INVERSE:
+ case COMPRESSION_PAR:
+ case DFSTOP:
+ case DFEED_L:
+ case DFEED_C:
+ case DFEED_R:
+ case STOP_SKEW:
+ case DESKEW:
+ case MIRROR:
+ case CROP:
+ case TOPPOS:
+ case BTMPOS:
+ case RED_CHROMA:
+ case BLUE_CHROMA:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case FEED_TIMEOUT:
+ s->val[option].w = *(SANE_Word *) val;
+ return kvs40xx_set_timeout (s, s->val[option].w);
+
+ /* String mode */
+ case IMAGE_EMPHASIS:
+ case GAMMA_CORRECTION:
+ case LAMP:
+ case HALFTONE_PATTERN:
+ case DFEED_SENCE:
+ case AUTOMATIC_THRESHOLD:
+ case WHITE_LEVEL:
+ case NOISE_REDUCTION:
+ strcpy (s->val[option].s, val);
+ return SANE_STATUS_GOOD;
+
+ case SOURCE:
+ strcpy (s->val[option].s, val);
+ if (strcmp (s->val[option].s, SANE_I18N ("adf")))
+ {
+ strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]);
+ strcpy (s->val[MANUALFEED].s, manual_feed_list[0]);
+ s->val[DUPLEX].w = SANE_FALSE;
+ s->val[DBLFEED].w = SANE_FALSE;
+ s->val[BTMPOS].w = SANE_FALSE;
+ s->val[TOPPOS].w = SANE_FALSE;
+ s->val[STOP_SKEW].w = SANE_FALSE;
+ s->val[LENGTHCTL].w = SANE_FALSE;
+ s->val[LONG_PAPER].w = SANE_FALSE;
+ s->opt[FEEDER_MODE].cap |= SANE_CAP_INACTIVE;
+ s->opt[MANUALFEED].cap |= SANE_CAP_INACTIVE;
+ s->opt[DUPLEX].cap |= SANE_CAP_INACTIVE;
+ s->opt[DBLFEED].cap |= SANE_CAP_INACTIVE;
+ s->opt[BTMPOS].cap |= SANE_CAP_INACTIVE;
+ s->opt[TOPPOS].cap |= SANE_CAP_INACTIVE;
+ s->opt[STOP_SKEW].cap |= SANE_CAP_INACTIVE;
+ s->opt[LENGTHCTL].cap |= SANE_CAP_INACTIVE;
+ s->opt[LONG_PAPER].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[FEEDER_MODE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[MANUALFEED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DUPLEX].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DBLFEED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[BTMPOS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[TOPPOS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[STOP_SKEW].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[LENGTHCTL].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[LONG_PAPER].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case FEEDER_MODE:
+ strcpy (s->val[option].s, val);
+ if (strcmp (s->val[option].s, SANE_I18N ("continuous")))
+ {
+ s->opt[LONG_PAPER].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[LONG_PAPER].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case MODE:
+ strcpy (s->val[option].s, val);
+ if (!strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[COMPRESSION].cap |= SANE_CAP_INACTIVE;
+ s->opt[COMPRESSION_PAR].cap |= SANE_CAP_INACTIVE;
+ s->opt[THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+
+ s->opt[AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[INVERSE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[RED_CHROMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[BLUE_CHROMA].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[COMPRESSION].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[COMPRESSION_PAR].cap &= ~SANE_CAP_INACTIVE;
+
+ s->opt[THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[INVERSE].cap |= SANE_CAP_INACTIVE;
+ s->opt[HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[WHITE_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->opt[NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[RED_CHROMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[BLUE_CHROMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (!strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ s->opt[INVERSE].cap &= ~SANE_CAP_INACTIVE;
+
+ s->opt[GAMMA_CORRECTION].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ case MANUALFEED:
+ strcpy (s->val[option].s, val);
+ if (strcmp (s->val[option].s, manual_feed_list[0]) == 0) /* off */
+ s->opt[FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE;
+ else
+ s->opt[FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case STAPELED_DOC:
+ strcpy (s->val[option].s, val);
+ if (strcmp (s->val[option].s, stapeled_list[0]) == 0)
+ {
+ s->opt[DBLFEED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFSTOP].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_L].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_SENCE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[DBLFEED].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFSTOP].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_L].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_C].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_SENCE].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case DBLFEED:
+ s->val[option].w = *(SANE_Word *) val;
+ if (!s->val[option].b)
+ {
+ s->opt[DFSTOP].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_L].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_C].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[DFEED_SENCE].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[DFSTOP].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_L].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[DFEED_SENCE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case PAPER_SIZE:
+ strcpy (s->val[option].s, val);
+ i = str_index (paper_list, s->val[option].s);
+ if (i == 0)
+ { /*user def */
+ s->opt[TL_X].cap &=
+ s->opt[TL_Y].cap &=
+ s->opt[BR_X].cap &= s->opt[BR_Y].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ s->val[LANDSCAPE].w = 0;
+ }
+ else
+ {
+ s->opt[TL_X].cap |=
+ s->opt[TL_Y].cap |=
+ s->opt[BR_X].cap |= s->opt[BR_Y].cap |= SANE_CAP_INACTIVE;
+ if ( /*i == 4 || */ i == 5 || i == 6 /*XXX*/
+ || i == 10 || i == 11)
+ { /*A4, A5, A6, B5, B6 */
+ if ((s->id == KV_S4085CL || s->id == KV_S4065CL)
+ && i == 4 && i == 10)
+ { /*A4, B5 */
+ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ s->val[LANDSCAPE].w = 0;
+ }
+ else
+ s->opt[LANDSCAPE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE;
+ s->val[LANDSCAPE].w = 0;
+ }
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ case COMPRESSION:
+ s->val[option].w = *(SANE_Word *) val;
+ if (!s->val[option].b)
+ {
+ s->opt[COMPRESSION_PAR].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[COMPRESSION_PAR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+kvs40xx_init_window (struct scanner *s, struct window *wnd, int wnd_id)
+{
+ int paper = str_index (paper_list, s->val[PAPER_SIZE].s), i;
+ memset (wnd, 0, sizeof (struct window));
+ *(u16 *) wnd->window_descriptor_block_length = cpu2be16 (66);
+
+ wnd->window_identifier = wnd_id;
+ *(u16 *) wnd->x_resolution = cpu2be16 (s->val[RESOLUTION].w);
+ *(u16 *) wnd->y_resolution = cpu2be16 (s->val[RESOLUTION].w);
+ if (!paper)
+ {
+ *(u32 *) wnd->upper_left_x =
+ cpu2be32 (mm2scanner_units (s->val[TL_X].w));
+ *(u32 *) wnd->upper_left_y =
+ cpu2be32 (mm2scanner_units (s->val[TL_Y].w));
+ *(u32 *) wnd->document_width =
+ cpu2be32 (mm2scanner_units (s->val[BR_X].w));
+ *(u32 *) wnd->width =
+ cpu2be32 (mm2scanner_units (s->val[BR_X].w - s->val[TL_X].w));
+ *(u32 *) wnd->document_length = cpu2be32 (mm2scanner_units
+ (s->val[BR_Y].w));
+ *(u32 *) wnd->length =
+ cpu2be32 (mm2scanner_units (s->val[BR_Y].w - s->val[TL_Y].w));
+ }
+ else
+ {
+ u32 w = cpu2be32 (mm2scanner_units (paper_sizes[paper].width));
+ u32 h = cpu2be32 (mm2scanner_units (paper_sizes[paper].height));
+ *(u32 *) wnd->upper_left_x = cpu2be32 (mm2scanner_units (0));
+ *(u32 *) wnd->upper_left_y = cpu2be32 (mm2scanner_units (0));
+ if (!s->val[LANDSCAPE].b)
+ {
+ *(u32 *) wnd->document_width = *(u32 *) wnd->width = w;
+ *(u32 *) wnd->document_length = *(u32 *) wnd->length = h;
+ }
+ else
+ {
+ *(u32 *) wnd->document_width = *(u32 *) wnd->width = h;
+ *(u32 *) wnd->document_length = *(u32 *) wnd->length = w;
+ }
+ }
+ wnd->brightness = s->val[BRIGHTNESS].w;
+ wnd->threshold = s->val[THRESHOLD].w;
+ wnd->contrast = s->val[CONTRAST].w;
+ wnd->image_composition = mode_val[str_index (mode_list, s->val[MODE].s)];
+ wnd->bit_per_pixel = bps_val[str_index (mode_list, s->val[MODE].s)];
+
+ *(u16 *) wnd->halftone_pattern =
+ cpu2be16 (str_index (halftone_pattern, s->val[HALFTONE_PATTERN].s));
+
+ wnd->rif_padding = s->val[INVERSE].b << 7;
+ *(u16 *) wnd->bit_ordering = cpu2be16 (BIT_ORDERING);
+ wnd->compression_type = s->val[COMPRESSION].b ? 0x81 : 0;
+ wnd->compression_argument = s->val[COMPRESSION_PAR].w;
+
+ wnd->vendor_unique_identifier = 0;
+ wnd->nobuf_fstspeed_dfstop = str_index (source_list,
+ s->val[SOURCE].s) << 7 |
+ str_index (stapeled_list,
+ s->val[STAPELED_DOC].s) << 5 |
+ s->val[STOP_SKEW].b << 4 | s->val[CROP].b << 3 | s->val[DFSTOP].b << 0;
+
+ wnd->mirror_image = s->val[MIRROR].b << 7 |
+ s->val[DFEED_L].b << 2 | s->val[DFEED_C].b << 1 | s->val[DFEED_R].b << 0;
+ wnd->image_emphasis = str_index (image_emphasis_list,
+ s->val[IMAGE_EMPHASIS].s);
+ wnd->gamma_correction = gamma_val[str_index (gamma_list,
+ s->val[GAMMA_CORRECTION].s)];
+ wnd->mcd_lamp_dfeed_sens =
+ str_index (lamp_list, s->val[LAMP].s) << 4 |
+ str_index (dfeed_sence_list, s->val[DFEED_SENCE].s);
+
+ wnd->document_size = (paper != 0) << 7
+ | s->val[LENGTHCTL].b << 6
+ | s->val[LONG_PAPER].b << 5 | s->val[LANDSCAPE].b << 4 | paper_val[paper];
+
+ wnd->ahead_deskew_dfeed_scan_area_fspeed_rshad =
+ (s->val[DESKEW].b || s->val[CROP].b ? 2 : 0) << 5 | /*XXX*/
+ s->val[DBLFEED].b << 4 | s->val[FIT_TO_PAGE].b << 2;
+ wnd->continuous_scanning_pages =
+ str_index (feeder_mode_list, s->val[FEEDER_MODE].s) ? 0xff : 0;
+ wnd->automatic_threshold_mode = automatic_threshold_val
+ [str_index (automatic_threshold_list, s->val[AUTOMATIC_THRESHOLD].s)];
+ wnd->automatic_separation_mode = 0; /*Does not supported */
+ wnd->standard_white_level_mode =
+ white_level_val[str_index (white_level_list, s->val[WHITE_LEVEL].s)];
+ wnd->b_wnr_noise_reduction =
+ str_index (noise_reduction_list, s->val[NOISE_REDUCTION].s);
+
+ i = str_index (manual_feed_list, s->val[MANUALFEED].s);
+ wnd->mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr = i << 6 |
+ s->val[TOPPOS].b << 5 | s->val[BTMPOS].b << 4;
+ wnd->stop_mode = 1;
+ wnd->red_chroma = s->val[RED_CHROMA].w;
+ wnd->blue_chroma = s->val[BLUE_CHROMA].w;
+}
+
+/* Get scan parameters */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Parameters *p = &s->params;
+
+ if (!s->scanning)
+ {
+ unsigned w, h, res = s->val[RESOLUTION].w;
+ unsigned i = str_index (paper_list,
+ s->val[PAPER_SIZE].s);
+ if (i)
+ {
+ if (s->val[LANDSCAPE].b)
+ {
+ w = paper_sizes[i].height;
+ h = paper_sizes[i].width;
+ }
+ else
+ {
+ w = paper_sizes[i].width;
+ h = paper_sizes[i].height;
+ }
+ }
+ else
+ {
+ w = s->val[BR_X].w - s->val[TL_X].w;
+ h = s->val[BR_Y].w - s->val[TL_Y].w;
+ }
+ p->pixels_per_line = w * res / 25.4 + .5;
+ p->lines = h * res / 25.4 + .5;
+ }
+
+ p->format = !strcmp (s->val[MODE].s,
+ SANE_VALUE_SCAN_MODE_COLOR) ? SANE_FRAME_RGB :
+ SANE_FRAME_GRAY;
+ p->last_frame = SANE_TRUE;
+ p->depth = bps_val[str_index (mode_list, s->val[MODE].s)];
+ p->bytes_per_line = p->depth * p->pixels_per_line / 8;
+ if (p->depth > 8)
+ p->depth = 8;
+ if (params)
+ memcpy (params, p, sizeof (SANE_Parameters));
+ s->side_size = p->bytes_per_line * p->lines;
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/leo.c b/backend/leo.c
new file mode 100644
index 0000000..b44a343
--- /dev/null
+++ b/backend/leo.c
@@ -0,0 +1,2014 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002-2003 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ Across FS-1130
+*/
+
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 11 /* 2004/06/30 */
+#define BACKEND_NAME leo
+#define LEO_CONFIG_FILE "leo.conf"
+
+/*--------------------------------------------------------------------------*/
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "leo.h"
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list[] = {
+ BLACK_WHITE_STR,
+ GRAY_STR,
+ COLOR_STR,
+ NULL
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Minimum and maximum width and length supported. */
+static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 };
+static SANE_Range y_range =
+ { SANE_FIX (0), SANE_FIX (11.5 * MM_PER_INCH), 0 };
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+static SANE_String_Const halftone_pattern_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Diamond"),
+ SANE_I18N ("8x8 Coarse Fatting"),
+ SANE_I18N ("8x8 Fine Fatting"),
+ SANE_I18N ("8x8 Bayer"),
+ SANE_I18N ("8x8 Vertical Line"),
+ NULL
+};
+
+static const halftone_pattern_t *const halftone_pattern_val[] = {
+ NULL,
+ &haltfone_pattern_diamond,
+ &haltfone_pattern_8x8_Coarse_Fatting,
+ &haltfone_pattern_8x8_Fine_Fatting,
+ &haltfone_pattern_8x8_Bayer,
+ &haltfone_pattern_8x8_Vertical_Line
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define the supported scanners and their characteristics. */
+static const struct scanners_supported scanners[] = {
+ {6, "ACROSS ", " ",
+ "Across", "FS-1130"},
+ {6, "LEO ", "LEOScan-S3 ",
+ "Leo", "S3"},
+ {6, "LEO", "LEOScan-S3",
+ "Leo", "S3"},
+ {6, "KYE CORP", "ColorPage-CS ",
+ "Genius", "FS1130"}
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of scanner attached. */
+static Leo_Scanner *first_dev = NULL;
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+
+
+/* Local functions. */
+
+/* Display a buffer in the log. */
+static void
+hexdump (int level, const char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+ char asc_buf[17];
+ char *asc_ptr;
+
+ DBG (level, "%s\n", comment);
+
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ DBG (level, "%s %s\n", line, asc_buf);
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ ptr += sprintf (ptr, " %2.2x", *p);
+ if (*p >= 32 && *p <= 127)
+ {
+ asc_ptr += sprintf (asc_ptr, "%c", *p);
+ }
+ else
+ {
+ asc_ptr += sprintf (asc_ptr, ".");
+ }
+ }
+ *ptr = '\0';
+ DBG (level, "%s %s\n", line, asc_buf);
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+get_string_list_index (SANE_String_Const list[], SANE_String_Const name)
+{
+ int index;
+
+ index = 0;
+ while (list[index] != NULL)
+ {
+ if (strcmp (list[index], name) == 0)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "name %s not found in list\n", name);
+
+ assert (0 == 1); /* bug in backend, core dump */
+
+ return (-1);
+}
+
+/* Initialize a scanner entry. Return an allocated scanner with some
+ * preset values. */
+static Leo_Scanner *
+leo_init (void)
+{
+ Leo_Scanner *dev;
+
+ DBG (DBG_proc, "leo_init: enter\n");
+
+ /* Allocate a new scanner entry. */
+ dev = malloc (sizeof (Leo_Scanner));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ memset (dev, 0, sizeof (Leo_Scanner));
+
+ /* Allocate the buffer used to transfer the SCSI data. */
+ dev->buffer_size = 64 * 1024;
+ dev->buffer = malloc (dev->buffer_size);
+ if (dev->buffer == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+ /* Allocate a buffer to store the temporary image. */
+ dev->image_size = 64 * 1024; /* enough for 1 line at max res */
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ free (dev->buffer);
+ free (dev);
+ return NULL;
+ }
+
+ dev->sfd = -1;
+
+ DBG (DBG_proc, "leo_init: exit\n");
+
+ return (dev);
+}
+
+/* Closes an open scanner. */
+static void
+leo_close (Leo_Scanner * dev)
+{
+ DBG (DBG_proc, "leo_close: enter\n");
+
+ if (dev->sfd != -1)
+ {
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+ }
+
+ DBG (DBG_proc, "leo_close: exit\n");
+}
+
+/* Frees the memory used by a scanner. */
+static void
+leo_free (Leo_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "leo_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ leo_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+
+ free (dev);
+
+ DBG (DBG_proc, "leo_free: exit\n");
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+leo_identify_scanner (Leo_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "leo_identify_scanner: enter\n");
+
+ size = 5;
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "leo_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ size = dev->buffer[4] + 5; /* total length of the inquiry data */
+
+ if (size < 36)
+ {
+ DBG (DBG_error,
+ "leo_identify_scanner: not enough data to identify device\n");
+ return (SANE_FALSE);
+ }
+
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "leo_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ dev->scsi_type = dev->buffer[0] & 0x1f;
+ memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08);
+ dev->scsi_vendor[0x08] = 0;
+ memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010);
+ dev->scsi_product[0x10] = 0;
+ memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04);
+ dev->scsi_version[0x04] = 0;
+
+ DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\"\n",
+ dev->scsi_vendor, dev->scsi_product, dev->scsi_version);
+
+ /* Lookup through the supported scanners table to find if this
+ * backend supports that one. */
+ for (i = 0; i < NELEMS (scanners); i++)
+ {
+ if (dev->scsi_type == scanners[i].scsi_type &&
+ strcmp (dev->scsi_vendor, scanners[i].scsi_vendor) == 0 &&
+ strcmp (dev->scsi_product, scanners[i].scsi_product) == 0)
+ {
+
+ DBG (DBG_error, "leo_identify_scanner: scanner supported\n");
+
+ /* Get the full inquiry, since that scanner does not fill
+ the length correctly. */
+ size = 0x30;
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (SANE_FALSE);
+ }
+
+ hexdump (DBG_info2, "inquiry", dev->buffer, size);
+
+ dev->def = &(scanners[i]);
+ dev->res_range.min = 1;
+ dev->res_range.max = B16TOI (&dev->buffer[42]);
+
+ dev->x_resolution_max = B16TOI (&dev->buffer[40]);
+ dev->y_resolution_max = B16TOI (&dev->buffer[42]);
+
+
+ return (SANE_TRUE);
+ }
+ }
+
+ DBG (DBG_proc, "leo_identify_scanner: exit, device not supported\n");
+
+ return (SANE_FALSE);
+}
+
+/* SCSI sense handler. Callback for SANE. */
+static SANE_Status
+leo_sense_handler (int scsi_fd, unsigned char *result, void __sane_unused__ *arg)
+{
+ int asc, ascq, sensekey;
+ int len;
+
+ DBG (DBG_proc, "leo_sense_handler (scsi_fd = %d)\n", scsi_fd);
+
+ sensekey = get_RS_sense_key (result);
+ len = 7 + get_RS_additional_length (result);
+
+ hexdump (DBG_info2, "sense", result, len);
+
+ if (get_RS_error_code (result) != 0x70)
+ {
+ DBG (DBG_error,
+ "leo_sense_handler: invalid sense key error code (%d)\n",
+ get_RS_error_code (result));
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (get_RS_ILI (result) != 0)
+ {
+ DBG (DBG_sense, "leo_sense_handler: short read\n");
+ }
+
+ if (len < 14)
+ {
+ DBG (DBG_error, "leo_sense_handler: sense too short, no ASC/ASCQ\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ asc = get_RS_ASC (result);
+ ascq = get_RS_ASCQ (result);
+
+ DBG (DBG_sense, "leo_sense_handler: sense=%d, ASC/ASCQ=%02x%02x\n",
+ sensekey, asc, ascq);
+
+ switch (sensekey)
+ {
+ case 0x00: /* no sense */
+ case 0x02: /* not ready */
+ case 0x03: /* medium error */
+ case 0x05:
+ case 0x06:
+ break;
+ }
+
+ DBG (DBG_sense,
+ "leo_sense_handler: unknown error condition. Please report it to the backend maintainer\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Set a window. */
+static SANE_Status
+leo_set_window (Leo_Scanner * dev)
+{
+ size_t size;
+ CDB cdb;
+ unsigned char window[48];
+ SANE_Status status;
+
+ DBG (DBG_proc, "leo_set_window: enter\n");
+
+ size = sizeof (window);
+ MKSCSI_SET_WINDOW (cdb, size);
+
+ memset (window, 0, size);
+
+ /* size of the windows descriptor block */
+ window[7] = sizeof (window) - 8;
+ window[1] = sizeof (window) - 2;
+
+ /* X and Y resolution */
+ Ito16 (dev->x_resolution, &window[10]);
+ Ito16 (dev->y_resolution, &window[12]);
+
+ /* Upper Left (X,Y) */
+ Ito32 (dev->x_tl, &window[14]);
+ Ito32 (dev->y_tl, &window[18]);
+
+ /* Width and length */
+ Ito32 (dev->width, &window[22]);
+ Ito32 (dev->length, &window[26]);
+
+
+ /* Image Composition */
+ switch (dev->scan_mode)
+ {
+ case LEO_BW:
+ window[33] = 0x00;
+ break;
+ case LEO_HALFTONE:
+ window[33] = 0x01;
+ break;
+ case LEO_GRAYSCALE:
+ window[33] = 0x02;
+ break;
+ case LEO_COLOR:
+ window[33] = 0x05;
+ break;
+ }
+
+ /* Depth */
+ window[34] = dev->depth;
+
+ /* Unknown - invariants */
+ window[31] = 0x80;
+ window[43] = 0x01;
+
+ hexdump (DBG_info2, "windows", window, sizeof (window));
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ window, sizeof (window), NULL, NULL);
+
+ DBG (DBG_proc, "leo_set_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Read the size of the scan. */
+static SANE_Status
+leo_get_scan_size (Leo_Scanner * dev)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "leo_get_scan_size: enter\n");
+
+ size = 0x10;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (size != 0x10)
+ {
+ DBG (DBG_error,
+ "leo_get_scan_size: GET DATA BUFFER STATUS returned an invalid size (%ld)\n",
+ (long) size);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ hexdump (DBG_info2, "leo_get_scan_size return", dev->buffer, size);
+
+
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+
+ /* The number of lines if the number of lines left plus the number
+ * of lines already waiting in the buffer. */
+ dev->params.lines = B16TOI (&dev->buffer[12]) +
+ (B24TOI (&dev->buffer[9]) / dev->params.bytes_per_line);
+
+ switch (dev->scan_mode)
+ {
+ case LEO_BW:
+ case LEO_HALFTONE:
+ dev->params.pixels_per_line &= ~0x7;
+ dev->params.bytes_per_line = dev->params.pixels_per_line / 8;
+ break;
+ case LEO_GRAYSCALE:
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ break;
+ case LEO_COLOR:
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ break;
+ }
+
+ DBG (DBG_proc, "leo_get_scan_size: exit, status=%d\n", status);
+
+ DBG (DBG_proc, "lines=%d, bpl=%d\n", dev->params.lines,
+ dev->params.bytes_per_line);
+
+ return (status);
+}
+
+/* Return the number of byte that can be read. */
+static SANE_Status
+get_filled_data_length (Leo_Scanner * dev, size_t * to_read)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "get_filled_data_length: enter\n");
+
+ *to_read = 0;
+
+ size = 0x10;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (size != 0x10)
+ {
+ DBG (DBG_error,
+ "get_filled_data_length: GET DATA BUFFER STATUS returned an invalid size (%ld)\n",
+ (long) size);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ hexdump (DBG_info2, "get_filled_data_length return", dev->buffer, size);
+
+ *to_read = B24TOI (&dev->buffer[9]);
+
+ DBG (DBG_info, "get_filled_data_length: to read = %ld\n", (long) *to_read);
+
+ DBG (DBG_proc, "get_filled_data_length: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Start a scan. */
+static SANE_Status
+leo_scan (Leo_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "leo_scan: enter\n");
+
+ MKSCSI_SCAN (cdb);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "leo_scan: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Attach a scanner to this backend. */
+static SANE_Status
+attach_scanner (const char *devicename, Leo_Scanner ** devp)
+{
+ Leo_Scanner *dev;
+ int sfd;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new scanner entry. */
+ dev = leo_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+ if (sanei_scsi_open (devicename, &sfd, leo_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_scanner: open failed\n");
+ leo_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Now, check that it is a scanner we support. */
+ if (leo_identify_scanner (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ leo_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ leo_close (dev);
+
+ /* Set the default options for that scanner. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->def->real_vendor;
+ dev->sane.model = dev->def->real_product;
+ dev->sane.type = "flatbed scanner";
+
+ /* Link the scanner with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_scanner: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that scanner. */
+static void
+leo_init_options (Leo_Scanner * dev)
+{
+ int i;
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RESOLUTION].constraint.range = &dev->res_range;
+ dev->val[OPT_RESOLUTION].w = dev->res_range.max / 2;
+
+ /* Halftone pattern */
+ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ dev->opt[OPT_HALFTONE_PATTERN].size =
+ max_string_size (halftone_pattern_list);
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
+ halftone_pattern_list;
+ dev->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]);
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &x_range;
+ dev->val[OPT_TL_X].w = x_range.min;
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &y_range;
+ dev->val[OPT_TL_Y].w = y_range.min;
+
+ /* Bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &x_range;
+ dev->val[OPT_BR_X].w = x_range.max;
+
+ /* Bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &y_range;
+ dev->val[OPT_BR_Y].w = y_range.max;
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* custom-gamma table */
+ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R;
+
+ /* green gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G;
+
+ /* blue gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B;
+
+ /* grayscale gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_GRAY].wa = dev->gamma_GRAY;
+
+ /* preview */
+ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scan_mode_list[0], NULL);
+}
+
+/*
+ * Wait until the scanner is ready.
+ */
+static SANE_Status
+leo_wait_scanner (Leo_Scanner * dev)
+{
+ SANE_Status status;
+ int timeout;
+ CDB cdb;
+
+ DBG (DBG_proc, "leo_wait_scanner: enter\n");
+
+ MKSCSI_TEST_UNIT_READY (cdb);
+
+ /* Set the timeout to 60 seconds. */
+ timeout = 60;
+
+ while (timeout > 0)
+ {
+
+ /* test unit ready */
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, NULL, NULL);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ };
+
+ DBG (DBG_proc, "leo_wait_scanner: scanner not ready\n");
+ return (SANE_STATUS_IO_ERROR);
+}
+
+/* Read the image from the scanner and fill the temporary buffer with it. */
+static SANE_Status
+leo_fill_image (Leo_Scanner * dev)
+{
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+ unsigned char *image;
+
+ DBG (DBG_proc, "leo_fill_image: enter\n");
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ while (dev->real_bytes_left)
+ {
+ /*
+ * Try to read the maximum number of bytes.
+ */
+ size = 0;
+ while (size == 0)
+ {
+ status = get_filled_data_length (dev, &size);
+ if (status)
+ return (status);
+ if (size == 0)
+ usleep (100000); /* sleep 1/10th of second */
+ }
+
+ if (size > dev->real_bytes_left)
+ size = dev->real_bytes_left;
+ if (size > dev->image_size - dev->image_end)
+ size = dev->image_size - dev->image_end;
+
+ /* The scanner seems to hang if more than 32KB are read. */
+ if (size > 0x7fff)
+ size = 0x7fff;
+
+ /* Always read a multiple of a line. */
+ size = size - (size % dev->params.bytes_per_line);
+
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_info, "leo_fill_image: to read = %ld bytes (bpl=%d)\n",
+ (long) size, dev->params.bytes_per_line);
+
+ MKSCSI_READ_10 (cdb, 0, 0, size);
+
+ hexdump (DBG_info2, "leo_fill_image: READ_10 CDB", cdb.data, 10);
+
+ image = dev->image + dev->image_end;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, image, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "leo_fill_image: cannot read from the scanner\n");
+ return status;
+ }
+
+ /* Some format conversion. */
+ if (dev->scan_mode == LEO_COLOR)
+ {
+
+ /* Reorder the lines. The scanner gives color by color for
+ * each line. */
+ unsigned char *src = image;
+ int nb_lines = size / dev->params.bytes_per_line;
+ int i, j;
+
+ for (i = 0; i < nb_lines; i++)
+ {
+
+ unsigned char *dest = dev->buffer;
+
+ for (j = 0; j < dev->params.pixels_per_line; j++)
+ {
+ *dest = src[j + 0 * dev->params.pixels_per_line];
+ dest++;
+ *dest = src[j + 1 * dev->params.pixels_per_line];
+ dest++;
+ *dest = src[j + 2 * dev->params.pixels_per_line];
+ dest++;
+ }
+
+ /* Copy the line back. */
+ memcpy (src, dev->buffer, dev->params.bytes_per_line);
+
+ src += dev->params.bytes_per_line;
+ }
+ }
+
+ dev->image_end += size;
+ dev->real_bytes_left -= size;
+
+ DBG (DBG_info, "leo_fill_image: real bytes left = %ld\n",
+ (long) dev->real_bytes_left);
+ }
+
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+/* Copy from the raw buffer to the buffer given by the backend.
+ *
+ * len in input is the maximum length available in buf, and, in
+ * output, is the length written into buf.
+ */
+static void
+leo_copy_raw_to_frontend (Leo_Scanner * dev, SANE_Byte * buf, size_t * len)
+{
+ size_t size;
+
+ size = dev->image_end - dev->image_begin;
+ if (size > *len)
+ {
+ size = *len;
+ }
+ *len = size;
+
+ memcpy (buf, dev->image + dev->image_begin, size);
+
+ dev->image_begin += size;
+}
+
+/* Stop a scan. */
+static SANE_Status
+do_cancel (Leo_Scanner * dev)
+{
+ DBG (DBG_sane_proc, "do_cancel enter\n");
+
+ if (dev->scanning == SANE_TRUE)
+ {
+
+ /* Reset the scanner */
+ dev->x_tl = 0;
+ dev->x_tl = 0;
+ dev->width = 0;
+ dev->length = 0;
+ leo_set_window (dev);
+
+ leo_scan (dev);
+
+ leo_close (dev);
+ }
+
+ dev->scanning = SANE_FALSE;
+
+ DBG (DBG_sane_proc, "do_cancel exit\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/* A default gamma table. */
+static const SANE_Word gamma_init[GAMMA_LENGTH] = {
+ 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,
+ 0x21, 0x23, 0x25, 0x27,
+ 0x28, 0x2A, 0x2C, 0x2D, 0x2F, 0x30, 0x32, 0x33, 0x35, 0x36, 0x38, 0x39,
+ 0x3A, 0x3C, 0x3D, 0x3F,
+ 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4D, 0x4E,
+ 0x4F, 0x50, 0x51, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60,
+ 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x80,
+ 0x81, 0x82, 0x83, 0x84,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x92,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0x9F, 0xA0,
+ 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xAA,
+ 0xAB, 0xAC, 0xAD, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB9, 0xB9, 0xBA,
+ 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, 0xC3,
+ 0xC4, 0xC5, 0xC6, 0xC6,
+ 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD2,
+ 0xD3, 0xD4, 0xD5, 0xD5, 0xD6, 0xD7, 0xD7, 0xD8, 0xD9, 0xDA, 0xDA, 0xDB,
+ 0xDC, 0xDC, 0xDD, 0xDE,
+ 0xDF, 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE4, 0xE4, 0xE5, 0xE6, 0xE6,
+ 0xE7, 0xE8, 0xE8, 0xE9,
+ 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1,
+ 0xF2, 0xF3, 0xF4, 0xF4,
+ 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC,
+ 0xFD, 0xFE, 0xFE, 0xFF
+};
+
+/* Send the gamma */
+static SANE_Status
+leo_send_gamma (Leo_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ struct
+ {
+ unsigned char gamma_R[GAMMA_LENGTH];
+ unsigned char gamma_G[GAMMA_LENGTH]; /* also gray */
+ unsigned char gamma_B[GAMMA_LENGTH];
+ }
+ param;
+ size_t i;
+ size_t size;
+
+ DBG (DBG_proc, "leo_send_gamma: enter\n");
+
+ size = sizeof (param);
+ assert (size == 3 * GAMMA_LENGTH);
+ MKSCSI_SEND_10 (cdb, 0x03, 0x01, size);
+
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* Use the custom gamma. */
+ if (dev->scan_mode == LEO_GRAYSCALE)
+ {
+ /* Gray */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = dev->gamma_GRAY[i];
+ param.gamma_G[i] = 0;
+ param.gamma_B[i] = 0;
+ }
+ }
+ else
+ {
+ /* Color */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = dev->gamma_R[i];
+ param.gamma_G[i] = dev->gamma_G[i];
+ param.gamma_B[i] = dev->gamma_B[i];
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = gamma_init[i];
+ param.gamma_G[i] = gamma_init[i];
+ param.gamma_B[i] = gamma_init[i];
+ }
+ }
+
+ hexdump (DBG_info2, "leo_send_gamma:", cdb.data, cdb.len);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ &param, size, NULL, NULL);
+
+ DBG (DBG_proc, "leo_send_gamma: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Send the halftone pattern */
+static SANE_Status
+leo_send_halftone_pattern (Leo_Scanner * dev)
+{
+ int i;
+ const halftone_pattern_t *pattern;
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+
+ DBG (DBG_proc, "leo_send_halftone_pattern: enter\n");
+
+ if (dev->scan_mode == LEO_HALFTONE)
+ {
+
+ i = get_string_list_index (halftone_pattern_list,
+ dev->val[OPT_HALFTONE_PATTERN].s);
+ pattern = halftone_pattern_val[i];
+
+ assert (pattern != NULL);
+
+ size = sizeof (halftone_pattern_t);
+ assert (size == 256);
+ MKSCSI_SEND_10 (cdb, 0x02, 0x0F, size);
+
+ hexdump (DBG_info2, "leo_send_gamma:", cdb.data, cdb.len);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ pattern, size, NULL, NULL);
+ }
+ else
+ {
+ status = SANE_STATUS_GOOD;
+ }
+
+ DBG (DBG_proc, "leo_send_halftone_pattern: exit, status=%d\n", status);
+
+ return status;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Sane entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ char dev_name[PATH_MAX];
+ size_t len;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ DBG (DBG_error, "This is sane-leo version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2002 by Frank Zago\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ fp = sanei_config_open (LEO_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach_scanner ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Leo_Scanner *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Leo_Scanner *dev;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No scanner found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ leo_init_options (dev);
+
+ /* Initialize the gamma table. */
+ memcpy (dev->gamma_R, gamma_init, dev->opt[OPT_GAMMA_VECTOR_R].size);
+ memcpy (dev->gamma_G, gamma_init, dev->opt[OPT_GAMMA_VECTOR_G].size);
+ memcpy (dev->gamma_B, gamma_init, dev->opt[OPT_GAMMA_VECTOR_B].size);
+ memcpy (dev->gamma_GRAY, gamma_init, dev->opt[OPT_GAMMA_VECTOR_GRAY].size);
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Leo_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Leo_Scanner *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ int i;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_PREVIEW:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ case OPT_HALFTONE_PATTERN:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* Gamma */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (val, dev->val[option].wa, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Numeric side-effect options */
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_RESOLUTION:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Numeric side-effect free options */
+ case OPT_PREVIEW:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect options */
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, BLACK_WHITE_STR) == 0)
+ {
+ i = get_string_list_index (halftone_pattern_list,
+ dev->val[OPT_HALFTONE_PATTERN].s);
+ if (halftone_pattern_val[i] == NULL)
+ {
+ dev->scan_mode = LEO_BW;
+ }
+ else
+ {
+ dev->scan_mode = LEO_HALFTONE;
+ }
+ dev->depth = 1;
+ dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, GRAY_STR) == 0)
+ {
+ dev->scan_mode = LEO_GRAYSCALE;
+ dev->depth = 8;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_STR) == 0)
+ {
+ dev->scan_mode = LEO_COLOR;
+ dev->depth = 8;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_HALFTONE_PATTERN:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ i = get_string_list_index (halftone_pattern_list,
+ dev->val[OPT_HALFTONE_PATTERN].s);
+ if (halftone_pattern_val[i] == NULL)
+ {
+ dev->scan_mode = LEO_BW;
+ }
+ else
+ {
+ dev->scan_mode = LEO_HALFTONE;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (dev->val[option].wa, val, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* use custom_gamma_table */
+ if (dev->scan_mode == LEO_GRAYSCALE)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Leo_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Setup the parameters for the scan. These values will be re-used
+ * in the SET WINDOWS command. */
+ if (dev->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ dev->x_resolution = 28;
+ dev->y_resolution = 28;
+ dev->x_tl = 0;
+ dev->y_tl = 0;
+ dev->x_br = mmToIlu (SANE_UNFIX (x_range.max));
+ dev->y_br = mmToIlu (SANE_UNFIX (y_range.max));
+ }
+ else
+ {
+ dev->x_resolution = dev->val[OPT_RESOLUTION].w;
+ dev->y_resolution = dev->val[OPT_RESOLUTION].w;
+ dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ }
+
+ /* Check the corners are OK. */
+ if (dev->x_tl > dev->x_br)
+ {
+ int s;
+ s = dev->x_tl;
+ dev->x_tl = dev->x_br;
+ dev->x_br = s;
+ }
+ if (dev->y_tl > dev->y_br)
+ {
+ int s;
+ s = dev->y_tl;
+ dev->y_tl = dev->y_br;
+ dev->y_br = s;
+ }
+
+ dev->width = dev->x_br - dev->x_tl;
+ dev->length = dev->y_br - dev->y_tl;
+
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.last_frame = SANE_TRUE;
+
+ switch (dev->scan_mode)
+ {
+ case LEO_BW:
+ case LEO_HALFTONE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line = dev->width & ~0x7;
+ dev->params.bytes_per_line = dev->params.pixels_per_line / 8;
+ dev->params.depth = 1;
+ break;
+ case LEO_GRAYSCALE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line = dev->width;
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ dev->params.depth = 8;
+ break;
+ case LEO_COLOR:
+ dev->params.format = SANE_FRAME_RGB;
+ dev->params.pixels_per_line = dev->width;
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->params.depth = 8;
+ break;
+ }
+
+ dev->params.lines = dev->length;
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Leo_Scanner *dev = handle;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ sane_get_parameters (dev, NULL);
+
+ /* Open again the scanner. */
+ if (sanei_scsi_open
+ (dev->devicename, &(dev->sfd), leo_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* The scanner must be ready. */
+ status = leo_wait_scanner (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ status = leo_set_window (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ status = leo_send_gamma (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ status = leo_send_halftone_pattern (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ status = leo_scan (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ status = leo_wait_scanner (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ status = leo_get_scan_size (dev);
+ if (status)
+ {
+ leo_close (dev);
+ return status;
+ }
+
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+ dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Leo_Scanner *dev = handle;
+ size_t size;
+ int buf_offset; /* offset into buf */
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning */
+ return do_cancel (dev);
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ buf_offset = 0;
+
+ do
+ {
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = leo_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Copy the data to the frontend buffer. */
+ size = max_len - buf_offset;
+ if (size > dev->bytes_left)
+ {
+ size = dev->bytes_left;
+ }
+ leo_copy_raw_to_frontend (dev, buf + buf_offset, &size);
+
+ buf_offset += size;
+
+ dev->bytes_left -= size;
+ *len += size;
+
+ }
+ while ((buf_offset != max_len) && dev->bytes_left);
+
+ DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n",
+ (long) dev->bytes_left);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ SANE_Status status;
+ Leo_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ if (!dev->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (non_blocking == SANE_FALSE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Leo_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ do_cancel (dev);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Leo_Scanner *dev = handle;
+ Leo_Scanner *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+ do_cancel (dev);
+ leo_close (dev);
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ leo_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/leo.conf.in b/backend/leo.conf.in
new file mode 100644
index 0000000..ecd918c
--- /dev/null
+++ b/backend/leo.conf.in
@@ -0,0 +1,8 @@
+# The FS-1130 respond to all luns
+scsi ACROSS * Scanner * * * 0
+
+# LEO S3
+scsi "LEO" "LEOScan-S3"
+
+/dev/scanner
+
diff --git a/backend/leo.h b/backend/leo.h
new file mode 100644
index 0000000..73bb7b1
--- /dev/null
+++ b/backend/leo.h
@@ -0,0 +1,554 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+*/
+
+/* Commands supported by the scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_INQUIRY 0x12
+#define SCSI_SCAN 0x1b
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_READ_10 0x28
+#define SCSI_SEND_10 0x2a
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_GET_DATA_BUFFER_STATUS 0x34
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+}
+CDB;
+
+
+/* Set a specific bit depending on a boolean.
+ * MKSCSI_BIT(TRUE, 3) will generate 0x08. */
+#define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0)
+
+/* Set a value in a range of bits.
+ * MKSCSI_I2B(5, 3, 5) will generate 0x28 */
+#define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1))
+
+/* Store an integer in 2, 3 or 4 byte in an array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+#define MKSCSI_GET_DATA_BUFFER_STATUS(cdb, wait, buflen) \
+ cdb.data[0] = SCSI_GET_DATA_BUFFER_STATUS; \
+ cdb.data[1] = MKSCSI_BIT(wait, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_INQUIRY(cdb, buflen) \
+ cdb.data[0] = SCSI_INQUIRY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SCAN(cdb) \
+ cdb.data[0] = SCSI_SCAN; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SEND_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_SEND_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_SET_WINDOW(cdb, buflen) \
+ cdb.data[0] = SCSI_SET_WINDOW; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_READ_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_REQUEST_SENSE(cdb, buflen) \
+ cdb.data[0] = SCSI_REQUEST_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (buflen); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_TEST_UNIT_READY(cdb) \
+ cdb.data[0] = SCSI_TEST_UNIT_READY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+/*--------------------------------------------------------------------------*/
+
+static int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7)
+
+/*--------------------------------------------------------------------------*/
+
+#define mmToIlu(mm) (((mm) * dev->x_resolution) / MM_PER_INCH)
+#define iluToMm(ilu) (((ilu) * MM_PER_INCH) / dev->x_resolution)
+
+/*--------------------------------------------------------------------------*/
+
+#define GAMMA_LENGTH 0x100 /* number of value per color */
+
+/*--------------------------------------------------------------------------*/
+
+enum Leo_Option
+{
+ /* Must come first */
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CUSTOM_GAMMA, /* Use the custom gamma tables */
+ OPT_GAMMA_VECTOR_R, /* Custom Red gamma table */
+ OPT_GAMMA_VECTOR_G, /* Custom Green Gamma table */
+ OPT_GAMMA_VECTOR_B, /* Custom Blue Gamma table */
+ OPT_GAMMA_VECTOR_GRAY, /* Custom Gray Gamma table */
+ OPT_HALFTONE_PATTERN, /* Halftone pattern */
+
+ OPT_PREVIEW, /* preview mode */
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Scanner supported by this backend.
+ */
+struct scanners_supported
+{
+ int scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+
+ const char *real_vendor;
+ const char *real_product;
+};
+
+/*--------------------------------------------------------------------------*/
+
+#define BLACK_WHITE_STR SANE_VALUE_SCAN_MODE_LINEART
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+/*--------------------------------------------------------------------------*/
+
+/* Define a scanner occurence. */
+typedef struct Leo_Scanner
+{
+ struct Leo_Scanner *next;
+ SANE_Device sane;
+
+ char *devicename;
+ int sfd; /* device handle */
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+ char scsi_version[5];
+
+ SANE_Range res_range;
+
+ int x_resolution_max; /* maximum X dpi */
+ int y_resolution_max; /* maximum Y dpi */
+
+ /* SCSI handling */
+ size_t buffer_size; /* size of the buffer */
+ SANE_Byte *buffer; /* for SCSI transfer. */
+
+
+ /* Scanner infos. */
+ const struct scanners_supported *def; /* default options for that scanner */
+
+ /* Scanning handling. */
+ int scanning; /* TRUE if a scan is running. */
+ int x_resolution; /* X resolution in DPI */
+ int y_resolution; /* Y resolution in DPI */
+ int x_tl; /* X top left */
+ int y_tl; /* Y top left */
+ int x_br; /* X bottom right */
+ int y_br; /* Y bottom right */
+ int width; /* width of the scan area in mm */
+ int length; /* length of the scan area in mm */
+ int pass; /* current pass number */
+
+ enum
+ {
+ LEO_BW,
+ LEO_HALFTONE,
+ LEO_GRAYSCALE,
+ LEO_COLOR
+ }
+ scan_mode;
+
+ int depth; /* depth per color */
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+
+ size_t real_bytes_left; /* number of bytes left the scanner will return. */
+
+ SANE_Byte *image; /* keep the raw image here */
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+
+ SANE_Parameters params;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+
+ /* Gamma table. 1 array per colour. */
+ SANE_Word gamma_R[GAMMA_LENGTH];
+ SANE_Word gamma_G[GAMMA_LENGTH];
+ SANE_Word gamma_B[GAMMA_LENGTH];
+ SANE_Word gamma_GRAY[GAMMA_LENGTH];
+}
+Leo_Scanner;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
+
+#define B24TOI(buf) \
+ ((((unsigned char *)buf)[0] << 16) | \
+ (((unsigned char *)buf)[1] << 8) | \
+ (((unsigned char *)buf)[2] << 0))
+
+#define B16TOI(buf) \
+ ((((unsigned char *)buf)[0] << 8) | \
+ (((unsigned char *)buf)[1] << 0))
+
+/*--------------------------------------------------------------------------*/
+
+/* Downloadable halftone patterns */
+typedef unsigned char halftone_pattern_t[256];
+
+static const halftone_pattern_t haltfone_pattern_diamond = {
+ 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x19, 0x61, 0xD8, 0xF0, 0xE0, 0x60, 0x20,
+ 0x00, 0x19, 0x61, 0xD8,
+ 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40,
+ 0x38, 0x58, 0x80, 0xB8,
+ 0x30, 0x50, 0x98, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x98, 0xB0,
+ 0xC8, 0xA8, 0x90, 0x48,
+ 0x08, 0x10, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x10, 0x70, 0xD0,
+ 0xF8, 0xE8, 0x68, 0x28,
+ 0x00, 0x18, 0x78, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x18, 0x78, 0xD8,
+ 0xF0, 0xE0, 0x60, 0x20,
+ 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8,
+ 0xC0, 0xA0, 0x88, 0x40,
+ 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x9B, 0xB0, 0xC8, 0xA8, 0x90, 0x48,
+ 0x30, 0x50, 0x9B, 0xB0,
+ 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x18, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28,
+ 0x08, 0x18, 0x70, 0xD0,
+ 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x19, 0x61, 0xD8, 0xF0, 0xE0, 0x60, 0x20,
+ 0x00, 0x19, 0x61, 0xD8,
+ 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40,
+ 0x38, 0x58, 0x80, 0xB8,
+ 0x30, 0x50, 0x98, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x98, 0xB0,
+ 0xC8, 0xA8, 0x90, 0x48,
+ 0x08, 0x10, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x10, 0x70, 0xD0,
+ 0xF8, 0xE8, 0x68, 0x28,
+ 0x00, 0x18, 0x78, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x18, 0x78, 0xD8,
+ 0xF0, 0xE0, 0x60, 0x20,
+ 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8,
+ 0xC0, 0xA0, 0x88, 0x40,
+ 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x9B, 0xB0, 0xC8, 0xA8, 0x90, 0x48,
+ 0x30, 0x50, 0x9B, 0xB0,
+ 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x18, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28,
+ 0x08, 0x18, 0x70, 0xD0
+};
+
+static const halftone_pattern_t haltfone_pattern_8x8_Coarse_Fatting = {
+ 0x12, 0x3A, 0xD2, 0xEA, 0xE2, 0xB6, 0x52, 0x1A, 0x12, 0x3A, 0xD2, 0xEA,
+ 0xE2, 0xB6, 0x52, 0x1A,
+ 0x42, 0x6A, 0x9A, 0xCA, 0xC2, 0x92, 0x72, 0x4A, 0x42, 0x6A, 0x9A, 0xCA,
+ 0xC2, 0x92, 0x72, 0x4A,
+ 0xAE, 0x8E, 0x7E, 0x26, 0x2E, 0x66, 0x86, 0xA6, 0xAE, 0x8E, 0x7E, 0x26,
+ 0x2E, 0x66, 0x86, 0xA6,
+ 0xFA, 0xBA, 0x5E, 0x06, 0x0E, 0x36, 0xDE, 0xF6, 0xFA, 0xBA, 0x5E, 0x06,
+ 0x0E, 0x36, 0xDE, 0xF6,
+ 0xE6, 0xBE, 0x56, 0x1E, 0x16, 0x3E, 0xD6, 0xEE, 0xE6, 0xBE, 0x56, 0x1E,
+ 0x16, 0x3E, 0xD6, 0xEE,
+ 0xC6, 0x96, 0x76, 0x4E, 0x46, 0x6E, 0x9E, 0xCE, 0xC6, 0x96, 0x76, 0x4E,
+ 0x46, 0x6E, 0x9E, 0xCE,
+ 0x2A, 0x62, 0x82, 0xA2, 0xAA, 0x8A, 0x7A, 0x22, 0x2A, 0x62, 0x82, 0xA2,
+ 0xAA, 0x8A, 0x7A, 0x22,
+ 0x0A, 0x32, 0xDA, 0xF2, 0xFE, 0xB2, 0x5A, 0x02, 0x0A, 0x32, 0xDA, 0xF2,
+ 0xFE, 0xB2, 0x5A, 0x02,
+ 0x12, 0x3A, 0xD2, 0xEA, 0xE2, 0xB6, 0x52, 0x1A, 0x12, 0x3A, 0xD2, 0xEA,
+ 0xE2, 0xB6, 0x52, 0x1A,
+ 0x42, 0x6A, 0x9A, 0xCA, 0xC2, 0x92, 0x72, 0x4A, 0x42, 0x6A, 0x9A, 0xCA,
+ 0xC2, 0x92, 0x72, 0x4A,
+ 0xAE, 0x8E, 0x7E, 0x26, 0x2E, 0x66, 0x86, 0xA6, 0xAE, 0x8E, 0x7E, 0x26,
+ 0x2E, 0x66, 0x86, 0xA6,
+ 0xFA, 0xBA, 0x5E, 0x06, 0x0E, 0x36, 0xDE, 0xF6, 0xFA, 0xBA, 0x5E, 0x06,
+ 0x0E, 0x36, 0xDE, 0xF6,
+ 0xE6, 0xBE, 0x56, 0x1E, 0x16, 0x3E, 0xD6, 0xEE, 0xE6, 0xBE, 0x56, 0x1E,
+ 0x16, 0x3E, 0xD6, 0xEE,
+ 0xC6, 0x96, 0x76, 0x4E, 0x46, 0x6E, 0x9E, 0xCE, 0xC6, 0x96, 0x76, 0x4E,
+ 0x46, 0x6E, 0x9E, 0xCE,
+ 0x2A, 0x62, 0x82, 0xA2, 0xAA, 0x8A, 0x7A, 0x22, 0x2A, 0x62, 0x82, 0xA2,
+ 0xAA, 0x8A, 0x7A, 0x22,
+ 0x0A, 0x32, 0xDA, 0xF2, 0xFE, 0xB2, 0x5A, 0x02, 0x0A, 0x32, 0xDA, 0xF2,
+ 0xFE, 0xB2, 0x5A, 0x02
+};
+
+static const halftone_pattern_t haltfone_pattern_8x8_Fine_Fatting = {
+ 0x02, 0x22, 0x92, 0xB2, 0x0A, 0x2A, 0x9A, 0xBA, 0x02, 0x22, 0x92, 0xB2,
+ 0x0A, 0x2A, 0x9A, 0xBA,
+ 0x42, 0x62, 0xD2, 0xF2, 0x4A, 0x6A, 0xDA, 0xFA, 0x42, 0x62, 0xD2, 0xF2,
+ 0x4A, 0x6A, 0xDA, 0xFA,
+ 0x82, 0xA2, 0x12, 0x32, 0x8A, 0xAA, 0x1A, 0x3A, 0x82, 0xA2, 0x12, 0x32,
+ 0x8A, 0xAA, 0x1A, 0x3A,
+ 0xC2, 0xE2, 0x52, 0x72, 0xCA, 0xEA, 0x5A, 0x7A, 0xC2, 0xE2, 0x52, 0x72,
+ 0xCA, 0xEA, 0x5A, 0x7A,
+ 0x0E, 0x2E, 0x9E, 0xBE, 0x06, 0x26, 0x96, 0xB6, 0x0E, 0x2E, 0x9E, 0xBE,
+ 0x06, 0x26, 0x96, 0xB6,
+ 0x4C, 0x6E, 0xDE, 0xFE, 0x46, 0x66, 0xD6, 0xF6, 0x4C, 0x6E, 0xDE, 0xFE,
+ 0x46, 0x66, 0xD6, 0xF6,
+ 0x8E, 0xAE, 0x1E, 0x3E, 0x86, 0xA6, 0x16, 0x36, 0x8E, 0xAE, 0x1E, 0x3E,
+ 0x86, 0xA6, 0x16, 0x36,
+ 0xCE, 0xEE, 0x60, 0x7E, 0xC6, 0xE6, 0x56, 0x76, 0xCE, 0xEE, 0x60, 0x7E,
+ 0xC6, 0xE6, 0x56, 0x76,
+ 0x02, 0x22, 0x92, 0xB2, 0x0A, 0x2A, 0x9A, 0xBA, 0x02, 0x22, 0x92, 0xB2,
+ 0x0A, 0x2A, 0x9A, 0xBA,
+ 0x42, 0x62, 0xD2, 0xF2, 0x4A, 0x6A, 0xDA, 0xFA, 0x42, 0x62, 0xD2, 0xF2,
+ 0x4A, 0x6A, 0xDA, 0xFA,
+ 0x82, 0xA2, 0x12, 0x32, 0x8A, 0xAA, 0x1A, 0x3A, 0x82, 0xA2, 0x12, 0x32,
+ 0x8A, 0xAA, 0x1A, 0x3A,
+ 0xC2, 0xE2, 0x52, 0x72, 0xCA, 0xEA, 0x5A, 0x7A, 0xC2, 0xE2, 0x52, 0x72,
+ 0xCA, 0xEA, 0x5A, 0x7A,
+ 0x0E, 0x2E, 0x9E, 0xBE, 0x06, 0x26, 0x96, 0xB6, 0x0E, 0x2E, 0x9E, 0xBE,
+ 0x06, 0x26, 0x96, 0xB6,
+ 0x4C, 0x6E, 0xDE, 0xFE, 0x46, 0x66, 0xD6, 0xF6, 0x4C, 0x6E, 0xDE, 0xFE,
+ 0x46, 0x66, 0xD6, 0xF6,
+ 0x8E, 0xAE, 0x1E, 0x3E, 0x86, 0xA6, 0x16, 0x36, 0x8E, 0xAE, 0x1E, 0x3E,
+ 0x86, 0xA6, 0x16, 0x36,
+ 0xCE, 0xEE, 0x60, 0x7E, 0xC6, 0xE6, 0x56, 0x76, 0xCE, 0xEE, 0x60, 0x7E,
+ 0xC6, 0xE6, 0x56, 0x76
+};
+
+static const halftone_pattern_t haltfone_pattern_8x8_Bayer = {
+ 0xF2, 0x42, 0x82, 0xC2, 0xFA, 0x4A, 0x8A, 0xCA, 0xF2, 0x42, 0x82, 0xC2,
+ 0xFA, 0x4A, 0x8A, 0xCA,
+ 0xB2, 0x02, 0x12, 0x52, 0xBA, 0x0A, 0x1A, 0x5A, 0xB2, 0x02, 0x12, 0x52,
+ 0xBA, 0x0A, 0x1A, 0x5A,
+ 0x72, 0x32, 0x22, 0x92, 0x7A, 0x3A, 0x2A, 0x9A, 0x72, 0x32, 0x22, 0x92,
+ 0x7A, 0x3A, 0x2A, 0x9A,
+ 0xE2, 0xA2, 0x62, 0xD2, 0xEA, 0xAA, 0x6A, 0xDA, 0xE2, 0xA2, 0x62, 0xD2,
+ 0xEA, 0xAA, 0x6A, 0xDA,
+ 0xFE, 0x4E, 0x8E, 0xCE, 0xF6, 0x46, 0xD6, 0xC6, 0xFE, 0x4E, 0x8E, 0xCE,
+ 0xF6, 0x46, 0xD6, 0xC6,
+ 0xBE, 0x0E, 0x1E, 0x5E, 0xB6, 0x06, 0x16, 0x56, 0xBE, 0x0E, 0x1E, 0x5E,
+ 0xB6, 0x06, 0x16, 0x56,
+ 0x7E, 0x3E, 0x2E, 0x9E, 0x76, 0x36, 0x26, 0x96, 0x7E, 0x3E, 0x2E, 0x9E,
+ 0x76, 0x36, 0x26, 0x96,
+ 0xEE, 0xAE, 0x6E, 0xDE, 0xE6, 0xA6, 0x66, 0xD6, 0xEE, 0xAE, 0x6E, 0xDE,
+ 0xE6, 0xA6, 0x66, 0xD6,
+ 0xF2, 0x42, 0x82, 0xC2, 0xFA, 0x4A, 0x8A, 0xCA, 0xF2, 0x42, 0x82, 0xC2,
+ 0xFA, 0x4A, 0x8A, 0xCA,
+ 0xB2, 0x02, 0x12, 0x52, 0xBA, 0x0A, 0x1A, 0x5A, 0xB2, 0x02, 0x12, 0x52,
+ 0xBA, 0x0A, 0x1A, 0x5A,
+ 0x72, 0x32, 0x22, 0x92, 0x7A, 0x3A, 0x2A, 0x9A, 0x72, 0x32, 0x22, 0x92,
+ 0x7A, 0x3A, 0x2A, 0x9A,
+ 0xE2, 0xA2, 0x62, 0xD2, 0xEA, 0xAA, 0x6A, 0xDA, 0xE2, 0xA2, 0x62, 0xD2,
+ 0xEA, 0xAA, 0x6A, 0xDA,
+ 0xFE, 0x4E, 0x8E, 0xCE, 0xF6, 0x46, 0xD6, 0xC6, 0xFE, 0x4E, 0x8E, 0xCE,
+ 0xF6, 0x46, 0xD6, 0xC6,
+ 0xBE, 0x0E, 0x1E, 0x5E, 0xB6, 0x06, 0x16, 0x56, 0xBE, 0x0E, 0x1E, 0x5E,
+ 0xB6, 0x06, 0x16, 0x56,
+ 0x7E, 0x3E, 0x2E, 0x9E, 0x76, 0x36, 0x26, 0x96, 0x7E, 0x3E, 0x2E, 0x9E,
+ 0x76, 0x36, 0x26, 0x96,
+ 0xEE, 0xAE, 0x6E, 0xDE, 0xE6, 0xA6, 0x66, 0xD6, 0xEE, 0xAE, 0x6E, 0xDE,
+ 0xE6, 0xA6, 0x66, 0xD6
+};
+
+static const halftone_pattern_t haltfone_pattern_8x8_Vertical_Line = {
+ 0x02, 0x42, 0x82, 0xC4, 0x0A, 0x4C, 0x8A, 0xCA, 0x02, 0x42, 0x82, 0xC4,
+ 0x0A, 0x4C, 0x8A, 0xCA,
+ 0x12, 0x52, 0x92, 0xD2, 0x1A, 0x5A, 0x9A, 0xDA, 0x12, 0x52, 0x92, 0xD2,
+ 0x1A, 0x5A, 0x9A, 0xDA,
+ 0x22, 0x62, 0xA2, 0xE2, 0x2A, 0x6A, 0xAA, 0xEA, 0x22, 0x62, 0xA2, 0xE2,
+ 0x2A, 0x6A, 0xAA, 0xEA,
+ 0x32, 0x72, 0xB2, 0xF2, 0x3A, 0x7A, 0xBA, 0xFA, 0x32, 0x72, 0xB2, 0xF2,
+ 0x3A, 0x7A, 0xBA, 0xFA,
+ 0x0E, 0x4E, 0x8E, 0xCE, 0x06, 0x46, 0x86, 0xC6, 0x0E, 0x4E, 0x8E, 0xCE,
+ 0x06, 0x46, 0x86, 0xC6,
+ 0x1E, 0x5E, 0x9E, 0xDE, 0x16, 0x56, 0x96, 0xD6, 0x1E, 0x5E, 0x9E, 0xDE,
+ 0x16, 0x56, 0x96, 0xD6,
+ 0x2E, 0x6E, 0xAE, 0xEE, 0x26, 0x66, 0xA6, 0xE6, 0x2E, 0x6E, 0xAE, 0xEE,
+ 0x26, 0x66, 0xA6, 0xE6,
+ 0x3E, 0x7E, 0xBE, 0xFE, 0x36, 0x76, 0xB6, 0xF6, 0x3E, 0x7E, 0xBE, 0xFE,
+ 0x36, 0x76, 0xB6, 0xF6,
+ 0x02, 0x42, 0x82, 0xC4, 0x0A, 0x4C, 0x8A, 0xCA, 0x02, 0x42, 0x82, 0xC4,
+ 0x0A, 0x4C, 0x8A, 0xCA,
+ 0x12, 0x52, 0x92, 0xD2, 0x1A, 0x5A, 0x9A, 0xDA, 0x12, 0x52, 0x92, 0xD2,
+ 0x1A, 0x5A, 0x9A, 0xDA,
+ 0x22, 0x62, 0xA2, 0xE2, 0x2A, 0x6A, 0xAA, 0xEA, 0x22, 0x62, 0xA2, 0xE2,
+ 0x2A, 0x6A, 0xAA, 0xEA,
+ 0x32, 0x72, 0xB2, 0xF2, 0x3A, 0x7A, 0xBA, 0xFA, 0x32, 0x72, 0xB2, 0xF2,
+ 0x3A, 0x7A, 0xBA, 0xFA,
+ 0x0E, 0x4E, 0x8E, 0xCE, 0x06, 0x46, 0x86, 0xC6, 0x0E, 0x4E, 0x8E, 0xCE,
+ 0x06, 0x46, 0x86, 0xC6,
+ 0x1E, 0x5E, 0x9E, 0xDE, 0x16, 0x56, 0x96, 0xD6, 0x1E, 0x5E, 0x9E, 0xDE,
+ 0x16, 0x56, 0x96, 0xD6,
+ 0x2E, 0x6E, 0xAE, 0xEE, 0x26, 0x66, 0xA6, 0xE6, 0x2E, 0x6E, 0xAE, 0xEE,
+ 0x26, 0x66, 0xA6, 0xE6,
+ 0x3E, 0x7E, 0xBE, 0xFE, 0x36, 0x76, 0xB6, 0xF6, 0x3E, 0x7E, 0xBE, 0xFE,
+ 0x36, 0x76, 0xB6, 0xF6
+};
diff --git a/backend/lexmark.c b/backend/lexmark.c
new file mode 100644
index 0000000..e0cd21f
--- /dev/null
+++ b/backend/lexmark.c
@@ -0,0 +1,1348 @@
+/* lexmark.c: SANE backend for Lexmark scanners.
+
+ (C) 2003-2004 Lexmark International, Inc. (Original Source code)
+ (C) 2005 Fred Odendaal
+ (C) 2006-2013 Stéphane Voltz <stef.dev@free.fr>
+ (C) 2010 "Torsten Houwaart" <ToHo@gmx.de> X74 support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ **************************************************************************/
+
+#include "lexmark.h"
+
+#define LEXMARK_CONFIG_FILE "lexmark.conf"
+#define BUILD 32
+#define MAX_OPTION_STRING_SIZE 255
+
+static Lexmark_Device *first_lexmark_device = 0;
+static SANE_Int num_lexmark_device = 0;
+static const SANE_Device **sane_device_list = NULL;
+
+/* Program globals F.O - Should this be per device?*/
+static SANE_Bool initialized = SANE_FALSE;
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_LINEART,
+ NULL
+};
+
+/* possible resolutions are: 75x75, 150x150, 300x300, 600x600, 600x1200 */
+
+static SANE_Int x1100_dpi_list[] = {
+ 5, 75, 150, 300, 600, 1200
+};
+
+static SANE_Int a920_dpi_list[] = {
+ 4, 75, 150, 300, 600
+};
+
+static SANE_Int x1200_dpi_list[] = {
+ 4, 75, 150, 300, 600
+};
+
+static SANE_Int x74_dpi_list[] = {
+ 75, 150, 300, 600
+};
+
+static SANE_Range threshold_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (100.0), /* maximum */
+ SANE_FIX (1.0) /* quantization */
+};
+
+static const SANE_Range gain_range = {
+ 0, /* minimum */
+ 31, /* maximum */
+ 0 /* quantization */
+};
+
+/* for now known models (2 ...) have the same scan window geometry.
+ coordinates are expressed in pixels, with a quantization factor of
+ 8 to have 'even' coordinates at 75 dpi */
+static SANE_Range x_range = {
+ 0, /* minimum */
+ 5104, /* maximum */
+ 16 /* quantization : 16 is required so we
+ never have an odd width */
+};
+
+static SANE_Range y_range = {
+ 0, /* minimum */
+ 6848, /* maximum */
+ /* 7032, for X74 */
+ 8 /* quantization */
+};
+
+/* static functions */
+static SANE_Status init_options (Lexmark_Device * lexmark_device);
+static SANE_Status attachLexmark (SANE_String_Const devname);
+
+SANE_Status
+init_options (Lexmark_Device * dev)
+{
+
+ SANE_Option_Descriptor *od;
+
+ DBG (2, "init_options: dev = %p\n", (void *) dev);
+
+ /* number of options */
+ od = &(dev->opt[OPT_NUM_OPTS]);
+ od->name = SANE_NAME_NUM_OPTIONS;
+ od->title = SANE_TITLE_NUM_OPTIONS;
+ od->desc = SANE_DESC_NUM_OPTIONS;
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* mode - sets the scan mode: Color, Gray, or Line Art */
+ od = &(dev->opt[OPT_MODE]);
+ od->name = SANE_NAME_SCAN_MODE;
+ od->title = SANE_TITLE_SCAN_MODE;
+ od->desc = SANE_DESC_SCAN_MODE;;
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = MAX_OPTION_STRING_SIZE;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = mode_list;
+ dev->val[OPT_MODE].s = malloc (od->size);
+ if (!dev->val[OPT_MODE].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR);
+
+ /* resolution */
+ od = &(dev->opt[OPT_RESOLUTION]);
+ od->name = SANE_NAME_SCAN_RESOLUTION;
+ od->title = SANE_TITLE_SCAN_RESOLUTION;
+ od->desc = SANE_DESC_SCAN_RESOLUTION;
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_DPI;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ switch (dev->model.sensor_type)
+ {
+ case X1100_2C_SENSOR:
+ case A920_SENSOR:
+ od->constraint.word_list = a920_dpi_list;
+ break;
+ case X1100_B2_SENSOR:
+ od->constraint.word_list = x1100_dpi_list;
+ break;
+ case X1200_SENSOR:
+ case X1200_USB2_SENSOR:
+ od->constraint.word_list = x1200_dpi_list;
+ break;
+ case X74_SENSOR:
+ od->constraint.word_list = x74_dpi_list;
+ break;
+ }
+ dev->val[OPT_RESOLUTION].w = 75;
+
+ /* preview mode */
+ od = &(dev->opt[OPT_PREVIEW]);
+ od->name = SANE_NAME_PREVIEW;
+ od->title = SANE_TITLE_PREVIEW;
+ od->desc = SANE_DESC_PREVIEW;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->type = SANE_TYPE_BOOL;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ od = &(dev->opt[OPT_GEOMETRY_GROUP]);
+ od->name = "";
+ od->title = SANE_I18N ("Geometry");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->size = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ od = &(dev->opt[OPT_TL_X]);
+ od->name = SANE_NAME_SCAN_TL_X;
+ od->title = SANE_TITLE_SCAN_TL_X;
+ od->desc = SANE_DESC_SCAN_TL_X;
+ od->type = SANE_TYPE_INT;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->size = sizeof (SANE_Word);
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &x_range;
+ dev->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ od = &(dev->opt[OPT_TL_Y]);
+ od->name = SANE_NAME_SCAN_TL_Y;
+ od->title = SANE_TITLE_SCAN_TL_Y;
+ od->desc = SANE_DESC_SCAN_TL_Y;
+ od->type = SANE_TYPE_INT;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->size = sizeof (SANE_Word);
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &y_range;
+ dev->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ od = &(dev->opt[OPT_BR_X]);
+ od->name = SANE_NAME_SCAN_BR_X;
+ od->title = SANE_TITLE_SCAN_BR_X;
+ od->desc = SANE_DESC_SCAN_BR_X;
+ od->type = SANE_TYPE_INT;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &x_range;
+ dev->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ od = &(dev->opt[OPT_BR_Y]);
+ od->name = SANE_NAME_SCAN_BR_Y;
+ od->title = SANE_TITLE_SCAN_BR_Y;
+ od->desc = SANE_DESC_SCAN_BR_Y;
+ od->type = SANE_TYPE_INT;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &y_range;
+ dev->val[OPT_BR_Y].w = y_range.max;
+
+ /* threshold */
+ od = &(dev->opt[OPT_THRESHOLD]);
+ od->name = SANE_NAME_THRESHOLD;
+ od->title = SANE_TITLE_THRESHOLD;
+ od->desc = SANE_DESC_THRESHOLD;
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_PERCENT;
+ od->size = sizeof (SANE_Fixed);
+ od->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &threshold_range;
+ dev->val[OPT_THRESHOLD].w = SANE_FIX (50.0);
+
+ /* gain group */
+ dev->opt[OPT_MANUAL_GAIN].name = "manual-channel-gain";
+ dev->opt[OPT_MANUAL_GAIN].title = SANE_I18N ("Gain");
+ dev->opt[OPT_MANUAL_GAIN].desc = SANE_I18N ("Color channels gain settings");
+ dev->opt[OPT_MANUAL_GAIN].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_MANUAL_GAIN].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ dev->opt[OPT_MANUAL_GAIN].size = sizeof (SANE_Bool);
+ dev->val[OPT_MANUAL_GAIN].w = SANE_FALSE;
+
+ /* gray gain */
+ dev->opt[OPT_GRAY_GAIN].name = "gray-gain";
+ dev->opt[OPT_GRAY_GAIN].title = SANE_I18N ("Gray gain");
+ dev->opt[OPT_GRAY_GAIN].desc = SANE_I18N ("Sets gray channel gain");
+ dev->opt[OPT_GRAY_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_GRAY_GAIN].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ dev->opt[OPT_GRAY_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GRAY_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_GRAY_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GRAY_GAIN].constraint.range = &gain_range;
+ dev->val[OPT_GRAY_GAIN].w = 10;
+
+ /* red gain */
+ dev->opt[OPT_RED_GAIN].name = "red-gain";
+ dev->opt[OPT_RED_GAIN].title = SANE_I18N ("Red gain");
+ dev->opt[OPT_RED_GAIN].desc = SANE_I18N ("Sets red channel gain");
+ dev->opt[OPT_RED_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_RED_GAIN].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ dev->opt[OPT_RED_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_RED_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_RED_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RED_GAIN].constraint.range = &gain_range;
+ dev->val[OPT_RED_GAIN].w = 10;
+
+ /* green gain */
+ dev->opt[OPT_GREEN_GAIN].name = "green-gain";
+ dev->opt[OPT_GREEN_GAIN].title = SANE_I18N ("Green gain");
+ dev->opt[OPT_GREEN_GAIN].desc = SANE_I18N ("Sets green channel gain");
+ dev->opt[OPT_GREEN_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_GREEN_GAIN].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ dev->opt[OPT_GREEN_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GREEN_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_GREEN_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GREEN_GAIN].constraint.range = &gain_range;
+ dev->val[OPT_GREEN_GAIN].w = 10;
+
+ /* blue gain */
+ dev->opt[OPT_BLUE_GAIN].name = "blue-gain";
+ dev->opt[OPT_BLUE_GAIN].title = SANE_I18N ("Blue gain");
+ dev->opt[OPT_BLUE_GAIN].desc = SANE_I18N ("Sets blue channel gain");
+ dev->opt[OPT_BLUE_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_BLUE_GAIN].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE |
+ SANE_CAP_ADVANCED;
+ dev->opt[OPT_BLUE_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BLUE_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_BLUE_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BLUE_GAIN].constraint.range = &gain_range;
+ dev->val[OPT_BLUE_GAIN].w = 10;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/***************************** SANE API ****************************/
+
+SANE_Status
+attachLexmark (SANE_String_Const devname)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Int dn, vendor, product, variant;
+ SANE_Status status;
+
+ DBG (2, "attachLexmark: devname=%s\n", devname);
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ /* already attached devices */
+ if (strcmp (lexmark_device->sane.name, devname) == 0)
+ {
+ lexmark_device->missing = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ lexmark_device = (Lexmark_Device *) malloc (sizeof (Lexmark_Device));
+ if (lexmark_device == NULL)
+ return SANE_STATUS_NO_MEM;
+
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_open (devname, &dn);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attachLexmark: couldn't open device `%s': %s\n", devname,
+ sane_strstatus (status));
+ return status;
+ }
+ else
+ DBG (2, "attachLexmark: device `%s' successfully opened\n", devname);
+
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+ /* put the id of the model you want to fake here */
+ vendor = 0x043d;
+ product = 0x007c; /* X11xx */
+ variant = 0xb2;
+#else
+ variant = 0;
+ status = sanei_usb_get_vendor_product (dn, &vendor, &product);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "attachLexmark: couldn't get vendor and product ids of device `%s': %s\n",
+ devname, sane_strstatus (status));
+#ifndef FAKE_USB
+ sanei_usb_close (dn);
+#endif
+ return status;
+ }
+#ifndef FAKE_USB
+ sanei_usb_close (dn);
+#endif
+
+ DBG (2, "attachLexmark: testing device `%s': 0x%04x:0x%04x, variant=%d\n",
+ devname, vendor, product, variant);
+ if (sanei_lexmark_low_assign_model (lexmark_device,
+ devname,
+ vendor,
+ product, variant) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "attachLexmark: unsupported device `%s': 0x%04x:0x%04x\n",
+ devname, vendor, product);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* add new device to device list */
+
+ /* there are two variant of the scanner with the same USB id,
+ * so we need to read registers from scanner to detect which one
+ * is really connected */
+ status = sanei_lexmark_low_open_device (lexmark_device);
+ sanei_usb_close (lexmark_device->devnum);
+
+ /* set up scanner start status */
+ sanei_lexmark_low_init (lexmark_device);
+
+ /* Set the default resolution here */
+ lexmark_device->x_dpi = 75;
+ lexmark_device->y_dpi = 75;
+
+ /* Make the pointer to the read buffer null here */
+ lexmark_device->read_buffer = NULL;
+
+ /* Set the default threshold for lineart mode here */
+ lexmark_device->threshold = 0x80;
+
+ lexmark_device->shading_coeff = NULL;
+
+ /* mark device as present */
+ lexmark_device->missing = SANE_FALSE;
+
+ /* insert it a the start of the chained list */
+ lexmark_device->next = first_lexmark_device;
+ first_lexmark_device = lexmark_device;
+
+ num_lexmark_device++;
+
+ return status;
+}
+
+/** probe for supported lexmark devices
+ * This function scan usb and try to attached to scanner
+ * configured in lexmark.conf .
+ */
+static SANE_Status
+probe_lexmark_devices (void)
+{
+ FILE *fp;
+ SANE_Char line[PATH_MAX];
+ const char *lp;
+ SANE_Int vendor, product;
+ size_t len;
+ Lexmark_Device *dev;
+
+ /* mark already detected devices as missing, during device probe
+ * detected devices will clear this flag */
+ dev = first_lexmark_device;
+ while (dev != NULL)
+ {
+ dev->missing = SANE_TRUE;
+ dev = dev->next;
+ }
+
+ /* open config file, parse option and try to open
+ * any device configure in it */
+ fp = sanei_config_open (LEXMARK_CONFIG_FILE);
+ if (!fp)
+ {
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ while (sanei_config_read (line, PATH_MAX, fp))
+ {
+ /* ignore comments */
+ if (line[0] == '#')
+ continue;
+ len = strlen (line);
+
+ /* delete newline characters at end */
+ if (line[len - 1] == '\n')
+ line[--len] = '\0';
+
+ lp = sanei_config_skip_whitespace (line);
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if (sscanf (lp, "usb %i %i", &vendor, &product) == 2)
+ ;
+ else if (strncmp ("libusb", lp, 6) == 0)
+ ;
+ else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3]))
+ {
+ lp += 3;
+ lp = sanei_config_skip_whitespace (lp);
+ }
+ else
+ continue;
+
+#ifdef FAKE_USB
+ attachLexmark ("FAKE_USB");
+#else
+ sanei_usb_attach_matching_devices (lp, attachLexmark);
+#endif
+ }
+
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (1, "SANE Lexmark backend version %d.%d.%d-devel\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+
+ DBG (2, "sane_init: version_code=%p\n", (void *) version_code);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+#ifndef FAKE_USB
+ sanei_usb_init ();
+#endif
+
+ status = probe_lexmark_devices ();
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ initialized = SANE_TRUE;
+ }
+ else
+ {
+ initialized = SANE_FALSE;
+ }
+
+ return status;
+}
+
+void
+sane_exit (void)
+{
+ Lexmark_Device *lexmark_device, *next_lexmark_device;
+
+ DBG (2, "sane_exit\n");
+
+ if (!initialized)
+ return;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = next_lexmark_device)
+ {
+ next_lexmark_device = lexmark_device->next;
+ sanei_lexmark_low_destroy (lexmark_device);
+ free (lexmark_device);
+ }
+
+ if (sane_device_list)
+ free (sane_device_list);
+
+ sanei_usb_exit();
+ initialized = SANE_FALSE;
+
+ return;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Int index;
+
+ DBG (2, "sane_get_devices: device_list=%p, local_only=%d\n",
+ (void *) device_list, local_only);
+
+ /* hot-plug case : detection of newly connected scanners */
+ sanei_usb_scan_devices ();
+ probe_lexmark_devices ();
+
+ if (sane_device_list)
+ free (sane_device_list);
+
+ sane_device_list = malloc ((num_lexmark_device + 1) *
+ sizeof (sane_device_list[0]));
+
+ if (!sane_device_list)
+ return SANE_STATUS_NO_MEM;
+
+ index = 0;
+ lexmark_device = first_lexmark_device;
+ while (lexmark_device != NULL)
+ {
+ if (lexmark_device->missing == SANE_FALSE)
+ {
+ sane_device_list[index] = &(lexmark_device->sane);
+ index++;
+ }
+ lexmark_device = lexmark_device->next;
+ }
+ sane_device_list[index] = 0;
+
+ *device_list = sane_device_list;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Open the backend, ie return the struct handle of a detected scanner
+ * The struct returned is choosne if it matches the name given, which is
+ * usefull when several scanners handled by the backend have been detected.
+ * However, special case empty string "" and "lexmark" pick the first
+ * available handle.
+ */
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Status status;
+
+ DBG (2, "sane_open: devicename=\"%s\", handle=%p\n", devicename,
+ (void *) handle);
+
+ if (!initialized)
+ {
+ DBG (2, "sane_open: not initialized\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!handle)
+ {
+ DBG (2, "sane_open: no handle\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* walk the linked list of scanner device until ther is a match
+ * with the device name */
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ DBG (2, "sane_open: devname from list: %s\n",
+ lexmark_device->sane.name);
+ if (strcmp (devicename, "") == 0
+ || strcmp (devicename, "lexmark") == 0
+ || strcmp (devicename, lexmark_device->sane.name) == 0)
+ break;
+ }
+
+ *handle = lexmark_device;
+
+ if (!lexmark_device)
+ {
+ DBG (2, "sane_open: Not a lexmark device\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = init_options (lexmark_device);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = sanei_lexmark_low_open_device (lexmark_device);
+ DBG (2, "sane_open: end.\n");
+
+ return status;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Lexmark_Device *lexmark_device;
+
+ DBG (2, "sane_close: handle=%p\n", (void *) handle);
+
+ if (!initialized)
+ return;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (!lexmark_device)
+ return;
+
+ sanei_lexmark_low_close_device (lexmark_device);
+
+ return;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Lexmark_Device *lexmark_device;
+
+ DBG (2, "sane_get_option_descriptor: handle=%p, option = %d\n",
+ (void *) handle, option);
+
+ if (!initialized)
+ return NULL;
+
+ /* Check for valid option number */
+ if ((option < 0) || (option >= NUM_OPTIONS))
+ return NULL;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (!lexmark_device)
+ return NULL;
+
+ if (lexmark_device->opt[option].name)
+ {
+ DBG (2, "sane_get_option_descriptor: name=%s\n",
+ lexmark_device->opt[option].name);
+ }
+
+ return &(lexmark_device->opt[option]);
+}
+
+/* rebuilds parameters if needed, called each time SANE_INFO_RELOAD_OPTIONS
+ is set */
+static void
+calc_parameters (Lexmark_Device * lexmark_device)
+{
+ if (strcmp (lexmark_device->val[OPT_MODE].s,
+ SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ lexmark_device->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ lexmark_device->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* changing color mode implies changing gain setting */
+ if (lexmark_device->val[OPT_MANUAL_GAIN].w == SANE_TRUE)
+ {
+ if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)
+ != 0)
+ {
+ lexmark_device->opt[OPT_GRAY_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ lexmark_device->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_RED_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_GREEN_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_BLUE_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ lexmark_device->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE;
+ lexmark_device->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE;
+ }
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *value, SANE_Int * info)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Status status;
+ SANE_Word w;
+
+ DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ (void *) handle, option, action, (void *) value, (void *) info);
+
+ if (!initialized)
+ return SANE_STATUS_INVAL;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (!lexmark_device)
+ return SANE_STATUS_INVAL;
+
+ if (value == NULL)
+ return SANE_STATUS_INVAL;
+
+ if (info != NULL)
+ *info = 0;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ if (lexmark_device->opt[option].type == SANE_TYPE_GROUP)
+ return SANE_STATUS_INVAL;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+
+ if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap))
+ return SANE_STATUS_INVAL;
+ if (!(lexmark_device->opt[option].cap & SANE_CAP_AUTOMATIC))
+ return SANE_STATUS_INVAL;
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+
+ if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap))
+ return SANE_STATUS_INVAL;
+
+ /* Make sure boolean values are only TRUE or FALSE */
+ if (lexmark_device->opt[option].type == SANE_TYPE_BOOL)
+ {
+ if (!
+ ((*(SANE_Bool *) value == SANE_FALSE)
+ || (*(SANE_Bool *) value == SANE_TRUE)))
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Check range constraints */
+ if (lexmark_device->opt[option].constraint_type ==
+ SANE_CONSTRAINT_RANGE)
+ {
+ status =
+ sanei_constrain_value (&(lexmark_device->opt[option]), value,
+ info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "SANE_CONTROL_OPTION: Bad value for range\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ switch (option)
+ {
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ lexmark_device->val[option].w = *(SANE_Int *) value;
+ sane_get_parameters (handle, 0);
+ break;
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ DBG (2, "Option value set to %d (%s)\n", *(SANE_Word *) value,
+ lexmark_device->opt[option].name);
+ lexmark_device->val[option].w = *(SANE_Word *) value;
+ if (lexmark_device->val[OPT_TL_X].w >
+ lexmark_device->val[OPT_BR_X].w)
+ {
+ w = lexmark_device->val[OPT_TL_X].w;
+ lexmark_device->val[OPT_TL_X].w =
+ lexmark_device->val[OPT_BR_X].w;
+ lexmark_device->val[OPT_BR_X].w = w;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ if (lexmark_device->val[OPT_TL_Y].w >
+ lexmark_device->val[OPT_BR_Y].w)
+ {
+ w = lexmark_device->val[OPT_TL_Y].w;
+ lexmark_device->val[OPT_TL_Y].w =
+ lexmark_device->val[OPT_BR_Y].w;
+ lexmark_device->val[OPT_BR_Y].w = w;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+ case OPT_THRESHOLD:
+ lexmark_device->val[option].w = *(SANE_Fixed *) value;
+ lexmark_device->threshold =
+ (0xFF * lexmark_device->val[option].w) / 100;
+ break;
+ case OPT_PREVIEW:
+ lexmark_device->val[option].w = *(SANE_Int *) value;
+ if (*(SANE_Word *) value)
+ {
+ lexmark_device->y_dpi = lexmark_device->val[OPT_RESOLUTION].w;
+ lexmark_device->val[OPT_RESOLUTION].w = 75;
+ }
+ else
+ {
+ lexmark_device->val[OPT_RESOLUTION].w = lexmark_device->y_dpi;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ sane_get_parameters (handle, 0);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_GRAY_GAIN:
+ case OPT_GREEN_GAIN:
+ case OPT_RED_GAIN:
+ case OPT_BLUE_GAIN:
+ lexmark_device->val[option].w = *(SANE_Word *) value;
+ return SANE_STATUS_GOOD;
+ break;
+ case OPT_MODE:
+ strcpy (lexmark_device->val[option].s, value);
+ calc_parameters (lexmark_device);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+ case OPT_MANUAL_GAIN:
+ w = *(SANE_Word *) value;
+
+ if (w == lexmark_device->val[OPT_MANUAL_GAIN].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ lexmark_device->val[OPT_MANUAL_GAIN].w = w;
+ calc_parameters (lexmark_device);
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (info != NULL)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ break;
+
+ case SANE_ACTION_GET_VALUE:
+
+ switch (option)
+ {
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_MANUAL_GAIN:
+ case OPT_GRAY_GAIN:
+ case OPT_GREEN_GAIN:
+ case OPT_RED_GAIN:
+ case OPT_BLUE_GAIN:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *) value = lexmark_device->val[option].w;
+ DBG (2, "Option value = %d (%s)\n", *(SANE_Word *) value,
+ lexmark_device->opt[option].name);
+ break;
+ case OPT_THRESHOLD:
+ *(SANE_Fixed *) value = lexmark_device->val[option].w;
+ DBG (2, "Option value = %f\n", SANE_UNFIX (*(SANE_Fixed *) value));
+ break;
+ case OPT_MODE:
+ strcpy (value, lexmark_device->val[option].s);
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Parameters *device_params;
+ SANE_Int xres, yres, width_px, height_px;
+ SANE_Int channels, bitsperchannel;
+
+ DBG (2, "sane_get_parameters: handle=%p, params=%p\n", (void *) handle,
+ (void *) params);
+
+ if (!initialized)
+ return SANE_STATUS_INVAL;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (!lexmark_device)
+ return SANE_STATUS_INVAL;
+
+ yres = lexmark_device->val[OPT_RESOLUTION].w;
+ if (yres == 1200)
+ xres = 600;
+ else
+ xres = yres;
+
+ /* 24 bit colour = 8 bits/channel for each of the RGB channels */
+ channels = 3;
+ bitsperchannel = 8;
+
+ /* If not color there is only 1 channel */
+ if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)
+ != 0)
+ {
+ channels = 1;
+ bitsperchannel = 8;
+ }
+
+ /* geometry in pixels */
+ width_px =
+ lexmark_device->val[OPT_BR_X].w - lexmark_device->val[OPT_TL_X].w;
+ height_px =
+ lexmark_device->val[OPT_BR_Y].w - lexmark_device->val[OPT_TL_Y].w;
+ DBG (7, "sane_get_parameters: tl=(%d,%d) br=(%d,%d)\n",
+ lexmark_device->val[OPT_TL_X].w, lexmark_device->val[OPT_TL_Y].w,
+ lexmark_device->val[OPT_BR_X].w, lexmark_device->val[OPT_BR_Y].w);
+
+
+ /* we must tell the front end the bitsperchannel for lineart is really */
+ /* only 1, so it can calculate the correct image size */
+ /* If not color there is only 1 channel */
+ if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)
+ == 0)
+ {
+ bitsperchannel = 1;
+ }
+
+ device_params = &(lexmark_device->params);
+ device_params->format = SANE_FRAME_RGB;
+ if (channels == 1)
+ device_params->format = SANE_FRAME_GRAY;
+ device_params->last_frame = SANE_TRUE;
+ device_params->lines = (height_px * yres) / 600;
+ device_params->depth = bitsperchannel;
+ device_params->pixels_per_line = (width_px * xres) / 600;
+ /* we always read an even number of sensor pixels */
+ if (device_params->pixels_per_line & 1)
+ device_params->pixels_per_line++;
+
+ /* data_size is the size transferred from the scanner to the backend */
+ /* therefore bitsperchannel is the same for gray and lineart */
+ /* note: bytes_per_line has been divided by 8 in lineart mode */
+ lexmark_device->data_size =
+ channels * device_params->pixels_per_line * device_params->lines;
+
+ if (bitsperchannel == 1)
+ {
+ device_params->bytes_per_line =
+ (SANE_Int) ((7 + device_params->pixels_per_line) / 8);
+ }
+ else
+ {
+ device_params->bytes_per_line =
+ (SANE_Int) (channels * device_params->pixels_per_line);
+ }
+ DBG (2, "sane_get_parameters: Data size determined as %ld\n",
+ lexmark_device->data_size);
+
+ DBG (2, "sane_get_parameters: \n");
+ if (device_params->format == SANE_FRAME_GRAY)
+ DBG (2, " format: SANE_FRAME_GRAY\n");
+ else if (device_params->format == SANE_FRAME_RGB)
+ DBG (2, " format: SANE_FRAME_RGB\n");
+ else
+ DBG (2, " format: UNKNOWN\n");
+ if (device_params->last_frame == SANE_TRUE)
+ DBG (2, " last_frame: TRUE\n");
+ else
+ DBG (2, " last_frame: FALSE\n");
+ DBG (2, " lines %d\n", device_params->lines);
+ DBG (2, " depth %d\n", device_params->depth);
+ DBG (2, " pixels_per_line %d\n", device_params->pixels_per_line);
+ DBG (2, " bytes_per_line %d\n", device_params->bytes_per_line);
+
+ if (params != 0)
+ {
+ params->format = device_params->format;
+ params->last_frame = device_params->last_frame;
+ params->lines = device_params->lines;
+ params->depth = device_params->depth;
+ params->pixels_per_line = device_params->pixels_per_line;
+ params->bytes_per_line = device_params->bytes_per_line;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Int offset;
+ SANE_Status status;
+ int resolution;
+
+ DBG (2, "sane_start: handle=%p\n", (void *) handle);
+
+ if (!initialized)
+ return SANE_STATUS_INVAL;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ sane_get_parameters (handle, 0);
+
+ if ((lexmark_device->params.lines == 0) ||
+ (lexmark_device->params.pixels_per_line == 0) ||
+ (lexmark_device->params.bytes_per_line == 0))
+ {
+ DBG (2, "sane_start: \n");
+ DBG (2, " ERROR: Zero size encountered in:\n");
+ DBG (2,
+ " number of lines, bytes per line, or pixels per line\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ lexmark_device->device_cancelled = SANE_FALSE;
+ lexmark_device->data_ctr = 0;
+ lexmark_device->eof = SANE_FALSE;
+
+
+ /* Need this cancel_ctr to determine how many times sane_cancel is called
+ since it is called more than once. */
+ lexmark_device->cancel_ctr = 0;
+
+ /* Find Home */
+ if (sanei_lexmark_low_search_home_fwd (lexmark_device))
+ {
+ DBG (2, "sane_start: Scan head initially at home position\n");
+ }
+ else
+ {
+ /* We may have been rewound too far, so move forward the distance from
+ the edge to the home position */
+ sanei_lexmark_low_move_fwd (0x01a8, lexmark_device,
+ lexmark_device->shadow_regs);
+
+ /* Scan backwards until we find home */
+ sanei_lexmark_low_search_home_bwd (lexmark_device);
+ }
+ /* do calibration before offset detection , use sensor max dpi, not motor's one */
+ resolution = lexmark_device->val[OPT_RESOLUTION].w;
+ if (resolution > 600)
+ {
+ resolution = 600;
+ }
+
+
+ sanei_lexmark_low_set_scan_regs (lexmark_device, resolution, 0, SANE_FALSE);
+ status = sanei_lexmark_low_calibration (lexmark_device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: calibration failed : %s ! \n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* At this point we're somewhere in the dot. We need to read a number of
+ lines greater than the diameter of the dot and determine how many lines
+ past the dot we've gone. We then use this information to see how far the
+ scan head must move before starting the scan. */
+ /* offset is in 600 dpi unit */
+ offset = sanei_lexmark_low_find_start_line (lexmark_device);
+ DBG (7, "start line offset=%d\n", offset);
+
+ /* Set the shadow registers for scan with the options (resolution, mode,
+ size) set in the front end. Pass the offset so we can get the vert.
+ start. */
+ sanei_lexmark_low_set_scan_regs (lexmark_device,
+ lexmark_device->val[OPT_RESOLUTION].w,
+ offset, SANE_TRUE);
+
+ if (sanei_lexmark_low_start_scan (lexmark_device) == SANE_STATUS_GOOD)
+ {
+ DBG (2, "sane_start: scan started\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ lexmark_device->device_cancelled = SANE_TRUE;
+ return SANE_STATUS_INVAL;
+ }
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Lexmark_Device *lexmark_device;
+ long bytes_read;
+
+ DBG (2, "sane_read: handle=%p, data=%p, max_length = %d, length=%p\n",
+ (void *) handle, (void *) data, max_length, (void *) length);
+
+ if (!initialized)
+ {
+ DBG (2, "sane_read: Not initialized\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (lexmark_device->device_cancelled)
+ {
+ DBG (2, "sane_read: Device was cancelled\n");
+ /* We don't know how far we've gone, so search for home. */
+ sanei_lexmark_low_search_home_bwd (lexmark_device);
+ return SANE_STATUS_EOF;
+ }
+
+ if (!length)
+ {
+ DBG (2, "sane_read: NULL length pointer\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *length = 0;
+
+ if (lexmark_device->eof)
+ {
+ DBG (2, "sane_read: Trying to read past EOF\n");
+ return SANE_STATUS_EOF;
+ }
+
+ if (!data)
+ return SANE_STATUS_INVAL;
+
+ bytes_read = sanei_lexmark_low_read_scan_data (data, max_length,
+ lexmark_device);
+ if (bytes_read < 0)
+ return SANE_STATUS_IO_ERROR;
+ else if (bytes_read == 0)
+ return SANE_STATUS_EOF;
+ else
+ {
+ *length = bytes_read;
+ lexmark_device->data_ctr += bytes_read;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Lexmark_Device *lexmark_device;
+/* ssize_t bytes_read; */
+ DBG (2, "sane_cancel: handle = %p\n", (void *) handle);
+
+ if (!initialized)
+ return;
+
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ /*If sane_cancel called more than once, return */
+ if (++lexmark_device->cancel_ctr > 1)
+ return;
+
+ /* Set the device flag so the next call to sane_read() can stop the scan. */
+ lexmark_device->device_cancelled = SANE_TRUE;
+
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Lexmark_Device *lexmark_device;
+
+ DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n",
+ (void *) handle, non_blocking);
+
+ if (!initialized)
+ return SANE_STATUS_INVAL;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Lexmark_Device *lexmark_device;
+
+ DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", (void *) handle,
+ fd ? "!=" : "=");
+
+ if (!initialized)
+ return SANE_STATUS_INVAL;
+
+ for (lexmark_device = first_lexmark_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/***************************** END OF SANE API ****************************/
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/lexmark.conf.in b/backend/lexmark.conf.in
new file mode 100644
index 0000000..c0bf868
--- /dev/null
+++ b/backend/lexmark.conf.in
@@ -0,0 +1,8 @@
+# X11xx series
+usb 0x043d 0x007c
+# X12xx series
+usb 0x043d 0x007d
+# Dell A920
+usb 0x413c 0x5105
+# X74
+usb 0x43d 0x0060
diff --git a/backend/lexmark.h b/backend/lexmark.h
new file mode 100644
index 0000000..0790551
--- /dev/null
+++ b/backend/lexmark.h
@@ -0,0 +1,281 @@
+/************************************************************************
+ lexmark.h - SANE library for Lexmark scanners.
+ Copyright (C) 2003-2004 Lexmark International, Inc. (original source)
+ Copyright (C) 2005 Fred Odendaal
+ Copyright (C) 2006-2010 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2010 "Torsten Houwaart" <ToHo@gmx.de> X74 support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ **************************************************************************/
+#ifndef LEXMARK_H
+#define LEXMARK_H
+
+#undef DEEP_DEBUG
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "../include/_stdint.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_backend.h"
+
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_THRESHOLD,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ /* manual gain handling */
+ OPT_MANUAL_GAIN, /* 6 */
+ OPT_GRAY_GAIN,
+ OPT_RED_GAIN,
+ OPT_GREEN_GAIN,
+ OPT_BLUE_GAIN,
+
+ /* must come last: */
+ NUM_OPTIONS
+}
+Lexmark_Options;
+
+/*
+ * this struct is used to described the specific parts of each model
+ */
+typedef struct Lexmark_Model
+{
+ SANE_Int vendor_id;
+ SANE_Int product_id;
+ SANE_Byte mainboard_id; /* matched against the content of reg B0 */
+ SANE_String_Const name;
+ SANE_String_Const vendor;
+ SANE_String_Const model;
+ SANE_Int motor_type;
+ SANE_Int sensor_type;
+ SANE_Int HomeEdgePoint1;
+ SANE_Int HomeEdgePoint2;
+} Lexmark_Model;
+
+/*
+ * this struct is used to store per sensor model constants
+ */
+typedef struct Lexmark_Sensor
+{
+ SANE_Int id;
+ SANE_Int offset_startx; /* starting x for offset calibration */
+ SANE_Int offset_endx; /* end x for offset calibration */
+ SANE_Int offset_threshold; /* target threshold for offset calibration */
+ SANE_Int xoffset; /* number of unusable pixels on the start of the sensor */
+ SANE_Int default_gain; /* value of the default gain for a scan */
+ SANE_Int red_gain_target;
+ SANE_Int green_gain_target;
+ SANE_Int blue_gain_target;
+ SANE_Int gray_gain_target;
+ SANE_Int red_shading_target;
+ SANE_Int green_shading_target;
+ SANE_Int blue_shading_target;
+ SANE_Int gray_shading_target;
+ SANE_Int offset_fallback; /* offset to use in case offset calibration fails */
+ SANE_Int gain_fallback; /* gain to use in case offset calibration fails */
+} Lexmark_Sensor;
+
+typedef enum
+{
+ RED = 0,
+ GREEN,
+ BLUE
+}
+Scan_Regions;
+
+/* struct to hold pre channel settings */
+typedef struct Channels
+{
+ SANE_Word red;
+ SANE_Word green;
+ SANE_Word blue;
+ SANE_Word gray;
+}
+Channels;
+
+/** @name Option_Value union
+ * convenience union to access option values given to the backend
+ * @{
+ */
+#ifndef SANE_OPTION
+#define SANE_OPTION 1
+typedef union
+{
+ SANE_Bool b;
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+}
+Option_Value;
+#endif
+/* @} */
+
+typedef struct Read_Buffer
+{
+ SANE_Int gray_offset;
+ SANE_Int max_gray_offset;
+ SANE_Int region;
+ SANE_Int red_offset;
+ SANE_Int green_offset;
+ SANE_Int blue_offset;
+ SANE_Int max_red_offset;
+ SANE_Int max_green_offset;
+ SANE_Int max_blue_offset;
+ SANE_Byte *data;
+ SANE_Byte *readptr;
+ SANE_Byte *writeptr;
+ SANE_Byte *max_writeptr;
+ size_t size;
+ size_t linesize;
+ SANE_Bool empty;
+ SANE_Int image_line_no;
+ SANE_Int bit_counter;
+ SANE_Int max_lineart_offset;
+}
+Read_Buffer;
+
+typedef struct Lexmark_Device
+{
+ struct Lexmark_Device *next;
+ SANE_Bool missing; /**< devices has been unplugged or swtiched off */
+
+ SANE_Device sane;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+ SANE_Int devnum;
+ long data_size;
+ SANE_Bool initialized;
+ SANE_Bool eof;
+ SANE_Int x_dpi;
+ SANE_Int y_dpi;
+ long data_ctr;
+ SANE_Bool device_cancelled;
+ SANE_Int cancel_ctr;
+ SANE_Byte *transfer_buffer;
+ size_t bytes_read;
+ size_t bytes_remaining;
+ size_t bytes_in_buffer;
+ SANE_Byte *read_pointer;
+ Read_Buffer *read_buffer;
+ SANE_Byte threshold;
+
+ Lexmark_Model model; /* per model data */
+ Lexmark_Sensor *sensor;
+ SANE_Byte shadow_regs[255]; /* shadow registers */
+ struct Channels offset;
+ struct Channels gain;
+ float *shading_coeff;
+}
+Lexmark_Device;
+
+/* Maximum transfer size */
+#define MAX_XFER_SIZE 0xFFC0
+
+/* motors and sensors type defines */
+#define X1100_MOTOR 1
+#define A920_MOTOR 2
+#define X74_MOTOR 3
+
+#define X1100_B2_SENSOR 4
+#define A920_SENSOR 5
+#define X1100_2C_SENSOR 6
+#define X1200_SENSOR 7 /* X1200 on USB 1.0 */
+#define X1200_USB2_SENSOR 8 /* X1200 on USB 2.0 */
+#define X74_SENSOR 9
+
+/* Non-static Function Proto-types (called by lexmark.c) */
+SANE_Status sanei_lexmark_low_init (Lexmark_Device * dev);
+void sanei_lexmark_low_destroy (Lexmark_Device * dev);
+SANE_Status sanei_lexmark_low_open_device (Lexmark_Device * dev);
+void sanei_lexmark_low_close_device (Lexmark_Device * dev);
+SANE_Bool sanei_lexmark_low_search_home_fwd (Lexmark_Device * dev);
+void sanei_lexmark_low_move_fwd (SANE_Int distance, Lexmark_Device * dev,
+ SANE_Byte * regs);
+SANE_Bool sanei_lexmark_low_X74_search_home (Lexmark_Device * dev,
+ SANE_Byte * regs);
+SANE_Bool sanei_lexmark_low_search_home_bwd (Lexmark_Device * dev);
+SANE_Int sanei_lexmark_low_find_start_line (Lexmark_Device * dev);
+SANE_Status sanei_lexmark_low_set_scan_regs (Lexmark_Device * dev,
+ SANE_Int resolution,
+ SANE_Int offset,
+ SANE_Bool calibrated);
+SANE_Status sanei_lexmark_low_start_scan (Lexmark_Device * dev);
+long sanei_lexmark_low_read_scan_data (SANE_Byte * data, SANE_Int size,
+ Lexmark_Device * dev);
+SANE_Status sanei_lexmark_low_assign_model (Lexmark_Device * dev,
+ SANE_String_Const devname,
+ SANE_Int vendor, SANE_Int product,
+ SANE_Byte mainboard);
+
+/*
+ * scanner calibration functions
+ */
+SANE_Status sanei_lexmark_low_offset_calibration (Lexmark_Device * dev);
+SANE_Status sanei_lexmark_low_gain_calibration (Lexmark_Device * dev);
+SANE_Status sanei_lexmark_low_shading_calibration (Lexmark_Device * dev);
+SANE_Status sanei_lexmark_low_calibration (Lexmark_Device * dev);
+
+#endif /* LEXMARK_H */
diff --git a/backend/lexmark_low.c b/backend/lexmark_low.c
new file mode 100644
index 0000000..ae0db2c
--- /dev/null
+++ b/backend/lexmark_low.c
@@ -0,0 +1,6106 @@
+/* lexmark-low.c: scanner-interface file for low Lexmark scanners.
+
+ (C) 2005 Fred Odendaal
+ (C) 2006-2013 Stéphane Voltz <stef.dev@free.fr>
+ (C) 2010 "Torsten Houwaart" <ToHo@gmx.de> X74 support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ **************************************************************************/
+
+#undef BACKEND_NAME
+#define BACKEND_NAME lexmark_low
+
+#include "lexmark.h"
+
+#include "lexmark_sensors.c"
+#include "lexmark_models.c"
+
+/* numbre of ranges for offset */
+#define OFFSET_RANGES 5
+
+typedef enum
+{
+ black = 0,
+ white
+}
+region_type;
+
+#define HomeTolerance 32
+
+
+#define LOBYTE(x) ((uint8_t)((x) & 0xFF))
+#define HIBYTE(x) ((uint8_t)((x) >> 8))
+
+/* Static low function proto-types */
+static SANE_Status low_usb_bulk_write (SANE_Int devnum,
+ SANE_Byte * cmd, size_t * size);
+static SANE_Status low_usb_bulk_read (SANE_Int devnum,
+ SANE_Byte * buf, size_t * size);
+static SANE_Status low_write_all_regs (SANE_Int devnum, SANE_Byte * regs);
+static SANE_Bool low_is_home_line (Lexmark_Device * dev,
+ unsigned char *buffer);
+static SANE_Status low_get_start_loc (SANE_Int resolution,
+ SANE_Int * vert_start,
+ SANE_Int * hor_start, SANE_Int offset,
+ Lexmark_Device * dev);
+static void low_rewind (Lexmark_Device * dev, SANE_Byte * regs);
+static SANE_Status low_start_mvmt (SANE_Int devnum);
+static SANE_Status low_stop_mvmt (SANE_Int devnum);
+static SANE_Status low_clr_c6 (SANE_Int devnum);
+static SANE_Status low_simple_scan (Lexmark_Device * dev,
+ SANE_Byte * regs,
+ int xoffset,
+ int pixels,
+ int yoffset,
+ int lines, SANE_Byte ** data);
+static void low_set_scan_area (SANE_Int res,
+ SANE_Int tlx,
+ SANE_Int tly,
+ SANE_Int brx,
+ SANE_Int bry,
+ SANE_Int offset,
+ SANE_Bool half_step,
+ SANE_Byte * regs, Lexmark_Device * dev);
+
+/* Static Read Buffer Proto-types */
+static SANE_Status read_buffer_init (Lexmark_Device * dev, int bytesperline);
+static SANE_Status read_buffer_free (Read_Buffer * rb);
+static size_t read_buffer_bytes_available (Read_Buffer * rb);
+static SANE_Status read_buffer_add_byte (Read_Buffer * rb,
+ SANE_Byte * byte_pointer);
+static SANE_Status read_buffer_add_byte_gray (Read_Buffer * rb,
+ SANE_Byte * byte_pointer);
+static SANE_Status read_buffer_add_bit_lineart (Read_Buffer * rb,
+ SANE_Byte * byte_pointer,
+ SANE_Byte threshold);
+static size_t read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer,
+ size_t rqst_size);
+static SANE_Bool read_buffer_is_empty (Read_Buffer * rb);
+
+
+/*
+ * RTS88XX START
+ *
+ * these rts88xx functions will be spin off in a separate lib
+ * so that they can be reused.
+ */
+
+/*
+ * registers helpers to avoid direct access
+ */
+static SANE_Bool
+rts88xx_is_color (SANE_Byte * regs)
+{
+ if ((regs[0x2f] & 0x11) == 0x11)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static void
+rts88xx_set_gray_scan (SANE_Byte * regs)
+{
+ regs[0x2f] = (regs[0x2f] & 0x0f) | 0x20;
+}
+
+#if 0
+static void
+rts88xx_set_color_scan (SANE_Byte * regs)
+{
+ regs[0x2f] = (regs[0x2f] & 0x0f) | 0x10;
+}
+#endif
+
+static void
+rts88xx_set_offset (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
+ SANE_Byte blue)
+{
+ /* offset for odd pixels */
+ regs[0x02] = red;
+ regs[0x03] = green;
+ regs[0x04] = blue;
+
+ /* offset for even pixels */
+ regs[0x05] = red;
+ regs[0x06] = green;
+ regs[0x07] = blue;
+}
+
+static void
+rts88xx_set_gain (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
+ SANE_Byte blue)
+{
+ regs[0x08] = red;
+ regs[0x09] = green;
+ regs[0x0a] = blue;
+}
+
+/* set # of head moves per CIS read */
+static int
+rts88xx_set_scan_frequency (SANE_Byte * regs, int frequency)
+{
+ regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f);
+ return 0;
+}
+
+/*
+ * read one register at given index
+ */
+static SANE_Status
+rts88xx_read_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char cmd[] = { 0x80, 0x00, 0x00, 0x01 };
+ size_t size;
+
+ cmd[1] = index;
+
+ size = 4;
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_write_bulk (devnum, cmd, &size);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "rts88xx_read_reg: bulk write failed\n");
+ return status;
+ }
+ size = 1;
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_read_bulk (devnum, reg, &size);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "rts88xx_read_reg: bulk read failed\n");
+ return status;
+ }
+ DBG (15, "rts88xx_read_reg: reg[0x%02x]=0x%02x\n", index, *reg);
+ return status;
+}
+
+/*
+ * write one register at given index
+ */
+static SANE_Status
+rts88xx_write_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char cmd[] = { 0x88, 0x00, 0x00, 0x01 };
+ size_t size;
+
+ cmd[1] = index;
+
+ size = 4;
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_write_bulk (devnum, cmd, &size);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "rts88xx_write_reg: bulk write failed\n");
+ return status;
+ }
+ size = 1;
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_write_bulk (devnum, reg, &size);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "rts88xx_write_reg: bulk write failed\n");
+ return status;
+ }
+ DBG (15, "rts88xx_write_reg: reg[0x%02x]=0x%02x\n", index, *reg);
+ return status;
+}
+
+/*
+ * write length consecutive registers, starting at index
+ * register 0xb3 is never wrote in bulk register write, so we split
+ * write if it belongs to the register set sent
+ */
+static SANE_Status
+rts88xx_write_regs (SANE_Int devnum, SANE_Int start, SANE_Byte * source,
+ SANE_Int length)
+{
+ size_t size = 0;
+
+ /* when writing several registers at a time, we avoid writing 0xb3
+ register */
+ if ((start + length > 0xb3) && (length > 1))
+ {
+ size = 0xb3 - start;
+ if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "rts88xx_write_regs : write registers part 1 failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* skip 0xB3 register */
+ size++;
+ start = 0xb4;
+ source = source + size;
+ }
+ size = length - size;
+ if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "rts88xx_write_regs : write registers part 2 failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/*
+ * reads 'needed' bytes of scanned data into 'data'. Actual number of bytes get
+ * is retruned in 'size'
+ */
+static SANE_Status
+rts88xx_read_data (SANE_Int devnum, size_t needed, SANE_Byte * data,
+ size_t * size)
+{
+ SANE_Byte read_cmd[] = { 0x91, 0x00, 0x00, 0x00 };
+ size_t cmd_size;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ /* this block would deserve to be a function */
+ if (needed > MAX_XFER_SIZE)
+ *size = MAX_XFER_SIZE;
+ else
+ *size = needed;
+ read_cmd[3] = (*size) & 0xff;
+ read_cmd[2] = (*size >> 8) & 0xff;
+ read_cmd[1] = (*size >> 16) & 0xff;
+
+ /* send header for 'get scanned data' */
+ cmd_size = 4;
+ status = low_usb_bulk_write (devnum, read_cmd, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ *size = 0;
+ DBG (5, "rts88xx_read_data : header sending failed ...\n");
+ return status;
+ }
+ /* get actual scanned data */
+ status = low_usb_bulk_read (devnum, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ *size = 0;
+ DBG (5, "rts88xx_read_data : data reading failed ...\n");
+ }
+ return status;
+}
+
+/* starts scan by sending color depth, stopping head, the starting it */
+static SANE_Status
+rts88xx_commit (SANE_Int devnum, SANE_Byte depth)
+{
+ SANE_Status status;
+ SANE_Byte reg;
+
+ DBG (2, "rts88xx_commit: start\n");
+
+ /* send color depth depth ??
+ * X1100 -> 0x0f
+ * X1100/B2 -> 0x0d
+ * X1200 -> 0x01 */
+ reg = depth;
+ rts88xx_write_reg (devnum, 0x2c, &reg);
+
+ /* stop before starting */
+ low_stop_mvmt (devnum);
+
+ /* effective start */
+ status = low_start_mvmt (devnum);
+
+ DBG (2, "rts88xx_commit: end\n");
+
+ return status;
+}
+
+/*
+ * RTS88XX END
+ */
+
+
+
+/*
+ * sets the scanner idle
+ */
+static SANE_Status
+lexmark_low_set_idle (SANE_Int devnum)
+{
+ SANE_Byte regs[14] =
+ { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60
+ };
+ if (rts88xx_write_regs (devnum, 16, regs, 14) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "lexmark_low_set_idle : register write failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/* wake up scanner */
+#if 0
+static SANE_Status
+lexmark_low_wake_up (Lexmark_Device * dev)
+{
+ SANE_Byte regs[5] = { 0x12, 0x14, 0x16, 0x18, 0x1a };
+ SANE_Byte values[5] = { 0x0f, 0x00, 0x07, 0x00, 0x00 };
+ int i;
+
+ /* send the wake-up sequence, one reg at at time */
+ for (i = 0; i < 10; i++)
+ {
+ if (rts88xx_write_reg (dev->devnum, regs[i], values + i) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "lexmark_low_wake_up : register write pass %d failed ...\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+
+/**
+ *
+ */
+#ifdef DEEP_DEBUG
+static void
+write_pnm_file (char *title, int pixels, int lines, int color,
+ unsigned char *data)
+{
+ FILE *fdbg;
+ int x, y;
+
+ fdbg = fopen (title, "wb");
+ if (fdbg == NULL)
+ return;
+
+ if (color)
+ {
+ fprintf (fdbg, "P6\n%d %d\n255\n", pixels, lines);
+ for (y = 0; y < lines; y++)
+ {
+ for (x = 0; x < pixels; x += 2)
+ {
+ fputc (data[y * pixels * 3 + x + 1], fdbg);
+ fputc (data[y * pixels * 3 + x + 1 + pixels], fdbg);
+ fputc (data[y * pixels * 3 + x + 1 + pixels * 2], fdbg);
+ fputc (data[y * pixels * 3 + x], fdbg);
+ fputc (data[y * pixels * 3 + x + pixels], fdbg);
+ fputc (data[y * pixels * 3 + x + pixels * 2], fdbg);
+ }
+ }
+ }
+ else
+ {
+ fprintf (fdbg, "P5\n%d %d\n255\n", pixels, lines);
+ fwrite (data, pixels, lines, fdbg);
+ }
+ fclose (fdbg);
+}
+#endif
+
+/*
+ * mid level hardware functions
+ */
+/*
+ * model init
+ */
+SANE_Status
+sanei_lexmark_low_init (Lexmark_Device * dev)
+{
+ int i;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ status = SANE_STATUS_UNSUPPORTED;
+ DBG (2, "low_init: start\n");
+
+ /* clear all registers first */
+ for (i = 0; i < 255; i++)
+ {
+ dev->shadow_regs[i] = 0;
+ }
+
+ /* set up per model constant values */
+ dev->shadow_regs[0xf3] = 0xf8;
+ dev->shadow_regs[0xf4] = 0x7f;
+
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x00] = 0x04;
+ dev->shadow_regs[0x01] = 0x43;
+ dev->shadow_regs[0x0b] = 0x70;
+ dev->shadow_regs[0x12] = 0x0f;
+ dev->shadow_regs[0x16] = 0x07;
+ dev->shadow_regs[0x1d] = 0x20;
+ dev->shadow_regs[0x28] = 0xe0;
+ dev->shadow_regs[0x29] = 0xe3;
+ dev->shadow_regs[0x2a] = 0xeb;
+ dev->shadow_regs[0x2b] = 0x0d;
+ dev->shadow_regs[0x2e] = 0x40;
+ dev->shadow_regs[0x2e] = 0x86;
+ dev->shadow_regs[0x2f] = 0x01;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x31] = 0x06;
+ dev->shadow_regs[0x33] = 0x01;
+ dev->shadow_regs[0x34] = 0x50;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x50;
+ dev->shadow_regs[0x37] = 0x01;
+ dev->shadow_regs[0x38] = 0x50;
+ dev->shadow_regs[0x3a] = 0x20;
+ dev->shadow_regs[0x3c] = 0x88;
+ dev->shadow_regs[0x3d] = 0x08;
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x66] = 0x64;
+ dev->shadow_regs[0x6c] = 0xc8;
+ dev->shadow_regs[0x72] = 0x1a;
+ dev->shadow_regs[0x74] = 0x23;
+ dev->shadow_regs[0x75] = 0x03;
+ dev->shadow_regs[0x79] = 0x40;
+ dev->shadow_regs[0x7A] = 0x01;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+ dev->shadow_regs[0x93] = 0x02;
+ dev->shadow_regs[0x94] = 0x0e;
+ dev->shadow_regs[0xa3] = 0xcc;
+ dev->shadow_regs[0xa4] = 0x27;
+ dev->shadow_regs[0xa5] = 0x24;
+ dev->shadow_regs[0xc2] = 0x80;
+ dev->shadow_regs[0xc3] = 0x01;
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x0a;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x0a;
+ dev->shadow_regs[0xe2] = 0x70;
+ dev->shadow_regs[0xe3] = 0x17;
+ dev->shadow_regs[0xf3] = 0xe0;
+ dev->shadow_regs[0xf4] = 0xff;
+ dev->shadow_regs[0xf5] = 0x01;
+ status = SANE_STATUS_GOOD;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x01] = 0x43;
+ dev->shadow_regs[0x0b] = 0x70;
+ dev->shadow_regs[0x11] = 0x01;
+ dev->shadow_regs[0x12] = 0x0f;
+ dev->shadow_regs[0x13] = 0x01;
+ dev->shadow_regs[0x15] = 0x01;
+ dev->shadow_regs[0x16] = 0x0f;
+ dev->shadow_regs[0x1d] = 0x20;
+ dev->shadow_regs[0x28] = 0xeb;
+ dev->shadow_regs[0x29] = 0xee;
+ dev->shadow_regs[0x2a] = 0xf7;
+ dev->shadow_regs[0x2b] = 0x01;
+ dev->shadow_regs[0x2e] = 0x86;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x33] = 0x01;
+ dev->shadow_regs[0x3a] = 0x20;
+ dev->shadow_regs[0x3b] = 0x37;
+ dev->shadow_regs[0x3c] = 0x88;
+ dev->shadow_regs[0x3d] = 0x08;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x72] = 0x05;
+ dev->shadow_regs[0x74] = 0x0e;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x8c] = 0x02;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+ dev->shadow_regs[0x94] = 0x0e;
+ dev->shadow_regs[0xa3] = 0xcc;
+ dev->shadow_regs[0xa4] = 0x27;
+ dev->shadow_regs[0xa5] = 0x24;
+ dev->shadow_regs[0xb0] = 0xb2;
+ dev->shadow_regs[0xb2] = 0x04;
+ dev->shadow_regs[0xc2] = 0x80;
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xed] = 0xc2;
+ dev->shadow_regs[0xee] = 0x02;
+ status = SANE_STATUS_GOOD;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x00] = 0x00;
+ dev->shadow_regs[0x01] = 0x43;
+ dev->shadow_regs[0x0b] = 0x70;
+ dev->shadow_regs[0x0c] = 0x28;
+ dev->shadow_regs[0x0d] = 0xa4;
+ dev->shadow_regs[0x11] = 0x01;
+ dev->shadow_regs[0x12] = 0x0f;
+ dev->shadow_regs[0x13] = 0x01;
+ dev->shadow_regs[0x15] = 0x01;
+ dev->shadow_regs[0x16] = 0x0f;
+ dev->shadow_regs[0x17] = 0x00;
+ dev->shadow_regs[0x1d] = 0x20;
+ dev->shadow_regs[0x28] = 0xf5;
+ dev->shadow_regs[0x29] = 0xf7;
+ dev->shadow_regs[0x2a] = 0xf5;
+ dev->shadow_regs[0x2b] = 0x17;
+ dev->shadow_regs[0x2d] = 0x41;
+ dev->shadow_regs[0x2e] = 0x86;
+ dev->shadow_regs[0x2f] = 0x11;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x31] = 0x01;
+ dev->shadow_regs[0x33] = 0x01;
+ dev->shadow_regs[0x34] = 0x50;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x50;
+ dev->shadow_regs[0x37] = 0x01;
+ dev->shadow_regs[0x38] = 0x50;
+ dev->shadow_regs[0x3a] = 0x20;
+ dev->shadow_regs[0x3b] = 0x37;
+ dev->shadow_regs[0x3c] = 0x88;
+ dev->shadow_regs[0x3d] = 0x08;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x47] = 0x01;
+ dev->shadow_regs[0x48] = 0x1a;
+ dev->shadow_regs[0x49] = 0x5b;
+ dev->shadow_regs[0x4a] = 0x1b;
+ dev->shadow_regs[0x4b] = 0x5b;
+ dev->shadow_regs[0x4c] = 0x05;
+ dev->shadow_regs[0x4d] = 0x3f;
+ dev->shadow_regs[0x60] = 0x2f;
+ dev->shadow_regs[0x61] = 0x36;
+ dev->shadow_regs[0x62] = 0x30;
+ dev->shadow_regs[0x63] = 0x36;
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x66] = 0x64;
+ dev->shadow_regs[0x6c] = 0xc8;
+ dev->shadow_regs[0x6d] = 0x00;
+ dev->shadow_regs[0x72] = 0x35;
+ dev->shadow_regs[0x74] = 0x4e;
+ dev->shadow_regs[0x75] = 0x03;
+ dev->shadow_regs[0x79] = 0x40;
+ dev->shadow_regs[0x7a] = 0x01;
+ dev->shadow_regs[0x85] = 0x02;
+ dev->shadow_regs[0x86] = 0x33;
+ dev->shadow_regs[0x87] = 0x0f;
+ dev->shadow_regs[0x88] = 0x24;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x8c] = 0x02;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+ dev->shadow_regs[0x91] = 0x19;
+ dev->shadow_regs[0x92] = 0x20;
+ dev->shadow_regs[0x93] = 0x02;
+ dev->shadow_regs[0x94] = 0x0e;
+ dev->shadow_regs[0xa3] = 0x0d;
+ dev->shadow_regs[0xa4] = 0x5e;
+ dev->shadow_regs[0xa5] = 0x23;
+ dev->shadow_regs[0xb0] = 0x2c;
+ dev->shadow_regs[0xb1] = 0x07;
+ dev->shadow_regs[0xb2] = 0x04;
+ dev->shadow_regs[0xc2] = 0x80;
+ dev->shadow_regs[0xc3] = 0x01;
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x0a;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xca] = 0x0a;
+ dev->shadow_regs[0xe2] = 0xf8;
+ dev->shadow_regs[0xe3] = 0x2a;
+ status = SANE_STATUS_GOOD;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x01] = 0x43;
+ dev->shadow_regs[0x11] = 0x01;
+ dev->shadow_regs[0x12] = 0x0f;
+ dev->shadow_regs[0x13] = 0x01;
+ dev->shadow_regs[0x15] = 0x01;
+ dev->shadow_regs[0x16] = 0x0f;
+ dev->shadow_regs[0x17] = 0x00;
+ dev->shadow_regs[0x1d] = 0x20;
+ dev->shadow_regs[0x28] = 0xf5;
+ dev->shadow_regs[0x29] = 0xf7;
+ dev->shadow_regs[0x2a] = 0xf5;
+ dev->shadow_regs[0x2b] = 0x17;
+ dev->shadow_regs[0x2d] = 0x41;
+ dev->shadow_regs[0x2e] = 0x86;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x31] = 0x01;
+ dev->shadow_regs[0x33] = 0x01;
+ dev->shadow_regs[0x34] = 0x50;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x50;
+ dev->shadow_regs[0x37] = 0x01;
+ dev->shadow_regs[0x38] = 0x50;
+ dev->shadow_regs[0x3c] = 0x88;
+ dev->shadow_regs[0x3d] = 0x08;
+ dev->shadow_regs[0x66] = 0x64;
+ dev->shadow_regs[0x67] = 0x00;
+ dev->shadow_regs[0x6c] = 0xc8;
+ dev->shadow_regs[0x6d] = 0x00;
+ dev->shadow_regs[0x72] = 0x35;
+ dev->shadow_regs[0x74] = 0x4e;
+ dev->shadow_regs[0x75] = 0x03;
+ dev->shadow_regs[0x7a] = 0x01;
+ dev->shadow_regs[0x93] = 0x0a;
+ dev->shadow_regs[0x94] = 0x0e;
+
+ dev->shadow_regs[0xc3] = 0x01;
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x0a;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xca] = 0x0a;
+ dev->shadow_regs[0xe2] = 0xf8;
+ dev->shadow_regs[0xe3] = 0x2a;
+ status = SANE_STATUS_GOOD;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x01] = 0x43;
+ dev->shadow_regs[0x0b] = 0x70;
+ dev->shadow_regs[0x0c] = 0x28;
+ dev->shadow_regs[0x0d] = 0xa4;
+ dev->shadow_regs[0x11] = 0x01;
+ dev->shadow_regs[0x12] = 0x0f;
+ dev->shadow_regs[0x13] = 0x01;
+ dev->shadow_regs[0x15] = 0x01;
+ dev->shadow_regs[0x16] = 0x07;
+ dev->shadow_regs[0x1d] = 0x20;
+ dev->shadow_regs[0x28] = 0xf5;
+ dev->shadow_regs[0x29] = 0xf7;
+ dev->shadow_regs[0x2a] = 0xf5;
+ dev->shadow_regs[0x2b] = 0x17;
+ dev->shadow_regs[0x2e] = 0x86;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x31] = 0x01;
+ dev->shadow_regs[0x33] = 0x01;
+ dev->shadow_regs[0x3a] = 0x20;
+ dev->shadow_regs[0x3b] = 0x37;
+ dev->shadow_regs[0x3c] = 0x88;
+ dev->shadow_regs[0x3d] = 0x08;
+ dev->shadow_regs[0x47] = 0x21;
+ dev->shadow_regs[0x48] = 0x1a;
+ dev->shadow_regs[0x49] = 0x5b;
+ dev->shadow_regs[0x4a] = 0x1b;
+ dev->shadow_regs[0x4b] = 0x5b;
+ dev->shadow_regs[0x4c] = 0x05;
+ dev->shadow_regs[0x4d] = 0x3f;
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x89] = 0xf5;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+ dev->shadow_regs[0x94] = 0x0e;
+ dev->shadow_regs[0xa3] = 0x0d;
+ dev->shadow_regs[0xa4] = 0x5e;
+ dev->shadow_regs[0xa5] = 0x23;
+ dev->shadow_regs[0xb0] = 0x2c;
+ dev->shadow_regs[0xb1] = 0x0f;
+ dev->shadow_regs[0xc2] = 0x80;
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc8] = 0x04;
+ status = SANE_STATUS_GOOD;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x01] = 0x43;
+ dev->shadow_regs[0x0b] = 0x70;
+ dev->shadow_regs[0x0c] = 0x28;
+ dev->shadow_regs[0x0d] = 0xa4;
+ dev->shadow_regs[0x11] = 0x01;
+ dev->shadow_regs[0x12] = 0x0f;
+ dev->shadow_regs[0x13] = 0x01;
+ dev->shadow_regs[0x15] = 0x01;
+ dev->shadow_regs[0x16] = 0x0f;
+ dev->shadow_regs[0x1d] = 0x20;
+ dev->shadow_regs[0x28] = 0xe9;
+ dev->shadow_regs[0x29] = 0xeb;
+ dev->shadow_regs[0x2a] = 0xe9;
+ dev->shadow_regs[0x2b] = 0x0b;
+ dev->shadow_regs[0x2d] = 0x01;
+ dev->shadow_regs[0x2e] = 0x86;
+ dev->shadow_regs[0x2f] = 0x11;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x33] = 0x01;
+ dev->shadow_regs[0x34] = 0x50;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x50;
+ dev->shadow_regs[0x37] = 0x01;
+ dev->shadow_regs[0x38] = 0x50;
+ dev->shadow_regs[0x3a] = 0x20;
+ dev->shadow_regs[0x3b] = 0x37;
+ dev->shadow_regs[0x3c] = 0x88;
+ dev->shadow_regs[0x3d] = 0x08;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x47] = 0x01;
+ dev->shadow_regs[0x48] = 0x1a;
+ dev->shadow_regs[0x49] = 0x5b;
+ dev->shadow_regs[0x4a] = 0x1b;
+ dev->shadow_regs[0x4b] = 0x5b;
+ dev->shadow_regs[0x4c] = 0x05;
+ dev->shadow_regs[0x4d] = 0x3f;
+ dev->shadow_regs[0x60] = 0x12;
+ dev->shadow_regs[0x62] = 0x81;
+ dev->shadow_regs[0x63] = 0x03;
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x66] = 0x64;
+ dev->shadow_regs[0x6c] = 0xc8;
+ dev->shadow_regs[0x72] = 0x1e;
+ dev->shadow_regs[0x74] = 0x3c;
+ dev->shadow_regs[0x75] = 0x03;
+ dev->shadow_regs[0x79] = 0x40;
+ dev->shadow_regs[0x7a] = 0x01;
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x1e;
+ dev->shadow_regs[0x87] = 0x39;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x8c] = 0x02;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+ dev->shadow_regs[0x92] = 0x92;
+ dev->shadow_regs[0x93] = 0x02;
+ dev->shadow_regs[0x94] = 0x0e;
+ dev->shadow_regs[0xa3] = 0x0d;
+ dev->shadow_regs[0xa4] = 0x5e;
+ dev->shadow_regs[0xa5] = 0x23;
+ dev->shadow_regs[0xb0] = 0x2c;
+ dev->shadow_regs[0xb1] = 0x07;
+ dev->shadow_regs[0xb2] = 0x04;
+ dev->shadow_regs[0xc2] = 0x80;
+ dev->shadow_regs[0xc3] = 0x01;
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x0a;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xca] = 0x0a;
+ dev->shadow_regs[0xe2] = 0xf8;
+ dev->shadow_regs[0xe3] = 0x2a;
+ dev->shadow_regs[0xf3] = 0xff;
+ dev->shadow_regs[0xf4] = 0x0f;
+ break;
+ }
+ DBG (5, "sanei_lexmark_low_init: init done for model %s/%s\n",
+ dev->model.model, dev->model.name);
+ DBG (2, "low_init: done\n");
+ return status;
+}
+
+void
+sanei_lexmark_low_destroy (Lexmark_Device * dev)
+{
+ /* free the read buffer */
+ if (dev->read_buffer != NULL)
+ read_buffer_free (dev->read_buffer);
+}
+
+
+SANE_Status
+low_usb_bulk_write (SANE_Int devnum, SANE_Byte * cmd, size_t * size)
+{
+ SANE_Status status;
+ size_t cmd_size;
+
+ cmd_size = *size;
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_write_bulk (devnum, cmd, size);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "low_usb_bulk_write: returned %s (size = %lu, expected %lu)\n",
+ sane_strstatus (status), (u_long) * size, (u_long) cmd_size);
+ /* F.O. should reset the pipe here... */
+ }
+ return status;
+}
+
+SANE_Status
+low_usb_bulk_read (SANE_Int devnum, SANE_Byte * buf, size_t * size)
+{
+ SANE_Status status;
+ size_t exp_size;
+
+ exp_size = *size;
+#ifdef FAKE_USB
+ status = SANE_STATUS_GOOD;
+#else
+ status = sanei_usb_read_bulk (devnum, buf, size);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5,
+ "low_usb_bulk_read: returned %s (size = %lu, expected %lu)\n",
+ sane_strstatus (status), (u_long) * size, (u_long) exp_size);
+ /* F.O. should reset the pipe here... */
+ }
+ DBG (7, "low_usb_bulk_read: returned size = %lu (required %lu)\n",
+ (u_long) * size, (u_long) exp_size);
+ return status;
+}
+
+
+SANE_Status
+low_start_mvmt (SANE_Int devnum)
+{
+ SANE_Status status;
+ SANE_Byte reg;
+
+ reg = 0x68;
+ rts88xx_write_reg (devnum, 0xb3, &reg);
+ status = rts88xx_write_reg (devnum, 0xb3, &reg);
+ return status;
+}
+
+SANE_Status
+low_stop_mvmt (SANE_Int devnum)
+{
+ SANE_Status status;
+ SANE_Byte reg;
+
+ /* Stop scanner - clear reg 0xb3: */
+ reg = 0x02;
+ rts88xx_write_reg (devnum, 0xb3, &reg);
+ rts88xx_write_reg (devnum, 0xb3, &reg);
+ reg = 0x00;
+ rts88xx_write_reg (devnum, 0xb3, &reg);
+ status = rts88xx_write_reg (devnum, 0xb3, &reg);
+ return status;
+}
+
+SANE_Status
+low_clr_c6 (SANE_Int devnum)
+{
+ SANE_Status status;
+ SANE_Byte reg;
+
+ /* Clear register 0xC6 */
+ /* cmd_size = 0x05;
+ return low_usb_bulk_write (devnum, clearC6_command_block, &cmd_size); */
+
+ reg = 0x00;
+ status = rts88xx_write_reg (devnum, 0xc6, &reg);
+ return status;
+}
+
+/* stops current scan */
+static SANE_Status
+low_cancel (SANE_Int devnum)
+{
+ SANE_Status status;
+
+ DBG (2, "low_cancel: start\n");
+ status = low_stop_mvmt (devnum);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ status = low_clr_c6 (devnum);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ DBG (2, "low_cancel: end.\n");
+ return status;
+}
+
+static SANE_Status
+low_start_scan (SANE_Int devnum, SANE_Byte * regs)
+{
+ SANE_Status status;
+
+ DBG (2, "low_start_scan: start\n");
+
+ /* writes registers to scanner */
+ regs[0x32] = 0x00;
+ status = low_write_all_regs (devnum, regs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ regs[0x32] = 0x40;
+ status = low_write_all_regs (devnum, regs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* Stop scanner - clear reg 0xb3: */
+ /* status = low_stop_mvmt (devnum);
+ if (status != SANE_STATUS_GOOD)
+ return status; */
+
+ /* then start */
+ status = rts88xx_commit (devnum, regs[0x2c]);
+ DBG (2, "low_start_scan: end.\n");
+ return status;
+}
+
+/* wait for scan data being available */
+static SANE_Status
+low_poll_data (SANE_Int devnum)
+{
+ SANE_Status status;
+ int loops = 0;
+ size_t size;
+ static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };
+ SANE_Byte result[3];
+ SANE_Word count;
+
+ /* Poll the available byte count until not 0 */
+ while (loops < 1000)
+ {
+ /* 10 ms sleep */
+ usleep (10000);
+
+ /* as stated in sanei_lexmark_low_search_home_bwd, we read
+ * available data count twice */
+ size = 4;
+ status = low_usb_bulk_write (devnum, command4_block, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ size = 0x3;
+ status = low_usb_bulk_read (devnum, result, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ size = 4;
+ /* read availbale data size again */
+ status = low_usb_bulk_write (devnum, command4_block, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ size = 0x3;
+ status = low_usb_bulk_read (devnum, result, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ count = result[0] + (result[1] << 8) + (result[2] << 16);
+ if (count != 0)
+ {
+ DBG (15, "low_poll_data: %d bytes available\n", count);
+ return SANE_STATUS_GOOD;
+ }
+ loops++;
+ }
+ return SANE_STATUS_IO_ERROR;
+}
+
+/**
+ * do a simple scan with the given registers. data buffer is allocated within
+ * the function
+ */
+static SANE_Status
+low_simple_scan (Lexmark_Device * dev, SANE_Byte * regs, int xoffset,
+ int pixels, int yoffset, int lines, SANE_Byte ** data)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ static SANE_Byte reg;
+ size_t size, read, needed;
+ int i, bpl, yend;
+
+ DBG (2, "low_simple_scan: start\n");
+ DBG (15, "low_simple_scan: x=%d, pixels=%d (ex=%d), y=%d, lines=%d\n",
+ xoffset, pixels, xoffset + pixels * regs[0x7a], yoffset, lines);
+
+ /* set up registers */
+ regs[0x60] = LOBYTE (yoffset);
+ regs[0x61] = HIBYTE (yoffset);
+ yend = yoffset + lines;
+ if ((dev->model.motor_type == A920_MOTOR
+ || dev->model.motor_type == X74_MOTOR) && rts88xx_is_color (regs)
+ && dev->val[OPT_RESOLUTION].w == 600)
+ yend *= 2;
+ regs[0x62] = LOBYTE (yend);
+ regs[0x63] = HIBYTE (yend);
+
+ regs[0x66] = LOBYTE (xoffset);
+ regs[0x67] = HIBYTE (xoffset);
+
+ regs[0x6c] = LOBYTE (xoffset + pixels * regs[0x7a]);
+ regs[0x6d] = HIBYTE (xoffset + pixels * regs[0x7a]);
+
+ /* allocate memory */
+ if (rts88xx_is_color (regs))
+ bpl = 3 * pixels;
+ else
+ bpl = pixels;
+ *data = (SANE_Byte *) malloc (bpl * lines);
+ if (*data == NULL)
+ {
+ DBG (2,
+ "low_simple_scan: failed to allocate %d bytes !\n", bpl * lines);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* start scan */
+ status = low_cancel (dev->devnum);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+ status = low_start_scan (dev->devnum, regs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* wait for data */
+ status = low_poll_data (dev->devnum);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "low_simple_scan: time-out while waiting for data.\n");
+ return status;
+ }
+
+ /* data reading loop */
+ needed = bpl * lines;
+ DBG (1, "low_simple_scan: bpl=%d, lines=%d, needed=%lu.\n", bpl, lines,
+ (u_long) needed);
+ read = 0;
+ do
+ {
+ /* this block would deserve to be a function */
+ status =
+ rts88xx_read_data (dev->devnum, needed - read, (*data) + read, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ read += size;
+ }
+ while (read < needed);
+
+ /* if needed, wait for motor to stop */
+ if (regs[0xc3] & 0x80)
+ {
+ i = 0;
+ do
+ {
+ if (rts88xx_read_reg (dev->devnum, 0xb3, &reg) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "low_simple_scan: register read failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ usleep (100000);
+ i++;
+ }
+ while ((reg & 0x08) && (i < 100));
+ if (reg & 0x08)
+ {
+ DBG (5,
+ "low_simple_scan : timeout waiting for motor to stop ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* stop scan */
+ status = low_cancel (dev->devnum);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "low_simple_scan: cancel failed.\n");
+ return status;
+ }
+
+ DBG (2, "low_simple_scan: end.\n");
+ return status;
+}
+
+/*
+ * open USB device ,read initial registers values and probe sensor
+ */
+SANE_Status
+sanei_lexmark_low_open_device (Lexmark_Device * dev)
+{
+ /* This function calls the Sane Interface to open this usb device.
+ It also needlessly does what the Windows driver does and reads
+ the entire register set - this may be removed. */
+
+ SANE_Status result;
+ static SANE_Byte command_block[] = { 0x80, 0, 0x00, 0xFF };
+ size_t size;
+ SANE_Byte variant = 0;
+ SANE_Byte shadow_regs[255];
+ int sx, ex;
+ int sy, ey;
+ int i;
+ char msg[2048];
+
+
+#ifdef FAKE_USB
+ result = SANE_STATUS_GOOD;
+ shadow_regs[0x00] = 0x91;
+ shadow_regs[0xb0] = 0x2c;
+ shadow_regs[0x10] = 0x97;
+ shadow_regs[0x10] = 0x87;
+ shadow_regs[0xf3] = 0xf8;
+ shadow_regs[0xf4] = 0x7f;
+#else
+ result = sanei_usb_open (dev->sane.name, &(dev->devnum));
+#endif
+ DBG (2, "sanei_lexmark_low_open_device: devnum=%d\n", dev->devnum);
+
+ size = 4;
+ low_usb_bulk_write (dev->devnum, command_block, &size);
+ size = 0xFF;
+ memset (shadow_regs, 0, sizeof (shadow_regs));
+ low_usb_bulk_read (dev->devnum, shadow_regs, &size);
+
+ if (DBG_LEVEL > 2)
+ {
+ DBG (2, "sanei_lexmark_low_open_device: initial registers values\n");
+ for (i = 0; i < 255; i++)
+ {
+ sprintf (msg + i * 5, "0x%02x ", shadow_regs[i]);
+ }
+ DBG (3, "%s\n", msg);
+ }
+
+ /* it seems that at first read after reset, registers hold information
+ * about the scanner. Register 0x00 is overwritten with 0, so only first read
+ * after USB plug-in gives this value */
+ if (shadow_regs[0] == 0x91)
+ {
+ sx = shadow_regs[0x67] * 256 + shadow_regs[0x66];
+ ex = shadow_regs[0x6d] * 256 + shadow_regs[0x6c];
+ DBG (7, "startx=%d, endx=%d, pixels=%d, coef=%d, r2f=0x%02x\n", sx, ex,
+ ex - sx, dev->shadow_regs[0x7a], shadow_regs[0x2f]);
+ sy = shadow_regs[0x61] * 256 + shadow_regs[0x60];
+ ey = shadow_regs[0x63] * 256 + shadow_regs[0x62];
+ DBG (7, "starty=%d, endy=%d, lines=%d\n", sy, ey, ey - sy);
+ }
+
+ /* we use register 0xb0 to identify details about models */
+ /* this register isn't overwritten during normal operation */
+ if (shadow_regs[0xb0] == 0x2c && dev->model.sensor_type == X1100_B2_SENSOR)
+ {
+ variant = shadow_regs[0xb0];
+ }
+ /* now the same with register 0x10 */
+ /* which most likely signals USB2.0/USB1.1 */
+ if ((dev->model.sensor_type == X1200_SENSOR) && (shadow_regs[0x10] == 0x97))
+ {
+ variant = shadow_regs[0x10];
+ }
+
+ /* if find a case where default model given is inappropriate, reassign it
+ * since we have now the informations to get the real one.
+ * We could avoid this if attach() did open and read registers, not init */
+ if (variant != 0)
+ {
+ DBG (3,
+ "sanei_lexmark_low_open_device: reassign model/sensor for variant 0x%02x\n",
+ variant);
+ sanei_lexmark_low_assign_model (dev, dev->sane.name,
+ dev->model.vendor_id,
+ dev->model.product_id, variant);
+ /* since model has changed, run init again */
+ sanei_lexmark_low_init (dev);
+ }
+ DBG (2, "sanei_lexmark_low_open_device: end\n");
+ return result;
+}
+
+void
+sanei_lexmark_low_close_device (Lexmark_Device * dev)
+{
+ /* put scanner in idle state */
+ lexmark_low_set_idle (dev->devnum);
+
+ /* This function calls the Sane USB library to close this usb device */
+#ifndef FAKE_USB
+ sanei_usb_close (dev->devnum);
+#endif
+ return;
+}
+
+
+/* This function writes the contents of the given registers to the
+ scanner. */
+SANE_Status
+low_write_all_regs (SANE_Int devnum, SANE_Byte * regs)
+{
+ int i;
+ SANE_Status status;
+ size_t size;
+ static SANE_Byte command_block1[0xb7];
+ static SANE_Byte command_block2[0x4f];
+ command_block1[0] = 0x88;
+ command_block1[1] = 0x00;
+ command_block1[2] = 0x00;
+ command_block1[3] = 0xb3;
+ for (i = 0; i < 0xb3; i++)
+ {
+ command_block1[i + 4] = regs[i];
+ }
+ command_block2[0] = 0x88;
+ command_block2[1] = 0xb4;
+ command_block2[2] = 0x00;
+ command_block2[3] = 0x4b;
+ for (i = 0; i < 0x4b; i++)
+ {
+ command_block2[i + 4] = regs[i + 0xb4];
+ }
+ size = 0xb7;
+
+#ifdef DEEP_DEBUG
+ fprintf (stderr, "write_all(0x00,255)=");
+ for (i = 0; i < 255; i++)
+ {
+ fprintf (stderr, "0x%02x ", regs[i]);
+ }
+ fprintf (stderr, "\n");
+#endif
+
+ status = low_usb_bulk_write (devnum, command_block1, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ size = 0x4f;
+ status = low_usb_bulk_write (devnum, command_block2, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Bool
+low_is_home_line (Lexmark_Device * dev, unsigned char *buffer)
+{
+ /*
+ This function assumes the buffer has a size of 2500 bytes.It is
+ destructive to the buffer.
+
+ Here is what it does:
+
+ Go through the buffer finding low and high values, which are computed by
+ comparing to the average:
+ average = (lowest value + highest value)/2
+ High bytes are changed to 0xFF (white), lower or equal bytes are changed
+ to 0x00 (black),so that the buffer only contains white (0xFF) or black
+ (0x00) values.
+
+ Next, we go through the buffer. We use a tolerance of 5 bytes on each end
+ of the buffer and check a region from bytes 5 to 2495. We start assuming
+ we are in a white region and look for the start of a black region. We save
+ this index as the transition from white to black. We also save where we
+ change from black back to white. We continue checking for transitions
+ until the end of the check region. If we don't have exactly two
+ transitions when we reach the end we return SANE_FALSE.
+
+ The final check compares the transition indices to the nominal values
+ plus or minus the tolerence. For the first transition (white to black
+ index) the value must lie in the range 1235-30 (1205) to 1235+30 (1265).
+ For the second transition (black to white) the value must lie in the range
+ 1258-30 (1228) to 1258+30 (1288). If the indices are out of range we
+ return SANE_FALSE. Otherwise, we return SANE_TRUE.
+ */
+
+
+ unsigned char max_byte = 0;
+ unsigned char min_byte = 0xFF;
+ unsigned char average;
+ int i;
+ int home_point1;
+ int home_point2;
+ region_type region;
+ int transition_counter;
+ int index1 = 0;
+ int index2 = 0;
+ int low_range, high_range;
+
+#ifdef DEEP_DEBUG
+ static int numero = 0;
+ char titre[80];
+ FILE *trace = NULL;
+ sprintf (titre, "lgn%03d.pnm", numero);
+ trace = fopen (titre, "wb");
+ if (trace)
+ {
+ fprintf (trace, "P5\n2500 1\n255\n");
+ fwrite (buffer, 2500, 1, trace);
+ fclose (trace);
+ }
+ numero++;
+#endif
+
+ DBG (15, "low_is_home_line: start\n");
+ /* Find the max and the min */
+ for (i = 0; i < 2500; i++)
+ {
+ if (*(buffer + i) > max_byte)
+ max_byte = *(buffer + i);
+ if (*(buffer + i) < min_byte)
+ min_byte = *(buffer + i);
+ }
+
+ /* The average */
+ average = ((max_byte + min_byte) / 2);
+
+ /* Set bytes as white (0xFF) or black (0x00) */
+ for (i = 0; i < 2500; i++)
+ {
+ if (*(buffer + i) > average)
+ *(buffer + i) = 0xFF;
+ else
+ *(buffer + i) = 0x00;
+ }
+
+ region = white;
+ transition_counter = 0;
+
+ /* Go through the check region - bytes 5 to 2495 */
+ /* XXX STEF XXX shrink the area to where the dot should be
+ * +-100 around the 1250 expected location */
+ for (i = 1150; i <= 1350; i++)
+ {
+ /* Check for transition to black */
+ if ((region == white) && (*(buffer + i) == 0))
+ {
+ if (transition_counter < 2)
+ {
+ region = black;
+ index1 = i;
+ transition_counter++;
+ }
+ else
+ {
+ DBG (15, "low_is_home_line: no transition to black \n");
+ return SANE_FALSE;
+ }
+ }
+ /* Check for transition to white */
+ else if ((region == black) && (*(buffer + i) == 0xFF))
+ {
+ if (transition_counter < 2)
+ {
+ region = white;
+ index2 = i;
+ transition_counter++;
+ }
+ else
+ {
+ DBG (15, "low_is_home_line: no transition to white \n");
+ return SANE_FALSE;
+ }
+ }
+ }
+
+ /* Check that the number of transitions is 2 */
+ if (transition_counter != 2)
+ {
+ DBG (15, "low_is_home_line: transitions!=2 (%d)\n", transition_counter);
+ return SANE_FALSE;
+ }
+
+
+
+
+ /* Check that the 1st index is in range */
+ home_point1 = dev->model.HomeEdgePoint1;
+ low_range = home_point1 - HomeTolerance;
+ high_range = home_point1 + HomeTolerance;
+
+ if ((index1 < low_range) || (index1 > high_range))
+ {
+ DBG (15, "low_is_home_line: index1=%d out of range\n", index1);
+ return SANE_FALSE;
+ }
+
+
+ /* Check that the 2nd index is in range */
+ home_point2 = dev->model.HomeEdgePoint2;
+ low_range = home_point2 - HomeTolerance;
+ high_range = home_point2 + HomeTolerance;
+
+ if ((index2 < low_range) || (index2 > high_range))
+ {
+ DBG (15, "low_is_home_line: index2=%d out of range.\n", index2);
+ return SANE_FALSE;
+ }
+
+ /* We made it this far, so its a good home line. Return True */
+ DBG (15, "low_is_home_line: success\n");
+ return SANE_TRUE;
+}
+
+void
+sanei_lexmark_low_move_fwd (SANE_Int distance, Lexmark_Device * dev,
+ SANE_Byte * regs)
+{
+ /*
+ This function moves the scan head forward with the highest vertical
+ resolution of 1200dpi. The distance moved is given by the distance
+ parameter.
+
+ As an example, given a distance parameter of 600, the scan head will
+ move 600/1200", or 1/2" forward.
+ */
+
+ static SANE_Byte pollstopmoving_command_block[] =
+ { 0x80, 0xb3, 0x00, 0x01 };
+
+
+ size_t cmd_size;
+ SANE_Int devnum;
+ SANE_Bool scan_head_moving;
+ SANE_Byte read_result;
+
+ DBG (2, "sanei_lexmark_low_move_fwd: \n");
+ devnum = dev->devnum;
+
+
+ /* registers set-up */
+ regs[0x2c] = 0x00;
+ regs[0x2d] = 0x41;
+ regs[0x65] = 0x80;
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ rts88xx_set_scan_frequency (regs, 0);
+ regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ regs[0x8b] = 0x00;
+ regs[0x8c] = 0x00;
+ regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ rts88xx_set_scan_frequency (regs, 0);
+ regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ rts88xx_set_scan_frequency (regs, 0);
+ regs[0x8b] = 0xff;
+ regs[0x8c] = 0x02;
+ regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x2d] = 0x01;
+ rts88xx_set_scan_frequency (regs, 0);
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x2d] = 0x4f;
+ rts88xx_set_scan_frequency (regs, 0);
+ break;
+ }
+
+ /* set grayscale scan + nodata/nochannel? */
+ regs[0x2f] = 0xa1;
+
+ /* set ? */
+ regs[0x34] = 0x50;
+ regs[0x35] = 0x01;
+ regs[0x36] = 0x50;
+ regs[0x37] = 0x01;
+ regs[0x38] = 0x50;
+ /* set motor resolution divisor */
+ regs[0x39] = 0x00;
+ /* set vertical start/end positions */
+ regs[0x60] = LOBYTE (distance - 1);
+ regs[0x61] = HIBYTE (distance - 1);
+ regs[0x62] = LOBYTE (distance);
+ regs[0x63] = HIBYTE (distance);
+ /* set horizontal start position */
+ regs[0x66] = 0x64;
+ regs[0x67] = 0x00;
+ /* set horizontal end position */
+ regs[0x6c] = 0xc8;
+ regs[0x6d] = 0x00;
+ /* set horizontal resolution */
+ regs[0x79] = 0x40;
+ regs[0x7a] = 0x01;
+ /* don't buffer data for this scan */
+ regs[0xb2] = 0x04;
+ /* Motor enable & Coordinate space denominator */
+ regs[0xc3] = 0x81;
+ /* Movement direction & step size */
+ regs[0xc6] = 0x09;
+ /* ? */
+ regs[0x80] = 0x00;
+ regs[0x81] = 0x00;
+ regs[0x82] = 0x00;
+ regs[0xc5] = 0x0a;
+
+
+ switch (dev->model.motor_type)
+ {
+ case X1100_MOTOR:
+ case A920_MOTOR:
+ /* step size range2 */
+ regs[0xc9] = 0x3b;
+ /* ? */
+ regs[0xca] = 0x0a;
+ /* motor curve stuff */
+ regs[0xe0] = 0x00;
+ regs[0xe1] = 0x00;
+ regs[0xe4] = 0x00;
+ regs[0xe5] = 0x00;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe2] = 0x09;
+ regs[0xe3] = 0x1a;
+ regs[0xe6] = 0xdc;
+ regs[0xe9] = 0x1b;
+ regs[0xec] = 0x07;
+ regs[0xef] = 0x03;
+ break;
+ case X74_MOTOR:
+ regs[0xc5] = 0x41;
+ /* step size range2 */
+ regs[0xc9] = 0x39;
+ /* ? */
+ regs[0xca] = 0x40;
+ /* motor curve stuff */
+ regs[0xe0] = 0x00;
+ regs[0xe1] = 0x00;
+ regs[0xe2] = 0x09;
+ regs[0xe3] = 0x1a;
+ regs[0xe4] = 0x00;
+ regs[0xe5] = 0x00;
+ regs[0xe6] = 0x64;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x32;
+ regs[0xec] = 0x0c;
+ regs[0xef] = 0x08;
+ break;
+ }
+
+
+ /* prepare for register write */
+ low_clr_c6 (devnum);
+ low_stop_mvmt (devnum);
+
+/* Move Forward without scanning: */
+ regs[0x32] = 0x00;
+ low_write_all_regs (devnum, regs);
+ regs[0x32] = 0x40;
+ low_write_all_regs (devnum, regs);
+
+ /* Stop scanner - clear reg 0xb3: */
+ /* low_stop_mvmt (devnum); */
+
+ rts88xx_commit (devnum, regs[0x2c]);
+
+ /* Poll for scanner stopped - return value(3:0) = 0: */
+ scan_head_moving = SANE_TRUE;
+ while (scan_head_moving)
+ {
+#ifdef FAKE_USB
+ scan_head_moving = SANE_FALSE;
+#else
+ cmd_size = 0x04;
+ low_usb_bulk_write (devnum, pollstopmoving_command_block, &cmd_size);
+ cmd_size = 0x1;
+ low_usb_bulk_read (devnum, &read_result, &cmd_size);
+ if ((read_result & 0xF) == 0x0)
+ {
+ scan_head_moving = SANE_FALSE;
+ }
+#endif
+ }
+
+ /* this is needed to find the start line properly */
+ if (dev->model.sensor_type == X74_SENSOR)
+ low_stop_mvmt (devnum);
+
+ DBG (2, "sanei_lexmark_low_move_fwd: end.\n");
+}
+
+SANE_Bool
+sanei_lexmark_low_search_home_fwd (Lexmark_Device * dev)
+{
+ /* This function actually searches backwards one line looking for home */
+
+ SANE_Int devnum;
+ int i;
+ SANE_Byte poll_result[3];
+ SANE_Byte *buffer;
+ SANE_Byte temp_byte;
+
+ static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };
+
+ static SANE_Byte command5_block[] = { 0x91, 0x00, 0x09, 0xc4 };
+
+ size_t cmd_size;
+ SANE_Bool got_line;
+ SANE_Bool ret_val;
+
+ devnum = dev->devnum;
+
+ DBG (2, "sanei_lexmark_low_search_home_fwd:\n");
+
+ /* set up registers according to the sensor type */
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x2c] = 0x03;
+ dev->shadow_regs[0x2d] = 0x45;
+ dev->shadow_regs[0x2f] = 0x21;
+ dev->shadow_regs[0x30] = 0x48;
+ dev->shadow_regs[0x31] = 0x06;
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x09;
+ dev->shadow_regs[0x37] = 0x09;
+ dev->shadow_regs[0x38] = 0x0d;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x75] = 0x00;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0f;
+ dev->shadow_regs[0x2d] = 0x51;
+ dev->shadow_regs[0x2f] = 0x21;
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x35] = 0x04;
+ dev->shadow_regs[0x36] = 0x08;
+ dev->shadow_regs[0x37] = 0x08;
+ dev->shadow_regs[0x38] = 0x0b;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x09;
+ dev->shadow_regs[0x37] = 0x09;
+ dev->shadow_regs[0x38] = 0x0d;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x72] = 0x35;
+ dev->shadow_regs[0x74] = 0x4e;
+
+ dev->shadow_regs[0x85] = 0x20; /* 05 */
+ dev->shadow_regs[0x86] = 0x00; /* 05 */
+ dev->shadow_regs[0x87] = 0x00; /* 05 */
+ dev->shadow_regs[0x88] = 0x00; /* 45 */
+ dev->shadow_regs[0x89] = 0x00;
+ dev->shadow_regs[0x8b] = 0xff;
+
+ dev->shadow_regs[0x93] = 0x06; /* 0e */
+
+ dev->shadow_regs[0x75] = 0x00; /* */
+ dev->shadow_regs[0x91] = 0x00; /* 60 */
+ dev->shadow_regs[0x92] = 0x00; /* 8d */
+ dev->shadow_regs[0xb1] = 0x00; /* */
+ dev->shadow_regs[0xc5] = 0x00; /* */
+ dev->shadow_regs[0xca] = 0x00; /* */
+ dev->shadow_regs[0xc3] = 0x01; /* */
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x09;
+ dev->shadow_regs[0x37] = 0x09;
+ dev->shadow_regs[0x38] = 0x0d;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x72] = 0x35;
+ dev->shadow_regs[0x74] = 0x4e;
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x88] = 0x45;
+ dev->shadow_regs[0x89] = 0x00;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x91] = 0x60;
+ dev->shadow_regs[0x92] = 0x8d;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x2c] = 0x01;
+ dev->shadow_regs[0x2d] = 0x03;
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x35] = 0x04;
+ dev->shadow_regs[0x36] = 0x08;
+ dev->shadow_regs[0x37] = 0x08;
+ dev->shadow_regs[0x38] = 0x0b;
+ dev->shadow_regs[0x66] = 0x88;
+ dev->shadow_regs[0x6c] = 0x10;
+ dev->shadow_regs[0x6d] = 0x14;
+ dev->shadow_regs[0x75] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ dev->shadow_regs[0xc5] = 0x00;
+ dev->shadow_regs[0xca] = 0x00;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x0b] = 0x70;
+ dev->shadow_regs[0x0c] = 0x28;
+ dev->shadow_regs[0x0d] = 0xa4;
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x2f] = 0x21;
+ dev->shadow_regs[0x32] = 0x40;
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x09;
+ dev->shadow_regs[0x37] = 0x09;
+ dev->shadow_regs[0x38] = 0x0d;
+ dev->shadow_regs[0x3a] = 0x20;
+ dev->shadow_regs[0x3b] = 0x37;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x47] = 0x01;
+ dev->shadow_regs[0x48] = 0x1a;
+ dev->shadow_regs[0x49] = 0x5b;
+ dev->shadow_regs[0x4a] = 0x1b;
+ dev->shadow_regs[0x4b] = 0x5b;
+ dev->shadow_regs[0x4c] = 0x05;
+ dev->shadow_regs[0x4d] = 0x3f;
+ dev->shadow_regs[0x75] = 0x00;
+ dev->shadow_regs[0x85] = 0x03;
+ dev->shadow_regs[0x86] = 0x33;
+ dev->shadow_regs[0x87] = 0x8f;
+ dev->shadow_regs[0x88] = 0x34;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+ dev->shadow_regs[0x91] = 0x59;
+ dev->shadow_regs[0x92] = 0x10;
+ dev->shadow_regs[0x93] = 0x06;
+ dev->shadow_regs[0xa3] = 0x0d;
+ dev->shadow_regs[0xa4] = 0x5e;
+ dev->shadow_regs[0xa5] = 0x23;
+ dev->shadow_regs[0xb1] = 0x07;
+ dev->shadow_regs[0xc2] = 0x80;
+ dev->shadow_regs[0xc5] = 0x00;
+ dev->shadow_regs[0xca] = 0x00;
+ break;
+ }
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x8c] = 0x02;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0xb2] = 0x00;
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+
+ rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain,
+ dev->sensor->default_gain, dev->sensor->default_gain);
+ rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80);
+
+ /* set grayscale scan */
+ rts88xx_set_gray_scan (dev->shadow_regs);
+
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x07;
+
+ /* set vertical start/end positions */
+ dev->shadow_regs[0x60] = 0x01;
+ dev->shadow_regs[0x61] = 0x00;
+ dev->shadow_regs[0x62] = 0x02;
+ dev->shadow_regs[0x63] = 0x00;
+
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+
+ /* set horizontal start position */
+ dev->shadow_regs[0x66] = 0x6a; /* 0x88 for X1200 */
+ dev->shadow_regs[0x67] = 0x00;
+ /* set horizontal end position */
+ dev->shadow_regs[0x6c] = 0xf2; /* 0x1410 for X1200 */
+ dev->shadow_regs[0x6d] = 0x13;
+ /* set horizontal resolution */
+ dev->shadow_regs[0x79] = 0x40;
+ dev->shadow_regs[0x7a] = 0x02;
+ /* Motor disable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x01;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x01;
+
+ switch (dev->model.motor_type)
+ {
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* step size range2 */
+ dev->shadow_regs[0xc9] = 0x3b;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x01;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x03;
+ break;
+ case X74_MOTOR:
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x00;
+ dev->shadow_regs[0xc8] = 0x04;
+ /* step size range2 */
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x00;
+ /* motor curve stuff */
+ dev->shadow_regs[0xe0] = 0x29;
+ dev->shadow_regs[0xe1] = 0x17;
+ dev->shadow_regs[0xe2] = 0x8f;
+ dev->shadow_regs[0xe3] = 0x06;
+ dev->shadow_regs[0xe4] = 0x61;
+ dev->shadow_regs[0xe5] = 0x16;
+ dev->shadow_regs[0xe6] = 0x64;
+ dev->shadow_regs[0xe7] = 0xb5;
+ dev->shadow_regs[0xe8] = 0x08;
+ dev->shadow_regs[0xe9] = 0x32;
+ dev->shadow_regs[0xec] = 0x0c;
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ }
+
+ /* Stop the scanner */
+ low_stop_mvmt (devnum);
+
+ /* write regs out twice */
+ dev->shadow_regs[0x32] = 0x00;
+ low_write_all_regs (devnum, dev->shadow_regs);
+ dev->shadow_regs[0x32] = 0x40;
+ low_write_all_regs (devnum, dev->shadow_regs);
+
+ /* Start Scan */
+ rts88xx_commit (devnum, dev->shadow_regs[0x2c]);
+
+ /* Poll the available byte count until not 0 */
+ got_line = SANE_FALSE;
+ while (!got_line)
+ {
+ cmd_size = 4;
+ low_usb_bulk_write (devnum, command4_block, &cmd_size);
+ cmd_size = 0x3;
+ low_usb_bulk_read (devnum, poll_result, &cmd_size);
+ if (!
+ (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0))
+ {
+ /* if result != 00 00 00 we got data */
+ got_line = SANE_TRUE;
+ }
+ }
+
+ /* create buffer for scan data */
+ buffer = calloc (2500, sizeof (char));
+ if (buffer == NULL)
+ {
+ return SANE_FALSE;
+ }
+
+ /* Tell the scanner to send the data */
+ /* Write: 91 00 09 c4 */
+ cmd_size = 4;
+ low_usb_bulk_write (devnum, command5_block, &cmd_size);
+ /* Read it */
+ cmd_size = 0x09c4;
+ low_usb_bulk_read (devnum, buffer, &cmd_size);
+
+ /* Reverse order of bytes in words of buffer */
+ for (i = 0; i < 2500; i = i + 2)
+ {
+ temp_byte = *(buffer + i);
+ *(buffer + i) = *(buffer + i + 1);
+ *(buffer + i + 1) = temp_byte;
+ }
+
+ /* check for home position */
+ ret_val = low_is_home_line (dev, buffer);
+
+ if (ret_val)
+ DBG (2, "sanei_lexmark_low_search_home_fwd: !!!HOME POSITION!!!\n");
+
+ /* free the buffer */
+ free (buffer);
+ DBG (2, "sanei_lexmark_low_search_home_fwd: end.\n");
+
+ return ret_val;
+}
+
+SANE_Bool
+sanei_lexmark_low_search_home_bwd (Lexmark_Device * dev)
+{
+/* This function must only be called if the scan head is past the home dot.
+ It could damage the scanner if not.
+
+ This function tells the scanner to do a grayscale scan backwards with a
+ 300dpi resolution. It reads 2500 bytes of data between horizontal
+ co-ordinates 0x6a and 0x13f2.
+
+ The scan is set to read between vertical co-ordinates from 0x0a to 0x0f46,
+ or 3900 lines. This equates to 13" at 300dpi, so we must stop the scan
+ before it bangs against the end. A line limit is set so that a maximum of
+ 0x0F3C (13"*300dpi) lines can be read.
+
+ To read the scan data we create a buffer space large enough to hold 10
+ lines of data. For each read we poll twice, ignoring the first poll. This
+ is required for timing. We repeat the double poll until there is data
+ available. The number of lines (or number of buffers in our buffer space)
+ is calculated from the size of the data available from the scanner. The
+ number of buffers is calculated as the space required to hold 1.5 times
+ the the size of the data available from the scanner.
+
+ After data is read from the scanner each line is checked if it is on the
+ home dot. Lines are continued to be read until we are no longer on the home
+ dot. */
+
+
+ SANE_Int devnum;
+ SANE_Status status;
+ int i, j;
+ SANE_Byte poll_result[3];
+ SANE_Byte *buffer;
+ SANE_Byte *buffer_start;
+ SANE_Byte temp_byte;
+
+ SANE_Int buffer_count = 0;
+ SANE_Int size_requested;
+ SANE_Int size_returned;
+ SANE_Int no_of_buffers;
+ SANE_Int buffer_limit = 0xF3C;
+
+ SANE_Int high_byte, mid_byte, low_byte;
+ SANE_Int home_line_count;
+ SANE_Bool in_home_region;
+
+ static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };
+
+ static SANE_Byte command5_block[] = { 0x91, 0x00, 0xff, 0xc0 };
+#ifdef DEEP_DEBUG
+ FILE *img = NULL;
+#endif
+
+ size_t cmd_size;
+ SANE_Bool got_line;
+
+ devnum = dev->devnum;
+
+ DBG (2, "sanei_lexmark_low_search_home_bwd:\n");
+
+ /* set up registers */
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x2c] = 0x03;
+ dev->shadow_regs[0x2d] = 0x45;
+ dev->shadow_regs[0x34] = 0x09;
+ dev->shadow_regs[0x35] = 0x09;
+ dev->shadow_regs[0x36] = 0x11;
+ dev->shadow_regs[0x37] = 0x11;
+ dev->shadow_regs[0x38] = 0x19;
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ dev->shadow_regs[0x40] = 0x80;
+ /* important for detection of b/w transitions */
+ dev->shadow_regs[0x75] = 0x00;
+ dev->shadow_regs[0x8b] = 0xff;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0f;
+ dev->shadow_regs[0x2d] = 0x51;
+ dev->shadow_regs[0x34] = 0x07;
+ dev->shadow_regs[0x35] = 0x07;
+ dev->shadow_regs[0x36] = 0x0f;
+ dev->shadow_regs[0x37] = 0x0f;
+ dev->shadow_regs[0x38] = 0x15;
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x34] = 0x09;
+ dev->shadow_regs[0x35] = 0x09;
+ dev->shadow_regs[0x36] = 0x11;
+ dev->shadow_regs[0x37] = 0x11;
+ dev->shadow_regs[0x38] = 0x19;
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x34] = 0x09;
+ dev->shadow_regs[0x35] = 0x09;
+ dev->shadow_regs[0x36] = 0x11;
+ dev->shadow_regs[0x37] = 0x11;
+ dev->shadow_regs[0x38] = 0x19;
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x2c] = 0x01;
+ dev->shadow_regs[0x2d] = 0x03;
+ dev->shadow_regs[0x34] = 0x07;
+ dev->shadow_regs[0x35] = 0x07;
+ dev->shadow_regs[0x36] = 0x0f;
+ dev->shadow_regs[0x37] = 0x0f;
+ dev->shadow_regs[0x38] = 0x15;
+ break;
+
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x34] = 0x09;
+ dev->shadow_regs[0x35] = 0x09;
+ dev->shadow_regs[0x36] = 0x11;
+ dev->shadow_regs[0x37] = 0x11;
+ dev->shadow_regs[0x38] = 0x19;
+ dev->shadow_regs[0x85] = 0x03;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ }
+ rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain,
+ dev->sensor->default_gain, dev->sensor->default_gain);
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x8b] = 0xff;
+ dev->shadow_regs[0x8c] = 0x02;
+ dev->shadow_regs[0xb2] = 0x00;
+
+ /* set calibration */
+ rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80);
+
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+ /* set vertical start/end positions */
+ dev->shadow_regs[0x60] = 0x0a;
+ dev->shadow_regs[0x61] = 0x00;
+ dev->shadow_regs[0x62] = 0x46;
+ dev->shadow_regs[0x63] = 0x0f;
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 2);
+ /* set horizontal start position */
+ dev->shadow_regs[0x66] = 0x6a; /* 0x88 for X1200 */
+ dev->shadow_regs[0x67] = 0x00;
+ /* set horizontal end position */
+ dev->shadow_regs[0x6c] = 0xf2; /* 0x1410 for X1200, 13f2 for X1200/rev. 97 */
+ dev->shadow_regs[0x6d] = 0x13;
+ /* set horizontal resolution */
+ dev->shadow_regs[0x79] = 0x40;
+ dev->shadow_regs[0x7a] = 0x02;
+
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x01;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x00;
+ dev->shadow_regs[0xc8] = 0x04;
+ /* step size range2 */
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x00;
+ /* motor curve stuff */
+ dev->shadow_regs[0xe0] = 0x29;
+ dev->shadow_regs[0xe1] = 0x17;
+ dev->shadow_regs[0xe2] = 0x8f;
+ dev->shadow_regs[0xe3] = 0x06;
+ dev->shadow_regs[0xe4] = 0x61;
+ dev->shadow_regs[0xe5] = 0x16;
+ dev->shadow_regs[0xe6] = 0x64;
+ dev->shadow_regs[0xe7] = 0xb5;
+ dev->shadow_regs[0xe8] = 0x08;
+ dev->shadow_regs[0xe9] = 0x32;
+ dev->shadow_regs[0xec] = 0x0c;
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x19;
+ /* step size range2 */
+ dev->shadow_regs[0xc9] = 0x3a;
+ /* ? */
+ dev->shadow_regs[0xca] = 0x08;
+ /* motor curve stuff */
+ dev->shadow_regs[0xe0] = 0xe3;
+ dev->shadow_regs[0xe1] = 0x18;
+ dev->shadow_regs[0xe2] = 0x03;
+ dev->shadow_regs[0xe3] = 0x06;
+ dev->shadow_regs[0xe4] = 0x2b;
+ dev->shadow_regs[0xe5] = 0x17;
+ dev->shadow_regs[0xe6] = 0xdc;
+ dev->shadow_regs[0xe7] = 0xb3;
+ dev->shadow_regs[0xe8] = 0x07;
+ dev->shadow_regs[0xe9] = 0x1b;
+ dev->shadow_regs[0xec] = 0x07;
+ dev->shadow_regs[0xef] = 0x03;
+ break;
+ }
+
+ /* Stop the scanner */
+ low_stop_mvmt (devnum);
+
+ /* write regs out twice */
+ dev->shadow_regs[0x32] = 0x00;
+ low_write_all_regs (devnum, dev->shadow_regs);
+ dev->shadow_regs[0x32] = 0x40;
+ low_write_all_regs (devnum, dev->shadow_regs);
+
+ /* Start Scan */
+ status = rts88xx_commit (devnum, dev->shadow_regs[0x2c]);
+
+ /* create buffer to hold up to 10 lines of scan data */
+ buffer = calloc (10 * 2500, sizeof (char));
+ if (buffer == NULL)
+ {
+ return SANE_FALSE;
+ }
+
+ home_line_count = 0;
+ in_home_region = SANE_FALSE;
+
+#ifdef DEEP_DEBUG
+ img = fopen ("find_bwd.pnm", "wb");
+ fprintf (img, "P5\n2500 100\n255\n");
+#endif
+ while (buffer_count < buffer_limit)
+ {
+ size_returned = 0;
+ got_line = SANE_FALSE;
+ while (!got_line)
+ {
+ /* always poll twice (needed for timing) - disregard 1st poll */
+ cmd_size = 4;
+ status = low_usb_bulk_write (devnum, command4_block, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+ cmd_size = 0x3;
+ status = low_usb_bulk_read (devnum, poll_result, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+ cmd_size = 4;
+ status = low_usb_bulk_write (devnum, command4_block, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+ cmd_size = 0x3;
+ status = low_usb_bulk_read (devnum, poll_result, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+ if (!
+ (poll_result[0] == 0 && poll_result[1] == 0
+ && poll_result[2] == 0))
+ {
+ /* if result != 00 00 00 we got data */
+ got_line = SANE_TRUE;
+ high_byte = poll_result[2] << 16;
+ mid_byte = poll_result[1] << 8;
+ low_byte = poll_result[0];
+ size_returned = high_byte + mid_byte + low_byte;
+ }
+ }
+
+ /*size_requested = size_returned;*/
+ size_requested = 2500;
+ no_of_buffers = size_returned * 3;
+ no_of_buffers = no_of_buffers / 2500;
+ no_of_buffers = no_of_buffers >> 1;
+ /* force 1 buffer at a time to improve accuray, which slow downs search */
+ no_of_buffers = 1;
+
+ if (no_of_buffers < 1)
+ no_of_buffers = 1;
+ else if (no_of_buffers > 10)
+ no_of_buffers = 10;
+ buffer_count = buffer_count + no_of_buffers;
+
+ size_requested = no_of_buffers * 2500;
+
+ /* Tell the scanner to send the data */
+ /* Write: 91 <size_requested> */
+ command5_block[1] = (SANE_Byte) (size_requested >> 16);
+ command5_block[2] = (SANE_Byte) (size_requested >> 8);
+ command5_block[3] = (SANE_Byte) (size_requested & 0xFF);
+
+ cmd_size = 4;
+ status = low_usb_bulk_write (devnum, command5_block, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+ /* Read it */
+ cmd_size = size_requested;
+ status = low_usb_bulk_read (devnum, buffer, &cmd_size);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+ for (i = 0; i < no_of_buffers; i++)
+ {
+ buffer_start = buffer + (i * 2500);
+ /* Reverse order of bytes in words of buffer */
+ for (j = 0; j < 2500; j = j + 2)
+ {
+ temp_byte = *(buffer_start + j);
+ *(buffer_start + j) = *(buffer_start + j + 1);
+ *(buffer_start + j + 1) = temp_byte;
+ }
+#ifdef DEEP_DEBUG
+ fwrite (buffer + (i * 2500), 2500, 1, img);
+#endif
+ if (low_is_home_line (dev, buffer_start))
+ {
+ home_line_count++;
+ if (home_line_count > 7)
+ in_home_region = SANE_TRUE;
+ }
+ if (in_home_region)
+ {
+ /* slow down scanning : on purpose backtracking */
+ if (home_line_count)
+ sleep (1);
+ free (buffer);
+#ifdef DEEP_DEBUG
+ fflush (img);
+ i = ftell (img) / 2500;
+ rewind (img);
+ DBG (2, "sanei_lexmark_low_search_home_bwd: offset=%d\n", i);
+ fprintf (img, "P5\n2500 %03d\n", i);
+ fclose (img);
+#endif
+ low_stop_mvmt (devnum);
+ DBG (2,
+ "sanei_lexmark_low_search_home_bwd: in home region, end.\n");
+ return SANE_TRUE;
+ }
+ }
+ } /* end while (buffer_count > buffer_limit); */
+ free (buffer);
+#ifdef DEEP_DEBUG
+ fflush (img);
+ i = ftell (img) / 2500;
+ rewind (img);
+ fprintf (img, "P5\n2500 %03d\n", i);
+ fclose (img);
+#endif
+ low_stop_mvmt (devnum);
+
+ DBG (2, "sanei_lexmark_low_search_home_bwd: end.\n");
+
+ return SANE_FALSE;
+}
+
+SANE_Status
+low_get_start_loc (SANE_Int resolution, SANE_Int * vert_start,
+ SANE_Int * hor_start, SANE_Int offset,
+ Lexmark_Device * dev)
+{
+ SANE_Int start_600;
+
+ switch (dev->model.sensor_type)
+ {
+ case X1100_2C_SENSOR:
+ case X1200_USB2_SENSOR:
+ case A920_SENSOR:
+ case X1200_SENSOR:
+ start_600 = 195 - offset;
+ *hor_start = 0x68;
+ break;
+ case X1100_B2_SENSOR:
+ start_600 = 195 - offset;
+ switch (resolution)
+ {
+ case 75:
+ *hor_start = 0x68;
+ break;
+ case 150:
+ *hor_start = 0x68;
+ break;
+ case 300:
+ *hor_start = 0x6a;
+ break;
+ case 600:
+ *hor_start = 0x6b;
+ break;
+ case 1200:
+ *hor_start = 0x6b;
+ break;
+ default:
+ /* If we're here we have an invalid resolution */
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case X74_SENSOR:
+ start_600 = 268 - offset;
+ switch (resolution)
+ {
+ case 75:
+ *hor_start = 0x48;
+ break;
+ case 150:
+ *hor_start = 0x48;
+ break;
+ case 300:
+ *hor_start = 0x4a;
+ break;
+ case 600:
+ *hor_start = 0x4b;
+ break;
+ default:
+ /* If we're here we have an invalid resolution */
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ }
+ /* Calculate vertical start distance at 600dpi */
+ switch (resolution)
+ {
+ case 75:
+ *vert_start = start_600 / 8;
+ break;
+ case 150:
+ *vert_start = start_600 / 4;
+ break;
+ case 300:
+ *vert_start = start_600 / 2;
+ break;
+ case 600:
+ *vert_start = start_600;
+ break;
+ case 1200:
+ *vert_start = start_600 * 2;
+ break;
+ default:
+ /* If we're here we have an invalid resolution */
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+low_set_scan_area (SANE_Int res,
+ SANE_Int tlx,
+ SANE_Int tly,
+ SANE_Int brx,
+ SANE_Int bry,
+ SANE_Int offset,
+ SANE_Bool half_step,
+ SANE_Byte * regs, Lexmark_Device * dev)
+{
+
+ SANE_Int vert_start = 0;
+ SANE_Int hor_start = 0;
+ SANE_Int vert_end;
+ SANE_Int hor_end;
+
+ low_get_start_loc (res, &vert_start, &hor_start, offset, dev);
+
+ /* convert pixel height to vertical location coordinates */
+ vert_end = vert_start + (bry * res) / 600;
+ vert_start += (tly * res) / 600;
+
+ /* scan area size : for A920, 600 color scans are done at 1200 y dpi */
+ /* this follow what was found in usb logs */
+ if (half_step)
+ {
+ vert_end = vert_end * 2;
+ vert_start = vert_start * 2;
+ }
+
+ /* set vertical start position registers */
+ regs[0x60] = LOBYTE (vert_start);
+ regs[0x61] = HIBYTE (vert_start);
+ /* set vertical end position registers */
+ regs[0x62] = LOBYTE (vert_end);
+ regs[0x63] = HIBYTE (vert_end);
+
+ /* convert pixel width to horizontal location coordinates */
+
+ hor_end = hor_start + brx;
+ hor_start += tlx;
+
+ regs[0x66] = LOBYTE (hor_start);
+ regs[0x67] = HIBYTE (hor_start);
+ /* set horizontal end position registers */
+ regs[0x6c] = LOBYTE (hor_end);
+ regs[0x6d] = HIBYTE (hor_end);
+
+ /* Debug */
+ DBG (2, "low_set_scan_area: vert_start: %d (tly=%d)\n", vert_start, tly);
+ DBG (2, "low_set_scan_area: vert_end: %d\n", vert_end);
+ DBG (2, "low_set_scan_area: hor_start: %d\n", hor_start);
+ DBG (2, "low_set_scan_area: hor_end: %d\n", hor_end);
+
+}
+
+SANE_Int
+sanei_lexmark_low_find_start_line (Lexmark_Device * dev)
+{
+ /*
+ This function scans forward 59 lines, reading 88 bytes per line from the
+ middle of the horizontal line: pixel 0xa84 to pixel 0x9d4. It scans with
+ the following parameters:
+ dir=fwd
+ mode=grayscale
+ h.res=300 dpi
+ v.res=600 dpi
+ hor. pixels = (0xa84 - 0x9d4)/2 = 0x58 = 88
+ vert. pixels = 0x3e - 0x03 = 0x3b = 59
+ data = 88x59=5192=0x1448
+
+ It assumes we are in the start dot, or just before it. We are reading
+ enough lines at 600dpi to read past the dot. We return the number of
+ entirely white lines read consecutively, so we know how far past the
+ dot we are.
+
+ To find the number of consecutive white lines we do the following:
+
+ Byte swap the order of the bytes in the buffer.
+
+ Go through the buffer finding low and high values, which are computed by
+ comparing to the weighted average:
+ weighted_average = (lowest value + (highest value - lowest value)/4)
+ Low bytes are changed to 0xFF (white), higher of equal bytes are changed
+ to 0x00 (black),so that the buffer only contains white (0xFF) or black
+ (0x00) values.
+
+ Next, we go through the buffer a line (88 bytes) at a time for 59 lines
+ to read the entire buffer. For each byte in a line we check if the
+ byte is black. If it is we increment the black byte counter.
+
+ After each line we check the black byte counter. If it is greater than 0
+ we increment the black line count and set the white line count to 0. If
+ there were no black bytes in the line we set the black line count to 0
+ and increment the white line count.
+
+ When all lines have been processed we return the white line count.
+ */
+
+
+ int blackLineCount = 0;
+ int whiteLineCount = 0;
+ int blackByteCounter = 0;
+ unsigned char max_byte = 0;
+ unsigned char min_byte = 0xFF;
+ unsigned char weighted_average;
+ int i, j;
+#ifdef DEEP_DEBUG
+ FILE *fdbg = NULL;
+#endif
+
+ SANE_Byte poll_result[3];
+ SANE_Byte *buffer;
+ SANE_Byte temp_byte;
+
+ static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };
+
+ static SANE_Byte command5_block[] = { 0x91, 0x00, 0x14, 0x48 };
+
+ size_t cmd_size;
+ SANE_Bool got_line;
+
+ DBG (2, "sanei_lexmark_low_find_start_line:\n");
+
+
+ /* set up registers */
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x2c] = 0x04;
+ dev->shadow_regs[0x2d] = 0x46;
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x0b;
+ dev->shadow_regs[0x37] = 0x0b;
+ dev->shadow_regs[0x38] = 0x11;
+ dev->shadow_regs[0x40] = 0x40;
+ rts88xx_set_gain (dev->shadow_regs, 6, 6, 6);
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0f;
+ dev->shadow_regs[0x2d] = 0x51;
+ dev->shadow_regs[0x34] = 0x0d;
+ dev->shadow_regs[0x35] = 0x0d;
+ dev->shadow_regs[0x36] = 0x1d;
+ dev->shadow_regs[0x37] = 0x1d;
+ dev->shadow_regs[0x38] = 0x29;
+ dev->shadow_regs[0x65] = 0x80;
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ rts88xx_set_gain (dev->shadow_regs, 6, 6, 6);
+ break;
+ case X1100_2C_SENSOR:
+ rts88xx_set_gain (dev->shadow_regs, 10, 10, 10);
+ dev->shadow_regs[0x28] = 0xf5;
+ dev->shadow_regs[0x29] = 0xf7;
+ dev->shadow_regs[0x2a] = 0xf5;
+ dev->shadow_regs[0x2b] = 0x17;
+
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x31] = 0x01;
+ dev->shadow_regs[0x34] = 0x11;
+ dev->shadow_regs[0x35] = 0x11;
+ dev->shadow_regs[0x36] = 0x21;
+ dev->shadow_regs[0x37] = 0x21;
+ dev->shadow_regs[0x38] = 0x31;
+ dev->shadow_regs[0x72] = 0x35;
+ dev->shadow_regs[0x74] = 0x4e;
+ dev->shadow_regs[0x85] = 0x02;
+ dev->shadow_regs[0x86] = 0x33;
+ dev->shadow_regs[0x87] = 0x0f;
+ dev->shadow_regs[0x88] = 0x24;
+ dev->shadow_regs[0x91] = 0x19;
+ dev->shadow_regs[0x92] = 0x20;
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ dev->shadow_regs[0x34] = 0x11;
+ dev->shadow_regs[0x35] = 0x11;
+ dev->shadow_regs[0x36] = 0x21;
+ dev->shadow_regs[0x37] = 0x21;
+ dev->shadow_regs[0x38] = 0x31;
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x88] = 0x44;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ rts88xx_set_gain (dev->shadow_regs, 6, 6, 6);
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x2c] = 0x01;
+ dev->shadow_regs[0x2d] = 0x03;
+ dev->shadow_regs[0x34] = 0x0d;
+ dev->shadow_regs[0x35] = 0x0d;
+ dev->shadow_regs[0x36] = 0x1d;
+ dev->shadow_regs[0x37] = 0x1d;
+ dev->shadow_regs[0x38] = 0x29;
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x00;
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+ dev->shadow_regs[0x92] = 0x00;
+ rts88xx_set_gain (dev->shadow_regs, 10, 10, 10);
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x01;
+ dev->shadow_regs[0x2d] = 0x03;
+ dev->shadow_regs[0x34] = 0x0d;
+ dev->shadow_regs[0x35] = 0x0d;
+ dev->shadow_regs[0x36] = 0x1d;
+ dev->shadow_regs[0x37] = 0x1d;
+ dev->shadow_regs[0x38] = 0x29;
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ rts88xx_set_gain (dev->shadow_regs, 10, 10, 10);
+ break;
+ }
+
+ /* set offset to a safe value */
+ rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80);
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x01;
+ /* set vertical start/end positions */
+ dev->shadow_regs[0x60] = 0x03;
+ dev->shadow_regs[0x61] = 0x00;
+ dev->shadow_regs[0x62] = 0x3e;
+ dev->shadow_regs[0x63] = 0x00;
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+ /* set horizontal start position */
+ dev->shadow_regs[0x66] = 0xd4; /* 0xf2 for X1200 */
+ dev->shadow_regs[0x67] = 0x09;
+ /* set horizontal end position */
+ dev->shadow_regs[0x6c] = 0x84; /* 0xa2 for X1200 */
+ dev->shadow_regs[0x6d] = 0x0a;
+ /* set horizontal resolution */
+ dev->shadow_regs[0x79] = 0x40;
+ dev->shadow_regs[0x7a] = 0x02;
+ /* set for ? */
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+
+
+
+
+
+ switch (dev->model.motor_type)
+ {
+ case X1100_MOTOR:
+ case A920_MOTOR:
+ /* set for ? */
+ dev->shadow_regs[0xc5] = 0x22;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ /* step size range2 */
+ dev->shadow_regs[0xc9] = 0x3b;
+ /* set for ? */
+ dev->shadow_regs[0xca] = 0x1f;
+ dev->shadow_regs[0xe0] = 0xf7;
+ dev->shadow_regs[0xe1] = 0x16;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x87;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x13;
+ dev->shadow_regs[0xe4] = 0x1b;
+ dev->shadow_regs[0xe5] = 0x16;
+ dev->shadow_regs[0xe6] = 0xdc;
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ dev->shadow_regs[0xe9] = 0x1b;
+ dev->shadow_regs[0xec] = 0x07;
+ dev->shadow_regs[0xef] = 0x03;
+ break;
+ case X74_MOTOR:
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x22;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0b;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x1f;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x2f;
+ dev->shadow_regs[0xe1] = 0x11;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x9f;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x0f;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0xcb;
+
+ dev->shadow_regs[0xe5] = 0x10;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x64;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x32;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x0c;
+ /* bounds of movement range4 -only for 75dpi grayscale */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ }
+
+
+ /* Stop the scanner */
+ low_stop_mvmt (dev->devnum);
+
+ /* write regs out twice */
+ dev->shadow_regs[0x32] = 0x00;
+ low_write_all_regs (dev->devnum, dev->shadow_regs);
+ dev->shadow_regs[0x32] = 0x40;
+ low_write_all_regs (dev->devnum, dev->shadow_regs);
+
+ /* Start Scan */
+ rts88xx_commit (dev->devnum, dev->shadow_regs[0x2c]);
+
+ /* Poll the available byte count until not 0 */
+ got_line = SANE_FALSE;
+ while (!got_line)
+ {
+ cmd_size = 4;
+ low_usb_bulk_write (dev->devnum, command4_block, &cmd_size);
+ cmd_size = 0x3;
+ low_usb_bulk_read (dev->devnum, poll_result, &cmd_size);
+ if (!
+ (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0))
+ {
+ /* if result != 00 00 00 we got data */
+ got_line = SANE_TRUE;
+ }
+#ifdef FAKE_USB
+ got_line = SANE_TRUE;
+#endif
+ }
+
+
+ /* create buffer for scan data */
+ buffer = calloc (5192, sizeof (char));
+ if (buffer == NULL)
+ {
+ return -1;
+ }
+
+ /* Tell the scanner to send the data */
+ /* Write: 91 00 14 48 */
+ cmd_size = 4;
+ low_usb_bulk_write (dev->devnum, command5_block, &cmd_size);
+ /* Read it */
+ cmd_size = 0x1448;
+ low_usb_bulk_read (dev->devnum, buffer, &cmd_size);
+
+ /* Stop the scanner */
+ low_stop_mvmt (dev->devnum);
+
+
+ /* Reverse order of bytes in words of buffer */
+ for (i = 0; i < 5192; i = i + 2)
+ {
+ temp_byte = *(buffer + i);
+ *(buffer + i) = *(buffer + i + 1);
+ *(buffer + i + 1) = temp_byte;
+ }
+
+#ifdef DEEP_DEBUG
+ fdbg = fopen ("find_start.pnm", "wb");
+ if (fdbg != NULL)
+ {
+ fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59);
+ fwrite (buffer, 5192, 1, fdbg);
+ fclose (fdbg);
+ }
+#endif
+
+ /* Find the max and the min */
+ for (i = 0; i < 5192; i++)
+ {
+ if (*(buffer + i) > max_byte)
+ max_byte = *(buffer + i);
+ if (*(buffer + i) < min_byte)
+ min_byte = *(buffer + i);
+ }
+
+ weighted_average = min_byte + ((max_byte - min_byte) / 4);
+
+ /* Set bytes as black (0x00) or white (0xFF) */
+ for (i = 0; i < 5192; i++)
+ {
+ if (*(buffer + i) > weighted_average)
+ *(buffer + i) = 0xFF;
+ else
+ *(buffer + i) = 0x00;
+ }
+
+#ifdef DEEP_DEBUG
+ fdbg = fopen ("find_start_after.pnm", "wb");
+ if (fdbg != NULL)
+ {
+ fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59);
+ fwrite (buffer, 5192, 1, fdbg);
+ fclose (fdbg);
+ }
+#endif
+
+ /* Go through 59 lines */
+ for (j = 0; j < 59; j++)
+ {
+ blackByteCounter = 0;
+ /* Go through 88 bytes per line */
+ for (i = 0; i < 88; i++)
+ {
+ /* Is byte black? */
+ if (*(buffer + (j * 88) + i) == 0)
+ {
+ blackByteCounter++;
+ }
+ } /* end for line */
+ if (blackByteCounter > 0)
+ {
+ /* This was a black line */
+ blackLineCount++;
+ whiteLineCount = 0;
+ }
+ else
+ {
+ /* This is a white line */
+ whiteLineCount++;
+ blackLineCount = 0;
+ }
+ } /* end for buffer */
+
+ free (buffer);
+ /* Stop the scanner.
+ This is needed to get the right distance to the scanning area */
+ if (dev->model.sensor_type == X74_SENSOR)
+ low_stop_mvmt (dev->devnum);
+
+ DBG (2, "sanei_lexmark_low_find_start_line: end.\n");
+ return whiteLineCount;
+}
+
+
+SANE_Status
+sanei_lexmark_low_set_scan_regs (Lexmark_Device * dev, SANE_Int resolution,
+ SANE_Int offset, SANE_Bool calibrated)
+{
+ SANE_Bool isColourScan;
+
+ DBG (2, "sanei_lexmark_low_set_scan_regs:\n");
+
+ DBG (7, "sanei_lexmark_low_set_scan_regs: resolution=%d DPI\n", resolution);
+
+ /* colour mode */
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ isColourScan = SANE_TRUE;
+ else
+ isColourScan = SANE_FALSE;
+
+ /* set up registers */
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x2c] = 0x03;
+ dev->shadow_regs[0x2d] = 0x45;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0f;
+ dev->shadow_regs[0x2d] = 0x51;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x2c] = 0x0d;
+ dev->shadow_regs[0x2d] = 0x4f;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x2c] = 0x01;
+ dev->shadow_regs[0x2d] = 0x03;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x2c] = 0x01;
+ dev->shadow_regs[0x2d] = 0x03;
+ break;
+ }
+
+ low_set_scan_area (resolution,
+ dev->val[OPT_TL_X].w,
+ dev->val[OPT_TL_Y].w,
+ dev->val[OPT_BR_X].w,
+ dev->val[OPT_BR_Y].w,
+ offset,
+ (dev->model.motor_type == A920_MOTOR
+ || dev->model.motor_type == X74_MOTOR) && isColourScan
+ && (resolution == 600), dev->shadow_regs, dev);
+
+ /* may be we could use a sensor descriptor that would held the max horiz dpi */
+ if (dev->val[OPT_RESOLUTION].w < 600)
+ dev->shadow_regs[0x7a] = 600 / dev->val[OPT_RESOLUTION].w;
+ else
+ dev->shadow_regs[0x7a] = 1;
+
+ /* 75dpi x 75dpi */
+ if (resolution == 75)
+ {
+ DBG (5, "sanei_lexmark_low_set_scan_regs(): 75 DPI resolution\n");
+
+ if (isColourScan)
+ {
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x36] = 0x03;
+ dev->shadow_regs[0x38] = 0x03;
+
+ dev->shadow_regs[0x79] = 0x08;
+
+ dev->shadow_regs[0x80] = 0x0d;
+ dev->shadow_regs[0x81] = 0x0e;
+ dev->shadow_regs[0x82] = 0x02;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;;
+
+ break;
+
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x36] = 0x05;
+ dev->shadow_regs[0x38] = 0x05;
+
+ dev->shadow_regs[0x80] = 0x0c;
+ dev->shadow_regs[0x81] = 0x0c;
+ dev->shadow_regs[0x82] = 0x09;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x8c;
+ dev->shadow_regs[0x92] = 0x40;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x03;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x38] = 0x03;
+
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x02;
+ dev->shadow_regs[0x82] = 0x03;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x38] = 0x03;
+
+ dev->shadow_regs[0x80] = 0x07;
+ dev->shadow_regs[0x81] = 0x0f;
+ dev->shadow_regs[0x82] = 0x03;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x44;
+
+ dev->shadow_regs[0x91] = 0x60;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x36] = 0x03;
+ dev->shadow_regs[0x38] = 0x01;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x80] = 0x08;
+ dev->shadow_regs[0x81] = 0x02;
+ dev->shadow_regs[0x82] = 0x0d;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x1e;
+ dev->shadow_regs[0x87] = 0x39;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ /* dev->shadow_regs[0x92] = 0x92; */
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x36] = 0x05;
+ dev->shadow_regs[0x38] = 0x04;
+
+ dev->shadow_regs[0x80] = 0x01;
+ dev->shadow_regs[0x81] = 0x0a;
+ dev->shadow_regs[0x82] = 0x0b;
+ break;
+ }
+
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ dev->shadow_regs[0xc2] = 0x80;
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x0c;
+ dev->shadow_regs[0xc6] = 0x0b;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x01;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x1b;
+ dev->shadow_regs[0xe1] = 0x0a;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x4f;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x01;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0xb3;
+
+ dev->shadow_regs[0xe5] = 0x09;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0d;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xe5;
+ dev->shadow_regs[0xe8] = 0x02;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0xa0;
+ dev->shadow_regs[0xeb] = 0x01;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* bounds of movement range4 */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x0a;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x2b;
+ dev->shadow_regs[0xe1] = 0x0a;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x7f;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x01;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0xbb;
+ dev->shadow_regs[0xe5] = 0x09;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0e;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x2b;
+ dev->shadow_regs[0xe8] = 0x03;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0xa0;
+ dev->shadow_regs[0xeb] = 0x01;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ }
+
+ /* set colour scan */
+ dev->shadow_regs[0x2f] = 0x11;
+
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x37] = 0x01;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x83;
+ }
+ else /* 75 dpi gray */
+ {
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x03;
+ dev->shadow_regs[0x39] = 0x0f;
+
+ dev->shadow_regs[0x40] = 0x80;
+
+ dev->shadow_regs[0x79] = 0x08;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x8c] = 0x02;
+ dev->shadow_regs[0x8d] = 0x01;
+ dev->shadow_regs[0x8e] = 0x60;
+ dev->shadow_regs[0x8f] = 0x80;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+
+ break;
+
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x35] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x37] = 0x04;
+ dev->shadow_regs[0x38] = 0x06;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x03; /* these are half of B2 sensor */
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x03;
+
+ dev->shadow_regs[0x85] = 0x0d;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x45;
+
+ dev->shadow_regs[0x91] = 0x60;
+ dev->shadow_regs[0x92] = 0x8d;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x02;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+
+ dev->shadow_regs[0x92] = 0x00;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x02;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+
+ dev->shadow_regs[0x92] = 0x00;
+ break;
+ }
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x0a;
+ dev->shadow_regs[0xc6] = 0x0b;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x01;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x07;
+ dev->shadow_regs[0xe1] = 0x18;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xe7;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x03;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0xe7;
+ dev->shadow_regs[0xe5] = 0x14;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x64;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xcb;
+ dev->shadow_regs[0xe8] = 0x08;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x32;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0xe3;
+ dev->shadow_regs[0xeb] = 0x04;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x0c;
+ /* bounds of movement range4 */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x10;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xca] = 0x01;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x4d;
+ dev->shadow_regs[0xe1] = 0x1c;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x71;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x02;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x6d;
+ dev->shadow_regs[0xe5] = 0x15;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0xdc;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xad;
+ dev->shadow_regs[0xe8] = 0x07;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x1b;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0xe1;
+ dev->shadow_regs[0xeb] = 0x03;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x07;
+ /* bounds of movement range4 -only for 75dpi grayscale */
+ dev->shadow_regs[0xed] = 0xc2;
+ dev->shadow_regs[0xee] = 0x02;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x03;
+ break;
+ }
+
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+
+ /* set ? only for colour? */
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x00;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+
+ }
+
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x0f;
+
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+
+ /* set horizontal resolution */
+ if (dev->model.sensor_type != X1200_SENSOR)
+ dev->shadow_regs[0x79] = 0x08;
+
+ }
+
+ /* 150dpi x 150dpi */
+ if (resolution == 150)
+ {
+ DBG (5, "sanei_lexmark_low_set_scan_regs(): 150 DPI resolution\n");
+
+ if (isColourScan)
+ {
+
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x34] = 0x08;
+ dev->shadow_regs[0x36] = 0x06;
+ dev->shadow_regs[0x38] = 0x05;
+ dev->shadow_regs[0x39] = 0x07;
+
+ /* resolution divisor */
+ dev->shadow_regs[0x79] = 0x08;
+
+ dev->shadow_regs[0x80] = 0x0a;
+ dev->shadow_regs[0x81] = 0x0c;
+ dev->shadow_regs[0x82] = 0x04;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x0b;
+ dev->shadow_regs[0x36] = 0x0b;
+ dev->shadow_regs[0x38] = 0x0a;
+
+ dev->shadow_regs[0x80] = 0x05;
+ dev->shadow_regs[0x81] = 0x05;
+ dev->shadow_regs[0x82] = 0x0a;
+
+ dev->shadow_regs[0x85] = 0x83;
+ dev->shadow_regs[0x86] = 0x7e;
+ dev->shadow_regs[0x87] = 0xad;
+ dev->shadow_regs[0x88] = 0x35;
+
+ dev->shadow_regs[0x91] = 0xfe;
+ dev->shadow_regs[0x92] = 0xdf;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x36] = 0x07;
+ dev->shadow_regs[0x38] = 0x05;
+
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x02;
+ dev->shadow_regs[0x82] = 0x06;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x03;
+ dev->shadow_regs[0x36] = 0x08;
+ dev->shadow_regs[0x38] = 0x05;
+
+ dev->shadow_regs[0x80] = 0x0e;
+ dev->shadow_regs[0x81] = 0x07;
+ dev->shadow_regs[0x82] = 0x02;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x04;
+
+ dev->shadow_regs[0x91] = 0xe0;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x36] = 0x05;
+ dev->shadow_regs[0x38] = 0x02;
+ /* data compression
+ dev->shadow_regs[0x40] = 0x90;
+ dev->shadow_regs[0x50] = 0x20; */
+ /* no data compression */
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x07;
+ dev->shadow_regs[0x82] = 0x0b;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x1e;
+ dev->shadow_regs[0x87] = 0x39;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x92] = 0x92;
+
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x36] = 0x05;
+ dev->shadow_regs[0x38] = 0x02;
+
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x07;
+ dev->shadow_regs[0x82] = 0x0b;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x1e;
+ dev->shadow_regs[0x87] = 0x39;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x92] = 0x92;
+ break;
+ } /* switch */
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ dev->shadow_regs[0xc2] = 0x80;
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+
+ dev->shadow_regs[0xc5] = 0x0e;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0b;
+ dev->shadow_regs[0xc7] = 0x00;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x03;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x41;
+ dev->shadow_regs[0xe1] = 0x09;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x89;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x02;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x0d;
+
+ dev->shadow_regs[0xe5] = 0x09;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0d;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xe8;
+ dev->shadow_regs[0xe8] = 0x02;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* bounds of movement range4 */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ case X1100_MOTOR:
+ case A920_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x0e;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3a;
+ dev->shadow_regs[0xca] = 0x03;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x61;
+ dev->shadow_regs[0xe1] = 0x0a;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xed;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x02;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x29;
+ dev->shadow_regs[0xe5] = 0x0a;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0e;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x29;
+ dev->shadow_regs[0xe8] = 0x03;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ }
+ /* set colour scan */
+ dev->shadow_regs[0x2f] = 0x11;
+
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x37] = 0x01;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x83;
+
+ } /* if (isColourScan) */
+ else
+ {
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x35] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x37] = 0x04;
+ dev->shadow_regs[0x38] = 0x06;
+ dev->shadow_regs[0x39] = 0x07;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0x40] = 0x40;
+
+ /* resolution divisor */
+ dev->shadow_regs[0x79] = 0x08;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x35] = 0x04;
+ dev->shadow_regs[0x36] = 0x07;
+ dev->shadow_regs[0x37] = 0x07;
+ dev->shadow_regs[0x38] = 0x0a;
+
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x35] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x37] = 0x04;
+ dev->shadow_regs[0x38] = 0x05;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x35] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x37] = 0x04;
+ dev->shadow_regs[0x38] = 0x05;
+
+ dev->shadow_regs[0x85] = 0x0d;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x45;
+
+ dev->shadow_regs[0x91] = 0x60;
+ dev->shadow_regs[0x92] = 0x8d;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x03;
+
+ /* dev->shadow_regs[0x40] = 0x90;
+ dev->shadow_regs[0x50] = 0x20; */
+ /* no data compression */
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+
+ dev->shadow_regs[0x92] = 0x92;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x01;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x02;
+ dev->shadow_regs[0x37] = 0x02;
+ dev->shadow_regs[0x38] = 0x03;
+
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x79] = 0x20;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+
+ dev->shadow_regs[0x92] = 0x92;
+ break;
+ } /* switch */
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x14;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0b;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x01;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x09;
+ dev->shadow_regs[0xe1] = 0x18;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xe9;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x03;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x79;
+
+ dev->shadow_regs[0xe5] = 0x16;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x64;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xcd;
+ dev->shadow_regs[0xe8] = 0x08;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x32;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0xe5;
+ dev->shadow_regs[0xeb] = 0x04;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x0c;
+ /* bounds of movement range4 */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ case X1100_MOTOR:
+ case A920_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x16;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xca] = 0x01;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0xdd;
+ dev->shadow_regs[0xe1] = 0x18;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x01;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x03;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x6d;
+ dev->shadow_regs[0xe5] = 0x15;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0xdc;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xad;
+ dev->shadow_regs[0xe8] = 0x07;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x1b;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0xe1;
+ dev->shadow_regs[0xeb] = 0x03;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x07;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x03;
+ break;
+ }
+
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x07;
+ /* set ? only for colour? */
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x00;
+
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+ } /* else (greyscale) */
+
+
+
+
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+
+ /* hum, horizontal resolution different for X1200 ? */
+ /* if (dev->model.sensor_type != X1200_SENSOR)
+ dev->shadow_regs[0x79] = 0x20; */
+
+ }
+
+ /*300dpi x 300dpi */
+ if (resolution == 300)
+ {
+ DBG (5, "sanei_lexmark_low_set_scan_regs(): 300 DPI resolution\n");
+
+ if (isColourScan)
+ {
+
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x34] = 0x08;
+ dev->shadow_regs[0x36] = 0x06;
+ dev->shadow_regs[0x38] = 0x05;
+ dev->shadow_regs[0x39] = 0x07;
+
+ dev->shadow_regs[0x80] = 0x08;
+ dev->shadow_regs[0x81] = 0x0a;
+ dev->shadow_regs[0x82] = 0x03;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x15;
+ dev->shadow_regs[0x36] = 0x15;
+ dev->shadow_regs[0x38] = 0x14;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+
+ dev->shadow_regs[0x80] = 0x0a;
+ dev->shadow_regs[0x81] = 0x0a;
+ dev->shadow_regs[0x82] = 0x06;
+
+ dev->shadow_regs[0x85] = 0x83;
+ dev->shadow_regs[0x86] = 0x7e;
+ dev->shadow_regs[0x87] = 0xad;
+ dev->shadow_regs[0x88] = 0x35;
+
+ dev->shadow_regs[0x91] = 0xfe;
+ dev->shadow_regs[0x92] = 0xdf;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x08;
+ dev->shadow_regs[0x36] = 0x0d;
+ dev->shadow_regs[0x38] = 0x09;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+
+ dev->shadow_regs[0x80] = 0x0e;
+ dev->shadow_regs[0x81] = 0x04;
+ dev->shadow_regs[0x82] = 0x0a;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x06;
+ dev->shadow_regs[0x36] = 0x10;
+ dev->shadow_regs[0x38] = 0x09;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+
+ dev->shadow_regs[0x80] = 0x0c;
+ dev->shadow_regs[0x81] = 0x02;
+ dev->shadow_regs[0x82] = 0x04;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x04;
+
+ dev->shadow_regs[0x91] = 0xe0;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x07;
+ dev->shadow_regs[0x36] = 0x09;
+ dev->shadow_regs[0x38] = 0x04;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+
+ /* data compression
+ dev->shadow_regs[0x40] = 0x90;
+ dev->shadow_regs[0x50] = 0x20; */
+ /* no data compression */
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x0e;
+ dev->shadow_regs[0x82] = 0x06;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x07;
+ dev->shadow_regs[0x36] = 0x09;
+ dev->shadow_regs[0x38] = 0x04;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x0e;
+ dev->shadow_regs[0x82] = 0x06;
+ break;
+ }
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x12;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x0f;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x5d;
+ dev->shadow_regs[0xe1] = 0x05;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xed;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x02;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x29;
+ dev->shadow_regs[0xe5] = 0x05;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0d;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* bounds of movement range4 -only for 75dpi grayscale */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x17;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3a;
+ dev->shadow_regs[0xca] = 0x0a;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x75;
+ dev->shadow_regs[0xe1] = 0x0a;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xdd;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x05;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x59;
+ dev->shadow_regs[0xe5] = 0x0a;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0e;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ }
+
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x37] = 0x01;
+
+ /* set colour scan */
+ dev->shadow_regs[0x2f] = 0x11;
+
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x83;
+
+ }
+ else /* greyscale */
+ {
+
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x35] = 0x04;
+ dev->shadow_regs[0x36] = 0x08;
+ dev->shadow_regs[0x37] = 0x08;
+ dev->shadow_regs[0x38] = 0x0c;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x08;
+ dev->shadow_regs[0x35] = 0x08;
+ dev->shadow_regs[0x36] = 0x0f;
+ dev->shadow_regs[0x37] = 0x0f;
+ dev->shadow_regs[0x38] = 0x16;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x04;
+ dev->shadow_regs[0x35] = 0x04;
+ dev->shadow_regs[0x36] = 0x07;
+ dev->shadow_regs[0x37] = 0x07;
+ dev->shadow_regs[0x38] = 0x0a;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x03;
+ dev->shadow_regs[0x35] = 0x03;
+ dev->shadow_regs[0x36] = 0x06;
+ dev->shadow_regs[0x37] = 0x06;
+ dev->shadow_regs[0x38] = 0x09;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x04;
+
+ dev->shadow_regs[0x91] = 0xe0;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x35] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x37] = 0x04;
+ dev->shadow_regs[0x38] = 0x06;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x02;
+ dev->shadow_regs[0x35] = 0x02;
+ dev->shadow_regs[0x36] = 0x04;
+ dev->shadow_regs[0x37] = 0x04;
+ dev->shadow_regs[0x38] = 0x06;
+ break;
+ }
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x1c;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0b;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x05;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x29;
+ dev->shadow_regs[0xe1] = 0x17;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x8f;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x06;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x61;
+
+ dev->shadow_regs[0xe5] = 0x16;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x64;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xb5;
+ dev->shadow_regs[0xe8] = 0x08;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x32;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x0c;
+ /* bounds of movement range4 -only for 75dpi grayscale */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x19;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3a;
+ dev->shadow_regs[0xca] = 0x08;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0xe3;
+ dev->shadow_regs[0xe1] = 0x18;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x03;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x06;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x2b;
+ dev->shadow_regs[0xe5] = 0x17;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0xdc;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0xb3;
+ dev->shadow_regs[0xe8] = 0x07;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x1b;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x07;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x03;
+ break;
+ } /* switch motortype */
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+
+ /* set ? only for colour? */
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x00;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+
+
+ } /* else (gray) */
+
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+ /* set horizontal resolution */
+ dev->shadow_regs[0x79] = 0x20;
+ }
+
+ /* 600dpi x 600dpi */
+ if (resolution == 600)
+ {
+ DBG (5, "sanei_lexmark_low_set_scan_regs(): 600 DPI resolution\n");
+
+
+
+ if (isColourScan)
+ {
+ /* 600 dpi color doesn't work for X74 yet */
+ if (dev->model.sensor_type == X74_SENSOR)
+ return SANE_STATUS_INVAL;
+
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x34] = 0x10;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x0c;
+ dev->shadow_regs[0x37] = 0x01;
+ dev->shadow_regs[0x38] = 0x09;
+
+ dev->shadow_regs[0x80] = 0x02;
+ dev->shadow_regs[0x81] = 0x08;
+ dev->shadow_regs[0x82] = 0x08;
+
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+
+ /*dev->shadow_regs[0x34] = 0x08;
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x36] = 0x06;
+ dev->shadow_regs[0x37] = 0x01;
+ dev->shadow_regs[0x38] = 0x05;
+
+
+ dev->shadow_regs[0x80] = 0x09;
+ dev->shadow_regs[0x81] = 0x0c;
+ dev->shadow_regs[0x82] = 0x04;
+
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break; */
+
+
+
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x15;
+ dev->shadow_regs[0x36] = 0x15;
+ dev->shadow_regs[0x38] = 0x14;
+
+ dev->shadow_regs[0x80] = 0x02;
+ dev->shadow_regs[0x81] = 0x02;
+ dev->shadow_regs[0x82] = 0x08;
+
+ dev->shadow_regs[0x85] = 0x83;
+ dev->shadow_regs[0x86] = 0x7e;
+ dev->shadow_regs[0x87] = 0xad;
+ dev->shadow_regs[0x88] = 0x35;
+
+ dev->shadow_regs[0x91] = 0xfe;
+ dev->shadow_regs[0x92] = 0xdf;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x08;
+ dev->shadow_regs[0x36] = 0x0d;
+ dev->shadow_regs[0x38] = 0x09;
+
+ dev->shadow_regs[0x80] = 0x0e;
+ dev->shadow_regs[0x81] = 0x02;
+ dev->shadow_regs[0x82] = 0x0a;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x06;
+ dev->shadow_regs[0x36] = 0x0f;
+ dev->shadow_regs[0x38] = 0x09;
+
+ dev->shadow_regs[0x79] = 0x40;
+
+ dev->shadow_regs[0x80] = 0x0e;
+ dev->shadow_regs[0x81] = 0x0e;
+ dev->shadow_regs[0x82] = 0x00;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x04;
+
+ dev->shadow_regs[0x91] = 0x60;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x07;
+ dev->shadow_regs[0x36] = 0x0a;
+ dev->shadow_regs[0x38] = 0x04;
+
+ /* data compression
+ dev->shadow_regs[0x40] = 0x90;
+ dev->shadow_regs[0x50] = 0x20; */
+
+ /* no data compression */
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x80] = 0x02;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x06;
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x0d;
+ dev->shadow_regs[0x36] = 0x13;
+ dev->shadow_regs[0x38] = 0x10;
+
+ dev->shadow_regs[0x80] = 0x04;
+ dev->shadow_regs[0x81] = 0x0e;
+ dev->shadow_regs[0x82] = 0x08;
+
+ dev->shadow_regs[0x85] = 0x02;
+ dev->shadow_regs[0x86] = 0x3b;
+ dev->shadow_regs[0x87] = 0x0f;
+ dev->shadow_regs[0x88] = 0x24;
+
+ dev->shadow_regs[0x91] = 0x19;
+ dev->shadow_regs[0x92] = 0x30;
+ dev->shadow_regs[0x93] = 0x0e;
+ dev->shadow_regs[0xc5] = 0x17;
+ dev->shadow_regs[0xc6] = 0x09;
+ dev->shadow_regs[0xca] = 0x0a;
+ break;
+ }
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x21;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x20;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x00;
+ dev->shadow_regs[0xe1] = 0x00;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xbf;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x05;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x00;
+ dev->shadow_regs[0xe5] = 0x00;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0d;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* bounds of movement range4 -only for 75dpi grayscale */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ case A920_MOTOR:
+ case X1100_MOTOR:
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x86;
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x27;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0c;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3a;
+ dev->shadow_regs[0xca] = 0x1a;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x57;
+ dev->shadow_regs[0xe1] = 0x0a;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xbf;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x05;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x3b;
+ dev->shadow_regs[0xe5] = 0x0a;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0e;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ break;
+ }
+ /* set colour scan */
+ dev->shadow_regs[0x2f] = 0x11;
+
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x37] = 0x01;
+
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x03;
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 2);
+
+
+ }
+ else
+ {
+ switch (dev->model.sensor_type)
+ {
+ case X74_SENSOR:
+ dev->shadow_regs[0x2c] = 0x04;
+ dev->shadow_regs[0x2d] = 0x46;
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x0b;
+ dev->shadow_regs[0x37] = 0x0b;
+ dev->shadow_regs[0x38] = 0x11;
+ dev->shadow_regs[0x40] = 0x40;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_B2_SENSOR:
+ dev->shadow_regs[0x34] = 0x11;
+ dev->shadow_regs[0x35] = 0x11;
+ dev->shadow_regs[0x36] = 0x21;
+ dev->shadow_regs[0x37] = 0x21;
+ dev->shadow_regs[0x38] = 0x31;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case X1100_2C_SENSOR:
+ dev->shadow_regs[0x34] = 0x07;
+ dev->shadow_regs[0x35] = 0x07;
+ dev->shadow_regs[0x36] = 0x0d;
+ dev->shadow_regs[0x37] = 0x0d;
+ dev->shadow_regs[0x38] = 0x13;
+
+ dev->shadow_regs[0x85] = 0x20;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ break;
+ case A920_SENSOR:
+ dev->shadow_regs[0x34] = 0x05;
+ dev->shadow_regs[0x35] = 0x05;
+ dev->shadow_regs[0x36] = 0x0b;
+ dev->shadow_regs[0x37] = 0x0b;
+ dev->shadow_regs[0x38] = 0x11;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x04;
+
+ dev->shadow_regs[0x91] = 0xe0;
+ dev->shadow_regs[0x92] = 0x85;
+ dev->shadow_regs[0x93] = 0x0e;
+ break;
+ case X1200_SENSOR:
+ dev->shadow_regs[0x34] = 0x03;
+ dev->shadow_regs[0x35] = 0x03;
+ dev->shadow_regs[0x36] = 0x07;
+ dev->shadow_regs[0x37] = 0x07;
+ dev->shadow_regs[0x38] = 0x0b;
+
+ /* data compression
+ dev->shadow_regs[0x40] = 0x90;
+ dev->shadow_regs[0x50] = 0x20; */
+ /* no data compression */
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+
+ dev->shadow_regs[0x92] = 0x00;
+
+ break;
+ case X1200_USB2_SENSOR:
+ dev->shadow_regs[0x34] = 0x03;
+ dev->shadow_regs[0x35] = 0x03;
+ dev->shadow_regs[0x36] = 0x07;
+ dev->shadow_regs[0x37] = 0x07;
+ dev->shadow_regs[0x38] = 0x0b;
+
+ dev->shadow_regs[0x40] = 0x80;
+ dev->shadow_regs[0x50] = 0x00;
+
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0xff;
+ dev->shadow_regs[0x88] = 0x02;
+
+ dev->shadow_regs[0x92] = 0x00;
+ break;
+ }
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+ /* ? */
+ dev->shadow_regs[0xc4] = 0x20;
+ dev->shadow_regs[0xc5] = 0x22;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0b;
+
+ dev->shadow_regs[0xc8] = 0x04;
+ dev->shadow_regs[0xc9] = 0x39;
+ dev->shadow_regs[0xca] = 0x1f;
+
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x2f;
+ dev->shadow_regs[0xe1] = 0x11;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x9f;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x0f;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0xcb;
+
+ dev->shadow_regs[0xe5] = 0x10;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x64;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x32;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x0c;
+ /* bounds of movement range4 -only for 75dpi grayscale */
+ dev->shadow_regs[0xed] = 0x00;
+ dev->shadow_regs[0xee] = 0x00;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x08;
+ break;
+ case X1100_MOTOR:
+ case A920_MOTOR:
+ /* set ? only for colour? */
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x00;
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x22;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3b;
+ dev->shadow_regs[0xca] = 0x1f;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0xf7;
+ dev->shadow_regs[0xe1] = 0x16;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x87;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x13;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x1b;
+ dev->shadow_regs[0xe5] = 0x16;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0xdc;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x1b;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x07;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x03;
+ break;
+ }
+
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x01;
+
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 1);
+
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+ } /* else (grayscale) */
+
+ /* set horizontal resolution */
+ dev->shadow_regs[0x79] = 0x40;
+
+ }
+ /*600dpi x 1200dpi */
+ if (resolution == 1200)
+ {
+ DBG (5, "sanei_lexmark_low_set_scan_regs(): 1200 DPI resolution\n");
+
+ /* 1200 dpi doesn't work for X74 yet */
+ if (dev->model.sensor_type == X74_SENSOR)
+ return SANE_STATUS_INVAL;
+
+ if (isColourScan)
+ {
+ /* set colour scan */
+ dev->shadow_regs[0x2f] = 0x11;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x01;
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 2);
+
+ if (dev->model.sensor_type == X1100_B2_SENSOR)
+ {
+ /* set ? */
+ dev->shadow_regs[0x34] = 0x29;
+ dev->shadow_regs[0x36] = 0x29;
+ dev->shadow_regs[0x38] = 0x28;
+ /* set ? */
+ dev->shadow_regs[0x80] = 0x04;
+ dev->shadow_regs[0x81] = 0x04;
+ dev->shadow_regs[0x82] = 0x08;
+ dev->shadow_regs[0x85] = 0x83;
+ dev->shadow_regs[0x86] = 0x7e;
+ dev->shadow_regs[0x87] = 0xad;
+ dev->shadow_regs[0x88] = 0x35;
+ dev->shadow_regs[0x91] = 0xfe;
+ dev->shadow_regs[0x92] = 0xdf;
+ }
+ else
+ { /* A920 case */
+ dev->shadow_regs[0x34] = 0x0c;
+ dev->shadow_regs[0x36] = 0x1e;
+ dev->shadow_regs[0x38] = 0x10;
+
+ dev->shadow_regs[0x80] = 0x0c;
+ dev->shadow_regs[0x81] = 0x08;
+ dev->shadow_regs[0x82] = 0x0c;
+
+ dev->shadow_regs[0x85] = 0x05;
+ dev->shadow_regs[0x86] = 0x14;
+ dev->shadow_regs[0x87] = 0x06;
+ dev->shadow_regs[0x88] = 0x04;
+
+ dev->shadow_regs[0x91] = 0x60;
+ dev->shadow_regs[0x92] = 0x85;
+ }
+
+ dev->shadow_regs[0x35] = 0x01;
+ dev->shadow_regs[0x37] = 0x01;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x01;
+ dev->shadow_regs[0x93] = 0x0e;
+
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x86;
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x41;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x0c;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3a;
+ dev->shadow_regs[0xca] = 0x40;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x00;
+ dev->shadow_regs[0xe1] = 0x00;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0x85;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x0b;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x00;
+ dev->shadow_regs[0xe5] = 0x00;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0x0e;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x05;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x01;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x01;
+ }
+ else
+ {
+ /* set grayscale scan */
+ dev->shadow_regs[0x2f] = 0x21;
+ /* set ? */
+ dev->shadow_regs[0x34] = 0x22;
+ dev->shadow_regs[0x35] = 0x22;
+ dev->shadow_regs[0x36] = 0x42;
+ dev->shadow_regs[0x37] = 0x42;
+ dev->shadow_regs[0x38] = 0x62;
+ /* set motor resolution divisor */
+ dev->shadow_regs[0x39] = 0x01;
+ /* set # of head moves per CIS read */
+ rts88xx_set_scan_frequency (dev->shadow_regs, 0);
+
+ /* set ? only for colour? */
+ dev->shadow_regs[0x80] = 0x00;
+ dev->shadow_regs[0x81] = 0x00;
+ dev->shadow_regs[0x82] = 0x00;
+ dev->shadow_regs[0x85] = 0x00;
+ dev->shadow_regs[0x86] = 0x00;
+ dev->shadow_regs[0x87] = 0x00;
+ dev->shadow_regs[0x88] = 0x00;
+ dev->shadow_regs[0x91] = 0x00;
+ dev->shadow_regs[0x92] = 0x00;
+ dev->shadow_regs[0x93] = 0x06;
+ /* Motor enable & Coordinate space denominator */
+ dev->shadow_regs[0xc3] = 0x81;
+ /* ? */
+ dev->shadow_regs[0xc5] = 0x41;
+ /* Movement direction & step size */
+ dev->shadow_regs[0xc6] = 0x09;
+ /* ? */
+ dev->shadow_regs[0xc9] = 0x3a;
+ dev->shadow_regs[0xca] = 0x40;
+ /* bounds of movement range0 */
+ dev->shadow_regs[0xe0] = 0x00;
+ dev->shadow_regs[0xe1] = 0x00;
+ /* step size range0 */
+ dev->shadow_regs[0xe2] = 0xc7;
+ /* ? */
+ dev->shadow_regs[0xe3] = 0x29;
+ /* bounds of movement range1 */
+ dev->shadow_regs[0xe4] = 0x00;
+ dev->shadow_regs[0xe5] = 0x00;
+ /* step size range1 */
+ dev->shadow_regs[0xe6] = 0xdc;
+ /* bounds of movement range2 */
+ dev->shadow_regs[0xe7] = 0x00;
+ dev->shadow_regs[0xe8] = 0x00;
+ /* step size range2 */
+ dev->shadow_regs[0xe9] = 0x1b;
+ /* bounds of movement range3 */
+ dev->shadow_regs[0xea] = 0x00;
+ dev->shadow_regs[0xeb] = 0x00;
+ /* step size range3 */
+ dev->shadow_regs[0xec] = 0x07;
+ /* step size range4 */
+ dev->shadow_regs[0xef] = 0x03;
+ }
+
+ /* set horizontal resolution */
+ dev->shadow_regs[0x79] = 0x40;
+ }
+
+ /* is calibration has been done, we override fixed settings with detected ones */
+ if (calibrated)
+ {
+ /* override fixed values with ones from calibration */
+ if (rts88xx_is_color (dev->shadow_regs))
+ {
+ rts88xx_set_offset (dev->shadow_regs,
+ dev->offset.red,
+ dev->offset.green, dev->offset.blue);
+ rts88xx_set_gain (dev->shadow_regs,
+ dev->gain.red, dev->gain.green, dev->gain.blue);
+ }
+ else
+ {
+ rts88xx_set_offset (dev->shadow_regs,
+ dev->offset.gray,
+ dev->offset.gray, dev->offset.gray);
+ rts88xx_set_gain (dev->shadow_regs,
+ dev->gain.gray, dev->gain.gray, dev->gain.gray);
+ }
+ }
+ DBG (2, "sanei_lexmark_low_set_scan_regs: end.\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_lexmark_low_start_scan (Lexmark_Device * dev)
+{
+ SANE_Int devnum;
+
+ static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };
+
+ static SANE_Byte command5_block[] = { 0x80, 0xb3, 0x00, 0x01 };
+
+ SANE_Byte poll_result[3];
+ SANE_Byte read_result;
+ SANE_Bool scan_head_moving;
+ size_t size;
+
+ devnum = dev->devnum;
+
+ dev->transfer_buffer = NULL; /* No data xferred yet */
+ DBG (2, "sanei_lexmark_low_start_scan:\n");
+
+
+ /* 80 b3 00 01 - poll for scanner not moving */
+ scan_head_moving = SANE_TRUE;
+ while (scan_head_moving)
+ {
+ size = 4;
+ low_usb_bulk_write (devnum, command5_block, &size);
+ size = 0x1;
+ low_usb_bulk_read (devnum, &read_result, &size);
+ if ((read_result & 0xF) == 0x0)
+ {
+ scan_head_moving = SANE_FALSE;
+ }
+ /* F.O. Should be a timeout here so we don't hang if something breaks */
+#ifdef FAKE_USB
+ scan_head_moving = SANE_FALSE;
+#endif
+ }
+
+ /* Clear C6 */
+ low_clr_c6 (devnum);
+ /* Stop the scanner */
+ low_stop_mvmt (devnum);
+
+ /*Set regs x2 */
+ dev->shadow_regs[0x32] = 0x00;
+ low_write_all_regs (devnum, dev->shadow_regs);
+ dev->shadow_regs[0x32] = 0x40;
+ low_write_all_regs (devnum, dev->shadow_regs);
+
+ /* Start Scan */
+ rts88xx_commit (devnum, dev->shadow_regs[0x2c]);
+
+ /* We start with 0 bytes remaining to be read */
+ dev->bytes_remaining = 0;
+ /* and 0 bytes in the transfer buffer */
+ dev->bytes_in_buffer = 0;
+ dev->bytes_read = 0;
+
+ /* Poll the available byte count until not 0 */
+ while (1)
+ {
+ size = 4;
+ low_usb_bulk_write (devnum, command4_block, &size);
+ size = 0x3;
+ low_usb_bulk_read (devnum, poll_result, &size);
+ if (!
+ (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0))
+ {
+ /* if result != 00 00 00 we got data */
+
+ /* data_size should be used to set bytes_remaining */
+ /* data_size is set from sane_get_parameters () */
+ dev->bytes_remaining = dev->data_size;
+ /* Initialize the read buffer */
+ read_buffer_init (dev, dev->params.bytes_per_line);
+ return SANE_STATUS_GOOD;
+
+ }
+ size = 4;
+ /* I'm not sure why the Windows driver does this - probably a timeout? */
+ low_usb_bulk_write (devnum, command5_block, &size);
+ size = 0x1;
+ low_usb_bulk_read (devnum, &read_result, &size);
+ if (read_result != 0x68)
+ {
+ dev->bytes_remaining = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ DBG (2, "sanei_lexmark_low_start_scan: end.\n");
+ return SANE_STATUS_GOOD;
+}
+
+long
+sanei_lexmark_low_read_scan_data (SANE_Byte * data, SANE_Int size,
+ Lexmark_Device * dev)
+{
+ SANE_Bool isColourScan, isGrayScan;
+ static SANE_Byte command1_block[] = { 0x91, 0x00, 0xff, 0xc0 };
+ size_t cmd_size, xfer_request;
+ long bytes_read;
+ SANE_Bool even_byte;
+ SANE_Status status;
+ int i, k, val;
+
+ DBG (2, "sanei_lexmark_low_read_scan_data:\n");
+
+ /* colour mode */
+ isGrayScan = SANE_FALSE;
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ isColourScan = SANE_TRUE;
+ else
+ {
+ isColourScan = SANE_FALSE;
+ /* grayscale mode */
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ isGrayScan = SANE_TRUE;
+ }
+
+ /* Check if we have a transfer buffer. Create one and fill it if we don't */
+ if (dev->transfer_buffer == NULL)
+ {
+ if (dev->bytes_remaining > 0)
+ {
+ if (dev->bytes_remaining > MAX_XFER_SIZE)
+ xfer_request = MAX_XFER_SIZE;
+ else
+ xfer_request = dev->bytes_remaining;
+
+ command1_block[2] = (SANE_Byte) (xfer_request >> 8);
+ command1_block[3] = (SANE_Byte) (xfer_request & 0xFF);
+
+ /* wait for data */
+ status = low_poll_data (dev->devnum);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_lexmark_low_read_scan_data: time-out while waiting for data.\n");
+ return status;
+ }
+
+ /* Create buffer to hold the amount we will request */
+ dev->transfer_buffer = (SANE_Byte *) malloc (MAX_XFER_SIZE);
+ if (dev->transfer_buffer == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* Fill it */
+ /* Write: 91 00 (xfer_size) */
+ cmd_size = 4;
+ low_usb_bulk_write (dev->devnum, command1_block, &cmd_size);
+
+ /* Read: xfer_size bytes */
+ cmd_size = xfer_request;
+ low_usb_bulk_read (dev->devnum, dev->transfer_buffer, &cmd_size);
+
+ /* apply shading coefficients */
+ k = dev->bytes_read % dev->read_buffer->linesize;
+ for (i = 0; i < (int) cmd_size; i++)
+ {
+ val = dev->transfer_buffer[i];
+ val = (int) ((float) val * dev->shading_coeff[k] + 0.5);
+ if (val > 255)
+ val = 255;
+ dev->transfer_buffer[i] = val;
+ k++;
+ if ((size_t) k == dev->read_buffer->linesize)
+ k = 0;
+ }
+
+ /* advance by the amount actually read from device */
+ dev->bytes_read += cmd_size;
+ dev->bytes_remaining -= cmd_size;
+ dev->bytes_in_buffer = cmd_size;
+ dev->read_pointer = dev->transfer_buffer;
+ DBG (2, "sanei_lexmark_low_read_scan_data:\n");
+ DBG (2, " Filled a buffer from the scanner\n");
+ DBG (2, " bytes_remaining: %lu\n", (u_long) dev->bytes_remaining);
+ DBG (2, " bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer);
+ DBG (2, " read_pointer: %p\n", dev->read_pointer);
+ }
+ }
+
+ DBG (5, "READ BUFFER INFO: \n");
+ DBG (5, " write ptr: %p\n", dev->read_buffer->writeptr);
+ DBG (5, " read ptr: %p\n", dev->read_buffer->readptr);
+ DBG (5, " max write ptr: %p\n", dev->read_buffer->max_writeptr);
+ DBG (5, " buffer size: %lu\n", (u_long) dev->read_buffer->size);
+ DBG (5, " line size: %lu\n", (u_long) dev->read_buffer->linesize);
+ DBG (5, " empty: %d\n", dev->read_buffer->empty);
+ DBG (5, " line no: %d\n", dev->read_buffer->image_line_no);
+
+
+ /* If there is space in the read buffer, copy the transfer buffer over */
+ if (read_buffer_bytes_available (dev->read_buffer) >= dev->bytes_in_buffer)
+ {
+ even_byte = SANE_TRUE;
+ while (dev->bytes_in_buffer)
+ {
+
+ /* Colour Scan */
+ if (isColourScan)
+ {
+ if (even_byte)
+ read_buffer_add_byte (dev->read_buffer,
+ dev->read_pointer + 1);
+ else
+ read_buffer_add_byte (dev->read_buffer,
+ dev->read_pointer - 1);
+ even_byte = !even_byte;
+ }
+ /* Gray Scan */
+ else if (isGrayScan)
+ {
+ if (even_byte)
+ read_buffer_add_byte_gray (dev->read_buffer,
+ dev->read_pointer + 1);
+ else
+ read_buffer_add_byte_gray (dev->read_buffer,
+ dev->read_pointer - 1);
+ even_byte = !even_byte;
+ }
+ /* Lineart Scan */
+ else
+ {
+ if (even_byte)
+ read_buffer_add_bit_lineart (dev->read_buffer,
+ dev->read_pointer + 1,
+ dev->threshold);
+ else
+ read_buffer_add_bit_lineart (dev->read_buffer,
+ dev->read_pointer - 1,
+ dev->threshold);
+ even_byte = !even_byte;
+ }
+ dev->read_pointer = dev->read_pointer + sizeof (SANE_Byte);
+ dev->bytes_in_buffer--;
+ }
+ /* free the transfer buffer */
+ free (dev->transfer_buffer);
+ dev->transfer_buffer = NULL;
+ }
+
+ DBG (5, "READ BUFFER INFO: \n");
+ DBG (5, " write ptr: %p\n", dev->read_buffer->writeptr);
+ DBG (5, " read ptr: %p\n", dev->read_buffer->readptr);
+ DBG (5, " max write ptr: %p\n", dev->read_buffer->max_writeptr);
+ DBG (5, " buffer size: %lu\n", (u_long) dev->read_buffer->size);
+ DBG (5, " line size: %lu\n", (u_long) dev->read_buffer->linesize);
+ DBG (5, " empty: %d\n", dev->read_buffer->empty);
+ DBG (5, " line no: %d\n", dev->read_buffer->image_line_no);
+
+ /* Read blocks out of read buffer */
+ bytes_read = read_buffer_get_bytes (dev->read_buffer, data, size);
+
+ DBG (2, "sanei_lexmark_low_read_scan_data:\n");
+ DBG (2, " Copying lines from buffer to data\n");
+ DBG (2, " bytes_remaining: %lu\n", (u_long) dev->bytes_remaining);
+ DBG (2, " bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer);
+ DBG (2, " read_pointer: %p\n", dev->read_buffer->readptr);
+ DBG (2, " bytes_read %lu\n", (u_long) bytes_read);
+
+ /* if no more bytes to xfer and read buffer empty we're at the end */
+ if ((dev->bytes_remaining == 0) && read_buffer_is_empty (dev->read_buffer))
+ {
+ if (!dev->eof)
+ {
+ DBG (2,
+ "sanei_lexmark_low_read_scan_data: EOF- parking the scanner\n");
+ dev->eof = SANE_TRUE;
+ low_rewind (dev, dev->shadow_regs);
+ }
+ else
+ {
+ DBG (2, "ERROR: Why are we trying to set eof more than once?\n");
+ }
+ }
+
+ DBG (2, "sanei_lexmark_low_read_scan_data: end.\n");
+ return bytes_read;
+}
+
+void
+low_rewind (Lexmark_Device * dev, SANE_Byte * regs)
+{
+ SANE_Int new_location;
+ SANE_Int location;
+ SANE_Int scale;
+
+ DBG (2, "low_rewind: \n");
+
+ /* We rewind at 1200dpi resolution. We rely on content of shadow registers
+ to compute the number of lines at 1200 dpi to go back */
+
+ /* first move to start of scanning area */
+ scale = 600 / dev->val[OPT_RESOLUTION].w;
+ new_location = ((dev->val[OPT_BR_Y].w / scale) * scale) * 2;
+
+ /* then add distance to go to the "origin dot" */
+ if (rts88xx_is_color (regs))
+ new_location += 400;
+ else
+ new_location += 420;
+
+ if (dev->model.sensor_type == X74_SENSOR)
+ new_location += 150;
+
+
+ location = new_location - 1;
+ DBG (2, "low_rewind: %d=>new_location=%d\n", dev->val[OPT_BR_Y].w,
+ new_location);
+
+ /* stops any pending scan */
+ low_clr_c6 (dev->devnum);
+ low_cancel (dev->devnum);
+
+ /* set regs for rewind */
+ regs[0x2f] = 0xa1;
+ regs[0x32] = 0x00;
+ regs[0x39] = 0x00;
+
+ /* all other regs are always the same. these ones change with parameters */
+ /* the following 4 regs are the location 61,60 and the location+1 63,62 */
+
+ regs[0x60] = LOBYTE (location);
+ regs[0x61] = HIBYTE (location);
+ regs[0x62] = LOBYTE (new_location);
+ regs[0x63] = HIBYTE (new_location);
+
+ switch (dev->model.motor_type)
+ {
+ case X74_MOTOR:
+ regs[0xc3] = 0x81;
+ regs[0xc6] = 0x03;
+ regs[0xc9] = 0x39;
+ regs[0xe0] = 0x81;
+ regs[0xe1] = 0x16;
+ regs[0xe2] = 0xe1;
+ regs[0xe3] = 0x04;
+ regs[0xe4] = 0xe7;
+ regs[0xe5] = 0x14;
+ regs[0xe6] = 0x64;
+ regs[0xe7] = 0xd5;
+ regs[0xe8] = 0x08;
+ regs[0xe9] = 0x32;
+ regs[0xea] = 0xed;
+ regs[0xeb] = 0x04;
+ regs[0xec] = 0x0c;
+ regs[0xef] = 0x08;
+ break;
+ case X1100_MOTOR:
+ case A920_MOTOR:
+ /* set regs for rewind */
+ regs[0x79] = 0x40;
+ regs[0xb2] = 0x04;
+ regs[0xc3] = 0x81;
+ regs[0xc6] = 0x01;
+ regs[0xc9] = 0x3b;
+ regs[0xe0] = 0x2b;
+ regs[0xe1] = 0x17;
+ regs[0xe2] = 0xe7;
+ regs[0xe3] = 0x03;
+ regs[0xe6] = 0xdc;
+ regs[0xe7] = 0xb3;
+ regs[0xe8] = 0x07;
+ regs[0xe9] = 0x1b;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x07;
+ regs[0xef] = 0x03;
+ break;
+ }
+
+
+ /* starts scan */
+ low_start_scan (dev->devnum, regs);
+ DBG (2, "low_rewind: end.\n");
+}
+
+
+SANE_Status
+read_buffer_init (Lexmark_Device * dev, int bytesperline)
+{
+ size_t no_lines_in_buffer;
+
+ DBG (2, "read_buffer_init: Start\n");
+
+ dev->read_buffer = (Read_Buffer *) malloc (sizeof (Read_Buffer));
+ if (dev->read_buffer == NULL)
+ return SANE_STATUS_NO_MEM;
+ dev->read_buffer->linesize = bytesperline;
+ dev->read_buffer->gray_offset = 0;
+ dev->read_buffer->max_gray_offset = bytesperline - 1;
+ dev->read_buffer->region = RED;
+ dev->read_buffer->red_offset = 0;
+ dev->read_buffer->green_offset = 1;
+ dev->read_buffer->blue_offset = 2;
+ dev->read_buffer->max_red_offset = bytesperline - 3;
+ dev->read_buffer->max_green_offset = bytesperline - 2;
+ dev->read_buffer->max_blue_offset = bytesperline - 1;
+ no_lines_in_buffer = 3 * MAX_XFER_SIZE / bytesperline;
+ dev->read_buffer->size = bytesperline * no_lines_in_buffer;
+ dev->read_buffer->data = (SANE_Byte *) malloc (dev->read_buffer->size);
+ if (dev->read_buffer->data == NULL)
+ return SANE_STATUS_NO_MEM;
+ dev->read_buffer->readptr = dev->read_buffer->data;
+ dev->read_buffer->writeptr = dev->read_buffer->data;
+ dev->read_buffer->max_writeptr = dev->read_buffer->data +
+ (no_lines_in_buffer - 1) * bytesperline;
+ dev->read_buffer->empty = SANE_TRUE;
+ dev->read_buffer->image_line_no = 0;
+ dev->read_buffer->bit_counter = 0;
+ dev->read_buffer->max_lineart_offset = dev->params.pixels_per_line - 1;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+read_buffer_free (Read_Buffer * read_buffer)
+{
+ DBG (2, "read_buffer_free:\n");
+ if (read_buffer)
+ {
+ free (read_buffer->data);
+ free (read_buffer);
+ read_buffer = NULL;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+size_t
+read_buffer_bytes_available (Read_Buffer * rb)
+{
+
+ DBG (2, "read_buffer_bytes_available:\n");
+
+ if (rb->empty)
+ return rb->size;
+ else if ((size_t) abs (rb->writeptr - rb->readptr) < rb->linesize)
+ return 0; /* ptrs are less than one line apart */
+ else if (rb->writeptr < rb->readptr)
+ return (rb->readptr - rb->writeptr - rb->linesize);
+ else
+ return (rb->size + rb->readptr - rb->writeptr - rb->linesize);
+}
+
+SANE_Status
+read_buffer_add_byte (Read_Buffer * rb, SANE_Byte * byte_pointer)
+{
+
+ /* DBG(2, "read_buffer_add_byte:\n"); */
+ /* F.O. Need to fix the endian byte ordering here */
+
+ switch (rb->region)
+ {
+ case RED:
+ *(rb->writeptr + rb->red_offset) = *byte_pointer;
+ if (rb->red_offset == rb->max_red_offset)
+ {
+ rb->red_offset = 0;
+ rb->region = GREEN;
+ }
+ else
+ rb->red_offset = rb->red_offset + (3 * sizeof (SANE_Byte));
+ return SANE_STATUS_GOOD;
+ case GREEN:
+ *(rb->writeptr + rb->green_offset) = *byte_pointer;
+ if (rb->green_offset == rb->max_green_offset)
+ {
+ rb->green_offset = 1;
+ rb->region = BLUE;
+ }
+ else
+ rb->green_offset = rb->green_offset + (3 * sizeof (SANE_Byte));
+ return SANE_STATUS_GOOD;
+ case BLUE:
+ *(rb->writeptr + rb->blue_offset) = *byte_pointer;
+ if (rb->blue_offset == rb->max_blue_offset)
+ {
+ rb->image_line_no++;
+ /* finished a line. read_buffer no longer empty */
+ rb->empty = SANE_FALSE;
+ rb->blue_offset = 2;
+ rb->region = RED;
+ if (rb->writeptr == rb->max_writeptr)
+ rb->writeptr = rb->data; /* back to beginning of buffer */
+ else
+ rb->writeptr = rb->writeptr + rb->linesize; /* next line */
+ }
+ else
+ rb->blue_offset = rb->blue_offset + (3 * sizeof (SANE_Byte));
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+read_buffer_add_byte_gray (Read_Buffer * rb, SANE_Byte * byte_pointer)
+{
+
+ /* DBG(2, "read_buffer_add_byte_gray:\n"); */
+
+ *(rb->writeptr + rb->gray_offset) = *byte_pointer;
+
+ if (rb->gray_offset == rb->max_gray_offset)
+ {
+ rb->image_line_no++;
+ /* finished a line. read_buffer no longer empty */
+ rb->empty = SANE_FALSE;
+ rb->gray_offset = 0;
+
+ if (rb->writeptr == rb->max_writeptr)
+ rb->writeptr = rb->data; /* back to beginning of buffer */
+ else
+ rb->writeptr = rb->writeptr + rb->linesize; /* next line */
+ }
+ else
+ rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+read_buffer_add_bit_lineart (Read_Buffer * rb, SANE_Byte * byte_pointer,
+ SANE_Byte threshold)
+{
+ SANE_Byte tmpByte;
+ SANE_Byte *currentBytePtr;
+ SANE_Int bitIndex;
+
+ /* DBG(2, "read_buffer_add_bit_lineart:\n"); */
+
+ /* threshold = 0x80; */
+ tmpByte = 0;
+ /* Create a bit by comparing incoming byte to threshold */
+ if (*byte_pointer <= threshold)
+ {
+ tmpByte = 128;
+ }
+
+ /* Calculate the bit index in the current byte */
+ bitIndex = rb->bit_counter % 8;
+ /* Move the bit to its correct position in the temporary byte */
+ tmpByte = tmpByte >> bitIndex;
+ /* Get the pointer to the current byte */
+ currentBytePtr = rb->writeptr + rb->gray_offset;
+
+ /* If this is the first write to this byte, clear the byte */
+ if (bitIndex == 0)
+ *currentBytePtr = 0;
+ /* Set the value of the bit in the current byte */
+ *currentBytePtr = *currentBytePtr | tmpByte;
+
+ /* last bit in the line? */
+ if (rb->bit_counter == rb->max_lineart_offset)
+ {
+ /* Check if we're at the last byte of the line - error if not */
+ if (rb->gray_offset != rb->max_gray_offset)
+ {
+ DBG (5, "read_buffer_add_bit_lineart:\n");
+ DBG (5, " Last bit of line is not last byte.\n");
+ DBG (5, " Bit Index: %d, Byte Index: %d. \n", rb->bit_counter,
+ rb->max_gray_offset);
+ return SANE_STATUS_INVAL;
+ }
+ rb->image_line_no++;
+ /* line finished read_buffer no longer empty */
+ rb->empty = SANE_FALSE;
+ rb->gray_offset = 0;
+ /* are we at the last line in the read buffer ? */
+ if (rb->writeptr == rb->max_writeptr)
+ rb->writeptr = rb->data; /* back to beginning of buffer */
+ else
+ rb->writeptr = rb->writeptr + rb->linesize; /* next line */
+ /* clear the bit counter */
+ rb->bit_counter = 0;
+ }
+ /* last bit in the byte? */
+ else if (bitIndex == 7)
+ {
+ /* Not at the end of the line, but byte done. Increment byte offset */
+ rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte));
+ /* increment bit counter */
+ rb->bit_counter++;
+ }
+ else
+ {
+ /* else increment bit counter */
+ rb->bit_counter++;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+size_t
+read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer, size_t rqst_size)
+{
+ /* Read_Buffer *rb; */
+ size_t available_bytes;
+
+ /* rb = read_buffer; */
+ if (rb->empty)
+ return 0;
+ else if (rb->writeptr > rb->readptr)
+ {
+ available_bytes = rb->writeptr - rb->readptr;
+ if (available_bytes <= rqst_size)
+ {
+ /* We can read from the read pointer up to the write pointer */
+ memcpy (buffer, rb->readptr, available_bytes);
+ rb->readptr = rb->writeptr;
+ rb->empty = SANE_TRUE;
+ return available_bytes;
+ }
+ else
+ {
+ /* We can read from the full request size */
+ memcpy (buffer, rb->readptr, rqst_size);
+ rb->readptr = rb->readptr + rqst_size;
+ return rqst_size;
+ }
+ }
+ else
+ {
+ /* The read pointer is ahead of the write pointer. Its wrapped around. */
+ /* We can read to the end of the buffer and make a recursive call to */
+ /* read any available lines at the beginning of the buffer */
+ available_bytes = rb->data + rb->size - rb->readptr;
+ if (available_bytes <= rqst_size)
+ {
+ /* We can read from the read pointer up to the end of the buffer */
+ memcpy (buffer, rb->readptr, available_bytes);
+ rb->readptr = rb->data;
+ if (rb->writeptr == rb->readptr)
+ rb->empty = SANE_TRUE;
+ return available_bytes +
+ read_buffer_get_bytes (rb, buffer + available_bytes,
+ rqst_size - available_bytes);
+ }
+ else
+ {
+ /* We can read from the full request size */
+ memcpy (buffer, rb->readptr, rqst_size);
+ rb->readptr = rb->readptr + rqst_size;
+ return rqst_size;
+ }
+ }
+}
+
+SANE_Bool
+read_buffer_is_empty (Read_Buffer * read_buffer)
+{
+ return read_buffer->empty;
+}
+
+/*
+ * average a width*height rgb/monochrome area
+ * return values in given pointers
+ */
+static int
+average_area (SANE_Byte * regs, SANE_Byte * data, int width, int height,
+ int *ra, int *ga, int *ba)
+{
+ int x, y;
+ int global = 0;
+ int rc, gc, bc;
+
+ *ra = 0;
+ *ga = 0;
+ *ba = 0;
+ rc = 0;
+ gc = 0;
+ bc = 0;
+ if (rts88xx_is_color (regs))
+ {
+ for (x = 0; x < width; x++)
+ for (y = 0; y < height; y++)
+ {
+ rc += data[3 * width * y + x];
+ gc += data[3 * width * y + width + x];
+ bc += data[3 * width * y + 2 * width + x];
+ }
+ global = (rc + gc + bc) / (3 * width * height);
+ *ra = rc / (width * height);
+ *ga = gc / (width * height);
+ *ba = bc / (width * height);
+ }
+ else
+ {
+ for (x = 0; x < width; x++)
+ for (y = 0; y < height; y++)
+ {
+ gc += data[width * y + x];
+ }
+ global = gc / (width * height);
+ *ga = gc / (width * height);
+ }
+ DBG (7, "average_area: global=%d, red=%d, green=%d, blue=%d\n", global, *ra,
+ *ga, *ba);
+ return global;
+}
+
+/**
+ * we scan a dark area with gain minimum to detect offset
+ */
+SANE_Status
+sanei_lexmark_low_offset_calibration (Lexmark_Device * dev)
+{
+ SANE_Byte regs[255]; /* we have our own copy of shadow registers */
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i, lines = 8, yoffset = 2;
+ int pixels;
+ int failed = 0;
+ /* offsets */
+ int ro = 0, go = 0, bo = 0;
+ /* averages */
+ int ra, ga, ba, average;
+ SANE_Byte *data = NULL;
+ SANE_Byte top[OFFSET_RANGES] = { 0, 0x7f, 0x9f, 0xbf, 0xff };
+#ifdef DEEP_DEBUG
+ char title[20];
+#endif
+
+ DBG (2, "sanei_lexmark_low_offset_calibration: start\n");
+ /* copy registers */
+ for (i = 0; i < 255; i++)
+ regs[i] = dev->shadow_regs[i];
+
+ /* we clear movement bit */
+ regs[0xc3] = regs[0xc3] & 0x7f;
+
+ pixels =
+ (dev->sensor->offset_endx - dev->sensor->offset_startx) / regs[0x7a];
+
+ /* there are 4 ranges of offset:
+ 00-7F : almost no offset
+ 80-9F : high noise
+ A0-BF : high noise
+ C0-FF : high noise
+ we start with the highest one and decrease until
+ overall offset is ok
+ First loop may have such an high offset that scanned data overflow
+ and gives a low average. So we allways skip its results
+ */
+
+ /* minimal gains */
+ DBG (3,
+ "sanei_lexmark_low_offset_calibration: setting gains to (1,1,1).\n");
+ rts88xx_set_gain (regs, 1, 1, 1);
+
+ i = OFFSET_RANGES;
+ average = 255;
+
+ /* loop on ranges until one fits. Then adjust offset, first loop is
+ * always done. TODO detect overflow by 'noise looking' data pattern */
+ while (((i > 0) && (average > dev->sensor->offset_threshold))
+ || (i == OFFSET_RANGES))
+ {
+ /* next range */
+ i--;
+
+ /* sets to top of range */
+ ro = top[i];
+ go = top[i];
+ bo = top[i];
+ rts88xx_set_offset (regs, ro, ro, ro);
+ DBG (3,
+ "sanei_lexmark_low_offset_calibration: setting offsets to (%d,%d,%d).\n",
+ ro, ro, ro);
+
+ status =
+ low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels,
+ yoffset, lines, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n");
+ if (data != NULL)
+ free (data);
+ return status;
+ }
+#ifdef DEEP_DEBUG
+ sprintf (title, "offset%02x.pnm", ro);
+ write_pnm_file (title, pixels, lines, rts88xx_is_color (regs), data);
+#endif
+ average = average_area (regs, data, pixels, lines, &ra, &ga, &ba);
+ free (data);
+ }
+ if (i == 0)
+ {
+ DBG (2, "sanei_lexmark_low_offset_calibration: failed !\n");
+ failed = 1;
+ }
+
+ /* increase gain and scan again */
+ /* increase gain for fine offset tuning */
+ rts88xx_set_gain (regs, 6, 6, 6);
+ status =
+ low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels, yoffset,
+ lines, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n");
+ if (data != NULL)
+ free (data);
+ return status;
+ }
+ average_area (regs, data, pixels, lines, &ra, &ga, &ba);
+#ifdef DEEP_DEBUG
+ write_pnm_file ("offset-final.pnm", pixels, lines, rts88xx_is_color (regs),
+ data);
+#endif
+
+ /* this "law" is a guess, may (should?) be changed ... */
+ if (!failed)
+ {
+ if (ro > ra)
+ dev->offset.red = ro - ra;
+ if (go > ga)
+ {
+ dev->offset.green = go - ga;
+ dev->offset.gray = go - ga;
+ }
+ if (bo > ba)
+ dev->offset.blue = bo - ba;
+ }
+ else
+ {
+ dev->offset.red = dev->sensor->offset_fallback;
+ dev->offset.green = dev->sensor->offset_fallback;
+ dev->offset.blue = dev->sensor->offset_fallback;
+ }
+ DBG (7,
+ "sanei_lexmark_low_offset_calibration: offset=(0x%02x,0x%02x,0x%02x).\n",
+ dev->offset.red, dev->offset.green, dev->offset.blue);
+
+ DBG (2, "sanei_lexmark_low_offset_calibration: end.\n");
+ free (data);
+ return status;
+}
+
+/*
+ * we scan a white area until average is good enough
+ * level is good enough when it maximize the range value of output:
+ * ie max-min is maximum
+ */
+SANE_Status
+sanei_lexmark_low_gain_calibration (Lexmark_Device * dev)
+{
+ SANE_Byte regs[255]; /* we have our own copy of shadow registers */
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i, lines = 4, yoffset = 1;
+ int sx, ex;
+ int pixels;
+ /* averages */
+ int ra, ga, ba;
+ SANE_Byte *data = NULL;
+ int red, green, blue;
+#ifdef DEEP_DEBUG
+ char title[20];
+#endif
+
+ DBG (2, "sanei_lexmark_low_gain_calibration: start\n");
+ /* copy registers */
+ for (i = 0; i < 255; i++)
+ regs[i] = dev->shadow_regs[i];
+
+ /* we clear movement bit */
+ regs[0xc3] = regs[0xc3] & 0x7f;
+ sx = regs[0x67] * 256 + regs[0x66];
+ ex = regs[0x6d] * 256 + regs[0x6c];
+ pixels = (ex - sx) / regs[0x7a];
+
+
+ /* set up inital gains */
+ red = 6;
+ green = 6;
+ blue = 6;
+ rts88xx_set_gain (regs, red, green, blue);
+
+ /* init loop */
+ i = 0;
+ ra = 0;
+ ba = 0;
+ ga = 0;
+
+ status = low_cancel (dev->devnum);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* we do a simple scan all 3 averages give the choosen level */
+ while (((rts88xx_is_color (regs)
+ && ((ra < dev->sensor->red_gain_target)
+ || (ga < dev->sensor->green_gain_target)
+ || (ba < dev->sensor->blue_gain_target)))
+ || (!rts88xx_is_color (regs)
+ && (ga < dev->sensor->gray_gain_target))) && (i < 25))
+ {
+ status = low_simple_scan (dev, regs, sx, pixels, yoffset, lines, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_lexmark_low_gain_calibration: low_simple_scan failed!\n");
+ if (data != NULL)
+ free (data);
+ return status;
+ }
+#ifdef DEEP_DEBUG
+ sprintf (title, "gain%02d.pnm", i);
+ write_pnm_file (title, pixels, lines, rts88xx_is_color (regs), data);
+#endif
+ average_area (regs, data, pixels, lines, &ra, &ga, &ba);
+ free (data);
+ if (ra < dev->sensor->red_gain_target)
+ red++;
+ if (ga < dev->sensor->green_gain_target
+ || (dev->sensor->gray_gain_target && !rts88xx_is_color (regs)))
+ green++;
+ if (ba < dev->sensor->blue_gain_target)
+ blue++;
+ rts88xx_set_gain (regs, red, green, blue);
+ i++;
+ }
+ dev->gain.red = red;
+ dev->gain.green = green;
+ dev->gain.blue = blue;
+ dev->gain.gray = green;
+ DBG (7,
+ "sanei_lexmark_low_gain_calibration: gain=(0x%02x,0x%02x,0x%02x).\n",
+ dev->gain.red, dev->gain.green, dev->gain.blue);
+ DBG (2, "sanei_lexmark_low_gain_calibration: end.\n");
+ return status;
+}
+
+/**
+ * there is no hardware shading correction. So we have to do it in software.
+ * We do it by scanning a pure white area which is before scanning area. Then
+ * we compute per pixel coefficient to move the scanned value to the target
+ * value. These coefficients are used later to correct scanned data.
+ * The scan is done with all the final scan settings but the heigth and vertical
+ * start position.
+ */
+SANE_Status
+sanei_lexmark_low_shading_calibration (Lexmark_Device * dev)
+{
+ SANE_Byte regs[255]; /* we have our own copy of shadow registers */
+ int i, j, pixels, bpl;
+ int sx, ex;
+ SANE_Byte *data = NULL;
+ SANE_Status status;
+ /* enough 75 dpi lines to "go off" home position dot,
+ and include shading area */
+ int lines = 4 + 4;
+ int lineoffset = 1;
+ int linetotal = lines + lineoffset;
+ int yoffset;
+ int x, y;
+ float rtarget, btarget, gtarget;
+
+ DBG (2, "sanei_lexmark_low_shading_calibration: start\n");
+ /* copy registers */
+ for (i = 0; i < 255; i++)
+ regs[i] = dev->shadow_regs[i];
+
+ /* allocate memory for scan */
+ sx = regs[0x67] * 256 + regs[0x66];
+ ex = regs[0x6d] * 256 + regs[0x6c];
+
+
+ DBG (7, "startx=%d, endx=%d, coef=%d, r2f=0x%02x\n",
+ sx, ex, regs[0x7a], regs[0x2f]);
+
+ pixels = (ex - sx) / regs[0x7a];
+ if (rts88xx_is_color (regs))
+ bpl = 3 * pixels;
+ else
+ bpl = pixels;
+
+ /* adjust the target area to the scanning resolution */
+ lines = (8 * lines) / regs[0x7a];
+ lineoffset = (8 * lineoffset) / regs[0x7a];
+ linetotal = (8 * linetotal) / regs[0x7a];
+
+ data = (SANE_Byte *) malloc (bpl * lines);
+ DBG (7, "pixels=%d, lines=%d, size=%d\n", pixels, lines, bpl * lines);
+ if (data == NULL)
+ {
+ DBG (2,
+ "sanei_lexmark_low_shading_calibration: failed to allocate %d bytes !\n",
+ bpl * lines);
+ return SANE_STATUS_NO_MEM;
+ }
+ if (dev->shading_coeff != NULL)
+ free (dev->shading_coeff);
+ dev->shading_coeff = (float *) malloc (bpl * sizeof (float));
+ if (dev->shading_coeff == NULL)
+ {
+ DBG (2,
+ "sanei_lexmark_low_shading_calibration: failed to allocate %d floats !\n",
+ bpl);
+ free (data);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* we set movement bit this time */
+ regs[0xc3] = regs[0xc3] | 0x80;
+
+ /* execute scan */
+ status = low_simple_scan (dev, regs, sx, pixels, lineoffset, lines, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n");
+ if (data != NULL)
+ free (data);
+ return status;
+ }
+
+ yoffset = -1;
+ /* the very first lines of the scan may include the dark dot used
+ * locate park position. We find the first line free of it in the scan.
+ * We can't use is_home_line since it modifies data.
+ */
+ for (y = 0; (y < lines) && (yoffset == y - 1); y++)
+ {
+ if (rts88xx_is_color (regs))
+ {
+ for (x = 0; x < 3 * pixels; x++)
+ {
+ if (data[x + y * 3 * pixels] < 30)
+ yoffset = y;
+ }
+ }
+ else
+ {
+ for (x = 0; x < pixels; x++)
+ {
+ if (data[x + y * pixels] < 30)
+ yoffset = y;
+ }
+ }
+ }
+ /* make sure we are really out of the dot */
+ yoffset++;
+
+ /* yoffset is index of last dot line, go to first white line */
+ if (yoffset >= lines - 1)
+ {
+ DBG (7,
+ "sanei_lexmark_low_shading_calibration: failed to detect yoffset.\n");
+ /* fail safe fallback, picture will be altered at dot position,
+ but scanner is safe */
+ yoffset = lines - 2;
+ }
+ else
+ yoffset++;
+ DBG (7, "sanei_lexmark_low_shading_calibration: yoffset=%d.\n", yoffset);
+
+#ifdef DEEP_DEBUG
+ write_pnm_file ("shading.pnm", pixels, lines, rts88xx_is_color (regs),
+ data);
+#endif
+
+ /* computes coefficients */
+ /* there are 8 lines usable for shading calibration at 150 dpi, between
+ bottom of "home position" dot and the start of the scanner's window
+ assembly, we only use 7 of them */
+ if (yoffset + (8 * 4) / regs[0x7a] < lines)
+ lines = yoffset + (8 * 4) / regs[0x7a];
+ rtarget = dev->sensor->red_shading_target;
+ gtarget = dev->sensor->green_shading_target;
+ btarget = dev->sensor->blue_shading_target;
+ for (i = 0; i < pixels; i++)
+ {
+ /* we computes the coefficient needed to move the scanned value to
+ the target value */
+ if (rts88xx_is_color (dev->shadow_regs))
+ {
+ /* RED */
+ dev->shading_coeff[i] = 0;
+ for (j = yoffset; j < lines; j++)
+ dev->shading_coeff[i] += data[i + j * bpl];
+ dev->shading_coeff[i] =
+ (rtarget / (dev->shading_coeff[i] / (lines - yoffset)));
+
+ /* GREEN */
+ dev->shading_coeff[i + pixels] = 0;
+ for (j = yoffset; j < lines; j++)
+ dev->shading_coeff[i + pixels] += data[i + j * bpl + pixels];
+ dev->shading_coeff[i + pixels] =
+ ((gtarget / dev->shading_coeff[i + pixels]) * (lines - yoffset));
+
+ /* BLUE */
+ dev->shading_coeff[i + 2 * pixels] = 0;
+ for (j = yoffset; j < lines; j++)
+ dev->shading_coeff[i + 2 * pixels] +=
+ data[i + j * bpl + 2 * pixels];
+ dev->shading_coeff[i + 2 * pixels] =
+ ((btarget / dev->shading_coeff[i + 2 * pixels]) *
+ (lines - yoffset));
+ }
+ else
+ {
+ dev->shading_coeff[i] = 0;
+ for (j = yoffset; j < lines; j++)
+ {
+ dev->shading_coeff[i] += data[i + j * bpl];
+ }
+ dev->shading_coeff[i] =
+ (rtarget / dev->shading_coeff[i]) * (lines - yoffset);
+ }
+ }
+ free(data);
+
+ /* do the scan backward to go back to start position */
+ regs[0xc6] &= 0xF7;
+ lines = (8 * 8) / regs[0x7a];
+ /* it shoud use linetotal to account for the lineoffset */
+ if (dev->model.sensor_type == X74_SENSOR)
+ lines = linetotal;
+
+ /* execute scan */
+ status = low_simple_scan (dev, regs, sx, pixels, 1, lines, &data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n");
+ if(data!=NULL)
+ free(data);
+ return status;
+ }
+
+#ifdef DEEP_DEBUG
+ write_pnm_file ("shading_bwd.pnm", pixels, lines, rts88xx_is_color (regs),
+ data);
+#endif
+ free (data);
+
+ DBG (2, "sanei_lexmark_low_shading_calibration: end.\n");
+ return status;
+}
+
+
+SANE_Status
+sanei_lexmark_low_calibration (Lexmark_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (2, "sanei_lexmark_low_calibration: start.\n");
+ status = sanei_lexmark_low_offset_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* we put the offset just computed in scanning regs */
+ if (rts88xx_is_color (dev->shadow_regs))
+ {
+ rts88xx_set_offset (dev->shadow_regs,
+ dev->offset.red,
+ dev->offset.green, dev->offset.blue);
+ }
+ else
+ {
+ rts88xx_set_offset (dev->shadow_regs,
+ dev->offset.gray,
+ dev->offset.gray, dev->offset.gray);
+ }
+
+ /* if manual gain settings, no gain calibration */
+ if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE)
+ {
+ if (rts88xx_is_color (dev->shadow_regs))
+ {
+ dev->gain.red = dev->val[OPT_RED_GAIN].w;
+ dev->gain.green = dev->val[OPT_GREEN_GAIN].w;
+ dev->gain.blue = dev->val[OPT_BLUE_GAIN].w;
+ }
+ else
+ dev->gain.gray = dev->val[OPT_GRAY_GAIN].w;
+ }
+ else
+ {
+ status = sanei_lexmark_low_gain_calibration (dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* put the calibrated or manual settings before shading calibration
+ which must be done with final setting values */
+ if (rts88xx_is_color (dev->shadow_regs))
+ {
+ rts88xx_set_gain (dev->shadow_regs, dev->gain.red, dev->gain.green,
+ dev->gain.blue);
+ }
+ else
+ {
+ rts88xx_set_gain (dev->shadow_regs, dev->gain.gray, dev->gain.gray,
+ dev->gain.gray);
+ }
+
+ status = sanei_lexmark_low_shading_calibration (dev);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG (2, "sanei_lexmark_low_calibration: end.\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* assign sensor data */
+static SANE_Status
+sanei_lexmark_low_assign_sensor (Lexmark_Device * dev)
+{
+ int dn;
+
+ /* init sensor data */
+ dn = 0;
+ while (sensor_list[dn].id != 0
+ && sensor_list[dn].id != dev->model.sensor_type)
+ dn++;
+ if (sensor_list[dn].id == 0)
+ {
+ DBG (1,
+ "sanei_lexmark_low_assign_sensor: unknown sensor %d\n",
+ dev->model.sensor_type);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ dev->sensor = &(sensor_list[dn]);
+ DBG (1, "sanei_lexmark_low_assign_sensor: assigned sensor number %d\n",
+ dev->model.sensor_type);
+ return SANE_STATUS_GOOD;
+}
+
+/* assign model description, based on USB id, and register content when
+ * available */
+SANE_Status
+sanei_lexmark_low_assign_model (Lexmark_Device * dev,
+ SANE_String_Const devname, SANE_Int vendor,
+ SANE_Int product, SANE_Byte mainboard)
+{
+ int dn;
+ SANE_Bool found = SANE_FALSE;
+
+ DBG_INIT ();
+
+ DBG (2, "sanei_lexmark_low_assign_model: start\n");
+ DBG (3,
+ "sanei_lexmark_low_assign_model: assigning %04x:%04x, variant %d\n",
+ vendor, product, mainboard);
+
+ dn = 0;
+ /* walk the list of known devices */
+ while (!found && model_list[dn].vendor_id != 0)
+ {
+ /* no mainboard id given (at attach time) */
+ if (mainboard == 0
+ && vendor == model_list[dn].vendor_id
+ && product == model_list[dn].product_id)
+ found = SANE_TRUE;
+ /* mainboard given (init time) */
+ if (mainboard != 0
+ && mainboard == model_list[dn].mainboard_id
+ && vendor == model_list[dn].vendor_id
+ && product == model_list[dn].product_id)
+ found = SANE_TRUE;
+
+ if (!found)
+ dn++;
+ }
+
+ /* we hit the end of list, so we don't know about the current model */
+ if (!found)
+ {
+ DBG (1,
+ "sanei_lexmark_low_assign_model: unknown device 0x%04x:0x%04x\n",
+ vendor, product);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor = model_list[dn].vendor;
+ dev->sane.model = model_list[dn].model;
+ dev->model = model_list[dn];
+ dev->sane.type = "flatbed scanner";
+
+ DBG (3, "sanei_lexmark_low_assign_model: assigned %s\n", dev->model.model);
+
+ /* init sensor data */
+ DBG (2, "sanei_lexmark_low_assign_model: end.\n");
+ return sanei_lexmark_low_assign_sensor (dev);
+}
diff --git a/backend/lexmark_models.c b/backend/lexmark_models.c
new file mode 100644
index 0000000..7819a8d
--- /dev/null
+++ b/backend/lexmark_models.c
@@ -0,0 +1,116 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2006-2010 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2010 "Torsten Houwaart" <ToHo@gmx.de> X74 support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+static Lexmark_Model model_list[] = {
+ {
+ 0x043d, /* vendor id */
+ 0x007c, /* product id */
+ 0xb2, /* submodel id */
+ "Lexmark X1100", /* name */
+ "Lexmark", /* vendor */
+ "X1100/rev. B2", /* model */
+ X1100_MOTOR,
+ /* X1100 series has 2 sensors */
+ X1100_B2_SENSOR,
+ 1235, /* first x-coordinate of Home Point */
+ 1258}, /* second x-coordinate of Home Point */
+ {
+ 0x043d, /* vendor id */
+ 0x007c, /* product id */
+ 0x2c, /* submodel id */
+ "Lexmark X1100", /* name */
+ "Lexmark", /* vendor */
+ "X1100/rev. 2C", /* model */
+ A920_MOTOR, /* X1100 series has 2 sensors, 2C or B2. It
+ is detected at sane_open() */
+ X1100_2C_SENSOR,
+ 1235, /* first x-coordinate of Home Point */
+ 1258}, /* second x-coordinate of Home Point */
+ {
+ 0x413c, /* vendor id */
+ 0x5105, /* product id */
+ 0x2c, /* submodel id */
+ "Dell A920", /* name */
+ "Dell", /* vendor */
+ "A920", /* model */
+ A920_MOTOR,
+ A920_SENSOR,
+ 1235, /* first x-coordinate of Home Point */
+ 1258}, /* second x-coordinate of Home Point */
+ {
+ 0x043d, /* vendor id */
+ 0x007d, /* product id */
+ 0x87, /* submodel id */
+ "Lexmark X1200", /* name */
+ "Lexmark", /* vendor */
+ "X1200/USB1.1", /* model */
+ A920_MOTOR,
+ X1200_SENSOR,
+ 1235, /* first x-coordinate of Home Point */
+ 1258}, /* second x-coordinate of Home Point */
+ {
+ 0x043d, /* vendor id */
+ 0x007d, /* product id */
+ 0x97, /* submodel id */
+ "Lexmark X1200", /* name */
+ "Lexmark", /* vendor */
+ "X1200/USB2.0", /* model */
+ A920_MOTOR,
+ X1200_USB2_SENSOR,
+ 1235, /* first x-coordinate of Home Point */
+ 1258}, /* second x-coordinate of Home Point */
+ {
+ 0x043d, /* vendor id */
+ 0x0060, /* product id */
+ 0x00, /* submodel id */
+ "Lexmark X74", /* name */
+ "Lexmark", /* vendor */
+ "X74", /* model */
+ X74_MOTOR,
+ X74_SENSOR,
+ 1222, /* first x-coordinate of Home Point */
+ 1322}, /* second x-coordinate of Home Point */
+ { /* termination model, must be last */
+ 0, 0, 0, NULL, NULL, NULL, 0, 0, 0, 0}
+}; /* end models description */
diff --git a/backend/lexmark_sensors.c b/backend/lexmark_sensors.c
new file mode 100644
index 0000000..9a34354
--- /dev/null
+++ b/backend/lexmark_sensors.c
@@ -0,0 +1,134 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2006-2010 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2010 "Torsten Houwaart" <ToHo@gmx.de> X74 support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+static Lexmark_Sensor sensor_list[] = {
+ {
+ X1100_B2_SENSOR,
+ /* start x, end x and target average for offset calibration */
+ 48, 80, 6,
+ /* usable pixel sensor startx */
+ 106,
+ /* default gain */
+ 16,
+ /* gain calibration targets */
+ 180, 180, 180, 180,
+ /* shading correction targets */
+ 260, 260, 260, 260,
+ /* offset and gain fallback */
+ 0x70, 17},
+ {
+ X1100_2C_SENSOR,
+ /* start x, end x and target average for offset calibration */
+ 48, 80, 12,
+ /* usable pixel sensor startx */
+ 106,
+ /* default gain */
+ 10,
+ /* gain calibration */
+ 140, 150, 150, 150,
+ /* shading correction */
+ 260, 260, 260, 260,
+ /* offset and gain fallback */
+ 0x70, 11},
+ { /* USB 1.1 settings */
+ X1200_SENSOR,
+ /* start x, end x and target average for offset calibration */
+ 32, 64, 15,
+ /* usable pixel sensor startx */
+ 136,
+ /* default gain */
+ 16,
+ /* gain calibration */
+ 180, 180, 180, 180,
+ /* shading correction */
+ 260, 260, 260, 260,
+ /* offset and gain fallback */
+ 0x86, 16},
+ { /* this one is a 1200 on USB2.0 */
+ X1200_USB2_SENSOR,
+ /* start x, end x and target average for offset calibration */
+ 32, 64, 12,
+ /* usable pixel sensor startx */
+ 136,
+ /* default gain */
+ 16,
+ /* gain calibration */
+ 180, 180, 180, 180,
+ /* shading correction */
+ 260, 260, 260, 260,
+ /* offset and gain fallback */
+ 0x86, 16},
+ {
+ A920_SENSOR,
+ /* start x, end x and target average for offset calibration */
+ 48, 80, 6,
+ /* usable pixel sensor startx */
+ 106,
+ /* default gain */
+ 12,
+ /* gain calibration target */
+ 130, 145, 150, 145,
+ /* gain calibration target */
+ 260, 260, 260, 260,
+ /* offset and gain fallback */
+ 0x70, 13},
+ {
+ X74_SENSOR,
+ /* start x TDONE, end x and target average for offset calibration */
+ /*36,68,12, */
+ 20, 52, 12,
+ /* usable pixel sensor startx */
+ /*104, */
+ 104,
+ /* default gain */
+ 10,
+ /* gain calibration target */
+ 130, 145, 150, 145,
+ /* gain calibration target */
+ 260, 260, 260, 260,
+ /* offset and gain fallback */
+ 0x70, 13},
+ /* termination list sensor, must be last */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
diff --git a/backend/lm9830.h b/backend/lm9830.h
new file mode 100644
index 0000000..cd6a4d1
--- /dev/null
+++ b/backend/lm9830.h
@@ -0,0 +1,58 @@
+/*
+ (c) 2001 Nathan Rutman nathan@gordian.com 11/13/01
+
+ National Semi LM9830 scanner-on-a-chip register defs
+*/
+
+#ifndef LM9830_H
+#define LM9830_H
+
+
+/* LM9830 registers (see lm9830 datasheet for a full description */
+#define IMAGE_DATA_AVAIL 0x01
+#define STATUS 0x02
+#define DATAPORT_TARGET 0x03
+#define DATAPORT_ADDR 0x04
+#define DATAPORT 0x06
+#define COMMAND 0x07
+#define CLOCK_DIV 0x08 /* Master clock divider */
+#define ACTIVE_PX_START 0x1e /* Active pixel start */
+#define LINE_END 0x20
+#define DATA_PX_START 0x22
+#define DATA_PX_END 0x24
+#define COLOR_MODE 0x26
+#define LAMP_R_ON 0x2c
+#define LAMP_R_OFF 0x2e
+#define LAMP_G_ON 0x30
+#define LAMP_G_OFF 0x32
+#define LAMP_B_ON 0x34
+#define LAMP_B_OFF 0x36
+#define PARALLEL_PORT 0x42 /* Parallel port settings */
+#define MICROSTEP 0x45
+#define STEP_SIZE 0x46
+#define FAST_STEP 0x48
+#define SKIP_STEPS 0x4a
+#define BUFFER_LIMIT 0x4e /* Pause scanning when buffer is this full */
+#define BUFFER_RESUME 0x4f
+#define REVERSE_STEPS 0x50
+#define STEP_PWM 0x57
+#define PAPER_SENSOR 0x58
+
+/* Register 02 flags */
+#define STATUS_HOME 0x02
+#define STATUS_PAUSE 0x10
+#define STATUS_POWER 0x20
+
+/* Register 03 flags */
+#define DP_GAMMA 0x00
+#define DP_OFFSET 0x01
+#define DP_R 0x00
+#define DP_G 0x02
+#define DP_B 0x04
+
+/* Register 04 flags */
+#define DP_WRITE 0x0000
+#define DP_READ 0x2000
+
+
+#endif /* LM9830_H */
diff --git a/backend/ma1509.c b/backend/ma1509.c
new file mode 100644
index 0000000..5fccd1c
--- /dev/null
+++ b/backend/ma1509.c
@@ -0,0 +1,2020 @@
+/* sane - Scanner Access Now Easy.
+ (C) 2003 Henning Meier-Geinitz <henning@meier-geinitz.de>.
+
+ Based on the mustek (SCSI) backend.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for scanners based on the Mustek
+ MA-1509 chipset. Currently the Mustek BearPaw 1200F is known to work.
+*/
+
+
+/**************************************************************************/
+/* ma1509 backend version */
+#define BUILD 3
+/**************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+
+#define BACKEND_NAME ma1509
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include "ma1509.h"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/* Debug level from sanei_init_debug */
+static SANE_Int debug_level;
+
+static SANE_Int num_devices;
+static Ma1509_Device *first_dev;
+static Ma1509_Scanner *first_handle;
+static const SANE_Device **devlist = 0;
+
+static int warmup_time = MA1509_WARMUP_TIME;
+
+/* Array of newly attached devices */
+static Ma1509_Device **new_dev;
+
+/* Length of new_dev array */
+static SANE_Int new_dev_len;
+
+/* Number of entries alloced for new_dev */
+static SANE_Int new_dev_alloced;
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static SANE_String_Const ta_source_list[] = {
+ SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"),
+ 0
+};
+
+static SANE_Word resolution_list[] = {
+ 9,
+ 50, 100, 150, 200, 300, 400, 450, 500, 600
+};
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+
+/* "SCSI" command buffers used by the backend */
+static const SANE_Byte scsi_inquiry[] = {
+ 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, INQ_LEN, 0x00
+};
+static const SANE_Byte scsi_test_unit_ready[] = {
+ 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+static const SANE_Byte scsi_set_window[] = {
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00
+};
+
+
+static void
+print_data_buffer (const SANE_Byte * buffer, size_t len)
+{
+ SANE_Byte buffer_byte_list[50];
+ SANE_Byte buffer_byte[5];
+ const SANE_Byte *pp;
+
+ buffer_byte_list[0] = '\0';
+ for (pp = buffer; pp < (buffer + len); pp++)
+ {
+ sprintf ((SANE_String) buffer_byte, " %02x", *pp);
+ strcat ((SANE_String) buffer_byte_list, (SANE_String) buffer_byte);
+ if (((pp - buffer) % 0x10 == 0x0f) || (pp >= (buffer + len - 1)))
+ {
+ DBG (5, "buffer: %s\n", buffer_byte_list);
+ buffer_byte_list[0] = '\0';
+ }
+ }
+}
+
+static SANE_Status
+ma1509_cmd (Ma1509_Scanner * s, const SANE_Byte * cmd, SANE_Byte * data,
+ size_t * data_size)
+{
+ SANE_Status status;
+ size_t size;
+#define MA1509_WRITE_LIMIT (1024 * 64)
+#define MA1509_READ_LIMIT (1024 * 256)
+
+ DBG (5, "ma1509_cmd: fd=%d, cmd=%p, data=%p, data_size=%ld\n",
+ s->fd, cmd, data, (long int) (data_size ? *data_size : 0));
+ DBG (5, "ma1509_cmd: cmd = %02x %02x %02x %02x %02x %02x %02x %02x \n",
+ cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7]);
+
+
+ size = MA1509_COMMAND_LENGTH;
+ status = sanei_usb_write_bulk (s->fd, cmd, &size);
+ if (status != SANE_STATUS_GOOD || size != MA1509_COMMAND_LENGTH)
+ {
+ DBG (5,
+ "ma1509_cmd: sanei_usb_write_bulk returned %s (size = %ld, expected %d)\n",
+ sane_strstatus (status), (long int) size, MA1509_COMMAND_LENGTH);
+ return status;
+ }
+
+ if (cmd[1] == 1)
+ {
+ /* receive data */
+ if (data && data_size && *data_size)
+ {
+ size_t bytes_left = *data_size;
+ DBG (5, "ma1509_cmd: trying to receive %ld bytes of data\n",
+ (long int) *data_size);
+
+ while (status == SANE_STATUS_GOOD && bytes_left > 0)
+ {
+ size = bytes_left;
+ if (size > MA1509_READ_LIMIT)
+ size = MA1509_READ_LIMIT;
+
+ status =
+ sanei_usb_read_bulk (s->fd, data + *data_size - bytes_left,
+ &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "ma1509_cmd: sanei_usb_read_bulk returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ bytes_left -= size;
+ DBG (5, "ma1509_cmd: read %ld bytes, %ld bytes to go\n",
+ (long int) size, (long int) bytes_left);
+ }
+ if (debug_level >= 5)
+ print_data_buffer (data, *data_size);
+ }
+ }
+ else
+ {
+ /* send data */
+ if (data && data_size && *data_size)
+ {
+ size_t bytes_left = *data_size;
+
+ DBG (5, "ma1509_cmd: sending %ld bytes of data\n",
+ (long int) *data_size);
+ if (debug_level >= 5)
+ print_data_buffer (data, *data_size);
+
+ while (status == SANE_STATUS_GOOD && bytes_left > 0)
+ {
+ size = bytes_left;
+ if (size > MA1509_WRITE_LIMIT)
+ size = MA1509_WRITE_LIMIT;
+ status =
+ sanei_usb_write_bulk (s->fd, data + *data_size - bytes_left,
+ &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "ma1509_cmd: sanei_usb_write_bulk returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ bytes_left -= size;
+ DBG (5, "ma1509_cmd: wrote %ld bytes, %ld bytes to go\n",
+ (long int) size, (long int) bytes_left);
+ }
+
+ }
+ }
+
+ DBG (5, "ma1509_cmd: finished: data_size=%ld, status=%s\n",
+ (long int) (data_size ? *data_size : 0), sane_strstatus (status));
+ return status;
+}
+
+static SANE_Status
+test_unit_ready (Ma1509_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Byte buffer[0x04];
+ size_t size = sizeof (buffer);
+
+ status = ma1509_cmd (s, scsi_test_unit_ready, buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "test_unit_ready: ma1509_cmd failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (buffer[1] == 0x14)
+ s->hw->has_adf = SANE_TRUE;
+ else
+ s->hw->has_adf = SANE_FALSE;
+
+ return status;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, Ma1509_Device ** devp)
+{
+ SANE_Int fw_revision;
+ SANE_Byte result[INQ_LEN];
+ SANE_Byte inquiry_byte_list[50], inquiry_text_list[17];
+ SANE_Byte inquiry_byte[5], inquiry_text[5];
+ SANE_Byte *model_name = result + 44;
+ Ma1509_Scanner s;
+ Ma1509_Device *dev, new_dev;
+ SANE_Status status;
+ size_t size;
+ SANE_Byte *pp;
+ SANE_Word vendor, product;
+
+ if (devp)
+ *devp = 0;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ memset (&new_dev, 0, sizeof (new_dev));
+ memset (&s, 0, sizeof (s));
+ s.hw = &new_dev;
+
+ DBG (3, "attach: trying device %s\n", devname);
+
+ status = sanei_usb_open (devname, &s.fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: sanei_usb_open failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ status = sanei_usb_get_vendor_product (s.fd, &vendor, &product);
+ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_UNSUPPORTED)
+ {
+ DBG (1, "attach: sanei_usb_get_vendor_product failed: %s\n",
+ sane_strstatus (status));
+ sanei_usb_close (s.fd);
+ return status;
+ }
+ if (status == SANE_STATUS_UNSUPPORTED)
+ {
+ DBG (3, "attach: can't detect vendor/product, trying anyway\n");
+ }
+ else if (vendor != 0x055f || product != 0x0010)
+ {
+ DBG (1, "attach: unknown vendor/product (0x%x/0x%x)\n", vendor,
+ product);
+ sanei_usb_close (s.fd);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (4, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (&s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test_unit_ready device %s failed (%s)\n", devname,
+ sane_strstatus (status));
+ sanei_usb_close (s.fd);
+ return status;
+ }
+
+ DBG (4, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ memset (result, 0, sizeof (result));
+ status = ma1509_cmd (&s, scsi_inquiry, result, &size);
+ if (status != SANE_STATUS_GOOD || size != INQ_LEN)
+ {
+ DBG (1, "attach: inquiry for device %s failed (%s)\n", devname,
+ sane_strstatus (status));
+ sanei_usb_close (s.fd);
+ return status;
+ }
+
+ sanei_usb_close (s.fd);
+
+ if ((result[0] & 0x1f) != 0x06)
+ {
+ DBG (1, "attach: device %s doesn't look like a scanner at all (%d)\n",
+ devname, result[0] & 0x1f);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (debug_level >= 5)
+ {
+ /* print out inquiry */
+ DBG (5, "attach: inquiry output:\n");
+ inquiry_byte_list[0] = '\0';
+ inquiry_text_list[0] = '\0';
+ for (pp = result; pp < (result + INQ_LEN); pp++)
+ {
+ sprintf ((SANE_String) inquiry_text, "%c",
+ (*pp < 127) && (*pp > 31) ? *pp : '.');
+ strcat ((SANE_String) inquiry_text_list,
+ (SANE_String) inquiry_text);
+ sprintf ((SANE_String) inquiry_byte, " %02x", *pp);
+ strcat ((SANE_String) inquiry_byte_list,
+ (SANE_String) inquiry_byte);
+ if ((pp - result) % 0x10 == 0x0f)
+ {
+ DBG (5, "%s %s\n", inquiry_byte_list, inquiry_text_list);
+ inquiry_byte_list[0] = '\0';
+ inquiry_text_list[0] = '\0';
+ }
+ }
+ }
+
+ /* get firmware revision as BCD number: */
+ fw_revision = (result[32] - '0') << 8 | (result[34] - '0') << 4
+ | (result[35] - '0');
+ DBG (4, "attach: firmware revision %d.%02x\n", fw_revision >> 8,
+ fw_revision & 0xff);
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy (dev, &new_dev, sizeof (*dev));
+
+ dev->name = strdup (devname);
+ if (!dev->name)
+ return SANE_STATUS_NO_MEM;
+ dev->sane.name = (SANE_String_Const) dev->name;
+ dev->sane.vendor = "Mustek";
+ dev->sane.type = "flatbed scanner";
+
+ dev->x_range.min = 0;
+ dev->y_range.min = 0;
+ dev->x_range.quant = SANE_FIX (0.1);
+ dev->y_range.quant = SANE_FIX (0.1);
+ dev->x_trans_range.min = 0;
+ dev->y_trans_range.min = 0;
+ /* default to something really small to be on the safe side: */
+ dev->x_trans_range.max = SANE_FIX (8.0 * MM_PER_INCH);
+ dev->y_trans_range.max = SANE_FIX (5.0 * MM_PER_INCH);
+ dev->x_trans_range.quant = SANE_FIX (0.1);
+ dev->y_trans_range.quant = SANE_FIX (0.1);
+
+ DBG (3, "attach: scanner id: %.11s\n", model_name);
+
+ /* BearPaw 1200F (SCSI-over-USB) */
+ if (strncmp ((SANE_String) model_name, " B06", 4) == 0)
+ {
+ dev->x_range.max = SANE_FIX (211.3);
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (296.7);
+
+ dev->x_trans_range.min = SANE_FIX (0);
+ dev->y_trans_range.min = SANE_FIX (0);
+ dev->x_trans_range.max = SANE_FIX (150.0);
+ dev->y_trans_range.max = SANE_FIX (175.0);
+
+ dev->sane.model = "BearPaw 1200F";
+ }
+ else
+ {
+ DBG (0, "attach: this scanner (ID: %s) is not supported yet\n",
+ model_name);
+ DBG (0, "attach: please set the debug level to 5 and send a debug "
+ "report\n");
+ DBG (0, "attach: to henning@meier-geinitz.de (export "
+ "SANE_DEBUG_MA1509=5\n");
+ DBG (0, "attach: scanimage -L 2>debug.txt). Thank you.\n");
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (2, "attach: found Mustek %s %s %s%s\n",
+ dev->sane.model, dev->sane.type, dev->has_ta ? "(TA)" : "",
+ dev->has_adf ? "(ADF)" : "");
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static SANE_Status
+init_options (Ma1509_Scanner * s)
+{
+ SANE_Int i;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = "";
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[1]);
+ if (!s->val[OPT_MODE].s)
+ return SANE_STATUS_NO_MEM;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = resolution_list;
+ s->val[OPT_RESOLUTION].w = 50;
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size (ta_source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = ta_source_list;
+ s->val[OPT_SOURCE].s = strdup (ta_source_list[0]);
+ if (!s->val[OPT_SOURCE].s)
+ return SANE_STATUS_NO_MEM;
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = s->hw->x_range.min;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = s->hw->y_range.min;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_THRESHOLD].w = 128;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = MA1509_GAMMA_SIZE * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->red_gamma_table[0];
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ for (i = 0; i < MA1509_GAMMA_SIZE; i++)
+ s->red_gamma_table[i] = i * MA1509_GAMMA_SIZE / 256;
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = MA1509_GAMMA_SIZE * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->green_gamma_table[0];
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ for (i = 0; i < MA1509_GAMMA_SIZE; i++)
+ s->green_gamma_table[i] = i * MA1509_GAMMA_SIZE / 256;
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = MA1509_GAMMA_SIZE * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->blue_gamma_table[0];
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ for (i = 0; i < MA1509_GAMMA_SIZE; i++)
+ s->blue_gamma_table[i] = i * MA1509_GAMMA_SIZE / 256;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ Ma1509_Device *dev;
+
+ attach (devname, &dev);
+ if (dev)
+ {
+ /* Keep track of newly attached devices so we can set options as
+ necessary. */
+ if (new_dev_len >= new_dev_alloced)
+ {
+ new_dev_alloced += 4;
+ if (new_dev)
+ new_dev =
+ realloc (new_dev, new_dev_alloced * sizeof (new_dev[0]));
+ else
+ new_dev = malloc (new_dev_alloced * sizeof (new_dev[0]));
+ if (!new_dev)
+ {
+ DBG (1, "attach_one_device: out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ new_dev[new_dev_len++] = dev;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+set_window (Ma1509_Scanner * s)
+{
+ SANE_Byte buffer[0x30], *cp;
+ double pixels_per_mm;
+ size_t size = sizeof (buffer);
+ SANE_Status status;
+ SANE_Int tlx, tly, width, height;
+ SANE_Int offset = 0;
+ struct timeval now;
+ long remaining_time;
+
+ /* check if lamp is warmed up */
+ gettimeofday (&now, 0);
+ remaining_time = warmup_time - (now.tv_sec - s->lamp_time);
+ if (remaining_time > 0)
+ {
+ DBG (0, "Warm-up in progress: please wait %2ld seconds\n",
+ remaining_time);
+ sleep (remaining_time);
+ }
+
+ memset (buffer, 0, size);
+ cp = buffer;
+
+ STORE16B (cp, 0); /* window identifier */
+ STORE16B (cp, s->val[OPT_RESOLUTION].w);
+ STORE16B (cp, 0); /* not used acc. to specs */
+
+ pixels_per_mm = s->val[OPT_RESOLUTION].w / MM_PER_INCH;
+
+ tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5;
+ tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5;
+
+ width = (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w))
+ * pixels_per_mm + 0.5;
+ height = (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w))
+ * pixels_per_mm + 0.5 + offset;
+
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ width /= 64;
+ width *= 64;
+ if (!width)
+ width = 64;
+ }
+ else
+ {
+ width /= 8;
+ width *= 8;
+ if (!width)
+ width = 8;
+ }
+
+
+ DBG (4, "set_window: tlx=%d (%d mm); tly=%d (%d mm); width=%d (%d mm); "
+ "height=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly,
+ (int) (tly / pixels_per_mm), width, (int) (width / pixels_per_mm),
+ height, (int) (height / pixels_per_mm));
+
+
+ STORE16B (cp, 0);
+ STORE16B (cp, tlx);
+ STORE16B (cp, 0);
+ STORE16B (cp, tly);
+ *cp++ = 0x14;
+ *cp++ = 0xc0;
+ STORE16B (cp, width);
+ *cp++ = 0x28;
+ *cp++ = 0x20;
+ STORE16B (cp, height);
+
+ s->hw->ppl = width;
+ s->hw->bpl = s->hw->ppl;
+
+ s->hw->lines = height;
+
+ *cp++ = 0x00; /* brightness, not impl. */
+ /* threshold */
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ *cp++ = (SANE_Byte) s->val[OPT_THRESHOLD].w;
+ else
+ *cp++ = 0x80;
+ *cp++ = 0x00; /* contrast, not impl. */
+ *cp++ = 0x00; /* ??? . */
+
+ /* Note that 'image composition' has no meaning for the SE series */
+ /* Mode selection is accomplished solely by bits/pixel (1, 8, 24) */
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ *cp++ = 24; /* 24 bits/pixel in color mode */
+ s->hw->bpl *= 3;
+ }
+ else if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ *cp++ = 8; /* 8 bits/pixel in gray mode */
+ else
+ {
+ *cp++ = 1; /* 1 bit/pixel in lineart mode */
+ s->hw->bpl /= 8;
+ }
+
+ cp += 13; /* skip reserved bytes */
+ *cp++ = 0x00; /* lamp mode */
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0)
+ *cp++ = 0x02; /* ??? */
+
+ status = ma1509_cmd (s, scsi_set_window, buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "set_window: ma1509_cmd failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ return status;
+}
+
+static SANE_Status
+calibration (Ma1509_Scanner * s)
+{
+ SANE_Byte cmd[0x08], *buffer, *calibration_buffer;
+ SANE_Status status;
+ SANE_Int ppl = 5312;
+ SANE_Int lines = 40;
+ size_t total_size = lines * ppl;
+ SANE_Int color, column, line;
+
+ buffer = malloc (total_size * 3);
+ if (!buffer)
+ {
+ DBG (1,
+ "calibration: couldn't malloc %lu bytes for calibration buffer\n",
+ (u_long) (total_size * 3));
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (buffer, 0x00, total_size);
+
+ memset (cmd, 0, 8);
+ cmd[0] = 0x28; /* read data */
+ cmd[1] = 0x01; /* read */
+ cmd[2] = 0x01; /* calibration */
+ cmd[4] = (total_size >> 16) & 0xff;
+ cmd[5] = (total_size >> 8) & 0xff;
+ cmd[6] = total_size & 0xff;
+ total_size *= 3;
+ status = ma1509_cmd (s, cmd, buffer, &total_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "calibration: ma1509_cmd read data failed: %s\n",
+ sane_strstatus (status));
+ free (buffer);
+ return status;
+ }
+
+ calibration_buffer = malloc (ppl);
+ if (!calibration_buffer)
+ {
+ DBG (1,
+ "calibration: couldn't malloc %d bytes for calibration buffer\n",
+ ppl);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (calibration_buffer, 0x00, ppl);
+
+ memset (cmd, 0, 8);
+ cmd[0] = 0x2a; /* send data */
+ cmd[1] = 0x00; /* write */
+ cmd[2] = 0x01; /* calibration */
+ cmd[5] = (ppl >> 8) & 0xff;
+ cmd[6] = ppl & 0xff;
+
+ for (color = 1; color < 4; color++)
+ {
+ cmd[4] = color;
+
+ for (column = 0; column < ppl; column++)
+ {
+ SANE_Int average = 0;
+
+ for (line = 0; line < lines; line++)
+ average += buffer[line * ppl * 3 + column * 3 + (color - 1)];
+ average /= lines;
+ if (average < 1)
+ average = 1;
+ if (average > 255)
+ average = 255;
+
+ average = (256 * 256) / average - 256;
+ if (average < 0)
+ average = 0;
+ if (average > 255)
+ average = 255;
+ calibration_buffer[column] = average;
+ }
+
+ total_size = ppl;
+ status = ma1509_cmd (s, cmd, calibration_buffer, &total_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "calibration: ma1509_cmd send data failed: %s\n",
+ sane_strstatus (status));
+ free (buffer);
+ free (calibration_buffer);
+ return status;
+ }
+ }
+ free (buffer);
+ free (calibration_buffer);
+ DBG (4, "calibration: done\n");
+ return status;
+}
+
+
+static SANE_Status
+send_gamma (Ma1509_Scanner * s)
+{
+ SANE_Byte cmd[0x08], *buffer;
+ SANE_Status status;
+ size_t total_size = MA1509_GAMMA_SIZE;
+ SANE_Int color;
+
+ buffer = malloc (total_size);
+ if (!buffer)
+ {
+ DBG (1, "send_gamma: couldn't malloc %lu bytes for gamma buffer\n",
+ (u_long) total_size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (cmd, 0, 8);
+ cmd[0] = 0x2a; /* send data */
+ cmd[1] = 0x00; /* write */
+ cmd[2] = 0x03; /* gamma */
+ cmd[5] = (total_size >> 8) & 0xff;
+ cmd[6] = total_size & 0xff;
+ for (color = 1; color < 4; color++)
+ {
+ unsigned int i;
+
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ SANE_Int *int_buffer;
+
+ if (color == 1)
+ int_buffer = s->red_gamma_table;
+ else if (color == 2)
+ int_buffer = s->green_gamma_table;
+ else
+ int_buffer = s->blue_gamma_table;
+ for (i = 0; i < total_size; i++)
+ buffer[i] = int_buffer[i];
+ }
+ else
+ {
+ /* linear tables */
+ for (i = 0; i < total_size; i++)
+ buffer[i] = i * 256 / total_size;
+ }
+
+ cmd[4] = color;
+ status = ma1509_cmd (s, cmd, buffer, &total_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send_gamma: ma1509_cmd send data failed: %s\n",
+ sane_strstatus (status));
+ free (buffer);
+ return status;
+ }
+ }
+ if (!s->val[OPT_CUSTOM_GAMMA].w)
+ free (buffer);
+ DBG (4, "send_gamma: done\n");
+ return status;
+}
+
+
+static SANE_Status
+start_scan (Ma1509_Scanner * s)
+{
+ SANE_Byte cmd[8];
+ SANE_Status status;
+
+ DBG (4, "start_scan\n");
+ memset (cmd, 0, 8);
+
+ cmd[0] = 0x1b;
+ cmd[1] = 0x01;
+ cmd[2] = 0x01;
+
+ status = ma1509_cmd (s, cmd, NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start_scan: ma1509_cmd failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ return status;
+}
+
+static SANE_Status
+turn_lamp (Ma1509_Scanner * s, SANE_Bool is_on)
+{
+ SANE_Status status;
+ SANE_Byte buffer[0x30];
+ size_t size = sizeof (buffer);
+ struct timeval lamp_time;
+
+ DBG (4, "turn_lamp %s\n", is_on ? "on" : "off");
+ memset (buffer, 0, size);
+ if (is_on)
+ buffer[0x28] = 0x01;
+ else
+ buffer[0x28] = 0x02;
+
+ status = ma1509_cmd (s, scsi_set_window, buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "turn_lamp: ma1509_cmd set_window failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ gettimeofday (&lamp_time, 0);
+ s->lamp_time = lamp_time.tv_sec;
+ return status;
+}
+
+static SANE_Status
+stop_scan (Ma1509_Scanner * s)
+{
+ SANE_Byte cmd[8];
+ SANE_Status status;
+
+ DBG (4, "stop_scan\n");
+ memset (cmd, 0, 8);
+
+ cmd[0] = 0x1b;
+ cmd[1] = 0x01;
+ cmd[2] = 0x00;
+
+ status = ma1509_cmd (s, cmd, NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "stop_scan: ma1509_cmd failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+
+ DBG (4, "stop_scan: scan stopped\n");
+ return status;
+}
+
+
+static SANE_Status
+start_read_data (Ma1509_Scanner * s)
+{
+ SANE_Byte cmd[8];
+ SANE_Status status;
+ SANE_Int total_size = s->hw->ppl * s->hw->lines;
+
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ total_size /= 8;
+
+ memset (cmd, 0, 8);
+
+ cmd[0] = 0x28; /* read data */
+ cmd[1] = 0x01; /* read */
+ cmd[2] = 0x00; /* scan data */
+ cmd[3] = (total_size >> 24) & 0xff;
+ cmd[4] = (total_size >> 16) & 0xff;
+ cmd[5] = (total_size >> 8) & 0xff;
+ cmd[6] = total_size & 0xff;
+ status = ma1509_cmd (s, cmd, NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "stop_scan: ma1509_cmd failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ return status;
+}
+
+static SANE_Status
+read_data (Ma1509_Scanner * s, SANE_Byte * buffer, SANE_Int * size)
+{
+ size_t local_size = *size;
+ SANE_Status status;
+
+ status = sanei_usb_read_bulk (s->fd, buffer, &local_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "read_data: sanei_usb_read_bulk failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ *size = local_size;
+ return status;
+}
+
+
+
+
+/**************************************************************************/
+/* SANE API calls */
+/**************************************************************************/
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Char line[PATH_MAX], *word, *end;
+ SANE_String_Const cp;
+ SANE_Int linenumber;
+ FILE *fp;
+
+ DBG_INIT ();
+
+#ifdef DBG_LEVEL
+ debug_level = DBG_LEVEL;
+#else
+ debug_level = 0;
+#endif
+
+ DBG (2, "SANE ma1509 backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (4, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ sanei_usb_init ();
+
+ num_devices = 0;
+ first_dev = 0;
+ first_handle = 0;
+ devlist = 0;
+ new_dev = 0;
+ new_dev_len = 0;
+ new_dev_alloced = 0;
+
+ fp = sanei_config_open (MA1509_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/usb/scanner0 instead of insisting on config file */
+ DBG (3, "sane_init: couldn't find config file (%s), trying "
+ "/dev/usb/scanner0 directly\n", MA1509_CONFIG_FILE);
+ attach ("/dev/usb/scanner0", 0);
+ return SANE_STATUS_GOOD;
+ }
+ linenumber = 0;
+ DBG (4, "sane_init: reading config file `%s'\n", MA1509_CONFIG_FILE);
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ word = 0;
+ linenumber++;
+
+ cp = sanei_config_get_string (line, &word);
+ if (!word || cp == line)
+ {
+ DBG (5, "sane_init: config file line %d: ignoring empty line\n",
+ linenumber);
+ if (word)
+ free (word);
+ continue;
+ }
+ if (word[0] == '#')
+ {
+ DBG (5, "sane_init: config file line %d: ignoring comment line\n",
+ linenumber);
+ free (word);
+ continue;
+ }
+ if (strcmp (word, "option") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+
+ if (!word)
+ {
+ DBG (1, "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ if (strcmp (word, "warmup-time") == 0)
+ {
+ long local_warmup_time;
+
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+
+ if (!word)
+ {
+ DBG (1, "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ errno = 0;
+ local_warmup_time = strtol (word, &end, 0);
+
+ if (end == word)
+ {
+ DBG (3, "sane-init: config file line %d: warmup-time must "
+ "have a parameter; using default (%d)\n",
+ linenumber, warmup_time);
+ }
+ else if (errno)
+ {
+ DBG (3, "sane-init: config file line %d: warmup-time `%s' "
+ "is invalid (%s); using default (%d)\n", linenumber,
+ word, strerror (errno), warmup_time);
+ }
+ else
+ {
+ warmup_time = local_warmup_time;
+ DBG (4,
+ "sane_init: config file line %d: warmup-time set "
+ "to %d seconds\n", linenumber, warmup_time);
+
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: ignoring unknown "
+ "option `%s'\n", linenumber, word);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+ else
+ {
+ new_dev_len = 0;
+ DBG (4, "sane_init: config file line %d: trying to attach `%s'\n",
+ linenumber, line);
+ sanei_usb_attach_matching_devices (line, attach_one_device);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+
+ if (new_dev_alloced > 0)
+ {
+ new_dev_len = new_dev_alloced = 0;
+ free (new_dev);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Ma1509_Device *dev, *next;
+
+ DBG (4, "sane_exit\n");
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->name);
+ free (dev);
+ }
+ if (devlist)
+ free (devlist);
+ devlist = 0;
+ first_dev = 0;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Ma1509_Device *dev;
+ SANE_Int i;
+
+ DBG (4, "sane_get_devices: %d devices %s\n", num_devices,
+ local_only ? "(local only)" : "");
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ DBG (5, "sane_get_devices: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Ma1509_Device *dev;
+ SANE_Status status;
+ Ma1509_Scanner *s;
+
+ if (!devicename)
+ {
+ DBG (1, "sane_open: devicename is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!handle)
+ {
+ DBG (1, "sane_open: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (4, "sane_open: devicename=%s\n", devicename);
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ /* empty devicname -> use first device */
+ dev = first_dev;
+
+ if (!dev)
+ {
+ DBG (1, "sane_open: %s doesn't seem to exist\n", devicename);
+ return SANE_STATUS_INVAL;
+ }
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->fd = -1;
+ s->hw = dev;
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ status = sanei_usb_open (s->hw->sane.name, &s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: couldn't open %s: %s\n", s->hw->sane.name,
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = turn_lamp (s, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: couldn't turn on lamp: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ status = turn_lamp (s, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: couldn't turn on lamp: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ *handle = s;
+ DBG (5, "sane_open: finished (handle=%p)\n", (void *) s);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Ma1509_Scanner *prev, *s;
+ SANE_Status status;
+
+ DBG (4, "sane_close: handle=%p\n", handle);
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->val[OPT_MODE].s)
+ free (s->val[OPT_MODE].s);
+ if (s->val[OPT_SOURCE].s)
+ free (s->val[OPT_SOURCE].s);
+
+ status = turn_lamp (s, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_close: couldn't turn off lamp: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+ sanei_usb_close (s->fd);
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+ free (handle);
+ handle = 0;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Ma1509_Scanner *s = handle;
+
+ if (((unsigned) option >= NUM_OPTIONS) || (option < 0))
+ {
+ DBG (3, "sane_get_option_descriptor: option %d >= NUM_OPTIONS or < 0\n",
+ option);
+ return 0;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_get_option_descriptor: handle is null!\n");
+ return 0;
+ }
+ if (s->opt[option].name && s->opt[option].name[0] != 0)
+ DBG (4, "sane_get_option_descriptor for option %s (%sactive%s)\n",
+ s->opt[option].name,
+ s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "",
+ s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : "");
+ else
+ DBG (4, "sane_get_option_descriptor for option \"%s\" (%sactive%s)\n",
+ s->opt[option].title,
+ s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "",
+ s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : "");
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Ma1509_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Word w;
+
+ if (((unsigned) option >= NUM_OPTIONS) || (option < 0))
+ {
+ DBG (3, "sane_control_option: option %d < 0 or >= NUM_OPTIONS\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_control_option: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!val && s->opt[option].type != SANE_TYPE_BUTTON)
+ {
+ DBG (1, "sane_control_option: val is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->opt[option].name && s->opt[option].name[0] != 0)
+ DBG (4, "sane_control_option (%s option %s)\n",
+ action == SANE_ACTION_GET_VALUE ? "get" :
+ (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"),
+ s->opt[option].name);
+ else
+ DBG (4, "sane_control_option (%s option \"%s\")\n",
+ action == SANE_ACTION_GET_VALUE ? "get" :
+ (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"),
+ s->opt[option].title);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ {
+ DBG (3, "sane_control_option: don't use while scanning (option %s)\n",
+ s->opt[option].name);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (3, "sane_control_option: option %s is inactive\n",
+ s->opt[option].name);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_PREVIEW:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_THRESHOLD:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_SOURCE:
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (3, "sane_control_option: option %s is not setable\n",
+ s->opt[option].name);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (4, "sane_control_option: constrain_value error (option %s)\n",
+ s->opt[option].name);
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_THRESHOLD:
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ {
+ SANE_Char *old_val = s->val[option].s;
+
+ if (old_val)
+ {
+ if (strcmp (old_val, val) == 0)
+ return SANE_STATUS_GOOD; /* no change */
+ free (old_val);
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SOURCE:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+
+ if (strcmp (val, "Transparency Adapter") == 0)
+ {
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_trans_range;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_trans_range;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_trans_range;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_trans_range;
+ }
+ else
+ {
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ }
+ return SANE_STATUS_GOOD;
+
+ /* options with side-effects: */
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ if (w && strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0)
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ }
+ DBG (4, "sane_control_option: unknown action for option %s\n",
+ s->opt[option].name);
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Ma1509_Scanner *s = handle;
+ SANE_String_Const mode;
+
+ if (!s)
+ {
+ DBG (1, "sane_get_parameters: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!s->scanning)
+ {
+ double width, height, dpi;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
+ height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
+ dpi = s->val[OPT_RESOLUTION].w;
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (dpi > 0.0 && width > 0.0 && height > 0.0)
+ {
+ double dots_per_mm = dpi / MM_PER_INCH;
+
+ s->params.pixels_per_line = width * dots_per_mm;
+ s->params.lines = height * dots_per_mm;
+ }
+ mode = s->val[OPT_MODE].s;
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ }
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ else
+ {
+ /* it's one of the color modes... */
+
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->params.format = SANE_FRAME_RGB;
+ }
+ }
+ s->params.last_frame = SANE_TRUE;
+ if (params)
+ *params = s->params;
+ DBG (4, "sane_get_parameters: frame = %d; last_frame = %s; depth = %d\n",
+ s->params.format, s->params.last_frame ? "true" : "false",
+ s->params.depth);
+ DBG (4, "sane_get_parameters: lines = %d; ppl = %d; bpl = %d\n",
+ s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Ma1509_Scanner *s = handle;
+ SANE_Status status;
+ SANE_String_Const mode;
+ struct timeval start;
+
+ if (!s)
+ {
+ DBG (1, "sane_start: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "sane_start\n");
+
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* Check for inconsistencies */
+
+ if (s->val[OPT_TL_X].w > s->val[OPT_BR_X].w)
+ {
+ DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
+ "-- aborting\n",
+ s->opt[OPT_TL_X].title, SANE_UNFIX (s->val[OPT_TL_X].w),
+ s->opt[OPT_BR_X].title, SANE_UNFIX (s->val[OPT_BR_X].w));
+ return SANE_STATUS_INVAL;
+ }
+ if (s->val[OPT_TL_Y].w > s->val[OPT_BR_Y].w)
+ {
+ DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
+ "-- aborting\n",
+ s->opt[OPT_TL_Y].title, SANE_UNFIX (s->val[OPT_TL_Y].w),
+ s->opt[OPT_BR_Y].title, SANE_UNFIX (s->val[OPT_BR_Y].w));
+ return SANE_STATUS_INVAL;
+ }
+
+ s->total_bytes = 0;
+ s->read_bytes = 0;
+
+ /* save start time */
+ gettimeofday (&start, 0);
+ s->start_time = start.tv_sec;
+ /* translate options into s->mode for convenient access: */
+ mode = s->val[OPT_MODE].s;
+
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: set window command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = test_unit_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: test_unit_ready failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0)
+ {
+ status = calibration (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: calibration failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = send_gamma (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: send_gamma failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ }
+
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: start_scan command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = start_read_data (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: start_read_data command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ s->params.bytes_per_line = s->hw->bpl;
+ s->params.pixels_per_line = s->params.bytes_per_line;
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ s->params.pixels_per_line /= 3;
+ else if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->params.pixels_per_line *= 8;
+
+ s->params.lines = s->hw->lines;
+
+ s->buffer = (SANE_Byte *) malloc (MA1509_BUFFER_SIZE);
+ if (!s->buffer)
+ return SANE_STATUS_NO_MEM;
+ s->buffer_bytes = 0;
+
+ DBG (5, "sane_start: finished\n");
+ return SANE_STATUS_GOOD;
+
+stop_scanner_and_return:
+ sanei_usb_close (s->fd);
+ s->scanning = SANE_FALSE;
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Ma1509_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Int total_size = s->hw->lines * s->hw->bpl;
+ SANE_Int i;
+
+ if (!s)
+ {
+ DBG (1, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!buf)
+ {
+ DBG (1, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (1, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sane_read\n");
+ *len = 0;
+
+ if (s->cancelled)
+ {
+ DBG (4, "sane_read: scan was cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (!s->scanning)
+ {
+ DBG (1, "sane_read: must call sane_start before sane_read\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (total_size - s->read_bytes <= 0)
+ {
+ DBG (4, "sane_read: EOF\n");
+ stop_scan (s);
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_EOF;
+ }
+
+ if (s->buffer_bytes == 0)
+ {
+ SANE_Int size = MA1509_BUFFER_SIZE;
+ if (size > (total_size - s->total_bytes))
+ size = total_size - s->total_bytes;
+ DBG (4, "sane_read: trying to read %d bytes\n", size);
+ status = read_data (s, s->buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_read: read_data failed: %s\n",
+ sane_strstatus (status));
+ *len = 0;
+ return status;
+ }
+ s->total_bytes += size;
+ s->buffer_start = s->buffer;
+ s->buffer_bytes = size;
+ }
+
+ *len = max_len;
+ if (*len > s->buffer_bytes)
+ *len = s->buffer_bytes;
+
+ memcpy (buf, s->buffer_start, *len);
+ s->buffer_start += (*len);
+ s->buffer_bytes -= (*len);
+ s->read_bytes += (*len);
+
+ /* invert for lineart mode */
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ for (i = 0; i < *len; i++)
+ buf[i] = ~buf[i];
+ }
+
+ DBG (4, "sane_read: read %d/%d bytes (%d bytes to go, %d total)\n", *len,
+ max_len, total_size - s->read_bytes, total_size);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Ma1509_Scanner *s = handle;
+
+ if (!s)
+ {
+ DBG (1, "sane_cancel: handle is null!\n");
+ return;
+ }
+
+ DBG (4, "sane_cancel\n");
+ if (s->scanning)
+ {
+ s->cancelled = SANE_TRUE;
+ stop_scan (s);
+ free (s->buffer);
+ }
+ s->scanning = SANE_FALSE;
+ DBG (4, "sane_cancel finished\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Ma1509_Scanner *s = handle;
+
+ if (!s)
+ {
+ DBG (1, "sane_set_io_mode: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "sane_set_io_mode: %s\n",
+ non_blocking ? "non-blocking" : "blocking");
+
+ if (!s->scanning)
+ {
+ DBG (1, "sane_set_io_mode: call sane_start before sane_set_io_mode");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Ma1509_Scanner *s = handle;
+
+ if (!s)
+ {
+ DBG (1, "sane_get_select_fd: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!fd)
+ {
+ DBG (1, "sane_get_select_fd: fd is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "sane_get_select_fd\n");
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/ma1509.conf.in b/backend/ma1509.conf.in
new file mode 100644
index 0000000..e3b1a93
--- /dev/null
+++ b/backend/ma1509.conf.in
@@ -0,0 +1,10 @@
+#ma1509.conf: see sane-ma1509(5)
+
+#Warm-up time for the lamp in seconds
+option warmup-time 30
+
+#Mustek BearPaw 1200F
+usb 0x055f 0x0010
+
+#Manual setting (e.g. for FreeBSD)
+#/dev/uscanner0
diff --git a/backend/ma1509.h b/backend/ma1509.h
new file mode 100644
index 0000000..455ca9c
--- /dev/null
+++ b/backend/ma1509.h
@@ -0,0 +1,177 @@
+/* sane - Scanner Access Now Easy.
+ (C) 2003 Henning Meier-Geinitz <henning@meier-geinitz.de>.
+
+ Based on the mustek (SCSI) backend.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for scanners based on the Mustek
+ MA-1509 chipset. Currently the Mustek BearPaw 1200F is known to work.
+*/
+
+#ifndef ma1509_h
+#define ma1509_h
+
+#include "../include/sane/config.h"
+#include <sys/types.h>
+
+/* Some constants */
+#define INQ_LEN 0x60 /* Length of SCSI inquiry */
+#define MA1509_COMMAND_LENGTH 8
+#define MA1509_GAMMA_SIZE 1024
+#define MA1509_BUFFER_SIZE (1024 * 128)
+#define MA1509_WARMUP_TIME 30
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MA1509_CONFIG_FILE "ma1509.conf"
+
+/* Convenience macros */
+#if defined(MIN)
+#undef MIN
+#endif
+#if defined(MAX)
+#undef MAX
+#endif
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+/* Copy values to memory ('L' = little endian, 'B' = big endian */
+#define STORE16L(cp,v) \
+do { \
+ int value = (v); \
+ \
+ *(cp)++ = (value >> 0) & 0xff; \
+ *(cp)++ = (value >> 8) & 0xff; \
+} while (0)
+#define STORE16B(cp,v) \
+do { \
+ int value = (v); \
+ \
+ *(cp)++ = (value >> 8) & 0xff; \
+ *(cp)++ = (value >> 0) & 0xff; \
+} while (0)
+#define STORE32B(cp,v) \
+do { \
+ long int value = (v); \
+ \
+ *(cp)++ = (value >> 24) & 0xff; \
+ *(cp)++ = (value >> 16) & 0xff; \
+ *(cp)++ = (value >> 8) & 0xff; \
+ *(cp)++ = (value >> 0) & 0xff; \
+} while (0)
+
+/* declarations */
+enum Ma1509_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_SOURCE,
+ OPT_PREVIEW,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_THRESHOLD,
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables? */
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+typedef struct Ma1509_Device
+{
+ struct Ma1509_Device *next;
+ SANE_String name;
+ SANE_Device sane;
+ SANE_Bool has_ta;
+ SANE_Bool has_adf;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ /* scan area when transparency adapter is used: */
+ SANE_Range x_trans_range;
+ SANE_Range y_trans_range;
+ /* values actually used by scanner, not necessarily the desired! */
+ SANE_Int bpl, ppl, lines;
+}
+Ma1509_Device;
+
+typedef struct Ma1509_Scanner
+{
+ /* all the state needed to define a scan request: */
+ struct Ma1509_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Bool scanning;
+ SANE_Bool cancelled;
+ SANE_Parameters params;
+
+ /* Parsed option values and variables that are valid only during
+ actual scanning: */
+ int fd; /* filedescriptor */
+ long start_time; /* at this time the scan started */
+ long lamp_time; /* at this time the lamp was turned on */
+ SANE_Word total_bytes; /* bytes read from scanner */
+ SANE_Word read_bytes; /* bytes transmitted by sane_read */
+
+ SANE_Int red_gamma_table[MA1509_GAMMA_SIZE];
+ SANE_Int green_gamma_table[MA1509_GAMMA_SIZE];
+ SANE_Int blue_gamma_table[MA1509_GAMMA_SIZE];
+
+ SANE_Byte *buffer, *buffer_start;
+ SANE_Int buffer_bytes;
+ /* scanner dependent/low-level state: */
+ Ma1509_Device *hw;
+}
+Ma1509_Scanner;
+
+#endif /* ma1509_h */
diff --git a/backend/magicolor.c b/backend/magicolor.c
new file mode 100644
index 0000000..e3cd80d
--- /dev/null
+++ b/backend/magicolor.c
@@ -0,0 +1,3006 @@
+/*
+ * magicolor.c - SANE library for Magicolor scanners.
+ *
+ * (C) 2010 Reinhold Kainhofer <reinhold@kainhofer.com>
+ *
+ * Based on the epson2 sane backend:
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for additional copyrights.
+ * Copyright (C) 2006-10 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#define MAGICOLOR_VERSION 0
+#define MAGICOLOR_REVISION 0
+#define MAGICOLOR_BUILD 1
+
+/* debugging levels:
+ *
+ * 127 mc_recv buffer
+ * 125 mc_send buffer
+ * 35 fine-grained status and progress
+ * 30 sane_read
+ * 25 setvalue, getvalue, control_option
+ * 20 low-level (I/O) mc_* functions
+ * 15 mid-level mc_* functions
+ * 10 high-level cmd_* functions
+ * 7 open/close/attach
+ * 6 print_params
+ * 5 basic functions
+ * 3 status info and progress
+ * 2 scanner info and capabilities
+ * 1 errors & warnings
+ */
+
+#include "sane/config.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <math.h>
+#include <poll.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+
+#if HAVE_LIBSNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/library/snmp_transport.h>
+#include <arpa/inet.h>
+#endif
+
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_tcp.h"
+#include "../include/sane/sanei_udp.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+#include "magicolor.h"
+
+
+#define min(x,y) (((x)<(y))?(x):(y))
+
+
+
+/****************************************************************************
+ * Devices supported by this backend
+ ****************************************************************************/
+
+
+
+/* Scanner command type
+ * | Start scan
+ * | | Poll for error
+ * | | | Stop scan?
+ * | | | | Query image parameters
+ * | | | | | set scan parameters
+ * | | | | | | Get status?
+ * | | | | | | | Read scanned data
+ * | | | | | | | | Unknown
+ * | | | | | | | | | Unknown
+ * | | | | | | | | | | Net wrapper command type
+ * | | | | | | | | | | | Net Welcome
+ * | | | | | | | | | | | | Net Lock
+ * | | | | | | | | | | | | | Net Lock ACK
+ * | | | | | | | | | | | | | | Net Unlock
+ * | | | | | | | | | | | | | | |
+*/
+static struct MagicolorCmd magicolor_cmd[] = {
+ {"mc1690mf", CMD, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, NET, 0x00, 0x01, 0x02, 0x03}
+};
+
+static SANE_Int magicolor_default_resolutions[] = {150, 300, 600};
+static SANE_Int magicolor_default_depths[] = {1,8};
+
+static struct MagicolorCap magicolor_cap[] = {
+
+ /* KONICA MINOLTA magicolor 1690MF, USB ID 0x123b:2089 */
+ {
+ 0x2089, "mc1690mf", "KONICA MINOLTA magicolor 1690MF", "1.3.6.1.4.1.183341.1.1.2.1.32.3.2",
+ -1, 0x85,
+ 600, {150, 600, 0}, magicolor_default_resolutions, 3, /* 600 dpi max, 3 resolutions */
+ 8, magicolor_default_depths, /* color depth 8 default, 1 and 8 possible */
+ {1, 9, 0}, /* brightness ranges (TODO!) */
+ {0, SANE_FIX(0x13f8 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x1b9c * MM_PER_INCH / 600), 0}, /* FBF x/y ranges (TODO!) */
+ SANE_TRUE, SANE_FALSE, /* non-duplex ADF, x/y ranges (TODO!) */
+ {0, SANE_FIX(0x1390 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x20dc * MM_PER_INCH / 600), 0},
+ }
+
+};
+
+static int MC_SNMP_Timeout = 2500;
+static int MC_Scan_Data_Timeout = 15000;
+static int MC_Request_Timeout = 5000;
+
+
+
+/****************************************************************************
+ * General configuration parameter definitions
+ ****************************************************************************/
+
+
+/*
+ * Definition of the mode_param struct, that is used to
+ * specify the valid parameters for the different scan modes.
+ *
+ * The depth variable gets updated when the bit depth is modified.
+ */
+
+static struct mode_param mode_params[] = {
+ {0x00, 1, 1}, /* Lineart, 1 color, 1 bit */
+ {0x02, 1, 24}, /* Grayscale, 1 color, 24 bit */
+ {0x03, 3, 24} /* Color, 3 colors, 24 bit */
+};
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static const SANE_String_Const adf_mode_list[] = {
+ SANE_I18N("Simplex"),
+ SANE_I18N("Duplex"),
+ NULL
+};
+
+/* Define the different scan sources */
+
+#define FBF_STR SANE_I18N("Flatbed")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+/*
+ * source list need one dummy entry (save device settings is crashing).
+ * NOTE: no const - this list gets created while exploring the capabilities
+ * of the scanner.
+ */
+
+static SANE_String_Const source_list[] = {
+ FBF_STR,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Some utility functions */
+
+static size_t
+max_string_size(const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++) {
+ size = strlen(strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status attach_one_usb(SANE_String_Const devname);
+static SANE_Status attach_one_net(SANE_String_Const devname, unsigned int device);
+
+static void
+print_params(const SANE_Parameters params)
+{
+ DBG(6, "params.format = %d\n", params.format);
+ DBG(6, "params.last_frame = %d\n", params.last_frame);
+ DBG(6, "params.bytes_per_line = %d\n", params.bytes_per_line);
+ DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line);
+ DBG(6, "params.lines = %d\n", params.lines);
+ DBG(6, "params.depth = %d\n", params.depth);
+}
+
+
+
+/****************************************************************************
+ * Low-level Network communication functions
+ ****************************************************************************/
+
+
+#define MAGICOLOR_SNMP_SYSDESCR_OID ".1.3.6.1.2.1.1.1.0"
+#define MAGICOLOR_SNMP_SYSOBJECT_OID ".1.3.6.1.2.1.1.2.0"
+#define MAGICOLOR_SNMP_MAC_OID ".1.3.6.1.2.1.2.2.1.6.1"
+#define MAGICOLOR_SNMP_DEVICE_TREE ".1.3.6.1.4.1.18334.1.1.1.1.1"
+
+
+/* We don't have a packet wrapper, which holds packet size etc., so we
+ don't have to use a *read_raw and a *_read function... */
+static int
+sanei_magicolor_net_read(struct Magicolor_Scanner *s, unsigned char *buf, size_t wanted,
+ SANE_Status * status)
+{
+ size_t size, read = 0;
+ struct pollfd fds[1];
+
+ *status = SANE_STATUS_GOOD;
+
+ /* poll for data-to-be-read (using a 5 seconds timeout) */
+ fds[0].fd = s->fd;
+ fds[0].events = POLLIN;
+ if (poll (fds, 1, MC_Request_Timeout) <= 0) {
+ *status = SANE_STATUS_IO_ERROR;
+ return read;
+ }
+
+ while (read < wanted) {
+ size = sanei_tcp_read(s->fd, buf + read, wanted - read);
+
+ if (size == 0)
+ break;
+
+ read += size;
+ }
+
+ if (read < wanted)
+ *status = SANE_STATUS_IO_ERROR;
+
+ return read;
+}
+
+/* We need to optionally pad the buffer with 0x00 to send 64-byte chunks.
+ On the other hand, the 0x04 commands don't need this, so we need two
+ functions, one *_write function that pads the buffer and then calls
+ *_write_raw */
+static int
+sanei_magicolor_net_write_raw(struct Magicolor_Scanner *s,
+ const unsigned char *buf, size_t buf_size,
+ SANE_Status *status)
+{
+ sanei_tcp_write(s->fd, buf, buf_size);
+ /* TODO: Check whether sending failed... */
+
+ *status = SANE_STATUS_GOOD;
+ return buf_size;
+}
+
+static int
+sanei_magicolor_net_write(struct Magicolor_Scanner *s,
+ const unsigned char *buf, size_t buf_size,
+ SANE_Status *status)
+{
+ size_t len = 64;
+ unsigned char *new_buf = malloc(len);
+ if (!new_buf) {
+ *status = SANE_STATUS_NO_MEM;
+ return 0;
+ }
+ memset(new_buf, 0x00, len);
+ if (buf_size > len)
+ buf_size = len;
+ if (buf_size)
+ memcpy(new_buf, buf, buf_size);
+ return sanei_magicolor_net_write_raw (s, new_buf, len, status);
+}
+
+static SANE_Status
+sanei_magicolor_net_open(struct Magicolor_Scanner *s)
+{
+ SANE_Status status;
+ unsigned char buf[5];
+
+ ssize_t read;
+ struct timeval tv;
+ struct MagicolorCmd *cmd = s->hw->cmd;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+ DBG(1, "%s\n", __func__);
+
+ /* the scanner sends a kind of welcome msg */
+ read = sanei_magicolor_net_read(s, buf, 3, &status);
+ if (read != 3)
+ return SANE_STATUS_IO_ERROR;
+ if (buf[0] != cmd->net_wrapper_cmd || buf[1] != cmd->net_welcome) {
+ DBG (32, "Invalid welcome message received, Expected 0x%02x %02x 00, but got 0x%02x %02x %02x\n",
+ cmd->net_wrapper_cmd, cmd->net_welcome, buf[0], buf[1], buf[2]);
+ return SANE_STATUS_IO_ERROR;
+ } else if (buf[2] != 0x00) {
+ /* TODO: Handle response "04 00 01", indicating an error! */
+ DBG (32, "Welcome message received, busy status %02x\n", buf[2]);
+ /* TODO: Return a human-readable error message (Unable to connect to scanner, scanner is not ready) */
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ buf[0] = cmd->net_wrapper_cmd;
+ buf[1] = cmd->net_lock;
+ buf[2] = 0x00;
+ /* Copy the device's USB id to bytes 3-4: */
+ buf[3] = s->hw->cap->id & 0xff;
+ buf[4] = (s->hw->cap->id >> 8) & 0xff;
+
+ DBG(32, "Proper welcome message received, locking the scanner...\n");
+ sanei_magicolor_net_write_raw(s, buf, 5, &status);
+
+ read = sanei_magicolor_net_read(s, buf, 3, &status);
+ if (read != 3)
+ return SANE_STATUS_IO_ERROR;
+ if (buf[0] != cmd->net_wrapper_cmd || buf[1] != cmd->net_lock_ack || buf[2] != 0x00) {
+ DBG (32, "Welcome message received, Expected 0x%x %x 00, but got 0x%x %x %x\n",
+ cmd->net_wrapper_cmd, cmd->net_lock_ack, buf[0], buf[1], buf[2]);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(32, "scanner locked\n");
+
+ return status;
+}
+
+static SANE_Status
+sanei_magicolor_net_close(struct Magicolor_Scanner *s)
+{
+ SANE_Status status;
+ struct MagicolorCmd *cmd = s->hw->cmd;
+ unsigned char buf[3];
+
+ DBG(1, "%s\n", __func__);
+ buf[0] = cmd->net_wrapper_cmd;
+ buf[1] = cmd->net_unlock;
+ buf[2] = 0x00;
+ sanei_magicolor_net_write_raw(s, buf, 3, &status);
+ return status;
+}
+
+
+
+/****************************************************************************
+ * Low-level USB communication functions
+ ****************************************************************************/
+
+#define SANE_MAGICOLOR_VENDOR_ID (0x132b)
+
+SANE_Word sanei_magicolor_usb_product_ids[] = {
+ 0x2089, /* magicolor 1690MF */
+ 0 /* last entry - this is used for devices that are specified
+ in the config file as "usb <vendor> <product>" */
+};
+
+static int
+sanei_magicolor_getNumberOfUSBProductIds (void)
+{
+ return sizeof (sanei_magicolor_usb_product_ids) / sizeof (SANE_Word);
+}
+
+
+
+
+/****************************************************************************
+ * Magicolor low-level communication commands
+ ****************************************************************************/
+
+static void dump_hex_buffer_dense (int level, const unsigned char *buf, size_t buf_size)
+{
+ size_t k;
+ char msg[1024], fmt_buf[1024];
+ memset (&msg[0], 0x00, 1024);
+ memset (&fmt_buf[0], 0x00, 1024);
+ for (k = 0; k < min(buf_size, 80); k++) {
+ if (k % 16 == 0) {
+ if (k>0) {
+ DBG (level, "%s\n", msg);
+ memset (&msg[0], 0x00, 1024);
+ }
+ sprintf (fmt_buf, " 0x%04lx ", (unsigned long)k);
+ strcat (msg, fmt_buf);
+ }
+ if (k % 8 == 0) {
+ strcat (msg, " ");
+ }
+ sprintf (fmt_buf, " %02x" , buf[k]);
+ strcat (msg, fmt_buf);
+ }
+ if (msg[0] != 0 ) {
+ DBG (level, "%s\n", msg);
+ }
+}
+
+/* Create buffers containing the command and arguments. Length of reserved
+ * buffer is returned. It's the caller's job to free the buffer! */
+static int mc_create_buffer (Magicolor_Scanner *s, unsigned char cmd_type, unsigned char cmd,
+ unsigned char **buf, unsigned char* arg1, size_t len1,
+ SANE_Status *status)
+{
+ unsigned char* b = NULL;
+ size_t buf_len = 2+4+len1+4;
+ NOT_USED (s);
+ if (len1 <= 0)
+ buf_len = 6; /* no args, just cmd + final 0x00 00 00 00 */
+ *buf = b = malloc (buf_len);
+ memset (b, 0x00, buf_len);
+ if (!b) {
+ *status = SANE_STATUS_NO_MEM;
+ return 0;
+ }
+ b[0] = cmd_type;
+ b[1] = cmd;
+ if (len1>0) {
+ b[2] = len1 & 0xff;
+ b[3] = (len1 >> 8) & 0xff;
+ b[4] = (len1 >> 16) & 0xff;
+ b[5] = (len1 >> 24) & 0xff;
+ if (arg1)
+ memcpy(b+6, arg1, len1);
+ }
+ /* Writing the final 0x00 00 00 00 is not necessary, they are 0x00 already */
+ *status = SANE_STATUS_GOOD;
+ return buf_len;
+}
+
+static int mc_create_buffer2 (Magicolor_Scanner *s, unsigned char cmd_type, unsigned char cmd,
+ unsigned char **buf, unsigned char* arg1, size_t len1,
+ unsigned char* arg2, size_t len2, SANE_Status *status)
+{
+ unsigned char* b = NULL;
+ size_t buf_len = 2+4+len1+4+len2+4;
+ /* If any of the two args has size 0, use the simpler mc_create_buffer */
+ if (len1<=0)
+ return mc_create_buffer (s, cmd_type, cmd, buf, arg2, len2, status);
+ else if (len2<=0)
+ return mc_create_buffer (s, cmd_type, cmd, buf, arg1, len1, status);
+ /* Allocate memory and copy over args and their lengths */
+ *buf = b = malloc (buf_len);
+ if (!b) {
+ *status = SANE_STATUS_NO_MEM;
+ return 0;
+ }
+ memset (b, 0x00, buf_len);
+ b[0] = cmd_type;
+ b[1] = cmd;
+ /* copy over the argument length in lower endian */
+ b[2] = len1 & 0xff;
+ b[3] = (len1 >> 8) & 0xff;
+ b[4] = (len1 >> 16) & 0xff;
+ b[5] = (len1 >> 24) & 0xff;
+ if (arg1) {
+ /* Copy the arguments */
+ memcpy(b+6, arg1, len1);
+ }
+ /* copy over the second argument length in little endian */
+ b[6+len1] = len2 & 0xff;
+ b[7+len1] = (len2 >> 8) & 0xff;
+ b[8+len1] = (len2 >> 16) & 0xff;
+ b[9+len1] = (len2 >> 24) & 0xff;
+ if (arg2) {
+ memcpy(b+10+len1, arg2, len2);
+ }
+ *status = SANE_STATUS_GOOD;
+ return buf_len;
+}
+
+static int
+mc_send(Magicolor_Scanner * s, void *buf, size_t buf_size, SANE_Status * status)
+{
+ DBG(15, "%s: size = %lu\n", __func__, (u_long) buf_size);
+
+ if (DBG_LEVEL >= 125) {
+ const unsigned char *s = buf;
+ DBG(125, "Cmd: 0x%02x %02x, complete buffer:\n", s[0], s[1]);
+ dump_hex_buffer_dense (125, s, buf_size);
+ }
+
+ if (s->hw->connection == SANE_MAGICOLOR_NET) {
+ return sanei_magicolor_net_write(s, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_MAGICOLOR_USB) {
+ size_t n;
+ n = buf_size;
+ *status = sanei_usb_write_bulk(s->fd, buf, &n);
+ DBG(125, "USB: wrote %lu bytes, status: %s\n", (unsigned long)n, sane_strstatus(*status));
+ return n;
+ }
+
+ *status = SANE_STATUS_INVAL;
+ return 0;
+ /* never reached */
+}
+
+static ssize_t
+mc_recv(Magicolor_Scanner * s, void *buf, ssize_t buf_size,
+ SANE_Status * status)
+{
+ ssize_t n = 0;
+
+ DBG(15, "%s: size = %ld, buf = %p\n", __func__, (long) buf_size, buf);
+
+ if (s->hw->connection == SANE_MAGICOLOR_NET) {
+ n = sanei_magicolor_net_read(s, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_MAGICOLOR_USB) {
+ /* !!! only report an error if we don't read anything */
+ n = buf_size; /* buf_size gets overwritten */
+ *status =
+ sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf,
+ (size_t *) & n);
+
+ if (n > 0)
+ *status = SANE_STATUS_GOOD;
+ }
+
+ if (n < buf_size) {
+ DBG(1, "%s: expected = %lu, got = %ld\n", __func__,
+ (u_long) buf_size, (long) n);
+ *status = SANE_STATUS_IO_ERROR;
+ }
+
+ /* dump buffer if appropriate */
+ if (DBG_LEVEL >= 127 && n > 0) {
+ const unsigned char* b=buf;
+ dump_hex_buffer_dense (125, b, buf_size);
+ }
+
+ return n;
+}
+
+/* Simple function to exchange a fixed amount of
+ * data with the scanner
+ */
+static SANE_Status
+mc_txrx(Magicolor_Scanner * s, unsigned char *txbuf, size_t txlen,
+ unsigned char *rxbuf, size_t rxlen)
+{
+ SANE_Status status;
+
+ mc_send(s, txbuf, txlen, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ mc_recv(s, rxbuf, rxlen, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status));
+ }
+
+ return status;
+}
+
+
+
+
+
+/****************************************************************************
+ * Magicolor high-level communication commands
+ ****************************************************************************/
+
+
+/** 0x03 09 01 - Request last error
+ * <- Information block (0x00 for OK, 0x01 for ERROR)
+ */
+static SANE_Status
+cmd_request_error (SANE_Handle handle)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params[1];
+ unsigned char *buf;
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+
+ if (s->hw->cmd->request_status == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_error,
+ &buf, NULL, 1, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ status = mc_txrx (s, buf, buflen, params, 1);
+ free(buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "status: %02x\n", params[0]);
+
+ switch (params[0]) {
+ case STATUS_READY:
+ DBG(1, " ready\n");
+ break;
+ case STATUS_ADF_JAM:
+ DBG(1, " paper jam in ADF\n");
+ return SANE_STATUS_JAMMED;
+ break;
+ case STATUS_OPEN:
+ DBG(1, " printer door open or waiting for button press\n");
+ return SANE_STATUS_COVER_OPEN;
+ break;
+ case STATUS_NOT_READY:
+ DBG(1, " scanner not ready (in use on another interface or warming up)\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ break;
+ default:
+ DBG(1, " unknown status 0x%x\n", params[0]);
+ }
+ return status;
+}
+
+/** 0x03 0d - Request status command */
+static SANE_Status
+cmd_request_status(SANE_Handle handle, unsigned char *b)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char *buf;
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+ if (!b) {
+ DBG(1, "%s called with NULL buffer\n", __func__);
+ return SANE_STATUS_INVAL;
+ }
+ memset (b, 0x00, 0x0b); /* initialize all 0x0b bytes with 0 */
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_status,
+ &buf, NULL, 0x0b, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ status = mc_txrx (s, buf, buflen, b, 0x0b);
+ free (buf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Status NOT successfully retrieved\n", __func__);
+ else {
+ DBG(8, "%s: Status successfully retrieved:\n", __func__);
+ /* TODO: debug output of the returned parameters... */
+ DBG (11, " ADF status: 0x%02x", b[1]);
+ if (b[1] & ADF_LOADED) {
+ DBG (11, " loaded\n");
+ } else {
+ DBG (11, " not loaded\n");
+ }
+ }
+ return status;
+}
+
+
+/** 0x03 08 - Start scan command */
+static SANE_Status
+cmd_start_scan (SANE_Handle handle, size_t value)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char params1[4], params2[1];
+ unsigned char *buf;
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+ /* Copy params to buffers */
+ /* arg1 is expected returned bytes per line, 4-byte little endian */
+ /* arg2 is unknown, seems to be always 0x00 */
+ params1[0] = value & 0xff;
+ params1[1] = (value >> 8) & 0xff;
+ params1[2] = (value >> 16) & 0xff;
+ params1[3] = (value >> 24) & 0xff;
+
+ params2[0] = 0x00;
+ buflen = mc_create_buffer2 (s, s->hw->cmd->scanner_cmd, s->hw->cmd->start_scanning,
+ &buf, params1, 4, params2, 1, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ mc_send(s, buf, buflen, &status);
+ free (buf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Data NOT successfully sent\n", __func__);
+ else
+ DBG(8, "%s: Data successfully sent\n", __func__);
+ return status;
+}
+
+/** 0x03 0a - Cancel(?) Scan command */
+/* TODO: Does this command really mean CANCEL??? */
+static SANE_Status
+cmd_cancel_scan (SANE_Handle handle)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char *buf;
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->stop_scanning,
+ &buf, NULL, 0, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ mc_send(s, buf, buflen, &status);
+ free (buf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Data NOT successfully sent\n", __func__);
+ else
+ DBG(8, "%s: Data successfully sent\n", __func__);
+ return status;
+}
+
+/** 0x03 12 - Finish(?) scan command */
+/* TODO: Does this command really mean FINISH??? */
+static SANE_Status
+cmd_finish_scan (SANE_Handle handle)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char *buf, returned[0x0b];
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->unknown2,
+ &buf, NULL, 0x0b, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ memset (&returned[0], 0x00, 0x0b);
+
+ status = mc_txrx (s, buf, buflen, returned, 0x0b);
+ free (buf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Data NOT successfully sent\n", __func__);
+ else
+ DBG(8, "%s: Data successfully sent\n", __func__);
+ return status;
+}
+
+/** 0x03 0b - Get scanning parameters command
+ * input buffer seems to be 0x00 always */
+static SANE_Status
+cmd_get_scanning_parameters(SANE_Handle handle,
+ SANE_Frame *format, SANE_Int *depth,
+ SANE_Int *data_pixels, SANE_Int *pixels_per_line,
+ SANE_Int *lines)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char *txbuf, rxbuf[8];
+ size_t buflen;
+ NOT_USED (format);
+ NOT_USED (depth);
+
+ DBG(8, "%s\n", __func__);
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd,
+ s->hw->cmd->request_scan_parameters,
+ &txbuf, NULL, 8, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ status = mc_txrx (s, txbuf, buflen, rxbuf, 8);
+ free (txbuf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Parameters NOT successfully retrieved\n", __func__);
+ else {
+ DBG(8, "%s: Parameters successfully retrieved\n", __func__);
+
+ /* Assign px_per_line and lines. Bytes 7-8 must match 3-4 */
+ if (rxbuf[2]!=rxbuf[6] || rxbuf[3]!=rxbuf[7]) {
+ DBG (1, "%s: ERROR: Returned image parameters indicate an "
+ "unsupported device: Bytes 3-4 do not match "
+ "bytes 7-8! Trying to continue with bytes 3-4.\n",
+ __func__);
+ dump_hex_buffer_dense (1, rxbuf, 8);
+ }
+ /* Read returned values, encoded in 2-byte little endian */
+ *data_pixels = rxbuf[1] * 0x100 + rxbuf[0];
+ *lines = rxbuf[3] * 0x100 + rxbuf[2];
+ *pixels_per_line = rxbuf[5] * 0x100 + rxbuf[4];
+ DBG (8, "%s: data_pixels = 0x%x (%u), lines = 0x%x (%u), "
+ "pixels_per_line = 0x%x (%u)\n", __func__,
+ *data_pixels, *data_pixels,
+ *lines, *lines,
+ *pixels_per_line, *pixels_per_line);
+ }
+
+ return status;
+}
+
+/** 0x03 0c - Set scanning parameters command */
+static SANE_Status
+cmd_set_scanning_parameters(SANE_Handle handle,
+ unsigned char resolution, unsigned char color_mode,
+ unsigned char brightness, unsigned char contrast,
+ int tl_x, int tl_y, int width, int height, unsigned char source)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char param[0x11];
+ unsigned char *buf;
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+ /* Copy over the params to the param byte array */
+ /* Byte structure:
+ * byte 0: resolution
+ * byte 1: color mode
+ * byte 2: brightness
+ * byte 3: 0xff
+ * byte 4-5: x-start
+ * byte 6-7: y-start
+ * byte 8-9: x-extent
+ * byte 10-11: y-extent
+ * byte 12: source (ADF/FBF)
+ **/
+ memset (&param[0], 0x00, 0x11);
+ param[0] = resolution;
+ param[1] = color_mode;
+ param[2] = brightness;
+ param[3] = contrast | 0xff; /* TODO: Always 0xff? What about contrast? */
+ /* Image coordinates are encoded 2-byte little endian: */
+ param[4] = tl_x & 0xff;
+ param[5] = (tl_x >> 8) & 0xff;
+ param[6] = tl_y & 0xff;
+ param[7] = (tl_y >> 8) & 0xff;
+ param[8] = width & 0xff;
+ param[9] = (width >> 8) & 0xff;
+ param[10] = height & 0xff;
+ param[11] = (height >> 8) & 0xff;
+
+ param[12] = source;
+
+ /* dump buffer if appropriate */
+ DBG (127, " Scanning parameter buffer:");
+ dump_hex_buffer_dense (127, param, 0x11);
+
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->set_scan_parameters,
+ &buf, param, 0x11, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ mc_send(s, buf, buflen, &status);
+ free (buf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Data NOT successfully sent\n", __func__);
+ else
+ DBG(8, "%s: Data successfully sent\n", __func__);
+ return status;
+}
+
+/** 0x03 ?? - Request push button status command */
+#if 0
+static SANE_Status
+cmd_request_push_button_status(SANE_Handle handle, unsigned char *bstatus)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char *buf;
+ size_t buflen;
+
+ DBG(8, "%s\n", __func__);
+
+
+ if (s->hw->cmd->unknown1 == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ DBG(8, "%s: Supported\n", __func__);
+ memset (bstatus, 0x00, 1);
+ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->unknown1,
+ &buf, bstatus, 1, &status);
+ if (buflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ status = mc_txrx (s, buf, buflen, bstatus, 1);
+ free(buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "push button status: %02x ", bstatus[0]);
+ switch (bstatus[0]) {
+ /* TODO: What's the response code for button pressed??? */
+ default:
+ DBG(1, " unknown\n");
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+ return status;
+}
+#endif
+
+/** 0x03 0e - Read data command */
+static SANE_Status
+cmd_read_data (SANE_Handle handle, unsigned char *buf, size_t len)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+ unsigned char *txbuf;
+ unsigned char param[4];
+ size_t txbuflen;
+ int oldtimeout = MC_Request_Timeout;
+
+ DBG(8, "%s\n", __func__);
+ param[0] = len & 0xff;
+ param[1] = (len >> 8) & 0xff;
+ param[2] = (len >> 16) & 0xff;
+ param[3] = (len >> 24) & 0xff;
+
+ txbuflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_data,
+ &txbuf, param, 4, &status);
+ if (txbuflen <= 0 ) {
+ return SANE_STATUS_NO_MEM;
+ } else if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ /* Temporarily set the poll timeout to 10 seconds instead of 2,
+ * because a color scan needs >5 seconds to initialize. */
+ MC_Request_Timeout = MC_Scan_Data_Timeout;
+ status = mc_txrx (s, txbuf, txbuflen, buf, len);
+ MC_Request_Timeout = oldtimeout;
+ free (txbuf);
+ if (status != SANE_STATUS_GOOD)
+ DBG(8, "%s: Image data NOT successfully retrieved\n", __func__);
+ else {
+ DBG(8, "%s: Image data successfully retrieved\n", __func__);
+ }
+
+ return status;
+}
+
+
+
+/* TODO: 0x03 0f command (unknown), 0x03 10 command (set button wait) */
+
+
+
+
+/****************************************************************************
+ * Magicolor backend high-level operations
+ ****************************************************************************/
+
+
+static void
+mc_dev_init(Magicolor_Device *dev, const char *devname, int conntype)
+{
+ DBG(5, "%s\n", __func__);
+
+ dev->name = NULL;
+ dev->model = NULL;
+ dev->connection = conntype;
+ dev->sane.name = devname;
+ dev->sane.model = NULL;
+ dev->sane.type = "flatbed scanner";
+ dev->sane.vendor = "Magicolor";
+ dev->cap = &magicolor_cap[MAGICOLOR_CAP_DEFAULT];
+ dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_DEFAULT];
+ /* Change default level when using a network connection */
+ if (dev->connection == SANE_MAGICOLOR_NET)
+ dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_NET];
+}
+
+static SANE_Status
+mc_dev_post_init(struct Magicolor_Device *dev)
+{
+ DBG(5, "%s\n", __func__);
+ NOT_USED (dev);
+ /* Correct device parameters if needed */
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+mc_set_model(Magicolor_Scanner * s, const char *model, size_t len)
+{
+ unsigned char *buf;
+ unsigned char *p;
+ struct Magicolor_Device *dev = s->hw;
+
+ buf = malloc(len + 1);
+ if (buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy(buf, model, len);
+ buf[len] = '\0';
+
+ p = &buf[len - 1];
+
+ while (*p == ' ') {
+ *p = '\0';
+ p--;
+ }
+
+ if (dev->model)
+ free(dev->model);
+
+ dev->model = strndup((const char *) buf, len);
+ dev->sane.model = dev->model;
+ DBG(10, "%s: model is '%s'\n", __func__, dev->model);
+
+ free(buf);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+mc_set_device (SANE_Handle handle, unsigned int device)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ Magicolor_Device *dev = s->hw;
+ const char* cmd_level;
+ int n;
+
+ DBG(1, "%s: 0x%x\n", __func__, device);
+
+ for (n = 0; n < NELEMS (magicolor_cap); n++) {
+ if (magicolor_cap[n].id == device)
+ break;
+ }
+ if (n < NELEMS(magicolor_cap)) {
+ dev->cap = &magicolor_cap[n];
+ } else {
+ dev->cap = &magicolor_cap[MAGICOLOR_CAP_DEFAULT];
+ DBG(1, " unknown device 0x%x, using default %s\n",
+ device, dev->cap->model);
+ }
+ mc_set_model (s, dev->cap->model, strlen (dev->cap->model));
+
+ cmd_level = dev->cap->cmds;
+ /* set command type and level */
+ for (n = 0; n < NELEMS(magicolor_cmd); n++) {
+ if (!strcmp(cmd_level, magicolor_cmd[n].level))
+ break;
+ }
+
+ if (n < NELEMS(magicolor_cmd)) {
+ dev->cmd = &magicolor_cmd[n];
+ } else {
+ dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_DEFAULT];
+ DBG(1, " unknown command level %s, using %s\n",
+ cmd_level, dev->cmd->level);
+ }
+}
+
+static SANE_Status
+mc_discover_capabilities(Magicolor_Scanner *s)
+{
+ SANE_Status status;
+ Magicolor_Device *dev = s->hw;
+
+ SANE_String_Const *source_list_add = source_list;
+
+ DBG(5, "%s\n", __func__);
+
+ /* always add flatbed */
+ *source_list_add++ = FBF_STR;
+ /* TODO: How can I check for existence of an ADF??? */
+ if (dev->cap->ADF)
+ *source_list_add++ = ADF_STR;
+
+ /* TODO: Is there any capability that we can extract from the
+ * device by some scanne command? So far, it looks like
+ * the device does not support any reporting. I don't even
+ * see a way to determine which device we are talking to!
+ */
+
+
+ /* request error status */
+ status = cmd_request_error(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ dev->x_range = &dev->cap->fbf_x_range;
+ dev->y_range = &dev->cap->fbf_y_range;
+
+ DBG(5, " x-range: %f %f\n", SANE_UNFIX(dev->x_range->min), SANE_UNFIX(dev->x_range->max));
+ DBG(5, " y-range: %f %f\n", SANE_UNFIX(dev->y_range->min), SANE_UNFIX(dev->y_range->max));
+
+ DBG(5, "End of %s, status:%s\n", __func__, sane_strstatus(status));
+ *source_list_add = NULL; /* add end marker to source list */
+ return status;
+}
+
+static SANE_Status
+mc_setup_block_mode (Magicolor_Scanner *s)
+{
+ /* block_len should always be a multiple of bytes_per_line, so
+ * we retrieve only whole lines at once */
+ s->block_len = (int)(0xff00/s->scan_bytes_per_line) * s->scan_bytes_per_line;
+ s->blocks = s->data_len / s->block_len;
+ s->last_len = s->data_len - (s->blocks * s->block_len);
+ if (s->last_len>0)
+ s->blocks++;
+ DBG(5, "%s: block_len=0x%x, last_len=0x%0x, blocks=%d\n", __func__, s->block_len, s->last_len, s->blocks);
+ s->counter = 0;
+ s->bytes_read_in_line = 0;
+ if (s->line_buffer)
+ free(s->line_buffer);
+ s->line_buffer = malloc(s->scan_bytes_per_line);
+ if (s->line_buffer == NULL) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+
+ DBG (5, " %s: Setup block mode - scan_bytes_per_line=%d, pixels_per_line=%d, depth=%d, data_len=%x, block_len=%x, blocks=%d, last_len=%x\n",
+ __func__, s->scan_bytes_per_line, s->params.pixels_per_line, s->params.depth, s->data_len, s->block_len, s->blocks, s->last_len);
+ return SANE_STATUS_GOOD;
+}
+
+/* Call the 0x03 0c command to set scanning parameters from the s->opt list */
+static SANE_Status
+mc_set_scanning_parameters(Magicolor_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char rs, source, brightness;
+ struct mode_param *mparam = &mode_params[s->val[OPT_MODE].w];
+ SANE_Int scan_pixels_per_line = 0;
+
+ DBG(1, "%s\n", __func__);
+
+ /* Find the resolution in the res list and assign the index to buf[1] */
+ for (rs=0; rs < s->hw->cap->res_list_size; rs++ ) {
+ if ( s->val[OPT_RESOLUTION].w == s->hw->cap->res_list[rs] )
+ break;
+ }
+
+ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap)) {
+ brightness = s->val[OPT_BRIGHTNESS].w;
+ } else {
+ brightness = 5;
+ }
+
+ /* ADF used? */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) {
+ /* Use ADF */
+ source = 0x01;
+ } else {
+ source = 0x00;
+ }
+
+ /* TODO: Any way to set PREVIEW??? */
+
+ /* Remaining bytes unused */
+ status = cmd_set_scanning_parameters(s,
+ rs, mparam->flags, /* res, color mode */
+ brightness, 0xff, /* brightness, contrast? */
+ s->left, s->top, /* top/left start */
+ s->width, s->height, /* extent */
+ source); /* source */
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (2, "%s: Command cmd_set_scanning_parameters failed, %s\n",
+ __func__, sane_strstatus(status));
+
+ /* Now query the scanner for the current image parameters */
+ status = cmd_get_scanning_parameters (s,
+ &s->params.format, &s->params.depth,
+ &scan_pixels_per_line,
+ &s->params.pixels_per_line, &s->params.lines);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (2, "%s: Command cmd_get_scanning_parameters failed, %s\n",
+ __func__, sane_strstatus(status));
+ return status;
+ }
+
+ /* Calculate how many bytes are really used per line */
+ s->params.bytes_per_line = ceil (s->params.pixels_per_line * s->params.depth / 8.0);
+ if (s->val[OPT_MODE].w == MODE_COLOR)
+ s->params.bytes_per_line *= 3;
+
+ /* Calculate how many bytes per line will be returned by the scanner.
+ * The values needed for this are returned by get_scannign_parameters */
+ s->scan_bytes_per_line = ceil (scan_pixels_per_line * s->params.depth / 8.0);
+ if (s->val[OPT_MODE].w == MODE_COLOR) {
+ s->scan_bytes_per_line *= 3;
+ }
+ s->data_len = s->params.lines * s->scan_bytes_per_line;
+
+ status = mc_setup_block_mode (s);
+ if (status != SANE_STATUS_GOOD)
+ DBG (2, "%s: Command mc_setup_block_mode failed, %s\n",
+ __func__, sane_strstatus(status));
+
+ DBG (1, "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line);
+
+ return status;
+}
+
+static SANE_Status
+mc_check_adf(Magicolor_Scanner * s)
+{
+ SANE_Status status;
+ unsigned char buf[0x0b];
+
+ DBG(5, "%s\n", __func__);
+
+ status = cmd_request_status(s, buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (!(buf[1] & ADF_LOADED))
+ return SANE_STATUS_NO_DOCS;
+
+ /* TODO: Check for jam in ADF */
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+mc_scan_finish(Magicolor_Scanner * s)
+{
+ SANE_Status status;
+ DBG(5, "%s\n", __func__);
+
+ /* If we have not yet read all data, cancel the scan */
+ if (s->buf && !s->eof)
+ status = cmd_cancel_scan (s);
+
+ if (s->line_buffer)
+ free (s->line_buffer);
+ s->line_buffer = NULL;
+ free(s->buf);
+ s->buf = s->end = s->ptr = NULL;
+
+ /* TODO: Any magicolor command for "scan finished"? */
+ status = cmd_finish_scan (s);
+
+ status = cmd_request_error(s);
+ if (status != SANE_STATUS_GOOD)
+ cmd_cancel_scan (s);
+ return status;
+
+ /* XXX required? */
+ /* TODO: cmd_reset(s);*/
+ return SANE_STATUS_GOOD;
+}
+
+static void
+mc_copy_image_data(Magicolor_Scanner * s, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+{
+ DBG (1, "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line);
+ if (s->params.format == SANE_FRAME_RGB) {
+ SANE_Int bytes_available, scan_pixels_per_line = s->scan_bytes_per_line/3;
+ *length = 0;
+
+ while ((max_length >= s->params.bytes_per_line) && (s->ptr < s->end)) {
+ SANE_Int bytes_to_copy = s->scan_bytes_per_line - s->bytes_read_in_line;
+ /* First, fill the line buffer for the current line: */
+ bytes_available = (s->end - s->ptr);
+ /* Don't copy more than we have buffer and available */
+ if (bytes_to_copy > bytes_available)
+ bytes_to_copy = bytes_available;
+
+ if (bytes_to_copy > 0) {
+ memcpy (s->line_buffer + s->bytes_read_in_line, s->ptr, bytes_to_copy);
+ s->ptr += bytes_to_copy;
+ s->bytes_read_in_line += bytes_to_copy;
+ }
+
+ /* We have filled as much as possible of the current line
+ * with data from the scanner. If we have a complete line,
+ * copy it over. */
+ if ((s->bytes_read_in_line >= s->scan_bytes_per_line) &&
+ (s->params.bytes_per_line <= max_length))
+ {
+ SANE_Int i;
+ SANE_Byte *line = s->line_buffer;
+ *length += s->params.bytes_per_line;
+ for (i=0; i< s->params.pixels_per_line; ++i) {
+ *data++ = line[0];
+ *data++ = line[scan_pixels_per_line];
+ *data++ = line[2 * scan_pixels_per_line];
+ line++;
+ }
+ max_length -= s->params.bytes_per_line;
+ s->bytes_read_in_line -= s->scan_bytes_per_line;
+ }
+ }
+
+ } else {
+ /* B/W and Grayscale use the same structure, so we use the same code */
+ SANE_Int bytes_available;
+ *length = 0;
+
+ while ((max_length != 0) && (s->ptr < s->end)) {
+ SANE_Int bytes_to_skip, bytes_to_copy;
+ bytes_available = (s->end - s->ptr);
+ bytes_to_copy = s->params.bytes_per_line - s->bytes_read_in_line;
+ bytes_to_skip = s->scan_bytes_per_line - s->bytes_read_in_line;
+
+ /* Don't copy more than we have buffer */
+ if (bytes_to_copy > max_length) {
+ bytes_to_copy = max_length;
+ bytes_to_skip = max_length;
+ }
+
+ /* Don't copy/skip more bytes than we have read in */
+ if (bytes_to_copy > bytes_available)
+ bytes_to_copy = bytes_available;
+ if (bytes_to_skip > bytes_available)
+ bytes_to_skip = bytes_available;
+
+ if (bytes_to_copy > 0) {
+ /* we have not yet copied all pixels of the line */
+ memcpy (data, s->ptr, bytes_to_copy);
+ max_length -= bytes_to_copy;
+ *length += bytes_to_copy;
+ data += bytes_to_copy;
+ }
+ if (bytes_to_skip > 0) {
+ s->ptr += bytes_to_skip;
+ s->bytes_read_in_line += bytes_to_skip;
+ }
+ if (s->bytes_read_in_line >= s->scan_bytes_per_line)
+ s->bytes_read_in_line -= s->scan_bytes_per_line;
+
+ }
+ }
+}
+
+static SANE_Status
+mc_init_parameters(Magicolor_Scanner * s)
+{
+ int dpi, optres;
+ struct mode_param *mparam;
+
+ DBG(5, "%s\n", __func__);
+
+ memset(&s->params, 0, sizeof(SANE_Parameters));
+
+ dpi = s->val[OPT_RESOLUTION].w;
+ optres = s->hw->cap->optical_res;
+
+ mparam = &mode_params[s->val[OPT_MODE].w];
+
+ if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 ||
+ SANE_UNFIX(s->val[OPT_BR_X].w) == 0)
+ return SANE_STATUS_INVAL;
+
+ /* TODO: Use OPT_RESOLUTION or fixed 600dpi for left/top/width/height? */
+ s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5;
+
+ s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5;
+
+ s->width =
+ ((SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5;
+
+ s->height =
+ ((SANE_UNFIX(s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5;
+
+ s->params.pixels_per_line = s->width * dpi / optres + 0.5;
+ s->params.lines = s->height * dpi / optres + 0.5;
+
+
+ DBG(1, "%s: resolution = %d, preview = %d\n",
+ __func__, dpi, s->val[OPT_PREVIEW].w);
+
+ DBG(1, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n",
+ __func__, (void *) s, (void *) s->val,
+ SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w),
+ SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w));
+
+ /*
+ * The default color depth is stored in mode_params.depth:
+ */
+ DBG(1, " %s, vor depth\n", __func__);
+
+ if (mode_params[s->val[OPT_MODE].w].depth == 1)
+ s->params.depth = 1;
+ else
+ s->params.depth = s->val[OPT_BIT_DEPTH].w;
+
+ s->params.last_frame = SANE_TRUE;
+
+ s->params.bytes_per_line = ceil (s->params.depth * s->params.pixels_per_line / 8.0);
+
+ switch (s->val[OPT_MODE].w) {
+ case MODE_BINARY:
+ case MODE_GRAY:
+ s->params.format = SANE_FRAME_GRAY;
+ break;
+ case MODE_COLOR:
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line *= 3;
+ break;
+ }
+
+
+ DBG(1, "%s: Parameters are format=%d, bytes_per_line=%d, lines=%d\n", __func__, s->params.format, s->params.bytes_per_line, s->params.lines);
+ return (s->params.lines > 0) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+mc_start_scan(Magicolor_Scanner * s)
+{
+ SANE_Status status = cmd_start_scan (s, s->data_len);
+ if (status != SANE_STATUS_GOOD ) {
+ DBG (1, "%s: starting the scan failed (%s)\n", __func__, sane_strstatus(status));
+ }
+ return status;
+}
+
+static SANE_Status
+mc_read(struct Magicolor_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ ssize_t buf_len = 0;
+
+ /* did we passed everything we read to sane? */
+ if (s->ptr == s->end) {
+
+ if (s->eof)
+ return SANE_STATUS_EOF;
+
+ s->counter++;
+ buf_len = s->block_len;
+
+ if (s->counter == s->blocks && s->last_len)
+ buf_len = s->last_len;
+
+ DBG(18, "%s: block %d/%d, size %lu\n", __func__,
+ s->counter, s->blocks,
+ (unsigned long) buf_len);
+
+ /* receive image data + error code */
+ status = cmd_read_data (s, s->buf, buf_len);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: Receiving image data failed (%s)\n",
+ __func__, sane_strstatus(status));
+ cmd_cancel_scan(s);
+ return status;
+ }
+
+ DBG(18, "%s: successfully read %lu bytes\n", __func__, (unsigned long) buf_len);
+
+ if (s->counter < s->blocks) {
+ if (s->canceling) {
+ cmd_cancel_scan(s);
+ return SANE_STATUS_CANCELLED;
+ }
+ } else
+ s->eof = SANE_TRUE;
+
+ s->end = s->buf + buf_len;
+ s->ptr = s->buf;
+ }
+
+ return status;
+}
+
+
+
+
+/****************************************************************************
+ * SANE API implementation (high-level functions)
+ ****************************************************************************/
+
+
+static struct MagicolorCap *
+mc_get_device_from_identification (const char*ident)
+{
+ int n;
+ for (n = 0; n < NELEMS (magicolor_cap); n++) {
+ if (strcmp (magicolor_cap[n].model, ident) || strcmp (magicolor_cap[n].OID, ident))
+ return &magicolor_cap[n];
+ }
+ return NULL;
+}
+
+
+/*
+ * close_scanner()
+ *
+ * Close the open scanner. Depending on the connection method, a different
+ * close function is called.
+ */
+static void
+close_scanner(Magicolor_Scanner *s)
+{
+ DBG(7, "%s: fd = %d\n", __func__, s->fd);
+
+ if (s->fd == -1)
+ return;
+
+ mc_scan_finish(s);
+ if (s->hw->connection == SANE_MAGICOLOR_NET) {
+ sanei_magicolor_net_close(s);
+ sanei_tcp_close(s->fd);
+ } else if (s->hw->connection == SANE_MAGICOLOR_USB) {
+ sanei_usb_close(s->fd);
+ }
+
+ s->fd = -1;
+}
+
+static SANE_Bool
+split_scanner_name (const char *name, char * IP, unsigned int *model)
+{
+ const char *device = name;
+ const char *qm;
+ *model = 0;
+ /* cut off leading net: */
+ if (strncmp(device, "net:", 4) == 0)
+ device = &device[4];
+
+ qm = strchr(device, '?');
+ if (qm != NULL) {
+ size_t len = qm-device;
+ strncpy (IP, device, len);
+ IP[len] = '\0';
+ qm++;
+ if (strncmp(qm, "model=", 6) == 0) {
+ qm += 6;
+ if (!sscanf(qm, "0x%x", model))
+ sscanf(qm, "%x", model);
+ }
+ } else {
+ strcpy (IP, device);
+ }
+ return SANE_TRUE;
+}
+
+/*
+ * open_scanner()
+ *
+ * Open the scanner device. Depending on the connection method,
+ * different open functions are called.
+ */
+
+static SANE_Status
+open_scanner(Magicolor_Scanner *s)
+{
+ SANE_Status status = 0;
+
+ DBG(7, "%s: %s\n", __func__, s->hw->sane.name);
+
+ if (s->fd != -1) {
+ DBG(7, "scanner is already open: fd = %d\n", s->fd);
+ return SANE_STATUS_GOOD; /* no need to open the scanner */
+ }
+
+ if (s->hw->connection == SANE_MAGICOLOR_NET) {
+ /* device name has the form net:ipaddr?model=... */
+ char IP[1024];
+ unsigned int model = 0;
+ if (!split_scanner_name (s->hw->sane.name, IP, &model))
+ return SANE_STATUS_INVAL;
+ status = sanei_tcp_open(IP, 4567, &s->fd);
+ if (model>0)
+ mc_set_device (s, model);
+ if (status == SANE_STATUS_GOOD) {
+ DBG(7, "awaiting welcome message\n");
+ status = sanei_magicolor_net_open (s);
+ }
+
+ } else if (s->hw->connection == SANE_MAGICOLOR_USB) {
+ status = sanei_usb_open(s->hw->sane.name, &s->fd);
+ if (s->hw->cap->out_ep>0)
+ sanei_usb_set_endpoint (s->fd,
+ USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, s->hw->cap->out_ep);
+ if (s->hw->cap->in_ep>0)
+ sanei_usb_set_endpoint (s->fd,
+ USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, s->hw->cap->in_ep);
+ }
+
+ if (status == SANE_STATUS_ACCESS_DENIED) {
+ DBG(1, "please check that you have permissions on the device.\n");
+ DBG(1, "if this is a multi-function device with a printer,\n");
+ DBG(1, "disable any conflicting driver (like usblp).\n");
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ DBG(1, "%s open failed: %s\n", s->hw->sane.name,
+ sane_strstatus(status));
+ else
+ DBG(3, "scanner opened\n");
+
+ return status;
+}
+
+static SANE_Status
+detect_usb(struct Magicolor_Scanner *s)
+{
+ SANE_Status status;
+ int vendor, product;
+ int i, numIds;
+ SANE_Bool is_valid;
+
+ /* if the sanei_usb_get_vendor_product call is not supported,
+ * then we just ignore this and rely on the user to config
+ * the correct device.
+ */
+
+ status = sanei_usb_get_vendor_product(s->fd, &vendor, &product);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "the device cannot be verified - will continue\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* check the vendor ID to see if we are dealing with an MAGICOLOR device */
+ if (vendor != SANE_MAGICOLOR_VENDOR_ID) {
+ /* this is not a supported vendor ID */
+ DBG(1, "not an Magicolor device at %s (vendor id=0x%x)\n",
+ s->hw->sane.name, vendor);
+ return SANE_STATUS_INVAL;
+ }
+
+ numIds = sanei_magicolor_getNumberOfUSBProductIds();
+ is_valid = SANE_FALSE;
+ i = 0;
+
+ /* check all known product IDs to verify that we know
+ * about the device */
+ while (i != numIds && !is_valid) {
+ if (product == sanei_magicolor_usb_product_ids[i])
+ is_valid = SANE_TRUE;
+ i++;
+ }
+
+ if (is_valid == SANE_FALSE) {
+ DBG(1, "the device at %s is not a supported (product id=0x%x)\n",
+ s->hw->sane.name, product);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(2, "found valid Magicolor scanner: 0x%x/0x%x (vendorID/productID)\n",
+ vendor, product);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a single-linked list of Magicolor_Device structs
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ */
+static int num_devices; /* number of scanners attached to backend */
+static Magicolor_Device *first_dev; /* first MAGICOLOR scanner in list */
+static const SANE_Device **devlist = NULL;
+
+static struct Magicolor_Scanner *
+scanner_create(struct Magicolor_Device *dev, SANE_Status *status)
+{
+ struct Magicolor_Scanner *s;
+
+ s = malloc(sizeof(struct Magicolor_Scanner));
+ if (s == NULL) {
+ *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ memset(s, 0x00, sizeof(struct Magicolor_Scanner));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ return s;
+}
+
+static struct Magicolor_Scanner *
+device_detect(const char *name, int type, SANE_Status *status)
+{
+ struct Magicolor_Scanner *s;
+ struct Magicolor_Device *dev;
+
+ /* try to find the device in our list */
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, name) == 0) {
+ dev->missing = 0;
+ DBG (10, "%s: Device %s already attached!\n", __func__,
+ name);
+ return scanner_create(dev, status);
+ }
+ }
+
+ if (type == SANE_MAGICOLOR_NODEV) {
+ *status = SANE_STATUS_INVAL;
+ return NULL;
+ }
+
+ /* alloc and clear our device structure */
+ dev = malloc(sizeof(*dev));
+ if (!dev) {
+ *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+ memset(dev, 0x00, sizeof(struct Magicolor_Device));
+
+ s = scanner_create(dev, status);
+ if (s == NULL)
+ return NULL;
+
+ mc_dev_init(dev, name, type);
+
+ *status = open_scanner(s);
+ if (*status != SANE_STATUS_GOOD) {
+ free(s);
+ return NULL;
+ }
+
+ /* from now on, close_scanner() must be called */
+
+ /* USB requires special care */
+ if (dev->connection == SANE_MAGICOLOR_USB) {
+ *status = detect_usb(s);
+ }
+
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ /* set name and model (if not already set) */
+ if (dev->model == NULL)
+ mc_set_model(s, "generic", 7);
+
+ dev->name = strdup(name);
+ dev->sane.name = dev->name;
+
+ *status = mc_discover_capabilities(s);
+ if (*status != SANE_STATUS_GOOD)
+ goto close;
+
+ if (source_list[0] == NULL || dev->cap->dpi_range.min == 0) {
+ DBG(1, "something is wrong in the discovery process, aborting.\n");
+ *status = SANE_STATUS_IO_ERROR;
+ goto close;
+ }
+
+ mc_dev_post_init(dev);
+
+ /* add this scanner to the device list */
+ num_devices++;
+ dev->missing = 0;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ return s;
+
+ close:
+ close_scanner(s);
+ free(s);
+ return NULL;
+}
+
+#if HAVE_LIBSNMP
+
+/* Keep a linked list of already observed IP addresses */
+/* typedef struct snmp_ip SNMP_IP; */
+typedef struct snmp_ip {
+ char ip_addr[1024];
+ struct snmp_ip*next;
+} snmp_ip;
+
+typedef struct {
+ int nr;
+ snmp_ip*handled;
+ snmp_ip*detected;
+} snmp_discovery_data;
+
+
+/** Handle one SNMP response (whether received sync or async) and if describes
+ * a magicolor device, attach it. Returns the number of attached devices (0
+ * or 1) */
+static int
+mc_network_discovery_handle (struct snmp_pdu *pdu, snmp_discovery_data *magic)
+{
+ netsnmp_variable_list *varlist = pdu->variables, *vp;
+ oid anOID[MAX_OID_LEN];
+ size_t anOID_len = MAX_OID_LEN;
+ /* Device information variables */
+ char ip_addr[1024];
+ char model[1024];
+ char device[1024];
+ /* remote IP detection variables */
+ netsnmp_indexed_addr_pair *responder = (netsnmp_indexed_addr_pair *) pdu->transport_data;
+ struct sockaddr_in *remote = NULL;
+ struct MagicolorCap *cap;
+ snmp_ip *ip = NULL;
+
+ DBG(5, "%s: Handling SNMP response \n", __func__);
+
+ if (responder == NULL || pdu->transport_data_length != sizeof(netsnmp_indexed_addr_pair )) {
+ DBG(1, "%s: Unable to extract IP address from SNMP response.\n",
+ __func__);
+ return 0;
+ }
+ remote = (struct sockaddr_in *) &(responder->remote_addr);
+ if (remote == NULL) {
+ DBG(1, "%s: Unable to extract IP address from SNMP response.\n",
+ __func__);
+ return 0;
+ }
+ snprintf(ip_addr, sizeof(ip_addr), "%s", inet_ntoa(remote->sin_addr));
+ DBG(35, "%s: IP Address of responder is %s\n", __func__, ip_addr);
+ if (magic)
+ ip = magic->handled;
+ while (ip) {
+ if (strcmp (ip->ip_addr, ip_addr) == 0) {
+ DBG (5, "%s: Already handled device %s, skipping\n", __func__, ip_addr);
+ return 0;
+ }
+ ip = ip->next;
+ }
+ if (magic) {
+ snmp_ip *new_handled = malloc(sizeof(snmp_ip));
+ strcpy (&new_handled->ip_addr[0], ip_addr);
+ new_handled->next = magic->handled;
+ magic->handled = new_handled;
+ }
+
+ /* System Object ID (Unique OID identifying model)
+ * This determines whether we really have a magicolor device */
+ anOID_len = MAX_OID_LEN;
+ read_objid(MAGICOLOR_SNMP_SYSOBJECT_OID, anOID, &anOID_len);
+ vp = find_varbind_in_list (varlist, anOID, anOID_len);
+ if (vp) {
+ size_t value_len = vp->val_len/sizeof(oid);
+ if (vp->type != ASN_OBJECT_ID) {
+ DBG (3, "%s: SystemObjectID does not return an OID, device is not a magicolor device\n", __func__);
+ return 0;
+ }
+ snprint_objid (device, sizeof(device), vp->val.objid, value_len);
+ DBG (5, "%s: Device object ID is '%s'\n", __func__, device);
+
+ anOID_len = MAX_OID_LEN;
+ read_objid (MAGICOLOR_SNMP_DEVICE_TREE, anOID, &anOID_len);
+ if (netsnmp_oid_is_subtree (anOID, anOID_len,
+ vp->val.objid, value_len) == 0) {
+ DBG (5, "%s: Device appears to be a magicolor device (OID=%s)\n", __func__, device);
+ } else {
+ DBG (5, "%s: Device is not a Magicolor device\n", __func__);
+ return 0;
+ }
+ }
+
+ /* Retrieve sysDescr (i.e. model name) */
+ anOID_len = MAX_OID_LEN;
+ read_objid(MAGICOLOR_SNMP_SYSDESCR_OID, anOID, &anOID_len);
+ vp = find_varbind_in_list (varlist, anOID, anOID_len);
+ if (vp) {
+ memcpy(model,vp->val.string,vp->val_len);
+ model[vp->val_len] = '\0';
+ DBG (5, "%s: Found model: %s\n", __func__, model);
+ }
+
+ DBG (1, "%s: Detected device '%s' on IP %s\n",
+ __func__, model, ip_addr);
+
+ vp = pdu->variables;
+ /* TODO: attach the IP with attach_one_net(ip) */
+ cap = mc_get_device_from_identification (device);
+ if (cap) {
+ DBG(1, "%s: Found autodiscovered device: %s (type 0x%x)\n", __func__, cap->model, cap->id);
+ attach_one_net (ip_addr, cap->id);
+ if (magic) {
+ snmp_ip *new_detected = malloc(sizeof(snmp_ip));
+ strcpy (&new_detected->ip_addr[0], ip_addr);
+ new_detected->next = magic->detected;
+ magic->detected = new_detected;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int
+mc_network_discovery_cb (int operation, struct snmp_session *sp, int reqid,
+ struct snmp_pdu *pdu, void *magic)
+{
+ NOT_USED (reqid);
+ NOT_USED (sp);
+ DBG(5, "%s: Received broadcast response \n", __func__);
+
+ if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
+ snmp_discovery_data *m = (snmp_discovery_data*)magic;
+ int nr = mc_network_discovery_handle (pdu, m);
+ m->nr += nr;
+ DBG(5, "%s: Added %d discovered host(s) for SNMP response.\n", __func__, nr);
+ }
+
+ return 0;
+}
+#endif
+
+/* Use SNMP for automatic network discovery. If host is given, try to detect
+ * that one host (using sync SNMP, otherwise send an SNMP broadcast (async).
+ */
+static int
+mc_network_discovery(const char*host)
+{
+#if HAVE_LIBSNMP
+ netsnmp_session session, *ss;
+ netsnmp_pdu *pdu;
+ oid anOID[MAX_OID_LEN];
+ size_t anOID_len = MAX_OID_LEN;
+ snmp_discovery_data magic;
+ magic.nr = 0;
+ magic.handled = 0;
+ magic.detected = 0;
+
+ DBG(1, "%s: running network discovery \n", __func__);
+
+ /* Win32: init winsock */
+ SOCK_STARTUP;
+ init_snmp("sane-magicolor-backend");
+ snmp_sess_init (&session);
+ session.version = SNMP_VERSION_2c;
+ session.community = "public";
+ session.community_len = strlen (session.community);
+ if (host) {
+ session.peername = host;
+ } else {
+ /* Do a network discovery via a broadcast */
+ session.peername = "255.255.255.255";
+ session.flags |= SNMP_FLAGS_UDP_BROADCAST;
+ session.callback = mc_network_discovery_cb;
+ session.callback_magic = &magic;
+ }
+
+ ss = snmp_open (&session); /* establish the session */
+ if (!ss) {
+ snmp_sess_perror ("ack", &session);
+ SOCK_CLEANUP;
+ return 0;
+ }
+
+ /* Create the PDU for the data for our request and add the three
+ * desired OIDs to the PDU */
+ pdu = snmp_pdu_create (SNMP_MSG_GET);
+
+ /* SNMPv2-MIB::sysDescr.0 */
+ anOID_len = MAX_OID_LEN;
+ if (read_objid(MAGICOLOR_SNMP_SYSDESCR_OID, anOID, &anOID_len)) {
+ snmp_add_null_var (pdu, anOID, anOID_len);
+ }
+ /* SNMPv2-MIB::sysObjectID.0 */
+ anOID_len = MAX_OID_LEN;
+ if (read_objid(MAGICOLOR_SNMP_SYSOBJECT_OID, anOID, &anOID_len)) {
+ snmp_add_null_var (pdu, anOID, anOID_len);
+ }
+ /* IF-MIB::ifPhysAddress.1 */
+ anOID_len = MAX_OID_LEN;
+ if (read_objid(MAGICOLOR_SNMP_MAC_OID, anOID, &anOID_len)) {
+ snmp_add_null_var (pdu, anOID, anOID_len);
+ }
+ /* TODO: Add more interesting OIDs, in particular vendor OIDs */
+
+ /* Now send out the request and wait for responses for some time.
+ * If we get a response, connect to that device (in the callback),
+ * otherwise we probably don't have a magicolor device in the
+ * LAN (or SNMP is turned off, in which case we have no way to detect
+ * it.
+ */
+ DBG(100, "%s: Sending SNMP packet\n", __func__);
+ if (host) {
+ /* sync request to given hostname, immediately read the reply */
+ netsnmp_pdu *response = 0;
+ int status = snmp_synch_response(ss, pdu, &response);
+ if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
+ magic.nr = mc_network_discovery_handle (response, &magic);
+ }
+ if (response)
+ snmp_free_pdu(response);
+
+
+ } else {
+ /* No hostname, so do a broadcast */
+ struct timeval nowtime, endtime; /* end time for SNMP scan */
+ struct timeval timeout;
+ int i=0;
+
+ if (!snmp_send(ss, pdu)) {
+ snmp_free_pdu(pdu);
+ DBG(100, "%s: Sending SNMP packet NOT successful\n", __func__);
+ return 0;
+ }
+ /* listen for responses for MC_AutoDetectionTimeout milliseconds: */
+ /* First get the final timeout time */
+ gettimeofday (&nowtime, NULL);
+ timeout.tv_sec = MC_SNMP_Timeout / 1000;
+ timeout.tv_usec = (MC_SNMP_Timeout % 1000) * 1000;
+ timeradd (&nowtime, &timeout, &endtime);
+
+ while (timercmp(&nowtime, &endtime, <)) {
+ int fds = 0, block = 0;
+ fd_set fdset;
+ DBG(1, " loop=%d\n", i++);
+ timeout.tv_sec = 0;
+ /* Use a 125ms timeout for select. If we get a response,
+ * the loop will be entered earlier again, anyway */
+ timeout.tv_usec = 125000;
+ FD_ZERO (&fdset);
+ snmp_select_info (&fds, &fdset, &timeout, &block);
+ fds = select (fds, &fdset, NULL, NULL, /*block?NULL:*/&timeout);
+ if (fds) snmp_read(&fdset);
+ else snmp_timeout();
+ gettimeofday(&nowtime, NULL);
+ }
+ /* Clean up the data in magic */
+ while (magic.handled) {
+ snmp_ip *tmp = magic.handled->next;
+ free (magic.handled);
+ magic.handled = tmp;
+ }
+ while (magic.detected) {
+ snmp_ip *tmp = magic.detected->next;
+ free (magic.detected);
+ magic.detected = tmp;
+ }
+ }
+
+ /* Clean up */
+ snmp_close(ss);
+ SOCK_CLEANUP;
+ DBG (5, "%s: Discovered %d host(s)\n", __func__, magic.nr);
+ return magic.nr;
+
+#else
+ DBG (1, "%s: net-snmp library not enabled, auto-detecting network scanners not supported.\n", __func__);
+ NOT_USED (host);
+ return 0;
+#endif
+}
+
+static SANE_Status
+attach(const char *name, int type)
+{
+ SANE_Status status;
+ Magicolor_Scanner *s;
+
+ DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type);
+
+ s = device_detect(name, type, &status);
+ if(s == NULL)
+ return status;
+
+ close_scanner(s);
+ free(s);
+ return status;
+}
+
+SANE_Status
+attach_one_usb(const char *dev)
+{
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ return attach(dev, SANE_MAGICOLOR_USB);
+}
+
+static SANE_Status
+attach_one_net(const char *dev, unsigned int model)
+{
+ char name[1024];
+
+ DBG(7, "%s: dev = %s\n", __func__, dev);
+ if (model > 0) {
+ snprintf(name, 1024, "net:%s?model=0x%x", dev, model);
+ } else {
+ snprintf(name, 1024, "net:%s", dev);
+ }
+
+ return attach(name, SANE_MAGICOLOR_NET);
+}
+
+static SANE_Status
+attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
+{
+ int vendor, product, timeout;
+
+ int len = strlen(line);
+
+ DBG(7, "%s: len = %d, line = %s\n", __func__, len, line);
+
+ if (sscanf(line, "usb %i %i", &vendor, &product) == 2) {
+ /* add the vendor and product IDs to the list of
+ * known devices before we call the attach function */
+
+ int numIds = sanei_magicolor_getNumberOfUSBProductIds();
+
+ if (vendor != SANE_MAGICOLOR_VENDOR_ID)
+ return SANE_STATUS_INVAL; /* this is not a KONICA MINOLTA device */
+
+ sanei_magicolor_usb_product_ids[numIds - 1] = product;
+ sanei_usb_attach_matching_devices(line, attach_one_usb);
+
+ } else if (strncmp(line, "usb", 3) == 0 && len == 3) {
+ int i, numIds;
+
+ numIds = sanei_magicolor_getNumberOfUSBProductIds();
+
+ for (i = 0; i < numIds; i++) {
+ sanei_usb_find_devices(SANE_MAGICOLOR_VENDOR_ID,
+ sanei_magicolor_usb_product_ids[i], attach_one_usb);
+ }
+
+ } else if (strncmp(line, "net", 3) == 0) {
+
+ /* remove the "net" sub string */
+ const char *name = sanei_config_skip_whitespace(line + 3);
+ char IP[1024];
+ unsigned int model = 0;
+
+ if (strncmp(name, "autodiscovery", 13) == 0) {
+ DBG (50, "%s: Initiating network autodiscovervy via SNMP\n", __func__);
+ mc_network_discovery(NULL);
+ } else if (sscanf(name, "%s %x", IP, &model) == 2) {
+ DBG(50, "%s: Using network device on IP %s, forcing model 0x%x\n", __func__, IP, model);
+ attach_one_net(IP, model);
+ } else {
+ /* use SNMP to detect the type. If not successful,
+ * add the host with model type 0 */
+ DBG(50, "%s: Using network device on IP %s, trying to autodetect model\n", __func__, IP);
+ if (mc_network_discovery(name)==0) {
+ DBG(1, "%s: Autodetecting device model failed, using default model\n", __func__);
+ attach_one_net(name, 0);
+ }
+ }
+
+ } else if (sscanf(line, "snmp-timeout %i\n", &timeout)) {
+ /* Timeout for SNMP network discovery */
+ DBG(50, "%s: SNMP timeout set to %d\n", __func__, timeout);
+ MC_SNMP_Timeout = timeout;
+
+ } else if (sscanf(line, "scan-data-timeout %i\n", &timeout)) {
+ /* Timeout for scan data requests */
+ DBG(50, "%s: Scan data timeout set to %d\n", __func__, timeout);
+ MC_Scan_Data_Timeout = timeout;
+
+ } else if (sscanf(line, "request-timeout %i\n", &timeout)) {
+ /* Timeout for all other read requests */
+ DBG(50, "%s: Request timeout set to %d\n", __func__, timeout);
+ MC_Request_Timeout = timeout;
+
+ } else {
+ /* TODO: Warning about unparsable line! */
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+free_devices(void)
+{
+ Magicolor_Device *dev, *next;
+
+ DBG(5, "%s\n", __func__);
+
+ for (dev = first_dev; dev; dev = next) {
+ next = dev->next;
+ free(dev->name);
+ free(dev->model);
+ free(dev);
+ }
+
+ if (devlist)
+ free(devlist);
+ devlist = NULL;
+ first_dev = NULL;
+}
+
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ DBG_INIT();
+ DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__);
+
+ DBG(1, "magicolor backend, version %i.%i.%i\n",
+ MAGICOLOR_VERSION, MAGICOLOR_REVISION, MAGICOLOR_BUILD);
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR,
+ MAGICOLOR_BUILD);
+
+ sanei_usb_init();
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Clean up the list of attached scanners. */
+void
+sane_exit(void)
+{
+ DBG(5, "%s\n", __func__);
+ free_devices();
+}
+
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Magicolor_Device *dev, *s, *prev=0;
+ int i;
+
+ DBG(5, "%s\n", __func__);
+
+ sanei_usb_init();
+
+ /* mark all existing scanners as missing, attach_one will remove mark */
+ for (s = first_dev; s; s = s->next) {
+ s->missing = 1;
+ }
+
+ /* Read the config, mark each device as found, possibly add new devs */
+ sanei_configure_attach(MAGICOLOR_CONFIG_FILE, NULL,
+ attach_one_config);
+
+ /*delete missing scanners from list*/
+ for (s = first_dev; s;) {
+ if (s->missing) {
+ DBG (5, "%s: missing scanner %s\n", __func__, s->name);
+
+ /*splice s out of list by changing pointer in prev to next*/
+ if (prev) {
+ prev->next = s->next;
+ free (s);
+ s = prev->next;
+ num_devices--;
+ } else {
+ /*remove s from head of list */
+ first_dev = s->next;
+ free(s);
+ s = first_dev;
+ prev=NULL;
+ num_devices--;
+ }
+ } else {
+ prev = s;
+ s = prev->next;
+ }
+ }
+
+ DBG (15, "%s: found %d scanner(s)\n", __func__, num_devices);
+ for (s = first_dev; s; s=s->next) {
+ DBG (15, "%s: found scanner %s\n", __func__, s->name);
+ }
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
+ if (!devlist) {
+ DBG(1, "out of memory (line %d)\n", __LINE__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(5, "%s - results:\n", __func__);
+
+ for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) {
+ DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model);
+ devlist[i] = &dev->sane;
+ }
+
+ devlist[i] = NULL;
+
+ if(device_list){
+ *device_list = devlist;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options(Magicolor_Scanner *s)
+{
+ int i;
+ SANE_Word *res_list;
+
+ for (i = 0; i < NUM_OPTIONS; i++) {
+ s->opt[i].size = sizeof(SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].name = SANE_NAME_STANDARD;
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_STANDARD;
+ s->opt[OPT_MODE_GROUP].desc = SANE_DESC_STANDARD;
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size(mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 0; /* Binary */
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->cap->depth_list;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1]; /* the first "real" element is the default */
+
+ if (s->hw->cap->depth_list[0] == 1) /* only one element in the list -> hide the option */
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cap->brightness;
+ s->val[OPT_BRIGHTNESS].w = 5; /* Normal */
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ res_list = malloc((s->hw->cap->res_list_size + 1) * sizeof(SANE_Word));
+ if (res_list == NULL) {
+ return SANE_STATUS_NO_MEM;
+ }
+ *(res_list) = s->hw->cap->res_list_size;
+ memcpy(&(res_list[1]), s->hw->cap->res_list, s->hw->cap->res_list_size * sizeof(SANE_Word));
+ s->opt[OPT_RESOLUTION].constraint.word_list = res_list;
+ s->val[OPT_RESOLUTION].w = s->hw->cap->dpi_range.min;
+
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size(source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */
+
+ s->opt[OPT_ADF_MODE].name = "adf-mode";
+ s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode");
+ s->opt[OPT_ADF_MODE].desc =
+ SANE_I18N("Selects the ADF mode (simplex/duplex)");
+ s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list);
+ s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
+ s->val[OPT_ADF_MODE].w = 0; /* simplex */
+ if ((!s->hw->cap->ADF) || (s->hw->cap->adf_duplex == SANE_FALSE))
+ s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
+
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *handle)
+{
+ SANE_Status status;
+ Magicolor_Scanner *s = NULL;
+
+ int l = strlen(name);
+
+ DBG(7, "%s: name = %s\n", __func__, name);
+
+ /* probe if empty device name provided */
+ if (l == 0) {
+
+ status = sane_get_devices(NULL,0);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ if (first_dev == NULL) {
+ DBG(1, "no device detected\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ s = device_detect(first_dev->sane.name, first_dev->connection,
+ &status);
+ if (s == NULL) {
+ DBG(1, "cannot open a perfectly valid device (%s),"
+ " please report to the authors\n", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ } else {
+
+ if (strncmp(name, "net:", 4) == 0) {
+ s = device_detect(name, SANE_MAGICOLOR_NET, &status);
+ if (s == NULL)
+ return status;
+ } else if (strncmp(name, "libusb:", 7) == 0) {
+ s = device_detect(name, SANE_MAGICOLOR_USB, &status);
+ if (s == NULL)
+ return status;
+ } else {
+
+ /* as a last resort, check for a match
+ * in the device list. This should handle platforms without libusb.
+ */
+ if (first_dev == NULL) {
+ status = sane_get_devices(NULL,0);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ }
+
+ s = device_detect(name, SANE_MAGICOLOR_NODEV, &status);
+ if (s == NULL) {
+ DBG(1, "invalid device name: %s\n", name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ }
+
+
+ /* s is always valid here */
+
+ DBG(1, "handle obtained\n");
+
+ init_options(s);
+
+ *handle = (SANE_Handle) s;
+
+ status = open_scanner(s);
+ if (status != SANE_STATUS_GOOD) {
+ free(s);
+ return status;
+ }
+
+ return status;
+}
+
+void
+sane_close(SANE_Handle handle)
+{
+ Magicolor_Scanner *s;
+
+ /*
+ * XXX Test if there is still data pending from
+ * the scanner. If so, then do a cancel
+ */
+
+ s = (Magicolor_Scanner *) handle;
+
+ if (s->fd != -1)
+ close_scanner(s);
+
+ free(s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return NULL;
+
+ return s->opt + option;
+}
+
+static const SANE_String_Const *
+search_string_list(const SANE_String_Const *list, SANE_String value)
+{
+ while (*list != NULL && strcmp(value, *list) != 0)
+ list++;
+
+ return ((*list == NULL) ? NULL : list);
+}
+
+/*
+ Activate, deactivate an option. Subroutines so we can add
+ debugging info if we want. The change flag is set to TRUE
+ if we changed an option. If we did not change an option,
+ then the value of the changed flag is not modified.
+*/
+
+static void
+activateOption(Magicolor_Scanner *s, SANE_Int option, SANE_Bool *change)
+{
+ if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
+ s->opt[option].cap &= ~SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+deactivateOption(Magicolor_Scanner *s, SANE_Int option, SANE_Bool *change)
+{
+ if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
+ s->opt[option].cap |= SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static SANE_Status
+getvalue(SANE_Handle handle, SANE_Int option, void *value)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ DBG(17, "%s: option = %d\n", __func__, option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ case OPT_BIT_DEPTH:
+ case OPT_BRIGHTNESS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *((SANE_Word *) value) = sval->w;
+ break;
+
+ case OPT_MODE:
+ case OPT_SOURCE:
+ case OPT_ADF_MODE:
+ strcpy((char *) value, sopt->constraint.string_list[sval->w]);
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ * Handles setting the source (flatbed, or auto document feeder (ADF)).
+ *
+ */
+
+static void
+change_source(Magicolor_Scanner *s, SANE_Int optindex, char *value)
+{
+ int force_max = SANE_FALSE;
+ SANE_Bool dummy;
+
+ DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex,
+ value);
+
+ if (s->val[OPT_SOURCE].w == optindex)
+ return;
+
+ s->val[OPT_SOURCE].w = optindex;
+
+ if (s->val[OPT_TL_X].w == s->hw->x_range->min
+ && s->val[OPT_TL_Y].w == s->hw->y_range->min
+ && s->val[OPT_BR_X].w == s->hw->x_range->max
+ && s->val[OPT_BR_Y].w == s->hw->y_range->max) {
+ force_max = SANE_TRUE;
+ }
+
+ if (strcmp(ADF_STR, value) == 0) {
+ s->hw->x_range = &s->hw->cap->adf_x_range;
+ s->hw->y_range = &s->hw->cap->adf_y_range;
+ if (s->hw->cap->adf_duplex) {
+ activateOption(s, OPT_ADF_MODE, &dummy);
+ } else {
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ s->val[OPT_ADF_MODE].w = 0;
+ }
+
+ DBG(1, "adf activated (%d)\n",s->hw->cap->adf_duplex);
+
+ } else {
+ /* ADF not active */
+ s->hw->x_range = &s->hw->cap->fbf_x_range;
+ s->hw->y_range = &s->hw->cap->fbf_y_range;
+
+ deactivateOption(s, OPT_ADF_MODE, &dummy);
+ }
+
+ s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
+ s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
+
+ if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
+ s->val[OPT_TL_X].w = s->hw->x_range->min;
+
+ if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
+ s->val[OPT_TL_Y].w = s->hw->y_range->min;
+
+ if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
+ s->val[OPT_BR_X].w = s->hw->x_range->max;
+
+ if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
+ s->val[OPT_BR_Y].w = s->hw->y_range->max;
+
+}
+
+static SANE_Status
+setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ SANE_Status status;
+ const SANE_String_Const *optval = NULL;
+ int optindex = 0;
+ SANE_Bool reload = SANE_FALSE;
+
+ DBG(17, "%s: option = %d, value = %p, as word: %d\n", __func__, option, value, *(SANE_Word *) value);
+
+ status = sanei_constrain_value(sopt, value, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (info && value && (*info & SANE_INFO_INEXACT)
+ && sopt->type == SANE_TYPE_INT)
+ DBG(17, "%s: constrained val = %d\n", __func__,
+ *(SANE_Word *) value);
+
+ if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) {
+ optval = search_string_list(sopt->constraint.string_list,
+ (char *) value);
+ if (optval == NULL)
+ return SANE_STATUS_INVAL;
+ optindex = optval - sopt->constraint.string_list;
+ }
+
+ switch (option) {
+
+ case OPT_MODE:
+ {
+ sval->w = optindex;
+ /* if binary, then disable the bit depth selection */
+ if (optindex == 0) {
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ } else {
+ if (s->hw->cap->depth_list[0] == 1)
+ s->opt[OPT_BIT_DEPTH].cap |=
+ SANE_CAP_INACTIVE;
+ else {
+ s->opt[OPT_BIT_DEPTH].cap &=
+ ~SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w =
+ mode_params[optindex].depth;
+ }
+ }
+ reload = SANE_TRUE;
+ break;
+ }
+
+ case OPT_BIT_DEPTH:
+ sval->w = *((SANE_Word *) value);
+ mode_params[s->val[OPT_MODE].w].depth = sval->w;
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_RESOLUTION:
+ sval->w = *((SANE_Word *) value);
+ DBG(17, "setting resolution to %d\n", sval->w);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ sval->w = *((SANE_Word *) value);
+ if (SANE_UNFIX(sval->w) == 0) {
+ DBG(17, "invalid br-x or br-y\n");
+ return SANE_STATUS_INVAL;
+ }
+ /* passthru */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ sval->w = *((SANE_Word *) value);
+ DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w));
+ if (NULL != info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_SOURCE:
+ change_source(s, optindex, (char *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_ADF_MODE:
+ sval->w = optindex; /* Simple lists */
+ break;
+
+ case OPT_BRIGHTNESS:
+ case OPT_PREVIEW: /* needed? */
+ sval->w = *((SANE_Word *) value);
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ if (reload && info != NULL)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ DBG(17, "%s: end\n", __func__);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *value, SANE_Int *info)
+{
+ DBG(17, "%s: action = %x, option = %d\n", __func__, action, option);
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ if (info != NULL)
+ *info = 0;
+
+ switch (action) {
+ case SANE_ACTION_GET_VALUE:
+ return getvalue(handle, option, value);
+
+ case SANE_ACTION_SET_VALUE:
+ return setvalue(handle, option, value, info);
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+
+ DBG(5, "%s\n", __func__);
+
+ if (params == NULL)
+ DBG(1, "%s: params is NULL\n", __func__);
+
+ /*
+ * If sane_start was already called, then just retrieve the parameters
+ * from the scanner data structure
+ */
+
+ if (!s->eof && s->ptr != NULL) {
+ DBG(5, "scan in progress, returning saved params structure\n");
+ } else {
+ /* otherwise initialize the params structure and gather the data */
+ mc_init_parameters(s);
+ }
+
+ if (params != NULL)
+ *params = s->params;
+
+ print_params(s->params);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function is part of the SANE API and gets called from the front end to
+ * start the scan process.
+ */
+
+SANE_Status
+sane_start(SANE_Handle handle)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+ SANE_Status status;
+
+ DBG(5, "%s\n", __func__);
+
+ /* calc scanning parameters */
+ status = mc_init_parameters(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ print_params(s->params);
+
+ /* set scanning parameters; also query the current image
+ * parameters from the sanner and save
+ * them to s->params */
+ status = mc_set_scanning_parameters(s);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* if we scan from ADF, check if it is loaded */
+ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) {
+ status = mc_check_adf(s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ /* prepare buffer here so that a memory allocation failure
+ * will leave the scanner in a sane state.
+ */
+ s->buf = realloc(s->buf, s->block_len);
+ if (s->buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ s->eof = SANE_FALSE;
+ s->ptr = s->end = s->buf;
+ s->canceling = SANE_FALSE;
+
+ /* start scanning */
+ DBG(1, "%s: scanning...\n", __func__);
+
+ status = mc_start_scan(s);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: start failed: %s\n", __func__,
+ sane_strstatus(status));
+
+ return status;
+ }
+
+ return status;
+}
+
+/* this moves data from our buffers to SANE */
+
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length,
+ SANE_Int *length)
+{
+ SANE_Status status;
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+
+ if (s->buf == NULL || s->canceling)
+ return SANE_STATUS_CANCELLED;
+
+ *length = 0;
+
+ status = mc_read(s);
+
+ if (status == SANE_STATUS_CANCELLED) {
+ mc_scan_finish(s);
+ return status;
+ }
+
+ DBG(18, "moving data %p %p, %d (%d lines)\n",
+ s->ptr, s->end,
+ max_length, max_length / s->params.bytes_per_line);
+
+ mc_copy_image_data(s, data, max_length, length);
+
+ DBG(18, "%d lines read, status: %d\n",
+ *length / s->params.bytes_per_line, status);
+
+ /* continue reading if appropriate */
+ if (status == SANE_STATUS_GOOD)
+ return status;
+
+ mc_scan_finish(s);
+
+ return status;
+}
+
+/*
+ * void sane_cancel(SANE_Handle handle)
+ *
+ * Set the cancel flag to true. The next time the backend requests data
+ * from the scanner the CAN message will be sent.
+ */
+
+void
+sane_cancel(SANE_Handle handle)
+{
+ Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
+
+ s->canceling = SANE_TRUE;
+}
+
+/*
+ * SANE_Status sane_set_io_mode()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_set_io_mode(SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/*
+ * SANE_Status sane_get_select_fd()
+ *
+ * not supported - for asynchronous I/O
+ */
+
+SANE_Status
+sane_get_select_fd(SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ *fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/magicolor.conf.in b/backend/magicolor.conf.in
new file mode 100644
index 0000000..9c25994
--- /dev/null
+++ b/backend/magicolor.conf.in
@@ -0,0 +1,42 @@
+### magicolor.conf
+###
+### here are some examples for how to configure the Magicolor backend
+
+### Timeout settings: SNMP autodetection, Scan data read requests and other
+### read requests. All values are given in milliseconds,
+### i.e. 1000 is 1 second.
+
+# SNMP auto-detection waits 1.5 seconds
+snmp-timeout 1500
+
+# wait 15 seconds for scan data (color scans take ~6 seconds to initialize,
+# so we need to wait longer than that)
+scan-data-timeout 15000
+
+# Wait 5 seconds for all other data requests
+request-timeout 5000
+
+
+### Network: Format is "net IP_ADDRESS [USB_ID]" or "net autodiscovery"
+### if USB_ID is left out, SNMP is used to detect the device type
+
+net autodiscovery
+#net 10.0.0.5
+
+### The following is a magicolor 1690mf with explicit IP-Address
+#net 10.0.0.5 0x2098
+# net 192.168.0.1
+
+### USB: format is "usb" for automatic (libusb) discovery, based on USB IDs,
+### or "usb <vendor ID> <device ID> to force the use of a particular
+### device (the backend has some additional checks and will not use
+### non-KONICA MINOLTA devices, though)
+
+usb
+
+### For libusb support for unknown scanners use the following command
+### usb <product ID> <device ID>
+### e.g.:
+
+# usb 0x132b 0x2098
+
diff --git a/backend/magicolor.h b/backend/magicolor.h
new file mode 100644
index 0000000..7ea1e1e
--- /dev/null
+++ b/backend/magicolor.h
@@ -0,0 +1,233 @@
+/*
+ * magicolor.h - SANE library for Magicolor scanners.
+ *
+ * (C) 2010 Reinhold Kainhofer <reinhold@kainhofer.com>
+ *
+ * Based on the epson2 sane backend:
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#ifndef magicolor_h
+#define magicolor_h
+
+#undef BACKEND_NAME
+#define BACKEND_NAME magicolor
+#define DEBUG_NOT_STATIC
+
+#include <sys/ioctl.h>
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+
+#ifdef __GNUC__
+#define __func__ __FUNCTION__
+#else
+#define __func__ "(undef)"
+/* I cast my vote for C99... :) */
+#endif
+
+/* Silence the compiler for unused arguments */
+#define NOT_USED(x) ( (void)(x) )
+
+#define MAGICOLOR_CONFIG_FILE "magicolor.conf"
+
+#define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */
+#define DEVICE_NAME_LEN (16) /* length of device name in extended status */
+
+
+
+/* misc constants */
+
+#define NET 0x04
+#define CMD 0x03
+
+
+/* status values */
+#define STATUS_READY 0x00 /* scanner is ready */
+#define STATUS_ADF_JAM 0x01 /* ADF paper jam */
+#define STATUS_OPEN 0x02 /* scanner is open */
+#define STATUS_NOT_READY 0x03 /* scanner is in use on another interface */
+
+#define ADF_LOADED 0x01 /* ADF is loaded */
+
+#define MAGICOLOR_CAP_DEFAULT 0
+
+#define MAGICOLOR_LEVEL_1690mf 0
+#define MAGICOLOR_LEVEL_DEFAULT MAGICOLOR_LEVEL_1690mf
+#define MAGICOLOR_LEVEL_NET MAGICOLOR_LEVEL_1690mf
+
+/* Structure holding the command set for a device */
+struct MagicolorCmd
+{
+ const char *level;
+ unsigned char scanner_cmd;
+ unsigned char start_scanning;
+ unsigned char request_error;
+ unsigned char stop_scanning;
+ unsigned char request_scan_parameters;
+ unsigned char set_scan_parameters;
+ unsigned char request_status;
+ unsigned char request_data;
+ unsigned char unknown1;
+ unsigned char unknown2;
+
+ unsigned char net_wrapper_cmd;
+ unsigned char net_welcome;
+ unsigned char net_lock;
+ unsigned char net_lock_ack;
+ unsigned char net_unlock;
+};
+
+/* Structure holding the device capabilities */
+struct MagicolorCap
+{
+ unsigned int id;
+ const char *cmds;
+ const char *model;
+ const char *OID;
+ SANE_Int out_ep, in_ep; /* USB bulk out/in endpoints */
+
+ SANE_Int optical_res; /* optical resolution */
+ SANE_Range dpi_range; /* max/min resolutions */
+
+ SANE_Int *res_list; /* list of resolutions */
+ SANE_Int res_list_size; /* number of entries in this list */
+
+ SANE_Int maxDepth; /* max. color depth */
+ SANE_Word *depth_list; /* list of color depths */
+
+ SANE_Range brightness; /* brightness range */
+
+ SANE_Range fbf_x_range; /* flattbed x range */
+ SANE_Range fbf_y_range; /* flattbed y range */
+
+ SANE_Bool ADF; /* ADF is installed */
+ SANE_Bool adf_duplex; /* does the ADF handle duplex scanning */
+ SANE_Range adf_x_range; /* autom. document feeder x range */
+ SANE_Range adf_y_range; /* autom. document feeder y range */
+};
+
+enum {
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_BIT_DEPTH,
+ OPT_BRIGHTNESS,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_SOURCE,
+ OPT_ADF_MODE,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ NUM_OPTIONS
+};
+
+typedef enum
+{ /* hardware connection to the scanner */
+ SANE_MAGICOLOR_NODEV, /* default, no HW specified yet */
+ SANE_MAGICOLOR_USB, /* USB interface */
+ SANE_MAGICOLOR_NET /* network interface */
+} Magicolor_Connection_Type;
+
+
+/* Structure holding the hardware description */
+
+struct Magicolor_Device
+{
+ struct Magicolor_Device *next;
+ int missing;
+
+ char *name;
+ char *model;
+
+ SANE_Device sane;
+
+ SANE_Range *x_range; /* x range w/out extension */
+ SANE_Range *y_range; /* y range w/out extension */
+
+ Magicolor_Connection_Type connection;
+
+ struct MagicolorCmd *cmd;
+ struct MagicolorCap *cap;
+};
+
+typedef struct Magicolor_Device Magicolor_Device;
+
+/* Structure holding an instance of a scanner (i.e. scanner has been opened) */
+struct Magicolor_Scanner
+{
+ struct Magicolor_Scanner *next;
+ struct Magicolor_Device *hw;
+
+ int fd;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+
+ SANE_Bool eof;
+ SANE_Byte *buf, *end, *ptr;
+ SANE_Bool canceling;
+
+ SANE_Int left, top;
+ SANE_Int width, height;
+
+ /* image block data */
+ SANE_Int data_len;
+ SANE_Int block_len;
+ SANE_Int last_len;
+ SANE_Int blocks;
+ SANE_Int counter;
+
+ /* store how many bytes of the current pixel line we have already
+ * read in previous read attempts. Since each line will be padded
+ * to multiples of 512 bytes, this is needed to know which bytes
+ * to ignore */
+ SANE_Int bytes_read_in_line;
+ SANE_Byte *line_buffer;
+ /* How many bytes are scanned per line (multiple of 512 bytes */
+ SANE_Int scan_bytes_per_line;
+};
+
+typedef struct Magicolor_Scanner Magicolor_Scanner;
+
+struct mode_param
+{
+ int flags;
+ int colors;
+ int depth;
+};
+
+enum {
+ MODE_BINARY, MODE_GRAY, MODE_COLOR
+};
+
+#endif
diff --git a/backend/matsushita.c b/backend/matsushita.c
new file mode 100644
index 0000000..e70c60a
--- /dev/null
+++ b/backend/matsushita.c
@@ -0,0 +1,2512 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002, 2004 Frank Zago (sane at zago dot net)
+ Copyright (C) 2002 Other SANE contributors
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ Matsushita/Panasonic KV-SS25, KV-SS50, KV-SS55, KV-SS50EX,
+ KV-SS55EX, KV-SS850, KV-SS855 SCSI scanners.
+
+ This backend may support more Panasonic scanners.
+*/
+
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 7 /* 2004-02-11 */
+#define BACKEND_NAME matsushita
+#define MATSUSHITA_CONFIG_FILE "matsushita.conf"
+
+/*--------------------------------------------------------------------------*/
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "matsushita.h"
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list_1[] = {
+ BLACK_WHITE_STR,
+ NULL
+};
+
+static SANE_String_Const scan_mode_list_3[] = {
+ BLACK_WHITE_STR,
+ GRAY4_STR,
+ GRAY8_STR,
+ NULL
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of supported resolutions (in DPI).
+ * 200 DPI scanners are using resolutions_list_200
+ * 300 DPI scanners are using resolutions_list_300
+ * 400 DPI scanners are using resolutions_list_400
+ *
+ * The resolutions_rounds_* lists provide the value with which round
+ * up the X value given by the interface.
+ */
+#ifdef unused_yet
+static const SANE_Word resolutions_list_200[4] = {
+ 3, 100, 150, 200
+};
+static const SANE_Word resolutions_rounds_200[4] = {
+ 3, 0x100, 0x40, 0x20
+};
+#endif
+
+static const SANE_Word resolutions_list_300[5] = {
+ 4, 150, 200, 240, 300
+};
+static const SANE_Word resolutions_rounds_300[5] = {
+ 4, 0x100, 0x40, 0x20, 0x80
+};
+
+static const SANE_Word resolutions_list_400[8] = {
+ 7, 100, 150, 200, 240, 300, 360, 400
+};
+static const SANE_Word resolutions_rounds_400[8] = {
+ 7, 0x100, 0x100, 0x40, 0x20, 0x80, 0x100, 0x100 /* TO FIX */
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of supported halftone. They are only valid with
+ * for the Black&White mode. */
+static SANE_String_Const halftone_pattern_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Bayer Dither 16"),
+ SANE_I18N ("Bayer Dither 64"),
+ SANE_I18N ("Halftone Dot 32"),
+ SANE_I18N ("Halftone Dot 64"),
+ SANE_I18N ("Error Diffusion"),
+ NULL
+};
+static const int halftone_pattern_val[] = {
+ -1,
+ 0x01,
+ 0x00,
+ 0x02,
+ 0x03,
+ 0x04
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of automatic threshold options */
+static SANE_String_Const automatic_threshold_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Mode 1"),
+ SANE_I18N ("Mode 2"),
+ SANE_I18N ("Mode 3"),
+ NULL
+};
+static const int automatic_threshold_val[] = {
+ 0,
+ 0x80,
+ 0x81,
+ 0x82
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of white level base. */
+static SANE_String_Const white_level_list[] = {
+ SANE_I18N ("From white stick"),
+ SANE_I18N ("From paper"),
+ SANE_I18N ("Automatic"),
+ NULL
+};
+static const int white_level_val[] = {
+ 0x00,
+ 0x80,
+ 0x81
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of noise reduction options. */
+static SANE_String_Const noise_reduction_list[] = {
+ SANE_I18N ("None"),
+ "1x1",
+ "2x2",
+ "3x3",
+ "4x4",
+ "5x5",
+ NULL
+};
+static const int noise_reduction_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of image emphasis options, 5 steps */
+static SANE_String_Const image_emphasis_list_5[] = {
+ SANE_I18N ("Smooth"),
+ SANE_I18N ("None"),
+ SANE_I18N ("Low"),
+ SANE_I18N ("Medium"), /* default */
+ SANE_I18N ("High"),
+ NULL
+};
+static const int image_emphasis_val_5[] = {
+ 0x80,
+ 0x00,
+ 0x01,
+ 0x30,
+ 0x50
+};
+
+/* List of image emphasis options, 3 steps */
+static SANE_String_Const image_emphasis_list_3[] = {
+ SANE_I18N ("Low"),
+ SANE_I18N ("Medium"), /* default ? */
+ SANE_I18N ("High"),
+ NULL
+};
+static const int image_emphasis_val_3[] = {
+ 0x01,
+ 0x30,
+ 0x50
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of gamma */
+static SANE_String_Const gamma_list[] = {
+ SANE_I18N ("Normal"),
+ SANE_I18N ("CRT"),
+ NULL
+};
+static const int gamma_val[] = {
+ 0x00,
+ 0x01
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Page feeder options */
+static SANE_String_Const feeder_mode_list[] = {
+ SANE_I18N ("One page"),
+ SANE_I18N ("All pages"),
+ NULL
+};
+static const int feeder_mode_val[] = {
+ 0x00,
+ 0xff
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Paper size in millimeters.
+ * Values from http://www.twics.com/~eds/paper/. */
+static const struct paper_sizes paper_sizes[] = {
+ {"2A0", 1189, 1682},
+ {"4A0", 1682, 2378},
+ {"A0", 841, 1189},
+ {"A1", 594, 841},
+ {"A2", 420, 594},
+ {"A3", 297, 420},
+ {"A4", 210, 297},
+ {"A5", 148, 210},
+ {"A6", 105, 148},
+ {"A7", 74, 105},
+ {"A8", 52, 74},
+ {"A9", 37, 52},
+ {"A10", 26, 37},
+ {"B0", 1000, 1414},
+ {"B1", 707, 1000},
+ {"B2", 500, 707},
+ {"B3", 353, 500},
+ {"B4", 250, 353},
+ {"B5", 176, 250},
+ {"B6", 125, 176},
+ {"B7", 88, 125},
+ {"B8", 62, 88},
+ {"B9", 44, 62},
+ {"B10", 31, 44},
+ {"C0", 917, 1297},
+ {"C1", 648, 917},
+ {"C2", 458, 648},
+ {"C3", 324, 458},
+ {"C4", 229, 324},
+ {"C5", 162, 229},
+ {"C6", 114, 162},
+ {"C7", 81, 114},
+ {"C8", 57, 81},
+ {"C9", 40, 57},
+ {"C10", 28, 40},
+ {"Legal", 8.5 * MM_PER_INCH, 14 * MM_PER_INCH},
+ {"Letter", 8.5 * MM_PER_INCH, 11 * MM_PER_INCH}
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define the supported scanners and their characteristics. */
+static const struct scanners_supported scanners[] = {
+
+ /* Panasonic KV-SS25 */
+ {
+ 0x06, "K.M.E. ", "KV-SS25A ",
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300,
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST |
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION},
+
+ /* Panasonic KV-SS25D */
+ {
+ 0x06, "K.M.E. ", "KV-SS25D ", /* TO FIX */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300,
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST |
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION},
+
+ /* Panasonic KV-SS50 */
+ {
+ 0x06, "K.M.E. ", "KV-SS50 ", /* TO FIX */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (14 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */
+ {1, 5, 1}, /* brightness range, TO FIX */
+ {0, 0, 0}, /* contrast range */
+ scan_mode_list_1,
+ resolutions_list_300, resolutions_rounds_300, /* TO FIX */
+ image_emphasis_list_3, image_emphasis_val_3,
+ MAT_CAP_PAPER_DETECT | MAT_CAP_MIRROR_IMAGE},
+
+ /* Panasonic KV-SS55 */
+ {
+ 0x06, "K.M.E. ", "KV-SS55 ", /* TO FIX */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (14 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */
+ {1, 5, 1}, /* brightness range, TO FIX */
+ {1, 255, 1}, /* contrast range, TO FIX */
+ scan_mode_list_1,
+ resolutions_list_300, resolutions_rounds_300, /* TO FIX */
+ image_emphasis_list_3, image_emphasis_val_3,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_PAPER_DETECT |
+ MAT_CAP_MIRROR_IMAGE},
+
+ /* Panasonic KV-SS50EX */
+ {
+ 0x06, "K.M.E. ", "KV-SS50EX ", /* TO FIX */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */
+ {1, 255, 1}, /* brightness range */
+ {0, 0, 0}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300, /* TO FIX */
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION | MAT_CAP_PAPER_DETECT | MAT_CAP_MIRROR_IMAGE},
+
+ /* Panasonic KV-SS55EX */
+ {
+ 0x06, "K.M.E. ", "KV-SS55EX ",
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300,
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST |
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION},
+
+ /* Panasonic KV-SS850 */
+ {
+ 0x06, "K.M.E. ", "KV-SS850 ", /* TO FIX */
+ {SANE_FIX (0), SANE_FIX (11.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */
+ {1, 255, 1}, /* brightness range */
+ {0, 0, 0}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300, /* TO FIX */
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL |
+ MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION | MAT_CAP_PAPER_DETECT |
+ MAT_CAP_DETECT_DOUBLE_FEED | MAT_CAP_MANUAL_FEED},
+
+ /* Panasonic KV-SS855 */
+ {
+ 0x06, "K.M.E. ", "KV-SS855 ", /* TO FIX */
+ {SANE_FIX (0), SANE_FIX (11.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range, TO FIX */
+ scan_mode_list_3,
+ resolutions_list_400, resolutions_rounds_400, /* TO FIX */
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD |
+ MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION |
+ MAT_CAP_PAPER_DETECT | MAT_CAP_DETECT_DOUBLE_FEED | MAT_CAP_MANUAL_FEED},
+
+ /* Panasonic KV-S2065L */
+ {
+ 0x06, "K.M.E. ", "KV-S2065L ",
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300,
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST |
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION},
+
+ /* Panasonic KV-S2025C */
+ {
+ 0x06, "K.M.E. ", "KV-S2025C ",
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300,
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST |
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION},
+
+ /* Panasonic KV-S2045C */
+ {
+ 0x06, "K.M.E. ", "KV-S2045C ",
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */
+ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */
+ {1, 255, 1}, /* brightness range */
+ {1, 255, 1}, /* contrast range */
+ scan_mode_list_3,
+ resolutions_list_300, resolutions_rounds_300,
+ image_emphasis_list_5, image_emphasis_val_5,
+ MAT_CAP_DUPLEX | MAT_CAP_CONTRAST |
+ MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA |
+ MAT_CAP_NOISE_REDUCTION}
+};
+
+
+/*--------------------------------------------------------------------------*/
+
+/* List of scanner attached. */
+static Matsushita_Scanner *first_dev = NULL;
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+
+
+/* Local functions. */
+
+/* Display a buffer in the log. */
+static void
+hexdump (int level, const char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+/* After the windows has been set, issue that command to get the
+ * document size. */
+static SANE_Status
+matsushita_read_document_size (Matsushita_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+
+ DBG (DBG_proc, "matsushita_read_document_size: enter\n");
+
+ size = 0x10;
+ MKSCSI_READ_10 (cdb, 0x80, 0, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD || size != 0x10)
+ {
+ DBG (DBG_error,
+ "matsushita_read_document_size: cannot read document size\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ hexdump (DBG_info2, "document size", dev->buffer, 16);
+
+ /* Check that X and Y are the same values the backend computed. */
+
+ assert (dev->params.lines == B32TOI (&dev->buffer[4]));
+ assert (dev->params.pixels_per_line == B32TOI (&dev->buffer[0]));
+
+ DBG (DBG_proc, "matsushita_read_document_size: exit, %ld bytes read\n",
+ (long)size);
+
+ return (SANE_STATUS_GOOD);
+}
+
+/* Initialize a scanner entry. Return an allocated scanner with some
+ * preset values. */
+static Matsushita_Scanner *
+matsushita_init (void)
+{
+ Matsushita_Scanner *dev;
+
+ DBG (DBG_proc, "matsushita_init: enter\n");
+
+ /* Allocate a new scanner entry. */
+ dev = malloc (sizeof (Matsushita_Scanner));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ memset (dev, 0, sizeof (Matsushita_Scanner));
+
+ /* Allocate the buffer used to transfer the SCSI data. */
+ dev->buffer_size = 64 * 1024;
+ dev->buffer = malloc (dev->buffer_size);
+ if (dev->buffer == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+ /* Allocate a buffer to store the temporary image. */
+ dev->image_size = 64 * 1024; /* enough for 1 line at max res */
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ free (dev->buffer);
+ free (dev);
+ return NULL;
+ }
+
+ dev->sfd = -1;
+
+ DBG (DBG_proc, "matsushita_init: exit\n");
+
+ return (dev);
+}
+
+/* Closes an open scanner. */
+static void
+matsushita_close (Matsushita_Scanner * dev)
+{
+ DBG (DBG_proc, "matsushita_close: enter\n");
+
+ if (dev->sfd != -1)
+ {
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+ }
+
+ DBG (DBG_proc, "matsushita_close: exit\n");
+}
+
+/* Frees the memory used by a scanner. */
+static void
+matsushita_free (Matsushita_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "matsushita_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ matsushita_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+ free (dev->paper_sizes_list);
+ free (dev->paper_sizes_val);
+
+ free (dev);
+
+ DBG (DBG_proc, "matsushita_free: exit\n");
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+matsushita_identify_scanner (Matsushita_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "matsushita_identify_scanner: enter\n");
+
+ size = 5;
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "matsushita_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ size = dev->buffer[4] + 5; /* total length of the inquiry data */
+
+ if (size < 36)
+ {
+ DBG (DBG_error,
+ "matsushita_identify_scanner: not enough data to identify device\n");
+ return (SANE_FALSE);
+ }
+
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "matsushita_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ hexdump (DBG_info2, "inquiry", dev->buffer, size);
+
+ dev->scsi_type = dev->buffer[0] & 0x1f;
+ memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08);
+ dev->scsi_vendor[0x08] = 0;
+ memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010);
+ dev->scsi_product[0x10] = 0;
+ memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04);
+ dev->scsi_version[0x04] = 0;
+
+ DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\"\n",
+ dev->scsi_vendor, dev->scsi_product, dev->scsi_version);
+
+ /* Lookup through the supported scanners table to find if this
+ * backend supports that one. */
+ for (i = 0; i < NELEMS (scanners); i++)
+ {
+ if (dev->scsi_type == scanners[i].scsi_type &&
+ strcmp (dev->scsi_vendor, scanners[i].scsi_vendor) == 0 &&
+ strcmp (dev->scsi_product, scanners[i].scsi_product) == 0)
+ {
+
+ DBG (DBG_error, "matsushita_identify_scanner: scanner supported\n");
+
+ dev->scnum = i;
+
+ return (SANE_TRUE);
+ }
+ }
+
+ DBG (DBG_proc, "matsushita_identify_scanner: exit, device not supported\n");
+
+ return (SANE_FALSE);
+}
+
+/* The interface can show different paper sizes. Show only the sizes
+ * available for that scanner. */
+static int
+matsushita_build_paper_sizes (Matsushita_Scanner * dev)
+{
+ SANE_String_Const *psl; /* string list */
+ int *psv; /* value list */
+ int num;
+ int i;
+
+ DBG (DBG_proc, "matsushita_build_paper_sizes: enter\n");
+
+ psl = malloc ((sizeof (SANE_String_Const) + 1) * NELEMS (paper_sizes));
+ if (psl == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ psv = malloc ((sizeof (int) + 1) * NELEMS (paper_sizes));
+ if (psv == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ free (psl);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0, num = 0; i < NELEMS (paper_sizes); i++)
+ {
+ if (SANE_UNFIX (scanners[dev->scnum].x_range.max) >=
+ paper_sizes[i].width
+ && SANE_UNFIX (scanners[dev->scnum].y_range.max) >=
+ paper_sizes[i].length)
+ {
+
+ /* This paper size fits into the scanner. */
+ psl[num] = paper_sizes[i].name;
+ psv[num] = i;
+ num++;
+ }
+ }
+ psl[num] = NULL; /* terminate the list */
+
+ dev->paper_sizes_list = psl;
+ dev->paper_sizes_val = psv;
+
+ DBG (DBG_proc, "matsushita_build_paper_sizes: exit (%d)\n", num);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+get_string_list_index (SANE_String_Const list[], SANE_String_Const name)
+{
+ int index;
+
+ index = 0;
+ while (list[index] != NULL)
+ {
+ if (strcmp (list[index], name) == 0)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "name %s not found in list\n", name);
+
+ assert (0 == 1); /* bug in backend, core dump */
+
+ return (-1);
+}
+
+/* Lookup an int list from one array and return its index. */
+static int
+get_int_list_index (const SANE_Word list[], const SANE_Word value)
+{
+ int index;
+ int size; /* number of elements */
+
+ index = 1;
+ size = list[0];
+ while (index <= size)
+ {
+ if (list[index] == value)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "word %d not found in list\n", value);
+
+ assert (0 == 1); /* bug in backend, core dump */
+
+ return (-1);
+}
+
+/* SCSI sense handler. Callback for SANE. */
+static SANE_Status
+matsushita_sense_handler (int scsi_fd, unsigned char *result, void __sane_unused__ *arg)
+{
+ int asc, ascq, sensekey;
+ int len;
+
+ DBG (DBG_proc, "matsushita_sense_handler (scsi_fd = %d)\n", scsi_fd);
+
+ sensekey = get_RS_sense_key (result);
+ len = 7 + get_RS_additional_length (result);
+
+ hexdump (DBG_info2, "sense", result, len);
+
+ if (get_RS_error_code (result) != 0x70)
+ {
+ DBG (DBG_error,
+ "matsushita_sense_handler: invalid sense key error code (%d)\n",
+ get_RS_error_code (result));
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (get_RS_ILI (result) != 0)
+ {
+ DBG (DBG_sense, "matsushita_sense_handler: short read\n");
+ }
+
+ if (len < 14)
+ {
+ DBG (DBG_error,
+ "matsushita_sense_handler: sense too short, no ASC/ASCQ\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ asc = get_RS_ASC (result);
+ ascq = get_RS_ASCQ (result);
+
+ DBG (DBG_sense, "matsushita_sense_handler: sense=%d, ASC/ASCQ=%02x%02x\n",
+ sensekey, asc, ascq);
+
+ switch (sensekey)
+ {
+ case 0x00: /* no sense */
+ if (get_RS_EOM (result) && asc == 0x00 && ascq == 0x00)
+ {
+ DBG (DBG_sense, "matsushita_sense_handler: EOF\n");
+ return SANE_STATUS_EOF;
+ }
+
+ return SANE_STATUS_GOOD;
+ break;
+
+ case 0x02: /* not ready */
+ if (asc == 0x04 && ascq == 0x81)
+ {
+ /* Jam door open. */
+ return SANE_STATUS_COVER_OPEN;
+ }
+ break;
+
+ case 0x03: /* medium error */
+ if (asc == 0x3a)
+ {
+ /* No paper in the feeder. */
+ return SANE_STATUS_NO_DOCS;
+ }
+ if (asc == 0x80)
+ {
+ /* Probably a paper jam. ascq might give more info. */
+ return SANE_STATUS_JAMMED;
+ }
+ break;
+
+ case 0x05:
+ if (asc == 0x20 || asc == 0x24 || asc == 0x26)
+ {
+ /* Invalid command, invalid field in CDB or invalid field in data.
+ * The backend has prepared some wrong combination of options.
+ * Shot the backend maintainer. */
+ return SANE_STATUS_IO_ERROR;
+ }
+ else if (asc == 0x2c && ascq == 0x80)
+ {
+ /* The scanner does have enough memory to scan the whole
+ * area. For instance the KV-SS25 has only 4MB of memory,
+ * which is not enough to scan a A4 page at 300dpi in gray
+ * 8 bits. */
+ return SANE_STATUS_NO_MEM;
+ }
+ break;
+
+ case 0x06:
+ if (asc == 0x29)
+ {
+ /* Reset occured. May be the backend should retry the
+ * command. */
+ return SANE_STATUS_GOOD;
+ }
+ break;
+ }
+
+ DBG (DBG_sense,
+ "matsushita_sense_handler: unknown error condition. Please report it to the backend maintainer\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Check that a new page is available by issuing an empty read. The
+ * sense handler might return SANE_STATUS_NO_DOCS which indicates that
+ * the feeder is now empty. */
+static SANE_Status
+matsushita_check_next_page (Matsushita_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "matsushita_check_next_page: enter\n");
+
+ MKSCSI_READ_10 (cdb, 0, 0, 0);
+ cdb.data[4] = dev->page_num; /* May be cdb.data[3] too? */
+ cdb.data[5] = dev->page_side;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "matsushita_check_next_page: exit with status %d\n", status);
+
+ return (status);
+}
+
+/* Attach a scanner to this backend. */
+static SANE_Status
+attach_scanner (const char *devicename, Matsushita_Scanner ** devp)
+{
+ Matsushita_Scanner *dev;
+ int sfd;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new scanner entry. */
+ dev = matsushita_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+ if (sanei_scsi_open (devicename, &sfd, matsushita_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_scanner: open failed\n");
+ matsushita_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Now, check that it is a scanner we support. */
+ if (matsushita_identify_scanner (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ matsushita_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ matsushita_close (dev);
+
+ /* Set the default options for that scanner. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = "Panasonic";
+ dev->sane.model = dev->scsi_product;
+ dev->sane.type = SANE_I18N ("sheetfed scanner");
+
+ /* Link the scanner with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_scanner: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that scanner. */
+static void
+matsushita_init_options (Matsushita_Scanner * dev)
+{
+ int i;
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size =
+ max_string_size (scanners[dev->scnum].scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list =
+ scanners[dev->scnum].scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list =
+ scanners[dev->scnum].resolutions_list;
+ dev->val[OPT_RESOLUTION].w = resolutions_list_300[1];
+
+ /* Duplex */
+ dev->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
+ dev->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
+ dev->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
+ dev->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_DUPLEX].unit = SANE_UNIT_NONE;
+ dev->val[OPT_DUPLEX].w = SANE_FALSE;
+ if ((scanners[dev->scnum].cap & MAT_CAP_DUPLEX) == 0)
+ dev->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+
+ /* Feeder mode */
+ dev->opt[OPT_FEEDER_MODE].name = "feeder-mode";
+ dev->opt[OPT_FEEDER_MODE].title = SANE_I18N ("Feeder mode");
+ dev->opt[OPT_FEEDER_MODE].desc = SANE_I18N ("Sets the feeding mode");
+ dev->opt[OPT_FEEDER_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_FEEDER_MODE].size = max_string_size (feeder_mode_list);
+ dev->opt[OPT_FEEDER_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_FEEDER_MODE].constraint.string_list = feeder_mode_list;
+ dev->val[OPT_FEEDER_MODE].s = strdup (feeder_mode_list[0]);
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Paper sizes list. */
+ dev->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
+ dev->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
+ dev->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
+ dev->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_PAPER_SIZE].size = max_string_size (dev->paper_sizes_list);
+ dev->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_PAPER_SIZE].constraint.string_list = dev->paper_sizes_list;
+ dev->val[OPT_PAPER_SIZE].s = strdup (""); /* will do it later */
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &(scanners[dev->scnum].x_range);
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &(scanners[dev->scnum].y_range);
+
+ /* Bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &(scanners[dev->scnum].x_range);
+
+ /* Bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &(scanners[dev->scnum].y_range);
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Brightness */
+ dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int);
+ dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BRIGHTNESS].constraint.range =
+ &(scanners[dev->scnum].brightness_range);
+ dev->val[OPT_BRIGHTNESS].w = 128;
+
+ /* Contrast */
+ dev->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ dev->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ dev->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ dev->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ dev->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_CONTRAST].size = sizeof (SANE_Int);
+ dev->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_CONTRAST].constraint.range =
+ &(scanners[dev->scnum].contrast_range);
+ dev->val[OPT_CONTRAST].w = 128;
+ if ((scanners[dev->scnum].cap & MAT_CAP_CONTRAST) == 0)
+ dev->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ /* Automatic threshold */
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].name = "automatic-threshold";
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].title = SANE_I18N ("Automatic threshold");
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].desc =
+ SANE_I18N
+ ("Automatically sets brightness, contrast, white level, gamma, noise reduction and image emphasis");
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].type = SANE_TYPE_STRING;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].size =
+ max_string_size (automatic_threshold_list);
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint.string_list =
+ automatic_threshold_list;
+ dev->val[OPT_AUTOMATIC_THRESHOLD].s = strdup (automatic_threshold_list[0]);
+ if ((scanners[dev->scnum].cap & MAT_CAP_AUTOMATIC_THRESHOLD) == 0)
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ /* Halftone pattern */
+ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ dev->opt[OPT_HALFTONE_PATTERN].size =
+ max_string_size (halftone_pattern_list);
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
+ halftone_pattern_list;
+ dev->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]);
+
+ /* Automatic separation */
+ dev->opt[OPT_AUTOMATIC_SEPARATION].name = SANE_NAME_AUTOSEP;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].title = SANE_TITLE_AUTOSEP;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].desc = SANE_DESC_AUTOSEP;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].unit = SANE_UNIT_NONE;
+ dev->val[OPT_AUTOMATIC_SEPARATION].w = SANE_FALSE;
+
+ /* White level base */
+ dev->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
+ dev->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
+ dev->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL;
+ dev->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_STRING;
+ dev->opt[OPT_WHITE_LEVEL].size = max_string_size (white_level_list);
+ dev->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_WHITE_LEVEL].constraint.string_list = white_level_list;
+ dev->val[OPT_WHITE_LEVEL].s = strdup (white_level_list[0]);
+ if ((scanners[dev->scnum].cap & MAT_CAP_WHITE_LEVEL) == 0)
+ dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE;
+
+ /* Noise reduction */
+ dev->opt[OPT_NOISE_REDUCTION].name = "noise-reduction";
+ dev->opt[OPT_NOISE_REDUCTION].title = SANE_I18N ("Noise reduction");
+ dev->opt[OPT_NOISE_REDUCTION].desc =
+ SANE_I18N ("Reduce the isolated dot noise");
+ dev->opt[OPT_NOISE_REDUCTION].type = SANE_TYPE_STRING;
+ dev->opt[OPT_NOISE_REDUCTION].size = max_string_size (noise_reduction_list);
+ dev->opt[OPT_NOISE_REDUCTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_NOISE_REDUCTION].constraint.string_list = noise_reduction_list;
+ dev->val[OPT_NOISE_REDUCTION].s = strdup (noise_reduction_list[0]);
+ if ((scanners[dev->scnum].cap & MAT_CAP_NOISE_REDUCTION) == 0)
+ dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE;
+
+ /* Image emphasis */
+ dev->opt[OPT_IMAGE_EMPHASIS].name = "image-emphasis";
+ dev->opt[OPT_IMAGE_EMPHASIS].title = SANE_I18N ("Image emphasis");
+ dev->opt[OPT_IMAGE_EMPHASIS].desc = SANE_I18N ("Sets the image emphasis");
+ dev->opt[OPT_IMAGE_EMPHASIS].type = SANE_TYPE_STRING;
+ dev->opt[OPT_IMAGE_EMPHASIS].size =
+ max_string_size (scanners[dev->scnum].image_emphasis_list);
+ dev->opt[OPT_IMAGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_IMAGE_EMPHASIS].constraint.string_list =
+ scanners[dev->scnum].image_emphasis_list;
+ dev->val[OPT_IMAGE_EMPHASIS].s = strdup (SANE_I18N ("Medium"));
+
+ /* Gamma */
+ dev->opt[OPT_GAMMA].name = "gamma";
+ dev->opt[OPT_GAMMA].title = SANE_I18N ("Gamma");
+ dev->opt[OPT_GAMMA].desc = SANE_I18N ("Gamma");
+ dev->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ dev->opt[OPT_GAMMA].size = max_string_size (gamma_list);
+ dev->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_GAMMA].constraint.string_list = gamma_list;
+ dev->val[OPT_GAMMA].s = strdup (gamma_list[0]);
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+ sane_control_option (dev, OPT_PAPER_SIZE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) dev->paper_sizes_list[0], NULL);
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scanners[dev->scnum].
+ scan_mode_list[0], NULL);
+}
+
+/* Wait until the scanner is ready.
+ *
+ * The only reason I know the scanner is not ready is because it is
+ * moving the CCD.
+ */
+static SANE_Status
+matsushita_wait_scanner (Matsushita_Scanner * dev)
+{
+ SANE_Status status;
+ int timeout;
+ CDB cdb;
+
+ DBG (DBG_proc, "matsushita_wait_scanner: enter\n");
+
+ MKSCSI_TEST_UNIT_READY (cdb);
+
+ /* Set the timeout to 60 seconds. */
+ timeout = 60;
+
+ while (timeout > 0)
+ {
+
+ /* test unit ready */
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, NULL, NULL);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ };
+
+ DBG (DBG_proc, "matsushita_wait_scanner: scanner not ready\n");
+ return (SANE_STATUS_IO_ERROR);
+}
+
+/* Reset a window. This is used to re-initialize the scanner. */
+static SANE_Status
+matsushita_reset_window (Matsushita_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "matsushita_reset_window: enter\n");
+
+ MKSCSI_SET_WINDOW (cdb, 0);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "matsushita_reset_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Set a window. */
+static SANE_Status
+matsushita_set_window (Matsushita_Scanner * dev, int side)
+{
+ size_t size;
+ CDB cdb;
+ unsigned char window[72];
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "matsushita_set_window: enter\n");
+
+ size = sizeof (window);
+ MKSCSI_SET_WINDOW (cdb, size);
+
+ memset (window, 0, size);
+
+ /* size of the windows descriptor block */
+ window[7] = sizeof (window) - 8;
+
+ /* Page side */
+ window[8] = side;
+
+ /* X and Y resolution */
+ Ito16 (dev->resolution, &window[10]);
+ Ito16 (dev->resolution, &window[12]);
+
+ /* Upper Left (X,Y) */
+ Ito32 (dev->x_tl, &window[14]);
+ Ito32 (dev->y_tl, &window[18]);
+
+ /* Width and length */
+ Ito32 (dev->width, &window[22]);
+ Ito32 (dev->length, &window[26]);
+ Ito32 (dev->width, &window[56]); /* again, verso? */
+ Ito32 (dev->length, &window[60]); /* again, verso? */
+
+ /* Brightness */
+ window[30] = 255 - dev->val[OPT_BRIGHTNESS].w;
+ window[31] = window[30]; /* same as brightness. */
+
+ /* Contrast */
+ window[32] = dev->val[OPT_CONTRAST].w;
+
+ /* Image Composition */
+ switch (dev->scan_mode)
+ {
+ case MATSUSHITA_BW:
+ window[33] = 0x00;
+ break;
+ case MATSUSHITA_HALFTONE:
+ window[33] = 0x01;
+ break;
+ case MATSUSHITA_GRAYSCALE:
+ window[33] = 0x02;
+ break;
+ }
+
+ /* Depth */
+ window[34] = dev->depth;
+
+ /* Halftone pattern. */
+ if (dev->scan_mode == MATSUSHITA_HALFTONE)
+ {
+ i = get_string_list_index (halftone_pattern_list,
+ dev->val[OPT_HALFTONE_PATTERN].s);
+ window[36] = halftone_pattern_val[i];
+ }
+
+ /* Gamma */
+ if (dev->scan_mode == MATSUSHITA_GRAYSCALE)
+ {
+ i = get_string_list_index (gamma_list, dev->val[OPT_GAMMA].s);
+ window[52] = gamma_val[i];
+ }
+
+ /* Feeder mode */
+ i = get_string_list_index (feeder_mode_list, dev->val[OPT_FEEDER_MODE].s);
+ window[65] = feeder_mode_val[i];
+
+ /* Image emphasis */
+ i = get_string_list_index (scanners[dev->scnum].image_emphasis_list,
+ dev->val[OPT_IMAGE_EMPHASIS].s);
+ window[51] = scanners[dev->scnum].image_emphasis_val[i];
+
+ /* White level */
+ i = get_string_list_index (white_level_list, dev->val[OPT_WHITE_LEVEL].s);
+ window[68] = white_level_val[i];
+
+ if (dev->scan_mode == MATSUSHITA_BW ||
+ dev->scan_mode == MATSUSHITA_HALFTONE)
+ {
+
+ /* Noise reduction */
+ i = get_string_list_index (noise_reduction_list,
+ dev->val[OPT_NOISE_REDUCTION].s);
+ window[69] = noise_reduction_val[i];
+
+ /* Automatic separation */
+ if (dev->val[OPT_AUTOMATIC_SEPARATION].w)
+ {
+ window[67] = 0x80;
+ }
+
+ /* Automatic threshold. Must be last because it may override
+ * some previous options. */
+ i = get_string_list_index (automatic_threshold_list,
+ dev->val[OPT_AUTOMATIC_THRESHOLD].s);
+ window[66] = automatic_threshold_val[i];
+
+ if (automatic_threshold_val[i] != 0)
+ {
+ /* Automatic threshold is enabled. */
+ window[30] = 0; /* brightness. */
+ window[31] = 0; /* same as brightness. */
+ window[32] = 0; /* contrast */
+ window[33] = 0; /* B&W mode */
+ window[36] = 0; /* Halftone pattern. */
+ window[51] = 0; /* Image emphasis */
+ window[67] = 0; /* Automatic separation */
+ window[68] = 0; /* White level */
+ window[69] = 0; /* Noise reduction */
+ }
+ }
+
+ hexdump (DBG_info2, "windows", window, 72);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ window, sizeof (window), NULL, NULL);
+
+ DBG (DBG_proc, "matsushita_set_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Read the image from the scanner and fill the temporary buffer with it. */
+static SANE_Status
+matsushita_fill_image (Matsushita_Scanner * dev)
+{
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+
+ DBG (DBG_proc, "matsushita_fill_image: enter\n");
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ while (dev->real_bytes_left)
+ {
+
+ /*
+ * Try to read the maximum number of bytes.
+ *
+ * The windows driver reads no more than 0x8000 byte.
+ *
+ * This backend operates differently than the windows
+ * driver. The windows TWAIN driver always read 2 more bytes
+ * at the end, so it gets a CHECK CONDITION with a short read
+ * sense. Since the linux scsi layer seem to be buggy
+ * regarding the resid, always read exactly the number of
+ * remaining bytes.
+ */
+
+ size = dev->real_bytes_left;
+ if (size > dev->image_size - dev->image_end)
+ size = dev->image_size - dev->image_end;
+ if (size > 0x8000)
+ size = 0x8000;
+
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_info, "sane_read: to read = %ld bytes (bpl=%d)\n",
+ (long) size, dev->params.bytes_per_line);
+
+ MKSCSI_READ_10 (cdb, 0, 0, size);
+ cdb.data[4] = dev->page_num; /* May be cdb.data[3] too? */
+ cdb.data[5] = dev->page_side;
+
+ hexdump (DBG_info2, "sane_read: READ_10 CDB", cdb.data, 10);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status == SANE_STATUS_EOF)
+ {
+ DBG (DBG_proc, "sane_read: exit, end of page scan\n");
+ return (SANE_STATUS_EOF);
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_read: cannot read from the scanner\n");
+ return status;
+ }
+
+ dev->real_bytes_left -= size;
+
+ switch (dev->depth)
+ {
+ case 1:
+ {
+ /* For Black & White, the bits in every bytes are mirrored.
+ * for instance 11010001 is coded as 10001011 */
+
+ unsigned char *src = dev->buffer;
+ unsigned char *dest = dev->image + dev->image_end;
+ unsigned char s;
+ unsigned char d;
+
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ {
+ s = *src;
+ d = 0;
+ if (s & 0x01)
+ d |= 0x80;
+ if (s & 0x02)
+ d |= 0x40;
+ if (s & 0x04)
+ d |= 0x20;
+ if (s & 0x08)
+ d |= 0x10;
+ if (s & 0x10)
+ d |= 0x08;
+ if (s & 0x20)
+ d |= 0x04;
+ if (s & 0x40)
+ d |= 0x02;
+ if (s & 0x80)
+ d |= 0x01;
+ *dest = d;
+ src++;
+ dest++;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ /* Adjust from a depth of 4 bits ([0..15]) to
+ * a depth of 8 bits ([0..255]) */
+
+ unsigned char *src = dev->buffer;
+ unsigned char *dest = dev->image + dev->image_end;
+ size_t i;
+
+ /* n bytes from image --> 2*n bytes in buf. */
+
+ for (i = 0; i < size; i++)
+ {
+ *dest = ((*src & 0x0f) >> 0) * 17;
+ dest++;
+ *dest = ((*src & 0xf0) >> 4) * 17;
+ dest++;
+ src++;
+ }
+
+ size *= 2;
+ }
+ break;
+
+ default:
+ memcpy (dev->image + dev->image_end, dev->buffer, size);
+ break;
+ }
+
+ dev->image_end += size;
+
+ }
+
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+/* Copy from the raw buffer to the buffer given by the backend.
+ *
+ * len in input is the maximum length available in buf, and, in
+ * output, is the length written into buf.
+ */
+static void
+matsushita_copy_raw_to_frontend (Matsushita_Scanner * dev, SANE_Byte * buf,
+ size_t * len)
+{
+ size_t size;
+
+ size = dev->image_end - dev->image_begin;
+ if (size > *len)
+ {
+ size = *len;
+ }
+ *len = size;
+
+ memcpy (buf, dev->image + dev->image_begin, size);
+ dev->image_begin += size;
+}
+
+/* Stop a scan. */
+static SANE_Status
+do_cancel (Matsushita_Scanner * dev)
+{
+ DBG (DBG_sane_proc, "do_cancel enter\n");
+
+ if (dev->scanning == SANE_TRUE)
+ {
+
+ /* Reset the scanner */
+ matsushita_reset_window (dev);
+
+ matsushita_close (dev);
+ }
+
+ dev->scanning = SANE_FALSE;
+
+ DBG (DBG_sane_proc, "do_cancel exit\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ char dev_name[PATH_MAX];
+ size_t len;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ DBG (DBG_error, "This is sane-matsushita version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2002 by Frank Zago\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ fp = sanei_config_open (MATSUSHITA_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach_scanner ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Matsushita_Scanner *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Matsushita_Scanner *dev;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No scanner found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Build a list a paper size that fit into this scanner. */
+ matsushita_build_paper_sizes (dev);
+
+ matsushita_init_options (dev);
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Matsushita_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Matsushita_Scanner *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+ int i;
+ SANE_Word value;
+ int rc;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ name = dev->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_DUPLEX:
+ case OPT_AUTOMATIC_SEPARATION:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ case OPT_FEEDER_MODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_PAPER_SIZE:
+ case OPT_AUTOMATIC_THRESHOLD:
+ case OPT_WHITE_LEVEL:
+ case OPT_NOISE_REDUCTION:
+ case OPT_IMAGE_EMPHASIS:
+ case OPT_GAMMA:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Side-effect options */
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_RESOLUTION:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* The length of X must be rounded (up). */
+ case OPT_TL_X:
+ case OPT_BR_X:
+
+ value = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+
+ i = get_int_list_index (scanners[dev->scnum].resolutions_list,
+ dev->val[OPT_RESOLUTION].w);
+
+ if (value & (scanners[dev->scnum].resolutions_round[i] - 1))
+ {
+ value =
+ (value | (scanners[dev->scnum].resolutions_round[i] - 1)) + 1;
+ if (info)
+ {
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+
+ *(SANE_Word *) val = SANE_FIX (iluToMm (value));
+
+ dev->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ /* Side-effect free options */
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ case OPT_DUPLEX:
+ case OPT_AUTOMATIC_SEPARATION:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String mode */
+ case OPT_WHITE_LEVEL:
+ case OPT_NOISE_REDUCTION:
+ case OPT_IMAGE_EMPHASIS:
+ case OPT_GAMMA:
+ case OPT_FEEDER_MODE:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ /* Set default options for the scan modes. */
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, BLACK_WHITE_STR) == 0)
+ {
+ dev->depth = 1;
+
+ dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE;
+
+ i = get_string_list_index (halftone_pattern_list,
+ dev->val[OPT_HALFTONE_PATTERN].s);
+ if (halftone_pattern_val[i] == -1)
+ {
+ dev->scan_mode = MATSUSHITA_BW;
+ }
+ else
+ {
+ dev->scan_mode = MATSUSHITA_HALFTONE;
+ }
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, GRAY4_STR) == 0)
+ {
+ dev->scan_mode = MATSUSHITA_GRAYSCALE;
+ dev->depth = 4;
+
+ dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, GRAY8_STR) == 0)
+ {
+ dev->scan_mode = MATSUSHITA_GRAYSCALE;
+ dev->depth = 8;
+
+ dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ assert (0 == 1);
+ }
+
+ /* Some options might not be supported by the scanner. */
+ if ((scanners[dev->scnum].cap & MAT_CAP_GAMMA) == 0)
+ dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_HALFTONE_PATTERN:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ i = get_string_list_index (halftone_pattern_list,
+ dev->val[OPT_HALFTONE_PATTERN].s);
+ if (halftone_pattern_val[i] == -1)
+ {
+ dev->scan_mode = MATSUSHITA_BW;
+ }
+ else
+ {
+ dev->scan_mode = MATSUSHITA_HALFTONE;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAPER_SIZE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_PAPER_SIZE].s);
+ dev->val[OPT_PAPER_SIZE].s = (SANE_Char *) strdup (val);
+
+ i = get_string_list_index (dev->paper_sizes_list,
+ dev->val[OPT_PAPER_SIZE].s);
+ i = dev->paper_sizes_val[i];
+
+ /* Set the 4 corners values. */
+ value = 0;
+ rc = sane_control_option (handle, OPT_TL_X, SANE_ACTION_SET_VALUE,
+ &value, info);
+ assert (rc == SANE_STATUS_GOOD);
+
+ value = 0;
+ rc = sane_control_option (handle, OPT_TL_Y, SANE_ACTION_SET_VALUE,
+ &value, info);
+ assert (rc == SANE_STATUS_GOOD);
+
+ value = SANE_FIX (paper_sizes[i].width);
+ rc = sane_control_option (handle, OPT_BR_X, SANE_ACTION_SET_VALUE,
+ &value, info);
+ assert (rc == SANE_STATUS_GOOD);
+
+ value = SANE_FIX (paper_sizes[i].length);
+ rc = sane_control_option (handle, OPT_BR_Y, SANE_ACTION_SET_VALUE,
+ &value, info);
+ assert (rc == SANE_STATUS_GOOD);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOMATIC_THRESHOLD:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_Char *) strdup (val);
+
+ /* If the threshold is not set to none, some option must
+ * disappear. */
+ dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_IMAGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[option].s, automatic_threshold_list[0]) == 0)
+ {
+ dev->opt[OPT_WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_IMAGE_EMPHASIS].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->scan_mode == MATSUSHITA_BW
+ || dev->scan_mode == MATSUSHITA_HALFTONE)
+ {
+ dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Matsushita_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Setup the parameters for the scan. These values will be re-used
+ * in the SET WINDOWS command. */
+ dev->resolution = dev->val[OPT_RESOLUTION].w;
+
+ dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+
+ /* Check the corners are OK. */
+ if (dev->x_tl > dev->x_br)
+ {
+ int s;
+ s = dev->x_tl;
+ dev->x_tl = dev->x_br;
+ dev->x_br = s;
+ }
+ if (dev->y_tl > dev->y_br)
+ {
+ int s;
+ s = dev->y_tl;
+ dev->y_tl = dev->y_br;
+ dev->y_br = s;
+ }
+
+ dev->width = dev->x_br - dev->x_tl;
+ dev->length = dev->y_br - dev->y_tl;
+
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.last_frame = SANE_TRUE;
+ dev->params.pixels_per_line =
+ (((dev->width * dev->resolution) / 1200) + 7) & ~0x7;
+
+ if (dev->depth == 4)
+ {
+ dev->params.depth = 8;
+ }
+ else
+ {
+ dev->params.depth = dev->depth;
+ }
+ dev->params.bytes_per_line =
+ (dev->params.pixels_per_line / 8) * dev->params.depth;
+ dev->params.lines = (dev->length * dev->resolution) / 1200;
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Matsushita_Scanner *dev = handle;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ sane_get_parameters (dev, NULL);
+
+ if (dev->image == NULL)
+ {
+ dev->image_size = 3 * dev->buffer_size;
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ /* Open again the scanner. */
+ if (sanei_scsi_open
+ (dev->devicename, &(dev->sfd), matsushita_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ dev->page_side = 0; /* page front */
+ dev->page_num = 0; /* first page */
+
+ /* The scanner must be ready. */
+ status = matsushita_wait_scanner (dev);
+ if (status)
+ {
+ matsushita_close (dev);
+ return status;
+ }
+
+ status = matsushita_reset_window (dev);
+ if (status)
+ {
+ matsushita_close (dev);
+ return status;
+ }
+
+ status = matsushita_set_window (dev, PAGE_FRONT);
+ if (status)
+ {
+ matsushita_close (dev);
+ return status;
+ }
+
+ if (dev->val[OPT_DUPLEX].w == SANE_TRUE)
+ {
+ status = matsushita_set_window (dev, PAGE_BACK);
+ if (status)
+ {
+ matsushita_close (dev);
+ return status;
+ }
+ }
+
+ status = matsushita_read_document_size (dev);
+ if (status)
+ {
+ matsushita_close (dev);
+ return status;
+ }
+
+ }
+ else
+ {
+ if (dev->val[OPT_DUPLEX].w == SANE_TRUE && dev->page_side == PAGE_FRONT)
+ {
+ dev->page_side = PAGE_BACK;
+ }
+ else
+ {
+ /* new sheet. */
+ dev->page_side = PAGE_FRONT;
+ dev->page_num++;
+ }
+
+ status = matsushita_check_next_page (dev);
+ if (status)
+ {
+ return status;
+ }
+ }
+
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+ dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines;
+ if (dev->depth == 4)
+ {
+ /* Every byte read will be expanded into 2 bytes. */
+ dev->real_bytes_left /= 2;
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Matsushita_Scanner *dev = handle;
+ size_t size;
+ int buf_offset; /* offset into buf */
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning */
+ return do_cancel (dev);
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ buf_offset = 0;
+
+ do
+ {
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = matsushita_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Copy the data to the frontend buffer. */
+ size = max_len - buf_offset;
+ if (size > dev->bytes_left)
+ {
+ size = dev->bytes_left;
+ }
+ matsushita_copy_raw_to_frontend (dev, buf + buf_offset, &size);
+
+ buf_offset += size;
+
+ dev->bytes_left -= size;
+ *len += size;
+
+ }
+ while ((buf_offset != max_len) && dev->bytes_left);
+
+ DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long)dev->bytes_left);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ SANE_Status status;
+ Matsushita_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ if (dev->scanning == SANE_FALSE)
+ {
+ return (SANE_STATUS_INVAL);
+ }
+
+ if (non_blocking == SANE_FALSE) {
+ status = SANE_STATUS_GOOD;
+ } else {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Matsushita_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ do_cancel (dev);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Matsushita_Scanner *dev = handle;
+ Matsushita_Scanner *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+ do_cancel (dev);
+ matsushita_close (dev);
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ matsushita_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/matsushita.conf.in b/backend/matsushita.conf.in
new file mode 100644
index 0000000..4d3cf24
--- /dev/null
+++ b/backend/matsushita.conf.in
@@ -0,0 +1,23 @@
+#
+# Panasonic / Matsushita scanners
+
+scsi "K.M.E. " "KV-SS25A "
+scsi "K.M.E. " "KV-SS55EX "
+scsi "K.M.E. " "KV-S2025C "
+scsi "K.M.E. " "KV-S2045C "
+scsi "K.M.E. " "KV-S2065L "
+
+# These scanners are untested.
+# If you have one:
+# - check that the vendor/product strings are correct
+# - uncomment the line
+# - test with a frontend (xscanimage, xsane, ...)
+#scsi "K.M.E. " "KV-SS25 "
+#scsi "K.M.E. " "KV-SS25D "
+#scsi "K.M.E. " "KV-SS50 "
+#scsi "K.M.E. " "KV-SS55 "
+#scsi "K.M.E. " "KV-SS50EX "
+#scsi "K.M.E. " "KV-SS850 "
+#scsi "K.M.E. " "KV-SS855 "
+
+/dev/scanner
diff --git a/backend/matsushita.h b/backend/matsushita.h
new file mode 100644
index 0000000..a1528bf
--- /dev/null
+++ b/backend/matsushita.h
@@ -0,0 +1,369 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+*/
+
+/* Commands supported by the KV-SS 25 scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_INQUIRY 0x12
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_READ_10 0x28
+#define SCSI_REQUEST_SENSE 0x03
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+}
+CDB;
+
+
+/* Set a specific bit depending on a boolean.
+ * MKSCSI_BIT(TRUE, 3) will generate 0x08. */
+#define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0)
+
+/* Set a value in a range of bits.
+ * MKSCSI_I2B(5, 3, 5) will generate 0x28 */
+#define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1))
+
+/* Store an integer in 2, 3 or 4 byte in an array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+#define MKSCSI_TEST_UNIT_READY(cdb) \
+ cdb.data[0] = SCSI_TEST_UNIT_READY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_INQUIRY(cdb, buflen) \
+ cdb.data[0] = SCSI_INQUIRY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SET_WINDOW(cdb, buflen) \
+ cdb.data[0] = SCSI_SET_WINDOW; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_READ_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_REQUEST_SENSE(cdb, buflen) \
+ cdb.data[0] = SCSI_REQUEST_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (buflen); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+/*--------------------------------------------------------------------------*/
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7)
+
+/*--------------------------------------------------------------------------*/
+
+#define mmToIlu(mm) (((mm) * 1200) / MM_PER_INCH)
+#define iluToMm(ilu) (((ilu) * MM_PER_INCH) / 1200)
+
+#define PAGE_FRONT 0x00
+#define PAGE_BACK 0x80
+
+/*--------------------------------------------------------------------------*/
+
+enum Matsushita_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+ OPT_DUPLEX, /* Duplex mode */
+ OPT_FEEDER_MODE, /* Feeding mode */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_PAPER_SIZE, /* Paper size */
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS, /* Brightness */
+ OPT_CONTRAST, /* Contrast */
+ OPT_AUTOMATIC_THRESHOLD, /* Automatic threshold */
+ OPT_HALFTONE_PATTERN, /* Halftone pattern */
+ OPT_AUTOMATIC_SEPARATION, /* Automatic separation */
+ OPT_WHITE_LEVEL, /* White level */
+ OPT_NOISE_REDUCTION, /* Noise reduction */
+ OPT_IMAGE_EMPHASIS, /* Image emphasis */
+ OPT_GAMMA, /* Gamma */
+
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+#define BLACK_WHITE_STR SANE_VALUE_SCAN_MODE_LINEART
+#define GRAY4_STR SANE_I18N("Grayscale 4 bits")
+#define GRAY8_STR SANE_I18N("Grayscale 8 bits")
+
+/*--------------------------------------------------------------------------*/
+
+#define SANE_NAME_DUPLEX "duplex"
+#define SANE_NAME_PAPER_SIZE "paper-size"
+#define SANE_NAME_AUTOSEP "autoseparation"
+
+#define SANE_TITLE_DUPLEX SANE_I18N("Duplex")
+#define SANE_TITLE_PAPER_SIZE SANE_I18N("Paper size")
+#define SANE_TITLE_AUTOSEP SANE_I18N("Automatic separation")
+
+#define SANE_DESC_DUPLEX \
+SANE_I18N("Enable Duplex (Dual-Sided) Scanning")
+#define SANE_DESC_PAPER_SIZE \
+SANE_I18N("Physical size of the paper in the ADF");
+#define SANE_DESC_AUTOSEP \
+SANE_I18N("Automatic separation")
+
+/*--------------------------------------------------------------------------*/
+
+/* Differences between the scanners.
+ * The scsi_* fields are used to lookup the correcte entry. */
+struct scanners_supported
+{
+ int scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+
+ SANE_String_Const *scan_mode_list; /* array of scan modes */
+ const SANE_Word *resolutions_list; /* array of available resolutions */
+ const SANE_Word *resolutions_round; /* rounding values for each resolutions */
+
+ SANE_String_Const *image_emphasis_list; /* list of image emphasis options */
+ const int *image_emphasis_val; /* list of image emphasis values */
+
+ /* Scanner capabilities. */
+ int cap; /* bit field */
+#define MAT_CAP_DUPLEX 0x00000002 /* can do duplex */
+#define MAT_CAP_CONTRAST 0x00000004 /* have contrast */
+#define MAT_CAP_AUTOMATIC_THRESHOLD 0x00000008
+#define MAT_CAP_WHITE_LEVEL 0x00000010
+#define MAT_CAP_GAMMA 0x00000020
+#define MAT_CAP_NOISE_REDUCTION 0x00000040
+#define MAT_CAP_PAPER_DETECT 0x00000080
+#define MAT_CAP_MIRROR_IMAGE 0x00000100
+#define MAT_CAP_DETECT_DOUBLE_FEED 0x00000200
+#define MAT_CAP_MANUAL_FEED 0x00000400
+};
+
+struct paper_sizes
+{
+ SANE_String_Const name; /* name of the paper */
+ int width;
+ int length;
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define a scanner occurence. */
+typedef struct Matsushita_Scanner
+{
+ struct Matsushita_Scanner *next;
+ SANE_Device sane;
+
+ char *devicename;
+ int sfd; /* device handle */
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+ char scsi_version[5];
+
+ /* Scanner infos. */
+ int scnum; /* index of that scanner in
+ * scanners_supported */
+
+ SANE_String_Const *paper_sizes_list; /* names of supported papers */
+ int *paper_sizes_val; /* indirection into paper_sizes[] */
+
+ /* SCSI handling */
+ size_t buffer_size; /* size of the buffer */
+ SANE_Byte *buffer; /* for SCSI transfer. */
+
+ /* Scanning handling. */
+ int scanning; /* TRUE if a scan is running. */
+ int resolution; /* resolution in DPI, for both X and Y */
+ int x_tl; /* X top left */
+ int y_tl; /* Y top left */
+ int x_br; /* X bottom right */
+ int y_br; /* Y bottom right */
+ int width; /* width of the scan area in mm */
+ int length; /* length of the scan area in mm */
+
+ enum
+ {
+ MATSUSHITA_BW,
+ MATSUSHITA_HALFTONE,
+ MATSUSHITA_GRAYSCALE
+ }
+ scan_mode;
+
+ int depth; /* depth per color */
+ int halftone_pattern; /* haltone number, valid for MATSUSHITA_HALFTONE */
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+
+ size_t real_bytes_left; /* number of bytes left the scanner will return. */
+
+ SANE_Parameters params;
+
+ int page_side; /* 0=front, 1=back */
+ int page_num; /* current number of the page */
+
+ /* For Grayscale 4 bits only */
+ SANE_Byte *image; /* keep the current image there */
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+}
+Matsushita_Scanner;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
diff --git a/backend/microtek.c b/backend/microtek.c
new file mode 100644
index 0000000..ff82df4
--- /dev/null
+++ b/backend/microtek.c
@@ -0,0 +1,4187 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ microtek.c
+
+ This file Copyright 2002 Matthew Marjanovic
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for Microtek scanners.
+
+ (feedback to: mtek-bugs@mir.com)
+ (for latest info: http://www.mir.com/mtek/)
+
+ ***************************************************************************/
+
+
+#define MICROTEK_MAJOR 0
+#define MICROTEK_MINOR 13
+#define MICROTEK_PATCH 1
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME microtek
+#include "../include/sane/sanei_backend.h"
+
+#include "microtek.h"
+
+
+#define MICROTEK_CONFIG_FILE "microtek.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+
+#define SCSI_BUFF_SIZE sanei_scsi_max_request_size
+
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+static int num_devices = 0;
+static Microtek_Device *first_dev = NULL; /* list of known devices */
+static Microtek_Scanner *first_handle = NULL; /* list of open scanners */
+static const SANE_Device **devlist = NULL; /* sane_get_devices() */
+
+
+static SANE_Bool inhibit_clever_precal = SANE_FALSE;
+static SANE_Bool inhibit_real_calib = SANE_FALSE;
+
+
+#define M_GSS_WAIT 5 /* seconds */
+
+#define M_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define M_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define M_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define M_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+#define M_OPAQUE "Opaque/Normal"
+#define M_TRANS "Transparency"
+#define M_AUTOFEED "AutoFeeder"
+
+#define M_NONE "None"
+#define M_SCALAR "Scalar"
+#define M_TABLE "Table"
+
+static SANE_String_Const gamma_mode_list[4] = {
+ M_NONE,
+ M_SCALAR,
+ M_TABLE,
+ NULL
+};
+
+
+/* These are for the E6. Does this hold for other models? */
+static SANE_String_Const halftone_mode_list[13] = {
+ " 1 53-dot screen (53 gray levels)",
+ " 2 Horiz. screen (65 gray levels)",
+ " 3 Vert. screen (65 gray levels)",
+ " 4 Mixed page (33 gray levels)",
+ " 5 71-dot screen (29 gray levels)",
+ " 6 60-dot #1 (26 gray levels)",
+ " 7 60-dot #2 (26 gray levels)",
+ " 8 Fine detail #1 (17 gray levels)",
+ " 9 Fine detail #2 (17 gray levels)",
+ "10 Slant line (17 gray levels)",
+ "11 Posterizing (10 gray levels)",
+ "12 High Contrast (5 gray levels)",
+ NULL
+};
+
+
+
+static SANE_Range speed_range = {1, 7, 1};
+
+static SANE_Range brightness_range = {-100, 100, 1};
+/*static SANE_Range brightness_range = {0, 255, 1};*/
+/*static SANE_Range exposure_range = {-18, 21, 3};*/
+/*static SANE_Range contrast_range = {-42, 49, 7};*/
+static SANE_Range u8_range = {0, 255, 1};
+static SANE_Range analog_gamma_range =
+{ SANE_FIX(0.1), SANE_FIX(4.0), SANE_FIX(0) };
+
+
+
+
+#define MAX_MDBG_LENGTH 1024
+static char _mdebug_string[MAX_MDBG_LENGTH];
+
+static void MDBG_INIT(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(_mdebug_string, MAX_MDBG_LENGTH, format, ap);
+ va_end(ap);
+}
+
+static void MDBG_ADD(const char *format, ...)
+{
+ int len = strlen(_mdebug_string);
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(_mdebug_string+len, MAX_MDBG_LENGTH-len, format, ap);
+ va_end(ap);
+}
+
+static void MDBG_FINISH(int dbglvl)
+{
+ DBG(dbglvl, "%s\n", _mdebug_string);
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/*** Utility Functions **********************************************/
+/********************************************************************/
+/********************************************************************/
+
+static size_t max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen(strings[i]) + 1;
+ if (size > max_size) max_size = size;
+ }
+ return max_size;
+}
+
+
+
+/********************************************************************/
+/* Allocate/create a new ring buffer */
+/********************************************************************/
+static ring_buffer *
+ring_alloc (size_t initial_size, size_t bpl, size_t ppl)
+{
+ ring_buffer *rb;
+ uint8_t *buff;
+
+ if ((rb = (ring_buffer *)malloc(sizeof(*rb))) == NULL)
+ return NULL;
+ if ((buff = (uint8_t *)malloc(initial_size * sizeof(*buff))) == NULL) {
+ free(rb);
+ return NULL;
+ }
+ rb->base = buff;
+ rb->size = initial_size;
+ rb->initial_size = initial_size;
+
+ rb->bpl = bpl;
+ rb->ppl = ppl;
+
+ rb->tail_red = 0;
+ rb->tail_green = 1;
+ rb->tail_blue = 2;
+ rb->head_complete = 0;
+
+ rb->red_extra = 0;
+ rb->green_extra = 0;
+ rb->blue_extra = 0;
+ rb->complete_count = 0;
+
+ return rb;
+}
+
+
+/********************************************************************/
+/* Enlarge an existing ring buffer */
+/********************************************************************/
+static SANE_Status
+ring_expand (ring_buffer *rb, size_t amount)
+{
+ uint8_t *buff;
+ size_t oldsize;
+
+ if (rb == NULL) return SANE_STATUS_INVAL;
+ buff = (uint8_t *)realloc(rb->base, (rb->size + amount) * sizeof(*buff));
+ if (buff == NULL) return SANE_STATUS_NO_MEM;
+
+ rb->base = buff;
+ oldsize = rb->size;
+ rb->size += amount;
+
+ DBG(23, "ring_expand: old, new, inc size: %lu, %lu, %lu\n",
+ (u_long)oldsize, (u_long)rb->size, (u_long)amount);
+ DBG(23, "ring_expand: old tr: %lu tg: %lu tb: %lu hc: %lu\n",
+ (u_long)rb->tail_red, (u_long)rb->tail_green,
+ (u_long)rb->tail_blue, (u_long)rb->head_complete);
+ /* if necessary, move data and fix up them pointers */
+ /* (will break subtly if ring is filled with G or B bytes,
+ and tail_g or tail_b have rolled over...) */
+ if (((rb->complete_count) ||
+ (rb->red_extra) ||
+ (rb->green_extra) ||
+ (rb->blue_extra)) && ((rb->tail_red <= rb->head_complete) ||
+ (rb->tail_green <= rb->head_complete) ||
+ (rb->tail_blue <= rb->head_complete))) {
+ memmove(rb->base + rb->head_complete + amount,
+ rb->base + rb->head_complete,
+ oldsize - rb->head_complete);
+ if ((rb->tail_red > rb->head_complete) ||
+ ((rb->tail_red == rb->head_complete) &&
+ !(rb->complete_count) && !(rb->red_extra)))
+ rb->tail_red += amount;
+ if ((rb->tail_green > rb->head_complete) ||
+ ((rb->tail_green == rb->head_complete) &&
+ !(rb->complete_count) && !(rb->green_extra)))
+ rb->tail_green += amount;
+ if ((rb->tail_blue > rb->head_complete) ||
+ ((rb->tail_blue == rb->head_complete) &&
+ !(rb->complete_count) && !(rb->blue_extra)))
+ rb->tail_blue += amount;
+ rb->head_complete += amount;
+ }
+ DBG(23, "ring_expand: new tr: %lu tg: %lu tb: %lu hc: %lu\n",
+ (u_long)rb->tail_red, (u_long)rb->tail_green,
+ (u_long)rb->tail_blue, (u_long)rb->head_complete);
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Deallocate a ring buffer */
+/********************************************************************/
+static void
+ring_free (ring_buffer *rb)
+{
+ free(rb->base);
+ free(rb);
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/*** Basic SCSI Commands ********************************************/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* parse sense from scsi error */
+/* (even though microtek sense codes are non-standard and */
+/* typically misinterpreted/munged by the low-level scsi driver) */
+/********************************************************************/
+static SANE_Status
+sense_handler (int scsi_fd, u_char *sense, void *arg)
+{
+ int *sense_flags = (int *)arg;
+ SANE_Status stat;
+
+ DBG(10, "SENSE! fd = %d\n", scsi_fd);
+ DBG(10, "sense = %02x %02x %02x %02x.\n",
+ sense[0], sense[1], sense[2], sense[3]);
+ switch(sense[0]) {
+ case 0x00:
+ return SANE_STATUS_GOOD;
+ case 0x81: /* COMMAND/DATA ERROR */
+ stat = SANE_STATUS_GOOD;
+ if (sense[1] & 0x01) {
+ if ((sense_flags != NULL) && (*sense_flags & MS_SENSE_IGNORE))
+ DBG(10, "sense: ERR_SCSICMD -- ignored\n");
+ else {
+ DBG(10, "sense: ERR_SCSICMD\n");
+ stat = SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sense[1] & 0x02) {
+ DBG(10, "sense: ERR_TOOMANY\n");
+ stat = SANE_STATUS_IO_ERROR;
+ }
+ return stat;
+ case 0x82 : /* SCANNER HARDWARE ERROR */
+ if (sense[1] & 0x01) DBG(10, "sense: ERR_CPURAMFAIL\n");
+ if (sense[1] & 0x02) DBG(10, "sense: ERR_SYSRAMFAIL\n");
+ if (sense[1] & 0x04) DBG(10, "sense: ERR_IMGRAMFAIL\n");
+ if (sense[1] & 0x10) DBG(10, "sense: ERR_CALIBRATE\n");
+ if (sense[1] & 0x20) DBG(10, "sense: ERR_LAMPFAIL\n");
+ if (sense[1] & 0x40) DBG(10, "sense: ERR_MOTORFAIL\n");
+ if (sense[1] & 0x80) DBG(10, "sense: ERR_FEEDERFAIL\n");
+ if (sense[2] & 0x01) DBG(10, "sense: ERR_POWERFAIL\n");
+ if (sense[2] & 0x02) DBG(10, "sense: ERR_ILAMPFAIL\n");
+ if (sense[2] & 0x04) DBG(10, "sense: ERR_IMOTORFAIL\n");
+ if (sense[2] & 0x08) DBG(10, "sense: ERR_PAPERFAIL\n");
+ if (sense[2] & 0x10) DBG(10, "sense: ERR_FILTERFAIL\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x83 : /* OPERATION ERROR */
+ if (sense[1] & 0x01) DBG(10, "sense: ERR_ILLGRAIN\n");
+ if (sense[1] & 0x02) DBG(10, "sense: ERR_ILLRES\n");
+ if (sense[1] & 0x04) DBG(10, "sense: ERR_ILLCOORD\n");
+ if (sense[1] & 0x10) DBG(10, "sense: ERR_ILLCNTR\n");
+ if (sense[1] & 0x20) DBG(10, "sense: ERR_ILLLENGTH\n");
+ if (sense[1] & 0x40) DBG(10, "sense: ERR_ILLADJUST\n");
+ if (sense[1] & 0x80) DBG(10, "sense: ERR_ILLEXPOSE\n");
+ if (sense[2] & 0x01) DBG(10, "sense: ERR_ILLFILTER\n");
+ if (sense[2] & 0x02) DBG(10, "sense: ERR_NOPAPER\n");
+ if (sense[2] & 0x04) DBG(10, "sense: ERR_ILLTABLE\n");
+ if (sense[2] & 0x08) DBG(10, "sense: ERR_ILLOFFSET\n");
+ if (sense[2] & 0x10) DBG(10, "sense: ERR_ILLBPP\n");
+ return SANE_STATUS_IO_ERROR;
+ default :
+ DBG(10, "sense: unknown error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* wait (via polling) until scanner seems "ready" */
+/********************************************************************/
+static SANE_Status
+wait_ready(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0, 0, 0, 0, 0, 0 };
+ SANE_Status status;
+ int retry = 0;
+
+ DBG(23, ".wait_ready %d...\n", ms->sfd);
+ while ((status = sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "wait_ready failed (%d)\n", retry);
+ if (retry > 5) return SANE_STATUS_IO_ERROR; /* XXXXXXXX */
+ retry++;
+ sleep(3);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* send scan region coordinates */
+/********************************************************************/
+static SANE_Status
+scanning_frame(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[15] = { 0x04, 0, 0, 0, 0x09, 0 };
+ int x1, y1, x2, y2;
+
+ DBG(23, ".scanning_frame...\n");
+
+ x1 = ms->x1;
+ x2 = ms->x2;
+ y1 = ms->y1;
+ y2 = ms->y2;
+ /* E6 weirdness (other models too?) */
+ if (ms->unit_type == MS_UNIT_18INCH) {
+ x1 /= 2;
+ x2 /= 2;
+ y1 /= 2;
+ y2 /= 2;
+ }
+
+ DBG(23, ".scanning_frame: in- %d,%d %d,%d\n",
+ ms->x1, ms->y1, ms->x2, ms->y2);
+ DBG(23, ".scanning_frame: out- %d,%d %d,%d\n", x1, y1, x2, y2);
+ data = comm + 6;
+ data[0] =
+ ((ms->unit_type == MS_UNIT_PIXELS) ? 0x08 : 0 ) |
+ ((ms->mode == MS_MODE_HALFTONE) ? 0x01 : 0 );
+ data[1] = x1 & 0xFF;
+ data[2] = (x1 >> 8) & 0xFF;
+ data[3] = y1 & 0xFF;
+ data[4] = (y1 >> 8) & 0xFF;
+ data[5] = x2 & 0xFF;
+ data[6] = (x2 >> 8) & 0xFF;
+ data[7] = y2 & 0xFF;
+ data[8] = (y2 >> 8) & 0xFF;
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "SF: ");
+ for (i=0;i<6+0x09;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SF: ");
+ for (i=0;i<6+0x09;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x09, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* send "mode_select" */
+/********************************************************************/
+static SANE_Status
+mode_select(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[19] = { 0x15, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".mode_select %d...\n", ms->sfd);
+ data = comm + 6;
+ data[0] =
+ 0x81 |
+ ((ms->unit_type == MS_UNIT_18INCH) ? 0 : 0x08) |
+ ((ms->res_type == MS_RES_5PER) ? 0 : 0x02);
+ data[1] = ms->resolution_code;
+ data[2] = ms->exposure;
+ data[3] = ms->contrast;
+ data[4] = ms->pattern;
+ data[5] = ms->velocity;
+ data[6] = ms->shadow;
+ data[7] = ms->highlight;
+ DBG(23, ".mode_select: pap_len: %d\n", ms->paper_length);
+ data[8] = ms->paper_length & 0xFF;
+ data[9] = (ms->paper_length >> 8) & 0xFF;
+ data[10] = ms->midtone;
+ /* set command/data length */
+ comm[4] = (ms->midtone_support) ? 0x0B : 0x0A;
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "MSL: ");
+ for (i=0;i<6+comm[4];i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("MSL: ");
+ for (i=0;i<6+comm[4];i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + comm[4], 0, 0);
+}
+
+
+
+/********************************************************************/
+/* send "mode_select_1" */
+/********************************************************************/
+static SANE_Status
+mode_select_1(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[16] = { 0x16, 0, 0, 0, 0x0A, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ DBG(23, ".mode_select_1 %d...\n", ms->sfd);
+ data = comm + 6;
+ data[1] = ms->bright_r;
+ data[3] = ((ms->allow_calibrate) ? 0 : 0x02); /* | 0x01; */
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "MSL1: ");
+ for (i=0;i<6+0x0A;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("MSL1: ");
+ for (i=0;i<6+0x0A;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x0A, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* record mode_sense results in the mode_sense buffer */
+/* (this is to tell if something catastrophic has happened */
+/* to the scanner in-between scans) */
+/********************************************************************/
+static SANE_Status
+save_mode_sense(Microtek_Scanner *ms)
+{
+ uint8_t data[20], comm[6] = { 0x1A, 0, 0, 0, 0, 0};
+ size_t lenp;
+ SANE_Status status;
+ int i;
+
+ DBG(23, ".save_mode_sense %d...\n", ms->sfd);
+ if (ms->onepass) comm[4] = 0x13;
+ else if (ms->midtone_support) comm[4] = 0x0B;
+ else comm[4] = 0x0A;
+ lenp = comm[4];
+
+ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp);
+ for (i=0; i<10; i++) ms->mode_sense_cache[i] = data[i];
+
+ if (DBG_LEVEL >= 192) {
+ unsigned int i;
+#if 0
+ fprintf(stderr, "SMS: ");
+ for (i=0;i<lenp;i++) fprintf(stderr, "%2x ", data[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SMS: ");
+ for (i=0;i<lenp;i++) MDBG_ADD("%2x ", data[i]);
+ MDBG_FINISH(192);
+ }
+
+ return status;
+}
+
+
+/********************************************************************/
+/* read mode_sense and compare to what we saved before */
+/********************************************************************/
+static SANE_Status
+compare_mode_sense(Microtek_Scanner *ms, int *match)
+{
+ uint8_t data[20], comm[6] = { 0x1A, 0, 0, 0, 0, 0};
+ size_t lenp;
+ SANE_Status status;
+ int i;
+
+ DBG(23, ".compare_mode_sense %d...\n", ms->sfd);
+ if (ms->onepass) comm[4] = 0x13;
+ else if (ms->midtone_support) comm[4] = 0x0B;
+ else comm[4] = 0x0A;
+ lenp = comm[4];
+
+ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp);
+ *match = 1;
+ for (i=0; i<10; i++)
+ *match = *match && (ms->mode_sense_cache[i] == data[i]);
+
+ if (DBG_LEVEL >= 192) {
+ unsigned int i;
+#if 0
+ fprintf(stderr, "CMS: ");
+ for (i=0;i<lenp;i++) fprintf(stderr, "%2x(%2x) ",
+ data[i],
+ ms->mode_sense_cache[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("CMS: ");
+ for (i=0;i<lenp;i++) MDBG_ADD("%2x(%2x) ",
+ data[i],
+ ms->mode_sense_cache[i]);
+ MDBG_FINISH(192);
+ }
+
+ return status;
+}
+
+/********************************************************************/
+/* send mode_sense_1, and upset every scsi driver known to mankind */
+/********************************************************************/
+#if 0
+static SANE_Status
+mode_sense_1(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[36] = { 0x19, 0, 0, 0, 0x1E, 0 };
+
+ DBG(23, ".mode_sense_1...\n");
+ data = comm + 6;
+ memset(data, 0, 30);
+ data[1] = ms->bright_r;
+ data[2] = ms->bright_g;
+ data[3] = ms->bright_b;
+ if (DBG_LEVEL >= 192) {
+ int i;
+ fprintf(stderr, "MS1: ");
+ for (i=0;i<6+0x1E;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x1E, 0, 0);
+}
+#endif
+
+
+
+/********************************************************************/
+/* send "accessory" command */
+/********************************************************************/
+static SANE_Status
+accessory(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0x10, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".accessory...\n");
+ comm[4] =
+ ((ms->useADF) ? 0x41 : 0x40) |
+ ((ms->prescan) ? 0x18 : 0x10) |
+ ((ms->transparency) ? 0x24 : 0x20) |
+ ((ms->allowbacktrack) ? 0x82 : 0x80);
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "AC: ");
+ for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("AC: ");
+ for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* start the scanner a-scannin' */
+/********************************************************************/
+static SANE_Status
+start_scan(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0x1B, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".start_scan...\n");
+ comm[4] =
+ 0x01 | /* "start" */
+ ((ms->expandedresolution) ? 0x80 : 0) |
+ ((ms->multibit) ? 0x40 : 0) |
+ ((ms->onepasscolor) ? 0x20 : 0) |
+ ((ms->reversecolors) ? 0x04 : 0) |
+ ((ms->fastprescan) ? 0x02 : 0) |
+ ((ms->filter == MS_FILT_RED) ? 0x08 : 0) |
+ ((ms->filter == MS_FILT_GREEN) ? 0x10 : 0) |
+ ((ms->filter == MS_FILT_BLUE) ? 0x18 : 0) ;
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "SS: ");
+ for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SS: ");
+ for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* stop the scanner a-scannin' */
+/********************************************************************/
+static SANE_Status
+stop_scan(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0x1B, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".stop_scan...\n");
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "SPS:");
+ for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SPS:");
+ for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* get scan status */
+/********************************************************************/
+static SANE_Status
+get_scan_status(Microtek_Scanner *ms,
+ SANE_Int *busy,
+ SANE_Int *bytes_per_line,
+ SANE_Int *lines)
+{
+ uint8_t data[6], comm[6] = { 0x0F, 0, 0, 0, 0x06, 0 };
+ SANE_Status status;
+ size_t lenp;
+ int retry = 0;
+
+ DBG(23, ".get_scan_status %d...\n", ms->sfd);
+
+ do {
+ lenp = 6;
+ /* do some retry stuff in here, too */
+ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(20, "get_scan_status: scsi error\n");
+ return status;
+ }
+ *busy = data[0];
+ *bytes_per_line = (data[1]) + (data[2] << 8);
+ *lines = (data[3]) + (data[4] << 8) + (data[5] << 16);
+
+ DBG(20, "get_scan_status(%lu): %d, %d, %d -> #%d\n",
+ (u_long) lenp, *busy, *bytes_per_line, *lines, retry);
+ DBG(20, "> %2x %2x %2x %2x %2x %2x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+ if (*busy != 0) {
+ retry++;
+ DBG(23, "get_scan_status: busy, retry in %d...\n",
+ M_GSS_WAIT * retry);
+ sleep(M_GSS_WAIT * retry);
+ }
+ } while ((*busy != 0) && (retry < 4));
+
+ if (*busy == 0) return status;
+ else return SANE_STATUS_IO_ERROR;
+}
+
+
+
+/********************************************************************/
+/* get scanlines from scanner */
+/********************************************************************/
+static SANE_Status
+read_scan_data(Microtek_Scanner *ms,
+ int lines,
+ uint8_t *buffer,
+ size_t *bufsize)
+{
+ uint8_t comm[6] = { 0x08, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".read_scan_data...\n");
+ comm[2] = (lines >> 16) & 0xFF;
+ comm[3] = (lines >> 8) & 0xFF;
+ comm[4] = (lines) & 0xFF;
+
+ return sanei_scsi_cmd(ms->sfd, comm, 6, buffer, bufsize);
+}
+
+
+
+/********************************************************************/
+/* download color LUT to scanner (if it takes one) */
+/********************************************************************/
+static SANE_Status
+download_gamma(Microtek_Scanner *ms)
+{
+ uint8_t *data, *comm; /* commbytes[10] = { 0x55, 0, 0x27, 0, 0,
+ 0, 0, 0, 0, 0 };*/
+ int i, pl;
+ int commsize;
+ int bit_depth = 8; /* hard-code for now, should match bpp XXXXXXX */
+ int max_entry;
+ SANE_Status status;
+
+ DBG(23, ".download_gamma...\n");
+ /* skip if scanner doesn't take 'em */
+ if (!(ms->gamma_entries)) {
+ DBG(23, ".download_gamma: no entries; skipping\n");
+ return SANE_STATUS_GOOD;
+ }
+ if ((ms->gamma_entry_size != 1) && (ms->gamma_entry_size != 2)) {
+ DBG(23, ".download_gamma: entry size %d?!?!?\n", ms->gamma_entry_size);
+ return SANE_STATUS_INVAL; /* XXXXXXXxx */
+ }
+
+ max_entry = (1 << bit_depth) - 1;
+
+ DBG(23, ".download_gamma: %d entries of %d bytes, max %d\n",
+ ms->gamma_entries, ms->gamma_entry_size, max_entry);
+ commsize = 10 + (ms->gamma_entries * ms->gamma_entry_size);
+ comm = calloc(commsize, sizeof(uint8_t));
+ if (comm == NULL) {
+ DBG(23, ".download_gamma: couldn't allocate %d bytes for comm buffer!\n",
+ commsize);
+ return SANE_STATUS_NO_MEM;
+ }
+ data = comm + 10;
+
+ comm[0] = 0x55;
+ comm[1] = 0;
+ comm[2] = 0x27;
+ comm[3] = 0;
+ comm[4] = 0;
+ comm[5] = 0;
+ comm[6] = 0;
+ comm[7] = ((ms->gamma_entries * ms->gamma_entry_size) >> 8) & 0xFF;
+ comm[8] = (ms->gamma_entries * ms->gamma_entry_size) & 0xFF;
+ comm[9] = (ms->gamma_entry_size == 2) ? 1 : 0;
+
+ if (!(strcmp(ms->val[OPT_CUSTOM_GAMMA].s, M_TABLE))) {
+ /***** Gamma by TABLE *****/
+ int table_shift = (ms->gamma_bit_depth - bit_depth);
+
+ DBG(23, ".download_gamma: by table (%d bpe, %d shift)\n",
+ ms->gamma_bit_depth, table_shift);
+
+ if (ms->val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = ms->gray_lut[i] >> table_shift;
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ } else {
+ pl = 1;
+ do {
+ SANE_Int *pl_lut;
+ switch (pl) {
+ case 1: pl_lut = ms->red_lut; break;
+ case 2: pl_lut = ms->green_lut; break;
+ case 3: pl_lut = ms->blue_lut; break;
+ default:
+ DBG(23, ".download_gamma: uh, exceeded pl bound!\n");
+ free(comm);
+ return SANE_STATUS_INVAL; /* XXXXXXXxx */
+ break;
+ }
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = pl_lut[i] >> table_shift;
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ /* XXXXXXX */
+ comm[9] = (comm[9] & 0x3F) | (pl << 6);
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ pl++;
+ } while ((pl < 4) && (status == SANE_STATUS_GOOD));
+ }
+ } else if (!(strcmp(ms->val[OPT_CUSTOM_GAMMA].s, M_SCALAR))) {
+ /***** Gamma by SCALAR *****/
+ DBG(23, ".download_gamma: by scalar\n");
+ if (ms->val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ double gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA].w);
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = (max_entry *
+ pow((double) i / ((double) ms->gamma_entries - 1.0),
+ 1.0 / gamma));
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ } else {
+ double gamma;
+ pl = 1;
+ do {
+ switch (pl) {
+ case 1: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_R].w); break;
+ case 2: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_G].w); break;
+ case 3: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_B].w); break;
+ default: gamma = 1.0; break; /* should never happen */
+ }
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = (max_entry *
+ pow((double) i / ((double) ms->gamma_entries - 1.0),
+ 1.0 / gamma));
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ comm[9] = (comm[9] & 0x3F) | (pl << 6);
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ pl++;
+ } while ((pl < 4) && (status == SANE_STATUS_GOOD));
+ }
+ } else {
+ /***** No custom Gamma *****/
+ DBG(23, ".download_gamma: by default\n");
+ for (i=0; i<ms->gamma_entries; i++) {
+ /* int val = (((double) max_entry * (double) i /
+ ((double) ms->gamma_entries - 1.0)) + 0.5); ROUNDING????*/
+ int val =
+ (double) max_entry * (double) i /
+ ((double) ms->gamma_entries - 1.0);
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val >> 8) & 0xFF;
+ break;
+ }
+ }
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ }
+ free(comm);
+ return status;
+}
+
+
+/********************************************************************/
+/* magic command to start calibration */
+/********************************************************************/
+static SANE_Status
+start_calibration(Microtek_Scanner *ms)
+{
+ uint8_t comm[8] = { 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0a };
+
+ DBG(23, ".start_calibrate...\n");
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "STCal:");
+ for (i=0;i<8;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("STCal:");
+ for (i=0;i<8;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 8, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* magic command to download calibration values */
+/********************************************************************/
+static SANE_Status
+download_calibration(Microtek_Scanner *ms, uint8_t *comm,
+ uint8_t letter, int linewidth)
+{
+ DBG(23, ".download_calibration... %c %d\n", letter, linewidth);
+
+ comm[0] = 0x0c;
+ comm[1] = 0x00;
+ comm[2] = 0x00;
+ comm[3] = (linewidth >> 8) & 0xFF;
+ comm[4] = linewidth & 0xFF;
+ comm[5] = 0x00;
+
+ comm[6] = 0x00;
+ switch (letter) {
+ case 'R': comm[7] = 0x40; break;
+ case 'G': comm[7] = 0x80; break;
+ case 'B': comm[7] = 0xc0; break;
+ default: /* XXXXXXX */ break;
+ }
+
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + linewidth, 0, 0);
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/* */
+/* Myriad of internal functions */
+/* */
+/********************************************************************/
+/********************************************************************/
+
+
+
+/********************************************************************/
+/* Initialize the options registry */
+/********************************************************************/
+static SANE_Status
+init_options(Microtek_Scanner *ms)
+{
+ int i;
+ SANE_Option_Descriptor *sod = ms->sod;
+ Option_Value *val = ms->val;
+
+ DBG(15, "init_options...\n");
+
+ memset(ms->sod, 0, sizeof(ms->sod));
+ memset(ms->val, 0, sizeof(ms->val));
+ /* default: software selectable word options... */
+ for (i=0; i<NUM_OPTIONS; i++) {
+ sod[i].size = sizeof(SANE_Word);
+ sod[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ sod[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ sod[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ sod[OPT_NUM_OPTS].size = sizeof (SANE_Word);
+ sod[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ sod[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* The Scan Mode Group */
+ sod[OPT_MODE_GROUP].name = "";
+ sod[OPT_MODE_GROUP].title = "Scan Mode";
+ sod[OPT_MODE_GROUP].desc = "";
+ sod[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_MODE_GROUP].cap = 0;
+ sod[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ sod[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ sod[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ sod[OPT_MODE].type = SANE_TYPE_STRING;
+ sod[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ {
+ SANE_String_Const *mode_list;
+ mode_list = (SANE_String_Const *) malloc(5 * sizeof(SANE_String_Const));
+ if (mode_list == NULL) return SANE_STATUS_NO_MEM;
+ i = 0;
+ if (ms->dev->info.modes & MI_MODES_COLOR) mode_list[i++] = M_COLOR;
+ if (ms->dev->info.modes & MI_MODES_GRAY) mode_list[i++] = M_GRAY;
+ if (ms->dev->info.modes & MI_MODES_HALFTONE) mode_list[i++] = M_HALFTONE;
+ if (ms->dev->info.modes & MI_MODES_LINEART) mode_list[i++] = M_LINEART;
+ mode_list[i] = NULL;
+ sod[OPT_MODE].constraint.string_list = mode_list;
+ sod[OPT_MODE].size = max_string_size(mode_list);
+ val[OPT_MODE].s = strdup(mode_list[0]);
+ }
+
+ sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ sod[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ sod[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ {
+ SANE_Int maxres = ms->dev->info.base_resolution;
+
+ ms->res_range.max = SANE_FIX(maxres);
+ ms->exp_res_range.max = SANE_FIX(2 * maxres);
+ if (ms->dev->info.res_step & MI_RESSTEP_1PER) {
+ DBG(23, "init_options: quant yes\n");
+ ms->res_range.min = SANE_FIX( maxres / 100 );
+ ms->res_range.quant = ms->res_range.min;
+ ms->exp_res_range.min = SANE_FIX(2 * maxres / 100);
+ ms->exp_res_range.quant = ms->exp_res_range.min;
+ } else {
+ /* XXXXXXXXXXX */
+ DBG(23, "init_options: quant no\n");
+ ms->res_range.quant = SANE_FIX( 0 );
+ }
+ sod[OPT_RESOLUTION].constraint.range = &(ms->res_range);
+ }
+ val[OPT_RESOLUTION].w = SANE_FIX(100);
+
+ sod[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ sod[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ sod[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ sod[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ sod[OPT_HALFTONE_PATTERN].size = max_string_size(halftone_mode_list);
+ sod[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_mode_list;
+ val[OPT_HALFTONE_PATTERN].s = strdup(halftone_mode_list[0]);
+
+ sod[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ sod[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ sod[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ sod[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ sod[OPT_NEGATIVE].cap |=
+ (ms->dev->info.modes & MI_MODES_NEGATIVE) ? 0 : SANE_CAP_INACTIVE;
+ val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ sod[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ sod[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ /* sod[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;*/
+ sod[OPT_SPEED].desc = "Scan speed throttle -- higher values are *slower*.";
+ sod[OPT_SPEED].type = SANE_TYPE_INT;
+ sod[OPT_SPEED].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_SPEED].unit = SANE_UNIT_NONE;
+ sod[OPT_SPEED].size = sizeof(SANE_Word);
+ sod[OPT_SPEED].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_SPEED].constraint.range = &speed_range;
+ val[OPT_SPEED].w = 1;
+
+ sod[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ sod[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ sod[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ sod[OPT_SOURCE].type = SANE_TYPE_STRING;
+ sod[OPT_SOURCE].unit = SANE_UNIT_NONE;
+ sod[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ {
+ SANE_String_Const *source_list;
+ source_list = (SANE_String_Const *) malloc(4 * sizeof(SANE_String_Const));
+ if (source_list == NULL) return SANE_STATUS_NO_MEM;
+ i = 0;
+ source_list[i++] = M_OPAQUE;
+ if (ms->dev->info.source_options & MI_SRC_HAS_TRANS)
+ source_list[i++] = M_TRANS;
+ if (ms->dev->info.source_options & MI_SRC_HAS_FEED)
+ source_list[i++] = M_AUTOFEED;
+ source_list[i] = NULL;
+ sod[OPT_SOURCE].constraint.string_list = source_list;
+ sod[OPT_SOURCE].size = max_string_size(source_list);
+ if (i < 2)
+ sod[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ val[OPT_SOURCE].s = strdup(source_list[0]);
+ }
+
+ sod[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ sod[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ sod[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ sod[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ sod[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ sod[OPT_PREVIEW].size = sizeof(SANE_Word);
+ val[OPT_PREVIEW].w = SANE_FALSE;
+
+
+ sod[OPT_GEOMETRY_GROUP].name = "";
+ sod[OPT_GEOMETRY_GROUP].title = "Geometry";
+ sod[OPT_GEOMETRY_GROUP].desc = "";
+ sod[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ sod[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ sod[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ sod[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ sod[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ sod[OPT_TL_X].type = SANE_TYPE_FIXED;
+ sod[OPT_TL_X].unit = SANE_UNIT_MM;
+ sod[OPT_TL_X].size = sizeof(SANE_Word);
+ sod[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ sod[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ sod[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ sod[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ sod[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ sod[OPT_TL_Y].unit = SANE_UNIT_MM;
+ sod[OPT_TL_Y].size = sizeof(SANE_Word);
+ sod[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ sod[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ sod[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ sod[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ sod[OPT_BR_X].type = SANE_TYPE_FIXED;
+ sod[OPT_BR_X].unit = SANE_UNIT_MM;
+ sod[OPT_BR_X].size = sizeof(SANE_Word);
+ sod[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ sod[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ sod[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ sod[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ sod[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ sod[OPT_BR_Y].unit = SANE_UNIT_MM;
+ sod[OPT_BR_Y].size = sizeof(SANE_Word);
+ sod[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ sod[OPT_TL_X].constraint.range =
+ sod[OPT_BR_X].constraint.range = &(ms->dev->info.doc_x_range);
+ sod[OPT_TL_Y].constraint.range =
+ sod[OPT_BR_Y].constraint.range = &(ms->dev->info.doc_y_range);
+
+ /* set default scan region to be maximum size */
+ val[OPT_TL_X].w = sod[OPT_TL_X].constraint.range->min;
+ val[OPT_TL_Y].w = sod[OPT_TL_Y].constraint.range->min;
+ val[OPT_BR_X].w = sod[OPT_BR_X].constraint.range->max;
+ val[OPT_BR_Y].w = sod[OPT_BR_Y].constraint.range->max;
+
+ sod[OPT_ENHANCEMENT_GROUP].name = "";
+ sod[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ sod[OPT_ENHANCEMENT_GROUP].desc = "";
+ sod[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_ENHANCEMENT_GROUP].cap = 0;
+ sod[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_EXPOSURE].name = "exposure";
+ sod[OPT_EXPOSURE].title = "Exposure";
+ sod[OPT_EXPOSURE].desc = "Analog exposure control";
+ sod[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE].size = sizeof(SANE_Word);
+ sod[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ ms->exposure_range.min = ms->dev->info.min_exposure;
+ ms->exposure_range.max = ms->dev->info.max_exposure;
+ ms->exposure_range.quant = 3;
+ sod[OPT_EXPOSURE].constraint.range = &(ms->exposure_range);
+ val[OPT_EXPOSURE].w = 0;
+
+ sod[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ sod[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BRIGHTNESS].size = sizeof(SANE_Word);
+ sod[OPT_BRIGHTNESS].cap |=
+ ((ms->dev->info.extra_cap & MI_EXCAP_OFF_CTL) ? 0: SANE_CAP_INACTIVE);
+ sod[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ val[OPT_BRIGHTNESS].w = 0;
+
+ sod[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ sod[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ sod[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ sod[OPT_CONTRAST].type = SANE_TYPE_INT;
+ sod[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ sod[OPT_CONTRAST].size = sizeof(SANE_Word);
+ sod[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ ms->contrast_range.min = ms->dev->info.min_contrast;
+ ms->contrast_range.max = ms->dev->info.max_contrast;
+ ms->contrast_range.quant = 7;
+ sod[OPT_CONTRAST].constraint.range = &(ms->contrast_range);
+ val[OPT_CONTRAST].w = 0;
+
+
+ sod[OPT_HIGHLIGHT].name = SANE_NAME_WHITE_LEVEL;
+ sod[OPT_HIGHLIGHT].title = SANE_TITLE_WHITE_LEVEL;
+ sod[OPT_HIGHLIGHT].desc = SANE_DESC_WHITE_LEVEL;
+ sod[OPT_HIGHLIGHT].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT].unit = SANE_UNIT_NONE;
+ sod[OPT_HIGHLIGHT].size = sizeof(SANE_Word);
+ sod[OPT_HIGHLIGHT].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_HIGHLIGHT].constraint.range = &u8_range;
+ val[OPT_HIGHLIGHT].w = 255;
+
+ sod[OPT_SHADOW].name = SANE_NAME_BLACK_LEVEL;
+ sod[OPT_SHADOW].title = SANE_TITLE_BLACK_LEVEL;
+ sod[OPT_SHADOW].desc = SANE_DESC_BLACK_LEVEL;
+ sod[OPT_SHADOW].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW].unit = SANE_UNIT_NONE;
+ sod[OPT_SHADOW].size = sizeof(SANE_Word);
+ sod[OPT_SHADOW].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_SHADOW].constraint.range = &u8_range;
+ val[OPT_SHADOW].w = 0;
+
+ if (ms->dev->info.enhance_cap & MI_ENH_CAP_SHADOW) {
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_SHADOW].cap |= SANE_CAP_ADVANCED;
+ } else {
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ }
+
+ sod[OPT_MIDTONE].name = "midtone";
+ sod[OPT_MIDTONE].title = "Midtone Level";
+ sod[OPT_MIDTONE].desc = "Midtone Level";
+ sod[OPT_MIDTONE].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE].unit = SANE_UNIT_NONE;
+ sod[OPT_MIDTONE].size = sizeof(SANE_Word);
+ sod[OPT_MIDTONE].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_MIDTONE].constraint.range = &u8_range;
+ val[OPT_MIDTONE].w = 128;
+ if (ms->midtone_support) {
+ sod[OPT_MIDTONE].cap |= SANE_CAP_ADVANCED;
+ } else {
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ }
+ /* XXXXXXXX is this supported by all scanners??
+ if ((strcmp(M_COLOR, val[OPT_MODE].s)) &&
+ (strcmp(M_GRAY, val[OPT_MODE].s))) {
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ }
+ */
+
+ sod[OPT_GAMMA_GROUP].name = "";
+ sod[OPT_GAMMA_GROUP].title = "Gamma Control";
+ sod[OPT_GAMMA_GROUP].desc = "";
+ sod[OPT_GAMMA_GROUP].type = SANE_TYPE_GROUP;
+ if (!(ms->gamma_entries))
+ sod[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_CUSTOM_GAMMA].name = "gamma-mode";
+ sod[OPT_CUSTOM_GAMMA].title = "Gamma Control Mode";
+ sod[OPT_CUSTOM_GAMMA].desc = "How to specify gamma correction, if at all";
+ sod[OPT_CUSTOM_GAMMA].type = SANE_TYPE_STRING;
+ sod[OPT_CUSTOM_GAMMA].size = max_string_size(gamma_mode_list);
+ sod[OPT_CUSTOM_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_CUSTOM_GAMMA].constraint.string_list = gamma_mode_list;
+ if (!(ms->gamma_entries))
+ sod[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ val[OPT_CUSTOM_GAMMA].s = strdup(gamma_mode_list[0]);
+
+ sod[OPT_GAMMA_BIND].name = SANE_NAME_ANALOG_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].title = SANE_TITLE_ANALOG_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].desc = SANE_DESC_ANALOG_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ val[OPT_GAMMA_BIND].w = SANE_TRUE;
+
+ sod[OPT_ANALOG_GAMMA].name = SANE_NAME_ANALOG_GAMMA;
+ sod[OPT_ANALOG_GAMMA].title = SANE_TITLE_ANALOG_GAMMA;
+ sod[OPT_ANALOG_GAMMA].desc = SANE_DESC_ANALOG_GAMMA;
+ sod[OPT_ANALOG_GAMMA].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA].w = SANE_FIX(1.0);
+
+ sod[OPT_ANALOG_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ sod[OPT_ANALOG_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ sod[OPT_ANALOG_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ sod[OPT_ANALOG_GAMMA_R].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA_R].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA_R].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA_R].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA_R].w = SANE_FIX(1.0);
+
+ sod[OPT_ANALOG_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ sod[OPT_ANALOG_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ sod[OPT_ANALOG_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ sod[OPT_ANALOG_GAMMA_G].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA_G].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA_G].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA_G].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA_G].w = SANE_FIX(1.0);
+
+ sod[OPT_ANALOG_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ sod[OPT_ANALOG_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ sod[OPT_ANALOG_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ sod[OPT_ANALOG_GAMMA_B].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA_B].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA_B].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA_B].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA_B].w = SANE_FIX(1.0);
+
+ sod[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ sod[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ sod[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ sod[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR].wa = ms->gray_lut;
+
+ sod[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR_R].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR_R].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR_R].wa = ms->red_lut;
+
+ sod[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR_G].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR_G].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR_G].wa = ms->green_lut;
+
+ sod[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR_B].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR_B].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR_B].wa = ms->blue_lut;
+
+ sod[OPT_EXP_RES].name = "exp_res";
+ sod[OPT_EXP_RES].title = "Expanded Resolution";
+ sod[OPT_EXP_RES].desc = "Enable double-resolution scans";
+ sod[OPT_EXP_RES].type = SANE_TYPE_BOOL;
+ sod[OPT_EXP_RES].cap |= SANE_CAP_ADVANCED;
+ if (!(ms->dev->info.expanded_resolution))
+ sod[OPT_EXP_RES].cap |= SANE_CAP_INACTIVE;
+ val[OPT_EXP_RES].w = SANE_FALSE;
+
+ sod[OPT_CALIB_ONCE].name = "calib_once";
+ sod[OPT_CALIB_ONCE].title = "Calibrate Only Once";
+ sod[OPT_CALIB_ONCE].desc = "Avoid CCD calibration on every scan" \
+ "(toggle off/on to cause calibration on next scan)";
+ sod[OPT_CALIB_ONCE].type = SANE_TYPE_BOOL;
+ sod[OPT_CALIB_ONCE].cap |= SANE_CAP_ADVANCED;
+ if (!(ms->do_real_calib)) {
+ sod[OPT_CALIB_ONCE].cap |= SANE_CAP_INACTIVE;
+ val[OPT_CALIB_ONCE].w = SANE_FALSE;
+ } else
+ val[OPT_CALIB_ONCE].w = SANE_TRUE;
+
+ /*
+ sod[OPT_].name = SANE_NAME_;
+ sod[OPT_].title = SANE_TITLE_;
+ sod[OPT_].desc = SANE_DESC_;
+ sod[OPT_].type = SANE_TYPE_;
+ sod[OPT_].unit = SANE_UNIT_NONE;
+ sod[OPT_].size = sizeof(SANE_Word);
+ sod[OPT_].cap = 0;
+ sod[OPT_].constraint_type = SANE_CONSTRAINT_NONE;
+ sod[OPT_].constraint = ;
+ val[OPT_].w = ;
+ */
+
+ DBG(15, "init_options: done.\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Parse an INQUIRY information block, as returned by scanner */
+/********************************************************************/
+static SANE_Status
+parse_inquiry(Microtek_Info *mi, unsigned char *result)
+{
+#if 0
+ unsigned char result[0x60] = {
+ 0x06,0x31,0x23,0x01,0x5b,0x00,0x00,0x00,0x41,0x47,0x46,0x41,0x20,0x20,0x20,0x20,
+ 0x53,0x74,0x75,0x64,0x69,0x6f,0x53,0x63,0x61,0x6e,0x20,0x49,0x49,0x20,0x20,0x20,
+ 0x32,0x2e,0x33,0x30,0x53,0x43,0x53,0x49,0x20,0x46,0x2f,0x57,0x56,0x33,0x2e,0x31,
+ 0x20,0x43,0x54,0x4c,0x35,0x33,0x38,0x30,0x03,0x4f,0x8c,0xc5,0x00,0xee,0x5b,0x43,
+ 0x01,0x01,0x02,0x00,0x00,0x03,0x00,0x01,0x00,0x4a,0x01,0x04,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff
+ };
+#endif
+ DBG(15, "parse_inquiry...\n");
+ strncpy(mi->vendor_id, (char *)&result[8], 8);
+ strncpy(mi->model_name, (char *)&result[16], 16);
+ strncpy(mi->revision_num, (char *)&result[32], 4);
+ strncpy(mi->vendor_string, (char *)&result[36], 20);
+ mi->vendor_id[8] = 0;
+ mi->model_name[16] = 0;
+ mi->revision_num[4] = 0;
+ mi->vendor_string[20] = 0;
+
+ mi->device_type = (SANE_Byte)(result[0] & 0x1f);
+ mi->SCSI_firmware_ver_major = (SANE_Byte)((result[1] & 0xf0) >> 4);
+ mi->SCSI_firmware_ver_minor = (SANE_Byte)(result[1] & 0x0f);
+ mi->scanner_firmware_ver_major = (SANE_Byte)((result[2] & 0xf0) >> 4);
+ mi->scanner_firmware_ver_minor = (SANE_Byte)(result[2] & 0x0f);
+ mi->response_data_format = (SANE_Byte)(result[3]);
+
+ mi->res_step = (SANE_Byte)(result[56] & 0x03);
+ mi->modes = (SANE_Byte)(result[57]);
+ mi->pattern_count = (SANE_Int)(result[58] & 0x7f);
+ mi->pattern_dwnld = (SANE_Byte)(result[58] & 0x80) > 0;
+ mi->feed_type = (SANE_Byte)(result[59] & 0x0F);
+ mi->compress_type = (SANE_Byte)(result[59] & 0x30);
+ mi->unit_type = (SANE_Byte)(result[59] & 0xC0);
+
+ mi->doc_size_code = (SANE_Byte)result[60];
+ /* we'll compute the max sizes after we know base resolution... */
+
+ /* why are these things set in two places (and probably wrong anyway)? */
+ mi->cont_settings = (SANE_Int)((result[61] & 0xf0) >> 4);
+ if ((SANE_Int)(result[72]))
+ mi->cont_settings = (SANE_Int)(result[72]);
+ mi->min_contrast = -42;
+ mi->max_contrast = (mi->cont_settings * 7) - 49;
+
+ mi->exp_settings = (SANE_Int)(result[61] & 0x0f);
+ if ((SANE_Int)(result[73]))
+ mi->exp_settings = (SANE_Int)(result[73]);
+ mi->min_exposure = -18;
+ mi->max_exposure = (mi->exp_settings * 3) - 21;
+#if 0
+ mi->contrast_vals = (SANE_Int)(result[72]);
+ mi->min_contrast = -42;
+ mi->max_contrast = 49;
+ if (mi->contrast_vals)
+ mi->max_contrast = (mi->contrast_vals * 7) - 49;
+
+ mi->exposure_vals = (SANE_Int)(result[73]);
+ mi->min_exposure = -18;
+ mi->max_exposure = 21;
+ if (mi->exposure_vals)
+ mi->max_exposure = (mi->exposure_vals * 3) - 21;
+#endif
+
+ mi->model_code = (SANE_Byte)(result[62]);
+ switch (mi->model_code) {
+ case 0x16: /* the other ScanMaker 600ZS */
+ case 0x50: /* ScanMaker II/IIXE */
+ case 0x54: /* ScanMaker IISP */
+ case 0x55: /* ScanMaker IIER */
+ case 0x58: /* ScanMaker IIG */
+ case 0x5a: /* Agfa StudioScan (untested!) */
+ case 0x5f: /* ScanMaker E3 */
+ case 0x56: /* ScanMaker A3t */
+ case 0x64: /* ScanMaker E2 (,Vobis RealScan) */
+ case 0x65: /* Color PageWiz */
+ case 0xC8: /* ScanMaker 600ZS */
+ mi->base_resolution = 300;
+ break;
+ case 0x5b: /* Agfa StudioScan II/IIsi (untested!) */
+ mi->base_resolution = 400;
+ break;
+ case 0x57: /* ScanMaker IIHR */
+ case 0x59: /* ScanMaker III */
+ case 0x5c: /* Agfa Arcus II */
+ case 0x5e: /* Agfa StudioStar */
+ case 0x63: /* ScanMaker E6 */
+ case 0x66: /* ScanMaker E6 (new)*/
+ mi->base_resolution = 600;
+ break;
+ case 0x51: /* ScanMaker 45t */
+ case 0x5d: /* Agfa DuoScan */
+ mi->base_resolution = 1000;
+ break;
+ case 0x52: /* ScanMaker 35t */
+ mi->base_resolution = 1828;
+ break;
+ case 0x62: /* ScanMaker 35t+, Polaroid 35/LE */
+ mi->base_resolution = 1950;
+ break;
+ default:
+ mi->base_resolution = 300;
+ DBG(15, "parse_inquiry: Unknown base resolution for 0x%x!\n",
+ mi->model_code);
+ break;
+ }
+
+ /* Our max_x,y is in pixels. `Some scanners think in 1/8", though.' */
+ /* max pixel is, of course, total - 1 */
+ switch (mi->doc_size_code) {
+ case 0x00:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 14.0 * mi->base_resolution - 1;
+ break;
+ case 0x01:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 11.0 * mi->base_resolution - 1;
+ break;
+ case 0x02:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 11.69 * mi->base_resolution - 1;
+ break;
+ case 0x03:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 13.0 * mi->base_resolution - 1;
+ break;
+ case 0x04:
+ mi->max_x = 8.0 * mi->base_resolution - 1;
+ mi->max_y = 10.0 * mi->base_resolution - 1;
+ break;
+ case 0x05:
+ mi->max_x = 8.3 * mi->base_resolution - 1;
+ mi->max_y = 14.0 * mi->base_resolution - 1;
+ break;
+ case 0x06:
+ mi->max_x = 8.3 * mi->base_resolution - 1;
+ mi->max_y = 13.5 * mi->base_resolution - 1;
+ break;
+ case 0x07:
+ mi->max_x = 8.0 * mi->base_resolution - 1;
+ mi->max_y = 14.0 * mi->base_resolution - 1;
+ break;
+ case 0x80:
+ /* Slide format, size is mm */
+ mi->max_x = (35.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ mi->max_y = (35.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ break;
+ case 0x81:
+ mi->max_x = 5.0 * mi->base_resolution - 1;
+ mi->max_y = 5.0 * mi->base_resolution - 1;
+ break;
+ case 0x82:
+ /* Slide format, size is mm */
+ mi->max_x = (36.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ mi->max_y = (36.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ break;
+ default:
+ /* Undefined document format code */
+ mi->max_x = mi->max_y = 0;
+ DBG(15, "parse_inquiry: Unknown doc_size_code! 0x%x\n",
+ mi->doc_size_code);
+ }
+
+ /* create the proper range constraints, given the doc size */
+ {
+ /* we need base resolution in dots-per-millimeter... */
+ float base_res_dpmm = (float) mi->base_resolution / MM_PER_INCH;
+ mi->doc_x_range.min = SANE_FIX(0);
+ mi->doc_x_range.max = SANE_FIX((float)mi->max_x / base_res_dpmm);
+ mi->doc_x_range.quant = SANE_FIX(0);
+ mi->doc_y_range.min = SANE_FIX(0);
+ mi->doc_y_range.max = SANE_FIX((float)mi->max_y / base_res_dpmm);
+ mi->doc_y_range.quant = SANE_FIX(0);
+ }
+
+ mi->source_options = (SANE_Byte)(result[63]);
+
+ mi->expanded_resolution = (result[64] & 0x01);
+ /* my E6 reports exp-res capability incorrectly */
+ if ((mi->model_code == 0x66) || (mi->model_code == 0x63)) {
+ mi->expanded_resolution = 0xFF;
+ DBG(4, "parse_inquiry: E6 falsely denies expanded resolution.\n");
+ }
+ /* the StudioScan II(si) does the expanded-mode aspect correction
+ within the scanner... (do others too?) */
+ if (mi->model_code == 0x5b) {
+ DBG(4, "parse_inquiry: does expanded-mode expansion internally.\n");
+ mi->does_expansion = 1;
+ } else
+ mi->does_expansion = 0;
+
+ mi->enhance_cap = (result[65] & 0x03);
+
+ /*
+ switch (result[66] & 0x0F) {
+ case 0x00: mi->max_lookup_size = 0; break;
+ case 0x01: mi->max_lookup_size = 256; break;
+ case 0x03: mi->max_lookup_size = 1024; break;
+ case 0x05: mi->max_lookup_size = 4096; break;
+ case 0x09: mi->max_lookup_size = 65536; break;
+ default:
+ mi->max_lookup_size = 0;
+ DBG(15, "parse_inquiry: Unknown gamma LUT size! 0x%x\n",
+ result[66]);
+ }
+ */
+
+ /* This is not how the vague documentation specifies this register.
+ We're going to take it literally here -- i.e. if the bit is
+ set, the scanner supports the value, otherwise it doesn't.
+ (The docs say all lower values are always supported. This is
+ not the case for the StudioScan IIsi, at least, which only
+ specifies 0x02==1024-byte table, and only supports that, too.)
+
+ All-in-all, it doesn't matter, since we take the largest
+ allowed LUT size anyway.
+ */
+ if (result[66] & 0x08)
+ mi->max_lookup_size = 65536;
+ else if (result[66] & 0x04)
+ mi->max_lookup_size = 4096;
+ else if (result[66] & 0x02)
+ mi->max_lookup_size = 1024;
+ else if (result[66] & 0x01)
+ mi->max_lookup_size = 256;
+ else
+ mi->max_lookup_size = 0;
+
+ /* my E6 reports incorrectly */
+ if ((mi->model_code == 0x66) || (mi->model_code == 0x63)) {
+ mi->max_lookup_size = 1024;
+ DBG(4, "parse_inquiry: E6 falsely denies 1024-byte LUT.\n");
+ }
+
+ /*
+ switch (result[66] >> 5) {
+ case 0x00: mi->max_gamma_val = 255; mi->gamma_size = 1; break;
+ case 0x01: mi->max_gamma_val = 1023; mi->gamma_size = 2; break;
+ case 0x02: mi->max_gamma_val = 4095; mi->gamma_size = 2; break;
+ case 0x03: mi->max_gamma_val = 65535; mi->gamma_size = 2; break;
+ default:
+ mi->max_gamma_val = 0; mi->gamma_size = 0;
+ DBG(15, "parse_inquiry: Unknown gamma max val! 0x%x\n",
+ result[66]);
+ }
+ */
+ switch (result[66] >> 5) {
+ case 0x00: mi->max_gamma_bit_depth = 8; mi->gamma_size = 1; break;
+ case 0x01: mi->max_gamma_bit_depth = 10; mi->gamma_size = 2; break;
+ case 0x02: mi->max_gamma_bit_depth = 12; mi->gamma_size = 2; break;
+ case 0x03: mi->max_gamma_bit_depth = 16; mi->gamma_size = 2; break;
+ default:
+ mi->max_gamma_bit_depth = 0; mi->gamma_size = 0;
+ DBG(15, "parse_inquiry: Unknown gamma max val! 0x%x\n",
+ result[66]);
+ }
+
+ mi->fast_color_preview = (SANE_Byte)(result[67] & 0x01);
+ mi->xfer_format_select = (SANE_Byte)(result[68] & 0x01);
+ mi->color_sequence = (SANE_Byte)(result[69] & 0x7f);
+ mi->does_3pass = (SANE_Byte)(!(result[69] & 0x80));
+ mi->does_mode1 = (SANE_Byte)(result[71] & 0x01);
+
+ mi->bit_formats = (SANE_Byte)(result[74] & 0x0F);
+ mi->extra_cap = (SANE_Byte)(result[75] & 0x07);
+
+ /* XXXXXX a quick hack to disable any [pre/real]cal stuff for
+ anything but an E6... */
+ if (!((mi->model_code == 0x66) || (mi->model_code == 0x63))) {
+ mi->extra_cap &= ~MI_EXCAP_DIS_RECAL;
+ DBG(4, "parse_inquiry: Not an E6 -- pretend recal cannot be disabled.\n");
+ }
+
+ /* The E2 lies... */
+ if (mi->model_code == 0x64) {
+ DBG(4, "parse_inquiry: The E2 lies about it's 3-pass heritage.\n");
+ mi->does_3pass = 1;
+ mi->modes &= ~MI_MODES_ONEPASS;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Dump all we know about scanner to stderr */
+/********************************************************************/
+static SANE_Status
+dump_inquiry(Microtek_Info *mi, unsigned char *result)
+{
+ int i;
+
+ DBG(15, "dump_inquiry...\n");
+ DBG(1, " === SANE/Microtek backend v%d.%d.%d ===\n",
+ MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH);
+ DBG(1, "========== Scanner Inquiry Block ========mm\n");
+ for (i=0; i<96; ) {
+ if (!(i % 16)) MDBG_INIT("");
+ MDBG_ADD("%02x ", (int)result[i++]);
+ if (!(i % 16)) MDBG_FINISH(1);
+ }
+ DBG(1, "========== Scanner Inquiry Report ==========\n");
+ DBG(1, "===== Scanner ID...\n");
+ DBG(1, "Device Type Code: 0x%02x\n", mi->device_type);
+ DBG(1, "Model Code: 0x%02x\n", mi->model_code);
+ DBG(1, "Vendor Name: '%s' Model Name: '%s'\n",
+ mi->vendor_id, mi->model_name);
+ DBG(1, "Vendor Specific String: '%s'\n", mi->vendor_string);
+ DBG(1, "Firmware Rev: '%s'\n", mi->revision_num);
+ DBG(1,
+ "SCSI F/W version: %1d.%1d Scanner F/W version: %1d.%1d\n",
+ mi->SCSI_firmware_ver_major, mi->SCSI_firmware_ver_minor,
+ mi->scanner_firmware_ver_major, mi->scanner_firmware_ver_minor);
+ DBG(1, "Response data format: 0x%02x\n", mi->response_data_format);
+
+ DBG(1, "===== Imaging Capabilities...\n");
+ DBG(1, "Modes: %s%s%s%s%s%s%s\n",
+ (mi->modes & MI_MODES_LINEART) ? "Lineart " : "",
+ (mi->modes & MI_MODES_HALFTONE) ? "Halftone " : "",
+ (mi->modes & MI_MODES_GRAY) ? "Gray " : "",
+ (mi->modes & MI_MODES_COLOR) ? "Color " : "",
+ (mi->modes & MI_MODES_TRANSMSV) ? "(X-msv) " : "",
+ (mi->modes & MI_MODES_ONEPASS) ? "(OnePass) " : "",
+ (mi->modes & MI_MODES_NEGATIVE) ? "(Negative) " : "");
+ DBG(1,
+ "Resolution Step Sizes: %s%s Expanded Resolution Support? %s%s\n",
+ (mi->res_step & MI_RESSTEP_1PER) ? "1% " : "",
+ (mi->res_step & MI_RESSTEP_5PER) ? "5%" : "",
+ (mi->expanded_resolution) ? "yes" : "no",
+ (mi->expanded_resolution == 0xFF) ? "(but says no)" : "");
+ DBG(1, "Supported Bits Per Sample: %s8 %s%s%s\n",
+ (mi->bit_formats & MI_FMT_CAP_4BPP) ? "4 " : "",
+ (mi->bit_formats & MI_FMT_CAP_10BPP) ? "10 " : "",
+ (mi->bit_formats & MI_FMT_CAP_12BPP) ? "12 " : "",
+ (mi->bit_formats & MI_FMT_CAP_16BPP) ? "16 " : "");
+ DBG(1, "Max. document size code: 0x%02x\n",
+ mi->doc_size_code);
+ DBG(1, "Max. document size: %d x %d pixels\n",
+ mi->max_x, mi->max_y);
+ DBG(1, "Frame units: %s%s\n",
+ (mi->unit_type & MI_UNIT_PIXELS) ? "pixels " : "",
+ (mi->unit_type & MI_UNIT_8TH_INCH) ? "1/8\"'s " : "");
+ DBG(1, "# of built-in halftones: %d Downloadable patterns? %s\n",
+ mi->pattern_count, (mi->pattern_dwnld) ? "Yes" : "No");
+
+ DBG(1, "Data Compression: %s%s\n",
+ (mi->compress_type & MI_COMPRSS_HUFF) ? "huffman " : "",
+ (mi->compress_type & MI_COMPRSS_RD) ? "read-data " : "");
+ DBG(1, "Contrast Settings: %d Exposure Settings: %d\n",
+ mi->cont_settings, mi->exp_settings);
+ DBG(1, "Adjustable Shadow/Highlight? %s Adjustable Midtone? %s\n",
+ (mi->enhance_cap & MI_ENH_CAP_SHADOW) ? "yes" : "no ",
+ (mi->enhance_cap & MI_ENH_CAP_MIDTONE) ? "yes" : "no ");
+ DBG(1, "Digital brightness/offset? %s\n",
+ (mi->extra_cap & MI_EXCAP_OFF_CTL) ? "yes" : "no");
+ /*
+ fprintf(stderr,
+ "Gamma Table Size: %d entries of %d bytes (max. value: %d)\n",
+ mi->max_lookup_size, mi->gamma_size, mi->max_gamma_val);
+ */
+ DBG(1,
+ "Gamma Table Size: %d entries of %d bytes (max. depth: %d)\n",
+ mi->max_lookup_size, mi->gamma_size, mi->max_gamma_bit_depth);
+
+ DBG(1, "===== Source Options...\n");
+ DBG(1, "Feed type: %s%s ADF support? %s\n",
+ (mi->feed_type & MI_FEED_FLATBED) ? "flatbed " : "",
+ (mi->feed_type & MI_FEED_EDGEFEED) ? "edge-feed " : "",
+ (mi->feed_type & MI_FEED_AUTOSUPP) ? "yes" : "no");
+ DBG(1, "Document Feeder Support? %s Feeder Backtracking? %s\n",
+ (mi->source_options & MI_SRC_FEED_SUPP) ? "yes" : "no ",
+ (mi->source_options & MI_SRC_FEED_BT) ? "yes" : "no ");
+ DBG(1, "Feeder Installed? %s Feeder Ready? %s\n",
+ (mi->source_options & MI_SRC_HAS_FEED) ? "yes" : "no ",
+ (mi->source_options & MI_SRC_FEED_RDY) ? "yes" : "no ");
+ DBG(1, "Transparency Adapter Installed? %s\n",
+ (mi->source_options & MI_SRC_HAS_TRANS) ? "yes" : "no ");
+ /* GET_TRANS GET_FEED XXXXXXXXX */
+ /* mt_SWslct ???? XXXXXXXXXXX */
+ /*#define DOC_ON_FLATBED 0x00
+ #define DOC_IN_FEEDER 0x01
+ #define TRANSPARENCY 0x10
+ */
+ DBG(1, "Fast Color Prescan? %s\n",
+ (mi->fast_color_preview) ? "yes" : "no");
+ DBG(1, "Selectable Transfer Format? %s\n",
+ (mi->xfer_format_select) ? "yes" : "no");
+ MDBG_INIT("Color Transfer Sequence: ");
+ switch (mi->color_sequence) {
+ case MI_COLSEQ_PLANE:
+ MDBG_ADD("plane-by-plane (3-pass)"); break;
+ case MI_COLSEQ_PIXEL:
+ MDBG_ADD("pixel-by-pixel RGB"); break;
+ case MI_COLSEQ_RGB:
+ MDBG_ADD("line-by-line, R-G-B sequence"); break;
+ case MI_COLSEQ_NONRGB:
+ MDBG_ADD("line-by-line, non-sequential with headers"); break;
+ case MI_COLSEQ_2PIXEL:
+ MDBG_ADD("2pixel-by-2pixel RRGGBB"); break;
+ default:
+ MDBG_ADD("UNKNOWN CODE (0x%02x)", mi->color_sequence);
+ }
+ MDBG_FINISH(1);
+ /* if (mi->modes & MI_MODES_ONEPASS) XXXXXXXXXXX */
+ DBG(1, "Three pass scan support? %s\n",
+ (mi->does_3pass ? "yes" : "no"));
+ DBG(1, "ModeSelect-1 and ModeSense-1 Support? %s\n",
+ (mi->does_mode1) ? "yes" : "no");
+ DBG(1, "Can Disable Linearization Table? %s\n",
+ (mi->extra_cap & MI_EXCAP_DIS_LNTBL) ? "yes" : "no");
+ DBG(1, "Can Disable Start-of-Scan Recalibration? %s\n",
+ (mi->extra_cap & MI_EXCAP_DIS_RECAL) ? "yes" : "no");
+
+ DBG(1, "Internal expanded expansion? %s\n",
+ mi->does_expansion ? "yes" : "no");
+ /*
+ fprintf(stderr, "cntr_vals = %d, min_cntr = %d, max_cntr = %d\n",
+ cntr_vals, min_cntr, max_cntr);
+ fprintf(stderr, "exp_vals = %d, min_exp = %d, max_exp = %d\n",
+ exp_vals, min_exp, max_exp);
+ */
+ DBG(1, "====== End of Scanner Inquiry Report =======\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Dump all we know about some unknown scanner to stderr */
+/********************************************************************/
+static SANE_Status
+dump_suspect_inquiry(unsigned char *result)
+{
+ int i;
+ char vendor_id[64], model_name[64], revision_num[16];
+ SANE_Byte device_type, model_code;
+ SANE_Byte SCSI_firmware_ver_major, SCSI_firmware_ver_minor;
+ SANE_Byte scanner_firmware_ver_major, scanner_firmware_ver_minor;
+ SANE_Byte response_data_format;
+
+ DBG(15, "dump_suspect_inquiry...\n");
+ DBG(1, " === SANE/Microtek backend v%d.%d.%d ===\n",
+ MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH);
+ DBG(1, "========== Scanner Inquiry Block ========mm\n");
+ for (i=0; i<96; ) {
+ if (!(i % 16)) MDBG_INIT("");
+ MDBG_ADD("%02x ", (int)result[i++]);
+ if (!(i % 16)) MDBG_FINISH(1);
+ }
+#if 0
+ for (i=0; i<96; i++) {
+ if (!(i % 16) && (i)) fprintf(stderr, "\n");
+ fprintf(stderr, "%02x ", (int)result[i]);
+ }
+ fprintf(stderr, "\n\n");
+#endif
+ strncpy(vendor_id, (char *)&result[8], 8);
+ strncpy(model_name, (char *)&result[16], 16);
+ strncpy(revision_num, (char *)&result[32], 4);
+ vendor_id[8] = 0;
+ model_name[16] = 0;
+ revision_num[5] = 0;
+ device_type = (SANE_Byte)(result[0] & 0x1f);
+ SCSI_firmware_ver_major = (SANE_Byte)((result[1] & 0xf0) >> 4);
+ SCSI_firmware_ver_minor = (SANE_Byte)(result[1] & 0x0f);
+ scanner_firmware_ver_major = (SANE_Byte)((result[2] & 0xf0) >> 4);
+ scanner_firmware_ver_minor = (SANE_Byte)(result[2] & 0x0f);
+ response_data_format = (SANE_Byte)(result[3]);
+ model_code = (SANE_Byte)(result[62]);
+
+ DBG(1, "========== Scanner Inquiry Report ==========\n");
+ DBG(1, "===== Scanner ID...\n");
+ DBG(1, "Device Type Code: 0x%02x\n", device_type);
+ DBG(1, "Model Code: 0x%02x\n", model_code);
+ DBG(1, "Vendor Name: '%s' Model Name: '%s'\n",
+ vendor_id, model_name);
+ DBG(1, "Firmware Rev: '%s'\n", revision_num);
+ DBG(1,
+ "SCSI F/W version: %1d.%1d Scanner F/W version: %1d.%1d\n",
+ SCSI_firmware_ver_major, SCSI_firmware_ver_minor,
+ scanner_firmware_ver_major, scanner_firmware_ver_minor);
+ DBG(1, "Response data format: 0x%02x\n", response_data_format);
+ DBG(1, "====== End of Scanner Inquiry Report =======\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Determine if device is a Microtek Scanner (from INQUIRY info) */
+/********************************************************************/
+static SANE_Status
+id_microtek(uint8_t *result, char **model_string)
+{
+ SANE_Byte device_type, response_data_format;
+ int forcewarn = 0;
+
+ DBG(15, "id_microtek...\n");
+ /* check device type first... */
+ device_type = (SANE_Byte)(result[0] & 0x1f);
+ if (device_type != 0x06) {
+ DBG(15, "id_microtek: not even a scanner: dev_type = %d\n",
+ device_type);
+ return SANE_STATUS_INVAL;
+ }
+ if (!(strncmp("MICROTEK", (char *)&(result[8]), 8)) ||
+ !(strncmp("MII SC31", (char *)&(result[8]), 8)) || /* the IISP */
+ !(strncmp("MII SC21", (char *)&(result[8]), 8)) || /* the 600ZS */
+ !(strncmp("MII SC23", (char *)&(result[8]), 8)) || /* the other 600ZS */
+ !(strncmp("MII SC25", (char *)&(result[8]), 8)) || /* some other 600GS */
+ !(strncmp("AGFA ", (char *)&(result[8]), 8)) || /* Arcus II */
+ !(strncmp("Microtek", (char *)&(result[8]), 8)) || /* some 35t+'s */
+ !(strncmp("Polaroid", (char *)&(result[8]), 8)) || /* SprintScan 35LE */
+ !(strncmp(" ", (char *)&(result[8]), 8)) ) {
+ switch (result[62]) {
+ case 0x16 :
+ *model_string = "ScanMaker 600ZS"; break;
+ case 0x50 :
+ *model_string = "ScanMaker II/IIXE"; break;
+ case 0x51 :
+ *model_string = "ScanMaker 45t"; break;
+ case 0x52 :
+ *model_string = "ScanMaker 35t"; break;
+ case 0x54 :
+ *model_string = "ScanMaker IISP"; break;
+ case 0x55 :
+ *model_string = "ScanMaker IIER"; break;
+ case 0x56 :
+ *model_string = "ScanMaker A3t"; break;
+ case 0x57 :
+ *model_string = "ScanMaker IIHR"; break;
+ case 0x58 :
+ *model_string = "ScanMaker IIG"; break;
+ case 0x59 :
+ *model_string = "ScanMaker III"; break;
+ case 0x5A :
+ *model_string = "Agfa StudioScan"; break;
+ case 0x5B :
+ *model_string = "Agfa StudioScan II"; break;
+ case 0x5C :
+ *model_string = "Agfa Arcus II"; break;
+ case 0x5f :
+ *model_string = "ScanMaker E3"; break;
+ case 0x62 :
+ if (!(strncmp("Polaroid", (char *)&(result[8]), 8)))
+ *model_string = "Polaroid SprintScan 35/LE";
+ else
+ *model_string = "ScanMaker 35t+";
+ break;
+ case 0x63 :
+ case 0x66 :
+ *model_string = "ScanMaker E6"; break;
+ case 0x64 : /* and "Vobis RealScan" */
+ *model_string = "ScanMaker E2"; break;
+ case 0x65:
+ *model_string = "Color PageWiz"; break;
+ case 0xC8:
+ *model_string = "ScanMaker 600ZS"; break;
+ /* the follow are listed in the docs, but are otherwise a mystery... */
+ case 0x5D:
+ *model_string = "Agfa DuoScan"; forcewarn = 1; break;
+ case 0x5E:
+ *model_string = "SS3"; forcewarn = 1; break;
+ case 0x60:
+ *model_string = "HR1"; forcewarn = 1; break;
+ case 0x61:
+ *model_string = "45T+"; forcewarn = 1; break;
+ case 0x67:
+ *model_string = "TR3"; forcewarn = 1; break;
+ default :
+ /* this might be a newer scanner, which uses the SCSI II command set. */
+ /* that's unfortunate, but we'll warn the user anyway.... */
+ response_data_format = (SANE_Byte)(result[3]);
+ if (response_data_format == 0x02) {
+ DBG(15, "id_microtek: (uses new SCSI II command set)\n");
+ if (DBG_LEVEL >= 15) {
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "========== Congratulations! ==========\n");
+ DBG(1, "You appear to be the proud owner of a \n");
+ DBG(1, "brand-new Microtek scanner, which uses\n");
+ DBG(1, "a new SCSI II command set. \n");
+ DBG(1, "\n");
+ DBG(1, "Try the `microtek2' backend instead. \n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ }
+ }
+ return SANE_STATUS_INVAL;
+ }
+ if (forcewarn) {
+ /* force debugging on, to encourage user to send in a report */
+#ifndef NDEBUG
+ DBG_LEVEL = 1;
+#endif
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "========== Congratulations! ==========\n");
+ DBG(1, "Your scanner appears to be supported \n");
+ DBG(1, "by the microtek backend. However, it \n");
+ DBG(1, "has never been tried before, and some \n");
+ DBG(1, "parameters are bound to be wrong. \n");
+ DBG(1, "\n");
+ DBG(1, "Please send the scanner inquiry log in\n");
+ DBG(1, "its entirety to mtek-bugs@mir.com and \n");
+ DBG(1, "include a description of the scanner, \n");
+ DBG(1, "including the base optical resolution.\n");
+ DBG(1, "\n");
+ DBG(1, "You'll find complete instructions for \n");
+ DBG(1, "submitting an error/debug log in the \n");
+ DBG(1, "'sane-microtek' man-page. \n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ }
+ return SANE_STATUS_GOOD;
+ }
+ DBG(15, "id_microtek: not microtek: %d, %d, %d\n",
+ strncmp("MICROTEK", (char *)&(result[8]), 8),
+ strncmp(" ", (char *)&(result[8]), 8),
+ result[62]);
+ return SANE_STATUS_INVAL;
+}
+
+
+
+/********************************************************************/
+/* Try to attach a device as a Microtek scanner */
+/********************************************************************/
+static SANE_Status
+attach_scanner(const char *devicename, Microtek_Device **devp)
+{
+ Microtek_Device *dev;
+ int sfd;
+ size_t size;
+ unsigned char result[0x60];
+ SANE_Status status;
+ char *model_string;
+ uint8_t inquiry[] = { 0x12, 0, 0, 0, 0x60, 0 };
+
+ DBG(15,"attach_scanner: %s\n", devicename);
+ /* check if device is already known... */
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, devicename) == 0) {
+ if (devp) *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* open scsi device... */
+ DBG(20, "attach_scanner: opening %s\n", devicename);
+ if (sanei_scsi_open(devicename, &sfd, sense_handler, NULL) != 0) {
+ DBG(20, "attach_scanner: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* say hello... */
+ DBG(20, "attach_scanner: sending INQUIRY\n");
+ size = sizeof(result);
+ status = sanei_scsi_cmd(sfd, inquiry, sizeof(inquiry), result, &size);
+ sanei_scsi_close (sfd);
+ if (status != SANE_STATUS_GOOD || size != 0x60) {
+ DBG(20, "attach_scanner: inquiry failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ if (id_microtek(result, &model_string) != SANE_STATUS_GOOD) {
+ DBG(15, "attach_scanner: device doesn't look like a Microtek scanner.");
+ if (DBG_LEVEL >= 5) dump_suspect_inquiry(result);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev=malloc(sizeof(*dev));
+ if (!dev) return SANE_STATUS_NO_MEM;
+ memset(dev, 0, sizeof(*dev));
+
+ parse_inquiry(&(dev->info), result);
+ if (DBG_LEVEL > 0) dump_inquiry(&(dev->info), result);
+
+ /* initialize dev structure */
+ dev->sane.name = strdup(devicename);
+ dev->sane.vendor = "Microtek";
+ dev->sane.model = strdup(model_string);
+ dev->sane.type = "flatbed scanner";
+
+ /* link into device list... */
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+ if (devp) *devp = dev;
+
+ DBG(15, "attach_scanner: happy.\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Attach a scanner (convenience wrapper for find_scanners...) */
+/********************************************************************/
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, 0);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/********************************************************************/
+/* End a scan, and clean up afterwards */
+/********************************************************************/
+static SANE_Status end_scan(Microtek_Scanner *s, SANE_Status ostat)
+{
+ SANE_Status status;
+
+ DBG(15, "end_scan...\n");
+ if (s->scanning) {
+ s->scanning = SANE_FALSE;
+ /* stop the scanner */
+ if (s->scan_started) {
+ status = stop_scan(s);
+ if (status != SANE_STATUS_GOOD)
+ DBG(23, "end_scan: OY! on stop_scan\n");
+ s->scan_started = SANE_FALSE;
+ }
+ /* close the SCSI device */
+ if (s->sfd != -1) {
+ sanei_scsi_close(s->sfd);
+ s->sfd = -1;
+ }
+ /* free the buffers we malloc'ed */
+ if (s->scsi_buffer != NULL) {
+ free(s->scsi_buffer);
+ s->scsi_buffer = NULL;
+ }
+ if (s->rb != NULL) {
+ ring_free(s->rb);
+ s->rb = NULL;
+ }
+ }
+ /* if this -was- pass 3, or cancel, then we must be done */
+ if ((s->this_pass == 3) || (s->cancel))
+ s->this_pass = 0;
+ return ostat;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/***** Scan-time operations *****/
+/********************************************************************/
+/********************************************************************/
+
+
+/* number of lines of calibration data returned by scanner */
+#define STRIPS 12 /* well, that's what it seems to be for the E6 */
+
+
+/* simple comparison for the qsort below */
+
+static int comparo(const void *a, const void *b)
+{
+ return (*(const int *)a - *(const int *)b);
+}
+
+
+/* extract values from scanlines and sort */
+
+static void sort_values(int *result, uint8_t *scanline[], int pix)
+{
+ int i;
+ for (i=0; i<STRIPS; i++) result[i] = (scanline[i])[pix];
+ qsort(result, STRIPS, sizeof(result[0]), comparo);
+}
+
+
+/********************************************************************/
+/* Calculate the calibration data. */
+/* This seems to be, for each pixel of each R/G/B ccd, the average */
+/* of the STRIPS# values read by the scanner, presumably off some */
+/* blank spot under the cover. */
+/* The raw scanner data does indeed resemble the intensity profile */
+/* of a lamp. */
+/* The sort is used to calc the median, which is used to remove */
+/* outliers in the data; maybe from dust under the cover? */
+/********************************************************************/
+
+
+static void calc_calibration(uint8_t *caldata, uint8_t *scanline[],
+ int pixels)
+{
+ int i,j;
+ int sorted[STRIPS];
+
+ DBG(23, ".calc_calibration...\n");
+ for (i=0; i<pixels; i++) {
+ int q1, q3;
+ int bot, top;
+ int sum = 0;
+ int count = 0;
+
+ sort_values(sorted, scanline, i);
+ q1 = sorted[STRIPS / 4]; /* first quartile */
+ q3 = sorted[STRIPS * 3 / 4]; /* third quartile */
+ bot = q1 - 3 * (q3 - q1) / 2; /* quick'n'easy bounds */
+ top = q3 + 3 * (q3 - q1) / 2;
+
+ for (j=0; j<STRIPS; j++) {
+ if ((sorted[j] >= bot) && (sorted[j] <= top)) {
+ sum += sorted[j];
+ count++;
+ }
+ }
+ if (count)
+ caldata[i] = (sum + (count / 2)) / count;
+ else {
+ DBG(23, "zero: i=%d b/t=%d/%d ", i, bot, top);
+ if (DBG_LEVEL >= 23) {
+ MDBG_INIT("");
+ for (j=0; j<STRIPS; j++) MDBG_ADD(" %3d", sorted[j]);
+ MDBG_FINISH(23);
+ }
+ caldata[i] = 0;
+ }
+ }
+}
+
+
+
+/********************************************************************/
+/* Calibrate scanner CCD, the "real" way. */
+/* This stuff is not documented in the command set, but this is */
+/* what Microtek's TWAIN driver seems to do, more or less, on an */
+/* E6 at least. What other scanners will do this??? */
+/********************************************************************/
+
+
+static SANE_Status do_real_calibrate(Microtek_Scanner *s)
+{
+ SANE_Status status, statusA;
+ SANE_Int busy, linewidth, lines;
+ size_t buffsize;
+ uint8_t *input, *scanline[STRIPS], *combuff;
+ uint8_t letter;
+ int i, spot;
+ int nmax, ntoget, nleft;
+
+ DBG(10, "do_real_calibrate...\n");
+
+ /* tell scanner to read it's little chart */
+ if ((status = start_calibration(s)) != SANE_STATUS_GOOD) return status;
+ if ((status = get_scan_status(s, &busy, &linewidth, &lines))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "do_real_cal: get_scan_status failed!\n");
+ return status;
+ }
+ /* make room for data in and data out */
+ input = calloc(STRIPS * 3 * linewidth, sizeof(input[0]));
+ combuff = calloc(linewidth + 6, sizeof(combuff[0]));
+ if ((input == NULL) || (combuff == NULL)) {
+ DBG(23, "do_real_cal: bad calloc %p %p\n", input, combuff);
+ free(input);
+ free(combuff);
+ return SANE_STATUS_NO_MEM;
+ }
+ /* read STRIPS lines of R, G, B ccd data */
+ nmax = SCSI_BUFF_SIZE / (3 * linewidth);
+ DBG(23, "do_real_cal: getting data (max=%d)\n", nmax);
+ for (nleft = STRIPS, spot=0;
+ nleft > 0;
+ nleft -= ntoget, spot += buffsize) {
+ ntoget = (nleft > nmax) ? nmax : nleft;
+ buffsize = ntoget * 3 * linewidth;
+ DBG(23, "...nleft %d toget %d size %lu spot %d input+spot %p\n",
+ nleft, ntoget, (u_long) buffsize, spot, input+spot);
+ if ((statusA = read_scan_data(s, ntoget, input+spot, &buffsize))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "...read scan failed\n");
+ break;
+ }
+ }
+ status = stop_scan(s);
+ if ((statusA != SANE_STATUS_GOOD) || (status != SANE_STATUS_GOOD)) {
+ free(input);
+ free(combuff);
+ return ((statusA != SANE_STATUS_GOOD) ? statusA : status);
+ }
+ /* calculate calibration data for each element and download */
+ for (letter = 'R'; letter != 'X'; ) {
+ DBG(23, "do_real_calibrate: working on %c\n", letter);
+ for (spot=0, i=0; spot < linewidth * STRIPS * 3; spot += linewidth) {
+ if (input[spot+1] == letter) {
+ DBG(23, " found %d (at %d)\n", i, spot);
+ if (i >= STRIPS) {
+ DBG(23, "WHOA!!! %i have already been found!\n", i);
+ break;
+ }
+ scanline[i] = &(input[spot+2]);
+ i++;
+ }
+ }
+ calc_calibration(combuff + 8, scanline, linewidth - 2);
+ if ((status = download_calibration(s, combuff, letter, linewidth))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "...download_calibration failed\n");
+ free(input);
+ free(combuff);
+ return status;
+ }
+ switch (letter) {
+ case 'R': letter = 'G'; break;
+ case 'G': letter = 'B'; break;
+ case 'B':
+ default: letter = 'X'; break;
+ }
+ }
+ /* clean up */
+ free(input);
+ free(combuff);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/********************************************************************/
+/* Cause scanner to calibrate, but don't really scan anything */
+/* (i.e. do everything but read data) */
+/********************************************************************/
+static SANE_Status do_precalibrate(SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status, statusA;
+ SANE_Int busy, linewidth, lines;
+
+ DBG(10, "do_precalibrate...\n");
+
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status;
+ {
+ SANE_Int y1 = s->y1;
+ SANE_Int y2 = s->y2;
+ /* some small range, but large enough to cause the scanner
+ to think it'll scan *something*... */
+ s->y1 = 0;
+ s->y2 =
+ (s->resolution > s->dev->info.base_resolution) ?
+ 4 : 4 * s->dev->info.base_resolution / s->resolution;
+ status = scanning_frame(s);
+ s->y1 = y1;
+ s->y2 = y2;
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ if (s->dev->info.source_options &
+ (MI_SRC_FEED_BT | MI_SRC_HAS_TRANS |
+ MI_SRC_FEED_SUPP | MI_SRC_HAS_FEED)) { /* ZZZZZZZZZZZ */
+ if ((status = accessory(s)) != SANE_STATUS_GOOD) return status;
+ }
+ if ((status = mode_select(s)) != SANE_STATUS_GOOD) return status;
+ /* why would we even try if this were not true?... */
+ /*if (s->dev->info.extra_cap & MI_EXCAP_DIS_RECAL) */
+ {
+ SANE_Bool allow_calibrate = s->allow_calibrate;
+ s->allow_calibrate = SANE_TRUE;
+ status = mode_select_1(s);
+ s->allow_calibrate = allow_calibrate;
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status;
+ if ((status = start_scan(s)) != SANE_STATUS_GOOD) return status;
+ if ((statusA = get_scan_status(s, &busy,
+ &linewidth, &lines)) != SANE_STATUS_GOOD) {
+ DBG(10, "do_precalibrate: get_scan_status fails\n");
+ }
+ if ((status = stop_scan(s)) != SANE_STATUS_GOOD) return status;
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status;
+ DBG(10, "do_precalibrate done.\n");
+ if (statusA != SANE_STATUS_GOOD)
+ return statusA;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Calibrate scanner, if necessary; record results */
+/********************************************************************/
+static SANE_Status finagle_precal(SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+ int match;
+
+ /* try to check if scanner has been reset */
+ /* if so, calibrate it
+ (either for real, or via a fake scan, with calibration */
+ /* (but only bother if you *could* disable calibration) */
+ DBG(23, "finagle_precal...\n");
+ if ((s->do_clever_precal) || (s->do_real_calib)) {
+ if ((status = compare_mode_sense(s, &match)) != SANE_STATUS_GOOD)
+ return status;
+ if (((s->do_real_calib) && (!s->calib_once)) || /* user want recal */
+ (!match) || /* or, possible reset */
+ ((s->mode == MS_MODE_COLOR) && /* or, other weirdness */
+ (s->precal_record < MS_PRECAL_COLOR)) ||
+ ((s->mode == MS_MODE_COLOR) &&
+ (s->expandedresolution) &&
+ (s->precal_record < MS_PRECAL_EXP_COLOR))) {
+ DBG(23, "finagle_precal: must precalibrate!\n");
+ s->precal_record = MS_PRECAL_NONE;
+ if (s->do_real_calib) { /* do a real calibration if allowed */
+ if ((status = do_real_calibrate(s)) != SANE_STATUS_GOOD)
+ return status;
+ } else if (s->do_clever_precal) {/* otherwise do the fake-scan version */
+ if ((status = do_precalibrate(s)) != SANE_STATUS_GOOD)
+ return status;
+ }
+ if (s->mode == MS_MODE_COLOR) {
+ if (s->expandedresolution)
+ s->precal_record = MS_PRECAL_EXP_COLOR;
+ else
+ s->precal_record = MS_PRECAL_COLOR;
+ } else
+ s->precal_record = MS_PRECAL_GRAY;
+ } else
+ DBG(23, "finagle_precal: no precalibrate necessary.\n");
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Set pass-dependent parameters (for 3-pass color scans) */
+/********************************************************************/
+static void
+set_pass_parameters (SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+
+ if (s->threepasscolor) {
+ s->this_pass += 1;
+ DBG(23, "set_pass_parameters: three-pass, on %d\n", s->this_pass);
+ switch (s->this_pass) {
+ case 1:
+ s->filter = MS_FILT_RED;
+ s->params.format = SANE_FRAME_RED;
+ s->params.last_frame = SANE_FALSE;
+ break;
+ case 2:
+ s->filter = MS_FILT_GREEN;
+ s->params.format = SANE_FRAME_GREEN;
+ s->params.last_frame = SANE_FALSE;
+ break;
+ case 3:
+ s->filter = MS_FILT_BLUE;
+ s->params.format = SANE_FRAME_BLUE;
+ s->params.last_frame = SANE_TRUE;
+ break;
+ default:
+ s->filter = MS_FILT_CLEAR;
+ DBG(23, "set_pass_parameters: What?!? pass %d = filter?\n",
+ s->this_pass);
+ break;
+ }
+ } else
+ s->this_pass = 0;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/***** Packing functions *****/
+/***** ...process raw scanner bytes, and shove into *****/
+/***** the ring buffer *****/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* Process flat (byte-by-byte) data */
+/********************************************************************/
+static SANE_Status pack_flat_data(Microtek_Scanner *s, size_t nlines)
+{
+ SANE_Status status;
+ ring_buffer *rb = s->rb;
+ size_t nbytes = nlines * rb->bpl;
+
+ size_t start = (rb->head_complete + rb->complete_count) % rb->size;
+ size_t max_xfer =
+ (start < rb->head_complete) ?
+ (rb->head_complete - start) :
+ (rb->size - start + rb->head_complete);
+ size_t length = MIN(nbytes, max_xfer);
+
+ if (nbytes > max_xfer) {
+ DBG(23, "pack_flat: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)(nbytes - max_xfer));
+ status = ring_expand(rb, (nbytes - max_xfer));
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ if (s->doexpansion) {
+ unsigned int line, bit;
+ SANE_Byte *sb, *db, byte;
+
+ size_t pos;
+
+ sb = s->scsi_buffer;
+ db = rb->base;
+ pos = start;
+
+ if (!(s->multibit)) {
+ for (line=0; line<nlines; line++) {
+ size_t i;
+ double x1, x2, n1, n2;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < rb->bpl;
+ i++) {
+ byte = 0;
+ for (bit=0;
+ bit < 8;
+ bit++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ /* #define getbit(byte, index) (((byte)>>(index))&1) */
+ byte |=
+ ((
+ (x2 == n2) ?
+ (((sb[(int)n1 / 8])>>(7 - ((int)n1) % 8))&1) :
+ (((double)(((sb[(int)n1/8])>>(7-(int)n1%8))&1) * (n2 - x1) +
+ (double)(((sb[(int)n2/8])>>(7-(int)n2%8))&1) * (x2 - n2)
+ ) / s->exp_aspect)
+ ) > 0.5) << (7 - bit);
+ }
+ db[pos] = byte;
+ if (++pos >= rb->size) pos = 0;
+ }
+ sb += s->pixel_bpl;
+ }
+ } else { /* multibit scan (8 is assumed!) */
+ for (line=0; line<nlines; line++) {
+ double x1, x2, n1, n2;
+ int i;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < s->dest_ppl;
+ i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ db[pos] =
+ (x2 == n2) ?
+ sb[(int)n1] :
+ (int)(((double)sb[(int)n1] * (n2 - x1) +
+ (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect);
+ if (++pos >= rb->size) pos = 0;
+ }
+ sb += s->pixel_bpl;
+ }
+ }
+ } else {
+ /* adjust for rollover!!! */
+ if ((start + length) < rb->size) {
+ memcpy(rb->base + start, s->scsi_buffer, length);
+ } else {
+ size_t chunk1 = rb->size - start;
+ size_t chunk2 = length - chunk1;
+ memcpy(rb->base + start, s->scsi_buffer, chunk1);
+ memcpy(rb->base, s->scsi_buffer + chunk1, chunk2);
+ }
+ }
+ rb->complete_count += length;
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Process sequential R-G-B scan lines (who uses this??? ) */
+/********************************************************************/
+static SANE_Status
+pack_seqrgb_data (Microtek_Scanner *s, size_t nlines)
+{
+ ring_buffer *rb = s->rb;
+ unsigned int seg;
+ SANE_Byte *db = rb->base;
+ SANE_Byte *sb = s->scsi_buffer;
+ size_t completed;
+ size_t spot;
+ SANE_Byte id;
+
+ {
+ size_t ar, ag, ab; /* allowed additions */
+ size_t dr, dg, db; /* additions which will occur */
+ SANE_Status status;
+
+ dr = dg = db = nlines * rb->bpl;
+ ar = rb->size - (rb->complete_count + rb->red_extra * 3);
+ ag = rb->size - (rb->complete_count + rb->green_extra * 3);
+ ab = rb->size - (rb->complete_count + rb->blue_extra * 3);
+ DBG(23, "pack_seq: dr/ar: %lu/%lu dg/ag: %lu/%lu db/ab: %lu/%lu\n",
+ (u_long)dr, (u_long)ar,
+ (u_long)dg, (u_long)ag,
+ (u_long)db, (u_long)ab);
+ if ((dr > ar) ||
+ (dg > ag) ||
+ (db > ab)) {
+ size_t increase = 0;
+ if (dr > ar) increase = (dr - ar);
+ if (dg > ag) increase = MAX(increase, (dg - ag));
+ if (db > ab) increase = MAX(increase, (db - ab));
+ DBG(23, "pack_seq: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)increase);
+ status = ring_expand(rb, increase);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ }
+
+ for (seg = 0, id = 0; seg < nlines * 3; seg++, id = (id+1)%3) {
+ switch (id) {
+ case 0: spot = rb->tail_red; break;
+ case 1: spot = rb->tail_green; break;
+ case 2: spot = rb->tail_blue; break;
+ default:
+ DBG(18, "pack_seq: missing scanline RGB header!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (s->doexpansion) {
+ int i;
+ double x1, x2, n1, n2;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < s->dest_ppl;
+ i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ db[spot] =
+ (x2 == n2) ?
+ sb[(int)n1] :
+ (int)(((double)sb[(int)n1] * (n2 - x1) +
+ (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect);
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ sb += s->ppl;
+ } else {
+ size_t i;
+ for (i=0; i < rb->ppl; i++) {
+ db[spot] = *sb;
+ sb++;
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ }
+
+ switch (id) {
+ case 0: rb->tail_red = spot; rb->red_extra += rb->ppl; break;
+ case 1: rb->tail_green = spot; rb->green_extra += rb->ppl; break;
+ case 2: rb->tail_blue = spot; rb->blue_extra += rb->ppl; break;
+ }
+ }
+
+ completed = MIN(rb->red_extra, MIN(rb->green_extra, rb->blue_extra));
+ rb->complete_count += completed * 3; /* 3 complete bytes per pixel! */
+ rb->red_extra -= completed;
+ rb->green_extra -= completed;
+ rb->blue_extra -= completed;
+
+ DBG(18, "pack_seq: extra r: %lu g: %lu b: %lu\n",
+ (u_long)rb->red_extra,
+ (u_long)rb->green_extra,
+ (u_long)rb->blue_extra);
+ DBG(18, "pack_seq: completed: %lu complete: %lu\n",
+ (u_long)completed, (u_long)rb->complete_count);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Process non-sequential R,G, and B scan-lines */
+/********************************************************************/
+static SANE_Status
+pack_goofyrgb_data(Microtek_Scanner *s, size_t nlines)
+{
+ ring_buffer *rb = s->rb;
+ unsigned int seg; /* , i;*/
+ SANE_Byte *db;
+ SANE_Byte *sb = s->scsi_buffer;
+ size_t completed;
+ size_t spot;
+ SANE_Byte id;
+
+ /* prescan to decide if ring should be expanded */
+ {
+ size_t ar, ag, ab; /* allowed additions */
+ size_t dr, dg, db; /* additions which will occur */
+ SANE_Status status;
+ SANE_Byte *pt;
+
+ for (dr = dg = db = 0, seg = 0, pt = s->scsi_buffer + 1;
+ seg < nlines * 3;
+ seg++, pt += s->ppl + 2) {
+ switch (*pt) {
+ case 'R': dr += rb->bpl; break;
+ case 'G': dg += rb->bpl; break;
+ case 'B': db += rb->bpl; break;
+ }
+ }
+ ar = rb->size - (rb->complete_count + rb->red_extra * 3);
+ ag = rb->size - (rb->complete_count + rb->green_extra * 3);
+ ab = rb->size - (rb->complete_count + rb->blue_extra * 3);
+ DBG(23, "pack_goofy: dr/ar: %lu/%lu dg/ag: %lu/%lu db/ab: %lu/%lu\n",
+ (u_long)dr, (u_long)ar,
+ (u_long)dg, (u_long)ag,
+ (u_long)db, (u_long)ab);
+ /* >, or >= ???????? */
+ if ((dr > ar) ||
+ (dg > ag) ||
+ (db > ab)) {
+ size_t increase = 0;
+ if (dr > ar) increase = (dr - ar);
+ if (dg > ag) increase = MAX(increase, (dg - ag));
+ if (db > ab) increase = MAX(increase, (db - ab));
+ DBG(23, "pack_goofy: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)increase);
+ status = ring_expand(rb, increase);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ }
+
+ db = rb->base;
+ for (seg = 0; seg < nlines * 3; seg++) {
+ sb++; /* skip first byte in line (two byte header) */
+ id = *sb;
+ switch (id) {
+ case 'R': spot = rb->tail_red; break;
+ case 'G': spot = rb->tail_green; break;
+ case 'B': spot = rb->tail_blue; break;
+ default:
+ DBG(18, "pack_goofy: missing scanline RGB header!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ sb++; /* skip the other header byte */
+
+ if (s->doexpansion) {
+ int i;
+ double x1, x2, n1, n2;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < s->dest_ppl;
+ i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ db[spot] =
+ (x2 == n2) ?
+ sb[(int)n1] :
+ (int)(((double)sb[(int)n1] * (n2 - x1) +
+ (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect);
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ sb += s->ppl;
+ } else {
+ unsigned int i;
+ for (i=0; i < rb->ppl; i++) {
+ db[spot] = *sb;
+ sb++;
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ }
+ switch (id) {
+ case 'R': rb->tail_red = spot; rb->red_extra += rb->ppl; break;
+ case 'G': rb->tail_green = spot; rb->green_extra += rb->ppl; break;
+ case 'B': rb->tail_blue = spot; rb->blue_extra += rb->ppl; break;
+ }
+ }
+
+ completed = MIN(rb->red_extra, MIN(rb->green_extra, rb->blue_extra));
+ rb->complete_count += completed * 3; /* 3 complete bytes per pixel! */
+ rb->red_extra -= completed;
+ rb->green_extra -= completed;
+ rb->blue_extra -= completed;
+
+ DBG(18, "pack_goofy: extra r: %lu g: %lu b: %lu\n",
+ (u_long)rb->red_extra,
+ (u_long)rb->green_extra,
+ (u_long)rb->blue_extra);
+ DBG(18, "pack_goofy: completed: %lu complete: %lu\n",
+ (u_long)completed, (u_long)rb->complete_count);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Process R1R2-G1G2-B1B2 double pixels (AGFA StudioStar) */
+/********************************************************************/
+
+static SANE_Status
+pack_seq2r2g2b_data(Microtek_Scanner *s, size_t nlines)
+{
+ SANE_Status status;
+ ring_buffer *rb = s->rb;
+ size_t nbytes = nlines * rb->bpl;
+
+ size_t start = (rb->head_complete + rb->complete_count) % rb->size;
+ size_t max_xfer =
+ (start < rb->head_complete) ?
+ (rb->head_complete - start) :
+ (rb->size - start + rb->head_complete);
+ size_t length = MIN(nbytes, max_xfer);
+
+ if (nbytes > max_xfer) {
+ DBG(23, "pack_2r2g2b: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)(nbytes - max_xfer));
+ status = ring_expand(rb, (nbytes - max_xfer));
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ {
+ unsigned int line;
+ int p;
+ size_t pos = start;
+ SANE_Byte *sb = s->scsi_buffer;
+ SANE_Byte *db = rb->base;
+
+ for (line = 0; line < nlines; line++) {
+ for (p = 0; p < s->dest_ppl; p += 2){
+ /* first pixel */
+ db[pos] = sb[0];
+ if (++pos >= rb->size) pos = 0; /* watch out for ringbuff end? */
+ db[pos] = sb[2];
+ if (++pos >= rb->size) pos = 0;
+ db[pos] = sb[4];
+ if (++pos >= rb->size) pos = 0;
+ /* second pixel */
+ db[pos] = sb[1];
+ if (++pos >= rb->size) pos = 0;
+ db[pos] = sb[3];
+ if (++pos >= rb->size) pos = 0;
+ db[pos] = sb[5];
+ if (++pos >= rb->size) pos = 0;
+ sb += 6;
+ }
+ }
+ }
+ rb->complete_count += length;
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/***** the basic scanning chunks for sane_read() *****/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* Request bytes from scanner (and put in scsi_buffer) */
+/********************************************************************/
+static SANE_Status
+read_from_scanner (Microtek_Scanner *s, int *nlines)
+{
+ SANE_Status status;
+ SANE_Int busy, linewidth, remaining;
+ size_t buffsize;
+
+ DBG(23, "read_from_scanner...\n");
+ if (s->unscanned_lines > 0) {
+ status = get_scan_status(s, &busy, &linewidth, &remaining);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(18, "read_from_scanner: bad get_scan_status!\n");
+ return status;
+ }
+ DBG(18, "read_from_scanner: gss busy, linewidth, remaining: %d, %d, %d\n",
+ busy, linewidth, remaining);
+ } else {
+ DBG(18, "read_from_scanner: no gss/no unscanned\n");
+ remaining = 0;
+ }
+
+ *nlines = MIN(remaining, s->max_scsi_lines);
+ DBG(18, "sane_read: max_scsi: %d, rem: %d, nlines: %d\n",
+ s->max_scsi_lines, remaining, *nlines);
+
+ /* grab them bytes! (only if the scanner still has bytes to give...) */
+ if (*nlines > 0) {
+ buffsize = *nlines * (s->pixel_bpl + s->header_bpl);/* == "* linewidth" */
+ status = read_scan_data(s, *nlines, s->scsi_buffer, &buffsize);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(18, "sane_read: bad read_scan_data!\n");
+ return status;
+ }
+ s->unscanned_lines -= *nlines;
+ DBG(18, "sane_read: buffsize: %lu, unscanned: %d\n",
+ (u_long) buffsize, s->unscanned_lines);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Process scanner bytes, and shove in ring_buffer */
+/********************************************************************/
+static SANE_Status
+pack_into_ring(Microtek_Scanner *s, int nlines)
+{
+ SANE_Status status;
+
+ DBG(23, "pack_into_ring...\n");
+ switch (s->line_format) {
+ case MS_LNFMT_FLAT:
+ status = pack_flat_data(s, nlines); break;
+ case MS_LNFMT_SEQ_RGB:
+ status = pack_seqrgb_data(s, nlines); break;
+ case MS_LNFMT_GOOFY_RGB:
+ status = pack_goofyrgb_data(s, nlines); break;
+ case MS_LNFMT_SEQ_2R2G2B:
+ status = pack_seq2r2g2b_data(s, nlines); break;
+ default:
+ status = SANE_STATUS_JAMMED;
+ }
+ return status;
+}
+
+
+
+/********************************************************************/
+/* Pack processed image bytes into frontend destination buffer */
+/********************************************************************/
+static SANE_Int
+pack_into_dest(SANE_Byte *dest_buffer, size_t dest_length, ring_buffer *rb)
+{
+ size_t ret_length = MIN(rb->complete_count, dest_length);
+
+ DBG(23, "pack_into_dest...\n");
+ DBG(23, "pack_into_dest: rl: %lu sz: %lu hc: %lu\n",
+ (u_long)ret_length, (u_long)rb->size, (u_long)rb->head_complete);
+ /* adjust for rollover!!! */
+ if ((rb->head_complete + ret_length) < rb->size) {
+ memcpy(dest_buffer, rb->base + rb->head_complete, ret_length);
+ rb->head_complete += ret_length;
+ } else {
+ size_t chunk1 = rb->size - rb->head_complete;
+ size_t chunk2 = ret_length - chunk1;
+ memcpy(dest_buffer, rb->base + rb->head_complete, chunk1);
+ memcpy(dest_buffer + chunk1, rb->base, chunk2);
+ rb->head_complete = chunk2;
+ }
+ rb->complete_count -= ret_length;
+ return ret_length;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/****** "Registered" SANE API Functions *****************************/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* sane_init() */
+/********************************************************************/
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize;
+ DBG_INIT();
+ DBG(1, "sane_init: MICROTEK says hello! (v%d.%d.%d)\n",
+ MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH);
+ /* return the SANE version we got compiled under */
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ /* parse config file */
+ fp = sanei_config_open (MICROTEK_CONFIG_FILE);
+ if (!fp) {
+ /* default to /dev/scanner instead of insisting on config file */
+ DBG(1, "sane_init: missing config file '%s'\n", MICROTEK_CONFIG_FILE);
+ attach_scanner("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+ while (sanei_config_read(dev_name, sizeof (dev_name), fp)) {
+ DBG(23, "sane_init: config-> %s\n", dev_name);
+ if (dev_name[0] == '#') continue; /* ignore comments */
+ if (!(strncmp("noprecal", dev_name, 8))) {
+ DBG(23,
+ "sane_init: Clever Precalibration will be forcibly disabled...\n");
+ inhibit_clever_precal = SANE_TRUE;
+ continue;
+ }
+ if (!(strncmp("norealcal", dev_name, 9))) {
+ DBG(23,
+ "sane_init: Real calibration will be forcibly disabled...\n");
+ inhibit_real_calib = SANE_TRUE;
+ continue;
+ }
+ len = strlen (dev_name);
+ if (!len) continue; /* ignore empty lines */
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_get_devices */
+/********************************************************************/
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list,
+ SANE_Bool local_only)
+{
+ Microtek_Device *dev;
+ int i;
+
+ local_only = local_only;
+ DBG(10, "sane_get_devices\n");
+ /* we keep an internal copy */
+ if (devlist)
+ free(devlist); /* hmm, free it if we want a new one, I guess. YYYYY*/
+
+ devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
+ if (!devlist) return SANE_STATUS_NO_MEM;
+
+ for (i=0, dev=first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_open */
+/********************************************************************/
+SANE_Status
+sane_open(SANE_String_Const devicename,
+ SANE_Handle *handle)
+{
+ Microtek_Scanner *scanner;
+ Microtek_Device *dev;
+ SANE_Status status;
+
+ DBG(10, "sane_open\n");
+ /* find device... */
+ DBG(23, "sane_open: find device...\n");
+ if (devicename[0]) {
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, devicename) == 0)
+ break;
+ }
+ if (!dev) { /* not in list, try manually... */
+ status = attach_scanner(devicename, &dev);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ } else { /* no device specified, so use first */
+ dev = first_dev;
+ }
+ if (!dev) return SANE_STATUS_INVAL;
+
+ /* create a scanner... */
+ DBG(23, "sane_open: create scanner...\n");
+ scanner = malloc(sizeof(*scanner));
+ if (!scanner) return SANE_STATUS_NO_MEM;
+ memset(scanner, 0, sizeof(*scanner));
+
+ /* initialize scanner dependent stuff */
+ DBG(23, "sane_open: initialize scanner dependent stuff...\n");
+ /* ZZZZZZZZZZZZZZ */
+ scanner->unit_type =
+ (dev->info.unit_type & MI_UNIT_PIXELS) ? MS_UNIT_PIXELS : MS_UNIT_18INCH;
+ scanner->res_type =
+ (dev->info.res_step & MI_RESSTEP_1PER) ? MS_RES_1PER : MS_RES_5PER;
+ scanner->midtone_support =
+ (dev->info.enhance_cap & MI_ENH_CAP_MIDTONE) ? SANE_TRUE : SANE_FALSE;
+ scanner->paper_length =
+ (scanner->unit_type == MS_UNIT_PIXELS) ?
+ dev->info.max_y :
+ (SANE_Int)((double)dev->info.max_y * 8.0 /
+ (double)dev->info.base_resolution);
+ /*
+ (SANE_Int)(SANE_UNFIX(dev->info.max_y) * dev->info.base_resolution) :
+ (SANE_Int)(SANE_UNFIX(dev->info.max_y) * 8);
+ ZZZZZZZ */
+ scanner->bright_r = 0;
+ scanner->bright_g = 0;
+ scanner->bright_b = 0;
+
+ /* calibration shenanigans */
+ if ((dev->info.extra_cap & MI_EXCAP_DIS_RECAL) &&
+ (!(inhibit_real_calib))) {
+ DBG(23, "sane_open: Real calibration enabled.\n");
+ scanner->allow_calibrate = SANE_FALSE;
+ scanner->do_real_calib = SANE_TRUE;
+ scanner->do_clever_precal = SANE_FALSE;
+ } else if ((dev->info.extra_cap & MI_EXCAP_DIS_RECAL) &&
+ (!(inhibit_clever_precal))) {
+ DBG(23, "sane_open: Clever precalibration enabled.\n");
+ scanner->allow_calibrate = SANE_FALSE;
+ scanner->do_real_calib = SANE_FALSE;
+ scanner->do_clever_precal = SANE_TRUE;
+ } else {
+ DBG(23, "sane_open: All calibration routines disabled.\n");
+ scanner->allow_calibrate = SANE_TRUE;
+ scanner->do_real_calib = SANE_FALSE;
+ scanner->do_clever_precal = SANE_FALSE;
+ }
+
+ scanner->onepass = (dev->info.modes & MI_MODES_ONEPASS);
+ scanner->allowbacktrack = SANE_TRUE; /* ??? XXXXXXX */
+ scanner->reversecolors = SANE_FALSE;
+ scanner->fastprescan = SANE_FALSE;
+ scanner->bits_per_color = 8;
+
+ /* init gamma tables */
+ if (dev->info.max_lookup_size) {
+ int j, v, max_entry;
+ DBG(23, "sane_open: init gamma tables...\n");
+ scanner->gamma_entries = dev->info.max_lookup_size;
+ scanner->gamma_entry_size = dev->info.gamma_size;
+ scanner->gamma_bit_depth = dev->info.max_gamma_bit_depth;
+ max_entry = (1 << scanner->gamma_bit_depth) - 1;
+ scanner->gamma_entry_range.min = 0;
+ scanner->gamma_entry_range.max = max_entry;
+ scanner->gamma_entry_range.quant = 1;
+
+ scanner->gray_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->gray_lut[0]));
+ scanner->red_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->red_lut[0]));
+ scanner->green_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->green_lut[0]));
+ scanner->blue_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->blue_lut[0]));
+ if ((scanner->gray_lut == NULL) ||
+ (scanner->red_lut == NULL) ||
+ (scanner->green_lut == NULL) ||
+ (scanner->blue_lut == NULL)) {
+ DBG(23, "sane_open: unable to allocate space for %d-entry LUT's;\n",
+ scanner->gamma_entries);
+ DBG(23, " so, gamma tables now DISABLED.\n");
+ free(scanner->gray_lut);
+ free(scanner->red_lut);
+ free(scanner->green_lut);
+ free(scanner->blue_lut);
+ }
+ for (j=0; j<scanner->gamma_entries; j += scanner->gamma_entry_size) {
+ v = (SANE_Int)
+ ((double) j * (double) max_entry /
+ ((double) scanner->gamma_entries - 1.0) + 0.5);
+ scanner->gray_lut[j] = v;
+ scanner->red_lut[j] = v;
+ scanner->green_lut[j] = v;
+ scanner->blue_lut[j] = v;
+ }
+ } else {
+ DBG(23, "sane_open: NO gamma tables. (max size = %lu)\n",
+ (u_long)dev->info.max_lookup_size);
+ scanner->gamma_entries = 0;
+ scanner->gray_lut = NULL;
+ scanner->red_lut = NULL;
+ scanner->green_lut = NULL;
+ scanner->blue_lut = NULL;
+ }
+
+ DBG(23, "sane_open: init pass-time variables...\n");
+ scanner->scanning = SANE_FALSE;
+ scanner->this_pass = 0;
+ scanner->sfd = -1;
+ scanner->dev = dev;
+ scanner->sense_flags = 0;
+ scanner->scan_started = SANE_FALSE;
+ scanner->woe = SANE_FALSE;
+ scanner->cancel = SANE_FALSE;
+
+ DBG(23, "sane_open: init clever cache...\n");
+ /* clear out that clever cache, so it doesn't match anything */
+ {
+ int j;
+ for (j=0; j<10; j++)
+ scanner->mode_sense_cache[j] = 0;
+ scanner->precal_record = MS_PRECAL_NONE;
+ }
+
+ DBG(23, "sane_open: initialize options: \n");
+ if ((status = init_options(scanner)) != SANE_STATUS_GOOD) return status;
+
+ scanner->next = first_handle;
+ first_handle = scanner;
+ *handle = scanner;
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_close */
+/********************************************************************/
+void
+sane_close (SANE_Handle handle)
+{
+ Microtek_Scanner *ms = handle;
+
+ DBG(10, "sane_close...\n");
+ /* free malloc'ed stuff (strdup counts too!) */
+ free((void *) ms->sod[OPT_MODE].constraint.string_list);
+ free((void *) ms->sod[OPT_SOURCE].constraint.string_list);
+ free(ms->val[OPT_MODE].s);
+ free(ms->val[OPT_HALFTONE_PATTERN].s);
+ free(ms->val[OPT_SOURCE].s);
+ free(ms->val[OPT_CUSTOM_GAMMA].s);
+ free(ms->gray_lut);
+ free(ms->red_lut);
+ free(ms->green_lut);
+ free(ms->blue_lut);
+ /* remove Scanner from linked list */
+ if (first_handle == ms)
+ first_handle = ms->next;
+ else {
+ Microtek_Scanner *ts = first_handle;
+ while ((ts != NULL) && (ts->next != ms)) ts = ts->next;
+ ts->next = ts->next->next; /* == ms->next */
+ }
+ /* finally, say goodbye to the Scanner */
+ free(ms);
+}
+
+
+
+/********************************************************************/
+/* sane_get_option_descriptor */
+/********************************************************************/
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option)
+{
+ Microtek_Scanner *scanner = handle;
+
+ DBG(96, "sane_get_option_descriptor (%d)...\n", option);
+ if ((unsigned)option >= NUM_OPTIONS) return NULL;
+ return &(scanner->sod[option]);
+}
+
+
+
+/********************************************************************/
+/* sane_control_option */
+/********************************************************************/
+SANE_Status
+sane_control_option (SANE_Handle handle,
+ SANE_Int option,
+ SANE_Action action,
+ void *value,
+ SANE_Int *info)
+{
+ Microtek_Scanner *scanner = handle;
+ SANE_Option_Descriptor *sod;
+ Option_Value *val;
+ SANE_Status status;
+
+ DBG(96, "sane_control_option (opt=%d,act=%d,val=%p,info=%p)\n",
+ option, action, value, (void*) info);
+
+ sod = scanner->sod;
+ val = scanner->val;
+
+ /* no changes while in mid-pass! */
+ if (scanner->scanning) return SANE_STATUS_DEVICE_BUSY;
+ /* and... no changes while in middle of three-pass series! */
+ if (scanner->this_pass != 0) return SANE_STATUS_DEVICE_BUSY;
+
+ if ( ((option >= NUM_OPTIONS) || (option < 0)) ||
+ (!SANE_OPTION_IS_ACTIVE(scanner->sod[option].cap)) )
+ return SANE_STATUS_INVAL;
+
+ if (info) *info = 0;
+
+ /* choose by action */
+ switch (action) {
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option) {
+ /* word options... */
+ case OPT_RESOLUTION:
+ case OPT_SPEED:
+ case OPT_BACKTRACK:
+ case OPT_NEGATIVE:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_EXPOSURE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_HIGHLIGHT:
+ case OPT_SHADOW:
+ case OPT_MIDTONE:
+ case OPT_GAMMA_BIND:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ case OPT_EXP_RES:
+ case OPT_CALIB_ONCE:
+ *(SANE_Word *)value = val[option].w;
+ return SANE_STATUS_GOOD;
+ /* word-array options... */
+ /* case OPT_HALFTONE_PATTERN:*/
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy(value, val[option].wa, sod[option].size);
+ return SANE_STATUS_GOOD;
+ /* string options... */
+ case OPT_MODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_SOURCE:
+ strcpy(value, val[option].s);
+ return SANE_STATUS_GOOD;
+ /* others.... */
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) value = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE: {
+ status = sanei_constrain_value(sod + option, value, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option) {
+ /* set word options... */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_RESOLUTION:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_SPEED:
+ case OPT_PREVIEW:
+ case OPT_BACKTRACK:
+ case OPT_NEGATIVE:
+ case OPT_EXPOSURE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ val[option].w = *(SANE_Word *)value;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HIGHLIGHT:
+ case OPT_SHADOW:
+ case OPT_MIDTONE:
+ val[option].w = *(SANE_Word *)value;
+ /* we need to (silently) make sure shadow <= midtone <= highlight */
+ if (scanner->midtone_support) {
+ if (val[OPT_SHADOW].w > val[OPT_MIDTONE].w) {
+ if (option == OPT_SHADOW)
+ val[OPT_SHADOW].w = val[OPT_MIDTONE].w;
+ else
+ val[OPT_MIDTONE].w = val[OPT_SHADOW].w;
+ }
+ if (val[OPT_HIGHLIGHT].w < val[OPT_MIDTONE].w) {
+ if (option == OPT_HIGHLIGHT)
+ val[OPT_HIGHLIGHT].w = val[OPT_MIDTONE].w;
+ else
+ val[OPT_MIDTONE].w = val[OPT_HIGHLIGHT].w;
+ }
+ } else {
+ if (val[OPT_SHADOW].w > val[OPT_HIGHLIGHT].w) {
+ if (option == OPT_SHADOW)
+ val[OPT_SHADOW].w = val[OPT_HIGHLIGHT].w;
+ else
+ val[OPT_HIGHLIGHT].w = val[OPT_SHADOW].w;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_EXP_RES:
+ if (val[option].w != *(SANE_Word *) value) {
+ val[option].w = *(SANE_Word *)value;
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (val[OPT_EXP_RES].w) {
+ sod[OPT_RESOLUTION].constraint.range = &(scanner->exp_res_range);
+ val[OPT_RESOLUTION].w *= 2;
+ } else {
+ sod[OPT_RESOLUTION].constraint.range = &(scanner->res_range);
+ val[OPT_RESOLUTION].w /= 2;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_CALIB_ONCE:
+ val[option].w = *(SANE_Word *)value;
+ /* toggling off and on should force a recalibration... */
+ if (!(val[option].w)) scanner->precal_record = MS_PRECAL_NONE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_BIND:
+ case OPT_CUSTOM_GAMMA:
+ if (option == OPT_GAMMA_BIND) {
+ if (val[option].w != *(SANE_Word *) value)
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+ val[option].w = *(SANE_Word *) value;
+ } else if (option == OPT_CUSTOM_GAMMA) {
+ if (val[option].s) {
+ if (strcmp(value, val[option].s))
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+ free(val[option].s);
+ }
+ val[option].s = strdup(value);
+ }
+ if ( !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)) ||
+ !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_SCALAR)) ) {
+ sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if ( !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)) ||
+ !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_TABLE)) ) {
+ sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_SCALAR))) {
+ if (val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ sod[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ } else {
+ sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_TABLE))) {
+ if (val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ sod[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ } else {
+ sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)))
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ else if (!(strcmp(val[OPT_MODE].s, M_COLOR)))
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_MODE:
+ if (val[option].s) {
+ if (strcmp(val[option].s, value))
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ free(val[option].s);
+ }
+ val[option].s = strdup(value);
+ if (strcmp(val[option].s, M_HALFTONE)) {
+ sod[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ } else {
+ sod[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (strcmp(val[option].s, M_COLOR)) { /* not color */
+ /*val[OPT_GAMMA_BIND].w = SANE_TRUE;*/
+ DBG(23, "FLIP ma LID! bind is %d\n", val[OPT_GAMMA_BIND].w);
+ {
+ SANE_Bool Trueness = SANE_TRUE;
+ SANE_Status status;
+ status = sane_control_option(handle,
+ OPT_GAMMA_BIND,
+ SANE_ACTION_SET_VALUE,
+ &Trueness,
+ NULL);
+ DBG(23, "stat is: %d\n", status);
+ }
+ DBG(23, "LID be FLIPPED! bind is %d\n", val[OPT_GAMMA_BIND].w);
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ /* sod[OPT_FORCE_3PASS].cap |= SANE_CAP_INACTIVE;*/
+ } else {
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ /* if (scanner->dev->info.modes & MI_MODES_ONEPASS)
+ sod[OPT_FORCE_3PASS].cap &= ~SANE_CAP_INACTIVE;*/
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_HALFTONE_PATTERN:
+ case OPT_SOURCE:
+ if (val[option].s) free(val[option].s);
+ val[option].s = strdup(value);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy(val[option].wa, value, sod[option].size);
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_UNSUPPORTED; /* We are DUMB. */
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_get_parameters */
+/********************************************************************/
+SANE_Status
+sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters *params)
+{
+ Microtek_Scanner *s = handle;
+
+ DBG(23, "sane_get_parameters...\n");
+
+ if (!s->scanning) {
+ /* decipher scan mode */
+ if (!(strcmp(s->val[OPT_MODE].s, M_LINEART)))
+ s->mode = MS_MODE_LINEART;
+ else if (!(strcmp(s->val[OPT_MODE].s, M_HALFTONE)))
+ s->mode = MS_MODE_HALFTONE;
+ else if (!(strcmp(s->val[OPT_MODE].s, M_GRAY)))
+ s->mode = MS_MODE_GRAY;
+ else if (!(strcmp(s->val[OPT_MODE].s, M_COLOR)))
+ s->mode = MS_MODE_COLOR;
+
+ if (s->mode == MS_MODE_COLOR) {
+ if (s->onepass) {
+ /* regular one-pass */
+ DBG(23, "sane_get_parameters: regular 1-pass color\n");
+ s->threepasscolor = SANE_FALSE;
+ s->onepasscolor = SANE_TRUE;
+ s->color_seq = s->dev->info.color_sequence;
+ } else { /* 3-pass scanner */
+ DBG(23, "sane_get_parameters: regular 3-pass color\n");
+ s->threepasscolor = SANE_TRUE;
+ s->onepasscolor = SANE_FALSE;
+ s->color_seq = s->dev->info.color_sequence;
+ }
+ } else { /* not color! */
+ DBG(23, "sane_get_parameters: non-color\n");
+ s->threepasscolor = SANE_FALSE;
+ s->onepasscolor = SANE_FALSE;
+ s->color_seq = s->dev->info.color_sequence;
+ }
+
+ s->transparency = !(strcmp(s->val[OPT_SOURCE].s, M_TRANS));
+ s->useADF = !(strcmp(s->val[OPT_SOURCE].s, M_AUTOFEED));
+ /* disallow exp. res. during preview scan XXXXXXXXXXX */
+ /*s->expandedresolution =
+ (s->val[OPT_EXP_RES].w) && !(s->val[OPT_PREVIEW].w);*/
+ s->expandedresolution = (s->val[OPT_EXP_RES].w);
+ s->doexpansion = (s->expandedresolution && !(s->dev->info.does_expansion));
+
+ if (s->res_type == MS_RES_1PER) {
+ s->resolution = (SANE_Int)(SANE_UNFIX(s->val[OPT_RESOLUTION].w));
+ s->resolution_code =
+ 0xFF & ((s->resolution * 100) /
+ s->dev->info.base_resolution /
+ (s->expandedresolution ? 2 : 1));
+ DBG(23, "sane_get_parameters: res_code = %d (%2x)\n",
+ s->resolution_code, s->resolution_code);
+ } else {
+ DBG(23, "sane_get_parameters: 5 percent!!!\n");
+ /* XXXXXXXXXXXXX */
+ }
+
+ s->calib_once = s->val[OPT_CALIB_ONCE].w;
+
+ s->reversecolors = s->val[OPT_NEGATIVE].w;
+ s->prescan = s->val[OPT_PREVIEW].w;
+ s->exposure = (s->val[OPT_EXPOSURE].w / 3) + 7;
+ s->contrast = (s->val[OPT_CONTRAST].w / 7) + 7;
+ s->velocity = s->val[OPT_SPEED].w;
+ s->shadow = s->val[OPT_SHADOW].w;
+ s->highlight = s->val[OPT_HIGHLIGHT].w;
+ s->midtone = s->val[OPT_MIDTONE].w;
+ if (SANE_OPTION_IS_ACTIVE(s->sod[OPT_BRIGHTNESS].cap)) {
+#if 1 /* this is _not_ what the docs specify! */
+ if (s->val[OPT_BRIGHTNESS].w >= 0)
+ s->bright_r = (SANE_Byte) (s->val[OPT_BRIGHTNESS].w);
+ else
+ s->bright_r = (SANE_Byte) (0x80 | (- s->val[OPT_BRIGHTNESS].w));
+#else
+ s->bright_r = (SANE_Byte) (s->val[OPT_BRIGHTNESS].w);
+#endif
+ s->bright_g = s->bright_b = s->bright_r;
+ DBG(23, "bright_r of %d set to 0x%0x\n",
+ s->val[OPT_BRIGHTNESS].w, s->bright_r);
+ } else {
+ s->bright_r = s->bright_g = s->bright_b = 0;
+ }
+ /* figure out halftone pattern selection... */
+ if (s->mode == MS_MODE_HALFTONE) {
+ int i = 0;
+ while ((halftone_mode_list[i] != NULL) &&
+ (strcmp(halftone_mode_list[i], s->val[OPT_HALFTONE_PATTERN].s)))
+ i++;
+ s->pattern = ((i < s->dev->info.pattern_count) ? i : 0);
+ } else
+ s->pattern = 0;
+
+
+
+ {
+ /* need to 'round' things properly! XXXXXXXX */
+ SANE_Int widthpix;
+ double dots_per_mm = s->resolution / MM_PER_INCH;
+ double units_per_mm =
+ (s->unit_type == MS_UNIT_18INCH) ?
+ (8.0 / MM_PER_INCH) : /* 1/8 inches */
+ (s->dev->info.base_resolution / MM_PER_INCH); /* pixels */
+
+ DBG(23, "sane_get_parameters: dots_per_mm: %f\n", dots_per_mm);
+ DBG(23, "sane_get_parameters: units_per_mm: %f\n", units_per_mm);
+
+ /* calculate frame coordinates...
+ * scanner coords are in 'units' -- pixels or 1/8"
+ * option coords are MM
+ */
+ s->x1 = (SANE_Int)(SANE_UNFIX(s->val[OPT_TL_X].w) * units_per_mm + 0.5);
+ s->y1 = (SANE_Int)(SANE_UNFIX(s->val[OPT_TL_Y].w) * units_per_mm + 0.5);
+ s->x2 = (SANE_Int)(SANE_UNFIX(s->val[OPT_BR_X].w) * units_per_mm + 0.5);
+ s->y2 = (SANE_Int)(SANE_UNFIX(s->val[OPT_BR_Y].w) * units_per_mm + 0.5);
+ /* bug out if length or width is <= zero... */
+ if ((s->x1 >= s->x2) || (s->y1 >= s->y2))
+ return SANE_STATUS_INVAL;
+
+ /* these are just an estimate... (but *should* be completely accurate)
+ * real values come from scanner after sane_start.
+ */
+ if (s->unit_type == MS_UNIT_18INCH) {
+ /* who *knows* what happens */
+ widthpix =
+ (SANE_Int)((double)(s->x2 - s->x1 + 1) / 8.0 *
+ (double)s->resolution);
+ s->params.lines =
+ (SANE_Int)((double)(s->y2 - s->y1 + 1) / 8.0 *
+ (double)s->resolution);
+ } else {
+ /* calculate pixels per scanline returned by scanner... */
+ /* scanner (E6 at least) always seems to return
+ an -even- number of -bytes- */
+ if (s->resolution <= s->dev->info.base_resolution)
+ widthpix =
+ (SANE_Int)((double)(s->x2 - s->x1 + 1) *
+ (double)(s->resolution) /
+ (double)(s->dev->info.base_resolution));
+ else
+ widthpix = (s->x2 - s->x1 + 1);
+ if ((s->mode == MS_MODE_LINEART) ||
+ (s->mode == MS_MODE_HALFTONE)) {
+ DBG(23, "WIDTHPIX: before: %d", widthpix);
+ widthpix = ((widthpix / 8) & ~0x1) * 8;
+ DBG(23, "after: %d", widthpix);
+ } else {
+ widthpix = widthpix & ~0x1;
+ }
+ DBG(23, "WIDTHPIX: before exp: %d\n", widthpix);
+ /* ok, now fix up expanded-mode conversions */
+ if (s->resolution > s->dev->info.base_resolution)
+ widthpix = (SANE_Int) ((double)widthpix *
+ (double)s->resolution /
+ (double)s->dev->info.base_resolution);
+ s->params.pixels_per_line = widthpix;
+ s->params.lines =
+ (SANE_Int)((double)(s->y2 - s->y1 + 1) *
+ (double)(s->resolution) /
+ (double)(s->dev->info.base_resolution));
+ }
+ }
+
+ switch (s->mode) {
+ case MS_MODE_LINEART:
+ case MS_MODE_HALFTONE:
+ s->multibit = SANE_FALSE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->filter = MS_FILT_CLEAR;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ break;
+ case MS_MODE_GRAY:
+ s->multibit = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = s->bits_per_color;
+ s->filter = MS_FILT_CLEAR;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ break;
+ case MS_MODE_COLOR:
+ s->multibit = SANE_TRUE;
+ if (s->onepasscolor) { /* a single-pass color scan */
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = s->bits_per_color;
+ s->filter = MS_FILT_CLEAR;
+ s->params.bytes_per_line = s->params.pixels_per_line * 3;
+ } else { /* a three-pass color scan */
+ s->params.depth = s->bits_per_color;
+ /* this will be correctly set in sane_start */
+ s->params.format = SANE_FRAME_RED;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ }
+ break;
+ }
+
+ DBG(23, "sane_get_parameters: lines: %d ppl: %d bpl: %d\n",
+ s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line);
+
+ /* also fixed in sane_start for multi-pass scans */
+ s->params.last_frame = SANE_TRUE; /* ?? XXXXXXXX */
+ }
+
+ if (params)
+ *params = s->params;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_start */
+/********************************************************************/
+static SANE_Status
+sane_start_guts (SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Int busy, linewidth;
+
+ DBG(10, "sane_start...\n");
+
+ if (s->sfd != -1) {
+ DBG(23, "sane_start: sfd already set!\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((status = sane_get_parameters(s, 0)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ set_pass_parameters(s);
+
+ s->scanning = SANE_TRUE;
+ s->cancel = SANE_FALSE;
+
+ status = sanei_scsi_open(s->dev->sane.name,
+ &(s->sfd),
+ sense_handler,
+ &(s->sense_flags));
+ if (status != SANE_STATUS_GOOD) {
+ DBG(10, "sane_start: open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ s->sfd = -1;
+ return end_scan(s, status);
+ }
+
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+
+ if ((status = finagle_precal(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+
+ if ((status = scanning_frame(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ if (s->dev->info.source_options &
+ (MI_SRC_FEED_BT | MI_SRC_HAS_TRANS |
+ MI_SRC_FEED_SUPP | MI_SRC_HAS_FEED)) { /* ZZZZZZZZZZZ */
+ if ((status = accessory(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ /* if SWslct ???? XXXXXXXXXXXXXXX */
+ }
+ if ((status = download_gamma(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ if ((status = mode_select(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ if (s->dev->info.does_mode1) {
+ if ((status = mode_select_1(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ }
+ if ((s->do_clever_precal) || (s->do_real_calib)) {
+ if ((status = save_mode_sense(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ }
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ s->scan_started = SANE_TRUE;
+ if ((status = start_scan(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ if ((status = get_scan_status(s, &busy,
+ &linewidth, &(s->unscanned_lines))) !=
+ SANE_STATUS_GOOD) {
+ DBG(10, "sane_start: get_scan_status fails\n");
+ return end_scan(s, status);
+ }
+ /* check for a bizarre linecount */
+ if ((s->unscanned_lines < 0) ||
+ (s->unscanned_lines >
+ (s->params.lines * 2 * (s->expandedresolution ? 2 : 1)))) {
+ DBG(10, "sane_start: get_scan_status returns weird line count %d\n",
+ s->unscanned_lines);
+ return end_scan(s, SANE_STATUS_DEVICE_BUSY);
+ }
+
+
+ /* figure out image format parameters */
+ switch (s->mode) {
+ case MS_MODE_LINEART:
+ case MS_MODE_HALFTONE:
+ s->pixel_bpl = linewidth;
+ s->header_bpl = 0;
+ s->ppl = linewidth * 8;
+ s->planes = 1;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MS_MODE_GRAY:
+ if (s->bits_per_color < 8) {
+ s->pixel_bpl = linewidth;
+ s->ppl = linewidth * (8 / s->bits_per_color);
+ } else {
+ s->pixel_bpl = linewidth * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ }
+ s->header_bpl = 0;
+ s->planes = 1;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MS_MODE_COLOR:
+ switch (s->color_seq) {
+ case MI_COLSEQ_PLANE:
+ s->pixel_bpl = linewidth * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 1;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MI_COLSEQ_NONRGB:
+ s->pixel_bpl = (linewidth - 2) * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth - 2;
+ s->header_bpl = 2 * 3;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_GOOFY_RGB;
+ break;
+ case MI_COLSEQ_PIXEL:
+ s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MI_COLSEQ_2PIXEL:
+ s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_SEQ_2R2G2B;
+ break;
+ case MI_COLSEQ_RGB:
+ s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_SEQ_RGB;
+ break;
+ default:
+ DBG(10, "sane_start: Unknown color_sequence: %d\n",
+ s->dev->info.color_sequence);
+ return end_scan(s, SANE_STATUS_INVAL);
+ }
+ break;
+ default:
+ DBG(10, "sane_start: Unknown scan mode: %d\n", s->mode);
+ return end_scan(s, SANE_STATUS_INVAL);
+ }
+
+ if ((s->doexpansion) &&
+ (s->resolution > s->dev->info.base_resolution)) {
+ s->dest_ppl = (int) ((double)s->ppl *
+ (double)s->resolution /
+ (double)s->dev->info.base_resolution);
+ /*+ 0.5 XXXXXX */
+ s->exp_aspect = (double)s->ppl / (double)s->dest_ppl;
+ s->dest_pixel_bpl = (int) ceil((double)s->pixel_bpl / s->exp_aspect);
+ /*s->exp_aspect =
+ (double) s->dev->info.base_resolution / (double) s->resolution;*/
+ /* s->dest_pixel_bpl = s->pixel_bpl / s->exp_aspect;
+ s->dest_ppl = s->ppl / s->exp_aspect;*/
+ /*s->dest_ppl = s->ppl / s->exp_aspect;
+ s->dest_pixel_bpl = (int) ceil((double)s->dest_ppl *
+ (double)s->pixel_bpl /
+ (double)s->ppl);*/
+ } else {
+ s->exp_aspect = 1.0;
+ s->dest_pixel_bpl = s->pixel_bpl;
+ s->dest_ppl = s->ppl;
+ }
+
+ s->params.lines = s->unscanned_lines;
+ s->params.pixels_per_line = s->dest_ppl;
+ s->params.bytes_per_line = s->dest_pixel_bpl;
+
+ /* calculate maximum line capacity of SCSI buffer */
+ s->max_scsi_lines = SCSI_BUFF_SIZE / (s->pixel_bpl + s->header_bpl);
+ if (s->max_scsi_lines < 1) {
+ DBG(10, "sane_start: SCSI buffer smaller that one scan line!\n");
+ return end_scan(s, SANE_STATUS_NO_MEM);
+ }
+
+ s->scsi_buffer = (uint8_t *) malloc(SCSI_BUFF_SIZE * sizeof(uint8_t));
+ if (s->scsi_buffer == NULL) return SANE_STATUS_NO_MEM;
+
+ /* what's a good initial size for this? */
+ s->rb = ring_alloc(s->max_scsi_lines * s->dest_pixel_bpl,
+ s->dest_pixel_bpl, s->dest_ppl);
+
+ s->undelivered_bytes = s->unscanned_lines * s->dest_pixel_bpl;
+
+ DBG(23, "Scan Param:\n");
+ DBG(23, "pix bpl: %d hdr bpl: %d ppl: %d\n",
+ s->pixel_bpl, s->header_bpl, s->ppl);
+ DBG(23, "undel bytes: %d unscan lines: %d planes: %d\n",
+ s->undelivered_bytes, s->unscanned_lines, s->planes);
+ DBG(23, "dest bpl: %d dest ppl: %d aspect: %f\n",
+ s->dest_pixel_bpl, s->dest_ppl, s->exp_aspect);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+
+ s->woe = SANE_TRUE;
+ status = sane_start_guts(handle);
+ s->woe = SANE_FALSE;
+ return status;
+}
+
+
+
+/********************************************************************/
+/* sane_read */
+/********************************************************************/
+static SANE_Status
+sane_read_guts (SANE_Handle handle, SANE_Byte *dest_buffer,
+ SANE_Int dest_length, SANE_Int *ret_length)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+ int nlines;
+ ring_buffer *rb = s->rb;
+
+ DBG(10, "sane_read...\n");
+
+ *ret_length = 0; /* default: no data */
+ /* we have been cancelled... */
+ if (s->cancel) return end_scan(s, SANE_STATUS_CANCELLED);
+ /* we're not really scanning!... */
+ if (!(s->scanning)) return SANE_STATUS_INVAL;
+ /* we are done scanning... */
+ if (s->undelivered_bytes <= 0) return end_scan(s, SANE_STATUS_EOF);
+
+ /* get more bytes if our ring is empty... */
+ while (rb->complete_count == 0) {
+ if ((status = read_from_scanner(s, &nlines)) != SANE_STATUS_GOOD) {
+ DBG(18, "sane_read: read_from_scanner failed.\n");
+ return end_scan(s, status);
+ }
+ if ((status = pack_into_ring(s, nlines)) != SANE_STATUS_GOOD) {
+ DBG(18, "sane_read: pack_into_ring failed.\n");
+ return end_scan(s, status);
+ }
+ }
+ /* return some data to caller */
+ *ret_length = pack_into_dest(dest_buffer, dest_length, rb);
+ s->undelivered_bytes -= *ret_length;
+
+ if (s->cancel) return end_scan(s, SANE_STATUS_CANCELLED);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *dest_buffer,
+ SANE_Int dest_length, SANE_Int *ret_length)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+
+ s->woe = SANE_TRUE;
+ status = sane_read_guts(handle, dest_buffer, dest_length, ret_length);
+ s->woe = SANE_FALSE;
+ return status;
+}
+
+
+
+/********************************************************************/
+/* sane_exit */
+/********************************************************************/
+void
+sane_exit (void)
+{
+ Microtek_Device *next;
+
+ DBG(10, "sane_exit...\n");
+ /* close all leftover Scanners */
+ /*(beware of how sane_close interacts with linked list) */
+ while (first_handle != NULL)
+ sane_close(first_handle);
+ /* free up device list */
+ while (first_dev != NULL) {
+ next = first_dev->next;
+ free((void *) first_dev->sane.name);
+ free((void *) first_dev->sane.model);
+ free(first_dev);
+ first_dev = next;
+ }
+ /* the devlist allocated by sane_get_devices */
+ free(devlist);
+ DBG(10, "sane_exit: MICROTEK says goodbye.\n");
+}
+
+
+
+/********************************************************************/
+/* sane_cancel */
+/********************************************************************/
+void
+sane_cancel (SANE_Handle handle)
+{
+ Microtek_Scanner *ms = handle;
+ DBG(10, "sane_cancel...\n");
+ ms->cancel = SANE_TRUE;
+ if (!(ms->woe)) end_scan(ms, SANE_STATUS_CANCELLED);
+}
+
+
+
+/********************************************************************/
+/* sane_set_io_mode */
+/********************************************************************/
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG(10, "sane_set_io_mode...\n");
+ handle = handle;
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_get_select_fd */
+/********************************************************************/
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG(10, "sane_get_select_fd...\n");
+ handle = handle, fd = fd;
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/microtek.conf.in b/backend/microtek.conf.in
new file mode 100644
index 0000000..18e721c
--- /dev/null
+++ b/backend/microtek.conf.in
@@ -0,0 +1,7 @@
+# Uncomment following line to disable "real calibration" routines...
+#norealcal
+# Uncomment following line to disable "clever precalibration" routines...
+#noprecal
+# Using "norealcal" will revert backend to pre-0.11.0 calibration code.
+scsi * * Scanner
+/dev/scanner
diff --git a/backend/microtek.h b/backend/microtek.h
new file mode 100644
index 0000000..d4c7abf
--- /dev/null
+++ b/backend/microtek.h
@@ -0,0 +1,379 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ microtek.h
+
+ This file Copyright 2002 Matthew Marjanovic
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for Microtek scanners.
+
+ (feedback to: mtek-bugs@mir.com)
+ (for latest info: http://www.mir.com/mtek/)
+
+ ***************************************************************************/
+
+
+#ifndef microtek_h
+#define microtek_h
+
+#include <sys/types.h>
+
+
+/*******************************************************************/
+/***** enumeration of Option Descriptors *****/
+/*******************************************************************/
+
+enum Mtek_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* -a,b,c,g */
+ OPT_HALFTONE_PATTERN, /* -H */
+ OPT_RESOLUTION, /* -r */
+ OPT_EXP_RES,
+ OPT_NEGATIVE, /* -n */
+ OPT_SPEED, /* -v */
+ OPT_SOURCE, /* -t */
+ OPT_PREVIEW,
+ OPT_CALIB_ONCE,
+
+ OPT_GEOMETRY_GROUP, /* -f .... */
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_EXPOSURE,
+ OPT_BRIGHTNESS, /* -d */
+ OPT_CONTRAST, /* -k */
+ OPT_HIGHLIGHT, /* -l */
+ OPT_SHADOW, /* -s */
+ OPT_MIDTONE, /* -m */
+
+ OPT_GAMMA_GROUP,
+ OPT_CUSTOM_GAMMA,
+ OPT_ANALOG_GAMMA,
+ OPT_ANALOG_GAMMA_R,
+ OPT_ANALOG_GAMMA_G,
+ OPT_ANALOG_GAMMA_B,
+ /* "The gamma vectors MUST appear in the order gray, red, green, blue." */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_GAMMA_BIND,
+
+ NUM_OPTIONS,
+
+ OPT_BACKTRACK, /* -B */
+
+ /* must come last: */
+ RNUM_OPTIONS
+};
+
+
+/*******************************************************************/
+/***** scanner hardware information (as discovered by INQUIRY) *****/
+/*******************************************************************/
+
+typedef struct Microtek_Info {
+ char vendor_id[9];
+ char model_name[17];
+ char revision_num[5];
+ char vendor_string[21];
+ SANE_Byte device_type;
+ SANE_Byte SCSI_firmware_ver_major;
+ SANE_Byte SCSI_firmware_ver_minor;
+ SANE_Byte scanner_firmware_ver_major;
+ SANE_Byte scanner_firmware_ver_minor;
+ SANE_Byte response_data_format;
+#define MI_RESSTEP_1PER 0x01
+#define MI_RESSTEP_5PER 0x02
+ SANE_Byte res_step;
+#define MI_MODES_LINEART 0x01
+#define MI_MODES_HALFTONE 0x02
+#define MI_MODES_GRAY 0x04 /* ??????? or "MultiBit"??? XXXXX*/
+#define MI_MODES_COLOR 0x08
+#define MI_MODES_TRANSMSV 0x20
+#define MI_MODES_ONEPASS 0x40
+#define MI_MODES_NEGATIVE 0x80
+ SANE_Byte modes;
+ SANE_Int pattern_count;
+ SANE_Byte pattern_dwnld;
+#define MI_FEED_FLATBED 0x01
+#define MI_FEED_EDGEFEED 0x02
+#define MI_FEED_AUTOSUPP 0x04
+ SANE_Byte feed_type;
+#define MI_COMPRSS_HUFF 0x10
+#define MI_COMPRSS_RD 0x20
+ SANE_Byte compress_type;
+#define MI_UNIT_8TH_INCH 0x40
+#define MI_UNIT_PIXELS 0x80
+ SANE_Byte unit_type;
+ SANE_Byte doc_size_code;
+ SANE_Int max_x; /* pixels */
+ SANE_Int max_y; /* pixels */
+ SANE_Range doc_x_range; /* mm */
+ SANE_Range doc_y_range; /* mm */
+ SANE_Int cont_settings;
+ SANE_Int exp_settings;
+ SANE_Byte model_code;
+ SANE_Int base_resolution; /* dpi, guessed by backend, per model code */
+#define MI_SRC_FEED_SUPP 0x01 /* support for feeder */
+#define MI_SRC_FEED_BT 0x02 /* support for feed backtracking control */
+#define MI_SRC_HAS_FEED 0x04 /* feeder installed */
+#define MI_SRC_FEED_RDY 0x08 /* feeder ready */
+#define MI_SRC_GET_FEED 0x10 /* if opaque: get from feeder */
+#define MI_SRC_GET_TRANS 0x20 /* get transparency (not opaque) */
+#define MI_SRC_HAS_TRANS 0x40 /* transparency adapter installed */
+ SANE_Byte source_options;
+ SANE_Byte expanded_resolution;
+#define MI_ENH_CAP_SHADOW 0x01 /* can adjust shadow/highlight */
+#define MI_ENH_CAP_MIDTONE 0x02 /* can adjust midtone */
+ SANE_Byte enhance_cap;
+ SANE_Int max_lookup_size; /* max. size of gamma LUT */
+ SANE_Int max_gamma_bit_depth; /* max. bits of a gamma LUT element */
+ SANE_Int gamma_size; /* size (bytes) of each LUT element */
+ SANE_Byte fast_color_preview; /* allows fast color preview? */
+ SANE_Byte xfer_format_select; /* allows select of transfer format? */
+#define MI_COLSEQ_PLANE 0x00
+#define MI_COLSEQ_PIXEL 0x01
+#define MI_COLSEQ_RGB 0x02
+#define MI_COLSEQ_NONRGB 0x03
+#define MI_COLSEQ_2PIXEL 0x11 /* Agfa StudioStar */
+ SANE_Byte color_sequence; /* color sequence spec. code */
+ SANE_Byte does_3pass; /* allows 3-pass scanning? */
+ SANE_Byte does_mode1; /* allows MODE1 sense/select comm's? */
+#define MI_FMT_CAP_4BPP 0x01
+#define MI_FMT_CAP_10BPP 0x02
+#define MI_FMT_CAP_12BPP 0x04
+#define MI_FMT_CAP_16BPP 0x08
+ SANE_Byte bit_formats; /* output bit formats capabilities */
+#define MI_EXCAP_OFF_CTL 0x01
+#define MI_EXCAP_DIS_LNTBL 0x02
+#define MI_EXCAP_DIS_RECAL 0x04
+ SANE_Byte extra_cap;
+ /* SANE_Int contrast_vals; rolled into cont_settings */
+ SANE_Int min_contrast;
+ SANE_Int max_contrast;
+ /* SANE_Int exposure_vals; rolled into exp_settings */
+ SANE_Int min_exposure;
+ SANE_Int max_exposure;
+ SANE_Byte does_expansion; /* does expanded-mode expansion internally? */
+} Microtek_Info;
+
+
+
+/*******************************************************************/
+/***** device structure (one for each device discovered) *****/
+/*******************************************************************/
+
+typedef struct Microtek_Device {
+ struct Microtek_Device *next; /* next, for linked list */
+ SANE_Device sane; /* SANE generic device block */
+ Microtek_Info info; /* detailed scanner spec */
+} Microtek_Device;
+
+
+
+/*******************************************************************/
+/***** ring buffer structure *****/
+/***** ....image workspace during scan *****/
+/*******************************************************************/
+
+typedef struct ring_buffer {
+ size_t bpl; /* bytes per line */
+ size_t ppl; /* pixels per line */
+
+ uint8_t *base; /* base address of buffer */
+
+ size_t size; /* size (bytes) of ring buffer */
+ size_t initial_size; /* initial size of ring buffer */
+
+ size_t tail_blue; /* byte index, next blue line */
+ size_t tail_green; /* byte index, next green line */
+ size_t tail_red; /* byte index, next red line */
+
+ size_t blue_extra; /* unmatched blue bytes */
+ size_t green_extra; /* unmatched green bytes */
+ size_t red_extra; /* unmatched red bytes */
+
+ size_t complete_count;
+ size_t head_complete;
+
+} ring_buffer;
+
+
+
+
+/*******************************************************************/
+/***** scanner structure (one for each device in use) *****/
+/***** ....all the state needed to define a scan request *****/
+/*******************************************************************/
+
+typedef struct Microtek_Scanner {
+ struct Microtek_Scanner *next; /* for linked list */
+ Microtek_Device *dev; /* raw device info */
+
+ SANE_Option_Descriptor sod[RNUM_OPTIONS]; /* option list for session */
+ Option_Value val[RNUM_OPTIONS]; /* option values for session */
+
+ /* SANE_Int gamma_table[4][256];*/
+ SANE_Int *gray_lut;
+ SANE_Int *red_lut;
+ SANE_Int *green_lut;
+ SANE_Int *blue_lut;
+
+ SANE_Range res_range; /* range desc. for resolution */
+ SANE_Range exp_res_range; /* range desc. for exp. resolution */
+
+ /* scan parameters, ready to toss to SCSI commands*/
+
+ /* ...set by sane_open (i.e. general/default scanner parameters) */
+#define MS_UNIT_PIXELS 0
+#define MS_UNIT_18INCH 1
+ SANE_Byte unit_type; /* pixels or 1/8" */
+#define MS_RES_1PER 0
+#define MS_RES_5PER 1
+ SANE_Byte res_type; /* 1% or 5% */
+ SANE_Bool midtone_support;
+ SANE_Int paper_length; /* whatever unit */
+
+ SANE_Bool do_clever_precal; /* calibrate scanner once, via fake scan */
+ SANE_Bool do_real_calib; /* calibrate via magic commands */
+ SANE_Bool calib_once; /* ...only calibrate magically once */
+
+ SANE_Bool allow_calibrate;
+ SANE_Bool onepass;
+ SANE_Bool prescan, allowbacktrack;
+ SANE_Bool reversecolors;
+ SANE_Bool fastprescan;
+ SANE_Int bits_per_color;
+ SANE_Int gamma_entries;
+ SANE_Int gamma_entry_size;
+ SANE_Int gamma_bit_depth;
+ /* SANE_Int gamma_max_entry;*/
+
+ SANE_Range gamma_entry_range;
+ SANE_Range contrast_range;
+ SANE_Range exposure_range;
+
+ /* ...set by sane_get_parameters (i.e. parameters specified by options) */
+ SANE_Parameters params; /* format, lastframe, lines, depth, ppl, bpl */
+ SANE_Int x1; /* in 'units' */
+ SANE_Int y1;
+ SANE_Int x2;
+ SANE_Int y2;
+#define MS_MODE_LINEART 0
+#define MS_MODE_HALFTONE 1
+#define MS_MODE_GRAY 2
+#define MS_MODE_COLOR 3
+ SANE_Int mode;
+#define MS_FILT_CLEAR 0
+#define MS_FILT_RED 1
+#define MS_FILT_GREEN 2
+#define MS_FILT_BLUE 3
+ SANE_Byte filter;
+ SANE_Bool onepasscolor, transparency, useADF;
+ SANE_Bool threepasscolor, expandedresolution;
+ SANE_Int resolution;
+ SANE_Byte resolution_code;
+ SANE_Byte exposure, contrast;
+ SANE_Byte pattern;
+ SANE_Byte velocity;
+ SANE_Byte shadow, highlight, midtone;
+ SANE_Byte bright_r, bright_g, bright_b; /* ??? XXXXXXXX signed char */
+ SANE_Bool multibit;
+ SANE_Byte color_seq;
+
+ /* ...stuff needed while in mid-scan */
+#define MS_LNFMT_FLAT 0
+#define MS_LNFMT_SEQ_RGB 1
+#define MS_LNFMT_GOOFY_RGB 2
+#define MS_LNFMT_SEQ_2R2G2B 3
+ SANE_Int line_format; /* specify how we need to repackage scanlines */
+
+ SANE_Int pixel_bpl; /* bytes per line, pixels */
+ SANE_Int header_bpl; /* bytes per line, headers */
+ SANE_Int ppl; /* pixels per line */
+ SANE_Int planes; /* color planes */
+
+ SANE_Bool doexpansion;
+ double exp_aspect;
+ SANE_Int dest_pixel_bpl;
+ SANE_Int dest_ppl;
+
+ SANE_Int unscanned_lines; /* lines still to be read from scanner */
+ SANE_Int undelivered_bytes; /* bytes still to be returned to frontend */
+ SANE_Int max_scsi_lines; /* max number of lines that fit in SCSI buffer */
+
+
+ int sfd; /* SCSI device file descriptor, -1 when not opened */
+ int scanning; /* true == mid-pass (between sane_start & sane_read=EOF) */
+ int scan_started; /* true == start_scan has scanner going... */
+ int woe; /* Woe! */
+ int this_pass; /* non-zero => in midst of a multipass scan (1,2,3) */
+ int cancel;
+
+ /* we cleverly compare mode_sense results between scans to detect
+ if the scanner may have been reset/power-cycled in the meantime */
+ SANE_Byte mode_sense_cache[10];
+#define MS_PRECAL_NONE 0
+#define MS_PRECAL_GRAY 1
+#define MS_PRECAL_COLOR 2
+#define MS_PRECAL_EXP_COLOR 3
+ SANE_Byte precal_record; /* record what precalibrations have been done */
+
+#define MS_SENSE_IGNORE 1
+ int sense_flags; /* flags passed to the sense handler */
+
+ uint8_t *scsi_buffer;
+ ring_buffer *rb;
+
+} Microtek_Scanner;
+
+
+#endif /* microtek_h */
+
diff --git a/backend/microtek2.c b/backend/microtek2.c
new file mode 100644
index 0000000..d56e568
--- /dev/null
+++ b/backend/microtek2.c
@@ -0,0 +1,8409 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ microtek2.c
+
+ This file (C) 1998, 1999 Bernd Schroeder
+ modifications 2000, 2001 Karsten Festag
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for Microtek scanners with
+ SCSI-2 command set.
+
+ (feedback to: bernd@aquila.muc.de)
+ ( karsten.festag@t-online.de)
+ ***************************************************************************/
+
+
+#ifdef _AIX
+# include <lalloca.h> /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <math.h>
+
+#include "../include/_stdint.h"
+
+#ifdef HAVE_AUTHORIZATION
+#include <sys/stat.h>
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifndef TESTBACKEND
+#define BACKEND_NAME microtek2
+#else
+#define BACKEND_NAME microtek2_test
+#endif
+
+/* for testing*/
+/*#define NO_PHANTOMTYPE_SHADING*/
+
+#include "../include/sane/sanei_backend.h"
+
+#include "microtek2.h"
+
+#ifdef HAVE_AUTHORIZATION
+static SANE_Auth_Callback auth_callback;
+#endif
+
+static int md_num_devices = 0; /* number of devices from config file */
+static Microtek2_Device *md_first_dev = NULL; /* list of known devices */
+static Microtek2_Scanner *ms_first_handle = NULL; /* list of open scanners */
+
+/* options that can be configured in the config file */
+static Config_Options md_options =
+ { 1.0, "off", "off", "off", "off", "off", "off"};
+static Config_Temp *md_config_temp = NULL;
+static int md_dump = 0; /* from config file: */
+ /* 1: inquiry + scanner attributes */
+ /* 2: + all scsi commands and data */
+ /* 3: + all scan data */
+static int md_dump_clear = 1;
+
+
+/*---------- sane_cancel() ---------------------------------------------------*/
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Microtek2_Scanner *ms = handle;
+
+ DBG(30, "sane_cancel: handle=%p\n", handle);
+
+ if ( ms->scanning == SANE_TRUE )
+ cleanup_scanner(ms);
+ ms->cancelled = SANE_TRUE;
+ ms->fd[0] = ms->fd[1] = -1;
+}
+
+
+/*---------- sane_close() ----------------------------------------------------*/
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Microtek2_Scanner *ms = handle;
+
+ DBG(30, "sane_close: ms=%p\n", (void *) ms);
+
+ if ( ! ms )
+ return;
+
+ /* free malloc'ed stuff */
+ cleanup_scanner(ms);
+
+ /* remove Scanner from linked list */
+ if ( ms_first_handle == ms )
+ ms_first_handle = ms->next;
+ else
+ {
+ Microtek2_Scanner *ts = ms_first_handle;
+ while ( (ts != NULL) && (ts->next != ms) )
+ ts = ts->next;
+ ts->next = ts->next->next; /* == ms->next */
+ }
+ DBG(100, "free ms at %p\n", (void *) ms);
+ free((void *) ms);
+ ms = NULL;
+}
+
+
+/*---------- sane_exit() -----------------------------------------------------*/
+
+void
+sane_exit (void)
+{
+ Microtek2_Device *next;
+ int i;
+
+ DBG(30, "sane_exit:\n");
+
+ /* close all leftover Scanners */
+ while (ms_first_handle != NULL)
+ sane_close(ms_first_handle);
+ /* free up device list */
+ while (md_first_dev != NULL)
+ {
+ next = md_first_dev->next;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( md_first_dev->custom_gamma_table[i] )
+ {
+ DBG(100, "free md_first_dev->custom_gamma_table[%d] at %p\n",
+ i, (void *) md_first_dev->custom_gamma_table[i]);
+ free((void *) md_first_dev->custom_gamma_table[i]);
+ md_first_dev->custom_gamma_table[i] = NULL;
+ }
+ }
+
+ if ( md_first_dev->shading_table_w )
+ {
+ DBG(100, "free md_first_dev->shading_table_w at %p\n",
+ md_first_dev->shading_table_w);
+ free((void *) md_first_dev->shading_table_w);
+ md_first_dev->shading_table_w = NULL;
+ }
+
+ if ( md_first_dev->shading_table_d )
+ {
+ DBG(100, "free md_first_dev->shading_table_d at %p\n",
+ md_first_dev->shading_table_d);
+ free((void *) md_first_dev->shading_table_d);
+ md_first_dev->shading_table_d = NULL;
+ }
+
+ DBG(100, "free md_first_dev at %p\n", (void *) md_first_dev);
+ free((void *) md_first_dev);
+ md_first_dev = next;
+ }
+ sane_get_devices(NULL, SANE_FALSE); /* free list of SANE_Devices */
+
+ DBG(30, "sane_exit: MICROTEK2 says goodbye.\n");
+}
+
+
+/*---------- sane_get_devices()-----------------------------------------------*/
+
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ /* return a list of available devices; available here means that we get */
+ /* a positive response to an 'INQUIRY' and possibly to a */
+ /* 'READ SCANNER ATTRIBUTE' call */
+
+ static const SANE_Device **sd_list = NULL;
+ Microtek2_Device *md;
+ SANE_Status status;
+ int index;
+
+ DBG(30, "sane_get_devices: local_only=%d\n", local_only);
+
+ /* this is hack to get the list freed with a call from sane_exit() */
+ if ( device_list == NULL )
+ {
+ if ( sd_list )
+ {
+ DBG(100, "free sd_list at %p\n", (void *) sd_list);
+ free(sd_list);
+ sd_list=NULL;
+ }
+ DBG(30, "sane_get_devices: sd_list_freed\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* first free old list, if there is one; frontend wants a new list */
+ if ( sd_list )
+ {
+ DBG(100, "free sd_list at %p\n", (void *) sd_list);
+ free(sd_list); /* free array of pointers */
+ }
+
+ sd_list = (const SANE_Device **)
+ malloc( (md_num_devices + 1) * sizeof(SANE_Device **));
+ DBG(100, "sane_get_devices: sd_list=%p, malloc'd %lu bytes\n",
+ (void *) sd_list, (u_long) ((md_num_devices + 1) * sizeof(SANE_Device **)));
+
+ if ( ! sd_list )
+ {
+ DBG(1, "sane_get_devices: malloc() for sd_list failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ *device_list = sd_list;
+ index = 0;
+ md = md_first_dev;
+ while ( md )
+ {
+ status = attach(md);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(10, "sane_get_devices: attach status '%s'\n",
+ sane_strstatus(status));
+ md = md->next;
+ continue;
+ }
+
+ /* check whether unit is ready, if so add it to the list */
+ status = scsi_test_unit_ready(md);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(10, "sane_get_devices: test_unit_ready status '%s'\n",
+ sane_strstatus(status));
+ md = md->next;
+ continue;
+ }
+
+ sd_list[index] = &md->sane;
+
+ ++index;
+ md = md->next;
+ }
+
+ sd_list[index] = NULL;
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_get_parameters() -------------------------------------------*/
+
+SANE_Status
+sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ Microtek2_Scanner *ms = handle;
+ Microtek2_Device *md;
+ Option_Value *val;
+ Microtek2_Info *mi;
+ int mode;
+ int depth;
+ int bits_pp_in; /* bits per pixel from scanner */
+ int bits_pp_out; /* bits_per_pixel transferred to frontend */
+ int bytes_per_line;
+ double x_pixel_per_mm;
+ double y_pixel_per_mm;
+ double x1_pixel;
+ double y1_pixel;
+ double width_pixel;
+ double height_pixel;
+
+
+ DBG(40, "sane_get_parameters: handle=%p, params=%p\n", handle,
+ (void *) params);
+
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ val= ms->val;
+
+ if ( ! ms->scanning ) /* get an estimate for the params */
+ {
+
+ get_scan_mode_and_depth(ms, &mode, &depth, &bits_pp_in, &bits_pp_out);
+
+ switch ( mode )
+ {
+ case MS_MODE_COLOR:
+ if ( mi->onepass )
+ {
+ ms->params.format = SANE_FRAME_RGB;
+ ms->params.last_frame = SANE_TRUE;
+ }
+ else
+ {
+ ms->params.format = SANE_FRAME_RED;
+ ms->params.last_frame = SANE_FALSE;
+ }
+ break;
+ case MS_MODE_GRAY:
+ case MS_MODE_HALFTONE:
+ case MS_MODE_LINEART:
+ case MS_MODE_LINEARTFAKE:
+ ms->params.format = SANE_FRAME_GRAY;
+ ms->params.last_frame = SANE_TRUE;
+ break;
+ default:
+ DBG(1, "sane_get_parameters: Unknown scan mode %d\n", mode);
+ break;
+ }
+
+ ms->params.depth = (SANE_Int) bits_pp_out;
+
+ /* calculate lines, pixels per line and bytes per line */
+ if ( val[OPT_RESOLUTION_BIND].w == SANE_TRUE )
+ {
+ x_pixel_per_mm = y_pixel_per_mm =
+ SANE_UNFIX(val[OPT_RESOLUTION].w) / MM_PER_INCH;
+ DBG(30, "sane_get_parameters: x_res=y_res=%f\n",
+ SANE_UNFIX(val[OPT_RESOLUTION].w));
+ }
+ else
+ {
+ x_pixel_per_mm = SANE_UNFIX(val[OPT_RESOLUTION].w) / MM_PER_INCH;
+ y_pixel_per_mm = SANE_UNFIX(val[OPT_Y_RESOLUTION].w) / MM_PER_INCH;
+ DBG(30, "sane_get_parameters: x_res=%f, y_res=%f\n",
+ SANE_UNFIX(val[OPT_RESOLUTION].w),
+ SANE_UNFIX(val[OPT_Y_RESOLUTION].w));
+ }
+
+ DBG(30, "sane_get_parameters: x_ppm=%f, y_ppm=%f\n",
+ x_pixel_per_mm, y_pixel_per_mm);
+
+ y1_pixel = SANE_UNFIX(ms->val[OPT_TL_Y].w) * y_pixel_per_mm;
+ height_pixel = fabs(SANE_UNFIX(ms->val[OPT_BR_Y].w) * y_pixel_per_mm
+ - y1_pixel) + 0.5;
+ ms->params.lines = (SANE_Int) height_pixel;
+
+ x1_pixel = SANE_UNFIX(ms->val[OPT_TL_X].w) * x_pixel_per_mm;
+ width_pixel = fabs(SANE_UNFIX(ms->val[OPT_BR_X].w) * x_pixel_per_mm
+ - x1_pixel) + 0.5;
+ ms->params.pixels_per_line = (SANE_Int) width_pixel;
+
+
+ if ( bits_pp_out == 1 )
+ bytes_per_line = (width_pixel + 7 ) / 8;
+ else
+ {
+ bytes_per_line = ( width_pixel * bits_pp_out ) / 8 ;
+ if ( mode == MS_MODE_COLOR && mi->onepass )
+ bytes_per_line *= 3;
+ }
+ ms->params.bytes_per_line = (SANE_Int) bytes_per_line;
+ } /* if ms->scanning */
+
+ if ( params )
+ *params = ms->params;
+
+ DBG(30,"sane_get_parameters: format=%d, last_frame=%d, lines=%d\n",
+ ms->params.format,ms->params.last_frame, ms->params.lines);
+ DBG(30,"sane_get_parameters: depth=%d, ppl=%d, bpl=%d\n",
+ ms->params.depth,ms->params.pixels_per_line, ms->params.bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_get_select_fd() --------------------------------------------*/
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+ Microtek2_Scanner *ms = handle;
+
+
+ DBG(30, "sane_get_select_fd: ms=%p\n", (void *) ms);
+
+ if ( ! ms->scanning )
+ {
+ DBG(1, "sane_get_select_fd: Scanner not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = (SANE_Int) ms->fd[0];
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_init() -----------------------------------------------------*/
+
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ Microtek2_Device *md;
+ FILE *fp;
+ int match;
+ SANE_Auth_Callback trash;
+
+
+ DBG_INIT();
+ DBG(1, "sane_init: Microtek2 (v%d.%d build %s) says hello...\n",
+ MICROTEK2_MAJOR, MICROTEK2_MINOR, MICROTEK2_BUILD);
+
+ if ( version_code )
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+#ifdef HAVE_AUTHORIZATION
+ auth_callback = authorize;
+#else
+ trash = authorize; /* prevents compiler warning "unused variable" */
+#endif
+
+ sanei_thread_init();
+
+ match = 0;
+ fp = sanei_config_open(MICROTEK2_CONFIG_FILE);
+ if ( fp == NULL )
+ DBG(10, "sane_init: file not opened: '%s'\n", MICROTEK2_CONFIG_FILE);
+ else
+ {
+ /* check config file for devices and associated options */
+ parse_config_file(fp, &md_config_temp);
+
+ while ( md_config_temp )
+ {
+ sanei_config_attach_matching_devices(md_config_temp->device,
+ attach_one);
+ if ( md_config_temp->next ) /* go to next device, if existent */
+ md_config_temp = md_config_temp->next;
+ else
+ break;
+ }
+
+ fclose(fp);
+ }
+
+ if ( md_first_dev == NULL )
+ {
+ /* config file not found or no valid entry; default to /dev/scanner */
+ /* instead of insisting on config file */
+ add_device_list("/dev/scanner", &md);
+ if ( md )
+ attach(md);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_open() -----------------------------------------------------*/
+
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *handle)
+{
+ SANE_Status status;
+ Microtek2_Scanner *ms;
+ Microtek2_Device *md;
+#ifdef HAVE_AUTHORIZATION
+ struct stat st;
+ int rc;
+#endif
+
+
+ DBG(30, "sane_open: device='%s'\n", name);
+
+ *handle = NULL;
+ md = md_first_dev;
+
+ if ( name )
+ {
+ /* add_device_list() returns a pointer to the device struct if */
+ /* the device is known or newly added, else it returns NULL */
+
+ status = add_device_list(name, &md);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ if ( ! md )
+ {
+ DBG(10, "sane_open: invalid device name '%s'\n", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* attach calls INQUIRY and READ SCANNER ATTRIBUTES */
+ status = attach(md);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms = malloc(sizeof(Microtek2_Scanner));
+ DBG(100, "sane_open: ms=%p, malloc'd %lu bytes\n",
+ (void *) ms, (u_long) sizeof(Microtek2_Scanner));
+ if ( ms == NULL )
+ {
+ DBG(1, "sane_open: malloc() for ms failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(ms, 0, sizeof(Microtek2_Scanner));
+ ms->dev = md;
+ ms->scanning = SANE_FALSE;
+ ms->cancelled = SANE_FALSE;
+ ms->current_pass = 0;
+ ms->sfd = -1;
+ ms->pid = -1;
+ ms->fp = NULL;
+ ms->gamma_table = NULL;
+ ms->buf.src_buf = ms->buf.src_buffer[0] = ms->buf.src_buffer[1] = NULL;
+ ms->control_bytes = NULL;
+ ms->shading_image = NULL;
+ ms->condensed_shading_w = NULL;
+ ms->condensed_shading_d = NULL;
+ ms->current_color = MS_COLOR_ALL;
+ ms->current_read_color = MS_COLOR_RED;
+
+ init_options(ms, MD_SOURCE_FLATBED);
+
+ /* insert scanner into linked list */
+ ms->next = ms_first_handle;
+ ms_first_handle = ms;
+
+ *handle = ms;
+
+#ifdef HAVE_AUTHORIZATION
+ /* check whether the file with the passwords exists. If it doesnt */
+ /* exist, we dont use any authorization */
+
+ rc = stat(PASSWD_FILE, &st);
+ if ( rc == -1 && errno == ENOENT )
+ return SANE_STATUS_GOOD;
+ else
+ {
+ status = do_authorization(md->name);
+ return status;
+ }
+#else
+ return SANE_STATUS_GOOD;
+#endif
+}
+
+
+/*---------- sane_read() -----------------------------------------------------*/
+
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len )
+{
+ Microtek2_Scanner *ms = handle;
+ SANE_Status status;
+ ssize_t nread;
+
+
+ DBG(30, "sane_read: handle=%p, buf=%p, maxlen=%d\n", handle, buf, maxlen);
+
+ *len = 0;
+
+ if ( ! ms->scanning || ms->cancelled )
+ {
+ if ( ms->cancelled )
+ {
+ status = SANE_STATUS_CANCELLED;
+ }
+ else
+ {
+ DBG(15, "sane_read: Scanner %p not scanning\n", (void *) ms);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ DBG(15, "sane_read: scan cancelled or scanner not scanning->cleanup\n");
+ cleanup_scanner(ms);
+ return status;
+ }
+
+
+ nread = read(ms->fd[0], (void *) buf, (int) maxlen);
+ if ( nread == -1 )
+ {
+ if ( errno == EAGAIN )
+ {
+ DBG(30, "sane_read: currently no data available\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(1, "sane_read: read() failed, errno=%d\n", errno);
+ cleanup_scanner(ms);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if ( nread == 0 )
+ {
+ DBG(15, "sane_read: read 0 bytes -> EOF\n");
+ ms->scanning = SANE_FALSE;
+ cleanup_scanner(ms);
+ return SANE_STATUS_EOF;
+ }
+
+
+ *len = (SANE_Int) nread;
+ DBG(30, "sane_read: *len=%d\n", *len);
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_set_io_mode() ---------------------------------------------*/
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Microtek2_Scanner *ms = handle;
+ int rc;
+
+
+ DBG(30, "sane_set_io_mode: handle=%p, nonblocking=%d\n",
+ handle, non_blocking);
+
+ if ( ! ms->scanning )
+ {
+ DBG(1, "sane_set_io_mode: Scanner not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ rc = fcntl(ms->fd[0], F_SETFL, non_blocking ? O_NONBLOCK : 0);
+ if ( rc == -1 )
+ {
+ DBG(1, "sane_set_io_mode: fcntl() failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- add_device_list() -----------------------------------------------*/
+
+static SANE_Status
+add_device_list(SANE_String_Const dev_name, Microtek2_Device **mdev)
+{
+ Microtek2_Device *md;
+ SANE_String hdev;
+ size_t len;
+
+
+ if ( (hdev = strdup(dev_name)) == NULL)
+ {
+ DBG(5, "add_device_list: malloc() for hdev failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ len = strlen(hdev);
+ if ( hdev[len - 1] == '\n' )
+ hdev[--len] = '\0';
+
+ DBG(30, "add_device_list: device='%s'\n", hdev);
+
+ /* check, if device is already known */
+ md = md_first_dev;
+ while ( md )
+ {
+ if ( strcmp(hdev, md->name) == 0 )
+ {
+ DBG(30, "add_device_list: device '%s' already in list\n", hdev);
+
+ *mdev = md;
+ return SANE_STATUS_GOOD;
+ }
+ md = md->next;
+ }
+
+ md = (Microtek2_Device *) malloc(sizeof(Microtek2_Device));
+ DBG(100, "add_device_list: md=%p, malloc'd %lu bytes\n",
+ (void *) md, (u_long) sizeof(Microtek2_Device));
+ if ( md == NULL )
+ {
+ DBG(1, "add_device_list: malloc() for md failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* initialize Device and add it at the beginning of the list */
+ memset(md, 0, sizeof(Microtek2_Device));
+ md->next = md_first_dev;
+ md_first_dev = md;
+ md->sane.name = NULL;
+ md->sane.vendor = NULL;
+ md->sane.model = NULL;
+ md->sane.type = NULL;
+ md->scan_source = MD_SOURCE_FLATBED;
+ md->shading_table_w = NULL;
+ md->shading_table_d = NULL;
+ strncpy(md->name, hdev, PATH_MAX - 1);
+ if ( md_config_temp )
+ md->opts = md_config_temp->opts;
+ else
+ md->opts = md_options;
+ ++md_num_devices;
+ *mdev = md;
+ DBG(100, "free hdev at %p\n", hdev);
+ free(hdev);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- attach() --------------------------------------------------------*/
+
+static SANE_Status
+attach(Microtek2_Device *md)
+{
+ /* This function is called from sane_init() to do the inquiry and to read */
+ /* the scanner attributes. If one of these calls fails, or if a new */
+ /* device is passed in sane_open() this function may also be called */
+ /* from sane_open() or sane_get_devices(). */
+
+ SANE_String model_string;
+ SANE_Status status;
+ SANE_Byte source_info;
+
+
+ DBG(30, "attach: device='%s'\n", md->name);
+
+ status = scsi_inquiry( &md->info[MD_SOURCE_FLATBED], md->name );
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "attach: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ /* We copy the inquiry info into the info structures for each scansource */
+ /* like ADF, TMA, STRIPE and SLIDE */
+
+ for ( source_info = 1; source_info < 5; ++source_info )
+ memcpy( &md->info[source_info],
+ &md->info[MD_SOURCE_FLATBED],
+ sizeof( Microtek2_Info ) );
+
+ /* Here we should insert a function, that stores all the relevant */
+ /* information in the info structure in a more conveniant format */
+ /* in the device structure, e.g. the model name with a trailing '\0'. */
+
+ status = check_inquiry(md, &model_string);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ md->sane.name = md->name;
+ md->sane.vendor = "Microtek";
+ md->sane.model = strdup(model_string);
+ if ( md->sane.model == NULL )
+ DBG(1, "attach: strdup for model string failed\n");
+ md->sane.type = "flatbed scanner";
+ md->revision = strtod(md->info[MD_SOURCE_FLATBED].revision, NULL);
+
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_FLATBED);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "attach: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( MI_LUTCAP_NONE( md->info[MD_SOURCE_FLATBED].lut_cap) )
+ /* no gamma tables */
+ md->model_flags |= MD_NO_GAMMA;
+
+ /* check whether the device supports transparency media adapters */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_TMA )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_TMA);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ /* check whether the device supports an ADF */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_ADF )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_ADF);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ /* check whether the device supports STRIPES */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_STRIPE )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_STRIPE);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ /* check whether the device supports SLIDES */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_SLIDE )
+ {
+ /* The Phantom 636cx indicates in its attributes that it supports */
+ /* slides, but it doesn't. Thus this command would fail. */
+
+ if ( ! (md->model_flags & MD_NO_SLIDE_MODE) )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_SLIDE);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+ }
+
+ status = scsi_read_system_status(md, -1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- attach_one() ----------------------------------------------------*/
+
+static SANE_Status
+attach_one (const char *name)
+{
+ Microtek2_Device *md;
+ Microtek2_Device *md_tmp;
+
+
+ DBG(30, "attach_one: name='%s'\n", name);
+
+ md_tmp = md_first_dev;
+ /* if add_device_list() adds an entry it does this at the beginning */
+ /* of the list and thus changes md_first_dev */
+ add_device_list(name, &md);
+ if ( md_tmp != md_first_dev )
+ attach(md);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- cancel_scan() ---------------------------------------------------*/
+
+static SANE_Status
+cancel_scan(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+
+
+ DBG(30, "cancel_scan: ms=%p\n", (void *) ms);
+
+ /* READ IMAGE with a transferlength of 0 aborts a scan */
+ ms->transfer_length = 0;
+ status = scsi_read_image(ms, (uint8_t *) NULL, 1);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "cancel_scan: cancel failed: '%s'\n", sane_strstatus(status));
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ status = SANE_STATUS_CANCELLED;
+
+ close(ms->fd[1]);
+
+ /* if we are aborting a scan because, for example, we run out
+ of material on a feeder, then pid may be already -1 and
+ kill(-1, SIGTERM), i.e. killing all our processes, is not
+ likely what we really want - --mj, 2001/Nov/19 */
+ if (ms->pid != -1)
+ {
+ sanei_thread_kill(ms->pid);
+ sanei_thread_waitpid(ms->pid, NULL);
+ }
+
+ return status;
+}
+
+
+/*---------- check_option() --------------------------------------------------*/
+
+static void
+check_option(const char *cp, Config_Options *co)
+{
+ /* This function analyses options in the config file */
+
+ char *endptr;
+
+ /* When this function is called, it is already made sure that this */
+ /* is an option line, i.e. a line that starts with option */
+
+ cp = sanei_config_skip_whitespace(cp); /* skip blanks */
+ cp = sanei_config_skip_whitespace(cp + 6); /* skip "option" */
+ if ( strncmp(cp, "dump", 4) == 0 && isspace(cp[4]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 4);
+ if ( *cp )
+ {
+ md_dump = (int) strtol(cp, &endptr, 10);
+ if ( md_dump > 4 || md_dump < 0 )
+ {
+ md_dump = 1;
+ DBG(30, "check_option: setting dump to %d\n", md_dump);
+ }
+ cp = sanei_config_skip_whitespace(endptr);
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ md_dump = 1;
+ DBG(30, "check_option: option value wrong\n");
+ }
+ }
+ else
+ {
+ DBG(30, "check_option: missing option value\n");
+ /* reasonable fallback */
+ md_dump = 1;
+ }
+ }
+ else if ( strncmp(cp, "strip-height", 12) == 0 && isspace(cp[12]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 12);
+ if ( *cp )
+ {
+ co->strip_height = strtod(cp, &endptr);
+ DBG(30, "check_option: setting strip_height to %f\n",
+ co->strip_height);
+ if ( co->strip_height <= 0.0 )
+ co->strip_height = 14.0;
+ cp = sanei_config_skip_whitespace(endptr);
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->strip_height = 14.0;
+ DBG(30, "check_option: option value wrong: %f\n",
+ co->strip_height);
+ }
+ }
+ }
+ else if ( strncmp(cp, "no-backtrack-option", 19) == 0
+ && isspace(cp[19]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 19);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->no_backtracking = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->no_backtracking = "off";
+ }
+ else
+ co->no_backtracking = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->no_backtracking = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "lightlid-35", 11) == 0
+ && isspace(cp[11]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 11);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->lightlid35 = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->lightlid35 = "off";
+ }
+ else
+ co->lightlid35 = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->lightlid35 = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "toggle-lamp", 11) == 0
+ && isspace(cp[11]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 11);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->toggle_lamp = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->toggle_lamp = "off";
+ }
+ else
+ co->toggle_lamp = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->toggle_lamp = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "lineart-autoadjust", 18) == 0
+ && isspace(cp[18]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 18);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->auto_adjust = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->auto_adjust = "off";
+ }
+ else
+ co->auto_adjust = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->auto_adjust = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "backend-calibration", 19) == 0
+ && isspace(cp[19]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 19);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->backend_calibration = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->backend_calibration = "off";
+ }
+ else
+ co->backend_calibration = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->backend_calibration = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "colorbalance-adjust", 19) == 0
+ && isspace(cp[19]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 19);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->colorbalance_adjust = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->colorbalance_adjust = "off";
+ }
+ else
+ co->colorbalance_adjust = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->colorbalance_adjust = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else
+ DBG(30, "check_option: invalid option in '%s'\n", cp);
+}
+
+
+/*---------- check_inquiry() -------------------------------------------------*/
+
+static SANE_Status
+check_inquiry(Microtek2_Device *md, SANE_String *model_string)
+{
+ Microtek2_Info *mi;
+
+ DBG(30, "check_inquiry: md=%p\n", (void *) md);
+
+ md->n_control_bytes = 0;
+ md->shading_length = 0;
+ md->shading_table_contents = 0;
+
+ mi = &md->info[MD_SOURCE_FLATBED];
+ if ( mi->scsi_version != MI_SCSI_II_VERSION )
+ {
+ DBG(1, "check_inquiry: Device is not a SCSI-II device, but 0x%02x\n",
+ mi->scsi_version);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( mi->device_type != MI_DEVTYPE_SCANNER )
+ {
+ DBG(1, "check_inquiry: Device is not a scanner, but 0x%02x\n",
+ mi->device_type);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( strncasecmp("MICROTEK", mi->vendor, INQ_VENDOR_L) != 0
+ && strncmp(" ", mi->vendor, INQ_VENDOR_L) != 0
+ && strncmp("AGFA ", mi->vendor, INQ_VENDOR_L) != 0 )
+ {
+ DBG(1, "check_inquiry: Device is not a Microtek, but '%.*s'\n",
+ INQ_VENDOR_L, mi->vendor);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ md->shading_depth = 16;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ md->shading_depth = 14;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ md->shading_depth = 12;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ md->shading_depth = 10;
+ else
+ md->shading_depth = 8;
+
+ switch (mi->model_code)
+ {
+ case 0x81:
+ case 0xab:
+ *model_string = "ScanMaker 4";
+ break;
+ case 0x85:
+ *model_string = "ScanMaker V300 / ColorPage-EP";
+ /* The ScanMaker V300 (FW < 2.70) returns some values for the */
+ /* "read image info" command in only two bytes */
+ /* and doesn't understand read_image_status */
+ md->model_flags |= MD_NO_RIS_COMMAND;
+ if ( md->revision < 2.70 )
+ md->model_flags |= MD_RII_TWO_BYTES;
+ break;
+ case 0x87:
+ *model_string = "ScanMaker 5";
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0x89:
+ *model_string = "ScanMaker 6400XL";
+ break;
+ case 0x8a:
+ *model_string = "ScanMaker 9600XL";
+ break;
+ case 0x8c:
+ *model_string = "ScanMaker 630 / ScanMaker V600";
+ break;
+ case 0x8d:
+ *model_string = "ScanMaker 336 / ScanMaker V310";
+ break;
+ case 0x90:
+ case 0x92:
+ *model_string = "E3+ / Vobis HighScan";
+ break;
+ case 0x91:
+ *model_string = "ScanMaker X6 / Phantom 636";
+ /* The X6 indicates a data format of segregated data in TMA mode */
+ /* but actually transfers as chunky data */
+ md->model_flags |= MD_DATA_FORMAT_WRONG;
+ if ( md->revision == 1.00 )
+ md->model_flags |= MD_OFFSET_2;
+ break;
+ case 0x93:
+ *model_string = "ScanMaker 336 / ScanMaker V310";
+ break;
+ case 0x70:
+ case 0x71:
+ case 0x94:
+ case 0xa0:
+ *model_string = "Phantom 330cx / Phantom 336cx / SlimScan C3";
+ /* These models do not accept gamma tables. Apparently they */
+ /* read the control bits and do not accept shading tables */
+ /* They also don't support enhancements (contrast, brightness...)*/
+ md->model_flags |= MD_NO_SLIDE_MODE
+ | MD_NO_GAMMA
+#ifndef NO_PHANTOMTYPE_SHADING
+ | MD_PHANTOM336CX_TYPE_SHADING
+#endif
+ | MD_READ_CONTROL_BIT
+ | MD_NO_ENHANCEMENTS;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ md->n_control_bytes = 320;
+ md->shading_length = 18;
+ md->shading_depth = 10;
+ md->controlbit_offset = 7;
+ break;
+ case 0x95:
+ *model_string = "ArtixScan 1010";
+ break;
+ case 0x97:
+ *model_string = "ScanMaker 636";
+ break;
+ case 0x98:
+ *model_string = "ScanMaker X6EL";
+ if ( md->revision == 1.00 )
+ md->model_flags |= MD_OFFSET_2;
+ break;
+ case 0x99:
+ *model_string = "ScanMaker X6USB";
+ if ( md->revision == 1.00 )
+ md->model_flags |= MD_OFFSET_2;
+ md->model_flags |= MD_X6_SHORT_TRANSFER;
+ break;
+ case 0x9a:
+ *model_string = "Phantom 636cx / C6";
+ /* The Phantom 636cx says it supports the SLIDE mode, but it */
+ /* doesn't. Thus inquring the attributes for slide mode would */
+ /* fail. Also it does not accept gamma tables. Apparently */
+ /* it reads the control bits and does not accept shading tables */
+ md->model_flags |= MD_NO_SLIDE_MODE
+ | MD_READ_CONTROL_BIT
+ | MD_NO_GAMMA
+ | MD_PHANTOM_C6;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ md->n_control_bytes = 647;
+ /* md->shading_length = 18; firmware values seem to work better */
+ md->shading_depth = 12;
+ md->controlbit_offset = 18;
+ break;
+ case 0x9d:
+ *model_string = "AGFA Duoscan T1200";
+ break;
+ case 0xa3:
+ *model_string = "ScanMaker V6USL";
+ /* The V6USL does not accept gamma tables */
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0xa5:
+ *model_string = "ArtixScan 4000t";
+ break;
+ case 0xac:
+ *model_string = "ScanMaker V6UL";
+ /* The V6USL does not accept gamma tables, perhaps the V6UL also */
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0xaf:
+ *model_string = "SlimScan C3";
+ md->model_flags |= MD_NO_SLIDE_MODE
+ | MD_NO_GAMMA
+ | MD_READ_CONTROL_BIT
+ | MD_NO_ENHANCEMENTS;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ md->n_control_bytes = 320;
+ md->controlbit_offset = 7;
+ break;
+ case 0xb0:
+ *model_string = "ScanMaker X12USL";
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->model_flags |= MD_16BIT_TRANSFER
+ | MD_CALIB_DIVISOR_600;
+ break;
+ case 0xb3:
+ *model_string = "ScanMaker 3600";
+ break;
+ case 0xb4:
+ *model_string = "ScanMaker 4700";
+ break;
+ case 0xb6:
+ *model_string = "ScanMaker V6UPL";
+ /* is like V6USL but with USB and Parport interface ?? */
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0xb8:
+ *model_string = "ScanMaker 3700";
+ break;
+ case 0xde:
+ *model_string = "ScanMaker 9800XL";
+ md->model_flags |= MD_NO_GAMMA
+ | MD_16BIT_TRANSFER;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ break;
+ default:
+ DBG(1, "check_inquiry: Model 0x%02x not supported\n", mi->model_code);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- cleanup_scanner() -----------------------------------------------*/
+
+static void
+cleanup_scanner(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ md = ms->dev;
+
+ DBG(30, "cleanup_scanner: ms=%p, ms->sfd=%d\n", (void *) ms, ms->sfd);
+
+ if ( ms->scanning == SANE_TRUE )
+ status=cancel_scan(ms);
+
+ if ( ms->sfd != -1 )
+ sanei_scsi_close(ms->sfd);
+ ms->sfd = -1;
+ ms->pid = -1;
+ ms->fp = NULL;
+ ms->current_pass = 0;
+ ms->scanning = SANE_FALSE;
+ ms->cancelled = SANE_FALSE;
+
+ /* free buffers */
+ if ( ms->buf.src_buffer[0] )
+ {
+ DBG(100, "free ms->buf.src_buffer[0] at %p\n", ms->buf.src_buffer[0]);
+ free((void *) ms->buf.src_buffer[0]);
+ ms->buf.src_buffer[0] = NULL;
+ ms->buf.src_buf = NULL;
+ }
+ if ( ms->buf.src_buffer[1] )
+ {
+ DBG(100, "free ms->buf.src_buffer[1] at %p\n", ms->buf.src_buffer[1]);
+ free((void *) ms->buf.src_buffer[1]);
+ ms->buf.src_buffer[1] = NULL;
+ ms->buf.src_buf = NULL;
+ }
+ if ( ms->buf.src_buf )
+ {
+ DBG(100, "free ms->buf.src_buf at %p\n", ms->buf.src_buf);
+ free((void *) ms->buf.src_buf);
+ ms->buf.src_buf = NULL;
+ }
+ if ( ms->temporary_buffer )
+ {
+ DBG(100, "free ms->temporary_buffer at %p\n", ms->temporary_buffer);
+ free((void *) ms->temporary_buffer);
+ ms->temporary_buffer = NULL;
+ }
+ if ( ms->gamma_table )
+ {
+ DBG(100, "free ms->gamma_table at %p\n", ms->gamma_table);
+ free((void *) ms->gamma_table);
+ ms->gamma_table = NULL;
+ }
+ if ( ms->control_bytes )
+ {
+ DBG(100, "free ms->control_bytes at %p\n", ms->control_bytes);
+ free((void *) ms->control_bytes);
+ ms->control_bytes = NULL;
+ }
+ if ( ms->condensed_shading_w )
+ {
+ DBG(100, "free ms->condensed_shading_w at %p\n",
+ ms->condensed_shading_w);
+ free((void *) ms->condensed_shading_w);
+ ms->condensed_shading_w = NULL;
+ }
+ if ( ms->condensed_shading_d )
+ {
+ DBG(100, "free ms->condensed_shading_d at %p\n",
+ ms->condensed_shading_d);
+ free((void *) ms->condensed_shading_d);
+ ms->condensed_shading_d = NULL;
+ }
+
+ return;
+}
+
+#ifdef HAVE_AUTHORIZATION
+/*---------- do_authorization() ----------------------------------------------*/
+
+static SANE_Status
+do_authorization(char *ressource)
+{
+ /* This function implements a simple authorization function. It looks */
+ /* up an entry in the file SANE_PATH_CONFIG_DIR/auth. Such an entry */
+ /* must be of the form device:user:password where password is a crypt() */
+ /* encrypted password. If several users are allowed to access a device */
+ /* an entry must be created for each user. If no entry exists for device */
+ /* or the file does not exist no authentication is neccessary. If the */
+ /* file exists, but cant be opened the authentication fails */
+
+ SANE_Status status;
+ FILE *fp;
+ int device_found;
+ char username[SANE_MAX_USERNAME_LEN];
+ char password[SANE_MAX_PASSWORD_LEN];
+ char line[MAX_LINE_LEN];
+ char *linep;
+ char *device;
+ char *user;
+ char *passwd;
+ char *p;
+
+
+ DBG(30, "do_authorization: ressource=%s\n", ressource);
+
+ if ( auth_callback == NULL ) /* frontend does not require authorization */
+ return SANE_STATUS_GOOD;
+
+ /* first check if an entry exists in for this device. If not, we dont */
+ /* use authorization */
+
+ fp = fopen(PASSWD_FILE, "r");
+ if ( fp == NULL )
+ {
+ if ( errno == ENOENT )
+ {
+ DBG(1, "do_authorization: file not found: %s\n", PASSWD_FILE);
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(1, "do_authorization: fopen() failed, errno=%d\n", errno);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ linep = &line[0];
+ device_found = 0;
+ while ( fgets(line, MAX_LINE_LEN, fp) )
+ {
+ p = index(linep, SEPARATOR);
+ if ( p )
+ {
+ *p = '\0';
+ device = linep;
+ if ( strcmp(device, ressource) == 0 )
+ {
+ DBG(2, "equal\n");
+ device_found = 1;
+ break;
+ }
+ }
+ }
+
+ if ( ! device_found )
+ {
+ fclose(fp);
+ return SANE_STATUS_GOOD;
+ }
+
+ fseek(fp, 0L, SEEK_SET);
+
+ (*auth_callback) (ressource, username, password);
+
+ status = SANE_STATUS_ACCESS_DENIED;
+ do
+ {
+ fgets(line, MAX_LINE_LEN, fp);
+ if ( ! ferror(fp) && ! feof(fp) )
+ {
+ /* neither strsep(3) nor strtok(3) seem to work on my system */
+ p = index(linep, SEPARATOR);
+ if ( p == NULL )
+ continue;
+ *p = '\0';
+ device = linep;
+ if ( strcmp( device, ressource) != 0 ) /* not a matching entry */
+ continue;
+
+ linep = ++p;
+ p = index(linep, SEPARATOR);
+ if ( p == NULL )
+ continue;
+
+ *p = '\0';
+ user = linep;
+ if ( strncmp(user, username, SANE_MAX_USERNAME_LEN) != 0 )
+ continue; /* username doesnt match */
+
+ linep = ++p;
+ /* rest of the line is considered to be the password */
+ passwd = linep;
+ /* remove newline */
+ *(passwd + strlen(passwd) - 1) = '\0';
+ p = crypt(password, SALT);
+ if ( strcmp(p, passwd) == 0 )
+ {
+ /* authentication ok */
+ status = SANE_STATUS_GOOD;
+ break;
+ }
+ else
+ continue;
+ }
+ } while ( ! ferror(fp) && ! feof(fp) );
+ fclose(fp);
+
+ return status;
+}
+#endif
+
+/*---------- dump_area() -----------------------------------------------------*/
+
+static SANE_Status
+dump_area(uint8_t *area, int len, char *info)
+{
+ /* this function dumps control or information blocks */
+
+#define BPL 16 /* bytes per line to print */
+
+ int i;
+ int o;
+ int o_limit;
+ char outputline[100];
+ char *outbuf;
+
+ if ( ! info[0] )
+ info = "No additional info available";
+
+ DBG(30, "dump_area: %s\n", info);
+
+ outbuf = outputline;
+ o_limit = (len + BPL - 1) / BPL;
+ for ( o = 0; o < o_limit; o++)
+ {
+ sprintf(outbuf, " %4d: ", o * BPL);
+ outbuf += 8;
+ for ( i=0; i < BPL && (o * BPL + i ) < len; i++)
+ {
+ if ( i == BPL / 2 )
+ {
+ sprintf(outbuf, " ");
+ outbuf +=1;
+ }
+ sprintf(outbuf, "%02x", area[o * BPL + i]);
+ outbuf += 2;
+ }
+
+ sprintf(outbuf, "%*s", 2 * ( 2 + BPL - i), " " );
+ outbuf += (2 * ( 2 + BPL - i));
+ sprintf(outbuf, "%s", (i == BPL / 2) ? " " : "");
+ outbuf += ((i == BPL / 2) ? 1 : 0);
+
+ for ( i = 0; i < BPL && (o * BPL + i ) < len; i++)
+ {
+ if ( i == BPL / 2 )
+ {
+ sprintf(outbuf, " ");
+ outbuf += 1;
+ }
+ sprintf(outbuf, "%c", isprint(area[o * BPL + i])
+ ? area[o * BPL + i]
+ : '.');
+ outbuf += 1;
+ }
+ outbuf = outputline;
+ DBG(1, "%s\n", outbuf);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- dump_area2() ----------------------------------------------------*/
+
+static SANE_Status
+dump_area2(uint8_t *area, int len, char *info)
+{
+
+#define BPL 16 /* bytes per line to print */
+
+ int i, linelength;
+ char outputline[100];
+ char *outbuf;
+ linelength = BPL * 3;
+
+ if ( ! info[0] )
+ info = "No additional info available";
+
+ DBG(1, "[%s]\n", info);
+
+ outbuf = outputline;
+ for ( i = 0; i < len; i++)
+ {
+ sprintf(outbuf, "%02x,", *(area + i));
+ outbuf += 3;
+ if ( ((i+1)%BPL == 0) || (i == len-1) )
+ {
+ outbuf = outputline;
+ DBG(1, "%s\n", outbuf);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- dump_to_file() --------------------------------------------------*/
+/*--- only for debugging, currently not used -----*/
+#if 0
+static SANE_Status
+dump_to_file(uint8_t *area, int len, char *filename, char *mode)
+{
+FILE *out;
+int i;
+
+ out = fopen(filename, mode);
+
+ for ( i = 0; i < len; i++)
+ fputc( *(area + i ), out);
+
+ fclose(out);
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+/*---------- dump_attributes() -----------------------------------------------*/
+
+static SANE_Status
+dump_attributes(Microtek2_Info *mi)
+{
+ /* dump all we know about the scanner */
+
+ int i;
+
+ DBG(30, "dump_attributes: mi=%p\n", (void *) mi);
+ DBG(1, "\n");
+ DBG(1, "Scanner attributes from device structure\n");
+ DBG(1, "========================================\n");
+ DBG(1, "Scanner ID...\n");
+ DBG(1, "~~~~~~~~~~~~~\n");
+ DBG(1, " Vendor Name%15s: '%s'\n", " ", mi->vendor);
+ DBG(1, " Model Name%16s: '%s'\n", " ", mi->model);
+ DBG(1, " Revision%18s: '%s'\n", " ", mi->revision);
+ DBG(1, " Model Code%16s: 0x%02x\n"," ", mi->model_code);
+ switch(mi->model_code)
+ {
+ case 0x80: DBG(1, "Redondo 2000XL / ArtixScan 2020\n"); break;
+ case 0x81: DBG(1, "ScanMaker 4 / Aruba\n"); break;
+ case 0x82: DBG(1, "Bali\n"); break;
+ case 0x83: DBG(1, "Washington\n"); break;
+ case 0x84: DBG(1, "Manhattan\n"); break;
+ case 0x85: DBG(1, "ScanMaker V300 / Phantom parallel / TR3\n"); break;
+ case 0x86: DBG(1, "CCP\n"); break;
+ case 0x87: DBG(1, "Scanmaker V\n"); break;
+ case 0x88: DBG(1, "Scanmaker VI\n"); break;
+ case 0x89: DBG(1, "ScanMaker 6400XL / A3-400\n"); break;
+ case 0x8a: DBG(1, "ScanMaker 9600XL / A3-600\n"); break;
+ case 0x8b: DBG(1, "Watt\n"); break;
+ case 0x8c: DBG(1, "ScanMaker V600 / TR6\n"); break;
+ case 0x8d: DBG(1, "ScanMaker V310 / Tr3 10-bit\n"); break;
+ case 0x8e: DBG(1, "CCB\n"); break;
+ case 0x8f: DBG(1, "Sun Rise\n"); break;
+ case 0x90: DBG(1, "ScanMaker E3+ 10-bit\n"); break;
+ case 0x91: DBG(1, "ScanMaker X6 / Phantom 636\n"); break;
+ case 0x92: DBG(1, "ScanMaker E3+ / Vobis Highscan\n"); break;
+ case 0x93: DBG(1, "ScanMaker V310\n"); break;
+ case 0x94: DBG(1, "SlimScan C3 / Phantom 330cx / 336cx\n"); break;
+ case 0x95: DBG(1, "ArtixScan 1010\n"); break;
+ case 0x97: DBG(1, "ScanMaker V636\n"); break;
+ case 0x98: DBG(1, "ScanMaker X6EL\n"); break;
+ case 0x99: DBG(1, "ScanMaker X6 / X6USB\n"); break;
+ case 0x9a: DBG(1, "SlimScan C6 / Phantom 636cx\n"); break;
+ case 0x9d: DBG(1, "AGFA DuoScan T1200\n"); break;
+ case 0xa0: DBG(1, "SlimScan C3 / Phantom 336cx\n"); break;
+ case 0xac: DBG(1, "ScanMaker V6UL\n"); break;
+ case 0xa3: DBG(1, "ScanMaker V6USL\n"); break;
+ case 0xaf: DBG(1, "SlimScan C3 / Phantom 336cx\n"); break;
+ case 0xb0: DBG(1, "ScanMaker X12USL\n"); break;
+ case 0xb3: DBG(1, "ScanMaker 3600\n"); break;
+ case 0xb4: DBG(1, "ScanMaker 4700\n"); break;
+ case 0xb6: DBG(1, "ScanMaker V6UPL\n"); break;
+ case 0xb8: DBG(1, "ScanMaker 3700\n"); break;
+ case 0xde: DBG(1, "ScanMaker 9800XL\n"); break;
+ default: DBG(1, "Unknown\n"); break;
+ }
+ DBG(1, " Device Type Code%10s: 0x%02x (%s),\n", " ",
+ mi->device_type,
+ mi->device_type & MI_DEVTYPE_SCANNER ?
+ "Scanner" : "Unknown type");
+
+ switch (mi->scanner_type)
+ {
+ case MI_TYPE_FLATBED:
+ DBG(1, " Scanner type%14s:%s", " ", " Flatbed scanner\n");
+ break;
+ case MI_TYPE_TRANSPARENCY:
+ DBG(1, " Scanner type%14s:%s", " ", " Transparency scanner\n");
+ break;
+ case MI_TYPE_SHEEDFEED:
+ DBG(1, " Scanner type%14s:%s", " ", " Sheet feed scanner\n");
+ break;
+ default:
+ DBG(1, " Scanner type%14s:%s", " ", " Unknown\n");
+ break;
+ }
+
+ DBG(1, " Supported options%9s: Automatic document feeder: %s\n",
+ " ", mi->option_device & MI_OPTDEV_ADF ? "Yes" : "No");
+ DBG(1, "%30sTransparency media adapter: %s\n",
+ " ", mi->option_device & MI_OPTDEV_TMA ? "Yes" : "No");
+ DBG(1, "%30sAuto paper detecting: %s\n",
+ " ", mi->option_device & MI_OPTDEV_ADP ? "Yes" : "No");
+ DBG(1, "%30sAdvanced picture system: %s\n",
+ " ", mi->option_device & MI_OPTDEV_APS ? "Yes" : "No");
+ DBG(1, "%30sStripes: %s\n",
+ " ", mi->option_device & MI_OPTDEV_STRIPE ? "Yes" : "No");
+ DBG(1, "%30sSlides: %s\n",
+ " ", mi->option_device & MI_OPTDEV_SLIDE ? "Yes" : "No");
+ DBG(1, " Scan button%15s: %s\n", " ", mi->scnbuttn ? "Yes" : "No");
+
+ DBG(1, "\n");
+ DBG(1, " Imaging Capabilities...\n");
+ DBG(1, " ~~~~~~~~~~~~~~~~~~~~~~~\n");
+ DBG(1, " Color scanner%6s: %s\n", " ", (mi->color) ? "Yes" : "No");
+ DBG(1, " Number passes%6s: %d pass%s\n", " ",
+ (mi->onepass) ? 1 : 3,
+ (mi->onepass) ? "" : "es");
+ DBG(1, " Resolution%9s: X-max: %5d dpi\n%35sY-max: %5d dpi\n",
+ " ", mi->max_xresolution, " ",mi->max_yresolution);
+ DBG(1, " Geometry%11s: Geometric width: %5d pts (%2.2f'')\n", " ",
+ mi->geo_width, (float) mi->geo_width / (float) mi->opt_resolution);
+ DBG(1, "%23sGeometric height:%5d pts (%2.2f'')\n", " ",
+ mi->geo_height, (float) mi->geo_height / (float) mi->opt_resolution);
+ DBG(1, " Optical resolution%1s: %d\n", " ", mi->opt_resolution);
+
+ DBG(1, " Modes%14s: Lineart: %s\n%35sHalftone: %s\n", " ",
+ (mi->scanmode & MI_HASMODE_LINEART) ? " Yes" : " No", " ",
+ (mi->scanmode & MI_HASMODE_HALFTONE) ? "Yes" : "No");
+
+ DBG(1, "%23sGray: %s\n%35sColor: %s\n", " ",
+ (mi->scanmode & MI_HASMODE_GRAY) ? " Yes" : " No", " ",
+ (mi->scanmode & MI_HASMODE_COLOR) ? " Yes" : " No");
+
+ DBG(1, " Depths%14s: Nibble Gray: %s\n",
+ " ", (mi->depth & MI_HASDEPTH_NIBBLE) ? "Yes" : "No");
+ DBG(1, "%23s10-bit-color: %s\n",
+ " ", (mi->depth & MI_HASDEPTH_10) ? "Yes" : "No");
+ DBG(1, "%23s12-bit-color: %s\n", " ",
+ (mi->depth & MI_HASDEPTH_12) ? "Yes" : "No");
+ DBG(1, "%23s14-bit-color: %s\n", " ",
+ (mi->depth & MI_HASDEPTH_14) ? "Yes" : "No");
+ DBG(1, "%23s16-bit-color: %s\n", " ",
+ (mi->depth & MI_HASDEPTH_16) ? "Yes" : "No");
+ DBG(1, " d/l of HT pattern%2s: %s\n",
+ " ", (mi->has_dnldptrn) ? "Yes" : "No");
+ DBG(1, " Builtin HT pattern%1s: %d\n", " ", mi->grain_slct);
+
+ if ( MI_LUTCAP_NONE(mi->lut_cap) )
+ DBG(1, " LUT capabilities : None\n");
+ if ( mi->lut_cap & MI_LUTCAP_256B )
+ DBG(1, " LUT capabilities : 256 bytes\n");
+ if ( mi->lut_cap & MI_LUTCAP_1024B )
+ DBG(1, " LUT capabilities : 1024 bytes\n");
+ if ( mi->lut_cap & MI_LUTCAP_1024W )
+ DBG(1, " LUT capabilities : 1024 words\n");
+ if ( mi->lut_cap & MI_LUTCAP_4096B )
+ DBG(1, " LUT capabilities : 4096 bytes\n");
+ if ( mi->lut_cap & MI_LUTCAP_4096W )
+ DBG(1, " LUT capabilities : 4096 words\n");
+ if ( mi->lut_cap & MI_LUTCAP_64k_W )
+ DBG(1, " LUT capabilities : 64k words\n");
+ if ( mi->lut_cap & MI_LUTCAP_16k_W )
+ DBG(1, " LUT capabilities : 16k words\n");
+ DBG(1, "\n");
+ DBG(1, " Miscellaneous capabilities...\n");
+ DBG(1, " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+ if ( mi->onepass)
+ {
+ switch(mi->data_format)
+ {
+ case MI_DATAFMT_CHUNKY:
+ DBG(1, " Data format :%s",
+ " Chunky data, R, G & B in one pixel\n");
+ break;
+ case MI_DATAFMT_LPLCONCAT:
+ DBG(1, " Data format :%s",
+ " Line by line in concatenated sequence,\n");
+ DBG(1, "%23swithout color indicator\n", " ");
+ break;
+ case MI_DATAFMT_LPLSEGREG:
+ DBG(1, " Data format :%s",
+ " Line by line in segregated sequence,\n");
+ DBG(1, "%23swith color indicator\n", " ");
+ break;
+ case MI_DATAFMT_WORDCHUNKY:
+ DBG(1, " Data format : Word chunky data\n");
+ break;
+ default:
+ DBG(1, " Data format : Unknown\n");
+ break;
+ }
+ }
+ else
+ DBG(1, "No information with 3-pass scanners\n");
+
+ DBG(1, " Color Sequence%17s: \n", " ");
+ for ( i = 0; i < RSA_COLORSEQUENCE_L; i++)
+ {
+ switch(mi->color_sequence[i])
+ {
+ case MI_COLSEQ_RED: DBG(1,"%34s%s\n", " ","R"); break;
+ case MI_COLSEQ_GREEN: DBG(1,"%34s%s\n", " ","G"); break;
+ case MI_COLSEQ_BLUE: DBG(1,"%34s%s\n", " ","B"); break;
+ }
+ }
+ if ( mi->new_image_status == SANE_TRUE )
+ DBG(1, " Using new ReadImageStatus format\n");
+ else
+ DBG(1, " Using old ReadImageStatus format\n");
+ if ( mi->direction & MI_DATSEQ_RTOL )
+ DBG(1, " Scanning direction : right to left\n");
+ else
+ DBG(1, " Scanning direction : left to right\n");
+ DBG(1, " CCD gap%24s: %d lines\n", " ", mi->ccd_gap);
+ DBG(1, " CCD pixels%21s: %d\n", " ", mi->ccd_pixels);
+ DBG(1, " Calib white stripe location%4s: %d\n",
+ " ", mi->calib_white);
+ DBG(1, " Max calib space%16s: %d\n", " ", mi->calib_space);
+ DBG(1, " Number of lens%17s: %d\n", " ", mi->nlens);
+ DBG(1, " Max number of windows%10s: %d\n", " ", mi->nwindows);
+ DBG(1, " Shading transfer function%6s: 0x%02x\n", " ",mi->shtrnsferequ);
+ DBG(1, " Red balance%20s: %d\n", " ", mi->balance[0]);
+ DBG(1, " Green balance%18s: %d\n", " ", mi->balance[1]);
+ DBG(1, " Blue balance%19s: %d\n", " " , mi->balance[2]);
+ DBG(1, " Buffer type%20s: %s\n",
+ " ", mi->buftype ? "Ping-Pong" : "Ring");
+ DBG(1, " FEPROM%25s: %s\n", " ", mi->feprom ? "Yes" : "No");
+
+ md_dump_clear = 0;
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- max_string_size() -----------------------------------------------*/
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size;
+ size_t max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen(strings[i]) + 1; /* +1 because NUL counts as part of string */
+ if (size > max_size) max_size = size;
+ }
+ return max_size;
+}
+
+/*---------- parse_config_file() ---------------------------------------------*/
+
+static void
+parse_config_file(FILE *fp, Config_Temp **ct)
+{
+ /* builds a list of device names with associated options from the */
+ /* config file for later use, when building the list of devices. */
+ /* ct->device = NULL indicates global options (valid for all devices */
+
+ char s[PATH_MAX];
+ Config_Options global_opts;
+ Config_Temp *hct1;
+ Config_Temp *hct2;
+
+
+ DBG(30, "parse_config_file: fp=%p\n", (void *) fp);
+
+ *ct = hct1 = NULL;
+
+ /* first read global options and store them in global_opts */
+ /* initialize global_opts with default values */
+
+ global_opts = md_options;
+
+ while ( sanei_config_read(s, sizeof(s), fp) )
+ {
+ DBG(100, "parse_config_file: read line: %s\n", s);
+ if ( *s == '#' || *s == '\0' ) /* ignore empty lines and comments */
+ continue;
+
+ if ( strncmp( sanei_config_skip_whitespace(s), "option ", 7) == 0
+ || strncmp( sanei_config_skip_whitespace(s), "option\t", 7) == 0 )
+ {
+ DBG(100, "parse_config_file: found global option %s\n", s);
+ check_option(s, &global_opts);
+ }
+ else /* it is considered a new device */
+ break;
+ }
+
+ if ( ferror(fp) || feof(fp) )
+ {
+ if ( ferror(fp) )
+ DBG(1, "parse_config_file: fread failed: errno=%d\n", errno);
+
+ return;
+ }
+
+ while ( ! feof(fp) && ! ferror(fp) )
+ {
+ if ( *s == '#' || *s == '\0' ) /* ignore empty lines and comments */
+ {
+ sanei_config_read(s, sizeof(s), fp);
+ continue;
+ }
+
+ if ( strncmp( sanei_config_skip_whitespace(s), "option ", 7) == 0
+ || strncmp( sanei_config_skip_whitespace(s), "option\t", 7) == 0 )
+ {
+ /* when we enter this loop for the first time we allocate */
+ /* memory, because the line surely contains a device name, */
+ /* so hct1 is always != NULL at this point */
+ DBG(100, "parse_config_file: found device option %s\n", s);
+ check_option(s, &hct1->opts);
+ }
+
+
+ else /* it is considered a new device */
+ {
+ DBG(100, "parse_config_file: found device %s\n", s);
+ hct2 = (Config_Temp *) malloc(sizeof(Config_Temp));
+ if ( hct2 == NULL )
+ {
+ DBG(1, "parse_config_file: malloc() failed\n");
+ return;
+ }
+
+ if ( *ct == NULL ) /* first element */
+ *ct = hct1 = hct2;
+
+ hct1->next = hct2;
+ hct1 = hct2;
+
+ hct1->device = strdup(s);
+ hct1->opts = global_opts;
+ hct1->next = NULL;
+ }
+ sanei_config_read(s, sizeof(s), fp);
+ }
+ /* set filepointer to the beginning of the file */
+ fseek(fp, 0L, SEEK_SET);
+ return;
+}
+
+
+/*---------- signal_handler() ------------------------------------------------*/
+
+static RETSIGTYPE
+signal_handler (int signal)
+{
+ if ( signal == SIGTERM )
+ {
+ sanei_scsi_req_flush_all ();
+ _exit (SANE_STATUS_GOOD);
+ }
+}
+
+/*---------- init_options() --------------------------------------------------*/
+
+static SANE_Status
+init_options(Microtek2_Scanner *ms, uint8_t current_scan_source)
+{
+ /* This function is called every time, when the scan source changes. */
+ /* The option values, that possibly change, are then reinitialized, */
+ /* whereas the option descriptors and option values that never */
+ /* change are not */
+
+ SANE_Option_Descriptor *sod;
+ SANE_Status status;
+ Option_Value *val;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ int tablesize;
+ int option_size;
+ int max_gamma_value;
+ int color;
+ int i;
+ static int first_call = 1; /* indicates, whether option */
+ /* descriptors must be initialized */
+ /* cannot be used as after a sane_close the sod's must be initialized */
+
+ DBG(30, "init_options: handle=%p, source=%d\n", (void *) ms,
+ current_scan_source);
+
+ sod = ms->sod;
+ val = ms->val;
+ md = ms->dev;
+ mi = &md->info[current_scan_source];
+
+ /* needed for gamma calculation */
+ get_lut_size(mi, &md->max_lut_size, &md->lut_entry_size);
+
+ /* calculate new values, where possibly needed */
+
+ /* Scan source */
+ if ( val[OPT_SOURCE].s )
+ free((void *) val[OPT_SOURCE].s);
+ i = 0;
+ md->scansource_list[i] = (SANE_String) MD_SOURCESTRING_FLATBED;
+ if ( current_scan_source == MD_SOURCE_FLATBED )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ if ( md->status.adfcnt )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_ADF;
+ if ( current_scan_source == MD_SOURCE_ADF )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+ if ( md->status.tmacnt )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_TMA;
+ if ( current_scan_source == MD_SOURCE_TMA )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+ if ( mi->option_device & MI_OPTDEV_STRIPE )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_STRIPE;
+ if ( current_scan_source == MD_SOURCE_STRIPE )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+
+ /* Comment this out as long as I do not know in which bit */
+ /* it is indicated, whether a slide adapter is connected */
+#if 0
+ if ( mi->option_device & MI_OPTDEV_SLIDE )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_SLIDE;
+ if ( current_scan_source == MD_SOURCE_SLIDE )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+#endif
+
+ md->scansource_list[++i] = NULL;
+
+ /* Scan mode */
+ if ( val[OPT_MODE].s )
+ free((void *) val[OPT_MODE].s);
+
+ i = 0;
+ if ( (mi->scanmode & MI_HASMODE_COLOR) )
+ {
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_COLOR;
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ }
+
+ if ( mi->scanmode & MI_HASMODE_GRAY )
+ {
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_GRAY;
+ if ( ! (mi->scanmode & MI_HASMODE_COLOR ) )
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ }
+
+ if ( mi->scanmode & MI_HASMODE_HALFTONE )
+ {
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_HALFTONE;
+ if ( ! (mi->scanmode & MI_HASMODE_COLOR )
+ && ! (mi->scanmode & MI_HASMODE_GRAY ) )
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ }
+
+ /* Always enable a lineart mode. Some models (X6, FW 1.40) say */
+ /* that they have no lineart mode. In this case we will do a grayscale */
+ /* scan and convert it to onebit data */
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_LINEART;
+ if ( ! (mi->scanmode & MI_HASMODE_COLOR )
+ && ! (mi->scanmode & MI_HASMODE_GRAY )
+ && ! (mi->scanmode & MI_HASMODE_HALFTONE ) )
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ md->scanmode_list[i] = NULL;
+
+ /* bitdepth */
+ i = 0;
+
+#if 0
+ if ( mi->depth & MI_HASDEPTH_NIBBLE )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_4;
+#endif
+
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_8;
+ if ( mi->depth & MI_HASDEPTH_10 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_10;
+ if ( mi->depth & MI_HASDEPTH_12 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_12;
+ if ( mi->depth & MI_HASDEPTH_14 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_14;
+ if ( mi->depth & MI_HASDEPTH_16 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_16;
+
+ md->bitdepth_list[0] = i;
+ if ( md->bitdepth_list[1] == (SANE_Int) MD_DEPTHVAL_8 )
+ val[OPT_BITDEPTH].w = md->bitdepth_list[1];
+ else
+ val[OPT_BITDEPTH].w = md->bitdepth_list[2];
+
+ /* Halftone */
+ md->halftone_mode_list[0] = (SANE_String) MD_HALFTONE0;
+ md->halftone_mode_list[1] = (SANE_String) MD_HALFTONE1;
+ md->halftone_mode_list[2] = (SANE_String) MD_HALFTONE2;
+ md->halftone_mode_list[3] = (SANE_String) MD_HALFTONE3;
+ md->halftone_mode_list[4] = (SANE_String) MD_HALFTONE4;
+ md->halftone_mode_list[5] = (SANE_String) MD_HALFTONE5;
+ md->halftone_mode_list[6] = (SANE_String) MD_HALFTONE6;
+ md->halftone_mode_list[7] = (SANE_String) MD_HALFTONE7;
+ md->halftone_mode_list[8] = (SANE_String) MD_HALFTONE8;
+ md->halftone_mode_list[9] = (SANE_String) MD_HALFTONE9;
+ md->halftone_mode_list[10] = (SANE_String) MD_HALFTONE10;
+ md->halftone_mode_list[11] = (SANE_String) MD_HALFTONE11;
+ md->halftone_mode_list[12] = NULL;
+ if ( val[OPT_HALFTONE].s )
+ free((void *) val[OPT_HALFTONE].s);
+ val[OPT_HALFTONE].s = strdup(md->halftone_mode_list[0]);
+
+ /* Resolution */
+ md->x_res_range_dpi.min = SANE_FIX(10.0);
+ md->x_res_range_dpi.max = SANE_FIX(mi->max_xresolution);
+ md->x_res_range_dpi.quant = SANE_FIX(1.0);
+ val[OPT_RESOLUTION].w = MIN(MD_RESOLUTION_DEFAULT, md->x_res_range_dpi.max);
+
+ md->y_res_range_dpi.min = SANE_FIX(10.0);
+ md->y_res_range_dpi.max = SANE_FIX(mi->max_yresolution);
+ md->y_res_range_dpi.quant = SANE_FIX(1.0);
+ val[OPT_Y_RESOLUTION].w = val[OPT_RESOLUTION].w; /* bind is default */
+
+ /* Preview mode */
+ val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Geometry */
+ md->x_range_mm.min = SANE_FIX(0.0);
+ md->x_range_mm.max = SANE_FIX((double) mi->geo_width
+ / (double) mi->opt_resolution
+ * MM_PER_INCH);
+ md->x_range_mm.quant = SANE_FIX(0.0);
+ md->y_range_mm.min = SANE_FIX(0.0);
+ md->y_range_mm.max = SANE_FIX((double) mi->geo_height
+ / (double) mi->opt_resolution
+ * MM_PER_INCH);
+ md->y_range_mm.quant = SANE_FIX(0.0);
+ val[OPT_TL_X].w = SANE_FIX(0.0);
+ val[OPT_TL_Y].w = SANE_FIX(0.0);
+ val[OPT_BR_X].w = md->x_range_mm.max;
+ val[OPT_BR_Y].w = md->y_range_mm.max;
+
+ /* Enhancement group */
+ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT;
+ val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT;
+ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT;
+
+ /* Gamma */
+ /* linear gamma must come first */
+ i = 0;
+ md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_LINEAR;
+ md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_SCALAR;
+ md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_CUSTOM;
+ if ( val[OPT_GAMMA_MODE].s )
+ free((void *) val[OPT_GAMMA_MODE].s);
+ val[OPT_GAMMA_MODE].s = strdup(md->gammamode_list[0]);
+
+ md->gammamode_list[i] = NULL;
+
+ /* bind gamma */
+ val[OPT_GAMMA_BIND].w = SANE_TRUE;
+ val[OPT_GAMMA_SCALAR].w = MD_GAMMA_DEFAULT;
+ val[OPT_GAMMA_SCALAR_R].w = MD_GAMMA_DEFAULT;
+ val[OPT_GAMMA_SCALAR_G].w = MD_GAMMA_DEFAULT;
+ val[OPT_GAMMA_SCALAR_B].w = MD_GAMMA_DEFAULT;
+
+ /* If the device supports gamma tables, we allocate memory according */
+ /* to lookup table capabilities, otherwise we allocate 4096 elements */
+ /* which is sufficient for a color depth of 12. If the device */
+ /* does not support gamma tables, we fill the table according to */
+ /* the actual bit depth, i.e. 256 entries with a range of 0..255 */
+ /* if the actual bit depth is 8, for example. This will hopefully*/
+ /* make no trouble if the bit depth is 1. */
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ tablesize = 4096;
+ option_size = (int) pow(2.0, (double) val[OPT_BITDEPTH].w );
+ max_gamma_value = option_size - 1;
+ }
+ else
+ {
+ tablesize = md->max_lut_size;
+ option_size = tablesize;
+ max_gamma_value = md->max_lut_size - 1;
+ }
+
+ for ( color = 0; color < 4; color++ )
+ {
+ /* index 0 is used if bind gamma == true, index 1 to 3 */
+ /* if bind gamma == false */
+ if ( md->custom_gamma_table[color] )
+ free((void *) md->custom_gamma_table[color]);
+ md->custom_gamma_table[color] =
+ (SANE_Int *) malloc(tablesize * sizeof(SANE_Int));
+ DBG(100, "init_options: md->custom_gamma_table[%d]=%p, malloc'd %lu bytes\n",
+ color, (void *) md->custom_gamma_table[color], (u_long) (tablesize * sizeof(SANE_Int)));
+ if ( md->custom_gamma_table[color] == NULL )
+ {
+ DBG(1, "init_options: malloc for custom gamma table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for ( i = 0; i < max_gamma_value; i++ )
+ md->custom_gamma_table[color][i] = i;
+ }
+
+ md->custom_gamma_range.min = 0;
+ md->custom_gamma_range.max = max_gamma_value;
+ md->custom_gamma_range.quant = 1;
+
+ sod[OPT_GAMMA_CUSTOM].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_R].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_G].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_B].size = option_size * sizeof (SANE_Int);
+
+ val[OPT_GAMMA_CUSTOM].wa = &md->custom_gamma_table[0][0];
+ val[OPT_GAMMA_CUSTOM_R].wa = &md->custom_gamma_table[1][0];
+ val[OPT_GAMMA_CUSTOM_G].wa = &md->custom_gamma_table[2][0];
+ val[OPT_GAMMA_CUSTOM_B].wa = &md->custom_gamma_table[3][0];
+
+ /* Shadow, midtone, highlight, exposure time */
+ md->channel_list[0] = (SANE_String) MD_CHANNEL_MASTER;
+ md->channel_list[1] = (SANE_String) MD_CHANNEL_RED;
+ md->channel_list[2] = (SANE_String) MD_CHANNEL_GREEN;
+ md->channel_list[3] = (SANE_String) MD_CHANNEL_BLUE;
+ md->channel_list[4] = NULL;
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup(md->channel_list[0]);
+ val[OPT_SHADOW].w = MD_SHADOW_DEFAULT;
+ val[OPT_SHADOW_R].w = MD_SHADOW_DEFAULT;
+ val[OPT_SHADOW_G].w = MD_SHADOW_DEFAULT;
+ val[OPT_SHADOW_B].w = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT;
+ val[OPT_MIDTONE_R].w = MD_MIDTONE_DEFAULT;
+ val[OPT_MIDTONE_G].w = MD_MIDTONE_DEFAULT;
+ val[OPT_MIDTONE_B].w = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_HIGHLIGHT_R].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_HIGHLIGHT_G].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_HIGHLIGHT_B].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_EXPOSURE_R].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_EXPOSURE_G].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_EXPOSURE_B].w = MD_EXPOSURE_DEFAULT;
+
+ /* special options */
+ val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
+
+ /* enable/disable option for backtracking */
+ val[OPT_DISABLE_BACKTRACK].w = md->opt_no_backtrack_default;
+
+ /* enable/disable calibration by backend */
+ val[OPT_CALIB_BACKEND].w = md->opt_backend_calib_default;
+
+ /* turn off the lamp during a scan */
+ val[OPT_LIGHTLID35].w = SANE_FALSE;
+
+ /* auto adjustment of threshold during a lineart scan */
+ val[OPT_AUTOADJUST].w = SANE_FALSE;
+
+ /* color balance (100% means no correction) */
+ val[OPT_BALANCE_R].w = SANE_FIX(100);
+ val[OPT_BALANCE_G].w = SANE_FIX(100);
+ val[OPT_BALANCE_B].w = SANE_FIX(100);
+
+ if ( first_call )
+ {
+ /* initialize option descriptors and ranges */
+
+ /* Percentage range for brightness, contrast */
+ md->percentage_range.min = 0 << SANE_FIXED_SCALE_SHIFT;
+ md->percentage_range.max = 200 << SANE_FIXED_SCALE_SHIFT;
+ md->percentage_range.quant = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ md->threshold_range.min = 1;
+ md->threshold_range.max = 255;
+ md->threshold_range.quant = 1;
+
+ md->scalar_gamma_range.min = SANE_FIX(0.1);
+ md->scalar_gamma_range.max = SANE_FIX(4.0);
+ md->scalar_gamma_range.quant = SANE_FIX(0.1);
+
+ md->shadow_range.min = 0;
+ md->shadow_range.max = 253;
+ md->shadow_range.quant = 1;
+
+ md->midtone_range.min = 1;
+ md->midtone_range.max = 254;
+ md->midtone_range.quant = 1;
+
+ md->highlight_range.min = 2;
+ md->highlight_range.max = 255;
+ md->highlight_range.quant = 1;
+
+ md->exposure_range.min = 0;
+ md->exposure_range.max = 510;
+ md->exposure_range.quant = 2;
+
+ md->balance_range.min = 0;
+ md->balance_range.max = 200 << SANE_FIXED_SCALE_SHIFT;
+ md->balance_range.quant = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ /* default for most options */
+ for ( i = 0; i < NUM_OPTIONS; i++ )
+ {
+ sod[i].type = SANE_TYPE_FIXED;
+ sod[i].unit = SANE_UNIT_NONE;
+ sod[i].size = sizeof(SANE_Fixed);
+ sod[i].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ sod[i].constraint_type = SANE_CONSTRAINT_RANGE;
+ }
+
+ sod[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ sod[OPT_NUM_OPTS].size = sizeof (SANE_Int);
+ sod[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ sod[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* NUM_OPTIONS is no option */
+ DBG(255, "sod=%p\n", (void *) sod);
+ DBG(255, "OPT_NUM_OPTS=%d\n", OPT_NUM_OPTS);
+ DBG(255, "SANE_CAP_SOFT_DETECT=%d\n", SANE_CAP_SOFT_DETECT);
+ DBG(255, "OPT_NUM_OPTS.cap=%d\n", sod[0].cap);
+
+ /* The Scan Mode Group */
+ sod[OPT_MODE_GROUP].title = M_TITLE_SCANMODEGRP;
+ sod[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_MODE_GROUP].size = 0;
+ sod[OPT_MODE_GROUP].desc = "";
+ sod[OPT_MODE_GROUP].cap = 0;
+ sod[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scan source */
+ sod[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ sod[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ sod[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ sod[OPT_SOURCE].type = SANE_TYPE_STRING;
+ sod[OPT_SOURCE].size = max_string_size(md->scansource_list);
+ /* if there is only one scan source, deactivate option */
+ if ( md->scansource_list[1] == NULL )
+ sod[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_SOURCE].constraint.string_list = md->scansource_list;
+
+ /* Scan mode */
+ sod[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ sod[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ sod[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ sod[OPT_MODE].type = SANE_TYPE_STRING;
+ sod[OPT_MODE].size = max_string_size(md->scanmode_list);
+ sod[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_MODE].constraint.string_list = md->scanmode_list;
+
+ /* Bit depth */
+ sod[OPT_BITDEPTH].name = SANE_NAME_BIT_DEPTH;
+ sod[OPT_BITDEPTH].title = SANE_TITLE_BIT_DEPTH;
+ sod[OPT_BITDEPTH].desc = SANE_DESC_BIT_DEPTH;
+ sod[OPT_BITDEPTH].type = SANE_TYPE_INT;
+ sod[OPT_BITDEPTH].unit = SANE_UNIT_BIT;
+ sod[OPT_BITDEPTH].size = sizeof(SANE_Int);
+ /* if we have only 8 bit color deactivate this option */
+ if ( md->bitdepth_list[0] == 1 )
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BITDEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ sod[OPT_BITDEPTH].constraint.word_list = md->bitdepth_list;
+
+ /* Halftone */
+ sod[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
+ sod[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ sod[OPT_HALFTONE].desc = SANE_DESC_HALFTONE;
+ sod[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ sod[OPT_HALFTONE].size = max_string_size(md->halftone_mode_list);
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_HALFTONE].constraint.string_list = md->halftone_mode_list;
+
+ /* Resolution */
+ sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ sod[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ sod[OPT_RESOLUTION].constraint.range = &md->x_res_range_dpi;
+
+ sod[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ sod[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ sod[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ sod[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_Y_RESOLUTION].constraint.range = &md->y_res_range_dpi;
+
+ /* Preview */
+ sod[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ sod[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ sod[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ sod[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ sod[OPT_PREVIEW].size = sizeof(SANE_Bool);
+ sod[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Geometry group, for scan area selection */
+ sod[OPT_GEOMETRY_GROUP].title = M_TITLE_GEOMGRP;
+ sod[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_GEOMETRY_GROUP].size = 0;
+ sod[OPT_GEOMETRY_GROUP].desc = "";
+ sod[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ sod[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ sod[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ sod[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ sod[OPT_TL_X].unit = SANE_UNIT_MM;
+ sod[OPT_TL_X].constraint.range = &md->x_range_mm;
+
+ sod[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ sod[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ sod[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ sod[OPT_TL_Y].unit = SANE_UNIT_MM;
+ sod[OPT_TL_Y].constraint.range = &md->y_range_mm;
+
+ sod[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ sod[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ sod[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ sod[OPT_BR_X].unit = SANE_UNIT_MM;
+ sod[OPT_BR_X].constraint.range = &md->x_range_mm;
+
+ sod[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ sod[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ sod[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ sod[OPT_BR_Y].unit = SANE_UNIT_MM;
+ sod[OPT_BR_Y].constraint.range = &md->y_range_mm;
+
+ /* Enhancement group */
+ sod[OPT_ENHANCEMENT_GROUP].title = M_TITLE_ENHANCEGRP;
+ sod[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_ENHANCEMENT_GROUP].desc = "";
+ sod[OPT_ENHANCEMENT_GROUP].size = 0;
+ sod[OPT_ENHANCEMENT_GROUP].cap = 0;
+ sod[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BRIGHTNESS].constraint.range = &md->percentage_range;
+
+ sod[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ sod[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ sod[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ sod[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ sod[OPT_CONTRAST].constraint.range = &md->percentage_range;
+
+ sod[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ sod[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ sod[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ sod[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ sod[OPT_THRESHOLD].size = sizeof(SANE_Int);
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].constraint.range = &md->threshold_range;
+
+ /* automatically adjust threshold for a lineart scan */
+ sod[OPT_AUTOADJUST].name = M_NAME_AUTOADJUST;
+ sod[OPT_AUTOADJUST].title = M_TITLE_AUTOADJUST;
+ sod[OPT_AUTOADJUST].desc = M_DESC_AUTOADJUST;
+ sod[OPT_AUTOADJUST].type = SANE_TYPE_BOOL;
+ sod[OPT_AUTOADJUST].size = sizeof(SANE_Bool);
+ sod[OPT_AUTOADJUST].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.auto_adjust, "off", 3) == 0 )
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+
+ /* Gamma */
+ sod[OPT_GAMMA_GROUP].title = "Gamma";
+ sod[OPT_GAMMA_GROUP].desc = "";
+ sod[OPT_GAMMA_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_GAMMA_GROUP].size = 0;
+ sod[OPT_GAMMA_GROUP].cap = 0;
+ sod[OPT_GAMMA_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_GAMMA_MODE].name = M_NAME_GAMMA_MODE;
+ sod[OPT_GAMMA_MODE].title = M_TITLE_GAMMA_MODE;
+ sod[OPT_GAMMA_MODE].desc = M_DESC_GAMMA_MODE;
+ sod[OPT_GAMMA_MODE].type = SANE_TYPE_STRING;
+ sod[OPT_GAMMA_MODE].size = max_string_size(md->gammamode_list);
+ sod[OPT_GAMMA_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_GAMMA_MODE].constraint.string_list = md->gammamode_list;
+
+ sod[OPT_GAMMA_BIND].name = M_NAME_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].title = M_TITLE_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].desc = M_DESC_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ sod[OPT_GAMMA_BIND].size = sizeof(SANE_Bool);
+ sod[OPT_GAMMA_BIND].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* this is active if gamma_bind == true and gammamode == scalar */
+ sod[OPT_GAMMA_SCALAR].name = M_NAME_GAMMA_SCALAR;
+ sod[OPT_GAMMA_SCALAR].title = M_TITLE_GAMMA_SCALAR;
+ sod[OPT_GAMMA_SCALAR].desc = M_DESC_GAMMA_SCALAR;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_SCALAR_R].name = M_NAME_GAMMA_SCALAR_R;
+ sod[OPT_GAMMA_SCALAR_R].title = M_TITLE_GAMMA_SCALAR_R;
+ sod[OPT_GAMMA_SCALAR_R].desc = M_DESC_GAMMA_SCALAR_R;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_SCALAR_G].name = M_NAME_GAMMA_SCALAR_G;
+ sod[OPT_GAMMA_SCALAR_G].title = M_TITLE_GAMMA_SCALAR_G;
+ sod[OPT_GAMMA_SCALAR_G].desc = M_DESC_GAMMA_SCALAR_G;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_SCALAR_B].name = M_NAME_GAMMA_SCALAR_B;
+ sod[OPT_GAMMA_SCALAR_B].title = M_TITLE_GAMMA_SCALAR_B;
+ sod[OPT_GAMMA_SCALAR_B].desc = M_DESC_GAMMA_SCALAR_B;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM].name = SANE_NAME_GAMMA_VECTOR;
+ sod[OPT_GAMMA_CUSTOM].title = SANE_TITLE_GAMMA_VECTOR;
+ sod[OPT_GAMMA_CUSTOM].desc = SANE_DESC_GAMMA_VECTOR;
+ sod[OPT_GAMMA_CUSTOM].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM].constraint.range = &md->custom_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_CUSTOM_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_CUSTOM_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_CUSTOM_R].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_R].constraint.range = &md->custom_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_CUSTOM_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_CUSTOM_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_CUSTOM_G].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_G].constraint.range = &md->custom_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_CUSTOM_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_CUSTOM_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_CUSTOM_B].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_B].constraint.range = &md->custom_gamma_range;
+
+ /* Shadow, midtone, highlight */
+ sod[OPT_SMH_GROUP].title = M_TITLE_SMHGRP;
+ sod[OPT_SMH_GROUP].desc = "";
+ sod[OPT_SMH_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_SMH_GROUP].size = 0;
+ sod[OPT_SMH_GROUP].cap = 0;
+ sod[OPT_SMH_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_CHANNEL].name = M_NAME_CHANNEL;
+ sod[OPT_CHANNEL].title = M_TITLE_CHANNEL;
+ sod[OPT_CHANNEL].desc = M_DESC_CHANNEL;
+ sod[OPT_CHANNEL].type = SANE_TYPE_STRING;
+ sod[OPT_CHANNEL].size = max_string_size(md->channel_list);
+ sod[OPT_CHANNEL].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_CHANNEL].constraint.string_list = md->channel_list;
+
+ sod[OPT_SHADOW].name = SANE_NAME_SHADOW;
+ sod[OPT_SHADOW].title = SANE_TITLE_SHADOW;
+ sod[OPT_SHADOW].desc = SANE_DESC_SHADOW;
+ sod[OPT_SHADOW].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW].constraint.range = &md->shadow_range;
+
+ sod[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R;
+ sod[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R;
+ sod[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R;
+ sod[OPT_SHADOW_R].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW_R].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW_R].constraint.range = &md->shadow_range;
+
+ sod[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G;
+ sod[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G;
+ sod[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G;
+ sod[OPT_SHADOW_G].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW_G].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW_G].constraint.range = &md->shadow_range;
+
+ sod[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B;
+ sod[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B;
+ sod[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B;
+ sod[OPT_SHADOW_B].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW_B].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW_B].constraint.range = &md->shadow_range;
+
+ sod[OPT_MIDTONE].name = M_NAME_MIDTONE;
+ sod[OPT_MIDTONE].title = M_TITLE_MIDTONE;
+ sod[OPT_MIDTONE].desc = M_DESC_MIDTONE;
+ sod[OPT_MIDTONE].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE].constraint.range = &md->midtone_range;
+
+ sod[OPT_MIDTONE_R].name = M_NAME_MIDTONE_R;
+ sod[OPT_MIDTONE_R].title = M_TITLE_MIDTONE_R;
+ sod[OPT_MIDTONE_R].desc = M_DESC_MIDTONE_R;
+ sod[OPT_MIDTONE_R].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE_R].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE_R].constraint.range = &md->midtone_range;
+
+ sod[OPT_MIDTONE_G].name = M_NAME_MIDTONE_G;
+ sod[OPT_MIDTONE_G].title = M_TITLE_MIDTONE_G;
+ sod[OPT_MIDTONE_G].desc = M_DESC_MIDTONE_G;
+ sod[OPT_MIDTONE_G].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE_G].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE_G].constraint.range = &md->midtone_range;
+
+ sod[OPT_MIDTONE_B].name = M_NAME_MIDTONE_B;
+ sod[OPT_MIDTONE_B].title = M_TITLE_MIDTONE_B;
+ sod[OPT_MIDTONE_B].desc = M_DESC_MIDTONE_B;
+ sod[OPT_MIDTONE_B].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE_B].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE_B].constraint.range = &md->midtone_range;
+
+ sod[OPT_HIGHLIGHT].name = SANE_NAME_HIGHLIGHT;
+ sod[OPT_HIGHLIGHT].title = SANE_TITLE_HIGHLIGHT;
+ sod[OPT_HIGHLIGHT].desc = SANE_DESC_HIGHLIGHT;
+ sod[OPT_HIGHLIGHT].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT].constraint.range = &md->highlight_range;
+
+ sod[OPT_HIGHLIGHT_R].name = SANE_NAME_HIGHLIGHT_R;
+ sod[OPT_HIGHLIGHT_R].title = SANE_TITLE_HIGHLIGHT_R;
+ sod[OPT_HIGHLIGHT_R].desc = SANE_DESC_HIGHLIGHT_R;
+ sod[OPT_HIGHLIGHT_R].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT_R].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT_R].constraint.range = &md->highlight_range;
+
+ sod[OPT_HIGHLIGHT_G].name = SANE_NAME_HIGHLIGHT_G;
+ sod[OPT_HIGHLIGHT_G].title = SANE_TITLE_HIGHLIGHT_G;
+ sod[OPT_HIGHLIGHT_G].desc = SANE_DESC_HIGHLIGHT_G;
+ sod[OPT_HIGHLIGHT_G].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT_G].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT_G].constraint.range = &md->highlight_range;
+
+ sod[OPT_HIGHLIGHT_B].name = SANE_NAME_HIGHLIGHT_B;
+ sod[OPT_HIGHLIGHT_B].title = SANE_TITLE_HIGHLIGHT_B;
+ sod[OPT_HIGHLIGHT_B].desc = SANE_DESC_HIGHLIGHT_B;
+ sod[OPT_HIGHLIGHT_B].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT_B].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT_B].constraint.range = &md->highlight_range;
+
+ sod[OPT_EXPOSURE].name = SANE_NAME_SCAN_EXPOS_TIME;
+ sod[OPT_EXPOSURE].title = SANE_TITLE_SCAN_EXPOS_TIME;
+ sod[OPT_EXPOSURE].desc = SANE_DESC_SCAN_EXPOS_TIME;
+ sod[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE].constraint.range = &md->exposure_range;
+
+ sod[OPT_EXPOSURE_R].name = SANE_NAME_SCAN_EXPOS_TIME_R;
+ sod[OPT_EXPOSURE_R].title = SANE_TITLE_SCAN_EXPOS_TIME_R;
+ sod[OPT_EXPOSURE_R].desc = SANE_DESC_SCAN_EXPOS_TIME_R;
+ sod[OPT_EXPOSURE_R].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE_R].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE_R].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE_R].constraint.range = &md->exposure_range;
+
+ sod[OPT_EXPOSURE_G].name = SANE_NAME_SCAN_EXPOS_TIME_G;
+ sod[OPT_EXPOSURE_G].title = SANE_TITLE_SCAN_EXPOS_TIME_G;
+ sod[OPT_EXPOSURE_G].desc = SANE_DESC_SCAN_EXPOS_TIME_G;
+ sod[OPT_EXPOSURE_G].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE_G].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE_G].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE_G].constraint.range = &md->exposure_range;
+
+ sod[OPT_EXPOSURE_B].name = SANE_NAME_SCAN_EXPOS_TIME_B;
+ sod[OPT_EXPOSURE_B].title = SANE_TITLE_SCAN_EXPOS_TIME_B;
+ sod[OPT_EXPOSURE_B].desc = SANE_DESC_SCAN_EXPOS_TIME_B;
+ sod[OPT_EXPOSURE_B].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE_B].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE_B].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE_B].constraint.range = &md->exposure_range;
+
+ /* The Special Options Group */
+ sod[OPT_SPECIAL].title = M_TITLE_SPECIALGRP;
+ sod[OPT_SPECIAL].type = SANE_TYPE_GROUP;
+ sod[OPT_SPECIAL].size = 0;
+ sod[OPT_SPECIAL].desc = "";
+ sod[OPT_SPECIAL].cap = SANE_CAP_ADVANCED;
+ sod[OPT_SPECIAL].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ sod[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ sod[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ sod[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ sod[OPT_RESOLUTION_BIND].size = sizeof(SANE_Bool);
+ sod[OPT_RESOLUTION_BIND].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_RESOLUTION_BIND].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* enable/disable option for backtracking */
+ sod[OPT_DISABLE_BACKTRACK].name = M_NAME_NOBACKTRACK;
+ sod[OPT_DISABLE_BACKTRACK].title = M_TITLE_NOBACKTRACK;
+ sod[OPT_DISABLE_BACKTRACK].desc = M_DESC_NOBACKTRACK;
+ sod[OPT_DISABLE_BACKTRACK].type = SANE_TYPE_BOOL;
+ sod[OPT_DISABLE_BACKTRACK].size = sizeof(SANE_Bool);
+ sod[OPT_DISABLE_BACKTRACK].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_DISABLE_BACKTRACK].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.no_backtracking, "off", 3) == 0 )
+ sod[OPT_DISABLE_BACKTRACK].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration by driver */
+ sod[OPT_CALIB_BACKEND].name = M_NAME_CALIBBACKEND;
+ sod[OPT_CALIB_BACKEND].title = M_TITLE_CALIBBACKEND;
+ sod[OPT_CALIB_BACKEND].desc = M_DESC_CALIBBACKEND;
+ sod[OPT_CALIB_BACKEND].type = SANE_TYPE_BOOL;
+ sod[OPT_CALIB_BACKEND].size = sizeof(SANE_Bool);
+ sod[OPT_CALIB_BACKEND].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_CALIB_BACKEND].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.backend_calibration, "off", 3) == 0 )
+ sod[OPT_CALIB_BACKEND].cap |= SANE_CAP_INACTIVE;
+
+ /* turn off the lamp of the flatbed during a scan */
+ sod[OPT_LIGHTLID35].name = M_NAME_LIGHTLID35;
+ sod[OPT_LIGHTLID35].title = M_TITLE_LIGHTLID35;
+ sod[OPT_LIGHTLID35].desc = M_DESC_LIGHTLID35;
+ sod[OPT_LIGHTLID35].type = SANE_TYPE_BOOL;
+ sod[OPT_LIGHTLID35].size = sizeof(SANE_Bool);
+ sod[OPT_LIGHTLID35].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_LIGHTLID35].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.lightlid35, "off", 3) == 0 )
+ sod[OPT_LIGHTLID35].cap |= SANE_CAP_INACTIVE;
+
+ /* toggle the lamp of the flatbed */
+ sod[OPT_TOGGLELAMP].name = M_NAME_TOGGLELAMP;
+ sod[OPT_TOGGLELAMP].title = M_TITLE_TOGGLELAMP;
+ sod[OPT_TOGGLELAMP].desc = M_DESC_TOGGLELAMP;
+ sod[OPT_TOGGLELAMP].type = SANE_TYPE_BUTTON;
+ sod[OPT_TOGGLELAMP].size = 0;
+ sod[OPT_TOGGLELAMP].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_TOGGLELAMP].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.toggle_lamp, "off", 3) == 0 )
+ sod[OPT_TOGGLELAMP].cap |= SANE_CAP_INACTIVE;
+
+ /* color balance */
+ sod[OPT_COLORBALANCE].title = M_TITLE_COLBALANCEGRP;
+ sod[OPT_COLORBALANCE].type = SANE_TYPE_GROUP;
+ sod[OPT_COLORBALANCE].size = 0;
+ sod[OPT_COLORBALANCE].desc = "";
+ sod[OPT_COLORBALANCE].cap = SANE_CAP_ADVANCED;
+ sod[OPT_COLORBALANCE].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_BALANCE_R].name = M_NAME_BALANCE_R;
+ sod[OPT_BALANCE_R].title = M_TITLE_BALANCE_R;
+ sod[OPT_BALANCE_R].desc = M_DESC_BALANCE_R;
+ sod[OPT_BALANCE_R].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_R].constraint.range = &md->balance_range;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+
+ sod[OPT_BALANCE_G].name = M_NAME_BALANCE_G;
+ sod[OPT_BALANCE_G].title = M_TITLE_BALANCE_G;
+ sod[OPT_BALANCE_G].desc = M_DESC_BALANCE_G;
+ sod[OPT_BALANCE_G].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_G].constraint.range = &md->balance_range;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+
+ sod[OPT_BALANCE_B].name = M_NAME_BALANCE_B;
+ sod[OPT_BALANCE_B].title = M_TITLE_BALANCE_B;
+ sod[OPT_BALANCE_B].desc = M_DESC_BALANCE_B;
+ sod[OPT_BALANCE_B].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_B].constraint.range = &md->balance_range;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+
+ sod[OPT_BALANCE_FW].name = M_NAME_BALANCE_FW;
+ sod[OPT_BALANCE_FW].title = M_TITLE_BALANCE_FW;
+ sod[OPT_BALANCE_FW].desc = M_DESC_BALANCE_FW;
+ sod[OPT_BALANCE_FW].type = SANE_TYPE_BUTTON;
+ sod[OPT_BALANCE_FW].size = 0;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_FW].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+ }
+
+ status = set_option_dependencies(ms, sod, val);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- set_option_dependencies() ---------------------------------------*/
+
+static SANE_Status
+set_option_dependencies(Microtek2_Scanner *ms, SANE_Option_Descriptor *sod,
+ Option_Value *val)
+{
+
+ Microtek2_Device *md;
+ md = ms->dev;
+
+ DBG(40, "set_option_dependencies: val=%p, sod=%p, mode=%s\n",
+ (void *) val, (void *) sod, val[OPT_MODE].s);
+
+ if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 )
+ {
+ /* activate brightness,..., deactivate halftone pattern */
+ /* and threshold */
+ sod[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ if ( md->bitdepth_list[0] != 1 )
+ sod[OPT_BITDEPTH].cap &= ~SANE_CAP_INACTIVE;
+ else
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+ if ( ! ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) )
+ {
+ sod[OPT_BALANCE_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap &= ~SANE_CAP_INACTIVE;
+ }
+ /* reset options values that are inactive to their default */
+ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT;
+ }
+
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ {
+ sod[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ if ( md->bitdepth_list[0] != 1 )
+ sod[OPT_BITDEPTH].cap &= ~SANE_CAP_INACTIVE;
+ else
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER);
+ }
+
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 )
+ {
+ sod[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT;
+ val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT;
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER);
+ val[OPT_SHADOW].w = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT;
+ }
+
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 )
+ {
+ sod[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ if ( val[OPT_AUTOADJUST].w == SANE_FALSE )
+ sod[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ else
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT;
+ val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT;
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER);
+ val[OPT_SHADOW].w = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT;
+ }
+
+ else
+ {
+ DBG(1, "set_option_dependencies: unknown mode '%s'\n",
+ val[OPT_MODE].s );
+ return SANE_STATUS_INVAL;
+ }
+
+ /* these ones are always inactive if the mode changes */
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ val[OPT_SHADOW_R].w = val[OPT_SHADOW_G].w = val[OPT_SHADOW_B].w
+ = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE_R].w = val[OPT_MIDTONE_G].w = val[OPT_MIDTONE_B].w
+ = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT_R].w = val[OPT_HIGHLIGHT_G].w = val[OPT_HIGHLIGHT_B].w
+ = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE_R].w = val[OPT_EXPOSURE_G].w = val[OPT_EXPOSURE_B].w
+ = MD_EXPOSURE_DEFAULT;
+
+ if ( SANE_OPTION_IS_SETTABLE(sod[OPT_GAMMA_MODE].cap) )
+ {
+ restore_gamma_options(sod, val);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- sane_control_option() -------------------------------------------*/
+
+SANE_Status
+sane_control_option(SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int *info)
+{
+ Microtek2_Scanner *ms = handle;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ Option_Value *val;
+ SANE_Option_Descriptor *sod;
+ SANE_Status status;
+
+ md = ms->dev;
+ val = &ms->val[0];
+ sod = &ms->sod[0];
+ mi = &md->info[md->scan_source];
+
+ if ( ms->scanning )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if ( option < 0 || option >= NUM_OPTIONS )
+ {
+ DBG(100, "sane_control_option: option %d; action %d \n", option, action);
+ DBG(10, "sane_control_option: option %d invalid\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ( ! SANE_OPTION_IS_ACTIVE(ms->sod[option].cap) )
+ {
+ DBG(100, "sane_control_option: option %d; action %d \n", option, action);
+ DBG(10, "sane_control_option: option %d not active\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ( info )
+ *info = 0;
+
+ switch ( action )
+ {
+ case SANE_ACTION_GET_VALUE: /* read out option values */
+ switch ( option )
+ {
+ /* word options */
+ case OPT_BITDEPTH:
+ case OPT_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_THRESHOLD:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ case OPT_MIDTONE:
+ case OPT_MIDTONE_R:
+ case OPT_MIDTONE_G:
+ case OPT_MIDTONE_B:
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ case OPT_EXPOSURE:
+ case OPT_EXPOSURE_R:
+ case OPT_EXPOSURE_G:
+ case OPT_EXPOSURE_B:
+ case OPT_GAMMA_SCALAR:
+ case OPT_GAMMA_SCALAR_R:
+ case OPT_GAMMA_SCALAR_G:
+ case OPT_GAMMA_SCALAR_B:
+ case OPT_BALANCE_R:
+ case OPT_BALANCE_G:
+ case OPT_BALANCE_B:
+
+ *(SANE_Word *) value = val[option].w;
+
+ if (sod[option].type == SANE_TYPE_FIXED )
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%f\n",
+ option, action, SANE_UNFIX(val[option].w));
+ else
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%d\n",
+ option, action, val[option].w);
+
+ return SANE_STATUS_GOOD;
+
+ /* boolean options */
+ case OPT_RESOLUTION_BIND:
+ case OPT_DISABLE_BACKTRACK:
+ case OPT_CALIB_BACKEND:
+ case OPT_LIGHTLID35:
+ case OPT_GAMMA_BIND:
+ case OPT_AUTOADJUST:
+ *(SANE_Bool *) value = val[option].w;
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%d\n",
+ option, action, val[option].w);
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_SOURCE:
+ case OPT_MODE:
+ case OPT_HALFTONE:
+ case OPT_CHANNEL:
+ case OPT_GAMMA_MODE:
+ strcpy(value, val[option].s);
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%s\n",
+ option, action, val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* word array options */
+ case OPT_GAMMA_CUSTOM:
+ case OPT_GAMMA_CUSTOM_R:
+ case OPT_GAMMA_CUSTOM_G:
+ case OPT_GAMMA_CUSTOM_B:
+ memcpy(value, val[option].wa, sod[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* button options */
+ case OPT_TOGGLELAMP:
+ case OPT_BALANCE_FW:
+ return SANE_STATUS_GOOD;
+
+ /* others */
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) value = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ /* NOTREACHED */
+ /* break; */
+
+ case SANE_ACTION_SET_VALUE: /* set option values */
+ if ( ! SANE_OPTION_IS_SETTABLE(sod[option].cap) )
+ {
+ DBG(100, "sane_control_option: option %d; action %d \n",
+ option, action);
+ DBG(10, "sane_control_option: trying to set unsettable option\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* do not check OPT_BR_Y, xscanimage sometimes tries to set */
+ /* it to a too large value; bug in xscanimage ? */
+ /* if ( option != OPT_BR_Y )
+ { */
+ status = sanei_constrain_value(ms->sod + option, value, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(10, "sane_control_option: invalid option value\n");
+ return status;
+ }
+ /* } */
+
+ switch ( sod[option].type )
+ {
+ case SANE_TYPE_BOOL:
+ DBG(50, "sane_control_option: option=%d, action=%d, value=%d\n",
+ option, action, *(SANE_Int *) value);
+ if ( ! ( ( *(SANE_Bool *) value == SANE_TRUE )
+ || ( *(SANE_Bool *) value == SANE_FALSE ) ) )
+ {
+ DBG(10, "sane_control_option: invalid BOOL option value\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ( val[option].w == *(SANE_Bool *) value ) /* no change */
+ return SANE_STATUS_GOOD;
+ val[option].w = *(SANE_Bool *) value;
+ break;
+
+ case SANE_TYPE_INT:
+ if ( sod[option].size == sizeof(SANE_Int) )
+ {
+ /* word option */
+ DBG(50, "sane_control_option: option=%d, action=%d, "
+ "value=%d\n", option, action, *(SANE_Int *) value);
+ if ( val[option].w == *(SANE_Int *) value ) /* no change */
+ return SANE_STATUS_GOOD;
+ val[option].w = *(SANE_Int *) value;
+ }
+ else
+ {
+ /* word array option */
+ memcpy(val[option].wa, value, sod[option].size);
+ }
+ break;
+
+ case SANE_TYPE_FIXED:
+ DBG(50, "sane_control_option: option=%d, action=%d, value=%f\n",
+ option, action, SANE_UNFIX( *(SANE_Fixed *) value));
+ if ( val[option].w == *(SANE_Fixed *) value ) /* no change */
+ return SANE_STATUS_GOOD;
+ val[option].w = *(SANE_Fixed *) value;
+ break;
+
+ case SANE_TYPE_STRING:
+ DBG(50, "sane_control_option: option=%d, action=%d, value=%s\n",
+ option, action, (SANE_String) value);
+ if ( strcmp(val[option].s, (SANE_String) value) == 0 )
+ return SANE_STATUS_GOOD; /* no change */
+ if ( val[option].s )
+ free((void *) val[option].s);
+ val[option].s = strdup(value);
+ if ( val[option].s == NULL )
+ {
+ DBG(1, "sane_control_option: strdup failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ break;
+
+ case SANE_TYPE_BUTTON:
+ break;
+
+ default:
+ DBG(1, "sane_control_option: unknown type %d\n",
+ sod[option].type);
+ break;
+ }
+
+ switch ( option )
+ {
+ case OPT_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ case OPT_DISABLE_BACKTRACK:
+ case OPT_CALIB_BACKEND:
+ case OPT_LIGHTLID35:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_EXPOSURE:
+ case OPT_EXPOSURE_R:
+ case OPT_EXPOSURE_G:
+ case OPT_EXPOSURE_B:
+ case OPT_GAMMA_SCALAR:
+ case OPT_GAMMA_SCALAR_R:
+ case OPT_GAMMA_SCALAR_G:
+ case OPT_GAMMA_SCALAR_B:
+ case OPT_GAMMA_CUSTOM:
+ case OPT_GAMMA_CUSTOM_R:
+ case OPT_GAMMA_CUSTOM_G:
+ case OPT_GAMMA_CUSTOM_B:
+ case OPT_HALFTONE:
+ case OPT_BALANCE_R:
+ case OPT_BALANCE_G:
+ case OPT_BALANCE_B:
+ return SANE_STATUS_GOOD;
+
+ case OPT_BITDEPTH:
+ /* If the bitdepth has changed we must change the size of */
+ /* the gamma table if the device does not support gamma */
+ /* tables. This will hopefully cause no trouble if the */
+ /* mode is one bit */
+
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ int max_gamma_value;
+ int size;
+ int color;
+ int i;
+
+ size = (int) pow(2.0, (double) val[OPT_BITDEPTH].w) - 1;
+ max_gamma_value = size - 1;
+ for ( color = 0; color < 4; color++ )
+ {
+ for ( i = 0; i < max_gamma_value; i++ )
+ md->custom_gamma_table[color][i] = (SANE_Int) i;
+ }
+ md->custom_gamma_range.max = (SANE_Int) max_gamma_value;
+ sod[OPT_GAMMA_CUSTOM].size = size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_R].size = size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_G].size = size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_B].size = size * sizeof (SANE_Int);
+
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ }
+
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ if ( strcmp(val[option].s, MD_SOURCESTRING_FLATBED) == 0 )
+ md->scan_source = MD_SOURCE_FLATBED;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_TMA) == 0 )
+ md->scan_source = MD_SOURCE_TMA;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_ADF) == 0 )
+ md->scan_source = MD_SOURCE_ADF;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_STRIPE) == 0 )
+ md->scan_source = MD_SOURCE_STRIPE;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_SLIDE) == 0 )
+ md->scan_source = MD_SOURCE_SLIDE;
+ else
+ {
+ DBG(1, "sane_control_option: unsupported option %s\n",
+ val[option].s);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ init_options(ms, md->scan_source);
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ status = set_option_dependencies(ms, sod, val);
+
+ /* Options with side effects need special treatment. They are */
+ /* reset, even if they were set by set_option_dependencies(): */
+ /* if we have more than one color depth activate this option */
+
+ if ( md->bitdepth_list[0] == 1 )
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ if ( strncmp(md->opts.auto_adjust, "off", 3) == 0 )
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CHANNEL:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if ( strcmp(val[option].s, MD_CHANNEL_MASTER) == 0 )
+ {
+ sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[option].s, MD_CHANNEL_RED) == 0 )
+ {
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[option].s, MD_CHANNEL_GREEN) == 0 )
+ {
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[option].s, MD_CHANNEL_BLUE) == 0 )
+ {
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_MODE:
+ restore_gamma_options(sod, val);
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_BIND:
+ restore_gamma_options(sod, val);
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ if ( val[option].w >= val[option + 1].w )
+ {
+ val[option + 1].w = val[option].w + 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if ( val[option + 1].w >= val[option + 2].w )
+ val[option + 2].w = val[option + 1].w + 1;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MIDTONE:
+ case OPT_MIDTONE_R:
+ case OPT_MIDTONE_G:
+ case OPT_MIDTONE_B:
+ if ( val[option].w <= val[option - 1].w )
+ {
+ val[option - 1].w = val[option].w - 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if ( val[option].w >= val[option + 1].w )
+ {
+ val[option + 1].w = val[option].w + 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ if ( val[option].w <= val[option - 1].w )
+ {
+ val[option - 1].w = val[option].w - 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if ( val[option - 1].w <= val[option - 2].w )
+ val[option - 2].w = val[option - 1].w - 1;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION_BIND:
+ if ( ms->val[option].w == SANE_FALSE )
+ {
+ ms->sod[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ ms->sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ }
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TOGGLELAMP:
+ status = scsi_read_system_status(md, -1);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_IO_ERROR;
+
+ md->status.flamp ^= 1;
+ status = scsi_send_system_status(md, -1);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_IO_ERROR;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOADJUST:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ if ( ms->val[option].w == SANE_FALSE )
+ ms->sod[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ else
+ ms->sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_BALANCE_FW:
+ val[OPT_BALANCE_R].w =
+ SANE_FIX((uint8_t)( (float)mi->balance[0] / 2.55 ) );
+ val[OPT_BALANCE_G].w =
+ SANE_FIX((uint8_t)( (float)mi->balance[1] / 2.55 ) );
+ val[OPT_BALANCE_B].w =
+ SANE_FIX((uint8_t)( (float)mi->balance[2] / 2.55 ) );
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#if 0
+ break;
+#endif
+ default:
+ DBG(1, "sane_control_option: Unsupported action %d\n", action);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+/*---------- sane_get_option_descriptor() ------------------------------------*/
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle handle, SANE_Int n)
+{
+ Microtek2_Scanner *ms = handle;
+
+ DBG(255, "sane_get_option_descriptor: handle=%p, sod=%p, opt=%d\n",
+ (void *) handle, (void *) ms->sod, n);
+
+ if ( n < 0 || n >= NUM_OPTIONS )
+ {
+ DBG(30, "sane_get_option_descriptor: invalid option %d\n", n);
+ return NULL;
+ }
+
+ return &ms->sod[n];
+}
+
+/*---------- restore_gamma_options() -----------------------------------------*/
+
+static SANE_Status
+restore_gamma_options(SANE_Option_Descriptor *sod, Option_Value *val)
+{
+
+ DBG(40, "restore_gamma_options: val=%p, sod=%p\n", (void *) val, (void *) sod);
+ /* if we dont have a gamma table return immediately */
+ if ( ! val[OPT_GAMMA_MODE].s )
+ return SANE_STATUS_GOOD;
+
+ if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 )
+ {
+ sod[OPT_GAMMA_MODE].cap &= ~SANE_CAP_INACTIVE;
+ if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_LINEAR) == 0 )
+ {
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_SCALAR) == 0 )
+ {
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if ( val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ {
+ sod[OPT_GAMMA_SCALAR].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_CUSTOM) == 0 )
+ {
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if ( val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ {
+ sod[OPT_GAMMA_CUSTOM].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ {
+ sod[OPT_GAMMA_MODE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_LINEAR) == 0 )
+ {
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_SCALAR) == 0 )
+ {
+ sod[OPT_GAMMA_SCALAR].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_CUSTOM) == 0 )
+ {
+ sod[OPT_GAMMA_CUSTOM].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0
+ || strcmp(val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 )
+ {
+ /* reset gamma to default */
+ if ( val[OPT_GAMMA_MODE].s )
+ free((void *) val[OPT_GAMMA_MODE].s);
+ val[OPT_GAMMA_MODE].s = strdup(MD_GAMMAMODE_LINEAR);
+ sod[OPT_GAMMA_MODE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ DBG(1, "restore_gamma_options: unknown mode %s\n", val[OPT_MODE].s);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- calculate_sane_params() -----------------------------------------*/
+
+static SANE_Status
+calculate_sane_params(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+
+ DBG(30, "calculate_sane_params: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( ! mi->onepass && ms->mode == MS_MODE_COLOR )
+ {
+ if ( ms->current_pass == 1 )
+ ms->params.format = SANE_FRAME_RED;
+ else if ( ms->current_pass == 2 )
+ ms->params.format = SANE_FRAME_GREEN;
+ else if ( ms->current_pass == 3 )
+ ms->params.format = SANE_FRAME_BLUE;
+ else
+ {
+ DBG(1, "calculate_sane_params: invalid pass number %d\n",
+ ms->current_pass);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else if ( mi->onepass && ms->mode == MS_MODE_COLOR )
+ ms->params.format = SANE_FRAME_RGB;
+ else
+ ms->params.format = SANE_FRAME_GRAY;
+
+ if ( ! mi->onepass && ms->mode == MS_MODE_COLOR && ms->current_pass < 3 )
+ ms->params.last_frame = SANE_FALSE;
+ else
+ ms->params.last_frame = SANE_TRUE;
+ ms->params.lines = ms->src_remaining_lines;
+ ms->params.pixels_per_line = ms->ppl;
+ ms->params.bytes_per_line = ms->real_bpl;
+ ms->params.depth = ms->bits_per_pixel_out;
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/*---------- get_calib_params() ----------------------------------------------*/
+
+static void
+get_calib_params(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+
+ DBG(30, "get_calib_params: handle=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( md->model_flags & MD_CALIB_DIVISOR_600 )
+ {
+ if ( ms->x_resolution_dpi <= 600 )
+ mi->calib_divisor = 2;
+ else
+ mi->calib_divisor = 1;
+ }
+ DBG(30, "Calib Divisor: %d\n", mi->calib_divisor);
+
+
+ ms->x_resolution_dpi = mi->opt_resolution / mi->calib_divisor;
+ ms->y_resolution_dpi = mi->opt_resolution / 5; /* ignore dust particles */
+ ms->x1_dots = 0;
+ ms->y1_dots = mi->calib_white;
+ ms->width_dots = mi->geo_width;
+ if ( md->shading_length != 0 )
+ ms->height_dots = md->shading_length;
+ else
+ ms->height_dots = mi->calib_space;
+
+ ms->mode = MS_MODE_COLOR;
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ ms->depth = 16;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ ms->depth = 14;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ ms->depth = 12;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ ms->depth = 10;
+ else
+ ms->depth = 8;
+
+ ms->stay = 0;
+ if ( mi->calib_space < 10 )
+ ms->stay = 1;
+ ms->rawdat = 1;
+ ms->quality = 1;
+ ms->fastscan = 0;
+/* ms->scan_source = md->scan_source; */
+ ms->scan_source = 0;
+ ms->brightness_m = ms->brightness_r = ms->brightness_g =
+ ms->brightness_b = 128;
+ ms->exposure_m = ms->exposure_r = ms->exposure_g = ms->exposure_b = 0;
+ ms->contrast_m = ms->contrast_r = ms->contrast_g = ms->contrast_b = 128;
+ ms->shadow_m = ms->shadow_r = ms->shadow_g = ms->shadow_b = 0;
+ ms->midtone_m = ms->midtone_r = ms->midtone_g = ms->midtone_b = 128;
+ ms->highlight_m = ms->highlight_r = ms->highlight_g = ms->highlight_b = 255;
+
+ return;
+}
+
+
+/*---------- get_scan_parameters () ------------------------------------------*/
+
+static SANE_Status
+get_scan_parameters(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ double dpm; /* dots per millimeter */
+ int x2_dots;
+ int y2_dots;
+ int i;
+
+
+ DBG(30, "get_scan_parameters: handle=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ get_scan_mode_and_depth(ms, &ms->mode, &ms->depth,
+ &ms->bits_per_pixel_in, &ms->bits_per_pixel_out);
+
+ /* get the scan_source */
+ if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_FLATBED) == 0 )
+ ms->scan_source = MS_SOURCE_FLATBED;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_ADF) == 0 )
+ ms->scan_source = MS_SOURCE_ADF;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_TMA) == 0 )
+ ms->scan_source = MS_SOURCE_TMA;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_STRIPE) == 0 )
+ ms->scan_source = MS_SOURCE_STRIPE;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_SLIDE) == 0 )
+ ms->scan_source = MS_SOURCE_SLIDE;
+
+ /* enable/disable backtracking */
+ if ( ms->val[OPT_DISABLE_BACKTRACK].w == SANE_TRUE )
+ ms->no_backtracking = 1;
+ else
+ ms->no_backtracking = 0;
+
+ /* turn off the lamp during a scan */
+ if ( ms->val[OPT_LIGHTLID35].w == SANE_TRUE )
+ ms->lightlid35 = 1;
+ else
+ ms->lightlid35 = 0;
+
+ /* automatic adjustment of threshold */
+ if ( ms->val[OPT_AUTOADJUST].w == SANE_TRUE)
+ ms->auto_adjust = 1;
+ else
+ ms->auto_adjust = 0;
+
+ /* color calibration by backend */
+ if ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE )
+ ms->calib_backend = 1;
+ else
+ ms->calib_backend = 0;
+
+ /* if halftone mode select halftone pattern */
+ if ( ms->mode == MS_MODE_HALFTONE )
+ {
+ i = 0;
+ while ( strcmp(md->halftone_mode_list[i], ms->val[OPT_HALFTONE].s) )
+ ++i;
+ ms->internal_ht_index = i;
+ }
+
+ /* if lineart get the value for threshold */
+ if ( ms->mode == MS_MODE_LINEART || ms->mode == MS_MODE_LINEARTFAKE)
+ ms->threshold = (uint8_t) ms->val[OPT_THRESHOLD].w;
+ else
+ ms->threshold = (uint8_t) M_THRESHOLD_DEFAULT;
+
+ DBG(30, "get_scan_parameters: mode=%d, depth=%d, bpp_in=%d, bpp_out=%d\n",
+ ms->mode, ms->depth, ms->bits_per_pixel_in,
+ ms->bits_per_pixel_out);
+
+ /* calculate positions, width and height in dots */
+ /* check for impossible values */
+ /* ensure a minimum scan area of 10 x 10 pixels */
+ dpm = (double) mi->opt_resolution / MM_PER_INCH;
+ ms->x1_dots = (SANE_Int) ( SANE_UNFIX(ms->val[OPT_TL_X].w) * dpm + 0.5 );
+ if ( ms->x1_dots > ( mi->geo_width - 10 ) )
+ ms->x1_dots = ( mi->geo_width - 10 );
+ ms->y1_dots = (SANE_Int) ( SANE_UNFIX(ms->val[OPT_TL_Y].w) * dpm + 0.5 );
+ if ( ms->y1_dots > ( mi->geo_height - 10 ) )
+ ms->y1_dots = ( mi->geo_height - 10 );
+ x2_dots = (int) ( SANE_UNFIX(ms->val[OPT_BR_X].w) * dpm + 0.5 );
+ if ( x2_dots >= mi->geo_width )
+ x2_dots = mi->geo_width - 1;
+ y2_dots = (int) ( SANE_UNFIX(ms->val[OPT_BR_Y].w) * dpm + 0.5 );
+ if ( y2_dots >= mi->geo_height )
+ y2_dots = mi->geo_height - 1;
+ ms->width_dots = x2_dots - ms->x1_dots;
+ if ( md->model_flags && MD_OFFSET_2 ) /* this firmware has problems with */
+ if ( ( ms->width_dots % 2 ) == 1 ) /* odd pixel numbers */
+ ms->width_dots -= 1;
+ if ( ms->width_dots < 10 )
+ ms->width_dots = 10;
+ ms->height_dots = y2_dots - ms->y1_dots;
+ if ( ms->height_dots < 10 )
+ ms->height_dots = 10;
+
+/*test!!!*/
+/* ms->y1_dots -= 50;*/
+
+ /* take scanning direction into account */
+ if ((mi->direction & MI_DATSEQ_RTOL) == 1)
+ ms->x1_dots = mi->geo_width - ms->x1_dots - ms->width_dots;
+
+ if ( ms->val[OPT_RESOLUTION_BIND].w == SANE_TRUE )
+ {
+ ms->x_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5);
+ ms->y_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5);
+ }
+ else
+ {
+ ms->x_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5);
+ ms->y_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_Y_RESOLUTION].w) + 0.5);
+ }
+
+ if ( ms->x_resolution_dpi < 10 )
+ ms->x_resolution_dpi = 10;
+ if ( ms->y_resolution_dpi < 10 )
+ ms->y_resolution_dpi = 10;
+
+ DBG(30, "get_scan_parameters: yres=%d, x1=%d, width=%d, y1=%d, height=%d\n",
+ ms->y_resolution_dpi, ms->x1_dots, ms->width_dots,
+ ms->y1_dots, ms->height_dots);
+
+ /* Preview mode */
+ if ( ms->val[OPT_PREVIEW].w == SANE_TRUE )
+ {
+ ms->fastscan = SANE_TRUE;
+ ms->quality = SANE_FALSE;
+ }
+ else
+ {
+ ms->fastscan = SANE_FALSE;
+ ms->quality = SANE_TRUE;
+ }
+
+ ms->rawdat = 0;
+
+ /* brightness, contrast, values 1,..,255 */
+ ms->brightness_m = (uint8_t) (SANE_UNFIX(ms->val[OPT_BRIGHTNESS].w)
+ / SANE_UNFIX(md->percentage_range.max) * 254.0) + 1;
+ ms->brightness_r = ms->brightness_g = ms->brightness_b = ms->brightness_m;
+
+ ms->contrast_m = (uint8_t) (SANE_UNFIX(ms->val[OPT_CONTRAST].w)
+ / SANE_UNFIX(md->percentage_range.max) * 254.0) + 1;
+ ms->contrast_r = ms->contrast_g = ms->contrast_b = ms->contrast_m;
+
+ /* shadow, midtone, highlight, exposure */
+ ms->shadow_m = (uint8_t) ms->val[OPT_SHADOW].w;
+ ms->shadow_r = (uint8_t) ms->val[OPT_SHADOW_R].w;
+ ms->shadow_g = (uint8_t) ms->val[OPT_SHADOW_G].w;
+ ms->shadow_b = (uint8_t) ms->val[OPT_SHADOW_B].w;
+ ms->midtone_m = (uint8_t) ms->val[OPT_MIDTONE].w;
+ ms->midtone_r = (uint8_t) ms->val[OPT_MIDTONE_R].w;
+ ms->midtone_g = (uint8_t) ms->val[OPT_MIDTONE_G].w;
+ ms->midtone_b = (uint8_t) ms->val[OPT_MIDTONE_B].w;
+ ms->highlight_m = (uint8_t) ms->val[OPT_HIGHLIGHT].w;
+ ms->highlight_r = (uint8_t) ms->val[OPT_HIGHLIGHT_R].w;
+ ms->highlight_g = (uint8_t) ms->val[OPT_HIGHLIGHT_G].w;
+ ms->highlight_b = (uint8_t) ms->val[OPT_HIGHLIGHT_B].w;
+ ms->exposure_m = (uint8_t) (ms->val[OPT_EXPOSURE].w / 2);
+ ms->exposure_r = (uint8_t) (ms->val[OPT_EXPOSURE_R].w / 2);
+ ms->exposure_g = (uint8_t) (ms->val[OPT_EXPOSURE_G].w / 2);
+ ms->exposure_b = (uint8_t) (ms->val[OPT_EXPOSURE_B].w / 2);
+
+ ms->gamma_mode = strdup( (char *) ms->val[OPT_GAMMA_MODE].s);
+
+ ms->balance[0] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_R].w));
+ ms->balance[1] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_G].w));
+ ms->balance[2] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_B].w));
+ DBG(255, "get_scan_parameters:ms->balance[0]=%d,[1]=%d,[2]=%d\n",
+ ms->balance[0], ms->balance[1], ms->balance[2]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- get_scan_mode_and_depth() ---------------------------------------*/
+
+static SANE_Status
+get_scan_mode_and_depth(Microtek2_Scanner *ms,
+ int *mode,
+ int *depth,
+ int *bits_per_pixel_in,
+ int *bits_per_pixel_out)
+{
+ /* This function translates the strings for the possible modes and */
+ /* bitdepth into a more conveniant format as needed for SET WINDOW. */
+ /* bits_per_pixel is the number of bits per color one pixel needs */
+ /* when transferred from the the scanner, bits_perpixel_out is the */
+ /* number of bits per color one pixel uses when transferred to the */
+ /* frontend. These may be different. For example, with a depth of 4 */
+ /* two pixels per byte are transferred from the scanner, but only one */
+ /* pixel per byte is transferred to the frontend. */
+ /* If lineart_fake is set to !=0, we need the parameters for a */
+ /* grayscale scan, because the scanner has no lineart mode */
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+ DBG(30, "get_scan_mode_and_depth: handle=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 )
+ *mode = MS_MODE_COLOR;
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ *mode = MS_MODE_GRAY;
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0)
+ *mode = MS_MODE_HALFTONE;
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 )
+ {
+ if ( MI_LINEART_NONE(mi->scanmode)
+ || ms->val[OPT_AUTOADJUST].w == SANE_TRUE
+ || md->model_flags & MD_READ_CONTROL_BIT)
+ *mode = MS_MODE_LINEARTFAKE;
+ else
+ *mode = MS_MODE_LINEART;
+ }
+ else
+ {
+ DBG(1, "get_scan_mode_and_depth: Unknown mode %s\n",
+ ms->val[OPT_MODE].s);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0
+ || strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ {
+ if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_16 )
+ {
+ *depth = 16;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_14 )
+ {
+ *depth = 14;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_12 )
+ {
+ *depth = 12;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_10 )
+ {
+ *depth = 10;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_8 )
+ {
+ *depth = 8;
+ *bits_per_pixel_in = *bits_per_pixel_out = 8;
+ }
+ else if ( ms->val[OPT_MODE].w == MD_DEPTHVAL_4 )
+ {
+ *depth = 4;
+ *bits_per_pixel_in = 4;
+ *bits_per_pixel_out = 8;
+ }
+ }
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 )
+ {
+ *depth = 1;
+ *bits_per_pixel_in = *bits_per_pixel_out = 1;
+ }
+ else /* lineart */
+ {
+ *bits_per_pixel_out = 1;
+ if ( *mode == MS_MODE_LINEARTFAKE )
+ {
+ *depth = 8;
+ *bits_per_pixel_in = 8;
+ }
+ else
+ {
+ *depth = 1;
+ *bits_per_pixel_in = 1;
+ }
+ }
+
+#if 0
+ if ( ms->val[OPT_PREVIEW].w == SANE_TRUE )
+ {
+ if ( *depth > 8 )
+ {
+ *depth = 8;
+ *bits_per_pixel_in = *bits_per_pixel_out = 8;
+ }
+ }
+#endif
+
+ DBG(30, "get_scan_mode_and_depth: mode=%d, depth=%d,"
+ " bits_pp_in=%d, bits_pp_out=%d, preview=%d\n",
+ *mode, *depth, *bits_per_pixel_in, *bits_per_pixel_out,
+ ms->val[OPT_PREVIEW].w);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_wait_for_image() -------------------------------------------*/
+
+static SANE_Status
+scsi_wait_for_image(Microtek2_Scanner *ms)
+{
+ int retry = 60;
+ SANE_Status status;
+
+
+ DBG(30, "scsi_wait_for_image: ms=%p\n", (void *) ms);
+
+ while ( retry-- > 0 )
+ {
+ status = scsi_read_image_status(ms);
+ if (status == SANE_STATUS_DEVICE_BUSY )
+ {
+ sleep(1);
+ continue;
+ }
+ if ( status == SANE_STATUS_GOOD )
+ return status;
+
+ /* status != GOOD && != BUSY */
+ DBG(1, "scsi_wait_for_image: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ /* BUSY after n retries */
+ DBG(1, "scsi_wait_for_image: '%s'\n", sane_strstatus(status));
+ return status;
+}
+
+
+/*---------- scsi_read_gamma() -----------------------------------------------*/
+
+/* currently not used */
+/*
+static SANE_Status
+scsi_read_gamma(Microtek2_Scanner *ms, int color)
+{
+ uint8_t readgamma[RG_CMD_L];
+ uint8_t result[3072];
+ size_t size;
+ SANE_Bool endiantype;
+ SANE_Status status;
+
+ RG_CMD(readgamma);
+ ENDIAN_TYPE(endiantype);
+ RG_PCORMAC(readgamma, endiantype);
+ RG_COLOR(readgamma, color);
+ RG_WORD(readgamma, ( ms->dev->lut_entry_size == 1 ) ? 0 : 1);
+ RG_TRANSFERLENGTH(readgamma, (color == 3 ) ? 3072 : 1024);
+
+ dump_area(readgamma, 10, "ReadGamma");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(ms->sfd, readgamma, sizeof(readgamma),
+ result, &size);
+ if ( status != SANE_STATUS_GOOD ) {
+ DBG(1, "scsi_read_gamma: (L,R) read_gamma failed: status '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ dump_area(result, 3072, "Result");
+
+ return SANE_STATUS_GOOD;
+}
+*/
+
+
+/*---------- scsi_send_gamma() -----------------------------------------------*/
+
+static SANE_Status
+scsi_send_gamma(Microtek2_Scanner *ms)
+{
+ SANE_Bool endiantype;
+ SANE_Status status;
+ size_t size;
+ uint8_t *cmd, color;
+
+
+ DBG(30, "scsi_send_gamma: pos=%p, size=%d, word=%d, color=%d\n",
+ ms->gamma_table, ms->lut_size_bytes, ms->word, ms->current_color);
+
+ if ( ( 3 * ms->lut_size_bytes ) <= 0xffff ) /*send Gamma with one command*/
+ {
+ cmd = (uint8_t *) alloca(SG_CMD_L + 3 * ms->lut_size_bytes);
+ if ( cmd == NULL )
+ {
+ DBG(1, "scsi_send_gamma: Couldn't get buffer for gamma table\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ SG_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ SG_SET_PCORMAC(cmd, endiantype);
+ SG_SET_COLOR(cmd, ms->current_color);
+ SG_SET_WORD(cmd, ms->word);
+ SG_SET_TRANSFERLENGTH(cmd, 3 * ms->lut_size_bytes);
+ memcpy(cmd + SG_CMD_L, ms->gamma_table, 3 * ms->lut_size_bytes);
+ size = 3 * ms->lut_size_bytes;
+ if ( md_dump >= 2 )
+ dump_area2(cmd, SG_CMD_L, "sendgammacmd");
+ if ( md_dump >= 3 )
+ dump_area2(cmd + SG_CMD_L, size, "sendgammadata");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, size + SG_CMD_L, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_gamma: '%s'\n", sane_strstatus(status));
+ }
+
+ else /* send gamma with 3 commands, one for each color */
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ cmd = (uint8_t *) alloca(SG_CMD_L + ms->lut_size_bytes);
+ if ( cmd == NULL )
+ {
+ DBG(1, "scsi_send_gamma: Couldn't get buffer for gamma table\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ SG_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ SG_SET_PCORMAC(cmd, endiantype);
+ SG_SET_COLOR(cmd, color);
+ SG_SET_WORD(cmd, ms->word);
+ SG_SET_TRANSFERLENGTH(cmd, ms->lut_size_bytes);
+ memcpy(cmd + SG_CMD_L,
+ ms->gamma_table + color * ms->lut_size_bytes,
+ ms->lut_size_bytes);
+ size = ms->lut_size_bytes;
+ if ( md_dump >= 2 )
+ dump_area2(cmd, SG_CMD_L, "sendgammacmd");
+ if ( md_dump >= 3 )
+ dump_area2(cmd + SG_CMD_L, size, "sendgammadata");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, size + SG_CMD_L, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_gamma: '%s'\n", sane_strstatus(status));
+ }
+
+ }
+
+ return status;
+}
+
+
+/*---------- scsi_inquiry() --------------------------------------------------*/
+
+static SANE_Status
+scsi_inquiry(Microtek2_Info *mi, char *device)
+{
+ SANE_Status status;
+ uint8_t cmd[INQ_CMD_L];
+ uint8_t *result;
+ uint8_t inqlen;
+ size_t size;
+ int sfd;
+
+
+ DBG(30, "scsi_inquiry: mi=%p, device='%s'\n", (void *) mi, device);
+
+ status = sanei_scsi_open(device, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_inquiry: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ INQ_CMD(cmd);
+ INQ_SET_ALLOC(cmd, INQ_ALLOC_L);
+ result = (uint8_t *) alloca(INQ_ALLOC_L);
+ if ( result == NULL )
+ {
+ DBG(1, "scsi_inquiry: malloc failed\n");
+ sanei_scsi_close(sfd);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ size = INQ_ALLOC_L;
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_inquiry: '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+
+ INQ_GET_INQLEN(inqlen, result);
+ INQ_SET_ALLOC(cmd, inqlen + INQ_ALLOC_L);
+ result = alloca(inqlen + INQ_ALLOC_L);
+ if ( result == NULL )
+ {
+ DBG(1, "scsi_inquiry: malloc failed\n");
+ sanei_scsi_close(sfd);
+ return SANE_STATUS_NO_MEM;
+ }
+ size = inqlen + INQ_ALLOC_L;
+ if (md_dump >= 2 )
+ dump_area2(cmd, sizeof(cmd), "inquiry");
+
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_inquiry: cmd '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+ sanei_scsi_close(sfd);
+
+ if (md_dump >= 2 )
+ {
+ dump_area2((uint8_t *) result, size, "inquiryresult");
+ dump_area((uint8_t *) result, size, "inquiryresult");
+ }
+
+ /* copy results */
+ INQ_GET_QUAL(mi->device_qualifier, result);
+ INQ_GET_DEVT(mi->device_type, result);
+ INQ_GET_VERSION(mi->scsi_version, result);
+ INQ_GET_VENDOR(mi->vendor, (char *)result);
+ INQ_GET_MODEL(mi->model, (char *)result);
+ INQ_GET_REV(mi->revision, (char *)result);
+ INQ_GET_MODELCODE(mi->model_code, result);
+
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_read_attributes() ------------------------------------------*/
+
+static SANE_Status
+scsi_read_attributes(Microtek2_Info *pmi, char *device, uint8_t scan_source)
+{
+ SANE_Status status;
+ Microtek2_Info *mi;
+ uint8_t readattributes[RSA_CMD_L];
+ uint8_t result[RSA_TRANSFERLENGTH];
+ size_t size;
+ int sfd;
+
+
+ mi = &pmi[scan_source];
+
+ DBG(30, "scsi_read_attributes: mi=%p, device='%s', source=%d\n",
+ (void *) mi, device, scan_source);
+
+ RSA_CMD(readattributes);
+ RSA_SETMEDIA(readattributes, scan_source);
+ status = sanei_scsi_open(device, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_attributes: open '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if (md_dump >= 2 )
+ dump_area2(readattributes, sizeof(readattributes), "scannerattributes");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(sfd, readattributes,
+ sizeof(readattributes), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_attributes: cmd '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+
+ sanei_scsi_close(sfd);
+
+ /* The X6 appears to lie about the data format for a TMA */
+ if ( (&pmi[0])->model_code == 0x91 )
+ result[0] &= 0xfd;
+ /* default value for calib_divisor ... bit49?? */
+ mi->calib_divisor = 1;
+ /* 9600XL */
+ if ( (&pmi[0])->model_code == 0xde )
+ mi->calib_divisor = 2;
+ /* 6400XL has problems in lineart mode*/
+ if ( (&pmi[0])->model_code == 0x89 )
+ result[13] &= 0xfe; /* simulate no lineart */
+#if 0
+ result[13] &= 0xfe; /* simulate no lineart */
+#endif
+
+ /* copy all the stuff into the info structure */
+ RSA_COLOR(mi->color, result);
+ RSA_ONEPASS(mi->onepass, result);
+ RSA_SCANNERTYPE(mi->scanner_type, result);
+ RSA_FEPROM(mi->feprom, result);
+ RSA_DATAFORMAT(mi->data_format, result);
+ RSA_COLORSEQUENCE(mi->color_sequence, result);
+ RSA_NIS(mi->new_image_status, result);
+ RSA_DATSEQ(mi->direction, result);
+ RSA_CCDGAP(mi->ccd_gap, result);
+ RSA_MAX_XRESOLUTION(mi->max_xresolution, result);
+ RSA_MAX_YRESOLUTION(mi->max_yresolution, result);
+ RSA_GEOWIDTH(mi->geo_width, result);
+ RSA_GEOHEIGHT(mi->geo_height, result);
+ RSA_OPTRESOLUTION(mi->opt_resolution, result);
+ RSA_DEPTH(mi->depth, result);
+ /* The X12USL doesn't say that it has 14bit */
+ if ( (&pmi[0])->model_code == 0xb0 )
+ mi->depth |= MI_HASDEPTH_14;
+ RSA_SCANMODE(mi->scanmode, result);
+ RSA_CCDPIXELS(mi->ccd_pixels, result);
+ RSA_LUTCAP(mi->lut_cap, result);
+ RSA_DNLDPTRN(mi->has_dnldptrn, result);
+ RSA_GRAINSLCT(mi->grain_slct, result);
+ RSA_SUPPOPT(mi->option_device, result);
+ RSA_CALIBWHITE(mi->calib_white, result);
+ RSA_CALIBSPACE(mi->calib_space, result);
+ RSA_NLENS(mi->nlens, result);
+ RSA_NWINDOWS(mi->nwindows, result);
+ RSA_SHTRNSFEREQU(mi->shtrnsferequ, result);
+ RSA_SCNBTTN(mi->scnbuttn, result);
+ RSA_BUFTYPE(mi->buftype, result);
+ RSA_REDBALANCE(mi->balance[0], result);
+ RSA_GREENBALANCE(mi->balance[1], result);
+ RSA_BLUEBALANCE(mi->balance[2], result);
+ RSA_APSMAXFRAMES(mi->aps_maxframes, result);
+
+ if (md_dump >= 2 )
+ dump_area2((uint8_t *) result, sizeof(result),
+ "scannerattributesresults");
+ if ( md_dump >= 1 && md_dump_clear )
+ dump_attributes(mi);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_read_control_bits() ----------------------------------------*/
+
+static SANE_Status
+scsi_read_control_bits(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ SANE_Status status;
+ uint8_t cmd[RCB_CMD_L];
+ uint32_t byte;
+ int bit;
+ int count_1s;
+
+ md = ms->dev;
+
+ DBG(30, "scsi_read_control_bits: ms=%p, fd=%d\n", (void *) ms, ms->sfd);
+ DBG(30, "ms->control_bytes = %p\n", ms->control_bytes);
+
+ RCB_SET_CMD(cmd);
+ RCB_SET_LENGTH(cmd, ms->n_control_bytes);
+
+ if ( md_dump >= 2)
+ dump_area2(cmd, RCB_CMD_L, "readcontrolbits");
+
+ status = sanei_scsi_cmd(ms->sfd,
+ cmd,
+ sizeof(cmd),
+ ms->control_bytes,
+ &ms->n_control_bytes);
+
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_control_bits: cmd '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2)
+ dump_area2(ms->control_bytes,
+ ms->n_control_bytes,
+ "readcontrolbitsresult");
+
+ count_1s = 0;
+ for ( byte = 0; byte < ms->n_control_bytes; byte++ )
+ {
+ for ( bit = 0; bit < 8; bit++ )
+ {
+ if ( (ms->control_bytes[byte] >> bit) & 0x01 )
+ ++count_1s;
+ }
+ }
+ DBG(20, "read_control_bits: number of 1's in controlbytes: %d\n", count_1s);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_set_window() -----------------------------------------------*/
+
+static SANE_Status
+scsi_set_window(Microtek2_Scanner *ms, int n) { /* n windows, not yet */
+ /* implemented */
+ SANE_Status status;
+ uint8_t *setwindow;
+ int size;
+
+
+ DBG(30, "scsi_set_window: ms=%p, wnd=%d\n", (void *) ms, n);
+
+ size = SW_CMD_L + SW_HEADER_L + n * SW_BODY_L;
+ setwindow = (uint8_t *) malloc(size);
+ DBG(100, "scsi_set_window: setwindow= %p, malloc'd %d Bytes\n",
+ setwindow, size);
+ if ( setwindow == NULL )
+ {
+ DBG(1, "scsi_set_window: malloc for setwindow failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(setwindow, 0, size);
+
+ SW_CMD(setwindow);
+ SW_PARAM_LENGTH(setwindow, SW_HEADER_L + n * SW_BODY_L);
+ SW_WNDDESCLEN(setwindow + SW_HEADER_P, SW_WNDDESCVAL);
+
+#define POS (setwindow + SW_BODY_P(n-1))
+
+ SW_WNDID(POS, n-1);
+ SW_XRESDPI(POS, ms->x_resolution_dpi);
+ SW_YRESDPI(POS, ms->y_resolution_dpi);
+ SW_XPOSTL(POS, ms->x1_dots);
+ SW_YPOSTL(POS, ms->y1_dots);
+ SW_WNDWIDTH(POS, ms->width_dots);
+ SW_WNDHEIGHT(POS, ms->height_dots);
+ SW_THRESHOLD(POS, ms->threshold);
+ SW_IMGCOMP(POS, ms->mode);
+ SW_BITSPERPIXEL(POS, ms->depth);
+ SW_EXTHT(POS, ms->use_external_ht);
+ SW_INTHTINDEX(POS, ms->internal_ht_index);
+ SW_RIF(POS, 1);
+ SW_LENS(POS, 0); /* ???? */
+ SW_INFINITE(POS, 0);
+ SW_STAY(POS, ms->stay);
+ SW_RAWDAT(POS, ms->rawdat);
+ SW_QUALITY(POS, ms->quality);
+ SW_FASTSCAN(POS, ms->fastscan);
+ SW_MEDIA(POS, ms->scan_source);
+ SW_BRIGHTNESS_M(POS, ms->brightness_m);
+ SW_CONTRAST_M(POS, ms->contrast_m);
+ SW_EXPOSURE_M(POS, ms->exposure_m);
+ SW_SHADOW_M(POS, ms->shadow_m);
+ SW_MIDTONE_M(POS, ms->midtone_m);
+ SW_HIGHLIGHT_M(POS, ms->highlight_m);
+ /* the following properties are only referenced if it's a color scan */
+ /* but I guess they don't matter at a gray scan */
+ SW_BRIGHTNESS_R(POS, ms->brightness_r);
+ SW_CONTRAST_R(POS, ms->contrast_r);
+ SW_EXPOSURE_R(POS, ms->exposure_r);
+ SW_SHADOW_R(POS, ms->shadow_r);
+ SW_MIDTONE_R(POS, ms->midtone_r);
+ SW_HIGHLIGHT_R(POS, ms->highlight_r);
+ SW_BRIGHTNESS_G(POS, ms->brightness_g);
+ SW_CONTRAST_G(POS, ms->contrast_g);
+ SW_EXPOSURE_G(POS, ms->exposure_g);
+ SW_SHADOW_G(POS, ms->shadow_g);
+ SW_MIDTONE_G(POS, ms->midtone_g);
+ SW_HIGHLIGHT_G(POS, ms->highlight_g);
+ SW_BRIGHTNESS_B(POS, ms->brightness_b);
+ SW_CONTRAST_B(POS, ms->contrast_b);
+ SW_EXPOSURE_B(POS, ms->exposure_b);
+ SW_SHADOW_B(POS, ms->shadow_b);
+ SW_MIDTONE_B(POS, ms->midtone_b);
+ SW_HIGHLIGHT_B(POS, ms->highlight_b);
+
+ if ( md_dump >= 2 )
+ {
+ dump_area2(setwindow, 10, "setwindowcmd");
+ dump_area2(setwindow + 10 ,8 , "setwindowheader");
+ dump_area2(setwindow + 18 ,61 , "setwindowbody");
+ }
+
+ status = sanei_scsi_cmd(ms->sfd, setwindow, size, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_set_window: '%s'\n", sane_strstatus(status));
+
+ DBG(100, "scsi_set_window: free setwindow at %p\n", setwindow);
+ free((void *) setwindow);
+ return status;
+}
+
+
+/*---------- scsi_read_image_info() ------------------------------------------*/
+
+static SANE_Status
+scsi_read_image_info(Microtek2_Scanner *ms)
+{
+ uint8_t cmd[RII_CMD_L];
+ uint8_t result[RII_RESULT_L];
+ size_t size;
+ SANE_Status status;
+ Microtek2_Device *md;
+
+ md = ms->dev;
+
+ DBG(30, "scsi_read_image_info: ms=%p\n", (void *) ms);
+
+ RII_SET_CMD(cmd);
+
+ if ( md_dump >= 2)
+ dump_area2(cmd, RII_CMD_L, "readimageinfo");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_image_info: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2)
+ dump_area2(result, size, "readimageinforesult");
+
+ /* The V300 returns some values in only two bytes */
+ if ( !(md->revision==2.70) && (md->model_flags & MD_RII_TWO_BYTES) )
+ {
+ RII_GET_V300_WIDTHPIXEL(ms->ppl, result);
+ RII_GET_V300_WIDTHBYTES(ms->bpl, result);
+ RII_GET_V300_HEIGHTLINES(ms->src_remaining_lines, result);
+ RII_GET_V300_REMAINBYTES(ms->remaining_bytes, result);
+ }
+ else
+ {
+ RII_GET_WIDTHPIXEL(ms->ppl, result);
+ RII_GET_WIDTHBYTES(ms->bpl, result);
+ RII_GET_HEIGHTLINES(ms->src_remaining_lines, result);
+ RII_GET_REMAINBYTES(ms->remaining_bytes, result);
+ }
+
+ DBG(30, "scsi_read_image_info: ppl=%d, bpl=%d, lines=%d, remain=%d\n",
+ ms->ppl, ms->bpl, ms->src_remaining_lines, ms->remaining_bytes);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_read_image() -----------------------------------------------*/
+
+static SANE_Status
+scsi_read_image(Microtek2_Scanner *ms, uint8_t *buffer, int bytes_per_pixel)
+{
+ uint8_t cmd[RI_CMD_L];
+ SANE_Bool endiantype;
+ SANE_Status status;
+ size_t size;
+ size_t i;
+ uint8_t tmp;
+
+
+ DBG(30, "scsi_read_image: ms=%p, buffer=%p\n", (void *) ms, buffer);
+
+ ENDIAN_TYPE(endiantype)
+ RI_SET_CMD(cmd);
+ RI_SET_PCORMAC(cmd, endiantype);
+ RI_SET_COLOR(cmd, ms->current_read_color);
+ RI_SET_TRANSFERLENGTH(cmd, ms->transfer_length);
+
+ DBG(30, "scsi_read_image: transferlength=%d\n", ms->transfer_length);
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, RI_CMD_L, "readimagecmd");
+
+ size = ms->transfer_length;
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), buffer, &size);
+
+ if ( buffer && ( ms->dev->model_flags & MD_PHANTOM_C6 ) && endiantype )
+ {
+ switch(bytes_per_pixel)
+ {
+ case 1: break;
+ case 2:
+ for ( i = 1; i < size; i += 2 )
+ {
+ tmp = buffer[i-1];
+ buffer[i-1] = buffer[i];
+ buffer[i] = tmp;
+ }
+ break;
+ default:
+ DBG(1, "scsi_read_image: Unexpected bytes_per_pixel=%d\n", bytes_per_pixel);
+ }
+ }
+
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_read_image: '%s'\n", sane_strstatus(status));
+
+ if ( md_dump > 3 )
+ dump_area2(buffer, ms->transfer_length, "readimageresult");
+
+ return status;
+}
+
+
+/*---------- scsi_read_image_status() ----------------------------------------*/
+
+static SANE_Status
+scsi_read_image_status(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint8_t cmd[RIS_CMD_L];
+ uint8_t dummy;
+ size_t dummy_length;
+ SANE_Status status;
+ SANE_Bool endian_type;
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ DBG(30, "scsi_read_image_status: ms=%p\n", (void *) ms);
+
+ ENDIAN_TYPE(endian_type)
+ RIS_SET_CMD(cmd);
+ RIS_SET_PCORMAC(cmd, endian_type);
+ RIS_SET_COLOR(cmd, ms->current_read_color);
+
+/* mi->new_image_status = SANE_TRUE; */ /* for testing*/
+
+ if ( mi->new_image_status == SANE_TRUE )
+ {
+ DBG(30, "scsi_read_image_status: use new image status \n");
+ dummy_length = 1;
+ cmd[8] = 1;
+ }
+ else
+ {
+ DBG(30, "scsi_read_image_status: use old image status \n");
+ dummy_length = 0;
+ cmd[8] = 0;
+ }
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, sizeof(cmd), "readimagestatus");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), &dummy, &dummy_length);
+
+ if ( mi->new_image_status == SANE_TRUE )
+ {
+ if ( dummy == 0 )
+ status = SANE_STATUS_GOOD;
+ else
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* For some (X6USB) scanner
+ We say we are going to try to read 1 byte of data (as recommended
+ in the Microtek SCSI command documentation under "New Image Status")
+ so that dubious SCSI host adapters (like the one in at least some
+ Microtek X6 USB scanners) don't get wedged trying to do a zero
+ length read. However, we do not actually try to read this byte of
+ data, as that wedges the USB scanner as well.
+ IOW the SCSI command says we are going to read 1 byte, but in fact
+ we don't: */
+ /*cmd[8] = 1;
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), &dummy, 0); */
+
+
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_read_image_status: '%s'\n", sane_strstatus(status));
+
+ return status;
+}
+
+/*---------- scsi_read_shading () --------------------------------------------*/
+
+static SANE_Status
+scsi_read_shading(Microtek2_Scanner *ms, uint8_t *buffer, uint32_t length)
+{
+ Microtek2_Device *md;
+ uint8_t cmd[RSI_CMD_L];
+ SANE_Bool endiantype;
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t size;
+
+ DBG(30, "scsi_read_shading: pos=%p, size=%d, word=%d, color=%d, dark=%d\n",
+ buffer, length, ms->word, ms->current_color, ms->dark);
+
+ md = ms->dev;
+
+ size = length;
+
+ RSI_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ RSI_SET_PCORMAC(cmd, endiantype);
+ RSI_SET_COLOR(cmd, ms->current_color);
+ RSI_SET_DARK(cmd, ms->dark);
+ RSI_SET_WORD(cmd, ms->word);
+ RSI_SET_TRANSFERLENGTH(cmd, size);
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, RSI_CMD_L, "readshading");
+
+ DBG(100, "scsi_read_shading: sfd=%d, cmd=%p, sizeofcmd=%lu,"
+ "dest=%p, destsize=%lu\n",
+ ms->sfd, cmd, (u_long) sizeof(cmd), buffer, (u_long) size);
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), buffer, &size);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_read_shading: '%s'\n", sane_strstatus(status));
+
+ if ( md_dump > 3)
+ dump_area2(buffer,
+ size,
+ "readshadingresult");
+
+ return status;
+}
+
+
+/*---------- scsi_send_shading () --------------------------------------------*/
+
+static SANE_Status
+scsi_send_shading(Microtek2_Scanner *ms,
+ uint8_t *shading_data,
+ uint32_t length,
+ uint8_t dark)
+{
+ SANE_Bool endiantype;
+ SANE_Status status;
+ size_t size;
+ uint8_t *cmd;
+
+
+ DBG(30, "scsi_send_shading: pos=%p, size=%d, word=%d, color=%d, dark=%d\n",
+ shading_data, length, ms->word, ms->current_color,
+ dark);
+
+ cmd = (uint8_t *) malloc(SSI_CMD_L + length);
+ DBG(100, "scsi_send_shading: cmd=%p, malloc'd %d bytes\n",
+ cmd, SSI_CMD_L + length);
+ if ( cmd == NULL )
+ {
+ DBG(1, "scsi_send_shading: Couldn't get buffer for shading table\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ SSI_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ SSI_SET_PCORMAC(cmd, endiantype);
+ SSI_SET_COLOR(cmd, ms->current_color);
+ SSI_SET_DARK(cmd, dark);
+ SSI_SET_WORD(cmd, ms->word);
+ SSI_SET_TRANSFERLENGTH(cmd, length);
+ memcpy(cmd + SSI_CMD_L, shading_data, length);
+ size = length;
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, SSI_CMD_L, "sendshading");
+ if ( md_dump >= 3 )
+ dump_area2(cmd + SSI_CMD_L, size, "sendshadingdata");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, size + SSI_CMD_L, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_shading: '%s'\n", sane_strstatus(status));
+
+ DBG(100, "free cmd at %p\n", cmd);
+ free((void *) cmd);
+
+ return status;
+
+}
+
+
+/*---------- scsi_read_system_status() ---------------------------------------*/
+
+static SANE_Status
+scsi_read_system_status(Microtek2_Device *md, int fd)
+{
+ uint8_t cmd[RSS_CMD_L];
+ uint8_t result[RSS_RESULT_L];
+ int sfd;
+ size_t size;
+ SANE_Status status;
+
+ DBG(30, "scsi_read_system_status: md=%p, fd=%d\n", (void *) md, fd);
+
+ if ( fd == -1 )
+ {
+ status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_system_status: open '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+ }
+ else
+ sfd = fd;
+
+ RSS_CMD(cmd);
+
+ if ( md_dump >= 2)
+ dump_area2(cmd, RSS_CMD_L, "readsystemstatus");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size);
+
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_system_status: cmd '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+
+ if ( fd == -1 )
+ sanei_scsi_close(sfd);
+
+ if ( md_dump >= 2)
+ dump_area2(result, size, "readsystemstatusresult");
+
+ md->status.sskip = RSS_SSKIP(result);
+ md->status.ntrack = RSS_NTRACK(result);
+ md->status.ncalib = RSS_NCALIB(result);
+ md->status.tlamp = RSS_TLAMP(result);
+ md->status.flamp = RSS_FLAMP(result);
+ md->status.rdyman= RSS_RDYMAN(result);
+ md->status.trdy = RSS_TRDY(result);
+ md->status.frdy = RSS_FRDY(result);
+ md->status.adp = RSS_RDYMAN(result);
+ md->status.detect = RSS_DETECT(result);
+ md->status.adptime = RSS_ADPTIME(result);
+ md->status.lensstatus = RSS_LENSSTATUS(result);
+ md->status.aloff = RSS_ALOFF(result);
+ md->status.timeremain = RSS_TIMEREMAIN(result);
+ md->status.tmacnt = RSS_TMACNT(result);
+ md->status.paper = RSS_PAPER(result);
+ md->status.adfcnt = RSS_ADFCNT(result);
+ md->status.currentmode = RSS_CURRENTMODE(result);
+ md->status.buttoncount = RSS_BUTTONCOUNT(result);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_request_sense() --------------------------------------------*/
+
+/* currently not used */
+
+#if 0
+
+static SANE_Status
+scsi_request_sense(Microtek2_Scanner *ms)
+{
+ uint8_t requestsense[RQS_CMD_L];
+ uint8_t buffer[100];
+ SANE_Status status;
+ int size;
+ int asl;
+ int as_info_length;
+
+ DBG(30, "scsi_request_sense: ms=%p\n", (void *) ms);
+
+ RQS_CMD(requestsense);
+ RQS_ALLOCLENGTH(requestsense, 100);
+
+ size = sizeof(buffer);
+ status = sanei_scsi_cmd(ms->sfd, requestsense, sizeof(requestsense),
+ buffer, &size);
+
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_request_sense: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2 )
+ dump_area2(buffer, size, "requestsenseresult");
+
+ dump_area(buffer, RQS_LENGTH(buffer), "RequestSense");
+ asl = RQS_ASL(buffer);
+ if ( (as_info_length = RQS_ASINFOLENGTH(buffer)) > 0 )
+ DBG(25, "scsi_request_sense: info '%.*s'\n",
+ as_info_length, RQS_ASINFO(buffer));
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+
+/*---------- scsi_send_system_status() ---------------------------------------*/
+
+static SANE_Status
+scsi_send_system_status(Microtek2_Device *md, int fd)
+{
+ uint8_t cmd[SSS_CMD_L + SSS_DATA_L];
+ uint8_t *pos;
+ int sfd;
+ SANE_Status status;
+
+
+ DBG(30, "scsi_send_system_status: md=%p, fd=%d\n", (void *) md, fd);
+
+ memset(cmd, 0, SSS_CMD_L + SSS_DATA_L);
+ if ( fd == -1 )
+ {
+ status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_send_system_status: open '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+ }
+ else
+ sfd = fd;
+
+ SSS_CMD(cmd);
+ pos = cmd + SSS_CMD_L;
+ SSS_STICK(pos, md->status.stick);
+ SSS_NTRACK(pos, md->status.ntrack);
+ SSS_NCALIB(pos, md->status.ncalib);
+ SSS_TLAMP(pos, md->status.tlamp);
+ SSS_FLAMP(pos, md->status.flamp);
+ SSS_RESERVED17(pos, md->status.reserved17);
+ SSS_RDYMAN(pos, md->status.rdyman);
+ SSS_TRDY(pos, md->status.trdy);
+ SSS_FRDY(pos, md->status.frdy);
+ SSS_ADP(pos, md->status.adp);
+ SSS_DETECT(pos, md->status.detect);
+ SSS_ADPTIME(pos, md->status.adptime);
+ SSS_LENSSTATUS(pos, md->status.lensstatus);
+ SSS_ALOFF(pos, md->status.aloff);
+ SSS_TIMEREMAIN(pos, md->status.timeremain);
+ SSS_TMACNT(pos, md->status.tmacnt);
+ SSS_PAPER(pos, md->status.paper);
+ SSS_ADFCNT(pos, md->status.adfcnt);
+ SSS_CURRENTMODE(pos, md->status.currentmode);
+ SSS_BUTTONCOUNT(pos, md->status.buttoncount);
+
+ if ( md_dump >= 2)
+ {
+ dump_area2(cmd, SSS_CMD_L, "sendsystemstatus");
+ dump_area2(cmd + SSS_CMD_L, SSS_DATA_L, "sendsystemstatusdata");
+ }
+
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_system_status: '%s'\n", sane_strstatus(status));
+
+ if ( fd == -1 )
+ sanei_scsi_close(sfd);
+ return status;
+}
+
+
+/*---------- scsi_sense_handler() --------------------------------------------*/
+/* rewritten 19.12.2001 for better SANE_STATUS return codes */
+
+static SANE_Status
+scsi_sense_handler (int fd, u_char *sense, void *arg)
+{
+ int as_info_length;
+ uint8_t sense_key;
+ uint8_t asl;
+ uint8_t asc;
+ uint8_t ascq;
+
+
+ DBG(30, "scsi_sense_handler: fd=%d, sense=%p arg=%p\n",fd, sense, arg);
+
+ dump_area(sense, RQS_LENGTH(sense), "SenseBuffer");
+
+ sense_key = RQS_SENSEKEY(sense);
+ asl = RQS_ASL(sense);
+ asc = RQS_ASC(sense);
+ ascq = RQS_ASCQ(sense);
+
+ DBG(5, "scsi_sense_handler: SENSE KEY (0x%02x), "
+ "ASC (0x%02x), ASCQ (0x%02x)\n", sense_key, asc, ascq);
+
+ if ( (as_info_length = RQS_ASINFOLENGTH(sense)) > 0 )
+ DBG(5,"scsi_sense_handler: info: '%*s'\n",
+ as_info_length, RQS_ASINFO(sense));
+
+ switch ( sense_key )
+ {
+ case RQS_SENSEKEY_NOSENSE:
+ return SANE_STATUS_GOOD;
+
+ case RQS_SENSEKEY_HWERR:
+ case RQS_SENSEKEY_ILLEGAL:
+ case RQS_SENSEKEY_VENDOR:
+ if ( asc == 0x4a && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Command phase error\n");
+ else if ( asc == 0x2c && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Command sequence error\n");
+ else if ( asc == 0x4b && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Data phase error\n");
+ else if ( asc == 0x40 )
+ {
+ DBG(5, "scsi_sense_handler: Hardware diagnostic failure:\n");
+ switch ( ascq )
+ {
+ case RQS_ASCQ_CPUERR:
+ DBG(5, "scsi_sense_handler: CPU error\n");
+ break;
+ case RQS_ASCQ_SRAMERR:
+ DBG(5, "scsi_sense_handler: SRAM error\n");
+ break;
+ case RQS_ASCQ_DRAMERR:
+ DBG(5, "scsi_sense_handler: DRAM error\n");
+ break;
+ case RQS_ASCQ_DCOFF:
+ DBG(5, "scsi_sense_handler: DC Offset error\n");
+ break;
+ case RQS_ASCQ_GAIN:
+ DBG(5, "scsi_sense_handler: Gain error\n");
+ break;
+ case RQS_ASCQ_POS:
+ DBG(5, "scsi_sense_handler: Positoning error\n");
+ break;
+ default:
+ DBG(5, "scsi_sense_handler: Unknown combination of ASC"
+ " (0x%02x) and ASCQ (0x%02x)\n", asc, ascq);
+ break;
+ }
+ }
+ else if ( asc == 0x00 && ascq == 0x05)
+ {
+ DBG(5, "scsi_sense_handler: End of data detected\n");
+ return SANE_STATUS_EOF;
+ }
+ else if ( asc == 0x3d && ascq == 0x00)
+ DBG(5, "scsi_sense_handler: Invalid bit in IDENTIFY\n");
+ else if ( asc == 0x2c && ascq == 0x02 )
+/* Ok */ DBG(5, "scsi_sense_handler: Invalid comb. of windows specfied\n");
+ else if ( asc == 0x20 && ascq == 0x00 )
+/* Ok */ DBG(5, "scsi_sense_handler: Invalid command opcode\n");
+ else if ( asc == 0x24 && ascq == 0x00 )
+/* Ok */ DBG(5, "scsi_sense_handler: Invalid field in CDB\n");
+ else if ( asc == 0x26 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Invalid field in the param list\n");
+ else if ( asc == 0x49 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Invalid message error\n");
+ else if ( asc == 0x60 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Lamp failure\n");
+ else if ( asc == 0x25 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Unsupported logic. unit\n");
+ else if ( asc == 0x53 && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: ADF paper jam or no paper\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ else if ( asc == 0x54 && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: Media bumping\n");
+ return SANE_STATUS_JAMMED; /* Don't know if this is right! */
+ }
+ else if ( asc == 0x55 && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: Scan Job stopped or cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ else if ( asc == 0x3a && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: Media (ADF or TMA) not available\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ else if ( asc == 0x3a && ascq == 0x01 )
+ {
+ DBG(5, "scsi_sense_handler: Door is not closed\n");
+ return SANE_STATUS_COVER_OPEN;
+ }
+ else if ( asc == 0x3a && ascq == 0x02 )
+ DBG(5, "scsi_sense_handler: Door is not opened\n");
+ else if ( asc == 0x00 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: No additional sense information\n");
+/* Ok */ else if ( asc == 0x1a && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Parameter list length error\n");
+ else if ( asc == 0x26 && ascq == 0x02 )
+ DBG(5, "scsi_sense_handler: Parameter value invalid\n");
+ else if ( asc == 0x03 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Peripheral device write fault - "
+ "Firmware Download Error\n");
+ else if ( asc == 0x2c && ascq == 0x01 )
+ DBG(5, "scsi_sense_handler: Too many windows specified\n");
+ else if ( asc == 0x80 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Target abort scan\n");
+ else if ( asc == 0x96 && ascq == 0x08 )
+ {
+ DBG(5, "scsi_sense_handler: Firewire Device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ else
+ DBG(5, "scsi_sense_handler: Unknown combination of SENSE KEY "
+ "(0x%02x), ASC (0x%02x) and ASCQ (0x%02x)\n",
+ sense_key, asc, ascq);
+
+ return SANE_STATUS_IO_ERROR;
+
+ default:
+ DBG(5, "scsi_sense_handler: Unknown sense key (0x%02x)\n",
+ sense_key);
+ return SANE_STATUS_IO_ERROR;
+ }
+}
+
+
+/*---------- scsi_test_unit_ready() ------------------------------------------*/
+
+static SANE_Status
+scsi_test_unit_ready(Microtek2_Device *md)
+{
+ SANE_Status status;
+ uint8_t tur[TUR_CMD_L];
+ int sfd;
+
+
+ DBG(30, "scsi_test_unit_ready: md=%s\n", md->name);
+
+ TUR_CMD(tur);
+ status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_test_unit_ready: open '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2 )
+ dump_area2(tur, sizeof(tur), "testunitready");
+
+ status = sanei_scsi_cmd(sfd, tur, sizeof(tur), NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_test_unit_ready: cmd '%s'\n", sane_strstatus(status));
+
+ sanei_scsi_close(sfd);
+ return status;
+}
+
+
+/*---------- sane_start() ----------------------------------------------------*/
+
+SANE_Status
+sane_start(SANE_Handle handle)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Microtek2_Scanner *ms = handle;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint8_t *pos;
+ int color, rc, retry;
+
+ DBG(30, "sane_start: handle=0x%p\n", handle);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ ms->n_control_bytes = md->n_control_bytes;
+
+ if ( md->model_flags & MD_READ_CONTROL_BIT )
+ {
+ if (ms->control_bytes) free((void *)ms->control_bytes);
+ ms->control_bytes = (uint8_t *) malloc(ms->n_control_bytes);
+ DBG(100, "sane_start: ms->control_bytes=%p, malloc'd %lu bytes\n",
+ ms->control_bytes, (u_long) ms->n_control_bytes);
+ if ( ms->control_bytes == NULL )
+ {
+ DBG(1, "sane_start: malloc() for control bits failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+
+ if (ms->sfd < 0) /* first or only pass of this scan */
+ {
+ /* open device */
+ for ( retry = 0; retry < 10; retry++ )
+ {
+ status = sanei_scsi_open (md->sane.name, &ms->sfd,
+ scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_DEVICE_BUSY )
+ break;
+ DBG(30, "sane_start: Scanner busy, trying again\n");
+ sleep(1);
+ }
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "sane_start: scsi_open: '%s'\n", sane_strstatus(status));
+ goto cleanup;
+ }
+
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ if ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE )
+ DBG(30, "sane_start: backend calibration on\n");
+ else
+ DBG(30, "sane_start: backend calibration off\n");
+
+ if ( ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE )
+ && !( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) )
+ {
+ /* Read shading only once - possible with CIS scanners */
+ /* assuming only CIS scanners use Controlbits */
+ if ( ( md->shading_table_w == NULL )
+ || !( md->model_flags & MD_READ_CONTROL_BIT ) )
+ {
+ status = get_scan_parameters(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = read_shading_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+ }
+
+ status = get_scan_parameters(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ md->status.aloff |= 128;
+ md->status.timeremain = 10;
+
+ if ( ms->scan_source == MS_SOURCE_FLATBED
+ || ms->scan_source == MS_SOURCE_ADF )
+ {
+ md->status.flamp |= MD_FLAMP_ON;
+ md->status.tlamp &= ~MD_TLAMP_ON;
+ }
+ else
+ {
+ md->status.flamp &= ~MD_FLAMP_ON;
+ md->status.tlamp |= MD_TLAMP_ON;
+ }
+
+ if ( ms->lightlid35 )
+ {
+ md->status.flamp &= ~MD_FLAMP_ON;
+/* md->status.tlamp |= MD_TLAMP_ON;*/
+/* with this line on some scanners (X6, 0x91) the Flamp goes on */
+ }
+
+ if ( ms->no_backtracking )
+ md->status.ntrack |= MD_NTRACK_ON;
+ else
+ md->status.ntrack &= ~MD_NTRACK_ON;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ /* calculate gamma: we assume, that the gamma values are transferred */
+ /* with one send gamma command, even if it is a 3 pass scanner */
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ ms->lut_size = (int) pow(2.0, (double) ms->depth);
+ ms->lut_entry_size = ms->depth > 8 ? 2 : 1;
+ }
+ else
+ {
+ get_lut_size(mi, &ms->lut_size, &ms->lut_entry_size);
+ }
+ ms->lut_size_bytes = ms->lut_size * ms->lut_entry_size;
+ ms->word = (ms->lut_entry_size == 2);
+
+ ms->gamma_table = (uint8_t *) malloc(3 * ms->lut_size_bytes );
+ DBG(100, "sane_start: ms->gamma_table=%p, malloc'd %d bytes\n",
+ ms->gamma_table, 3 * ms->lut_size_bytes);
+ if ( ms->gamma_table == NULL )
+ {
+ DBG(1, "sane_start: malloc for gammatable failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ pos = ms->gamma_table + color * ms->lut_size_bytes;
+ calculate_gamma(ms, pos, color, ms->gamma_mode);
+ }
+
+ /* Some models ignore the settings for the exposure time, */
+ /* so we must do it ourselves. Apparently this seems to be */
+ /* the case for all models that have the chunky data format */
+
+ if ( mi->data_format == MI_DATAFMT_CHUNKY )
+ set_exposure(ms);
+
+ if ( ! (md->model_flags & MD_NO_GAMMA) )
+ {
+ status = scsi_send_gamma(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ status = scsi_set_window(ms, 1);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ ms->scanning = SANE_TRUE;
+ ms->cancelled = SANE_FALSE;
+ }
+
+ ++ms->current_pass;
+
+ status = scsi_read_image_info(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = prepare_buffers(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = calculate_sane_params(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ if ( !( md->model_flags & MD_NO_RIS_COMMAND ) )
+ {
+ /* !!FIXME!! - hack for C6USB because RIS over USB doesn't wait until */
+ /* scanner ready */
+ if (mi->model_code == 0x9a)
+ sleep(2);
+
+ status = scsi_wait_for_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ if ( ms->calib_backend
+ && ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ && ( ( md->shading_table_w == NULL )
+ || ( ms->mode != md->shading_table_contents )
+ )
+ )
+ {
+ status = read_cx_shading(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ if ( ms->lightlid35 )
+ /* hopefully this leads to a switched off flatbed lamp with lightlid */
+ {
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ md->status.flamp &= ~MD_FLAMP_ON;
+ md->status.tlamp &= ~MD_TLAMP_ON;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ if ( md->model_flags & MD_READ_CONTROL_BIT )
+ {
+ status = scsi_read_control_bits(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ if ( ms->calib_backend )
+ {
+ status = condense_shading(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+ }
+
+ /* open a pipe and fork a child process, that actually reads the data */
+ rc = pipe(ms->fd);
+ if ( rc == -1 )
+ {
+ DBG(1, "sane_start: pipe failed\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* create reader routine as new thread or process */
+ ms->pid = sanei_thread_begin( reader_process,(void*) ms);
+
+ if ( ms->pid == -1 )
+ {
+ DBG(1, "sane_start: fork failed\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ if (sanei_thread_is_forked()) close(ms->fd[1]);
+
+ return SANE_STATUS_GOOD;
+
+cleanup:
+ cleanup_scanner(ms);
+ return status;
+}
+
+/*---------- prepare_buffers -------------------------------------------------*/
+
+static SANE_Status
+prepare_buffers(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t strip_lines;
+ int i;
+
+ status = SANE_STATUS_GOOD;
+ DBG(30, "prepare_buffers: ms=0x%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ /* calculate maximum number of lines to read */
+ strip_lines = (int) ((double) ms->y_resolution_dpi * md->opts.strip_height);
+ if ( strip_lines == 0 )
+ strip_lines = 1;
+
+ /* calculate number of lines that fit into the source buffer */
+#ifdef TESTBACKEND
+ ms->src_max_lines = MIN( 5000000 / ms->bpl, strip_lines);
+#else
+ ms->src_max_lines = MIN( sanei_scsi_max_request_size / ms->bpl, strip_lines);
+#endif
+ if ( ms->src_max_lines == 0 )
+ {
+ DBG(1, "sane_start: Scan buffer too small\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* allocate buffers */
+ ms->src_buffer_size = ms->src_max_lines * ms->bpl;
+
+ if ( ms->mode == MS_MODE_COLOR && mi->data_format == MI_DATAFMT_LPLSEGREG )
+ {
+ /* In this case the data is not neccessarily in the order RGB */
+ /* and there may be different numbers of read red, green and blue */
+ /* segments. We allocate a second buffer to read new lines in */
+ /* and hold undelivered pixels in the other buffer */
+ int extra_buf_size;
+
+ extra_buf_size = 2 * ms->bpl * mi->ccd_gap
+ * (int) ceil( (double) mi->max_yresolution
+ / (double) mi->opt_resolution);
+ for ( i = 0; i < 2; i++ )
+ {
+ if ( ms->buf.src_buffer[i] )
+ free((void *) ms->buf.src_buffer[i]);
+ ms->buf.src_buffer[i] = (uint8_t *) malloc(ms->src_buffer_size
+ + extra_buf_size);
+ DBG(100, "prepare_buffers: ms->buf.src_buffer[%d]=%p,"
+ "malloc'd %d bytes\n", i, ms->buf.src_buffer[i],
+ ms->src_buffer_size + extra_buf_size);
+ if ( ms->buf.src_buffer[i] == NULL )
+ {
+ DBG(1, "sane_start: malloc for scan buffer failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+ ms->buf.free_lines = ms->src_max_lines + extra_buf_size / ms->bpl;
+ ms->buf.free_max_lines = ms->buf.free_lines;
+ ms->buf.src_buf = ms->buf.src_buffer[0];
+ ms->buf.current_src = 0; /* index to current buffer */
+ }
+ else
+ {
+ if ( ms->buf.src_buf )
+ free((void *) ms->buf.src_buf);
+ ms->buf.src_buf = malloc(ms->src_buffer_size);
+ DBG(100, "sane_start: ms->buf.src_buf=%p, malloc'd %d bytes\n",
+ ms->buf.src_buf, ms->src_buffer_size);
+ if ( ms->buf.src_buf == NULL )
+ {
+ DBG(1, "sane_start: malloc for scan buffer failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+
+ for ( i = 0; i < 3; i++ )
+ {
+ ms->buf.current_pos[i] = ms->buf.src_buffer[0];
+ ms->buf.planes[0][i] = 0;
+ ms->buf.planes[1][i] = 0;
+ }
+
+ /* allocate a temporary buffer for the data, if auto_adjust threshold */
+ /* is selected. */
+
+ if ( ms->auto_adjust == 1 )
+ {
+ ms->temporary_buffer = (uint8_t *) malloc(ms->remaining_bytes);
+ DBG(100, "sane_start: ms->temporary_buffer=%p, malloc'd %d bytes\n",
+ ms->temporary_buffer, ms->remaining_bytes);
+ if ( ms->temporary_buffer == NULL )
+ {
+ DBG(1, "sane_start: malloc() for temporary buffer failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+ else
+ ms->temporary_buffer = NULL;
+
+ /* some data formats have additional information in a scan line, which */
+ /* is not transferred to the frontend; real_bpl is the number of bytes */
+ /* per line, that is copied into the frontend's buffer */
+ ms->real_bpl = (uint32_t) ceil( ((double) ms->ppl *
+ (double) ms->bits_per_pixel_out) / 8.0 );
+ if ( mi->onepass && ms->mode == MS_MODE_COLOR )
+ ms->real_bpl *= 3;
+
+ ms->real_remaining_bytes = ms->real_bpl * ms->src_remaining_lines;
+
+ return SANE_STATUS_GOOD;
+
+cleanup:
+ cleanup_scanner(ms);
+ return status;
+
+}
+static void
+write_shading_buf_pnm(Microtek2_Scanner *ms, uint32_t lines)
+{
+ FILE *outfile;
+ uint16_t pixel, color, linenr, factor;
+ unsigned char img_val_out;
+ float img_val = 0;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ factor = 256;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ factor = 64;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ factor = 16;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ factor = 4;
+ else
+ factor = 1;
+ if ( md->model_flags & MD_16BIT_TRANSFER )
+ factor = 256;
+
+ outfile = fopen("shading_buf_w.pnm", "w");
+ fprintf(outfile, "P6\n#imagedata\n%d %d\n255\n",
+ mi->geo_width / mi->calib_divisor, lines);
+ for ( linenr=0; linenr < lines; linenr++ )
+ {
+ if (mi->data_format == MI_DATAFMT_LPLSEGREG)
+ {
+ DBG(1, "Output of shading buffer unsupported for"
+ "Segreg Data format\n");
+ break;
+ }
+
+ for ( pixel=0;
+ pixel < (uint16_t) (mi->geo_width / mi->calib_divisor);
+ pixel++)
+ {
+ for ( color=0; color < 3; color++ )
+ {
+ switch( mi->data_format )
+ {
+ case MI_DATAFMT_LPLCONCAT:
+ if ( md->shading_depth > 8)
+ img_val = *((uint16_t *) ms->shading_image
+ + linenr * ( ms->bpl / ms->lut_entry_size )
+ + mi->color_sequence[color]
+ * ( ms->bpl / ms->lut_entry_size / 3 )
+ + pixel);
+ else
+ img_val = *((uint8_t *) ms->shading_image
+ + linenr * ( ms->bpl / ms->lut_entry_size )
+ + mi->color_sequence[color]
+ * ( ms->bpl / ms->lut_entry_size / 3 )
+ + pixel);
+
+ break;
+ case MI_DATAFMT_CHUNKY:
+ case MI_DATAFMT_9800:
+ img_val = *((uint16_t *)ms->shading_image
+ + linenr * 3 * ( mi->geo_width
+ / mi->calib_divisor )
+ + 3 * pixel
+ + mi->color_sequence[color]);
+ break;
+ }
+ img_val /= factor;
+ img_val_out = (unsigned char)img_val;
+ fputc(img_val_out, outfile);
+ }
+ }
+ }
+ fclose(outfile);
+
+ return;
+}
+
+static void
+write_shading_pnm(Microtek2_Scanner *ms)
+{
+ FILE *outfile_w = NULL, *outfile_d = NULL;
+ int pixel, color, line, offset, num_shading_pixels, output_height;
+ uint16_t img_val, factor;
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+ output_height = 180;
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ DBG(30, "write_shading_pnm: ms=%p\n", (void *) ms);
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ factor = 256;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ factor = 64;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ factor = 16;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ factor = 4;
+ else
+ factor = 1;
+ if ( md->model_flags & MD_16BIT_TRANSFER )
+ factor = 256;
+
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ num_shading_pixels = ms->n_control_bytes * 8;
+ else
+ num_shading_pixels = mi->geo_width / mi->calib_divisor;
+ if ( md->shading_table_w != NULL )
+ {
+ outfile_w = fopen("microtek2_shading_w.pnm", "w");
+ fprintf(outfile_w, "P6\n#imagedata\n%d %d\n255\n",
+ num_shading_pixels, output_height);
+ }
+ if ( md->shading_table_d != NULL )
+ {
+ outfile_d = fopen("microtek2_shading_d.pnm", "w");
+ fprintf(outfile_d, "P6\n#imagedata\n%d %d\n255\n",
+ num_shading_pixels, output_height);
+ }
+ for ( line=0; line < output_height; ++line )
+ {
+ for ( pixel=0; pixel < num_shading_pixels ; ++pixel)
+ {
+ for ( color=0; color < 3; ++color )
+ {
+ offset = mi->color_sequence[color]
+ * num_shading_pixels
+ + pixel;
+ if ( md->shading_table_w != NULL )
+ {
+ if ( ms->lut_entry_size == 2 )
+ {
+ img_val = *((uint16_t *) md->shading_table_w + offset );
+ img_val /= factor;
+ }
+ else
+ img_val = *((uint8_t *) md->shading_table_w + offset );
+ fputc((unsigned char)img_val, outfile_w);
+ }
+
+ if ( md->shading_table_d != NULL )
+ {
+ if ( ms->lut_entry_size == 2 )
+ {
+ img_val = *((uint16_t *) md->shading_table_d + offset );
+ img_val /= factor;
+ }
+ else
+ img_val = *((uint8_t *) md->shading_table_d + offset );
+ fputc((unsigned char)img_val, outfile_d);
+ }
+ }
+ }
+ }
+ if ( md->shading_table_w != NULL )
+ fclose(outfile_w);
+ if ( md->shading_table_d != NULL )
+ fclose(outfile_d);
+
+ return;
+}
+
+static void
+write_cshading_pnm(Microtek2_Scanner *ms)
+{
+ FILE *outfile;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ int pixel, color, line, offset, img_val, img_height=30, factor;
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ factor = 256;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ factor = 64;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ factor = 16;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ factor = 4;
+ else
+ factor = 1;
+ if ( md->model_flags & MD_16BIT_TRANSFER )
+ factor = 256;
+
+ outfile = fopen("microtek2_cshading_w.pnm", "w");
+ if ( ms->mode == MS_MODE_COLOR )
+ fprintf(outfile, "P6\n#imagedata\n%d %d\n255\n", ms->ppl, img_height);
+ else
+ fprintf(outfile, "P5\n#imagedata\n%d %d\n255\n", ms->ppl, img_height);
+
+ for ( line=0; line < img_height; ++line )
+ {
+ for ( pixel=0; pixel < (int)ms->ppl; ++pixel)
+ {
+ for ( color=0; color < 3; ++color )
+ {
+ offset = color * (int)ms->ppl + pixel;
+ if ( ms->lut_entry_size == 1 )
+ img_val = (int) *((uint8_t *)ms->condensed_shading_w + offset);
+ else
+ {
+ img_val = (int) *((uint16_t *)ms->condensed_shading_w
+ + offset);
+ img_val /= factor;
+ }
+ fputc((unsigned char)img_val, outfile);
+ if ( ms->mode == MS_MODE_GRAY )
+ break;
+ }
+ }
+ }
+ fclose(outfile);
+
+ return;
+}
+
+
+
+/*---------- condense_shading() ----------------------------------------------*/
+
+static SANE_Status
+condense_shading(Microtek2_Scanner *ms)
+{
+ /* This function extracts the relevant shading pixels from */
+ /* the shading image according to the 1's in the result of */
+ /* 'read control bits', and stores them in a memory block. */
+ /* We will then have as many shading pixels as there are */
+ /* pixels per line. The order of the pixels in the condensed */
+ /* shading data block will always be left to right. The color */
+ /* sequence remains unchanged. */
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t byte;
+ uint32_t cond_length; /* bytes per condensed shading line */
+ int color, count, lfd_bit;
+ int shad_bplc, shad_pixels; /* bytes per line & color in shading image */
+ int bit, flag;
+ uint32_t sh_offset, csh_offset;
+ int gray_filter_color = 1; /* which color of the shading is taken for gray*/
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ DBG(30, "condense_shading: ms=%p, ppl=%d\n", (void *) ms, ms->ppl);
+ if ( md->shading_table_w == NULL )
+ {
+ DBG(1, "condense shading: no shading table found, skip shading\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ get_lut_size( mi, &ms->lut_size, &ms->lut_entry_size );
+
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ {
+ shad_pixels = ms->n_control_bytes * 8;
+ gray_filter_color = 0; /* 336CX reads only one shading in gray mode*/
+ }
+ else
+ shad_pixels = mi->geo_width;
+
+ shad_bplc = shad_pixels * ms->lut_entry_size;
+
+ if ( md_dump >= 3 )
+ {
+ dump_area2(md->shading_table_w, shad_bplc * 3, "shading_table_w");
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ write_shading_pnm(ms);
+ }
+
+ cond_length = ms->bpl * ms->lut_entry_size;
+
+ if ( ms->condensed_shading_w )
+ {
+ free((void*) ms->condensed_shading_w );
+ ms->condensed_shading_w = NULL;
+ }
+ ms->condensed_shading_w = (uint8_t *)malloc(cond_length);
+ DBG(100, "condense_shading: ms->condensed_shading_w=%p,"
+ "malloc'd %d bytes\n", ms->condensed_shading_w, cond_length);
+ if ( ms->condensed_shading_w == NULL )
+ {
+ DBG(1, "condense_shading: malloc for white table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if ( md->shading_table_d != NULL )
+ {
+ if ( md_dump >= 3 )
+ dump_area2(md->shading_table_d, shad_bplc * 3,
+ "shading_table_d");
+
+ if ( ms->condensed_shading_d )
+ {
+ free((void*) ms->condensed_shading_d );
+ ms->condensed_shading_d = NULL;
+ }
+ ms->condensed_shading_d = (uint8_t *)malloc(cond_length);
+ DBG(100, "condense_shading: ms->condensed_shading_d=%p,"
+ " malloc'd %d bytes\n", ms->condensed_shading_d, cond_length);
+ if ( ms->condensed_shading_d == NULL )
+ {
+ DBG(1, "condense_shading: malloc for dark table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ DBG(128, "controlbit offset=%d\n", md->controlbit_offset);
+
+ count = 0;
+
+ for (lfd_bit = 0; ( lfd_bit < mi->geo_width ) && ( count < (int)ms->ppl );
+ ++lfd_bit)
+ {
+ byte = ( lfd_bit + md->controlbit_offset ) / 8;
+ bit = ( lfd_bit + md->controlbit_offset ) % 8;
+
+ if ( mi->direction & MI_DATSEQ_RTOL )
+ flag = ((ms->control_bytes[byte] >> bit) & 0x01);
+ else
+ flag = ((ms->control_bytes[byte] >> (7 - bit)) & 0x01);
+
+ if ( flag == 1 ) /* flag==1 if byte's bit is set */
+ {
+ for ( color = 0; color < 3; ++color )
+ {
+ if ( ( ms->mode == MS_MODE_COLOR )
+ || ( ( ms->mode == MS_MODE_GRAY )
+ && ( color == gray_filter_color ) )
+ || ( ( ms->mode == MS_MODE_LINEARTFAKE )
+ && ( color == gray_filter_color ) )
+ )
+ {
+ sh_offset = color * shad_pixels + lfd_bit;
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ sh_offset += md->controlbit_offset;
+ if ( ms->mode == MS_MODE_COLOR )
+ csh_offset = color * ms->ppl + count;
+ else
+ csh_offset = count;
+
+ if ( csh_offset > cond_length )
+ {
+ DBG(1, "condense_shading: wrong control bits data, " );
+ DBG(1, "csh_offset (%d) > cond_length(%d)\n",
+ csh_offset, cond_length );
+ csh_offset = cond_length;
+ }
+
+ if ( ms->lut_entry_size == 2 )
+ {
+ *((uint16_t *)ms->condensed_shading_w + csh_offset) =
+ *((uint16_t *)md->shading_table_w
+ + sh_offset);
+ if ( ms->condensed_shading_d != NULL )
+ *((uint16_t *)ms->condensed_shading_d + csh_offset) =
+ *((uint16_t *)md->shading_table_d
+ + sh_offset);
+ }
+ else
+ {
+ *((uint8_t *)ms->condensed_shading_w + csh_offset) =
+ *((uint8_t *)md->shading_table_w
+ + sh_offset);
+ if ( ms->condensed_shading_d != NULL )
+ *((uint8_t *)ms->condensed_shading_d + csh_offset) =
+ *((uint8_t *)md->shading_table_d
+ + sh_offset);
+ }
+ }
+ }
+ ++count;
+ }
+ }
+
+ if ( md_dump >= 3 )
+ {
+ dump_area2(ms->condensed_shading_w, cond_length, "condensed_shading_w");
+ if ( ms->condensed_shading_d != NULL )
+ dump_area2(ms->condensed_shading_d, cond_length,
+ "condensed_shading_d");
+
+ write_cshading_pnm(ms);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- read_shading_image() --------------------------------------------*/
+
+static SANE_Status
+read_shading_image(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t lines;
+ uint8_t *buf;
+ int max_lines;
+ int lines_to_read;
+
+ DBG(30, "read_shading_image: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+
+ if ( ! MI_WHITE_SHADING_ONLY(mi->shtrnsferequ)
+ || ( md->model_flags & MD_PHANTOM_C6 ) )
+
+ /* Dark shading correction */
+ /* ~~~~~~~~~~~~~~~~~~~~~~~ */
+ {
+ DBG(30, "read_shading_image: reading black data\n");
+ md->status.ntrack |= MD_NTRACK_ON;
+ md->status.ncalib &= ~MD_NCALIB_ON;
+ md->status.flamp |= MD_FLAMP_ON;
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ {
+ md->status.stick |= MD_STICK_ON;
+ md->status.reserved17 |= MD_RESERVED17_ON;
+ }
+
+ get_calib_params(ms);
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ ms->stay = 1;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_set_window(ms, 1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+#ifdef TESTBACKEND
+ status = scsi_read_sh_image_info(ms);
+#else
+ status = scsi_read_image_info(ms);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_wait_for_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ md->status.flamp &= ~MD_FLAMP_ON;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->shading_image = malloc(ms->bpl * ms->src_remaining_lines);
+ DBG(100, "read shading image: ms->shading_image=%p,"
+ " malloc'd %d bytes\n",
+ ms->shading_image, ms->bpl * ms->src_remaining_lines);
+ if ( ms->shading_image == NULL )
+ {
+ DBG(1, "read_shading_image: malloc for buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+
+#ifdef TESTBACKEND
+ max_lines = 5000000 / ms->bpl;
+#else
+ max_lines = sanei_scsi_max_request_size / ms->bpl;
+#endif
+ if ( max_lines == 0 )
+ {
+ DBG(1, "read_shading_image: buffer too small\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ lines = ms->src_remaining_lines;
+ while ( ms->src_remaining_lines > 0 )
+ {
+ lines_to_read = MIN(max_lines, ms->src_remaining_lines);
+ ms->src_buffer_size = lines_to_read * ms->bpl;
+ ms->transfer_length = ms->src_buffer_size;
+#ifdef TESTBACKEND
+ status = scsi_read_sh_d_image(ms, buf);
+#else
+ status = scsi_read_image(ms, buf, md->shading_depth>8 ? 2 : 1);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "read_shading_image: read image failed: '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ ms->src_remaining_lines -= lines_to_read;
+ buf += ms->src_buffer_size;
+ }
+
+ status = prepare_shading_data(ms, lines, &md->shading_table_d);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ /* send shading data to the device */
+ /* Some models use "read_control bit", and the shading must be */
+ /* applied by the backend later */
+ if ( ! (md->model_flags & MD_READ_CONTROL_BIT) )
+ {
+ status = shading_function(ms, md->shading_table_d);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->word = ms->lut_entry_size == 2 ? 1 : 0;
+ ms->current_color = MS_COLOR_ALL;
+ status = scsi_send_shading(ms,
+ md->shading_table_d,
+ 3 * ms->lut_entry_size
+ * mi->geo_width / mi->calib_divisor,
+ 1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ DBG(100, "free memory for ms->shading_image at %p\n",
+ ms->shading_image);
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+ }
+
+ /* white shading correction */
+ /* ~~~~~~~~~~~~~~~~~~~~~~~~ */
+ DBG(30, "read_shading_image: reading white data\n");
+
+ /* According to the doc NCalib must be set for white shading data */
+ /* if we have a black and a white shading correction and must be */
+ /* cleared if we have only a white shading collection */
+ if ( ! MI_WHITE_SHADING_ONLY(mi->shtrnsferequ)
+ || ( md->model_flags & MD_PHANTOM_C6 ) )
+ md->status.ncalib |= MD_NCALIB_ON;
+ else
+ md->status.ncalib &= ~MD_NCALIB_ON;
+
+ md->status.flamp |= MD_FLAMP_ON;
+/* md->status.tlamp &= ~MD_TLAMP_ON; */
+ md->status.ntrack |= MD_NTRACK_ON;
+
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ {
+ md->status.stick &= ~MD_STICK_ON;
+ md->status.reserved17 |= MD_RESERVED17_ON;
+ }
+
+ get_calib_params(ms);
+
+#ifdef NO_PHANTOMTYPE_SHADING
+/* md->status.stick &= ~MD_STICK_ON; */
+/* md->status.ncalib &= ~MD_NCALIB_ON; */
+/* md->status.reserved17 &= ~MD_RESERVED17_ON; */
+ ms->rawdat = 0;
+#endif
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_set_window(ms, 1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+#ifdef TESTBACKEND
+ status = scsi_read_sh_image_info(ms);
+#else
+ status = scsi_read_image_info(ms);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_wait_for_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+#ifdef NO_PHANTOMTYPE_SHADING
+ if ( !( md->model_flags & MD_READ_CONTROL_BIT ) )
+ {
+#endif
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+#ifdef NO_PHANTOMTYPE_SHADING
+ }
+#endif
+
+#ifdef NO_PHANTOMTYPE_SHADING
+ if ( mi->model_code == 0x94 )
+ status = scsi_read_control_bits(ms);
+#endif
+
+ ms->shading_image = malloc(ms->bpl * ms->src_remaining_lines);
+ DBG(100, "read shading image: ms->shading_image=%p, malloc'd %d bytes\n",
+ ms->shading_image, ms->bpl * ms->src_remaining_lines);
+ if ( ms->shading_image == NULL )
+ {
+ DBG(1, "read_shading_image: malloc for buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+#ifdef TESTBACKEND
+ max_lines = 5000000 / ms->bpl;
+#else
+ max_lines = sanei_scsi_max_request_size / ms->bpl;
+#endif
+ if ( max_lines == 0 )
+ {
+ DBG(1, "read_shading_image: buffer too small\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ lines = ms->src_remaining_lines;
+ while ( ms->src_remaining_lines > 0 )
+ {
+ lines_to_read = MIN(max_lines, ms->src_remaining_lines);
+ ms->src_buffer_size = lines_to_read * ms->bpl;
+ ms->transfer_length = ms->src_buffer_size;
+
+#ifdef TESTBACKEND
+ status = scsi_read_sh_w_image(ms, buf);
+#else
+ status = scsi_read_image(ms, buf, md->shading_depth>8 ? 2 : 1);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->src_remaining_lines -= lines_to_read;
+ buf += ms->src_buffer_size;
+ }
+
+ status = prepare_shading_data(ms, lines, &md->shading_table_w);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ if ( md_dump >= 3 )
+ {
+ write_shading_buf_pnm(ms, lines);
+ write_shading_pnm(ms);
+ }
+
+ /* send shading data to the device */
+ /* Some models use "read_control bit", and the shading must be */
+ /* applied by the backend later */
+ if ( ! (md->model_flags & MD_READ_CONTROL_BIT) )
+ {
+ status = shading_function(ms, md->shading_table_w);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->word = ms->lut_entry_size == 2 ? 1 : 0;
+ ms->current_color = MS_COLOR_ALL;
+ status = scsi_send_shading(ms,
+ md->shading_table_w,
+ 3 * ms->lut_entry_size
+ * mi->geo_width / mi->calib_divisor,
+ 0);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ ms->rawdat = 0;
+ ms->stay = 0;
+ md->status.ncalib |= MD_NCALIB_ON;
+
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ {
+ md->status.stick &= ~MD_STICK_ON;
+ md->status.reserved17 &= ~MD_RESERVED17_ON;
+ }
+
+#ifdef NO_PHANTOMTYPE_SHADING
+ if (mi->model_code == 0x94)
+ md->status.ncalib &= ~MD_NCALIB_ON;
+#endif
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ DBG(100, "free memory for ms->shading_image at %p\n",
+ ms->shading_image);
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/*---------- prepare_shading_data() ------------------------------------------*/
+
+static SANE_Status
+prepare_shading_data(Microtek2_Scanner *ms, uint32_t lines, uint8_t **data)
+{
+ /* This function calculates one line of black or white shading data */
+ /* from the shading image. At the end we have one line. The */
+ /* color sequence is unchanged. */
+
+#define MICROTEK2_CALIB_USE_MEDIAN
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t length,line;
+ int color, i;
+ SANE_Status status;
+
+#ifdef MICROTEK2_CALIB_USE_MEDIAN
+ uint16_t *sortbuf, value;
+#else
+ uint32_t value;
+#endif
+
+ DBG(30, "prepare_shading_data: ms=%p, lines=%d, *data=%p\n",
+ (void *) ms, lines, *data);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ status = SANE_STATUS_GOOD;
+
+ get_lut_size(mi, &ms->lut_size, &ms->lut_entry_size);
+ length = 3 * ms->lut_entry_size * mi->geo_width / mi->calib_divisor;
+
+ if ( *data == NULL )
+ {
+ *data = (uint8_t *) malloc(length);
+ DBG(100, "prepare_shading_data: malloc'd %d bytes at %p\n",
+ length, *data);
+ if ( *data == NULL )
+ {
+ DBG(1, "prepare_shading_data: malloc for shading table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+#ifdef MICROTEK2_CALIB_USE_MEDIAN
+ sortbuf = malloc( lines * ms->lut_entry_size );
+ DBG(100, "prepare_shading_data: sortbuf= %p, malloc'd %d Bytes\n",
+ (void *) sortbuf, lines * ms->lut_entry_size);
+ if ( sortbuf == NULL )
+ {
+ DBG(1, "prepare_shading_data: malloc for sort buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+#endif
+
+ switch( mi->data_format )
+ {
+ case MI_DATAFMT_LPLCONCAT:
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "prepare_shading_data: wordsize == 1 unsupported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ )
+ {
+ value = 0;
+ for ( line = 0; line < lines; line++ )
+#ifndef MICROTEK2_CALIB_USE_MEDIAN
+/* average the shading lines to get the shading data */
+ value += *((uint16_t *) ms->shading_image
+ + line * ( ms->bpl / ms->lut_entry_size )
+ + color * ( ms->bpl / ms->lut_entry_size / 3 )
+ + i);
+ value /= lines;
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+#else
+/* use a median filter to get the shading data -- should be better */
+ *(sortbuf + line ) =
+ *((uint16_t *) ms->shading_image
+ + line * ( ms->bpl / ms->lut_entry_size )
+ + color * ( ms->bpl / ms->lut_entry_size / 3 )
+ + i);
+ qsort(sortbuf, lines, sizeof(uint16_t),
+ (qsortfunc)compare_func_16);
+ value = *(sortbuf + ( lines - 1 ) / 2 );
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) = value;
+#endif
+ }
+ }
+ break;
+
+ case MI_DATAFMT_CHUNKY:
+ case MI_DATAFMT_9800:
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "prepare_shading_data: wordsize == 1 unsupported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ )
+ {
+ value = 0;
+ for ( line = 0; line < lines; line++ )
+#ifndef MICROTEK2_CALIB_USE_MEDIAN
+/* average the shading lines to get the shading data */
+ value += *((uint16_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+
+ value /= lines;
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+#else
+/* use a median filter to get the shading data -- should be better */
+ *(sortbuf + line ) =
+ *((uint16_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+ qsort(sortbuf, lines, sizeof(uint16_t),
+ (qsortfunc)compare_func_16);
+ value = *(sortbuf + ( lines - 1 ) / 2 );
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) = value;
+#endif
+ }
+ }
+ break;
+
+ case MI_DATAFMT_LPLSEGREG:
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ )
+ {
+ value = 0;
+ if ( ms->lut_entry_size == 1 )
+ {
+ for ( line = 0; line < lines; line++ )
+ value += *((uint8_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+
+ value /= lines;
+ *((uint8_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint8_t) MIN(0xff, value);
+
+ }
+ else
+ {
+ for ( line = 0; line < lines; line++ )
+ value += *((uint16_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+
+ value /= lines;
+#ifndef MICROTEK2_CALIB_USE_MEDIAN
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+#else
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) = value;
+#endif
+ }
+
+ }
+ }
+ break;
+
+ default:
+ DBG(1, "prepare_shading_data: Unsupported data format 0x%02x\n",
+ mi->data_format);
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+#ifdef MICROTEK2_CALIB_USE_MEDIAN
+ DBG(100, "prepare_shading_data: free sortbuf at %p\n", (void *) sortbuf);
+ free(sortbuf);
+ sortbuf = NULL;
+#endif
+ return status;
+}
+
+
+/*---------- read_cx_shading() -----------------------------------------------*/
+
+static SANE_Status
+read_cx_shading(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ md = ms->dev;
+
+ DBG(30, "read_cx_shading: ms=%p\n",(void *) ms);
+
+ md->shading_table_contents = ms->mode;
+
+ if ( ms->mode == MS_MODE_COLOR )
+ ms->current_color = MS_COLOR_ALL;
+ else
+ ms->current_color = MS_COLOR_GREEN; /* for grayscale */
+
+ ms->word = 1;
+ ms->dark = 0;
+
+ status = read_cx_shading_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ ms->word = 0; /* the Windows driver reads dark shading with word=0 */
+ ms->dark = 1;
+ status = read_cx_shading_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ return SANE_STATUS_GOOD;
+
+cleanup:
+ cleanup_scanner(ms);
+ return status;
+}
+
+
+/*---------- read_cx_shading_image() -----------------------------------------*/
+
+static SANE_Status
+read_cx_shading_image(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ uint32_t shading_bytes, linesize, buffer_size;
+ uint8_t *buf;
+ int max_lines, lines_to_read, remaining_lines;
+
+ md = ms->dev;
+
+ shading_bytes = ms->n_control_bytes * 8 * md->shading_length;
+ if ( ms->current_color == MS_COLOR_ALL )
+ shading_bytes *= 3;
+ if ( ms->word == 1 )
+ shading_bytes *= 2;
+
+ if ( ms->shading_image )
+ {
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+ }
+ ms->shading_image = malloc(shading_bytes);
+ DBG(100, "read_cx_shading: ms->shading_image=%p, malloc'd %d bytes\n",
+ ms->shading_image, shading_bytes);
+ if ( ms->shading_image == NULL )
+ {
+ DBG(1, "read_cx_shading: malloc for cx_shading buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+
+ DBG(30, "read_cx_shading_image: ms=%p, shading_bytes=%d\n",
+ (void *) ms, shading_bytes);
+
+ linesize = shading_bytes / md->shading_length;
+#ifdef TESTBACKEND
+ max_lines = 5000000 / linesize;
+#else
+ max_lines = sanei_scsi_max_request_size / linesize;
+#endif
+ /* the following part is like in "read_shading_image" */
+ remaining_lines = md->shading_length;
+ while ( remaining_lines > 0 )
+ {
+ lines_to_read = MIN(max_lines, remaining_lines);
+ buffer_size = lines_to_read * linesize;
+
+ status = scsi_read_shading(ms, buf, buffer_size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "read_cx_shading: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+ remaining_lines -= lines_to_read;
+ buf += buffer_size;
+ }
+
+ status = calc_cx_shading_line(ms);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "read_cx_shading: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( ms->shading_image )
+ {
+ DBG(100, "free memory for ms->shading_image at %p\n",
+ ms->shading_image);
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+ }
+
+ return status;
+}
+
+/*---------- calc_cx_shading_line() ------------------------------------------*/
+/* calculates the mean value of the shading lines and stores one line of */
+/* 8-bit shading data. Scanning direction + color sequence remain as they are */
+/* ToDo: more than 8-bit data */
+
+static SANE_Status
+calc_cx_shading_line(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ SANE_Status status;
+ uint8_t *current_byte, *buf, *shading_table_pointer;
+ uint8_t color, factor;
+ uint32_t shading_line_pixels, shading_line_bytes,
+ shading_data_bytes, line, i, accu, color_offset;
+ uint16_t *sortbuf, value;
+
+ md = ms->dev;
+ status = SANE_STATUS_GOOD;
+
+ sortbuf = malloc( md->shading_length * sizeof(float) );
+ DBG(100, "calc_cx_shading: sortbuf= %p, malloc'd %lu Bytes\n",
+ (void *) sortbuf, (u_long) (md->shading_length * sizeof(float)));
+ if ( sortbuf == NULL )
+ {
+ DBG(1, "calc_cx_shading: malloc for sort buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+ shading_line_pixels = ms->n_control_bytes * 8; /* = 2560 for 330CX */
+ shading_line_bytes = shading_line_pixels; /* grayscale */
+ if ( ms->mode == MS_MODE_COLOR ) /* color */
+ shading_line_bytes *= 3;
+ shading_data_bytes = shading_line_bytes; /* 8-bit color depth */
+ if (ms->word == 1) /* > 8-bit color depth */
+ shading_data_bytes *= 2;
+ factor = 4; /* shading bit depth = 10bit; shading line bit depth = 8bit */
+
+ if (ms->dark == 0) /* white shading data */
+ {
+ if ( md->shading_table_w )
+ free( (void *)md->shading_table_w );
+ md->shading_table_w = (uint8_t *) malloc(shading_line_bytes);
+ DBG(100, "calc_cx_shading: md->shading_table_w=%p, malloc'd %d bytes\n",
+ md->shading_table_w, shading_line_bytes);
+ if ( md->shading_table_w == NULL )
+ {
+ DBG(100, "calc_cx_shading: malloc for white shadingtable failed\n");
+ status = SANE_STATUS_NO_MEM;
+ cleanup_scanner(ms);
+ }
+
+ shading_table_pointer = md->shading_table_w;
+ }
+
+ else /* dark shading data */
+ {
+ if ( md->shading_table_d )
+ free( (void *)md->shading_table_d);
+ md->shading_table_d = (uint8_t *) malloc(shading_line_bytes);
+ DBG(100, "calc_cx_shading: md->shading_table_d=%p, malloc'd %d bytes\n",
+ md->shading_table_d, shading_line_bytes);
+
+ if ( md->shading_table_d == NULL )
+ {
+ DBG(1, "calc_cx_shading: malloc for dark shading table failed\n");
+ status = SANE_STATUS_NO_MEM;
+ cleanup_scanner(ms);
+ }
+
+ shading_table_pointer = md->shading_table_d;
+ }
+
+ DBG(30, "calc_cx_shading_line: ms=%p\n"
+ "md->shading_table_w=%p\n"
+ "md->shading_table_d=%p\n"
+ "shading_line_bytes=%d\n"
+ "shading_line_pixels=%d\n"
+ "shading_table_pointer=%p\n",
+ (void *) ms, md->shading_table_w, md->shading_table_d,
+ shading_line_bytes, shading_line_pixels, shading_table_pointer);
+
+ /* calculating the median pixel values over the shading lines */
+ /* and write them to the shading table */
+ for (color = 0; color < 3; color++)
+ {
+ color_offset = color * shading_line_pixels;
+ if ( ms->word == 1 )
+ color_offset *=2;
+
+ for (i = 0; i < shading_line_pixels; i++)
+ {
+ value = 0;
+ for (line = 0; line < md->shading_length; line++)
+ {
+ current_byte = buf + ( line * shading_data_bytes )
+ + color_offset + i;
+ accu = *current_byte;
+
+ /* word shading data: the lower bytes per line and color are */
+ /* transfered first in one block and then the high bytes */
+ /* in one block */
+ /* the dark shading data is also 10 bit, but only the */
+ /* low byte is transferred (ms->word = 0) */
+ if ( ms->word == 1 )
+ {
+ current_byte = buf + ( line * shading_data_bytes )
+ + color_offset + shading_line_pixels + i;
+ accu += ( *current_byte * 256 );
+ }
+ *( sortbuf + line ) = accu;
+ }
+/* this is the Median filter: sort the values ascending and take the middlest */
+ qsort(sortbuf, md->shading_length, sizeof(float),
+ (qsortfunc)compare_func_16);
+ value = *( sortbuf + ( md->shading_length - 1 ) / 2 );
+ *shading_table_pointer = (uint8_t) (value / factor);
+ shading_table_pointer++;
+ }
+ if ( ms->mode != MS_MODE_COLOR )
+ break;
+ }
+ return status;
+}
+
+
+
+/*---------- get_lut_size() --------------------------------------------------*/
+
+static SANE_Status
+get_lut_size(Microtek2_Info *mi, int *max_lut_size, int *lut_entry_size)
+{
+ /* returns the maximum lookup table size. A device might indicate */
+ /* several lookup table sizes. */
+
+ DBG(30, "get_lut_size: mi=%p\n", (void *) mi);
+
+ *max_lut_size = 0;
+ *lut_entry_size = 0;
+
+ /* Normally this function is used for both gamma and shading tables */
+ /* If, however, the device indicates, that it does not support */
+ /* lookup tables, we set these values as if the device has a maximum */
+ /* bitdepth of 12, and these values are only used to determine the */
+ /* size of the shading table */
+ if ( MI_LUTCAP_NONE(mi->lut_cap) )
+ {
+ *max_lut_size = 4096;
+ *lut_entry_size = 2;
+ }
+
+ if ( mi->lut_cap & MI_LUTCAP_256B )
+ {
+ *max_lut_size = 256;
+ *lut_entry_size = 1;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_1024B )
+ {
+ *max_lut_size = 1024;
+ *lut_entry_size = 1;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_1024W )
+ {
+ *max_lut_size = 1024;
+ *lut_entry_size = 2;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_4096B )
+ {
+ *max_lut_size = 4096;
+ *lut_entry_size = 1;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_4096W )
+ {
+ *max_lut_size = 4096;
+ *lut_entry_size = 2;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_64k_W )
+ {
+ *max_lut_size = 65536;
+ *lut_entry_size = 2;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_16k_W )
+ {
+ *max_lut_size = 16384;
+ *lut_entry_size = 2;
+ }
+ DBG(30, "get_lut_size: mi=%p, lut_size=%d, lut_entry_size=%d\n",
+ (void *) mi, *max_lut_size, *lut_entry_size);
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- calculate_gamma() -----------------------------------------------*/
+
+static SANE_Status
+calculate_gamma(Microtek2_Scanner *ms, uint8_t *pos, int color, char *mode)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ double exp;
+ double mult;
+ double steps;
+ unsigned int val;
+ int i;
+ int factor; /* take into account the differences between the */
+ /* possible values for the color and the number */
+ /* of bits the scanner works with internally. */
+ /* If depth == 1 handle this as if the maximum */
+ /* depth was chosen */
+
+
+ DBG(30, "calculate_gamma: ms=%p, pos=%p, color=%d, mode=%s\n",
+ (void *) ms, pos, color, mode);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ /* does this work everywhere ? */
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ factor = 1;
+ mult = (double) (ms->lut_size - 1);
+ }
+ else
+ {
+ if ( mi->depth & MI_HASDEPTH_16 )
+ {
+ factor = ms->lut_size / 65536;
+ mult = 65535.0;
+ }
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ {
+ factor = ms->lut_size / 16384;
+ mult = 16383.0;
+ }
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ {
+ factor = ms->lut_size / 4096;
+ mult = 4095.0;
+ }
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ {
+ factor = ms->lut_size / 1024;
+ mult = 1023.0;
+ }
+ else
+ {
+ factor = ms->lut_size / 256;
+ mult = 255.0;
+ }
+ }
+
+#if 0
+ factor = ms->lut_size / (int) pow(2.0, (double) ms->depth);
+ mult = pow(2.0, (double) ms->depth) - 1.0; /* depending on output size */
+#endif
+
+ steps = (double) (ms->lut_size - 1); /* depending on input size */
+
+ DBG(30, "calculate_gamma: factor=%d, mult =%f, steps=%f, mode=%s\n",
+ factor, mult, steps, ms->val[OPT_GAMMA_MODE].s);
+
+
+ if ( strcmp(mode, MD_GAMMAMODE_SCALAR) == 0 )
+ {
+ int option;
+
+ option = OPT_GAMMA_SCALAR;
+ /* OPT_GAMMA_SCALAR_R follows OPT_GAMMA_SCALAR directly */
+ if ( ms->val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ exp = 1.0 / SANE_UNFIX(ms->val[option].w);
+ else
+ exp = 1.0 / SANE_UNFIX(ms->val[option + color + 1].w);
+
+ for ( i = 0; i < ms->lut_size; i++ )
+ {
+ val = (unsigned int) (mult * pow((double) i / steps, exp) + .5);
+
+ if ( ms->lut_entry_size == 2 )
+ *((uint16_t *) pos + i) = (uint16_t) val;
+ else
+ *((uint8_t *) pos + i) = (uint8_t) val;
+ }
+ }
+ else if ( strcmp(mode, MD_GAMMAMODE_CUSTOM) == 0 )
+ {
+ int option;
+ SANE_Int *src;
+
+ option = OPT_GAMMA_CUSTOM;
+ if ( ms->val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ src = ms->val[option].wa;
+ else
+ src = ms->val[option + color + 1].wa;
+
+ for ( i = 0; i < ms->lut_size; i++ )
+ {
+ if ( ms->lut_entry_size == 2 )
+ *((uint16_t *) pos + i) = (uint16_t) (src[i] / factor);
+ else
+ *((uint8_t *) pos + i) = (uint8_t) (src[i] / factor);
+ }
+ }
+ else if ( strcmp(mode, MD_GAMMAMODE_LINEAR) == 0 )
+ {
+ for ( i = 0; i < ms->lut_size; i++ )
+ {
+ if ( ms->lut_entry_size == 2 )
+ *((uint16_t *) pos + i) = (uint16_t) (i / factor);
+ else
+ *((uint8_t *) pos + i) = (uint8_t) (i / factor);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- shading_function() ----------------------------------------------*/
+
+static SANE_Status
+shading_function(Microtek2_Scanner *ms, uint8_t *data)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t value;
+ int color;
+ int i;
+
+
+ DBG(40, "shading_function: ms=%p, data=%p\n", (void *) ms, data);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+/* mi = &md->info[MD_SOURCE_FLATBED]; */
+
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "shading_function: wordsize = 1 unsupported\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++)
+ {
+ value = *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i);
+ switch ( mi->shtrnsferequ )
+ {
+ case 0x00:
+ /* output == input */
+ break;
+
+ case 0x01:
+ value = (ms->lut_size * ms->lut_size) / value;
+ *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+ break;
+
+ case 0x11:
+ value = (ms->lut_size * ms->lut_size)
+ / (uint32_t) ( (double) value
+ * ((double) mi->balance[color]
+ / 255.0));
+ *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+ break;
+ case 0x15:
+ value = (uint32_t) ( ( 1073741824 / (double) value )
+ * ( (double) mi->balance[color]
+ / 256.0) );
+ value = MIN(value, (uint32_t)65535);
+ *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+ break;
+
+ default:
+ DBG(1, "Unsupported shading transfer function 0x%02x\n",
+ mi->shtrnsferequ );
+ break;
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- set_exposure() --------------------------------------------------*/
+
+static void
+set_exposure(Microtek2_Scanner *ms)
+{
+ /* This function manipulates the colors according to the exposure time */
+ /* settings on models where they are ignored. Currently this seems to */
+ /* be the case for all models with the data format chunky data. They */
+ /* all have tables with two byte gamma output, so for now we ignore */
+ /* gamma tables with one byte output */
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ int color;
+ int size;
+ int depth;
+ int maxval;
+ int byte;
+ uint32_t val32;
+ uint8_t *from;
+ uint8_t exposure;
+ uint8_t exposure_rgb[3];
+
+
+ DBG(30, "set_exposure: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "set_exposure: 1 byte gamma output tables currently ignored\n");
+ return;
+ }
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ depth = 16;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ depth = 14;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ depth = 12;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ depth = 10;
+ else
+ depth = 8;
+
+ maxval = ( 1 << depth ) - 1;
+
+ from = ms->gamma_table;
+ size = ms->lut_size;
+
+ /* first master channel, apply transformation to all colors */
+ exposure = ms->exposure_m;
+ for ( byte = 0; byte < ms->lut_size; byte++ )
+ {
+ for ( color = 0; color < 3; color++)
+ {
+ val32 = (uint32_t) *((uint16_t *) from + color * size + byte);
+ val32 = MIN(val32 + val32
+ * (2 * (uint32_t) exposure / 100), (uint32_t) maxval);
+ *((uint16_t *) from + color * size + byte) = (uint16_t) val32;
+ }
+ }
+
+ /* and now apply transformation to each channel */
+
+ exposure_rgb[0] = ms->exposure_r;
+ exposure_rgb[1] = ms->exposure_g;
+ exposure_rgb[2] = ms->exposure_b;
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( byte = 0; byte < size; byte++ )
+ {
+ val32 = (uint32_t) *((uint16_t *) from + color * size + byte);
+ val32 = MIN(val32 + val32
+ * (2 * (uint32_t) exposure_rgb[color] / 100),
+ (uint32_t) maxval);
+ *((uint16_t *) from + color * size + byte) = (uint16_t) val32;
+ }
+ }
+
+ return;
+}
+
+
+/*---------- reader_process() ------------------------------------------------*/
+
+static int
+reader_process(void *data)
+{
+ Microtek2_Scanner *ms = (Microtek2_Scanner *) data;
+
+ SANE_Status status;
+ Microtek2_Info *mi;
+ Microtek2_Device *md;
+ struct SIGACTION act;
+ sigset_t sigterm_set;
+ static uint8_t *temp_current = NULL;
+
+ DBG(30, "reader_process: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if (sanei_thread_is_forked()) close(ms->fd[0]);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = signal_handler;
+ sigaction (SIGTERM, &act, 0);
+
+ ms->fp = fdopen(ms->fd[1], "w");
+ if ( ms->fp == NULL )
+ {
+ DBG(1, "reader_process: fdopen() failed, errno=%d\n", errno);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( ms->auto_adjust == 1 )
+ {
+ if ( temp_current == NULL )
+ temp_current = ms->temporary_buffer;
+ }
+
+ while ( ms->src_remaining_lines > 0 )
+ {
+
+ ms->src_lines_to_read = MIN(ms->src_remaining_lines, ms->src_max_lines);
+ ms->transfer_length = ms->src_lines_to_read * ms->bpl;
+
+ DBG(30, "reader_process: transferlength=%d, lines=%d, linelength=%d, "
+ "real_bpl=%d, srcbuf=%p\n", ms->transfer_length,
+ ms->src_lines_to_read, ms->bpl, ms->real_bpl, ms->buf.src_buf);
+
+ sigprocmask (SIG_BLOCK, &sigterm_set, 0);
+ status = scsi_read_image(ms, ms->buf.src_buf, (ms->depth > 8) ? 2 : 1);
+ sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_IO_ERROR;
+
+ ms->src_remaining_lines -= ms->src_lines_to_read;
+
+ /* prepare data for frontend */
+ switch (ms->mode)
+ {
+ case MS_MODE_COLOR:
+ if ( ! mi->onepass )
+ /* TODO */
+ {
+ DBG(1, "reader_process: 3 pass not yet supported\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ switch ( mi->data_format )
+ {
+ case MI_DATAFMT_CHUNKY:
+ case MI_DATAFMT_9800:
+ status = chunky_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MI_DATAFMT_LPLCONCAT:
+ status = lplconcat_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MI_DATAFMT_LPLSEGREG:
+ status = segreg_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MI_DATAFMT_WORDCHUNKY:
+ status = wordchunky_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ default:
+ DBG(1, "reader_process: format %d\n", mi->data_format);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ break;
+ case MS_MODE_GRAY:
+ status = gray_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MS_MODE_HALFTONE:
+ case MS_MODE_LINEART:
+ status = proc_onebit_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MS_MODE_LINEARTFAKE:
+ if ( ms->auto_adjust == 1 )
+ status = auto_adjust_proc_data(ms, &temp_current);
+ else
+ status = lineartfake_proc_data(ms);
+
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ default:
+ DBG(1, "reader_process: Unknown scan mode %d\n", ms->mode);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ fclose(ms->fp);
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- chunky_proc_data() ----------------------------------------------*/
+
+static SANE_Status
+chunky_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t line;
+ uint8_t *from;
+ int pad;
+ int bpp; /* bytes per pixel */
+ int bits_pp_in; /* bits per pixel input */
+ int bits_pp_out; /* bits per pixel output */
+ int bpl_ppl_diff;
+
+
+ DBG(30, "chunky_proc_data: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ bits_pp_in = ms->bits_per_pixel_in;
+ bits_pp_out = ms->bits_per_pixel_out;
+ pad = (int) ceil( (double) (ms->ppl * bits_pp_in) / 8.0 ) % 2;
+ bpp = bits_pp_out / 8;
+
+ /* Some models have 3 * ppl + 6 bytes per line if the number of pixels */
+ /* per line is even and 3 * ppl + 3 bytes per line if the number of */
+ /* pixels per line is odd. According to the documentation it should be */
+ /* bpl = 3*ppl (even number of pixels) or bpl=3*ppl+1 (odd number of */
+ /* pixels. Even worse: On different models it is different at which */
+ /* position in a scanline the image data starts. bpl_ppl_diff tries */
+ /* to fix this. */
+
+ if ( (md->model_flags & MD_OFFSET_2) && pad == 1 )
+ bpl_ppl_diff = 2;
+ else
+ bpl_ppl_diff = 0;
+
+#if 0
+ if ( md->revision == 1.00 && mi->model_code != 0x81 )
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ) - pad;
+ else
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp );
+
+ if ( md->revision > 1.00 )
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp );
+ else
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ) - pad;
+#endif
+
+ from = ms->buf.src_buf;
+ from += bpl_ppl_diff;
+
+ DBG(30, "chunky_proc_data: lines=%d, bpl=%d, ppl=%d, bpp=%d, depth=%d"
+ " junk=%d\n", ms->src_lines_to_read, ms->bpl, ms->ppl,
+ bpp, ms->depth, bpl_ppl_diff);
+
+ for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ )
+ {
+ status = chunky_copy_pixels(ms, from);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ from += ms->bpl;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- chunky_copy_pixels() --------------------------------------------*/
+
+static SANE_Status
+chunky_copy_pixels(Microtek2_Scanner *ms, uint8_t *from)
+{
+ Microtek2_Device *md;
+ uint32_t pixel;
+ int color;
+
+ DBG(30, "chunky_copy_pixels: from=%p, pixels=%d, fp=%p, depth=%d\n",
+ from, ms->ppl, (void *) ms->fp, ms->depth);
+
+ md = ms->dev;
+ if ( ms->depth > 8 )
+ {
+ if ( !( md->model_flags & MD_16BIT_TRANSFER ) )
+ {
+ int scale1;
+ int scale2;
+ uint16_t val16;
+
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ val16 = *( (uint16_t *) from + 3 * pixel + color );
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+ }
+ }
+ else
+ {
+ fwrite((void *) from, 2, 3 * ms->ppl, ms->fp);
+ }
+ }
+ else if ( ms->depth == 8 )
+ {
+ fwrite((void *) from, 1, 3 * ms->ppl, ms->fp);
+ }
+ else
+ {
+ DBG(1, "chunky_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- segreg_proc_data() ----------------------------------------------*/
+
+static SANE_Status
+segreg_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ char colormap[] = "RGB";
+ uint8_t *from;
+ uint32_t lines_to_deliver;
+ int bpp; /* bytes per pixel */
+ int bpf; /* bytes per frame including color indicator */
+ int pad;
+ int colseq2;
+ int color;
+ int save_current_src;
+ int frame;
+ int right_to_left;
+
+ DBG(30, "segreg_proc_data: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ /* take a trailing junk byte into account */
+ pad = (int) ceil( (double) (ms->ppl * ms->bits_per_pixel_in) / 8.0 ) % 2;
+ bpp = ms->bits_per_pixel_out / 8; /* bits_per_pixel_out is either 8 or 16 */
+ bpf = ms->bpl / 3;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ DBG(30, "segreg_proc_data: lines=%d, bpl=%d, ppl=%d, bpf=%d, bpp=%d,\n"
+ "depth=%d, pad=%d, freelines=%d, calib_backend=%d\n",
+ ms->src_lines_to_read, ms->bpl, ms->ppl, bpf, bpp,
+ ms->depth, pad, ms->buf.free_lines, ms->calib_backend);
+
+ /* determine how many planes of each color are in the source buffer */
+ from = ms->buf.src_buf;
+ for ( frame = 0; frame < 3 * ms->src_lines_to_read; frame++, from += bpf )
+ {
+ switch ( *from )
+ {
+ case 'R':
+ ++ms->buf.planes[0][MS_COLOR_RED];
+ break;
+ case 'G':
+ ++ms->buf.planes[0][MS_COLOR_GREEN];
+ break;
+ case 'B':
+ ++ms->buf.planes[0][MS_COLOR_BLUE];
+ break;
+ default:
+ DBG(1, "segreg_proc_data: unknown color indicator (1) "
+ "0x%02x\n", *from);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ ms->buf.free_lines -= ms->src_lines_to_read;
+ save_current_src = ms->buf.current_src;
+ if ( ms->buf.free_lines < ms->src_max_lines )
+ {
+ ms->buf.current_src = ++ms->buf.current_src % 2;
+ ms->buf.src_buf = ms->buf.src_buffer[ms->buf.current_src];
+ ms->buf.free_lines = ms->buf.free_max_lines;
+ }
+ else
+ ms->buf.src_buf += ms->src_lines_to_read * ms->bpl;
+
+ colseq2 = mi->color_sequence[2];
+ lines_to_deliver = ms->buf.planes[0][colseq2] + ms->buf.planes[1][colseq2];
+ if ( lines_to_deliver == 0 )
+ return SANE_STATUS_GOOD;
+
+ DBG(30, "segreg_proc_data: planes[0][0]=%d, planes[0][1]=%d, "
+ "planes[0][2]=%d\n", ms->buf.planes[0][0], ms->buf.planes[0][1],
+ ms->buf.planes[0][2] );
+ DBG(30, "segreg_proc_data: planes[1][0]=%d, planes[1][1]=%d, "
+ "planes[1][2]=%d\n", ms->buf.planes[1][0], ms->buf.planes[1][1],
+ ms->buf.planes[1][2] );
+
+ while ( lines_to_deliver > 0 )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ /* get the position of the next plane for each color */
+ do
+ {
+ if ( *ms->buf.current_pos[color] == colormap[color] )
+ break;
+ ms->buf.current_pos[color] += bpf;
+ } while ( 1 );
+
+ ms->buf.current_pos[color] += 2; /* skip color indicator */
+ }
+
+ status = segreg_copy_pixels(ms);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "segreg_copy_pixels:status %d\n", status);
+ return status;
+ }
+
+ for ( color = 0; color < 3; color++ )
+ {
+ /* skip a padding byte at the end, if present */
+ ms->buf.current_pos[color] += pad;
+
+ if ( ms->buf.planes[1][color] > 0 )
+ {
+ --ms->buf.planes[1][color];
+ if ( ms->buf.planes[1][color] == 0 )
+ /* we have copied from the prehold buffer and are */
+ /* done now, we continue with the source buffer */
+ ms->buf.current_pos[color] =
+ ms->buf.src_buffer[save_current_src];
+ }
+ else
+ {
+ --ms->buf.planes[0][color];
+ if ( ms->buf.planes[0][color] == 0
+ && ms->buf.current_src != save_current_src )
+
+ ms->buf.current_pos[color] =
+ ms->buf.src_buffer[ms->buf.current_src];
+ }
+ }
+ DBG(100, "planes_to_deliver=%d\n", lines_to_deliver);
+ --lines_to_deliver;
+ }
+
+ if ( ms->buf.current_src != save_current_src )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ ms->buf.planes[1][color] += ms->buf.planes[0][color];
+ ms->buf.planes[0][color] = 0;
+ }
+ }
+
+ DBG(30, "segreg_proc_data: src_buf=%p, free_lines=%d\n",
+ ms->buf.src_buf, ms->buf.free_lines);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- segreg_copy_pixels() --------------------------------------------*/
+
+static SANE_Status
+segreg_copy_pixels(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t pixel;
+ int color, i, gamma_by_backend, right_to_left, scale1, scale2, bpp_in;
+ float s_w, s_d; /* shading byte from condensed_shading */
+ float val, maxval = 0, shading_factor = 0;
+ uint16_t val16 = 0;
+ uint8_t val8 = 0;
+ uint8_t *from_effective;
+ uint8_t *gamma[3];
+ float f[3]; /* color balance factor */
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+ bpp_in = ( ms->bits_per_pixel_in + 7 ) / 8; /*Bytes per pixel from scanner*/
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend)
+ {
+ maxval = (float) pow(2.0, (float) ms->depth) - 1.0;
+ s_w = maxval;
+ s_d = 0.0;
+ shading_factor = (float) pow(2.0, (double) (md->shading_depth
+ - ms->depth) );
+ }
+
+ if ( gamma_by_backend )
+ {
+ i = (ms->depth > 8) ? 2 : 1;
+ for ( color = 0; color < 3; color++)
+ gamma[color] = ms->gamma_table
+ + i * (int) pow(2.0, (double)ms->depth);
+ }
+
+ DBG(30, "segreg_copy_pixels: pixels=%d\n", ms->ppl);
+ DBG(100, "segreg_copy_pixels: buffer 0x%p, right_to_left=%d, depth=%d\n",
+ (void *) ms->buf.current_pos, right_to_left, ms->depth);
+
+ for (color = 0; color < 3; color++ )
+ f[color] = (float) ms->balance[color] / 100.0;
+
+ DBG(100, "segreg_copy_pixels: color balance:\n"
+ " ms->balance[R]=%d, ms->balance[G]=%d, ms->balance[B]=%d\n",
+ ms->balance[0], ms->balance[1], ms->balance[2]);
+
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ if ( right_to_left )
+ from_effective = ms->buf.current_pos[color]
+ + ( ms->ppl - 1 - pixel ) * bpp_in;
+ else
+ from_effective = ms->buf.current_pos[color] + pixel * bpp_in;
+
+ if ( ms->depth > 8 )
+ val = (float) *(uint16_t *)from_effective;
+ else if ( ms->depth == 8 )
+ val = (float) *from_effective;
+ else
+ {
+ DBG(1, "segreg_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ color,
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+
+
+ if ( s_w == s_d ) s_w = s_d + 1;
+ if ( val < s_d ) val = s_d;
+ val = maxval *( val - s_d ) / ( s_w - s_d );
+
+ val *= f[color];
+
+ /* if scanner doesn't support brightness, contrast */
+ if ( md->model_flags & MD_NO_ENHANCEMENTS )
+ {
+ val += ( ( ms->brightness_m - 128 ) * 2 );
+ val = ( val - 128 ) * ( ms->contrast_m / 128 ) + 128;
+ }
+
+ val = MAX( 0.0, val);
+ val = MIN( maxval, val );
+ }
+
+ val16 = (uint16_t) val;
+ val8 = (uint8_t) val;
+
+ /* apply gamma correction if needed */
+ if ( gamma_by_backend )
+ {
+ if ( ms->depth > 8 )
+ val16 = *((uint16_t *) gamma[color] + val16);
+ else
+ val8 = gamma[color][val8];
+ }
+
+ if ( ms->depth > 8 )
+ {
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+ else
+ {
+ fputc((unsigned char) val8, ms->fp);
+ }
+
+ }
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ ms->buf.current_pos[color] += ms->ppl;
+ if ( ms->depth > 8 )
+ ms->buf.current_pos[color] += ms->ppl;
+ }
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+/*---------- lplconcat_proc_data() -------------------------------------------*/
+static SANE_Status
+lplconcat_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t line;
+ uint8_t *from[3];
+ uint8_t *save_from[3];
+ int color;
+ int bpp;
+ int pad;
+ int gamma_by_backend;
+ int right_to_left; /* 0=left to right, 1=right to left */
+
+
+ DBG(30, "lplconcat_proc_data: ms=%p\n", (void *) ms);
+
+ /* This data format seems to honour the color sequence indicator */
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ bpp = ms->bits_per_pixel_out / 8; /* ms->bits_per_pixel_out is 8 or 16 */
+ pad = (ms->ppl * bpp) % 2;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+ gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0;
+
+ if ( right_to_left == 1 )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ from[color] = ms->buf.src_buf
+ + ( mi->color_sequence[color] + 1 ) * ( ms->bpl / 3 )
+ - bpp - (ms->bpl - 3 * ms->ppl * bpp) / 3;
+ }
+ }
+ else
+ for ( color = 0; color < 3; color++ )
+ from[color] = ms->buf.src_buf
+ + mi->color_sequence[color] * ( ms->bpl / 3 );
+
+ for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ )
+ {
+ for ( color = 0 ; color < 3; color++ )
+ save_from[color] = from[color];
+
+ status = lplconcat_copy_pixels(ms,
+ from,
+ right_to_left,
+ gamma_by_backend);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ for ( color = 0; color < 3; color++ )
+ from[color] = save_from[color] + ms->bpl;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- lplconcat_copy_pixels() -----------------------------------------*/
+
+static SANE_Status
+lplconcat_copy_pixels(Microtek2_Scanner *ms,
+ uint8_t **from,
+ int right_to_left,
+ int gamma_by_backend)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t pixel;
+ uint16_t val16 = 0;
+ uint8_t val8 = 0;
+ uint8_t *gamma[3];
+ float s_d; /* dark shading pixel */
+ float s_w; /* white shading pixel */
+ float shading_factor = 0;
+ float f[3]; /* color balance factor */
+ float val, maxval = 0;
+ int color;
+ int step, scale1, scale2;
+ int i;
+
+
+ DBG(30, "lplconcat_copy_pixels: ms=%p, righttoleft=%d, gamma=%d,\n",
+ (void *) ms, right_to_left, gamma_by_backend);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend)
+ {
+ shading_factor = (float) pow(2.0,(double)(md->shading_depth - ms->depth));
+ maxval = (float) pow(2.0, (double) ms->depth) - 1.0;
+ s_w = maxval;
+ s_d = 0.0;
+ }
+
+ step = ( right_to_left == 1 ) ? -1 : 1;
+ if ( ms->depth > 8 ) step *= 2;
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+
+ if ( gamma_by_backend )
+ {
+ i = ( ms->depth > 8 ) ? 2 : 1;
+ for ( color = 0; color < 3; color++ )
+ gamma[color] = ms->gamma_table + i * (int) pow(2.0,(double)ms->depth);
+ }
+
+ for (color = 0; color < 3; color++ )
+ f[color] = (float)ms->balance[color] / 100.0;
+
+ DBG(100, "lplconcat_copy_pixels: color balance:\n"
+ " ms->balance[R]=%d, ms->balance[G]=%d, ms->balance[B]=%d\n",
+ ms->balance[0], ms->balance[1], ms->balance[2]);
+
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ if ( ms->depth > 8 )
+ val = (float) *(uint16_t *) from[color];
+ else if ( ms->depth == 8 )
+ val = (float) *from[color];
+ else
+ {
+ DBG(1, "lplconcat_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ mi->color_sequence[color],
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+
+ if ( val < s_d ) val = s_d;
+ if ( s_w == s_d ) s_w = s_d + 1;
+ val = ( maxval * ( val - s_d ) ) / (s_w - s_d);
+
+ val *= f[color]; /* apply color balance */
+
+ /* if scanner doesn't support brightness, contrast ... */
+ if ( md->model_flags & MD_NO_ENHANCEMENTS )
+ {
+ val += ( ( ms->brightness_m - 128 ) * 2 );
+ val = ( val - 128 ) * ( ms->contrast_m / 128 ) + 128;
+ }
+
+ if ( val > maxval ) val = maxval;
+ if ( val < 0.0 ) val = 0.0;
+ }
+
+ val16 = (uint16_t) val;
+ val8 = (uint8_t) val;
+
+ /* apply gamma correction if needed */
+ if ( gamma_by_backend )
+ {
+ if ( ms->depth > 8 )
+ val16 = *((uint16_t *) gamma[color] + val16);
+ else
+ val8 = gamma[color][val8];
+ }
+
+ if ( ms->depth > 8 )
+ {
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+ else
+ {
+ fputc((unsigned char) val8, ms->fp);
+ }
+ from[color] += step;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/*---------- wordchunky_proc_data() ------------------------------------------*/
+
+static SANE_Status
+wordchunky_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ uint8_t *from;
+ uint32_t line;
+
+
+ DBG(30, "wordchunky_proc_data: ms=%p\n", (void *) ms);
+
+ from = ms->buf.src_buf;
+ for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ )
+ {
+ status = wordchunky_copy_pixels(from, ms->ppl, ms->depth, ms->fp);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ from += ms->bpl;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- wordchunky_copy_pixels() ----------------------------------------*/
+
+static SANE_Status
+wordchunky_copy_pixels(uint8_t *from, uint32_t pixels, int depth, FILE *fp)
+{
+ uint32_t pixel;
+ int color;
+
+ DBG(30, "wordchunky_copy_pixels: from=%p, pixels=%d, depth=%d\n",
+ from, pixels, depth);
+
+ if ( depth > 8 )
+ {
+ int scale1;
+ int scale2;
+ uint16_t val16;
+
+ scale1 = 16 - depth;
+ scale2 = 2 * depth - 16;
+ for ( pixel = 0; pixel < pixels; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ val16 = *(uint16_t *) from;
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, fp);
+ from += 2;
+ }
+ }
+ }
+ else if ( depth == 8 )
+ {
+ pixel = 0;
+ do
+ {
+ fputc((char ) *from, fp);
+ fputc((char) *(from + 2), fp);
+ fputc((char) *(from + 4), fp);
+ ++pixel;
+ if ( pixel < pixels )
+ {
+ fputc((char) *(from + 1), fp);
+ fputc((char) *(from + 3), fp);
+ fputc((char) *(from + 5), fp);
+ ++pixel;
+ }
+ from += 6;
+ } while ( pixel < pixels );
+ }
+ else
+ {
+ DBG(1, "wordchunky_copy_pixels: Unknown depth %d\n", depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- gray_proc_data() ------------------------------------------------*/
+
+static SANE_Status
+gray_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint8_t *from;
+ int gamma_by_backend, bpp;
+ int right_to_left; /* for scanning direction */
+
+
+ DBG(30, "gray_proc_data: lines=%d, bpl=%d, ppl=%d, depth=%d\n",
+ ms->src_lines_to_read, ms->bpl, ms->ppl, ms->depth);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+ bpp = ( ms->bits_per_pixel_in + 7 ) / 8;
+
+ if ( right_to_left == 1 )
+ from = ms->buf.src_buf + ms->ppl * bpp - bpp;
+ else
+ from = ms->buf.src_buf;
+
+ do
+ {
+ status = gray_copy_pixels(ms,
+ from,
+ right_to_left,
+ gamma_by_backend);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ from += ms->bpl;
+ --ms->src_lines_to_read;
+ } while ( ms->src_lines_to_read > 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- gray_copy_pixels() ----------------------------------------------*/
+
+static SANE_Status
+gray_copy_pixels(Microtek2_Scanner *ms,
+ uint8_t *from,
+ int right_to_left,
+ int gamma_by_backend)
+{
+ Microtek2_Device *md;
+ uint32_t pixel;
+ uint16_t val16;
+ uint8_t val8;
+ int step, scale1, scale2;
+ float val, maxval = 0;
+ float s_w, s_d, shading_factor = 0;
+
+ DBG(30, "gray_copy_pixels: pixels=%d, from=%p, fp=%p, depth=%d\n",
+ ms->ppl, from, (void *) ms->fp, ms->depth);
+
+ md = ms->dev;
+ step = right_to_left == 1 ? -1 : 1;
+ if ( ms->depth > 8 ) step *= 2;
+ val = 0;
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend)
+ {
+ maxval = (float) pow(2.0, (float) ms->depth) - 1.0;
+ s_w = maxval;
+ s_d = 0.0;
+ shading_factor = (float) pow(2.0, (double) (md->shading_depth - ms->depth) );
+ }
+
+ if ( ms->depth >= 8 )
+ {
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ if ( ms->depth > 8 )
+ val = (float) *(uint16_t *) from;
+ if ( ms->depth == 8 )
+ val = (float) *from;
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ 0,
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+
+ if ( val < s_d ) val = s_d;
+ val = ( val - s_d ) * maxval / (s_w - s_d );
+ val = MAX( 0.0, val );
+ val = MIN( maxval, val );
+ }
+
+ if ( ms->depth > 8 )
+ {
+ val16 = (uint16_t) val;
+ if ( gamma_by_backend )
+ val16 = *((uint16_t *) ms->gamma_table + val16);
+ if ( !( md->model_flags & MD_16BIT_TRANSFER ) )
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+
+ if ( ms->depth == 8 )
+ {
+ val8 = (uint8_t) val;
+ if ( gamma_by_backend )
+ val8 = ms->gamma_table[(int)val8];
+ fputc((char)val8, ms->fp);
+ }
+ from += step;
+ }
+ }
+ else if ( ms->depth == 4 )
+ {
+ pixel = 0;
+ while ( pixel < ms->ppl )
+ {
+ fputc((char) ( ((*from >> 4) & 0x0f) | (*from & 0xf0) ), ms->fp);
+ ++pixel;
+ if ( pixel < ms->ppl )
+ fputc((char) ((*from & 0x0f) | ((*from << 4) & 0xf0)), ms->fp);
+ from += step;
+ ++pixel;
+ }
+ }
+ else
+ {
+ DBG(1, "gray_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- proc_onebit_data() ----------------------------------------------*/
+
+static SANE_Status
+proc_onebit_data(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t bytes_to_copy; /* bytes per line to copy */
+ uint32_t line;
+ uint32_t byte;
+ uint32_t ppl;
+ uint8_t *from;
+ uint8_t to;
+ int right_to_left;
+ int bit;
+ int toindex;
+
+
+ DBG(30, "proc_onebit_data: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ from = ms->buf.src_buf;
+ bytes_to_copy = ( ms->ppl + 7 ) / 8 ;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ DBG(30, "proc_onebit_data: bytes_to_copy=%d, lines=%d\n",
+ bytes_to_copy, ms->src_lines_to_read);
+
+ line = 0;
+ to = 0;
+ do
+ {
+ /* in onebit mode black and white colors are inverted */
+ if ( right_to_left )
+ {
+ /* If the direction is right_to_left, we must skip some */
+ /* trailing bits at the end of the scan line and invert the */
+ /* bit sequence. We copy 8 bits into a byte, but these bits */
+ /* are normally not byte aligned. */
+
+ /* Determine the position of the first bit to copy */
+ ppl = ms->ppl;
+ byte = ( ppl + 7 ) / 8 - 1;
+ bit = ppl % 8 - 1;
+ to = 0;
+ toindex = 8;
+
+ while ( ppl > 0 )
+ {
+ to |= ( ( from[byte] >> (7 - bit) ) & 0x01);
+ --toindex;
+ if ( toindex == 0 )
+ {
+ fputc( (char) ~to, ms->fp);
+ toindex = 8;
+ to = 0;
+ }
+ else
+ to <<= 1;
+
+ --bit;
+ if ( bit < 0 )
+ {
+ bit = 7;
+ --byte;
+ }
+ --ppl;
+ }
+ /* print the last byte of the line, if it was not */
+ /* completely filled */
+ bit = ms->ppl % 8;
+ if ( bit != 0 )
+ fputc( (char) ~(to << (7 - bit)), ms->fp);
+ }
+ else
+ for ( byte = 0; byte < bytes_to_copy; byte++ )
+ fputc( (char) ~from[byte], ms->fp);
+
+ from += ms->bpl;
+
+ } while ( ++line < (uint32_t) ms->src_lines_to_read );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- lineartfake_proc_data() -----------------------------------------*/
+
+static SANE_Status
+lineartfake_proc_data(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ SANE_Status status;
+ uint8_t *from;
+ int right_to_left;
+
+
+ DBG(30, "lineartfake_proc_data: lines=%d, bpl=%d, ppl=%d, depth=%d\n",
+ ms->src_lines_to_read, ms->bpl, ms->ppl, ms->depth);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ if ( right_to_left == 1 )
+ from = ms->buf.src_buf + ms->ppl - 1;
+ else
+ from = ms->buf.src_buf;
+
+ do
+ {
+ status = lineartfake_copy_pixels(ms,
+ from,
+ ms->ppl,
+ ms->threshold,
+ right_to_left,
+ ms->fp);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ from += ms->bpl;
+ --ms->src_lines_to_read;
+ } while ( ms->src_lines_to_read > 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- lineartfake_copy_pixels() ---------------------------------------*/
+
+static SANE_Status
+lineartfake_copy_pixels(Microtek2_Scanner *ms,
+ uint8_t *from,
+ uint32_t pixels,
+ uint8_t threshold,
+ int right_to_left,
+ FILE *fp)
+{
+ Microtek2_Device *md;
+ uint32_t pixel;
+ uint32_t bit;
+ uint8_t dest;
+ uint8_t val;
+ float s_d, s_w, maxval, shading_factor, grayval;
+ int step;
+
+
+ DBG(30, "lineartfake_copy_pixels: from=%p,pixels=%d,threshold=%d,file=%p\n",
+ from, pixels, threshold, (void *) fp);
+ md = ms->dev;
+ bit = 0;
+ dest = 0;
+ step = right_to_left == 1 ? -1 : 1;
+ maxval = 255.0;
+ s_w = maxval;
+ s_d = 0.0;
+ shading_factor = (float) pow(2.0, (double) (md->shading_depth - 8) );
+
+ for ( pixel = 0; pixel < pixels; pixel++ )
+ {
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ 0,
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+ }
+ else /* no shading */
+ {
+ s_w = maxval;
+ s_d = 0.0;
+ }
+
+ grayval = (float) *from;
+
+ if ( grayval < s_d ) grayval = s_d;
+ grayval = ( grayval - s_d ) * maxval / (s_w - s_d );
+ grayval = MAX( 0.0, grayval );
+ grayval = MIN( maxval, grayval );
+
+ if ( (uint8_t)grayval < threshold ) val = 1; else val = 0;
+ dest = ( dest << 1 ) | val;
+ bit = ( bit + 1 ) % 8;
+ if ( bit == 0 ) /* 8 input bytes processed */
+ {
+ fputc((char) dest, fp);
+ dest = 0;
+ }
+ from += step;
+ }
+
+ if ( bit != 0 )
+ {
+ dest <<= 7 - bit;
+ fputc((char) dest, fp);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- auto_adjust_proc_data() -----------------------------------------*/
+
+static SANE_Status
+auto_adjust_proc_data(Microtek2_Scanner *ms, uint8_t **temp_current)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ SANE_Status status;
+ uint8_t *from;
+ uint32_t line;
+ uint32_t lines;
+ uint32_t pixel;
+ uint32_t threshold;
+ int right_to_left;
+
+
+ DBG(30, "auto_adjust_proc_data: ms=%p, temp_current=%p\n",
+ (void *) ms, *temp_current);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ memcpy(*temp_current, ms->buf.src_buf, ms->transfer_length);
+ *temp_current += ms->transfer_length;
+ threshold = 0;
+ status = SANE_STATUS_GOOD;
+
+ if ( ms->src_remaining_lines == 0 ) /* we have read all the image data, */
+ { /* calculate threshold value */
+ for ( pixel = 0; pixel < ms->remaining_bytes; pixel++ )
+ threshold += *(ms->temporary_buffer + pixel);
+
+ threshold /= ms->remaining_bytes;
+ lines = ms->remaining_bytes / ms->bpl;
+ for ( line = 0; line < lines; line++ )
+ {
+ from = ms->temporary_buffer + line * ms->bpl;
+ if ( right_to_left == 1 )
+ from += ms->ppl - 1;
+ status = lineartfake_copy_pixels(ms,
+ from,
+ ms->ppl,
+ (uint8_t) threshold,
+ right_to_left,
+ ms->fp);
+ }
+ *temp_current = NULL;
+ }
+
+ return status;
+}
+
+/*-------------- get_cshading_values -----------------------------------------*/
+
+static SANE_Status
+get_cshading_values(Microtek2_Scanner *ms,
+ uint8_t color,
+ uint32_t pixel,
+ float shading_factor,
+ int right_to_left,
+ float *s_d,
+ float *s_w)
+{
+ Microtek2_Device *md;
+ uint32_t csh_offset;
+
+ md = ms->dev;
+
+ if ( right_to_left == 1 )
+ csh_offset = (color + 1) * ms->ppl - 1 - pixel;
+ else
+ csh_offset = color * ms->ppl + pixel;
+
+ if ( ( md->shading_depth > 8 ) && ( ms->lut_entry_size == 2) )
+ /* condensed shading is 2 byte color data */
+ {
+ if ( ms->condensed_shading_d != NULL )
+ *s_d = (float) *( (uint16_t *)ms->condensed_shading_d
+ + csh_offset );
+ else
+ *s_d = 0.0;
+
+ *s_w = (float) *( (uint16_t *)ms->condensed_shading_w
+ + csh_offset );
+ *s_w /= shading_factor;
+ *s_d /= shading_factor;
+ }
+
+ else
+ /* condensed shading is 8 bit data */
+ {
+ *s_w = (float) *( ms->condensed_shading_w + csh_offset );
+ if ( ms->condensed_shading_d != NULL )
+ *s_d = (float) *( ms->condensed_shading_d + csh_offset );
+ else
+ *s_d = 0.0;
+ }
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/microtek2.conf.in b/backend/microtek2.conf.in
new file mode 100644
index 0000000..5d5631a
--- /dev/null
+++ b/backend/microtek2.conf.in
@@ -0,0 +1,11 @@
+# See sane-microtek2(5) for a description of the options
+
+option dump 1
+#option strip-height 14.0
+option no-backtrack-option on
+option lightlid-35 on
+option toggle-lamp on
+option lineart-autoadjust on
+option backend-calibration on
+#option colorbalance-adjust on
+scsi * * Scanner
diff --git a/backend/microtek2.h b/backend/microtek2.h
new file mode 100644
index 0000000..4100fad
--- /dev/null
+++ b/backend/microtek2.h
@@ -0,0 +1,1384 @@
+/*******************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ microtek2.h
+
+ This file (C) 1998, 1999 Bernd Schroeder
+ 2000, 2001 Karsten Festag
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ fOUNDATIOn, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+
+*******************************************************************************/
+
+
+#ifndef microtek2_h
+#define microtek2_h
+
+#include <sys/types.h>
+
+
+/******************************************************************************/
+/* Miscellaneous defines */
+/******************************************************************************/
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#ifdef HAVE_AUTHORIZATION
+#ifndef PATH_SEP
+#if defined(_WIN32) || defined(HAVE_OS2_H)
+# define PATH_SEP "\\"
+#else
+# define PATH_SEP "/"
+#endif
+#endif
+
+#define MAX_LINE_LEN 512 /* max length of entry in password file */
+#define PASSWD_FILE STRINGIFY(PATH_SANE_CONFIG_DIR) PATH_SEP "auth"
+#define SEPARATOR ':' /* separator in that file */
+#define SALT "ab" /* used by crypt() */
+
+#endif /* HAVE_AUTHORIZATION */
+
+
+#define ENDIAN_TYPE(d) { unsigned i, test = 0; \
+ for (i=0; i < sizeof(int); i++ ) \
+ test += i << (8 * i); \
+ d = ((char *) &test)[0] == 0 ? 0 : 1; }
+
+#define MIN(a,b) ((a) < (b)) ? (a) : (b)
+#define MAX(a,b) ((a) > (b)) ? (a) : (b)
+
+#define MICROTEK2_MAJOR 0
+#define MICROTEK2_MINOR 96
+#define MICROTEK2_BUILD "200410042220"
+#define MICROTEK2_CONFIG_FILE "microtek2.conf"
+
+
+/******************************************************************************/
+/* defines that are common to all devices */
+/******************************************************************************/
+
+#define MD_RESOLUTION_DEFAULT 72 << SANE_FIXED_SCALE_SHIFT
+#define MD_BRIGHTNESS_DEFAULT 100 << SANE_FIXED_SCALE_SHIFT
+#define MD_CONTRAST_DEFAULT 100 << SANE_FIXED_SCALE_SHIFT
+#define MD_THRESHOLD_DEFAULT 128
+#define MD_GAMMA_DEFAULT SANE_FIX(2.2)
+#define MD_SHADOW_DEFAULT 0
+#define MD_MIDTONE_DEFAULT 128
+#define MD_HIGHLIGHT_DEFAULT 255
+#define MD_EXPOSURE_DEFAULT 0
+#define M_BRIGHTNESS_DEFAULT 128
+#define M_CONTRAST_DEFAULT 128
+#define M_SHADOW_DEFAULT 0
+#define M_MIDTONE_DEFAULT 128
+#define M_HIGHLIGHT_DEFAULT 255
+#define M_EXPOSURE_DEFAULT 0
+#define M_THRESHOLD_DEFAULT 128
+
+
+/******************************************************************************/
+/* SCSI commands used by the scanner */
+/******************************************************************************/
+
+/* INQUIRY */
+#define INQ_CMD(d) d[0] = 0x12; d[1] = 0x00; d[2] = 0x00; \
+ d[3] = 0x00; d[4] = 0x00; d[5] = 0x00
+#define INQ_CMD_L 6
+#define INQ_ALLOC_L 5 /* first get 5 bytes */
+#define INQ_ALLOC_P 4
+#define INQ_SET_ALLOC(d,s) (d)[4] = (s)
+
+#define INQ_GET_INQLEN(d,s) d = (s)[4]
+#define INQ_GET_QUAL(d,s) d = ((s)[0] >> 5) & 0x07
+#define INQ_GET_DEVT(d,s) d = (s)[0] & 0x1f
+#define INQ_GET_VERSION(d,s) d = (s)[2] & 0x02
+#define INQ_VENDOR_L 8
+#define INQ_GET_VENDOR(d,s) strncpy(d, &(s)[8], INQ_VENDOR_L); \
+ d[INQ_VENDOR_L] = '\0'
+#define INQ_MODEL_L 16
+#define INQ_GET_MODEL(d,s) strncpy(d, &(s)[16], INQ_MODEL_L); \
+ d[INQ_MODEL_L] = '\0'
+#define INQ_REV_L 4
+#define INQ_GET_REV(d,s) strncpy(d, &(s)[32], INQ_REV_L); \
+ d[INQ_REV_L] = '\0'
+#define INQ_GET_MODELCODE(d,s) d = (s)[36]
+
+
+
+/* TEST_UNIT_READY */
+#define TUR_CMD(d) d[0]=0x00; d[1]=0x00; d[2]=0x00; \
+ d[3]=0x00; d[4]=0x00; d[5]=0x00
+#define TUR_CMD_L 6
+
+
+/* READ GAMMA TABLE */
+#define RG_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x03; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \
+ (d)[9] = 0x00
+#define RG_CMD_L 10
+#define RG_PCORMAC(d,p) (d)[5] |= (((p) << 7) & 0x80)
+#define RG_COLOR(d,p) (d)[5] |= (((p) << 5) & 0x60)
+#define RG_WORD(d,p) (d)[5] |= ((p) & 0x01)
+#define RG_TRANSFERLENGTH(d,p) (d)[7] = (((p) >> 8) & 0xff); \
+ (d)[8] = ((p) & 0xff)
+
+/* SEND GAMMA TABLE */
+#define SG_SET_CMD(d) (d)[0] = 0x2a; (d)[1] = 0x00; (d)[2] = 0x03; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \
+ (d)[9] = 0x00
+#define SG_CMD_L 10
+#define SG_SET_PCORMAC(d,p) (d)[5] |= (((p) << 7) & 0x80)
+#define SG_SET_COLOR(d,p) (d)[5] |= (((p) << 5) & 0x60)
+#define SG_SET_WORD(d,p) (d)[5] |= ((p) & 0x01)
+#define SG_SET_TRANSFERLENGTH(d,p) (d)[7] = (((p) >> 8) & 0xff); \
+ (d)[8] = ((p) & 0xff)
+#define SG_DATA_P SG_CMD_L
+
+
+/* READ CONTROL BITS */
+#define RCB_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x90; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \
+ (d)[9] = 0x00
+#define RCB_CMD_L 10
+#define RCB_SET_LENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \
+ (d)[7] = (((s) >> 8) & 0xff); \
+ (d)[8] = ((s) & 0xff);
+
+
+/* READ_SCANNER_ATTRIBUTES */
+#define RSA_CMD(d) d[0]=0x28; d[1]=0x00; d[2]=0x82; d[3]=0x00; \
+ d[4]=0x00; d[5]=0x00; d[6]=0x00; d[7]=0x00; \
+ d[8]=0x28; d[9]=0x00
+#define RSA_CMD_L 10
+#define RSA_SETMEDIA(d,p) d[5] |= ((p) & 0x77)
+#define RSA_TRANSFERLENGTH 40
+
+#define RSA_COLOR(d,s) d = (((s)[0] >> 7) & 0x01)
+#define RSA_ONEPASS(d,s) d = (((s)[0] >> 6) & 0x01)
+#define RSA_SCANNERTYPE(d,s) d = (((s)[0] >> 4) & 0x03)
+#define RSA_FEPROM(d,s) d = (((s)[0] >> 3) & 0x01)
+#define RSA_DATAFORMAT(d,s) d = ((s)[0] & 0x07)
+#define RSA_COLORSEQUENCE_L 3
+#define RSA_COLORSEQUENCE(d,s) { \
+ d[0] = (((s)[1] >> 6) & 0x03); \
+ d[1] = (((s)[1] >> 4) & 0x03); \
+ d[2] = (((s)[1] >> 2) & 0x03); \
+ }
+#define RSA_NIS(d,s) d = ((s)[1] & 0x02)
+#define RSA_DATSEQ(d,s) d = ((s)[1] & 0x01)
+#define RSA_CCDGAP(d,s) d = (s)[2]
+#define RSA_MAX_XRESOLUTION(d,s) d = ((s)[3] << 8) + (s)[4]
+#define RSA_MAX_YRESOLUTION(d,s) d = ((s)[5] << 8) + (s)[6]
+#define RSA_GEOWIDTH(d,s) d = ((s)[7] << 8) + (s)[8]
+#define RSA_GEOHEIGHT(d,s) d = ((s)[9] << 8) + (s)[10]
+#define RSA_OPTRESOLUTION(d,s) d = ((s)[11] << 8) + (s)[12]
+#define RSA_DEPTH(d,s) d = (((s)[13] >> 4) & 0x0f)
+#define RSA_SCANMODE(d,s) d = (s)[13] & 0x0f
+#define RSA_CCDPIXELS(d,s) d = ((s)[14] << 8) + (s)[15]
+#define RSA_LUTCAP(d,s) d = (s)[16]
+#define RSA_DNLDPTRN(d,s) d = (((s)[17] >> 7) & 0x01)
+#define RSA_GRAINSLCT(d,s) d = (s)[17] & 0x7f
+#define RSA_SUPPOPT(d,s) d = (s)[18] & 0xf3
+#define RSA_CALIBWHITE(d,s) d = ((s)[19] << 24) + ((s)[20] << 16) \
+ + ((s)[21] << 8) + (s)[22]
+#define RSA_CALIBSPACE(d,s) d = ((s)[23] << 24) + ((s)[24] << 16) \
+ + ((s)[25] << 8) + (s)[26]
+#define RSA_NLENS(d,s) d = (s)[27]
+#define RSA_NWINDOWS(d,s) d = (s)[28]
+#define RSA_SHTRNSFEREQU(d,s) d = (((s)[29] >> 2) & 0x3f)
+#define RSA_SCNBTTN(d,s) d = (((s)[29] >> 1) & 0x01)
+#define RSA_BUFTYPE(d,s) d = (s)[29] & 0x01
+#define RSA_REDBALANCE(d,s) d = ((s)[30] << 8) | (s)[31]
+#define RSA_GREENBALANCE(d,s) d = ((s)[32] << 8) | (s)[33]
+#define RSA_BLUEBALANCE(d,s) d = ((s)[34] << 8) | (s)[35]
+#define RSA_APSMAXFRAMES(d,s) d = (s)[36]
+
+
+/* READ IMAGE INFORMATION */
+#define RII_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x80; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x10; \
+ (d)[9] = 0x00
+#define RII_CMD_L 10
+#define RII_RESULT_L 16
+
+#define RII_GET_WIDTHPIXEL(d,s) d = ((s)[0] << 24) + ((s)[1] << 16) \
+ + ((s)[2] << 8) + (s)[3]
+#define RII_GET_WIDTHBYTES(d,s) d = ((s)[4] << 24) + ((s)[5] << 16) \
+ + ((s)[6] << 8) + (s)[7]
+#define RII_GET_HEIGHTLINES(d,s) d = ((s)[8] << 24) + ((s)[9] << 16) \
+ + ((s)[10] << 8) + (s)[11]
+#define RII_GET_REMAINBYTES(d,s) d = ((s)[12] << 24) + ((s)[13] << 16) \
+ + ((s)[14] << 8) + (s)[15]
+
+/* The V300 returns some values in only two bytes */
+#define RII_GET_V300_WIDTHPIXEL(d,s) d = ((s)[0] << 8) + (s)[1]
+#define RII_GET_V300_WIDTHBYTES(d,s) d = ((s)[2] << 8) + (s)[3]
+#define RII_GET_V300_HEIGHTLINES(d,s) d = ((s)[4] << 8) + (s)[5]
+#define RII_GET_V300_REMAINBYTES(d,s) d = ((s)[6] << 24) + ((s)[7] << 16) \
+ + ((s)[8] << 8) + (s)[9]
+
+/* READ SHADING INFORMATION */
+#define RSI_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x01; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x10; \
+ (d)[9] = 0x00
+#define RSI_CMD_L 10
+#define RSI_SET_PCORMAC(d,s) (d)[5] |= (((s) << 7) & 0x80)
+#define RSI_SET_COLOR(d,s) (d)[5] |= (((s) << 5) & 0x60)
+#define RSI_SET_DARK(d,s) (d)[5] |= (((s) << 1) & 0x02) /*(KF)*/
+ /* RSI_SET_DARK was missing*/
+#define RSI_SET_WORD(d,s) (d)[5] |= ((s) & 0x01)
+#define RSI_SET_TRANSFERLENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \
+ (d)[7] = (((s) >> 8) & 0xff); \
+ (d)[8] = ((s) & 0xff);
+
+/* SEND SHADING INFORMATION */
+#define SSI_SET_CMD(d) (d)[0] = 0x2a; (d)[1] = 0x00; (d)[2] = 0x01; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \
+ (d)[9] = 0x00
+#define SSI_CMD_L 10
+#define SSI_SET_PCORMAC(d,s) (d)[5] |= (((s) << 7) & 0x80)
+#define SSI_SET_COLOR(d,s) (d)[5] |= (((s) << 5) & 0x60)
+#define SSI_SET_DARK(d,s) (d)[5] |= (((s) << 1) & 0x02)
+#define SSI_SET_WORD(d,s) (d)[5] |= ((s) & 0x01)
+#define SSI_SET_TRANSFERLENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \
+ (d)[7] = (((s) >> 8) & 0xff); \
+ (d)[8] = ((s) & 0xff);
+
+
+/* READ IMAGE */
+
+/* READ IMAGE */
+#define RI_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x00; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \
+ (d)[9] = 0x00
+#define RI_CMD_L 10
+
+
+#define RI_SET_PCORMAC(d,s) (d)[4] |= (((s) << 7) & 0x80)
+#define RI_SET_COLOR(d,s) (d)[4] |= (((s) << 5) & 0x60)
+#define RI_SET_TRANSFERLENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \
+ (d)[7] = (((s) >> 8) & 0xff); \
+ (d)[8] = ((s) & 0xff);
+
+
+/* READ SYSTEM_STATUS */
+#define RSS_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x81; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x09; \
+ (d)[9] = 0x00
+#define RSS_CMD_L 10
+#define RSS_RESULT_L 9
+
+#define RSS_FIRSTSCAN(s) (s)[0] & 0x80
+#define RSS_AFOCUS(s) (s)[0] & 0x40
+#define RSS_SSKIP(s) (s)[0] & 0x20
+#define RSS_STICK(s) (s)[0] & 0x10
+#define RSS_NTRACK(s) (s)[0] & 0x08
+#define RSS_NCALIB(s) (s)[0] & 0x04
+#define RSS_TLAMP(s) (s)[0] & 0x02
+#define RSS_FLAMP(s) (s)[0] & 0x01
+#define RSS_FSH(s) (s)[1] & 0x20
+#define RSS_TEFLAG(s) (s)[1] & 0x10
+#define RSS_WHITESTRIE(s) (s)[1] & 0x08
+#define RSS_RDYMAN(s) (s)[1] & 0x04
+#define RSS_TRDY(s) (s)[1] & 0x02
+#define RSS_FRDY(s) (s)[1] & 0x01
+#define RSS_ADP(s) (s)[2] & 0x80
+#define RSS_DETECT(s) (s)[2] & 0x40
+#define RSS_ADPTIME(s) (s)[2] & 0x3f
+#define RSS_LENSSTATUS(s) (s)[3]
+#define RSS_ALOFF(s) (s)[4] & 0x80
+#define RSS_TIMEREMAIN(s) (s)[4] & 0x7f
+#define RSS_MTMACNT(s) (s)[5] & 0x40
+#define RSS_MOVE(s) (s)[5] & 0x20
+#define RSS_LAMP(s) (s)[5] & 0x10
+#define RSS_EJECT(s) (s)[5] & 0x08
+#define RSS_TMACNT(s) (s)[5] & 0x04
+#define RSS_PAPER(s) (s)[5] & 0x02
+#define RSS_ADFCNT(s) (s)[5] & 0x01
+#define RSS_CANCELBTN(s) (s)[6] & 0x80
+#define RSS_COPYBTN(s) (s)[6] & 0x40
+#define RSS_EMAILBTN(s) (s)[6] & 0x20
+#define RSS_GOBTN(s) (s)[6] & 0x10
+#define RSS_TURBOBTN(s) (s)[6] & 0x08
+#define RSS_CURRENTMODE(s) (s)[6] & 0x07
+#define RSS_BUTTONCOUNT(s) (s)[7]
+#define RSS_MFOCUS(s) (s)[9]
+
+/* SEND SYSTEM STATUS */
+#define SSS_CMD(d) (d)[0] = 0x2a; (d)[1] = 0x00; (d)[2] = 0x81; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x09; \
+ (d)[9] = 0x00
+#define SSS_CMD_L 10
+#define SSS_DATA_L 9
+
+#define SSS_AFOCUS(d,p) d[0] |= (p) & 0x40
+#define SSS_STICK(d,p) d[0] |= (p) & 0x10
+#define SSS_NTRACK(d,p) d[0] |= (p) & 0x08
+#define SSS_NCALIB(d,p) d[0] |= (p) & 0x04
+#define SSS_TLAMP(d,p) d[0] |= (p) & 0x02
+#define SSS_FLAMP(d,p) d[0] |= (p) & 0x01
+#define SSS_RESERVED17(d,p) d[1] |= (p) & 0x80
+#define SSS_FSH(d,p) d[1] |= (p) & 0x40
+#define SSS_RDYMAN(d,p) d[1] |= (p) & 0x04
+#define SSS_TRDY(d,p) d[1] |= (p) & 0x02
+#define SSS_FRDY(d,p) d[1] |= (p) & 0x01
+#define SSS_ADP(d,p) d[2] |= (p) & 0x80
+#define SSS_DETECT(d,p) d[2] |= (p) & 0x40
+#define SSS_ADPTIME(d,p) d[2] |= (p) & 0x3f
+#define SSS_LENSSTATUS(d,p) d[3] |= (p)
+#define SSS_ALOFF(d,p) d[4] |= (p) & 0x80
+#define SSS_TIMEREMAIN(d,p) d[4] |= (p) & 0x7f
+#define SSS_TMACNT(d,p) d[5] |= (p) & 0x04
+#define SSS_PAPER(d,p) d[5] |= (p) & 0x02
+#define SSS_ADFCNT(d,p) d[5] |= (p) & 0x01
+#define SSS_CURRENTMODE(d,p) d[6] |= (p) & 0x07
+#define SSS_BUTTONCOUNT(d,p) d[6] |= (p)
+#define SSS_MFOCUS(s) d[9] |= (p)
+
+
+/* SET WINDOW */
+#define SW_CMD(d) d[0]=0x24; d[1]=0x00; d[2]=0x00; d[3]=0x00; \
+ d[4]=0x00; d[5]=0x00; d[6]=0x00; d[7]=0x00;\
+ d[8]=0x00; d[9]=0x00
+
+#define SW_CMD_L 10
+#define SW_HEADER_L 8
+#define SW_BODY_L 61
+#define SW_CMD_P 0 /* command at postion 0 */
+#define SW_HEADER_P SW_CMD_L
+#define SW_BODY_P(n) SW_CMD_L + SW_HEADER_L + (n) * SW_BODY_L
+
+/* d: SW_CMD_P, SW_HEADER_P, SW_BODY_P(n) */
+#define SW_PARAM_LENGTH(d,p) (d)[6] = ((p) >> 16) & 0xff; \
+ (d)[7] = ((p) >> 8) & 0xff;\
+ (d)[8] = (p) & 0xff
+#define SW_WNDDESCVAL SW_BODY_L
+#define SW_WNDDESCLEN(d,p) (d)[6] = ((p) >> 8) & 0xff; \
+ (d)[7] = (p) & 0xff
+#define SW_WNDID(d,p) (d)[0] = (p)
+#define SW_XRESDPI(d,p) (d)[2] = ((p) >> 8) & 0xff; \
+ (d)[3] = (p) & 0xff
+#define SW_YRESDPI(d,p) (d)[4] = ((p) >> 8) & 0xff; \
+ (d)[5] = (p) & 0xff
+#define SW_XPOSTL(d,p) (d)[6] = ((p) >> 24) & 0xff; \
+ (d)[7] = ((p) >> 16) & 0xff; \
+ (d)[8] = ((p) >> 8) & 0xff; \
+ (d)[9] = ((p)) & 0xff
+#define SW_YPOSTL(d,p) (d)[10] = ((p) >> 24) & 0xff; \
+ (d)[11] = ((p) >> 16) & 0xff; \
+ (d)[12] = ((p) >> 8) & 0xff; \
+ (d)[13] = (p) & 0xff
+#define SW_WNDWIDTH(d,p) (d)[14] = ((p) >> 24) & 0xff; \
+ (d)[15] = ((p) >> 16) & 0xff; \
+ (d)[16] = ((p) >> 8) & 0xff; \
+ (d)[17] = (p) & 0xff
+#define SW_WNDHEIGHT(d,p) (d)[18] = ((p) >> 24) & 0xff; \
+ (d)[19] = ((p) >> 16) & 0xff; \
+ (d)[20] = ((p) >> 8) & 0xff; \
+ (d)[21] = (p) & 0xff
+#define SW_BRIGHTNESS_M(d,p) (d)[22] = (p)
+#define SW_THRESHOLD(d,p) (d)[23] = (p)
+#define SW_CONTRAST_M(d,p) (d)[24] = (p)
+#define SW_IMGCOMP(d,p) (d)[25] = (p) & 0x0f /* take lineartfake */
+ /* into account */
+#define SW_BITSPERPIXEL(d,p) (d)[26] = (p)
+#define SW_EXPOSURE_M(d,p) (d)[27] = (p)
+#define SW_EXTHT(d,p) (d)[28] |= (((p) << 7) & 0x80)
+#define SW_INTHTINDEX(d,p) (d)[28] |= ((p) & 0x7f)
+#define SW_RIF(d,p) (d)[29] |= (((p) << 7) & 0x80)
+#define SW_NOGAMMA(d,p) (d)[29] |= (((p) << 6) & 0x40)
+#define SW_SLOWSCAN(d,p) (d)[29] |= (((p) << 5) & 0x20)
+#define SW_LENS(d,p) (d)[30] = (p)
+#define SW_INFINITE(d,p) (d)[31] |= (((p) << 7) & 0x80)
+#define SW_STAY(d,p) (d)[31] |= (((p) << 6) & 0x40)
+#define SW_RAWDAT(d,p) (d)[31] |= (((p) << 5) & 0x20)
+#define SW_QUALITY(d,p) (d)[31] |= (((p) << 4) & 0x10)
+#define SW_FASTSCAN(d,p) (d)[31] |= (((p) << 3) & 0x08)
+#define SW_MEDIA(d,p) (d)[31] |= ((p) & 0x07)
+#define SW_JPEGENABLE(d,p) (d)[34] |= (((p) << 7) & 0x80)
+#define SW_JPEGALGOR(d,p) (d)[34] |= (((p) << 5) & 0x60)
+#define SW_JPEGMODE(d,p) (d)[34] |= (((p) << 3) & 0x18)
+#define SW_SHADOW_M(d,p) (d)[40] = (p)
+#define SW_MIDTONE_M(d,p) (d)[41] = (p)
+#define SW_HIGHLIGHT_M(d,p) (d)[42] = (p)
+#define SW_BRIGHTNESS_R(d,p) (d)[43] = (p)
+#define SW_CONTRAST_R(d,p) (d)[44] = (p)
+#define SW_EXPOSURE_R(d,p) (d)[45] = (p)
+#define SW_SHADOW_R(d,p) (d)[46] = (p)
+#define SW_MIDTONE_R(d,p) (d)[47] = (p)
+#define SW_HIGHLIGHT_R(d,p) (d)[48] = (p)
+#define SW_BRIGHTNESS_G(d,p) (d)[49] = (p)
+#define SW_CONTRAST_G(d,p) (d)[50] = (p)
+#define SW_EXPOSURE_G(d,p) (d)[51] = (p)
+#define SW_SHADOW_G(d,p) (d)[52] = (p)
+#define SW_MIDTONE_G(d,p) (d)[53] = (p)
+#define SW_HIGHLIGHT_G(d,p) (d)[54] = (p)
+#define SW_BRIGHTNESS_B(d,p) (d)[55] = (p)
+#define SW_CONTRAST_B(d,p) (d)[56] = (p)
+#define SW_EXPOSURE_B(d,p) (d)[57] = (p)
+#define SW_SHADOW_B(d,p) (d)[58] = (p)
+#define SW_MIDTONE_B(d,p) (d)[59] = (p)
+#define SW_HIGHLIGHT_B(d,p) (d)[60] = (p)
+
+
+/* READ IMAGE STATUS */
+#define RIS_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x83; \
+ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \
+ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \
+ (d)[9] = 0x00
+#define RIS_CMD_L 10
+#define RIS_SET_PCORMAC(d,p) (d)[4] |= (((p) << 7) & 0x80)
+#define RIS_SET_COLOR(d,p) (d)[4] |= (((p) << 5) & 0x60)
+
+
+/* REQUEST SENSE */
+#define RQS_CMD(d) (d)[0] = 0x03; (d)[1] = 0x00; (d)[2] = 0x00; \
+ (d)[3] = 0x00; (d)[5] = 0x00
+#define RQS_CMD_L 6
+#define RQS_ALLOCLENGTH(d,p) (d)[4] = (p)
+
+#define RQS_LENGTH(s) (s)[7] + 7
+
+#define RQS_SENSEKEY_NOSENSE 0x00
+#define RQS_SENSEKEY_HWERR 0x04
+#define RQS_SENSEKEY_ILLEGAL 0x05
+#define RQS_SENSEKEY_VENDOR 0x09
+#define RQS_SENSEKEY(s) ((s)[2] & 0x0f)
+
+#define RQS_INFO(s) ((s)[3] << 24) + ((s)[4] << 16) \
+ + ((s)[5] << 8) + ((s)[6])
+#define RQS_ASL(s) (s)[7]
+#define RQS_REMAINBYTES(s) ((s)[8] << 24) + ((s)[9] << 16) \
+ + ((s)[10] << 8) + ((s)[11])
+#define RQS_ASC(s) (s)[12]
+/* ASC == 0x40 */
+#define RQS_ASCQ_CPUERR 0x81
+#define RQS_ASCQ_SRAMERR 0x82
+#define RQS_ASCQ_DRAMERR 0x84
+#define RQS_ASCQ_DCOFF 0x88
+#define RQS_ASCQ_GAIN 0x90
+#define RQS_ASCQ_POS 0xa0
+#define RQS_ASCQ(s) (s)[13]
+
+#define RQS_ASINFO(s) &((s)[18])
+#define RQS_ASINFOLENGTH(s) (s)[7] - 11
+
+/******************************************************************************/
+/* enumeration of Option Descriptors */
+/******************************************************************************/
+
+enum Microtek2_Option
+{
+ /*0*/
+ OPT_NUM_OPTS = 0,
+
+ /*1*/OPT_MODE_GROUP,
+ OPT_SOURCE,
+ OPT_MODE,
+ OPT_BITDEPTH,
+ OPT_RESOLUTION,
+ OPT_Y_RESOLUTION,
+ OPT_PREVIEW,
+
+ /*9*/ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ /*14*/ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+ OPT_HALFTONE,
+ OPT_AUTOADJUST,
+
+ /*20*/ OPT_GAMMA_GROUP,
+ OPT_GAMMA_MODE,
+ OPT_GAMMA_SCALAR,
+ OPT_GAMMA_SCALAR_R,
+ OPT_GAMMA_SCALAR_G,
+ OPT_GAMMA_SCALAR_B,
+ OPT_GAMMA_CUSTOM,
+ OPT_GAMMA_CUSTOM_R,
+ OPT_GAMMA_CUSTOM_G,
+ OPT_GAMMA_CUSTOM_B,
+ OPT_GAMMA_BIND,
+
+ /*31*/ OPT_SMH_GROUP,
+ /* these options must appear in exactly this order, */
+ /* sane_control_option relies on it */
+ OPT_CHANNEL,
+ OPT_SHADOW,
+ OPT_MIDTONE,
+ OPT_HIGHLIGHT,
+ OPT_SHADOW_R,
+ OPT_MIDTONE_R,
+ OPT_HIGHLIGHT_R,
+ OPT_SHADOW_G,
+ OPT_MIDTONE_G,
+ OPT_HIGHLIGHT_G,
+ OPT_SHADOW_B,
+ OPT_MIDTONE_B,
+ OPT_HIGHLIGHT_B,
+ OPT_EXPOSURE,
+ OPT_EXPOSURE_R,
+ OPT_EXPOSURE_G,
+ OPT_EXPOSURE_B,
+
+ /*49*/ OPT_SPECIAL,
+ OPT_RESOLUTION_BIND,
+ OPT_DISABLE_BACKTRACK,
+ OPT_CALIB_BACKEND,
+ OPT_LIGHTLID35,
+ OPT_TOGGLELAMP,
+
+ /*55*/ OPT_COLORBALANCE,
+ OPT_BALANCE_R,
+ OPT_BALANCE_G,
+ OPT_BALANCE_B,
+ OPT_BALANCE_FW,
+
+ /*60*/ NUM_OPTIONS
+};
+
+
+/******************************************************************************/
+/* Description of options not included in saneopts.h */
+/******************************************************************************/
+
+#define M_TITLE_SCANMODEGRP SANE_I18N("Scan Mode")
+#define M_TITLE_GEOMGRP SANE_I18N("Geometry")
+#define M_TITLE_ENHANCEGRP SANE_I18N("Enhancement")
+#define M_TITLE_SMHGRP SANE_I18N("Shadow, midtone, highlight,"\
+ " exposure time");
+#define M_TITLE_SPECIALGRP SANE_I18N("Special options")
+#define M_TITLE_COLBALANCEGRP SANE_I18N("Color balance")
+
+#define M_NAME_NOBACKTRACK "no-backtracking"
+#define M_TITLE_NOBACKTRACK SANE_I18N("Disable backtracking")
+#define M_DESC_NOBACKTRACK SANE_I18N("If checked the scanner does not" \
+ " perform backtracking")
+
+#define M_NAME_TOGGLELAMP "toggle-lamp"
+#define M_TITLE_TOGGLELAMP SANE_I18N("Toggle lamp of flatbed")
+#define M_DESC_TOGGLELAMP SANE_I18N("Toggles the lamp of the flatbed")
+
+#define M_NAME_CALIBBACKEND "backend-calibration"
+#define M_TITLE_CALIBBACKEND SANE_I18N("Calibration by backend")
+#define M_DESC_CALIBBACKEND SANE_I18N("If checked the color calibration" \
+ " before a scan is done by the backend")
+
+#define M_NAME_LIGHTLID35 "lightlid35"
+#define M_TITLE_LIGHTLID35 SANE_I18N("Use the lightlid-35mm adapter")
+#define M_DESC_LIGHTLID35 SANE_I18N("This option turns off the lamp of" \
+ " the flatbed during a scan")
+
+#define M_NAME_QUALITY_SCAN "quality-scan"
+#define M_TITLE_QUALITY_SCAN SANE_I18N("Quality scan")
+#define M_DESC_QUALITY_SCAN SANE_I18N("Highest quality but lower speed")
+
+#define M_NAME_FAST_SCAN "fast-scan"
+#define M_TITLE_FAST_SCAN SANE_I18N("Fast scan")
+#define M_DESC_FAST_SCAN SANE_I18N("Highest speed but lower quality")
+
+#define M_NAME_AUTOADJUST "lineart-auto-adjust"
+#define M_TITLE_AUTOADJUST SANE_I18N("Automatic adjustment of threshold")
+#define M_DESC_AUTOADJUST SANE_I18N("If checked the backend automatically"\
+ " tries to determine an optimal value for the" \
+ " threshold.")
+
+#define M_NAME_GAMMA_MODE "gamma-correction"
+#define M_TITLE_GAMMA_MODE SANE_I18N("Gamma correction")
+#define M_DESC_GAMMA_MODE SANE_I18N("Selects the gamma correction mode.")
+
+#define M_NAME_GAMMA_BIND "bind-gamma"
+#define M_TITLE_GAMMA_BIND SANE_I18N("Bind gamma")
+#define M_DESC_GAMMA_BIND SANE_I18N("Use same gamma values for all" \
+ " colour channels.")
+
+#define M_NAME_GAMMA_SCALAR "scalar-gamma"
+#define M_TITLE_GAMMA_SCALAR SANE_I18N("Scalar gamma")
+#define M_DESC_GAMMA_SCALAR SANE_I18N("Selects a value for scalar" \
+ " gamma correction.")
+
+#define M_NAME_GAMMA_SCALAR_R "scalar-gamma-r"
+#define M_TITLE_GAMMA_SCALAR_R SANE_I18N("Scalar gamma red")
+#define M_DESC_GAMMA_SCALAR_R SANE_I18N("Selects a value for scalar gamma" \
+ " correction (red channel)")
+
+#define M_NAME_GAMMA_SCALAR_G "scalar-gamma-g"
+#define M_TITLE_GAMMA_SCALAR_G SANE_I18N("Scalar gamma green")
+#define M_DESC_GAMMA_SCALAR_G SANE_I18N("Selects a value for scalar gamma" \
+ " correction (green channel)")
+
+#define M_NAME_GAMMA_SCALAR_B "scalar-gamma-b"
+#define M_TITLE_GAMMA_SCALAR_B SANE_I18N("Scalar gamma blue")
+#define M_DESC_GAMMA_SCALAR_B SANE_I18N("Selects a value for scalar gamma" \
+ " correction (blue channel)")
+
+#define M_NAME_CHANNEL "channel"
+#define M_TITLE_CHANNEL SANE_I18N("Channel")
+#define M_DESC_CHANNEL SANE_I18N("Selects the colour band, \"Master\"" \
+ " means that all colours are affected.")
+
+#define M_NAME_MIDTONE "midtone"
+#define M_TITLE_MIDTONE SANE_I18N("Midtone")
+#define M_DESC_MIDTONE SANE_I18N("Selects which radiance level should" \
+ " be considered \"50 % gray\".")
+
+#define M_NAME_MIDTONE_R "midtone-r"
+#define M_TITLE_MIDTONE_R SANE_I18N("Midtone for red")
+#define M_DESC_MIDTONE_R SANE_I18N("Selects which radiance level should" \
+ " be considered \"50 % red\".")
+
+#define M_NAME_MIDTONE_G "midtone-g"
+#define M_TITLE_MIDTONE_G SANE_I18N("Midtone for green")
+#define M_DESC_MIDTONE_G SANE_I18N("Selects which radiance level should" \
+ " be considered \"50 % green\".")
+
+#define M_NAME_MIDTONE_B "midtone-b"
+#define M_TITLE_MIDTONE_B SANE_I18N("Midtone for blue")
+#define M_DESC_MIDTONE_B SANE_I18N("Selects which radiance level should" \
+ " be considered \"50 % blue\".")
+
+#define M_NAME_BALANCE_R "balance-r"
+#define M_TITLE_BALANCE_R SANE_I18N("Red balance")
+#define M_DESC_BALANCE_R SANE_I18N("Balance factor for red. A value of" \
+ " 100% means no correction.")
+
+#define M_NAME_BALANCE_G "balance-g"
+#define M_TITLE_BALANCE_G SANE_I18N("Green balance")
+#define M_DESC_BALANCE_G SANE_I18N("Balance factor for green. A value of"\
+ " 100% means no correction.")
+
+#define M_NAME_BALANCE_B "balance-b"
+#define M_TITLE_BALANCE_B SANE_I18N("Blue balance")
+#define M_DESC_BALANCE_B SANE_I18N("Balance factor for blue. A value of" \
+ " 100% means no correction.")
+
+#define M_NAME_BALANCE_FW "balance-fw"
+#define M_TITLE_BALANCE_FW SANE_I18N("Firmware balance")
+#define M_DESC_BALANCE_FW SANE_I18N("Sets the color balance values to"\
+ " the firmware provided values.")
+
+/******************************************************************************/
+/* Structure that contains global options */
+/******************************************************************************/
+
+typedef struct Config_Options
+{
+ double strip_height; /* inch */
+ char *no_backtracking; /* enable/disable option for */
+ /* backtracking */
+ char *lightlid35; /* enable/disable lightlid35 option */
+ char *toggle_lamp; /* enable/disable lightlid35 option */
+ char *backend_calibration; /* calibration by backend */
+ char *auto_adjust; /* automatically choose threshold */
+ char *colorbalance_adjust; /* color balance can be modified */
+} Config_Options;
+
+
+
+/******************************************************************************/
+/* Structure that is temporarily used, when the config file is parsed */
+/******************************************************************************/
+
+typedef struct Config_Temp
+{
+ struct Config_Temp *next;
+ char *device; /* possible device name */
+ Config_Options opts; /* options belonging to this device name */
+
+} Config_Temp;
+
+
+/******************************************************************************/
+/* scanner hardware info (as discovered by INQUIRY and READ ATTRIBUTES) */
+/******************************************************************************/
+
+typedef struct Microtek2_Info {
+ /* from inquiry */
+ SANE_Byte device_qualifier;
+#define MI_DEVTYPE_SCANNER 0x06
+ SANE_Byte device_type;
+#define MI_SCSI_II_VERSION 0x02
+ SANE_Byte scsi_version;
+ SANE_Char vendor[INQ_VENDOR_L + 1];
+ SANE_Char model[INQ_MODEL_L + 1];
+ SANE_Char revision[INQ_REV_L + 1];
+ SANE_Byte model_code;
+ /* from read scanner attributes */
+ SANE_Bool color;
+#define MI_HAS_ONEPASS SANE_TRUE
+ SANE_Bool onepass;
+ /* the following 3 defines must correspond to byte 31 of SET WINDOW cmd */
+#define MI_TYPE_FLATBED 0x01
+#define MI_TYPE_SHEEDFEED 0x02
+#define MI_TYPE_TRANSPARENCY 0x03
+ SANE_Byte scanner_type;
+#define MI_HAS_FEPROM SANE_TRUE
+ SANE_Bool feprom;
+ /* MI_DATAFMT_X must correspond to Byte 0 in READ SCANNER ATTRIBUTE */
+#define MI_DATAFMT_CHUNKY 1
+#define MI_DATAFMT_LPLCONCAT 2
+#define MI_DATAFMT_LPLSEGREG 3
+#define MI_DATAFMT_9800 4
+#define MI_DATAFMT_WORDCHUNKY 5
+ SANE_Byte data_format;
+#define MI_COLSEQ_RED 0
+#define MI_COLSEQ_GREEN 1
+#define MI_COLSEQ_BLUE 2
+#define MI_COLSEQ_ILLEGAL 3
+ uint8_t color_sequence[RSA_COLORSEQUENCE_L];
+ SANE_Bool new_image_status;
+#define MI_DATSEQ_RTOL 1
+ uint8_t direction;
+ SANE_Byte ccd_gap;
+ SANE_Int max_xresolution;
+ SANE_Int max_yresolution;
+ SANE_Int geo_width;
+ SANE_Int geo_height;
+ SANE_Int opt_resolution;
+#define MI_HASDEPTH_NIBBLE 1
+#define MI_HASDEPTH_10 2
+#define MI_HASDEPTH_12 4
+#define MI_HASDEPTH_16 8
+#define MI_HASDEPTH_14 16 /*This is not in Byte 13 of
+ scanner attributes but in
+ byte 48-bit0*/
+
+ SANE_Byte depth;
+#define MI_LINEART_NONE(d) (((d) & 0x01) == 0)
+#define MI_HASMODE_LINEART 1
+#define MI_HASMODE_HALFTONE 2
+#define MI_HASMODE_GRAY 4
+#define MI_HASMODE_COLOR 8
+ SANE_Byte scanmode;
+ SANE_Int ccd_pixels;
+#define MI_LUTCAP_NONE(d) ((d) == 0)
+#define MI_LUTCAP_256B 1
+#define MI_LUTCAP_1024B 2
+#define MI_LUTCAP_1024W 4
+#define MI_LUTCAP_4096B 8
+#define MI_LUTCAP_4096W 16
+#define MI_LUTCAP_64k_W 32
+#define MI_LUTCAP_16k_W 64
+#define MI_LUTCAP_UNKNOWN 128
+ SANE_Byte lut_cap;
+#define MI_HAS_DNLDPTRN SANE_TRUE
+ SANE_Bool has_dnldptrn;
+ SANE_Byte grain_slct;
+#define MI_OPTDEV_ADF 0x01
+#define MI_OPTDEV_TMA 0x02
+#define MI_OPTDEV_ADP 0x10
+#define MI_OPTDEV_APS 0x20
+#define MI_OPTDEV_STRIPE 0x40
+#define MI_OPTDEV_SLIDE 0x80
+ SANE_Byte option_device;
+ SANE_Int calib_white;
+ SANE_Int calib_space;
+ SANE_Byte nlens;
+ SANE_Byte nwindows;
+ SANE_Byte shtrnsferequ;
+#define MI_WHITE_SHADING_ONLY(x) ((x) & 0x20) == 0
+#define MI_HAS_SCNBTTN SANE_TRUE
+ SANE_Bool scnbuttn;
+#define MI_HAS_PIPOBUF SANE_TRUE
+ SANE_Bool buftype;
+ uint16_t balance[3]; /* balance factor for red, green */
+ /* and blue data */
+ uint16_t aps_maxframes;
+ SANE_Int calib_divisor; /* e.g. the X12USL reads and sends */
+ /* shading at 1/2 * opt_resolution */
+} Microtek2_Info;
+
+/******************************************************************************/
+/* device structure (one for each device in config file) */
+/******************************************************************************/
+
+typedef struct Microtek2_Device {
+ struct Microtek2_Device *next; /* next, for linked list */
+
+#define MD_SOURCE_FLATBED 0
+#define MD_SOURCE_ADF 1
+#define MD_SOURCE_TMA 2
+#define MD_SOURCE_SLIDE 3
+#define MD_SOURCE_STRIPE 4
+ Microtek2_Info info[5]; /* detailed scanner spec */
+ SANE_Device sane; /* SANE generic device block */
+ char name[PATH_MAX]; /* name from config file */
+
+ SANE_Int *custom_gamma_table[4]; /* used for the custom gamma */
+ /* values before a scan starts */
+ /* the following two are derived from lut_cap */
+ int max_lut_size; /* in bytes */
+ int lut_entry_size; /* word or byte transfer in LUT */
+ uint8_t scan_source;
+ double revision;
+
+ /* basically the following two variables should go into the */
+ /* Microtek2_Scanner structure, but their values must be retained */
+ /* over several scans, and would be lost, if the application issues */
+ /* a sane_close. */
+ uint8_t *shading_table_w; /* shading table white */
+ uint8_t *shading_table_d; /* shading table dark */
+ uint8_t shading_table_contents; /* values like ms->mode */
+ /* the following structure describes the device status */
+#define MD_TLAMP_ON 2
+#define MD_FLAMP_ON 1
+#define MD_NCALIB_ON 4
+#define MD_NTRACK_ON 8
+#define MD_STICK_ON 16
+#define MD_RESERVED17_ON 128
+#define MD_CURRENT_MODE_FLATBED 0
+ struct {
+ uint8_t sskip;
+ uint8_t stick;
+ uint8_t ntrack;
+ uint8_t ncalib;
+ uint8_t tlamp;
+ uint8_t flamp;
+ uint8_t reserved17;
+ uint8_t rdyman;
+ uint8_t trdy;
+ uint8_t frdy;
+ uint8_t adp;
+ uint8_t detect;
+ uint8_t adptime;
+ uint8_t lensstatus;
+ uint8_t aloff;
+ uint8_t timeremain;
+ uint8_t tmacnt;
+ uint8_t paper;
+ uint8_t adfcnt;
+ uint8_t currentmode;
+ uint8_t buttoncount;
+ } status;
+
+ /* The following defines are related to the model. Some models have */
+ /* more or less subtle deviations from the spec, which are indicated */
+ /* by these defines */
+ uint32_t model_flags;
+#define MD_NO_SLIDE_MODE 1 /* indicates that it has slide */
+ /* mode, but it does not */
+#define MD_DATA_FORMAT_WRONG 2 /* X6 indicates wrong mode for TMA */
+#define MD_NO_ENHANCEMENTS 4 /* image enhancements do not work */
+ /* (Brightness, contrast, .....) */
+#define MD_RII_TWO_BYTES 8 /* return some image information */
+ /* in two byte */
+#define MD_NO_GAMMA 16 /* if device does not accept */
+ /* gamma tables */
+#define MD_PHANTOM336CX_TYPE_SHADING 32 /* Phantom 336cx type shading */
+#define MD_READ_CONTROL_BIT 64 /* uses "read control bit" */
+#define MD_PHANTOM_C6 128 /* It is a Phantom C6 */
+#define MD_OFFSET_2 256 /* Image data starts 2 bytes */
+ /* from the beginning of a */
+ /* scanline */
+#define MD_X6_SHORT_TRANSFER 512 /* X6 USB crashes if you read
+ too much */
+#define MD_NO_RIS_COMMAND 1024 /* doesn't like read_image_status */
+#define MD_16BIT_TRANSFER 2048 /* transfers 10/12/14bit scans as */
+ /* 16bit data */
+#define MD_CALIB_DIVISOR_600 4096 /* uses mi->calib_divisor=2 below
+ 600dpi */
+
+ size_t n_control_bytes; /* for read_control_bits; the */
+ /* number is model dependent */
+ /* and can not be inquired */
+ uint32_t shading_length; /* length of the shading image */
+ /* Phantom 336cx, C6, ... */
+ uint8_t shading_depth; /* bit depth of shading image */
+ uint8_t controlbit_offset; /* first relevant control bit */
+
+
+#define MD_MODESTRING_NUMS 4
+#define MD_MODESTRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+#define MD_MODESTRING_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define MD_MODESTRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define MD_MODESTRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
+ SANE_String_Const scanmode_list[MD_MODESTRING_NUMS + 1];
+
+#define MD_DEPTHVAL_NUMS 6
+#define MD_DEPTHVAL_16 16
+#define MD_DEPTHVAL_14 14
+#define MD_DEPTHVAL_12 12
+#define MD_DEPTHVAL_10 10
+#define MD_DEPTHVAL_8 8
+#define MD_DEPTHVAL_4 4
+ SANE_Int bitdepth_list[MD_DEPTHVAL_NUMS + 1];
+
+#define MD_SOURCESTRING_NUMS 5
+#define MD_SOURCESTRING_FLATBED "Flatbed"
+#define MD_SOURCESTRING_ADF "ADF"
+#define MD_SOURCESTRING_TMA "TMA"
+#define MD_SOURCESTRING_STRIPE "Filmstrip"
+#define MD_SOURCESTRING_SLIDE "Slide"
+ SANE_String_Const scansource_list[MD_SOURCESTRING_NUMS + 1];
+
+#define MD_HALFTONE_NUMS 12
+#define MD_HALFTONE0 "53-dot screen (53 gray levels)"
+#define MD_HALFTONE1 "Horiz. screen (65 gray levels)"
+#define MD_HALFTONE2 "Vert. screen (65 gray levels)"
+#define MD_HALFTONE3 "Mixed page (33 gray levels)"
+#define MD_HALFTONE4 "71-dot screen (29 gray levels)"
+#define MD_HALFTONE5 "60-dot #1 (26 gray levels)"
+#define MD_HALFTONE6 "60-dot #2 (26 gray levels)"
+#define MD_HALFTONE7 "Fine detail #1 (17 gray levels)"
+#define MD_HALFTONE8 "Fine detail #2 (17 gray levels)"
+#define MD_HALFTONE9 "Slant line (17 gray levels)"
+#define MD_HALFTONE10 "Posterizing (10 gray levels)"
+#define MD_HALFTONE11 "High Contrast (5 gray levels)"
+ SANE_String_Const halftone_mode_list[MD_HALFTONE_NUMS + 1];
+
+#define MD_CHANNEL_NUMS 4
+#define MD_CHANNEL_MASTER "Master"
+#define MD_CHANNEL_RED "Red"
+#define MD_CHANNEL_GREEN "Green"
+#define MD_CHANNEL_BLUE "Blue"
+ SANE_String_Const channel_list[MD_CHANNEL_NUMS + 1];
+
+#define MD_GAMMAMODE_NUMS 3
+#define MD_GAMMAMODE_LINEAR "None"
+#define MD_GAMMAMODE_SCALAR "Scalar"
+#define MD_GAMMAMODE_CUSTOM "Custom"
+ SANE_String_Const gammamode_list[MD_GAMMAMODE_NUMS + 1];
+
+ SANE_Range x_res_range_dpi; /* X resolution in dpi */
+ SANE_Range y_res_range_dpi; /* Y resolution in dpi */
+ SANE_Range x_range_mm; /* scan width in mm */
+ SANE_Range y_range_mm; /* scan height in mm */
+ SANE_Range percentage_range; /* for brightness, shadow, ... */
+ SANE_Range custom_gamma_range; /* for custom gamma values */
+ SANE_Range scalar_gamma_range; /* for scalar gamma values */
+ SANE_Range shadow_range; /* shadow of blue channel */
+ SANE_Range midtone_range; /* midtone shadow of blue channel */
+ SANE_Range exposure_range; /* for lengthening exposure time */
+ SANE_Range highlight_range; /* highlight of master channel */
+ SANE_Range threshold_range; /* 1 - 255 */
+ SANE_Range balance_range; /* for user provided color balance */
+ Config_Options opts; /* options from the config file */
+ SANE_Word opt_backend_calib_default; /* corresponds to scanner model */
+ SANE_Word opt_no_backtrack_default; /* corresponds to scanner model */
+} Microtek2_Device;
+
+
+
+/******************************************************************************/
+/* scanner structure (one for each device in use) */
+/* ....all the state needed to define a scan request */
+/******************************************************************************/
+
+typedef struct Microtek2_Scanner {
+ struct Microtek2_Scanner *next; /* for linked list */
+ Microtek2_Device *dev; /* raw device info */
+ Option_Value val[NUM_OPTIONS + 1]; /* option values for session */
+ SANE_Parameters params; /* format, lastframe, lines, depth, ppl, bpl */
+ SANE_Option_Descriptor sod[NUM_OPTIONS + 1]; /* option list for session */
+
+ uint8_t *gamma_table;
+ uint8_t *shading_image; /* used for shading image */
+ uint8_t *condensed_shading_w; /* used when a model uses "read */
+ uint8_t *condensed_shading_d; /* control bit", stores the relevant */
+ /* shading pixels for each color */
+ uint8_t *temporary_buffer; /* used when automatic adjustment */
+ /* is selected */
+ char *gamma_mode; /* none, linear or custom */
+
+/* the following defines must correspond to byte 25 of SET WINDOW body */
+#define MS_MODE_LINEART 0x00
+#define MS_MODE_HALFTONE 0x01
+#define MS_MODE_GRAY 0x02
+#define MS_MODE_LINEARTFAKE 0x12 /* no real mode */
+#define MS_MODE_COLOR 0x05
+
+/* the following defines must correspond to byte 31 of SET WINDOW body */
+#define MS_SOURCE_FLATBED 0x00
+#define MS_SOURCE_ADF 0x01
+#define MS_SOURCE_TMA 0x02
+#define MS_SOURCE_STRIPE 0x05
+#define MS_SOURCE_SLIDE 0x06
+
+ SANE_Int mode;
+ SANE_Int depth;
+ SANE_Int x_resolution_dpi;
+ SANE_Int y_resolution_dpi;
+ SANE_Int x1_dots; /* x-position in units of optical resolution */
+ SANE_Int y1_dots; /* same for y-position */
+ SANE_Int width_dots; /* scan width in units of optical resolution */
+ SANE_Int height_dots; /* same for height */
+ uint8_t brightness_m;
+ uint8_t contrast_m;
+ uint8_t exposure_m;
+ uint8_t shadow_m;
+ uint8_t midtone_m;
+ uint8_t highlight_m;
+ uint8_t brightness_r;
+ uint8_t contrast_r;
+ uint8_t exposure_r;
+ uint8_t shadow_r;
+ uint8_t midtone_r;
+ uint8_t highlight_r;
+ uint8_t brightness_g;
+ uint8_t contrast_g;
+ uint8_t exposure_g;
+ uint8_t shadow_g;
+ uint8_t midtone_g;
+ uint8_t highlight_g;
+ uint8_t brightness_b;
+ uint8_t contrast_b;
+ uint8_t exposure_b;
+ uint8_t shadow_b;
+ uint8_t midtone_b;
+ uint8_t highlight_b;
+ uint8_t threshold;
+
+ SANE_Bool use_external_ht;
+ SANE_Byte internal_ht_index;
+ uint8_t stay;
+ uint8_t rawdat;
+ SANE_Bool quality;
+ SANE_Bool fastscan;
+ SANE_Byte scan_source;
+ uint8_t no_backtracking;
+ uint8_t lightlid35;
+ uint8_t auto_adjust;
+ uint8_t calib_backend;
+ uint8_t colorbalance_adjust;
+ int current_pass; /* current pass if 3-pass scan */
+ int lut_size; /* size of gamma lookup table */
+ int lut_entry_size; /* size of one entry in lookup table */
+ uint32_t lut_size_bytes; /* size of LUT in bytes */
+ uint8_t word; /* word transfer, used in some read cmds */
+ /* MS_COLOR_X must correspond to color field in READ IMAGE STATUS */
+#define MS_COLOR_RED 0
+#define MS_COLOR_GREEN 1
+#define MS_COLOR_BLUE 2
+#define MS_COLOR_ALL 3
+ uint8_t current_color; /* for gamma calc. and 3-pass scanners */
+ uint8_t current_read_color; /* dto, for RI and RIS */
+ uint8_t dark; /* is 1 for reading dark shading */
+ uint32_t ppl; /* pixels per line as returned by RII */
+ uint32_t bpl; /* bytes per line as returned by RII */
+ uint32_t remaining_bytes; /* remaining bytes as returned by RII */
+ uint32_t real_remaining_bytes;/* bytes to transfer to the frontend */
+ uint32_t real_bpl; /* bpl to transfer to the frontend */
+ SANE_Int src_remaining_lines; /* remaining lines to read */
+ SANE_Int src_lines_to_read; /* actual number of lines read */
+ SANE_Int src_max_lines; /* maximum number of lines that fit */
+ /* into the scsi buffer */
+ /* sent to the frontend */
+ int bits_per_pixel_in; /* bits per pixel transferred from scanner */
+ int bits_per_pixel_out; /* bits per pixel transf. to frontend */
+ uint32_t src_buffer_size; /* size of the buffer */
+ int transfer_length; /* transfer length for RI command */
+ uint8_t balance[3]; /* user provided balance factor for */
+ /* red, green and blue data */
+ struct {
+ uint8_t *src_buffer[2]; /* two buffers because of CCD gap */
+ uint8_t *src_buf;
+ int current_src;
+ SANE_Int free_lines;
+ SANE_Int free_max_lines;
+ uint8_t *current_pos[3]; /* actual position in the source buffer */
+ int planes[2][3]; /* # of red, green, blue planes in the */
+ /* current source buffer and leftover */
+ /* planes from previous "read image" */
+ } buf;
+
+ SANE_Bool onepass;
+
+ size_t n_control_bytes; /* for READ CONTROL BITS */
+ uint8_t *control_bytes; /* pointer to the result */
+
+ int scanning; /* true == between sane_start & sane_read=EOF */
+ int cancelled;
+ int sfd; /* SCSI filedescriptor */
+ int fd[2]; /* file descriptors for pipe */
+ SANE_Pid pid; /* pid of child process */
+ FILE *fp;
+
+} Microtek2_Scanner;
+
+/******************************************************************************/
+/* Function prototypes */
+/******************************************************************************/
+
+static SANE_Status
+add_device_list(SANE_String_Const, Microtek2_Device **);
+
+static SANE_Status
+attach(Microtek2_Device *);
+
+static SANE_Status
+attach_one (const char *);
+
+static SANE_Status
+auto_adjust_proc_data (Microtek2_Scanner *, uint8_t **);
+
+static SANE_Status
+calc_cx_shading_line(Microtek2_Scanner *); /* (KF) new */
+
+static SANE_Status
+calculate_gamma(Microtek2_Scanner *, uint8_t *, int, char *);
+
+static SANE_Status
+calculate_sane_params(Microtek2_Scanner *);
+
+static SANE_Status
+cancel_scan(Microtek2_Scanner *);
+
+static SANE_Status
+check_inquiry(Microtek2_Device *, SANE_String *);
+
+static void
+check_option(const char *, Config_Options *);
+
+static SANE_Status
+chunky_copy_pixels(Microtek2_Scanner *, uint8_t *);
+
+static SANE_Status
+chunky_proc_data(Microtek2_Scanner *);
+
+#if 0
+static void
+chunky_set_exposure(uint8_t *, uint32_t, uint8_t, uint8_t, int);
+#endif
+
+static void
+cleanup_scanner(Microtek2_Scanner *);
+
+static SANE_Status
+condense_shading(Microtek2_Scanner *);
+
+#ifdef HAVE_AUTHORIZATION
+static SANE_Status
+do_authorization(char *);
+#endif
+
+static SANE_Status
+read_shading_image(Microtek2_Scanner *);
+
+static SANE_Status
+dump_area(uint8_t *, int, char *);
+
+static SANE_Status
+dump_area2(uint8_t *, int, char *);
+
+/* currently not used */
+#if 0
+static SANE_Status
+dump_to_file(uint8_t *, int, char *, char *);
+#endif
+
+static SANE_Status
+dump_attributes(Microtek2_Info *);
+
+static void
+get_calib_params(Microtek2_Scanner *);
+
+static SANE_Status
+get_cshading_values(Microtek2_Scanner *,uint8_t, uint32_t, float, int,
+ float *, float *); /* (KF) new */
+static SANE_Status
+get_scan_mode_and_depth(Microtek2_Scanner *, int *, SANE_Int *, int *, int *);
+
+static SANE_Status
+get_scan_parameters(Microtek2_Scanner *);
+
+static SANE_Status
+get_lut_size(Microtek2_Info *, int *, int *);
+
+static SANE_Status
+gray_copy_pixels(Microtek2_Scanner *ms, uint8_t *, int, int);
+
+static SANE_Status
+gray_proc_data(Microtek2_Scanner *);
+
+#if 0
+static void
+gray_set_exposure(uint8_t *, uint32_t, uint8_t, uint8_t);
+#endif
+
+static SANE_Status
+init_options(Microtek2_Scanner *, uint8_t);
+
+static SANE_Status
+lineartfake_copy_pixels(Microtek2_Scanner *, uint8_t *, uint32_t, uint8_t,
+ int, FILE *);
+
+static SANE_Status
+lineartfake_proc_data(Microtek2_Scanner *);
+
+static SANE_Status
+lplconcat_copy_pixels(Microtek2_Scanner *, uint8_t **, int, int);
+
+static SANE_Status
+lplconcat_proc_data(Microtek2_Scanner *ms);
+
+static size_t
+max_string_size (const SANE_String_Const * /* strings[] */);
+
+static void
+parse_config_file(FILE *, Config_Temp **);
+
+static SANE_Status
+prepare_buffers(Microtek2_Scanner *);
+
+static SANE_Status
+prepare_shading_data(Microtek2_Scanner *, uint32_t, uint8_t **);
+
+static SANE_Status
+proc_onebit_data(Microtek2_Scanner *);
+
+static SANE_Status
+read_cx_shading_image(Microtek2_Scanner *);
+
+static SANE_Status
+read_cx_shading(Microtek2_Scanner *);
+
+static int
+reader_process(void *);
+
+static SANE_Status
+restore_gamma_options(SANE_Option_Descriptor *, Option_Value *);
+
+static SANE_Status
+segreg_copy_pixels(Microtek2_Scanner *);
+
+static SANE_Status
+segreg_proc_data(Microtek2_Scanner *ms);
+
+static void
+set_exposure(Microtek2_Scanner *);
+
+static SANE_Status
+set_option_dependencies(Microtek2_Scanner *,
+ SANE_Option_Descriptor *, Option_Value *);
+
+static SANE_Status
+shading_function(Microtek2_Scanner *, uint8_t *);
+
+static RETSIGTYPE
+signal_handler (int);
+
+static SANE_Status
+wordchunky_copy_pixels(uint8_t *, uint32_t, int, FILE *);
+
+static SANE_Status
+wordchunky_proc_data(Microtek2_Scanner *);
+
+typedef int (*qsortfunc)(const void *, const void *);
+
+static int compare_func_16(const uint16_t *p1, uint16_t *p2)
+ {
+ return ( *p1 - *p2 );
+ }
+
+
+/******************************************************************************/
+/* Function prototypes for basic SCSI commands */
+/******************************************************************************/
+
+static SANE_Status
+scsi_inquiry(Microtek2_Info *, char *);
+
+static SANE_Status
+scsi_read_attributes(Microtek2_Info *, char *, uint8_t);
+
+static SANE_Status
+scsi_read_control_bits(Microtek2_Scanner *);
+
+/* currently not used */
+/* static SANE_Status
+scsi_read_gamma(Microtek2_Scanner *); */
+
+static SANE_Status
+scsi_read_image(Microtek2_Scanner *, uint8_t *, int);
+
+static SANE_Status
+scsi_read_image_info(Microtek2_Scanner *);
+
+static SANE_Status
+scsi_read_image_status(Microtek2_Scanner *);
+
+static SANE_Status
+scsi_read_system_status(Microtek2_Device *, int);
+
+/* currently not used */
+/* static SANE_Status
+scsi_request_sense(Microtek2_Scanner *); */
+
+static SANE_Status
+scsi_send_gamma(Microtek2_Scanner *);
+
+static SANE_Status
+scsi_send_shading(Microtek2_Scanner *, uint8_t *, uint32_t, uint8_t);
+
+static SANE_Status
+scsi_read_shading(Microtek2_Scanner *, uint8_t *, uint32_t);
+
+static SANE_Status
+scsi_send_system_status(Microtek2_Device *, int);
+
+static SANE_Status
+scsi_sense_handler (int, u_char *, void *);
+
+static SANE_Status
+scsi_test_unit_ready(Microtek2_Device *);
+
+static SANE_Status
+scsi_set_window(Microtek2_Scanner *, int);
+
+static SANE_Status
+scsi_wait_for_image(Microtek2_Scanner *);
+
+#endif
diff --git a/backend/mustek.c b/backend/mustek.c
new file mode 100644
index 0000000..7f0db8c
--- /dev/null
+++ b/backend/mustek.c
@@ -0,0 +1,6776 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Czechanowski,
+ 1998 Andreas Bolsch for extension to ScanExpress models version 0.6,
+ 2000-2005 Henning Meier-Geinitz,
+ 2003 James Perry (600 EP).
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek and some Trust flatbed
+ scanners with SCSI, parallel port (600 EP) or proprietary interface. */
+
+
+/**************************************************************************/
+/* Mustek backend version */
+#define BUILD 138
+/**************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_ab306.h"
+#include "../include/sane/sanei_thread.h"
+
+#define BACKEND_NAME mustek
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include "mustek.h"
+#include "mustek_scsi_pp.h"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/* Debug level from sanei_init_debug */
+static SANE_Int debug_level;
+
+/* Maximum # of inches to scan in one swoop. 0 means "unlimited."
+ This is here to be nice on the SCSI bus---Mustek scanners don't
+ disconnect while scanning is in progress, which confuses some
+ drivers that expect no reasonable SCSI request would take more than
+ 10 seconds. That's not really true for Mustek scanners operating
+ in certain modes, hence this limit. Usually you don't need to set it. */
+static double strip_height;
+
+/* Should we wait for the scan slider to return after each scan? */
+static SANE_Bool force_wait;
+
+/* Should we disable double buffering when reading data from the scanner? */
+static SANE_Bool disable_double_buffering;
+
+static SANE_Int num_devices;
+static Mustek_Device *first_dev;
+static Mustek_Scanner *first_handle;
+static const SANE_Device **devlist = 0;
+
+/* Array of newly attached devices */
+static Mustek_Device **new_dev;
+
+/* Length of new_dev array */
+static SANE_Int new_dev_len;
+
+/* Number of entries alloced for new_dev */
+static SANE_Int new_dev_alloced;
+
+static SANE_Int lamp_off_time = 60;
+
+/* Used for line-distance correction: */
+static const SANE_Int color_seq[] = {
+ 1, 2, 0 /* green, blue, red */
+};
+
+/* Which modes are supported? */
+static SANE_String_Const mode_list_paragon[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+static SANE_String_Const mode_list_se[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static SANE_String_Const bit_depth_list_pro[] = {
+ "8", "12",
+ 0
+};
+
+/* Some scanners support setting speed manually */
+static SANE_String_Const speed_list[] = {
+ SANE_I18N ("Slowest"), SANE_I18N ("Slower"), SANE_I18N ("Normal"),
+ SANE_I18N ("Faster"), SANE_I18N ("Fastest"),
+ 0
+};
+
+/* Which scan-sources are supported? */
+static const SANE_String_Const source_list[] = {
+ SANE_I18N ("Flatbed"),
+ 0
+};
+static SANE_String_Const adf_source_list[] = {
+ SANE_I18N ("Flatbed"), SANE_I18N ("Automatic Document Feeder"),
+ 0
+};
+static SANE_String_Const ta_source_list[] = {
+ SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"),
+ 0
+};
+
+/* Range used for gamma and halftone pattern */
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/* Which kind of halftone patterns are available? */
+static SANE_String_Const halftone_list[] = {
+ SANE_I18N ("8x8 coarse"), SANE_I18N ("8x8 normal"), SANE_I18N ("8x8 fine"),
+ SANE_I18N ("8x8 very fine"), SANE_I18N ("6x6 normal"),
+ SANE_I18N ("5x5 coarse"), SANE_I18N ("5x5 fine"), SANE_I18N ("4x4 coarse"),
+ SANE_I18N ("4x4 normal"), SANE_I18N ("4x4 fine"), SANE_I18N ("3x3 normal"),
+ SANE_I18N ("2x2 normal"), SANE_I18N ("8x8 custom"),
+ SANE_I18N ("6x6 custom"),
+ SANE_I18N ("5x5 custom"), SANE_I18N ("4x4 custom"),
+ SANE_I18N ("3x3 custom"),
+ SANE_I18N ("2x2 custom"),
+ 0
+};
+
+/* Range used for brightness and contrast */
+static const SANE_Range percentage_range = {
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+/* SCSI command buffers used by the backend */
+static const SANE_Byte scsi_inquiry[] = {
+ MUSTEK_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00
+};
+static const SANE_Byte scsi_test_unit_ready[] = {
+ MUSTEK_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const SANE_Byte scsi_area_and_windows[] = {
+ MUSTEK_SCSI_AREA_AND_WINDOWS, 0x00, 0x00, 0x00, 0x09, 0x00
+};
+static const SANE_Byte scsi_request_sense[] = {
+ MUSTEK_SCSI_REQUEST_SENSE, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+static const SANE_Byte scsi_start_stop[] = {
+ MUSTEK_SCSI_START_STOP, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const SANE_Byte scsi_ccd_distance[] = {
+ MUSTEK_SCSI_CCD_DISTANCE, 0x00, 0x00, 0x00, 0x05, 0x00
+};
+static const SANE_Byte scsi_get_image_status[] = {
+ MUSTEK_SCSI_GET_IMAGE_STATUS, 0x00, 0x00, 0x00, 0x06, 0x00
+};
+static const SANE_Byte scsi_set_window[] = {
+ MUSTEK_SCSI_SET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00
+};
+static const SANE_Byte scsi_get_window[] = {
+ MUSTEK_SCSI_GET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00
+};
+static const SANE_Byte scsi_read_data[] = {
+ MUSTEK_SCSI_READ_DATA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const SANE_Byte scsi_send_data[] = {
+ MUSTEK_SCSI_SEND_DATA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const SANE_Byte scsi_lookup_table[] = {
+ MUSTEK_SCSI_LOOKUP_TABLE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+};
+
+/* prototypes */
+static SANE_Status area_and_windows (Mustek_Scanner * s);
+static SANE_Status inquiry (Mustek_Scanner * s);
+
+/* Test if this machine is little endian (from coolscan.c) */
+static SANE_Bool
+little_endian (void)
+{
+ SANE_Int testvalue = 255;
+ uint8_t *firstbyte = (uint8_t *) & testvalue;
+
+ if (*firstbyte == 255)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+/* Used for Pro series. First value seems to be always 85, second one varies.
+ First bit of second value clear == device ready (?) */
+static SANE_Status
+scsi_sense_wait_ready (Mustek_Scanner * s)
+{
+ struct timeval now, start;
+ SANE_Status status;
+ size_t len;
+ SANE_Byte sense_buffer[4];
+ SANE_Byte bytetxt[300], dbgtxt[300], *pp;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ len = sizeof (sense_buffer);
+
+ DBG (5, "scsi_sense_wait_ready: command size = %ld, sense size = %ld\n",
+ (long int) sizeof (scsi_request_sense), (long int) len);
+ status = sanei_scsi_cmd (s->fd, scsi_request_sense,
+ sizeof (scsi_request_sense), sense_buffer,
+ &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "scsi_sense_wait_ready: failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dbgtxt[0] = '\0';
+ for (pp = sense_buffer; pp < (sense_buffer + 4); pp++)
+ {
+ sprintf ((SANE_String) bytetxt, " %02x", *pp);
+ strcat ((SANE_String) dbgtxt, (SANE_String) bytetxt);
+ }
+ DBG (5, "scsi_sense_wait_ready: sensebuffer: %s\n", dbgtxt);
+
+ if (!(sense_buffer[1] & 0x01))
+ {
+ DBG (4, "scsi_sense_wait_ready: ok\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (1, "scsi_sense_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+/* Used for 3pass series */
+static SANE_Status
+scsi_area_wait_ready (Mustek_Scanner * s)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ DBG (5, "scsi_area_wait_ready\n");
+ while (1)
+ {
+ status = area_and_windows (s);
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (3, "scsi_area_wait_ready: failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (1, "scsi_area_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+scsi_unit_wait_ready (Mustek_Scanner * s)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG (5, "scsi_unit_wait_ready: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd (s->fd, scsi_test_unit_ready,
+ sizeof (scsi_test_unit_ready), 0, 0);
+ DBG (5, "scsi_unit_wait_ready: TEST_UNIT_READY finished\n");
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (3, "scsi_unit_wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+scsi_inquiry_wait_ready (Mustek_Scanner * s)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG (5, "scsi_inquiry_wait_ready: sending INQUIRY\n");
+ status = inquiry (s);
+ DBG (5, "scsi_inquiry_wait_ready: INQUIRY finished\n");
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (3, "scsi_unit_wait_ready: inquiry failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (500000); /* retry after 500ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+n_wait_ready (Mustek_Scanner * s)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ DBG (5, "n_wait_ready\n");
+ while (1)
+ {
+ status = sanei_ab306_test_ready (s->fd);
+ if (status == SANE_STATUS_GOOD)
+ return SANE_STATUS_GOOD;
+
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (1, "n_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ }
+}
+
+static SANE_Status
+scsi_pp_wait_ready (Mustek_Scanner * s)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ DBG (5, "scsi_pp_wait_ready\n");
+ while (1)
+ {
+ status = mustek_scsi_pp_test_ready (s->fd);
+ if (status == SANE_STATUS_GOOD)
+ return SANE_STATUS_GOOD;
+
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG (1, "scsi_pp_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ }
+}
+
+static SANE_Status
+dev_wait_ready (Mustek_Scanner * s)
+{
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ return n_wait_ready (s);
+ else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
+ return scsi_pp_wait_ready (s);
+ else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ SANE_Status status;
+
+ /* some 3-pass scanners seem to need the inquiry wait, too */
+ status = scsi_area_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return scsi_inquiry_wait_ready (s);
+ }
+ else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ || (s->hw->flags & MUSTEK_FLAG_PARAGON_2))
+ return scsi_inquiry_wait_ready (s);
+ else if (s->hw->flags & MUSTEK_FLAG_PRO)
+ return scsi_sense_wait_ready (s);
+ else
+ return scsi_unit_wait_ready (s);
+}
+
+static SANE_Status
+dev_open (SANE_String_Const devname, Mustek_Scanner * s,
+ SANEI_SCSI_Sense_Handler handler)
+{
+ SANE_Status status;
+
+ DBG (5, "dev_open %s\n", devname);
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ s->hw->buffer_size = s->hw->max_buffer_size;
+ status = sanei_scsi_open_extended (devname, &s->fd, handler, 0,
+ &s->hw->buffer_size);
+#else
+ s->hw->buffer_size = MIN (sanei_scsi_max_request_size,
+ s->hw->max_buffer_size);
+ status = sanei_scsi_open (devname, &s->fd, handler, 0);
+#endif
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (3, "dev_open: %s is a SCSI device\n", devname);
+ DBG (4, "dev_open: wanted %d kbytes, got %d kbytes buffer\n",
+ s->hw->max_buffer_size / 1024, s->hw->buffer_size / 1024);
+ if (s->hw->buffer_size < 4096)
+ {
+ DBG (1, "dev_open: sanei_scsi_open buffer too small\n");
+ sanei_scsi_close (s->fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ else
+ {
+ DBG (3, "dev_open: %s: can't open %s as a SCSI device\n",
+ sane_strstatus (status), devname);
+
+ status = sanei_ab306_open (devname, &s->fd);
+ if (status == SANE_STATUS_GOOD)
+ {
+ s->hw->flags |= MUSTEK_FLAG_N;
+ DBG (3, "dev_open: %s is an AB306N device\n", devname);
+ }
+ else
+ {
+ DBG (3, "dev_open: %s: can't open %s as an AB306N device\n",
+ sane_strstatus (status), devname);
+
+ status = mustek_scsi_pp_open (devname, &s->fd);
+ if (status == SANE_STATUS_GOOD)
+ {
+ s->hw->flags |= MUSTEK_FLAG_SCSI_PP;
+ DBG (3, "dev_open: %s is a SCSI-over-parallel device\n",
+ devname);
+ }
+ else
+ {
+ DBG (3,
+ "dev_open: %s: can't open %s as a SCSI-over-parallel device\n",
+ sane_strstatus (status), devname);
+ DBG (1, "dev_open: can't open %s\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+dev_cmd (Mustek_Scanner * s, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ SANE_Status status;
+ SANE_Byte cmd_byte_list[50];
+ SANE_Byte cmd_byte[5];
+ const SANE_Byte *pp;
+
+ DBG (5, "dev_cmd: fd=%d, src=%p, src_size=%ld, dst=%p, dst_size=%ld\n",
+ s->fd, src, (long int) src_size, dst,
+ (long int) (dst_size ? *dst_size : 0));
+
+ if (src && (debug_level >= 5)) /* output data sent to SCSI device */
+ {
+ cmd_byte_list[0] = '\0';
+ for (pp = (const SANE_Byte *) src;
+ pp < (((const SANE_Byte *) src) + src_size); pp++)
+ {
+ sprintf ((SANE_String) cmd_byte, " %02x", *pp);
+ strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte);
+ if (((pp - (const SANE_Byte *) src) % 0x10 == 0x0f)
+ || (pp >= (((const SANE_Byte *) src) + src_size - 1)))
+ {
+ DBG (5, "dev_cmd: sending: %s\n", cmd_byte_list);
+ cmd_byte_list[0] = '\0';
+ }
+ }
+ }
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ status = sanei_ab306_cmd (s->fd, src, src_size, dst, dst_size);
+ else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
+ status = mustek_scsi_pp_cmd (s->fd, src, src_size, dst, dst_size);
+ else
+ status = sanei_scsi_cmd (s->fd, src, src_size, dst, dst_size);
+
+ if (dst && dst_size && (debug_level >= 5))
+ /* output data received from SCSI device */
+ {
+ cmd_byte_list[0] = '\0';
+ for (pp = (const SANE_Byte *) dst;
+ pp < (((const SANE_Byte *) dst) + *dst_size); pp++)
+ {
+ sprintf ((SANE_String) cmd_byte, " %02x", *pp);
+ strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte);
+ if (((pp - (const SANE_Byte *) dst) % 0x10 == 0x0f)
+ || (pp >= (((const SANE_Byte *) dst) + *dst_size - 1)))
+ {
+ DBG (5, "dev_cmd: receiving: %s\n", cmd_byte_list);
+ cmd_byte_list[0] = '\0';
+ }
+ }
+ }
+
+ DBG (5, "dev_cmd: finished: dst_size=%ld, status=%s\n",
+ (long int) (dst_size ? *dst_size : 0), sane_strstatus (status));
+ return status;
+}
+
+static SANE_Status
+dev_req_wait (void *id)
+{
+ if (!id)
+ return SANE_STATUS_GOOD;
+ else
+ return sanei_scsi_req_wait (id);
+}
+
+static SANE_Status
+dev_block_read_start (Mustek_Scanner * s, SANE_Int lines)
+{
+ DBG (4, "dev_block_read_start: entering block for %d lines\n", lines);
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ SANE_Byte readlines[6];
+
+ memset (readlines, 0, sizeof (readlines));
+ readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
+ readlines[2] = (lines >> 16) & 0xff;
+ readlines[3] = (lines >> 8) & 0xff;
+ readlines[4] = (lines >> 0) & 0xff;
+ return sanei_ab306_cmd (s->fd, readlines, sizeof (readlines), 0, 0);
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
+ {
+ SANE_Byte readlines[6];
+
+ memset (readlines, 0, sizeof (readlines));
+ readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
+ readlines[2] = (lines >> 16) & 0xff;
+ readlines[3] = (lines >> 8) & 0xff;
+ readlines[4] = (lines >> 0) & 0xff;
+ return mustek_scsi_pp_cmd (s->fd, readlines, sizeof (readlines), 0, 0);
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2)
+ {
+ SANE_Byte buffer[6];
+ size_t len;
+ SANE_Int color;
+ SANE_Status status;
+
+ /* reset line-distance values */
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ for (color = 0; color < 3; ++color)
+ {
+ s->ld.index[color] = -s->ld.dist[color];
+ }
+ s->ld.lmod3 = -1;
+ s->ld.ld_line = 0;
+ }
+
+ /* Get image status (necessary to start new block) */
+ len = sizeof (buffer);
+ status = dev_cmd (s, scsi_get_image_status,
+ sizeof (scsi_get_image_status), buffer, &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* tell scanner how many lines to scan in one block */
+ memset (buffer, 0, sizeof (buffer));
+ buffer[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
+ buffer[2] = (lines >> 16) & 0xff;
+ buffer[3] = (lines >> 8) & 0xff;
+ buffer[4] = (lines >> 0) & 0xff;
+ buffer[5] = 0x04;
+ return sanei_scsi_cmd (s->fd, buffer, sizeof (buffer), 0, 0);
+ }
+ else
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+dev_read_req_enter (Mustek_Scanner * s, SANE_Byte * buf, SANE_Int lines,
+ SANE_Int bpl, size_t * lenp, void **idp, SANE_Int bank,
+ SANE_Byte * command)
+{
+ *lenp = lines * bpl;
+
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ SANE_Int planes;
+
+ *idp = 0;
+ planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1;
+
+ return sanei_ab306_rdata (s->fd, planes, buf, lines, bpl);
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
+ {
+ SANE_Int planes;
+
+ *idp = 0;
+ planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1;
+
+ return mustek_scsi_pp_rdata (s->fd, planes, buf, lines, bpl);
+ }
+ else
+ {
+ if (s->hw->flags & MUSTEK_FLAG_SE)
+ {
+ if (s->mode & MUSTEK_MODE_COLOR)
+ lines *= 3;
+
+ memset (command, 0, 10);
+ command[0] = MUSTEK_SCSI_READ_DATA;
+ command[6] = bank; /* buffer bank not used ??? */
+ command[7] = (lines >> 8) & 0xff;
+ command[8] = (lines >> 0) & 0xff;
+ return sanei_scsi_req_enter (s->fd, command, 10, buf, lenp, idp);
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+ memset (command, 0, 6);
+ command[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
+ command[2] = ((lines * bpl) >> 16) & 0xff;
+ command[3] = ((lines * bpl) >> 8) & 0xff;
+ command[4] = ((lines * bpl) >> 0) & 0xff;
+
+ return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp);
+ }
+ else /* Paragon series */
+ {
+ memset (command, 0, 6);
+ command[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
+ command[2] = (lines >> 16) & 0xff;
+ command[3] = (lines >> 8) & 0xff;
+ command[4] = (lines >> 0) & 0xff;
+ return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp);
+ }
+ }
+}
+
+static void
+dev_close (Mustek_Scanner * s)
+{
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ sanei_ab306_close (s->fd);
+ else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
+ mustek_scsi_pp_close (s->fd);
+ else
+ sanei_scsi_close (s->fd);
+}
+
+static SANE_Status
+sense_handler (SANE_Int scsi_fd, SANE_Byte * result, void *arg)
+{
+ if (!result)
+ {
+ DBG (5, "sense_handler: no sense buffer\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (!arg)
+ DBG (5, "sense_handler: got sense code %02x for fd %d (arg = null)\n",
+ result[0], scsi_fd);
+ else
+ DBG (5, "sense_handler: got sense code %02x for fd %d (arg = %uc)\n",
+ result[0], scsi_fd, *(SANE_Byte *) arg);
+ switch (result[0])
+ {
+ case 0x00:
+ break;
+
+ case 0x82:
+ if (result[1] & 0x80)
+ {
+ DBG (3, "sense_handler: ADF is jammed\n");
+ return SANE_STATUS_JAMMED; /* ADF is jammed */
+ }
+ break;
+
+ case 0x83:
+ if (result[2] & 0x02)
+ {
+ DBG (3, "sense_handler: ADF is out of documents\n");
+ return SANE_STATUS_NO_DOCS; /* ADF out of documents */
+ }
+ break;
+
+ case 0x84:
+ if (result[1] & 0x10)
+ {
+ DBG (3, "sense_handler: transparency adapter cover open\n");
+ return SANE_STATUS_COVER_OPEN; /* open transparency adapter cover */
+ }
+ break;
+
+ default:
+ DBG (1, "sense_handler: got unknown sense code %02x for fd %d\n",
+ result[0], scsi_fd);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+inquiry (Mustek_Scanner * s)
+{
+ SANE_Byte result[INQ_LEN];
+ size_t size;
+ SANE_Status status;
+
+ DBG (5, "inquiry: sending INQUIRY\n");
+ size = sizeof (result);
+
+ memset (result, 0, size);
+
+ status = dev_cmd (s, scsi_inquiry, sizeof (scsi_inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* checking ADF status */
+ if (s->hw->flags & MUSTEK_FLAG_ADF)
+ {
+ if (result[63] & (1 << 3))
+ {
+ s->hw->flags |= MUSTEK_FLAG_ADF_READY;
+ DBG (4, "inquiry: ADF ready\n");
+ }
+ else
+ {
+ s->hw->flags &= ~MUSTEK_FLAG_ADF_READY;
+ DBG (4, "inquiry: ADF not ready (out of paper)\n");
+ }
+ }
+ if (!result[0])
+ return SANE_STATUS_DEVICE_BUSY;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+paragon_2_get_adf_status (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ size_t len;
+ SANE_Byte sense_buffer[4];
+
+ len = sizeof (sense_buffer);
+
+ status = sanei_scsi_cmd (s->fd, scsi_request_sense,
+ sizeof (scsi_request_sense), sense_buffer, &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "paragon_2_get_adf_status: %s\n", sane_strstatus (status));
+ return status;
+ }
+ DBG (5, "paragon_2_get_adf_status: sense_buffer: %x %x %x %x\n",
+ sense_buffer[0], sense_buffer[1], sense_buffer[3], sense_buffer[3]);
+
+ if (sense_buffer[0] == 0x00 && sense_buffer[1] == 0x00)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+static SANE_Bool
+ta_available_pro (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ size_t len;
+ SANE_Byte sense_buffer[4];
+
+ len = sizeof (sense_buffer);
+
+ status = sanei_scsi_cmd (s->fd, scsi_request_sense,
+ sizeof (scsi_request_sense), sense_buffer, &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "ta_available_pro: failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ DBG (5, "ta_available_pro: sense_buffer[2] = %x\n", sense_buffer[2]);
+
+ scsi_unit_wait_ready (s);
+ if (sense_buffer[2] == 0x40)
+ return SANE_TRUE;
+
+ return SANE_FALSE;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, Mustek_Device ** devp, SANE_Bool may_wait)
+{
+ SANE_Int mustek_scanner, fw_revision;
+ SANE_Byte result[INQ_LEN];
+ SANE_Byte inquiry_byte_list[50], inquiry_text_list[17];
+ SANE_Byte inquiry_byte[5], inquiry_text[5];
+ SANE_Byte *model_name = result + 44;
+ Mustek_Scanner s;
+ Mustek_Device *dev, new_dev;
+ SANE_Status status;
+ size_t size;
+ SANE_String scsi_device_type[] = {
+ "Direct-Access", "Sequential-Access", "Printer", "Processor",
+ "Write-Once", "CD-ROM", "Scanner", "Optical Memory", "Medium Changer",
+ "Communications"
+ };
+ SANE_Byte scsi_vendor[9];
+ SANE_Byte scsi_product[17];
+ SANE_Byte scsi_revision[5];
+ SANE_Byte *pp;
+ SANE_Bool warning = SANE_FALSE;
+ SANE_Int firmware_format = 0;
+ SANE_Int firmware_revision_system = 0;
+
+ if (devp)
+ *devp = 0;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ memset (&new_dev, 0, sizeof (new_dev));
+ memset (&s, 0, sizeof (s));
+ s.hw = &new_dev;
+ s.hw->max_buffer_size = 8 * 1024;
+
+ DBG (3, "attach: trying device %s\n", devname);
+
+ status = dev_open (devname, &s, sense_handler);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (may_wait || force_wait)
+ dev_wait_ready (&s);
+
+ DBG (5, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ memset (result, 0, sizeof (result));
+ status = dev_cmd (&s, scsi_inquiry, sizeof (scsi_inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD || size != INQ_LEN)
+ {
+ DBG (1, "attach: inquiry for device %s failed (%s)\n", devname,
+ sane_strstatus (status));
+ dev_close (&s);
+ return status;
+ }
+
+ status = dev_wait_ready (&s);
+ dev_close (&s);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if ((result[0] & 0x1f) != 0x06)
+ {
+ DBG (1, "attach: device %s doesn't look like a scanner at all (%d)\n",
+ devname, result[0] & 0x1f);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (debug_level >= 3)
+ {
+ /* clear spaces and special chars */
+ strncpy ((SANE_String) scsi_vendor, (SANE_String) result + 8, 8);
+ scsi_vendor[8] = '\0';
+ pp = scsi_vendor + 7;
+ while (pp >= scsi_vendor && (*pp == ' ' || *pp >= 127))
+ *pp-- = '\0';
+ strncpy ((SANE_String) scsi_product, (SANE_String) result + 16, 16);
+ scsi_product[16] = '\0';
+ pp = scsi_product + 15;
+ while (pp >= scsi_product && (*pp == ' ' || *pp >= 127))
+ *pp-- = '\0';
+ strncpy ((SANE_String) scsi_revision, (SANE_String) result + 32, 4);
+ scsi_revision[4] = '\0';
+ pp = scsi_revision + 3;
+ while (pp >= scsi_revision && (*pp == ' ' || *pp >= 127))
+ *pp-- = '\0';
+ DBG (3, "attach: SCSI Vendor: `%-8s' Model: `%-16s' Rev.: `%-4s'\n",
+ scsi_vendor, scsi_product, scsi_revision);
+ DBG (3, "attach: SCSI Type: %s; ANSI rev.: %d\n",
+ ((result[0] & 0x1f) < 0x10) ?
+ scsi_device_type[result[0] & 0x1f] : "Unknown", result[2] & 0x03);
+ DBG (3, "attach: SCSI flags: %s%s%s%s%s%s%s\n",
+ (result[7] & 0x80) ? "RelAdr " : "",
+ (result[7] & 0x40) ? "WBus32 " : "",
+ (result[7] & 0x20) ? "WBus16 " : "",
+ (result[7] & 0x10) ? "Sync " : "",
+ (result[7] & 0x08) ? "Linked " : "",
+ (result[7] & 0x02) ? "CmdQue " : "",
+ (result[7] & 0x01) ? "SftRe " : "");
+ }
+
+ if (debug_level >= 4)
+ {
+ /* print out inquiry */
+ DBG (4, "attach: inquiry output:\n");
+ inquiry_byte_list[0] = '\0';
+ inquiry_text_list[0] = '\0';
+ for (pp = result; pp < (result + INQ_LEN); pp++)
+ {
+ sprintf ((SANE_String) inquiry_text, "%c",
+ (*pp < 127) && (*pp > 31) ? *pp : '.');
+ strcat ((SANE_String) inquiry_text_list,
+ (SANE_String) inquiry_text);
+ sprintf ((SANE_String) inquiry_byte, " %02x", *pp);
+ strcat ((SANE_String) inquiry_byte_list,
+ (SANE_String) inquiry_byte);
+ if ((pp - result) % 0x10 == 0x0f)
+ {
+ DBG (4, "%s %s\n", inquiry_byte_list, inquiry_text_list);
+ inquiry_byte_list[0] = '\0';
+ inquiry_text_list[0] = '\0';
+ }
+ }
+ }
+
+ /* first check for new firmware format: */
+ mustek_scanner = (strncmp ((SANE_String) result + 36, "MUSTEK", 6) == 0);
+ if (mustek_scanner)
+ {
+ if (result[43] == 'M')
+ {
+ DBG (3, "attach: found Mustek scanner (pro series firmware "
+ "format)\n");
+ firmware_format = 2;
+ model_name = result + 43;
+ }
+ else
+ {
+ DBG (3, "attach: found Mustek scanner (new firmware format)\n");
+ firmware_format = 1;
+ }
+ }
+ else
+ {
+ /* check for old format: */
+ mustek_scanner = (strncmp ((SANE_String) result + 8, "MUSTEK", 6) == 0);
+ if (mustek_scanner)
+ {
+ model_name = result + 16;
+ DBG (3, "attach: found Mustek scanner (old firmware format)\n");
+ firmware_format = 0;
+ }
+ else
+ {
+ /* Check for some non-Mustek scanners an print warning */
+ if (strncmp ((SANE_String) result + 8, "Trust", 5) == 0)
+ DBG (1, "attach: this is a real Trust scanner. It is not "
+ " supported by this backend.\n");
+ if (strncmp ((SANE_String) result + 8, "Aashima", 7) == 0)
+ DBG (1, "attach: this is an Aashima/Teco scanner. It is not "
+ " supported by this backend.\n");
+ if (strncmp ((SANE_String) result + 16, "Flatbed Scanner", 15) == 0
+ && strncmp ((SANE_String) result + 42, "TECO", 4) == 0)
+ DBG (1, "attach: this is a Relysis/Teco scanner. It is not "
+ " supported by this backend.\n");
+ DBG (1, "attach: device %s doesn't look like a Mustek scanner\n",
+ devname);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /* get firmware revision as BCD number: */
+ /* General format: x.yz */
+ /* Newer ScanExpress scanners (ID XC06): Vxyz */
+ if (result[33] == '.')
+ {
+ fw_revision =
+ (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] -
+ '0');
+ firmware_revision_system = 0;
+ DBG (4, "attach: old firmware revision system\n");
+ }
+ else
+ {
+ fw_revision =
+ (result[33] - '0') << 8 | (result[34] - '0') << 4 | (result[35] -
+ '0');
+ firmware_revision_system = 1;
+ DBG (4, "attach: new firmware revision system\n");
+ }
+ DBG (3, "attach: firmware revision %d.%02x\n",
+ fw_revision >> 8, fw_revision & 0xff);
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memcpy (dev, &new_dev, sizeof (*dev));
+
+ dev->name = strdup (devname);
+ if (!dev->name)
+ return SANE_STATUS_NO_MEM;
+ dev->sane.name = (SANE_String_Const) dev->name;
+ dev->sane.vendor = "Mustek";
+ dev->sane.type = "flatbed scanner";
+
+ dev->x_range.min = 0;
+ dev->y_range.min = 0;
+ dev->x_range.quant = 0;
+ dev->y_range.quant = 0;
+ dev->x_trans_range.min = 0;
+ dev->y_trans_range.min = 0;
+ /* default to something really small to be on the safe side: */
+ dev->x_trans_range.max = SANE_FIX (8.0 * MM_PER_INCH);
+ dev->y_trans_range.max = SANE_FIX (5.0 * MM_PER_INCH);
+ dev->x_trans_range.quant = 0;
+ dev->y_trans_range.quant = 0;
+ dev->dpi_range.min = SANE_FIX (72); /* some scanners don't like low dpi */
+ dev->dpi_range.quant = SANE_FIX (1);
+ /* default to 128 kB */
+ dev->max_buffer_size = 128 * 1024; /* SCSI buffer -> use 64 k per buffer */
+ dev->max_block_buffer_size = 1024 * 1024 * 1024;
+ dev->firmware_format = firmware_format;
+ dev->firmware_revision_system = firmware_revision_system;
+
+ DBG (3, "attach: scanner id: %.11s\n", model_name);
+ if (strncmp ((SANE_String) model_name + 10, "PRO", 3) == 0)
+ DBG (3, "attach: this is probably a Paragon Pro series scanner\n");
+ else if (strncmp ((SANE_String) model_name, "MFC", 3) == 0)
+ DBG (3, "attach: this is probably a Paragon series II scanner\n");
+ else if (strncmp ((SANE_String) model_name, "M", 1) == 0)
+ DBG (3,
+ "attach: this is probably a Paragon series I or 3-pass scanner\n");
+ else if (strncmp ((SANE_String) model_name, " C", 2) == 0)
+ DBG (3, "attach: this is probably a ScanExpress series A4 scanner\n");
+ else if (strncmp ((SANE_String) model_name, " L", 2) == 0)
+ DBG (3, "attach: this is probably a ScanExpress series A3 scanner\n");
+ else if (strncmp ((SANE_String) model_name, "XC", 2) == 0)
+ DBG (3,
+ "attach: this is probably a ScanExpress Plus series A4 scanner\n");
+ else
+ DBG (3, "attach: I am not sure what type of scanner this is\n");
+
+ /* Paragon 3-pass series */
+ if (strncmp ((SANE_String) model_name, "MFS-12000CX", 11) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon MFS-12000CX v4.00 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.max = SANE_FIX (14.00 * MM_PER_INCH);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (1.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->sane.model = "MFS-12000CX";
+ }
+ /* There are two different versions of the MFS-6000CX, one has the model
+ name "MFS-06000CX", the other one is "MSF-06000CZ" */
+ else if (strncmp ((SANE_String) model_name, "MFS-06000CX", 11) == 0)
+ {
+ /* These values were measured and tested with a Paragon MFS-6000CX
+ v4.06 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.max = SANE_FIX (13.86 * MM_PER_INCH);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (2.0);
+ dev->x_trans_range.max = SANE_FIX (203.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+
+ dev->dpi_range.max = SANE_FIX (600);
+ dev->sane.model = "MFS-6000CX";
+ }
+ else if (strncmp ((SANE_String) model_name, "MSF-06000CZ", 11) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon MFS-6000CX v4.00 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (2.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+
+ dev->dpi_range.max = SANE_FIX (600);
+ dev->sane.model = "MFS-6000CX";
+ }
+
+ /* Paragon 1-pass 14" series I */
+
+ /* I haven't seen a single report for this, but it is mentioned in
+ the old man page. All reported Paragon 1200SP had a model name
+ "MFS-12000SP" */
+ else if (strncmp ((SANE_String) model_name, "MSF-12000SP", 11) == 0)
+ {
+ /* These values are not tested and mostly guessed. */
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (1.0);
+ dev->x_trans_range.max = SANE_FIX (200.0);
+ dev->y_trans_range.max = SANE_FIX (250.0);
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->flags |= MUSTEK_FLAG_LD_NONE;
+ dev->flags |= MUSTEK_FLAG_PARAGON_1;
+ dev->flags |= MUSTEK_FLAG_USE_BLOCK;
+ dev->sane.model = "MFS-12000SP";
+ warning = SANE_TRUE;
+ }
+ /* MFS-8000 SP v 1.x */
+ else if (strncmp ((SANE_String) model_name, "MSF-08000SP", 11) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon MFS-8000SP v1.20 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (355.6);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (1.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+
+ dev->dpi_range.max = SANE_FIX (800);
+ /* At least scanners with firmware 1.20 need a gamma table upload
+ in color mode, otherwise the image is red */
+ if (fw_revision == 0x120)
+ dev->flags |= MUSTEK_FLAG_FORCE_GAMMA;
+ dev->flags |= MUSTEK_FLAG_PARAGON_1;
+ dev->sane.model = "MFS-8000SP";
+ }
+ /* This model name exists */
+ else if (strncmp ((SANE_String) model_name, "MSF-06000SP", 11) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon MFS-6000SP v3.12 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.max = SANE_FIX (355.6);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (1.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+ dev->dpi_range.max = SANE_FIX (600);
+ /* Looks like at least some versions of this scanner produce black images
+ in gray and color mode if MUSTEK_FORCE_GAMMA is not set. At least
+ versions 2.01, 2.02 and 2.10 are reported as having this bug. 3.12
+ doesn't need this workaround but it doesn't harm either. */
+ dev->flags |= MUSTEK_FLAG_FORCE_GAMMA;
+ dev->flags |= MUSTEK_FLAG_PARAGON_1;
+ dev->sane.model = "MFS-6000SP";
+ }
+
+ /* This one was reported multiple times */
+ else if (strncmp ((SANE_String) model_name, "MFS-12000SP", 11) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon MFS-12000SP v1.02 and v1.00 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (217.0);
+ dev->y_range.min = SANE_FIX (2.0);
+ dev->y_range.max = SANE_FIX (352.0);
+ dev->x_trans_range.min = SANE_FIX (0.0);
+ dev->y_trans_range.min = SANE_FIX (0.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (250.0);
+
+ dev->dpi_range.max = SANE_FIX (1200);
+ /* Earlier versions of this source code used MUSTEK_FLAG_LD_MFS
+ for firmware versions < 1.02 and LD_NONE for the rest. This
+ didn't work for my scanners. 1.00 doesn't need any LD
+ correction, 1.02, 1.07 and 1.11 do need normal LD
+ corrections. Maybe all != 1.00 need normal LD */
+ dev->flags |= MUSTEK_FLAG_PARAGON_1;
+ dev->flags |= MUSTEK_FLAG_LD_BLOCK;
+ dev->flags |= MUSTEK_FLAG_USE_BLOCK;
+ dev->sane.model = "MFS-12000SP";
+ }
+ /* MFS-8000 SP v2.x */
+ else if (strncmp ((SANE_String) model_name, "MFS-08000SP", 11) == 0)
+ {
+ /* These values are tested with a MFS-08000SP v 2.04 */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (355.6);
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (1.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+
+ dev->dpi_range.max = SANE_FIX (800);
+ /* At least scanners with firmware 1.20 need a gamma table upload
+ in color mode, otherwise the image is red */
+ if (fw_revision == 0x120)
+ dev->flags |= MUSTEK_FLAG_FORCE_GAMMA;
+ dev->flags |= MUSTEK_FLAG_PARAGON_1;
+ dev->sane.model = "MFS-8000SP";
+ }
+ /* I have never seen one of those */
+ else if (strncmp ((SANE_String) model_name, "MFS-06000SP", 11) == 0)
+ {
+ /* These values are not tested. */
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.max = SANE_FIX (13.84 * MM_PER_INCH);
+ /* copied from MSF-06000SP */
+ dev->x_trans_range.min = SANE_FIX (1.0);
+ dev->y_trans_range.min = SANE_FIX (1.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (255.0);
+ dev->dpi_range.max = SANE_FIX (600);
+ dev->flags |= MUSTEK_FLAG_PARAGON_1;
+ dev->sane.model = "MFS-6000SP";
+ warning = SANE_TRUE;
+ }
+
+ /* Paragon 1-pass A4 series II */
+ else if (strncmp ((SANE_String) model_name, "MFC-08000CZ", 11) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon 800 II SP v1.06. */
+ dev->x_range.min = SANE_FIX (1.5);
+ dev->x_range.max = SANE_FIX (218.0);
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.max = SANE_FIX (293.0);
+ dev->x_trans_range.min = SANE_FIX (0.0);
+ dev->y_trans_range.min = SANE_FIX (0.0);
+ dev->x_trans_range.max = SANE_FIX (205.0);
+ dev->y_trans_range.max = SANE_FIX (254.0);
+
+ dev->dpi_range.max = SANE_FIX (800);
+ dev->max_block_buffer_size = 2 * 1024 * 1024;
+
+ dev->flags |= MUSTEK_FLAG_PARAGON_2;
+ dev->flags |= MUSTEK_FLAG_LD_BLOCK;
+ dev->flags |= MUSTEK_FLAG_USE_BLOCK;
+ dev->sane.model = "800S/800 II SP";
+ }
+ else if (strncmp ((SANE_String) model_name, "MFC-06000CZ", 11) == 0)
+ {
+ /* These values were measured and compared to those from the
+ Windows driver. Tested with a Paragon 600 II CD, a Paragon
+ MFC-600S and a Paragon 600 II N. */
+ dev->x_range.min = SANE_FIX (0.0);
+ dev->x_range.max = SANE_FIX (218.0);
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.max = SANE_FIX (293.0);
+ dev->x_trans_range.min = SANE_FIX (0.0);
+ dev->y_trans_range.min = SANE_FIX (0.0);
+ dev->x_trans_range.max = SANE_FIX (201.0);
+ dev->y_trans_range.max = SANE_FIX (257.0);
+
+ dev->dpi_range.max = SANE_FIX (600);
+ /* This model comes in a non-scsi version, too. It is supplied
+ with its own parallel-port like adapter, an AB306N. Two
+ firmware revisions are known: 1.01 and 2.00. Each needs its
+ own line-distance correction code. */
+ if (dev->flags & MUSTEK_FLAG_N)
+ {
+ if (fw_revision < 0x200)
+ dev->flags |= MUSTEK_FLAG_LD_N1;
+ else
+ dev->flags |= MUSTEK_FLAG_LD_N2;
+ dev->x_trans_range.min = SANE_FIX (33.0);
+ dev->y_trans_range.min = SANE_FIX (62.0);
+ dev->x_trans_range.max = SANE_FIX (183.0);
+ dev->y_trans_range.max = SANE_FIX (238.0);
+ dev->max_block_buffer_size = 1024 * 1024 * 1024;
+ dev->sane.model = "600 II N";
+ }
+ else if (dev->flags & MUSTEK_FLAG_SCSI_PP)
+ {
+ /* FIXME; experiment with different line distance codes later */
+ dev->dpi_range.min = SANE_FIX (75.0);
+ dev->flags |= MUSTEK_FLAG_LD_NONE;
+ dev->max_block_buffer_size = 2 * 1024 * 1024;
+ dev->sane.model = "600 II EP";
+ }
+ else
+ {
+ dev->sane.model = "600S/600 II CD";
+ dev->flags |= MUSTEK_FLAG_PARAGON_2;
+ dev->flags |= MUSTEK_FLAG_LD_BLOCK;
+ dev->flags |= MUSTEK_FLAG_USE_BLOCK;
+ dev->max_block_buffer_size = 2 * 1024 * 1024;
+ }
+ }
+
+ /* ScanExpress and ScanMagic series */
+ else if (strncmp ((SANE_String) model_name, " C03", 4) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a ScannExpress 6000SP 1.00 */
+ dev->x_range.max = SANE_FIX (215);
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (293);
+
+ dev->x_trans_range.min = SANE_FIX (0);
+ dev->y_trans_range.min = SANE_FIX (0);
+ dev->x_trans_range.max = SANE_FIX (150.0);
+ dev->y_trans_range.max = SANE_FIX (175.0);
+
+ dev->dpi_range.max = SANE_FIX (600);
+ dev->dpi_range.min = SANE_FIX (60);
+ dev->flags |= MUSTEK_FLAG_SE;
+ /* At least the SE 6000SP with firmware 1.00 limits its
+ x-resolution to 300 dpi and does *no* interpolation at higher
+ resolutions. So this has to be done in software. */
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ dev->sane.model = "ScanExpress 6000SP";
+ }
+ /* There are two different versions of the ScanExpress 12000SP, one
+ has the model name " C06", the other one is "XC06". The latter
+ seems to be used in the newer "Plus" models.
+ Also there is the Mustek ScanExpress 1200 FS, which looks similar to the
+ ScanExpress 12000 SP but has an "F" instead of the "V" in the
+ firmware version.
+ */
+ else if (strncmp ((SANE_String) model_name, " C06", 4) == 0)
+ {
+ if (result[32] == 'F')
+ {
+ /* Mustek ScanExpress 1200 FS. Completely untested. */
+ dev->x_range.min = SANE_FIX (0);
+ dev->y_range.min = SANE_FIX (0);
+ dev->x_range.max = SANE_FIX (215.9);
+ dev->y_range.max = SANE_FIX (291.2);
+
+ dev->x_trans_range.min = SANE_FIX (0);
+ dev->y_trans_range.min = SANE_FIX (0);
+ dev->x_trans_range.max = SANE_FIX (150.0);
+ dev->y_trans_range.max = SANE_FIX (175.0);
+
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->dpi_range.min = SANE_FIX (60);
+ dev->flags |= MUSTEK_FLAG_SE;
+ /* The ScanExpress models limit their x-resolution to 600 dpi
+ and do *no* interpolation at higher resolutions. So this has
+ to be done in software. */
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
+ dev->sane.model = "ScanExpress 12000 FS (untested)";
+ }
+ else
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a ScaneExpress 12000SP 2.02 and a ScanMagic
+ 9636S v 1.01 */
+ dev->x_range.min = SANE_FIX (0);
+ dev->y_range.min = SANE_FIX (0);
+ dev->x_range.max = SANE_FIX (215.9);
+ dev->y_range.max = SANE_FIX (291.2);
+
+ dev->x_trans_range.min = SANE_FIX (0);
+ dev->y_trans_range.min = SANE_FIX (0);
+ dev->x_trans_range.max = SANE_FIX (150.0);
+ dev->y_trans_range.max = SANE_FIX (175.0);
+
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->dpi_range.min = SANE_FIX (60);
+ dev->flags |= MUSTEK_FLAG_SE;
+ /* The ScanExpress models limit their x-resolution to 600 dpi
+ and do *no* interpolation at higher resolutions. So this has
+ to be done in software. */
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
+ dev->sane.model = "ScanExpress 12000SP";
+ }
+ }
+ else if (strncmp ((SANE_String) model_name, "XC06", 4) == 0)
+ {
+ /* These values are tested with a SE 12000 SP Plus v 1.01 */
+ dev->x_range.max = SANE_FIX (216);
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (294.5);
+
+ dev->x_trans_range.min = SANE_FIX (0);
+ dev->y_trans_range.min = SANE_FIX (0);
+ dev->x_trans_range.max = SANE_FIX (152.0);
+ dev->y_trans_range.max = SANE_FIX (177.0);
+
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->dpi_range.min = SANE_FIX (60);
+
+ dev->flags |= MUSTEK_FLAG_SE;
+ dev->flags |= MUSTEK_FLAG_SE_PLUS;
+ /* The ScanExpress models limit their x-resolution to 600 dpi
+ and do *no* interpolation at higher resolutions. So this has
+ to be done in software. */
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
+ dev->sane.model = "ScanExpress 12000SP Plus";
+ }
+ /* ScanExpress A3 SP */
+ else if (strncmp ((SANE_String) model_name, " L03", 4) == 0)
+ {
+ /* These values were measured with a ScannExpress A3 SP 2.00 */
+ dev->x_range.max = SANE_FIX (297);
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (430);
+
+ /* TA couldn't be tested due to lack of equipment. So At least
+ the TA IV (A4 size) is supported */
+ dev->x_trans_range.min = SANE_FIX (0);
+ dev->y_trans_range.min = SANE_FIX (0);
+ dev->x_trans_range.max = SANE_FIX (150.0);
+ dev->y_trans_range.max = SANE_FIX (175.0);
+
+ dev->dpi_range.max = SANE_FIX (600);
+ dev->dpi_range.min = SANE_FIX (60);
+ dev->flags |= MUSTEK_FLAG_SE;
+ /* The ScanExpress models limit their x-resolution to 300 dpi
+ and do *no* interpolation at higher resolutions. So this has
+ to be done in software. */
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
+ dev->sane.model = "ScanExpress A3 SP";
+ }
+ /* Paragon 1200 SP Pro */
+ else if (strncmp ((SANE_String) model_name, "MFS-1200SPPRO", 13) == 0)
+ {
+ /* These values were measured with a Paragon 1200 SP Pro v2.01 */
+ dev->x_range.max = SANE_FIX (8.6 * MM_PER_INCH);
+ dev->y_range.max = SANE_FIX (13.70 * MM_PER_INCH);
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->sane.model = "1200 SP PRO";
+ dev->flags |= MUSTEK_FLAG_LD_NONE;
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ }
+ /* No documentation, but it works: Paragon 1200 A3 PRO */
+ else if (strncmp ((SANE_String) model_name, "MFS-1200A3PRO", 13) == 0)
+ {
+ /* These values were measured and compared to those from the Windows
+ driver. Tested with a Paragon 1200 A3 Pro v1.10 */
+ dev->x_range.max = SANE_FIX (11.7 * MM_PER_INCH);
+ dev->y_range.max = SANE_FIX (424);
+ dev->dpi_range.max = SANE_FIX (1200);
+ dev->sane.model = "1200 A3 PRO";
+ dev->flags |= MUSTEK_FLAG_LD_NONE;
+ dev->flags |= MUSTEK_FLAG_ENLARGE_X;
+ }
+ else
+ {
+ DBG (0, "attach: this Mustek scanner (ID: %s) is not supported yet\n",
+ model_name);
+ DBG (0, "attach: please set the debug level to 5 and send a debug "
+ "report\n");
+ DBG (0, "attach: to henning@meier-geinitz.de (export "
+ "SANE_DEBUG_MUSTEK=5\n");
+ DBG (0, "attach: scanimage -L 2>debug.txt). Thank you.\n");
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->flags & MUSTEK_FLAG_SE)
+ {
+ DBG (3, "attach: this is a single-pass scanner\n");
+ if (result[63] & (1 << 6))
+ {
+ dev->flags |= MUSTEK_FLAG_TA;
+ DBG (3, "attach: scanner supports transparency adapter (TA)\n");
+ }
+ }
+ else
+ {
+ if (result[57] & (1 << 6))
+ {
+ DBG (3, "attach: this is a single-pass scanner\n");
+ if (dev->flags & MUSTEK_FLAG_LD_NONE)
+ DBG (4,
+ "attach: scanner doesn't need line-distance correction\n");
+ else if (dev->flags & MUSTEK_FLAG_LD_N1)
+ DBG (4, "attach: scanner has N1 line-distance correction\n");
+ else if (dev->flags & MUSTEK_FLAG_LD_N2)
+ DBG (4, "attach: scanner has N2 line-distance correction\n");
+ else if (dev->flags & MUSTEK_FLAG_LD_BLOCK)
+ DBG (4, "attach: scanner has block line-distance correction\n");
+ else
+ DBG (4, "attach: scanner has normal line-distance correction\n");
+ }
+ else
+ {
+ dev->flags |= MUSTEK_FLAG_THREE_PASS;
+ /* three-pass scanners quantize to 0.5% of the maximum resolution: */
+ dev->dpi_range.quant = dev->dpi_range.max / 200;
+ dev->dpi_range.min = dev->dpi_range.quant;
+ DBG (3, "attach: this is a three-pass scanner\n");
+ }
+ if (result[57] & (1 << 5))
+ {
+ DBG (3, "attach: this is a professional series scanner\n");
+ dev->flags |= MUSTEK_FLAG_PRO;
+ status = dev_open (devname, &s, sense_handler);
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (ta_available_pro (&s))
+ {
+ dev->flags |= MUSTEK_FLAG_TA;
+ DBG (3, "attach: found transparency adapter (TA)\n");
+ }
+ dev_close (&s);
+ }
+ else
+ {
+ DBG (1, "attach: couldn't open device: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ if (result[63] & (1 << 2))
+ {
+ dev->flags |= MUSTEK_FLAG_ADF;
+ DBG (3, "attach: found automatic document feeder (ADF)\n");
+ if (result[63] & (1 << 3))
+ {
+ dev->flags |= MUSTEK_FLAG_ADF_READY;
+ DBG (4, "attach: automatic document feeder is ready\n");
+ }
+ else
+ {
+ DBG (4, "attach: automatic document feeder is out of "
+ "documents\n");
+ }
+ }
+
+ if (result[63] & (1 << 6))
+ {
+ dev->flags |= MUSTEK_FLAG_TA;
+ DBG (3, "attach: found transparency adapter (TA)\n");
+ }
+ }
+
+ if (dev->flags & MUSTEK_FLAG_COVER_SENSOR)
+ {
+ if (result[62] & (1 << 0))
+ DBG (4, "attach: scanner cover is closed\n");
+ else
+ DBG (4, "attach: scanner cover is open\n");
+ }
+
+ if (warning == SANE_TRUE)
+ {
+ DBG (0,
+ "WARNING: Your scanner was detected by the SANE Mustek backend, "
+ "but\n it is not fully tested. It may or may not work. Be "
+ "carefull and read\n the PROBLEMS file in the sane directory. "
+ "Please set the debug level of this\n backend to maximum "
+ "(export SANE_DEBUG_MUSTEK=255) and send the output of\n "
+ "scanimage -L to the SANE mailing list sane-devel@lists.alioth.debian.org. "
+ "Please include\n the exact model name of your scanner and to "
+ "which extend it works.\n");
+ }
+
+ DBG (2, "attach: found Mustek %s %s, %s%s%s%s\n",
+ dev->sane.model, dev->sane.type,
+ (dev->flags & MUSTEK_FLAG_THREE_PASS) ? "3-pass" : "1-pass",
+ (dev->flags & MUSTEK_FLAG_ADF) ? ", ADF" : "",
+ (dev->flags & MUSTEK_FLAG_TA) ? ", TA" : "",
+ (dev->flags & MUSTEK_FLAG_SE) ? ", SE" : "");
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+constrain_value (Mustek_Scanner * s, SANE_Int option, void *value,
+ SANE_Int * info)
+{
+ SANE_Fixed w, dpi;
+ SANE_Status status;
+
+ if (value)
+ w = *(SANE_Fixed *) value;
+ else
+ w = 0;
+
+ if (option == OPT_RESOLUTION)
+ {
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ /* The three pass scanners use a 0.5% of the maximum resolution
+ increment for resolutions less than or equal to half of the
+ maximum resolution. The MFS-06000CX uses a 5% of the maximum
+ resolution increment for larger resolutions. The models
+ MFS-12000CX and MSF-06000CZ use 1% of the maximum resolution.
+ We can't represent this easily in SANE, so the constraint is
+ simply for 0.5% and then we round to the 5% or 1% increments
+ if necessary. */
+ SANE_Fixed max_dpi, quant, half_res;
+
+ /*w = *(SANE_Word *) value; */
+ max_dpi = s->hw->dpi_range.max;
+ half_res = max_dpi / 2;
+
+ if (w > half_res)
+ {
+ /* quantizize to 1% step */
+ quant = max_dpi / 100;
+
+ dpi = (w + quant / 2) / quant;
+ dpi *= quant;
+ if (dpi != w)
+ {
+ *(SANE_Word *) value = dpi;
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+
+ }
+ }
+
+ status = sanei_constrain_value (s->opt + option, value, info);
+ if (s->opt[option].type == SANE_TYPE_FIXED)
+ DBG (5, "constrain_value: %s = %.2f (was %.2f)\n", s->opt[option].name,
+ SANE_UNFIX (*(SANE_Word *) value), SANE_UNFIX (w));
+ return status;
+}
+
+/* Quantize s->val[OPT_RESOLUTION].w and return the resolution code for the
+ quantized resolution. Quantization depends on scanner type (single
+ pass vs. three-pass) and resolution */
+static SANE_Int
+encode_resolution (Mustek_Scanner * s)
+{
+ SANE_Fixed max_dpi, dpi;
+ SANE_Int code, mode = 0;
+
+ dpi = s->val[OPT_RESOLUTION].w;
+
+ if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ code = dpi >> SANE_FIXED_SCALE_SHIFT;
+ }
+ else
+ {
+ SANE_Fixed quant, half_res;
+
+ max_dpi = s->hw->dpi_range.max;
+ half_res = max_dpi / 2;
+
+ if (dpi <= half_res)
+ {
+ /* quantizize to 0.5% step */
+ quant = max_dpi / 200;
+ }
+ else
+ {
+ /* quantizize to 1% step */
+ quant = max_dpi / 100;
+ mode = 0x100; /* indicate 5% or 1% quantization */
+ }
+
+ code = (dpi + quant / 2) / quant;
+ if (code < 1)
+ code = 1;
+
+ }
+ DBG (5, "encode_resolution: code = 0x%x (%d); mode = %x\n", code, code,
+ mode);
+ return code | mode;
+}
+
+static SANE_Int
+encode_percentage (Mustek_Scanner * s, double value)
+{
+ SANE_Int max, code, sign = 0;
+
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ code = (int) ((value / 100.0 * 12) + 12.5);
+ max = 0x18;
+ }
+ else
+ {
+ if (value < 0.0)
+ {
+ value = -value;
+ sign = 0x80;
+ }
+ code = (int) (value / 100.0 * 127 + 0.5);
+ code |= sign;
+ max = 0xff;
+ }
+ if (code > max)
+ code = max;
+ if (code < 0)
+ code = 0x00;
+ return code;
+}
+
+/* encode halftone pattern type and size */
+static SANE_Status
+encode_halftone (Mustek_Scanner * s)
+{
+ SANE_String selection = s->val[OPT_HALFTONE_DIMENSION].s;
+ SANE_Int i = 0;
+
+ while ((halftone_list[i] != 0) && (strcmp (selection, halftone_list[i]) != 0))
+ {
+ i++;
+ }
+ if (halftone_list[i] == 0)
+ return SANE_STATUS_INVAL;
+
+ if (i < 0x0c) /* standard pattern */
+ {
+ s->custom_halftone_pattern = SANE_FALSE;
+ s->halftone_pattern_type = i;
+ }
+ else /* custom pattern */
+ {
+ s->custom_halftone_pattern = SANE_TRUE;
+ i -= 0x0c;
+ i = 8 - i;
+ if (i < 8)
+ i--;
+ i = i + (i << 4);
+ s->halftone_pattern_type = i;
+ }
+
+ DBG (5, "encode_halftone: %s pattern type %x\n",
+ s->custom_halftone_pattern ? "custom" : "standard",
+ s->halftone_pattern_type);
+ return SANE_STATUS_GOOD;
+}
+
+/* Paragon series */
+static SANE_Status
+area_and_windows (Mustek_Scanner * s)
+{
+ SANE_Byte cmd[117], *cp;
+ SANE_Int i, offset;
+
+ /* setup SCSI command (except length): */
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = MUSTEK_SCSI_AREA_AND_WINDOWS;
+
+ cp = cmd + 6;
+
+ /* Some scanners need a larger scanarea for line-distance correction */
+ offset = 0;
+ if (((s->hw->flags & MUSTEK_FLAG_LD_N1)
+ || ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK)
+ && (s->hw->flags & MUSTEK_FLAG_PARAGON_1)))
+ && (s->mode & MUSTEK_MODE_COLOR))
+ offset = MAX_LINE_DIST;
+
+ /* fill in frame header: */
+
+ if (s->hw->flags & MUSTEK_FLAG_USE_EIGHTS)
+ {
+ double eights_per_mm = 8 / MM_PER_INCH;
+ SANE_Int tlx, tly, brx, bry;
+ /*
+ * The MSF-06000CZ seems to lock-up if the pixel-unit is used.
+ * Using 1/8" works.
+ * This doesn't seem to be true with the current scheme.
+ * This code isn't used at the moment. <henning@meier-geinitz.de>
+ */
+ *cp++ = ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01);
+
+ tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * eights_per_mm + 0.5;
+ tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * eights_per_mm + 0.5;
+ brx = SANE_UNFIX (s->val[OPT_BR_X].w) * eights_per_mm + 0.5;
+ bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * eights_per_mm + 0.5;
+ STORE16L (cp, tlx);
+ STORE16L (cp, tly);
+ STORE16L (cp, brx);
+ STORE16L (cp, bry);
+ DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); "
+ "brx=%d (%d mm); bry=%d (%d mm)\n", tlx,
+ (int) (tlx / eights_per_mm), tly, (int) (tly / eights_per_mm), brx,
+ (int) (brx / eights_per_mm), bry, (int) (bry / eights_per_mm));
+ }
+ else
+ {
+ double pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH;
+ SANE_Int tlx, tly, brx, bry;
+
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ /* 3pass scanners use 1/2 of the max resolution as base */
+ pixels_per_mm /= 2;
+
+ /* pixel unit and halftoning: */
+ *cp++ = 0x8 | ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01);
+
+ /* fill in scanning area: */
+ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
+ {
+ /* must mirror the x coordinates */
+ brx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_TL_X].w)
+ * pixels_per_mm + 0.5;
+ tlx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_BR_X].w)
+ * pixels_per_mm + 0.5;
+ }
+ else
+ {
+ tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5;
+ brx = SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5;
+
+ }
+ tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5;
+ bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5 + offset;
+ STORE16L (cp, tlx);
+ STORE16L (cp, tly);
+ STORE16L (cp, brx);
+ STORE16L (cp, bry);
+ DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); "
+ "brx=%d (%d mm); bry=%d (%d mm)\n", tlx,
+ (int) (tlx / pixels_per_mm), tly, (int) (tly / pixels_per_mm), brx,
+ (int) (brx / pixels_per_mm), bry, (int) (bry / pixels_per_mm));
+ }
+
+ if (s->custom_halftone_pattern)
+ {
+ *cp++ = 0x40; /* mark presence of user pattern */
+ *cp++ = s->halftone_pattern_type; /* set pattern length */
+ for (i = 0; i < (s->halftone_pattern_type & 0x0f) *
+ ((s->halftone_pattern_type >> 4) & 0x0f); ++i)
+ *cp++ = s->val[OPT_HALFTONE_PATTERN].wa[i];
+ }
+
+ cmd[4] = (cp - cmd) - 6;
+
+ return dev_cmd (s, cmd, (cp - cmd), 0, 0);
+}
+
+/* ScanExpress */
+static SANE_Status
+set_window_se (Mustek_Scanner * s, SANE_Int lamp)
+{
+ SANE_Byte cmd[58], *cp;
+ double pixels_per_mm;
+ SANE_Int offset;
+ SANE_Int tlx, tly, width, height;
+
+ /* setup SCSI command (except length): */
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = MUSTEK_SCSI_SET_WINDOW;
+ cp = cmd + sizeof (scsi_set_window); /* skip command block */
+
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ /* We have to increase the specified resolution to the next */
+ /* "standard" resolution due to a firmware bug(?) in color mode */
+ /* It's possible to scan in 36, 75, 100, 150, 200, 250, 300, */
+ /* 400, 500, 600, 900, 1200 dpi but the speed is only different */
+ /* with 36, 150, 300, 600, 1200 dpi. */
+ /* Additionally we must increase the window length slightly to */
+ /* compensate for different line counts for r/g/b */
+ const SANE_Int resolution_list[] = { 36, 150, 300, 600, 1200, 0 };
+ SANE_Int entry = 0;
+
+ while (resolution_list[entry] < s->resolution_code)
+ entry++;
+ s->ld.peak_res = resolution_list[entry];
+
+ offset = MAX_LINE_DIST; /* distance r/b lines */
+ }
+ else
+ {
+ /* In gray and lineart modes all resolutions are possible */
+ s->ld.peak_res = s->resolution_code;
+ offset = 0;
+ }
+ DBG (5, "set_window_se: hardware resolution is %d dpi; offset is %d\n",
+ s->ld.peak_res, offset);
+
+ STORE16B (cp, 0); /* window identifier */
+ STORE16B (cp, s->ld.peak_res);
+ /* x and y resolution */
+ STORE16B (cp, 0); /* not used acc. to specs */
+
+ pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH;
+
+ /* fill in scanning area, begin and length(!) */
+ if ((strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) &&
+ !(s->hw->flags & MUSTEK_FLAG_TA))
+ {
+ /* need to add the start values of the transparency adapter */
+ tlx = (SANE_UNFIX (s->val[OPT_TL_X].w) + 33.0) * pixels_per_mm + 0.5;
+ tly = (SANE_UNFIX (s->val[OPT_TL_Y].w) + 60.0) * pixels_per_mm + 0.5;
+ DBG (5, "set_window_se: added offset for transparency adapter\n");
+ }
+ else
+ {
+ /* no transparency adapter selected or calculation done in firmware */
+ tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5;
+ tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5;
+ }
+ width = (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w))
+ * pixels_per_mm + 0.5;
+ height = (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w))
+ * pixels_per_mm + 0.5 + offset;
+
+ DBG (5, "set_window_se: tlx=%d (%d mm); tly=%d (%d mm); width=%d (%d mm); "
+ "height=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly,
+ (int) (tly / pixels_per_mm), width, (int) (width / pixels_per_mm),
+ height, (int) (height / pixels_per_mm));
+
+
+ STORE32B (cp, tlx);
+ STORE32B (cp, tly);
+ STORE32B (cp, width);
+ STORE32B (cp, height);
+
+ *cp++ = 0x00; /* brightness, not impl. */
+ *cp++ = 0x80; /* threshold, not impl. */
+ *cp++ = 0x00; /* contrast, not impl. */
+
+ /* Note that 'image composition' has no meaning for the SE series */
+ /* Mode selection is accomplished solely by bits/pixel (1, 8, 24) */
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ *cp++ = 0x05; /* actually not used! */
+ *cp++ = 24; /* 24 bits/pixel in color mode */
+ }
+ else if (s->mode & MUSTEK_MODE_GRAY)
+ {
+ *cp++ = 0x02; /* actually not used! */
+ *cp++ = 8; /* 8 bits/pixel in gray mode */
+ }
+ else
+ {
+ *cp++ = 0x00; /* actually not used! */
+ *cp++ = 1; /* 1 bit/pixel in lineart mode */
+ }
+
+ cp += 14; /* skip reserved bytes */
+ *cp++ = lamp; /* 0 = normal, 1 = on, 2 = off */
+
+ if ((s->hw->flags & MUSTEK_FLAG_TA)
+ && (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0))
+ *cp++ = 1;
+ else
+ *cp++ = 0;
+ cp += 5; /* skip reserved bytes */
+
+ cmd[8] = cp - cmd - sizeof (scsi_set_window);
+ return dev_cmd (s, cmd, (cp - cmd), 0, 0);
+}
+
+/* Pro series */
+static SANE_Status
+set_window_pro (Mustek_Scanner * s)
+{
+ SANE_Byte cmd[20], *cp;
+ double pixels_per_mm;
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = MUSTEK_SCSI_SET_WINDOW;
+ if (strcmp (s->hw->sane.model, "1200 SP PRO") == 0)
+ cmd[8] = 0x09;
+ else
+ cmd[8] = 0x0a;
+
+ cp = cmd + sizeof (scsi_set_window); /* skip command block */
+
+ *cp++ = 0; /* what's this? */
+ pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH;
+
+ /* The next for 16 bit values are x0, y0, x1, y1 in pixels at max res */
+ STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5);
+ STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5);
+ STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5);
+ STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5);
+
+ if (strcmp (s->hw->sane.model, "1200 SP PRO") != 0)
+ *cp++ = lamp_off_time; /* Only needed for A3 Pro, default: 60 minutes until lamp-off */
+ DBG (5, "set_window_pro\n");
+
+ return dev_cmd (s, cmd, (cp - cmd), 0, 0);
+}
+
+/* Pro series calibration */
+static SANE_Status
+get_calibration_size_pro (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Byte cmd[6];
+ SANE_Byte result[6];
+ size_t len;
+
+ memset (cmd, 0, sizeof (cmd));
+ memset (result, 0, sizeof (result));
+ cmd[0] = MUSTEK_SCSI_GET_IMAGE_STATUS;
+ cmd[4] = 0x06; /* size of result */
+ cmd[5] = 0x80; /* get back buffer size and number of buffers */
+ len = sizeof (result);
+ status = dev_cmd (s, cmd, sizeof (cmd), result, &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->hw->cal.bytes = result[1] | (result[2] << 8);
+ s->hw->cal.lines = result[3] | (result[4] << 8);
+
+ DBG (4, "get_calibration_size_pro: bytes=%d, lines=%d\n", s->hw->cal.bytes,
+ s->hw->cal.lines);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_calibration_lines_pro (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Byte cmd[10];
+ size_t len;
+ SANE_Int line;
+
+ DBG (2, "get_calibration_lines_pro: please wait for warmup\n");
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = MUSTEK_SCSI_READ_DATA;
+ len = s->hw->cal.bytes;
+ cmd[6] = (len >> 16) & 0xff;
+ cmd[7] = (len >> 8) & 0xff;
+ cmd[8] = (len >> 0) & 0xff;
+
+ for (line = 0; line < s->hw->cal.lines; line++)
+ {
+ status = dev_cmd (s, cmd, sizeof (scsi_read_data),
+ s->hw->cal.buffer + line * len, &len);
+
+ if ((status != SANE_STATUS_GOOD)
+ || (len != (unsigned int) s->hw->cal.bytes))
+ {
+ DBG (1, "get_calibration_lines_pro: read failed\n");
+ return status;
+ }
+ }
+ DBG (5, "get_calibration_lines_pro finished. Assuming 12 bits per color\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+send_calibration_lines_pro (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Byte *cmd1, *cmd2;
+ size_t buf_size;
+ SANE_Word column, line, color;
+
+ DBG (5, "send_calibration_lines_pro\n");
+
+ buf_size = s->hw->cal.bytes / 2;
+ cmd1 = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data));
+ cmd2 = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data));
+ if (!cmd1 || !cmd2)
+ {
+ DBG (1, "send_calibration_lines_pro: failed to malloc %ld bytes for "
+ "sending lines\n",
+ (long int) (buf_size + sizeof (scsi_send_data)));
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (cmd1, 0, sizeof (scsi_send_data));
+ memset (cmd2, 0, sizeof (scsi_send_data));
+
+ cmd1[0] = cmd2[0] = MUSTEK_SCSI_SEND_DATA;
+ cmd1[6] = cmd2[6] = (buf_size >> 16) & 0xff;
+ cmd1[7] = cmd2[7] = (buf_size >> 8) & 0xff;
+ cmd1[8] = cmd2[8] = (buf_size >> 0) & 0xff;
+ cmd1[9] = 0; /* Least significant 8 bits */
+ cmd2[9] = 0x80; /* Most significant 2 bits */
+
+ for (color = 0; color < 3; color++)
+ {
+ for (column = 0; column < s->hw->cal.bytes / 6; column++)
+ {
+ SANE_Word calibration_word = 0;
+ for (line = 0; line < s->hw->cal.lines; line++)
+ {
+ calibration_word +=
+ *(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 0)
+ +
+ (*(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 1)
+ << 8);
+ }
+ if (!calibration_word)
+ calibration_word = 1;
+ calibration_word = (1024 * 65536 / calibration_word) - 1024;
+ if (calibration_word > 1023)
+ calibration_word = 1023;
+ *(cmd1 + sizeof (scsi_send_data) + (buf_size / 3) * color + column)
+ = calibration_word & 0xff;
+ *(cmd2 + sizeof (scsi_send_data) + (buf_size / 3) * color + column)
+ = (calibration_word >> 8) & 0xff;
+ }
+ }
+
+ status = dev_cmd (s, cmd1, buf_size + sizeof (scsi_send_data), 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send_calibration_lines_pro: send failed\n");
+ return status;
+ }
+
+ status = dev_cmd (s, cmd2, buf_size + sizeof (scsi_send_data), 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send_calibration_lines_pro: send failed\n");
+ return status;
+ }
+ free (cmd1);
+ free (cmd2);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+calibration_pro (Mustek_Scanner * s)
+{
+ SANE_Status status;
+
+ if (s->val[OPT_QUALITY_CAL].w)
+ DBG (4, "calibration_pro: doing calibration\n");
+ else
+ {
+ DBG (4, "calibration_pro: calibration not necessary\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ status = get_calibration_size_pro (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes *
+ s->hw->cal.lines);
+ if (!s->hw->cal.buffer)
+ {
+ DBG (1, "calibration_pro: failed to malloc %d bytes for buffer\n",
+ s->hw->cal.bytes * s->hw->cal.lines);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = get_calibration_lines_pro (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_calibration_lines_pro (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ free (s->hw->cal.buffer);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ScanExpress series calibration */
+static SANE_Status
+get_calibration_lines_se (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Byte cmd[10];
+ size_t len;
+ SANE_Word lines, bytes_per_color;
+
+ if (s->mode == MUSTEK_MODE_COLOR)
+ {
+ lines = s->hw->cal.lines * 3;
+ bytes_per_color = s->hw->cal.bytes / 3;
+ }
+ else
+ {
+ lines = s->hw->cal.lines;
+ bytes_per_color = s->hw->cal.bytes;
+ }
+
+ DBG (4, "get_calibration_lines_se: reading %d lines (%d bytes per color)\n",
+ lines, bytes_per_color);
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = MUSTEK_SCSI_READ_DATA;
+ cmd[2] = 1;
+ cmd[7] = (lines >> 8) & 0xff;
+ cmd[8] = (lines >> 0) & 0xff;
+ len = lines * bytes_per_color;
+ status = dev_cmd (s, cmd, sizeof (scsi_read_data), s->hw->cal.buffer, &len);
+ if ((status != SANE_STATUS_GOOD)
+ || (len != (unsigned int) (lines * bytes_per_color)))
+ {
+ DBG (1, "get_calibration_lines_se: read failed\n");
+ return status;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+send_calibration_lines_se (Mustek_Scanner * s, SANE_Word color)
+{
+ SANE_Status status;
+ SANE_Byte *cmd;
+ size_t buf_size;
+ SANE_Word column;
+ SANE_Word lines, bytes_per_color;
+
+ if (s->mode == MUSTEK_MODE_COLOR)
+ {
+ lines = s->hw->cal.lines * 3;
+ bytes_per_color = s->hw->cal.bytes / 3;
+ }
+ else
+ {
+ lines = s->hw->cal.lines;
+ bytes_per_color = s->hw->cal.bytes;
+ }
+
+ buf_size = bytes_per_color;
+
+ DBG (5, "send_calibration_lines_se: %d bytes, color: %d\n",
+ bytes_per_color, color + 1);
+
+ cmd = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data));
+ if (!cmd)
+ {
+ DBG (1, "send_calibration_lines_se: failed to malloc %ld bytes for "
+ "sending lines\n",
+ (long int) (buf_size + sizeof (scsi_send_data)));
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (cmd, 0, sizeof (scsi_send_data));
+
+ for (column = 0; column < bytes_per_color; column++)
+ {
+ SANE_Word line;
+ SANE_Word cali_word = 0;
+ SANE_Int color_seq[] = { 2, 0, 1 };
+
+ for (line = 0; line < s->hw->cal.lines; line++)
+ cali_word += *(s->hw->cal.buffer
+ + line * bytes_per_color
+ + bytes_per_color * color_seq[color] + column);
+ if (!cali_word)
+ cali_word = 1;
+ cali_word = 256 * s->hw->cal.lines * 255 / cali_word - 256;
+ if (cali_word > 255)
+ cali_word = 255;
+ *(cmd + sizeof (scsi_send_data) + column) = cali_word;
+ }
+
+ cmd[0] = MUSTEK_SCSI_SEND_DATA;
+ cmd[2] = 1;
+ cmd[6] = color + 1;
+ cmd[7] = (buf_size >> 8) & 0xff;
+ cmd[8] = (buf_size >> 0) & 0xff;
+
+ status = dev_cmd (s, cmd, buf_size + sizeof (scsi_send_data), 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send_calibration_lines_se: send failed\n");
+ return status;
+ }
+ free (cmd);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+calibration_se (Mustek_Scanner * s)
+{
+ SANE_Status status;
+
+ if (!s->val[OPT_QUALITY_CAL].w || s->val[OPT_PREVIEW].w
+ || s->mode == MUSTEK_MODE_LINEART)
+ return SANE_STATUS_GOOD;
+
+ DBG (4, "calibration_se: doing calibration\n");
+
+ s->hw->cal.lines = MIN (s->hw->cal.lines,
+ s->hw->buffer_size / s->hw->cal.bytes);
+
+ s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes
+ * s->hw->cal.lines);
+ if (!s->hw->cal.buffer)
+ {
+ DBG (1, "calibration_se: failed to malloc %d bytes for buffer\n",
+ s->hw->cal.bytes * s->hw->cal.lines);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = get_calibration_lines_se (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (s->mode == MUSTEK_MODE_GRAY)
+ status = send_calibration_lines_se (s, 0);
+ else
+ {
+ status = send_calibration_lines_se (s, 0);
+ status = send_calibration_lines_se (s, 1);
+ status = send_calibration_lines_se (s, 2);
+ }
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ free (s->hw->cal.buffer);
+ return SANE_STATUS_GOOD;
+}
+
+/* ScanExpress series */
+static SANE_Status
+send_gamma_table_se (Mustek_Scanner * s)
+{
+ SANE_Status status;
+ SANE_Byte gamma[10 + 4096], *cp;
+ SANE_Int color, factor, val_a, val_b;
+ SANE_Int i, j;
+# define CLIP(x) ((x) < 0 ? 0 : ((x) > 255 ? 255 : (x)))
+
+ memset (gamma, 0, sizeof (scsi_send_data));
+
+ gamma[0] = MUSTEK_SCSI_SEND_DATA;
+ gamma[2] = 0x03; /* indicates gamma table */
+
+ if ((s->mode & MUSTEK_MODE_GRAY) || (s->mode & MUSTEK_MODE_COLOR))
+ {
+ if (s->hw->gamma_length + sizeof (scsi_send_data) > sizeof (gamma))
+ return SANE_STATUS_NO_MEM;
+ gamma[7] = (s->hw->gamma_length >> 8) & 0xff;
+ gamma[8] = (s->hw->gamma_length >> 0) & 0xff;
+
+ factor = s->hw->gamma_length / 256;
+ color = (s->mode & MUSTEK_MODE_COLOR) ? 1 : 0;
+
+ do
+ {
+ gamma[6] = color;
+
+ if (color == 0)
+ {
+ val_a = s->gamma_table[0][1];
+ val_b = s->gamma_table[0][0];
+ }
+ else
+ {
+ /* compose intensity gamma and color channel gamma: */
+ val_a = s->gamma_table[0][s->gamma_table[color][1]];
+ val_b = s->gamma_table[0][s->gamma_table[color][0]];
+ }
+ /* Now val_a is extrapolated from [0] and [1] */
+ val_a = MAX (2 * val_b - val_a, 0);
+
+ /* Interpolate first entries from 256 entry table */
+ cp = gamma + sizeof (scsi_send_data);
+ for (j = 0; j < factor; j++)
+ *cp++ = CLIP (((factor - j) * val_a + j * val_b
+ + factor / 2) / factor);
+
+ for (i = 1; i < 256; i++)
+ {
+ if (color == 0)
+ {
+ val_a = s->gamma_table[0][i - 1];
+ val_b = s->gamma_table[0][i];
+ }
+ else
+ {
+ /* compose intensity gamma and color channel gamma: */
+ val_a = s->gamma_table[0][s->gamma_table[color][i - 1]];
+ val_b = s->gamma_table[0][s->gamma_table[color][i]];
+ }
+
+ /* Interpolate next entries from the 256 entry table */
+ for (j = 0; j < factor; j++)
+ *cp++ = CLIP (((factor - j) * val_a + j * val_b
+ + factor / 2) / factor);
+ }
+
+ DBG (5, "send_gamma_table_se: sending table for color %d\n",
+ gamma[6]);
+ status = dev_cmd (s, gamma, sizeof (scsi_send_data)
+ + s->hw->gamma_length, 0, 0);
+ ++color;
+ }
+ while ((color != 1) & (color < 4) & (status == SANE_STATUS_GOOD));
+
+ return status;
+ }
+ else
+ {
+ /* In lineart mode the threshold is encoded in byte 8 as follows */
+ /* brightest -> 00 01 02 ... 7F 80 81 82 ... FF <- darkest image */
+ gamma[6] = 0x04;
+ gamma[8] = 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0;
+
+ DBG (5, "send_gamma_table_se: sending lineart threshold %2X\n",
+ gamma[8]);
+ return dev_cmd (s, gamma, sizeof (scsi_send_data), 0, 0);
+ }
+}
+
+/* Paragon series */
+static SANE_Status
+mode_select_paragon (Mustek_Scanner * s, SANE_Int color_code)
+{
+ SANE_Int speed_code;
+ SANE_Byte mode[19], *cp;
+
+ /* calculate funky speed code: */
+ for (speed_code = 0; speed_list[speed_code]; ++speed_code)
+ {
+ if (strcmp (speed_list[speed_code], s->val[OPT_SPEED].s) == 0)
+ break;
+ }
+ if (speed_code > 4)
+ speed_code = 4;
+ else if (speed_code < 0)
+ speed_code = 0;
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ speed_code = 5 - speed_code; /* 1 is fast, 5 is slow */
+ }
+ else
+ {
+ speed_code = 4 - speed_code; /* 0 is fast, 4 is slow */
+ }
+ memset (mode, 0, sizeof (mode));
+ mode[0] = MUSTEK_SCSI_MODE_SELECT;
+
+ /* set command length and resolution code: */
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ mode[4] = 0x0b;
+ mode[7] = s->resolution_code;
+ }
+ else
+ {
+ mode[4] = 0x0d;
+ cp = mode + 17;
+ STORE16L (cp, s->resolution_code);
+ }
+ /* set mode byte: */
+ mode[6] = 0x83 | (color_code << 5);
+ if (!(s->hw->flags & MUSTEK_FLAG_USE_EIGHTS))
+ mode[6] |= 0x08;
+ if (s->custom_halftone_pattern)
+ mode[6] |= 0x10;
+ if (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ {
+ if ((s->mode == MUSTEK_MODE_LINEART)
+ || (s->mode == MUSTEK_MODE_HALFTONE))
+ {
+ mode[8] =
+ encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
+ mode[9] =
+ encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w));
+ }
+ else
+ {
+ mode[8] = 0x0c;
+ mode[9] = 0x0c;
+ }
+ mode[10] = 2; /* grain */
+ if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w)
+ mode[11] = 0x01;
+ else if ((s->mode == MUSTEK_MODE_COLOR)
+ || (s->mode == MUSTEK_MODE_HALFTONE))
+ mode[11] = 0x00; /* speed */
+ else
+ mode[11] = 0x02; /* speed */
+ mode[12] = 0x00; /* shadow param not used by Mustek */
+ mode[13] = 0xff; /* highlight only used by some scanners */
+ mode[14] = 0x70; /* paper- */
+ mode[15] = 0x00; /* length */
+ mode[16] = 0x53; /* midtone param not used by Mustek */
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2)
+ {
+ mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
+ mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w));
+ mode[10] = 2; /* grain */
+ if ((s->mode == MUSTEK_MODE_COLOR) || (s->mode == MUSTEK_MODE_HALFTONE))
+ mode[11] = 0x00; /* speed */
+ else
+ mode[11] = 0x02; /* speed */
+ mode[12] = 0x00; /* shadow param not used by Mustek */
+ mode[13] = 0x00; /* highlight param not used by Mustek */
+ mode[14] = 0x5c; /* paper- */
+ mode[15] = 0x00; /* length */
+ mode[16] = 0x41; /* midtone param not used by Mustek */
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ mode[8] = encode_percentage
+ (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS + s->pass + 1].w - 1));
+ mode[9] = encode_percentage
+ (s, SANE_UNFIX (s->val[OPT_CONTRAST + s->pass + 1].w - 1));
+ }
+ else
+ {
+ mode[8] = encode_percentage
+ (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w - 1));
+ mode[9] = encode_percentage
+ (s, SANE_UNFIX (s->val[OPT_CONTRAST].w - 1));
+ }
+ mode[10] = s->halftone_pattern_type;
+ mode[11] = speed_code; /* lamp setting not supported yet */
+ mode[12] = 0; /* shadow param not used by Mustek */
+ mode[13] = 0; /* highlight param not used by Mustek */
+ mode[14] = mode[15] = 0; /* paperlength not used by Mustek */
+ mode[16] = 0; /* midtone param not used by Mustek */
+ }
+ else
+ {
+ mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
+ mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w));
+ mode[10] = s->halftone_pattern_type;
+ mode[11] = speed_code; /* lamp setting not supported yet */
+ mode[12] = 0; /* shadow param not used by Mustek */
+ mode[13] = 0; /* highlight param not used by Mustek */
+ mode[14] = mode[15] = 0; /* paperlength not used by Mustek */
+ mode[16] = 0; /* midtone param not used by Mustek */
+ }
+
+ DBG (5, "mode_select: resolution_code=%d (0x%x)\n", s->resolution_code,
+ s->resolution_code);
+ return dev_cmd (s, mode, 6 + mode[4], 0, 0);
+}
+
+/* Pro series */
+static SANE_Status
+mode_select_pro (Mustek_Scanner * s)
+{
+ SANE_Byte mode[19], *cp;
+
+ memset (mode, 0, sizeof (mode));
+
+ mode[0] = MUSTEK_SCSI_MODE_SELECT;
+ mode[4] = 0x0d;
+
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
+ mode[6] = 0xE0;
+ else
+ mode[6] = 0x60;
+ }
+ else if (s->mode & MUSTEK_MODE_GRAY)
+ {
+ if (s->val[OPT_FAST_GRAY_MODE].w)
+ mode[6] = 0x20;
+ else
+ mode[6] = 0x40;
+ }
+ else
+ mode[6] = 0x00; /* lineart */
+
+ mode[7] = 0;
+ mode[8] = 0;
+ mode[9] = 0;
+ mode[10] = 0;
+ mode[11] = 0x00;
+ mode[12] = 0x27;
+ mode[13] = 0xb0;
+ mode[14] = 0x04;
+ mode[15] = 0x43;
+ mode[16] = 0x41;
+
+ cp = mode + 17;
+ STORE16L (cp, s->resolution_code);
+
+ DBG (5, "mode_select_pro: resolution_code=%d (0x%x), mode=0x%x\n",
+ s->resolution_code, s->resolution_code, mode[6]);
+ return dev_cmd (s, mode, 6 + mode[4], 0, 0);
+}
+
+/* Paragon and Pro series. According to Mustek, the only builtin gamma
+ table is a linear table, so all we support here is user-defined
+ gamma tables. */
+static SANE_Status
+gamma_correction (Mustek_Scanner * s, SANE_Int color_code)
+{
+ SANE_Int i, j, table = 0, len = 0, bytes_per_channel, num_channels = 1;
+ SANE_Byte gamma[4096 + 10], val, *cp; /* for Paragon models 3 x 256 is the
+ maximum. Pro needs 4096 bytes */
+
+ if ((s->hw->flags & MUSTEK_FLAG_N)
+ && ((s->mode & MUSTEK_MODE_LINEART)
+ || (s->mode & MUSTEK_MODE_HALFTONE)))
+ {
+ /* sigh! - the 600 II N needs a (dummy) table download even for
+ lineart and halftone mode, else it produces a completely
+ white image. Thank Mustek for their buggy firmware ! */
+ memset (gamma, 0, sizeof (gamma));
+ gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE;
+ gamma[2] = 0x0; /* indicate any preloaded gamma table */
+ DBG (5, "gamma_correction: sending dummy gamma table\n");
+ return dev_cmd (s, gamma, 6, 0, 0);
+ }
+
+ if (((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE))
+ && !(s->hw->flags & MUSTEK_FLAG_PRO))
+ {
+ DBG (5, "gamma_correction: nothing to do in lineart mode -- exiting\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ if ((!s->val[OPT_CUSTOM_GAMMA].w) && (!(s->hw->flags & MUSTEK_FLAG_PRO)))
+ {
+ /* Do we need to upload a gamma table even if the user didn't select
+ this option? Some scanners need this work around. */
+ if (!(s->hw->flags & MUSTEK_FLAG_FORCE_GAMMA) ||
+ !((s->mode & MUSTEK_MODE_COLOR) || (s->mode & MUSTEK_MODE_GRAY)))
+ {
+ DBG (5,
+ "gamma_correction: no custom table selected -- exititing\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ table = 1;
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ table += s->pass;
+ else
+ {
+ if ((color_code == MUSTEK_CODE_GRAY)
+ && !(s->hw->flags & MUSTEK_FLAG_PRO))
+ num_channels = 3;
+ else
+ table = color_code;
+ }
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ /* it seems 600 II N (firmware 1.x at least) wants 768 bytes in
+ * gray mode too */
+ num_channels = 3;
+ }
+
+ memset (gamma, 0, sizeof (gamma));
+ gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE;
+
+ if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+ bytes_per_channel = 4096;
+ len = bytes_per_channel;
+ if (s->mode == MUSTEK_MODE_COLOR)
+ {
+ gamma[9] = (color_code << 6); /* color */
+ if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
+ gamma[2] = 0x7f; /* medium brightness */
+ }
+ else if (s->mode == MUSTEK_MODE_GRAY)
+ {
+ gamma[9] = 0x80; /* grayscale */
+ if (s->val[OPT_FAST_GRAY_MODE].w)
+ gamma[2] = 0x7f; /* medium brightness */
+ }
+ else /* lineart */
+ {
+ gamma[2] =
+ 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0;
+ gamma[9] = 0x80; /* grayscale/lineart */
+ DBG (5, "gamma_correction: sending brightness information\n");
+ }
+ gamma[7] = (len >> 8) & 0xff; /* big endian! */
+ gamma[8] = (len >> 0) & 0xff;
+ }
+ else
+ {
+ bytes_per_channel = 256;
+ len = num_channels * bytes_per_channel;
+ gamma[2] = 0x27; /* indicate user-selected gamma table */
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ /* 600 II N always uses 6-byte cdb */
+ gamma[3] = (len >> 8) & 0xff; /* big endian! */
+ gamma[4] = (len >> 0) & 0xff;
+ /* no way to pass color_code (?) */
+ }
+ else
+ {
+ gamma[7] = (len >> 8) & 0xff; /* big endian! */
+ gamma[8] = (len >> 0) & 0xff;
+ gamma[9] = (color_code << 6);
+ }
+ }
+
+ if (len > 0)
+ {
+ cp = gamma + 10;
+ for (j = 0; j < num_channels; ++j)
+ {
+ for (i = 0; i < bytes_per_channel; ++i)
+ {
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ val = s->gamma_table[table][i * 256 / bytes_per_channel];
+ else
+ val = i * 256 / bytes_per_channel;
+ if ((s->mode & MUSTEK_MODE_COLOR)
+ && (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE))
+ /* compose intensity gamma and color channel gamma: */
+ val = s->gamma_table[0][val];
+ *cp++ = val;
+ }
+ if (!(s->hw->flags & MUSTEK_FLAG_N)
+ || !(s->mode & MUSTEK_MODE_GRAY))
+ table++;
+ }
+ }
+ DBG (5, "gamma_correction: sending gamma table of %d bytes\n", len);
+ return dev_cmd (s, gamma, 10 + len, 0, 0);
+}
+
+static SANE_Status
+send_gamma_table (Mustek_Scanner * s)
+{
+ SANE_Status status;
+
+ if (s->one_pass_color_scan)
+ {
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ /* This _should_ work for all one-pass scanners (not just
+ AB306N scanners), but it doesn't work for my Paragon
+ 600 II SP with firmware rev 1.01. Too bad, since it would
+ simplify the gamma correction code quite a bit. */
+ status = gamma_correction (s, MUSTEK_CODE_GRAY);
+ else
+ {
+ status = gamma_correction (s, MUSTEK_CODE_RED);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = gamma_correction (s, MUSTEK_CODE_GREEN);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = gamma_correction (s, MUSTEK_CODE_BLUE);
+ }
+ }
+ else
+ status = gamma_correction (s, MUSTEK_CODE_GRAY);
+ return status;
+}
+
+
+/* ScanExpress and Paragon series */
+static SANE_Status
+start_scan (Mustek_Scanner * s)
+{
+ SANE_Byte start[6];
+ SANE_Status status;
+
+ memset (start, 0, sizeof (start));
+ start[0] = MUSTEK_SCSI_START_STOP;
+ start[4] = 0x01;
+
+ DBG (4, "start_scan\n");
+ /* ScanExpress and Pro models don't have any variants */
+ if (!(s->hw->flags & MUSTEK_FLAG_SE) && !(s->hw->flags & MUSTEK_FLAG_PRO))
+ {
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ start[4] |= ((s->pass + 1) << 3);
+ else
+ start[4] |= 0x20;
+ }
+ /* or in single/multi bit: */
+ start[4] |= ((s->mode & MUSTEK_MODE_LINEART)
+ || (s->mode & MUSTEK_MODE_HALFTONE)) ? 0 : (1 << 6);
+
+ /* or in expanded resolution bit: */
+ if (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2)
+ && ((s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ || (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ || (s->hw->flags & MUSTEK_FLAG_PARAGON_2)))
+ start[4] |= 1 << 7;
+
+ /* block mode (or whatever) */
+ if (s->hw->flags & MUSTEK_FLAG_USE_BLOCK)
+ {
+ start[5] = 0x08;
+ DBG (4, "start_scan: using block mode\n");
+ }
+ }
+
+ status = dev_cmd (s, start, sizeof (start), 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ DBG (1, "start_scan returned status %s\n", sane_strstatus (status));
+ return status;
+}
+
+static SANE_Status
+do_eof (Mustek_Scanner * s)
+{
+ if (s->pipe >= 0)
+ {
+ close (s->pipe);
+ s->pipe = -1;
+ DBG (5, "do_eof: closing pipe\n");
+ }
+ return SANE_STATUS_EOF;
+}
+
+static SANE_Status
+do_stop (Mustek_Scanner * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (5, "do_stop\n");
+
+ if (s->cancelled)
+ status = SANE_STATUS_CANCELLED;
+
+ s->scanning = SANE_FALSE;
+ s->pass = 0;
+
+ if (s->reader_pid != -1)
+ {
+ SANE_Int exit_status;
+ struct timeval now;
+ long int scan_time;
+ long int scan_size;
+ SANE_Pid pid;
+
+ /* print scanning time */
+ gettimeofday (&now, 0);
+ scan_time = now.tv_sec - s->start_time;
+ if (scan_time < 1)
+ scan_time = 1;
+ scan_size = s->hw->bpl * s->hw->lines / 1024;
+ DBG (2, "Scanning time was %ld seconds, %ld kB/s\n", scan_time,
+ scan_size / scan_time);
+
+ if (s->total_bytes == s->params.lines * s->params.bytes_per_line)
+ DBG (3, "Scanned %d bytes as expected\n", s->total_bytes);
+ else if (s->total_bytes < s->params.lines * s->params.bytes_per_line)
+ DBG (3, "Scanned %d bytes, expected %d bytes\n", s->total_bytes,
+ s->params.lines * s->params.bytes_per_line);
+ else
+ DBG (1, "Warning: Scanned %d bytes, but expected only %d bytes\n",
+ s->total_bytes, s->params.lines * s->params.bytes_per_line);
+
+ /* ensure child knows it's time to stop: */
+ DBG (5, "do_stop: terminating reader process\n");
+ sanei_thread_kill (s->reader_pid);
+
+ pid = sanei_thread_waitpid (s->reader_pid, &exit_status);
+ if (pid == -1)
+ {
+ DBG (1,
+ "do_stop: sanei_thread_waitpid failed, already terminated? (%s)\n",
+ strerror (errno));
+ }
+ else
+ {
+ DBG (2, "do_stop: reader process terminated with status %s\n",
+ sane_strstatus (exit_status));
+ if (status != SANE_STATUS_CANCELLED
+ && exit_status != SANE_STATUS_GOOD)
+ status = exit_status;
+ }
+
+ s->reader_pid = -1;
+ }
+
+ if (s->fd >= 0)
+ {
+ if (!sanei_thread_is_forked ())
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+
+ if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+ if (s->total_bytes < s->params.lines * s->params.bytes_per_line)
+ status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop),
+ 0, 0);
+ dev_wait_ready (s);
+ }
+ else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ || (s->hw->flags & MUSTEK_FLAG_PARAGON_2)
+ || (s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ if (s->cancelled &&
+ (s->total_bytes < s->params.lines * s->params.bytes_per_line))
+ status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop),
+ 0, 0);
+ }
+ else
+ status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), 0, 0);
+
+ if (force_wait)
+ {
+ DBG (5, "do_stop: waiting for scanner to be ready\n");
+ dev_wait_ready (s);
+ }
+
+ do_eof (s);
+ DBG (5, "do_stop: closing scanner\n");
+ dev_close (s);
+ s->fd = -1;
+ }
+
+ DBG (5, "do_stop: finished\n");
+ return status;
+}
+
+/* Paragon I + II: Determine the CCD's distance between the primary color
+ lines. */
+static SANE_Status
+line_distance (Mustek_Scanner * s)
+{
+ SANE_Int factor, color, res, peak_res;
+ SANE_Status status;
+ SANE_Byte result[5];
+ size_t len;
+
+ memset (result, 0, 5);
+
+ res = SANE_UNFIX (s->val[OPT_RESOLUTION].w) + 0.5;
+ peak_res = SANE_UNFIX (s->hw->dpi_range.max) + 0.5;
+
+ s->ld.buf[0] = NULL;
+
+ len = sizeof (result);
+ status = dev_cmd (s, scsi_ccd_distance, sizeof (scsi_ccd_distance),
+ result, &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG (3, "line_distance: got factor=%d, (r/g/b)=(%d/%d/%d)\n",
+ result[0] | (result[1] << 8), result[2], result[3], result[4]);
+
+ if (s->hw->flags & MUSTEK_FLAG_LD_FIX)
+ {
+ result[0] = 0xff;
+ result[1] = 0xff;
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ /* According to Andreas Czechanowski, the line-distance values
+ returned for the AB306N scanners are garbage, so we have to
+ fix things up manually. Not good.
+ This seems to be true only for firmware 2.00 which is
+ extremely seldom.. AB306N scanners with firmware 1.01 don't
+ need this fix. <henning@meier-geinitz.de> */
+ if (peak_res == 600)
+ {
+ if (res < 51)
+ {
+ result[0] = 8;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 2;
+ result[4] = 3;
+ }
+ else if (res < 75 || (res > 90 && res < 150))
+ {
+ /* 51-74 and 91-149 dpi: */
+ result[0] = 4;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 3;
+ result[4] = 5;
+ }
+ else if (res <= 90 || (res >= 150 && res <= 300))
+ {
+ /* 75-90 and 150-300 dpi: */
+ result[0] = 2;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 5;
+ result[4] = 9;
+ }
+ else
+ {
+ /* 301-600 dpi: */
+ result[0] = 1;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 9;
+ result[4] = 23;
+ }
+ }
+ else
+ DBG (1, "don't know how to fix up line-distance for %d dpi\n",
+ peak_res);
+ }
+ else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE))
+ {
+ if (peak_res == 600)
+ {
+ if (res < 51)
+ {
+ /* 1-50 dpi: */
+ result[0] = 4;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 3;
+ result[4] = 5;
+ }
+ else if (res <= 300)
+ {
+ /* 51-300 dpi: */
+ result[0] = 2;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 5;
+ result[4] = 9;
+ }
+ else
+ {
+ /*301-600 dpi: */
+ result[0] = 1;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 9;
+ result[4] = 17;
+ }
+ }
+ else if (peak_res == 800)
+ {
+ if (res < 72)
+ {
+ /* 1-71 dpi: */
+ result[0] = 4;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 3;
+ result[4] = 5;
+ }
+ else if (res <= 400)
+ {
+ /* 72-400 dpi: */
+ result[0] = 2;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 9;
+ result[4] = 17;
+ }
+ else
+ {
+ /*401-800 dpi: */
+ result[0] = 1;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 16;
+ result[4] = 32;
+ }
+ }
+ }
+ }
+ DBG (4, "line_distance: fixed up to factor=%d, (r/g/b)=(%d/%d/%d)\n",
+ result[0] | (result[1] << 8), result[2], result[3], result[4]);
+ }
+
+ factor = result[0] | (result[1] << 8);
+ if (factor != 0xffff)
+ {
+ /* need to do line-distance adjustment ourselves... */
+
+ s->ld.max_value = peak_res;
+
+ if (factor == 0)
+ {
+ if (res <= peak_res / 2)
+ res *= 2;
+ }
+ else
+ res *= factor;
+ s->ld.peak_res = res;
+ for (color = 0; color < 3; ++color)
+ {
+ s->ld.dist[color] = result[2 + color];
+ s->ld.quant[color] = s->ld.max_value;
+ s->ld.index[color] = -s->ld.dist[color];
+ }
+ s->ld.lmod3 = -1;
+
+ DBG (4, "line_distance: max_value = %d, peak_res = %d, ld.quant = "
+ "(%d, %d, %d)\n", s->ld.max_value, s->ld.peak_res, s->ld.quant[0],
+ s->ld.quant[1], s->ld.quant[2]);
+ }
+ else
+ s->ld.max_value = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Paragon + Pro series */
+static SANE_Status
+get_image_status (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines)
+{
+ SANE_Byte result[6];
+ SANE_Status status;
+ size_t len;
+ SANE_Int busy, offset;
+ long res, half_res;
+
+ memset (result, 0, 6);
+
+ /* The 600 II N v1.01 and Paragon 12000SP need a larger scan-area for
+ line-distance correction in color mode */
+ offset = 0;
+ if ((s->hw->flags & MUSTEK_FLAG_LD_N1) && (s->mode & MUSTEK_MODE_COLOR))
+ offset = s->ld.dist[1];
+ else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK)
+ && (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ && (s->mode & MUSTEK_MODE_COLOR))
+ offset = MAX_LINE_DIST * SANE_UNFIX (s->val[OPT_RESOLUTION].w)
+ / SANE_UNFIX (s->hw->dpi_range.max);
+
+ do
+ {
+ len = sizeof (result);
+ status = dev_cmd (s, scsi_get_image_status,
+ sizeof (scsi_get_image_status), result, &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ busy = result[0];
+ if (busy)
+ usleep (100000);
+
+ if (!s->scanning) /* ? */
+ if (!(s->hw->flags & MUSTEK_FLAG_PRO))
+ return do_stop (s);
+ }
+ while (busy);
+
+ s->hw->bpl = result[1] | (result[2] << 8);
+ s->hw->lines = result[3] | (result[4] << 8) | (result[5] << 16);
+
+ res = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
+ /* Need to interpolate resolutions > max x-resolution? */
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
+ {
+ *bpl = (s->hw->bpl * res) / half_res / 3;
+ *bpl *= 3;
+ DBG (4, "get_image_status: resolution > x-max; enlarge %d bpl to "
+ "%d bpl\n", s->hw->bpl, *bpl);
+ }
+ else
+ *bpl = s->hw->bpl;
+
+ *lines = s->hw->lines - offset;
+
+ DBG (3, "get_image_status: bytes_per_line=%d, lines=%d (offset = %d)\n",
+ *bpl, *lines, offset);
+ return SANE_STATUS_GOOD;
+}
+
+/* ScanExpress models */
+static SANE_Status
+get_window (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines,
+ SANE_Int * pixels)
+{
+ SANE_Byte result[48];
+ SANE_Status status;
+ size_t len;
+ SANE_Int color;
+ long res, half_res;
+
+ res = s->resolution_code;
+ half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
+
+ DBG (5, "get_window: resolution: %ld dpi (hardware: %d dpi)\n",
+ res, s->ld.peak_res);
+
+ len = sizeof (result);
+ status = dev_cmd (s, scsi_get_window, sizeof (scsi_get_window), result,
+ &len);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (!s->scanning)
+ return do_stop (s);
+
+ s->hw->cal.bytes = (result[6] << 24) | (result[7] << 16) |
+ (result[8] << 8) | (result[9] << 0);
+ s->hw->cal.lines = (result[10] << 24) | (result[11] << 16) |
+ (result[12] << 8) | (result[13] << 0);
+
+ DBG (4, "get_window: calibration bpl=%d, lines=%d\n",
+ s->hw->cal.bytes, s->hw->cal.lines);
+
+ s->hw->bpl = (result[14] << 24) | (result[15] << 16) |
+ (result[16] << 8) | result[17];
+
+ s->hw->lines = (result[18] << 24) | (result[19] << 16) |
+ (result[20] << 8) | result[21];
+
+ DBG (4, "get_window: scan bpl=%d, lines=%d\n", s->hw->bpl, s->hw->lines);
+
+ if ((s->hw->cal.bytes == 0) || (s->hw->cal.lines == 0)
+ || (s->hw->bpl == 0) || (s->hw->lines == 0))
+ {
+ DBG (1, "get_window: oops, none of these values should be 0 "
+ "-- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ s->hw->gamma_length = 1 << result[26];
+ DBG (4, "get_window: gamma length=%d\n", s->hw->gamma_length);
+
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ s->ld.buf[0] = NULL;
+ for (color = 0; color < 3; ++color)
+ {
+ s->ld.dist[color] = result[42 + color];
+ }
+
+ DBG (4, "get_window: LD res=%d, (r/g/b)=(%d/%d/%d)\n",
+ (result[40] << 8) | result[41], s->ld.dist[0],
+ s->ld.dist[1], s->ld.dist[2]);
+ s->ld.max_value = (result[40] << 8) | result[41];
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
+ {
+ /* We must interpolate resolutions > max x-resolution */
+ *bpl = *pixels = (((s->hw->bpl / 3) * res) / half_res) * 3;
+ }
+ else
+ {
+ /* Scale down the image according to desired resolution */
+ *bpl = *pixels = (((s->hw->bpl / 3) * res) / s->ld.peak_res) * 3;
+ }
+ *lines = (s->hw->lines - s->ld.dist[2]) * res / s->ld.peak_res;
+ }
+ else
+ {
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
+ {
+ /* We must interpolate resolutions > max x-resolution */
+ *bpl = s->hw->bpl * res / half_res;
+ }
+ else
+ {
+ *bpl = s->hw->bpl;
+ }
+ *lines = s->hw->lines;
+ }
+ DBG (4, "get_window: bpl = %d (hardware: %d), lines = %d (hardware: %d)\n",
+ *bpl, s->hw->bpl, *lines, s->hw->lines);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+adf_and_backtrack (Mustek_Scanner * s)
+{
+ SANE_Byte backtrack[6];
+ SANE_Int code = 0x80;
+
+ if (!(s->hw->flags & MUSTEK_FLAG_NO_BACKTRACK))
+ code |= 0x02; /* enable backtracking */
+
+ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
+ code |= 0x01;
+ else if (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0)
+ code |= 0x04;
+ memset (backtrack, 0, sizeof (backtrack));
+ backtrack[0] = MUSTEK_SCSI_ADF_AND_BACKTRACK;
+ backtrack[4] = code;
+
+ DBG (4, "adf_and_backtrack: backtrack: %s; ADF: %s; TA: %s\n",
+ code & 0x02 ? "yes" : "no", code & 0x01 ? "yes" : "no",
+ code & 0x04 ? "yes" : "no");
+ return dev_cmd (s, backtrack, sizeof (backtrack), 0, 0);
+}
+
+/* 600 II N firmware 2.x */
+static SANE_Int
+fix_line_distance_n_2 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
+ SANE_Byte * raw, SANE_Byte * out)
+{
+ SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
+ SANE_Int c, num_saved_lines, line;
+
+ if (!s->ld.buf[0])
+ {
+ /* This buffer must be big enough to hold maximum line distance
+ times max_bpl bytes. The maximum line distance for the
+ Paragon 600 II N scanner is 23, so 40 should be safe. */
+ DBG (5,
+ "fix_line_distance_n_2: allocating temp buffer of %d*%d bytes\n",
+ MAX_LINE_DIST, bpl);
+ s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl);
+ if (!s->ld.buf[0])
+ {
+ DBG (1,
+ "fix_line_distance_n_2: failed to malloc temporary buffer\n");
+ return 0;
+ }
+ }
+
+ num_saved_lines = s->ld.index[0] - s->ld.index[2];
+ if (num_saved_lines > 0)
+ /* restore the previously saved lines: */
+ memcpy (out, s->ld.buf[0], num_saved_lines * bpl);
+
+ while (1)
+ {
+ if (++s->ld.lmod3 >= 3)
+ s->ld.lmod3 = 0;
+
+ c = color_seq[s->ld.lmod3];
+ if (s->ld.index[c] < 0)
+ ++s->ld.index[c];
+ else if (s->ld.index[c] < s->params.lines)
+ {
+ s->ld.quant[c] += s->ld.peak_res;
+ if (s->ld.quant[c] > s->ld.max_value)
+ {
+ s->ld.quant[c] -= s->ld.max_value;
+ line = s->ld.index[c]++ - s->ld.ld_line;
+ out_ptr = out + line * bpl + c;
+ out_end = out_ptr + bpl;
+ while (out_ptr != out_end)
+ {
+ *out_ptr = *raw++;
+ out_ptr += 3;
+ }
+
+ if (raw >= raw_end)
+ {
+ DBG (3, "fix_line_distance_n_2: lmod3=%d, "
+ "index=(%d,%d,%d)\n", s->ld.lmod3,
+ s->ld.index[0], s->ld.index[1], s->ld.index[2]);
+ num_lines = s->ld.index[2] - s->ld.ld_line;
+
+ /* copy away the lines with at least one missing
+ color component, so that we can interleave them
+ with new scan data on the next call */
+ num_saved_lines = s->ld.index[0] - s->ld.index[2];
+ memcpy (s->ld.buf[0], out + num_lines * bpl,
+ num_saved_lines * bpl);
+
+ /* notice the number of lines we processed */
+ s->ld.ld_line = s->ld.index[2];
+ /* return number of complete (r+g+b) lines */
+ return num_lines;
+ }
+ }
+ }
+ }
+}
+
+/* 600 II N firmware 1.x */
+static SANE_Int
+fix_line_distance_n_1 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
+ SANE_Byte * raw, SANE_Byte * out)
+{
+ SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
+ SANE_Int c, num_saved_lines, line;
+
+ /* For firmware 1.x the scanarea must be soemwhat bigger than needed
+ because of the linedistance correction */
+
+ if (!s->ld.buf[0])
+ {
+ /* This buffer must be big enough to hold maximum line distance
+ times max_bpl bytes. The maximum line distance for the 600 II N
+ is 23, so 40 is safe. */
+ DBG (5,
+ "fix_line_distance_n_1: allocating temp buffer of %d*%d bytes\n",
+ MAX_LINE_DIST, bpl);
+ s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl);
+ if (!s->ld.buf[0])
+ {
+ DBG (1,
+ "fix_line_distance_n_1: failed to malloc temporary buffer\n");
+ return 0;
+ }
+ }
+ num_saved_lines = s->ld.index[0] - s->ld.index[1];
+ DBG (5, "fix_line_distance_n_1: got %d lines, %d bpl\n", num_lines, bpl);
+ DBG (5, "fix_line_distance_n_1: num_saved_lines = %d; peak_res = %d; "
+ "max_value = %d\n", num_saved_lines, s->ld.peak_res, s->ld.max_value);
+ if (num_saved_lines > 0)
+ /* restore the previously saved lines: */
+ memcpy (out, s->ld.buf[0], num_saved_lines * bpl);
+
+ while (1)
+ {
+ if (++s->ld.lmod3 >= 3)
+ s->ld.lmod3 = 0;
+ c = s->ld.lmod3;
+ if (s->ld.index[c] < 0)
+ ++s->ld.index[c];
+ else
+ {
+ s->ld.quant[c] += s->ld.peak_res;
+ if (s->ld.quant[c] > s->ld.max_value)
+ {
+ s->ld.quant[c] -= s->ld.max_value;
+ line = s->ld.index[c]++ - s->ld.ld_line;
+ out_ptr = out + line * bpl + c;
+ out_end = out_ptr + bpl;
+ while (out_ptr != out_end)
+ {
+ *out_ptr = *raw++;
+ out_ptr += 3;
+ }
+ DBG (5, "fix_line_distance_n_1: copied line %d (color %d)\n",
+ line, c);
+ }
+ }
+ if ((raw >= raw_end) || ((s->ld.index[0] >= s->params.lines) &&
+ (s->ld.index[1] >= s->params.lines) &&
+ (s->ld.index[2] >= s->params.lines)))
+ {
+ DBG (3, "fix_line_distance_n_1: lmod3=%d, index=(%d,%d,%d)%s\n",
+ s->ld.lmod3,
+ s->ld.index[0], s->ld.index[1], s->ld.index[2],
+ raw >= raw_end ? " raw >= raw_end" : "");
+ num_lines = s->ld.index[1] - s->ld.ld_line;
+ if (num_lines < 0)
+ num_lines = 0;
+ DBG (4, "fix_line_distance_n_1: lines ready: %d\n", num_lines);
+
+ /* copy away the lines with at least one missing
+ color component, so that we can interleave them
+ with new scan data on the next call */
+ num_saved_lines = s->ld.index[0] - s->ld.index[1];
+ DBG (4, "fix_line_distance_n_1: copied %d lines to "
+ "ld.buf\n", num_saved_lines);
+ memcpy (s->ld.buf[0], out + num_lines * bpl, num_saved_lines * bpl);
+ /* notice the number of lines we processed */
+ s->ld.ld_line = s->ld.index[1];
+ if (s->ld.ld_line < 0)
+ s->ld.ld_line = 0;
+ /* return number of complete (r+g+b) lines */
+ return num_lines;
+ }
+
+ }
+}
+
+/* For ScanExpress models */
+static SANE_Int
+fix_line_distance_se (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
+ SANE_Byte * raw, SANE_Byte * out)
+{
+ SANE_Byte *raw_end = raw + num_lines * bpl;
+ SANE_Byte *out_ptr[3], *ptr;
+ SANE_Int index[3], lines[3], quant[3], dist[3];
+ SANE_Int max_value;
+ SANE_Int color, pixel, res, half_res, scale;
+ SANE_Int bpc = bpl / 3; /* bytes per color (per line) */
+ SANE_Bool preview = SANE_FALSE;
+
+ res = s->resolution_code;
+ half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
+ max_value = s->ld.max_value;
+ if ((s->val[OPT_PREVIEW].w == SANE_TRUE)
+ && (s->val[OPT_FAST_PREVIEW].w == SANE_TRUE))
+ {
+ preview = SANE_TRUE;
+ /*max_value = 75; */
+ dist[0] = s->ld.dist[0];
+ dist[1] = s->ld.dist[1];
+ dist[2] = s->ld.dist[2];
+ }
+ else
+ {
+ dist[0] = s->ld.dist[0];
+ dist[1] = s->ld.dist[1];
+ dist[2] = s->ld.dist[2];
+ }
+
+ if (!s->ld.buf[0])
+ {
+ /* This buffer must be big enough to hold maximum line distance times
+ 3*bpl bytes. The maximum line distance for 1200 dpi is 32 */
+ DBG (5, "fix_line_distance_se: allocating temp buffer of %d*%d bytes\n",
+ 3 * MAX_LINE_DIST, bpc);
+ s->ld.buf[0] = malloc (3 * MAX_LINE_DIST * (long) bpc);
+
+ if (!s->ld.buf[0])
+ {
+ DBG (1,
+ "fix_line_distance_se: failed to malloc temporary buffer\n");
+ return 0;
+ }
+
+ /* Note that either s->ld.buf[1] or s->ld.buf[2] is never used. */
+ s->ld.buf[1] = s->ld.buf[2] =
+ s->ld.buf[0] + 2 * MAX_LINE_DIST * (long) bpc;
+
+ /* Since the blocks don't start necessarily with red note color. */
+ s->ld.color = 0;
+
+ /* The scan area must be longer than desired because of the line
+ distance. So me must count complete (r+g+b) lines already
+ submitted to the fronted. */
+ s->ld.ld_line = s->params.lines;
+
+ for (color = 0; color < 3; ++color)
+ {
+ s->ld.index[color] = -dist[color];
+ s->ld.quant[color] = 0;
+ s->ld.saved[color] = 0;
+ }
+ }
+
+ num_lines *= 3;
+ DBG (5, "fix_line_distance_se: start color: %d; %d lines \n",
+ s->ld.color, num_lines);
+
+ /* First scan the lines read and count red, green and blue ones.
+ Since we will step through the lines a second time we must not
+ alter any global variables here! */
+ for (color = 0; color < 3; ++color)
+ {
+ index[color] = s->ld.index[color];
+ lines[color] = s->ld.saved[color];
+ quant[color] = s->ld.quant[color];
+ }
+
+ color = s->ld.color;
+ while (num_lines > 0)
+ {
+ if (index[color] < 0)
+ ++index[color];
+ else
+ {
+ quant[color] += res;
+ if (quant[color] >= max_value)
+ {
+ /* This line must be processed, not dropped. */
+ quant[color] -= max_value;
+ ++lines[color];
+ --num_lines;
+ }
+ else if (!preview)
+ --num_lines;
+
+ }
+ if (++color > 2)
+ color = 0;
+ }
+
+ /* Calculate how many triples of color lines we can output now.
+ Because the number of available red lines is always greater
+ than for the other colors we may ignore the red ones here. */
+ num_lines = MIN (lines[1], lines[2]);
+
+ DBG (5, "fix_line_distance_se: saved lines: %d/%d/%d\n", s->ld.saved[0],
+ s->ld.saved[1], s->ld.saved[2]);
+ DBG (5, "fix_line_distance_se: available: %d/%d/%d --> triples: %d\n",
+ lines[0], lines[1], lines[2], num_lines);
+
+ lines[0] = lines[1] = lines[2] = num_lines;
+
+ /* Output the color lines saved in previous call first.
+ Note that data is converted in r/g/b interleave on the fly. */
+ for (color = 0; color < 3; ++color)
+ {
+ out_ptr[color] = out + color;
+ ptr = s->ld.buf[color];
+ while ((s->ld.saved[color] > 0) && (lines[color] > 0))
+ {
+ scale = 0;
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
+ {
+ /* Need to enlarge x-resolution */
+ SANE_Byte *ptr_start = ptr;
+ for (pixel = 0; pixel < s->params.pixels_per_line; ++pixel)
+ {
+ *out_ptr[color] = *ptr;
+ out_ptr[color] += 3;
+ scale += half_res;
+ if (scale >= half_res)
+ {
+ scale -= res;
+ ++ptr;
+ }
+ }
+ DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; "
+ "color: %d; raw bytes: %lu; out bytes: %d\n",
+ s->ld.saved[color], lines[color], color, (u_long) (ptr - ptr_start),
+ s->params.pixels_per_line);
+ ptr = ptr_start + bpc;
+ }
+ else
+ {
+ if (preview)
+ {
+ for (pixel = 0; pixel < bpc; ++pixel)
+ {
+ *out_ptr[color] = *ptr++;
+ out_ptr[color] += 3;
+ }
+ }
+ else
+ {
+ for (pixel = 0; pixel < bpc; ++pixel)
+ {
+ scale += res;
+ if (scale >= max_value)
+ {
+ scale -= max_value;
+ *out_ptr[color] = *ptr;
+ out_ptr[color] += 3;
+ }
+ ++ptr;
+ }
+ }
+ DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; "
+ "color: %d\n", s->ld.saved[color], lines[color], color);
+ }
+ --(s->ld.saved[color]);
+ --lines[color];
+ }
+ if (s->ld.saved[color] > 0)
+ memmove (s->ld.buf[color], ptr, s->ld.saved[color] * bpc);
+ }
+
+ while (1)
+ {
+ if (s->ld.index[s->ld.color] < 0)
+ ++(s->ld.index[s->ld.color]);
+ else
+ {
+ s->ld.quant[s->ld.color] += res;
+ if (s->ld.quant[s->ld.color] >= max_value)
+ {
+ /* This line must be processed, not dropped. */
+ s->ld.quant[s->ld.color] -= max_value;
+
+ if (lines[s->ld.color] > 0)
+ {
+ /* There's still a line to be output for current color.
+ Then shuffle current color line to output buffer. */
+ scale = 0;
+ /* need to enlarge x-resolution? */
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X)
+ && (res > half_res))
+ {
+ SANE_Byte *raw_start = raw;
+ for (pixel = 0; pixel < s->params.pixels_per_line;
+ ++pixel)
+ {
+ *out_ptr[s->ld.color] = *raw;
+ out_ptr[s->ld.color] += 3;
+ scale += half_res;
+ if (scale >= half_res)
+ {
+ scale -= res;
+ ++raw;
+ }
+
+ }
+ DBG (5,
+ "fix_line_distance_se: got line: %d; color: %d; "
+ "raw bytes: %lu; out bytes: %d\n",
+ lines[s->ld.color], s->ld.color, (u_long) (raw - raw_start),
+ s->params.pixels_per_line);
+ raw = raw_start + bpc;
+ }
+ else
+ {
+ if (preview)
+ {
+ for (pixel = 0; pixel < bpc; ++pixel)
+ {
+ *out_ptr[s->ld.color] = *raw++;
+ out_ptr[s->ld.color] += 3;
+ }
+ }
+ else
+ {
+ for (pixel = 0; pixel < bpc; ++pixel)
+ {
+ scale += res;
+ if (scale >= max_value)
+ {
+ scale -= max_value;
+ *out_ptr[s->ld.color] = *raw;
+ out_ptr[s->ld.color] += 3;
+ }
+ ++raw;
+ }
+ }
+
+ DBG (5, "fix_line_distance_se: got line: %d; color: "
+ "%d\n", lines[s->ld.color], s->ld.color);
+ }
+ --lines[s->ld.color];
+ }
+ else
+ {
+ /* At least one component missing, so save this line. */
+ memcpy (s->ld.buf[s->ld.color] + s->ld.saved[s->ld.color]
+ * bpc, raw, bpc);
+ DBG (5, "fix_line_distance_se: saved line %d; color %d\n",
+ s->ld.saved[s->ld.color], s->ld.color);
+ ++(s->ld.saved[s->ld.color]);
+ raw += bpc;
+ }
+ }
+ else
+ {
+ if (!preview)
+ raw += bpc;
+ DBG (5, "fix_line_distance_se: ignored line; color: %d\n",
+ s->ld.color);
+ }
+
+ if (raw >= raw_end)
+ {
+ /* Reduce num_lines if we encounter excess lines. */
+ if (num_lines > s->ld.ld_line)
+ num_lines = s->ld.ld_line;
+ s->ld.ld_line -= num_lines;
+
+ if (++s->ld.color > 2)
+ s->ld.color = 0;
+ return num_lines;
+ }
+ }
+ if (++s->ld.color > 2)
+ s->ld.color = 0;
+ }
+}
+
+
+/* For Pro models. Not really a linedistance correction (they don't need one)
+ only enlarging x-res here */
+static void
+fix_line_distance_pro (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
+ SANE_Byte * raw, SANE_Byte * out)
+{
+ SANE_Byte *out_addr, *in_addr;
+ SANE_Int res, half_res, y, x_out, x_in;
+
+
+ res = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
+
+ DBG (5, "fix_line_distance_pro: res=%d; halfres=%d; num_lines=%d; bpl=%d\n",
+ res, half_res, num_lines, bpl);
+
+ if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
+ {
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
+ {
+ /*12 bit, need to enlarge x-resolution */
+ DBG (5, "fix_line_distance_pro: res > half_res --> need to "
+ "enlarge x\n");
+ if (little_endian ())
+ for (y = 0; y < num_lines; y++)
+ {
+ for (x_out = 0; x_out < s->params.pixels_per_line; x_out++)
+ {
+ x_in = x_out * bpl / s->params.bytes_per_line / 2;
+ x_in *= 2;
+ out_addr = out + y * s->params.bytes_per_line + x_out * 6;
+ in_addr = raw + y * bpl + x_in * 6;
+ *(out_addr) = *(in_addr) << 4;
+ *(out_addr + 1) = (*(in_addr) >> 4) +
+ (*(in_addr + 1) << 4);
+ *(out_addr + 2) = *(in_addr + 2) << 4;
+ *(out_addr + 3) = (*(in_addr + 2) >> 4) +
+ (*(in_addr + 3) << 4);
+ *(out_addr + 4) = *(in_addr + 4) << 4;
+ *(out_addr + 5) = (*(in_addr + 4) >> 4) +
+ (*(in_addr + 5) << 4);
+ }
+ }
+ else /* big endian */
+ for (y = 0; y < num_lines; y++)
+ {
+ for (x_out = 0; x_out < s->params.pixels_per_line; x_out++)
+ {
+ x_in = x_out * bpl / s->params.bytes_per_line / 2;
+ out_addr = out + y * s->params.bytes_per_line + x_out * 6;
+ in_addr = raw + y * bpl + x_in * 6;
+ *(out_addr) = (*(in_addr) >> 4) + (*(in_addr + 1) << 4);
+ *(out_addr + 1) = *(in_addr) << 4;
+ *(out_addr + 2) = (*(in_addr + 2) >> 4) +
+ (*(in_addr + 3) << 4);
+ *(out_addr + 3) = *(in_addr + 2) << 4;
+ *(out_addr + 4) = (*(in_addr + 4) >> 4) +
+ (*(in_addr + 5) << 4);
+ *(out_addr + 5) = *(in_addr + 4) << 4;
+ }
+ }
+ }
+ else /* 12 bit, no need to enlarge x */
+ {
+ SANE_Word pixel;
+
+ if (little_endian ())
+ for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++)
+ {
+ *(out + pixel * 2) = *(raw + pixel * 2) << 4;
+ *(out + pixel * 2 + 1) = (*(raw + pixel * 2) >> 4) +
+ (*(raw + pixel * 2 + 1) << 4);
+ }
+ else /* big endian */
+ for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++)
+ {
+ *(out + pixel * 2) = (*(raw + pixel * 2) >> 4) +
+ (*(raw + pixel * 2 + 1) << 4);
+ *(out + pixel * 2 + 1) = *(raw + pixel * 2) << 4;
+ }
+
+ }
+ }
+ else /* 8 bit */
+ {
+ /* need to enlarge x-resolution? */
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
+ {
+ DBG (5, "fix_line_distance_pro: res > half_res --> need to "
+ "enlarge x\n");
+
+ for (y = 0; y < num_lines; y++)
+ {
+ for (x_out = 0; x_out < s->params.pixels_per_line; x_out++)
+ {
+ x_in = x_out * bpl / s->params.bytes_per_line;
+ out_addr = out + y * s->params.bytes_per_line + x_out * 3;
+ in_addr = raw + y * bpl + x_in * 3;
+ *(out_addr) = *(in_addr);
+ *(out_addr + 1) = *(in_addr + 1);
+ *(out_addr + 2) = *(in_addr + 2);
+ }
+ }
+ }
+ else
+ memcpy (out, raw, num_lines * bpl);
+ }
+ return;
+}
+
+/* For MFS-08000SP, MFS-06000SP. MFC-08000CZ, MFC-06000CZ */
+static void
+fix_line_distance_normal (Mustek_Scanner * s, SANE_Int num_lines,
+ SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out)
+{
+ SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
+ SANE_Int index[3]; /* index of the next output line for color C */
+ SANE_Int i, color;
+
+ /* Initialize the indices with the line distances that were returned
+ by the CCD linedistance command or set manually (option
+ linedistance-fix). We want to skip data for the first OFFSET
+ rounds, so we initialize the indices to the negative of this
+ offset. */
+
+ DBG (5, "fix_line_distance_normal: %d lines, %d bpl\n", num_lines, bpl);
+
+ for (color = 0; color < 3; ++color)
+ index[color] = -s->ld.dist[color];
+
+ while (1)
+ {
+ for (i = 0; i < 3; ++i)
+ {
+ color = color_seq[i];
+ if (index[color] < 0)
+ ++index[color];
+ else if (index[color] < num_lines)
+ {
+ s->ld.quant[color] += s->ld.peak_res;
+ if (s->ld.quant[color] > s->ld.max_value)
+ {
+ s->ld.quant[color] -= s->ld.max_value;
+ out_ptr = out + index[color] * bpl + color;
+ out_end = out_ptr + bpl;
+ while (out_ptr != out_end)
+ {
+ *out_ptr = *raw++;
+ out_ptr += 3;
+ }
+ ++index[color];
+ if (raw >= raw_end)
+ return;
+ }
+ }
+ }
+ }
+}
+
+/* Paragon series I + II. */
+static SANE_Int
+fix_line_distance_block (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
+ SANE_Byte * raw, SANE_Byte * out,
+ SANE_Int num_lines_total)
+{
+ SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
+ SANE_Int c, num_saved_lines, line, max_index, min_index;
+
+ if (!s->ld.buf[0])
+ {
+ DBG (5, "fix_line_distance_block: allocating temp buffer of %d*%d "
+ "bytes\n", MAX_LINE_DIST, bpl);
+ s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl);
+ if (!s->ld.buf[0])
+ {
+ DBG (1, "fix_line_distance_block: failed to malloc temporary "
+ "buffer\n");
+ return 0;
+ }
+ }
+ DBG (5, "fix_line_distance_block: s->ld.index = {%d, %d, %d}, "
+ "s->ld.lmod3 = %d\n", s->ld.index[0], s->ld.index[1], s->ld.index[2],
+ s->ld.lmod3);
+ DBG (5, "fix_line_distance_block: s->ld.quant = {%d, %d, %d}, "
+ "s->ld.max_value = %d\n", s->ld.quant[0], s->ld.quant[1],
+ s->ld.quant[2], s->ld.max_value);
+ DBG (5,
+ "fix_line_distance_block: s->ld.peak_res = %d, s->ld.ld_line = %d\n",
+ s->ld.peak_res, s->ld.ld_line);
+
+ /* search maximum and minimum index */
+ max_index = MAX (s->ld.index[0], MAX (s->ld.index[1], s->ld.index[2]));
+ min_index = MIN (s->ld.index[0], MIN (s->ld.index[1], s->ld.index[2]));
+ num_saved_lines = max_index - min_index;
+ if (s->ld.index[0] == 0)
+ num_saved_lines = 0;
+ /* restore the previously saved lines: */
+ memcpy (out, s->ld.buf[0], num_saved_lines * bpl);
+ DBG (5, "fix_line_distance_block: copied %d lines from "
+ "ld.buf to buffer (max=%d, min=%d)\n", num_saved_lines, max_index,
+ min_index);
+ while (1)
+ {
+ if (++s->ld.lmod3 >= 3)
+ s->ld.lmod3 = 0;
+
+ c = color_seq[s->ld.lmod3];
+ if (s->ld.index[c] < 0)
+ ++s->ld.index[c];
+ else if (s->ld.index[c] < num_lines_total)
+ {
+ s->ld.quant[c] += s->ld.peak_res;
+ if (s->ld.quant[c] > s->ld.max_value)
+ {
+ s->ld.quant[c] -= s->ld.max_value;
+ line = s->ld.index[c]++ - s->ld.ld_line;
+ out_ptr = out + line * bpl + c;
+ out_end = out_ptr + bpl;
+ while (out_ptr != out_end)
+ {
+ *out_ptr = *raw++;
+ out_ptr += 3;
+ }
+ DBG (5, "fix_line_distance_block: copied line %d (color %d)\n",
+ line + s->ld.ld_line, c);
+
+ max_index = MAX (s->ld.index[0],
+ MAX (s->ld.index[1], s->ld.index[2]));
+ min_index = MIN (s->ld.index[0],
+ MIN (s->ld.index[1], s->ld.index[2]));
+ if ((raw >= raw_end) || ((min_index >= num_lines_total)))
+ {
+ DBG (5, "fix_line_distance_block: got num_lines: %d\n",
+ num_lines);
+ num_lines = min_index - s->ld.ld_line;
+ if (num_lines < 0)
+ num_lines = 0;
+ if ((s->total_lines + num_lines) > s->params.lines)
+ num_lines = s->params.lines - s->total_lines;
+ s->total_lines += num_lines;
+
+ /* copy away the lines with at least one missing
+ color component, so that we can interleave them
+ with new scan data on the next call */
+ num_saved_lines = max_index - min_index;
+
+ DBG (5, "fix_line_distance_block: num_saved_lines = %d; "
+ "num_lines = %d; bpl = %d\n", num_saved_lines,
+ num_lines, bpl);
+
+ memcpy (s->ld.buf[0], out + num_lines * bpl,
+ num_saved_lines * bpl);
+
+ DBG (5, "fix_line_distance_block: copied %d lines to "
+ "ld.buf\n", num_saved_lines);
+
+ /* notice the number of lines we processed */
+ s->ld.ld_line = min_index;
+ if (s->ld.ld_line < 0)
+ s->ld.ld_line = 0;
+
+ DBG (4, "fix_line_distance_block: lmod3=%d, "
+ "index=(%d,%d,%d), line = %d, lines = %d\n",
+ s->ld.lmod3,
+ s->ld.index[0], s->ld.index[1], s->ld.index[2],
+ s->ld.ld_line, num_lines);
+ /* return number of complete (r+g+b) lines */
+ return num_lines;
+ }
+ }
+ }
+ }
+}
+
+/* For MFS-1200SP 1.00 and others */
+/* No LD correction necessary, just shuffle around data */
+static SANE_Int
+fix_line_distance_none (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
+ SANE_Byte * raw, SANE_Byte * out)
+{
+ SANE_Byte *red_ptr, *grn_ptr, *blu_ptr, *ptr, *ptr_end;
+ SANE_Word y;
+
+ ptr = out;
+ red_ptr = raw;
+
+ DBG (5, "fix_line_distance_none: no ld correction necessary (%d lines)\n",
+ num_lines);
+
+ s->ld.ld_line += num_lines;
+
+ if (s->ld.ld_line > s->params.lines)
+ num_lines -= (s->ld.ld_line - s->params.lines);
+ if (num_lines < 0)
+ num_lines = 0;
+
+ DBG (5, "fix_line_distance_none: using %d lines (ld_line = %d, "
+ "s->params.lines = %d)\n", num_lines, s->ld.ld_line, s->params.lines);
+
+ for (y = 0; y < num_lines; ++y)
+ {
+ grn_ptr = red_ptr + bpl / 3;
+ blu_ptr = grn_ptr + bpl / 3;
+ ptr_end = red_ptr + bpl;
+
+ while (blu_ptr != ptr_end)
+ {
+ *ptr++ = *red_ptr++;
+ *ptr++ = *grn_ptr++;
+ *ptr++ = *blu_ptr++;
+ }
+ red_ptr = ptr_end;
+ }
+ return num_lines;
+}
+
+
+static SANE_Status
+init_options (Mustek_Scanner * s)
+{
+ SANE_Int i, j, gammasize;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = "";
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ if (s->hw->flags & MUSTEK_FLAG_SE)
+ {
+ s->opt[OPT_MODE].size = max_string_size (mode_list_se);
+ s->opt[OPT_MODE].constraint.string_list = mode_list_se;
+ s->val[OPT_MODE].s = strdup (mode_list_se[1]);
+ if (!s->val[OPT_MODE].s)
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ s->opt[OPT_MODE].size = max_string_size (mode_list_paragon);
+ s->opt[OPT_MODE].constraint.string_list = mode_list_paragon;
+ s->val[OPT_MODE].s = strdup (mode_list_paragon[2]);
+ if (!s->val[OPT_MODE].s)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* fast gray mode (pro models) */
+ s->opt[OPT_FAST_GRAY_MODE].name = "fast-gray-mode";
+ s->opt[OPT_FAST_GRAY_MODE].title = SANE_I18N ("Fast gray mode");
+ s->opt[OPT_FAST_GRAY_MODE].desc = SANE_I18N ("Scan in fast gray mode "
+ "(lower quality).");
+ s->opt[OPT_FAST_GRAY_MODE].type = SANE_TYPE_BOOL;
+ s->val[OPT_FAST_GRAY_MODE].w = SANE_FALSE;
+ s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE;
+ if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+ /* Only Pro models support fast gray mode */
+ s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w = MAX (SANE_FIX (72), s->hw->dpi_range.min);
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_STRING;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].size = max_string_size (bit_depth_list_pro);
+ s->opt[OPT_BIT_DEPTH].constraint.string_list = bit_depth_list_pro;
+ s->val[OPT_BIT_DEPTH].s = strdup (bit_depth_list_pro[0]);
+ if (!s->val[OPT_BIT_DEPTH].s)
+ return SANE_STATUS_NO_MEM;
+
+ /* speed */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ s->opt[OPT_SPEED].type = SANE_TYPE_STRING;
+ s->opt[OPT_SPEED].size = max_string_size (speed_list);
+ s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SPEED].constraint.string_list = speed_list;
+ s->val[OPT_SPEED].s = strdup (speed_list[4]);
+ if (!s->val[OPT_SPEED].s)
+ return SANE_STATUS_NO_MEM;
+ if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ /* Speed only supported by 3-pass scanners */
+ s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+
+ if ((s->hw->flags & MUSTEK_FLAG_SE) || (s->hw->flags & MUSTEK_FLAG_N)
+ || (s->hw->flags & MUSTEK_FLAG_TA))
+ {
+ s->opt[OPT_SOURCE].size = max_string_size (ta_source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = ta_source_list;
+ s->val[OPT_SOURCE].s = strdup (ta_source_list[0]);
+ if (!s->val[OPT_SOURCE].s)
+ return SANE_STATUS_NO_MEM;
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_ADF)
+ {
+ s->opt[OPT_SOURCE].size = max_string_size (adf_source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = adf_source_list;
+ s->val[OPT_SOURCE].s = strdup (adf_source_list[0]);
+ if (!s->val[OPT_SOURCE].s)
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].s = strdup (source_list[0]);
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ if (!s->val[OPT_SOURCE].s)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* fast preview */
+ s->opt[OPT_FAST_PREVIEW].name = "fast-preview";
+ s->opt[OPT_FAST_PREVIEW].title = SANE_I18N ("Fast preview");
+ s->opt[OPT_FAST_PREVIEW].desc = SANE_I18N ("Request that all previews are "
+ "done in the fastest (low-quality) mode. This may be a non-color "
+ "mode or a low resolution mode.");
+ s->opt[OPT_FAST_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_FAST_PREVIEW].w = SANE_FALSE;
+
+ /* lamp off time*/
+ s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time";
+ s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time (minutes)");
+ s->opt[OPT_LAMP_OFF_TIME].desc = SANE_I18N ("Set the time (in minutes) after "
+ "which the lamp is shut off.");
+ s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT;
+ if (strcmp (s->hw->sane.model, "1200 A3 PRO") != 0)
+ s->opt[OPT_LAMP_OFF_TIME].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_LAMP_OFF_TIME].constraint.range = &u8_range;
+ s->val[OPT_LAMP_OFF_TIME].w = 60;
+
+ /* shut lamp off */
+ s->opt[OPT_LAMP_OFF_BUTTON].name = "lamp-off";
+ s->opt[OPT_LAMP_OFF_BUTTON].title = SANE_I18N ("Turn lamp off");
+ s->opt[OPT_LAMP_OFF_BUTTON].desc = SANE_I18N ("Turns the lamp off immediately.");
+ s->opt[OPT_LAMP_OFF_BUTTON].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_LAMP_OFF_BUTTON].cap = SANE_CAP_SOFT_SELECT;
+ if (strcmp (s->hw->sane.model, "1200 A3 PRO") != 0)
+ s->opt[OPT_LAMP_OFF_BUTTON].cap |= SANE_CAP_INACTIVE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = s->hw->x_range.min;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = s->hw->y_range.min;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ if (!s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ /* 1-pass scanners don't support brightness in multibit mode */
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* brightness red */
+ s->opt[OPT_BRIGHTNESS_R].name = "brightness-r";
+ s->opt[OPT_BRIGHTNESS_R].title = SANE_I18N ("Red brightness");
+ s->opt[OPT_BRIGHTNESS_R].desc = SANE_I18N ("Controls the brightness of "
+ "the red channel of the "
+ "acquired image.");
+ s->opt[OPT_BRIGHTNESS_R].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS_R].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS_R].constraint.range = &percentage_range;
+ s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BRIGHTNESS_R].w = 0;
+
+ /* brightness green */
+ s->opt[OPT_BRIGHTNESS_G].name = "brightness-g";
+ s->opt[OPT_BRIGHTNESS_G].title = SANE_I18N ("Green brightness");
+ s->opt[OPT_BRIGHTNESS_G].desc = SANE_I18N ("Controls the brightness of "
+ "the green channel of the "
+ "acquired image.");
+ s->opt[OPT_BRIGHTNESS_G].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS_G].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS_G].constraint.range = &percentage_range;
+ s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BRIGHTNESS_G].w = 0;
+
+ /* brightness blue */
+ s->opt[OPT_BRIGHTNESS_B].name = "brightness-b";
+ s->opt[OPT_BRIGHTNESS_B].title = SANE_I18N ("Blue brightness");
+ s->opt[OPT_BRIGHTNESS_B].desc = SANE_I18N ("Controls the brightness of "
+ "the blue channel of the "
+ "acquired image.");
+ s->opt[OPT_BRIGHTNESS_B].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS_B].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS_B].constraint.range = &percentage_range;
+ s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BRIGHTNESS_B].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ /* 1-pass scanners don't support contrast in multibit mode */
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* contrast red */
+ s->opt[OPT_CONTRAST_R].name = "contrast-r";
+ s->opt[OPT_CONTRAST_R].title = SANE_I18N ("Contrast red channel");
+ s->opt[OPT_CONTRAST_R].desc = SANE_I18N ("Controls the contrast of "
+ "the red channel of the "
+ "acquired image.");
+ s->opt[OPT_CONTRAST_R].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST_R].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST_R].constraint.range = &percentage_range;
+ s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CONTRAST_R].w = 0;
+
+ /* contrast green */
+ s->opt[OPT_CONTRAST_G].name = "contrast-g";
+ s->opt[OPT_CONTRAST_G].title = SANE_I18N ("Contrast green channel");
+ s->opt[OPT_CONTRAST_G].desc = SANE_I18N ("Controls the contrast of "
+ "the green channel of the "
+ "acquired image.");
+ s->opt[OPT_CONTRAST_G].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST_G].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST_G].constraint.range = &percentage_range;
+ s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CONTRAST_G].w = 0;
+
+ /* contrast blue */
+ s->opt[OPT_CONTRAST_B].name = "contrast-b";
+ s->opt[OPT_CONTRAST_B].title = SANE_I18N ("Contrast blue channel");
+ s->opt[OPT_CONTRAST_B].desc = SANE_I18N ("Controls the contrast of "
+ "the blue channel of the "
+ "acquired image.");
+ s->opt[OPT_CONTRAST_B].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST_B].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST_B].constraint.range = &percentage_range;
+ s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CONTRAST_B].w = 0;
+
+ /* gamma */
+ gammasize = 256;
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < gammasize; ++j)
+ s->gamma_table[i][j] = j;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+
+ /* quality calibration */
+ s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].desc = SANE_DESC_QUALITY_CAL;
+ s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL;
+ if (s->hw->flags & MUSTEK_FLAG_PRO)
+ s->val[OPT_QUALITY_CAL].w = SANE_TRUE;
+ else
+ s->val[OPT_QUALITY_CAL].w = SANE_FALSE;
+ s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE;
+ if ((s->hw->flags & MUSTEK_FLAG_PRO)
+ || (s->hw->flags & MUSTEK_FLAG_SE_PLUS))
+ {
+ /* Only Pro and SE Plus models support calibration */
+ s->opt[OPT_QUALITY_CAL].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ /* halftone dimension */
+ s->opt[OPT_HALFTONE_DIMENSION].name = SANE_NAME_HALFTONE_DIMENSION;
+ s->opt[OPT_HALFTONE_DIMENSION].title = SANE_TITLE_HALFTONE_DIMENSION;
+ s->opt[OPT_HALFTONE_DIMENSION].desc = SANE_DESC_HALFTONE_DIMENSION;
+ s->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_DIMENSION].size = max_string_size (halftone_list);
+ s->opt[OPT_HALFTONE_DIMENSION].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_DIMENSION].constraint.string_list = halftone_list;
+ s->val[OPT_HALFTONE_DIMENSION].s = strdup (halftone_list[0]);
+ if (!s->val[OPT_HALFTONE_DIMENSION].s)
+ return SANE_STATUS_NO_MEM;
+ s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE;
+
+ /* halftone pattern */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_HALFTONE_PATTERN].constraint.range = &u8_range;
+ s->val[OPT_HALFTONE_PATTERN].wa = s->halftone_pattern;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* The following three functions execute as a child process. The
+ reason for using a subprocess is that some (most?) generic SCSI
+ interfaces block a SCSI request until it has completed. With a
+ subprocess, we can let it block waiting for the request to finish
+ while the main process can go about to do more important things
+ (such as recognizing when the user presses a cancel button).
+
+ WARNING: Since this is executed as a subprocess, it's NOT possible
+ to update any of the variables in the main process (in particular
+ the scanner state cannot be updated).
+
+ NOTE: At least for Linux, it seems that we could get rid of the
+ subprocess. Linux v2.0 does seem to allow select() on SCSI
+ descriptors. */
+
+static void
+output_data (Mustek_Scanner * s, FILE * fp,
+ SANE_Byte * data, SANE_Int lines_per_buffer, SANE_Int bpl,
+ SANE_Byte * extra)
+{
+ SANE_Byte *ptr, *ptr_end;
+ SANE_Int y, num_lines;
+
+ DBG (5, "output_data: data=%p, lpb=%d, bpl=%d, extra=%p\n",
+ data, lines_per_buffer, bpl, extra);
+
+ /* convert to pixel-interleaved format: */
+ if ((s->mode & MUSTEK_MODE_COLOR)
+ && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ num_lines = lines_per_buffer;
+
+ /* need to correct for distance between r/g/b sensors: */
+ if (s->hw->flags & MUSTEK_FLAG_PRO)
+ fix_line_distance_pro (s, num_lines, bpl, data, extra);
+ else if (s->hw->flags & MUSTEK_FLAG_SE)
+ {
+ num_lines = fix_line_distance_se (s, num_lines, bpl, data, extra);
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ if (s->hw->flags & MUSTEK_FLAG_LD_N2)
+ num_lines = fix_line_distance_n_2 (s, num_lines, bpl, data,
+ extra);
+ else
+ num_lines = fix_line_distance_n_1 (s, num_lines, bpl, data,
+ extra);
+ }
+ else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK)
+ && (s->ld.max_value != 0))
+ {
+ if (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ num_lines = fix_line_distance_block (s, num_lines, bpl, data,
+ extra, s->hw->lines);
+ else
+ num_lines = fix_line_distance_block (s, num_lines, bpl, data,
+ extra,
+ s->hw->lines_per_block);
+ }
+ else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE)
+ && (s->ld.max_value != 0))
+ fix_line_distance_normal (s, num_lines, bpl, data, extra);
+ else
+ num_lines = fix_line_distance_none (s, num_lines, bpl, data, extra);
+
+ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
+ {
+ /* need to revert line direction */
+ SANE_Int line_number;
+ SANE_Int byte_number;
+
+ DBG (5, "output_data: ADF found, mirroring lines\n");
+ for (line_number = 0; line_number < num_lines; line_number++)
+ {
+ for (byte_number = bpl - 3; byte_number >= 0; byte_number -= 3)
+ {
+ fputc (*(extra + line_number * bpl + byte_number), fp);
+ fputc (*(extra + line_number * bpl + byte_number + 1), fp);
+ fputc (*(extra + line_number * bpl + byte_number + 2), fp);
+ }
+ }
+ }
+ else
+ fwrite (extra, num_lines, s->params.bytes_per_line, fp);
+ }
+ else
+ {
+ DBG (5, "output_data: write %d lpb; %d bpl\n", lines_per_buffer, bpl);
+ /* Scale x-resolution above 1/2 of the maximum resolution for
+ SE and Pro scanners */
+ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) &&
+ (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2)))
+ {
+ SANE_Int x;
+ SANE_Int half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
+ SANE_Int res = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ SANE_Int res_counter;
+ SANE_Int enlarged_x;
+
+ DBG (5, "output_data: enlarge lines from %d bpl to %d bpl\n",
+ s->hw->bpl, s->params.bytes_per_line);
+
+ for (y = 0; y < lines_per_buffer; y++)
+ {
+ SANE_Byte byte = 0;
+
+ x = 0;
+ res_counter = 0;
+ enlarged_x = 0;
+
+ while (enlarged_x < s->params.pixels_per_line)
+ {
+ if (s->mode & MUSTEK_MODE_GRAY)
+ {
+ fputc (*(data + y * bpl + x), fp);
+ res_counter += half_res;
+ if (res_counter >= half_res)
+ {
+ res_counter -= res;
+ x++;
+ }
+ enlarged_x++;
+ }
+ else /* lineart */
+ {
+ /* need to invert image because of funny SANE 1-bit image
+ polarity */
+ if (*(data + x / 8 + y * bpl) & (1 << (7 - (x % 8))))
+ byte |= 1 << (7 - (enlarged_x % 8));
+
+ if ((enlarged_x % 8) == 7)
+ {
+ fputc (~byte, fp); /* invert image */
+ byte = 0;
+ }
+ res_counter += half_res;
+ if (res_counter >= half_res)
+ {
+ res_counter -= res;
+ x++;
+ }
+ enlarged_x++;
+ }
+ }
+ }
+ }
+ else /* lineart, gray or halftone (nothing to scale) */
+ {
+ if ((s->mode & MUSTEK_MODE_LINEART)
+ || (s->mode & MUSTEK_MODE_HALFTONE))
+ {
+ /* need to invert image because of funny SANE 1-bit image
+ polarity */
+ ptr = data;
+ ptr_end = ptr + lines_per_buffer * bpl;
+
+ if (strcmp (s->val[OPT_SOURCE].s,
+ "Automatic Document Feeder") == 0)
+ {
+ while (ptr != ptr_end)
+ {
+ (*ptr) = ~(*ptr);
+ ptr++;
+ /* need to revert bit direction */
+ *ptr = ((*ptr & 0x80) >> 7) + ((*ptr & 0x40) >> 5)
+ + ((*ptr & 0x20) >> 3) + ((*ptr & 0x10) >> 1)
+ + ((*ptr & 0x08) << 1) + ((*ptr & 0x04) << 3)
+ + ((*ptr & 0x02) << 5) + ((*ptr & 0x01) << 7);
+ }
+ }
+ else
+ while (ptr != ptr_end)
+ {
+ (*ptr) = ~(*ptr);
+ ptr++;
+ }
+ }
+ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
+ {
+ /* need to revert line direction */
+ SANE_Int line_number;
+ SANE_Int byte_number;
+
+ DBG (5, "output_data: ADF found, mirroring lines\n");
+ for (line_number = 0; line_number < lines_per_buffer;
+ line_number++)
+ {
+ for (byte_number = bpl - 1; byte_number >= 0; byte_number--)
+ {
+ fputc (*(data + line_number * bpl + byte_number), fp);
+ }
+ }
+ }
+ else
+ {
+ fwrite (data, lines_per_buffer, bpl, fp);
+ }
+ }
+ }
+ DBG (5, "output_data: end\n");
+}
+
+static RETSIGTYPE
+sigterm_handler (int signal)
+{
+ DBG (4,
+ "sigterm_handler: started, signal is %d, starting sanei_scsi_req_flush_all()\n",
+ signal);
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+ DBG (4,
+ "sigterm_handler: sanei_scsi_req_flush_all() finisheshed, _exiting()\n");
+ _exit (SANE_STATUS_GOOD);
+}
+
+
+static SANE_Int
+reader_process (void *data)
+{
+ Mustek_Scanner *s = (Mustek_Scanner *) data;
+ SANE_Int lines_per_buffer, bpl;
+ SANE_Byte *extra = 0, *ptr;
+ sigset_t sigterm_set;
+ struct SIGACTION act;
+ SANE_Status status;
+ FILE *fp;
+ int fd = s->reader_fds;
+ SANE_Int buffernumber = 0;
+ SANE_Int buffer_count, max_buffers;
+ struct
+ {
+ void *id; /* scsi queue id */
+ SANE_Byte *data; /* data buffer */
+ SANE_Byte *command; /* command buffer */
+ SANE_Int lines; /* # lines in buffer */
+ size_t num_read; /* # of bytes read (return value) */
+ SANE_Int bank; /* needed by SE models */
+ SANE_Bool ready; /* ready to send to application? */
+ SANE_Bool finished; /* block is finished */
+ }
+ bstat[2];
+
+ DBG (3, "reader_process: started\n");
+ if (sanei_thread_is_forked ())
+ {
+ DBG (4, "reader_process: using fork ()\n");
+ close (s->pipe);
+ s->pipe = -1;
+ }
+ else
+ {
+ DBG (4, "reader_process: using threads\n");
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ /* ignore SIGTERM while writing SCSI commands */
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ /* call our sigterm handler to clean up ongoing SCSI requests */
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = sigterm_handler;
+ sigaction (SIGTERM, &act, 0);
+ }
+
+ if (disable_double_buffering)
+ DBG (3, "reader_process: disable_double_buffering is set, this may be "
+ "slow\n");
+
+ fp = fdopen (fd, "w");
+ if (!fp)
+ return SANE_STATUS_IO_ERROR;
+
+ s->total_lines = 0;
+ bpl = s->hw->bpl;
+
+ /* buffer size is scanner dependant */
+ lines_per_buffer = s->hw->buffer_size / bpl / 2;
+
+ if (strip_height > 0.0)
+ {
+ SANE_Int max_lines;
+ double dpi;
+
+ dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ max_lines = (int) (strip_height * dpi + 0.5);
+
+ if (lines_per_buffer > max_lines)
+ {
+ DBG (2, "reader_process: limiting strip height to %g inches "
+ "(%d lines)\n", strip_height, max_lines);
+ lines_per_buffer = max_lines;
+ }
+ }
+
+ if (!lines_per_buffer)
+ {
+ DBG (1, "reader_process: bpl (%d) > SCSI buffer size / 2 (%d)\n",
+ bpl, s->hw->buffer_size / 2);
+ return SANE_STATUS_NO_MEM; /* resolution is too high */
+ }
+
+ DBG (4, "reader_process: %d lines per buffer, %d bytes per line, "
+ "%d bytes per buffer\n", lines_per_buffer, bpl,
+ lines_per_buffer * bpl);
+
+ bstat[0].data = malloc (2 * lines_per_buffer * (long) bpl);
+ if (!bstat[0].data)
+ {
+ DBG (1, "reader_process: failed to malloc %ld bytes for data buffer\n",
+ lines_per_buffer * (long) bpl);
+ return SANE_STATUS_NO_MEM;
+ }
+ bstat[1].data = bstat[0].data + lines_per_buffer * (long) bpl;
+
+ bstat[0].command = malloc (2 * 10);
+ if (!bstat[0].command)
+ {
+ DBG (1,
+ "reader_process: failed to malloc %d bytes for command buffer\n",
+ 2 * 10);
+ return SANE_STATUS_NO_MEM;
+ }
+ bstat[1].command = bstat[0].command + 10;
+
+ /* Touch all pages of the buffer to fool the memory management. */
+ ptr = bstat[0].data + 2 * lines_per_buffer * (long) bpl - 1;
+ while (ptr >= bstat[0].data)
+ {
+ *ptr = 0x00;
+ ptr -= 256;
+ }
+
+ if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ /* get temporary buffer for line-distance correction and/or bit
+ expansion. For some scanners more space is needed because the
+ data must be read in as single big block (cut up into pieces
+ of lines_per_buffer). This requires that the line distance
+ correction continues on every call exactly where it stopped
+ if the image shall be reconstructed without any stripes. */
+
+ extra = malloc ((lines_per_buffer + MAX_LINE_DIST)
+ * (long) s->params.bytes_per_line);
+ if (!extra)
+ {
+ DBG (1, "reader_process: failed to malloc extra buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ if (s->hw->flags & MUSTEK_FLAG_N)
+ {
+ /* reacquire port access rights (lost because of fork()): */
+ sanei_ab306_get_io_privilege (s->fd);
+ }
+
+ if ((s->hw->flags & MUSTEK_FLAG_N) || (s->hw->flags & MUSTEK_FLAG_LD_BLOCK))
+ {
+ /* reset counter of line number for line-dictance correction */
+ s->ld.ld_line = 0;
+ }
+
+ max_buffers = s->hw->max_block_buffer_size / (lines_per_buffer * bpl);
+ if (max_buffers < 1)
+ {
+ DBG (1, "reader_process: buffersize > blocksize!\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (4, "reader_process: limiting block read to %d buffers (%d lines)\n",
+ max_buffers, MIN (s->hw->lines, (max_buffers * lines_per_buffer)));
+
+ while (s->line < s->hw->lines)
+ {
+ s->hw->lines_per_block =
+ MIN (s->hw->lines - s->line, (max_buffers * lines_per_buffer));
+ status = dev_block_read_start (s, s->hw->lines_per_block);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ for (buffernumber = 0; buffernumber < 2; buffernumber++)
+ {
+ bstat[buffernumber].ready = SANE_FALSE;
+ bstat[buffernumber].finished = SANE_FALSE;
+ }
+ buffer_count = 0;
+ buffernumber = 0;
+
+ while (1)
+ {
+ /* omit reading first two buffers (not yet ready) */
+ if (bstat[buffernumber].ready == SANE_TRUE)
+ {
+ DBG (4, "reader_process: buffer %d: waiting for request to be "
+ "ready\n", buffernumber + 1);
+ status = dev_req_wait (bstat[buffernumber].id);
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (4, "reader_process: buffer %d is ready, wanted %d, "
+ "got %ld bytes\n", buffernumber + 1,
+ bstat[buffernumber].lines * bpl,
+ (long int) bstat[buffernumber].num_read);
+ }
+ else
+ {
+ DBG (1, "reader_process: failed to read data, status: %s, "
+ "buffer: %d\n", sane_strstatus (status),
+ buffernumber + 1);
+ if (status == SANE_STATUS_NO_MEM)
+ {
+ DBG (1,
+ "Probably the size of the kernel SCSI buffer is "
+ "too small for the\n selected buffersize "
+ "in mustek.conf. Either decrease "
+ "buffersize in\n mustek.conf to e.g. 32, "
+ "increase SG_BIG_BUF in kernel to 130560, "
+ "or\n use SANE_SG_BUFFERSIZE variable. "
+ "See man sane-scsi and README for\n "
+ "details.\n");
+ }
+ return status;
+ }
+
+ DBG (4, "reader_process: buffer %d: sending %ld bytes to "
+ "output_data\n", buffernumber + 1,
+ (long int) bstat[buffernumber].num_read);
+ output_data (s, fp, bstat[buffernumber].data,
+ bstat[buffernumber].lines, bpl, extra);
+ if (bstat[buffernumber].finished)
+ break; /* everything written; exit loop */
+ }
+ if (disable_double_buffering)
+ {
+ /* Enter only one buffer at once */
+ if (buffernumber == 1)
+ buffernumber = 0;
+ else
+ buffernumber = 1;
+ }
+
+ /* enter read requests only if data left */
+ if ((s->line < s->hw->lines) && (buffer_count < max_buffers))
+ {
+ if (s->line + lines_per_buffer >= s->hw->lines)
+ {
+ /* do the last few lines: */
+ bstat[buffernumber].lines = s->hw->lines - s->line;
+ bstat[buffernumber].bank = 0x01;
+ bstat[buffernumber].finished = SANE_TRUE;
+ }
+ else
+ {
+ bstat[buffernumber].lines = lines_per_buffer;
+ bstat[buffernumber].bank = 0x00;
+ }
+
+ if ((buffer_count + 1) >= max_buffers)
+ bstat[buffernumber].finished = SANE_TRUE;
+
+ s->line += bstat[buffernumber].lines;
+ bstat[buffernumber].ready = SANE_TRUE;
+
+ buffer_count++;
+
+ DBG (4,
+ "reader_process: buffer %d: entering read request for %d "
+ "bytes (buffer %d)\n", buffernumber + 1,
+ bstat[buffernumber].lines * bpl, buffer_count);
+ sigprocmask (SIG_BLOCK, &sigterm_set, 0);
+ status = dev_read_req_enter (s, bstat[buffernumber].data,
+ bstat[buffernumber].lines, bpl,
+ &bstat[buffernumber].num_read,
+ &bstat[buffernumber].id,
+ bstat[buffernumber].bank,
+ bstat[buffernumber].command);
+ sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
+
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (5, "reader_process: buffer %d: entered (line %d of %d,"
+ " buffer %d)\n", buffernumber + 1, s->line,
+ s->hw->lines, buffer_count);
+ }
+ else
+ {
+ DBG (1, "reader_process: buffer %d: failed to enter read "
+ "request, status: %s\n", buffernumber + 1,
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ if (!disable_double_buffering)
+ {
+ if (buffernumber == 1)
+ buffernumber = 0;
+ else
+ buffernumber = 1;
+ }
+ /* This is said to fix the scanner hangs that reportedly show on
+ some MFS-12000SP scanners. */
+ if (s->mode == 0 && (s->hw->flags & MUSTEK_FLAG_LINEART_FIX))
+ usleep (200000);
+ }
+ }
+
+ fclose (fp);
+ free (bstat[0].data);
+ if (s->ld.buf[0])
+ free (s->ld.buf[0]);
+ s->ld.buf[0] = NULL;
+ if (extra)
+ free (extra);
+ close (fd);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ Mustek_Device *dev;
+
+ attach (devname, &dev, SANE_FALSE);
+ if (dev)
+ {
+ /* Keep track of newly attached devices so we can set options as
+ necessary. */
+ if (new_dev_len >= new_dev_alloced)
+ {
+ new_dev_alloced += 4;
+ if (new_dev)
+ new_dev =
+ realloc (new_dev, new_dev_alloced * sizeof (new_dev[0]));
+ else
+ new_dev = malloc (new_dev_alloced * sizeof (new_dev[0]));
+ if (!new_dev)
+ {
+ DBG (1, "attach_one_device: out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ new_dev[new_dev_len++] = dev;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**************************************************************************/
+/* SANE API calls */
+/**************************************************************************/
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Char line[PATH_MAX], *word, *end;
+ SANE_String_Const cp;
+ SANE_Int linenumber;
+ FILE *fp;
+
+ DBG_INIT ();
+
+ sanei_thread_init ();
+
+#ifdef DBG_LEVEL
+ debug_level = DBG_LEVEL;
+#else
+ debug_level = 0;
+#endif
+
+ DBG (2, "SANE mustek backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ DBG (5, "sane_init: using sanei_scsi_open_extended\n");
+#else
+ DBG (5, "sane_init: using sanei_scsi_open with buffer size = %d bytes\n",
+ sanei_scsi_max_request_size);
+#endif
+
+ num_devices = 0;
+ force_wait = SANE_FALSE;
+ disable_double_buffering = SANE_FALSE;
+ first_dev = 0;
+ first_handle = 0;
+ devlist = 0;
+ new_dev = 0;
+ new_dev_len = 0;
+ new_dev_alloced = 0;
+
+ fp = sanei_config_open (MUSTEK_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ DBG (3, "sane_init: couldn't find config file (%s), trying "
+ "/dev/scanner directly\n", MUSTEK_CONFIG_FILE);
+ attach ("/dev/scanner", 0, SANE_FALSE);
+ return SANE_STATUS_GOOD;
+ }
+ linenumber = 0;
+ DBG (4, "sane_init: reading config file `%s'\n", MUSTEK_CONFIG_FILE);
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ word = 0;
+ linenumber++;
+
+ cp = sanei_config_get_string (line, &word);
+ if (!word || cp == line)
+ {
+ DBG (5, "sane_init: config file line %d: ignoring empty line\n",
+ linenumber);
+ if (word)
+ free (word);
+ continue;
+ }
+ if (word[0] == '#')
+ {
+ DBG (5, "sane_init: config file line %d: ignoring comment line\n",
+ linenumber);
+ free (word);
+ continue;
+ }
+
+ if (strcmp (word, "option") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ {
+ DBG (1,
+ "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ if (strcmp (word, "strip-height") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ {
+ DBG (1,
+ "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ errno = 0;
+ strip_height = strtod (word, &end);
+ if (end == word)
+ {
+ DBG (3, "sane-init: config file line %d: strip-height "
+ "must have a parameter; using 1 inch\n", linenumber);
+ strip_height = 1.0;
+ }
+ if (errno)
+ {
+ DBG (3, "sane-init: config file line %d: strip-height `%s' "
+ "is invalid (%s); using 1 inch\n", linenumber,
+ word, strerror (errno));
+ strip_height = 1.0;
+ }
+ else
+ {
+ if (strip_height < 0.1)
+ strip_height = 0.1;
+ DBG (3, "sane_init: config file line %d: strip-height set "
+ "to %g inches\n", linenumber, strip_height);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "force-wait") == 0)
+ {
+ DBG (3, "sane_init: config file line %d: enabling force-wait\n",
+ linenumber);
+ force_wait = SANE_TRUE;
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "disable-double-buffering") == 0)
+ {
+ DBG (3, "sane_init: config file line %d: disabling "
+ "double-buffering\n", linenumber);
+ disable_double_buffering = SANE_TRUE;
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "legal-size") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ /* Check for 12000 LS, no way to find out automatically */
+ if (strcmp (new_dev[new_dev_len - 1]->sane.model,
+ "ScanExpress 12000SP") == 0)
+ {
+ new_dev[new_dev_len - 1]->x_range.max =
+ SANE_FIX (220.0);
+ new_dev[new_dev_len - 1]->y_range.max =
+ SANE_FIX (360.0);
+ new_dev[new_dev_len - 1]->sane.model =
+ "Paragon 1200 LS";
+ DBG (3,
+ "sane_init: config file line %d: enabling "
+ "legal-size for %s\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "legal-size ignored, device %s is not a "
+ "Paragon 1200 LS\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "legal-size ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "linedistance-fix") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LD_FIX;
+ DBG (3, "sane_init: config file line %d: enabling "
+ "linedistance-fix for %s\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "linedistance-fix ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "disable-backtracking") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_NO_BACKTRACK;
+ DBG (3, "sane_init: config file line %d: disabling "
+ "backtracking for %s\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "disable-backtracking ignored, was set before any "
+ "device name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "lineart-fix") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LINEART_FIX;
+ DBG (3, "sane_init: config file line %d: enabling "
+ "lineart-fix for %s\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "lineart-fix ignored, was set before any device name\n",
+ linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "buffersize") == 0)
+ {
+ long buffer_size;
+
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ {
+ DBG (1,
+ "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ errno = 0;
+ buffer_size = strtol (word, &end, 0);
+
+ if (end == word)
+ {
+ DBG (3, "sane-init: config file line %d: buffersize must "
+ "have a parameter; using default (%d kb)\n",
+ linenumber, new_dev[new_dev_len - 1]->max_buffer_size);
+ }
+ if (errno)
+ {
+ DBG (3, "sane-init: config file line %d: buffersize `%s' "
+ "is invalid (%s); using default (%d kb)\n", linenumber,
+ word, strerror (errno),
+ new_dev[new_dev_len - 1]->max_buffer_size);
+ }
+ else
+ {
+ if (new_dev_len > 0)
+ {
+ if (buffer_size < 32.0)
+ buffer_size = 32.0;
+ new_dev[new_dev_len - 1]->max_buffer_size =
+ buffer_size * 1024;
+ DBG (3,
+ "sane_init: config file line %d: buffersize set "
+ "to %ld kb for %s\n", linenumber, buffer_size,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "buffersize ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "blocksize") == 0)
+ {
+ long block_size;
+
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ {
+ DBG (1,
+ "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ errno = 0;
+ block_size = strtol (word, &end, 0);
+
+ if (end == word)
+ {
+ DBG (3, "sane-init: config file line %d:: blocksize must "
+ "have a parameter; using default (1 GB)\n",
+ linenumber);
+ }
+ if (errno)
+ {
+ DBG (3, "sane-init: config file line %d: blocksize `%s' "
+ "is invalid (%s); using default (1 GB)\n", linenumber,
+ word, strerror (errno));
+ }
+ else
+ {
+ if (new_dev_len > 0)
+ {
+ if (block_size < 256.0)
+ block_size = 256.0;
+ new_dev[new_dev_len - 1]->max_block_buffer_size =
+ block_size * 1024;
+ DBG (3, "sane_init: config file line %d: blocksize set "
+ "to %ld kb for %s\n", linenumber, block_size,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "blocksize ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: ignoring unknown "
+ "option `%s'\n", linenumber, word);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+ else
+ {
+ new_dev_len = 0;
+ DBG (4, "sane_init: config file line %d: trying to attach `%s'\n",
+ linenumber, line);
+ sanei_config_attach_matching_devices (line, attach_one_device);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+
+ if (new_dev_alloced > 0)
+ {
+ new_dev_len = new_dev_alloced = 0;
+ free (new_dev);
+ }
+ fclose (fp);
+ DBG (5, "sane_init: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Mustek_Device *dev, *next;
+
+ DBG (4, "sane_exit\n");
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->name);
+ free (dev);
+ }
+ if (devlist)
+ free (devlist);
+ devlist = 0;
+ first_dev = 0;
+ sanei_ab306_exit (); /* may have to do some cleanup */
+ mustek_scsi_pp_exit ();
+ DBG (5, "sane_exit: finished\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Mustek_Device *dev;
+ SANE_Int i;
+
+ DBG (4, "sane_get_devices: %d devices %s\n", num_devices,
+ local_only ? "(local only)" : "");
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ DBG (5, "sane_get_devices: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Mustek_Device *dev;
+ SANE_Status status;
+ Mustek_Scanner *s;
+
+ if (!devicename)
+ {
+ DBG (1, "sane_open: devicename is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!handle)
+ {
+ DBG (1, "sane_open: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (4, "sane_open: devicename=%s\n", devicename);
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach (devicename, &dev, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ /* empty devicname -> use first device */
+ dev = first_dev;
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->fd = -1;
+ s->pipe = -1;
+ s->hw = dev;
+ s->ld.ld_line = 0;
+ s->halftone_pattern = malloc (8 * 8 * sizeof (SANE_Int));
+ if (!s->halftone_pattern)
+ return SANE_STATUS_NO_MEM;
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+ DBG (4, "sane_open: finished (handle=%p)\n", (void *) s);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Mustek_Scanner *prev, *s;
+
+ DBG (4, "sane_close: handle=%p\n", handle);
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ do_stop (handle);
+
+ if (s->ld.buf[0])
+ free (s->ld.buf[0]);
+ if (s->val[OPT_MODE].s)
+ free (s->val[OPT_MODE].s);
+ if (s->val[OPT_BIT_DEPTH].s)
+ free (s->val[OPT_BIT_DEPTH].s);
+ if (s->val[OPT_SPEED].s)
+ free (s->val[OPT_SPEED].s);
+ if (s->val[OPT_SOURCE].s)
+ free (s->val[OPT_SOURCE].s);
+ if (s->val[OPT_HALFTONE_DIMENSION].s)
+ free (s->val[OPT_HALFTONE_DIMENSION].s);
+ if (s->halftone_pattern)
+ free (s->halftone_pattern);
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+ free (handle);
+ handle = 0;
+ DBG (5, "sane_close: finished\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Mustek_Scanner *s = handle;
+
+ if (((unsigned) option >= NUM_OPTIONS) || (option < 0))
+ {
+ DBG (4, "sane_get_option_descriptor: option %d >= NUM_OPTIONS or < 0\n",
+ option);
+ return 0;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_get_option_descriptor: handle is null!\n");
+ return 0;
+ }
+ if (s->opt[option].name && s->opt[option].name[0] != 0)
+ DBG (5, "sane_get_option_descriptor for option %s (%sactive%s)\n",
+ s->opt[option].name,
+ s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "",
+ s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : "");
+ else
+ DBG (5, "sane_get_option_descriptor for option \"%s\" (%sactive%s)\n",
+ s->opt[option].title,
+ s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "",
+ s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : "");
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Mustek_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word w, cap;
+
+ if (((unsigned) option >= NUM_OPTIONS) || (option < 0))
+ {
+ DBG (4, "sane_control_option: option %d < 0 or >= NUM_OPTIONS\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_control_option: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->opt[option].type != SANE_TYPE_BUTTON && !val)
+ {
+ DBG (1, "sane_control_option: val is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->opt[option].name && s->opt[option].name[0] != 0)
+ DBG (5, "sane_control_option (%s option %s)\n",
+ action == SANE_ACTION_GET_VALUE ? "get" :
+ (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"),
+ s->opt[option].name);
+ else
+ DBG (5, "sane_control_option (%s option \"%s\")\n",
+ action == SANE_ACTION_GET_VALUE ? "get" :
+ (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"),
+ s->opt[option].title);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ {
+ DBG (4, "sane_control_option: don't use while scanning (option %s)\n",
+ s->opt[option].name);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (4, "sane_control_option: option %s is inactive\n",
+ s->opt[option].name);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_PREVIEW:
+ case OPT_FAST_PREVIEW:
+ case OPT_RESOLUTION:
+ case OPT_FAST_GRAY_MODE:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_BRIGHTNESS_R:
+ case OPT_BRIGHTNESS_G:
+ case OPT_BRIGHTNESS_B:
+ case OPT_CONTRAST:
+ case OPT_CONTRAST_R:
+ case OPT_CONTRAST_G:
+ case OPT_CONTRAST_B:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_QUALITY_CAL:
+ case OPT_LAMP_OFF_TIME:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_HALFTONE_PATTERN:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_SPEED:
+ case OPT_SOURCE:
+ case OPT_MODE:
+ case OPT_BIT_DEPTH:
+ case OPT_HALFTONE_DIMENSION:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (4, "sane_control_option: option %s is not setable\n",
+ s->opt[option].name);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = constrain_value (s, option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (4, "sane_control_option: constrain_value error (option %s)\n",
+ s->opt[option].name);
+ return status;
+ }
+
+ switch (option)
+ {
+ case OPT_LAMP_OFF_BUTTON:
+ {
+ SANE_Int old_time = lamp_off_time;
+ SANE_Status status;
+
+ status = dev_open (s->hw->sane.name, s, sense_handler);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ lamp_off_time = 0;
+ set_window_pro (s);
+ lamp_off_time = old_time;
+ dev_close (s);
+ return SANE_STATUS_GOOD;
+ }
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_PREVIEW:
+ case OPT_FAST_PREVIEW:
+ case OPT_FAST_GRAY_MODE:
+ case OPT_BRIGHTNESS:
+ case OPT_BRIGHTNESS_R:
+ case OPT_BRIGHTNESS_G:
+ case OPT_BRIGHTNESS_B:
+ case OPT_CONTRAST:
+ case OPT_CONTRAST_R:
+ case OPT_CONTRAST_G:
+ case OPT_CONTRAST_B:
+ case OPT_QUALITY_CAL:
+ case OPT_LAMP_OFF_TIME:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free word-array options: */
+ case OPT_HALFTONE_PATTERN:
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free single-string options: */
+ case OPT_SPEED:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free string list options: */
+ case OPT_BIT_DEPTH:
+ {
+ SANE_Char *old_val = s->val[option].s;
+
+ if (old_val)
+ {
+ if (strcmp (old_val, val) == 0)
+ return SANE_STATUS_GOOD; /* no change */
+ free (old_val);
+ }
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+ return SANE_STATUS_GOOD;
+ }
+
+ /* options with side-effects: */
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ if (w)
+ {
+ SANE_String_Const mode = s->val[OPT_MODE].s;
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ && (s->hw->flags & MUSTEK_FLAG_PRO))
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ {
+ SANE_Char *old_val = s->val[option].s;
+ SANE_Int halftoning, binary;
+
+ if (old_val)
+ {
+ if (strcmp (old_val, val) == 0)
+ return SANE_STATUS_GOOD; /* no change */
+ free (old_val);
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ halftoning = strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE) == 0;
+ binary = (halftoning || strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0);
+
+ if (binary)
+ {
+ /* enable brightness/contrast for when in a binary mode */
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ /* The SE and paragon models support only threshold
+ in lineart */
+ if (!(s->hw->flags & MUSTEK_FLAG_SE)
+ && !(s->hw->flags & MUSTEK_FLAG_PRO))
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+
+ if (halftoning)
+ {
+ s->opt[OPT_HALFTONE_DIMENSION].cap &= ~SANE_CAP_INACTIVE;
+ encode_halftone (s);
+ if (s->custom_halftone_pattern)
+ {
+ s->opt[OPT_HALFTONE_PATTERN].cap
+ &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ else
+ {
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ s->opt[OPT_BRIGHTNESS_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS_B].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE;
+ else
+ s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE;
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ else
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_SE_PLUS)
+ {
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ else
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_HALFTONE_DIMENSION:
+ /* halftone pattern dimension affects halftone pattern option: */
+ {
+ if (strcmp (s->val[option].s, (SANE_String) val) == 0)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+ encode_halftone (s);
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ if (s->custom_halftone_pattern)
+ {
+ s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ /* BUG: The SANE standard does nor allow to change the option
+ size at run time */
+ s->opt[OPT_HALFTONE_PATTERN].size =
+ (s->halftone_pattern_type & 0x0f) * sizeof (SANE_Word);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SOURCE:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+
+ if (strcmp (val, "Transparency Adapter") == 0)
+ {
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_trans_range;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_trans_range;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_trans_range;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_trans_range;
+ }
+ else
+ {
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG (4, "sane_control_option: unknown action for option %s\n",
+ s->opt[option].name);
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Mustek_Scanner *s = handle;
+ SANE_String_Const mode;
+
+ if (!s)
+ {
+ DBG (1, "sane_get_parameters: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!s->scanning)
+ {
+ double width, height, dpi;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
+ height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
+ dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (dpi > 0.0 && width > 0.0 && height > 0.0)
+ {
+ double dots_per_mm = dpi / MM_PER_INCH;
+
+ s->params.pixels_per_line = width * dots_per_mm;
+ s->params.lines = height * dots_per_mm;
+ }
+ encode_halftone (s);
+ mode = s->val[OPT_MODE].s;
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0 || strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ }
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ else
+ {
+ /* it's one of the color modes... */
+
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ s->params.format = SANE_FRAME_RED + s->pass;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ else /* 1-pass */
+ {
+ if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
+ {
+ s->params.bytes_per_line = 6 * s->params.pixels_per_line;
+ s->params.depth = 16;
+ }
+ else
+ {
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ s->params.format = SANE_FRAME_RGB;
+ }
+ }
+ }
+ else if ((s->mode & MUSTEK_MODE_COLOR)
+ && (s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ s->params.format = SANE_FRAME_RED + s->pass;
+ s->params.last_frame = (s->params.format != SANE_FRAME_RED
+ && s->params.format != SANE_FRAME_GREEN);
+ if (params)
+ *params = s->params;
+ DBG (4, "sane_get_parameters: frame = %d; last_frame = %s; depth = %d\n",
+ s->params.format, s->params.last_frame ? "true" : "false",
+ s->params.depth);
+ DBG (4, "sane_get_parameters: lines = %d; ppl = %d; bpl = %d\n",
+ s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Mustek_Scanner *s = handle;
+ SANE_Status status;
+ int fds[2];
+ struct SIGACTION act;
+
+ if (!s)
+ {
+ DBG (1, "sane_start: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "sane_start\n");
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* Check for inconsistencies */
+
+ if (s->val[OPT_TL_X].w > s->val[OPT_BR_X].w)
+ {
+ DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
+ "-- aborting\n",
+ s->opt[OPT_TL_X].title, SANE_UNFIX (s->val[OPT_TL_X].w),
+ s->opt[OPT_BR_X].title, SANE_UNFIX (s->val[OPT_BR_X].w));
+ return SANE_STATUS_INVAL;
+ }
+ if (s->val[OPT_TL_Y].w > s->val[OPT_BR_Y].w)
+ {
+ DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
+ "-- aborting\n",
+ s->opt[OPT_TL_Y].title, SANE_UNFIX (s->val[OPT_TL_Y].w),
+ s->opt[OPT_BR_Y].title, SANE_UNFIX (s->val[OPT_BR_Y].w));
+ return SANE_STATUS_INVAL;
+ }
+
+ s->total_bytes = 0;
+
+ if (s->fd < 0)
+ {
+ /* this is the first (and maybe only) pass... */
+ SANE_String_Const mode;
+ struct timeval start;
+
+ /* save start time */
+ gettimeofday (&start, 0);
+ s->start_time = start.tv_sec;
+ /* translate options into s->mode for convenient access: */
+ mode = s->val[OPT_MODE].s;
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->mode = MUSTEK_MODE_LINEART;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ s->mode = MUSTEK_MODE_HALFTONE;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->mode = MUSTEK_MODE_GRAY;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ s->mode = MUSTEK_MODE_COLOR;
+
+ /* scanner dependant specials */
+ s->one_pass_color_scan = SANE_FALSE;
+ if ((s->mode & MUSTEK_MODE_COLOR)
+ && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ s->one_pass_color_scan = SANE_TRUE;
+ }
+
+ s->resolution_code = encode_resolution (s);
+
+ if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w)
+ {
+ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ {
+ if (s->mode & MUSTEK_MODE_COLOR)
+ {
+ /* Force gray-scale mode when previewing. */
+ s->mode = MUSTEK_MODE_GRAY;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.last_frame = SANE_TRUE;
+ }
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_SE)
+ {
+ /* use 36 dpi color in any case */
+ s->mode = MUSTEK_MODE_COLOR;
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 8;
+ s->one_pass_color_scan = SANE_TRUE;
+ s->resolution_code = 36;
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
+ {
+ /* use 36 dpi */
+ s->resolution_code = 36;
+ }
+ else if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+ /* use 30 dpi color mode */
+ s->mode = MUSTEK_MODE_COLOR;
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 8;
+ s->one_pass_color_scan = SANE_TRUE;
+ s->resolution_code = 30;
+ }
+ DBG (4, "sane_start: use fast preview (res=%d dpi)\n",
+ s->resolution_code);
+ }
+
+ status = dev_open (s->hw->sane.name, s, sense_handler);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ status = dev_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: wait_ready() failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ if (!(s->hw->flags & MUSTEK_FLAG_SCSI_PP))
+ {
+ /* SCSI-over-parallel port doesn't seem to like being inquired here */
+ status = inquiry (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: inquiry command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+ }
+
+ if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) &&
+ !(s->hw->flags & MUSTEK_FLAG_ADF_READY))
+ {
+ DBG (2, "sane_start: automatic document feeder is out of documents\n");
+ status = SANE_STATUS_NO_DOCS;
+ goto stop_scanner_and_return;
+ }
+
+ if (s->hw->flags & MUSTEK_FLAG_SE)
+ {
+ status = set_window_se (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: set window command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+
+ dev_wait_ready (s);
+
+ status = get_window (s, &s->params.bytes_per_line,
+ &s->params.lines, &s->params.pixels_per_line);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: get window command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = calibration_se (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = send_gamma_table_se (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+ }
+
+ else if (s->hw->flags & MUSTEK_FLAG_PRO)
+ {
+
+ status = dev_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = set_window_pro (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+
+ status = adf_and_backtrack (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = mode_select_pro (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = scsi_sense_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = calibration_pro (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = send_gamma_table (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = get_image_status (s, &s->params.bytes_per_line,
+ &s->params.lines);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = scsi_sense_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+ }
+
+ else /* Paragon series */
+ {
+ status = area_and_windows (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: set scan area command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = adf_and_backtrack (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ if (s->one_pass_color_scan)
+ {
+ status = mode_select_paragon (s, MUSTEK_CODE_RED);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = mode_select_paragon (s, MUSTEK_CODE_GREEN);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = mode_select_paragon (s, MUSTEK_CODE_BLUE);
+ }
+ else
+ status = mode_select_paragon (s, MUSTEK_CODE_GRAY);
+
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+
+ status = send_gamma_table (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ if (!(s->hw->flags & MUSTEK_FLAG_SCSI_PP))
+ {
+ /* This second gamma table download upsets the SCSI-over-parallel models */
+ status = send_gamma_table (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+ }
+
+ s->ld.max_value = 0;
+ if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
+ {
+ status = line_distance (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+ }
+
+ status = get_image_status (s, &s->params.bytes_per_line,
+ &s->params.lines);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
+ && (s->hw->flags & MUSTEK_FLAG_PARAGON_2))
+ {
+ status = paragon_2_get_adf_status (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+ }
+ }
+
+ s->params.pixels_per_line = s->params.bytes_per_line;
+ if (s->one_pass_color_scan)
+ {
+ if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
+ s->params.pixels_per_line /= 6;
+ else
+ s->params.pixels_per_line /= 3;
+ }
+ else if ((s->mode & MUSTEK_MODE_LINEART)
+ || (s->mode & MUSTEK_MODE_HALFTONE))
+ s->params.pixels_per_line *= 8;
+
+ s->line = 0;
+
+ /* don't call any SIGTERM or SIGCHLD handlers
+ this is to stop xsane and other frontends from calling
+ its quit handlers */
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+ sigaction (SIGCHLD, &act, 0);
+
+ if (pipe (fds) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ s->reader_fds = fds[1];
+
+ /* create reader routine as new process or thread */
+ s->reader_pid = sanei_thread_begin (reader_process, (void *) s);
+
+ if (s->reader_pid == -1)
+ {
+ DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ close (s->reader_fds);
+ s->reader_fds = -1;
+ }
+
+ s->pipe = fds[0];
+
+ return SANE_STATUS_GOOD;
+
+stop_scanner_and_return:
+ do_stop (s);
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Mustek_Scanner *s = handle;
+ SANE_Status status;
+ ssize_t ntotal;
+ ssize_t nread;
+
+
+ if (!s)
+ {
+ DBG (1, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!buf)
+ {
+ DBG (1, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (1, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sane_read\n");
+ *len = 0;
+ ntotal = 0;
+
+ if (s->cancelled)
+ {
+ DBG (4, "sane_read: scan was cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (!s->scanning)
+ {
+ DBG (3, "sane_read: must call sane_start before sane_read\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ while (*len < max_len)
+ {
+ nread = read (s->pipe, buf + *len, max_len - *len);
+
+ if (s->cancelled)
+ {
+ DBG (4, "sane_read: scan was cancelled\n");
+ *len = 0;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ if (*len == 0)
+ DBG (5, "sane_read: no more data at the moment--try again\n");
+ else
+ DBG (5, "sane_read: read buffer of %d bytes "
+ "(%d bytes total)\n", *len, s->total_bytes);
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG (1, "sane_read: IO error\n");
+ do_stop (s);
+ *len = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len += nread;
+ s->total_bytes += nread;
+
+ if (nread == 0)
+ {
+ if (*len == 0)
+ {
+ if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)
+ || !(s->mode & MUSTEK_MODE_COLOR) || ++s->pass >= 3)
+ {
+ DBG (5, "sane_read: pipe was closed ... calling do_stop\n");
+ status = do_stop (s);
+ if (status != SANE_STATUS_CANCELLED
+ && status != SANE_STATUS_GOOD)
+ return status; /* something went wrong */
+ }
+ else /* 3pass color first or second pass */
+ {
+ DBG (5,
+ "sane_read: pipe was closed ... finishing pass %d\n",
+ s->pass);
+ }
+
+ return do_eof (s);
+ }
+ else
+ {
+ DBG (5, "sane_read: read last buffer of %d bytes "
+ "(%d bytes total)\n", *len, s->total_bytes);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ }
+ DBG (5, "sane_read: read full buffer of %d bytes (%d total bytes)\n",
+ *len, s->total_bytes);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Mustek_Scanner *s = handle;
+
+ if (!s)
+ {
+ DBG (1, "sane_cancel: handle is null!\n");
+ return;
+ }
+
+ DBG (4, "sane_cancel\n");
+ if (s->scanning)
+ {
+ s->cancelled = SANE_TRUE;
+ do_stop (handle);
+ }
+ DBG (5, "sane_cancel: finished\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Mustek_Scanner *s = handle;
+
+ if (!s)
+ {
+ DBG (1, "sane_set_io_mode: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "sane_set_io_mode: %s\n",
+ non_blocking ? "non-blocking" : "blocking");
+
+ if (!s->scanning)
+ {
+ DBG (1, "sane_set_io_mode: call sane_start before sane_set_io_mode");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ DBG (1, "sane_set_io_mode: can't set io mode");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Mustek_Scanner *s = handle;
+
+ if (!s)
+ {
+ DBG (1, "sane_get_select_fd: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!fd)
+ {
+ DBG (1, "sane_get_select_fd: fd is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (4, "sane_get_select_fd\n");
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ *fd = s->pipe;
+ return SANE_STATUS_GOOD;
+}
+
+#include "mustek_scsi_pp.c"
diff --git a/backend/mustek.conf.in b/backend/mustek.conf.in
new file mode 100644
index 0000000..3233fd8
--- /dev/null
+++ b/backend/mustek.conf.in
@@ -0,0 +1,41 @@
+# See sane-mustek(5) for documentation.
+
+#--------------------------- Global options ---------------------------------
+#option strip-height 1 # some SCSI adapters need this; scanning may
+ # be faster without this option
+#option force-wait # wait for scanner to be ready (only necessary
+ # when scanner freezes)
+#option disable-double-buffering # try this if you have SCSI trouble
+
+#-------------------------- SCSI scanners -----------------------------------
+scsi MUSTEK * Scanner
+# option linedistance-fix # stripes may go away in color mode
+# option buffersize 1024 # set non standard buffer size (in kb)
+# option blocksize 2048 # set non standard block size (in kb)
+# option lineart-fix # lineart may be faster with this option off.
+# option disable-backtracking # faster, but may produce stripes
+
+scsi SCANNER
+# option linedistance-fix # stripes may go away in color mode
+# option buffersize 1024 # set non standard buffer size (in kb)
+# option blocksize 2048 # set non standard block size (in kb)
+# option lineart-fix # lineart may be faster with this option off.
+# option disable-backtracking # faster, but may produce stripes
+
+/dev/scanner
+# option linedistance-fix # stripes may go away in color mode
+# option buffersize 1024 # set non standard buffer size (in kb)
+# option blocksize 2048 # set non standard block size (in kb)
+# option lineart-fix # lineart may be faster with this option off.
+# option disable-backtracking # faster, but may produce stripes
+
+#-------------------------- 600 II N ----------------------------------------
+#0x2eb
+ # For the 600 II N try one of 0x26b, 0x2ab,
+ # 0x2eb, 0x22b, 0x32b, 0x36b, 0x3ab, 0x3eb.
+# option linedistance-fix # only neccessary with firmware 2.x
+
+#-------------------------- 600 II EP ---------------------------------------
+#parport0
+ # parport0, parport1, ...,
+ # or: 0x378(=lpt1), 0x278(=lpt2), 0x3bc(=lpt3)
diff --git a/backend/mustek.h b/backend/mustek.h
new file mode 100644
index 0000000..5532af5
--- /dev/null
+++ b/backend/mustek.h
@@ -0,0 +1,303 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang, 1998 Andreas Bolsch for
+ extension to ScanExpress models version 0.5,
+ 2000 - 2005 Henning Meier-Geinitz.
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek and some Trust flatbed
+ scanners with SCSI or proprietary interface. */
+
+#ifndef mustek_h
+#define mustek_h
+
+#include "../include/sane/config.h"
+#include <sys/types.h>
+
+/* Some constants */
+#define INQ_LEN 0x60 /* Length of SCSI inquiry */
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+#define MUSTEK_CONFIG_FILE "mustek.conf"
+
+#define MAX_WAITING_TIME 60 /* How long to wait for scanner to become ready */
+#define MAX_LINE_DIST 40 /* Extra lines needed for LD correction */
+
+/* Flag values */
+/* Scanner types */
+#define MUSTEK_FLAG_THREE_PASS (1 << 0) /* three pass scanner */
+#define MUSTEK_FLAG_PARAGON_1 (1 << 1) /* Paragon series I scanner */
+#define MUSTEK_FLAG_PARAGON_2 (1 << 2) /* Paragon series II(A4) scanner */
+#define MUSTEK_FLAG_SE (1 << 3) /* ScanExpress scanner */
+#define MUSTEK_FLAG_SE_PLUS (1 << 4) /* ScanExpress Plus scanner */
+#define MUSTEK_FLAG_PRO (1 << 5) /* Professional series scanner */
+#define MUSTEK_FLAG_N (1 << 6) /* N-type scanner (non SCSI) */
+#define MUSTEK_FLAG_SCSI_PP (1 << 22) /* SCSI over parallel (e.g. 600 II EP) */
+/* Additional equipment */
+#define MUSTEK_FLAG_ADF (1 << 7) /* automatic document feeder */
+#define MUSTEK_FLAG_ADF_READY (1 << 8) /* paper present */
+#define MUSTEK_FLAG_TA (1 << 9) /* transparency adapter */
+/* Line-distance correction */
+#define MUSTEK_FLAG_LD_NONE (1 << 10) /* no line-distance corr */
+#define MUSTEK_FLAG_LD_BLOCK (1 << 11) /* blockwise LD corr */
+#define MUSTEK_FLAG_LD_N1 (1 << 12) /* LD corr for N-type v1 */
+#define MUSTEK_FLAG_LD_N2 (1 << 13) /* LD corr for N-type v2 */
+/* Manual fixes */
+#define MUSTEK_FLAG_LD_FIX (1 << 14) /* need line-distance fix? */
+#define MUSTEK_FLAG_LINEART_FIX (1 << 15) /* lineart fix/hack */
+#define MUSTEK_FLAG_USE_EIGHTS (1 << 16) /* use 1/8" lengths */
+#define MUSTEK_FLAG_FORCE_GAMMA (1 << 17) /* force gamma table upload */
+#define MUSTEK_FLAG_ENLARGE_X (1 << 18) /* need to enlarge x-res */
+#define MUSTEK_FLAG_COVER_SENSOR (1 << 19) /* scanner can detect open cover */
+#define MUSTEK_FLAG_USE_BLOCK (1 << 20) /* use blockmode */
+#define MUSTEK_FLAG_LEGAL_SIZE (1 << 21) /* scanner has legal size */
+#define MUSTEK_FLAG_NO_BACKTRACK (1 << 21) /* scanner has legal size */
+
+/* Source values: */
+#define MUSTEK_SOURCE_FLATBED 0
+#define MUSTEK_SOURCE_ADF 1
+#define MUSTEK_SOURCE_TA 2
+
+/* Mode values: */
+#define MUSTEK_MODE_LINEART (1 << 0) /* grayscale 1 bit / pixel */
+#define MUSTEK_MODE_GRAY (1 << 1) /* grayscale 8 bits / pixel */
+#define MUSTEK_MODE_COLOR (1 << 2) /* color 24 bits / pixel */
+#define MUSTEK_MODE_HALFTONE (1 << 3) /* use dithering */
+
+/* Color band codes: */
+#define MUSTEK_CODE_GRAY 0
+#define MUSTEK_CODE_RED 1
+#define MUSTEK_CODE_GREEN 2
+#define MUSTEK_CODE_BLUE 3
+
+/* SCSI commands that the Mustek scanners understand (or not): */
+#define MUSTEK_SCSI_TEST_UNIT_READY 0x00
+#define MUSTEK_SCSI_REQUEST_SENSE 0x03
+#define MUSTEK_SCSI_AREA_AND_WINDOWS 0x04
+#define MUSTEK_SCSI_READ_SCANNED_DATA 0x08
+#define MUSTEK_SCSI_GET_IMAGE_STATUS 0x0f
+#define MUSTEK_SCSI_ADF_AND_BACKTRACK 0x10
+#define MUSTEK_SCSI_CCD_DISTANCE 0x11
+#define MUSTEK_SCSI_INQUIRY 0x12
+#define MUSTEK_SCSI_MODE_SELECT 0x15
+#define MUSTEK_SCSI_START_STOP 0x1b
+#define MUSTEK_SCSI_SET_WINDOW 0x24
+#define MUSTEK_SCSI_GET_WINDOW 0x25
+#define MUSTEK_SCSI_READ_DATA 0x28
+#define MUSTEK_SCSI_SEND_DATA 0x2a
+#define MUSTEK_SCSI_LOOKUP_TABLE 0x55
+
+/* Convenience macros */
+#if defined(MIN)
+#undef MIN
+#endif
+#if defined(MAX)
+#undef MAX
+#endif
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+/* Copy values to memory ('L' = little endian, 'B' = big endian */
+#define STORE16L(cp,v) \
+do { \
+ int value = (v); \
+ \
+ *(cp)++ = (value >> 0) & 0xff; \
+ *(cp)++ = (value >> 8) & 0xff; \
+} while (0)
+#define STORE16B(cp,v) \
+do { \
+ int value = (v); \
+ \
+ *(cp)++ = (value >> 8) & 0xff; \
+ *(cp)++ = (value >> 0) & 0xff; \
+} while (0)
+#define STORE32B(cp,v) \
+do { \
+ long int value = (v); \
+ \
+ *(cp)++ = (value >> 24) & 0xff; \
+ *(cp)++ = (value >> 16) & 0xff; \
+ *(cp)++ = (value >> 8) & 0xff; \
+ *(cp)++ = (value >> 0) & 0xff; \
+} while (0)
+
+/* declarations */
+enum Mustek_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_FAST_GRAY_MODE,
+ OPT_RESOLUTION,
+ OPT_BIT_DEPTH,
+ OPT_SPEED,
+ OPT_SOURCE,
+ OPT_PREVIEW,
+ OPT_FAST_PREVIEW,
+ OPT_LAMP_OFF_TIME,
+ OPT_LAMP_OFF_BUTTON,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_BRIGHTNESS_R,
+ OPT_BRIGHTNESS_G,
+ OPT_BRIGHTNESS_B,
+ OPT_CONTRAST,
+ OPT_CONTRAST_R,
+ OPT_CONTRAST_G,
+ OPT_CONTRAST_B,
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables? */
+ /* The gamma vectors MUST appear in the order gray, red, green,
+ blue. */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_QUALITY_CAL,
+ OPT_HALFTONE_DIMENSION,
+ OPT_HALFTONE_PATTERN,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+typedef struct Mustek_Device
+{
+ struct Mustek_Device *next;
+ SANE_String name;
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ /* scan area when transparency adapter is used: */
+ SANE_Range x_trans_range;
+ SANE_Range y_trans_range;
+ SANE_Word flags;
+ /* length of gamma table, probably always <= 4096 for the SE */
+ SANE_Int gamma_length;
+ /* values actually used by scanner, not necessarily the desired! */
+ SANE_Int bpl, lines;
+ /* what is needed for calibration (ScanExpress and Pro series) */
+ struct
+ {
+ SANE_Int bytes;
+ SANE_Int lines;
+ SANE_Byte *buffer;
+ SANE_Word *line_buffer[3];
+ }
+ cal;
+ /* current and maximum buffer size used by the backend */
+ /* the buffer sent to the scanner is actually half of this size */
+ SANE_Int buffer_size;
+ SANE_Int max_buffer_size;
+ /* maximum size scanned in one block and corresponding lines */
+ SANE_Int max_block_buffer_size;
+ SANE_Int lines_per_block;
+ SANE_Byte *block_buffer;
+
+ /* firmware format: 0 = old, MUSTEK at pos 8; 1 = new, MUSTEK at
+ pos 36 */
+ SANE_Int firmware_format;
+ /* firmware revision system: 0 = old, x.yz; 1 = new, Vxyz */
+ SANE_Int firmware_revision_system;
+}
+Mustek_Device;
+
+typedef struct Mustek_Scanner
+{
+ /* all the state needed to define a scan request: */
+ struct Mustek_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Int gamma_table[4][256];
+ SANE_Int *halftone_pattern;
+ SANE_Bool custom_halftone_pattern;
+ SANE_Int halftone_pattern_type;
+
+ SANE_Bool scanning;
+ SANE_Bool cancelled;
+ SANE_Int pass; /* pass number */
+ SANE_Int line; /* current line number */
+ SANE_Parameters params;
+
+ /* Parsed option values and variables that are valid only during
+ actual scanning: */
+ SANE_Word mode;
+ SANE_Bool one_pass_color_scan;
+ SANE_Int resolution_code;
+ int fd; /* SCSI filedescriptor */
+ SANE_Pid reader_pid; /* process id of reader */
+ int reader_fds; /* OS/2: pipe write handler for reader */
+ int pipe; /* pipe to reader process */
+ long start_time; /* at this time the scan started */
+ SANE_Word total_bytes; /* bytes transmitted by sane_read */
+ SANE_Word total_lines; /* lines transmitted to sane_read pipe */
+
+ /* scanner dependent/low-level state: */
+ Mustek_Device *hw;
+
+ /* line-distance correction related state: */
+ struct
+ {
+ SANE_Int color; /* first color appearing in read data */
+ SANE_Int max_value;
+ SANE_Int peak_res;
+ SANE_Int dist[3]; /* line distance */
+ SANE_Int index[3]; /* index for R/G/B color assignment */
+ SANE_Int quant[3]; /* for resolution correction */
+ SANE_Int saved[3]; /* number of saved color lines */
+ /* these are used for SE, MFS and N line-distance correction: */
+ SANE_Byte *buf[3];
+ /* these are used for N line-distance correction only: */
+ SANE_Int ld_line; /* line # currently processed in
+ ld-correction */
+ SANE_Int lmod3; /* line # modulo 3 */
+ }
+ ld;
+}
+Mustek_Scanner;
+
+#endif /* mustek_h */
diff --git a/backend/mustek_pp.c b/backend/mustek_pp.c
new file mode 100644
index 0000000..cd86bb9
--- /dev/null
+++ b/backend/mustek_pp.c
@@ -0,0 +1,1958 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek PP flatbed scanners. */
+
+#include "../include/sane/config.h"
+
+#if defined(HAVE_STDLIB_H)
+# include <stdlib.h>
+#endif
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#elif defined(HAVE_STRINGS_H)
+# include <strings.h>
+#endif
+#if defined(HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+#include <math.h>
+#include <fcntl.h>
+#include <time.h>
+#if defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#include <sys/wait.h>
+
+#define BACKEND_NAME mustek_pp
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#include "../include/sane/sanei_backend.h"
+
+#include "../include/sane/sanei_config.h"
+#define MUSTEK_PP_CONFIG_FILE "mustek_pp.conf"
+
+#include "../include/sane/sanei_pa4s2.h"
+
+#include "mustek_pp.h"
+#include "mustek_pp_drivers.h"
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* converts millimeter to pixels at a given resolution */
+#define MM_TO_PIXEL(mm, dpi) (((float )mm * 5.0 / 127.0) * (float)dpi)
+ /* and back */
+#define PIXEL_TO_MM(pixel, dpi) (((float )pixel / (float )dpi) * 127.0 / 5.0)
+
+/* if you change the source, please set MUSTEK_PP_STATE to "devel". Do *not*
+ * change the MUSTEK_PP_BUILD. */
+#define MUSTEK_PP_BUILD 13
+#define MUSTEK_PP_STATE "beta"
+
+
+/* auth callback... since basic user authentication is done by saned, this
+ * callback mechanism isn't used */
+SANE_Auth_Callback sane_auth;
+
+/* count of present devices */
+static int num_devices = 0;
+
+/* list of present devices */
+static Mustek_pp_Device *devlist = NULL;
+
+/* temporary array of configuration options used during device attachment */
+static Mustek_pp_config_option *cfgoptions = NULL;
+static int numcfgoptions = 0;
+
+/* list of pointers to the SANE_Device structures of the Mustek_pp_Devices */
+static SANE_Device **devarray = NULL;
+
+/* currently active Handles */
+static Mustek_pp_Handle *first_hndl = NULL;
+
+static SANE_String_Const mustek_pp_modes[4] = {SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL};
+static SANE_Word mustek_pp_modes_size = 10;
+
+static SANE_String_Const mustek_pp_speeds[6] = {"Slowest", "Slower", "Normal", "Faster", "Fastest", NULL};
+static SANE_Word mustek_pp_speeds_size = 8;
+static SANE_Word mustek_pp_depths[5] = {4, 8, 10, 12, 16};
+
+/* prototypes */
+static void free_cfg_options(int *numoptions, Mustek_pp_config_option** options);
+static SANE_Status do_eof(Mustek_pp_Handle *hndl);
+static SANE_Status do_stop(Mustek_pp_Handle *hndl);
+static int reader_process (Mustek_pp_Handle * hndl, int pipe);
+static SANE_Status sane_attach(SANE_String_Const port, SANE_String_Const name,
+ SANE_Int driver, SANE_Int info);
+static void init_options(Mustek_pp_Handle *hndl);
+static void attach_device(SANE_String *driver, SANE_String *name,
+ SANE_String *port, SANE_String *option_ta);
+
+
+/*
+ * Auxiliary function for freeing arrays of configuration options,
+ */
+static void
+free_cfg_options(int *numoptions, Mustek_pp_config_option** options)
+{
+ int i;
+ if (*numoptions)
+ {
+ for (i=0; i<*numoptions; ++i)
+ {
+ free ((*options)[i].name);
+ free ((*options)[i].value);
+ }
+ free (*options);
+ }
+ *options = NULL;
+ *numoptions = 0;
+}
+
+/* do_eof:
+ * closes the pipeline
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * closes the pipe (read-only end)
+ */
+static SANE_Status
+do_eof (Mustek_pp_Handle *hndl)
+{
+ if (hndl->pipe >= 0) {
+
+ close (hndl->pipe);
+ hndl->pipe = -1;
+ }
+
+ return SANE_STATUS_EOF;
+}
+
+/* do_stop:
+ * ends the reader_process and stops the scanner
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * kills the reader process with a SIGTERM and cancels the scanner
+ */
+static SANE_Status
+do_stop(Mustek_pp_Handle *hndl)
+{
+
+ int exit_status;
+
+ do_eof (hndl);
+
+ if (hndl->reader > 0) {
+
+ DBG (3, "do_stop: terminating reader process\n");
+ kill (hndl->reader, SIGTERM);
+
+ while (wait (&exit_status) != hndl->reader);
+
+ DBG ((exit_status == SANE_STATUS_GOOD ? 3 : 1),
+ "do_stop: reader_process terminated with status ``%s''\n",
+ sane_strstatus(exit_status));
+ hndl->reader = 0;
+ hndl->dev->func->stop (hndl);
+
+ return exit_status;
+
+ }
+
+ hndl->dev->func->stop (hndl);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* sigterm_handler:
+ * cancel scanner when receiving a SIGTERM
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * just exit... reader_process takes care that nothing bad will happen
+ *
+ * EDG - Jan 14, 2004:
+ * Make sure that the parport is released again by the child process
+ * under all circumstances, because otherwise the parent process may no
+ * longer be able to claim it (they share the same file descriptor, and
+ * the kernel doesn't release the child's claim because the file
+ * descriptor isn't cleaned up). If that would happen, the lamp may stay
+ * on and may not return to its home position, unless the scanner
+ * frontend is restarted.
+ * (This happens only when sanei_pa4s2 uses libieee1284 AND
+ * libieee1284 goes via /dev/parportX).
+ *
+ */
+static int fd_to_release = 0;
+/*ARGSUSED*/
+static RETSIGTYPE
+sigterm_handler (int signal __UNUSED__)
+{
+ sanei_pa4s2_enable(fd_to_release, SANE_FALSE);
+ _exit (SANE_STATUS_GOOD);
+}
+
+/* reader_process:
+ * receives data from the scanner and stuff it into the pipeline
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * The signal handle for SIGTERM is initialized.
+ *
+ */
+static int
+reader_process (Mustek_pp_Handle * hndl, int pipe)
+{
+ sigset_t sigterm_set;
+ struct SIGACTION act;
+ FILE *fp;
+ SANE_Status status;
+ int line;
+ int size, elem;
+
+ SANE_Byte *buffer;
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ if (!(buffer = malloc (hndl->params.bytes_per_line)))
+ return SANE_STATUS_NO_MEM;
+
+ if (!(fp = fdopen(pipe, "w")))
+ return SANE_STATUS_IO_ERROR;
+
+ fd_to_release = hndl->fd;
+ memset (&act, 0, sizeof(act));
+ act.sa_handler = sigterm_handler;
+ sigaction (SIGTERM, &act, NULL);
+
+ if ((status = hndl->dev->func->start (hndl)) != SANE_STATUS_GOOD)
+ return status;
+
+ size = hndl->params.bytes_per_line;
+ elem = 1;
+
+ for (line=0; line<hndl->params.lines ; line++) {
+
+ sigprocmask (SIG_BLOCK, &sigterm_set, NULL);
+
+ hndl->dev->func->read (hndl, buffer);
+
+ if (getppid() == 1) {
+ /* The parent process has died. Stop the scan (to make
+ sure that the lamp is off and returns home). This is
+ a safety measure to make sure that we don't break
+ the scanner in case the frontend crashes. */
+ DBG (1, "reader_process: front-end died; aborting.\n");
+ hndl->dev->func->stop (hndl);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ sigprocmask (SIG_UNBLOCK, &sigterm_set, NULL);
+
+ fwrite (buffer, size, elem, fp);
+ }
+
+ fclose (fp);
+
+ free (buffer);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* sane_attach:
+ * adds a new entry to the Mustek_pp_Device *devlist list
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * After memory for a new device entry is allocated, the
+ * parameters for the device are determined by a call to
+ * capabilities().
+ *
+ * Afterwards the new device entry is inserted into the
+ * devlist
+ *
+ */
+static SANE_Status
+sane_attach (SANE_String_Const port, SANE_String_Const name, SANE_Int driver, SANE_Int info)
+{
+ Mustek_pp_Device *dev;
+
+ DBG (3, "sane_attach: attaching device ``%s'' to port %s (driver %s v%s by %s)\n",
+ name, port, Mustek_pp_Drivers[driver].driver,
+ Mustek_pp_Drivers[driver].version,
+ Mustek_pp_Drivers[driver].author);
+
+ if ((dev = malloc (sizeof (Mustek_pp_Device))) == NULL) {
+
+ DBG (1, "sane_attach: not enough free memory\n");
+ return SANE_STATUS_NO_MEM;
+
+ }
+
+ memset (dev, 0, sizeof (Mustek_pp_Device));
+
+ memset (&dev->sane, 0, sizeof (SANE_Device));
+
+ dev->func = &Mustek_pp_Drivers[driver];
+
+ dev->sane.name = dev->name = strdup (name);
+ dev->port = strdup (port);
+ dev->info = info; /* Modified by EDG */
+
+ /* Transfer the options parsed from the configuration file */
+ dev->numcfgoptions = numcfgoptions;
+ dev->cfgoptions = cfgoptions;
+ numcfgoptions = 0;
+ cfgoptions = NULL;
+
+ dev->func->capabilities (info, &dev->model, &dev->vendor, &dev->type,
+ &dev->maxres, &dev->minres, &dev->maxhsize, &dev->maxvsize,
+ &dev->caps);
+
+ dev->sane.model = dev->model;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.type = dev->type;
+
+ dev->next = devlist;
+ devlist = dev;
+
+ num_devices++;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* init_options:
+ * Sets up the option descriptors for a device
+ *
+ * ChangeLog:
+ *
+ * Description:
+ */
+static void
+init_options(Mustek_pp_Handle *hndl)
+{
+ int i;
+
+ memset (hndl->opt, 0, sizeof (hndl->opt));
+ memset (hndl->val, 0, sizeof (hndl->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ hndl->opt[i].size = sizeof (SANE_Word);
+ hndl->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ hndl->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ hndl->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ hndl->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ hndl->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ hndl->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ hndl->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+
+ hndl->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ hndl->opt[OPT_MODE_GROUP].desc = "";
+ hndl->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ hndl->opt[OPT_MODE_GROUP].cap = 0;
+ hndl->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ hndl->opt[OPT_MODE_GROUP].size = 0;
+
+ /* scan mode */
+ hndl->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ hndl->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ hndl->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ hndl->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ hndl->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ hndl->opt[OPT_MODE].size = mustek_pp_modes_size;
+ hndl->opt[OPT_MODE].constraint.string_list = mustek_pp_modes;
+ hndl->val[OPT_MODE].s = strdup (mustek_pp_modes[2]);
+
+ /* resolution */
+ hndl->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ hndl->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ hndl->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ hndl->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ hndl->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ hndl->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_RESOLUTION].constraint.range = &hndl->dpi_range;
+ hndl->val[OPT_RESOLUTION].w = SANE_FIX (hndl->dev->minres);
+ hndl->dpi_range.min = SANE_FIX (hndl->dev->minres);
+ hndl->dpi_range.max = SANE_FIX (hndl->dev->maxres);
+ hndl->dpi_range.quant = SANE_FIX (1);
+
+ /* speed */
+ hndl->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ hndl->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ hndl->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ hndl->opt[OPT_SPEED].type = SANE_TYPE_STRING;
+ hndl->opt[OPT_SPEED].size = mustek_pp_speeds_size;
+ hndl->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ hndl->opt[OPT_SPEED].constraint.string_list = mustek_pp_speeds;
+ hndl->val[OPT_SPEED].s = strdup (mustek_pp_speeds[2]);
+
+ if (! (hndl->dev->caps & CAP_SPEED_SELECT))
+ hndl->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
+
+ /* preview */
+ hndl->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ hndl->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ hndl->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ hndl->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ hndl->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* gray preview */
+ hndl->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
+ hndl->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
+ hndl->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
+ hndl->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
+ hndl->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
+
+ /* color dept */
+ hndl->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ hndl->opt[OPT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ hndl->opt[OPT_DEPTH].desc =
+ "Number of bits per sample for color scans, typical values are 8 for truecolor (24bpp)"
+ "up to 16 for far-to-many-color (48bpp).";
+ hndl->opt[OPT_DEPTH].type = SANE_TYPE_INT;
+ hndl->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ hndl->opt[OPT_DEPTH].constraint.word_list = mustek_pp_depths;
+ hndl->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
+ hndl->opt[OPT_DEPTH].size = sizeof(SANE_Word);
+ hndl->val[OPT_DEPTH].w = 8;
+
+ if ( !(hndl->dev->caps & CAP_DEPTH))
+ hndl->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+
+ /* "Geometry" group: */
+
+ hndl->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ hndl->opt[OPT_GEOMETRY_GROUP].desc = "";
+ hndl->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ hndl->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ hndl->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ hndl->opt[OPT_GEOMETRY_GROUP].size = 0;
+
+ /* top-left x */
+ hndl->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ hndl->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ hndl->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ hndl->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ hndl->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ hndl->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_TL_X].constraint.range = &hndl->x_range;
+ hndl->x_range.min = SANE_FIX (0);
+ hndl->x_range.max = SANE_FIX (PIXEL_TO_MM(hndl->dev->maxhsize,hndl->dev->maxres));
+ hndl->x_range.quant = 0;
+ hndl->val[OPT_TL_X].w = hndl->x_range.min;
+
+ /* top-left y */
+ hndl->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ hndl->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ hndl->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ hndl->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ hndl->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ hndl->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_TL_Y].constraint.range = &hndl->y_range;
+ hndl->y_range.min = SANE_FIX(0);
+ hndl->y_range.max = SANE_FIX(PIXEL_TO_MM(hndl->dev->maxvsize,hndl->dev->maxres));
+ hndl->y_range.quant = 0;
+ hndl->val[OPT_TL_Y].w = hndl->y_range.min;
+
+ /* bottom-right x */
+ hndl->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ hndl->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ hndl->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ hndl->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ hndl->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ hndl->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_BR_X].constraint.range = &hndl->x_range;
+ hndl->val[OPT_BR_X].w = hndl->x_range.max;
+
+ /* bottom-right y */
+ hndl->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ hndl->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ hndl->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ hndl->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ hndl->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ hndl->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_BR_Y].constraint.range = &hndl->y_range;
+ hndl->val[OPT_BR_Y].w = hndl->y_range.max;
+
+ /* "Enhancement" group: */
+
+ hndl->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ hndl->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ hndl->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ hndl->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ hndl->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ hndl->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+
+
+ /* custom-gamma table */
+ hndl->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ hndl->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ hndl->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ hndl->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ hndl->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ if ( !(hndl->dev->caps & CAP_GAMMA_CORRECT))
+ hndl->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+
+ /* grayscale gamma vector */
+ hndl->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ hndl->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ hndl->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ hndl->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ hndl->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ hndl->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_GAMMA_VECTOR].constraint.range = &hndl->gamma_range;
+ hndl->val[OPT_GAMMA_VECTOR].wa = &hndl->gamma_table[0][0];
+
+ /* red gamma vector */
+ hndl->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ hndl->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ hndl->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ hndl->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ hndl->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].constraint.range = &hndl->gamma_range;
+ hndl->val[OPT_GAMMA_VECTOR_R].wa = &hndl->gamma_table[1][0];
+
+ /* green gamma vector */
+ hndl->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ hndl->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ hndl->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ hndl->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ hndl->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].constraint.range = &hndl->gamma_range;
+ hndl->val[OPT_GAMMA_VECTOR_G].wa = &hndl->gamma_table[2][0];
+
+ /* blue gamma vector */
+ hndl->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ hndl->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ hndl->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ hndl->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ hndl->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].constraint.range = &hndl->gamma_range;
+ hndl->val[OPT_GAMMA_VECTOR_B].wa = &hndl->gamma_table[3][0];
+
+ hndl->gamma_range.min = 0;
+ hndl->gamma_range.max = 255;
+ hndl->gamma_range.quant = 1;
+
+ hndl->opt[OPT_INVERT].name = SANE_NAME_NEGATIVE;
+ hndl->opt[OPT_INVERT].title = SANE_TITLE_NEGATIVE;
+ hndl->opt[OPT_INVERT].desc = SANE_DESC_NEGATIVE;
+ hndl->opt[OPT_INVERT].type = SANE_TYPE_BOOL;
+ hndl->val[OPT_INVERT].w = SANE_FALSE;
+
+ if (! (hndl->dev->caps & CAP_INVERT))
+ hndl->opt[OPT_INVERT].cap |= SANE_CAP_INACTIVE;
+
+
+}
+
+/* attach_device:
+ * Attempts to attach a device to the list after parsing of a section
+ * of the configuration file.
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * After parsing a scanner section of the config file, this function
+ * is called to look for a driver with a matching name. When found,
+ * this driver is called to initialize the device.
+ */
+static void
+attach_device(SANE_String *driver, SANE_String *name,
+ SANE_String *port, SANE_String *option_ta)
+{
+ int found = 0, driver_no, port_no;
+ const char **ports;
+
+ if (!strcmp (*port, "*"))
+ {
+ ports = sanei_pa4s2_devices();
+ DBG (3, "sanei_init: auto probing port\n");
+ }
+ else
+ {
+ ports = malloc (sizeof(char *) * 2);
+ ports[0] = *port;
+ ports[1] = NULL;
+ }
+
+ for (port_no=0; ports[port_no] != NULL; port_no++)
+ {
+ for (driver_no=0 ; driver_no<MUSTEK_PP_NUM_DRIVERS ; driver_no++)
+ {
+ if (strcasecmp (Mustek_pp_Drivers[driver_no].driver, *driver) == 0)
+ {
+ Mustek_pp_Drivers[driver_no].init (
+ (*option_ta == 0 ? CAP_NOTHING : CAP_TA),
+ ports[port_no], *name, sane_attach);
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ free (ports);
+
+ if (found == 0)
+ {
+ DBG (1, "sane_init: no scanner detected\n");
+ DBG (3, "sane_init: either the driver name ``%s'' is invalid, or no scanner was detected\n", *driver);
+ }
+
+ free (*name);
+ free (*port);
+ free (*driver);
+ if (*option_ta)
+ free (*option_ta);
+ *name = *port = *driver = *option_ta = 0;
+
+ /* In case of a successful initialization, the configuration options
+ should have been transfered to the device, but this function can
+ deal with that. */
+ free_cfg_options(&numcfgoptions, &cfgoptions);
+}
+
+/* sane_init:
+ * Reads configuration file and registers hardware driver
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * in *version_code the SANE version this backend was compiled with and the
+ * version of the backend is returned. The value of authorize is stored in
+ * the global variable sane_auth.
+ *
+ * Next the configuration file is read. If it isn't present, all drivers
+ * are auto-probed with default values (port 0x378, with and without TA).
+ *
+ * The configuration file is expected to contain lines of the form
+ *
+ * scanner <name> <port> <driver> [<option_ta>]
+ *
+ * where <name> is a arbitrary name to identify this entry
+ * <port> is the port where the scanner is attached to
+ * <driver> is the name of the driver to use
+ *
+ * if the optional argument "option_ta" is present the driver uses special
+ * parameters fitting for a trasparency adapter.
+ */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ FILE *fp;
+ char config_line[1024];
+ const char *config_line_ptr;
+ int line=0, driver_no;
+ char *driver = 0, *port = 0, *name = 0, *option_ta = 0;
+
+ DBG_INIT ();
+ DBG (3, "sane-mustek_pp, version 0.%d-%s. build for SANE %s\n",
+ MUSTEK_PP_BUILD, MUSTEK_PP_STATE, VERSION);
+ DBG (3, "backend by Jochen Eisinger <jochen.eisinger@gmx.net>\n");
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, MUSTEK_PP_BUILD);
+
+ sane_auth = authorize;
+
+
+ fp = sanei_config_open (MUSTEK_PP_CONFIG_FILE);
+
+ if (fp == NULL)
+ {
+ char driver_name[64];
+ const char **devices = sanei_pa4s2_devices();
+ int device_no;
+
+ DBG (2, "sane_init: could not open configuration file\n");
+
+ for (device_no = 0; devices[device_no] != NULL; device_no++)
+ {
+ DBG (3, "sane_init: trying ``%s''\n", devices[device_no]);
+ for (driver_no=0 ; driver_no<MUSTEK_PP_NUM_DRIVERS ; driver_no++)
+ {
+ Mustek_pp_Drivers[driver_no].init(CAP_NOTHING, devices[device_no],
+ Mustek_pp_Drivers[driver_no].driver, sane_attach);
+
+ snprintf (driver_name, 64, "%s-ta",
+ Mustek_pp_Drivers[driver_no].driver);
+
+ Mustek_pp_Drivers[driver_no].init(CAP_TA, devices[device_no],
+ driver_name, sane_attach);
+ }
+ }
+
+ free (devices);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (config_line, 1023, fp))
+ {
+ line++;
+ if ((!*config_line) || (*config_line == '#'))
+ continue;
+
+ config_line_ptr = config_line;
+
+ if (strncmp(config_line_ptr, "scanner", 7) == 0)
+ {
+ config_line_ptr += 7;
+
+ if (name)
+ {
+ /* Parsing of previous scanner + options is finished. Attach
+ the device before we parse the next section. */
+ attach_device(&driver, &name, &port, &option_ta);
+ }
+
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+ if (!*config_line_ptr)
+ {
+ DBG (1, "sane_init: parse error in line %d after ``scanner''\n",
+ line);
+ continue;
+ }
+
+ config_line_ptr = sanei_config_get_string (config_line_ptr, &name);
+ if ((name == NULL) || (!*name))
+ {
+ DBG (1, "sane_init: parse error in line %d after ``scanner''\n",
+ line);
+ if (name != NULL)
+ free (name);
+ name = 0;
+ continue;
+ }
+
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+ if (!*config_line_ptr)
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``scanner %s''\n", line, name);
+ free (name);
+ name = 0;
+ continue;
+ }
+
+ config_line_ptr = sanei_config_get_string (config_line_ptr, &port);
+ if ((port == NULL) || (!*port))
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``scanner %s''\n", line, name);
+ free (name);
+ name = 0;
+ if (port != NULL)
+ free (port);
+ port = 0;
+ continue;
+ }
+
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+ if (!*config_line_ptr)
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``scanner %s %s''\n", line, name, port);
+ free (name);
+ free (port);
+ name = 0;
+ port = 0;
+ continue;
+ }
+
+ config_line_ptr = sanei_config_get_string (config_line_ptr, &driver);
+ if ((driver == NULL) || (!*driver))
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``scanner %s %s''\n", line, name, port);
+ free (name);
+ name = 0;
+ free (port);
+ port = 0;
+ if (driver != NULL)
+ free (driver);
+ driver = 0;
+ continue;
+ }
+
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+
+ if (*config_line_ptr)
+ {
+ config_line_ptr = sanei_config_get_string (config_line_ptr,
+ &option_ta);
+
+ if ((option_ta == NULL) || (!*option_ta) ||
+ (strcasecmp (option_ta, "use_ta") != 0))
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``scanner %s %s %s''\n", line, name, port, driver);
+ free (name);
+ free (port);
+ free (driver);
+ if (option_ta)
+ free (option_ta);
+ name = port = driver = option_ta = 0;
+ continue;
+ }
+ }
+
+ if (*config_line_ptr)
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``scanner %s %s %s %s\n", line, name, port, driver,
+ (option_ta == 0 ? "" : option_ta));
+ free (name);
+ free (port);
+ free (driver);
+ if (option_ta)
+ free (option_ta);
+ name = port = driver = option_ta = 0;
+ continue;
+ }
+ }
+ else if (strncmp(config_line_ptr, "option", 6) == 0)
+ {
+ /* Format for options: option <name> [<value>]
+ Note that the value is optional. */
+ char *optname, *optval = 0;
+ Mustek_pp_config_option *tmpoptions;
+
+ config_line_ptr += 6;
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+ if (!*config_line_ptr)
+ {
+ DBG (1, "sane_init: parse error in line %d after ``option''\n",
+ line);
+ continue;
+ }
+
+ config_line_ptr = sanei_config_get_string (config_line_ptr, &optname);
+ if ((optname == NULL) || (!*optname))
+ {
+ DBG (1, "sane_init: parse error in line %d after ``option''\n",
+ line);
+ if (optname != NULL)
+ free (optname);
+ continue;
+ }
+
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+ if (*config_line_ptr)
+ {
+ /* The option has a value.
+ No need to check the value; that's up to the backend */
+ config_line_ptr = sanei_config_get_string (config_line_ptr,
+ &optval);
+
+ config_line_ptr = sanei_config_skip_whitespace (config_line_ptr);
+ }
+
+ if (*config_line_ptr)
+ {
+ DBG (1, "sane_init: parse error in line %d after "
+ "``option %s %s''\n", line, optname,
+ (optval == 0 ? "" : optval));
+ free (optname);
+ if (optval)
+ free (optval);
+ continue;
+ }
+
+ if (!strcmp (optname, "no_epp"))
+ {
+ u_int pa4s2_options;
+ if (name)
+ DBG (2, "sane_init: global option found in local scope, "
+ "executing anyway\n");
+ free (optname);
+ if (optval)
+ {
+ DBG (1, "sane_init: unexpected value for option no_epp\n");
+ free (optval);
+ continue;
+ }
+ DBG (3, "sane_init: disabling mode EPP\n");
+ sanei_pa4s2_options (&pa4s2_options, SANE_FALSE);
+ pa4s2_options |= SANEI_PA4S2_OPT_NO_EPP;
+ sanei_pa4s2_options (&pa4s2_options, SANE_TRUE);
+ continue;
+ }
+ else if (!name)
+ {
+ DBG (1, "sane_init: parse error in line %d: unexpected "
+ " ``option''\n", line);
+ free (optname);
+ if (optval)
+ free (optval);
+ continue;
+ }
+
+
+ /* Extend the (global) array of options */
+ tmpoptions = realloc(cfgoptions,
+ (numcfgoptions+1)*sizeof(cfgoptions[0]));
+ if (!tmpoptions)
+ {
+ DBG (1, "sane_init: not enough memory for device options\n");
+ free_cfg_options(&numcfgoptions, &cfgoptions);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ cfgoptions = tmpoptions;
+ cfgoptions[numcfgoptions].name = optname;
+ cfgoptions[numcfgoptions].value = optval;
+ ++numcfgoptions;
+ }
+ else
+ {
+ DBG (1, "sane_init: parse error at beginning of line %d\n", line);
+ continue;
+ }
+
+ }
+
+ /* If we hit the end of the file, we still may have to process the
+ last driver */
+ if (name)
+ attach_device(&driver, &name, &port, &option_ta);
+
+ fclose(fp);
+ return SANE_STATUS_GOOD;
+
+}
+
+/* sane_exit:
+ * Unloads all drivers and frees allocated memory
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * All open devices are closed first. Then all registered devices
+ * are removed.
+ *
+ */
+
+void
+sane_exit (void)
+{
+ Mustek_pp_Handle *hndl;
+ Mustek_pp_Device *dev;
+
+ if (first_hndl)
+ DBG (3, "sane_exit: closing open devices\n");
+
+ while (first_hndl)
+ {
+ hndl = first_hndl;
+ sane_close (hndl);
+ }
+
+ dev = devlist;
+ num_devices = 0;
+ devlist = NULL;
+
+ while (dev) {
+
+ free (dev->port);
+ free (dev->name);
+ free (dev->vendor);
+ free (dev->model);
+ free (dev->type);
+ free_cfg_options (&dev->numcfgoptions, &dev->cfgoptions);
+ dev = dev->next;
+
+ }
+
+ if (devarray != NULL)
+ free (devarray);
+ devarray = NULL;
+
+ DBG (3, "sane_exit: all drivers unloaded\n");
+
+}
+
+/* sane_get_devices:
+ * Returns a list of registered devices
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * A possible present old device_list is removed first. A new
+ * devarray is allocated and filled with pointers to the
+ * SANE_Device structures of the Mustek_pp_Devices
+ */
+/*ARGSUSED*/
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only __UNUSED__)
+{
+ int ctr;
+ Mustek_pp_Device *dev;
+
+ if (devarray != NULL)
+ free (devarray);
+
+ devarray = malloc ((num_devices + 1) * sizeof (devarray[0]));
+
+ if (devarray == NULL)
+ {
+ DBG (1, "sane_get_devices: not enough memory for device list\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev = devlist;
+
+ for (ctr=0 ; ctr<num_devices ; ctr++) {
+ devarray[ctr] = &dev->sane;
+ dev = dev->next;
+ }
+
+ devarray[num_devices] = NULL;
+ *device_list = (const SANE_Device **)devarray;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* sane_open:
+ * opens a device and prepares it for operation
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * The device identified by ``devicename'' is looked
+ * up in the list, or if devicename is zero, the
+ * first device from the list is taken.
+ *
+ * open is called for the selected device.
+ *
+ * The handel is set up with default values, and the
+ * option descriptors are initialized
+ */
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+
+ Mustek_pp_Handle *hndl;
+ Mustek_pp_Device *dev;
+ SANE_Status status;
+ int fd, i;
+
+ if (devicename[0]) {
+
+ dev = devlist;
+
+ while (dev) {
+
+ if (strcmp (dev->name, devicename) == 0)
+ break;
+
+ dev = dev->next;
+
+ }
+
+ if (!dev) {
+
+ DBG (1, "sane_open: unknown devicename ``%s''\n", devicename);
+ return SANE_STATUS_INVAL;
+
+ }
+ } else
+ dev = devlist;
+
+ if (!dev) {
+ DBG (1, "sane_open: no devices present...\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "sane_open: Using device ``%s'' (driver %s v%s by %s)\n",
+ dev->name, dev->func->driver, dev->func->version, dev->func->author);
+
+ if ((hndl = malloc (sizeof (Mustek_pp_Handle))) == NULL) {
+
+ DBG (1, "sane_open: not enough free memory for the handle\n");
+ return SANE_STATUS_NO_MEM;
+
+ }
+
+ if ((status = dev->func->open (dev->port, dev->caps, &fd)) != SANE_STATUS_GOOD) {
+
+ DBG (1, "sane_open: could not open device (%s)\n",
+ sane_strstatus (status));
+ return status;
+
+ }
+
+ hndl->next = first_hndl;
+ hndl->dev = dev;
+ hndl->fd = fd;
+ hndl->state = STATE_IDLE;
+ hndl->pipe = -1;
+
+ init_options (hndl);
+
+ dev->func->setup (hndl);
+
+ /* Initialize driver-specific configuration options. This must be
+ done after calling the setup() function because only then the
+ driver is guaranteed to be fully initialized */
+ for (i = 0; i<dev->numcfgoptions; ++i)
+ {
+ status = dev->func->config (hndl,
+ dev->cfgoptions[i].name,
+ dev->cfgoptions[i].value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: could not set option %s for device (%s)\n",
+ dev->cfgoptions[i].name, sane_strstatus (status));
+
+ /* Question: should the initialization be aborted when an
+ option cannot be handled ?
+ The driver should have reasonable built-in defaults, so
+ an illegal option value or an unknown option should not
+ be fatal. Therefore, it's probably ok to ignore the error. */
+ }
+ }
+
+ first_hndl = hndl;
+
+ *handle = hndl;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* sane_close:
+ * closes a given device and frees all resources
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * The handle is searched in the list of active handles.
+ * If it's found, the handle is removed.
+ *
+ * If the associated device is still scanning, the process
+ * is cancelled.
+ *
+ * Then the backend makes sure, the lamp was at least
+ * 2 seconds on.
+ *
+ * Afterwards the selected handel is closed
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ Mustek_pp_Handle *prev, *hndl;
+
+ prev = NULL;
+
+ for (hndl = first_hndl; hndl; hndl = hndl->next)
+ {
+ if (hndl == handle)
+ break;
+ prev = hndl;
+ }
+
+ if (hndl == NULL)
+ {
+ DBG (2, "sane_close: unknown device handle\n");
+ return;
+ }
+
+ if (hndl->state == STATE_SCANNING) {
+ sane_cancel (handle);
+ do_eof (handle);
+ }
+
+ if (prev != NULL)
+ prev->next = hndl->next;
+ else
+ first_hndl = hndl->next;
+
+ DBG (3, "sane_close: maybe waiting for lamp...\n");
+ if (hndl->lamp_on)
+ while (time (NULL) - hndl->lamp_on < 2)
+ sleep (1);
+
+ hndl->dev->func->close (hndl);
+
+ DBG (3, "sane_close: device closed\n");
+
+ free (handle);
+
+}
+
+/* sane_get_option_descriptor:
+ * does what it says
+ *
+ * ChangeLog:
+ *
+ * Description:
+ *
+ */
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Mustek_pp_Handle *hndl = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ DBG (2, "sane_get_option_descriptor: option %d doesn't exist\n", option);
+ return NULL;
+ }
+
+ return hndl->opt + option;
+}
+
+
+/* sane_control_option:
+ * Reads or writes an option
+ *
+ * ChangeLog:
+ *
+ * Desription:
+ * If a pointer to info is given, the value is initialized to zero
+ * while scanning options cannot be read or written. next a basic
+ * check whether the request is valid is done.
+ *
+ * Depending on ``action'' the value of the option is either read
+ * (in the first block) or written (in the second block). auto
+ * values aren't supported.
+ *
+ * before a value is written, some checks are performed. Depending
+ * on the option, that is written, other options also change
+ *
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Mustek_pp_Handle *hndl = handle;
+ SANE_Status status;
+ SANE_Word w, cap;
+
+ if (info)
+ *info = 0;
+
+ if (hndl->state == STATE_SCANNING)
+ {
+ DBG (2, "sane_control_option: device is scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((unsigned int) option >= NUM_OPTIONS)
+ {
+ DBG (2, "sane_control_option: option %d doesn't exist\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = hndl->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (2, "sane_control_option: option %d isn't active\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options: */
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_INVERT:
+ case OPT_DEPTH:
+
+ *(SANE_Word *) val = hndl->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+
+ memcpy (val, hndl->val[option].wa, hndl->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_SPEED:
+
+ strcpy (val, hndl->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (2, "sane_control_option: option can't be set (%s)\n",
+ hndl->opt[option].name);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (hndl->opt + option, val, info);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "sane_control_option: constrain_value failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_INVERT:
+ case OPT_DEPTH:
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ hndl->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+
+ memcpy (hndl->val[option].wa, val, hndl->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free string options: */
+ case OPT_SPEED:
+
+ if (hndl->val[option].s)
+ free (hndl->val[option].s);
+
+ hndl->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+
+ /* options with side-effects: */
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == hndl->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ hndl->val[OPT_CUSTOM_GAMMA].w = w;
+
+ if (w == SANE_TRUE)
+ {
+ const char *mode = hndl->val[OPT_MODE].s;
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ {
+ char *old_val = hndl->val[option].s;
+
+ if (old_val)
+ {
+ if (strcmp (old_val, val) == 0)
+ return SANE_STATUS_GOOD; /* no change */
+
+ free (old_val);
+ }
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ hndl->val[option].s = strdup (val);
+
+ hndl->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ hndl->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ if ((hndl->dev->caps & CAP_DEPTH) && (strcmp(val, SANE_VALUE_SCAN_MODE_COLOR) == 0))
+ hndl->opt[OPT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+
+ if (!(hndl->dev->caps & CAP_GAMMA_CORRECT))
+ return SANE_STATUS_GOOD;
+
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) != 0)
+ hndl->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+
+ if (hndl->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ {
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ hndl->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+ }
+
+ DBG (2, "sane_control_option: unknown action\n");
+ return SANE_STATUS_INVAL;
+}
+
+
+/* sane_get_parameters:
+ * returns the set of parameters, that is used for the next scan
+ *
+ * ChangeLog:
+ *
+ * Description:
+ *
+ * First of all it is impossible to change the parameter set
+ * while scanning.
+ *
+ * sane_get_parameters not only returns the parameters for
+ * the next scan, it also sets them, i.e. converts the
+ * options in actuall parameters.
+ *
+ * The following parameters are set:
+ *
+ * scanmode: according to the option SCANMODE, but
+ * 24bit color, if PREVIEW is selected and
+ * grayscale if GRAY_PREVIEW is selected
+ * depth: the bit depth for color modes (if
+ * supported) or 24bit by default
+ * (ignored in bw/grayscale or if not
+ * supported)
+ * dpi: resolution
+ * invert: if supported else defaults to false
+ * gamma: if supported and selected
+ * ta: if supported by the device
+ * speed: selected speed (or fastest if not
+ * supported)
+ * scanarea: the scanarea is calculated from the
+ * selections the user has mode. note
+ * that the area may slightly differ from
+ * the scanarea selected due to rounding
+ * note also, that a scanarea of
+ * (0,0)-(100,100) will include all pixels
+ * where 0 <= x < 100 and 0 <= y < 100
+ * afterwards, all values are copied into the SANE_Parameters
+ * structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Mustek_pp_Handle *hndl = handle;
+ char *mode;
+ int dpi, ctr;
+
+ if (hndl->state != STATE_SCANNING)
+ {
+
+
+ memset (&hndl->params, 0, sizeof (hndl->params));
+
+
+ if ((hndl->dev->caps & CAP_DEPTH) && (hndl->mode == MODE_COLOR))
+ hndl->depth = hndl->val[OPT_DEPTH].w;
+ else
+ hndl->depth = 8;
+
+ dpi = (int) (SANE_UNFIX (hndl->val[OPT_RESOLUTION].w) + 0.5);
+
+ hndl->res = dpi;
+
+ if (hndl->dev->caps & CAP_INVERT)
+ hndl->invert = hndl->val[OPT_INVERT].w;
+ else
+ hndl->invert = SANE_FALSE;
+
+ if (hndl->dev->caps & CAP_TA)
+ hndl->use_ta = SANE_TRUE;
+ else
+ hndl->use_ta = SANE_FALSE;
+
+ if ((hndl->dev->caps & CAP_GAMMA_CORRECT) && (hndl->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE))
+ hndl->do_gamma = SANE_TRUE;
+ else
+ hndl->do_gamma = SANE_FALSE;
+
+ if (hndl->dev->caps & CAP_SPEED_SELECT) {
+
+ for (ctr=SPEED_SLOWEST; ctr<=SPEED_FASTEST; ctr++)
+ if (strcmp(mustek_pp_speeds[ctr], hndl->val[OPT_SPEED].s) == 0)
+ hndl->speed = ctr;
+
+
+
+ } else
+ hndl->speed = SPEED_NORMAL;
+
+ mode = hndl->val[OPT_MODE].s;
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ hndl->mode = MODE_BW;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ hndl->mode = MODE_GRAYSCALE;
+ else
+ hndl->mode = MODE_COLOR;
+
+ if (hndl->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+
+ hndl->speed = SPEED_FASTEST;
+ hndl->depth = 8;
+ if (! hndl->use_ta)
+ hndl->invert = SANE_FALSE;
+ hndl->do_gamma = SANE_FALSE;
+
+ if (hndl->val[OPT_GRAY_PREVIEW].w == SANE_TRUE)
+ hndl->mode = MODE_GRAYSCALE;
+ else {
+ hndl->mode = MODE_COLOR;
+ }
+
+ }
+
+ hndl->topX =
+ MIN ((int)
+ (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_TL_X].w), hndl->dev->maxres) +
+ 0.5), hndl->dev->maxhsize);
+ hndl->topY =
+ MIN ((int)
+ (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_TL_Y].w), hndl->dev->maxres) +
+ 0.5), hndl->dev->maxvsize);
+
+ hndl->bottomX =
+ MIN ((int)
+ (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_BR_X].w), hndl->dev->maxres) +
+ 0.5), hndl->dev->maxhsize);
+ hndl->bottomY =
+ MIN ((int)
+ (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_BR_Y].w), hndl->dev->maxres) +
+ 0.5), hndl->dev->maxvsize);
+
+ /* If necessary, swap the upper and lower boundaries to avoid negative
+ distances. */
+ if (hndl->topX > hndl->bottomX) {
+ SANE_Int tmp = hndl->topX;
+ hndl->topX = hndl->bottomX;
+ hndl->bottomX = tmp;
+ }
+ if (hndl->topY > hndl->bottomY) {
+ SANE_Int tmp = hndl->topY;
+ hndl->topY = hndl->bottomY;
+ hndl->bottomY = tmp;
+ }
+
+ hndl->params.pixels_per_line = (hndl->bottomX - hndl->topX) * hndl->res
+ / hndl->dev->maxres;
+
+ hndl->params.bytes_per_line = hndl->params.pixels_per_line;
+
+ switch (hndl->mode)
+ {
+
+ case MODE_BW:
+ hndl->params.bytes_per_line /= 8;
+
+ if ((hndl->params.pixels_per_line % 8) != 0)
+ hndl->params.bytes_per_line++;
+
+ hndl->params.depth = 1;
+ break;
+
+ case MODE_GRAYSCALE:
+ hndl->params.depth = 8;
+ hndl->params.format = SANE_FRAME_GRAY;
+ break;
+
+ case MODE_COLOR:
+ hndl->params.depth = hndl->depth;
+ hndl->params.bytes_per_line *= 3;
+ if (hndl->depth > 8)
+ hndl->params.bytes_per_line *= 2;
+ hndl->params.format = SANE_FRAME_RGB;
+ break;
+
+ }
+
+ hndl->params.last_frame = SANE_TRUE;
+
+ hndl->params.lines = (hndl->bottomY - hndl->topY) * hndl->res /
+ hndl->dev->maxres;
+ }
+ else
+ DBG (2, "sane_get_parameters: can't set parameters while scanning\n");
+
+ if (params != NULL)
+ *params = hndl->params;
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+/* sane_start:
+ * starts the scan. data aquisition will start immedially
+ *
+ * ChangeLog:
+ *
+ * Description:
+ *
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Mustek_pp_Handle *hndl = handle;
+ int pipeline[2];
+
+ if (hndl->state == STATE_SCANNING) {
+ DBG (2, "sane_start: device is already scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+
+ }
+
+ sane_get_parameters (hndl, NULL);
+
+ if (pipe(pipeline) < 0) {
+ DBG (1, "sane_start: could not initialize pipe (%s)\n",
+ strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ hndl->reader = fork();
+
+ if (hndl->reader == 0) {
+
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ close (pipeline[0]);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+ sigprocmask (SIG_SETMASK, &ignore_set, NULL);
+
+ memset (&act, 0, sizeof(act));
+ sigaction (SIGTERM, &act, NULL);
+
+ _exit (reader_process (hndl, pipeline[1]));
+
+ }
+
+ close (pipeline[1]);
+
+ hndl->pipe = pipeline[0];
+
+ hndl->state = STATE_SCANNING;
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+/* sane_read:
+ * receives data from pipeline and passes it to the caller
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * ditto
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Mustek_pp_Handle *hndl = handle;
+ SANE_Int nread;
+
+
+ if (hndl->state == STATE_CANCELLED) {
+ DBG (2, "sane_read: device already cancelled\n");
+ do_eof (hndl);
+ hndl->state = STATE_IDLE;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (hndl->state != STATE_SCANNING) {
+ DBG (1, "sane_read: device isn't scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ *len = nread = 0;
+
+ while (*len < max_len) {
+
+ nread = read(hndl->pipe, buf + *len, max_len - *len);
+
+ if (hndl->state == STATE_CANCELLED) {
+
+ *len = 0;
+ DBG(3, "sane_read: scan was cancelled\n");
+
+ do_eof (hndl);
+ hndl->state = STATE_IDLE;
+ return SANE_STATUS_CANCELLED;
+
+ }
+
+ if (nread < 0) {
+
+ if (errno == EAGAIN) {
+
+ if (*len == 0)
+ DBG(3, "sane_read: no data at the moment\n");
+ else
+ DBG(3, "sane_read: %d bytes read\n", *len);
+
+ return SANE_STATUS_GOOD;
+
+ } else {
+
+ DBG(1, "sane_read: IO error (%s)\n", strerror(errno));
+
+ hndl->state = STATE_IDLE;
+ do_stop(hndl);
+
+ do_eof (hndl);
+
+ *len = 0;
+ return SANE_STATUS_IO_ERROR;
+
+ }
+ }
+
+ *len += nread;
+
+ if (nread == 0) {
+
+ if (*len == 0) {
+
+ DBG (3, "sane_read: read finished\n");
+ do_stop(hndl);
+
+ hndl->state = STATE_IDLE;
+
+ return do_eof(hndl);
+
+ }
+
+ DBG(3, "sane_read: read last buffer of %d bytes\n",
+ *len);
+
+ return SANE_STATUS_GOOD;
+
+ }
+
+ }
+
+ DBG(3, "sane_read: read full buffer of %d bytes\n", *len);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* sane_cancel:
+ * stops a scan and ends the reader process
+ *
+ * ChangeLog:
+ *
+ * Description:
+ *
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ Mustek_pp_Handle *hndl = handle;
+
+ if (hndl->state != STATE_SCANNING)
+ return;
+
+ hndl->state = STATE_CANCELLED;
+
+ do_stop (hndl);
+
+}
+
+
+/* sane_set_io_mode:
+ * toggles between blocking and non-blocking reading
+ *
+ * ChangeLog:
+ *
+ * Description:
+ *
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+
+ Mustek_pp_Handle *hndl=handle;
+
+ if (hndl->state != STATE_SCANNING)
+ return SANE_STATUS_INVAL;
+
+
+ if (fcntl (hndl->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) {
+
+ DBG(1, "sane_set_io_mode: can't set io mode\n");
+
+ return SANE_STATUS_IO_ERROR;
+
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* sane_get_select_fd:
+ * returns the pipeline fd for direct reading
+ *
+ * ChangeLog:
+ *
+ * Description:
+ * to allow the frontend to receive the data directly it
+ * can read from the pipeline itself
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Mustek_pp_Handle *hndl=handle;
+
+ if (hndl->state != STATE_SCANNING)
+ return SANE_STATUS_INVAL;
+
+ *fd = hndl->pipe;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* include drivers */
+#include "mustek_pp_decl.h"
+#include "mustek_pp_null.c"
+#include "mustek_pp_cis.h"
+#include "mustek_pp_cis.c"
+#include "mustek_pp_ccd300.h"
+#include "mustek_pp_ccd300.c"
diff --git a/backend/mustek_pp.conf.in b/backend/mustek_pp.conf.in
new file mode 100644
index 0000000..17f0e26
--- /dev/null
+++ b/backend/mustek_pp.conf.in
@@ -0,0 +1,103 @@
+# For documentation see sane-mustek_pp(5)
+
+# Global options:
+# ===============
+#
+# option no_epp
+#
+# Disable parallel port mode EPP: works around a known bug in
+# the Linux parport code. Enable this option, if the backend
+# hangs when trying to access the parallel port in EPP mode:
+#
+# # SANE_DEBUG_SANEI_PA4S2=128 scanimage -L
+# ...
+# hangs here -> [sanei_pa4s2] sanei_pa4s2_readbyte: read in EPP mode
+#
+# Scanner definition template:
+# ============================
+#
+# scanner <name> <port> <type>
+# option <optname> <optval>?
+# option <optname> <optval>?
+# ...
+#
+# where:
+#
+# <name> is an arbitrary name for the scanner (eg. Mustek-1200CP)
+#
+# <port> is the parallel port to which the scanner is connected
+# Possible values are 0x378, 0x278, and 0x3bc. For Linux, the
+# mapping between ports an numbers is different for kernel
+# version 2.2 and 2.4. Port 0x378 corresponds to lp0 on 2.4 kernel.
+# If you are using libieee1284, you can as well use parport0, etc..
+# If you use the magic value * the port is probed.
+#
+# <type> is an identification of the scanner type.
+# Possible values are:
+# - cis600 (for Mustek 600CP & OEM versions),
+# - cis1200 (for Mustek 1200CP & OEM versions),
+# - cis1200+ (for Mustek 1200CP+ & OEM versions),
+# - ccd300 (for Mustek 600 III EPP & OEM versions)
+# - ... more types will be added in the future
+#
+# <optname> is a name of an option, and <optval> an optional value
+# for the option.
+# Currently available options for *CIS* type scanners are:
+# - top_adjust <value>:
+# Vertical adjustment of origin, in millimeter.
+# Values between -5.0 and +5.0 mm are possible
+# (floating point).
+# Default: 0.0
+# - slow_skip:
+# Boolean option. Disables fast skipping to the start
+# of the scan region. May be necessary in case fast
+# skipping results in inaccuracies.
+# Default: fast skipping enabled
+# - bw <value>:
+# Black/white discrimination value for lineart scans.
+# Pixel values below that value are considered black,
+# others are considered white. Range: 0-255.
+# Default: 127
+#
+# Currently available options for *CCD* type scanners are:
+# - wait_bank <value>
+# usecs to wait for a bank change. Positive integer
+# values are possible. You shouldn't mess with this
+# parameter.
+# Default: 700
+# - bw <value>
+# Black/white discrimination value for lineart scans.
+# Pixel values below that value are considered black,
+# others are considered white. Range: 0-255.
+# Default: 127
+# - top <value>
+# Scanlines to skip to the top area. Positive integer
+# values are possible. 47 and 56 are values I know of.
+# Default: 47
+#
+#
+# Example for a LifeTec LT9350 (Mustek 1200CP clone):
+#
+# scanner LT9350 0x378 cis1200
+# option top_adjust 0
+# option bw 127
+#
+# Example for Mustek 6000P
+#
+# scanner 6000P 0x378 ccd300
+# option top 56
+#
+#
+# Uncomment/customize to your needs
+#
+# scanner Mustek-600CP 0x378 cis600
+# scanner Mustek-1200CP 0x378 cis1200
+# scanner Mustek-1200CP+ 0x378 cis1200+
+# scanner Mustek-600-IIIEP 0x378 ccd300
+#
+# auto probing:
+#
+# scanner mustek-cis600 * cis600
+# scanner mustek-cis1200 * cis1200
+# scanner mustek-cis1200+ * cis1200+
+# scanner mustek-ccd300 * ccd300
diff --git a/backend/mustek_pp.h b/backend/mustek_pp.h
new file mode 100644
index 0000000..19f1765
--- /dev/null
+++ b/backend/mustek_pp.h
@@ -0,0 +1,286 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef mustek_pp_h
+#define mustek_pp_h
+
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#if defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#endif
+
+#define DEBUG_NOT_STATIC
+#include "../include/sane/sanei_debug.h"
+
+/* Please note: ASSERT won't go away if you define NDEBUG, it just won't
+ * output a message when ASSERT failes. So if "cond" does anything, it will
+ * be executed, even if NDEBUG is defined...
+ */
+#define ASSERT(cond, retval) do { \
+ if (!(cond)) { \
+ DBG(2, "assertion %s failed\n", \
+ STRINGIFY(cond)); \
+ if (retval >= 0) \
+ return retval; \
+ else \
+ return; \
+ } \
+ }
+
+/* This macro uses a otherwise unused argument */
+#if defined(__GNUC__)
+# define __UNUSED__ __attribute__ ((unused))
+#else
+# define __UNUSED__
+#endif
+
+
+/* the function init uses this callback to register a device to the backend */
+typedef SANE_Status (*SANE_Attach_Callback) (SANE_String_Const port, SANE_String_Const name,
+ SANE_Int driver, SANE_Int info);
+
+typedef struct {
+
+ const char *driver;
+ const char *author;
+ const char *version;
+
+ /* this function detects the presence of a scanner at the
+ * given location */
+ SANE_Status (*init)(SANE_Int options,
+ SANE_String_Const port,
+ SANE_String_Const name,
+ SANE_Attach_Callback attach);
+ /* this function returns the informationen needed to set up
+ * the device entry. the info parameter is passed from
+ * init to the attach_callback to this function, to
+ * help to identify the device, before it is registered
+ */
+ void (*capabilities)(SANE_Int info,
+ SANE_String *model,
+ SANE_String *vendor,
+ SANE_String *type,
+ SANE_Int *maxres,
+ SANE_Int *minres,
+ SANE_Int *maxhsize,
+ SANE_Int *maxvsize,
+ SANE_Int *caps);
+
+ /* tries to open the given device. returns a fd on success */
+ SANE_Status (*open)(SANE_String port, SANE_Int caps, SANE_Int *fd);
+
+ /* start scanning session */
+ void (*setup)(SANE_Handle hndl);
+
+ /* processes a configuration option */
+ SANE_Status (*config)(SANE_Handle hndl,
+ SANE_String_Const optname,
+ SANE_String_Const optval);
+
+ /* stop scanning session */
+ void (*close)(SANE_Handle hndl);
+
+ /* start actuall scan */
+ SANE_Status (*start)(SANE_Handle hndl);
+
+ /* read data (one line) */
+ void (*read)(SANE_Handle hndl, SANE_Byte *buffer);
+
+ /* stop scanner and return scanhead home */
+ void (*stop)(SANE_Handle hndl);
+
+} Mustek_pp_Functions;
+
+/* Drivers */
+
+
+
+#define MUSTEK_PP_NUM_DRIVERS ((int)(sizeof(Mustek_pp_Drivers) / \
+ sizeof(Mustek_pp_Functions)))
+
+#define CAP_NOTHING 0
+#define CAP_GAMMA_CORRECT 1
+#define CAP_INVERT 2
+#define CAP_SPEED_SELECT 4
+#define CAP_LAMP_OFF 8
+#define CAP_TA 16
+#define CAP_DEPTH 32
+
+/* Structure for holding name/value options from the configuration file */
+typedef struct Mustek_pp_config_option {
+
+ SANE_String name;
+ SANE_String value;
+
+} Mustek_pp_config_option;
+
+typedef struct Mustek_pp_Device {
+
+ struct Mustek_pp_Device *next;
+
+ SANE_Device sane;
+
+ /* non-const copy of SANE_Device */
+ SANE_String name, vendor, model, type;
+
+ /* port */
+ SANE_String port;
+
+ /* part describing hardware capabilities */
+ int minres;
+ int maxres;
+ int maxhsize;
+ int maxvsize;
+ int caps;
+
+ /* functions */
+ Mustek_pp_Functions *func;
+
+ /* Modified by EDG: device identification is needed to initialize
+ private device descriptor */
+ SANE_Int info;
+
+ /* Array of configuration file options */
+ int numcfgoptions;
+ Mustek_pp_config_option *cfgoptions;
+
+} Mustek_pp_Device;
+
+#define STATE_IDLE 0
+#define STATE_CANCELLED 1
+#define STATE_SCANNING 2
+
+#define MODE_BW 0
+#define MODE_GRAYSCALE 1
+#define MODE_COLOR 2
+
+#define SPEED_SLOWEST 0
+#define SPEED_SLOWER 1
+#define SPEED_NORMAL 2
+#define SPEED_FASTER 3
+#define SPEED_FASTEST 4
+
+
+enum Mustek_pp_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_DEPTH,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_GRAY_PREVIEW,
+ OPT_SPEED,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+
+
+ OPT_INVERT,
+
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables? */
+ /* The gamma vectors MUST appear in the order gray, red, green,
+ blue. */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+
+typedef struct Mustek_pp_Handle {
+
+ struct Mustek_pp_Handle *next;
+
+
+
+ Mustek_pp_Device *dev;
+
+ int fd;
+
+ int reader;
+ int pipe;
+
+ int state;
+
+ int topX, topY;
+ int bottomX, bottomY;
+ int mode;
+ int res;
+
+ /* gamma table, etc... */
+ SANE_Int gamma_table[4][256];
+ int do_gamma;
+ int invert;
+ int use_ta;
+ int depth;
+ int speed;
+
+ /* current parameters */
+ SANE_Parameters params;
+
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range gamma_range;
+
+ /* options */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+
+ time_t lamp_on;
+
+ void *priv;
+
+} Mustek_pp_Handle;
+
+#endif /* mustek_pp_h */
diff --git a/backend/mustek_pp_ccd300.c b/backend/mustek_pp_ccd300.c
new file mode 100644
index 0000000..c5351a4
--- /dev/null
+++ b/backend/mustek_pp_ccd300.c
@@ -0,0 +1,2061 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the hardware driver scanners using a 300dpi CCD */
+
+#include "mustek_pp_ccd300.h"
+
+#define MUSTEK_PP_CCD300 4
+
+static void config_ccd_101x (Mustek_pp_Handle * dev);
+static void config_ccd (Mustek_pp_Handle * dev);
+static void lamp (Mustek_pp_Handle * dev, int lamp_on);
+
+#define CCD300_ASIC1013 0xa8
+#define CCD300_ASIC1015 0xa5
+
+#define CCD300_CHANNEL_RED 0
+#define CCD300_CHANNEL_GREEN 1
+#define CCD300_CHANNEL_BLUE 2
+#define CCD300_CHANNEL_GRAY 1
+
+#define CCD300_MAXHSIZE 2600
+#define CCD300_MAXVSIZE 3500
+
+/*
+ * Here starts the driver code for the different chipsets
+ *
+ * The 1013 & 1015 chipsets share large portions of the code. This
+ * shared functions end with _101x.
+ */
+
+static const u_char chan_codes_1013[] = { 0x82, 0x42, 0xC2 };
+static const u_char chan_codes_1015[] = { 0x80, 0x40, 0xC0 };
+static const u_char fullstep[] = { 0x09, 0x0C, 0x06, 0x03 };
+static const u_char halfstep[] = { 0x02, 0x03, 0x01, 0x09,
+ 0x08, 0x0C, 0x04, 0x06
+};
+static const u_char voltages[4][3] = { {0x5C, 0x5A, 0x63},
+{0xE6, 0xB4, 0xBE},
+{0xB4, 0xB4, 0xB4},
+{0x64, 0x50, 0x64}
+};
+
+/* Forward declarations of 1013/1015 functions */
+static void set_ccd_channel_1013 (Mustek_pp_Handle * dev, int channel);
+static void motor_backward_1013 (Mustek_pp_Handle * dev);
+static void return_home_1013 (Mustek_pp_Handle * dev);
+static void motor_forward_1013 (Mustek_pp_Handle * dev);
+static void config_ccd_1013 (Mustek_pp_Handle * dev);
+
+static void set_ccd_channel_1015 (Mustek_pp_Handle * dev, int channel);
+/* static void motor_backward_1015 (Mustek_pp_Handle * dev); */
+static void return_home_1015 (Mustek_pp_Handle * dev, SANE_Bool nowait);
+static void motor_forward_1015 (Mustek_pp_Handle * dev);
+static void config_ccd_1015 (Mustek_pp_Handle * dev);
+
+
+/* These functions are common to all 1013/1015 chipsets */
+
+static void
+set_led (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ sanei_pa4s2_writebyte (dev->fd, 6,
+ (priv->motor_step % 5 == 0 ? 0x03 : 0x13));
+
+}
+
+static void
+set_sti (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ sanei_pa4s2_writebyte (dev->fd, 3, 0);
+ priv->bank_count++;
+ priv->bank_count &= 7;
+
+}
+
+static void
+get_bank_count (Mustek_pp_Handle * dev)
+{
+ u_char val;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ sanei_pa4s2_readbegin (dev->fd, 3);
+ sanei_pa4s2_readbyte (dev->fd, &val);
+ sanei_pa4s2_readend (dev->fd);
+
+ priv->bank_count = (val & 0x07);
+
+}
+
+static void
+reset_bank_count (Mustek_pp_Handle * dev)
+{
+ sanei_pa4s2_writebyte (dev->fd, 6, 7);
+}
+
+static void
+wait_bank_change (Mustek_pp_Handle * dev, int bankcount, int niceload)
+{
+ struct timeval start, end;
+ unsigned long diff;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ int first_time = 1;
+
+ gettimeofday (&start, NULL);
+
+ do
+ {
+ if ((niceload == 0) && (first_time == 0))
+ {
+ usleep (1); /* could be as well sched_yield */
+ first_time = 0;
+ }
+ get_bank_count (dev);
+
+ gettimeofday (&end, NULL);
+ diff = (end.tv_sec * 1000 + end.tv_usec / 1000) -
+ (start.tv_sec * 1000 + start.tv_usec / 1000);
+
+ }
+ while ((priv->bank_count != bankcount) && (diff < priv->wait_bank));
+
+}
+
+static void
+set_dpi_value (Mustek_pp_Handle * dev)
+{
+ u_char val = 0;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x80);
+
+ switch (priv->hwres)
+ {
+ case 100:
+ val = 0x00;
+ break;
+ case 200:
+ val = 0x10;
+ break;
+ case 300:
+ val = 0x20;
+ break;
+ }
+
+
+ if (priv->ccd_type == 1)
+ val |= 0x01;
+
+ sanei_pa4s2_writebyte (dev->fd, 5, val);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x00);
+
+ DBG (5, "set_dpi_value: value 0x%02x\n", val);
+
+}
+
+static void
+set_line_adjust (Mustek_pp_Handle * dev)
+{
+ int adjustline;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ adjustline = (dev->bottomX - dev->topX) * priv->hwres / 300;
+ priv->adjustskip = priv->adjustskip * priv->hwres / 300;
+
+ DBG (5, "set_line_adjust: ppl %u (%u), adjust %u, skip %u\n",
+ dev->params.pixels_per_line, (dev->bottomX - dev->topX), adjustline,
+ priv->adjustskip);
+
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x11);
+ sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + priv->adjustskip) >> 8);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x21);
+ sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + priv->adjustskip) & 0xFF);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x01);
+
+}
+
+static void
+set_lamp (Mustek_pp_Handle * dev, int lamp_on)
+{
+
+ int ctr;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0xC3);
+
+ for (ctr = 0; ctr < 3; ctr++)
+ {
+ sanei_pa4s2_writebyte (dev->fd, 6, (lamp_on ? 0x47 : 0x57));
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x77);
+ }
+
+ priv->motor_step = lamp_on;
+
+ set_led (dev);
+
+}
+
+static void
+send_voltages (Mustek_pp_Handle * dev)
+{
+
+ int voltage, sel = 8, ctr;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ switch (priv->ccd_type)
+ {
+ case 0:
+ voltage = 0;
+ break;
+ case 1:
+ voltage = 1;
+ break;
+ default:
+ voltage = 2;
+ break;
+ }
+
+ for (ctr = 0; ctr < 3; ctr++)
+ {
+
+ sel <<= 1;
+ sanei_pa4s2_writebyte (dev->fd, 6, sel);
+ sanei_pa4s2_writebyte (dev->fd, 5, voltages[voltage][ctr]);
+
+ }
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x00);
+
+}
+
+static int
+compar (const void *a, const void *b)
+{
+ return (signed int) (*(const SANE_Byte *) a) -
+ (signed int) (*(const SANE_Byte *) b);
+}
+
+static void
+set_ccd_channel_101x (Mustek_pp_Handle * dev, int channel)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ switch (priv->asic)
+ {
+ case CCD300_ASIC1013:
+ set_ccd_channel_1013 (dev, channel);
+ break;
+
+ case CCD300_ASIC1015:
+ set_ccd_channel_1015 (dev, channel);
+ break;
+ }
+}
+
+static void
+motor_forward_101x (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ switch (priv->asic)
+ {
+ case CCD300_ASIC1013:
+ motor_forward_1013 (dev);
+ break;
+
+ case CCD300_ASIC1015:
+ motor_forward_1015 (dev);
+ break;
+ }
+}
+
+static void
+motor_backward_101x (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ switch (priv->asic)
+ {
+ case CCD300_ASIC1013:
+ motor_backward_1013 (dev);
+ break;
+
+ case CCD300_ASIC1015:
+/* motor_backward_1015 (dev); */
+ break;
+ }
+}
+
+static void
+move_motor_101x (Mustek_pp_Handle * dev, int forward)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ if (forward == SANE_TRUE)
+ motor_forward_101x (dev);
+ else
+ motor_backward_101x (dev);
+
+ wait_bank_change (dev, priv->bank_count, 1);
+ reset_bank_count (dev);
+}
+
+
+static void
+config_ccd_101x (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ switch (priv->asic)
+ {
+ case CCD300_ASIC1013:
+ config_ccd_1013 (dev);
+ break;
+
+ case CCD300_ASIC1015:
+ config_ccd_1015 (dev);
+ break;
+ }
+}
+
+
+
+static void
+read_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf, SANE_Int pixel,
+ SANE_Int RefBlack, SANE_Byte * calib, SANE_Int * gamma)
+{
+
+ SANE_Byte *cal = calib;
+ u_char color;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ int ctr, skips = priv->adjustskip + 1, cval;
+
+ if (pixel <= 0)
+ return;
+
+ sanei_pa4s2_readbegin (dev->fd, 1);
+
+
+ if (priv->hwres == dev->res)
+ {
+
+ while (skips--)
+ sanei_pa4s2_readbyte (dev->fd, &color);
+
+ for (ctr = 0; ctr < pixel; ctr++)
+ {
+
+ sanei_pa4s2_readbyte (dev->fd, &color);
+
+ cval = color;
+
+ if (cval < RefBlack)
+ cval = 0;
+ else
+ cval -= RefBlack;
+
+ if (cal)
+ {
+ if (cval >= cal[ctr])
+ cval = 0xFF;
+ else
+ {
+ cval <<= 8;
+ cval /= (int) cal[ctr];
+ }
+ }
+
+ if (gamma)
+ cval = gamma[cval];
+
+ buf[ctr] = cval;
+
+ }
+
+ }
+ else
+ {
+
+ int pos = 0, bpos = 0;
+
+ while (skips--)
+ sanei_pa4s2_readbyte (dev->fd, &color);
+
+ ctr = 0;
+
+ do
+ {
+
+ sanei_pa4s2_readbyte (dev->fd, &color);
+
+ cval = color;
+
+ if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT))
+ {
+ ctr++;
+ continue;
+ }
+
+ ctr++;
+ pos += priv->res_step;
+
+
+ if (cval < RefBlack)
+ cval = 0;
+ else
+ cval -= RefBlack;
+
+ if (cal)
+ {
+ if (cval >= cal[bpos])
+ cval = 0xFF;
+ else
+ {
+ cval <<= 8;
+ cval /= (int) cal[bpos];
+ }
+ }
+
+ if (gamma)
+ cval = gamma[cval];
+
+ buf[bpos++] = cval;
+
+ }
+ while (bpos < pixel);
+
+ }
+
+ sanei_pa4s2_readend (dev->fd);
+
+}
+
+static void
+read_average_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf, int pixel,
+ int RefBlack)
+{
+
+ SANE_Byte lbuf[4][CCD300_MAXHSIZE * 2];
+ int ctr, sum;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ for (ctr = 0; ctr < 4; ctr++)
+ {
+
+ wait_bank_change (dev, priv->bank_count, 1);
+ read_line_101x (dev, lbuf[ctr], pixel, RefBlack, NULL, NULL);
+ reset_bank_count (dev);
+ if (ctr < 3)
+ set_sti (dev);
+
+ }
+
+ for (ctr = 0; ctr < pixel; ctr++)
+ {
+
+ sum = lbuf[0][ctr] + lbuf[1][ctr] + lbuf[2][ctr] + lbuf[3][ctr];
+
+ buf[ctr] = (sum / 4);
+
+ }
+
+}
+
+static void
+find_black_side_edge_101x (Mustek_pp_Handle * dev)
+{
+ SANE_Byte buf[CCD300_MAXHSIZE * 2];
+ SANE_Byte blackposition[5];
+ int pos = 0, ctr, blackpos;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+
+ for (ctr = 0; ctr < 20; ctr++)
+ {
+
+ motor_forward_101x (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+ read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
+ reset_bank_count (dev);
+
+ priv->ref_black = priv->ref_red = priv->ref_green = priv->ref_blue =
+ buf[0];
+
+ blackpos = CCD300_MAXHSIZE / 4;
+
+ while ((abs (buf[blackpos] - buf[0]) >= 15) && (blackpos > 0))
+ blackpos--;
+
+ if (blackpos > 1)
+ blackposition[pos++] = blackpos;
+
+ if (pos == 5)
+ break;
+
+ }
+
+ blackpos = 0;
+
+ for (ctr = 0; ctr < pos; ctr++)
+ if (blackposition[ctr] > blackpos)
+ blackpos = blackposition[ctr];
+
+ if (blackpos < 0x66)
+ blackpos = 0x6A;
+
+ priv->blackpos = blackpos;
+ priv->saved_skipcount = (blackpos + 12) & 0xFF;
+
+}
+
+static void
+min_color_levels_101x (Mustek_pp_Handle * dev)
+{
+
+ SANE_Byte buf[CCD300_MAXHSIZE * 2];
+ int ctr, sum = 0;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ for (ctr = 0; ctr < 8; ctr++)
+ {
+
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_RED);
+ set_sti (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+
+ read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
+
+ reset_bank_count (dev);
+
+ sum += buf[3];
+
+ }
+
+ priv->ref_red = sum / 8;
+
+ sum = 0;
+
+ for (ctr = 0; ctr < 8; ctr++)
+ {
+
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN);
+ set_sti (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+
+ read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
+
+ reset_bank_count (dev);
+
+ sum += buf[3];
+
+ }
+
+ priv->ref_green = sum / 8;
+
+ sum = 0;
+
+ for (ctr = 0; ctr < 8; ctr++)
+ {
+
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE);
+ set_sti (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+
+ read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL);
+
+ reset_bank_count (dev);
+
+ sum += buf[3];
+
+ }
+
+ priv->ref_blue = sum / 8;
+
+}
+
+
+static void
+max_color_levels_101x (Mustek_pp_Handle * dev)
+{
+
+ int ctr, line, sum;
+ SANE_Byte rbuf[32][CCD300_MAXHSIZE * 2];
+ SANE_Byte gbuf[32][CCD300_MAXHSIZE * 2];
+ SANE_Byte bbuf[32][CCD300_MAXHSIZE * 2];
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ SANE_Byte maxbuf[32];
+
+ for (ctr = 0; ctr < 32; ctr++)
+ {
+
+ if (dev->mode == MODE_COLOR)
+ {
+
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_RED);
+ motor_forward_101x (dev);
+
+ read_average_line_101x (dev, rbuf[ctr], dev->params.pixels_per_line,
+ priv->ref_red);
+
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN);
+ set_sti (dev);
+
+ read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line,
+ priv->ref_green);
+
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE);
+ set_sti (dev);
+
+ read_average_line_101x (dev, bbuf[ctr], dev->params.pixels_per_line,
+ priv->ref_blue);
+
+ }
+ else
+ {
+
+ priv->channel = CCD300_CHANNEL_GRAY;
+
+ motor_forward_101x (dev);
+
+ read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line,
+ priv->ref_black);
+
+ }
+
+ }
+
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ {
+ for (line = 0; line < 32; line++)
+ maxbuf[line] = gbuf[line][ctr];
+
+ qsort (maxbuf, 32, sizeof (maxbuf[0]), compar);
+
+ sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7];
+
+ priv->calib_g[ctr] = sum / 4;
+
+ }
+
+ if (dev->mode == MODE_COLOR)
+ {
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ {
+ for (line = 0; line < 32; line++)
+ maxbuf[line] = rbuf[line][ctr];
+
+ qsort (maxbuf, 32, sizeof (maxbuf[0]), compar);
+
+ sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7];
+
+ priv->calib_r[ctr] = sum / 4;
+
+ }
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ {
+ for (line = 0; line < 32; line++)
+ maxbuf[line] = bbuf[line][ctr];
+
+ qsort (maxbuf, 32, sizeof (maxbuf[0]), compar);
+
+ sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7];
+
+ priv->calib_b[ctr] = sum / 4;
+
+ }
+
+ }
+
+}
+
+static void
+find_black_top_edge_101x (Mustek_pp_Handle * dev)
+{
+
+ int lines = 0, ctr, pos;
+ SANE_Byte buf[CCD300_MAXHSIZE * 2];
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->channel = CCD300_CHANNEL_GRAY;
+
+ do
+ {
+
+ motor_forward_101x (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+
+ read_line_101x (dev, buf, CCD300_MAXHSIZE, priv->ref_black, NULL, NULL);
+
+ reset_bank_count (dev);
+
+ pos = 0;
+
+ for (ctr = priv->blackpos; ctr > priv->blackpos - 10; ctr--)
+ if (buf[ctr] <= 15)
+ pos++;
+
+ }
+ while ((pos >= 8) && (lines++ < 67));
+
+}
+
+static void
+calibrate_device_101x (Mustek_pp_Handle * dev)
+{
+
+ int saved_ppl = dev->params.pixels_per_line, ctr;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->saved_mode = dev->mode;
+ priv->saved_invert = dev->invert;
+ priv->saved_skipcount = priv->skipcount;
+ priv->saved_skipimagebyte = priv->skipimagebytes;
+ priv->saved_adjustskip = priv->adjustskip;
+ priv->saved_res = dev->res;
+ priv->saved_hwres = priv->hwres;
+ priv->saved_res_step = priv->res_step;
+ priv->saved_line_step = priv->line_step;
+ priv->saved_channel = priv->channel;
+
+ dev->params.pixels_per_line = CCD300_MAXHSIZE;
+ priv->hwres = dev->res = 300;
+ dev->mode = MODE_GRAYSCALE;
+ priv->skipcount = priv->skipimagebytes = 0;
+ dev->invert = SANE_FALSE;
+ priv->channel = CCD300_CHANNEL_GRAY;
+
+ config_ccd_101x (dev);
+ get_bank_count (dev);
+
+ find_black_side_edge_101x (dev);
+
+ for (ctr = 0; ctr < 4; ctr++)
+ move_motor_101x (dev, SANE_TRUE);
+
+ dev->mode = priv->saved_mode;
+ dev->invert = priv->saved_invert;
+ priv->skipcount = priv->saved_skipcount;
+ priv->skipimagebytes = priv->saved_skipimagebyte;
+ priv->adjustskip = priv->saved_adjustskip;
+ dev->res = priv->saved_res;
+ priv->hwres = priv->saved_hwres;
+ priv->res_step = priv->saved_res_step;
+ priv->line_step = priv->saved_line_step;
+ priv->channel = priv->saved_channel;
+
+ priv->hwres = dev->res = 300;
+ priv->skipcount = priv->skipimagebytes = 0;
+ dev->invert = SANE_FALSE;
+
+ config_ccd_101x (dev);
+ get_bank_count (dev);
+
+ if ((dev->mode == MODE_COLOR) && (priv->ccd_type != 0))
+ min_color_levels_101x (dev);
+
+ dev->mode = priv->saved_mode;
+ dev->invert = priv->saved_invert;
+ priv->skipcount = priv->saved_skipcount;
+ priv->skipimagebytes = priv->saved_skipimagebyte;
+ priv->adjustskip = priv->saved_adjustskip;
+ dev->res = priv->saved_res;
+ priv->hwres = priv->saved_hwres;
+ priv->res_step = priv->saved_res_step;
+ priv->line_step = priv->saved_line_step;
+ priv->channel = priv->saved_channel;
+
+ dev->params.pixels_per_line = saved_ppl;
+ dev->invert = SANE_FALSE;
+
+ config_ccd_101x (dev);
+ get_bank_count (dev);
+
+ max_color_levels_101x (dev);
+
+ dev->params.pixels_per_line = CCD300_MAXHSIZE;
+ dev->mode = MODE_GRAYSCALE;
+ priv->hwres = dev->res = 300;
+ priv->skipcount = priv->skipimagebytes = 0;
+ dev->invert = SANE_FALSE;
+
+ config_ccd_101x (dev);
+ get_bank_count (dev);
+
+ find_black_top_edge_101x (dev);
+
+ dev->mode = priv->saved_mode;
+ dev->invert = priv->saved_invert;
+ priv->skipcount = priv->saved_skipcount;
+ priv->skipimagebytes = priv->saved_skipimagebyte;
+ priv->adjustskip = priv->saved_adjustskip;
+ dev->res = priv->saved_res;
+ priv->hwres = priv->saved_hwres;
+ priv->res_step = priv->saved_res_step;
+ priv->line_step = priv->saved_line_step;
+ priv->channel = priv->saved_channel;
+
+ dev->params.pixels_per_line = saved_ppl;
+
+ config_ccd_101x (dev);
+ get_bank_count (dev);
+
+}
+
+static void
+get_grayscale_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf)
+{
+
+ int skips;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->line_diff += SANE_FIX (300.0 / (float) dev->res);
+
+ skips = (priv->line_diff >> SANE_FIXED_SCALE_SHIFT);
+
+ while (--skips)
+ {
+ motor_forward_101x (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+ reset_bank_count (dev);
+ }
+
+ priv->line_diff &= 0xFFFF;
+
+ motor_forward_101x (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+
+ read_line_101x (dev, buf, dev->params.pixels_per_line, priv->ref_black,
+ priv->calib_g, NULL);
+
+ reset_bank_count (dev);
+
+}
+
+static void
+get_lineart_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf)
+{
+
+ int ctr;
+ SANE_Byte gbuf[CCD300_MAXHSIZE * 2];
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ get_grayscale_line_101x (dev, gbuf);
+
+ memset (buf, 0xFF, dev->params.bytes_per_line);
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ buf[ctr >> 3] ^= ((gbuf[ctr] > priv->bw) ? (1 << (7 - ctr % 8)) : 0);
+
+}
+
+static void
+get_color_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf)
+{
+
+ SANE_Byte *red, *blue, *src, *dest;
+ int gotline = 0, ctr;
+ int gored, goblue, gogreen;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ int step = priv->line_step;
+
+ do
+ {
+
+ red = priv->red[priv->redline];
+ blue = priv->blue[priv->blueline];
+
+ priv->ccd_line++;
+
+ if ((priv->rdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line)
+ {
+ gored = 1;
+ priv->rdiff += step;
+ }
+ else
+ gored = 0;
+
+ if ((priv->bdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line)
+ {
+ goblue = 1;
+ priv->bdiff += step;
+ }
+ else
+ goblue = 0;
+
+ if ((priv->gdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line)
+ {
+ gogreen = 1;
+ priv->gdiff += step;
+ }
+ else
+ gogreen = 0;
+
+ if (!gored && !goblue && !gogreen)
+ {
+ motor_forward_101x (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+ reset_bank_count (dev);
+ if (priv->ccd_line >= (priv->line_step >> SANE_FIXED_SCALE_SHIFT))
+ priv->redline = ++priv->redline % priv->green_offs;
+ if (priv->ccd_line >=
+ priv->blue_offs + (priv->line_step >> SANE_FIXED_SCALE_SHIFT))
+ priv->blueline = ++priv->blueline % priv->blue_offs;
+ continue;
+ }
+
+ if (gored)
+ priv->channel = CCD300_CHANNEL_RED;
+ else if (goblue)
+ priv->channel = CCD300_CHANNEL_BLUE;
+ else
+ priv->channel = CCD300_CHANNEL_GREEN;
+
+ motor_forward_101x (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+
+ if (priv->ccd_line >= priv->green_offs && gogreen)
+ {
+ src = red;
+ dest = buf;
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ {
+ *dest = *src++;
+ dest += 3;
+ }
+ }
+
+ if (gored)
+ {
+
+ read_line_101x (dev, red, dev->params.pixels_per_line,
+ priv->ref_red, priv->calib_r, NULL);
+
+ reset_bank_count (dev);
+
+ }
+
+ priv->redline = ++priv->redline % priv->green_offs;
+
+ if (priv->ccd_line >= priv->green_offs && gogreen)
+ {
+ src = blue;
+ dest = buf + 2;
+
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ {
+ *dest = *src++;
+ dest += 3;
+ }
+
+ }
+
+ if (goblue)
+ {
+ if (gored)
+ {
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE);
+ set_sti (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+ }
+
+ read_line_101x (dev, blue, dev->params.pixels_per_line,
+ priv->ref_blue, priv->calib_b, NULL);
+
+ reset_bank_count (dev);
+
+ }
+
+ if (priv->ccd_line >=
+ priv->blue_offs + (priv->line_step >> SANE_FIXED_SCALE_SHIFT))
+ priv->blueline = ++priv->blueline % priv->blue_offs;
+
+ if (gogreen)
+ {
+
+ if (gored || goblue)
+ {
+ set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN);
+ set_sti (dev);
+ wait_bank_change (dev, priv->bank_count, 1);
+ }
+
+ read_line_101x (dev, priv->green, dev->params.pixels_per_line,
+ priv->ref_green, priv->calib_g, NULL);
+
+ reset_bank_count (dev);
+
+ src = priv->green;
+ dest = buf + 1;
+
+ for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++)
+ {
+ *dest = *src++;
+ dest += 3;
+ }
+
+ gotline = 1;
+ }
+
+ }
+ while (!gotline);
+
+}
+
+
+
+/* these functions are for the 1013 chipset */
+
+static void
+set_ccd_channel_1013 (Mustek_pp_Handle * dev, int channel)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ priv->channel = channel;
+ sanei_pa4s2_writebyte (dev->fd, 6, chan_codes_1013[channel]);
+}
+
+static void
+motor_backward_1013 (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->motor_step++;
+ set_led (dev);
+
+ if (priv->motor_phase > 3)
+ priv->motor_phase = 3;
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x62);
+ sanei_pa4s2_writebyte (dev->fd, 5, fullstep[priv->motor_phase]);
+
+ priv->motor_phase = (priv->motor_phase == 0 ? 3 : priv->motor_phase - 1);
+
+ set_ccd_channel_1013 (dev, priv->channel);
+ set_sti (dev);
+
+}
+
+static void
+return_home_1013 (Mustek_pp_Handle * dev)
+{
+ u_char ishome;
+ int ctr;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ /* 1013 can't return home all alone, nowait ignored */
+
+ for (ctr = 0; ctr < 4500; ctr++)
+ {
+
+ /* check_is_home_1013 */
+ sanei_pa4s2_readbegin (dev->fd, 2);
+ sanei_pa4s2_readbyte (dev->fd, &ishome);
+ sanei_pa4s2_readend (dev->fd);
+
+ /* yes, it should be is_not_home */
+ if ((ishome & 1) == 0)
+ break;
+
+ motor_backward_1013 (dev);
+ wait_bank_change (dev, priv->bank_count, 0);
+ reset_bank_count (dev);
+
+ }
+
+}
+
+static void
+motor_forward_1013 (Mustek_pp_Handle * dev)
+{
+
+ int ctr;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->motor_step++;
+ set_led (dev);
+
+ for (ctr = 0; ctr < 2; ctr++)
+ {
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x62);
+ sanei_pa4s2_writebyte (dev->fd, 5, halfstep[priv->motor_phase]);
+
+ priv->motor_phase =
+ (priv->motor_phase == 7 ? 0 : priv->motor_phase + 1);
+
+ }
+
+ set_ccd_channel_1013 (dev, priv->channel);
+ set_sti (dev);
+}
+
+
+
+static void
+config_ccd_1013 (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ if (dev->res != 0)
+ priv->res_step = SANE_FIX ((float) priv->hwres / (float) dev->res);
+
+ set_dpi_value (dev);
+
+ /* set_start_channel_1013 (dev); */
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x05);
+
+ switch (dev->mode)
+ {
+ case MODE_BW:
+ case MODE_GRAYSCALE:
+ priv->channel = CCD300_CHANNEL_GRAY;
+ break;
+
+ case MODE_COLOR:
+ priv->channel = CCD300_CHANNEL_RED;
+ break;
+
+ }
+
+ set_ccd_channel_1013 (dev, priv->channel);
+
+ /* set_invert_1013 (dev); */
+
+ sanei_pa4s2_writebyte (dev->fd, 6,
+ (dev->invert == SANE_TRUE ? 0x04 : 0x14));
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x37);
+ reset_bank_count (dev);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x27);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x67);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x17);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x77);
+
+ /* set_initial_skip_1013 (dev); */
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x41);
+
+ priv->adjustskip = priv->skipcount + priv->skipimagebytes;
+
+ DBG (5, "config_ccd_1013: adjustskip %u\n", priv->adjustskip);
+
+ sanei_pa4s2_writebyte (dev->fd, 5, priv->adjustskip / 16 + 2);
+
+ priv->adjustskip %= 16;
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x81);
+ sanei_pa4s2_writebyte (dev->fd, 5, 0x70);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x01);
+
+
+ set_line_adjust (dev);
+
+ get_bank_count (dev);
+
+}
+
+/* these functions are for the 1015 chipset */
+
+
+static void
+motor_control_1015 (Mustek_pp_Handle * dev, u_char control)
+{
+ u_char val;
+
+ DBG (5, "motor_controll_1015: control code 0x%02x\n",
+ (unsigned int) control);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0xF6);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x22);
+ sanei_pa4s2_writebyte (dev->fd, 5, control);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x02);
+
+ do
+ {
+
+ sanei_pa4s2_readbegin (dev->fd, 2);
+ sanei_pa4s2_readbyte (dev->fd, &val);
+ sanei_pa4s2_readend (dev->fd);
+
+ }
+ while ((val & 0x08) != 0);
+
+}
+
+static void
+return_home_1015 (Mustek_pp_Handle * dev, SANE_Bool nowait)
+{
+
+ u_char ishome, control = 0xC3;
+
+ motor_control_1015 (dev, control);
+
+ do
+ {
+
+ /* check_is_home_1015 */
+ sanei_pa4s2_readbegin (dev->fd, 2);
+ sanei_pa4s2_readbyte (dev->fd, &ishome);
+ sanei_pa4s2_readend (dev->fd);
+
+ if (nowait)
+ break;
+
+ usleep (1000); /* much nicer load */
+
+ }
+ while ((ishome & 2) == 0);
+
+}
+
+static void
+motor_forward_1015 (Mustek_pp_Handle * dev)
+{
+ u_char control = 0x1B;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->motor_step++;
+ set_led (dev);
+
+
+ motor_control_1015 (dev, control);
+
+ set_ccd_channel_1015 (dev, priv->channel);
+ set_sti (dev);
+
+}
+
+/*
+static void
+motor_backward_1015 (Mustek_pp_Handle * dev)
+{
+ u_char control = 0x43;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->motor_step++;
+
+ set_led (dev);
+
+ switch (priv->ccd_type)
+ {
+ case 1:
+ control = 0x1B;
+ break;
+
+ default:
+ control = 0x43;
+ break;
+ }
+
+ motor_control_1015 (dev, control);
+
+ set_ccd_channel_1015 (dev, priv->channel);
+ set_sti (dev);
+
+}
+*/
+
+
+static void
+set_ccd_channel_1015 (Mustek_pp_Handle * dev, int channel)
+{
+
+ u_char chancode = chan_codes_1015[channel];
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->channel = channel;
+
+ priv->image_control &= 0x34;
+ chancode |= priv->image_control;
+
+
+ priv->image_control = chancode;
+
+ sanei_pa4s2_writebyte (dev->fd, 6, chancode);
+
+}
+
+
+static void
+config_ccd_1015 (Mustek_pp_Handle * dev)
+{
+
+ u_char val;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ if (dev->res != 0)
+ priv->res_step = SANE_FIX ((float) priv->hwres / (float) dev->res);
+
+
+ set_dpi_value (dev);
+
+ priv->image_control = 4;
+
+ /* set_start_channel_1015 (dev); */
+
+ switch (dev->mode)
+ {
+ case MODE_BW:
+ case MODE_GRAYSCALE:
+ priv->channel = CCD300_CHANNEL_GRAY;
+ break;
+
+ case MODE_COLOR:
+ priv->channel = CCD300_CHANNEL_RED;
+ break;
+
+ }
+
+ set_ccd_channel_1015 (dev, priv->channel);
+
+
+ /* set_invert_1015 (dev); */
+
+ priv->image_control &= 0xE4;
+
+ if (dev->invert == SANE_FALSE)
+ priv->image_control |= 0x10;
+
+
+ sanei_pa4s2_writebyte (dev->fd, 6, priv->image_control);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x23);
+ sanei_pa4s2_writebyte (dev->fd, 5, 0x00);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x43);
+
+ switch (priv->ccd_type)
+ {
+ case 1:
+ val = 0x6B;
+ break;
+ case 4:
+ val = 0x9F;
+ break;
+ default:
+ val = 0x92;
+ break;
+ }
+
+ sanei_pa4s2_writebyte (dev->fd, 5, val);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x03);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x37);
+ reset_bank_count (dev);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x27);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x67);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x17);
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x77);
+
+ /* set_initial_skip_1015 (dev); */
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x41);
+
+ priv->adjustskip = priv->skipcount + priv->skipimagebytes;
+
+ /* if (dev->CCD.mode == MODE_COLOR)
+ dev->CCD.adjustskip <<= 3; */
+
+
+ sanei_pa4s2_writebyte (dev->fd, 5, priv->adjustskip / 32 + 1);
+
+ priv->adjustskip %= 32;
+
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x81);
+
+ /* expose time */
+ switch (priv->ccd_type)
+ {
+ case 1:
+
+ val = 0xA8;
+ break;
+ case 0:
+ val = 0x8A;
+ break;
+ default:
+ val = 0xA8;
+ break;
+ }
+
+ sanei_pa4s2_writebyte (dev->fd, 5, val);
+
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x01);
+
+
+ set_line_adjust (dev);
+
+ get_bank_count (dev);
+
+}
+
+
+/* these functions are interfaces only */
+static void
+config_ccd (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ DBG (5, "config_ccd: %d dpi, mode %d, invert %d, size %d\n",
+ priv->hwres, dev->mode, dev->invert, dev->params.pixels_per_line);
+
+ switch (priv->asic)
+ {
+ case CCD300_ASIC1013:
+ config_ccd_1013 (dev);
+ break;
+
+ case CCD300_ASIC1015:
+ config_ccd_1015 (dev);
+ break;
+ }
+
+}
+
+static void
+return_home (Mustek_pp_Handle * dev, SANE_Bool nowait)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ priv->saved_mode = dev->mode;
+ priv->saved_invert = dev->invert;
+ priv->saved_skipcount = priv->skipcount;
+ priv->saved_skipimagebyte = priv->skipimagebytes;
+ priv->saved_adjustskip = priv->adjustskip;
+ priv->saved_res = dev->res;
+ priv->saved_hwres = priv->hwres;
+ priv->saved_res_step = priv->res_step;
+ priv->saved_line_step = priv->line_step;
+ priv->saved_channel = priv->channel;
+
+
+ priv->hwres = dev->res = 100;
+ dev->mode = MODE_GRAYSCALE;
+
+ priv->skipcount = priv->skipimagebytes = 0;
+
+ config_ccd (dev);
+
+ switch (priv->asic)
+ {
+ case CCD300_ASIC1013:
+ return_home_1013 (dev);
+ break;
+
+ case CCD300_ASIC1015:
+ return_home_1015 (dev, nowait);
+ break;
+ }
+
+
+ dev->mode = priv->saved_mode;
+ dev->invert = priv->saved_invert;
+ priv->skipcount = priv->saved_skipcount;
+ priv->skipimagebytes = priv->saved_skipimagebyte;
+ priv->adjustskip = priv->saved_adjustskip;
+ dev->res = priv->saved_res;
+ priv->hwres = priv->saved_hwres;
+ priv->res_step = priv->saved_res_step;
+ priv->line_step = priv->saved_line_step;
+ priv->channel = priv->saved_channel;
+ priv->motor_step = 0;
+
+ config_ccd (dev);
+}
+
+static void
+lamp (Mustek_pp_Handle * dev, int lamp_on)
+{
+
+ set_lamp (dev, lamp_on);
+
+}
+
+static void
+set_voltages (Mustek_pp_Handle * dev)
+{
+ send_voltages (dev);
+}
+
+static void
+move_motor (Mustek_pp_Handle * dev, int count, int forward)
+{
+
+ int ctr;
+
+ DBG (5, "move_motor: %u steps (%s)\n", count,
+ (forward == SANE_TRUE ? "forward" : "backward"));
+
+
+ for (ctr = 0; ctr < count; ctr++)
+ {
+
+ move_motor_101x (dev, forward);
+
+ }
+
+
+}
+
+static void
+calibrate (Mustek_pp_Handle * dev)
+{
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ DBG (5, "calibrate entered (asic = 0x%02x)\n", priv->asic);
+
+ calibrate_device_101x (dev);
+
+ DBG (5, "calibrate: ref_black %d, blackpos %d\n",
+ priv->ref_black, priv->blackpos);
+
+}
+
+
+static void
+get_lineart_line (Mustek_pp_Handle * dev, SANE_Byte * buf)
+{
+ get_lineart_line_101x (dev, buf);
+}
+
+static void
+get_grayscale_line (Mustek_pp_Handle * dev, SANE_Byte * buf)
+{
+
+ get_grayscale_line_101x (dev, buf);
+}
+
+static void
+get_color_line (Mustek_pp_Handle * dev, SANE_Byte * buf)
+{
+
+ get_color_line_101x (dev, buf);
+
+}
+
+
+static SANE_Status
+ccd300_init (SANE_Int options, SANE_String_Const port,
+ SANE_String_Const name, SANE_Attach_Callback attach)
+{
+ SANE_Status status;
+ unsigned char asic, ccd;
+ int fd;
+
+ if (options != CAP_NOTHING)
+ {
+ DBG (1, "ccd300_init: called with unknown options (%#02x)\n", options);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* try to attach to he supplied port */
+ status = sanei_pa4s2_open (port, &fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "ccd300_init: couldn't attach to port ``%s'' (%s)\n",
+ port, sane_strstatus (status));
+ return status;
+ }
+
+ sanei_pa4s2_enable (fd, SANE_TRUE);
+ sanei_pa4s2_readbegin (fd, 0);
+ sanei_pa4s2_readbyte (fd, &asic);
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_readbegin (fd, 2);
+ sanei_pa4s2_readbyte (fd, &ccd);
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ sanei_pa4s2_close (fd);
+
+ if (asic != CCD300_ASIC1013 && asic != CCD300_ASIC1015)
+ {
+ DBG (2, "ccd300_init: scanner not recognized (unknown ASIC id %#02x)\n",
+ asic);
+ return SANE_STATUS_INVAL;
+ }
+
+ ccd &= (asic == CCD300_ASIC1013 ? 0x04 : 0x05);
+
+ DBG (3, "ccd_init: found scanner on port ``%s'' (ASIC id %#02x, CCD %d)\n",
+ port, asic, ccd);
+
+ return attach (port, name, MUSTEK_PP_CCD300, options);
+
+}
+
+static void
+ccd300_capabilities (SANE_Int info, SANE_String * model,
+ SANE_String * vendor, SANE_String * type,
+ SANE_Int * maxres, SANE_Int * minres,
+ SANE_Int * maxhsize, SANE_Int * maxvsize,
+ SANE_Int * caps)
+{
+ *model = strdup ("600 III EP Plus");
+ *vendor = strdup ("Mustek");
+ *type = strdup ("flatbed (CCD 300 dpi)");
+ DBG (3,
+ "ccd300_capabilities: 600 III EP Plus flatbed CCD (300 dpi) scanner\n");
+
+ *maxres = 300;
+ *minres = 50;
+ *maxhsize = CCD300_MAXHSIZE;
+ *maxvsize = CCD300_MAXVSIZE;
+ *caps = info | CAP_INVERT | CAP_LAMP_OFF;
+}
+
+static SANE_Status
+ccd300_open (SANE_String port, SANE_Int caps, SANE_Int * fd)
+{
+ SANE_Status status;
+
+ if (caps & ~(CAP_NOTHING | CAP_INVERT | CAP_LAMP_OFF))
+ {
+ DBG (1, "ccd300_open: called with unknonw capabilities (%#02x)\n",
+ caps);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "ccd300_open: called for port ``%s''\n", port);
+
+ status = sanei_pa4s2_open (port, fd);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (2, "ccd300_open: open failed (%s)\n", sane_strstatus (status));
+
+ return status;
+}
+
+static void
+ccd300_setup (SANE_Handle handle)
+{
+ Mustek_pp_Handle *dev = handle;
+ mustek_pp_ccd300_priv *priv;
+ unsigned char asic, ccd;
+
+ DBG (3, "ccd300_setup: called for port ``%s''\n", dev->dev->port);
+
+ if ((priv = malloc (sizeof (mustek_pp_ccd300_priv))) == NULL)
+ {
+ DBG (1, "ccd300_setup: not enough memory\n");
+ return; /* can you here the shit hitting the fan? */
+ }
+
+ dev->priv = priv;
+ memset (priv, 0, sizeof (mustek_pp_ccd300_priv));
+
+ priv->bw = 128;
+ priv->wait_bank = 700;
+ priv->top = 47;
+
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+
+ sanei_pa4s2_readbegin (dev->fd, 0);
+ sanei_pa4s2_readbyte (dev->fd, &asic);
+ sanei_pa4s2_readend (dev->fd);
+ sanei_pa4s2_readbegin (dev->fd, 2);
+ sanei_pa4s2_readbyte (dev->fd, &ccd);
+ sanei_pa4s2_readend (dev->fd);
+ ccd &= (asic == CCD300_ASIC1013 ? 0x04 : 0x05);
+ priv->asic = asic;
+ priv->ccd_type = ccd;
+
+ return_home (dev, SANE_TRUE);
+ lamp (dev, SANE_TRUE);
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ dev->lamp_on = time (NULL);
+ dev->res = priv->hwres = 300;
+ dev->mode = MODE_COLOR;
+}
+
+static void
+ccd300_close (SANE_Handle handle)
+{
+
+ Mustek_pp_Handle *dev = handle;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ DBG (3, "ccd300_close: called for port ``%s''\n", dev->dev->port);
+
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+ lamp (dev, SANE_FALSE);
+ return_home (dev, SANE_FALSE);
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+
+ sanei_pa4s2_close (dev->fd);
+ free (priv);
+
+ DBG (3, "ccd300_close: device shut down and all buffers freed\n");
+}
+
+static SANE_Status
+ccd300_config (SANE_Handle handle, SANE_String_Const optname,
+ SANE_String_Const optval)
+{
+ Mustek_pp_Handle *dev = handle;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ int value = -1;
+
+ DBG (3, "ccd300_config: called for port ``%s'' (%s%s%s)\n",
+ dev->dev->port,
+ optname, (optval ? " = " : ""), (optval ? optval : ""));
+
+ if (!strcmp (optname, "bw"))
+ {
+
+ if (!optval)
+ {
+ DBG (1, "ccd300_config: missing value for option ``bw''\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* ok, ok, should be strtol... know what? send me a patch. */
+ value = atoi (optval);
+
+ if ((value < 0) || (value > 255))
+ {
+ DBG (1,
+ "ccd300_config: value ``%s'' for option ``bw'' is out of range (0 <= bw <= 255)\n",
+ optval);
+ return SANE_STATUS_INVAL;
+ }
+
+ priv->bw = value;
+
+ }
+ else if (!strcmp (optname, "waitbank"))
+ {
+
+ if (!optval)
+ {
+ DBG (1, "ccd300_config: missing value for option ``waitbank''\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ value = atoi (optval);
+
+ if (value < 0)
+ {
+ DBG (1,
+ "ccd300_config: value ``%s'' for option ``waitbank'' is out of range (>= 0)\n",
+ optval);
+ return SANE_STATUS_INVAL;
+ }
+
+ priv->wait_bank = value;
+ }
+ else if (!strcmp (optname, "top"))
+ {
+
+ if (!optval)
+ {
+ DBG (1, "ccd300_config: missing value for option ``top''\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ value = atoi (optval);
+
+ if (value < 0)
+ {
+ DBG (1,
+ "ccd300_config: value ``%s'' for option ``top'' is out of range (>= 0)\n",
+ optval);
+ return SANE_STATUS_INVAL;
+ }
+
+ priv->top = value;
+ }
+ else
+ {
+ DBG (1, "ccd300_config: unkown option ``%s''", optname);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+
+}
+
+static void
+ccd300_stop (SANE_Handle handle)
+{
+ Mustek_pp_Handle *dev = handle;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+ int cnt;
+
+ DBG (3, "ccd300_stop: stopping scan operating on port ``%s''\n",
+ dev->dev->port);
+
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+ return_home (dev, SANE_TRUE);
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+
+ free (priv->calib_r);
+ free (priv->calib_g);
+ free (priv->calib_b);
+
+ if (priv->red)
+ {
+ for (cnt = 0; cnt < priv->green_offs; cnt++)
+ free (priv->red[cnt]);
+ free (priv->red);
+ }
+ if (priv->blue)
+ {
+ for (cnt = 0; cnt < priv->blue_offs; cnt++)
+ free (priv->blue[cnt]);
+ free (priv->blue);
+ }
+ free (priv->green);
+
+ priv->calib_r = priv->calib_g = priv->calib_b = NULL;
+ priv->red = priv->blue = NULL;
+ priv->green = NULL;
+
+}
+
+static SANE_Status
+ccd300_start (SANE_Handle handle)
+{
+ Mustek_pp_Handle *dev = handle;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ DBG (3, "ccd300_start: called for port ``%s''\n", dev->dev->port);
+
+ if (dev->res <= 100)
+ priv->hwres = 100;
+ else if (dev->res <= 200)
+ priv->hwres = 200;
+ else if (dev->res <= 300)
+ priv->hwres = 300;
+
+ DBG (4, "ccd300_start: setting hardware resolution to %d dpi\n",
+ priv->hwres);
+
+ priv->skipimagebytes = dev->topX;
+
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+ config_ccd (dev);
+ set_voltages (dev);
+ get_bank_count (dev);
+
+ if (priv->bank_count != 0)
+ {
+ DBG (2, "ccd300_start: bank count is not zero...\n");
+ }
+
+ return_home (dev, SANE_FALSE);
+
+ priv->motor_step = 0;
+
+ /* allocate memory for calibration */
+ if ((priv->calib_g = malloc (dev->params.pixels_per_line)) == NULL)
+ {
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (1, "ccd300_start: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (dev->mode == MODE_COLOR)
+ {
+ priv->calib_r = malloc (dev->params.pixels_per_line);
+ priv->calib_b = malloc (dev->params.pixels_per_line);
+
+ if ((priv->calib_r == NULL) || (priv->calib_b == NULL))
+ {
+ free (priv->calib_g);
+ free (priv->calib_r);
+ free (priv->calib_b);
+ priv->calib_r = priv->calib_g = priv->calib_b = NULL;
+
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (1, "ccd300_start: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ calibrate (dev);
+
+ if (priv->ccd_type == 1)
+ {
+ priv->blue_offs = 4;
+ priv->green_offs = 8;
+ }
+ else
+ {
+ priv->blue_offs = 8;
+ priv->green_offs = 16;
+ }
+
+ move_motor (dev, priv->top + dev->topY -
+ (dev->mode == MODE_COLOR ? priv->green_offs : 0), SANE_TRUE);
+
+ if (priv->ccd_type == 1)
+ sanei_pa4s2_writebyte (dev->fd, 6, 0x15);
+
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+
+ if (dev->mode == MODE_COLOR)
+ {
+ int failed = SANE_FALSE, cnt;
+
+ priv->line_step = SANE_FIX (300.0 / (float) dev->res);
+ priv->rdiff = priv->line_step;
+ priv->bdiff = priv->rdiff + (priv->blue_offs << SANE_FIXED_SCALE_SHIFT);
+ priv->gdiff =
+ priv->rdiff + (priv->green_offs << SANE_FIXED_SCALE_SHIFT);
+
+ priv->red = malloc (sizeof (SANE_Byte *) * priv->green_offs);
+ priv->blue = malloc (sizeof (SANE_Byte *) * priv->blue_offs);
+ priv->green = malloc (dev->params.pixels_per_line);
+
+ if ((priv->red == NULL) || (priv->blue == NULL)
+ || (priv->green == NULL))
+ {
+ free (priv->calib_r);
+ free (priv->calib_g);
+ free (priv->calib_b);
+ priv->calib_r = priv->calib_g = priv->calib_b = NULL;
+
+ free (priv->red);
+ free (priv->green);
+ free (priv->blue);
+ priv->red = priv->blue = NULL;
+ priv->green = NULL;
+
+ DBG (1, "ccd300_start: not enough memory for ld buffers\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* note to myself: better allocate one huge chunk of memory and set
+ pointers */
+ for (cnt = 0; cnt < priv->green_offs; cnt++)
+ if ((priv->red[cnt] = malloc (dev->params.pixels_per_line)) == NULL)
+ failed = SANE_TRUE;
+
+ for (cnt = 0; cnt < priv->blue_offs; cnt++)
+ if ((priv->blue[cnt] = malloc (dev->params.pixels_per_line)) == NULL)
+ failed = SANE_TRUE;
+
+ if (failed == SANE_TRUE)
+ {
+ free (priv->calib_r);
+ free (priv->calib_g);
+ free (priv->calib_b);
+ priv->calib_r = priv->calib_g = priv->calib_b = NULL;
+
+ for (cnt = 0; cnt < priv->green_offs; cnt++)
+ free (priv->red[cnt]);
+ for (cnt = 0; cnt < priv->blue_offs; cnt++)
+ free (priv->blue[cnt]);
+
+ free (priv->red);
+ free (priv->green);
+ free (priv->blue);
+ priv->red = priv->blue = NULL;
+ priv->green = NULL;
+
+ DBG (1, "ccd300_start: not enough memory for ld buffers\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ priv->redline = priv->blueline = priv->ccd_line = 0;
+ }
+
+ priv->lines = 0;
+ priv->lines_left = dev->params.lines;
+
+ DBG (3, "ccd300_start: device ready for scanning\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+ccd300_read (SANE_Handle handle, SANE_Byte * buffer)
+{
+ Mustek_pp_Handle *dev = handle;
+ mustek_pp_ccd300_priv *priv = dev->priv;
+
+ DBG (3, "ccd300_read: receiving one line from port ``%s''\n",
+ dev->dev->port);
+
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+
+ switch (dev->mode)
+ {
+ case MODE_BW:
+ get_lineart_line (dev, buffer);
+ break;
+
+ case MODE_GRAYSCALE:
+ get_grayscale_line (dev, buffer);
+ break;
+
+ case MODE_COLOR:
+ get_color_line (dev, buffer);
+ break;
+ }
+
+ priv->lines_left--;
+ priv->lines++;
+
+ DBG (4, "ccd300_read: %d lines read (%d to go)\n", priv->lines,
+ priv->lines_left);
+
+ if (priv->lines_left == 0)
+ {
+ DBG (3, "ccd300_read: scan finished\n");
+ return_home (dev, SANE_TRUE);
+ }
+
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+
+}
diff --git a/backend/mustek_pp_ccd300.h b/backend/mustek_pp_ccd300.h
new file mode 100644
index 0000000..5dfe42d
--- /dev/null
+++ b/backend/mustek_pp_ccd300.h
@@ -0,0 +1,102 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the hardware driver scanners using a 300dpi CCD */
+#ifndef __MUSTEK_PP_CCD300_H
+#define __MUSTEK_PP_CCD300_H
+
+
+/* i might sort and comment this struct one day... */
+
+typedef struct
+{
+ unsigned char asic;
+ unsigned char ccd_type;
+ int top;
+ int motor_stop;
+ int bank_count;
+ unsigned int wait_bank;
+ int hwres;
+ int adjustskip;
+ int ref_black;
+ int ref_red;
+ int ref_green;
+ int ref_blue;
+ int res_step;
+ int blackpos;
+ int motor_step;
+ int saved_skipcount;
+ int channel;
+ int saved_mode;
+ int saved_invert;
+ int skipcount;
+ int saved_skipimagebyte;
+ int skipimagebytes;
+ int saved_adjustskip;
+ int saved_res;
+ int saved_hwres;
+ int saved_res_step;
+ int saved_line_step;
+ int line_step;
+ int saved_channel;
+ unsigned char *calib_g;
+ unsigned char *calib_r;
+ unsigned char *calib_b;
+ int line_diff;
+ int bw;
+ unsigned char **red;
+ unsigned char **blue;
+ unsigned char *green;
+ int redline;
+ int blueline;
+ int ccd_line;
+ int rdiff;
+ int bdiff;
+ int gdiff;
+ int green_offs;
+ int blue_offs;
+ int motor_phase;
+ int image_control;
+ int lines;
+ int lines_left;
+}
+mustek_pp_ccd300_priv;
+
+#endif
diff --git a/backend/mustek_pp_cis.c b/backend/mustek_pp_cis.c
new file mode 100644
index 0000000..026734f
--- /dev/null
+++ b/backend/mustek_pp_cis.c
@@ -0,0 +1,2854 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2001-2003 Eddy De Greef <eddy_de_greef at scarlet dot be>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek PP flatbed _CIS_ scanners.
+*/
+
+/*
+ Global picture
+
+ Mustek_PP_handle -> Mustek_PP_dev
+ -> priv = Mustek_PP_CIS_dev -> CIS
+*/
+
+/*
+ * This flag determines whether the scanner uses fast skipping at high
+ * resolutions. It is possible that this fast skipping introduces
+ * inaccuracies. It if turns out to be a problem, fast skipping can
+ * be disabled by setting this flag to 0.
+ */
+#define MUSTEK_PP_CIS_FAST_SKIP 1
+#define MUSTEK_PP_CIS_WAIT_BANK 200
+
+/*
+ * These parameters determine where the scanable area starts at the top.
+ * If there is a consistent offset error, you can tune it through these
+ * parameters. Note that an inaccuracy in the order of 1 mm seems to be
+ * normal for the Mustek 600/1200 CP series.
+ */
+#define MUSTEK_PP_CIS_600CP_DEFAULT_SKIP 250
+#define MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP 330
+
+/*
+ * Number of scan lines on which the average is taken to determine the
+ * maximum number of color levels.
+ */
+#define MUSTEK_PP_CIS_AVERAGE_COUNT 32
+
+#define MUSTEK_PP_CIS600 1
+#define MUSTEK_PP_CIS1200 2
+#define MUSTEK_PP_CIS1200PLUS 3
+
+#define MUSTEK_PP_CIS_CHANNEL_RED 0
+#define MUSTEK_PP_CIS_CHANNEL_GREEN 1
+#define MUSTEK_PP_CIS_CHANNEL_BLUE 2
+#define MUSTEK_PP_CIS_CHANNEL_GRAY 1
+
+#define MUSTEK_PP_CIS_MAX_H_PIXEL 5118
+#define MUSTEK_PP_CIS_MAX_V_PIXEL 7000
+
+#define MUSTEK_PP_CIS_MOTOR_REVERSE 0
+
+#include "../include/sane/config.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_pa4s2.h"
+#define DEBUG_DECLARE_ONLY
+#include "mustek_pp.h"
+#include "mustek_pp_decl.h"
+#include "mustek_pp_cis.h"
+
+/******************************************************************************
+ ******************************************************************************
+ *** MA1015 chipset related functionality ***
+ ******************************************************************************
+ *****************************************************************************/
+
+/*
+ These defines control some debugging functionality
+
+ #define M1015_TRACE_REGS -> trace the status of the internal registers
+ #define M1015_LOG_HL -> create a high-level log file (register-level)
+ #define M1015_LOG_LL -> create a low-level log file (byte-level)
+
+ By default, all logging/tracing is turned off.
+*/
+
+/******************************************************************************
+ * Low level logging: logs read and writes at the byte level, similar to
+ * the sequences produced by tool of Jochen Eisinger
+ * for analysing the TWAIN driver communication.
+ * This simplifies comparison of the sequences.
+ *****************************************************************************/
+#ifdef M1015_LOG_LL
+
+ static FILE* M1015_LOG_1;
+
+ #define M1015_START_LL\
+ M1015_LOG_1 = fopen("cis_ll.log", "w");
+
+ #define M1015_STOP_LL\
+ fclose(M1015_LOG_1);
+
+ #define SANEI_PA4S2_WRITEBYTE(fd, reg, val)\
+ do\
+ {\
+ sanei_pa4s2_writebyte (fd, reg, val);\
+ fprintf(M1015_LOG_1, "\tsanei_pa4s2_writebyte(fd, %d, 0x%02X);\n", \
+ reg, val);\
+ } while (0)
+
+ static const char* cis_last_rreg_name;
+ static int cis_read_count;
+
+ #define SANEI_PA4S2_READBEGIN(fd, reg)\
+ do\
+ {\
+ cis_last_rreg_name = Mustek_PP_1015_reg_r_name(reg);\
+ cis_read_count = 0;\
+ sanei_pa4s2_readbegin(fd, reg);\
+ } while (0)
+
+ #define SANEI_PA4S2_READBYTE(fd, val)\
+ do\
+ {\
+ sanei_pa4s2_readbyte(fd, val);\
+ ++cis_read_count;\
+ } while (0)
+
+ #define SANEI_PA4S2_READEND(fd)\
+ do\
+ {\
+ sanei_pa4s2_readend(fd);\
+ fprintf(M1015_LOG_1, "\tread_reg(%s, %d);\n", \
+ cis_last_rreg_name, cis_read_count);\
+ } while (0)
+
+ #define M1015_MARK_LL(info)\
+ fprintf(M1015_LOG_1, "* %s\n", info);
+
+#else /* M1015_LOG_LL */
+
+ #define M1015_START_LL
+ #define M1015_STOP_LL
+
+ #define SANEI_PA4S2_WRITEBYTE(fd, reg, val)\
+ sanei_pa4s2_writebyte (fd, reg, val)
+
+ #define SANEI_PA4S2_READBEGIN(fd, reg)\
+ sanei_pa4s2_readbegin(fd, reg)
+
+ #define SANEI_PA4S2_READBYTE(fd, val)\
+ sanei_pa4s2_readbyte(fd, val)
+
+ #define SANEI_PA4S2_READEND(fd)\
+ sanei_pa4s2_readend(fd)
+
+ #define M1015_MARK_LL(info)
+
+#endif /* M1015_LOG_LL */
+
+
+/******************************************************************************
+ * High-level logging: traces the flow of the driver in a hierarchical way
+ * up to the level of register acccesses.
+ *****************************************************************************/
+#ifdef M1015_LOG_HL
+
+ static FILE* M1015_LOG_2;
+ static char hl_prev_line[4096], hl_next_line[4096], hl_repeat_count;
+
+ /*
+ * A few variables for hierarchical log message indentation.
+ */
+
+ static const char* cis_indent_start =
+ " ";
+ static const char* cis_indent;
+ static const char* cis_indent_end;
+
+ #define M1015_START_HL\
+ M1015_LOG_2 = fopen("cis_hl.log", "w");\
+ cis_indent = cis_indent_start + strlen(cis_indent_start);\
+ cis_indent_end = cis_indent;\
+ hl_prev_line[0] = 0;\
+ hl_next_line[0] = 0;\
+ hl_repeat_count = 0;
+
+ #define M1015_FLUSH_HL\
+ if (strcmp(hl_prev_line, hl_next_line))\
+ {\
+ fprintf(M1015_LOG_2, &hl_prev_line[0]);\
+ strcpy(&hl_prev_line[0], &hl_next_line[0]);\
+ if (hl_repeat_count != 0)\
+ {\
+ fprintf(M1015_LOG_2, "%s [last message repeated %d times]\n",\
+ cis_indent, hl_repeat_count+1); \
+ }\
+ hl_repeat_count = 0;\
+ }\
+ else\
+ {\
+ hl_repeat_count += 1;\
+ }
+
+ #define M1015_MARK(info)\
+ sprintf(&hl_next_line[0], "%s+ %s\n", cis_indent, info);\
+ M1015_FLUSH_HL
+
+ #define M1015_STOP_HL\
+ hl_next_line[0] = 0;\
+ M1015_FLUSH_HL\
+ fclose(M1015_LOG_2);
+
+#else /* M1015_LOG_HL */
+
+ #define M1015_START_HL
+ #define M1015_STOP_HL
+ #define M1015_MARK(info)
+ #define M1015_FLUSH_HL
+
+#endif /* M1015_LOG_HL */
+
+#ifdef M1015_TRACE_REGS
+ #define M1015_DISPLAY_REGS(dev, msg) Mustek_PP_1015_display_regs(dev, msg)
+ #define M1015_DISPLAY_REG(msg, val) Mustek_PP_1015_display_reg(msg, val)
+#else
+ #define M1015_DISPLAY_REGS(dev, msg)
+ #define M1015_DISPLAY_REG(msg, val)
+#endif
+
+
+#if defined (M1015_LOG_HL) || defined (M1015_LOG_LL)
+static const char*
+Mustek_PP_1015_reg_r_name(Mustek_PP_1015R_reg id)
+{
+ static const char* names[4] = { "ASIC", "SCAN_VAL", "MOTOR", "BANK_COUNT" };
+ return names[id & 0x03];
+}
+
+static const char*
+Mustek_PP_1015_bit_name(Mustek_PP_1015R_bit id)
+{
+ static const char* names[4] = { "????", "MOTOR_HOME", "????", "MOTOR_BUSY" };
+ return names[id & 0x03];
+}
+
+static const char*
+Mustek_PP_1015_reg_w_name(Mustek_PP_1015R_reg id)
+{
+ static const char* names[4][4] =
+ {
+ { "RED_REF", "GREEN_REF", "BLUE_REF", "DPI_CONTROL" },
+ { "BYTE_COUNT_HB", "BYTE_COUNT_LB", "SKIP_COUNT", "EXPOSE_TIME" },
+ { "SRAM_SOURCE_PC", "MOTOR_CONTROL", "UNKNOWN_42", "UNKNOWN_82" },
+ { "POWER_ON_DELAY", "CCD_TIMING", "CCD_TIMING_ADJ", "RIGHT_BOUND" }
+ };
+ return names[(id & 0x30) >> 4][id & 0x03];
+}
+#endif
+
+/******************************************************************************
+ * Converts a register value to a hex/dec/bin representation.
+ *****************************************************************************/
+static const char*
+Mustek_PP_1015_show_val(int val)
+{
+ /*
+ Since we use a static temporary buffer, we must make sure that the
+ buffer isn't altered while it is still in use (typically because
+ more than one value is converted in a printf statement).
+ Therefore the buffer is organized as a ring buffer. If should contain
+ at least 21 elements in order to be able to display all registers
+ with one printf statement.
+ */
+ #define Mustek_PP_1015_RING_BUFFER_SIZE 50
+ static char buf[Mustek_PP_1015_RING_BUFFER_SIZE][64];
+ static int index = 0;
+ int i;
+ char* current = (char*)buf[index++];
+
+ if (index >= Mustek_PP_1015_RING_BUFFER_SIZE) index = 0;
+
+ if (val < 0)
+ {
+ /* The register has not been initialized yet. */
+ sprintf(current, "---- (---) --------");
+ }
+ else
+ {
+ sprintf(current, "0x%02X (%3d) ", val & 0xFF, val & 0xFF);
+ for (i=0; i<8; ++i)
+ {
+ sprintf(current+11+i, "%d", (val >> (7-i)) & 1);
+ }
+ }
+ return current;
+}
+
+#ifdef M1015_TRACE_REGS
+/******************************************************************************
+ * Displays the contents of all registers of the scanner on stderr.
+ *****************************************************************************/
+static void
+Mustek_PP_1015_display_regs(Mustek_PP_CIS_dev * dev, const char* info)
+{
+ /*
+ * Register naming convention:
+ * Rx : read-only register no. x
+ * ByWx : write-only register no. x of bank no. y
+ */
+
+ fprintf(stderr,
+ "\n"
+ "Register status: %s\n"
+ "\n"
+ " R0: %s : ASIC info\n"
+ " R1: %s : scan value\n"
+ " R2: %s : CCD/motor info\n"
+ " R3: %s : bank count\n"
+ "\n"
+ " B0W0: %s : red reference\n"
+ " B0W1: %s : green reference\n"
+ " B0W2: %s : blue reference\n"
+ " B0W3: %s : DPI control\n"
+ "\n"
+ " B1W0: %s : byte count, high byte\n"
+ " B1W1: %s : byte count, low byte\n"
+ " B1W2: %s : skip x32 pixels\n"
+ " B1W3: %s : expose time (CCDWIDTH)\n"
+ "\n"
+ " B2W0: %s : SRAM source PC\n"
+ " B2W1: %s : motor control\n"
+ " B2W2: %s : -\n"
+ " B2W3: %s : -\n"
+ "\n"
+ " B3W0: %s : power on delay\n"
+ " B3W1: %s : CCD timing - always 0x05\n"
+ " B3W2: %s : CCD timing adjust - always 0x00\n"
+ " B3W3: %s : right bound (not used)\n"
+ "\n"
+ " CHAN: %s : channel [%s]\n"
+ "\n",
+ info,
+ Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[0]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[1]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[2]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[3]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][0]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][1]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][2]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][3]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][0]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][1]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][2]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][3]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][0]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][1]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][2]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][3]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][0]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][1]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][2]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][3]),
+ Mustek_PP_1015_show_val (dev->CIS.regs.channel),
+ (dev->CIS.regs.channel == 0x80 ? "RED" :
+ (dev->CIS.regs.channel == 0x40 ? "GREEN" :
+ (dev->CIS.regs.channel == 0xC0 ? "BLUE" : "unknown")))
+ );
+}
+
+/******************************************************************************
+ * Displays a single register value
+ *****************************************************************************/
+static void
+Mustek_PP_1015_display_reg(const char* info, int val)
+{
+ fprintf (stderr, "%s: %s\n", info, Mustek_PP_1015_show_val(val));
+}
+
+#endif /* M1015_TRACE_REGS */
+
+
+/******************************************************************************
+ *
+ * Reads one of the 4 internal registers of the scanner
+ *
+ * 0: ASIC identification
+ * 1: scan values
+ * 2: CCD info / motor info
+ * 3: bank count info
+ *
+ *****************************************************************************/
+static SANE_Byte
+Mustek_PP_1015_read_reg(Mustek_PP_CIS_dev * dev, Mustek_PP_1015R_reg reg)
+{
+ SANE_Byte tmp;
+ assert(reg <= 3);
+
+ SANEI_PA4S2_READBEGIN (dev->desc->fd, reg & 0x03);
+ SANEI_PA4S2_READBYTE (dev->desc->fd, &tmp);
+ SANEI_PA4S2_READEND (dev->desc->fd);
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s read_reg(%s); [%s]\n", cis_indent,
+ Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_show_val(tmp));
+ M1015_FLUSH_HL;
+#endif
+
+#ifdef M1015_TRACE_REGS
+ dev->CIS.regs.in_regs[reg & 0x03] = tmp;
+#endif
+
+ return tmp;
+}
+
+/******************************************************************************
+ *
+ * Waits for a bit of register to become 1 or 0. The period of checking can be
+ * controlled through the sleep parameter (microseconds).
+ *
+ *****************************************************************************/
+static SANE_Bool
+Mustek_PP_1015_wait_bit(Mustek_PP_CIS_dev * dev, Mustek_PP_1015R_reg reg,
+ Mustek_PP_1015R_bit bit, SANE_Bool on, unsigned period)
+{
+ SANE_Byte tmp;
+ SANE_Byte mask, val;
+ int tries = 0;
+
+ assert(reg <= 3);
+ assert(bit <= 3);
+
+ mask = 1 << bit;
+
+ /* We don't want to wait forever */
+ while (dev->desc->state != STATE_CANCELLED)
+ {
+#if defined (M1015_LOG_LL) || defined (M1015_LOG_HL)
+ ++tries;
+#endif
+
+ sanei_pa4s2_readbegin (dev->desc->fd, reg & 0x03);
+ sanei_pa4s2_readbyte (dev->desc->fd, &tmp);
+ sanei_pa4s2_readend (dev->desc->fd);
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d): %s %s;\n", cis_indent,
+ Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit),
+ on?1:0, Mustek_PP_1015_show_val(mask), Mustek_PP_1015_show_val(tmp));
+ M1015_FLUSH_HL;
+#endif
+ val = ((on == SANE_TRUE) ? tmp : ~tmp ) & mask;
+
+ if (val != 0) break;
+
+ if (period) usleep(period);
+
+ if (tries > 50000)
+ {
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d): failed;\n", cis_indent,
+ Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0);
+ M1015_FLUSH_HL;
+#endif
+ DBG(2, "Mustek_PP_1015_wait_bit: failed (reg %d, bit %d, on: %d)\n",
+ reg, bit, on?1:0);
+ return SANE_FALSE;
+ }
+ }
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d);\n", cis_indent,
+ Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0);
+ M1015_FLUSH_HL;
+#endif
+#ifdef M1015_LOG_LL
+ fprintf(M1015_LOG_1, "\tread_reg(%s, %d);\n", Mustek_PP_1015_reg_r_name(reg),
+ tries);
+#endif
+
+#ifdef M1015_TRACE_REGS
+ dev->CIS.regs.in_regs[reg & 0x03] = tmp;
+#endif
+ return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE;
+}
+
+/******************************************************************************
+ *
+ * Writes one out of 4 registers of one of the 4 register banks (I guess)
+ *
+ * Bank 0
+ * 0: voltage red --+
+ * 1: voltage green +-> always set to 0x96
+ * 2: voltage blue --+
+ * 3: DPI control
+ *
+ * Bank 1
+ * 0: line adjust (?) - high byte
+ * 1: line adjust (?) - low byte
+ * 2: unknown (values seen: 0x00, 0x02, 0x03, 0x1D)
+ * 3: expose time (?) (values seen: 0xAA, 0xFD, 0xFE, 0xFF)
+ *
+ * Bank 2
+ * 0: unknown, used to start linear sequence during calibration
+ * 1: motor control code (forward, return home, ...)
+ * 2: never used
+ * 3: never used
+ *
+ * Bank 3
+ * 0: reduction factor (16bit internal -> 8bit) -> target for calibration
+ * 1: unknown -> always set to 0x05
+ * 2: unknown -> always set to 0x00
+ * 3: never used
+ *
+ *****************************************************************************/
+
+static void
+Mustek_PP_1015_write_reg(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, SANE_Byte val)
+{
+
+ SANE_Byte regBank = (reg & 0xF0) >> 4;
+ SANE_Byte regNo = (reg & 0x0F);
+
+ assert (regNo <= 3);
+ assert (regBank <= 3);
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
+
+#ifdef M1015_TRACE_REGS
+ dev->CIS.regs.out_regs[regBank][regNo] = val;
+#endif
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s write_reg(%s, 0x%02X);\n", cis_indent,
+ Mustek_PP_1015_reg_w_name(reg), val);
+ M1015_FLUSH_HL;
+#endif
+}
+
+/******************************************************************************
+ *
+ * Writes 2 values to 2 adjecent registers.
+ * It is probably equivalent to 2 simple write operations (but I'm not sure).
+ *
+ * val1 is written to register[regNo]
+ * val2 is written to register[regNo+1]
+ *
+ *****************************************************************************/
+static void
+Mustek_PP_1015_write_reg2(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg,
+ SANE_Byte val1, SANE_Byte val2)
+{
+ SANE_Byte regBank = (reg & 0xF0) >> 4;
+ SANE_Byte regNo = (reg & 0x0F);
+
+ assert (regNo <= 2);
+ assert (regBank <= 3);
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val1);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (5+regNo))+ regBank);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val2);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
+
+#ifdef M1015_TRACE_REGS
+ dev->CIS.regs.out_regs[regBank][regNo] = val1;
+ dev->CIS.regs.out_regs[regBank][regNo+1] = val2;
+#endif
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s write_reg2(%s, 0x%02X, 0x%02X);\n",
+ cis_indent, Mustek_PP_1015_reg_w_name(reg), val1, val2);
+ M1015_FLUSH_HL;
+#endif
+}
+
+/******************************************************************************
+ *
+ * Writes 3 values to 3 adjecent registers.
+ * It is probably equivalent to 3 simple write operations (but I'm not sure).
+ *
+ * val1 is written to register[regNo]
+ * val2 is written to register[regNo+1]
+ * val3 is written to register[regNo+2]
+ *
+ *****************************************************************************/
+static void
+Mustek_PP_1015_write_reg3(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg,
+ SANE_Byte val1, SANE_Byte val2, SANE_Byte val3)
+{
+ SANE_Byte regBank = (reg & 0xF0) >> 4;
+ SANE_Byte regNo = (reg & 0x0F);
+
+ assert (regNo <= 1);
+ assert (regBank <= 3);
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val1);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (5+regNo))+ regBank);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val2);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (6+regNo))+ regBank);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val3);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
+
+#ifdef M1015_TRACE_REGS
+ dev->CIS.regs.out_regs[regBank][regNo ] = val1;
+ dev->CIS.regs.out_regs[regBank][regNo+1] = val2;
+ dev->CIS.regs.out_regs[regBank][regNo+2] = val3;
+#endif
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s write_reg3(%s, 0x%02X, 0x%02X, 0x%02X);\n",
+ cis_indent, Mustek_PP_1015_reg_w_name(reg), val1, val2, val3);
+ M1015_FLUSH_HL;
+#endif
+}
+
+/******************************************************************************
+ * Opens a register for a (series of) write operation(s).
+ *****************************************************************************/
+static void
+Mustek_PP_1015_write_reg_start(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg)
+{
+ SANE_Byte regBank = (reg & 0xF0) >> 4;
+ SANE_Byte regNo = (reg & 0x0F);
+
+ assert (regNo <= 3);
+ assert (regBank <= 3);
+
+ dev->CIS.regs.current_write_reg = reg;
+
+#ifdef M1015_LOG_HL
+ dev->CIS.regs.write_count = 0;
+#endif
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
+}
+
+/******************************************************************************
+ * Writes a value to the currently open register.
+ *****************************************************************************/
+static void
+Mustek_PP_1015_write_reg_val(Mustek_PP_CIS_dev * dev, SANE_Byte val)
+{
+#ifdef M1015_TRACE_REGS
+ SANE_Byte regBank = (dev->CIS.regs.current_write_reg & 0xF0) >> 4;
+ SANE_Byte regNo = (dev->CIS.regs.current_write_reg & 0x0F);
+
+ assert (regNo <= 3);
+ assert (regBank <= 3);
+
+ dev->CIS.regs.out_regs[regBank][regNo] = val;
+#endif
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val);
+
+#ifdef M1015_LOG_HL
+ ++dev->CIS.regs.write_count;
+#endif
+}
+
+/******************************************************************************
+ * Closes a register after a (series of) write operation(s).
+ *****************************************************************************/
+static void
+Mustek_PP_1015_write_reg_stop(Mustek_PP_CIS_dev * dev)
+{
+ SANE_Byte regBank = (dev->CIS.regs.current_write_reg & 0xF0) >> 4;
+#ifdef M1015_LOG_HL
+ SANE_Byte regNo = (dev->CIS.regs.current_write_reg & 0x0F);
+ assert (regNo <= 3);
+
+ sprintf(&hl_next_line[0], "%s write_reg_multi(%s, *%d);\n", cis_indent,
+ Mustek_PP_1015_reg_w_name(dev->CIS.regs.current_write_reg),
+ dev->CIS.regs.write_count);
+ M1015_FLUSH_HL;
+#endif
+ assert (regBank <= 3);
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
+}
+
+/******************************************************************************
+ *
+ * Sends a command to the scanner. The command should not access one of the
+ * internal registers, ie., the 3rd bit should not be zero.
+ *
+ *****************************************************************************/
+static void
+Mustek_PP_1015_send_command(Mustek_PP_CIS_dev * dev, SANE_Byte command)
+{
+ assert (command & 0x04);
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, command);
+
+#ifdef M1015_LOG_HL
+ sprintf(&hl_next_line[0], "%s send_command(0x%02X);\n", cis_indent, command);
+ M1015_FLUSH_HL;
+#endif
+}
+
+/******************************************************************************
+ ##############################################################################
+ ## CIS driver ##
+ ##############################################################################
+ *****************************************************************************/
+
+/******************************************************************************
+ * Resolution conversion functions
+ *****************************************************************************/
+static int
+max2hw_hres(Mustek_PP_CIS_dev *dev, int dist)
+{
+ return (int)((dist * dev->CIS.hw_hres) / dev->desc->dev->maxres + 0.5);
+}
+
+#ifdef NOT_USED
+static int
+max2hw_vres(Mustek_PP_CIS_dev *dev, int dist)
+{
+ return (int)((dist * dev->CIS.hw_vres) / dev->desc->dev->maxres + 0.5);
+}
+#endif
+
+static int
+max2cis_hres(Mustek_PP_CIS_dev *dev, int dist)
+{
+ return (int)((dist * dev->CIS.cisRes) / dev->desc->dev->maxres + 0.5);
+}
+
+static int
+cis2max_res(Mustek_PP_CIS_dev *dev, int dist)
+{
+ return (int)((dist * dev->desc->dev->maxres) / dev->CIS.cisRes + 0.5);
+}
+
+#ifdef NOT_USED
+static int
+hw2max_vres(Mustek_PP_CIS_dev *dev, int dist)
+{
+ return (int)((dist * dev->desc->dev->maxres) / dev->CIS.hw_vres + 0.5);
+}
+#endif
+
+/******************************************************************************
+ * Attempts to extract the current bank no.
+ *****************************************************************************/
+static void
+cis_get_bank_count(Mustek_PP_CIS_dev *dev)
+{
+ dev->bank_count = (Mustek_PP_1015_read_reg(dev, MA1015R_BANK_COUNT) & 0x7);
+ if (dev->CIS.use8KBank) dev->bank_count >>= 1;
+}
+
+/******************************************************************************
+ * Triggers a bank switch (I assume).
+ *****************************************************************************/
+static void
+cis_set_sti(Mustek_PP_CIS_dev *dev)
+{
+ SANEI_PA4S2_WRITEBYTE(dev->desc->fd, 3, 0xFF);
+ dev->bank_count++;
+ dev->bank_count &= (dev->CIS.use8KBank == SANE_TRUE) ? 3 : 7;
+}
+
+/******************************************************************************
+ * Wait till the bank with a given number becomes available.
+ *****************************************************************************/
+static SANE_Bool
+cis_wait_bank_change (Mustek_PP_CIS_dev * dev, int bankcount)
+{
+ struct timeval start, end;
+ unsigned long diff;
+ int firsttime = 1;
+
+ gettimeofday (&start, NULL);
+
+ do
+ {
+ if (1 /*niceload*/)
+ {
+ if (firsttime)
+ firsttime = 0;
+ else
+ usleep (10); /* for a little nicer load */
+ }
+ cis_get_bank_count (dev);
+
+ gettimeofday (&end, NULL);
+ diff = (end.tv_sec * 1000 + end.tv_usec / 1000) -
+ (start.tv_sec * 1000 + start.tv_usec / 1000);
+
+ }
+ while ((dev->bank_count != bankcount) && (diff < MUSTEK_PP_CIS_WAIT_BANK));
+
+ if (dev->bank_count != bankcount && dev->desc->state != STATE_CANCELLED)
+ {
+ u_char tmp;
+ tmp = Mustek_PP_1015_read_reg(dev, 3);
+ DBG(2, "cis_wait_bank_change: Missed a bank: got %d [%s], "
+ "wanted %d, waited %d msec\n",
+ dev->bank_count, Mustek_PP_1015_show_val(tmp), bankcount,
+ MUSTEK_PP_CIS_WAIT_BANK);
+ }
+
+ return dev->bank_count == bankcount ? SANE_TRUE : SANE_FALSE;
+}
+
+/******************************************************************************
+ * Configure the CIS for a given resolution.
+ *
+ * CIS scanners seem to have 2 modes:
+ *
+ * low resolution (50-300 DPI) and
+ * high resolution (300-600 DPI).
+ *
+ * Depending on the resolution requested by the user, the scanner is used
+ * in high or low resolution mode. In high resolution mode, the motor step
+ * sizes are also reduced by a factor of two.
+ *
+ *****************************************************************************/
+static void
+cis_set_dpi_value (Mustek_PP_CIS_dev * dev)
+{
+ u_char val = 0;
+
+ if (dev->model == MUSTEK_PP_CIS1200PLUS)
+ {
+ /* Toshiba CIS: only 600 DPI + decimation */
+ switch (dev->CIS.hw_hres)
+ {
+ case 75:
+ val = 0x48; /* 1/8 */
+ break;
+ case 100:
+ val = 0x08; /* 1/6 */
+ break;
+ case 200:
+ val = 0x00; /* 1/3 */
+ break;
+ case 300:
+ val = 0x50; /* 2/4 */
+ break;
+ case 400:
+ val = 0x10; /* 2/3 */
+ break;
+ case 600:
+ val = 0x20; /* 3/3 */
+ break;
+ default:
+ assert (0);
+ }
+ }
+ else
+ {
+ /* Canon CIS: sensor can use 300 or 600 DPI */
+ switch (dev->CIS.hw_hres)
+ {
+ case 50:
+ val = 0x08; /* 1/6 */
+ break;
+ case 100:
+ val = 0x00; /* 1/3 */
+ break;
+ case 200:
+ val = 0x10; /* 2/3 */
+ break;
+ case 300:
+ val = 0x20; /* 3/3 */
+ break;
+ case 400:
+ val = 0x10; /* 2/3 */
+ break;
+ case 600:
+ val = 0x20; /* 3/3 */
+ break;
+ default:
+ assert (0);
+ }
+ }
+
+ Mustek_PP_1015_write_reg(dev, MA1015W_DPI_CONTROL, val | 0x04);
+
+ DBG (4, "cis_set_dpi_value: dpi: %d -> value 0x%02x\n", dev->CIS.hw_hres, val);
+}
+
+static void
+cis_set_ccd_channel (Mustek_PP_CIS_dev * dev)
+{
+
+ SANE_Byte codes[] = { 0x84, 0x44, 0xC4 };
+ SANE_Byte chancode;
+
+ assert (dev->CIS.channel < 3);
+
+ chancode = codes[dev->CIS.channel];
+
+ /*
+ The TWAIN driver sets an extra bit in lineart mode.
+ When I do this too, I don't see any effect on the image.
+ Moreover, for 1 resolution, namely 400 dpi, the bank counter seems
+ to behave strangely, and the synchronization get completely lost.
+ I guess the software conversion from gray to lineart is good enough,
+ so I'll leave it like that.
+
+ if (dev->CIS.setParameters)
+ {
+ chancode |= (dev->desc->mode == MODE_BW) ? 0x20: 0;
+ }
+ */
+
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, chancode);
+
+#ifdef M1015_TRACE_REGS
+ dev->CIS.regs.channel = chancode;
+#endif
+}
+
+static void
+cis_config_ccd (Mustek_PP_CIS_dev * dev)
+{
+ SANE_Int skipCount, byteCount;
+
+ if (dev->CIS.res != 0)
+ dev->CIS.hres_step =
+ SANE_FIX ((float) dev->CIS.hw_hres / (float) dev->CIS.res);
+
+ /* CIS: <= 300 dpi -> 0x86
+ > 300 dpi -> 0x96 */
+
+ if (dev->CIS.cisRes == 600)
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, 0x96);
+ else
+ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, 0x86);
+
+ cis_set_dpi_value(dev);
+
+ if (dev->CIS.setParameters)
+ {
+ dev->CIS.channel = dev->desc->mode == MODE_COLOR ?
+ MUSTEK_PP_CIS_CHANNEL_RED : MUSTEK_PP_CIS_CHANNEL_GRAY;
+ }
+ else
+ {
+ dev->CIS.channel = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ }
+
+ cis_set_ccd_channel (dev);
+
+ Mustek_PP_1015_write_reg (dev, MA1015W_POWER_ON_DELAY, 0xAA);
+ Mustek_PP_1015_write_reg (dev, MA1015W_CCD_TIMING, 0x05);
+ Mustek_PP_1015_write_reg (dev, MA1015W_CCD_TIMING_ADJ, 0x00);
+
+ Mustek_PP_1015_send_command (dev, 0x45); /* or 0x05 for no 8kbank */
+
+ /*
+ * Unknown sequence.
+ * Seems to be always the same during configuration, independent of the
+ * mode and the resolution.
+ */
+ CIS_CLEAR_FULLFLAG(dev);
+ CIS_INC_READ(dev);
+ CIS_CLEAR_READ_BANK(dev);
+ CIS_CLEAR_WRITE_ADDR(dev);
+ CIS_CLEAR_WRITE_BANK(dev);
+ CIS_CLEAR_TOGGLE(dev);
+
+ /*
+ # SkipImage = expressed in max resolution (600 DPI)
+ #
+ # Formulas
+ #
+ # <= 300 DPI:
+ #
+ # Skip = 67 + skipimage/2
+ #
+ # Skip1 = Skip / 32
+ # Skip2 = Skip % 32
+ #
+ # Bytes = Skip2 * hw_hres/300 + (imagebytes * hw_hres/res) + 2
+ #
+ # > 300 DPI
+ #
+ # Skip = 67 + skipimage
+ #
+ # Skip1 = Skip / 32
+ # Skip2 = Skip % 32
+ #
+ # Bytes = Skip2*hw_hres/600 + (imagebytes * hw_hres/res) + 2
+ #
+ */
+
+ skipCount = 67; /* Hardware parameter - fixed */
+
+ if (dev->CIS.setParameters == SANE_TRUE)
+ {
+ /*
+ * It seems that the TWAIN driver always adds 2 mm extra. When I do the
+ * inverse calculation from the parameters that driver sends, I always
+ * get a difference of exactly 2mm, at every resolution and for
+ * different positions of the scan area. Moreover, when I don't add this
+ * offset, the resulting scan seems to start 2mm to soon.
+ * I can't find this back in the backend of the TWAIN driver, but I
+ * assume that this 2mm offset is taken care off at the higher levels.
+ */
+ DBG(4, "cis_config_ccd: Skip count: %d\n", skipCount);
+ skipCount += max2cis_hres(dev, dev->CIS.skipimagebytes);
+ DBG(4, "cis_config_ccd: Skip count: %d (cis res: %d)\n", skipCount,
+ dev->CIS.cisRes);
+ skipCount += (int)(2.0/25.4*dev->CIS.cisRes);
+ DBG(4, "cis_config_ccd: Skip count: %d\n", skipCount);
+
+ Mustek_PP_1015_write_reg (dev, MA1015W_SKIP_COUNT, skipCount / 32);
+ DBG(4, "cis_config_ccd: Skip count: %d (x32)\n", skipCount / 32);
+ }
+ else
+ {
+ Mustek_PP_1015_write_reg (dev, MA1015W_SKIP_COUNT, 0);
+ DBG(4, "cis_config_ccd: Skip count: 67 (x32)\n");
+ }
+
+ skipCount %= 32;
+ skipCount = cis2max_res(dev, skipCount); /* Back to max res */
+
+ Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime);
+
+ DBG(4, "cis_config_ccd: skipcount: %d imagebytes: %d\n", skipCount, dev->CIS.imagebytes);
+ /* set_initial_skip_1015 (dev); */
+ if (dev->CIS.setParameters == SANE_TRUE)
+ {
+ Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime);
+ Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, 0xAA);
+ /* The TWAIN drivers always sends the same value: 0x96 */
+ Mustek_PP_1015_write_reg3(dev, MA1015W_RED_REF, 0x96, 0x96, 0x96);
+ dev->CIS.adjustskip = max2hw_hres(dev, skipCount);
+ byteCount = max2hw_hres(dev, skipCount + dev->CIS.imagebytes) + 2;
+ dev->CIS.setParameters = SANE_FALSE;
+ }
+ else
+ {
+ dev->CIS.adjustskip = 0;
+ byteCount = max2hw_hres(dev, skipCount);
+ }
+ DBG(4, "cis_config_ccd: adjust skip: %d bytecount: %d\n",
+ dev->CIS.adjustskip, byteCount);
+
+ Mustek_PP_1015_write_reg2(dev, MA1015W_BYTE_COUNT_HB,
+ byteCount >> 8, byteCount & 0xFF);
+
+ cis_get_bank_count (dev);
+ DBG(5, "cis_config_ccd: done\n");
+}
+
+static SANE_Bool
+cis_wait_motor_stable (Mustek_PP_CIS_dev * dev)
+{
+ static struct timeval timeoutVal;
+ SANE_Bool ret =
+ Mustek_PP_1015_wait_bit (dev, MA1015R_MOTOR, MA1015B_MOTOR_STABLE,
+ SANE_FALSE, 0);
+#ifdef HAVE_SYS_SELECT_H
+ if (dev->engine_delay > 0)
+ {
+ timeoutVal.tv_sec = 0;
+ timeoutVal.tv_usec = dev->engine_delay*1000;
+ select(0, NULL, NULL, NULL, &timeoutVal);
+ }
+#endif
+ return ret;
+}
+
+static void
+cis_motor_forward (Mustek_PP_CIS_dev * dev)
+{
+ SANE_Byte control;
+
+ if (dev->model == MUSTEK_PP_CIS600)
+ {
+ switch (dev->CIS.hw_vres)
+ {
+ case 150:
+ control = 0x7B;
+ break;
+ case 300:
+ control = 0x73;
+ break;
+ case 600:
+ control = 0x13;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ else
+ {
+ switch (dev->CIS.hw_vres)
+ {
+ case 300:
+ control = 0x7B;
+ break;
+ case 600:
+ control = 0x73;
+ break;
+ case 1200:
+ control = 0x13;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+#if MUSTEK_PP_CIS_MOTOR_REVERSE == 1
+ control ^= 0x10;
+#endif
+
+ DBG(4, "cis_motor_forward: @%d dpi: 0x%02X.\n", dev->CIS.hw_vres, control);
+ if (!cis_wait_motor_stable (dev))
+ return;
+
+ Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control);
+}
+
+static void
+cis_move_motor (Mustek_PP_CIS_dev * dev, SANE_Int steps) /* steps @ maxres */
+{
+ /* Note: steps is expressed at maximum resolution */
+ SANE_Byte fullStep = 0x13, biStep = 0x73, quadStep = 0x7B;
+ SANE_Int fullSteps, biSteps, quadSteps;
+ /*
+ * During a multi-step feed, the expose time is fixed. The value depends
+ * on the type of the motor (600/1200 CP)
+ */
+ SANE_Byte savedExposeTime = dev->CIS.exposeTime;
+ dev->CIS.exposeTime = 85;
+
+ DBG(4, "cis_move_motor: Moving motor %d steps.\n", steps);
+
+ /* Just in case ... */
+ if (steps < 0)
+ {
+ DBG(1, "cis_move_motor: trying to move negative steps: %d\n", steps);
+ steps = 0; /* We must go through the configuration procedure */
+ }
+
+ /*
+ * Using the parameter settings for the 600 CP on a 1200 CP scanner
+ * doesn't work: the engine doesn't move and makes a sharp noise, which
+ * doesn't sound too healthy. It could be harmful to the motor !
+ * Apparently, the same happens on a real 600 CP (reported by Disma
+ * Goggia), so it's probably better to always use the 1200 CP settings.
+ */
+ dev->CIS.exposeTime <<= 1;
+ cis_config_ccd(dev);
+ dev->CIS.exposeTime = savedExposeTime;
+
+ /*
+ * This is a minor speed optimization: when we are using the high
+ * resolution mode, long feeds (eg, to move to a scan area at the bottom
+ * of the page) can be made almost twice as fast by using double motor
+ * steps as much as possible.
+ * It is possible, though, that fast skipping (which is the default) is
+ * not very accurate on some scanners. Therefore, the user can disable
+ * this through the configuration file.
+ */
+
+ fullSteps = steps & 1;
+ biSteps = steps >> 1;
+ if (dev->fast_skip) {
+ quadSteps = biSteps >> 1;
+ biSteps &= 1;
+ }
+ else {
+ quadSteps = 0;
+ }
+
+ M1015_DISPLAY_REGS(dev, "Before move");
+
+#if MUSTEK_PP_CIS_MOTOR_REVERSE == 1
+ fullStep ^= 0x10;
+ biStep ^= 0x10;
+ quadStep ^= 0x10;
+#endif
+
+ DBG(4, "cis_move_motor: 4x%d 2x%d 1x%d\n", quadSteps, biSteps, fullSteps);
+ /* Note: the TWAIN driver opens the motor control register only
+ once before the loop, and closes it after the loop. I've tried this
+ too, but it resulted in inaccurate skip distances; therefore, the
+ motor control register is now opened and closed for each step. */
+
+ while (quadSteps-- > 0 && dev->desc->state != STATE_CANCELLED)
+ {
+ cis_wait_motor_stable (dev);
+ Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, quadStep);
+ }
+
+ while (biSteps-- > 0 && dev->desc->state != STATE_CANCELLED)
+ {
+ cis_wait_motor_stable (dev);
+ Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, biStep);
+ }
+
+ while (fullSteps-- > 0 && dev->desc->state != STATE_CANCELLED)
+ {
+ cis_wait_motor_stable (dev);
+ Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, fullStep);
+ }
+}
+
+static void
+cis_set_et_pd_sti (Mustek_PP_CIS_dev * dev)
+{
+ Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME,
+ dev->CIS.exposeTime);
+ Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY,
+ dev->CIS.powerOnDelay[dev->CIS.channel]);
+ cis_set_ccd_channel (dev);
+ cis_set_sti (dev);
+}
+
+/*
+ * Prepare the scanner for catching the next channel and, if necessary,
+ * move the head one step further.
+ */
+static SANE_Bool
+cis_wait_next_channel (Mustek_PP_CIS_dev * dev)
+{
+ int moveAtChannel = dev->desc->mode == MODE_COLOR ?
+ MUSTEK_PP_CIS_CHANNEL_BLUE : MUSTEK_PP_CIS_CHANNEL_GRAY;
+
+ if (!cis_wait_bank_change (dev, dev->bank_count))
+ {
+ DBG(2, "cis_wait_next_channel: Could not get next bank.\n");
+ return SANE_FALSE;
+ }
+
+ moveAtChannel = (dev->desc->mode == MODE_COLOR) ?
+ MUSTEK_PP_CIS_CHANNEL_BLUE : MUSTEK_PP_CIS_CHANNEL_GRAY;
+
+ if (dev->CIS.channel == moveAtChannel && !dev->CIS.dontMove)
+ {
+ cis_motor_forward (dev);
+ }
+
+ cis_set_et_pd_sti (dev);
+
+ if (dev->desc->mode == MODE_COLOR)
+ {
+ ++dev->CIS.channel;
+ dev->CIS.channel %= 3;
+ }
+
+ return SANE_TRUE;
+}
+
+/*
+ * Wait for the device to be ready for scanning. Cycles through the different
+ * channels and sets the parameters (only green channel in gray/lineart).
+ */
+static SANE_Bool
+cis_wait_read_ready (Mustek_PP_CIS_dev * dev)
+{
+ int channel;
+ dev->CIS.dontIncRead = SANE_TRUE;
+
+ dev->CIS.channel = dev->desc->mode == MODE_COLOR ?
+ MUSTEK_PP_CIS_CHANNEL_RED : MUSTEK_PP_CIS_CHANNEL_GRAY;
+
+ for (channel = 0; channel < 3; ++channel)
+ {
+ if (!cis_wait_next_channel(dev)) return SANE_FALSE;
+ }
+ return SANE_TRUE;
+}
+
+static int
+delay_read (int delay)
+{
+ /*
+ * A (very) smart compiler may complete optimize the delay loop away. By
+ * adding some difficult data dependencies, we can try to prevent this.
+ */
+ static int prevent_removal, i;
+ for (i = 0; i<delay; ++i)
+ {
+ prevent_removal = sqrt(prevent_removal+1.); /* Just waste some cycles */
+ }
+ return prevent_removal; /* another data dependency */
+}
+
+/*
+** Reads one line of pixels
+*/
+static void
+cis_read_line_low_level (Mustek_PP_CIS_dev * dev, SANE_Byte * buf,
+ SANE_Int pixel, SANE_Byte * calib_low,
+ SANE_Byte * calib_hi, SANE_Int * gamma)
+{
+ u_char color;
+ int ctr, skips = dev->CIS.adjustskip, cval;
+ int bpos = 0;
+ SANE_Byte low_val = 0, hi_val = 255;
+
+ if (pixel <= 0)
+ return;
+
+ SANEI_PA4S2_READBEGIN (dev->desc->fd, 1);
+
+ while(skips-- >= 0)
+ {
+ if (dev->CIS.delay) delay_read(dev->CIS.delay);
+ SANEI_PA4S2_READBYTE (dev->desc->fd, &color);
+ }
+
+ if (dev->CIS.hw_hres == dev->CIS.res)
+ {
+ /* One-to one mapping */
+ DBG (6, "cis_read_line_low_level: one-to-one\n");
+ for (ctr = 0; ctr < pixel; ctr++)
+ {
+ if (dev->CIS.delay) delay_read(dev->CIS.delay);
+ SANEI_PA4S2_READBYTE (dev->desc->fd, &color);
+
+ cval = color;
+
+ if (calib_low) {
+ low_val = calib_low[ctr] ;
+ }
+
+ if (calib_hi) {
+ hi_val = calib_hi[ctr] ;
+ }
+
+ cval -= low_val ;
+ cval <<= 8 ;
+ cval /= hi_val-low_val ;
+
+ if (cval < 0) cval = 0;
+ else if (cval > 255) cval = 255;
+
+ if (gamma)
+ cval = gamma[cval];
+
+ buf[ctr] = cval;
+ }
+ }
+ else if (dev->CIS.hw_hres > dev->CIS.res)
+ {
+ /* Sub-sampling */
+
+ int pos = 0;
+ DBG (6, "cis_read_line_low_level: sub-sampling\n");
+ ctr = 0;
+ do
+ {
+ if (dev->CIS.delay) delay_read(dev->CIS.delay);
+ SANEI_PA4S2_READBYTE (dev->desc->fd, &color);
+
+ cval = color;
+ if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT))
+ {
+ ctr++;
+ continue;
+ }
+
+ ctr++;
+ pos += dev->CIS.hres_step;
+
+ if (calib_low) {
+ low_val = calib_low[bpos] ;
+ }
+
+ if (calib_hi) {
+ hi_val = calib_hi[bpos] ;
+ }
+ cval -= low_val ;
+ cval <<= 8 ;
+ cval /= hi_val-low_val ;
+
+ if (cval < 0) cval = 0 ;
+ else if (cval > 255) cval = 255 ;
+
+ if (gamma) cval = gamma[cval];
+
+ buf[bpos++] = cval;
+ }
+ while (bpos < pixel);
+ }
+ else
+ {
+ int calctr = 0;
+ SANE_Int pos = 0, nextPos = 1;
+ /* Step: eg: 600 DPI -> 700 DPI -> hres_step = 6/7 -> step = 1/7 */
+ SANE_Int step = SANE_FIX(1) - dev->CIS.hres_step;
+
+ /* Super-sampling */
+ DBG (6, "cis_read_line_low_level: super-sampling\n");
+ do
+ {
+ if (dev->CIS.delay) delay_read(dev->CIS.delay);
+ SANEI_PA4S2_READBYTE (dev->desc->fd, &color);
+
+ cval = color;
+
+ if (calib_low) {
+ low_val = calib_low[calctr] ;
+ }
+
+ if (calib_hi) {
+ hi_val = calib_hi[calctr] ;
+ }
+
+ if (++calctr >= dev->calib_pixels) {
+ /* Avoid array boundary violations due to rounding errors
+ (due to the incremental calculation, the current position
+ may be inaccurate to up to two pixels, so we may need to
+ read a few extra bytes -> use the last calibration value) */
+ calctr = dev->calib_pixels - 1;
+ DBG (3, "cis_read_line_low_level: calibration overshoot\n");
+ }
+
+ cval -= low_val ;
+ cval <<= 8 ;
+ cval /= hi_val-low_val ;
+
+ if (cval < 0) cval = 0 ;
+ else if (cval > 255) cval = 255 ;
+
+ if (gamma)
+ cval = gamma[cval];
+
+ pos += step;
+
+ if ((pos >> SANE_FIXED_SCALE_SHIFT) >= nextPos)
+ {
+ nextPos++;
+
+ /* Insert an interpolated value */
+ buf[bpos] = (buf[bpos-1] + cval)/2; /* Interpolate */
+ ++bpos;
+
+ /* Store the plain value, but only if we still need pixels */
+ if (bpos < pixel)
+ buf[bpos++] = cval;
+
+ pos += step; /* Take interpolated value into account for pos */
+ }
+ else
+ {
+ buf[bpos++] = cval;
+ }
+ }
+ while (bpos < pixel);
+ }
+
+ SANEI_PA4S2_READEND (dev->desc->fd);
+ DBG (6, "cis_read_line_low_level: done\n");
+}
+
+static SANE_Bool
+cis_read_line (Mustek_PP_CIS_dev * dev, SANE_Byte* buf, SANE_Int pixel,
+ SANE_Bool raw)
+{
+ if (!dev->CIS.dontIncRead)
+ CIS_INC_READ(dev);
+ else
+ dev->CIS.dontIncRead = SANE_FALSE;
+
+
+ if (raw)
+ {
+ /* No color correction; raw data */
+ cis_read_line_low_level (dev, buf, pixel, NULL, NULL, NULL);
+ }
+ else
+ {
+ /* Color correction */
+ cis_read_line_low_level (dev, buf, pixel,
+ dev->calib_low[dev->CIS.channel],
+ dev->calib_hi[dev->CIS.channel],
+ (dev->desc->val[OPT_CUSTOM_GAMMA].w ?
+ dev->desc->gamma_table[dev->CIS.channel] : NULL));
+ }
+
+ return cis_wait_next_channel(dev);
+}
+
+static void
+cis_get_next_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
+{
+ SANE_Byte *dest, *tmpbuf = dev->tmpbuf;
+ int ctr, channel, first, last, stride, ignore, step = dev->CIS.line_step;
+ SANE_Byte gotline;
+
+ if (dev->desc->mode == MODE_COLOR)
+ {
+ first = MUSTEK_PP_CIS_CHANNEL_RED;
+ last = MUSTEK_PP_CIS_CHANNEL_BLUE;
+ stride = 3;
+ ignore = 1; /* 1 * 3 channels */
+ }
+ else
+ {
+ first = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ last = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ stride = 1;
+ ignore = 3; /* 3 * 1 channel */
+ }
+
+ gotline = SANE_FALSE;
+ do
+ {
+ dev->ccd_line++;
+ if ((dev->line_diff >> SANE_FIXED_SCALE_SHIFT) != dev->ccd_line)
+ {
+ cis_motor_forward (dev);
+ continue;
+ }
+
+ dev->line_diff += step;
+
+ for (channel = first; channel <= last; ++channel)
+ {
+ if (!cis_read_line(dev, tmpbuf, dev->desc->params.pixels_per_line,
+ SANE_FALSE))
+ return;
+
+ dest = buf + channel - first;
+ for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++)
+ {
+ *dest = tmpbuf[ctr];
+ dest += stride;
+ }
+ }
+ gotline = SANE_TRUE;
+ }
+ while (!gotline && dev->desc->state != STATE_CANCELLED);
+}
+
+static void
+cis_get_grayscale_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
+{
+ cis_get_next_line(dev, buf);
+}
+
+static void
+cis_get_lineart_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
+{
+ int ctr;
+ SANE_Byte gbuf[MUSTEK_PP_CIS_MAX_H_PIXEL * 2];
+
+ cis_get_grayscale_line (dev, gbuf);
+ memset (buf, 0xFF, dev->desc->params.bytes_per_line);
+
+ for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++)
+ buf[ctr >> 3] ^= ((gbuf[ctr] > dev->bw_limit) ? (1 << (7 - ctr % 8)) : 0);
+}
+
+static void
+cis_get_color_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
+{
+ cis_get_next_line(dev, buf);
+}
+
+
+/******************************************************************************
+ * Saves the state of the device during reset and calibration.
+ *****************************************************************************/
+static void
+cis_save_state (Mustek_PP_CIS_dev * dev)
+{
+ dev->Saved_CIS = dev->CIS;
+}
+
+/******************************************************************************
+ * Restores the state of the device after reset and calibration.
+ *****************************************************************************/
+static void
+cis_restore_state (Mustek_PP_CIS_dev * dev)
+{
+ dev->CIS = dev->Saved_CIS;
+}
+
+#define CIS_TOO_BRIGHT 1
+#define CIS_OK 0
+#define CIS_TOO_DARK -1
+
+static int
+cis_check_result(SANE_Byte* buffer, int pixel)
+{
+ int i, maxVal = 0;
+
+ for (i=0;i<pixel;++i)
+ if (buffer[i] > maxVal) maxVal = buffer[i];
+
+ if (maxVal > 250) return CIS_TOO_BRIGHT;
+ if (maxVal < 240) return CIS_TOO_DARK;
+ return CIS_OK;
+}
+
+static SANE_Bool
+cis_maximize_dynamic_range(Mustek_PP_CIS_dev * dev)
+{
+ /* The device is in its final configuration already. */
+ int i, j, pixel, channel, minExposeTime, first, last;
+ SANE_Byte powerOnDelayLower[3], powerOnDelayUpper[3], exposeTime[3];
+ SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
+ SANE_Int pixels = dev->calib_pixels;
+
+ DBG(3, "cis_maximize_dynamic_range: starting\n");
+
+ for (channel = 0; channel < 3; ++channel)
+ {
+ exposeTime[channel] = 254;
+ dev->CIS.powerOnDelay[channel] = 170;
+ powerOnDelayLower[channel] = 1;
+ powerOnDelayUpper[channel] = 254;
+ }
+ dev->CIS.setParameters = SANE_TRUE;
+ dev->CIS.exposeTime = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN];
+ cis_config_ccd(dev);
+
+ M1015_DISPLAY_REGS(dev, "before maximizing dynamic range");
+ dev->CIS.dontMove = SANE_TRUE; /* Don't move while calibrating */
+
+ if (!cis_wait_read_ready(dev) && dev->desc->state != STATE_CANCELLED)
+ {
+ DBG(2, "cis_maximize_dynamic_range: DEVICE NOT READY!\n");
+ return SANE_FALSE;
+ }
+
+ if (dev->desc->mode == MODE_COLOR)
+ {
+ first = MUSTEK_PP_CIS_CHANNEL_RED;
+ last = MUSTEK_PP_CIS_CHANNEL_BLUE;
+ }
+ else
+ {
+ first = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ last = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ }
+
+ dev->CIS.channel = first;
+
+ /* Perform a kind of binary search. In the worst case, we should find
+ the optimal power delay values after 8 iterations */
+ for( i=0; i<8; i++)
+ {
+ for (channel = first; channel <= last; ++channel)
+ {
+ dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] +
+ powerOnDelayUpper[channel]) / 2;
+ }
+ Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY,
+ dev->CIS.powerOnDelay[1]); /* Green */
+
+ for (pixel = 0; pixel < pixels; ++pixel)
+ {
+ buf[0][pixel] = buf[1][pixel] = buf[2][pixel] = 255;
+ }
+
+ /* Scan 4 lines, but ignore the first 3 ones. */
+ for (j = 0; j < 4; ++j)
+ {
+ for (channel = first; channel <= last; ++channel)
+ {
+ if (!cis_read_line(dev, &buf[channel][0], pixels,
+ /* raw = */ SANE_TRUE))
+ return SANE_FALSE;
+ }
+ }
+
+ for (channel = first; channel <= last; ++channel)
+ {
+ switch (cis_check_result(buf[channel], pixels))
+ {
+ case CIS_TOO_BRIGHT:
+ powerOnDelayLower[channel] = dev->CIS.powerOnDelay[channel];
+ break;
+
+ case CIS_TOO_DARK:
+ powerOnDelayUpper[channel] = dev->CIS.powerOnDelay[channel];
+ break;
+
+ default:
+ break;
+ }
+ }
+ DBG (4, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n",
+ dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1],
+ dev->CIS.powerOnDelay[2]);
+ }
+ dev->CIS.dontMove = SANE_FALSE;
+
+ DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n",
+ dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1],
+ dev->CIS.powerOnDelay[2]);
+
+ minExposeTime = (dev->CIS.hw_hres <= 300) ? 170 : 253;
+
+ for (channel = first; channel <= last; ++channel)
+ {
+ dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] +
+ powerOnDelayUpper[channel]) / 2;
+ exposeTime[channel] -= dev->CIS.powerOnDelay[channel] - 1;
+ dev->CIS.powerOnDelay[channel] = 1;
+
+ if (exposeTime[channel] < minExposeTime)
+ {
+ dev->CIS.powerOnDelay[channel] +=
+ minExposeTime - exposeTime[channel];
+ exposeTime[channel] = minExposeTime;
+ }
+ }
+
+ dev->CIS.exposeTime = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN];
+
+ DBG (3, "cis_maximize_dynamic_range: expose time: %3d\n", exposeTime[1]);
+ DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n",
+ dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1],
+ dev->CIS.powerOnDelay[2]);
+
+ /*
+ * Short the calibration. Temporary, to find out what is wrong with
+ * the calibration on a 600 CP.
+ *
+ dev->CIS.exposeTime = 170;
+ dev->CIS.powerOnDelay[0] = 120;
+ dev->CIS.powerOnDelay[1] = 120;
+ dev->CIS.powerOnDelay[2] = 120;
+ */
+ return SANE_TRUE;
+}
+
+static SANE_Bool
+cis_measure_extremes(Mustek_PP_CIS_dev * dev, SANE_Byte* calib[3],
+ SANE_Int pixels, SANE_Int first, SANE_Int last)
+{
+ SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
+ SANE_Byte min[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
+ SANE_Byte max[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
+ SANE_Int sum[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
+ int channel, cnt, p;
+
+ memset((void*)&min, 255, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte));
+ memset((void*)&max, 0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte));
+ memset((void*)&sum, 0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Int));
+
+ dev->CIS.channel = first;
+
+ /* Purge the banks first (there's always a 3-cycle delay) */
+ for (channel = first; channel <= last; ++channel)
+ {
+ if (!cis_read_line(dev, &buf[channel%3][0], pixels,
+ /* raw = */ SANE_TRUE))
+ return SANE_FALSE;
+ }
+ --dev->CIS.skipsToOrigin;
+
+ for (cnt = 0; cnt < MUSTEK_PP_CIS_AVERAGE_COUNT + 2; ++cnt)
+ {
+ for (channel = first; channel <= last; ++channel)
+ {
+ DBG(4, "cis_measure_extremes: Reading line %d - channel %d\n",
+ cnt, channel);
+ if (!cis_read_line(dev, &buf[channel][0], pixels,
+ /* raw = */ SANE_TRUE))
+ return SANE_FALSE;
+
+ for (p = 0; p < pixels; ++p)
+ {
+ SANE_Byte val = buf[channel][p];
+ if (val < min[channel][p]) min[channel][p] = val;
+ if (val > max[channel][p]) max[channel][p] = val;
+ sum[channel][p] += val;
+ }
+ }
+ --dev->CIS.skipsToOrigin;
+ }
+ DBG(4, "cis_measure_extremes: Averaging\n");
+ for (channel = first; channel <= last; ++channel)
+ {
+ /* Ignore the extreme values and take the average of the others. */
+ for (p = 0; p < pixels; ++p)
+ {
+ sum[channel][p] -= min[channel][p] + max[channel][p];
+ sum[channel][p] /= MUSTEK_PP_CIS_AVERAGE_COUNT;
+ if (calib[channel]) calib[channel][p] = sum[channel][p];
+ }
+ }
+ DBG(4, "cis_measure_extremes: Done\n");
+ return SANE_TRUE;
+}
+
+static SANE_Bool
+cis_normalize_ranges(Mustek_PP_CIS_dev * dev)
+{
+ SANE_Byte cal_low, cal_hi ;
+ SANE_Byte powerOnDelay[3] ;
+ SANE_Int pixels = dev->calib_pixels;
+ SANE_Int channel, p, first, last;
+
+ if (dev->desc->mode == MODE_COLOR)
+ {
+ first = MUSTEK_PP_CIS_CHANNEL_RED;
+ last = MUSTEK_PP_CIS_CHANNEL_BLUE;
+ }
+ else
+ {
+ first = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ last = MUSTEK_PP_CIS_CHANNEL_GRAY;
+ }
+
+ DBG(3, "cis_normalize_ranges: Measuring high extremes\n");
+ /* Measure extremes with normal lighting */
+ if (!cis_measure_extremes(dev, dev->calib_hi, pixels, first, last)) {
+ return SANE_FALSE;
+ }
+
+ /* Measure extremes without lighting */
+ for (channel=first; channel<=last; ++channel) {
+ powerOnDelay[channel] = dev->CIS.powerOnDelay[channel];
+ dev->CIS.powerOnDelay[channel] = dev->CIS.exposeTime;
+ }
+
+ DBG(3, "cis_normalize_ranges: Measuring low extremes\n");
+ if (!cis_measure_extremes(dev, dev->calib_low, pixels, first, last)) {
+ return SANE_FALSE;
+ }
+
+ /* Restore settings */
+ for (channel=first; channel<=last; ++channel) {
+ dev->CIS.powerOnDelay[channel] = powerOnDelay[channel];
+ }
+
+ /* Make sure calib_hi is greater than calib_low */
+ for (channel = first; channel <= last; ++channel) {
+ for (p = 0; p<pixels; p++) {
+ if (dev->calib_low[channel]) {
+ cal_low = dev->calib_low[channel][p];
+ } else {
+ cal_low = 0;
+ }
+ if (dev->calib_hi[channel]) {
+ cal_hi = dev->calib_hi[channel][p];
+ } else {
+ cal_hi = 255;
+ }
+ if (cal_hi <= cal_low) {
+ if(cal_hi<255) {
+ /* calib_hi exists, else cal_hi would be 255 */
+ dev->calib_hi[channel][p] = cal_low+1;
+ } else {
+ /* calib_low exists, else cal_low would be 0, < 255 */
+ dev->calib_low[channel][p] = cal_hi-1;
+ }
+ }
+ }
+ }
+ DBG(3, "cis_normalize_ranges: calibration done\n");
+ return SANE_TRUE;
+}
+
+/*
+ * This routine measures the time that we have to wait between reading
+ * to pixels from the scanner. Especially at low resolutions, but also
+ * for narrow-width scans at high resolutions, reading too fast cause
+ * color stability problems.
+ * This routine sends a test pattern to the scanner memory banks and tries
+ * to measure how fast it can be retrieved without errors.
+ * The same is done by the TWAIN driver (TESTIO.CPP:TestDelay).
+ */
+static SANE_Bool
+cis_measure_delay(Mustek_PP_CIS_dev * dev)
+{
+ SANE_Byte buf[2][2048];
+ unsigned i, j, d;
+ int saved_res;
+ SANE_Bool error = SANE_FALSE;
+
+ CIS_CLEAR_FULLFLAG(dev);
+ CIS_CLEAR_WRITE_ADDR(dev);
+ CIS_CLEAR_WRITE_BANK(dev);
+ CIS_INC_READ(dev);
+ CIS_CLEAR_READ_BANK(dev);
+
+ M1015_DISPLAY_REGS(dev, "Before delay measurement");
+ assert(dev->CIS.adjustskip == 0);
+
+ /* Sawtooth */
+ for (i=0; i<2048; ++i)
+ {
+ buf[0][i] = i % 255; /* Why 255 ? Seems to have no real importance */
+ }
+
+ Mustek_PP_1015_write_reg_start(dev, MA1015W_SRAM_SOURCE_PC);
+ for (i=0; i<2048; ++i)
+ {
+ Mustek_PP_1015_write_reg_val(dev, buf[0][i]);
+ }
+ Mustek_PP_1015_write_reg_stop(dev);
+
+ /* Bank offset measurement */
+ dev->CIS.delay = 0; /* Initialize to zero, measure next */
+
+ saved_res = dev->CIS.res;
+ dev->CIS.res = dev->CIS.hw_hres;
+
+ /*
+ * Note: the TWAIN driver seems to have a fast EPP mode too. That one is
+ * tried first, and then they try the normal mode. I haven't figured out
+ * yet how the fast mode works, so I'll only check the normal mode for now.
+ * Moreover, from the behaviour that I've witnessed from the TWAIN driver,
+ * I must conclude that the fast mode probably doesn't work on my computer,
+ * so I can't test it anyhow.
+ */
+ /* Gradually increase the delay till we have no more errors */
+ for (d = 0; d < 75 /* 255 */ && dev->desc->state != STATE_CANCELLED; d += 5)
+ {
+ dev->CIS.delay = d;
+
+ /*
+ * We read the line 5 times to make sure that all garbage is flushed.
+ */
+ for (i=0; i<5; ++i)
+ {
+ CIS_INC_READ(dev);
+ CIS_CLEAR_READ_BANK(dev);
+ cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL);
+ if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE;
+ }
+
+ error = SANE_FALSE;
+ /* Check 100 times whether we can read without errors. */
+ for (i=0; i<100 && !error; ++i)
+ {
+ CIS_INC_READ(dev);
+ CIS_CLEAR_READ_BANK(dev);
+ cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL);
+ if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE;
+
+ for (j=0; j<2048; ++j)
+ {
+ if (buf[0][j] != buf[1][j])
+ {
+ error = SANE_TRUE;
+ break;
+ }
+ }
+ }
+
+ DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay);
+ if (!error)
+ break;
+ }
+
+ dev->CIS.res = saved_res;
+
+ if (error)
+ {
+ fprintf(stderr, "mustek_pp_cis: failed to measure delay.\n");
+ fprintf(stderr, "Buffer contents:\n");
+ for (j = 0; j < 20; ++j)
+ {
+ fprintf(stderr, "%d ", buf[1][j]);
+ }
+ fprintf(stderr, "\n");
+ dev->CIS.delay = 0;
+ }
+
+ DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay);
+ return SANE_TRUE;
+}
+
+static void
+cis_motor_control (Mustek_PP_CIS_dev * dev, u_char control)
+{
+ cis_wait_motor_stable (dev);
+ Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control);
+}
+
+static void
+cis_return_home (Mustek_PP_CIS_dev * dev, SANE_Bool nowait)
+{
+ SANE_Byte savedExposeTime = dev->CIS.exposeTime;
+ DBG(4, "cis_return_home: returning home; nowait: %d\n", nowait);
+ /* During a return-home, the expose time is fixed. */
+ dev->CIS.exposeTime = 170;
+ cis_config_ccd(dev);
+ dev->CIS.exposeTime = savedExposeTime;
+
+ cis_motor_control (dev, 0xEB);
+
+ if (nowait == SANE_FALSE)
+ Mustek_PP_1015_wait_bit(dev, MA1015R_MOTOR, MA1015B_MOTOR_HOME,
+ SANE_TRUE, 1000);
+}
+
+/******************************************************************************
+ * Does a full reset of the device, ie. configures the CIS to a default
+ * resolution of 300 DPI (in high or low resolution mode, depending on the
+ * resolution requested by the user).
+ *****************************************************************************/
+static void
+cis_reset_device (Mustek_PP_CIS_dev * dev)
+{
+ DBG(4, "cis_reset_device: resetting device\n");
+ dev->CIS.adjustskip = 0;
+ dev->CIS.dontIncRead = SANE_TRUE;
+ dev->CIS.dontMove = SANE_FALSE;
+
+ cis_save_state(dev);
+
+ dev->CIS.hw_hres = 300;
+ dev->CIS.channel = MUSTEK_PP_CIS_CHANNEL_GREEN;
+ dev->CIS.setParameters = SANE_FALSE;
+ dev->CIS.exposeTime = 0xAA;
+
+ cis_config_ccd (dev);
+
+ cis_restore_state(dev);
+
+}
+
+static SANE_Bool
+cis_calibrate (Mustek_PP_CIS_dev * dev)
+{
+ int i, saved_res = dev->CIS.res, saved_vres = dev->CIS.hw_vres;
+
+ /*
+ * Flow of operation observed from the twain driver
+ * (it is assumed that the lamp is at the origin, and that the CIS is
+ * configured for 300 DPI, ie. cis_reset_device has been called.)
+ *
+ * - Reset the device and return the lamp to its home position
+ *
+ * - Unknown short sequence
+ *
+ * - Send a sawtooth-like pattern to one of the memory banks.
+ *
+ * - Repetitive read_line of 2048 bytes, interleaved with an unknown
+ * command. The number varies between 102 and 170 times, but there
+ * doesn't seem to be any correlation with the current mode of the
+ * scanner, so I assume that the exact number isn't really relevant.
+ * The values that are read are the one that were sent to the bank,
+ * rotated by 1 byte in my case.
+ *
+ *
+ * It seems that the width of the black border is being measured at
+ * this stage, possibly multiple times till it stabilizes.
+ * I assume that the buffer is read 100 times to allow the lamp to
+ * warm up and that the the width of the black border is then being
+ * measured till it stabilizes. That would explain the minimum number
+ * of 102 iterations that I've seen.
+ *
+ * - reset the device
+ *
+ * - move the motor 110 steps forward. The TWAIN driver moves 90 steps,
+ * and I've used 90 steps for a long time too, but occasionally,
+ * 90 steps is a fraction to short to reach the start of the
+ * calibration strip (the motor movements are not very accurate;
+ * an offset of 1 mm is not unusual). Therefore, I've increased it to
+ * 110 steps. This gives us an additional 1.6 mm slack, which should
+ * prevent calibration errors.
+ * (Note that the MUSTEK_PP_CIS_????CP_DEFAULT_SKIP constants have to
+ * be adjusted if the number of steps is altered.)
+ *
+ * - configure the CIS : actual resolution + set parameters
+ *
+ */
+
+ /*
+ * We must make sure that we are in the scanning state; otherwise we may
+ * still be in the canceled state from a previous scan (even if terminated
+ * normally), and the whole calibration would go wrong.
+ */
+ dev->desc->state = STATE_SCANNING;
+
+ cis_reset_device (dev);
+ cis_return_home (dev, SANE_FALSE); /* Wait till it's home */
+
+ /* Use maximum resolution during calibration; otherwise we may calibrate
+ past the calibration strip. */
+ dev->CIS.hw_vres = dev->desc->dev->maxres;
+ /* This field remembers how many steps we still have to go @ max res */
+ dev->CIS.skipsToOrigin = dev->top_skip; /*max2hw_vres(dev, dev->top_skip); */
+
+ if (!cis_measure_delay(dev))
+ return SANE_FALSE;
+
+ cis_reset_device (dev);
+
+ /* Move motor 110 steps @ 300 DPI */
+ Mustek_PP_1015_write_reg_start(dev, MA1015W_MOTOR_CONTROL);
+ for (i=0; i<110; ++i)
+ {
+ if (dev->model == MUSTEK_PP_CIS600)
+ {
+ Mustek_PP_1015_write_reg_val (dev, 0x73);
+ }
+ else
+ {
+ Mustek_PP_1015_write_reg_val (dev, 0x7B);
+ }
+ cis_wait_motor_stable (dev);
+ }
+ Mustek_PP_1015_write_reg_stop(dev);
+
+ /* Next, we maximize the dynamic range of the scanner. During calibration
+ we don't want to extrapolate, so we limit the resolution if necessary */
+
+ if (dev->CIS.hw_hres < dev->CIS.res)
+ dev->CIS.res = dev->CIS.hw_hres;
+
+ if (!cis_maximize_dynamic_range(dev))
+ return SANE_FALSE;
+
+ if (!cis_normalize_ranges(dev))
+ return SANE_FALSE;
+
+ dev->CIS.res = saved_res;
+ dev->CIS.hw_vres = saved_vres;
+
+ /* Convert steps back to max res size, which are used during skipping */
+/* dev->CIS.skipsToOrigin = hw2max_vres(dev, dev->CIS.skipsToOrigin); */
+
+ /* Move to the origin */
+ DBG(3, "cis_calibrate: remaining skips to origin @maxres: %d\n",
+ dev->CIS.skipsToOrigin);
+ cis_move_motor(dev, dev->CIS.skipsToOrigin);
+
+ if (dev->calib_mode)
+ {
+ /* In calibration mode, we scan the interior of the scanner before the
+ glass plate in order to find the position of the calibration strip
+ and the start of the glass plate. */
+ DBG(3, "cis_calibrate: running in calibration mode. Returning home.\n");
+ cis_return_home (dev, SANE_FALSE); /* Wait till it's home */
+ }
+
+ return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE;
+
+}
+
+/******************************************************************************
+ ******************************************************************************
+ *** Mustek PP interface ***
+ ******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+* Init *
+******************************************************************************/
+
+/* Shared initialization routine */
+static SANE_Status cis_attach(SANE_String_Const port,
+ SANE_String_Const name,
+ SANE_Attach_Callback attach,
+ SANE_Int driverNo,
+ SANE_Int info)
+{
+ int fd;
+ SANE_Status status;
+ u_char asic;
+
+ status = sanei_pa4s2_open (port, &fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ SANE_Status altStatus;
+ SANE_String_Const altPort;
+
+ DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port,
+ sane_strstatus (status));
+
+ /* Make migration to libieee1284 painless for users that used
+ direct io in the past */
+ if (strcmp(port, "0x378") == 0) altPort = "parport0";
+ else if (strcmp(port, "0x278") == 0) altPort = "parport1";
+ else if (strcmp(port, "0x3BC") == 0) altPort = "parport2";
+ else return status;
+
+ DBG (2, "cis_attach: trying alternative port name: %s\n", altPort);
+
+ altStatus = sanei_pa4s2_open (altPort, &fd);
+ if (altStatus != SANE_STATUS_GOOD)
+ {
+ DBG (2, "cis_attach: couldn't attach to alternative port `%s' "
+ "(%s)\n", altPort, sane_strstatus (altStatus));
+ return status; /* Return original status, not alternative status */
+ }
+ }
+
+ M1015_START_LL;
+ M1015_START_HL;
+
+
+ sanei_pa4s2_enable (fd, SANE_TRUE);
+ SANEI_PA4S2_READBEGIN (fd, 0);
+ SANEI_PA4S2_READBYTE (fd, &asic);
+ SANEI_PA4S2_READEND (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+
+ sanei_pa4s2_close (fd);
+
+ if (asic != 0xA5) /* Identifies the MA1015 chipset */
+ {
+ /* CIS driver only works for MA1015 chipset */
+ DBG (2, "cis_attach: asic id (0x%02x) not recognized\n", asic);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "cis_attach: device %s attached\n", name);
+ DBG (3, "cis_attach: asic 0x%02x\n", asic);
+
+ return attach(port, name, driverNo, info);
+}
+
+SANE_Status cis600_drv_init(SANE_Int options, SANE_String_Const port,
+ SANE_String_Const name, SANE_Attach_Callback attach)
+{
+ if (options != CAP_NOTHING)
+ return SANE_STATUS_INVAL;
+
+ return cis_attach(port, name, attach, MUSTEK_PP_CIS600, MUSTEK_PP_CIS600);
+}
+
+SANE_Status cis1200_drv_init(SANE_Int options, SANE_String_Const port,
+ SANE_String_Const name, SANE_Attach_Callback attach)
+{
+ if (options != CAP_NOTHING)
+ return SANE_STATUS_INVAL;
+
+ return cis_attach(port, name, attach, MUSTEK_PP_CIS1200, MUSTEK_PP_CIS1200);
+}
+
+SANE_Status cis1200p_drv_init(SANE_Int options, SANE_String_Const port,
+ SANE_String_Const name, SANE_Attach_Callback attach)
+{
+ if (options != CAP_NOTHING)
+ return SANE_STATUS_INVAL;
+
+ return cis_attach(port, name, attach, MUSTEK_PP_CIS1200PLUS, MUSTEK_PP_CIS1200PLUS);
+}
+
+/******************************************************************************
+* Capabilities *
+******************************************************************************/
+void cis_drv_capabilities(SANE_Int info, SANE_String *model,
+ SANE_String *vendor, SANE_String *type,
+ SANE_Int *maxres, SANE_Int *minres,
+ SANE_Int *maxhsize, SANE_Int *maxvsize,
+ SANE_Int *caps)
+{
+ *vendor = strdup("Mustek");
+ *type = strdup("flatbed scanner");
+ *caps = CAP_NOTHING;
+
+ switch(info)
+ {
+ case MUSTEK_PP_CIS600:
+ *model = strdup("600CP");
+ *maxres = 600;
+ *minres = 50;
+ *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL;
+ *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL;
+ break;
+ case MUSTEK_PP_CIS1200:
+ *model = strdup("1200CP");
+ *maxres = 1200;
+ *minres = 50;
+ *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2;
+ *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2;
+ break;
+ case MUSTEK_PP_CIS1200PLUS:
+ *model = strdup("1200CP+");
+ *maxres = 1200;
+ *minres = 50;
+ *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2;
+ *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2;
+ break;
+ }
+}
+
+/******************************************************************************
+* Open *
+******************************************************************************/
+SANE_Status cis_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd)
+{
+ SANE_Status status;
+
+ if (caps != CAP_NOTHING)
+ {
+ DBG (1, "cis_drv_open: called with unknown capabilities (0x%02X)\n", caps);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "cis_drv_open: called for port %s\n", port);
+
+ status = sanei_pa4s2_open (port, fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ SANE_Status altStatus;
+ SANE_String_Const altPort;
+
+ DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port,
+ sane_strstatus (status));
+
+ /* Make migration to libieee1284 painless for users that used
+ direct io in the past */
+ if (strcmp(port, "0x378") == 0) altPort = "parport0";
+ else if (strcmp(port, "0x278") == 0) altPort = "parport1";
+ else if (strcmp(port, "0x3BC") == 0) altPort = "parport2";
+ else return status;
+
+ DBG (2, "cis_attach: trying alternative port name: %s\n", altPort);
+
+ altStatus = sanei_pa4s2_open (altPort, fd);
+ if (altStatus != SANE_STATUS_GOOD)
+ {
+ DBG (2, "cis_attach: couldn't attach to alternative port `%s' "
+ "(%s)\n", altPort, sane_strstatus (altStatus));
+ return status; /* Return original status, not alternative status */
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************
+* Setup *
+******************************************************************************/
+void cis_drv_setup (SANE_Handle hndl)
+{
+ Mustek_pp_Handle *dev = hndl;
+ Mustek_PP_CIS_dev *cisdev;
+ cisdev = (Mustek_PP_CIS_dev*)malloc(sizeof(Mustek_PP_CIS_dev));
+ if (cisdev == NULL)
+ {
+ DBG (2, "cis_drv_setup: not enough memory for device descriptor\n");
+ sanei_pa4s2_close (dev->fd);
+ return;
+ }
+ memset(cisdev, 0, sizeof(Mustek_PP_CIS_dev));
+ DBG(3, "cis_drv_setup: cis device allocated\n");
+
+ dev->lamp_on = 0;
+ dev->priv = cisdev;
+
+ cisdev->desc = dev;
+ cisdev->model = dev->dev->info;
+ cisdev->CIS.hw_hres = 300;
+ cisdev->CIS.cisRes = 300;
+ cisdev->CIS.hw_vres = 300;
+
+ /* Default values for configurable parameters; configuration file
+ may override them. */
+ cisdev->fast_skip = SANE_TRUE;
+ cisdev->bw_limit = 127;
+ cisdev->calib_mode = SANE_FALSE;
+ cisdev->engine_delay = 0;
+ if (cisdev->model == MUSTEK_PP_CIS600)
+ {
+ cisdev->top_skip = MUSTEK_PP_CIS_600CP_DEFAULT_SKIP;
+ }
+ else
+ {
+ cisdev->top_skip = MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP;
+ }
+}
+
+/******************************************************************************
+* Config *
+******************************************************************************/
+SANE_Status cis_drv_config(SANE_Handle hndl, SANE_String_Const optname,
+ SANE_String_Const optval)
+{
+ Mustek_pp_Handle *dev = hndl;
+ Mustek_PP_CIS_dev *cisdev = dev->priv;
+ int value = 0;
+ double dvalue = 0;
+ DBG (3, "cis_drv_cfg option: %s=%s\n", optname, optval ? optval : "");
+ if (!strcmp(optname, "top_adjust"))
+ {
+ if (!optval)
+ {
+ DBG (1, "cis_drv_config: missing value for option top_adjust\n");
+ return SANE_STATUS_INVAL;
+ }
+ dvalue = atof(optval);
+
+ /* An adjustment of +/- 5 mm should be sufficient and safe */
+ if (dvalue < -5.0)
+ {
+ DBG (1, "cis_drv_config: value for option top_adjust too small: "
+ "%.2f < -5; limiting to -5 mm\n", dvalue);
+ dvalue = -5.0;
+ }
+ if (dvalue > 5.0)
+ {
+ DBG (1, "cis_drv_config: value for option top_adjust too large: "
+ "%.2f > 5; limiting to 5 mm\n", dvalue);
+ dvalue = 5.0;
+ }
+ /* In practice, there is a lower bound on the value that can be used,
+ but if the top_skip value is smaller than that value, the only result
+ will be that the driver tries to move the head a negative number
+ of steps after calibration. The move routine just ignores negative
+ steps, so no harm can be done. */
+ cisdev->top_skip += MM_TO_PIXEL(dvalue, dev->dev->maxres);
+ DBG (3, "cis_drv_config: setting top skip value to %d\n",
+ cisdev->top_skip);
+
+ /* Just to be cautious; we don't want the head to hit the bottom */
+ if (cisdev->top_skip > 600) cisdev->top_skip = 600;
+ if (cisdev->top_skip < -600) cisdev->top_skip = -600;
+ }
+ else if (!strcmp(optname, "slow_skip"))
+ {
+ if (optval)
+ {
+ DBG (1, "cis_drv_config: unexpected value for option slow_skip\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (3, "cis_drv_config: disabling fast skipping\n");
+ cisdev->fast_skip = SANE_FALSE;
+ }
+ else if (!strcmp(optname, "bw"))
+ {
+ if (!optval)
+ {
+ DBG (1, "cis_drv_config: missing value for option bw\n");
+ return SANE_STATUS_INVAL;
+ }
+ value = atoi(optval);
+ if (value < 0 || value > 255)
+ {
+ DBG (1, "cis_drv_config: value for option bw out of range: "
+ "%d < 0 or %d > 255\n", value, value);
+ return SANE_STATUS_INVAL;
+ }
+ cisdev->bw_limit = value;
+ }
+ else if (!strcmp(optname, "calibration_mode"))
+ {
+ if (optval)
+ {
+ DBG (1, "cis_drv_config: unexpected value for option calibration_mode\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (3, "cis_drv_config: using calibration mode\n");
+ cisdev->calib_mode = SANE_TRUE;
+ }
+ else if (!strcmp(optname, "engine_delay"))
+ {
+ if (!optval)
+ {
+ DBG (1, "cis_drv_config: missing value for option engine_delay\n");
+ return SANE_STATUS_INVAL;
+ }
+ value = atoi(optval);
+ if (value < 0 || value > 100) /* 100 ms is already pretty slow */
+ {
+ DBG (1, "cis_drv_config: value for option engine_delay out of range: "
+ "%d < 0 or %d > 100\n", value, value);
+ return SANE_STATUS_INVAL;
+ }
+ cisdev->engine_delay = value;
+ }
+ else
+ {
+ DBG (1, "cis_drv_config: unknown options %s\n", optname);
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************
+* Close *
+******************************************************************************/
+void cis_drv_close (SANE_Handle hndl)
+{
+ Mustek_pp_Handle *dev = hndl;
+ Mustek_PP_CIS_dev *cisdev = dev->priv;
+ DBG (3, "cis_close: resetting device.\n");
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+ cis_reset_device (cisdev);
+ DBG (3, "cis_close: returning home.\n");
+ cis_return_home (cisdev, SANE_TRUE); /* Don't wait */
+ DBG (3, "cis_close: disabling fd.\n");
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (3, "cis_close: closing fd.\n");
+ sanei_pa4s2_close (dev->fd);
+ DBG (3, "cis_close: done.\n");
+ DBG (6, "cis_close: lamp_on: %d\n", (int)dev->lamp_on);
+ M1015_STOP_LL;
+ M1015_STOP_HL;
+}
+
+/******************************************************************************
+* Start *
+******************************************************************************/
+SANE_Status cis_drv_start (SANE_Handle hndl)
+{
+ Mustek_pp_Handle *dev = hndl;
+ Mustek_PP_CIS_dev *cisdev = dev->priv;
+ SANE_Int pixels = dev->params.pixels_per_line;
+
+ if (!cisdev)
+ {
+ DBG (2, "cis_drv_start: not enough memory for device\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ cisdev->CIS.exposeTime = 0xAA;
+ cisdev->CIS.setParameters = SANE_FALSE;
+ cisdev->CIS.use8KBank = SANE_TRUE;
+ cisdev->CIS.imagebytes = dev->bottomX - dev->topX;
+ cisdev->CIS.skipimagebytes = dev->topX;
+
+ cisdev->CIS.res = dev->res;
+
+ DBG (3, "cis_drv_start: %d dpi\n", dev->res);
+
+ if (dev->res <= 50 && cisdev->model != MUSTEK_PP_CIS1200PLUS)
+ {
+ cisdev->CIS.hw_hres = 50;
+ }
+ else if (dev->res <= 75 && cisdev->model == MUSTEK_PP_CIS1200PLUS)
+ {
+ cisdev->CIS.hw_hres = 75;
+ }
+ else if (dev->res <= 100)
+ {
+ cisdev->CIS.hw_hres = 100;
+ }
+ else if (dev->res <= 200)
+ {
+ cisdev->CIS.hw_hres = 200;
+ }
+ else if (dev->res <= 300)
+ {
+ cisdev->CIS.hw_hres = 300;
+ }
+ else
+ {
+ if (cisdev->model == MUSTEK_PP_CIS600)
+ {
+ cisdev->CIS.hw_hres = 300; /* Limit for 600 CP */
+ }
+ else if (dev->res <= 400)
+ {
+ cisdev->CIS.hw_hres = 400;
+ }
+ else
+ {
+ cisdev->CIS.hw_hres = 600; /* Limit for 1200 CP/CP+ */
+ }
+ }
+
+ if (cisdev->model == MUSTEK_PP_CIS600)
+ {
+ if (dev->res <= 150)
+ {
+ cisdev->CIS.hw_vres = 150;
+ }
+ else if (dev->res <= 300)
+ {
+ cisdev->CIS.hw_vres = 300;
+ }
+ else
+ {
+ cisdev->CIS.hw_vres = 600;
+ }
+ }
+ else
+ {
+ if (dev->res <= 300)
+ {
+ cisdev->CIS.hw_vres = 300;
+ }
+ else if (dev->res <= 600)
+ {
+ cisdev->CIS.hw_vres = 600;
+ }
+ else
+ {
+ cisdev->CIS.hw_vres = 1200;
+ }
+ }
+
+ if (cisdev->model == MUSTEK_PP_CIS600 ||
+ (cisdev->model == MUSTEK_PP_CIS1200 && dev->res <= 300))
+ cisdev->CIS.cisRes = 300;
+ else
+ cisdev->CIS.cisRes = 600;
+
+ /* Calibration only makes sense for hardware pixels, not for interpolated
+ pixels, so we limit the number of calibration pixels to the maximum
+ number of hardware pixels corresponding to the selected area */
+ if (dev->res > cisdev->CIS.hw_hres)
+ cisdev->calib_pixels = (pixels * cisdev->CIS.hw_hres) / dev->res;
+ else
+ cisdev->calib_pixels = pixels;
+
+ DBG (3, "cis_drv_start: hres: %d vres: %d cisres: %d\n",
+ cisdev->CIS.hw_hres, cisdev->CIS.hw_vres, cisdev->CIS.cisRes);
+
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+
+ cis_reset_device (cisdev);
+ cis_return_home (cisdev, SANE_TRUE); /* Don't wait here */
+
+#ifdef M1015_TRACE_REGS
+ {
+ int i, j;
+
+ /*
+ * Set all registers to -1 (uninitialized)
+ */
+ for (i=0; i<4; ++i)
+ {
+ cisdev->CIS.regs.in_regs[i] = -1;
+ for (j=0; j<4; ++j)
+ {
+ cisdev->CIS.regs.out_regs[i][j] = -1;
+ }
+ }
+
+ cisdev->CIS.regs.channel = -1;
+ /* These values have been read earlier. */
+ cisdev->CIS.regs.in_regs[0] = 0xA5;
+ }
+#endif
+
+ cis_reset_device (cisdev);
+ cis_return_home (cisdev, SANE_TRUE); /* no wait */
+
+ /* Allocate memory for temporary color buffer */
+ cisdev->tmpbuf = malloc (pixels);
+ if (cisdev->tmpbuf == NULL)
+ {
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (2, "cis_drv_start: not enough memory for temporary buffer\n");
+ free(cisdev);
+ dev->priv = NULL;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Allocate memory for calibration; calibrating interpolated pixels
+ makes no sense */
+ if (pixels > (dev->dev->maxhsize >> 1))
+ pixels = (dev->dev->maxhsize >> 1);
+
+ cisdev->calib_low[1] = malloc (pixels);
+ cisdev->calib_hi[1] = malloc (pixels);
+
+ if (cisdev->calib_low[1] == NULL || cisdev->calib_hi[1] == NULL)
+ {
+ free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
+ free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (2, "cis_drv_start: not enough memory for calibration buffer\n");
+ free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
+ free(cisdev); dev->priv = NULL;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ cisdev->calib_low[0] = NULL;
+ cisdev->calib_low[2] = NULL;
+ cisdev->calib_hi[0] = NULL;
+ cisdev->calib_hi[2] = NULL;
+ if (dev->mode == MODE_COLOR)
+ {
+ cisdev->calib_low[0] = malloc (pixels);
+ cisdev->calib_low[2] = malloc (pixels);
+ cisdev->calib_hi[0] = malloc (pixels);
+ cisdev->calib_hi[2] = malloc (pixels);
+
+ if ((cisdev->calib_low[0] == NULL) || (cisdev->calib_low[2] == NULL) ||
+ (cisdev->calib_hi[0] == NULL) || (cisdev->calib_hi[2] == NULL))
+ {
+ free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
+ free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
+ free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
+ free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
+ free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
+ free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
+ free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
+ free(cisdev); dev->priv = NULL;
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (2, "cis_drv_start: not enough memory for color calib buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ DBG (3, "cis_drv_start: executing calibration\n");
+
+ if (!cis_calibrate (cisdev))
+ {
+ free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
+ free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
+ free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
+ free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
+ free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
+ free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
+ free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
+ free(cisdev); dev->priv = NULL;
+ return SANE_STATUS_CANCELLED; /* Most likely cause */
+ }
+
+/* M1015_DISPLAY_REGS(dev, "after calibration"); */
+
+ cis_get_bank_count(cisdev);
+
+ cis_move_motor (cisdev, dev->topY); /* Measured in max resolution */
+
+ /* It is vital to reinitialize the scanner right before we start the
+ real scanning. Otherwise the bank synchronization may have gotten lost
+ by the time we reach the top of the scan area */
+
+ cisdev->CIS.setParameters = SANE_TRUE;
+ cis_config_ccd(cisdev);
+ cis_wait_read_ready(cisdev);
+
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+
+ cisdev->CIS.line_step =
+ SANE_FIX ((float) cisdev->CIS.hw_vres / (float) cisdev->CIS.res);
+
+ /*
+ * It is very important that line_diff is not initialized at zero !
+ * If it is set to zero, the motor will keep on moving forever (or better,
+ * till the scanner breaks).
+ */
+ cisdev->line_diff = cisdev->CIS.line_step;
+ cisdev->ccd_line = 0;
+ cisdev->line = 0;
+ cisdev->lines_left = dev->params.lines;
+
+ dev->state = STATE_SCANNING;
+
+ DBG (3, "cis_drv_start: device ready for scanning\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/******************************************************************************
+* Read *
+******************************************************************************/
+void cis_drv_read (SANE_Handle hndl, SANE_Byte *buffer)
+{
+ Mustek_pp_Handle *dev = hndl;
+ Mustek_PP_CIS_dev *cisdev = dev->priv;
+ DBG(6, "cis_drv_read: Reading line\n");
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+ switch (dev->mode)
+ {
+ case MODE_BW:
+ cis_get_lineart_line(cisdev, buffer);
+ break;
+
+ case MODE_GRAYSCALE:
+ cis_get_grayscale_line(cisdev, buffer);
+ break;
+
+ case MODE_COLOR:
+ cis_get_color_line(cisdev, buffer);
+ break;
+ }
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+}
+
+/******************************************************************************
+* Stop *
+******************************************************************************/
+void cis_drv_stop (SANE_Handle hndl)
+{
+ Mustek_pp_Handle *dev = hndl;
+ Mustek_PP_CIS_dev *cisdev = dev->priv;
+
+ /* device is scanning: return lamp and free buffers */
+ DBG (3, "cis_drv_stop: stopping current scan\n");
+ dev->state = STATE_CANCELLED;
+
+ DBG (9, "cis_drv_stop: enabling fd\n");
+ sanei_pa4s2_enable (dev->fd, SANE_TRUE);
+ Mustek_PP_1015_write_reg(cisdev, MA1015W_MOTOR_CONTROL, 0); /* stop */
+ DBG (9, "cis_drv_stop: resetting device (1)\n");
+ cis_reset_device (cisdev);
+ DBG (9, "cis_drv_stop: returning home\n");
+ cis_return_home (cisdev, SANE_TRUE); /* don't wait */
+ DBG (9, "cis_drv_stop: resetting device (2)\n");
+ cis_reset_device (cisdev);
+ DBG (9, "cis_drv_stop: disabling fd\n");
+ sanei_pa4s2_enable (dev->fd, SANE_FALSE);
+ DBG (9, "cis_drv_stop: freeing buffers\n");
+
+ /* This is no good: canceling while the device is scanning and
+ freeing the data buffers can result in illegal memory accesses if
+ the device is still scanning in another thread. */
+ free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
+ free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
+ free (cisdev->tmpbuf); cisdev->tmpbuf = NULL;
+ DBG (3, "cis_drv_stop: freed green and temporary buffers\n");
+
+ if (cisdev->CIS.mode == MODE_COLOR)
+ {
+ free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
+ free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
+ free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
+ free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
+ }
+ DBG (3, "cis_drv_stop: freed buffers\n");
+ DBG (6, "cis_drv_stop: lamp_on: %d\n", (int)dev->lamp_on);
+}
diff --git a/backend/mustek_pp_cis.h b/backend/mustek_pp_cis.h
new file mode 100644
index 0000000..2a38d89
--- /dev/null
+++ b/backend/mustek_pp_cis.h
@@ -0,0 +1,273 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2001-2003 Eddy De Greef <eddy_de_greef at scarlet dot be>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek PP flatbed _CIS_ scanners.
+*/
+
+#ifndef mustek_pp_cis_h
+#define mustek_pp_cis_h
+
+#include "../include/sane/sane.h"
+
+/******************************************************************************
+ * Read register symbols.
+ *****************************************************************************/
+typedef enum {
+ MA1015R_ASIC = 0x00,
+ MA1015R_SCAN_VAL = 0x01,
+ MA1015R_MOTOR = 0x02,
+ MA1015R_BANK_COUNT = 0x03
+ }
+Mustek_PP_1015R_reg;
+
+/******************************************************************************
+ * Read register bitmask symbols.
+ *****************************************************************************/
+typedef enum {
+ MA1015B_MOTOR_HOME = 0x01,
+ MA1015B_MOTOR_STABLE = 0x03
+ }
+Mustek_PP_1015R_bit;
+
+/******************************************************************************
+ * Write register symbols: (bank number << 4) + register number.
+ *****************************************************************************/
+
+typedef enum {
+ MA1015W_RED_REF = 0x00,
+ MA1015W_GREEN_REF = 0x01,
+ MA1015W_BLUE_REF = 0x02,
+ MA1015W_DPI_CONTROL = 0x03,
+
+ MA1015W_BYTE_COUNT_HB = 0x10,
+ MA1015W_BYTE_COUNT_LB = 0x11,
+ MA1015W_SKIP_COUNT = 0x12,
+ MA1015W_EXPOSE_TIME = 0x13,
+
+ MA1015W_SRAM_SOURCE_PC = 0x20,
+ MA1015W_MOTOR_CONTROL = 0x21,
+ MA1015W_UNKNOWN_42 = 0x22,
+ MA1015W_UNKNOWN_82 = 0x23,
+
+ MA1015W_POWER_ON_DELAY = 0x30,
+ MA1015W_CCD_TIMING = 0x31,
+ MA1015W_CCD_TIMING_ADJ = 0x32,
+ MA1015W_RIGHT_BOUND = 0x33
+ }
+Mustek_PP_1015W_reg;
+
+
+/******************************************************************************
+ * Mustek MA1015 register tracing structure.
+ * Can be used to trace the status of the registers of the MA1015 chipset
+ * during debugging. Most fields are not used in production code.
+ *****************************************************************************/
+typedef struct Mustek_PP_1015_Registers
+{
+ SANE_Byte in_regs[4];
+ SANE_Byte out_regs[4][4];
+ SANE_Byte channel;
+
+ Mustek_PP_1015R_reg current_read_reg;
+ SANE_Int read_count;
+
+ Mustek_PP_1015W_reg current_write_reg; /* always used */
+ SANE_Int write_count;
+}
+Mustek_PP_1015_Registers;
+
+
+/******************************************************************************
+ * CIS information
+ *****************************************************************************/
+typedef struct Mustek_PP_CIS_Info
+{
+ /* Expose time (= time the lamp is on ?) */
+ SANE_Byte exposeTime;
+
+ /* Power-on delay (= time between lamp on and start of capturing ?) */
+ SANE_Byte powerOnDelay[3];
+
+ /* Motor step control */
+ SANE_Byte phaseType;
+
+ /* Use 8K bank or 4K bank */
+ SANE_Bool use8KBank;
+
+ /* High resolution (600 DPI) or not (300 DPI) */
+ SANE_Bool highRes;
+
+ /* delay between pixels; reading too fast causes stability problems */
+ SANE_Int delay;
+
+ /* Register representation */
+ Mustek_PP_1015_Registers regs;
+
+ /* Current color channel */
+ SANE_Int channel;
+
+ /* Blocks motor movements during calibration */
+ SANE_Bool dontMove;
+
+ /* Prevents read increment the before the first read */
+ SANE_Bool dontIncRead;
+
+ /* Controls whether or not calibration parameters are transmitted
+ during CIS configuration */
+ SANE_Bool setParameters;
+
+ /* Number of lines to skip to reach the origin (used during calibration) */
+ SANE_Int skipsToOrigin;
+
+ /* Physical resolution of the CIS: either 300 or 600 DPI */
+ SANE_Int cisRes;
+
+ /* CCD mode (color/grayscale/lineart) */
+ SANE_Int mode;
+
+ /* how many positions to skip until scan area starts @ max res */
+ SANE_Int skipimagebytes;
+
+ /* how many image bytes to scan @ max res */
+ SANE_Int imagebytes;
+
+ /* total skip, adjusted to resolution */
+ SANE_Int adjustskip;
+
+ /* current resolution */
+ SANE_Int res;
+
+ /* current horizontal hardware resolution */
+ SANE_Int hw_hres;
+
+ /* current vertical hardware resolution */
+ SANE_Int hw_vres;
+
+ /* how many positions to scan for one pixel */
+ SANE_Int hres_step;
+
+ /* how many lines to scan for one scanline */
+ SANE_Int line_step;
+
+ /* inversion */
+ SANE_Bool invert;
+
+} Mustek_PP_CIS_Info;
+
+struct Mustek_pp_Handle;
+typedef struct Mustek_PP_CIS_dev
+{
+ /* device descriptor */
+ struct Mustek_pp_Handle *desc;
+
+ /* model identification (600CP/1200CP/1200CP+) */
+ SANE_Int model;
+
+ /* CIS status */
+ Mustek_PP_CIS_Info CIS;
+
+ /* used during calibration & return_home */
+ Mustek_PP_CIS_Info Saved_CIS;
+
+ /* bank count */
+ int bank_count;
+
+ /* those are used to count the hardware line the scanner is at, the
+ line the current bank is at and the lines we've scanned */
+ int line;
+ int line_diff;
+ int ccd_line;
+ int lines_left;
+
+ /* Configuration parameters that the user can calibrate */
+ /* Starting position at the top */
+ SANE_Int top_skip;
+ /* Use fast skipping method for head movements ? (default: yes) */
+ SANE_Bool fast_skip;
+ /* Discrimination value to choose between black and white */
+ SANE_Byte bw_limit;
+ /* Run in calibration mode ? (default: no) */
+ SANE_Bool calib_mode;
+ /* Extra delay between engine commands (ms). Default: zero. */
+ SANE_Int engine_delay;
+
+ /* temporary buffer for 1 line (of one color) */
+ SANE_Byte *tmpbuf;
+
+ /* calibration buffers (low cut, high cut) */
+ SANE_Byte *calib_low[3];
+ SANE_Byte *calib_hi[3];
+
+ /* Number of pixels in calibration buffers (<= number of pixels to scan) */
+ int calib_pixels;
+
+} Mustek_PP_CIS_dev;
+
+#define CIS_AVERAGE_NONE(dev) Mustek_PP_1015_send_command(dev, 0x05)
+#define CIS_AVERAGE_TWOPIXEL(dev) Mustek_PP_1015_send_command(dev, 0x15)
+#define CIS_AVERAGE_THREEPIXEL(dev) Mustek_PP_1015_send_command(dev, 0x35)
+#define CIS_WIDTH_4K(dev) Mustek_PP_1015_send_command(dev, 0x05)
+#define CIS_WIDTH_8K(dev) Mustek_PP_1015_send_command(dev, 0x45)
+#define CIS_STOP_TOGGLE(dev) Mustek_PP_1015_send_command(dev, 0x85)
+
+#define CIS_PIP_AS_INPUT(dev) Mustek_PP_1015_send_command(dev, 0x46)
+#define CIS_PIP_AS_OUTPUT_0(dev) Mustek_PP_1015_send_command(dev, 0x06)
+#define CIS_PIP_AS_OUTPUT_1(dev) Mustek_PP_1015_send_command(dev, 0x16)
+#define CIS_POP_AS_INPUT(dev) Mustek_PP_1015_send_command(dev, 0x86)
+#define CIS_POP_AS_OUTPUT_0(dev) Mustek_PP_1015_send_command(dev, 0x06)
+#define CIS_POP_AS_OUTPUT_1(dev) Mustek_PP_1015_send_command(dev, 0x26)
+
+#define CIS_INC_READ(dev) Mustek_PP_1015_send_command(dev, 0x07)
+#define CIS_CLEAR_WRITE_BANK(dev) Mustek_PP_1015_send_command(dev, 0x17)
+#define CIS_CLEAR_READ_BANK(dev) Mustek_PP_1015_send_command(dev, 0x27)
+#define CIS_CLEAR_FULLFLAG(dev) Mustek_PP_1015_send_command(dev, 0x37)
+#define CIS_POWER_ON(dev) Mustek_PP_1015_send_command(dev, 0x47)
+#define CIS_POWER_OFF(dev) Mustek_PP_1015_send_command(dev, 0x57)
+#define CIS_CLEAR_WRITE_ADDR(dev) Mustek_PP_1015_send_command(dev, 0x67)
+#define CIS_CLEAR_TOGGLE(dev) Mustek_PP_1015_send_command(dev, 0x77)
+
+#define CIS_NO(dev) Mustek_PP_1015_send_command(dev, 0x08)
+#define CIS_OST_POS(dev) Mustek_PP_1015_send_command(dev, 0x18)
+#define CIS_OST_TYP(dev) Mustek_PP_1015_send_command(dev, 0x28)
+#define CIS_OP_MOD_0(dev) Mustek_PP_1015_send_command(dev, 0x48)
+#define CIS_OP_MOD_1(dev) Mustek_PP_1015_send_command(dev, 0x88)
+
+#endif /* __mustek_pp_cis_h */
diff --git a/backend/mustek_pp_decl.h b/backend/mustek_pp_decl.h
new file mode 100644
index 0000000..3875f3d
--- /dev/null
+++ b/backend/mustek_pp_decl.h
@@ -0,0 +1,83 @@
+
+#ifndef MUSTEK_PP_DECL_H
+#define MUSTEK_PP_DECL_H
+/* debug driver, version 0.11-devel, author Jochen Eisinger */
+static SANE_Status debug_drv_init (SANE_Int options, SANE_String_Const port,
+ SANE_String_Const name, SANE_Attach_Callback attach);
+static void debug_drv_capabilities (SANE_Int info, SANE_String *model,
+ SANE_String *vendor, SANE_String *type,
+ SANE_Int *maxres, SANE_Int *minres,
+ SANE_Int *maxhsize, SANE_Int *maxvsize,
+ SANE_Int *caps);
+static SANE_Status debug_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd);
+static void debug_drv_setup (SANE_Handle hndl);
+static SANE_Status debug_drv_config (SANE_Handle hndl,
+ SANE_String_Const optname,
+ SANE_String_Const optval);
+static void debug_drv_close (SANE_Handle hndl);
+static SANE_Status debug_drv_start (SANE_Handle hndl);
+static void debug_drv_read (SANE_Handle hndl, SANE_Byte *buffer);
+static void debug_drv_stop (SANE_Handle hndl);
+
+
+/* CIS drivers for 600CP, 1200CP, and 1200CP+
+ Version 0.13-beta, author Eddy De Greef */
+
+static SANE_Status cis600_drv_init (SANE_Int options,
+ SANE_String_Const port,
+ SANE_String_Const name,
+ SANE_Attach_Callback attach);
+static SANE_Status cis1200_drv_init (SANE_Int options,
+ SANE_String_Const port,
+ SANE_String_Const name,
+ SANE_Attach_Callback attach);
+static SANE_Status cis1200p_drv_init(SANE_Int options,
+ SANE_String_Const port,
+ SANE_String_Const name,
+ SANE_Attach_Callback attach);
+static void cis_drv_capabilities(SANE_Int info,
+ SANE_String *model,
+ SANE_String *vendor,
+ SANE_String *type,
+ SANE_Int *maxres,
+ SANE_Int *minres,
+ SANE_Int *maxhsize,
+ SANE_Int *maxvsize,
+ SANE_Int *caps);
+static SANE_Status cis_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd);
+static void cis_drv_setup (SANE_Handle hndl);
+static SANE_Status cis_drv_config (SANE_Handle hndl,
+ SANE_String_Const optname,
+ SANE_String_Const optval);
+static void cis_drv_close (SANE_Handle hndl);
+static SANE_Status cis_drv_start (SANE_Handle hndl);
+static void cis_drv_read (SANE_Handle hndl, SANE_Byte *buffer);
+static void cis_drv_stop (SANE_Handle hndl);
+
+/* CCD drivers for 300 dpi models
+ Version 0.11-devel, author Jochen Eisinger */
+
+static SANE_Status ccd300_init (SANE_Int options,
+ SANE_String_Const port,
+ SANE_String_Const name,
+ SANE_Attach_Callback attach);
+static void ccd300_capabilities(SANE_Int info,
+ SANE_String *model,
+ SANE_String *vendor,
+ SANE_String *type,
+ SANE_Int *maxres,
+ SANE_Int *minres,
+ SANE_Int *maxhsize,
+ SANE_Int *maxvsize,
+ SANE_Int *caps);
+static SANE_Status ccd300_open (SANE_String port, SANE_Int caps, SANE_Int *fd);
+static void ccd300_setup (SANE_Handle hndl);
+static SANE_Status ccd300_config (SANE_Handle hndl,
+ SANE_String_Const optname,
+ SANE_String_Const optval);
+static void ccd300_close (SANE_Handle hndl);
+static SANE_Status ccd300_start (SANE_Handle hndl);
+static void ccd300_read (SANE_Handle hndl, SANE_Byte *buffer);
+static void ccd300_stop (SANE_Handle hndl);
+
+#endif
diff --git a/backend/mustek_pp_drivers.h b/backend/mustek_pp_drivers.h
new file mode 100644
index 0000000..19de4de
--- /dev/null
+++ b/backend/mustek_pp_drivers.h
@@ -0,0 +1,72 @@
+
+#ifndef MUSTEK_PP_DRIVERS_H
+#define MUSTEK_PP_DRIVERS_H
+
+#include "mustek_pp.h"
+#include "mustek_pp_decl.h"
+
+static Mustek_pp_Functions Mustek_pp_Drivers[] = {
+ {
+ "debug", "Jochen Eisinger", "0.11-devel",
+ debug_drv_init,
+ debug_drv_capabilities,
+ debug_drv_open,
+ debug_drv_setup,
+ debug_drv_config,
+ debug_drv_close,
+ debug_drv_start,
+ debug_drv_read,
+ debug_drv_stop
+ },
+ {
+ "cis600", "Eddy De Greef", "0.13-beta",
+ cis600_drv_init,
+ cis_drv_capabilities,
+ cis_drv_open,
+ cis_drv_setup,
+ cis_drv_config,
+ cis_drv_close,
+ cis_drv_start,
+ cis_drv_read,
+ cis_drv_stop
+ },
+ {
+ "cis1200", "Eddy De Greef", "0.13-beta",
+ cis1200_drv_init,
+ cis_drv_capabilities,
+ cis_drv_open,
+ cis_drv_setup,
+ cis_drv_config,
+ cis_drv_close,
+ cis_drv_start,
+ cis_drv_read,
+ cis_drv_stop
+ },
+ {
+ "cis1200+", "Eddy De Greef", "0.13-beta",
+ cis1200p_drv_init,
+ cis_drv_capabilities,
+ cis_drv_open,
+ cis_drv_setup,
+ cis_drv_config,
+ cis_drv_close,
+ cis_drv_start,
+ cis_drv_read,
+ cis_drv_stop
+ },
+ {
+ "ccd300", "Jochen Eisinger", "0.11-devel",
+ ccd300_init,
+ ccd300_capabilities,
+ ccd300_open,
+ ccd300_setup,
+ ccd300_config,
+ ccd300_close,
+ ccd300_start,
+ ccd300_read,
+ ccd300_stop
+ }
+
+};
+
+#endif
diff --git a/backend/mustek_pp_null.c b/backend/mustek_pp_null.c
new file mode 100644
index 0000000..e1c6a35
--- /dev/null
+++ b/backend/mustek_pp_null.c
@@ -0,0 +1,153 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek PP flatbed scanners. */
+
+#include "../include/sane/config.h"
+
+#if defined(HAVE_STDLIB_H)
+# include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <stdio.h>
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#elif defined(HAVE_STRINGS_H)
+# include <strings.h>
+#endif
+
+#define DEBUG_DECLARE_ONLY
+
+#include "mustek_pp.h"
+#include "mustek_pp_decl.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+
+#define MUSTEK_PP_NULL_DRIVER 0
+
+static SANE_Status
+debug_drv_init(SANE_Int options, SANE_String_Const port,
+ SANE_String_Const name, SANE_Attach_Callback attach)
+{
+
+ if (options != CAP_NOTHING)
+ return SANE_STATUS_INVAL;
+
+ return attach(port, name, MUSTEK_PP_NULL_DRIVER, 0);
+
+}
+
+/*ARGSUSED*/
+static void
+debug_drv_capabilities(SANE_Int info __UNUSED__, SANE_String *model,
+ SANE_String *vendor, SANE_String *type,
+ SANE_Int *maxres, SANE_Int *minres,
+ SANE_Int *maxhsize, SANE_Int *maxvsize,
+ SANE_Int *caps)
+{
+
+ *model = strdup("debugger");
+ *vendor = strdup("mustek_pp");
+ *type = strdup("software emulated");
+ *maxres = 300;
+ *minres = 50;
+ *maxhsize = 1000;
+ *maxvsize = 3000;
+ *caps = CAP_NOTHING;
+
+}
+
+/*ARGSUSED*/
+static SANE_Status
+debug_drv_open (SANE_String port __UNUSED__,
+ SANE_Int caps __UNUSED__, SANE_Int *fd)
+{
+ *fd = 1;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+debug_drv_setup (SANE_Handle hndl)
+{
+
+ Mustek_pp_Handle *dev = hndl;
+
+ dev->lamp_on = 0;
+ dev->priv = NULL;
+}
+
+/*ARGSUSED*/
+static SANE_Status
+debug_drv_config(SANE_Handle hndl __UNUSED__,
+ SANE_String_Const optname,
+ SANE_String_Const optval)
+{
+ DBG (3, "debug_drv cfg option: %s=%s\n", optname, optval ? optval : "");
+ return SANE_STATUS_GOOD;
+}
+
+/*ARGSUSED*/
+static void
+debug_drv_close (SANE_Handle hndl __UNUSED__)
+{
+}
+
+/*ARGSUSED*/
+static SANE_Status
+debug_drv_start (SANE_Handle hndl __UNUSED__)
+{
+ return SANE_STATUS_GOOD;
+}
+
+static void
+debug_drv_read (SANE_Handle hndl, SANE_Byte *buffer)
+{
+
+ Mustek_pp_Handle *dev = hndl;
+
+ memset (buffer, 0, dev->params.bytes_per_line);
+}
+
+/*ARGSUSED*/
+static void
+debug_drv_stop (SANE_Handle hndl __UNUSED__)
+{
+
+}
diff --git a/backend/mustek_scsi_pp.c b/backend/mustek_scsi_pp.c
new file mode 100644
index 0000000..3dab544
--- /dev/null
+++ b/backend/mustek_scsi_pp.c
@@ -0,0 +1,1086 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 James Perry
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the Mustek SCSI-over-parallel port protocol
+ used by, for example, the Paragon 600 II EP
+*/
+
+
+/**************************************************************************/
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <time.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_pa4s2.h"
+
+/*
+ * Number of times to retry sending a SCSI command before giving up
+ */
+#define MUSTEK_SCSI_PP_NUM_RETRIES 4
+
+/*
+ * Internal middle-level API functionality
+ */
+static int mustek_scsi_pp_timeout = 5000;
+
+/* FIXME: use same method as mustek.c ? */
+static int
+mustek_scsi_pp_get_time ()
+{
+ struct timeval tv;
+ int retval;
+
+ gettimeofday (&tv, 0);
+
+ retval = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+ return retval;
+}
+
+static u_char mustek_scsi_pp_register = 0;
+
+
+static SANE_Status
+mustek_scsi_pp_select_register (int fd, u_char reg)
+{
+ DBG (5, "mustek_scsi_pp_select_register: selecting register %d on fd %d\n",
+ reg, fd);
+
+ mustek_scsi_pp_register = reg;
+
+ return sanei_pa4s2_scsi_pp_reg_select (fd, reg);
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_valid_status (int fd)
+{
+ int start_time;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_valid_status: entering\n");
+
+ start_time = mustek_scsi_pp_get_time ();
+
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_wait_for_valid_status: I/O error while getting status\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status &= 0xf0;
+
+ if ((status != 0xf0) && (!(status & 0x40)) && (status & 0x20))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_valid_status: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - start_time) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_valid_status: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_5_set (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_set: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x20)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_5_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_set: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_5_clear (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_clear: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (!(status & 0x20))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_5_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_clear: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_7_set (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_set: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x80)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_7_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_set: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_7_clear (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_clear: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (!(status & 0x80))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_7_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_clear: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_4_set (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_set: entering\n");
+
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (status & 0x10)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x40)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: bit 6 set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x10)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_4_clear (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_clear: entering\n");
+
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (!(status & 0x10))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x40)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: bit 6 set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (!(status & 0x10))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static u_char mustek_scsi_pp_bit_4_state = 0;
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_4_toggle (int fd)
+{
+ SANE_Status result;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_toggle: entering\n");
+
+ mustek_scsi_pp_bit_4_state ^= 0xff;
+ if (mustek_scsi_pp_bit_4_state)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_toggle: waiting for set\n");
+ result = mustek_scsi_pp_wait_for_status_bit_4_set (fd);
+ mustek_scsi_pp_timeout = 5000;
+ }
+ else
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_toggle: waiting for clear\n");
+ result = mustek_scsi_pp_wait_for_status_bit_4_clear (fd);
+ }
+
+ return result;
+}
+
+static SANE_Status
+mustek_scsi_pp_send_command_byte (int fd, u_char cmd)
+{
+ DBG (5, "mustek_scsi_pp_send_command byte: sending 0x%02X\n", cmd);
+
+ mustek_scsi_pp_select_register (fd, 0);
+
+ if (mustek_scsi_pp_wait_for_status_bit_7_clear (fd) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_select_register (fd, 0);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (sanei_pa4s2_writebyte (fd, mustek_scsi_pp_register, cmd) !=
+ SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ mustek_scsi_pp_select_register (fd, 1);
+
+ if (mustek_scsi_pp_wait_for_status_bit_7_set (fd) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_select_register (fd, 0);
+ return SANE_STATUS_IO_ERROR;
+ }
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (5, "mustek_scsi_pp_send_command_byte: returning success\n");
+ return SANE_STATUS_GOOD;
+}
+
+static u_char
+mustek_scsi_pp_read_response (int fd)
+{
+ u_char result;
+
+ DBG (5, "mustek_scsi_pp_read_response: entering\n");
+
+ if (mustek_scsi_pp_wait_for_status_bit_7_set (fd) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_select_register (fd, 0);
+ return 0xff;
+ }
+
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD)
+ {
+ return 0xff;
+ }
+ if (sanei_pa4s2_readbyte (fd, &result) != SANE_STATUS_GOOD)
+ {
+ return 0xff;
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ return 0xff;
+ }
+
+ mustek_scsi_pp_select_register (fd, 1);
+ if (mustek_scsi_pp_wait_for_status_bit_7_clear (fd) != SANE_STATUS_GOOD)
+ {
+ result = 0xff;
+ }
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (5, "mustek_scsi_pp_read_response: returning 0x%02X\n", result);
+ return result;
+}
+
+static SANE_Status
+mustek_scsi_pp_check_response (int fd)
+{
+ if (mustek_scsi_pp_wait_for_status_bit_5_clear (fd) != SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (mustek_scsi_pp_read_response (fd) != 0xA5)
+ {
+ DBG (2, "mustek_scsi_pp_check_response: response!=0xA5\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "mustek_scsi_pp_check_response: returning success\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+mustek_scsi_pp_send_command (int fd, const u_char * cmd)
+{
+ int i;
+ signed char checksum;
+
+ DBG (5, "mustek_scsi_pp_send_command: sending SCSI command 0x%02X\n",
+ cmd[0]);
+
+ /* Set timeout depending on command type */
+ switch (cmd[0])
+ {
+ case 0xf:
+ case 0x8:
+ mustek_scsi_pp_timeout = 1000;
+ break;
+ case 0x2:
+ mustek_scsi_pp_timeout = 80;
+ break;
+ case 0x12:
+ case 0x3:
+ case 0x11:
+ mustek_scsi_pp_timeout = 500;
+ break;
+ default:
+ mustek_scsi_pp_timeout = 1000;
+ break;
+ }
+
+ if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_command: timed out waiting for bit 5 to set\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ checksum = 0;
+ for (i = 0; i < 6; i++)
+ {
+ if (mustek_scsi_pp_send_command_byte (fd, cmd[i]) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_command: error sending byte %d (0x%02X)\n",
+ i, cmd[i]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ checksum += cmd[i];
+ }
+ if (mustek_scsi_pp_send_command_byte (fd, -checksum) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_command: error sending checksum (0x%02X)\n",
+ -checksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return mustek_scsi_pp_check_response (fd);
+}
+
+static SANE_Status
+mustek_scsi_pp_send_data_block (int fd, const u_char * data, int len)
+{
+ int i;
+ signed char checksum;
+
+ DBG (5, "mustek_scsi_pp_send_data_block: sending block of length %d\n",
+ len);
+
+ if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_data_block: timed out waiting for bit 5 to set\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ checksum = 0;
+ for (i = 0; i < len; i++)
+ {
+ if (mustek_scsi_pp_send_command_byte (fd, data[i]) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_data_block: error sending byte %d (0x%02X)\n",
+ i, data[i]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ checksum += data[i];
+ }
+ if (mustek_scsi_pp_send_command_byte (fd, -checksum) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_data_block: error sending checksum (0x%02X)\n",
+ -checksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return mustek_scsi_pp_check_response (fd);
+}
+
+static SANE_Status
+mustek_scsi_pp_read_data_block (int fd, u_char * buffer, int len)
+{
+ int i;
+ signed char checksum;
+
+ DBG (5, "mustek_scsi_pp_read_data_block: reading block of length %d\n",
+ len);
+
+ if (mustek_scsi_pp_wait_for_status_bit_5_clear (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_read_data_block: timed out waiting for bit 5 to clear\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ checksum = 0;
+ for (i = 0; i < len; i++)
+ {
+ buffer[i] = mustek_scsi_pp_read_response (fd);
+ checksum += buffer[i];
+ }
+ if ((signed char) mustek_scsi_pp_read_response (fd) != (-checksum))
+ {
+ mustek_scsi_pp_send_command_byte (fd, 0xff);
+ DBG (2, "mustek_scsi_pp_read_data_block: checksums do not match\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_read_data_block: error waiting for bit 5 to set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (mustek_scsi_pp_send_command_byte (fd, 0) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_send_command_byte (fd, 0xff);
+ DBG (2, "mustek_scsi_pp_read_data_block: error sending final 0 byte\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "mustek_scsi_pp_read_data_block: returning success\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/*
+ * Externally visible functions
+ */
+SANE_Status
+mustek_scsi_pp_open (const char *dev, int *fd)
+{
+ SANE_Status status;
+
+ status = sanei_pa4s2_scsi_pp_open (dev, fd);
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (5, "mustek_scsi_pp_open: device %s opened as fd %d\n", dev, *fd);
+ }
+ else
+ {
+ DBG (2, "mustek_scsi_pp_open: error opening device %s\n", dev);
+ }
+ return status;
+}
+
+static void
+mustek_scsi_pp_close (int fd)
+{
+ DBG (5, "mustek_scsi_pp_close: closing fd %d\n", fd);
+ sanei_pa4s2_close (fd);
+}
+
+static void
+mustek_scsi_pp_exit (void)
+{
+ DBG (5, "mustek_scsi_pp_exit: entering\n");
+}
+
+static SANE_Status
+mustek_scsi_pp_test_ready (int fd)
+{
+ u_char status;
+ SANE_Status retval;
+
+ DBG (5, "mustek_scsi_pp_test_ready: entering with fd=%d\n", fd);
+
+ if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_test_ready: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_test_ready: error getting status\n");
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ return SANE_STATUS_INVAL;
+ }
+
+ retval = SANE_STATUS_GOOD;
+
+ status &= 0xf0;
+
+ if (status == 0xf0)
+ {
+ retval = SANE_STATUS_DEVICE_BUSY;
+ }
+ if (status & 0x40)
+ {
+ retval = SANE_STATUS_DEVICE_BUSY;
+ }
+ if (!(status & 0x20))
+ {
+ retval = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_test_ready: error disabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (retval == SANE_STATUS_GOOD)
+ {
+ DBG (5, "mustek_scsi_pp_test_ready: returning SANE_STATUS_GOOD\n");
+ }
+ else
+ {
+ DBG (5,
+ "mustek_scsi_pp_test_ready: returning SANE_STATUS_DEVICE_BUSY\n");
+ }
+
+ return retval;
+}
+
+static SANE_Status
+mustek_scsi_pp_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ SANE_Status stat;
+ int num_tries = 0;
+ static u_char scan_options = 0;
+ const u_char *cmd;
+ u_char stop_cmd[6] = { 0x1b, 0, 0, 0, 0, 0 };
+ int max_tries;
+
+ max_tries = MUSTEK_SCSI_PP_NUM_RETRIES;
+
+ cmd = (const u_char *) src;
+
+ DBG (5, "mustek_scsi_pp_cmd: sending command 0x%02X to device %d\n",
+ cmd[0], fd);
+
+ if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (cmd[0] == 0x1b)
+ {
+ if (!(cmd[4] & 0x1))
+ {
+ unsigned char c;
+ int i;
+
+ DBG (5, "mustek_scsi_pp_cmd: doing stop-specific stuff\n");
+
+ /*
+ * Remembers what flags were sent with a 'start' command, and
+ * replicate them with a stop command.
+ */
+ stop_cmd[4] = scan_options & 0xfe;
+ cmd = &stop_cmd[0];
+
+ /*
+ * In color mode at least, the scanner doesn't seem to like stopping at
+ * the end. It's a bit of a horrible hack, but reading loads of bytes and
+ * allowing 20 tries for the stop command is the only way I've found that
+ * solves the problem.
+ */
+ max_tries = 20;
+
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error in readbegin for stop\n");
+ }
+
+ for (i = 0; i < 10000; i++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &c) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_cmd: error reading byte for stop\n");
+ break;
+ }
+ DBG (5, "mustek_scsi_pp_cmd: successfully read byte %d\n", i);
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error in readend for stop\n");
+ }
+ }
+ }
+
+ if (cmd[0] == 0x08)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: doing read-specific stuff\n");
+ mustek_scsi_pp_timeout = 30000;
+ mustek_scsi_pp_bit_4_state = 0xff;
+ }
+
+ /*
+ * Send the command itself in one block, then any extra input data in a second
+ * block. Not sure if that's necessary.
+ */
+ if (src_size < 6)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_cmd: source size is only %lu (<6)\n", (u_long) src_size);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * Retry the command several times, as occasionally it doesn't
+ * work first time.
+ */
+ do
+ {
+ stat = mustek_scsi_pp_send_command (fd, cmd);
+ num_tries++;
+ }
+ while ((stat != SANE_STATUS_GOOD) && (num_tries < max_tries));
+
+ if (stat != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_cmd: sending command failed\n");
+ return stat;
+ }
+
+ if (src_size > 6)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: sending data block of length %lu\n",
+ (u_long) (src_size - 6));
+
+ stat =
+ mustek_scsi_pp_send_data_block (fd, ((const u_char *) src) + 6,
+ src_size - 6);
+ if (stat != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_cmd: sending data block failed\n");
+ return stat;
+ }
+ }
+
+
+ if (dst)
+ {
+ unsigned int length;
+
+ /* check buffer is big enough to receive data */
+ length = (cmd[3] << 8) | cmd[4];
+
+ DBG (5, "mustek_scsi_pp_cmd: reading %d bytes\n", length);
+
+ if (length > *dst_size)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_cmd: buffer (size %lu) not big enough for data (size %d)\n",
+ (u_long) *dst_size, length);
+ return SANE_STATUS_INVAL;
+ }
+
+ stat = mustek_scsi_pp_read_data_block (fd, dst, length);
+ if (stat != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error reading data block\n");
+ }
+ }
+
+ if (cmd[0] == 0x1b)
+ {
+ if (cmd[4] & 0x1)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: doing start-specific stuff\n");
+
+ scan_options = cmd[4];
+
+ /* 'Start' command - wait for valid status */
+ mustek_scsi_pp_timeout = 70000;
+ stat = mustek_scsi_pp_wait_for_valid_status (fd);
+
+ if (stat != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_cmd: error waiting for valid status after start\n");
+ }
+ }
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error disabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (stat == SANE_STATUS_GOOD)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: returning success\n");
+ }
+
+ return stat;
+}
+
+static SANE_Status
+mustek_scsi_pp_rdata (int fd, int planes, SANE_Byte * buf, int lines, int bpl)
+{
+ int i, j;
+
+ DBG (5,
+ "mustek_scsi_pp_rdata: reading %d lines at %d bpl, %d planes from %d\n",
+ lines, bpl, planes, fd);
+
+ if ((planes != 1) && (planes != 3))
+ {
+ DBG (2, "mustek_scsi_pp_rdata: invalid number of planes (%d)\n",
+ planes);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_rdata: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ for (i = 0; i < lines; i++)
+ {
+ if (planes == 3)
+ {
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for red, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readbegin for red, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ for (j = 0; j < (bpl / 3); j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j]) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading red byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readend for red, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for green, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readbegin for green, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ for (j = 0; j < (bpl / 3); j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j + (bpl / 3)]) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading green byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readend for green, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for blue, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readbegin for blue, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ for (j = 0; j < (bpl / 3); j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j + (2 * (bpl / 3))]) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading blue byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readend for blue, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ {
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_rdata: error in readbegin, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ for (j = 0; j < bpl; j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j]) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_rdata: error in readend, line %d\n", i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ buf += bpl;
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_rdata: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "mustek_scsi_pp_rdata: returning success\n");
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/mustek_scsi_pp.h b/backend/mustek_scsi_pp.h
new file mode 100644
index 0000000..5d1c900
--- /dev/null
+++ b/backend/mustek_scsi_pp.h
@@ -0,0 +1,123 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 James Perry
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the SCSI-over-parallel port protocol used in,
+ for example, the Paragon 600 II EP
+*/
+
+#ifndef mustek_scsi_pp_h
+#define mustek_scsi_pp_h
+
+static int mustek_scsi_pp_get_time (void);
+
+/**
+ * Open the connection to a Mustek SCSI-over-pp device.
+ *
+ * @param dev Port address as text.
+ * @param fd Information about port address and I/O method. fd is not a file
+ * descriptor. The name and type are used for compatibility reasons.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_INVAL - if the port address can't be interpreted
+ * - SANE_STATUS_IO_ERROR - if the device file for a port couldn't be accessed
+ */
+static SANE_Status mustek_scsi_pp_open (const char *dev, int *fd);
+
+/**
+ * Close the connection to a Mustek SCSI-over-PP device.
+ *
+ * @param fd Information about port address and I/O method.
+ *
+ */
+static void mustek_scsi_pp_close (int fd);
+
+/**
+ * Exit Mustek SCSI-over-PP.
+ */
+static void mustek_scsi_pp_exit (void);
+
+/**
+ * Find out if the device is ready to accept new commands.
+ *
+ * @param fd Information about port address and I/O method.
+ *
+ * @return
+ * - SANE_STATUS_GOOD - if the device is ready
+ * - SANE_STATUS_DEVICE_BUSY if the device is still busy (try again later)
+ */
+static SANE_Status mustek_scsi_pp_test_ready (int fd);
+
+/**
+ * Send a command to the Mustek SCSI-over-pp device.
+ *
+ * @param fd Information about port address and I/O method.
+ * @param src Data to be sent to the device.
+ * @param src_size Size of data to be sent to the device.
+ * @param dst Data to be received from the device.
+ * @param dst_size Size of data to be received from the device
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_IO_ERROR - if an error occured during the dialog with the
+ * device
+ */
+static SANE_Status mustek_scsi_pp_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size);
+
+/**
+ * Read scanned image data.
+ *
+ * @param fd Information about port address and I/O method.
+ * @param planes Bytes per pixel (3 for color, 1 for all other modes)
+ * @param buf Buffer for image data.
+ * @param lines Number of lines
+ * @param bpl Bytes per line
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_IO_ERROR - if an error occured during the dialog with the
+ * device
+ */
+static SANE_Status mustek_scsi_pp_rdata (int fd, int planes,
+ SANE_Byte * buf, int lines, int bpl);
+
+
+#endif /* mustek_scsi_pp_h */
diff --git a/backend/mustek_usb.c b/backend/mustek_usb.c
new file mode 100644
index 0000000..66b1813
--- /dev/null
+++ b/backend/mustek_usb.c
@@ -0,0 +1,1610 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001 - 2004 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#define BUILD 18
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME mustek_usb
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+
+#include "mustek_usb.h"
+#include "mustek_usb_high.c"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+static SANE_Int num_devices;
+static Mustek_Usb_Device *first_dev;
+static Mustek_Usb_Scanner *first_handle;
+static const SANE_Device **devlist = 0;
+
+/* Maximum amount of data read in one turn from USB. */
+static SANE_Word max_block_size = (8 * 1024);
+
+/* Array of newly attached devices */
+static Mustek_Usb_Device **new_dev;
+
+/* Length of new_dev array */
+static SANE_Int new_dev_len;
+
+/* Number of entries alloced for new_dev */
+static SANE_Int new_dev_alloced;
+
+static SANE_String_Const mode_list[6];
+
+static const SANE_Range char_range = {
+ -127, 127, 1
+};
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static SANE_Status
+calc_parameters (Mustek_Usb_Scanner * s)
+{
+ SANE_String val;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int max_x, max_y;
+
+ DBG (5, "calc_parameters: start\n");
+ val = s->val[OPT_MODE].s;
+
+ s->params.last_frame = SANE_TRUE;
+
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->bpp = 1;
+ s->channels = 1;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 8;
+ s->bpp = 8;
+ s->channels = 1;
+ }
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR))
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 8;
+ s->bpp = 24;
+ s->channels = 3;
+ }
+ else
+ {
+ DBG (1, "calc_parameters: invalid mode %s\n", (SANE_Char *) val);
+ status = SANE_STATUS_INVAL;
+ }
+
+ s->tl_x = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH;
+ s->tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH;
+ s->width = SANE_UNFIX (s->val[OPT_BR_X].w) / MM_PER_INCH - s->tl_x;
+ s->height = SANE_UNFIX (s->val[OPT_BR_Y].w) / MM_PER_INCH - s->tl_y;
+
+ if (s->width < 0)
+ {
+ DBG (1, "calc_parameters: warning: tl_x > br_x\n");
+ }
+ if (s->height < 0)
+ {
+ DBG (1, "calc_parameters: warning: tl_y > br_y\n");
+ }
+ max_x = s->hw->max_width * SANE_UNFIX (s->val[OPT_RESOLUTION].w) / 300;
+ max_y = s->hw->max_height * SANE_UNFIX (s->val[OPT_RESOLUTION].w) / 300;
+
+ s->tl_x_dots = s->tl_x * SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ s->width_dots = s->width * SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ s->tl_y_dots = s->tl_y * SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ s->height_dots = s->height * SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+
+ if (s->width_dots > max_x)
+ s->width_dots = max_x;
+ if (s->height_dots > max_y)
+ s->height_dots = max_y;
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ s->width_dots = (s->width_dots / 8) * 8;
+ if (s->width_dots == 0)
+ s->width_dots = 8;
+ }
+ if (s->tl_x_dots < 0)
+ s->tl_x_dots = 0;
+ if (s->tl_y_dots < 0)
+ s->tl_y_dots = 0;
+ if (s->tl_x_dots + s->width_dots > max_x)
+ s->tl_x_dots = max_x - s->width_dots;
+ if (s->tl_y_dots + s->height_dots > max_y)
+ s->tl_y_dots = max_y - s->height_dots;
+
+ s->val[OPT_TL_X].w = SANE_FIX (s->tl_x * MM_PER_INCH);
+ s->val[OPT_TL_Y].w = SANE_FIX (s->tl_y * MM_PER_INCH);
+ s->val[OPT_BR_X].w = SANE_FIX ((s->tl_x + s->width) * MM_PER_INCH);
+ s->val[OPT_BR_Y].w = SANE_FIX ((s->tl_y + s->height) * MM_PER_INCH);
+
+ s->params.pixels_per_line = s->width_dots;
+ if (s->params.pixels_per_line < 0)
+ s->params.pixels_per_line = 0;
+ s->params.lines = s->height_dots;
+ if (s->params.lines < 0)
+ s->params.lines = 0;
+ s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8
+ * s->channels;
+
+ DBG (4, "calc_parameters: format=%d\n", s->params.format);
+ DBG (4, "calc_parameters: last frame=%d\n", s->params.last_frame);
+ DBG (4, "calc_parameters: lines=%d\n", s->params.lines);
+ DBG (4, "calc_parameters: pixels per line=%d\n", s->params.pixels_per_line);
+ DBG (4, "calc_parameters: bytes per line=%d\n", s->params.bytes_per_line);
+ DBG (4, "calc_parameters: Pixels %dx%dx%d\n",
+ s->params.pixels_per_line, s->params.lines, 1 << s->params.depth);
+
+ DBG (5, "calc_parameters: exit\n");
+ return status;
+}
+
+
+static SANE_Status
+init_options (Mustek_Usb_Scanner * s)
+{
+ SANE_Int option;
+ SANE_Status status;
+
+ DBG (5, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->opt[option].size = sizeof (SANE_Word);
+ s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ mode_list[0] = SANE_VALUE_SCAN_MODE_COLOR;
+ mode_list[1] = SANE_VALUE_SCAN_MODE_GRAY;
+ mode_list[2] = SANE_VALUE_SCAN_MODE_LINEART;
+ mode_list[3] = NULL;
+
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[1]);
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
+ if (s->hw->chip->scanner_type == MT_600CU)
+ s->hw->dpi_range.max = SANE_FIX (600);
+ else
+ s->hw->dpi_range.max = SANE_FIX (1200);
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_THRESHOLD].w = 128;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* gray gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gray_gamma_table[0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->red_gamma_table[0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->green_gamma_table[0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->blue_gamma_table[0];
+
+ RIE (calc_parameters (s));
+
+ DBG (5, "init_options: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+attach (SANE_String_Const devname, Mustek_Usb_Device ** devp,
+ SANE_Bool may_wait)
+{
+ Mustek_Usb_Device *dev;
+ SANE_Status status;
+ Mustek_Type scanner_type;
+ SANE_Int fd;
+
+ DBG (5, "attach: start: devp %s NULL, may_wait = %d\n", devp ? "!=" : "==",
+ may_wait);
+ if (!devname)
+ {
+ DBG (1, "attach: devname == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ DBG (4, "attach: device `%s' was already in device list\n", devname);
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (4, "attach: trying to open device `%s'\n", devname);
+ status = sanei_usb_open (devname, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "attach: couldn't open device `%s': %s\n", devname,
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (4, "attach: device `%s' successfully opened\n", devname);
+
+ /* try to identify model */
+ DBG (4, "attach: trying to identify device `%s'\n", devname);
+ status = usb_low_identify_scanner (fd, &scanner_type);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: device `%s' doesn't look like a supported scanner\n",
+ devname);
+ sanei_usb_close (fd);
+ return status;
+ }
+ sanei_usb_close (fd);
+ if (scanner_type == MT_UNKNOWN)
+ {
+ DBG (3, "attach: warning: couldn't identify device `%s', must set "
+ "type manually\n", devname);
+ }
+
+ dev = malloc (sizeof (Mustek_Usb_Device));
+ if (!dev)
+ {
+ DBG (1, "attach: couldn't malloc Mustek_Usb_Device\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (dev, 0, sizeof (*dev));
+ dev->name = strdup (devname);
+ dev->sane.name = (SANE_String_Const) dev->name;
+ dev->sane.vendor = "Mustek";
+ switch (scanner_type)
+ {
+ case MT_1200CU:
+ dev->sane.model = "1200 CU";
+ break;
+ case MT_1200CU_PLUS:
+ dev->sane.model = "1200 CU Plus";
+ break;
+ case MT_1200USB:
+ dev->sane.model = "1200 USB (unsupported)";
+ break;
+ case MT_1200UB:
+ dev->sane.model = "1200 UB";
+ break;
+ case MT_600CU:
+ dev->sane.model = "600 CU";
+ break;
+ case MT_600USB:
+ dev->sane.model = "600 USB (unsupported)";
+ break;
+ default:
+ dev->sane.model = "(unidentified)";
+ break;
+ }
+ dev->sane.type = "flatbed scanner";
+
+ dev->x_range.min = 0;
+ dev->x_range.max = SANE_FIX (8.4 * MM_PER_INCH);
+ dev->x_range.quant = 0;
+
+ dev->y_range.min = 0;
+ dev->y_range.max = SANE_FIX (11.7 * MM_PER_INCH);
+ dev->y_range.quant = 0;
+
+ dev->max_height = 11.7 * 300;
+ dev->max_width = 8.4 * 300;
+ dev->dpi_range.min = SANE_FIX (50);
+ dev->dpi_range.max = SANE_FIX (600);
+ dev->dpi_range.quant = SANE_FIX (1);
+
+ status = usb_high_scan_init (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: usb_high_scan_init returned status: %s\n",
+ sane_strstatus (status));
+ free (dev);
+ return status;
+ }
+ dev->chip->scanner_type = scanner_type;
+ dev->chip->max_block_size = max_block_size;
+
+ DBG (2, "attach: found %s %s %s at %s\n", dev->sane.vendor, dev->sane.type,
+ dev->sane.model, dev->sane.name);
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (5, "attach: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ Mustek_Usb_Device *dev;
+ SANE_Status status;
+
+ RIE (attach (devname, &dev, SANE_FALSE));
+
+ if (dev)
+ {
+ /* Keep track of newly attached devices so we can set options as
+ necessary. */
+ if (new_dev_len >= new_dev_alloced)
+ {
+ new_dev_alloced += 4;
+ if (new_dev)
+ new_dev =
+ realloc (new_dev, new_dev_alloced * sizeof (new_dev[0]));
+ else
+ new_dev = malloc (new_dev_alloced * sizeof (new_dev[0]));
+ if (!new_dev)
+ {
+ DBG (1, "attach_one_device: out of memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ new_dev[new_dev_len++] = dev;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+fit_lines (Mustek_Usb_Scanner * s, SANE_Byte * src, SANE_Byte * dst,
+ SANE_Word src_lines, SANE_Word * dst_lines)
+{
+ SANE_Int threshold;
+ SANE_Word src_width, dst_width;
+ SANE_Word dst_pixel, src_pixel;
+ SANE_Word dst_line, src_line;
+ SANE_Word pixel_switch;
+ SANE_Word src_address, dst_address;
+ src_width = s->hw->width;
+ dst_width = s->width_dots;
+
+ threshold = s->val[OPT_THRESHOLD].w;
+
+ DBG (5, "fit_lines: dst_width=%d, src_width=%d, src_lines=%d, "
+ "offset=%d\n", dst_width, src_width, src_lines, s->hw->line_offset);
+
+ dst_line = 0;
+ src_line = s->hw->line_offset;
+
+ while (src_line < src_lines)
+ {
+ DBG (5, "fit_lines: getting line: dst_line=%d, src_line=%d, "
+ "line_switch=%d\n", dst_line, src_line, s->hw->line_switch);
+
+ src_pixel = 0;
+ pixel_switch = src_width;
+ for (dst_pixel = 0; dst_pixel < dst_width; dst_pixel++)
+ {
+ while (pixel_switch > dst_width)
+ {
+ src_pixel++;
+ pixel_switch -= dst_width;
+ }
+ pixel_switch += src_width;
+
+ src_address = src_pixel * s->hw->bpp / 8
+ + src_width * src_line * s->hw->bpp / 8;
+ dst_address = dst_pixel * s->bpp / 8
+ + dst_width * dst_line * s->bpp / 8;
+
+ if (s->bpp == 8)
+ {
+ dst[dst_address] = s->gray_table[src[src_address]];
+ }
+ else if (s->bpp == 24)
+ {
+ dst[dst_address]
+ = s->red_table[s->gray_table[src[src_address]]];
+ dst[dst_address + 1]
+ = s->green_table[s->gray_table[src[src_address + 1]]];
+ dst[dst_address + 2]
+ = s->blue_table[s->gray_table[src[src_address + 2]]];
+ }
+ else /* lineart */
+ {
+ if ((dst_pixel % 8) == 0)
+ dst[dst_address] = 0;
+ dst[dst_address] |=
+ (((src[src_address] > threshold) ? 0 : 1)
+ << (7 - (dst_pixel % 8)));
+ }
+ }
+
+ dst_line++;
+ while (s->hw->line_switch >= s->height_dots)
+ {
+ src_line++;
+ s->hw->line_switch -= s->height_dots;
+ }
+ s->hw->line_switch += s->hw->height;
+ }
+
+ *dst_lines = dst_line;
+ s->hw->line_offset = (src_line - src_lines);
+
+ DBG (4, "fit_lines: exit, src_line=%d, *dst_lines=%d, offset=%d\n",
+ src_line, *dst_lines, s->hw->line_offset);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+check_gamma_table (SANE_Word * table)
+{
+ SANE_Word entry, value;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ for (entry = 0; entry < 256; entry++)
+ {
+ value = table[entry];
+ if (value > 255)
+ {
+ DBG (1, "check_gamma_table: warning: entry %d > 255 (%d) - fixed\n",
+ entry, value);
+ table[entry] = 255;
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ return status;
+}
+
+/* -------------------------- SANE API functions ------------------------- */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Char line[PATH_MAX];
+ SANE_Char *word, *end;
+ SANE_String_Const cp;
+ SANE_Int linenumber;
+ FILE *fp;
+
+ DBG_INIT ();
+ DBG (2, "SANE Mustek USB backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+
+ num_devices = 0;
+ first_dev = 0;
+ first_handle = 0;
+ devlist = 0;
+ new_dev = 0;
+ new_dev_len = 0;
+ new_dev_alloced = 0;
+
+ sanei_usb_init ();
+
+ fp = sanei_config_open (MUSTEK_USB_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/usb/scanner instead of insisting on config file */
+ DBG (3, "sane_init: couldn't open config file `%s': %s. Using "
+ "/dev/usb/scanner directly\n", MUSTEK_USB_CONFIG_FILE,
+ strerror (errno));
+ attach ("/dev/usb/scanner", 0, SANE_FALSE);
+ return SANE_STATUS_GOOD;
+ }
+ linenumber = 0;
+ DBG (4, "sane_init: reading config file `%s'\n", MUSTEK_USB_CONFIG_FILE);
+
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ word = 0;
+ linenumber++;
+
+ cp = sanei_config_get_string (line, &word);
+ if (!word || cp == line)
+ {
+ DBG (5, "sane_init: config file line %d: ignoring empty line\n",
+ linenumber);
+ if (word)
+ free (word);
+ continue;
+ }
+ if (word[0] == '#')
+ {
+ DBG (5, "sane_init: config file line %d: ignoring comment line\n",
+ linenumber);
+ free (word);
+ continue;
+ }
+
+ if (strcmp (word, "option") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+
+ if (!word)
+ {
+ DBG (1, "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ if (strcmp (word, "max_block_size") == 0)
+ {
+ free (word);
+ word = 0;
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ {
+ DBG (1, "sane_init: config file line %d: missing quotation mark?\n",
+ linenumber);
+ continue;
+ }
+
+ errno = 0;
+ max_block_size = strtol (word, &end, 0);
+ if (end == word)
+ {
+ DBG (3, "sane-init: config file line %d: max_block_size "
+ "must have a parameter; using 8192 bytes\n",
+ linenumber);
+ max_block_size = 8192;
+ }
+ if (errno)
+ {
+ DBG (3,
+ "sane-init: config file line %d: max_block_size `%s' "
+ "is invalid (%s); using 8192 bytes\n", linenumber,
+ word, strerror (errno));
+ max_block_size = 8192;
+ }
+ else
+ {
+ DBG (3,
+ "sane_init: config file line %d: max_block_size set "
+ "to %d bytes\n", linenumber, max_block_size);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "1200ub") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ /* this is a 1200 UB */
+ new_dev[new_dev_len - 1]->chip->scanner_type = MT_1200UB;
+ new_dev[new_dev_len - 1]->sane.model = "1200 UB";
+ DBG (3, "sane_init: config file line %d: `%s' is a Mustek "
+ "1200 UB\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "1200ub ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "1200cu") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ /* this is a 1200 CU */
+ new_dev[new_dev_len - 1]->chip->scanner_type = MT_1200CU;
+ new_dev[new_dev_len - 1]->sane.model = "1200 CU";
+ DBG (3, "sane_init: config file line %d: `%s' is a Mustek "
+ "1200 CU\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "1200cu ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "1200cu_plus") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ /* this is a 1200 CU Plus */
+ new_dev[new_dev_len - 1]->chip->scanner_type
+ = MT_1200CU_PLUS;
+ new_dev[new_dev_len - 1]->sane.model = "1200 CU Plus";
+ DBG (3, "sane_init: config file line %d: `%s' is a Mustek "
+ "1200 CU Plus\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "1200cu_plus ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else if (strcmp (word, "600cu") == 0)
+ {
+ if (new_dev_len > 0)
+ {
+ /* this is a 600 CU */
+ new_dev[new_dev_len - 1]->chip->scanner_type = MT_600CU;
+ new_dev[new_dev_len - 1]->sane.model = "600 CU";
+ DBG (3, "sane_init: config file line %d: `%s' is a Mustek "
+ "600 CU\n", linenumber,
+ new_dev[new_dev_len - 1]->sane.name);
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "600cu ignored, was set before any device "
+ "name\n", linenumber);
+ }
+ if (word)
+ free (word);
+ word = 0;
+ }
+ else
+ {
+ DBG (3, "sane_init: config file line %d: option "
+ "%s is unknown\n", linenumber, word);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+ else
+ {
+ new_dev_len = 0;
+ DBG (4, "sane_init: config file line %d: trying to attach `%s'\n",
+ linenumber, line);
+ sanei_usb_attach_matching_devices (line, attach_one_device);
+ if (word)
+ free (word);
+ word = 0;
+ }
+ }
+
+ if (new_dev_alloced > 0)
+ {
+ new_dev_len = new_dev_alloced = 0;
+ free (new_dev);
+ }
+
+ fclose (fp);
+ DBG (5, "sane_init: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Mustek_Usb_Device *dev, *next;
+ SANE_Status status;
+
+ DBG (5, "sane_exit: start\n");
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ if (dev->is_prepared)
+ {
+ status = usb_high_scan_clearup (dev);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_close: usb_high_scan_clearup returned %s\n",
+ sane_strstatus (status));
+ }
+ status = usb_high_scan_exit (dev);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_close: usb_high_scan_exit returned %s\n",
+ sane_strstatus (status));
+ if (dev->chip)
+ {
+ status = usb_high_scan_exit (dev);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3,
+ "sane_exit: while closing %s, usb_high_scan_exit returned: "
+ "%s\n", dev->name, sane_strstatus (status));
+ }
+ free ((void *) dev->name);
+ free (dev);
+ }
+ first_dev = 0;
+ if (devlist)
+ free (devlist);
+ devlist = 0;
+
+ DBG (5, "sane_exit: exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Mustek_Usb_Device *dev;
+ SANE_Int dev_num;
+
+ DBG (5, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ dev_num = 0;
+ for (dev = first_dev; dev_num < num_devices; dev = dev->next)
+ devlist[dev_num++] = &dev->sane;
+ devlist[dev_num++] = 0;
+
+ *device_list = devlist;
+
+ DBG (5, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Mustek_Usb_Device *dev;
+ SANE_Status status;
+ Mustek_Usb_Scanner *s;
+ SANE_Int value;
+
+ DBG (5, "sane_open: start (devicename = `%s')\n", devicename);
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ DBG (5,
+ "sane_open: couldn't find `%s' in devlist, trying attach)\n",
+ devicename);
+ RIE (attach (devicename, &dev, SANE_TRUE));
+ }
+ else
+ DBG (5, "sane_open: found `%s' in devlist\n", dev->name);
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = first_dev;
+ if (dev)
+ DBG (5, "sane_open: empty devicename, trying `%s'\n", dev->name);
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (dev->chip->scanner_type == MT_UNKNOWN)
+ {
+ DBG (0, "sane_open: the type of your scanner is unknown, edit "
+ "mustek_usb.conf before using the scanner\n");
+ return SANE_STATUS_INVAL;
+ }
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->hw = dev;
+
+ RIE (init_options (s));
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+ strcpy (s->hw->device_name, dev->name);
+
+ RIE (usb_high_scan_turn_power (s->hw, SANE_TRUE));
+ RIE (usb_high_scan_back_home (s->hw));
+
+ s->hw->scan_buffer = (SANE_Byte *) malloc (SCAN_BUFFER_SIZE * 2);
+ if (!s->hw->scan_buffer)
+ {
+ DBG (5, "sane_open: couldn't malloc s->hw->scan_buffer (%d bytes)\n",
+ SCAN_BUFFER_SIZE * 2);
+ return SANE_STATUS_NO_MEM;
+ }
+ s->hw->scan_buffer_len = 0;
+ s->hw->scan_buffer_start = s->hw->scan_buffer;
+
+ s->hw->temp_buffer = (SANE_Byte *) malloc (SCAN_BUFFER_SIZE);
+ if (!s->hw->temp_buffer)
+ {
+ DBG (5, "sane_open: couldn't malloc s->hw->temp_buffer (%d bytes)\n",
+ SCAN_BUFFER_SIZE);
+ return SANE_STATUS_NO_MEM;
+ }
+ s->hw->temp_buffer_len = 0;
+ s->hw->temp_buffer_start = s->hw->temp_buffer;
+
+ for (value = 0; value < 256; value++)
+ {
+ s->linear_gamma_table[value] = value;
+ s->red_gamma_table[value] = value;
+ s->green_gamma_table[value] = value;
+ s->blue_gamma_table[value] = value;
+ s->gray_gamma_table[value] = value;
+ }
+
+ s->red_table = s->linear_gamma_table;
+ s->green_table = s->linear_gamma_table;
+ s->blue_table = s->linear_gamma_table;
+ s->gray_table = s->linear_gamma_table;
+
+ DBG (5, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Mustek_Usb_Scanner *prev, *s;
+ SANE_Status status;
+
+ DBG (5, "sane_close: start\n");
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (5, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ if (s->hw->is_open)
+ {
+ status = usb_high_scan_turn_power (s->hw, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_close: usb_high_scan_turn_power returned %s\n",
+ sane_strstatus (status));
+ }
+#if 0
+ if (s->hw->is_prepared)
+ {
+ status = usb_high_scan_clearup (s->hw);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_close: usb_high_scan_clearup returned %s\n",
+ sane_strstatus (status));
+ }
+ status = usb_high_scan_exit (s->hw);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_close: usb_high_scan_exit returned %s\n",
+ sane_strstatus (status));
+#endif
+ if (s->hw->scan_buffer)
+ {
+ free (s->hw->scan_buffer);
+ s->hw->scan_buffer = 0;
+ }
+ if (s->hw->temp_buffer)
+ {
+ free (s->hw->temp_buffer);
+ s->hw->temp_buffer = 0;
+ }
+
+ free (handle);
+
+ DBG (5, "sane_close: exit\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Mustek_Usb_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ DBG (5, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option);
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Mustek_Usb_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (5, "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" :
+ (action == SANE_ACTION_SET_VALUE) ? "set" :
+ (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->opt[option].name, option);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ {
+ DBG (1, "sane_control_option: don't call this function while "
+ "scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (1, "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (2, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_THRESHOLD:
+ case OPT_CUSTOM_GAMMA:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ break;
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ break;
+ default:
+ DBG (2, "sane_control_option: can't get unknown option %d\n",
+ option);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (2, "sane_control_option: option %d is not settable\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_THRESHOLD:
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ /* Boolean */
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Bool *) val;
+ break;
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ check_gamma_table (s->val[option].wa);
+ break;
+ case OPT_CUSTOM_GAMMA:
+ s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ {
+ s->red_table = s->red_gamma_table;
+ s->green_table = s->green_gamma_table;
+ s->blue_table = s->blue_gamma_table;
+ s->gray_table = s->gray_gamma_table;
+ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ s->red_table = s->linear_gamma_table;
+ s->green_table = s->linear_gamma_table;
+ s->blue_table = s->linear_gamma_table;
+ s->gray_table = s->linear_gamma_table;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ break;
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+
+ RIE (calc_parameters (s));
+
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+ default:
+ DBG (2, "sane_control_option: can't set unknown option %d\n",
+ option);
+ }
+ }
+ else
+ {
+ DBG (2, "sane_control_option: unknown action %d for option %d\n",
+ action, option);
+ return SANE_STATUS_INVAL;
+ }
+ if (info)
+ *info = myinfo;
+
+ DBG (5, "sane_control_option: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Mustek_Usb_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (5, "sane_get_parameters: start\n");
+
+ RIE (calc_parameters (s));
+ if (params)
+ *params = s->params;
+
+ DBG (5, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Mustek_Usb_Scanner *s = handle;
+ SANE_Status status;
+ SANE_String val;
+ Colormode color_mode;
+ SANE_Word dpi, x, y, width, height;
+
+ DBG (5, "sane_start: start\n");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+
+ s->total_bytes = 0;
+ s->total_lines = 0;
+ RIE (calc_parameters (s));
+
+ if (s->width_dots <= 0)
+ {
+ DBG (0, "sane_start: top left x > bottom right x --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->height_dots <= 0)
+ {
+ DBG (0, "sane_start: top left y > bottom right y --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ val = s->val[OPT_MODE].s;
+ if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART))
+ color_mode = GRAY8;
+ else if (!strcmp (val, SANE_VALUE_SCAN_MODE_GRAY))
+ color_mode = GRAY8;
+ else /* Color */
+ color_mode = RGB24;
+
+ dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ x = s->tl_x_dots;
+ y = s->tl_y_dots;
+ width = s->width_dots;
+ height = s->height_dots;
+
+ if (!s->hw->is_prepared)
+ {
+ RIE (usb_high_scan_prepare (s->hw));
+ RIE (usb_high_scan_reset (s->hw));
+ }
+ RIE (usb_high_scan_set_threshold (s->hw, 128));
+ RIE (usb_high_scan_embed_gamma (s->hw, NULL));
+ RIE (usb_high_scan_suggest_parameters (s->hw, dpi, x, y, width, height,
+ color_mode));
+ RIE (usb_high_scan_setup_scan (s->hw, s->hw->scan_mode, s->hw->x_dpi,
+ s->hw->y_dpi, 0, s->hw->x, s->hw->y,
+ s->hw->width));
+
+ DBG (3, "sane_start: wanted: dpi=%d, x=%d, y=%d, width=%d, height=%d, "
+ "scan_mode=%d\n", dpi, x, y, width, height, color_mode);
+ DBG (3, "sane_start: got: x_dpi=%d, y_dpi=%d, x=%d, y=%d, width=%d, "
+ "height=%d, scan_mode=%d\n", s->hw->x_dpi, s->hw->y_dpi, s->hw->x,
+ s->hw->y, s->hw->width, s->hw->height, s->hw->scan_mode);
+
+ s->scanning = SANE_TRUE;
+ s->read_rows = s->hw->height;
+ s->hw->line_switch = s->hw->height;
+ s->hw->line_offset = 0;
+ s->hw->scan_buffer_len = 0;
+
+ DBG (5, "sane_start: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Mustek_Usb_Scanner *s = handle;
+ SANE_Word lines_to_read, lines_read;
+ SANE_Status status;
+
+ DBG (5, "sane_read: start\n");
+
+ if (!s)
+ {
+ DBG (1, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!buf)
+ {
+ DBG (1, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (1, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *len = 0;
+
+ if (!s->scanning)
+ {
+ DBG (3, "sane_read: scan was cancelled, is over or has not been "
+ "initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (s->hw->scan_buffer_len == 0)
+ {
+ if (s->read_rows > 0)
+ {
+ lines_to_read = SCAN_BUFFER_SIZE / (s->hw->width * s->hw->bpp / 8);
+ if (lines_to_read > s->read_rows)
+ lines_to_read = s->read_rows;
+ s->hw->temp_buffer_start = s->hw->temp_buffer;
+ s->hw->temp_buffer_len = (s->hw->width * s->hw->bpp / 8)
+ * lines_to_read;
+ DBG (4, "sane_read: reading %d source lines\n", lines_to_read);
+ RIE (usb_high_scan_get_rows (s->hw, s->hw->temp_buffer,
+ lines_to_read, SANE_FALSE));
+ RIE (fit_lines (s, s->hw->temp_buffer, s->hw->scan_buffer,
+ lines_to_read, &lines_read));
+ s->read_rows -= lines_to_read;
+ if ((s->total_lines + lines_read) > s->height_dots)
+ lines_read = s->height_dots - s->total_lines;
+ s->total_lines += lines_read;
+ DBG (4, "sane_read: %d destination lines, %d total\n",
+ lines_read, s->total_lines);
+ s->hw->scan_buffer_start = s->hw->scan_buffer;
+ s->hw->scan_buffer_len = (s->width_dots * s->bpp / 8) * lines_read;
+ }
+ else
+ {
+ DBG (4, "sane_read: scan finished -- exit\n");
+ return SANE_STATUS_EOF;
+ }
+ }
+ if (s->hw->scan_buffer_len == 0)
+ {
+ DBG (4, "sane_read: scan finished -- exit\n");
+ return SANE_STATUS_EOF;
+ }
+
+ *len = MIN (max_len, (SANE_Int) s->hw->scan_buffer_len);
+ memcpy (buf, s->hw->scan_buffer_start, *len);
+ DBG (4, "sane_read: exit, read %d bytes from scan_buffer; "
+ "%ld bytes remaining\n", *len,
+ (long int) (s->hw->scan_buffer_len - *len));
+ s->hw->scan_buffer_len -= (*len);
+ s->hw->scan_buffer_start += (*len);
+ s->total_bytes += (*len);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Mustek_Usb_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (5, "sane_cancel: start\n");
+
+ status = usb_high_scan_stop_scan (s->hw);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_cancel: usb_high_scan_stop_scan returned `%s' for `%s'\n",
+ sane_strstatus (status), s->hw->name);
+ usb_high_scan_back_home (s->hw);
+ if (status != SANE_STATUS_GOOD)
+ DBG (3, "sane_cancel: usb_high_scan_back_home returned `%s' for `%s'\n",
+ sane_strstatus (status), s->hw->name);
+
+ if (s->scanning)
+ {
+ s->scanning = SANE_FALSE;
+ if (s->total_bytes != (s->params.bytes_per_line * s->params.lines))
+ DBG (1, "sane_cancel: warning: scanned %d bytes, expected %d "
+ "bytes\n", s->total_bytes,
+ s->params.bytes_per_line * s->params.lines);
+ else
+ DBG (3, "sane_cancel: scan finished, scanned %d bytes\n",
+ s->total_bytes);
+ }
+ else
+ {
+ DBG (4, "sane_cancel: scan has not been initiated yet, "
+ "or it is allready aborted\n");
+ }
+ DBG (5, "sane_cancel: exit\n");
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Mustek_Usb_Scanner *s = handle;
+
+ DBG (5, "sane_set_io_mode: handle = %p, non_blocking = %s\n",
+ handle, non_blocking == SANE_TRUE ? "true" : "false");
+ if (!s->scanning)
+ {
+ DBG (1, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Mustek_Usb_Scanner *s = handle;
+
+ DBG (5, "sane_get_select_fd: handle = %p, fd = %p\n", handle, (void *) fd);
+ if (!s->scanning)
+ {
+ DBG (1, "sane_get_select_fd: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/mustek_usb.conf.in b/backend/mustek_usb.conf.in
new file mode 100644
index 0000000..c9b3ec4
--- /dev/null
+++ b/backend/mustek_usb.conf.in
@@ -0,0 +1,39 @@
+# mustek_usb.conf: Configuration file for Mustek USB scanner
+# Read man sane-mustek_usb for documentation
+
+# If USB errors occur, using this option may help
+#option max_block_size 1024
+
+# Autodetect 1200 UB and Trust Compact Scan USB 19200
+usb 0x055f 0x0006
+
+# Autodetect 1200 USB (not supported)
+# usb 0x055f 0x0003
+
+# Autodetect 1200 CU
+usb 0x055f 0x0001
+
+# Autodetect 1200 CU Plus
+usb 0x055f 0x0008
+
+# Autodetect 600 CU
+usb 0x055f 0x0002
+
+# Autodetect 600 USB (not supported)
+usb 0x055f 0x0873
+
+# If autodetection doesn't work uncomment or add your device file and one
+# suitable option (1200ub is also for Trust Compact Scan USB 19200).
+
+#/dev/usb/scanner0
+#option 1200ub
+#option 1200cu
+#option 1200cu_plus
+#option 600cu
+
+#/dev/usbscanner0
+#option 1200ub
+#option 1200cu
+#option 1200cu_plus
+#option 600cu
+
diff --git a/backend/mustek_usb.h b/backend/mustek_usb.h
new file mode 100644
index 0000000..7e68402
--- /dev/null
+++ b/backend/mustek_usb.h
@@ -0,0 +1,62 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001, 2002 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#ifndef mustek_usb_h
+#define mustek_usb_h
+
+#include <sys/types.h>
+#include "mustek_usb_high.h"
+
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+#define MUSTEK_USB_CONFIG_FILE "mustek_usb.conf"
+#define SCAN_BUFFER_SIZE (64 * 1024)
+
+#endif /* mustek_usb_h */
diff --git a/backend/mustek_usb2.c b/backend/mustek_usb2.c
new file mode 100644
index 0000000..25b8464
--- /dev/null
+++ b/backend/mustek_usb2.c
@@ -0,0 +1,2696 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000-2005 Mustek.
+ Originally maintained by Mustek
+
+ Copyright (C) 2001-2005 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#define BUILD 10
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME mustek_usb2
+
+#include "../include/sane/sanei_backend.h"
+#include "mustek_usb2_high.c"
+
+#include "mustek_usb2.h"
+
+static SANE_Int num_devices;
+static const SANE_Device **devlist = 0;
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+static SANE_Range x_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (8.3 * MM_PER_INCH), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range y_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (11.6 * MM_PER_INCH), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range gamma_range = {
+ SANE_FIX (0.01), /* minimum */
+ SANE_FIX (5.0), /* maximum */
+ SANE_FIX (0.01) /* quantization */
+};
+static SANE_String_Const mode_list[] = {
+ SANE_I18N ("Color48"),
+ SANE_I18N ("Color24"),
+ SANE_I18N ("Gray16"),
+ SANE_I18N ("Gray8"),
+ SANE_VALUE_SCAN_MODE_LINEART,
+ 0
+};
+
+static SANE_String_Const negative_mode_list[] = {
+ SANE_I18N ("Color24"),
+ 0
+};
+
+static SANE_String_Const source_list[] = {
+ SANE_I18N ("Reflective"),
+ SANE_I18N ("Positive"),
+ SANE_I18N ("Negative"),
+ 0
+};
+static Scanner_Model mustek_A2nu2_model = {
+ "mustek-A2nu2", /* Name */
+ "Mustek", /* Device vendor string */
+
+ "BearPaw 2448TA Pro", /* Device model name */
+ "", /* Name of the firmware file */
+
+ {1200, 600, 300, 150, 75, 0}, /* possible resolutions */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (8.3 * MM_PER_INCH), /* Size of scan area in mm (x) */
+ SANE_FIX (11.6 * MM_PER_INCH), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (1.46 * MM_PER_INCH), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (6.45 * MM_PER_INCH), /* Size of scan area in TA mode in mm (y) */
+
+ 0, /* Order of the CCD/CIS colors 0:RO_RGB 1:RO_BGR */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* Setup and tested */
+};
+
+
+/* Forward declarations */
+
+static SANE_Bool GetDeviceStatus (void);
+static SANE_Bool PowerControl (SANE_Bool isLampOn, SANE_Bool isTaLampOn);
+static SANE_Bool CarriageHome (void);
+static SANE_Bool SetParameters (LPSETPARAMETERS pSetParameters);
+static SANE_Bool GetParameters (LPGETPARAMETERS pGetParameters);
+static SANE_Bool StartScan (void);
+static SANE_Bool ReadScannedData (LPIMAGEROWS pImageRows);
+static SANE_Bool StopScan (void);
+static SANE_Bool IsTAConnected (void);
+static void AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine);
+static size_t max_string_size (const SANE_String_Const strings[]);
+static SANE_Status calc_parameters (Mustek_Scanner * s);
+#ifdef SANE_UNUSED
+static SANE_Bool GetGammaInfo (LPGAMMAINFO pGamaInfo);
+static SANE_Bool GetKeyStatus (SANE_Byte * pKey);
+static void QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine);
+static void QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight);
+#endif
+
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+calc_parameters (Mustek_Scanner * s)
+{
+ SANE_String val, val_source;
+ val = s->val[OPT_MODE].s;
+ val_source = s->val[OPT_SOURCE].s;
+
+ s->params.last_frame = SANE_TRUE;
+
+ if (strcmp (val, "Color48") == 0) /* Color48 */
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 16;
+ s->setpara.smScanMode = SM_RGB48;
+ if (s->val[OPT_PREVIEW].w)
+ {
+ DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_RGB24\n");
+ s->params.depth = 8;
+ s->setpara.smScanMode = SM_RGB24;
+ }
+ }
+ else if (strcmp (val, "Color24") == 0) /* Color24 */
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 8;
+ s->setpara.smScanMode = SM_RGB24;
+ }
+ else if (strcmp (val, "Gray16") == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 16;
+ s->setpara.smScanMode = SM_GRAY16;
+ if (s->val[OPT_PREVIEW].w)
+ {
+ s->params.depth = 8;
+ DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_GRAY\n");
+ s->setpara.smScanMode = SM_GRAY;
+ }
+ }
+ else if (strcmp (val, "Gray8") == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 8;
+ s->setpara.smScanMode = SM_GRAY;
+ }
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->setpara.smScanMode = SM_TEXT;
+ }
+
+ /*set Scan Source */
+ DBG (DBG_DET, "calc_parameters :scan Source = %s\n", val_source);
+ if (strcmp (val_source, "Reflective") == 0)
+ {
+ s->setpara.ssScanSource = SS_Reflective;
+ }
+ else if (strcmp (val_source, "Positive") == 0)
+ {
+ s->setpara.ssScanSource = SS_Positive;
+ }
+ else if (strcmp (val_source, "Negative") == 0)
+ {
+ s->setpara.ssScanSource = SS_Negative;
+ }
+
+
+ s->setpara.fmArea.x1 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_X].w) * 300.0) / MM_PER_INCH + 0.5);
+ s->setpara.fmArea.x2 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_X].w) * 300.0) / MM_PER_INCH + 0.5);
+ s->setpara.fmArea.y1 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_Y].w) * 300.0) / MM_PER_INCH + 0.5);
+ s->setpara.fmArea.y2 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_Y].w) * 300.0) / MM_PER_INCH + 0.5);
+
+ if (s->val[OPT_PREVIEW].w)
+ {
+ s->setpara.fmArea.y1 = s->setpara.fmArea.y1 + PER_ADD_START_LINES;
+ s->setpara.fmArea.x1 += PRE_ADD_START_X;
+ } /*just for range bug. */
+
+ s->setpara.pfPixelFlavor = PF_BlackIs0;
+ s->setpara.wLinearThreshold = s->val[OPT_THRESHOLD].w;
+
+ s->setpara.wTargetDPI = s->val[OPT_RESOLUTION].w;
+ if (s->val[OPT_PREVIEW].w)
+ {
+ s->setpara.wTargetDPI = 75;
+ }
+
+ s->setpara.pGammaTable = NULL;
+
+ s->params.pixels_per_line =
+ (SANE_Int) ((s->setpara.fmArea.x2 -
+ s->setpara.fmArea.x1) * s->setpara.wTargetDPI / 300.0 + 0.5);
+
+ switch (s->params.format)
+ {
+ case SANE_FRAME_RGB:
+ if (s->params.depth == 8)
+ s->params.bytes_per_line = s->params.pixels_per_line * 3;
+ if (s->params.depth == 16)
+ s->params.bytes_per_line = s->params.pixels_per_line * 6;
+ break;
+ case SANE_FRAME_GRAY:
+ if (s->params.depth == 1)
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ if (s->params.depth == 8)
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ if (s->params.depth == 16)
+ s->params.bytes_per_line = s->params.pixels_per_line * 2;
+ break;
+ default:
+ DBG (DBG_DET, "sane_star:sane params .format = %d\n", s->params.format);
+ }
+
+ s->params.lines =
+ (SANE_Int) ((s->setpara.fmArea.y2 -
+ s->setpara.fmArea.y1) * s->setpara.wTargetDPI / 300 + 0.5);
+
+ DBG (DBG_FUNC, "calc_parameters: end\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (Mustek_Scanner * s)
+{
+ SANE_Int option, count;
+ SANE_Word *dpi_list; /*Resolution Support */
+
+ DBG (DBG_FUNC, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->opt[option].size = sizeof (SANE_Word);
+ s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ /* Option num */
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+
+ /* Scan Source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].s = strdup ("Reflective");
+
+ if (!IsTAConnected ())
+ {
+ DISABLE (OPT_SOURCE);
+ }
+
+
+ /* resolution */
+
+ for (count = 0; s->model.dpi_values[count] != 0; count++)
+ {
+ }
+ dpi_list = malloc ((count + 1) * sizeof (SANE_Word));
+ if (!dpi_list)
+ return SANE_STATUS_NO_MEM;
+ dpi_list[0] = count;
+
+ for (count = 0; s->model.dpi_values[count] != 0; count++)
+ dpi_list[count + 1] = s->model.dpi_values[count];
+
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+
+
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+ s->val[OPT_RESOLUTION].w = 300;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Debug" group: */
+ s->opt[OPT_DEBUG_GROUP].title = SANE_I18N ("Debugging Options");
+ s->opt[OPT_DEBUG_GROUP].desc = "";
+ s->opt[OPT_DEBUG_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_DEBUG_GROUP].size = 0;
+ s->opt[OPT_DEBUG_GROUP].cap = 0;
+ s->opt[OPT_DEBUG_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* auto warmup */
+ s->opt[OPT_AUTO_WARMUP].name = "auto-warmup";
+ s->opt[OPT_AUTO_WARMUP].title = SANE_I18N ("Automatic warmup");
+ s->opt[OPT_AUTO_WARMUP].desc =
+ SANE_I18N ("Warm-up until the lamp's brightness is constant "
+ "instead of insisting on 40 seconds warm-up time.");
+ s->opt[OPT_AUTO_WARMUP].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTO_WARMUP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_AUTO_WARMUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTO_WARMUP].w = SANE_FALSE;
+ if (s->model.is_cis)
+ DISABLE (OPT_AUTO_WARMUP);
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_THRESHOLD].w = DEF_LINEARTTHRESHOLD;
+
+ /* internal gamma value */
+ s->opt[OPT_GAMMA_VALUE].name = "gamma-value";
+ s->opt[OPT_GAMMA_VALUE].title = SANE_I18N ("Gamma value");
+ s->opt[OPT_GAMMA_VALUE].desc =
+ SANE_I18N ("Sets the gamma value of all channels.");
+ s->opt[OPT_GAMMA_VALUE].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_VALUE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VALUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VALUE].constraint.range = &gamma_range;
+ s->opt[OPT_GAMMA_VALUE].cap |= SANE_CAP_EMULATED;
+ s->val[OPT_GAMMA_VALUE].w = s->model.default_gamma_value;
+
+ DISABLE (OPT_GAMMA_VALUE);
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ x_range.max = s->model.x_size;
+ y_range.max = s->model.y_size;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y].w = y_range.max;
+
+ calc_parameters (s);
+
+ DBG (DBG_FUNC, "init_options: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/***************************** Code from spicall.c *****************************/
+
+static SANE_Byte * g_lpNegImageData = NULL;
+static SANE_Bool g_bIsFirstGetNegData = TRUE;
+static SANE_Bool g_bIsMallocNegData = FALSE;
+static unsigned int g_dwAlreadyGetNegLines = 0;
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Check the device connect status
+Parameters:
+ none
+Return value:
+ if the device is connected
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetDeviceStatus ()
+{
+ DBG (DBG_FUNC, "GetDeviceStatus: start\n");
+ return MustScanner_GetScannerState ();
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Turn the lamp on or off
+Parameters:
+ isLampOn: turn the lamp on or off
+ isTALampOn: turn the TA lamp on or off
+Return value:
+ if operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn)
+{
+ DBG (DBG_FUNC, "PowerControl: start\n");
+ return MustScanner_PowerControl (isLampOn, isTALampOn);
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Turn the carriage home
+Parameters:
+ none
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+CarriageHome ()
+{
+ DBG (DBG_FUNC, "CarriageHome: start\n");
+ return MustScanner_BackHome ();
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Get gamma input/output bit count
+Parameters:
+ pGammaInfo: the gamma information
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetGammaInfo (LPGAMMAINFO pGammaInfo)
+{
+ DBG (DBG_FUNC, "GetGammaInfo: start\n");
+
+ switch (pGammaInfo->smScanMode)
+ {
+ case SM_GRAY:
+ pGammaInfo->wInputGammaBits = 12;
+ pGammaInfo->wOutputGammaBits = 8;
+ break;
+ case SM_RGB24:
+ pGammaInfo->wInputGammaBits = 12;
+ pGammaInfo->wOutputGammaBits = 8;
+ break;
+ case SM_GRAY16:
+ pGammaInfo->wInputGammaBits = 16;
+ pGammaInfo->wOutputGammaBits = 16;
+ break;
+ case SM_RGB48:
+ pGammaInfo->wInputGammaBits = 16;
+ pGammaInfo->wOutputGammaBits = 16;
+ break;
+ default:
+ pGammaInfo->wInputGammaBits = 0;
+ pGammaInfo->wOutputGammaBits = 0;
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC, "GetGammaInfo: exit\n");
+ return TRUE;
+}
+#endif
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ set scan parameters
+Parameters:
+ pSetParameters: the information of scaning
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+SetParameters (LPSETPARAMETERS pSetParameters)
+{
+ unsigned short X1inTargetDpi;
+ unsigned short Y1inTargetDpi;
+ unsigned short X2inTargetDpi;
+ unsigned short Y2inTargetDpi;
+
+ DBG (DBG_FUNC, "SetParameters: start\n");
+
+ /*0. Reset */
+ if (ST_Reflective == g_ScanType)
+ {
+ Reflective_Reset ();
+ }
+ else
+ {
+ Transparent_Reset ();
+ }
+
+ /*1. Scan mode */
+ switch (pSetParameters->smScanMode)
+ {
+ case SM_TEXT:
+ g_tiTarget.cmColorMode = CM_TEXT;
+ break;
+ case SM_GRAY:
+ g_tiTarget.cmColorMode = CM_GRAY8;
+ break;
+ case SM_GRAY16:
+ g_tiTarget.cmColorMode = CM_GRAY16;
+ break;
+ case SM_RGB24:
+ g_tiTarget.cmColorMode = CM_RGB24;
+ break;
+ case SM_RGB48:
+ g_tiTarget.cmColorMode = CM_RGB48;
+ break;
+ default:
+ return FALSE;
+ }
+
+ /*2. Scan source */
+ g_ssScanSource = pSetParameters->ssScanSource;
+ g_tiTarget.bScanSource = pSetParameters->ssScanSource;
+
+
+ if (SS_Reflective == pSetParameters->ssScanSource)
+ {
+ g_ScanType = ST_Reflective;
+ }
+ else if (SS_Positive == pSetParameters->ssScanSource
+ || SS_Negative == pSetParameters->ssScanSource
+ || SS_ADF == pSetParameters->ssScanSource)
+ {
+ g_ScanType = ST_Transparent;
+ }
+ else
+ {
+ DBG (DBG_ERR, "SetParameters: ScanSource error\n");
+ return FALSE;
+ }
+
+ /*3. pixel flavor */
+ if (PF_BlackIs0 == pSetParameters->pfPixelFlavor
+ || PF_WhiteIs0 == pSetParameters->pfPixelFlavor)
+ {
+ g_PixelFlavor = pSetParameters->pfPixelFlavor;
+ }
+ else
+ {
+ DBG (DBG_ERR, "SetParameters: PixelFlavor error\n");
+ return FALSE;
+ }
+
+ /*4. Scan area */
+ if (pSetParameters->fmArea.x1 >= pSetParameters->fmArea.x2)
+ {
+ DBG (DBG_ERR, "SetParameters: x1 > x2, error\n");
+ return FALSE;
+ }
+ if (pSetParameters->fmArea.y1 >= pSetParameters->fmArea.y2)
+ {
+ DBG (DBG_ERR, "SetParameters: y1 >= y2, error\n");
+ return FALSE;
+ }
+ if (pSetParameters->fmArea.x2 > MAX_SCANNING_WIDTH) /* Just for A4 size */
+ {
+ DBG (DBG_ERR, "SetParameters: x2 > MAX_SCANNING_WIDTH, error\n");
+ return FALSE;
+ }
+ if (pSetParameters->fmArea.y2 > MAX_SCANNING_HEIGHT) /* Just for A4 size */
+ {
+ DBG (DBG_ERR, "SetParameters: y2 > MAX_SCANNING_HEIGHT, error\n");
+ return FALSE;
+ }
+
+ X1inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.x1) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+ Y1inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.y1) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+ X2inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.x2) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+ Y2inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.y2) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+
+ g_tiTarget.isOptimalSpeed = TRUE;
+ g_tiTarget.wDpi = pSetParameters->wTargetDPI;
+ g_tiTarget.wX = X1inTargetDpi;
+ g_tiTarget.wY = Y1inTargetDpi;
+ g_tiTarget.wWidth = X2inTargetDpi - X1inTargetDpi;
+ g_tiTarget.wHeight = Y2inTargetDpi - Y1inTargetDpi;
+
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wDpi=%d\n", g_tiTarget.wDpi);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wX=%d\n", g_tiTarget.wX);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wY=%d\n", g_tiTarget.wY);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wWidth=%d\n", g_tiTarget.wWidth);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wHeight=%d\n",
+ g_tiTarget.wHeight);
+
+ /*5.Prepare */
+ if (FALSE == MustScanner_Prepare (g_tiTarget.bScanSource))
+ {
+ DBG (DBG_ERR, "SetParameters: MustScanner_Prepare fail\n");
+ return FALSE;
+ }
+
+ /*6. Linear threshold */
+ if (pSetParameters->wLinearThreshold > 256
+ && pSetParameters->smScanMode == SM_TEXT)
+ {
+ DBG (DBG_ERR, "SetParameters: LinearThreshold error\n");
+ return FALSE;
+ }
+ else
+ {
+ g_wLineartThreshold = pSetParameters->wLinearThreshold;
+ }
+
+ /*7. Gamma table */
+ if (NULL != pSetParameters->pGammaTable)
+ {
+ DBG (DBG_INFO, "SetParameters: IN gamma table not NULL\n");
+ g_pGammaTable = pSetParameters->pGammaTable;
+ g_isSelfGamma = FALSE;
+ }
+ else if (pSetParameters->smScanMode == SM_GRAY
+ || pSetParameters->smScanMode == SM_RGB24)
+ {
+ unsigned short i;
+ SANE_Byte byGammaData;
+ double pow_d;
+ double pow_z = (double) 10 / 16.0;
+
+ g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 4096 * 3);
+
+ DBG (DBG_INFO, "SetParameters: gamma table malloc %ld Bytes\n",
+ (long int) sizeof (unsigned short) * 4096 * 3);
+ DBG (DBG_INFO, "SetParameters: address of g_pGammaTable=%p\n",
+ (void *) g_pGammaTable);
+
+ if (NULL == g_pGammaTable)
+ {
+ DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n");
+ return FALSE;
+ }
+ g_isSelfGamma = TRUE;
+
+ for (i = 0; i < 4096; i++)
+ {
+ pow_d = (double) i / (double) 4096;
+
+ byGammaData = (SANE_Byte) (pow (pow_d, pow_z) * 255);
+
+ *(g_pGammaTable + i) = byGammaData;
+ *(g_pGammaTable + i + 4096) = byGammaData;
+ *(g_pGammaTable + i + 8192) = byGammaData;
+ }
+ }
+ else if (pSetParameters->smScanMode == SM_GRAY16
+ || pSetParameters->smScanMode == SM_RGB48)
+ {
+ unsigned int i, wGammaData;
+ g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 65536 * 3);
+
+ if (g_pGammaTable == NULL)
+ {
+ DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n");
+ return FALSE;
+ }
+ g_isSelfGamma = TRUE;
+
+ for (i = 0; i < 65536; i++)
+ {
+ wGammaData =
+ (unsigned short) (pow ((((float) i) / 65536.0), (((float) 10) / 16.0)) *
+ 65535);
+
+ *(g_pGammaTable + i) = wGammaData;
+ *(g_pGammaTable + i + 65536) = wGammaData;
+ *(g_pGammaTable + i + 65536 * 2) = wGammaData;
+ }
+ }
+ else
+ {
+ DBG (DBG_INFO, "SetParameters: set g_pGammaTable to NULL\n");
+ g_pGammaTable = NULL;
+ }
+
+ DBG (DBG_FUNC, "SetParameters: exit\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ get the optical dpi and scan area
+Parameters:
+ pGetParameters: the information of scan
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetParameters (LPGETPARAMETERS pGetParameters)
+{
+ DBG (DBG_FUNC, "GetParameters: start\n");
+ if (ST_Reflective == g_ScanType)
+ {
+ if (FALSE == Reflective_ScanSuggest (&g_tiTarget, &g_ssSuggest))
+ {
+ DBG (DBG_ERR, "GetParameters: Reflective_ScanSuggest error\n");
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (FALSE == Transparent_ScanSuggest (&g_tiTarget, &g_ssSuggest))
+ {
+ DBG (DBG_ERR, "GetParameters: Transparent_ScanSuggest error\n");
+ return FALSE;
+ }
+ }
+
+ pGetParameters->wSourceXDPI = g_ssSuggest.wXDpi;
+ pGetParameters->wSourceYDPI = g_ssSuggest.wYDpi;
+ pGetParameters->dwLength = (unsigned int) g_ssSuggest.wHeight;
+ pGetParameters->dwLineByteWidth = g_ssSuggest.dwBytesPerRow;
+
+ DBG (DBG_FUNC, "GetParameters: exit\n");
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+
+Routine Description:
+ start scan image
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+StartScan ()
+{
+ DBG (DBG_FUNC, "StartScan: start\n");
+ if (ST_Reflective == g_ScanType)
+ {
+ DBG (DBG_INFO, "StartScan: g_ScanType==ST_Reflective\n");
+
+ return Reflective_SetupScan (g_ssSuggest.cmScanMode,
+ g_ssSuggest.wXDpi,
+ g_ssSuggest.wYDpi,
+ PF_BlackIs0,
+ g_ssSuggest.wX,
+ g_ssSuggest.wY,
+ g_ssSuggest.wWidth, g_ssSuggest.wHeight);
+ }
+ else
+
+ {
+
+ DBG (DBG_INFO, "StartScan: g_ScanType==ST_Transparent\n");
+
+ return Transparent_SetupScan (g_ssSuggest.cmScanMode,
+ g_ssSuggest.wXDpi,
+ g_ssSuggest.wYDpi,
+ PF_BlackIs0,
+ g_ssSuggest.wX,
+ g_ssSuggest.wY,
+ g_ssSuggest.wWidth, g_ssSuggest.wHeight);
+ }
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Read the scanner data
+Parameters:
+
+ pImageRows: the information of the data
+Return value:
+ if the operation is seccuss
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+ReadScannedData (LPIMAGEROWS pImageRows)
+{
+ SANE_Bool isRGBInvert;
+ unsigned short Rows = 0;
+ SANE_Byte *lpBlock = (SANE_Byte *) pImageRows->pBuffer;
+ SANE_Byte *lpReturnData = (SANE_Byte *) pImageRows->pBuffer;
+ int i = 0;
+
+ DBG (DBG_FUNC, "ReadScannedData: start\n");
+
+ if (pImageRows->roRgbOrder == RO_RGB)
+ isRGBInvert = FALSE;
+ else
+ isRGBInvert = TRUE;
+
+ Rows = pImageRows->wWantedLineNum;
+
+ DBG (DBG_INFO, "ReadScannedData: wanted Rows = %d\n", Rows);
+
+ if (ST_Reflective == g_ScanType)
+ {
+ if (FALSE == Reflective_GetRows (lpBlock, &Rows, isRGBInvert))
+ return FALSE;
+ }
+ else if (SS_Positive == g_ssScanSource)
+ {
+ if (FALSE == Transparent_GetRows (lpBlock, &Rows, isRGBInvert))
+ return FALSE;
+ }
+
+ pImageRows->wXferedLineNum = Rows;
+
+ if (g_PixelFlavor == PF_WhiteIs0 || g_ScanMode == CM_TEXT)
+ {
+ int TotalSize = Rows * g_ssSuggest.dwBytesPerRow;
+ for (i = 0; i < TotalSize; i++)
+ {
+ *(lpBlock++) ^= 0xff;
+ }
+ }
+
+ if (SS_Negative == g_ssScanSource)
+ {
+ DBG (DBG_INFO, "ReadScannedData: deal with the Negative\n");
+
+ if (g_bIsFirstGetNegData)
+ {
+ unsigned int TotalImgeSize = g_SWHeight * g_ssSuggest.dwBytesPerRow;
+ g_lpNegImageData = (SANE_Byte *) malloc (TotalImgeSize);
+ if (NULL != g_lpNegImageData)
+ {
+ SANE_Byte * lpTempData = g_lpNegImageData;
+ DBG (DBG_INFO,
+ "ReadScannedData: malloc the negative data is success!\n");
+ g_bIsMallocNegData = TRUE;
+ if (!Transparent_GetRows
+ (g_lpNegImageData, &g_SWHeight, isRGBInvert))
+ {
+ return FALSE;
+ }
+
+ DBG (DBG_INFO, "ReadScannedData: get image data is over!\n");
+
+ for (i = 0; i < (int) TotalImgeSize; i++)
+ {
+ *(g_lpNegImageData++) ^= 0xff;
+ }
+ g_lpNegImageData = lpTempData;
+ AutoLevel (g_lpNegImageData, g_ScanMode, g_SWHeight,
+ g_ssSuggest.dwBytesPerRow);
+ DBG (DBG_INFO, "ReadScannedData: autolevel is ok\n");
+ }
+ g_bIsFirstGetNegData = FALSE;
+ }
+
+ if (g_bIsMallocNegData)
+ {
+ memcpy (pImageRows->pBuffer,
+ g_lpNegImageData +
+ g_ssSuggest.dwBytesPerRow * g_dwAlreadyGetNegLines,
+ g_ssSuggest.dwBytesPerRow * Rows);
+
+ DBG (DBG_INFO, "ReadScannedData: copy the data over!\n");
+
+ g_dwAlreadyGetNegLines += Rows;
+ if (g_dwAlreadyGetNegLines >= g_SWHeight)
+ {
+ DBG (DBG_INFO, "ReadScannedData: free the image data!\n");
+ free (g_lpNegImageData);
+ g_lpNegImageData = NULL;
+ g_bIsFirstGetNegData = TRUE;
+ g_dwAlreadyGetNegLines = 0;
+ g_bIsMallocNegData = FALSE;
+ }
+ }
+ else
+ {
+ int TotalSize = Rows * g_ssSuggest.dwBytesPerRow;
+ DBG (DBG_INFO,
+ "ReadScannedData: malloc the negative data is fail!\n");
+ if (!Transparent_GetRows (lpReturnData, &Rows, isRGBInvert))
+ {
+ return FALSE;
+ }
+
+ for (i = 0; i < TotalSize; i++)
+ {
+ *(lpReturnData++) ^= 0xff;
+ }
+ pImageRows->wXferedLineNum = Rows;
+
+ g_dwAlreadyGetNegLines += Rows;
+ if (g_dwAlreadyGetNegLines >= g_SWHeight)
+ {
+ g_bIsFirstGetNegData = TRUE;
+ g_dwAlreadyGetNegLines = 0;
+ g_bIsMallocNegData = FALSE;
+ }
+ }
+
+ }
+
+ DBG (DBG_FUNC, "ReadScannedData: leave ReadScannedData\n");
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Stop scan
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+StopScan ()
+{
+ SANE_Bool rt;
+ int i;
+
+ DBG (DBG_FUNC, "StopScan: start\n");
+
+ /*stop read data and kill thread */
+ if (ST_Reflective == g_ScanType)
+ {
+ rt = Reflective_StopScan ();
+ }
+ else
+ {
+ rt = Transparent_StopScan ();
+ }
+
+ /*free gamma table */
+ if (g_isSelfGamma && g_pGammaTable != NULL)
+ {
+ for (i = 0; i < 20; i++)
+ {
+ if (!g_isScanning)
+ {
+ free (g_pGammaTable);
+ g_pGammaTable = NULL;
+ break;
+ }
+ else
+ {
+ sleep (1); /*waiting ReadScannedData return. */
+ }
+ }
+ }
+
+ /*free image buffer */
+ if (g_lpReadImageHead != NULL)
+
+ {
+ free (g_lpReadImageHead);
+ g_lpReadImageHead = NULL;
+ }
+
+ DBG (DBG_FUNC, "StopScan: exit\n");
+ return rt;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Check the status of TA
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+IsTAConnected ()
+{
+ SANE_Bool hasTA;
+
+ DBG (DBG_FUNC, "StopScan: start\n");
+
+ if (Asic_Open (&g_chip, g_pDeviceFile) != STATUS_GOOD)
+ {
+ return FALSE;
+ }
+
+ if (Asic_IsTAConnected (&g_chip, &hasTA) != STATUS_GOOD)
+ {
+ Asic_Close (&g_chip);
+ return FALSE;
+ }
+
+ Asic_Close (&g_chip);
+
+ DBG (DBG_FUNC, "StopScan: exit\n");
+ return hasTA;
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Get the status of the HK
+Parameters:
+ pKey: the status of key
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetKeyStatus (SANE_Byte * pKey)
+{
+ SANE_Byte pKeyTemp = 0x00;
+ STATUS status = Asic_CheckFunctionKey (&g_chip, &pKeyTemp);
+ DBG (DBG_FUNC, "GetKeyStatus: start\n");
+
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_ERR, "GetKeyStatus: Asic_Open is fail\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != status)
+ {
+ DBG (DBG_ERR, "GetKeyStatus: Asic_CheckFunctionKey is fail\n");
+ return FALSE;
+ }
+
+ if (0x01 == pKeyTemp)
+ {
+ *pKey = 0x01; /*Scan key pressed */
+ }
+
+ if (0x02 == pKeyTemp)
+ {
+ *pKey = 0x02; /*Copy key pressed */
+ }
+ if (0x04 == pKeyTemp)
+ {
+ *pKey = 0x03; /*Fax key pressed */
+ }
+ if (0x08 == pKeyTemp)
+ {
+ *pKey = 0x04; /*Email key pressed */
+ }
+ if (0x10 == pKeyTemp)
+ {
+ *pKey = 0x05; /*Panel key pressed */
+ }
+
+ if (STATUS_GOOD != Asic_Close (&g_chip))
+ {
+ DBG (DBG_ERR, "GetKeyStatus: Asic_Close is fail\n");
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC, "GetKeyStatus: exit\n");
+ return TRUE;
+}
+#endif
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Deal with the image with auto level
+Parameters:
+ lpSource: the data of image
+ scanMode: the scan mode
+ ScanLines: the rows of image
+ BytesPerLine: the bytes of per line
+Return value:
+ none
+***********************************************************************/
+static void
+AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine)
+{
+ int ii;
+ unsigned int i, j;
+ unsigned int tLines, CountPixels, TotalImgSize;
+ unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B;
+ float fmax_R, fmax_G, fmax_B;
+ unsigned int sum_R = 0, sum_G = 0, sum_B = 0;
+ float mean_R, mean_G, mean_B;
+ unsigned int hisgram_R[256], hisgram_G[256], hisgram_B[256];
+
+ unsigned int iWidth = BytesPerLine / 3;
+ unsigned int iHeight = ScanLines;
+ SANE_Byte *pbmpdata = (SANE_Byte *) lpSource;
+
+ unsigned int tmp = 0;
+ unsigned short imin_threshold[3];
+ unsigned short imax_threshold[3];
+
+ DBG (DBG_FUNC, "AutoLevel: start\n");
+
+ if (scanMode != CM_RGB24ext)
+ {
+ return;
+ }
+
+ i = j = 0;
+ tLines = CountPixels = TotalImgSize = 0;
+
+ TotalImgSize = iWidth * iHeight;
+
+
+
+ for (i = 0; i < 256; i++)
+ {
+
+ hisgram_R[i] = 0;
+ hisgram_G[i] = 0;
+ hisgram_B[i] = 0;
+ }
+
+
+ DBG (DBG_INFO, "AutoLevel: init data is over\n");
+
+ /* Find min , max, mean */
+ max_R = max_G = max_B = 0;
+ min_R = min_G = min_B = 255;
+ tLines = 0;
+ DBG (DBG_INFO, "AutoLevel: iHeight = %d, iWidth = %d\n", iHeight, iWidth);
+
+ for (j = 0; j < iHeight; j++)
+ {
+ tLines = j * iWidth * 3;
+
+
+ for (i = 0; i < iWidth; i++)
+ {
+ R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+
+ max_R = _MAX (R, max_R);
+ max_G = _MAX (G, max_G);
+ max_B = _MAX (B, max_B);
+
+ min_R = _MIN (R, min_R);
+ min_G = _MIN (G, min_G);
+ min_B = _MIN (B, min_B);
+
+ hisgram_R[(SANE_Byte) R]++;
+ hisgram_G[(SANE_Byte) G]++;
+ hisgram_B[(SANE_Byte) B]++;
+
+ sum_R += R;
+ sum_G += G;
+ sum_B += B;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
+ *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;
+
+ CountPixels++;
+ }
+
+ }
+
+ DBG (DBG_INFO, "AutoLevel: Find min , max is over!\n");
+
+ mean_R = (float) (sum_R / TotalImgSize);
+ mean_G = (float) (sum_G / TotalImgSize);
+ mean_B = (float) (sum_B / TotalImgSize);
+
+
+ imin_threshold[0] = 0;
+ imin_threshold[1] = 0;
+ imin_threshold[2] = 0;
+ imax_threshold[0] = 0;
+ imax_threshold[1] = 0;
+ imax_threshold[2] = 0;
+
+ for (ii = 0; ii < 256; ii++)
+ {
+ if (hisgram_R[ii] > 0)
+ if (hisgram_R[ii] >= imin_threshold[0])
+ {
+ min_R = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 255; ii >= 0; ii--)
+ {
+ if (hisgram_R[ii] > 0)
+ if (hisgram_R[ii] >= imax_threshold[0])
+ {
+ max_R = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 0; ii < 256; ii++)
+ {
+ if (hisgram_G[ii] > 0)
+ if (hisgram_G[ii] >= imin_threshold[1])
+ {
+ min_G = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 255; ii >= 0; ii--)
+ {
+ if (hisgram_G[ii] > 0)
+ if (hisgram_G[ii] >= imax_threshold[1])
+ {
+ max_G = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 0; ii < 256; ii++)
+ {
+ if (hisgram_B[ii] > 0)
+ if (hisgram_B[ii] >= imin_threshold[2])
+ {
+ min_B = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 255; ii >= 0; ii--)
+ {
+ if (hisgram_B[ii] > 0)
+ if (hisgram_B[ii] >= imax_threshold[2])
+ {
+ max_B = ii;
+ break;
+ }
+ }
+
+ DBG (DBG_INFO, "AutoLevel: Set min , max is over!\n");
+
+ /*Autolevel: */
+ sum_R = max_R - min_R;
+ sum_G = max_G - min_G;
+ sum_B = max_B - min_B;
+
+ for (j = 0; j < iHeight; j++)
+ {
+ tLines = j * iWidth * 3;
+ for (i = 0; i < iWidth; i++)
+ {
+ R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+
+ /*R*/ if (sum_R == 0)
+ R = max_R;
+ else if (R < min_R)
+ R = 0;
+ else if (R <= 255)
+ {
+ fmax_R = ((float) ((R - min_R) * 255) / (float) sum_R);
+ R = (unsigned short) fmax_R;
+ fmax_R = (fmax_R - R) * 10;
+
+ if (fmax_R >= 5)
+ R++;
+ }
+ if (R > 255)
+ R = 255;
+
+ /*G*/ if (sum_G == 0)
+ G = max_G;
+ else if (G < min_G)
+ G = 0;
+ else if (G <= 255)
+ {
+ fmax_G = ((float) ((G - min_G) * 255) / (float) sum_G);
+ G = (unsigned short) fmax_G;
+ fmax_G = (fmax_G - G) * 10;
+ if (fmax_G >= 5)
+ G++;
+
+ }
+ if (G > 255)
+ G = 255;
+
+ /*B*/ if (sum_B == 0)
+ B = max_B;
+ else if (B < min_B)
+ B = 0;
+ else if (B <= 255)
+ {
+ fmax_B = ((float) (B - min_B) * 255 / (float) sum_B);
+ B = (unsigned short) fmax_B;
+ fmax_B = (fmax_B - B) * 10;
+ if (fmax_B >= 5)
+ B++;
+ }
+ if (B > 255)
+ B = 255;
+
+ hisgram_R[(SANE_Byte) R]++;
+ hisgram_G[(SANE_Byte) G]++;
+ hisgram_B[(SANE_Byte) B]++;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
+ *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;
+
+ }
+ }
+
+ DBG (DBG_FUNC, "AutoLevel: exit\n");
+ return;
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Deal with image with auto level
+Parameters:
+ pDIB: the data of image
+ ImageWidth: the width of image
+ ImageHeight: the height of image
+Return value:
+ none
+***********************************************************************/
+static void
+QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight)
+{
+ unsigned short *pbmpdata;
+ float fRPercent = 0.0;
+ float fGPercent = 0.0;
+ float fBPercent = 0.0;
+ float fRSum, fGSum, fBSum;
+
+ int i, j;
+ unsigned int tLines, CountPixels, TotalImgSize;
+ unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B;
+ unsigned short wIndexR, wIndexG, wIndexB;
+ float fmax_R, fmax_G, fmax_B;
+ unsigned int sum_R = 0, sum_G = 0, sum_B = 0;
+ unsigned int hisgram_R[1024], hisgram_G[1024], hisgram_B[1024];
+
+ if (!pDIB)
+ {
+ return;
+ }
+
+ pbmpdata = (unsigned short *) pDIB;
+
+ CountPixels = 0;
+ TotalImgSize = ImageWidth * ImageHeight;
+
+
+ for (i = 0; i < 1024; i++)
+ {
+
+ hisgram_R[i] = 0;
+ hisgram_G[i] = 0;
+ hisgram_B[i] = 0;
+ }
+
+
+ /*Find min , max, mean */
+ max_R = max_G = max_B = 0;
+ min_R = min_G = min_B = 1023;
+ tLines = 0;
+
+ for (j = 0; j < (int) ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+ for (i = 0; i < (int) ImageWidth; i++)
+ {
+ R = *(pbmpdata + (tLines + i * 3 + 2));
+ G = *(pbmpdata + (tLines + i * 3 + 1));
+ B = *(pbmpdata + (tLines + i * 3));
+
+ max_R = _MAX (R, max_R);
+ max_G = _MAX (G, max_G);
+ max_B = _MAX (B, max_B);
+
+ min_R = _MIN (R, min_R);
+ min_G = _MIN (G, min_G);
+ min_B = _MIN (B, min_B);
+
+ hisgram_R[R]++;
+ hisgram_G[G]++;
+ hisgram_B[B]++;
+
+ sum_R += R;
+ sum_G += G;
+ sum_B += B;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = G;
+ *(pbmpdata + (tLines + i * 3)) = B;
+
+ CountPixels++;
+ }
+
+ }
+
+
+ fRSum = 0.0;
+ fGSum = 0.0;
+ fBSum = 0.0;
+
+ wIndexR = 511;
+ wIndexG = 511;
+ wIndexB = 511;
+
+ for (i = 0; i < 1024; i++)
+ {
+ fRSum += (float) hisgram_R[i];
+ fRPercent = (fRSum / CountPixels) * 100;
+ if (fRPercent > 50)
+ {
+ wIndexR = i;
+ break;
+ }
+
+ }
+
+ for (i = 0; i < 1024; i++)
+ {
+ fGSum += (float) hisgram_G[i];
+ fGPercent = (fGSum / CountPixels) * 100;
+ if (fGPercent > 50)
+ {
+ wIndexG = i;
+ break;
+ }
+ }
+
+ for (i = 0; i < 1024; i++)
+ {
+ fBSum += (float) hisgram_B[i];
+ fBPercent = (fBSum / CountPixels) * 100;
+ if (fBPercent > 50)
+ {
+ wIndexB = i;
+ break;
+ }
+
+ }
+
+
+ fRSum = 0.0;
+
+ for (i = wIndexR; i >= 0; i--)
+ {
+ fRSum += (float) hisgram_R[i];
+ fRPercent = (fRSum / CountPixels) * 100;
+ if (fRPercent >= 48)
+ {
+ min_R = i;
+ break;
+ }
+
+ }
+
+ fRSum = 0.0;
+ for (i = wIndexR; i < 1024; i++)
+ {
+ fRSum += (float) hisgram_R[i];
+ fRPercent = (fRSum / CountPixels) * 100;
+ if (fRPercent >= 47)
+ {
+ max_R = i;
+ break;
+ }
+
+ }
+
+
+ fGSum = 0.0;
+ for (i = wIndexG; i >= 0; i--)
+ {
+ fGSum += (float) hisgram_G[i];
+ fGPercent = (fGSum / CountPixels) * 100;
+ if (fGPercent >= 48)
+ {
+ min_G = i;
+ break;
+ }
+
+ }
+
+ fGSum = 0.0;
+ for (i = wIndexG; i < 1024; i++)
+ {
+ fGSum += (float) hisgram_G[i];
+ fGPercent = (fGSum / CountPixels) * 100;
+ if (fGPercent >= 47)
+ {
+ max_G = i;
+ break;
+ }
+
+ }
+
+ fBSum = 0.0;
+ for (i = wIndexB; i >= 0; i--)
+ {
+ fBSum += (float) hisgram_B[i];
+ fBPercent = (fBSum / CountPixels) * 100;
+ if (fBPercent >= 46)
+ {
+ min_B = i;
+ break;
+ }
+
+ }
+
+ fBSum = 0.0;
+ for (i = wIndexB; i < 1024; i++)
+ {
+ fBSum += (float) hisgram_B[i];
+ fBPercent = (fBSum / CountPixels) * 100;
+ if (fBPercent >= 47)
+ {
+ max_B = i;
+ break;
+ }
+
+ }
+
+
+ /*Autolevel: */
+ sum_R = max_R - min_R;
+ sum_G = max_G - min_G;
+ sum_B = max_B - min_B;
+
+ for (j = 0; j < (int) ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+ for (i = 0; i < (int) ImageWidth; i++)
+ {
+ R = *(pbmpdata + (tLines + i * 3 + 2));
+ G = *(pbmpdata + (tLines + i * 3 + 1));
+ B = *(pbmpdata + (tLines + i * 3));
+
+
+ /*R*/ if (sum_R == 0)
+ R = max_R;
+ else if (R < min_R)
+ {
+
+ R = 0;
+ }
+ else if ((R >= min_R) && (R <= 1023))
+ {
+ fmax_R = ((float) ((R - min_R) * 923) / (float) sum_R) + 100;
+ R = (unsigned short) fmax_R;
+ fmax_R = (fmax_R - R) * 10;
+ if (fmax_R >= 5)
+ R++;
+ }
+ if (R > 1023)
+ R = 1023;
+
+ /*G*/ if (sum_G == 0)
+ G = max_G;
+ else if (G < min_G)
+ {
+
+ G = 0;
+ }
+ else if ((G >= min_G) && (G <= 1023))
+ {
+ fmax_G = ((float) ((G - min_G) * 923) / (float) sum_G) + 100;
+ G = (unsigned short) fmax_G;
+ fmax_G = (fmax_G - G) * 10;
+ if (fmax_G >= 5)
+ G++;
+ }
+ if (G > 1023)
+ G = 1023;
+
+ /*B*/ if (sum_B == 0)
+ B = max_B;
+ else if (B < min_R)
+ {
+
+ B = 0;
+ }
+ else if ((B >= min_B) && (R <= 1023))
+ {
+ fmax_B = ((float) (B - min_B) * 923 / (float) sum_B) + 100;
+
+ B = (unsigned short) fmax_B;
+ fmax_B = (fmax_B - B) * 10;
+ if (fmax_B >= 5)
+ B++;
+ }
+ if (B > 1023)
+ B = 1023;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = G;
+ *(pbmpdata + (tLines + i * 3)) = B;
+
+ }
+ }
+
+ return;
+}
+#endif
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Change the image data and deal with auto level
+Parameters:
+ lpSource: the data of image
+ scanMode: the scan mode
+ ScanLines: the rows of image
+ BytesPerLine: the bytes of per line
+Return value:
+ none
+***********************************************************************/
+static void
+QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine)
+{
+ unsigned short i, j;
+ unsigned int tLines, TotalImgSize;
+ unsigned short R1, G1, B1, R, G, B, R2, G2, B2, QBET_RGB = 0, PointF, PointB;
+ unsigned short *pwRGB;
+
+ int k;
+
+ unsigned int ImageWidth = BytesPerLine / 3;
+ unsigned int ImageHeight = ScanLines;
+ SANE_Byte *pbmpdata = (SANE_Byte *) lpSource;
+
+ if (scanMode != CM_RGB24ext)
+ {
+ return;
+ }
+
+
+ TotalImgSize = ImageWidth * ImageHeight * 3 * 2;
+ if ((pwRGB = (unsigned short *) malloc (TotalImgSize)) == NULL)
+ {
+ return;
+ }
+
+
+ for (j = 0; j < ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+ for (i = 0; i < ImageWidth; i++)
+ {
+ if (i == 0)
+ {
+ R1 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G1 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B1 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+ R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2));
+ G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1));
+ B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3));
+ }
+ else if (i == (ImageWidth - 1))
+ {
+ R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2));
+ G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1));
+ B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3));
+ R2 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G2 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B2 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+ }
+ else
+ {
+ R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2));
+ G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1));
+ B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3));
+
+ R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+
+ R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2));
+ G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1));
+ B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3));
+ }
+
+ R1 = R1 & 0x0003;
+ G1 = G1 & 0x0003;
+ B1 = B1 & 0x0003;
+
+ R2 = R2 & 0x0003;
+ G2 = G2 & 0x0003;
+ B2 = B2 & 0x0003;
+ for (k = 0; k < 3; k++)
+ {
+ if (k == 0)
+ {
+ PointF = R1;
+ PointB = R2;
+ }
+ else if (k == 1)
+ {
+ PointF = G1;
+ PointB = G2;
+ }
+ else if (k == 2)
+ {
+ PointF = B1;
+ PointB = B2;
+ }
+
+ switch (PointF)
+ {
+ case 0:
+ case 1:
+ if (PointB == 0)
+ QBET_RGB = 0xFFFC;
+ else if (PointB == 1)
+ QBET_RGB = 0xFFFC;
+ else if (PointB == 2)
+ QBET_RGB = 0xFFFD;
+ else if (PointB == 3)
+ QBET_RGB = 0xFFFE;
+ break;
+ case 2:
+ if (PointB == 0)
+ QBET_RGB = 0xFFFD;
+ else if (PointB == 1)
+ QBET_RGB = 0xFFFD;
+ else if (PointB == 2)
+ QBET_RGB = 0xFFFF;
+ else if (PointB == 3)
+ QBET_RGB = 0xFFFF;
+ break;
+ case 3:
+ if (PointB == 0)
+ QBET_RGB = 0xFFFE;
+ else if (PointB == 1)
+ QBET_RGB = 0xFFFE;
+ else if (PointB == 2)
+ QBET_RGB = 0xFFFF;
+ else if (PointB == 3)
+ QBET_RGB = 0xFFFF;
+ break;
+ default:
+ break;
+ }
+
+ if (k == 0)
+ {
+ R = R << 2;
+ R = R + 0x0003;
+ R = R & QBET_RGB;
+ }
+ else if (k == 1)
+ {
+ G = G << 2;
+ G = G + 0x0003;
+ G = G & QBET_RGB;
+ }
+ else if (k == 2)
+ {
+ B = B << 2;
+ B = B + 0x0003;
+ B = B & QBET_RGB;
+ }
+
+ }
+
+ *(pwRGB + (tLines + i * 3 + 2)) = R;
+ *(pwRGB + (tLines + i * 3 + 1)) = G;
+ *(pwRGB + (tLines + i * 3)) = B;
+
+ }
+
+ }
+
+
+ QBETDetectAutoLevel (pwRGB, ImageWidth, ImageHeight);
+
+
+ for (j = 0; j < ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+
+ for (i = 0; i < ImageWidth; i++)
+ {
+ R = *(pwRGB + (tLines + i * 3 + 2));
+ G = *(pwRGB + (tLines + i * 3 + 1));
+ B = *(pwRGB + (tLines + i * 3));
+
+ R = R >> 2;
+ G = G >> 2;
+
+ B = B >> 2;
+ if (R > 255)
+ R = 255;
+ if (G > 255)
+ G = 255;
+ if (B > 255)
+ B = 255;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
+ *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;
+
+ }
+
+ }
+
+
+ if (pwRGB != NULL)
+ {
+ free (pwRGB);
+ }
+
+ return;
+}
+#endif
+
+/****************************** SANE API functions *****************************/
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+ DBG (DBG_FUNC, "sane_init: start\n");
+ DBG (DBG_ERR, "SANE Mustek USB2 backend version %d.%d build %d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ num_devices = 1; /* HOLD: only one device in this backend */
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (DBG_INFO, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ DBG (DBG_FUNC, "sane_init: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_FUNC, "sane_exit: start\n");
+
+ if (devlist != NULL)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ devlist = NULL;
+ DBG (DBG_FUNC, "sane_exit: exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Int dev_num;
+ DBG (DBG_FUNC, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ if (devlist != NULL)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (devlist == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev_num = 0;
+ /* HOLD: This is ugly (only one scanner!) and should go to sane_init */
+ if (GetDeviceStatus ())
+ {
+ SANE_Device *sane_device;
+
+ sane_device = malloc (sizeof (*sane_device));
+ if (sane_device == NULL)
+ return SANE_STATUS_NO_MEM;
+ sane_device->name = strdup (device_name);
+ sane_device->vendor = strdup ("Mustek");
+ sane_device->model = strdup ("BearPaw 2448 TA Pro");
+ sane_device->type = strdup ("flatbed scanner");
+ devlist[dev_num++] = sane_device;
+ }
+ devlist[dev_num] = 0;
+ *device_list = devlist;
+ DBG (DBG_FUNC, "sane_get_devices: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Mustek_Scanner *s;
+
+ DBG (DBG_FUNC, "sane_open: start :devicename = %s\n", devicename);
+
+ if (!MustScanner_Init ())
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (!PowerControl (SANE_FALSE, SANE_FALSE))
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (!CarriageHome ())
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ s = malloc (sizeof (*s));
+ if (s == NULL)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->gamma_table = NULL;
+ memcpy (&s->model, &mustek_A2nu2_model, sizeof (Scanner_Model));
+ s->next = NULL;
+ s->bIsScanning = SANE_FALSE;
+ s->bIsReading = SANE_FALSE;
+
+ init_options (s);
+ *handle = s;
+
+ s->read_rows = 0;
+ s->scan_buffer_len = 0;
+
+ DBG (DBG_FUNC, "sane_open: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Mustek_Scanner *s = handle;
+ DBG (DBG_FUNC, "sane_close: start\n");
+
+ PowerControl (SANE_FALSE, SANE_FALSE);
+
+ CarriageHome ();
+
+ if (NULL != g_pDeviceFile)
+ {
+ free (g_pDeviceFile);
+ g_pDeviceFile = NULL;
+ }
+
+ if (s->Scan_data_buf != NULL)
+ free (s->Scan_data_buf);
+
+ s->Scan_data_buf = NULL;
+
+ free (handle);
+
+ DBG (DBG_FUNC, "sane_close: exit\n");
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Mustek_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ DBG (DBG_FUNC, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option);
+ return s->opt + option;
+}
+
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Mustek_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (DBG_FUNC,
+ "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
+ SANE_ACTION_SET_VALUE) ?
+ "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->opt[option].name, option);
+
+
+ if (info)
+ *info = 0;
+
+ if (s->bIsScanning)
+ {
+ DBG (DBG_ERR, "sane_control_option: don't call this function while "
+ "scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (DBG_ERR,
+ "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (DBG_ERR, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_AUTO_WARMUP:
+ case OPT_GAMMA_VALUE:
+ case OPT_THRESHOLD:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ break;
+
+ case OPT_SOURCE:
+ strcpy (val, s->val[option].s);
+ break;
+ default:
+ DBG (DBG_ERR, "sane_control_option: can't get unknown option %d\n",
+ option);
+ ;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_ERR, "sane_control_option: option %d is not settable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_THRESHOLD:
+ case OPT_AUTO_WARMUP:
+ case OPT_GAMMA_VALUE:
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ /* side-effect-free word-array options: */
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ ENABLE (OPT_THRESHOLD);
+ }
+ else
+ {
+ DISABLE (OPT_THRESHOLD);
+ }
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_SOURCE:
+ if (strcmp (s->val[option].s, val) != 0)
+ { /* something changed */
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, "Reflective") == 0)
+ {
+ PowerControl (SANE_TRUE, SANE_FALSE);
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+ x_range.max = s->model.x_size;
+ y_range.max = s->model.y_size;
+ }
+ else if (0 == strcmp (s->val[option].s, "Negative"))
+ {
+ PowerControl (SANE_FALSE, SANE_TRUE);
+ s->opt[OPT_MODE].size =
+ max_string_size (negative_mode_list);
+ s->opt[OPT_MODE].constraint.string_list =
+ negative_mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+ x_range.max = s->model.x_size_ta;
+ y_range.max = s->model.y_size_ta;
+ }
+ else if (0 == strcmp (s->val[option].s, "Positive"))
+ {
+ PowerControl (SANE_FALSE, SANE_TRUE);
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+ x_range.max = s->model.x_size_ta;
+ y_range.max = s->model.y_size_ta;
+ }
+ }
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ default:
+ DBG (DBG_ERR, "sane_control_option: can't set unknown option %d\n",
+ option);
+ }
+ }
+ else
+ {
+ DBG (DBG_ERR, "sane_control_option: unknown action %d for option %d\n",
+ action, option);
+ return SANE_STATUS_INVAL;
+ }
+ if (info)
+ *info = myinfo;
+
+ DBG (DBG_FUNC, "sane_control_option: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Mustek_Scanner *s = handle;
+
+ DBG (DBG_FUNC, "sane_get_parameters: start\n");
+
+ DBG (DBG_INFO, "sane_get_parameters :params.format = %d\n",
+ s->params.format);
+
+ DBG (DBG_INFO, "sane_get_parameters :params.depth = %d\n", s->params.depth);
+ DBG (DBG_INFO, "sane_get_parameters :params.pixels_per_line = %d\n",
+ s->params.pixels_per_line);
+ DBG (DBG_INFO, "sane_get_parameters :params.bytes_per_line = %d\n",
+ s->params.bytes_per_line);
+ DBG (DBG_INFO, "sane_get_parameters :params.lines = %d\n", s->params.lines);
+ if (params != NULL)
+ *params = s->params;
+
+ DBG (DBG_FUNC, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int i;
+ Mustek_Scanner *s = handle;
+
+ DBG (DBG_FUNC, "sane_start: start\n");
+
+ s->scan_buffer_len = 0;
+
+ calc_parameters (s);
+
+ if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w)
+ {
+ DBG (DBG_CRIT,
+ "sane_start: top left x >= bottom right x --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w)
+ {
+ DBG (DBG_CRIT,
+ "sane_start: top left y >= bottom right y --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ s->setpara.pGammaTable = NULL;
+
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x1=%d\n",
+ s->setpara.fmArea.x1);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x2=%d\n",
+ s->setpara.fmArea.x2);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y1=%d\n",
+ s->setpara.fmArea.y1);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y2=%d\n",
+ s->setpara.fmArea.y2);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.pfPixelFlavor=%d\n",
+ s->setpara.pfPixelFlavor);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.wLinearThreshold=%d\n",
+ s->setpara.wLinearThreshold);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.wTargetDPI=%d\n",
+ s->setpara.wTargetDPI);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.smScanMode=%d\n",
+ s->setpara.smScanMode);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.ssScanSource =%d\n",
+ s->setpara.ssScanSource);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.pGammaTable =%p\n",
+ (void *) s->setpara.pGammaTable);
+
+ SetParameters (&s->setpara);
+
+ GetParameters (&s->getpara);
+
+ switch (s->params.format)
+ {
+ case SANE_FRAME_RGB:
+ if (s->params.depth == 8)
+
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth / 3;
+ if (s->params.depth == 16)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth / 6;
+
+
+ break;
+ case SANE_FRAME_GRAY:
+ if (s->params.depth == 1)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth * 8;
+ if (s->params.depth == 8)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth;
+ if (s->params.depth == 16)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth / 2;
+ break;
+ default:
+ DBG (DBG_INFO, "sane_start: sane_params.format = %d\n",
+ s->params.format);
+ }
+
+ s->params.bytes_per_line = s->getpara.dwLineByteWidth;
+ s->params.lines = s->getpara.dwLength;
+
+ s->params.last_frame = TRUE;
+
+
+ s->read_rows = s->getpara.dwLength;
+ DBG (DBG_INFO, "sane_start : read_rows = %d\n", s->read_rows);
+
+ /*warmming up */
+ if (s->val[OPT_AUTO_WARMUP].w)
+ {
+ for (i = 30; i > 0; i--)
+ {
+ sleep (1);
+ DBG (DBG_ERR, "warming up: %d\n", i);
+ }
+ }
+ DBG (DBG_INFO, "SCANNING ... \n");
+
+ s->bIsScanning = SANE_TRUE;
+ if (s->Scan_data_buf != NULL)
+ free (s->Scan_data_buf);
+ s->Scan_data_buf = NULL;
+
+ s->Scan_data_buf = malloc (SCAN_BUFFER_SIZE * sizeof (SANE_Byte));
+ if (s->Scan_data_buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ StartScan ();
+
+ DBG (DBG_FUNC, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+
+ Mustek_Scanner *s = handle;
+ static SANE_Byte *tempbuf;
+ SANE_Int lines_to_read, lines_read;
+ IMAGEROWS image_row;
+
+ int maxbuffersize = max_len;
+
+ DBG (DBG_FUNC, "sane_read: start: max_len=%d\n", max_len);
+
+ if (s == NULL)
+ {
+ DBG (DBG_ERR, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (buf == NULL)
+ {
+ DBG (DBG_ERR, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (len == NULL)
+ {
+ DBG (DBG_ERR, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ *len = 0;
+ if (!s->bIsScanning)
+ {
+ DBG (DBG_WARN, "sane_read: scan was cancelled, is over or has not been "
+ "initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ DBG (DBG_DBG, "sane_read: before read data read_row=%d\n", s->read_rows);
+ if (s->scan_buffer_len == 0)
+ {
+ if (s->read_rows > 0)
+ {
+ lines_to_read = SCAN_BUFFER_SIZE / s->getpara.dwLineByteWidth;
+
+ if (lines_to_read > s->read_rows)
+ lines_to_read = s->read_rows;
+
+ tempbuf =
+ (SANE_Byte *) malloc (sizeof (SANE_Byte) * lines_to_read *
+ s->getpara.dwLineByteWidth + 3 * 1024 + 1);
+ memset (tempbuf, 0,
+ sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth +
+ 3 * 1024 + 1);
+
+ DBG (DBG_INFO, "sane_read: buffer size is %ld\n",
+ (long int) sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth +
+ 3 * 1024 + 1);
+
+ image_row.roRgbOrder = mustek_A2nu2_model.line_mode_color_order;
+ image_row.wWantedLineNum = lines_to_read;
+ image_row.pBuffer = (SANE_Byte *) tempbuf;
+ s->bIsReading = SANE_TRUE;
+
+ if (!ReadScannedData (&image_row))
+ {
+ DBG (DBG_ERR, "sane_read: ReadScannedData error\n");
+ s->bIsReading = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (DBG_DBG, "sane_read: Finish ReadScanedData\n");
+ s->bIsReading = SANE_FALSE;
+ memset (s->Scan_data_buf, 0, SCAN_BUFFER_SIZE);
+ s->scan_buffer_len =
+ image_row.wXferedLineNum * s->getpara.dwLineByteWidth;
+ DBG (DBG_INFO, "sane_read : s->scan_buffer_len = %ld\n",
+ (long int) s->scan_buffer_len);
+
+ memcpy (s->Scan_data_buf, tempbuf, s->scan_buffer_len);
+
+ DBG (DBG_DBG, "sane_read :after memcpy\n");
+ free (tempbuf);
+ s->Scan_data_buf_start = s->Scan_data_buf;
+ s->read_rows -= image_row.wXferedLineNum;
+
+ }
+ else
+ {
+ DBG (DBG_FUNC, "sane_read: scan finished -- exit\n");
+ sane_cancel (handle);
+ return SANE_STATUS_EOF;
+ }
+ }
+ if (s->scan_buffer_len == 0)
+ {
+ DBG (DBG_FUNC, "sane_read: scan finished -- exit\n");
+ sane_cancel (handle);
+ return SANE_STATUS_EOF;
+ }
+
+
+
+
+ lines_read =
+ (maxbuffersize <
+ (SANE_Int) s->scan_buffer_len) ? maxbuffersize : (SANE_Int) s->scan_buffer_len;
+ DBG (DBG_DBG, "sane_read: after %d\n", lines_read);
+
+ *len = (SANE_Int) lines_read;
+
+ DBG (DBG_INFO, "sane_read : get lines_read = %d\n", lines_read);
+ DBG (DBG_INFO, "sane_read : get *len = %d\n", *len);
+ memcpy (buf, s->Scan_data_buf_start, lines_read);
+
+ s->scan_buffer_len -= lines_read;
+ s->Scan_data_buf_start += lines_read;
+ DBG (DBG_FUNC, "sane_read: exit\n");
+ return SANE_STATUS_GOOD;
+
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Mustek_Scanner *s = handle;
+ int i;
+ DBG (DBG_FUNC, "sane_cancel: start\n");
+ if (s->bIsScanning)
+ {
+ s->bIsScanning = SANE_FALSE;
+ if (s->read_rows > 0)
+ {
+ DBG (DBG_INFO, "sane_cancel: warning: is scanning\n");
+
+ }
+ else
+ {
+ DBG (DBG_INFO, "sane_cancel: Scan finished\n");
+ }
+
+ StopScan ();
+
+ CarriageHome ();
+ for (i = 0; i < 20; i++)
+ {
+ if (s->bIsReading == SANE_FALSE)
+ {
+ if (s->gamma_table != NULL)
+ {
+ free (s->gamma_table);
+ s->gamma_table = NULL;
+ break;
+ }
+ }
+ else
+ sleep (1);
+ }
+ if (s->Scan_data_buf != NULL)
+ {
+ free (s->Scan_data_buf);
+ s->Scan_data_buf = NULL;
+ s->Scan_data_buf_start = NULL;
+ }
+
+ s->read_rows = 0;
+ s->scan_buffer_len = 0;
+ memset (&s->setpara, 0, sizeof (s->setpara));
+ memset (&s->getpara, 0, sizeof (s->getpara));
+
+ }
+ else
+ {
+ DBG (DBG_INFO, "sane_cancel: do nothing\n");
+ }
+
+
+ DBG (DBG_FUNC, "sane_cancel: exit\n");
+
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Mustek_Scanner *s = handle;
+ DBG (DBG_FUNC, "sane_set_io_mode: handle = %p, non_blocking = %s\n",
+ handle, non_blocking == SANE_TRUE ? "true" : "false");
+ if (!s->bIsScanning)
+ {
+ DBG (DBG_WARN, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Mustek_Scanner *s = handle;
+ DBG (DBG_FUNC, "sane_get_select_fd: handle = %p, fd = %p\n", handle,
+ (void *) fd);
+ if (!s->bIsScanning)
+ {
+ DBG (DBG_WARN, "%s", "sane_get_select_fd: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/mustek_usb2.h b/backend/mustek_usb2.h
new file mode 100644
index 0000000..b80c51a
--- /dev/null
+++ b/backend/mustek_usb2.h
@@ -0,0 +1,158 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000-2005 Mustek.
+ Originally maintained by Mustek
+
+ Copyright (C) 2001-2005 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#ifndef MUSTEK_USB2_H
+#define MUSTEK_USB2_H
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+/* RIE: return if error */
+#define RIE(function) do {status = function; if (status != SANE_STATUS_GOOD) \
+return status;} while (SANE_FALSE)
+
+#define SCAN_BUFFER_SIZE (64 * 1024)
+#define MAX_RESOLUTIONS 12
+#define DEF_LINEARTTHRESHOLD 128
+#define PER_ADD_START_LINES 0
+#define PRE_ADD_START_X 0
+
+
+enum Mustek_Usb_Option
+{
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_SOURCE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+
+ OPT_DEBUG_GROUP,
+ OPT_AUTO_WARMUP,
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_THRESHOLD,
+ OPT_GAMMA_VALUE,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+
+typedef struct Scanner_Model
+{
+ /** @name Identification */
+ /*@{ */
+
+ /** A single lowercase word to be used in the configuration file. */
+ SANE_String_Const name;
+
+ /** Device vendor string. */
+ SANE_String_Const vendor;
+
+ /** Device model name. */
+ SANE_String_Const model;
+
+ /** Name of the firmware file. */
+ SANE_String_Const firmware_name;
+
+ /** @name Scanner model parameters */
+ /*@{ */
+
+ SANE_Int dpi_values[MAX_RESOLUTIONS]; /* possible resolutions */
+ SANE_Fixed x_offset; /* Start of scan area in mm */
+ SANE_Fixed y_offset; /* Start of scan area in mm */
+ SANE_Fixed x_size; /* Size of scan area in mm */
+ SANE_Fixed y_size; /* Size of scan area in mm */
+
+ SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */
+ SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */
+
+
+ RGBORDER line_mode_color_order; /* Order of the CCD/CIS colors */
+ SANE_Fixed default_gamma_value; /* Default gamma value */
+
+ SANE_Bool is_cis; /* Is this a CIS or CCD scanner? */
+
+ SANE_Word flags; /* Which hacks are needed for this scanner? */
+ /*@} */
+} Scanner_Model;
+
+typedef struct Mustek_Scanner
+{
+ /* all the state needed to define a scan request: */
+ struct Mustek_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ unsigned short *gamma_table;
+ SANE_Parameters params; /**< SANE Parameters */
+ Scanner_Model model;
+ SETPARAMETERS setpara;
+ GETPARAMETERS getpara;
+ SANE_Bool bIsScanning;
+ SANE_Bool bIsReading;
+ SANE_Word read_rows; /* transfer image's lines */
+ SANE_Byte *Scan_data_buf; /*store Scanned data for transfer */
+ SANE_Byte *Scan_data_buf_start; /*point to data need to transfer */
+ size_t scan_buffer_len; /* length of data buf */
+}
+Mustek_Scanner;
+
+#endif
diff --git a/backend/mustek_usb2_asic.c b/backend/mustek_usb2_asic.c
new file mode 100644
index 0000000..3019e5e
--- /dev/null
+++ b/backend/mustek_usb2_asic.c
@@ -0,0 +1,5264 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Mustek.
+ Originally maintained by Mustek
+ Author:Roy 2005.5.24
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#include "mustek_usb2_asic.h"
+
+/* ---------------------- low level asic functions -------------------------- */
+
+static SANE_Byte RegisterBankStatus = -1;
+
+static STATUS
+WriteIOControl (PAsic chip, unsigned short wValue, unsigned short wIndex, unsigned short wLength,
+ SANE_Byte * lpbuf)
+{
+ STATUS status = STATUS_GOOD;
+
+ status =
+ sanei_usb_control_msg (chip->fd, 0x40, 0x01, wValue, wIndex, wLength,
+ lpbuf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "WriteIOControl Error!\n");
+ return status;
+ }
+
+ return STATUS_GOOD;
+}
+
+static STATUS
+ReadIOControl (PAsic chip, unsigned short wValue, unsigned short wIndex, unsigned short wLength,
+ SANE_Byte * lpbuf)
+{
+ STATUS status = STATUS_GOOD;
+
+ status =
+ sanei_usb_control_msg (chip->fd, 0xc0, 0x01, wValue, wIndex, wLength,
+ lpbuf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "WriteIOControl Error!\n");
+ return status;
+ }
+
+ return status;
+}
+
+static STATUS
+Mustek_ClearFIFO (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte buf[4];
+ DBG (DBG_ASIC, "Mustek_ClearFIFO:Enter\n");
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ status = WriteIOControl (chip, 0x05, 0, 4, (SANE_Byte *) (buf));
+ if (status != STATUS_GOOD)
+ return status;
+
+ status = WriteIOControl (chip, 0xc0, 0, 4, (SANE_Byte *) (buf));
+ if (status != STATUS_GOOD)
+ return status;
+
+
+ DBG (DBG_ASIC, "Mustek_ClearFIFO:Exit\n");
+ return STATUS_GOOD;
+}
+
+
+static STATUS
+Mustek_SendData (PAsic chip, unsigned short reg, SANE_Byte data)
+{
+ SANE_Byte buf[4];
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Mustek_SendData: Enter. reg=%x,data=%x\n", reg, data);
+
+
+ if (reg <= 0xFF)
+ {
+ if (RegisterBankStatus != 0)
+ {
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ buf[0] = ES01_5F_REGISTER_BANK_SELECT;
+ buf[1] = SELECT_REGISTER_BANK0;
+ buf[2] = ES01_5F_REGISTER_BANK_SELECT;
+ buf[3] = SELECT_REGISTER_BANK0;
+ WriteIOControl (chip, 0xb0, 0, 4, buf);
+ RegisterBankStatus = 0;
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ }
+
+ }
+ else if (reg <= 0x1FF)
+ {
+ if (RegisterBankStatus != 1)
+ {
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ buf[0] = ES01_5F_REGISTER_BANK_SELECT;
+ buf[1] = SELECT_REGISTER_BANK1;
+ buf[2] = ES01_5F_REGISTER_BANK_SELECT;
+ buf[3] = SELECT_REGISTER_BANK1;
+
+ WriteIOControl (chip, 0xb0, 0, 4, buf);
+ RegisterBankStatus = 1;
+ }
+ }
+ else if (reg <= 0x2FF)
+ {
+ if (RegisterBankStatus != 2)
+ {
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ buf[0] = ES01_5F_REGISTER_BANK_SELECT;
+ buf[1] = SELECT_REGISTER_BANK2;
+ buf[2] = ES01_5F_REGISTER_BANK_SELECT;
+ buf[3] = SELECT_REGISTER_BANK2;
+
+ WriteIOControl (chip, 0xb0, 0, 4, buf);
+ RegisterBankStatus = 2;
+ }
+ }
+
+ buf[0] = LOBYTE (reg);
+ buf[1] = data;
+ buf[2] = LOBYTE (reg);
+ buf[3] = data;
+ status = WriteIOControl (chip, 0xb0, 0, 4, buf);
+ if (status != STATUS_GOOD)
+ DBG (DBG_ERR, ("Mustek_SendData: write error\n"));
+
+ return status;
+}
+
+static STATUS
+Mustek_ReceiveData (PAsic chip, SANE_Byte * reg)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte buf[4];
+
+ DBG (DBG_ASIC, "Mustek_ReceiveData\n");
+
+ status = ReadIOControl (chip, 0x07, 0, 4, buf);
+
+ *reg = buf[0];
+ return status;
+}
+
+static STATUS
+Mustek_WriteAddressLineForRegister (PAsic chip, SANE_Byte x)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte buf[4];
+
+ DBG (DBG_ASIC, "Mustek_WriteAddressLineForRegister: Enter\n");
+
+ buf[0] = x;
+ buf[1] = x;
+ buf[2] = x;
+ buf[3] = x;
+ status = WriteIOControl (chip, 0x04, x, 4, buf);
+
+ DBG (DBG_ASIC, "Mustek_WriteAddressLineForRegister: Exit\n");
+ return status;
+}
+
+
+static STATUS
+SetRWSize (PAsic chip, SANE_Byte ReadWrite, unsigned int size)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "SetRWSize: Enter\n");
+
+ if (ReadWrite == 0)
+ { /*write */
+ status = Mustek_SendData (chip, 0x7C, (SANE_Byte) (size));
+ if (status != STATUS_GOOD)
+ return status;
+ status = Mustek_SendData (chip, 0x7D, (SANE_Byte) (size >> 8));
+ if (status != STATUS_GOOD)
+ return status;
+ status = Mustek_SendData (chip, 0x7E, (SANE_Byte) (size >> 16));
+ if (status != STATUS_GOOD)
+ return status;
+ status = Mustek_SendData (chip, 0x7F, (SANE_Byte) (size >> 24));
+ if (status != STATUS_GOOD)
+ return status;
+ }
+ else
+ { /* read */
+ status = Mustek_SendData (chip, 0x7C, (SANE_Byte) (size >> 1));
+ if (status != STATUS_GOOD)
+ return status;
+ status = Mustek_SendData (chip, 0x7D, (SANE_Byte) (size >> 9));
+ if (status != STATUS_GOOD)
+ return status;
+ status = Mustek_SendData (chip, 0x7E, (SANE_Byte) (size >> 17));
+ if (status != STATUS_GOOD)
+ return status;
+ status = Mustek_SendData (chip, 0x7F, (SANE_Byte) (size >> 25));
+ if (status != STATUS_GOOD)
+ return status;
+ }
+
+ DBG (DBG_ASIC, "SetRWSize: Exit\n");
+ return STATUS_GOOD;
+}
+
+static STATUS
+Mustek_DMARead (PAsic chip, unsigned int size, SANE_Byte * lpdata)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned int i, buf[1];
+ unsigned int read_size;
+
+ DBG (DBG_ASIC, "Mustek_DMARead: Enter\n");
+
+ status = Mustek_ClearFIFO (chip);
+ if (status != STATUS_GOOD)
+ return status;
+
+ buf[0] = read_size = 32 * 1024;
+ for (i = 0; i < size / (read_size); i++)
+ {
+ SetRWSize (chip, 1, buf[0]);
+ status = WriteIOControl (chip, 0x03, 0, 4, (SANE_Byte *) (buf));
+
+ status =
+ sanei_usb_read_bulk (chip->fd, lpdata + i * read_size,
+ (size_t *) buf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Mustek_DMARead: read error\n");
+ return status;
+ }
+ }
+
+ buf[0] = size - i * read_size;
+ if (buf[0] > 0)
+ {
+ SetRWSize (chip, 1, buf[0]);
+ status = WriteIOControl (chip, 0x03, 0, 4, (SANE_Byte *) (buf));
+
+ status =
+ sanei_usb_read_bulk (chip->fd, lpdata + i * read_size,
+ (size_t *) buf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Mustek_DMARead: read error\n");
+ return status;
+ }
+
+ usleep (20000);
+ }
+
+ DBG (DBG_ASIC, "Mustek_DMARead: Exit\n");
+ return STATUS_GOOD;
+}
+
+static STATUS
+Mustek_DMAWrite (PAsic chip, unsigned int size, SANE_Byte * lpdata)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned int buf[1];
+ unsigned int i;
+ unsigned int write_size;
+
+ DBG (DBG_ASIC, "Mustek_DMAWrite: Enter:size=%d\n", size);
+
+ status = Mustek_ClearFIFO (chip);
+ if (status != STATUS_GOOD)
+ return status;
+
+ buf[0] = write_size = 32 * 1024;
+ for (i = 0; i < size / (write_size); i++)
+ {
+ SetRWSize (chip, 0, buf[0]);
+ WriteIOControl (chip, 0x02, 0, 4, (SANE_Byte *) buf);
+
+ status =
+ sanei_usb_write_bulk (chip->fd, lpdata + i * write_size,
+ (size_t *) buf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Mustek_DMAWrite: write error\n");
+ return status;
+ }
+ }
+
+
+ buf[0] = size - i * write_size;
+ if (buf[0] > 0)
+ {
+ SetRWSize (chip, 0, buf[0]);
+ WriteIOControl (chip, 0x02, 0, 4, (SANE_Byte *) buf);
+
+ status =
+ sanei_usb_write_bulk (chip->fd, lpdata + i * write_size,
+ (size_t *) buf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Mustek_DMAWrite: write error\n");
+ return status;
+ }
+ }
+
+ Mustek_ClearFIFO (chip);
+
+ DBG (DBG_ASIC, "Mustek_DMAWrite: Exit\n");
+ return STATUS_GOOD;
+}
+
+
+static STATUS
+Mustek_SendData2Byte (PAsic chip, unsigned short reg, SANE_Byte data)
+{
+ static SANE_Bool isTransfer = FALSE;
+ static SANE_Byte BankBuf[4];
+ static SANE_Byte DataBuf[4];
+
+ if (reg <= 0xFF)
+ {
+ if (RegisterBankStatus != 0)
+ {
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ BankBuf[0] = ES01_5F_REGISTER_BANK_SELECT;
+ BankBuf[1] = SELECT_REGISTER_BANK0;
+ BankBuf[2] = ES01_5F_REGISTER_BANK_SELECT;
+ BankBuf[3] = SELECT_REGISTER_BANK0;
+ WriteIOControl (chip, 0xb0, 0, 4, BankBuf);
+
+ RegisterBankStatus = 0;
+ }
+ }
+ else if (reg <= 0x1FF)
+ {
+ if (RegisterBankStatus != 1)
+ {
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ BankBuf[0] = ES01_5F_REGISTER_BANK_SELECT;
+ BankBuf[1] = SELECT_REGISTER_BANK1;
+ BankBuf[2] = ES01_5F_REGISTER_BANK_SELECT;
+
+ BankBuf[3] = SELECT_REGISTER_BANK1;
+ WriteIOControl (chip, 0xb0, 0, 4, BankBuf);
+ RegisterBankStatus = 1;
+ }
+ }
+ else if (reg <= 0x2FF)
+ {
+ if (RegisterBankStatus != 2)
+ {
+ DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus);
+ BankBuf[0] = ES01_5F_REGISTER_BANK_SELECT;
+ BankBuf[1] = SELECT_REGISTER_BANK2;
+ BankBuf[2] = ES01_5F_REGISTER_BANK_SELECT;
+ BankBuf[3] = SELECT_REGISTER_BANK2;
+ WriteIOControl (chip, 0xb0, 0, 4, BankBuf);
+ RegisterBankStatus = 2;
+ }
+ }
+
+ if (isTransfer == FALSE)
+ {
+ DataBuf[0] = LOBYTE (reg);
+ DataBuf[1] = data;
+ isTransfer = TRUE;
+ }
+ else
+ {
+ DataBuf[2] = LOBYTE (reg);
+ DataBuf[3] = data;
+ WriteIOControl (chip, 0xb0, 0, 4, DataBuf);
+ isTransfer = FALSE;
+ }
+
+ return STATUS_GOOD;
+}
+
+
+
+/* ---------------------- asic motor functions ----------------------------- */
+
+static STATUS
+LLFRamAccess (PAsic chip, LLF_RAMACCESS * RamAccess)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte a[2];
+
+ DBG (DBG_ASIC, "LLFRamAccess:Enter\n");
+
+ /*Set start address. Unit is a word. */
+ Mustek_SendData (chip, ES01_A0_HostStartAddr0_7,
+ LOBYTE (RamAccess->LoStartAddress));
+
+ if (RamAccess->IsOnChipGamma == ON_CHIP_FINAL_GAMMA)
+ { /* final gamma */
+ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15,
+ HIBYTE (RamAccess->LoStartAddress));
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21,
+ LOBYTE (RamAccess->HiStartAddress) | ACCESS_GAMMA_RAM);
+ }
+ else if (RamAccess->IsOnChipGamma == ON_CHIP_PRE_GAMMA)
+ { /* pre gamma */
+ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15,
+ HIBYTE (RamAccess->
+ LoStartAddress) | ES01_ACCESS_PRE_GAMMA);
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21,
+ LOBYTE (RamAccess->HiStartAddress) | ACCESS_GAMMA_RAM);
+ }
+ else
+ { /* dram */
+ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15,
+ HIBYTE (RamAccess->LoStartAddress));
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21,
+ LOBYTE (RamAccess->HiStartAddress) | ACCESS_DRAM);
+ }
+
+ /* set SDRAM delay time */
+ Mustek_SendData (chip, ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL,
+ SDRAMCLK_DELAY_12_ns);
+
+ /*Set end address. Unit is a word. */
+ Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, 0xff);
+ Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, 0xff);
+ Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, 0xff);
+ Mustek_ClearFIFO (chip);
+
+ if (RamAccess->ReadWrite == WRITE_RAM)
+ { /*Write RAM */
+ Mustek_DMAWrite (chip, RamAccess->RwSize, RamAccess->BufferPtr); /* read-write size must be even */
+
+ /*steal read 2byte */
+ usleep (20000);
+ RamAccess->RwSize = 2;
+ RamAccess->BufferPtr = (SANE_Byte *) a;
+ RamAccess->ReadWrite = READ_RAM;
+ LLFRamAccess (chip, RamAccess);
+ DBG (DBG_ASIC, "end steal 2 byte!\n");
+ }
+ else
+ { /* Read RAM */
+ Mustek_DMARead (chip, RamAccess->RwSize, RamAccess->BufferPtr); /* read-write size must be even */
+ }
+
+ DBG (DBG_ASIC, "LLFRamAccess:Exit\n");
+ return status;
+}
+
+
+static STATUS
+LLFSetMotorCurrentAndPhase (PAsic chip,
+ LLF_MOTOR_CURRENT_AND_PHASE *
+ MotorCurrentAndPhase)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte MotorPhase;
+
+ DBG (DBG_ASIC, "LLFSetMotorCurrentAndPhase:Enter\n");
+
+ if (MotorCurrentAndPhase->MotorDriverIs3967 == 1)
+ {
+ MotorPhase = 0xFE;
+ }
+ else
+ {
+ MotorPhase = 0xFF;
+ }
+
+ DBG (DBG_ASIC, "MotorPhase=0x%x\n", MotorPhase);
+ Mustek_SendData (chip, ES02_50_MOTOR_CURRENT_CONTORL, 0x01);
+
+ if (MotorCurrentAndPhase->FillPhase == 0)
+ {
+ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x00);
+
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*2 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*3 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*4 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+ }
+ else
+ {
+ if (MotorCurrentAndPhase->MoveType == _4_TABLE_SPACE_FOR_FULL_STEP)
+ { /* Full Step */
+ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x00);
+
+ /*1 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*2 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*3 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*4 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+ }
+ else if (MotorCurrentAndPhase->MoveType ==
+ _8_TABLE_SPACE_FOR_1_DIV_2_STEP)
+ { /* Half Step */
+ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x01);
+
+ /*1 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x25 & MotorPhase);
+
+ /*2 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x07 & MotorPhase);
+
+ /*3 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x24 & MotorPhase);
+
+ /*4 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x30 & MotorPhase);
+
+ /*5 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x2c & MotorPhase);
+
+ /*6 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x0e & MotorPhase);
+
+ /*7 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x2d & MotorPhase);
+
+ /*8 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ MotorCurrentAndPhase->MotorCurrentTableA[0]);
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ MotorCurrentAndPhase->MotorCurrentTableB[0]);
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x39 & MotorPhase);
+ }
+ else if (MotorCurrentAndPhase->MoveType ==
+ _16_TABLE_SPACE_FOR_1_DIV_4_STEP)
+ { /* 1/4 step */
+ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x02);
+
+ /*1 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*2 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*3 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*4 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*5 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*6 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*7 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*8 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*9 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*10 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*11 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*12 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*13 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*14 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*15 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*16 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 4 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ }
+ else if (MotorCurrentAndPhase->MoveType ==
+ _32_TABLE_SPACE_FOR_1_DIV_8_STEP)
+ {
+ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x03);
+
+ /*1 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*2 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*3 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*4 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*5 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*6 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*7 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*8 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x00 & MotorPhase);
+
+ /*9 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*10 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*11 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*12 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*13 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*14 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*15 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*16 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x08 & MotorPhase);
+
+ /*17 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*18 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*19 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*20 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*21 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*22 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*23 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*24 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x09 & MotorPhase);
+
+ /*25 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (0 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*26 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (1 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*27 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (2 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*28 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (3 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*29 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (4 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*30 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (5 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*31 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (6 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ /*32 */
+ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableA[0] * sin (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B,
+ (SANE_Byte) (MotorCurrentAndPhase->
+ MotorCurrentTableB[0] * cos (7 *
+ 3.141592654
+ * 90 /
+ 8 /
+ 180)));
+ Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1,
+ 0x01 & MotorPhase);
+
+ }
+ }
+
+ if (MotorCurrentAndPhase->FillPhase != 0)
+ {
+ Mustek_SendData (chip, ES02_50_MOTOR_CURRENT_CONTORL,
+ 0x00 | MotorCurrentAndPhase->MoveType);
+ }
+ else
+ {
+ Mustek_SendData (chip, ES02_50_MOTOR_CURRENT_CONTORL, 0x00);
+ }
+
+ DBG (DBG_ASIC, "LLFSetMotorCurrentAndPhase:Exit\n");
+ return status;
+}
+
+
+#if SANE_UNUSED
+static STATUS
+LLFStopMotorMove (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "LLFStopMotorMove:Enter\n");
+
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+ Asic_WaitUnitReady (chip);
+
+ DBG (DBG_ASIC, "LLFStopMotorMove:Exit\n");
+ return status;
+}
+#endif
+
+static STATUS
+LLFSetMotorTable (PAsic chip, LLF_SETMOTORTABLE * LLF_SetMotorTable)
+{
+ STATUS status = STATUS_GOOD;
+ LLF_RAMACCESS RamAccess;
+
+ DBG (DBG_ASIC, "LLFSetMotorTable:Enter\n");
+ if (LLF_SetMotorTable->MotorTablePtr != NULL)
+ {
+ RamAccess.ReadWrite = WRITE_RAM;
+ RamAccess.IsOnChipGamma = EXTERNAL_RAM;
+ RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns;
+
+ RamAccess.LoStartAddress = 0;
+ RamAccess.LoStartAddress |= LLF_SetMotorTable->MotorTableAddress;
+ RamAccess.LoStartAddress <<= TABLE_OFFSET_BASE;
+ RamAccess.LoStartAddress |= 0x3000;
+
+ RamAccess.HiStartAddress = 0;
+ RamAccess.HiStartAddress |= LLF_SetMotorTable->MotorTableAddress;
+ RamAccess.HiStartAddress >>= (16 - TABLE_OFFSET_BASE);
+
+ RamAccess.RwSize = 512 * 2 * 8; /* BYTE */
+ RamAccess.BufferPtr = (SANE_Byte *) LLF_SetMotorTable->MotorTablePtr;
+
+ LLFRamAccess (chip, &RamAccess);
+
+ /* tell scan chip the motor table address, unit is 2^14 words */
+ Mustek_SendData (chip, ES01_9D_MotorTableAddrA14_A21,
+ LLF_SetMotorTable->MotorTableAddress);
+ }
+
+ DBG (DBG_ASIC, "LLFSetMotorTable:Exit\n");
+ return status;
+}
+
+static STATUS
+LLFMotorMove (PAsic chip, LLF_MOTORMOVE * LLF_MotorMove)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned int motor_steps;
+ SANE_Byte temp_motor_action;
+ SANE_Byte temp_status;
+
+ DBG (DBG_ASIC, "LLFMotorMove:Enter\n");
+
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+ status = Asic_WaitUnitReady (chip);
+
+ DBG (DBG_ASIC, "Set start/end pixel\n");
+
+ Mustek_SendData (chip, ES01_B8_ChannelRedExpStartPixelLSB, LOBYTE (100));
+ Mustek_SendData (chip, ES01_B9_ChannelRedExpStartPixelMSB, HIBYTE (100));
+ Mustek_SendData (chip, ES01_BA_ChannelRedExpEndPixelLSB, LOBYTE (101));
+ Mustek_SendData (chip, ES01_BB_ChannelRedExpEndPixelMSB, HIBYTE (101));
+
+ Mustek_SendData (chip, ES01_BC_ChannelGreenExpStartPixelLSB, LOBYTE (100));
+ Mustek_SendData (chip, ES01_BD_ChannelGreenExpStartPixelMSB, HIBYTE (100));
+ Mustek_SendData (chip, ES01_BE_ChannelGreenExpEndPixelLSB, LOBYTE (101));
+ Mustek_SendData (chip, ES01_BF_ChannelGreenExpEndPixelMSB, HIBYTE (101));
+
+ Mustek_SendData (chip, ES01_C0_ChannelBlueExpStartPixelLSB, LOBYTE (100));
+ Mustek_SendData (chip, ES01_C1_ChannelBlueExpStartPixelMSB, HIBYTE (100));
+ Mustek_SendData (chip, ES01_C2_ChannelBlueExpEndPixelLSB, LOBYTE (101));
+ Mustek_SendData (chip, ES01_C3_ChannelBlueExpEndPixelMSB, HIBYTE (101));
+
+ /*set motor accelerate steps MAX 511 steps */
+ Mustek_SendData (chip, ES01_E0_MotorAccStep0_7,
+ LOBYTE (LLF_MotorMove->AccStep));
+ Mustek_SendData (chip, ES01_E1_MotorAccStep8_8,
+ HIBYTE (LLF_MotorMove->AccStep));
+ DBG (DBG_ASIC, "AccStep=%d\n", LLF_MotorMove->AccStep);
+
+ Mustek_SendData (chip, ES01_E2_MotorStepOfMaxSpeed0_7,
+ LOBYTE (LLF_MotorMove->FixMoveSteps));
+ Mustek_SendData (chip, ES01_E3_MotorStepOfMaxSpeed8_15,
+ HIBYTE (LLF_MotorMove->FixMoveSteps));
+ Mustek_SendData (chip, ES01_E4_MotorStepOfMaxSpeed16_19, 0);
+ DBG (DBG_ASIC, "FixMoveSteps=%d\n", LLF_MotorMove->FixMoveSteps);
+
+ /*set motor decelerate steps MAX 255 steps */
+ Mustek_SendData (chip, ES01_E5_MotorDecStep, LLF_MotorMove->DecStep);
+ DBG (DBG_ASIC, "DecStep=%d\n", LLF_MotorMove->DecStep);
+
+ /*set motor uniform speed only for uniform speed
+ //only used for UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE
+ //If you use acc mode, this two reg are not used. */
+ Mustek_SendData (chip, ES01_FD_MotorFixedspeedLSB,
+ LOBYTE (LLF_MotorMove->FixMoveSpeed));
+ Mustek_SendData (chip, ES01_FE_MotorFixedspeedMSB,
+ HIBYTE (LLF_MotorMove->FixMoveSpeed));
+ DBG (DBG_ASIC, "FixMoveSpeed=%d\n", LLF_MotorMove->FixMoveSpeed);
+
+ /*Set motor type */
+ Mustek_SendData (chip, ES01_A6_MotorOption, LLF_MotorMove->MotorSelect |
+ LLF_MotorMove->HomeSensorSelect | LLF_MotorMove->
+ MotorMoveUnit);
+
+ /*Set motor speed unit, for all motor mode,
+ //inclue uniform, acc, motor speed of scan */
+ Mustek_SendData (chip, ES01_F6_MorotControl1,
+ LLF_MotorMove->MotorSpeedUnit | LLF_MotorMove->
+ MotorSyncUnit);
+
+ /* below is setting action register */
+ if (LLF_MotorMove->ActionType == ACTION_TYPE_BACKTOHOME)
+ {
+ DBG (DBG_ASIC, "ACTION_TYPE_BACKTOHOME\n");
+
+ temp_motor_action = MOTOR_BACK_HOME_AFTER_SCAN_ENABLE;
+ motor_steps = 30000 * 2;
+ }
+ else
+ {
+ DBG (DBG_ASIC, "Forward or Backward\n");
+ temp_motor_action = MOTOR_MOVE_TO_FIRST_LINE_ENABLE;
+ motor_steps = LLF_MotorMove->FixMoveSteps;
+
+ if (LLF_MotorMove->ActionType == ACTION_TYPE_BACKWARD)
+ {
+ DBG (DBG_ASIC, "ACTION_TYPE_BACKWARD\n");
+ temp_motor_action =
+ temp_motor_action | INVERT_MOTOR_DIRECTION_ENABLE;
+ }
+ }
+
+ if (LLF_MotorMove->ActionType == ACTION_TYPE_TEST_MODE)
+ {
+ DBG (DBG_ASIC, "ACTION_TYPE_TEST_MODE\n");
+ temp_motor_action = temp_motor_action |
+ MOTOR_MOVE_TO_FIRST_LINE_ENABLE |
+ MOTOR_BACK_HOME_AFTER_SCAN_ENABLE | MOTOR_TEST_LOOP_ENABLE;
+ }
+
+ Mustek_SendData (chip, ES01_94_PowerSaveControl,
+ 0x27 | LLF_MotorMove->Lamp0PwmFreq | LLF_MotorMove->
+ Lamp1PwmFreq);
+
+ /* fix speed move steps */
+ Mustek_SendData (chip, ES01_E2_MotorStepOfMaxSpeed0_7,
+ LOBYTE (motor_steps));
+ Mustek_SendData (chip, ES01_E3_MotorStepOfMaxSpeed8_15,
+ HIBYTE (motor_steps));
+ Mustek_SendData (chip, ES01_E4_MotorStepOfMaxSpeed16_19,
+ (SANE_Byte) ((motor_steps & 0x00ff0000) >> 16));
+ DBG (DBG_ASIC, "motor_steps=%d\n", motor_steps);
+ DBG (DBG_ASIC, "LOBYTE(motor_steps)=%d\n", LOBYTE (motor_steps));
+ DBG (DBG_ASIC, "HIBYTE(motor_steps)=%d\n", HIBYTE (motor_steps));
+ DBG (DBG_ASIC, "(SANE_Byte)((motor_steps & 0x00ff0000) >> 16)=%d\n",
+ (SANE_Byte) ((motor_steps & 0x00ff0000) >> 16));
+
+ if (LLF_MotorMove->ActionMode == ACTION_MODE_UNIFORM_SPEED_MOVE)
+ {
+ temp_motor_action =
+ temp_motor_action | UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE;
+ }
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, SCAN_DISABLE |
+ SCAN_BACK_TRACKING_DISABLE | temp_motor_action);
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_ENABLE);
+
+ temp_status = 0;
+ if (LLF_MotorMove->WaitOrNoWait == 1)
+ {
+ if (LLF_MotorMove->ActionType == ACTION_TYPE_BACKTOHOME)
+ {
+ DBG (DBG_ASIC, "ACTION_TYPE_BACKTOHOME\n");
+ Asic_WaitCarriageHome (chip, FALSE);
+ }
+ else
+ {
+ Asic_WaitUnitReady (chip);
+ }
+ }
+
+ DBG (DBG_ASIC, "LLFMotorMove:Exit\n");
+ return status;
+}
+
+static STATUS
+SetMotorStepTable (PAsic chip, LLF_MOTORMOVE * MotorStepsTable, unsigned short wStartY,
+ unsigned int dwScanImageSteps, unsigned short wYResolution)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short wAccSteps = 511;
+ unsigned short wForwardSteps = 20;
+ SANE_Byte bDecSteps = 255;
+ unsigned short wMotorSycnPixelNumber = 0;
+ unsigned short wScanAccSteps = 511;
+ SANE_Byte bScanDecSteps = 255;
+ unsigned short wFixScanSteps = 20;
+ unsigned short wScanBackTrackingSteps = 40;
+ unsigned short wScanRestartSteps = 40;
+ unsigned short wScanBackHomeExtSteps = 100;
+ unsigned int dwTotalMotorSteps;
+
+ DBG (DBG_ASIC, "SetMotorStepTable:Enter\n");
+
+ dwTotalMotorSteps = dwScanImageSteps;
+
+ switch (wYResolution)
+ {
+ case 2400:
+ case 1200:
+ wScanAccSteps = 100;
+ bScanDecSteps = 10;
+ wScanBackTrackingSteps = 10;
+ wScanRestartSteps = 10;
+ break;
+ case 600:
+ case 300:
+ wScanAccSteps = 300;
+ bScanDecSteps = 40;
+ break;
+ case 150:
+ wScanAccSteps = 300;
+ bScanDecSteps = 40;
+ break;
+ case 100:
+ case 75:
+ case 50:
+ wScanAccSteps = 300;
+ bScanDecSteps = 40;
+ break;
+ }
+
+ if (wStartY < (wAccSteps + wForwardSteps + bDecSteps + wScanAccSteps)) /*not including T0,T1 steps */
+ {
+ wAccSteps = 1;
+ bDecSteps = 1;
+ wFixScanSteps = (wStartY - wScanAccSteps) > 0 ?
+ (wStartY - wScanAccSteps) : 0;
+ wForwardSteps = 0;
+
+ chip->isMotorGoToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_DISABLE;
+ }
+ else
+ {
+ wForwardSteps =
+ (wStartY - wAccSteps - (unsigned short) bDecSteps - wScanAccSteps -
+ wFixScanSteps) >
+ 0 ? (wStartY - wAccSteps - (unsigned short) bDecSteps - wScanAccSteps -
+ wFixScanSteps) : 0;
+
+ chip->isMotorGoToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_ENABLE;
+ }
+
+ dwTotalMotorSteps += wAccSteps;
+ dwTotalMotorSteps += wForwardSteps;
+ dwTotalMotorSteps += bDecSteps;
+ dwTotalMotorSteps += wScanAccSteps;
+ dwTotalMotorSteps += wFixScanSteps;
+ dwTotalMotorSteps += bScanDecSteps;
+ dwTotalMotorSteps += 2;
+
+
+ MotorStepsTable->AccStep = wAccSteps;
+ MotorStepsTable->DecStep = bDecSteps;
+ MotorStepsTable->wForwardSteps = wForwardSteps;
+ MotorStepsTable->wScanAccSteps = wScanAccSteps;
+ MotorStepsTable->bScanDecSteps = bScanDecSteps;
+ MotorStepsTable->wFixScanSteps = wFixScanSteps;
+ MotorStepsTable->MotorSyncUnit = (SANE_Byte) wMotorSycnPixelNumber;
+ MotorStepsTable->wScanBackHomeExtSteps = wScanBackHomeExtSteps;
+ MotorStepsTable->wScanRestartSteps = wScanRestartSteps;
+ MotorStepsTable->wScanBackTrackingSteps = wScanBackTrackingSteps;
+
+ /*state 1 */
+ Mustek_SendData (chip, ES01_E0_MotorAccStep0_7, LOBYTE (wAccSteps));
+ Mustek_SendData (chip, ES01_E1_MotorAccStep8_8, HIBYTE (wAccSteps));
+ /*state 2 */
+ Mustek_SendData (chip, ES01_E2_MotorStepOfMaxSpeed0_7,
+ LOBYTE (wForwardSteps));
+ Mustek_SendData (chip, ES01_E3_MotorStepOfMaxSpeed8_15,
+ HIBYTE (wForwardSteps));
+ Mustek_SendData (chip, ES01_E4_MotorStepOfMaxSpeed16_19, 0);
+ /*state 3 */
+ Mustek_SendData (chip, ES01_E5_MotorDecStep, bDecSteps);
+ /*state 4 */
+ Mustek_SendData (chip, ES01_AE_MotorSyncPixelNumberM16LSB,
+ LOBYTE (wMotorSycnPixelNumber));
+ Mustek_SendData (chip, ES01_AF_MotorSyncPixelNumberM16MSB,
+ HIBYTE (wMotorSycnPixelNumber));
+ /*state 5 */
+ Mustek_SendData (chip, ES01_EC_ScanAccStep0_7, LOBYTE (wScanAccSteps));
+ Mustek_SendData (chip, ES01_ED_ScanAccStep8_8, HIBYTE (wScanAccSteps));
+ /*state 6 */
+ Mustek_SendData (chip, ES01_EE_FixScanStepLSB, LOBYTE (wFixScanSteps));
+ Mustek_SendData (chip, ES01_8A_FixScanStepMSB, HIBYTE (wFixScanSteps));
+ /*state 8 */
+ Mustek_SendData (chip, ES01_EF_ScanDecStep, bScanDecSteps);
+ /*state 10 */
+ Mustek_SendData (chip, ES01_E6_ScanBackTrackingStepLSB,
+ LOBYTE (wScanBackTrackingSteps));
+ Mustek_SendData (chip, ES01_E7_ScanBackTrackingStepMSB,
+ HIBYTE (wScanBackTrackingSteps));
+ /*state 15 */
+ Mustek_SendData (chip, ES01_E8_ScanRestartStepLSB,
+ LOBYTE (wScanRestartSteps));
+ Mustek_SendData (chip, ES01_E9_ScanRestartStepMSB,
+ HIBYTE (wScanRestartSteps));
+ /*state 19 */
+ Mustek_SendData (chip, ES01_EA_ScanBackHomeExtStepLSB,
+ LOBYTE (wScanBackHomeExtSteps));
+ Mustek_SendData (chip, ES01_EB_ScanBackHomeExtStepMSB,
+ HIBYTE (wScanBackHomeExtSteps));
+
+ /*total motor steps */
+ Mustek_SendData (chip, ES01_F0_ScanImageStep0_7,
+ LOBYTE (dwTotalMotorSteps));
+ Mustek_SendData (chip, ES01_F1_ScanImageStep8_15,
+ HIBYTE (dwTotalMotorSteps));
+ Mustek_SendData (chip, ES01_F2_ScanImageStep16_19,
+ (SANE_Byte) ((dwTotalMotorSteps & 0x00ff0000) >> 16));
+
+ DBG (DBG_ASIC, "SetMotorStepTable:Exit\n");
+ return status;
+}
+
+static STATUS
+CalculateMotorTable (LLF_CALCULATEMOTORTABLE * lpCalculateMotorTable,
+ unsigned short wYResolution)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short i;
+ unsigned short wEndSpeed, wStartSpeed;
+ unsigned short wScanAccSteps;
+ SANE_Byte bScanDecSteps;
+ double PI = 3.1415926;
+ double x = PI / 2;
+ long double y;
+ unsigned short *lpMotorTable;
+
+ DBG (DBG_ASIC, "CalculateMotorTable:Enter\n");
+
+ wStartSpeed = lpCalculateMotorTable->StartSpeed;
+ wEndSpeed = lpCalculateMotorTable->EndSpeed;
+ wScanAccSteps = lpCalculateMotorTable->AccStepBeforeScan;
+ bScanDecSteps = lpCalculateMotorTable->DecStepAfterScan;
+ lpMotorTable = lpCalculateMotorTable->lpMotorTable;
+
+ /*Motor T0 & T6 Acc Table */
+ for (i = 0; i < 512; i++)
+ {
+ y = (6000 - 3500);
+ y *= (pow (0.09, (x * i) / 512) - pow (0.09, (x * 511) / 512));
+ y += 4500;
+ *((unsigned short *) lpMotorTable + i) = (unsigned short) y; /*T0 */
+ *((unsigned short *) lpMotorTable + i + 512 * 6) = (unsigned short) y; /*T6 */
+ }
+
+ /*Motor T1 & T7 Dec Table */
+ for (i = 0; i < 256; i++)
+ {
+ y = (6000 - 3500);
+ y *= pow (0.3, (x * i) / 256);
+ y = 6000 - y;
+ *((unsigned short *) lpMotorTable + i + 512) = (unsigned short) y; /*T1 */
+ *((unsigned short *) lpMotorTable + i + 512 * 7) = (unsigned short) y; /*T7 */
+ }
+
+ switch (wYResolution)
+ {
+ case 2400:
+ case 1200:
+ case 600:
+ case 300:
+ case 150:
+ case 100:
+ case 75:
+ case 50:
+ for (i = 0; i < wScanAccSteps; i++)
+ {
+ y = (wStartSpeed - wEndSpeed);
+ y *=
+ (pow (0.09, (x * i) / wScanAccSteps) -
+ pow (0.09, (x * (wScanAccSteps - 1)) / wScanAccSteps));
+ y += wEndSpeed;
+ *((unsigned short *) lpMotorTable + i + 512 * 2) = (unsigned short) y; /*T2 */
+ *((unsigned short *) lpMotorTable + i + 512 * 4) = (unsigned short) y; /*T4 */
+ }
+ for (i = wScanAccSteps; i < 512; i++)
+ {
+ *((unsigned short *) lpMotorTable + i + 512 * 2) = (unsigned short) wEndSpeed; /*T2 */
+ *((unsigned short *) lpMotorTable + i + 512 * 4) = (unsigned short) wEndSpeed; /*T4 */
+ }
+
+
+ for (i = 0; i < (unsigned short) bScanDecSteps; i++)
+ {
+ y = (wStartSpeed - wEndSpeed);
+ y *= pow (0.3, (x * i) / bScanDecSteps);
+ y = wStartSpeed - y;
+ *((unsigned short *) lpMotorTable + i + 512 * 3) = (unsigned short) (y); /*T3 */
+ *((unsigned short *) lpMotorTable + i + 512 * 5) = (unsigned short) (y); /*T5 */
+ }
+ for (i = bScanDecSteps; i < 256; i++)
+ {
+ *((unsigned short *) lpMotorTable + i + 512 * 3) = (unsigned short) wStartSpeed; /*T3 */
+ *((unsigned short *) lpMotorTable + i + 512 * 5) = (unsigned short) wStartSpeed; /*T5 */
+ }
+ break;
+ }
+
+ DBG (DBG_ASIC, "CalculateMotorTable:Exit\n");
+ return status;
+}
+
+static STATUS
+LLFCalculateMotorTable (LLF_CALCULATEMOTORTABLE * LLF_CalculateMotorTable)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short i;
+ double PI = 3.1415926535;
+ double x;
+
+ DBG (DBG_ASIC, "LLF_CALCULATEMOTORTABLE:Enter\n");
+
+ x = PI / 2;
+
+ for (i = 0; i < 512; i++)
+ {
+ /* befor scan acc table */
+ *(LLF_CalculateMotorTable->lpMotorTable + i) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.09,
+ (x * i) / 512) +
+ LLF_CalculateMotorTable->EndSpeed);
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 2) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.09,
+ (x * i) / 512) +
+ LLF_CalculateMotorTable->EndSpeed);
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 4) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.09,
+ (x * i) / 512) +
+ LLF_CalculateMotorTable->EndSpeed);
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 6) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.09,
+ (x * i) / 512) +
+ LLF_CalculateMotorTable->EndSpeed);
+ }
+
+ for (i = 0; i < 255; i++)
+ {
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512) =
+ (unsigned short) (LLF_CalculateMotorTable->StartSpeed -
+ (LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.3,
+ (x * i) / 256));
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 3) =
+ (unsigned short) (LLF_CalculateMotorTable->StartSpeed -
+ (LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.3,
+ (x * i) / 256));
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 5) =
+ (unsigned short) (LLF_CalculateMotorTable->StartSpeed -
+ (LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.3,
+ (x * i) / 256));
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 7) =
+ (unsigned short) (LLF_CalculateMotorTable->StartSpeed -
+ (LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.3,
+ (x * i) / 256));
+ }
+
+ for (i = 0; i < 512; i++)
+ { /* back acc table */
+ *(LLF_CalculateMotorTable->lpMotorTable + i) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.09,
+ (x * i) / 512) +
+ LLF_CalculateMotorTable->EndSpeed);
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 6) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * pow (0.09,
+ (x * i) / 512) +
+ LLF_CalculateMotorTable->EndSpeed);
+ }
+
+ if (LLF_CalculateMotorTable->AccStepBeforeScan == 0)
+ {
+ }
+ else
+ {
+ for (i = 0; i < LLF_CalculateMotorTable->AccStepBeforeScan; i++)
+ {
+ *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 2) =
+ (unsigned short) ((LLF_CalculateMotorTable->StartSpeed -
+ LLF_CalculateMotorTable->EndSpeed) * (pow (0.09,
+ (x * i) /
+ LLF_CalculateMotorTable->
+ AccStepBeforeScan)
+ - pow (0.09,
+ (x *
+ (LLF_CalculateMotorTable->
+ AccStepBeforeScan
+ -
+ 1)) /
+ LLF_CalculateMotorTable->
+ AccStepBeforeScan))
+ + LLF_CalculateMotorTable->EndSpeed);
+ }
+ }
+
+ DBG (DBG_ASIC, "LLF_CALCULATEMOTORTABLE:Exit\n");
+ return status;
+}
+
+
+static STATUS
+SetMotorCurrent (PAsic chip, unsigned short dwMotorSpeed,
+ LLF_MOTOR_CURRENT_AND_PHASE * CurrentPhase)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "SetMotorCurrent:Enter\n");
+
+ chip = chip;
+
+ if (dwMotorSpeed < 2000)
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 255;
+ CurrentPhase->MotorCurrentTableB[0] = 255;
+ }
+ else if (dwMotorSpeed < 3500)
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 200;
+ CurrentPhase->MotorCurrentTableB[0] = 200;
+ }
+ else if (dwMotorSpeed < 5000)
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 160;
+ CurrentPhase->MotorCurrentTableB[0] = 160;
+ }
+ else if (dwMotorSpeed < 10000)
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 70;
+ CurrentPhase->MotorCurrentTableB[0] = 70;
+ }
+ else if (dwMotorSpeed < 17000)
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 60;
+ CurrentPhase->MotorCurrentTableB[0] = 60;
+ }
+ else if (dwMotorSpeed < 25000)
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 50;
+ CurrentPhase->MotorCurrentTableB[0] = 50;
+ }
+ else
+ {
+ CurrentPhase->MotorCurrentTableA[0] = 50;
+ CurrentPhase->MotorCurrentTableB[0] = 50;
+ }
+
+ DBG (DBG_ASIC, "SetMotorCurrent:Exit\n");
+ return status;
+}
+
+
+static STATUS
+MotorBackHome (PAsic chip, SANE_Byte WaitOrNoWait)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short BackHomeMotorTable[512 * 8];
+ LLF_CALCULATEMOTORTABLE CalMotorTable;
+ LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase;
+ LLF_SETMOTORTABLE LLF_SetMotorTable;
+ LLF_MOTORMOVE MotorMove;
+
+ DBG (DBG_ASIC, "MotorBackHome:Enter\n");
+
+ CalMotorTable.StartSpeed = 5000;
+ CalMotorTable.EndSpeed = 1200;
+ CalMotorTable.AccStepBeforeScan = 511;
+ CalMotorTable.DecStepAfterScan = 255;
+ CalMotorTable.lpMotorTable = BackHomeMotorTable;
+ LLFCalculateMotorTable (&CalMotorTable);
+
+
+ CurrentPhase.MotorCurrentTableA[0] = 220;
+ CurrentPhase.MotorCurrentTableB[0] = 220;
+ CurrentPhase.MoveType = _4_TABLE_SPACE_FOR_FULL_STEP;
+ LLFSetMotorCurrentAndPhase (chip, &CurrentPhase);
+
+ LLF_SetMotorTable.MotorTableAddress = 0;
+ LLF_SetMotorTable.MotorTablePtr = BackHomeMotorTable;
+ LLFSetMotorTable (chip, &LLF_SetMotorTable);
+
+ MotorMove.MotorSelect = MOTOR_0_ENABLE | MOTOR_1_DISABLE;
+ MotorMove.MotorMoveUnit = ES03_TABLE_DEFINE;
+ MotorMove.MotorSpeedUnit = SPEED_UNIT_1_PIXEL_TIME;
+ MotorMove.MotorSyncUnit = MOTOR_SYNC_UNIT_1_PIXEL_TIME;
+ MotorMove.HomeSensorSelect = HOME_SENSOR_0_ENABLE;
+ MotorMove.ActionMode = ACTION_MODE_ACCDEC_MOVE;
+ MotorMove.ActionType = ACTION_TYPE_BACKTOHOME;
+
+ MotorMove.AccStep = 511;
+ MotorMove.DecStep = 255;
+ MotorMove.FixMoveSteps = 0;
+ MotorMove.FixMoveSpeed = 3000;
+ MotorMove.WaitOrNoWait = WaitOrNoWait;
+ LLFMotorMove (chip, &MotorMove);
+
+ DBG (DBG_ASIC, "MotorBackHome:Exit\n");
+ return status;
+}
+
+
+static STATUS
+LLFSetRamAddress (PAsic chip, unsigned int dwStartAddr, unsigned int dwEndAddr,
+ SANE_Byte byAccessTarget)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte * pStartAddr = (SANE_Byte *) & dwStartAddr;
+ SANE_Byte * pEndAddr = (SANE_Byte *) & dwEndAddr;
+
+ DBG (DBG_ASIC, "LLFSetRamAddress:Enter\n");
+
+ /*Set start address. */
+ Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, *(pStartAddr));
+ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, *(pStartAddr + 1));
+ if (byAccessTarget == ACCESS_DRAM)
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21,
+ *(pStartAddr + 2) | ACCESS_DRAM);
+ else
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21,
+ *(pStartAddr + 2) | ACCESS_GAMMA_RAM);
+
+ /*Set end address. */
+ Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, *(pEndAddr));
+ Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, *(pEndAddr + 1));
+ Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, *(pEndAddr + 2));
+
+ Mustek_ClearFIFO (chip);
+
+ DBG (DBG_ASIC, "LLFSetRamAddress:Exit\n");
+ return status;
+}
+
+
+
+/* ---------------------- medium level asic functions ---------------------- */
+
+static STATUS
+InitTiming (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "InitTiming:Enter\n");
+
+ chip->Timing.AFE_ADCCLK_Timing = 1010580480;
+ chip->Timing.AFE_ADCVS_Timing = 12582912;
+ chip->Timing.AFE_ADCRS_Timing = 3072;
+ chip->Timing.AFE_ChannelA_LatchPos = 3080;
+ chip->Timing.AFE_ChannelB_LatchPos = 3602;
+ chip->Timing.AFE_ChannelC_LatchPos = 5634;
+ chip->Timing.AFE_ChannelD_LatchPos = 1546;
+ chip->Timing.AFE_Secondary_FF_LatchPos = 12;
+
+ /* Sensor */
+ chip->Timing.CCD_DummyCycleTiming = 0;
+ chip->Timing.PHTG_PluseWidth = 12;
+ chip->Timing.PHTG_WaitWidth = 1;
+ chip->Timing.PHTG_TimingAdj = 1;
+ chip->Timing.PHTG_TimingSetup = 0;
+ chip->Timing.ChannelR_StartPixel = 100;
+ chip->Timing.ChannelR_EndPixel = 200;
+ chip->Timing.ChannelG_StartPixel = 100;
+ chip->Timing.ChannelG_EndPixel = 200;
+ chip->Timing.ChannelB_StartPixel = 100;
+ chip->Timing.ChannelB_EndPixel = 200;
+
+ /*1200dpi Timing */
+ chip->Timing.CCD_PH2_Timing_1200 = 1048320;
+ chip->Timing.CCD_PHRS_Timing_1200 = 983040;
+ chip->Timing.CCD_PHCP_Timing_1200 = 61440;
+ chip->Timing.CCD_PH1_Timing_1200 = 4293918720u;
+ chip->Timing.DE_CCD_SETUP_REGISTER_1200 = 32;
+ chip->Timing.wCCDPixelNumber_1200 = 11250;
+
+ /*600dpi Timing */
+ chip->Timing.CCD_PH2_Timing_600 = 1048320;
+ chip->Timing.CCD_PHRS_Timing_600 = 983040;
+ chip->Timing.CCD_PHCP_Timing_600 = 61440;
+ chip->Timing.CCD_PH1_Timing_600 = 4293918720u;
+ chip->Timing.DE_CCD_SETUP_REGISTER_600 = 0;
+ chip->Timing.wCCDPixelNumber_600 = 7500;
+
+ DBG (DBG_ASIC, "InitTiming:Exit\n");
+ return status;
+}
+
+static STATUS
+OpenScanChip (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte x[4];
+
+ DBG (DBG_ASIC, "OpenScanChip:Enter\n");
+
+ x[0] = 0x64;
+ x[1] = 0x64;
+ x[2] = 0x64;
+ x[3] = 0x64;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+ if (status != STATUS_GOOD)
+ return status;
+
+ x[0] = 0x65;
+ x[1] = 0x65;
+ x[2] = 0x65;
+ x[3] = 0x65;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+ if (status != STATUS_GOOD)
+ return status;
+
+ x[0] = 0x44;
+ x[1] = 0x44;
+ x[2] = 0x44;
+ x[3] = 0x44;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+ if (status != STATUS_GOOD)
+ return status;
+
+ x[0] = 0x45;
+ x[1] = 0x45;
+ x[2] = 0x45;
+ x[3] = 0x45;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+
+ DBG (DBG_ASIC, "OpenScanChip: Exit\n");
+ return status;
+}
+
+
+static STATUS
+CloseScanChip (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte x[4];
+
+ DBG (DBG_ASIC, "CloseScanChip:Enter\n");
+
+ x[0] = 0x64;
+ x[1] = 0x64;
+ x[2] = 0x64;
+ x[3] = 0x64;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+ if (status != STATUS_GOOD)
+ return status;
+
+ x[0] = 0x65;
+ x[1] = 0x65;
+ x[2] = 0x65;
+ x[3] = 0x65;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+ if (status != STATUS_GOOD)
+ return status;
+
+ x[0] = 0x16;
+ x[1] = 0x16;
+ x[2] = 0x16;
+ x[3] = 0x16;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+ if (status != STATUS_GOOD)
+ return status;
+
+ x[0] = 0x17;
+ x[1] = 0x17;
+ x[2] = 0x17;
+ x[3] = 0x17;
+ status = WriteIOControl (chip, 0x90, 0, 4, x);
+
+ DBG (DBG_ASIC, "CloseScanChip: Exit\n");
+ return status;
+}
+
+
+static STATUS
+SafeInitialChip (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+
+ DBG (DBG_ASIC, "SafeInitialChip:Enter\n");
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, 0);
+ Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle,
+ CLOSE_ALL_CLOCK_DISABLE);
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+ status = Asic_WaitUnitReady (chip);
+
+ DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip);
+ if (chip->isFirstOpenChip)
+ {
+ DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip);
+ status = DRAM_Test (chip);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ASIC, "DRAM_Test: Error\n");
+ return status;
+ }
+ chip->isFirstOpenChip = FALSE;
+ }
+
+ DBG (DBG_ASIC, "SafeInitialChip: exit\n");
+ return status;
+}
+
+
+static STATUS
+DRAM_Test (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned char *temps;
+ unsigned int i;
+
+ DBG (DBG_ASIC, "DRAM_Test:Enter\n");
+
+ temps = (unsigned char *) malloc (64);
+
+ for (i = 0; i < 64; i++)
+ {
+ *(temps + i) = i;
+ }
+
+ /*set start address */
+ status = Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, 0x00);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, 0x00);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status =
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, 0x00 | ACCESS_DRAM);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ Mustek_SendData (chip, ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL,
+ SDRAMCLK_DELAY_12_ns);
+ status = Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, 0xff);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, 0xff);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, 0xff);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_DMAWrite (chip, 64, (SANE_Byte *) (temps));
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ASIC, "Mustek_DMAWrite error\n");
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, 0x00);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, 0x00);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status =
+ Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, 0x00 | ACCESS_DRAM);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ /*set end address */
+ status = Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, 0xff);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, 0xff);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ status = Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, 0xff);
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ memset (temps, 0, 64);
+
+ status = Mustek_DMARead (chip, 64, (SANE_Byte *) (temps));
+ if (status != STATUS_GOOD)
+ {
+ free (temps);
+ return status;
+ }
+
+ for (i = 0; i < 60; i += 10)
+ {
+ DBG (DBG_ASIC, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ *(temps + i), *(temps + i + 1), *(temps + i + 2), *(temps + i + 3),
+ *(temps + i + 4), *(temps + i + 5), *(temps + i + 6),
+ *(temps + i + 7), *(temps + i + 8), *(temps + i + 9));
+ }
+
+ for (i = 0; i < 64; i++)
+ {
+ if (*(temps + i) != i)
+ {
+ DBG (DBG_ERR, "DRAM Test error...(No.=%d)\n", i + 1);
+ return STATUS_IO_ERROR;
+ }
+ }
+
+ free (temps);
+
+ DBG (DBG_ASIC, "DRAM_Text: Exit\n");
+ return status;
+}
+
+#if SANE_UNUSED
+static STATUS
+SetPowerSave (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "SetPowerSave:Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+ OpenScanChip (chip);
+
+ if (chip->firmwarestate > FS_OPENED)
+ Asic_ScanStop (chip);
+
+ Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x10);
+
+ chip->firmwarestate = FS_OPENED;
+ DBG (DBG_ASIC, "SetPowerSave:Exit\n");
+ return status;
+}
+#endif
+
+static STATUS
+SetLineTimeAndExposure (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "SetLineTimeAndExposure:Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+ OpenScanChip (chip);
+
+ Mustek_SendData (chip, ES01_C4_MultiTGTimesRed, 0);
+ Mustek_SendData (chip, ES01_C5_MultiTGTimesGreen, 0);
+ Mustek_SendData (chip, ES01_C6_MultiTGTimesBlue, 0);
+
+ Mustek_SendData (chip, ES01_C7_MultiTGDummyPixelNumberLSB, 0);
+ Mustek_SendData (chip, ES01_C8_MultiTGDummyPixelNumberMSB, 0);
+
+ Mustek_SendData (chip, ES01_C9_CCDDummyPixelNumberLSB, 0);
+ Mustek_SendData (chip, ES01_CA_CCDDummyPixelNumberMSB, 0);
+
+ Mustek_SendData (chip, ES01_CB_CCDDummyCycleNumber, 0);
+
+
+ chip->firmwarestate = FS_OPENED;
+
+ DBG (DBG_ASIC, "SetLineTimeAndExposure:Exit\n");
+ return status;
+}
+
+
+
+
+
+static STATUS
+CCDTiming (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned int dwPH1, dwPH2, dwPHRS, dwPHCP;
+
+ DBG (DBG_ASIC, "CCDTiming:Enter\n");
+ DBG (DBG_ASIC, "Dpi=%d\n", chip->Scan.Dpi);
+
+ if (chip->firmwarestate < FS_OPENED)
+ OpenScanChip (chip);
+
+ Mustek_SendData (chip, ES01_82_AFE_ADCCLK_TIMING_ADJ_BYTE0,
+ (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing));
+ Mustek_SendData (chip, ES01_83_AFE_ADCCLK_TIMING_ADJ_BYTE1,
+ (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing >> 8));
+ Mustek_SendData (chip, ES01_84_AFE_ADCCLK_TIMING_ADJ_BYTE2,
+ (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing >> 16));
+ Mustek_SendData (chip, ES01_85_AFE_ADCCLK_TIMING_ADJ_BYTE3,
+ (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing >> 24));
+
+ Mustek_SendData (chip, ES01_1F0_AFERS_TIMING_ADJ_B0,
+ (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing));
+ Mustek_SendData (chip, ES01_1F1_AFERS_TIMING_ADJ_B1,
+ (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing >> 8));
+ Mustek_SendData (chip, ES01_1F2_AFERS_TIMING_ADJ_B2,
+ (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing >> 16));
+ Mustek_SendData (chip, ES01_1F3_AFERS_TIMING_ADJ_B3,
+ (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing >> 24));
+
+ Mustek_SendData (chip, ES01_1EC_AFEVS_TIMING_ADJ_B0,
+ (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing));
+ Mustek_SendData (chip, ES01_1ED_AFEVS_TIMING_ADJ_B1,
+ (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing >> 8));
+ Mustek_SendData (chip, ES01_1EE_AFEVS_TIMING_ADJ_B2,
+ (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing >> 16));
+ Mustek_SendData (chip, ES01_1EF_AFEVS_TIMING_ADJ_B3,
+ (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing >> 24));
+
+ Mustek_SendData (chip, ES01_160_CHANNEL_A_LATCH_POSITION_HB,
+ HIBYTE (chip->Timing.AFE_ChannelA_LatchPos));
+ Mustek_SendData (chip, ES01_161_CHANNEL_A_LATCH_POSITION_LB,
+ LOBYTE (chip->Timing.AFE_ChannelA_LatchPos));
+
+ Mustek_SendData (chip, ES01_162_CHANNEL_B_LATCH_POSITION_HB,
+ HIBYTE (chip->Timing.AFE_ChannelB_LatchPos));
+ Mustek_SendData (chip, ES01_163_CHANNEL_B_LATCH_POSITION_LB,
+ LOBYTE (chip->Timing.AFE_ChannelB_LatchPos));
+
+ Mustek_SendData (chip, ES01_164_CHANNEL_C_LATCH_POSITION_HB,
+ HIBYTE (chip->Timing.AFE_ChannelC_LatchPos));
+ Mustek_SendData (chip, ES01_165_CHANNEL_C_LATCH_POSITION_LB,
+ LOBYTE (chip->Timing.AFE_ChannelC_LatchPos));
+
+ Mustek_SendData (chip, ES01_166_CHANNEL_D_LATCH_POSITION_HB,
+ HIBYTE (chip->Timing.AFE_ChannelD_LatchPos));
+ Mustek_SendData (chip, ES01_167_CHANNEL_D_LATCH_POSITION_LB,
+ LOBYTE (chip->Timing.AFE_ChannelD_LatchPos));
+
+ Mustek_SendData (chip, ES01_168_SECONDARY_FF_LATCH_POSITION,
+ chip->Timing.AFE_Secondary_FF_LatchPos);
+
+ Mustek_SendData (chip, ES01_1D0_DUMMY_CYCLE_TIMING_B0,
+ (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming));
+ Mustek_SendData (chip, ES01_1D1_DUMMY_CYCLE_TIMING_B1,
+ (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming >> 8));
+ Mustek_SendData (chip, ES01_1D2_DUMMY_CYCLE_TIMING_B2,
+ (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming >> 16));
+ Mustek_SendData (chip, ES01_1D3_DUMMY_CYCLE_TIMING_B3,
+ (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming >> 24));
+
+ if (chip->Scan.Dpi >= 1200)
+ {
+ dwPH1 = chip->Timing.CCD_PH1_Timing_1200;
+ dwPH2 = chip->Timing.CCD_PH2_Timing_1200;
+ dwPHRS = chip->Timing.CCD_PHRS_Timing_1200;
+ dwPHCP = chip->Timing.CCD_PHCP_Timing_1200;
+ }
+ else
+ {
+ dwPH1 = chip->Timing.CCD_PH1_Timing_600;
+ dwPH2 = chip->Timing.CCD_PH2_Timing_600;
+ dwPHRS = chip->Timing.CCD_PHRS_Timing_600;
+ dwPHCP = chip->Timing.CCD_PHCP_Timing_600;
+ }
+
+ Mustek_SendData (chip, ES01_1D4_PH1_TIMING_ADJ_B0, (SANE_Byte) (dwPH1));
+ Mustek_SendData (chip, ES01_1D5_PH1_TIMING_ADJ_B1, (SANE_Byte) (dwPH1 >> 8));
+ Mustek_SendData (chip, ES01_1D6_PH1_TIMING_ADJ_B2, (SANE_Byte) (dwPH1 >> 16));
+ Mustek_SendData (chip, ES01_1D7_PH1_TIMING_ADJ_B3, (SANE_Byte) (dwPH1 >> 24));
+
+ /* set ccd ph1 ph2 rs cp */
+ Mustek_SendData (chip, ES01_D0_PH1_0, 0);
+ Mustek_SendData (chip, ES01_D1_PH2_0, 4);
+ Mustek_SendData (chip, ES01_D4_PHRS_0, 0);
+ Mustek_SendData (chip, ES01_D5_PHCP_0, 0);
+
+ Mustek_SendData (chip, ES01_1D8_PH2_TIMING_ADJ_B0, (SANE_Byte) (dwPH2));
+ Mustek_SendData (chip, ES01_1D9_PH2_TIMING_ADJ_B1, (SANE_Byte) (dwPH2 >> 8));
+ Mustek_SendData (chip, ES01_1DA_PH2_TIMING_ADJ_B2, (SANE_Byte) (dwPH2 >> 16));
+ Mustek_SendData (chip, ES01_1DB_PH2_TIMING_ADJ_B3, (SANE_Byte) (dwPH2 >> 24));
+
+ Mustek_SendData (chip, ES01_1E4_PHRS_TIMING_ADJ_B0, (SANE_Byte) (dwPHRS));
+ Mustek_SendData (chip, ES01_1E5_PHRS_TIMING_ADJ_B1, (SANE_Byte) (dwPHRS >> 8));
+ Mustek_SendData (chip, ES01_1E6_PHRS_TIMING_ADJ_B2, (SANE_Byte) (dwPHRS >> 16));
+ Mustek_SendData (chip, ES01_1E7_PHRS_TIMING_ADJ_B3, (SANE_Byte) (dwPHRS >> 24));
+
+ Mustek_SendData (chip, ES01_1E8_PHCP_TIMING_ADJ_B0, (SANE_Byte) (dwPHCP));
+ Mustek_SendData (chip, ES01_1E9_PHCP_TIMING_ADJ_B1, (SANE_Byte) (dwPHCP >> 8));
+ Mustek_SendData (chip, ES01_1EA_PHCP_TIMING_ADJ_B2, (SANE_Byte) (dwPHCP >> 16));
+ Mustek_SendData (chip, ES01_1EB_PHCP_TIMING_ADJ_B3, (SANE_Byte) (dwPHCP >> 24));
+
+ chip->firmwarestate = FS_OPENED;
+ DBG (DBG_ASIC, "CCDTiming:Exit\n");
+ return status;
+}
+
+static STATUS
+IsCarriageHome (PAsic chip, SANE_Bool * LampHome, SANE_Bool * TAHome)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte temp;
+
+ DBG (DBG_ASIC, "IsCarriageHome:Enter\n");
+
+ status = GetChipStatus (chip, 0, &temp);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ASIC, "IsCarriageHome:Error!\n");
+ return status;
+ }
+
+ if ((temp & SENSOR0_DETECTED) == SENSOR0_DETECTED)
+ *LampHome = TRUE;
+ else
+ {
+ *LampHome = FALSE;
+ }
+
+ *TAHome = TRUE;
+
+ DBG (DBG_ASIC, "LampHome=%d\n", *LampHome);
+
+ DBG (DBG_ASIC, "IsCarriageHome:Exit\n");
+ return status;
+}
+
+
+static STATUS
+GetChipStatus (PAsic chip, SANE_Byte Selector, SANE_Byte * ChipStatus)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "GetChipStatus:Enter\n");
+
+ status = Mustek_SendData (chip, ES01_8B_Status, Selector);
+ if (status != STATUS_GOOD)
+ return status;
+
+ status = Mustek_WriteAddressLineForRegister (chip, ES01_8B_Status);
+ if (status != STATUS_GOOD)
+ return status;
+
+ *ChipStatus = ES01_8B_Status;
+ status = Mustek_ReceiveData (chip, ChipStatus);
+ if (status != STATUS_GOOD)
+ return status;
+
+ DBG (DBG_ASIC, "GetChipStatus:Exit\n");
+ return status;
+}
+
+static STATUS
+SetAFEGainOffset (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ int i = 0;
+
+ DBG (DBG_ASIC, "SetAFEGainOffset:Enter\n");
+
+ if (chip->AD.DirectionR)
+ { /* Negative */
+ Mustek_SendData (chip, ES01_60_AFE_AUTO_GAIN_OFFSET_RED_LB,
+ (chip->AD.GainR << 1) | 0x01);
+ Mustek_SendData (chip, ES01_61_AFE_AUTO_GAIN_OFFSET_RED_HB,
+ chip->AD.OffsetR);
+ }
+ else
+ { /* Postive */
+ Mustek_SendData (chip, ES01_60_AFE_AUTO_GAIN_OFFSET_RED_LB,
+ (chip->AD.GainR << 1));
+ Mustek_SendData (chip, ES01_61_AFE_AUTO_GAIN_OFFSET_RED_HB,
+ chip->AD.OffsetR);
+ }
+
+ if (chip->AD.DirectionG)
+ {
+ Mustek_SendData (chip, ES01_62_AFE_AUTO_GAIN_OFFSET_GREEN_LB,
+ (chip->AD.GainG << 1) | 0x01);
+ Mustek_SendData (chip, ES01_63_AFE_AUTO_GAIN_OFFSET_GREEN_HB,
+ chip->AD.OffsetG);
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_62_AFE_AUTO_GAIN_OFFSET_GREEN_LB,
+ (chip->AD.GainG << 1));
+
+ Mustek_SendData (chip, ES01_63_AFE_AUTO_GAIN_OFFSET_GREEN_HB,
+ chip->AD.OffsetG);
+ }
+
+ if (chip->AD.DirectionB)
+ {
+ Mustek_SendData (chip, ES01_64_AFE_AUTO_GAIN_OFFSET_BLUE_LB,
+ (chip->AD.GainB << 1) | 0x01);
+ Mustek_SendData (chip, ES01_65_AFE_AUTO_GAIN_OFFSET_BLUE_HB,
+ chip->AD.OffsetB);
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_64_AFE_AUTO_GAIN_OFFSET_BLUE_LB,
+ (chip->AD.GainB << 1));
+ Mustek_SendData (chip, ES01_65_AFE_AUTO_GAIN_OFFSET_BLUE_HB,
+ chip->AD.OffsetB);
+ }
+
+
+ Mustek_SendData (chip, ES01_2A0_AFE_GAIN_OFFSET_CONTROL, 0x01);
+
+ for (i = 0; i < 4; i++)
+ {
+
+ if (chip->AD.DirectionR == 0)
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN,
+ (SANE_Byte) (chip->AD.GainR << 1));
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET,
+ (SANE_Byte) (chip->AD.OffsetR));
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN,
+ (SANE_Byte) (chip->AD.GainR << 1) | 0x01);
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET,
+ (SANE_Byte) (chip->AD.OffsetR));
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (chip->AD.DirectionG == 0)
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN,
+ (SANE_Byte) (chip->AD.GainG << 1));
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET,
+ (SANE_Byte) (chip->AD.OffsetG));
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN,
+ (SANE_Byte) (chip->AD.GainG << 1) | 0x01);
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET,
+ (SANE_Byte) (chip->AD.OffsetG));
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (chip->AD.DirectionB == 0)
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN,
+ (SANE_Byte) (chip->AD.GainB << 1));
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET,
+ (SANE_Byte) (chip->AD.OffsetB));
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN,
+ (SANE_Byte) (chip->AD.GainB << 1) | 0x01);
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET,
+ (SANE_Byte) (chip->AD.OffsetB));
+ }
+ }
+
+ for (i = 0; i < 36; i++)
+ {
+ Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, 0);
+ Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, 0);
+ }
+
+ Mustek_SendData (chip, ES01_2A0_AFE_GAIN_OFFSET_CONTROL, 0x00);
+
+ /* Set to AFE */
+ Mustek_SendData (chip, ES01_04_ADAFEPGACH1, chip->AD.GainR);
+ Mustek_SendData (chip, ES01_06_ADAFEPGACH2, chip->AD.GainG);
+ Mustek_SendData (chip, ES01_08_ADAFEPGACH3, chip->AD.GainB);
+
+ if (chip->AD.DirectionR)
+ Mustek_SendData (chip, ES01_0B_AD9826OffsetRedN, chip->AD.OffsetR);
+ else
+ Mustek_SendData (chip, ES01_0A_AD9826OffsetRedP, chip->AD.OffsetR);
+
+ if (chip->AD.DirectionG)
+ Mustek_SendData (chip, ES01_0D_AD9826OffsetGreenN, chip->AD.OffsetG);
+ else
+ Mustek_SendData (chip, ES01_0C_AD9826OffsetGreenP, chip->AD.OffsetG);
+
+ if (chip->AD.DirectionB)
+ Mustek_SendData (chip, ES01_0F_AD9826OffsetBlueN, chip->AD.OffsetB);
+ else
+ Mustek_SendData (chip, ES01_0E_AD9826OffsetBlueP, chip->AD.OffsetB);
+
+
+ LLFSetRamAddress (chip, 0x0, PackAreaStartAddress - (512 * 8 - 1),
+ ACCESS_DRAM);
+
+ Mustek_SendData (chip, ES01_F3_ActionOption,
+ MOTOR_MOVE_TO_FIRST_LINE_DISABLE |
+ MOTOR_BACK_HOME_AFTER_SCAN_DISABLE |
+ SCAN_ENABLE |
+ SCAN_BACK_TRACKING_DISABLE |
+ INVERT_MOTOR_DIRECTION_DISABLE |
+ UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE |
+ ES01_STATIC_SCAN_DISABLE | MOTOR_TEST_LOOP_DISABLE);
+
+ Mustek_SendData (chip, ES01_9A_AFEControl,
+ AD9826_AFE | AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE);
+
+ Mustek_SendData (chip, ES01_00_ADAFEConfiguration, 0x70);
+ Mustek_SendData (chip, ES01_02_ADAFEMuxConfig, 0x80);
+
+ DBG (DBG_ASIC, "SetAFEGainOffset:Exit\n");
+ return status;
+}
+
+static STATUS
+SetLEDTime (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "SetLEDTime:Enter\n");
+
+ Mustek_SendData (chip, ES01_B8_ChannelRedExpStartPixelLSB,
+ LOBYTE (chip->Timing.ChannelR_StartPixel));
+ Mustek_SendData (chip, ES01_B9_ChannelRedExpStartPixelMSB,
+ HIBYTE (chip->Timing.ChannelR_StartPixel));
+ Mustek_SendData (chip, ES01_BA_ChannelRedExpEndPixelLSB,
+ LOBYTE (chip->Timing.ChannelR_EndPixel));
+ Mustek_SendData (chip, ES01_BB_ChannelRedExpEndPixelMSB,
+ HIBYTE (chip->Timing.ChannelR_EndPixel));
+
+ Mustek_SendData (chip, ES01_BC_ChannelGreenExpStartPixelLSB,
+ LOBYTE (chip->Timing.ChannelG_StartPixel));
+ Mustek_SendData (chip, ES01_BD_ChannelGreenExpStartPixelMSB,
+ HIBYTE (chip->Timing.ChannelG_StartPixel));
+ Mustek_SendData (chip, ES01_BE_ChannelGreenExpEndPixelLSB,
+ LOBYTE (chip->Timing.ChannelG_EndPixel));
+ Mustek_SendData (chip, ES01_BF_ChannelGreenExpEndPixelMSB,
+ HIBYTE (chip->Timing.ChannelG_EndPixel));
+
+ Mustek_SendData (chip, ES01_C0_ChannelBlueExpStartPixelLSB,
+ LOBYTE (chip->Timing.ChannelB_StartPixel));
+ Mustek_SendData (chip, ES01_C1_ChannelBlueExpStartPixelMSB,
+ HIBYTE (chip->Timing.ChannelB_StartPixel));
+ Mustek_SendData (chip, ES01_C2_ChannelBlueExpEndPixelLSB,
+ LOBYTE (chip->Timing.ChannelB_EndPixel));
+ Mustek_SendData (chip, ES01_C3_ChannelBlueExpEndPixelMSB,
+ HIBYTE (chip->Timing.ChannelB_EndPixel));
+
+ DBG (DBG_ASIC, "SetLEDTime:Exit\n");
+ return status;
+}
+
+static STATUS
+SetScanMode (PAsic chip, SANE_Byte bScanBits)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte temp_f5_register = 0;
+ SANE_Byte GrayBWChannel;
+
+ DBG (DBG_ASIC, "SetScanMode():Enter; set f5 register\n");
+
+ if (bScanBits >= 24)
+ {
+ temp_f5_register |= COLOR_ES02;
+ }
+ else
+ {
+ temp_f5_register |= GRAY_ES02;
+ }
+
+ if ((bScanBits == 8) || (bScanBits == 24))
+ {
+ temp_f5_register |= _8_BITS_ES02;
+ }
+ else if (bScanBits == 1)
+ {
+ temp_f5_register |= _1_BIT_ES02;
+ }
+ else
+ {
+ temp_f5_register |= _16_BITS_ES02;
+ }
+
+ if (bScanBits < 24)
+ {
+ GrayBWChannel = 1;
+ }
+ else
+ {
+ GrayBWChannel = 4;
+ }
+
+ if (GrayBWChannel == 0)
+ {
+ temp_f5_register |= GRAY_RED_ES02;
+ }
+ else if (GrayBWChannel == 1)
+ {
+ temp_f5_register |= GRAY_GREEN_ES02;
+ }
+ else if (GrayBWChannel == 2)
+ {
+ temp_f5_register |= GRAY_BLUE_ES02;
+ }
+ else
+ {
+ temp_f5_register |= GRAY_GREEN_BLUE_ES02;
+ }
+
+ status = Mustek_SendData (chip, ES01_F5_ScanDataFormat, temp_f5_register);
+
+ DBG (DBG_ASIC, "F5_ScanDataFormat=0x%x\n", temp_f5_register);
+ DBG (DBG_ASIC, "SetScanMode():Exit\n");
+ return status;
+}
+
+static STATUS
+SetPackAddress (PAsic chip, unsigned short wXResolution, unsigned short wWidth, unsigned short wX,
+ double XRatioAdderDouble, double XRatioTypeDouble,
+ SANE_Byte byClear_Pulse_Width, unsigned short * PValidPixelNumber)
+{
+ STATUS status = STATUS_GOOD;
+
+ unsigned short LineTotalOverlapPixel;
+ SANE_Byte OverLapPixel;
+ SANE_Byte TotalLineShift;
+ SANE_Byte InvalidPixelNumberBackup;
+ unsigned short SegmentTotalPixel;
+ unsigned int dwLineTotalPixel;
+ unsigned short ValidPixelNumber = *PValidPixelNumber;
+
+ unsigned int FinalLinePixelPerSegment;
+ SANE_Byte InValidPixelNumber;
+ unsigned int CISPackAreaStartAddress;
+ SANE_Byte PackAreaUseLine;
+
+ unsigned int MaxPixelHW;
+ int i;
+
+ DBG (DBG_ASIC, "SetPackAddress:Enter\n");
+
+ LineTotalOverlapPixel = 0;
+ OverLapPixel = 0;
+ TotalLineShift = 1;
+ PackAreaUseLine = TotalLineShift + 1;
+
+ if (wXResolution > (SENSOR_DPI / 2))
+ {
+ ValidPixelNumber = ValidPixelNumberFor1200DPI;
+ OverLapPixel = OverLapPixelNumber1200;
+ }
+ else
+ {
+ ValidPixelNumber = ValidPixelNumberFor600DPI;
+ OverLapPixel = OverLapPixelNumber600;
+ }
+
+ ValidPixelNumber = (unsigned short) ((wWidth + 10 + 15) * XRatioAdderDouble);
+ ValidPixelNumber >>= 4;
+ ValidPixelNumber <<= 4;
+
+ ValidPixelNumber += (OverLapPixel * 2);
+
+ for (i = 0; i < 16; i++)
+ {
+ Mustek_SendData (chip, ES01_2B0_SEGMENT0_OVERLAP_SEGMENT1 + i,
+ OverLapPixel);
+ Mustek_SendData (chip, ES01_2C0_VALID_PIXEL_PARAMETER_OF_SEGMENT1 + i,
+ 0);
+ }
+ LineTotalOverlapPixel = OverLapPixel * 16;
+
+ FinalLinePixelPerSegment = ValidPixelNumber + OverLapPixel * 2;
+
+ if ((FinalLinePixelPerSegment % 8) > 0)
+ {
+ InValidPixelNumber = (SANE_Byte) (8 - (FinalLinePixelPerSegment % 8));
+ }
+ else
+ {
+ InValidPixelNumber = 0;
+ }
+
+ InvalidPixelNumberBackup = InValidPixelNumber;
+
+ Mustek_SendData (chip, ES01_1B0_SEGMENT_PIXEL_NUMBER_LB,
+ LOBYTE (ValidPixelNumber));
+ Mustek_SendData (chip, ES01_1B1_SEGMENT_PIXEL_NUMBER_HB,
+ HIBYTE (ValidPixelNumber));
+
+ SegmentTotalPixel =
+ ValidPixelNumber + OverLapPixel * 2 + InValidPixelNumber;
+
+ Mustek_SendData (chip, ES01_169_NUMBER_OF_SEGMENT_PIXEL_LB,
+ LOBYTE (ValidPixelNumber));
+ Mustek_SendData (chip, ES01_16A_NUMBER_OF_SEGMENT_PIXEL_HB,
+ HIBYTE (ValidPixelNumber));
+ Mustek_SendData (chip, ES01_16B_BETWEEN_SEGMENT_INVALID_PIXEL, 0);
+
+ Mustek_SendData (chip, ES01_B6_LineWidthPixelLSB,
+ LOBYTE (ValidPixelNumber));
+ Mustek_SendData (chip, ES01_B7_LineWidthPixelMSB,
+ HIBYTE (ValidPixelNumber));
+
+ Mustek_SendData (chip, ES01_19A_CHANNEL_LINE_GAP_LB,
+ LOBYTE (ValidPixelNumber));
+ Mustek_SendData (chip, ES01_19B_CHANNEL_LINE_GAP_HB,
+ HIBYTE (ValidPixelNumber));
+ DBG (DBG_ASIC, "ValidPixelNumber=%d\n", ValidPixelNumber);
+
+ for (i = 0; i < 36; i++)
+ {
+ Mustek_SendData (chip, 0x270 + i, 0);
+ }
+
+ Mustek_SendData (chip, 0x270,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 1)));
+ Mustek_SendData (chip, 0x271,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 1) >> 8));
+ Mustek_SendData (chip, 0x272,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) *
+ 1) >> 16));
+
+ Mustek_SendData (chip, 0x27C,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 2)));
+ Mustek_SendData (chip, 0x27D,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 2) >> 8));
+ Mustek_SendData (chip, 0x27E,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) *
+ 2) >> 16));
+
+ Mustek_SendData (chip, 0x288,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 3)));
+ Mustek_SendData (chip, 0x289,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 3) >> 8));
+ Mustek_SendData (chip, 0x28A,
+ (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) *
+ 3) >> 16));
+ DBG (DBG_ASIC, "channel gap=%d\n", SegmentTotalPixel * (PackAreaUseLine));
+
+
+ Mustek_SendData (chip, ES01_B4_StartPixelLSB, LOBYTE (wX + 0));
+ Mustek_SendData (chip, ES01_B5_StartPixelMSB, HIBYTE (wX + 0));
+
+
+ dwLineTotalPixel = ValidPixelNumber;
+
+ Mustek_SendData (chip, ES01_1B9_LINE_PIXEL_NUMBER_LB,
+ LOBYTE (XRatioTypeDouble * (dwLineTotalPixel - 1)));
+ Mustek_SendData (chip, ES01_1BA_LINE_PIXEL_NUMBER_HB,
+ HIBYTE (XRatioTypeDouble * (dwLineTotalPixel - 1)));
+
+ /* final start read out pixel */
+ Mustek_SendData (chip, ES01_1F4_START_READ_OUT_PIXEL_LB, LOBYTE (0));
+ Mustek_SendData (chip, ES01_1F5_START_READ_OUT_PIXEL_HB, HIBYTE (0));
+
+ MaxPixelHW = (dwLineTotalPixel + InValidPixelNumber) - 10;
+
+ if (wWidth > MaxPixelHW)
+ {
+ DBG (DBG_ERR, "read out pixel over max pixel! image will shift!!!\n");
+ }
+
+ /* final read pixel width */
+ Mustek_SendData (chip, ES01_1F6_READ_OUT_PIXEL_LENGTH_LB,
+ LOBYTE (wWidth + 9));
+ Mustek_SendData (chip, ES01_1F7_READ_OUT_PIXEL_LENGTH_HB,
+ HIBYTE (wWidth + 9));
+
+ /* data output sequence */
+ Mustek_SendData (chip, ES01_1F8_PACK_CHANNEL_SELECT_B0, 0);
+ Mustek_SendData (chip, ES01_1F9_PACK_CHANNEL_SELECT_B1, 0);
+ Mustek_SendData (chip, ES01_1FA_PACK_CHANNEL_SELECT_B2, 0x18);
+
+ Mustek_SendData (chip, ES01_1FB_PACK_CHANNEL_SIZE_B0,
+ (SANE_Byte) ((SegmentTotalPixel * PackAreaUseLine)));
+ Mustek_SendData (chip, ES01_1FC_PACK_CHANNEL_SIZE_B1,
+ (SANE_Byte) ((SegmentTotalPixel * PackAreaUseLine) >> 8));
+ Mustek_SendData (chip, ES01_1FD_PACK_CHANNEL_SIZE_B2,
+ (SANE_Byte) ((SegmentTotalPixel * PackAreaUseLine) >> 16));
+
+ Mustek_SendData (chip, ES01_16C_LINE_SHIFT_OUT_TIMES_DIRECTION, 0x01);
+ Mustek_SendData (chip, ES01_1CE_LINE_SEGMENT_NUMBER, 0x00);
+ Mustek_SendData (chip, ES01_D8_PHTG_EDGE_TIMING_ADJUST, 0x17);
+
+ Mustek_SendData (chip, ES01_D9_CLEAR_PULSE_WIDTH, byClear_Pulse_Width);
+
+ Mustek_SendData (chip, ES01_DA_CLEAR_SIGNAL_INVERTING_OUTPUT, 0x54 | 0x01);
+ Mustek_SendData (chip, ES01_CD_TG_R_CONTROL, 0x3C);
+ Mustek_SendData (chip, ES01_CE_TG_G_CONTROL, 0);
+ Mustek_SendData (chip, ES01_CF_TG_B_CONTROL, 0x3C);
+
+
+ /* set pack area address */
+
+ CISPackAreaStartAddress = PackAreaStartAddress;
+ DBG (DBG_ASIC, "CISPackAreaStartAddress=%d\n", CISPackAreaStartAddress);
+
+ /* cycle 1 */
+ Mustek_SendData (chip, ES01_16D_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0)));
+ Mustek_SendData (chip, ES01_16E_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0) >> 8));
+ Mustek_SendData (chip, ES01_16F_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0) >> 16));
+
+ Mustek_SendData (chip, ES01_170_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_171_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_172_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_173_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_174_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_175_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_176_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_177_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_178_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ /* cycle 2 */
+ Mustek_SendData (chip, ES01_179_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_17A_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_17B_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_17C_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_17D_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_17E_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_17F_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_180_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_181_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_182_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_183_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_184_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ /* cycle 3 */
+ Mustek_SendData (chip, ES01_185_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+
+ Mustek_SendData (chip, ES01_186_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_187_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_188_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_189_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_18A_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_18B_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_18C_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_18D_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+
+ Mustek_SendData (chip, ES01_18E_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000)));
+ Mustek_SendData (chip, ES01_18F_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8));
+ Mustek_SendData (chip, ES01_190_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16));
+ DBG (DBG_ASIC, "set CISPackAreaStartAddress ok\n");
+
+ Mustek_SendData (chip, 0x260, InValidPixelNumber);
+ Mustek_SendData (chip, 0x261, InValidPixelNumber << 4);
+ Mustek_SendData (chip, 0x262, InValidPixelNumber);
+ Mustek_SendData (chip, 0x263, 0);
+ DBG (DBG_ASIC, "InValidPixelNumber=%d\n", InValidPixelNumber);
+
+ Mustek_SendData (chip, 0x264, 0);
+ Mustek_SendData (chip, 0x265, 0);
+ Mustek_SendData (chip, 0x266, 0);
+ Mustek_SendData (chip, 0x267, 0);
+
+ Mustek_SendData (chip, 0x268, 0);
+ Mustek_SendData (chip, 0x269, 0);
+ Mustek_SendData (chip, 0x26A, 0);
+ Mustek_SendData (chip, 0x26B, 0);
+
+ Mustek_SendData (chip, 0x26C, 0);
+ Mustek_SendData (chip, 0x26D, 0);
+ Mustek_SendData (chip, 0x26E, 0);
+ Mustek_SendData (chip, 0x26F, 0);
+ DBG (DBG_ASIC, "Set Invalid Pixel ok\n");
+
+
+ /* Pack Start Address */
+ Mustek_SendData (chip, ES01_19E_PACK_AREA_R_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 0)))));
+ Mustek_SendData (chip, ES01_19F_PACK_AREA_R_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel *
+ (PackAreaUseLine * 0))) >> 8));
+ Mustek_SendData (chip, ES01_1A0_PACK_AREA_R_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel *
+ (PackAreaUseLine * 0))) >> 16));
+
+
+ Mustek_SendData (chip, ES01_1A1_PACK_AREA_G_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 1)))));
+ Mustek_SendData (chip, ES01_1A2_PACK_AREA_G_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel *
+ (PackAreaUseLine * 1))) >> 8));
+ Mustek_SendData (chip, ES01_1A3_PACK_AREA_G_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel *
+ (PackAreaUseLine * 1))) >> 16));
+
+ Mustek_SendData (chip, ES01_1A4_PACK_AREA_B_START_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 2)))));
+ Mustek_SendData (chip, ES01_1A5_PACK_AREA_B_START_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel *
+ (PackAreaUseLine * 2))) >> 8));
+ Mustek_SendData (chip, ES01_1A6_PACK_AREA_B_START_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel *
+ (PackAreaUseLine * 2))) >> 16));
+
+ /* Pack End Address */
+ Mustek_SendData (chip, ES01_1A7_PACK_AREA_R_END_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 1) -
+ 1))));
+ Mustek_SendData (chip, ES01_1A8_PACK_AREA_R_END_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 1) -
+ 1)) >> 8));
+ Mustek_SendData (chip, ES01_1A9_PACK_AREA_R_END_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 1) -
+ 1)) >> 16));
+
+ Mustek_SendData (chip, ES01_1AA_PACK_AREA_G_END_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 2) -
+ 1))));
+ Mustek_SendData (chip, ES01_1AB_PACK_AREA_G_END_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 2) -
+ 1)) >> 8));
+ Mustek_SendData (chip, ES01_1AC_PACK_AREA_G_END_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 2) -
+ 1)) >> 16));
+
+ Mustek_SendData (chip, ES01_1AD_PACK_AREA_B_END_ADDR_BYTE0,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 3) -
+ 1))));
+ Mustek_SendData (chip, ES01_1AE_PACK_AREA_B_END_ADDR_BYTE1,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 3) -
+ 1)) >> 8));
+ Mustek_SendData (chip, ES01_1AF_PACK_AREA_B_END_ADDR_BYTE2,
+ (SANE_Byte) ((CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 3) -
+ 1)) >> 16));
+ DBG (DBG_ASIC,
+ "CISPackAreaStartAddress + (SegmentTotalPixel*(PackAreaUseLine*1))=%d\n",
+ (CISPackAreaStartAddress +
+ (SegmentTotalPixel * (PackAreaUseLine * 1))));
+
+ Mustek_SendData (chip, ES01_19C_MAX_PACK_LINE, PackAreaUseLine);
+
+ status =
+ Mustek_SendData (chip, ES01_19D_PACK_THRESHOLD_LINE, TotalLineShift);
+ DBG (DBG_ASIC, "PackAreaUseLine=%d,TotalLineShift=%d\n", PackAreaUseLine,
+ TotalLineShift);
+
+ *PValidPixelNumber = ValidPixelNumber;
+
+ DBG (DBG_ASIC, "SetPackAddress:Enter\n");
+ return status;
+}
+
+static STATUS
+SetExtraSetting (PAsic chip, unsigned short wXResolution, unsigned short wCCD_PixelNumber,
+ SANE_Bool isCaribrate)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte byPHTG_PulseWidth, byPHTG_WaitWidth;
+ SANE_Byte temp_ff_register = 0;
+ SANE_Byte bThreshold = 128;
+
+ DBG (DBG_ASIC, "SetExtraSetting:Enter\n");
+
+ Mustek_SendData (chip, ES01_B8_ChannelRedExpStartPixelLSB,
+ LOBYTE (chip->Timing.ChannelR_StartPixel));
+ Mustek_SendData (chip, ES01_B9_ChannelRedExpStartPixelMSB,
+ HIBYTE (chip->Timing.ChannelR_StartPixel));
+ Mustek_SendData (chip, ES01_BA_ChannelRedExpEndPixelLSB,
+ LOBYTE (chip->Timing.ChannelR_EndPixel));
+ Mustek_SendData (chip, ES01_BB_ChannelRedExpEndPixelMSB,
+ HIBYTE (chip->Timing.ChannelR_EndPixel));
+
+ Mustek_SendData (chip, ES01_BC_ChannelGreenExpStartPixelLSB,
+ LOBYTE (chip->Timing.ChannelG_StartPixel));
+ Mustek_SendData (chip, ES01_BD_ChannelGreenExpStartPixelMSB,
+ HIBYTE (chip->Timing.ChannelG_StartPixel));
+ Mustek_SendData (chip, ES01_BE_ChannelGreenExpEndPixelLSB,
+ LOBYTE (chip->Timing.ChannelG_EndPixel));
+ Mustek_SendData (chip, ES01_BF_ChannelGreenExpEndPixelMSB,
+ HIBYTE (chip->Timing.ChannelG_EndPixel));
+
+ Mustek_SendData (chip, ES01_C0_ChannelBlueExpStartPixelLSB,
+ LOBYTE (chip->Timing.ChannelB_StartPixel));
+ Mustek_SendData (chip, ES01_C1_ChannelBlueExpStartPixelMSB,
+ HIBYTE (chip->Timing.ChannelB_StartPixel));
+ Mustek_SendData (chip, ES01_C2_ChannelBlueExpEndPixelLSB,
+ LOBYTE (chip->Timing.ChannelB_EndPixel));
+ Mustek_SendData (chip, ES01_C3_ChannelBlueExpEndPixelMSB,
+ HIBYTE (chip->Timing.ChannelB_EndPixel));
+
+ byPHTG_PulseWidth = chip->Timing.PHTG_PluseWidth;
+ byPHTG_WaitWidth = chip->Timing.PHTG_WaitWidth;
+ Mustek_SendData (chip, ES01_B2_PHTGPulseWidth, byPHTG_PulseWidth);
+ Mustek_SendData (chip, ES01_B3_PHTGWaitWidth, byPHTG_WaitWidth);
+
+ Mustek_SendData (chip, ES01_CC_PHTGTimingAdjust,
+ chip->Timing.PHTG_TimingAdj);
+ Mustek_SendData (chip, ES01_D0_PH1_0, chip->Timing.PHTG_TimingSetup);
+
+ DBG (DBG_ASIC, "ChannelR_StartPixel=%d,ChannelR_EndPixel=%d\n",
+ chip->Timing.ChannelR_StartPixel, chip->Timing.ChannelR_EndPixel);
+
+ if (wXResolution == 1200)
+ {
+ Mustek_SendData (chip, ES01_DE_CCD_SETUP_REGISTER,
+ chip->Timing.DE_CCD_SETUP_REGISTER_1200);
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_DE_CCD_SETUP_REGISTER,
+ chip->Timing.DE_CCD_SETUP_REGISTER_600);
+ }
+
+ if (isCaribrate == TRUE)
+ {
+ temp_ff_register |= BYPASS_DARK_SHADING_ENABLE;
+ temp_ff_register |= BYPASS_WHITE_SHADING_ENABLE;
+ }
+ else /*Setwindow */
+ {
+ temp_ff_register |= BYPASS_DARK_SHADING_DISABLE;
+ temp_ff_register |= BYPASS_WHITE_SHADING_DISABLE;
+ }
+
+ temp_ff_register |= BYPASS_PRE_GAMMA_ENABLE;
+
+ temp_ff_register |= BYPASS_CONVOLUTION_ENABLE;
+
+
+ temp_ff_register |= BYPASS_MATRIX_ENABLE;
+
+ temp_ff_register |= BYPASS_GAMMA_ENABLE;
+
+ if (isCaribrate == TRUE)
+ {
+ Mustek_SendData (chip, ES01_FF_SCAN_IMAGE_OPTION, 0xfc | (0x00 & 0x03));
+ DBG (DBG_ASIC, "FF_SCAN_IMAGE_OPTION=0x%x\n", 0xfc | (0x00 & 0x03));
+ }
+ else /*Setwindow */
+ {
+ Mustek_SendData (chip, ES01_FF_SCAN_IMAGE_OPTION,
+ temp_ff_register | (0x00 & 0x03));
+ DBG (DBG_ASIC, "FF_SCAN_IMAGE_OPTION=0x%x\n",
+ temp_ff_register | (0x00 & 0x03));
+ }
+
+ /* pixel process time */
+ Mustek_SendData (chip, ES01_B0_CCDPixelLSB, LOBYTE (wCCD_PixelNumber));
+ Mustek_SendData (chip, ES01_B1_CCDPixelMSB, HIBYTE (wCCD_PixelNumber));
+ Mustek_SendData (chip, ES01_DF_ICG_CONTROL, 0x17);
+ DBG (DBG_ASIC, "wCCD_PixelNumber=%d\n", wCCD_PixelNumber);
+
+ Mustek_SendData (chip, ES01_88_LINE_ART_THRESHOLD_HIGH_VALUE, bThreshold);
+ Mustek_SendData (chip, ES01_89_LINE_ART_THRESHOLD_LOW_VALUE,
+ bThreshold - 1);
+ DBG (DBG_ASIC, "bThreshold=%d\n", bThreshold);
+
+ usleep (50000);
+
+ DBG (DBG_ASIC, "SetExtraSetting:Exit\n");
+ return status;
+}
+
+
+/* ---------------------- high level asic functions ------------------------ */
+
+
+/* HOLD: We don't want to have global vid/pids */
+static unsigned short ProductID = 0x0409;
+static unsigned short VendorID = 0x055f;
+
+static SANE_String_Const device_name;
+
+static SANE_Status
+attach_one_scanner (SANE_String_Const devname)
+{
+ DBG (DBG_ASIC, "attach_one_scanner: enter\n");
+ DBG (DBG_INFO, "attach_one_scanner: devname = %s\n", devname);
+ device_name = devname;
+ return SANE_STATUS_GOOD;
+}
+
+static STATUS
+Asic_Open (PAsic chip, SANE_Byte *pDeviceName)
+{
+ STATUS status;
+ SANE_Status sane_status;
+
+ DBG (DBG_ASIC, "Asic_Open: Enter\n");
+
+ device_name = NULL;
+
+ if (chip->firmwarestate > FS_OPENED)
+ {
+ DBG (DBG_ASIC, "chip has been opened. fd=%d\n", chip->fd);
+ return STATUS_INVAL;
+ }
+
+ /* init usb */
+ sanei_usb_init ();
+
+ /* find scanner */
+ sane_status =
+ sanei_usb_find_devices (VendorID, ProductID, attach_one_scanner);
+ if (sane_status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_Open: sanei_usb_find_devices failed: %s\n",
+ sane_strstatus (sane_status));
+ return STATUS_INVAL;
+ }
+ /* open usb */
+ if (device_name == NULL)
+ {
+ DBG (DBG_ERR, "Asic_Open: no scanner found\n");
+ return STATUS_INVAL;
+ }
+ sane_status = sanei_usb_open (device_name, &chip->fd);
+ if (sane_status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_Open: sanei_usb_open of %s failed: %s\n",
+ device_name, sane_strstatus (sane_status));
+ return STATUS_INVAL;
+ }
+
+ /* open scanner chip */
+ status = OpenScanChip (chip);
+ if (status != STATUS_GOOD)
+ {
+ sanei_usb_close (chip->fd);
+ DBG (DBG_ASIC, "Asic_Open: OpenScanChip error\n");
+ return status;
+ }
+
+ Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27);
+ Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle,
+ CLOSE_ALL_CLOCK_DISABLE);
+ Mustek_SendData (chip, ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL,
+ SDRAMCLK_DELAY_12_ns);
+
+ /* SDRAM initial sequence */
+ Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0xf1);
+ Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0xa5);
+ Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0x91);
+ Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0x81);
+ Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0xf0);
+
+
+ chip->firmwarestate = FS_OPENED;
+ Asic_WaitUnitReady (chip);
+ DBG (DBG_ASIC, "Asic_WaitUnitReady\n");
+ status = SafeInitialChip (chip);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_Open: SafeInitialChip error\n");
+ return status;
+ }
+
+ pDeviceName = (SANE_Byte *) strdup (device_name);
+ if (!pDeviceName)
+ {
+ DBG (DBG_ERR, "Asic_Open: not enough memory\n");
+ return STATUS_INVAL;
+ }
+ DBG (DBG_INFO, "Asic_Open: device %s successfully opened\n", pDeviceName);
+ DBG (DBG_ASIC, "Asic_Open: Exit\n");
+ return status;
+}
+
+
+static STATUS
+Asic_Close (PAsic chip)
+{
+ STATUS status;
+ DBG (DBG_ASIC, "Asic_Close: Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+ {
+ DBG (DBG_ASIC, "Asic_Close: Scanner is not opened\n");
+ return STATUS_GOOD;
+ }
+
+ if (chip->firmwarestate > FS_OPENED)
+ {
+ DBG (DBG_ASIC,
+ "Asic_Close: Scanner is scanning, try to stop scanning\n");
+ Asic_ScanStop (chip);
+ }
+
+ Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle,
+ CLOSE_ALL_CLOCK_ENABLE);
+
+ status = CloseScanChip (chip);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_Close: CloseScanChip error\n");
+ return status;
+ }
+
+ sanei_usb_close (chip->fd);
+ chip->firmwarestate = FS_ATTACHED;
+
+ DBG (DBG_ASIC, "Asic_Close: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_TurnLamp (PAsic chip, SANE_Bool isLampOn)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte PWM;
+
+ DBG (DBG_ASIC, "Asic_TurnLamp: Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_TurnLamp: Scanner is not opened\n");
+ return STATUS_INVAL;
+ }
+
+ if (chip->firmwarestate > FS_OPENED)
+ {
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+ }
+
+ if (isLampOn)
+ {
+ PWM = LAMP0_PWM_DEFAULT;
+ }
+ else
+ {
+ PWM = 0;
+ }
+ Mustek_SendData (chip, ES01_99_LAMP_PWM_FREQ_CONTROL, 1);
+ Mustek_SendData (chip, ES01_90_Lamp0PWM, PWM);
+ DBG (DBG_ASIC, "Lamp0 PWM = %d\n", PWM);
+
+ chip->firmwarestate = FS_OPENED;
+
+ DBG (DBG_ASIC, "Asic_TurnLamp: Exit\n");
+ return status;
+}
+
+
+static STATUS
+Asic_TurnTA (PAsic chip, SANE_Bool isTAOn)
+{
+ SANE_Byte PWM;
+
+ DBG (DBG_ASIC, "Asic_TurnTA: Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_TurnTA: Scanner is not opened\n");
+ return STATUS_INVAL;
+ }
+
+ if (chip->firmwarestate > FS_OPENED)
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+ if (isTAOn)
+ {
+ PWM = LAMP1_PWM_DEFAULT;
+ }
+ else
+ {
+ PWM = 0;
+
+ }
+ Mustek_SendData (chip, ES01_99_LAMP_PWM_FREQ_CONTROL, 1);
+ Mustek_SendData (chip, ES01_91_Lamp1PWM, PWM);
+ DBG (DBG_ASIC, "Lamp1 PWM = %d\n", PWM);
+
+ chip->firmwarestate = FS_OPENED;
+ DBG (DBG_ASIC, "Asic_TurnTA: Exit\n");
+ return STATUS_GOOD;
+}
+
+static STATUS
+Asic_WaitUnitReady (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte temp_status;
+ int i = 0;
+
+ DBG (DBG_ASIC, "Asic_WaitUnitReady:Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_WaitUnitReady: Scanner has not been opened\n");
+
+ return STATUS_INVAL;
+ }
+
+
+ do
+ {
+ status = GetChipStatus (chip, 1, &temp_status);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ASIC, "WaitChipIdle:Error!\n");
+ return status;
+ }
+ i++;
+ usleep (100000);
+ }
+ while (((temp_status & 0x1f) != 0) && i < 300);
+ DBG (DBG_ASIC, "Wait %d s\n", (unsigned short) (i * 0.1));
+
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+ chip->motorstate = MS_STILL;
+
+ DBG (DBG_ASIC, "Asic_WaitUnitReady: Exit\n");
+ return status;
+}
+
+#if SANE_UNUSED
+static STATUS
+Asic_Release (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_Release()\n");
+
+ if (chip->firmwarestate > FS_ATTACHED)
+ status = Asic_Close (chip);
+
+ chip->firmwarestate = FS_NULL;
+
+ DBG (DBG_ASIC, "Asic_Release: Exit\n");
+ return status;
+}
+#endif
+
+static STATUS
+Asic_Initialize (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_Initialize:Enter\n");
+
+
+ chip->motorstate = MS_STILL;
+ chip->dwBytesCountPerRow = 0;
+ chip->lpGammaTable = NULL;
+ DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip);
+
+ chip->isFirstOpenChip = TRUE;
+ DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip);
+
+ chip->SWWidth = 0;
+ chip->TA_Status = TA_UNKNOW;
+ chip->lpShadingTable = NULL;
+ chip->isMotorMove = MOTOR_0_ENABLE;
+
+ Asic_Reset (chip);
+ InitTiming (chip);
+
+ chip->isUniformSpeedToScan = UNIFORM_MOTOR_AND_SCAN_SPEED_DISABLE;
+ chip->isMotorGoToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_ENABLE;
+
+ chip->UsbHost = HT_USB10;
+
+ DBG (DBG_ASIC, "Asic_Initialize: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_SetWindow (PAsic chip, SANE_Byte bScanBits,
+ unsigned short wXResolution, unsigned short wYResolution,
+ unsigned short wX, unsigned short wY, unsigned short wWidth, unsigned short wLength)
+{
+ STATUS status = STATUS_GOOD;
+
+ unsigned short ValidPixelNumber;
+
+ unsigned short wPerLineNeedBufferSize = 0;
+ unsigned short BytePerPixel = 0;
+ unsigned int dwTotal_PerLineNeedBufferSize = 0;
+ unsigned int dwTotalLineTheBufferNeed = 0;
+ unsigned short dwTotal_CCDResolution = 1200;
+ unsigned short wThinkCCDResolution = 0;
+ unsigned short wCCD_PixelNumber = 0;
+ unsigned int dwLineWidthPixel = 0;
+ unsigned short wNowMotorDPI;
+ unsigned short XRatioTypeWord;
+ double XRatioTypeDouble;
+ double XRatioAdderDouble;
+
+ LLF_MOTORMOVE *lpMotorStepsTable =
+ (LLF_MOTORMOVE *) malloc (sizeof (LLF_MOTORMOVE));
+ SANE_Byte byDummyCycleNum = 0;
+ unsigned short Total_MotorDPI;
+
+ unsigned short wMultiMotorStep = 1;
+ SANE_Byte bMotorMoveType = _MOTOR_MOVE_TYPE;
+
+ SANE_Byte byClear_Pulse_Width = 0;
+
+ unsigned int dwLinePixelReport;
+ SANE_Byte byPHTG_PulseWidth, byPHTG_WaitWidth;
+
+ unsigned short StartSpeed, EndSpeed;
+ LLF_CALCULATEMOTORTABLE CalMotorTable;
+ LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase;
+ LLF_RAMACCESS RamAccess;
+ unsigned int dwStartAddr, dwEndAddr, dwTableBaseAddr, dwShadingTableAddr;
+
+ SANE_Byte isMotorMoveToFirstLine = chip->isMotorGoToFirstLine;
+ SANE_Byte isUniformSpeedToScan = chip->isUniformSpeedToScan;
+ SANE_Byte isScanBackTracking = SCAN_BACK_TRACKING_ENABLE;
+ unsigned short * lpMotorTable;
+ unsigned int RealTableSize;
+ double dbXRatioAdderDouble;
+ unsigned short wFullBank;
+
+ DBG (DBG_ASIC, "Asic_SetWindow: Enter\n");
+ DBG (DBG_ASIC,
+ "bScanBits=%d,wXResolution=%d,wYResolution=%d,wX=%d,wY=%d,wWidth=%d,wLength=%d\n",
+ bScanBits, wXResolution, wYResolution, wX, wY, wWidth, wLength);
+
+ if (chip->firmwarestate != FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_SetWindow: Scanner is not opened\n");
+ return STATUS_INVAL;
+ }
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, 0);
+ Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle,
+ CLOSE_ALL_CLOCK_DISABLE);
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+ status = Asic_WaitUnitReady (chip);
+
+ /* dummy clock mode */
+ Mustek_SendData (chip, 0x1CD, 0);
+
+ /* LED Flash */
+ Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27 | 64 | 128);
+
+ /* calculate byte per line */
+ if (bScanBits > 24)
+ {
+ wPerLineNeedBufferSize = wWidth * 6;
+ BytePerPixel = 6;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 6;
+ }
+ else if (bScanBits == 24)
+ {
+ wPerLineNeedBufferSize = wWidth * 3;
+ BytePerPixel = 3;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 3;
+ }
+ else if ((bScanBits > 8) && (bScanBits <= 16))
+ {
+ wPerLineNeedBufferSize = wWidth * 2;
+ BytePerPixel = 2;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 2;
+ }
+ else if ((bScanBits == 8))
+ {
+ wPerLineNeedBufferSize = wWidth;
+ BytePerPixel = 1;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth);
+ }
+ else if ((bScanBits < 8))
+ {
+ wPerLineNeedBufferSize = wWidth >> 3;
+ BytePerPixel = 1;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth);
+ }
+ DBG (DBG_ASIC, "dwBytesCountPerRow = %d\n", chip->dwBytesCountPerRow);
+
+ byDummyCycleNum = 0;
+ if (chip->lsLightSource == LS_REFLECTIVE)
+ {
+ if (chip->UsbHost == HT_USB10)
+ {
+ switch (wYResolution)
+ {
+ case 2400:
+ case 1200:
+ if (chip->dwBytesCountPerRow > 22000)
+ byDummyCycleNum = 4;
+ else if (chip->dwBytesCountPerRow > 15000)
+ byDummyCycleNum = 3;
+ else if (chip->dwBytesCountPerRow > 10000)
+ byDummyCycleNum = 2;
+ else if (chip->dwBytesCountPerRow > 5000)
+ byDummyCycleNum = 1;
+ break;
+ case 600:
+ case 300:
+ case 150:
+ case 100:
+ if (chip->dwBytesCountPerRow > 21000)
+ byDummyCycleNum = 7;
+ else if (chip->dwBytesCountPerRow > 18000)
+ byDummyCycleNum = 6;
+ else if (chip->dwBytesCountPerRow > 15000)
+ byDummyCycleNum = 5;
+ else if (chip->dwBytesCountPerRow > 12000)
+ byDummyCycleNum = 4;
+ else if (chip->dwBytesCountPerRow > 9000)
+ byDummyCycleNum = 3;
+ else if (chip->dwBytesCountPerRow > 6000)
+ byDummyCycleNum = 2;
+ else if (chip->dwBytesCountPerRow > 3000)
+ byDummyCycleNum = 1;
+ break;
+ case 75:
+ case 50:
+ byDummyCycleNum = 1;
+ break;
+ default:
+ byDummyCycleNum = 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (wYResolution)
+ {
+ case 2400:
+ case 1200:
+ case 75:
+ case 50:
+ byDummyCycleNum = 1;
+ break;
+ default:
+ byDummyCycleNum = 0;
+ break;
+ }
+ }
+ }
+
+ dwTotal_PerLineNeedBufferSize = wPerLineNeedBufferSize;
+ dwTotalLineTheBufferNeed = wLength;
+
+ chip->Scan.Dpi = wXResolution;
+ CCDTiming (chip);
+
+ dwTotal_CCDResolution = SENSOR_DPI;
+
+ if (chip->lsLightSource == LS_REFLECTIVE)
+ {
+ if (wXResolution > (dwTotal_CCDResolution / 2))
+ { /* full ccd resolution 1200dpi */
+ wThinkCCDResolution = dwTotal_CCDResolution;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01);
+
+ wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_1200;
+ }
+ else
+ { /*600dpi */
+ wThinkCCDResolution = dwTotal_CCDResolution / 2;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00);
+ wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_600;
+ }
+ }
+ else
+ {
+ if (wXResolution > (dwTotal_CCDResolution / 2))
+ {
+ wThinkCCDResolution = dwTotal_CCDResolution;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01);
+ wCCD_PixelNumber = TA_IMAGE_PIXELNUMBER;
+ }
+ else
+ {
+ wThinkCCDResolution = dwTotal_CCDResolution / 2;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00);
+ wCCD_PixelNumber = TA_IMAGE_PIXELNUMBER;
+ }
+ }
+
+ dwLineWidthPixel = wWidth;
+
+ SetLineTimeAndExposure (chip);
+
+ Mustek_SendData (chip, ES01_CB_CCDDummyCycleNumber, byDummyCycleNum);
+
+
+ SetLEDTime (chip);
+
+ /* calculate Y ratio */
+ Total_MotorDPI = 1200;
+ wNowMotorDPI = Total_MotorDPI;
+
+
+ /* SetADConverter */
+ Mustek_SendData (chip, ES01_74_HARDWARE_SETTING,
+ MOTOR1_SERIAL_INTERFACE_G10_8_ENABLE | LED_OUT_G11_DISABLE
+ | SLAVE_SERIAL_INTERFACE_G15_14_DISABLE |
+ SHUTTLE_CCD_DISABLE);
+
+ /* set AFE */
+ Mustek_SendData (chip, ES01_9A_AFEControl,
+ AD9826_AFE | AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE);
+ SetAFEGainOffset (chip);
+
+ Mustek_SendData (chip, ES01_F7_DigitalControl, DIGITAL_REDUCE_DISABLE);
+
+ /* calculate X ratio */
+ XRatioTypeDouble = wXResolution;
+ XRatioTypeDouble /= wThinkCCDResolution;
+ XRatioAdderDouble = 1 / XRatioTypeDouble;
+ XRatioTypeWord = (unsigned short) (XRatioTypeDouble * 32768); /* 32768 = 2^15 */
+
+ XRatioAdderDouble = (double) (XRatioTypeWord) / 32768;
+ XRatioAdderDouble = 1 / XRatioAdderDouble;
+
+ /* 0x8000 = 1.00000 -> get all pixel */
+ Mustek_SendData (chip, ES01_9E_HorizontalRatio1to15LSB,
+ LOBYTE (XRatioTypeWord));
+ Mustek_SendData (chip, ES01_9F_HorizontalRatio1to15MSB,
+ HIBYTE (XRatioTypeWord));
+
+ /* SetMotorType */
+ Mustek_SendData (chip, ES01_A6_MotorOption, MOTOR_0_ENABLE |
+ MOTOR_1_DISABLE |
+ HOME_SENSOR_0_ENABLE | ES03_TABLE_DEFINE);
+
+ Mustek_SendData (chip, ES01_F6_MorotControl1, SPEED_UNIT_1_PIXEL_TIME |
+ MOTOR_SYNC_UNIT_1_PIXEL_TIME);
+
+ /* SetScanStepTable */
+
+ /*set Motor move type */
+ if (wYResolution >= 1200)
+ bMotorMoveType = _8_TABLE_SPACE_FOR_1_DIV_2_STEP;
+
+ switch (bMotorMoveType)
+ {
+ case _4_TABLE_SPACE_FOR_FULL_STEP:
+ wMultiMotorStep = 1;
+ break;
+
+ case _8_TABLE_SPACE_FOR_1_DIV_2_STEP:
+ wMultiMotorStep = 2;
+ break;
+
+ case _16_TABLE_SPACE_FOR_1_DIV_4_STEP:
+ wMultiMotorStep = 4;
+ break;
+
+ case _32_TABLE_SPACE_FOR_1_DIV_8_STEP:
+ wMultiMotorStep = 8;
+ break;
+ }
+ wY *= wMultiMotorStep;
+
+ SetScanMode (chip, bScanBits);
+
+
+
+ /*set white shading int and dec */
+ if (chip->lsLightSource == LS_REFLECTIVE)
+ Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT,
+ ES01_SHADING_3_INT_13_DEC);
+ else
+ Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT,
+ ES01_SHADING_4_INT_12_DEC);
+
+ SetPackAddress (chip, wXResolution, wWidth, wX, XRatioAdderDouble,
+ XRatioTypeDouble, byClear_Pulse_Width, &ValidPixelNumber);
+ SetExtraSetting (chip, wXResolution, wCCD_PixelNumber, FALSE);
+
+ /* calculate line time */
+ byPHTG_PulseWidth = chip->Timing.PHTG_PluseWidth;
+ byPHTG_WaitWidth = chip->Timing.PHTG_WaitWidth;
+ dwLinePixelReport = ((byClear_Pulse_Width + 1) * 2 +
+ (byPHTG_PulseWidth + 1) * (1) +
+ (byPHTG_WaitWidth + 1) * (1) +
+ (wCCD_PixelNumber + 1)) * (byDummyCycleNum + 1);
+
+ DBG (DBG_ASIC, "Motor Time = %d\n",
+ (dwLinePixelReport * wYResolution / wNowMotorDPI) / wMultiMotorStep);
+ if ((dwLinePixelReport * wYResolution / wNowMotorDPI) / wMultiMotorStep >
+ 64000)
+ {
+ DBG (DBG_ASIC, "Motor Time Over Flow !!!\n");
+ }
+
+ EndSpeed =
+ (unsigned short) ((dwLinePixelReport * wYResolution / wNowMotorDPI) /
+ wMultiMotorStep);
+ SetMotorStepTable (chip, lpMotorStepsTable, wY, dwTotalLineTheBufferNeed * wNowMotorDPI / wYResolution * wMultiMotorStep, wYResolution); /*modified by Chester 92/04/08 */
+
+
+ if (EndSpeed >= 20000)
+ {
+ Asic_MotorMove (chip, 1, wY / wMultiMotorStep);
+ isUniformSpeedToScan = UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE;
+ isScanBackTracking = SCAN_BACK_TRACKING_DISABLE;
+ isMotorMoveToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_DISABLE;
+ }
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, isMotorMoveToFirstLine |
+ MOTOR_BACK_HOME_AFTER_SCAN_DISABLE |
+ SCAN_ENABLE |
+ isScanBackTracking |
+ INVERT_MOTOR_DIRECTION_DISABLE |
+ isUniformSpeedToScan |
+ ES01_STATIC_SCAN_DISABLE | MOTOR_TEST_LOOP_DISABLE);
+
+ if (EndSpeed > 8000)
+ {
+ StartSpeed = EndSpeed;
+ }
+ else
+ {
+ if (EndSpeed <= 1000)
+ StartSpeed = EndSpeed + 4500;
+ else
+ StartSpeed = EndSpeed + 3500;
+ }
+
+ Mustek_SendData (chip, ES01_FD_MotorFixedspeedLSB, LOBYTE (EndSpeed));
+ Mustek_SendData (chip, ES01_FE_MotorFixedspeedMSB, HIBYTE (EndSpeed));
+
+ lpMotorTable = (unsigned short *) malloc (512 * 8 * 2);
+ memset (lpMotorTable, 0, 512 * 8 * sizeof (unsigned short));
+
+ CalMotorTable.StartSpeed = StartSpeed;
+ CalMotorTable.EndSpeed = EndSpeed;
+ CalMotorTable.AccStepBeforeScan = lpMotorStepsTable->wScanAccSteps;
+ CalMotorTable.DecStepAfterScan = lpMotorStepsTable->bScanDecSteps;
+ CalMotorTable.lpMotorTable = lpMotorTable;
+
+ CalculateMotorTable (&CalMotorTable, wYResolution);
+
+ CurrentPhase.MotorDriverIs3967 = 0;
+ CurrentPhase.FillPhase = 1;
+ CurrentPhase.MoveType = bMotorMoveType;
+ SetMotorCurrent (chip, EndSpeed, &CurrentPhase);
+ LLFSetMotorCurrentAndPhase (chip, &CurrentPhase);
+
+ DBG (DBG_ASIC,
+ "EndSpeed = %d, BytesCountPerRow=%d, MotorCurrentTable=%d, LinePixelReport=%d\n",
+ EndSpeed, chip->dwBytesCountPerRow, CurrentPhase.MotorCurrentTableA[0],
+ dwLinePixelReport);
+
+ /* write motor table */
+ RealTableSize = 512 * 8;
+ dwTableBaseAddr = PackAreaStartAddress - RealTableSize;
+
+ dwStartAddr = dwTableBaseAddr;
+
+ RamAccess.ReadWrite = WRITE_RAM;
+ RamAccess.IsOnChipGamma = EXTERNAL_RAM;
+ RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns;
+ RamAccess.LoStartAddress = (unsigned short) (dwStartAddr);
+ RamAccess.HiStartAddress = (unsigned short) (dwStartAddr >> 16);
+ RamAccess.RwSize = RealTableSize * 2;
+ RamAccess.BufferPtr = (SANE_Byte *) lpMotorTable;
+ LLFRamAccess (chip, &RamAccess);
+
+ Mustek_SendData (chip, ES01_DB_PH_RESET_EDGE_TIMING_ADJUST, 0x00);
+
+ Mustek_SendData (chip, ES01_DC_CLEAR_EDGE_TO_PH_TG_EDGE_WIDTH, 0);
+
+ Mustek_SendData (chip, ES01_9D_MotorTableAddrA14_A21,
+ (SANE_Byte) (dwTableBaseAddr >> TABLE_OFFSET_BASE));
+
+
+ /* set address and shading table */
+ /*set image buffer range and write shading table */
+ RealTableSize =
+ (ACC_DEC_STEP_TABLE_SIZE * NUM_OF_ACC_DEC_STEP_TABLE) +
+ ShadingTableSize (ValidPixelNumber);
+ dwShadingTableAddr =
+ PackAreaStartAddress -
+ (((RealTableSize +
+ (TABLE_BASE_SIZE - 1)) >> TABLE_OFFSET_BASE) << TABLE_OFFSET_BASE);
+ dwEndAddr = dwShadingTableAddr - 1;
+ dwStartAddr = dwShadingTableAddr;
+
+ if (wXResolution > 600)
+ dbXRatioAdderDouble = 1200 / wXResolution;
+ else
+ dbXRatioAdderDouble = 600 / wXResolution;
+
+ RamAccess.ReadWrite = WRITE_RAM;
+ RamAccess.IsOnChipGamma = EXTERNAL_RAM;
+ RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns;
+ RamAccess.LoStartAddress = (unsigned short) (dwStartAddr);
+ RamAccess.HiStartAddress = (unsigned short) (dwStartAddr >> 16);
+ RamAccess.RwSize =
+ ShadingTableSize ((int) ((wWidth + 4) * dbXRatioAdderDouble)) *
+ sizeof (unsigned short);
+ RamAccess.BufferPtr = (SANE_Byte *) chip->lpShadingTable;
+ LLFRamAccess (chip, &RamAccess);
+
+ /*tell scan chip the shading table address, unit is 2^15 bytes(2^14 word) */
+ Mustek_SendData (chip, ES01_9B_ShadingTableAddrA14_A21,
+ (SANE_Byte) (dwShadingTableAddr >> TABLE_OFFSET_BASE));
+
+ /*empty bank */
+ Mustek_SendData (chip, ES01_FB_BufferEmptySize16WordLSB,
+ LOBYTE (WaitBufferOneLineSize >> (7 - 3)));
+ Mustek_SendData (chip, ES01_FC_BufferEmptySize16WordMSB,
+ HIBYTE (WaitBufferOneLineSize >> (7 - 3)));
+
+ /*full bank */
+ wFullBank =
+ (unsigned short) ((dwEndAddr -
+ (((dwLineWidthPixel * BytePerPixel) / 2) * 3 * 1)) / BANK_SIZE);
+ Mustek_SendData (chip, ES01_F9_BufferFullSize16WordLSB, LOBYTE (wFullBank));
+ Mustek_SendData (chip, ES01_FA_BufferFullSize16WordMSB, HIBYTE (wFullBank));
+
+
+ /* set buffer address */
+ LLFSetRamAddress (chip, 0x0, dwEndAddr, ACCESS_DRAM);
+
+ Mustek_SendData (chip, ES01_00_ADAFEConfiguration, 0x70);
+ Mustek_SendData (chip, ES01_02_ADAFEMuxConfig, 0x80);
+
+ free (lpMotorTable);
+ free (lpMotorStepsTable);
+ chip->firmwarestate = FS_OPENED;
+
+ DBG (DBG_ASIC, "Asic_SetWindow: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_Reset (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_Reset: Enter\n");
+
+ chip->lsLightSource = LS_REFLECTIVE;
+
+ chip->dwBytesCountPerRow = 0;
+ chip->AD.DirectionR = 0;
+ chip->AD.DirectionG = 0;
+ chip->AD.DirectionB = 0;
+ chip->AD.GainR = 0;
+ chip->AD.GainG = 0;
+ chip->AD.GainB = 0;
+
+ chip->AD.OffsetR = 0;
+ chip->AD.OffsetG = 0;
+ chip->AD.OffsetB = 0;
+
+ chip->Scan.TotalMotorSteps = 60000;
+ chip->Scan.StartLine = 0;
+ chip->Scan.StartPixel = 0;
+
+ DBG (DBG_ASIC, "Asic_Reset: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_SetSource (PAsic chip, LIGHTSOURCE lsLightSource)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_SetSource: Enter\n");
+
+ chip->lsLightSource = lsLightSource;
+ switch (chip->lsLightSource)
+ {
+ case 1:
+ DBG (DBG_ASIC, "Asic_SetSource: Source is Reflect\n");
+ break;
+ case 2:
+ DBG (DBG_ASIC, "Asic_SetSource: Source is Postion\n");
+ break;
+ case 4:
+ DBG (DBG_ASIC, "Asic_SetSource: Source is Negtive\n");
+ break;
+ default:
+ DBG (DBG_ASIC, "Asic_SetSource: Source error\n");
+ }
+
+ DBG (DBG_ASIC, "Asic_SetSource: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_ScanStart (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_ScanStart: Enter\n");
+
+ if (chip->firmwarestate != FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_ScanStart: Scanner is not opened\n");
+ return STATUS_INVAL;
+ }
+
+ Mustek_SendData (chip, ES01_8B_Status, 0x1c | 0x20);
+ Mustek_WriteAddressLineForRegister (chip, 0x8B);
+ Mustek_ClearFIFO (chip);
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_ENABLE);
+
+
+ chip->firmwarestate = FS_SCANNING;
+
+ DBG (DBG_ASIC, "Asic_ScanStart: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_ScanStop (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte temps[2];
+ SANE_Byte buf[4];
+
+ DBG (DBG_ASIC, "Asic_ScanStop: Enter\n");
+
+ if (chip->firmwarestate < FS_SCANNING)
+ {
+ return status;
+ }
+
+ usleep (100 * 1000);
+
+ buf[0] = 0x02; /*stop */
+ buf[1] = 0x02;
+ buf[2] = 0x02;
+ buf[3] = 0x02;
+ status = WriteIOControl (chip, 0xc0, 0, 4, buf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_ScanStop: Stop scan error\n");
+ return status;
+ }
+
+ buf[0] = 0x00; /*clear */
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ status = WriteIOControl (chip, 0xc0, 0, 4, buf);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_ScanStop: Clear scan error\n");
+ return status;
+ }
+
+ status = Mustek_DMARead (chip, 2, temps);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Asic_ScanStop: DMAReadGeneralMode error\n");
+ return status;
+ }
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, 0);
+ Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle,
+ CLOSE_ALL_CLOCK_DISABLE);
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+ Mustek_ClearFIFO (chip);
+
+ chip->firmwarestate = FS_OPENED;
+ DBG (DBG_ASIC, "Asic_ScanStop: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_ReadImage (PAsic chip, SANE_Byte * pBuffer, unsigned short LinesCount)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned int dwXferBytes;
+
+ DBG (DBG_ASIC, "Asic_ReadImage: Enter : LinesCount = %d\n", LinesCount);
+
+ if (chip->firmwarestate != FS_SCANNING)
+ {
+ DBG (DBG_ERR, "Asic_ReadImage: Scanner is not scanning\n");
+ return STATUS_INVAL;
+ }
+
+ dwXferBytes = (unsigned int) (LinesCount) * chip->dwBytesCountPerRow;
+ DBG (DBG_ASIC, "Asic_ReadImage: chip->dwBytesCountPerRow = %d\n",
+ chip->dwBytesCountPerRow);
+
+ /* HOLD: an unsigned long can't be < 0
+ if (dwXferBytes < 0)
+ {
+ DBG (DBG_ASIC, "Asic_ReadImage: dwXferBytes <0\n");
+ return STATUS_INVAL;
+ }
+ */
+ if (dwXferBytes == 0)
+
+ {
+ DBG (DBG_ASIC, "Asic_ReadImage: dwXferBytes == 0\n");
+ return STATUS_GOOD;
+ }
+
+ status = Mustek_DMARead (chip, dwXferBytes, pBuffer);
+
+ DBG (DBG_ASIC, "Asic_ReadImage: Exit\n");
+ return status;
+}
+
+
+#if SANE_UNUSED
+static STATUS
+Asic_CheckFunctionKey (PAsic chip, SANE_Byte * key)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte bBuffer_1 = 0xff;
+ SANE_Byte bBuffer_2 = 0xff;
+
+ DBG (DBG_ASIC, "Asic_CheckFunctionKey: Enter\n");
+
+ if (chip->firmwarestate != FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_CheckFunctionKey: Scanner is not Opened\n");
+ return STATUS_INVAL;
+ }
+
+ Mustek_SendData (chip, ES01_97_GPIOControl0_7, 0x00);
+ Mustek_SendData (chip, ES01_95_GPIOValue0_7, 0x17);
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x00);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x08);
+
+ GetChipStatus (chip, 0x02, &bBuffer_1);
+ GetChipStatus (chip, 0x03, &bBuffer_2);
+
+
+ if (((0xff - bBuffer_1) & 0x10) == 0x10)
+ *key = 0x01;
+
+ if (((0xff - bBuffer_1) & 0x01) == 0x01)
+ *key = 0x02;
+ if (((0xff - bBuffer_1) & 0x04) == 0x04)
+ *key = 0x04;
+ if (((0xff - bBuffer_2) & 0x08) == 0x08)
+ *key = 0x08;
+ if (((0xff - bBuffer_1) & 0x02) == 0x02)
+ *key = 0x10;
+
+
+ DBG (DBG_ASIC, "CheckFunctionKey=%d\n", *key);
+ DBG (DBG_ASIC, "Asic_CheckFunctionKey: Exit\n");
+ return status;
+}
+#endif
+
+static STATUS
+Asic_IsTAConnected (PAsic chip, SANE_Bool * hasTA)
+{
+ SANE_Byte bBuffer_1 = 0xff;
+
+ DBG (DBG_ASIC, "Asic_IsTAConnected: Enter\n");
+
+ Mustek_SendData (chip, ES01_97_GPIOControl0_7, 0x00);
+ Mustek_SendData (chip, ES01_95_GPIOValue0_7, 0x00);
+
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x00);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00);
+
+
+ GetChipStatus (chip, 0x02, &bBuffer_1);
+
+ if (((0xff - bBuffer_1) & 0x08) == 0x08)
+ *hasTA = TRUE;
+ else
+ *hasTA = FALSE;
+
+ DBG (DBG_ASIC, "hasTA=%d\n", *hasTA);
+ DBG (DBG_ASIC, "Asic_IsTAConnected():Exit\n");
+ return STATUS_GOOD;
+}
+
+#if SANE_UNUSED
+static STATUS
+Asic_DownloadGammaTable (PAsic chip, void * lpBuffer)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_DownloadGammaTable()\n");
+
+ chip->lpGammaTable = lpBuffer;
+
+ DBG (DBG_ASIC, "Asic_DownloadGammaTable: Exit\n");
+ return status;
+}
+#endif
+
+static STATUS
+Asic_ReadCalibrationData (PAsic chip, void * pBuffer,
+ unsigned int dwXferBytes, SANE_Byte bScanBits)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Byte * pCalBuffer;
+ unsigned int dwTotalReadData;
+ unsigned int dwReadImageData;
+
+ DBG (DBG_ASIC, "Asic_ReadCalibrationData: Enter\n");
+
+ if (chip->firmwarestate != FS_SCANNING)
+ {
+ DBG (DBG_ERR, "Asic_ReadCalibrationData: Scanner is not scanning\n");
+ return STATUS_INVAL;
+ }
+
+ if (bScanBits == 24)
+ {
+ unsigned int i;
+ pCalBuffer = (SANE_Byte *) malloc (dwXferBytes);
+ if (pCalBuffer == NULL)
+ {
+ DBG (DBG_ERR,
+ "Asic_ReadCalibrationData: Can't malloc bCalBuffer memory\n");
+ return STATUS_MEM_ERROR;
+ }
+
+ for (dwTotalReadData = 0; dwTotalReadData < dwXferBytes;)
+ {
+ dwReadImageData = (dwXferBytes - dwTotalReadData) < 65536 ?
+ (dwXferBytes - dwTotalReadData) : 65536;
+
+ Mustek_DMARead (chip, dwReadImageData,
+ (SANE_Byte *) (pCalBuffer + dwTotalReadData));
+ dwTotalReadData += dwReadImageData;
+ }
+
+ dwXferBytes /= 3;
+ for (i = 0; i < dwXferBytes; i++)
+
+ {
+ *((SANE_Byte *) pBuffer + i) = *(pCalBuffer + i * 3);
+ *((SANE_Byte *) pBuffer + dwXferBytes + i) = *(pCalBuffer + i * 3 + 1);
+ *((SANE_Byte *) pBuffer + dwXferBytes * 2 + i) =
+ *(pCalBuffer + i * 3 + 2);
+ }
+ free (pCalBuffer);
+ }
+ else if (bScanBits == 8)
+ {
+ for (dwTotalReadData = 0; dwTotalReadData < dwXferBytes;)
+ {
+ dwReadImageData = (dwXferBytes - dwTotalReadData) < 65536 ?
+ (dwXferBytes - dwTotalReadData) : 65536;
+
+ Mustek_DMARead (chip, dwReadImageData,
+ (SANE_Byte *) pBuffer + dwTotalReadData);
+ dwTotalReadData += dwReadImageData;
+ }
+ }
+
+ DBG (DBG_ASIC, "Asic_ReadCalibrationData: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_SetMotorType (PAsic chip, SANE_Bool isMotorMove, SANE_Bool isUniformSpeed)
+{
+ STATUS status = STATUS_GOOD;
+ isUniformSpeed = isUniformSpeed;
+ DBG (DBG_ASIC, "Asic_SetMotorType:Enter\n");
+
+ if (isMotorMove)
+ {
+ chip->isMotorMove = MOTOR_0_ENABLE;
+ }
+ else
+ chip->isMotorMove = MOTOR_0_DISABLE;
+
+ DBG (DBG_ASIC, "isMotorMove=%d\n", chip->isMotorMove);
+ DBG (DBG_ASIC, "Asic_SetMotorType: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_MotorMove (PAsic chip, SANE_Bool isForward, unsigned int dwTotalSteps)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short *NormalMoveMotorTable;
+ LLF_CALCULATEMOTORTABLE CalMotorTable;
+ LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase;
+ LLF_SETMOTORTABLE LLF_SetMotorTable;
+ LLF_MOTORMOVE MotorMove;
+
+ DBG (DBG_ASIC, "Asic_MotorMove:Enter\n");
+
+ NormalMoveMotorTable = (unsigned short *) malloc (512 * 8 * 2);
+
+ CalMotorTable.StartSpeed = 5000;
+ CalMotorTable.EndSpeed = 1800;
+ CalMotorTable.AccStepBeforeScan = 511;
+ CalMotorTable.lpMotorTable = NormalMoveMotorTable;
+ LLFCalculateMotorTable (&CalMotorTable);
+
+ CurrentPhase.MotorDriverIs3967 = 0;
+ CurrentPhase.MotorCurrentTableA[0] = 200;
+ CurrentPhase.MotorCurrentTableB[0] = 200;
+ CurrentPhase.MoveType = _4_TABLE_SPACE_FOR_FULL_STEP;
+ LLFSetMotorCurrentAndPhase (chip, &CurrentPhase);
+
+ LLF_SetMotorTable.MotorTableAddress = 0;
+ LLF_SetMotorTable.MotorTablePtr = NormalMoveMotorTable;
+ LLFSetMotorTable (chip, &LLF_SetMotorTable);
+
+ free (NormalMoveMotorTable);
+
+ MotorMove.MotorSelect = MOTOR_0_ENABLE | MOTOR_1_DISABLE;
+ MotorMove.MotorMoveUnit = ES03_TABLE_DEFINE;
+ MotorMove.MotorSpeedUnit = SPEED_UNIT_1_PIXEL_TIME;
+ MotorMove.MotorSyncUnit = MOTOR_SYNC_UNIT_1_PIXEL_TIME;
+ MotorMove.HomeSensorSelect = HOME_SENSOR_0_ENABLE;
+ MotorMove.ActionMode = ACTION_MODE_ACCDEC_MOVE;
+ MotorMove.ActionType = isForward;
+
+ if (dwTotalSteps > 1000)
+ {
+ MotorMove.AccStep = 511;
+ MotorMove.DecStep = 255;
+ MotorMove.FixMoveSteps = dwTotalSteps - (511 + 255);
+ }
+ else
+ {
+
+ MotorMove.ActionMode = ACTION_MODE_UNIFORM_SPEED_MOVE;
+ MotorMove.AccStep = 1;
+ MotorMove.DecStep = 1;
+ MotorMove.FixMoveSteps = dwTotalSteps - 2;
+ }
+
+ MotorMove.FixMoveSpeed = 7000;
+ MotorMove.WaitOrNoWait = TRUE;
+
+ LLFMotorMove (chip, &MotorMove);
+
+ DBG (DBG_ASIC, "Asic_MotorMove: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_CarriageHome (PAsic chip, SANE_Bool isTA)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Bool LampHome, TAHome;
+ isTA = isTA;
+
+ DBG (DBG_ASIC, "Asic_CarriageHome:Enter\n");
+
+ status = IsCarriageHome (chip, &LampHome, &TAHome);
+ if (!LampHome)
+ {
+ status = MotorBackHome (chip, TRUE);
+ }
+
+ DBG (DBG_ASIC, "Asic_CarriageHome: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_SetShadingTable (PAsic chip, unsigned short * lpWhiteShading,
+ unsigned short * lpDarkShading,
+ unsigned short wXResolution, unsigned short wWidth, unsigned short wX)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short i, j, n;
+ unsigned short wValidPixelNumber;
+ double dbXRatioAdderDouble;
+ unsigned int wShadingTableSize;
+
+ wX = wX;
+ DBG (DBG_ASIC, "Asic_SetShadingTable:Enter\n");
+
+ if (chip->firmwarestate < FS_OPENED)
+
+ OpenScanChip (chip);
+ if (chip->firmwarestate == FS_SCANNING)
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+
+ if (wXResolution > 600)
+ dbXRatioAdderDouble = 1200 / wXResolution;
+ else
+ dbXRatioAdderDouble = 600 / wXResolution;
+
+ wValidPixelNumber = (unsigned short) ((wWidth + 4) * dbXRatioAdderDouble);
+ DBG (DBG_ASIC, "wValidPixelNumber = %d\n", wValidPixelNumber);
+
+ /* clear old Shading table, if it has. */
+ /* first 4 element and lastest 5 of Shading table can't been used */
+ wShadingTableSize = (ShadingTableSize (wValidPixelNumber)) * sizeof (unsigned short);
+ if (chip->lpShadingTable != NULL)
+ {
+ free (chip->lpShadingTable);
+
+ }
+
+ DBG (DBG_ASIC, "Alloc a new shading table= %d Byte!\n", wShadingTableSize);
+ chip->lpShadingTable = (SANE_Byte *) malloc (wShadingTableSize);
+ if (chip->lpShadingTable == NULL)
+ {
+ DBG (DBG_ASIC, "lpShadingTable == NULL\n");
+ return STATUS_MEM_ERROR;
+ }
+
+ n = 0;
+ for (i = 0; i <= (wValidPixelNumber / 40); i++)
+ {
+ if (i < (wValidPixelNumber / 40))
+ {
+ for (j = 0; j < 40; j++)
+ {
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6) =
+ *((unsigned short *) lpDarkShading + n * 3);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 2) =
+ *((unsigned short *) lpDarkShading + n * 3 + 1);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 4) =
+ *((unsigned short *) lpDarkShading + n * 3 + 2);
+
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 1) =
+ *((unsigned short *) lpWhiteShading + n * 3);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 3) =
+ *((unsigned short *) lpWhiteShading + n * 3 + 1);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 5) =
+ *((unsigned short *) lpWhiteShading + n * 3 + 2);
+ if ((j % (unsigned short) dbXRatioAdderDouble) ==
+ (dbXRatioAdderDouble - 1))
+ n++;
+
+ if (i == 0 && j < 4 * dbXRatioAdderDouble)
+ n = 0;
+ }
+ }
+ else
+ {
+ for (j = 0; j < (wValidPixelNumber % 40); j++)
+ {
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6) =
+ *((unsigned short *) lpDarkShading + (n) * 3);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 2) =
+ *((unsigned short *) lpDarkShading + (n) * 3 + 1);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 4) =
+ *((unsigned short *) lpDarkShading + (n) * 3 + 2);
+
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 1) =
+ *((unsigned short *) lpWhiteShading + (n) * 3);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 3) =
+ *((unsigned short *) lpWhiteShading + (n) * 3 + 1);
+ *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 5) =
+ *((unsigned short *) lpWhiteShading + (n) * 3 + 2);
+
+ if ((j % (unsigned short) dbXRatioAdderDouble) ==
+ (dbXRatioAdderDouble - 1))
+ n++;
+
+ if (i == 0 && j < 4 * dbXRatioAdderDouble)
+ n = 0;
+ }
+ }
+ }
+
+ DBG (DBG_ASIC, "Asic_SetShadingTable: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_WaitCarriageHome (PAsic chip, SANE_Bool isTA)
+{
+ STATUS status = STATUS_GOOD;
+ SANE_Bool LampHome, TAHome;
+ int i;
+
+ isTA = isTA;
+
+ DBG (DBG_ASIC, "Asic_WaitCarriageHome:Enter\n");
+
+ for (i = 0; i < 100; i++)
+ {
+ status = IsCarriageHome (chip, &LampHome, &TAHome);
+ if (LampHome)
+ break;
+ usleep (300000);
+ }
+ if (i == 100)
+ status = STATUS_DEVICE_BUSY;
+ DBG (DBG_ASIC, "Wait %d s\n", (unsigned short) (i * 0.3));
+
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+ chip->firmwarestate = FS_OPENED;
+ chip->motorstate = MS_STILL;
+
+ DBG (DBG_ASIC, "Asic_WaitCarriageHome: Exit\n");
+ return status;
+}
+
+static STATUS
+Asic_SetCalibrate (PAsic chip, SANE_Byte bScanBits, unsigned short wXResolution,
+ unsigned short wYResolution, unsigned short wX, unsigned short wY,
+ unsigned short wWidth, unsigned short wLength, SANE_Bool isShading)
+{
+ STATUS status = STATUS_GOOD;
+ unsigned short ValidPixelNumber;
+
+ unsigned short wPerLineNeedBufferSize = 0;
+ unsigned short BytePerPixel = 0;
+ unsigned int dwTotal_PerLineNeedBufferSize = 0;
+ unsigned int dwTotalLineTheBufferNeed = 0;
+ unsigned short dwTotal_CCDResolution = 0;
+ unsigned short wThinkCCDResolution = 0;
+ unsigned short wCCD_PixelNumber = 0;
+ unsigned short wScanAccSteps = 1;
+ SANE_Byte byScanDecSteps = 1;
+ unsigned int dwLineWidthPixel = 0;
+
+ unsigned short wNowMotorDPI;
+ unsigned short XRatioTypeWord;
+ double XRatioTypeDouble;
+ double XRatioAdderDouble;
+
+ LLF_MOTORMOVE *lpMotorStepsTable =
+ (LLF_MOTORMOVE *) malloc (sizeof (LLF_MOTORMOVE));
+
+ SANE_Byte byDummyCycleNum = 1;
+ unsigned short Total_MotorDPI;
+ unsigned short BeforeScanFixSpeedStep = 0;
+ unsigned short BackTrackFixSpeedStep = 20;
+ unsigned short wMultiMotorStep = 1;
+ SANE_Byte bMotorMoveType = _MOTOR_MOVE_TYPE;
+ SANE_Byte isMotorMoveToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_DISABLE;
+ SANE_Byte isUniformSpeedToScan = UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE;
+ SANE_Byte isScanBackTracking = SCAN_BACK_TRACKING_DISABLE;
+ unsigned int TotalStep = 0;
+ unsigned short StartSpeed, EndSpeed;
+ LLF_CALCULATEMOTORTABLE CalMotorTable;
+ LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase;
+ LLF_RAMACCESS RamAccess;
+ unsigned int dwStartAddr, dwEndAddr, dwTableBaseAddr;
+ SANE_Byte byClear_Pulse_Width = 0;
+ unsigned int dwLinePixelReport = 0;
+ SANE_Byte byPHTG_PulseWidth, byPHTG_WaitWidth;
+ unsigned short * lpMotorTable = (unsigned short *) malloc (512 * 8 * 2);
+ unsigned int RealTableSize;
+ unsigned short wFullBank;
+
+ DBG (DBG_ASIC, "Asic_SetCalibrate: Enter\n");
+ DBG (DBG_ASIC,
+ "bScanBits=%d,wXResolution=%d, wYResolution=%d, wX=%d, wY=%d, wWidth=%d, wLength=%d\n",
+ bScanBits, wXResolution, wYResolution, wX, wY, wWidth, wLength);
+
+ if (chip->firmwarestate != FS_OPENED)
+ {
+ DBG (DBG_ERR, "Asic_SetCalibrate: Scanner is not opened\n");
+ return STATUS_INVAL;
+ }
+
+ if (lpMotorStepsTable == NULL)
+ {
+ DBG (DBG_ERR, "Asic_SetCalibrate: insufficiency memory!\n");
+ return STATUS_INVAL;
+ }
+ DBG (DBG_ASIC, "malloc LLF_MOTORMOVE =%ld Byte\n", (long int) (sizeof (LLF_MOTORMOVE)));
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, 0);
+ Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle,
+ CLOSE_ALL_CLOCK_DISABLE);
+ Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE);
+
+ status = Asic_WaitUnitReady (chip);
+
+ Mustek_SendData (chip, 0x1CD, 0);
+
+ Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27 | 64 | 128);
+
+ if (bScanBits > 24)
+ {
+ wPerLineNeedBufferSize = wWidth * 6;
+ BytePerPixel = 6;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 6;
+ }
+ else if (bScanBits == 24)
+ {
+ wPerLineNeedBufferSize = wWidth * 3;
+ chip->dwCalibrationBytesCountPerRow = wWidth * 3;
+ BytePerPixel = 3;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 3;
+ }
+ else if ((bScanBits > 8) && (bScanBits <= 16))
+ {
+ wPerLineNeedBufferSize = wWidth * 2;
+ BytePerPixel = 2;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 2;
+ }
+ else if ((bScanBits == 8))
+ {
+ wPerLineNeedBufferSize = wWidth;
+ BytePerPixel = 1;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth);
+ }
+ else if ((bScanBits < 8))
+ {
+ wPerLineNeedBufferSize = wWidth >> 3;
+ BytePerPixel = 1;
+ chip->dwBytesCountPerRow = (unsigned int) (wWidth);
+ }
+ DBG (DBG_ASIC,
+ "wPerLineNeedBufferSize=%d,BytePerPixel=%d,dwBytesCountPerRow=%d\n",
+ wPerLineNeedBufferSize, BytePerPixel, chip->dwBytesCountPerRow);
+
+
+ dwTotal_PerLineNeedBufferSize = wPerLineNeedBufferSize;
+ dwTotalLineTheBufferNeed = wLength;
+ DBG (DBG_ASIC, "wPerLineNeedBufferSize=%d,wLength=%d\n",
+ wPerLineNeedBufferSize, wLength);
+
+
+ chip->Scan.Dpi = wXResolution;
+ CCDTiming (chip);
+
+ dwTotal_CCDResolution = SENSOR_DPI;
+ if (chip->lsLightSource == LS_REFLECTIVE)
+ {
+ if (wXResolution > (dwTotal_CCDResolution / 2))
+ {
+ wThinkCCDResolution = dwTotal_CCDResolution;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01);
+ wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_1200;
+ }
+ else
+ {
+ wThinkCCDResolution = dwTotal_CCDResolution / 2;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00);
+ wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_600;
+ }
+ }
+ else
+ {
+ if (wXResolution > (dwTotal_CCDResolution / 2))
+ {
+ wThinkCCDResolution = dwTotal_CCDResolution;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01);
+ wCCD_PixelNumber = TA_CAL_PIXELNUMBER;
+ }
+ else
+ {
+ wThinkCCDResolution = dwTotal_CCDResolution / 2;
+ Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01);
+ Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00);
+ wCCD_PixelNumber = TA_CAL_PIXELNUMBER;
+ }
+ }
+ DBG (DBG_ASIC, "wThinkCCDResolution=%d,wCCD_PixelNumber=%d\n",
+ wThinkCCDResolution, wCCD_PixelNumber);
+
+ dwLineWidthPixel = wWidth;
+
+ if (isShading)
+ wYResolution = 600;
+ DBG (DBG_ASIC, "dwLineWidthPixel=%d,wYResolution=%d\n", dwLineWidthPixel,
+ wYResolution);
+
+ SetLineTimeAndExposure (chip);
+ if (wYResolution == 600)
+ {
+ Mustek_SendData (chip, ES01_CB_CCDDummyCycleNumber, byDummyCycleNum);
+ DBG (DBG_ASIC, "Find Boundary CCDDummyCycleNumber == %d\n",
+ byDummyCycleNum);
+ }
+
+ SetLEDTime (chip);
+
+ /* calculate Y ratio */
+ Total_MotorDPI = 1200;
+
+ wNowMotorDPI = Total_MotorDPI;
+ DBG (DBG_ASIC, "wNowMotorDPI=%d\n", wNowMotorDPI);
+
+ /* SetADConverter */
+ Mustek_SendData (chip, ES01_74_HARDWARE_SETTING,
+ MOTOR1_SERIAL_INTERFACE_G10_8_ENABLE | LED_OUT_G11_DISABLE
+ | SLAVE_SERIAL_INTERFACE_G15_14_DISABLE |
+ SHUTTLE_CCD_DISABLE);
+
+ /* set AFE */
+ Mustek_SendData (chip, ES01_9A_AFEControl,
+ AD9826_AFE | AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE);
+ Mustek_SendData (chip, ES01_F7_DigitalControl, DIGITAL_REDUCE_DISABLE);
+
+ XRatioTypeDouble = wXResolution;
+ XRatioTypeDouble /= wThinkCCDResolution;
+ XRatioAdderDouble = 1 / XRatioTypeDouble;
+ XRatioTypeWord = (unsigned short) (XRatioTypeDouble * 32768);
+
+ XRatioAdderDouble = (double) (XRatioTypeWord) / 32768;
+ XRatioAdderDouble = 1 / XRatioAdderDouble;
+
+ Mustek_SendData (chip, ES01_9E_HorizontalRatio1to15LSB,
+ LOBYTE (XRatioTypeWord));
+ Mustek_SendData (chip, ES01_9F_HorizontalRatio1to15MSB,
+ HIBYTE (XRatioTypeWord));
+ DBG (DBG_ASIC,
+ "XRatioTypeDouble=%.2f,XRatioAdderDouble=%.2f,XRatioTypeWord=%d\n",
+ XRatioTypeDouble, XRatioAdderDouble, XRatioTypeWord);
+
+ if (chip->isMotorMove == MOTOR_0_ENABLE)
+ {
+ Mustek_SendData (chip, ES01_A6_MotorOption, MOTOR_0_ENABLE |
+ MOTOR_1_DISABLE |
+ HOME_SENSOR_0_ENABLE | ES03_TABLE_DEFINE);
+ }
+ else
+ {
+ Mustek_SendData (chip, ES01_A6_MotorOption, MOTOR_0_DISABLE |
+ MOTOR_1_DISABLE |
+ HOME_SENSOR_0_ENABLE | ES03_TABLE_DEFINE);
+ }
+ DBG (DBG_ASIC, "isMotorMove=%d\n", chip->isMotorMove);
+
+ Mustek_SendData (chip, ES01_F6_MorotControl1, SPEED_UNIT_1_PIXEL_TIME |
+ MOTOR_SYNC_UNIT_1_PIXEL_TIME);
+
+ wScanAccSteps = 1;
+ byScanDecSteps = 1;
+ DBG (DBG_ASIC, "wScanAccSteps=%d,byScanDecSteps=%d\n", wScanAccSteps,
+ byScanDecSteps);
+
+ Mustek_SendData (chip, ES01_AE_MotorSyncPixelNumberM16LSB, LOBYTE (0));
+ Mustek_SendData (chip, ES01_AF_MotorSyncPixelNumberM16MSB, HIBYTE (0));
+ DBG (DBG_ASIC, "MotorSyncPixelNumber=%d\n", 0);
+
+ Mustek_SendData (chip, ES01_EC_ScanAccStep0_7, LOBYTE (wScanAccSteps));
+ Mustek_SendData (chip, ES01_ED_ScanAccStep8_8, HIBYTE (wScanAccSteps));
+ DBG (DBG_ASIC, "wScanAccSteps=%d\n", wScanAccSteps);
+
+ DBG (DBG_ASIC, "BeforeScanFixSpeedStep=%d,BackTrackFixSpeedStep=%d\n",
+ BeforeScanFixSpeedStep, BackTrackFixSpeedStep);
+
+ Mustek_SendData (chip, ES01_EE_FixScanStepLSB,
+ LOBYTE (BeforeScanFixSpeedStep));
+ Mustek_SendData (chip, ES01_8A_FixScanStepMSB,
+ HIBYTE (BeforeScanFixSpeedStep));
+ DBG (DBG_ASIC, "BeforeScanFixSpeedStep=%d\n", BeforeScanFixSpeedStep);
+
+ Mustek_SendData (chip, ES01_EF_ScanDecStep, byScanDecSteps);
+ DBG (DBG_ASIC, "byScanDecSteps=%d\n", byScanDecSteps);
+
+ Mustek_SendData (chip, ES01_E6_ScanBackTrackingStepLSB,
+ LOBYTE (BackTrackFixSpeedStep));
+ Mustek_SendData (chip, ES01_E7_ScanBackTrackingStepMSB,
+ HIBYTE (BackTrackFixSpeedStep));
+ DBG (DBG_ASIC, "BackTrackFixSpeedStep=%d\n", BackTrackFixSpeedStep);
+
+ Mustek_SendData (chip, ES01_E8_ScanRestartStepLSB,
+ LOBYTE (BackTrackFixSpeedStep));
+ Mustek_SendData (chip, ES01_E9_ScanRestartStepMSB,
+ HIBYTE (BackTrackFixSpeedStep));
+ DBG (DBG_ASIC, "BackTrackFixSpeedStep=%d\n", BackTrackFixSpeedStep);
+
+ switch (bMotorMoveType)
+ {
+ case _4_TABLE_SPACE_FOR_FULL_STEP:
+ wMultiMotorStep = 1;
+ break;
+
+ case _8_TABLE_SPACE_FOR_1_DIV_2_STEP:
+ wMultiMotorStep = 2;
+ break;
+
+ case _16_TABLE_SPACE_FOR_1_DIV_4_STEP:
+ wMultiMotorStep = 4;
+ break;
+
+ case _32_TABLE_SPACE_FOR_1_DIV_8_STEP:
+ wMultiMotorStep = 8;
+ break;
+ }
+ DBG (DBG_ASIC, "wMultiMotorStep=%d\n", wMultiMotorStep);
+
+ TotalStep = wScanAccSteps + BeforeScanFixSpeedStep +
+ (dwTotalLineTheBufferNeed * wNowMotorDPI / wYResolution) + byScanDecSteps;
+ DBG (DBG_ASIC, "TotalStep=%d\n", TotalStep);
+
+ Mustek_SendData (chip, ES01_F0_ScanImageStep0_7, (SANE_Byte) (TotalStep));
+ Mustek_SendData (chip, ES01_F1_ScanImageStep8_15, (SANE_Byte) (TotalStep >> 8));
+ Mustek_SendData (chip, ES01_F2_ScanImageStep16_19,
+ (SANE_Byte) (TotalStep >> 16));
+
+ SetScanMode (chip, bScanBits);
+
+ DBG (DBG_ASIC,
+ "isMotorMoveToFirstLine=%d,isUniformSpeedToScan=%d,isScanBackTracking=%d\n",
+ isMotorMoveToFirstLine, isUniformSpeedToScan, isScanBackTracking);
+
+
+ Mustek_SendData (chip, ES01_F3_ActionOption, isMotorMoveToFirstLine |
+ MOTOR_BACK_HOME_AFTER_SCAN_DISABLE |
+ SCAN_ENABLE |
+ isScanBackTracking |
+ INVERT_MOTOR_DIRECTION_DISABLE |
+ isUniformSpeedToScan |
+ ES01_STATIC_SCAN_DISABLE | MOTOR_TEST_LOOP_DISABLE);
+
+ if (chip->lsLightSource == LS_REFLECTIVE)
+ Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT,
+ ES01_SHADING_3_INT_13_DEC);
+ else
+ Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT,
+ ES01_SHADING_4_INT_12_DEC);
+
+ SetPackAddress (chip, wXResolution, wWidth, wX, XRatioAdderDouble,
+ XRatioTypeDouble, byClear_Pulse_Width, &ValidPixelNumber);
+ SetExtraSetting (chip, wXResolution, wCCD_PixelNumber, TRUE);
+
+ byPHTG_PulseWidth = chip->Timing.PHTG_PluseWidth;
+ byPHTG_WaitWidth = chip->Timing.PHTG_WaitWidth;
+ dwLinePixelReport = ((byClear_Pulse_Width + 1) * 2 +
+ (byPHTG_PulseWidth + 1) * (1) +
+ (byPHTG_WaitWidth + 1) * (1) +
+ (wCCD_PixelNumber + 1)) * (byDummyCycleNum + 1);
+
+ DBG (DBG_ASIC, "Motor Time = %d\n",
+ (dwLinePixelReport * wYResolution / wNowMotorDPI));
+ if ((dwLinePixelReport * wYResolution / wNowMotorDPI) > 64000)
+ {
+ DBG (DBG_ASIC, "Motor Time Over Flow !!!\n");
+ }
+
+ EndSpeed = (unsigned short) (dwLinePixelReport / (wNowMotorDPI / wYResolution));
+
+ if (wXResolution > 600)
+ {
+ StartSpeed = EndSpeed;
+ }
+ else
+ {
+ StartSpeed = EndSpeed + 3500;
+ }
+ DBG (DBG_ASIC, "StartSpeed =%d, EndSpeed = %d\n", StartSpeed, EndSpeed);
+
+
+ Mustek_SendData (chip, ES01_FD_MotorFixedspeedLSB, LOBYTE (EndSpeed));
+ Mustek_SendData (chip, ES01_FE_MotorFixedspeedMSB, HIBYTE (EndSpeed));
+ memset (lpMotorTable, 0, 512 * 8 * sizeof (unsigned short));
+
+ CalMotorTable.StartSpeed = StartSpeed;
+ CalMotorTable.EndSpeed = EndSpeed;
+ CalMotorTable.AccStepBeforeScan = wScanAccSteps;
+ CalMotorTable.lpMotorTable = lpMotorTable;
+
+ LLFCalculateMotorTable (&CalMotorTable);
+
+ CurrentPhase.MotorDriverIs3967 = 0;
+ CurrentPhase.FillPhase = 1;
+ CurrentPhase.MoveType = bMotorMoveType;
+ CurrentPhase.MotorCurrentTableA[0] = 200;
+ CurrentPhase.MotorCurrentTableB[0] = 200;
+ LLFSetMotorCurrentAndPhase (chip, &CurrentPhase);
+
+ RealTableSize = 512 * 8;
+ dwTableBaseAddr = PackAreaStartAddress - RealTableSize;
+
+ dwStartAddr = dwTableBaseAddr;
+
+ RamAccess.ReadWrite = WRITE_RAM;
+ RamAccess.IsOnChipGamma = EXTERNAL_RAM;
+ RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns;
+ RamAccess.LoStartAddress = (unsigned short) (dwStartAddr);
+ RamAccess.HiStartAddress = (unsigned short) (dwStartAddr >> 16);
+ RamAccess.RwSize = RealTableSize * 2;
+ RamAccess.BufferPtr = (SANE_Byte *) lpMotorTable;
+ LLFRamAccess (chip, &RamAccess);
+
+ Mustek_SendData (chip, ES01_9D_MotorTableAddrA14_A21,
+ (SANE_Byte) (dwTableBaseAddr >> TABLE_OFFSET_BASE));
+
+ dwEndAddr = PackAreaStartAddress - (512 * 8) - 1;
+
+ Mustek_SendData (chip, ES01_FB_BufferEmptySize16WordLSB,
+ LOBYTE (WaitBufferOneLineSize >> (7 - 3)));
+ Mustek_SendData (chip, ES01_FC_BufferEmptySize16WordMSB,
+ HIBYTE (WaitBufferOneLineSize >> (7 - 3)));
+
+ wFullBank =
+ (unsigned short) ((dwEndAddr -
+ (((dwLineWidthPixel * BytePerPixel) / 2) * 3 * 1)) / BANK_SIZE);
+ Mustek_SendData (chip, ES01_F9_BufferFullSize16WordLSB, LOBYTE (wFullBank));
+ Mustek_SendData (chip, ES01_FA_BufferFullSize16WordMSB, HIBYTE (wFullBank));
+
+ Mustek_SendData (chip, ES01_DB_PH_RESET_EDGE_TIMING_ADJUST, 0x00);
+
+ LLFSetRamAddress (chip, 0x0, dwEndAddr, ACCESS_DRAM);
+
+ Mustek_SendData (chip, ES01_DC_CLEAR_EDGE_TO_PH_TG_EDGE_WIDTH, 0);
+
+ Mustek_SendData (chip, ES01_00_ADAFEConfiguration, 0x70);
+ Mustek_SendData (chip, ES01_02_ADAFEMuxConfig, 0x80);
+
+
+ free (lpMotorTable);
+ free (lpMotorStepsTable);
+
+ DBG (DBG_ASIC, "Asic_SetCalibrate: Exit\n");
+ return status;
+}
+
+
+static STATUS
+Asic_SetAFEGainOffset (PAsic chip)
+{
+ STATUS status = STATUS_GOOD;
+ DBG (DBG_ASIC, "Asic_SetAFEGainOffset:Enter\n");
+
+ status = SetAFEGainOffset (chip);
+
+ DBG (DBG_ASIC, "Asic_SetAFEGainOffset: Exit\n");
+ return status;
+}
diff --git a/backend/mustek_usb2_asic.h b/backend/mustek_usb2_asic.h
new file mode 100644
index 0000000..ffc92ff
--- /dev/null
+++ b/backend/mustek_usb2_asic.h
@@ -0,0 +1,1421 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Mustek.
+ Originally maintained by Mustek
+ Author:Roy 2005.5.24
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#ifndef MUSTEK_USB2_ASIC_H
+#define MUSTEK_USB2_ASIC_H
+
+#include "../include/sane/sanei_usb.h"
+
+/* ---------------------- low level asic defines -------------------------- */
+
+#define TRUE 1
+#define FALSE 0
+
+#define _MAX(a,b) ((a)>(b)?(a):(b))
+#define _MIN(a,b) ((a)<(b)?(a):(b))
+
+#ifndef LOBYTE
+#define LOBYTE(w) (SANE_Byte)((unsigned short)(w) & 0x00ff)
+#endif
+
+#ifndef HIBYTE
+#define HIBYTE(w) (SANE_Byte)((unsigned short)(w)>>8 & 0x00ff)
+#endif
+
+
+typedef enum tagFIRMWARESTATE
+{
+ FS_NULL = 0,
+ FS_ATTACHED = 1,
+ FS_OPENED = 2,
+ FS_SCANNING = 3
+} FIRMWARESTATE, *LPFIRMWARESTATE;
+
+typedef enum tagMOTORSTATE
+{
+ MS_STILL = 0,
+ MS_MOVED = 1
+} MOTORSTATE, *LPMOTORSTATE;
+
+typedef enum tagUSBHOST
+{
+ HT_USB10 = 0,
+ HT_USB20 = 1
+} USBHOST;
+
+typedef enum tagLIGHTSOURCE
+{
+ LS_REFLECTIVE = 1,
+ LS_POSITIVE = 2,
+ LS_NEGATIVE = 4
+} LIGHTSOURCE;
+
+typedef struct
+{
+ unsigned int LongX;
+ unsigned int PicWidth;
+ unsigned int PicHeight;
+
+ unsigned int Top;
+ unsigned int Bottom;
+ unsigned int Left;
+ unsigned int Right;
+ unsigned int ScanMode;
+ unsigned int Dpi;
+ unsigned int TotalMotorSteps;
+
+ unsigned int CCD_Pixel_Length;
+ SANE_Byte LineGap;
+ SANE_Byte TG_Pulse_Width_Pixel;
+ SANE_Byte TG_Wait_Width_Pixel;
+ unsigned short Multi_TG_Dummy_Pixel;
+ unsigned short CCD_Dummy_Pixel;
+ SANE_Byte Dummy_Cycle;
+ SANE_Byte TG_Times;
+
+ double LineTime;
+
+ unsigned short StartPixel;
+ unsigned short StartLine;
+}
+ScanParam;
+
+typedef struct
+{
+ unsigned int Shading_Table_Size;
+ unsigned int Image_Buffer_Size;
+ unsigned int Full_Bank;
+ unsigned int Line_Pixel;
+ double Line_Time;
+ SANE_Byte LineGap;
+}
+Temps;
+
+typedef struct
+{
+ /* AFE */
+ unsigned int AFE_ADCCLK_Timing;
+ unsigned int AFE_ADCVS_Timing;
+ unsigned int AFE_ADCRS_Timing;
+ unsigned short AFE_ChannelA_LatchPos;
+ unsigned short AFE_ChannelB_LatchPos;
+ unsigned short AFE_ChannelC_LatchPos;
+ unsigned short AFE_ChannelD_LatchPos;
+ SANE_Byte AFE_Secondary_FF_LatchPos;
+ /* Sensor */
+ unsigned int CCD_DummyCycleTiming;
+ SANE_Byte PHTG_PluseWidth;
+ SANE_Byte PHTG_WaitWidth;
+ unsigned short ChannelR_StartPixel;
+ unsigned short ChannelR_EndPixel;
+ unsigned short ChannelG_StartPixel;
+ unsigned short ChannelG_EndPixel;
+ unsigned short ChannelB_StartPixel;
+ unsigned short ChannelB_EndPixel;
+ SANE_Byte PHTG_TimingAdj;
+ SANE_Byte PHTG_TimingSetup;
+
+ /*1200dpi */
+ unsigned int CCD_PHRS_Timing_1200;
+ unsigned int CCD_PHCP_Timing_1200;
+ unsigned int CCD_PH1_Timing_1200;
+ unsigned int CCD_PH2_Timing_1200;
+ SANE_Byte DE_CCD_SETUP_REGISTER_1200;
+ unsigned short wCCDPixelNumber_1200;
+
+ /*600dpi */
+ unsigned int CCD_PHRS_Timing_600;
+ unsigned int CCD_PHCP_Timing_600;
+ unsigned int CCD_PH1_Timing_600;
+ unsigned int CCD_PH2_Timing_600;
+ SANE_Byte DE_CCD_SETUP_REGISTER_600;
+ unsigned short wCCDPixelNumber_600;
+} Timings;
+
+
+typedef struct tagADConverter
+{
+ SANE_Byte GainR;
+ SANE_Byte GainG;
+ SANE_Byte GainB;
+ SANE_Byte OffsetR;
+ SANE_Byte OffsetG;
+ SANE_Byte OffsetB;
+ SANE_Bool DirectionR;
+ SANE_Bool DirectionG;
+ SANE_Bool DirectionB;
+} ADConverter, LPADConverter;
+
+
+
+typedef struct
+{
+ unsigned int Shading;
+ SANE_Byte Shading_0;
+ SANE_Byte Shading_1;
+ SANE_Byte Shading_2;
+
+ unsigned int Motor;
+ SANE_Byte Motor_0;
+ SANE_Byte Motor_1;
+ SANE_Byte Motor_2;
+
+ SANE_Byte ImageEndAddr_0;
+ SANE_Byte ImageEndAddr_1;
+ SANE_Byte ImageEndAddr_2;
+
+ SANE_Byte ImageFullBank_0;
+ SANE_Byte ImageFullBank_1;
+}
+RamPosition;
+
+typedef enum tagTASSTATUS
+{
+ TA_NOT_PLUGIN = 0,
+ TA_PLUGIN = 1,
+ TA_UNKNOW = 2
+} TASTATUS;
+
+typedef struct
+{
+ int fd; /* File Description of Scanner */
+
+ FIRMWARESTATE firmwarestate; /* record firmware state */
+ MOTORSTATE motorstate; /* record motor status */
+ SANE_Bool isFirstOpenChip; /* If first open chip, is TRUE */
+ USBHOST UsbHost; /* The type of USB port */
+ LIGHTSOURCE lsLightSource; /* light source of scanner */
+ ScanParam Scan; /* The parameters of Scan */
+
+ unsigned int dwBytesCountPerRow;
+ unsigned int dwCalibrationBytesCountPerRow;
+
+ Temps Temp;
+ Timings Timing;
+ ADConverter AD;
+
+ SANE_Bool isHardwareShading;
+
+ RamPosition RamPositions;
+
+ unsigned short * lpGammaTable;
+ SANE_Byte isMotorMove;
+
+ unsigned int ibase1;
+ unsigned int ibase2;
+
+ unsigned short SWWidth;
+
+ TASTATUS TA_Status;
+
+ SANE_Byte isMotorGoToFirstLine; /*Roy add */
+ SANE_Byte * lpShadingTable; /*Roy add */
+ SANE_Byte isUniformSpeedToScan;
+}
+Asic, *PAsic;
+
+typedef enum
+{
+ STATUS_GOOD = 0,
+ STATUS_CANCELLED,
+ STATUS_EOF,
+ STATUS_DEVICE_BUSY,
+ STATUS_INVAL,
+ STATUS_MEM_ERROR,
+ STATUS_IO_ERROR,
+ STATUS_ACCESS_ERROR
+}
+STATUS;
+
+
+/* For ScanObj */
+typedef struct Point
+{
+ unsigned int x;
+ unsigned int y;
+}
+Point;
+
+typedef struct Rect
+{
+ unsigned int left;
+ unsigned int right;
+ unsigned int top;
+ unsigned int bottom;
+}
+Rect;
+
+typedef struct RGBColor
+{
+ unsigned short Red;
+ unsigned short Green;
+ unsigned short Blue;
+}
+RGBColor;
+
+/* debug levels */
+#define DBG_CRIT 0 /* Critical errors thatshould be printed even
+ if user hasn't enabled debugging -- use
+ with care and only after sane_open has been
+ called */
+#define DBG_ERR 1 /* Other errors */
+#define DBG_WARN 2 /* unusual conditions that may not be fatal */
+#define DBG_INFO 3 /* information useful for the deucated user */
+#define DBG_DET 4 /* more detailed information */
+#define DBG_FUNC 5 /* start and exits of high level functions */
+#define DBG_ASIC 6 /* starts and exits of low level functions */
+#define DBG_DBG 10 /* usefull only for tracing bugs */
+
+
+#define DPI_2400 0x8000
+#define DPI_1200 0x8000
+#define DPI_600 0x8000
+#define DPI_300 0x4000
+#define DPI_200 0x2aaa
+#define DPI_150 0x2000
+#define DPI_100 0x1555
+#define DPI_75 0x1000
+#define DPI_50 0xaaa
+#define PIXEL_TIME 333 /*unit : ms */
+#define DRAM_1Mx16_SIZE (1024*1024) /*unit : word */
+#define PackAreaStartAddress ((DRAM_1Mx16_SIZE/4)*3)
+
+#define TEMP_MEMORY_SIZE_64K 64*1024
+
+#define CALIBRATION_PIXEL_WIDTH 10240 /*need 512x */
+#define CALIBRATE_WHITE_LINECOUNT 40
+#define CALIBRATE_DARK_LINECOUNT 2
+
+#define ACTION_MODE_ACCDEC_MOVE 0
+#define ACTION_MODE_UNIFORM_SPEED_MOVE 1
+
+#define ACTION_TYPE_BACKWARD 0
+#define ACTION_TYPE_FORWARD 1
+#define ACTION_TYPE_BACKTOHOME 2
+#define ACTION_TYPE_TEST_MODE 3
+
+
+#define SENSOR0_DETECTED 0x10
+#define SENSOR1_DETECTED 0x20
+#define SENSOR0AND1_DETECTED 0x30
+
+#define READ_RAM 0
+#define WRITE_RAM 1
+
+#define EXTERNAL_RAM 0
+#define ON_CHIP_PRE_GAMMA 1
+#define ON_CHIP_FINAL_GAMMA 2
+
+#define MOTOR_TABLE_SIZE 512*8
+
+#define ValidPixelNumberFor600DPI 5100 + 50 + 250
+#define ValidPixelNumberFor1200DPI 10200 + 100 + 500
+
+#define OverLapPixelNumber600 0
+#define OverLapPixelNumber1200 0
+#define SegmentGap 0
+#define BANK_SIZE (64)
+
+#define WaitBufferOneLineSize 11000*6
+
+#define CCD_PIXEL_NUMBER 21600
+#define CCD_Line_Spacing 24
+#define CCD_EvneOdd_Spacing 2
+
+#define ShadingTableSize(x) ( ((x + 10)*6) + ( ((x + 10)*6)/240)*16 )
+#define ACC_DEC_STEP_TABLE_SIZE (512) /*unit : word */
+#define TableBase(x) ((((x)+((1<<TABLE_OFFSET_BASE)-1))>>TABLE_OFFSET_BASE)<<TABLE_OFFSET_BASE)
+#define NUM_OF_ACC_DEC_STEP_TABLE (8) /*unit : word */
+#define TABLE_BASE_SIZE (1024*2*2*2*2) /*unit : word */
+#define LAMP0_PWM_DEFAULT 255
+#define LAMP1_PWM_DEFAULT 255
+
+#define TABLE_OFFSET_BASE (14) /*unit : word */
+#define CHECK_HOME_SLEEP_TIME 100
+
+#define _MOTOR_MOVE_TYPE _4_TABLE_SPACE_FOR_FULL_STEP
+
+
+#define TA_CAL_PIXELNUMBER 50000
+
+#define SENSOR_DPI 1200
+#define TA_IMAGE_PIXELNUMBER 61000
+#define MAX_PATH 256
+
+
+/**************************** ASIC registers ***********************/
+
+/*ES01_XX = Easy Scan_01 register hex xx*/
+#define ES01_00_AFEReg0 0x00
+#define ES01_01_AFEReg1 0x01
+#define ES01_02_AFEReg2 0x02
+#define ES01_03_AFEReg3 0x03
+#define ES01_04_AFEReset 0x04
+#define ES01_05_AFEReg4 0x05
+
+#define ES01_20_DACRed 0x20
+#define ES01_21_DACGreen 0x21
+#define ES01_22_DACBlue 0x22
+#define ES01_24_SignRed 0x24
+#define ES01_25_SignGreen 0x25
+#define ES01_26_SignBlue 0x26
+#define ES01_28_PGARed 0x28
+#define ES01_29_PGAGreen 0x29
+#define ES01_2A_PGABlue 0x2A
+
+#define ES01_00_ADAFEConfiguration 0x00
+#define ES01_02_ADAFEMuxConfig 0x02
+#define ES01_04_ADAFEPGACH1 0x04
+#define ES01_06_ADAFEPGACH2 0x06
+#define ES01_08_ADAFEPGACH3 0x08
+
+#define ES01_0C_ADAFEOffsetCH1P 0x0C
+#define ES01_0D_ADAFEOffsetCH1N 0x0D
+#define ES01_0E_ADAFEOffsetCH2P 0x0E
+#define ES01_0F_ADAFEOffsetCH2N 0x0F
+#define ES01_10_ADAFEOffsetCH3P 0x10
+#define ES01_11_ADAFEOffsetCH3N 0x11
+
+#define ES01_00_AD9826Configuration 0x00
+#define ES01_02_AD9826MuxConfig 0x02
+#define ES01_04_AD9826PGARed 0x04
+#define ES01_06_AD9826PGAGreen 0x06
+#define ES01_08_AD9826PGABlue 0x08
+#define ES01_0A_AD9826OffsetRedP 0x0a
+#define ES01_0B_AD9826OffsetRedN 0x0b
+#define ES01_0C_AD9826OffsetGreenP 0x0c
+#define ES01_0D_AD9826OffsetGreenN 0x0d
+#define ES01_0E_AD9826OffsetBlueP 0x0e
+#define ES01_0F_AD9826OffsetBlueN 0x0f
+
+#define ES02_50_MOTOR_CURRENT_CONTORL 0x50
+ /* bit[0] */
+#define DOWN_LOAD_MOTOR_TABLE_DISABLE 0x00
+#define DOWN_LOAD_MOTOR_TABLE_ENABLE 0x01
+ /* bit[3:1] */
+#define _4_TABLE_SPACE_FOR_FULL_STEP 0x00
+#define _8_TABLE_SPACE_FOR_1_DIV_2_STEP 0x02
+#define _16_TABLE_SPACE_FOR_1_DIV_4_STEP 0x06
+#define _32_TABLE_SPACE_FOR_1_DIV_8_STEP 0x0E
+ /* bit[4] */
+#define MOTOR_TABLE_ADDR_SHOW_IN_FIRST_PIXEL_OF_LINE_DISABLE 0x00
+#define MOTOR_TABLE_ADDR_SHOW_IN_FIRST_PIXEL_OF_LINE_ENABLE 0x10
+ /* bit[5] */
+#define MOTOR_CURRENT_TABLE_ADDRESS_BIT4_TO_BIT0_DISABLE 0x00
+#define MOTOR_CURRENT_TABLE_ADDRESS_BIT4_TO_BIT0_ENABLE 0x20
+
+#define ES02_51_MOTOR_PHASE_TABLE_1 0x51
+#define ES02_52_MOTOR_CURRENT_TABLE_A 0x52
+#define ES02_53_MOTOR_CURRENT_TABLE_B 0x53
+
+#define ES01_5F_REGISTER_BANK_SELECT 0x5F
+ /* bit[1:0] */
+#define SELECT_REGISTER_BANK0 0x00
+#define SELECT_REGISTER_BANK1 0x01
+#define SELECT_REGISTER_BANK2 0x02
+
+/* AFE Auto Configuration Gain & Offset Register */
+#define ES01_60_AFE_AUTO_GAIN_OFFSET_RED_LB 0x60
+#define ES01_61_AFE_AUTO_GAIN_OFFSET_RED_HB 0x61
+#define ES01_62_AFE_AUTO_GAIN_OFFSET_GREEN_LB 0x62
+#define ES01_63_AFE_AUTO_GAIN_OFFSET_GREEN_HB 0x63
+#define ES01_64_AFE_AUTO_GAIN_OFFSET_BLUE_LB 0x64
+#define ES01_65_AFE_AUTO_GAIN_OFFSET_BLUE_HB 0x65
+
+#define ES01_74_HARDWARE_SETTING 0x74
+/* bit[0] */
+#define MOTOR1_SERIAL_INTERFACE_G10_8_ENABLE 0x01
+#define MOTOR1_SERIAL_INTERFACE_G10_8_DISABLE 0x00
+/* bit[1]*/
+#define LED_OUT_G11_ENABLE 0x02
+#define LED_OUT_G11_DISABLE 0x00
+ /* bit[2] */
+#define SLAVE_SERIAL_INTERFACE_G15_14_ENABLE 0x04
+#define SLAVE_SERIAL_INTERFACE_G15_14_DISABLE 0x00
+ /* bit[3] */
+#define SHUTTLE_CCD_ENABLE 0x08
+#define SHUTTLE_CCD_DISABLE 0x00
+ /* bit[4] */
+#define HARDWARE_RESET_ESIC_AFE_ENABLE 0x10
+#define HARDWARE_RESET_ESIC_AFE_DISABLE 0x00
+
+#define ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL 0x79
+ /* bit[3:0] */
+#define AFEMCLK_DELAY_0_ns 0x00
+#define AFEMCLK_DELAY_2_ns 0x01
+#define AFEMCLK_DELAY_4_ns 0x02
+#define AFEMCLK_DELAY_6_ns 0x03
+#define AFEMCLK_DELAY_8_ns 0x04
+#define AFEMCLK_DELAY_10_ns 0x05
+#define AFEMCLK_DELAY_12_ns 0x06
+#define AFEMCLK_DELAY_14_ns 0x07
+#define AFEMCLK_DELAY_16_ns 0x08
+#define AFEMCLK_DELAY_18_ns 0x09
+#define AFEMCLK_DELAY_20_ns 0x0A
+/* bit[7:4]*/
+#define SDRAMCLK_DELAY_0_ns 0x00
+#define SDRAMCLK_DELAY_2_ns 0x10
+#define SDRAMCLK_DELAY_4_ns 0x20
+#define SDRAMCLK_DELAY_6_ns 0x30
+#define SDRAMCLK_DELAY_8_ns 0x40
+#define SDRAMCLK_DELAY_10_ns 0x50
+#define SDRAMCLK_DELAY_12_ns 0x60
+#define SDRAMCLK_DELAY_14_ns 0x70
+#define SDRAMCLK_DELAY_16_ns 0x80
+#define SDRAMCLK_DELAY_18_ns 0x90
+#define SDRAMCLK_DELAY_20_ns 0xA0
+
+#define ES01_82_AFE_ADCCLK_TIMING_ADJ_BYTE0 0x82
+#define ES01_83_AFE_ADCCLK_TIMING_ADJ_BYTE1 0x83
+#define ES01_84_AFE_ADCCLK_TIMING_ADJ_BYTE2 0x84
+#define ES01_85_AFE_ADCCLK_TIMING_ADJ_BYTE3 0x85
+#define ES01_86_DisableAllClockWhenIdle 0x86
+ /* bit[0] */
+#define CLOSE_ALL_CLOCK_ENABLE 0x01
+#define CLOSE_ALL_CLOCK_DISABLE 0x00
+#define ES01_87_SDRAM_Timing 0x87
+
+#define ES01_88_LINE_ART_THRESHOLD_HIGH_VALUE 0x88
+#define ES01_89_LINE_ART_THRESHOLD_LOW_VALUE 0x89
+
+#define ES01_8A_FixScanStepMSB 0x8a
+#define ES01_8B_Status 0x8b
+ /* bit[4:0] */
+#define H1H0L1L0_PS_MJ 0x00
+#define SCAN_STATE 0x01
+#define GPIO0_7 0x02
+#define GPIO8_15 0x03
+#define AVAILABLE_BANK_COUNT0_7 0x04
+#define AVAILABLE_BANK_COUNT8_15 0x05
+#define RAM_ADDRESS_POINTER0_7 0x06
+#define RAM_ADDRESS_POINTER8_15 0x07
+#define RAM_ADDRESS_POINTER16_19 0x08
+#define CARRIAGE_POS_DURING_SCAN0_7 0x09
+#define CARRIAGE_POS_DURING_SCAN8_15 0x0a
+#define CARRIAGE_POS_DURING_SCAN16_19 0x0b
+#define LINE_TIME0_7 0x0C
+#define LINE_TIME8_15 0x0d
+#define LINE_TIME16_19 0x0e
+#define LAST_COMMAND_ADDRESS 0x0f
+#define LAST_COMMAND_DATA 0x10
+#define SERIAL_READ_REGISTER_0 0x11
+#define SERIAL_READ_REGISTER_1 0x12
+#define SERIAL_READ_REGISTER_2 0x13
+#define SERIAL_READ_REGISTER_3 0x14
+#define MOTOR_STEP_TRIGER_POSITION7_0 0x15
+#define MOTOR_STEP_TRIGER_POSITION15_8 0x16
+#define MOTOR_STEP_TRIGER_POSITION23_16 0x17
+#define CHIP_STATUS_A 0x18 /*reserve */
+#define CHIP_STRING_0 0x19 /*0x45'E' */
+#define CHIP_STRING_1 0x1a /*0x53'S' */
+#define CHIP_STRING_2 0x1b /*0x43'C' */
+#define CHIP_STRING_3 0x1c /*0x41'A' */
+#define CHIP_STRING_4 0x1d /*0x4E'N' */
+#define CHIP_STRING_5 0x1e /*0x30'0' */
+#define CHIP_STRING_6 0x1f /*0x31'1' */
+#define ES01_8C_RestartMotorSynPixelNumberM16LSB 0x8c
+#define ES01_8D_RestartMotorSynPixelNumberM16MSB 0x8d
+#define ES01_90_Lamp0PWM 0x90
+#define ES01_91_Lamp1PWM 0x91
+#define ES01_92_TimerPowerSaveTime 0x92
+#define ES01_93_MotorWatchDogTime 0x93
+#define ES01_94_PowerSaveControl 0x94
+ /* bit[0] */
+#define TIMER_POWER_SAVE_ENABLE 0x01
+#define TIMER_POWER_SAVE_DISABLE 0x00
+
+/* bit[1]*/
+#define USB_POWER_SAVE_ENABLE 0x02
+#define USB_POWER_SAVE_DISABLE 0x00
+/* bit[2]*/
+#define USB_REMOTE_WAKEUP_ENABLE 0x04
+#define USB_REMOTE_WAKEUP_DISABLE 0x00
+/* bit[5:4]*/
+#define LED_MODE_ON 0x00
+#define LED_MODE_OFF 0x10
+#define LED_MODE_FLASH_SLOWLY 0x20
+#define LED_MODE_FLASH_QUICKLY 0x30
+
+
+#define ES01_95_GPIOValue0_7 0x95
+#define ES01_96_GPIOValue8_15 0x96
+#define ES01_97_GPIOControl0_7 0x97
+#define ES01_98_GPIOControl8_15 0x98
+#define ES01_99_LAMP_PWM_FREQ_CONTROL 0x99
+#define ES01_9A_AFEControl 0x9a
+ /*bit[0] */
+#define ADAFE_AFE 0x00
+#define AD9826_AFE 0x01
+/*bit[1]*/
+#define AUTO_CHANGE_AFE_GAIN_OFFSET_ENABLE 0x02
+#define AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE 0x00
+
+#define ES01_9B_ShadingTableAddrA14_A21 0x9B
+#define ES01_9C_ShadingTableAddrODDA12_A19 0x9c
+#define ES01_9D_MotorTableAddrA14_A21 0x9d
+#define ES01_9E_HorizontalRatio1to15LSB 0x9e
+#define ES01_9F_HorizontalRatio1to15MSB 0x9f
+#define ES01_A0_HostStartAddr0_7 0xa0
+#define ES01_A1_HostStartAddr8_15 0xa1
+ /* bit[3] */
+#define ES01_ACCESS_PRE_GAMMA 0x08
+#define ES01_ACCESS_FINAL_GAMMA 0x00
+#define ES01_A2_HostStartAddr16_21 0xa2
+ /* bit[7] */
+#define ACCESS_DRAM 0x00
+#define ACCESS_GAMMA_RAM 0x80
+#define ES01_A3_HostEndAddr0_7 0xa3
+#define ES01_A4_HostEndAddr8_15 0xa4
+#define ES01_A5_HostEndAddr16_21 0xa5
+
+#define ES01_A6_MotorOption 0xA6
+ /* bit[0] */
+#define MOTOR_0_ENABLE 0x01
+#define MOTOR_0_DISABLE 0x00
+/* bit[1]*/
+#define MOTOR_1_ENABLE 0x02
+#define MOTOR_1_DISABLE 0x00
+/* bit[3:2]*/
+#define HOME_SENSOR_0_ENABLE 0x00
+#define HOME_SENSOR_1_ENABLE 0x04
+#define HOME_SENSOR_BOTH_ENABLE 0x08
+#define HOME_SENSOR_0_INVERT_ENABLE 0x0c
+/* bit[7:4]*/
+#define ES03_UNIPOLAR_FULL_STEP_2003 0x00
+#define ES03_BIPOLAR_FULL_2916 0x10
+#define ES03_BIPOLAR_FULL_3955_3966 0x20
+#define ES03_UNIPOLAR_PWM_2003 0x30
+#define ES03_BIPOLAR_3967 0x40
+#define ES03_TABLE_DEFINE 0x50
+#define ES01_A7_MotorPWMOnTimePhasA 0xa7
+#define ES01_A8_MotorPWMOnTimePhasB 0xa8
+#define ES01_A9_MotorPWMOffTimePhasA 0xa9
+#define ES01_AA_MotorPWMOffTimePhasB 0xaa
+#define ES01_AB_PWM_CURRENT_CONTROL 0xab
+
+ /* bit[1:0] */
+#define MOTOR_PWM_CURRENT_0 0x00
+#define MOTOR_PWM_CURRENT_1 0x01
+#define MOTOR_PWM_CURRENT_2 0x02
+#define MOTOR_PWM_CURRENT_3 0x03
+/* bit[3:2]*/
+#define MOTOR1_GPO_VALUE_0 0x00
+#define MOTOR1_GPO_VALUE_1 0x04
+#define MOTOR1_GPO_VALUE_2 0x08
+#define MOTOR1_GPO_VALUE_3 0x0c
+/* bit[4]*/
+#define GPO_OUTPUT_ENABLE 0x10
+#define GPO_OUTPUT_DISABLE 0x00
+/* bit[5]*/
+#define SERIAL_PORT_CONTINUOUS_OUTPUT_ENABLE 0x20
+#define SERIAL_PORT_CONTINUOUS_OUTPUT_DISABLE 0x00
+#define ES01_AC_MotorPWMJamRangeLSB 0xac
+#define ES01_AD_MotorPWMJamRangeMSB 0xad
+#define ES01_AE_MotorSyncPixelNumberM16LSB 0xae
+#define ES01_AF_MotorSyncPixelNumberM16MSB 0xaf
+#define ES01_B0_CCDPixelLSB 0xb0
+#define ES01_B1_CCDPixelMSB 0xb1
+#define ES01_B2_PHTGPulseWidth 0xb2
+#define ES01_B3_PHTGWaitWidth 0xb3
+#define ES01_B4_StartPixelLSB 0xb4
+#define ES01_B5_StartPixelMSB 0xb5
+#define ES01_B6_LineWidthPixelLSB 0xb6
+#define ES01_B7_LineWidthPixelMSB 0xb7
+
+#define ES01_B8_ChannelRedExpStartPixelLSB 0xb8
+#define ES01_B9_ChannelRedExpStartPixelMSB 0xb9
+#define ES01_BA_ChannelRedExpEndPixelLSB 0xba
+#define ES01_BB_ChannelRedExpEndPixelMSB 0xbb
+#define ES01_BC_ChannelGreenExpStartPixelLSB 0xbc
+#define ES01_BD_ChannelGreenExpStartPixelMSB 0xbd
+#define ES01_BE_ChannelGreenExpEndPixelLSB 0xbe
+#define ES01_BF_ChannelGreenExpEndPixelMSB 0xbf
+#define ES01_C0_ChannelBlueExpStartPixelLSB 0xc0
+#define ES01_C1_ChannelBlueExpStartPixelMSB 0xc1
+#define ES01_C2_ChannelBlueExpEndPixelLSB 0xc2
+#define ES01_C3_ChannelBlueExpEndPixelMSB 0xc3
+
+
+#define ES01_C4_MultiTGTimesRed 0xc4
+#define ES01_C5_MultiTGTimesGreen 0xc5
+#define ES01_C6_MultiTGTimesBlue 0xc6
+#define ES01_C7_MultiTGDummyPixelNumberLSB 0xc7
+#define ES01_C8_MultiTGDummyPixelNumberMSB 0xc8
+#define ES01_C9_CCDDummyPixelNumberLSB 0xc9
+#define ES01_CA_CCDDummyPixelNumberMSB 0xca
+#define ES01_CB_CCDDummyCycleNumber 0xcb
+#define ES01_CC_PHTGTimingAdjust 0xcc
+ /* bit[0] */
+#define PHTG_INVERT_OUTPUT_ENABLE 0x01
+#define PHTG_INVERT_OUTPUT_DISABLE 0x00
+/* bit[1]*/
+#define TWO_TG 0x01
+#define MULTI_TG 0x00
+ /* bit[3:2] */
+#define CCD_PIXEL_MODE_RED 0x0c
+#define CCD_LINE_MOE_RED_00 0x00
+#define CCD_LINE_MOE_RED_01 0x04
+#define CCD_LINE_MOE_RED_10 0x08
+/* bit[5:4]*/
+#define CCD_PIXEL_MODE_GREEN 0x30
+#define CCD_LINE_MOE_GREEN_00 0x00
+#define CCD_LINE_MOE_GREEN_01 0x40
+#define CCD_LINE_MOE_GREEN_10 0x80
+/* bit[7:6]*/
+#define CCD_PIXEL_MODE_BLUE 0xc0
+#define CCD_LINE_MOE_BLUE_00 0x00
+#define CCD_LINE_MOE_BLUE_01 0x40
+#define CCD_LINE_MOE_BLUE_10 0x80
+
+#define ES01_CD_TG_R_CONTROL 0xCD
+#define ES01_CE_TG_G_CONTROL 0xCE
+#define ES01_CF_TG_B_CONTROL 0xCF
+
+#define ES01_D9_CLEAR_PULSE_WIDTH 0xD9
+#define ES01_DA_CLEAR_SIGNAL_INVERTING_OUTPUT 0xDA
+#define ES01_DB_PH_RESET_EDGE_TIMING_ADJUST 0xDB
+#define ES01_DC_CLEAR_EDGE_TO_PH_TG_EDGE_WIDTH 0xDC
+#define ES01_D0_PH1_0 0xD0
+#define ES01_D1_PH2_0 0xD1
+#define ES01_D2_PH1B_0 0xD2
+#define ES01_D4_PHRS_0 0xD4
+#define ES01_D5_PHCP_0 0xD5
+#define ES01_D6_AFE_VSAMP_0 0xD6
+#define ES01_D7_AFE_RSAMP_0 0xD7
+
+#define ES01_D8_PHTG_EDGE_TIMING_ADJUST 0xD8
+
+#define ES01_CD_PH1_0 0xcd
+#define ES01_CE_PH1_1 0xce
+#define ES01_CF_PH2_0 0xcf
+#define ES01_D0_PH2_1 0xd0
+#define ES01_D1_PH1B_0 0xd1
+#define ES01_D2_PH1B_1 0xd2
+#define ES01_D3_PH2B_0 0xd3
+#define ES01_D4_PH2B_1 0xd4
+#define ES01_D5_PHRS_0 0xd5
+#define ES01_D6_PHRS_1 0xd6
+#define ES01_D7_PHCP_0 0xd7
+#define ES01_D8_PHCP_1 0xd8
+#define ES01_D9_AFE_VSAMP_0 0xd9
+#define ES01_DA_AFE_VSAMP_1 0xda
+#define ES01_DB_AFE_RSAMP_0 0xdb
+#define ES01_DC_AFE_RSAMP_1 0xdc
+#define ES01_DD_PH1234_IN_DUMMY_TG 0xdd
+#define ES01_DE_CCD_SETUP_REGISTER 0xDE
+ /* bit[0] */
+#define ES01_LINE_SCAN_MODE_DISABLE 0x00
+#define ES01_LINE_SCAN_MODE_ENABLE 0x01
+ /* bit[1] */
+#define ES01_CIS_SENSOR_MODE_DISABLE 0x00
+#define ES01_CIS_SENSOR_MODE_ENABLE 0x02
+ /* bit[2] */
+#define ES01_CIS_LED_OUTPUT_RGB 0x00
+#define ES01_CIS_LED_OUTPUT_RtoGtoB 0x04
+ /* bit[3] */
+#define ES01_CIS_LED_NORMAL_OUTPUT 0x00
+#define ES01_CIS_LED_INVERT_OUTPUT 0x08
+ /* bit[4] */
+#define ES01_ACC_IN_IDLE_DISABLE 0x00
+#define ES01_ACC_IN_IDLE_ENABLE 0x10
+ /* bit[5] */
+#define ES01_EVEN_ODD_DISABLE 0x00
+#define ES01_EVEN_ODD_ENABLE 0x20
+ /* bit[6] */
+#define ES01_ALTERNATE_EVEN_ODD_DISABLE 0x00
+#define ES01_ALTERNATE_EVEN_ODD_ENABLE 0x40
+ /* bit[7] */
+#define ES01_RESET_CCD_STATE_DISABLE 0x00
+#define ES01_RESET_CCD_STATE_ENABLE 0x80
+
+#define ES01_DF_ICG_CONTROL 0xdf
+ /* bit[2:0] */
+#define BEFORE_PHRS_0_ns 0x00
+#define BEFORE_PHRS_416_7t_ns 0x01
+#define BEFORE_PHRS_416_6t_ns 0x02
+#define BEFORE_PHRS_416_5t_ns 0x03
+#define BEFORE_PHRS_416_4t_ns 0x04
+#define BEFORE_PHRS_416_3t_ns 0x05
+#define BEFORE_PHRS_416_2t_ns 0x06
+#define BEFORE_PHRS_416_1t_ns 0x07
+/* bit[2:0]*/
+#define ICG_UNIT_1_PIXEL_TIME 0x00
+#define ICG_UNIT_4_PIXEL_TIME 0x10
+#define ICG_UNIT_8_PIXEL_TIME 0x20
+#define ICG_UNIT_16_PIXEL_TIME 0x30
+#define ICG_UNIT_32_PIXEL_TIME 0x40
+#define ICG_UNIT_64_PIXEL_TIME 0x50
+#define ICG_UNIT_128_PIXEL_TIME 0x60
+#define ICG_UNIT_256_PIXEL_TIME 0x70
+
+#define ES01_E0_MotorAccStep0_7 0xe0
+#define ES01_E1_MotorAccStep8_8 0xe1
+#define ES01_E2_MotorStepOfMaxSpeed0_7 0xe2
+#define ES01_E3_MotorStepOfMaxSpeed8_15 0xe3
+#define ES01_E4_MotorStepOfMaxSpeed16_19 0xe4
+#define ES01_E5_MotorDecStep 0xe5
+#define ES01_E6_ScanBackTrackingStepLSB 0xe6
+#define ES01_E7_ScanBackTrackingStepMSB 0xe7
+#define ES01_E8_ScanRestartStepLSB 0xe8
+#define ES01_E9_ScanRestartStepMSB 0xe9
+#define ES01_EA_ScanBackHomeExtStepLSB 0xea
+#define ES01_EB_ScanBackHomeExtStepMSB 0xeb
+#define ES01_EC_ScanAccStep0_7 0xec
+#define ES01_ED_ScanAccStep8_8 0xed
+#define ES01_EE_FixScanStepLSB 0xee
+#define ES01_EF_ScanDecStep 0xef
+#define ES01_F0_ScanImageStep0_7 0xf0
+#define ES01_F1_ScanImageStep8_15 0xf1
+#define ES01_F2_ScanImageStep16_19 0xf2
+
+#define ES01_F3_ActionOption 0xf3
+ /* bit[0] */
+#define MOTOR_MOVE_TO_FIRST_LINE_ENABLE 0x01
+#define MOTOR_MOVE_TO_FIRST_LINE_DISABLE 0x00
+/* bit[1]*/
+#define MOTOR_BACK_HOME_AFTER_SCAN_ENABLE 0x02
+#define MOTOR_BACK_HOME_AFTER_SCAN_DISABLE 0x00
+/* bit[2]*/
+#define SCAN_ENABLE 0x04
+#define SCAN_DISABLE 0x00
+/* bit[3]*/
+#define SCAN_BACK_TRACKING_ENABLE 0x08
+#define SCAN_BACK_TRACKING_DISABLE 0x00
+/* bit[4]*/
+#define INVERT_MOTOR_DIRECTION_ENABLE 0x10
+#define INVERT_MOTOR_DIRECTION_DISABLE 0x00
+/* bit[5]*/
+#define UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE 0x20
+#define UNIFORM_MOTOR_AND_SCAN_SPEED_DISABLE 0x00
+
+ /* bit[6] */
+#define ES01_STATIC_SCAN_ENABLE 0x40
+#define ES01_STATIC_SCAN_DISABLE 0x00
+
+/* bit[7]*/
+#define MOTOR_TEST_LOOP_ENABLE 0x80
+#define MOTOR_TEST_LOOP_DISABLE 0x00
+#define ES01_F4_ActiveTriger 0xf4
+ /* bit[0] */
+#define ACTION_TRIGER_ENABLE 0x01
+#define ACTION_TRIGER_DISABLE 0x00
+
+#define ES01_F5_ScanDataFormat 0xf5
+ /* bit[0] */
+#define COLOR_ES02 0x00
+#define GRAY_ES02 0x01
+/* bit[2:1]*/
+#define _8_BITS_ES02 0x00
+#define _16_BITS_ES02 0x02
+#define _1_BIT_ES02 0x04
+/* bit[5:4]*/
+#define GRAY_RED_ES02 0x00
+#define GRAY_GREEN_ES02 0x10
+#define GRAY_BLUE_ES02 0x20
+#define GRAY_GREEN_BLUE_ES02 0x30
+
+#define ES01_F6_MorotControl1 0xf6
+ /* bit[2:0] */
+#define SPEED_UNIT_1_PIXEL_TIME 0x00
+#define SPEED_UNIT_4_PIXEL_TIME 0x01
+#define SPEED_UNIT_8_PIXEL_TIME 0x02
+#define SPEED_UNIT_16_PIXEL_TIME 0x03
+#define SPEED_UNIT_32_PIXEL_TIME 0x04
+#define SPEED_UNIT_64_PIXEL_TIME 0x05
+#define SPEED_UNIT_128_PIXEL_TIME 0x06
+#define SPEED_UNIT_256_PIXEL_TIME 0x07
+/* bit[5:4]*/
+#define MOTOR_SYNC_UNIT_1_PIXEL_TIME 0x00
+#define MOTOR_SYNC_UNIT_16_PIXEL_TIME 0x10
+#define MOTOR_SYNC_UNIT_64_PIXEL_TIME 0x20
+#define MOTOR_SYNC_UNIT_256_PIXEL_TIME 0x30
+#define ES01_F7_DigitalControl 0xf7
+ /* bit[0] */
+#define DIGITAL_REDUCE_ENABLE 0x01
+#define DIGITAL_REDUCE_DISABLE 0x00
+/* bit[3:1]*/
+#define DIGITAL_REDUCE_1_1 0x00
+#define DIGITAL_REDUCE_1_2 0x02
+#define DIGITAL_REDUCE_1_4 0x04
+#define DIGITAL_REDUCE_1_8 0x06
+#define DIGITAL_REDUCE_1_16 0x08
+
+#define ES01_F8_WHITE_SHADING_DATA_FORMAT 0xF8
+ /* bit[1:0] */
+#define ES01_SHADING_2_INT_14_DEC 0x00
+#define ES01_SHADING_3_INT_13_DEC 0x01
+#define ES01_SHADING_4_INT_12_DEC 0x02
+#define ES01_SHADING_5_INT_11_DEC 0x03
+#define ES01_F9_BufferFullSize16WordLSB 0xf9
+#define ES01_FA_BufferFullSize16WordMSB 0xfa
+#define ES01_FB_BufferEmptySize16WordLSB 0xfb
+#define ES01_FC_BufferEmptySize16WordMSB 0xfc
+#define ES01_FD_MotorFixedspeedLSB 0xfd
+#define ES01_FE_MotorFixedspeedMSB 0xfe
+#define ES01_FF_TestControl 0xff
+ /* bit[0] */
+#define OUTPUT_HORIZONTAL_PATTERN_ENABLE 0x01
+#define OUTPUT_HORIZONTAL_PATTERN_DISABLE 0x00
+/* bit[1]*/
+#define OUTPUT_VERTICAL_PATTERN_ENABLE 0x02
+#define OUTPUT_VERTICAL_PATTERN_DISABLE 0x00
+/* bit[2]*/
+#define BYPASS_DARK_SHADING_ENABLE 0x04
+#define BYPASS_DARK_SHADING_DISABLE 0x00
+/* bit[3]*/
+#define BYPASS_WHITE_SHADING_ENABLE 0x08
+#define BYPASS_WHITE_SHADING_DISABLE 0x00
+/* bit[4]*/
+#define BYPASS_PRE_GAMMA_ENABLE 0x10
+#define BYPASS_PRE_GAMMA_DISABLE 0x00
+/* bit[5]*/
+#define BYPASS_CONVOLUTION_ENABLE 0x20
+#define BYPASS_CONVOLUTION_DISABLE 0x00
+/* bit[6]*/
+#define BYPASS_MATRIX_ENABLE 0x40
+#define BYPASS_MATRIX_DISABLE 0x00
+/* bit[7]*/
+#define BYPASS_GAMMA_ENABLE 0x80
+#define BYPASS_GAMMA_DISABLE 0x00
+#define ES01_FF_SCAN_IMAGE_OPTION 0xFF
+ /* bit[0] */
+#define OUTPUT_HORIZONTAL_PATTERN_ENABLE 0x01
+#define OUTPUT_HORIZONTAL_PATTERN_DISABLE 0x00
+/* bit[1]*/
+#define OUTPUT_VERTICAL_PATTERN_ENABLE 0x02
+#define OUTPUT_VERTICAL_PATTERN_DISABLE 0x00
+/* bit[2]*/
+#define BYPASS_DARK_SHADING_ENABLE 0x04
+#define BYPASS_DARK_SHADING_DISABLE 0x00
+/* bit[3]*/
+#define BYPASS_WHITE_SHADING_ENABLE 0x08
+#define BYPASS_WHITE_SHADING_DISABLE 0x00
+/* bit[4]*/
+#define BYPASS_PRE_GAMMA_ENABLE 0x10
+#define BYPASS_PRE_GAMMA_DISABLE 0x00
+/* bit[5]*/
+#define BYPASS_CONVOLUTION_ENABLE 0x20
+#define BYPASS_CONVOLUTION_DISABLE 0x00
+/* bit[6]*/
+#define BYPASS_MATRIX_ENABLE 0x40
+#define BYPASS_MATRIX_DISABLE 0x00
+/* bit[7]*/
+#define BYPASS_GAMMA_ENABLE 0x80
+#define BYPASS_GAMMA_DISABLE 0x00
+
+/*******************************************************************/
+#define ES01_160_CHANNEL_A_LATCH_POSITION_HB 0x160
+#define ES01_161_CHANNEL_A_LATCH_POSITION_LB 0x161
+#define ES01_162_CHANNEL_B_LATCH_POSITION_HB 0x162
+#define ES01_163_CHANNEL_B_LATCH_POSITION_LB 0x163
+#define ES01_164_CHANNEL_C_LATCH_POSITION_HB 0x164
+#define ES01_165_CHANNEL_C_LATCH_POSITION_LB 0x165
+#define ES01_166_CHANNEL_D_LATCH_POSITION_HB 0x166
+#define ES01_167_CHANNEL_D_LATCH_POSITION_LB 0x167
+
+#define ES01_168_SECONDARY_FF_LATCH_POSITION 0x168
+
+#define ES01_169_NUMBER_OF_SEGMENT_PIXEL_LB 0x169
+#define ES01_16A_NUMBER_OF_SEGMENT_PIXEL_HB 0x16A
+
+#define ES01_16B_BETWEEN_SEGMENT_INVALID_PIXEL 0x16B
+#define ES01_16C_LINE_SHIFT_OUT_TIMES_DIRECTION 0x16C /* bit[3:0] */
+
+/* segment start address */
+#define ES01_16D_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE0 0x16D
+#define ES01_16E_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE1 0x16E
+#define ES01_16F_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE2 0x16F /* bit[3:0] */
+
+#define ES01_170_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE0 0x170
+#define ES01_171_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE1 0x171
+#define ES01_172_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE2 0x172 /* bit[3:0] */
+
+#define ES01_173_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE0 0x173
+#define ES01_174_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE1 0x174
+#define ES01_175_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE2 0x175 /* bit[3:0] */
+
+#define ES01_176_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE0 0x176
+#define ES01_177_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE1 0x177
+#define ES01_178_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE2 0x178 /* bit[3:0] */
+
+#define ES01_179_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE0 0x179
+#define ES01_17A_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE1 0x17A
+#define ES01_17B_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE2 0x17B /* bit[3:0] */
+
+#define ES01_17C_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE0 0x17C
+#define ES01_17D_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE1 0x17D
+#define ES01_17E_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE2 0x17E /* bit[3:0] */
+
+#define ES01_17F_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE0 0x17F
+#define ES01_180_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE1 0x180
+#define ES01_181_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE2 0x181 /* bit[3:0] */
+
+#define ES01_182_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE0 0x182
+#define ES01_183_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE1 0x183
+#define ES01_184_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE2 0x184 /* bit[3:0] */
+
+#define ES01_185_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE0 0x185
+#define ES01_186_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE1 0x186
+#define ES01_187_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE2 0x187 /* bit[3:0] */
+
+#define ES01_188_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE0 0x188
+#define ES01_189_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE1 0x189
+#define ES01_18A_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE2 0x18A /* bit[3:0] */
+
+#define ES01_18B_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE0 0x18B
+#define ES01_18C_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE1 0x18C
+#define ES01_18D_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE2 0x18D /* bit[3:0] */
+
+#define ES01_18E_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE0 0x18E
+#define ES01_18F_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE1 0x18F
+#define ES01_190_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE2 0x190 /* bit[3:0] */
+
+/* channel gap */
+#define ES01_191_CHANNEL_GAP1_BYTE0 0x191
+#define ES01_192_CHANNEL_GAP1_BYTE1 0x192
+#define ES01_193_CHANNEL_GAP1_BYTE2 0x193 /* bit[3:0] */
+
+#define ES01_194_CHANNEL_GAP2_BYTE0 0x194
+#define ES01_195_CHANNEL_GAP2_BYTE1 0x195
+#define ES01_196_CHANNEL_GAP2_BYTE2 0x196 /* bit[3:0] */
+
+#define ES01_197_CHANNEL_GAP3_BYTE0 0x197
+#define ES01_198_CHANNEL_GAP3_BYTE1 0x198
+#define ES01_199_CHANNEL_GAP3_BYTE2 0x199 /* bit[3:0] */
+
+/* channel line gap */
+#define ES01_19A_CHANNEL_LINE_GAP_LB 0x19A
+#define ES01_19B_CHANNEL_LINE_GAP_HB 0x19B
+
+/* max pack line */
+#define ES01_19C_MAX_PACK_LINE 0x19C /* bit[5:0] */
+
+/*pack threshold */
+#define ES01_19D_PACK_THRESHOLD_LINE 0x19D
+
+/* pack area start address */
+#define ES01_19E_PACK_AREA_R_START_ADDR_BYTE0 0x19E
+#define ES01_19F_PACK_AREA_R_START_ADDR_BYTE1 0x19F
+#define ES01_1A0_PACK_AREA_R_START_ADDR_BYTE2 0x1A0 /* bit[3:0] */
+
+#define ES01_1A1_PACK_AREA_G_START_ADDR_BYTE0 0x1A1
+#define ES01_1A2_PACK_AREA_G_START_ADDR_BYTE1 0x1A2
+#define ES01_1A3_PACK_AREA_G_START_ADDR_BYTE2 0x1A3 /* bit[3:0] */
+
+#define ES01_1A4_PACK_AREA_B_START_ADDR_BYTE0 0x1A4
+#define ES01_1A5_PACK_AREA_B_START_ADDR_BYTE1 0x1A5
+#define ES01_1A6_PACK_AREA_B_START_ADDR_BYTE2 0x1A6 /* bit[3:0] */
+
+/* pack area end address */
+#define ES01_1A7_PACK_AREA_R_END_ADDR_BYTE0 0x1A7
+#define ES01_1A8_PACK_AREA_R_END_ADDR_BYTE1 0x1A8
+#define ES01_1A9_PACK_AREA_R_END_ADDR_BYTE2 0x1A9 /* bit[3:0] */
+
+#define ES01_1AA_PACK_AREA_G_END_ADDR_BYTE0 0x1AA
+#define ES01_1AB_PACK_AREA_G_END_ADDR_BYTE1 0x1AB
+#define ES01_1AC_PACK_AREA_G_END_ADDR_BYTE2 0x1AC /* bit[3:0] */
+
+#define ES01_1AD_PACK_AREA_B_END_ADDR_BYTE0 0x1AD
+#define ES01_1AE_PACK_AREA_B_END_ADDR_BYTE1 0x1AE
+#define ES01_1AF_PACK_AREA_B_END_ADDR_BYTE2 0x1AF /* bit[3:0] */
+
+/* segment pixel number */
+#define ES01_1B0_SEGMENT_PIXEL_NUMBER_LB 0x1B0
+#define ES01_1B1_SEGMENT_PIXEL_NUMBER_HB 0x1B1
+
+/*overlap pixel number and hold pixel number */
+#define ES01_1B2_OVERLAP_AND_HOLD_PIXEL_NUMBER 0x1B2
+
+/*convolution parameter */
+#define ES01_1B3_CONVOLUTION_A 0x1B3
+#define ES01_1B4_CONVOLUTION_B 0x1B4
+#define ES01_1B5_CONVOLUTION_C 0x1B5
+#define ES01_1B6_CONVOLUTION_D 0x1B6
+#define ES01_1B7_CONVOLUTION_E 0x1B7
+#define ES01_1B8_CONVOLUTION_F 0x1B8 /* bit[2:0] */
+
+/* line pixel number */
+#define ES01_1B9_LINE_PIXEL_NUMBER_LB 0x1B9
+#define ES01_1BA_LINE_PIXEL_NUMBER_HB 0x1BA
+
+/* matrix parameter */
+#define ES01_1BB_MATRIX_A_LB 0x1BB
+#define ES01_1BC_MATRIX_A_HB 0x1BC /* bit[3:0] */
+
+#define ES01_1BD_MATRIX_B_LB 0x1BD
+#define ES01_1BE_MATRIX_B_HB 0x1BE /* bit[3:0] */
+
+#define ES01_1BF_MATRIX_C_LB 0x1BF
+#define ES01_1C0_MATRIX_C_HB 0x1C0 /* bit[3:0] */
+
+#define ES01_1C1_MATRIX_D_LB 0x1C1
+#define ES01_1C2_MATRIX_D_HB 0x1C2 /* bit[3:0] */
+
+#define ES01_1C3_MATRIX_E_LB 0x1C3
+#define ES01_1C4_MATRIX_E_HB 0x1C4 /* bit[3:0] */
+
+#define ES01_1C5_MATRIX_F_LB 0x1C5
+#define ES01_1C6_MATRIX_F_HB 0x1C6 /* bit[3:0] */
+
+#define ES01_1C7_MATRIX_G_LB 0x1C7
+#define ES01_1C8_MATRIX_G_HB 0x1C8 /* bit[3:0] */
+
+#define ES01_1C9_MATRIX_H_LB 0x1C9
+#define ES01_1CA_MATRIX_H_HB 0x1CA /* bit[3:0] */
+
+#define ES01_1CB_MATRIX_I_LB 0x1CB
+#define ES01_1CC_MATRIX_I_HB 0x1CC /* bit[3:0] */
+
+/*dummy clock number */
+#define ES01_1CD_DUMMY_CLOCK_NUMBER 0x1CD /* bit[3:0] */
+
+/* line segment number */
+#define ES01_1CE_LINE_SEGMENT_NUMBER 0x1CE
+
+/* dummy cycle timing */
+#define ES01_1D0_DUMMY_CYCLE_TIMING_B0 0x1D0
+#define ES01_1D1_DUMMY_CYCLE_TIMING_B1 0x1D1
+#define ES01_1D2_DUMMY_CYCLE_TIMING_B2 0x1D2
+#define ES01_1D3_DUMMY_CYCLE_TIMING_B3 0x1D3
+
+/* PH1 timing adjust register */
+#define ES01_1D4_PH1_TIMING_ADJ_B0 0x1D4
+#define ES01_1D5_PH1_TIMING_ADJ_B1 0x1D5
+#define ES01_1D6_PH1_TIMING_ADJ_B2 0x1D6
+#define ES01_1D7_PH1_TIMING_ADJ_B3 0x1D7
+
+/* PH2 timing adjust register */
+#define ES01_1D8_PH2_TIMING_ADJ_B0 0x1D8
+#define ES01_1D9_PH2_TIMING_ADJ_B1 0x1D9
+#define ES01_1DA_PH2_TIMING_ADJ_B2 0x1DA
+#define ES01_1DB_PH2_TIMING_ADJ_B3 0x1DB
+
+/* PH3 timing adjust register */
+#define ES01_1DC_PH3_TIMING_ADJ_B0 0x1DC
+#define ES01_1DD_PH3_TIMING_ADJ_B1 0x1DD
+#define ES01_1DE_PH3_TIMING_ADJ_B2 0x1DE
+#define ES01_1DF_PH3_TIMING_ADJ_B3 0x1DF
+
+/* PH4 timing adjust register */
+#define ES01_1E0_PH4_TIMING_ADJ_B0 0x1E0
+#define ES01_1E1_PH4_TIMING_ADJ_B1 0x1E1
+#define ES01_1E2_PH4_TIMING_ADJ_B2 0x1E2
+#define ES01_1E3_PH4_TIMING_ADJ_B3 0x1E3
+
+/*PHRS timing adjust register */
+#define ES01_1E4_PHRS_TIMING_ADJ_B0 0x1E4
+#define ES01_1E5_PHRS_TIMING_ADJ_B1 0x1E5
+#define ES01_1E6_PHRS_TIMING_ADJ_B2 0x1E6
+#define ES01_1E7_PHRS_TIMING_ADJ_B3 0x1E7
+
+/* PHCP timing adjust register */
+#define ES01_1E8_PHCP_TIMING_ADJ_B0 0x1E8
+#define ES01_1E9_PHCP_TIMING_ADJ_B1 0x1E9
+#define ES01_1EA_PHCP_TIMING_ADJ_B2 0x1EA
+#define ES01_1EB_PHCP_TIMING_ADJ_B3 0x1EB
+
+/*AFEVS timing adjust register */
+#define ES01_1EC_AFEVS_TIMING_ADJ_B0 0x1EC
+#define ES01_1ED_AFEVS_TIMING_ADJ_B1 0x1ED
+#define ES01_1EE_AFEVS_TIMING_ADJ_B2 0x1EE
+#define ES01_1EF_AFEVS_TIMING_ADJ_B3 0x1EF
+
+/*AFERS timing adjust register */
+#define ES01_1F0_AFERS_TIMING_ADJ_B0 0x1F0
+#define ES01_1F1_AFERS_TIMING_ADJ_B1 0x1F1
+#define ES01_1F2_AFERS_TIMING_ADJ_B2 0x1F2
+#define ES01_1F3_AFERS_TIMING_ADJ_B3 0x1F3
+
+/* read out pixel */
+#define ES01_1F4_START_READ_OUT_PIXEL_LB 0x1F4
+#define ES01_1F5_START_READ_OUT_PIXEL_HB 0x1F5
+#define ES01_1F6_READ_OUT_PIXEL_LENGTH_LB 0x1F6
+#define ES01_1F7_READ_OUT_PIXEL_LENGTH_HB 0x1F7
+
+/* pack channel setting */
+#define ES01_1F8_PACK_CHANNEL_SELECT_B0 0x1F8
+#define ES01_1F9_PACK_CHANNEL_SELECT_B1 0x1F9
+#define ES01_1FA_PACK_CHANNEL_SELECT_B2 0x1FA
+#define ES01_1FB_PACK_CHANNEL_SIZE_B0 0x1FB
+#define ES01_1FC_PACK_CHANNEL_SIZE_B1 0x1FC
+#define ES01_1FD_PACK_CHANNEL_SIZE_B2 0x1FD
+
+/* AFE gain offset control*/
+ /* rom code ver 0.10 */
+#define ES01_2A0_AFE_GAIN_OFFSET_CONTROL 0x2A0
+#define ES01_2A1_AFE_AUTO_CONFIG_GAIN 0x2A1
+#define ES01_2A2_AFE_AUTO_CONFIG_OFFSET 0x2A2
+
+#define ES01_2B0_SEGMENT0_OVERLAP_SEGMENT1 0x2B0
+#define ES01_2B1_SEGMENT1_OVERLAP_SEGMENT2 0x2B1
+#define ES01_2B2_SEGMENT2_OVERLAP_SEGMENT3 0x2B2
+
+/* valid pixel parameter */
+#define ES01_2C0_VALID_PIXEL_PARAMETER_OF_SEGMENT1 0x2C0
+#define ES01_2C1_VALID_PIXEL_PARAMETER_OF_SEGMENT2 0x2C1
+#define ES01_2C2_VALID_PIXEL_PARAMETER_OF_SEGMENT3 0x2C2
+#define ES01_2C3_VALID_PIXEL_PARAMETER_OF_SEGMENT4 0x2C3
+#define ES01_2C4_VALID_PIXEL_PARAMETER_OF_SEGMENT5 0x2C4
+#define ES01_2C5_VALID_PIXEL_PARAMETER_OF_SEGMENT6 0x2C5
+#define ES01_2C6_VALID_PIXEL_PARAMETER_OF_SEGMENT7 0x2C6
+#define ES01_2C7_VALID_PIXEL_PARAMETER_OF_SEGMENT8 0x2C7
+#define ES01_2C8_VALID_PIXEL_PARAMETER_OF_SEGMENT9 0x2C8
+#define ES01_2C9_VALID_PIXEL_PARAMETER_OF_SEGMENT10 0x2C9
+#define ES01_2CA_VALID_PIXEL_PARAMETER_OF_SEGMENT11 0x2CA
+#define ES01_2CB_VALID_PIXEL_PARAMETER_OF_SEGMENT12 0x2CB
+#define ES01_2CC_VALID_PIXEL_PARAMETER_OF_SEGMENT13 0x2CC
+#define ES01_2CD_VALID_PIXEL_PARAMETER_OF_SEGMENT14 0x2CD
+#define ES01_2CE_VALID_PIXEL_PARAMETER_OF_SEGMENT15 0x2CE
+#define ES01_2CF_VALID_PIXEL_PARAMETER_OF_SEGMENT16 0x2CF
+
+/* forward declarations */
+static STATUS OpenScanChip (PAsic chip);
+static STATUS CloseScanChip (PAsic chip);
+static STATUS SafeInitialChip (PAsic chip);
+static STATUS DRAM_Test (PAsic chip);
+#if SANE_UNUSED
+static STATUS SetPowerSave (PAsic chip);
+#endif
+static STATUS SetLineTimeAndExposure (PAsic chip);
+static STATUS CCDTiming (PAsic chip);
+static STATUS IsCarriageHome (PAsic chip, SANE_Bool * LampHome, SANE_Bool * TAHome);
+static STATUS InitTiming (PAsic chip);
+static STATUS GetChipStatus (PAsic chip, SANE_Byte Selector, SANE_Byte * ChipStatus);
+static STATUS SetAFEGainOffset (PAsic chip);
+static STATUS SetLEDTime (PAsic chip);
+static STATUS SetScanMode (PAsic chip, SANE_Byte bScanBits);
+static STATUS SetPackAddress (PAsic chip, unsigned short wXResolution,
+ unsigned short wWidth, unsigned short wX, double XRatioAdderDouble,
+ double XRatioTypeDouble,
+ SANE_Byte byClear_Pulse_Width,
+ unsigned short * PValidPixelNumber);
+static STATUS SetExtraSetting (PAsic chip, unsigned short wXResolution,
+ unsigned short wCCD_PixelNumber, SANE_Bool isCaribrate);
+
+
+/* Forward declarations */
+
+static STATUS Mustek_SendData (PAsic chip, unsigned short reg, SANE_Byte data);
+static STATUS Mustek_SendData2Byte (PAsic chip, unsigned short reg, SANE_Byte data);
+static STATUS Mustek_ReceiveData (PAsic chip, SANE_Byte * reg);
+static STATUS Mustek_WriteAddressLineForRegister (PAsic chip, SANE_Byte x);
+static STATUS WriteIOControl (PAsic chip, unsigned short wValue, unsigned short wIndex,
+ unsigned short wLength, SANE_Byte * lpbuf);
+static STATUS ReadIOControl (PAsic chip, unsigned short wValue, unsigned short wIndex,
+ unsigned short wLength, SANE_Byte * lpbuf);
+static STATUS Mustek_DMARead (PAsic chip, unsigned int size, SANE_Byte * lpdata);
+static STATUS Mustek_DMAWrite (PAsic chip, unsigned int size, SANE_Byte * lpdata);
+static STATUS Mustek_ClearFIFO (PAsic chip);
+static STATUS SetRWSize (PAsic chip, SANE_Byte ReadWrite, unsigned int size);
+
+/* Open Scanner by Scanner Name and return Chip Information */
+static STATUS Asic_Open (PAsic chip, SANE_Byte *pDeviceName);
+/* Close Scanner */
+static STATUS Asic_Close (PAsic chip);
+#if SANE_UNUSED
+/* Release Scanner Resource */
+static STATUS Asic_Release (PAsic chip);
+#endif
+/* Initialize Scanner Parameters */
+static STATUS Asic_Initialize (PAsic chip);
+/* Set Scan Window */
+static STATUS Asic_SetWindow (PAsic chip, SANE_Byte bScanBits,
+ unsigned short wXResolution, unsigned short wYResolution,
+ unsigned short wX, unsigned short wY, unsigned short wWidth, unsigned short wLength);
+/* Turn Lamp ON or OFF */
+static STATUS Asic_TurnLamp (PAsic chip, SANE_Bool isLampOn);
+/* Turn TA ON or OFF */
+static STATUS Asic_TurnTA (PAsic chip, SANE_Bool isTAOn);
+/* Reset some parameter of asic */
+static STATUS Asic_Reset (PAsic chip);
+/* Set scan source */
+static STATUS Asic_SetSource (PAsic chip, LIGHTSOURCE lsLightSource);
+/* Start scanner to scan */
+static STATUS Asic_ScanStart (PAsic chip);
+/* Stop scanner to scan */
+static STATUS Asic_ScanStop (PAsic chip);
+/* Read One Scan Line When Scanning */
+static STATUS Asic_ReadImage (PAsic chip, SANE_Byte * pBuffer, unsigned short LinesCount);
+#if SANE_UNUSED
+/* To Check Hard Key */
+static STATUS Asic_CheckFunctionKey (PAsic chip, SANE_Byte * key);
+#endif
+/* To Check if TA id connected */
+static STATUS Asic_IsTAConnected (PAsic chip, SANE_Bool *hasTA);
+#if SANE_UNUSED
+/* Download GammaTable to Scanner */
+static STATUS Asic_DownloadGammaTable (PAsic chip, void * lpBuffer);
+#endif
+/* For AdjustAD Calculate Scanner*/
+static STATUS Asic_ReadCalibrationData (PAsic chip, void * pBuffer,
+ unsigned int dwXferBytes, SANE_Byte bScanBits);
+/* Set motor move or not */
+static STATUS Asic_SetMotorType (PAsic chip, SANE_Bool isMotorMove, SANE_Bool isUniformSpeed);
+/* Move Motor Forward or Backword */
+static STATUS Asic_MotorMove (PAsic chip, SANE_Bool isForward, unsigned int dwTotalSteps);
+/* Move Motor to Home. */
+/* If isTA is TRUE, move TA to home, else move Lamp to home */
+static STATUS Asic_CarriageHome (PAsic chip, SANE_Bool isTA);
+/* For ShadingTable */
+static STATUS Asic_SetShadingTable (PAsic chip, unsigned short * lpWhiteShading,
+ unsigned short * lpDarkShading,
+ unsigned short wXResolution, unsigned short wWidth, unsigned short wX);
+/* Wait motor move to home. isTA no used */
+static STATUS Asic_WaitCarriageHome (PAsic chip, SANE_Bool isTA);
+/* Wait until asic idle */
+static STATUS Asic_WaitUnitReady (PAsic chip);
+/* Set Scan Parameter to Scanner */
+static STATUS Asic_SetCalibrate (PAsic chip, SANE_Byte bScanBits, unsigned short wXResolution,
+ unsigned short wYResolution, unsigned short wX, unsigned short wY,
+ unsigned short wWidth, unsigned short wLength, SANE_Bool isShading);
+/* Set AFE Parameter to Scanner */
+static STATUS Asic_SetAFEGainOffset (PAsic chip);
+
+/* ---------------------- asic motor defines -------------------------- */
+
+
+#define ACTION_MODE_ACCDEC_MOVE 0
+#define ACTION_MODE_UNIFORM_SPEED_MOVE 1
+
+
+typedef struct tagMOTOR_CURRENT_AND_PHASE
+{
+ SANE_Byte MoveType;
+ SANE_Byte FillPhase;
+ SANE_Byte MotorDriverIs3967;
+ SANE_Byte MotorCurrentTableA[32];
+ SANE_Byte MotorCurrentTableB[32];
+ SANE_Byte MotorPhaseTable[32];
+} MOTOR_CURRENT_AND_PHASE, LPMOTOR_CURRENT_AND_PHASE;
+
+typedef struct tagLLF_RAMACCESS
+{
+ SANE_Byte ReadWrite;
+ SANE_Byte IsOnChipGamma;
+ unsigned short LoStartAddress;
+ unsigned short HiStartAddress;
+ int RwSize;
+ SANE_Byte DramDelayTime;
+ SANE_Byte *BufferPtr;
+} LLF_RAMACCESS;
+
+typedef struct tagLLF_MOTOR_CURRENT_AND_PHASE
+{
+ SANE_Byte MoveType;
+ SANE_Byte FillPhase;
+ SANE_Byte MotorDriverIs3967;
+ SANE_Byte MotorCurrentTableA[32];
+ SANE_Byte MotorCurrentTableB[32];
+ SANE_Byte MotorPhaseTable[32];
+} LLF_MOTOR_CURRENT_AND_PHASE;
+
+typedef struct tagLLF_CALCULATEMOTORTABLE
+{
+ unsigned short StartSpeed;
+ unsigned short EndSpeed;
+ unsigned short AccStepBeforeScan;
+ SANE_Byte DecStepAfterScan;
+ unsigned short * lpMotorTable;
+} LLF_CALCULATEMOTORTABLE;
+
+typedef struct tagLLF_SETMOTORTABLE
+{
+ unsigned int TableSize;
+ SANE_Byte MotorTableAddress;
+ unsigned short *MotorTablePtr;
+} LLF_SETMOTORTABLE;
+
+typedef struct tagLLF_MOTORMOVE
+{
+ SANE_Byte ActionMode; /* 0: AccDec Mode, 1: Uniform Speed Mode, 2: Test Mode */
+ SANE_Byte ActionType; /* 0: Forward, 1: Backward, 2:Back To Home */
+ SANE_Byte MotorSelect; /* 0: Motor 0 only, 1: Motor 1 only, 2: Motor 0 & 1 */
+ SANE_Byte HomeSensorSelect; /* 0: Sensor 0, 1: Sensor 1, 2: Sensor 0 & 1, 3:Invert Sensor 1 */
+ unsigned short FixMoveSpeed;
+ unsigned int FixMoveSteps; /* 3 bytes */
+ SANE_Byte MotorSpeedUnit;
+ SANE_Byte MotorSyncUnit;
+ unsigned short AccStep; /*Max = 511 */
+ SANE_Byte DecStep; /* Max = 255 */
+ SANE_Byte MotorMoveUnit;
+ SANE_Byte WaitOrNoWait; /* 0: no wait, 1: wait */
+ SANE_Byte Lamp0PwmFreq; /* Lamp0 PWM freq */
+ SANE_Byte Lamp1PwmFreq; /* Lamp1 PWM freq */
+
+ unsigned short wForwardSteps;
+ unsigned short wScanAccSteps;
+ SANE_Byte bScanDecSteps;
+ unsigned short wFixScanSteps;
+ unsigned short wScanBackTrackingSteps;
+ unsigned short wScanRestartSteps;
+ unsigned short wScanBackHomeExtSteps;
+} LLF_MOTORMOVE;
+
+static STATUS CalculateMotorTable (LLF_CALCULATEMOTORTABLE *
+ lpCalculateMotorTable, unsigned short wYResolution);
+static STATUS LLFCalculateMotorTable (LLF_CALCULATEMOTORTABLE *
+ lpCalculateMotorTable);
+static STATUS LLFSetMotorCurrentAndPhase (PAsic chip,
+ LLF_MOTOR_CURRENT_AND_PHASE *
+ MotorCurrentAndPhase);
+static STATUS SetMotorStepTable (PAsic chip, LLF_MOTORMOVE * MotorStepsTable,
+ unsigned short wStartY, unsigned int dwScanImageSteps,
+ unsigned short wYResolution);
+static STATUS LLFSetMotorTable (PAsic chip,
+ LLF_SETMOTORTABLE * LLF_SetMotorTable);
+static STATUS SetMotorCurrent (PAsic chip, unsigned short dwMotorSpeed,
+ LLF_MOTOR_CURRENT_AND_PHASE * CurrentPhase);
+static STATUS LLFMotorMove (PAsic chip, LLF_MOTORMOVE * LLF_MotorMove);
+#if SANE_UNUSED
+static STATUS LLFStopMotorMove (PAsic chip);
+#endif
+static STATUS LLFSetRamAddress (PAsic chip, unsigned int dwStartAddr,
+ unsigned int dwEndAddr, SANE_Byte byAccessTarget);
+static STATUS LLFRamAccess (PAsic chip, LLF_RAMACCESS * RamAccess);
+static STATUS MotorBackHome (PAsic chip, SANE_Byte WaitOrNoWait);
+
+#endif
diff --git a/backend/mustek_usb2_high.c b/backend/mustek_usb2_high.c
new file mode 100644
index 0000000..a10dbc7
--- /dev/null
+++ b/backend/mustek_usb2_high.c
@@ -0,0 +1,3240 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Mustek.
+ Originally maintained by Mustek
+ Author:Jack Roy 2005.5.24
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#include <pthread.h> /* HOLD */
+#include <stdlib.h>
+
+/* local header files */
+#include "mustek_usb2_asic.c"
+
+#include "mustek_usb2_high.h"
+
+/* ******************++ spuicall_g.h ****************************/
+
+/*global variable HOLD: these should go to scanner structure */
+
+/*base type*/
+static SANE_Bool g_bOpened;
+static SANE_Bool g_bPrepared;
+static SANE_Bool g_isCanceled;
+static SANE_Bool g_bSharpen;
+static SANE_Bool g_bFirstReadImage;
+static SANE_Bool g_isScanning;
+static SANE_Bool g_isSelfGamma;
+
+static SANE_Byte g_bScanBits;
+static SANE_Byte *g_lpReadImageHead;
+
+static unsigned short s_wOpticalYDpi[] = { 1200, 600, 300, 150, 75, 0 };
+static unsigned short s_wOpticalXDpi[] = { 1200, 600, 300, 150, 75, 0 };
+static unsigned short g_X;
+static unsigned short g_Y;
+static unsigned short g_Width;
+static unsigned short g_Height;
+static unsigned short g_XDpi;
+static unsigned short g_YDpi;
+static unsigned short g_SWWidth;
+static unsigned short g_SWHeight;
+static unsigned short g_wPixelDistance; /*even & odd sensor problem */
+static unsigned short g_wLineDistance;
+static unsigned short g_wScanLinesPerBlock;
+static unsigned short g_wReadedLines;
+static unsigned short g_wReadImageLines;
+static unsigned short g_wReadyShadingLine;
+static unsigned short g_wStartShadingLinePos;
+static unsigned short g_wLineartThreshold;
+
+static unsigned int g_wtheReadyLines;
+static unsigned int g_wMaxScanLines;
+static unsigned int g_dwScannedTotalLines;
+static unsigned int g_dwImageBufferSize;
+static unsigned int g_BytesPerRow;
+static unsigned int g_SWBytesPerRow;
+static unsigned int g_dwCalibrationSize;
+static unsigned int g_dwBufferSize;
+
+static unsigned int g_dwTotalTotalXferLines;
+
+static unsigned short *g_pGammaTable;
+static unsigned char *g_pDeviceFile;
+
+static pthread_t g_threadid_readimage;
+
+/*user define type*/
+static COLORMODE g_ScanMode;
+static TARGETIMAGE g_tiTarget;
+static SCANTYPE g_ScanType = ST_Reflective;
+static SCANSOURCE g_ssScanSource;
+static PIXELFLAVOR g_PixelFlavor;
+
+static SUGGESTSETTING g_ssSuggest;
+static Asic g_chip;
+
+static int g_nSecLength, g_nDarkSecLength;
+static int g_nSecNum, g_nDarkSecNum;
+static unsigned short g_wCalWidth;
+static unsigned short g_wDarkCalWidth;
+static int g_nPowerNum;
+static unsigned short g_wStartPosition;
+
+static pthread_mutex_t g_scannedLinesMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t g_readyLinesMutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*for modify the last point*/
+static SANE_Byte * g_lpBefLineImageData = NULL;
+static SANE_Bool g_bIsFirstReadBefData = TRUE;
+static unsigned int g_dwAlreadyGetLines = 0;
+
+/* forward declarations */
+static SANE_Bool MustScanner_Init (void);
+static SANE_Bool MustScanner_GetScannerState (void);
+static SANE_Bool MustScanner_PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn);
+static SANE_Bool MustScanner_BackHome (void);
+static SANE_Bool MustScanner_Prepare (SANE_Byte bScanSource);
+#ifdef SANE_UNUSED
+static SANE_Bool MustScanner_AdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset,
+ SANE_Byte * bLastMin, SANE_Byte * bLastOffset,
+ unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound,
+ SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel,
+ unsigned short wStdMaxLevel);
+static SANE_Bool MustScanner_SecondAdjustOffset (int nTimes, SANE_Bool * bDirection,
+ SANE_Byte * bOffset, SANE_Byte * bLastMin,
+ SANE_Byte * bLastOffset, unsigned short * wMinValue,
+ SANE_Byte * bOffsetUpperBound,
+ SANE_Byte * bOffsetLowerBound,
+ unsigned short wStdMinLevel, unsigned short wStdMaxLevel);
+#endif
+static unsigned short MustScanner_FiltLower (unsigned short * pSort, unsigned short TotalCount, unsigned short LowCount,
+ unsigned short HighCount);
+static SANE_Bool MustScanner_GetRgb48BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetRgb48BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetRgb24BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetRgb24BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetMono16BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetMono16BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetMono8BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetMono8BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetMono1BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static SANE_Bool MustScanner_GetMono1BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount);
+static void *MustScanner_ReadDataFromScanner (void * dummy);
+static void MustScanner_PrepareCalculateMaxMin (unsigned short wResolution);
+static void MustScanner_CalculateMaxMin (SANE_Byte * pBuffer, unsigned short * lpMaxValue,
+ unsigned short * lpMinValue, unsigned short wResolution);
+
+static SANE_Byte QBET4 (SANE_Byte A, SANE_Byte B);
+static unsigned int GetScannedLines (void);
+static unsigned int GetReadyLines (void);
+static void AddScannedLines (unsigned short wAddLines);
+static void AddReadyLines (void);
+static void ModifyLinePoint (SANE_Byte * lpImageData,
+ SANE_Byte * lpImageDataBefore,
+ unsigned int dwBytesPerLine,
+ unsigned int dwLinesCount,
+ unsigned short wPixDistance, unsigned short wModPtCount);
+
+#include "mustek_usb2_reflective.c"
+#include "mustek_usb2_transparent.c"
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+ Routine Description:
+Parameters:
+ none
+Return value:
+ if initialize the scanner success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_Init ()
+{
+ DBG (DBG_FUNC, "MustScanner_Init: Call in\n");
+
+ g_chip.firmwarestate = FS_NULL;
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_FUNC, "MustScanner_Init: Asic_Open return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_Initialize (&g_chip))
+ {
+ DBG (DBG_FUNC, "MustScanner_Init: Asic_Initialize return error\n");
+ return FALSE;
+ }
+
+ g_dwImageBufferSize = 24L * 1024L * 1024L;
+ g_dwBufferSize = 64L * 1024L;
+ g_dwCalibrationSize = 64L * 1024L;
+ g_lpReadImageHead = NULL;
+
+ g_isCanceled = FALSE;
+ g_bFirstReadImage = TRUE;
+ g_bOpened = FALSE;
+ g_bPrepared = FALSE;
+ g_bSharpen = FALSE;
+
+ g_isScanning = FALSE;
+ g_isSelfGamma = FALSE;
+ g_pGammaTable = NULL;
+
+ if (NULL != g_pDeviceFile)
+ {
+ free (g_pDeviceFile);
+ g_pDeviceFile = NULL;
+ }
+
+ g_ssScanSource = SS_Reflective;
+ g_PixelFlavor = PF_BlackIs0;
+
+ Asic_Close (&g_chip);
+
+ DBG (DBG_FUNC, "MustScanner_Init: leave MustScanner_Init\n");
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ check the scanner connect status
+Parameters:
+ none
+Return value:
+ if scanner's status is OK
+ return TRUE
+ else
+ return FASLE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetScannerState ()
+{
+
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_FUNC, "MustScanner_GetScannerState: Asic_Open return error\n");
+ return FALSE;
+ }
+ else
+ {
+ Asic_Close (&g_chip);
+ return TRUE;
+ }
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Turn the lamp on or off
+Parameters:
+ isLampOn: turn the lamp on or off
+ isTALampOn: turn the TA lamp on or off
+Return value:
+ if operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn)
+{
+ SANE_Bool hasTA;
+ DBG (DBG_FUNC, "MustScanner_PowerControl: Call in\n");
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_FUNC, "MustScanner_PowerControl: Asic_Open return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_TurnLamp (&g_chip, isLampOn))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_PowerControl: Asic_TurnLamp return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_IsTAConnected (&g_chip, &hasTA))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_PowerControl: Asic_IsTAConnected return error\n");
+ return FALSE;
+ }
+
+ if (hasTA)
+ {
+ if (STATUS_GOOD != Asic_TurnTA (&g_chip, isTALampOn))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_PowerControl: Asic_TurnTA return error\n");
+ return FALSE;
+ }
+ }
+
+ Asic_Close (&g_chip);
+
+ DBG (DBG_FUNC,
+ "MustScanner_PowerControl: leave MustScanner_PowerControl\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Turn the carriage home
+Parameters:
+ none
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_BackHome ()
+{
+ DBG (DBG_FUNC, "MustScanner_BackHome: call in \n");
+
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_FUNC, "MustScanner_BackHome: Asic_Open return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_CarriageHome (&g_chip, FALSE))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_BackHome: Asic_CarriageHome return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_WaitUnitReady (&g_chip))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_BackHome: Asic_WaitUnitReady return error\n");
+ return FALSE;
+ }
+
+ Asic_Close (&g_chip);
+
+ DBG (DBG_FUNC, "MustScanner_BackHome: leave MustScanner_BackHome\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ prepare the scan image
+Parameters:
+ bScanSource: the scan source
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_Prepare (SANE_Byte bScanSource)
+{
+ DBG (DBG_FUNC, "MustScanner_Prepare: call in\n");
+
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+
+
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare: Asic_Open return error\n");
+ return FALSE;
+ }
+ if (STATUS_GOOD != Asic_WaitUnitReady (&g_chip))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_Prepare: Asic_WaitUnitReady return error\n");
+ return FALSE;
+ }
+
+ if (SS_Reflective == bScanSource)
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Reflective\n");
+ if (STATUS_GOOD != Asic_TurnLamp (&g_chip, TRUE))
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnLamp return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_SetSource (&g_chip, LS_REFLECTIVE))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_Prepare: Asic_SetSource return error\n");
+ return FALSE;
+ }
+ }
+ else if (SS_Positive == bScanSource)
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Positive\n");
+ if (STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE))
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnTA return error\n");
+ return FALSE;
+ }
+ if (STATUS_GOOD != Asic_SetSource (&g_chip, LS_POSITIVE))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_Prepare: Asic_SetSource return error\n");
+ return FALSE;
+ }
+ }
+ else if (SS_Negative == bScanSource)
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Negative\n");
+
+ if (STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE))
+ {
+ DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnTA return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_SetSource (&g_chip, LS_NEGATIVE))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_Prepare: Asic_SetSource return error\n");
+ return FALSE;
+ }
+ DBG (DBG_FUNC, "MustScanner_Prepare: Asic_SetSource return good\n");
+ }
+
+ Asic_Close (&g_chip);
+ g_bPrepared = TRUE;
+
+ DBG (DBG_FUNC, "MustScanner_Prepare: leave MustScanner_Prepare\n");
+ return TRUE;
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Adjuest the offset
+Parameters:
+ nTimes: Adjuest offset the times
+ bDirection: whether direction
+ bOffset: the data of offset
+ bLastMin: the last min data
+ bLastOffset: the last offset data
+ wMinValue: the min value of offset
+ bOffsetUpperBound: the upper bound of offset
+ bOffsetLowerBound: the lower bound of offset
+ wStdMinLevel: the min level of offset
+ wStdMaxLevel: the max level of offset
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_AdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset,
+ SANE_Byte * bLastMin, SANE_Byte * bLastOffset,
+ unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound,
+ SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel,
+ unsigned short wStdMaxLevel)
+{
+ if ((*wMinValue <= wStdMaxLevel) && (*wMinValue >= wStdMinLevel))
+ {
+ return TRUE;
+ }
+
+ if (nTimes == 0)
+ {
+ *bLastMin = LOSANE_Byte (*wMinValue);
+ *bLastOffset = *bOffset;
+
+ if (*wMinValue == 255)
+ {
+ *bOffset = 0;
+ }
+ else
+ {
+ *bOffset = 255;
+ }
+ }
+
+ if (nTimes == 1)
+ {
+ if (*wMinValue > *bLastMin)
+ {
+ if (*wMinValue > wStdMaxLevel && *bLastMin > wStdMaxLevel)
+ {
+ *bDirection = !(*bDirection);
+ return TRUE;
+ }
+
+ if (*wMinValue < wStdMinLevel && *bLastMin < wStdMinLevel)
+ return TRUE;
+ }
+
+ if (*wMinValue < *bLastMin)
+ {
+ if (*wMinValue < wStdMinLevel && *bLastMin < wStdMinLevel)
+ *bDirection = !(*bDirection);
+
+ if (*wMinValue > wStdMaxLevel && *bLastMin > wStdMaxLevel)
+ return TRUE;
+ }
+ }
+
+ if (nTimes > 1)
+ {
+ if (*wMinValue > *bLastMin)
+ {
+ SANE_Byte bTemp;
+
+ bTemp = *bOffset;
+
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+
+ if (nTimes > 2)
+ {
+ if (*wMinValue > wStdMaxLevel)
+ if (bDirection)
+ *bOffsetLowerBound = bTemp;
+ else
+ *bOffsetUpperBound = bTemp;
+
+
+ else if (bDirection)
+ *bOffsetUpperBound = bTemp;
+ else
+ *bOffsetLowerBound = bTemp;
+ }
+
+ *bLastOffset = bTemp;
+ *bLastMin = (SANE_Byte) * wMinValue;
+ }
+ else
+ {
+ SANE_Byte bTemp;
+
+ bTemp = *bOffset;
+
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+
+ if (nTimes > 2)
+ {
+ if (*wMinValue == *bLastMin)
+ {
+ if (*wMinValue > wStdMaxLevel)
+ {
+ if (!bDirection)
+ *bOffsetUpperBound = bTemp;
+ else
+ *bOffsetLowerBound = bTemp;
+ }
+ else
+ {
+ if (!bDirection)
+ *bOffsetLowerBound = bTemp;
+ else
+ *bOffsetUpperBound = bTemp;
+ }
+ }
+ else
+ {
+ if (*wMinValue > wStdMaxLevel)
+ {
+ if (bDirection)
+ *bOffsetUpperBound = bTemp;
+ else
+ *bOffsetLowerBound = bTemp;
+ }
+ else
+ {
+ if (bDirection)
+ *bOffsetLowerBound = bTemp;
+ else
+ *bOffsetUpperBound = bTemp;
+ }
+ }
+ }
+
+ *bLastOffset = bTemp;
+ *bLastMin = (SANE_Byte) * wMinValue;
+
+ }
+ } /* end of if(nTimes > 1) */
+
+ return TRUE;
+}
+#endif
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Adjuest the offset second times
+Parameters:
+ nTimes: Adjuest offset the times
+ bDirection: whether direction
+ bOffset: the data of offset
+ bLastMin: the last min data
+ bLastOffset: the last offset data
+ wMinValue: the min value of offset
+ bOffsetUpperBound: the upper bound of offset
+ bOffsetLowerBound: the lower bound of offset
+ wStdMinLevel: the min level of offset
+ wStdMaxLevel: the max level of offset
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_SecondAdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset,
+ SANE_Byte * bLastMin, SANE_Byte * bLastOffset,
+ unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound,
+ SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel,
+ unsigned short wStdMaxLevel)
+{
+ if ((*wMinValue <= wStdMaxLevel) && (*wMinValue >= wStdMinLevel))
+ {
+ return TRUE;
+ }
+ if (nTimes == 0)
+ {
+ *bLastMin = LOSANE_Byte (*wMinValue);
+ *bLastOffset = *bOffset;
+
+ if (*bDirection == 0)
+ {
+ *bOffsetUpperBound = *bLastOffset;
+ *bOffsetLowerBound = 0;
+ *bOffset = 0;
+ }
+ else
+ {
+ *bOffsetUpperBound = 255;
+ *bOffsetLowerBound = *bLastOffset;
+ *bOffset = 255;
+ }
+ }
+
+ if (nTimes >= 1)
+ {
+ if (*wMinValue > wStdMaxLevel)
+ {
+ if (*wMinValue > *bLastMin)
+ {
+ if (*bDirection == 0)
+ {
+ *bOffsetUpperBound = *bOffset;
+ }
+ else
+ {
+ *bOffsetLowerBound = *bOffset;
+ }
+
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+ }
+ else
+ {
+ if (*bDirection == 1)
+ {
+ *bOffsetUpperBound = *bOffset;
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+ }
+ else
+ {
+ *bOffsetLowerBound = *bOffset;
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+ }
+ }
+ } /*end of if(*wMinValue > MAX_OFFSET) */
+
+
+ if (*wMinValue < wStdMinLevel)
+ {
+ if (*wMinValue > *bLastMin)
+ {
+ if (*bDirection == 0)
+ {
+ *bOffsetLowerBound = *bOffset;
+ }
+ else
+ {
+ *bOffsetUpperBound = *bOffset;
+ }
+
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+ }
+ else
+ {
+ if (*bDirection == 1)
+ {
+ *bOffsetUpperBound = *bOffset;
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+ }
+ else
+ {
+ *bOffsetLowerBound = *bOffset;
+ *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
+ }
+ }
+ } /*end of if(*wMinValue > MIN_OFFSET) */
+ *bLastMin = (SANE_Byte) * wMinValue;
+ } /*end of if(nTimes >= 1) */
+
+ /* HOLD: missing return value! Correct? */
+ return FALSE;
+}
+#endif
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Filter the data
+Parameters:
+ pSort: the sort data
+ TotalCount: the total count
+ LowCount: the low count
+ HighCount: the upper count
+Return value:
+ the data of Filter
+***********************************************************************/
+static unsigned short
+MustScanner_FiltLower (unsigned short * pSort, unsigned short TotalCount, unsigned short LowCount,
+ unsigned short HighCount)
+{
+ unsigned short Bound = TotalCount - 1;
+ unsigned short LeftCount = HighCount - LowCount;
+ int Temp = 0;
+ unsigned int Sum = 0;
+ unsigned short i, j;
+
+ for (i = 0; i < Bound; i++)
+
+ {
+ for (j = 0; j < Bound - i; j++)
+ {
+ if (pSort[j + 1] > pSort[j])
+ {
+ Temp = pSort[j];
+ pSort[j] = pSort[j + 1];
+ pSort[j + 1] = Temp;
+ }
+ }
+ }
+
+ for (i = 0; i < LeftCount; i++)
+ Sum += pSort[i + LowCount];
+ return (unsigned short) (Sum / LeftCount);
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when single CCD and color is 48bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetRgb48BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned short wRLinePos = 0;
+ unsigned short wGLinePos = 0;
+ unsigned short wBLinePos = 0;
+ unsigned short wRTempData;
+ unsigned short wGTempData;
+ unsigned short wBTempData;
+ unsigned short i;
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: call in \n");
+
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+ TotalXferLines = 0;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ if (!isOrderInvert)
+ {
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
+ wGLinePos =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePos =
+ (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ wRTempData =
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
+ 0);
+ wRTempData +=
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
+ 1) << 8;
+ wGTempData =
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
+ 2);
+ wGTempData +=
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
+ 3) << 8;
+ wBTempData =
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
+ 4);
+ wBTempData +=
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
+ 5) << 8;
+ *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 2) =
+ LOBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 3) =
+ HIBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 4) =
+ LOBYTE (g_pGammaTable[wBTempData + 131072]);
+ *(lpLine + i * 6 + 5) =
+ HIBYTE (g_pGammaTable[wBTempData + 131072]);
+ }
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+ }
+
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");
+
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
+ wGLinePos =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePos =
+ (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ wRTempData =
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
+ 0);
+ wRTempData +=
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
+ 1) << 8;
+ wGTempData =
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
+ 2);
+ wGTempData +=
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
+ 3) << 8;
+ wBTempData =
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
+ 4);
+ wBTempData +=
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
+ 5) << 8;
+ *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 2) =
+ LOBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 3) =
+ HIBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 0) =
+ LOBYTE (g_pGammaTable[wBTempData + 131072]);
+ *(lpLine + i * 6 + 1) =
+ HIBYTE (g_pGammaTable[wBTempData + 131072]);
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");
+ break;
+ }
+ } /*end for */
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb48BitLine: leave MustScanner_GetRgb48BitLine\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when double CCD and color is 48bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetRgb48BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+
+ unsigned short wRLinePosOdd = 0;
+ unsigned short wGLinePosOdd = 0;
+ unsigned short wBLinePosOdd = 0;
+ unsigned short wRLinePosEven = 0;
+ unsigned short wGLinePosEven = 0;
+ unsigned short wBLinePosEven = 0;
+ unsigned int wRTempData;
+ unsigned int wGTempData;
+ unsigned int wBTempData;
+ unsigned int wNextTempData;
+ unsigned short i;
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: call in \n");
+
+ TotalXferLines = 0;
+ wWantedTotalLines = *wLinesCount;
+
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ if (!isOrderInvert)
+ {
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+ {
+ wRLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+ else
+ {
+ wRLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if (i + 1 != g_SWWidth)
+ {
+ wRTempData =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ i * 6 + 0);
+ wRTempData +=
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ i * 6 + 1) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 0);
+ wNextTempData +=
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 1) << 8;
+ wRTempData = (wRTempData + wNextTempData) >> 1;
+
+ wGTempData =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ i * 6 + 2);
+ wGTempData +=
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ i * 6 + 3) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 2);
+ wNextTempData +=
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 3) << 8;
+ wGTempData = (wGTempData + wNextTempData) >> 1;
+
+ wBTempData =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ i * 6 + 4);
+ wBTempData +=
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ i * 6 + 5) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 4);
+ wNextTempData +=
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 5) << 8;
+ wBTempData = (wBTempData + wNextTempData) >> 1;
+
+ *(lpLine + i * 6 + 0) =
+ LOBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 1) =
+ HIBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 2) =
+ LOBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 3) =
+ HIBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 4) =
+ LOBYTE (g_pGammaTable[wBTempData + 131072]);
+ *(lpLine + i * 6 + 5) =
+ HIBYTE (g_pGammaTable[wBTempData + 131072]);
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ wRTempData =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ i * 6 + 0);
+ wRTempData +=
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ i * 6 + 1) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 0);
+ wNextTempData +=
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 1) << 8;
+ wRTempData = (wRTempData + wNextTempData) >> 1;
+
+ wGTempData =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ i * 6 + 2);
+ wGTempData +=
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ i * 6 + 3) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 2);
+ wNextTempData +=
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 3) << 8;
+ wGTempData = (wGTempData + wNextTempData) >> 1;
+
+ wBTempData =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ i * 6 + 4);
+ wBTempData +=
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ i * 6 + 5) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 4);
+ wNextTempData +=
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 5) << 8;
+ wBTempData = (wBTempData + wNextTempData) >> 1;
+
+ *(lpLine + i * 6 + 0) =
+ LOBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 1) =
+ HIBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 2) =
+ LOBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 3) =
+ HIBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 4) =
+ LOBYTE (g_pGammaTable[wBTempData + 131072]);
+ *(lpLine + i * 6 + 5) =
+ HIBYTE (g_pGammaTable[wBTempData + 131072]);
+
+ i++;
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");
+ break;
+
+ }
+ }
+
+
+ }
+ else
+ {
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+ {
+ wRLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+ else
+ {
+ wRLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if ((i + 1) != g_SWWidth)
+ {
+ wRTempData =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ i * 6 + 0);
+ wRTempData +=
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ i * 6 + 1) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 0);
+ wNextTempData +=
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 1) << 8;
+ wRTempData = (wRTempData + wNextTempData) >> 1;
+
+ wGTempData =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ i * 6 + 2);
+ wGTempData +=
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ i * 6 + 3) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 2);
+ wNextTempData +=
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 3) << 8;
+ wGTempData = (wGTempData + wNextTempData) >> 1;
+
+ wBTempData =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ i * 6 + 4);
+ wBTempData +=
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ i * 6 + 5) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 4);
+ wNextTempData +=
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ (i + 1) * 6 + 5) << 8;
+ wBTempData = (wBTempData + wNextTempData) >> 1;
+
+ *(lpLine + i * 6 + 4) =
+ LOBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 5) =
+ HIBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 2) =
+ LOBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 3) =
+ HIBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 0) =
+ LOBYTE (g_pGammaTable[wBTempData + 131072]);
+ *(lpLine + i * 6 + 1) =
+ HIBYTE (g_pGammaTable[wBTempData + 131072]);
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ wRTempData =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ i * 6 + 0);
+ wRTempData +=
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ i * 6 + 1) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 0);
+ wNextTempData +=
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 1) << 8;
+ wRTempData = (wRTempData + wNextTempData) >> 1;
+
+ wGTempData =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ i * 6 + 2);
+ wGTempData +=
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ i * 6 + 3) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 2);
+ wNextTempData +=
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 3) << 8;
+ wGTempData = (wGTempData + wNextTempData) >> 1;
+
+
+ wBTempData =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ i * 6 + 4);
+ wBTempData +=
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ i * 6 + 5) << 8;
+ wNextTempData =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 4);
+ wNextTempData +=
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ (i + 1) * 6 + 5) << 8;
+ wBTempData = (wBTempData + wNextTempData) >> 1;
+
+ *(lpLine + i * 6 + 4) =
+ LOBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 5) =
+ HIBYTE (g_pGammaTable[wRTempData]);
+ *(lpLine + i * 6 + 2) =
+ LOBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 3) =
+ HIBYTE (g_pGammaTable[wGTempData + 65536]);
+ *(lpLine + i * 6 + 0) =
+ LOBYTE (g_pGammaTable[wBTempData + 131072]);
+ *(lpLine + i * 6 + 1) =
+ HIBYTE (g_pGammaTable[wBTempData + 131072]);
+ i++;
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");
+
+ break;
+ }
+ }
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb48BitLine1200DPI: leave MustScanner_GetRgb48BitLine1200DPI\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when single CCD and color is 24bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetRgb24BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned short wRLinePos = 0;
+ unsigned short wGLinePos = 0;
+ unsigned short wBLinePos = 0;
+ SANE_Byte byRed;
+ SANE_Byte byGreen;
+ SANE_Byte byBlue;
+ SANE_Byte bNextPixel = 0;
+ unsigned short i;
+
+ unsigned short tempR, tempG, tempB;
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: call in\n");
+
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+
+ wWantedTotalLines = *wLinesCount;
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: get wWantedTotalLines= %d\n",
+ wWantedTotalLines);
+
+ TotalXferLines = 0;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread create\n");
+
+ g_bFirstReadImage = FALSE;
+ }
+
+ if (!isOrderInvert)
+ {
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: !isOrderInvert\n");
+
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
+ wGLinePos =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePos =
+ (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ byRed =
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 3 +
+ 0);
+ bNextPixel =
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow +
+ (i + 1) * 3 + 0);
+ byRed = (byRed + bNextPixel) >> 1;
+
+ byGreen =
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 3 +
+ 1);
+ bNextPixel =
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow +
+ (i + 1) * 3 + 1);
+ byGreen = (byGreen + bNextPixel) >> 1;
+
+ byBlue =
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 3 +
+ 2);
+ bNextPixel =
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow +
+ (i + 1) * 3 + 2);
+ byBlue = (byBlue + bNextPixel) >> 1;
+
+#ifdef ENABLE_GAMMA
+ tempR = (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen));
+ tempG = (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue));
+ tempB = (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed));
+
+ *(lpLine + i * 3 + 0) =
+ (unsigned char) (*(g_pGammaTable + tempR));
+ *(lpLine + i * 3 + 1) =
+ (unsigned char) (*(g_pGammaTable + 4096 + tempG));
+ *(lpLine + i * 3 + 2) =
+ (unsigned char) (*(g_pGammaTable + 8192 + tempB));
+#else
+ *(lpLine + i * 3 + 0) = (unsigned char) byRed;
+ *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
+ *(lpLine + i * 3 + 2) = (unsigned char) byBlue;
+#endif
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: g_dwTotalTotalXferLines=%d,g_SWHeight=%d\n",
+ g_dwTotalTotalXferLines, g_SWHeight);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: g_SWBytesPerRow=%d\n",
+ g_SWBytesPerRow);
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: isOrderInvert is TRUE\n");
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
+ wGLinePos =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePos =
+ (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: before byRed\n");
+ byRed =
+ *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 3 +
+ 0);
+ bNextPixel = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + (i + 1) * 3 + 0); /*R-channel */
+ byRed = (byRed + bNextPixel) >> 1;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: before byGreen\n");
+
+ byGreen =
+ *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 3 +
+ 1);
+ bNextPixel = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + (i + 1) * 3 + 1); /*G-channel */
+ byGreen = (byGreen + bNextPixel) >> 1;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: before byBlue\n");
+
+ byBlue =
+ *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 3 +
+ 2);
+ bNextPixel = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + (i + 1) * 3 + 2); /*B-channel */
+ byBlue = (byBlue + bNextPixel) >> 1;
+
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: before set lpLine\n");
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: i=%d\n", i);
+#ifdef ENABLE_GAMMA
+ *(lpLine + i * 3 + 2) =
+ (unsigned
+ char) (*(g_pGammaTable +
+ (unsigned short) ((byRed << 4) |
+ QBET4 (byBlue, byGreen))));
+ *(lpLine + i * 3 + 1) =
+ (unsigned
+ char) (*(g_pGammaTable + 4096 +
+ (unsigned short) ((byGreen << 4) |
+ QBET4 (byRed, byBlue))));
+ *(lpLine + i * 3 + 0) =
+ (unsigned
+ char) (*(g_pGammaTable + 8192 +
+ (unsigned short) ((byBlue << 4) |
+ QBET4 (byGreen, byRed))));
+#else
+ *(lpLine + i * 3 + 2) = (unsigned char) byRed;
+ *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
+ *(lpLine + i * 3 + 0) = (unsigned char) byBlue;
+#endif
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: g_dwTotalTotalXferLines=%d,g_SWHeight=%d\n",
+ g_dwTotalTotalXferLines, g_SWHeight);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: g_SWBytesPerRow=%d\n",
+ g_SWBytesPerRow);
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");
+
+ break;
+ }
+ } /*end for */
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine: leave MustScanner_GetRgb24BitLine\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when double CCD and color is 24bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetRgb24BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ SANE_Byte *lpTemp;
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned short wRLinePosOdd = 0;
+ unsigned short wGLinePosOdd = 0;
+ unsigned short wBLinePosOdd = 0;
+ unsigned short wRLinePosEven = 0;
+ unsigned short wGLinePosEven = 0;
+ unsigned short wBLinePosEven = 0;
+ SANE_Byte byRed;
+ SANE_Byte byGreen;
+ SANE_Byte byBlue;
+ SANE_Byte bNextPixel = 0;
+ unsigned short i;
+
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: call in\n");
+
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ TotalXferLines = 0;
+ wWantedTotalLines = *wLinesCount;
+ lpTemp = lpLine;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread create\n");
+
+ g_bFirstReadImage = FALSE;
+ }
+
+ if (!isOrderInvert)
+ {
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
+ g_dwTotalTotalXferLines);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
+ g_Height);
+
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+ {
+ wRLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+ else
+ {
+ wRLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+
+
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if ((i + 1) != g_SWWidth)
+ {
+ byRed =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ i * 3 + 0);
+ bNextPixel = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 3 + 0); /*R-channel */
+ byRed = (byRed + bNextPixel) >> 1;
+
+ byGreen =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ i * 3 + 1);
+ bNextPixel = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 3 + 1); /*G-channel */
+ byGreen = (byGreen + bNextPixel) >> 1;
+
+ byBlue =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ i * 3 + 2);
+ bNextPixel = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 3 + 2); /*B-channel */
+ byBlue = (byBlue + bNextPixel) >> 1;
+#ifdef ENABLE_GAMMA
+ *(lpLine + i * 3 + 0) =
+ (unsigned
+ char) (*(g_pGammaTable +
+ (unsigned short) ((byRed << 4) |
+ QBET4 (byBlue, byGreen))));
+ *(lpLine + i * 3 + 1) =
+ (unsigned
+ char) (*(g_pGammaTable + 4096 +
+ (unsigned short) ((byGreen << 4) |
+ QBET4 (byRed, byBlue))));
+ *(lpLine + i * 3 + 2) =
+ (unsigned
+ char) (*(g_pGammaTable + 8192 +
+ (unsigned short) ((byBlue << 4) |
+ QBET4 (byGreen, byRed))));
+#else
+ *(lpLine + i * 3 + 0) = (unsigned char) byRed;
+ *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
+ *(lpLine + i * 3 + 2) = (unsigned char) byBlue;
+#endif
+
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ byRed =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ i * 3 + 0);
+ bNextPixel =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ (i + 1) * 3 + 0);
+ byRed = (byRed + bNextPixel) >> 1;
+
+ byGreen =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ i * 3 + 1);
+ bNextPixel =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ (i + 1) * 3 + 1);
+ byGreen = (byGreen + bNextPixel) >> 1;
+
+ byBlue =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ i * 3 + 2);
+ bNextPixel =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ (i + 1) * 3 + 2);
+ byBlue = (byBlue + bNextPixel) >> 1;
+#ifdef ENABLE_GAMMA
+ *(lpLine + i * 3 + 0) =
+ (unsigned
+ char) (*(g_pGammaTable +
+ (unsigned short) ((byRed << 4) |
+ QBET4 (byBlue, byGreen))));
+ *(lpLine + i * 3 + 1) =
+ (unsigned
+ char) (*(g_pGammaTable + 4096 +
+ (unsigned short) ((byGreen << 4) |
+ QBET4 (byRed, byBlue))));
+ *(lpLine + i * 3 + 2) =
+ (unsigned
+ char) (*(g_pGammaTable + 8192 +
+ (unsigned short) ((byBlue << 4) |
+ QBET4 (byGreen, byRed))));
+#else
+ *(lpLine + i * 3 + 0) = (unsigned char) byRed;
+ *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
+ *(lpLine + i * 3 + 2) = (unsigned char) byBlue;
+#endif
+ i++;
+ }
+ }
+
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
+ g_dwTotalTotalXferLines);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
+ g_Height);
+
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");
+
+ break;
+ }
+
+ }
+ }
+ else
+ {
+
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
+ g_dwTotalTotalXferLines);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
+ g_Height);
+
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+ {
+ wRLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+ else
+ {
+ wRLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wGLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wBLinePosEven =
+ (g_wtheReadyLines - g_wLineDistance * 2 -
+ g_wPixelDistance) % g_wMaxScanLines;
+ wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ wGLinePosOdd =
+ (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
+ wBLinePosOdd =
+ (g_wtheReadyLines -
+ g_wLineDistance * 2) % g_wMaxScanLines;
+ }
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if ((i + 1) != g_SWWidth)
+ {
+ byRed =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ i * 3 + 0);
+ bNextPixel =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ (i + 1) * 3 + 0);
+ byRed = (byRed + bNextPixel) >> 1;
+
+ byGreen =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ i * 3 + 1);
+ bNextPixel =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ (i + 1) * 3 + 1);
+ byGreen = (byGreen + bNextPixel) >> 1;
+
+ byBlue =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ i * 3 + 2);
+ bNextPixel =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ (i + 1) * 3 + 2);
+ byBlue = (byBlue + bNextPixel) >> 1;
+
+#ifdef ENABLE_GAMMA
+ *(lpLine + i * 3 + 2) =
+ (unsigned
+ char) (*(g_pGammaTable +
+ (unsigned short) ((byRed << 4) |
+ QBET4 (byBlue, byGreen))));
+ *(lpLine + i * 3 + 1) =
+ (unsigned
+ char) (*(g_pGammaTable + 4096 +
+ (unsigned short) ((byGreen << 4) |
+ QBET4 (byRed, byBlue))));
+ *(lpLine + i * 3 + 0) =
+ (unsigned
+ char) (*(g_pGammaTable + 8192 +
+ (unsigned short) ((byBlue << 4) |
+ QBET4 (byGreen, byRed))));
+#else
+ *(lpLine + i * 3 + 2) = (unsigned char) byRed;
+ *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
+ *(lpLine + i * 3 + 0) = (unsigned char) byBlue;
+#endif
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ byRed =
+ *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
+ i * 3 + 0);
+ bNextPixel =
+ *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
+ (i + 1) * 3 + 0);
+ byRed = (byRed + bNextPixel) >> 1;
+
+ byGreen =
+ *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
+ i * 3 + 1);
+ bNextPixel =
+ *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
+ (i + 1) * 3 + 1);
+ byGreen = (byGreen + bNextPixel) >> 1;
+
+ byBlue =
+ *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
+ i * 3 + 2);
+ bNextPixel =
+ *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
+ (i + 1) * 3 + 2);
+ byBlue = (byBlue + bNextPixel) >> 1;
+#ifdef ENABLE_GAMMA
+ *(lpLine + i * 3 + 2) =
+ (unsigned
+ char) (*(g_pGammaTable +
+ (unsigned short) ((byRed << 4) |
+ QBET4 (byBlue, byGreen))));
+ *(lpLine + i * 3 + 1) =
+ (unsigned
+ char) (*(g_pGammaTable + 4096 +
+ (unsigned short) ((byGreen << 4) |
+ QBET4 (byRed, byBlue))));
+ *(lpLine + i * 3 + 0) =
+ (unsigned
+ char) (*(g_pGammaTable + 8192 +
+ (unsigned short) ((byBlue << 4) |
+ QBET4 (byGreen, byRed))));
+#else
+ *(lpLine + i * 3 + 2) = (unsigned char) byRed;
+ *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
+ *(lpLine + i * 3 + 0) = (unsigned char) byBlue;
+#endif
+ i++;
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
+ g_dwTotalTotalXferLines);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
+ g_Height);
+
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");
+
+
+ break;
+ }
+ }
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetRgb24BitLine1200DPI: leave MustScanner_GetRgb24BitLine1200DPI\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when single CCD and color is 16bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetMono16BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned int wTempData;
+
+ unsigned short wLinePos = 0;
+ unsigned short i;
+
+ isOrderInvert = isOrderInvert;
+
+ DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: call in\n");
+
+ TotalXferLines = 0;
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wLinePos = g_wtheReadyLines % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ wTempData =
+ *(g_lpReadImageHead + wLinePos * g_BytesPerRow + i * 2 + 0);
+ wTempData +=
+ *(g_lpReadImageHead + wLinePos * g_BytesPerRow + i * 2 +
+ 1) << 8;
+ *(lpLine + i * 2 + 0) = LOBYTE (g_pGammaTable[wTempData]);
+ *(lpLine + i * 2 + 1) = HIBYTE (g_pGammaTable[wTempData]);
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread exit\n");
+
+ break;
+ }
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono16BitLine: leave MustScanner_GetMono16BitLine\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when double CCD and color is 16bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetMono16BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned int dwTempData;
+ unsigned short wLinePosOdd = 0;
+ unsigned short wLinePosEven = 0;
+ unsigned short i;
+ SANE_Byte * lpTemp = lpLine;
+
+ isOrderInvert = isOrderInvert;
+ DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: call in\n");
+
+ TotalXferLines = 0;
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono16BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+ {
+ wLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ }
+ else
+ {
+ wLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ }
+
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if ((i + 1) != g_SWWidth)
+ {
+ dwTempData =
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosOdd * g_BytesPerRow + i * 2 + 0));
+ dwTempData +=
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosOdd * g_BytesPerRow + i * 2 + 1) << 8);
+ dwTempData +=
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosEven * g_BytesPerRow + (i + 1) * 2 +
+ 0));
+ dwTempData +=
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosEven * g_BytesPerRow + (i + 1) * 2 +
+ 1) << 8);
+ dwTempData = g_pGammaTable[dwTempData >> 1];
+ *(lpLine + i * 2 + 0) = LOBYTE ((unsigned short) dwTempData);
+ *(lpLine + i * 2 + 1) = HIBYTE ((unsigned short) dwTempData);
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ dwTempData =
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosEven * g_BytesPerRow + i * 2 + 0));
+ dwTempData +=
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosEven * g_BytesPerRow + i * 2 + 1) << 8);
+ dwTempData +=
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosOdd * g_BytesPerRow + (i + 1) * 2 + 0));
+ dwTempData +=
+ (unsigned int) (*
+ (g_lpReadImageHead +
+ wLinePosOdd * g_BytesPerRow + (i + 1) * 2 +
+ 1) << 8);
+ dwTempData = g_pGammaTable[dwTempData >> 1];
+ *(lpLine + i * 2 + 0) = LOBYTE ((unsigned short) dwTempData);
+ *(lpLine + i * 2 + 1) = HIBYTE ((unsigned short) dwTempData);
+ i++;
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono16BitLine1200DPI: thread exit\n");
+
+ break;
+ }
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ /*for modify the last point */
+ if (g_bIsFirstReadBefData)
+ {
+ g_lpBefLineImageData = (SANE_Byte *) malloc (g_SWBytesPerRow);
+ if (NULL == g_lpBefLineImageData)
+ {
+ return FALSE;
+ }
+ memset (g_lpBefLineImageData, 0, g_SWBytesPerRow);
+ memcpy (g_lpBefLineImageData, lpTemp, g_SWBytesPerRow);
+ g_bIsFirstReadBefData = FALSE;
+ }
+
+ ModifyLinePoint (lpTemp, g_lpBefLineImageData, g_SWBytesPerRow,
+ wWantedTotalLines, 2, 4);
+
+ memcpy (g_lpBefLineImageData,
+ lpTemp + (wWantedTotalLines - 1) * g_SWBytesPerRow,
+ g_SWBytesPerRow);
+ g_dwAlreadyGetLines += wWantedTotalLines;
+ if (g_dwAlreadyGetLines >= g_SWHeight)
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono16BitLine1200DPI: free before line data!\n");
+ free (g_lpBefLineImageData);
+ g_lpBefLineImageData = NULL;
+ g_dwAlreadyGetLines = 0;
+ g_bIsFirstReadBefData = TRUE;
+ }
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono16BitLine1200DPI: leave MustScanner_GetMono16BitLine1200DPI\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when single CCD and color is 8bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetMono8BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+
+ unsigned short i;
+ unsigned short wLinePos = 0;
+
+ isOrderInvert = isOrderInvert;
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: call in\n");
+
+ TotalXferLines = 0;
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wLinePos = g_wtheReadyLines % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ *(lpLine + i) =
+ (SANE_Byte) * (g_pGammaTable +
+ (unsigned short) ((*
+ (g_lpReadImageHead +
+ wLinePos * g_BytesPerRow +
+ i) << 4) | (rand () & 0x0f)));
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread exit\n");
+
+ break;
+ }
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono8BitLine: leave MustScanner_GetMono8BitLine\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when double CCD and color is 8bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetMono8BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ SANE_Byte *lpTemp;
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+
+ unsigned short wLinePosOdd = 0;
+ unsigned short wLinePosEven = 0;
+ SANE_Byte byGray;
+ unsigned short i;
+ SANE_Byte bNextPixel = 0;
+
+ isOrderInvert = isOrderInvert;
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: call in\n");
+
+ TotalXferLines = 0;
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+ lpTemp = lpLine;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ for (; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+
+ {
+ wLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ }
+ else
+ {
+ wLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ }
+
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if ((i + 1) != g_SWWidth)
+ {
+ byGray =
+ *(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i);
+ bNextPixel =
+ *(g_lpReadImageHead + wLinePosEven * g_BytesPerRow +
+ (i + 1));
+ byGray = (byGray + bNextPixel) >> 1;
+
+ *(lpLine + i) =
+ (SANE_Byte) * (g_pGammaTable +
+ (byGray << 4 | (rand () & 0x0f)));
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ byGray =
+ *(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i);
+ bNextPixel =
+ *(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow +
+ (i + 1));
+ byGray = (byGray + bNextPixel) >> 1;
+
+ *(lpLine + i) =
+ (SANE_Byte) * (g_pGammaTable +
+ (byGray << 4 | (rand () & 0x0f)));
+ i++;
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow;
+ AddReadyLines ();
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread exit\n");
+
+ break;
+ }
+ }
+
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ /*for modify the last point */
+ if (g_bIsFirstReadBefData)
+ {
+ g_lpBefLineImageData = (SANE_Byte *) malloc (g_SWBytesPerRow);
+ if (NULL == g_lpBefLineImageData)
+ {
+ return FALSE;
+ }
+ memset (g_lpBefLineImageData, 0, g_SWBytesPerRow);
+ memcpy (g_lpBefLineImageData, lpTemp, g_SWBytesPerRow);
+ g_bIsFirstReadBefData = FALSE;
+ }
+
+ ModifyLinePoint (lpTemp, g_lpBefLineImageData, g_SWBytesPerRow,
+ wWantedTotalLines, 1, 4);
+
+ memcpy (g_lpBefLineImageData,
+ lpTemp + (wWantedTotalLines - 1) * g_SWBytesPerRow,
+ g_SWBytesPerRow);
+ g_dwAlreadyGetLines += wWantedTotalLines;
+ if (g_dwAlreadyGetLines >= g_SWHeight)
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono8BitLine1200DPI: free the before line data!\n");
+ free (g_lpBefLineImageData);
+ g_lpBefLineImageData = NULL;
+ g_dwAlreadyGetLines = 0;
+ g_bIsFirstReadBefData = TRUE;
+ }
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono8BitLine1200DPI: leave MustScanner_GetMono8BitLine1200DPI\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when single CCD and color is 1bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetMono1BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned short wLinePos;
+ unsigned short i;
+
+ isOrderInvert = isOrderInvert;
+
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: call in\n");
+
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ memset (lpLine, 0, wWantedTotalLines * g_SWWidth / 8);
+
+ for (TotalXferLines = 0; TotalXferLines < wWantedTotalLines;)
+
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ wLinePos = g_wtheReadyLines % g_wMaxScanLines;
+
+ for (i = 0; i < g_SWWidth; i++)
+ {
+ if (*(g_lpReadImageHead + wLinePos * g_BytesPerRow + i) >
+ g_wLineartThreshold)
+ {
+ *(lpLine + i / 8) += (0x80 >> (i % 8));
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += (g_SWBytesPerRow / 8);
+ AddReadyLines ();
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread exit\n");
+
+ break;
+ }
+ }
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono1BitLine: leave MustScanner_GetMono1BitLine\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Repair line when double CCD and color is 1bit
+Parameters:
+ lpLine: point to image be repaired
+ isOrderInvert: RGB or BGR
+ wLinesCount: how many line be repaired
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+MustScanner_GetMono1BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
+ unsigned short * wLinesCount)
+{
+ unsigned short wWantedTotalLines;
+ unsigned short TotalXferLines;
+ unsigned short i;
+ unsigned short wLinePosOdd;
+ unsigned short wLinePosEven;
+
+ isOrderInvert = isOrderInvert;
+
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: call in\n");
+
+ g_isCanceled = FALSE;
+ g_isScanning = TRUE;
+ wWantedTotalLines = *wLinesCount;
+
+ if (g_bFirstReadImage)
+ {
+ pthread_create (&g_threadid_readimage, NULL,
+ MustScanner_ReadDataFromScanner, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread create\n");
+ g_bFirstReadImage = FALSE;
+ }
+
+ memset (lpLine, 0, wWantedTotalLines * g_SWWidth / 8);
+
+ for (TotalXferLines = 0; TotalXferLines < wWantedTotalLines;)
+ {
+ if (g_dwTotalTotalXferLines >= g_SWHeight)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread exit\n");
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+ return TRUE;
+ }
+
+ if (GetScannedLines () > g_wtheReadyLines)
+ {
+ if (ST_Reflective == g_ScanType)
+ {
+ wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
+ wLinePosOdd =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ }
+ else
+ {
+ wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
+ wLinePosEven =
+ (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
+ }
+
+
+
+ for (i = 0; i < g_SWWidth;)
+ {
+ if ((i + 1) != g_SWWidth)
+ {
+ if (*(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i) >
+ g_wLineartThreshold)
+ *(lpLine + i / 8) += (0x80 >> (i % 8));
+ i++;
+ if (i >= g_SWWidth)
+ {
+ break;
+ }
+
+ if (*(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i)
+ > g_wLineartThreshold)
+ *(lpLine + i / 8) += (0x80 >> (i % 8));
+ i++;
+ }
+ }
+
+ TotalXferLines++;
+ g_dwTotalTotalXferLines++;
+ lpLine += g_SWBytesPerRow / 8;
+ AddReadyLines ();
+
+
+ }
+ if (g_isCanceled)
+ {
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+ DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread exit\n");
+
+ break;
+ }
+ } /*end for */
+
+ *wLinesCount = TotalXferLines;
+ g_isScanning = FALSE;
+
+ DBG (DBG_FUNC,
+ "MustScanner_GetMono1BitLine1200DPI: leave MustScanner_GetMono1BitLine1200DPI\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/21
+Routine Description:
+ prepare calculate Max and Min value
+Parameters:
+ wResolution: the scan resolution
+Return value:
+ none
+***********************************************************************/
+static void
+MustScanner_PrepareCalculateMaxMin (unsigned short wResolution)
+{
+ g_wDarkCalWidth = 52;
+ if (wResolution <= 600)
+ {
+ g_wCalWidth = ((5120 * wResolution / 600 + 511) >> 9) << 9;
+ g_wDarkCalWidth = g_wDarkCalWidth / (1200 / wResolution);
+
+ if (wResolution < 200)
+ {
+ g_nPowerNum = 3;
+ g_nSecLength = 8; /* 2^nPowerNum */
+ g_nDarkSecLength = g_wDarkCalWidth / 2; /* Dark has at least 2 sections */
+ }
+ else
+ {
+ g_nPowerNum = 6;
+ g_nSecLength = 64; /* 2^nPowerNum */
+ g_nDarkSecLength = g_wDarkCalWidth / 3;
+ }
+ }
+ else
+ {
+ g_nPowerNum = 6;
+ g_nSecLength = 64; /*2^nPowerNum */
+ g_wCalWidth = 10240;
+ g_nDarkSecLength = g_wDarkCalWidth / 5;
+ }
+
+ if (g_nDarkSecLength <= 0)
+ {
+ g_nDarkSecLength = 1;
+ }
+
+ g_wStartPosition = 13 * wResolution / 1200;
+ g_wCalWidth -= g_wStartPosition;
+
+
+ /* start of find Max value */
+ g_nSecNum = (int) (g_wCalWidth / g_nSecLength);
+
+ /* start of fin min value */
+ g_nDarkSecNum = (int) (g_wDarkCalWidth / g_nDarkSecLength);
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/21
+Routine Description:
+ calculate the Max and Min value
+Parameters:
+ pBuffer: the image data
+ lpMaxValue: the max value
+ lpMinValue: the min value
+ wResolution: the scan resolution
+Return value:
+ none
+***********************************************************************/
+static void
+MustScanner_CalculateMaxMin (SANE_Byte * pBuffer, unsigned short * lpMaxValue,
+ unsigned short * lpMinValue, unsigned short wResolution)
+{
+ unsigned short *wSecData = NULL, *wDarkSecData = NULL;
+ int i, j;
+
+ wResolution = wResolution;
+
+ wSecData = (unsigned short *) malloc (sizeof (unsigned short) * g_nSecNum);
+ if (wSecData == NULL)
+ {
+ return;
+ }
+ else
+ {
+ memset (wSecData, 0, g_nSecNum * sizeof (unsigned short));
+ }
+
+ for (i = 0; i < g_nSecNum; i++)
+ {
+
+ for (j = 0; j < g_nSecLength; j++)
+ wSecData[i] += *(pBuffer + g_wStartPosition + i * g_nSecLength + j);
+ wSecData[i] >>= g_nPowerNum;
+ }
+
+ *lpMaxValue = wSecData[0];
+ for (i = 0; i < g_nSecNum; i++)
+ {
+ if (*lpMaxValue < wSecData[i])
+ *lpMaxValue = wSecData[i];
+ }
+
+ free (wSecData);
+
+ wDarkSecData = (unsigned short *) malloc (sizeof (unsigned short) * g_nDarkSecNum);
+ if (wDarkSecData == NULL)
+ {
+ return;
+ }
+ else
+ {
+ memset (wDarkSecData, 0, g_nDarkSecNum * sizeof (unsigned short));
+ }
+
+ for (i = 0; i < g_nDarkSecNum; i++)
+ {
+ for (j = 0; j < g_nDarkSecLength; j++)
+ wDarkSecData[i] +=
+ *(pBuffer + g_wStartPosition + i * g_nDarkSecLength + j);
+
+ wDarkSecData[i] /= g_nDarkSecLength;
+ }
+
+ *lpMinValue = wDarkSecData[0];
+ for (i = 0; i < g_nDarkSecNum; i++)
+ {
+ if (*lpMinValue > wDarkSecData[i])
+ *lpMinValue = wDarkSecData[i];
+ }
+ free (wDarkSecData);
+}
+
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Read the data from scanner
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static void *
+MustScanner_ReadDataFromScanner (void * dummy)
+{
+ unsigned short wTotalReadImageLines = 0;
+ unsigned short wWantedLines = g_Height;
+ SANE_Byte * lpReadImage = g_lpReadImageHead;
+ SANE_Bool isWaitImageLineDiff = FALSE;
+ unsigned int wMaxScanLines = g_wMaxScanLines;
+ unsigned short wReadImageLines = 0;
+ unsigned short wScanLinesThisBlock;
+ unsigned short wBufferLines = g_wLineDistance * 2 + g_wPixelDistance;
+
+ dummy = dummy;
+ DBG (DBG_FUNC,
+ "MustScanner_ReadDataFromScanner: call in, and in new thread\n");
+
+ while (wTotalReadImageLines < wWantedLines && g_lpReadImageHead)
+ {
+ if (!isWaitImageLineDiff)
+ {
+ wScanLinesThisBlock =
+ (wWantedLines - wTotalReadImageLines) <
+ g_wScanLinesPerBlock ? (wWantedLines -
+ wTotalReadImageLines) :
+ g_wScanLinesPerBlock;
+
+ DBG (DBG_FUNC,
+ "MustScanner_ReadDataFromScanner: wWantedLines=%d\n",
+ wWantedLines);
+
+ DBG (DBG_FUNC,
+ "MustScanner_ReadDataFromScanner: wScanLinesThisBlock=%d\n",
+ wScanLinesThisBlock);
+
+ if (STATUS_GOOD !=
+ Asic_ReadImage (&g_chip, lpReadImage, wScanLinesThisBlock))
+ {
+ DBG (DBG_FUNC,
+ "MustScanner_ReadDataFromScanner:Asic_ReadImage return error\n");
+ DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner:thread exit\n");
+ return NULL;
+ }
+
+ /*has read in memroy Buffer */
+ wReadImageLines += wScanLinesThisBlock;
+
+ AddScannedLines (wScanLinesThisBlock);
+
+ wTotalReadImageLines += wScanLinesThisBlock;
+
+ lpReadImage += wScanLinesThisBlock * g_BytesPerRow;
+
+ /*Buffer is full */
+ if (wReadImageLines >= wMaxScanLines)
+ {
+ lpReadImage = g_lpReadImageHead;
+ wReadImageLines = 0;
+ }
+
+ if ((g_dwScannedTotalLines - GetReadyLines ())
+ >= (wMaxScanLines - (wBufferLines + g_wScanLinesPerBlock))
+ && g_dwScannedTotalLines > GetReadyLines ())
+ {
+ isWaitImageLineDiff = TRUE;
+ }
+ }
+ else if (g_dwScannedTotalLines <=
+ GetReadyLines () + wBufferLines + g_wScanLinesPerBlock)
+ {
+ isWaitImageLineDiff = FALSE;
+ }
+
+ pthread_testcancel ();
+ }
+
+ DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: Read image ok\n");
+ DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: thread exit\n");
+ DBG (DBG_FUNC,
+ "MustScanner_ReadDataFromScanner: leave MustScanner_ReadDataFromScanner\n");
+ return NULL;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/26
+Routine Description:
+ get the lines of scanned
+Parameters:
+ none
+Return value:
+ the lines of scanned
+***********************************************************************/
+static unsigned int
+GetScannedLines ()
+{
+ unsigned int dwScannedLines = 0;
+
+ pthread_mutex_lock (&g_scannedLinesMutex);
+ dwScannedLines = g_dwScannedTotalLines;
+ pthread_mutex_unlock (&g_scannedLinesMutex);
+
+ return dwScannedLines;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/26
+
+Routine Description:
+ get lines which pass to superstratum
+Parameters:
+ none
+Return value:
+ the lines which pass to superstratum
+***********************************************************************/
+static unsigned int
+GetReadyLines ()
+{
+ unsigned int dwReadyLines = 0;
+
+ pthread_mutex_lock (&g_readyLinesMutex);
+ dwReadyLines = g_wtheReadyLines;
+ pthread_mutex_unlock (&g_readyLinesMutex);
+
+ return dwReadyLines;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/26
+Routine Description:
+ add the scanned total lines
+Parameters:
+ wAddLines: add the lines
+Return value:
+ none
+***********************************************************************/
+static void
+AddScannedLines (unsigned short wAddLines)
+{
+ pthread_mutex_lock (&g_scannedLinesMutex);
+
+ g_dwScannedTotalLines += wAddLines;
+
+ pthread_mutex_unlock (&g_scannedLinesMutex);
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/26
+Routine Description:
+ add the ready lines
+Parameters:
+ none
+Return value:
+ none
+***********************************************************************/
+static void
+AddReadyLines ()
+{
+ pthread_mutex_lock (&g_readyLinesMutex);
+ g_wtheReadyLines++;
+ pthread_mutex_unlock (&g_readyLinesMutex);
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/26
+Routine Description:
+ modify the point
+Parameters:
+ lpImageData: the data of image
+ lpImageDataBefore: the data of before line image
+ dwBytesPerLine: the bytes of per line
+ dwLinesCount: the line count
+ wPixDistance: the pixel distance
+ wModPtCount: the modify point count
+Return value:
+ none
+***********************************************************************/
+static void
+ModifyLinePoint (SANE_Byte * lpImageData,
+ SANE_Byte * lpImageDataBefore,
+ unsigned int dwBytesPerLine,
+ unsigned int dwLinesCount, unsigned short wPixDistance, unsigned short wModPtCount)
+{
+ unsigned short i = 0;
+ unsigned short j = 0;
+ unsigned short wLines = 0;
+ unsigned int dwWidth = dwBytesPerLine / wPixDistance;
+ for (i = wModPtCount; i > 0; i--)
+ {
+ for (j = 0; j < wPixDistance; j++)
+ {
+ /*modify the first line */
+ *(lpImageData + (dwWidth - i) * wPixDistance + j) =
+ (*(lpImageData + (dwWidth - i - 1) * wPixDistance + j) +
+ *(lpImageDataBefore + (dwWidth - i) * wPixDistance + j)) / 2;
+ /*modify other lines */
+ for (wLines = 1; wLines < dwLinesCount; wLines++)
+ {
+ unsigned int dwBytesBefor = (wLines - 1) * dwBytesPerLine;
+ unsigned int dwBytes = wLines * dwBytesPerLine;
+ *(lpImageData + dwBytes + (dwWidth - i) * wPixDistance + j) =
+ (*
+ (lpImageData + dwBytes + (dwWidth - i - 1) * wPixDistance +
+ j) + *(lpImageData + dwBytesBefor + (dwWidth -
+ i) * wPixDistance +
+ j)) / 2;
+ }
+ }
+ }
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Modifiy the image data
+Parameters:
+ A: the input the image data
+ B: the input the iamge data
+Return value:
+ the modified data
+***********************************************************************/
+static SANE_Byte
+QBET4 (SANE_Byte A, SANE_Byte B)
+{
+ SANE_Byte bQBET[16][16] = {
+ {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
+ {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
+ {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
+ {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
+ {1, 1, 1, 1, 3, 3, 3, 3, 6, 6, 6, 6, 10, 10, 11, 11},
+ {1, 1, 1, 1, 3, 3, 3, 3, 6, 6, 6, 6, 10, 10, 11, 11},
+ {2, 2, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 10, 10, 11, 11},
+ {2, 2, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 10, 10, 11, 11},
+ {4, 4, 4, 4, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
+ {4, 4, 4, 4, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
+ {5, 5, 5, 5, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
+ {5, 5, 5, 5, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
+ {8, 8, 8, 8, 10, 10, 10, 10, 13, 13, 13, 13, 15, 15, 15, 15},
+ {8, 8, 8, 8, 10, 10, 10, 10, 13, 13, 13, 13, 15, 15, 15, 15},
+ {9, 9, 9, 9, 11, 11, 11, 11, 14, 14, 14, 14, 15, 15, 15, 15},
+ {9, 9, 9, 9, 11, 11, 11, 11, 14, 14, 14, 14, 15, 15, 15, 15}
+ };
+
+ A = A & 0x0f;
+ B = B & 0x0f;
+ return bQBET[A][B];
+} /* end of the file MustScanner.c */
diff --git a/backend/mustek_usb2_high.h b/backend/mustek_usb2_high.h
new file mode 100644
index 0000000..33f463f
--- /dev/null
+++ b/backend/mustek_usb2_high.h
@@ -0,0 +1,263 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Mustek.
+ Originally maintained by Mustek
+ Author:Jack Roy 2005.5.24
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#ifndef MUSTEK_USB2_HIGH_H
+#define MUSTEK_USB2_HIGH_H
+
+/* const use in structures*/
+
+/*scan mode*/
+typedef unsigned short SCANMODE, *LPSCANMODE;
+#define SM_TEXT 0x00
+#define SM_GRAY 0x01
+#define SM_RGB24 0x02
+#define SM_GRAY10 0x03
+#define SM_RGB30 0x04
+#define SM_GRAY12 0x05
+#define SM_RGB36 0x06
+#define SM_GRAY14 0x07
+#define SM_RGB42 0x08
+#define SM_GRAY16 0x09
+#define SM_RGB48 0x0a
+
+/*pixel flavor*/
+typedef SANE_Byte PIXELFLAVOR, *LPPIXELFLAVOR;
+#define PF_BlackIs0 0x00
+#define PF_WhiteIs0 0x01
+
+/*scan source*/
+typedef SANE_Byte SCANSOURCE, *LPSCANSOURCE;
+#define SS_Reflective 0x00
+#define SS_Positive 0x01
+#define SS_Negative 0x02
+#define SS_ADF 0x03
+
+/*RGB order*/
+typedef unsigned short RGBORDER, *LPRGBORDER;
+#define RO_RGB 0x00
+#define RO_BGR 0x01
+
+/* structures use in parameters of export function*/
+
+typedef struct tagGAMMAINFO
+{
+ SCANMODE smScanMode;
+ unsigned short wInputGammaBits;
+ unsigned short wOutputGammaBits;
+} GAMMAINFO, *LPGAMMAINFO;
+
+typedef struct tagGETPARAMETERS
+{
+ unsigned short wSourceXDPI;
+ unsigned short wSourceYDPI;
+ unsigned int dwLineByteWidth;
+ unsigned int dwLength;
+} GETPARAMETERS, *LPGETPARAMETERS;
+
+typedef struct tagFRAME
+{
+ unsigned short x1;
+ unsigned short y1;
+ unsigned short x2;
+ unsigned short y2;
+} FRAME, *LPFRAME;
+
+typedef struct tagSETPARAMETERS
+{
+ FRAME fmArea;
+ unsigned short wTargetDPI;
+ SCANMODE smScanMode;
+ unsigned short wLinearThreshold; /*threshold for Line art mode */
+ PIXELFLAVOR pfPixelFlavor;
+ SCANSOURCE ssScanSource;
+ unsigned short * pGammaTable;
+} SETPARAMETERS, *LPSETPARAMETERS;
+
+typedef struct tagIMAGEROWS
+{
+ RGBORDER roRgbOrder;
+ unsigned short wWantedLineNum;
+ unsigned short wXferedLineNum;
+ SANE_Byte * pBuffer;
+} IMAGEROWS, *LPIMAGEROWS;
+
+
+/*Macro define*/
+
+#define R_GAIN 0
+#define G_GAIN 0
+#define B_GAIN 0
+#define R_OFFSET 0
+#define G_OFFSET 0
+#define B_OFFSET 0
+#define R_DIRECTION 0
+#define G_DIRECTION 0
+#define B_DIRECTION 0
+
+/* use for adjust AD's offset*/
+
+/* for Reflective*/
+#define REFL_DARK_MAX_LEVEL 20
+#define REFL_DARK_MIN_LEVEL 10
+#define REFL_WHITE_MAX_LEVEL 220
+#define REFL_WHITE_MIN_LEVEL 210
+#define REFL_MAX_LEVEL_RANGE 210
+#define REFL_MIN_LEVEL_RANGE 190
+
+/*for Transparent*/
+#define TRAN_DARK_MAX_LEVEL 20
+#define TRAN_DARK_MIN_LEVEL 10
+#define TRAN_WHITE_MAX_LEVEL 220
+#define TRAN_WHITE_MIN_LEVEL 210
+#define TRAN_MAX_LEVEL_RANGE 210
+#define TRAN_MIN_LEVEL_RANGE 190
+
+
+/* in 600 dpi*/
+#define FIND_LEFT_TOP_WIDTH_IN_DIP 512
+#define FIND_LEFT_TOP_HEIGHT_IN_DIP 180
+#define FIND_LEFT_TOP_CALIBRATE_RESOLUTION 600
+
+#define TA_FIND_LEFT_TOP_WIDTH_IN_DIP 2668
+#define TA_FIND_LEFT_TOP_HEIGHT_IN_DIP 300
+
+#define TA_MOTOR_BACK_STEP_AFTER_FIND_BOUNDARY 150
+#define TA_MOTOR_FORWARD_STEP_AFTER_READ_WHITE_DATA 1100
+
+/*must be 8x*/
+#define LINE_CALIBRATION__16BITS_HEIGHT 40
+
+/* the length from block bar to start Calibration position*/
+#define BEFORE_SCANNING_MOTOR_FORWARD_PIXEL 40
+
+#define PRE_MOVE_MOTOR_LENGTH_IN_DPI 1450
+
+/* if the motor is 1/8 step, setup MOTOR_STEP_MULTI as 8
+ if the motor is 1/4 step, setup MOTOR_STEP_MULTI as 4
+ if the motor is full step, setup MOTOR_STEP_MULTI as 1
+#define MOTOR_EIGHTH_STEP*/
+#ifdef MOTOR_EIGHTH_STEP
+#define MOTOR_STEP_MULTI 8
+#define GPIO_95_Config 0x68
+#else
+#define MOTOR_STEP_MULTI 4
+
+#define GPIO_95_Config 0x60
+#endif
+
+#define TRAN_START_POS 4550
+
+/* in 300dpi*/
+#define MAX_SCANNING_WIDTH 2550 /*just for A4 */
+#define MAX_SCANNING_HEIGHT 3540 /*just for A4 */
+
+#define INIFILENAME "./msam.ini"
+
+/*enable gamma*/
+#define ENABLE_GAMMA
+
+/*save debug image*/
+/*#define DEBUG_SAVE_IMAGE*/
+
+/*type define*/
+typedef unsigned char SCANTYPE;
+#define ST_Reflective 0x00
+#define ST_Transparent 0x01
+
+typedef enum tagCOLORMODE
+{
+ CM_RGB48 = 0,
+ CM_RGB42 = 1,
+ CM_RGB36 = 2,
+ CM_RGB30 = 3,
+ CM_RGB24 = 4,
+ CM_GRAY16 = 5,
+ CM_GRAY14 = 6,
+ CM_GRAY12 = 7,
+ CM_GRAY10 = 8,
+ CM_GRAY8 = 9,
+ CM_TEXT = 10,
+ CM_RGB48ext = 11,
+ CM_RGB42ext = 12,
+ CM_RGB36ext = 13,
+ CM_RGB30ext = 14,
+ CM_RGB24ext = 15,
+ CM_GRAY16ext = 16,
+ CM_GRAY14ext = 17,
+ CM_GRAY12ext = 18,
+ CM_GRAY10ext = 19,
+ CM_GRAY8ext = 20,
+ CM_TEXText = 21
+} COLORMODE, *PCOLORMODE;
+
+typedef struct tagTARGETIMAGE
+{
+ SANE_Bool isOptimalSpeed;
+ COLORMODE cmColorMode;
+ unsigned short wDpi;
+ unsigned short wX;
+ unsigned short wY;
+ unsigned short wWidth;
+ unsigned short wHeight;
+ SANE_Byte bScanSource;
+} TARGETIMAGE, *PTARGETIMAGE;
+
+typedef struct tagSUGGESTSETTING
+{
+ COLORMODE cmScanMode;
+ unsigned short wXDpi;
+ unsigned short wYDpi;
+ unsigned short wX;
+ unsigned short wY;
+ unsigned short wWidth;
+ unsigned short wHeight;
+ unsigned int dwBytesPerRow;
+} SUGGESTSETTING, *PSUGGESTSETTING;
+
+
+
+#endif
diff --git a/backend/mustek_usb2_reflective.c b/backend/mustek_usb2_reflective.c
new file mode 100644
index 0000000..7bf954c
--- /dev/null
+++ b/backend/mustek_usb2_reflective.c
@@ -0,0 +1,1943 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Mustek.
+ Originally maintained by Mustek
+ Author:Jack Roy 2005.5.24
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+/* forward declarations */
+
+static SANE_Bool Reflective_Reset (void);
+static SANE_Bool Reflective_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest);
+static SANE_Bool Reflective_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi,
+ SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width,
+ unsigned short Height);
+static SANE_Bool Reflective_StopScan (void);
+static SANE_Bool Reflective_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert);
+static SANE_Bool Reflective_AdjustAD (void);
+static SANE_Bool Reflective_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY);
+static SANE_Bool Reflective_LineCalibration16Bits (void);
+static SANE_Bool Reflective_PrepareScan (void);
+
+/*function description*/
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ reset the scanner status
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ els
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_Reset ()
+{
+ DBG (DBG_FUNC, "Reflective_Reset: call in\n");
+
+ if (g_bOpened)
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: scanner has been opened\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_Open return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_Reset (&g_chip))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_Reset return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_SetSource (&g_chip, LS_REFLECTIVE))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_SetSource return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_TurnLamp (&g_chip, TRUE))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_TurnLamp return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_Close (&g_chip))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_Close return error\n");
+ return FALSE;
+ }
+
+ g_Y = 0;
+ g_X = 0;
+ g_Width = 0;
+ g_SWWidth = 0;
+ g_Height = 0;
+ g_SWHeight = 0;
+
+ g_wLineartThreshold = 128;
+ g_dwTotalTotalXferLines = 0;
+ g_bFirstReadImage = TRUE;
+
+ g_pGammaTable = NULL;
+
+ if (NULL != g_pDeviceFile)
+ {
+ free (g_pDeviceFile);
+ g_pDeviceFile = NULL;
+ }
+
+ DBG (DBG_FUNC, "Reflective_Reset: exit\n");
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ get the suggest parameter of scaning
+Parameters:
+ pTarget: the information of scaning
+ pSuggest: suggest parameter of scaning
+Return value:
+ if the operation is success
+ return TRUE
+ els
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest)
+{
+ int i;
+ unsigned short wMaxWidth, wMaxHeight;
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: call in\n");
+
+ if (NULL == pTarget || NULL == pSuggest)
+ {
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: parameters error\n");
+ return FALSE;
+ }
+
+ /*1. Looking up Optical Y Resolution */
+ for (i = 0; s_wOpticalYDpi[i] != 0; i++)
+ {
+ if (s_wOpticalYDpi[i] <= pTarget->wDpi)
+ {
+ pSuggest->wYDpi = s_wOpticalYDpi[i];
+ break;
+ }
+ }
+ if (s_wOpticalYDpi[i] == 0)
+ {
+ i--;
+ pSuggest->wYDpi = s_wOpticalYDpi[i];
+ }
+
+ /*2. Looking up Optical X Resolution */
+ for (i = 0; s_wOpticalXDpi[i] != 0; i++)
+ {
+ if (s_wOpticalXDpi[i] <= pTarget->wDpi)
+ {
+ pSuggest->wXDpi = s_wOpticalXDpi[i];
+ break;
+ }
+ }
+ if (s_wOpticalXDpi[i] == 0)
+ {
+ i--;
+ pSuggest->wXDpi = s_wOpticalXDpi[i];
+ }
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wDpi = %d\n",
+ pTarget->wDpi);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wXDpi = %d\n",
+ pSuggest->wXDpi);
+
+
+
+
+
+
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wYDpi = %d\n",
+ pSuggest->wYDpi);
+
+ /*3. suggest scan area */
+ pSuggest->wX =
+ (unsigned short) (((unsigned int) (pTarget->wX) * (unsigned int) (pSuggest->wXDpi)) /
+ (unsigned int) (pTarget->wDpi));
+ pSuggest->wY =
+ (unsigned short) (((unsigned int) (pTarget->wY) * (unsigned int) (pSuggest->wYDpi)) /
+ (unsigned int) (pTarget->wDpi));
+ pSuggest->wWidth =
+ (unsigned short) (((unsigned int) (pTarget->wWidth) * (unsigned int) (pSuggest->wXDpi)) /
+ (unsigned int) (pTarget->wDpi));
+ pSuggest->wHeight =
+ (unsigned short) (((unsigned int) (pTarget->wHeight) * (unsigned int) (pSuggest->wYDpi)) /
+ (unsigned int) (pTarget->wDpi));
+
+ pSuggest->wWidth = (pSuggest->wWidth / 2) * 2;
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wX = %d\n", pTarget->wX);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wY = %d\n", pTarget->wY);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wWidth = %d\n",
+ pTarget->wWidth);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wHeight = %d\n",
+ pTarget->wHeight);
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wX = %d\n", pSuggest->wX);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wY = %d\n", pSuggest->wY);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wWidth = %d\n",
+ pSuggest->wWidth);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wHeight = %d\n",
+ pSuggest->wHeight);
+
+ if (pTarget->cmColorMode == CM_TEXT)
+ {
+ pSuggest->wWidth = ((pSuggest->wWidth + 7) >> 3) << 3;
+ if (pSuggest->wWidth < 8)
+ pSuggest->wWidth = 8;
+ }
+
+ /*4. check width and height */
+ wMaxWidth = (MAX_SCANNING_WIDTH * pSuggest->wXDpi) / 300;
+ wMaxHeight = (3480 * pSuggest->wYDpi) / 300; /* 3480 for bumping */
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: wMaxWidth = %d\n", wMaxWidth);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: wMaxHeight = %d\n", wMaxHeight);
+
+ if (CM_TEXT == pTarget->cmColorMode)
+ {
+ wMaxWidth = (wMaxWidth >> 3) << 3;
+ }
+ if (pSuggest->wWidth > wMaxWidth)
+ {
+ pSuggest->wWidth = wMaxWidth;
+ }
+
+
+ if (pSuggest->wHeight > wMaxHeight)
+ {
+ pSuggest->wHeight = wMaxHeight;
+ }
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: g_Width=%d\n", g_Width);
+
+ g_Width = ((pSuggest->wWidth + 15) >> 4) << 4; /*Real Scan Width */
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: again, g_Width=%d\n", g_Width);
+
+ g_Height = pSuggest->wHeight;
+
+ if (pTarget->isOptimalSpeed)
+ {
+ switch (pTarget->cmColorMode)
+ {
+ case CM_RGB48:
+ pSuggest->cmScanMode = CM_RGB48;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6);
+ break;
+ case CM_RGB24:
+ pSuggest->cmScanMode = CM_RGB24ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3);
+ break;
+ case CM_GRAY16:
+ pSuggest->cmScanMode = CM_GRAY16ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2);
+ break;
+ case CM_GRAY8:
+ pSuggest->cmScanMode = CM_GRAY8ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth));
+ break;
+ case CM_TEXT:
+ pSuggest->cmScanMode = CM_TEXT;
+ pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (pTarget->cmColorMode)
+ {
+ case CM_RGB48:
+ pSuggest->cmScanMode = CM_RGB48;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6);
+ break;
+ case CM_RGB24:
+ pSuggest->cmScanMode = CM_RGB24ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3);
+ break;
+ case CM_GRAY16:
+ pSuggest->cmScanMode = CM_GRAY16ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2);
+ break;
+ case CM_GRAY8:
+ pSuggest->cmScanMode = CM_GRAY8ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth));
+ break;
+ case CM_TEXT:
+ pSuggest->cmScanMode = CM_TEXT;
+ pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8;
+ break;
+ default:
+ break;
+ }
+ }
+
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->dwBytesPerRow = %d\n",
+ pSuggest->dwBytesPerRow);
+ DBG (DBG_FUNC, "Reflective_ScanSuggest: leave Reflective_ScanSuggest\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ setup scanning process
+Parameters:
+ ColorMode: ScanMode of Scanning, CM_RGB48, CM_GRAY and so on
+ XDpi: X Resolution
+ YDpi: Y Resolution
+ isInvert: the RGB order
+ X: X start coordinate
+ Y: Y start coordinate
+ Width: Width of Scan Image
+ Height: Height of Scan Image
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_SetupScan (COLORMODE ColorMode,
+ unsigned short XDpi,
+ unsigned short YDpi,
+ SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width, unsigned short Height)
+{
+ isInvert = isInvert;
+ DBG (DBG_FUNC, "Reflective_SetupScan: Call in\n");
+ if (g_bOpened)
+ {
+ DBG (DBG_FUNC, "Reflective_SetupScan: scanner has been opened\n");
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Reflective_SetupScan: scanner not prepared\n");
+ return FALSE;
+ }
+
+ g_ScanMode = ColorMode;
+ g_XDpi = XDpi;
+ g_YDpi = YDpi;
+ g_SWWidth = Width;
+ g_SWHeight = Height;
+
+ switch (g_YDpi)
+ {
+ case 1200:
+ g_wPixelDistance = 4; /*even & odd sensor problem */
+ g_wLineDistance = 24;
+ g_Height += g_wPixelDistance;
+ break;
+ case 600:
+ g_wPixelDistance = 0; /*no even & odd problem */
+ g_wLineDistance = 12;
+ break;
+ case 300:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 6;
+ break;
+ case 150:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 3;
+ break;
+
+ case 75:
+ case 50:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 1;
+ break;
+ default:
+ g_wLineDistance = 0;
+ }
+
+ switch (g_ScanMode)
+ {
+ case CM_RGB48:
+ g_BytesPerRow = 6 * g_Width;
+ g_SWBytesPerRow = 6 * g_SWWidth;
+ g_bScanBits = 48;
+ g_Height += g_wLineDistance * 2; /*add height to do line distance */
+ break;
+ case CM_RGB24ext:
+ g_BytesPerRow = 3 * g_Width;
+ g_SWBytesPerRow = 3 * g_SWWidth;
+ g_bScanBits = 24;
+ g_Height += g_wLineDistance * 2; /*add height to do line distance */
+ break;
+ case CM_GRAY16ext:
+ g_BytesPerRow = 2 * g_Width;
+ g_SWBytesPerRow = 2 * g_SWWidth;
+ g_bScanBits = 16;
+ break;
+ case CM_GRAY8ext:
+ case CM_TEXT:
+ g_BytesPerRow = g_Width;
+ g_SWBytesPerRow = g_SWWidth;
+ g_bScanBits = 8;
+ break;
+ default:
+ break;
+ }
+
+ if (Asic_Open (&g_chip, g_pDeviceFile) != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC, "Reflective_SetupScan: Asic_Open return error\n");
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC, "Reflective_SetupScan: Asic_Open successfully\n");
+
+ g_bOpened = TRUE;
+
+ Asic_TurnLamp (&g_chip, FALSE);
+ Asic_TurnTA (&g_chip, FALSE);
+ Asic_TurnLamp (&g_chip, TRUE);
+
+ if (1200 == g_XDpi)
+ {
+ g_XDpi = 600;
+
+ if (Reflective_AdjustAD () == FALSE)
+ {
+
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_AdjustAD return error\n");
+ return FALSE;
+ }
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_AdjustAD successfully\n");
+
+ if (Reflective_FindTopLeft (&g_X, &g_Y) == FALSE)
+ {
+ g_X = 187;
+ g_Y = 43;
+ }
+
+ g_XDpi = 1200;
+
+ if (Reflective_AdjustAD () == FALSE)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_AdjustAD return error\n");
+ return FALSE;
+ }
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_AdjustAD successfully\n");
+ }
+ else
+ {
+ if (Reflective_AdjustAD () == FALSE)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_AdjustAD return error\n");
+ return FALSE;
+ }
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_AdjustAD successfully\n");
+ if (Reflective_FindTopLeft (&g_X, &g_Y) == FALSE)
+ {
+ g_X = 187;
+ g_Y = 43;
+ }
+ }
+
+
+ DBG (DBG_FUNC, "after find top left,g_X=%d,g_Y=%d\n", g_X, g_Y);
+
+ if (1200 == g_XDpi)
+ {
+ g_X =
+ g_X * 1200 / FIND_LEFT_TOP_CALIBRATE_RESOLUTION + X * 1200 / g_XDpi +
+ 47;
+
+ }
+ else
+ {
+ if (75 == g_XDpi)
+ {
+ g_X = g_X + X * 600 / g_XDpi;
+ }
+ else
+ {
+ g_X = g_X + X * 600 / g_XDpi + 23;
+ }
+ }
+
+ g_Y =
+ g_Y * 1200 / FIND_LEFT_TOP_CALIBRATE_RESOLUTION + Y * 1200 / g_YDpi + 47;
+
+
+ DBG (DBG_FUNC, "before line calibration,g_X=%d,g_Y=%d\n", g_X, g_Y);
+
+ if (Reflective_LineCalibration16Bits () == FALSE)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: Reflective_LineCalibration16Bits return error\n");
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: after Reflective_LineCalibration16Bits,g_X=%d,g_Y=%d\n",
+ g_X, g_Y);
+
+ DBG (DBG_FUNC, "Reflective_SetupScan: before Asic_SetWindow\n");
+
+ DBG (DBG_FUNC,
+ "Reflective_SetupScan: g_bScanBits=%d, g_XDpi=%d, g_YDpi=%d, g_X=%d, g_Y=%d, g_Width=%d, g_Height=%d\n",
+ g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width, g_Height);
+
+ if (g_Y > 300)
+ {
+ Asic_MotorMove (&g_chip, TRUE, g_Y - 300);
+ }
+ else
+ {
+ Asic_MotorMove (&g_chip, FALSE, 300 - g_Y);
+ }
+ g_Y = 300;
+
+ Asic_SetWindow (&g_chip, g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width,
+ g_Height);
+
+ DBG (DBG_FUNC, "Reflective_SetupScan: leave Reflective_SetupScan\n");
+ return Reflective_PrepareScan ();
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ To adjust the value of offset gain of R/G/B
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_AdjustAD ()
+{
+ SANE_Byte * lpCalData;
+ unsigned short wCalWidth;
+ int nTimesOfCal;
+ unsigned short wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB;
+#if 0
+ SANE_Byte bDarkMaxLevel;
+ SANE_Byte bDarkMinLevel;
+ SANE_Byte bLastMinR, bLastROffset, bROffsetUpperBound = 255, bROffsetLowerBound =
+ 0;
+ SANE_Byte bLastMinG, bLastGOffset, bGOffsetUpperBound = 255, bGOffsetLowerBound =
+ 0;
+ SANE_Byte bLastMinB, bLastBOffset, bBOffsetUpperBound = 255, bBOffsetLowerBound =
+ 0;
+ float fRFactor = 1.0;
+ float fGFactor = 1.0;
+ float fBFactor = 1.0;
+#endif
+ unsigned short wAdjustADResolution;
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: call in\n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC, "Reflective_AdjustAD: scanner has been opened\n");
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Reflective_AdjustAD: scanner not prepared\n");
+ return FALSE;
+ }
+
+
+ g_chip.AD.DirectionR = R_DIRECTION;
+ g_chip.AD.DirectionG = G_DIRECTION;
+ g_chip.AD.DirectionB = B_DIRECTION;
+ g_chip.AD.GainR = R_GAIN;
+ g_chip.AD.GainG = G_GAIN;
+ g_chip.AD.GainB = B_GAIN;
+ g_chip.AD.OffsetR = 152;
+ g_chip.AD.OffsetG = 56;
+ g_chip.AD.OffsetB = 8;
+
+ if (g_XDpi <= 600)
+ {
+ wAdjustADResolution = 600;
+ }
+ else
+ {
+ wAdjustADResolution = 1200;
+ }
+ wCalWidth = 10240;
+
+ lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * 3);
+ if (lpCalData == NULL)
+ {
+ DBG (DBG_FUNC, "Reflective_AdjustAD: lpCalData malloc error\n");
+ return FALSE;
+ }
+
+ Asic_SetMotorType (&g_chip, FALSE, TRUE);
+
+ Asic_SetCalibrate (&g_chip, 24, wAdjustADResolution, wAdjustADResolution, 0,
+ 0, wCalWidth, 1, FALSE);
+ MustScanner_PrepareCalculateMaxMin (wAdjustADResolution);
+ nTimesOfCal = 0;
+
+#ifdef DEBUG_SAVE_IMAGE
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ FILE *stream = NULL;
+ SANE_Byte * lpBuf = (SANE_Byte *) malloc (50);
+ if (NULL == lpBuf)
+ {
+ return FALSE;
+ }
+ memset (lpBuf, 0, 50);
+
+ stream = fopen ("/root/AD(Ref).pnm\n", "wb+\n");
+ sprintf (lpBuf, "P6\n%d %d\n255\n\n", wCalWidth, 1);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * 3, stream);
+ fclose (stream);
+ free (lpBuf);
+#endif
+
+ do
+ {
+ DBG (DBG_FUNC,
+ "Reflective_AdjustAD: run in first adjust offset do-while\n");
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR,
+ wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG,
+ &wMinValueG, wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB,
+ &wMinValueB, wAdjustADResolution);
+
+ if (g_chip.AD.DirectionR == 0)
+ {
+ if (wMinValueR > 15)
+ {
+ if (g_chip.AD.OffsetR < 8)
+ g_chip.AD.DirectionR = 1;
+ else
+ g_chip.AD.OffsetR -= 8;
+ }
+ else if (wMinValueR < 5)
+ g_chip.AD.OffsetR += 8;
+ }
+ else
+ {
+ if (wMinValueR > 15)
+ g_chip.AD.OffsetR += 8;
+ else if (wMinValueR < 5)
+ g_chip.AD.OffsetR -= 8;
+ }
+
+ if (g_chip.AD.DirectionG == 0)
+ {
+ if (wMinValueG > 15)
+ {
+ if (g_chip.AD.OffsetG < 8)
+ g_chip.AD.DirectionG = 1;
+ else
+ g_chip.AD.OffsetG -= 8;
+ }
+ else if (wMinValueG < 5)
+ g_chip.AD.OffsetG += 8;
+ }
+ else
+ {
+ if (wMinValueG > 15)
+ g_chip.AD.OffsetG += 8;
+ else if (wMinValueG < 5)
+ g_chip.AD.OffsetG -= 8;
+ }
+
+ if (g_chip.AD.DirectionB == 0)
+ {
+ if (wMinValueB > 15)
+ {
+ if (g_chip.AD.OffsetB < 8)
+ g_chip.AD.DirectionB = 1;
+ else
+ g_chip.AD.OffsetB -= 8;
+ }
+
+ else if (wMinValueB < 5)
+ g_chip.AD.OffsetB += 8;
+ }
+ else
+ {
+ if (wMinValueB > 15)
+ g_chip.AD.OffsetB += 8;
+ else if (wMinValueB < 5)
+ g_chip.AD.OffsetB -= 8;
+ }
+
+ nTimesOfCal++;
+ if (nTimesOfCal > 10)
+ break;
+ }
+ while (wMinValueR > 15 || wMinValueR < 5
+ || wMinValueG > 15 || wMinValueG < 5
+ || wMinValueB > 15 || wMinValueB < 5);
+
+ DBG (DBG_FUNC,
+ "Reflective_AdjustAD: run out first adjust offset do-while\n");
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: \
+ g_chip.AD.OffsetR=%d,\
+ g_chip.AD.OffsetG=%d,\
+ g_chip.AD.OffsetB=%d\n", g_chip.AD.OffsetR, g_chip.AD.OffsetG, g_chip.AD.OffsetB);
+
+ g_chip.AD.GainR = 1 - (double) (wMaxValueR - wMinValueR) / 210 > 0 ?
+ (SANE_Byte) (((1 -
+ (double) (wMaxValueR - wMinValueR) / 210)) * 63 * 6 / 5) : 0;
+ g_chip.AD.GainG =
+ 1 - (double) (wMaxValueG - wMinValueG) / 210 >
+ 0 ? (SANE_Byte) (((1 - (double) (wMaxValueG - wMinValueG) / 210)) * 63 * 6 /
+ 5) : 0;
+ g_chip.AD.GainB =
+ 1 - (double) (wMaxValueB - wMinValueB) / 210 >
+ 0 ? (SANE_Byte) (((1 - (double) (wMaxValueB - wMinValueB) / 210)) * 63 * 6 /
+ 5) : 0;
+
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: "
+ "g_chip.AD.GainR = %d,"
+ "g_chip.AD.GainG = %d,"
+ "g_chip.AD.GainB = %d\n",
+ g_chip.AD.GainR, g_chip.AD.GainG, g_chip.AD.GainB);
+
+ nTimesOfCal = 0;
+ do
+ {
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR,
+ wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG,
+ &wMinValueG, wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB,
+ &wMinValueB, wAdjustADResolution);
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: "
+ "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n",
+ g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR,
+ g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG,
+ g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB);
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: "
+ "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n",
+ wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB,
+ wMinValueB);
+
+ /*R Channel */
+ if ((wMaxValueR - wMinValueR) > REFL_MAX_LEVEL_RANGE)
+ {
+ if (g_chip.AD.GainR > 0)
+ g_chip.AD.GainR--;
+ }
+ else
+ {
+ if ((wMaxValueR - wMinValueR) < REFL_MIN_LEVEL_RANGE)
+ {
+ if (wMaxValueR < REFL_WHITE_MIN_LEVEL)
+ {
+ g_chip.AD.GainR++;
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ }
+ else
+ {
+ if (wMaxValueR > REFL_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainR < 1)
+ g_chip.AD.GainR = 0;
+ else
+ g_chip.AD.GainR--;
+ }
+ else
+ {
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ else
+ g_chip.AD.GainR++;
+ }
+ }
+ }
+ else
+ {
+ if (wMaxValueR > REFL_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainR < 1)
+ g_chip.AD.GainR = 0;
+ else
+ g_chip.AD.GainR--;
+ }
+
+ if (wMaxValueR < REFL_WHITE_MIN_LEVEL)
+ {
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ else
+ g_chip.AD.GainR++;
+ }
+ }
+ }
+
+ /*G Channel */
+ if ((wMaxValueG - wMinValueG) > REFL_MAX_LEVEL_RANGE)
+ {
+ if (g_chip.AD.GainG > 0)
+ g_chip.AD.GainG--;
+ }
+ else
+ {
+ if ((wMaxValueG - wMinValueG) < REFL_MIN_LEVEL_RANGE)
+ {
+ if (wMaxValueG < REFL_WHITE_MIN_LEVEL)
+ {
+ g_chip.AD.GainG++;
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ }
+ else
+ {
+ if (wMaxValueG > REFL_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainG < 1)
+ g_chip.AD.GainG = 0;
+ else
+ g_chip.AD.GainG--;
+ }
+ else
+ {
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ else
+ g_chip.AD.GainG++;
+ }
+ }
+ }
+ else
+ {
+ if (wMaxValueG > REFL_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainG < 1)
+ g_chip.AD.GainG = 0;
+ else
+ g_chip.AD.GainG--;
+ }
+
+ if (wMaxValueG < REFL_WHITE_MIN_LEVEL)
+ {
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ else
+ g_chip.AD.GainG++;
+ }
+ }
+ }
+
+ /* B Channel */
+ if ((wMaxValueB - wMinValueB) > REFL_MAX_LEVEL_RANGE)
+ {
+ if (g_chip.AD.GainB > 0)
+ g_chip.AD.GainB--;
+ }
+ else
+ {
+ if ((wMaxValueB - wMinValueB) < REFL_MIN_LEVEL_RANGE)
+ {
+ if (wMaxValueB < REFL_WHITE_MIN_LEVEL)
+ {
+ g_chip.AD.GainB++;
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+ }
+ else
+ {
+ if (wMaxValueB > REFL_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainB < 1)
+ g_chip.AD.GainB = 0;
+ else
+ g_chip.AD.GainB--;
+ }
+ else
+ {
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+ else
+ g_chip.AD.GainB++;
+ }
+ }
+ }
+ else
+ {
+ if (wMaxValueB > REFL_WHITE_MAX_LEVEL)
+
+ {
+ if (g_chip.AD.GainB < 1)
+ g_chip.AD.GainB = 0;
+ else
+ g_chip.AD.GainB--;
+ }
+
+ if (wMaxValueB < REFL_WHITE_MIN_LEVEL)
+ {
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+ else
+ g_chip.AD.GainB++;
+ }
+ }
+ }
+ nTimesOfCal++;
+ if (nTimesOfCal > 10)
+ break;
+ }
+ while ((wMaxValueR - wMinValueR) > REFL_MAX_LEVEL_RANGE
+ || (wMaxValueR - wMinValueR) < REFL_MIN_LEVEL_RANGE
+ || (wMaxValueG - wMinValueG) > REFL_MAX_LEVEL_RANGE
+ || (wMaxValueG - wMinValueG) < REFL_MIN_LEVEL_RANGE
+ || (wMaxValueB - wMinValueB) > REFL_MAX_LEVEL_RANGE
+ || (wMaxValueB - wMinValueB) < REFL_MIN_LEVEL_RANGE);
+
+ /* Adjust Offset 2nd */
+ nTimesOfCal = 0;
+ do
+ {
+ DBG (DBG_FUNC,
+ "Reflective_AdjustAD: run in second adjust offset do-while\n");
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR,
+ wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG,
+ &wMinValueG, wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB,
+ &wMinValueB, wAdjustADResolution);
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: "
+ "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n",
+ g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR,
+ g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG,
+ g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB);
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: "
+ "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n",
+ wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB,
+ wMinValueB);
+
+ if (g_chip.AD.DirectionR == 0)
+ {
+ if (wMinValueR > 20)
+ {
+ if (g_chip.AD.OffsetR < 8)
+ g_chip.AD.DirectionR = 1;
+ else
+ g_chip.AD.OffsetR -= 8;
+ }
+
+ else if (wMinValueR < 10)
+ g_chip.AD.OffsetR += 8;
+ }
+ else
+ {
+ if (wMinValueR > 20)
+ g_chip.AD.OffsetR += 8;
+ else if (wMinValueR < 10)
+ g_chip.AD.OffsetR -= 8;
+ }
+
+ if (g_chip.AD.DirectionG == 0)
+ {
+ if (wMinValueG > 20)
+ {
+ if (g_chip.AD.OffsetG < 8)
+ g_chip.AD.DirectionG = 1;
+ else
+ g_chip.AD.OffsetG -= 8;
+ }
+ else if (wMinValueG < 10)
+ g_chip.AD.OffsetG += 8;
+ }
+ else
+ {
+ if (wMinValueG > 20)
+ g_chip.AD.OffsetG += 8;
+ else if (wMinValueG < 10)
+ g_chip.AD.OffsetG -= 8;
+ }
+
+ if (g_chip.AD.DirectionB == 0)
+ {
+ if (wMinValueB > 20)
+ {
+ if (g_chip.AD.OffsetB < 8)
+ g_chip.AD.DirectionB = 1;
+ else
+ g_chip.AD.OffsetB -= 8;
+ }
+ else if (wMinValueB < 10)
+ g_chip.AD.OffsetB += 8;
+ }
+ else
+ {
+ if (wMinValueB > 20)
+ g_chip.AD.OffsetB += 8;
+ else if (wMinValueB < 10)
+ g_chip.AD.OffsetB -= 8;
+ }
+
+ nTimesOfCal++;
+ if (nTimesOfCal > 8)
+ break;
+
+ }
+ while (wMinValueR > 20 || wMinValueR < 10
+ || wMinValueG > 20 || wMinValueG < 10
+ || wMinValueB > 20 || wMinValueB < 10);
+
+ DBG (DBG_FUNC,
+ "Reflective_AdjustAD: run in second adjust offset do-while\n");
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD:after ad gain\n");
+ DBG (DBG_FUNC, "Reflective_AdjustAD: "
+ "g_chip.AD.GainR = %d,"
+ "g_chip.AD.GainG = %d,"
+ "g_chip.AD.GainB = %d\n",
+ g_chip.AD.GainR, g_chip.AD.GainG, g_chip.AD.GainB);
+
+ free (lpCalData);
+
+ DBG (DBG_FUNC, "Reflective_AdjustAD: leave Reflective_AdjustAD\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Find top and left side
+Parameters:
+ lpwStartX: the left side
+ lpwStartY: the top side
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY)
+{
+ unsigned short wCalWidth = FIND_LEFT_TOP_WIDTH_IN_DIP;
+ unsigned short wCalHeight = FIND_LEFT_TOP_HEIGHT_IN_DIP;
+
+ int i, j;
+ unsigned short wLeftSide;
+ unsigned short wTopSide;
+ int nScanBlock;
+ SANE_Byte * lpCalData;
+ unsigned int dwTotalSize;
+ unsigned short wXResolution, wYResolution;
+
+ DBG (DBG_FUNC, "Reflective_FindTopLeft: call in\n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC, "Reflective_FindTopLeft: scanner has been opened\n");
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Reflective_FindTopLeft: scanner not prepared\n");
+ return FALSE;
+ }
+
+ wXResolution = wYResolution = FIND_LEFT_TOP_CALIBRATE_RESOLUTION;
+
+ lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * wCalHeight);
+ if (lpCalData == NULL)
+ {
+ DBG (DBG_FUNC, "Reflective_FindTopLeft: lpCalData malloc error\n");
+ return FALSE;
+ }
+
+ dwTotalSize = wCalWidth * wCalHeight;
+ nScanBlock = (int) (dwTotalSize / g_dwCalibrationSize);
+
+ Asic_SetMotorType (&g_chip, TRUE, TRUE);
+ Asic_SetCalibrate (&g_chip, 8, wXResolution, wYResolution, 0, 0, wCalWidth,
+ wCalHeight, FALSE);
+ Asic_SetAFEGainOffset (&g_chip);
+ if (Asic_ScanStart (&g_chip) != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC, "Reflective_FindTopLeft: Asic_ScanStart return error\n");
+ free (lpCalData);
+ return FALSE;
+ }
+
+ for (i = 0; i < nScanBlock; i++)
+ {
+ if (STATUS_GOOD !=
+ Asic_ReadCalibrationData (&g_chip,
+ lpCalData + i * g_dwCalibrationSize,
+ g_dwCalibrationSize, 8))
+ {
+ DBG (DBG_FUNC,
+ "Reflective_FindTopLeft: Asic_ReadCalibrationData return error\n");
+ free (lpCalData);
+ return FALSE;
+ }
+ }
+
+ if (STATUS_GOOD !=
+ Asic_ReadCalibrationData (&g_chip,
+ lpCalData +
+ (nScanBlock) * g_dwCalibrationSize,
+ (dwTotalSize -
+ g_dwCalibrationSize * nScanBlock), 8))
+ {
+
+ DBG (DBG_FUNC,
+ "Reflective_FindTopLeft: Asic_ReadCalibrationData return error\n");
+ free (lpCalData);
+ return FALSE;
+ }
+
+ Asic_ScanStop (&g_chip);
+
+#ifdef DEBUG_SAVE_IMAGE
+ FILE *stream = NULL;
+ stream = fopen ("/root/bound(Ref).pnm", "wb+\n");
+ SANE_Byte * lpBuf = (SANE_Byte *) malloc (50);
+ if (NULL == lpBuf)
+ {
+ return FALSE;
+ }
+ memset (lpBuf, 0, 50);
+ sprintf (lpBuf, "P5\n%d %d\n255\n", wCalWidth, wCalHeight);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * wCalHeight, stream);
+
+ fclose (stream);
+ free (lpBuf);
+#endif
+
+ wLeftSide = 0;
+ wTopSide = 0;
+
+ /* Find Left Side */
+ for (i = wCalWidth - 1; i > 0; i--)
+ {
+ wLeftSide = *(lpCalData + i);
+ wLeftSide += *(lpCalData + wCalWidth * 2 + i);
+ wLeftSide += *(lpCalData + wCalWidth * 4 + i);
+ wLeftSide += *(lpCalData + wCalWidth * 6 + i);
+ wLeftSide += *(lpCalData + wCalWidth * 8 + i);
+ wLeftSide /= 5;
+ if (wLeftSide < 60)
+ {
+ if (i == wCalWidth - 1)
+ {
+ break;
+ }
+ *lpwStartX = i;
+ {
+ break;
+ }
+ }
+ }
+
+ /*Find Top Side i=left side */
+ for (j = 0; j < wCalHeight; j++)
+ {
+ wTopSide = *(lpCalData + wCalWidth * j + i - 2);
+ wTopSide += *(lpCalData + wCalWidth * j + i - 4);
+ wTopSide += *(lpCalData + wCalWidth * j + i - 6);
+ wTopSide += *(lpCalData + wCalWidth * j + i - 8);
+ wTopSide += *(lpCalData + wCalWidth * j + i - 10);
+
+ wTopSide /= 5;
+ if (wTopSide > 60)
+ {
+ if (j == 0)
+ {
+ break;
+ }
+ *lpwStartY = j;
+ {
+ break;
+ }
+ }
+ }
+
+ if ((*lpwStartX < 100) || (*lpwStartX > 250))
+ {
+ *lpwStartX = 187;
+ }
+
+ if ((*lpwStartY < 10) || (*lpwStartY > 100))
+ {
+ *lpwStartY = 43;
+ }
+
+ DBG (DBG_FUNC,
+ "Reflective_FindTopLeft: *lpwStartY = %d, *lpwStartX = %d\n",
+ *lpwStartY, *lpwStartX);
+ Asic_MotorMove (&g_chip, FALSE,
+ (wCalHeight - *lpwStartY +
+ BEFORE_SCANNING_MOTOR_FORWARD_PIXEL) * 1200 /
+ wYResolution);
+
+ free (lpCalData);
+
+ DBG (DBG_FUNC, "Reflective_FindTopLeft: leave Reflective_FindTopLeft\n");
+ return TRUE;
+
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Stop scan
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_StopScan ()
+{
+ DBG (DBG_FUNC, "Reflective_StopScan: call in\n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC, "Reflective_StopScan: scanner not opened\n");
+
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Reflective_StopScan: scanner not prepared\n");
+
+ return FALSE;
+ }
+
+ g_isCanceled = TRUE; /*tell parent process stop read image */
+
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+
+ DBG (DBG_FUNC, "Reflective_StopScan: thread exit\n");
+
+ Asic_ScanStop (&g_chip);
+ Asic_Close (&g_chip);
+
+ g_bOpened = FALSE;
+
+ DBG (DBG_FUNC, "Reflective_StopScan: leave Reflective_StopScan\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Get the calibration data
+Parameters:
+ none
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_LineCalibration16Bits ()
+{
+ STATUS status;
+ SANE_Byte * lpWhiteData;
+ SANE_Byte * lpDarkData;
+ unsigned int dwWhiteTotalSize;
+ unsigned int dwDarkTotalSize;
+ unsigned short wCalHeight = LINE_CALIBRATION__16BITS_HEIGHT;
+ unsigned short wCalWidth;
+
+ unsigned short *lpWhiteShading;
+ unsigned short *lpDarkShading;
+ double wRWhiteLevel = 0;
+ double wGWhiteLevel = 0;
+ double wBWhiteLevel = 0;
+ unsigned int dwRDarkLevel = 0;
+ unsigned int dwGDarkLevel = 0;
+ unsigned int dwBDarkLevel = 0;
+ unsigned int dwREvenDarkLevel = 0;
+ unsigned int dwGEvenDarkLevel = 0;
+ unsigned int dwBEvenDarkLevel = 0;
+ unsigned short * lpRWhiteSort;
+ unsigned short * lpGWhiteSort;
+ unsigned short * lpBWhiteSort;
+ unsigned short * lpRDarkSort;
+ unsigned short * lpGDarkSort;
+ unsigned short * lpBDarkSort;
+ int i, j;
+
+ DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: call in\n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: scanner not opened\n");
+
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: scanner not prepared\n");
+
+ return FALSE;
+ }
+
+ wCalWidth = g_Width;
+
+ dwWhiteTotalSize = wCalWidth * wCalHeight * 3 * 2;
+ dwDarkTotalSize = wCalWidth * wCalHeight * 3 * 2;
+ lpWhiteData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwWhiteTotalSize);
+ lpDarkData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwDarkTotalSize);
+
+ if (lpWhiteData == NULL || lpDarkData == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: lpWhiteData or lpDarkData malloc error \n");
+
+ return FALSE;
+ }
+
+ Asic_SetMotorType (&g_chip, TRUE, TRUE);
+ Asic_SetAFEGainOffset (&g_chip);
+ status =
+ Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth,
+ wCalHeight, TRUE);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_SetCalibrate return error \n");
+
+ free (lpWhiteData);
+
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ status = Asic_ScanStart (&g_chip);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_ScanStart return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ status =
+ Asic_ReadCalibrationData (&g_chip, lpWhiteData, dwWhiteTotalSize, 8);
+ if (status != STATUS_GOOD)
+ {
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ Asic_ScanStop (&g_chip);
+
+ /*Read dark level data */
+ status = Asic_SetMotorType (&g_chip, FALSE, TRUE);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_SetMotorType return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ status =
+ Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth,
+ wCalHeight, TRUE);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_SetCalibrate return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ status = Asic_TurnLamp (&g_chip, FALSE);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_TurnLamp return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ usleep (500000);
+
+ status = Asic_ScanStart (&g_chip);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_ScanStart return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ status = Asic_ReadCalibrationData (&g_chip, lpDarkData, dwDarkTotalSize, 8);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_ReadCalibrationData return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ Asic_ScanStop (&g_chip);
+
+ /* Turn on lamp */
+ status = Asic_TurnLamp (&g_chip, TRUE);
+ if (status != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: Asic_TurnLamp return error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+#ifdef DEBUG_SAVE_IMAGE
+ FILE *stream = NULL;
+ SANE_Byte * lpBuf = (SANE_Byte *) malloc (50);
+ if (NULL == lpBuf)
+ {
+ return FALSE;
+
+ }
+ memset (lpBuf, 0, 50);
+ stream = fopen ("/root/whiteshading(Ref).pnm", "wb+\n");
+ sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpWhiteData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream);
+ fclose (stream);
+
+ memset (lpBuf, 0, 50);
+ stream = fopen ("/root/darkshading(Ref).pnm", "wb+\n");
+ sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpDarkData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream);
+ fclose (stream);
+ free (lpBuf);
+#endif
+
+ sleep (1);
+
+ lpWhiteShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3);
+ lpDarkShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3);
+
+ lpRWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpGWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpBWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpRDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpGDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpBDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+
+ if (lpWhiteShading == NULL || lpDarkShading == NULL
+ || lpRWhiteSort == NULL || lpGWhiteSort == NULL || lpBWhiteSort == NULL
+ || lpRDarkSort == NULL || lpGDarkSort == NULL || lpBDarkSort == NULL)
+ {
+ DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: malloc error \n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ /* create dark level shading */
+ dwRDarkLevel = 0;
+ dwGDarkLevel = 0;
+ dwBDarkLevel = 0;
+ dwREvenDarkLevel = 0;
+ dwGEvenDarkLevel = 0;
+ dwBEvenDarkLevel = 0;
+
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: wCalWidth = %d, wCalHeight = %d\n",
+ wCalWidth, wCalHeight);
+
+ for (i = 0; i < wCalWidth; i++)
+ {
+ for (j = 0; j < wCalHeight; j++)
+ {
+ lpRDarkSort[j] =
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 0));
+ lpRDarkSort[j] +=
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 1) << 8);
+
+ lpGDarkSort[j] =
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 2));
+ lpGDarkSort[j] +=
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 3) << 8);
+
+ lpBDarkSort[j] =
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 4));
+ lpBDarkSort[j] +=
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 5) << 8);
+ }
+
+ if (g_XDpi == 1200)
+ {
+
+ /*do dark shading table with mean */
+ if (i % 2)
+ {
+ dwRDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20,
+ 30);
+ dwGDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20,
+ 30);
+ dwBDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20,
+ 30);
+ }
+ else
+ {
+ dwREvenDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20,
+ 30);
+
+ dwGEvenDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20,
+ 30);
+ dwBEvenDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20,
+ 30);
+ }
+ }
+ else
+ {
+
+ dwRDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30);
+ dwGDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30);
+ dwBDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30);
+ }
+ }
+
+ if (g_XDpi == 1200)
+ {
+ dwRDarkLevel = (unsigned int) (dwRDarkLevel / (wCalWidth / 2));
+ dwGDarkLevel = (unsigned int) (dwGDarkLevel / (wCalWidth / 2));
+ dwBDarkLevel = (unsigned int) (dwBDarkLevel / (wCalWidth / 2));
+ dwREvenDarkLevel = (unsigned int) (dwREvenDarkLevel / (wCalWidth / 2));
+ dwGEvenDarkLevel = (unsigned int) (dwGEvenDarkLevel / (wCalWidth / 2));
+ dwBEvenDarkLevel = (unsigned int) (dwBEvenDarkLevel / (wCalWidth / 2));
+ }
+ else
+ {
+ dwRDarkLevel = (unsigned int) (dwRDarkLevel / wCalWidth);
+ dwGDarkLevel = (unsigned int) (dwGDarkLevel / wCalWidth);
+ dwBDarkLevel = (unsigned int) (dwBDarkLevel / wCalWidth);
+ }
+
+ /*Create white shading */
+ for (i = 0; i < wCalWidth; i++)
+ {
+ wRWhiteLevel = 0;
+ wGWhiteLevel = 0;
+ wBWhiteLevel = 0;
+
+ for (j = 0; j < wCalHeight; j++)
+ {
+ lpRWhiteSort[j] =
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 0));
+ lpRWhiteSort[j] +=
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 1) << 8);
+
+ lpGWhiteSort[j] =
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 2));
+ lpGWhiteSort[j] +=
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 3) << 8);
+
+ lpBWhiteSort[j] =
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 4));
+ lpBWhiteSort[j] +=
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 5) << 8);
+ }
+
+ if (g_XDpi == 1200)
+ {
+ if (i % 2)
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGDarkLevel;
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel;
+ }
+ else
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwREvenDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGEvenDarkLevel;
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBEvenDarkLevel;
+ }
+ }
+ else
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGDarkLevel;
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel;
+ }
+
+
+ /*Create white shading */
+ wRWhiteLevel =
+ (double) (MustScanner_FiltLower (lpRWhiteSort, wCalHeight, 20, 30) -
+ *(lpDarkShading + i * 3 + 0));
+ wGWhiteLevel =
+ (double) (MustScanner_FiltLower (lpGWhiteSort, wCalHeight, 20, 30) -
+ *(lpDarkShading + i * 3 + 1));
+ wBWhiteLevel =
+ (double) (MustScanner_FiltLower (lpBWhiteSort, wCalHeight, 20, 30) -
+ *(lpDarkShading + i * 3 + 2));
+
+ if (wRWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 0) =
+ (unsigned short) (((float) 65535 / wRWhiteLevel * 0x2000));
+ else
+ *(lpWhiteShading + i * 3 + 0) = 0x2000;
+
+ if (wGWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 1) =
+ (unsigned short) (((float) 65535 / wGWhiteLevel * 0x2000));
+ else
+ *(lpWhiteShading + i * 3 + 1) = 0x2000;
+
+ if (wBWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 2) =
+ (unsigned short) (((float) 65535 / wBWhiteLevel * 0x2000));
+ else
+ *(lpWhiteShading + i * 3 + 2) = 0x2000;
+ }
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ free (lpRWhiteSort);
+ free (lpGWhiteSort);
+ free (lpBWhiteSort);
+ free (lpRDarkSort);
+ free (lpGDarkSort);
+ free (lpBDarkSort);
+
+ Asic_SetShadingTable (&g_chip, lpWhiteShading, lpDarkShading, g_XDpi,
+ wCalWidth, 0);
+
+ free (lpWhiteShading);
+ free (lpDarkShading);
+
+ DBG (DBG_FUNC,
+ "Reflective_LineCalibration16Bits: leave Reflective_LineCalibration16Bits\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Prepare scan image
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_PrepareScan ()
+{
+ g_wScanLinesPerBlock = g_dwBufferSize / g_BytesPerRow;
+ g_wMaxScanLines = g_dwImageBufferSize / g_BytesPerRow;
+ g_wMaxScanLines =
+ (g_wMaxScanLines / g_wScanLinesPerBlock) * g_wScanLinesPerBlock;
+
+ g_isCanceled = FALSE;
+
+ g_dwScannedTotalLines = 0;
+ g_wReadedLines = 0;
+ g_wtheReadyLines = 0;
+ g_wReadImageLines = 0;
+
+ g_wReadyShadingLine = 0;
+ g_wStartShadingLinePos = 0;
+
+ switch (g_ScanMode)
+ {
+ case CM_RGB48:
+ g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance;
+ DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n",
+ g_wtheReadyLines);
+
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n",
+ g_dwImageBufferSize);
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan: g_lpReadImageHead malloc error \n");
+ return FALSE;
+ }
+ break;
+
+ case CM_RGB24ext:
+ g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance;
+ DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n",
+ g_wtheReadyLines);
+
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n",
+ g_dwImageBufferSize);
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan: g_lpReadImageHead malloc error \n");
+ return FALSE;
+ }
+ break;
+ case CM_GRAY16ext:
+ g_wtheReadyLines = g_wPixelDistance;
+ DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n",
+ g_wtheReadyLines);
+
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n",
+ g_dwImageBufferSize);
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan: g_lpReadImageHead malloc error \n");
+ return FALSE;
+ }
+ break;
+ case CM_GRAY8ext:
+ g_wtheReadyLines = g_wPixelDistance;
+ DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n",
+ g_wtheReadyLines);
+
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n",
+ g_dwImageBufferSize);
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan: g_lpReadImageHead malloc error \n");
+ return FALSE;
+ }
+ break;
+ case CM_TEXT:
+ g_wtheReadyLines = g_wPixelDistance;
+ DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n",
+ g_wtheReadyLines);
+
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n",
+ g_dwImageBufferSize);
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Reflective_PrepareScan: g_lpReadImageHead malloc error \n");
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ Asic_ScanStart (&g_chip);
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Get the data of image
+Parameters:
+ lpBlock: the data of image
+ Rows: the rows of image
+
+ isOrderInvert: the RGB order
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Reflective_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert)
+{
+ DBG (DBG_FUNC, "Reflective_GetRows: call in \n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC, "Reflective_GetRows: scanner not opened \n");
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Reflective_GetRows: scanner not prepared \n");
+ return FALSE;
+ }
+
+ switch (g_ScanMode)
+ {
+ case CM_RGB48:
+ if (g_XDpi == 1200)
+ return MustScanner_GetRgb48BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetRgb48BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_RGB24ext:
+ if (g_XDpi == 1200)
+ return MustScanner_GetRgb24BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetRgb24BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_GRAY16ext:
+ if (g_XDpi == 1200)
+ return MustScanner_GetMono16BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetMono16BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_GRAY8ext:
+ if (g_XDpi == 1200)
+ return MustScanner_GetMono8BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetMono8BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_TEXT:
+ if (g_XDpi == 1200)
+ return MustScanner_GetMono1BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetMono1BitLine (lpBlock, isOrderInvert, Rows);
+ default:
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC, "Reflective_GetRows: leave Reflective_GetRows \n");
+ return FALSE;
+} /* end of the file ScannerReflective.c */
diff --git a/backend/mustek_usb2_transparent.c b/backend/mustek_usb2_transparent.c
new file mode 100644
index 0000000..43bda0b
--- /dev/null
+++ b/backend/mustek_usb2_transparent.c
@@ -0,0 +1,1745 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005 Mustek.
+ Originally maintained by Mustek
+ Author:Jack Roy 2005.5.24
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+
+/* forward declarations */
+static SANE_Bool Transparent_Reset (void);
+static SANE_Bool Transparent_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest);
+static SANE_Bool Transparent_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi,
+ SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width,
+ unsigned short Height);
+static SANE_Bool Transparent_StopScan (void);
+static SANE_Bool Transparent_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert);
+static SANE_Bool Transparent_AdjustAD (void);
+static SANE_Bool Transparent_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY);
+static SANE_Bool Transparent_LineCalibration16Bits (unsigned short wTAShadingMinus);
+static SANE_Bool Transparent_PrepareScan (void);
+
+
+/*function description*/
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ reset the scanner
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_Reset ()
+{
+ DBG (DBG_FUNC, "Transparent_Reset: call in\n");
+
+ if (g_bOpened)
+ {
+ DBG (DBG_FUNC, "Transparent_Reset: scanner has been opened\n");
+ return FALSE;
+ }
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_FUNC, "Transparent_Reset: can not open scanner\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_Reset (&g_chip))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_Reset return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_SetSource (&g_chip, LS_POSITIVE))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_SetSource return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_TurnLamp (&g_chip, FALSE))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_TurnLamp return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_TurnTA return error\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != Asic_Close (&g_chip))
+ {
+ DBG (DBG_FUNC, "Reflective_Reset: Asic_Close return error\n");
+ return FALSE;
+ }
+
+ g_Y = 0;
+ g_wLineartThreshold = 128;
+ g_dwTotalTotalXferLines = 0;
+ g_bFirstReadImage = TRUE;
+ g_pGammaTable = NULL;
+
+ DBG (DBG_FUNC, "Transparent_Reset: leave Transparent_Reset\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ get suggest parameter of scaning
+Parameters:
+ pTarget: the information of scaning
+ pSuggest: the suggest parameter of scaning
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest)
+{
+ unsigned short wMaxWidth, wMaxHeight;
+ int i;
+
+ DBG (DBG_FUNC, "Transparent_ScanSuggest: call in\n");
+
+ for (i = 0; s_wOpticalYDpi[i] != 0; i++)
+ {
+ if (s_wOpticalYDpi[i] <= pTarget->wDpi)
+ {
+ pSuggest->wYDpi = s_wOpticalYDpi[i];
+ break;
+ }
+ }
+ if (s_wOpticalYDpi[i] == 0)
+ {
+ i--;
+ pSuggest->wYDpi = s_wOpticalYDpi[i];
+ }
+
+ for (i = 0; s_wOpticalXDpi[i] != 0; i++)
+ {
+ if (s_wOpticalXDpi[i] <= pTarget->wDpi)
+ {
+ pSuggest->wXDpi = s_wOpticalXDpi[i];
+ break;
+ }
+ }
+ if (s_wOpticalXDpi[i] == 0)
+ {
+ i--;
+ pSuggest->wXDpi = s_wOpticalXDpi[i];
+ }
+
+ pSuggest->wX =
+ (unsigned short) (((unsigned int) (pTarget->wX) * (unsigned int) (pSuggest->wXDpi)) /
+ (unsigned int) (pTarget->wDpi));
+ pSuggest->wY =
+ (unsigned short) (((unsigned int) (pTarget->wY) * (unsigned int) (pSuggest->wYDpi)) /
+ (unsigned int) (pTarget->wDpi));
+ pSuggest->wWidth =
+ (unsigned short) (((unsigned int) (pTarget->wWidth) * (unsigned int) (pSuggest->wXDpi)) /
+ (unsigned int) (pTarget->wDpi));
+ pSuggest->wHeight =
+ (unsigned short) (((unsigned int) (pTarget->wHeight) * (unsigned int) (pSuggest->wYDpi)) /
+ (unsigned int) (pTarget->wDpi));
+
+ pSuggest->wWidth = (pSuggest->wWidth / 2) * 2;
+
+ if (pTarget->cmColorMode == CM_TEXT)
+ {
+ pSuggest->wWidth = ((pSuggest->wWidth + 7) >> 3) << 3;
+ if (pSuggest->wWidth < 8)
+ pSuggest->wWidth = 8;
+ }
+
+ g_Width = ((pSuggest->wWidth + 15) >> 4) << 4; /* Real Scan Width */
+ g_Height = pSuggest->wHeight;
+
+ wMaxWidth = (MAX_SCANNING_WIDTH * pSuggest->wXDpi) / 300;
+ wMaxHeight = (MAX_SCANNING_HEIGHT * pSuggest->wYDpi) / 300;
+
+ if (pTarget->cmColorMode == CM_TEXT)
+ wMaxWidth = (wMaxWidth >> 3) << 3;
+
+ if (pSuggest->wWidth > wMaxWidth)
+ pSuggest->wWidth = wMaxWidth;
+ if (pSuggest->wHeight > wMaxHeight)
+ pSuggest->wHeight = wMaxHeight;
+
+ if (pTarget->isOptimalSpeed)
+ {
+ DBG (DBG_FUNC, "Transparent_ScanSuggest: isOptimalSpeed is true\n");
+
+ switch (pTarget->cmColorMode)
+ {
+ case CM_RGB48:
+ pSuggest->cmScanMode = CM_RGB48;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6);
+ break;
+ case CM_RGB24:
+ pSuggest->cmScanMode = CM_RGB24ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3);
+ break;
+
+ case CM_GRAY16:
+ pSuggest->cmScanMode = CM_GRAY16ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2);
+ break;
+ case CM_GRAY8:
+ pSuggest->cmScanMode = CM_GRAY8ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth));
+ break;
+ case CM_TEXT:
+ pSuggest->cmScanMode = CM_TEXT;
+ pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ DBG (DBG_FUNC, "Transparent_ScanSuggest: isOptimalSpeed not true\n");
+
+ switch (pTarget->cmColorMode)
+ {
+ case CM_RGB48:
+ pSuggest->cmScanMode = CM_RGB48;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6);
+ break;
+ case CM_RGB24:
+ pSuggest->cmScanMode = CM_RGB24ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3);
+ break;
+ case CM_GRAY16:
+ pSuggest->cmScanMode = CM_GRAY16ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2);
+ break;
+ case CM_GRAY8:
+ pSuggest->cmScanMode = CM_GRAY8ext;
+ pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth));
+ break;
+ case CM_TEXT:
+ pSuggest->cmScanMode = CM_TEXT;
+ pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8;
+ break;
+ default:
+ break;
+ }
+ }
+
+ DBG (DBG_FUNC, "Transparent_ScanSuggest: leave Transparent_ScanSuggest\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ setup scanning process
+Parameters:
+ ColorMode: ScanMode of Scanning, CM_RGB48, CM_GRAY and so on
+ XDpi: X Resolution
+ YDpi: Y Resolution
+ isInvert: the RGB order
+ X: X start coordinate
+ Y: Y start coordinate
+ Width: Width of Scan Image
+ Height: Height of Scan Image
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi,
+ SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width, unsigned short Height)
+{
+ SANE_Bool hasTA;
+ unsigned short wTAShadingMinus = 0;
+
+ isInvert = isInvert;
+ DBG (DBG_FUNC, "Transparent_SetupScan: call in\n");
+
+ if (g_bOpened)
+ {
+ DBG (DBG_FUNC, "Transparent_SetupScan: scanner has been opened\n");
+ return FALSE;
+ }
+
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Transparent_SetupScan: scanner not prepared\n");
+ return FALSE;
+ }
+
+ g_ScanMode = ColorMode;
+ g_XDpi = XDpi;
+ g_YDpi = YDpi;
+ g_SWWidth = Width;
+ g_SWHeight = Height;
+
+ switch (g_YDpi)
+ {
+ case 1200:
+ g_wPixelDistance = 4;
+ g_wLineDistance = 24;
+ g_Height += g_wPixelDistance;
+ break;
+ case 600:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 12;
+ g_Height += g_wPixelDistance;
+ break;
+ case 300:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 6;
+ break;
+ case 150:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 3;
+ break;
+ case 75:
+ case 50:
+ g_wPixelDistance = 0;
+ g_wLineDistance = 1;
+ break;
+ default:
+ g_wLineDistance = 0;
+
+ }
+
+ DBG (DBG_FUNC, "Transparent_SetupScan: g_YDpi=%d\n", g_YDpi);
+ DBG (DBG_FUNC, "Transparent_SetupScan: g_wLineDistance=%d\n",
+ g_wLineDistance);
+ DBG (DBG_FUNC, "Transparent_SetupScan: g_wPixelDistance=%d\n",
+ g_wPixelDistance);
+
+ switch (g_ScanMode)
+ {
+ case CM_RGB48:
+ g_BytesPerRow = 6 * g_Width; /* ASIC limit : width must be 8x */
+ g_SWBytesPerRow = 6 * g_SWWidth; /* ASIC limit : width must be 8x */
+ g_bScanBits = 48;
+ g_Height += g_wLineDistance * 2;
+ break;
+ case CM_RGB24ext:
+ g_BytesPerRow = 3 * g_Width; /*ASIC limit : width must be 8x */
+ g_SWBytesPerRow = 3 * g_SWWidth;
+ g_bScanBits = 24;
+ g_Height += g_wLineDistance * 2;
+ break;
+ case CM_GRAY16ext:
+ g_BytesPerRow = 2 * g_Width; /* ASIC limit : width must be 8x */
+ g_SWBytesPerRow = 2 * g_SWWidth;
+ g_bScanBits = 16;
+ break;
+ case CM_GRAY8ext:
+ case CM_TEXT:
+ g_BytesPerRow = g_Width; /*ASIC limit : width must be 8x */
+ g_SWBytesPerRow = g_SWWidth;
+ g_bScanBits = 8;
+ break;
+ default:
+ break;
+ }
+
+ if (Asic_Open (&g_chip, g_pDeviceFile) != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC, "Transparent_SetupScan: Asic_Open return error\n");
+ return FALSE;
+ }
+
+ g_bOpened = TRUE;
+
+ if (STATUS_GOOD != Asic_TurnLamp (&g_chip, FALSE))
+ {
+ DBG (DBG_FUNC, "Transparent_SetupScan: Asic_TurnLamp return error\n");
+ return FALSE;
+ }
+
+ if (Asic_IsTAConnected (&g_chip, &hasTA) != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC,
+ "Transparent_SetupScan: Asic_IsTAConnected return error\n");
+ return FALSE;
+ }
+ if (!hasTA)
+ {
+ DBG (DBG_FUNC, "Transparent_SetupScan: no TA device\n");
+ return FALSE;
+ }
+
+ if (Asic_TurnTA (&g_chip, TRUE) != STATUS_GOOD)
+ {
+ DBG (DBG_FUNC, "Transparent_SetupScan: Asic_TurnTA return error\n");
+ return FALSE;
+ }
+
+ /* Begin Find Left&Top Side */
+ Asic_MotorMove (&g_chip, TRUE, TRAN_START_POS);
+
+ if (1200 == g_XDpi)
+ {
+ wTAShadingMinus = 1680;
+ g_XDpi = 600;
+ Transparent_AdjustAD ();
+ Transparent_FindTopLeft (&g_X, &g_Y);
+
+ g_XDpi = 1200;
+ Transparent_AdjustAD ();
+ }
+ else
+ {
+ wTAShadingMinus = 840;
+ Transparent_AdjustAD ();
+ Transparent_FindTopLeft (&g_X, &g_Y);
+ }
+
+ DBG (DBG_FUNC,
+ "Transparent_SetupScan: after find top and left g_X=%d, g_Y=%d\n", g_X,
+ g_Y);
+
+ if (1200 == g_XDpi)
+ {
+ g_X =
+ g_X * 1200 / FIND_LEFT_TOP_CALIBRATE_RESOLUTION + X * 1200 / g_XDpi;
+ }
+ else
+ {
+ if (75 == g_XDpi)
+ {
+ g_X = g_X + X * 600 / g_XDpi - 23;
+ }
+ else
+ {
+ g_X = g_X + X * 600 / g_XDpi;
+ }
+ }
+
+ DBG (DBG_FUNC,
+ "Transparent_SetupScan: before line calibration,g_X=%d,g_Y=%d\n", g_X,
+ g_Y);
+
+ Transparent_LineCalibration16Bits (wTAShadingMinus);
+
+ DBG (DBG_FUNC,
+ "Transparent_SetupScan: after Reflective_LineCalibration16Bits,g_X=%d,g_Y=%d\n",
+ g_X, g_Y);
+
+ DBG (DBG_FUNC,
+ "Transparent_SetupScan: g_bScanBits=%d, g_XDpi=%d, g_YDpi=%d, g_X=%d, g_Y=%d, g_Width=%d, g_Height=%d\n",
+ g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width, g_Height);
+
+ g_Y = Y * 1200 / g_YDpi + (300 - 40) + 189;
+ Asic_MotorMove (&g_chip, TRUE, g_Y - 360);
+ g_Y = 360;
+
+ Asic_SetWindow (&g_chip, g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width,
+ g_Height);
+
+ DBG (DBG_FUNC, "Transparent_SetupScan: leave Transparent_SetupScan\n");
+ return Transparent_PrepareScan ();
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Stop scan
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_StopScan ()
+{
+ DBG (DBG_FUNC, "Transparent_StopScan: call in\n");
+
+ if (!g_bOpened)
+ {
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ return FALSE;
+ }
+
+ g_isCanceled = TRUE;
+
+ pthread_cancel (g_threadid_readimage);
+ pthread_join (g_threadid_readimage, NULL);
+
+ DBG (DBG_FUNC, "Transparent_StopScan: thread exit\n");
+
+ Asic_ScanStop (&g_chip);
+ Asic_Close (&g_chip);
+ g_bOpened = FALSE;
+
+ DBG (DBG_FUNC, "Transparent_StopScan: leave Transparent_StopScan\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Get the data of image
+Parameters:
+ lpBlock: the data of image
+ Rows: the rows of image
+ isOrderInvert: the RGB order
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert)
+{
+ DBG (DBG_FUNC, "Transparent_GetRows: call in\n");
+
+ if (!g_bOpened)
+ {
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ return FALSE;
+ }
+
+ switch (g_ScanMode)
+ {
+ case CM_RGB48:
+ if (g_XDpi == 1200)
+ return MustScanner_GetRgb48BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetRgb48BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_RGB24ext:
+ if (g_XDpi == 1200)
+ return MustScanner_GetRgb24BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetRgb24BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_GRAY16ext:
+ if (g_XDpi == 1200)
+ return MustScanner_GetMono16BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetMono16BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_GRAY8ext:
+ if (g_XDpi == 1200)
+ return MustScanner_GetMono8BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetMono8BitLine (lpBlock, isOrderInvert, Rows);
+
+ case CM_TEXT:
+ if (g_XDpi == 1200)
+ return MustScanner_GetMono1BitLine1200DPI (lpBlock, isOrderInvert,
+ Rows);
+ else
+ return MustScanner_GetMono1BitLine (lpBlock, isOrderInvert, Rows);
+ default:
+ return FALSE;
+ }
+ return FALSE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ To adjust the value of offset gain of R/G/B
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_AdjustAD ()
+{
+ SANE_Byte * lpCalData;
+ unsigned short wCalWidth;
+ int nTimesOfCal;
+ unsigned short wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB;
+#if 0
+ float fRFactor = 1.0;
+ float fGFactor = 1.0;
+ float fBFactor = 1.0;
+ SANE_Byte bDarkMaxLevel;
+ SANE_Byte bDarkMinLevel;
+ SANE_Byte bLastMinR, bLastROffset, bROffsetUpperBound = 255, bROffsetLowerBound =
+ 0;
+ SANE_Byte bLastMinG, bLastGOffset, bGOffsetUpperBound = 255, bGOffsetLowerBound =
+ 0;
+ SANE_Byte bLastMinB, bLastBOffset, bBOffsetUpperBound = 255, bBOffsetLowerBound =
+ 0;
+#endif
+ unsigned short wAdjustADResolution;
+
+ DBG (DBG_FUNC, "Transparent_AdjustAD: call in\n");
+ if (!g_bOpened)
+ {
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ return FALSE;
+ }
+
+
+ g_chip.AD.DirectionR = R_DIRECTION;
+ g_chip.AD.DirectionG = G_DIRECTION;
+ g_chip.AD.DirectionB = B_DIRECTION;
+ g_chip.AD.GainR = R_GAIN;
+ g_chip.AD.GainG = G_GAIN;
+ g_chip.AD.GainB = B_GAIN;
+ g_chip.AD.OffsetR = 159;
+ g_chip.AD.OffsetG = 50;
+ g_chip.AD.OffsetB = 45;
+
+ if (g_XDpi <= 600)
+ {
+ wAdjustADResolution = 600;
+ }
+ else
+ {
+ wAdjustADResolution = 1200;
+ }
+
+ wCalWidth = 10240;
+
+ lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * 3);
+ if (lpCalData == NULL)
+ {
+ return FALSE;
+ }
+
+ Asic_SetMotorType (&g_chip, FALSE, TRUE);
+
+ Asic_SetCalibrate (&g_chip, 24, wAdjustADResolution, wAdjustADResolution, 0,
+ 0, wCalWidth, 1, FALSE);
+ MustScanner_PrepareCalculateMaxMin (wAdjustADResolution);
+ nTimesOfCal = 0;
+
+#ifdef DEBUG_SAVE_IMAGE
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ FILE *stream = NULL;
+ SANE_Byte * lpBuf = (SANE_Byte *) malloc (50);
+ if (NULL == lpBuf)
+ {
+ DBG (DBG_FUNC,
+ "Transparent_AdjustAD: Leave Transparent_AdjustAD for malloc fail!\n");
+ return FALSE;
+ }
+ memset (lpBuf, 0, 50);
+ stream = fopen ("/root/AD(Tra).pnm", "wb+\n");
+ sprintf (lpBuf, "P6\n%d %d\n255\n", wCalWidth, 3);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * 3, stream);
+ fclose (stream);
+ free (lpBuf);
+#endif
+
+ do
+ {
+ DBG (DBG_FUNC,
+ "Transparent_AdjustAD: run in first adjust offset do-while\n");
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR,
+ wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG,
+ &wMinValueG, wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB,
+ &wMinValueB, wAdjustADResolution);
+
+ if (g_chip.AD.DirectionR == 0)
+ {
+ if (wMinValueR > 15)
+ {
+ if (g_chip.AD.OffsetR < 8)
+ g_chip.AD.DirectionR = 1;
+ else
+ g_chip.AD.OffsetR -= 8;
+ }
+ else if (wMinValueR < 5)
+ g_chip.AD.OffsetR += 8;
+ }
+ else
+ {
+ if (wMinValueR > 15)
+ g_chip.AD.OffsetR += 8;
+ else if (wMinValueR < 5)
+ g_chip.AD.OffsetR -= 8;
+ }
+
+ if (g_chip.AD.DirectionG == 0)
+ {
+ if (wMinValueG > 15)
+ {
+ if (g_chip.AD.OffsetG < 8)
+ g_chip.AD.DirectionG = 1;
+
+ else
+ g_chip.AD.OffsetG -= 8;
+ }
+ else if (wMinValueG < 5)
+ g_chip.AD.OffsetG += 8;
+ }
+ else
+ {
+ if (wMinValueG > 15)
+ g_chip.AD.OffsetG += 8;
+ else if (wMinValueG < 5)
+ g_chip.AD.OffsetG -= 8;
+ }
+
+ if (g_chip.AD.DirectionB == 0)
+ {
+ if (wMinValueB > 15)
+ {
+ if (g_chip.AD.OffsetB < 8)
+ g_chip.AD.DirectionB = 1;
+ else
+ g_chip.AD.OffsetB -= 8;
+ }
+ else if (wMinValueB < 5)
+ g_chip.AD.OffsetB += 8;
+ }
+ else
+ {
+ if (wMinValueB > 15)
+ g_chip.AD.OffsetB += 8;
+ else if (wMinValueB < 5)
+ g_chip.AD.OffsetB -= 8;
+ }
+
+ nTimesOfCal++;
+ if (nTimesOfCal > 10)
+ break;
+ }
+ while (wMinValueR > 15 || wMinValueR < 5
+ || wMinValueG > 15 || wMinValueG < 5
+ || wMinValueB > 15 || wMinValueB < 5);
+
+ g_chip.AD.GainR = 1 - (double) (wMaxValueR - wMinValueR) / 210 > 0 ?
+ (SANE_Byte) (((1 -
+ (double) (wMaxValueR - wMinValueR) / 210)) * 63 * 6 / 5) : 0;
+ g_chip.AD.GainG =
+ 1 - (double) (wMaxValueG - wMinValueG) / 210 >
+ 0 ? (SANE_Byte) (((1 - (double) (wMaxValueG - wMinValueG) / 210)) * 63 * 6 /
+ 5) : 0;
+ g_chip.AD.GainB =
+ 1 - (double) (wMaxValueB - wMinValueB) / 210 >
+ 0 ? (SANE_Byte) (((1 - (double) (wMaxValueB - wMinValueB) / 210)) * 63 * 6 /
+ 5) : 0;
+
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+
+
+
+ nTimesOfCal = 0;
+ do
+ {
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR,
+ wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG,
+ &wMinValueG, wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB,
+ &wMinValueB, wAdjustADResolution);
+
+ DBG (DBG_FUNC, "Transparent_AdjustAD: "
+ "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n",
+ g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR,
+ g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG,
+ g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB);
+
+ DBG (DBG_FUNC, "Transparent_AdjustAD: "
+ "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n",
+ wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB,
+ wMinValueB);
+
+ /*R Channel */
+ if ((wMaxValueR - wMinValueR) > TRAN_MAX_LEVEL_RANGE)
+ {
+ if (g_chip.AD.GainR > 0)
+ g_chip.AD.GainR--;
+ }
+ else
+ {
+ if ((wMaxValueR - wMinValueR) < TRAN_MIN_LEVEL_RANGE)
+ {
+ if (wMaxValueR < TRAN_WHITE_MIN_LEVEL)
+ {
+ g_chip.AD.GainR++;
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ }
+ else
+ {
+ if (wMaxValueR > TRAN_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainR < 1)
+ g_chip.AD.GainR = 0;
+ else
+ g_chip.AD.GainR--;
+ }
+ else
+ {
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ else
+ g_chip.AD.GainR++;
+ }
+ }
+ }
+ else
+ {
+ if (wMaxValueR > TRAN_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainR < 1)
+ g_chip.AD.GainR = 0;
+ else
+ g_chip.AD.GainR--;
+ }
+
+ if (wMaxValueR < TRAN_WHITE_MIN_LEVEL)
+ {
+ if (g_chip.AD.GainR > 63)
+ g_chip.AD.GainR = 63;
+ else
+ g_chip.AD.GainR++;
+
+ }
+ }
+ }
+
+ /*G Channel */
+ if ((wMaxValueG - wMinValueG) > TRAN_MAX_LEVEL_RANGE)
+ {
+ if (g_chip.AD.GainG > 0)
+ g_chip.AD.GainG--;
+ }
+ else
+ {
+ if ((wMaxValueG - wMinValueG) < TRAN_MIN_LEVEL_RANGE)
+ {
+ if (wMaxValueG < TRAN_WHITE_MIN_LEVEL)
+ {
+ g_chip.AD.GainG++;
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ }
+ else
+ {
+ if (wMaxValueG > TRAN_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainG < 1)
+ g_chip.AD.GainG = 0;
+ else
+ g_chip.AD.GainG--;
+ }
+ else
+ {
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ else
+ g_chip.AD.GainG++;
+ }
+ }
+ }
+ else
+ {
+ if (wMaxValueG > TRAN_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainG < 1)
+ g_chip.AD.GainG = 0;
+ else
+ g_chip.AD.GainG--;
+ }
+
+ if (wMaxValueG < TRAN_WHITE_MIN_LEVEL)
+ {
+ if (g_chip.AD.GainG > 63)
+ g_chip.AD.GainG = 63;
+ else
+ g_chip.AD.GainG++;
+ }
+ }
+ }
+
+ /* B Channel */
+ if ((wMaxValueB - wMinValueB) > TRAN_MAX_LEVEL_RANGE)
+ {
+ if (g_chip.AD.GainB > 0)
+ g_chip.AD.GainB--;
+ }
+ else
+ {
+ if ((wMaxValueB - wMinValueB) < TRAN_MIN_LEVEL_RANGE)
+ {
+ if (wMaxValueB < TRAN_WHITE_MIN_LEVEL)
+ {
+ g_chip.AD.GainB++;
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+ }
+ else
+ {
+ if (wMaxValueB > TRAN_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainB < 1)
+ g_chip.AD.GainB = 0;
+ else
+ g_chip.AD.GainB--;
+ }
+ else
+ {
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+ else
+ g_chip.AD.GainB++;
+ }
+ }
+ }
+ else
+ {
+ if (wMaxValueB > TRAN_WHITE_MAX_LEVEL)
+ {
+ if (g_chip.AD.GainB < 1)
+ g_chip.AD.GainB = 0;
+ else
+ g_chip.AD.GainB--;
+ }
+
+ if (wMaxValueB < TRAN_WHITE_MIN_LEVEL)
+ {
+ if (g_chip.AD.GainB > 63)
+ g_chip.AD.GainB = 63;
+ else
+ g_chip.AD.GainB++;
+ }
+ }
+ }
+
+ nTimesOfCal++;
+ if (nTimesOfCal > 10)
+ break;
+ }
+ while ((wMaxValueR - wMinValueR) > TRAN_MAX_LEVEL_RANGE
+ || (wMaxValueR - wMinValueR) < TRAN_MIN_LEVEL_RANGE
+ || (wMaxValueG - wMinValueG) > TRAN_MAX_LEVEL_RANGE
+ || (wMaxValueG - wMinValueG) < TRAN_MIN_LEVEL_RANGE
+ || (wMaxValueB - wMinValueB) > TRAN_MAX_LEVEL_RANGE
+ || (wMaxValueB - wMinValueB) < TRAN_MIN_LEVEL_RANGE);
+
+ /* Adjust Offset 2nd */
+ nTimesOfCal = 0;
+ do
+ {
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24);
+ Asic_ScanStop (&g_chip);
+
+ MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR,
+ wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG,
+ &wMinValueG, wAdjustADResolution);
+ MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB,
+ &wMinValueB, wAdjustADResolution);
+ DBG (DBG_FUNC,
+ "Transparent_AdjustAD: "
+ "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n",
+ g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR,
+ g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG,
+ g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB);
+
+ DBG (DBG_FUNC, "Transparent_AdjustAD: "
+ "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n",
+ wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB,
+ wMinValueB);
+
+ if (g_chip.AD.DirectionR == 0)
+ {
+ if (wMinValueR > 20)
+ {
+ if (g_chip.AD.OffsetR < 8)
+ g_chip.AD.DirectionR = 1;
+ else
+ g_chip.AD.OffsetR -= 8;
+ }
+ else if (wMinValueR < 10)
+ g_chip.AD.OffsetR += 8;
+ }
+ else
+ {
+ if (wMinValueR > 20)
+ g_chip.AD.OffsetR += 8;
+ else if (wMinValueR < 10)
+ g_chip.AD.OffsetR -= 8;
+ }
+
+ if (g_chip.AD.DirectionG == 0)
+ {
+ if (wMinValueG > 20)
+ {
+ if (g_chip.AD.OffsetG < 8)
+ g_chip.AD.DirectionG = 1;
+ else
+ g_chip.AD.OffsetG -= 8;
+ }
+ else if (wMinValueG < 10)
+ g_chip.AD.OffsetG += 8;
+ }
+ else
+ {
+ if (wMinValueG > 20)
+ g_chip.AD.OffsetG += 8;
+ else if (wMinValueG < 10)
+ g_chip.AD.OffsetG -= 8;
+ }
+
+ if (g_chip.AD.DirectionB == 0)
+ {
+ if (wMinValueB > 20)
+ {
+ if (g_chip.AD.OffsetB < 8)
+ g_chip.AD.DirectionB = 1;
+ else
+ g_chip.AD.OffsetB -= 8;
+ }
+ else if (wMinValueB < 10)
+ g_chip.AD.OffsetB += 8;
+ }
+ else
+ {
+ if (wMinValueB > 20)
+ g_chip.AD.OffsetB += 8;
+ else if (wMinValueB < 10)
+ g_chip.AD.OffsetB -= 8;
+ }
+
+ nTimesOfCal++;
+ if (nTimesOfCal > 8)
+ break;
+
+ }
+ while (wMinValueR > 20 || wMinValueR < 10
+ || wMinValueG > 20 || wMinValueG < 10
+ || wMinValueB > 20 || wMinValueB < 10);
+
+ DBG (DBG_FUNC, "Transparent_AdjustAD: leave Transparent_AdjustAD\n");
+ free (lpCalData);
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Find top and left side
+Parameters:
+ lpwStartX: the left side
+ lpwStartY: the top side
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY)
+{
+ unsigned short wCalWidth = TA_FIND_LEFT_TOP_WIDTH_IN_DIP;
+ unsigned short wCalHeight = TA_FIND_LEFT_TOP_HEIGHT_IN_DIP;
+
+ int i, j;
+ unsigned short wLeftSide;
+ unsigned short wTopSide;
+ int nScanBlock;
+ SANE_Byte * lpCalData;
+ unsigned int dwTotalSize;
+ unsigned short wXResolution, wYResolution;
+
+ DBG (DBG_FUNC, "Transparent_FindTopLeft: call in\n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC, "Transparent_FindTopLeft: scanner not opened\n");
+
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC, "Transparent_FindTopLeft: scanner not prepared\n");
+ return FALSE;
+ }
+
+ wXResolution = wYResolution = FIND_LEFT_TOP_CALIBRATE_RESOLUTION;
+
+
+ lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * wCalHeight);
+ if (lpCalData == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_FindTopLeft: lpCalData malloc fail\n");
+ return FALSE;
+ }
+
+ dwTotalSize = wCalWidth * wCalHeight;
+ nScanBlock = (int) (dwTotalSize / g_dwCalibrationSize);
+
+ Asic_SetMotorType (&g_chip, TRUE, TRUE);
+ Asic_SetCalibrate (&g_chip, 8, wXResolution, wYResolution, 0, 0, wCalWidth,
+ wCalHeight, FALSE);
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_ScanStart (&g_chip);
+
+ for (i = 0; i < nScanBlock; i++)
+ Asic_ReadCalibrationData (&g_chip, lpCalData + i * g_dwCalibrationSize,
+ g_dwCalibrationSize, 8);
+
+ Asic_ReadCalibrationData (&g_chip,
+ lpCalData + (nScanBlock) * g_dwCalibrationSize,
+ (dwTotalSize - g_dwCalibrationSize * nScanBlock),
+ 8);
+ Asic_ScanStop (&g_chip);
+
+
+#ifdef DEBUG_SAVE_IMAGE
+ FILE *stream = NULL;
+ SANE_Byte * lpBuf = (SANE_Byte *) malloc (50);
+ if (NULL == lpBuf)
+ {
+ return FALSE;
+ }
+ memset (lpBuf, 0, 50);
+ stream = fopen ("/root/bound(Tra).pnm", "wb+\n");
+ sprintf (lpBuf, "P5\n%d %d\n255\n", wCalWidth, wCalHeight);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * wCalHeight, stream);
+ fclose (stream);
+ free (lpBuf);
+#endif
+
+ wLeftSide = 0;
+ wTopSide = 0;
+
+ /* Find Left Side */
+ for (i = (wCalWidth - 1); i > 0; i--)
+ {
+ wLeftSide = *(lpCalData + i);
+ wLeftSide += *(lpCalData + wCalWidth * 2 + i);
+ wLeftSide += *(lpCalData + wCalWidth * 4 + i);
+ wLeftSide += *(lpCalData + wCalWidth * 6 + i);
+ wLeftSide += *(lpCalData + wCalWidth * 8 + i);
+ wLeftSide /= 5;
+ if (wLeftSide < 60)
+ {
+ if (i == (wCalWidth - 1))
+ {
+ break;
+ }
+ *lpwStartX = i;
+ break;
+ }
+ }
+
+ /* Find Top Side i=left side */
+ for (j = 0; j < wCalHeight; j++)
+ {
+ wTopSide = *(lpCalData + wCalWidth * j + i + 2);
+ wTopSide += *(lpCalData + wCalWidth * j + i + 4);
+ wTopSide += *(lpCalData + wCalWidth * j + i + 6);
+ wTopSide += *(lpCalData + wCalWidth * j + i + 8);
+ wTopSide += *(lpCalData + wCalWidth * j + i + 10);
+ wTopSide /= 5;
+ if (wTopSide < 60)
+ {
+ if (j == 0)
+ {
+ break;
+ }
+ *lpwStartY = j;
+ break;
+ }
+ }
+
+ if ((*lpwStartX < 2200) || (*lpwStartX > 2300))
+ {
+ *lpwStartX = 2260;
+ }
+
+ if ((*lpwStartY < 100) || (*lpwStartY > 200))
+ {
+ *lpwStartY = 124;
+ }
+
+
+ Asic_MotorMove (&g_chip, FALSE,
+ (wCalHeight - *lpwStartY) * 1200 / wYResolution + 300);
+
+ free (lpCalData);
+
+ DBG (DBG_FUNC,
+ "Transparent_FindTopLeft: *lpwStartY = %d, *lpwStartX = %d\n",
+ *lpwStartY, *lpwStartX);
+ DBG (DBG_FUNC, "Transparent_FindTopLeft: leave Transparent_FindTopLeft\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/15
+Routine Description:
+ Get the calibration data
+Parameters:
+ none
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_LineCalibration16Bits (unsigned short wTAShadingMinus)
+{
+
+ unsigned short *lpWhiteShading;
+ unsigned short *lpDarkShading;
+ double wRWhiteLevel = 0;
+ double wGWhiteLevel = 0;
+ double wBWhiteLevel = 0;
+ unsigned int dwRDarkLevel = 0;
+ unsigned int dwGDarkLevel = 0;
+ unsigned int dwBDarkLevel = 0;
+ unsigned int dwREvenDarkLevel = 0;
+ unsigned int dwGEvenDarkLevel = 0;
+ unsigned int dwBEvenDarkLevel = 0;
+ unsigned short * lpRWhiteSort;
+ unsigned short * lpGWhiteSort;
+ unsigned short * lpBWhiteSort;
+ unsigned short * lpRDarkSort;
+ unsigned short * lpGDarkSort;
+ unsigned short * lpBDarkSort;
+ int i, j;
+
+ SANE_Byte * lpWhiteData;
+ SANE_Byte * lpDarkData;
+ unsigned int dwWhiteTotalSize;
+ unsigned int dwDarkTotalSize;
+ unsigned short wCalHeight = LINE_CALIBRATION__16BITS_HEIGHT;
+ unsigned short wCalWidth = g_Width;
+
+ DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: call in\n");
+ if (!g_bOpened)
+ {
+ DBG (DBG_FUNC,
+ "Transparent_LineCalibration16Bits: scanner not opened\n");
+ return FALSE;
+ }
+ if (!g_bPrepared)
+ {
+ DBG (DBG_FUNC,
+ "Transparent_LineCalibration16Bits: scanner not prepared\n");
+ return FALSE;
+ }
+
+ if (g_XDpi < 600)
+ {
+ wTAShadingMinus = wTAShadingMinus * g_XDpi / 600;
+ }
+
+ dwWhiteTotalSize = wCalWidth * wCalHeight * 3 * 2;
+ dwDarkTotalSize = wCalWidth * wCalHeight * 3 * 2;
+ lpWhiteData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwWhiteTotalSize);
+ lpDarkData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwDarkTotalSize);
+ if (lpWhiteData == NULL || lpDarkData == NULL)
+ {
+ DBG (DBG_FUNC,
+ "Transparent_LineCalibration16Bits: lpWhiteData or lpDarkData malloc fail\n");
+ return FALSE;
+ }
+
+ /*Read white level data */
+ Asic_SetMotorType (&g_chip, TRUE, TRUE);
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth,
+ wCalHeight, TRUE);
+ Asic_ScanStart (&g_chip);
+
+ /* Read Data */
+ Asic_ReadCalibrationData (&g_chip, lpWhiteData, dwWhiteTotalSize, 8);
+ Asic_ScanStop (&g_chip);
+
+
+ /* Read dark level data */
+ Asic_SetMotorType (&g_chip, FALSE, TRUE);
+ Asic_SetAFEGainOffset (&g_chip);
+ Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth,
+ wCalHeight, TRUE);
+
+ Asic_TurnLamp (&g_chip, FALSE);
+ Asic_TurnTA (&g_chip, FALSE);
+
+ usleep (500000);
+
+ Asic_ScanStart (&g_chip);
+ Asic_ReadCalibrationData (&g_chip, lpDarkData, dwDarkTotalSize, 8);
+
+ Asic_ScanStop (&g_chip);
+
+ Asic_TurnTA (&g_chip, TRUE);
+
+#ifdef DEBUG_SAVE_IMAGE
+ FILE *stream = NULL;
+ SANE_Byte * lpBuf = (SANE_Byte *) malloc (50);
+ if (NULL == lpBuf)
+ {
+ return FALSE;
+ }
+ memset (lpBuf, 0, 50);
+ stream = fopen ("/root/whiteshading(Tra).pnm", "wb+\n");
+ sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpWhiteData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream);
+ fclose (stream);
+
+ memset (lpBuf, 0, 50);
+ stream = fopen ("/root/darkshading(Tra).pnm", "wb+\n");
+ sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth * wCalHeight);
+ fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
+ fwrite (lpDarkData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream);
+ fclose (stream);
+ free (lpBuf);
+#endif
+
+ lpWhiteShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3);
+ lpDarkShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3);
+
+ lpRWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpGWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpBWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpRDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpGDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+ lpBDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight);
+
+ if (lpWhiteShading == NULL || lpDarkShading == NULL
+ || lpRWhiteSort == NULL || lpGWhiteSort == NULL || lpBWhiteSort == NULL
+ || lpRDarkSort == NULL || lpGDarkSort == NULL || lpBDarkSort == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: malloc fail\n");
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC,
+ "Transparent_LineCalibration16Bits: wCalWidth = %d, wCalHeight = %d\n",
+ wCalWidth, wCalHeight);
+
+ /* create dark level shading */
+ dwRDarkLevel = 0;
+ dwGDarkLevel = 0;
+ dwBDarkLevel = 0;
+ dwREvenDarkLevel = 0;
+ dwGEvenDarkLevel = 0;
+ dwBEvenDarkLevel = 0;
+
+ for (i = 0; i < wCalWidth; i++)
+
+ {
+ for (j = 0; j < wCalHeight; j++)
+ {
+ lpRDarkSort[j] =
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 0));
+ lpRDarkSort[j] +=
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 1) << 8);
+
+ lpGDarkSort[j] =
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 2));
+ lpGDarkSort[j] +=
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 3) << 8);
+
+ lpBDarkSort[j] =
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 4));
+ lpBDarkSort[j] +=
+ (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 5) << 8);
+ }
+
+ /* sum of dark level for all pixels */
+ if (g_XDpi == 1200)
+ {
+ /* do dark shading table with mean */
+ if (i % 2)
+ {
+ dwRDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20,
+ 30);
+ dwGDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20,
+ 30);
+ dwBDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20,
+ 30);
+ }
+ else
+ {
+ dwREvenDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20,
+ 30);
+ dwGEvenDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20,
+ 30);
+ dwBEvenDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20,
+ 30);
+ }
+ }
+ else
+ {
+ dwRDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30);
+ dwGDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30);
+ dwBDarkLevel +=
+ (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30);
+ }
+ }
+
+ if (g_XDpi == 1200)
+ {
+ dwRDarkLevel = (unsigned int) (dwRDarkLevel / (wCalWidth / 2)) - 512;
+ dwGDarkLevel = (unsigned int) (dwGDarkLevel / (wCalWidth / 2)) - 512;
+ dwBDarkLevel = (unsigned int) (dwBDarkLevel / (wCalWidth / 2)) - 512;
+
+ dwREvenDarkLevel = (unsigned int) (dwREvenDarkLevel / (wCalWidth / 2)) - 512;
+ dwGEvenDarkLevel = (unsigned int) (dwGEvenDarkLevel / (wCalWidth / 2)) - 512;
+ dwBEvenDarkLevel = (unsigned int) (dwBEvenDarkLevel / (wCalWidth / 2)) - 512;
+ }
+ else
+ {
+ dwRDarkLevel = (unsigned int) (dwRDarkLevel / wCalWidth) - 512;
+ dwGDarkLevel = (unsigned int) (dwGDarkLevel / wCalWidth) - 512;
+ dwBDarkLevel = (unsigned int) (dwBDarkLevel / wCalWidth) - 512;
+ }
+
+ /* Create white shading */
+ for (i = 0; i < wCalWidth; i++)
+ {
+ wRWhiteLevel = 0;
+ wGWhiteLevel = 0;
+ wBWhiteLevel = 0;
+
+ for (j = 0; j < wCalHeight; j++)
+ {
+ lpRWhiteSort[j] =
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 0));
+ lpRWhiteSort[j] +=
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 1) << 8);
+
+ lpGWhiteSort[j] =
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 2));
+ lpGWhiteSort[j] +=
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 3) << 8);
+
+ lpBWhiteSort[j] =
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 4));
+ lpBWhiteSort[j] +=
+ (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 5) << 8);
+ }
+
+ if (1200 == g_XDpi)
+ {
+ if (i % 2)
+ {
+ if (SS_Negative == g_ssScanSource)
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGDarkLevel;
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel;
+ }
+ else
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) (dwGDarkLevel * 0.78);
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel;
+ }
+ }
+ else
+ {
+ if (SS_Negative == g_ssScanSource)
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwREvenDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGEvenDarkLevel;
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBEvenDarkLevel;
+ }
+ else
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwREvenDarkLevel;
+ *(lpDarkShading + i * 3 + 1) =
+ (unsigned short) (dwGEvenDarkLevel * 0.78);
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBEvenDarkLevel;
+ }
+ }
+ }
+ else
+ {
+ if (SS_Negative == g_ssScanSource)
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwRDarkLevel;
+ }
+ else
+ {
+ *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel;
+ *(lpDarkShading + i * 3 + 1) = (unsigned short) (dwRDarkLevel * 0.78);
+ *(lpDarkShading + i * 3 + 2) = (unsigned short) dwRDarkLevel;
+ }
+ }
+
+ /* Create white shading */
+ wRWhiteLevel =
+ (double) (MustScanner_FiltLower (lpRWhiteSort, wCalHeight, 20, 30) -
+ *(lpDarkShading + i * 3 + 0));
+ wGWhiteLevel =
+ (double) (MustScanner_FiltLower (lpGWhiteSort, wCalHeight, 20, 30) -
+ *(lpDarkShading + i * 3 + 1));
+ wBWhiteLevel =
+ (double) (MustScanner_FiltLower (lpBWhiteSort, wCalHeight, 20, 30) -
+ *(lpDarkShading + i * 3 + 2));
+
+ if (g_ssScanSource == SS_Negative)
+ {
+ if (wRWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 0) =
+ (unsigned short) ((float) 65536 / wRWhiteLevel * 0x1000);
+ else
+ *(lpWhiteShading + i * 3 + 0) = 0x1000;
+
+ if (wGWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 1) =
+ (unsigned short) ((float) (65536 * 1.5) / wGWhiteLevel * 0x1000);
+ else
+ *(lpWhiteShading + i * 3 + 1) = 0x1000;
+
+ if (wBWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 2) =
+ (unsigned short) ((float) (65536 * 2.0) / wBWhiteLevel * 0x1000);
+ else
+ *(lpWhiteShading + i * 3 + 2) = 0x1000;
+ }
+ else
+ {
+ if (wRWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 0) =
+ (unsigned short) ((float) 65536 / wRWhiteLevel * 0x1000);
+ else
+ *(lpWhiteShading + i * 3 + 0) = 0x1000;
+
+ if (wGWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 1) =
+ (unsigned short) ((float) (65536 * 1.04) / wGWhiteLevel * 0x1000);
+ else
+ *(lpWhiteShading + i * 3 + 1) = 0x1000;
+
+ if (wBWhiteLevel > 0)
+ *(lpWhiteShading + i * 3 + 2) =
+ (unsigned short) ((float) 65536 / wBWhiteLevel * 0x1000);
+ else
+ *(lpWhiteShading + i * 3 + 2) = 0x1000;
+ }
+ }
+
+ free (lpWhiteData);
+ free (lpDarkData);
+ free (lpRWhiteSort);
+ free (lpGWhiteSort);
+ free (lpBWhiteSort);
+ free (lpRDarkSort);
+ free (lpGDarkSort);
+ free (lpBDarkSort);
+
+ Asic_SetShadingTable (&g_chip, lpWhiteShading, lpDarkShading, g_XDpi,
+ wCalWidth, 0);
+
+ free (lpWhiteShading);
+ free (lpDarkShading);
+
+ DBG (DBG_FUNC,
+ "Transparent_LineCalibration16Bits: leave Transparent_LineCalibration16Bits\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Prepare scan image
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+Transparent_PrepareScan ()
+{
+ DBG (DBG_FUNC, "Transparent_PrepareScan: call in\n");
+
+ g_wScanLinesPerBlock = g_dwBufferSize / g_BytesPerRow;
+ g_wMaxScanLines = g_dwImageBufferSize / g_BytesPerRow;
+ g_wMaxScanLines =
+ (g_wMaxScanLines / g_wScanLinesPerBlock) * g_wScanLinesPerBlock;
+ g_isCanceled = FALSE;
+
+ g_dwScannedTotalLines = 0;
+ g_wReadedLines = 0;
+ g_wtheReadyLines = 0;
+ g_wReadImageLines = 0;
+
+ g_wReadyShadingLine = 0;
+ g_wStartShadingLinePos = 0;
+
+ switch (g_ScanMode)
+ {
+
+ case CM_RGB48:
+
+ g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance;
+
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n");
+ return FALSE;
+ }
+ break;
+
+ case CM_RGB24ext:
+ g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance;
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n");
+ return FALSE;
+ }
+ break;
+
+ case CM_GRAY16ext:
+ g_wtheReadyLines = g_wPixelDistance;
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n");
+ return FALSE;
+ }
+ break;
+
+ case CM_GRAY8ext:
+ g_wtheReadyLines = g_wPixelDistance;
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n");
+ return FALSE;
+ }
+ break;
+
+ case CM_TEXT:
+ g_wtheReadyLines = g_wPixelDistance;
+ g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize);
+ if (g_lpReadImageHead == NULL)
+ {
+ DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n");
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+
+ Asic_ScanStart (&g_chip);
+
+ DBG (DBG_FUNC, "Transparent_PrepareScan: leave Transparent_PrepareScan\n");
+ return TRUE;
+} /* end of the file ScannerTransparent.c */
diff --git a/backend/mustek_usb_high.c b/backend/mustek_usb_high.c
new file mode 100644
index 0000000..f6a0125
--- /dev/null
+++ b/backend/mustek_usb_high.c
@@ -0,0 +1,2751 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001, 2002 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#include "mustek_usb_high.h"
+#include "mustek_usb_mid.c"
+
+/* ------------------------ calibration functions ------------------------- */
+
+static SANE_Byte gray_map[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static inline double
+filter_lower_end (SANE_Int * buffer, SANE_Word total_count,
+ SANE_Word filter_count)
+{
+ SANE_Word bound = total_count - 1;
+ SANE_Word left_count = total_count - filter_count;
+ SANE_Int temp = 0;
+ SANE_Word i, j;
+ SANE_Int sum = 0;
+
+ for (i = 0; i < bound; i++)
+ {
+ for (j = 0; j < bound - i; j++)
+ {
+ if (buffer[j + 1] > buffer[j])
+ {
+ temp = buffer[j];
+ buffer[j] = buffer[j + 1];
+ buffer[j + 1] = temp;
+ }
+ }
+ }
+ for (i = 0; i < left_count; i++)
+ sum += buffer[i];
+ return (double) sum;
+}
+
+SANE_Status
+usb_high_cal_init (Calibrator * cal, SANE_Byte type, SANE_Word target_white,
+ SANE_Word target_dark)
+{
+ DBG (5, "usb_high_cal_init: start, cal=%p, type=%d, target_white=%d "
+ "target_dark=%d\n", (void *) cal, type, target_white, target_dark);
+ cal->is_prepared = SANE_FALSE;
+ cal->k_white = NULL;
+ cal->k_dark = NULL;
+ /* Working Buffer */
+ cal->white_line = NULL;
+ cal->dark_line = NULL;
+ cal->white_buffer = NULL;
+ /* Necessary Parameters */
+ cal->k_white_level = 240 << 8;
+ cal->k_dark_level = 0;
+ cal->threshold = 2048;
+ cal->major_average = 0;
+ cal->minor_average = 0;
+ cal->filter = 0;
+ cal->white_needed = 0;
+ cal->dark_needed = 0;
+ cal->max_width = 0;
+ cal->width = 100;
+ cal->gamma_table = 0;
+ cal->calibrator_type = type;
+ cal->k_white_level = target_white / 16;
+ cal->k_dark_level = 0;
+ DBG (5, "usb_high_cal_init: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_exit (Calibrator * cal)
+{
+ DBG (5, "usb_high_cal_exit: start\n");
+
+ if (!cal)
+ {
+ DBG (3, "usb_high_cal_exit: cal == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_exit: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "usb_high_cal_exit: 1\n");
+
+ if (cal->k_dark)
+ {
+ free (cal->k_dark);
+ }
+ cal->k_dark = NULL;
+ DBG (5, "usb_high_cal_exit: 2\n");
+ if (cal->k_white)
+ {
+ free (cal->k_white);
+ }
+ cal->k_white = NULL;
+ DBG (5, "usb_high_cal_exit: 3\n");
+
+ cal->is_prepared = SANE_FALSE;
+ DBG (5, "usb_high_cal_exit: 4\n");
+ DBG (5, "usb_high_cal_exit: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_embed_gamma (Calibrator * cal, SANE_Word * gamma_table)
+{
+ DBG (5, "usb_high_cal_embed_gamma: start\n");
+ cal->gamma_table = gamma_table;
+ DBG (5, "usb_high_cal_embed_gamma: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_prepare (Calibrator * cal, SANE_Word max_width)
+{
+ DBG (5, "usb_high_cal_Parepare: start\n");
+
+ if (cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_Parepare: is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (cal->k_white)
+ {
+ free (cal->k_white);
+ }
+ cal->k_white = (SANE_Word *) malloc (max_width * sizeof (SANE_Word));
+ if (!cal->k_white)
+ return SANE_STATUS_NO_MEM;
+
+ if (cal->k_dark)
+ {
+ free (cal->k_dark);
+ }
+ cal->k_dark = (SANE_Word *) malloc (max_width * sizeof (SANE_Word));
+ if (!cal->k_dark)
+ return SANE_STATUS_NO_MEM;
+
+ cal->max_width = max_width;
+
+ cal->is_prepared = SANE_TRUE;
+
+ DBG (5, "usb_high_cal_Parepare: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_high_cal_setup (Calibrator * cal, SANE_Word major_average,
+ SANE_Word minor_average, SANE_Word filter,
+ SANE_Word width, SANE_Word * white_needed,
+ SANE_Word * dark_needed)
+{
+ SANE_Int i;
+
+ DBG (5, "usb_high_cal_setup: start\n");
+
+ if (!cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_setup: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (major_average == 0)
+ {
+ DBG (3, "usb_high_cal_setup: major_average==0\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (minor_average == 0)
+ {
+ DBG (3, "usb_high_cal_setup: minor_average==0\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (width > cal->max_width)
+ {
+ DBG (3, "usb_high_cal_setup: width>max_width\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ cal->major_average = major_average;
+ cal->minor_average = minor_average;
+ cal->filter = filter;
+ cal->width = width;
+ cal->white_needed = major_average * 16 + filter;
+ cal->dark_needed = major_average * 16;
+ *white_needed = cal->white_needed;
+ *dark_needed = cal->dark_needed;
+
+ if (cal->white_line)
+ {
+ free (cal->white_line);
+ }
+ cal->white_line = (double *) malloc (cal->width * sizeof (double));
+ if (!cal->white_line)
+ return SANE_STATUS_NO_MEM;
+
+ if (cal->dark_line)
+ {
+ free (cal->dark_line);
+ }
+ cal->dark_line = (double *) malloc (cal->width * sizeof (double));
+ if (!cal->dark_line)
+ return SANE_STATUS_NO_MEM;
+
+ for (i = 0; i < cal->width; i++)
+ {
+ cal->white_line[i] = 0.0;
+ cal->dark_line[i] = 0.0;
+ }
+
+ if (cal->white_buffer)
+ {
+ free (cal->white_buffer);
+ }
+ cal->white_buffer =
+ (SANE_Int *) malloc (cal->white_needed * cal->width * sizeof (SANE_Int));
+ if (!cal->white_buffer)
+ return SANE_STATUS_NO_MEM;
+
+ for (i = 0; i < cal->white_needed * cal->width; i++)
+ {
+ *(cal->white_buffer + i) = 0;
+ }
+
+ return SANE_STATUS_GOOD;
+ DBG (5, "usb_high_cal_setup: start\n");
+}
+
+SANE_Status
+usb_high_cal_evaluate_white (Calibrator * cal, double factor)
+{
+ /* Caculate white_line */
+ double loop_division;
+ double average;
+ SANE_Int *buffer;
+ SANE_Word i, j;
+
+ DBG (5, "usb_high_cal_evaluate_white: start\n");
+ loop_division = (double) (cal->major_average * cal->minor_average);
+ buffer = (SANE_Int *) malloc (cal->white_needed * sizeof (SANE_Int));
+ if (!buffer)
+ return SANE_STATUS_NO_MEM;
+
+ if (cal->white_buffer == NULL)
+ {
+ DBG (3, "usb_high_cal_evaluate_white: white_buffer==NULL\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < cal->width; i++)
+ {
+ for (j = 0; j < cal->white_needed; j++)
+ {
+ *(buffer + j) = *(cal->white_buffer + j * cal->width + i);
+ }
+ average =
+ filter_lower_end (buffer, cal->white_needed,
+ cal->filter) * factor / loop_division;
+ if (average >= 4096.0)
+ cal->white_line[i] = 4095.9999;
+ else if (average < 0.0)
+ cal->white_line[i] = 0.0;
+ else
+ cal->white_line[i] = average;
+ }
+ free (buffer);
+ buffer = NULL;
+ free (cal->white_buffer);
+ cal->white_buffer = NULL;
+ DBG (5, "usb_high_cal_evaluate_white: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_evaluate_dark (Calibrator * cal, double factor)
+{
+ SANE_Word i;
+ double loop_division;
+
+ DBG (5, "usb_high_cal_evaluate_dark: start\n");
+ /* Caculate dark_line */
+ factor *= 16.0;
+ loop_division = (double) (cal->major_average * cal->minor_average);
+ for (i = 0; i < cal->width; i++)
+ {
+ cal->dark_line[i] /= loop_division;
+ cal->dark_line[i] -= factor;
+ if (cal->dark_line[i] < 0.0)
+ cal->dark_line[i] = 0.0;
+ }
+ DBG (5, "usb_high_cal_evaluate_dark: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_evaluate_calibrator (Calibrator * cal)
+{
+ SANE_Int average = 0;
+ SANE_Word i;
+
+ DBG (5, "usb_high_cal_evaluate_calibrator: start\n");
+ if (cal->white_line == NULL)
+ {
+ DBG (3, "usb_high_cal_evaluate_calibrator: white_line==NULL\n");
+ return SANE_FALSE;
+ }
+ if (cal->dark_line == NULL)
+ {
+ DBG (3, "usb_high_cal_evaluate_calibrator: dark_line==NULL\n");
+ return SANE_FALSE;
+ }
+
+ for (i = 0; i < cal->width; i++)
+ {
+ average = (SANE_Int) (cal->white_line[i])
+ - (SANE_Int) (cal->dark_line[i]);
+ if (average <= 0)
+ average = 1;
+ else if (average >= 4096)
+ average = 4095;
+ cal->k_white[i] = (SANE_Word) (average);
+ cal->k_dark[i] = (SANE_Word) (cal->dark_line[i]);
+ }
+ free (cal->dark_line);
+ cal->dark_line = NULL;
+ free (cal->white_line);
+ cal->white_line = NULL;
+
+ DBG (5, "usb_high_cal_evaluate_calibrator: start\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* virtual function switcher */
+SANE_Status
+usb_high_cal_fill_in_white (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *white_pattern)
+{
+ DBG (5, "usb_high_cal_fill_in_white: start\n");
+ switch (cal->calibrator_type)
+ {
+ case I8O8RGB:
+ case I8O8MONO:
+ return usb_high_cal_i8o8_fill_in_white (cal, major, minor,
+ white_pattern);
+ break;
+ case I4O1MONO:
+ return usb_high_cal_i4o1_fill_in_white (cal, major, minor,
+ white_pattern);
+ break;
+ }
+ DBG (5, "usb_high_cal_fill_in_white: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor,
+ void *dark_pattern)
+{
+ DBG (5, "usb_high_cal_fill_in_dark: start\n");
+ switch (cal->calibrator_type)
+ {
+ case I8O8RGB:
+ case I8O8MONO:
+ return usb_high_cal_i8o8_fill_in_dark (cal, major, minor, dark_pattern);
+ break;
+ case I4O1MONO:
+ return usb_high_cal_i4o1_fill_in_dark (cal, major, minor, dark_pattern);
+ break;
+ }
+ DBG (5, "usb_high_cal_fill_in_dark: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_calibrate (Calibrator * cal, void *src, void *target)
+{
+ DBG (5, "usb_high_cal_calibrate: start\n");
+ switch (cal->calibrator_type)
+ {
+ case I8O8RGB:
+ return usb_high_cal_i8o8_rgb_calibrate (cal, src, target);
+ break;
+ case I8O8MONO:
+ return usb_high_cal_i8o8_mono_calibrate (cal, src, target);
+ break;
+ case I4O1MONO:
+ return usb_high_cal_i4o1_calibrate (cal, src, target);
+ break;
+ }
+ DBG (5, "usb_high_cal_calibrate: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i8o8_fill_in_white (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *white_pattern)
+{
+ SANE_Byte *pattern;
+ SANE_Word j;
+
+ pattern = (SANE_Byte *) white_pattern;
+
+ DBG (5, "usb_high_cal_i8o8_fill_in_white: start, minor=%d\n", minor);
+ if (!cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_i8o8_fill_in_white: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (cal->white_needed == 0)
+ {
+ DBG (3, "usb_high_cal_i8o8_fill_in_white: white_needed==0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (j = 0; j < cal->width; j++)
+ {
+ *(cal->white_buffer + major * cal->width + j) +=
+ (SANE_Int) (pattern[j]);
+ }
+ DBG (5, "usb_high_cal_i8o8_fill_in_white: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i8o8_fill_in_dark (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *dark_pattern)
+{
+ SANE_Byte *pattern = (SANE_Byte *) dark_pattern;
+ SANE_Word j;
+
+ DBG (5, "usb_high_cal_i8o8_fill_in_dark: start, major=%d, minor=%d\n",
+ major, minor);
+ if (!cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_i8o8_fill_in_dark: !is_prepared\n");
+ return SANE_FALSE;
+ }
+ if (cal->dark_needed == 0)
+ {
+ DBG (3, "usb_high_cal_i8o8_fill_in_dark: dark_needed==0\n");
+ return SANE_FALSE;
+ }
+
+ for (j = 0; j < cal->width; j++)
+ {
+ cal->dark_line[j] += (double) (pattern[j]);
+ }
+ DBG (5, "usb_high_cal_i8o8_fill_in_dark: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i4o1_fill_in_white (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *white_pattern)
+{
+ SANE_Byte *pattern;
+ SANE_Word j = 0;
+
+ pattern = (SANE_Byte *) white_pattern;
+
+ DBG (5, "usb_high_cal_i4o1_fill_in_white: minor=%d\n", minor);
+ if (!cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_i4o1_fill_in_white: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (cal->white_needed == 0)
+ {
+ DBG (3, "usb_high_cal_i4o1_fill_in_white: white_needed==0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ while (j < cal->width)
+ {
+ *(cal->white_buffer + major * cal->width + j) +=
+ (SANE_Int) (*(pattern) & 0xf0);
+ j++;
+ if (j >= cal->width)
+ break;
+ *(cal->white_buffer + major * cal->width + j) +=
+ (SANE_Int) ((SANE_Byte) (*(pattern++) << 4));
+ j++;
+ }
+ DBG (5, "usb_high_cal_i8o8_fill_in_white: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i4o1_fill_in_dark (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *dark_pattern)
+{
+ SANE_Byte *pattern;
+ SANE_Word j = 0;
+
+ pattern = (SANE_Byte *) dark_pattern;
+
+ DBG (5, "usb_high_cal_i4o1_fill_in_dark: start, major=%d, minor=%d\n",
+ major, minor);
+ if (!cal->is_prepared)
+ {
+ DBG (3, "usb_high_cal_i4o1_fill_in_dark: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (cal->dark_needed == 0)
+ {
+ DBG (5, "usb_high_cal_i4o1_fill_in_dark: dark_needed==0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ while (j < cal->width)
+ {
+ cal->dark_line[j++] += (double) (*(pattern) & 0xf0);
+ if (j >= cal->width)
+ break;
+ cal->dark_line[j++] += (double) ((SANE_Byte) (*(pattern++) << 4));
+ }
+ DBG (5, "usb_high_cal_i4o1_fill_in_dark: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i8o8_mono_calibrate (Calibrator * cal, void *src, void *target)
+{
+ SANE_Byte *gray_src;
+ SANE_Byte *gray_target;
+ SANE_Int base = 0;
+ SANE_Word value = 0;
+ SANE_Word i;
+
+ DBG (5, "usb_high_cal_i8o8_mono_calibrate: start\n");
+
+ gray_src = (SANE_Byte *) src;
+ gray_target = (SANE_Byte *) target;
+
+ if (cal->gamma_table == NULL)
+ {
+ SANE_Word k_white_level = cal->k_white_level >> 4;
+ for (i = 0; i < cal->width; i++)
+ {
+ base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4)
+ - (SANE_Int) (cal->k_dark[i]);
+ if (base < 0)
+ base = 0;
+ value = ((SANE_Word) (base) * k_white_level) / cal->k_white[i];
+ if (value > 0x00ff)
+ value = 0x00ff;
+ gray_target[i] = (SANE_Byte) (value);
+ }
+ }
+ else
+ {
+ for (i = 0; i < cal->width; i++)
+ {
+ base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4)
+ - (SANE_Int) (cal->k_dark[i]);
+ if (base < 0)
+ base = 0;
+ value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[i];
+ if (value > 0x0fff)
+ value = 0x0fff;
+ gray_target[i] = (SANE_Byte) (cal->gamma_table[value]);
+ }
+ }
+ DBG (5, "usb_high_cal_i8o8_mono_calibrate: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i8o8_rgb_calibrate (Calibrator * cal, void *src, void *target)
+{
+ SANE_Byte *gray_src;
+ SANE_Byte *rgb_target;
+ SANE_Int base = 0;
+ SANE_Word value = 0;
+ SANE_Word i;
+
+ DBG (5, "usb_high_cal_i8o8_rgb_calibrate: start\n");
+ gray_src = (SANE_Byte *) src;
+ rgb_target = (SANE_Byte *) target;
+
+ if (cal->gamma_table == NULL)
+ {
+ SANE_Word k_white_level = cal->k_white_level >> 4;
+ for (i = 0; i < cal->width; i++)
+ {
+ base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4)
+ - (SANE_Int) (cal->k_dark[i]);
+ if (base < 0)
+ base = 0;
+ value = ((SANE_Word) (base) * k_white_level) / cal->k_white[i];
+ if (value > 0x00ff)
+ value = 0x00ff;
+ *rgb_target = (SANE_Byte) (value);
+ rgb_target += 3;
+ }
+ }
+ else
+ {
+ for (i = 0; i < cal->width; i++)
+ {
+ base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4)
+ - (SANE_Int) (cal->k_dark[i]);
+ if (base < 0)
+ base = 0;
+ value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[i];
+ if (value > 0x0fff)
+ value = 0x0fff;
+ *(rgb_target) = (SANE_Byte) (cal->gamma_table[value]);
+ rgb_target += 3;
+ }
+ }
+ DBG (5, "usb_high_cal_i8o8_rgb_calibrate: start\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_cal_i4o1_calibrate (Calibrator * cal, void *src, void *target)
+{
+ SANE_Byte *local_src;
+ SANE_Byte *local_target;
+ SANE_Int base = 0;
+ SANE_Word value = 0;
+ SANE_Word j = 0;
+ SANE_Int count = 0;
+
+ DBG (5, "usb_high_cal_i4o1_calibrate: start\n");
+ local_src = (SANE_Byte *) src;
+ local_target = (SANE_Byte *) target;
+
+ *local_target = 0;
+ while (j < cal->width)
+ {
+ base =
+ (SANE_Int) ((SANE_Word) (*local_src & 0xf0) << 4)
+ - (SANE_Int) (cal->k_dark[j]);
+ if (base < 0)
+ base = 0;
+ value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[j];
+ if (value > 0x0fff)
+ value = 0x0fff;
+ if (value >= cal->threshold)
+ *(local_target) |= gray_map[count];
+ count++;
+ j++;
+ if (j >= cal->width)
+ break;
+ base = (SANE_Int) ((SANE_Word) (*(local_src++) & 0x0f) << 8) -
+ (SANE_Int) (cal->k_dark[j]);
+ if (base < 0)
+ base = 0;
+ value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[j];
+ if (value > 0x0fff)
+ value = 0x0fff;
+ if (value >= cal->threshold)
+ *(local_target) |= gray_map[count];
+ count++;
+ if (count >= 8)
+ {
+ local_target++;
+ *local_target = 0;
+ count = 0;
+ }
+ j++;
+ }
+ DBG (5, "usb_high_cal_i4o1_calibrate: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/* --------------------------- scan functions ----------------------------- */
+
+
+SANE_Status
+usb_high_scan_init (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_init: start\n");
+
+ dev->init_bytes_per_strip = 8 * 1024;
+ dev->adjust_length_300 = 2560;
+ dev->adjust_length_600 = 5120;
+ dev->init_min_expose_time = 4992;
+ dev->init_skips_per_row_300 = 56; /* this value must be times of 6 */
+ dev->init_skips_per_row_600 = 72; /* this value must be times of 6 */
+ dev->init_j_lines = 154;
+ dev->init_k_lines = 16;
+ dev->init_k_filter = 8;
+ dev->init_k_loops = 2;
+ dev->init_pixel_rate_lines = 50;
+ dev->init_pixel_rate_filts = 37;
+ dev->init_powerdelay_lines = 2;
+ dev->init_home_lines = 160;
+ dev->init_dark_lines = 50;
+ dev->init_k_level = 245;
+ dev->init_max_power_delay = 240;
+ dev->init_min_power_delay = 136;
+ dev->init_adjust_way = 1;
+ dev->init_green_black_factor = 0.0;
+ dev->init_blue_black_factor = 0.0;
+ dev->init_red_black_factor = 0.0;
+ dev->init_gray_black_factor = 0.0;
+ dev->init_green_factor = 0.82004;
+ dev->init_blue_factor = 0.84954;
+ dev->init_red_factor = 0.826375;
+ dev->init_gray_factor = 0.833375;
+
+ dev->init_red_rgb_600_pga = 8;
+ dev->init_green_rgb_600_pga = 8;
+ dev->init_blue_rgb_600_pga = 8;
+ dev->init_mono_600_pga = 8;
+ dev->init_red_rgb_300_pga = 8;
+ dev->init_green_rgb_300_pga = 8;
+ dev->init_blue_rgb_300_pga = 8;
+ dev->init_mono_300_pga = 8;
+ dev->init_expose_time = 9024;
+ dev->init_red_rgb_600_power_delay = 80;
+ dev->init_green_rgb_600_power_delay = 80;
+ dev->init_blue_rgb_600_power_delay = 80;
+ dev->init_red_mono_600_power_delay = 80;
+ dev->init_green_mono_600_power_delay = 80;
+ dev->init_blue_mono_600_power_delay = 80;
+ dev->init_red_rgb_300_power_delay = 80;
+ dev->init_green_rgb_300_power_delay = 80;
+ dev->init_blue_rgb_300_power_delay = 80;
+ dev->init_red_mono_300_power_delay = 80;
+ dev->init_green_mono_300_power_delay = 80;
+ dev->init_blue_mono_300_power_delay = 80;
+ dev->init_threshold = 128;
+
+ dev->init_top_ref = 128;
+ dev->init_front_end = 16;
+ dev->init_red_offset = 0;
+ dev->init_green_offset = 0;
+ dev->init_blue_offset = 0;
+
+ dev->init_rgb_24_back_track = 80;
+ dev->init_mono_8_back_track = 80;
+
+ dev->is_open = SANE_FALSE;
+ dev->is_prepared = SANE_FALSE;
+ dev->expose_time = 4000;
+ dev->width = 2550;
+ dev->x_dpi = 300;
+ dev->y_dpi = 300;
+ dev->scan_mode = RGB24EXT;
+ dev->bytes_per_row = 2550 * 3;
+ dev->dummy = 0;
+ dev->bytes_per_strip = 2550;
+ dev->image_buffer = NULL;
+ dev->red = NULL;
+ dev->green = NULL;
+ dev->blue = NULL;
+ dev->get_line = NULL;
+ dev->backtrack = NULL;
+ dev->is_adjusted_rgb_600_power_delay = SANE_FALSE;
+ dev->is_adjusted_mono_600_power_delay = SANE_FALSE;
+ dev->is_adjusted_rgb_300_power_delay = SANE_FALSE;
+ dev->is_adjusted_mono_300_power_delay = SANE_FALSE;
+ dev->is_evaluate_pixel_rate = SANE_FALSE;
+ dev->red_rgb_600_pga = 0;
+ dev->green_rgb_600_pga = 0;
+ dev->blue_rgb_600_pga = 0;
+ dev->mono_600_pga = 0;
+ dev->red_rgb_600_power_delay = 0;
+ dev->green_rgb_600_power_delay = 0;
+ dev->blue_rgb_600_power_delay = 0;
+ dev->red_mono_600_power_delay = 0;
+ dev->green_mono_600_power_delay = 0;
+ dev->blue_mono_600_power_delay = 0;
+ dev->red_rgb_300_pga = 0;
+ dev->green_rgb_300_pga = 0;
+ dev->blue_rgb_300_pga = 0;
+ dev->mono_300_pga = 0;
+ dev->red_rgb_300_power_delay = 0;
+ dev->green_rgb_300_power_delay = 0;
+ dev->blue_rgb_300_power_delay = 0;
+ dev->red_mono_300_power_delay = 0;
+ dev->green_mono_300_power_delay = 0;
+ dev->blue_mono_300_power_delay = 0;
+ dev->pixel_rate = 2000;
+ dev->threshold = 128;
+ dev->gamma_table = 0;
+ dev->skips_per_row = 0;
+
+
+ dev->red_calibrator = NULL;
+ dev->green_calibrator = NULL;
+ dev->blue_calibrator = NULL;
+ dev->mono_calibrator = NULL;
+
+ dev->is_cis_detected = SANE_FALSE;
+ dev->is_sensor_detected = SANE_FALSE;
+
+ RIE (usb_low_init (&dev->chip));
+
+ DBG (5, "usb_high_scan_init: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_exit (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_exit: start\n");
+ if (!dev->chip)
+ {
+ DBG (5, "usb_high_scan_exit: already exited (`%s')\n", dev->name);
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_exit (dev->chip));
+ dev->chip = 0;
+ DBG (5, "usb_high_scan_exit: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_prepare: start dev=%p\n", (void *) dev);
+ if (dev->is_prepared)
+ {
+ DBG (5, "usb_high_scan_prepare: is already prepared\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (dev->image_buffer)
+ {
+ free (dev->image_buffer);
+ }
+ dev->image_buffer = (SANE_Byte *) malloc (dev->init_bytes_per_strip * 3);
+ if (!dev->image_buffer)
+ return SANE_STATUS_NO_MEM;
+
+ dev->red = dev->image_buffer;
+ dev->green = dev->image_buffer + dev->init_bytes_per_strip;
+ dev->blue = dev->image_buffer + dev->init_bytes_per_strip * 2;
+
+ dev->is_prepared = SANE_TRUE;
+ DBG (5, "usb_high_scan_prepare: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_clearup (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_clearup: start, dev=%p\n", (void *) dev);
+ if (!dev->is_prepared)
+ {
+ DBG (3, "usb_high_scan_clearup: is not prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (dev->image_buffer)
+ {
+ free (dev->image_buffer);
+ }
+ dev->image_buffer = NULL;
+ dev->red = NULL;
+ dev->green = NULL;
+ dev->blue = NULL;
+
+ dev->is_prepared = SANE_FALSE;
+ DBG (5, "usb_high_scan_clearup: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_turn_power (Mustek_Usb_Device * dev, SANE_Bool is_on)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_turn_power: start, turn %s power\n",
+ is_on ? "on" : "off");
+
+ if (is_on)
+ {
+ if (dev->is_open)
+ {
+ DBG (3, "usb_high_scan_turn_power: wanted to turn on power, "
+ "but scanner already open\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_open (dev->chip, dev->device_name));
+ dev->is_open = SANE_TRUE;
+ RIE (usb_low_turn_peripheral_power (dev->chip, SANE_TRUE));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+ }
+ else
+ {
+ if (!dev->is_open)
+ {
+ DBG (3, "usb_high_scan_turn_power: wanted to turn off power, "
+ "but scanner already closed\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE));
+ RIE (usb_low_close (dev->chip));
+ dev->is_open = SANE_FALSE;
+ }
+
+ DBG (5, "usb_high_scan_turn_power: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_back_home (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_back_home: start\n");
+
+ if (!dev->is_open)
+ {
+ DBG (3, "usb_high_scan_back_home: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time));
+ RIE (usb_mid_motor_prepare_home (dev->chip));
+
+ DBG (5, "usb_high_scan_back_home: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_set_threshold (Mustek_Usb_Device * dev, SANE_Byte threshold)
+{
+ DBG (5, "usb_high_scan_set_threshold: start, dev=%p, threshold=%d\n",
+ (void *) dev, threshold);
+
+ dev->threshold = threshold;
+ DBG (5, "usb_high_scan_set_threshold: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_embed_gamma (Mustek_Usb_Device * dev, SANE_Word * gamma_table)
+{
+ DBG (5, "usb_high_scan_embed_gamma: start, dev=%p, gamma_table=%p\n",
+ (void *) dev, (void *) gamma_table);
+ if (!dev->is_prepared)
+ {
+ DBG (5, "usb_high_scan_embed_gamma !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ dev->gamma_table = gamma_table;
+ DBG (5, "usb_high_scan_embed_gamma: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_reset (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_reset: start\n");
+
+ if (!dev->is_open)
+ {
+ DBG (3, "usb_high_scan_reset: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!dev->is_prepared)
+ {
+ DBG (3, "usb_high_scan_reset: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_high_scan_init_asic (dev, dev->chip->sensor));
+ RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time));
+ RIE (usb_mid_motor_prepare_home (dev->chip));
+ RIE (usb_high_scan_set_threshold (dev, dev->init_threshold));
+ RIE (usb_high_scan_embed_gamma (dev, NULL));
+ dev->is_adjusted_rgb_600_power_delay = SANE_FALSE;
+ dev->is_adjusted_mono_600_power_delay = SANE_FALSE;
+ dev->is_adjusted_rgb_300_power_delay = SANE_FALSE;
+ dev->is_adjusted_mono_300_power_delay = SANE_FALSE;
+ dev->is_evaluate_pixel_rate = SANE_FALSE;
+ DBG (5, "usb_high_scan_reset: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_wait_carriage_home (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_wait_carriage_home: start\n");
+
+ status = usb_low_get_home_sensor (dev->chip);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time));
+ RIE (usb_mid_motor_prepare_home (dev->chip));
+ do
+ {
+ status = usb_low_get_home_sensor (dev->chip);
+ if (status != SANE_STATUS_GOOD)
+ usleep (18 * 1000);
+ }
+ while (status != SANE_STATUS_GOOD);
+ }
+
+ /* No Motor & Forward */
+ RIE (usb_low_move_motor_home (dev->chip, SANE_FALSE, SANE_FALSE));
+ DBG (5, "usb_high_scan_wait_carriage_home: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_hardware_calibration (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_hardware_calibration: start\n");
+
+ if (dev->is_cis_detected)
+ RIE (usb_high_scan_safe_forward (dev, dev->init_home_lines));
+
+ switch (dev->init_adjust_way)
+ {
+ case 1: /* CIS */
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ {
+ dev->expose_time = dev->init_expose_time;
+ dev->red_rgb_600_pga = dev->init_red_rgb_600_pga;
+ dev->green_rgb_600_pga = dev->init_green_rgb_600_pga;
+ dev->blue_rgb_600_pga = dev->init_blue_rgb_600_pga;
+ RIE (usb_high_scan_adjust_rgb_600_power_delay (dev));
+ }
+ else
+ {
+ dev->expose_time = dev->init_expose_time;
+ dev->red_rgb_300_pga = dev->init_red_rgb_300_pga;
+ dev->green_rgb_300_pga = dev->init_green_rgb_300_pga;
+ dev->blue_rgb_300_pga = dev->init_blue_rgb_300_pga;
+ RIE (usb_high_scan_adjust_rgb_300_power_delay (dev));
+ }
+ break;
+ case GRAY8EXT:
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ {
+ dev->expose_time = dev->init_expose_time;
+ dev->mono_600_pga = dev->init_mono_600_pga;
+ RIE (usb_high_scan_evaluate_pixel_rate (dev));
+ RIE (usb_high_scan_adjust_mono_600_power_delay (dev));
+ }
+ else
+ {
+ dev->expose_time = dev->init_expose_time;
+ dev->mono_300_pga = dev->init_mono_300_pga;
+ RIE (usb_high_scan_evaluate_pixel_rate (dev));
+ RIE (usb_high_scan_adjust_mono_300_power_delay (dev));
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 3: /* CCD */
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ dev->red_rgb_600_pga = dev->init_red_rgb_600_pga;
+ dev->green_rgb_600_pga = dev->init_green_rgb_600_pga;
+ dev->blue_rgb_600_pga = dev->init_blue_rgb_600_pga;
+ dev->skips_per_row = dev->init_skips_per_row_600;
+ /* RIE(usb_high_scan_adjust_rgb_600_exposure (dev); fixme */
+ /* RIE(usb_high_scan_adjust_rgb_600_offset (dev); fixme */
+ /* RIE(usb_high_scan_adjust_rgb_600_pga (dev); fixme */
+ /* m_isAdjustedRgb600Offset=FALSE; */
+ /* RIE(usb_high_scan_adjust_rgb_600_offset (dev); fixme */
+ /* RIE(usb_high_scan_adjust_rgb_600_skips_per_row (dev); fixme */
+ break;
+ case GRAY8EXT:
+ dev->mono_600_pga = dev->init_mono_600_pga;
+ dev->skips_per_row = dev->init_skips_per_row_600;
+ RIE (usb_high_scan_adjust_mono_600_exposure (dev));
+ /* RIE(usb_high_scan_adjust_mono_600_offset (dev); fixme */
+ /* RIE(usb_high_scan_adjust_mono_600_pga (dev); fixme */
+ dev->is_adjusted_mono_600_offset = SANE_FALSE;
+ /* RIE(usb_high_scan_adjust_mono_600_offset (dev); fixme */
+ /* RIE(usb_high_scan_adjust_mono_600_skips_per_row (dev); fixme */
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ dev->expose_time = dev->init_expose_time;
+ dev->red_rgb_600_power_delay = dev->init_red_rgb_600_power_delay;
+ dev->green_rgb_600_power_delay = dev->init_green_rgb_600_power_delay;
+ dev->blue_rgb_600_power_delay = dev->init_blue_rgb_600_power_delay;
+ dev->red_mono_600_power_delay = dev->init_red_mono_600_power_delay;
+ dev->green_mono_600_power_delay = dev->init_green_mono_600_power_delay;
+ dev->blue_mono_600_power_delay = dev->init_blue_mono_600_power_delay;
+ dev->red_rgb_600_pga = dev->init_red_rgb_600_pga;
+ dev->green_rgb_600_pga = dev->init_green_rgb_600_pga;
+ dev->blue_rgb_600_pga = dev->init_blue_rgb_600_pga;
+ dev->mono_600_pga = dev->init_mono_600_pga;
+ dev->red_rgb_300_power_delay = dev->init_red_rgb_300_power_delay;
+ dev->green_rgb_300_power_delay = dev->init_green_rgb_300_power_delay;
+ dev->blue_rgb_300_power_delay = dev->init_blue_rgb_300_power_delay;
+ dev->red_mono_300_power_delay = dev->init_red_mono_300_power_delay;
+ dev->green_mono_300_power_delay = dev->init_green_mono_300_power_delay;
+ dev->blue_mono_300_power_delay = dev->init_blue_mono_300_power_delay;
+ dev->red_rgb_300_pga = dev->init_red_rgb_300_pga;
+ dev->green_rgb_300_pga = dev->init_green_rgb_300_pga;
+ dev->blue_rgb_300_pga = dev->init_blue_rgb_300_pga;
+ dev->mono_300_pga = dev->init_mono_300_pga;
+ break;
+ }
+ DBG (5, "usb_high_scan_hardware_calibration: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_line_calibration (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_line_calibration: start\n");
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ RIE (usb_high_scan_prepare_rgb_24 (dev));
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ RIE (usb_high_scan_prepare_rgb_signal_600_dpi (dev));
+ else
+ RIE (usb_high_scan_prepare_rgb_signal_300_dpi (dev));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, dev->x_dpi));
+ RIE (usb_high_scan_calibration_rgb_24 (dev));
+ break;
+ case GRAY8EXT:
+ RIE (usb_high_scan_prepare_mono_8 (dev));
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ RIE (usb_high_scan_prepare_mono_signal_600_dpi (dev));
+ else
+ RIE (usb_high_scan_prepare_mono_signal_300_dpi (dev));
+ RIE (usb_mid_sensor_prepare_mono (dev->chip, dev->x_dpi));
+ RIE (usb_high_scan_calibration_mono_8 (dev));
+ break;
+ default:
+ DBG (3, "usb_high_scan_line_calibration: mode not matched\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ DBG (5, "usb_high_scan_line_calibration: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare_scan (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_scan: start\n");
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ RIE (usb_high_scan_prepare_rgb_24 (dev));
+ dev->get_line = &usb_high_scan_get_rgb_24_bit_line;
+ dev->backtrack = &usb_high_scan_backtrack_rgb_24;
+
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ RIE (usb_high_scan_prepare_rgb_signal_600_dpi (dev));
+ else
+ RIE (usb_high_scan_prepare_rgb_signal_300_dpi (dev));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, dev->x_dpi));
+ RIE (usb_mid_motor_prepare_rgb (dev->chip, dev->y_dpi));
+ break;
+ case GRAY8EXT:
+ RIE (usb_high_scan_prepare_mono_8 (dev));
+ dev->get_line = &usb_high_scan_get_mono_8_bit_line;
+ dev->backtrack = &usb_high_scan_backtrack_mono_8;
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ RIE (usb_high_scan_prepare_mono_signal_600_dpi (dev));
+ else
+ RIE (usb_high_scan_prepare_mono_signal_300_dpi (dev));
+ RIE (usb_mid_sensor_prepare_mono (dev->chip, dev->x_dpi));
+ RIE (usb_mid_motor_prepare_mono (dev->chip, dev->y_dpi));
+ break;
+ default:
+ DBG (5, "usb_high_scan_prepare_scan: unmatched mode\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ DBG (5, "usb_high_scan_prepare_scan: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_suggest_parameters (Mustek_Usb_Device * dev, SANE_Word dpi,
+ SANE_Word x, SANE_Word y, SANE_Word width,
+ SANE_Word height, Colormode color_mode)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_suggest_parameters: start\n");
+
+ RIE (usb_high_scan_detect_sensor (dev));
+ /* Looking up Optical Y Resolution */
+ RIE (usb_mid_motor_get_dpi (dev->chip, dpi, &dev->y_dpi));
+ /* Looking up Optical X Resolution */
+ RIE (usb_mid_sensor_get_dpi (dev->chip, dpi, &dev->x_dpi));
+
+ dev->x = x * dev->x_dpi / dpi;
+ dev->y = y * dev->y_dpi / dpi;
+ dev->width = width * dev->x_dpi / dpi;
+ dev->height = height * dev->y_dpi / dpi;
+
+ switch (color_mode)
+ {
+ case RGB24:
+ dev->scan_mode = RGB24EXT;
+ dev->bytes_per_row = dev->width * 3;
+ dev->bpp = 24;
+ break;
+ case GRAY8:
+ dev->scan_mode = GRAY8EXT;
+ dev->bpp = 8;
+ dev->bytes_per_row = dev->width;
+ break;
+ default:
+ DBG (3, "usb_high_scan_suggest_parameters: unmatched mode\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ DBG (5, "usb_high_scan_suggest_parameters: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_detect_sensor (Mustek_Usb_Device * dev)
+{
+ if (dev->is_sensor_detected)
+ {
+ DBG (5, "usb_high_scan_detect_sensor: sensor already detected\n");
+ return SANE_STATUS_GOOD;
+ }
+ dev->is_sensor_detected = SANE_TRUE;
+
+ switch (dev->chip->scanner_type)
+ {
+ case MT_600CU:
+ dev->chip->sensor = ST_CANON300;
+ dev->chip->motor = MT_600;
+ dev->is_cis_detected = SANE_TRUE;
+ DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 300 dpi, motor="
+ "600 dpi\n");
+ break;
+ case MT_1200USB:
+ dev->chip->sensor = ST_NEC600;
+ dev->chip->motor = MT_1200;
+ dev->init_min_expose_time = 2250;
+ dev->init_skips_per_row_600 = 0;
+ dev->init_home_lines = 32;
+ dev->init_dark_lines = 10;
+ dev->init_max_power_delay = 220;
+ dev->init_min_power_delay = 220;
+ dev->init_adjust_way = 3;
+ dev->init_red_rgb_600_pga = 30;
+ dev->init_green_rgb_600_pga = 30;
+ dev->init_blue_rgb_600_pga = 30;
+ dev->init_mono_600_pga = 30;
+ dev->init_expose_time = 16000;
+
+ dev->init_top_ref = 6;
+ dev->init_front_end = 12;
+ dev->init_red_offset = 128;
+ dev->init_green_offset = 128;
+ dev->init_blue_offset = 128;
+
+ dev->init_rgb_24_back_track = 0;
+ dev->init_mono_8_back_track = 40;
+
+ dev->is_cis_detected = SANE_FALSE;
+
+ DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 600 dpi, motor="
+ "1200 dpi\n");
+ break;
+ case MT_1200UB:
+ case MT_1200CU_PLUS:
+ case MT_1200CU: /* need to check if it's a 300600 or 600 dpi sensor */
+ {
+ SANE_Byte *buffer;
+ static SANE_Word l_temp = 0, r_temp = 0;
+ SANE_Int i;
+ SANE_Status status;
+ SANE_Word lines_left;
+
+ dev->chip->motor = MT_1200;
+ dev->is_cis_detected = SANE_TRUE;
+
+ buffer = NULL;
+ l_temp = 0;
+ r_temp = 0;
+
+ buffer = (SANE_Byte *) malloc (dev->init_bytes_per_strip);
+
+ if (!buffer)
+ return SANE_STATUS_NO_MEM;
+
+ for (i = 0; i < 5400; i++)
+ buffer[i] = 0xaa;
+
+ dev->scan_mode = GRAY8EXT;
+ dev->x_dpi = 600;
+ dev->y_dpi = 1200;
+ dev->width = 5400;
+
+ RIE (usb_high_scan_init_asic (dev, ST_CANON600));
+ RIE (usb_low_turn_peripheral_power (dev->chip, SANE_TRUE));
+ RIE (usb_low_enable_motor (dev->chip, SANE_TRUE)); /* Enable Motor */
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+ RIE (usb_low_invert_image (dev->chip, SANE_FALSE));
+ RIE (usb_low_set_image_dpi (dev->chip, SANE_TRUE, SW_P6P6));
+ dev->bytes_per_strip = dev->adjust_length_600;
+ dev->bytes_per_row = 5400;
+ dev->dummy = 0;
+
+ RIE (usb_high_scan_wait_carriage_home (dev));
+ RIE (usb_high_scan_hardware_calibration (dev));
+ RIE (usb_high_scan_prepare_scan (dev));
+
+ /* Get Data */
+ RIE (usb_low_start_rowing (dev->chip));
+ RIE (usb_low_get_row (dev->chip, buffer, &lines_left));
+ RIE (usb_low_stop_rowing (dev->chip));
+ /* Calculate */
+ for (i = 0; i < 256; i++)
+ l_temp = l_temp + buffer[512 + i];
+ for (i = 0; i < 256; i++)
+ r_temp = r_temp + buffer[3500 + i];
+
+ l_temp = l_temp / 256;
+ r_temp = r_temp / 256;
+
+ /* 300/600 switch CIS or 600 CIS */
+ DBG (5, "usb_high_scan_detect_sensor: l_temp=%d, r_temp=%d\n",
+ l_temp, r_temp);
+ if (r_temp > 50)
+ {
+ dev->chip->sensor = ST_CANON600;
+ DBG (4,
+ "usb_high_scan_detect_sensor: sensor=Canon 600 dpi, motor="
+ "1200 dpi\n");
+ }
+ else
+ {
+ DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 300/600 dpi, "
+ "motor=1200 dpi\n");
+ dev->chip->sensor = ST_CANON300600;
+ }
+
+ /* Release Resource */
+ free (buffer);
+ buffer = NULL;
+
+ break;
+ }
+ default:
+ DBG (5, "usb_high_scan_detect_sensor: I don't know this scanner type "
+ "(%d)\n", dev->chip->scanner_type);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_high_scan_setup_scan (Mustek_Usb_Device * dev, Colormode color_mode,
+ SANE_Word x_dpi, SANE_Word y_dpi,
+ SANE_Bool is_invert, SANE_Word x, SANE_Word y,
+ SANE_Word width)
+{
+ SANE_Status status;
+ SANE_Word upper_bound;
+ SANE_Word left_bound;
+
+ DBG (5, "usb_high_scan_setup_scan: start, is_invert=%d\n", is_invert);
+ if (!dev->is_open)
+ {
+ DBG (5, "usb_high_scan_setup_scan: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!dev->is_prepared)
+ {
+ DBG (5, "usb_high_scan_setup_scan: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_high_scan_init_asic (dev, dev->chip->sensor));
+ RIE (usb_low_turn_peripheral_power (dev->chip, SANE_TRUE));
+ RIE (usb_low_enable_motor (dev->chip, SANE_TRUE)); /* Enable Motor */
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+ RIE (usb_low_invert_image (dev->chip, SANE_FALSE));
+ if (!dev->is_cis_detected)
+ {
+ usb_mid_front_set_front_end_mode (dev->chip, 16);
+ usb_mid_front_enable (dev->chip, SANE_TRUE);
+ usb_mid_front_set_top_reference (dev->chip, 244);
+ usb_mid_front_set_rgb_signal (dev->chip);
+ }
+
+ /* Compute necessary variables */
+ dev->scan_mode = color_mode;
+ dev->x_dpi = x_dpi;
+ dev->y_dpi = y_dpi;
+ dev->width = width;
+
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ dev->bytes_per_row = 3 * dev->width;
+ upper_bound = ((y * 600) / dev->y_dpi) + dev->init_j_lines;
+ break;
+ case GRAY8EXT:
+ dev->bytes_per_row = dev->width;
+ upper_bound = ((y * 600) / dev->y_dpi) + dev->init_j_lines + 4;
+ /* fixme */
+ break;
+ default:
+ upper_bound = ((y * 600) / dev->y_dpi) + dev->init_j_lines + 4;
+ break;
+ }
+
+ if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi))
+ {
+ /* in 600dpi */
+ left_bound = (x * 600 / dev->x_dpi) + dev->init_skips_per_row_600;
+ dev->skips_per_row = (((left_bound % 32) * dev->x_dpi + 300) / 600);
+ }
+ else
+ {
+ /* in 300dpi */
+ left_bound = (x * 300 / dev->x_dpi) + dev->init_skips_per_row_300;
+ dev->skips_per_row = (((left_bound % 32) * dev->x_dpi + 150) / 300);
+ }
+
+ dev->dummy = (left_bound / 32) * 32;
+
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ dev->bytes_per_strip = dev->skips_per_row + dev->width;
+ break;
+ case GRAY8EXT:
+ dev->bytes_per_strip = dev->skips_per_row + dev->width;
+ break;
+ default:
+ break;
+ }
+
+ dev->bytes_per_strip = ((dev->bytes_per_strip + 1) / 2) * 2;
+ /* make bytes_per_strip is as 2n to advoid 64n+1 */
+
+ RIE (usb_high_scan_wait_carriage_home (dev));
+ RIE (usb_high_scan_hardware_calibration (dev));
+ RIE (usb_high_scan_line_calibration (dev));
+ RIE (usb_high_scan_step_forward (dev, upper_bound));
+ RIE (usb_high_scan_prepare_scan (dev));
+ RIE (usb_low_start_rowing (dev->chip));
+ /* pat_chromator fixme (init for calibration?) */
+ DBG (5, "usb_high_scan_setup_scan: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_get_rows (Mustek_Usb_Device * dev, SANE_Byte * block,
+ SANE_Word rows, SANE_Bool is_order_invert)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_get_rows: start, %d rows\n", rows);
+ if (!dev->is_open)
+ {
+ DBG (3, "usb_high_scan_get_rows: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!dev->is_prepared)
+ {
+ DBG (3, "usb_high_scan_get_rows: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ while (rows > 0)
+ {
+ RIE ((*dev->get_line) (dev, block, is_order_invert));
+ block += dev->bytes_per_row;
+ rows--;
+ }
+ DBG (5, "usb_high_scan_get_rows: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_stop_scan (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_stop_scan: start\n");
+ if (!dev->is_open)
+ {
+ DBG (3, "usb_high_scan_stop_scan: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!dev->is_prepared)
+ {
+ DBG (3, "usb_high_scan_stop_scan: !is_prepared\n");
+ return SANE_STATUS_INVAL;
+ }
+ switch (dev->scan_mode)
+ {
+ case RGB24EXT:
+ RIE (usb_high_cal_exit (dev->blue_calibrator));
+ if (dev->blue_calibrator)
+ free (dev->blue_calibrator);
+ dev->blue_calibrator = NULL;
+ RIE (usb_high_cal_exit (dev->green_calibrator));
+ if (dev->green_calibrator)
+ free (dev->green_calibrator);
+ dev->green_calibrator = NULL;
+ RIE (usb_high_cal_exit (dev->red_calibrator));
+ if (dev->red_calibrator)
+ free (dev->red_calibrator);
+ dev->red_calibrator = NULL;
+ break;
+ case GRAY8EXT:
+ RIE (usb_high_cal_exit (dev->mono_calibrator));
+ if (dev->mono_calibrator)
+ free (dev->mono_calibrator);
+ dev->mono_calibrator = NULL;
+ break;
+ default:
+ break;
+ }
+
+ RIE (usb_low_stop_rowing (dev->chip));
+ if (!dev->is_cis_detected)
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE));
+
+ DBG (5, "usb_high_scan_stop_scan: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_init_asic (Mustek_Usb_Device * dev, Sensor_Type sensor)
+{
+ SANE_Byte ccd_dpi = 0;
+ SANE_Byte select = 0;
+ SANE_Byte adjust = 0;
+ SANE_Byte pin = 0;
+ SANE_Byte motor = 0;
+ SANE_Bool fix_pattern = SANE_FALSE;
+ SANE_Byte ad_timing = 0;
+ Banksize bank_size;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_init_asic: start\n");
+ switch (sensor)
+ {
+ case ST_TOSHIBA600:
+ ccd_dpi = 32;
+ select = 240;
+ adjust = 0;
+ pin = 18;
+ motor = 0;
+ fix_pattern = SANE_FALSE;
+ ad_timing = 0;
+ bank_size = BS_16K;
+ DBG (5, "usb_high_scan_init_asic: sensor is set to TOSHIBA600\n");
+ break;
+ case ST_CANON300:
+ ccd_dpi = 232;
+ select = 232;
+ adjust = 0;
+ pin = 18;
+ motor = 0;
+ fix_pattern = SANE_FALSE;
+ ad_timing = 1;
+ bank_size = BS_4K;
+ DBG (5, "usb_high_scan_init_asic: sensor is set to CANON300\n");
+ break;
+ case ST_CANON300600:
+ ccd_dpi = 232;
+ select = 232;
+ adjust = 64;
+ pin = 18;
+ motor = 0;
+ fix_pattern = SANE_FALSE;
+ ad_timing = 1;
+ bank_size = BS_16K;
+ DBG (5, "usb_high_scan_init_asic: sensor is set to CANON300600\n");
+ break;
+ case ST_CANON600:
+ ccd_dpi = 232;
+ select = 232;
+ adjust = 64;
+ pin = 18;
+ motor = 0;
+ fix_pattern = SANE_FALSE;
+ ad_timing = 1;
+ bank_size = BS_16K;
+ DBG (5, "usb_high_scan_init_asic: sensor is set to CANON600\n");
+ break;
+ case ST_NEC600: /* fixme */
+ ccd_dpi = 32;
+ select = 224;
+ adjust = 112;
+ pin = 18;
+ motor = 0;
+ fix_pattern = SANE_FALSE;
+ ad_timing = 0;
+ bank_size = BS_16K;
+ DBG (5, "usb_high_scan_init_asic: sensor is set to NEC600\n");
+ break;
+ default:
+ DBG (5, "usb_high_scan_init_asic: unknown sensor\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ RIE (usb_low_adjust_timing (dev->chip, adjust));
+ RIE (usb_low_select_timing (dev->chip, select));
+ RIE (usb_low_set_timing (dev->chip, ccd_dpi));
+ RIE (usb_low_set_sram_bank (dev->chip, bank_size));
+ RIE (usb_low_set_asic_io_pins (dev->chip, pin));
+ RIE (usb_low_set_rgb_sel_pins (dev->chip, pin));
+ RIE (usb_low_set_motor_signal (dev->chip, motor));
+ RIE (usb_low_set_test_sram_mode (dev->chip, SANE_FALSE));
+ RIE (usb_low_set_fix_pattern (dev->chip, fix_pattern));
+ RIE (usb_low_set_ad_timing (dev->chip, ad_timing));
+
+ DBG (5, "usb_high_scan_init_asic: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_evaluate_max_level (Mustek_Usb_Device * dev,
+ SANE_Word sample_lines,
+ SANE_Int sample_length,
+ SANE_Byte * ret_max_level)
+{
+ SANE_Byte max_level = 0;
+ SANE_Word i;
+ SANE_Int j;
+ SANE_Status status;
+ SANE_Word lines_left;
+
+ DBG (5, "usb_high_scan_evaluate_max_level: start\n");
+
+ sample_length -= 20;
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < sample_lines; i++)
+ {
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ for (j = 20; j < sample_length; j++)
+ {
+ if (max_level < dev->green[j])
+ max_level = dev->green[j];
+ }
+ }
+ RIE (usb_low_stop_rowing (dev->chip));
+ if (ret_max_level)
+ *ret_max_level = max_level;
+ DBG (5, "usb_high_scan_evaluate_max_level: exit, max_level = %d\n",
+ max_level);
+ return SANE_STATUS_GOOD;
+}
+
+/* Binary Search for Single Channel Power Delay */
+SANE_Status
+usb_high_scan_bssc_power_delay (Mustek_Usb_Device * dev,
+ Powerdelay_Function set_power_delay,
+ Signal_State * signal_state,
+ SANE_Byte * target, SANE_Byte max,
+ SANE_Byte min, SANE_Byte threshold,
+ SANE_Int length)
+{
+ SANE_Byte max_level;
+ SANE_Byte max_max = max;
+ SANE_Byte min_min = min;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_bssc_power_delay: start\n");
+
+ *target = (max + min) / 2;
+ RIE ((*set_power_delay) (dev->chip, *target));
+ while (*target != min)
+ {
+ RIE (usb_high_scan_evaluate_max_level (dev, dev->init_powerdelay_lines,
+ length, &max_level));
+ if (max_level > threshold)
+ {
+ min = *target;
+ *target = (max + min) / 2;
+ *signal_state = SS_BRIGHTER;
+ }
+ else if (max_level < threshold)
+ {
+ max = *target;
+ *target = (max + min) / 2;
+ *signal_state = SS_DARKER;
+ }
+ else if (max_level == threshold)
+ { /* Found. */
+ *signal_state = SS_EQUAL;
+ return SANE_STATUS_GOOD;
+ }
+ RIE ((*set_power_delay) (dev->chip, *target));
+ }
+ /* Fail... */
+ if (max == max_max || min == min_min)
+ { /* Boundary check */
+ if (max == max_max) /*target on max side */
+ *target = max_max;
+ else
+ *target = min_min;
+ RIE ((*set_power_delay) (dev->chip, *target));
+ RIE (usb_high_scan_evaluate_max_level (dev, dev->init_powerdelay_lines,
+ length, &max_level));
+
+ if (max_level > threshold)
+ {
+ *signal_state = SS_BRIGHTER;
+ }
+ else if (max_level < threshold)
+ {
+ *signal_state = SS_DARKER;
+ }
+ else if (max_level == threshold)
+ {
+ *signal_state = SS_EQUAL;
+ }
+ }
+ else
+ { /* Fail... will always on mimnum side, make it darker */
+ target++;
+ *signal_state = SS_DARKER;
+ }
+ DBG (5, "usb_high_scan_bssc_power_delay: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_adjust_rgb_600_power_delay (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+ SANE_Byte max_power_delay;
+ Signal_State signal_state = SS_UNKNOWN;
+
+ DBG (5, "usb_high_scan_adjust_rgb_600_power_delay: start\n");
+ max_power_delay = (SANE_Byte) (dev->expose_time / 64);
+
+ if (dev->is_adjusted_rgb_600_power_delay)
+ return SANE_STATUS_GOOD;
+ /* Setup Initial State */
+ dev->red_rgb_600_power_delay = max_power_delay;
+ dev->green_rgb_600_power_delay = max_power_delay;
+ dev->blue_rgb_600_power_delay = max_power_delay;
+
+ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_600));
+ RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_600));
+ RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT));
+
+ /* adjust GreenPD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600));
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_600_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_green_pd, &signal_state,
+ &dev->green_rgb_600_power_delay,
+ max_power_delay, 0, dev->init_max_power_delay,
+ dev->adjust_length_600));
+
+ /* adjust BluePD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_BLUE));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600));
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_600_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_blue_pd, &signal_state,
+ &dev->blue_rgb_600_power_delay,
+ max_power_delay, 0, dev->init_max_power_delay,
+ dev->adjust_length_600));
+
+ /* adjust RedPD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_RED));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600));
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_600_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_red_pd, &signal_state,
+ &dev->red_rgb_600_power_delay, max_power_delay, 0,
+ dev->init_max_power_delay, dev->adjust_length_600));
+
+ dev->is_adjusted_rgb_600_power_delay = SANE_TRUE;
+ DBG (5, "usb_high_scan_adjust_rgb_600_power_delay: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_adjust_mono_600_power_delay (Mustek_Usb_Device * dev)
+{
+ SANE_Byte max_power_delay;
+ Signal_State signal_state = SS_UNKNOWN;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_adjust_mono_600_power_delay: start\n");
+ max_power_delay = (SANE_Byte) (dev->expose_time / 64);
+ if (dev->is_adjusted_mono_600_power_delay)
+ return SANE_STATUS_GOOD;
+ /* Setup Initial State */
+ dev->red_mono_600_power_delay = max_power_delay;
+ dev->green_mono_600_power_delay = max_power_delay;
+ dev->blue_mono_600_power_delay = max_power_delay;
+
+ /* Compute Gray PD */
+ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_600));
+ RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_600));
+ RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT));
+
+ /* adjust GreenGrayPD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600));
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_600_pga));
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_600_pga));
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_600_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_green_pd, &signal_state,
+ &dev->green_mono_600_power_delay,
+ max_power_delay, 0, dev->init_max_power_delay,
+ dev->adjust_length_600));
+
+ dev->is_adjusted_mono_600_power_delay = SANE_TRUE;
+ DBG (5, "usb_high_scan_adjust_mono_600_power_delay: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* CCD */
+SANE_Status
+usb_high_scan_adjust_mono_600_exposure (Mustek_Usb_Device * dev)
+{
+ SANE_Word transfer_time;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_adjust_mono_600_exposure: start\n");
+ if (dev->is_adjusted_mono_600_exposure)
+ return SANE_STATUS_GOOD;
+
+ RIE (usb_high_scan_evaluate_pixel_rate (dev));
+ transfer_time = dev->pixel_rate * dev->x_dpi / 600;
+ if (transfer_time > 16000)
+ transfer_time = 16000;
+
+ dev->mono_600_exposure =
+ MAX (5504, MAX (transfer_time,
+ usb_mid_motor_mono_capability (dev->chip, dev->y_dpi)));
+ dev->mono_600_exposure = ((dev->mono_600_exposure + 63) / 64) * 64;
+ dev->is_adjusted_mono_600_exposure = SANE_TRUE;
+ DBG (5, "usb_high_scan_adjust_mono_600_exposure: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+#if 0
+/* CCD */
+SANE_Status
+usb_high_scan_adjust_mono_600_offset (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_adjust_mono_600_offset: start\n");
+ if (dev->is_adjusted_mono_600_offset)
+ return SANE_STATUS_GOOD;
+
+ DBG (5, "usb_high_scan_adjust_mono_600_offset: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* CCD */
+SANE_Status
+usb_high_scan_adjust_mono_600_pga (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_adjust_mono_600_pga: start (dev = %p)\n", dev);
+ DBG (5, "usb_high_scan_adjust_mono_600_pga: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* CCD */
+SANE_Status
+usb_high_scan_adjust_mono_600_skips_per_row (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_adjust_mono_600_skips_per_row: start (dev = %p)\n",
+ dev);
+ DBG (5, "usb_high_scan_adjust_mono_600_skips_per_row: exit\n");
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+SANE_Status
+usb_high_scan_adjust_rgb_300_power_delay (Mustek_Usb_Device * dev)
+{
+ /* Setup Initial State */
+ SANE_Byte max_power_delay;
+ Signal_State signal_state = SS_UNKNOWN;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_adjust_rgb_300_power_delay: start\n");
+ max_power_delay = (SANE_Byte) (dev->expose_time / 64);
+ if (dev->is_adjusted_rgb_300_power_delay)
+ return SANE_STATUS_GOOD;
+
+ dev->red_rgb_300_power_delay = max_power_delay;
+ dev->green_rgb_300_power_delay = max_power_delay;
+ dev->blue_rgb_300_power_delay = max_power_delay;
+
+ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_300));
+ RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_300));
+ RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT));
+
+ /* adjust GreenPD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300));
+
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_300_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_green_pd, &signal_state,
+ &dev->green_rgb_300_power_delay,
+ max_power_delay, 0, dev->init_max_power_delay,
+ dev->adjust_length_300));
+
+ /* adjust BluePD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_BLUE));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300));
+
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_300_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_blue_pd, &signal_state,
+ &dev->blue_rgb_300_power_delay, max_power_delay, 0,
+ dev->init_max_power_delay, dev->adjust_length_300));
+
+ /* adjust RedPD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_RED));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300));
+
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_300_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_red_pd, &signal_state,
+ &dev->red_rgb_300_power_delay, max_power_delay, 0,
+ dev->init_max_power_delay, dev->adjust_length_300));
+ dev->is_adjusted_rgb_300_power_delay = SANE_TRUE;
+ DBG (5, "usb_high_scan_adjust_rgb_300_power_delay: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_adjust_mono_300_power_delay (Mustek_Usb_Device * dev)
+{
+ SANE_Byte max_power_delay;
+ Signal_State signal_state = SS_UNKNOWN;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_adjust_mono_300_power_delay: start\n");
+ max_power_delay = (SANE_Byte) (dev->expose_time / 64);
+ if (dev->is_adjusted_mono_300_power_delay)
+ return SANE_STATUS_GOOD;
+ /* Setup Initial State */
+ dev->red_mono_300_power_delay = max_power_delay;
+ dev->green_mono_300_power_delay = max_power_delay;
+ dev->blue_mono_300_power_delay = max_power_delay;
+
+ /* Compute Gray PD */
+ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_300));
+ RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_300));
+ RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT));
+
+ /* adjust GreenGrayPD */
+ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN));
+ RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300));
+
+ signal_state = SS_UNKNOWN;
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_300_pga));
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_300_pga));
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_300_pga));
+ RIE (usb_high_scan_bssc_power_delay
+ (dev, &usb_low_set_green_pd, &signal_state,
+ &dev->green_mono_300_power_delay,
+ max_power_delay, 0, dev->init_max_power_delay,
+ dev->adjust_length_300));
+
+ dev->is_adjusted_mono_300_power_delay = SANE_TRUE;
+ DBG (5, "usb_high_scan_adjust_mono_300_power_delay: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_evaluate_pixel_rate (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_evaluate_pixel_rate: start, dev=%p\n", (void *) dev);
+
+ /* fixme: new for CCD */
+ dev->pixel_rate = 2000;
+ dev->is_evaluate_pixel_rate = SANE_TRUE;
+ DBG (5, "usb_high_scan_evaluate_pixel_rate: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_calibration_rgb_24 (Mustek_Usb_Device * dev)
+{
+ SANE_Word white_need;
+ SANE_Word dark_need;
+ SANE_Word i;
+ SANE_Status status;
+ SANE_Word lines_left;
+ SANE_Word minor_average;
+
+ DBG (5, "usb_high_scan_calibration_rgb_24: start, dev=%p\n", (void *) dev);
+ if (dev->is_cis_detected)
+ {
+ RIE (usb_mid_motor_prepare_calibrate_rgb (dev->chip, dev->y_dpi));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+ minor_average = 2;
+ }
+ else
+ {
+ minor_average = 1;
+ }
+
+ dev->red_calibrator = (Calibrator *) malloc (sizeof (Calibrator));
+ if (!dev->red_calibrator)
+ return SANE_STATUS_NO_MEM;
+
+ RIE (usb_high_cal_init (dev->red_calibrator, I8O8RGB,
+ dev->init_k_level << 8, 0));
+ RIE (usb_high_cal_prepare (dev->red_calibrator, dev->width));
+ RIE (usb_high_cal_embed_gamma (dev->red_calibrator, dev->gamma_table));
+ RIE (usb_high_cal_setup
+ (dev->red_calibrator, 1, minor_average, 8, dev->width, &white_need,
+ &dark_need));
+
+ dev->green_calibrator = (Calibrator *) malloc (sizeof (Calibrator));
+ if (!dev->green_calibrator)
+ return SANE_STATUS_NO_MEM;
+ RIE (usb_high_cal_init (dev->green_calibrator, I8O8RGB,
+ dev->init_k_level << 8, 0));
+ RIE (usb_high_cal_prepare (dev->green_calibrator, dev->width));
+ RIE (usb_high_cal_embed_gamma (dev->green_calibrator, dev->gamma_table));
+ RIE (usb_high_cal_setup (dev->green_calibrator, 1, minor_average, 8,
+ dev->width, &white_need, &dark_need));
+
+ dev->blue_calibrator = (Calibrator *) malloc (sizeof (Calibrator));
+ if (!dev->blue_calibrator)
+ return SANE_STATUS_NO_MEM;
+
+ RIE (usb_high_cal_init (dev->blue_calibrator, I8O8RGB,
+ dev->init_k_level << 8, 0));
+ RIE (usb_high_cal_prepare (dev->blue_calibrator, dev->width));
+ RIE (usb_high_cal_embed_gamma (dev->blue_calibrator, dev->gamma_table));
+ RIE (usb_high_cal_setup (dev->blue_calibrator, 1, minor_average, 8,
+ dev->width, &white_need, &dark_need));
+
+ /* K White */
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < white_need; i++)
+ {
+ /* Read Green Channel */
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->green_calibrator, i, 0,
+ (void *) (dev->green +
+ dev->skips_per_row)));
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->green_calibrator, i, 1,
+ (void *) (dev->green +
+ dev->skips_per_row)));
+ /* Read Blue Channel */
+ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->blue_calibrator, i, 0,
+ (void *) (dev->blue +
+ dev->skips_per_row)));
+ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->blue_calibrator, i, 1,
+ (void *) (dev->blue +
+ dev->skips_per_row)));
+ /* Read Red Channel */
+ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->red_calibrator, i, 0,
+ (void *) (dev->red +
+ dev->skips_per_row)));
+ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->red_calibrator, i, 1,
+ (void *) (dev->red +
+ dev->skips_per_row)));
+ }
+ RIE (usb_low_stop_rowing (dev->chip));
+ /* Caculate average */
+ RIE (usb_high_cal_evaluate_white (dev->green_calibrator,
+ dev->init_green_factor));
+ RIE (usb_high_cal_evaluate_white (dev->blue_calibrator,
+ dev->init_blue_factor));
+ RIE (usb_high_cal_evaluate_white (dev->red_calibrator,
+ dev->init_red_factor));
+
+ RIE (usb_mid_motor_prepare_calibrate_rgb (dev->chip, dev->y_dpi));
+ RIE (usb_low_enable_motor (dev->chip, SANE_FALSE));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE));
+
+ /* K Black */
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < dark_need; i++)
+ {
+ /* Read Green Channel */
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->green_calibrator, i, 0,
+ (void *) (dev->green +
+ dev->skips_per_row)));
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->green_calibrator, i, 1,
+ (void *) (dev->green +
+ dev->skips_per_row)));
+ /* Read Blue Channel */
+ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->blue_calibrator, i, 0,
+ (void *) (dev->blue +
+ dev->skips_per_row)));
+ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->blue_calibrator, i, 1,
+ (void *) (dev->blue +
+ dev->skips_per_row)));
+ /* Read Red Channel */
+ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->red_calibrator, i, 0,
+ (void *) (dev->red +
+ dev->skips_per_row)));
+ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->red_calibrator, i, 1,
+ (void *) (dev->red +
+ dev->skips_per_row)));
+ }
+ RIE (usb_low_stop_rowing (dev->chip));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+ /* Calculate average */
+ RIE (usb_high_cal_evaluate_dark (dev->green_calibrator,
+ dev->init_green_black_factor));
+ RIE (usb_high_cal_evaluate_dark (dev->blue_calibrator,
+ dev->init_blue_black_factor));
+ RIE (usb_high_cal_evaluate_dark (dev->red_calibrator,
+ dev->init_red_black_factor));
+ /* Calculate Mapping */
+ RIE (usb_high_cal_evaluate_calibrator (dev->green_calibrator));
+ RIE (usb_high_cal_evaluate_calibrator (dev->blue_calibrator));
+ RIE (usb_high_cal_evaluate_calibrator (dev->red_calibrator));
+ DBG (5, "usb_high_scan_calibration_rgb_24: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_calibration_mono_8 (Mustek_Usb_Device * dev)
+{
+ SANE_Word white_need;
+ SANE_Word dark_need;
+ SANE_Word i;
+ SANE_Status status;
+ SANE_Word lines_left;
+
+ DBG (5, "usb_high_scan_calibration_mono_8: start\n");
+ RIE (usb_mid_motor_prepare_calibrate_mono (dev->chip, dev->y_dpi));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+
+ dev->mono_calibrator = (Calibrator *) malloc (sizeof (Calibrator));
+ if (!dev->mono_calibrator)
+ return SANE_STATUS_NO_MEM;
+
+ RIE (usb_high_cal_init (dev->mono_calibrator, I8O8MONO,
+ dev->init_k_level << 8, 0));
+ RIE (usb_high_cal_prepare (dev->mono_calibrator, dev->width));
+ RIE (usb_high_cal_embed_gamma (dev->mono_calibrator, dev->gamma_table));
+ RIE (usb_high_cal_setup (dev->mono_calibrator, 1, 1, 8,
+ dev->width, &white_need, &dark_need));
+
+ /* K White */
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < white_need; i++)
+ {
+ /* Read Green Channel */
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_fill_in_white (dev->mono_calibrator, i, 0,
+ (void *) (dev->green +
+ dev->skips_per_row)));
+ }
+ RIE (usb_low_stop_rowing (dev->chip));
+ /* Caculate average */
+ RIE (usb_high_cal_evaluate_white (dev->mono_calibrator,
+ dev->init_gray_factor));
+
+ RIE (usb_mid_motor_prepare_calibrate_mono (dev->chip, dev->y_dpi));
+ RIE (usb_low_enable_motor (dev->chip, SANE_FALSE));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE));
+
+ /* K Black */
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < dark_need; i++)
+ {
+ /* Read Green Channel */
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_fill_in_dark (dev->mono_calibrator, i, 0,
+ (void *) (dev->green +
+ dev->skips_per_row)));
+ }
+ RIE (usb_low_stop_rowing (dev->chip));
+ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE));
+ /* Caculate Green Black */
+ RIE (usb_high_cal_evaluate_dark (dev->mono_calibrator,
+ dev->init_gray_black_factor));
+ /* Caculate Mapping */
+ RIE (usb_high_cal_evaluate_calibrator (dev->mono_calibrator));
+ DBG (5, "usb_high_scan_calibration_mono_8: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_step_forward (Mustek_Usb_Device * dev, SANE_Int step_count)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_step_forward: start\n");
+
+ if (step_count <= 0)
+ return SANE_STATUS_INVAL;
+ /* Initialize */
+ RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time));
+ RIE (usb_low_set_motor_direction (dev->chip, SANE_FALSE));
+ RIE (usb_mid_motor_prepare_step (dev->chip, (SANE_Word) step_count));
+ /* Startup */
+ RIE (usb_low_start_rowing (dev->chip));
+ /* Wait for stop */
+ /* Linux USB seems buggy on timeout... sleep before really try */
+ /* to read the flag from scanner */
+ usleep (step_count * 2 * 1000);
+ RIE (usb_low_wait_rowing_stop (dev->chip));
+ if (!dev->is_cis_detected)
+ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time));
+
+ DBG (5, "usb_high_scan_step_forward: start\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_safe_forward (Mustek_Usb_Device * dev, SANE_Int step_count)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_safe_forward: start\n");
+ if (step_count <= 0)
+ return SANE_STATUS_INVAL;
+ /* Initialize */
+ RIE (usb_low_set_ccd_width (dev->chip, 5400));
+ RIE (usb_low_set_motor_direction (dev->chip, SANE_FALSE));
+ RIE (usb_mid_motor_prepare_step (dev->chip, (SANE_Word) step_count));
+ /* Startup */
+ RIE (usb_low_start_rowing (dev->chip));
+ /* Wait to Stop */
+ RIE (usb_low_wait_rowing_stop (dev->chip));
+ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time));
+ DBG (5, "usb_high_scan_safe_forward: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Word
+usb_high_scan_calculate_max_rgb_600_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd)
+{
+ SANE_Word red_light_up;
+ SANE_Word green_light_up;
+ SANE_Word blue_light_up;
+ SANE_Word max_light_up;
+ SANE_Word ideal_expose_time;
+
+ DBG (5, "usb_high_scan_calculate_max_rgb_600_expose: dev=%p\n", (void *) dev);
+
+ red_light_up = dev->expose_time - dev->red_rgb_600_power_delay * 64;
+ green_light_up = dev->expose_time - dev->green_rgb_600_power_delay * 64;
+ blue_light_up = dev->expose_time - dev->blue_rgb_600_power_delay * 64;
+ max_light_up = MAX (red_light_up, MAX (green_light_up, blue_light_up));
+ if (dev->chip->sensor == ST_NEC600)
+ {
+ ideal_expose_time
+ = MAX (MAX (5504, max_light_up),
+ usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi));
+ }
+ else
+ {
+ ideal_expose_time
+ = MAX (MAX (5376, max_light_up),
+ usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi));
+ }
+ ideal_expose_time = (ideal_expose_time + 63) / 64 * 64;
+ *ideal_red_pd = (SANE_Byte) ((ideal_expose_time - red_light_up) / 64);
+ *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - green_light_up) / 64);
+ *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time - blue_light_up) / 64);
+ DBG (5, "usb_high_scan_calculate_max_rgb_600_expose: exit\n");
+ return ideal_expose_time;
+}
+
+SANE_Word
+usb_high_scan_calculate_max_mono_600_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd)
+{
+ SANE_Word max_light_up;
+ SANE_Word ideal_expose_time;
+ SANE_Word transfer_time;
+
+ DBG (5, "usb_high_scan_calculate_max_mono_600_expose: dev=%p\n", (void *) dev);
+
+ max_light_up = dev->expose_time - dev->green_mono_600_power_delay * 64;
+ transfer_time = (SANE_Word) ((SANE_Word) (dev->pixel_rate)
+ * (SANE_Word) (dev->x_dpi) / 600);
+ /* base on 600, but double it. */
+
+ if (transfer_time > 16000)
+ transfer_time = 16000;
+ if (dev->chip->sensor == ST_NEC600)
+ {
+ ideal_expose_time
+ = MAX (MAX (5504, max_light_up),
+ MAX (transfer_time, usb_mid_motor_mono_capability
+ (dev->chip, dev->y_dpi)));
+ }
+ else
+ {
+ ideal_expose_time
+ = MAX (MAX (5376, max_light_up),
+ MAX (transfer_time, usb_mid_motor_mono_capability
+ (dev->chip, dev->y_dpi)));
+ }
+ ideal_expose_time = (ideal_expose_time + 63) / 64 * 64;
+ *ideal_red_pd = (SANE_Byte) ((ideal_expose_time) / 64);
+ *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - max_light_up) / 64);
+ *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time) / 64);
+ DBG (5, "usb_high_scan_calculate_max_mono_600_expose: exit\n");
+ return ideal_expose_time;
+}
+
+SANE_Word
+usb_high_scan_calculate_max_rgb_300_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd)
+{
+ SANE_Word red_light_up;
+ SANE_Word green_light_up;
+ SANE_Word blue_light_up;
+ SANE_Word max_light_up;
+ SANE_Word ideal_expose_time;
+
+ DBG (5, "usb_high_scan_calculate_max_rgb_300_expose: start\n");
+ red_light_up = dev->expose_time - dev->red_rgb_300_power_delay * 64;
+ green_light_up = dev->expose_time - dev->green_rgb_300_power_delay * 64;
+ blue_light_up = dev->expose_time - dev->blue_rgb_300_power_delay * 64;
+ max_light_up = MAX (red_light_up, MAX (green_light_up, blue_light_up));
+
+ if (dev->chip->sensor == ST_CANON300600)
+ {
+ ideal_expose_time =
+ MAX (MAX (2624, max_light_up),
+ usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi));
+ }
+ else if (dev->chip->sensor == ST_CANON300)
+ {
+ ideal_expose_time = MAX (MAX (2624, max_light_up), /* fixme? */
+ usb_mid_motor_rgb_capability (dev->chip,
+ dev->y_dpi));
+ }
+ else
+ {
+ ideal_expose_time =
+ MAX (MAX (5376, max_light_up),
+ usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi));
+ }
+
+ ideal_expose_time = (ideal_expose_time + 63) / 64 * 64;
+ *ideal_red_pd = (SANE_Byte) ((ideal_expose_time - red_light_up) / 64);
+ *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - green_light_up) / 64);
+ *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time - blue_light_up) / 64);
+ DBG (5, "usb_high_scan_calculate_max_rgb_300_expose: exit\n");
+ return ideal_expose_time;
+}
+
+SANE_Word
+usb_high_scan_calculate_max_mono_300_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd)
+{
+ SANE_Word max_light_up;
+ SANE_Word transfer_time;
+ SANE_Word ideal_expose_time;
+
+ DBG (5, "usb_high_scan_calculate_max_mono_300_expose: start\n");
+ max_light_up = dev->expose_time - dev->green_mono_300_power_delay * 64;
+ transfer_time =
+ (SANE_Word) ((SANE_Word) (dev->pixel_rate) *
+ (SANE_Word) (dev->x_dpi) / 600);
+ /* base on 600, but double it. */
+
+ if (transfer_time > 16000)
+ transfer_time = 16000;
+ if (dev->chip->sensor == ST_CANON300600)
+ {
+ ideal_expose_time =
+ MAX (MAX (2688, max_light_up),
+ MAX (transfer_time,
+ usb_mid_motor_mono_capability (dev->chip, dev->y_dpi)));
+ }
+ else if (dev->chip->sensor == ST_CANON300)
+ {
+ ideal_expose_time = MAX (MAX (2688, max_light_up), /* fixme? */
+ MAX (transfer_time,
+ usb_mid_motor_mono_capability (dev->chip,
+ dev->
+ y_dpi)));
+ }
+ else
+ {
+ ideal_expose_time =
+ MAX (MAX (5376, max_light_up),
+ MAX (transfer_time,
+ usb_mid_motor_mono_capability (dev->chip, dev->y_dpi)));
+ }
+
+ ideal_expose_time = (ideal_expose_time + 63) / 64 * 64;
+ *ideal_red_pd = (SANE_Byte) ((ideal_expose_time) / 64);
+ *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - max_light_up) / 64);
+ *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time) / 64);
+ DBG (5, "usb_high_scan_calculate_max_mono_300_expose: exit\n");
+ return ideal_expose_time;
+}
+
+SANE_Status
+usb_high_scan_prepare_rgb_signal_600_dpi (Mustek_Usb_Device * dev)
+{
+ SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd;
+ SANE_Word ideal_expose_time;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_rgb_signal_600_dpi: start\n");
+ ideal_expose_time = usb_high_scan_calculate_max_rgb_600_expose
+ (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd);
+
+ RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_600_pga));
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_600_pga));
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_600_pga));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd));
+ RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd));
+ RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd));
+ DBG (5, "usb_high_scan_prepare_rgb_signal_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare_mono_signal_600_dpi (Mustek_Usb_Device * dev)
+{
+ SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd;
+ SANE_Word ideal_expose_time;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_mono_signal_600_dpi: start\n");
+ ideal_expose_time = usb_high_scan_calculate_max_mono_600_expose
+ (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd);
+
+ RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_600_pga));
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_600_pga));
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_600_pga));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd));
+ RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd));
+ RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd));
+ DBG (5, "usb_high_scan_prepare_mono_signal_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare_rgb_signal_300_dpi (Mustek_Usb_Device * dev)
+{
+ SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd;
+ SANE_Word ideal_expose_time;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_rgb_signal_300_dpi: start\n");
+
+ ideal_expose_time = usb_high_scan_calculate_max_rgb_300_expose
+ (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd);
+
+ RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_300_pga));
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_300_pga));
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_300_pga));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd));
+ RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd));
+ RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd));
+ DBG (5, "usb_high_scan_prepare_rgb_signal_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare_mono_signal_300_dpi (Mustek_Usb_Device * dev)
+{
+ /* Setup Scan Here */
+ SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd;
+ SANE_Word ideal_expose_time;
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_mono_signal_300_dpi: start\n");
+ ideal_expose_time = usb_high_scan_calculate_max_mono_300_expose
+ (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd);
+
+ RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time));
+ RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end));
+ RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref));
+ RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset));
+ RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset));
+ RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset));
+ RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_300_pga));
+ RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_300_pga));
+ RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_300_pga));
+ RIE (usb_mid_front_set_rgb_signal (dev->chip));
+ RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd));
+ RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd));
+ RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd));
+ DBG (5, "usb_high_scan_prepare_mono_signal_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare_rgb_24 (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_rgb_24: start\n");
+ RIE (usb_low_set_image_byte_width (dev->chip, dev->bytes_per_strip));
+ RIE (usb_low_set_dummy (dev->chip, dev->dummy));
+ RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT));
+ DBG (5, "usb_high_scan_prepare_rgb_24: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_prepare_mono_8 (Mustek_Usb_Device * dev)
+{
+ SANE_Status status;
+
+ DBG (5, "usb_high_scan_prepare_mono_8: start\n");
+ RIE (usb_low_set_image_byte_width (dev->chip, dev->bytes_per_strip));
+ RIE (usb_low_set_dummy (dev->chip, dev->dummy));
+ RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT));
+ DBG (5, "usb_high_scan_prepare_mono_8: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_get_rgb_24_bit_line (Mustek_Usb_Device * dev, SANE_Byte * line,
+ SANE_Bool is_order_invert)
+{
+ SANE_Status status;
+ SANE_Word lines_left;
+
+ DBG (5, "usb_high_scan_get_rgb_24_bit_line: start, dev=%p, line=%p, "
+ "is_order_invert=%d\n", (void *) dev, line, is_order_invert);
+
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+
+ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left));
+
+ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left));
+ RIE (usb_high_cal_calibrate (dev->green_calibrator,
+ dev->green + dev->skips_per_row, line + 1));
+ RIE (usb_high_cal_calibrate (dev->blue_calibrator,
+ dev->blue + dev->skips_per_row,
+ line + ((is_order_invert) ? 0 : 2)));
+ RIE (usb_high_cal_calibrate (dev->red_calibrator,
+ dev->red + dev->skips_per_row,
+ line + ((is_order_invert) ? 2 : 0)));
+
+ DBG (5, "usb_high_scan_get_rgb_24_bit_line: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_high_scan_get_mono_8_bit_line (Mustek_Usb_Device * dev, SANE_Byte * line,
+ SANE_Bool is_order_invert)
+{
+ SANE_Status status;
+ SANE_Word lines_left;
+
+ DBG (5, "usb_high_scan_get_mono_8_bit_line: start, dev=%p, line=%p, "
+ "is_order_invert=%d\n", (void *) dev, line, is_order_invert);
+
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ RIE (usb_high_cal_calibrate (dev->mono_calibrator, dev->green +
+ dev->skips_per_row, line));
+ DBG (5, "usb_high_scan_get_mono_8_bit_line: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_backtrack_rgb_24 (Mustek_Usb_Device * dev)
+{
+ DBG (5, "usb_high_scan_backtrack_rgb_24: noop, dev=%p\n", (void *) dev);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_high_scan_backtrack_mono_8 (Mustek_Usb_Device * dev)
+{
+ SANE_Int i;
+ SANE_Status status;
+ SANE_Word lines_left;
+
+ DBG (5, "usb_high_scan_backtrack_mono_8: start, dev=%p\n", (void *) dev);
+
+ if (dev->y_dpi >= 300)
+ {
+ RIE (usb_low_stop_rowing (dev->chip));
+ RIE (usb_low_set_motor_direction (dev->chip, SANE_TRUE));
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < dev->init_mono_8_back_track; i++)
+ {
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ }
+ usleep (100 * 1000);
+ RIE (usb_low_stop_rowing (dev->chip));
+ RIE (usb_low_set_motor_direction (dev->chip, SANE_FALSE));
+ RIE (usb_low_start_rowing (dev->chip));
+ for (i = 0; i < dev->init_mono_8_back_track; i++)
+ {
+ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left));
+ }
+ }
+ DBG (5, "usb_high_scan_backtrack_mono_8: exit\n");
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/mustek_usb_high.h b/backend/mustek_usb_high.h
new file mode 100644
index 0000000..d3f1ae4
--- /dev/null
+++ b/backend/mustek_usb_high.h
@@ -0,0 +1,591 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001, 2002 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#ifndef mustek_usb_high_h
+#define mustek_usb_high_h
+#include "mustek_usb_mid.h"
+
+/* ---------------------------------- macros ------------------------------ */
+
+#define I8O8RGB 0
+#define I8O8MONO 1
+#define I4O1MONO 2
+
+/* ---------------------------------- types ------------------------------- */
+
+struct Mustek_Usb_Device;
+
+typedef SANE_Status (*Powerdelay_Function) (ma1017 *, SANE_Byte);
+
+typedef SANE_Status
+ (*Getline_Function) (struct Mustek_Usb_Device * dev, SANE_Byte *,
+ SANE_Bool is_order_invert);
+
+typedef SANE_Status (*Backtrack_Function) (struct Mustek_Usb_Device * dev);
+
+typedef enum Colormode
+{
+ RGB48 = 0,
+ RGB42 = 1,
+ RGB36 = 2,
+ RGB30 = 3,
+ RGB24 = 4,
+ GRAY16 = 5,
+ GRAY14 = 6,
+ GRAY12 = 7,
+ GRAY10 = 8,
+ GRAY8 = 9,
+ TEXT = 10,
+ RGB48EXT = 11,
+ RGB42EXT = 12,
+ RGB36EXT = 13,
+ RGB30EXT = 14,
+ RGB24EXT = 15,
+ GRAY16EXT = 16,
+ GRAY14EXT = 17,
+ GRAY12EXT = 18,
+ GRAY10EXT = 19,
+ GRAY8EXT = 20,
+ TEXTEXT = 21
+}
+Colormode;
+
+typedef enum Signal_State
+{
+ SS_UNKNOWN = 0,
+ SS_BRIGHTER = 1,
+ SS_DARKER = 2,
+ SS_EQUAL = 3
+}
+Signal_State;
+
+typedef struct Calibrator
+{
+ /* Calibration Data */
+ SANE_Bool is_prepared;
+ SANE_Word *k_white;
+ SANE_Word *k_dark;
+ /* Working Buffer */
+ double *white_line;
+ double *dark_line;
+ SANE_Int *white_buffer;
+ /* Necessary Parameters */
+ SANE_Word k_white_level;
+ SANE_Word k_dark_level;
+ SANE_Word major_average;
+ SANE_Word minor_average;
+ SANE_Word filter;
+ SANE_Word white_needed;
+ SANE_Word dark_needed;
+ SANE_Word max_width;
+ SANE_Word width;
+ SANE_Word threshold;
+ SANE_Word *gamma_table;
+ SANE_Byte calibrator_type;
+}
+Calibrator;
+
+enum Mustek_Usb_Modes
+{
+ MUSTEK_USB_MODE_LINEART = 0,
+ MUSTEK_USB_MODE_GRAY,
+ MUSTEK_USB_MODE_COLOR
+};
+
+enum Mustek_Usb_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+
+ OPT_GEOMETRY_GROUP, /* 5 */
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP, /* 10 */
+ OPT_THRESHOLD,
+ OPT_CUSTOM_GAMMA,
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+typedef struct Mustek_Usb_Device
+{
+ struct Mustek_Usb_Device *next;
+ SANE_String name;
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ /* max width & max height in 300 dpi */
+ SANE_Int max_width;
+ SANE_Int max_height;
+
+ ma1017 *chip; /* registers of the scanner controller chip */
+
+ Colormode scan_mode;
+ SANE_Word x_dpi;
+ SANE_Word y_dpi;
+ SANE_Word x;
+ SANE_Word y;
+ SANE_Word width;
+ SANE_Word height;
+ SANE_Word bytes_per_row;
+ SANE_Word bpp;
+
+ SANE_Byte *scan_buffer;
+ SANE_Byte *scan_buffer_start;
+ size_t scan_buffer_len;
+
+ SANE_Byte *temp_buffer;
+ SANE_Byte *temp_buffer_start;
+ size_t temp_buffer_len;
+
+ SANE_Word line_switch;
+ SANE_Word line_offset;
+
+ SANE_Bool is_cis_detected;
+
+ SANE_Word init_bytes_per_strip;
+ SANE_Word adjust_length_300;
+ SANE_Word adjust_length_600;
+ SANE_Word init_min_expose_time;
+ SANE_Word init_skips_per_row_300;
+ SANE_Word init_skips_per_row_600;
+ SANE_Word init_j_lines;
+ SANE_Word init_k_lines;
+ SANE_Word init_k_filter;
+ SANE_Int init_k_loops;
+ SANE_Word init_pixel_rate_lines;
+ SANE_Word init_pixel_rate_filts;
+ SANE_Word init_powerdelay_lines;
+ SANE_Word init_home_lines;
+ SANE_Word init_dark_lines;
+ SANE_Word init_k_level;
+ SANE_Byte init_max_power_delay;
+ SANE_Byte init_min_power_delay;
+ SANE_Byte init_adjust_way;
+ double init_green_black_factor;
+ double init_blue_black_factor;
+ double init_red_black_factor;
+ double init_gray_black_factor;
+ double init_green_factor;
+ double init_blue_factor;
+ double init_red_factor;
+ double init_gray_factor;
+
+ SANE_Int init_red_rgb_600_pga;
+ SANE_Int init_green_rgb_600_pga;
+ SANE_Int init_blue_rgb_600_pga;
+ SANE_Int init_mono_600_pga;
+ SANE_Int init_red_rgb_300_pga;
+ SANE_Int init_green_rgb_300_pga;
+ SANE_Int init_blue_rgb_300_pga;
+ SANE_Int init_mono_300_pga;
+ SANE_Word init_expose_time;
+ SANE_Byte init_red_rgb_600_power_delay;
+ SANE_Byte init_green_rgb_600_power_delay;
+ SANE_Byte init_blue_rgb_600_power_delay;
+ SANE_Byte init_red_mono_600_power_delay;
+ SANE_Byte init_green_mono_600_power_delay;
+ SANE_Byte init_blue_mono_600_power_delay;
+ SANE_Byte init_red_rgb_300_power_delay;
+ SANE_Byte init_green_rgb_300_power_delay;
+ SANE_Byte init_blue_rgb_300_power_delay;
+ SANE_Byte init_red_mono_300_power_delay;
+ SANE_Byte init_green_mono_300_power_delay;
+ SANE_Byte init_blue_mono_300_power_delay;
+ SANE_Byte init_threshold;
+
+ SANE_Byte init_top_ref;
+ SANE_Byte init_front_end;
+ SANE_Byte init_red_offset;
+ SANE_Byte init_green_offset;
+ SANE_Byte init_blue_offset;
+
+ SANE_Int init_rgb_24_back_track;
+ SANE_Int init_mono_8_back_track;
+
+ SANE_Bool is_open;
+ SANE_Bool is_prepared;
+ SANE_Word expose_time;
+ SANE_Word dummy;
+ SANE_Word bytes_per_strip;
+ SANE_Byte *image_buffer;
+ SANE_Byte *red;
+ SANE_Byte *green;
+ SANE_Byte *blue;
+ Getline_Function get_line;
+ Backtrack_Function backtrack;
+ SANE_Bool is_adjusted_rgb_600_power_delay;
+ SANE_Bool is_adjusted_mono_600_power_delay;
+ SANE_Bool is_adjusted_rgb_300_power_delay;
+ SANE_Bool is_adjusted_mono_300_power_delay;
+ SANE_Bool is_evaluate_pixel_rate;
+ SANE_Int red_rgb_600_pga;
+ SANE_Int green_rgb_600_pga;
+ SANE_Int blue_rgb_600_pga;
+ SANE_Int mono_600_pga;
+ SANE_Byte red_rgb_600_power_delay;
+ SANE_Byte green_rgb_600_power_delay;
+ SANE_Byte blue_rgb_600_power_delay;
+ SANE_Byte red_mono_600_power_delay;
+ SANE_Byte green_mono_600_power_delay;
+ SANE_Byte blue_mono_600_power_delay;
+ SANE_Int red_rgb_300_pga;
+ SANE_Int green_rgb_300_pga;
+ SANE_Int blue_rgb_300_pga;
+ SANE_Int mono_300_pga;
+ SANE_Byte red_rgb_300_power_delay;
+ SANE_Byte green_rgb_300_power_delay;
+ SANE_Byte blue_rgb_300_power_delay;
+ SANE_Byte red_mono_300_power_delay;
+ SANE_Byte green_mono_300_power_delay;
+ SANE_Byte blue_mono_300_power_delay;
+ SANE_Word pixel_rate;
+ SANE_Byte threshold;
+ SANE_Word *gamma_table;
+ SANE_Word skips_per_row;
+
+ /* CCD */
+ SANE_Bool is_adjusted_mono_600_offset;
+ SANE_Bool is_adjusted_mono_600_exposure;
+ SANE_Word mono_600_exposure;
+
+ Calibrator *red_calibrator;
+ Calibrator *green_calibrator;
+ Calibrator *blue_calibrator;
+ Calibrator *mono_calibrator;
+
+ SANE_Char device_name[256];
+
+ SANE_Bool is_sensor_detected;
+}
+Mustek_Usb_Device;
+
+typedef struct Mustek_Usb_Scanner
+{
+ /* all the state needed to define a scan request: */
+ struct Mustek_Usb_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Int channels;
+
+ /* scan window in inches: top left x+y and width+height */
+ double tl_x;
+ double tl_y;
+ double width;
+ double height;
+ /* scan window in dots (at current resolution):
+ top left x+y and width+height */
+ SANE_Int tl_x_dots;
+ SANE_Int tl_y_dots;
+ SANE_Int width_dots;
+ SANE_Int height_dots;
+
+ SANE_Word bpp;
+
+ SANE_Bool scanning;
+ SANE_Parameters params;
+ SANE_Word read_rows;
+ SANE_Word red_gamma_table[256];
+ SANE_Word green_gamma_table[256];
+ SANE_Word blue_gamma_table[256];
+ SANE_Word gray_gamma_table[256];
+ SANE_Word linear_gamma_table[256];
+ SANE_Word *red_table;
+ SANE_Word *green_table;
+ SANE_Word *blue_table;
+ SANE_Word *gray_table;
+ SANE_Word total_bytes;
+ SANE_Word total_lines;
+ /* scanner dependent/low-level state: */
+ Mustek_Usb_Device *hw;
+}
+Mustek_Usb_Scanner;
+
+
+/* ------------------- calibration function declarations ------------------ */
+
+
+static SANE_Status
+usb_high_cal_init (Calibrator * cal, SANE_Byte type, SANE_Word target_white,
+ SANE_Word target_dark);
+
+static SANE_Status usb_high_cal_exit (Calibrator * cal);
+
+static SANE_Status
+usb_high_cal_embed_gamma (Calibrator * cal, SANE_Word * gamma_table);
+
+static SANE_Status
+usb_high_cal_prepare (Calibrator * cal, SANE_Word max_width);
+
+static SANE_Status
+usb_high_cal_setup (Calibrator * cal, SANE_Word major_average,
+ SANE_Word minor_average, SANE_Word filter,
+ SANE_Word width, SANE_Word * white_needed,
+ SANE_Word * dark_needed);
+
+static SANE_Status
+usb_high_cal_evaluate_white (Calibrator * cal, double factor);
+
+static SANE_Status
+usb_high_cal_evaluate_dark (Calibrator * cal, double factor);
+
+static SANE_Status usb_high_cal_evaluate_calibrator (Calibrator * cal);
+
+static SANE_Status
+usb_high_cal_fill_in_white (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *white_pattern);
+
+static SANE_Status
+usb_high_cal_fill_in_dark (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *dark_pattern);
+
+static SANE_Status
+usb_high_cal_calibrate (Calibrator * cal, void *src, void *target);
+
+static SANE_Status
+usb_high_cal_i8o8_fill_in_white (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *white_pattern);
+
+static SANE_Status
+usb_high_cal_i8o8_fill_in_dark (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *dark_pattern);
+
+static SANE_Status
+usb_high_cal_i8o8_mono_calibrate (Calibrator * cal, void *src, void *target);
+
+static SANE_Status
+usb_high_cal_i8o8_rgb_calibrate (Calibrator * cal, void *src, void *target);
+
+static SANE_Status
+usb_high_cal_i4o1_fill_in_white (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *white_pattern);
+
+static SANE_Status
+usb_high_cal_i4o1_fill_in_dark (Calibrator * cal, SANE_Word major,
+ SANE_Word minor, void *dark_pattern);
+
+static SANE_Status
+usb_high_cal_i4o1_calibrate (Calibrator * cal, void *src, void *target);
+
+/* -------------------- scanning function declarations -------------------- */
+
+static SANE_Status usb_high_scan_init (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_exit (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_prepare (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_clearup (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_turn_power (Mustek_Usb_Device * dev, SANE_Bool is_on);
+
+static SANE_Status usb_high_scan_back_home (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_set_threshold (Mustek_Usb_Device * dev, SANE_Byte threshold);
+
+static SANE_Status
+usb_high_scan_embed_gamma (Mustek_Usb_Device * dev, SANE_Word * gamma_table);
+
+static SANE_Status usb_high_scan_reset (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_suggest_parameters (Mustek_Usb_Device * dev, SANE_Word dpi,
+ SANE_Word x, SANE_Word y, SANE_Word width,
+ SANE_Word height, Colormode color_mode);
+static SANE_Status usb_high_scan_detect_sensor (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_setup_scan (Mustek_Usb_Device * dev, Colormode color_mode,
+ SANE_Word x_dpi, SANE_Word y_dpi,
+ SANE_Bool is_invert, SANE_Word x, SANE_Word y,
+ SANE_Word width);
+
+static SANE_Status
+usb_high_scan_get_rows (Mustek_Usb_Device * dev, SANE_Byte * block,
+ SANE_Word rows, SANE_Bool is_order_invert);
+
+static SANE_Status usb_high_scan_stop_scan (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_step_forward (Mustek_Usb_Device * dev, SANE_Int step_count);
+
+static SANE_Status
+usb_high_scan_safe_forward (Mustek_Usb_Device * dev, SANE_Int step_count);
+
+static SANE_Status
+usb_high_scan_init_asic (Mustek_Usb_Device * dev, Sensor_Type sensor);
+
+static SANE_Status usb_high_scan_wait_carriage_home (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_hardware_calibration (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_line_calibration (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_prepare_scan (Mustek_Usb_Device * dev);
+
+static SANE_Word
+usb_high_scan_calculate_max_rgb_600_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd);
+
+static SANE_Word
+usb_high_scan_calculate_max_mono_600_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd);
+
+static SANE_Status
+usb_high_scan_prepare_rgb_signal_600_dpi (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_prepare_mono_signal_600_dpi (Mustek_Usb_Device * dev);
+
+static SANE_Word
+usb_high_scan_calculate_max_rgb_300_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd);
+
+static SANE_Word
+usb_high_scan_calculate_max_mono_300_expose (Mustek_Usb_Device * dev,
+ SANE_Byte * ideal_red_pd,
+ SANE_Byte * ideal_green_pd,
+ SANE_Byte * ideal_blue_pd);
+
+static SANE_Status
+usb_high_scan_prepare_rgb_signal_300_dpi (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_prepare_mono_signal_300_dpi (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_evaluate_max_level (Mustek_Usb_Device * dev,
+ SANE_Word sample_lines,
+ SANE_Int sample_length,
+ SANE_Byte * ret_max_level);
+
+static SANE_Status
+usb_high_scan_bssc_power_delay (Mustek_Usb_Device * dev,
+ Powerdelay_Function set_power_delay,
+ Signal_State * signal_state,
+ SANE_Byte * target, SANE_Byte max,
+ SANE_Byte min, SANE_Byte threshold,
+ SANE_Int length);
+
+static SANE_Status
+usb_high_scan_adjust_rgb_600_power_delay (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_adjust_mono_600_power_delay (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_adjust_mono_600_exposure (Mustek_Usb_Device * dev);
+
+#if 0
+/* CCD */
+static SANE_Status
+usb_high_scan_adjust_mono_600_offset (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_adjust_mono_600_pga (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_adjust_mono_600_skips_per_row (Mustek_Usb_Device * dev);
+#endif
+
+static SANE_Status
+usb_high_scan_adjust_rgb_300_power_delay (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_adjust_mono_300_power_delay (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_evaluate_pixel_rate (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_calibration_rgb_24 (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_calibration_mono_8 (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_prepare_rgb_24 (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_prepare_mono_8 (Mustek_Usb_Device * dev);
+
+static SANE_Status
+usb_high_scan_get_rgb_24_bit_line (Mustek_Usb_Device * dev,
+ SANE_Byte * line,
+ SANE_Bool is_order_invert);
+
+static SANE_Status
+usb_high_scan_get_mono_8_bit_line (Mustek_Usb_Device * dev,
+ SANE_Byte * line,
+ SANE_Bool is_order_invert);
+
+static SANE_Status usb_high_scan_backtrack_rgb_24 (Mustek_Usb_Device * dev);
+
+static SANE_Status usb_high_scan_backtrack_mono_8 (Mustek_Usb_Device * dev);
+
+#endif /* mustek_usb_high_h */
diff --git a/backend/mustek_usb_low.c b/backend/mustek_usb_low.c
new file mode 100644
index 0000000..e626b65
--- /dev/null
+++ b/backend/mustek_usb_low.c
@@ -0,0 +1,2914 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001 - 2004 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_usb.h"
+#include "mustek_usb_low.h"
+
+
+SANE_Status
+usb_low_init (ma1017 ** chip_address)
+{
+ SANE_Int i;
+ ma1017 *chip;
+
+ DBG (7, "usb_low_init: start\n");
+ if (!chip_address)
+ return SANE_STATUS_INVAL;
+
+ chip = (ma1017 *) malloc (sizeof (ma1017));
+
+ if (!chip)
+ {
+ DBG (3, "usb_low_init: couldn't malloc %ld bytes for chip\n",
+ (long int) sizeof (ma1017));
+ *chip_address = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ *chip_address = chip;
+
+ /* io */
+ chip->is_rowing = SANE_FALSE;
+ chip->is_opened = SANE_FALSE;
+ chip->fd = -1;
+
+ /* Construction/Destruction */
+ chip->is_opened = SANE_FALSE;
+ chip->is_rowing = SANE_FALSE;
+
+ /* A2 */
+ chip->append = 0x00;
+ chip->test_sram = 0x00;
+ chip->fix_pattern = 0x00;
+ /* A4 */
+ chip->select = 0x00;
+ chip->frontend = 0x00;
+ /* A6 */
+ chip->rgb_sel_pin = 0x02;
+ chip->asic_io_pins = 0x9c;
+ /* A7 */
+ chip->timing = 0xe8;
+ chip->sram_bank = 0x02;
+ /* A8 */
+ chip->dummy_msb = 0x00;
+ chip->ccd_width_msb = 0x00;
+ chip->cmt_table_length = 0x00;
+ /* A9 */
+ chip->cmt_second_pos = 0x00;
+ /* A10 + A8ID5 */
+ chip->ccd_width = 0x0c80;
+ /* A11 + A8ID6 */
+ chip->dummy = 0x0020;
+ /* A12 + A13 */
+ chip->byte_width = 0x09f6;
+ /* A14 + A30W */
+ chip->loop_count = 0x0db5;
+ /* A15 */
+ chip->motor_enable = 0x00;
+ chip->motor_movement = 0x60;
+ chip->motor_direction = 0x10;
+ chip->motor_signal = 0x00;
+ chip->motor_home = 0x00;
+ /* A16 */
+ chip->pixel_depth = 0x00;
+ chip->image_invert = 0x00;
+ chip->optical_600 = 0x00;
+ chip->sample_way = 0x06;
+ /* A17 + A18 + A19 */
+ chip->red_ref = 0xff;
+ chip->green_ref = 0xff;
+ chip->blue_ref = 0xff;
+ /* A20 + A21 + A22 */
+ chip->red_pd = 0x00;
+ chip->green_pd = 0x00;
+ chip->blue_pd = 0x00;
+ /* A23 */
+ chip->a23 = 0x80;
+ /* A24 */
+ chip->fy1_delay = 0x00;
+ chip->special_ad = 0x00;
+ /* A27 */
+ chip->sclk = 0x00;
+ chip->sen = 0x00;
+ chip->serial_length = 0x10;
+
+ /* Use for Rowing */
+ chip->get_row = NULL;
+
+ chip->cmt_table_length_word = 0x00000000;
+ chip->cmt_second_pos_word = 0x00000000;
+ chip->row_size = 0x00;
+ chip->soft_resample = 0x01;
+ chip->total_lines = 0x00;
+ chip->lines_left = 0x00;
+ for (i = 0; i < 32; i++)
+ chip->is_transfer_table[i] = SANE_FALSE;
+ chip->sensor = ST_CANON600;
+ chip->motor = MT_1200;
+
+ chip->total_read_urbs = 0;
+ chip->total_write_urbs = 0;
+ DBG (7, "usb_low_init: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_exit (ma1017 * chip)
+{
+ DBG (7, "usb_low_exit: chip = %p\n", (void *) chip);
+ if (chip)
+ {
+ if (chip->fd >= 0 && chip->is_opened)
+ usb_low_close (chip);
+ DBG (7, "usb_low_exit: freeing chip\n");
+ free (chip);
+ }
+ DBG (5, "usb_low_exit: read %d URBs, wrote %d URBs\n",
+ chip->total_read_urbs, chip->total_write_urbs);
+ DBG (7, "usb_low_exit: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A0 ~ A1 */
+SANE_Status
+usb_low_set_cmt_table (ma1017 * chip, SANE_Int index, Channel channel,
+ SANE_Bool is_move_motor, SANE_Bool is_transfer)
+{
+ SANE_Byte pattern = ((SANE_Byte) index) << 4;
+ SANE_Byte reg_no = 0;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_cmt_table: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_cmt_table: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_cmt_table: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((unsigned int) index > 31)
+ {
+ DBG (7, "usb_low_set_cmt_table: CMT index (%d) exceed 31", index);
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (channel)
+ {
+ case CH_RED:
+ pattern |= 0x04;
+ break;
+ case CH_GREEN:
+ pattern |= 0x08;
+ break;
+ case CH_BLUE:
+ pattern |= 0x0c;
+ break;
+ default:
+ break;
+ }
+ if (is_move_motor)
+ pattern |= 0x02;
+ if (is_transfer)
+ pattern |= 0x01;
+ if (index > 15)
+ reg_no++;
+
+ RIE (usb_low_write_reg (chip, reg_no, pattern));
+
+ chip->is_transfer_table[index] = is_transfer;
+
+ DBG (7, "usb_low_set_cmt_table: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* A2 */
+SANE_Status
+usb_low_get_a2 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a2: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a2: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a2: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_read_reg (chip, 2, &pattern));
+
+ chip->append = pattern & 0x10;
+ chip->test_sram = pattern & 0x20;
+ chip->fix_pattern = pattern & 0x80;
+ if (value)
+ *value = pattern;
+ DBG (7, "usb_low_get_a2: exit, value =%d\n", pattern);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_start_cmt_table (ma1017 * chip)
+{
+ SANE_Byte data_field[2];
+ SANE_Status status;
+ size_t n;
+
+ DBG (7, "usb_low_start_cmt_table: start\n");
+
+ data_field[0] = 0x02 | chip->append | chip->test_sram | chip->fix_pattern;
+ data_field[1] = 2;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_start_cmt_table: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (7, "usb_low_start_cmt_table: Already Rowing\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ data_field[1] |= 0x60;
+ n = 2;
+ status = sanei_usb_write_bulk (chip->fd, data_field, &n);
+ if (status != SANE_STATUS_GOOD || n != 2)
+ {
+ DBG (3, "usb_low_start_cmt_table: can't write, wanted 2 bytes, "
+ "wrote %lu bytes\n", (unsigned long int) n);
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_write_urbs++;
+ chip->is_rowing = SANE_TRUE;
+ DBG (7, "usb_low_start_cmt_table: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_stop_cmt_table (ma1017 * chip)
+{
+ SANE_Byte data_field[2];
+ SANE_Byte read_byte;
+ size_t n;
+ SANE_Status status;
+
+ DBG (7, "usb_low_stop_cmt_table: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_stop_cmt_table: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!chip->is_rowing)
+ {
+ DBG (7, "usb_low_stop_cmt_table: Not Rowing yet\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ data_field[0] = 0x01 | chip->append | chip->test_sram | chip->fix_pattern;
+ data_field[1] = 2;
+ data_field[1] |= 0x80;
+ n = 2;
+ status = sanei_usb_write_bulk (chip->fd, data_field, &n);
+ if (status != SANE_STATUS_GOOD || n != 2)
+ {
+ DBG (3, "usb_low_stop_cmt_table: couldn't write, wanted 2 bytes, wrote "
+ "%lu bytes\n", (unsigned long int) n);
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_write_urbs++;
+ n = 1;
+ status = sanei_usb_read_bulk (chip->fd, &read_byte, &n);
+ if (status != SANE_STATUS_GOOD || n != 1)
+ {
+ DBG (3, "usb_low_stop_cmt_table: couldn't read, wanted 1 byte, got %lu "
+ "bytes\n", (unsigned long int) n);
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_read_urbs++;
+ chip->is_rowing = SANE_FALSE;
+
+ DBG (7, "usb_low_stop_cmt_table: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_test_sram_mode (ma1017 * chip, SANE_Bool is_test)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_test_sram_mode: start\n");
+
+ data = chip->append | chip->test_sram | chip->fix_pattern;
+ reg_no = 2;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_test_sram_mode: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_test_sram_mode: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_test)
+ chip->test_sram = 0x20;
+ else
+ chip->test_sram = 0x00;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_test_sram_mode: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_fix_pattern (ma1017 * chip, SANE_Bool is_fix)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_fix_pattern: start\n");
+
+ data = chip->append | chip->test_sram | chip->fix_pattern;
+ reg_no = 2;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_fix_pattern: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_fix_pattern: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_fix)
+ chip->fix_pattern = 0x80;
+ else
+ chip->fix_pattern = 0x00;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_fix_pattern: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_adjust_timing (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Status status;
+ SANE_Byte reg_no;
+
+ DBG (7, "usb_low_adjust_timing: start\n");
+
+ reg_no = 3;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_adjust_timing: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_adjust_timing: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_adjust_timing: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* A4 */
+SANE_Status
+usb_low_get_a4 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Status status;
+ SANE_Byte pattern;
+
+ DBG (7, "usb_low_get_a4: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a4: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a4: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 4, &pattern));
+
+ chip->select = pattern & 0xfe;
+ chip->frontend = pattern & 0x01;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a4: exit, value=%d\n", pattern);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_select_timing (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Status status;
+ SANE_Byte reg_no;
+
+ DBG (7, "usb_low_select_timing: start\n");
+
+ reg_no = 4;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_select_timing: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_select_timing: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->select = data & 0xfe;
+ chip->frontend = data & 0x01;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_select_timing: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_turn_frontend_mode (ma1017 * chip, SANE_Bool is_on)
+{
+ SANE_Status status;
+ SANE_Byte data, reg_no;
+
+ DBG (7, "usb_low_turn_frontend_mode: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_turn_frontend_mode: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_turn_frontend_mode: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (is_on)
+ chip->frontend = 0x01;
+ else
+ chip->frontend = 0x00;
+
+ data = chip->select | chip->frontend;
+ reg_no = 4;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_turn_frontend_mode: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A6 */
+SANE_Status
+usb_low_get_a6 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a6: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a6: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a6: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_read_reg (chip, 6, &pattern));
+
+ chip->asic_io_pins = pattern & 0xdc;
+ chip->rgb_sel_pin = pattern & 0x03;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a6: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_asic_io_pins (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Status status;
+ SANE_Byte reg_no;
+
+ DBG (7, "usb_low_set_asic_io_pins: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_asic_io_pins: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_asic_io_pins: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->asic_io_pins = data & 0xdc;
+
+ data = chip->asic_io_pins | chip->rgb_sel_pin;
+ reg_no = 6;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_asic_io_pins: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_rgb_sel_pins (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Status status;
+ SANE_Byte reg_no;
+
+ DBG (7, "usb_low_set_rgb_sel_pins: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_rgb_sel_pins: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_rgb_sel_pins: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ chip->rgb_sel_pin = data & 0x03;
+ data = chip->asic_io_pins | chip->rgb_sel_pin;
+ reg_no = 6;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_rgb_sel_pins: exit\n");
+ return SANE_STATUS_GOOD; /* was false? */
+}
+
+/* A7 */
+SANE_Status
+usb_low_get_a7 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a7: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a7: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a7: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_read_reg (chip, 7, &pattern));
+
+ if (value)
+ *value = pattern;
+
+ chip->timing = pattern & 0xfc;
+ chip->sram_bank = pattern & 0x03;
+
+ DBG (7, "usb_low_get_a7: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_timing (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Status status;
+ SANE_Byte reg_no;
+
+ DBG (7, "usb_low_set_timing: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_timing: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_timing: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->timing = data & 0xfc;
+ data = chip->timing | chip->sram_bank;
+ reg_no = 7;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_timing: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_sram_bank (ma1017 * chip, Banksize banksize)
+{
+ SANE_Status status;
+ SANE_Byte data, reg_no;
+
+ DBG (7, "usb_low_set_sram_bank: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_sram_bank: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_sram_bank: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (banksize)
+ {
+ case BS_4K:
+ chip->sram_bank = 0x00;
+ break;
+ case BS_8K:
+ chip->sram_bank = 0x01;
+ break;
+ case BS_16K:
+ chip->sram_bank = 0x02;
+ break;
+ default:
+ DBG (3, "usb_low_set_sram_bank: bsBankSize error\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ data = chip->timing | chip->sram_bank;
+ reg_no = 7;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_sram_bank: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A8 */
+SANE_Status
+usb_low_get_a8 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a8: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a8: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a8: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 8, &pattern));
+
+ chip->dummy_msb = pattern & 0x40;
+ chip->ccd_width_msb = pattern & 0x20;
+ chip->cmt_table_length = pattern & 0x1f;
+ chip->ccd_width =
+ ((chip->ccd_width / 32) & 0x00ff) * 32 +
+ ((chip->ccd_width_msb == 0) ? 0 : 0x0100 * 32);
+ chip->dummy =
+ ((chip->dummy / 32) & 0x00ff) * 32 +
+ ((chip->dummy_msb == 0) ? 0 : 0x0100 * 32);
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a8: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_cmt_table_length (ma1017 * chip, SANE_Byte table_length)
+{
+ SANE_Status status;
+ SANE_Byte data, reg_no;
+
+ DBG (7, "usb_low_set_cmt_table_length: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_cmt_table_length: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_cmt_table_length: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (table_length > 32)
+ {
+ DBG (3, "usb_low_set_cmt_table_length: length %d exceeds 32\n",
+ (int) table_length);
+ return SANE_STATUS_INVAL;
+ }
+ if (table_length == 0)
+ {
+ DBG (3, "usb_low_set_cmt_table_length: length is 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->cmt_table_length = table_length - 1;
+ chip->cmt_table_length_word = (SANE_Word) table_length;
+ data = chip->cmt_table_length | chip->ccd_width_msb | chip->dummy_msb;
+ reg_no = 8;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_cmt_table_length: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A9 */
+SANE_Status
+usb_low_get_a9 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a9: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a9: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a9: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_read_reg (chip, 9, &pattern));
+
+ chip->cmt_second_pos = pattern & 0x1f;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a9: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_cmt_second_position (ma1017 * chip, SANE_Byte position)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_cmt_second_position: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_cmt_second_position: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_cmt_second_position: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (position > 31)
+ {
+ DBG (3, "usb_low_set_cmt_second_position: length: %d exceeds 31\n",
+ (int) position);
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->cmt_second_pos = position;
+ chip->cmt_second_pos_word = (SANE_Word) (position);
+ data = chip->cmt_second_pos;
+ reg_no = 9;
+
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_cmt_second_position: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* A10 + A8ID5 */
+SANE_Status
+usb_low_get_a10 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a10: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a10: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a10: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 10, &pattern));
+
+ chip->ccd_width =
+ ((SANE_Word) (pattern)) * 32 +
+ ((chip->ccd_width_msb == 0) ? 0 : 0x0100 * 32);
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a10: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_ccd_width (ma1017 * chip, SANE_Word ccd_width)
+{
+ SANE_Status status;
+ SANE_Byte data, reg_no;
+
+ DBG (7, "usb_low_set_ccd_width: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_ccd_width: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_ccd_width: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (ccd_width / 32 > 0x01ff)
+ {
+ DBG (3, "usb_low_set_ccd_width: width %d too high\n", (int) ccd_width);
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->ccd_width = ccd_width;
+ ccd_width /= 32;
+ if (HIBYTE (ccd_width) == 0x01)
+ chip->ccd_width_msb = 0x20;
+ else
+ chip->ccd_width_msb = 0x00;
+
+ data = chip->cmt_table_length | chip->ccd_width_msb | chip->dummy_msb;
+ reg_no = 8;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ data = LOBYTE (ccd_width);
+ reg_no = 10;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_ccd_width: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A11 + A8ID6 */
+SANE_Status
+usb_low_get_a11 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a11: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a11: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a11: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 11, &pattern));
+
+ chip->dummy =
+ ((SANE_Word) (pattern)) * 32 + ((chip->dummy_msb == 0) ? 0 : 0x0100 * 32);
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a11: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_dummy (ma1017 * chip, SANE_Word dummy)
+{
+ SANE_Status status;
+ SANE_Byte data, reg_no;
+
+ DBG (7, "usb_low_set_dummy: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_dummy: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_dummy: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (dummy / 32 > 0x01ff)
+ {
+ DBG (7, "usb_low_set_dummy: width %d exceeded\n", (int) dummy);
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->dummy = dummy;
+ dummy /= 32;
+ dummy++;
+ if (HIBYTE (dummy) == 0x01)
+ chip->dummy_msb = 0x40;
+ else
+ chip->dummy_msb = 0x00;
+ data = chip->cmt_table_length | chip->ccd_width_msb | chip->dummy_msb;
+ reg_no = 8;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ data = LOBYTE (dummy);
+ reg_no = 11;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_dummy: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A12 + A13 */
+SANE_Status
+usb_low_get_a12 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a12: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a12: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a12: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 12, &pattern));
+
+ chip->byte_width = (chip->byte_width & 0x3f00) + ((SANE_Word) pattern);
+ chip->soft_resample = (chip->soft_resample == 0) ? 1 : chip->soft_resample;
+ chip->get_row =
+ (chip->soft_resample == 1)
+ ? &usb_low_get_row_direct : &usb_low_get_row_resample;
+ chip->row_size = chip->byte_width / chip->soft_resample;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a12: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_a13 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a13: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a13: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a13: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 13, &pattern));
+
+ chip->byte_width =
+ (chip->byte_width & 0x00ff) + (((SANE_Word) (pattern & 0x3f)) << 8);
+ chip->soft_resample = (chip->soft_resample == 0) ? 1 : chip->soft_resample;
+ chip->get_row =
+ (chip->soft_resample ==
+ 1) ? &usb_low_get_row_direct : &usb_low_get_row_resample;
+ chip->row_size = chip->byte_width / chip->soft_resample;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a13: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_image_byte_width (ma1017 * chip, SANE_Word row_size)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_image_byte_width: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_image_byte_width: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_image_byte_width: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->row_size = row_size;
+ chip->soft_resample = (chip->soft_resample == 0) ? 1 : chip->soft_resample;
+ chip->get_row = (chip->soft_resample == 1) ? &usb_low_get_row_direct
+ : &usb_low_get_row_resample;
+ chip->byte_width = chip->row_size * chip->soft_resample;
+ if (chip->byte_width > 0x3fff)
+ {
+ DBG (3, "usb_low_set_image_byte_width: width %d exceeded\n",
+ (int) chip->byte_width);
+ return SANE_STATUS_INVAL;
+ }
+
+ data = LOBYTE (chip->byte_width);
+ reg_no = 12;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ data = HIBYTE (chip->byte_width);
+ reg_no = 13;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_image_byte_width: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_soft_resample (ma1017 * chip, SANE_Word soft_resample)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_soft_resample: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_soft_resample: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_soft_resample: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (soft_resample == 0x00)
+ {
+ DBG (3, "usb_low_set_soft_resample: soft_resample==0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->soft_resample = soft_resample;
+ chip->get_row = (chip->soft_resample == 1) ? &usb_low_get_row_direct
+ : &usb_low_get_row_resample;
+ chip->byte_width = chip->row_size * chip->soft_resample;
+ if (chip->byte_width > 0x3fff)
+ {
+ DBG (3, "usb_low_set_soft_resample: width %d exceeded",
+ (int) chip->byte_width);
+ return SANE_STATUS_INVAL;
+ }
+
+ data = LOBYTE (chip->byte_width);
+ reg_no = 12;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ data = HIBYTE (chip->byte_width);
+ reg_no = 13;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_soft_resample: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A14 + A30W */
+SANE_Status
+usb_low_set_cmt_loop_count (ma1017 * chip, SANE_Word loop_count)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_cmt_loop_count: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_cmt_loop_count: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_cmt_loop_count: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->loop_count = loop_count;
+
+ data = LOBYTE (loop_count);
+ reg_no = 14;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ data = HIBYTE (loop_count);
+ reg_no = 30;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_cmt_loop_count: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A15 */
+SANE_Status
+usb_low_get_a15 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a15: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a15: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a15: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 15, &pattern));
+
+ chip->motor_enable = pattern & 0x80;
+ chip->motor_movement = pattern & 0x68;
+ chip->motor_direction = pattern & 10;
+ chip->motor_signal = pattern & 0x06;
+ chip->motor_home = pattern & 0x01;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a15: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_enable_motor (ma1017 * chip, SANE_Bool is_enable)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_enable_motor: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_enable_motor: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_enable_motor: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->motor_enable = 0x00;
+ if (is_enable)
+ chip->motor_enable |= 0x80;
+ data = chip->motor_enable | chip->motor_movement
+ | chip->motor_direction | chip->motor_signal | chip->motor_home;
+ reg_no = 15;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_enable_motor: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_motor_movement (ma1017 * chip, SANE_Bool is_full_step,
+ SANE_Bool is_double_phase, SANE_Bool is_two_step)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_motor_movement: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_motor_movement: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_motor_movement: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->motor_movement = 0x00;
+ if (is_full_step)
+ chip->motor_movement |= 0x40;
+ if (is_double_phase)
+ chip->motor_movement |= 0x20;
+ if (is_two_step)
+ chip->motor_movement |= 0x08;
+ data = chip->motor_enable | chip->motor_movement
+ | chip->motor_direction | chip->motor_signal | chip->motor_home;
+ reg_no = 15;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_motor_movement: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_motor_direction (ma1017 * chip, SANE_Bool is_backward)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_motor_direction: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_motor_direction: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_motor_direction: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->motor_direction = 0x00;
+ if (is_backward)
+ chip->motor_direction |= 0x10;
+ data = chip->motor_enable | chip->motor_movement
+ | chip->motor_direction | chip->motor_signal | chip->motor_home;
+ reg_no = 15;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_motor_direction: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_motor_signal (ma1017 * chip, SANE_Byte signal)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_motor_signal: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_motor_signal: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_motor_signal: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->motor_signal = signal & 0x06;
+ data = chip->motor_enable | chip->motor_movement
+ | chip->motor_direction | chip->motor_signal | chip->motor_home;
+ reg_no = 15;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_motor_signal: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_move_motor_home (ma1017 * chip, SANE_Bool is_home,
+ SANE_Bool is_backward)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_move_motor_home: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_move_motor_home: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_move_motor_home: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->motor_enable = 0x00;
+ chip->motor_direction = 0x00;
+ chip->motor_home = 0x00;
+ if (is_backward)
+ chip->motor_direction |= 0x10;
+ if (is_home)
+ {
+ chip->motor_enable |= 0x80;
+ chip->motor_home |= 0x01;
+ }
+ data = chip->motor_enable | chip->motor_movement
+ | chip->motor_direction | chip->motor_signal | chip->motor_home;
+ reg_no = 15;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_move_motor_home: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A16 */
+SANE_Status
+usb_low_get_a16 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a16: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a16: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a16: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 16, &pattern));
+
+ chip->pixel_depth = pattern & 0xe0;
+ chip->image_invert = pattern & 0x10;
+ chip->optical_600 = pattern & 0x08;
+ chip->sample_way = pattern & 0x07;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a16: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_image_dpi (ma1017 * chip, SANE_Bool is_optical600,
+ Sampleway sampleway)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_image_dpi: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_image_dpi: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_image_dpi: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->optical_600 = 0x00;
+ chip->sample_way = 0x00;
+ if (is_optical600)
+ chip->optical_600 |= 0x08;
+ switch (sampleway)
+ {
+ case SW_P1P6:
+ chip->sample_way = 0x01;
+ break;
+ case SW_P2P6:
+ chip->sample_way = 0x02;
+ break;
+ case SW_P3P6:
+ chip->sample_way = 0x03;
+ break;
+ case SW_P4P6:
+ chip->sample_way = 0x04;
+ break;
+ case SW_P5P6:
+ chip->sample_way = 0x05;
+ break;
+ case SW_P6P6:
+ chip->sample_way = 0x06;
+ break;
+ default:
+ DBG (3, "usb_low_set_image_dpi: swsample_way error\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ data = chip->pixel_depth | chip->image_invert | chip->optical_600
+ | chip->sample_way;
+ reg_no = 16;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_image_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_pixel_depth (ma1017 * chip, Pixeldepth pixeldepth)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_pixel_depth: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_pixel_depth: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_pixel_depth: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->pixel_depth = 0x00;
+ switch (pixeldepth)
+ {
+ case PD_1BIT:
+ chip->pixel_depth = 0x80;
+ break;
+ case PD_4BIT:
+ chip->pixel_depth = 0xc0;
+ break;
+ case PD_8BIT:
+ chip->pixel_depth = 0x00;
+ break;
+ case PD_12BIT:
+ chip->pixel_depth = 0x20;
+ break;
+ default:
+ DBG (3, "usb_low_set_pixel_depth: pdPixelDepth error\n");
+ return SANE_STATUS_INVAL;
+ }
+ data = chip->pixel_depth | chip->image_invert | chip->optical_600
+ | chip->sample_way;
+ reg_no = 16;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_SetPixelDeepth: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_invert_image (ma1017 * chip, SANE_Bool is_invert)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_invert_image: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_invert_image: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_invert_image: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->image_invert = 0x00;
+ if (is_invert)
+ chip->image_invert |= 0x10;
+ data = chip->pixel_depth | chip->image_invert | chip->optical_600
+ | chip->sample_way;
+ reg_no = 16;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_invert_image: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A17 + A18 + A19 */
+SANE_Status
+usb_low_get_a17 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a17: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a17: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a17: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 17, &pattern));
+
+ chip->red_ref = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a17: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_a18 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a18: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a18: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a18: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 18, &pattern));
+
+ chip->green_ref = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a18: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_a19 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a19: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a19: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a19:stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 19, &pattern));
+
+ chip->blue_ref = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a19: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_red_ref (ma1017 * chip, SANE_Byte red_ref)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_red_ref: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_red_ref: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_red_ref: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->red_ref = red_ref;
+ data = red_ref;
+ reg_no = 17;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_red_ref: stop\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_green_ref (ma1017 * chip, SANE_Byte green_ref)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_green_ref: start\n");
+
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_green_ref: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_green_ref: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->green_ref = green_ref;
+
+ data = green_ref;
+ reg_no = 18;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_green_ref: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_blue_ref (ma1017 * chip, SANE_Byte blue_ref)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_blue_ref: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_blue_ref: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_blue_ref: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->blue_ref = blue_ref;
+
+ data = blue_ref;
+ reg_no = 19;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_blue_ref: stop\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A20 + A21 + A22 */
+SANE_Status
+usb_low_get_a20 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a20: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a20: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a20: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+ RIE (usb_low_read_reg (chip, 20, &pattern));
+
+ chip->red_pd = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a20: stop\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_a21 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a21: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a21: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a21: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 21, &pattern));
+
+ chip->green_pd = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a21: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_a22 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a22: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a22: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a22: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 22, &pattern));
+
+ chip->blue_pd = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a22: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_red_pd (ma1017 * chip, SANE_Byte red_pd)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_red_pd: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_red_pd: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_red_pd: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->red_pd = red_pd;
+
+ data = chip->red_pd;
+ reg_no = 20;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_red_pd: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_green_pd (ma1017 * chip, SANE_Byte green_pd)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_green_pd: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_green_pd: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_green_pd: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->green_pd = green_pd;
+ data = chip->green_pd;
+ reg_no = 21;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_green_pd: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_blue_pd (ma1017 * chip, SANE_Byte blue_pd)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_blue_pd: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_blue_pd: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_blue_pd: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->blue_pd = blue_pd;
+
+ data = chip->blue_pd;
+ reg_no = 22;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_blue_pd: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A23 */
+SANE_Status
+usb_low_get_a23 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a23: start\n");
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a23: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a23: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 23, &pattern));
+
+ chip->a23 = pattern;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a23: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_turn_peripheral_power (ma1017 * chip, SANE_Bool is_on)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_turn_peripheral_power: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_turn_peripheral_power: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_turn_peripheral_power: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->a23 &= 0x7f;
+ if (is_on)
+ chip->a23 |= 0x80;
+ data = chip->a23;
+ reg_no = 23;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_turn_peripheral_power: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_turn_lamp_power (ma1017 * chip, SANE_Bool is_on)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_turn_lamp_power: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_turn_lamp_power: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_turn_lamp_power: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->a23 &= 0xbf;
+ if (is_on)
+ chip->a23 |= 0x40;
+
+ data = chip->a23;
+ reg_no = 23;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_turn_lamp_power: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_io_3 (ma1017 * chip, SANE_Bool is_high)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_io_3: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_io_3: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_io_3: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->a23 &= 0xf7;
+ if (is_high)
+ chip->a23 |= 0x08;
+
+ data = chip->a23;
+ reg_no = 23;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_io_3: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_led_light_all (ma1017 * chip, SANE_Bool is_light_all)
+{
+ SANE_Byte data, reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_led_light_all: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_led_light_all: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_led_light_all: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->a23 &= 0xfe;
+ if (is_light_all)
+ chip->a23 |= 0x01;
+
+ data = chip->a23;
+ reg_no = 23;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_led_light_all: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A24 */
+SANE_Status
+usb_low_get_a24 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a24: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a24: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a24: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 24, &pattern));
+
+ chip->fy1_delay = pattern & 0x01;
+ chip->special_ad = pattern & 0x02;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a24: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_ad_timing (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Byte reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_ad_timing: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_ad_timing: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_ad_timing: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ chip->fy1_delay = data & 0x01;
+ chip->special_ad = data & 0x02;
+
+ data = chip->special_ad | chip->fy1_delay;
+ reg_no = 24;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_ad_timing: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A25 + A26 */
+SANE_Status
+usb_low_set_serial_byte1 (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Byte reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_serial_byte1: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_serial_byte1: not opened\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_serial_byte1: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ reg_no = 25;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_serial_byte1: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_serial_byte2 (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Byte reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_serial_byte2: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_serial_byte2: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_serial_byte2: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ reg_no = 26;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_serial_byte2: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* A27 */
+SANE_Status
+usb_low_get_a27 (ma1017 * chip, SANE_Byte * value)
+{
+ SANE_Byte pattern;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_a27: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_a27: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_a27: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 27, &pattern));
+
+ chip->sclk = pattern & 0x80;
+ chip->sen = pattern & 0x40;
+ chip->serial_length = pattern & 0x1f;
+
+ if (value)
+ *value = pattern;
+
+ DBG (7, "usb_low_get_a27: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_set_serial_format (ma1017 * chip, SANE_Byte data)
+{
+ SANE_Byte reg_no;
+ SANE_Status status;
+
+ DBG (7, "usb_low_set_serial_format: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_set_serial_format: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_set_serial_format: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ chip->sclk = data & 0x80;
+ chip->sen = data & 0x40;
+ chip->serial_length = data & 0x1f;
+
+ reg_no = 27;
+ RIE (usb_low_write_reg (chip, reg_no, data));
+
+ DBG (7, "usb_low_set_serial_format: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_home_sensor (ma1017 * chip)
+{
+ SANE_Byte data;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_home_sensor: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_get_home_sensor: not opened yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_get_home_sensor: stop rowing first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_read_reg (chip, 31, &data));
+
+ DBG (7, "usb_low_get_home_sensor: exit\n");
+ if ((data & 0x80) != 0)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Special Mode */
+SANE_Status
+usb_low_start_rowing (ma1017 * chip)
+{
+ SANE_Word line_of_first = 0;
+ SANE_Word line_of_second = 0;
+ SANE_Int i;
+ SANE_Status status;
+
+ DBG (7, "usb_low_start_rowing: start\n");
+
+ if (chip->loop_count == 0)
+ {
+ DBG (3, "usb_low_start_rowing loop_count hasn't been set yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->cmt_table_length_word == 0)
+ {
+ DBG (3, "usb_low_start_rowing: cmt_table_length_word hasn't been set "
+ "yet\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->cmt_table_length_word <= chip->cmt_second_pos_word)
+ {
+ DBG (3, "usb_low_start_rowing: cmt_second_pos_word cannot be larger "
+ "than cmt_table_length_word\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (i = 0; i < (int) chip->cmt_second_pos_word; i++)
+ {
+ if (chip->is_transfer_table[i])
+ line_of_first++;
+ }
+ for (; i < (int) chip->cmt_table_length_word; i++)
+ {
+ if (chip->is_transfer_table[i])
+ {
+ line_of_first++;
+ line_of_second++;
+ }
+ }
+
+ chip->total_lines =
+ ((SANE_Word) (chip->loop_count - 1)) * line_of_second + line_of_first;
+ chip->lines_left = chip->total_lines;
+
+ RIE (usb_low_start_cmt_table (chip));
+
+ DBG (7, "usb_low_start_rowing: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_stop_rowing (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (7, "usb_low_stop_rowing: start\n");
+
+ RIE (usb_low_stop_cmt_table (chip));
+
+ DBG (7, "usb_low_stop_rowing: exit\n");
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+usb_low_wait_rowing_stop (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (7, "usb_low_wait_rowing_stop: start\n");
+ if (chip->total_lines != 0)
+ {
+ DBG (3, "usb_low_wait_rowing_stop: total_lines must be 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ RIE (usb_low_wait_rowing (chip));
+ chip->is_rowing = SANE_FALSE;
+ DBG (7, "usb_low_wait_rowing_stop: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_read_all_registers (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (7, "usb_low_read_all_registers: start\n");
+
+ RIE (usb_low_get_a2 (chip, 0));
+ RIE (usb_low_get_a4 (chip, 0));
+ RIE (usb_low_get_a6 (chip, 0));
+ RIE (usb_low_get_a7 (chip, 0));
+ RIE (usb_low_get_a8 (chip, 0));
+ RIE (usb_low_get_a9 (chip, 0));
+ RIE (usb_low_get_a10 (chip, 0));
+ RIE (usb_low_get_a11 (chip, 0));
+ RIE (usb_low_get_a12 (chip, 0));
+ RIE (usb_low_get_a13 (chip, 0));
+ RIE (usb_low_get_a15 (chip, 0));
+ RIE (usb_low_get_a16 (chip, 0));
+ RIE (usb_low_get_a17 (chip, 0));
+ RIE (usb_low_get_a18 (chip, 0));
+ RIE (usb_low_get_a19 (chip, 0));
+ RIE (usb_low_get_a20 (chip, 0));
+ RIE (usb_low_get_a21 (chip, 0));
+ RIE (usb_low_get_a22 (chip, 0));
+ RIE (usb_low_get_a23 (chip, 0));
+ RIE (usb_low_get_a24 (chip, 0));
+ RIE (usb_low_get_a27 (chip, 0));
+
+ return SANE_STATUS_GOOD;
+ DBG (7, "usb_low_read_all_registers: exit\n");
+}
+
+SANE_Status
+usb_low_get_row (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left)
+{
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_row: start\n");
+ RIE ((*chip->get_row) (chip, data, lines_left));
+ DBG (7, "usb_low_get_row: exit\n");
+ return SANE_STATUS_GOOD;;
+}
+
+SANE_Status
+usb_low_get_row_direct (ma1017 * chip, SANE_Byte * data,
+ SANE_Word * lines_left)
+{
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_row_direct: start\n");
+ if (chip->lines_left == 0)
+ {
+ DBG (3, "usb_low_get_row_direct: lines_left == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (chip->lines_left <= 1)
+ {
+ RIE (usb_low_read_rows (chip, data, chip->byte_width));
+ RIE (usb_low_wait_rowing (chip));
+
+ chip->lines_left = 0x00;
+ chip->is_rowing = SANE_FALSE;
+ *lines_left = 0;
+ }
+ else
+ {
+ RIE (usb_low_read_rows (chip, data, chip->byte_width));
+ chip->lines_left--;
+ *lines_left = chip->lines_left;
+ }
+ DBG (7, "usb_low_get_row_direct: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_get_row_resample (ma1017 * chip, SANE_Byte * data,
+ SANE_Word * lines_left)
+{
+ static SANE_Byte resample_buffer[8 * 1024];
+ SANE_Word *pixel_temp;
+ SANE_Word i;
+ SANE_Word j;
+ SANE_Word k;
+ SANE_Status status;
+
+ DBG (7, "usb_low_get_row_resample: start\n");
+
+ if (chip->lines_left == 0)
+ {
+ DBG (3, "usb_low_get_row_resample: lines_left == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (chip->lines_left <= 1)
+ {
+ RIE (usb_low_read_rows (chip, resample_buffer, chip->byte_width));
+
+ if ((chip->sensor == ST_CANON600) && (chip->pixel_depth == 0x20))
+ {
+ pixel_temp = (SANE_Word *) malloc (6 * 1024 * sizeof (SANE_Word));
+ if (!pixel_temp)
+ return SANE_STATUS_NO_MEM;
+
+ j = 0;
+ for (i = 0; i < chip->byte_width; i += 3)
+ {
+ pixel_temp[j] = (SANE_Word) resample_buffer[i];
+ pixel_temp[j] |= ((SANE_Word)
+ (resample_buffer[i + 1] & 0xf0)) << 4;
+ j++;
+ pixel_temp[j] = ((SANE_Word)
+ (resample_buffer[i + 1] & 0x0f)) << 8;
+ pixel_temp[j] |= (SANE_Word) resample_buffer[i + 2];
+ j++;
+ }
+
+ k = 0;
+ for (i = 0; i < j; i += chip->soft_resample * 2)
+ {
+ data[k] = (SANE_Byte) (pixel_temp[i] & 0x00ff);
+ k++;
+ data[k] = (SANE_Byte) ((pixel_temp[i] & 0x0f00) >> 4);
+ data[k] |= (SANE_Byte) ((pixel_temp[i + 2] & 0x0f00) >> 8);
+ k++;
+ data[k] = (SANE_Byte) (pixel_temp[i + 2] & 0x00ff);
+ k++;
+ }
+ free (pixel_temp);
+ }
+ else /* fixme ? */
+ {
+ for (i = 0; i < chip->byte_width; i += chip->soft_resample)
+ *(data++) = resample_buffer[i];
+ }
+ RIE (usb_low_wait_rowing (chip));
+
+ chip->lines_left = 0x00;
+ chip->is_rowing = SANE_FALSE;
+ *lines_left = 0;
+ }
+ else
+ {
+ RIE (usb_low_read_rows (chip, resample_buffer, chip->byte_width));
+
+ if ((chip->sensor == ST_CANON600) && (chip->pixel_depth == 0x20))
+ {
+ pixel_temp = (SANE_Word *) malloc (6 * 1024 * sizeof (SANE_Word));
+ if (!pixel_temp)
+ return SANE_STATUS_NO_MEM;
+
+ j = 0;
+ for (i = 0; i < chip->byte_width; i += 3)
+ {
+ pixel_temp[j] = (SANE_Word) resample_buffer[i];
+ pixel_temp[j] |=
+ ((SANE_Word) (resample_buffer[i + 1] & 0xf0)) << 4;
+ j++;
+ pixel_temp[j] =
+ ((SANE_Word) (resample_buffer[i + 1] & 0x0f)) << 8;
+ pixel_temp[j] |= (SANE_Word) resample_buffer[i + 2];
+ j++;
+ }
+
+ k = 0;
+ for (i = 0; i < j; i += chip->soft_resample * 2)
+ {
+ data[k] = (SANE_Byte) (pixel_temp[i] & 0x00ff);
+ k++;
+ data[k] = (SANE_Byte) ((pixel_temp[i] & 0x0f00) >> 4);
+ data[k] |= (SANE_Byte) ((pixel_temp[i + 2] & 0x0f00) >> 8);
+ k++;
+ data[k] = (SANE_Byte) (pixel_temp[i + 2] & 0x00ff);
+ k++;
+ }
+ free (pixel_temp);
+ }
+ else /* fixme? */
+ {
+ for (i = 0; i < chip->byte_width; i += chip->soft_resample)
+ *(data++) = resample_buffer[i];
+ }
+ chip->lines_left--;
+ *lines_left = chip->lines_left;
+ }
+ DBG (7, "usb_low_get_row_resample: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_wait_rowing (ma1017 * chip)
+{
+ SANE_Byte read_byte;
+ size_t n;
+ SANE_Status status;
+
+ DBG (7, "usb_low_wait_rowing: start\n");
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_wait_rowing: open first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!chip->is_rowing)
+ {
+ DBG (3, "usb_low_wait_rowing: not rowing\n");
+ return SANE_STATUS_INVAL;
+ }
+ n = 1;
+ status = sanei_usb_read_bulk (chip->fd, (SANE_Byte *) & read_byte, &n);
+ if (status != SANE_STATUS_GOOD || n != 1)
+ {
+ DBG (3, "usb_low_wait_rowing: couldn't read: %s\n",
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_read_urbs++;
+ chip->is_rowing = SANE_FALSE;
+ DBG (7, "usb_low_wait_rowing: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_read_rows (ma1017 * chip, SANE_Byte * data, SANE_Word byte_count)
+{
+ size_t n, bytes_total;
+ SANE_Status status;
+
+ DBG (7, "usb_low_read_rows: start\n");
+ if (!(chip->is_opened))
+ {
+ DBG (3, "usb_low_read_rows: is_opened==SANE_FALSE\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!(chip->is_rowing))
+ {
+ DBG (3, "usb_low_read_rows: is_rowing==SANE_FALSE\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ n = MIN (byte_count, chip->max_block_size);
+ bytes_total = 0;
+
+ while ((SANE_Word) bytes_total < byte_count)
+ {
+ status =
+ sanei_usb_read_bulk (chip->fd, (SANE_Byte *) (data + bytes_total),
+ &n);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (7, "usb_low_read_rows: problems during read: %s -- exiting\n",
+ sane_strstatus (status));
+ return status;
+ }
+ /* Count the number of URBs. This is a bit tricky, as we are reading
+ bigger chunks here but the scanner can only handle 64 bytes at once. */
+ chip->total_read_urbs += ((n + 63) / 64);
+ bytes_total += n;
+ if ((SANE_Word) bytes_total != byte_count)
+ {
+ DBG (7, "usb_low_read_rows: wanted %d, got %d "
+ "bytes (%d in total) -- retrying\n", byte_count, (SANE_Word) n,
+ (SANE_Word) bytes_total);
+ }
+ n = MIN ((byte_count - (SANE_Word) bytes_total), chip->max_block_size);
+ }
+
+ DBG (7, "usb_low_read_rows: exit, read %d bytes\n",
+ (SANE_Word) bytes_total);
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_low_write_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte data)
+{
+ size_t n;
+ SANE_Status status;
+ SANE_Byte data_field[2];
+
+ data_field[0] = data;
+ data_field[1] = reg_no;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_write_reg: open first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_write_reg: rowing, stop first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (reg_no > 0x20)
+ {
+ DBG (3, "usb_low_write_reg: reg_no out of range\n");
+ return SANE_STATUS_INVAL;
+ }
+ n = 2;
+ status = sanei_usb_write_bulk (chip->fd, data_field, &n);
+ if (status != SANE_STATUS_GOOD || n != 2)
+ {
+ DBG (3, "usb_low_write_reg: couldn't write, tried to write %d, "
+ "wrote %lu: %s\n", 2, (unsigned long int) n,
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_write_urbs++;
+ DBG (7, "usb_low_write_reg: reg: 0x%02x, value: 0x%02x\n", reg_no, data);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_read_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte * data)
+{
+ SANE_Byte data_field[2];
+ SANE_Byte read_byte;
+ size_t n;
+ SANE_Status status;
+
+ data_field[0] = 0x00;
+ data_field[1] = reg_no | 0x20;
+
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_read_reg: open first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_read_reg: rowing, stop first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (reg_no > 0x20)
+ {
+ DBG (3, "usb_low_read_reg: reg_no out of range\n");
+ return SANE_STATUS_INVAL;
+ }
+ n = 2;
+ DBG (5, "usb_low_read_reg: trying to write %lu bytes\n", (unsigned long int) n);
+
+ status = sanei_usb_write_bulk (chip->fd, data_field, &n);
+ if (status != SANE_STATUS_GOOD || n != 2)
+ {
+ DBG (3, "usb_low_read_reg: couldn't write, tried to write %d, "
+ "wrote %lu: %s\n", 2, (unsigned long int) n,
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_write_urbs++;
+ n = 1;
+ DBG (5, "usb_low_read_reg: trying to read %lu bytes\n", (unsigned long int) n);
+ status = sanei_usb_read_bulk (chip->fd, (SANE_Byte *) & read_byte, &n);
+ if (status != SANE_STATUS_GOOD || n != 1)
+ {
+ DBG (3, "usb_low_read_reg: couldn't read, tried to read %lu, "
+ "read %lu: %s\n", (unsigned long int) 1,
+ (unsigned long int) n, sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ chip->total_read_urbs++;
+ if (data)
+ *data = read_byte;
+ DBG (7, "usb_low_read_reg: Reg: 0x%02x, Value: 0x%02x\n",
+ reg_no, read_byte);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_identify_scanner (SANE_Int fd, Mustek_Type * scanner_type)
+{
+ SANE_Status status;
+ SANE_Word devvendor, devproduct;
+ Mustek_Type devtype;
+
+ DBG (7, "usb_low_identify_scanner: start\n");
+
+ status = sanei_usb_get_vendor_product (fd, &devvendor, &devproduct);
+ devtype = MT_UNKNOWN;
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (devvendor == 0x055f)
+ {
+ switch (devproduct)
+ {
+ case 0x0001:
+ devtype = MT_1200CU;
+ break;
+ case 0x0002:
+ devtype = MT_600CU;
+ break;
+ case 0x0003:
+ devtype = MT_1200USB;
+ break;
+ case 0x0006:
+ devtype = MT_1200UB;
+ break;
+ case 0x0008:
+ devtype = MT_1200CU_PLUS;
+ break;
+ case 0x0873:
+ devtype = MT_600USB;
+ break;
+ default:
+ if (scanner_type)
+ *scanner_type = devtype;
+ DBG (3, "usb_low_identify_scanner: unknown product id: "
+ "0x%04x\n", devproduct);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ }
+ else
+ {
+ if (scanner_type)
+ *scanner_type = devtype;
+ DBG (3, "usb_low_identify_scanner: unknown vendor id: 0x%04d\n",
+ devvendor);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ if (scanner_type)
+ *scanner_type = devtype;
+ DBG (7, "usb_low_identify_scanner: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_open (ma1017 * chip, SANE_String_Const devname)
+{
+ SANE_Status status;
+ Mustek_Type scanner_type;
+
+ DBG (7, "usb_low_open: start: chip = %p\n", (void *) chip);
+
+ if (chip->is_rowing)
+ {
+ DBG (3, "usb_low_open: already rowing\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (chip->is_opened)
+ {
+ DBG (3, "usb_low_open: already opened\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_usb_open ((SANE_String_Const) devname, &chip->fd);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (7, "usb_low_open: device %s successfully opened\n", devname);
+ chip->is_opened = SANE_TRUE;
+ /* Try to get vendor and device ids */
+ DBG (7, "usb_low_open: trying to identify device `%s'\n", devname);
+ status = usb_low_identify_scanner (chip->fd, &scanner_type);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "usb_low_open: device `%s' doesn't look like a supported "
+ "scanner\n", devname);
+ sanei_usb_close (chip->fd);
+ return status;
+ }
+ else
+ {
+ if (scanner_type == MT_UNKNOWN)
+ {
+ DBG (3, "usb_low_open: device `%s' can't be identified\n",
+ devname);
+ }
+ else if (scanner_type != chip->scanner_type)
+ {
+ DBG (3, "usb_low_open: device `%s' is supported but"
+ "it's not the same as at the start\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ }
+ else
+ {
+ DBG (1, "usb_low_open: device %s couldn't be opened: %s\n",
+ devname, sane_strstatus (status));
+ return status;
+ }
+
+ chip->is_opened = SANE_TRUE;
+
+ RIE (usb_low_read_all_registers (chip));
+
+ DBG (7, "usb_low_open: exit, type is %d\n", scanner_type);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_low_close (ma1017 * chip)
+{
+ DBG (7, "usb_low_close: start, chip=%p\n", (void *) chip);
+ if (!chip->is_opened)
+ {
+ DBG (3, "usb_low_close: already close or never opened\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (chip->fd >= 0)
+ {
+ SANE_Byte dummy;
+
+ if (chip->is_rowing)
+ usb_low_stop_rowing (chip);
+ /* Now make sure that both the number of written and
+ read URBs is even. Use some dummy writes/reads. That's to avoid
+ a nasty bug in the MA 1017 chipset that causes timeouts when
+ the number of URBs is odd (toggle bug). */
+ if ((chip->total_read_urbs % 2) == 1)
+ usb_low_get_a4 (chip, &dummy);
+ if ((chip->total_write_urbs % 2) == 1)
+ usb_low_set_fix_pattern (chip, SANE_FALSE);
+ sanei_usb_close (chip->fd);
+ chip->fd = -1;
+ }
+ chip->is_opened = SANE_FALSE;
+ chip->is_rowing = SANE_FALSE;
+
+ DBG (7, "usb_low_close: exit\n");
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/mustek_usb_low.h b/backend/mustek_usb_low.h
new file mode 100644
index 0000000..e5605db
--- /dev/null
+++ b/backend/mustek_usb_low.h
@@ -0,0 +1,450 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001, 2002 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#ifndef mustek_usb_low_h
+#define mustek_usb_low_h
+
+#include "../include/sane/sane.h"
+
+
+/* ---------------------------------- macros ------------------------------ */
+
+
+/* calculate the minimum/maximum values */
+#if defined(MIN)
+#undef MIN
+#endif
+#if defined(MAX)
+#undef MAX
+#endif
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+/* return the lower/upper 8 bits of a 16 bit word */
+#define HIBYTE(w) ((SANE_Byte)(((SANE_Word)(w) >> 8) & 0xFF))
+#define LOBYTE(w) ((SANE_Byte)(w))
+/* RIE: return if error */
+#define RIE(function) do {status = function; if (status != SANE_STATUS_GOOD) \
+ return status;} while (SANE_FALSE)
+
+
+/* ---------------------------------- types ------------------------------- */
+
+
+typedef enum Mustek_Type
+{
+ MT_UNKNOWN = 0,
+ MT_1200USB,
+ MT_1200UB,
+ MT_1200CU,
+ MT_1200CU_PLUS,
+ MT_600CU,
+ MT_600USB
+}
+Mustek_Type;
+
+typedef enum Sensor_Type
+{
+ ST_NONE = 0,
+ ST_INI = 1,
+ ST_INI_DARK = 2,
+ ST_CANON300 = 3,
+ ST_CANON600 = 4,
+ ST_TOSHIBA600 = 5,
+ ST_CANON300600 = 6,
+ ST_NEC600 = 7
+}
+Sensor_Type;
+
+typedef enum Motor_Type
+{
+ MT_NONE = 0,
+ MT_600 = 1,
+ MT_1200 = 2
+}
+Motor_Type;
+
+struct ma1017;
+
+typedef struct ma1017
+{
+ int fd;
+
+ SANE_Bool is_opened;
+ SANE_Bool is_rowing;
+
+ /* A2 */
+ SANE_Byte append;
+ SANE_Byte test_sram;
+ SANE_Byte fix_pattern;
+ /* A4 */
+ SANE_Byte select;
+ SANE_Byte frontend;
+ /* A6 */
+ SANE_Byte rgb_sel_pin;
+ SANE_Byte asic_io_pins;
+ /* A7 */
+ SANE_Byte timing;
+ SANE_Byte sram_bank;
+ /* A8 */
+ SANE_Byte dummy_msb;
+ SANE_Byte ccd_width_msb;
+ SANE_Byte cmt_table_length;
+ /* A9 */
+ SANE_Byte cmt_second_pos;
+ /* A10 + A8ID5 */
+ SANE_Word ccd_width;
+ /* A11 + A8ID6 */
+ SANE_Word dummy;
+ /* A12 + A13 */
+ SANE_Word byte_width;
+ /* A14 + A30W */
+ SANE_Word loop_count;
+ /* A15 */
+ SANE_Byte motor_enable;
+ SANE_Byte motor_movement;
+ SANE_Byte motor_direction;
+ SANE_Byte motor_signal;
+ SANE_Byte motor_home;
+ /* A16 */
+ SANE_Byte pixel_depth;
+ SANE_Byte image_invert;
+ SANE_Byte optical_600;
+ SANE_Byte sample_way;
+ /* A17 + A18 + A19 */
+ SANE_Byte red_ref;
+ SANE_Byte green_ref;
+ SANE_Byte blue_ref;
+ /* A20 + A21 + A22 */
+ SANE_Byte red_pd;
+ SANE_Byte green_pd;
+ SANE_Byte blue_pd;
+ /* A23 */
+ SANE_Byte a23;
+ /* A24 */
+ SANE_Byte fy1_delay;
+ SANE_Byte special_ad;
+ /* A27 */
+ SANE_Byte sclk;
+ SANE_Byte sen;
+ SANE_Byte serial_length;
+
+ /* Use for Rowing */
+ SANE_Status (*get_row) (struct ma1017 * chip, SANE_Byte * row,
+ SANE_Word * lines_left);
+
+ SANE_Word cmt_table_length_word;
+ SANE_Word cmt_second_pos_word;
+ SANE_Word row_size;
+ SANE_Word soft_resample;
+ SANE_Word total_lines;
+ SANE_Word lines_left;
+ SANE_Bool is_transfer_table[32];
+ Sensor_Type sensor;
+ Motor_Type motor;
+ Mustek_Type scanner_type;
+ SANE_Word max_block_size;
+
+ SANE_Word total_read_urbs;
+ SANE_Word total_write_urbs;
+}
+ma1017;
+
+typedef enum Channel
+{
+ CH_NONE = 0,
+ CH_RED = 1,
+ CH_GREEN = 2,
+ CH_BLUE = 3
+}
+Channel;
+
+typedef enum Banksize
+{
+ BS_NONE = 0,
+ BS_4K = 1,
+ BS_8K = 2,
+ BS_16K = 3
+}
+Banksize;
+
+typedef enum Pixeldepth
+{
+ PD_NONE = 0,
+ PD_1BIT = 1,
+ PD_4BIT = 2,
+ PD_8BIT = 3,
+ PD_12BIT = 4
+}
+Pixeldepth;
+
+typedef enum Sampleway
+{
+ SW_NONE = 0,
+ SW_P1P6 = 1,
+ SW_P2P6 = 2,
+ SW_P3P6 = 3,
+ SW_P4P6 = 4,
+ SW_P5P6 = 5,
+ SW_P6P6 = 6
+}
+Sampleway;
+
+/* ------------------------- function declarations ------------------------ */
+
+static SANE_Status usb_low_init (ma1017 ** chip);
+
+static SANE_Status usb_low_exit (ma1017 * chip);
+
+/* Register read and write functions */
+/* A0 ~ A1 */
+static SANE_Status
+usb_low_set_cmt_table (ma1017 * chip, SANE_Int index, Channel channel,
+ SANE_Bool is_move_motor, SANE_Bool is_transfer);
+
+/* A2 */
+static SANE_Status usb_low_get_a2 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_start_cmt_table (ma1017 * chip);
+
+static SANE_Status usb_low_stop_cmt_table (ma1017 * chip);
+
+static SANE_Status
+usb_low_set_test_sram_mode (ma1017 * chip, SANE_Bool is_test);
+
+static SANE_Status usb_low_set_fix_pattern (ma1017 * chip, SANE_Bool is_fix);
+
+/* A3 */
+static SANE_Status usb_low_adjust_timing (ma1017 * chip, SANE_Byte data);
+
+/* A4 */
+static SANE_Status usb_low_get_a4 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_select_timing (ma1017 * chip, SANE_Byte data);
+
+static SANE_Status
+usb_low_turn_frontend_mode (ma1017 * chip, SANE_Bool is_on);
+
+/* A6 */
+static SANE_Status usb_low_get_a6 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_asic_io_pins (ma1017 * chip, SANE_Byte data);
+
+static SANE_Status usb_low_set_rgb_sel_pins (ma1017 * chip, SANE_Byte data);
+
+/* A7 */
+static SANE_Status usb_low_get_a7 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_timing (ma1017 * chip, SANE_Byte data);
+
+static SANE_Status usb_low_set_sram_bank (ma1017 * chip, Banksize banksize);
+
+/* A8 */
+static SANE_Status usb_low_get_a8 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status
+usb_low_set_cmt_table_length (ma1017 * chip, SANE_Byte table_length);
+
+/* A9 */
+static SANE_Status usb_low_get_a9 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status
+usb_low_set_cmt_second_position (ma1017 * chip, SANE_Byte position);
+
+/* A10 + A8ID5 */
+static SANE_Status usb_low_get_a10 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_ccd_width (ma1017 * chip, SANE_Word ccd_width);
+
+/* A11 + A8ID6 */
+static SANE_Status usb_low_get_a11 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_dummy (ma1017 * chip, SANE_Word dummy);
+
+/* A12 + A13 */
+static SANE_Status usb_low_get_a12 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_get_a13 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status
+usb_low_set_image_byte_width (ma1017 * chip, SANE_Word row_size);
+
+static SANE_Status
+usb_low_set_soft_resample (ma1017 * chip, SANE_Word soft_resample);
+
+/* A14 + A30W */
+static SANE_Status
+usb_low_set_cmt_loop_count (ma1017 * chip, SANE_Word loop_count);
+
+/* A15 */
+static SANE_Status usb_low_get_a15 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_enable_motor (ma1017 * chip, SANE_Bool is_enable);
+
+static SANE_Status
+usb_low_set_motor_movement (ma1017 * chip, SANE_Bool is_full_step,
+ SANE_Bool is_double_phase, SANE_Bool is_two_step);
+
+static SANE_Status usb_low_set_motor_signal (ma1017 * chip, SANE_Byte signal);
+
+static SANE_Status
+usb_low_set_motor_direction (ma1017 * chip, SANE_Bool is_backward);
+
+static SANE_Status
+usb_low_move_motor_home (ma1017 * chip, SANE_Bool is_home,
+ SANE_Bool is_backward);
+
+/* A16 */
+static SANE_Status usb_low_get_a16 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status
+usb_low_set_image_dpi (ma1017 * chip, SANE_Bool is_optical600,
+ Sampleway sampleway);
+
+static SANE_Status
+usb_low_set_pixel_depth (ma1017 * chip, Pixeldepth pixeldepth);
+
+static SANE_Status usb_low_invert_image (ma1017 * chip, SANE_Bool is_invert);
+
+/* A17 + A18 + A19 */
+static SANE_Status usb_low_get_a17 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_get_a18 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_get_a19 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_red_ref (ma1017 * chip, SANE_Byte red_ref);
+
+static SANE_Status usb_low_set_green_ref (ma1017 * chip, SANE_Byte green_ref);
+
+static SANE_Status usb_low_set_blue_ref (ma1017 * chip, SANE_Byte blue_ref);
+
+/* A20 + A21 + A22 */
+static SANE_Status usb_low_get_a20 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_get_a21 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_get_a22 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_red_pd (ma1017 * chip, SANE_Byte red_pd);
+
+static SANE_Status usb_low_set_green_pd (ma1017 * chip, SANE_Byte green_pd);
+
+static SANE_Status usb_low_set_blue_pd (ma1017 * chip, SANE_Byte blue_pd);
+
+/* A23 */
+static SANE_Status usb_low_get_a23 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status
+usb_low_turn_peripheral_power (ma1017 * chip, SANE_Bool is_on);
+
+static SANE_Status usb_low_turn_lamp_power (ma1017 * chip, SANE_Bool is_on);
+
+static SANE_Status usb_low_set_io_3 (ma1017 * chip, SANE_Bool is_high);
+
+static SANE_Status
+usb_low_set_led_light_all (ma1017 * chip, SANE_Bool is_light_all);
+
+/* A24 */
+static SANE_Status usb_low_get_a24 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_ad_timing (ma1017 * chip, SANE_Byte pattern);
+
+/* A25 + A26 */
+static SANE_Status usb_low_set_serial_byte1 (ma1017 * chip, SANE_Byte data);
+
+static SANE_Status usb_low_set_serial_byte2 (ma1017 * chip, SANE_Byte data);
+
+/* A27 */
+static SANE_Status usb_low_get_a27 (ma1017 * chip, SANE_Byte * value);
+
+static SANE_Status usb_low_set_serial_format (ma1017 * chip, SANE_Byte data);
+
+/* A31 */
+static SANE_Status usb_low_get_home_sensor (ma1017 * chip);
+
+/* Special Mode */
+static SANE_Status usb_low_start_rowing (ma1017 * chip);
+
+static SANE_Status usb_low_stop_rowing (ma1017 * chip);
+
+static SANE_Status usb_low_wait_rowing_stop (ma1017 * chip);
+
+/* Global functions */
+static SANE_Status usb_low_read_all_registers (ma1017 * chip);
+
+static SANE_Status
+usb_low_get_row (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left);
+
+static SANE_Status
+usb_low_get_row_direct (ma1017 * chip, SANE_Byte * data,
+ SANE_Word * lines_left);
+
+static SANE_Status
+usb_low_get_row_resample (ma1017 * chip, SANE_Byte * data,
+ SANE_Word * lines_left);
+
+/* Direct access */
+static SANE_Status usb_low_wait_rowing (ma1017 * chip);
+
+static SANE_Status
+usb_low_read_rows (ma1017 * chip, SANE_Byte * data, SANE_Word byte_count);
+
+static SANE_Status
+usb_low_write_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte data);
+
+static SANE_Status
+usb_low_read_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte * data);
+
+static SANE_Status
+usb_low_identify_scanner (SANE_Int fd, Mustek_Type * scanner_type);
+
+static SANE_Status usb_low_open (ma1017 * chip, const char *devname);
+
+static SANE_Status usb_low_close (ma1017 * chip);
+
+#endif /* defined mustek_usb_low_h */
diff --git a/backend/mustek_usb_mid.c b/backend/mustek_usb_mid.c
new file mode 100644
index 0000000..9ca51d9
--- /dev/null
+++ b/backend/mustek_usb_mid.c
@@ -0,0 +1,2688 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001, 2002 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#include "mustek_usb_mid.h"
+#include "mustek_usb_low.c"
+
+/* ------------------ sensor NEC 3797 600 CIS functions ------------------- */
+
+static SANE_Word usb_mid_n600_optical_x_dpi[] =
+ { 600, 400, 300, 200, 100, 50, 0 };
+
+SANE_Status
+usb_mid_n600_prepare_rgb_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_rgb_600_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_n600_prepare_rgb_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_rgb_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_rgb_400_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_n600_prepare_rgb_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_rgb_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_rgb_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_n600_prepare_rgb_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_rgb_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_rgb_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_n600_prepare_rgb_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_rgb_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_rgb_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_n600_prepare_rgb_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_rgb_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_rgb_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 2));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_n600_prepare_rgb_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_mono_600_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_n600_prepare_mono_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_mono_400_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_n600_prepare_mono_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_mono_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_n600_prepare_mono_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_mono_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_n600_prepare_mono_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_mono_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_n600_prepare_mono_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_n600_prepare_mono_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 2));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_n600_prepare_mono_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_n600_prepare_rgb: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_n600_prepare_rgb_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_n600_prepare_rgb_100_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_n600_prepare_rgb_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_n600_prepare_rgb_300_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_n600_prepare_rgb_400_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_n600_prepare_rgb_600_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_n600_prepare_rgb: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ DBG (6, "usb_mid_n600_prepare_rgb: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_n600_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_n600_prepare_mono: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_n600_prepare_mono_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_n600_prepare_mono_100_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_n600_prepare_mono_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_n600_prepare_mono_300_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_n600_prepare_mono_400_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_n600_prepare_mono_600_dpi (chip);
+ break;
+ default:
+ DBG (6, "usb_mid_n600_prepare_mono: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* ---------------------- sensor 600 CIS functions ----------------------- */
+
+static SANE_Word usb_mid_c600_optical_x_dpi[] =
+ { 600, 400, 300, 200, 150, 100, 50, 0 };
+
+SANE_Status
+usb_mid_c600_prepare_rgb_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_600_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_400_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P4P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_150_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 2));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_rgb_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 2));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_rgb_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_600_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_400_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P4P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_150_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 2));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c600_prepare_mono_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 2));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c600_prepare_mono_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_c600_prepare_rgb: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_c600_prepare_rgb_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_c600_prepare_rgb_100_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_c600_prepare_rgb_150_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_c600_prepare_rgb_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_c600_prepare_rgb_300_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_c600_prepare_rgb_400_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_c600_prepare_rgb_600_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_c600_prepare_rgb: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ DBG (6, "usb_mid_c600_prepare_rgb: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c600_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_c600_prepare_mono: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_c600_prepare_mono_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_c600_prepare_mono_100_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_c600_prepare_mono_150_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_c600_prepare_mono_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_c600_prepare_mono_300_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_c600_prepare_mono_400_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_c600_prepare_mono_600_dpi (chip);
+ break;
+ default:
+ DBG (6, "usb_mid_c600_prepare_mono: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* ------------------- sensor 300/600 CIS functions ----------------------- */
+
+
+static SANE_Word usb_mid_c300600_optical_x_dpi[] =
+ { 600, 400, 300, 200, 150, 100, 50, 0 };
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_600_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_400_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_150_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_rgb_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_rgb_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_rgb_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_600_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_400_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_150_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300600_prepare_mono_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6));
+ RIE (usb_low_set_soft_resample (chip, 1));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300600_prepare_mono_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_c300600_prepare_rgb: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_c300600_prepare_rgb_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_c300600_prepare_rgb_100_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_c300600_prepare_rgb_150_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_c300600_prepare_rgb_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_c300600_prepare_rgb_300_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_c300600_prepare_rgb_400_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_c300600_prepare_rgb_600_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_c300600_prepare_rgb: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300600_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_c300600_prepare_mono_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_c300600_prepare_mono_100_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_c300600_prepare_mono_150_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_c300600_prepare_mono_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_c300600_prepare_mono_300_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_c300600_prepare_mono_400_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_c300600_prepare_mono_600_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_c300600_prepare_mono: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* ---------------------- sensor 300 CIS functions ----------------------- */
+
+static SANE_Word usb_mid_c300_optical_x_dpi[] = { 300, 200, 150, 100, 50, 0 };
+
+SANE_Status
+usb_mid_c300_prepare_rgb_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_rgb_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300_prepare_rgb_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_rgb_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_rgb_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300_prepare_rgb_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_rgb_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_rgb_150_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300_prepare_rgb_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_rgb_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_rgb_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300_prepare_rgb_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_rgb_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_rgb_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_FALSE));
+ DBG (6, "usb_mid_c300_prepare_rgb_50_dpi: start\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_mono_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_mono_300_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_c300_prepare_mono_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_mono_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_mono_200_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_c300_prepare_mono_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_mono_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_mono_150_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_c300_prepare_mono_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_mono_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_mono_100_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_c300_prepare_mono_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_mono_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_c300_prepare_mono_50_dpi: start\n");
+ RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6));
+ RIE (usb_low_set_led_light_all (chip, SANE_TRUE));
+ DBG (6, "usb_mid_c300_prepare_mono_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_c300_prepare_rgb: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_c300_prepare_rgb_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_c300_prepare_rgb_100_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_c300_prepare_rgb_150_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_c300_prepare_rgb_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_c300_prepare_rgb_300_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_c300_prepare_rgb: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_c300_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_c300_prepare_mono: start\n");
+ switch (dpi)
+ {
+ case 50:
+ return usb_mid_c300_prepare_mono_50_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_c300_prepare_mono_100_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_c300_prepare_mono_150_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_c300_prepare_mono_200_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_c300_prepare_mono_300_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_c300_prepare_mono: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* -------------------------- sensor functions ---------------------------- */
+
+SANE_Bool
+usb_mid_sensor_is600_mode (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->sensor == ST_CANON300)
+ {
+ DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, FALSE\n",
+ (void *) chip, dpi);
+ return SANE_FALSE;
+ }
+ else if ((chip->sensor == ST_CANON600) || (chip->sensor == ST_NEC600))
+ {
+ DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, TRUE\n",
+ (void *) chip, dpi);
+ return SANE_TRUE;
+ }
+ else
+ {
+ switch (dpi)
+ {
+ case 300:
+ case 150:
+ case 100:
+ case 50:
+ DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, FALSE\n",
+ (void *) chip, dpi);
+ return SANE_FALSE;
+ case 600:
+ case 400:
+ case 200:
+ DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, TRUE\n",
+ (void *) chip, dpi);
+ return SANE_TRUE;
+ default:
+ DBG (3, "usb_mid_sensor_is600_mode: unmatched dpi: %d\n", dpi);
+ return SANE_FALSE;
+ break;
+ }
+
+ }
+
+ return SANE_FALSE;
+}
+
+static SANE_Status
+usb_mid_sensor_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->sensor == ST_CANON300)
+ return usb_mid_c300_prepare_rgb (chip, dpi);
+ else if (chip->sensor == ST_CANON600)
+ return usb_mid_c600_prepare_rgb (chip, dpi);
+ else if (chip->sensor == ST_NEC600)
+ return usb_mid_n600_prepare_rgb (chip, dpi);
+ else
+ return usb_mid_c300600_prepare_rgb (chip, dpi);
+
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+usb_mid_sensor_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->sensor == ST_CANON300)
+ return usb_mid_c300_prepare_mono (chip, dpi);
+ else if (chip->sensor == ST_CANON600)
+ return usb_mid_c600_prepare_mono (chip, dpi);
+ else if (chip->sensor == ST_NEC600)
+ return usb_mid_n600_prepare_mono (chip, dpi);
+ else
+ return usb_mid_c300600_prepare_mono (chip, dpi);
+
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+usb_mid_sensor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi)
+{
+ SANE_Word *dpi_list;
+ SANE_Word i;
+
+ if (!dpi)
+ return SANE_STATUS_INVAL;
+
+ DBG (5, "usb_mid_sensor_get_dpi: chip->sensor=%d\n", chip->sensor);
+
+ if (chip->sensor == ST_CANON300)
+ dpi_list = usb_mid_c300_optical_x_dpi;
+ else if (chip->sensor == ST_CANON300600)
+ dpi_list = usb_mid_c300600_optical_x_dpi;
+ else if (chip->sensor == ST_CANON600)
+ dpi_list = usb_mid_c600_optical_x_dpi;
+ else if (chip->sensor == ST_NEC600)
+ dpi_list = usb_mid_n600_optical_x_dpi;
+ else
+ return SANE_STATUS_INVAL;
+
+ for (i = 0; dpi_list[i] != 0; i++)
+ {
+ if (wanted_dpi > dpi_list[i])
+ break;
+ }
+ if (i)
+ i--;
+ *dpi = dpi_list[i];
+ DBG (5, "usb_mid_sensor_get_dpi: wanted %d dpi, got %d dpi\n", wanted_dpi,
+ *dpi);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ---------------1200 dpi motor function declarations --------------------- */
+
+static SANE_Word usb_mid_motor1200_optical_dpi[] =
+ { 1200, 600, 400, 300, 200, 150, 100, 50, 0 };
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_1200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_1200_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_FALSE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_1200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_600_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_FALSE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_400_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_200_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_150_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_100_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_50_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_1200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_1200_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_1200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_600_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 7, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 8, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 9, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 10, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 11, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 12, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 13, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 14, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 15, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 16, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 17, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 18, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 19, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 20, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 21, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 22, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 23, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 24, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 25, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 26, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 26));
+ RIE (usb_low_set_cmt_second_position (chip, 24));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_400_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_400_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_400_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 7, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 8, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 9, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 10, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 11, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 12, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 13, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 14, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 15, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 16, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 17, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 18, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 19, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 20, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 21, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 22, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 23, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 24, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 25, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 26, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 26));
+ RIE (usb_low_set_cmt_second_position (chip, 24));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_200_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_150_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_100_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_50_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_half_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_half_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_half_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_bi_full_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_FALSE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_half_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_half_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_half_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_bi_full_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_rgb: start\n");
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ RIE (usb_low_set_motor_direction (chip, SANE_FALSE));
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ switch (dpi)
+ {
+ case 1200:
+ return usb_mid_motor1200_prepare_rgb_1200_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_motor1200_prepare_rgb_600_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_motor1200_prepare_rgb_400_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_motor1200_prepare_rgb_300_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_motor1200_prepare_rgb_200_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_motor1200_prepare_rgb_150_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_motor1200_prepare_rgb_100_dpi (chip);
+ break;
+ case 50:
+ return usb_mid_motor1200_prepare_rgb_50_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor1200_prepare_rgb: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ SANE_Status status;
+
+ DBG (3, "usb_mid_motor1200_prepare_mono: start\n");
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ RIE (usb_low_set_motor_direction (chip, SANE_FALSE));
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ switch (dpi)
+ {
+ case 1200:
+ return usb_mid_motor1200_prepare_mono_1200_dpi (chip);
+ break;
+ case 600:
+ return usb_mid_motor1200_prepare_mono_600_dpi (chip);
+ break;
+ case 400:
+ return usb_mid_motor1200_prepare_mono_400_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_motor1200_prepare_mono_300_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_motor1200_prepare_mono_200_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_motor1200_prepare_mono_150_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_motor1200_prepare_mono_100_dpi (chip);
+ break;
+ case 50:
+ return usb_mid_motor1200_prepare_mono_50_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor1200_prepare_mono_: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_calibrate_rgb: start\n");
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ RIE (usb_low_set_motor_direction (chip, SANE_FALSE));
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ switch (dpi)
+ {
+ case 1200:
+ case 400:
+ case 300:
+ return usb_mid_motor1200_prepare_rgb_half_300_dpi (chip);
+ break;
+ case 600:
+ case 200:
+ case 150:
+ return usb_mid_motor1200_prepare_rgb_bi_full_300_dpi (chip);
+ break;
+ case 100:
+ case 50:
+ return usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor1200_prepare_calibrate_rgb: unmatched dpi: "
+ "%d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_calibrate_mono: start\n");
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ RIE (usb_low_set_motor_direction (chip, SANE_FALSE));
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ switch (dpi)
+ {
+ case 1200:
+ case 600:
+ case 400:
+ return usb_mid_motor1200_prepare_mono_half_300_dpi (chip);
+ break;
+ case 300:
+ case 200:
+ return usb_mid_motor1200_prepare_mono_bi_full_300_dpi (chip);
+ break;
+ case 150:
+ case 100:
+ case 50:
+ return usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor1200_prepare_calibrate_mono: unmatched dpi: %d\n",
+ dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_step (ma1017 * chip, SANE_Word step_count)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_step: start\n");
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ /* Make it in 600dpi */
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ if (step_count == 1)
+ {
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 1));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, step_count));
+ }
+ else if (step_count % 2 == 1)
+ {
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 1));
+ RIE (usb_low_set_cmt_loop_count (chip, (step_count - 1) / 2));
+ }
+ else
+ {
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, step_count / 2));
+ }
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_step: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_home (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_home: start\n");
+ if (chip->sensor == ST_NEC600)
+ RIE (usb_low_set_motor_movement
+ (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ else
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ /* Make it in 600dpi */
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */
+ RIE (usb_low_move_motor_home (chip, SANE_TRUE, SANE_TRUE));
+ DBG (6, "usb_mid_motor1200_prepare_home: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor1200_prepare_adjust (ma1017 * chip, Channel channel)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor1200_prepare_adjust: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, channel, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, channel, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, channel, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ DBG (6, "usb_mid_motor1200_prepare_adjust: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Word
+usb_mid_motor1200_rgb_capability (SANE_Word dpi)
+{
+ DBG (6, "usb_mid_motor1200_rgb_capability: start\n");
+ switch (dpi)
+ {
+ case 1200:
+ case 400:
+ return 3008; /* 2816 */ ;
+ case 600:
+ return 3008;
+ case 200:
+ return 5056;
+ case 300:
+ return 3008;
+ case 150:
+ return 5056;
+ case 100:
+ case 50:
+ return 10048;
+ default:
+ DBG (3, "usb_mid_motor1200_rgb_capability: unmatched dpi: %d\n", dpi);
+ return 0;
+ }
+}
+
+SANE_Word
+usb_mid_motor1200_mono_capability (SANE_Word dpi)
+{
+ DBG (5, "usb_mid_motor1200_mono_capability: start\n");
+ switch (dpi)
+ {
+ case 1200:
+ case 400:
+ return 3008;
+ case 600:
+ return 3008;
+ case 200:
+ return 5056;
+ case 300:
+ return 5056;
+ case 150:
+ case 100:
+ case 50:
+ return 10048;
+ default:
+ DBG (3, "usb_mid_motor1200_mono_capability: unmatched dpi: %d\n", dpi);
+ return 0;
+ }
+}
+
+/* ---------------600 dpi motor function declarations --------------------- */
+
+
+static SANE_Word usb_mid_motor600_optical_dpi[] =
+ { 600, 300, 200, 150, 100, 50, 0 };
+
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_600_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_FALSE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 4));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_FALSE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_200_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 5));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_150_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_100_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 5));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_50_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_600_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_600_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_600_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_200_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_200_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_200_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_mid_motor600_prepare_mono_150_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_150_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_150_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_100_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_100_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_100_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_50_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_50_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_50_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_half_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_half_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_half_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_rgb_bi_full_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_rgb_bi_full_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 6));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_FALSE));
+ DBG (6, "usb_mid_motor600_prepare_rgb_bi_full_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_half_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_half_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_half_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono_bi_full_300_dpi (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_mono_bi_full_300_dpi: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_mono_bi_full_300_dpi: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_mid_motor600_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_motor600_prepare_rgb: start\n");
+ switch (dpi)
+ {
+ case 600:
+ return usb_mid_motor600_prepare_rgb_600_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_motor600_prepare_rgb_300_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_motor600_prepare_rgb_200_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_motor600_prepare_rgb_150_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_motor600_prepare_rgb_100_dpi (chip);
+ break;
+ case 50:
+ return usb_mid_motor600_prepare_rgb_50_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor600_prepare_rgb: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ DBG (6, "usb_mid_motor600_prepare_mono: start\n");
+ switch (dpi)
+ {
+ case 600:
+ return usb_mid_motor600_prepare_mono_600_dpi (chip);
+ break;
+ case 300:
+ return usb_mid_motor600_prepare_mono_300_dpi (chip);
+ break;
+ case 200:
+ return usb_mid_motor600_prepare_mono_200_dpi (chip);
+ break;
+ case 150:
+ return usb_mid_motor600_prepare_mono_150_dpi (chip);
+ break;
+ case 100:
+ return usb_mid_motor600_prepare_mono_100_dpi (chip);
+ break;
+ case 50:
+ return usb_mid_motor600_prepare_mono_50_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor600_prepare_mono_: unmatched dpi: %d\n", dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_calibrate_rgb: start\n");
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ RIE (usb_low_set_motor_direction (chip, SANE_FALSE));
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ switch (dpi)
+ {
+ case 600:
+ case 200:
+ return usb_mid_motor600_prepare_rgb_half_300_dpi (chip);
+ break;
+ case 300:
+ case 150:
+ case 100:
+ case 50:
+ return usb_mid_motor600_prepare_rgb_bi_full_300_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor600_prepare_calibrate_rgb: unmatched dpi: %d\n",
+ dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_calibrate_mono: start\n");
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ RIE (usb_low_set_motor_direction (chip, SANE_FALSE));
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ switch (dpi)
+ {
+ case 600:
+ case 200:
+ return usb_mid_motor600_prepare_mono_half_300_dpi (chip);
+ break;
+ case 300:
+ case 150:
+ case 100:
+ case 50:
+ return usb_mid_motor600_prepare_mono_bi_full_300_dpi (chip);
+ break;
+ default:
+ DBG (3, "usb_mid_motor600_prepare_calibrate_mono: unmatched dpi: %d\n",
+ dpi);
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_step (ma1017 * chip, SANE_Word step_count)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_step: start\n");
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE));
+ /* Make it in 300dpi */
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */
+ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE));
+ /* No Motor & Forward */
+ if (step_count == 1)
+ {
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 1));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, step_count));
+ }
+ else if (step_count % 2 == 1)
+ {
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 3));
+ RIE (usb_low_set_cmt_second_position (chip, 1));
+ RIE (usb_low_set_cmt_loop_count (chip, (step_count - 1) / 2));
+ }
+ else
+ {
+ RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, step_count / 2));
+ }
+ RIE (usb_low_enable_motor (chip, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_step: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_home (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_home: start\n");
+ RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_TRUE));
+ /* Make it in 600dpi */
+ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */
+ RIE (usb_low_move_motor_home (chip, SANE_TRUE, SANE_TRUE));
+ DBG (6, "usb_mid_motor600_prepare_home: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_motor600_prepare_adjust (ma1017 * chip, Channel channel)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_motor600_prepare_adjust: start\n");
+ RIE (usb_low_set_cmt_table (chip, 0, channel, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 1, channel, SANE_FALSE, SANE_TRUE));
+ RIE (usb_low_set_cmt_table (chip, 2, channel, SANE_FALSE, SANE_FALSE));
+ RIE (usb_low_set_cmt_table_length (chip, 2));
+ RIE (usb_low_set_cmt_second_position (chip, 0));
+ RIE (usb_low_set_cmt_loop_count (chip, 0xefff));
+ DBG (6, "usb_mid_motor600_prepare_adjust: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Word
+usb_mid_motor600_rgb_capability (SANE_Word dpi)
+{
+ DBG (6, "usb_mid_motor600_rgb_capability: start\n");
+ switch (dpi)
+ {
+ case 600:
+ case 300:
+ case 200:
+ return 2600;
+ case 100:
+ return 4500;
+ case 150:
+ case 50:
+ return 9000;
+ default:
+ DBG (3, "usb_mid_motor600_rgb_capability: unmatched dpi: %d\n", dpi);
+ return 0;
+ }
+}
+
+SANE_Word
+usb_mid_motor600_mono_capability (SANE_Word dpi)
+{
+ DBG (5, "usb_mid_motor600_mono_capability: start\n");
+ switch (dpi)
+ {
+ case 600:
+ case 200:
+ return 2600;
+ case 300:
+ case 100:
+ return 4500;
+ case 150:
+ case 50:
+ return 9000;
+ default:
+ DBG (3, "usb_mid_motor600_mono_capability: unmatched dpi: %d\n", dpi);
+ return 0;
+ }
+}
+
+/* ------------------ motor function declarations ------------------------ */
+static SANE_Status
+usb_mid_motor_prepare_home (ma1017 * chip)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_home (chip);
+ else
+ return usb_mid_motor1200_prepare_home (chip);
+}
+
+static SANE_Status
+usb_mid_motor_prepare_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_rgb (chip, dpi);
+ else
+ return usb_mid_motor1200_prepare_rgb (chip, dpi);
+}
+
+static SANE_Status
+usb_mid_motor_prepare_mono (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_mono (chip, dpi);
+ else
+ return usb_mid_motor1200_prepare_mono (chip, dpi);
+}
+
+static SANE_Status
+usb_mid_motor_prepare_adjust (ma1017 * chip, Channel channel)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_adjust (chip, channel);
+ else
+ return usb_mid_motor1200_prepare_adjust (chip, channel);
+}
+
+static SANE_Status
+usb_mid_motor_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_calibrate_rgb (chip, dpi);
+ else
+ return usb_mid_motor1200_prepare_calibrate_rgb (chip, dpi);
+}
+
+static SANE_Status
+usb_mid_motor_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_calibrate_mono (chip, dpi);
+ else
+ return usb_mid_motor1200_prepare_calibrate_mono (chip, dpi);
+}
+
+static SANE_Status
+usb_mid_motor_prepare_step (ma1017 * chip, SANE_Word step_count)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_prepare_step (chip, step_count);
+ else
+ return usb_mid_motor1200_prepare_step (chip, step_count);
+}
+
+static SANE_Word
+usb_mid_motor_rgb_capability (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_rgb_capability (dpi);
+ else
+ return usb_mid_motor1200_rgb_capability (dpi);
+}
+
+static SANE_Word
+usb_mid_motor_mono_capability (ma1017 * chip, SANE_Word dpi)
+{
+ if (chip->motor == MT_600)
+ return usb_mid_motor600_mono_capability (dpi);
+ else
+ return usb_mid_motor1200_mono_capability (dpi);
+}
+
+
+static SANE_Status
+usb_mid_motor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi)
+{
+ SANE_Word *dpi_list;
+ SANE_Word i;
+
+ if (!dpi)
+ return SANE_STATUS_INVAL;
+
+ if (chip->motor == MT_600)
+ dpi_list = usb_mid_motor600_optical_dpi;
+ else if (chip->motor == MT_1200)
+ dpi_list = usb_mid_motor1200_optical_dpi;
+ else
+ return SANE_STATUS_INVAL;
+
+ for (i = 0; dpi_list[i] != 0; i++)
+ {
+ if (wanted_dpi > dpi_list[i])
+ break;
+ }
+ if (i)
+ i--;
+ *dpi = dpi_list[i];
+ DBG (5, "usb_mid_motor_get_dpi: wanted %d dpi, got %d dpi\n", wanted_dpi,
+ *dpi);
+ return SANE_STATUS_GOOD;
+}
+
+/* ----------------------------- frontend ------------------------------- */
+
+SANE_Status
+usb_mid_front_set_front_end_mode (ma1017 * chip, SANE_Byte mode)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_front_end_mode: start\n");
+ RIE (usb_low_set_serial_format (chip, mode));
+ DBG (6, "usb_mid_front_set_front_end_mode: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_enable (ma1017 * chip, SANE_Bool is_enable)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_enable: start\n");
+ RIE (usb_low_turn_frontend_mode (chip, is_enable));
+ DBG (6, "usb_mid_front_enable: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_top_reference (ma1017 * chip, SANE_Byte top)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_top_reference: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x00));
+ RIE (usb_low_set_serial_byte2 (chip, top));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_top_reference: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_red_offset (ma1017 * chip, SANE_Byte offset)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_red_offset: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x10));
+ RIE (usb_low_set_serial_byte2 (chip, offset));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_red_offset: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_green_offset (ma1017 * chip, SANE_Byte offset)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_green_offset: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x50));
+ RIE (usb_low_set_serial_byte2 (chip, offset));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_green_offset: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_blue_offset (ma1017 * chip, SANE_Byte offset)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_blue_offset: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x30));
+ RIE (usb_low_set_serial_byte2 (chip, offset));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_blue_offset: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+#if 0
+/* CCD */
+SANE_Word
+usb_mid_frontend_max_offset_index (ma1017 * chip)
+{
+ DBG (6, "usb_mid_front_max_offset_index: start (chip = %p)\n", chip);
+
+ DBG (6, "usb_mid_front_max_offset_index: exit\n");
+ return (OFFSET_TABLE_SIZE - 1);
+}
+#endif
+
+SANE_Status
+usb_mid_front_set_red_pga (ma1017 * chip, SANE_Byte pga)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_red_pga: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x40));
+ RIE (usb_low_set_serial_byte2 (chip, pga));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_red_pga: start\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_green_pga (ma1017 * chip, SANE_Byte pga)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_green_pga: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x20));
+ RIE (usb_low_set_serial_byte2 (chip, pga));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_green_pga: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_blue_pga (ma1017 * chip, SANE_Byte pga)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_blue_pga: start\n");
+ RIE (usb_mid_front_enable (chip, SANE_TRUE));
+ RIE (usb_low_set_serial_byte1 (chip, 0x60));
+ RIE (usb_low_set_serial_byte2 (chip, pga));
+ RIE (usb_mid_front_enable (chip, SANE_FALSE));
+ DBG (6, "usb_mid_front_set_blue_pga: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+usb_mid_front_set_rgb_signal (ma1017 * chip)
+{
+ SANE_Status status;
+
+ DBG (6, "usb_mid_front_set_rgb_signal: start\n");
+ RIE (usb_low_set_red_ref (chip, 0xEF));
+ RIE (usb_low_set_green_ref (chip, 0xF7));
+ RIE (usb_low_set_blue_ref (chip, 0xFF));
+ DBG (6, "usb_mid_front_set_rgb_signal: exit\n");
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/mustek_usb_mid.h b/backend/mustek_usb_mid.h
new file mode 100644
index 0000000..d8701a7
--- /dev/null
+++ b/backend/mustek_usb_mid.h
@@ -0,0 +1,390 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Mustek.
+ Originally maintained by Tom Wang <tom.wang@mustek.com.tw>
+
+ Copyright (C) 2001, 2002 by Henning Meier-Geinitz.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Mustek 1200UB and similar
+ USB flatbed scanners. */
+
+#ifndef mustek_usb_mid_h
+#define mustek_usb_mid_h
+
+#include "mustek_usb_low.h"
+#include "../include/sane/sane.h"
+
+/* ---------------------------------- macros ------------------------------ */
+
+
+/* ---------------- sensor NEC 600 CCD function declarations -------------- */
+
+static SANE_Status usb_mid_n600_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_n600_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_n600_prepare_rgb_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_rgb_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_rgb_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_rgb_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_rgb_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_rgb_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_mono_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_mono_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_mono_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_mono_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_mono_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_n600_prepare_mono_50_dpi (ma1017 * chip);
+
+/* ----------------- sensor 600 CIS function declarations ----------------- */
+
+static SANE_Status usb_mid_c600_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_c600_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_c600_prepare_rgb_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_rgb_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_rgb_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_rgb_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_rgb_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_rgb_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_rgb_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c600_prepare_mono_50_dpi (ma1017 * chip);
+
+/* -------------- sensor 300/600 CIS function declarations ---------------- */
+
+static SANE_Status usb_mid_c300600_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_c300600_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_rgb_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300600_prepare_mono_50_dpi (ma1017 * chip);
+
+/* ----------------- sensor 300 CIS function declarations ----------------- */
+
+static SANE_Status usb_mid_c300_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_c300_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_c300_prepare_rgb_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_rgb_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_rgb_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_rgb_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_rgb_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_mono_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_mono_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_mono_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_mono_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_c300_prepare_mono_50_dpi (ma1017 * chip);
+
+/* --------------------- sensor function declarations -------------------- */
+
+static SANE_Bool usb_mid_sensor_is600_mode (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_sensor_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_sensor_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_sensor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi);
+
+/* ------------------- motor 1200 function declarations ------------------ */
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_1200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_1200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_400_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_mono_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor1200_prepare_rgb_half_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_rgb_bi_full_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_mono_half_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_mono_bi_full_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor1200_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor1200_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor1200_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor1200_prepare_step (ma1017 * chip, SANE_Word step_count);
+
+static SANE_Status usb_mid_motor1200_prepare_home (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor1200_prepare_adjust (ma1017 * chip, Channel channel);
+
+static SANE_Word usb_mid_motor1200_rgb_capability (SANE_Word dpi);
+
+static SANE_Word usb_mid_motor1200_mono_capability (SANE_Word dpi);
+
+/* ---------------600 dpi motor function declarations --------------------- */
+
+static SANE_Status usb_mid_motor600_prepare_rgb_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_rgb_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_rgb_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_rgb_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_rgb_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_rgb_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_600_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_200_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_150_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_100_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_50_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_rgb_half_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor600_prepare_rgb_bi_full_300_dpi (ma1017 * chip);
+
+static SANE_Status usb_mid_motor600_prepare_mono_half_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor600_prepare_mono_bi_full_300_dpi (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor600_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor600_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor600_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor600_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor600_prepare_step (ma1017 * chip, SANE_Word step_count);
+
+static SANE_Status usb_mid_motor600_prepare_home (ma1017 * chip);
+
+static SANE_Status
+usb_mid_motor600_prepare_adjust (ma1017 * chip, Channel channel);
+
+static SANE_Word usb_mid_motor600_rgb_capability (SANE_Word dpi);
+
+static SANE_Word usb_mid_motor600_mono_capability (SANE_Word dpi);
+
+/* ------------------ motor function declarations ------------------------ */
+
+static SANE_Status usb_mid_motor_prepare_home (ma1017 * chip);
+
+static SANE_Status usb_mid_motor_prepare_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status usb_mid_motor_prepare_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor_prepare_adjust (ma1017 * chip, Channel channel);
+
+static SANE_Status
+usb_mid_motor_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor_prepare_step (ma1017 * chip, SANE_Word step_count);
+
+static SANE_Word usb_mid_motor_rgb_capability (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Word usb_mid_motor_mono_capability (ma1017 * chip, SANE_Word dpi);
+
+static SANE_Status
+usb_mid_motor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi);
+
+/* --------------------- frontend function declarations ------------------- */
+
+
+static SANE_Status
+usb_mid_front_set_front_end_mode (ma1017 * chip, SANE_Byte mode);
+
+static SANE_Status usb_mid_front_enable (ma1017 * chip, SANE_Bool is_enable);
+
+static SANE_Status
+usb_mid_front_set_top_reference (ma1017 * chip, SANE_Byte top);
+
+static SANE_Status
+usb_mid_front_set_red_offset (ma1017 * chip, SANE_Byte offset);
+
+static SANE_Status
+usb_mid_front_set_green_offset (ma1017 * chip, SANE_Byte offset);
+
+static SANE_Status
+usb_mid_front_set_blue_offset (ma1017 * chip, SANE_Byte offset);
+
+static SANE_Status usb_mid_front_set_red_pga (ma1017 * chip, SANE_Byte pga);
+
+static SANE_Status usb_mid_front_set_green_pga (ma1017 * chip, SANE_Byte pga);
+
+static SANE_Status usb_mid_front_set_blue_pga (ma1017 * chip, SANE_Byte pga);
+
+static SANE_Status usb_mid_front_set_rgb_signal (ma1017 * chip);
+
+#if 0
+/* CCD */
+static SANE_Word usb_mid_frontend_max_offset_index (ma1017 * chip);
+#define OFFSET_TABLE_SIZE 256
+#endif
+
+#endif /* mustek_usb_mid_h */
diff --git a/backend/nec.c b/backend/nec.c
new file mode 100644
index 0000000..c7af955
--- /dev/null
+++ b/backend/nec.c
@@ -0,0 +1,3717 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000-2001 Kazuya Fukuda, based on sharp.c, which is
+ based on canon.c.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for NEC flatbed scanners. */
+
+/*
+ Version 0.12
+ - Remove references to sharp backend (grep for "JX").
+ - Check for HAVE_SYS_SHM_H before including sys/shm.h and
+ disable shared memory support if necessary.
+ - free devlist allocated in sane_get_devices() in sane_exit()
+ - resolution setting bug fixed(PC-IN500/4C 10dpi step)
+ - remove resolution list
+ Version 0.11
+ - get_data_buffer_status is not called in sane_get_parameter and
+ sane_read_direct, sane_read_shuffled.
+ - change some #include <> to ""
+ Version 0.10
+ - First release!
+ - suppoted scanner
+ PC-IN500/4C available
+ MultiReder 300U/300S series not available
+ MultiReder 600U/600S series not available
+ MultiReader PetiScan series not available
+*/
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+/* QUEUEDEBUG should be undefined unless you want to play
+ with the sanei_scsi.c under Linux and/or with the Linux's SG driver,
+ or your suspect problems with command queueing
+*/
+#define QUEUEDEBUG
+/*#define DEBUG*/
+#ifdef DEBUG
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+/* USE_FORK: fork a special reader process
+ disable shared memory support.
+*/
+#if 0
+#ifdef HAVE_SYS_SHM_H
+#define USE_FORK
+#endif
+#endif
+
+#ifdef USE_FORK
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#endif /* USE_FORK */
+
+#ifndef USE_CUSTOM_GAMMA
+#define USE_CUSTOM_GAMMA
+#endif
+#ifndef USE_COLOR_THRESHOLD
+#define USE_COLOR_THRESHOLD
+#endif
+/* enable a short list of some standard resolutions. XSane provides
+ its own resolution list; therefore its is generally not reasonable
+ to enable this list, if you mainly using XSane. But it might be handy
+ if you are working with xscanimage
+*/
+/* #define USE_RESOLUTION_LIST */
+
+#define BACKEND_NAME nec
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define DEFAULT_MUD_1200 1200
+
+#define PIX_TO_MM(x, mud) ((x) * 25.4 / mud)
+#define MM_TO_PIX(x, mud) ((x) * mud / 25.4)
+
+#include "../include/sane/sanei_config.h"
+#define NEC_CONFIG_FILE "nec.conf"
+
+#include "nec.h"
+
+static int num_devices = 0;
+static NEC_Device *first_dev = NULL;
+static NEC_Scanner *first_handle = NULL;
+static const SANE_Device **devlist = 0;
+
+typedef enum
+ {
+ MODES_LINEART = 0,
+ MODES_GRAY,
+ MODES_COLOR,
+ MODES_LINEART_COLOR
+ }
+Modes;
+
+#define M_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define M_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define M_LINEART_COLOR "Lineart Color"
+#define M_COLOR SANE_VALUE_SCAN_MODE_COLOR
+static const SANE_String_Const mode_list[] =
+{
+#if 0
+ M_LINEART, M_GRAY, M_LINEART_COLOR, M_COLOR,
+#endif
+ M_LINEART, M_GRAY, M_COLOR,
+ 0
+};
+
+#define M_BILEVEL "none"
+#define M_BAYER "Dither Bayer"
+#define M_SPIRAL "Dither Spiral"
+#define M_DISPERSED "Dither Dispersed"
+#define M_ERRDIFFUSION "Error Diffusion"
+
+#define M_DITHER1 "Dither 1"
+#define M_DITHER2 "Dither 2"
+#define M_DITHER3 "Dither 3"
+#define M_DITHERUSER "User defined"
+
+static const SANE_String_Const halftone_list[] =
+{
+ M_BILEVEL, M_DITHER1, M_DITHER2, M_DITHER3,
+ 0
+};
+
+#define LIGHT_GREEN "green"
+#define LIGHT_RED "red"
+#define LIGHT_BLUE "blue"
+#define LIGHT_NONE "none"
+#define LIGHT_WHITE "white"
+
+static const SANE_String_Const light_color_list[] =
+{
+ LIGHT_GREEN, LIGHT_RED, LIGHT_BLUE, LIGHT_NONE,
+ 0
+};
+
+/* possible values for ADF/FSU selection */
+static SANE_String use_adf = "Automatic Document Feeder";
+static SANE_String use_fsu = "Transparency Adapter";
+static SANE_String use_simple = "Flatbed";
+
+#define HAVE_FSU 1
+#define HAVE_ADF 2
+
+/* The follow #defines are used in NEC_Scanner.adf_fsu_mode
+ and as indexes for the arrays x_ranges, y_ranges in NEC_Device
+*/
+#define SCAN_SIMPLE 0
+#define SCAN_WITH_FSU 1
+#define SCAN_WITH_ADF 2
+
+#define LOAD_PAPER 1
+#define UNLOAD_PAPER 0
+
+#define PAPER_MAX 10
+#define W_LETTER "11\"x17\""
+#define INVOICE "8.5\"x5.5\""
+static const SANE_String_Const paper_list_pcinxxx[] =
+{
+ "A3", "A4", "A5", "A6", "B4", "B5",
+ W_LETTER, "Legal", "Letter", INVOICE,
+ 0
+};
+
+static const SANE_String_Const paper_list_pcin500[] =
+{
+ "A4", "A5", "A6", "B5",
+ 0
+};
+
+
+#define CRT1 "CRT1"
+#define CRT2 "CRT2"
+#define PRINTER1 "PRINTER1"
+#define PRINTER2 "PRINTER2"
+#define NONE "NONE"
+/* #define CUSTOM "CUSTOM" */
+static const SANE_String_Const gamma_list[] =
+{
+ CRT1, CRT2, PRINTER1, PRINTER2, NONE,
+ 0
+};
+
+#if 0
+#define SPEED_NORMAL "Normal"
+#define SPEED_FAST "Fast"
+static const SANE_String_Const speed_list[] =
+{
+ SPEED_NORMAL, SPEED_FAST,
+ 0
+};
+#endif
+
+#ifdef USE_RESOLUTION_LIST
+#define RESOLUTION_MAX_PCINXXX 8
+static const SANE_String_Const resolution_list_pcinxxx[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "600", "Select",
+ 0
+};
+
+#define RESOLUTION_MAX_PCIN500 8
+static const SANE_String_Const resolution_list_pcin500[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "480", "Select",
+ 0
+};
+#endif
+
+#define EDGE_NONE "None"
+#define EDGE_MIDDLE "Middle"
+#define EDGE_STRONG "Strong"
+#define EDGE_BLUR "Blur"
+static const SANE_String_Const edge_emphasis_list[] =
+{
+ EDGE_NONE, EDGE_MIDDLE, EDGE_STRONG, EDGE_BLUR,
+ 0
+};
+
+#ifdef USE_CUSTOM_GAMMA
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+#endif
+
+static SANE_Status
+sense_handler(int fd, u_char *sense_buffer, void *ss)
+{
+ int sense_key;
+ NEC_Sense_Data *sdat = (NEC_Sense_Data *) ss;
+
+ fd = fd; /* silence compilation warnings */
+
+ #define add_sense_code sense_buffer[12]
+ #define add_sense_qual sense_buffer[13]
+
+ memcpy(sdat->sb, sense_buffer, 16);
+
+ DBG(10, "sense code: %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ sense_buffer[0], sense_buffer[1], sense_buffer[2], sense_buffer[3],
+ sense_buffer[4], sense_buffer[5], sense_buffer[6], sense_buffer[7],
+ sense_buffer[8], sense_buffer[9], sense_buffer[10], sense_buffer[11],
+ sense_buffer[12], sense_buffer[13], sense_buffer[14], sense_buffer[15]);
+
+ sense_key = sense_buffer[1] & 0x0F;
+ /* do we have additional information ? */
+ if (sense_buffer[7] >= 5)
+ {
+ if (sdat->model == PCIN500)
+ {
+ switch (sense_key)
+ {
+ case 0x02: /* not ready */
+ switch (add_sense_code)
+ {
+ case 0x80:
+ switch (add_sense_qual & 0xf0)
+ {
+ case 0x10:
+ DBG(1, "Scanner not ready: memory error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x20:
+ DBG(1, "Scanner not ready: hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x30:
+ DBG(1, "Scanner not ready: optical error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x40:
+ DBG(1, "Scanner not ready: optical error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x50:
+ DBG(1, "Scanner not ready: marker error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x60:
+ DBG(1, "Scanner not ready: mechanical error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x70:
+ DBG(1, "Scanner not ready: hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x80:
+ DBG(1, "Scanner not ready: hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x90:
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ case 0x03: /* medium error */
+ DBG(5, "medium error: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x04: /* hardware error */
+ DBG(1, "general hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x05: /* illegal request */
+ DBG(10, "error: illegal request\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x06: /* unit attention */
+ DBG(5, "unit attention: exact reason not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x0B: /* data remains */
+ DBG(5, "error: aborted command\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG(5, "error: sense code not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static u_char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< test_unit_ready ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+request_sense (int fd, void *sense_buf, size_t *sense_size)
+{
+ static u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, SENSE_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< request_sense ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_buf, sense_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+inquiry (int fd, void *inq_buf, size_t *inq_size)
+{
+ static u_char cmd[] = {INQUIRY, 0, 0, 0, INQUIRY_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< inquiry ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), inq_buf, inq_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select_mud (int fd, int mud)
+{
+ static u_char cmd[6 + MODEPARAM_LEN] =
+ {MODE_SELECT6, 0x10, 0, 0, MODEPARAM_LEN, 0};
+ mode_select_param *mp;
+ SANE_Status status;
+ DBG (11, "<< mode_select_mud ");
+
+ mp = (mode_select_param *)(cmd + 6);
+ memset (mp, 0, MODEPARAM_LEN);
+ mp->mode_param_header1 = 11;
+ mp->page_code = 3;
+ mp->page_length = 6;
+ mp->mud[0] = mud >> 8;
+ mp->mud[1] = mud & 0xFF;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+mode_select_adf_fsu (int fd, int mode)
+{
+ static u_char cmd[6 + MODE_SUBDEV_LEN] =
+ {MODE_SELECT6, 0x10, 0, 0, MODE_SUBDEV_LEN, 0};
+ mode_select_subdevice *mp;
+ SANE_Status status;
+ DBG (11, "<< mode_select_adf_fsu ");
+
+ mp = (mode_select_subdevice *)(cmd + 6);
+ memset (mp, 0, MODE_SUBDEV_LEN);
+ mp->page_code = 0x20;
+ mp->page_length = 26;
+ switch (mode)
+ {
+ case SCAN_SIMPLE:
+ mp->a_mode = 0x40;
+ mp->f_mode = 0x40;
+ break;
+ case SCAN_WITH_FSU:
+ mp->a_mode = 0;
+ mp->f_mode = 0x40;
+ break;
+ case SCAN_WITH_ADF:
+ mp->a_mode = 0x40;
+ mp->f_mode = 0;
+ break;
+ }
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status wait_ready(int fd);
+
+static SANE_Status
+mode_sense (int fd, void *modeparam_buf, size_t * modeparam_size,
+ int page)
+{
+ static u_char cmd[6] = {MODE_SENSE6, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< mode_sense ");
+ cmd[0] = 0x1a;
+ cmd[2] = page;
+ cmd[4] = *modeparam_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), modeparam_buf,
+ modeparam_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+scan (int fd)
+{
+ static u_char cmd[] = {SCAN, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< scan ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+send_diagnostics (int fd)
+{
+ static u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< send_diagnostics ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+set_window (int fd, window_param *wp, int len)
+{
+ static u_char cmd[10 + WINDOW_LEN] =
+ {SET_WINDOW, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ window_param *winp;
+ SANE_Status status;
+ DBG (11, "<< set_window ");
+
+ cmd[8] = len;
+ winp = (window_param *)(cmd + 10);
+ memset (winp, 0, WINDOW_LEN);
+ memcpy (winp, wp, len);
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+
+}
+
+static SANE_Status
+get_window (int fd, void *buf, size_t * buf_size)
+{
+ static u_char cmd[10] = {GET_WINDOW, 0, 0, 0, 0, 0, 0, 0, WINDOW_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< get_window ");
+
+ cmd[8] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+get_data_buffer_status (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10] =
+ {GET_DATA_BUFFER_STATUS, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< get_data_buffer_status ");
+
+ cmd[8] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+#ifdef USE_FORK
+
+/* the following four functions serve simply the purpose
+ to avoid "over-optimised" code when reader_process and
+ read_data wait for the buffer to become ready. The simple
+ while-loops in these functions which check the buffer
+ status may be optimised so that the machine code only
+ operates with registers instead of using the variable
+ values stored in memory. (This is only a workaround -
+ it would be better to set a compiler pragma, which ensures
+ that the program looks into the RAM in these while loops --
+ but unfortunately I could not find appropriate information
+ about this at least for gcc, not to speak about other
+ compilers...
+ Abel)
+*/
+
+static int
+cancel_requested(NEC_Scanner *s)
+{
+ return s->rdr_ctl->cancel;
+}
+
+static SANE_Status
+rdr_status(NEC_Scanner *s)
+{
+ return s->rdr_ctl->status;
+}
+
+static int
+buf_status(NEC_shmem_ctl *s)
+{
+ return s->shm_status;
+}
+
+static int
+reader_running(NEC_Scanner *s)
+{
+ return s->rdr_ctl->running;
+}
+
+static int
+reader_process(NEC_Scanner *s)
+{
+ SANE_Status status;
+ sigset_t sigterm_set;
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int full_count = 0, counted;
+ size_t waitindex, cmdindex;
+ size_t bytes_to_queue;
+ size_t nread;
+ size_t max_bytes_per_read;
+ int max_queue;
+ int i;
+ NEC_shmem_ctl *bc;
+
+ s->rdr_ctl->running = 1;
+ DBG(11, "<< reader_process\n");
+
+ sigemptyset (&sigterm_set);
+
+ bytes_to_queue = s->bytes_to_read;
+
+ max_bytes_per_read = s->dev->info.bufsize / s->params.bytes_per_line;
+ if (max_bytes_per_read)
+ max_bytes_per_read *= s->params.bytes_per_line;
+ else
+ /* this is a really tiny buffer..*/
+ max_bytes_per_read = s->dev->info.bufsize;
+
+ /* wait_ready(s->fd); */
+
+ if (s->dev->info.queued_reads <= s->dev->info.buffers)
+ max_queue = s->dev->info.queued_reads;
+ else
+ max_queue = s->dev->info.buffers;
+ for (i = 0; i < max_queue; i++)
+ {
+ bc = &s->rdr_ctl->buf_ctl[i];
+ if (bytes_to_queue)
+ {
+ nread = bytes_to_queue;
+ if (nread > max_bytes_per_read)
+ nread = max_bytes_per_read;
+ bc->used = nread;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_enter...\n");
+#endif
+ status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
+ bc->buffer,
+ &bc->used,
+ &bc->qid);
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_enter ok\n");
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ bc->shm_status = SHM_BUSY;
+ bc->nreq = bc->used;
+ bytes_to_queue -= bc->nreq;
+ }
+ else
+ {
+ bc->used = 0;
+ bc->shm_status = SHM_EMPTY;
+ }
+ }
+ waitindex = 0;
+ cmdindex = i % s->dev->info.buffers;
+
+ while(s->bytes_to_read > 0)
+ {
+ if (cancel_requested(s))
+ {
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: flushing requests...\n");
+#endif
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: flushing requests ok\n");
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+
+ bc = &s->rdr_ctl->buf_ctl[waitindex];
+ if (bc->shm_status == SHM_BUSY)
+ {
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: waiting for data %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_wait...\n");
+#endif
+ status = sanei_scsi_req_wait(bc->qid);
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_wait ok\n");
+#endif
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: data received %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ s->bytes_to_read -= bc->used;
+ bytes_to_queue += bc->nreq - bc->used;
+ bc->start = 0;
+ bc->shm_status = SHM_FULL;
+
+ waitindex++;
+ if (waitindex == s->dev->info.buffers)
+ waitindex = 0;
+
+ }
+
+ if (bytes_to_queue)
+ {
+ /* wait until the next buffer is completely read via read_data */
+ bc = &s->rdr_ctl->buf_ctl[cmdindex];
+ counted = 0;
+ while (buf_status(bc) != SHM_EMPTY)
+ {
+ if (!counted)
+ {
+ counted = 1;
+ full_count++;
+ }
+ if (cancel_requested(s))
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+ }
+
+ nread = bytes_to_queue;
+ if (nread > max_bytes_per_read)
+ nread = max_bytes_per_read;
+ bc->used = nread;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+ status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
+ bc->buffer, &bc->used, &bc->qid);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ bc->shm_status = SHM_BUSY;
+ bc->nreq = nread;
+ bytes_to_queue -= nread;
+
+ cmdindex++;
+ if (cmdindex == s->dev->info.buffers)
+ cmdindex = 0;
+ }
+
+ if (cancel_requested(s))
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+ }
+
+ DBG(1, "buffer full conditions: %i\n", full_count);
+ DBG(11, " reader_process>>\n");
+
+ s->rdr_ctl->running = 0;
+ return 0;
+}
+
+static SANE_Status
+read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ size_t copysize, copied = 0;
+ NEC_shmem_ctl *bc;
+
+ DBG(11, "<< read_data ");
+
+ bc = &s->rdr_ctl->buf_ctl[s->read_buff];
+
+ while (copied < *buf_size)
+ {
+ /* wait until the reader process delivers data or a scanner error occurs: */
+ while ( buf_status(bc) != SHM_FULL
+ && rdr_status(s) == SANE_STATUS_GOOD)
+ {
+ usleep(10); /* could perhaps be longer. make this user configurable?? */
+ }
+
+ if (rdr_status(s) != SANE_STATUS_GOOD)
+ {
+ return rdr_status(s);
+ DBG(11, ">>\n");
+ }
+
+ copysize = bc->used - bc->start;
+
+ if (copysize > *buf_size - copied )
+ copysize = *buf_size - copied;
+
+ memcpy(buf, &(bc->buffer[bc->start]), copysize);
+
+ copied += copysize;
+ buf = &buf[copysize];
+
+ bc->start += copysize;
+ if (bc->start >= bc->used)
+ {
+ bc->start = 0;
+ bc->shm_status = SHM_EMPTY;
+ s->read_buff++;
+ if (s->read_buff == s->dev->info.buffers)
+ s->read_buff = 0;
+ bc = &s->rdr_ctl->buf_ctl[s->read_buff];
+ }
+ }
+
+ DBG(11, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+#else /* don't USE_FORK: */
+
+static SANE_Status
+read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t remain = *buf_size;
+ size_t nread;
+ DBG (11, "<< read_data ");
+
+ /* sane_read_shuffled requires that read_data returns
+ exactly *buf_size bytes, so it must be guaranteed here.
+ Further make sure that not more bytes are read in than
+ sanei_scsi_max_request_size allows, to avoid a failure
+ of the read command
+ */
+ while (remain > 0)
+ {
+ nread = remain;
+ if (nread > s->dev->info.bufsize)
+ nread = s->dev->info.bufsize;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd),
+ &buf[*buf_size - remain], &nread);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(11, ">>\n");
+ return(status);
+ }
+ remain -= nread;
+ }
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (10, "<< max_string_size ");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (10, ">>\n");
+ return max_size;
+}
+
+static SANE_Status
+wait_ready(int fd)
+{
+ SANE_Status status;
+ int retry = 0;
+
+ while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "wait_ready failed (%d)\n", retry);
+ DBG (5, "wait_ready status = (%d)\n", status);
+ if (retry++ > 15){
+ return SANE_STATUS_IO_ERROR;
+ }
+ sleep(3);
+ }
+ return (status);
+
+}
+
+static SANE_Status
+attach (const char *devnam, NEC_Device ** devp)
+{
+ SANE_Status status;
+ NEC_Device *dev;
+ NEC_Sense_Data sensedat;
+
+ int fd;
+ unsigned char inquiry_data[INQUIRY_LEN];
+ const unsigned char *model_name;
+ mode_sense_param msp;
+ size_t buf_size;
+ DBG (10, "<< attach ");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ sensedat.model = unknown;
+ sensedat.complain_on_adf_error = 0;
+ DBG (3, "attach: opening %s\n", devnam);
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ {
+ int bufsize = 4096;
+ status = sanei_scsi_open_extended (devnam, &fd, &sense_handler, &sensedat, &bufsize);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ if (bufsize < 4096)
+ {
+ DBG(1, "attach: open failed. no memory\n");
+ sanei_scsi_close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+#else
+ status = sanei_scsi_open (devnam, &fd, &sense_handler, &sensedat);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+#endif
+
+ DBG (3, "attach: sending INQUIRY\n");
+ memset (inquiry_data, 0, sizeof (inquiry_data));
+ buf_size = sizeof (inquiry_data);
+ status = inquiry (fd, inquiry_data, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ if (inquiry_data[0] == 6 && strncmp ((char *)inquiry_data + 8, "NEC", 3) == 0)
+ {
+ if (strncmp ((char *)inquiry_data + 16, "PC-IN500/4C", 11) == 0)
+ sensedat.model = PCIN500;
+ else
+ sensedat.model = unknown;
+ }
+
+ if (sensedat.model == unknown)
+ {
+ DBG (1, "attach: device doesn't look like a NEC scanner\n");
+ DBG (1, " : Only PC-IN500/4C is supported.\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+ DBG (3, "attach: sending MODE SELECT\n");
+
+ if (sensedat.model == PCIN500)
+ status = mode_select_mud (fd, DEFAULT_MUD_1200);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT_MUD failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending MODE SENSE/MUP page\n");
+ memset (&msp, 0, sizeof (msp));
+ buf_size = sizeof (msp);
+ status = mode_sense (fd, &msp, &buf_size, 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE/MUP page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+#ifdef DEBUG_NEC
+ DBG (3,"attach: MODE SENSE parameter\n");
+ DBG(11, "%02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ msp.mode_data_length,
+ msp.mode_param_header2,
+ msp.mode_param_header3,
+ msp.mode_desciptor_length,
+ msp.page_code,
+ msp.page_length,
+ msp.bmu,
+ msp.res2,
+ msp.mud[0],
+ msp.mud[1],
+ msp.res3,
+ msp.res4);
+#endif
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = (SANE_String) strdup (devnam);
+ dev->sane.vendor = "NEC";
+ model_name = inquiry_data + 16;
+ dev->sane.model = strndup ((const char *)model_name, 10);
+ dev->sane.type = "flatbed scanner";
+
+ dev->sensedat.model = sensedat.model;
+
+ DBG (5, "dev->sane.name = %s\n", dev->sane.name);
+ DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor);
+ DBG (5, "dev->sane.model = %s\n", dev->sane.model);
+ DBG (5, "dev->sane.type = %s\n", dev->sane.type);
+
+ if (sensedat.model == PCIN500)
+ dev->info.res_range.quant = 10;
+ else
+ dev->info.res_range.quant = 0;
+
+ dev->info.tl_x_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
+ dev->info.br_x_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
+ dev->info.br_y_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.br_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.br_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+
+ if (sensedat.model == PCIN500)
+ dev->info.res_default = 15;
+ else
+ dev->info.res_default = 150;
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(209);
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(296);
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297);
+
+ dev->info.bmu = msp.bmu;
+ dev->info.mud = (msp.mud[0] << 8) + msp.mud[1];
+
+ dev->info.adf_fsu_installed = 0;
+ if (dev->sensedat.model == PCIN500)
+ {
+ dev->info.res_range.max = 48;
+ dev->info.res_range.min = 5;
+
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ }
+ else
+ {
+ dev->info.res_range.max = 400;
+ dev->info.res_range.min = 50;
+
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ }
+ sanei_scsi_close (fd);
+
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 0;
+
+ dev->info.tint_range.min = 1;
+ dev->info.tint_range.max = 255;
+ dev->info.tint_range.quant = 0;
+
+ dev->info.color_range.min = 1;
+ dev->info.color_range.max = 255;
+ dev->info.color_range.quant = 0;
+
+ DBG (5, "res_default=%d\n", dev->info.res_default);
+ DBG (5, "res_range.max=%d\n", dev->info.res_range.max);
+ DBG (5, "res_range.min=%d\n", dev->info.res_range.min);
+ DBG (5, "res_range.quant=%d\n", dev->info.res_range.quant);
+
+ DBG (5, "x_default=%f\n", SANE_UNFIX(dev->info.x_default));
+ DBG (5, "tl_x_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].max));
+ DBG (5, "tl_x_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].min));
+ DBG (5, "tl_x_range[0].quant=%d\n", dev->info.tl_x_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "br_x_range[0].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].max));
+ DBG (5, "br_x_range[0].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].min));
+ DBG (5, "br_x_range[0].quant=%d\n", dev->info.br_x_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "y_default=%f\n", SANE_UNFIX(dev->info.y_default));
+ DBG (5, "tl_y_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].max));
+ DBG (5, "tl_y_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].min));
+ DBG (5, "tl_y_range[0].quant=%d\n", dev->info.tl_y_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "br_y_range[0].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].max));
+ DBG (5, "br_y_range[0].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].min));
+ DBG (5, "br_y_range[0].quant=%d\n", dev->info.br_y_ranges[SCAN_SIMPLE].quant);
+
+ if (dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ DBG (5, "tl_x_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "tl_x_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "tl_x_range[1].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "br_x_range[1].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "br_x_range[1].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "br_x_range[1].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "tl_y_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "tl_y_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "tl_y_range[1].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "br_y_range[1].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "br_y_range[1].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "br_y_range[1].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_FSU].quant);
+ }
+
+ if (dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ DBG (5, "tl_x_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "tl_x_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "tl_x_range[2].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "br_x_range[2].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "br_x_range[2].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "br_x_range[2].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "tl_y_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "tl_y_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "tl_y_range[2].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "br_y_range[2].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "br_y_range[2].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "br_y_range[2].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_ADF].quant);
+ }
+
+ DBG (5, "bmu=%d\n", dev->info.bmu);
+ DBG (5, "mud=%d\n", dev->info.mud);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/* Enabling / disabling of gamma options.
+ Depends on many user settable options, so lets put it into
+ one function to be called by init_options and by sane_control_option
+
+*/
+#ifdef USE_CUSTOM_GAMMA
+static void
+set_gamma_caps(NEC_Scanner *s)
+{
+ /* neither fixed nor custom gamma for line art modes */
+ if ( strcmp(s->val[OPT_MODE].s, M_LINEART) == 0
+ || strcmp(s->val[OPT_MODE].s, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp(s->val[OPT_MODE].s, M_GRAY) == 0)
+ {
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ }
+}
+#endif /* USE_CUSTOM_GAMMA */
+
+/* The next function is a slightly modified version of sanei_constrain_value
+ Instead of returning status information like STATUS_INVAL, it adjusts
+ an invaild value to the nearest allowed one.
+*/
+static void
+clip_value (const SANE_Option_Descriptor * opt, void * value)
+{
+ const SANE_String_Const * string_list;
+ const SANE_Word * word_list;
+ int i, num_matches, match;
+ const SANE_Range * range;
+ SANE_Word w, v;
+ size_t len;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ w = *(SANE_Word *) value;
+ range = opt->constraint.range;
+
+ if (w < range->min)
+ w = range->min;
+ else if (w > range->max)
+ w = range->max;
+
+ if (range->quant)
+ {
+ v = (w - range->min + range->quant/2) / range->quant;
+ w = v * range->quant + range->min;
+ *(SANE_Word*) value = w;
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ w = *(SANE_Word *) value;
+ word_list = opt->constraint.word_list;
+ for (i = 1; w != word_list[i]; ++i)
+ if (i >= word_list[0])
+ /* somewhat arbitrary... Would be better to have a default value
+ explicitly defined.
+ */
+ *(SANE_Word*) value = word_list[1];
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ /* Matching algorithm: take the longest unique match ignoring
+ case. If there is an exact match, it is admissible even if
+ the same string is a prefix of a longer option name. */
+ string_list = opt->constraint.string_list;
+ len = strlen (value);
+
+ /* count how many matches of length LEN characters we have: */
+ num_matches = 0;
+ match = -1;
+ for (i = 0; string_list[i]; ++i)
+ if (strncasecmp (value, string_list[i], len) == 0
+ && len <= strlen (string_list[i]))
+ {
+ match = i;
+ if (len == strlen (string_list[i]))
+ {
+ /* exact match... */
+ if (strcmp (value, string_list[i]) != 0)
+ /* ...but case differs */
+ strcpy (value, string_list[match]);
+ }
+ ++num_matches;
+ }
+
+ if (num_matches > 1)
+ /* xxx quite arbitrary... We could also choose the first match
+ */
+ strcpy(value, string_list[match]);
+ else if (num_matches == 1)
+ strcpy (value, string_list[match]);
+ else
+ strcpy (value, string_list[0]);
+
+ default:
+ break;
+ }
+}
+
+/* make sure that enough memory is allocated for each string,
+ so that the strcpy in sane_control_option / set value cannot
+ write behind the end of the allocated memory.
+*/
+static SANE_Status
+init_string_option(NEC_Scanner *s, SANE_String_Const name,
+ SANE_String_Const title, SANE_String_Const desc,
+ const SANE_String_Const *string_list, int option, int default_index)
+{
+ int i;
+
+ s->opt[option].name = name;
+ s->opt[option].title = title;
+ s->opt[option].desc = desc;
+ s->opt[option].type = SANE_TYPE_STRING;
+ s->opt[option].size = max_string_size (string_list);
+ s->opt[option].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[option].constraint.string_list = string_list;
+ s->val[option].s = malloc(s->opt[option].size);
+ if (s->val[option].s == 0)
+ {
+ for (i = 1; i < NUM_OPTIONS; i++)
+ {
+ if (s->val[i].s && s->opt[i].type == SANE_TYPE_STRING)
+ free(s->val[i].s);
+ }
+ return SANE_STATUS_NO_MEM;
+ }
+ strcpy(s->val[option].s, string_list[default_index]);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (NEC_Scanner * s)
+{
+ int i, default_source;
+ SANE_Word scalar;
+ DBG (10, "<< init_options ");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->val[i].s = 0;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* Mode group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ init_string_option(s, SANE_NAME_SCAN_MODE, SANE_TITLE_SCAN_MODE,
+ SANE_DESC_SCAN_MODE, mode_list, OPT_MODE, MODES_COLOR);
+
+ /* half tone */
+ init_string_option(s, SANE_NAME_HALFTONE_PATTERN, SANE_TITLE_HALFTONE_PATTERN,
+ SANE_DESC_HALFTONE " (not support)", halftone_list, OPT_HALFTONE, 0);
+
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+
+ i = 0;
+ default_source = -1;
+
+ if (s->dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ s->dev->info.scansources[i++] = use_adf;
+ default_source = SCAN_WITH_ADF;
+ }
+ if (s->dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ s->dev->info.scansources[i++] = use_fsu;
+ if (default_source < 0)
+ default_source = SCAN_WITH_FSU;
+ }
+ s->dev->info.scansources[i++] = use_simple;
+ if (default_source < 0)
+ default_source = SCAN_SIMPLE;
+ s->dev->info.scansources[i] = 0;
+
+ init_string_option(s, SANE_NAME_SCAN_SOURCE, SANE_TITLE_SCAN_SOURCE,
+ SANE_DESC_SCAN_SOURCE, (SANE_String_Const*)s->dev->info.scansources,
+ OPT_SCANSOURCE, 0);
+
+ if (i < 2)
+ s->opt[OPT_SCANSOURCE].cap |= SANE_CAP_INACTIVE;
+
+ if (s->dev->sensedat.model == PCIN500)
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_pcin500, OPT_PAPER, 0);
+ else
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_pcinxxx, OPT_PAPER, 1);
+
+ /* gamma */
+ init_string_option(s, "Gamma", "Gamma", "Gamma", gamma_list, OPT_GAMMA, 0);
+
+ /* Resolution Group */
+ s->opt[OPT_RESOLUTION_GROUP].title = "Resolution";
+ s->opt[OPT_RESOLUTION_GROUP].desc = "";
+ s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_RESOLUTION_GROUP].cap = 0;
+ s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+#ifdef USE_RESOLUTION_LIST
+ /* select resolution */
+ if (s->dev->sensedat.model == PCIN500)
+ init_string_option(s, "Resolution", "Resolution", "Resolution",
+ resolution_list_pcin500, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCIN500);
+ else
+ init_string_option(s, "Resolution", "Resolution", "Resolution",
+ resolution_list_pcinxxx, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCINXXX);
+#endif
+
+ /* x & y resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION"(x 10)";
+ else
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->dev->info.res_range;
+ s->val[OPT_RESOLUTION].w = s->dev->info.res_default;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->dev->info.tl_x_ranges[default_source];
+ s->val[OPT_TL_X].w = s->dev->info.tl_x_ranges[default_source].min;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->dev->info.tl_y_ranges[default_source];
+ s->val[OPT_TL_Y].w = s->dev->info.tl_y_ranges[default_source].min;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->dev->info.br_x_ranges[default_source];
+ scalar = s->dev->info.x_default;
+ clip_value (&s->opt[OPT_BR_X], &scalar);
+ s->val[OPT_BR_X].w = scalar;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->dev->info.br_y_ranges[default_source];
+ scalar = s->dev->info.y_default;
+ clip_value (&s->opt[OPT_BR_X], &scalar);
+ s->val[OPT_BR_Y].w = scalar;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* edge emphasis */
+ init_string_option(s, "Edge emphasis", "Edge emphasis",
+ "Edge emphasis", edge_emphasis_list,
+ OPT_EDGE_EMPHASIS, 0);
+
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_EDGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
+
+ /* OR */
+ s->opt[OPT_OR].name = "OR";
+ s->opt[OPT_OR].title = "OR";
+ s->opt[OPT_OR].desc = "Select OR emphancement";
+ s->opt[OPT_OR].type = SANE_TYPE_BOOL;
+ s->val[OPT_OR].w = SANE_FALSE;
+
+ /* EDGE */
+ s->opt[OPT_EDGE].name = "edge";
+ s->opt[OPT_EDGE].title = "Edge";
+ s->opt[OPT_EDGE].desc = "Select Edge emphancement";
+ s->opt[OPT_EDGE].type = SANE_TYPE_BOOL;
+ s->val[OPT_EDGE].w = SANE_FALSE;
+
+ /* NR */
+ s->opt[OPT_NR].name = "NR";
+ s->opt[OPT_NR].title = "NR";
+ s->opt[OPT_NR].desc = "Select noise reduction";
+ s->opt[OPT_NR].type = SANE_TYPE_BOOL;
+ s->val[OPT_NR].w = SANE_FALSE;
+
+ if (s->dev->sensedat.model != PCIN500)
+ {
+ s->opt[OPT_EDGE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_NR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_OR].cap |= SANE_CAP_INACTIVE;
+ }
+ /* tint */
+ s->opt[OPT_TINT].name = "tint";
+ s->opt[OPT_TINT].title = "Tint";
+ s->opt[OPT_TINT].desc = "Select tint";
+ s->opt[OPT_TINT].type = SANE_TYPE_INT;
+ s->opt[OPT_TINT].unit = SANE_UNIT_NONE;
+ s->opt[OPT_TINT].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TINT].constraint.range = &s->dev->info.tint_range;
+ s->val[OPT_TINT].w = 128;
+ if (s->dev->sensedat.model != PCIN500)
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+
+ /* color */
+ s->opt[OPT_COLOR].name = "color";
+ s->opt[OPT_COLOR].title = "Color";
+ s->opt[OPT_COLOR].desc = "Select color";
+ s->opt[OPT_COLOR].type = SANE_TYPE_INT;
+ s->opt[OPT_COLOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_COLOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_COLOR].constraint.range = &s->dev->info.color_range;
+ s->val[OPT_COLOR].w = 128;
+ if (s->dev->sensedat.model != PCIN500)
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD].w = 128;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].name = SANE_NAME_THRESHOLD "-red";
+ /* xxx the titles and decriptions are confusing:
+ "set white point (red)"
+ Any idea? maybe "threshold to get the red component on"
+ */
+ s->opt[OPT_THRESHOLD_R].title = SANE_TITLE_THRESHOLD " (red)";
+ s->opt[OPT_THRESHOLD_R].desc = SANE_DESC_THRESHOLD " (red)";
+ s->opt[OPT_THRESHOLD_R].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_R].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_R].w = 128;
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_THRESHOLD_G].name = SANE_NAME_THRESHOLD "-green";
+ s->opt[OPT_THRESHOLD_G].title = SANE_TITLE_THRESHOLD " (green)";
+ s->opt[OPT_THRESHOLD_G].desc = SANE_DESC_THRESHOLD " (green)";
+ s->opt[OPT_THRESHOLD_G].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_G].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_G].w = 128;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_THRESHOLD_B].name = SANE_NAME_THRESHOLD "-blue";
+ s->opt[OPT_THRESHOLD_B].title = SANE_TITLE_THRESHOLD " (blue)";
+ s->opt[OPT_THRESHOLD_B].desc = SANE_DESC_THRESHOLD " (blue)";
+ s->opt[OPT_THRESHOLD_B].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_B].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_B].w = 128;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+
+#endif
+
+ /* light color (for gray scale and line art scans) */
+ init_string_option(s, "LightColor", "LightColor", "LightColor",
+ light_color_list, OPT_LIGHTCOLOR, 3);
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+
+#ifdef USE_CUSTOM_GAMMA
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+ set_gamma_caps(s);
+#endif
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (NEC_Scanner * s)
+{
+ DBG (10, "<< do_cancel ");
+
+#ifdef USE_FORK
+ if (s->reader_pid > 0)
+ {
+ int exit_status;
+ int count = 0;
+ /* ensure child knows it's time to stop: */
+
+ DBG(11, "stopping reader process\n");
+ s->rdr_ctl->cancel = 1;
+ while(reader_running(s) && count < 100)
+ {
+ usleep(100000);
+ count++;
+ };
+ if (reader_running(s))
+ {
+ kill(s->reader_pid, SIGKILL);
+ }
+ wait(&exit_status);
+ DBG(11, "reader process stopped\n");
+
+ s->reader_pid = 0;
+ }
+
+#endif
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ if (s->shmid != -1)
+ shmctl(s->shmid, IPC_RMID, &ds);
+ s->shmid = -1;
+ }
+#endif
+ if (s->buffer)
+ free(s->buffer);
+ s->buffer = 0;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+static NEC_New_Device *new_devs = 0;
+static NEC_New_Device *new_dev_pool = 0;
+
+static SANE_Status
+attach_and_list(const char *devnam)
+{
+ SANE_Status res;
+ NEC_Device *devp;
+ NEC_New_Device *np;
+
+ res = attach(devnam, &devp);
+ if (res == SANE_STATUS_GOOD)
+ {
+ if (new_dev_pool)
+ {
+ np = new_dev_pool;
+ new_dev_pool = np->next;
+ }
+ else
+ {
+ np = malloc(sizeof(NEC_New_Device));
+ if (np == 0)
+ return SANE_STATUS_NO_MEM;
+ }
+ np->next =new_devs;
+ np->dev = devp;
+ new_devs = np;
+ }
+ return res;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ char line[PATH_MAX];
+ const char *lp;
+ char *word;
+ char *end;
+ FILE *fp;
+ int opt_index = 0;
+ int buffers[2] = {DEFAULT_BUFFERS, DEFAULT_BUFFERS};
+ int bufsize[2] = {DEFAULT_BUFSIZE, DEFAULT_BUFSIZE};
+ int queued_reads[2] = {DEFAULT_QUEUED_READS, DEFAULT_QUEUED_READS};
+ int linecount = 0;
+#if 1
+ NEC_Device nd;
+ NEC_Device *dp = &nd;
+#else
+ NEC_Device *dp;
+#endif
+ NEC_New_Device *np;
+ int i;
+
+ authorize = authorize; /* silence compilation warnings */
+
+ DBG_INIT ();
+ DBG (10, "<< sane_init ");
+
+ DBG (1, "sane_init: NEC (Ver %d.%d)\n", NEC_MAJOR, NEC_MINOR);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (NEC_CONFIG_FILE);
+ if (!fp)
+ {
+ /* use "/dev/scanner" as the default device name if no
+ config file is available
+ */
+ attach (devnam, &dp);
+ /* make sure that there are at least two buffers */
+ if (DEFAULT_BUFFERS < 2)
+ dp->info.buffers = DEFAULT_BUFFERS;
+ else
+ dp->info.buffers = 2;
+ dp->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ dp->info.queued_reads = DEFAULT_QUEUED_READS;
+ return SANE_STATUS_GOOD;
+ }
+
+ while (fgets(line, PATH_MAX, fp))
+ {
+ linecount++;
+ word = 0;
+ lp = sanei_config_get_string(line, &word);
+ if (word)
+ {
+ if (word[0] != '#')
+ {
+ if (strcmp(word, "option") == 0)
+ {
+ free(word);
+ word = 0;
+ lp = sanei_config_get_string(lp, &word);
+ if (strcmp(word, "buffers") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (end == word)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ if (i > 2)
+ buffers[opt_index] = i;
+ else
+ buffers[opt_index] = 2;
+ }
+ else if (strcmp(word, "buffersize") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ bufsize[opt_index] = i;
+ }
+ else if (strcmp(word, "readqueue") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ queued_reads[opt_index] = i;
+ }
+ else
+ {
+ DBG(1, "error in config file, line %i: unknown option\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ }
+ else
+ {
+ while (new_devs)
+ {
+ if (buffers[1] >= 2)
+ new_devs->dev->info.buffers = buffers[1];
+ else
+ new_devs->dev->info.buffers = 2;
+ if (bufsize[1] > 0)
+ new_devs->dev->info.wanted_bufsize = bufsize[1];
+ else
+ new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ if (queued_reads[1] >= 0)
+ new_devs->dev->info.queued_reads = queued_reads[1];
+ else
+ new_devs->dev->info.queued_reads = 0;
+ np = new_devs->next;
+ new_devs->next = new_dev_pool;
+ new_dev_pool = new_devs;
+ new_devs = np;
+ }
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ sanei_config_attach_matching_devices(line, &attach_and_list);
+ buffers[1] = buffers[0];
+ bufsize[1] = bufsize[0];
+ queued_reads[1] = queued_reads[0];
+ opt_index = 1;
+ }
+ }
+ if (word) free(word);
+ }
+ }
+
+ while (new_devs)
+ {
+ if (buffers[1] >= 2)
+ new_devs->dev->info.buffers = buffers[1];
+ else
+ new_devs->dev->info.buffers = 2;
+ if (bufsize[1] > 0)
+ new_devs->dev->info.wanted_bufsize = bufsize[1];
+ else
+ new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ if (queued_reads[1] >= 0)
+ new_devs->dev->info.queued_reads = queued_reads[1];
+ else
+ new_devs->dev->info.queued_reads = 0;
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ np = new_devs->next;
+ free(new_devs);
+ new_devs = np;
+ }
+ while (new_dev_pool)
+ {
+ np = new_dev_pool->next;
+ free(new_dev_pool);
+ new_dev_pool = np;
+ }
+ fclose(fp);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_exit (void)
+{
+ NEC_Device *dev, *next;
+ DBG (10, "<< sane_exit ");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+ first_dev = 0;
+
+ if (devlist)
+ free(devlist);
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ NEC_Device *dev;
+ int i;
+ DBG (10, "<< sane_get_devices ");
+
+ local_only = local_only; /* silence compilation warnings */
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ NEC_Device *dev;
+ NEC_Scanner *s;
+#ifdef USE_CUSTOM_GAMMA
+ int i, j;
+#endif
+
+ DBG (10, "<< sane_open ");
+
+ if (devnam[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->dev = dev;
+
+ s->buffer = 0;
+#ifdef USE_CUSTOM_GAMMA
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 256; ++j)
+ s->gamma_table[i][j] = j;
+#endif
+ status = init_options (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* xxx clean up mallocs */
+ return status;
+ }
+
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ NEC_Scanner *s = (NEC_Scanner *) handle;
+ DBG (10, "<< sane_close ");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ if (s->shmid != -1)
+ shmctl(s->shmid, IPC_RMID, &ds);
+ }
+#endif
+ if (s->buffer)
+ free(s->buffer);
+ free (s);
+
+ DBG (10, ">>\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ NEC_Scanner *s = handle;
+ DBG (10, "<< sane_get_option_descriptor ");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (10, ">>\n");
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+#ifdef USE_CUSTOM_GAMMA
+ SANE_Word w, cap;
+#else
+ SANE_Word cap;
+#endif
+ int range_index;
+ DBG (10, "<< sane_control_option %i", option);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+ case OPT_TINT:
+ case OPT_COLOR:
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_OR:
+ case OPT_NR:
+ case OPT_EDGE:
+ case OPT_PREVIEW:
+#ifdef USE_CUSTOM_GAMMA
+ case OPT_CUSTOM_GAMMA:
+#endif
+ *(SANE_Word *) val = s->val[option].w;
+#if 0 /* here, values are read; reload should not be necessary */
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+#endif
+ return (SANE_STATUS_GOOD);
+
+#ifdef USE_CUSTOM_GAMMA
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+#endif
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_HALFTONE:
+ case OPT_PAPER:
+ case OPT_GAMMA:
+#ifdef USE_RESOLUTION_LIST
+ case OPT_RESOLUTION_LIST:
+#endif
+ case OPT_EDGE_EMPHASIS:
+ case OPT_LIGHTCOLOR:
+ case OPT_SCANSOURCE:
+ strcpy (val, s->val[option].s);
+#if 0
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+#endif
+
+ return (SANE_STATUS_GOOD);
+
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+ /* xxx theoretically, we could use OPT_THRESHOLD in
+ bi-level color mode to adjust all three other
+ threshold together. But this would require to set
+ the bit SANE_INFO_RELOAD_OPTIONS in *info, and that
+ would unfortunately cause a crash in both xscanimage
+ and xsane... Therefore, OPT_THRESHOLD is disabled
+ for bi-level color scan right now.
+ */
+ case OPT_TINT:
+ case OPT_COLOR:
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_OR:
+ case OPT_NR:
+ case OPT_EDGE:
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_MODE:
+ if (strcmp (val, M_LINEART) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap &= ~SANE_CAP_INACTIVE;
+#endif
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, M_GRAY) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap &= ~SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+#if 0
+ if ( strcmp (val, M_LINEART) == 0
+ || strcmp (val, M_GRAY) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ }
+#endif
+ strcpy(s->val[option].s, val);
+#ifdef USE_CUSTOM_GAMMA
+ set_gamma_caps(s);
+#endif
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_GAMMA:
+ case OPT_HALFTONE:
+ case OPT_EDGE_EMPHASIS:
+ case OPT_LIGHTCOLOR:
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_SCANSOURCE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ if (strcmp(val, use_fsu) == 0)
+ range_index = SCAN_WITH_FSU;
+ else if (strcmp(val, use_adf) == 0)
+ range_index = SCAN_WITH_ADF;
+ else
+ range_index = SCAN_SIMPLE;
+
+ s->opt[OPT_TL_X].constraint.range
+ = &s->dev->info.tl_x_ranges[range_index];
+ clip_value (&s->opt[OPT_TL_X], &s->val[OPT_TL_X].w);
+
+ s->opt[OPT_TL_Y].constraint.range
+ = &s->dev->info.tl_y_ranges[range_index];
+ clip_value (&s->opt[OPT_TL_Y], &s->val[OPT_TL_Y].w);
+
+ s->opt[OPT_BR_X].constraint.range
+ = &s->dev->info.br_x_ranges[range_index];
+ clip_value (&s->opt[OPT_BR_X], &s->val[OPT_BR_X].w);
+
+ s->opt[OPT_BR_Y].constraint.range
+ = &s->dev->info.br_y_ranges[range_index];
+ clip_value (&s->opt[OPT_BR_Y], &s->val[OPT_BR_Y].w);
+
+ return (SANE_STATUS_GOOD);
+
+ case OPT_PAPER:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ s->val[OPT_TL_X].w = SANE_FIX(0);
+ s->val[OPT_TL_Y].w = SANE_FIX(0);
+ if (strcmp (s->val[option].s, "A3") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(297);
+ s->val[OPT_BR_Y].w = SANE_FIX(420);
+ }else if (strcmp (s->val[option].s, "A4") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(210);
+ s->val[OPT_BR_Y].w = SANE_FIX(297);
+ }else if (strcmp (s->val[option].s, "A5") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(148.5);
+ s->val[OPT_BR_Y].w = SANE_FIX(210);
+ }else if (strcmp (s->val[option].s, "A6") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(105);
+ s->val[OPT_BR_Y].w = SANE_FIX(148.5);
+ }else if (strcmp (s->val[option].s, "B4") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(250);
+ s->val[OPT_BR_Y].w = SANE_FIX(353);
+ }else if (strcmp (s->val[option].s, "B5") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(182);
+ s->val[OPT_BR_Y].w = SANE_FIX(257);
+ }else if (strcmp (s->val[option].s, W_LETTER) == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(279.4);
+ s->val[OPT_BR_Y].w = SANE_FIX(431.8);
+ }else if (strcmp (s->val[option].s, "Legal") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(355.6);
+ }else if (strcmp (s->val[option].s, "Letter") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(279.4);
+ }else if (strcmp (s->val[option].s, INVOICE) == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(139.7);
+ }else{
+ }
+ return (SANE_STATUS_GOOD);
+
+#ifdef USE_RESOLUTION_LIST
+ case OPT_RESOLUTION_LIST:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ for (i = 0; s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]; i++) {
+ if (strcmp (val,
+ s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]) == 0){
+ s->val[OPT_RESOLUTION].w
+ = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+#endif
+
+#ifdef USE_CUSTOM_GAMMA
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ set_gamma_caps(s);
+ return SANE_STATUS_GOOD;
+#endif
+ }
+ }
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_INVAL);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int width, length, res;
+ const char *mode;
+ NEC_Scanner *s = handle;
+ DBG (10, "<< sane_get_parameters ");
+
+ res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
+ if (!s->scanning)
+ {
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_X].w)
+ - SANE_UNFIX(s->val[OPT_TL_X].w),
+ s->dev->info.mud);
+ length = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_Y].w)
+ - SANE_UNFIX(s->val[OPT_TL_Y].w),
+ s->dev->info.mud);
+
+ s->width = width;
+ s->length = length;
+ s->params.pixels_per_line = width * res / s->dev->info.mud;
+ s->params.lines = length * res / s->dev->info.mud;
+
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ s->params.pixels_per_line += 1;
+ s->params.lines += 1;
+ }
+ s->unscanned_lines = s->params.lines;
+ }
+#if 0
+ else
+ {
+ buffer_status bs;
+ SANE_Status status;
+ size_t len = sizeof (buffer_status);
+ status = get_data_buffer_status (s->fd, &bs, &len);
+ DBG (11, "<< get_data_buffer_status ");
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel(s);
+ return (status);
+ }
+ DBG (11, ">>\n ");
+ {
+#ifdef DEBUG_NEC
+ int i;
+ u_char *buf = &bs;
+ DBG(11, "get data buffer status(debug):\n");
+ for (i = 0; i < len; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x \n",
+ buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7], buf[i+8],
+ buf[i+9], buf[i+10], buf[i+11]);
+ }
+#endif
+ }
+ }
+#endif
+ res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
+
+ mode = s->val[OPT_MODE].s;
+
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ s->modes = MODES_LINEART;
+ }
+ else if (strcmp (mode, M_GRAY) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->modes = MODES_GRAY;
+ }
+ else if (strcmp (mode, M_LINEART_COLOR) == 0)
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 8;
+ s->modes = MODES_LINEART_COLOR;
+ }
+ else
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->modes = MODES_COLOR;
+ }
+ s->params.last_frame = SANE_TRUE;
+
+ if (params)
+ *params = s->params;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+#ifdef USE_CUSTOM_GAMMA
+
+static int
+sprint_gamma(Option_Value val, SANE_Byte *dst)
+{
+ int i;
+ SANE_Byte *p = dst;
+
+ p += sprintf((char *) p, "%i", val.wa[0]);
+ for (i = 1; i < 256; i++)
+ p += sprintf((char *) p, ",%i", val.wa[i] > 255 ? 255 : val.wa[i]);
+ return p - dst;
+}
+
+static SANE_Status
+send_ascii_gamma_tables (NEC_Scanner *s)
+{
+ SANE_Status status;
+ int i;
+
+ DBG(11, "<< send_ascii_gamma_tables ");
+
+ /* we need: 4 bytes for each gamma value (3 digits + delimiter)
+ + 10 bytes for the command header
+ i.e. 4 * 4 * 256 + 10 = 4106 bytes
+ */
+
+ if (s->dev->info.bufsize < 4106)
+ return SANE_STATUS_NO_MEM;
+
+ memset(s->buffer, 0, 4106);
+
+ i = sprint_gamma(s->val[OPT_GAMMA_VECTOR_R], &s->buffer[10]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_G], &s->buffer[10+i]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_B], &s->buffer[10+i]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR], &s->buffer[10+i]);
+
+ DBG(12, "%s\n", &s->buffer[10]);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[7] = i >> 8;
+ s->buffer[8] = i & 0xff;
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+#endif
+
+static SANE_Status
+send_binary_g_table(NEC_Scanner *s, SANE_Word *a, int dtq)
+{
+ SANE_Status status;
+ unsigned int i, j;
+
+ dtq = dtq; /* silence compilation warnings */
+
+ DBG(11, "<< send_binary_g_table\n");
+
+ i = 256;
+ if (s->dev->info.bufsize < i)
+ return SANE_STATUS_NO_MEM;
+ memset(s->buffer, 0, i+10);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[7] = i >> 8;
+ s->buffer[8] = i & 0xff;
+
+ for (i = 0; i < 256; i++)
+ {
+ s->buffer[i+11] = a[i&0xff] & 0xff;
+ }
+
+ for (j = 0; j < 256; j += 16)
+ {
+ DBG(11, "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ a[j ], a[j+1], a[j+2], a[j+3],
+ a[j+4], a[j+5], a[j+6], a[j+7],
+ a[j+8], a[j+9], a[j+10], a[j+11],
+ a[j+12], a[j+13], a[j+14], a[j+15]);
+ }
+ DBG(12, "transfer length = %d\n", i);
+ DBG(12, "buffer[7] = %d\n", s->buffer[7]);
+ DBG(12, "buffer[8] = %d\n", s->buffer[8]);
+
+ /* wait_ready(s->fd); */
+ status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+
+#ifdef USE_CUSTOM_GAMMA
+static SANE_Status
+send_binary_gamma_tables (NEC_Scanner *s)
+{
+ SANE_Status status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR].wa, 0x10);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ DBG(11, "send_binary_gamma_tables\n");
+#if 0
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_R].wa, 0x11);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_G].wa, 0x12);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_B].wa, 0x13);
+#endif
+ return status;
+}
+
+static SANE_Status
+send_gamma_tables (NEC_Scanner *s)
+{
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ return send_binary_gamma_tables(s);
+ }
+ else
+ {
+ return send_ascii_gamma_tables(s);
+ }
+
+}
+#endif
+
+#ifdef USE_COLOR_THRESHOLD
+/* not used? */
+#if 0
+static SANE_Status
+send_threshold_data(NEC_Scanner *s)
+{
+ SANE_Status status;
+ SANE_Byte cmd[26] = {SEND, 0, 0x82, 0, 0, 0, 0, 0, 0, 0};
+ int len;
+
+ memset(cmd, 0, sizeof(cmd));
+ /* maximum string length: 3 bytes for each number (they are
+ restricted to the range 0..255), 3 '/' and the null-byte,
+ total: 16 bytes.
+ */
+ len = sprintf((char *) &cmd[10], "%i/%i/%i/%i",
+ s->val[OPT_THRESHOLD_R].w,
+ s->val[OPT_THRESHOLD_G].w,
+ s->val[OPT_THRESHOLD_B].w,
+ s->val[OPT_THRESHOLD].w);
+ cmd[8] = len;
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd(s->fd, cmd, len + 10, 0, 0);
+ return status;
+}
+#endif
+#endif
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char *mode, *halftone, *paper, *gamma, *edge, *lightcolor, *adf_fsu;
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+ size_t buf_size;
+ window_param wp;
+
+ DBG (10, "<< sane_start ");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->dev->sensedat.complain_on_adf_error = 1;
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ s->dev->info.bufsize = s->dev->info.wanted_bufsize;
+ if (s->dev->info.bufsize < 32 * 1024)
+ s->dev->info.bufsize = 32 * 1024;
+ {
+ int bsize = s->dev->info.bufsize;
+ status = sanei_scsi_open_extended (s->dev->sane.name, &s->fd,
+ &sense_handler, &s->dev->sensedat, &bsize);
+ s->dev->info.bufsize = bsize;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ return (status);
+ }
+
+ /* make sure that we got at least 32 kB. Even then, the scan will be
+ awfully slow.
+
+ */
+ if (s->dev->info.bufsize < 32 * 1024)
+ {
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+#else
+ status = sanei_scsi_open(s->dev->sane.name, &s->fd, &sense_handler,
+ &s->dev->sensedat);
+ if (s->dev->info.wanted_bufsize < sanei_scsi_max_request_size)
+ s->dev->info.bufsize = s->dev->info.wanted_bufsize;
+ else
+ s->dev->info.bufsize = sanei_scsi_max_request_size;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ return (status);
+ }
+#endif
+
+ s->buffer = malloc(s->dev->info.bufsize);
+ if (!s->buffer) {
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ free(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ size_t n;
+
+ s->shmid = shmget(IPC_PRIVATE,
+ sizeof(NEC_rdr_ctl)
+ + s->dev->info.buffers *
+ (sizeof(NEC_shmem_ctl) + s->dev->info.bufsize),
+ IPC_CREAT | 0600);
+ if (s->shmid == -1)
+ {
+ free(s->buffer);
+ s->buffer = 0;
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+ s->rdr_ctl = (NEC_rdr_ctl*) shmat(s->shmid, 0, 0);
+ if ((int)s->rdr_ctl == -1)
+ {
+ shmctl(s->shmid, IPC_RMID, &ds);
+ free(s->buffer);
+ s->buffer = 0;
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->rdr_ctl->buf_ctl = (NEC_shmem_ctl*) &s->rdr_ctl[1];
+ for (n = 0; n < s->dev->info.buffers; n++)
+ {
+ s->rdr_ctl->buf_ctl[n].buffer =
+ (SANE_Byte*) &s->rdr_ctl->buf_ctl[s->dev->info.buffers]
+ + n * s->dev->info.bufsize;
+ }
+ }
+#endif /* USE_FORK */
+
+ DBG (5, "start: TEST_UNIT_READY\n");
+ status = test_unit_ready (s->fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "TEST UNIT READY failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ DBG (3, "start: sending MODE SELECT\n");
+ status = mode_select_mud (s->fd, s->dev->info.mud);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start: MODE_SELECT6 failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ mode = s->val[OPT_MODE].s;
+ halftone = s->val[OPT_HALFTONE].s;
+ paper = s->val[OPT_PAPER].s;
+ gamma = s->val[OPT_GAMMA].s;
+ edge = s->val[OPT_EDGE_EMPHASIS].s;
+ lightcolor = s->val[OPT_LIGHTCOLOR].s;
+ adf_fsu = s->val[OPT_SCANSOURCE].s;
+
+ if (s->val[OPT_PREVIEW].w == SANE_FALSE)
+ {
+ s->res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
+ }
+ else
+ {
+ s->res = 75;
+ }
+ s->ulx = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_X].w), s->dev->info.mud);
+ s->uly = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_Y].w), s->dev->info.mud);
+ s->threshold = s->val[OPT_THRESHOLD].w;
+
+ if (strcmp (mode, M_LINEART_COLOR) == 0)
+ s->bpp = 1;
+ else
+ s->bpp = s->params.depth;
+
+ s->adf_fsu_mode = SCAN_SIMPLE; /* default: scan without ADF and FSU */
+
+ if (strcmp(adf_fsu, use_fsu) == 0)
+ s->adf_fsu_mode = SCAN_WITH_FSU;
+ else if (strcmp(adf_fsu, use_adf) == 0)
+ s->adf_fsu_mode = SCAN_WITH_ADF;
+ else if (strcmp(adf_fsu, use_adf) == 0)
+ s->adf_fsu_mode = SCAN_SIMPLE;
+
+ /* halftone must not set to be zero PC-IN500 */
+ s->halftone = 0x01;
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->reverse = 0;
+ s->image_composition = 1;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->image_composition = 0;
+ s->halftone = 0x01;
+ }
+ else if (strcmp(halftone, M_DITHER1) == 0)
+ s->halftone = 0x01;
+ else if (strcmp(halftone, M_DITHER2) == 0)
+ s->halftone = 0x10;
+ else if (strcmp(halftone, M_DITHER3) == 0)
+ s->halftone = 0x20;
+ }
+ else if (strcmp (mode, M_GRAY) == 0)
+ {
+ s->image_composition = 2;
+ s->reverse = 1;
+ }
+ else if (strcmp (mode, M_LINEART_COLOR) == 0)
+ {
+ s->reverse = 1;
+ s->image_composition = 4;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->image_composition = 3;
+ s->halftone = 0x01;
+ }
+ else if (strcmp(halftone, M_DITHER1) == 0)
+ s->halftone = 0x01;
+ else if (strcmp(halftone, M_DITHER2) == 0)
+ s->halftone = 0x10;
+ else if (strcmp(halftone, M_DITHER3) == 0)
+ s->halftone = 0x20;
+ }
+ else if (strcmp (mode, M_COLOR) == 0)
+ {
+ s->image_composition = 5;
+ s->reverse = 1;
+ }
+
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ s->or = s->val[OPT_OR].w;
+ s->nr = s->val[OPT_NR].w;
+ s->edge = s->val[OPT_EDGE].w;
+ }
+ else
+ {
+ if (strcmp (edge, EDGE_NONE) == 0)
+ s->edge = 0;
+ else if (strcmp (edge, EDGE_MIDDLE) == 0)
+ s->edge = 1;
+ else if (strcmp (edge, EDGE_STRONG) == 0)
+ s->edge = 2;
+ else if (strcmp (edge, EDGE_BLUR) == 0)
+ s->edge = 3;
+ }
+
+ s->lightcolor = 3;
+ if (strcmp(lightcolor, LIGHT_GREEN) == 0)
+ s->lightcolor = 0;
+ else if (strcmp(lightcolor, LIGHT_RED) == 0)
+ s->lightcolor = 1;
+ else if (strcmp(lightcolor, LIGHT_BLUE) == 0)
+ s->lightcolor = 2;
+ else if (strcmp(lightcolor, LIGHT_NONE) == 0)
+ s->lightcolor = 3;
+
+ s->adf_scan = 0;
+
+#ifdef USE_CUSTOM_GAMMA
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+#endif
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ if (strcmp (gamma, CRT1))
+ s->gamma = 1;
+ else if (strcmp (gamma, CRT2))
+ s->gamma = 2;
+ else if (strcmp (gamma, PRINTER1))
+ s->gamma = 3;
+ else if (strcmp (gamma, PRINTER2))
+ s->gamma = 4;
+ else if (strcmp (gamma, NONE))
+ s->gamma = 5;
+ }
+#if 0
+ if (s->dev->sensedat.model != PCIN500)
+ {
+ ss.dtc = 0x03;
+ if (strcmp (gamma, GAMMA10) == 0)
+ ss.dtq = 0x01;
+ else
+ ss.dtq = 0x02;
+ ss.length = 0;
+ DBG (5, "start: SEND\n");
+ status = send (s->fd, &ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ }
+ else
+#else
+ {
+ int i;
+ SANE_Word gtbl[256];
+#if 0
+ if (strcmp (gamma, GAMMA10) == 0)
+ for (i = 0; i < 256; i++)
+ gtbl[i] = i;
+ else
+#endif
+ {
+ gtbl[0] = 0;
+ for (i = 1; i < 256; i++)
+ gtbl[i] = 255 * exp(0.45 * log(i/255.0));
+ }
+ send_binary_g_table(s, gtbl, 0x10);
+ send_binary_g_table(s, gtbl, 0x11);
+ send_binary_g_table(s, gtbl, 0x12);
+ send_binary_g_table(s, gtbl, 0x13);
+ }
+#endif /* DEBUG_NEC */
+#ifdef USE_CUSTOM_GAMMA
+ }
+ else
+ {
+ s->gamma = 9;
+ status = send_gamma_tables(s);
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#endif
+
+ s->tint = s->val[OPT_TINT].w;
+ s->color = s->val[OPT_COLOR].w;
+
+ memset (&wp, 0, sizeof (wp));
+ /* every NEC scanner seems to have a different
+ window descriptor block...
+ */
+ if (s->dev->sensedat.model == PCIN500)
+ buf_size = sizeof(WDB) + sizeof(WDBX500);
+ else
+ buf_size = sizeof(WDB);
+
+ wp.wpdh.wdl[0] = buf_size >> 8;
+ wp.wpdh.wdl[1] = buf_size;
+ wp.wdb.x_res[0] = s->res >> 8;
+ wp.wdb.x_res[1] = s->res;
+ wp.wdb.y_res[0] = s->res >> 8;
+ wp.wdb.y_res[1] = s->res;
+ wp.wdb.x_ul[0] = s->ulx >> 24;
+ wp.wdb.x_ul[1] = s->ulx >> 16;
+ wp.wdb.x_ul[2] = s->ulx >> 8;
+ wp.wdb.x_ul[3] = s->ulx;
+ wp.wdb.y_ul[0] = s->uly >> 24;
+ wp.wdb.y_ul[1] = s->uly >> 16;
+ wp.wdb.y_ul[2] = s->uly >> 8;
+ wp.wdb.y_ul[3] = s->uly;
+ wp.wdb.width[0] = s->width >> 24;
+ wp.wdb.width[1] = s->width >> 16;
+ wp.wdb.width[2] = s->width >> 8;
+ wp.wdb.width[3] = s->width;
+ wp.wdb.length[0] = s->length >> 24;
+ wp.wdb.length[1] = s->length >> 16;
+ wp.wdb.length[2] = s->length >> 8;
+ wp.wdb.length[3] = s->length;
+ wp.wdb.brightness = 0;
+ wp.wdb.threshold = s->threshold;
+ wp.wdb.brightness = 128;
+ wp.wdb.contrast = 128;
+ wp.wdb.image_composition = s->image_composition;
+ if (s->image_composition <= 2 || s->image_composition >= 5)
+ wp.wdb.bpp = s->bpp;
+ else
+ wp.wdb.bpp = 1;
+ wp.wdb.ht_pattern[0] = 0;
+ wp.wdb.ht_pattern[1] = 0;
+ if (s->dev->sensedat.model == PCIN500)
+ wp.wdb.ht_pattern[1] = s->halftone;
+ wp.wdb.rif_padding = (s->reverse * 128) + 0;
+
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ wp.wdbx500.data_length = 0x07;
+ wp.wdbx500.control = 0 | (s->or << 7) | (s->edge << 6) | (s->nr << 5) |
+ (s->lightcolor << 2);
+ wp.wdbx500.format = 0;
+ wp.wdbx500.gamma = s->gamma;
+ wp.wdbx500.tint = s->tint;
+ wp.wdbx500.color = s->color;
+ wp.wdbx500.reserved1 = 0;
+ wp.wdbx500.reserved2 = 0;
+ }
+
+ DBG (5, "wdl=%d\n", (wp.wpdh.wdl[0] << 8) + wp.wpdh.wdl[1]);
+ DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
+ DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
+ DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
+ (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
+ DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
+ (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
+ DBG (5, "width=%d\n", (wp.wdb.width[0] << 8) + (wp.wdb.width[1] << 16) +
+ (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
+ DBG (5, "length=%d\n", (wp.wdb.length[0] << 16) + (wp.wdb.length[1] << 16) +
+ (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
+
+ DBG (5, "threshold=%d\n", wp.wdb.threshold);
+ DBG (5, "image_composition=%d\n", wp.wdb.image_composition);
+ DBG (5, "bpp=%d\n", wp.wdb.bpp);
+ DBG (5, "rif_padding=%d\n", wp.wdb.rif_padding);
+
+#ifdef DEBUG_NEC
+ {
+ window_param foo;
+ size_t len = buf_size;
+ len += sizeof(WPDH);
+ DBG (5, "start: GET WINDOW\n");
+ status = get_window (s->fd, &foo, &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#if 1
+ {
+ unsigned char *p = (unsigned char*) &foo;
+ int i;
+ DBG(11, "get window(debug):\n");
+ for (i = 0; i < len; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+ foo.wpdh.wpdh[1] = 0;
+ status = set_window (s->fd, &foo, len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#if 1
+ {
+ unsigned char *p = (unsigned char*) &foo;
+ int i;
+ DBG(11, "set window(debug):\n");
+ for (i = 0; i < len; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+ }
+#endif /* debug_nec */
+
+#ifdef DEBUG_NEC
+ {
+ unsigned char *p = (unsigned char*) &wp;
+ int i;
+ DBG(11, "set window(debug):\n");
+ for (i = 0; i < sizeof(wp.wdb) + sizeof(wp.wdbx500); i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif /* debug_nec */
+
+ buf_size += sizeof(WPDH);
+ DBG (5, "start: SET WINDOW\n");
+ status = set_window (s->fd, &wp, buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ memset (&wp, 0, buf_size);
+ DBG (5, "start: GET WINDOW\n");
+ status = get_window (s->fd, &wp, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#ifdef DEBUG_NEC
+ {
+ unsigned char *p = (unsigned char*) &wp;
+ int i;
+ DBG(11, "get window(debug):\n");
+ for (i = 0; i < buf_size; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+ DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
+ DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
+ DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
+ (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
+ DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
+ (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
+ DBG (5, "width=%d\n", (wp.wdb.width[0] << 24) + (wp.wdb.width[1] << 16) +
+ (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
+ DBG (5, "length=%d\n", (wp.wdb.length[0] << 24) + (wp.wdb.length[1] << 16) +
+ (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
+ DBG (5, "start: SCAN\n");
+ s->scanning = SANE_TRUE;
+ s->busy = SANE_TRUE;
+ s->cancel = SANE_FALSE;
+ s->get_params_called = 0;
+
+ status = scan (s->fd);
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: scan started %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ s->busy = SANE_FALSE;
+ s->cancel = SANE_FALSE;
+ return (status);
+ }
+
+ /* ask the scanner for the scan size */
+ /* wait_ready(s->fd); */
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: wait_ready ok %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ sane_get_parameters(s, 0);
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: get_params ok %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (strcmp (mode, M_LINEART_COLOR) != 0)
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ else
+ {
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ }
+
+#ifdef USE_FORK
+ {
+ size_t i;
+ for (i = 0; i < s->dev->info.buffers; i++)
+ s->rdr_ctl->buf_ctl[i].shm_status = SHM_EMPTY;
+ s->read_buff = 0;
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->running = 0;
+ s->rdr_ctl->status = SANE_STATUS_GOOD;
+ }
+ s->reader_pid = fork();
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: forked %li.%06li %i\n", t.tv_sec, t.tv_usec,
+ s->reader_pid);
+ }
+#endif
+ if (s->reader_pid == 0)
+ {
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ /* don't use exit() since that would run the atexit() handlers... */
+ _exit (reader_process (s));
+ }
+ else if (s->reader_pid == -1)
+ {
+ s->busy = SANE_FALSE;
+ do_cancel(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+#endif /* USE_FORK */
+
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_RESOLUTION].w);
+
+ s->busy = SANE_FALSE;
+ s->buf_used = 0;
+ s->buf_pos = 0;
+
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ DBG (10, ">>\n");
+ return(SANE_STATUS_CANCELLED);
+ }
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+
+}
+
+static SANE_Status
+sane_read_direct (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ DBG (10, "<< sane_read_direct ");
+
+#if 0
+ {
+ buffer_status bs;
+ size_t len = sizeof (buffer_status);
+ get_data_buffer_status (s->fd, &bs, &len);
+ DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]);
+ }
+#endif
+ DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
+ *len = 0;
+
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ return (do_cancel (s));
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ if (nread > s->dev->info.bufsize)
+ nread = s->dev->info.bufsize;
+
+#ifdef USE_FORK
+ status = read_data(s, dst_buf, &nread);
+#else
+#ifdef NOTUSE_PCIN500
+ wait_ready(s->fd);
+#endif
+ status = read_data (s, dst_buf, &nread);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+ DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+sane_read_shuffled (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len, int eight_bit_data)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Byte *dest, *red, *green, *blue, mask;
+ SANE_Int transfer;
+ size_t nread, ntest, pixel, max_pixel, line, max_line;
+ size_t start_input, bytes_per_line_in;
+ DBG (10, "<< sane_read_shuffled ");
+
+#if 0
+ {
+ buffer_status bs;
+ size_t len = sizeof (buffer_status);
+ get_data_buffer_status (s->fd, &bs, &len);
+ DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]);
+ }
+#endif
+ *len = 0;
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ {
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ {
+ DBG (10, ">>\n");
+ return(do_cancel(s));
+ }
+
+ if (s->buf_pos < s->buf_used)
+ {
+ transfer = s->buf_used - s->buf_pos;
+ if (transfer > max_len)
+ transfer = max_len;
+
+ memcpy(dst_buf, &(s->buffer[s->buf_pos]), transfer);
+ s->buf_pos += transfer;
+ max_len -= transfer;
+ *len = transfer;
+ }
+
+ while (max_len > 0 && s->bytes_to_read > 0)
+ {
+ if (eight_bit_data)
+ {
+ nread = s->dev->info.bufsize / s->params.bytes_per_line - 1;
+ nread *= s->params.bytes_per_line;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ max_line = nread / s->params.bytes_per_line;
+ start_input = s->params.bytes_per_line;
+ bytes_per_line_in = s->params.bytes_per_line;
+ }
+ else
+ {
+ bytes_per_line_in = (s->params.pixels_per_line + 7) / 8;
+ bytes_per_line_in *= 3;
+ max_line = s->params.bytes_per_line + bytes_per_line_in;
+ max_line = s->dev->info.bufsize / max_line;
+ nread = max_line * bytes_per_line_in;
+ if (nread > s->bytes_to_read)
+ {
+ nread = s->bytes_to_read;
+ max_line = nread / bytes_per_line_in;
+ }
+ start_input = s->dev->info.bufsize - nread;
+ }
+ ntest = nread;
+
+#ifdef USE_FORK
+ status = read_data (s, &(s->buffer[start_input]), &nread);
+#else
+ status = read_data (s, &(s->buffer[start_input]), &nread);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (nread != ntest)
+ {
+ /* if this happens, something is wrong in the input buffer
+ management...
+ */
+ DBG(1, "Warning: could not read an integral number of scan lines\n");
+ DBG(1, " image will be scrambled\n");
+ }
+
+
+ s->buf_used = max_line * s->params.bytes_per_line;
+ s->buf_pos = 0;
+ s->bytes_to_read -= nread;
+ dest = s->buffer;
+ max_pixel = s->params.pixels_per_line;
+
+ if (eight_bit_data)
+ for (line = 1; line <= max_line; line++)
+ {
+ red = &(s->buffer[line * s->params.bytes_per_line]);
+ green = &(red[max_pixel]);
+ blue = &(green[max_pixel]);
+ for (pixel = 0; pixel < max_pixel; pixel++)
+ {
+ *dest++ = *red++;
+ *dest++ = *green++;
+ *dest++ = *blue++;
+ }
+ }
+ else
+ for (line = 0; line < max_line; line++)
+ {
+ red = &(s->buffer[start_input + line * bytes_per_line_in]);
+ green = &(red[(max_pixel+7)/8]);
+ blue = &(green[(max_pixel+7)/8]);
+ mask = 0x80;
+ for (pixel = 0; pixel < max_pixel; pixel++)
+ {
+ *dest++ = (*red & mask) ? 0xff : 0;
+ *dest++ = (*green & mask) ? 0xff : 0;
+ *dest++ = (*blue & mask) ? 0xff : 0;
+ mask = mask >> 1;
+ if (mask == 0)
+ {
+ mask = 0x80;
+ red++;
+ green++;
+ blue++;
+ }
+ }
+ }
+
+ transfer = max_len;
+ if (transfer > s->buf_used)
+ transfer = s->buf_used;
+ memcpy(&(dst_buf[*len]), s->buffer, transfer);
+
+ max_len -= transfer;
+ s->buf_pos += transfer;
+ *len += transfer;
+ }
+
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (10, "<< sane_read ");
+ s->busy = SANE_TRUE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ *len = 0;
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ if (s->image_composition <= 2)
+ status = sane_read_direct(handle, dst_buf, max_len, len);
+ else if (s->image_composition <= 4)
+ status = sane_read_shuffled(handle, dst_buf, max_len, len, 0);
+ else if (s->dev->sensedat.model == PCIN500)
+ status = sane_read_direct(handle, dst_buf, max_len, len);
+ else
+ status = sane_read_shuffled(handle, dst_buf, max_len, len, 1);
+
+ s->busy = SANE_FALSE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ DBG (10, ">> \n");
+ return (status);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ NEC_Scanner *s = handle;
+ DBG (10, "<< sane_cancel ");
+
+ s->cancel = SANE_TRUE;
+ if (s->busy == SANE_FALSE)
+ do_cancel(s);
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ handle = handle;
+ non_blocking = non_blocking; /* silence compilation warnings */
+
+ DBG (10, "<< sane_set_io_mode");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle;
+ fd = fd; /* silence compilation warnings */
+
+ DBG (10, "<< sane_get_select_fd");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/nec.conf.in b/backend/nec.conf.in
new file mode 100644
index 0000000..caa0783
--- /dev/null
+++ b/backend/nec.conf.in
@@ -0,0 +1 @@
+/dev/scanner
diff --git a/backend/nec.h b/backend/nec.h
new file mode 100644
index 0000000..5f03513
--- /dev/null
+++ b/backend/nec.h
@@ -0,0 +1,447 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Kazuya Fukuda
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef nec_h
+#define nec_h 1
+
+#include <sys/types.h>
+
+/* default values for configurable options.
+ Though these options are only meaningful if USE_FORK is defined,
+ they are
+ DEFAULT_BUFFERS: number of buffers allocated as shared memory
+ for the data transfer from reader_process to
+ read_data. The minimum value is 2
+ DEFAULT_BUFSIZE: default size of one buffer. Must be greater
+ than zero.
+ DEFAULT_QUEUED_READS: number of read requests queued by
+ sanei_scsi_req_enter. Since queued read requests
+ are currently only supported for Linux and
+ DomainOS, this value should automatically be set
+ dependent on the target OS...
+ For Linux, 2 is the optimum; for DomainOS, I
+ don't have any recommendation; other OS
+ should use the value zero.
+
+ The value for DEFAULT_BUFSIZE is probably too Linux-oriented...
+*/
+
+#define DEFAULT_BUFFERS 12
+#define DEFAULT_BUFSIZE 128 * 1024
+#define DEFAULT_QUEUED_READS 2
+
+#define NEC_MAJOR 0
+#define NEC_MINOR 12
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_HALFTONE,
+ OPT_PAPER,
+ OPT_SCANSOURCE,
+ OPT_GAMMA,
+#ifdef USE_CUSTOM_GAMMA
+ OPT_CUSTOM_GAMMA,
+#endif
+ OPT_RESOLUTION_GROUP,
+#ifdef USE_RESOLUTION_LIST
+ OPT_RESOLUTION_LIST,
+#endif
+ OPT_RESOLUTION,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_EDGE_EMPHASIS,
+ OPT_OR,
+ OPT_NR,
+ OPT_EDGE,
+ OPT_THRESHOLD,
+#ifdef USE_COLOR_THRESHOLD
+ OPT_THRESHOLD_R,
+ OPT_THRESHOLD_G,
+ OPT_THRESHOLD_B,
+#endif
+ OPT_LIGHTCOLOR,
+ OPT_TINT,
+ OPT_COLOR,
+ OPT_PREVIEW,
+
+#ifdef USE_CUSTOM_GAMMA
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+#endif
+ /* must come last: */
+ NUM_OPTIONS
+ }
+NEC_Option;
+
+#ifdef USE_FORK
+
+/* status defines for a buffer:
+ buffer not used / read request queued / buffer contains data
+*/
+#define SHM_EMPTY 0
+#define SHM_BUSY 1
+#define SHM_FULL 2
+typedef struct NEC_shmem_ctl
+ {
+ int shm_status; /* can be SHM_EMPTY, SHM_BUSY, SHM_FULL */
+ size_t used; /* number of bytes successfully read from scanner */
+ size_t nreq; /* number of bytes requested from scanner */
+ size_t start; /* index of the begin of used area of the buffer */
+ void *qid;
+ SANE_Byte *buffer;
+ }
+NEC_shmem_ctl;
+
+typedef struct NEC_rdr_ctl
+ {
+ int cancel; /* 1 = flag for the reader process to cancel */
+ int running; /* 1 indicates that the reader process is alive */
+ SANE_Status status; /* return status of the reader process */
+ NEC_shmem_ctl *buf_ctl;
+ }
+NEC_rdr_ctl;
+#endif /* USE_FORK */
+
+typedef enum
+ {
+ /* PCIN500, PCINXXX are used as array indices, so the corresponding
+ numbers should start at 0
+ */
+ unknown = -1,
+ PCIN500,
+ PCINXXX
+ }
+NEC_Model;
+
+typedef struct NEC_Info
+ {
+ SANE_Range res_range;
+ SANE_Range tl_x_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range br_x_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range tl_y_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range br_y_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range threshold_range;
+ SANE_Range tint_range;
+ SANE_Range color_range;
+
+ SANE_Int res_default;
+ SANE_Int x_default;
+ SANE_Int y_default;
+ SANE_Int bmu;
+ SANE_Int mud;
+ SANE_Int adf_fsu_installed;
+ SANE_String_Const scansources[5];
+ size_t buffers;
+ size_t bufsize;
+ int wanted_bufsize;
+ size_t queued_reads;
+ }
+NEC_Info;
+
+typedef struct NEC_Sense_Data
+ {
+ NEC_Model model;
+ /* flag, if conditions like "paper jam" or "cover open"
+ are considered as an error. Should be 0 for attach, else
+ a frontend might refuse to start, if the scanner returns
+ these errors.
+ */
+ int complain_on_adf_error;
+ /* Linux returns only 16 bytes of sense data... */
+ u_char sb[16];
+ }
+NEC_Sense_Data;
+
+typedef struct NEC_Device
+ {
+ struct NEC_Device *next;
+ SANE_Device sane;
+ NEC_Info info;
+ /* xxx now part of sense data NEC_Model model; */
+ NEC_Sense_Data sensedat;
+ }
+NEC_Device;
+
+typedef struct NEC_New_Device
+ {
+ struct NEC_Device *dev;
+ struct NEC_New_Device *next;
+ }
+NEC_New_Device;
+
+typedef struct NEC_Scanner
+ {
+ struct NEC_Scanner *next;
+ int fd;
+ NEC_Device *dev;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+
+ int get_params_called;
+ SANE_Byte *buffer; /* for color data re-ordering */
+ SANE_Int buf_used;
+ SANE_Int buf_pos;
+ SANE_Int modes;
+ SANE_Int res;
+ SANE_Int ulx;
+ SANE_Int uly;
+ SANE_Int width;
+ SANE_Int length;
+ SANE_Int threshold;
+ SANE_Int image_composition;
+ SANE_Int bpp;
+ SANE_Int halftone;
+ SANE_Bool reverse;
+ SANE_Bool or;
+ SANE_Bool nr;
+ SANE_Int gamma;
+ SANE_Int edge;
+ SANE_Int lightcolor;
+ SANE_Int adf_fsu_mode; /* mode selected by user */
+ SANE_Int adf_scan; /* flag, if the actual scan is an ADF scan */
+
+ SANE_Int tint;
+ SANE_Int color;
+
+ size_t bytes_to_read;
+ size_t max_lines_to_read;
+ size_t unscanned_lines;
+ SANE_Bool scanning;
+ SANE_Bool busy;
+ SANE_Bool cancel;
+#ifdef USE_CUSTOM_GAMMA
+ SANE_Int gamma_table[4][256];
+#endif
+#ifdef USE_FORK
+ pid_t reader_pid;
+ NEC_rdr_ctl *rdr_ctl;
+ int shmid;
+ size_t read_buff; /* index of the buffer actually used by read_data */
+#endif /* USE_FORK */
+ }
+NEC_Scanner;
+
+typedef struct NEC_Send
+{
+ SANE_Int dtc;
+ SANE_Int dtq;
+ SANE_Int length;
+ SANE_Byte *data;
+}
+NEC_Send;
+
+typedef struct WPDH
+{
+ u_char wpdh[6];
+ u_char wdl[2];
+}
+WPDH;
+
+typedef struct WDB
+{
+ SANE_Byte wid;
+ SANE_Byte autobit;
+ SANE_Byte x_res[2];
+ SANE_Byte y_res[2];
+
+ SANE_Byte x_ul[4];
+ SANE_Byte y_ul[4];
+ SANE_Byte width[4];
+ SANE_Byte length[4];
+
+ SANE_Byte brightness;
+ SANE_Byte threshold;
+ SANE_Byte contrast;
+ SANE_Byte image_composition;
+ SANE_Byte bpp;
+
+ SANE_Byte ht_pattern[2];
+ SANE_Byte rif_padding;
+ SANE_Byte bit_ordering[2];
+ SANE_Byte compression_type;
+ SANE_Byte compression_argument;
+ SANE_Byte reserved[6];
+}
+WDB;
+
+/* "extension" of the window descriptor block for the PC-IN500 */
+typedef struct XWDBX500
+ {
+ SANE_Byte data_length;
+ SANE_Byte control;
+ SANE_Byte format;
+ SANE_Byte gamma;
+ SANE_Byte tint;
+ SANE_Byte color;
+ SANE_Byte reserved1;
+ SANE_Byte reserved2;
+ }
+WDBX500;
+
+typedef struct window_param
+{
+ WPDH wpdh;
+ WDB wdb;
+ WDBX500 wdbx500;
+}
+window_param;
+
+typedef struct mode_sense_param
+{
+ SANE_Byte mode_data_length;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_desciptor_length;
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 6 */
+ SANE_Byte bmu;
+ SANE_Byte res2;
+ SANE_Byte mud[2];
+ SANE_Byte res3;
+ SANE_Byte res4;
+}
+mode_sense_param;
+
+typedef struct mode_sense_subdevice
+{
+ SANE_Byte mode_data_length;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_desciptor_length;
+ SANE_Byte res1[5];
+ SANE_Byte blocklength[3];
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 0x1a */
+ SANE_Byte a_mode_type;
+ SANE_Byte f_mode_type;
+ SANE_Byte res2;
+ SANE_Byte max_x[4];
+ SANE_Byte max_y[4];
+ SANE_Byte res3[2];
+ SANE_Byte x_basic_resolution[2];
+ SANE_Byte y_basic_resolution[2];
+ SANE_Byte x_max_resolution[2];
+ SANE_Byte y_max_resolution[2];
+ SANE_Byte x_min_resolution[2];
+ SANE_Byte y_min_resolution[2];
+ SANE_Byte res4;
+}
+mode_sense_subdevice;
+
+typedef struct mode_select_param
+{
+ SANE_Byte mode_param_header1;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_param_header4;
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 6 */
+ SANE_Byte res1;
+ SANE_Byte res2;
+ SANE_Byte mud[2];
+ SANE_Byte res3;
+ SANE_Byte res4;
+}
+mode_select_param;
+
+typedef struct mode_select_subdevice
+{
+ SANE_Byte mode_param_header1;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_param_header4;
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 0x1A */
+ SANE_Byte a_mode;
+ SANE_Byte f_mode;
+ SANE_Byte res[24];
+}
+mode_select_subdevice;
+
+typedef struct buffer_status
+{
+ SANE_Byte data_length[3];
+ SANE_Byte block;
+ SANE_Byte window_id;
+ SANE_Byte reserved;
+ SANE_Byte bsa[3]; /* buffer space available */
+ SANE_Byte fdb[3]; /* filled data buffer */
+}
+buffer_status;
+
+/* SCSI commands */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define MODE_SELECT6 0x15
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define MODE_SENSE6 0x1a
+#define SCAN 0x1b
+#define SEND_DIAGNOSTIC 0x1d
+#define SET_WINDOW 0x24
+#define GET_WINDOW 0x25
+#define READ 0x28
+#define SEND 0x2a
+#define GET_DATA_BUFFER_STATUS 0x34
+
+#define SENSE_LEN 18
+#define INQUIRY_LEN 36
+#define MODEPARAM_LEN 12
+#define MODE_SUBDEV_LEN 32
+#define WINDOW_LEN 76
+#define BUFFERSTATUS_LEN 12
+
+#endif /* not nec_h */
diff --git a/backend/net.c b/backend/net.c
new file mode 100644
index 0000000..16fba2f
--- /dev/null
+++ b/backend/net.c
@@ -0,0 +1,2378 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ Copyright (C) 2003, 2008 Julien BLACHE <jb@jblache.org>
+ AF-independent code + IPv6, Avahi support
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE network-based meta backend. */
+
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+#include "../include/_stdint.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_LIBC_H
+# include <libc.h> /* NeXTStep/OpenStep */
+#endif
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <netdb.h> /* OS/2 needs this _after_ <netinet/in.h>, grrr... */
+
+#ifdef WITH_AVAHI
+# include <avahi-client/client.h>
+# include <avahi-client/lookup.h>
+
+# include <avahi-common/thread-watch.h>
+# include <avahi-common/malloc.h>
+# include <avahi-common/error.h>
+
+# define SANED_SERVICE_DNS "_sane-port._tcp"
+
+static AvahiClient *avahi_client = NULL;
+static AvahiThreadedPoll *avahi_thread = NULL;
+static AvahiServiceBrowser *avahi_browser = NULL;
+#endif /* WITH_AVAHI */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_net.h"
+#include "../include/sane/sanei_codec_bin.h"
+#include "net.h"
+
+#define BACKEND_NAME net
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define NET_CONFIG_FILE "net.conf"
+
+/* Please increase version number with every change
+ (don't forget to update net.desc) */
+
+/* define the version string depending on which network code is used */
+#if defined (HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO)
+# define NET_USES_AF_INDEP
+# ifdef ENABLE_IPV6
+# define NET_VERSION "1.0.14 (AF-indep+IPv6)"
+# else
+# define NET_VERSION "1.0.14 (AF-indep)"
+# endif /* ENABLE_IPV6 */
+#else
+# undef ENABLE_IPV6
+# define NET_VERSION "1.0.14"
+#endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */
+
+static SANE_Auth_Callback auth_callback;
+static Net_Device *first_device;
+static Net_Scanner *first_handle;
+static const SANE_Device **devlist;
+static int client_big_endian; /* 1 == big endian; 0 == little endian */
+static int server_big_endian; /* 1 == big endian; 0 == little endian */
+static int depth; /* bits per pixel */
+static int connect_timeout = -1; /* timeout for connection to saned */
+
+#ifndef NET_USES_AF_INDEP
+static int saned_port;
+#endif /* !NET_USES_AF_INDEP */
+
+/* This variable is only needed, if the depth is 16bit/channel and
+ client/server have different endianness. A value of -1 means, that there's
+ no hang over; otherwise the value has to be casted to SANE_Byte. hang_over
+ means, that there is a remaining byte from a previous call to sane_read,
+ which could not be byte-swapped, e.g. because the frontend requested an odd
+ number of bytes.
+*/
+static int hang_over;
+
+/* This variable is only needed, if the depth is 16bit/channel and
+ client/server have different endianness. A value of -1 means, that there's
+ no left over; otherwise the value has to be casted to SANE_Byte. left_over
+ means, that there is a remaining byte from a previous call to sane_read,
+ which already is in the the correct byte order, but could not be returned,
+ e.g. because the frontend requested only one byte per call.
+*/
+static int left_over;
+
+
+#ifdef NET_USES_AF_INDEP
+static SANE_Status
+add_device (const char *name, Net_Device ** ndp)
+{
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *resp;
+ struct sockaddr_in *sin;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 *sin6;
+#endif /* ENABLE_IPV6 */
+
+ Net_Device *nd = NULL;
+
+ int error;
+ short sane_port = htons (6566);
+
+ DBG (1, "add_device: adding backend %s\n", name);
+
+ for (nd = first_device; nd; nd = nd->next)
+ if (strcmp (nd->name, name) == 0)
+ {
+ DBG (1, "add_device: already in list\n");
+
+ if (ndp)
+ *ndp = nd;
+
+ return SANE_STATUS_GOOD;
+ }
+
+ memset (&hints, 0, sizeof(hints));
+
+# ifdef ENABLE_IPV6
+ hints.ai_family = PF_UNSPEC;
+# else
+ hints.ai_family = PF_INET;
+# endif /* ENABLE_IPV6 */
+
+ error = getaddrinfo (name, "sane-port", &hints, &res);
+ if (error)
+ {
+ error = getaddrinfo (name, NULL, &hints, &res);
+ if (error)
+ {
+ DBG (1, "add_device: error while getting address of host %s: %s\n",
+ name, gai_strerror (error));
+
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ for (resp = res; resp != NULL; resp = resp->ai_next)
+ {
+ switch (resp->ai_family)
+ {
+ case AF_INET:
+ sin = (struct sockaddr_in *) resp->ai_addr;
+ sin->sin_port = sane_port;
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) resp->ai_addr;
+ sin6->sin6_port = sane_port;
+ break;
+#endif /* ENABLE_IPV6 */
+ }
+ }
+ }
+ }
+
+ nd = malloc (sizeof (Net_Device));
+ if (!nd)
+ {
+ DBG (1, "add_device: not enough memory for Net_Device struct\n");
+
+ freeaddrinfo (res);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (nd, 0, sizeof (Net_Device));
+ nd->name = strdup (name);
+ if (!nd->name)
+ {
+ DBG (1, "add_device: not enough memory to duplicate name\n");
+ free(nd);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ nd->addr = res;
+ nd->ctl = -1;
+
+ nd->next = first_device;
+
+ first_device = nd;
+
+ if (ndp)
+ *ndp = nd;
+ DBG (2, "add_device: backend %s added\n", name);
+ return SANE_STATUS_GOOD;
+}
+
+#else /* !NET_USES_AF_INDEP */
+
+static SANE_Status
+add_device (const char *name, Net_Device ** ndp)
+{
+ struct hostent *he;
+ Net_Device *nd;
+ struct sockaddr_in *sin;
+
+ DBG (1, "add_device: adding backend %s\n", name);
+
+ for (nd = first_device; nd; nd = nd->next)
+ if (strcmp (nd->name, name) == 0)
+ {
+ DBG (1, "add_device: already in list\n");
+
+ if (ndp)
+ *ndp = nd;
+
+ return SANE_STATUS_GOOD;
+ }
+
+ he = gethostbyname (name);
+ if (!he)
+ {
+ DBG (1, "add_device: can't get address of host %s\n", name);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (he->h_addrtype != AF_INET)
+ {
+ DBG (1, "add_device: don't know how to deal with addr family %d\n",
+ he->h_addrtype);
+ return SANE_STATUS_INVAL;
+ }
+
+ nd = malloc (sizeof (*nd));
+ if (!nd)
+ {
+ DBG (1, "add_device: not enough memory for Net_Device struct\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (nd, 0, sizeof (*nd));
+ nd->name = strdup (name);
+ if (!nd->name)
+ {
+ DBG (1, "add_device: not enough memory to duplicate name\n");
+ free (nd);
+ return SANE_STATUS_NO_MEM;
+ }
+ nd->addr.sa_family = he->h_addrtype;
+
+ sin = (struct sockaddr_in *) &nd->addr;
+ memcpy (&sin->sin_addr, he->h_addr_list[0], he->h_length);
+
+ nd->ctl = -1;
+ nd->next = first_device;
+ first_device = nd;
+ if (ndp)
+ *ndp = nd;
+ DBG (2, "add_device: backend %s added\n", name);
+ return SANE_STATUS_GOOD;
+}
+#endif /* NET_USES_AF_INDEP */
+
+
+#ifdef NET_USES_AF_INDEP
+static SANE_Status
+connect_dev (Net_Device * dev)
+{
+ struct addrinfo *addrp;
+
+ SANE_Word version_code;
+ SANE_Init_Reply reply;
+ SANE_Status status = SANE_STATUS_IO_ERROR;
+ SANE_Init_Req req;
+ SANE_Bool connected = SANE_FALSE;
+#ifdef TCP_NODELAY
+ int on = 1;
+ int level = -1;
+#endif
+ struct timeval tv;
+
+ int i;
+
+ DBG (2, "connect_dev: trying to connect to %s\n", dev->name);
+
+ for (addrp = dev->addr, i = 0; (addrp != NULL) && (connected == SANE_FALSE); addrp = addrp->ai_next, i++)
+ {
+# ifdef ENABLE_IPV6
+ if ((addrp->ai_family != AF_INET) && (addrp->ai_family != AF_INET6))
+# else /* !ENABLE_IPV6 */
+ if (addrp->ai_family != AF_INET)
+# endif /* ENABLE_IPV6 */
+ {
+ DBG (1, "connect_dev: [%d] don't know how to deal with addr family %d\n",
+ i, addrp->ai_family);
+ continue;
+ }
+
+ dev->ctl = socket (addrp->ai_family, SOCK_STREAM, 0);
+ if (dev->ctl < 0)
+ {
+ DBG (1, "connect_dev: [%d] failed to obtain socket (%s)\n",
+ i, strerror (errno));
+ dev->ctl = -1;
+ continue;
+ }
+
+ /* Set SO_SNDTIMEO for the connection to saned */
+ if (connect_timeout > 0)
+ {
+ tv.tv_sec = connect_timeout;
+ tv.tv_usec = 0;
+
+ if (setsockopt (dev->ctl, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
+ {
+ DBG (1, "connect_dev: [%d] failed to set SO_SNDTIMEO (%s)\n", i, strerror (errno));
+ }
+ }
+
+ if (connect (dev->ctl, addrp->ai_addr, addrp->ai_addrlen) < 0)
+ {
+ DBG (1, "connect_dev: [%d] failed to connect (%s)\n", i, strerror (errno));
+ dev->ctl = -1;
+ continue;
+ }
+ DBG (3, "connect_dev: [%d] connection succeeded (%s)\n", i, (addrp->ai_family == AF_INET6) ? "IPv6" : "IPv4");
+ dev->addr_used = addrp;
+ connected = SANE_TRUE;
+ }
+
+ if (connected != SANE_TRUE)
+ {
+ DBG (1, "connect_dev: couldn't connect to host (see messages above)\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+#else /* !NET_USES_AF_INDEP */
+
+static SANE_Status
+connect_dev (Net_Device * dev)
+{
+ struct sockaddr_in *sin;
+ SANE_Word version_code;
+ SANE_Init_Reply reply;
+ SANE_Status status = SANE_STATUS_IO_ERROR;
+ SANE_Init_Req req;
+#ifdef TCP_NODELAY
+ int on = 1;
+ int level = -1;
+#endif
+ struct timeval tv;
+
+ DBG (2, "connect_dev: trying to connect to %s\n", dev->name);
+
+ if (dev->addr.sa_family != AF_INET)
+ {
+ DBG (1, "connect_dev: don't know how to deal with addr family %d\n",
+ dev->addr.sa_family);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ dev->ctl = socket (dev->addr.sa_family, SOCK_STREAM, 0);
+ if (dev->ctl < 0)
+ {
+ DBG (1, "connect_dev: failed to obtain socket (%s)\n",
+ strerror (errno));
+ dev->ctl = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+ sin = (struct sockaddr_in *) &dev->addr;
+ sin->sin_port = saned_port;
+
+
+ /* Set SO_SNDTIMEO for the connection to saned */
+ if (connect_timeout > 0)
+ {
+ tv.tv_sec = connect_timeout;
+ tv.tv_usec = 0;
+
+ if (setsockopt (dev->ctl, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
+ {
+ DBG (1, "connect_dev: failed to set SO_SNDTIMEO (%s)\n", strerror (errno));
+ }
+ }
+
+ if (connect (dev->ctl, &dev->addr, sizeof (dev->addr)) < 0)
+ {
+ DBG (1, "connect_dev: failed to connect (%s)\n", strerror (errno));
+ dev->ctl = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (3, "connect_dev: connection succeeded\n");
+#endif /* NET_USES_AF_INDEP */
+
+ /* We're connected now, so reset SO_SNDTIMEO to the default value of 0 */
+ if (connect_timeout > 0)
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ if (setsockopt (dev->ctl, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
+ {
+ DBG (1, "connect_dev: failed to reset SO_SNDTIMEO (%s)\n", strerror (errno));
+ }
+ }
+
+#ifdef TCP_NODELAY
+# ifdef SOL_TCP
+ level = SOL_TCP;
+# else /* !SOL_TCP */
+ /* Look up the protocol level in the protocols database. */
+ {
+ struct protoent *p;
+ p = getprotobyname ("tcp");
+ if (p == 0)
+ DBG (1, "connect_dev: cannot look up `tcp' protocol number");
+ else
+ level = p->p_proto;
+ }
+# endif /* SOL_TCP */
+
+ if (level == -1 ||
+ setsockopt (dev->ctl, level, TCP_NODELAY, &on, sizeof (on)))
+ DBG (1, "connect_dev: failed to put send socket in TCP_NODELAY mode (%s)",
+ strerror (errno));
+#endif /* !TCP_NODELAY */
+
+ DBG (2, "connect_dev: sanei_w_init\n");
+ sanei_w_init (&dev->wire, sanei_codec_bin_init);
+ dev->wire.io.fd = dev->ctl;
+ dev->wire.io.read = read;
+ dev->wire.io.write = write;
+
+ /* exchange version codes with the server: */
+ req.version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR,
+ SANEI_NET_PROTOCOL_VERSION);
+ req.username = getlogin ();
+ DBG (2, "connect_dev: net_init (user=%s, local version=%d.%d.%d)\n",
+ req.username, V_MAJOR, V_MINOR, SANEI_NET_PROTOCOL_VERSION);
+ sanei_w_call (&dev->wire, SANE_NET_INIT,
+ (WireCodecFunc) sanei_w_init_req, &req,
+ (WireCodecFunc) sanei_w_init_reply, &reply);
+
+ if (dev->wire.status != 0)
+ {
+ DBG (1, "connect_dev: argument marshalling error (%s)\n",
+ strerror (dev->wire.status));
+ status = SANE_STATUS_IO_ERROR;
+ goto fail;
+ }
+
+ status = reply.status;
+ version_code = reply.version_code;
+ DBG (2, "connect_dev: freeing init reply (status=%s, remote "
+ "version=%d.%d.%d)\n", sane_strstatus (status),
+ SANE_VERSION_MAJOR (version_code),
+ SANE_VERSION_MINOR (version_code), SANE_VERSION_BUILD (version_code));
+ sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_init_reply, &reply);
+
+ if (status != 0)
+ {
+ DBG (1, "connect_dev: access to %s denied\n", dev->name);
+ goto fail;
+ }
+ if (SANE_VERSION_MAJOR (version_code) != V_MAJOR)
+ {
+ DBG (1, "connect_dev: major version mismatch: got %d, expected %d\n",
+ SANE_VERSION_MAJOR (version_code), V_MAJOR);
+ status = SANE_STATUS_IO_ERROR;
+ goto fail;
+ }
+ if (SANE_VERSION_BUILD (version_code) != SANEI_NET_PROTOCOL_VERSION
+ && SANE_VERSION_BUILD (version_code) != 2)
+ {
+ DBG (1, "connect_dev: network protocol version mismatch: "
+ "got %d, expected %d\n",
+ SANE_VERSION_BUILD (version_code), SANEI_NET_PROTOCOL_VERSION);
+ status = SANE_STATUS_IO_ERROR;
+ goto fail;
+ }
+ dev->wire.version = SANE_VERSION_BUILD (version_code);
+ DBG (4, "connect_dev: done\n");
+ return SANE_STATUS_GOOD;
+
+fail:
+ DBG (2, "connect_dev: closing connection to %s\n", dev->name);
+ close (dev->ctl);
+ dev->ctl = -1;
+ return status;
+}
+
+
+static SANE_Status
+fetch_options (Net_Scanner * s)
+{
+ int option_number;
+ DBG (3, "fetch_options: %p\n", (void *) s);
+
+ if (s->opt.num_options)
+ {
+ DBG (2, "fetch_options: %d option descriptors cached... freeing\n",
+ s->opt.num_options);
+ sanei_w_set_dir (&s->hw->wire, WIRE_FREE);
+ s->hw->wire.status = 0;
+ sanei_w_option_descriptor_array (&s->hw->wire, &s->opt);
+ if (s->hw->wire.status)
+ {
+ DBG (1, "fetch_options: failed to free old list (%s)\n",
+ strerror (s->hw->wire.status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ DBG (3, "fetch_options: get_option_descriptors\n");
+ sanei_w_call (&s->hw->wire, SANE_NET_GET_OPTION_DESCRIPTORS,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_option_descriptor_array, &s->opt);
+ if (s->hw->wire.status)
+ {
+ DBG (1, "fetch_options: failed to get option descriptors (%s)\n",
+ strerror (s->hw->wire.status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (s->local_opt.num_options == 0)
+ {
+ DBG (3, "fetch_options: creating %d local option descriptors\n",
+ s->opt.num_options);
+ s->local_opt.desc =
+ malloc (s->opt.num_options * sizeof (s->local_opt.desc));
+ if (!s->local_opt.desc)
+ {
+ DBG (1, "fetch_options: couldn't malloc s->local_opt.desc\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ for (option_number = 0;
+ option_number < s->opt.num_options;
+ option_number++)
+ {
+ s->local_opt.desc[option_number] =
+ malloc (sizeof (SANE_Option_Descriptor));
+ if (!s->local_opt.desc[option_number])
+ {
+ DBG (1, "fetch_options: couldn't malloc "
+ "s->local_opt.desc[%d]\n", option_number);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ s->local_opt.num_options = s->opt.num_options;
+ }
+ else if (s->local_opt.num_options != s->opt.num_options)
+ {
+ DBG (1, "fetch_options: option number count changed during runtime?\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "fetch_options: copying %d option descriptors\n",
+ s->opt.num_options);
+
+ for (option_number = 0; option_number < s->opt.num_options; option_number++)
+ {
+ memcpy (s->local_opt.desc[option_number], s->opt.desc[option_number],
+ sizeof (SANE_Option_Descriptor));
+ }
+
+ s->options_valid = 1;
+ DBG (3, "fetch_options: %d options fetched\n", s->opt.num_options);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (Net_Scanner * s)
+{
+ DBG (2, "do_cancel: %p\n", (void *) s);
+ s->hw->auth_active = 0;
+ if (s->data >= 0)
+ {
+ DBG (3, "do_cancel: closing data pipe\n");
+ close (s->data);
+ s->data = -1;
+ }
+ return SANE_STATUS_CANCELLED;
+}
+
+static void
+do_authorization (Net_Device * dev, SANE_String resource)
+{
+ SANE_Authorization_Req req;
+ SANE_Char username[SANE_MAX_USERNAME_LEN];
+ SANE_Char password[SANE_MAX_PASSWORD_LEN];
+ char *net_resource;
+
+ DBG (2, "do_authorization: dev=%p resource=%s\n", (void *) dev, resource);
+
+ dev->auth_active = 1;
+
+ memset (&req, 0, sizeof (req));
+ memset (username, 0, sizeof (SANE_Char) * SANE_MAX_USERNAME_LEN);
+ memset (password, 0, sizeof (SANE_Char) * SANE_MAX_PASSWORD_LEN);
+
+ net_resource = malloc (strlen (resource) + 6 + strlen (dev->name));
+
+ if (net_resource != NULL)
+ {
+ sprintf (net_resource, "net:%s:%s", dev->name, resource);
+ if (auth_callback)
+ {
+ DBG (2, "do_authorization: invoking auth_callback, resource = %s\n",
+ net_resource);
+ (*auth_callback) (net_resource, username, password);
+ }
+ else
+ DBG (1, "do_authorization: no auth_callback present\n");
+ free (net_resource);
+ }
+ else /* Is this necessary? If we don't have these few bytes we will get
+ in trouble later anyway */
+ {
+ DBG (1, "do_authorization: not enough memory for net_resource\n");
+ if (auth_callback)
+ {
+ DBG (2, "do_authorization: invoking auth_callback, resource = %s\n",
+ resource);
+ (*auth_callback) (resource, username, password);
+ }
+ else
+ DBG (1, "do_authorization: no auth_callback present\n");
+ }
+
+ if (dev->auth_active)
+ {
+ SANE_Word ack;
+
+ req.resource = resource;
+ req.username = username;
+ req.password = password;
+ DBG (2, "do_authorization: relaying authentication data\n");
+ sanei_w_call (&dev->wire, SANE_NET_AUTHORIZE,
+ (WireCodecFunc) sanei_w_authorization_req, &req,
+ (WireCodecFunc) sanei_w_word, &ack);
+ }
+ else
+ DBG (1, "do_authorization: auth_active is false... strange\n");
+}
+
+
+#ifdef WITH_AVAHI
+static void
+net_avahi_resolve_callback (AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiResolverEvent event, const char *name, const char *type,
+ const char *domain, const char *host_name, const AvahiAddress *address,
+ uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags,
+ void *userdata)
+{
+ char a[AVAHI_ADDRESS_STR_MAX];
+ char *t;
+
+ /* unused */
+ interface = interface;
+ protocol = protocol;
+ userdata = userdata;
+
+ if (!r)
+ return;
+
+ switch (event)
+ {
+ case AVAHI_RESOLVER_FAILURE:
+ DBG (1, "net_avahi_resolve_callback: failed to resolve service '%s' of type '%s' in domain '%s': %s\n",
+ name, type, domain, avahi_strerror (avahi_client_errno (avahi_service_resolver_get_client (r))));
+ break;
+
+ case AVAHI_RESOLVER_FOUND:
+ DBG (3, "net_avahi_resolve_callback: service '%s' of type '%s' in domain '%s':\n", name, type, domain);
+
+ avahi_address_snprint(a, sizeof (a), address);
+ t = avahi_string_list_to_string (txt);
+
+ DBG (3, "\t%s:%u (%s)\n\tTXT=%s\n\tcookie is %u\n\tis_local: %i\n\tour_own: %i\n"
+ "\twide_area: %i\n\tmulticast: %i\n\tcached: %i\n",
+ host_name, port, a, t, avahi_string_list_get_service_cookie (txt),
+ !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
+ !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
+ !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
+
+ /* TODO: evaluate TXT record */
+
+ /* Try first with the name */
+ if (add_device (host_name, NULL) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "net_avahi_resolve_callback: couldn't add backend with name %s\n", host_name);
+
+ /* Then try the raw IP address */
+ if (add_device (t, NULL) != SANE_STATUS_GOOD)
+ DBG (1, "net_avahi_resolve_callback: couldn't add backend with IP address %s either\n", t);
+ }
+
+ avahi_free (t);
+ break;
+ }
+
+ avahi_service_resolver_free(r);
+}
+
+static void
+net_avahi_browse_callback (AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiBrowserEvent event, const char *name, const char *type,
+ const char *domain, AvahiLookupResultFlags flags, void *userdata)
+{
+ AvahiProtocol proto;
+
+ /* unused */
+ flags = flags;
+ userdata = userdata;
+
+ if (!b)
+ return;
+
+ switch (event)
+ {
+ case AVAHI_BROWSER_FAILURE:
+ DBG (1, "net_avahi_browse_callback: %s\n", avahi_strerror (avahi_client_errno (avahi_service_browser_get_client (b))));
+ avahi_threaded_poll_quit (avahi_thread);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ DBG (3, "net_avahi_browse_callback: NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+
+ /* The server will actually be added to our list in the resolver callback */
+
+ /* The resolver object will be freed in the resolver callback, or by
+ * the server if it terminates before the callback is called.
+ */
+#ifdef ENABLE_IPV6
+ proto = AVAHI_PROTO_UNSPEC;
+#else
+ proto = AVAHI_PROTO_INET;
+#endif /* ENABLE_IPV6 */
+ if (!(avahi_service_resolver_new (avahi_client, interface, protocol, name, type, domain, proto, 0, net_avahi_resolve_callback, NULL)))
+ DBG (2, "net_avahi_browse_callback: failed to resolve service '%s': %s\n", name, avahi_strerror (avahi_client_errno (avahi_client)));
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ DBG (3, "net_avahi_browse_callback: REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+ /* With the current architecture, we cannot safely remove a server from the list */
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ DBG (3, "net_avahi_browse_callback: %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+static void
+net_avahi_callback (AvahiClient *c, AvahiClientState state, void * userdata)
+{
+ AvahiProtocol proto;
+ int error;
+
+ /* unused */
+ userdata = userdata;
+
+ if (!c)
+ return;
+
+ switch (state)
+ {
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+
+ case AVAHI_CLIENT_S_COLLISION:
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ if (avahi_browser)
+ return;
+
+#ifdef ENABLE_IPV6
+ proto = AVAHI_PROTO_UNSPEC;
+#else
+ proto = AVAHI_PROTO_INET;
+#endif /* ENABLE_IPV6 */
+
+ avahi_browser = avahi_service_browser_new (c, AVAHI_IF_UNSPEC, proto, SANED_SERVICE_DNS, NULL, 0, net_avahi_browse_callback, NULL);
+ if (avahi_browser == NULL)
+ {
+ DBG (1, "net_avahi_callback: could not create service browser: %s\n", avahi_strerror (avahi_client_errno (c)));
+ avahi_threaded_poll_quit (avahi_thread);
+ }
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ error = avahi_client_errno (c);
+
+ if (error == AVAHI_ERR_DISCONNECTED)
+ {
+ /* Server disappeared - try to reconnect */
+ avahi_client_free (avahi_client);
+ avahi_client = NULL;
+
+ if (avahi_browser)
+ {
+ avahi_service_browser_free (avahi_browser);
+ avahi_browser = NULL;
+ }
+
+ avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_thread), AVAHI_CLIENT_NO_FAIL, net_avahi_callback, NULL, &error);
+ if (avahi_client == NULL)
+ {
+ DBG (1, "net_avahi_init: could not create Avahi client: %s\n", avahi_strerror (error));
+ avahi_threaded_poll_quit (avahi_thread);
+ }
+ }
+ else
+ {
+ /* Another error happened - game over */
+ DBG (1, "net_avahi_callback: server connection failure: %s\n", avahi_strerror (error));
+ avahi_threaded_poll_quit (avahi_thread);
+ }
+ break;
+ }
+}
+
+
+static void
+net_avahi_init (void)
+{
+ int error;
+
+ avahi_thread = avahi_threaded_poll_new ();
+ if (avahi_thread == NULL)
+ {
+ DBG (1, "net_avahi_init: could not create threaded poll object\n");
+ goto fail;
+ }
+
+ avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_thread), AVAHI_CLIENT_NO_FAIL, net_avahi_callback, NULL, &error);
+ if (avahi_client == NULL)
+ {
+ DBG (1, "net_avahi_init: could not create Avahi client: %s\n", avahi_strerror (error));
+ goto fail;
+ }
+
+ if (avahi_threaded_poll_start (avahi_thread) < 0)
+ {
+ DBG (1, "net_avahi_init: Avahi thread failed to start\n");
+ goto fail;
+ }
+
+ /* All done */
+ return;
+
+ fail:
+ DBG (1, "net_avahi_init: Avahi init failed, support disabled\n");
+
+ if (avahi_client)
+ {
+ avahi_client_free (avahi_client);
+ avahi_client = NULL;
+ }
+
+ if (avahi_thread)
+ {
+ avahi_threaded_poll_free (avahi_thread);
+ avahi_thread = NULL;
+ }
+}
+
+static void
+net_avahi_cleanup (void)
+{
+ if (!avahi_thread)
+ return;
+
+ DBG (1, "net_avahi_cleanup: stopping thread\n");
+
+ avahi_threaded_poll_stop (avahi_thread);
+
+ if (avahi_browser)
+ avahi_service_browser_free (avahi_browser);
+
+ if (avahi_client)
+ avahi_client_free (avahi_client);
+
+ avahi_threaded_poll_free (avahi_thread);
+
+ DBG (1, "net_avahi_cleanup: done\n");
+}
+#endif /* WITH_AVAHI */
+
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char device_name[PATH_MAX];
+ const char *optval;
+ const char *env;
+ size_t len;
+ FILE *fp;
+ short ns = 0x1234;
+ unsigned char *p = (unsigned char *)(&ns);
+
+#ifndef NET_USES_AF_INDEP
+ struct servent *serv;
+#endif /* !NET_USES_AF_INDEP */
+
+ DBG_INIT ();
+
+ DBG (2, "sane_init: authorize %s null, version_code %s null\n", (authorize) ? "!=" : "==",
+ (version_code) ? "!=" : "==");
+
+ devlist = NULL;
+ first_device = NULL;
+ first_handle = NULL;
+
+#ifdef WITH_AVAHI
+ net_avahi_init ();
+#endif /* WITH_AVAHI */
+
+ auth_callback = authorize;
+
+ /* Return the version number of the sane-backends package to allow
+ the frontend to print them. This is done only for net and dll,
+ because these backends are usually called by the frontend. */
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_DLL_V_MAJOR, SANE_DLL_V_MINOR,
+ SANE_DLL_V_BUILD);
+
+ DBG (1, "sane_init: SANE net backend version %s from %s\n", NET_VERSION,
+ PACKAGE_STRING);
+
+ /* determine (client) machine byte order */
+ if (*p == 0x12)
+ {
+ client_big_endian = 1;
+ DBG (3, "sane_init: Client has big endian byte order\n");
+ }
+ else
+ {
+ client_big_endian = 0;
+ DBG (3, "sane_init: Client has little endian byte order\n");
+ }
+
+#ifndef NET_USES_AF_INDEP
+ DBG (2, "sane_init: determining sane service port\n");
+ serv = getservbyname ("sane-port", "tcp");
+
+ if (serv)
+ {
+ DBG (2, "sane_init: found port %d\n", ntohs (serv->s_port));
+ saned_port = serv->s_port;
+ }
+ else
+ {
+ saned_port = htons (6566);
+ DBG (1, "sane_init: could not find `sane-port' service (%s); using default "
+ "port %d\n", strerror (errno), ntohs (saned_port));
+ }
+#endif /* !NET_USES_AF_INDEP */
+
+ DBG (2, "sane_init: searching for config file\n");
+ fp = sanei_config_open (NET_CONFIG_FILE);
+ if (fp)
+ {
+ while (sanei_config_read (device_name, sizeof (device_name), fp))
+ {
+ if (device_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (device_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ /*
+ * Check for net backend options.
+ * Anything that isn't an option is a saned host.
+ */
+ if (strstr(device_name, "connect_timeout") != NULL)
+ {
+ /* Look for the = sign; if it's not there, error out */
+ optval = strchr(device_name, '=');
+
+ if (!optval)
+ continue;
+
+ optval = sanei_config_skip_whitespace (++optval);
+ if ((optval != NULL) && (*optval != '\0'))
+ {
+ connect_timeout = atoi(optval);
+
+ DBG (2, "sane_init: connect timeout set to %d seconds\n", connect_timeout);
+ }
+
+ continue;
+ }
+
+ DBG (2, "sane_init: trying to add %s\n", device_name);
+ add_device (device_name, 0);
+ }
+
+ fclose (fp);
+ DBG (2, "sane_init: done reading config\n");
+ }
+ else
+ DBG (1, "sane_init: could not open config file (%s): %s\n",
+ NET_CONFIG_FILE, strerror (errno));
+
+ DBG (2, "sane_init: evaluating environment variable SANE_NET_HOSTS\n");
+ env = getenv ("SANE_NET_HOSTS");
+ if (env)
+ {
+ char *copy, *next, *host;
+ if ((copy = strdup (env)) != NULL)
+ {
+ next = copy;
+ while ((host = strsep (&next, ":")))
+ {
+#ifdef ENABLE_IPV6
+ if (host[0] == '[')
+ {
+ /* skip '[' (host[0]) */
+ host++;
+ /* get the rest of the IPv6 addr (we're screwed if ] is missing)
+ * Is it worth checking for the matching ] ? Not for now. */
+ strsep (&next, "]");
+ /* add back the ":" that got removed by the strsep() */
+ host[strlen (host)] = ':';
+ /* host now holds the IPv6 address */
+
+ /* skip the ':' that could be after ] (avoids a call to strsep() */
+ if (next[0] == ':')
+ next++;
+ }
+
+ /*
+ * if the IPv6 is last in the list, the strsep() call in the while()
+ * will return a string with the first char being '\0'. Skip it.
+ */
+ if (host[0] == '\0')
+ continue;
+#endif /* ENABLE_IPV6 */
+ DBG (2, "sane_init: trying to add %s\n", host);
+ add_device (host, 0);
+ }
+ free (copy);
+ }
+ else
+ DBG (1, "sane_init: not enough memory to duplicate "
+ "environment variable\n");
+ }
+
+ DBG (2, "sane_init: evaluating environment variable SANE_NET_TIMEOUT\n");
+ env = getenv ("SANE_NET_TIMEOUT");
+ if (env)
+ {
+ connect_timeout = atoi(env);
+ DBG (2, "sane_init: connect timeout set to %d seconds from env\n", connect_timeout);
+ }
+
+ DBG (2, "sane_init: done\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Net_Scanner *handle, *next_handle;
+ Net_Device *dev, *next_device;
+ int i;
+
+ DBG (1, "sane_exit: exiting\n");
+
+#ifdef WITH_AVAHI
+ net_avahi_cleanup ();
+#endif /* WITH_AVAHI */
+
+ /* first, close all handles: */
+ for (handle = first_handle; handle; handle = next_handle)
+ {
+ next_handle = handle->next;
+ sane_close (handle);
+ }
+ first_handle = 0;
+
+ /* now close all devices: */
+ for (dev = first_device; dev; dev = next_device)
+ {
+ next_device = dev->next;
+
+ DBG (2, "sane_exit: closing dev %p, ctl=%d\n", (void *) dev, dev->ctl);
+
+ if (dev->ctl >= 0)
+ {
+ sanei_w_call (&dev->wire, SANE_NET_EXIT,
+ (WireCodecFunc) sanei_w_void, 0,
+ (WireCodecFunc) sanei_w_void, 0);
+ sanei_w_exit (&dev->wire);
+ close (dev->ctl);
+ }
+ if (dev->name)
+ free ((void *) dev->name);
+
+#ifdef NET_USES_AF_INDEP
+ if (dev->addr)
+ freeaddrinfo(dev->addr);
+#endif /* NET_USES_AF_INDEP */
+
+ free (dev);
+ }
+ if (devlist)
+ {
+ for (i = 0; devlist[i]; ++i)
+ {
+ if (devlist[i]->vendor)
+ free ((void *) devlist[i]->vendor);
+ if (devlist[i]->model)
+ free ((void *) devlist[i]->model);
+ if (devlist[i]->type)
+ free ((void *) devlist[i]->type);
+ free ((void *) devlist[i]);
+ }
+ free (devlist);
+ }
+ DBG (3, "sane_exit: finished.\n");
+}
+
+/* Note that a call to get_devices() implies that we'll have to
+ connect to all remote hosts. To avoid this, you can call
+ sane_open() directly (assuming you know the name of the
+ backend/device). This is appropriate for the command-line
+ interface of SANE, for example.
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static int devlist_size = 0, devlist_len = 0;
+ static const SANE_Device *empty_devlist[1] = { 0 };
+ SANE_Get_Devices_Reply reply;
+ SANE_Status status;
+ Net_Device *dev;
+ char *full_name;
+ int i, num_devs;
+ size_t len;
+#define ASSERT_SPACE(n) \
+ { \
+ if (devlist_len + (n) > devlist_size) \
+ { \
+ devlist_size += (n) + 15; \
+ if (devlist) \
+ devlist = realloc (devlist, devlist_size * sizeof (devlist[0])); \
+ else \
+ devlist = malloc (devlist_size * sizeof (devlist[0])); \
+ if (!devlist) \
+ { \
+ DBG (1, "sane_get_devices: not enough memory\n"); \
+ return SANE_STATUS_NO_MEM; \
+ } \
+ } \
+ }
+
+ DBG (3, "sane_get_devices: local_only = %d\n", local_only);
+
+ if (local_only)
+ {
+ *device_list = empty_devlist;
+ return SANE_STATUS_GOOD;
+ }
+
+ if (devlist)
+ {
+ DBG (2, "sane_get_devices: freeing devlist\n");
+ for (i = 0; devlist[i]; ++i)
+ {
+ if (devlist[i]->vendor)
+ free ((void *) devlist[i]->vendor);
+ if (devlist[i]->model)
+ free ((void *) devlist[i]->model);
+ if (devlist[i]->type)
+ free ((void *) devlist[i]->type);
+ free ((void *) devlist[i]);
+ }
+ free (devlist);
+ devlist = 0;
+ }
+ devlist_len = 0;
+ devlist_size = 0;
+
+ for (dev = first_device; dev; dev = dev->next)
+ {
+ if (dev->ctl < 0)
+ {
+ status = connect_dev (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_get_devices: ignoring failure to connect to %s\n",
+ dev->name);
+ continue;
+ }
+ }
+ sanei_w_call (&dev->wire, SANE_NET_GET_DEVICES,
+ (WireCodecFunc) sanei_w_void, 0,
+ (WireCodecFunc) sanei_w_get_devices_reply, &reply);
+ if (reply.status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_get_devices: ignoring rpc-returned status %s\n",
+ sane_strstatus (reply.status));
+ sanei_w_free (&dev->wire,
+ (WireCodecFunc) sanei_w_get_devices_reply, &reply);
+ continue;
+ }
+
+ /* count the number of devices for this backend: */
+ for (num_devs = 0; reply.device_list[num_devs]; ++num_devs);
+
+ ASSERT_SPACE (num_devs);
+
+ for (i = 0; i < num_devs; ++i)
+ {
+ SANE_Device *rdev;
+ char *mem;
+#ifdef ENABLE_IPV6
+ SANE_Bool IPv6 = SANE_FALSE;
+#endif /* ENABLE_IPV6 */
+
+ /* create a new device entry with a device name that is the
+ sum of the backend name a colon and the backend's device
+ name: */
+ len = strlen (dev->name) + 1 + strlen (reply.device_list[i]->name);
+
+#ifdef ENABLE_IPV6
+ if (strchr (dev->name, ':') != NULL)
+ {
+ len += 2;
+ IPv6 = SANE_TRUE;
+ }
+#endif /* ENABLE_IPV6 */
+
+ mem = malloc (sizeof (*dev) + len + 1);
+ if (!mem)
+ {
+ DBG (1, "sane_get_devices: not enough free memory\n");
+ sanei_w_free (&dev->wire,
+ (WireCodecFunc) sanei_w_get_devices_reply,
+ &reply);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (mem, 0, sizeof (*dev) + len);
+ full_name = mem + sizeof (*dev);
+
+#ifdef ENABLE_IPV6
+ if (IPv6 == SANE_TRUE)
+ strcat (full_name, "[");
+#endif /* ENABLE_IPV6 */
+
+ strcat (full_name, dev->name);
+
+#ifdef ENABLE_IPV6
+ if (IPv6 == SANE_TRUE)
+ strcat (full_name, "]");
+#endif /* ENABLE_IPV6 */
+
+ strcat (full_name, ":");
+ strcat (full_name, reply.device_list[i]->name);
+ DBG (3, "sane_get_devices: got %s\n", full_name);
+
+ rdev = (SANE_Device *) mem;
+ rdev->name = full_name;
+ rdev->vendor = strdup (reply.device_list[i]->vendor);
+ rdev->model = strdup (reply.device_list[i]->model);
+ rdev->type = strdup (reply.device_list[i]->type);
+
+ if ((!rdev->vendor) || (!rdev->model) || (!rdev->type))
+ {
+ DBG (1, "sane_get_devices: not enough free memory\n");
+ if (rdev->vendor)
+ free ((void *) rdev->vendor);
+ if (rdev->model)
+ free ((void *) rdev->model);
+ if (rdev->type)
+ free ((void *) rdev->type);
+ free (rdev);
+ sanei_w_free (&dev->wire,
+ (WireCodecFunc) sanei_w_get_devices_reply,
+ &reply);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ devlist[devlist_len++] = rdev;
+ }
+ /* now free up the rpc return value: */
+ sanei_w_free (&dev->wire,
+ (WireCodecFunc) sanei_w_get_devices_reply, &reply);
+ }
+
+ /* terminate device list with NULL entry: */
+ ASSERT_SPACE (1);
+ devlist[devlist_len++] = 0;
+
+ *device_list = devlist;
+ DBG (2, "sane_get_devices: finished (%d devices)\n", devlist_len - 1);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
+{
+ SANE_Open_Reply reply;
+ const char *dev_name;
+#ifdef ENABLE_IPV6
+ const char *tmp_name;
+ SANE_Bool v6addr = SANE_FALSE;
+#endif /* ENABLE_IPV6 */
+ SANE_String nd_name;
+ SANE_Status status;
+ SANE_Word handle;
+ SANE_Word ack;
+ Net_Device *dev;
+ Net_Scanner *s;
+ int need_auth;
+
+ DBG (3, "sane_open(\"%s\")\n", full_name);
+
+#ifdef ENABLE_IPV6
+ /*
+ * Check whether a numerical IPv6 host was specified
+ * [2001:42:42::12] <== check for '[' as full_name[0]
+ * ex: [2001:42:42::12]:test:0 (syntax taken from Apache 2)
+ */
+ if (full_name[0] == '[')
+ {
+ v6addr = SANE_TRUE;
+ tmp_name = strchr (full_name, ']');
+ if (!tmp_name)
+ {
+ DBG (1, "sane_open: incorrect host address: missing matching ']'\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ tmp_name = full_name;
+
+ dev_name = strchr (tmp_name, ':');
+#else /* !ENABLE_IPV6 */
+
+ dev_name = strchr (full_name, ':');
+#endif /* ENABLE_IPV6 */
+
+ if (dev_name)
+ {
+#ifdef strndupa
+# ifdef ENABLE_IPV6
+ if (v6addr == SANE_TRUE)
+ nd_name = strndupa (full_name + 1, dev_name - full_name - 2);
+ else
+ nd_name = strndupa (full_name, dev_name - full_name);
+
+# else /* !ENABLE_IPV6 */
+
+ nd_name = strndupa (full_name, dev_name - full_name);
+# endif /* ENABLE_IPV6 */
+
+ if (!nd_name)
+ {
+ DBG (1, "sane_open: not enough free memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+#else
+ char *tmp;
+
+# ifdef ENABLE_IPV6
+ if (v6addr == SANE_TRUE)
+ tmp = alloca (dev_name - full_name - 2 + 1);
+ else
+ tmp = alloca (dev_name - full_name + 1);
+
+# else /* !ENABLE_IPV6 */
+
+ tmp = alloca (dev_name - full_name + 1);
+# endif /* ENABLE_IPV6 */
+
+ if (!tmp)
+ {
+ DBG (1, "sane_open: not enough free memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+# ifdef ENABLE_IPV6
+ if (v6addr == SANE_TRUE)
+ {
+ memcpy (tmp, full_name + 1, dev_name - full_name - 2);
+ tmp[dev_name - full_name - 2] = '\0';
+ }
+ else
+ {
+ memcpy (tmp, full_name, dev_name - full_name);
+ tmp[dev_name - full_name] = '\0';
+ }
+
+# else /* !ENABLE_IPV6 */
+
+ memcpy (tmp, full_name, dev_name - full_name);
+ tmp[dev_name - full_name] = '\0';
+# endif /* ENABLE_IPV6 */
+
+ nd_name = tmp;
+#endif
+ ++dev_name; /* skip colon */
+ }
+ else
+ {
+ /* if no colon interpret full_name as the host name; an empty
+ device name will cause us to open the first device of that
+ host. */
+#ifdef ENABLE_IPV6
+ if (v6addr == SANE_TRUE)
+ {
+ nd_name = alloca (strlen (full_name) - 2 + 1);
+ if (!nd_name)
+ {
+ DBG (1, "sane_open: not enough free memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memcpy (nd_name, full_name + 1, strlen (full_name) - 2);
+ nd_name[strlen (full_name) - 2] = '\0';
+ }
+ else
+ nd_name = (char *) full_name;
+
+#else /* !ENABLE_IPV6 */
+
+ nd_name = (char *) full_name;
+#endif /* ENABLE_IPV6 */
+
+ dev_name = "";
+ }
+ DBG (2, "sane_open: host = %s, device = %s\n", nd_name, dev_name);
+
+ if (!nd_name[0])
+ {
+ /* Unlike other backends, we never allow an empty backend-name.
+ Otherwise, it's possible that sane_open("") will result in
+ endless looping (consider the case where NET is the first
+ backend...) */
+
+ DBG (1, "sane_open: empty backend name is not allowed\n");
+ return SANE_STATUS_INVAL;
+ }
+ else
+ for (dev = first_device; dev; dev = dev->next)
+ if (strcmp (dev->name, nd_name) == 0)
+ break;
+
+ if (!dev)
+ {
+ DBG (1,
+ "sane_open: device %s not found, trying to register it anyway\n",
+ nd_name);
+ status = add_device (nd_name, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: could not open device\n");
+ return status;
+ }
+ }
+ else
+ DBG (2, "sane_open: device found in list\n");
+
+ if (dev->ctl < 0)
+ {
+ DBG (2, "sane_open: device not connected yet...\n");
+ status = connect_dev (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: could not connect to device\n");
+ return status;
+ }
+ }
+
+ DBG (3, "sane_open: net_open\n");
+ sanei_w_call (&dev->wire, SANE_NET_OPEN,
+ (WireCodecFunc) sanei_w_string, &dev_name,
+ (WireCodecFunc) sanei_w_open_reply, &reply);
+ do
+ {
+ if (dev->wire.status != 0)
+ {
+ DBG (1, "sane_open: open rpc call failed (%s)\n",
+ strerror (dev->wire.status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status = reply.status;
+ handle = reply.handle;
+ need_auth = (reply.resource_to_authorize != 0);
+
+ if (need_auth)
+ {
+ DBG (3, "sane_open: authorization required\n");
+ do_authorization (dev, reply.resource_to_authorize);
+
+ sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_open_reply,
+ &reply);
+
+ if (dev->wire.direction != WIRE_DECODE)
+ sanei_w_set_dir (&dev->wire, WIRE_DECODE);
+ sanei_w_open_reply (&dev->wire, &reply);
+
+ continue;
+ }
+ else
+ sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_open_reply, &reply);
+
+ if (need_auth && !dev->auth_active)
+ {
+ DBG (2, "sane_open: open cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: remote open failed\n");
+ return reply.status;
+ }
+ }
+ while (need_auth);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ {
+ DBG (1, "sane_open: not enough free memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (s, 0, sizeof (*s));
+ s->hw = dev;
+ s->handle = handle;
+ s->data = -1;
+ s->next = first_handle;
+ s->local_opt.desc = 0;
+ s->local_opt.num_options = 0;
+
+ DBG (3, "sane_open: getting option descriptors\n");
+ status = fetch_options (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_open: fetch_options failed (%s), closing device again\n",
+ sane_strstatus (status));
+
+ sanei_w_call (&s->hw->wire, SANE_NET_CLOSE,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_word, &ack);
+
+ free (s);
+
+ return status;
+ }
+
+ first_handle = s;
+ *meta_handle = s;
+
+ DBG (3, "sane_open: success\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Net_Scanner *prev, *s;
+ SANE_Word ack;
+ int option_number;
+
+ DBG (3, "sane_close: handle %p\n", handle);
+
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ if (s->opt.num_options)
+ {
+ DBG (2, "sane_close: removing cached option descriptors\n");
+ sanei_w_set_dir (&s->hw->wire, WIRE_FREE);
+ s->hw->wire.status = 0;
+ sanei_w_option_descriptor_array (&s->hw->wire, &s->opt);
+ if (s->hw->wire.status)
+ DBG (1, "sane_close: couldn't free sanei_w_option_descriptor_array "
+ "(%s)\n", sane_strstatus (s->hw->wire.status));
+ }
+
+ DBG (2, "sane_close: removing local option descriptors\n");
+ for (option_number = 0; option_number < s->local_opt.num_options;
+ option_number++)
+ free (s->local_opt.desc[option_number]);
+ if (s->local_opt.desc)
+ free (s->local_opt.desc);
+
+ DBG (2, "sane_close: net_close\n");
+ sanei_w_call (&s->hw->wire, SANE_NET_CLOSE,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_word, &ack);
+ if (s->data >= 0)
+ {
+ DBG (2, "sane_close: closing data pipe\n");
+ close (s->data);
+ }
+ free (s);
+ DBG (2, "sane_close: done\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Net_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (3, "sane_get_option_descriptor: option %d\n", option);
+
+ if (!s->options_valid)
+ {
+ DBG (3, "sane_get_option_descriptor: getting option descriptors\n");
+ status = fetch_options (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_get_option_descriptor: fetch_options failed (%s)\n",
+ sane_strstatus (status));
+ return 0;
+ }
+ }
+
+ if (((SANE_Word) option >= s->opt.num_options) || (option < 0))
+ {
+ DBG (2, "sane_get_option_descriptor: invalid option number\n");
+ return 0;
+ }
+ return s->local_opt.desc[option];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Word * info)
+{
+ Net_Scanner *s = handle;
+ SANE_Control_Option_Req req;
+ SANE_Control_Option_Reply reply;
+ SANE_Status status;
+ size_t value_size;
+ int need_auth;
+ SANE_Word local_info;
+
+ DBG (3, "sane_control_option: option %d, action %d\n", option, action);
+
+ if (!s->options_valid)
+ {
+ DBG (1, "sane_control_option: FRONTEND BUG: option descriptors reload needed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (((SANE_Word) option >= s->opt.num_options) || (option < 0))
+ {
+ DBG (1, "sane_control_option: invalid option number\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (s->opt.desc[option]->type)
+ {
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP: /* shouldn't happen... */
+ /* the SANE standard defines that the option size of a BUTTON or
+ GROUP is IGNORED. */
+ value_size = 0;
+ break;
+ case SANE_TYPE_STRING: /* strings can be smaller than size */
+ value_size = s->opt.desc[option]->size;
+ if ((action == SANE_ACTION_SET_VALUE)
+ && (((SANE_Int) strlen ((SANE_String) value) + 1)
+ < s->opt.desc[option]->size))
+ value_size = strlen ((SANE_String) value) + 1;
+ break;
+ default:
+ value_size = s->opt.desc[option]->size;
+ break;
+ }
+
+ /* Avoid leaking memory bits */
+ if (value && (action != SANE_ACTION_SET_VALUE))
+ memset (value, 0, value_size);
+
+ /* for SET_AUTO the parameter ``value'' is ignored */
+ if (action == SANE_ACTION_SET_AUTO)
+ value_size = 0;
+
+ req.handle = s->handle;
+ req.option = option;
+ req.action = action;
+ req.value_type = s->opt.desc[option]->type;
+ req.value_size = value_size;
+ req.value = value;
+
+ local_info = 0;
+
+ DBG (3, "sane_control_option: remote control option\n");
+ sanei_w_call (&s->hw->wire, SANE_NET_CONTROL_OPTION,
+ (WireCodecFunc) sanei_w_control_option_req, &req,
+ (WireCodecFunc) sanei_w_control_option_reply, &reply);
+
+ do
+ {
+ status = reply.status;
+ need_auth = (reply.resource_to_authorize != 0);
+ if (need_auth)
+ {
+ DBG (3, "sane_control_option: auth required\n");
+ do_authorization (s->hw, reply.resource_to_authorize);
+ sanei_w_free (&s->hw->wire,
+ (WireCodecFunc) sanei_w_control_option_reply, &reply);
+
+ sanei_w_set_dir (&s->hw->wire, WIRE_DECODE);
+
+ sanei_w_control_option_reply (&s->hw->wire, &reply);
+ continue;
+
+ }
+ else if (status == SANE_STATUS_GOOD)
+ {
+ local_info = reply.info;
+
+ if (info)
+ *info = reply.info;
+ if (value_size > 0)
+ {
+ if ((SANE_Word) value_size == reply.value_size)
+ memcpy (value, reply.value, reply.value_size);
+ else
+ DBG (1, "sane_control_option: size changed from %d to %d\n",
+ s->opt.desc[option]->size, reply.value_size);
+ }
+
+ if (reply.info & SANE_INFO_RELOAD_OPTIONS)
+ s->options_valid = 0;
+ }
+ sanei_w_free (&s->hw->wire,
+ (WireCodecFunc) sanei_w_control_option_reply, &reply);
+ if (need_auth && !s->hw->auth_active)
+ return SANE_STATUS_CANCELLED;
+ }
+ while (need_auth);
+
+ DBG (2, "sane_control_option: remote done (%s, info %x)\n", sane_strstatus (status), local_info);
+
+ if ((status == SANE_STATUS_GOOD) && (info == NULL) && (local_info & SANE_INFO_RELOAD_OPTIONS))
+ {
+ DBG (2, "sane_control_option: reloading options as frontend does not care\n");
+
+ status = fetch_options (s);
+
+ DBG (2, "sane_control_option: reload done (%s)\n", sane_strstatus (status));
+ }
+
+ DBG (2, "sane_control_option: done (%s, info %x)\n", sane_strstatus (status), local_info);
+
+ return status;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Net_Scanner *s = handle;
+ SANE_Get_Parameters_Reply reply;
+ SANE_Status status;
+
+ DBG (3, "sane_get_parameters\n");
+
+ if (!params)
+ {
+ DBG (1, "sane_get_parameters: parameter params not supplied\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "sane_get_parameters: remote get parameters\n");
+ sanei_w_call (&s->hw->wire, SANE_NET_GET_PARAMETERS,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_get_parameters_reply, &reply);
+
+ status = reply.status;
+ *params = reply.params;
+ depth = reply.params.depth;
+ sanei_w_free (&s->hw->wire,
+ (WireCodecFunc) sanei_w_get_parameters_reply, &reply);
+
+ DBG (3, "sane_get_parameters: returned status %s\n",
+ sane_strstatus (status));
+ return status;
+}
+
+#ifdef NET_USES_AF_INDEP
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Net_Scanner *s = handle;
+ SANE_Start_Reply reply;
+ struct sockaddr_in sin;
+ struct sockaddr *sa;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 sin6;
+#endif /* ENABLE_IPV6 */
+ SANE_Status status;
+ int fd, need_auth;
+ socklen_t len;
+ uint16_t port; /* Internet-specific */
+
+
+ DBG (3, "sane_start\n");
+
+ hang_over = -1;
+ left_over = -1;
+
+ if (s->data >= 0)
+ {
+ DBG (2, "sane_start: data pipe already exists\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Do this ahead of time so in case anything fails, we can
+ recover gracefully (without hanging our server). */
+
+ switch (s->hw->addr_used->ai_family)
+ {
+ case AF_INET:
+ len = sizeof (sin);
+ sa = (struct sockaddr *) &sin;
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ len = sizeof (sin6);
+ sa = (struct sockaddr *) &sin6;
+ break;
+#endif /* ENABLE_IPV6 */
+ default:
+ DBG (1, "sane_start: unknown address family : %d\n",
+ s->hw->addr_used->ai_family);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (getpeername (s->hw->ctl, sa, &len) < 0)
+ {
+ DBG (1, "sane_start: getpeername() failed (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ fd = socket (s->hw->addr_used->ai_family, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ DBG (1, "sane_start: socket() failed (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (3, "sane_start: remote start\n");
+ sanei_w_call (&s->hw->wire, SANE_NET_START,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_start_reply, &reply);
+ do
+ {
+ status = reply.status;
+ port = reply.port;
+ if (reply.byte_order == 0x1234)
+ {
+ server_big_endian = 0;
+ DBG (1, "sane_start: server has little endian byte order\n");
+ }
+ else
+ {
+ server_big_endian = 1;
+ DBG (1, "sane_start: server has big endian byte order\n");
+ }
+
+ need_auth = (reply.resource_to_authorize != 0);
+ if (need_auth)
+ {
+ DBG (3, "sane_start: auth required\n");
+ do_authorization (s->hw, reply.resource_to_authorize);
+
+ sanei_w_free (&s->hw->wire,
+ (WireCodecFunc) sanei_w_start_reply, &reply);
+
+ sanei_w_set_dir (&s->hw->wire, WIRE_DECODE);
+
+ sanei_w_start_reply (&s->hw->wire, &reply);
+
+ continue;
+ }
+ sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply,
+ &reply);
+ if (need_auth && !s->hw->auth_active)
+ return SANE_STATUS_CANCELLED;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: remote start failed (%s)\n",
+ sane_strstatus (status));
+ close (fd);
+ return status;
+ }
+ }
+ while (need_auth);
+ DBG (3, "sane_start: remote start finished, data at port %hu\n", port);
+
+ switch (s->hw->addr_used->ai_family)
+ {
+ case AF_INET:
+ sin.sin_port = htons (port);
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ sin6.sin6_port = htons (port);
+ break;
+#endif /* ENABLE_IPV6 */
+ }
+
+ if (connect (fd, sa, len) < 0)
+ {
+ DBG (1, "sane_start: connect() failed (%s)\n", strerror (errno));
+ close (fd);
+ return SANE_STATUS_IO_ERROR;
+ }
+ shutdown (fd, 1);
+ s->data = fd;
+ s->reclen_buf_offset = 0;
+ s->bytes_remaining = 0;
+ DBG (3, "sane_start: done (%s)\n", sane_strstatus (status));
+ return status;
+}
+
+#else /* !NET_USES_AF_INDEP */
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Net_Scanner *s = handle;
+ SANE_Start_Reply reply;
+ struct sockaddr_in sin;
+ SANE_Status status;
+ int fd, need_auth;
+ socklen_t len;
+ uint16_t port; /* Internet-specific */
+
+
+ DBG (3, "sane_start\n");
+
+ hang_over = -1;
+ left_over = -1;
+
+ if (s->data >= 0)
+ {
+ DBG (2, "sane_start: data pipe already exists\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Do this ahead of time so in case anything fails, we can
+ recover gracefully (without hanging our server). */
+ len = sizeof (sin);
+ if (getpeername (s->hw->ctl, (struct sockaddr *) &sin, &len) < 0)
+ {
+ DBG (1, "sane_start: getpeername() failed (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ fd = socket (s->hw->addr.sa_family, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ DBG (1, "sane_start: socket() failed (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (3, "sane_start: remote start\n");
+ sanei_w_call (&s->hw->wire, SANE_NET_START,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_start_reply, &reply);
+ do
+ {
+
+ status = reply.status;
+ port = reply.port;
+ if (reply.byte_order == 0x1234)
+ {
+ server_big_endian = 0;
+ DBG (1, "sane_start: server has little endian byte order\n");
+ }
+ else
+ {
+ server_big_endian = 1;
+ DBG (1, "sane_start: server has big endian byte order\n");
+ }
+
+ need_auth = (reply.resource_to_authorize != 0);
+ if (need_auth)
+ {
+ DBG (3, "sane_start: auth required\n");
+ do_authorization (s->hw, reply.resource_to_authorize);
+
+ sanei_w_free (&s->hw->wire,
+ (WireCodecFunc) sanei_w_start_reply, &reply);
+
+ sanei_w_set_dir (&s->hw->wire, WIRE_DECODE);
+
+ sanei_w_start_reply (&s->hw->wire, &reply);
+
+ continue;
+ }
+ sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply,
+ &reply);
+ if (need_auth && !s->hw->auth_active)
+ return SANE_STATUS_CANCELLED;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: remote start failed (%s)\n",
+ sane_strstatus (status));
+ close (fd);
+ return status;
+ }
+ }
+ while (need_auth);
+ DBG (3, "sane_start: remote start finished, data at port %hu\n", port);
+ sin.sin_port = htons (port);
+
+ if (connect (fd, (struct sockaddr *) &sin, len) < 0)
+ {
+ DBG (1, "sane_start: connect() failed (%s)\n", strerror (errno));
+ close (fd);
+ return SANE_STATUS_IO_ERROR;
+ }
+ shutdown (fd, 1);
+ s->data = fd;
+ s->reclen_buf_offset = 0;
+ s->bytes_remaining = 0;
+ DBG (3, "sane_start: done (%s)\n", sane_strstatus (status));
+ return status;
+}
+#endif /* NET_USES_AF_INDEP */
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+{
+ Net_Scanner *s = handle;
+ ssize_t nread;
+ SANE_Int cnt;
+ SANE_Int start_cnt;
+ SANE_Int end_cnt;
+ SANE_Byte swap_buf;
+ SANE_Byte temp_hang_over;
+ int is_even;
+
+ DBG (3, "sane_read: handle=%p, data=%p, max_length=%d, length=%p\n",
+ handle, data, max_length, (void *) length);
+ if (!length)
+ {
+ DBG (1, "sane_read: length == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ is_even = 1;
+ *length = 0;
+
+ /* If there's a left over, i.e. a byte already in the correct byte order,
+ return it immediately; otherwise read may fail with a SANE_STATUS_EOF and
+ the caller never can read the last byte */
+ if ((depth == 16) && (server_big_endian != client_big_endian))
+ {
+ if (left_over > -1)
+ {
+ DBG (3, "sane_read: left_over from previous call, return "
+ "immediately\n");
+ /* return the byte, we've currently scanned; hang_over becomes
+ left_over */
+ *data = (SANE_Byte) left_over;
+ left_over = -1;
+ *length = 1;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ if (s->data < 0)
+ {
+ DBG (1, "sane_read: data pipe doesn't exist, scan cancelled?\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (s->bytes_remaining == 0)
+ {
+ /* boy, is this painful or what? */
+
+ DBG (4, "sane_read: reading packet length\n");
+ nread = read (s->data, s->reclen_buf + s->reclen_buf_offset,
+ 4 - s->reclen_buf_offset);
+ if (nread < 0)
+ {
+ DBG (3, "sane_read: read failed (%s)\n", strerror (errno));
+ if (errno == EAGAIN)
+ {
+ DBG (3, "sane_read: try again later\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG (1, "sane_read: cancelling read\n");
+ do_cancel (s);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ DBG (4, "sane_read: read %lu bytes, %d from 4 total\n", (u_long) nread,
+ s->reclen_buf_offset);
+ s->reclen_buf_offset += nread;
+ if (s->reclen_buf_offset < 4)
+ {
+ DBG (4, "sane_read: enough for now\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ s->reclen_buf_offset = 0;
+ s->bytes_remaining = (((u_long) s->reclen_buf[0] << 24)
+ | ((u_long) s->reclen_buf[1] << 16)
+ | ((u_long) s->reclen_buf[2] << 8)
+ | ((u_long) s->reclen_buf[3] << 0));
+ DBG (3, "sane_read: next record length=%ld bytes\n",
+ (long) s->bytes_remaining);
+ if (s->bytes_remaining == 0xffffffff)
+ {
+ char ch;
+
+ DBG (2, "sane_read: received error signal\n");
+
+ /* turn off non-blocking I/O (s->data will be closed anyhow): */
+ fcntl (s->data, F_SETFL, 0);
+
+ /* read the status byte: */
+ if (read (s->data, &ch, sizeof (ch)) != 1)
+ {
+ DBG (1, "sane_read: failed to read error code\n");
+ ch = SANE_STATUS_IO_ERROR;
+ }
+ DBG (1, "sane_read: error code %s\n",
+ sane_strstatus ((SANE_Status) ch));
+ do_cancel (s);
+ return (SANE_Status) ch;
+ }
+ }
+
+ if (max_length > (SANE_Int) s->bytes_remaining)
+ max_length = s->bytes_remaining;
+
+ nread = read (s->data, data, max_length);
+
+ if (nread < 0)
+ {
+ DBG (2, "sane_read: error code %s\n", strerror (errno));
+ if (errno == EAGAIN)
+ return SANE_STATUS_GOOD;
+ else
+ {
+ DBG (1, "sane_read: cancelling scan\n");
+ do_cancel (s);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ s->bytes_remaining -= nread;
+
+ *length = nread;
+ /* Check whether we are scanning with a depth of 16 bits/pixel and whether
+ server and client have different byte order. If this is true, then it's
+ neccessary to check whether read returned an odd number. If an odd number
+ has been returned, we must save the last byte.
+ */
+ if ((depth == 16) && (server_big_endian != client_big_endian))
+ {
+ DBG (1,"sane_read: client/server have different byte order; "
+ "must swap\n");
+ /* special case: 1 byte scanned and hang_over */
+ if ((nread == 1) && (hang_over > -1))
+ {
+ /* return the byte, we've currently scanned; hang_over becomes
+ left_over */
+ left_over = hang_over;
+ hang_over = -1;
+ return SANE_STATUS_GOOD;
+ }
+ /* check whether an even or an odd number of bytes has been scanned */
+ if ((nread % 2) == 0)
+ is_even = 1;
+ else
+ is_even = 0;
+ /* check, whether there's a hang over from a previous call;
+ in this case we memcopy the data up one byte */
+ if ((nread > 1) && (hang_over > -1))
+ {
+ /* store last byte */
+ temp_hang_over = *(data + nread - 1);
+ memmove (data + 1, data, nread - 1);
+ *data = (SANE_Byte) hang_over;
+ /* what happens with the last byte depends on whether the number
+ of bytes is even or odd */
+ if (is_even == 1)
+ {
+ /* number of bytes is even; no new hang_over, exchange last
+ byte with hang over; last byte becomes left_over */
+ left_over = *(data + nread - 1);
+ *(data + nread - 1) = temp_hang_over;
+ hang_over = -1;
+ start_cnt = 0;
+ /* last byte already swapped */
+ end_cnt = nread - 2;
+ }
+ else
+ {
+ /* number of bytes is odd; last byte becomes new hang_over */
+ hang_over = temp_hang_over;
+ left_over = -1;
+ start_cnt = 0;
+ end_cnt = nread - 1;
+ }
+ }
+ else if (nread == 1)
+ {
+ /* if only one byte has been read, save it as hang_over and return
+ length=0 */
+ hang_over = (int) *data;
+ *length = 0;
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ /* no hang_over; test for even or odd byte number */
+ if(is_even == 1)
+ {
+ start_cnt = 0;
+ end_cnt = *length;
+ }
+ else
+ {
+ start_cnt = 0;
+ hang_over = *(data + *length - 1);
+ *length -= 1;
+ end_cnt = *length;
+ }
+ }
+ /* swap the bytes */
+ for (cnt = start_cnt; cnt < end_cnt - 1; cnt += 2)
+ {
+ swap_buf = *(data + cnt);
+ *(data + cnt) = *(data + cnt + 1);
+ *(data + cnt + 1) = swap_buf;
+ }
+ }
+ DBG (3, "sane_read: %lu bytes read, %lu remaining\n", (u_long) nread,
+ (u_long) s->bytes_remaining);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Net_Scanner *s = handle;
+ SANE_Word ack;
+
+ DBG (3, "sane_cancel: sending net_cancel\n");
+
+ sanei_w_call (&s->hw->wire, SANE_NET_CANCEL,
+ (WireCodecFunc) sanei_w_word, &s->handle,
+ (WireCodecFunc) sanei_w_word, &ack);
+ do_cancel (s);
+ DBG (4, "sane_cancel: done\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Net_Scanner *s = handle;
+
+ DBG (3, "sane_set_io_mode: non_blocking = %d\n", non_blocking);
+ if (s->data < 0)
+ {
+ DBG (1, "sane_set_io_mode: pipe doesn't exist\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (fcntl (s->data, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ DBG (1, "sane_set_io_mode: fcntl failed (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Net_Scanner *s = handle;
+
+ DBG (3, "sane_get_select_fd\n");
+
+ if (s->data < 0)
+ {
+ DBG (1, "sane_get_select_fd: pipe doesn't exist\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->data;
+ DBG (3, "sane_get_select_fd: done; *fd = %d\n", *fd);
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/net.conf.in b/backend/net.conf.in
new file mode 100644
index 0000000..f55f29c
--- /dev/null
+++ b/backend/net.conf.in
@@ -0,0 +1,14 @@
+# This is the net backend config file.
+
+## net backend options
+# Timeout for the initial connection to saned. This will prevent the backend
+# from blocking for several minutes trying to connect to an unresponsive
+# saned host (network outage, host down, ...). Value in seconds.
+# connect_timeout = 60
+
+## saned hosts
+# Each line names a host to attach to.
+# If you list "localhost" then your backends can be accessed either
+# directly or through the net backend. Going through the net backend
+# may be necessary to access devices that need special privileges.
+# localhost
diff --git a/backend/net.h b/backend/net.h
new file mode 100644
index 0000000..7d72fdd
--- /dev/null
+++ b/backend/net.h
@@ -0,0 +1,88 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ Copyright (C) 2003 Julien BLACHE <jb@jblache.org>
+ AF-independent code + IPv6
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+#ifndef net_h
+#define net_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "../include/sane/sanei_wire.h"
+#include "../include/sane/config.h"
+
+typedef struct Net_Device
+ {
+ struct Net_Device *next;
+ const char *name;
+#if defined (HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO)
+ struct addrinfo *addr;
+ struct addrinfo *addr_used;
+#else
+ struct sockaddr addr;
+#endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */
+ int ctl; /* socket descriptor (or -1) */
+ Wire wire;
+ int auth_active;
+ }
+Net_Device;
+
+typedef struct Net_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct Net_Scanner *next;
+
+ int options_valid; /* are the options current? */
+ SANE_Option_Descriptor_Array opt, local_opt;
+
+ SANE_Word handle; /* remote handle (it's a word, not a ptr!) */
+
+ int data; /* data socket descriptor */
+ int reclen_buf_offset;
+ u_char reclen_buf[4];
+ size_t bytes_remaining; /* how many bytes left in this record? */
+
+ /* device (host) info: */
+ Net_Device *hw;
+ }
+Net_Scanner;
+
+#endif /* net_h */
diff --git a/backend/niash.c b/backend/niash.c
new file mode 100644
index 0000000..7bc8a25
--- /dev/null
+++ b/backend/niash.c
@@ -0,0 +1,1486 @@
+/*
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+/*
+ Concept for a backend for scanners based on the NIASH chipset,
+ such as HP3300C, HP3400C, HP4300C, Agfa Touch.
+ Parts of this source were inspired by other backends.
+*/
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memcpy */
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+/* definitions for debug */
+#define BACKEND_NAME niash
+#define BUILD 1
+
+#define DBG_ASSERT 1
+#define DBG_ERR 16
+#define DBG_MSG 32
+
+/* Just to avoid conflicts between niash backend and testtool */
+#define WITH_NIASH 1
+
+
+/* (source) includes for data transfer methods */
+#define STATIC static
+
+#include "niash_core.c"
+#include "niash_xfer.c"
+
+
+#define ASSERT(cond) (!(cond) ? DBG(DBG_ASSERT, "!!! ASSERT(%S) FAILED!!!\n",STRINGIFY(cond));)
+
+
+#define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4 )
+#define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_) )
+
+
+/* options enumerator */
+typedef enum
+{
+ optCount = 0,
+
+ optGroupGeometry,
+ optTLX, optTLY, optBRX, optBRY,
+ optDPI,
+
+ optGroupImage,
+ optGammaTable, /* gamma table */
+
+ optGroupMode,
+ optMode,
+
+ optGroupEnhancement,
+ optThreshold,
+
+
+ optLast,
+/* put temporarily disabled options here after optLast */
+
+ optGroupMisc,
+ optLamp,
+
+ optCalibrate,
+ optGamma /* analog gamma = single number */
+} EOptionIndex;
+
+
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+} TOptionValue;
+
+#define HW_GAMMA_SIZE 4096
+#define SANE_GAMMA_SIZE 4096
+
+typedef struct
+{
+ SANE_Option_Descriptor aOptions[optLast];
+ TOptionValue aValues[optLast];
+
+ TScanParams ScanParams;
+ THWParams HWParams;
+
+ TDataPipe DataPipe;
+ int iLinesLeft; /* lines to scan */
+ int iBytesLeft; /* bytes to read */
+ int iPixelsPerLine; /* pixels in one scan line */
+
+ SANE_Int aGammaTable[SANE_GAMMA_SIZE]; /* a 12-to-8 bit color lookup table */
+
+ /* fCancelled needed to let sane issue the cancel message
+ instead of an error message */
+ SANE_Bool fCancelled; /* SANE_TRUE if scanning cancelled */
+
+ SANE_Bool fScanning; /* SANE_TRUE if actively scanning */
+
+ int WarmUpTime; /* time to wait before a calibration starts */
+ unsigned char CalWhite[3]; /* values for the last calibration of white */
+ struct timeval WarmUpStarted;
+ /* system type to trace the time elapsed */
+} TScanner;
+
+
+/* linked list of SANE_Device structures */
+typedef struct TDevListEntry
+{
+ struct TDevListEntry *pNext;
+ SANE_Device dev;
+} TDevListEntry;
+
+
+static TDevListEntry *_pFirstSaneDev = 0;
+static int iNumSaneDev = 0;
+static const SANE_Device **_pSaneDevList = 0;
+
+
+/* option constraints */
+static const SANE_Range rangeGammaTable = { 0, 255, 1 };
+
+/* available scanner resolutions */
+static const SANE_Int setResolutions[] = { 4, 75, 150, 300, 600 };
+
+/* range of an analog gamma */
+static const SANE_Range rangeGamma = { SANE_FIX (0.25), SANE_FIX (4.0),
+ SANE_FIX (0.0)
+};
+
+/* interpolate a sane gamma table to a hardware appropriate one
+ just in case the sane gamma table would be smaller */
+static void
+_ConvertGammaTable (SANE_Word * saneGamma, unsigned char *hwGamma)
+{
+ int i;
+ int current = 0;
+ for (i = 0; i < SANE_GAMMA_SIZE; ++i)
+ {
+ int j;
+ int next;
+
+ /* highest range of copy indices */
+ next = ((i + 1) * HW_GAMMA_SIZE) / SANE_GAMMA_SIZE;
+
+ /* always copy the first */
+ hwGamma[current] = saneGamma[i];
+
+ /* the interpolation of the rest depends on the gap */
+ for (j = current + 1; j < HW_GAMMA_SIZE && j < next; ++j)
+ {
+ hwGamma[j] =
+ (saneGamma[i] * (next - j) +
+ saneGamma[i + 1] * (j - current)) / (next - current);
+ }
+ current = next;
+ }
+}
+
+/* create a unity gamma table */
+static void
+_UnityGammaTable (unsigned char *hwGamma)
+{
+ int i;
+ for (i = 0; i < HW_GAMMA_SIZE; ++i)
+ {
+ hwGamma[i] = (i * 256) / HW_GAMMA_SIZE;
+ }
+
+}
+
+static const SANE_Range rangeXmm = { 0, 220, 1 };
+static const SANE_Range rangeYmm = { 0, 296, 1 };
+static const SANE_Int startUpGamma = SANE_FIX (1.6);
+
+static const char colorStr[] = { SANE_VALUE_SCAN_MODE_COLOR };
+static const char grayStr[] = { SANE_VALUE_SCAN_MODE_GRAY };
+static const char lineartStr[] = { SANE_VALUE_SCAN_MODE_LINEART };
+
+#define DEPTH_LINEART 1
+#define DEPTH_GRAY 8
+#define DEPTH_COLOR 8
+
+#define BYTES_PER_PIXEL_GRAY 1
+#define BYTES_PER_PIXEL_COLOR 3
+
+#define BITS_PER_PIXEL_LINEART 1
+#define BITS_PER_PIXEL_GRAY DEPTH_GRAY
+#define BITS_PER_PIXEL_COLOR (DEPTH_COLOR*3)
+
+#define BITS_PER_BYTE 8
+#define BITS_PADDING (BITS_PER_BYTE-1)
+
+#define MODE_COLOR 0
+#define MODE_GRAY 1
+#define MODE_LINEART 2
+
+/* lineart treshold range */
+static const SANE_Range rangeThreshold = {
+ 0,
+ 100,
+ 1
+};
+
+/* scanning modes */
+static SANE_String_Const modeList[] = {
+ colorStr,
+ grayStr,
+ lineartStr,
+ NULL
+};
+
+static int
+_bytesPerLineLineart (int pixelsPerLine)
+{
+ return (pixelsPerLine * BITS_PER_PIXEL_LINEART +
+ BITS_PADDING) / BITS_PER_BYTE;
+}
+
+static int
+_bytesPerLineGray (int pixelsPerLine)
+{
+ return (pixelsPerLine * BITS_PER_PIXEL_GRAY + BITS_PADDING) / BITS_PER_BYTE;
+}
+
+static int
+_bytesPerLineColor (int pixelsPerLine)
+{
+ return (pixelsPerLine * BITS_PER_PIXEL_COLOR +
+ BITS_PADDING) / BITS_PER_BYTE;
+}
+
+
+/* dummy*/
+static void
+_rgb2rgb (unsigned char *buffer, int pixels, int threshold)
+{
+ /* make the compiler content */
+ buffer = buffer;
+ pixels = pixels;
+ threshold = threshold;
+}
+
+
+/* convert 24bit RGB to 8bit GRAY */
+static void
+_rgb2gray (unsigned char *buffer, int pixels, int threshold)
+{
+#define WEIGHT_R 27
+#define WEIGHT_G 54
+#define WEIGHT_B 19
+#define WEIGHT_W (WEIGHT_R + WEIGHT_G + WEIGHT_B)
+ static int aWeight[BYTES_PER_PIXEL_COLOR] =
+ { WEIGHT_R, WEIGHT_G, WEIGHT_B };
+ int nbyte = pixels * BYTES_PER_PIXEL_COLOR;
+ int acc = 0;
+ int x;
+
+ /* make the compiler content */
+ threshold = threshold;
+
+ for (x = 0; x < nbyte; ++x)
+ {
+ acc += aWeight[x % BYTES_PER_PIXEL_COLOR] * buffer[x];
+ if ((x + 1) % BYTES_PER_PIXEL_COLOR == 0)
+ {
+ buffer[x / BYTES_PER_PIXEL_COLOR] =
+ (unsigned char) (acc / WEIGHT_W);
+ acc = 0;
+ }
+ }
+#undef WEIGHT_R
+#undef WEIGHT_G
+#undef WEIGHT_B
+#undef WEIGHT_W
+}
+
+/* convert 24bit RGB to 1bit B/W */
+static void
+_rgb2lineart (unsigned char *buffer, int pixels, int threshold)
+{
+ static const int aMask[BITS_PER_BYTE] = { 128, 64, 32, 16, 8, 4, 2, 1 };
+ int acc = 0;
+ int nx;
+ int x;
+ int thresh;
+ _rgb2gray (buffer, pixels, 0);
+ nx = ((pixels + BITS_PADDING) / BITS_PER_BYTE) * BITS_PER_BYTE;
+ thresh = 255 * threshold / rangeThreshold.max;
+ for (x = 0; x < nx; ++x)
+ {
+ if (x < pixels && buffer[x] < thresh)
+ {
+ acc |= aMask[x % BITS_PER_BYTE];
+ }
+ if ((x + 1) % BITS_PER_BYTE == 0)
+ {
+ buffer[x / BITS_PER_BYTE] = (unsigned char) (acc);
+ acc = 0;
+ }
+ }
+}
+
+typedef struct tgModeParam
+{
+ SANE_Int depth;
+ SANE_Frame format;
+ int (*bytesPerLine) (int pixelsPerLine);
+ void (*adaptFormat) (unsigned char *rgbBuffer, int pixels, int threshold);
+
+} TModeParam;
+
+static const TModeParam modeParam[] = {
+ {DEPTH_COLOR, SANE_FRAME_RGB, _bytesPerLineColor, _rgb2rgb},
+ {DEPTH_GRAY, SANE_FRAME_GRAY, _bytesPerLineGray, _rgb2gray},
+ {DEPTH_LINEART, SANE_FRAME_GRAY, _bytesPerLineLineart, _rgb2lineart}
+};
+
+
+#define WARMUP_AFTERSTART 1 /* flag for 1st warm up */
+#define WARMUP_INSESSION 0
+#define WARMUP_TESTINTERVAL 15 /* test every 15sec */
+#define WARMUP_TIME 30 /* first wait is 30sec minimum */
+#define WARMUP_MAXTIME 90 /* after one and a half minute start latest */
+
+#define CAL_DEV_MAX 15
+/* maximum deviation of cal values in percent between 2 tests */
+
+/* different warm up after start and after automatic off */
+static const int aiWarmUpTime[] = { WARMUP_TESTINTERVAL, WARMUP_TIME };
+
+
+
+/* returns 1, when the warm up time "iTime" has elasped */
+static int
+_TimeElapsed (struct timeval *start, struct timeval *now, int iTime)
+{
+
+ /* this is a bit strange, but can deal with overflows */
+ if (start->tv_sec > now->tv_sec)
+ return (start->tv_sec / 2 - now->tv_sec / 2 > iTime / 2);
+ else
+ return (now->tv_sec - start->tv_sec >= iTime);
+}
+
+static void
+_WarmUpLamp (TScanner * s, int iMode)
+{
+ SANE_Bool fLampOn;
+ /* on startup don't care what was before
+ assume lamp was off, and the previous
+ cal values can never be reached */
+ if (iMode == WARMUP_AFTERSTART)
+ {
+ fLampOn = SANE_FALSE;
+ s->CalWhite[0] = s->CalWhite[1] = s->CalWhite[2] = (unsigned char) (-1);
+ }
+ else
+ GetLamp (&s->HWParams, &fLampOn);
+
+ if (!fLampOn)
+ {
+ /* get the current system time */
+ gettimeofday (&s->WarmUpStarted, 0);
+ /* determine the time to wait at least */
+ s->WarmUpTime = aiWarmUpTime[iMode];
+ /* switch on the lamp */
+ SetLamp (&s->HWParams, SANE_TRUE);
+ }
+}
+
+static void
+_WaitForLamp (TScanner * s, unsigned char *pabCalibTable)
+{
+ struct timeval now[2]; /* toggling time holder */
+ int i; /* rgb loop */
+ int iCal = 0; /* counter */
+ int iCurrent = 0; /* buffer and time-holder swap flag */
+ SANE_Bool fHasCal;
+ unsigned char CalWhite[2][3]; /* toggling buffer */
+ int iDelay = 0; /* delay loop counter */
+ _WarmUpLamp (s, SANE_FALSE);
+
+
+ /* get the time stamp for the wait loops */
+ if (s->WarmUpTime)
+ gettimeofday (&now[iCurrent], 0);
+ SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]);
+ fHasCal = SANE_TRUE;
+
+ DBG (DBG_MSG, "_WaitForLamp: first calibration\n");
+
+
+ /* wait until time has elapsed or for values to stabilze */
+ while (s->WarmUpTime)
+ {
+ /* check if the last scan has lower calibration values than
+ the current one would have */
+ if (s->WarmUpTime && fHasCal)
+ {
+ SANE_Bool fOver = SANE_TRUE;
+ for (i = 0; fOver && i < 3; ++i)
+ {
+ if (!s->CalWhite[i])
+ fOver = SANE_FALSE;
+ else if (CalWhite[iCurrent][i] < s->CalWhite[i])
+ fOver = SANE_FALSE;
+ }
+
+ /* warm up is not needed, when calibration data is above
+ the calibration data of the last scan */
+ if (fOver)
+ {
+ s->WarmUpTime = 0;
+ DBG (DBG_MSG,
+ "_WaitForLamp: Values seem stable, skipping next calibration cycle\n");
+ }
+ }
+
+
+ /* break the loop, when the longest wait time has expired
+ to prevent a hanging application,
+ even if the values might not be good, yet */
+ if (s->WarmUpTime && fHasCal && iCal)
+ {
+ /* abort, when we have waited long enough */
+ if (_TimeElapsed
+ (&s->WarmUpStarted, &now[iCurrent], WARMUP_MAXTIME))
+ {
+ /* stop idling */
+ s->WarmUpTime = 0;
+ DBG (DBG_MSG, "_WaitForLamp: WARMUP_MAXTIME=%ds elapsed!\n",
+ WARMUP_MAXTIME);
+ }
+ }
+
+
+ /* enter a delay loop, when there is still time to wait */
+ if (s->WarmUpTime)
+ {
+ /* if the (too low) calibration values have just been acquired
+ we start waiting */
+ if (fHasCal)
+ DBG (DBG_MSG, "_WaitForLamp: entering delay loop\r");
+ else
+ DBG (DBG_MSG, "_WaitForLamp: delay loop %d \r", ++iDelay);
+ sleep (1);
+ fHasCal = SANE_FALSE;
+ gettimeofday (&now[!iCurrent], 0);
+ }
+
+
+ /* look if we should check again */
+ if (s->WarmUpTime /* did we have to wait at all */
+ /* is the minimum time elapsed */
+ && _TimeElapsed (&s->WarmUpStarted, &now[!iCurrent], s->WarmUpTime)
+ /* has the minimum time elapsed since the last calibration */
+ && _TimeElapsed (&now[iCurrent], &now[!iCurrent],
+ WARMUP_TESTINTERVAL))
+ {
+ int dev = 0; /* 0 percent deviation in cal value as default */
+ iDelay = 0; /* all delays processed */
+ /* new calibration */
+ ++iCal;
+ iCurrent = !iCurrent; /* swap the test-buffer, and time-holder */
+ SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]);
+ fHasCal = SANE_TRUE;
+
+ for (i = 0; i < 3; ++i)
+ {
+ /* copy for faster and clearer access */
+ int cwa;
+ int cwb;
+ int ldev;
+ cwa = CalWhite[!iCurrent][i];
+ cwb = CalWhite[iCurrent][i];
+ /* find the biggest deviation of one color */
+ if (cwa > cwb)
+ ldev = 0;
+ else if (cwa && cwb)
+ ldev = ((cwb - cwa) * 100) / cwb;
+ else
+ ldev = 100;
+ dev = MAX (dev, ldev);
+ }
+
+ /* show the biggest deviation of the calibration values */
+ DBG (DBG_MSG, "_WaitForLamp: recalibration #%d, deviation = %d%%\n",
+ iCal, dev);
+
+ /* the deviation to the previous calibration is tolerable */
+ if (dev <= CAL_DEV_MAX)
+ s->WarmUpTime = 0;
+ }
+ }
+
+ /* remember the values of this calibration
+ for the next time */
+ for (i = 0; i < 3; ++i)
+ {
+ s->CalWhite[i] = CalWhite[iCurrent][i];
+ }
+}
+
+
+/* used, when setting gamma as 1 value */
+static void
+_SetScalarGamma (SANE_Int * aiGamma, SANE_Int sfGamma)
+{
+ int j;
+ double fGamma;
+ fGamma = SANE_UNFIX (sfGamma);
+ for (j = 0; j < SANE_GAMMA_SIZE; j++)
+ {
+ int iData;
+ iData =
+ floor (256.0 *
+ pow (((double) j / (double) SANE_GAMMA_SIZE), 1.0 / fGamma));
+ if (iData > 255)
+ iData = 255;
+ aiGamma[j] = iData;
+ }
+}
+
+
+/* return size of longest string in a string list */
+static size_t
+_MaxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+/* change a sane cap and return true, when a change took place */
+static int
+_ChangeCap (SANE_Word * pCap, SANE_Word cap, int isSet)
+{
+ SANE_Word prevCap = *pCap;
+ if (isSet)
+ {
+ *pCap |= cap;
+ }
+ else
+ {
+ *pCap &= ~cap;
+ }
+ return *pCap != prevCap;
+}
+
+
+static void
+_InitOptions (TScanner * s)
+{
+ int i;
+ SANE_Option_Descriptor *pDesc;
+ TOptionValue *pVal;
+ _SetScalarGamma (s->aGammaTable, startUpGamma);
+
+ for (i = optCount; i < optLast; i++)
+ {
+
+ pDesc = &s->aOptions[i];
+ pVal = &s->aValues[i];
+
+ /* defaults */
+ pDesc->name = "";
+ pDesc->title = "";
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = 0;
+
+ switch (i)
+ {
+
+ case optCount:
+ pDesc->title = SANE_TITLE_NUM_OPTIONS;
+ pDesc->desc = SANE_DESC_NUM_OPTIONS;
+ pDesc->cap = SANE_CAP_SOFT_DETECT;
+ pVal->w = (SANE_Word) optLast;
+ break;
+
+ case optGroupGeometry:
+ pDesc->title = "Geometry";
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->size = 0;
+ break;
+
+ case optTLX:
+ pDesc->name = SANE_NAME_SCAN_TL_X;
+ pDesc->title = SANE_TITLE_SCAN_TL_X;
+ pDesc->desc = SANE_DESC_SCAN_TL_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeXmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = rangeXmm.min;
+ break;
+
+ case optTLY:
+ pDesc->name = SANE_NAME_SCAN_TL_Y;
+ pDesc->title = SANE_TITLE_SCAN_TL_Y;
+ pDesc->desc = SANE_DESC_SCAN_TL_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeYmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = rangeYmm.min;
+ break;
+
+ case optBRX:
+ pDesc->name = SANE_NAME_SCAN_BR_X;
+ pDesc->title = SANE_TITLE_SCAN_BR_X;
+ pDesc->desc = SANE_DESC_SCAN_BR_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeXmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = 210 /* A4 width instead of rangeXmm.max */ ;
+ break;
+
+ case optBRY:
+ pDesc->name = SANE_NAME_SCAN_BR_Y;
+ pDesc->title = SANE_TITLE_SCAN_BR_Y;
+ pDesc->desc = SANE_DESC_SCAN_BR_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeYmm;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = 290 /* have a bit reserve instaed of rangeYmm.max */ ;
+ break;
+
+ case optDPI:
+ pDesc->name = SANE_NAME_SCAN_RESOLUTION;
+ pDesc->title = SANE_TITLE_SCAN_RESOLUTION;
+ pDesc->desc = SANE_DESC_SCAN_RESOLUTION;
+ pDesc->unit = SANE_UNIT_DPI;
+ pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pDesc->constraint.word_list = setResolutions;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = setResolutions[2]; /* default to 150dpi */
+ break;
+
+ case optGroupImage:
+ pDesc->title = SANE_I18N ("Image");
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->size = 0;
+ break;
+
+ case optGamma:
+ pDesc->name = SANE_NAME_ANALOG_GAMMA;
+ pDesc->title = SANE_TITLE_ANALOG_GAMMA;
+ pDesc->desc = SANE_DESC_ANALOG_GAMMA;
+ pDesc->type = SANE_TYPE_FIXED;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeGamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = startUpGamma;
+ break;
+
+ case optGammaTable:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR;
+ pDesc->size = sizeof (s->aGammaTable);
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeGammaTable;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = s->aGammaTable;
+ break;
+
+ case optGroupMisc:
+ pDesc->title = SANE_I18N ("Miscellaneous");
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->size = 0;
+ break;
+
+ case optLamp:
+ pDesc->name = "lamp";
+ pDesc->title = SANE_I18N ("Lamp status");
+ pDesc->desc = SANE_I18N ("Switches the lamp on or off.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ /* switch the lamp on when starting for first the time */
+ pVal->w = SANE_TRUE;
+ break;
+
+ case optCalibrate:
+ pDesc->name = "calibrate";
+ pDesc->title = SANE_I18N ("Calibrate");
+ pDesc->desc = SANE_I18N ("Calibrates for black and white level.");
+ pDesc->type = SANE_TYPE_BUTTON;
+ pDesc->cap = SANE_CAP_SOFT_SELECT;
+ pDesc->size = 0;
+ break;
+
+ case optGroupMode:
+ pDesc->title = SANE_I18N ("Scan Mode");
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_GROUP;
+ break;
+
+ case optMode:
+ /* scan mode */
+ pDesc->name = SANE_NAME_SCAN_MODE;
+ pDesc->title = SANE_TITLE_SCAN_MODE;
+ pDesc->desc = SANE_DESC_SCAN_MODE;
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = _MaxStringSize (modeList);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = modeList;
+ pDesc->cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
+ pVal->w = MODE_COLOR;
+ break;
+
+ case optGroupEnhancement:
+ pDesc->title = SANE_I18N ("Enhancement");
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_GROUP;
+ break;
+
+ case optThreshold:
+ pDesc->name = SANE_NAME_THRESHOLD;
+ pDesc->title = SANE_TITLE_THRESHOLD;
+ pDesc->desc = SANE_DESC_THRESHOLD;
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_PERCENT;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &rangeThreshold;
+ pDesc->cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
+ SANE_CAP_EMULATED;
+ pVal->w = 50;
+
+ default:
+ DBG (DBG_ERR, "Uninitialised option %d\n", i);
+ break;
+ }
+ }
+}
+
+
+static int
+_ReportDevice (TScannerModel * pModel, const char *pszDeviceName)
+{
+ TDevListEntry *pNew, *pDev;
+
+ DBG (DBG_MSG, "niash: _ReportDevice '%s'\n", pszDeviceName);
+
+ pNew = malloc (sizeof (TDevListEntry));
+ if (!pNew)
+ {
+ DBG (DBG_ERR, "no mem\n");
+ return -1;
+ }
+
+ /* add new element to the end of the list */
+ if (_pFirstSaneDev == 0)
+ {
+ _pFirstSaneDev = pNew;
+ }
+ else
+ {
+ for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext)
+ {
+ ;
+ }
+ pDev->pNext = pNew;
+ }
+
+ /* fill in new element */
+ pNew->pNext = 0;
+ pNew->dev.name = strdup (pszDeviceName);
+ pNew->dev.vendor = pModel->pszVendor;
+ pNew->dev.model = pModel->pszName;
+ pNew->dev.type = "flatbed scanner";
+
+ iNumSaneDev++;
+
+ return 0;
+}
+
+
+
+/*****************************************************************************/
+
+SANE_Status
+sane_init (SANE_Int * piVersion, SANE_Auth_Callback pfnAuth)
+{
+ /* prevent compiler from complaing about unused parameters */
+ pfnAuth = pfnAuth;
+
+ DBG_INIT ();
+ DBG (DBG_MSG, "sane_init\n");
+
+ if (piVersion != NULL)
+ {
+ *piVersion = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ /* initialise transfer methods */
+ iNumSaneDev = 0;
+ NiashXferInit (_ReportDevice);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_exit (void)
+{
+ TDevListEntry *pDev, *pNext;
+
+ DBG (DBG_MSG, "sane_exit\n");
+
+ /* free device list memory */
+ if (_pSaneDevList)
+ {
+ for (pDev = _pFirstSaneDev; pDev; pDev = pNext)
+ {
+ pNext = pDev->pNext;
+ free ((void *) pDev->dev.name);
+ free (pDev);
+ }
+ _pFirstSaneDev = 0;
+ free (_pSaneDevList);
+ _pSaneDevList = 0;
+ }
+}
+
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ TDevListEntry *pDev;
+ int i;
+
+ DBG (DBG_MSG, "sane_get_devices\n");
+
+ local_only = local_only;
+
+ if (_pSaneDevList)
+ {
+ free (_pSaneDevList);
+ }
+
+ _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1));
+ if (!_pSaneDevList)
+ {
+ DBG (DBG_MSG, "no mem\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ i = 0;
+ for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext)
+ {
+ _pSaneDevList[i++] = &pDev->dev;
+ }
+ _pSaneDevList[i++] = 0; /* last entry is 0 */
+
+ *device_list = _pSaneDevList;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ TScanner *s;
+
+ DBG (DBG_MSG, "sane_open: %s\n", name);
+
+ /* check the name */
+ if (strlen (name) == 0)
+ {
+ /* default to first available device */
+ name = _pFirstSaneDev->dev.name;
+ }
+
+ s = malloc (sizeof (TScanner));
+ if (!s)
+ {
+ DBG (DBG_MSG, "malloc failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (NiashOpen (&s->HWParams, name) < 0)
+ {
+ /* is this OK ? */
+ DBG (DBG_ERR, "NiashOpen failed\n");
+ free ((void *) s);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ _InitOptions (s);
+ s->fScanning = SANE_FALSE;
+ s->fCancelled = SANE_FALSE;
+ *h = s;
+
+ /* Turn on lamp by default at startup */
+ _WarmUpLamp (s, WARMUP_AFTERSTART);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle h)
+{
+ TScanner *s;
+
+ DBG (DBG_MSG, "sane_close\n");
+
+ s = (TScanner *) h;
+
+ /* turn off scanner lamp */
+ SetLamp (&s->HWParams, SANE_FALSE);
+
+ /* close scanner */
+ NiashClose (&s->HWParams);
+
+ /* free scanner object memory */
+ free ((void *) s);
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ TScanner *s;
+
+ DBG (DBG_MSG, "sane_get_option_descriptor %d\n", n);
+
+ if ((n < optCount) || (n >= optLast))
+ {
+ return NULL;
+ }
+
+ s = (TScanner *) h;
+ return &s->aOptions[n];
+}
+
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action,
+ void *pVal, SANE_Int * pInfo)
+{
+ TScanner *s;
+ SANE_Bool fVal;
+ static char szTable[100];
+ int *pi;
+ int i;
+ SANE_Int info;
+ SANE_Bool fLampIsOn;
+ SANE_Status status;
+ SANE_Bool fSame;
+
+ DBG (DBG_MSG, "sane_control_option: option %d, action %d\n", n, Action);
+
+ s = (TScanner *) h;
+ info = 0;
+
+ switch (Action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ switch (n)
+ {
+
+ /* Get options of type SANE_Word */
+ case optCount:
+ case optDPI:
+ case optGamma:
+ case optTLX:
+ case optTLY:
+ case optBRX:
+ case optBRY:
+ case optThreshold:
+ DBG (DBG_MSG,
+ "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
+ (int) s->aValues[n].w);
+ *(SANE_Word *) pVal = s->aValues[n].w;
+ break;
+
+ /* Get options of type SANE_Word array */
+ case optGammaTable:
+ DBG (DBG_MSG, "Reading gamma table\n");
+ memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size);
+ break;
+
+ case optMode:
+ DBG (DBG_MSG, "Reading scan mode %s\n",
+ modeList[s->aValues[optMode].w]);
+ strcpy ((char *) pVal, modeList[s->aValues[optMode].w]);
+ break;
+
+ /* Get options of type SANE_Bool */
+ case optLamp:
+ GetLamp (&s->HWParams, &fLampIsOn);
+ *(SANE_Bool *) pVal = fLampIsOn;
+ break;
+
+ case optCalibrate:
+ /* although this option has nothing to read,
+ it's added here to avoid a warning when running scanimage --help */
+ break;
+
+ default:
+ DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n);
+ }
+ break;
+
+
+ case SANE_ACTION_SET_VALUE:
+ if (s->fScanning)
+ {
+ DBG (DBG_ERR,
+ "sane_control_option: SANE_ACTION_SET_VALUE not allowed during scan\n");
+ return SANE_STATUS_INVAL;
+ }
+ switch (n)
+ {
+
+ case optCount:
+ return SANE_STATUS_INVAL;
+
+ case optGamma:
+ case optThreshold:
+ case optDPI:
+
+ info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+
+ case optTLX:
+ case optTLY:
+ case optBRX:
+ case optBRY:
+
+ status = sanei_constrain_value (&s->aOptions[n], pVal, &info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "Failed to constrain option %d (%s)\n", n,
+ s->aOptions[n].title);
+ return status;
+ }
+
+ /* check values if they are equal */
+ fSame = s->aValues[n].w == *(SANE_Word *) pVal;
+
+ /* set the values */
+ s->aValues[n].w = *(SANE_Word *) pVal;
+ DBG (DBG_MSG,
+ "sane_control_option: SANE_ACTION_SET_VALUE %d = %d\n", n,
+ (int) s->aValues[n].w);
+ if (n == optGamma)
+ {
+ if (!fSame && optLast > optGammaTable)
+ {
+ info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ _SetScalarGamma (s->aGammaTable, s->aValues[n].w);
+ }
+ break;
+
+ case optGammaTable:
+ DBG (DBG_MSG, "Writing gamma table\n");
+ pi = (SANE_Int *) pVal;
+ memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size);
+
+ /* prepare table for debug */
+ strcpy (szTable, "Gamma table summary:");
+ for (i = 0; i < SANE_GAMMA_SIZE; i++)
+ {
+ if ((SANE_GAMMA_SIZE / 16) && (i % (SANE_GAMMA_SIZE / 16)) == 0)
+ {
+ DBG (DBG_MSG, "%s\n", szTable);
+ szTable[0] = '\0';
+ }
+ /* test for number print */
+ if ((SANE_GAMMA_SIZE / 64) && (i % (SANE_GAMMA_SIZE / 64)) == 0)
+ {
+ sprintf (szTable + strlen(szTable), " %04X", pi[i]);
+ }
+ }
+ if (strlen (szTable))
+ {
+ DBG (DBG_MSG, "%s\n", szTable);
+ }
+ break;
+
+ case optMode:
+ {
+ SANE_Word *pCap;
+ int fCapChanged = 0;
+
+ pCap = &s->aOptions[optThreshold].cap;
+
+ if (strcmp ((char const *) pVal, colorStr) == 0)
+ {
+ s->aValues[optMode].w = MODE_COLOR;
+ fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1);
+ }
+ if (strcmp ((char const *) pVal, grayStr) == 0)
+ {
+ s->aValues[optMode].w = MODE_GRAY;
+ fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1);
+ }
+ if (strcmp ((char const *) pVal, lineartStr) == 0)
+ {
+ s->aValues[optMode].w = MODE_LINEART;
+ fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 0);
+
+ }
+ info |= SANE_INFO_RELOAD_PARAMS;
+ if (fCapChanged)
+ {
+ info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ DBG (DBG_MSG, "setting scan mode: %s\n", (char const *) pVal);
+ }
+ break;
+
+
+
+ case optLamp:
+ fVal = *(SANE_Bool *) pVal;
+ DBG (DBG_MSG, "lamp %s\n", fVal ? "on" : "off");
+ if (fVal)
+ _WarmUpLamp (s, WARMUP_INSESSION);
+ else
+ SetLamp (&s->HWParams, SANE_FALSE);
+ break;
+
+ case optCalibrate:
+/* SimpleCalib(&s->HWParams); */
+ break;
+
+ default:
+ DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n);
+ }
+ if (pInfo != NULL)
+ {
+ *pInfo |= info;
+ }
+ break;
+
+
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_UNSUPPORTED;
+
+
+ default:
+ DBG (DBG_ERR, "Invalid action (%d)\n", Action);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ TScanner *s;
+ TModeParam const *pMode;
+
+ DBG (DBG_MSG, "sane_get_parameters\n");
+
+ s = (TScanner *) h;
+
+ /* first do some checks */
+ if (s->aValues[optTLX].w >= s->aValues[optBRX].w)
+ {
+ DBG (DBG_ERR, "TLX should be smaller than BRX\n");
+ return SANE_STATUS_INVAL; /* proper error code? */
+ }
+ if (s->aValues[optTLY].w >= s->aValues[optBRY].w)
+ {
+ DBG (DBG_ERR, "TLY should be smaller than BRY\n");
+ return SANE_STATUS_INVAL; /* proper error code? */
+ }
+
+
+ pMode = &modeParam[s->aValues[optMode].w];
+
+ /* return the data */
+ p->format = pMode->format;
+ p->last_frame = SANE_TRUE;
+
+ p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w,
+ s->aValues[optDPI].w);
+ p->depth = pMode->depth;
+ p->pixels_per_line =
+ MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w,
+ s->aValues[optDPI].w);
+ p->bytes_per_line = pMode->bytesPerLine (p->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* get the scale down factor for a resolution that is
+ not supported by hardware */
+static int
+_SaneEmulateScaling (int iDpi)
+{
+ if (iDpi == 75)
+ return 2;
+ else
+ return 1;
+}
+
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ TScanner *s;
+ SANE_Parameters par;
+ int iLineCorr;
+ int iScaleDown;
+ static unsigned char abGamma[HW_GAMMA_SIZE];
+ static unsigned char abCalibTable[HW_PIXELS * 6];
+
+ DBG (DBG_MSG, "sane_start\n");
+
+ s = (TScanner *) h;
+
+ if (sane_get_parameters (h, &par) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_MSG, "Invalid scan parameters\n");
+ return SANE_STATUS_INVAL;
+ }
+ iScaleDown = _SaneEmulateScaling (s->aValues[optDPI].w);
+ s->iLinesLeft = par.lines;
+
+ /* fill in the scanparams using the option values */
+ s->ScanParams.iDpi = s->aValues[optDPI].w * iScaleDown;
+ s->ScanParams.iLpi = s->aValues[optDPI].w * iScaleDown;
+
+ /* calculate correction for filling of circular buffer */
+ iLineCorr = 3 * s->HWParams.iSensorSkew; /* usually 16 motor steps */
+ /* calculate correction for garbage lines */
+ iLineCorr += s->HWParams.iSkipLines * (HW_LPI / s->ScanParams.iLpi);
+
+ s->ScanParams.iTop =
+ MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY,
+ HW_LPI) - iLineCorr;
+ s->ScanParams.iLeft =
+ MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI);
+
+ s->ScanParams.iWidth = par.pixels_per_line * iScaleDown;
+ s->ScanParams.iHeight = par.lines * iScaleDown;
+ s->ScanParams.iBottom = HP3300C_BOTTOM;
+ s->ScanParams.fCalib = SANE_FALSE;
+
+ /* perform a simple calibration just before scanning */
+ _WaitForLamp (s, abCalibTable);
+
+ if (s->aValues[optMode].w == MODE_LINEART)
+ {
+ /* use a unity gamma table for lineart to be independent from Gamma settings */
+ _UnityGammaTable (abGamma);
+ }
+ else
+ {
+ /* copy gamma table */
+ _ConvertGammaTable (s->aGammaTable, abGamma);
+ }
+
+ WriteGammaCalibTable (abGamma, abGamma, abGamma, abCalibTable, 0, 0,
+ &s->HWParams);
+
+ /* prepare the actual scan */
+ if (!InitScan (&s->ScanParams, &s->HWParams))
+ {
+ DBG (DBG_MSG, "Invalid scan parameters\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* init data pipe */
+ s->DataPipe.iSkipLines = s->HWParams.iSkipLines;
+ /* on the hp3400 and hp4300 we cannot set the top of the scan area (yet),
+ so instead we just scan and throw away the data until the top */
+ if (s->HWParams.fReg07)
+ {
+ s->DataPipe.iSkipLines +=
+ MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY,
+ s->aValues[optDPI].w * iScaleDown);
+ }
+ s->iBytesLeft = 0;
+ s->iPixelsPerLine = par.pixels_per_line;
+
+ /* hack */
+ s->DataPipe.pabLineBuf = (unsigned char *) malloc (HW_PIXELS * 3);
+ CircBufferInit (s->HWParams.iXferHandle, &s->DataPipe,
+ par.pixels_per_line, s->ScanParams.iHeight,
+ s->ScanParams.iLpi * s->HWParams.iSensorSkew / HW_LPI,
+ s->HWParams.iReversedHead, iScaleDown, iScaleDown);
+
+ s->fScanning = SANE_TRUE;
+ s->fCancelled = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ TScanner *s;
+ TDataPipe *p;
+ TModeParam const *pMode;
+
+ DBG (DBG_MSG, "sane_read: buf=%p, maxlen=%d, ", buf, maxlen);
+
+ s = (TScanner *) h;
+
+ pMode = &modeParam[s->aValues[optMode].w];
+
+ /* sane_read only allowed after sane_start */
+ if (!s->fScanning)
+ {
+ if (s->fCancelled)
+ {
+ DBG (DBG_MSG, "\n");
+ DBG (DBG_MSG, "sane_read: sane_read cancelled\n");
+ s->fCancelled = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+ else
+ {
+ DBG (DBG_ERR,
+ "sane_read: sane_read only allowed after sane_start\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ p = &s->DataPipe;
+
+ /* anything left to read? */
+ if ((s->iLinesLeft == 0) && (s->iBytesLeft == 0))
+ {
+ CircBufferExit (p);
+ free (p->pabLineBuf);
+ p->pabLineBuf = NULL;
+ FinishScan (&s->HWParams);
+ *len = 0;
+ DBG (DBG_MSG, "\n");
+ DBG (DBG_MSG, "sane_read: end of scan\n");
+ s->fCancelled = SANE_FALSE;
+ s->fScanning = SANE_FALSE;
+ return SANE_STATUS_EOF;
+ }
+
+ /* time to read the next line? */
+ if (s->iBytesLeft == 0)
+ {
+ /* read a line from the transfer buffer */
+ if (CircBufferGetLineEx (s->HWParams.iXferHandle, p, p->pabLineBuf,
+ s->HWParams.iReversedHead, SANE_TRUE))
+ {
+ pMode->adaptFormat (p->pabLineBuf, s->iPixelsPerLine,
+ s->aValues[optThreshold].w);
+ s->iBytesLeft = pMode->bytesPerLine (s->iPixelsPerLine);
+ s->iLinesLeft--;
+ }
+ /* stop scanning further, when the read action fails
+ because we try read after the end of the buffer */
+ else
+ {
+ FinishScan (&s->HWParams);
+ CircBufferExit (p);
+ free (p->pabLineBuf);
+ p->pabLineBuf = NULL;
+ *len = 0;
+ DBG (DBG_MSG, "\n");
+ DBG (DBG_MSG, "sane_read: read after end of buffer\n");
+ s->fCancelled = SANE_FALSE;
+ s->fScanning = SANE_FALSE;
+ return SANE_STATUS_EOF;
+ }
+
+ }
+
+ /* copy (part of) a line */
+ *len = MIN (maxlen, s->iBytesLeft);
+ memcpy (buf,
+ &p->pabLineBuf[pMode->bytesPerLine (s->iPixelsPerLine) -
+ s->iBytesLeft], *len);
+ s->iBytesLeft -= *len;
+
+ DBG (DBG_MSG, " read=%d \n", *len);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_cancel (SANE_Handle h)
+{
+ TScanner *s;
+
+ DBG (DBG_MSG, "sane_cancel\n");
+
+ s = (TScanner *) h;
+ /* Make sure the scanner head returns home */
+ FinishScan (&s->HWParams);
+ /* delete allocated data */
+ if (s->fScanning)
+ {
+ CircBufferExit (&s->DataPipe);
+ free (s->DataPipe.pabLineBuf);
+ s->DataPipe.pabLineBuf = NULL;
+ DBG (DBG_MSG, "sane_cancel: freeing buffers\n");
+ }
+ s->fCancelled = SANE_TRUE;
+ s->fScanning = SANE_FALSE;
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ DBG (DBG_MSG, "sane_set_io_mode %s\n", m ? "non-blocking" : "blocking");
+
+ /* prevent compiler from complaining about unused parameters */
+ h = h;
+
+ if (m)
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ DBG (DBG_MSG, "sane_select_fd\n");
+
+ /* prevent compiler from complaining about unused parameters */
+ h = h;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/niash_core.c b/backend/niash_core.c
new file mode 100644
index 0000000..e3ae2b8
--- /dev/null
+++ b/backend/niash_core.c
@@ -0,0 +1,1362 @@
+/*
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+/*
+ Core NIASH chip functions.
+*/
+
+#include <stdio.h> /* fopen, fread, fwrite, fclose etc */
+#include <stdarg.h> /* va_list for vfprintf */
+#include <string.h> /* memcpy, memset */
+#include <unistd.h> /* unlink */
+#include <stdlib.h> /* malloc, free */
+#include <math.h> /* exp, pow */
+
+#include "niash_xfer.h"
+#include "niash_core.h"
+
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+
+#define XFER_BUF_SIZE 0xF000
+
+
+/* HP3400 firmware data */
+static unsigned char abData0000[] = {
+ 0xfe, 0x9f, 0x58, 0x1b, 0x00, 0x03, 0xa4, 0x02, 0x63, 0x02, 0x33, 0x02,
+ 0x0d, 0x02, 0xf0, 0x01,
+ 0xd8, 0x01, 0xc5, 0x01, 0xb5, 0x01, 0xa8, 0x01, 0x9d, 0x01, 0x93, 0x01,
+ 0x8b, 0x01, 0x84, 0x01,
+ 0x7e, 0x01, 0x79, 0x01, 0x74, 0x01, 0x70, 0x01, 0x6d, 0x01, 0x69, 0x01,
+ 0x67, 0x01, 0x64, 0x01,
+ 0x62, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x5d, 0x01, 0x5c, 0x01, 0x5b, 0x01,
+ 0x5a, 0x01, 0x59, 0x01,
+ 0x58, 0x01, 0x57, 0x01, 0x57, 0x01, 0x56, 0x01, 0x56, 0x01, 0x55, 0x01,
+ 0x55, 0x01, 0x54, 0x01,
+ 0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x53, 0x01, 0x53, 0x01, 0x53, 0x01,
+ 0x53, 0x01, 0x52, 0x81
+};
+
+/* 1st word : 0x9ffe = 40958, strip 15th bit: 0x1ffe = 8190
+ 2nd word : 0x1b58 = 7000 -> coincidence ?
+ other words: formula: y = 676 / (2 - exp(0.113 * (1-x)) ), where x = 0 for first entry
+*/
+
+/* more HP3400 firmware data */
+static unsigned char abData0400[] = {
+ 0xa4, 0x82, 0x00, 0x80, 0xa4, 0x82, 0xaa, 0x02, 0xc0, 0x02, 0xe8, 0x02,
+ 0x3e, 0x03, 0xc8, 0x03,
+ 0x58, 0x1b, 0xfe, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+
+
+static void
+_ConvertMotorTable (unsigned char *pabOld, unsigned char *pabNew, int iSize,
+ int iLpi)
+{
+ int iData, i, iBit15;
+
+ for (i = 0; i < (iSize / 2); i++)
+ {
+ iData = pabOld[2 * i + 0] + (pabOld[2 * i + 1] << 8);
+ iBit15 = (iData & 0x8000);
+ iData = (iData & 0x7FFF);
+ if (iData <= 0x400)
+ {
+ iData = iData * iLpi / 300;
+ }
+ if (iBit15 != 0)
+ {
+ iData |= 0x8000;
+ }
+ pabNew[2 * i + 0] = iData & 255;
+ pabNew[2 * i + 1] = (iData >> 8) & 255;
+ }
+}
+
+
+/*************************************************************************
+ _ProbeRegisters
+ ===============
+ Tries to determine certain hardware properties.
+
+ This is done by checking the writeability of some scanner registers.
+ We cannot rely simply on the scanner model to contain a specific
+ chip. The HP3300c for example uses one of at least three slightly
+ different scanner ASICs (NIASH00012, NIASH00013 and NIASH00014).
+
+ OUT pHWParams Hardware parameters, updated fields:
+ fGamma16 TRUE if 16 bit gamma tables can be used
+ fReg07 TRUE if reg07 is writeable
+ iBufferSize Size of scanner's internal buffer
+
+ Returns TRUE if a NIASH chipset was found.
+*************************************************************************/
+static SANE_Bool
+_ProbeRegisters (THWParams * pHWParams)
+{
+ unsigned char bData1, bData2;
+ int iHandle;
+
+ iHandle = pHWParams->iXferHandle;
+
+ DBG (DBG_MSG, "Probing scanner...\n");
+
+ /* check register 0x04 */
+ NiashWriteReg (iHandle, 0x04, 0x55);
+ NiashReadReg (iHandle, 0x04, &bData1);
+ NiashWriteReg (iHandle, 0x04, 0xAA);
+ NiashReadReg (iHandle, 0x04, &bData2);
+ NiashWriteReg (iHandle, 0x04, 0x07);
+ if ((bData1 != 0x55) || (bData2 != 0xAA))
+ {
+ DBG (DBG_ERR, " No NIASH chipset found!\n");
+ return SANE_FALSE;
+ }
+
+ /* check writeability of register 3 bit 1 */
+ NiashReadReg (iHandle, 0x03, &bData1);
+ NiashWriteReg (iHandle, 0x03, bData1 | 0x02);
+ NiashReadReg (iHandle, 0x03, &bData2);
+ NiashWriteReg (iHandle, 0x03, bData1);
+ pHWParams->fGamma16 = ((bData2 & 0x02) != 0);
+ DBG (DBG_MSG, " Gamma table entries are %d bit\n",
+ pHWParams->fGamma16 ? 16 : 8);
+
+ /* check register 0x07 */
+ NiashReadReg (iHandle, 0x07, &bData1);
+ NiashWriteReg (iHandle, 0x07, 0x1C);
+ NiashReadReg (iHandle, 0x07, &bData2);
+ NiashWriteReg (iHandle, 0x07, bData1);
+ pHWParams->fReg07 = (bData2 == 0x1C);
+
+ if (!pHWParams->fGamma16)
+ {
+ /* internal scan buffer size is an educated guess, but seems to correlate
+ well with the size calculated from several windows driver log files
+ size = 128kB - 44088 unsigned chars (space required for gamma/calibration table)
+ */
+ pHWParams->iBufferSize = 86984L;
+ DBG (DBG_MSG, " NIASH version < 00014\n");
+ }
+ else
+ {
+ pHWParams->iBufferSize = 0x60000L;
+ if (!pHWParams->fReg07)
+ {
+ DBG (DBG_MSG, " NIASH version = 00014\n");
+ }
+ else
+ {
+ DBG (DBG_MSG, " NIASH version > 00014\n");
+ }
+ }
+
+ return SANE_TRUE;
+}
+
+
+/* returns 0 on success, < 0 otherwise */
+STATIC int
+NiashOpen (THWParams * pHWParams, const char *pszName)
+{
+ int iXferHandle;
+
+ iXferHandle = NiashXferOpen (pszName, &pHWParams->eModel);
+ if (iXferHandle < 0)
+ {
+ DBG (DBG_ERR, "NiashXferOpen failed for '%s'\n", pszName);
+ return -1;
+ }
+
+ pHWParams->iXferHandle = iXferHandle;
+
+ NiashWakeup (pHWParams->iXferHandle);
+
+ /* default HW params */
+ pHWParams->iSensorSkew = 8;
+ pHWParams->iTopLeftX = 0;
+ pHWParams->iTopLeftY = 3;
+ pHWParams->fReg07 = SANE_FALSE;
+ pHWParams->iSkipLines = 0;
+ pHWParams->iExpTime = 5408;
+ pHWParams->iReversedHead = SANE_TRUE;
+
+ switch (pHWParams->eModel)
+ {
+
+ case eHp3300c:
+ DBG (DBG_MSG, "Setting params for Hp3300\n");
+ pHWParams->iTopLeftX = 4;
+ pHWParams->iTopLeftY = 11;
+ pHWParams->iSkipLines = 14;
+ break;
+
+ case eHp3400c:
+ case eHp4300c:
+ DBG (DBG_MSG, "Setting params for Hp3400c/Hp4300c\n");
+ pHWParams->iTopLeftX = 3;
+ pHWParams->iTopLeftY = 14;
+ pHWParams->fReg07 = SANE_TRUE;
+ break;
+
+ case eAgfaTouch:
+ DBG (DBG_MSG, "Setting params for AgfaTouch\n");
+ pHWParams->iReversedHead = SANE_FALSE; /* head not reversed on Agfa Touch */
+ pHWParams->iTopLeftX = 3;
+ pHWParams->iTopLeftY = 10;
+ pHWParams->iSkipLines = 7;
+ break;
+
+ case eUnknownModel:
+ DBG (DBG_MSG, "Setting params for UnknownModel\n");
+ break;
+
+ default:
+ DBG (DBG_ERR, "ERROR: internal error! (%d)\n", (int) pHWParams->eModel);
+ return -1;
+ }
+
+ /* autodetect some hardware properties */
+ if (!_ProbeRegisters (pHWParams))
+ {
+ DBG (DBG_ERR, "_ProbeRegisters failed!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+STATIC void
+NiashClose (THWParams * pHWPar)
+{
+ NiashXferClose (pHWPar->iXferHandle);
+ pHWPar->iXferHandle = 0;
+}
+
+
+static void
+WriteRegWord (int iHandle, unsigned char bReg, SANE_Word wData)
+{
+ NiashWriteReg (iHandle, bReg, wData & 0xFF);
+ NiashWriteReg (iHandle, bReg + 1, (wData >> 8) & 0xFF);
+}
+
+
+/* calculate a 4096 unsigned char gamma table */
+STATIC void
+CalcGamma (unsigned char *pabTable, double Gamma)
+{
+ int i, iData;
+
+ /* fill gamma table */
+ for (i = 0; i < 4096; i++)
+ {
+ iData = floor (256.0 * pow (((double) i / 4096.0), 1.0 / Gamma));
+ pabTable[i] = iData;
+ }
+}
+
+
+/*
+ Hp3400WriteFw
+ =============
+ Writes data to scanners with a NIASH00019 chipset, e.g.
+ gamma, calibration and motor control data.
+
+ IN pabData pointer to firmware data
+ iLen Size of firmware date (unsigned chars)
+ iAddr Scanner address to write to
+*/
+static void
+Hp3400cWriteFW (int iXferHandle, unsigned char *pabData, int iLen, int iAddr)
+{
+ iAddr--;
+ NiashWriteReg (iXferHandle, 0x21, iAddr & 0xFF);
+ NiashWriteReg (iXferHandle, 0x22, (iAddr >> 8) & 0xFF);
+ NiashWriteReg (iXferHandle, 0x23, (iAddr >> 16) & 0xFF);
+ NiashWriteBulk (iXferHandle, pabData, iLen);
+}
+
+
+/* Writes the gamma and offset/gain tables to the scanner.
+ In case a calibration file exist, it will be used for offset/gain */
+STATIC void
+WriteGammaCalibTable (unsigned char *pabGammaR, unsigned char *pabGammaG,
+ unsigned char *pabGammaB, unsigned char *pabCalibTable,
+ int iGain, int iOffset, THWParams * pHWPar)
+{
+ int i, j, k;
+ static unsigned char abGamma[60000];
+ int iData;
+ int iHandle;
+
+ iHandle = pHWPar->iXferHandle;
+
+ j = 0;
+ /* fill gamma table for red component */
+ /* pad entries with 0 for 16-bit gamma table */
+ for (i = 0; i < 4096; i++)
+ {
+ if (pHWPar->fGamma16)
+ {
+ abGamma[j++] = 0;
+ }
+ abGamma[j++] = pabGammaR[i];
+ }
+ /* fill gamma table for green component */
+ for (i = 0; i < 4096; i++)
+ {
+ if (pHWPar->fGamma16)
+ {
+ abGamma[j++] = 0;
+ }
+ abGamma[j++] = pabGammaG[i];
+ }
+ /* fill gamma table for blue component */
+ for (i = 0; i < 4096; i++)
+ {
+ if (pHWPar->fGamma16)
+ {
+ abGamma[j++] = 0;
+ }
+ abGamma[j++] = pabGammaB[i];
+ }
+
+ if (pabCalibTable == NULL)
+ {
+ iData = (iGain << 6) + iOffset;
+ for (i = 0; i < HW_PIXELS; i++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ abGamma[j++] = (iData) & 255;
+ abGamma[j++] = (iData >> 8) & 255;
+ }
+ }
+ }
+ else
+ {
+ memcpy (&abGamma[j], pabCalibTable, HW_PIXELS * 6);
+ j += HW_PIXELS * 6;
+ }
+
+ NiashWriteReg (iHandle, 0x02, 0x80);
+ NiashWriteReg (iHandle, 0x03, 0x01);
+ NiashWriteReg (iHandle, 0x03, 0x11);
+ NiashWriteReg (iHandle, 0x02, 0x84);
+
+ if (pHWPar->fReg07)
+ {
+ Hp3400cWriteFW (iHandle, abGamma, j, 0x2000);
+ }
+ else
+ {
+ NiashWriteBulk (iHandle, abGamma, j);
+ }
+
+ NiashWriteReg (iHandle, 0x02, 0x80);
+}
+
+
+static void
+WriteAFEReg (int iHandle, int iReg, int iData)
+{
+ NiashWriteReg (iHandle, 0x25, iReg);
+ NiashWriteReg (iHandle, 0x26, iData);
+}
+
+
+/* setup the analog front-end -> coarse calibration */
+static void
+WriteAFE (int iHandle)
+{
+ /* see WM8143 datasheet */
+
+ WriteAFEReg (iHandle, 0x04, 0x00);
+ WriteAFEReg (iHandle, 0x03, 0x12);
+ WriteAFEReg (iHandle, 0x02, 0x04);
+ WriteAFEReg (iHandle, 0x05, 0x10);
+ WriteAFEReg (iHandle, 0x01, 0x03);
+
+ WriteAFEReg (iHandle, 0x20, 0xc0); /*c8 *//* red offset */
+ WriteAFEReg (iHandle, 0x21, 0xc0); /*c8 *//* green offset */
+ WriteAFEReg (iHandle, 0x22, 0xc0); /*d0 *//* blue offset */
+
+ WriteAFEReg (iHandle, 0x28, 0x05); /*5 *//* red gain */
+ WriteAFEReg (iHandle, 0x29, 0x03); /*3 *//* green gain */
+ WriteAFEReg (iHandle, 0x2A, 0x04); /*4 *//* blue gain */
+}
+
+
+/* wait for the carriage to return */
+static void
+WaitReadyBit (int iHandle)
+{
+ unsigned char bData;
+
+ do
+ {
+ NiashReadReg (iHandle, 0x03, &bData);
+ }
+ while ((bData & 8) == 0);
+}
+
+
+/*
+ Initialisation specific for NIASH00014 and lower chips
+*/
+static void
+InitNiash00014 (TScanParams * pParams, THWParams * pHWParams)
+{
+ int iHandle, iLpiCode;
+
+ iHandle = pHWParams->iXferHandle;
+
+ /* exposure time (in units 24/Fcrystal)? */
+ WriteRegWord (iHandle, 0x08, pHWParams->iExpTime - 1);
+
+ /* width in pixels */
+ WriteRegWord (iHandle, 0x12, pParams->iWidth - 1);
+
+ /* top */
+ WriteRegWord (iHandle, 0x17, pParams->iTop);
+ WriteRegWord (iHandle, 0x19, pParams->iTop);
+
+ /* time between stepper motor steps (in units of 24/Fcrystal)? */
+ iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L;
+
+ if (!pHWParams->fGamma16)
+ {
+ /* NIASH 00012 / 00013 init */
+
+ /* LPI specific settings */
+ if (pParams->iLpi < 600)
+ {
+ /* set halfres bit */
+ NiashWriteReg (iHandle, 0x06, 0x01);
+ /* double lpi code because of halfres bit */
+ iLpiCode *= 2;
+ }
+ else
+ {
+ /* clear halfres bit */
+ NiashWriteReg (iHandle, 0x06, 0x00);
+ /* add exptime to make it scan slower */
+ iLpiCode += pHWParams->iExpTime;
+ }
+
+ /* unknown setting */
+ WriteRegWord (iHandle, 0x27, 0x7FD2);
+ WriteRegWord (iHandle, 0x29, 0x6421);
+
+ }
+ else
+ {
+ /* NIASH 00014 init */
+
+ /* halfres bit always cleared */
+ NiashWriteReg (iHandle, 0x06, 0x00);
+
+ /* LPI specific settings */
+ if (pParams->iLpi >= 600)
+ {
+ /* add exptime to make it scan slower */
+ iLpiCode += pHWParams->iExpTime;
+ }
+
+ /* unknown setting */
+ WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */
+ WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */
+ }
+
+ /* LPI code */
+ WriteRegWord (iHandle, 0x0A, iLpiCode - 1);
+
+ /* backtrack reversing speed */
+ NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32);
+}
+
+
+/*
+ Initialisation specific for NIASH00019 chips
+*/
+static void
+InitNiash00019 (TScanParams * pParams, THWParams * pHWParams)
+{
+ int iHandle, iLpiCode;
+ static unsigned char abMotor[512];
+
+
+ iHandle = pHWParams->iXferHandle;
+
+ /* exposure time (in units 24/Fcrystal)? */
+ WriteRegWord (iHandle, 0x08, pHWParams->iExpTime);
+
+ /* width in pixels */
+ WriteRegWord (iHandle, 0x12, pParams->iWidth);
+
+ /* ? */
+ WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */
+ WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */
+
+ /* specific handling of 150 dpi resolution */
+ if (pParams->iLpi == 150)
+ {
+ /* use 300 LPI but skip every other line */
+ pParams->iLpi = 300;
+ NiashWriteReg (iHandle, 0x06, 0x01);
+ }
+ else
+ {
+ NiashWriteReg (iHandle, 0x06, 0x00);
+ }
+
+ /* DPI and position table */
+ NiashWriteReg (iHandle, 0x07, 0x02);
+ _ConvertMotorTable (abData0000, abMotor, sizeof (abData0000),
+ pParams->iLpi);
+ Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0000), 0x000);
+ _ConvertMotorTable (abData0400, abMotor, sizeof (abData0400),
+ pParams->iLpi);
+ Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0400), 0x400);
+
+ /* backtrack reversing speed */
+ iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L;
+ NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32);
+}
+
+
+/*
+ Scanner initialisation common to all NIASH chips
+*/
+static void
+InitNiashCommon (TScanParams * pParams, THWParams * pHWParams)
+{
+ int iWidthHW, iHandle, iMaxLevel;
+
+
+ iHandle = pHWParams->iXferHandle;
+
+ NiashWriteReg (iHandle, 0x02, 0x80);
+ NiashWriteReg (iHandle, 0x03, 0x11);
+ NiashWriteReg (iHandle, 0x01, 0x8B);
+ NiashWriteReg (iHandle, 0x05, 0x01);
+
+ /* dpi */
+ WriteRegWord (iHandle, 0x0C, pParams->iDpi);
+
+ /* calculate width in units of HW resolution */
+ iWidthHW = pParams->iWidth * (HW_DPI / pParams->iDpi);
+
+ /* set left and right limits */
+ if (pHWParams->iReversedHead)
+ {
+ /* head is reversed */
+ /* right */
+ WriteRegWord (iHandle, 0x0E,
+ 3 * (HW_PIXELS - (pParams->iLeft + iWidthHW)));
+
+ /* left */
+ WriteRegWord (iHandle, 0x10, 3 * (HW_PIXELS - pParams->iLeft) - 1);
+ }
+ else
+ {
+ /* head is not reversed */
+ /*left */
+ WriteRegWord (iHandle, 0x0E, 3 * pParams->iLeft);
+
+ /* right */
+ WriteRegWord (iHandle, 0x10, 3 * (pParams->iLeft + iWidthHW) - 1);
+ }
+
+ /* bottom */
+ WriteRegWord (iHandle, 0x1B, pParams->iBottom); /* 0x393C); */
+
+ /* forward jogging speed */
+ NiashWriteReg (iHandle, 0x1D, 0x60);
+
+ /* backtrack reversing speed? */
+ NiashWriteReg (iHandle, 0x2B, 0x15);
+
+ /* backtrack distance */
+ if (pParams->iLpi < 600)
+ {
+ NiashWriteReg (iHandle, 0x1F, 0x30);
+ }
+ else
+ {
+ NiashWriteReg (iHandle, 0x1F, 0x18);
+ }
+
+ /* max buffer level before backtrace */
+ iMaxLevel = MIN (pHWParams->iBufferSize / pParams->iWidth, 250);
+ NiashWriteReg (iHandle, 0x14, iMaxLevel - 1);
+
+ /* lamp PWM, max = 0x1ff? */
+ WriteRegWord (iHandle, 0x2C, 0x01FF);
+
+ /* not needed? */
+ NiashWriteReg (iHandle, 0x15, 0x90); /* 90 */
+ NiashWriteReg (iHandle, 0x16, 0x70); /* 70 */
+
+ WriteAFE (iHandle);
+
+ WaitReadyBit (iHandle);
+
+ NiashWriteReg (iHandle, 0x03, 0x05);
+
+ NiashWriteReg (iHandle, 0x02, pParams->fCalib ? 0x88 : 0xA8);
+}
+
+
+/* write registers */
+STATIC SANE_Bool
+InitScan (TScanParams * pParams, THWParams * pHWParams)
+{
+ int iHeight;
+ int iExpTime;
+ TScanParams Params;
+ int iHandle;
+
+ iHandle = pHWParams->iXferHandle;
+
+ /* check validity of scanparameters */
+ switch (pParams->iDpi)
+ {
+ case 150:
+ case 300:
+ case 600:
+ break;
+ default:
+ DBG (DBG_ERR, "Invalid dpi (%d)\n", pParams->iDpi);
+ return SANE_FALSE;
+ }
+
+ iHeight = (pParams->iBottom - pParams->iTop + 1);
+ if (iHeight <= 0)
+ {
+ DBG (DBG_ERR, "Invalid height (%d)\n", iHeight);
+ return SANE_FALSE;
+ }
+
+ if (pParams->iWidth <= 0)
+ {
+ DBG (DBG_ERR, "Invalid width (%d)\n", pParams->iWidth);
+ return SANE_FALSE;
+ }
+
+ switch (pParams->iLpi)
+ {
+ case 150:
+ case 300:
+ case 600:
+ break;
+ default:
+ DBG (DBG_ERR, "Invalid lpi (%d)\n", pParams->iLpi);
+ return SANE_FALSE;
+ }
+
+ /* exposure time (in units of 24/Fcrystal?), must be divisible by 8 !!! */
+ iExpTime = 5408;
+ if ((iExpTime % 8) != 0)
+ {
+ DBG (DBG_ERR, "Invalid exposure time (%d)\n", iExpTime);
+ return SANE_FALSE;
+ }
+
+ /*
+ *** Done checking scan parameters validity ***
+ */
+
+ /*
+ copy the parameters locally and make pParams point to the local copy
+ */
+ memcpy (&Params, pParams, sizeof (Params));
+ pParams = &Params;
+
+ if (!pHWParams->fReg07)
+ {
+ /* init NIASH00014 and lower */
+ InitNiash00014 (pParams, pHWParams);
+ }
+ else
+ {
+ /* init NIASH00019 */
+ InitNiash00019 (pParams, pHWParams);
+ }
+
+ /* common NIASH init */
+ InitNiashCommon (pParams, pHWParams);
+
+ return SANE_TRUE;
+}
+
+
+/************************************************************************/
+
+static SANE_Bool
+XferBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine,
+ SANE_Bool fReturn)
+{
+ unsigned char bData, bData2;
+ SANE_Bool fJustDone = SANE_FALSE;
+ /* all calculated transfers done ? */
+ if (p->iLinesLeft == 0)
+ return SANE_FALSE;
+
+ /* time for a fresh read? */
+ if (p->iCurLine == 0)
+ {
+ int iLines;
+ iLines = p->iLinesPerXferBuf;
+ /* read only as many lines as needed */
+ if (p->iLinesLeft > 0 && p->iLinesLeft <= iLines)
+ {
+ iLines = p->iLinesLeft;
+ DBG (DBG_MSG, "\n");
+ DBG (DBG_MSG, "last bulk read\n");
+ if (iLines < p->iLinesPerXferBuf)
+ {
+ DBG (DBG_MSG,
+ "reading reduced number of lines: %d instead of %d\n",
+ iLines, p->iLinesPerXferBuf);
+ }
+ fJustDone = SANE_TRUE;
+ }
+ /* reading old buffer level */
+ NiashReadReg (iHandle, 0x20, &bData);
+ NiashReadBulk (iHandle, p->pabXferBuf, iLines * p->iBytesPerLine);
+ /* reding new buffer level */
+ NiashReadReg (iHandle, 0x20, &bData2);
+ if (fJustDone && fReturn)
+ {
+ NiashWriteReg (iHandle, 0x02, 0x80);
+ DBG (DBG_MSG, "returning scanner head\n");
+ }
+ DBG (DBG_MSG,
+ "buffer level = %3d, <reading %5d unsigned chars>, buffer level = %3d\r",
+ (int) bData, iLines * p->iBytesPerLine, (int) bData2);
+ fflush (stdout);
+ }
+ /* copy one line */
+ if (pabLine != NULL)
+ {
+ memcpy (pabLine, &p->pabXferBuf[p->iCurLine * p->iBytesPerLine],
+ p->iBytesPerLine);
+ }
+ /* advance pointer */
+ p->iCurLine = (p->iCurLine + 1) % p->iLinesPerXferBuf;
+
+ /* one transfer line less to the XFerBuffer */
+ if (p->iLinesLeft > 0)
+ --(p->iLinesLeft);
+ return SANE_TRUE;
+}
+
+
+static void
+XferBufferInit (int iHandle, TDataPipe * p)
+{
+ int i;
+
+ p->pabXferBuf = (unsigned char *) malloc (XFER_BUF_SIZE);
+ p->iCurLine = 0;
+
+ /* skip garbage lines */
+ for (i = 0; i < p->iSkipLines; i++)
+ {
+ XferBufferGetLine (iHandle, p, NULL, SANE_FALSE);
+ }
+}
+
+/* static procedure that fills the circular buffer in advance to any
+ circular buffer data retrieval */
+static void
+CircBufferFill (int iHandle, TDataPipe * p, SANE_Bool iReversedHead)
+{
+ int i;
+ for (i = 0; i < p->iLinesPerCircBuf; i++)
+ {
+ if (iReversedHead)
+ {
+ XferBufferGetLine (iHandle, p,
+ &p->pabCircBuf[p->iRedLine * p->iBytesPerLine],
+ SANE_FALSE);
+ }
+ else
+ {
+ XferBufferGetLine (iHandle, p,
+ &p->pabCircBuf[p->iBluLine * p->iBytesPerLine],
+ SANE_FALSE);
+ }
+ /* advance pointers */
+ p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf;
+ p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf;
+ p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf;
+ }
+}
+
+static void
+XferBufferExit (TDataPipe * p)
+{
+ if (p->pabXferBuf != NULL)
+ {
+ free (p->pabXferBuf);
+ p->pabXferBuf = NULL;
+ }
+ else
+ {
+ DBG (DBG_ERR, "XferBufExit: Xfer buffer not initialised!\n");
+ }
+}
+
+
+/* unscrambles a line:
+ - combining the proper R, G and B lines and converting them to interpixel RGB
+ - mirroring left to right
+*/
+static void
+_UnscrambleLine (unsigned char *pabLine,
+ unsigned char *pabRed, unsigned char *pabGrn,
+ unsigned char *pabBlu, int iWidth, SANE_Bool iReversedHead,
+ int iScaleDownDpi, int iBufWeight)
+{
+ /* never change an approved algorithm ...
+ so take Bertriks original source for this special case */
+ if (iScaleDownDpi == 1 && iBufWeight == 0)
+ {
+ int i, j;
+ if (iReversedHead)
+ {
+ /* reversed */
+ for (i = 0; i < iWidth; i++)
+ {
+ j = (iWidth - i) * 3;
+ pabLine[j - 3] = pabRed[i];
+ pabLine[j - 2] = pabGrn[i + iWidth];
+ pabLine[j - 1] = pabBlu[i + iWidth * 2];
+ }
+ }
+ else
+ {
+ /* not reversed */
+ for (i = 0; i < iWidth; i++)
+ {
+ pabLine[3 * i] = pabRed[i];
+ pabLine[3 * i + 1] = pabGrn[i + iWidth];
+ pabLine[3 * i + 2] = pabBlu[i + iWidth * 2];
+ }
+ }
+ }
+ else
+ {
+ int i, j; /* loop variables */
+ int c; /* color buffer accumulator for horizontal avarage */
+
+ /* initialize for incremental color buffer access */
+ int iInc = 1;
+ int iStart = 0;
+
+ /* set for "from the end to the front" of the circular color buffers */
+ if (iReversedHead)
+ {
+ iStart = iWidth - iScaleDownDpi;
+ iInc = -1;
+ }
+
+ /* each pixel is the mean of iScaleDownDpi
+ so set the skip width accordingly */
+ iInc *= iScaleDownDpi;
+
+ for (i = iStart; i >= 0 && i < iWidth; i += iInc)
+ {
+ /* collect the red pixels */
+ for (c = j = 0; j < iScaleDownDpi; ++j)
+ c += pabRed[i + j];
+ *pabLine =
+ (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
+ pabLine++;
+
+ /* collect the green pixels */
+ for (c = j = 0; j < iScaleDownDpi; ++j)
+ c += pabGrn[i + iWidth + j];
+ *pabLine =
+ (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
+ pabLine++;
+
+ /* collect the blue pixels */
+ for (c = j = 0; j < iScaleDownDpi; ++j)
+ c += pabBlu[i + 2 * iWidth + j];
+ *pabLine =
+ (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
+ pabLine++;
+ }
+ }
+
+}
+
+
+/* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage,
+ if fReturn==SANE_TRUE, the head will return automatically on an end of scan */
+STATIC SANE_Bool
+CircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine,
+ SANE_Bool iReversedHead, SANE_Bool fReturn)
+{
+ int iLineCount;
+ for (iLineCount = 0; iLineCount < p->iScaleDownLpi; ++iLineCount)
+ {
+ if (iReversedHead)
+ {
+ if (!XferBufferGetLine (iHandle, p,
+ &p->pabCircBuf[p->iRedLine *
+ p->iBytesPerLine], fReturn))
+ return SANE_FALSE;
+ }
+ else
+ {
+ if (!XferBufferGetLine (iHandle, p,
+ &p->pabCircBuf[p->iBluLine *
+ p->iBytesPerLine], fReturn))
+ return SANE_FALSE;
+ }
+ if (pabLine != NULL)
+ {
+ _UnscrambleLine (pabLine,
+ &p->pabCircBuf[p->iRedLine * p->iBytesPerLine],
+ &p->pabCircBuf[p->iGrnLine * p->iBytesPerLine],
+ &p->pabCircBuf[p->iBluLine * p->iBytesPerLine],
+ p->iWidth * p->iScaleDownDpi, iReversedHead,
+ p->iScaleDownDpi, iLineCount);
+ }
+
+ /* advance pointers */
+ p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf;
+ p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf;
+ p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf;
+ }
+ return SANE_TRUE;
+}
+
+
+/* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage */
+STATIC SANE_Bool
+CircBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine,
+ SANE_Bool iReversedHead)
+{
+ return CircBufferGetLineEx (iHandle, p, pabLine, iReversedHead, SANE_FALSE);
+}
+
+
+/* try to keep the number of transfers the same, but make them all
+ as good as possible the same size to avoid cranking in critical
+ situations
+*/
+static int
+_OptimizeXferSize (int iLines, int iLinesPerXfer)
+{
+ int iXfers;
+ iXfers = (iLines + iLinesPerXfer - 1) / iLinesPerXfer;
+ while (--iLinesPerXfer > 0
+ && (iLines + iLinesPerXfer - 1) / iLinesPerXfer == iXfers);
+ return iLinesPerXfer + 1;
+}
+
+STATIC void
+CircBufferInit (int iHandle, TDataPipe * p,
+ int iWidth, int iHeight,
+ int iMisAlignment, SANE_Bool iReversedHead,
+ int iScaleDownDpi, int iScaleDownLpi)
+{
+
+ /* relevant for internal read and write functions */
+ p->iScaleDownLpi = iScaleDownLpi;
+ p->iScaleDownDpi = iScaleDownDpi;
+ p->iWidth = iWidth;
+ p->iBytesPerLine = iWidth * iScaleDownDpi * BYTES_PER_PIXEL;
+ p->iSaneBytesPerLine = iWidth * BYTES_PER_PIXEL;
+ if (iMisAlignment == 0)
+ {
+ p->iLinesPerCircBuf = 1;
+ }
+ else
+ {
+ p->iLinesPerCircBuf = 3 * iMisAlignment;
+ }
+
+ DBG (DBG_MSG, "_iScaleDown (Dpi,Lpi) = (%d,%d)\n", p->iScaleDownDpi,
+ p->iScaleDownLpi);
+ DBG (DBG_MSG, "_iBytesPerLine = %d\n", p->iBytesPerLine);
+ DBG (DBG_MSG, "_iLinesPerCircBuf = %d\n", p->iLinesPerCircBuf);
+ p->pabCircBuf =
+ (unsigned char *) malloc (p->iBytesPerLine * p->iLinesPerCircBuf);
+ if (p->pabCircBuf == NULL)
+ {
+ DBG (DBG_ERR,
+ "Unable to allocate %d unsigned chars for circular buffer\n",
+ (int) (p->iBytesPerLine * p->iLinesPerCircBuf));
+ return;
+ }
+ DBG (DBG_MSG, "Allocated %d unsigned chars for circular buffer\n",
+ p->iBytesPerLine * p->iLinesPerCircBuf);
+
+ if (iReversedHead)
+ {
+ p->iBluLine = 0;
+ p->iGrnLine = iMisAlignment;
+ p->iRedLine = iMisAlignment * 2;
+ }
+ else
+ {
+ p->iRedLine = 0;
+ p->iGrnLine = iMisAlignment;
+ p->iBluLine = iMisAlignment * 2;
+ }
+
+ /* negative height is an indication for "no Check" */
+ if (iHeight < 0)
+ {
+ p->iLinesLeft = -1;
+ p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine;
+ DBG (DBG_MSG, "using unchecked XFER_BUF_SIZE\n");
+ DBG (DBG_MSG, "_iXFerSize = %d\n",
+ p->iBytesPerLine * p->iLinesPerXferBuf);
+ }
+ else
+ {
+#define SAFETY_LINES 0
+#define MAX_LINES_PER_XFERBUF 800
+ /* estimate of number of unsigned chars to transfer at all via the USB */
+ /* add some lines for securtiy */
+
+ p->iLinesLeft =
+ iHeight + p->iSkipLines + p->iLinesPerCircBuf + SAFETY_LINES;
+ p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine;
+ /* with more than 800 lines the timing is spoiled */
+ if (p->iLinesPerXferBuf > MAX_LINES_PER_XFERBUF)
+ {
+ p->iLinesPerXferBuf = MAX_LINES_PER_XFERBUF;
+ }
+ /* final optimization to keep critical scans smooth */
+ p->iLinesPerXferBuf =
+ _OptimizeXferSize (p->iLinesLeft, p->iLinesPerXferBuf);
+
+ DBG (DBG_MSG, "_iXFerSize = %d for %d transfer(s)\n",
+ (int) p->iLinesPerXferBuf * p->iBytesPerLine,
+ (p->iLinesLeft + p->iLinesPerXferBuf - 1) / p->iLinesPerXferBuf);
+ }
+ DBG (DBG_MSG, "_iLinesPerXferBuf = %d\n", p->iLinesPerXferBuf);
+
+ /* init transfer buffer */
+ XferBufferInit (iHandle, p);
+
+ /* fill circular buffer */
+ CircBufferFill (iHandle, p, iReversedHead);
+}
+
+
+STATIC void
+CircBufferExit (TDataPipe * p)
+{
+ XferBufferExit (p);
+ if (p->pabCircBuf != NULL)
+ {
+ DBG (DBG_MSG, "\n");
+ free (p->pabCircBuf);
+ p->pabCircBuf = NULL;
+ }
+ else
+ {
+ DBG (DBG_ERR, "CircBufferExit: Circular buffer not initialised!\n");
+ }
+}
+
+
+/************************************************************************/
+
+
+
+static int
+_CalcAvg (unsigned char *pabBuf, int n, int iStep)
+{
+ int i, j, x;
+
+ for (i = j = x = 0; i < n; i++)
+ {
+ x += pabBuf[j];
+ j += iStep;
+ }
+ return (x / n);
+}
+
+
+/* converts white line data and black point data into a calibration table */
+static void
+CreateCalibTable (unsigned char *abWhite, unsigned char bBlackR,
+ unsigned char bBlackG, unsigned char bBlackB,
+ int iReversedHead, unsigned char *pabCalibTable)
+{
+ int i, j, iGain, iOffset, iData;
+ unsigned char *pabPixel;
+
+ j = 0;
+ for (i = 0; i < HW_PIXELS; i++)
+ {
+ if (iReversedHead)
+ {
+ pabPixel = &abWhite[(HW_PIXELS - i - 1) * 3];
+ }
+ else
+ {
+ pabPixel = &abWhite[i * 3];
+ }
+ /* red */
+ if (bBlackR > 16)
+ bBlackR = 16;
+ iGain = 65536 / MAX (1, pabPixel[0] - bBlackR);
+ iOffset = bBlackR * 4;
+ if (iOffset > 63)
+ iOffset = 63;
+ iData = (iGain << 6) + iOffset;
+ pabCalibTable[j++] = (iData) & 255;
+ pabCalibTable[j++] = (iData >> 8) & 255;
+ /* green */
+ if (bBlackG > 16)
+ bBlackG = 16;
+ iGain = 65536 / MAX (1, pabPixel[1] - bBlackG);
+ iOffset = bBlackG * 4;
+ if (iOffset > 63)
+ iOffset = 63;
+ iData = (iGain << 6) + iOffset;
+ pabCalibTable[j++] = (iData) & 255;
+ pabCalibTable[j++] = (iData >> 8) & 255;
+ /* blue */
+ if (bBlackB > 16)
+ bBlackB = 16;
+ iGain = 65536 / MAX (1, pabPixel[2] - bBlackB);
+ iOffset = bBlackB * 4;
+ if (iOffset > 63)
+ iOffset = 63;
+ iData = (iGain << 6) + iOffset;
+ pabCalibTable[j++] = (iData) & 255;
+ pabCalibTable[j++] = (iData >> 8) & 255;
+ }
+}
+
+
+/*************************************************************************
+ Lamp control functions
+*************************************************************************/
+STATIC SANE_Bool
+GetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn)
+{
+ unsigned char bData;
+
+ NiashReadReg (pHWParams->iXferHandle, 0x03, &bData);
+ *pfLampIsOn = ((bData & 0x01) != 0);
+ return SANE_TRUE;
+}
+
+
+STATIC SANE_Bool
+SetLamp (THWParams * pHWParams, SANE_Bool fLampOn)
+{
+ unsigned char bData;
+ int iHandle;
+
+ iHandle = pHWParams->iXferHandle;
+
+ NiashReadReg (iHandle, 0x03, &bData);
+ if (fLampOn)
+ {
+ NiashWriteReg (iHandle, 0x03, bData | 0x01);
+ }
+ else
+ {
+ NiashWriteReg (iHandle, 0x03, bData & ~0x01);
+ }
+ return SANE_TRUE;
+}
+
+
+/*************************************************************************
+ Experimental simple calibration, but also returning the white levels
+*************************************************************************/
+STATIC SANE_Bool
+SimpleCalibExt (THWParams * pHWPar, unsigned char *pabCalibTable,
+ unsigned char *pabCalWhite)
+{
+ unsigned char bMinR, bMinG, bMinB;
+ TDataPipe DataPipe;
+ TScanParams Params;
+ unsigned char abGamma[4096];
+ int i, j;
+ static unsigned char abBuf[HW_PIXELS * 3 * 71]; /* Carefull : see startWhite and endWhite below */
+ static unsigned char abLine[HW_PIXELS * 3];
+ static unsigned char abWhite[HW_PIXELS * 3];
+ unsigned char *pabWhite;
+ int iWhiteR, iWhiteG, iWhiteB;
+ int iHandle;
+ SANE_Bool iReversedHead;
+ int startWhiteY, endWhiteY;
+ int startBlackY, endBlackY;
+ int startBlackX, endBlackX;
+
+ iHandle = pHWPar->iXferHandle;
+ iReversedHead = pHWPar->iReversedHead;
+
+ DataPipe.iSkipLines = pHWPar->iSkipLines;
+
+ Params.iDpi = HW_DPI;
+ Params.iLpi = HW_DPI;
+ if (iReversedHead) /* hp scanners */
+ Params.iTop = 60;
+ else /* agfa scanners */
+ Params.iTop = 30;
+ Params.iBottom = HP3300C_BOTTOM;
+ Params.iLeft = 0;
+ Params.iWidth = HW_PIXELS;
+ Params.iHeight = 54;
+ Params.fCalib = SANE_TRUE;
+
+ /* write gamma table with neutral gain / offset */
+ CalcGamma (abGamma, 1.0);
+ WriteGammaCalibTable (abGamma, abGamma, abGamma, NULL, 256, 0, pHWPar);
+
+ if (!InitScan (&Params, pHWPar))
+ {
+ if (pabCalWhite)
+ pabCalWhite[0] = pabCalWhite[1] = pabCalWhite[2] = 0;
+ return SANE_FALSE;
+ }
+
+ /* Definition of white and black areas */
+ if (iReversedHead)
+ { /* hp scanners */
+ startWhiteY = 0;
+ endWhiteY = 15;
+ startBlackY = 16;
+ endBlackY = 135;
+ startBlackX = 0;
+ endBlackX = HW_PIXELS;
+ }
+ else
+ { /* agfa scanners */
+ startWhiteY = 0;
+ endWhiteY = 70;
+ startBlackY = 86;
+ endBlackY = 135;
+ startBlackX = 1666;
+ endBlackX = 3374;
+ }
+
+ CircBufferInit (iHandle, &DataPipe, HW_PIXELS, -1, Params.iLpi / 150,
+ iReversedHead, 1, 1);
+ /* white level */
+ /* skip some lines */
+ for (i = 0; i < startWhiteY; i++)
+ {
+ CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
+ }
+ /* Get white lines */
+ for (i = 0; i < endWhiteY - startWhiteY + 1; i++)
+ {
+ CircBufferGetLine (iHandle, &DataPipe, &abBuf[i * HW_PIXELS * 3],
+ iReversedHead);
+ }
+ /* black level */
+ bMinR = 255;
+ bMinG = 255;
+ bMinB = 255;
+ /* Skip some lines */
+ for (i = 0; i < startBlackY; i++)
+ {
+ CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
+ }
+ for (i = 0; i < endBlackY - startBlackY + 1; i++)
+ {
+ CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
+ for (j = 0; j < endBlackX; j++)
+ {
+ bMinR = MIN (abLine[j * 3 + 0], bMinR);
+ bMinG = MIN (abLine[j * 3 + 1], bMinG);
+ bMinB = MIN (abLine[j * 3 + 2], bMinB);
+ }
+ }
+ CircBufferExit (&DataPipe);
+ FinishScan (pHWPar);
+
+ /* calc average white level */
+ pabWhite = abBuf;
+ for (i = 0; i < HW_PIXELS; i++)
+ {
+ abWhite[i * 3 + 0] =
+ _CalcAvg (&pabWhite[i * 3 + 0], endWhiteY - startWhiteY + 1,
+ HW_PIXELS * 3);
+ abWhite[i * 3 + 1] =
+ _CalcAvg (&pabWhite[i * 3 + 1], endWhiteY - startWhiteY + 1,
+ HW_PIXELS * 3);
+ abWhite[i * 3 + 2] =
+ _CalcAvg (&pabWhite[i * 3 + 2], endWhiteY - startWhiteY + 1,
+ HW_PIXELS * 3);
+ }
+ iWhiteR = _CalcAvg (&abWhite[0], HW_PIXELS, 3);
+ iWhiteG = _CalcAvg (&abWhite[1], HW_PIXELS, 3);
+ iWhiteB = _CalcAvg (&abWhite[2], HW_PIXELS, 3);
+
+ DBG (DBG_MSG, "Black level (%d,%d,%d), White level (%d,%d,%d)\n",
+ (int) bMinR, (int) bMinG, (int) bMinB, iWhiteR, iWhiteG, iWhiteB);
+
+ /* convert the white line and black point into a calibration table */
+ CreateCalibTable (abWhite, bMinR, bMinG, bMinB, iReversedHead,
+ pabCalibTable);
+ /* assign the White Levels */
+ if (pabCalWhite)
+ {
+ pabCalWhite[0] = iWhiteR;
+ pabCalWhite[1] = iWhiteG;
+ pabCalWhite[2] = iWhiteB;
+ }
+ return SANE_TRUE;
+}
+
+
+/*************************************************************************
+ FinishScan
+ ==========
+ Finishes the scan. Makes the scanner head move back to the home position.
+
+*************************************************************************/
+STATIC void
+FinishScan (THWParams * pHWParams)
+{
+ NiashWriteReg (pHWParams->iXferHandle, 0x02, 0x80);
+}
diff --git a/backend/niash_core.h b/backend/niash_core.h
new file mode 100644
index 0000000..9bd6b91
--- /dev/null
+++ b/backend/niash_core.h
@@ -0,0 +1,136 @@
+/*
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+/*
+ Core NIASH chip functions.
+*/
+
+
+#ifndef _NIASH_CORE_H_
+#define _NIASH_CORE_H_
+
+#include <unistd.h>
+
+#include "niash_xfer.h" /* for EScannerModel */
+
+#define HP3300C_RIGHT 330
+#define HP3300C_TOP 452
+#define HP3300C_BOTTOM (HP3300C_TOP + 14200UL)
+
+#define HW_PIXELS 5300 /* number of pixels supported by hardware */
+#define HW_DPI 600 /* horizontal resolution of hardware */
+#define HW_LPI 1200 /* vertical resolution of hardware */
+
+#define BYTES_PER_PIXEL 3
+
+typedef struct
+{
+ int iXferHandle; /* handle used for data transfer to HW */
+ int iTopLeftX; /* in mm */
+ int iTopLeftY; /* in mm */
+ int iSensorSkew; /* in units of 1/1200 inch */
+ int iSkipLines; /* lines of garbage to skip */
+ SANE_Bool fReg07; /* NIASH00019 */
+ SANE_Bool fGamma16; /* if TRUE, gamma entries are 16 bit */
+ int iExpTime;
+ SANE_Bool iReversedHead; /* Head is reversed */
+ int iBufferSize; /* Size of internal scan buffer */
+ EScannerModel eModel;
+} THWParams;
+
+
+typedef struct
+{
+ int iDpi; /* horizontal resolution */
+ int iLpi; /* vertical resolution */
+ int iTop; /* in HW coordinates */
+ int iLeft; /* in HW coordinates */
+ int iWidth; /* pixels */
+ int iHeight; /* lines */
+ int iBottom;
+
+ int fCalib; /* if TRUE, disable backtracking? */
+} TScanParams;
+
+
+typedef struct
+{
+ unsigned char *pabXferBuf; /* transfer buffer */
+ int iCurLine; /* current line in the transfer buffer */
+ int iBytesPerLine; /* unsigned chars in one scan line */
+ int iLinesPerXferBuf; /* number of lines held in the transfer buffer */
+ int iLinesLeft; /* transfer (down) counter for pabXFerBuf */
+ int iSaneBytesPerLine; /* how many unsigned chars to be read by SANE per line */
+ int iScaleDownDpi; /* factors used to emulate lower resolutions */
+ int iScaleDownLpi; /* than those offered by hardware */
+ int iSkipLines; /* line to skip at the start of scan */
+ int iWidth; /* number of pixels expected by SANE */
+ unsigned char *pabCircBuf; /* circular buffer */
+ int iLinesPerCircBuf; /* lines held in the circular buffer */
+ int iRedLine, iGrnLine, /* start indices for the color information */
+ iBluLine; /* in the circular buffer */
+ unsigned char *pabLineBuf; /* buffer used to pass data to SANE */
+} TDataPipe;
+
+
+STATIC int NiashOpen (THWParams * pHWParams, const char *pszName);
+STATIC void NiashClose (THWParams * pHWParams);
+
+/* more sof. method that also returns the values of the white (RGB) value */
+STATIC SANE_Bool SimpleCalibExt (THWParams * pHWPar,
+ unsigned char *pabCalibTable,
+ unsigned char *pabCalWhite);
+
+STATIC SANE_Bool GetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn);
+STATIC SANE_Bool SetLamp (THWParams * pHWParams, SANE_Bool fLampOn);
+
+STATIC SANE_Bool InitScan (TScanParams * pParams, THWParams * pHWParams);
+STATIC void FinishScan (THWParams * pHWParams);
+
+STATIC void CalcGamma (unsigned char *pabTable, double Gamma);
+STATIC void WriteGammaCalibTable (unsigned char *pabGammaR,
+ unsigned char *pabGammaG,
+ unsigned char *pabGammaB,
+ unsigned char *pabCalibTable, int iGain,
+ int iOffset, THWParams * pHWPar);
+
+/* set -1 for iHeight to disable all checks on buffer transfers */
+/* iWidth is in pixels of SANE */
+/* iHeight is lines in scanner resolution */
+STATIC void CircBufferInit (int iHandle, TDataPipe * p,
+ int iWidth, int iHeight,
+ int iMisAlignment, SANE_Bool iReversedHead,
+ int iScaleDownDpi, int iScaleDownLpi);
+
+/* returns false, when trying to read after end of buffer */
+STATIC SANE_Bool CircBufferGetLine (int iHandle, TDataPipe * p,
+ unsigned char *pabLine,
+ SANE_Bool iReversedHead);
+
+/* returns false, when trying to read after end of buffer
+ if fReturn==SANE_TRUE, the head will return automatically on an end of scan */
+
+STATIC SANE_Bool
+CircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine,
+ SANE_Bool iReversedHead, SANE_Bool fReturn);
+
+STATIC void CircBufferExit (TDataPipe * p);
+
+#endif /* _NIASH_CORE_H_ */
diff --git a/backend/niash_xfer.c b/backend/niash_xfer.c
new file mode 100644
index 0000000..a3d0ea2
--- /dev/null
+++ b/backend/niash_xfer.c
@@ -0,0 +1,330 @@
+/*
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+/*
+ Provides a simple interface to read and write data from the scanner,
+ without any knowledge whether it's a parallel or USB scanner
+*/
+
+#include <stdio.h> /* printf */
+#include <errno.h> /* better error reports */
+#include <string.h> /* better error reports */
+
+#include "niash_xfer.h"
+
+#include "../include/sane/sanei_usb.h"
+
+/* list of supported models */
+STATIC TScannerModel ScannerModels[] = {
+ {"Hewlett-Packard", "ScanJet 3300C", 0x3F0, 0x205, eHp3300c}
+ ,
+ {"Hewlett-Packard", "ScanJet 3400C", 0x3F0, 0x405, eHp3400c}
+ ,
+ {"Hewlett-Packard", "ScanJet 4300C", 0x3F0, 0x305, eHp4300c}
+ ,
+ {"Silitek Corp.", "HP ScanJet 4300c", 0x47b, 0x1002, eHp3400c}
+ ,
+ {"Agfa", "Snapscan Touch", 0x6BD, 0x100, eAgfaTouch}
+ ,
+ {"Trust", "Office Scanner USB 19200", 0x47b, 0x1000, eAgfaTouch}
+ ,
+/* last entry all zeros */
+ {0, 0, 0, 0, 0}
+};
+
+static TFnReportDevice *_pfnReportDevice;
+static TScannerModel *_pModel;
+
+/*
+ MatchUsbDevice
+ ==============
+ Matches a given USB vendor and product id against a list of
+ supported scanners.
+
+ IN iVendor USB vendor ID
+ iProduct USB product ID
+ OUT *ppModel Pointer to TScannerModel structure
+
+ Returns TRUE if a matching USB scanner was found
+*/
+STATIC SANE_Bool
+MatchUsbDevice (int iVendor, int iProduct, TScannerModel ** ppModel)
+{
+ TScannerModel *pModels = ScannerModels;
+
+ DBG (DBG_MSG, "Matching USB device 0x%04X-0x%04X ... ", iVendor, iProduct);
+ while (pModels->pszName != NULL)
+ {
+ if ((pModels->iVendor == iVendor) && (pModels->iProduct == iProduct))
+ {
+ DBG (DBG_MSG, "found %s %s\n", pModels->pszVendor,
+ pModels->pszName);
+ *ppModel = pModels;
+ return SANE_TRUE;
+ }
+ /* next model to match */
+ pModels++;
+ }
+ DBG (DBG_MSG, "nothing found\n");
+ return SANE_FALSE;
+}
+
+/************************************************************************
+ Public functions for the SANE compilation
+************************************************************************/
+
+
+/* callback for sanei_usb_attach_matching_devices */
+static SANE_Status
+_AttachUsb (SANE_String_Const devname)
+{
+ DBG (DBG_MSG, "_AttachUsb: found %s\n", devname);
+
+ _pfnReportDevice (_pModel, (const char *) devname);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*
+ NiashXferInit
+ ===============
+ Initialises all registered data transfer modules, which causes
+ them to report any devices found through the pfnReport callback.
+
+ IN pfnReport Function to call to report a transfer device
+*/
+static void
+NiashXferInit (TFnReportDevice * pfnReport)
+{
+ TScannerModel *pModels = ScannerModels;
+
+ sanei_usb_init ();
+ _pfnReportDevice = pfnReport;
+
+ /* loop over all scanner models */
+ while (pModels->pszName != NULL)
+ {
+ DBG (DBG_MSG, "Looking for %s...\n", pModels->pszName);
+ _pModel = pModels;
+ if (sanei_usb_find_devices ((SANE_Int) pModels->iVendor,
+ (SANE_Int) pModels->iProduct,
+ _AttachUsb) != SANE_STATUS_GOOD)
+ {
+
+ DBG (DBG_ERR, "Error invoking sanei_usb_find_devices");
+ break;
+ }
+ pModels++;
+ }
+}
+
+
+static int
+NiashXferOpen (const char *pszName, EScannerModel * peModel)
+{
+ SANE_Status status;
+ SANE_Word vendor, product;
+ int fd;
+ TScannerModel *pModel = 0;
+
+ DBG (DBG_MSG, "Trying to open %s...\n", pszName);
+
+ status = sanei_usb_open (pszName, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return -1;
+ }
+
+ status = sanei_usb_get_vendor_product (fd, &vendor, &product);
+ if (status == SANE_STATUS_GOOD)
+ {
+ MatchUsbDevice (vendor, product, &pModel);
+ *peModel = pModel->eModel;
+ }
+
+ DBG (DBG_MSG, "handle = %d\n", (int) fd);
+ return fd;
+}
+
+
+static void
+NiashXferClose (int iHandle)
+{
+ /* close usb device */
+ if (iHandle != -1)
+ {
+ sanei_usb_close (iHandle);
+ }
+}
+
+
+static void
+parusb_write_reg (int fd, unsigned char bReg, unsigned char bValue)
+{
+ sanei_usb_control_msg (fd,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x0C, bReg, 0, 1, &bValue);
+}
+
+
+static void
+parusb_read_reg (int fd, unsigned char bReg, unsigned char *pbValue)
+{
+ sanei_usb_control_msg (fd,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x0C, bReg, 0, 1, pbValue);
+}
+
+
+static void
+NiashWriteReg (int iHandle, unsigned char bReg, unsigned char bData)
+{
+ if (iHandle < 0)
+ {
+ DBG (DBG_MSG, "Invalid handle %d\n", iHandle);
+ return;
+ }
+
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, EPP_ADDR, bReg);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, EPP_DATA_WRITE, bData);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+}
+
+
+static void
+NiashReadReg (int iHandle, unsigned char bReg, unsigned char *pbData)
+{
+ if (iHandle < 0)
+ {
+ return;
+ }
+
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, EPP_ADDR, bReg);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x34);
+ parusb_read_reg (iHandle, EPP_DATA_READ, pbData);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+}
+
+
+static void
+NiashWriteBulk (int iHandle, unsigned char *pabBuf, int iSize)
+{
+ /* byte abSetup[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ HP3400 probably needs 0x01, 0x01 */
+ SANE_Byte abSetup[8] = { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ size_t size;
+
+ if (iHandle < 0)
+ {
+ return;
+ }
+
+ /* select scanner register 0x24 */
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, EPP_ADDR, 0x24);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+
+ /* tell scanner that a bulk transfer follows */
+ abSetup[4] = (iSize) & 0xFF;
+ abSetup[5] = (iSize >> 8) & 0xFF;
+ sanei_usb_control_msg (iHandle,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x04, USB_SETUP, 0, 8, abSetup);
+
+ /* do the bulk write */
+ size = iSize;
+ if (sanei_usb_write_bulk (iHandle, pabBuf, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "ERROR: Bulk write failed\n");
+ }
+}
+
+
+static void
+NiashReadBulk (int iHandle, unsigned char *pabBuf, int iSize)
+{
+ SANE_Byte abSetup[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ size_t size;
+
+ if (iHandle < 0)
+ {
+ return;
+ }
+
+ /* select scanner register 0x24 */
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, EPP_ADDR, 0x24);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+
+ /* tell scanner that a bulk transfer follows */
+ abSetup[4] = (iSize) & 0xFF;
+ abSetup[5] = (iSize >> 8) & 0xFF;
+ sanei_usb_control_msg (iHandle,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x04, USB_SETUP, 0, 8, abSetup);
+
+ /* do the bulk read */
+ size = iSize;
+ if (sanei_usb_read_bulk (iHandle, pabBuf, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_ERR, "ERROR: Bulk read failed\n");
+ }
+}
+
+
+static void
+NiashWakeup (int iHandle)
+{
+ unsigned char abMagic[] = { 0xA0, 0xA8, 0x50, 0x58, 0x90, 0x98, 0xC0, 0xC8,
+ 0x90, 0x98, 0xE0, 0xE8
+ };
+ int i;
+
+ if (iHandle < 0)
+ {
+ return;
+ }
+
+ /* write magic startup sequence */
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ for (i = 0; i < (int) sizeof (abMagic); i++)
+ {
+ parusb_write_reg (iHandle, SPP_DATA, abMagic[i]);
+ }
+
+ /* write 0x04 to scanner register 0x00 the hard way */
+ parusb_write_reg (iHandle, SPP_DATA, 0x00);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x15);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x1D);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x15);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+
+ parusb_write_reg (iHandle, SPP_DATA, 0x04);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x15);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x17);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x15);
+ parusb_write_reg (iHandle, SPP_CONTROL, 0x14);
+}
diff --git a/backend/niash_xfer.h b/backend/niash_xfer.h
new file mode 100644
index 0000000..b325240
--- /dev/null
+++ b/backend/niash_xfer.h
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+/*
+ Provides a simple interface to read and write data from the scanner,
+ without any knowledge whether it's a parallel or USB scanner
+*/
+
+#ifndef _NIASH_XFER_H_
+#define _NIASH_XFER_H_
+
+#include <stdio.h> /* for FILE * */
+
+/* register codes for the USB - IEEE1284 bridge */
+#define USB_SETUP 0x82
+#define EPP_ADDR 0x83
+#define EPP_DATA_READ 0x84
+#define EPP_DATA_WRITE 0x85
+#define SPP_STATUS 0x86
+#define SPP_CONTROL 0x87
+#define SPP_DATA 0x88
+
+
+typedef enum
+{
+ eUnknownModel = 0,
+ eHp3300c,
+ eHp3400c,
+ eHp4300c,
+ eAgfaTouch
+} EScannerModel;
+
+
+typedef struct
+{
+ char *pszVendor;
+ char *pszName;
+ int iVendor;
+ int iProduct;
+ EScannerModel eModel;
+} TScannerModel;
+
+
+typedef int (TFnReportDevice) (TScannerModel * pModel,
+ const char *pszDeviceName);
+
+
+/* Creates our own DBG definitions, externs are define in main.c*/
+#ifndef WITH_NIASH
+#define DBG fprintf
+extern FILE *DBG_MSG;
+extern FILE *DBG_ERR;
+extern FILE *BG_ASSERT;
+#endif /* NO WITH_NIASH */
+
+/* we do not make data prototypes */
+#ifndef WITH_NIASH
+/* list of supported models, the actual list is in niash_xfer.c */
+extern TScannerModel ScannerModels[];
+#endif /* NO WITH_NIASH */
+
+STATIC void NiashXferInit (TFnReportDevice * pfnReport);
+STATIC int NiashXferOpen (const char *pszName, EScannerModel * peModel);
+STATIC void NiashXferClose (int iXferHandle);
+
+STATIC void NiashWriteReg (int iXferHandle, unsigned char bReg,
+ unsigned char bData);
+STATIC void NiashReadReg (int iXferHandle, unsigned char bReg,
+ unsigned char *pbData);
+STATIC void NiashWriteBulk (int iXferHandle, unsigned char *pabBuf,
+ int iSize);
+STATIC void NiashReadBulk (int iXferHandle, unsigned char *pabBuf, int iSize);
+STATIC void NiashWakeup (int iXferHandle);
+
+STATIC SANE_Bool MatchUsbDevice (int iVendor, int iProduct,
+ TScannerModel ** ppeModel);
+
+#endif /* _NIASH_XFER_H_ */
diff --git a/backend/p5.c b/backend/p5.c
new file mode 100644
index 0000000..30ab41e
--- /dev/null
+++ b/backend/p5.c
@@ -0,0 +1,2048 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2009-12 Stéphane Voltz <stef.dev@free.fr>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+*/
+/* --------------------------------------------------------------------------
+
+*/
+
+/* ------------------------------------------------------------------------- */
+/*! \mainpage Primax PagePartner Parallel Port scanner Index Page
+ *
+ * \section intro_sec Introduction
+ *
+ * This backend provides support for the Prima PagePartner sheet fed parallel
+ * port scanner.
+ *
+ * \section sane_api SANE API
+ *
+ * \subsection sane_flow sane flow
+ SANE FLOW
+ - sane_init() : initialize backend, attach scanners.
+ - sane_get_devices() : query list of scanner devices, backend must
+ probe for new devices.
+ - sane_open() : open a particular scanner device, adding a handle
+ to the opened device
+ - sane_set_io_mode() : set blocking mode
+ - sane_get_select_fd() : get scanner fd
+ - sane_get_option_descriptor() : get option information
+ - sane_control_option() : change option values
+ - sane_start() : start image acquisition
+ - sane_get_parameters() : returns actual scan parameters for the ongoing scan
+ - sane_read() : read image data
+ - sane_cancel() : cancel operation, end scan
+ - sane_close() : close opened scanner device, freeing scanner handle
+ - sane_exit() : terminate use of backend, freeing all resources for attached
+ devices when last frontend quits
+ */
+
+/**
+ * the build number allow to know which version of the backend is running.
+ */
+#define BUILD 2301
+
+#include "p5.h"
+
+/**
+ * Import directly the low level part needed to
+ * operate scanner. The alternative is to prefix all public functions
+ * with sanei_p5_ ,and have all the functions prototyped in
+ * p5_device.h .
+ */
+#include "p5_device.c"
+
+/**
+ * number of time the backend has been loaded by sane_init.
+ */
+static int init_count = 0;
+
+/**
+ * NULL terminated list of opened frontend sessions. Sessions are
+ * inserted here on sane_open() and removed on sane_close().
+ */
+static P5_Session *sessions = NULL;
+
+/**
+ * NULL terminated list of detected physical devices.
+ * The same device may be opened several time by different sessions.
+ * Entry are inserted here by the attach() function.
+ * */
+static P5_Device *devices = NULL;
+
+/**
+ * NULL terminated list of devices needed by sane_get_devices(), since
+ * the result returned must stay consistent until next call.
+ */
+static const SANE_Device **devlist = 0;
+
+/**
+ * list of possible color modes
+ */
+static SANE_String_Const mode_list[] = {
+ SANE_I18N (COLOR_MODE),
+ SANE_I18N (GRAY_MODE),
+ /* SANE_I18N (LINEART_MODE), not supported yet */
+ 0
+};
+
+static SANE_Range x_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (216.0), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range y_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (299.0), /* maximum */
+ SANE_FIX (0.0) /* no quantization */
+};
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* no quantization */
+};
+
+static const SANE_Range threshold_percentage_range = {
+ SANE_FIX (0), /* minimum */
+ SANE_FIX (100), /* maximum */
+ SANE_FIX (1) /* quantization */
+};
+
+/**
+ * finds the maximum string length in a string array.
+ */
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+/**> placeholders for decoded configuration values */
+static P5_Config p5cfg;
+
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * SANE Interface
+ */
+
+
+/**
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Status status;
+
+ authorize = authorize; /* get rid of compiler warning */
+
+ init_count++;
+
+ /* init backend debug */
+ DBG_INIT ();
+ DBG (DBG_info, "SANE P5 backend version %d.%d-%d\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ DBG (DBG_proc, "sane_init: start\n");
+ DBG (DBG_trace, "sane_init: init_count=%d\n", init_count);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ /* cold-plugging case : probe for already plugged devices */
+ status = probe_p5_devices ();
+
+ DBG (DBG_proc, "sane_init: exit\n");
+ return status;
+}
+
+
+/**
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ * @param device_list pointer where to store the device list
+ * @param local_only SANE_TRUE if only local devices are required.
+ * @return SANE_STATUS_GOOD when successfull
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ int dev_num, devnr;
+ struct P5_Device *device;
+ SANE_Device *sane_device;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ /* free existing devlist first */
+ if (devlist)
+ {
+ for (i = 0; devlist[i] != NULL; i++)
+ free ((void *)devlist[i]);
+ free (devlist);
+ devlist = NULL;
+ }
+
+ /**
+ * Since sane_get_devices() may be called repeatedly to detect new devices,
+ * the device detection must be run at each call. We are handling
+ * hot-plugging : we probe for devices plugged since sane_init() was called.
+ */
+ probe_p5_devices ();
+
+ /* if no devices detected, just return an empty list */
+ if (devices == NULL)
+ {
+ devlist = malloc (sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ devlist[0] = NULL;
+ *device_list = devlist;
+ DBG (DBG_proc, "sane_get_devices: exit with no device\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* count physical devices */
+ devnr = 1;
+ device = devices;
+ while (device->next)
+ {
+ devnr++;
+ device = device->next;
+ }
+
+ /* allocate room for the list, plus 1 for the NULL terminator */
+ devlist = malloc ((devnr + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ *device_list = devlist;
+
+ dev_num = 0;
+ device = devices;
+
+ /* we build a list of SANE_Device from the list of attached devices */
+ for (i = 0; i < devnr; i++)
+ {
+ /* add device according to local only flag */
+ if ((local_only == SANE_TRUE && device->local == SANE_TRUE)
+ || local_only == SANE_FALSE)
+ {
+ /* allocate memory to add the device */
+ sane_device = malloc (sizeof (*sane_device));
+ if (!sane_device)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* copy data */
+ sane_device->name = device->name;
+ sane_device->vendor = device->model->vendor;
+ sane_device->model = device->model->product;
+ sane_device->type = device->model->type;
+ devlist[dev_num] = sane_device;
+
+ /* increment device counter */
+ dev_num++;
+ }
+
+ /* go to next detected device */
+ device = device->next;
+ }
+ devlist[dev_num] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Called to establish connection with the session. This function will
+ * also establish meaningful defaults and initialize the options.
+ *
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device). Another special case is to only give
+ * the name of the backend as the device name, in this case the first
+ * available device will also be used.
+ * @param name name of the device to open
+ * @param handle opaque pointer where to store the pointer of
+ * the opened P5_Session
+ * @return SANE_STATUS_GOOD on success
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct P5_Session *session = NULL;
+ struct P5_Device *device = NULL;
+
+ DBG (DBG_proc, "sane_open: start (devicename=%s)\n", name);
+
+ /* check there is at least a device */
+ if (devices == NULL)
+ {
+ DBG (DBG_proc, "sane_open: exit, no device to open!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (name[0] == 0 || strncmp (name, "p5", strlen ("p5")) == 0)
+ {
+ DBG (DBG_info,
+ "sane_open: no specific device requested, using default\n");
+ if (devices)
+ {
+ device = devices;
+ DBG (DBG_info, "sane_open: device %s used as default device\n",
+ device->name);
+ }
+ }
+ else
+ {
+ DBG (DBG_info, "sane_open: device %s requested\n", name);
+ /* walk the device list until we find a matching name */
+ device = devices;
+ while (device && strcmp (device->name, name) != 0)
+ {
+ DBG (DBG_trace, "sane_open: device %s doesn't match\n",
+ device->name);
+ device = device->next;
+ }
+ }
+
+ /* check wether we have found a match or reach the end of the device list */
+ if (!device)
+ {
+ DBG (DBG_info, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now we have a device, duplicate it and return it in handle */
+ DBG (DBG_info, "sane_open: device %s found\n", name);
+
+ /* device initialization */
+ if (device->initialized == SANE_FALSE)
+ {
+ /**
+ * call to hardware initialization function here.
+ */
+ device->fd = open_pp (device->name);
+ if (device->fd < 0)
+ {
+ DBG (DBG_error, "sane_open: failed to open '%s' device!\n",
+ device->name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now try to connect to scanner */
+ if (connect (device->fd) != SANE_TRUE)
+ {
+ DBG (DBG_error, "sane_open: failed to connect!\n");
+ close_pp (device->fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* load calibration data */
+ restore_calibration (device);
+
+ /* device link is OK now */
+ device->initialized = SANE_TRUE;
+ }
+ device->buffer = NULL;
+ device->gain = NULL;
+ device->offset = NULL;
+
+ /* prepare handle to return */
+ session = (P5_Session *) malloc (sizeof (P5_Session));
+ if (session == NULL)
+ {
+ DBG (DBG_proc, "sane_open: exit OOM\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* initalize session */
+ session->dev = device;
+ session->scanning = SANE_FALSE;
+ session->non_blocking = SANE_FALSE;
+
+ /* initialize SANE options for this session */
+ init_options (session);
+
+ /* add the handle to the linked list of sessions */
+ session->next = sessions;
+ sessions = session;
+
+ /* store result */
+ *handle = session;
+
+ /* exit success */
+ DBG (DBG_proc, "sane_open: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Set non blocking mode. In this mode, read return immediatly when
+ * no data is available whithin sane_read(), instead of polling the scanner.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ P5_Session *session = (P5_Session *) handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: start\n");
+ if (session->scanning != SANE_TRUE)
+ {
+ DBG (DBG_error, "sane_set_io_mode: called out of a scan\n");
+ return SANE_STATUS_INVAL;
+ }
+ session->non_blocking = non_blocking;
+ DBG (DBG_info, "sane_set_io_mode: I/O mode set to %sblocking.\n",
+ non_blocking ? "non " : " ");
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * An advanced method we don't support but have to define. At SANE API
+ * level this function is meant to provide a file descriptor on which the
+ * frontend can do select()/poll() to wait for data.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fdp)
+{
+ /* make compiler happy ... */
+ handle = handle;
+ fdp = fdp;
+
+ DBG (DBG_proc, "sane_get_select_fd: start\n");
+ DBG (DBG_warn, "sane_get_select_fd: unsupported ...\n");
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/**
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct P5_Session *session = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: start\n");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ DBG (DBG_info, "sane_get_option_descriptor: \"%s\"\n",
+ session->options[option].descriptor.name);
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+ return &(session->options[option].descriptor);
+}
+
+/**
+ * sets automatic value for an option , called by sane_control_option after
+ * all checks have been done */
+static SANE_Status
+set_automatic_value (P5_Session * s, int option, SANE_Int * myinfo)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int i, min;
+ SANE_Word *dpi_list;
+
+ switch (option)
+ {
+ case OPT_TL_X:
+ s->options[OPT_TL_X].value.w = x_range.min;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_TL_Y:
+ s->options[OPT_TL_Y].value.w = y_range.min;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BR_X:
+ s->options[OPT_BR_X].value.w = x_range.max;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BR_Y:
+ s->options[OPT_BR_Y].value.w = y_range.max;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_RESOLUTION:
+ /* we set up to the lowest available dpi value */
+ dpi_list =
+ (SANE_Word *) s->options[OPT_RESOLUTION].descriptor.constraint.
+ word_list;
+ min = 65536;
+ for (i = 1; i < dpi_list[0]; i++)
+ {
+ if (dpi_list[i] < min)
+ min = dpi_list[i];
+ }
+ s->options[OPT_RESOLUTION].value.w = min;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_PREVIEW:
+ s->options[OPT_PREVIEW].value.w = SANE_FALSE;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_MODE:
+ if (s->options[OPT_MODE].value.s)
+ free (s->options[OPT_MODE].value.s);
+ s->options[OPT_MODE].value.s = strdup (mode_list[0]);
+ *myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ default:
+ DBG (DBG_warn, "set_automatic_value: can't set unknown option %d\n",
+ option);
+ }
+
+ return status;
+}
+
+/**
+ * sets an option , called by sane_control_option after all
+ * checks have been done */
+static SANE_Status
+set_option_value (P5_Session * s, int option, void *val, SANE_Int * myinfo)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Word tmpw;
+
+ switch (option)
+ {
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ s->options[option].value.w = *(SANE_Word *) val;
+ /* we ensure geometry is coherent */
+ /* this happens when user drags TL corner right or below the BR point */
+ if (s->options[OPT_BR_Y].value.w < s->options[OPT_TL_Y].value.w)
+ {
+ tmpw = s->options[OPT_BR_Y].value.w;
+ s->options[OPT_BR_Y].value.w = s->options[OPT_TL_Y].value.w;
+ s->options[OPT_TL_Y].value.w = tmpw;
+ }
+ if (s->options[OPT_BR_X].value.w < s->options[OPT_TL_X].value.w)
+ {
+ tmpw = s->options[OPT_BR_X].value.w;
+ s->options[OPT_BR_X].value.w = s->options[OPT_TL_X].value.w;
+ s->options[OPT_TL_X].value.w = tmpw;
+ }
+
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ s->options[option].value.w = *(SANE_Word *) val;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_MODE:
+ if (s->options[option].value.s)
+ free (s->options[option].value.s);
+ s->options[option].value.s = strdup (val);
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_CALIBRATE:
+ status = sheetfed_calibration (s->dev);
+ *myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_CLEAR_CALIBRATION:
+ cleanup_calibration (s->dev);
+ *myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ default:
+ DBG (DBG_warn, "set_option_value: can't set unknown option %d\n",
+ option);
+ }
+ return status;
+}
+
+/**
+ * gets an option , called by sane_control_option after all checks
+ * have been done */
+static SANE_Status
+get_option_value (P5_Session * s, int option, void *val)
+{
+ SANE_Status status;
+
+ switch (option)
+ {
+ /* word or word equivalent options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *) val = s->options[option].value.w;
+ break;
+
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->options[option].value.s);
+ break;
+
+ /* sensor options */
+ case OPT_PAGE_LOADED_SW:
+ status = test_document (s->dev->fd);
+ if (status == SANE_STATUS_GOOD)
+ s->options[option].value.b = SANE_TRUE;
+ else
+ s->options[option].value.b = SANE_FALSE;
+ *(SANE_Bool *) val = s->options[option].value.b;
+ break;
+
+ case OPT_NEED_CALIBRATION_SW:
+ *(SANE_Bool *) val = !s->dev->calibrated;
+ break;
+
+
+ /* unhandled options */
+ default:
+ DBG (DBG_warn, "get_option_value: can't get unknown option %d\n",
+ option);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ * action is SANE_ACTION_GET_VALUE, SANE_ACTION_SET_VALUE or SANE_ACTION_SET_AUTO
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ P5_Session *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (DBG_io2,
+ "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
+ SANE_ACTION_SET_VALUE) ?
+ "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->options[option].descriptor.name, option);
+
+ if (info)
+ *info = 0;
+
+ /* do checks before trying to apply action */
+
+ if (s->scanning)
+ {
+ DBG (DBG_warn, "sane_control_option: don't call this function while "
+ "scanning (option = %s (%d))\n",
+ s->options[option].descriptor.name, option);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* option must be within existing range */
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (DBG_warn,
+ "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* don't access an inactive option */
+ cap = s->options[option].descriptor.cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (DBG_warn, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now checks have been done, apply action */
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ status = get_option_value (s, option, val);
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_warn, "sane_control_option: option %d is not settable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status =
+ sanei_constrain_value (&s->options[option].descriptor, val, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn,
+ "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* return immediatly if no change */
+ if (s->options[option].descriptor.type == SANE_TYPE_INT
+ && *(SANE_Word *) val == s->options[option].value.w)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ { /* apply change */
+ status = set_option_value (s, option, val, &myinfo);
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ /* sets automatic values */
+ if (!(cap & SANE_CAP_AUTOMATIC))
+ {
+ DBG (DBG_warn,
+ "sane_control_option: option %d is not autosettable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = set_automatic_value (s, option, &myinfo);
+ break;
+
+ default:
+ DBG (DBG_error, "sane_control_option: invalid action %d\n", action);
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (info)
+ *info = myinfo;
+
+ DBG (DBG_io2, "sane_control_option: exit\n");
+ return status;
+}
+
+/**
+ * Called by SANE when a page acquisition operation is to be started.
+ * @param handle opaque handle to a frontend session
+ * @return SANE_STATUS_GOOD on success, SANE_STATUS_BUSY if the device is
+ * in use by another session or SANE_STATUS_WARMING_UP if the device is
+ * warming up. In this case the fronted as to call sane_start again until
+ * warming up is done. Any other values returned are error status.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct P5_Session *session = handle;
+ int status = SANE_STATUS_GOOD;
+ P5_Device *dev = session->dev;
+
+ DBG (DBG_proc, "sane_start: start\n");
+
+ /* if already scanning, tell we're busy */
+ if (session->scanning == SANE_TRUE)
+ {
+ DBG (DBG_info, "sane_start: device is already scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* check that the device has been initialized */
+ if (dev->initialized == SANE_FALSE)
+ {
+ DBG (DBG_error, "sane_start: device is not initialized\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* check if there is a document */
+ status = test_document (dev->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: device is already scanning\n");
+ return status;
+ }
+
+ /* we compute all the scan parameters so that */
+ /* we will be able to set up the registers correctly */
+ compute_parameters (session);
+
+ /* move to scan area if needed */
+ if (dev->ystart > 0)
+ {
+ status = move (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: failed to move to scan area\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /* send scan command */
+ status = start_scan (dev, dev->mode, dev->ydpi, dev->xstart, dev->pixels);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: failed to start scan\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* allocates work buffer */
+ if (dev->buffer != NULL)
+ {
+ free (dev->buffer);
+ }
+
+ dev->position = 0;
+ dev->top = 0;
+ /* compute amount of lines needed for lds correction */
+ dev->bottom = dev->bytes_per_line * 2 * dev->lds;
+ /* computes buffer size, 66 color lines plus eventual amount needed for lds */
+ dev->size = dev->pixels * 3 * 66 + dev->bottom;
+ dev->buffer = (uint8_t *) malloc (dev->size);
+ if (dev->buffer == NULL)
+ {
+ DBG (DBG_error, "sane_start: failed to allocate %lu bytes\n", (unsigned long)dev->size);
+ sane_cancel (handle);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* return now the scan has been initiated */
+ session->scanning = SANE_TRUE;
+ session->sent = 0;
+
+ DBG (DBG_io, "sane_start: to_send=%d\n", session->to_send);
+ DBG (DBG_io, "sane_start: size=%lu\n", (unsigned long)dev->size);
+ DBG (DBG_io, "sane_start: top=%lu\n", (unsigned long)dev->top);
+ DBG (DBG_io, "sane_start: bottom=%lu\n", (unsigned long)dev->bottom);
+ DBG (DBG_io, "sane_start: position=%lu\n", (unsigned long)dev->position);
+
+ DBG (DBG_proc, "sane_start: exit\n");
+ return status;
+}
+
+/** @brief compute scan parameters
+ * This function computes two set of parameters. The one for the SANE's standard
+ * and the other for the hardware. Among these parameters are the bit depth, total
+ * number of lines, total number of columns, extra line to read for data reordering...
+ * @param session fronted session to compute final scan parameters
+ * @return SANE_STATUS_GOOD on success
+ */
+static SANE_Status
+compute_parameters (P5_Session * session)
+{
+ P5_Device *dev = session->dev;
+ SANE_Int dpi; /* dpi for scan */
+ SANE_String mode;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ int tl_x, tl_y, br_x, br_y;
+
+ mode = session->options[OPT_MODE].value.s;
+ dpi = session->options[OPT_RESOLUTION].value.w;
+
+ /* scan coordinates */
+ tl_x = SANE_UNFIX (session->options[OPT_TL_X].value.w);
+ tl_y = SANE_UNFIX (session->options[OPT_TL_Y].value.w);
+ br_x = SANE_UNFIX (session->options[OPT_BR_X].value.w);
+ br_y = SANE_UNFIX (session->options[OPT_BR_Y].value.w);
+
+ /* only single pass scanning supported */
+ session->params.last_frame = SANE_TRUE;
+
+ /* gray modes */
+ if (strcmp (mode, GRAY_MODE) == 0)
+ {
+ session->params.format = SANE_FRAME_GRAY;
+ dev->mode = MODE_GRAY;
+ dev->lds = 0;
+ }
+ else if (strcmp (mode, LINEART_MODE) == 0)
+ {
+ session->params.format = SANE_FRAME_GRAY;
+ dev->mode = MODE_LINEART;
+ dev->lds = 0;
+ }
+ else
+ {
+ /* Color */
+ session->params.format = SANE_FRAME_RGB;
+ dev->mode = MODE_COLOR;
+ dev->lds = (dev->model->lds * dpi) / dev->model->max_ydpi;
+ }
+
+ /* SANE level values */
+ session->params.lines = ((br_y - tl_y) * dpi) / MM_PER_INCH;
+ if (session->params.lines == 0)
+ session->params.lines = 1;
+ session->params.pixels_per_line = ((br_x - tl_x) * dpi) / MM_PER_INCH;
+ if (session->params.pixels_per_line == 0)
+ session->params.pixels_per_line = 1;
+
+ DBG (DBG_data, "compute_parameters: pixels_per_line =%d\n",
+ session->params.pixels_per_line);
+
+ if (strcmp (mode, LINEART_MODE) == 0)
+ {
+ session->params.depth = 1;
+ /* in lineart, having pixels multiple of 8 avoids a costly test */
+ /* at each bit to see we must go to the next byte */
+ /* TODO : implement this requirement in sane_control_option */
+ session->params.pixels_per_line =
+ ((session->params.pixels_per_line + 7) / 8) * 8;
+ }
+ else
+ session->params.depth = 8;
+
+ /* width needs to be even */
+ if (session->params.pixels_per_line & 1)
+ session->params.pixels_per_line++;
+
+ /* Hardware settings : they can differ from the ones at SANE level */
+ /* for instance the effective DPI used by a sensor may be higher */
+ /* than the one needed for the SANE scan parameters */
+ dev->lines = session->params.lines;
+ dev->pixels = session->params.pixels_per_line;
+
+ /* motor and sensor DPI */
+ dev->xdpi = dpi;
+ dev->ydpi = dpi;
+
+ /* handle bounds of motor's dpi range */
+ if (dev->ydpi > dev->model->max_ydpi)
+ {
+ dev->ydpi = dev->model->max_ydpi;
+ dev->lines = (dev->lines * dev->model->max_ydpi) / dpi;
+ if (dev->lines == 0)
+ dev->lines = 1;
+
+ /* round number of lines */
+ session->params.lines =
+ (session->params.lines / dev->lines) * dev->lines;
+ if (session->params.lines == 0)
+ session->params.lines = 1;
+ }
+ if (dev->ydpi < dev->model->min_ydpi)
+ {
+ dev->ydpi = dev->model->min_ydpi;
+ dev->lines = (dev->lines * dev->model->min_ydpi) / dpi;
+ }
+
+ /* hardware values */
+ dev->xstart =
+ ((SANE_UNFIX (dev->model->x_offset) + tl_x) * dpi) / MM_PER_INCH;
+ dev->ystart =
+ ((SANE_UNFIX (dev->model->y_offset) + tl_y) * dev->ydpi) / MM_PER_INCH;
+
+ /* take lds correction into account when moving to scan area */
+ if (dev->ystart > 2 * dev->lds)
+ dev->ystart -= 2 * dev->lds;
+
+
+ /* computes bytes per line */
+ session->params.bytes_per_line = session->params.pixels_per_line;
+ dev->bytes_per_line = dev->pixels;
+ if (session->params.format == SANE_FRAME_RGB)
+ {
+ dev->bytes_per_line *= 3;
+ }
+
+ /* in lineart mode we adjust bytes_per_line needed by frontend */
+ /* we do that here because we needed sent/to_send to be as if */
+ /* there was no lineart */
+ if (session->params.depth == 1)
+ {
+ session->params.bytes_per_line =
+ (session->params.bytes_per_line + 7) / 8;
+ }
+
+ session->params.bytes_per_line = dev->bytes_per_line;
+ session->to_send = session->params.bytes_per_line * session->params.lines;
+ session->params.bytes_per_line = dev->bytes_per_line;
+
+ DBG (DBG_data, "compute_parameters: bytes_per_line =%d\n",
+ session->params.bytes_per_line);
+ DBG (DBG_data, "compute_parameters: depth =%d\n",
+ session->params.depth);
+ DBG (DBG_data, "compute_parameters: lines =%d\n",
+ session->params.lines);
+ DBG (DBG_data, "compute_parameters: image size =%d\n",
+ session->to_send);
+
+ DBG (DBG_data, "compute_parameters: xstart =%d\n", dev->xstart);
+ DBG (DBG_data, "compute_parameters: ystart =%d\n", dev->ystart);
+ DBG (DBG_data, "compute_parameters: dev lines =%d\n", dev->lines);
+ DBG (DBG_data, "compute_parameters: dev bytes per line=%d\n",
+ dev->bytes_per_line);
+ DBG (DBG_data, "compute_parameters: dev pixels =%d\n", dev->pixels);
+ DBG (DBG_data, "compute_parameters: lds =%d\n", dev->lds);
+
+ return status;
+}
+
+
+/**
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle of the
+ * device for which the parameters should be obtained and a pointer
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Status status;
+ struct P5_Session *session = (struct P5_Session *) handle;
+
+ DBG (DBG_proc, "sane_get_parameters: start\n");
+
+ /* call parameters computing function */
+ status = compute_parameters (session);
+ if (status == SANE_STATUS_GOOD && params)
+ *params = session->params;
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+ return status;
+}
+
+
+/**
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ *
+ * Returned data is read from working buffer.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ struct P5_Session *session = (struct P5_Session *) handle;
+ struct P5_Device *dev = session->dev;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int count;
+ int size, lines;
+ SANE_Bool x2;
+ SANE_Int i;
+
+ DBG (DBG_proc, "sane_read: start\n");
+ DBG (DBG_io, "sane_read: up to %d bytes required by frontend\n", max_len);
+
+ /* some sanity checks first to protect from would be buggy frontends */
+ if (!session)
+ {
+ DBG (DBG_error, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!buf)
+ {
+ DBG (DBG_error, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (DBG_error, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* no data read yet */
+ *len = 0;
+
+ /* check if session is scanning */
+ if (!session->scanning)
+ {
+ DBG (DBG_warn,
+ "sane_read: scan was cancelled, is over or has not been initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* check for EOF, must be done before any physical read */
+ if (session->sent >= session->to_send)
+ {
+ DBG (DBG_io, "sane_read: end of scan reached\n");
+ return SANE_STATUS_EOF;
+ }
+
+ /* if working buffer is empty, we do a physical data read */
+ if (dev->top <= dev->bottom)
+ {
+ DBG (DBG_io, "sane_read: physical data read\n");
+ /* check is there is data available. In case of non-blocking mode we return
+ * as soon it is detected there is no data yet. Reads must by done line by
+ * line, so we read only when count is bigger than bytes per line
+ * */
+ count = available_bytes (dev->fd);
+ DBG (DBG_io, "sane_read: count=%d bytes\n", count);
+ if (count < dev->bytes_per_line && session->non_blocking == SANE_TRUE)
+ {
+ DBG (DBG_io, "sane_read: scanner hasn't enough data available\n");
+ DBG (DBG_proc, "sane_read: exit\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* now we can wait for data here */
+ while (count < dev->bytes_per_line)
+ {
+ /* test if document left the feeder, so we have to terminate the scan */
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS)
+ {
+ session->to_send = session->sent;
+ return SANE_STATUS_EOF;
+ }
+
+ /* don't call scanner too often */
+ usleep (10000);
+ count = available_bytes (dev->fd);
+ }
+
+ /** compute size of physical data to read
+ * on first read, position will be 0, while it will be 'bottom'
+ * for the subsequent reads.
+ * We try to read a complete buffer */
+ size = dev->size - dev->position;
+
+ if (session->to_send - session->sent < size)
+ {
+ /* not enough data left, so read remainder of scan */
+ size = session->to_send - session->sent;
+ }
+
+ /* 600 dpi is 300x600 physical, and 400 is 200x400 */
+ if (dev->ydpi > dev->model->max_xdpi)
+ {
+ x2 = SANE_TRUE;
+ }
+ else
+ {
+ x2 = SANE_FALSE;
+ }
+ lines = read_line (dev,
+ dev->buffer + dev->position,
+ dev->bytes_per_line,
+ size / dev->bytes_per_line,
+ SANE_TRUE, x2, dev->mode, dev->calibrated);
+
+ /* handle document end detection TODO try to recover the partial
+ * buffer already read before EOD */
+ if (lines == -1)
+ {
+ DBG (DBG_io, "sane_read: error reading line\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* gather lines until we have more than needed for lds */
+ dev->position += lines * dev->bytes_per_line;
+ dev->top = dev->position;
+ if (dev->position > dev->bottom)
+ {
+ dev->position = dev->bottom;
+ }
+ DBG (DBG_io, "sane_read: size =%lu\n", (unsigned long)dev->size);
+ DBG (DBG_io, "sane_read: bottom =%lu\n", (unsigned long)dev->bottom);
+ DBG (DBG_io, "sane_read: position=%lu\n", (unsigned long)dev->position);
+ DBG (DBG_io, "sane_read: top =%lu\n", (unsigned long)dev->top);
+ } /* end of physical data reading */
+
+ /* logical data reading */
+ /* check if there data available in working buffer */
+ if (dev->position < dev->top && dev->position >= dev->bottom)
+ {
+ DBG (DBG_io, "sane_read: logical data read\n");
+ /* we have more data in internal buffer than asked ,
+ * then send only max data */
+ size = dev->top - dev->position;
+ if (max_len < size)
+ {
+ *len = max_len;
+ }
+ else
+ /* if we don't have enough, send all what we have */
+ {
+ *len = dev->top - dev->position;
+ }
+
+ /* data copy */
+ if (dev->lds == 0)
+ {
+ memcpy (buf, dev->buffer + dev->position, *len);
+ }
+ else
+ {
+ /* compute count of bytes for lds */
+ count = dev->lds * dev->bytes_per_line;
+
+ /* adjust for lds as we copy data to frontend */
+ for (i = 0; i < *len; i++)
+ {
+ switch ((dev->position + i) % 3)
+ {
+ /* red */
+ case 0:
+ buf[i] = dev->buffer[dev->position + i - 2 * count];
+ break;
+ /* green */
+ case 1:
+ buf[i] = dev->buffer[dev->position + i - count];
+ break;
+ /* blue */
+ default:
+ buf[i] = dev->buffer[dev->position + i];
+ break;
+ }
+ }
+ }
+ dev->position += *len;
+
+ /* update byte accounting */
+ session->sent += *len;
+ DBG (DBG_io, "sane_read: sent %d bytes from buffer to frontend\n",
+ *len);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* check if we exhausted working buffer */
+ if (dev->position >= dev->top && dev->position >= dev->bottom)
+ {
+ /* copy extra lines needed for lds in next buffer */
+ if (dev->position > dev->bottom && dev->lds > 0)
+ {
+ memcpy (dev->buffer,
+ dev->buffer + dev->position - dev->bottom, dev->bottom);
+ }
+
+ /* restart buffer */
+ dev->position = dev->bottom;
+ dev->top = 0;
+ }
+
+ DBG (DBG_io, "sane_read: size =%lu\n", (unsigned long)dev->size);
+ DBG (DBG_io, "sane_read: bottom =%lu\n", (unsigned long)dev->bottom);
+ DBG (DBG_io, "sane_read: position=%lu\n", (unsigned long)dev->position);
+ DBG (DBG_io, "sane_read: top =%lu\n", (unsigned long)dev->top);
+
+ DBG (DBG_proc, "sane_read: exit\n");
+ return status;
+}
+
+
+/**
+ * Cancels a scan.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ P5_Session *session = handle;
+
+ DBG (DBG_proc, "sane_cancel: start\n");
+
+ /* if scanning, abort and park head */
+ if (session->scanning == SANE_TRUE)
+ {
+ /* detects if we are called after the scan is finished,
+ * or if the scan is aborted */
+ if (session->sent < session->to_send)
+ {
+ DBG (DBG_info, "sane_cancel: aborting scan.\n");
+ /* device hasn't finished scan, we are aborting it
+ * and we may have to do something specific for it here */
+ }
+ else
+ {
+ DBG (DBG_info, "sane_cancel: cleaning up after scan.\n");
+ }
+ session->scanning = SANE_FALSE;
+ }
+ eject (session->dev->fd);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+
+/**
+ * Ends use of the session.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ *
+ * Handle resources are free'd before disposing the handle. But devices
+ * resources must not be mdofied, since it could be used or reused until
+ * sane_exit() is called.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ P5_Session *prev, *session;
+
+ DBG (DBG_proc, "sane_close: start\n");
+
+ /* remove handle from list of open handles: */
+ prev = NULL;
+ for (session = sessions; session; session = session->next)
+ {
+ if (session == handle)
+ break;
+ prev = session;
+ }
+ if (!session)
+ {
+ DBG (DBG_error0, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ /* cancel any active scan */
+ if (session->scanning == SANE_TRUE)
+ {
+ sane_cancel (handle);
+ }
+
+ if (prev)
+ prev->next = session->next;
+ else
+ sessions = session->next;
+
+ /* close low level device */
+ if (session->dev->initialized == SANE_TRUE)
+ {
+ if (session->dev->calibrated == SANE_TRUE)
+ {
+ save_calibration (session->dev);
+ }
+ disconnect (session->dev->fd);
+ close_pp (session->dev->fd);
+ session->dev->fd = -1;
+ session->dev->initialized = SANE_FALSE;
+
+ /* free device data */
+ if (session->dev->buffer != NULL)
+ {
+ free (session->dev->buffer);
+ }
+ if (session->dev->buffer != NULL)
+ {
+ free (session->dev->gain);
+ free (session->dev->offset);
+ }
+ if (session->dev->calibrated == SANE_TRUE)
+ {
+ cleanup_calibration (session->dev);
+ }
+ }
+
+ /* free per session data */
+ free (session->options[OPT_MODE].value.s);
+ free ((void *)session->options[OPT_RESOLUTION].descriptor.constraint.word_list);
+
+ free (session);
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+
+/**
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct P5_Session *session, *next;
+ struct P5_Device *dev, *nextdev;
+ int i;
+
+ DBG (DBG_proc, "sane_exit: start\n");
+ init_count--;
+
+ if (init_count > 0)
+ {
+ DBG (DBG_info,
+ "sane_exit: still %d fronteds to leave before effective exit.\n",
+ init_count);
+ return;
+ }
+
+ /* free session structs */
+ for (session = sessions; session; session = next)
+ {
+ next = session->next;
+ sane_close ((SANE_Handle *) session);
+ free (session);
+ }
+ sessions = NULL;
+
+ /* free devices structs */
+ for (dev = devices; dev; dev = nextdev)
+ {
+ nextdev = dev->next;
+ free (dev->name);
+ free (dev);
+ }
+ devices = NULL;
+
+ /* now list of devices */
+ if (devlist)
+ {
+ i = 0;
+ while ((SANE_Device *) devlist[i])
+ {
+ free ((SANE_Device *) devlist[i]);
+ i++;
+ }
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
+
+
+/** @brief probe for all supported devices
+ * This functions tries to probe if any of the supported devices of
+ * the backend is present. Each detected device will be added to the
+ * 'devices' list
+ */
+static SANE_Status
+probe_p5_devices (void)
+{
+ /**> configuration structure used during attach */
+ SANEI_Config config;
+ /**> list of configuration options */
+ SANE_Option_Descriptor *cfg_options[NUM_CFG_OPTIONS];
+ /**> placeholders pointers for option values */
+ void *values[NUM_CFG_OPTIONS];
+ int i;
+ SANE_Status status;
+
+ DBG (DBG_proc, "probe_p5_devices: start\n");
+
+ /* initialize configuration options */
+ cfg_options[CFG_MODEL_NAME] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ cfg_options[CFG_MODEL_NAME]->name = "modelname";
+ cfg_options[CFG_MODEL_NAME]->desc = "user provided scanner's model name";
+ cfg_options[CFG_MODEL_NAME]->type = SANE_TYPE_INT;
+ cfg_options[CFG_MODEL_NAME]->unit = SANE_UNIT_NONE;
+ cfg_options[CFG_MODEL_NAME]->size = sizeof (SANE_Word);
+ cfg_options[CFG_MODEL_NAME]->cap = SANE_CAP_SOFT_SELECT;
+ cfg_options[CFG_MODEL_NAME]->constraint_type = SANE_CONSTRAINT_NONE;
+ values[CFG_MODEL_NAME] = &p5cfg.modelname;
+
+ /* set configuration options structure */
+ config.descriptors = cfg_options;
+ config.values = values;
+ config.count = NUM_CFG_OPTIONS;
+
+ /* generic configure and attach function */
+ status = sanei_configure_attach (P5_CONFIG_FILE, &config, config_attach);
+ /* free allocated options */
+ for (i = 0; i < NUM_CFG_OPTIONS; i++)
+ {
+ free (cfg_options[i]);
+ }
+
+ DBG (DBG_proc, "probe_p5_devices: end\n");
+ return status;
+}
+
+/** This function is called by sanei_configure_attach to try
+ * to attach the backend to a device specified by the configuration file.
+ *
+ * @param config configuration structure filled with values read
+ * from configuration file
+ * @param devname name of the device to try to attach to, it is
+ * the unprocessed line of the configuration file
+ *
+ * @return status SANE_STATUS_GOOD if no errors (even if no matching
+ * devices found)
+ * SANE_STATUS_INVAL in case of error
+ */
+static SANE_Status
+config_attach (SANEI_Config * config, const char *devname)
+{
+ /* currently, the config is a global variable so config is useless here */
+ /* the correct thing would be to have a generic sanei_attach_matching_devices
+ * using an attach function with a config parameter */
+ config = config;
+
+ /* the devname has been processed and is ready to be used
+ * directly. The config struct contains all the configuration data for
+ * the corresponding device. Since there is no ressources common to each
+ * backends regarding parallel port, we can directly call the attach
+ * function. */
+ attach_p5 (devname, config);
+
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief try to attach to a device by its name
+ * The attach tries to open the given device and match it
+ * with devices handled by the backend. The configuration parameter
+ * contains the values of the already parsed configuration options
+ * from the conf file.
+ * @param config configuration structure filled with values read
+ * from configuration file
+ * @param devicename name of the device to try to attach to, it is
+ * the unprocessed line of the configuration file
+ *
+ * @return status SANE_STATUS_GOOD if no errors (even if no matching
+ * devices found)
+ * SANE_STATUS_NOM_MEM if there isn't enough memory to allocate the
+ * device structure
+ * SANE_STATUS_UNSUPPORTED if the device if unknown by the backend
+ * SANE_STATUS_INVAL in case of other error
+ */
+static SANE_Status
+attach_p5 (const char *devicename, SANEI_Config * config)
+{
+ struct P5_Device *device;
+ struct P5_Model *model;
+
+ DBG (DBG_proc, "attach(%s): start\n", devicename);
+ if(config==NULL)
+ {
+ DBG (DBG_warn, "attach: config is NULL\n");
+ }
+
+ /* search if we already have it attached */
+ for (device = devices; device; device = device->next)
+ {
+ if (strcmp (device->name, devicename) == 0)
+ {
+ DBG (DBG_info, "attach: device already attached\n");
+ DBG (DBG_proc, "attach: exit\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /**
+ * do physical probe of the device here. In case the device is recognized,
+ * we allocate a device struct and give it options and model.
+ * Else we return SANE_STATUS_UNSUPPORTED.
+ */
+ model = probe (devicename);
+ if (model == NULL)
+ {
+ DBG (DBG_info,
+ "attach: device %s is not managed by the backend\n", devicename);
+ DBG (DBG_proc, "attach: exit\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* allocate device struct */
+ device = malloc (sizeof (*device));
+ if (device == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ DBG (DBG_proc, "attach: exit\n");
+ }
+ memset (device, 0, sizeof (*device));
+ device->model = model;
+
+ /* name of the device */
+ device->name = strdup (devicename);
+
+ DBG (DBG_info, "attach: found %s %s %s at %s\n",
+ device->model->vendor, device->model->product, device->model->type,
+ device->name);
+
+ /* we insert new device at start of the chained list */
+ /* head of the list becomes the next, and start is replaced */
+ /* with the new session struct */
+ device->next = devices;
+ devices = device;
+
+ /* intialization is done at sane_open */
+ device->initialized = SANE_FALSE;
+ device->calibrated = SANE_FALSE;
+
+ DBG (DBG_proc, "attach: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief set initial value for the scanning options
+ * for each sessions, control options are initalized based on the capability
+ * of the model of the physical device.
+ * @param session scanner session to initialize options
+ * @return SANE_STATUS_GOOD on success
+ */
+static SANE_Status
+init_options (struct P5_Session *session)
+{
+ SANE_Int option, i, min, idx;
+ SANE_Word *dpi_list;
+ P5_Model *model = session->dev->model;
+
+ DBG (DBG_proc, "init_options: start\n");
+
+ /* we first initialize each options with a default value */
+ memset (session->options, 0, sizeof (session->options[OPT_NUM_OPTS]));
+ for (option = 0; option < NUM_OPTIONS; option++)
+ {
+ session->options[option].descriptor.size = sizeof (SANE_Word);
+ session->options[option].descriptor.cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* we set up all the options listed in the P5_Option enum */
+
+ /* last option / end of list marker */
+ session->options[OPT_NUM_OPTS].descriptor.name = SANE_NAME_NUM_OPTIONS;
+ session->options[OPT_NUM_OPTS].descriptor.title = SANE_TITLE_NUM_OPTIONS;
+ session->options[OPT_NUM_OPTS].descriptor.desc = SANE_DESC_NUM_OPTIONS;
+ session->options[OPT_NUM_OPTS].descriptor.type = SANE_TYPE_INT;
+ session->options[OPT_NUM_OPTS].descriptor.cap = SANE_CAP_SOFT_DETECT;
+ session->options[OPT_NUM_OPTS].value.w = NUM_OPTIONS;
+
+ /* "Standard" group: */
+ session->options[OPT_STANDARD_GROUP].descriptor.title = SANE_TITLE_STANDARD;
+ session->options[OPT_STANDARD_GROUP].descriptor.name = SANE_NAME_STANDARD;
+ session->options[OPT_STANDARD_GROUP].descriptor.desc = SANE_DESC_STANDARD;
+ session->options[OPT_STANDARD_GROUP].descriptor.type = SANE_TYPE_GROUP;
+ session->options[OPT_STANDARD_GROUP].descriptor.size = 0;
+ session->options[OPT_STANDARD_GROUP].descriptor.cap = 0;
+ session->options[OPT_STANDARD_GROUP].descriptor.constraint_type =
+ SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ session->options[OPT_MODE].descriptor.name = SANE_NAME_SCAN_MODE;
+ session->options[OPT_MODE].descriptor.title = SANE_TITLE_SCAN_MODE;
+ session->options[OPT_MODE].descriptor.desc = SANE_DESC_SCAN_MODE;
+ session->options[OPT_MODE].descriptor.type = SANE_TYPE_STRING;
+ session->options[OPT_MODE].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_MODE].descriptor.constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ session->options[OPT_MODE].descriptor.size = max_string_size (mode_list);
+ session->options[OPT_MODE].descriptor.constraint.string_list = mode_list;
+ session->options[OPT_MODE].value.s = strdup (mode_list[0]);
+
+ /* preview */
+ session->options[OPT_PREVIEW].descriptor.name = SANE_NAME_PREVIEW;
+ session->options[OPT_PREVIEW].descriptor.title = SANE_TITLE_PREVIEW;
+ session->options[OPT_PREVIEW].descriptor.desc = SANE_DESC_PREVIEW;
+ session->options[OPT_PREVIEW].descriptor.type = SANE_TYPE_BOOL;
+ session->options[OPT_PREVIEW].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_PREVIEW].descriptor.unit = SANE_UNIT_NONE;
+ session->options[OPT_PREVIEW].descriptor.constraint_type =
+ SANE_CONSTRAINT_NONE;
+ session->options[OPT_PREVIEW].value.w = SANE_FALSE;
+
+ /** @brief build resolution list
+ * We merge xdpi and ydpi list to provide only one resolution option control.
+ * This is the most common case for backends and fronteds and give 'square'
+ * pixels. The SANE API allow to control x and y dpi independantly, but this is
+ * rarely done and may confuse both frontends and users. In case a dpi value exists
+ * for one but not for the other, the backend will have to crop data so that the
+ * frontend is unaffected. A common case is that motor resolution (ydpi) is higher
+ * than sensor resolution (xdpi), so scan lines must be scaled up to keep square
+ * pixel when doing sane_read().
+ * TODO this deserves a dedicated function and some unit testing
+ */
+
+ /* find minimum first */
+ min = 65535;
+ for (i = 0; i < MAX_RESOLUTIONS && model->xdpi_values[i] > 0; i++)
+ {
+ if (model->xdpi_values[i] < min)
+ min = model->xdpi_values[i];
+ }
+ for (i = 0; i < MAX_RESOLUTIONS && model->ydpi_values[i] > 0; i++)
+ {
+ if (model->ydpi_values[i] < min)
+ min = model->ydpi_values[i];
+ }
+
+ dpi_list = malloc ((MAX_RESOLUTIONS * 2 + 1) * sizeof (SANE_Word));
+ if (!dpi_list)
+ return SANE_STATUS_NO_MEM;
+ dpi_list[1] = min;
+ idx = 2;
+
+ /* find any value greater than the last used min and
+ * less than the max value
+ */
+ do
+ {
+ min = 65535;
+ for (i = 0; i < MAX_RESOLUTIONS && model->xdpi_values[i] > 0; i++)
+ {
+ if (model->xdpi_values[i] < min
+ && model->xdpi_values[i] > dpi_list[idx - 1])
+ min = model->xdpi_values[i];
+ }
+ for (i = 0; i < MAX_RESOLUTIONS && model->ydpi_values[i] > 0; i++)
+ {
+ if (model->ydpi_values[i] < min
+ && model->ydpi_values[i] > dpi_list[idx - 1])
+ min = model->ydpi_values[i];
+ }
+ if (min < 65535)
+ {
+ dpi_list[idx] = min;
+ idx++;
+ }
+ }
+ while (min != 65535);
+ dpi_list[idx] = 0;
+ /* the count of different resolution is put at the beginning */
+ dpi_list[0] = idx - 1;
+
+ session->options[OPT_RESOLUTION].descriptor.name =
+ SANE_NAME_SCAN_RESOLUTION;
+ session->options[OPT_RESOLUTION].descriptor.title =
+ SANE_TITLE_SCAN_RESOLUTION;
+ session->options[OPT_RESOLUTION].descriptor.desc =
+ SANE_DESC_SCAN_RESOLUTION;
+ session->options[OPT_RESOLUTION].descriptor.type = SANE_TYPE_INT;
+ session->options[OPT_RESOLUTION].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_RESOLUTION].descriptor.unit = SANE_UNIT_DPI;
+ session->options[OPT_RESOLUTION].descriptor.constraint_type =
+ SANE_CONSTRAINT_WORD_LIST;
+ session->options[OPT_RESOLUTION].descriptor.constraint.word_list = dpi_list;
+
+ /* initial value is lowest available dpi */
+ session->options[OPT_RESOLUTION].value.w = min;
+
+ /* "Geometry" group: */
+ session->options[OPT_GEOMETRY_GROUP].descriptor.title = SANE_TITLE_GEOMETRY;
+ session->options[OPT_GEOMETRY_GROUP].descriptor.name = SANE_NAME_GEOMETRY;
+ session->options[OPT_GEOMETRY_GROUP].descriptor.desc = SANE_DESC_GEOMETRY;
+ session->options[OPT_GEOMETRY_GROUP].descriptor.type = SANE_TYPE_GROUP;
+ session->options[OPT_GEOMETRY_GROUP].descriptor.cap = SANE_CAP_ADVANCED;
+ session->options[OPT_GEOMETRY_GROUP].descriptor.size = 0;
+ session->options[OPT_GEOMETRY_GROUP].descriptor.constraint_type =
+ SANE_CONSTRAINT_NONE;
+
+ /* adapt the constraint range to the detected model */
+ x_range.max = model->x_size;
+ y_range.max = model->y_size;
+
+ /* top-left x */
+ session->options[OPT_TL_X].descriptor.name = SANE_NAME_SCAN_TL_X;
+ session->options[OPT_TL_X].descriptor.title = SANE_TITLE_SCAN_TL_X;
+ session->options[OPT_TL_X].descriptor.desc = SANE_DESC_SCAN_TL_X;
+ session->options[OPT_TL_X].descriptor.type = SANE_TYPE_FIXED;
+ session->options[OPT_TL_X].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_TL_X].descriptor.unit = SANE_UNIT_MM;
+ session->options[OPT_TL_X].descriptor.constraint_type =
+ SANE_CONSTRAINT_RANGE;
+ session->options[OPT_TL_X].descriptor.constraint.range = &x_range;
+ session->options[OPT_TL_X].value.w = 0;
+
+ /* top-left y */
+ session->options[OPT_TL_Y].descriptor.name = SANE_NAME_SCAN_TL_Y;
+ session->options[OPT_TL_Y].descriptor.title = SANE_TITLE_SCAN_TL_Y;
+ session->options[OPT_TL_Y].descriptor.desc = SANE_DESC_SCAN_TL_Y;
+ session->options[OPT_TL_Y].descriptor.type = SANE_TYPE_FIXED;
+ session->options[OPT_TL_Y].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_TL_Y].descriptor.unit = SANE_UNIT_MM;
+ session->options[OPT_TL_Y].descriptor.constraint_type =
+ SANE_CONSTRAINT_RANGE;
+ session->options[OPT_TL_Y].descriptor.constraint.range = &y_range;
+ session->options[OPT_TL_Y].value.w = 0;
+
+ /* bottom-right x */
+ session->options[OPT_BR_X].descriptor.name = SANE_NAME_SCAN_BR_X;
+ session->options[OPT_BR_X].descriptor.title = SANE_TITLE_SCAN_BR_X;
+ session->options[OPT_BR_X].descriptor.desc = SANE_DESC_SCAN_BR_X;
+ session->options[OPT_BR_X].descriptor.type = SANE_TYPE_FIXED;
+ session->options[OPT_BR_X].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_BR_X].descriptor.unit = SANE_UNIT_MM;
+ session->options[OPT_BR_X].descriptor.constraint_type =
+ SANE_CONSTRAINT_RANGE;
+ session->options[OPT_BR_X].descriptor.constraint.range = &x_range;
+ session->options[OPT_BR_X].value.w = x_range.max;
+
+ /* bottom-right y */
+ session->options[OPT_BR_Y].descriptor.name = SANE_NAME_SCAN_BR_Y;
+ session->options[OPT_BR_Y].descriptor.title = SANE_TITLE_SCAN_BR_Y;
+ session->options[OPT_BR_Y].descriptor.desc = SANE_DESC_SCAN_BR_Y;
+ session->options[OPT_BR_Y].descriptor.type = SANE_TYPE_FIXED;
+ session->options[OPT_BR_Y].descriptor.cap |= SANE_CAP_AUTOMATIC;
+ session->options[OPT_BR_Y].descriptor.unit = SANE_UNIT_MM;
+ session->options[OPT_BR_Y].descriptor.constraint_type =
+ SANE_CONSTRAINT_RANGE;
+ session->options[OPT_BR_Y].descriptor.constraint.range = &y_range;
+ session->options[OPT_BR_Y].value.w = y_range.max;
+
+ /* sensor group */
+ session->options[OPT_SENSOR_GROUP].descriptor.name = SANE_NAME_SENSORS;
+ session->options[OPT_SENSOR_GROUP].descriptor.title = SANE_TITLE_SENSORS;
+ session->options[OPT_SENSOR_GROUP].descriptor.desc = SANE_DESC_SENSORS;
+ session->options[OPT_SENSOR_GROUP].descriptor.type = SANE_TYPE_GROUP;
+ session->options[OPT_SENSOR_GROUP].descriptor.constraint_type =
+ SANE_CONSTRAINT_NONE;
+
+ /* page loaded sensor */
+ session->options[OPT_PAGE_LOADED_SW].descriptor.name =
+ SANE_NAME_PAGE_LOADED;
+ session->options[OPT_PAGE_LOADED_SW].descriptor.title =
+ SANE_TITLE_PAGE_LOADED;
+ session->options[OPT_PAGE_LOADED_SW].descriptor.desc =
+ SANE_DESC_PAGE_LOADED;
+ session->options[OPT_PAGE_LOADED_SW].descriptor.type = SANE_TYPE_BOOL;
+ session->options[OPT_PAGE_LOADED_SW].descriptor.unit = SANE_UNIT_NONE;
+ session->options[OPT_PAGE_LOADED_SW].descriptor.cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ session->options[OPT_PAGE_LOADED_SW].value.b = 0;
+
+ /* calibration needed */
+ session->options[OPT_NEED_CALIBRATION_SW].descriptor.name =
+ "need-calibration";
+ session->options[OPT_NEED_CALIBRATION_SW].descriptor.title =
+ SANE_I18N ("Need calibration");
+ session->options[OPT_NEED_CALIBRATION_SW].descriptor.desc =
+ SANE_I18N ("The scanner needs calibration for the current settings");
+ session->options[OPT_NEED_CALIBRATION_SW].descriptor.type = SANE_TYPE_BOOL;
+ session->options[OPT_NEED_CALIBRATION_SW].descriptor.unit = SANE_UNIT_NONE;
+ session->options[OPT_NEED_CALIBRATION_SW].descriptor.cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ session->options[OPT_NEED_CALIBRATION_SW].value.b = 0;
+
+ /* button group */
+ session->options[OPT_BUTTON_GROUP].descriptor.name = "Buttons";
+ session->options[OPT_BUTTON_GROUP].descriptor.title = SANE_I18N ("Buttons");
+ session->options[OPT_BUTTON_GROUP].descriptor.desc = SANE_I18N ("Buttons");
+ session->options[OPT_BUTTON_GROUP].descriptor.type = SANE_TYPE_GROUP;
+ session->options[OPT_BUTTON_GROUP].descriptor.constraint_type =
+ SANE_CONSTRAINT_NONE;
+
+ /* calibrate button */
+ session->options[OPT_CALIBRATE].descriptor.name = "calibrate";
+ session->options[OPT_CALIBRATE].descriptor.title = SANE_I18N ("Calibrate");
+ session->options[OPT_CALIBRATE].descriptor.desc =
+ SANE_I18N ("Start calibration using special sheet");
+ session->options[OPT_CALIBRATE].descriptor.type = SANE_TYPE_BUTTON;
+ session->options[OPT_CALIBRATE].descriptor.unit = SANE_UNIT_NONE;
+ session->options[OPT_CALIBRATE].descriptor.cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
+ SANE_CAP_AUTOMATIC;
+ session->options[OPT_CALIBRATE].value.b = 0;
+
+ /* clear calibration cache button */
+ session->options[OPT_CLEAR_CALIBRATION].descriptor.name = "clear";
+ session->options[OPT_CLEAR_CALIBRATION].descriptor.title =
+ SANE_I18N ("Clear calibration");
+ session->options[OPT_CLEAR_CALIBRATION].descriptor.desc =
+ SANE_I18N ("Clear calibration cache");
+ session->options[OPT_CLEAR_CALIBRATION].descriptor.type = SANE_TYPE_BUTTON;
+ session->options[OPT_CLEAR_CALIBRATION].descriptor.unit = SANE_UNIT_NONE;
+ session->options[OPT_CLEAR_CALIBRATION].descriptor.cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
+ SANE_CAP_AUTOMATIC;
+ session->options[OPT_CLEAR_CALIBRATION].value.b = 0;
+
+ /* until work on calibration isfinished */
+ DISABLE (OPT_CALIBRATE);
+ DISABLE (OPT_CLEAR_CALIBRATION);
+
+ DBG (DBG_proc, "init_options: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief physical probe of a device
+ * This function probes for a scanning device using the given name. If the
+ * device is managed, a model structure describing the device will be returned.
+ * @param devicename low level device to access to probe hardware
+ * @return NULL is the device is unsupported, or a model struct describing the
+ * device.
+ */
+P5_Model *
+probe (const char *devicename)
+{
+ int fd;
+
+ /* open parallel port device */
+ fd = open_pp (devicename);
+ if (fd < 0)
+ {
+ DBG (DBG_error, "probe: failed to open '%s' device!\n", devicename);
+ return NULL;
+ }
+
+ /* now try to connect to scanner */
+ if (connect (fd) != SANE_TRUE)
+ {
+ DBG (DBG_error, "probe: failed to connect!\n");
+ close_pp (fd);
+ return NULL;
+ }
+
+ /* set up for memory test */
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REG7, 0x00);
+ write_reg (fd, REG0, 0x00);
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REGF, 0x80);
+ if (memtest (fd, 0x0100) != SANE_TRUE)
+ {
+ disconnect (fd);
+ close_pp (fd);
+ DBG (DBG_error, "probe: memory test failed!\n");
+ return NULL;
+ }
+ else
+ {
+ DBG (DBG_info, "memtest() OK...\n");
+ }
+ write_reg (fd, REG7, 0x00);
+
+ /* check for document presence 0xC6: present, 0xC3 no document */
+ test_document (fd);
+
+ /* release device nd parport for next uses */
+ disconnect (fd);
+ close_pp (fd);
+
+ /* for there is only one supported model, so we use hardcoded values */
+ DBG (DBG_proc, "probe: exit\n");
+ return &pagepartner_model;
+}
+
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/p5.conf.in b/backend/p5.conf.in
new file mode 100644
index 0000000..9130856
--- /dev/null
+++ b/backend/p5.conf.in
@@ -0,0 +1,10 @@
+# configuration file for the p5 backend.
+
+# configuration option to override detected model name
+#option modelname "Prima PagePartner"
+
+# when the parser find this line, it detects it is not an option,
+# then it calls the attach function with this value.
+# Currently only user mode parallel port support is possible:
+# auto, /dev/paraport* (ppdev device name)
+auto
diff --git a/backend/p5.h b/backend/p5.h
new file mode 100644
index 0000000..10352a3
--- /dev/null
+++ b/backend/p5.h
@@ -0,0 +1,205 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2009-2012 stef.dev@free.fr
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+*/
+
+/** @file p5.h
+ * @brief Declaration of high level structures used by the p5 backend.
+ *
+ * The structures and functions declared here are used to do the deal with
+ * the SANE API.
+ */
+
+
+#ifndef P5_H
+#define P5_H
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+/**< macro to enable an option */
+#define ENABLE(OPTION) session->options[OPTION].descriptor.cap &= ~SANE_CAP_INACTIVE
+
+/**< macro to disable an option */
+#define DISABLE(OPTION) session->options[OPTION].descriptor.cap |= SANE_CAP_INACTIVE
+
+/** macro to test is an option is active */
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+/**< name of the configuration file */
+#define P5_CONFIG_FILE "p5.conf"
+
+/**< macro to define texts that should translated */
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/** color mode names
+ */
+/* @{ */
+#define COLOR_MODE "Color"
+#define GRAY_MODE "Gray"
+#define LINEART_MODE "Lineart"
+/* @} */
+
+#include "p5_device.h"
+
+/**
+ * List of all SANE options available for the frontend. Given a specific
+ * device, some options may be set to inactive when the scanner model is
+ * detected. The default values and the ranges they belong maybe also model
+ * dependent.
+ */
+enum P5_Options
+{
+ OPT_NUM_OPTS = 0, /** first enum which must be zero */
+ /** @name standard options group
+ */
+ /* @{ */
+ OPT_STANDARD_GROUP,
+ OPT_MODE, /** set the mode: color, grey levels or lineart */
+ OPT_PREVIEW, /** set up for preview */
+ OPT_RESOLUTION, /** set scan's resolution */
+ /* @} */
+
+ /** @name geometry group
+ * geometry related options
+ */
+ /* @{ */
+ OPT_GEOMETRY_GROUP, /** group of options defining the position and size of the scanned area */
+ OPT_TL_X, /** top-left x of the scanned area*/
+ OPT_TL_Y, /** top-left y of the scanned area*/
+ OPT_BR_X, /** bottom-right x of the scanned area*/
+ OPT_BR_Y, /** bottom-right y of the scanned area*/
+ /* @} */
+
+ /** @name sensor group
+ * detectors group
+ */
+ /* @{ */
+ OPT_SENSOR_GROUP,
+ OPT_PAGE_LOADED_SW,
+ OPT_NEED_CALIBRATION_SW,
+ /* @} */
+
+ /** @name button group
+ * buttons group
+ */
+ /* @{ */
+ OPT_BUTTON_GROUP,
+ OPT_CALIBRATE,
+ OPT_CLEAR_CALIBRATION,
+ /* @} */
+
+ /** @name option list terminator
+ * must come last so it can be used for array and list size
+ */
+ NUM_OPTIONS
+};
+
+/**
+ * Contains one SANE option description and its value.
+ */
+typedef struct P5_Option
+{
+ SANE_Option_Descriptor descriptor; /** option description */
+ Option_Value value; /** option value */
+} P5_Option;
+
+/**
+ * Frontend session. This struct holds informations usefull for
+ * the functions defined in SANE's standard. Informations closer
+ * to the hardware are in the P5_Device structure. There is
+ * as many session structure than frontends using the backend.
+ */
+typedef struct P5_Session
+{
+ /**
+ * Point to the next session in a linked list
+ */
+ struct P5_Session *next;
+
+ /**
+ * low-level device object used by the session
+ */
+ P5_Device *dev;
+
+ /**
+ * array of possible options and their values for the backend
+ */
+ P5_Option options[NUM_OPTIONS];
+
+ /**
+ * SANE_True if a scan is in progress, ie sane_start has been called.
+ * Stay SANE_True until sane_cancel() is called.
+ */
+ SANE_Bool scanning;
+
+ /** @brief non blocking flag
+ * SANE_TRUE if sane_read are non-blocking, ie returns immediatly if there
+ * is no data available from the scanning device. Modified by sane_set_io_mode()
+ */
+ SANE_Bool non_blocking;
+
+ /**
+ * SANE Parameters describes what the next or current scan will be
+ * according to the current values of the options
+ */
+ SANE_Parameters params;
+
+ /**
+ * bytes to send to frontend for the scan
+ */
+ SANE_Int to_send;
+
+ /**
+ * bytes currently sent to frontend during the scan
+ */
+ SANE_Int sent;
+
+} P5_Session;
+
+
+static SANE_Status probe_p5_devices (void);
+static P5_Model *probe (const char *devicename);
+static SANE_Status config_attach (SANEI_Config * config, const char *devname);
+static SANE_Status attach_p5 (const char *name, SANEI_Config * config);
+static SANE_Status init_options (struct P5_Session *session);
+static SANE_Status compute_parameters (struct P5_Session *session);
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
+
+#endif /* not P5_H */
diff --git a/backend/p5_device.c b/backend/p5_device.c
new file mode 100644
index 0000000..c065ca2
--- /dev/null
+++ b/backend/p5_device.c
@@ -0,0 +1,1611 @@
+/**
+ * Description of the Primax PagePartner model
+ */
+static P5_Model pagepartner_model = {
+ "Primax PagePartner",
+ "Primax",
+ "PagePartner",
+ SANE_I18N ("sheetfed scanner"),
+
+ {300, 200, 150, 100, 0},
+ /* 500 seems also possible */
+ {600, 400, 300, 200, 150, 100, 0},
+
+ 300,
+ 600,
+ 100,
+ 100,
+ 16,
+
+ SANE_FIX (0.0),
+ SANE_FIX (0.0),
+ SANE_FIX (215.9),
+ SANE_FIX (300.0),
+};
+
+#ifdef HAVE_LINUX_PPDEV_H
+static char *
+addr_name (uint16_t addr)
+{
+ switch (addr)
+ {
+ case DATA:
+ return "DATA";
+ break;
+ case STATUS:
+ return "STATUS";
+ break;
+ case CONTROL:
+ return "CONTROL";
+ break;
+ case EPPADR:
+ return "EPPADR";
+ break;
+ case EPPDATA:
+ return "EPPDATA";
+ break;
+ default:
+ return "*ERROR*";
+ }
+}
+#endif
+
+/** @brief low level hardware access functions
+ * @{
+ */
+
+static uint8_t
+inb (int fd, uint16_t addr)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ uint8_t val = 0xff;
+ int rc, mode = 0xff;
+
+ switch (addr)
+ {
+ case DATA:
+ rc = ioctl (fd, PPRDATA, &val);
+ break;
+ case STATUS:
+ rc = ioctl (fd, PPRSTATUS, &val);
+ break;
+ case CONTROL:
+ rc = ioctl (fd, PPRCONTROL, &val);
+ break;
+ case EPPDATA:
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+#endif
+ rc = read (fd, &val, 1);
+ break;
+ default:
+ DBG (DBG_error, "inb(%s) escaped ppdev\n", addr_name (addr));
+ return 0xFF;
+ }
+ if (rc < 0)
+ {
+ DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
+ }
+ return val;
+#else
+ if(fd && addr)
+ return 0;
+ return 0;
+#endif
+}
+
+static void
+outb (int fd, uint16_t addr, uint8_t value)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int rc = 0, mode = 0xff;
+
+ switch (addr)
+ {
+ case DATA:
+ rc = ioctl (fd, PPWDATA, &value);
+ break;
+ case CONTROL:
+ mode = value & 0x20;
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (!rc)
+ {
+ value = value & 0xDF;
+ rc = ioctl (fd, PPWCONTROL, &value);
+ }
+ break;
+ case EPPDATA:
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &value, 1);
+ break;
+ case EPPADR:
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &value, 1);
+ break;
+ default:
+ DBG (DBG_error, "outb(%s,0x%02x) escaped ppdev\n", addr_name (addr),
+ value);
+ break;
+ }
+ if (rc < 0)
+ {
+ DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
+ }
+#else
+ if(fd && addr && value)
+ return;
+#endif /* HAVE_LINUX_PPDEV_H */
+}
+
+static void
+write_reg (int fd, uint8_t index, uint8_t value)
+{
+ uint8_t idx;
+
+ /* both nibbles hold the same value */
+ idx = index & 0x0F;
+ DBG (DBG_io2, "write_reg(REG%X,0x%x)\n", idx, value);
+ idx = idx << 4 | idx;
+ outb (fd, EPPADR, idx);
+ outb (fd, EPPDATA, value);
+}
+
+static uint8_t
+read_reg (int fd, uint8_t index)
+{
+ uint8_t idx;
+
+ /* both nibbles hold the same value */
+ idx = index & 0x0F;
+ idx = idx << 4 | idx;
+ outb (fd, EPPADR, idx);
+ return inb (fd, EPPDATA);
+}
+
+#ifdef HAVE_LINUX_PPDEV_H
+static int
+read_data (int fd, uint8_t * data, int length)
+{
+ int mode, rc, nb;
+ unsigned char bval;
+
+ bval = REG8;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ nb = 0;
+ while (nb < length)
+ {
+ rc = read (fd, data + nb, length - nb);
+ if (rc < 0)
+ {
+ DBG (DBG_error, "memtest: error reading data back!\n");
+ return 0;
+ }
+ else
+ {
+ nb += rc;
+ }
+ }
+
+ return 1;
+}
+
+static void
+index_write_data (int fd, uint8_t index, uint8_t * data, int length)
+{
+ int mode, rc;
+ unsigned char bval;
+
+ bval = index;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &bval, 1);
+
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ rc = write (fd, data, length);
+ return;
+}
+
+static void
+write_data (int fd, uint8_t * data, int length)
+{
+ index_write_data (fd, REG8, data, length);
+}
+
+static void
+write_reg2 (int fd, uint8_t index, uint16_t value)
+{
+ uint8_t data2[2];
+
+ data2[0] = value & 0xff;
+ data2[1] = value >> 8;
+ index_write_data (fd, index, data2, 2);
+}
+#else
+
+static int
+read_data (int fd, uint8_t * data, int length)
+{
+ if(fd && data && length)
+ return -1;
+ return -1;
+}
+
+static void
+write_data (int fd, uint8_t * data, int length)
+{
+ if(fd && data && length)
+ return;
+}
+
+static void
+write_reg2 (int fd, uint8_t index, uint16_t value)
+{
+ if(fd && index && value)
+ return;
+}
+#endif
+
+/**
+ * @}
+ */
+
+
+/** @brief This function checks a memory buffer.
+ * This function writes at the given memory address then read it back
+ * to check the scanner is correctly working.
+ * @param fd file descriptor used to access hardware
+ * @param addr address where to write and read
+ * @return SANE_TRUE on succes, SANE_FALSE otherwise
+ */
+static int
+memtest (int fd, uint16_t addr)
+{
+ uint8_t sent[256];
+ uint8_t back[256];
+ int i;
+
+ write_reg2 (fd, REG1, addr);
+ for (i = 0; i < 256; i++)
+ {
+ sent[i] = (uint8_t) i;
+ back[i] = 0;
+ }
+ write_data (fd, sent, 256);
+ read_data (fd, back, 256);
+
+ /* check if data read back is the same that the one sent */
+ for (i = 0; i < 256; i++)
+ {
+ if (back[i] != sent[i])
+ {
+ return SANE_FALSE;
+ }
+ }
+
+ return SANE_TRUE;
+}
+
+
+#define INB(k,y,z) val=inb(k,y); if(val!=z) { DBG(DBG_error,"expected 0x%02x, got 0x%02x\n",z, val); return SANE_FALSE; }
+
+/** @brief connect to scanner
+ * This function sends the connect sequence for the scanner.
+ * @param fd filedescriptor of the parallel port communication channel
+ * @return SANE_TRUE in case of success, SANE_FALSE otherwise
+ */
+static int
+connect (int fd)
+{
+ uint8_t val;
+
+ inb (fd, CONTROL);
+ outb (fd, CONTROL, 0x04);
+ outb (fd, DATA, 0x02);
+ INB (fd, DATA, 0x02);
+ outb (fd, DATA, 0x03);
+ INB (fd, DATA, 0x03);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0xFF);
+ DBG (DBG_info, "connect() OK...\n");
+ return SANE_TRUE;
+}
+
+static int
+disconnect (int fd)
+{
+ uint8_t val;
+
+ outb (fd, CONTROL, 0x04);
+ outb (fd, DATA, 0x00);
+ INB (fd, DATA, 0x00);
+ outb (fd, DATA, 0x01);
+ INB (fd, DATA, 0x01);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ INB (fd, DATA, 0x81);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x81);
+ INB (fd, DATA, 0x81);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ INB (fd, DATA, 0x81);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ inb (fd, CONTROL);
+ outb (fd, CONTROL, 0x0C);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+setadresses (int fd, uint16_t start, uint16_t end)
+{
+ write_reg (fd, REG3, start & 0xff);
+ write_reg (fd, REG4, start >> 8);
+ write_reg (fd, REG5, end & 0xff);
+ write_reg (fd, REG6, end >> 8);
+ DBG (DBG_io, "setadresses(0x%x,0x%x); OK...\n", start, end);
+}
+
+#ifdef HAVE_LINUX_PPDEV_H
+/** @brief open parallel port device
+ * opens parallel port's low level device in EPP mode
+ * @param devicename nam of the real device or the special value 'auto'
+ * @return file descriptor in cas of successn -1 otherwise
+ */
+static int
+open_pp (const char *devicename)
+{
+ int fd, rc, mode = 0;
+ char *name;
+
+ DBG (DBG_proc, "open_pp: start, devicename=%s\n", devicename);
+ /* TODO improve auto device finding */
+ if (strncmp (devicename, "auto", 4) == 0)
+ {
+ name = strdup("/dev/parport0");
+ }
+ else
+ {
+ name = strdup(devicename);
+ }
+
+ /* open device */
+ fd = open (name, O_RDWR);
+ if (fd < 0)
+ {
+ switch (errno)
+ {
+ case ENOENT:
+#ifdef ENIO
+ case ENXIO:
+#endif
+#ifdef ENODEV
+ case ENODEV:
+#endif
+ DBG (DBG_error, "open_pp: no %s device ...\n", name);
+ break;
+ case EACCES:
+ DBG (DBG_error,
+ "open_pp: current user cannot use existing %s device ...\n",
+ name);
+ break;
+ default:
+ DBG (DBG_error, "open_pp: %s while opening %s\n", strerror (errno),
+ name);
+ }
+ return -1;
+ }
+ free(name);
+
+ /* claim device and set it to EPP */
+ rc = ioctl (fd, PPCLAIM);
+ rc = ioctl (fd, PPGETMODES, &mode);
+ if (mode & PARPORT_MODE_PCSPP)
+ DBG (DBG_io, "PARPORT_MODE_PCSPP\n");
+ if (mode & PARPORT_MODE_TRISTATE)
+ DBG (DBG_io, "PARPORT_MODE_TRISTATE\n");
+ if (mode & PARPORT_MODE_EPP)
+ DBG (DBG_io, "PARPORT_MODE_EPP\n");
+ if (mode & PARPORT_MODE_ECP)
+ DBG (DBG_io, "PARPORT_MODE_ECP\n");
+ if (mode & PARPORT_MODE_COMPAT)
+ DBG (DBG_io, "PARPORT_MODE_COMPAT\n");
+ if (mode & PARPORT_MODE_DMA)
+ DBG (DBG_io, "PARPORT_MODE_DMA\n");
+ if (mode & PARPORT_MODE_EPP)
+ {
+ mode = IEEE1284_MODE_EPP;
+ }
+ else
+ {
+ /*
+ if (mode & PARPORT_MODE_ECP)
+ {
+ mode = IEEE1284_MODE_ECP;
+ }
+ else
+ */
+ {
+ mode = -1;
+ }
+ }
+ if (mode == -1)
+ {
+ DBG (DBG_error, "open_pp: no EPP mode, giving up ...\n");
+ rc = ioctl (fd, PPRELEASE);
+ close (fd);
+ return -1;
+ }
+ rc = ioctl (fd, PPNEGOT, &mode);
+ rc = ioctl (fd, PPSETMODE, &mode);
+ DBG (DBG_proc, "open_pp: exit\n");
+ return fd;
+}
+
+/** close low level device
+ * release and close low level hardware device
+ */
+static void
+close_pp (int fd)
+{
+ int mode = IEEE1284_MODE_COMPAT;
+
+ if (fd > 2)
+ {
+ ioctl (fd, PPNEGOT, &mode);
+ ioctl (fd, PPRELEASE);
+ close (fd);
+ }
+}
+
+#else /* HAVE_LINUX_PPDEV_H */
+
+static int
+open_pp (const char *devicename)
+{
+ if(devicename)
+ return -1;
+ return -1;
+}
+
+static void
+close_pp (int fd)
+{
+ if(fd)
+ return;
+}
+#endif /* HAVE_LINUX_PPDEV_H */
+
+/** @brief test if a document is inserted
+ * Test if a document is inserted by reading register E
+ * @param fd file descriptor to access scanner
+ * @return SANE_STATUS_NO_DOCS if no document or SANE_STATUS_GOOD
+ * if something is present.
+ */
+static SANE_Status
+test_document (int fd)
+{
+ int detector;
+
+ /* check for document presence 0xC6: present, 0xC3 no document */
+ detector = read_reg (fd, REGE);
+ DBG (DBG_io, "test_document: detector=0x%02X\n", detector);
+
+ /* document inserted */
+ if (detector & 0x04)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+/**
+ * return the amount of scanned data available
+ * @param fd file descriptor to access scanner
+ * @return avaible byte number
+ */
+static int
+available_bytes (int fd)
+{
+ int counter;
+
+ /* read the number of 256 bytes block of scanned data */
+ counter = read_reg (fd, REG9);
+ DBG (DBG_io, "available_bytes: available_bytes=0x%02X\n", counter);
+ return 256 * counter;
+}
+
+static SANE_Status
+build_correction (P5_Device * dev, unsigned int dpi, unsigned int mode,
+ unsigned int start, unsigned int width)
+{
+ unsigned int i, j, shift, step;
+
+ DBG (DBG_proc, "build_correction: start=%d, width=%d\n", start, width);
+ DBG (DBG_trace, "build_correction: dpi=%d, mode=%d\n", dpi, mode);
+
+ /* loop on calibration data to find the matching one */
+ j = 0;
+ while (dev->calibration_data[j]->dpi != dpi)
+ {
+ j++;
+ if (j > MAX_RESOLUTIONS)
+ {
+ DBG (DBG_error, "build_correction: couldn't find calibration!\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (dev->gain != NULL)
+ {
+ free (dev->gain);
+ dev->gain = NULL;
+ }
+ if (dev->offset != NULL)
+ {
+ free (dev->offset);
+ dev->offset = NULL;
+ }
+ dev->gain = (float *) malloc (width * sizeof (float));
+ if (dev->gain == NULL)
+ {
+ DBG (DBG_error,
+ "build_correction: failed to allocate memory for gain!\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->offset = (uint8_t *) malloc (width);
+ if (dev->offset == NULL)
+ {
+ DBG (DBG_error,
+ "build_correction: failed to allocate memory for offset!\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* compute starting point of calibration data to use */
+ shift = start;
+ step = 1;
+ if (mode == MODE_GRAY)
+ {
+ /* we use green data */
+ shift += 1;
+ step = 3;
+ }
+ for (i = 0; i < width; i += step)
+ {
+ if (dev->calibration_data[j]->white_data[shift + i] -
+ dev->calibration_data[0]->black_data[shift + i] > BLACK_LEVEL)
+ {
+ dev->gain[i] =
+ WHITE_TARGET /
+ ((float)
+ (dev->calibration_data[j]->white_data[shift + i] -
+ dev->calibration_data[j]->black_data[shift + i]));
+ dev->offset[i] = dev->calibration_data[j]->black_data[shift + i];
+ }
+ else
+ {
+ dev->gain[i] = 1.0;
+ dev->offset[i] = 0;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ DBG (DBG_proc, "build_correction: end\n");
+}
+
+/** @brief start up a real scan
+ * This function starts the scan with the given parameters.
+ * @param dev device describing hardware
+ * @param mode color, gray level or lineart.
+ * @param dpi desired scan resolution.
+ * @param startx coordinate of the first pixel to scan in
+ * scan's resolution coordinate
+ * @param width width of the scanned area
+ * scanner's physical scan aread.
+ * @return SANE_STATUS_GOOD if scan is successfully started
+ */
+static SANE_Status
+start_scan (P5_Device * dev, int mode, unsigned int dpi, unsigned int startx,
+ unsigned int width)
+{
+ uint8_t reg0=0;
+ uint8_t reg2=0;
+ uint8_t regF=0;
+ uint16_t addr=0;
+ uint16_t start, end;
+ unsigned int xdpi;
+
+ DBG (DBG_proc, "start_scan: start \n");
+ DBG (DBG_io, "start_scan: startx=%d, width=%d, dpi=%d\n", startx, width,
+ dpi);
+
+ /** @brief register values
+ * - reg2 : reg2 seems related to x dpi and provides only 100,
+ * 150, 200 and 300 resolutions.
+ * - regF : lower nibble gives y dpi resolution ranging from 150
+ * to 1200 dpi.
+ */
+ xdpi = dpi;
+ switch (dpi)
+ {
+ case 100:
+ reg2 = 0x90;
+ regF = 0xA2;
+ break;
+ case 150:
+ reg2 = 0x10;
+ regF = 0xA4;
+ break;
+ case 200:
+ reg2 = 0x80;
+ regF = 0xA6;
+ break;
+ case 300:
+ reg2 = 0x00;
+ regF = 0xA8;
+ break;
+ case 400:
+ reg2 = 0x80; /* xdpi=200 */
+ regF = 0xAA;
+ xdpi = 200;
+ break;
+ case 500:
+ reg2 = 0x00;
+ regF = 0xAC;
+ xdpi = 300;
+ break;
+ case 600:
+ reg2 = 0x00;
+ regF = 0xAE;
+ xdpi = 300;
+ break;
+ }
+
+ switch (mode)
+ {
+ case MODE_COLOR:
+ reg0 = 0x00;
+ addr = 0x0100;
+ break;
+ case MODE_GRAY:
+ /* green channel only */
+ reg0 = 0x20;
+ addr = 0x0100;
+ break;
+ case MODE_LINEART:
+ reg0 = 0x40;
+ addr = 0x0908;
+ break;
+ }
+
+ write_reg (dev->fd, REG1, 0x01);
+ write_reg (dev->fd, REG7, 0x00);
+ write_reg (dev->fd, REG0, reg0);
+ write_reg (dev->fd, REG1, 0x00);
+ write_reg (dev->fd, REGF, regF);
+ /* the memory addr used to test need not to be related
+ * to resolution, 0x0100 could be always used */
+ /* TODO get rid of it */
+ memtest (dev->fd, addr);
+
+ /* handle case where dpi>xdpi */
+ start = startx;
+ if (dpi > xdpi)
+ {
+ width = (width * xdpi) / dpi;
+ start = (startx * xdpi) / dpi;
+ }
+
+ /* compute and set start addr */
+ if (mode == MODE_COLOR)
+ {
+ start = start * 3;
+ width = width * 3;
+ }
+ end = start + width + 1;
+
+ /* build calibration data for the scan */
+ if (dev->calibrated)
+ {
+ build_correction (dev, xdpi, mode, start, width);
+ }
+
+ setadresses (dev->fd, start, end);
+
+ write_reg (dev->fd, REG1, addr >> 8);
+ write_reg (dev->fd, REG2, reg2);
+ regF = (regF & 0x0F) | 0x80;
+ write_reg (dev->fd, REGF, regF);
+ write_reg (dev->fd, REG0, reg0);
+ if (mode == MODE_LINEART)
+ {
+ write_reg (dev->fd, 0x07, 0x04);
+ }
+ else
+ {
+ write_reg (dev->fd, 0x07, 0x00);
+ }
+ write_reg (dev->fd, REG1, addr >> 8);
+ write_reg2 (dev->fd, REG1, addr);
+ write_reg (dev->fd, REGF, regF | 0x01);
+ write_reg (dev->fd, REG0, reg0 | 0x0C);
+ if (mode == MODE_LINEART)
+ {
+ write_reg (dev->fd, REG1, 0x19);
+ }
+ else
+ {
+ write_reg (dev->fd, REG1, 0x11);
+ }
+ DBG (DBG_proc, "start_scan: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/** read a line of scan data
+ * @param dev device to read
+ * @param data pointer where to store data
+ * @param length total bytes to read on one line
+ * @param ltr total number of lines to read
+ * @param retry signals that the function must read as much lines it can
+ * @param x2 tells that lines must be enlarged by a 2 factor
+ * @param mode COLOR_MODE if color mode
+ * @returns number of data lines read, -1 in case of error
+ */
+static int
+read_line (P5_Device * dev, uint8_t * data, size_t length, int ltr,
+ SANE_Bool retry, SANE_Bool x2, int mode, SANE_Bool correction)
+{
+ uint8_t counter, read, cnt;
+ uint8_t inbuffer[MAX_SENSOR_PIXELS * 2 * 3 + 2];
+ unsigned int i, factor;
+ float val;
+
+ DBG (DBG_proc, "read_line: trying to read %d lines of %lu bytes\n", ltr,
+ (unsigned long)length);
+
+ counter = read_reg (dev->fd, REG9);
+ DBG (DBG_io, "read_line: %d bytes available\n", counter * 256);
+ read = 0;
+ if (x2 == SANE_FALSE)
+ {
+ factor = 1;
+ }
+ else
+ {
+ factor = 2;
+ }
+
+ /* in retry mode we read until not enough data, but in no retry
+ * read only one line , counter give us 256 bytes block available
+ * and we want an number multiple of color channels */
+ cnt = (255 + length / factor) / 256;
+ while ((counter > cnt && retry == 1) || (counter > cnt && read == 0))
+ {
+ /* read data from scanner, first and last byte aren't picture data */
+ read_data (dev->fd, inbuffer, length / factor + 2);
+
+ /* image correction */
+ if (correction == SANE_TRUE)
+ {
+ for (i = 0; i < length / factor; i++)
+ {
+ val = inbuffer[i + 1] - dev->offset[i];
+ if (val > 0)
+ {
+ val = val * dev->gain[i];
+ if (val < 255)
+ inbuffer[i + 1] = val;
+ else
+ inbuffer[i + 1] = 255;
+ }
+ else
+ {
+ inbuffer[i + 1] = 0;
+ }
+ }
+ }
+
+ /* handle horizontal data doubling */
+ if (x2 == SANE_FALSE)
+ {
+ memcpy (data + read * length, inbuffer + 1, length);
+ }
+ else
+ {
+ if (mode == MODE_COLOR)
+ {
+ for (i = 0; i < length / factor; i += 3)
+ {
+ data[read * length + i * factor] = inbuffer[i + 1];
+ data[read * length + i * factor + 1] = inbuffer[i + 2];
+ data[read * length + i * factor + 2] = inbuffer[i + 3];
+ data[read * length + i * factor + 3] = inbuffer[i + 1];
+ data[read * length + i * factor + 4] = inbuffer[i + 2];
+ data[read * length + i * factor + 5] = inbuffer[i + 3];
+ }
+ }
+ else
+ {
+ for (i = 0; i < length / factor; i++)
+ {
+ data[read * length + i * factor] = inbuffer[i + 1];
+ data[read * length + i * factor + 1] = inbuffer[i + 1];
+ }
+ }
+ }
+ read++;
+ if (retry == SANE_TRUE)
+ {
+ read_reg (dev->fd, REGF);
+ read_reg (dev->fd, REGA);
+ read_reg (dev->fd, REG9);
+ counter = read_reg (dev->fd, REG9);
+ read_reg (dev->fd, REGA);
+ if (read >= ltr)
+ {
+ DBG (DBG_io, "read_line returning %d lines\n", read);
+ return read;
+ }
+ counter = read_reg (dev->fd, REG9);
+ }
+ }
+ read_reg (dev->fd, REGF);
+ read_reg (dev->fd, REGA);
+ read_reg (dev->fd, REG9);
+ counter = read_reg (dev->fd, REG9);
+ read_reg (dev->fd, REGA);
+ DBG (DBG_io, "read_line returning %d lines\n", read);
+ return read;
+}
+
+
+static SANE_Status
+eject (int fd)
+{
+ int detector;
+
+ DBG (DBG_proc, "eject: start ...\n");
+
+ do
+ {
+ write_reg2 (fd, REG1, 0x1110);
+ detector = read_reg (fd, REGE);
+ detector = read_reg (fd, REGE);
+ }
+ while ((detector & 0x04) != 0);
+ write_reg (fd, REG0, 0x00);
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REGF, 0x82);
+ write_reg (fd, REG7, 0x00);
+
+ DBG (DBG_proc, "eject: end.\n");
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief wait for document to be present in feeder
+ * Polls document sensor until something is present. Give up after 20 seconds
+ * @param fd file descriptor of the physical device
+ */
+/* static int
+wait_document (int fd, uint8_t detector)
+{
+ int count = 0;
+ uint8_t val;
+
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REG7, 0x00);
+ detector = read_reg (fd, REGE);
+ while (detector == 0xc3 && count < 20)
+ {
+ sleep (1);
+ count++;
+ detector = read_reg (fd, REGE);
+ }
+ setadresses (fd, 0x002d, 0x09c7);
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REG2, 0x90);
+ write_reg (fd, REGF, 0x82);
+ write_reg (fd, REG0, 0x00);
+ val = inb (fd, STATUS) & 0xf8;
+ if (val != 0xf8)
+ {
+ DBG (DBG_error, "wait_document: unexpected STATUS value 0x%02x instead of 0xf8", val);
+ }
+ if (count >= 20)
+ {
+ DBG (DBG_error, "wait_document: failed to detect document!\n");
+ return 0;
+ }
+ return 1;
+} */
+
+/** @brief move at 150 dpi
+ * move the paper at 150 dpi motor speed by the amount specified
+ * @params dev pointer to the device structure
+ */
+static SANE_Status
+move (P5_Device * dev)
+{
+ int skip, done, read, count;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char buffer[256];
+
+ DBG (DBG_proc, "move: start\n");
+
+ /* compute number of lines to skip */
+ skip = dev->ystart;
+
+ /* works, but remains to be explained ... */
+ if (dev->ydpi > 300)
+ skip = skip / 2;
+
+ DBG (DBG_io, "move: skipping %d lines at %d dpi\n", skip, dev->ydpi);
+
+ /* we do a real scan of small width, discarding data */
+ done = 0;
+ status = start_scan (dev, MODE_GRAY, dev->ydpi, 0, 256);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "move: failed to start scan\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ do
+ {
+ /* test if document left the feeder */
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS)
+ {
+ DBG (DBG_info,
+ "move: document was shorter than the required move\n");
+ return SANE_STATUS_INVAL;
+ }
+ /* test if data is available */
+ count = available_bytes (dev->fd);
+ if (count)
+ {
+ read =
+ read_line (dev, buffer, 256, 1, SANE_FALSE, SANE_FALSE,
+ MODE_GRAY, SANE_FALSE);
+ if (read == -1)
+ {
+ DBG (DBG_error, "move: failed to read data\n");
+ return SANE_STATUS_INVAL;
+ }
+ done += read;
+ }
+ }
+ while (done < skip);
+
+ /* reset scanner */
+ write_reg2 (dev->fd, REG1, 0x1110);
+ count = read_reg (dev->fd, REGE);
+ count = read_reg (dev->fd, REGE);
+ write_reg (dev->fd, REG0, 0x00);
+ write_reg (dev->fd, REG1, 0x00);
+ write_reg (dev->fd, REGF, 0x82);
+ write_reg (dev->fd, REG7, 0x00);
+
+ DBG (DBG_proc, "move: exit\n");
+ return status;
+}
+
+/** clean up calibration data
+ * @param dev device to clean up
+ */
+static void
+cleanup_calibration (P5_Device * dev)
+{
+ int i;
+
+ for (i = 0; i < MAX_RESOLUTIONS * 2; i++)
+ {
+ if (dev->calibration_data[i] != NULL)
+ {
+ free (dev->calibration_data[i]);
+ dev->calibration_data[i] = NULL;
+ }
+ }
+ dev->calibrated = SANE_FALSE;
+}
+
+/** detect a black scan line
+ * parses the given buffer and retrun SANE_TRUE if the line is an
+ * acceptable black line for calibration
+ * @param buffer data line to parse
+ * @param pixels number of pixels
+ * @param mode MODE_COLOR or MODE_GRAY
+ * @returns SANE_TRUE if it is considered as a white line
+ */
+static SANE_Bool
+is_black_line (uint8_t * buffer, unsigned int pixels, int mode)
+{
+ unsigned int i, start, end, count, width;
+
+ /* compute width in bytes */
+ if (mode == MODE_COLOR)
+ {
+ width = pixels * 3;
+ }
+ else
+ {
+ width = pixels;
+ }
+
+ /* we allow the calibration target to be narrower than full width, ie
+ * black margin at both ends of the line */
+ start = (5 * width) / 100;
+ end = (95 * width) / 100;
+ count = 0;
+
+ /* count number of black bytes */
+ for (i = start; i < end; i++)
+ {
+ if (buffer[i] > BLACK_LEVEL)
+ {
+ count++;
+ }
+ }
+
+ /* we allow 3% black pixels maximum */
+ if (count > (3 * width) / 100)
+ {
+ DBG (DBG_io, "is_black_line=SANE_FALSE\n");
+ return SANE_FALSE;
+ }
+
+ DBG (DBG_io, "is_black_line=SANE_TRUE\n");
+ return SANE_TRUE;
+}
+
+/** detect a white scan line
+ * parses the given buffer and retrun SANE_TRUE if the line is an
+ * acceptable white line for calibration
+ * @param buffer data line to parse
+ * @param pixels number of pixels
+ * @param mode MODE_COLOR or MODE_GRAY
+ * @returns SANE_TRUE if it is considered as a white line
+ */
+static SANE_Bool
+is_white_line (uint8_t * buffer, unsigned int pixels, int mode)
+{
+ unsigned int i, start, end, count, width;
+
+ /* compute width in bytes */
+ if (mode == MODE_COLOR)
+ {
+ width = pixels * 3;
+ }
+ else
+ {
+ width = pixels;
+ }
+
+ /* we allow the calibration target to be narrower than full width, ie
+ * black margin at both ends of the line */
+ start = (5 * width) / 100;
+ end = (95 * width) / 100;
+ count = 0;
+
+ /* count number of black bytes */
+ for (i = start; i < end; i++)
+ {
+ if (buffer[i] < BLACK_LEVEL)
+ {
+ count++;
+ }
+ }
+
+ /* we allow 3% black pixels maximum */
+ if (count > (3 * width) / 100)
+ {
+ DBG (DBG_io, "is_white_line=SANE_FALSE\n");
+ return SANE_FALSE;
+ }
+
+ DBG (DBG_io, "is_white_line=SANE_TRUE\n");
+ return SANE_TRUE;
+}
+
+/* ------------------------------------------------------------------------- */
+/* writes gray data to a pnm file */
+/*
+static void
+write_gray_data (unsigned char *image, char *name, SANE_Int width,
+ SANE_Int height)
+{
+ FILE *fdbg = NULL;
+
+ fdbg = fopen (name, "wb");
+ if (fdbg == NULL)
+ return;
+ fprintf (fdbg, "P5\n%d %d\n255\n", width, height);
+ fwrite (image, width, height, fdbg);
+ fclose (fdbg);
+}
+*/
+
+/* ------------------------------------------------------------------------- */
+/* writes rgb data to a pnm file */
+static void
+write_rgb_data (char *name, unsigned char *image, SANE_Int width,
+ SANE_Int height)
+{
+ FILE *fdbg = NULL;
+
+ fdbg = fopen (name, "wb");
+ if (fdbg == NULL)
+ return;
+ fprintf (fdbg, "P6\n%d %d\n255\n", width, height);
+ fwrite (image, width * 3, height, fdbg);
+ fclose (fdbg);
+}
+
+/** give calibration file name
+ * computes the calibration file name to use based on the
+ * backend name and device
+ */
+static char *
+calibration_file (const char *devicename)
+{
+ char *ptr = NULL;
+ char tmp_str[PATH_MAX];
+
+ ptr = getenv ("HOME");
+ if (ptr != NULL)
+ {
+ sprintf (tmp_str, "%s/.sane/p5-%s.cal", ptr, devicename);
+ }
+ else
+ {
+ ptr = getenv ("TMPDIR");
+ if (ptr != NULL)
+ {
+ sprintf (tmp_str, "%s/p5-%s.cal", ptr, devicename);
+ }
+ else
+ {
+ sprintf (tmp_str, "/tmp/p5-%s.cal", devicename);
+ }
+ }
+ DBG (DBG_trace, "calibration_file: using >%s< for calibration file name\n",
+ tmp_str);
+ return strdup (tmp_str);
+}
+
+/** restore calibration data
+ * restore calibration data by loading previously saved calibration data
+ * @param dev device to restore
+ * @return SANE_STATUS_GOOD on success, otherwise error code
+ */
+static SANE_Status
+restore_calibration (P5_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ char *fname = NULL;
+ FILE *fcalib = NULL;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "restore_calibration: start\n");
+ cleanup_calibration (dev);
+ fname = calibration_file (dev->model->name);
+ fcalib = fopen (fname, "rb");
+ if (fcalib == NULL)
+ {
+ DBG (DBG_error, "restore_calibration: failed to open %s!\n", fname);
+ free (fname);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* loop filling calibration data until EOF reached */
+ i = 0;
+ while (!feof (fcalib) && (i < 2 * MAX_RESOLUTIONS))
+ {
+ dev->calibration_data[i] = malloc (sizeof (P5_Calibration_Data));
+ if (dev->calibration_data[i] == NULL)
+ {
+ cleanup_calibration (dev);
+ free (fname);
+ fclose (fcalib);
+ DBG (DBG_error,
+ "restore_calibration: failed to allocate memory for calibration\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ size =
+ fread (dev->calibration_data[i], 1, sizeof (P5_Calibration_Data),
+ fcalib);
+ if (feof (fcalib))
+ {
+ free (dev->calibration_data[i]);
+ dev->calibration_data[i] = NULL;
+ }
+ else if (size != sizeof (P5_Calibration_Data))
+ {
+ cleanup_calibration (dev);
+ free (fname);
+ fclose (fcalib);
+ DBG (DBG_error, "restore_calibration: failed to read from file\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_trace,
+ "restore_calibration: read 1 calibration structure from file\n");
+ i++;
+ }
+
+ dev->calibrated = SANE_TRUE;
+ fclose (fcalib);
+ free (fname);
+
+ DBG (DBG_proc, "restore_calibration: end\n");
+ return status;
+}
+
+/** save calibration data
+ * save calibration data from memory to file
+ * @param dev device calibration to save
+ * @return SANE_STATUS_GOOD on success, otherwise error code
+ */
+static SANE_Status
+save_calibration (P5_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ char *fname = NULL;
+ FILE *fcalib = NULL;
+ int i;
+ size_t size;
+
+ DBG (DBG_proc, "save_calibration: start\n");
+ fname = calibration_file (dev->model->name);
+ fcalib = fopen (fname, "wb");
+ if (fcalib == NULL)
+ {
+ DBG (DBG_error, "save_calibration: failed to open %s!\n", fname);
+ free (fname);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* loop filling calibration data until EOF reached */
+ i = 0;
+ while (dev->calibration_data[i] != NULL && (i < 2 * MAX_RESOLUTIONS))
+ {
+ size =
+ fwrite (dev->calibration_data[i], sizeof (P5_Calibration_Data), 1,
+ fcalib);
+ if (size != sizeof (P5_Calibration_Data))
+ {
+ free (fname);
+ fclose (fcalib);
+ DBG (DBG_error, "save_calibration: failed to write to file\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_trace,
+ "save_calibration: wrote 1 calibration structure to file\n");
+ i++;
+ }
+
+ fclose (fcalib);
+ free (fname);
+
+ DBG (DBG_proc, "save_calibration: end\n");
+ return status;
+}
+
+/** calibrate scanner
+ * calibrates scanner by scanning a white sheet to get
+ * reference data. The black reference data is extracted from the lines
+ * that precede the physical document.
+ * Calibration is done at 300 color, then data is built for other modes
+ * and resolutions.
+ * @param dev device to calibrate
+ */
+static SANE_Status
+sheetfed_calibration (P5_Device * dev)
+{
+ uint8_t buffer[MAX_SENSOR_PIXELS * 3];
+ uint16_t white_data[MAX_SENSOR_PIXELS * 3];
+ uint16_t black_data[MAX_SENSOR_PIXELS * 3];
+ unsigned int i, j, k, dpi, pixels, read, black, white;
+ float coeff;
+ unsigned int red, green, blue;
+ int line;
+ SANE_Status status;
+ char title[40];
+
+ FILE *dbg = fopen ("debug.pnm", "wb");
+ fprintf (dbg, "P6\n%d %d\n255\n", MAX_SENSOR_PIXELS,
+ CALIBRATION_SKIP_LINES * 4);
+
+ DBG (DBG_proc, "sheetfed_calibration: start\n");
+
+ /* check calibration target has been loaded in ADF */
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS)
+ {
+ DBG (DBG_error,
+ "sheetfed_calibration: no calibration target present!\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ /* clean up calibration data */
+ cleanup_calibration (dev);
+
+ /* a RGB scan to get reference data */
+ /* initialize calibration slot for the resolution */
+ i = 0;
+ dpi = dev->model->max_xdpi;
+ pixels = MAX_SENSOR_PIXELS;
+ dev->calibration_data[i] =
+ (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
+ if (dev->calibration_data[i] == NULL)
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: failed to allocate memory for calibration\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->calibration_data[i]->dpi = dpi;
+
+ /* start scan */
+ status = start_scan (dev, MODE_COLOR, dpi, 0, pixels);
+ if (status != SANE_STATUS_GOOD)
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: failed to start scan at %d dpi\n", dpi);
+ return SANE_STATUS_INVAL;
+ }
+
+ white = 0;
+ black = 0;
+ read = 0;
+ for (j = 0; j < pixels * 3; j++)
+ {
+ black_data[j] = 0;
+ white_data[j] = 0;
+ }
+
+ /* read lines and gather black and white ones until enough for sensor's
+ * native resolution */
+ do
+ {
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS && (white < 10 || black < 10))
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: calibration sheet too short!\n");
+ return SANE_STATUS_INVAL;
+ }
+ memset (buffer, 0x00, MAX_SENSOR_PIXELS * 3);
+ line =
+ read_line (dev, buffer, pixels * 3, 1, SANE_FALSE, SANE_FALSE,
+ MODE_COLOR, SANE_FALSE);
+ if (line == -1)
+ {
+ DBG (DBG_error, "sheetfed_calibration: failed to read data\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* if a data line has been read, add it to reference data */
+ if (line)
+ {
+ read++;
+ fwrite (buffer, pixels * 3, 1, dbg);
+ if (is_white_line (buffer, pixels, MODE_COLOR) && white < 256)
+ {
+ white++;
+ /* first calibration lines are skipped */
+ for (j = 0; j < pixels * 3 && read > CALIBRATION_SKIP_LINES;
+ j++)
+ {
+ white_data[j] += buffer[j];
+ }
+ }
+ if (is_black_line (buffer, pixels, MODE_COLOR) && black < 256)
+ {
+ black++;
+ for (j = 0; j < pixels * 3; j++)
+ {
+ black_data[j] += buffer[j];
+ }
+ }
+ }
+ }
+ while (test_document (dev->fd) != SANE_STATUS_NO_DOCS);
+ DBG (DBG_trace, "sheetfed_calibration: white lines=%d, black lines=%d\n",
+ white, black);
+
+ /* average pixels and store in per dpi calibration data */
+ for (j = 0; j < pixels * 3; j++)
+ {
+ dev->calibration_data[i]->white_data[j] = white_data[j] / white;
+ dev->calibration_data[i]->black_data[j] = black_data[j] / black;
+ }
+
+ /* we average red, green and blue offset on the full sensor */
+ red = 0;
+ green = 0;
+ blue = 0;
+ for (j = 0; j < pixels * 3; j += 3)
+ {
+ red += dev->calibration_data[i]->black_data[j];
+ green += dev->calibration_data[i]->black_data[j + 1];
+ blue += dev->calibration_data[i]->black_data[j + 2];
+ }
+ for (j = 0; j < pixels * 3; j += 3)
+ {
+ dev->calibration_data[i]->black_data[j] = red / pixels;
+ dev->calibration_data[i]->black_data[j + 1] = green / pixels;
+ dev->calibration_data[i]->black_data[j + 2] = blue / pixels;
+ }
+
+ /* trace calibration data for debug */
+ if (DBG_LEVEL > DBG_data)
+ {
+ sprintf (title, "calibration-white-%d.pnm",
+ dev->calibration_data[i]->dpi);
+ write_rgb_data (title, dev->calibration_data[i]->white_data, pixels, 1);
+ sprintf (title, "calibration-black-%d.pnm",
+ dev->calibration_data[i]->dpi);
+ write_rgb_data (title, dev->calibration_data[i]->black_data, pixels, 1);
+ }
+
+ /* loop on all remaining resolution and compute calibration data from it */
+ for (i = 1; i < MAX_RESOLUTIONS && dev->model->xdpi_values[i] > 0; i++)
+ {
+ dev->calibration_data[i] =
+ (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
+ if (dev->calibration_data[i] == NULL)
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: failed to allocate memory for calibration\n");
+ return SANE_STATUS_INVAL;
+ }
+ dev->calibration_data[i]->dpi = dev->model->xdpi_values[i];
+
+ coeff = ((float) dev->model->xdpi_values[i]) / (float) dpi;
+
+ /* generate data by decimation */
+ for (j = 0; j < pixels / coeff; j++)
+ {
+ k = j * coeff;
+ dev->calibration_data[i]->white_data[j] =
+ dev->calibration_data[0]->white_data[k];
+ dev->calibration_data[i]->white_data[j + 1] =
+ dev->calibration_data[0]->white_data[k + 1];
+ dev->calibration_data[i]->white_data[j + 2] =
+ dev->calibration_data[0]->white_data[k + 2];
+ dev->calibration_data[i]->black_data[j] =
+ dev->calibration_data[0]->black_data[k];
+ dev->calibration_data[i]->black_data[j + 1] =
+ dev->calibration_data[0]->black_data[k + 1];
+ dev->calibration_data[i]->black_data[j + 2] =
+ dev->calibration_data[0]->black_data[k + 2];
+ }
+ }
+
+ fclose (dbg);
+ dev->calibrated = SANE_TRUE;
+
+ /* eject calibration target */
+ eject (dev->fd);
+
+ DBG (DBG_proc, "sheetfed_calibration: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/p5_device.h b/backend/p5_device.h
new file mode 100644
index 0000000..4582256
--- /dev/null
+++ b/backend/p5_device.h
@@ -0,0 +1,304 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2009-2012 stef.dev@free.fr
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+*/
+
+/** @file p5_device.h
+ * @brief Declaration of low level structures used by the p5 backend.
+ *
+ * The structures and function declared here are used to do the low level
+ * communication with the physical device.
+ */
+
+#ifndef P5_DEVICE_H
+#define P5_DEVICE_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "../include/_stdint.h"
+
+#ifdef HAVE_LINUX_PPDEV_H
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/ppdev.h>
+#include <linux/parport.h>
+#endif
+
+/** @name debugging levels
+ */
+/* @{ */
+#define DBG_error0 0 /* errors/warnings printed even with devuglevel 0 */
+#define DBG_error 1 /* fatal errors */
+#define DBG_warn 2 /* warnings and non-fatal errors */
+#define DBG_info 4 /* informational messages */
+#define DBG_proc 8 /* starting/finishing functions */
+#define DBG_trace 16 /* tracing messages */
+#define DBG_io 32 /* io functions */
+#define DBG_io2 64 /* io functions that are called very often */
+#define DBG_data 128 /* log image data */
+/* @} */
+
+/**
+ * maximal number of resolutions
+ */
+#define MAX_RESOLUTIONS 8
+
+/**> sensor's number of pixels 8.5' @ 300 dpi */
+#define MAX_SENSOR_PIXELS 2550
+
+/**> number of lines to skip when doing calibration */
+#define CALIBRATION_SKIP_LINES 80
+
+/**> last value considered as black for calibration */
+#define BLACK_LEVEL 40
+
+/**> white target value for calibration */
+#define WHITE_TARGET 220.0
+
+/** per dpi calibration rgb data
+ * Calibration data structure
+ */
+typedef struct P5_Calibration_Data
+{
+ unsigned int dpi;
+ uint8_t black_data[MAX_SENSOR_PIXELS * 3];
+ uint8_t white_data[MAX_SENSOR_PIXELS * 3];
+} P5_Calibration_Data;
+
+/**
+ * This structure describes a particular model which is handled by the backend.
+ * Contained data is immutable and is used to initalize the P5_Device
+ * structure.
+ */
+typedef struct P5_Model
+{
+ /** @name device identifier
+ * These values are set up once the physical device has been detected. They
+ * are used to build the return value of sane_get_devices().
+ */
+ /* @{ */
+ SANE_String_Const name;
+ SANE_String_Const vendor;
+ SANE_String_Const product;
+ SANE_String_Const type;
+ /* @} */
+
+ /** @name resolution
+ * list of avalailable physical resolution.
+ * The resolutions must sorted from lower to higher value. The list is terminated
+ * by a value of 0.
+ */
+ /* @{ */
+ int xdpi_values[MAX_RESOLUTIONS]; /** possible x resolutions */
+ int ydpi_values[MAX_RESOLUTIONS]; /** possible y resolutions */
+ /* @} */
+
+ /** @name scan area description
+ * Minimal and maximal values. It's easier to have dedicated members instead
+ * of searching these values in the dpi lists. They are initialized from dpi
+ * lists.
+ */
+ /* @{ */
+ int max_xdpi; /** physical maximum x dpi */
+ int max_ydpi; /** physical maximum y dpi */
+ int min_xdpi; /** physical minimum x dpi */
+ int min_ydpi; /** physical minimum y dpi */
+ /* @} */
+
+ /** @name line distance shift
+ * Distance between CCD arrays for each color. Expressed in line
+ * number at maximum motor resolution.
+ */
+ int lds;
+
+ /** @name scan area description
+ * The geometry values are expressed from the head parking position,
+ * or the start. For a given model, the scan area selected by a frontend
+ * will have to fit within these values.
+ */
+ /* @{ */
+ SANE_Fixed x_offset; /** Start of scan area in mm */
+ SANE_Fixed y_offset; /** Start of scan area in mm */
+ SANE_Fixed x_size; /** Size of scan area in mm */
+ SANE_Fixed y_size; /** Size of scan area in mm */
+ /* @} */
+
+} P5_Model;
+
+
+/**
+ * Enumeration of configuration options for a device. It must starts at 0.
+ */
+enum P5_Configure_Option
+{
+ CFG_MODEL_NAME = 0, /**<option to override model name */
+ NUM_CFG_OPTIONS /** MUST be last to give the actual number of configuration options */
+};
+
+/**
+ * Device specific configuration structure to hold option values for
+ * devices handled by the p5 backend. There must one member for
+ * each configuration option.
+ */
+typedef struct P5_Config
+{
+ SANE_String modelname; /** model name to use, overrinding the one from detection */
+} P5_Config;
+
+
+/**
+ * Hardware device description.
+ * Since the settings used for a scan may actually differ from the one of the
+ * SANE level, it may contains scanning parameters and data relative to a current
+ * scan such as data buffers and counters.
+ */
+typedef struct P5_Device
+{
+ /**
+ * Point to the next device in a linked list
+ */
+ struct P5_Device *next;
+
+ /**
+ * Points to a structure that decribes model capabilities, geometry
+ * and default settings.
+ */
+ P5_Model *model;
+
+ /**
+ * @brief name of the device
+ * Name of the device: it may be the file name used to access the hardware.
+ * For instance parport0 for a parallel port device, or the libusb file name
+ * for an USB scanner.
+ */
+ SANE_String name;
+
+ /**
+ * SANE_TRUE if the device is local (ie not over network)
+ */
+ SANE_Bool local;
+
+ /**
+ * True if device has been intialized.
+ */
+ SANE_Bool initialized;
+
+ /**
+ * Configuration options for the device read from
+ * configuration file at attach time. This member is filled at
+ * attach time.
+ */
+ P5_Config *config;
+
+ /** @brief scan parameters
+ * The scan done by the hardware can be different from the one at the SANE
+ * frontend session. For instance:
+ * - xdpy and ydpi may be different to accomodate hardware capabilites.
+ * - many CCD scanners need to scan more lines to correct the 'line
+ * distance shift' effect.
+ * - emulated modes (lineart from gray scan, or gray scan for color one)
+ */
+ /* @{ */
+ int xdpi; /** real horizontal resolution */
+ int ydpi; /** real vertical resolution */
+ int lines; /** physical lines to scan */
+ int pixels; /** physical width of scan area */
+ int bytes_per_line; /** number of bytes per line */
+ int xstart; /** x start coordinate */
+ int ystart; /** y start coordinate */
+ int mode; /** color, gray or lineart mode */
+ int lds; /** line distance shift */
+ /* @} */
+
+ /** @brief device file descriptor
+ * low level device file descriptor
+ */
+ int fd;
+
+ /**
+ * work buffer for scans
+ */
+ uint8_t *buffer;
+
+ /**
+ * buffer size
+ */
+ size_t size;
+
+ /**
+ * position in buffer
+ */
+ size_t position;
+
+ /**
+ * top value of available bytes in buffer
+ */
+ size_t top;
+
+ /**
+ * bottom value of available bytes in buffer
+ */
+ size_t bottom;
+
+ /**
+ * True if device has been calibrated.
+ */
+ SANE_Bool calibrated;
+
+ P5_Calibration_Data *calibration_data[MAX_RESOLUTIONS * 2];
+
+ /**> correction coefficient for the current scan */
+ float *gain;
+ uint8_t *offset;
+
+} P5_Device;
+
+
+#define DATA 0
+#define STATUS 1
+#define CONTROL 2
+#define EPPADR 3
+#define EPPDATA 4
+
+#define REG0 0x00
+#define REG1 0x11
+#define REG2 0x22
+#define REG3 0x33
+#define REG4 0x44
+#define REG5 0x55
+#define REG6 0x66
+#define REG7 0x77
+#define REG8 0x88
+#define REG9 0x99
+#define REGA 0xAA
+#define REGB 0xBB
+#define REGC 0xCC
+#define REGD 0xDD
+#define REGE 0xEE
+#define REGF 0xFF
+
+#define MODE_COLOR 0
+#define MODE_GRAY 1
+#define MODE_LINEART 2
+
+#endif /* not P5_DEVICE_H */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/pie-scsidef.h b/backend/pie-scsidef.h
new file mode 100644
index 0000000..d86a0c9
--- /dev/null
+++ b/backend/pie-scsidef.h
@@ -0,0 +1,433 @@
+/* ----------------------------------------------------------------------------- */
+
+/* sane - Scanner Access Now Easy.
+
+ pie-scsidef.h: scsi-definiton header file for PIE scanner driver.
+
+ Copyright (C) 2000 Simon Munton, based on the umax-scsidef.h by Oliver Rauch & Michael Johnson
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#ifndef PIE_SCSIDEF_H
+#define PIE_SCSIDEF_H
+
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* I'm using functions derived from Eric Youngdale's scsiinfo
+ * program here for dealing with parts of SCSI commands.
+ */
+
+static inline void setbitfield(unsigned char * pageaddr, int mask, int shift, int val)
+{ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift); }
+
+static inline void resetbitfield(unsigned char * pageaddr, int mask, int shift, int val)
+{ *pageaddr = (*pageaddr & ~(mask << shift)) | (((!val) & mask) << shift); }
+
+static inline int getbitfield(unsigned char * pageaddr, int mask, int shift)
+{ return ((*pageaddr >> shift) & mask); }
+
+/* ------------------------------------------------------------------------- */
+
+static inline int getnbyte(unsigned char * pnt, int nbytes)
+{
+ unsigned int result = 0;
+ int i;
+
+ for(i=0; i<nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline int getnbyte1(unsigned char * pnt, int nbytes)
+{
+ unsigned int result = 0;
+ int i;
+
+ for(i=nbytes-1; i >= 0; i--)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void putnbyte(unsigned char * pnt, unsigned int value, unsigned int nbytes)
+{
+ int i;
+
+ for(i=nbytes-1; i>= 0; i--)
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+static inline void putnbyte1(unsigned char * pnt, unsigned int value, unsigned int nbytes)
+{
+ unsigned int i;
+
+ for(i=0; i< nbytes; i++)
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* Not all of these are defined in scsi.h, so we'll make sure
+ * we agree about them here...
+ */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define SCAN 0x1B
+#define READ 0x08
+#define WRITE 0x0A
+#define PARAM 0x0F
+#define MODE 0x15
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define STD_WDB_LEN 0x28 /* wdb_len if nothing is set by inquiry */
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* SCSI commands */
+
+typedef struct
+{
+ unsigned char *cmd;
+ size_t size;
+} scsiblk;
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define set_inquiry_return_size(icb,val) icb[0x04]=val
+static unsigned char inquiryC[] = { INQUIRY, 0x00, 0x00, 0x00, 0x7c, 0x00 };
+static scsiblk inquiry = { inquiryC, sizeof(inquiryC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define get_inquiry_periph_qual(in) getbitfield(in, 0x07, 5)
+# define IN_periph_qual_lun 0x00
+# define IN_periph_qual_nolun 0x03
+#define get_inquiry_periph_devtype(in) getbitfield(in, 0x1f, 0)
+# define IN_periph_devtype_scanner 0x06
+# define IN_periph_devtype_unknown 0x1f
+
+#define get_inquiry_rmb(in) getbitfield(in + 0x01, 0x01, 7)
+#define get_inquiry_0x01_bit6(in) getbitfield(in + 0x01, 0x01, 6)
+#define get_inquiry_0x01_bit5(in) getbitfield(in + 0x01, 0x01, 5)
+
+#define get_inquiry_iso_version(in) getbitfield(in + 0x02, 0x03, 6)
+#define get_inquiry_ecma_version(in) getbitfield(in + 0x02, 0x07, 3)
+#define get_inquiry_ansi_version(in) getbitfield(in + 0x02, 0x07, 0)
+
+#define get_inquiry_aenc(in) getbitfield(in + 0x03, 0x01, 7)
+#define get_inquiry_tmiop(in) getbitfield(in + 0x03, 0x01, 6)
+#define get_inquiry_0x03_bit5(in) getbitfield(in + 0x03, 0x01, 5)
+#define get_inquiry_0x03_bit4(in) getbitfield(in + 0x03, 0x01, 4)
+#define get_inquiry_response_format(in) getbitfield(in + 0x03, 0x0f, 0)
+# define IN_recognized 0x02
+
+#define get_inquiry_additional_length(in) in[0x04]
+#define set_inquiry_length(out,n) out[0x04]=n-5
+
+#define get_inquiry_vendor(in, buf) strncpy(buf, in + 0x08, 0x08)
+#define get_inquiry_product(in, buf) strncpy(buf, in + 0x10, 0x010)
+#define get_inquiry_version(in, buf) strncpy(buf, in + 0x20, 0x04)
+
+#define get_inquiry_max_x_res(in) getnbyte1(in + 0x24, 2)
+#define get_inquiry_max_y_res(in) getnbyte1(in + 0x26, 2)
+#define get_inquiry_fb_max_scan_width(in) getnbyte1(in + 0x28, 2)
+#define get_inquiry_fb_max_scan_length(in) getnbyte1(in + 0x2a, 2)
+#define get_inquiry_filters(in) in[0x2c]
+#define get_inquiry_color_depths(in) in[0x2d]
+#define get_inquiry_color_format(in) in[0x2e]
+#define get_inquiry_image_format(in) in[0x30]
+#define get_inquiry_scan_capability(in) in[0x31]
+#define get_inquiry_optional_devices(in) in[0x32]
+#define get_inquiry_enhancements(in) in[0x33]
+#define get_inquiry_gamma_bits(in) in[0x34]
+#define get_inquiry_last_filter(in) in[0x35]
+#define get_inquiry_fast_preview_res(in) getnbyte1(in + 0x36, 2)
+#define get_inquiry_halftones(in) in[0x60]
+#define get_inquiry_halftone_max_width(in) in[0x61]
+#define get_inquiry_halftone_max_heighgt(in) in[0x62]
+#define get_inquiry_max_windows(in) in[0x63]
+#define get_inquiry_min_highlight(in) in[0x65]
+#define get_inquiry_max_shadow(in) in[0x66]
+#define get_inquiry_cal_eqn(in) in[0x67]
+#define get_inquiry_max_exp(in) getnbyte1(in + 0x68, 2)
+#define get_inquiry_min_exp(in) getnbyte1(in + 0x6a, 2)
+#define get_inquiry_trans_x1(in) getnbyte1(in + 0x6c, 2)
+#define get_inquiry_trans_y1(in) getnbyte1(in + 0x6e, 2)
+#define get_inquiry_trans_x2(in) getnbyte1(in + 0x70, 2)
+#define get_inquiry_trans_y2(in) getnbyte1(in + 0x72, 2)
+
+#define INQ_ONE_PASS_COLOR 0x80
+#define INQ_FILTER_BLUE 0x08
+#define INQ_FILTER_GREEN 0x04
+#define INQ_FILTER_RED 0x02
+#define INQ_FILTER_NEUTRAL 0x01
+
+#define INQ_COLOR_DEPTH_16 0x20
+#define INQ_COLOR_DEPTH_12 0x10
+#define INQ_COLOR_DEPTH_10 0x08
+#define INQ_COLOR_DEPTH_8 0x04
+#define INQ_COLOR_DEPTH_4 0x02
+#define INQ_COLOR_DEPTH_1 0x01
+
+#define INQ_COLOR_FORMAT_INDEX 0x04
+#define INQ_COLOR_FORMAT_LINE 0x02
+#define INQ_COLOR_FORMAT_PIXEL 0x01
+
+#define INQ_IMG_FMT_OKLINE 0x08
+#define INQ_IMG_FMT_BLK_ONE 0x04
+#define INQ_IMG_FMT_MOTOROLA 0x02
+#define INQ_IMG_FMT_INTEL 0x01
+
+#define INQ_CAP_PWRSAV 0x80
+#define INQ_CAP_EXT_CAL 0x40
+#define INQ_CAP_FAST_PREVIEW 0x10
+#define INQ_CAP_DISABLE_CAL 0x08
+#define INQ_CAP_SPEEDS 0x07
+
+#define INQ_OPT_DEV_MPCL 0x80
+#define INQ_OPT_DEV_TP1 0x04
+#define INQ_OPT_DEV_TP 0x02
+#define INQ_OPT_DEV_ADF 0x01
+
+#define INQ_ENHANCE_EDGE 0x02
+
+#define INQ_LAST_FILTER_BLUE 0x08
+#define INQ_LAST_FILTER_GREEN 0x04
+#define INQ_LAST_FILTER_RED 0x02
+#define INQ_LAST_FILTER_NEUTRAL 0x01
+
+#define INQ_DWNLD_HALFTONE 0x80
+#define INQ_NUM_HALFTONES 0x7f
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char test_unit_readyC[] = { TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk test_unit_ready = { test_unit_readyC,sizeof(test_unit_readyC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char reserve_unitC[] = { RESERVE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk reserve_unit = { reserve_unitC, sizeof(reserve_unitC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char release_unitC[] = { RELEASE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk release_unit = { release_unitC, sizeof(release_unitC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char paramC[] = { PARAM, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk param = { paramC, sizeof(paramC) };
+
+#define set_param_length(in, l) putnbyte(in + 3, (l), 2)
+
+#define get_param_scan_width(b) getnbyte1(b, 2)
+#define get_param_scan_lines(b) getnbyte1(b + 2, 2)
+#define get_param_scan_bytes(b) getnbyte1(b + 4, 2)
+#define get_param_scan_filter_offset1(b) b[6]
+#define get_param_scan_filter_offset2(b) b[7]
+#define get_param_scan_period(b) getnbyte1(b + 8, 4)
+#define get_param_scsi_xfer_rate(b) getnbyte1(b + 12, 2)
+#define get_param_scan_available_lines(b) getnbyte1(b + 14, 2)
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char writeC[] = { WRITE, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk swrite = { writeC, sizeof(writeC) };
+
+#define set_write_length(in, l) putnbyte(in + 2, (l), 3)
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char modeC[] = { MODE, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk smode = { modeC, sizeof(modeC) };
+
+#define set_mode_length(in, l) putnbyte(in + 3, (l), 2)
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char scanC[] = { SCAN, 0x00, 0x00, 0x00, 0x01, 0x00 };
+
+static scsiblk scan = { scanC, sizeof(scanC) };
+
+#define set_scan_cmd(in, l) in[4] = l
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* sread instead of read because read is a libc primitive */
+static unsigned char sreadC[] = { READ, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk sread = { sreadC, sizeof(sreadC) };
+
+#define set_read_length(in, l) putnbyte(in + 2, (l), 3)
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#if 0
+static unsigned char request_senseC[] = { REQUEST_SENSE, 0x00, 0x00, 0x00, 0x00, 0x00 };
+#define set_RS_allocation_length(sb,val) sb[0x04]=val
+#define set_RS_LUN(sb,val) setbitfield(sb + 0x01, 7, 5) /* ??? */
+
+static scsiblk request_sense = { request_senseC, sizeof(request_senseC) };
+#endif
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid */
+#define get_RS_CD(b) getbitfield(b+0x0f,1,6) /* 1=CDB */
+#define get_RS_field_pointer(b) getnbyte(b+0x10, 2)
+
+#define get_RS_additional_sense(b) getnbyte(b+0x12, 2)
+
+#define rs_return_block_size 0x1f
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static char *sense_str[] = {"NO SENSE",
+ "RECOVERED ERROR",
+ "NOT READY",
+ "MEDIUM ERROR",
+ "HARDWARE ERROR",
+ "ILLEGAL REQUEST",
+ "UNIT ATTENTION",
+ "DATA PROTECT",
+ "BLANK CHECK",
+ "VENDOR SPECIFIC",
+ "COPY ABORTED",
+ "ABORTED COMMAND",
+ "EQUAL",
+ "VOLUME OVERFLOW",
+ "MISCOMPARE",
+ "??? - SENSE 0FH" };
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* command codes used in the data part of a SCSI write command */
+
+#define SET_POWER_SAVE_CONTROL 0x01
+#define DWNLD_GAMMA_TABLE 0x10
+#define DWNLD_HALFTONE 0x11
+#define SET_SCAN_FRAME 0x12
+#define SET_EXP_TIME 0x13
+#define SET_HIGHLIGHT_SHADOW 0x14
+#define SEND_CAL_DATA 0x16
+
+#define READ_POWER_SAVE_CONTROL 0x81
+#define READ_GAMMA_TABLE 0x90
+#define READ_HALFTONE 0x91
+#define READ_SCAN_FRAME 0x92
+#define READ_EXP_TIME 0x93
+#define READ_HIGHLIGHT_SHADOW 0x94
+#define READ_CAL_INFO 0x95
+
+
+#define set_command(in, cmd) putnbyte1(in, cmd, 2)
+#define set_data_length(in, len) putnbyte1(in + 2, len, 2)
+#define set_data(in, ofs, val, num) putnbyte1(in + ofs, val, num)
+
+
+
+#define FILTER_BLUE 0x08
+#define FILTER_GREEN 0x04
+#define FILTER_RED 0x02
+#define FILTER_NEUTRAL 0x01
+
+
+#endif
diff --git a/backend/pie.c b/backend/pie.c
new file mode 100644
index 0000000..941ed62
--- /dev/null
+++ b/backend/pie.c
@@ -0,0 +1,3828 @@
+/* sane - Scanner Access Now Easy.
+
+ pie.c
+
+ Copyright (C) 2000 Simon Munton, based on the umax backend by Oliver Rauch
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/*
+ * 22-2-2003 set devlist to NULL in sane_exit()
+ * set first_dev to NULL in sane_exit()
+ * eliminated num_devices
+ *
+ * 23-7-2002 added TL_X > BR_X, TL_Y > BR_Y check in sane_start
+ *
+ * 17-9-2001 changed ADLIB to AdLib as the comparison is case sensitive and
+ * the scanner returns AdLib
+ *
+ * 7-5-2001 removed removal of '\n' after sanei_config_read()
+ * free devlist allocated in sane_get_devices() on sane_exit()
+ *
+ * 2-3-2001 improved the reordering of RGB data in pie_reader_process()
+ *
+ * 11-11-2000 eliminated some warnings about signed/unsigned comparisons
+ * removed #undef NDEBUG and C++ style comments
+ *
+ * 1-10-2000 force gamma table to one to one mappping if lineart or halftone selected
+ *
+ * 30-9-2000 added ADLIB devices to scanner_str[]
+ *
+ * 29-9-2000 wasn't setting 'background is halftone bit' (BGHT) in halftone mode
+ *
+ * 27-9-2000 went public with build 4
+ */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+
+#define BACKEND_NAME pie
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+# include "../include/sane/sanei_thread.h"
+
+#include "pie-scsidef.h"
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+#define DBG_dump 14
+
+#define BUILD 9
+
+#define PIE_CONFIG_FILE "pie.conf"
+
+#define LINEART_STR SANE_VALUE_SCAN_MODE_LINEART
+#define HALFTONE_STR SANE_VALUE_SCAN_MODE_HALFTONE
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+#define LINEART 1
+#define HALFTONE 2
+#define GRAYSCALE 3
+#define RGB 4
+
+#define CAL_MODE_PREVIEW (INQ_CAP_FAST_PREVIEW)
+#define CAL_MODE_FLATBED 0x00
+#define CAL_MODE_ADF (INQ_OPT_DEV_ADF)
+#define CAL_MODE_TRANPSARENCY (INQ_OPT_DEV_TP)
+#define CAL_MODE_TRANPSARENCY1 (INQ_OPT_DEV_TP1)
+
+#define min(a,b) (((a)<(b))?(a):(b))
+#define max(a,b) (((a)>(b))?(a):(b))
+
+
+/* names of scanners that are supported because */
+/* the inquiry_return_block is ok and driver is tested */
+
+static char *scanner_str[] = {
+ "DEVCOM", "9636PRO",
+ "DEVCOM", "9636S",
+ "DEVCOM", "9630S",
+ "PIE", "ScanAce 1236S",
+ "PIE", "ScanAce 1230S",
+ "PIE", "ScanAce II",
+ "PIE", "ScanAce III",
+ "PIE", "ScanAce Plus",
+ "PIE", "ScanAce II Plus",
+ "PIE", "ScanAce III Plus",
+ "PIE", "ScanAce V",
+ "PIE", "ScanMedia",
+ "PIE", "ScanMedia II",
+ "PIE", "ScanAce 630S",
+ "PIE", "ScanAce 636S",
+ "AdLib", "JetScan 630",
+ "AdLib", "JetScan 636PRO",
+ "END_OF_LIST"
+};
+
+/* times (in us) to delay after certain commands. Scanner seems to lock up if it returns busy
+ * status and commands are repeatedly reissued (by kernel error handler) */
+
+#define DOWNLOAD_GAMMA_WAIT_TIME (1000000)
+#define SCAN_WAIT_TIME (1000000)
+#define SCAN_WARMUP_WAIT_TIME (500000)
+#define TUR_WAIT_TIME (500000)
+
+
+/* options supported by the scanner */
+
+enum Pie_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ /* ------------------------------------------- */
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+
+
+ /* ------------------------------------------- */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ /* ------------------------------------------- */
+
+ OPT_ENHANCEMENT_GROUP,
+
+ OPT_HALFTONE_PATTERN,
+ OPT_SPEED,
+ OPT_THRESHOLD,
+
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ /* ------------------------------------------- */
+
+ OPT_ADVANCED_GROUP,
+ OPT_PREVIEW,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+
+
+
+/* This defines the information needed during calibration */
+
+struct Pie_cal_info
+{
+ int cal_type;
+ int receive_bits;
+ int send_bits;
+ int num_lines;
+ int pixels_per_line;
+};
+
+
+/* This structure holds the information about a physical scanner */
+
+typedef struct Pie_Device
+{
+ struct Pie_Device *next;
+
+ char *devicename; /* name of the scanner device */
+
+ char vendor[9]; /* will be xxxxx */
+ char product[17]; /* e.g. "SuperVista_S12" or so */
+ char version[5]; /* e.g. V1.3 */
+
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+
+ SANE_Range exposure_range;
+ SANE_Range shadow_range;
+ SANE_Range highlight_range;
+
+ int inquiry_len; /* length of inquiry return block */
+
+ int inquiry_x_res; /* maximum x-resolution */
+ int inquiry_y_res; /* maximum y-resolution */
+ int inquiry_pixel_resolution;
+ double inquiry_fb_width; /* flatbed width in inches */
+ double inquiry_fb_length; /* flatbed length in inches */
+
+ int inquiry_trans_top_left_x;
+ int inquiry_trans_top_left_y;
+ double inquiry_trans_width; /* transparency width in inches */
+ double inquiry_trans_length; /* transparency length in inches */
+
+ int inquiry_halftones; /* number of halftones supported */
+ int inquiry_filters; /* available colour filters */
+ int inquiry_color_depths; /* available colour depths */
+ int inquiry_color_format; /* colour format from scanner */
+ int inquiry_image_format; /* image data format */
+ int inquiry_scan_capability; /* additional scanner features, number of speeds */
+ int inquiry_optional_devices; /* optional devices */
+ int inquiry_enhancements; /* enhancements */
+ int inquiry_gamma_bits; /* no of bits used for gamma table */
+ int inquiry_fast_preview_res; /* fast preview resolution */
+ int inquiry_min_highlight; /* min highlight % that can be used */
+ int inquiry_max_shadow; /* max shadow % that can be used */
+ int inquiry_cal_eqn; /* which calibration equation to use */
+ int inquiry_min_exp; /* min exposure % */
+ int inquiry_max_exp; /* max exposure % */
+
+ SANE_String scan_mode_list[7]; /* holds names of types of scan (color, ...) */
+
+ SANE_String halftone_list[17]; /* holds the names of the halftone patterns from the scanner */
+
+ SANE_String speed_list[9]; /* holds the names of available speeds */
+
+ int cal_info_count; /* number of calibration info sets */
+ struct Pie_cal_info *cal_info; /* points to the actual calibration information */
+}
+Pie_Device;
+
+/* This structure holds information about an instance of an 'opened' scanner */
+
+typedef struct Pie_Scanner
+{
+ struct Pie_Scanner *next;
+ Pie_Device *device; /* pointer to physical scanner */
+
+ int sfd; /* scanner file desc. */
+ int bufsize; /* max scsi buffer size */
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS]; /* option descriptions for this instance */
+ Option_Value val[NUM_OPTIONS]; /* option settings for this instance */
+ SANE_Int *gamma_table[4]; /* gamma tables for this instance */
+ SANE_Range gamma_range;
+ int gamma_length; /* size of gamma table */
+
+ int scanning; /* true if actually doing a scan */
+ SANE_Parameters params;
+
+ SANE_Pid reader_pid;
+ int pipe;
+ int reader_fds;
+
+ int colormode; /* whether RGB, GRAY, LINEART, HALFTONE */
+ int resolution;
+ int cal_mode; /* set to value to compare cal_info mode to */
+
+ int cal_filter; /* set to indicate which filters will provide data for cal */
+
+ int filter_offset1; /* offsets between colors in indexed scan mode */
+ int filter_offset2;
+
+ int bytes_per_line; /* number of bytes per line */
+
+}
+Pie_Scanner;
+
+static const SANE_Range percentage_range_100 = {
+ 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 0 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static Pie_Device *first_dev = NULL;
+static Pie_Scanner *first_handle = NULL;
+static const SANE_Device **devlist = NULL;
+
+
+
+static SANE_Status pie_wait_scanner (Pie_Scanner * scanner);
+
+
+/* ---------------------------------- PIE DUMP_BUFFER ---------------------------------- */
+
+#define DBG_DUMP(level, buf, n) { if (DBG_LEVEL >= (level)) pie_dump_buffer(level,buf,n); }
+
+
+static void
+pie_dump_buffer (int level, unsigned char *buf, int n)
+{
+ char s[80], *p = s;
+ int a = 0;
+
+ while (n--)
+ {
+ if ((a % 16) == 0)
+ p += sprintf (p, " %04X ", a);
+
+ p += sprintf (p, "%02X ", *buf++);
+
+ if ((n == 0) || (a % 16) == 15)
+ {
+ DBG (level, "%s\n", s);
+ p = s;
+ }
+ a++;
+ }
+}
+
+/* ---------------------------------- PIE INIT ---------------------------------- */
+
+static void
+pie_init (Pie_Device * dev) /* pie_init is called once while driver-initialization */
+{
+ DBG (DBG_proc, "init\n");
+
+ dev->cal_info_count = 0;
+ dev->cal_info = NULL;
+
+ dev->devicename = NULL;
+ dev->inquiry_len = 0;
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ DBG (DBG_info,
+ "variable scsi buffer size (usage of sanei_scsi_open_extended)\n");
+#else
+ DBG (DBG_info, "fixed scsi buffer size = %d bytes\n",
+ sanei_scsi_max_request_size);
+#endif
+}
+
+
+/* ---------------------------- SENSE_HANDLER ------------------------------ */
+
+
+static SANE_Status
+sense_handler (__sane_unused__ int scsi_fd, unsigned char *result, __sane_unused__ void *arg) /* is called by sanei_scsi */
+{
+ unsigned char asc, ascq, sensekey;
+ int asc_ascq, len;
+ /* Pie_Device *dev = arg; */
+
+ DBG (DBG_proc, "check condition sense handler\n");
+
+ sensekey = get_RS_sense_key (result);
+ asc = get_RS_ASC (result);
+ ascq = get_RS_ASCQ (result);
+ asc_ascq = (int) (256 * asc + ascq);
+ len = 7 + get_RS_additional_length (result);
+
+ if (get_RS_error_code (result) != 0x70)
+ {
+ DBG (DBG_proc, "invalid sense key => handled as DEVICE BUSY!\n");
+ return SANE_STATUS_DEVICE_BUSY; /* sense key invalid */
+ }
+
+ DBG (DBG_sense, "check condition sense: %s\n", sense_str[sensekey]);
+
+ if (get_RS_ILI (result) != 0)
+ {
+ DBG (DBG_sense,
+ "-> ILI-ERROR: requested data length is larger than actual length\n");
+ }
+
+ switch (sensekey)
+ {
+ case 0x00: /* no sense, could have been busy */
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ case 0x02:
+ if (asc_ascq == 0x0401)
+ DBG (DBG_sense, "-> Not Ready - Warming Up\n");
+ else if (asc_ascq == 0x0483)
+ DBG (DBG_sense, "-> Not Ready - Need manual service\n");
+ else if (asc_ascq == 0x0881)
+ DBG (DBG_sense, "-> Not Ready - Communication time out\n");
+ else
+ DBG (DBG_sense, "-> unknown medium error: asc=%d, ascq=%d\n", asc,
+ ascq);
+ break;
+
+ case 0x03: /* medium error */
+ if (asc_ascq == 0x5300)
+ DBG (DBG_sense, "-> Media load or eject failure\n");
+ else if (asc_ascq == 0x3a00)
+ DBG (DBG_sense, "-> Media not present\n");
+ else if (asc_ascq == 0x3b05)
+ DBG (DBG_sense, "-> Paper jam\n");
+ else if (asc_ascq == 0x3a80)
+ DBG (DBG_sense, "-> ADF paper out\n");
+ else
+ DBG (DBG_sense, "-> unknown medium error: asc=%d, ascq=%d\n", asc,
+ ascq);
+ break;
+
+
+ case 0x04: /* hardware error */
+ if (asc_ascq == 0x4081)
+ DBG (DBG_sense, "-> CPU RAM failure\n");
+ else if (asc_ascq == 0x4082)
+ DBG (DBG_sense, "-> Scanning system RAM failure\n");
+ else if (asc_ascq == 0x4083)
+ DBG (DBG_sense, "-> Image buffer failure\n");
+ else if (asc_ascq == 0x0403)
+ DBG (DBG_sense, "-> Manual intervention required\n");
+ else if (asc_ascq == 0x6200)
+ DBG (DBG_sense, "-> Scan head position error\n");
+ else if (asc_ascq == 0x6000)
+ DBG (DBG_sense, "-> Lamp or CCD failure\n");
+ else if (asc_ascq == 0x6081)
+ DBG (DBG_sense, "-> Transparency lamp failure\n");
+ else if (asc_ascq == 0x8180)
+ DBG (DBG_sense, "-> DC offset or black level calibration failure\n");
+ else if (asc_ascq == 0x8181)
+ DBG (DBG_sense,
+ "-> Integration time adjustment failure (too light)\n");
+ else if (asc_ascq == 0x8182)
+ DBG (DBG_sense,
+ "-> Integration time adjustment failure (too dark)\n");
+ else if (asc_ascq == 0x8183)
+ DBG (DBG_sense, "-> Shading curve adjustment failure\n");
+ else if (asc_ascq == 0x8184)
+ DBG (DBG_sense, "-> Gain adjustment failure\n");
+ else if (asc_ascq == 0x8185)
+ DBG (DBG_sense, "-> Optical alignment failure\n");
+ else if (asc_ascq == 0x8186)
+ DBG (DBG_sense, "-> Optical locating failure\n");
+ else if (asc_ascq == 0x8187)
+ DBG (DBG_sense, "-> Scan pixel map less than 5100 pixels!\n");
+ else if (asc_ascq == 0x4700)
+ DBG (DBG_sense, "-> Parity error on SCSI bus\n");
+ else if (asc_ascq == 0x4b00)
+ DBG (DBG_sense, "-> Data phase error\n");
+ else
+ DBG (DBG_sense, "-> unknown hardware error: asc=%d, ascq=%d\n", asc,
+ ascq);
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+
+ case 0x05: /* illegal request */
+ if (asc_ascq == 0x1a00)
+ DBG (DBG_sense, "-> Parameter list length error\n");
+ else if (asc_ascq == 0x2c01)
+ DBG (DBG_sense, "-> Too many windows specified\n");
+ else if (asc_ascq == 0x2c02)
+ DBG (DBG_sense, "-> Invalid combination of windows\n");
+ else if (asc_ascq == 0x2c81)
+ DBG (DBG_sense, "-> Illegal scanning frame\n");
+ else if (asc_ascq == 0x2400)
+ DBG (DBG_sense, "-> Invalid field in CDB\n");
+ else if (asc_ascq == 0x2481)
+ DBG (DBG_sense, "-> Request too many lines of data\n");
+ else if (asc_ascq == 0x2000)
+ DBG (DBG_sense, "-> Invalid command OP code\n");
+ else if (asc_ascq == 0x2501)
+ DBG (DBG_sense, "-> LUN not supported\n");
+ else if (asc_ascq == 0x2601)
+ DBG (DBG_sense, "-> Parameter not supported\n");
+ else if (asc_ascq == 0x2602)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Parameter not specified\n");
+ else if (asc_ascq == 0x2603)
+ DBG (DBG_sense, "-> Parameter value invalid - Invalid threshold\n");
+ else if (asc_ascq == 0x2680)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Control command sequence error\n");
+ else if (asc_ascq == 0x2681)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Grain setting (halftone pattern\n");
+ else if (asc_ascq == 0x2682)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal resolution setting\n");
+ else if (asc_ascq == 0x2683)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Invalid filter assignment\n");
+ else if (asc_ascq == 0x2684)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal gamma adjustment setting (look-up table)\n");
+ else if (asc_ascq == 0x2685)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal offset setting (digital brightness)\n");
+ else if (asc_ascq == 0x2686)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal bits per pixel setting\n");
+ else if (asc_ascq == 0x2687)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal contrast setting\n");
+ else if (asc_ascq == 0x2688)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal paper length setting\n");
+ else if (asc_ascq == 0x2689)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal highlight/shadow setting\n");
+ else if (asc_ascq == 0x268a)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal exposure time setting (analog brightness)\n");
+ else if (asc_ascq == 0x268b)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Invalid device select or device not exist\n");
+ else if (asc_ascq == 0x268c)
+ DBG (DBG_sense,
+ "-> Parameter value invalid - Illegal color packing\n");
+ else if (asc_ascq == 0x3d00)
+ DBG (DBG_sense, "-> Invalid bits in identify field\n");
+
+
+
+ else if (asc_ascq == 0x4900)
+ DBG (DBG_sense, "-> Invalid message\n");
+ else if (asc_ascq == 0x8101)
+ DBG (DBG_sense, "-> Not enough memory for color packing\n");
+
+ if (len >= 0x11)
+ {
+ if (get_RS_SKSV (result) != 0)
+ {
+ if (get_RS_CD (result) == 0)
+ {
+
+ DBG (DBG_sense, "-> illegal parameter in CDB\n");
+ }
+ else
+ {
+ DBG (DBG_sense,
+ "-> illegal parameter is in the data parameters sent during data out phase\n");
+ }
+
+ DBG (DBG_sense, "-> error detected in byte %d\n",
+ get_RS_field_pointer (result));
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+
+ case 0x06: /* unit attention */
+ if (asc_ascq == 0x2900)
+ DBG (DBG_sense, "-> power on, reset or bus device reset\n");
+ if (asc_ascq == 0x8200)
+ DBG (DBG_sense,
+ "-> unit attention - calibration disable not granted\n");
+ if (asc_ascq == 0x8300)
+ DBG (DBG_sense, "-> unit attention - calibration will be ignored\n");
+ else
+ DBG (DBG_sense, "-> unit attention: asc=%d, ascq=%d\n", asc, ascq);
+ break;
+
+
+ case 0x09: /* vendor specific */
+ DBG (DBG_sense, "-> vendor specific sense-code: asc=%d, ascq=%d\n", asc,
+ ascq);
+ break;
+
+ case 0x0b:
+ if (asc_ascq == 0x0006)
+ DBG (DBG_sense, "-> Received ABORT message from initiator\n");
+ if (asc_ascq == 0x4800)
+ DBG (DBG_sense, "-> Initiator detected error message received\n");
+ if (asc_ascq == 0x4300)
+ DBG (DBG_sense, "-> Message error\n");
+ if (asc_ascq == 0x4500)
+ DBG (DBG_sense, "-> Select or re-select error\n");
+ else
+ DBG (DBG_sense, "-> aborted command: asc=%d, ascq=%d\n", asc, ascq);
+ break;
+
+ }
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+
+/* -------------------------------- PIE PRINT INQUIRY ------------------------- */
+
+
+static void
+pie_print_inquiry (Pie_Device * dev)
+{
+ DBG (DBG_inquiry, "INQUIRY:\n");
+ DBG (DBG_inquiry, "========\n");
+ DBG (DBG_inquiry, "\n");
+ DBG (DBG_inquiry, "vendor........................: '%s'\n", dev->vendor);
+ DBG (DBG_inquiry, "product.......................: '%s'\n", dev->product);
+ DBG (DBG_inquiry, "version.......................: '%s'\n", dev->version);
+
+ DBG (DBG_inquiry, "X resolution..................: %d dpi\n",
+ dev->inquiry_x_res);
+ DBG (DBG_inquiry, "Y resolution..................: %d dpi\n",
+ dev->inquiry_y_res);
+ DBG (DBG_inquiry, "pixel resolution..............: %d dpi\n",
+ dev->inquiry_pixel_resolution);
+ DBG (DBG_inquiry, "fb width......................: %f in\n",
+ dev->inquiry_fb_width);
+ DBG (DBG_inquiry, "fb length.....................: %f in\n",
+ dev->inquiry_fb_length);
+
+ DBG (DBG_inquiry, "transparency width............: %f in\n",
+ dev->inquiry_trans_width);
+ DBG (DBG_inquiry, "transparency length...........: %f in\n",
+ dev->inquiry_trans_length);
+ DBG (DBG_inquiry, "transparency offset...........: %d,%d\n",
+ dev->inquiry_trans_top_left_x, dev->inquiry_trans_top_left_y);
+
+ DBG (DBG_inquiry, "# of halftones................: %d\n",
+ dev->inquiry_halftones);
+
+ DBG (DBG_inquiry, "One pass color................: %s\n",
+ dev->inquiry_filters & INQ_ONE_PASS_COLOR ? "yes" : "no");
+
+ DBG (DBG_inquiry, "Filters.......................: %s%s%s%s (%02x)\n",
+ dev->inquiry_filters & INQ_FILTER_RED ? "Red " : "",
+ dev->inquiry_filters & INQ_FILTER_GREEN ? "Green " : "",
+ dev->inquiry_filters & INQ_FILTER_BLUE ? "Blue " : "",
+ dev->inquiry_filters & INQ_FILTER_NEUTRAL ? "Neutral " : "",
+ dev->inquiry_filters);
+
+ DBG (DBG_inquiry, "Color depths..................: %s%s%s%s%s%s (%02x)\n",
+ dev->inquiry_color_depths & INQ_COLOR_DEPTH_16 ? "16 bit " : "",
+ dev->inquiry_color_depths & INQ_COLOR_DEPTH_12 ? "12 bit " : "",
+ dev->inquiry_color_depths & INQ_COLOR_DEPTH_10 ? "10 bit " : "",
+ dev->inquiry_color_depths & INQ_COLOR_DEPTH_8 ? "8 bit " : "",
+ dev->inquiry_color_depths & INQ_COLOR_DEPTH_4 ? "4 bit " : "",
+ dev->inquiry_color_depths & INQ_COLOR_DEPTH_1 ? "1 bit " : "",
+ dev->inquiry_color_depths);
+
+ DBG (DBG_inquiry, "Color Format..................: %s%s%s (%02x)\n",
+ dev->inquiry_color_format & INQ_COLOR_FORMAT_INDEX ? "Indexed " : "",
+ dev->inquiry_color_format & INQ_COLOR_FORMAT_LINE ? "Line " : "",
+ dev->inquiry_color_format & INQ_COLOR_FORMAT_PIXEL ? "Pixel " : "",
+ dev->inquiry_color_format);
+
+ DBG (DBG_inquiry, "Image Format..................: %s%s%s%s (%02x)\n",
+ dev->inquiry_image_format & INQ_IMG_FMT_OKLINE ? "OKLine " : "",
+ dev->inquiry_image_format & INQ_IMG_FMT_BLK_ONE ? "BlackOne " : "",
+ dev->inquiry_image_format & INQ_IMG_FMT_MOTOROLA ? "Motorola " : "",
+ dev->inquiry_image_format & INQ_IMG_FMT_INTEL ? "Intel" : "",
+ dev->inquiry_image_format);
+
+ DBG (DBG_inquiry,
+ "Scan Capability...............: %s%s%s%s%d speeds (%02x)\n",
+ dev->inquiry_scan_capability & INQ_CAP_PWRSAV ? "PowerSave " : "",
+ dev->inquiry_scan_capability & INQ_CAP_EXT_CAL ? "ExtCal " : "",
+ dev->inquiry_scan_capability & INQ_CAP_FAST_PREVIEW ? "FastPreview" :
+ "",
+ dev->inquiry_scan_capability & INQ_CAP_DISABLE_CAL ? "DisCal " : "",
+ dev->inquiry_scan_capability & INQ_CAP_SPEEDS,
+ dev->inquiry_scan_capability);
+
+ DBG (DBG_inquiry, "Optional Devices..............: %s%s%s%s (%02x)\n",
+ dev->inquiry_optional_devices & INQ_OPT_DEV_MPCL ? "MultiPageLoad " :
+ "",
+ dev->inquiry_optional_devices & INQ_OPT_DEV_TP1 ? "TransModule1 " : "",
+ dev->inquiry_optional_devices & INQ_OPT_DEV_TP ? "TransModule " : "",
+ dev->inquiry_optional_devices & INQ_OPT_DEV_ADF ? "ADF " : "",
+ dev->inquiry_optional_devices);
+
+ DBG (DBG_inquiry, "Enhancement...................: %02x\n",
+ dev->inquiry_enhancements);
+ DBG (DBG_inquiry, "Gamma bits....................: %d\n",
+ dev->inquiry_gamma_bits);
+
+ DBG (DBG_inquiry, "Fast Preview Resolution.......: %d\n",
+ dev->inquiry_fast_preview_res);
+ DBG (DBG_inquiry, "Min Highlight.................: %d\n",
+ dev->inquiry_min_highlight);
+ DBG (DBG_inquiry, "Max Shadow....................: %d\n",
+ dev->inquiry_max_shadow);
+ DBG (DBG_inquiry, "Cal Eqn.......................: %d\n",
+ dev->inquiry_cal_eqn);
+ DBG (DBG_inquiry, "Min Exposure..................: %d\n",
+ dev->inquiry_min_exp);
+ DBG (DBG_inquiry, "Max Exposure..................: %d\n",
+ dev->inquiry_max_exp);
+}
+
+
+/* ------------------------------ PIE GET INQUIRY VALUES -------------------- */
+
+
+static void
+pie_get_inquiry_values (Pie_Device * dev, unsigned char *buffer)
+{
+ DBG (DBG_proc, "get_inquiry_values\n");
+
+ dev->inquiry_len = get_inquiry_additional_length (buffer) + 5;
+
+ get_inquiry_vendor ((char *) buffer, dev->vendor);
+ dev->vendor[8] = '\0';
+ get_inquiry_product ((char *) buffer, dev->product);
+ dev->product[16] = '\0';
+ get_inquiry_version ((char *) buffer, dev->version);
+ dev->version[4] = '\0';
+
+ dev->inquiry_x_res = get_inquiry_max_x_res (buffer);
+ dev->inquiry_y_res = get_inquiry_max_y_res (buffer);
+
+ if (dev->inquiry_y_res < 256)
+ {
+ /* y res is a multiplier */
+ dev->inquiry_pixel_resolution = dev->inquiry_x_res;
+ dev->inquiry_x_res *= dev->inquiry_y_res;
+ dev->inquiry_y_res = dev->inquiry_x_res;
+ }
+ else
+ {
+ /* y res really is resolution */
+ dev->inquiry_pixel_resolution =
+ min (dev->inquiry_x_res, dev->inquiry_y_res);
+ }
+
+ dev->inquiry_fb_width =
+ (double) get_inquiry_fb_max_scan_width (buffer) /
+ dev->inquiry_pixel_resolution;
+ dev->inquiry_fb_length =
+ (double) get_inquiry_fb_max_scan_length (buffer) /
+ dev->inquiry_pixel_resolution;
+
+ dev->inquiry_trans_top_left_x = get_inquiry_trans_x1 (buffer);
+ dev->inquiry_trans_top_left_y = get_inquiry_trans_y1 (buffer);
+
+ dev->inquiry_trans_width =
+ (double) (get_inquiry_trans_x2 (buffer) -
+ get_inquiry_trans_x1 (buffer)) / dev->inquiry_pixel_resolution;
+ dev->inquiry_trans_length =
+ (double) (get_inquiry_trans_y2 (buffer) -
+ get_inquiry_trans_y1 (buffer)) / dev->inquiry_pixel_resolution;
+
+ dev->inquiry_halftones = get_inquiry_halftones (buffer) & 0x0f;
+
+ dev->inquiry_filters = get_inquiry_filters (buffer);
+ dev->inquiry_color_depths = get_inquiry_color_depths (buffer);
+ dev->inquiry_color_format = get_inquiry_color_format (buffer);
+ dev->inquiry_image_format = get_inquiry_image_format (buffer);
+
+ dev->inquiry_scan_capability = get_inquiry_scan_capability (buffer);
+ dev->inquiry_optional_devices = get_inquiry_optional_devices (buffer);
+ dev->inquiry_enhancements = get_inquiry_enhancements (buffer);
+ dev->inquiry_gamma_bits = get_inquiry_gamma_bits (buffer);
+ dev->inquiry_fast_preview_res = get_inquiry_fast_preview_res (buffer);
+ dev->inquiry_min_highlight = get_inquiry_min_highlight (buffer);
+ dev->inquiry_max_shadow = get_inquiry_max_shadow (buffer);
+ dev->inquiry_cal_eqn = get_inquiry_cal_eqn (buffer);
+ dev->inquiry_min_exp = get_inquiry_min_exp (buffer);
+ dev->inquiry_max_exp = get_inquiry_max_exp (buffer);
+
+ pie_print_inquiry (dev);
+
+ return;
+}
+
+/* ----------------------------- PIE DO INQUIRY ---------------------------- */
+
+
+static void
+pie_do_inquiry (int sfd, unsigned char *buffer)
+{
+ size_t size;
+ SANE_Status status;
+
+ DBG (DBG_proc, "do_inquiry\n");
+ memset (buffer, '\0', 256); /* clear buffer */
+
+ size = 5;
+
+ set_inquiry_return_size (inquiry.cmd, size); /* first get only 5 bytes to get size of inquiry_return_block */
+ status = sanei_scsi_cmd (sfd, inquiry.cmd, inquiry.size, buffer, &size);
+ if (status)
+ {
+ DBG (DBG_error, "pie_do_inquiry: command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ size = get_inquiry_additional_length (buffer) + 5;
+
+ set_inquiry_return_size (inquiry.cmd, size); /* then get inquiry with actual size */
+ status = sanei_scsi_cmd (sfd, inquiry.cmd, inquiry.size, buffer, &size);
+ if (status)
+ {
+ DBG (DBG_error, "pie_do_inquiry: command returned status %s\n",
+ sane_strstatus (status));
+ }
+}
+
+/* ---------------------- PIE IDENTIFY SCANNER ---------------------- */
+
+
+static int
+pie_identify_scanner (Pie_Device * dev, int sfd)
+{
+ char vendor[9];
+ char product[0x11];
+ char version[5];
+ char *pp;
+ int i = 0;
+ unsigned char inquiry_block[256];
+
+ DBG (DBG_proc, "identify_scanner\n");
+
+ pie_do_inquiry (sfd, inquiry_block); /* get inquiry */
+
+ if (get_inquiry_periph_devtype (inquiry_block) != IN_periph_devtype_scanner)
+ {
+ return 1;
+ } /* no scanner */
+
+ get_inquiry_vendor ((char *) inquiry_block, vendor);
+ get_inquiry_product ((char *) inquiry_block, product);
+ get_inquiry_version ((char *) inquiry_block, version);
+
+ pp = &vendor[8];
+ vendor[8] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &product[0x10];
+ product[0x10] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &version[4];
+
+ version[4] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ DBG (DBG_info, "Found %s scanner %s version %s on device %s\n", vendor,
+ product, version, dev->devicename);
+
+ while (strncmp ("END_OF_LIST", scanner_str[2 * i], 11) != 0) /* Now identify full supported scanners */
+ {
+ if (!strncmp (vendor, scanner_str[2 * i], strlen (scanner_str[2 * i])))
+ {
+ if (!strncmp
+ (product, scanner_str[2 * i + 1],
+ strlen (scanner_str[2 * i + 1])))
+ {
+ DBG (DBG_info, "found supported scanner\n");
+
+ pie_get_inquiry_values (dev, inquiry_block);
+ return 0;
+ }
+ }
+ i++;
+ }
+
+ return 1; /* NO SUPPORTED SCANNER: short inquiry-block and unknown scanner */
+}
+
+
+/* ------------------------------- GET SPEEDS ----------------------------- */
+
+static void
+pie_get_speeds (Pie_Device * dev)
+{
+ int speeds = dev->inquiry_scan_capability & INQ_CAP_SPEEDS;
+
+ DBG (DBG_proc, "get_speeds\n");
+
+ if (speeds == 3)
+ {
+ dev->speed_list[0] = strdup ("Normal");
+ dev->speed_list[1] = strdup ("Fine");
+ dev->speed_list[2] = strdup ("Pro");
+ dev->speed_list[3] = NULL;
+ }
+ else
+ {
+ int i;
+ char buf[2];
+
+ buf[1] = '\0';
+
+ for (i = 0; i < speeds; i++)
+ {
+ buf[0] = '1' + i;
+ dev->speed_list[i] = strdup (buf);
+ }
+
+ dev->speed_list[i] = NULL;
+ }
+}
+
+/* ------------------------------- GET HALFTONES ----------------------------- */
+
+static void
+pie_get_halftones (Pie_Device * dev, int sfd)
+{
+ int i;
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+ unsigned char buffer[128];
+
+ DBG (DBG_proc, "get_halftones\n");
+
+ for (i = 0; i < dev->inquiry_halftones; i++)
+ {
+ size = 6;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, READ_HALFTONE);
+ set_data_length (data, 2);
+ data[4] = i;
+
+ status = sanei_scsi_cmd (sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_get_halftones: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ /* now read the halftone data */
+ memset (buffer, '\0', sizeof buffer); /* clear buffer */
+
+ size = 128;
+ set_read_length (sread.cmd, size);
+
+ DBG (DBG_info, "doing read\n");
+ status = sanei_scsi_cmd (sfd, sread.cmd, sread.size, buffer, &size);
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_get_halftones: read command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ unsigned char *s;
+
+ s = buffer + 8 + buffer[6] * buffer[7];
+
+ DBG (DBG_info, "halftone %d: %s\n", i, s);
+
+ dev->halftone_list[i] = strdup ((char *)s);
+ }
+ }
+ }
+ dev->halftone_list[i] = NULL;
+}
+
+/* ------------------------------- GET CAL DATA ----------------------------- */
+
+static void
+pie_get_cal_info (Pie_Device * dev, int sfd)
+{
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+ unsigned char buffer[280];
+
+ DBG (DBG_proc, "get_cal_info\n");
+
+ if (!(dev->inquiry_scan_capability & INQ_CAP_EXT_CAL))
+ return;
+
+ size = 6;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, READ_CAL_INFO);
+
+ status = sanei_scsi_cmd (sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_get_cal_info: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ /* now read the cal data */
+ memset (buffer, '\0', sizeof buffer); /* clear buffer */
+
+ size = 128;
+ set_read_length (sread.cmd, size);
+
+ DBG (DBG_info, "doing read\n");
+ status = sanei_scsi_cmd (sfd, sread.cmd, sread.size, buffer, &size);
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_get_cal_info: read command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ int i;
+
+ dev->cal_info_count = buffer[4];
+ dev->cal_info =
+ malloc (sizeof (struct Pie_cal_info) * dev->cal_info_count);
+
+ for (i = 0; i < dev->cal_info_count; i++)
+ {
+ dev->cal_info[i].cal_type = buffer[8 + i * buffer[5]];
+ dev->cal_info[i].send_bits = buffer[9 + i * buffer[5]];
+ dev->cal_info[i].receive_bits = buffer[10 + i * buffer[5]];
+ dev->cal_info[i].num_lines = buffer[11 + i * buffer[5]];
+ dev->cal_info[i].pixels_per_line =
+ (buffer[13 + i * buffer[5]] << 8) + buffer[12 +
+ i * buffer[5]];
+
+ DBG (DBG_info2, "%02x %2d %2d %2d %d\n",
+ dev->cal_info[i].cal_type, dev->cal_info[i].send_bits,
+ dev->cal_info[i].receive_bits, dev->cal_info[i].num_lines,
+ dev->cal_info[i].pixels_per_line);
+ }
+ }
+ }
+}
+
+/* ------------------------------- ATTACH SCANNER ----------------------------- */
+
+static SANE_Status
+attach_scanner (const char *devicename, Pie_Device ** devp)
+{
+ Pie_Device *dev;
+ int sfd;
+ int bufsize;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ bufsize = 16384; /* 16KB */
+
+ if (sanei_scsi_open_extended
+ (devicename, &sfd, sense_handler, dev, &bufsize) != 0)
+ {
+ DBG (DBG_error, "attach_scanner: open failed\n");
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (bufsize < 4096) /* < 4KB */
+ {
+ DBG (DBG_error,
+ "attach_scanner: sanei_scsi_open_extended returned too small scsi buffer (%d)\n",
+ bufsize);
+ sanei_scsi_close (sfd);
+ free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info,
+ "attach_scanner: sanei_scsi_open_extended returned scsi buffer size = %d\n",
+ bufsize);
+#else
+ bufsize = sanei_scsi_max_request_size;
+
+ if (sanei_scsi_open (devicename, &sfd, sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "attach_scanner: open failed\n");
+ free (dev);
+
+ return SANE_STATUS_INVAL;
+
+ }
+#endif
+
+ pie_init (dev); /* preset values in structure dev */
+
+ dev->devicename = strdup (devicename);
+
+ if (pie_identify_scanner (dev, sfd) != 0)
+ {
+ DBG (DBG_error, "attach_scanner: scanner-identification failed\n");
+ sanei_scsi_close (sfd);
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ pie_get_halftones (dev, sfd);
+ pie_get_cal_info (dev, sfd);
+ pie_get_speeds (dev);
+
+ dev->scan_mode_list[0] = COLOR_STR;
+ dev->scan_mode_list[1] = GRAY_STR;
+ dev->scan_mode_list[2] = LINEART_STR;
+ dev->scan_mode_list[3] = HALFTONE_STR;
+ dev->scan_mode_list[4] = 0;
+
+ sanei_scsi_close (sfd);
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.model = dev->product;
+ dev->sane.type = "flatbed scanner";
+
+ dev->x_range.min = SANE_FIX (0);
+ dev->x_range.quant = SANE_FIX (0);
+ dev->x_range.max = SANE_FIX (dev->inquiry_fb_width * MM_PER_INCH);
+
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.quant = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (dev->inquiry_fb_length * MM_PER_INCH);
+
+ dev->dpi_range.min = SANE_FIX (25);
+ dev->dpi_range.quant = SANE_FIX (1);
+ dev->dpi_range.max =
+ SANE_FIX (max (dev->inquiry_x_res, dev->inquiry_y_res));
+
+ dev->shadow_range.min = SANE_FIX (0);
+ dev->shadow_range.quant = SANE_FIX (1);
+ dev->shadow_range.max = SANE_FIX (dev->inquiry_max_shadow);
+
+ dev->highlight_range.min = SANE_FIX (dev->inquiry_min_highlight);
+ dev->highlight_range.quant = SANE_FIX (1);
+ dev->highlight_range.max = SANE_FIX (100);
+
+ dev->exposure_range.min = SANE_FIX (dev->inquiry_min_exp);
+ dev->exposure_range.quant = SANE_FIX (1);
+ dev->exposure_range.max = SANE_FIX (dev->inquiry_max_exp);
+
+#if 0
+ dev->analog_gamma_range.min = SANE_FIX (1.0);
+ dev->analog_gamma_range.quant = SANE_FIX (0.01);
+ dev->analog_gamma_range.max = SANE_FIX (2.0);
+
+#endif
+
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* --------------------------- MAX STRING SIZE ---------------------------- */
+
+
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+
+/* --------------------------- INIT OPTIONS ------------------------------- */
+
+
+static SANE_Status
+init_options (Pie_Scanner * scanner)
+{
+ int i;
+
+ DBG (DBG_sane_proc, "init_options\n");
+
+ memset (scanner->opt, 0, sizeof (scanner->opt));
+ memset (scanner->val, 0, sizeof (scanner->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ scanner->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ scanner->opt[OPT_MODE_GROUP].desc = "";
+ scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_MODE_GROUP].cap = 0;
+ scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_MODE].size =
+ max_string_size ((SANE_String_Const *) scanner->device->scan_mode_list);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list =
+ (SANE_String_Const *) scanner->device->scan_mode_list;
+ scanner->val[OPT_MODE].s =
+ (SANE_Char *) strdup (scanner->device->scan_mode_list[0]);
+
+ /* x-resolution */
+ scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_RESOLUTION].constraint.range = &scanner->device->dpi_range;
+ scanner->val[OPT_RESOLUTION].w = 100 << SANE_FIXED_SCALE_SHIFT;
+
+ /* "Geometry" group: */
+
+ scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
+ scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_X].constraint.range = &(scanner->device->x_range);
+ scanner->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_X].constraint.range = &(scanner->device->x_range);
+ scanner->val[OPT_BR_X].w = scanner->device->x_range.max;
+
+ /* bottom-right y */
+ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_BR_Y].w = scanner->device->y_range.max;
+
+ /* "enhancement" group: */
+
+ scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* grayscale gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR].wa = scanner->gamma_table[0];
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &scanner->gamma_range;
+ scanner->opt[OPT_GAMMA_VECTOR].size =
+ scanner->gamma_length * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+
+ /* red gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_R].wa = scanner->gamma_table[1];
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_R].size =
+ scanner->gamma_length * sizeof (SANE_Word);
+
+ /* green gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_G].wa = scanner->gamma_table[2];
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_G].size =
+ scanner->gamma_length * sizeof (SANE_Word);
+
+
+ /* blue gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_B].wa = scanner->gamma_table[3];
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_B].size =
+ scanner->gamma_length * sizeof (SANE_Word);
+
+ /* halftone pattern */
+ scanner->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_HALFTONE_PATTERN].size =
+ max_string_size ((SANE_String_Const *) scanner->device->halftone_list);
+ scanner->opt[OPT_HALFTONE_PATTERN].constraint_type =
+ SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
+ (SANE_String_Const *) scanner->device->halftone_list;
+ scanner->val[OPT_HALFTONE_PATTERN].s =
+ (SANE_Char *) strdup (scanner->device->halftone_list[0]);
+ scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ /* speed */
+ scanner->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ scanner->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ scanner->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ scanner->opt[OPT_SPEED].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_SPEED].size =
+ max_string_size ((SANE_String_Const *) scanner->device->speed_list);
+ scanner->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_SPEED].constraint.string_list =
+ (SANE_String_Const *) scanner->device->speed_list;
+ scanner->val[OPT_SPEED].s =
+ (SANE_Char *) strdup (scanner->device->speed_list[0]);
+
+ /* lineart threshold */
+ scanner->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_THRESHOLD].constraint.range = &percentage_range_100;
+ scanner->val[OPT_THRESHOLD].w = SANE_FIX (50);
+ scanner->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ /* "advanced" group: */
+
+ scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
+ scanner->opt[OPT_ADVANCED_GROUP].desc = "";
+ scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* preview */
+ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_PREVIEW].w = SANE_FALSE;
+
+
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*------------------------- PIE POWER SAVE -----------------------------*/
+
+static SANE_Status
+pie_power_save (Pie_Scanner * scanner, int time)
+{
+ unsigned char buffer[128];
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+
+ DBG (DBG_proc, "pie_power_save: %d min\n", time);
+
+ size = 6;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, SET_POWER_SAVE_CONTROL);
+ set_data_length (data, size - 4);
+ data[4] = time & 0x7f;
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_power_save: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+/*------------------------- PIE SEND EXPOSURE ONE -----------------------------*/
+
+
+static SANE_Status
+pie_send_exposure_one (Pie_Scanner * scanner, int filter, int value)
+{
+ unsigned char buffer[128];
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+
+ DBG (DBG_proc, "pie_send_exposure_one\n");
+
+ size = 8;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, SET_EXP_TIME);
+ set_data_length (data, size - 4);
+
+ data[4] = filter;
+
+ set_data (data, 6, (int) value, 2);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_send_exposure_one: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+/*------------------------- PIE SEND EXPOSURE -----------------------------*/
+
+static SANE_Status
+pie_send_exposure (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "pie_send_exposure\n");
+
+ status = pie_send_exposure_one (scanner, FILTER_RED, 100);
+ if (status)
+ return status;
+
+ status = pie_send_exposure_one (scanner, FILTER_GREEN, 100);
+ if (status)
+ return status;
+
+ status = pie_send_exposure_one (scanner, FILTER_BLUE, 100);
+ if (status)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*------------------------- PIE SEND HIGHLIGHT/SHADOW ONE -----------------------------*/
+
+static SANE_Status
+pie_send_highlight_shadow_one (Pie_Scanner * scanner, int filter,
+ int highlight, int shadow)
+{
+ unsigned char buffer[128];
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+
+ DBG (DBG_proc, "pie_send_highlight_shadow_one\n");
+
+ size = 8;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, SET_EXP_TIME);
+ set_data_length (data, size - 4);
+
+ data[4] = filter;
+
+ data[6] = highlight;
+ data[7] = shadow;
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_send_highlight_shadow_one: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+/*------------------------- PIE SEND HIGHLIGHT/SHADOW -----------------------------*/
+
+static SANE_Status
+pie_send_highlight_shadow (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "pie_send_highlight_shadow\n");
+
+ status = pie_send_highlight_shadow_one (scanner, FILTER_RED, 100, 0);
+ if (status)
+ return status;
+
+ status = pie_send_highlight_shadow_one (scanner, FILTER_GREEN, 100, 0);
+ if (status)
+ return status;
+
+ status = pie_send_highlight_shadow_one (scanner, FILTER_BLUE, 100, 0);
+ if (status)
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*------------------------- PIE PERFORM CAL ----------------------------*/
+
+static SANE_Status
+pie_perform_cal (Pie_Scanner * scanner, int cal_index)
+{
+ long *red_result;
+ long *green_result;
+ long *blue_result;
+ long *neutral_result;
+ long *result = NULL;
+ int rcv_length, send_length;
+ int rcv_lines, rcv_bits, send_bits;
+ int pixels_per_line;
+ int i;
+ unsigned char *rcv_buffer, *rcv_ptr;
+ unsigned char *send_buffer, *send_ptr;
+ size_t size;
+ int fullscale;
+ int cal_limit;
+ int k;
+ int filter;
+ SANE_Status status;
+
+ DBG (DBG_proc, "pie_perform_cal\n");
+
+ pixels_per_line = scanner->device->cal_info[cal_index].pixels_per_line;
+ rcv_length = pixels_per_line;
+ send_length = pixels_per_line;
+
+ rcv_bits = scanner->device->cal_info[cal_index].receive_bits;
+ if (rcv_bits > 8)
+ rcv_length *= 2; /* 2 bytes / sample */
+
+ send_bits = scanner->device->cal_info[cal_index].send_bits;
+ if (send_bits > 8)
+ send_length *= 2; /* 2 bytes / sample */
+
+ rcv_lines = scanner->device->cal_info[cal_index].num_lines;
+
+ send_length += 2; /* space for filter at start */
+
+ if (scanner->colormode == RGB)
+ {
+ rcv_lines *= 3;
+ send_length *= 3;
+ rcv_length += 2; /* 2 bytes for index at front of data (only in RGB??) */
+ }
+
+ send_length += 4; /* space for header at start of data */
+
+ /* alllocate buffers for the receive data, the result buffers, and for the send data */
+ rcv_buffer = (unsigned char *) malloc (rcv_length);
+
+ red_result = (long *) calloc (pixels_per_line, sizeof (long));
+ green_result = (long *) calloc (pixels_per_line, sizeof (long));
+ blue_result = (long *) calloc (pixels_per_line, sizeof (long));
+ neutral_result = (long *) calloc (pixels_per_line, sizeof (long));
+
+ if (!rcv_buffer || !red_result || !green_result || !blue_result
+ || !neutral_result)
+ {
+ /* at least one malloc failed, so free all buffers (free accepts NULL) */
+ free (rcv_buffer);
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* read the cal data a line at a time, and accumulate into the result arrays */
+ while (rcv_lines--)
+ {
+ /* TUR */
+ status = pie_wait_scanner (scanner);
+ if (status)
+ {
+ free (rcv_buffer);
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ return status;
+ }
+
+ set_read_length (sread.cmd, 1);
+ size = rcv_length;
+
+ DBG (DBG_info, "pie_perform_cal: reading 1 line (%lu bytes)\n", (u_long) size);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, sread.cmd, sread.size, rcv_buffer,
+ &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_perform_cal: read command returned status %s\n",
+ sane_strstatus (status));
+ free (rcv_buffer);
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ return status;
+ }
+
+ DBG_DUMP (DBG_dump, rcv_buffer, 32);
+
+ /* which result buffer does this line belong to? */
+ if (scanner->colormode == RGB)
+ {
+ if (*rcv_buffer == 'R')
+ result = red_result;
+ else if (*rcv_buffer == 'G')
+ result = green_result;
+ else if (*rcv_buffer == 'B')
+ result = blue_result;
+ else if (*rcv_buffer == 'N')
+ result = neutral_result;
+ else
+ {
+ DBG (DBG_error, "pie_perform_cal: invalid index byte (%02x)\n",
+ *rcv_buffer);
+ DBG_DUMP (DBG_error, rcv_buffer, 32);
+ free (rcv_buffer);
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ return SANE_STATUS_INVAL;
+ }
+ rcv_ptr = rcv_buffer + 2;
+ }
+ else
+ {
+ /* monochrome - no bytes indicating filter here */
+ result = neutral_result;
+ rcv_ptr = rcv_buffer;
+ }
+
+ /* now add the values in this line to the result array */
+ for (i = 0; i < pixels_per_line; i++)
+ {
+ result[i] += *rcv_ptr++;
+ if (rcv_bits > 8)
+ {
+ result[i] += (*rcv_ptr++) << 8;
+ }
+ }
+ }
+
+ /* got all the cal data, now process it ready to send back */
+ free (rcv_buffer);
+ send_buffer = (unsigned char *) malloc (send_length + swrite.size);
+
+ if (!send_buffer)
+ {
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ rcv_lines = scanner->device->cal_info[cal_index].num_lines;
+ fullscale = (1 << rcv_bits) - 1;
+ cal_limit = fullscale / (1 << scanner->device->inquiry_cal_eqn);
+ k = (1 << scanner->device->inquiry_cal_eqn) - 1;
+
+ /* set up scsi command and data */
+ size = send_length;
+
+ memcpy (send_buffer, swrite.cmd, swrite.size);
+ set_write_length (send_buffer, size);
+
+ set_command (send_buffer + swrite.size, SEND_CAL_DATA);
+ set_data_length (send_buffer + swrite.size, size - 4);
+
+ send_ptr = send_buffer + swrite.size + 4;
+
+ for (filter = FILTER_NEUTRAL; filter <= FILTER_BLUE; filter <<= 1)
+ {
+
+ /* only send data for filter we expect to send */
+ if (!(filter & scanner->cal_filter))
+ continue;
+
+ set_data (send_ptr, 0, filter, 2);
+ send_ptr += 2;
+
+ if (scanner->colormode == RGB)
+ {
+ switch (filter)
+ {
+ case FILTER_RED:
+ result = red_result;
+ break;
+
+ case FILTER_GREEN:
+ result = green_result;
+ break;
+
+ case FILTER_BLUE:
+ result = blue_result;
+ break;
+
+ case FILTER_NEUTRAL:
+ result = neutral_result;
+ break;
+ }
+ }
+ else
+ result = neutral_result;
+
+ /* for each pixel */
+ for (i = 0; i < pixels_per_line; i++)
+ {
+ long x;
+
+ /* make average */
+ x = result[i] / rcv_lines;
+
+ /* ensure not overflowed */
+ if (x > fullscale)
+ x = fullscale;
+
+ /* process according to required calibration equation */
+ if (scanner->device->inquiry_cal_eqn)
+ {
+ if (x <= cal_limit)
+ x = fullscale;
+ else
+ x = ((fullscale - x) * fullscale) / (x * k);
+ }
+
+ if (rcv_bits > send_bits)
+ x >>= (rcv_bits - send_bits);
+ else if (send_bits > rcv_bits)
+ x <<= (send_bits - rcv_bits);
+
+ /* put result into send buffer */
+ *send_ptr++ = x;
+ if (send_bits > 8)
+ *send_ptr++ = x >> 8;
+ }
+ }
+
+ /* now send the data back to scanner */
+
+ /* TUR */
+ status = pie_wait_scanner (scanner);
+ if (status)
+ {
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ free (send_buffer);
+ return status;
+ }
+
+ DBG (DBG_info, "pie_perform_cal: sending cal data (%lu bytes)\n", (u_long) size);
+ DBG_DUMP (DBG_dump, send_buffer, 64);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, send_buffer, swrite.size + size, NULL,
+ NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_perform_cal: write command returned status %s\n",
+ sane_strstatus (status));
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ free (send_buffer);
+ return status;
+ }
+
+ free (red_result);
+ free (green_result);
+ free (blue_result);
+ free (neutral_result);
+ free (send_buffer);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*------------------------- PIE DO CAL -----------------------------*/
+
+static SANE_Status
+pie_do_cal (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+ int cal_index;
+
+ DBG (DBG_proc, "pie_do_cal\n");
+
+ if (scanner->device->inquiry_scan_capability & INQ_CAP_EXT_CAL)
+ {
+ for (cal_index = 0; cal_index < scanner->device->cal_info_count;
+ cal_index++)
+ if (scanner->device->cal_info[cal_index].cal_type ==
+ scanner->cal_mode)
+ {
+ status = pie_perform_cal (scanner, cal_index);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*------------------------- PIE DWNLD GAMMA ONE -----------------------------*/
+
+static SANE_Status
+pie_dwnld_gamma_one (Pie_Scanner * scanner, int filter, SANE_Int * table)
+{
+ unsigned char *buffer;
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+ int i;
+
+ DBG (DBG_proc, "pie_dwnld_gamma_one\n");
+
+ /* TUR */
+ status = pie_wait_scanner (scanner);
+ if (status)
+ {
+ return status;
+ }
+
+ if (scanner->device->inquiry_gamma_bits > 8)
+ size = scanner->gamma_length * 2 + 6;
+ else
+ size = scanner->gamma_length + 6;
+
+ buffer = malloc (size + swrite.size);
+ if (!buffer)
+ return SANE_STATUS_NO_MEM;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, DWNLD_GAMMA_TABLE);
+ set_data_length (data, size - 4);
+
+ data[4] = filter;
+
+ for (i = 0; i < scanner->gamma_length; i++)
+ {
+ if (scanner->device->inquiry_gamma_bits > 8)
+ {
+ set_data (data, 6 + 2 * i, table ? table[i] : i, 2);
+ }
+ else
+ {
+ set_data (data, 6 + i, table ? table[i] : i, 1);
+ }
+ }
+
+ DBG_DUMP (DBG_dump, data, 128);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error,
+ "pie_dwnld_gamma_one: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ free (buffer);
+
+ return status;
+}
+
+/*------------------------- PIE DWNLD GAMMA -----------------------------*/
+
+static SANE_Status
+pie_dwnld_gamma (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "pie_dwnld_gamma\n");
+
+ if (scanner->colormode == RGB)
+ {
+ status =
+ pie_dwnld_gamma_one (scanner, FILTER_RED, scanner->gamma_table[1]);
+ if (status)
+ return status;
+
+
+ status =
+ pie_dwnld_gamma_one (scanner, FILTER_GREEN, scanner->gamma_table[2]);
+ if (status)
+ return status;
+
+ status =
+ pie_dwnld_gamma_one (scanner, FILTER_BLUE, scanner->gamma_table[3]);
+ if (status)
+ return status;
+ }
+ else
+ {
+ SANE_Int *table;
+
+ /* if lineart or half tone, force gamma to be one to one by passing NULL */
+ if (scanner->colormode == GRAYSCALE)
+ table = scanner->gamma_table[0];
+ else
+ table = NULL;
+
+ status = pie_dwnld_gamma_one (scanner, FILTER_GREEN, table);
+ if (status)
+ return status;
+ }
+
+ usleep (DOWNLOAD_GAMMA_WAIT_TIME);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*------------------------- PIE SET WINDOW -----------------------------*/
+
+static SANE_Status
+pie_set_window (Pie_Scanner * scanner)
+{
+ unsigned char buffer[128];
+ size_t size;
+ SANE_Status status;
+ unsigned char *data;
+ double x, dpmm;
+
+ DBG (DBG_proc, "pie_set_window\n");
+
+ size = 14;
+
+ set_write_length (swrite.cmd, size);
+
+ memcpy (buffer, swrite.cmd, swrite.size);
+
+ data = buffer + swrite.size;
+ memset (data, 0, size);
+
+ set_command (data, SET_SCAN_FRAME);
+ set_data_length (data, size - 4);
+
+ data[4] = 0x80;
+ if (scanner->colormode == HALFTONE)
+ data[4] |= 0x40;
+
+ dpmm = (double) scanner->device->inquiry_pixel_resolution / MM_PER_INCH;
+
+ x = SANE_UNFIX (scanner->val[OPT_TL_X].w) * dpmm;
+ set_data (data, 6, (int) x, 2);
+ DBG (DBG_info, "TL_X: %d\n", (int) x);
+
+ x = SANE_UNFIX (scanner->val[OPT_TL_Y].w) * dpmm;
+ set_data (data, 8, (int) x, 2);
+ DBG (DBG_info, "TL_Y: %d\n", (int) x);
+
+ x = SANE_UNFIX (scanner->val[OPT_BR_X].w) * dpmm;
+ set_data (data, 10, (int) x, 2);
+ DBG (DBG_info, "BR_X: %d\n", (int) x);
+
+ x = SANE_UNFIX (scanner->val[OPT_BR_Y].w) * dpmm;
+ set_data (data, 12, (int) x, 2);
+ DBG (DBG_info, "BR_Y: %d\n", (int) x);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, buffer, swrite.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_set_window: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+
+/*------------------------- PIE MODE SELECT -----------------------------*/
+
+static SANE_Status
+pie_mode_select (Pie_Scanner * scanner)
+{
+
+ SANE_Status status;
+ unsigned char buffer[128];
+ size_t size;
+ unsigned char *data;
+ int i;
+
+ DBG (DBG_proc, "pie_mode_select\n");
+
+ size = 14;
+
+ set_mode_length (smode.cmd, size);
+
+ memcpy (buffer, smode.cmd, smode.size);
+
+ data = buffer + smode.size;
+ memset (data, 0, size);
+
+ /* size of data */
+ data[1] = size - 2;
+
+ /* set resolution required */
+ set_data (data, 2, scanner->resolution, 2);
+
+ /* set color filter and color depth */
+ switch (scanner->colormode)
+ {
+ case RGB:
+ if (scanner->device->inquiry_filters & INQ_ONE_PASS_COLOR)
+ {
+ data[4] = INQ_ONE_PASS_COLOR;
+ scanner->cal_filter = FILTER_RED | FILTER_GREEN | FILTER_BLUE;
+ }
+ else
+ {
+ DBG (DBG_error,
+ "pie_mode_select: support for multipass color not yet implemented\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ data[5] = INQ_COLOR_DEPTH_8;
+ break;
+
+ case GRAYSCALE:
+ case LINEART:
+ case HALFTONE:
+ /* choose which filter to use for monochrome mode */
+ if (scanner->device->inquiry_filters & INQ_FILTER_NEUTRAL)
+ {
+ data[4] = FILTER_NEUTRAL;
+ scanner->cal_filter = FILTER_NEUTRAL;
+ }
+ else if (scanner->device->inquiry_filters & INQ_FILTER_GREEN)
+ {
+ data[4] = FILTER_GREEN;
+ scanner->cal_filter = FILTER_GREEN;
+ }
+ else if (scanner->device->inquiry_filters & INQ_FILTER_RED)
+ {
+ data[4] = FILTER_RED;
+ scanner->cal_filter = FILTER_RED;
+ }
+ else if (scanner->device->inquiry_filters & INQ_FILTER_BLUE)
+ {
+ data[4] = FILTER_BLUE;
+ scanner->cal_filter = FILTER_BLUE;
+ }
+ else
+ {
+ DBG (DBG_error,
+ "pie_mode_select: scanner doesn't appear to support monochrome\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (scanner->colormode == GRAYSCALE)
+ data[5] = INQ_COLOR_DEPTH_8;
+ else
+ data[5] = INQ_COLOR_DEPTH_1;
+ break;
+ }
+
+ /* choose color packing method */
+ if (scanner->device->inquiry_color_format & INQ_COLOR_FORMAT_LINE)
+ data[6] = INQ_COLOR_FORMAT_LINE;
+ else if (scanner->device->inquiry_color_format & INQ_COLOR_FORMAT_INDEX)
+ data[6] = INQ_COLOR_FORMAT_INDEX;
+ else
+ {
+ DBG (DBG_error,
+ "pie_mode_select: support for pixel packing not yet implemented\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* choose data format */
+ if (scanner->device->inquiry_image_format & INQ_IMG_FMT_INTEL)
+ data[8] = INQ_IMG_FMT_INTEL;
+ else
+ {
+ DBG (DBG_error,
+ "pie_mode_select: support for Motorola format not yet implemented\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* set required speed */
+ i = 0;
+ while (scanner->device->speed_list[i] != NULL)
+ {
+ if (strcmp (scanner->device->speed_list[i], scanner->val[OPT_SPEED].s)
+ == 0)
+ break;
+ i++;
+ }
+
+ if (scanner->device->speed_list[i] == NULL)
+ data[9] = 0;
+ else
+ data[9] = i;
+
+ scanner->cal_mode = CAL_MODE_FLATBED;
+
+ /* if preview supported, ask for preview, limit resolution to max for fast preview */
+ if (scanner->val[OPT_PREVIEW].w
+ && (scanner->device->inquiry_scan_capability & INQ_CAP_FAST_PREVIEW))
+ {
+ DBG (DBG_info, "pie_mode_select: setting preview\n");
+ scanner->cal_mode |= CAL_MODE_PREVIEW;
+ data[9] |= INQ_CAP_FAST_PREVIEW;
+ data[9] &= ~INQ_CAP_SPEEDS;
+ if (scanner->resolution > scanner->device->inquiry_fast_preview_res)
+ set_data (data, 2, scanner->device->inquiry_fast_preview_res, 2);
+ }
+
+
+ /* set required halftone pattern */
+ i = 0;
+ while (scanner->device->halftone_list[i] != NULL)
+ {
+ if (strcmp
+ (scanner->device->halftone_list[i],
+ scanner->val[OPT_HALFTONE_PATTERN].s) == 0)
+ break;
+ i++;
+ }
+
+ if (scanner->device->halftone_list[i] == NULL)
+ data[12] = 0; /* halftone pattern */
+ else
+ data[12] = i;
+
+ data[13] = SANE_UNFIX (scanner->val[OPT_THRESHOLD].w) * 255 / 100; /* lineart threshold */
+
+ DBG (DBG_info, "pie_mode_select: speed %02x\n", data[9]);
+ DBG (DBG_info, "pie_mode_select: halftone %d\n", data[12]);
+ DBG (DBG_info, "pie_mode_select: threshold %02x\n", data[13]);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, buffer, smode.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_mode_select: write command returned status %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+
+/*------------------------- PIE SCAN -----------------------------*/
+
+static SANE_Status
+pie_scan (Pie_Scanner * scanner, int start)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "pie_scan\n");
+
+ /* TUR */
+ status = pie_wait_scanner (scanner);
+ if (status)
+ {
+ return status;
+ }
+
+ set_scan_cmd (scan.cmd, start);
+
+ do
+ {
+ status = sanei_scsi_cmd (scanner->sfd, scan.cmd, scan.size, NULL, NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_scan: write command returned status %s\n",
+ sane_strstatus (status));
+ usleep (SCAN_WARMUP_WAIT_TIME);
+ }
+ }
+ while (start && status);
+
+ usleep (SCAN_WAIT_TIME);
+
+ return status;
+}
+
+
+/* --------------------------------------- PIE WAIT SCANNER -------------------------- */
+
+
+static SANE_Status
+pie_wait_scanner (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+ int cnt = 0;
+
+ DBG (DBG_proc, "wait_scanner\n");
+
+ do
+ {
+ if (cnt > 100) /* maximal 100 * 0.5 sec = 50 sec */
+ {
+ DBG (DBG_warning, "scanner does not get ready\n");
+ return -1;
+ }
+ /* test unit ready */
+ status =
+ sanei_scsi_cmd (scanner->sfd, test_unit_ready.cmd,
+ test_unit_ready.size, NULL, NULL);
+ cnt++;
+
+ if (status)
+ {
+ if (cnt == 1)
+ {
+ DBG (DBG_info2, "scanner reports %s, waiting ...\n",
+ sane_strstatus (status));
+ }
+
+ usleep (TUR_WAIT_TIME);
+ }
+ }
+ while (status != SANE_STATUS_GOOD);
+
+ DBG (DBG_info, "scanner ready\n");
+
+
+ return status;
+}
+
+
+/* -------------------------------------- PIE GET PARAMS -------------------------- */
+
+
+static SANE_Status
+pie_get_params (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned char buffer[128];
+
+ DBG (DBG_proc, "pie_get_params\n");
+
+ status = pie_wait_scanner (scanner);
+ if (status)
+ return status;
+
+ if (scanner->device->inquiry_image_format & INQ_IMG_FMT_OKLINE)
+ size = 16;
+ else
+
+ size = 14;
+
+ set_param_length (param.cmd, size);
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, param.cmd, param.size, buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error, "pie_get_params: command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ DBG (DBG_info, "Scan Width: %d\n", get_param_scan_width (buffer));
+ DBG (DBG_info, "Scan Lines: %d\n", get_param_scan_lines (buffer));
+ DBG (DBG_info, "Scan bytes: %d\n", get_param_scan_bytes (buffer));
+
+ DBG (DBG_info, "Offset 1: %d\n",
+ get_param_scan_filter_offset1 (buffer));
+ DBG (DBG_info, "Offset 2: %d\n",
+ get_param_scan_filter_offset2 (buffer));
+ DBG (DBG_info, "Scan period: %d\n", get_param_scan_period (buffer));
+ DBG (DBG_info, "Xfer rate: %d\n", get_param_scsi_xfer_rate (buffer));
+ if (scanner->device->inquiry_image_format & INQ_IMG_FMT_OKLINE)
+ DBG (DBG_info, "Avail lines: %d\n",
+ get_param_scan_available_lines (buffer));
+
+ scanner->filter_offset1 = get_param_scan_filter_offset1 (buffer);
+ scanner->filter_offset2 = get_param_scan_filter_offset2 (buffer);
+ scanner->bytes_per_line = get_param_scan_bytes (buffer);
+
+ scanner->params.pixels_per_line = get_param_scan_width (buffer);
+ scanner->params.lines = get_param_scan_lines (buffer);
+
+ switch (scanner->colormode)
+ {
+ case RGB:
+ scanner->params.format = SANE_FRAME_RGB;
+ scanner->params.depth = 8;
+ scanner->params.bytes_per_line = 3 * get_param_scan_bytes (buffer);
+ break;
+
+ case GRAYSCALE:
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.depth = 8;
+ scanner->params.bytes_per_line = get_param_scan_bytes (buffer);
+ break;
+
+ case HALFTONE:
+ case LINEART:
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.depth = 1;
+ scanner->params.bytes_per_line = get_param_scan_bytes (buffer);
+ break;
+ }
+
+ scanner->params.last_frame = 0;
+ }
+
+ return status;
+}
+
+
+/* -------------------------------------- PIE GRAB SCANNER -------------------------- */
+
+
+static SANE_Status
+pie_grab_scanner (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "grab_scanner\n");
+
+
+ status = pie_wait_scanner (scanner);
+ if (status)
+ return status;
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, reserve_unit.cmd, reserve_unit.size, NULL,
+ NULL);
+
+
+ if (status)
+ {
+ DBG (DBG_error, "pie_grab_scanner: command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ DBG (DBG_info, "scanner reserved\n");
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------ PIE GIVE SCANNER -------------------------- */
+
+
+static SANE_Status
+pie_give_scanner (Pie_Scanner * scanner)
+{
+ SANE_Status status;
+
+ DBG (DBG_info2, "trying to release scanner ...\n");
+
+ status =
+ sanei_scsi_cmd (scanner->sfd, release_unit.cmd, release_unit.size, NULL,
+ NULL);
+ if (status)
+ {
+ DBG (DBG_error, "pie_give_scanner: command returned status %s\n",
+ sane_strstatus (status));
+ }
+ else
+ {
+ DBG (DBG_info, "scanner released\n");
+ }
+ return status;
+}
+
+
+/* ------------------- PIE READER PROCESS INDEXED ------------------- */
+
+static int
+pie_reader_process_indexed (Pie_Scanner * scanner, FILE * fp)
+{
+ int status;
+ int lines;
+ unsigned char *buffer, *reorder = NULL;
+ unsigned char *red_buffer = NULL, *green_buffer = NULL;
+ unsigned char *red_in = NULL, *red_out = NULL;
+ unsigned char *green_in = NULL, *green_out = NULL;
+ int red_size = 0, green_size = 0;
+ int bytes_per_line;
+ int red_count = 0, green_count = 0;
+
+ size_t size;
+
+ DBG (DBG_read, "reading %d lines of %d bytes/line (indexed)\n",
+ scanner->params.lines, scanner->params.bytes_per_line);
+
+ lines = scanner->params.lines;
+ bytes_per_line = scanner->bytes_per_line;
+
+ /* allocate receive buffer */
+ buffer = malloc (bytes_per_line + 2);
+ if (!buffer)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* allocate deskew buffers for RGB mode */
+ if (scanner->colormode == RGB)
+ {
+ lines *= 3;
+
+ red_size = bytes_per_line * (scanner->filter_offset1 +
+ scanner->filter_offset2 + 2);
+ green_size = bytes_per_line * (scanner->filter_offset2 + 2);
+
+ DBG (DBG_info2,
+ "pie_reader_process_indexed: alloc %d lines (%d bytes) for red buffer\n",
+ red_size / bytes_per_line, red_size);
+ DBG (DBG_info2,
+ "pie_reader_process_indexed: alloc %d lines (%d bytes) for green buffer\n",
+ green_size / bytes_per_line, green_size);
+
+ reorder = malloc (scanner->params.bytes_per_line);
+ red_buffer = malloc (red_size);
+ green_buffer = malloc (green_size);
+
+ if (!reorder || !red_buffer || !green_buffer)
+ {
+ free (buffer);
+ free (reorder);
+ free (red_buffer);
+ free (green_buffer);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ red_in = red_out = red_buffer;
+ green_in = green_out = green_buffer;
+ }
+
+ while (lines--)
+ {
+ set_read_length (sread.cmd, 1);
+ size = bytes_per_line + 2;
+
+ do
+ {
+ status =
+ sanei_scsi_cmd (scanner->sfd, sread.cmd, sread.size, buffer,
+ &size);
+ }
+ while (status);
+
+ DBG_DUMP (DBG_dump, buffer, 64);
+
+ if (scanner->colormode == RGB)
+ {
+ /* we're assuming that we get red before green before blue here */
+ switch (*buffer)
+ {
+ case 'R':
+ /* copy to red buffer */
+ memcpy (red_in, buffer + 2, bytes_per_line);
+
+ /* advance in pointer, and check for wrap */
+ red_in += bytes_per_line;
+ if (red_in >= (red_buffer + red_size))
+ red_in = red_buffer;
+
+ /* increment red line count */
+ red_count++;
+ DBG (DBG_info2,
+ "pie_reader_process_indexed: got a red line (%d)\n",
+ red_count);
+ break;
+
+ case 'G':
+ /* copy to green buffer */
+ memcpy (green_in, buffer + 2, bytes_per_line);
+
+ /* advance in pointer, and check for wrap */
+ green_in += bytes_per_line;
+ if (green_in >= (green_buffer + green_size))
+ green_in = green_buffer;
+
+ /* increment green line count */
+ green_count++;
+ DBG (DBG_info2,
+ "pie_reader_process_indexed: got a green line (%d)\n",
+ green_count);
+ break;
+
+ case 'B':
+ /* check we actually have red and green data available */
+ if (!red_count || !green_count)
+ {
+ DBG (DBG_error,
+ "pie_reader_process_indexed: deskew buffer empty (%d %d)\n",
+ red_count, green_count);
+ return SANE_STATUS_INVAL;
+ }
+ red_count--;
+ green_count--;
+
+ DBG (DBG_info2,
+ "pie_reader_process_indexed: got a blue line\n");
+
+ {
+ int i;
+ unsigned char *red, *green, *blue, *dest;
+
+ /* now pack the pixels lines into RGB format */
+ dest = reorder;
+ red = red_out;
+ green = green_out;
+ blue = buffer + 2;
+
+ for (i = bytes_per_line; i > 0; i--)
+ {
+ *dest++ = *red++;
+ *dest++ = *green++;
+ *dest++ = *blue++;
+ }
+ fwrite (reorder, 1, scanner->params.bytes_per_line, fp);
+
+ /* advance out pointers, and check for wrap */
+ red_out += bytes_per_line;
+ if (red_out >= (red_buffer + red_size))
+ red_out = red_buffer;
+ green_out += bytes_per_line;
+ if (green_out >= (green_buffer + green_size))
+ green_out = green_buffer;
+ }
+ break;
+
+ default:
+ DBG (DBG_error,
+ "pie_reader_process_indexed: bad filter index\n");
+ }
+ }
+ else
+ {
+ DBG (DBG_info2,
+ "pie_reader_process_indexed: got a line (%lu bytes)\n", (u_long) size);
+
+ /* just send the data on, assume filter bytes not present as per calibration case */
+ fwrite (buffer, 1, scanner->params.bytes_per_line, fp);
+ }
+ }
+
+ free (buffer);
+ free (reorder);
+ free (red_buffer);
+ free (green_buffer);
+ return 0;
+}
+
+/* --------------------------------- PIE READER PROCESS ------------------------ */
+
+static int
+pie_reader_process (Pie_Scanner * scanner, FILE * fp)
+{
+ int status;
+ int lines;
+ unsigned char *buffer, *reorder;
+ size_t size;
+
+ DBG (DBG_read, "reading %d lines of %d bytes/line\n", scanner->params.lines,
+ scanner->params.bytes_per_line);
+
+ buffer = malloc (scanner->params.bytes_per_line);
+ reorder = malloc (scanner->params.bytes_per_line);
+ if (!buffer || !reorder)
+ {
+ free (buffer);
+ free (reorder);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ lines = scanner->params.lines;
+
+ while (lines--)
+ {
+ set_read_length (sread.cmd, 1);
+ size = scanner->params.bytes_per_line;
+
+ do
+ {
+ status =
+ sanei_scsi_cmd (scanner->sfd, sread.cmd, sread.size, buffer,
+ &size);
+ }
+ while (status);
+
+ DBG_DUMP (DBG_dump, buffer, 64);
+
+ if (scanner->colormode == RGB)
+ {
+ int i;
+ unsigned char *src, *dest;
+ int offset;
+
+ dest = reorder;
+ src = buffer;
+ offset = scanner->params.pixels_per_line;
+
+ for (i = scanner->params.pixels_per_line; i > 0; i--)
+ {
+ *dest++ = *src;
+ *dest++ = *(src + offset);
+ *dest++ = *(src + 2 * offset);
+ src++;
+ }
+ fwrite (reorder, 1, scanner->params.bytes_per_line, fp);
+ }
+ else
+ {
+ fwrite (buffer, 1, scanner->params.bytes_per_line, fp);
+ }
+
+ fflush (fp);
+ }
+
+ free (buffer);
+ free (reorder);
+
+ return 0;
+}
+
+
+
+/* --------------------------------- READER PROCESS SIGTERM HANDLER ------------ */
+
+
+static RETSIGTYPE
+reader_process_sigterm_handler (int signal)
+{
+ DBG (DBG_sane_info, "reader_process: terminated by signal %d\n", signal);
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+#else
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+#endif
+
+ _exit (SANE_STATUS_GOOD);
+}
+
+
+
+/* ------------------------------ READER PROCESS ----------------------------- */
+
+
+static int
+reader_process ( void *data ) /* executed as a child process */
+{
+ int status;
+ FILE *fp;
+ Pie_Scanner * scanner;
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ scanner = (Pie_Scanner *)data;
+
+ if (sanei_thread_is_forked ()) {
+
+ close ( scanner->pipe );
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+ }
+
+ DBG (DBG_sane_proc, "reader_process started\n");
+
+ memset (&act, 0, sizeof (act)); /* define SIGTERM-handler */
+ act.sa_handler = reader_process_sigterm_handler;
+ sigaction (SIGTERM, &act, 0);
+
+ fp = fdopen (scanner->reader_fds, "w");
+ if (!fp)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_sane_info, "reader_process: starting to READ data\n");
+
+ if (scanner->device->inquiry_color_format & INQ_COLOR_FORMAT_LINE)
+ status = pie_reader_process (scanner, fp);
+ else if (scanner->device->inquiry_color_format & INQ_COLOR_FORMAT_INDEX)
+ status = pie_reader_process_indexed (scanner, fp);
+ else
+ status = SANE_STATUS_UNSUPPORTED;
+
+ fclose (fp);
+
+ DBG (DBG_sane_info, "reader_process: finished reading data\n");
+
+ return status;
+}
+
+
+/* -------------------------------- ATTACH_ONE ---------------------------------- */
+
+
+/* callback function for sanei_config_attach_matching_devices(dev_name, attach_one) */
+static SANE_Status
+attach_one (const char *name)
+{
+ attach_scanner (name, 0);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ----------------------------- CLOSE PIPE ---------------------------------- */
+
+
+static SANE_Status
+close_pipe (Pie_Scanner * scanner)
+{
+ DBG (DBG_sane_proc, "close_pipe\n");
+
+ if (scanner->pipe >= 0)
+ {
+ close (scanner->pipe);
+ scanner->pipe = -1;
+ }
+
+ return SANE_STATUS_EOF;
+}
+
+
+
+/* ---------------------------- DO CANCEL ---------------------------------- */
+
+
+static SANE_Status
+do_cancel (Pie_Scanner * scanner)
+{
+ DBG (DBG_sane_proc, "do_cancel\n");
+
+ scanner->scanning = SANE_FALSE;
+
+ if (scanner->reader_pid != -1)
+ {
+ DBG (DBG_sane_info, "killing reader_process\n");
+ sanei_thread_kill (scanner->reader_pid);
+ sanei_thread_waitpid (scanner->reader_pid, 0);
+ scanner->reader_pid = -1;
+ DBG (DBG_sane_info, "reader_process killed\n");
+ }
+
+ if (scanner->sfd >= 0)
+ {
+ pie_scan (scanner, 0);
+
+ pie_power_save (scanner, 15);
+
+ pie_give_scanner (scanner); /* reposition and release scanner */
+
+ DBG (DBG_sane_info, "closing scannerdevice filedescriptor\n");
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+
+
+/* --------------------------------------- SANE INIT ---------------------------------- */
+
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init() build %d\n", BUILD);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ fp = sanei_config_open (PIE_CONFIG_FILE);
+ if (!fp)
+ {
+ attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#')
+ {
+ continue;
+ } /* ignore line comments */
+
+ len = strlen (dev_name);
+
+ if (!len) /* ignore empty lines */
+ {
+ continue;
+ }
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ----------------------------------------- SANE EXIT ---------------------------------- */
+
+
+void
+sane_exit (void)
+{
+ Pie_Device *dev, *next;
+ int i;
+
+ DBG (DBG_sane_init, "sane_exit()\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->devicename);
+ free (dev->cal_info);
+ i = 0;
+ while (dev->halftone_list[i] != NULL)
+ free (dev->halftone_list[i++]);
+ i = 0;
+ while (dev->speed_list[i] != NULL)
+ free (dev->speed_list[i++]);
+
+ free (dev);
+ }
+
+ first_dev = NULL;
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+}
+
+
+/* ------------------------------------------ SANE GET DEVICES --------------------------- */
+
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Pie_Device *dev;
+ int i;
+
+ DBG (DBG_sane_init, "sane_get_devices\n");
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ i++;
+
+ if (devlist)
+ {
+ free (devlist);
+ }
+
+ devlist = malloc ((i + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ i = 0;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ devlist[i++] = &dev->sane;
+ }
+
+ devlist[i] = NULL;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* --------------------------------------- SANE OPEN ---------------------------------- */
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Pie_Device *dev;
+ SANE_Status status;
+ Pie_Scanner *scanner;
+ int i, j;
+
+ DBG (DBG_sane_init, "sane_open(%s)\n", devicename);
+
+ if (devicename[0]) /* search for devicename */
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+
+ if (!dev)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ scanner = malloc (sizeof (*scanner));
+ if (!scanner)
+
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (scanner, 0, sizeof (*scanner));
+
+ scanner->device = dev;
+ scanner->sfd = -1;
+ scanner->pipe = -1;
+
+ scanner->gamma_length = 1 << (scanner->device->inquiry_gamma_bits);
+
+ DBG (DBG_sane_info, "Using %d bits for gamma input\n",
+ scanner->device->inquiry_gamma_bits);
+
+ scanner->gamma_range.min = 0;
+ scanner->gamma_range.max = scanner->gamma_length - 1;
+ scanner->gamma_range.quant = 0;
+
+ scanner->gamma_table[0] =
+ (SANE_Int *) malloc (scanner->gamma_length * sizeof (SANE_Int));
+ scanner->gamma_table[1] =
+ (SANE_Int *) malloc (scanner->gamma_length * sizeof (SANE_Int));
+ scanner->gamma_table[2] =
+ (SANE_Int *) malloc (scanner->gamma_length * sizeof (SANE_Int));
+ scanner->gamma_table[3] =
+ (SANE_Int *) malloc (scanner->gamma_length * sizeof (SANE_Int));
+
+ for (i = 0; i < 4; ++i) /* gamma_table[0,1,2,3] */
+ {
+ for (j = 0; j < scanner->gamma_length; ++j)
+ {
+ scanner->gamma_table[i][j] = j;
+ }
+ }
+
+ init_options (scanner);
+
+ scanner->next = first_handle; /* insert newly opened handle into list of open handles: */
+ first_handle = scanner;
+
+ *handle = scanner;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------ SANE CLOSE --------------------------------- */
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Pie_Scanner *prev, *scanner;
+
+ DBG (DBG_sane_init, "sane_close\n");
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+
+ for (scanner = first_handle; scanner; scanner = scanner->next)
+ {
+ if (scanner == handle)
+ {
+ break;
+ }
+
+ prev = scanner;
+ }
+
+ if (!scanner)
+ {
+ DBG (DBG_error, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (scanner->scanning) /* stop scan if still scanning */
+ {
+ do_cancel (handle);
+ }
+
+ if (prev)
+ {
+ prev->next = scanner->next;
+ }
+ else
+ {
+ first_handle = scanner->next;
+ }
+
+ free (scanner->gamma_table[0]); /* free custom gamma tables */
+ free (scanner->gamma_table[1]);
+ free (scanner->gamma_table[2]);
+ free (scanner->gamma_table[3]);
+ free (scanner->val[OPT_MODE].s);
+ free (scanner->val[OPT_SPEED].s);
+ free (scanner->val[OPT_HALFTONE_PATTERN].s);
+
+ scanner->bufsize = 0;
+
+ free (scanner); /* free scanner */
+}
+
+
+/* ---------------------------------- SANE GET OPTION DESCRIPTOR ----------------- */
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Pie_Scanner *scanner = handle;
+
+ DBG (DBG_sane_option, "sane_get_option_descriptor %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ return 0;
+ }
+
+ return scanner->opt + option;
+}
+
+
+/* ---------------------------------- SANE CONTROL OPTION ------------------------ */
+
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *val, SANE_Int * info)
+{
+ Pie_Scanner *scanner = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (scanner->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = scanner->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ name = scanner->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ DBG (DBG_sane_option, "get %s [#%d]\n", name, option);
+
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_THRESHOLD:
+ *(SANE_Word *) val = scanner->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, scanner->val[option].wa, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+#if 0
+ /* string options: */
+ case OPT_SOURCE:
+#endif
+ case OPT_MODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_SPEED:
+ strcpy (val, scanner->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ switch (scanner->opt[option].type)
+ {
+ case SANE_TYPE_INT:
+ DBG (DBG_sane_option, "set %s [#%d] to %d\n", name, option,
+ *(SANE_Word *) val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ DBG (DBG_sane_option, "set %s [#%d] to %f\n", name, option,
+ SANE_UNFIX (*(SANE_Word *) val));
+ break;
+
+ case SANE_TYPE_STRING:
+ DBG (DBG_sane_option, "set %s [#%d] to %s\n", name, option,
+ (char *) val);
+ break;
+
+ case SANE_TYPE_BOOL:
+ DBG (DBG_sane_option, "set %s [#%d] to %d\n", name, option,
+ *(SANE_Word *) val);
+ break;
+
+ default:
+ DBG (DBG_sane_option, "set %s [#%d]\n", name, option);
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (scanner->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_PREVIEW:
+ case OPT_THRESHOLD:
+ scanner->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (scanner->val[option].wa, val, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* options with side-effects: */
+
+ case OPT_MODE:
+ {
+ int halftoning;
+
+ if (scanner->val[option].s)
+ {
+ free (scanner->val[option].s);
+ }
+
+ scanner->val[option].s = (SANE_Char *) strdup (val);
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ halftoning = (strcmp (val, HALFTONE_STR) == 0);
+
+ if (halftoning || strcmp (val, LINEART_STR) == 0)
+ { /* one bit modes */
+ if (halftoning)
+ { /* halftoning modes */
+ scanner->opt[OPT_HALFTONE_PATTERN].cap &=
+ ~SANE_CAP_INACTIVE;
+ }
+ else
+ { /* lineart modes */
+ }
+ scanner->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ { /* multi-bit modes(gray or color) */
+ }
+
+ if ((strcmp (val, LINEART_STR) == 0)
+ || (strcmp (val, HALFTONE_STR) == 0)
+ || (strcmp (val, GRAY_STR) == 0))
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, COLOR_STR) == 0)
+ {
+ /* scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; */
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SPEED:
+ case OPT_HALFTONE_PATTERN:
+ {
+ if (scanner->val[option].s)
+ {
+ free (scanner->val[option].s);
+ }
+
+ scanner->val[option].s = (SANE_Char *) strdup (val);
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+ } /* else */
+ return SANE_STATUS_INVAL;
+}
+
+
+/* ------------------------------------ SANE GET PARAMETERS ------------------------ */
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Pie_Scanner *scanner = handle;
+ const char *mode;
+
+ DBG (DBG_sane_info, "sane_get_parameters\n");
+
+ if (!scanner->scanning)
+ { /* not scanning, so lets use recent values */
+ double width, length, x_dpi, y_dpi;
+
+ memset (&scanner->params, 0, sizeof (scanner->params));
+
+ width =
+ SANE_UNFIX (scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w);
+ length =
+ SANE_UNFIX (scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w);
+ x_dpi = SANE_UNFIX (scanner->val[OPT_RESOLUTION].w);
+ y_dpi = x_dpi;
+
+#if 0
+ if ((scanner->val[OPT_RESOLUTION_BIND].w == SANE_TRUE)
+ || (scanner->val[OPT_PREVIEW].w == SANE_TRUE))
+ {
+ y_dpi = x_dpi;
+ }
+#endif
+ if (x_dpi > 0.0 && y_dpi > 0.0 && width > 0.0 && length > 0.0)
+ {
+ double x_dots_per_mm = x_dpi / MM_PER_INCH;
+ double y_dots_per_mm = y_dpi / MM_PER_INCH;
+
+ scanner->params.pixels_per_line = width * x_dots_per_mm;
+ scanner->params.lines = length * y_dots_per_mm;
+ }
+ }
+
+ mode = scanner->val[OPT_MODE].s;
+
+ if (strcmp (mode, LINEART_STR) == 0 || strcmp (mode, HALFTONE_STR) == 0)
+ {
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.bytes_per_line =
+ (scanner->params.pixels_per_line + 7) / 8;
+ scanner->params.depth = 1;
+ }
+ else if (strcmp (mode, GRAY_STR) == 0)
+ {
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line;
+ scanner->params.depth = 8;
+ }
+ else /* RGB */
+ {
+ scanner->params.format = SANE_FRAME_RGB;
+ scanner->params.bytes_per_line = 3 * scanner->params.pixels_per_line;
+ scanner->params.depth = 8;
+ }
+
+ scanner->params.last_frame = (scanner->params.format != SANE_FRAME_RED
+ && scanner->params.format !=
+ SANE_FRAME_GREEN);
+
+ if (params)
+ {
+ *params = scanner->params;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ----------------------------------------- SANE START --------------------------------- */
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Pie_Scanner *scanner = handle;
+ int fds[2];
+ const char *mode;
+ int status;
+
+ DBG (DBG_sane_init, "sane_start\n");
+
+ /* Check for inconsistencies */
+
+ if (scanner->val[OPT_TL_X].w > scanner->val[OPT_BR_X].w)
+ {
+ DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
+ "-- aborting\n",
+ scanner->opt[OPT_TL_X].title, SANE_UNFIX (scanner->val[OPT_TL_X].w),
+ scanner->opt[OPT_BR_X].title, SANE_UNFIX (scanner->val[OPT_BR_X].w));
+ return SANE_STATUS_INVAL;
+ }
+ if (scanner->val[OPT_TL_Y].w > scanner->val[OPT_BR_Y].w)
+ {
+ DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
+ "-- aborting\n",
+ scanner->opt[OPT_TL_Y].title, SANE_UNFIX (scanner->val[OPT_TL_Y].w),
+ scanner->opt[OPT_BR_Y].title, SANE_UNFIX (scanner->val[OPT_BR_Y].w));
+ return SANE_STATUS_INVAL;
+ }
+
+ mode = scanner->val[OPT_MODE].s;
+
+ if (scanner->sfd < 0) /* first call, don`t run this routine again on multi frame or multi image scan */
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ int scsi_bufsize = 131072; /* 128KB */
+
+ if (sanei_scsi_open_extended
+ (scanner->device->sane.name, &(scanner->sfd), sense_handler,
+ scanner->device, &scsi_bufsize) != 0)
+
+ {
+ DBG (DBG_error, "sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scsi_bufsize < 32768) /* < 32KB */
+ {
+ DBG (DBG_error,
+ "sane_start: sanei_scsi_open_extended returned too small scsi buffer (%d)\n",
+ scsi_bufsize);
+ sanei_scsi_close ((scanner->sfd));
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (DBG_info,
+ "sane_start: sanei_scsi_open_extended returned scsi buffer size = %d\n",
+ scsi_bufsize);
+
+
+ scanner->bufsize = scsi_bufsize;
+#else
+ if (sanei_scsi_open
+ (scanner->device->sane.name, &(scanner->sfd), sense_handler,
+ scanner->device) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: open of %s failed:\n",
+ scanner->device->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* there is no need to reallocate the buffer because the size is fixed */
+#endif
+
+#if 0
+ if (pie_check_values (scanner->device) != 0)
+ {
+ DBG (DBG_error, "ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ pie_give_scanner (scanner); /* reposition and release scanner */
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+#endif
+#if 0
+ scanner->params.bytes_per_line = scanner->device->row_len;
+ scanner->params.pixels_per_line = scanner->device->width_in_pixels;
+ scanner->params.lines = scanner->device->length_in_pixels;
+
+ sane_get_parameters (scanner, 0);
+
+ DBG (DBG_sane_info, "x_resolution (dpi) = %u\n",
+ scanner->device->x_resolution);
+ DBG (DBG_sane_info, "y_resolution (dpi) = %u\n",
+ scanner->device->y_resolution);
+ DBG (DBG_sane_info, "x_coordinate_base (dpi) = %u\n",
+ scanner->device->x_coordinate_base);
+ DBG (DBG_sane_info, "y_coordinate_base (dpi) = %u\n",
+ scanner->device->y_coordinate_base);
+ DBG (DBG_sane_info, "upper_left_x (xbase) = %d\n",
+ scanner->device->upper_left_x);
+ DBG (DBG_sane_info, "upper_left_y (ybase) = %d\n",
+ scanner->device->upper_left_y);
+ DBG (DBG_sane_info, "scanwidth (xbase) = %u\n",
+ scanner->device->scanwidth);
+ DBG (DBG_sane_info, "scanlength (ybase) = %u\n",
+ scanner->device->scanlength);
+ DBG (DBG_sane_info, "width in pixels = %u\n",
+ scanner->device->width_in_pixels);
+ DBG (DBG_sane_info, "length in pixels = %u\n",
+ scanner->device->length_in_pixels);
+ DBG (DBG_sane_info, "bits per pixel/color = %u\n",
+ scanner->device->bits_per_pixel);
+ DBG (DBG_sane_info, "bytes per line = %d\n",
+ scanner->params.bytes_per_line);
+ DBG (DBG_sane_info, "pixels_per_line = %d\n",
+ scanner->params.pixels_per_line);
+ DBG (DBG_sane_info, "lines = %d\n",
+ scanner->params.lines);
+#endif
+
+ /* grab scanner */
+ if (pie_grab_scanner (scanner))
+ {
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ DBG (DBG_warning,
+ "WARNING: unable to reserve scanner: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ scanner->scanning = SANE_TRUE;
+
+ pie_power_save (scanner, 0);
+ } /* ------------ end of first call -------------- */
+
+
+ if (strcmp (mode, LINEART_STR) == 0)
+ {
+ scanner->colormode = LINEART;
+ }
+ else if (strcmp (mode, HALFTONE_STR) == 0)
+ {
+ scanner->colormode = HALFTONE;
+ }
+ else if (strcmp (mode, GRAY_STR) == 0)
+ {
+ scanner->colormode = GRAYSCALE;
+ }
+ else if (strcmp (mode, COLOR_STR) == 0)
+ {
+ scanner->colormode = RGB;
+ }
+
+ /* get and set geometric values for scanning */
+ scanner->resolution = SANE_UNFIX (scanner->val[OPT_RESOLUTION].w);
+
+ pie_set_window (scanner);
+ pie_send_exposure (scanner);
+ pie_mode_select (scanner);
+ pie_send_highlight_shadow (scanner);
+
+ pie_scan (scanner, 1);
+
+ status = pie_do_cal (scanner);
+ if (status)
+ return status;
+
+ /* send gammacurves */
+
+ pie_dwnld_gamma (scanner);
+
+ pie_get_params (scanner);
+
+ if (pipe (fds) < 0) /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
+ {
+ DBG (DBG_error, "ERROR: could not create pipe\n");
+ scanner->scanning = SANE_FALSE;
+ pie_scan (scanner, 0);
+ pie_give_scanner (scanner); /* reposition and release scanner */
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe = fds[0];
+ scanner->reader_fds = fds[1];
+ scanner->reader_pid = sanei_thread_begin( reader_process, (void*)scanner );
+
+ if (scanner->reader_pid == -1)
+ {
+ DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ close (scanner->reader_fds);
+ scanner->reader_fds = -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* -------------------------------------- SANE READ ---------------------------------- */
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Pie_Scanner *scanner = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read (scanner->pipe, buf, max_len);
+ DBG (DBG_sane_info, "sane_read: read %ld bytes\n", (long) nread);
+
+ if (!(scanner->scanning)) /* OOPS, not scanning */
+ {
+ return do_cancel (scanner);
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ DBG (DBG_sane_info, "sane_read: EAGAIN\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel (scanner); /* we had an error, stop scanner */
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0) /* EOF */
+ {
+ do_cancel (scanner);
+
+ return close_pipe (scanner); /* close pipe */
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------- SANE CANCEL -------------------------------- */
+
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Pie_Scanner *scanner = handle;
+
+ DBG (DBG_sane_init, "sane_cancel\n");
+
+ if (scanner->scanning)
+ {
+ do_cancel (scanner);
+ }
+}
+
+
+/* -------------------------------------- SANE SET IO MODE --------------------------- */
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Pie_Scanner *scanner = handle;
+
+ DBG (DBG_sane_init, "sane_set_io_mode: non_blocking=%d\n", non_blocking);
+
+ if (!scanner->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (fcntl (scanner->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* --------------------------------------- SANE GET SELECT FD ------------------------- */
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Pie_Scanner *scanner = handle;
+
+ DBG (DBG_sane_init, "sane_get_select_fd\n");
+
+ if (!scanner->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ *fd = scanner->pipe;
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/pie.conf.in b/backend/pie.conf.in
new file mode 100644
index 0000000..b8a519b
--- /dev/null
+++ b/backend/pie.conf.in
@@ -0,0 +1,4 @@
+scsi DEVCOM * Scanner
+scsi PIE * Scanner
+scsi AdLib * Scanner
+/dev/scanner
diff --git a/backend/pint.c b/backend/pint.c
new file mode 100644
index 0000000..f0cc697
--- /dev/null
+++ b/backend/pint.c
@@ -0,0 +1,987 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Gordon Matzigkeit
+ Copyright (C) 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+extern int errno;
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define PINT_CONFIG_FILE "pint.conf"
+
+#include "pint.h"
+
+#define DECIPOINTS_PER_MM (720.0 / MM_PER_INCH)
+#define TWELVEHUNDS_PER_MM (1200.0 / MM_PER_INCH)
+
+
+static int num_devices;
+static PINT_Device *first_dev;
+static PINT_Scanner *first_handle;
+
+/* A zero-terminated list of valid scanner modes. */
+static SANE_String_Const mode_list[8];
+
+static const SANE_Range s7_range =
+ {
+ -127, /* minimum */
+ 127, /* maximum */
+ 1 /* quantization */
+ };
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+attach (const char *devname, PINT_Device **devp)
+{
+ int fd;
+ long lastguess, inc;
+ PINT_Device *dev;
+ struct scan_io scanio;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(3, "attach: opening %s\n", devname);
+ fd = open (devname, O_RDONLY, 0);
+ if (fd < 0)
+ {
+ DBG(1, "attach: open failed (%s)\n", strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(3, "attach: sending SCIOCGET\n");
+ if (ioctl (fd, SCIOCGET, &scanio) < 0)
+ {
+ DBG(1, "attach: get status failed (%s)\n", strerror (errno));
+ close (fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset(dev, 0, sizeof (*dev));
+
+ /* Copy the original scanner state to the device structure. */
+ memcpy (&dev->scanio, &scanio, sizeof (dev->scanio));
+
+ /* FIXME: PINT currently has no good way to determine maxima and minima.
+ So, do binary searches to find out what limits the driver has. */
+
+ /* Assume that minimum range of x and y is 0. */
+ dev->x_range.min = SANE_FIX (0);
+ dev->y_range.min = SANE_FIX (0);
+ dev->x_range.quant = 0;
+ dev->y_range.quant = 0;
+
+ /* x range */
+ inc = 8.5 * 1200;
+
+ /* Converge on the maximum scan width. */
+ while ((inc /= 2) != 0)
+ {
+ /* Move towards the extremum until we overflow. */
+ do
+ {
+ lastguess = scanio.scan_width;
+ scanio.scan_width += inc;
+ }
+ while (ioctl (fd, SCIOCSET, &scanio) >= 0);
+
+ /* Pick the last valid guess, divide by two, and try again. */
+ scanio.scan_width = lastguess;
+ }
+ dev->x_range.max = SANE_FIX (scanio.scan_width / TWELVEHUNDS_PER_MM);
+
+ /* y range */
+ inc = 11 * 1200;
+ while ((inc /= 2) != 0)
+ {
+ do
+ {
+ lastguess = scanio.scan_height;
+ scanio.scan_height += inc;
+ }
+ while (ioctl (fd, SCIOCSET, &scanio) >= 0);
+ scanio.scan_height = lastguess;
+ }
+ dev->y_range.max = SANE_FIX (scanio.scan_height / TWELVEHUNDS_PER_MM);
+
+ /* Converge on the minimum scan resolution. */
+ dev->dpi_range.quant = 1;
+
+ if (scanio.scan_x_resolution > scanio.scan_y_resolution)
+ scanio.scan_x_resolution = scanio.scan_y_resolution;
+ else
+ scanio.scan_y_resolution = scanio.scan_x_resolution;
+
+ inc = -scanio.scan_x_resolution;
+ while ((inc /= 2) != 0)
+ {
+ do
+ {
+ lastguess = scanio.scan_x_resolution;
+ scanio.scan_x_resolution = scanio.scan_y_resolution += inc;
+ }
+ while (ioctl (fd, SCIOCSET, &scanio) >= 0);
+ scanio.scan_x_resolution = scanio.scan_y_resolution = lastguess;
+ }
+ dev->dpi_range.min = scanio.scan_x_resolution;
+
+ /* Converge on the maximum scan resolution. */
+ inc = 600;
+ while ((inc /= 2) != 0)
+ {
+ do
+ {
+ lastguess = scanio.scan_x_resolution;
+ scanio.scan_x_resolution = scanio.scan_y_resolution += inc;
+ }
+ while (ioctl (fd, SCIOCSET, &scanio) >= 0);
+ scanio.scan_x_resolution = scanio.scan_y_resolution = lastguess;
+ }
+ dev->dpi_range.max = scanio.scan_x_resolution;
+
+ /* Determine the valid scan modes for mode_list. */
+ lastguess = 0;
+#define CHECK_MODE(flag,modename) \
+ scanio.scan_image_mode = flag; \
+ if (ioctl (fd, SCIOCSET, &scanio) >= 0) \
+ mode_list[lastguess ++] = modename
+
+ CHECK_MODE(SIM_BINARY_MONOCHROME, SANE_VALUE_SCAN_MODE_LINEART);
+ CHECK_MODE(SIM_DITHERED_MONOCHROME, SANE_VALUE_SCAN_MODE_HALFTONE);
+ CHECK_MODE(SIM_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY);
+ CHECK_MODE(SIM_COLOR, SANE_VALUE_SCAN_MODE_COLOR);
+ CHECK_MODE(SIM_RED, "Red");
+ CHECK_MODE(SIM_GREEN, "Green");
+ CHECK_MODE(SIM_BLUE, "Blue");
+#undef CHECK_MODE
+
+ /* Zero-terminate the list of modes. */
+ mode_list[lastguess] = 0;
+
+ /* Restore the scanner state. */
+ if (ioctl (fd, SCIOCSET, &dev->scanio))
+ DBG (2, "cannot reset original scanner state: %s\n", strerror (errno));
+ close (fd);
+
+ dev->sane.name = strdup (devname);
+
+ /* Determine vendor. */
+ switch (scanio.scan_scanner_type)
+ {
+ case EPSON_ES300C:
+ dev->sane.vendor = "Epson";
+ break;
+
+ case FUJITSU_M3096G:
+ dev->sane.vendor = "Fujitsu";
+ break;
+
+ case HP_SCANJET_IIC:
+ dev->sane.vendor = "HP";
+ break;
+
+ case IBM_2456:
+ dev->sane.vendor = "IBM";
+ break;
+
+ case MUSTEK_06000CX:
+ case MUSTEK_12000CX:
+ dev->sane.vendor = "Mustek";
+ break;
+
+ case RICOH_FS1:
+ case RICOH_IS410:
+ case RICOH_IS50:
+ dev->sane.vendor = "Ricoh";
+ break;
+
+ case SHARP_JX600:
+ dev->sane.vendor = "Sharp";
+ break;
+
+ case UMAX_UC630:
+ case UMAX_UG630:
+ dev->sane.vendor = "UMAX";
+ break;
+
+ default:
+ dev->sane.vendor = "PINT";
+ }
+
+ /* Determine model. */
+ switch (scanio.scan_scanner_type)
+ {
+ case EPSON_ES300C:
+ dev->sane.vendor = "Epson";
+ break;
+
+ case FUJITSU_M3096G:
+ dev->sane.model = "M3096G";
+ break;
+
+ case HP_SCANJET_IIC:
+ dev->sane.model = "ScanJet IIc";
+ break;
+
+ case IBM_2456:
+ dev->sane.vendor = "IBM";
+ break;
+
+ case MUSTEK_06000CX:
+ case MUSTEK_12000CX:
+ dev->sane.vendor = "Mustek";
+ break;
+
+ case RICOH_FS1:
+ dev->sane.model = "FS1";
+ break;
+
+ case RICOH_IS410:
+ dev->sane.model = "IS-410";
+ break;
+
+ case RICOH_IS50:
+ dev->sane.vendor = "Ricoh";
+ break;
+
+ case SHARP_JX600:
+ dev->sane.vendor = "Sharp";
+ break;
+
+ case UMAX_UC630:
+ case UMAX_UG630:
+ dev->sane.vendor = "UMAX";
+ break;
+
+ default:
+ dev->sane.model = "unknown";
+ }
+
+ /* Determine the scanner type. */
+ switch (scanio.scan_scanner_type)
+ {
+ case HP_SCANJET_IIC:
+ dev->sane.type = "flatbed scanner";
+
+ /* FIXME: which of these are flatbed or handhelds? */
+ case EPSON_ES300C:
+ case FUJITSU_M3096G:
+ case IBM_2456:
+ case MUSTEK_06000CX:
+ case MUSTEK_12000CX:
+ case RICOH_FS1:
+ case RICOH_IS410:
+ case RICOH_IS50:
+ case SHARP_JX600:
+ case UMAX_UC630:
+ case UMAX_UG630:
+ default:
+ dev->sane.type = "generic scanner";
+ }
+
+ DBG(1, "attach: found %s %s, x=%g-%gmm, y=%g-%gmm, "
+ "resolution=%d-%ddpi\n", dev->sane.vendor, dev->sane.model,
+ SANE_UNFIX (dev->x_range.min), SANE_UNFIX (dev->x_range.max),
+ SANE_UNFIX (dev->y_range.min), SANE_UNFIX (dev->y_range.max),
+ dev->dpi_range.min, dev->dpi_range.max);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (PINT_Scanner *s)
+{
+ int i;
+ int x0, x1, y0, y1;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+
+ /* Translate the current PINT mode into a string. */
+ switch (s->hw->scanio.scan_image_mode)
+ {
+ case SIM_BINARY_MONOCHROME:
+ s->val[OPT_MODE].s = strdup (mode_list[0]);
+ break;
+
+ case SIM_DITHERED_MONOCHROME:
+ s->val[OPT_MODE].s = strdup (mode_list[1]);
+ break;
+
+ case SIM_COLOR:
+ s->val[OPT_MODE].s = strdup (mode_list[3]);
+ break;
+
+ case SIM_RED:
+ s->val[OPT_MODE].s = strdup (mode_list[4]);
+ break;
+
+ case SIM_GREEN:
+ s->val[OPT_MODE].s = strdup (mode_list[5]);
+ break;
+
+ case SIM_BLUE:
+ s->val[OPT_MODE].s = strdup (mode_list[6]);
+ break;
+
+ case SIM_GRAYSCALE:
+ default:
+ s->val[OPT_MODE].s = strdup (mode_list[2]);
+ }
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w =
+ (s->hw->scanio.scan_x_resolution > s->hw->scanio.scan_y_resolution) ?
+ s->hw->scanio.scan_x_resolution : s->hw->scanio.scan_y_resolution;
+
+ /* "Geometry" group: */
+
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Calculate the x and y millimetre coordinates from the scanio. */
+ x0 = SANE_FIX (s->hw->scanio.scan_x_origin / TWELVEHUNDS_PER_MM);
+ y0 = SANE_FIX (s->hw->scanio.scan_y_origin / TWELVEHUNDS_PER_MM);
+ x1 = SANE_FIX ((s->hw->scanio.scan_x_origin + s->hw->scanio.scan_width)
+ / TWELVEHUNDS_PER_MM);
+ y1 = SANE_FIX ((s->hw->scanio.scan_y_origin + s->hw->scanio.scan_height)
+ / TWELVEHUNDS_PER_MM);
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = x0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = y0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = x1;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = y1;
+
+ /* "Enhancement" group: */
+
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s7_range;
+ s->val[OPT_BRIGHTNESS].w = s->hw->scanio.scan_brightness - 128;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &s7_range;
+ s->val[OPT_CONTRAST].w = s->hw->scanio.scan_contrast - 128;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (PINT_Scanner *s)
+{
+ /* FIXME: PINT doesn't have any good way to cancel ScanJets right now. */
+#define gobble_up_buf_len 1024
+ char buf[gobble_up_buf_len];
+
+ /* Send the restart code. */
+ buf[0] = ioctl (s->fd, SCIOCRESTART, 0);
+
+ if (!s->scanning)
+ return SANE_STATUS_CANCELLED;
+
+ s->scanning = SANE_FALSE;
+
+ /* Read to the end of the file. */
+ while (read (s->fd, buf, gobble_up_buf_len) > 0)
+ ;
+#undef gobble_up_buf_len
+
+ /* Finally, close the file descriptor. */
+ if (s->fd >= 0)
+ {
+ close (s->fd);
+ s->fd = -1;
+ }
+ return SANE_STATUS_CANCELLED;
+}
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (PINT_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ attach (dev_name, 0);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ PINT_Device *dev, *next;
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free (dev);
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ PINT_Device *dev;
+ int i;
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle *handle)
+{
+ SANE_Status status;
+ PINT_Device *dev;
+ PINT_Scanner *s;
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ /* empty devicename -> use first device */
+ dev = first_dev;
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->hw = dev;
+ s->fd = -1;
+
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ PINT_Scanner *prev, *s;
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG(1, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ do_cancel (handle);
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ PINT_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int *info)
+{
+ PINT_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
+{
+ PINT_Scanner *s = handle;
+ struct scan_io scanio;
+
+ if (!s->scanning)
+ {
+ u_long x0, y0, width, height;
+ const char *mode;
+
+ /* Grab the scanio for this device. */
+ if (s->fd < 0)
+ {
+ s->fd = open (s->hw->sane.name, O_RDONLY, 0);
+ if (s->fd < 0)
+ {
+ DBG(1, "open of %s failed: %s\n",
+ s->hw->sane.name, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (ioctl (s->fd, SCIOCGET, &scanio) < 0)
+ {
+ DBG(1, "getting scanner state failed: %s", strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ /* FIXME: there is some lossage here: the parameters change due to
+ roundoff errors between converting to fixed point millimetres
+ and back. */
+ x0 = SANE_UNFIX (s->val[OPT_TL_X].w * TWELVEHUNDS_PER_MM);
+ y0 = SANE_UNFIX (s->val[OPT_TL_Y].w * TWELVEHUNDS_PER_MM);
+ width = SANE_UNFIX ((s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
+ * TWELVEHUNDS_PER_MM);
+ height = SANE_UNFIX ((s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
+ * TWELVEHUNDS_PER_MM);
+
+ /* x and y dpi: */
+ scanio.scan_x_resolution = s->val[OPT_RESOLUTION].w;
+ scanio.scan_y_resolution = s->val[OPT_RESOLUTION].w;
+
+ /* set scan extents, in 1/1200'ths of an inch */
+ scanio.scan_x_origin = x0;
+ scanio.scan_y_origin = y0;
+ scanio.scan_width = width;
+ scanio.scan_height = height;
+
+ /* brightness and contrast */
+ scanio.scan_brightness = s->val[OPT_BRIGHTNESS].w + 128;
+ scanio.scan_contrast = s->val[OPT_CONTRAST].w + 128;
+
+ /* set the scan image mode */
+ mode = s->val[OPT_MODE].s;
+ if (!strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ scanio.scan_image_mode = SIM_BINARY_MONOCHROME;
+ }
+ else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ scanio.scan_image_mode = SIM_DITHERED_MONOCHROME;
+ }
+ else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY))
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ scanio.scan_image_mode = SIM_GRAYSCALE;
+ }
+ else if (!strcmp (mode, "Red"))
+ {
+ s->params.format = SANE_FRAME_RED;
+ scanio.scan_image_mode = SIM_RED;
+ }
+ else if (!strcmp (mode, "Green"))
+ {
+ s->params.format = SANE_FRAME_GREEN;
+ scanio.scan_image_mode = SIM_GREEN;
+ }
+ else if (!strcmp (mode, "Blue"))
+ {
+ s->params.format = SANE_FRAME_BLUE;
+ scanio.scan_image_mode = SIM_BLUE;
+ }
+ else
+ {
+ s->params.format = SANE_FRAME_RGB;
+ scanio.scan_image_mode = SIM_COLOR;
+ }
+
+ /* inquire resulting size of image after setting it up */
+ if (ioctl (s->fd, SCIOCSET, &scanio) < 0)
+ {
+ DBG(1, "setting scan parameters failed: %s", strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ if (ioctl (s->fd, SCIOCGET, &scanio) < 0)
+ {
+ DBG(1, "getting scan parameters failed: %s", strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Save all the PINT-computed values. */
+ s->params.pixels_per_line = scanio.scan_pixels_per_line;
+ s->params.bytes_per_line =
+ (scanio.scan_bits_per_pixel * scanio.scan_pixels_per_line + 7) / 8;
+ s->params.lines = scanio.scan_lines;
+ s->params.depth = (scanio.scan_image_mode == SIM_COLOR) ?
+ scanio.scan_bits_per_pixel / 3 : scanio.scan_bits_per_pixel;
+
+ /* FIXME: this will need to be different for hand scanners. */
+ s->params.last_frame = SANE_TRUE;
+ }
+ if (params)
+ *params = s->params;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ PINT_Scanner *s = handle;
+ SANE_Status status;
+
+ /* First make sure we have a current parameter set. This call actually
+ uses the PINT driver to do the calculations, so we trust its results. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "%d pixels per line, %d bytes, %d lines high, dpi=%d\n",
+ s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines,
+ s->val[OPT_RESOLUTION].w);
+
+ /* The scan is triggered in sane_read. */
+ s->scanning = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
+{
+ PINT_Scanner *s = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ if (!s->scanning)
+ return do_cancel (s);
+
+ /* Verrry simple. Just suck up all the data PINT passes to us. */
+ nread = read (s->fd, buf, max_len);
+ if (nread <= 0)
+ {
+ do_cancel (s);
+ return (nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_IO_ERROR;
+ }
+
+ *len = nread;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ PINT_Scanner *s = handle;
+ do_cancel (s);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/pint.h b/backend/pint.h
new file mode 100644
index 0000000..773dcee
--- /dev/null
+++ b/backend/pint.h
@@ -0,0 +1,105 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Gordon Matzigkeit
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+#ifndef _PINT_H
+#define _PINT_H
+
+#include <sys/types.h>
+
+/* FIXME - in the PINT sources, this is set to ifdef __NetBSD__ */
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_SCANIO_H
+#include <sys/scanio.h>
+#endif
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ /* FIXME: eventually need to have both X and Y resolution. */
+ OPT_RESOLUTION,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ /* must come last: */
+ NUM_OPTIONS
+ }
+PINT_Option;
+
+typedef struct PINT_Device
+ {
+ struct PINT_Device *next;
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ struct scan_io scanio; /* Scanner hardware state. */
+ }
+PINT_Device;
+
+typedef struct PINT_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct PINT_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ int scanning;
+ SANE_Parameters params;
+
+ int fd; /* Device file descriptor */
+
+ /* scanner dependent/low-level state: */
+ PINT_Device *hw;
+ }
+PINT_Scanner;
+
+#endif /* _PINT_H */
diff --git a/backend/pixma.c b/backend/pixma.c
new file mode 100644
index 0000000..6a2492e
--- /dev/null
+++ b/backend/pixma.c
@@ -0,0 +1,1825 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef USE_PTHREAD
+# include <pthread.h>
+#endif
+#include <signal.h> /* sigaction(POSIX) */
+#include <unistd.h> /* POSIX: write read close pipe */
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#include "pixma_rename.h"
+#include "pixma.h"
+
+# define DEBUG_NOT_STATIC
+# include "../include/sane/sane.h"
+# include "../include/sane/sanei.h"
+# include "../include/sane/saneopts.h"
+# include "../include/sane/sanei_thread.h"
+# include "../include/sane/sanei_backend.h"
+# include "../include/sane/sanei_config.h"
+
+#ifdef NDEBUG
+# define PDBG(x)
+#else
+# define PDBG(x) IF_DBG(x)
+#endif /* NDEBUG */
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+#define DECL_CTX pixma_sane_t *ss = check_handle(h)
+#define OPT_IN_CTX ss->opt
+#define SOD(opt) OPT_IN_CTX[opt].sod
+#define OVAL(opt) OPT_IN_CTX[opt].val
+#define AUTO_GAMMA 2.2
+
+/* pixma_sane_options.h generated by
+ * scripts/pixma_gen_options.py h < pixma.c > pixma_sane_options.h
+ */
+#include "pixma_sane_options.h"
+
+#define BUTTON_GROUP_SIZE ( opt_scan_resolution - opt_button_1 + 1 )
+#define BUTTON_GROUP_INDEX(x) ( x - opt_button_1 )
+
+typedef struct pixma_sane_t
+{
+ struct pixma_sane_t *next;
+ pixma_t *s;
+ pixma_scan_param_t sp;
+ SANE_Bool cancel;
+
+ /* valid states: idle, !idle && scanning, !idle && !scanning */
+ SANE_Bool idle;
+ SANE_Bool scanning;
+ SANE_Status last_read_status; /* valid if !idle && !scanning */
+
+ option_descriptor_t opt[opt_last];
+ char button_option_is_cached[BUTTON_GROUP_SIZE];
+ SANE_Range xrange, yrange;
+ SANE_Word dpi_list[9]; /* up to 9600 dpi */
+ SANE_String_Const mode_list[6];
+ pixma_scan_mode_t mode_map[6];
+ uint8_t gamma_table[4096];
+ SANE_String_Const source_list[4];
+ pixma_paper_source_t source_map[4];
+
+ unsigned byte_pos_in_line, output_line_size;
+ uint64_t image_bytes_read;
+ unsigned page_count; /* valid for ADF */
+
+ SANE_Pid reader_taskid;
+ int wpipe, rpipe;
+ SANE_Bool reader_stop;
+} pixma_sane_t;
+
+
+static const char vendor_str[] = "CANON";
+static const char type_str[] = "multi-function peripheral";
+
+static pixma_sane_t *first_scanner = NULL;
+static const SANE_Device **dev_list = NULL;
+static const char* conf_devices[MAX_CONF_DEVICES];
+
+static void mark_all_button_options_cached ( struct pixma_sane_t * ss )
+{
+ int i;
+ for (i = 0; i < (opt__group_5 - opt_button_1); i++ )
+ ss -> button_option_is_cached[i] = 1;
+}
+
+static SANE_Status config_attach_pixma(SANEI_Config * config, const char *devname)
+{
+ int i;
+ UNUSED(config);
+ for (i=0; i < (MAX_CONF_DEVICES -1); i++)
+ {
+ if(conf_devices[i] == NULL)
+ {
+ conf_devices[i] = strdup(devname);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+map_error (int error)
+{
+ if (error >= 0)
+ return SANE_STATUS_GOOD;
+
+ switch (error)
+ {
+ case PIXMA_ENOMEM:
+ return SANE_STATUS_NO_MEM;
+ case PIXMA_ECANCELED:
+ return SANE_STATUS_CANCELLED;
+ case PIXMA_EBUSY:
+ return SANE_STATUS_DEVICE_BUSY;
+ case PIXMA_EINVAL:
+ return SANE_STATUS_INVAL;
+ case PIXMA_EACCES:
+ return SANE_STATUS_ACCESS_DENIED;
+ case PIXMA_EPAPER_JAMMED:
+ return SANE_STATUS_JAMMED;
+ case PIXMA_ENO_PAPER:
+ return SANE_STATUS_NO_DOCS;
+ case PIXMA_ECOVER_OPEN:
+ return SANE_STATUS_COVER_OPEN;
+ case PIXMA_ENOTSUP:
+ return SANE_STATUS_UNSUPPORTED;
+ case PIXMA_EPROTO:
+ case PIXMA_ENODEV:
+ case PIXMA_EIO:
+ case PIXMA_ETIMEDOUT:
+ return SANE_STATUS_IO_ERROR;
+ }
+ PDBG (pixma_dbg (1, "BUG: unmapped error %d\n", error));
+ return SANE_STATUS_IO_ERROR;
+}
+
+static int
+getenv_atoi (const char *name, int def)
+{
+ const char *str = getenv (name);
+ return (str) ? atoi (str) : def;
+}
+
+#define CONST_CAST(t,x) (t)(x)
+
+static void
+free_block (const void * ptr)
+{
+ free (CONST_CAST (void *, ptr));
+}
+
+static void
+cleanup_device_list (void)
+{
+ if (dev_list)
+ {
+ int i;
+ for (i = 0; dev_list[i]; i++)
+ {
+ free_block ((const void *) dev_list[i]->name);
+ free_block ((const void *) dev_list[i]->model);
+ free_block ((const void *) dev_list[i]);
+ }
+ }
+ free (dev_list);
+ dev_list = NULL;
+}
+
+static void
+find_scanners (void)
+{
+ unsigned i, nscanners;
+
+ cleanup_device_list ();
+ nscanners = pixma_find_scanners (conf_devices);
+ PDBG (pixma_dbg (3, "pixma_find_scanners() found %u devices\n", nscanners));
+ dev_list =
+ (const SANE_Device **) calloc (nscanners + 1, sizeof (*dev_list));
+ if (!dev_list)
+ return;
+ for (i = 0; i != nscanners; i++)
+ {
+ SANE_Device *sdev = (SANE_Device *) calloc (1, sizeof (*sdev));
+ char *name, *model;
+ if (!sdev)
+ goto nomem;
+ name = strdup (pixma_get_device_id (i));
+ model = strdup (pixma_get_device_model (i));
+ if (!name || !model)
+ {
+ free (name);
+ free (model);
+ free (sdev);
+ goto nomem;
+ }
+ sdev->name = name;
+ sdev->model = model;
+ sdev->vendor = vendor_str;
+ sdev->type = type_str;
+ dev_list[i] = sdev;
+ }
+ /* dev_list is already NULL terminated by calloc(). */
+ return;
+
+nomem:
+ PDBG (pixma_dbg (1, "WARNING:not enough memory for device list\n"));
+ return;
+}
+
+static pixma_sane_t *
+check_handle (SANE_Handle h)
+{
+ pixma_sane_t *p;
+
+ for (p = first_scanner; p && (SANE_Handle) p != h; p = p->next)
+ {
+ }
+ return p;
+}
+
+static void
+update_button_state (pixma_sane_t * ss, SANE_Int * info)
+{
+ SANE_Int b1 = OVAL (opt_button_1).w;
+ SANE_Int b2 = OVAL (opt_button_2).w;
+ uint32_t ev = pixma_wait_event (ss->s, 300);
+ switch (ev & ~PIXMA_EV_ACTION_MASK)
+ {
+ case PIXMA_EV_BUTTON1:
+ b1 = 1;
+ break;
+ case PIXMA_EV_BUTTON2:
+ b2 = 1;
+ break;
+ }
+
+ if (b1 != OVAL (opt_button_1).w || b2 != OVAL (opt_button_2).w)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ OVAL (opt_button_1).w = b1;
+ OVAL (opt_button_2).w = b2;
+ OVAL (opt_original).w = GET_EV_ORIGINAL(ev);
+ OVAL (opt_target).w = GET_EV_TARGET(ev);
+ OVAL (opt_scan_resolution).w = GET_EV_DPI(ev);
+ }
+ mark_all_button_options_cached(ss);
+}
+
+static SANE_Bool
+enable_option (pixma_sane_t * ss, SANE_Int o, SANE_Bool enable)
+{
+ SANE_Word save = SOD (o).cap;
+ if (enable)
+ SOD (o).cap &= ~SANE_CAP_INACTIVE;
+ else
+ SOD (o).cap |= SANE_CAP_INACTIVE;
+ return (save != SOD (o).cap);
+}
+
+static void
+clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info)
+{
+ SANE_Option_Descriptor *sod = &SOD (n);
+ SANE_Word *va = (SANE_Word *) v;
+ const SANE_Range *range = sod->constraint.range;
+ int i, nmemb;
+
+ nmemb = sod->size / sizeof (SANE_Word);
+ for (i = 0; i < nmemb; i++)
+ {
+ SANE_Word value = va[i];
+ if (value < range->min)
+ {
+ value = range->min;
+ }
+ else if (value > range->max)
+ {
+ value = range->max;
+ }
+ if (range->quant != 0)
+ {
+ value = (value - range->min + range->quant / 2) /
+ range->quant * range->quant;
+ }
+ if (value != va[i])
+ {
+ va[i] = value;
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+}
+
+/* create dynamic mode_list
+ * ss: scanner device
+ * tpu = 0: flatbed or ADF mode
+ * 1 bit lineart, 8 bit grayscale and 24 bit color scans
+ * tpu = 1: TPU mode
+ * 16 bit grayscale and 48 bit color scans */
+static void
+create_mode_list (pixma_sane_t * ss)
+{
+ SANE_Bool tpu;
+ const pixma_config_t *cfg;
+ int i;
+
+ cfg = pixma_get_config (ss->s);
+ tpu = (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU);
+
+ /* setup available mode */
+ i = 0;
+ ss->mode_list[i] = SANE_VALUE_SCAN_MODE_COLOR;
+ ss->mode_map[i] = PIXMA_SCAN_MODE_COLOR;
+ i++;
+ if (cfg->cap & PIXMA_CAP_GRAY)
+ {
+ ss->mode_list[i] = SANE_VALUE_SCAN_MODE_GRAY;
+ ss->mode_map[i] = PIXMA_SCAN_MODE_GRAY;
+ i++;
+ }
+ if (tpu && (cfg->cap & PIXMA_CAP_NEGATIVE))
+ {
+ ss->mode_list[i] = SANE_I18N ("Negative color");
+ ss->mode_map[i] = PIXMA_SCAN_MODE_NEGATIVE_COLOR;
+ i++;
+ if (cfg->cap & PIXMA_CAP_GRAY)
+ {
+ ss->mode_list[i] = SANE_I18N ("Negative gray");
+ ss->mode_map[i] = PIXMA_SCAN_MODE_NEGATIVE_GRAY;
+ i++;
+ }
+ }
+ if (tpu && (cfg->cap & PIXMA_CAP_TPUIR) == PIXMA_CAP_TPUIR)
+ {
+ ss->mode_list[i] = SANE_I18N ("Infrared");
+ ss->mode_map[i] = PIXMA_SCAN_MODE_TPUIR;
+ i++;
+ }
+ if (!tpu && (cfg->cap & PIXMA_CAP_48BIT))
+ {
+ ss->mode_list[i] = SANE_I18N ("48 bits color");
+ ss->mode_map[i] = PIXMA_SCAN_MODE_COLOR_48;
+ i++;
+ if (cfg->cap & PIXMA_CAP_GRAY)
+ {
+ ss->mode_list[i] = SANE_I18N ("16 bits gray");
+ ss->mode_map[i] = PIXMA_SCAN_MODE_GRAY_16;
+ i++;
+ }
+ }
+ if (!tpu && (cfg->cap & PIXMA_CAP_LINEART))
+ {
+ ss->mode_list[i] = SANE_VALUE_SCAN_MODE_LINEART;
+ ss->mode_map[i] = PIXMA_SCAN_MODE_LINEART;
+ i++;
+ }
+ /* terminate mode_list and mode_map */
+ ss->mode_list[i] = 0;
+ ss->mode_map[i] = 0;
+}
+
+/* create dynamic dpi_list
+ * ss: scanner device */
+static void
+create_dpi_list (pixma_sane_t * ss)
+{
+ const pixma_config_t *cfg;
+ int i, j;
+ int min;
+ unsigned min_dpi;
+ unsigned max_dpi;
+
+ cfg = pixma_get_config (ss->s);
+
+ /* get min/max dpi */
+ max_dpi = cfg->xdpi;
+ min_dpi = 75;
+ if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU
+ && ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_TPUIR)
+ { /* IR mode */
+ /*PDBG (pixma_dbg (4, "*create_dpi_list***** TPUIR mode\n"));*/
+ min_dpi = (cfg->tpuir_min_dpi) ? cfg->tpuir_min_dpi : 75;
+ max_dpi = (cfg->tpuir_max_dpi) ? cfg->tpuir_max_dpi : cfg->xdpi;
+ }
+ else if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU
+ || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADF
+ || ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_ADFDUP)
+ { /* ADF / TPU mode */
+ /*PDBG (pixma_dbg (4, "*create_dpi_list***** ADF/TPU mode\n"));*/
+ min_dpi = (cfg->adftpu_min_dpi) ? cfg->adftpu_min_dpi : 75;
+ max_dpi = (cfg->adftpu_max_dpi) ? cfg->adftpu_max_dpi : cfg->xdpi;
+ }
+ else if (ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED
+ && (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_COLOR_48
+ || ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_GRAY_16))
+ { /* 48 bits flatbed */
+ /*PDBG (pixma_dbg (4, "*create_dpi_list***** 48 bits flatbed mode\n"));*/
+ min_dpi = 150;
+ }
+
+ /* set j for min. dpi
+ * 75 dpi: j = 0
+ * 150 dpi: j = 1 \
+ * 300 dpi: j = 2 |--> from cfg->adftpu_min_dpi or cfg->tpuir_min_dpi
+ * 600 dpi: j = 3 /
+ * */
+ j = -1;
+ min = min_dpi / 75;
+ do
+ {
+ j++;
+ min >>= 1;
+ }
+ while (min > 0);
+
+ /* create dpi_list
+ * use j for min. dpi */
+ i = 0;
+ do
+ {
+ i++; j++;
+ ss->dpi_list[i] = 75 * (1 << (j - 1)); /* 75 x 2^(j-1) */
+ }
+ while ((unsigned) ss->dpi_list[i] < max_dpi);
+ ss->dpi_list[0] = i;
+ /*PDBG (pixma_dbg (4, "*create_dpi_list***** min_dpi = %d, max_dpi = %d\n", min_dpi, max_dpi));*/
+}
+
+static void
+select_value_from_list (pixma_sane_t * ss, SANE_Int n, void *v,
+ SANE_Int * info)
+{
+ SANE_Option_Descriptor *sod = &SOD (n);
+ SANE_Word *va = (SANE_Word *) v;
+ const SANE_Word *list = sod->constraint.word_list;
+ int i, j, nmemb;
+
+ nmemb = sod->size / sizeof (SANE_Word);
+ for (i = 0; i < nmemb; i++)
+ {
+ SANE_Word value = va[i];
+ SANE_Word mindelta = abs (value - list[1]);
+ SANE_Word nearest = list[1];
+ for (j = 2; j <= list[0]; j++)
+ {
+ SANE_Word delta = abs (value - list[j]);
+ if (delta < mindelta)
+ {
+ mindelta = delta;
+ nearest = list[j];
+ }
+ if (mindelta == 0)
+ break;
+ }
+ if (va[i] != nearest)
+ {
+ va[i] = nearest;
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+}
+
+static SANE_Status
+control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v,
+ SANE_Int * info)
+{
+ option_descriptor_t *opt = &(OPT_IN_CTX[n]);
+ SANE_Word val;
+
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+ switch (opt->sod.type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ *(SANE_Word *) v = opt->val.w;
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+
+ case SANE_ACTION_SET_VALUE:
+ switch (opt->sod.type)
+ {
+ case SANE_TYPE_BOOL:
+ val = *(SANE_Word *) v;
+ if (val != SANE_TRUE && val != SANE_FALSE)
+ return SANE_STATUS_INVAL;
+ opt->val.w = val;
+ break;
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE)
+ clamp_value (ss, n, v, info);
+ else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST)
+ select_value_from_list (ss, n, v, info);
+ opt->val.w = *(SANE_Word *) v;
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ *info |= opt->info;
+ return SANE_STATUS_GOOD;
+
+ case SANE_ACTION_SET_AUTO:
+ switch (opt->sod.type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ opt->val.w = opt->def.w;
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ *info |= opt->info;
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+static SANE_Status
+control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v,
+ SANE_Int * info)
+{
+ option_descriptor_t *opt = &(OPT_IN_CTX[n]);
+ const SANE_String_Const *slist = opt->sod.constraint.string_list;
+ SANE_String str = (SANE_String) v;
+
+ if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE)
+ {
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+ strcpy (str, opt->val.s);
+ break;
+ case SANE_ACTION_SET_AUTO:
+ str = opt->def.s;
+ /* fall through */
+ case SANE_ACTION_SET_VALUE:
+ strncpy (opt->val.s, str, opt->sod.size - 1);
+ *info |= opt->info;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ int i;
+
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+ strcpy (str, slist[opt->val.w]);
+ break;
+ case SANE_ACTION_SET_AUTO:
+ str = opt->def.ptr;
+ /* fall through */
+ case SANE_ACTION_SET_VALUE:
+ i = 0;
+ while (slist[i] && strcasecmp (str, slist[i]) != 0)
+ i++;
+ if (!slist[i])
+ return SANE_STATUS_INVAL;
+ if (strcmp (slist[i], str) != 0)
+ {
+ strcpy (str, slist[i]);
+ *info |= SANE_INFO_INEXACT;
+ }
+ opt->val.w = i;
+ *info |= opt->info;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+ }
+}
+
+static SANE_Status
+control_option (pixma_sane_t * ss, SANE_Int n,
+ SANE_Action a, void *v, SANE_Int * info)
+{
+ int result, i;
+ const pixma_config_t *cfg;
+ SANE_Int dummy;
+
+ /* info may be null, better to set a dummy here then test everywhere */
+ if (info == NULL)
+ info = &dummy;
+
+ cfg = pixma_get_config (ss->s);
+
+ /* PDBG (pixma_dbg (4, "*control_option***** n = %u, a = %u\n", n, a)); */
+
+ /* first deal with options that require special treatment */
+ result = SANE_STATUS_UNSUPPORTED;
+ switch (n)
+ {
+ case opt_gamma_table:
+ switch (a)
+ {
+ case SANE_ACTION_SET_VALUE:
+ clamp_value (ss, n, v, info);
+ for (i = 0; i != 4096; i++)
+ ss->gamma_table[i] = *((SANE_Int *) v + i);
+ break;
+ case SANE_ACTION_GET_VALUE:
+ for (i = 0; i != 4096; i++)
+ *((SANE_Int *) v + i) = ss->gamma_table[i];
+ break;
+ case SANE_ACTION_SET_AUTO:
+ pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table,
+ sizeof (ss->gamma_table));
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+
+ case opt_button_update:
+ if (a == SANE_ACTION_SET_VALUE)
+ {
+ update_button_state (ss, info);
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case opt_button_1:
+ case opt_button_2:
+ case opt_original:
+ case opt_target:
+ case opt_scan_resolution:
+ /* poll scanner if option is not cached */
+ if (! ss->button_option_is_cached[ BUTTON_GROUP_INDEX(n) ] )
+ update_button_state (ss, info);
+ /* mark this option as read */
+ ss->button_option_is_cached[ BUTTON_GROUP_INDEX(n) ] = 0;
+ }
+
+ /* now deal with getting and setting of options */
+ switch (SOD (n).type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ result = control_scalar_option (ss, n, a, v, info);
+ break;
+ case SANE_TYPE_STRING:
+ result = control_string_option (ss, n, a, v, info);
+ break;
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP:
+ PDBG (pixma_dbg (1, "BUG:control_option():Unhandled option\n"));
+ result = SANE_STATUS_INVAL;
+ break;
+ }
+ if (result != SANE_STATUS_GOOD)
+ return result;
+
+ /* deal with dependencies between options */
+ switch (n)
+ {
+ case opt_custom_gamma:
+ if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)
+ {
+ if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b))
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ case opt_gamma:
+ if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)
+ {
+ /* PDBG (pixma_dbg (4, "*control_option***** gamma = %f *\n",
+ SANE_UNFIX (OVAL (opt_gamma).w))); */
+ pixma_fill_gamma_table (SANE_UNFIX (OVAL (opt_gamma).w),
+ ss->gamma_table, sizeof (ss->gamma_table));
+ }
+ break;
+ case opt_mode:
+ if (cfg->cap & (PIXMA_CAP_48BIT|PIXMA_CAP_LINEART|PIXMA_CAP_TPUIR)
+ && (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO))
+ { /* new mode selected: Color, Gray, ... */
+ /* PDBG (pixma_dbg (4, "*control_option***** mode = %u *\n",
+ ss->mode_map[OVAL (opt_mode).w])); */
+ /* recreate dynamic lists */
+ create_dpi_list (ss);
+ if (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_LINEART)
+ { /* lineart */
+ enable_option (ss, opt_threshold, SANE_TRUE);
+ enable_option (ss, opt_threshold_curve, SANE_TRUE);
+ }
+ else
+ { /* all other modes */
+ enable_option (ss, opt_threshold, SANE_FALSE);
+ enable_option (ss, opt_threshold_curve, SANE_FALSE);
+ }
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ case opt_source:
+ if ((cfg->cap & (PIXMA_CAP_ADF|PIXMA_CAP_ADFDUP|PIXMA_CAP_TPU))
+ && (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO))
+ { /* new source selected: flatbed, ADF, TPU, ... */
+ /* to avoid fatal errors,
+ * select first entry of dynamic mode_list
+ * identifiers are unknown here */
+ OVAL (opt_mode).w = ss->mode_map[0];
+ /* recreate dynamic lists */
+ create_mode_list (ss);
+ create_dpi_list (ss);
+ /* to avoid fatal errors,
+ * select first entry of dynamic dpi_list
+ * identifiers are unknown here */
+ OVAL (opt_resolution).w = ss->dpi_list[1];
+ if (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_LINEART)
+ { /* lineart */
+ enable_option (ss, opt_threshold, SANE_TRUE);
+ enable_option (ss, opt_threshold_curve, SANE_TRUE);
+ }
+ else
+ { /* all other modes */
+ enable_option (ss, opt_threshold, SANE_FALSE);
+ enable_option (ss, opt_threshold_curve, SANE_FALSE);
+ }
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ }
+
+ return result;
+}
+
+#ifndef NDEBUG
+static void
+print_scan_param (int level, const pixma_scan_param_t * sp)
+{
+ pixma_dbg (level, "Scan parameters\n");
+ pixma_dbg (level, " line_size=%"PRIu64" image_size=%"PRIu64" channels=%u depth=%u\n",
+ sp->line_size, sp->image_size, sp->channels, sp->depth);
+ pixma_dbg (level, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n",
+ sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h);
+ pixma_dbg (level, " gamma_table=%p source=%d\n", sp->gamma_table,
+ sp->source);
+}
+#endif
+
+static int
+calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp)
+{
+ int x1, y1, x2, y2;
+ int error;
+
+ memset (sp, 0, sizeof (*sp));
+
+ sp->channels = (OVAL (opt_mode).w == 0) ? 3 : 1;
+ sp->depth = (OVAL (opt_mode).w == 2) ? 1 : 8;
+ sp->xdpi = sp->ydpi = OVAL (opt_resolution).w;
+
+#define PIXEL(x,dpi) (int)((SANE_UNFIX(x) / 25.4 * (dpi)) + 0.5)
+ x1 = PIXEL (OVAL (opt_tl_x).w, sp->xdpi);
+ x2 = PIXEL (OVAL (opt_br_x).w, sp->xdpi);
+ if (x2 < x1)
+ {
+ int temp = x1;
+ x1 = x2;
+ x2 = temp;
+ }
+ y1 = PIXEL (OVAL (opt_tl_y).w, sp->ydpi);
+ y2 = PIXEL (OVAL (opt_br_y).w, sp->ydpi);
+ if (y2 < y1)
+ {
+ int temp = y1;
+ y1 = y2;
+ y2 = temp;
+ }
+#undef PIXEL
+ sp->x = x1;
+ sp->y = y1;
+ sp->w = x2 - x1;
+ sp->h = y2 - y1;
+ if (sp->w == 0)
+ sp->w = 1;
+ if (sp->h == 0)
+ sp->h = 1;
+ sp->tpu_offset_added = 0;
+
+ sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL;
+ sp->source = ss->source_map[OVAL (opt_source).w];
+ sp->mode = ss->mode_map[OVAL (opt_mode).w];
+ sp->adf_pageid = ss->page_count;
+ sp->threshold = 2.55 * OVAL (opt_threshold).w;
+ sp->threshold_curve = OVAL (opt_threshold_curve).w;
+
+ error = pixma_check_scan_param (ss->s, sp);
+ if (error < 0)
+ {
+ PDBG (pixma_dbg (1, "BUG:calc_scan_param() failed %d\n", error));
+ PDBG (print_scan_param (1, sp));
+ }
+ return error;
+}
+
+static void
+init_option_descriptors (pixma_sane_t * ss)
+{
+ const pixma_config_t *cfg;
+ int i;
+
+ cfg = pixma_get_config (ss->s);
+
+ /* setup range for the scan area. */
+ ss->xrange.min = SANE_FIX (0);
+ ss->xrange.max = SANE_FIX (cfg->width / 75.0 * 25.4);
+ ss->xrange.quant = SANE_FIX (0);
+
+ ss->yrange.min = SANE_FIX (0);
+ ss->yrange.max = SANE_FIX (cfg->height / 75.0 * 25.4);
+ ss->yrange.quant = SANE_FIX (0);
+
+ /* mode_list and source_list were already NULL-terminated,
+ * because the whole pixma_sane_t was cleared during allocation. */
+
+ /* setup available mode. */
+ create_mode_list (ss);
+
+ /* setup dpi up to the value supported by the scanner. */
+ create_dpi_list (ss);
+
+ /* setup paper source */
+ i = 0;
+ ss->source_list[i] = SANE_I18N ("Flatbed");
+ ss->source_map[i] = PIXMA_SOURCE_FLATBED;
+ i++;
+ if (cfg->cap & PIXMA_CAP_ADF)
+ {
+ ss->source_list[i] = SANE_I18N ("Automatic Document Feeder");
+ ss->source_map[i] = PIXMA_SOURCE_ADF;
+ i++;
+ }
+ if ((cfg->cap & PIXMA_CAP_ADFDUP) == PIXMA_CAP_ADFDUP)
+ {
+ ss->source_list[i] = SANE_I18N ("ADF Duplex");
+ ss->source_map[i] = PIXMA_SOURCE_ADFDUP;
+ i++;
+ }
+ if (cfg->cap & PIXMA_CAP_TPU)
+ {
+ ss->source_list[i] = SANE_I18N ("Transparency Unit");
+ ss->source_map[i] = PIXMA_SOURCE_TPU;
+ i++;
+ }
+
+ build_option_descriptors (ss);
+
+ /* Enable options that are available only in some scanners. */
+ if (cfg->cap & PIXMA_CAP_GAMMA_TABLE)
+ {
+ enable_option (ss, opt_gamma, SANE_TRUE);
+ enable_option (ss, opt_custom_gamma, SANE_TRUE);
+ sane_control_option (ss, opt_custom_gamma, SANE_ACTION_SET_AUTO,
+ NULL, NULL);
+ pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, 4096);
+ }
+ enable_option (ss, opt_button_controlled,
+ ((cfg->cap & PIXMA_CAP_EVENTS) != 0));
+}
+
+/* Writing to reader_ss outside reader_process() is a BUG! */
+static pixma_sane_t *reader_ss = NULL;
+
+static RETSIGTYPE
+reader_signal_handler (int sig)
+{
+ if (reader_ss)
+ {
+ reader_ss->reader_stop = SANE_TRUE;
+ /* reader process is ended by SIGTERM, so no cancel in this case */
+ if (sig != SIGTERM)
+ pixma_cancel (reader_ss->s);
+ }
+}
+
+static int
+write_all (pixma_sane_t * ss, void *buf_, size_t size)
+{
+ uint8_t *buf = (uint8_t *) buf_;
+ int count;
+
+ while (size != 0 && !ss->reader_stop)
+ {
+ count = write (ss->wpipe, buf, size);
+ if (count == -1 && errno != EINTR)
+ break;
+ if (count == -1 && errno == EINTR)
+ continue;
+ buf += count;
+ size -= count;
+ }
+ return buf - (uint8_t *) buf_;
+}
+
+/* NOTE: reader_loop() runs either in a separate thread or process. */
+static SANE_Status
+reader_loop (pixma_sane_t * ss)
+{
+ void *buf;
+ unsigned bufsize;
+ int count = 0;
+
+ PDBG (pixma_dbg (3, "Reader task started\n"));
+ /*bufsize = ss->sp.line_size + 1;*/ /* XXX: "odd" bufsize for testing pixma_read_image() */
+ bufsize = ss->sp.line_size; /* bufsize EVEN needed by Xsane for 48 bits depth */
+ buf = malloc (bufsize);
+ if (!buf)
+ {
+ count = PIXMA_ENOMEM;
+ goto done;
+ }
+
+ count = pixma_activate_connection (ss->s);
+ if (count < 0)
+ goto done;
+
+ pixma_enable_background (ss->s, 1);
+ if (OVAL (opt_button_controlled).b && ss->page_count == 0)
+ {
+ int start = 0;
+#ifndef NDEBUG
+ pixma_dbg (1, "==== Button-controlled scan mode is enabled.\n");
+ pixma_dbg (1, "==== To proceed, press 'SCAN' or 'COLOR' button. "
+ "To cancel, press 'GRAY' or 'END' button.\n");
+#endif
+ while (pixma_wait_event (ss->s, 10) != 0)
+ {
+ }
+ while (!start)
+ {
+ uint32_t events;
+ if (ss->reader_stop)
+ {
+ count = PIXMA_ECANCELED;
+ goto done;
+ }
+ events = pixma_wait_event (ss->s, 1000);
+ switch (events & ~PIXMA_EV_ACTION_MASK)
+ {
+ case PIXMA_EV_BUTTON1:
+ start = 1;
+ break;
+ case PIXMA_EV_BUTTON2:
+ count = PIXMA_ECANCELED;
+ goto done;
+ }
+ }
+ }
+ count = pixma_scan (ss->s, &ss->sp);
+ if (count >= 0)
+ {
+ while ((count = pixma_read_image (ss->s, buf, bufsize)) > 0)
+ {
+ if (write_all (ss, buf, count) != count)
+ pixma_cancel (ss->s);
+ }
+ }
+
+done:
+ pixma_enable_background (ss->s, 0);
+ pixma_deactivate_connection (ss->s);
+ free (buf);
+ close (ss->wpipe);
+ ss->wpipe = -1;
+ if (count >= 0)
+ {
+ PDBG (pixma_dbg (3, "Reader task terminated\n"));
+ }
+ else
+ {
+ PDBG (pixma_dbg
+ (2, "Reader task terminated: %s\n", pixma_strerror (count)));
+ }
+ return map_error (count);
+}
+
+static int
+reader_process (void *arg)
+{
+ pixma_sane_t *ss = (pixma_sane_t *) arg;
+ struct SIGACTION sa;
+
+ reader_ss = ss;
+ memset (&sa, 0, sizeof (sa));
+ sigemptyset (&sa.sa_mask);
+ sa.sa_handler = reader_signal_handler;
+ /* FIXME: which signal else? */
+ sigaction (SIGHUP, &sa, NULL);
+ sigaction (SIGINT, &sa, NULL);
+ sigaction (SIGPIPE, &sa, NULL);
+ sigaction (SIGTERM, &sa, NULL);
+ close (ss->rpipe);
+ ss->rpipe = -1;
+ return reader_loop (ss);
+}
+
+static int
+reader_thread (void *arg)
+{
+ pixma_sane_t *ss = (pixma_sane_t *) arg;
+#ifdef USE_PTHREAD
+ /* Block SIGPIPE. We will handle this in reader_loop() by checking
+ ss->reader_stop and the return value from write(). */
+ sigset_t sigs;
+ sigemptyset (&sigs);
+ sigaddset (&sigs, SIGPIPE);
+ pthread_sigmask (SIG_BLOCK, &sigs, NULL);
+#endif /* USE_PTHREAD */
+ return reader_loop (ss);
+}
+
+static SANE_Pid
+terminate_reader_task (pixma_sane_t * ss, int *exit_code)
+{
+ SANE_Pid result, pid;
+ int status = 0;
+
+ pid = ss->reader_taskid;
+ if (pid == -1)
+ return -1;
+ if (sanei_thread_is_forked ())
+ {
+ sanei_thread_kill (pid);
+ }
+ else
+ {
+ ss->reader_stop = SANE_TRUE;
+/* pixma_cancel (ss->s); What is this for ? Makes end-of-scan buggy => removing */
+ }
+ result = sanei_thread_waitpid (pid, &status);
+ ss->reader_taskid = -1;
+
+ if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
+ ss->idle = SANE_TRUE;
+
+ if (result == pid)
+ {
+ if (exit_code)
+ *exit_code = status;
+ return pid;
+ }
+ else
+ {
+ PDBG (pixma_dbg (1, "WARNING:waitpid() failed %s\n", strerror (errno)));
+ return -1;
+ }
+}
+
+static int
+start_reader_task (pixma_sane_t * ss)
+{
+ int fds[2];
+ SANE_Pid pid;
+ int is_forked;
+
+ if (ss->rpipe != -1 || ss->wpipe != -1)
+ {
+ PDBG (pixma_dbg
+ (1, "BUG:rpipe = %d, wpipe = %d\n", ss->rpipe, ss->wpipe));
+ close (ss->rpipe);
+ close (ss->wpipe);
+ ss->rpipe = -1;
+ ss->wpipe = -1;
+ }
+ if (ss->reader_taskid != -1)
+ {
+ PDBG (pixma_dbg
+ (1, "BUG:reader_taskid(%ld) != -1\n", (long) ss->reader_taskid));
+ terminate_reader_task (ss, NULL);
+ }
+ if (pipe (fds) == -1)
+ {
+ PDBG (pixma_dbg (1, "ERROR:start_reader_task():pipe() failed %s\n",
+ strerror (errno)));
+ return PIXMA_ENOMEM;
+ }
+ ss->rpipe = fds[0];
+ ss->wpipe = fds[1];
+ ss->reader_stop = SANE_FALSE;
+
+ is_forked = sanei_thread_is_forked ();
+ if (is_forked)
+ {
+ pid = sanei_thread_begin (reader_process, ss);
+ if (pid > 0)
+ {
+ close (ss->wpipe);
+ ss->wpipe = -1;
+ }
+ }
+ else
+ {
+ pid = sanei_thread_begin (reader_thread, ss);
+ }
+ if (pid == -1)
+ {
+ close (ss->wpipe);
+ close (ss->rpipe);
+ ss->wpipe = -1;
+ ss->rpipe = -1;
+ PDBG (pixma_dbg (1, "ERROR:unable to start reader task\n"));
+ return PIXMA_ENOMEM;
+ }
+ PDBG (pixma_dbg (3, "Reader task id=%ld (%s)\n", (long) pid,
+ (is_forked) ? "forked" : "threaded"));
+ ss->reader_taskid = pid;
+ return 0;
+}
+
+static SANE_Status
+read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
+{
+ int count, status;
+
+ if (readlen)
+ *readlen = 0;
+ if (ss->image_bytes_read >= ss->sp.image_size)
+ return SANE_STATUS_EOF;
+
+ do
+ {
+ if (ss->cancel)
+ /* ss->rpipe has already been closed by sane_cancel(). */
+ return SANE_STATUS_CANCELLED;
+ count = read (ss->rpipe, buf, size);
+ }
+ while (count == -1 && errno == EINTR);
+
+ if (count == -1)
+ {
+ if (errno == EAGAIN)
+ return SANE_STATUS_GOOD;
+ if (!ss->cancel)
+ {
+ PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n",
+ strerror (errno)));
+ }
+ close (ss->rpipe);
+ ss->rpipe = -1;
+ terminate_reader_task (ss, NULL);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* here count >= 0 */
+ ss->image_bytes_read += count;
+ if (ss->image_bytes_read > ss->sp.image_size)
+ {
+ PDBG (pixma_dbg (1, "BUG:ss->image_bytes_read > ss->sp.image_size\n"));
+ }
+ if (ss->image_bytes_read >= ss->sp.image_size)
+ {
+ close (ss->rpipe);
+ ss->rpipe = -1;
+ terminate_reader_task (ss, NULL);
+ }
+ else if (count == 0)
+ {
+ PDBG (pixma_dbg (3, "read_image():reader task closed the pipe:%"
+ PRIu64" bytes received, %"PRIu64" bytes expected\n",
+ ss->image_bytes_read, ss->sp.image_size));
+ close (ss->rpipe);
+ ss->rpipe = -1;
+ if (terminate_reader_task (ss, &status) != -1
+ && status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ else
+ {
+ /* either terminate_reader_task failed or
+ rpipe was closed but we expect more data */
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (readlen)
+ *readlen = count;
+ return SANE_STATUS_GOOD;
+}
+
+
+/*******************************************************************
+ ** SANE API
+ *******************************************************************/
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ int status, myversion, i;
+ SANEI_Config config;
+
+ UNUSED (authorize);
+
+ if (!version_code)
+ return SANE_STATUS_INVAL;
+ myversion = 100 * PIXMA_VERSION_MAJOR + PIXMA_VERSION_MINOR;
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, myversion);
+ DBG_INIT ();
+ sanei_thread_init ();
+ pixma_set_debug_level (DBG_LEVEL);
+
+ PDBG(pixma_dbg(2, "pixma is compiled %s pthread support.\n",
+ (sanei_thread_is_forked () ? "without" : "with")));
+
+ for (i = 0; i < MAX_CONF_DEVICES; i++)
+ conf_devices[i] = NULL;
+
+ config.count = 0;
+ config.descriptors = NULL;
+ config.values = NULL;
+
+ if (sanei_configure_attach(PIXMA_CONFIG_FILE, &config, config_attach_pixma) !=
+ SANE_STATUS_GOOD)
+ PDBG(pixma_dbg(2, "Could not read pixma configuration file: %s\n",
+ PIXMA_CONFIG_FILE));
+
+ status = pixma_init ();
+ if (status < 0)
+ {
+ PDBG (pixma_dbg (2, "pixma_init() failed %s\n", pixma_strerror (status)));
+ }
+ return map_error (status);
+}
+
+void
+sane_exit (void)
+{
+ while (first_scanner)
+ sane_close (first_scanner);
+ cleanup_device_list ();
+ pixma_cleanup ();
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ UNUSED (local_only);
+
+ if (!device_list)
+ return SANE_STATUS_INVAL;
+ find_scanners ();
+ *device_list = dev_list;
+ return (dev_list) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ unsigned i, j, nscanners;
+ int error = 0;
+ pixma_sane_t *ss = NULL;
+ const pixma_config_t *cfg;
+
+ if (!name || !h)
+ return SANE_STATUS_INVAL;
+
+ *h = NULL;
+ nscanners = pixma_find_scanners (conf_devices);
+ if (nscanners == 0)
+ return SANE_STATUS_INVAL;
+ if (name[0] == '\0')
+ name = pixma_get_device_id (0);
+
+ /* Have we already opened the scanner? */
+ for (ss = first_scanner; ss; ss = ss->next)
+ {
+ if (strcmp (pixma_get_string (ss->s, PIXMA_STRING_ID), name) == 0)
+ {
+ /* We have already opened it! */
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ i = 0;
+ while (strcmp (pixma_get_device_id (i), name) != 0)
+ {
+ if (++i >= nscanners)
+ return SANE_STATUS_INVAL;
+ }
+ cfg = pixma_get_device_config (i);
+ if ((cfg->cap & PIXMA_CAP_EXPERIMENT) != 0)
+ {
+#ifndef NDEBUG
+ pixma_dbg (1, "WARNING:"
+ "Experimental backend CAN DAMAGE your hardware!\n");
+ if (getenv_atoi ("PIXMA_EXPERIMENT", 0) == 0)
+ {
+ pixma_dbg (1, "Experimental SANE backend for %s is disabled "
+ "by default.\n", pixma_get_device_model (i));
+ pixma_dbg (1, "To enable it, set the environment variable "
+ "PIXMA_EXPERIMENT to non-zero.\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#else
+ return SANE_STATUS_UNSUPPORTED;
+#endif
+ }
+
+ ss = (pixma_sane_t *) calloc (1, sizeof (*ss));
+ if (!ss)
+ return SANE_STATUS_NO_MEM;
+ ss->next = first_scanner;
+ first_scanner = ss;
+ ss->reader_taskid = -1;
+ ss->wpipe = -1;
+ ss->rpipe = -1;
+ ss->idle = SANE_TRUE;
+ ss->scanning = SANE_FALSE;
+ for (j=0; j < BUTTON_GROUP_SIZE; j++)
+ ss->button_option_is_cached[j] = 0;
+ error = pixma_open (i, &ss->s);
+ if (error < 0)
+ {
+ sane_close (ss);
+ return map_error (error);
+ }
+ pixma_enable_background (ss->s, 0);
+ init_option_descriptors (ss);
+ *h = ss;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ pixma_sane_t **p, *ss;
+
+ for (p = &first_scanner; *p && *p != (pixma_sane_t *) h; p = &((*p)->next))
+ {
+ }
+ if (!(*p))
+ return;
+ ss = *p;
+ sane_cancel (ss);
+ pixma_close (ss->s);
+ *p = ss->next;
+ free (ss);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ DECL_CTX;
+
+ if (ss && 0 <= n && n < opt_last)
+ return &SOD (n);
+ return NULL;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int n,
+ SANE_Action a, void *v, SANE_Int * i)
+{
+ DECL_CTX;
+ SANE_Int info = 0;
+ int error;
+ option_descriptor_t *opt;
+
+ if (i)
+ *i = 0;
+ if (!ss)
+ return SANE_STATUS_INVAL;
+ if (n < 0 || n >= opt_last)
+ return SANE_STATUS_UNSUPPORTED;
+ if (!ss->idle && a != SANE_ACTION_GET_VALUE)
+ {
+ PDBG (pixma_dbg (3, "Warning: !idle && !SANE_ACTION_GET_VALUE\n"));
+ if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
+ return SANE_STATUS_INVAL;
+ }
+
+ opt = &(OPT_IN_CTX[n]);
+ if (!SANE_OPTION_IS_ACTIVE (opt->sod.cap))
+ return SANE_STATUS_INVAL;
+ switch (a)
+ {
+ case SANE_ACTION_SET_VALUE:
+ if ((opt->sod.type != SANE_TYPE_BUTTON && !v) ||
+ !SANE_OPTION_IS_SETTABLE (opt->sod.cap))
+ return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
+ break;
+ case SANE_ACTION_SET_AUTO:
+ if (!(opt->sod.cap & SANE_CAP_AUTOMATIC) ||
+ !SANE_OPTION_IS_SETTABLE (opt->sod.cap))
+ return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
+ break;
+ case SANE_ACTION_GET_VALUE:
+ if (!v || !(opt->sod.cap & SANE_CAP_SOFT_DETECT))
+ return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ error = control_option (ss, n, a, v, &info);
+ if (error == SANE_STATUS_GOOD && i)
+ *i = info;
+ return error;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ DECL_CTX;
+ pixma_scan_param_t temp, *sp;
+
+ if (!ss || !p)
+ return SANE_STATUS_INVAL;
+
+ if (!ss->idle)
+ {
+ sp = &ss->sp; /* sp is calculated in sane_start() */
+ }
+ else
+ {
+ calc_scan_param (ss, &temp);
+ sp = &temp;
+ }
+ p->format = (sp->channels == 3) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ p->last_frame = SANE_TRUE;
+ p->lines = sp->h;
+ p->depth = sp->depth;
+ p->pixels_per_line = sp->w;
+ /* p->bytes_per_line = sp->line_size; NOTE: It should work this way, but it doesn't. No SANE frontend can cope with this. */
+ p->bytes_per_line = (sp->w * sp->channels * sp->depth) / 8;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ DECL_CTX;
+ int error = 0;
+
+ if (!ss)
+ return SANE_STATUS_INVAL;
+ if (!ss->idle && ss->scanning)
+ {
+ PDBG (pixma_dbg (3, "Warning in Sane_start: !idle && scanning. idle=%d, ss->scanning=%d\n",
+ ss->idle, ss->scanning));
+ if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
+ return SANE_STATUS_INVAL;
+ }
+
+ ss->cancel = SANE_FALSE;
+ if (ss->idle ||
+ ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED ||
+ ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU)
+ ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */
+ else
+ ss->page_count++;
+ if (calc_scan_param (ss, &ss->sp) < 0)
+ return SANE_STATUS_INVAL;
+ ss->image_bytes_read = 0;
+ /* TODO: Check paper here in sane_start(). A function like
+ pixma_get_status() is needed. */
+ error = start_reader_task (ss);
+ if (error >= 0)
+ {
+ ss->output_line_size = (ss->sp.w * ss->sp.channels * ss->sp.depth) / 8;
+ ss->byte_pos_in_line = 0;
+ ss->last_read_status = SANE_STATUS_GOOD;
+ ss->scanning = SANE_TRUE;
+ ss->idle = SANE_FALSE;
+ }
+ return map_error (error);
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ DECL_CTX;
+ int sum, n;
+ /* Due to 32 pixels alignment, sizeof(temp) is to be greater than:
+ * max(nchannels) * max (sp.line_size - output_line_size)
+ * so currently: 3 * 32 = 96 for better end line cropping efficiency */
+ SANE_Byte temp[100];
+ SANE_Status status;
+
+ if (len)
+ *len = 0;
+ if (!ss || !buf || !len)
+ return SANE_STATUS_INVAL;
+ if (ss->cancel)
+ return SANE_STATUS_CANCELLED;
+ if ((ss->idle)
+ && (ss->sp.source == PIXMA_SOURCE_ADF || ss->sp.source == PIXMA_SOURCE_ADFDUP))
+ return SANE_STATUS_INVAL;
+ if (!ss->scanning)
+ return ss->last_read_status;
+
+ status = SANE_STATUS_GOOD;
+ /* CCD scanners use software lineart
+ * the scanner must scan 24 bit color or 8 bit grayscale for one bit lineart */
+ if ((ss->sp.line_size - ((ss->sp.software_lineart == 1) ? (ss->output_line_size * 8) : ss->output_line_size)) == 0)
+ {
+ status = read_image (ss, buf, maxlen, &sum);
+ }
+ else
+ {
+ /* FIXME: Because there is no frontend that can cope with padding at
+ the end of line, we've to remove it here in the backend! */
+ PDBG (pixma_dbg (1, "*sane_read***** Warning: padding may cause incomplete scan results\n"));
+ sum = 0;
+ while (sum < maxlen)
+ {
+ if (ss->byte_pos_in_line < ss->output_line_size)
+ {
+ n = ss->output_line_size - ss->byte_pos_in_line;
+ if ((maxlen - sum) < n)
+ n = maxlen - sum;
+ status = read_image (ss, buf, n, &n);
+ if (n == 0)
+ break;
+ sum += n;
+ buf += n;
+ ss->byte_pos_in_line += n;
+ }
+ else
+ {
+ /* skip padding */
+ n = ss->sp.line_size - ss->byte_pos_in_line;
+ if (n > (int) sizeof (temp))
+ {
+ PDBG (pixma_dbg (3, "Inefficient skip buffer. Should be %d\n", n));
+ n = sizeof (temp);
+ }
+ status = read_image (ss, temp, n, &n);
+ if (n == 0)
+ break;
+ ss->byte_pos_in_line += n;
+ if (ss->byte_pos_in_line == ss->sp.line_size)
+ ss->byte_pos_in_line = 0;
+ }
+ }
+ }
+ if (ss->cancel)
+ status = SANE_STATUS_CANCELLED;
+ else if ((status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF) &&
+ sum > 0)
+ {
+ *len = sum;
+ status = SANE_STATUS_GOOD;
+ }
+ ss->scanning = (status == SANE_STATUS_GOOD);
+ ss->last_read_status = status;
+ return status;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ DECL_CTX;
+
+ if (!ss)
+ return;
+ ss->cancel = SANE_TRUE;
+ if (ss->idle)
+ return;
+ close (ss->rpipe);
+ ss->rpipe = -1;
+ terminate_reader_task (ss, NULL);
+ ss->idle = SANE_TRUE;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ DECL_CTX;
+
+ if (!ss || ss->idle || ss->rpipe == -1)
+ return SANE_STATUS_INVAL;
+#ifdef HAVE_FCNTL_H
+ PDBG (pixma_dbg (2, "Setting %sblocking mode\n", (m) ? "non-" : ""));
+ if (fcntl (ss->rpipe, F_SETFL, (m) ? O_NONBLOCK : 0) == -1)
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING:fcntl(F_SETFL) failed %s\n", strerror (errno)));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+#else
+ return (m) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
+#endif
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ DECL_CTX;
+
+ *fd = -1;
+ if (!ss || !fd || ss->idle || ss->rpipe == -1)
+ return SANE_STATUS_INVAL;
+ *fd = ss->rpipe;
+ return SANE_STATUS_GOOD;
+}
+
+/*
+BEGIN SANE_Option_Descriptor
+
+rem -------------------------------------------
+type group
+ title Scan mode
+
+type int resolution
+ unit dpi
+ constraint @word_list = ss->dpi_list
+ default 75
+ title @SANE_TITLE_SCAN_RESOLUTION
+ desc @SANE_DESC_SCAN_RESOLUTION
+ cap soft_select soft_detect automatic
+ info reload_params
+
+type string mode[30]
+ constraint @string_list = ss->mode_list
+ default @s = SANE_VALUE_SCAN_MODE_COLOR
+ title @SANE_TITLE_SCAN_MODE
+ desc @SANE_DESC_SCAN_MODE
+ cap soft_select soft_detect automatic
+ info reload_params
+
+type string source[30]
+ constraint @string_list = ss->source_list
+ title @SANE_TITLE_SCAN_SOURCE
+ desc Selects the scan source (such as a document-feeder). Set source before mode and resolution. Resets mode and resolution to auto values.
+ default Flatbed
+ cap soft_select soft_detect
+
+type bool button-controlled
+ title Button-controlled scan
+ desc When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button.
+ default SANE_FALSE
+ cap soft_select soft_detect inactive
+
+rem -------------------------------------------
+type group
+ title Gamma
+
+type bool custom-gamma
+ default SANE_TRUE
+ title @SANE_TITLE_CUSTOM_GAMMA
+ desc @SANE_DESC_CUSTOM_GAMMA
+ cap soft_select soft_detect automatic inactive
+
+type int gamma-table[4096]
+ constraint (0,255,0)
+ title @SANE_TITLE_GAMMA_VECTOR
+ desc @SANE_DESC_GAMMA_VECTOR
+ cap soft_select soft_detect automatic inactive
+
+type fixed gamma
+ default AUTO_GAMMA
+ constraint (0.3,5,0)
+ title Gamma function exponent
+ desc Changes intensity of midtones
+ cap soft_select soft_detect automatic inactive
+
+rem -------------------------------------------
+type group
+ title Geometry
+
+type fixed tl-x
+ unit mm
+ default 0
+ constraint @range = &ss->xrange
+ title @SANE_TITLE_SCAN_TL_X
+ desc @SANE_DESC_SCAN_TL_X
+ cap soft_select soft_detect automatic
+ info reload_params
+
+type fixed tl-y
+ unit mm
+ default 0
+ constraint @range = &ss->yrange
+ title @SANE_TITLE_SCAN_TL_Y
+ desc @SANE_DESC_SCAN_TL_Y
+ cap soft_select soft_detect automatic
+ info reload_params
+
+type fixed br-x
+ unit mm
+ default _MAX
+ constraint @range = &ss->xrange
+ title @SANE_TITLE_SCAN_BR_X
+ desc @SANE_DESC_SCAN_BR_X
+ cap soft_select soft_detect automatic
+ info reload_params
+
+type fixed br-y
+ unit mm
+ default _MAX
+ constraint @range = &ss->yrange
+ title @SANE_TITLE_SCAN_BR_Y
+ desc @SANE_DESC_SCAN_BR_Y
+ cap soft_select soft_detect automatic
+ info reload_params
+
+rem -------------------------------------------
+type group
+ title Buttons
+
+type button button-update
+ title Update button state
+ cap soft_select soft_detect advanced
+
+type int button-1
+ default 0
+ title Button 1
+ cap soft_detect advanced
+
+type int button-2
+ default 0
+ title Button 2
+ cap soft_detect advanced
+
+type int original
+ default 0
+ title Type of original to scan
+ cap soft_detect advanced
+
+type int target
+ default 0
+ title Target operation type
+ cap soft_detect advanced
+
+type int scan-resolution
+ default 0
+ title Scan resolution
+ cap soft_detect advanced
+
+rem -------------------------------------------
+type group
+ title Extras
+
+type int threshold
+ unit PERCENT
+ default 50
+ constraint (0,100,1)
+ title @SANE_TITLE_THRESHOLD
+ desc @SANE_DESC_THRESHOLD
+ cap soft_select soft_detect automatic inactive
+
+type int threshold-curve
+ constraint (0,127,1)
+ title Threshold curve
+ desc Dynamic threshold curve, from light to dark, normally 50-65
+ cap soft_select soft_detect automatic inactive
+
+rem -------------------------------------------
+END SANE_Option_Descriptor
+*/
+
+/* pixma_sane_options.c generated by
+ * scripts/pixma_gen_options.py < pixma.c > pixma_sane_options.c
+ *
+ * pixma_sane_options.h generated by
+ * scripts/pixma_gen_options.py h < pixma.c > pixma_sane_options.h
+ */
+#include "pixma_sane_options.c"
diff --git a/backend/pixma.conf.in b/backend/pixma.conf.in
new file mode 100644
index 0000000..a275b03
--- /dev/null
+++ b/backend/pixma.conf.in
@@ -0,0 +1,14 @@
+# pixma.conf configuration for the sane pixma backend
+#
+# define URI's of scanners (one per line)
+# This is only used for network scanners.
+# normally scanners will be detected by sending a broadcast
+# if this does not work under your OS, or if the scanners
+# are on a different subnet, configure your scanners URI here
+#
+# method must be bjnp
+# port number can normally be left out, port 8612 is used as default
+# Example:
+# bjnp://myscanner.my.domain:8612
+# bjnp://printer-1.pheasant.org
+#
diff --git a/backend/pixma.h b/backend/pixma.h
new file mode 100644
index 0000000..56bd14d
--- /dev/null
+++ b/backend/pixma.h
@@ -0,0 +1,490 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#ifndef PIXMA_H
+#define PIXMA_H
+
+/*!
+ * \mainpage Scanner driver for Canon PIXMA MP series
+ * \section example Sample code for application
+ * \code
+ * pixma_set_debug_level(level);
+ * pixma_init();
+ * nscanners = pixma_find_scanners();
+ * devnr = choose_scanner(nscanners);
+ * scanner = pixma_open(devnr);
+ * setup_param(param);
+ * pixma_check_scan_param(scanner, param);
+ * do {
+ * if (I_need_events &&
+ * (ev = pixma_wait_event(scanner, timeout)) > 0) {
+ * handle_event(ev);
+ * }
+ * pixma_scan(scanner, param);
+ * while ((count = pixma_read_image(scanner, buf, len)) > 0) {
+ * write(buf, count);
+ * if (error_occured_in_write) {
+ * pixma_cancel(scanner);
+ * }
+ * }
+ * } while (!enough);
+ * pixma_close(scanner);
+ * pixma_cleanup();
+ * \endcode
+ *
+ * <b>Note:</b> pixma_cancel() can be called asynchronously to
+ * interrupt pixma_read_image(). It does not cancel the operation
+ * immediately. pixma_read_image() <em>must</em> be called until it
+ * returns zero or an error (probably \c PIXMA_ECANCELED).
+ *
+ * \section reference Reference
+ * - \subpage API
+ * - \subpage IO
+ * - \subpage subdriver
+ * - \subpage debug
+ */
+
+/*!
+ * \defgroup API The driver API
+ * \brief The driver API.
+ *
+ * The return value of functions that returns \c int has the following
+ * meaning if not otherwise specified:
+ * - >= 0 if succeeded
+ * - < 0 if failed
+ */
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h> /* available in ISO C99 */
+#else
+# include <sys/types.h>
+typedef uint8_t uint8_t;
+typedef uint16_t uint16_t;
+typedef uint32_t uint32_t;
+#endif /* HAVE_STDINT_H */
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h> /* available in ISO C99 */
+#endif /* HAVE_INTTYPES_H */
+
+/** \addtogroup API
+ * @{ */
+/** \name Version of the driver */
+/**@{*/
+#define PIXMA_VERSION_MAJOR 0
+#define PIXMA_VERSION_MINOR 17
+#define PIXMA_VERSION_BUILD 4
+/**@}*/
+
+/** \name Error codes */
+/**@{*/
+#define PIXMA_EIO -1
+#define PIXMA_ENODEV -2
+#define PIXMA_EACCES -3
+#define PIXMA_ENOMEM -4
+#define PIXMA_EINVAL -5
+#define PIXMA_EBUSY -6
+#define PIXMA_ECANCELED -7
+#define PIXMA_ENOTSUP -8
+#define PIXMA_ETIMEDOUT -9
+#define PIXMA_EPROTO -10
+#define PIXMA_EPAPER_JAMMED -11
+#define PIXMA_ECOVER_OPEN -12
+#define PIXMA_ENO_PAPER -13
+#define PIXMA_EOF -14
+/**@}*/
+
+/** \name Capabilities for using with pixma_config_t::cap */
+/**@{*/
+#define PIXMA_CAP_EASY_RGB (1 << 0)
+#define PIXMA_CAP_GRAY (1 << 1)
+#define PIXMA_CAP_ADF (1 << 2)
+#define PIXMA_CAP_48BIT (1 << 3)
+#define PIXMA_CAP_GAMMA_TABLE (1 << 4)
+#define PIXMA_CAP_EVENTS (1 << 5)
+#define PIXMA_CAP_TPU (1 << 6)
+#define PIXMA_CAP_ADFDUP ((1 << 7) | PIXMA_CAP_ADF)
+#define PIXMA_CAP_CIS (0)
+#define PIXMA_CAP_CCD (1 << 8)
+#define PIXMA_CAP_LINEART (1 << 9)
+#define PIXMA_CAP_NEGATIVE (1 << 10)
+#define PIXMA_CAP_TPUIR ((1 << 11) | PIXMA_CAP_TPU)
+#define PIXMA_CAP_EXPERIMENT (1 << 31)
+/**@}*/
+
+/** \name Button events and related information returned by pixma_wait_event() */
+/**@{*/
+#define PIXMA_EV_NONE 0
+#define PIXMA_EV_ACTION_MASK (0xffffff)
+#define PIXMA_EV_BUTTON1 (1 << 24)
+#define PIXMA_EV_BUTTON2 (2 << 24)
+#define PIXMA_EV_TARGET_MASK (0xff)
+#define PIXMA_EV_ORIGINAL_MASK (0xff00)
+#define PIXMA_EV_DPI_MASK (0xff0000)
+
+#define GET_EV_TARGET(x) (x & PIXMA_EV_TARGET_MASK)
+#define GET_EV_ORIGINAL(x) ( (x & PIXMA_EV_ORIGINAL_MASK) >> 8 )
+#define GET_EV_DPI(x) ( (x & PIXMA_EV_DPI_MASK) >> 16 )
+
+/**@}*/
+/** @} end of API group */
+
+#define PIXMA_CONFIG_FILE "pixma.conf"
+#define MAX_CONF_DEVICES 15
+
+struct pixma_t;
+struct pixma_scan_ops_t;
+struct pixma_scan_param_t;
+struct pixma_config_t;
+struct pixma_cmdbuf_t;
+struct pixma_imagebuf_t;
+struct pixma_device_status_t;
+
+typedef struct pixma_t pixma_t;
+typedef struct pixma_scan_ops_t pixma_scan_ops_t;
+typedef struct pixma_scan_param_t pixma_scan_param_t;
+typedef struct pixma_config_t pixma_config_t;
+typedef struct pixma_cmdbuf_t pixma_cmdbuf_t;
+typedef struct pixma_imagebuf_t pixma_imagebuf_t;
+typedef struct pixma_device_status_t pixma_device_status_t;
+
+
+/** \addtogroup API
+ * @{ */
+/** String index constants */
+typedef enum pixma_string_index_t
+{
+ PIXMA_STRING_MODEL,
+ PIXMA_STRING_ID,
+ PIXMA_STRING_LAST
+} pixma_string_index_t;
+
+/** Paper sources */
+typedef enum pixma_paper_source_t
+{
+ PIXMA_SOURCE_FLATBED,
+ PIXMA_SOURCE_ADF,
+ PIXMA_SOURCE_TPU,
+ PIXMA_SOURCE_ADFDUP /* duplex */
+} pixma_paper_source_t;
+
+/** Scan modes */
+typedef enum pixma_scan_mode_t
+{
+ /* standard scan modes */
+ PIXMA_SCAN_MODE_COLOR,
+ PIXMA_SCAN_MODE_GRAY,
+ /* TPU scan modes for negatives */
+ PIXMA_SCAN_MODE_NEGATIVE_COLOR,
+ PIXMA_SCAN_MODE_NEGATIVE_GRAY,
+ /* extended scan modes for 48 bit flatbed scanners */
+ PIXMA_SCAN_MODE_COLOR_48,
+ PIXMA_SCAN_MODE_GRAY_16,
+ /* 1 bit lineart scan mode */
+ PIXMA_SCAN_MODE_LINEART,
+ /* TPUIR scan mode */
+ PIXMA_SCAN_MODE_TPUIR
+} pixma_scan_mode_t;
+
+typedef enum pixma_hardware_status_t
+{
+ PIXMA_HARDWARE_OK,
+ PIXMA_HARDWARE_ERROR
+} pixma_hardware_status_t;
+
+typedef enum pixma_lamp_status_t
+{
+ PIXMA_LAMP_OK,
+ PIXMA_LAMP_WARMING_UP,
+ PIXMA_LAMP_OFF,
+ PIXMA_LAMP_ERROR
+} pixma_lamp_status_t;
+
+typedef enum pixma_adf_status_t
+{
+ PIXMA_ADF_OK,
+ PIXMA_ADF_NO_PAPER,
+ PIXMA_ADF_JAMMED,
+ PIXMA_ADF_COVER_OPEN,
+ PIXMA_ADF_ERROR
+} pixma_adf_status_t;
+
+typedef enum pixma_calibration_status_t
+{
+ PIXMA_CALIBRATION_OK,
+ PIXMA_CALIBRATION_IN_PROGRESS,
+ PIXMA_CALIBRATION_OFF,
+ PIXMA_CALIBRATION_ERROR
+} pixma_calibration_status_t;
+
+/** Device status. */
+struct pixma_device_status_t
+{
+ pixma_hardware_status_t hardware;
+ pixma_lamp_status_t lamp;
+ pixma_adf_status_t adf;
+ pixma_calibration_status_t cal;
+};
+
+/** Scan parameters. */
+struct pixma_scan_param_t
+{
+ /** Size in bytes of one image line (row).
+ * line_size >= depth / 8 * channels * w <br>
+ * This field will be set by pixma_check_scan_param(). */
+ uint64_t line_size;
+
+ /** Size in bytes of the whole image.
+ * image_size = line_size * h <br>
+ * This field will be set by pixma_check_scan_param(). */
+ uint64_t image_size;
+
+ /** Channels per pixel. 1 = grayscale and lineart, 3 = color */
+ unsigned channels;
+
+ /** Bits per channels.
+ * 1 = 1 bit B/W lineart (flatbed)
+ * 8 = 8 bit grayscale,
+ * 24 bit color (both flatbed)
+ * 16 = 16 bit grayscale (TPU, flatbed not implemeted),
+ * 48 bit color (TPU, flatbed not implemented) */
+ unsigned depth;
+
+ /*@{ */
+ /** Resolution. Valid values are 75,150,300,600,1200... */
+ unsigned xdpi, ydpi;
+ /*@} */
+
+ /*! \name Scan area in pixels
+ * (0,0) = top left; positive x extends to the right; positive y to the
+ * bottom; in pixels.
+ * xs is the offset in x direction of the selected scan range relative
+ * to the range read from the scanner and wx the width in x direction
+ * of the scan line read from scanner. */
+ /*@{ */
+ unsigned x, y, w, h, xs, wx;
+ /*@} */
+
+ /** Flag indicating whether the offset correction for TPU scans
+ * was already performed (to avoid repeated corrections).
+ * Currently only used in pixma_mp810.c sub-driver */
+ unsigned tpu_offset_added;
+
+ /** Flag indicating whether a software-lineart scan is in progress
+ * 0 = other scan
+ * 1 = software-lineart scan */
+ unsigned software_lineart;
+
+ /** Threshold for software-lineart scans */
+ unsigned threshold;
+
+ /** lineart threshold curve for dynamic rasterization */
+ unsigned threshold_curve;
+
+ /* look up table used in dynamic rasterization */
+ unsigned char lineart_lut[256];
+
+ /** Gamma table. 4096 entries, 12 bit => 8 bit. If \c NULL, default gamma
+ * specified by subdriver will be used. */
+ const uint8_t *gamma_table;
+
+ /** \see #pixma_paper_source_t */
+ pixma_paper_source_t source;
+
+ /** \see #pixma_scan_mode_t */
+ pixma_scan_mode_t mode;
+
+ /** The current page # in the same ADF scan session, 0 in non ADF */
+ unsigned adf_pageid;
+};
+
+/** PIXMA model information */
+struct pixma_config_t
+{
+ /* If you change this structure, don't forget to update the device list in
+ * subdrivers. */
+ const char *name; /**< Model name. */
+ const char *model; /**< Short model */
+ uint16_t vid; /**< USB Vendor ID */
+ uint16_t pid; /**< USB Product ID */
+ unsigned iface; /**< USB Interface number */
+ const pixma_scan_ops_t *ops; /**< Subdriver ops */
+ unsigned xdpi; /**< Maximum horizontal resolution[DPI] */
+ unsigned ydpi; /**< Maximum vertical resolution[DPI] */
+ unsigned adftpu_min_dpi; /**< Maximum horizontal resolution[DPI] for adf/tpu
+ * only needed if ADF/TPU has another min. dpi value than 75 dpi */
+ unsigned adftpu_max_dpi; /**< Maximum vertical resolution[DPI] for adf/tpu
+ * only needed if ADF/TPU has another max. dpi value than xdpi */
+ unsigned tpuir_min_dpi; /**< Minimum resolution[DPI] for tpu-ir
+ * only needed if TPU-IR has another min. dpi value than 75 dpi */
+ unsigned tpuir_max_dpi; /**< Maximum resolution[DPI] for tpu-ir
+ * only needed if TPU-IR has another max. dpi value than xdpi */
+ unsigned width; /**< Maximum width of scannable area in pixels at 75DPI */
+ unsigned height; /**< Maximum height of scannable area in pixels at 75DPI */
+ unsigned cap; /**< Capability bitfield \see PIXMA_CAP_* */
+};
+
+
+/* Defined in pixma_common.c */
+
+/** Initialize the driver. It must be called before any other functions
+ * except pixma_set_debug_level(). */
+int pixma_init (void);
+
+/** Free resources allocated by the driver. */
+void pixma_cleanup (void);
+
+/** Set the debug level.
+ * \param[in] level the debug level
+ * - 0 No debug output at all
+ * - 1 Only errors and warning
+ * - 2 General information
+ * - 3 Debugging messages
+ * - 10 USB traffic dump */
+void pixma_set_debug_level (int level);
+
+/** Find scanners. The device number used in pixma_open(),
+ * pixma_get_device_model(), pixma_get_device_id() and
+ * pixma_get_device_config() must be less than the value returned by the last
+ * call of this function.
+ *
+ * \return The number of scanners found currently. The return value is
+ * guaranteed to be valid until the next call to pixma_find_scanners(). */
+int pixma_find_scanners (const char **conf_devices);
+
+/** Return the model name of the device \a devnr. */
+const char *pixma_get_device_model (unsigned devnr);
+
+/** Return the unique ID of the device \a devnr. */
+const char *pixma_get_device_id (unsigned devnr);
+
+/** Return the device configuration of the device \a devnr. */
+const struct pixma_config_t *pixma_get_device_config (unsigned devnr);
+
+/** Open a connection to the scanner \a devnr.
+ * \param[in] devnr The scanner number
+ * \param[out] handle The device handle
+ * \see pixma_find_scanners() */
+int pixma_open (unsigned devnr, pixma_t ** handle);
+
+/** Close the connection to the scanner. The scanning process is aborted
+ * if necessary before the function returns. */
+void pixma_close (pixma_t * s);
+
+/** Initiate an image acquisition process. You must keep \a sp valid until the
+ * image acquisition process has finished. */
+int pixma_scan (pixma_t *, pixma_scan_param_t * sp);
+
+/** Read a block of image data. It blocks until there is at least one byte
+ * available or an error occurs.
+ *
+ * \param[out] buf Pointer to the buffer
+ * \param[in] len Size of the buffer
+ *
+ * \retval count Number of bytes written to the buffer or error. Possible
+ * return value:
+ * - count = 0 for end of image
+ * - count = \a len
+ * - 0 < count < \a len if and only if it is the last block.
+ * - count < 0 for error */
+int pixma_read_image (pixma_t *, void *buf, unsigned len);
+
+#if 0
+/** Read a block of image data and write to \a fd.
+ * \param[in] fd output file descriptor
+ * \see pixma_read_image() */
+int pixma_read_image_write (pixma_t *, int fd);
+#endif
+
+/** Cancel the scanning process. No effect if no scanning process is in
+ * progress. It can be called asynchronously e.g. within a signal
+ * handle. pixma_cancel() doesn't abort the operation immediately. It
+ * guarantees that the current call or, at the latest, the next call to
+ * pixma_read_image() will return zero or an error (probably PIXMA_ECANCELED). */
+void pixma_cancel (pixma_t *);
+
+/** Check the scan parameters. This function can change your parameters to
+ * match the device capability, e.g. adjust width and height to the available
+ * area.
+ * \return PIXMA_EINVAL for invalid parameters. */
+int pixma_check_scan_param (pixma_t *, pixma_scan_param_t *);
+
+/** Wait until a scanner button is pressed or it times out. It should not be
+ * called during image acquisition is in progress.
+ * \param[in] timeout in milliseconds, less than 0 means forever
+ * \return
+ * - \c PIXMA_EV_NONE if it timed out.
+ * - non-zero value indicates which button was pressed.
+ * \see PIXMA_EV_*
+ */
+uint32_t pixma_wait_event (pixma_t *, int timeout);
+
+/** Activate connection to scanner */
+int pixma_activate_connection (pixma_t *);
+
+/** De-activate connection to scanner */
+
+int pixma_deactivate_connection (pixma_t *);
+
+
+/** Enable or disable background tasks. Currently, the only one task
+ * is submitting interrupt URB in background.
+ * \param[in] enabled if not zero, enable background task.
+ * \see pixma_set_interrupt_mode() */
+int pixma_enable_background (pixma_t *, int enabled);
+
+/** Read the current device status.
+ * \param[out] status the current device status
+ * \return 0 if succeeded. Otherwise, failed.
+ */
+int pixma_get_device_status (pixma_t *, pixma_device_status_t * status);
+
+const char *pixma_get_string (pixma_t *, pixma_string_index_t);
+const pixma_config_t *pixma_get_config (pixma_t *);
+void pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n);
+const char *pixma_strerror (int error);
+
+/** @} end of API group */
+
+#endif
diff --git a/backend/pixma_bjnp.c b/backend/pixma_bjnp.c
new file mode 100644
index 0000000..3046e9d
--- /dev/null
+++ b/backend/pixma_bjnp.c
@@ -0,0 +1,2356 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2008 2012 by Louis Lagendijk
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+#undef BACKEND_NAME
+#define BACKEND_NAME bjnp
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+
+/*
+ * Standard types etc
+ */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <unistd.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+/*
+ * networking stuff
+ */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+#ifdef HAVE_SYS_SELSECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "pixma_bjnp_private.h"
+#include "pixma_bjnp.h"
+/* #include "pixma_rename.h" */
+#include "pixma.h"
+#include "pixma_common.h"
+
+
+/* static data */
+static bjnp_device_t device[BJNP_NO_DEVICES];
+static int bjnp_no_devices = 0;
+
+/*
+ * Private functions
+ */
+
+static void
+u8tohex (char *string, const uint8_t *value, int len )
+{
+ int i;
+ int x;
+ const char hdigit[16] =
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+ 'e', 'f'
+ };
+ for (i = 0; i < len; i++)
+ {
+ x = value[i];
+ string[ 2 * i ] = hdigit[(x >> 4) & 0xf];
+ string[ 2 * i + 1] = hdigit[x & 0xf];
+ }
+ string[2 * len ] = '\0';
+}
+
+static void
+u32tohex (uint32_t x, char *str)
+{
+ uint8_t uint8[4];
+ uint8[0]= (uint8_t) x >> 24;
+ uint8[1] = (uint8_t)x >> 16;
+ uint8[2] = (uint8_t)x >> 8;
+ uint8[3] = (uint8_t)x ;
+ u8tohex(str, uint8, 4);
+}
+
+static void
+bjnp_hexdump (int level, const void *d_, unsigned len)
+{
+ const uint8_t *d = (const uint8_t *) (d_);
+ unsigned ofs, c, plen;
+ char line[100]; /* actually only 1+8+1+8*3+1+8*3+1 = 61 bytes needed */
+
+ if (level > DBG_LEVEL)
+ return;
+ if (level == DBG_LEVEL)
+ /* if debuglevel == exact match and buffer contains more than 3 lines, print 2 lines + .... */
+ plen = (len > 64) ? 32: len;
+ else
+ plen = len;
+ ofs = 0;
+ while (ofs < plen)
+ {
+ char *p;
+ line[0] = ' ';
+ u32tohex (ofs, line + 1);
+ line[9] = ':';
+ p = line + 10;
+ for (c = 0; c != 16 && (ofs + c) < plen; c++)
+ {
+ u8tohex (p, d + ofs + c, 1);
+ p[2] = ' ';
+ p += 3;
+ if (c == 7)
+ {
+ p[0] = ' ';
+ p++;
+ }
+ }
+ p[0] = '\0';
+ bjnp_dbg (level, "%s\n", line);
+ ofs += c;
+ }
+ if (len > plen)
+ bjnp_dbg(level, "......\n");
+}
+
+static int sa_is_equal( const bjnp_sockaddr_t * sa1, const bjnp_sockaddr_t * sa2)
+{
+ if ((sa1 == NULL) || (sa2 == NULL) )
+ return 0;
+
+ if (sa1->addr.sa_family == sa2-> addr.sa_family)
+ {
+ if( sa1 -> addr.sa_family == AF_INET)
+ {
+ if ( (sa1->ipv4.sin_port == sa2->ipv4.sin_port) &&
+ (sa1->ipv4.sin_addr.s_addr == sa2->ipv4.sin_addr.s_addr))
+ {
+ return 1;
+ }
+ }
+#ifdef ENABLE_IPV6
+ else if (sa1 -> addr.sa_family == AF_INET6 )
+ {
+ if ( (sa1-> ipv6.sin6_port == sa2->ipv6.sin6_port) &&
+ (memcmp(&(sa1->ipv6.sin6_addr), &(sa2->ipv6.sin6_addr), sizeof(struct in6_addr)) == 0))
+ {
+ return 1;
+ }
+ }
+#endif
+ }
+ return 0;
+}
+
+static int
+sa_size( const bjnp_sockaddr_t *sa)
+{
+ switch (sa -> addr.sa_family)
+ {
+ case AF_INET:
+ return (sizeof(struct sockaddr_in) );
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return (sizeof(struct sockaddr_in6) );
+#endif
+ default:
+ /* should not occur */
+ return sizeof( bjnp_sockaddr_t );
+ }
+}
+
+static int
+get_protocol_family( const bjnp_sockaddr_t *sa)
+{
+ switch (sa -> addr.sa_family)
+ {
+ case AF_INET:
+ return PF_INET;
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return PF_INET6;
+ break;
+#endif
+ default:
+ /* should not occur */
+ return -1;
+ }
+}
+
+static void
+get_address_info ( const bjnp_sockaddr_t *addr, char * addr_string, int *port)
+{
+ char tmp_addr[BJNP_HOST_MAX];
+ if ( addr->addr.sa_family == AF_INET)
+ {
+ inet_ntop( AF_INET, &(addr -> ipv4.sin_addr.s_addr), addr_string, BJNP_HOST_MAX);
+ *port = ntohs (addr->ipv4.sin_port);
+ }
+#ifdef ENABLE_IPV6
+ else if (addr->addr.sa_family == AF_INET6)
+ {
+ inet_ntop( AF_INET6, addr -> ipv6.sin6_addr.s6_addr, tmp_addr, sizeof(tmp_addr) );
+
+ if (IN6_IS_ADDR_LINKLOCAL( &(addr -> ipv6.sin6_addr) ) )
+ sprintf(addr_string, "[%s%%%d]", tmp_addr, addr -> ipv6.sin6_scope_id);
+
+ *port = ntohs (addr->ipv6.sin6_port);
+ }
+#endif
+ else
+ {
+ /* unknown address family, should not occur */
+ strcpy(addr_string, "Unknown address family");
+ *port = 0;
+ }
+}
+
+static int
+parse_IEEE1284_to_model (char *scanner_id, char *model)
+{
+/*
+ * parses the IEEE1284 ID of the scanner to retrieve make and model
+ * of the scanner
+ * Returns: 0 = not found
+ * 1 = found, model is set
+ */
+
+ char s[BJNP_IEEE1284_MAX];
+ char *tok;
+
+ strcpy (s, scanner_id);
+ model[0] = '\0';
+
+ tok = strtok (s, ";");
+ while (tok != NULL)
+ {
+ /* MDL contains make and model */
+
+ if (strncmp (tok, "MDL:", 4) == 0)
+ {
+ strcpy (model, tok + 4);
+ return 1;
+ }
+ tok = strtok (NULL, ";");
+ }
+ return 0;
+}
+
+static int
+charTo2byte (char *d, const char *s, int len)
+{
+ /*
+ * copy ASCII string to UTF-16 unicode string
+ * len is length of destination buffer
+ * Returns: number of characters copied
+ */
+
+ int done = 0;
+ int copied = 0;
+ int i;
+
+ len = len / 2;
+ for (i = 0; i < len; i++)
+ {
+ d[2 * i] = '\0';
+ if (s[i] == '\0')
+ {
+ done = 1;
+ }
+ if (done == 0)
+ {
+ d[2 * i + 1] = s[i];
+ copied++;
+ }
+ else
+ d[2 * i + 1] = '\0';
+ }
+ return copied;
+}
+
+static char *
+getusername (void)
+{
+ static char noname[] = "sane_pixma";
+ struct passwd *pwdent;
+
+#ifdef HAVE_PWD_H
+ if (((pwdent = getpwuid (geteuid ())) != NULL) && (pwdent->pw_name != NULL))
+ return pwdent->pw_name;
+#endif
+ return noname;
+}
+
+
+static char *
+determine_scanner_serial (const char *hostname, const char * mac_address, char *serial)
+{
+ char *dot;
+ char copy[BJNP_HOST_MAX];
+
+ /* determine a "serial number" for the scanner */
+ /* if available we use the hostname or ipv4 address of the printer */
+ /* if we only have a literal ipv6 address, we use the mac-address */
+
+ strcpy(copy, hostname);
+ while (strlen (copy) >= SHORT_HOSTNAME_MAX)
+ {
+ /* if this is a FQDN, not an ip-address, remove domain part of the name */
+ if ((dot = strchr (copy, '.')) != NULL)
+ {
+ *dot = '\0';
+ }
+ else
+ strcpy(copy, mac_address);
+ break;
+ }
+ strcpy( serial, copy );
+ return serial;
+}
+
+static int
+bjnp_open_tcp (int devno)
+{
+ int sock;
+ int val;
+ bjnp_sockaddr_t *addr = device[devno].addr;
+ char host[BJNP_HOST_MAX];
+ int port;
+
+ get_address_info( addr, host, &port);
+ PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_open_tcp: Setting up a TCP socket, dest: %s port %d\n",
+ host, port ) );
+
+ if ((sock = socket (get_protocol_family( addr ) , SOCK_STREAM, 0)) < 0)
+ {
+ PDBG (bjnp_dbg (LOG_CRIT, "bjnp_open_tcp: Can not create socket: %s\n",
+ strerror (errno)));
+ return -1;
+ }
+
+ val = 1;
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof (val));
+
+#if 0
+ val = 1;
+ setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof (val));
+
+ val = 1;
+#endif
+
+ /*
+ * Using TCP_NODELAY improves responsiveness, especially on systems
+ * with a slow loopback interface...
+ */
+
+ val = 1;
+ setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof (val));
+
+/*
+ * Close this socket when starting another process...
+ */
+
+ fcntl (sock, F_SETFD, FD_CLOEXEC);
+
+ if (connect
+ (sock, &(addr->addr), sa_size(device[devno].addr) )!= 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "bjnp_open_tcp: Can not connect to scanner: %s\n",
+ strerror (errno)));
+ return -1;
+ }
+ device[devno].tcp_socket = sock;
+ return 0;
+}
+
+static int
+split_uri (const char *devname, char *method, char *host, char *port,
+ char *args)
+{
+ char copy[1024];
+ char *start;
+ char next;
+ int i;
+
+ strcpy (copy, devname);
+ start = copy;
+
+/*
+ * retrieve method
+ */
+ i = 0;
+ while ((start[i] != '\0') && (start[i] != ':'))
+ {
+ i++;
+ }
+
+ if (((strncmp (start + i, "://", 3) != 0)) || (i > BJNP_METHOD_MAX -1 ))
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Can not find method in %s (offset %d)\n",
+ devname, i));
+ return -1;
+ }
+
+ start[i] = '\0';
+ strcpy (method, start);
+ start = start + i + 3;
+
+/*
+ * retrieve host
+ */
+
+ if (start[0] == '[')
+ {
+ /* literal IPv6 address */
+
+ char *end_of_address = strchr(start, ']');
+
+ if ( ( end_of_address == NULL) ||
+ ( (end_of_address[1] != ':') && (end_of_address[1] != '/' ) && (end_of_address[1] != '\0' )) ||
+ ( (end_of_address - start) >= BJNP_HOST_MAX ) )
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Can not find hostname or address in %s\n", devname));
+ return -1;
+ }
+ next = end_of_address[1];
+ *end_of_address = '\0';
+ strcpy(host, start + 1);
+ start = end_of_address + 2;
+ }
+ else
+ {
+ i = 0;
+ while ((start[i] != '\0') && (start[i] != '/') && (start[i] != ':'))
+ {
+ i++;
+ }
+ next = start[i];
+ start[i] = '\0';
+ if ((i == 0) || (i >= BJNP_HOST_MAX ) )
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Can not find hostname or address in %s\n", devname));
+ return -1;
+ }
+ strcpy (host, start);
+ start = start + i +1;
+ }
+
+
+/*
+ * retrieve port number
+ */
+
+ if (next != ':')
+ strcpy(port, "");
+ else
+ {
+ char *end_of_port = strchr(start, '/');
+ if (end_of_port == NULL)
+ {
+ next = '\0';
+ }
+ else
+ {
+ next = *end_of_port;
+ *end_of_port = '\0';
+ }
+ if ((strlen(start) == 0) || (strlen(start) >= BJNP_PORT_MAX ) )
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Can not find port in %s (have \"%s\")\n", devname, start));
+ return -1;
+ }
+ strcpy(port, start);
+ }
+
+/*
+ * Retrieve arguments
+ */
+
+ if (next == '/')
+ {
+ i = strlen(start);
+ if ( i >= BJNP_ARGS_MAX)
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Argument string too long in %s\n", devname));
+ }
+ strcpy (args, start);
+ }
+ else
+ strcpy (args, "");
+ return 0;
+}
+
+
+
+static void
+set_cmd (int devno, struct BJNP_command *cmd, char cmd_code, int payload_len)
+{
+ /*
+ * Set command buffer with command code, session_id and lenght of payload
+ * Returns: sequence number of command
+ */
+ strncpy (cmd->BJNP_id, BJNP_STRING, sizeof (cmd->BJNP_id));
+ cmd->dev_type = BJNP_CMD_SCAN;
+ cmd->cmd_code = cmd_code;
+ cmd->unknown1 = htons (0);
+ if (devno == -1)
+ {
+ /* device not yet opened, use 0 for serial and session) */
+ cmd->seq_no = htons (0);
+ cmd->session_id = htons (0);
+ }
+ else
+ {
+ cmd->seq_no = htons (++(device[devno].serial));
+ cmd->session_id = (cmd_code == CMD_UDP_POLL ) ? 0 : htons (device[devno].session_id);
+ device[devno].last_cmd = cmd_code;
+ }
+ cmd->payload_len = htonl (payload_len);
+}
+
+static int
+bjnp_setup_udp_socket ( const int dev_no )
+{
+ /*
+ * Setup a udp socket for the given device
+ * Returns the socket or -1 in case of error
+ */
+
+ int sockfd;
+ char addr_string[256];
+ int port;
+ bjnp_sockaddr_t * addr = device[dev_no].addr;
+
+ get_address_info( addr, addr_string, &port);
+
+ PDBG (bjnp_dbg (LOG_DEBUG, "setup_udp_socket: Setting up a UDP socket, dest: %s port %d\n",
+ addr_string, port ) );
+
+ if ((sockfd = socket (get_protocol_family( addr ), SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "setup_udp_socket: can not open socket - %s\n",
+ strerror (errno)));
+ return -1;
+ }
+
+ if (connect
+ (sockfd, &(device[dev_no].addr->addr), sa_size(device[dev_no].addr) )!= 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "setup_udp_socket: connect failed- %s\n",
+ strerror (errno)));
+ close(sockfd);
+ return -1;
+ }
+ return sockfd;
+}
+
+static int
+udp_command (const int dev_no, char *command, int cmd_len, char *response,
+ int resp_len)
+{
+ /*
+ * send udp command to given device and recieve the response`
+ * returns: the legth of the response or -1
+ */
+ int sockfd;
+ struct timeval timeout;
+ int result;
+ int try, attempt;
+ int numbytes;
+ fd_set fdset;
+ struct BJNP_command *resp = (struct BJNP_command *) response;
+ struct BJNP_command *cmd = (struct BJNP_command *) command;
+
+ if ( (sockfd = bjnp_setup_udp_socket(dev_no) ) == -1 )
+ {
+ PDBG (bjnp_dbg( LOG_CRIT, "udp_command: Can not setup socket\n") );
+ return -1;
+ }
+
+ for (try = 0; try < BJNP_UDP_RETRY_MAX; try++)
+ {
+ if ((numbytes = send (sockfd, command, cmd_len, 0)) != cmd_len)
+ {
+ PDBG (bjnp_dbg
+ (LOG_NOTICE, "udp_command: Sent %d bytes, expected %d\n",
+ numbytes, cmd_len));
+ continue;
+ }
+
+ attempt = 0;
+
+ /* wait for data to be received, ignore signals being received */
+ /* skip late udp responses (they have an incorrect sequence number */
+ do
+ {
+ FD_ZERO (&fdset);
+ FD_SET (sockfd, &fdset);
+
+ timeout.tv_sec = BJNP_TIMEOUT_UDP;
+ timeout.tv_usec = 0;
+ }
+ while (((result =
+ select (sockfd + 1, &fdset, NULL, NULL, &timeout)) <= 0)
+ && (errno == EINTR) && (attempt++ < BJNP_MAX_SELECT_ATTEMPTS)
+ && resp-> seq_no != cmd->seq_no);
+
+ if (result <= 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_NOTICE, "udp_command: select failed: %s\n",
+ result == 0 ? "timed out" : strerror (errno)));
+ continue;
+ }
+
+ if ((numbytes = recv (sockfd, response, resp_len, 0)) == -1)
+ {
+ PDBG (bjnp_dbg
+ (LOG_NOTICE, "udp_command: recv failed: %s",
+ strerror (errno)));
+ continue;
+ }
+ close(sockfd);
+ return numbytes;
+ }
+
+ /* no response even after retry */
+
+ close(sockfd);
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "udp_command: no data received\n" ) );
+ return -1;
+}
+
+static int
+get_scanner_id (const int dev_no, char *model)
+{
+ /*
+ * get scanner identity
+ * Sets model (make and model)
+ * Return 0 on success, -1 in case of errors
+ */
+
+ struct BJNP_command cmd;
+ struct IDENTITY *id;
+ char scanner_id[BJNP_IEEE1284_MAX];
+ int resp_len;
+ char resp_buf[BJNP_RESP_MAX];
+ int id_len;
+
+ /* set defaults */
+
+ strcpy (model, "Unidentified scanner");
+
+ set_cmd (dev_no, &cmd, CMD_UDP_GET_ID, 0);
+
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Get scanner identity\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, (char *) &cmd,
+ sizeof (struct BJNP_command)));
+
+ if ( ( resp_len = udp_command (dev_no, (char *) &cmd, sizeof (struct BJNP_command),
+ resp_buf, BJNP_RESP_MAX) ) < (int)sizeof(struct BJNP_command) )
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG, "Failed to retrieve scanner identity:\n"));
+ return -1;
+ }
+ PDBG (bjnp_dbg (LOG_DEBUG2, "scanner identity:\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, resp_buf, resp_len));
+
+ id = (struct IDENTITY *) resp_buf;
+
+ /* truncate string to be safe */
+ id_len = htons( id-> id_len ) - sizeof(id->id_len);
+ id->id[id_len] = '\0';
+ strcpy (scanner_id, id->id);
+
+ PDBG (bjnp_dbg (LOG_INFO, "Scanner identity string = %s - lenght = %d\n", scanner_id, id_len));
+
+ /* get make&model from IEEE1284 id */
+
+ if (model != NULL)
+ {
+ parse_IEEE1284_to_model (scanner_id, model);
+ PDBG (bjnp_dbg (LOG_INFO, "Scanner model = %s\n", model));
+ }
+ return 0;
+}
+
+static int
+get_scanner_name(const bjnp_sockaddr_t *scanner_sa, char *host)
+{
+ /*
+ * Parse identify command responses to ip-address
+ * and hostname
+ */
+
+ struct addrinfo *results;
+ struct addrinfo *result;
+ char ip_address[BJNP_HOST_MAX];
+ int port;
+ int error;
+ int match = 0;
+ int level;
+ char service[64];
+
+#ifdef ENABLE_IPV6
+ if ( ( scanner_sa -> addr.sa_family == AF_INET6 ) &&
+ ( IN6_IS_ADDR_LINKLOCAL( &(scanner_sa -> ipv6.sin6_addr ) ) ) )
+ level = BJNP_ADDRESS_IS_LINK_LOCAL;
+ else
+#endif
+ level = BJNP_ADDRESS_IS_GLOBAL;
+
+ get_address_info( scanner_sa, ip_address, &port );
+
+ /* do reverse name lookup, if hostname can not be found return ip-address */
+
+ if( (error = getnameinfo( &(scanner_sa -> addr) , sa_size( scanner_sa),
+ host, BJNP_HOST_MAX , NULL, 0, NI_NAMEREQD) ) != 0 )
+ {
+ PDBG (bjnp_dbg(LOG_INFO, "Name for %s not found : %s\n",
+ ip_address, gai_strerror(error) ) );
+ strcpy(host, ip_address);
+ return level;
+ }
+ else
+ {
+ sprintf(service, "%d", port);
+ /* some buggy routers return rubbish if reverse lookup fails, so
+ * we do a forward lookup on the received name to see if the result matches */
+
+ if (getaddrinfo(host , service, NULL, &results) == 0)
+ {
+ result = results;
+
+ while (result != NULL)
+ {
+ if(sa_is_equal( scanner_sa, (bjnp_sockaddr_t *)result-> ai_addr))
+ {
+ /* found match, good */
+ PDBG (bjnp_dbg (LOG_INFO,
+ "Forward lookup for %s succeeded, using as hostname\n", host));
+ match = 1;
+ level = BJNP_ADDRESS_HAS_FQDN;
+ break;
+ }
+ result = result-> ai_next;
+ }
+ freeaddrinfo(results);
+
+ if (match != 1)
+ {
+ PDBG (bjnp_dbg (LOG_INFO,
+ "Forward lookup for %s succeeded, IP-address does not match, using IP-address %s instead\n",
+ host, ip_address));
+ strcpy (host, ip_address);
+ }
+ }
+ else
+ {
+ /* forward lookup failed, use ip-address */
+ PDBG ( bjnp_dbg (LOG_INFO, "Forward lookup of %s failed, using IP-address", ip_address));
+ strcpy (host, ip_address);
+ }
+ }
+ return level;
+}
+
+static int create_broadcast_socket( const bjnp_sockaddr_t * local_addr )
+{
+ int sockfd = -1;
+ int broadcast = 1;
+ int ipv6_v6only = 1;
+
+
+ if ((sockfd = socket (local_addr-> addr.sa_family, SOCK_DGRAM, 0)) == -1)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "create_broadcast_socket: can not open socket - %s",
+ strerror (errno)));
+ return -1;
+ }
+
+ /* Set broadcast flag on socket */
+
+ if (setsockopt
+ (sockfd, SOL_SOCKET, SO_BROADCAST, (const char *) &broadcast,
+ sizeof (broadcast)) != 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "create_broadcast_socket: setting socket option SO_BROADCAST failed - %s",
+ strerror (errno)));
+ close (sockfd);
+ return -1;
+ };
+
+ /* For an IPv6 socket, bind to v6 only so a V6 socket can co-exist with a v4 socket */
+ if ( (local_addr -> addr.sa_family == AF_INET6) && ( setsockopt
+ (sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &ipv6_v6only,
+ sizeof (ipv6_v6only)) != 0) )
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "create_broadcast_socket: setting socket option IPV6_V6ONLY failed - %s",
+ strerror (errno)));
+ close (sockfd);
+ return -1;
+ };
+
+ if (bind
+ (sockfd, &(local_addr->addr),
+ (socklen_t) sa_size( local_addr)) != 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "create_broadcast_socket: bind socket to local address failed - %s\n",
+ strerror (errno)));
+ close (sockfd);
+ return -1;
+ }
+ return sockfd;
+}
+
+static int
+prepare_socket(const char *if_name, const bjnp_sockaddr_t *local_sa,
+ const bjnp_sockaddr_t *broadcast_sa, bjnp_sockaddr_t * dest_sa)
+{
+ /*
+ * Prepare a socket for broadcast or multicast
+ * Input:
+ * if_name: the name of the interface
+ * local_sa: local address to use
+ * broadcast_sa: broadcast address to use, if NULL we use all hosts
+ * dest_sa: (write) where to return destination address of broadcast
+ * retuns: open socket or -1
+ */
+
+ int socket = -1;
+ bjnp_sockaddr_t local_sa_copy;
+
+ if ( local_sa == NULL )
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG,
+ "%s is not a valid IPv4 interface, skipping...\n",
+ if_name));
+ return -1;
+ }
+
+ memset( &local_sa_copy, 0, sizeof(local_sa_copy) );
+ memcpy( &local_sa_copy, local_sa, sa_size(local_sa) );
+
+ switch( local_sa_copy.addr.sa_family )
+ {
+ case AF_INET:
+ {
+ local_sa_copy.ipv4.sin_port = htons(BJNP_PORT_SCAN);
+
+ if (local_sa_copy.ipv4.sin_addr.s_addr == htonl (INADDR_LOOPBACK) )
+ {
+ /* not a valid interface */
+
+ PDBG (bjnp_dbg (LOG_DEBUG,
+ "%s is not a valid IPv4 interface, skipping...\n",
+ if_name));
+ return -1;
+ }
+
+
+ /* send broadcasts to the broadcast address of the interface */
+
+ memcpy(dest_sa, broadcast_sa, sa_size(dest_sa) );
+ dest_sa -> ipv4.sin_port = htons(BJNP_PORT_SCAN);
+ if ( (socket = create_broadcast_socket( &local_sa_copy) ) != -1)
+ {
+ PDBG (bjnp_dbg (LOG_INFO, "%s is IPv4 capable, sending broadcast, socket = %d\n",
+ if_name, socket));
+ }
+ else
+ {
+ PDBG (bjnp_dbg (LOG_INFO, "%s is IPv4 capable, but failed to create a socket.\n",
+ if_name));
+ return -1;
+ }
+ }
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ {
+ local_sa_copy.ipv6.sin6_port = htons(BJNP_PORT_SCAN);
+
+ if (IN6_IS_ADDR_LOOPBACK( &(local_sa_copy.ipv6.sin6_addr) ) )
+ {
+ /* not a valid interface */
+
+ PDBG (bjnp_dbg (LOG_DEBUG,
+ "%s is not a valid IPv6 interface, skipping...\n",
+ if_name));
+ return -1;
+ }
+ else
+ {
+ dest_sa -> ipv6.sin6_family = AF_INET6;
+ dest_sa -> ipv6.sin6_port = htons(BJNP_PORT_SCAN);
+ inet_pton(AF_INET6, "ff02::1", dest_sa -> ipv6.sin6_addr.s6_addr);
+ if ( (socket = create_broadcast_socket( &local_sa_copy ) ) != -1)
+ {
+ PDBG (bjnp_dbg (LOG_INFO, "%s is IPv6 capable, sending broadcast, socket = %d\n",
+ if_name, socket));
+ }
+ else
+ {
+ PDBG (bjnp_dbg (LOG_INFO, "%s is IPv6 capable, but failed to create a socket.\n",
+ if_name));
+ return -1;
+ }
+ }
+ }
+ break;
+#endif
+
+ default:
+ socket = -1;
+ }
+ return socket;
+}
+
+static int
+bjnp_send_broadcast (int sockfd, const bjnp_sockaddr_t * broadcast_addr,
+ struct BJNP_command cmd, int size)
+{
+ int num_bytes;
+
+ /* set address to send packet to */
+ /* usebroadcast address of interface */
+
+ if ((num_bytes = sendto (sockfd, &cmd, size, 0,
+ &(broadcast_addr->addr),
+ sa_size( broadcast_addr)) ) != size)
+ {
+ PDBG (bjnp_dbg (LOG_INFO,
+ "bjnp_send_broadcast: Socket: %d: sent only %x = %d bytes of packet, error = %s\n",
+ sockfd, num_bytes, num_bytes, strerror (errno)));
+ /* not allowed, skip this interface */
+
+ return -1;
+ }
+ return sockfd;
+}
+
+static void
+bjnp_finish_job (int devno)
+{
+/*
+ * Signal end of scanjob to scanner
+ */
+
+ char resp_buf[BJNP_RESP_MAX];
+ int resp_len;
+ struct BJNP_command cmd;
+
+ set_cmd (devno, &cmd, CMD_UDP_CLOSE, 0);
+
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Finish scanjob\n"));
+ PDBG (bjnp_hexdump
+ (LOG_DEBUG2, (char *) &cmd, sizeof (struct BJNP_command)));
+ resp_len =
+ udp_command (devno, (char *) &cmd, sizeof (struct BJNP_command), resp_buf,
+ BJNP_RESP_MAX);
+
+ if (resp_len != sizeof (struct BJNP_command))
+ {
+ PDBG (bjnp_dbg
+ (LOG_INFO,
+ "Received %d characters on close scanjob command, expected %d\n",
+ resp_len, (int) sizeof (struct BJNP_command)));
+ return;
+ }
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Finish scanjob response\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, resp_buf, resp_len));
+
+}
+
+#ifdef PIXMA_BJNP_USE_STATUS
+static int
+bjnp_poll_scanner (int devno, char type,char *hostname, char *user, SANE_Byte *status, int size)
+{
+/*
+ * send details of user to the scanner
+ */
+
+ char cmd_buf[BJNP_CMD_MAX];
+ char resp_buf[BJNP_RESP_MAX];
+ int resp_len;
+ int len = 0; /* payload length */
+ int buf_len; /* length of the whole command buffer */
+ struct POLL_DETAILS *poll;
+ struct POLL_RESPONSE *response;
+ char user_host[256];
+ time_t t;
+ int user_host_len;
+
+ poll = (struct POLL_DETAILS *) cmd_buf;
+ memset( poll, 0, sizeof( struct POLL_DETAILS));
+ memset( &resp_buf, 0, sizeof( resp_buf) );
+
+
+ /* create payload */
+ poll->type = htons(type);
+
+ user_host_len = sizeof( poll -> extensions.type2.user_host);
+ snprintf(user_host, (user_host_len /2) ,"%s %s", user, hostname);
+ user_host[ user_host_len /2 + 1] = '\0';
+
+ switch( type) {
+ case 0:
+ len = 80;
+ break;
+ case 1:
+ charTo2byte(poll->extensions.type1.user_host, user_host, user_host_len);
+ len = 80;
+ break;
+ case 2:
+ poll->extensions.type2.dialog = htonl(device[devno].dialog);
+ charTo2byte(poll->extensions.type2.user_host, user_host, user_host_len);
+ poll->extensions.type2.unknown_1 = htonl(0x14);
+ poll->extensions.type2.unknown_2 = htonl(0x10);
+ t = time (NULL);
+ strftime (poll->extensions.type2.ascii_date,
+ sizeof (poll->extensions.type2.ascii_date),
+ "%Y%m%d%H%M%S", localtime (&t));
+ len = 116;
+ break;
+ case 5:
+ poll->extensions.type5.dialog = htonl(device[devno].dialog);
+ charTo2byte(poll->extensions.type5.user_host, user_host, user_host_len);
+ poll->extensions.type5.unknown_1 = htonl(0x14);
+ poll->extensions.type5.key = htonl(device[devno].status_key);
+ len = 100;
+ break;
+ default:
+ PDBG (bjnp_dbg (LOG_INFO, "bjnp_poll_scanner: unknown packet type: %d\n", type));
+ return -1;
+ };
+ /* we can only now set the header as we now know the length of the payload */
+ set_cmd (devno, (struct BJNP_command *) cmd_buf, CMD_UDP_POLL,
+ len);
+
+ buf_len = len + sizeof(struct BJNP_command);
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Poll details (type %d)\n", type));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, cmd_buf,
+ buf_len));
+
+ resp_len = udp_command (devno, cmd_buf, buf_len, resp_buf, BJNP_RESP_MAX);
+
+ if (resp_len > 0)
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Poll details response:\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, resp_buf, resp_len));
+ response = (struct POLL_RESPONSE *) resp_buf;
+
+ device[devno].dialog = ntohl( response -> dialog );
+
+ if ( response -> result[3] == 1 )
+ {
+ return BJNP_RESTART_POLL;
+ }
+ if ( (response -> result[2] & 0x80) != 0)
+ {
+ memcpy( status, response->status, size);
+ PDBG( bjnp_dbg(LOG_INFO, "received button status!\n"));
+ PDBG (bjnp_hexdump( LOG_DEBUG2, status, size ));
+ device[devno].status_key = ntohl( response -> key );
+ return size;
+ }
+ }
+ return 0;
+}
+#endif
+
+static void
+bjnp_send_job_details (int devno, char *hostname, char *user, char *title)
+{
+/*
+ * send details of scanjob to scanner
+ */
+
+ char cmd_buf[BJNP_CMD_MAX];
+ char resp_buf[BJNP_RESP_MAX];
+ int resp_len;
+ struct JOB_DETAILS *job;
+ struct BJNP_command *resp;
+
+ /* send job details command */
+
+ set_cmd (devno, (struct BJNP_command *) cmd_buf, CMD_UDP_JOB_DETAILS,
+ sizeof (*job) - sizeof (struct BJNP_command));
+
+ /* create payload */
+
+ job = (struct JOB_DETAILS *) (cmd_buf);
+ charTo2byte (job->unknown, "", sizeof (job->unknown));
+ charTo2byte (job->hostname, hostname, sizeof (job->hostname));
+ charTo2byte (job->username, user, sizeof (job->username));
+ charTo2byte (job->jobtitle, title, sizeof (job->jobtitle));
+
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Job details\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, cmd_buf,
+ (sizeof (struct BJNP_command) + sizeof (*job))));
+
+ resp_len = udp_command (devno, cmd_buf,
+ sizeof (struct JOB_DETAILS), resp_buf,
+ BJNP_RESP_MAX);
+
+ if (resp_len > 0)
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Job details response:\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, resp_buf, resp_len));
+ resp = (struct BJNP_command *) resp_buf;
+ device[devno].session_id = ntohs (resp->session_id);
+ }
+}
+
+static int
+bjnp_get_scanner_mac_address ( int devno, char *mac_address )
+{
+/*
+ * send discover to scanner
+ */
+
+ char cmd_buf[BJNP_CMD_MAX];
+ char resp_buf[BJNP_RESP_MAX];
+ int resp_len;
+ struct DISCOVER_RESPONSE *resp = (struct DISCOVER_RESPONSE * )&resp_buf;;
+
+ /* send job details command */
+
+ set_cmd (devno, (struct BJNP_command *) cmd_buf, CMD_UDP_DISCOVER, 0);
+ resp_len = udp_command (devno, cmd_buf,
+ sizeof (struct BJNP_command), resp_buf,
+ BJNP_RESP_MAX);
+
+ if (resp_len > 0)
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Discover response:\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, resp_buf, resp_len));
+ u8tohex( mac_address, resp -> mac_addr, sizeof( resp -> mac_addr ) );
+ return 0;
+ }
+ return -1;
+}
+
+static int
+bjnp_write (int devno, const SANE_Byte * buf, size_t count)
+{
+/*
+ * This function writes scandata to the scanner.
+ * Returns: number of bytes written to the scanner
+ */
+ int sent_bytes;
+ int terrno;
+ struct SCAN_BUF bjnp_buf;
+
+ if (device[devno].scanner_data_left)
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "bjnp_write: ERROR: scanner data left = 0x%lx = %ld\n",
+ (unsigned long) device[devno].scanner_data_left,
+ (unsigned long) device[devno].scanner_data_left));
+
+ /* set BJNP command header */
+
+ set_cmd (devno, (struct BJNP_command *) &bjnp_buf, CMD_TCP_SEND, count);
+ memcpy (bjnp_buf.scan_data, buf, count);
+ PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_write: sending 0x%lx = %ld bytes\n",
+ (unsigned long) count, (unsigned long) count);
+ PDBG (bjnp_hexdump (LOG_DEBUG2, (char *) &bjnp_buf,
+ sizeof (struct BJNP_command) + count)));
+
+ if ((sent_bytes =
+ send (device[devno].tcp_socket, &bjnp_buf,
+ sizeof (struct BJNP_command) + count, 0)) <
+ (ssize_t) (sizeof (struct BJNP_command) + count))
+ {
+ /* return result from write */
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT, "bjnp_write: Could not send data!\n"));
+ errno = terrno;
+ return sent_bytes;
+ }
+ /* correct nr of bytes sent for length of command */
+
+ else if (sent_bytes != (int) (sizeof (struct BJNP_command) + count))
+ {
+ errno = EIO;
+ return -1;
+ }
+ return count;
+}
+
+static int
+bjnp_send_read_request (int devno)
+{
+/*
+ * This function reads responses from the scanner.
+ * Returns: 0 on success, else -1
+ *
+ */
+ int sent_bytes;
+ int terrno;
+ struct BJNP_command bjnp_buf;
+
+ if (device[devno].scanner_data_left)
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "bjnp_send_read_request: ERROR scanner data left = 0x%lx = %ld\n",
+ (unsigned long) device[devno].scanner_data_left,
+ (unsigned long) device[devno].scanner_data_left));
+
+ /* set BJNP command header */
+
+ set_cmd (devno, (struct BJNP_command *) &bjnp_buf, CMD_TCP_REQ, 0);
+
+ PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_send_read_req sending command\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, (char *) &bjnp_buf,
+ sizeof (struct BJNP_command)));
+
+ if ((sent_bytes =
+ send (device[devno].tcp_socket, &bjnp_buf, sizeof (struct BJNP_command),
+ 0)) < 0)
+ {
+ /* return result from write */
+ terrno = errno;
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "bjnp_send_read_request: Could not send data!\n"));
+ errno = terrno;
+ return -1;
+ }
+ return 0;
+}
+
+static SANE_Status
+bjnp_recv_header (int devno, size_t *payload_size )
+{
+/*
+ * This function receives the response header to bjnp commands.
+ * devno device number
+ * size: return value for data size returned by scanner
+ * Returns:
+ * SANE_STATUS_IO_ERROR when any IO error occurs
+ * SANE_STATUS_GOOD in case no errors were encountered
+ */
+ struct BJNP_command resp_buf;
+ fd_set input;
+ struct timeval timeout;
+ int recv_bytes;
+ int terrno;
+ int result;
+ int fd;
+ int attempt;
+
+ PDBG (bjnp_dbg
+ (LOG_DEBUG, "bjnp_recv_header: receiving response header\n") );
+ fd = device[devno].tcp_socket;
+
+ *payload_size = 0;
+ attempt = 0;
+ do
+ {
+ /* wait for data to be received, ignore signals being received */
+ FD_ZERO (&input);
+ FD_SET (fd, &input);
+
+ timeout.tv_sec = BJNP_TIMEOUT_TCP;
+ timeout.tv_usec = 0;
+ }
+ while ( ( (result = select (fd + 1, &input, NULL, NULL, &timeout)) <= 0) &&
+ (errno == EINTR) && (attempt++ < BJNP_MAX_SELECT_ATTEMPTS));
+
+ if (result < 0)
+ {
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "bjnp_recv_header: could not read response header (select): %s!\n",
+ strerror (terrno)));
+ errno = terrno;
+ return SANE_STATUS_IO_ERROR;
+ }
+ else if (result == 0)
+ {
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "bjnp_recv_header: could not read response header (select timed out)!\n" ) );
+ errno = terrno;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* get response header */
+
+ if ((recv_bytes =
+ recv (fd, (char *) &resp_buf,
+ sizeof (struct BJNP_command),
+ 0)) != sizeof (struct BJNP_command))
+ {
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "bjnp_recv_header: (recv) could not read response header, received %d bytes!\n",
+ recv_bytes));
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "bjnp_recv_header: (recv) error: %s!\n",
+ strerror (terrno)));
+ errno = terrno;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (resp_buf.cmd_code != device[devno].last_cmd)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "bjnp_recv_header:ERROR, Received response has cmd code %d, expected %d\n",
+ resp_buf.cmd_code, device[devno].last_cmd));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (ntohs (resp_buf.seq_no) != (uint16_t) device[devno].serial)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "bjnp_recv_header:ERROR, Received response has serial %d, expected %d\n",
+ (int) ntohs (resp_buf.seq_no), (int) device[devno].serial));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* got response header back, retrieve length of scanner data */
+
+
+ *payload_size = ntohl (resp_buf.payload_len);
+ PDBG (bjnp_dbg
+ (LOG_DEBUG, "TCP response header(scanner data = %ld bytes):\n",
+ *payload_size) );
+ PDBG (bjnp_hexdump
+ (LOG_DEBUG2, (char *) &resp_buf, sizeof (struct BJNP_command)));
+ return SANE_STATUS_GOOD;
+}
+
+static int
+bjnp_init_device_structure(int dn, bjnp_sockaddr_t *sa )
+{
+ /* initialize device structure */
+
+ char name[BJNP_HOST_MAX];
+
+ device[dn].open = 0;
+#ifdef PIXMA_BJNP_USE_STATUS
+ device[dn].polling_status = BJNP_POLL_STOPPED;
+ device[dn].dialog = 0;
+ device[dn].status_key = 0;
+#endif
+ device[dn].tcp_socket = -1;
+
+ device[dn].addr = (bjnp_sockaddr_t *) malloc(sizeof ( bjnp_sockaddr_t) );
+ memset( device[dn].addr, 0, sizeof( bjnp_sockaddr_t ) );
+ memcpy(device[dn].addr, sa, sa_size((bjnp_sockaddr_t *)sa) );
+ device[dn].address_level = get_scanner_name(sa, name);
+ device[dn].session_id = 0;
+ device[dn].serial = -1;
+ device[dn].bjnp_timeout = 0;
+ device[dn].scanner_data_left = 0;
+ device[dn].last_cmd = 0;
+ device[dn].blocksize = 2048; /* safe assumption, we start low */
+ device[dn].last_block = 0;
+ /* fill mac_address */
+
+ if (bjnp_get_scanner_mac_address(dn, device[dn].mac_address) != 0 )
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "Cannot read mac address, skipping this scanner\n" ) );
+ return -1;
+ }
+ return 0;
+}
+
+static void
+bjnp_free_device_structure( int dn)
+{
+ if (device[dn].addr != NULL)
+ {
+ free (device[dn].addr );
+ device[dn].addr = NULL;
+ }
+ device[dn].open = 0;
+}
+
+static SANE_Status
+bjnp_recv_data (int devno, SANE_Byte * buffer, size_t * len)
+{
+/*
+ * This function receives the responses to the write commands.
+ * NOTE: len may not exceed SSIZE_MAX (as that is max for recv)
+ * Returns: number of bytes of payload received from device
+ */
+
+ fd_set input;
+ struct timeval timeout;
+ ssize_t recv_bytes;
+ int terrno;
+ int result;
+ int fd;
+ int attempt;
+
+ PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_recv_data: receiving response data\n"));
+ fd = device[devno].tcp_socket;
+
+ PDBG (bjnp_dbg
+ (LOG_DEBUG, "bjnp_recv_data: read response payload (%ld bytes max)\n",
+ (unsigned long) *len));
+
+ attempt = 0;
+ do
+ {
+ /* wait for data to be received, retry on a signal being received */
+ FD_ZERO (&input);
+ FD_SET (fd, &input);
+ timeout.tv_sec = BJNP_TIMEOUT_TCP;
+ timeout.tv_usec = 0;
+ }
+ while (((result = select (fd + 1, &input, NULL, NULL, &timeout)) <= 0) &&
+ (errno == EINTR) && (attempt++ < BJNP_MAX_SELECT_ATTEMPTS));
+
+ if (result < 0)
+ {
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "bjnp_recv_data: could not read response payload (select): %s!\n",
+ strerror (errno)));
+ errno = terrno;
+ *len = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ else if (result == 0)
+ {
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "bjnp_recv_data: could not read response payload (select timed out): %s!\n",
+ strerror (terrno)));
+ errno = terrno;
+ *len = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ((recv_bytes = recv (fd, buffer, *len, 0)) < 0)
+ {
+ terrno = errno;
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "bjnp_recv_data: could not read response payload (recv): %s!\n",
+ strerror (errno)));
+ errno = terrno;
+ *len = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Received TCP response payload (%ld bytes):\n",
+ (unsigned long) recv_bytes));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, buffer, recv_bytes));
+
+ *len = recv_bytes;
+ return SANE_STATUS_GOOD;
+}
+
+static BJNP_Status
+bjnp_allocate_device (SANE_String_Const devname,
+ SANE_Int * dn, char *res_host)
+{
+ char method[BJNP_METHOD_MAX];
+ char host[BJNP_HOST_MAX];
+ char port[BJNP_PORT_MAX] = "";
+ char args[BJNP_ARGS_MAX];
+ struct addrinfo *res, *cur;
+ struct addrinfo hints;
+ int result;
+ int i;
+
+ PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_allocate_device(%s) %d\n", devname, bjnp_no_devices));
+
+ if (split_uri (devname, method, host, port, args) != 0)
+ {
+ return BJNP_STATUS_INVAL;
+ }
+
+ if (strlen (args) != 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "URI may not contain userid, password or aguments: %s\n",
+ devname));
+
+ return BJNP_STATUS_INVAL;
+ }
+ if (strcmp (method, BJNP_METHOD) != 0)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "URI %s contains invalid method: %s\n", devname,
+ method));
+ return BJNP_STATUS_INVAL;
+ }
+
+ if (strlen(port) == 0)
+ {
+ sprintf( port, "%d", BJNP_PORT_SCAN );
+ }
+
+ hints.ai_flags = 0;
+#ifdef ENABLE_IPV6
+ hints.ai_family = AF_UNSPEC;
+#else
+ hints.ai_family = AF_INET;
+#endif
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = 0;
+ hints.ai_addrlen = 0;
+ hints.ai_addr = NULL;
+ hints.ai_canonname = NULL;
+ hints.ai_next = NULL;
+
+ result = getaddrinfo (host, port, &hints, &res );
+ if (result != 0 )
+ {
+ PDBG (bjnp_dbg (LOG_CRIT, "Cannot resolve host: %s port %s\n", host, port));
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Check if a device number is already allocated to any of the scanner's addresses */
+
+ cur = res;
+ while( cur != NULL)
+ {
+ /* create a new device structure for this address */
+
+ if (bjnp_no_devices == BJNP_NO_DEVICES)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT,
+ "Too many devices, ran out of device structures, can not add %s\n",
+ devname));
+ freeaddrinfo(res);
+ return BJNP_STATUS_INVAL;
+ }
+ if (bjnp_init_device_structure( bjnp_no_devices, (bjnp_sockaddr_t *)cur -> ai_addr) != 0)
+ {
+ /* giving up on this address, try next one if any */
+ break;
+ }
+ for (i = 0; i < bjnp_no_devices; i++)
+ {
+ /* we check for matching addresses as wel as matching mac_addresses as */
+ /* an IPv6 host can have multiple adresses */
+
+ if ( (sa_is_equal( device[i].addr, (bjnp_sockaddr_t *)cur -> ai_addr) ) ||
+ ( strcmp( device[i].mac_address, device[bjnp_no_devices].mac_address ) == 0 ) )
+ {
+ if ( device[i].address_level < device[bjnp_no_devices].address_level )
+ {
+ /* use the new address instead as it is better */
+ free (device[i].addr);
+ device[i].addr = device[bjnp_no_devices].addr;
+ device[bjnp_no_devices].addr = NULL;
+ device[i].address_level = device[bjnp_no_devices].address_level;
+ }
+ freeaddrinfo(res);
+ *dn = i;
+ bjnp_free_device_structure( bjnp_no_devices);
+ return BJNP_STATUS_ALREADY_ALLOCATED;
+ }
+ }
+ cur = cur->ai_next;
+ }
+ freeaddrinfo(res);
+
+ PDBG (bjnp_dbg (LOG_INFO, "Scanner not yet in our list, added it: %s:%s\n", host, port));
+
+ /* return hostname if required */
+
+ if (res_host != NULL)
+ {
+ strcpy (res_host, host);
+ }
+ *dn = bjnp_no_devices;
+
+ /* Commit new device structure */
+
+ bjnp_no_devices++;
+
+ return BJNP_STATUS_GOOD;
+}
+
+static void add_scanner(SANE_Int *dev_no,
+ const char *uri,
+ SANE_Status (*attach_bjnp)
+ (SANE_String_Const devname,
+ SANE_String_Const makemodel,
+ SANE_String_Const serial,
+ const struct pixma_config_t *
+ const pixma_devices[]),
+ const struct pixma_config_t *const pixma_devices[])
+
+{
+ char scanner_host[BJNP_HOST_MAX];
+ char serial[BJNP_SERIAL_MAX];
+ char makemodel[BJNP_IEEE1284_MAX];
+
+ /* Allocate device structure for scanner */
+ switch (bjnp_allocate_device (uri, dev_no, scanner_host))
+ {
+ case BJNP_STATUS_GOOD:
+ if (get_scanner_id (*dev_no, makemodel) != 0)
+ {
+ PDBG (bjnp_dbg (LOG_CRIT, "Cannot read scanner make & model: %s\n",
+ uri));
+ }
+ else
+ {
+ /*
+ * inform caller of found scanner
+ */
+
+ determine_scanner_serial (scanner_host, device[*dev_no].mac_address, serial);
+ attach_bjnp (uri, makemodel,
+ serial, pixma_devices);
+ }
+ break;
+ case BJNP_STATUS_ALREADY_ALLOCATED:
+ PDBG (bjnp_dbg (LOG_NOTICE, "Scanner at %s was added before, good!\n",
+ uri));
+ break;
+
+ case BJNP_STATUS_INVAL:
+ PDBG (bjnp_dbg (LOG_NOTICE, "Scanner at %s can not be added\n",
+ uri));
+ break;
+ }
+}
+
+
+/*
+ * Public functions
+ */
+
+/** Initialize sanei_bjnp.
+ *
+ * Call this before any other sanei_bjnp function.
+ */
+extern void
+sanei_bjnp_init (void)
+{
+ DBG_INIT();
+ bjnp_no_devices = 0;
+}
+
+/**
+ * Find devices that implement the bjnp protocol
+ *
+ * The function attach is called for every device which has been found.
+ *
+ * @param attach attach function
+ *
+ * @return SANE_STATUS_GOOD - on success (even if no scanner was found)
+ */
+extern SANE_Status
+sanei_bjnp_find_devices (const char **conf_devices,
+ SANE_Status (*attach_bjnp)
+ (SANE_String_Const devname,
+ SANE_String_Const makemodel,
+ SANE_String_Const serial,
+ const struct pixma_config_t *
+ const pixma_devices[]),
+ const struct pixma_config_t *const pixma_devices[])
+{
+ int numbytes = 0;
+ struct BJNP_command cmd;
+ unsigned char resp_buf[2048];
+ struct DISCOVER_RESPONSE *disc_resp = ( struct DISCOVER_RESPONSE *) & resp_buf;
+ int socket_fd[BJNP_SOCK_MAX];
+ int no_sockets;
+ int i;
+ int attempt;
+ int last_socketfd = 0;
+ fd_set fdset;
+ fd_set active_fdset;
+ struct timeval timeout;
+ char scanner_host[256];
+ char uri[256];
+ int dev_no;
+ bjnp_sockaddr_t broadcast_addr[BJNP_SOCK_MAX];
+ bjnp_sockaddr_t scanner_sa;
+ socklen_t socklen;
+
+ memset( broadcast_addr, 0, sizeof( broadcast_addr) );
+ memset( &scanner_sa, 0 ,sizeof( scanner_sa ) );
+ PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_find_devices:\n"));
+ bjnp_no_devices = 0;
+
+ for (i=0; i < BJNP_SOCK_MAX; i++)
+ {
+ socket_fd[i] = -1;
+ }
+ /* First add devices from config file */
+
+ if (conf_devices[0] == NULL)
+ PDBG (bjnp_dbg( LOG_DEBUG, "No devices specified in configuration file.\n" ) );
+
+ for (i = 0; conf_devices[i] != NULL; i++)
+ {
+ PDBG (bjnp_dbg
+ (LOG_DEBUG, "Adding scanner from pixma.conf: %s\n", conf_devices[i]));
+ add_scanner(&dev_no, conf_devices[i], attach_bjnp, pixma_devices);
+ }
+ PDBG (bjnp_dbg
+ (LOG_DEBUG,
+ "Added all configured scanners, now do auto detection...\n"));
+
+ /*
+ * Send UDP DISCOVER to discover scanners and return the list of scanners found
+ */
+
+ FD_ZERO (&fdset);
+ set_cmd (-1, &cmd, CMD_UDP_DISCOVER, 0);
+
+ no_sockets = 0;
+#ifdef HAVE_IFADDRS_H
+ {
+ struct ifaddrs *interfaces = NULL;
+ struct ifaddrs *interface;
+ getifaddrs (&interfaces);
+
+ /* create a socket for each suitable interface */
+
+ interface = interfaces;
+ while ((no_sockets < BJNP_SOCK_MAX) && (interface != NULL))
+ {
+ if ( ! (interface -> ifa_flags & IFF_POINTOPOINT) &&
+ ( (socket_fd[no_sockets] =
+ prepare_socket( interface -> ifa_name,
+ (bjnp_sockaddr_t *) interface -> ifa_addr,
+ (bjnp_sockaddr_t *) interface -> ifa_broadaddr,
+ &broadcast_addr[no_sockets] ) ) != -1 ) )
+ {
+ /* track highest used socket for later use in select */
+ if (socket_fd[no_sockets] > last_socketfd)
+ {
+ last_socketfd = socket_fd[no_sockets];
+ }
+ FD_SET (socket_fd[no_sockets], &fdset);
+ no_sockets++;
+ }
+ interface = interface->ifa_next;
+ }
+ freeifaddrs (interfaces);
+ }
+#else
+ /* we have no easy way to find interfaces with their broadcast addresses. */
+ /* use global broadcast and all-hosts instead */
+ {
+ bjnp_sockaddr_t local;
+ bjnp_sockaddr_t bc_addr;
+
+ memset( &local, 0, sizeof( local) );
+ local.ipv4.sin_family = AF_INET;
+ local.ipv4.sin_addr.s_addr = htonl (INADDR_ANY);
+
+ bc_addr.ipv4.sin_family = AF_INET;
+ bc_addr.ipv4.sin_port = htons(BJNP_PORT_SCAN);
+ bc_addr.ipv4.sin_addr.s_addr = htonl (INADDR_BROADCAST);
+
+ socket_fd[no_sockets] = prepare_socket( "any_interface",
+ &local,
+ &bc_addr,
+ &broadcast_addr[no_sockets] );
+ if (socket_fd[no_sockets] >= 0)
+ {
+ FD_SET (socket_fd[no_sockets], &fdset);
+ if (socket_fd[no_sockets] > last_socketfd)
+ {
+ last_socketfd = socket_fd[no_sockets];
+ }
+ no_sockets++;
+ }
+#ifdef ENABLE_IPV6
+ local.ipv6.sin6_family = AF_INET6;
+ local.ipv6.sin6_addr = in6addr_any;
+
+ socket_fd[no_sockets] = prepare_socket( "any_interface",
+ &local,
+ NULL,
+ &broadcast_addr[no_sockets] );
+ if (socket_fd[no_sockets] >= 0)
+ {
+ FD_SET (socket_fd[no_sockets], &fdset);
+ if (socket_fd[no_sockets] > last_socketfd)
+ {
+ last_socketfd = socket_fd[no_sockets];
+ }
+ no_sockets++;
+ }
+#endif
+ }
+#endif
+
+ /* send BJNP_MAX_BROADCAST_ATTEMPTS broadcasts on each prepared socket */
+ for (attempt = 0; attempt < BJNP_MAX_BROADCAST_ATTEMPTS; attempt++)
+ {
+ for ( i=0; i < no_sockets; i++)
+ {
+ bjnp_send_broadcast ( socket_fd[i], &broadcast_addr[i], cmd, sizeof (cmd));
+ }
+ /* wait for some time between broadcast packets */
+ usleep (BJNP_BROADCAST_INTERVAL * BJNP_USLEEP_MS);
+ }
+
+ /* wait for a UDP response */
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = BJNP_BC_RESPONSE_TIMEOUT * BJNP_USLEEP_MS;
+
+
+ active_fdset = fdset;
+
+ while (select (last_socketfd + 1, &active_fdset, NULL, NULL, &timeout) > 0)
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG, "Select returned, time left %d.%d....\n",
+ (int) timeout.tv_sec, (int) timeout.tv_usec));
+ for (i = 0; i < no_sockets; i++)
+ {
+ if (FD_ISSET (socket_fd[i], &active_fdset))
+ {
+ socklen = sizeof(scanner_sa);
+ if ((numbytes =
+ recvfrom (socket_fd[i], resp_buf, sizeof (resp_buf), 0,
+ &(scanner_sa.addr), &socklen ) ) == -1)
+ {
+ PDBG (bjnp_dbg
+ (LOG_INFO, "find_devices: no data received"));
+ break;
+ }
+ else
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG2, "Discover response:\n"));
+ PDBG (bjnp_hexdump (LOG_DEBUG2, &resp_buf, numbytes));
+
+ /* check if something sensible is returned */
+ if ( (numbytes < (int)sizeof (struct BJNP_command)) ||
+ (strncmp ("BJNP", disc_resp-> response.BJNP_id, 4) != 0))
+ {
+ /* not a valid response, assume not a scanner */
+
+ char bjnp_id[5];
+ strncpy(bjnp_id, disc_resp-> response.BJNP_id, 4);
+ bjnp_id[4] = '\0';
+ PDBG (bjnp_dbg (LOG_INFO,
+ "Invalid discover response! Length = %d, Id = %s\n",
+ numbytes, bjnp_id ) );
+ break;
+ }
+ if ( ! ((disc_resp -> response.dev_type) & 0x80) )
+ {
+ /* not a response, a command from somebody else or */
+ /* a discover command that we generated */
+ break;
+ }
+ };
+
+ /* scanner found, get IP-address or hostname */
+ get_scanner_name( &scanner_sa, scanner_host);
+
+ /* construct URI */
+ sprintf (uri, "%s://%s:%d", BJNP_METHOD, scanner_host,
+ BJNP_PORT_SCAN);
+
+ add_scanner( &dev_no, uri, attach_bjnp, pixma_devices);
+
+ }
+ }
+ active_fdset = fdset;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = BJNP_BC_RESPONSE_TIMEOUT * BJNP_USLEEP_MS;
+ }
+ PDBG (bjnp_dbg (LOG_DEBUG, "scanner discovery finished...\n"));
+
+ for (i = 0; i < no_sockets; i++)
+ close (socket_fd[i]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Open a BJNP device.
+ *
+ * The device is opened by its name devname and the device number is
+ * returned in dn on success.
+ *
+ * Device names consist of an URI
+ * Where:
+ * type = bjnp
+ * hostname = resolvable name or IP-address
+ * port = 8612 for a scanner
+ * An example could look like this: bjnp://host.domain:8612
+ *
+ * @param devname name of the device to open
+ * @param dn device number
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_ACCESS_DENIED - if the file couldn't be accessed due to
+ * permissions
+ * - SANE_STATUS_INVAL - on every other error
+ */
+
+extern SANE_Status
+sanei_bjnp_open (SANE_String_Const devname, SANE_Int * dn)
+{
+ int result;
+
+ PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_open(%s, %d):\n", devname, *dn));
+
+ result = bjnp_allocate_device (devname, dn, NULL);
+ if ( (result != BJNP_STATUS_GOOD) && (result != BJNP_STATUS_ALREADY_ALLOCATED ) )
+ return SANE_STATUS_INVAL;
+
+ return sanei_bjnp_activate( *dn);;
+}
+
+/** Close a BJNP device.
+ *
+ * @param dn device number
+ */
+
+void
+sanei_bjnp_close (SANE_Int dn)
+{
+ PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_close(%d):\n", dn));
+ sanei_bjnp_deactivate(dn);
+ device[dn].open = 0;
+}
+
+/** Activate BJNP device connection
+ *
+ * @param dn device number
+ */
+
+SANE_Status
+sanei_bjnp_activate (SANE_Int dn)
+{
+ char hostname[256];
+ char pid_str[64];
+
+ PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_activate (%d)\n", dn));
+
+ gethostname (hostname, 256);
+ hostname[255] = '\0';
+ sprintf (pid_str, "Process ID = %d", getpid ());
+
+ bjnp_send_job_details (dn, hostname, getusername (), pid_str);
+
+ if (bjnp_open_tcp (dn) != 0)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Deactivate BJNP device connection
+ *
+ * @paran dn device number
+ */
+
+SANE_Status
+sanei_bjnp_deactivate (SANE_Int dn)
+{
+ PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_deactivate (%d)\n", dn));
+
+ bjnp_finish_job (dn);
+
+ if ( device[dn].tcp_socket != -1)
+ {
+ close (device[dn].tcp_socket);
+ device[dn].tcp_socket = -1;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** Set the timeout for interrupt reads.
+ * we do not use it for bulk reads!
+ * @param timeout the new timeout in ms
+ */
+extern void
+sanei_bjnp_set_timeout (SANE_Int devno, SANE_Int timeout)
+{
+ PDBG (bjnp_dbg (LOG_INFO, "bjnp_set_timeout to %d\n",
+ timeout));
+
+ device[devno].bjnp_timeout = timeout;
+}
+
+/** Initiate a bulk transfer read.
+ *
+ * Read up to size bytes from the device to buffer. After the read, size
+ * contains the number of bytes actually read.
+ *
+ * @param dn device number
+ * @param buffer buffer to store read data in
+ * @param size size of the data
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on succes
+ * - SANE_STATUS_EOF - if zero bytes have been read
+ * - SANE_STATUS_IO_ERROR - if an error occured during the read
+ * - SANE_STATUS_INVAL - on every other error
+ *
+ */
+
+extern SANE_Status
+sanei_bjnp_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size)
+{
+ SANE_Status result;
+ SANE_Status error;
+ size_t recvd;
+ size_t more;
+ size_t requested;
+
+ PDBG (bjnp_dbg
+ (LOG_INFO, "bjnp_read_bulk(%d, bufferptr, 0x%lx = %ld)\n", dn,
+ (unsigned long) *size, (unsigned long) *size));
+
+ recvd = 0;
+ requested = *size;
+
+ PDBG (bjnp_dbg
+ (LOG_DEBUG, "bjnp_read_bulk: 0x%lx = %ld bytes available at start\n",
+ (unsigned long) device[dn].scanner_data_left,
+ (unsigned long) device[dn].scanner_data_left ) );
+
+ while ( (recvd < requested) && !( device[dn].last_block && (device[dn].scanner_data_left == 0)) )
+
+ {
+ PDBG (bjnp_dbg
+ (LOG_DEBUG,
+ "Received 0x%lx = %ld bytes, backend requested 0x%lx = %ld bytes\n",
+ (unsigned long) recvd, (unsigned long) recvd,
+ (unsigned long) requested, (unsigned long)requested ));
+
+ if (device[dn].scanner_data_left == 0)
+ {
+ /* send new read request */
+
+ PDBG (bjnp_dbg (LOG_DEBUG,
+ "No (more) scanner data available, requesting more( blocksize = %ld =%lx\n",
+ (long int) device[dn].blocksize, (long int) device[dn].blocksize ));
+
+ if ((error = bjnp_send_read_request (dn)) != SANE_STATUS_GOOD)
+ {
+ *size = recvd;
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ( ( error = bjnp_recv_header (dn, &(device[dn].scanner_data_left) ) ) != SANE_STATUS_GOOD)
+ {
+ *size = recvd;
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* correct blocksize if applicable */
+
+ device[dn].blocksize = MAX (device[dn].blocksize, device[dn].scanner_data_left);
+
+ if ( device[dn].scanner_data_left < device[dn].blocksize)
+ {
+ /* the scanner will not react at all to a read request, when no more data is available */
+ /* we now determine end of data by comparing the payload size to the maximun blocksize */
+ /* this block is shorter than blocksize, so after this block we are done */
+
+ device[dn].last_block = 1;
+ }
+ if ( device[dn].scanner_data_left == 0 )
+ {
+ break;
+ }
+ }
+
+ PDBG (bjnp_dbg (LOG_DEBUG, "Scanner reports 0x%lx = %ld bytes available\n",
+ (unsigned long) device[dn].scanner_data_left,
+ (unsigned long) device[dn].scanner_data_left));
+
+ /* read as many bytes as needed and available */
+
+ more = MIN( device[dn].scanner_data_left, (requested - recvd) );
+
+ PDBG (bjnp_dbg
+ (LOG_DEBUG,
+ "reading 0x%lx = %ld (of max 0x%lx = %ld) bytes\n",
+ (unsigned long) more,
+ (unsigned long) more,
+ (unsigned long) device[dn].scanner_data_left,
+ (unsigned long) device[dn].scanner_data_left) );
+
+ result = bjnp_recv_data (dn, buffer + recvd, &more);
+ if (result != SANE_STATUS_GOOD)
+ {
+ *size = recvd;
+ return SANE_STATUS_IO_ERROR;
+ }
+ PDBG (bjnp_dbg (LOG_DEBUG, "Requested %ld bytes, received: %ld\n",
+ MIN( device[dn].scanner_data_left, (requested - recvd) ), more) );
+
+ device[dn].scanner_data_left = device[dn].scanner_data_left - more;
+ recvd = recvd + more;
+ }
+
+ PDBG (bjnp_dbg (LOG_DEBUG, "returning %ld bytes, backend expexts %ld\n",
+ recvd, *size ) );
+ *size = recvd;
+ if ( *size == 0 )
+ return SANE_STATUS_EOF;
+ return SANE_STATUS_GOOD;
+}
+
+/** Initiate a bulk transfer write.
+ *
+ * Write up to size bytes from buffer to the device. After the write size
+ * contains the number of bytes actually written.
+ *
+ * @param dn device number
+ * @param buffer buffer to write to device
+ * @param size size of the data
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on succes
+ * - SANE_STATUS_IO_ERROR - if an error occured during the write
+ * - SANE_STATUS_INVAL - on every other error
+ */
+
+extern SANE_Status
+sanei_bjnp_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size)
+{
+ ssize_t sent;
+ size_t recvd;
+ uint32_t buf;
+ size_t payload_size;
+
+ PDBG (bjnp_dbg
+ (LOG_INFO, "bjnp_write_bulk(%d, bufferptr, 0x%lx = %ld)\n", dn,
+ (unsigned long) *size, (unsigned long) *size));
+ sent = bjnp_write (dn, buffer, *size);
+ if (sent < 0)
+ return SANE_STATUS_IO_ERROR;
+ if (sent != (int) *size)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "Sent only %ld bytes to scanner, expected %ld!!\n",
+ (unsigned long) sent, (unsigned long) *size));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (bjnp_recv_header (dn, &payload_size) != SANE_STATUS_GOOD)
+ {
+ PDBG (bjnp_dbg (LOG_CRIT, "Could not read response to command!\n"));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (payload_size != 4)
+ {
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "Scanner length of write confirmation = 0x%lx bytes = %ld, expected %d!!\n",
+ (unsigned long) payload_size,
+ (unsigned long) payload_size, 4));
+ return SANE_STATUS_IO_ERROR;
+ }
+ recvd = payload_size;
+ if ((bjnp_recv_data (dn, (unsigned char *) &buf, &recvd) !=
+ SANE_STATUS_GOOD) || (recvd != payload_size))
+ {
+ PDBG (bjnp_dbg (LOG_CRIT,
+ "Could not read length of data confirmed by device\n"));
+ return SANE_STATUS_IO_ERROR;
+ }
+ recvd = ntohl (buf);
+ if (recvd != *size)
+ {
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "Scanner confirmed %ld bytes, expected %ld!!\n",
+ (unsigned long) recvd, (unsigned long) *size));
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* we can expect data from the scanner again */
+
+ device[dn].last_block = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Initiate a interrupt transfer read.
+ *
+ * Read up to size bytes from the interrupt endpoint from the device to
+ * buffer. After the read, size contains the number of bytes actually read.
+ *
+ * @param dn device number
+ * @param buffer buffer to store read data in
+ * @param size size of the data
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on succes
+ * - SANE_STATUS_EOF - if zero bytes have been read
+ * - SANE_STATUS_IO_ERROR - if an error occured during the read
+ * - SANE_STATUS_INVAL - on every other error
+ *
+ */
+
+extern SANE_Status
+sanei_bjnp_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
+{
+#ifndef PIXMA_BJNP_USE_STATUS
+ PDBG (bjnp_dbg
+ (LOG_INFO, "bjnp_read_int(%d, bufferptr, 0x%lx = %ld):\n", dn,
+ (unsigned long) *size, (unsigned long) *size));
+
+ memset (buffer, 0, *size);
+ sleep (1);
+ return SANE_STATUS_IO_ERROR;
+#else
+
+ char hostname[256];
+ int resp_len;
+ int timeout;
+ int seconds;
+
+ PDBG (bjnp_dbg
+ (LOG_INFO, "bjnp_read_int(%d, bufferptr, 0x%lx = %ld):\n", dn,
+ (unsigned long) *size, (unsigned long) *size));
+
+ memset (buffer, 0, *size);
+
+ gethostname (hostname, 32);
+ hostname[32] = '\0';
+
+
+ switch (device[dn].polling_status)
+ {
+ case BJNP_POLL_STOPPED:
+
+ /* establish dialog */
+
+ if ( (bjnp_poll_scanner (dn, 0, hostname, getusername (), buffer, *size ) != 0) ||
+ (bjnp_poll_scanner (dn, 1, hostname, getusername (), buffer, *size ) != 0) )
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Failed to setup read_intr dialog with device!\n"));
+ device[dn].dialog = 0;
+ device[dn].status_key = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ device[dn].polling_status = BJNP_POLL_STARTED;
+
+ case BJNP_POLL_STARTED:
+ /* we use only seonds accuracy between poll attempts */
+ timeout = device[dn].bjnp_timeout /1000;
+
+ do
+ {
+ if ( (resp_len = bjnp_poll_scanner (dn, 2, hostname, getusername (), buffer, *size ) ) < 0 )
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Restarting polling dialog!\n"));
+ device[dn].polling_status = BJNP_POLL_STOPPED;
+ *size = 0;
+ return SANE_STATUS_EOF;
+ }
+ *size = (size_t) resp_len;
+ if ( resp_len > 0 )
+ {
+ device[dn].polling_status = BJNP_POLL_STATUS_RECEIVED;
+
+ /* this is a bit of a hack, but the scanner does not like */
+ /* us to continue using the existing tcp socket */
+
+ sanei_bjnp_deactivate(dn);
+ sanei_bjnp_activate(dn);
+
+ return SANE_STATUS_GOOD;
+ }
+ seconds = timeout > 2 ? 2 : timeout;
+ sleep(seconds);
+ timeout = timeout - seconds;
+ } while ( timeout > 0 ) ;
+ break;
+ case BJNP_POLL_STATUS_RECEIVED:
+ if ( (resp_len = bjnp_poll_scanner (dn, 5, hostname, getusername (), buffer, *size ) ) < 0 )
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "Restarting polling dialog!\n"));
+ device[dn].polling_status = BJNP_POLL_STOPPED;
+ *size = 0;
+ break;
+ }
+ }
+ return SANE_STATUS_EOF;
+#endif
+}
diff --git a/backend/pixma_bjnp.h b/backend/pixma_bjnp.h
new file mode 100644
index 0000000..e2939d7
--- /dev/null
+++ b/backend/pixma_bjnp.h
@@ -0,0 +1,202 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2008 by Louis Lagendijk
+ based on sane_usb.h:
+ Copyright (C) 2003, 2005 Rene Rebe (sanei_read_int,sanei_set_timeout)
+ Copyright (C) 2001, 2002 Henning Meier-Geinitz
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+/** @file sanei_bjnp.h
+ * This file provides a generic BJNP interface.
+ */
+
+#ifndef sanei_bjnp_h
+#define sanei_bjnp_h
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "pixma.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* for size_t */
+#endif
+
+/** Initialize sanei_bjnp.
+ *
+ * Call this before any other sanei_bjnp function.
+ */
+extern void sanei_bjnp_init (void);
+
+/** Find scanners responding to a BJNP broadcast.
+ *
+ * The function attach is called for every device which has been found.
+ * Serial is the address of the scanner in human readable form of max
+ * SHORT_HOSTNAME_MAX characters
+ * @param conf_devices list of pre-configures device URI's to attach
+ * @param attach attach function
+ * @param pixma_devices device informatio needed by attach function
+ *
+ * @return SANE_STATUS_GOOD - on success (even if no scanner was found)
+ */
+
+#define SHORT_HOSTNAME_MAX 16
+
+extern SANE_Status
+sanei_bjnp_find_devices (const char **conf_devices,
+ SANE_Status (*attach_bjnp)
+ (SANE_String_Const devname,
+ SANE_String_Const makemodel,
+ SANE_String_Const serial,
+ const struct pixma_config_t *
+ const pixma_devices[]),
+ const struct pixma_config_t *const pixma_devices[]);
+
+/** Open a BJNP device.
+ *
+ * The device is opened by its name devname and the device number is
+ * returned in dn on success.
+ *
+ * Device names consist of an URI
+ * Where:
+ * method = bjnp
+ * hostname = resolvable name or IP-address
+ * port = 8612 for a scanner
+ * An example could look like this: bjnp://host.domain:8612
+ *
+ * @param devname name of the device to open
+ * @param dn device number
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on success
+ * - SANE_STATUS_ACCESS_DENIED - if the file couldn't be accessed due to
+ * permissions
+ * - SANE_STATUS_INVAL - on every other error
+ */
+extern SANE_Status sanei_bjnp_open (SANE_String_Const devname, SANE_Int * dn);
+
+/** Close a BJNP device.
+ *
+ * @param dn device number
+ */
+
+extern void sanei_bjnp_close (SANE_Int dn);
+
+/** Activate a BJNP device connection
+ *
+ * @param dn device number
+ */
+
+extern SANE_Status sanei_bjnp_activate (SANE_Int dn);
+
+/** De-activate a BJNP device connection
+ *
+ * @param dn device number
+ */
+
+extern SANE_Status sanei_bjnp_deactivate (SANE_Int dn);
+
+/** Set the libbjnp timeout for bulk and interrupt reads.
+ *
+ * @param devno device number
+ * @param timeout the new timeout in ms
+ */
+extern void sanei_bjnp_set_timeout (SANE_Int devno, SANE_Int timeout);
+
+/** Check if sanei_bjnp_set_timeout() is available.
+ */
+#define HAVE_SANEI_BJNP_SET_TIMEOUT
+
+/** Initiate a bulk transfer read.
+ *
+ * Read up to size bytes from the device to buffer. After the read, size
+ * contains the number of bytes actually read.
+ *
+ * @param dn device number
+ * @param buffer buffer to store read data in
+ * @param size size of the data
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on succes
+ * - SANE_STATUS_EOF - if zero bytes have been read
+ * - SANE_STATUS_IO_ERROR - if an error occured during the read
+ * - SANE_STATUS_INVAL - on every other error
+ *
+ */
+extern SANE_Status
+sanei_bjnp_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size);
+
+/** Initiate a bulk transfer write.
+ *
+ * Write up to size bytes from buffer to the device. After the write size
+ * contains the number of bytes actually written.
+ *
+ * @param dn device number
+ * @param buffer buffer to write to device
+ * @param size size of the data
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on succes
+ * - SANE_STATUS_IO_ERROR - if an error occured during the write
+ * - SANE_STATUS_INVAL - on every other error
+ */
+extern SANE_Status
+sanei_bjnp_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size);
+
+/** Initiate a interrupt transfer read.
+ *
+ * Read up to size bytes from the interrupt endpoint from the device to
+ * buffer. After the read, size contains the number of bytes actually read.
+ *
+ * @param dn device number
+ * @param buffer buffer to store read data in
+ * @param size size of the data
+ *
+ * @return
+ * - SANE_STATUS_GOOD - on succes
+ * - SANE_STATUS_EOF - if zero bytes have been read
+ * - SANE_STATUS_IO_ERROR - if an error occured during the read
+ * - SANE_STATUS_INVAL - on every other error
+ *
+ */
+
+extern SANE_Status
+sanei_bjnp_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size);
+
+/*------------------------------------------------------*/
+#endif /* sanei_bjnp_h */
diff --git a/backend/pixma_bjnp_private.h b/backend/pixma_bjnp_private.h
new file mode 100644
index 0000000..859baa5
--- /dev/null
+++ b/backend/pixma_bjnp_private.h
@@ -0,0 +1,349 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2008 by Louis Lagendijk
+
+ This file is part of the SANE package.
+
+ Data structures and definitions for
+ bjnp backend for the Common UNIX Printing System (CUPS).
+
+ These coded instructions, statements, and computer programs are the
+ property of Louis Lagendijk and are protected by Federal copyright
+ law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ "LICENSE" which should have been included with this file. If this
+ file is missing or damaged, see the license at "http://www.cups.org/".
+
+ This file is subject to the Apple OS-Developed Software exception.
+
+ SANE is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ * BJNP definitions
+ */
+
+/* selection of options */
+/* This works now, disable when it gives you problems */
+#define PIXMA_BJNP_USE_STATUS 1
+
+/* sizes */
+
+#define BJNP_PRINTBUF_MAX 1400 /* size of printbuffer */
+#define BJNP_CMD_MAX 2048 /* size of BJNP response buffer */
+#define BJNP_RESP_MAX 2048 /* size of BJNP response buffer */
+#define BJNP_SOCK_MAX 256 /* maximum number of open sockets */
+#define BJNP_MODEL_MAX 64 /* max allowed size for make&model */
+#define BJNP_STATUS_MAX 256 /* max size for status string */
+#define BJNP_IEEE1284_MAX 1024 /* max. allowed size of IEEE1284 id */
+#define BJNP_METHOD_MAX 16 /* max length of method */
+#define BJNP_HOST_MAX 128 /* max length of hostname or address */
+#define BJNP_PORT_MAX 64 /* max length of port string */
+#define BJNP_ARGS_MAX 128 /* max length of argument string */
+#define BJNP_SERIAL_MAX 16 /* maximum length of serial number */
+#define BJNP_NO_DEVICES 16 /* max number of open devices */
+#define BJNP_SCAN_BUF_MAX 65536 /* size of scanner data intermediate buffer */
+
+/* timers */
+#define BJNP_BROADCAST_INTERVAL 10 /* ms between broadcasts */
+#define BJNP_BC_RESPONSE_TIMEOUT 500 /* waiting time for broadc. responses */
+#define BJNP_TIMEOUT_UDP 4 /* standard UDP timeout in seconds */
+#define BJNP_TIMEOUT_TCP 4 /* standard TCP timeout in seconds */
+#define BJNP_USLEEP_MS 1000 /* sleep for 1 msec */
+
+/* retries */
+#define BJNP_MAX_SELECT_ATTEMPTS 3 /* max nr of retries on select (EINTR) */
+#define BJNP_MAX_BROADCAST_ATTEMPTS 2 /* number of broadcast packets to be sent */
+#define BJNP_UDP_RETRY_MAX 3 /* max nt of retries on a udp command */
+
+#define bjnp_dbg DBG
+#include "../include/sane/sanei_debug.h"
+
+/* loglevel definitions */
+
+#define LOG_CRIT 0
+#define LOG_NOTICE 1
+#define LOG_INFO 2
+#define LOG_DEBUG 3
+#define LOG_DEBUG2 4
+#define LOG_DEBUG3 5
+
+#define BJNP_RESTART_POLL -1
+
+/*************************************/
+/* BJNP protocol related definitions */
+/*************************************/
+
+/* port numbers */
+typedef enum bjnp_port_e
+{
+ BJNP_PORT_BROADCAST_BASE = 8610,
+ BJNP_PORT_PRINT = 8611,
+ BJNP_PORT_SCAN = 8612,
+ BJNP_PORT_3 = 8613,
+ BJNP_PORT_4 = 8614
+} bjnp_port_t;
+
+#define BJNP_METHOD "bjnp"
+#define BJNP_STRING "BJNP"
+
+/* commands */
+typedef enum bjnp_cmd_e
+{
+ CMD_UDP_DISCOVER = 0x01, /* discover if service type is listening at this port */
+ CMD_UDP_START_SCAN = 0x02, /* start scan pressed, sent from scanner to 224.0.0.1 */
+ CMD_UDP_JOB_DETAILS = 0x10, /* send print/ scanner job owner details */
+ CMD_UDP_CLOSE = 0x11, /* request connection closure */
+ CMD_UDP_GET_STATUS = 0x20, /* get printer status */
+ CMD_TCP_REQ = 0x20, /* read data from device */
+ CMD_TCP_SEND = 0x21, /* send data to device */
+ CMD_UDP_GET_ID = 0x30, /* get printer identity */
+ CMD_UDP_POLL = 0x32 /* poll scanner for button status */
+} bjnp_cmd_t;
+
+/* command type */
+
+typedef enum uint8_t
+{
+ BJNP_CMD_PRINT = 0x1, /* printer command */
+ BJNP_CMD_SCAN = 0x2, /* scanner command */
+ BJNP_RES_PRINT = 0x81, /* printer response */
+ BJNP_RES_SCAN = 0x82 /* scanner response */
+} bjnp_cmd_type_t;
+
+/***************************/
+/* BJNP protocol structure */
+/***************************/
+
+/* The common protocol header */
+
+struct __attribute__ ((__packed__)) BJNP_command
+{
+ char BJNP_id[4]; /* string: BJNP */
+ uint8_t dev_type; /* 1 = printer, 2 = scanner */
+ /* responses have MSB set */
+ uint8_t cmd_code; /* command code/response code */
+ int16_t unknown1; /* unknown, always 0? */
+ int16_t seq_no; /* sequence number */
+ uint16_t session_id; /* session id for printing */
+ uint32_t payload_len; /* length of command buffer */
+};
+
+/* Layout of the init response buffer */
+
+struct __attribute__ ((__packed__)) DISCOVER_RESPONSE
+{
+ struct BJNP_command response; /* reponse header */
+ char unknown1[4]; /* 00 01 08 00 */
+ char mac_len; /* length of mac address */
+ char addr_len; /* length od address field */
+ unsigned char mac_addr[6]; /* printers mac address */
+ union {
+ struct __attribute__ ((__packed__)) {
+ unsigned char ipv4_addr[4];
+ } ipv4;
+ struct __attribute__ ((__packed__)) {
+ unsigned char ipv6_addr_1[16];
+ unsigned char ipv6_addr_2[16];
+ } ipv6;
+ } addresses;
+};
+
+/* layout of payload for the JOB_DETAILS command */
+
+struct __attribute__ ((__packed__)) JOB_DETAILS
+{
+ struct BJNP_command cmd; /* command header */
+ char unknown[8]; /* don't know what these are for */
+ char hostname[64]; /* hostname of sender */
+ char username[64]; /* username */
+ char jobtitle[256]; /* job title */
+};
+
+/* layout of the poll command, not everything is complete */
+
+struct __attribute__ ((__packed__)) POLL_DETAILS
+{
+ struct BJNP_command cmd; /* command header */
+ uint16_t type; /* 0, 1, 2 or 5 */
+ /* 05 = reset status */
+ union {
+ struct __attribute__ ((__packed__)) {
+ char empty0[78]; /* type 0 has only 0 */
+ } type0; /* length = 80 */
+
+ struct __attribute__ ((__packed__)) {
+ char empty1[6]; /* 0 */
+ char user_host[64]; /* unicode user <space> <space> hostname */
+ uint64_t emtpy2; /* 0 */
+ } type1; /* length = 80 */
+
+ struct __attribute__ ((__packed__)) {
+ uint16_t empty_1; /* 00 00 */
+ uint32_t dialog; /* constant dialog id, from previous response */
+ char user_host[64]; /* unicode user <space> <space> hostname */
+ uint32_t unknown_1; /* 00 00 00 14 */
+ uint32_t empty_2[5]; /* only 0 */
+ uint32_t unknown_2; /* 00 00 00 10 */
+ char ascii_date[16]; /* YYYYMMDDHHMMSS only for type 2 */
+ } type2; /* length = 116 */
+
+ struct __attribute__ ((__packed__)) {
+ uint16_t empty_1; /* 00 00 */
+ uint32_t dialog; /* constant dialog id, from previous response */
+ char user_host[64]; /* unicode user <space> <space> hostname */
+ uint32_t unknown_1; /* 00 00 00 14 */
+ uint32_t key; /* copied from key field in status msg */
+ uint32_t unknown_3[5]; /* only 0 */
+ } type5; /* length = 100 */
+
+ } extensions;
+};
+
+/* the poll response layout */
+
+struct __attribute__ ((__packed__)) POLL_RESPONSE
+{
+ struct BJNP_command cmd; /* command header */
+
+ unsigned char result[4]; /* unknown stuff, result[2] = 80 -> status is available*/
+ /* result[8] is dialog, size? */
+ uint32_t dialog; /* to be returned in next request */
+ uint32_t unknown_2; /* returns the 00 00 00 14 from unknown_2 in request */
+ uint32_t key; /* to be returned in type 5 status reset */
+ unsigned char status[20]; /* interrupt status */
+};
+
+/* Layout of ID and status responses */
+
+struct __attribute__ ((__packed__)) IDENTITY
+{
+ struct BJNP_command cmd;
+ uint16_t id_len; /* length of identity */
+ char id[BJNP_IEEE1284_MAX]; /* identity */
+};
+
+
+/* response to TCP print command */
+
+struct __attribute__ ((__packed__)) SCAN_BUF
+{
+ struct BJNP_command cmd;
+ char scan_data[65536];
+};
+
+/**************************/
+/* Local enum definitions */
+/**************************/
+
+typedef enum bjnp_paper_status_e
+{
+ BJNP_PAPER_UNKNOWN = -1,
+ BJNP_PAPER_OK = 0,
+ BJNP_PAPER_OUT = 1
+} bjnp_paper_status_t;
+
+typedef enum
+{
+ BJNP_STATUS_GOOD,
+ BJNP_STATUS_INVAL,
+ BJNP_STATUS_ALREADY_ALLOCATED
+} BJNP_Status;
+
+/* button polling */
+
+typedef enum
+{
+ BJNP_POLL_STOPPED = 0,
+ BJNP_POLL_STARTED = 1,
+ BJNP_POLL_STATUS_RECEIVED = 2
+} BJNP_polling_status_e;
+
+typedef union
+{
+ struct sockaddr_storage storage;
+ struct sockaddr addr;
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+} bjnp_sockaddr_t;
+
+typedef enum
+{
+ BJNP_ADDRESS_IS_LINK_LOCAL = 0,
+ BJNP_ADDRESS_IS_GLOBAL = 1,
+ BJNP_ADDRESS_HAS_FQDN = 2
+} bjnp_address_type_t;
+
+
+/*
+ * Device information for opened devices
+ */
+
+typedef struct device_s
+{
+ int open; /* connection to scanner is opened */
+
+ /* sockets */
+
+ int tcp_socket; /* open tcp socket for communcation to scannner */
+ int16_t serial; /* sequence number of command */
+
+ /* communication state */
+
+ int session_id; /* session id used in bjnp protocol for TCP packets */
+ int last_cmd; /* last command sent */
+
+ /* TCP bulk read state information */
+
+ size_t blocksize; /* size of (TCP) blocks returned by the scanner */
+ size_t scanner_data_left; /* TCP data left from last read request */
+ char last_block; /* last TCP read command was shorter than blocksize */
+
+ /* device information */
+ char mac_address[BJNP_HOST_MAX];
+ /* mac-address, used as device serial no */
+ bjnp_sockaddr_t * addr; /* ip-address of the scanner */
+ int address_level; /* link local, public or has a FQDN */
+ int bjnp_timeout; /* timeout (msec) for next poll command */
+
+#ifdef PIXMA_BJNP_USE_STATUS
+ /* polling state information */
+
+ char polling_status; /* status polling ongoing */
+ uint32_t dialog; /* poll dialog */
+ uint32_t status_key; /* key of last received status message */
+#endif
+} bjnp_device_t;
+
diff --git a/backend/pixma_common.c b/backend/pixma_common.c
new file mode 100644
index 0000000..b417d07
--- /dev/null
+++ b/backend/pixma_common.c
@@ -0,0 +1,1172 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h> /* pow(C90) */
+
+#include <sys/time.h> /* gettimeofday(4.3BSD) */
+#include <unistd.h> /* usleep */
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+extern const pixma_config_t pixma_mp150_devices[];
+extern const pixma_config_t pixma_mp750_devices[];
+extern const pixma_config_t pixma_mp730_devices[];
+extern const pixma_config_t pixma_mp810_devices[];
+extern const pixma_config_t pixma_iclass_devices[];
+
+static const pixma_config_t *const pixma_devices[] = {
+ pixma_mp150_devices,
+ pixma_mp750_devices,
+ pixma_mp730_devices,
+ pixma_mp810_devices,
+ pixma_iclass_devices,
+ NULL
+};
+
+static pixma_t *first_pixma = NULL;
+static time_t tstart_sec = 0;
+static uint32_t tstart_usec = 0;
+static int debug_level = 1;
+
+
+#ifndef NDEBUG
+
+static void
+u8tohex (uint8_t x, char *str)
+{
+ static const char hdigit[16] =
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+ 'e', 'f'
+ };
+ str[0] = hdigit[(x >> 4) & 0xf];
+ str[1] = hdigit[x & 0xf];
+ str[2] = '\0';
+}
+
+static void
+u32tohex (uint32_t x, char *str)
+{
+ u8tohex (x >> 24, str);
+ u8tohex (x >> 16, str + 2);
+ u8tohex (x >> 8, str + 4);
+ u8tohex (x, str + 6);
+}
+
+void
+pixma_hexdump (int level, const void *d_, unsigned len)
+{
+ const uint8_t *d = (const uint8_t *) (d_);
+ unsigned ofs, c, plen;
+ char line[100]; /* actually only 1+8+1+8*3+1+8*3+1 = 61 bytes needed */
+
+ if (level > debug_level)
+ return;
+ if (level == debug_level)
+ /* if debuglevel == exact match and buffer contains more than 3 lines, print 2 lines + .... */
+ plen = (len > 64) ? 32: len;
+ else
+ plen = len;
+ ofs = 0;
+ while (ofs < plen)
+ {
+ char *p;
+ line[0] = ' ';
+ u32tohex (ofs, line + 1);
+ line[9] = ':';
+ p = line + 10;
+ for (c = 0; c != 16 && (ofs + c) < plen; c++)
+ {
+ u8tohex (d[ofs + c], p);
+ p[2] = ' ';
+ p += 3;
+ if (c == 7)
+ {
+ p[0] = ' ';
+ p++;
+ }
+ }
+ p[0] = '\0';
+ pixma_dbg (level, "%s\n", line);
+ ofs += c;
+ }
+ if (len > plen)
+ pixma_dbg(level, "......\n");
+}
+
+static void
+time2str (char *buf, unsigned size)
+{
+ time_t sec;
+ uint32_t usec;
+
+ pixma_get_time (&sec, &usec);
+ sec -= tstart_sec;
+ if (usec >= tstart_usec)
+ {
+ usec -= tstart_usec;
+ }
+ else
+ {
+ usec = 1000000 + usec - tstart_usec;
+ sec--;
+ }
+ snprintf (buf, size, "%lu.%03u", (unsigned long) sec,
+ (unsigned) (usec / 1000));
+}
+
+void
+pixma_dump (int level, const char *type, const void *data, int len,
+ int size, int max)
+{
+ int actual_len, print_len;
+ char buf[20];
+
+ if (level > debug_level)
+ return;
+ if (debug_level >= 20)
+ max = -1; /* dump every bytes */
+
+ time2str (buf, sizeof (buf));
+ pixma_dbg (level, "%s T=%s len=%d\n", type, buf, len);
+
+ actual_len = (size >= 0) ? size : len;
+ print_len = (max >= 0 && max < actual_len) ? max : actual_len;
+ if (print_len >= 0)
+ {
+ pixma_hexdump (level, data, print_len);
+ if (print_len < actual_len)
+ pixma_dbg (level, " ...\n");
+ }
+ if (len < 0)
+ pixma_dbg (level, " ERROR: %s\n", pixma_strerror (len));
+ pixma_dbg (level, "\n");
+}
+
+
+#endif /* NDEBUG */
+
+/* NOTE: non-reentrant */
+const char *
+pixma_strerror (int error)
+{
+ static char buf[50];
+
+ /* TODO: more human friendly messages */
+ switch (error)
+ {
+ case PIXMA_EIO:
+ return "EIO";
+ case PIXMA_ENODEV:
+ return "ENODEV";
+ case PIXMA_EACCES:
+ return "EACCES";
+ case PIXMA_ENOMEM:
+ return "ENOMEM";
+ case PIXMA_EINVAL:
+ return "EINVAL";
+ case PIXMA_EBUSY:
+ return "EBUSY";
+ case PIXMA_ECANCELED:
+ return "ECANCELED";
+ case PIXMA_ENOTSUP:
+ return "ENOTSUP";
+ case PIXMA_ETIMEDOUT:
+ return "ETIMEDOUT";
+ case PIXMA_EPROTO:
+ return "EPROTO";
+ case PIXMA_EPAPER_JAMMED:
+ return "EPAPER_JAMMED";
+ case PIXMA_ECOVER_OPEN:
+ return "ECOVER_OPEN";
+ case PIXMA_ENO_PAPER:
+ return "ENO_PAPER";
+ case PIXMA_EOF:
+ return "EEOF";
+ }
+ snprintf (buf, sizeof (buf), "EUNKNOWN:%d", error);
+ return buf;
+}
+
+void
+pixma_set_debug_level (int level)
+{
+ debug_level = level;
+}
+
+void
+pixma_set_be16 (uint16_t x, uint8_t * buf)
+{
+ buf[0] = x >> 8;
+ buf[1] = x;
+}
+
+void
+pixma_set_be32 (uint32_t x, uint8_t * buf)
+{
+ buf[0] = x >> 24;
+ buf[1] = x >> 16;
+ buf[2] = x >> 8;
+ buf[3] = x;
+}
+
+uint16_t
+pixma_get_be16 (const uint8_t * buf)
+{
+ return ((uint16_t) buf[0] << 8) | buf[1];
+}
+
+uint32_t
+pixma_get_be32 (const uint8_t * buf)
+{
+ return ((uint32_t) buf[0] << 24) + ((uint32_t) buf[1] << 16) +
+ ((uint32_t) buf[2] << 8) + buf[3];
+}
+
+uint8_t
+pixma_sum_bytes (const void *data, unsigned len)
+{
+ const uint8_t *d = (const uint8_t *) data;
+ unsigned i, sum = 0;
+ for (i = 0; i != len; i++)
+ sum += d[i];
+ return sum;
+}
+
+void
+pixma_sleep (unsigned long usec)
+{
+ usleep (usec);
+}
+
+void
+pixma_get_time (time_t * sec, uint32_t * usec)
+{
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+ if (sec)
+ *sec = tv.tv_sec;
+ if (usec)
+ *usec = tv.tv_usec;
+}
+
+/* convert 24/48 bit RGB to 8/16 bit ir
+ *
+ * Formular: g = R
+ * drop G + B
+ *
+ * sptr: source color scale buffer
+ * gptr: destination gray scale buffer
+ * c == 3: 24 bit RGB -> 8 bit ir
+ * c == 6: 48 bit RGB -> 16 bit ir
+ */
+uint8_t *
+pixma_r_to_ir (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c)
+{
+ unsigned i, j, g;
+
+ /* PDBG (pixma_dbg (4, "*pixma_rgb_to_ir*****\n")); */
+
+ for (i = 0; i < w; i++)
+ {
+ *gptr++ = *sptr++;
+ if (c == 6) *gptr++ = *sptr++; /* 48 bit RGB: high byte */
+ sptr += (c == 6) ? 4 : 2; /* drop G + B */
+ }
+ return gptr;
+}
+
+/* convert 24/48 bit RGB to 8/16 bit grayscale
+ *
+ * Formular: g = (R + G + B) / 3
+ *
+ * sptr: source color scale buffer
+ * gptr: destination gray scale buffer
+ * c == 3: 24 bit RGB -> 8 bit gray
+ * c == 6: 48 bit RGB -> 16 bit gray
+ */
+uint8_t *
+pixma_rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c)
+{
+ unsigned i, j, g;
+
+ /* PDBG (pixma_dbg (4, "*pixma_rgb_to_gray*****\n")); */
+
+ for (i = 0; i < w; i++)
+ {
+ for (j = 0, g = 0; j < 3; j++)
+ {
+ g += *sptr++;
+ if (c == 6) g += (*sptr++ << 8); /* 48 bit RGB: high byte */
+ }
+
+ g /= 3; /* 8 or 16 bit gray */
+ *gptr++ = g;
+ if (c == 6) *gptr++ = (g >> 8); /* 16 bit gray: high byte */
+ }
+ return gptr;
+}
+
+/**
+ * This code was taken from the genesys backend
+ * uses threshold and threshold_curve to control software binarization
+ * @param sp device set up for the scan
+ * @param dst pointer where to store result
+ * @param src pointer to raw data
+ * @param width width of the processed line
+ * @param c 1 for 1-channel single-byte data,
+ * 3 for 3-channel single-byte data,
+ * 6 for double-byte data
+ * */
+uint8_t *
+pixma_binarize_line(pixma_scan_param_t * sp, uint8_t * dst, uint8_t * src, unsigned width, unsigned c)
+{
+ unsigned j, x, windowX, sum = 0;
+ unsigned threshold;
+ unsigned offset, addCol;
+ int dropCol, offsetX;
+ unsigned char mask;
+ uint8_t min, max;
+
+ /* PDBG (pixma_dbg (4, "*pixma_binarize_line***** src = %u, dst = %u, width = %u, c = %u, threshold = %u, thershold_curve = %u *****\n",
+ src, dst, width, c, sp->threshold, sp->threshold_curve)); */
+
+ /* 16 bit grayscale not supported */
+ if (c == 6)
+ {
+ PDBG (pixma_dbg (1, "*pixma_binarize_line***** Error: 16 bit grayscale not supported\n"));
+ return dst;
+ }
+
+ /* first, color convert to grayscale */
+ if (c != 1)
+ pixma_rgb_to_gray(dst, src, width, c);
+
+ /* second, normalize line */
+ min = 255;
+ max = 0;
+ for (x = 0; x < width; x++)
+ {
+ if (src[x] > max)
+ {
+ max = src[x];
+ }
+ if (src[x] < min)
+ {
+ min = src[x];
+ }
+ }
+
+ /* safeguard against dark or white areas */
+ if(min>80)
+ min=0;
+ if(max<80)
+ max=255;
+ for (x = 0; x < width; x++)
+ {
+ src[x] = ((src[x] - min) * 255) / (max - min);
+ }
+
+ /* third, create sliding window, prefill the sliding sum */
+ /* ~1mm works best, but the window needs to have odd # of pixels */
+ windowX = (6 * sp->xdpi) / 150;
+ if (!(windowX % 2))
+ windowX++;
+
+ /* to avoid conflicts with *dst start with offset */
+ offsetX = 1 + (windowX / 2) / 8;
+ for (j = offsetX; j <= windowX; j++)
+ sum += src[j];
+ /* PDBG (pixma_dbg (4, " *pixma_binarize_line***** windowX = %u, startX = %u, sum = %u\n",
+ windowX, startX, sum)); */
+
+ /* fourth, walk the input buffer, output bits */
+ for (j = 0; j < width; j++)
+ {
+ /* output image location */
+ offset = j % 8;
+ mask = 0x80 >> offset;
+ threshold = sp->threshold;
+
+ /* move sum/update threshold only if there is a curve */
+ if (sp->threshold_curve)
+ {
+ addCol = j + windowX / 2;
+ dropCol = addCol - windowX;
+
+ if (dropCol >= offsetX && addCol < width)
+ {
+ sum += src[addCol];
+ sum -= (sum < src[dropCol] ? sum : src[dropCol]); /* no negative sum */
+ }
+ threshold = sp->lineart_lut[sum / windowX];
+ /* PDBG (pixma_dbg (4, " *pixma_binarize_line***** addCol = %u, dropCol = %d, sum = %u, windowX = %u, lut-element = %d, threshold = %u\n",
+ addCol, dropCol, sum, windowX, sum/windowX, threshold)); */
+ }
+
+ /* lookup threshold */
+ if (src[j] > threshold)
+ *dst &= ~mask; /* white */
+ else
+ *dst |= mask; /* black */
+
+ if (offset == 7)
+ dst++;
+ }
+
+ /* PDBG (pixma_dbg (4, " *pixma_binarize_line***** ready: src = %u, dst = %u *****\n", src, dst)); */
+
+ return dst;
+}
+
+/**
+ This code was taken from the genesys backend
+ Function to build a lookup table (LUT), often
+ used by scanners to implement brightness/contrast/gamma
+ or by backends to speed binarization/thresholding
+
+ offset and slope inputs are -127 to +127
+
+ slope rotates line around central input/output val,
+ 0 makes horizontal line
+
+ pos zero neg
+ . x . . x
+ . x . . x
+ out . x .xxxxxxxxxxx . x
+ . x . . x
+ ....x....... ............ .......x....
+ in in in
+
+ offset moves line vertically, and clamps to output range
+ 0 keeps the line crossing the center of the table
+
+ high low
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+
+ out_min/max provide bounds on output values,
+ useful when building thresholding lut.
+ 0 and 255 are good defaults otherwise.
+ * */
+static SANE_Status
+load_lut (unsigned char * lut,
+ int in_bits, int out_bits,
+ int out_min, int out_max,
+ int slope, int offset)
+{
+ int i, j;
+ double shift, rise;
+ int max_in_val = (1 << in_bits) - 1;
+ int max_out_val = (1 << out_bits) - 1;
+ unsigned char * lut_p = lut;
+
+ /* PDBG (pixma_dbg (4, "*load_lut***** start %d %d *****\n", slope, offset)); */
+
+ /* slope is converted to rise per unit run:
+ * first [-127,127] to [-1,1]
+ * then multiply by PI/2 to convert to radians
+ * then take the tangent (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ rise = tan((double)slope/127 * M_PI/2) * max_out_val / max_in_val;
+
+ /* line must stay vertically centered, so figure
+ * out vertical offset at central input value */
+ shift = (double)max_out_val/2 - (rise*max_in_val/2);
+
+ /* convert the user offset setting to scale of output
+ * first [-127,127] to [-1,1]
+ * then to [-max_out_val/2,max_out_val/2]*/
+ shift += (double)offset / 127 * max_out_val / 2;
+
+ for(i=0;i<=max_in_val;i++){
+ j = rise*i + shift;
+
+ if(j<out_min){
+ j=out_min;
+ }
+ else if(j>out_max){
+ j=out_max;
+ }
+
+ *lut_p=j;
+ lut_p++;
+ }
+
+ /* PDBG (pixma_dbg (4, "*load_lut***** finish *****\n")); */
+ /* PDBG (pixma_hexdump (4, lut, max_in_val+1)); */
+
+ return SANE_STATUS_GOOD;
+}
+
+int
+pixma_map_status_errno (unsigned status)
+{
+ switch (status)
+ {
+ case PIXMA_STATUS_OK:
+ return 0;
+ case PIXMA_STATUS_FAILED:
+ return PIXMA_ECANCELED;
+ case PIXMA_STATUS_BUSY:
+ return PIXMA_EBUSY;
+ default:
+ return PIXMA_EPROTO;
+ }
+}
+
+int
+pixma_check_result (pixma_cmdbuf_t * cb)
+{
+ const uint8_t *r = cb->buf;
+ unsigned header_len = cb->res_header_len;
+ unsigned expected_reslen = cb->expected_reslen;
+ int error;
+ unsigned len;
+
+ if (cb->reslen < 0)
+ return cb->reslen;
+
+ len = (unsigned) cb->reslen;
+ if (len >= header_len)
+ {
+ error = pixma_map_status_errno (pixma_get_be16 (r));
+ if (expected_reslen != 0)
+ {
+ if (len == expected_reslen)
+ {
+ if (pixma_sum_bytes (r + header_len, len - header_len) != 0)
+ error = PIXMA_EPROTO;
+ }
+ else
+ {
+ /* This case will happen when a command cannot be completely
+ executed, e.g. because you press the cancel button. The
+ device will return only a header with PIXMA_STATUS_FAILED. */
+ if (len != header_len)
+ error = PIXMA_EPROTO;
+ }
+ }
+ }
+ else
+ error = PIXMA_EPROTO;
+
+#ifndef NDEBUG
+ if (error == PIXMA_EPROTO)
+ {
+ pixma_dbg (1, "WARNING: result len=%d expected %d\n",
+ len, cb->expected_reslen);
+ pixma_hexdump (1, r, MIN (len, 64));
+ }
+#endif
+ return error;
+}
+
+int
+pixma_cmd_transaction (pixma_t * s, const void *cmd, unsigned cmdlen,
+ void *data, unsigned expected_len)
+{
+ int error, tmo;
+
+ error = pixma_write (s->io, cmd, cmdlen);
+ if (error != (int) cmdlen)
+ {
+ if (error >= 0)
+ {
+ /* Write timeout is too low? */
+ PDBG (pixma_dbg
+ (1, "ERROR: incomplete write, %u out of %u written\n",
+ (unsigned) error, cmdlen));
+ error = PIXMA_ETIMEDOUT;
+ }
+ return error;
+ }
+
+ /* When you send the start_session command while the scanner optic is
+ going back to the home position after the last scan session has been
+ cancelled, you won't get the response before it arrives home. This takes
+ about 5 seconds. If the last session was succeeded, the scanner will
+ immediatly answer with PIXMA_STATUS_BUSY.
+
+ Is 8 seconds timeout enough? This affects ALL commands that use
+ pixma_cmd_transaction(). */
+ tmo = 8;
+ do
+ {
+ error = pixma_read (s->io, data, expected_len);
+ if (error == PIXMA_ETIMEDOUT)
+ PDBG (pixma_dbg (2, "No response yet. Timed out in %d sec.\n", tmo));
+ }
+ while (error == PIXMA_ETIMEDOUT && --tmo != 0);
+ if (error < 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING: Error in response phase. cmd:%02x%02x\n",
+ ((const uint8_t *) cmd)[0],
+ ((const uint8_t *) cmd)[1]));
+ PDBG (pixma_dbg (1," If the scanner hangs, reset it and/or unplug the "
+ "USB cable.\n"));
+ }
+ return error; /* length of the result packet or error */
+}
+
+uint8_t *
+pixma_newcmd (pixma_cmdbuf_t * cb, unsigned cmd,
+ unsigned dataout, unsigned datain)
+{
+ unsigned cmdlen = cb->cmd_header_len + dataout;
+ unsigned reslen = cb->res_header_len + datain;
+
+ if (cmdlen > cb->size || reslen > cb->size)
+ return NULL;
+ memset (cb->buf, 0, cmdlen);
+ cb->cmdlen = cmdlen;
+ cb->expected_reslen = reslen;
+ pixma_set_be16 (cmd, cb->buf);
+ pixma_set_be16 (dataout + datain, cb->buf + cb->cmd_len_field_ofs);
+ if (dataout != 0)
+ return cb->buf + cb->cmd_header_len;
+ else
+ return cb->buf + cb->res_header_len;
+}
+
+int
+pixma_exec (pixma_t * s, pixma_cmdbuf_t * cb)
+{
+ if (cb->cmdlen > cb->cmd_header_len)
+ pixma_fill_checksum (cb->buf + cb->cmd_header_len,
+ cb->buf + cb->cmdlen - 1);
+ cb->reslen =
+ pixma_cmd_transaction (s, cb->buf, cb->cmdlen, cb->buf,
+ cb->expected_reslen);
+ return pixma_check_result (cb);
+}
+
+int
+pixma_exec_short_cmd (pixma_t * s, pixma_cmdbuf_t * cb, unsigned cmd)
+{
+ pixma_newcmd (cb, cmd, 0, 0);
+ return pixma_exec (s, cb);
+}
+
+int
+pixma_check_dpi (unsigned dpi, unsigned max)
+{
+ /* valid dpi = 75 * 2^n */
+ unsigned temp = dpi / 75;
+ if (dpi > max || dpi < 75 || 75 * temp != dpi || (temp & (temp - 1)) != 0)
+ return PIXMA_EINVAL;
+ return 0;
+}
+
+
+int
+pixma_init (void)
+{
+ PDBG (pixma_dbg (2, "pixma version %d.%d.%d\n", PIXMA_VERSION_MAJOR,
+ PIXMA_VERSION_MINOR, PIXMA_VERSION_BUILD));
+ PASSERT (first_pixma == NULL);
+ if (tstart_sec == 0)
+ pixma_get_time (&tstart_sec, &tstart_usec);
+ return pixma_io_init ();
+}
+
+void
+pixma_cleanup (void)
+{
+ while (first_pixma)
+ pixma_close (first_pixma);
+ pixma_io_cleanup ();
+}
+
+int
+pixma_open (unsigned devnr, pixma_t ** handle)
+{
+ int error;
+ pixma_t *s;
+ const pixma_config_t *cfg;
+
+ *handle = NULL;
+ cfg = pixma_get_device_config (devnr);
+ if (!cfg)
+ return PIXMA_EINVAL; /* invalid devnr */
+ PDBG (pixma_dbg (2, "pixma_open(): %s\n", cfg->name));
+
+ s = (pixma_t *) calloc (1, sizeof (s[0]));
+ if (!s)
+ return PIXMA_ENOMEM;
+ s->next = first_pixma;
+ first_pixma = s;
+
+ s->cfg = cfg;
+ error = pixma_connect (devnr, &s->io);
+ if (error < 0)
+ {
+ PDBG (pixma_dbg
+ (2, "pixma_connect() failed %s\n", pixma_strerror (error)));
+ goto rollback;
+ }
+ strncpy (s->id, pixma_get_device_id (devnr), sizeof (s->id) - 1);
+ s->ops = s->cfg->ops;
+ s->scanning = 0;
+ error = s->ops->open (s);
+ if (error < 0)
+ goto rollback;
+ error = pixma_deactivate (s->io);
+ if (error < 0)
+ goto rollback;
+ *handle = s;
+ return 0;
+
+rollback:
+ PDBG (pixma_dbg (2, "pixma_open() failed %s\n", pixma_strerror (error)));
+ pixma_close (s);
+ return error;
+}
+
+void
+pixma_close (pixma_t * s)
+{
+ pixma_t **p;
+
+ if (!s)
+ return;
+ for (p = &first_pixma; *p && *p != s; p = &((*p)->next))
+ {
+ }
+ PASSERT (*p);
+ if (!(*p))
+ return;
+ PDBG (pixma_dbg (2, "pixma_close(): %s\n", s->cfg->name));
+ if (s->io)
+ {
+ if (s->scanning)
+ {
+ PDBG (pixma_dbg (3, "pixma_close(): scanning in progress, call"
+ " finish_scan()\n"));
+ s->ops->finish_scan (s);
+ }
+ s->ops->close (s);
+ pixma_disconnect (s->io);
+ }
+ *p = s->next;
+ free (s);
+}
+
+int
+pixma_scan (pixma_t * s, pixma_scan_param_t * sp)
+{
+ int error;
+
+ error = pixma_check_scan_param (s, sp);
+ if (error < 0)
+ return error;
+
+ if (sp->mode == PIXMA_SCAN_MODE_LINEART)
+ {
+ load_lut(sp->lineart_lut, 8, 8, 50, 205,
+ sp->threshold_curve, sp->threshold-127);
+ }
+
+#ifndef NDEBUG
+ pixma_dbg (3, "\n");
+ pixma_dbg (3, "pixma_scan(): start\n");
+ pixma_dbg (3, " line_size=%"PRIu64" image_size=%"PRIu64" channels=%u depth=%u\n",
+ sp->line_size, sp->image_size, sp->channels, sp->depth);
+ pixma_dbg (3, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n",
+ sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h);
+ pixma_dbg (3, " gamma_table=%p source=%d\n", sp->gamma_table, sp->source);
+ pixma_dbg (3, " threshold=%d threshold_curve=%d\n", sp->threshold, sp->threshold_curve);
+ pixma_dbg (3, " ADF page count: %d\n", sp->adf_pageid);
+#endif
+
+ s->param = sp;
+ s->cancel = 0;
+ s->cur_image_size = 0;
+ s->imagebuf.wptr = NULL;
+ s->imagebuf.wend = NULL;
+ s->imagebuf.rptr = NULL;
+ s->imagebuf.rend = NULL;
+ s->underrun = 0;
+ error = s->ops->scan (s);
+ if (error >= 0)
+ {
+ s->scanning = 1;
+ }
+ else
+ {
+ PDBG (pixma_dbg
+ (3, "pixma_scan() failed %s\n", pixma_strerror (error)));
+ }
+
+ return error;
+}
+
+static uint8_t *
+fill_pixels (pixma_t * s, uint8_t * ptr, uint8_t * end, uint8_t value)
+{
+ if (s->cur_image_size < s->param->image_size)
+ {
+ long n = s->param->image_size - s->cur_image_size;
+ if (n > (end - ptr))
+ n = end - ptr;
+ memset (ptr, value, n);
+ s->cur_image_size += n;
+ ptr += n;
+ }
+ return ptr;
+}
+
+int
+pixma_read_image (pixma_t * s, void *buf, unsigned len)
+{
+ int result;
+ pixma_imagebuf_t ib;
+
+ if (!s->scanning)
+ return 0;
+ if (s->cancel)
+ {
+ result = PIXMA_ECANCELED;
+ goto cancel;
+ }
+
+ ib = s->imagebuf; /* get rptr and rend */
+ ib.wptr = (uint8_t *) buf;
+ ib.wend = ib.wptr + len;
+
+ if (s->underrun)
+ {
+ if (s->cur_image_size < s->param->image_size)
+ {
+ ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff);
+ }
+ else
+ {
+ PDBG (pixma_dbg
+ (3, "pixma_read_image(): completed (underrun detected)\n"));
+ s->scanning = 0;
+ }
+ return ib.wptr - (uint8_t *) buf;
+ }
+
+ while (ib.wptr != ib.wend)
+ {
+ if (ib.rptr == ib.rend)
+ {
+ ib.rptr = ib.rend = NULL;
+ result = s->ops->fill_buffer (s, &ib);
+ if (result < 0)
+ goto cancel;
+ if (result == 0)
+ { /* end of image? */
+ s->ops->finish_scan (s);
+ if (s->cur_image_size != s->param->image_size)
+ {
+ pixma_dbg (1, "WARNING:image size mismatches\n");
+ pixma_dbg (1,
+ " %"PRIu64" expected (%d lines) but %"PRIu64" received (%"PRIu64" lines)\n",
+ s->param->image_size, s->param->h,
+ s->cur_image_size,
+ s->cur_image_size / s->param->line_size);
+ if ((s->cur_image_size % s->param->line_size) != 0)
+ {
+ pixma_dbg (1,
+ "BUG:received data not multiple of line_size\n");
+ }
+ }
+ if (s->cur_image_size < s->param->image_size)
+ {
+ s->underrun = 1;
+ ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff);
+ }
+ else
+ {
+ PDBG (pixma_dbg (3, "pixma_read_image():completed\n"));
+ s->scanning = 0;
+ }
+ break;
+ }
+ s->cur_image_size += result;
+
+ PASSERT (s->cur_image_size <= s->param->image_size);
+ }
+ if (ib.rptr)
+ {
+ unsigned count = MIN (ib.rend - ib.rptr, ib.wend - ib.wptr);
+ memcpy (ib.wptr, ib.rptr, count);
+ ib.rptr += count;
+ ib.wptr += count;
+ }
+ }
+ s->imagebuf = ib; /* store rptr and rend */
+ return ib.wptr - (uint8_t *) buf;
+
+cancel:
+ s->ops->finish_scan (s);
+ s->scanning = 0;
+ if (result == PIXMA_ECANCELED)
+ {
+ PDBG (pixma_dbg (3, "pixma_read_image(): cancelled by %sware\n",
+ (s->cancel) ? "soft" : "hard"));
+ }
+ else
+ {
+ PDBG (pixma_dbg (3, "pixma_read_image() failed %s\n",
+ pixma_strerror (result)));
+ }
+ return result;
+}
+
+void
+pixma_cancel (pixma_t * s)
+{
+ s->cancel = 1;
+}
+
+int
+pixma_enable_background (pixma_t * s, int enabled)
+{
+ return pixma_set_interrupt_mode (s->io, enabled);
+}
+
+int
+pixma_activate_connection(pixma_t * s)
+{
+ return pixma_activate (s->io);
+}
+
+int
+pixma_deactivate_connection(pixma_t * s)
+{
+ return pixma_deactivate (s->io);
+}
+
+uint32_t
+pixma_wait_event (pixma_t * s, int timeout /*ms */ )
+{
+ unsigned events;
+
+ if (s->events == PIXMA_EV_NONE && s->ops->wait_event)
+ s->ops->wait_event (s, timeout);
+ events = s->events;
+ s->events = PIXMA_EV_NONE;
+ return events;
+}
+
+#define CLAMP2(x,w,min,max,dpi) do { \
+ unsigned m = (max) * (dpi) / 75; \
+ x = MIN(x, m - min); \
+ w = MIN(w, m - x); \
+ if (w < min) w = min; \
+} while(0)
+
+int
+pixma_check_scan_param (pixma_t * s, pixma_scan_param_t * sp)
+{
+ unsigned cfg_xdpi;
+
+ if (!(sp->channels == 3 ||
+ (sp->channels == 1 && (s->cfg->cap & PIXMA_CAP_GRAY) != 0)))
+ return PIXMA_EINVAL;
+
+ /* flatbed: use s->cfg->xdpi
+ * TPU/ADF: use s->cfg->adftpu_max_dpi, if configured with dpi value */
+ cfg_xdpi = ((sp->source == PIXMA_SOURCE_FLATBED
+ || s->cfg->adftpu_max_dpi == 0) ? s->cfg->xdpi
+ : s->cfg->adftpu_max_dpi);
+
+ if (pixma_check_dpi (sp->xdpi, cfg_xdpi) < 0 ||
+ pixma_check_dpi (sp->ydpi, s->cfg->ydpi) < 0)
+ return PIXMA_EINVAL;
+
+ /* xdpi must be equal to ydpi except that
+ xdpi = max_xdpi and ydpi = max_ydpi. */
+ if (!(sp->xdpi == sp->ydpi ||
+ (sp->xdpi == cfg_xdpi && sp->ydpi == s->cfg->ydpi)))
+ return PIXMA_EINVAL;
+
+ if (s->ops->check_param (s, sp) < 0)
+ return PIXMA_EINVAL;
+
+ /* FIXME: I assume the same minimum width and height for every model. */
+ CLAMP2 (sp->x, sp->w, 13, s->cfg->width, sp->xdpi);
+ CLAMP2 (sp->y, sp->h, 8, s->cfg->height, sp->ydpi);
+
+ switch (sp->source)
+ {
+ case PIXMA_SOURCE_FLATBED:
+ break;
+
+ case PIXMA_SOURCE_TPU:
+ if ((s->cfg->cap & PIXMA_CAP_TPU) != PIXMA_CAP_TPU)
+ {
+ sp->source = PIXMA_SOURCE_FLATBED;
+ PDBG (pixma_dbg
+ (1, "WARNING: TPU unsupported, fallback to flatbed.\n"));
+ }
+ break;
+
+ case PIXMA_SOURCE_ADF:
+ if ((s->cfg->cap & PIXMA_CAP_ADF) != PIXMA_CAP_ADF)
+ {
+ sp->source = PIXMA_SOURCE_FLATBED;
+ PDBG (pixma_dbg
+ (1, "WARNING: ADF unsupported, fallback to flatbed.\n"));
+ }
+ break;
+
+ case PIXMA_SOURCE_ADFDUP:
+ if ((s->cfg->cap & PIXMA_CAP_ADFDUP) != PIXMA_CAP_ADFDUP)
+ {
+ if (s->cfg->cap & PIXMA_CAP_ADF)
+ {
+ sp->source = PIXMA_SOURCE_ADF;
+ }
+ else
+ {
+ sp->source = PIXMA_SOURCE_FLATBED;
+ }
+ PDBG (pixma_dbg
+ (1, "WARNING: ADF duplex unsupported, fallback to %d.\n",
+ sp->source));
+ }
+ break;
+ }
+
+ if (sp->depth == 0)
+ sp->depth = 8;
+ if ((sp->depth % 8) != 0 && sp->depth != 1)
+ return PIXMA_EINVAL;
+
+ sp->line_size = 0;
+
+ if (s->ops->check_param (s, sp) < 0)
+ return PIXMA_EINVAL;
+
+ if (sp->line_size == 0)
+ sp->line_size = sp->depth / 8 * sp->channels * sp->w;
+ sp->image_size = sp->line_size * sp->h;
+
+ /* image_size for software lineart is counted in bits */
+ if (sp->software_lineart == 1)
+ sp->image_size /= 8;
+ return 0;
+}
+
+const char *
+pixma_get_string (pixma_t * s, pixma_string_index_t i)
+{
+ switch (i)
+ {
+ case PIXMA_STRING_MODEL:
+ return s->cfg->name;
+ case PIXMA_STRING_ID:
+ return s->id;
+ case PIXMA_STRING_LAST:
+ return NULL;
+ }
+ return NULL;
+}
+
+const pixma_config_t *
+pixma_get_config (pixma_t * s)
+{
+ return s->cfg;
+}
+
+void
+pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n)
+{
+ int i;
+ double r_gamma = 1.0 / gamma;
+ double out_scale = 255.0;
+ double in_scale = 1.0 / (n - 1);
+
+ for (i = 0; (unsigned) i != n; i++)
+ {
+ table[i] = (int) (out_scale * pow (i * in_scale, r_gamma) + 0.5);
+ }
+}
+
+int
+pixma_find_scanners (const char **conf_devices)
+{
+ return pixma_collect_devices (conf_devices, pixma_devices);
+}
+
+const char *
+pixma_get_device_model (unsigned devnr)
+{
+ const pixma_config_t *cfg = pixma_get_device_config (devnr);
+ return (cfg) ? cfg->name : NULL;
+}
+
+
+int
+pixma_get_device_status (pixma_t * s, pixma_device_status_t * status)
+{
+ if (!status)
+ return PIXMA_EINVAL;
+ memset (status, 0, sizeof (*status));
+ return s->ops->get_status (s, status);
+}
diff --git a/backend/pixma_common.h b/backend/pixma_common.h
new file mode 100644
index 0000000..86d459f
--- /dev/null
+++ b/backend/pixma_common.h
@@ -0,0 +1,231 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#ifndef PIXMA_COMMON_H
+#define PIXMA_COMMON_H
+
+
+#include <time.h> /* time_t */
+#include "pixma.h"
+
+
+/*! \defgroup subdriver Subdriver Interface
+ * \brief Subdriver interface. */
+
+/*! \defgroup debug Debug utilities
+ * \brief Debug utilities. */
+
+#ifdef NDEBUG
+# define PDBG(x) do {} while(0)
+# define PASSERT(x) do {} while(0)
+#else
+# define PDBG(x) x
+# define PASSERT(x) do { \
+ if (!(x)) \
+ pixma_dbg(1, "ASSERT failed:%s:%d: " \
+ #x "\n", __FILE__, __LINE__); \
+ } while(0)
+#endif
+
+
+#define PIXMA_STATUS_OK 0x0606
+#define PIXMA_STATUS_FAILED 0x1515
+#define PIXMA_STATUS_BUSY 0x1414
+
+#define PIXMA_MAX_ID_LEN 30
+
+/* These may have been defined elsewhere */
+#ifndef MIN
+#define MIN(x,y) (((x) < (y)) ? (x):(y))
+#endif
+#ifndef MAX
+#define MAX(x,y) (((x) < (y)) ? (y):(x))
+#endif
+#define ALIGN_SUP(x,n) (((x) + (n) - 1) / (n) * (n))
+#define ALIGN_INF(x,n) (((x) / (n)) * (n))
+
+struct pixma_io_t;
+
+struct pixma_limits_t
+{
+ unsigned xdpi, ydpi;
+ unsigned width, height;
+};
+
+struct pixma_cmdbuf_t
+{
+ unsigned cmd_header_len, res_header_len, cmd_len_field_ofs;
+ unsigned expected_reslen, cmdlen;
+ int reslen;
+ unsigned size;
+ uint8_t *buf;
+};
+
+struct pixma_imagebuf_t
+{
+ uint8_t *wptr, *wend;
+ const uint8_t *rptr, *rend;
+};
+
+struct pixma_t
+{
+ pixma_t *next;
+ struct pixma_io_t *io;
+ const pixma_scan_ops_t *ops;
+ pixma_scan_param_t *param;
+ const pixma_config_t *cfg;
+ char id[PIXMA_MAX_ID_LEN + 1];
+ int cancel; /* NOTE: It can be set in a signal handler. */
+ uint32_t events;
+ void *subdriver; /* can be used by model driver. */
+
+ /* private */
+ uint64_t cur_image_size;
+ pixma_imagebuf_t imagebuf;
+ unsigned scanning:1;
+ unsigned underrun:1;
+};
+
+/** \addtogroup subdriver
+ * @{ */
+/** Scan operations for subdriver. */
+struct pixma_scan_ops_t
+{
+ /** Allocate a data structure for the subdriver. It is called after the
+ * core driver connected to the scanner. The subdriver should reset the
+ * scanner to a known state in this function. */
+ int (*open) (pixma_t *);
+
+ /** Free resources allocated by the subdriver. Don't forget to send abort
+ * command to the scanner if it is scanning. */
+ void (*close) (pixma_t *);
+
+ /** Setup the scanner for scan parameters defined in \a s->param. */
+ int (*scan) (pixma_t * s);
+
+ /** Fill a buffer with image data. The subdriver has two choices:
+ * -# Fill the buffer pointed by ib->wptr directly and leave
+ * ib->rptr and ib->rend untouched. The length of the buffer is
+ * ib->wend - ib->wptr. It must update ib->wptr accordingly.
+ * -# Update ib->rptr and ib->rend to point to the the beginning and
+ * the end of the internal buffer resp. The length of the buffer
+ * is ib->rend - ib->rptr. This function is called again if
+ * and only if pixma_read_image() has copied the whole buffer.
+ *
+ * The subdriver must wait until there is at least one byte to read or
+ * return 0 for the end of image. */
+ int (*fill_buffer) (pixma_t *, pixma_imagebuf_t * ib);
+
+ /** Cancel the scan operation if necessary and free resources allocated in
+ * scan(). */
+ void (*finish_scan) (pixma_t *);
+
+ /** [Optional] Wait for a user's event, e.g. button event. \a timeout is
+ * in milliseconds. If an event occured before it's timed out, flags in
+ * \a s->events should be set accordingly.
+ * \see PIXMA_EV_* */
+ void (*wait_event) (pixma_t * s, int timeout);
+
+ /** Check the scan parameters. The parameters can be adjusted if they are
+ * out of range, e.g. width > max_width. */
+ int (*check_param) (pixma_t *, pixma_scan_param_t *);
+
+ /** Read the device status. \see pixma_get_device_status() */
+ int (*get_status) (pixma_t *, pixma_device_status_t *);
+};
+
+
+/** \name Funtions for read and write big-endian integer values */
+/**@{*/
+void pixma_set_be16 (uint16_t x, uint8_t * buf);
+void pixma_set_be32 (uint32_t x, uint8_t * buf);
+uint16_t pixma_get_be16 (const uint8_t * buf);
+uint32_t pixma_get_be32 (const uint8_t * buf);
+/**@}*/
+
+/** \name Utility functions */
+/**@{*/
+uint8_t pixma_sum_bytes (const void *data, unsigned len);
+int pixma_check_dpi (unsigned dpi, unsigned max);
+void pixma_sleep (unsigned long usec);
+void pixma_get_time (time_t * sec, uint32_t * usec);
+uint8_t * pixma_r_to_ir (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c);
+uint8_t * pixma_rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c);
+uint8_t * pixma_binarize_line(pixma_scan_param_t *, uint8_t * dst, uint8_t * src, unsigned width, unsigned c);
+/**@}*/
+
+/** \name Command related functions */
+/**@{*/
+int pixma_cmd_transaction (pixma_t *, const void *cmd, unsigned cmdlen,
+ void *data, unsigned expected_len);
+int pixma_check_result (pixma_cmdbuf_t *);
+uint8_t *pixma_newcmd (pixma_cmdbuf_t *, unsigned cmd,
+ unsigned dataout, unsigned datain);
+int pixma_exec (pixma_t *, pixma_cmdbuf_t *);
+int pixma_exec_short_cmd (pixma_t *, pixma_cmdbuf_t *, unsigned cmd);
+int pixma_map_status_errno (unsigned status);
+/**@}*/
+
+#define pixma_fill_checksum(start, end) do { \
+ *(end) = -pixma_sum_bytes(start, (end)-(start)); \
+} while(0)
+
+/** @} end of group subdriver */
+
+/** \addtogroup debug
+ * @{ */
+void pixma_set_debug_level (int level);
+#ifndef NDEBUG
+void pixma_hexdump (int level, const void *d_, unsigned len);
+
+/* len: length of data or error code.
+ size: if >= 0, force to print 'size' bytes.
+ max: maximum number of bytes to print(-1 means no limit). */
+void pixma_dump (int level, const char *type, const void *data, int len,
+ int size, int max);
+# define DEBUG_DECLARE_ONLY
+# include "../include/sane/sanei_debug.h"
+#endif /* NDEBUG */
+/** @} end of group debug */
+
+#endif
diff --git a/backend/pixma_imageclass.c b/backend/pixma_imageclass.c
new file mode 100644
index 0000000..c0287a3
--- /dev/null
+++ b/backend/pixma_imageclass.c
@@ -0,0 +1,799 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2009 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2008 Dennis Lou, dlou 99 at yahoo dot com
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+
+/*
+ * imageCLASS backend based on pixma_mp730.c
+ */
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+#define IMAGE_BLOCK_SIZE (0xffff)
+#define MAX_CHUNK_SIZE (0x1000)
+#define MIN_CHUNK_SIZE (0x0200)
+#define CMDBUF_SIZE 512
+
+#define MF4200_PID 0x26b5
+#define MF4100_PID 0x26a3
+#define MF4600_PID 0x26b0
+#define MF4010_PID 0x26b4
+#define MF4360_PID 0x26ec
+#define D480_PID 0x26ed
+#define MF4320_PID 0x26ee
+#define D420_PID 0x26ef
+#define MF3200_PID 0x2684
+#define MF6500_PID 0x2686
+#define MF4410_PID 0x2737
+#define MF3010_PID 0x2759
+#define MF4770_PID 0x2774
+/* the following are all untested */
+#define MF5630_PID 0x264e
+#define MF5650_PID 0x264f
+#define MF8100_PID 0x2659
+#define MF5880_PID 0x26f9
+#define MF6680_PID 0x26fa
+#define MF8030_PID 0x2707
+#define MF4550_PID 0x2736
+#define MF4570_PID 0x275a
+#define IR1133_PID 0x2742
+
+
+enum iclass_state_t
+{
+ state_idle,
+ state_warmup, /* MF4200 always warm/calibrated; others? */
+ state_scanning,
+ state_finished
+};
+
+enum iclass_cmd_t
+{
+ cmd_start_session = 0xdb20,
+ cmd_select_source = 0xdd20,
+ cmd_scan_param = 0xde20,
+ cmd_status = 0xf320,
+ cmd_abort_session = 0xef20,
+ cmd_read_image = 0xd420,
+ cmd_read_image2 = 0xd460, /* New multifunctionals, such as MF4410 */
+ cmd_error_info = 0xff20,
+
+ cmd_activate = 0xcf60
+};
+
+typedef struct iclass_t
+{
+ enum iclass_state_t state;
+ pixma_cmdbuf_t cb;
+ unsigned raw_width;
+ uint8_t current_status[12];
+
+ uint8_t *buf, *blkptr, *lineptr;
+ unsigned buf_len, blk_len;
+
+ unsigned last_block;
+
+ uint8_t generation; /* New multifunctionals are (generation == 2) */
+} iclass_t;
+
+
+static void iclass_finish_scan (pixma_t * s);
+
+/* checksumming is sometimes different than pixmas */
+static int
+iclass_exec (pixma_t * s, pixma_cmdbuf_t * cb, char invcksum)
+{
+ if (cb->cmdlen > cb->cmd_header_len)
+ pixma_fill_checksum (cb->buf + cb->cmd_header_len,
+ cb->buf + cb->cmdlen - 2);
+ cb->buf[cb->cmdlen - 1] = invcksum ? -cb->buf[cb->cmdlen - 2] : 0;
+ cb->reslen =
+ pixma_cmd_transaction (s, cb->buf, cb->cmdlen, cb->buf,
+ cb->expected_reslen);
+ return pixma_check_result (cb);
+}
+
+static int
+has_paper (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ return ((mf->current_status[1] & 0x0f) == 0); /* allow 0x10 as ADF paper OK */
+}
+
+static int
+abort_session (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mf->cb, cmd_abort_session);
+}
+
+static int
+query_status (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mf->cb, cmd_status, 0, 12);
+ error = pixma_exec (s, &mf->cb);
+ if (error >= 0)
+ {
+ memcpy (mf->current_status, data, 12);
+ DBG (3, "Current status: paper=%u cal=%u lamp=%u\n",
+ data[1], data[8], data[7]);
+ PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u\n",
+ data[1], data[8], data[7]));
+ }
+ return error;
+}
+
+static int
+activate (pixma_t * s, uint8_t x)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ uint8_t *data = pixma_newcmd (&mf->cb, cmd_activate, 10, 0);
+ data[0] = 1;
+ data[3] = x;
+ switch (s->cfg->pid)
+ {
+ case MF4200_PID:
+ case MF4600_PID:
+ case MF6500_PID:
+ case D480_PID:
+ case D420_PID:
+ case MF4360_PID:
+ case MF4100_PID:
+ return iclass_exec (s, &mf->cb, 1);
+ break;
+ default:
+ return pixma_exec (s, &mf->cb);
+ }
+}
+
+static int
+start_session (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mf->cb, cmd_start_session);
+}
+
+static int
+select_source (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ uint8_t *data = pixma_newcmd (&mf->cb, cmd_select_source, 10, 0);
+ data[0] = (s->param->source == PIXMA_SOURCE_ADF ||
+ s->param->source == PIXMA_SOURCE_ADFDUP) ? 2 : 1;
+ data[5] = (s->param->source == PIXMA_SOURCE_ADFDUP) ? 3 : 0;
+ switch (s->cfg->pid)
+ {
+ case MF4200_PID:
+ case MF4600_PID:
+ case MF6500_PID:
+ case D480_PID:
+ case D420_PID:
+ case MF4360_PID:
+ case MF4100_PID:
+ return iclass_exec (s, &mf->cb, 0);
+ break;
+ default:
+ return pixma_exec (s, &mf->cb);
+ }
+}
+
+static int
+send_scan_param (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ uint8_t *data;
+
+ data = pixma_newcmd (&mf->cb, cmd_scan_param, 0x2e, 0);
+ pixma_set_be16 (s->param->xdpi | 0x1000, data + 0x04);
+ pixma_set_be16 (s->param->ydpi | 0x1000, data + 0x06);
+ pixma_set_be32 (s->param->x, data + 0x08);
+ pixma_set_be32 (s->param->y, data + 0x0c);
+ pixma_set_be32 (mf->raw_width, data + 0x10);
+ pixma_set_be32 (s->param->h, data + 0x14);
+ data[0x18] = (s->param->channels == 1) ? 0x04 : 0x08;
+ data[0x19] = s->param->channels * s->param->depth; /* bits per pixel */
+ data[0x1f] = 0x7f;
+ data[0x20] = 0xff;
+ data[0x23] = 0x81;
+ switch (s->cfg->pid)
+ {
+ case MF4200_PID:
+ case MF4600_PID:
+ case MF6500_PID:
+ case D480_PID:
+ case D420_PID:
+ case MF4360_PID:
+ case MF4100_PID:
+ return iclass_exec (s, &mf->cb, 0);
+ break;
+ default:
+ return pixma_exec (s, &mf->cb);
+ }
+}
+
+static int
+request_image_block (pixma_t * s, unsigned flag, uint8_t * info,
+ unsigned * size, uint8_t * data, unsigned * datalen)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ int error;
+ unsigned expected_len;
+ const int hlen = 2 + 6;
+
+ memset (mf->cb.buf, 0, 11);
+ pixma_set_be16 (((s->cfg->pid == MF3010_PID ||
+ s->cfg->pid == MF4410_PID ||
+ s->cfg->pid == MF4770_PID ||
+ s->cfg->pid == MF4550_PID) ? cmd_read_image2 : cmd_read_image), mf->cb.buf);
+ mf->cb.buf[8] = flag;
+ mf->cb.buf[10] = 0x06;
+ expected_len = (s->cfg->pid == MF3010_PID ||
+ s->cfg->pid == MF4410_PID ||
+ s->cfg->pid == MF4770_PID ||
+ s->cfg->pid == MF4550_PID ||
+ s->cfg->pid == MF4600_PID ||
+ s->cfg->pid == MF6500_PID ||
+ s->cfg->pid == MF8030_PID) ? 512 : hlen;
+ mf->cb.reslen = pixma_cmd_transaction (s, mf->cb.buf, 11, mf->cb.buf, expected_len);
+ if (mf->cb.reslen >= hlen)
+ {
+ *info = mf->cb.buf[2];
+ *size = pixma_get_be16 (mf->cb.buf + 6); /* 16bit size */
+ error = 0;
+
+ if (s->cfg->pid == MF3010_PID ||
+ s->cfg->pid == MF4410_PID ||
+ s->cfg->pid == MF4770_PID ||
+ s->cfg->pid == MF4550_PID ||
+ s->cfg->pid == MF4600_PID ||
+ s->cfg->pid == MF6500_PID ||
+ s->cfg->pid == MF8030_PID)
+ { /* 32bit size */
+ *datalen = mf->cb.reslen - hlen;
+ *size = (*datalen + hlen == 512) ? pixma_get_be32 (mf->cb.buf + 4) - *datalen : 0;
+ memcpy (data, mf->cb.buf + hlen, *datalen);
+ }
+ }
+ else
+ {
+ error = PIXMA_EPROTO;
+ }
+ return error;
+}
+
+static int
+read_image_block (pixma_t * s, uint8_t * data, unsigned size)
+{
+ int error;
+ unsigned maxchunksize, chunksize, count = 0;
+
+ maxchunksize = MAX_CHUNK_SIZE * ((s->cfg->pid == MF3010_PID ||
+ s->cfg->pid == MF4410_PID ||
+ s->cfg->pid == MF4770_PID ||
+ s->cfg->pid == MF4550_PID ||
+ s->cfg->pid == MF4600_PID ||
+ s->cfg->pid == MF6500_PID ||
+ s->cfg->pid == MF8030_PID) ? 4 : 1);
+ while (size)
+ {
+ if (size >= maxchunksize)
+ chunksize = maxchunksize;
+ else if (size < MIN_CHUNK_SIZE)
+ chunksize = size;
+ else
+ chunksize = size - (size % MIN_CHUNK_SIZE);
+ error = pixma_read (s->io, data, chunksize);
+ if (error < 0)
+ return count;
+ count += error;
+ data += error;
+ size -= error;
+ }
+ return count;
+}
+
+static int
+read_error_info (pixma_t * s, void *buf, unsigned size)
+{
+ unsigned len = 16;
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mf->cb, cmd_error_info, 0, len);
+ switch (s->cfg->pid)
+ {
+ case MF4200_PID:
+ case MF4600_PID:
+ case MF6500_PID:
+ case D480_PID:
+ case D420_PID:
+ case MF4360_PID:
+ case MF4100_PID:
+ error = iclass_exec (s, &mf->cb, 0);
+ break;
+ default:
+ error = pixma_exec (s, &mf->cb);
+ }
+ if (error < 0)
+ return error;
+ if (buf && len < size)
+ {
+ size = len;
+ /* NOTE: I've absolutely no idea what the returned data mean. */
+ memcpy (buf, data, size);
+ error = len;
+ }
+ return error;
+}
+
+static int
+handle_interrupt (pixma_t * s, int timeout)
+{
+ uint8_t buf[16];
+ int len;
+
+ len = pixma_wait_interrupt (s->io, buf, sizeof (buf), timeout);
+ if (len == PIXMA_ETIMEDOUT)
+ return 0;
+ if (len < 0)
+ return len;
+ if (len != 16)
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING:unexpected interrupt packet length %d\n", len));
+ return PIXMA_EPROTO;
+ }
+ if (buf[12] & 0x40)
+ query_status (s);
+ if (buf[15] & 1)
+ s->events = PIXMA_EV_BUTTON1;
+ return 1;
+}
+
+static int
+step1 (pixma_t * s)
+{
+ int error;
+ iclass_t *mf = (iclass_t *) s->subdriver;
+
+ error = query_status (s);
+ if (error < 0)
+ return error;
+ if (s->param->source == PIXMA_SOURCE_ADF && !has_paper (s))
+ return PIXMA_ENO_PAPER;
+ /* activate only seen for generation 1 scanners */
+ if (mf->generation == 1)
+ {
+ if (error >= 0)
+ error = activate (s, 0);
+ if (error >= 0)
+ error = activate (s, 4);
+ }
+ return error;
+}
+
+/* line in=rrr... ggg... bbb... line out=rgbrgbrgb... */
+static void
+pack_rgb (const uint8_t * src, unsigned nlines, unsigned w, uint8_t * dst)
+{
+ unsigned w2, stride;
+
+ w2 = 2 * w;
+ stride = 3 * w;
+ for (; nlines != 0; nlines--)
+ {
+ unsigned x;
+ for (x = 0; x != w; x++)
+ {
+ *dst++ = src[x + 0];
+ *dst++ = src[x + w];
+ *dst++ = src[x + w2];
+ }
+ src += stride;
+ }
+}
+
+static int
+iclass_open (pixma_t * s)
+{
+ iclass_t *mf;
+ uint8_t *buf;
+
+ mf = (iclass_t *) calloc (1, sizeof (*mf));
+ if (!mf)
+ return PIXMA_ENOMEM;
+
+ buf = (uint8_t *) malloc (CMDBUF_SIZE);
+ if (!buf)
+ {
+ free (mf);
+ return PIXMA_ENOMEM;
+ }
+
+ s->subdriver = mf;
+ mf->state = state_idle;
+
+ mf->cb.buf = buf;
+ mf->cb.size = CMDBUF_SIZE;
+ mf->cb.res_header_len = 2;
+ mf->cb.cmd_header_len = 10;
+ mf->cb.cmd_len_field_ofs = 7;
+
+ /* set generation = 2 for new multifunctionals */
+ mf->generation = (s->cfg->pid >= MF8030_PID) ? 2 : 1;
+ PDBG (pixma_dbg (3, "*iclass_open***** This is a generation %d scanner. *****\n", mf->generation));
+
+ PDBG (pixma_dbg (3, "Trying to clear the interrupt buffer...\n"));
+ if (handle_interrupt (s, 200) == 0)
+ {
+ PDBG (pixma_dbg (3, " no packets in buffer\n"));
+ }
+ return 0;
+}
+
+static void
+iclass_close (pixma_t * s)
+{
+ iclass_t *mf = (iclass_t *) s->subdriver;
+
+ iclass_finish_scan (s);
+ free (mf->cb.buf);
+ free (mf->buf);
+ free (mf);
+ s->subdriver = NULL;
+}
+
+static int
+iclass_check_param (pixma_t * s, pixma_scan_param_t * sp)
+{
+ UNUSED (s);
+
+ sp->depth = 8;
+ sp->line_size = ALIGN_SUP (sp->w, 32) * sp->channels;
+
+ /* Some exceptions here for particular devices */
+ /* Those devices can scan up to Legal 14" with ADF, but A4 11.7" in flatbed */
+ if (sp->source == PIXMA_SOURCE_FLATBED
+ && ( s->cfg->pid == MF4770_PID ))
+ sp->h = MIN (sp->h, 877 * sp->xdpi / 75);
+
+ return 0;
+}
+
+static int
+iclass_scan (pixma_t * s)
+{
+ int error, n;
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ uint8_t *buf, ignore;
+ unsigned buf_len, ignore2;
+
+ if (mf->state != state_idle)
+ return PIXMA_EBUSY;
+
+ /* clear interrupt packets buffer */
+ while (handle_interrupt (s, 0) > 0)
+ {
+ }
+
+ mf->raw_width = ALIGN_SUP (s->param->w, 32);
+ PDBG (pixma_dbg (3, "raw_width = %u\n", mf->raw_width));
+
+ n = IMAGE_BLOCK_SIZE / s->param->line_size + 1;
+ buf_len = (n + 1) * s->param->line_size + IMAGE_BLOCK_SIZE;
+ if (buf_len > mf->buf_len)
+ {
+ buf = (uint8_t *) realloc (mf->buf, buf_len);
+ if (!buf)
+ return PIXMA_ENOMEM;
+ mf->buf = buf;
+ mf->buf_len = buf_len;
+ }
+ mf->lineptr = mf->buf;
+ mf->blkptr = mf->buf + n * s->param->line_size;
+ mf->blk_len = 0;
+
+ error = step1 (s);
+ if (error >= 0 && (s->param->adf_pageid == 0 || mf->generation == 1))
+ { /* single sheet or first sheet from ADF */
+ PDBG (pixma_dbg (3, "*iclass_scan***** start scanning *****\n"));
+ error = start_session (s);
+ if (error >= 0)
+ mf->state = state_scanning;
+ if (error >= 0)
+ error = select_source (s);
+ }
+ else if (error >= 0)
+ { /* next sheet from ADF */
+ PDBG (pixma_dbg (3, "*iclass_scan***** scan next sheet from ADF *****\n"));
+ mf->state = state_scanning;
+ }
+ if (error >= 0)
+ error = send_scan_param (s);
+ if (error >= 0)
+ error = request_image_block (s, 0, &ignore, &ignore2, &ignore, &ignore2);
+ if (error < 0)
+ {
+ iclass_finish_scan (s);
+ return error;
+ }
+ mf->last_block = 0;
+ return 0;
+}
+
+
+static int
+iclass_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ int error, n;
+ iclass_t *mf = (iclass_t *) s->subdriver;
+ unsigned block_size, lines_size, first_block_size;
+ uint8_t info;
+
+/*
+ * 1. send a block request cmd (d4 20 00... 04 00 06)
+ * 2. examine the response for block size and/or end-of-scan flag
+ * 3. read the block one chunk at a time
+ * 4. repeat until have enough to process >=1 lines
+ */
+ do
+ {
+ do
+ {
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ if (mf->last_block)
+ {
+ /* end of image */
+ mf->state = state_finished;
+ return 0;
+ }
+
+ first_block_size = 0;
+ error = request_image_block (s, 4, &info, &block_size,
+ mf->blkptr + mf->blk_len, &first_block_size);
+ /* add current block to remainder of previous */
+ mf->blk_len += first_block_size;
+ if (error < 0)
+ {
+ /* NOTE: seen in traffic logs but don't know the meaning. */
+ read_error_info (s, NULL, 0);
+ if (error == PIXMA_ECANCELED)
+ return error;
+ }
+
+ /* info: 0x28 = end; 0x38 = end + ADF empty */
+ mf->last_block = info & 0x38;
+ if ((info & ~0x38) != 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n"));
+ PDBG (pixma_hexdump (1, &info, 1));
+ }
+
+ if (block_size == 0)
+ {
+ /* no image data at this moment. */
+ /*pixma_sleep(100000); *//* FIXME: too short, too long? */
+ handle_interrupt (s, 100);
+ }
+ }
+ while (block_size == 0 && first_block_size == 0);
+
+ error = read_image_block (s, mf->blkptr + mf->blk_len, block_size);
+ block_size = error;
+ if (error < 0)
+ return error;
+
+ /* add current block to remainder of previous */
+ mf->blk_len += block_size;
+ /* n = number of full lines (rows) we have in the buffer. */
+ n = mf->blk_len / s->param->line_size;
+ if (n != 0)
+ {
+ if (s->param->channels != 1 &&
+ s->cfg->pid != MF3010_PID &&
+ s->cfg->pid != MF4410_PID &&
+ s->cfg->pid != MF4770_PID &&
+ s->cfg->pid != MF4550_PID &&
+ s->cfg->pid != MF4600_PID &&
+ s->cfg->pid != MF6500_PID &&
+ s->cfg->pid != MF8030_PID)
+ {
+ /* color and not MF46xx or MF65xx */
+ pack_rgb (mf->blkptr, n, mf->raw_width, mf->lineptr);
+ }
+ else
+ {
+ /* grayscale */
+ memcpy (mf->lineptr, mf->blkptr, n * s->param->line_size);
+ }
+ lines_size = n * s->param->line_size;
+ /* cull remainder and shift left */
+ mf->blk_len -= lines_size;
+ memcpy (mf->blkptr, mf->blkptr + lines_size, mf->blk_len);
+ }
+ }
+ while (n == 0);
+
+ /* output full lines, keep partial lines for next block */
+ ib->rptr = mf->lineptr;
+ ib->rend = mf->lineptr + lines_size;
+ return ib->rend - ib->rptr;
+}
+
+static void
+iclass_finish_scan (pixma_t * s)
+{
+ int error;
+ iclass_t *mf = (iclass_t *) s->subdriver;
+
+ switch (mf->state)
+ {
+ /* fall through */
+ case state_warmup:
+ case state_scanning:
+ error = abort_session (s);
+ if (error < 0)
+ PDBG (pixma_dbg
+ (1, "WARNING:abort_session() failed %s\n",
+ pixma_strerror (error)));
+ /* fall through */
+ case state_finished:
+ query_status (s);
+ query_status (s);
+ if (mf->generation == 1)
+ { /* activate only seen for generation 1 scanners */
+ activate (s, 0);
+ query_status (s);
+ }
+ /* 0x38 = last block and ADF empty
+ * 0x28 = last block and Paper in ADF */
+ if (mf->last_block==0x38 /* ADF empty */
+ || (mf->generation == 1 && mf->last_block == 0x28)) /* generation 1 scanner or Paper in ADF */
+ {
+ PDBG (pixma_dbg (3, "*iclass_finish_scan***** abort session *****\n"));
+ abort_session (s);
+ }
+ else
+ PDBG (pixma_dbg (3, "*iclass_finish_scan***** wait for next page from ADF *****\n"));
+
+ mf->state = state_idle;
+ /* fall through */
+ case state_idle:
+ break;
+ }
+}
+
+static void
+iclass_wait_event (pixma_t * s, int timeout)
+{
+ /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for
+ * instance. */
+ while (s->events == 0 && handle_interrupt (s, timeout) > 0)
+ {
+ }
+}
+
+static int
+iclass_get_status (pixma_t * s, pixma_device_status_t * status)
+{
+ int error;
+
+ error = query_status (s);
+ if (error < 0)
+ return error;
+ status->hardware = PIXMA_HARDWARE_OK;
+ status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER;
+ return 0;
+}
+
+
+static const pixma_scan_ops_t pixma_iclass_ops = {
+ iclass_open,
+ iclass_close,
+ iclass_scan,
+ iclass_fill_buffer,
+ iclass_finish_scan,
+ iclass_wait_event,
+ iclass_check_param,
+ iclass_get_status
+};
+
+#define DEV(name, model, pid, dpi, w, h, cap) { \
+ name, /* name */ \
+ model, /* model */ \
+ 0x04a9, pid, /* vid pid */ \
+ 1, /* iface */ \
+ &pixma_iclass_ops, /* ops */ \
+ dpi, dpi, /* xdpi, ydpi */ \
+ 0, 0, /* adftpu_min_dpi & adftpu_max_dpi not used in this subdriver */ \
+ 0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
+ w, h, /* width, height */ \
+ PIXMA_CAP_GRAY|PIXMA_CAP_EVENTS|cap \
+}
+const pixma_config_t pixma_iclass_devices[] = {
+ DEV ("Canon imageCLASS MF4270", "MF4270", MF4200_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageCLASS MF4150", "MF4100", MF4100_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageCLASS MF4690", "MF4690", MF4600_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageCLASS D420", "D420", D420_PID, 600, 640, 877, PIXMA_CAP_ADFDUP),
+ DEV ("Canon imageCLASS D480", "D480", D480_PID, 600, 640, 877, PIXMA_CAP_ADFDUP),
+ DEV ("Canon imageCLASS MF4360", "MF4360", MF4360_PID, 600, 640, 877, PIXMA_CAP_ADFDUP),
+ DEV ("Canon imageCLASS MF4320", "MF4320", MF4320_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageCLASS MF4010", "MF4010", MF4010_PID, 600, 640, 877, 0),
+ DEV ("Canon imageCLASS MF3240", "MF3240", MF3200_PID, 600, 640, 877, 0),
+ DEV ("Canon imageClass MF6500", "MF6500", MF6500_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageCLASS MF4410", "MF4410", MF4410_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon i-SENSYS MF4550d", "MF4550", MF4550_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon i-SENSYS MF3010", "MF3010", MF3010_PID, 600, 640, 877, 0),
+ DEV ("Canon imageCLASS MF4770n", "MF4770", MF4770_PID, 600, 640, 1050, PIXMA_CAP_ADF),
+ /* FIXME: the following capabilities all need updating/verifying */
+ DEV ("Canon imageCLASS MF5630", "MF5630", MF5630_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon laserBase MF5650", "MF5650", MF5650_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageCLASS MF8170c", "MF8170c", MF8100_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon imageClass MF8030", "MF8030", MF8030_PID, 600, 640, 877, PIXMA_CAP_ADF),
+ DEV ("Canon i-SENSYS MF5880dn", "MF5880", MF5880_PID, 600, 640, 877, PIXMA_CAP_ADFDUP),
+ DEV ("Canon i-SENSYS MF6680dn", "MF6680", MF6680_PID, 600, 640, 877, PIXMA_CAP_ADFDUP),
+ DEV ("Canon imageCLASS MF4570dw", "MF4570dw", MF4570_PID, 600, 640, 877, 0),
+ DEV ("Canon imageRUNNER 1133", "iR1133", IR1133_PID, 600, 637, 877, PIXMA_CAP_ADFDUP),
+ DEV (NULL, NULL, 0, 0, 0, 0, 0)
+};
diff --git a/backend/pixma_io.h b/backend/pixma_io.h
new file mode 100644
index 0000000..004e008
--- /dev/null
+++ b/backend/pixma_io.h
@@ -0,0 +1,186 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#ifndef PIXMA_IO_H
+#define PIXMA_IO_H
+
+/* TODO: move to pixma_common.h, to reduce the number of files */
+
+/*!
+ * \defgroup IO IO interface
+ * \brief The IO interface.
+ *
+ * Return value of functions that return \c int if not otherwise specified:
+ * - >= if succeeded
+ * - < 0 if failed (e.g. \c PIXMA_ETIMEDOUT)
+ * .
+ * @{
+ */
+
+/** Timeout for pixma_read() in milliseconds */
+#define PIXMA_BULKIN_TIMEOUT 20000
+/** Timeout for pixma_write() in milliseconds */
+#define PIXMA_BULKOUT_TIMEOUT 20000
+
+
+struct pixma_io_t;
+struct pixma_config_t;
+
+/** IO handle */
+typedef struct pixma_io_t pixma_io_t;
+
+
+/** Initialize IO module. It must be called before any other functions in this
+ * module.
+ * \return 0 on success or
+ * - \c PIXMA_ENOMEM
+ * - \c PIXMA_EACCES
+ * - \c PIXMA_EIO */
+int pixma_io_init (void);
+
+/** Shutdown all connections and free resources allocated in this module. */
+void pixma_io_cleanup (void);
+
+/** Find devices currently connected to the computer.
+ * \c devnr passed to functions
+ * - pixma_get_device_config()
+ * - pixma_get_device_id()
+ * - pixma_connect()
+ * .
+ * should be less than the number of devices returned by this function.
+ * \param[in] pixma_devices A \c NULL terminated array of pointers to
+ * array of pixma_config_t which is terminated by setting
+ * pixma_config_t::name to \c NULL.
+ * \return Number of devices found */
+unsigned pixma_collect_devices (const char ** conf_devices,
+ const struct pixma_config_t *const
+ pixma_devices[]);
+
+/** Get device configuration. */
+const struct pixma_config_t *pixma_get_device_config (unsigned devnr);
+
+/** Get a unique ID of the device \a devnr. */
+const char *pixma_get_device_id (unsigned devnr);
+
+/** Connect to the device and claim the scanner interface.
+ * \param[in] devnr
+ * \param[out] handle
+ * \return 0 on success or
+ * - \c PIXMA_ENODEV the device is gone from the system.
+ * - \c PIXMA_EINVAL \a devnr is invalid.
+ * - \c PIXMA_EBUSY
+ * - \c PIXMA_EACCES
+ * - \c PIXMA_ENOMEM
+ * - \c PIXMA_EIO */
+int pixma_connect (unsigned devnr, pixma_io_t ** handle);
+
+/** Release the scanner interface and disconnect from the device. */
+void pixma_disconnect (pixma_io_t *);
+
+/** Activate connection to scanner */
+int pixma_activate (pixma_io_t *);
+
+/** De-activate connection to scanner */
+int pixma_deactivate (pixma_io_t *);
+
+/** Reset the USB interface. \warning Use with care! */
+int pixma_reset_device (pixma_io_t *);
+
+/** Write data to the device. This function may not be interrupted by signals.
+ * It will return iff
+ * - \a len bytes have been successfully written or
+ * - an error (inclusive timeout) occured.
+ * .
+ * \note Calling pixma_write(io, buf, n1) and pixma(io, buf+n1, n2) may
+ * not be the same as pixma_write(io, buf, n1+n2) if n1 is not
+ * multiple of the maximum packet size of the endpoint.
+ * \param[in] cmd Data
+ * \param[in] len Length of data
+ * \return Number of bytes successfully written (always = \a len) or
+ * - \c PIXMA_ETIMEDOUT
+ * - \c PIXMA_EIO
+ * - \c PIXMA_ENOMEM
+ * \see #PIXMA_BULKOUT_TIMEOUT */
+int pixma_write (pixma_io_t *, const void *cmd, unsigned len);
+
+/** Read data from the device. This function may not be interrupted by signals.
+ * It will return iff
+ * - \a size bytes have been successfully read,
+ * - a short packet has been read or
+ * - an error (inclusive timeout) occured.
+ * .
+ * \param[out] buf
+ * \param[in] size of the buffer
+ * \return Number of bytes successfully read. A return value of zero means that
+ * a zero length USB packet was received. Or
+ * - \c PIXMA_ETIMEDOUT
+ * - \c PIXMA_EIO
+ * - \c PIXMA_ENOMEM
+ * \see #PIXMA_BULKIN_TIMEOUT */
+int pixma_read (pixma_io_t *, void *buf, unsigned size);
+
+/** Wait for an interrupt. This function can be interrupted by signals.
+ * \a size should be less than or equal to the maximum packet size.
+ * \param[out] buf
+ * \param[in] size of the buffer
+ * \param[in] timeout in milliseconds; if < 0, wait forever.
+ * \return Number of bytes successfully read or
+ * - \c PIXMA_ETIMEDOUT
+ * - \c PIXMA_EIO
+ * - \c PIXMA_ENOMEM
+ * - \c PIXMA_ECANCELED if it was interrupted by a signal. */
+int pixma_wait_interrupt (pixma_io_t *, void *buf, unsigned size,
+ int timeout);
+
+/** Enable or disable background interrupt monitoring.
+ * Background mode is enabled by default.
+ * \param[in] background if not zero, a URB is submitted in background
+ * for interrupt endpoint.
+ * \return 0 on success or
+ * - \c PIXMA_ENOTSUP
+ * - \c PIXMA_EIO
+ * - \c PIXMA_ENOMEM */
+int pixma_set_interrupt_mode (pixma_io_t *, int background);
+
+/** @} end of IO group */
+
+#endif
diff --git a/backend/pixma_io_sanei.c b/backend/pixma_io_sanei.c
new file mode 100644
index 0000000..b25c7eb
--- /dev/null
+++ b/backend/pixma_io_sanei.c
@@ -0,0 +1,592 @@
+/* SANE - Scanner Access Now Easy.
+ * For limitations, see function sanei_usb_get_vendor_product().
+
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#include "../include/sane/config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h> /* INT_MAX */
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+#include "pixma_bjnp.h"
+
+#include "../include/sane/sanei_usb.h"
+
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+/* MAC OS X does not support timeouts in darwin/libusb interrupt reads
+ * This is a very basic turnaround for MAC OS X
+ * Button scan will not work with this wrapper */
+#ifdef __APPLE__
+# define sanei_usb_read_int sanei_usb_read_bulk
+#endif
+
+
+struct pixma_io_t
+{
+ pixma_io_t *next;
+ int interface;
+ SANE_Int dev;
+};
+
+typedef struct scanner_info_t
+{
+ struct scanner_info_t *next;
+ char *devname;
+ int interface;
+ const pixma_config_t *cfg;
+ char serial[PIXMA_MAX_ID_LEN + 1]; /* "xxxxyyyy_zzzzzzz..."
+ x = vid, y = pid, z = serial */
+} scanner_info_t;
+
+#define INT_USB 0
+#define INT_BJNP 1
+
+static scanner_info_t *first_scanner = NULL;
+static pixma_io_t *first_io = NULL;
+static unsigned nscanners;
+
+
+static scanner_info_t *
+get_scanner_info (unsigned devnr)
+{
+ scanner_info_t *si;
+ for (si = first_scanner; si && devnr != 0; --devnr, si = si->next)
+ {
+ }
+ return si;
+}
+
+static const struct pixma_config_t *lookup_scanner(const char *makemodel,
+ const struct pixma_config_t *const pixma_devices[])
+{
+ int i;
+ const struct pixma_config_t *cfg;
+ char *match;
+
+ for (i = 0; pixma_devices[i]; i++)
+ {
+ /* loop through the device classes (mp150, mp730 etc) */
+ for (cfg = pixma_devices[i]; cfg->name; cfg++)
+ {
+ /* loop through devices in class */
+ if ((match = strcasestr (makemodel, cfg->model)) != NULL)
+ {
+ /* possible match found, make sure it is not a partial match */
+ /* MP600 and MP600R are different models! */
+ /* some models contain ranges, so check for a '-' too */
+
+ if ((match[strlen(cfg->model)] == ' ') ||
+ (match[strlen(cfg->model)] == '\0') ||
+ (match[strlen(cfg->model)] == '-'))
+ {
+ pixma_dbg (3, "Scanner model found: Name %s(%s) matches %s\n", cfg->model, cfg->name, makemodel);
+ return cfg;
+ }
+ }
+ pixma_dbg (20, "Scanner model %s(%s) not found, giving up! %s\n", cfg->model, cfg->name, makemodel);
+ }
+ }
+ return NULL;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname)
+{
+ scanner_info_t *si;
+
+ si = (scanner_info_t *) calloc (1, sizeof (*si));
+ if (!si)
+ return SANE_STATUS_NO_MEM;
+ si->devname = strdup (devname);
+ if (!si->devname)
+ return SANE_STATUS_NO_MEM;
+ si -> interface = INT_USB;
+ si->next = first_scanner;
+ first_scanner = si;
+ nscanners++;
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+attach_bjnp (SANE_String_Const devname, SANE_String_Const makemodel,
+ SANE_String_Const serial,
+ const struct pixma_config_t *const pixma_devices[])
+{
+ scanner_info_t *si;
+ const pixma_config_t *cfg;
+ SANE_Status error;
+
+ si = (scanner_info_t *) calloc (1, sizeof (*si));
+ if (!si)
+ return SANE_STATUS_NO_MEM;
+ si->devname = strdup (devname);
+ if (!si->devname)
+ return SANE_STATUS_NO_MEM;
+ if ((cfg = lookup_scanner(makemodel, pixma_devices)) == (struct pixma_config_t *)NULL)
+ error = SANE_STATUS_INVAL;
+ else
+ {
+ si->cfg = cfg;
+ sprintf(si->serial, "%s_%s", cfg->model, serial);
+ si -> interface = INT_BJNP;
+ si->next = first_scanner;
+ first_scanner = si;
+ nscanners++;
+ error = SANE_STATUS_GOOD;
+ }
+ return error;
+}
+
+static void
+clear_scanner_list (void)
+{
+ scanner_info_t *si = first_scanner;
+ while (si)
+ {
+ scanner_info_t *temp = si;
+ free (si->devname);
+ si = si->next;
+ free (temp);
+ }
+ nscanners = 0;
+ first_scanner = NULL;
+}
+
+static SANE_Status
+get_descriptor (SANE_Int dn, SANE_Int type, SANE_Int descidx,
+ SANE_Int index, SANE_Int length, SANE_Byte * data)
+{
+ return sanei_usb_control_msg (dn, 0x80, USB_REQ_GET_DESCRIPTOR,
+ ((type & 0xff) << 8) | (descidx & 0xff),
+ index, length, data);
+}
+
+static SANE_Status
+get_string_descriptor (SANE_Int dn, SANE_Int index, SANE_Int lang,
+ SANE_Int length, SANE_Byte * data)
+{
+ return get_descriptor (dn, USB_DT_STRING, index, lang, length, data);
+}
+
+static void
+u16tohex (uint16_t x, char *str)
+{
+ static const char hdigit[16] =
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F'
+ };
+ str[0] = hdigit[(x >> 12) & 0xf];
+ str[1] = hdigit[(x >> 8) & 0xf];
+ str[2] = hdigit[(x >> 4) & 0xf];
+ str[3] = hdigit[x & 0xf];
+ str[4] = '\0';
+}
+
+static void
+read_serial_number (scanner_info_t * si)
+{
+ uint8_t unicode[2 * (PIXMA_MAX_ID_LEN - 9) + 2];
+ uint8_t ddesc[18];
+ int iSerialNumber;
+ SANE_Int usb;
+ char *serial = si->serial;
+
+ u16tohex (si->cfg->vid, serial);
+ u16tohex (si->cfg->pid, serial + 4);
+
+ if (SANE_STATUS_GOOD != sanei_usb_open (si->devname, &usb))
+ return;
+ if (get_descriptor (usb, USB_DT_DEVICE, 0, 0, 18, ddesc)
+ != SANE_STATUS_GOOD)
+ goto done;
+ iSerialNumber = ddesc[16];
+ if (iSerialNumber != 0)
+ {
+ int i, len;
+ SANE_Status status;
+
+ /*int iSerialNumber = ddesc[16];*/
+ /* Read the first language code. Assumed that there is at least one. */
+ if (get_string_descriptor (usb, 0, 0, 4, unicode) != SANE_STATUS_GOOD)
+ goto done;
+ /* Read the serial number string. */
+ status = get_string_descriptor (usb, iSerialNumber,
+ unicode[3] * 256 + unicode[2],
+ sizeof (unicode), unicode);
+ if (status != SANE_STATUS_GOOD)
+ goto done;
+ /* Assumed charset: Latin1 */
+ len = unicode[0];
+ if (len > (int) sizeof (unicode))
+ {
+ len = sizeof (unicode);
+ PDBG (pixma_dbg (1, "WARNING:Truncated serial number\n"));
+ }
+ serial[8] = '_';
+ for (i = 2; i < len; i += 2)
+ {
+ serial[9 + i / 2 - 1] = unicode[i];
+ }
+ serial[9 + i / 2 - 1] = '\0';
+ }
+ else
+ {
+ PDBG (pixma_dbg (1, "WARNING:No serial number\n"));
+ }
+done:
+ sanei_usb_close (usb);
+}
+
+static int
+map_error (SANE_Status ss)
+{
+ switch (ss)
+ {
+ case SANE_STATUS_GOOD:
+ return 0;
+ case SANE_STATUS_UNSUPPORTED:
+ return PIXMA_ENODEV;
+ case SANE_STATUS_DEVICE_BUSY:
+ return PIXMA_EBUSY;
+ case SANE_STATUS_INVAL:
+ return PIXMA_EINVAL;
+ case SANE_STATUS_IO_ERROR:
+ return PIXMA_EIO;
+ case SANE_STATUS_NO_MEM:
+ return PIXMA_ENOMEM;
+ case SANE_STATUS_ACCESS_DENIED:
+ return PIXMA_EACCES;
+ case SANE_STATUS_CANCELLED:
+ return PIXMA_ECANCELED;
+ case SANE_STATUS_JAMMED:
+ return PIXMA_EPAPER_JAMMED;
+ case SANE_STATUS_COVER_OPEN:
+ return PIXMA_ECOVER_OPEN;
+ case SANE_STATUS_NO_DOCS:
+ return PIXMA_ENO_PAPER;
+ case SANE_STATUS_EOF:
+ return PIXMA_EOF;
+#ifdef SANE_STATUS_HW_LOCKED
+ case SANE_STATUS_HW_LOCKED: /* unused by pixma */
+#endif
+#ifdef SANE_STATUS_WARMING_UP
+ case SANE_STATUS_WARMING_UP: /* unused by pixma */
+#endif
+ break;
+ }
+ PDBG (pixma_dbg (1, "BUG:Unmapped SANE Status code %d\n", ss));
+ return PIXMA_EIO; /* should not happen */
+}
+
+
+int
+pixma_io_init (void)
+{
+ sanei_usb_init ();
+ sanei_bjnp_init();
+ nscanners = 0;
+ return 0;
+}
+
+void
+pixma_io_cleanup (void)
+{
+ while (first_io)
+ pixma_disconnect (first_io);
+ clear_scanner_list ();
+}
+
+unsigned
+pixma_collect_devices (const char **conf_devices,
+ const struct pixma_config_t *const pixma_devices[])
+{
+ unsigned i, j;
+ struct scanner_info_t *si;
+ const struct pixma_config_t *cfg;
+
+ clear_scanner_list ();
+ j = 0;
+ for (i = 0; pixma_devices[i]; i++)
+ {
+ for (cfg = pixma_devices[i]; cfg->name; cfg++)
+ {
+ sanei_usb_find_devices (cfg->vid, cfg->pid, attach);
+ si = first_scanner;
+ while (j < nscanners)
+ {
+ PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n",
+ cfg->name, si->devname));
+ si->cfg = cfg;
+ read_serial_number (si);
+ si = si->next;
+ j++;
+ }
+ }
+ }
+ sanei_bjnp_find_devices(conf_devices, attach_bjnp, pixma_devices);
+ si = first_scanner;
+ while (j < nscanners)
+ {
+ PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n",
+ si->cfg->name, si->devname));
+ si = si->next;
+ j++;
+
+ }
+ return nscanners;
+}
+
+const pixma_config_t *
+pixma_get_device_config (unsigned devnr)
+{
+ const scanner_info_t *si = get_scanner_info (devnr);
+ return (si) ? si->cfg : NULL;
+}
+
+const char *
+pixma_get_device_id (unsigned devnr)
+{
+ const scanner_info_t *si = get_scanner_info (devnr);
+ return (si) ? si->serial : NULL;
+}
+
+int
+pixma_connect (unsigned devnr, pixma_io_t ** handle)
+{
+ pixma_io_t *io;
+ SANE_Int dev;
+ const scanner_info_t *si;
+ int error;
+
+ *handle = NULL;
+ si = get_scanner_info (devnr);
+ if (!si)
+ return PIXMA_EINVAL;
+ if (si-> interface == INT_BJNP)
+ error = map_error (sanei_bjnp_open (si->devname, &dev));
+ else
+ error = map_error (sanei_usb_open (si->devname, &dev));
+
+ if (error < 0)
+ return error;
+ io = (pixma_io_t *) calloc (1, sizeof (*io));
+ if (!io)
+ {
+ if (si -> interface == INT_BJNP)
+ sanei_bjnp_close (dev);
+ else
+ sanei_usb_close (dev);
+ return PIXMA_ENOMEM;
+ }
+ io->next = first_io;
+ first_io = io;
+ io->dev = dev;
+ io->interface = si->interface;
+ *handle = io;
+ return 0;
+}
+
+
+void
+pixma_disconnect (pixma_io_t * io)
+{
+ pixma_io_t **p;
+
+ if (!io)
+ return;
+ for (p = &first_io; *p && *p != io; p = &((*p)->next))
+ {
+ }
+ PASSERT (*p);
+ if (!(*p))
+ return;
+ if (io-> interface == INT_BJNP)
+ sanei_bjnp_close (io->dev);
+ else
+ sanei_usb_close (io->dev);
+ *p = io->next;
+ free (io);
+}
+
+int pixma_activate (pixma_io_t * io)
+{
+ int error;
+ if (io->interface == INT_BJNP)
+ {
+ error = map_error(sanei_bjnp_activate (io->dev));
+ }
+ else
+ /* noop for USB interface */
+ error = 0;
+ return error;
+}
+
+int pixma_deactivate (pixma_io_t * io)
+{
+ int error;
+ if (io->interface == INT_BJNP)
+ {
+ error = map_error(sanei_bjnp_deactivate (io->dev));
+ }
+ else
+ /* noop for USB interface */
+ error = 0;
+ return error;
+
+}
+
+int
+pixma_reset_device (pixma_io_t * io)
+{
+ UNUSED (io);
+ return PIXMA_ENOTSUP;
+}
+
+int
+pixma_write (pixma_io_t * io, const void *cmd, unsigned len)
+{
+ size_t count = len;
+ int error;
+
+ if (io->interface == INT_BJNP)
+ {
+ sanei_bjnp_set_timeout (io->dev, PIXMA_BULKOUT_TIMEOUT);
+ error = map_error (sanei_bjnp_write_bulk (io->dev, cmd, &count));
+ }
+ else
+ {
+#ifdef HAVE_SANEI_USB_SET_TIMEOUT
+ sanei_usb_set_timeout (PIXMA_BULKOUT_TIMEOUT);
+#endif
+ error = map_error (sanei_usb_write_bulk (io->dev, cmd, &count));
+ }
+ if (error == PIXMA_EIO)
+ error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */
+ if (count != len)
+ {
+ PDBG (pixma_dbg (1, "WARNING:pixma_write(): count(%u) != len(%u)\n",
+ (unsigned) count, len));
+ error = PIXMA_EIO;
+ }
+ if (error >= 0)
+ error = count;
+ PDBG (pixma_dump (10, "OUT ", cmd, error, len, 128));
+ return error;
+}
+
+int
+pixma_read (pixma_io_t * io, void *buf, unsigned size)
+{
+ size_t count = size;
+ int error;
+
+ if (io-> interface == INT_BJNP)
+ {
+ sanei_bjnp_set_timeout (io->dev, PIXMA_BULKIN_TIMEOUT);
+ error = map_error (sanei_bjnp_read_bulk (io->dev, buf, &count));
+ }
+ else
+ {
+#ifdef HAVE_SANEI_USB_SET_TIMEOUT
+ sanei_usb_set_timeout (PIXMA_BULKIN_TIMEOUT);
+#endif
+ error = map_error (sanei_usb_read_bulk (io->dev, buf, &count));
+ }
+
+ if (error == PIXMA_EIO)
+ error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */
+ if (error >= 0)
+ error = count;
+ PDBG (pixma_dump (10, "IN ", buf, error, -1, 128));
+ return error;
+}
+
+int
+pixma_wait_interrupt (pixma_io_t * io, void *buf, unsigned size, int timeout)
+{
+ size_t count = size;
+ int error;
+
+ /* FIXME: What is the meaning of "timeout" in sanei_usb? */
+ if (timeout < 0)
+ timeout = INT_MAX;
+ else if (timeout < 100)
+ timeout = 100;
+ if (io-> interface == INT_BJNP)
+ {
+ sanei_bjnp_set_timeout (io->dev, timeout);
+ error = map_error (sanei_bjnp_read_int (io->dev, buf, &count));
+ }
+ else
+ {
+#ifdef HAVE_SANEI_USB_SET_TIMEOUT
+ sanei_usb_set_timeout (timeout);
+#endif
+ error = map_error (sanei_usb_read_int (io->dev, buf, &count));
+ }
+ if (error == PIXMA_EIO || error == PIXMA_EOF)
+ error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */
+ if (error == 0)
+ error = count;
+ if (error != PIXMA_ETIMEDOUT)
+ PDBG (pixma_dump (10, "INTR", buf, error, -1, -1));
+ return error;
+}
+
+int
+pixma_set_interrupt_mode (pixma_io_t * s, int background)
+{
+ UNUSED (s);
+ return (background) ? PIXMA_ENOTSUP : 0;
+}
diff --git a/backend/pixma_mp150.c b/backend/pixma_mp150.c
new file mode 100644
index 0000000..e29dccd
--- /dev/null
+++ b/backend/pixma_mp150.c
@@ -0,0 +1,1759 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2009 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+/* test cases
+ 1. short USB packet (must be no -ETIMEDOUT)
+ 2. cancel using button on the printer (look for abort command)
+ 3. start scan while busy (status 0x1414)
+ 4. cancel using ctrl-c (must send abort command)
+ */
+
+#define TPU_48 /* uncomment to activate TPU scan at 48 bits */
+/*#define DEBUG_TPU_48*/ /* uncomment to debug 48 bits TPU on a non TPU device */
+/*#define DEBUG_TPU_24*/ /* uncomment to debug 24 bits TPU on a non TPU device */
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h> /* localtime(C90) */
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+
+/* Some macro code to enhance readability */
+#define RET_IF_ERR(x) do { \
+ if ((error = (x)) < 0) \
+ return error; \
+ } while(0)
+
+#define WAIT_INTERRUPT(x) do { \
+ error = handle_interrupt (s, x); \
+ if (s->cancel) \
+ return PIXMA_ECANCELED; \
+ if (error != PIXMA_ECANCELED && error < 0) \
+ return error; \
+ } while(0)
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+/* Size of the command buffer should be multiple of wMaxPacketLength and
+ greater than 4096+24.
+ 4096 = size of gamma table. 24 = header + checksum */
+#define IMAGE_BLOCK_SIZE (512*1024)
+#define CMDBUF_SIZE (4096 + 24)
+#define DEFAULT_GAMMA 2.0 /***** Gamma different from 1.0 is potentially impacting color profile generation *****/
+#define UNKNOWN_PID 0xffff
+
+
+#define CANON_VID 0x04a9
+
+/* Generation 1 */
+#define MP150_PID 0x1709
+#define MP170_PID 0x170a
+#define MP450_PID 0x170b
+#define MP500_PID 0x170c
+#define MP530_PID 0x1712
+#define MP800_PID 0x170d
+#define MP800R_PID 0x170e
+#define MP830_PID 0x1713
+
+/* Generation 2 */
+#define MP160_PID 0x1714
+#define MP180_PID 0x1715
+#define MP460_PID 0x1716
+#define MP510_PID 0x1717
+#define MP600_PID 0x1718
+#define MP600R_PID 0x1719
+
+#define MP140_PID 0x172b
+
+/* Generation 3 */
+/* PIXMA 2007 vintage */
+#define MX7600_PID 0x171c
+#define MP210_PID 0x1721
+#define MP220_PID 0x1722
+#define MP470_PID 0x1723
+#define MP520_PID 0x1724
+#define MP610_PID 0x1725
+#define MX300_PID 0x1727
+#define MX310_PID 0x1728
+#define MX700_PID 0x1729
+#define MX850_PID 0x172c
+
+/* PIXMA 2008 vintage */
+#define MP630_PID 0x172e
+#define MP620_PID 0x172f
+#define MP540_PID 0x1730
+#define MP480_PID 0x1731
+#define MP240_PID 0x1732
+#define MP260_PID 0x1733
+#define MP190_PID 0x1734
+
+/* PIXMA 2009 vintage */
+#define MX860_PID 0x1735
+#define MX320_PID 0x1736 /* untested */
+#define MX330_PID 0x1737
+
+/* Generation 4 */
+#define MP250_PID 0x173a
+#define MP270_PID 0x173b
+#define MP490_PID 0x173c
+#define MP550_PID 0x173d
+#define MP560_PID 0x173e
+#define MP640_PID 0x173f
+
+/* PIXMA 2010 vintage */
+#define MX340_PID 0x1741
+#define MX350_PID 0x1742
+#define MX870_PID 0x1743
+
+/* 2010 new devices (untested) */
+#define MP280_PID 0x1746
+#define MP495_PID 0x1747
+#define MG5100_PID 0x1748
+#define MG5200_PID 0x1749
+#define MG6100_PID 0x174a
+
+/* PIXMA 2011 vintage */
+#define MX360_PID 0x174d
+#define MX410_PID 0x174e
+#define MX420_PID 0x174f
+#define MX880_PID 0x1750
+
+/* 2011 new devices (untested) */
+#define MG2100_PID 0x1751
+#define MG3100_PID 0x1752
+#define MG4100_PID 0x1753
+#define MG5300_PID 0x1754
+#define MG6200_PID 0x1755
+#define MP493_PID 0x1757
+#define E500_PID 0x1758
+
+/* 2012 new devices (untested) */
+#define MX370_PID 0x1759
+#define MX430_PID 0x175B
+#define MX510_PID 0x175C
+#define MX710_PID 0x175D
+#define MX890_PID 0x175E
+#define E600_PID 0x175A
+#define MG4200_PID 0x1763
+
+/* 2013 new devices */
+#define MP230_PID 0x175F
+#define MG6300_PID 0x1765
+
+/* 2013 new devices (untested) */
+#define MG2200_PID 0x1760
+#define E510_PID 0x1761
+#define MG3200_PID 0x1762
+#define MG5400_PID 0x1764
+#define MX390_PID 0x1766
+#define E610_PID 0x1767
+#define MX450_PID 0x1768
+#define MX520_PID 0x1769
+#define MX720_PID 0x176a
+#define MX920_PID 0x176b
+#define MG2400_PID 0x176c
+#define MG2500_PID 0x176d
+#define MG3500_PID 0x176e
+#define MG6500_PID 0x176f
+#define MG6400_PID 0x1770
+#define MG5500_PID 0x1771
+#define MG7100_PID 0x1772
+
+
+/* Generation 4 XML messages that encapsulates the Pixma protocol messages */
+#define XML_START_1 \
+"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
+<cmd xmlns:ivec=\"http://www.canon.com/ns/cmd/2008/07/common/\">\
+<ivec:contents><ivec:operation>StartJob</ivec:operation>\
+<ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\
+<ivec:bidi>1</ivec:bidi></ivec:param_set></ivec:contents></cmd>"
+
+#define XML_START_2 \
+"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
+<cmd xmlns:ivec=\"http://www.canon.com/ns/cmd/2008/07/common/\" xmlns:vcn=\"http://www.canon.com/ns/cmd/2008/07/canon/\">\
+<ivec:contents><ivec:operation>VendorCmd</ivec:operation>\
+<ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\
+<vcn:ijoperation>ModeShift</vcn:ijoperation><vcn:ijmode>1</vcn:ijmode>\
+</ivec:param_set></ivec:contents></cmd>"
+
+#define XML_END \
+"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
+<cmd xmlns:ivec=\"http://www.canon.com/ns/cmd/2008/07/common/\">\
+<ivec:contents><ivec:operation>EndJob</ivec:operation>\
+<ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\
+</ivec:param_set></ivec:contents></cmd>"
+
+#define XML_OK "<ivec:response>OK</ivec:response>"
+
+enum mp150_state_t
+{
+ state_idle,
+ state_warmup,
+ state_scanning,
+ state_transfering,
+ state_finished
+};
+
+enum mp150_cmd_t
+{
+ cmd_start_session = 0xdb20,
+ cmd_select_source = 0xdd20,
+ cmd_gamma = 0xee20,
+ cmd_scan_param = 0xde20,
+ cmd_status = 0xf320,
+ cmd_abort_session = 0xef20,
+ cmd_time = 0xeb80,
+ cmd_read_image = 0xd420,
+ cmd_error_info = 0xff20,
+
+ cmd_start_calibrate_ccd_3 = 0xd520,
+ cmd_end_calibrate_ccd_3 = 0xd720,
+ cmd_scan_param_3 = 0xd820,
+ cmd_scan_start_3 = 0xd920,
+ cmd_status_3 = 0xda20,
+ cmd_get_tpu_info_3 = 0xf520,
+ cmd_set_tpu_info_3 = 0xea20,
+
+ cmd_e920 = 0xe920 /* seen in MP800 */
+};
+
+typedef struct mp150_t
+{
+ enum mp150_state_t state;
+ pixma_cmdbuf_t cb;
+ uint8_t *imgbuf;
+ uint8_t current_status[16];
+ unsigned last_block;
+ uint8_t generation;
+ /* for Generation 3 and CCD shift */
+ uint8_t *linebuf;
+ uint8_t *data_left_ofs;
+ unsigned data_left_len;
+ int shift[3];
+ unsigned color_shift;
+ unsigned stripe_shift;
+ uint8_t tpu_datalen;
+ uint8_t tpu_data[0x40];
+} mp150_t;
+
+/*
+ STAT: 0x0606 = ok,
+ 0x1515 = failed (PIXMA_ECANCELED),
+ 0x1414 = busy (PIXMA_EBUSY)
+
+ Transaction scheme
+ 1. command_header/data | result_header
+ 2. command_header | result_header/data
+ 3. command_header | result_header/image_data
+
+ - data has checksum in the last byte.
+ - image_data has no checksum.
+ - data and image_data begins in the same USB packet as
+ command_header or result_header.
+
+ command format #1:
+ u16be cmd
+ u8[6] 0
+ u8[4] 0
+ u32be PLEN parameter length
+ u8[PLEN-1] parameter
+ u8 parameter check sum
+ result:
+ u16be STAT
+ u8 0
+ u8 0 or 0x21 if STAT == 0x1414
+ u8[4] 0
+
+ command format #2:
+ u16be cmd
+ u8[6] 0
+ u8[4] 0
+ u32be RLEN result length
+ result:
+ u16be STAT
+ u8[6] 0
+ u8[RLEN-1] result
+ u8 result check sum
+
+ command format #3: (only used by read_image_block)
+ u16be 0xd420
+ u8[6] 0
+ u8[4] 0
+ u32be max. block size + 8
+ result:
+ u16be STAT
+ u8[6] 0
+ u8 block info bitfield: 0x8 = end of scan, 0x10 = no more paper, 0x20 = no more data
+ u8[3] 0
+ u32be ILEN image data size
+ u8[ILEN] image data
+ */
+
+static void mp150_finish_scan (pixma_t * s);
+
+static int
+is_scanning_from_adf (pixma_t * s)
+{
+ return (s->param->source == PIXMA_SOURCE_ADF
+ || s->param->source == PIXMA_SOURCE_ADFDUP);
+}
+
+static int
+is_scanning_from_adfdup (pixma_t * s)
+{
+ return (s->param->source == PIXMA_SOURCE_ADFDUP);
+}
+
+static int
+is_scanning_from_tpu (pixma_t * s)
+{
+ return (s->param->source == PIXMA_SOURCE_TPU);
+}
+
+static int
+send_xml_dialog (pixma_t * s, const char * xml_message)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ int datalen;
+
+ datalen = pixma_cmd_transaction (s, xml_message, strlen (xml_message),
+ mp->cb.buf, 1024);
+ if (datalen < 0)
+ return datalen;
+
+ mp->cb.buf[datalen] = 0;
+
+ PDBG (pixma_dbg (10, "XML message sent to scanner:\n%s\n", xml_message));
+ PDBG (pixma_dbg (10, "XML response back from scanner:\n%s\n", mp->cb.buf));
+
+ return (strcasestr ((const char *) mp->cb.buf, XML_OK) != NULL);
+}
+
+static void
+new_cmd_tpu_msg (pixma_t *s, pixma_cmdbuf_t * cb, uint16_t cmd)
+{
+ pixma_newcmd (cb, cmd, 0, 0);
+ cb->buf[3] = (is_scanning_from_tpu (s)) ? 0x01 : 0x00;
+}
+
+static int
+start_session (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ new_cmd_tpu_msg (s, &mp->cb, cmd_start_session);
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+start_scan_3 (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ new_cmd_tpu_msg (s, &mp->cb, cmd_scan_start_3);
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+send_cmd_start_calibrate_ccd_3 (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ pixma_newcmd (&mp->cb, cmd_start_calibrate_ccd_3, 0, 0);
+ mp->cb.buf[3] = 0x01;
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+is_calibrated (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ if (mp->generation >= 3)
+ {
+ return ((mp->current_status[0] & 0x01) == 1);
+ }
+ if (mp->generation == 1)
+ {
+ return (mp->current_status[8] == 1);
+ }
+ else
+ {
+ return (mp->current_status[9] == 1);
+ }
+}
+
+static int
+has_paper (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ if (is_scanning_from_adfdup (s))
+ return (mp->current_status[1] == 0 || mp->current_status[2] == 0);
+ else
+ return (mp->current_status[1] == 0);
+}
+
+static void
+drain_bulk_in (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) >= 0);
+}
+
+static int
+abort_session (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
+}
+
+static int
+send_cmd_e920 (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_e920);
+}
+
+static int
+select_source (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+
+ data = pixma_newcmd (&mp->cb, cmd_select_source, 12, 0);
+ data[5] = ((mp->generation == 2) ? 1 : 0);
+ switch (s->param->source)
+ {
+ case PIXMA_SOURCE_FLATBED:
+ data[0] = 1;
+ data[1] = 1;
+ break;
+
+ case PIXMA_SOURCE_ADF:
+ data[0] = 2;
+ data[5] = 1;
+ data[6] = 1;
+ break;
+
+ case PIXMA_SOURCE_ADFDUP:
+ data[0] = 2;
+ data[5] = 3;
+ data[6] = 3;
+ break;
+
+ case PIXMA_SOURCE_TPU:
+ data[0] = 4;
+ data[1] = 2;
+ break;
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+send_get_tpu_info_3 (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_get_tpu_info_3, 0, 0x34);
+ RET_IF_ERR (pixma_exec (s, &mp->cb));
+ memcpy (mp->tpu_data, data, 0x34);
+ return error;
+}
+
+static int
+send_set_tpu_info (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+
+ if (mp->tpu_datalen == 0)
+ return 0;
+ data = pixma_newcmd (&mp->cb, cmd_set_tpu_info_3, 0x34, 0);
+ memcpy (data, mp->tpu_data, 0x34);
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+send_gamma_table (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ const uint8_t *lut = s->param->gamma_table;
+ uint8_t *data;
+
+ if (mp->generation == 1)
+ {
+ data = pixma_newcmd (&mp->cb, cmd_gamma, 4096 + 8, 0);
+ data[0] = (s->param->channels == 3) ? 0x10 : 0x01;
+ pixma_set_be16 (0x1004, data + 2);
+ if (lut)
+ memcpy (data + 4, lut, 4096);
+ else
+ pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096);
+ }
+ else
+ {
+ /* FIXME: Gamma table for 2nd generation: 1024 * uint16_le */
+ data = pixma_newcmd (&mp->cb, cmd_gamma, 2048 + 8, 0);
+ data[0] = 0x10;
+ pixma_set_be16 (0x0804, data + 2);
+ if (lut)
+ {
+ int i;
+ for (i = 0; i < 1024; i++)
+ {
+ int j = (i << 2) + (i >> 8);
+ data[4 + 2 * i + 0] = lut[j];
+ data[4 + 2 * i + 1] = lut[j];
+ }
+ }
+ else
+ {
+ int i;
+ pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 2048);
+ for (i = 0; i < 1024; i++)
+ {
+ int j = (i << 1) + (i >> 9);
+ data[4 + 2 * i + 0] = data[4 + j];
+ data[4 + 2 * i + 1] = data[4 + j];
+ }
+ }
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static unsigned
+calc_raw_width (const mp150_t * mp, const pixma_scan_param_t * param)
+{
+ unsigned raw_width;
+ /* NOTE: Actually, we can send arbitary width to MP150. Lines returned
+ are always padded to multiple of 4 or 12 pixels. Is this valid for
+ other models, too? */
+ if (mp->generation >= 2)
+ {
+ raw_width = ALIGN_SUP (param->w + param->xs, 32);
+ /* PDBG (pixma_dbg (4, "*calc_raw_width***** width %i extended by %i and rounded to %i *****\n", param->w, param->xs, raw_width)); */
+ }
+ else if (param->channels == 1)
+ {
+ raw_width = ALIGN_SUP (param->w + param->xs, 12);
+ }
+ else
+ {
+ raw_width = ALIGN_SUP (param->w + param->xs, 4);
+ }
+ return raw_width;
+}
+
+static int
+has_ccd_sensor (pixma_t * s)
+{
+ return ((s->cfg->cap & PIXMA_CAP_CCD) != 0);
+}
+
+static int
+is_ccd_grayscale (pixma_t * s)
+{
+ return (has_ccd_sensor (s) && (s->param->channels == 1) && !s->param->software_lineart);
+}
+
+static int
+is_ccd_lineart (pixma_t * s)
+{
+ return (has_ccd_sensor (s) && s->param->software_lineart);
+}
+
+/* CCD sensors don't have neither a Grayscale mode nor a Lineart mode,
+ * but use color mode instead */
+static unsigned
+get_cis_ccd_line_size (pixma_t * s)
+{
+ return ((s->param->wx ? s->param->line_size / s->param->w * s->param->wx
+ : s->param->line_size) * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : 1));
+}
+
+static unsigned
+calc_shifting (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ /* If stripes shift needed (CCD devices), how many pixels shift */
+ mp->stripe_shift = 0;
+ switch (s->cfg->pid)
+ {
+ case MP800_PID:
+ case MP800R_PID:
+ if (s->param->xdpi == 2400)
+ {
+ if (is_scanning_from_tpu(s))
+ mp->stripe_shift = 6;
+ else
+ mp->stripe_shift = 3;
+ }
+ break;
+
+ case MP830_PID:
+ if (s->param->xdpi == 2400)
+ {
+ if (is_scanning_from_tpu(s))
+ mp->stripe_shift = 6;
+ else
+ mp->stripe_shift = 3;
+ }
+ break;
+
+ default: /* Default, and all CIS devices */
+ break;
+ }
+ /* If color plane shift (CCD devices), how many pixels shift */
+ mp->color_shift = mp->shift[0] = mp->shift[1] = mp->shift[2] = 0;
+ if (s->param->ydpi > 75)
+ {
+ switch (s->cfg->pid)
+ {
+ case MP800_PID:
+ case MP800R_PID:
+ case MP830_PID:
+ mp->color_shift = s->param->ydpi / ((s->param->ydpi < 1200) ? 150 : 75);
+
+ if (is_scanning_from_tpu (s))
+ mp->color_shift = s->param->ydpi / 75;
+
+ mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
+ if (is_scanning_from_adf (s))
+ { /* ADF */
+ mp->shift[0] = 0;
+ mp->shift[2] = 2 * mp->shift[1];
+ }
+ else
+ { /* Flatbed or TPU */
+ mp->shift[0] = 2 * mp->shift[1];
+ mp->shift[2] = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return (2 * mp->color_shift + mp->stripe_shift);
+}
+
+static int
+send_scan_param (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+ unsigned raw_width = calc_raw_width (mp, s->param);
+ unsigned h = MIN (s->param->h + calc_shifting (s),
+ s->cfg->height * s->param->ydpi / 75);
+
+ /* TPU scan does not support lineart */
+ if (is_scanning_from_tpu (s) && is_ccd_lineart (s))
+ {
+ return PIXMA_ENOTSUP;
+ }
+
+ if (mp->generation <= 2)
+ {
+ /*PDBG (pixma_dbg (4, "*send_scan_param gen. 1-2 ***** Setting: xdpi=%hi ydpi=%hi x=%i y=%i w=%i ***** \n",
+ s->param->xdpi,s->param->ydpi,(s->param->x)-(s->param->xs),s->param->y,raw_width));*/
+ data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x30, 0);
+ pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04);
+ pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06);
+ pixma_set_be32 (s->param->x, data + 0x08);
+ if (mp->generation == 2)
+ pixma_set_be32 (s->param->x - s->param->xs, data + 0x08);
+ pixma_set_be32 (s->param->y, data + 0x0c);
+ pixma_set_be32 (raw_width, data + 0x10);
+ pixma_set_be32 (h, data + 0x14);
+ data[0x18] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04;
+ data[0x19] = ((s->param->software_lineart) ? 8 : s->param->depth)
+ * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels); /* bits per pixel */
+ data[0x1a] = (is_scanning_from_tpu (s) ? 1 : 0);
+ data[0x20] = 0xff;
+ data[0x23] = 0x81;
+ data[0x26] = 0x02;
+ data[0x27] = 0x01;
+ }
+ else
+ {
+ data = pixma_newcmd (&mp->cb, cmd_scan_param_3, 0x38, 0);
+ data[0x00] = (is_scanning_from_adf (s)) ? 0x02 : 0x01;
+ data[0x01] = 0x01;
+ if (is_scanning_from_tpu (s))
+ {
+ data[0x00] = 0x04;
+ data[0x01] = 0x02;
+ data[0x1e] = 0x02;
+ }
+ data[0x02] = 0x01;
+ if (is_scanning_from_adfdup (s))
+ {
+ data[0x02] = 0x03;
+ data[0x03] = 0x03;
+ }
+ data[0x05] = 0x01; /* This one also seen at 0. Don't know yet what's used for */
+ pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x08);
+ pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x0a);
+ /*PDBG (pixma_dbg (4, "*send_scan_param gen. 3+ ***** Setting: xdpi=%hi ydpi=%hi x=%i y=%i w=%i ***** \n",
+ s->param->xdpi,s->param->ydpi,(s->param->x)-(s->param->xs),s->param->y,raw_width));*/
+ pixma_set_be32 (s->param->x - s->param->xs, data + 0x0c);
+ pixma_set_be32 (s->param->y, data + 0x10);
+ pixma_set_be32 (raw_width, data + 0x14);
+ pixma_set_be32 (h, data + 0x18);
+ data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04;
+
+#ifdef DEBUG_TPU_48
+ data[0x1d] = 24;
+#else
+ data[0x1d] = (is_scanning_from_tpu (s)) ? 48
+ : (((s->param->software_lineart) ? 8 : s->param->depth)
+ * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels)); /* bits per pixel */
+#endif
+
+ data[0x1f] = 0x01; /* This one also seen at 0. Don't know yet what's used for */
+ data[0x20] = 0xff;
+ data[0x21] = 0x81;
+ data[0x23] = 0x02;
+ data[0x24] = 0x01;
+
+ switch (s->cfg->pid)
+ {
+ case MG5300_PID:
+ /* unknown values (perhaps counter) for MG5300 series---values must be 0x30-0x39: decimal 0-9 */
+ data[0x26] = 0x32; /* using example values from a real scan here */
+ data[0x27] = 0x31;
+ data[0x28] = 0x34;
+ data[0x29] = 0x35;
+ break;
+
+ default:
+ break;
+ }
+
+ data[0x30] = 0x01;
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+query_status_3 (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+ int error, status_len;
+
+ status_len = 8;
+ data = pixma_newcmd (&mp->cb, cmd_status_3, 0, status_len);
+ RET_IF_ERR (pixma_exec (s, &mp->cb));
+ memcpy (mp->current_status, data, status_len);
+ return error;
+}
+
+static int
+query_status (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+ int error, status_len;
+
+ status_len = (mp->generation == 1) ? 12 : 16;
+ data = pixma_newcmd (&mp->cb, cmd_status, 0, status_len);
+ RET_IF_ERR (pixma_exec (s, &mp->cb));
+ memcpy (mp->current_status, data, status_len);
+ PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u busy=%u\n",
+ data[1], data[8], data[7], data[9]));
+ return error;
+}
+
+static int
+send_time (pixma_t * s)
+{
+ /* Why does a scanner need a time? */
+ time_t now;
+ struct tm *t;
+ uint8_t *data;
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
+ pixma_get_time (&now, NULL);
+ t = localtime (&now);
+ snprintf ((char *) data, 16,
+ "%02d/%02d/%02d %02d:%02d",
+ t->tm_year % 100, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min);
+ PDBG (pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
+ return pixma_exec (s, &mp->cb);
+}
+
+/* TODO: Simplify this function. Read the whole data packet in one shot. */
+static int
+read_image_block (pixma_t * s, uint8_t * header, uint8_t * data)
+{
+ uint8_t cmd[16];
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ const int hlen = 8 + 8;
+ int error, datalen;
+
+ memset (cmd, 0, sizeof (cmd));
+ pixma_set_be16 (cmd_read_image, cmd);
+ if ((mp->last_block & 0x20) == 0)
+ pixma_set_be32 ((IMAGE_BLOCK_SIZE / 65536) * 65536 + 8, cmd + 0xc);
+ else
+ pixma_set_be32 (32 + 8, cmd + 0xc);
+
+ mp->state = state_transfering;
+ mp->cb.reslen =
+ pixma_cmd_transaction (s, cmd, sizeof (cmd), mp->cb.buf, 512);
+ datalen = mp->cb.reslen;
+ if (datalen < 0)
+ return datalen;
+
+ memcpy (header, mp->cb.buf, hlen);
+ if (datalen >= hlen)
+ {
+ datalen -= hlen;
+ memcpy (data, mp->cb.buf + hlen, datalen);
+ data += datalen;
+ if (mp->cb.reslen == 512)
+ {
+ error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen);
+ RET_IF_ERR (error);
+ datalen += error;
+ }
+ }
+
+ mp->state = state_scanning;
+ mp->cb.expected_reslen = 0;
+ RET_IF_ERR (pixma_check_result (&mp->cb));
+ if (mp->cb.reslen < hlen)
+ return PIXMA_EPROTO;
+ return datalen;
+}
+
+static int
+read_error_info (pixma_t * s, void *buf, unsigned size)
+{
+ unsigned len = 16;
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len);
+ RET_IF_ERR (pixma_exec (s, &mp->cb));
+ if (buf && len < size)
+ {
+ size = len;
+ /* NOTE: I've absolutely no idea what the returned data mean. */
+ memcpy (buf, data, size);
+ error = len;
+ }
+ return error;
+}
+
+/*
+handle_interrupt() waits until it receives an interrupt packet or times out.
+It calls send_time() and query_status() if necessary. Therefore, make sure
+that handle_interrupt() is only called from a safe context for send_time()
+and query_status().
+
+ Returns:
+ 0 timed out
+ 1 an interrupt packet received
+ PIXMA_ECANCELED interrupted by signal
+ <0 error
+*/
+static int
+handle_interrupt (pixma_t * s, int timeout)
+{
+ uint8_t buf[64];
+ int len;
+
+ len = pixma_wait_interrupt (s->io, buf, sizeof (buf), timeout);
+ if (len == PIXMA_ETIMEDOUT)
+ return 0;
+ if (len < 0)
+ return len;
+ if (len%16) /* len must be a multiple of 16 bytes */
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING:unexpected interrupt packet length %d\n", len));
+ return PIXMA_EPROTO;
+ }
+
+ /* s->event = 0x0brroott
+ * b: button
+ * oo: original
+ * tt: target
+ * rr: scan resolution
+ * poll event with 'scanimage -A' */
+ if (s->cfg->pid == MG6200_PID
+ || s->cfg->pid == MG6300_PID)
+ /* button no. in buf[7]
+ * size in buf[10] 01=A4; 02=Letter; 08=10x15; 09=13x18; 0b=auto
+ * format in buf[11] 01=JPEG; 02=TIFF; 03=PDF; 04=Kompakt-PDF
+ * dpi in buf[12] 01=75; 02=150; 03=300; 04=600
+ * target = format; original = size; scan-resolution = dpi */
+ {
+ if (buf[7] & 1)
+ s->events = PIXMA_EV_BUTTON1 | buf[11] | buf[10]<<8 | buf[12]<<16; /* color scan */
+ if (buf[7] & 2)
+ s->events = PIXMA_EV_BUTTON2 | buf[11] | buf[10]<<8 | buf[12]<<16; /* b/w scan */
+ }
+ else
+ /* button no. in buf[0]
+ * original in buf[0]
+ * target in buf[1] */
+ {
+ /* More than one event can be reported at the same time. */
+ if (buf[3] & 1)
+ send_time (s);
+ if (buf[9] & 2)
+ query_status (s);
+ if (buf[0] & 2)
+ s->events = PIXMA_EV_BUTTON2 | buf[1] | ((buf[0] & 0xf0) << 4); /* b/w scan */
+ if (buf[0] & 1)
+ s->events = PIXMA_EV_BUTTON1 | buf[1] | ((buf[0] & 0xf0) << 4); /* color scan */
+ }
+ return 1;
+}
+
+static int
+init_ccd_lamp_3 (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ uint8_t *data;
+ int error, status_len, tmo;
+
+ status_len = 8;
+ RET_IF_ERR (query_status (s));
+ RET_IF_ERR (query_status (s));
+ RET_IF_ERR (send_cmd_start_calibrate_ccd_3 (s));
+ RET_IF_ERR (query_status (s));
+ tmo = 20; /* like Windows driver, CCD lamp adjustment */
+ while (--tmo >= 0)
+ {
+ data = pixma_newcmd (&mp->cb, cmd_end_calibrate_ccd_3, 0, status_len);
+ RET_IF_ERR (pixma_exec (s, &mp->cb));
+ memcpy (mp->current_status, data, status_len);
+ PDBG (pixma_dbg (3, "Lamp status: %u , timeout in: %u\n", data[0], tmo));
+ if (mp->current_status[0] == 3 || !is_scanning_from_tpu (s))
+ break;
+ WAIT_INTERRUPT (1000);
+ }
+ return error;
+}
+
+static int
+wait_until_ready (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ int error, tmo = 60;
+
+ RET_IF_ERR ((mp->generation >= 3) ? query_status_3 (s)
+ : query_status (s));
+ while (!is_calibrated (s))
+ {
+ WAIT_INTERRUPT (1000);
+ if (mp->generation >= 3)
+ RET_IF_ERR (query_status_3 (s));
+ else if (s->cfg->pid == MP600_PID ||
+ s->cfg->pid == MP600R_PID ||
+ s->cfg->pid == MP800R_PID)
+ RET_IF_ERR (query_status (s));
+ if (--tmo == 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING:Timed out in wait_until_ready()\n"));
+ PDBG (query_status (s));
+ return PIXMA_ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+static uint8_t *
+shift_colors (uint8_t * dptr, uint8_t * sptr,
+ unsigned w, unsigned dpi, unsigned pid, unsigned c,
+ int * colshft, unsigned strshft)
+{
+ unsigned i, sr, sg, sb, st;
+ UNUSED(dpi);
+ UNUSED(pid);
+ sr = colshft[0]; sg = colshft[1]; sb = colshft[2];
+ for (i = 0; i < w; i++)
+ {
+ /* stripes shift for MP800, MP800R at 2400 dpi */
+ st = (i % 2 == 0) ? strshft : 0;
+
+ *sptr++ = *(dptr++ + sr + st);
+ if (c == 6) *sptr++ = *(dptr++ + sr + st);
+ *sptr++ = *(dptr++ + sg + st);
+ if (c == 6) *sptr++ = *(dptr++ + sg + st);
+ *sptr++ = *(dptr++ + sb + st);
+ if (c == 6) *sptr++ = *(dptr++ + sb + st);
+ }
+ return dptr;
+}
+
+static void
+reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, unsigned n,
+ unsigned m, unsigned w, unsigned line_size)
+{
+ unsigned i;
+
+ for (i = 0; i < w; i++)
+ {
+ memcpy (linebuf + c * (n * (i % m) + i / m), sptr + c * i, c);
+ }
+ memcpy (sptr, linebuf, line_size);
+}
+
+#ifndef TPU_48
+static unsigned
+pack_48_24_bpc (uint8_t * sptr, unsigned n)
+{
+ unsigned i;
+ uint8_t *cptr, lsb;
+ static uint8_t offset = 0;
+
+ cptr = sptr;
+ if (n % 2 != 0)
+ PDBG (pixma_dbg (3, "WARNING: misaligned image.\n"));
+ for (i = 0; i < n; i += 2)
+ {
+ /* offset = 1 + (offset % 3); */
+ lsb = *sptr++;
+ *cptr++ = ((*sptr++) << offset) | lsb >> (8 - offset);
+ }
+ return (n / 2);
+}
+#endif
+
+/* This function deals both with PIXMA CCD sensors producing shifted color
+ * planes images, Grayscale CCD scan and Generation >= 3 high dpi images.
+ * Each complete line in mp->imgbuf is processed for shifting CCD sensor
+ * color planes, reordering pixels above 600 dpi for Generation >= 3, and
+ * converting to Grayscale for CCD sensors. */
+static unsigned
+post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ unsigned c, lines, line_size, n, m, cw, cx;
+ uint8_t *sptr, *dptr, *gptr, *cptr;
+
+ c = ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels)
+ * ((s->param->software_lineart) ? 8 : s->param->depth) / 8;
+ cw = c * s->param->w;
+ cx = c * s->param->xs;
+
+ if (mp->generation >= 3)
+ n = s->param->xdpi / 600;
+ else /* FIXME: maybe need different values for CIS and CCD sensors */
+ n = s->param->xdpi / 2400;
+
+ if (s->cfg->pid == MP600_PID || s->cfg->pid == MP600R_PID)
+ n = s->param->xdpi / 1200;
+
+ m = (n > 0) ? s->param->wx / n : 1;
+ sptr = dptr = gptr = cptr = mp->imgbuf;
+ line_size = get_cis_ccd_line_size (s);
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** ----- Set n=%u, m=%u, line_size=%u ----- ***** \n", n, m, line_size));*/
+
+ lines = (mp->data_left_ofs - mp->imgbuf) / line_size;
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** lines = %i > 2 * mp->color_shift + mp->stripe_shift = %i ***** \n",
+ lines, 2 * mp->color_shift + mp->stripe_shift));*/
+ if (lines > 2 * mp->color_shift + mp->stripe_shift)
+ {
+ unsigned i;
+
+ lines -= 2 * mp->color_shift + mp->stripe_shift;
+ for (i = 0; i < lines; i++, sptr += line_size)
+ {
+ /* Color plane and stripes shift needed by e.g. CCD */
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** Processing with c=%u, n=%u, m=%u, w=%i, line_size=%u ***** \n",
+ c, n, m, s->param->wx, line_size));*/
+ if (s->cfg->pid != MG5300_PID && s->cfg->pid != MG6300_PID && c >= 3)
+ dptr = shift_colors (dptr, sptr,
+ s->param->wx, s->param->xdpi, s->cfg->pid, c,
+ mp->shift, mp->stripe_shift);
+
+ /* special image format for *most* devices at high dpi.
+ * MP220, MX360, MX370, MX890, MG5300 are exceptions */
+ if (n > 0
+ && s->cfg->pid != MP220_PID
+ && s->cfg->pid != MX360_PID
+ && s->cfg->pid != MX370_PID
+ && s->cfg->pid != MX890_PID
+ && s->cfg->pid != MG3100_PID
+ && s->cfg->pid != MG2100_PID
+ && s->cfg->pid != MG5300_PID
+ && s->cfg->pid != MG6300_PID)
+ reorder_pixels (mp->linebuf, sptr, c, n, m, s->param->wx, line_size);
+
+ /* Crop line to selected borders */
+ memmove(cptr, sptr + cx, cw);
+
+ /* Color / Gray to Lineart convert */
+ if (s->param->software_lineart)
+ cptr = gptr = pixma_binarize_line (s->param, gptr, cptr, s->param->w, c);
+ /* Color to Grayscale convert for CCD sensor */
+ else if (is_ccd_grayscale (s))
+ cptr = gptr = pixma_rgb_to_gray (gptr, cptr, s->param->w, c);
+ else
+ cptr += cw;
+ }
+ }
+ ib->rptr = mp->imgbuf;
+ ib->rend = cptr;
+ return mp->data_left_ofs - sptr; /* # of non processed bytes */
+}
+
+static int
+mp150_open (pixma_t * s)
+{
+ mp150_t *mp;
+ uint8_t *buf;
+
+ mp = (mp150_t *) calloc (1, sizeof (*mp));
+ if (!mp)
+ return PIXMA_ENOMEM;
+
+ buf = (uint8_t *) malloc (CMDBUF_SIZE + IMAGE_BLOCK_SIZE);
+ if (!buf)
+ {
+ free (mp);
+ return PIXMA_ENOMEM;
+ }
+
+ s->subdriver = mp;
+ mp->state = state_idle;
+
+ mp->cb.buf = buf;
+ mp->cb.size = CMDBUF_SIZE;
+ mp->cb.res_header_len = 8;
+ mp->cb.cmd_header_len = 16;
+ mp->cb.cmd_len_field_ofs = 14;
+
+ mp->imgbuf = buf + CMDBUF_SIZE;
+
+ /* General rules for setting Pixma protocol generation # */
+ mp->generation = (s->cfg->pid >= MP160_PID) ? 2 : 1;
+
+ if (s->cfg->pid >= MX7600_PID)
+ mp->generation = 3;
+
+ if (s->cfg->pid >= MP250_PID)
+ mp->generation = 4;
+
+ /* And exceptions to be added here */
+ if (s->cfg->pid == MP140_PID)
+ mp->generation = 2;
+
+ /* TPU info data setup */
+ mp->tpu_datalen = 0;
+
+ if (mp->generation < 4)
+ {
+ query_status (s);
+ handle_interrupt (s, 200);
+ if (mp->generation == 3 && has_ccd_sensor (s))
+ send_cmd_start_calibrate_ccd_3 (s);
+ }
+ return 0;
+}
+
+static void
+mp150_close (pixma_t * s)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ mp150_finish_scan (s);
+ free (mp->cb.buf);
+ free (mp);
+ s->subdriver = NULL;
+}
+
+static int
+mp150_check_param (pixma_t * s, pixma_scan_param_t * sp)
+{
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ /* PDBG (pixma_dbg (4, "*mp150_check_param***** Initially: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n",
+ sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx)); */
+
+ /* MP150 only supports 8 bit per channel in color and grayscale mode */
+ if (sp->depth != 1)
+ {
+ sp->software_lineart = 0;
+ sp->depth = 8;
+#ifdef TPU_48
+#ifndef DEBUG_TPU_48
+ if (sp->source == PIXMA_SOURCE_TPU)
+#endif
+ sp->depth = 16; /* TPU in 16 bits mode */
+#endif
+ }
+ else
+ {
+ /* software lineart */
+ sp->software_lineart = 1;
+ sp->depth = 1;
+ sp->channels = 1;
+ }
+
+ /* for software lineart w must be a multiple of 8 */
+ if (sp->software_lineart == 1 && sp->w % 8)
+ {
+ unsigned w_max;
+
+ sp->w += 8 - (sp->w % 8);
+
+ /* do not exceed the scanner capability */
+ w_max = s->cfg->width * s->cfg->xdpi / 75;
+ w_max -= w_max % 8;
+ if (sp->w > w_max)
+ sp->w = w_max;
+ }
+
+ if (mp->generation >= 2)
+ {
+ /* mod 32 and expansion of the X scan limits */
+ /*PDBG (pixma_dbg (4, "*mp150_check_param***** ----- Initially: x=%i, y=%i, w=%i, h=%i *****\n", sp->x, sp->y, sp->w, sp->h));*/
+ sp->xs = (sp->x) % 32;
+ }
+ else
+ sp->xs = 0;
+ /*PDBG (pixma_dbg (4, "*mp150_check_param***** Selected origin, origin shift: %i, %i *****\n", sp->x, sp->xs));*/
+ sp->wx = calc_raw_width (mp, sp);
+ sp->line_size = sp->w * sp->channels * (((sp->software_lineart) ? 8 : sp->depth) / 8); /* bytes per line per color after cropping */
+ /*PDBG (pixma_dbg (4, "*mp150_check_param***** Final scan width and line-size: %i, %i *****\n", sp->wx, sp->line_size));*/
+
+ /* Some exceptions here for particular devices */
+ /* Those devices can scan up to 14" with ADF, but A4 11.7" in flatbed */
+ if (( s->cfg->pid == MX850_PID ||
+ s->cfg->pid == MX860_PID ||
+ s->cfg->pid == MX870_PID ||
+ s->cfg->pid == MX880_PID ||
+ s->cfg->pid == MX320_PID ||
+ s->cfg->pid == MX330_PID ||
+ s->cfg->pid == MX340_PID ||
+ s->cfg->pid == MX350_PID ||
+ s->cfg->pid == MX360_PID ||
+ s->cfg->pid == MX410_PID ||
+ s->cfg->pid == MX420_PID ||
+ s->cfg->pid == MX7600_PID )
+ &&
+ sp->source == PIXMA_SOURCE_FLATBED)
+ sp->h = MIN (sp->h, 877 * sp->xdpi / 75);
+
+ if (sp->source == PIXMA_SOURCE_TPU)
+ {
+ uint8_t k;
+
+ /* TPU mode: lowest res is 150 or 300 dpi */
+ if (mp->generation >= 3)
+ k = MAX (sp->xdpi, 300) / sp->xdpi;
+ else
+ k = MAX (sp->xdpi, 150) / sp->xdpi;
+ sp->x *= k;
+ sp->xs *= k;
+ sp->y *= k;
+ sp->w *= k;
+ sp->wx *= k;
+ sp->h *= k;
+ sp->xdpi *= k;
+ sp->ydpi = sp->xdpi;
+ }
+
+ if (sp->source == PIXMA_SOURCE_ADF || sp->source == PIXMA_SOURCE_ADFDUP)
+ {
+ uint8_t k = 1;
+
+ /* ADF/ADF duplex mode: max scan res is 600 dpi, at least for generation 4 */
+ if (mp->generation >= 4)
+ k = sp->xdpi / MIN (sp->xdpi, 600);
+ sp->x /= k;
+ sp->xs /= k;
+ sp->y /= k;
+ sp->w /= k;
+ sp->wx /= k;
+ sp->h /= k;
+ sp->xdpi /= k;
+ sp->ydpi = sp->xdpi;
+ }
+
+ /*PDBG (pixma_dbg (4, "*mp150_check_param***** Finally: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n",
+ sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx));*/
+ return 0;
+}
+
+static int
+mp150_scan (pixma_t * s)
+{
+ int error = 0, tmo;
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ if (mp->state != state_idle)
+ return PIXMA_EBUSY;
+
+ /* Generation 4: send XML dialog */
+ if (mp->generation == 4 && s->param->adf_pageid == 0)
+ {
+ if (!send_xml_dialog (s, XML_START_1))
+ return PIXMA_EPROTO;
+ if (!send_xml_dialog (s, XML_START_2))
+ return PIXMA_EPROTO;
+ }
+
+ /* clear interrupt packets buffer */
+ while (handle_interrupt (s, 0) > 0)
+ {
+ }
+
+ /* FIXME: Duplex ADF: check paper status only before odd pages (1,3,5,...). */
+ if (is_scanning_from_adf (s))
+ {
+ if ((error = query_status (s)) < 0)
+ return error;
+ tmo = 10;
+ while (!has_paper (s) && --tmo >= 0)
+ {
+ WAIT_INTERRUPT (1000);
+ PDBG (pixma_dbg
+ (2, "No paper in ADF. Timed out in %d sec.\n", tmo));
+ }
+ if (!has_paper (s))
+ return PIXMA_ENO_PAPER;
+ }
+
+ if (has_ccd_sensor (s) && (mp->generation <= 2))
+ {
+ error = send_cmd_e920 (s);
+ switch (error)
+ {
+ case PIXMA_ECANCELED:
+ case PIXMA_EBUSY:
+ PDBG (pixma_dbg (2, "cmd e920 or d520 returned %s\n",
+ pixma_strerror (error)));
+ /* fall through */
+ case 0:
+ query_status (s);
+ break;
+ default:
+ PDBG (pixma_dbg (1, "WARNING:cmd e920 or d520 failed %s\n",
+ pixma_strerror (error)));
+ return error;
+ }
+ tmo = 3; /* like Windows driver, CCD calibration ? */
+ while (--tmo >= 0)
+ {
+ WAIT_INTERRUPT (1000);
+ PDBG (pixma_dbg (2, "CCD Calibration ends in %d sec.\n", tmo));
+ }
+ /* pixma_sleep(2000000); */
+ }
+
+ tmo = 10;
+ if (s->param->adf_pageid == 0 || mp->generation <= 2)
+ {
+ error = start_session (s);
+ while (error == PIXMA_EBUSY && --tmo >= 0)
+ {
+ if (s->cancel)
+ {
+ error = PIXMA_ECANCELED;
+ break;
+ }
+ PDBG (pixma_dbg
+ (2, "Scanner is busy. Timed out in %d sec.\n", tmo + 1));
+ pixma_sleep (1000000);
+ error = start_session (s);
+ }
+ if (error == PIXMA_EBUSY || error == PIXMA_ETIMEDOUT)
+ {
+ /* The scanner maybe hangs. We try to empty output buffer of the
+ * scanner and issue the cancel command. */
+ PDBG (pixma_dbg (2, "Scanner hangs? Sending abort_session command.\n"));
+ drain_bulk_in (s);
+ abort_session (s);
+ pixma_sleep (500000);
+ error = start_session (s);
+ }
+ if ((error >= 0) || (mp->generation >= 3))
+ mp->state = state_warmup;
+ if ((error >= 0) && (mp->generation <= 2))
+ error = select_source (s);
+ if ((error >= 0) && (mp->generation >= 3) && has_ccd_sensor (s))
+ error = init_ccd_lamp_3 (s);
+ if ((error >= 0) && !is_scanning_from_tpu (s))
+ {
+ int i;
+
+ for (i = (mp->generation >= 3) ? 3 : 1 ; i > 0 && error >= 0; i--)
+ error = send_gamma_table (s);
+ }
+ else if (error >= 0) /* in TPU mode, for gen 1, 2, and 3 */
+ error = send_set_tpu_info (s);
+ }
+ else /* ADF pageid != 0 and gen3 or above */
+ pixma_sleep (1000000);
+
+ if ((error >= 0) || (mp->generation >= 3))
+ mp->state = state_warmup;
+ if (error >= 0)
+ error = send_scan_param (s);
+ if ((error >= 0) && (mp->generation >= 3))
+ error = start_scan_3 (s);
+ if (error < 0)
+ {
+ mp->last_block = 0x38; /* Force abort session if ADF scan */
+ mp150_finish_scan (s);
+ return error;
+ }
+ return 0;
+}
+
+static int
+mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ int error;
+ mp150_t *mp = (mp150_t *) s->subdriver;
+ unsigned block_size, bytes_received, proc_buf_size, line_size;
+ uint8_t header[16];
+
+ if (mp->state == state_warmup)
+ {
+ RET_IF_ERR (wait_until_ready (s));
+ pixma_sleep (1000000); /* No need to sleep, actually, but Window's driver
+ * sleep 1.5 sec. */
+ mp->state = state_scanning;
+ mp->last_block = 0;
+
+ line_size = get_cis_ccd_line_size (s);
+ proc_buf_size = (2 * calc_shifting (s) + 2) * line_size;
+ mp->cb.buf = realloc (mp->cb.buf,
+ CMDBUF_SIZE + IMAGE_BLOCK_SIZE + proc_buf_size);
+ if (!mp->cb.buf)
+ return PIXMA_ENOMEM;
+ mp->linebuf = mp->cb.buf + CMDBUF_SIZE;
+ mp->imgbuf = mp->data_left_ofs = mp->linebuf + line_size;
+ mp->data_left_len = 0;
+ }
+
+ do
+ {
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ if ((mp->last_block & 0x28) == 0x28)
+ { /* end of image */
+ mp->state = state_finished;
+ return 0;
+ }
+ /*PDBG (pixma_dbg (4, "*mp150_fill_buffer***** moving %u bytes into buffer *****\n", mp->data_left_len));*/
+ memmove (mp->imgbuf, mp->data_left_ofs, mp->data_left_len);
+ error = read_image_block (s, header, mp->imgbuf + mp->data_left_len);
+ if (error < 0)
+ {
+ if (error == PIXMA_ECANCELED)
+ {
+ /* NOTE: I see this in traffic logs but I don't know its meaning. */
+ read_error_info (s, NULL, 0);
+ }
+ return error;
+ }
+
+ bytes_received = error;
+ /*PDBG (pixma_dbg (4, "*mp150_fill_buffer***** %u bytes received by read_image_block *****\n", bytes_received));*/
+ block_size = pixma_get_be32 (header + 12);
+ mp->last_block = header[8] & 0x38;
+ if ((header[8] & ~0x38) != 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n"));
+ PDBG (pixma_hexdump (1, header, 16));
+ }
+ PASSERT (bytes_received == block_size);
+
+ if (block_size == 0)
+ { /* no image data at this moment. */
+ pixma_sleep (10000);
+ }
+ /* For TPU at 48 bits/pixel to output at 24 bits/pixel */
+#ifndef DEBUG_TPU_48
+#ifndef TPU_48
+#ifndef DEBUG_TPU_24
+ if (is_scanning_from_tpu (s))
+#endif
+ bytes_received = pack_48_24_bpc (mp->imgbuf + mp->data_left_len, bytes_received);
+#endif
+#endif
+ /* Post-process the image data */
+ mp->data_left_ofs = mp->imgbuf + mp->data_left_len + bytes_received;
+ mp->data_left_len = post_process_image_data (s, ib);
+ mp->data_left_ofs -= mp->data_left_len;
+ }
+ while (ib->rend == ib->rptr);
+
+ return ib->rend - ib->rptr;
+}
+
+static void
+mp150_finish_scan (pixma_t * s)
+{
+ int error;
+ mp150_t *mp = (mp150_t *) s->subdriver;
+
+ switch (mp->state)
+ {
+ case state_transfering:
+ drain_bulk_in (s);
+ /* fall through */
+ case state_scanning:
+ case state_warmup:
+ case state_finished:
+ /* Send the get TPU info message */
+ if (is_scanning_from_tpu (s) && mp->tpu_datalen == 0)
+ send_get_tpu_info_3 (s);
+ /* FIXME: to process several pages ADF scan, must not send
+ * abort_session and start_session between pages (last_block=0x28) */
+ if (mp->generation <= 2 || !is_scanning_from_adf (s) || mp->last_block == 0x38)
+ {
+ error = abort_session (s); /* FIXME: it probably doesn't work in duplex mode! */
+ if (error < 0)
+ PDBG (pixma_dbg (1, "WARNING:abort_session() failed %d\n", error));
+
+ /* Generation 4: send XML end of scan dialog */
+ if (mp->generation == 4)
+ {
+ if (!send_xml_dialog (s, XML_END))
+ PDBG (pixma_dbg (1, "WARNING:XML_END dialog failed \n"));
+ }
+ }
+ mp->state = state_idle;
+ /* fall through */
+ case state_idle:
+ break;
+ }
+}
+
+static void
+mp150_wait_event (pixma_t * s, int timeout)
+{
+ /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for
+ * instance. */
+ while (s->events == 0 && handle_interrupt (s, timeout) > 0)
+ {
+ }
+}
+
+static int
+mp150_get_status (pixma_t * s, pixma_device_status_t * status)
+{
+ int error;
+
+ RET_IF_ERR (query_status (s));
+ status->hardware = PIXMA_HARDWARE_OK;
+ status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER;
+ status->cal =
+ (is_calibrated (s)) ? PIXMA_CALIBRATION_OK : PIXMA_CALIBRATION_OFF;
+ return 0;
+}
+
+static const pixma_scan_ops_t pixma_mp150_ops = {
+ mp150_open,
+ mp150_close,
+ mp150_scan,
+ mp150_fill_buffer,
+ mp150_finish_scan,
+ mp150_wait_event,
+ mp150_check_param,
+ mp150_get_status
+};
+
+#define DEVICE(name, model, pid, dpi, adftpu_min_dpi, adftpu_max_dpi, w, h, cap) { \
+ name, /* name */ \
+ model, /* model */ \
+ CANON_VID, pid, /* vid pid */ \
+ 0, /* iface */ \
+ &pixma_mp150_ops, /* ops */ \
+ dpi, 2*(dpi), /* xdpi, ydpi */ \
+ adftpu_min_dpi, adftpu_max_dpi, /* adftpu_min_dpi, adftpu_max_dpi */ \
+ 0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
+ w, h, /* width, height */ \
+ PIXMA_CAP_EASY_RGB| \
+ PIXMA_CAP_GRAY| /* CIS with native grayscale and CCD with software grayscale */ \
+ PIXMA_CAP_LINEART| /* all scanners with software lineart */ \
+ PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \
+}
+
+#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0)
+
+const pixma_config_t pixma_mp150_devices[] = {
+ /* Generation 1: CIS */
+ DEVICE ("Canon PIXMA MP150", "MP150", MP150_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP170", "MP170", MP170_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP450", "MP450", MP450_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP500", "MP500", MP500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP530", "MP530", MP530_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+
+ /* Generation 1: CCD */
+ DEVICE ("Canon PIXMA MP800", "MP800", MP800_PID, 2400, 150, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP800R", "MP800R", MP800R_PID, 2400, 150, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP830", "MP830", MP830_PID, 2400, 150, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_ADFDUP),
+
+ /* Generation 2: CIS */
+ DEVICE ("Canon PIXMA MP140", "MP140", MP140_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP160", "MP160", MP160_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP180", "MP180", MP180_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP460", "MP460", MP460_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP510", "MP510", MP510_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP600", "MP600", MP600_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP600R", "MP600R", MP600R_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Generation 3: CIS */
+ DEVICE ("Canon PIXMA MP210", "MP210", MP210_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP220", "MP220", MP220_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP470", "MP470", MP470_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP520", "MP520", MP520_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP610", "MP610", MP610_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ DEVICE ("Canon PIXMA MX300", "MX300", MX300_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX310", "MX310", MX310_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX700", "MX700", MX700_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX850", "MX850", MX850_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX7600", "MX7600", MX7600_PID, 4800, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+
+ DEVICE ("Canon PIXMA MP630", "MP630", MP630_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP620", "MP620", MP620_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP540", "MP540", MP540_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP480", "MP480", MP480_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP240", "MP240", MP240_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP260", "MP260", MP260_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP190", "MP190", MP190_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* PIXMA 2009 vintage */
+ DEVICE ("Canon PIXMA MX320", "MX320", MX320_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX330", "MX330", MX330_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX860", "MX860", MX860_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+/* width and height adjusted to flatbed size 21.8 x 30.2 cm^2 respective
+ * Not sure if anything's going wrong here, leaving as is
+ DEVICE ("Canon PIXMA MX860", "MX860", MX860_PID, 2400, 0, 0, 638, 880, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),*/
+
+ /* PIXMA 2010 vintage */
+ DEVICE ("Canon PIXMA MX340", "MX340", MX340_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX350", "MX350", MX350_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX870", "MX870", MX870_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+
+ /* PIXMA 2011 vintage */
+ DEVICE ("Canon PIXMA MX360", "MX360", MX360_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX410", "MX410", MX410_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX420", "MX420", MX420_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX880 Series", "MX880", MX880_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+
+ /* Generation 4: CIS */
+ DEVICE ("Canon PIXMA MP640", "MP640", MP640_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP560", "MP560", MP560_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP550", "MP550", MP550_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP490", "MP490", MP490_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP250", "MP250", MP250_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP270", "MP270", MP270_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Latest devices (2010) Generation 4 CIS/CCD */
+ DEVICE ("Canon PIXMA MP280", "MP280", MP280_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS), /* TODO: 1200dpi doesn't work yet */
+ DEVICE ("Canon PIXMA MP495", "MP495", MP495_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5100", "MG5100", MG5100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5200", "MG5200", MG5200_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6100", "MG6100", MG6100_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Latest devices (2011) Generation 4 CIS/CCD */
+ DEVICE ("Canon PIXMA MG2100", "MG2100", MG2100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3100", "MG3100", MG3100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG4100", "MG4100", MG4100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5300", "MG5300", MG5300_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6200", "MG6200", MG6200_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP493", "MP493", MP493_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E500", "E500", E500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Latest devices (2012) Generation 4 CIS */
+ DEVICE ("Canon PIXMA MX370 Series", "MX370", MX370_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX430 Series", "MX430", MX430_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX510 Series", "MX510", MX510_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX710 Series", "MX710", MX710_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX890 Series", "MX890", MX890_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA E600 Series", "E600", E600_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MG4200", "MG4200", MG4200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Latest devices (2013) Generation 4 CIS */
+ DEVICE ("Canon PIXMA E510", "E510", E510_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E610", "E610", E610_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MP230", "MP230", MP230_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG2200 Series", "MG2200", MG2200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3200 Series", "MG3200", MG3200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5400 Series", "MG5400", MG5400_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6300 Series", "MG6300", MG6300_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX390 Series", "MX390", MX390_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX450 Series", "MX450", MX450_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX520 Series", "MX520", MX520_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX720 Series", "MX720", MX720_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX920 Series", "MX920", MX920_PID, 2400, 0, 600, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MG2400 Series", "MG2400", MG2400_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG2500 Series", "MG2500", MG2500_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3500 Series", "MG3500", MG3500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5500 Series", "MG5500", MG5500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6400 Series", "MG6400", MG6400_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6500 Series", "MG6500", MG6500_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG7100 Series", "MG7100", MG7100_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ END_OF_DEVICE_LIST
+};
diff --git a/backend/pixma_mp730.c b/backend/pixma_mp730.c
new file mode 100644
index 0000000..1980fcc
--- /dev/null
+++ b/backend/pixma_mp730.c
@@ -0,0 +1,828 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h> /* localtime(C90) */
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+#define IMAGE_BLOCK_SIZE (0xc000)
+#define CMDBUF_SIZE 512
+
+#define MP730_PID 0x262f
+#define MP700_PID 0x2630
+
+#define MP360_PID 0x263c
+#define MP370_PID 0x263d
+#define MP390_PID 0x263e
+#define MP375R_PID 0x263f /* untested */
+
+#define MP740_PID 0x264c /* Untested */
+#define MP710_PID 0x264d
+
+#define MF5730_PID 0x265d /* Untested */
+#define MF5750_PID 0x265e /* Untested */
+#define MF5770_PID 0x265f
+#define MF3110_PID 0x2660
+
+#define IR1020_PID 0x26e6
+
+enum mp730_state_t
+{
+ state_idle,
+ state_warmup,
+ state_scanning,
+ state_transfering,
+ state_finished
+};
+
+enum mp730_cmd_t
+{
+ cmd_start_session = 0xdb20,
+ cmd_select_source = 0xdd20,
+ cmd_gamma = 0xee20,
+ cmd_scan_param = 0xde20,
+ cmd_status = 0xf320,
+ cmd_abort_session = 0xef20,
+ cmd_time = 0xeb80,
+ cmd_read_image = 0xd420,
+ cmd_error_info = 0xff20,
+
+ cmd_activate = 0xcf60,
+ cmd_calibrate = 0xe920
+};
+
+typedef struct mp730_t
+{
+ enum mp730_state_t state;
+ pixma_cmdbuf_t cb;
+ unsigned raw_width;
+ uint8_t current_status[12];
+
+ uint8_t *buf, *imgbuf, *lbuf;
+ unsigned imgbuf_len;
+
+ unsigned last_block:1;
+} mp730_t;
+
+
+static void mp730_finish_scan (pixma_t * s);
+
+static int
+has_paper (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ return (mp->current_status[1] == 0);
+}
+
+static void
+drain_bulk_in (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) >= 0);
+}
+
+static int
+abort_session (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
+}
+
+static int
+query_status (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_status, 0, 12);
+ error = pixma_exec (s, &mp->cb);
+ if (error >= 0)
+ {
+ memcpy (mp->current_status, data, 12);
+ PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u\n",
+ data[1], data[8], data[7]));
+ }
+ return error;
+}
+
+static int
+activate (pixma_t * s, uint8_t x)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ uint8_t *data = pixma_newcmd (&mp->cb, cmd_activate, 10, 0);
+ data[0] = 1;
+ data[3] = x;
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+start_session (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session);
+}
+
+static int
+select_source (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ uint8_t *data = pixma_newcmd (&mp->cb, cmd_select_source, 10, 0);
+ switch (s->param->source)
+ {
+ case PIXMA_SOURCE_ADF:
+ data[0] = 2;
+ break;
+
+ case PIXMA_SOURCE_ADFDUP:
+ data[0] = 2;
+ data[5] = 3;
+ break;
+
+ default:
+ data[0] = 1;
+ break;
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+send_scan_param (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ uint8_t *data;
+
+ data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x2e, 0);
+ pixma_set_be16 (s->param->xdpi | 0x1000, data + 0x04);
+ pixma_set_be16 (s->param->ydpi | 0x1000, data + 0x06);
+ pixma_set_be32 (s->param->x, data + 0x08);
+ pixma_set_be32 (s->param->y, data + 0x0c);
+ pixma_set_be32 (mp->raw_width, data + 0x10);
+ pixma_set_be32 (s->param->h, data + 0x14);
+
+ if (s->param->channels == 1)
+ {
+ if (s->param->depth == 1)
+ data[0x18] = 0x01;
+ else
+ data[0x18] = 0x04;
+ }
+ else
+ data[0x18] = 0x08;
+
+ data[0x19] = s->param->channels * s->param->depth; /* bits per pixel, for lineart should be 0x01 */
+ data[0x1e] = (s->param->depth == 1) ? 0x80 : 0x00; /* modify for lineart: 0x80 NEW */
+ data[0x1f] = (s->param->depth == 1) ? 0x80 : 0x7f; /* modify for lineart: 0x80 */
+ data[0x20] = (s->param->depth == 1) ? 0x01 : 0xff; /* modify for lineart: 0x01 */
+ data[0x23] = 0x81;
+
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+calibrate (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_calibrate);
+}
+
+static int
+read_image_block (pixma_t * s, uint8_t * header, uint8_t * data)
+{
+ static const uint8_t cmd[10] = /* 0xd420 cmd */
+ { 0xd4, 0x20, 0, 0, 0, 0, 0, IMAGE_BLOCK_SIZE / 256, 4, 0 };
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ const int hlen = 2 + 4;
+ int error, datalen;
+
+ mp->state = state_transfering;
+ mp->cb.reslen =
+ pixma_cmd_transaction (s, cmd, sizeof (cmd), mp->cb.buf, 512);
+ datalen = mp->cb.reslen;
+ if (datalen < 0)
+ return datalen;
+
+ memcpy (header, mp->cb.buf, hlen);
+ if (datalen >= hlen)
+ {
+ datalen -= hlen;
+ memcpy (data, mp->cb.buf + hlen, datalen);
+ data += datalen;
+ if (mp->cb.reslen == 512)
+ {
+ error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen);
+ if (error < 0)
+ return error;
+ datalen += error;
+ }
+ }
+
+ mp->state = state_scanning;
+ mp->cb.expected_reslen = 0;
+ error = pixma_check_result (&mp->cb);
+ if (error < 0)
+ return error;
+ if (mp->cb.reslen < hlen)
+ return PIXMA_EPROTO;
+ return datalen;
+}
+
+static int
+send_time (pixma_t * s)
+{
+ /* Why does a scanner need a time? */
+ time_t now;
+ struct tm *t;
+ uint8_t *data;
+ mp730_t *mp = (mp730_t *) s->subdriver;
+
+ data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
+ pixma_get_time (&now, NULL);
+ t = localtime (&now);
+ snprintf ((char *) data, 16,
+ "%02d/%02d/%02d %02d:%02d",
+ t->tm_year % 100, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min);
+ PDBG (pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+handle_interrupt (pixma_t * s, int timeout)
+{
+ uint8_t buf[16];
+ int len;
+
+ len = pixma_wait_interrupt (s->io, buf, sizeof (buf), timeout);
+ if (len == PIXMA_ETIMEDOUT)
+ return 0;
+ if (len < 0)
+ return len;
+ switch (s->cfg->pid)
+ {
+ case MP360_PID:
+ case MP370_PID:
+ case MP375R_PID:
+ case MP390_PID:
+ case MF5730_PID:
+ case MF5750_PID:
+ case MF5770_PID:
+ case MF3110_PID:
+ case IR1020_PID:
+ if (len != 16)
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING:unexpected interrupt packet length %d\n", len));
+ return PIXMA_EPROTO;
+ }
+ if (buf[12] & 0x40)
+ query_status (s);
+ if (buf[10] & 0x40)
+ send_time (s);
+ /* FIXME: following is unverified! */
+ if (buf[15] & 1)
+ s->events = PIXMA_EV_BUTTON2; /* b/w scan */
+ if (buf[15] & 2)
+ s->events = PIXMA_EV_BUTTON1; /* color scan */
+ break;
+
+ case MP700_PID:
+ case MP730_PID:
+ case MP710_PID:
+ case MP740_PID:
+ if (len != 8)
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING:unexpected interrupt packet length %d\n", len));
+ return PIXMA_EPROTO;
+ }
+ if (buf[7] & 0x10)
+ s->events = PIXMA_EV_BUTTON1;
+ if (buf[5] & 8)
+ send_time (s);
+ break;
+
+ default:
+ PDBG (pixma_dbg (1, "WARNING:unknown interrupt, please report!\n"));
+ PDBG (pixma_hexdump (1, buf, len));
+ }
+ return 1;
+}
+
+static int
+has_ccd_sensor (pixma_t * s)
+{
+ return (s->cfg->pid == MP360_PID ||
+ s->cfg->pid == MP370_PID ||
+ s->cfg->pid == MP375R_PID ||
+ s->cfg->pid == MP390_PID ||
+ s->cfg->pid == MF5730_PID ||
+ s->cfg->pid == MF5750_PID ||
+ s->cfg->pid == MF5770_PID);
+}
+
+static int
+read_error_info (pixma_t * s, void *buf, unsigned size)
+{
+ unsigned len = 16;
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len);
+ error = pixma_exec (s, &mp->cb);
+ if (error < 0)
+ return error;
+ if (buf && len < size)
+ {
+ size = len;
+ /* NOTE: I've absolutely no idea what the returned data mean. */
+ memcpy (buf, data, size);
+ error = len;
+ }
+ return error;
+}
+
+static int
+step1 (pixma_t * s)
+{
+ int error;
+
+ error = query_status (s);
+ if (error < 0)
+ return error;
+ if ((s->param->source == PIXMA_SOURCE_ADF
+ || s->param->source == PIXMA_SOURCE_ADFDUP)
+ && !has_paper (s))
+ return PIXMA_ENO_PAPER;
+ if (has_ccd_sensor (s))
+ {
+ switch (s->cfg->pid)
+ {
+ case MF5730_PID:
+ case MF5750_PID:
+ case MF5770_PID:
+ /* MF57x0: Wait 10 sec before starting for 1st page only */
+ if (s->param->adf_pageid == 0)
+ {
+ int tmo = 10; /* like Windows driver, 10 sec CCD calibration ? */
+ while (--tmo >= 0)
+ {
+ error = handle_interrupt (s, 1000); \
+ if (s->cancel) \
+ return PIXMA_ECANCELED; \
+ if (error != PIXMA_ECANCELED && error < 0) \
+ return error;
+ PDBG (pixma_dbg (2, "CCD Calibration ends in %d sec.\n", tmo));
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ activate (s, 0);
+ error = calibrate (s);
+
+ switch (s->cfg->pid)
+ {
+ case MF5730_PID:
+ case MF5750_PID:
+ case MF5770_PID:
+ /* MF57x0: calibration returns PIXMA_STATUS_FAILED */
+ if (error == PIXMA_ECANCELED)
+ error = read_error_info (s, NULL, 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (error >= 0)
+ error = activate (s, 0);
+ if (error >= 0)
+ error = activate (s, 4);
+ return error;
+}
+
+static void
+pack_rgb (const uint8_t * src, unsigned nlines, unsigned w, uint8_t * dst)
+{
+ unsigned w2, stride;
+
+ w2 = 2 * w;
+ stride = 3 * w;
+ for (; nlines != 0; nlines--)
+ {
+ unsigned x;
+ for (x = 0; x != w; x++)
+ {
+ *dst++ = src[x + 0];
+ *dst++ = src[x + w];
+ *dst++ = src[x + w2];
+ }
+ src += stride;
+ }
+}
+
+static int
+mp730_open (pixma_t * s)
+{
+ mp730_t *mp;
+ uint8_t *buf;
+
+ mp = (mp730_t *) calloc (1, sizeof (*mp));
+ if (!mp)
+ return PIXMA_ENOMEM;
+
+ buf = (uint8_t *) malloc (CMDBUF_SIZE);
+ if (!buf)
+ {
+ free (mp);
+ return PIXMA_ENOMEM;
+ }
+
+ s->subdriver = mp;
+ mp->state = state_idle;
+
+ mp->cb.buf = buf;
+ mp->cb.size = CMDBUF_SIZE;
+ mp->cb.res_header_len = 2;
+ mp->cb.cmd_header_len = 10;
+ mp->cb.cmd_len_field_ofs = 7;
+
+ PDBG (pixma_dbg (3, "Trying to clear the interrupt buffer...\n"));
+ if (handle_interrupt (s, 200) == 0)
+ {
+ PDBG (pixma_dbg (3, " no packets in buffer\n"));
+ }
+ return 0;
+}
+
+static void
+mp730_close (pixma_t * s)
+{
+ mp730_t *mp = (mp730_t *) s->subdriver;
+
+ mp730_finish_scan (s);
+ free (mp->cb.buf);
+ free (mp->buf);
+ free (mp);
+ s->subdriver = NULL;
+}
+
+static unsigned
+calc_raw_width (pixma_t * s, const pixma_scan_param_t * sp)
+{
+ unsigned raw_width;
+ /* FIXME: Does MP730 need the alignment? */
+ /* TODO test: MP710/740 */
+ if (sp->channels == 1)
+ {
+ if (sp->depth == 8) /* grayscale */
+ {
+ if (s->cfg->pid == MP700_PID ||
+ s->cfg->pid == MP730_PID ||
+ s->cfg->pid == MP360_PID ||
+ s->cfg->pid == MP370_PID ||
+ s->cfg->pid == MP375R_PID ||
+ s->cfg->pid == MP390_PID ||
+ s->cfg->pid == IR1020_PID)
+ raw_width = ALIGN_SUP (sp->w, 4);
+ else
+ raw_width = ALIGN_SUP (sp->w, 12);
+ }
+ else /* depth = 1 : LINEART */
+ raw_width = ALIGN_SUP (sp->w, 16);
+ }
+ else
+ raw_width = ALIGN_SUP (sp->w, 4);
+ return raw_width;
+}
+
+static int
+mp730_check_param (pixma_t * s, pixma_scan_param_t * sp)
+{
+ uint8_t k = 1;
+
+ /* check if channels is 3, or if depth is 1 then channels also 1 else set depth to 8 */
+ if ((sp->channels==3) || !(sp->channels==1 && sp->depth==1))
+ {
+ sp->depth=8;
+ }
+ /* for MP360/370, MP700/730 in grayscale & lineart modes, max scan res is 600 dpi */
+ if (s->cfg->pid == MP700_PID ||
+ s->cfg->pid == MP730_PID ||
+ s->cfg->pid == MP360_PID ||
+ s->cfg->pid == MP370_PID ||
+ s->cfg->pid == MP375R_PID ||
+ s->cfg->pid == MP390_PID)
+ {
+ if (sp->channels == 1)
+ k = sp->xdpi / MIN (sp->xdpi, 600);
+ }
+
+ sp->x /= k;
+ sp->y /= k;
+ sp->h /= k;
+ sp->xdpi /= k;
+ sp->ydpi = sp->xdpi;
+
+ sp->w = calc_raw_width (s, sp);
+ sp->w /= k;
+ sp->line_size = (calc_raw_width (s, sp) * sp->channels * sp->depth) / 8;
+
+ return 0;
+}
+
+static int
+mp730_scan (pixma_t * s)
+{
+ int error, n;
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ uint8_t *buf;
+
+ if (mp->state != state_idle)
+ return PIXMA_EBUSY;
+
+ /* clear interrupt packets buffer */
+ while (handle_interrupt (s, 0) > 0)
+ {
+ }
+
+ mp->raw_width = calc_raw_width (s, s->param);
+ PDBG (pixma_dbg (3, "raw_width = %u\n", mp->raw_width));
+
+ n = IMAGE_BLOCK_SIZE / s->param->line_size + 1;
+ buf = (uint8_t *) malloc ((n + 1) * s->param->line_size + IMAGE_BLOCK_SIZE);
+ if (!buf)
+ return PIXMA_ENOMEM;
+ mp->buf = buf;
+ mp->lbuf = buf;
+ mp->imgbuf = buf + n * s->param->line_size;
+ mp->imgbuf_len = 0;
+
+ error = step1 (s);
+ if (error >= 0)
+ error = start_session (s);
+ if (error >= 0)
+ mp->state = state_scanning;
+ if (error >= 0)
+ error = select_source (s);
+ if (error >= 0)
+ error = send_scan_param (s);
+ if (error < 0)
+ {
+ mp730_finish_scan (s);
+ return error;
+ }
+ mp->last_block = 0;
+ return 0;
+}
+
+static int
+mp730_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ int error, n;
+ mp730_t *mp = (mp730_t *) s->subdriver;
+ unsigned block_size, bytes_received;
+ uint8_t header[16];
+
+ do
+ {
+ do
+ {
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ if (mp->last_block) /* end of image */
+ return 0;
+
+ error = read_image_block (s, header, mp->imgbuf + mp->imgbuf_len);
+ if (error < 0)
+ return error;
+
+ bytes_received = error;
+ block_size = pixma_get_be16 (header + 4);
+ mp->last_block = ((header[2] & 0x28) == 0x28);
+ if (mp->last_block)
+ { /* end of image */
+ mp->state = state_finished;
+ }
+ if ((header[2] & ~0x38) != 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n"));
+ PDBG (pixma_hexdump (1, header, 16));
+ }
+ PASSERT (bytes_received == block_size);
+
+ if (block_size == 0)
+ {
+ /* no image data at this moment. */
+ /*pixma_sleep(100000); *//* FIXME: too short, too long? */
+ handle_interrupt (s, 100);
+ }
+ }
+ while (block_size == 0);
+
+ /* TODO: simplify! */
+ mp->imgbuf_len += bytes_received;
+ n = mp->imgbuf_len / s->param->line_size;
+ /* n = number of full lines (rows) we have in the buffer. */
+ if (n != 0)
+ {
+ if (s->param->channels != 1 &&
+ s->cfg->pid != MF5730_PID &&
+ s->cfg->pid != MF5750_PID &&
+ s->cfg->pid != MF5770_PID &&
+ s->cfg->pid != MF3110_PID &&
+ s->cfg->pid != IR1020_PID)
+ {
+ /* color, and not an MF57x0 nor MF3110 */
+ pack_rgb (mp->imgbuf, n, mp->raw_width, mp->lbuf);
+ }
+ else
+ /* grayscale/lineart or MF57x0 or MF3110 */
+ memcpy (mp->lbuf, mp->imgbuf, n * s->param->line_size);
+
+ block_size = n * s->param->line_size;
+ mp->imgbuf_len -= block_size;
+ memcpy (mp->imgbuf, mp->imgbuf + block_size, mp->imgbuf_len);
+ }
+ }
+ while (n == 0);
+
+ ib->rptr = mp->lbuf;
+ ib->rend = mp->lbuf + block_size;
+ return ib->rend - ib->rptr;
+}
+
+static void
+mp730_finish_scan (pixma_t * s)
+{
+ int error, aborted = 0;
+ mp730_t *mp = (mp730_t *) s->subdriver;
+
+ switch (mp->state)
+ {
+ case state_transfering:
+ drain_bulk_in (s);
+ /* fall through */
+ case state_scanning:
+ case state_warmup:
+ aborted = 1;
+ error = abort_session (s);
+ if (error < 0)
+ PDBG (pixma_dbg
+ (1, "WARNING:abort_session() failed %s\n",
+ pixma_strerror (error)));
+ /* fall through */
+ case state_finished:
+ query_status (s);
+ query_status (s);
+ activate (s, 0);
+
+ if (! aborted && s->cfg->pid == IR1020_PID)
+ {
+ error = abort_session (s);
+ if (error < 0)
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING:abort_session() failed %s\n",
+ pixma_strerror (error)));
+ query_status (s);
+ query_status (s);
+ activate (s, 0);
+ }
+ }
+ mp->buf = mp->lbuf = mp->imgbuf = NULL;
+ mp->state = state_idle;
+ /* fall through */
+ case state_idle:
+ break;
+ }
+}
+
+static void
+mp730_wait_event (pixma_t * s, int timeout)
+{
+ /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for
+ * instance. */
+ while (s->events == 0 && handle_interrupt (s, timeout) > 0)
+ {
+ }
+}
+
+static int
+mp730_get_status (pixma_t * s, pixma_device_status_t * status)
+{
+ int error;
+
+ error = query_status (s);
+ if (error < 0)
+ return error;
+ status->hardware = PIXMA_HARDWARE_OK;
+ status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER;
+ return 0;
+}
+
+
+static const pixma_scan_ops_t pixma_mp730_ops = {
+ mp730_open,
+ mp730_close,
+ mp730_scan,
+ mp730_fill_buffer,
+ mp730_finish_scan,
+ mp730_wait_event,
+ mp730_check_param,
+ mp730_get_status
+};
+
+/* TODO: implement adftpu_min_dpi & adftpu_max_dpi for grayscale & lineart */
+#define DEVICE(name, model, pid, dpi, w, h, cap) { \
+ name, /* name */ \
+ model, /* model */ \
+ 0x04a9, pid, /* vid pid */ \
+ 1, /* iface */ \
+ &pixma_mp730_ops, /* ops */ \
+ dpi, dpi, /* xdpi, ydpi */ \
+ 0, 0, /* adftpu_min_dpi & adftpu_max_dpi not used in this subdriver */ \
+ 0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
+ w, h, /* width, height */ \
+ PIXMA_CAP_GRAY|PIXMA_CAP_EVENTS|cap \
+}
+const pixma_config_t pixma_mp730_devices[] = {
+/* TODO: check area limits */
+ DEVICE ("PIXMA MP360", "MP360", MP360_PID, 1200, 636, 868, PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP370", "MP370", MP370_PID, 1200, 636, 868, PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP375R", "MP375R", MP375R_PID, 1200, 636, 868, PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP390", "MP390", MP390_PID, 1200, 636, 868, PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP700", "MP700", MP700_PID, 1200, 638, 877 /*1035 */ , PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP710", "MP710", MP710_PID, 1200, 637, 868, PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP730", "MP730", MP730_PID, 1200, 637, 868, PIXMA_CAP_ADF | PIXMA_CAP_LINEART),
+ DEVICE ("PIXMA MP740", "MP740", MP740_PID, 1200, 637, 868, PIXMA_CAP_ADF | PIXMA_CAP_LINEART),
+
+ DEVICE ("Canon imageCLASS MF5730", "MF5730", MF5730_PID, 1200, 636, 868, PIXMA_CAP_ADF),
+ DEVICE ("Canon imageCLASS MF5750", "MF5750", MF5750_PID, 1200, 636, 868, PIXMA_CAP_ADF),
+ DEVICE ("Canon imageCLASS MF5770", "MF5770", MF5770_PID, 1200, 636, 868, PIXMA_CAP_ADF),
+ DEVICE ("Canon imageCLASS MF3110", "MF3110", MF3110_PID, 600, 636, 868, 0),
+
+ DEVICE ("Canon iR 1020/1024/1025", "iR1020", IR1020_PID, 600, 636, 868, PIXMA_CAP_ADFDUP),
+
+ DEVICE (NULL, NULL, 0, 0, 0, 0, 0)
+};
diff --git a/backend/pixma_mp750.c b/backend/pixma_mp750.c
new file mode 100644
index 0000000..ea35851
--- /dev/null
+++ b/backend/pixma_mp750.c
@@ -0,0 +1,970 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+
+/****************************************************************************
+ * Credits should go to Martin Schewe (http://pixma.schewe.com) who analysed
+ * the protocol of MP750.
+ ****************************************************************************/
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+
+/* TODO: remove lines marked with SIM. They are inserted so that I can test
+ the subdriver with the simulator. WY */
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+#define IMAGE_BLOCK_SIZE 0xc000
+#define CMDBUF_SIZE 512
+#define HAS_PAPER(s) (s[1] == 0)
+#define IS_WARMING_UP(s) (s[7] != 3)
+#define IS_CALIBRATED(s) (s[8] == 0xf)
+
+#define MP750_PID 0x1706
+#define MP760_PID 0x1708
+#define MP780_PID 0x1707
+
+
+enum mp750_state_t
+{
+ state_idle,
+ state_warmup,
+ state_scanning,
+ state_transfering,
+ state_finished
+};
+
+enum mp750_cmd_t
+{
+ cmd_start_session = 0xdb20,
+ cmd_select_source = 0xdd20,
+ cmd_scan_param = 0xde20,
+ cmd_status = 0xf320,
+ cmd_abort_session = 0xef20,
+ cmd_time = 0xeb80,
+ cmd_read_image = 0xd420,
+
+ cmd_activate = 0xcf60,
+ cmd_calibrate = 0xe920,
+ cmd_error_info = 0xff20
+};
+
+typedef struct mp750_t
+{
+ enum mp750_state_t state;
+ pixma_cmdbuf_t cb;
+ unsigned raw_width, raw_height;
+ uint8_t current_status[12];
+
+ uint8_t *buf, *rawimg, *img;
+ /* make new buffer for rgb_to_gray to act on */
+ uint8_t *imgcol;
+ unsigned line_size; /* need in 2 functions */
+
+ unsigned rawimg_left, imgbuf_len, last_block_size, imgbuf_ofs;
+ int shifted_bytes;
+ int stripe_shift; /* for 2400dpi */
+ unsigned last_block;
+
+ unsigned monochrome:1;
+ unsigned needs_abort:1;
+} mp750_t;
+
+
+
+static void mp750_finish_scan (pixma_t * s);
+static void check_status (pixma_t * s);
+
+static int
+has_paper (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ return HAS_PAPER (mp->current_status);
+}
+
+static int
+is_warming_up (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ return IS_WARMING_UP (mp->current_status);
+}
+
+static int
+is_calibrated (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ return IS_CALIBRATED (mp->current_status);
+}
+
+static void
+drain_bulk_in (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ while (pixma_read (s->io, mp->buf, IMAGE_BLOCK_SIZE) >= 0);
+}
+
+static int
+abort_session (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
+}
+
+static int
+query_status (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_status, 0, 12);
+ error = pixma_exec (s, &mp->cb);
+ if (error >= 0)
+ {
+ memcpy (mp->current_status, data, 12);
+ PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u\n",
+ data[1], data[8], data[7]));
+ }
+ return error;
+}
+
+static int
+activate (pixma_t * s, uint8_t x)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ uint8_t *data = pixma_newcmd (&mp->cb, cmd_activate, 10, 0);
+ data[0] = 1;
+ data[3] = x;
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+activate_cs (pixma_t * s, uint8_t x)
+{
+ /*SIM*/ check_status (s);
+ return activate (s, x);
+}
+
+static int
+start_session (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session);
+}
+
+static int
+select_source (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ uint8_t *data = pixma_newcmd (&mp->cb, cmd_select_source, 10, 0);
+ data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1;
+ data[1] = 1;
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+has_ccd_sensor (pixma_t * s)
+{
+ return ((s->cfg->cap & PIXMA_CAP_CCD) != 0);
+}
+
+static int
+is_ccd_grayscale (pixma_t * s)
+{
+ return (has_ccd_sensor (s) && (s->param->channels == 1));
+}
+
+/* CCD sensors don't have a Grayscale mode, but use color mode instead */
+static unsigned
+get_cis_ccd_line_size (pixma_t * s)
+{
+ return (s->param->wx ? s->param->line_size / s->param->w * s->param->wx
+ : s->param->line_size) * ((is_ccd_grayscale (s)) ? 3 : 1);
+}
+
+static int
+send_scan_param (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ uint8_t *data;
+
+ data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x2e, 0);
+ pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04);
+ pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06);
+ pixma_set_be32 (s->param->x, data + 0x08);
+ pixma_set_be32 (s->param->y, data + 0x0c);
+ pixma_set_be32 (mp->raw_width, data + 0x10);
+ pixma_set_be32 (mp->raw_height, data + 0x14);
+ data[0x18] = 8; /* 8 = color, 4 = grayscale(?) */
+ /* GH: No, there is no grayscale for CCD devices, Windows shows same */
+ data[0x19] = s->param->depth * ((is_ccd_grayscale (s)) ? 3 : s->param->channels); /* bits per pixel */
+ data[0x20] = 0xff;
+ data[0x23] = 0x81;
+ data[0x26] = 0x02;
+ data[0x27] = 0x01;
+ data[0x29] = mp->monochrome ? 0 : 1;
+
+ return pixma_exec (s, &mp->cb);
+}
+
+static int
+calibrate (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_calibrate);
+}
+
+static int
+calibrate_cs (pixma_t * s)
+{
+ /*SIM*/ check_status (s);
+ return calibrate (s);
+}
+
+static int
+request_image_block_ex (pixma_t * s, unsigned *size, uint8_t * info,
+ unsigned flag)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ int error;
+
+ memset (mp->cb.buf, 0, 10);
+ pixma_set_be16 (cmd_read_image, mp->cb.buf);
+ mp->cb.buf[7] = *size >> 8;
+ mp->cb.buf[8] = 4 | flag;
+ mp->cb.reslen = pixma_cmd_transaction (s, mp->cb.buf, 10, mp->cb.buf, 6);
+ mp->cb.expected_reslen = 0;
+ error = pixma_check_result (&mp->cb);
+ if (error >= 0)
+ {
+ if (mp->cb.reslen == 6)
+ {
+ *info = mp->cb.buf[2];
+ *size = pixma_get_be16 (mp->cb.buf + 4);
+ }
+ else
+ {
+ error = PIXMA_EPROTO;
+ }
+ }
+ return error;
+}
+
+static int
+request_image_block (pixma_t * s, unsigned *size, uint8_t * info)
+{
+ return request_image_block_ex (s, size, info, 0);
+}
+
+static int
+request_image_block2 (pixma_t * s, uint8_t * info)
+{
+ unsigned temp = 0;
+ return request_image_block_ex (s, &temp, info, 0x20);
+}
+
+static int
+read_image_block (pixma_t * s, uint8_t * data)
+{
+ int count, temp;
+
+ count = pixma_read (s->io, data, IMAGE_BLOCK_SIZE);
+ if (count < 0)
+ return count;
+ if (count == IMAGE_BLOCK_SIZE)
+ {
+ int error = pixma_read (s->io, &temp, 0);
+ if (error < 0)
+ {
+ PDBG (pixma_dbg
+ (1, "WARNING: reading zero-length packet failed %d\n", error));
+ }
+ }
+ return count;
+}
+
+static int
+read_error_info (pixma_t * s, void *buf, unsigned size)
+{
+ unsigned len = 16;
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len);
+ error = pixma_exec (s, &mp->cb);
+ if (error >= 0 && buf)
+ {
+ if (len < size)
+ size = len;
+ /* NOTE: I've absolutely no idea what the returned data mean. */
+ memcpy (buf, data, size);
+ error = len;
+ }
+ return error;
+}
+
+static int
+send_time (pixma_t * s)
+{
+ /* TODO: implement send_time() */
+ UNUSED (s);
+ PDBG (pixma_dbg (3, "send_time() is not yet implemented.\n"));
+ return 0;
+}
+
+static int
+handle_interrupt (pixma_t * s, int timeout)
+{
+ int error;
+ uint8_t intr[16];
+
+ error = pixma_wait_interrupt (s->io, intr, sizeof (intr), timeout);
+ if (error == PIXMA_ETIMEDOUT)
+ return 0;
+ if (error < 0)
+ return error;
+ if (error != 16)
+ {
+ PDBG (pixma_dbg (1, "WARNING: unexpected interrupt packet length %d\n",
+ error));
+ return PIXMA_EPROTO;
+ }
+
+ if (intr[10] & 0x40)
+ send_time (s);
+ if (intr[12] & 0x40)
+ query_status (s);
+ if (intr[15] & 1)
+ s->events = PIXMA_EV_BUTTON2; /* b/w scan */
+ if (intr[15] & 2)
+ s->events = PIXMA_EV_BUTTON1; /* color scan */
+ return 1;
+}
+
+static void
+check_status (pixma_t * s)
+{
+ while (handle_interrupt (s, 0) > 0);
+}
+
+static int
+step1 (pixma_t * s)
+{
+ int error, tmo;
+
+ error = activate (s, 0);
+ if (error < 0)
+ return error;
+ error = query_status (s);
+ if (error < 0)
+ return error;
+ if (s->param->source == PIXMA_SOURCE_ADF && !has_paper (s))
+ return PIXMA_ENO_PAPER;
+ error = activate_cs (s, 0);
+ /*SIM*/ if (error < 0)
+ return error;
+ error = activate_cs (s, 0x20);
+ if (error < 0)
+ return error;
+
+ tmo = 60;
+ error = calibrate_cs (s);
+ while (error == PIXMA_EBUSY && --tmo >= 0)
+ {
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ PDBG (pixma_dbg
+ (2, "Scanner is busy. Timed out in %d sec.\n", tmo + 1));
+ pixma_sleep (1000000);
+ error = calibrate_cs (s);
+ }
+ return error;
+}
+
+static void
+shift_rgb (const uint8_t * src, unsigned pixels,
+ int sr, int sg, int sb, int stripe_shift,
+ int line_size, uint8_t * dst)
+{
+ unsigned st;
+
+ for (; pixels != 0; pixels--)
+ {
+ st = (pixels % 2 == 0) ? -2 * stripe_shift * line_size : 0;
+ *(dst++ + sr + st) = *src++;
+ *(dst++ + sg + st) = *src++;
+ *(dst++ + sb + st) = *src++;
+ }
+}
+
+static uint8_t *
+rgb_to_gray (uint8_t * gptr, const uint8_t * cptr, unsigned pixels, unsigned c)
+{
+ unsigned i, j, g;
+
+ /* gptr: destination gray scale buffer */
+ /* cptr: source color scale buffer */
+ /* c: 3 for 3-channel single-byte data, 6 for double-byte data */
+
+ for (i=0; i < pixels; i++)
+ {
+ for (j = 0, g = 0; j < 3; j++)
+ {
+ g += *cptr++;
+ if (c == 6) g += (*cptr++ << 8);
+ }
+ g /= 3;
+ *gptr++ = g;
+ if (c == 6) *gptr++ = (g >> 8);
+ }
+ return gptr;
+}
+
+static int
+calc_component_shifting (pixma_t * s)
+{
+ switch (s->cfg->pid)
+ {
+ case MP760_PID:
+ switch (s->param->ydpi)
+ {
+ case 300:
+ return 3;
+ case 600:
+ return 6;
+ default:
+ return s->param->ydpi / 75;
+ }
+ /* never reached */
+ break;
+
+ case MP750_PID:
+ case MP780_PID:
+ default:
+ return 2 * s->param->ydpi / 75;
+ }
+}
+
+static void
+workaround_first_command (pixma_t * s)
+{
+ /* FIXME: Send a dummy command because the device doesn't response to the
+ first command that is sent directly after the USB interface has been
+ set up. Why? USB isn't set up properly? */
+ uint8_t cmd[10];
+ int error;
+
+ if (s->cfg->pid == MP750_PID)
+ return; /* MP750 doesn't have this problem(?) */
+
+ PDBG (pixma_dbg
+ (1,
+ "Work-around for the problem: device doesn't response to the first command.\n"));
+ memset (cmd, 0, sizeof (cmd));
+ pixma_set_be16 (cmd_calibrate, cmd);
+ error = pixma_write (s->io, cmd, 10);
+ if (error != 10)
+ {
+ if (error < 0)
+ {
+ PDBG (pixma_dbg
+ (1, " Sending a dummy command failed: %s\n",
+ pixma_strerror (error)));
+ }
+ else
+ {
+ PDBG (pixma_dbg
+ (1, " Sending a dummy command failed: count = %d\n", error));
+ }
+ return;
+ }
+ error = pixma_read (s->io, cmd, sizeof (cmd));
+ if (error >= 0)
+ {
+ PDBG (pixma_dbg
+ (1, " Got %d bytes response from a dummy command.\n", error));
+ }
+ else
+ {
+ PDBG (pixma_dbg
+ (1, " Reading response of a dummy command failed: %s\n",
+ pixma_strerror (error)));
+ }
+}
+
+static int
+mp750_open (pixma_t * s)
+{
+ mp750_t *mp;
+ uint8_t *buf;
+
+ mp = (mp750_t *) calloc (1, sizeof (*mp));
+ if (!mp)
+ return PIXMA_ENOMEM;
+
+ buf = (uint8_t *) malloc (CMDBUF_SIZE);
+ if (!buf)
+ {
+ free (mp);
+ return PIXMA_ENOMEM;
+ }
+
+ s->subdriver = mp;
+ mp->state = state_idle;
+
+ /* ofs: 0 1 2 3 4 5 6 7 8 9
+ cmd: cmd1 cmd2 00 00 00 00 00 00 00 00
+ data length-^^^^^ => cmd_len_field_ofs
+ |--------- cmd_header_len --------|
+
+ res: res1 res2
+ |---------| res_header_len
+ */
+ mp->cb.buf = buf;
+ mp->cb.size = CMDBUF_SIZE;
+ mp->cb.res_header_len = 2;
+ mp->cb.cmd_header_len = 10;
+ mp->cb.cmd_len_field_ofs = 7;
+
+ handle_interrupt (s, 200);
+ workaround_first_command (s);
+ return 0;
+}
+
+static void
+mp750_close (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+
+ mp750_finish_scan (s);
+ free (mp->cb.buf);
+ free (mp);
+ s->subdriver = NULL;
+}
+
+static int
+mp750_check_param (pixma_t * s, pixma_scan_param_t * sp)
+{
+ unsigned raw_width;
+
+ UNUSED (s);
+
+ sp->depth = 8; /* FIXME: Does MP750 supports other depth? */
+
+ /* GH: my implementation */
+ /* if ((sp->channels == 3) || (is_ccd_grayscale (s)))
+ raw_width = ALIGN_SUP (sp->w, 4);
+ else
+ raw_width = ALIGN_SUP (sp->w, 12);*/
+
+ /* the above code gives segmentation fault?!? why... it seems to work in the mp750_scan function */
+ raw_width = ALIGN_SUP (sp->w, 4);
+
+ /*sp->line_size = raw_width * sp->channels;*/
+ sp->line_size = raw_width * sp->channels * (sp->depth / 8); /* no cropping? */
+ return 0;
+}
+
+static int
+mp750_scan (pixma_t * s)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ int error;
+ uint8_t *buf;
+ unsigned size, dpi, spare;
+
+ dpi = s->param->ydpi;
+ /* add a stripe shift for 2400dpi */
+ mp->stripe_shift = (dpi == 2400) ? 4 : 0;
+
+ if (mp->state != state_idle)
+ return PIXMA_EBUSY;
+
+ /* clear interrupt packets buffer */
+ while (handle_interrupt (s, 0) > 0)
+ {
+ }
+
+ /* if (s->param->channels == 1)
+ mp->raw_width = ALIGN_SUP (s->param->w, 12);
+ else
+ mp->raw_width = ALIGN_SUP (s->param->w, 4);*/
+
+ /* change to use CCD grayscale mode --- why does this give segmentation error at runtime in mp750_check_param? */
+ if ((s->param->channels == 3) || (is_ccd_grayscale (s)))
+ mp->raw_width = ALIGN_SUP (s->param->w, 4);
+ else
+ mp->raw_width = ALIGN_SUP (s->param->w, 12);
+ /* not sure about MP750, but there is no need for aligning at 12 for the MP760/770, MP780/790 since always use CCD color mode */
+
+ /* modify for stripe shift */
+ spare = 2 * calc_component_shifting (s) + 2 * mp->stripe_shift; /* FIXME: or maybe (2*... + 1)? */
+ mp->raw_height = s->param->h + spare;
+ PDBG (pixma_dbg (3, "raw_width=%u raw_height=%u dpi=%u\n",
+ mp->raw_width, mp->raw_height, dpi));
+
+ /* PDBG (pixma_dbg (4, "line_size=%"PRIu64"\n",s->param->line_size)); */
+
+ mp->line_size = get_cis_ccd_line_size (s); /* scanner hardware line_size multiplied by 3 for CCD grayscale */
+
+ size = 8 + 2 * IMAGE_BLOCK_SIZE + spare * mp->line_size;
+ buf = (uint8_t *) malloc (size);
+ if (!buf)
+ return PIXMA_ENOMEM;
+ mp->buf = buf;
+ mp->rawimg = buf;
+ mp->imgbuf_ofs = spare * mp->line_size;
+ mp->imgcol = mp->rawimg + IMAGE_BLOCK_SIZE + 8; /* added to make rgb->gray */
+ mp->img = mp->rawimg + IMAGE_BLOCK_SIZE + 8;
+ mp->imgbuf_len = IMAGE_BLOCK_SIZE + mp->imgbuf_ofs;
+ mp->rawimg_left = 0;
+ mp->last_block_size = 0;
+ mp->shifted_bytes = -(int) mp->imgbuf_ofs;
+
+ error = step1 (s);
+ if (error >= 0)
+ error = start_session (s);
+ if (error >= 0)
+ mp->state = state_warmup;
+ if (error >= 0)
+ error = select_source (s);
+ if (error >= 0)
+ error = send_scan_param (s);
+ if (error < 0)
+ {
+ mp750_finish_scan (s);
+ return error;
+ }
+ return 0;
+}
+
+
+static int
+mp750_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ mp750_t *mp = (mp750_t *) s->subdriver;
+ int error;
+ uint8_t info;
+ unsigned block_size, bytes_received, n;
+ int shift[3], base_shift;
+ int c;
+
+ c = ((is_ccd_grayscale (s)) ? 3 : s->param->channels) * s->param->depth / 8; /* single-byte or double-byte data */
+
+ if (mp->state == state_warmup)
+ {
+ int tmo = 60;
+
+ query_status (s);
+ check_status (s);
+ /*SIM*/ while (!is_calibrated (s) && --tmo >= 0)
+ {
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ if (handle_interrupt (s, 1000) > 0)
+ {
+ block_size = 0;
+ error = request_image_block (s, &block_size, &info);
+ /*SIM*/ if (error < 0)
+ return error;
+ }
+ }
+ if (tmo < 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING: Timed out waiting for calibration\n"));
+ return PIXMA_ETIMEDOUT;
+ }
+ pixma_sleep (100000);
+ query_status (s);
+ if (is_warming_up (s) || !is_calibrated (s))
+ {
+ PDBG (pixma_dbg (1, "WARNING: Wrong status: wup=%d cal=%d\n",
+ is_warming_up (s), is_calibrated (s)));
+ return PIXMA_EPROTO;
+ }
+ block_size = 0;
+ request_image_block (s, &block_size, &info);
+ /*SIM*/ mp->state = state_scanning;
+ mp->last_block = 0;
+ }
+
+ /* TODO: Move to other place, values are constant. */
+ base_shift = calc_component_shifting (s) * mp->line_size;
+ if (s->param->source == PIXMA_SOURCE_ADF)
+ {
+ shift[0] = 0;
+ shift[1] = -base_shift;
+ shift[2] = -2 * base_shift;
+ }
+ else
+ {
+ shift[0] = -2 * base_shift;
+ shift[1] = -base_shift;
+ shift[2] = 0;
+ }
+
+ do
+ {
+ if (mp->last_block_size > 0)
+ {
+ block_size = mp->imgbuf_len - mp->last_block_size;
+ memcpy (mp->img, mp->img + mp->last_block_size, block_size);
+ }
+
+ do
+ {
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ if (mp->last_block)
+ {
+ /* end of image */
+ info = mp->last_block;
+ if (info != 0x38)
+ {
+ query_status (s);
+ /*SIM*/ while ((info & 0x28) != 0x28)
+ {
+ pixma_sleep (10000);
+ error = request_image_block2 (s, &info);
+ if (s->cancel)
+ return PIXMA_ECANCELED; /* FIXME: Is it safe to cancel here? */
+ if (error < 0)
+ return error;
+ }
+ }
+ mp->needs_abort = (info != 0x38);
+ mp->last_block = info;
+ mp->state = state_finished;
+ return 0;
+ }
+
+ check_status (s);
+ /*SIM*/ while (handle_interrupt (s, 1) > 0);
+ /*SIM*/ block_size = IMAGE_BLOCK_SIZE;
+ error = request_image_block (s, &block_size, &info);
+ if (error < 0)
+ {
+ if (error == PIXMA_ECANCELED)
+ read_error_info (s, NULL, 0);
+ return error;
+ }
+ mp->last_block = info;
+ if ((info & ~0x38) != 0)
+ {
+ PDBG (pixma_dbg (1, "WARNING: Unknown info byte %x\n", info));
+ }
+ if (block_size == 0)
+ {
+ /* no image data at this moment. */
+ pixma_sleep (10000);
+ }
+ }
+ while (block_size == 0);
+
+ error = read_image_block (s, mp->rawimg + mp->rawimg_left);
+ if (error < 0)
+ {
+ mp->state = state_transfering;
+ return error;
+ }
+ bytes_received = error;
+ PASSERT (bytes_received == block_size);
+
+ /* TODO: simplify! */
+ mp->rawimg_left += bytes_received;
+ n = mp->rawimg_left / 3;
+ /* n = number of pixels in the buffer? */
+
+ /* Color to Grayscale converion for CCD sensor */
+ if (is_ccd_grayscale (s)) {
+ shift_rgb (mp->rawimg, n, shift[0], shift[1], shift[2], mp->stripe_shift, mp->line_size,
+ mp->imgcol + mp->imgbuf_ofs);
+ /* dst: img, src: imgcol */
+ rgb_to_gray (mp->img, mp->imgcol, n, c); /* cropping occurs later? */
+ PDBG (pixma_dbg (4, "*fill_buffer: did grayscale conversion \n"));
+ }
+ /* Color image processing */
+ else {
+ shift_rgb (mp->rawimg, n, shift[0], shift[1], shift[2], mp->stripe_shift, mp->line_size,
+ mp->img + mp->imgbuf_ofs);
+ PDBG (pixma_dbg (4, "*fill_buffer: no grayscale conversion---keep color \n"));
+ }
+
+ /* entering remaining unprocessed bytes after last complete pixel into mp->rawimg buffer -- no influence on mp->img */
+ n *= 3;
+ mp->shifted_bytes += n;
+ mp->rawimg_left -= n; /* rawimg_left = 0, 1 or 2 bytes left in the buffer. */
+ mp->last_block_size = n;
+ memcpy (mp->rawimg, mp->rawimg + n, mp->rawimg_left);
+
+ }
+ while (mp->shifted_bytes <= 0);
+
+ if ((unsigned) mp->shifted_bytes < mp->last_block_size)
+ {
+ if (is_ccd_grayscale (s))
+ ib->rptr = mp->img + mp->last_block_size/3 - mp->shifted_bytes/3; /* testing---works OK */
+ else
+ ib->rptr = mp->img + mp->last_block_size - mp->shifted_bytes;
+ }
+ else
+ ib->rptr = mp->img;
+ if (is_ccd_grayscale (s))
+ ib->rend = mp->img + mp->last_block_size/3; /* testing---works OK */
+ else
+ ib->rend = mp->img + mp->last_block_size;
+ return ib->rend - ib->rptr;
+}
+
+static void
+mp750_finish_scan (pixma_t * s)
+{
+ int error;
+ mp750_t *mp = (mp750_t *) s->subdriver;
+
+ switch (mp->state)
+ {
+ case state_transfering:
+ drain_bulk_in (s);
+ /* fall through */
+ case state_scanning:
+ case state_warmup:
+ error = abort_session (s);
+ if (error == PIXMA_ECANCELED)
+ read_error_info (s, NULL, 0);
+ /* fall through */
+ case state_finished:
+ if (s->param->source == PIXMA_SOURCE_FLATBED)
+ {
+ /*SIM*/ query_status (s);
+ if (abort_session (s) == PIXMA_ECANCELED)
+ {
+ read_error_info (s, NULL, 0);
+ query_status (s);
+ }
+ }
+ query_status (s);
+ /*SIM*/ activate (s, 0);
+ if (mp->needs_abort)
+ {
+ mp->needs_abort = 0;
+ abort_session (s);
+ }
+ free (mp->buf);
+ mp->buf = mp->rawimg = NULL;
+ mp->state = state_idle;
+ /* fall through */
+ case state_idle:
+ break;
+ }
+}
+
+static void
+mp750_wait_event (pixma_t * s, int timeout)
+{
+ /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for
+ * instance. */
+ while (s->events == 0 && handle_interrupt (s, timeout) > 0)
+ {
+ }
+}
+
+static int
+mp750_get_status (pixma_t * s, pixma_device_status_t * status)
+{
+ int error;
+
+ error = query_status (s);
+ if (error < 0)
+ return error;
+ status->hardware = PIXMA_HARDWARE_OK;
+ status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER;
+ status->cal =
+ (is_calibrated (s)) ? PIXMA_CALIBRATION_OK : PIXMA_CALIBRATION_OFF;
+ status->lamp = (is_warming_up (s)) ? PIXMA_LAMP_WARMING_UP : PIXMA_LAMP_OK;
+ return 0;
+}
+
+
+static const pixma_scan_ops_t pixma_mp750_ops = {
+ mp750_open,
+ mp750_close,
+ mp750_scan,
+ mp750_fill_buffer,
+ mp750_finish_scan,
+ mp750_wait_event,
+ mp750_check_param,
+ mp750_get_status
+};
+
+#define DEVICE(name, model, pid, dpi, cap) { \
+ name, /* name */ \
+ model, /* model */ \
+ 0x04a9, pid, /* vid pid */ \
+ 0, /* iface */ \
+ &pixma_mp750_ops, /* ops */ \
+ dpi, 2*(dpi), /* xdpi, ydpi */ \
+ 0, 0, /* adftpu_min_dpi & adftpu_max_dpi not used in this subdriver */ \
+ 0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
+ 637, 877, /* width, height */ \
+ PIXMA_CAP_GRAY|PIXMA_CAP_EVENTS|cap \
+}
+
+const pixma_config_t pixma_mp750_devices[] = {
+ DEVICE ("Canon PIXMA MP750", "MP750", MP750_PID, 2400, PIXMA_CAP_CCD | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MP760/770", "MP760/770", MP760_PID, 2400, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP780/790", "MP780/790", MP780_PID, 2400, PIXMA_CAP_CCD | PIXMA_CAP_ADF),
+ DEVICE (NULL, NULL, 0, 0, 0)
+};
diff --git a/backend/pixma_mp810.c b/backend/pixma_mp810.c
new file mode 100644
index 0000000..5b6cdfe
--- /dev/null
+++ b/backend/pixma_mp810.c
@@ -0,0 +1,2365 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2009 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+ Copyright (C) 2011-2013 Rolf Bensch <rolf at bensch hyphen online dot de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+/* test cases
+ 1. short USB packet (must be no -ETIMEDOUT)
+ 2. cancel using button on the printer (look for abort command)
+ 3. start scan while busy (status 0x1414)
+ 4. cancel using ctrl-c (must send abort command)
+ */
+
+#define TPU_48 /* uncomment to activate TPU scan at 48 bits */
+/*#define DEBUG_TPU_48*//* uncomment to debug 48 bits TPU on a non TPU device */
+/*#define DEBUG_TPU_24*//* uncomment to debug 24 bits TPU on a non TPU device */
+
+/*#define TPUIR_USE_RGB*/ /* uncomment to use RGB channels and convert them to gray; otherwise use R channel only */
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h> /* localtime(C90) */
+
+#include "pixma_rename.h"
+#include "pixma_common.h"
+#include "pixma_io.h"
+
+/* Some macro code to enhance readability */
+#define RET_IF_ERR(x) do { \
+ if ((error = (x)) < 0) \
+ return error; \
+ } while(0)
+
+#define WAIT_INTERRUPT(x) do { \
+ error = handle_interrupt (s, x); \
+ if (s->cancel) \
+ return PIXMA_ECANCELED; \
+ if (error != PIXMA_ECANCELED && error < 0) \
+ return error; \
+ } while(0)
+
+#ifdef __GNUC__
+# define UNUSED(v) (void) v
+#else
+# define UNUSED(v)
+#endif
+
+/* Size of the command buffer should be multiple of wMaxPacketLength and
+ greater than 4096+24.
+ 4096 = size of gamma table. 24 = header + checksum */
+#define IMAGE_BLOCK_SIZE (512*1024)
+#define CMDBUF_SIZE (4096 + 24)
+#define DEFAULT_GAMMA 2.0 /***** Gamma different from 1.0 is potentially impacting color profile generation *****/
+#define UNKNOWN_PID 0xffff
+
+#define CANON_VID 0x04a9
+
+/* Generation 2 */
+#define MP810_PID 0x171a
+#define MP960_PID 0x171b
+
+/* Generation 3 */
+/* PIXMA 2007 vintage */
+#define MP970_PID 0x1726
+
+/* Flatbed scanner CCD (2007) */
+#define CS8800F_PID 0x1901
+
+/* PIXMA 2008 vintage */
+#define MP980_PID 0x172d
+
+/* Generation 4 */
+#define MP990_PID 0x1740
+
+/* Flatbed scanner CCD (2010) */
+#define CS9000F_PID 0x1908
+
+/* 2010 new device (untested) */
+#define MG8100_PID 0x174b /* CCD */
+
+/* 2011 new device (untested) */
+#define MG8200_PID 0x1756 /* CCD */
+
+/* 2013 new device */
+#define CS9000F_MII_PID 0x190d
+
+/* Generation 4 XML messages that encapsulates the Pixma protocol messages */
+#define XML_START_1 \
+"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
+<cmd xmlns:ivec=\"http://www.canon.com/ns/cmd/2008/07/common/\">\
+<ivec:contents><ivec:operation>StartJob</ivec:operation>\
+<ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\
+<ivec:bidi>1</ivec:bidi></ivec:param_set></ivec:contents></cmd>"
+
+#define XML_START_2 \
+"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
+<cmd xmlns:ivec=\"http://www.canon.com/ns/cmd/2008/07/common/\" xmlns:vcn=\"http://www.canon.com/ns/cmd/2008/07/canon/\">\
+<ivec:contents><ivec:operation>VendorCmd</ivec:operation>\
+<ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\
+<vcn:ijoperation>ModeShift</vcn:ijoperation><vcn:ijmode>1</vcn:ijmode>\
+</ivec:param_set></ivec:contents></cmd>"
+
+#define XML_END \
+"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
+<cmd xmlns:ivec=\"http://www.canon.com/ns/cmd/2008/07/common/\">\
+<ivec:contents><ivec:operation>EndJob</ivec:operation>\
+<ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\
+</ivec:param_set></ivec:contents></cmd>"
+
+#define XML_OK "<ivec:response>OK</ivec:response>"
+
+enum mp810_state_t
+{
+ state_idle,
+ state_warmup,
+ state_scanning,
+ state_transfering,
+ state_finished
+};
+
+enum mp810_cmd_t
+{
+ cmd_start_session = 0xdb20,
+ cmd_select_source = 0xdd20,
+ cmd_gamma = 0xee20,
+ cmd_scan_param = 0xde20,
+ cmd_status = 0xf320,
+ cmd_abort_session = 0xef20,
+ cmd_time = 0xeb80,
+ cmd_read_image = 0xd420,
+ cmd_error_info = 0xff20,
+
+ cmd_start_calibrate_ccd_3 = 0xd520,
+ cmd_end_calibrate_ccd_3 = 0xd720,
+ cmd_scan_param_3 = 0xd820,
+ cmd_scan_start_3 = 0xd920,
+ cmd_status_3 = 0xda20,
+ cmd_get_tpu_info_3 = 0xf520,
+ cmd_set_tpu_info_3 = 0xea20,
+
+ cmd_e920 = 0xe920 /* seen in MP800 */
+};
+
+typedef struct mp810_t
+{
+ enum mp810_state_t state;
+ pixma_cmdbuf_t cb;
+ uint8_t *imgbuf;
+ uint8_t current_status[16];
+ unsigned last_block;
+ uint8_t generation;
+ /* for Generation 3 and CCD shift */
+ uint8_t *linebuf;
+ uint8_t *data_left_ofs;
+ unsigned data_left_len;
+ int shift[3];
+ unsigned color_shift;
+ unsigned stripe_shift;
+ unsigned stripe_shift2; /* added for MP810, MP960 at 4800dpi & 9000F at 9600dpi */
+ unsigned jumplines; /* added for MP810, MP960 at 4800dpi & 9000F at 9600dpi */
+ uint8_t tpu_datalen;
+ uint8_t tpu_data[0x40];
+} mp810_t;
+
+/*
+ STAT: 0x0606 = ok,
+ 0x1515 = failed (PIXMA_ECANCELED),
+ 0x1414 = busy (PIXMA_EBUSY)
+
+ Transaction scheme
+ 1. command_header/data | result_header
+ 2. command_header | result_header/data
+ 3. command_header | result_header/image_data
+
+ - data has checksum in the last byte.
+ - image_data has no checksum.
+ - data and image_data begins in the same USB packet as
+ command_header or result_header.
+
+ command format #1:
+ u16be cmd
+ u8[6] 0
+ u8[4] 0
+ u32be PLEN parameter length
+ u8[PLEN-1] parameter
+ u8 parameter check sum
+ result:
+ u16be STAT
+ u8 0
+ u8 0 or 0x21 if STAT == 0x1414
+ u8[4] 0
+
+ command format #2:
+ u16be cmd
+ u8[6] 0
+ u8[4] 0
+ u32be RLEN result length
+ result:
+ u16be STAT
+ u8[6] 0
+ u8[RLEN-1] result
+ u8 result check sum
+
+ command format #3: (only used by read_image_block)
+ u16be 0xd420
+ u8[6] 0
+ u8[4] 0
+ u32be max. block size + 8
+ result:
+ u16be STAT
+ u8[6] 0
+ u8 block info bitfield: 0x8 = end of scan, 0x10 = no more paper, 0x20 = no more data
+ u8[3] 0
+ u32be ILEN image data size
+ u8[ILEN] image data
+ */
+
+static void mp810_finish_scan (pixma_t * s);
+
+static int is_scanning_from_adf (pixma_t * s)
+{
+ return (s->param->source == PIXMA_SOURCE_ADF
+ || s->param->source == PIXMA_SOURCE_ADFDUP);
+}
+
+static int is_scanning_from_adfdup (pixma_t * s)
+{
+ return (s->param->source == PIXMA_SOURCE_ADFDUP);
+}
+
+static int is_scanning_from_tpu (pixma_t * s)
+{
+ return (s->param->source == PIXMA_SOURCE_TPU);
+}
+
+static int send_xml_dialog (pixma_t * s, const char * xml_message)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ int datalen;
+
+ datalen = pixma_cmd_transaction (s, xml_message, strlen (xml_message),
+ mp->cb.buf, 1024);
+ if (datalen < 0)
+ return datalen;
+
+ mp->cb.buf[datalen] = 0;
+
+ PDBG(pixma_dbg (10, "XML message sent to scanner:\n%s\n", xml_message));
+ PDBG(pixma_dbg (10, "XML response back from scanner:\n%s\n", mp->cb.buf));
+
+ return (strcasestr ((const char *) mp->cb.buf, XML_OK) != NULL);
+}
+
+static void new_cmd_tpu_msg (pixma_t *s, pixma_cmdbuf_t * cb, uint16_t cmd)
+{
+ pixma_newcmd (cb, cmd, 0, 0);
+ cb->buf[3] = (is_scanning_from_tpu (s)) ? 0x01 : 0x00;
+}
+
+static int start_session (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ new_cmd_tpu_msg (s, &mp->cb, cmd_start_session);
+ return pixma_exec (s, &mp->cb);
+}
+
+static int start_scan_3 (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ new_cmd_tpu_msg (s, &mp->cb, cmd_scan_start_3);
+ return pixma_exec (s, &mp->cb);
+}
+
+static int send_cmd_start_calibrate_ccd_3 (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ pixma_newcmd (&mp->cb, cmd_start_calibrate_ccd_3, 0, 0);
+ mp->cb.buf[3] = 0x01;
+ return pixma_exec (s, &mp->cb);
+}
+
+static int is_calibrated (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ if (mp->generation >= 3)
+ {
+ return ((mp->current_status[0] & 0x01) == 1);
+ }
+ if (mp->generation == 1)
+ {
+ return (mp->current_status[8] == 1);
+ }
+ else
+ {
+ return (mp->current_status[9] == 1);
+ }
+}
+
+static int has_paper (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ if (is_scanning_from_adfdup (s))
+ return (mp->current_status[1] == 0 || mp->current_status[2] == 0);
+ else
+ return (mp->current_status[1] == 0);
+}
+
+static void drain_bulk_in (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) >= 0)
+ ;
+}
+
+static int abort_session (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
+}
+
+static int send_cmd_e920 (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ return pixma_exec_short_cmd (s, &mp->cb, cmd_e920);
+}
+
+static int select_source (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+
+ data = pixma_newcmd (&mp->cb, cmd_select_source, 12, 0);
+ data[5] = ((mp->generation == 2) ? 1 : 0);
+ switch (s->param->source)
+ {
+ case PIXMA_SOURCE_FLATBED:
+ data[0] = 1;
+ data[1] = 1;
+ break;
+
+ case PIXMA_SOURCE_ADF:
+ data[0] = 2;
+ data[5] = 1;
+ data[6] = 1;
+ break;
+
+ case PIXMA_SOURCE_ADFDUP:
+ data[0] = 2;
+ data[5] = 3;
+ data[6] = 3;
+ break;
+
+ case PIXMA_SOURCE_TPU:
+ data[0] = 4;
+ data[1] = 2;
+ break;
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static int send_get_tpu_info_3 (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_get_tpu_info_3, 0, 0x34);
+ RET_IF_ERR(pixma_exec (s, &mp->cb));
+ memcpy (mp->tpu_data, data, 0x34);
+ return error;
+}
+
+static int send_set_tpu_info (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+
+ if (mp->tpu_datalen == 0)
+ return 0;
+ data = pixma_newcmd (&mp->cb, cmd_set_tpu_info_3, 0x34, 0);
+ memcpy (data, mp->tpu_data, 0x34);
+ return pixma_exec (s, &mp->cb);
+}
+
+static int send_gamma_table (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ const uint8_t *lut = s->param->gamma_table;
+ uint8_t *data;
+
+ if (mp->generation == 1)
+ {
+ data = pixma_newcmd (&mp->cb, cmd_gamma, 4096 + 8, 0);
+ data[0] = (s->param->channels == 3) ? 0x10 : 0x01;
+ pixma_set_be16 (0x1004, data + 2);
+ if (lut)
+ memcpy (data + 4, lut, 4096);
+ else
+ pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096);
+ }
+ else
+ {
+ /* FIXME: Gamma table for 2nd generation: 1024 * uint16_le */
+ data = pixma_newcmd (&mp->cb, cmd_gamma, 2048 + 8, 0);
+ data[0] = 0x10;
+ pixma_set_be16 (0x0804, data + 2);
+ if (lut)
+ {
+ int i;
+ for (i = 0; i < 1024; i++)
+ {
+ int j = (i << 2) + (i >> 8);
+ data[4 + 2 * i + 0] = lut[j];
+ data[4 + 2 * i + 1] = lut[j];
+ }
+ }
+ else
+ {
+ int i;
+ pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 2048);
+ for (i = 0; i < 1024; i++)
+ {
+ int j = (i << 1) + (i >> 9);
+ data[4 + 2 * i + 0] = data[4 + j];
+ data[4 + 2 * i + 1] = data[4 + j];
+ }
+ }
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static unsigned calc_raw_width (const mp810_t * mp,
+ const pixma_scan_param_t * param)
+{
+ unsigned raw_width;
+ /* NOTE: Actually, we can send arbitary width to MP810. Lines returned
+ are always padded to multiple of 4 or 12 pixels. Is this valid for
+ other models, too? */
+ if (mp->generation >= 2)
+ {
+ raw_width = ALIGN_SUP (param->w + param->xs, 32);
+ /* PDBG (pixma_dbg (4, "*calc_raw_width***** width %u extended by %u and rounded to %u *****\n", param->w, param->xs, raw_width)); */
+ }
+ else if (param->channels == 1)
+ {
+ raw_width = ALIGN_SUP (param->w + param->xs, 12);
+ }
+ else
+ {
+ raw_width = ALIGN_SUP (param->w + param->xs, 4);
+ }
+ return raw_width;
+}
+
+static int has_ccd_sensor (pixma_t * s)
+{
+ return ((s->cfg->cap & PIXMA_CAP_CCD) != 0);
+}
+
+#if 0
+static int is_color (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_COLOR);
+}
+
+static int is_color_48 (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_COLOR_48);
+}
+
+static int is_color_negative (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_NEGATIVE_COLOR);
+}
+
+static int is_color_all (pixma_t * s)
+{
+ return (is_color (s) || is_color_48 (s) || is_color_negative (s));
+}
+#endif
+
+static int is_gray (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_GRAY);
+}
+
+static int is_gray_16 (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_GRAY_16);
+}
+
+static int is_gray_negative (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_NEGATIVE_GRAY);
+}
+
+static int is_gray_all (pixma_t * s)
+{
+ return (is_gray (s) || is_gray_16 (s) || is_gray_negative (s));
+}
+
+static int is_lineart (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_LINEART);
+}
+
+static int is_tpuir (pixma_t * s)
+{
+ return (s->param->mode == PIXMA_SCAN_MODE_TPUIR);
+}
+
+/* CCD sensors don't have neither a Grayscale mode nor a Lineart mode,
+ * but use color mode instead */
+static unsigned get_cis_ccd_line_size (pixma_t * s)
+{
+ return ((
+ s->param->wx ? s->param->line_size / s->param->w * s->param->wx
+ : s->param->line_size)
+ * ((is_tpuir (s) || is_gray_all (s) || is_lineart (s)) ? 3 : 1));
+}
+
+static unsigned calc_shifting (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ /* If stripes shift needed (CCD devices), how many pixels shift */
+ mp->stripe_shift = 0;
+ mp->stripe_shift2 = 0;
+ mp->jumplines = 0;
+
+ switch (s->cfg->pid)
+ {
+ case MP970_PID: /* MP970 at 4800 dpi */
+ case CS8800F_PID: /* CanoScan 8800F at 4800 dpi */
+ if (s->param->xdpi == 4800)
+ {
+ if (is_scanning_from_tpu (s))
+ mp->stripe_shift = 6;
+ else
+ mp->stripe_shift = 3;
+ }
+ break;
+
+ case CS9000F_PID: /* CanoScan 9000F at 4800 dpi */
+ case CS9000F_MII_PID:
+ if (s->param->xdpi == 4800)
+ {
+ if (is_scanning_from_tpu (s))
+ mp->stripe_shift = 6; /* should work for CS9000F same as manual */
+ else
+ mp->stripe_shift = 3;
+ }
+ if (s->param->xdpi == 9600)
+ {
+ if (is_scanning_from_tpu (s))
+ {
+ /* need to double up for TPU */
+ mp->stripe_shift = 6; /* for 1st set of 4 images */
+ /* unfortunately there are 2 stripe shifts */
+ mp->stripe_shift2 = 6; /* for 2nd set of 4 images */
+ mp->jumplines = 32; /* try 33 or 34 */
+ }
+ /* there is no 9600dpi support in flatbed mode */
+ }
+ break;
+
+ case MP960_PID:
+ if (s->param->xdpi == 2400)
+ {
+ if (is_scanning_from_tpu (s))
+ mp->stripe_shift = 6;
+ else
+ mp->stripe_shift = 3;
+ }
+ if (s->param->xdpi == 4800)
+ {
+ if (is_scanning_from_tpu (s))
+ {
+ mp->stripe_shift = 6;
+ mp->stripe_shift2 = 6;
+ }
+ else
+ {
+ mp->stripe_shift = 3;
+ mp->stripe_shift2 = 3;
+ }
+ mp->jumplines = 33; /* better than 32 or 34 : applies to flatbed & TPU */
+ }
+ break;
+
+ case MP810_PID:
+ if (s->param->xdpi == 2400)
+ {
+ if (is_scanning_from_tpu (s))
+ mp->stripe_shift = 6;
+ else
+ mp->stripe_shift = 3;
+ }
+ if (s->param->xdpi == 4800)
+ {
+ if (is_scanning_from_tpu (s))
+ {
+ mp->stripe_shift = 6;
+ mp->stripe_shift2 = 6;
+ }
+ else
+ {
+ mp->stripe_shift = 3;
+ mp->stripe_shift2 = 3;
+ }
+ mp->jumplines = 33; /* better than 32 or 34 : applies to flatbed & TPU */
+ }
+ break;
+
+ default: /* Default, and all CIS devices */
+ break;
+ }
+ /* If color plane shift (CCD devices), how many pixels shift */
+ mp->color_shift = mp->shift[0] = mp->shift[1] = mp->shift[2] = 0;
+ if (s->param->ydpi > 75)
+ {
+ switch (s->cfg->pid)
+ {
+ case MP970_PID:
+ case CS8800F_PID: /* CanoScan 8800F */
+ mp->color_shift = s->param->ydpi / 50;
+ mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
+ mp->shift[0] = 0;
+ mp->shift[2] = 2 * mp->shift[1];
+ break;
+
+ case CS9000F_PID: /* CanoScan 9000F */
+ case CS9000F_MII_PID:
+ mp->color_shift = s->param->ydpi / 30;
+ mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
+ mp->shift[0] = 0;
+ mp->shift[2] = 2 * mp->shift[1];
+ break;
+
+ case MP980_PID:
+ case MP990_PID:
+ case MG8200_PID:
+ if (s->param->ydpi > 150)
+ {
+ mp->color_shift = s->param->ydpi / 75;
+ mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
+ mp->shift[0] = 0;
+ mp->shift[2] = 2 * mp->shift[1];
+ }
+ break;
+
+ case MP810_PID:
+ case MP960_PID:
+ mp->color_shift = s->param->ydpi / 50;
+ if (is_scanning_from_tpu (s))
+ mp->color_shift = s->param->ydpi / 50;
+ mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
+ mp->shift[0] = 2 * mp->shift[1];
+ mp->shift[2] = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+ /* special settings for 16 bit flatbed mode @ 75 dpi
+ * minimum of 150 dpi is used yet */
+ /* else if (!is_scanning_from_tpu (s))
+ {
+ switch (s->cfg->pid)
+ {
+ case CS9000F_PID:
+ case CS9000F_MII_PID:
+ if (is_color_48 (s) || is_gray_16 (s))
+ {
+ mp->color_shift = 5;
+ mp->shift[1] = 0;
+ mp->shift[0] = 0;
+ mp->shift[2] = 0;
+ }
+ break;
+ }
+ } */
+ /* PDBG (pixma_dbg (4, "*calc_shifing***** color_shift = %u, stripe_shift = %u, jumplines = %u *****\n",
+ mp->color_shift, mp->stripe_shift, mp->jumplines)); */
+ return (2 * mp->color_shift + mp->stripe_shift + mp->jumplines); /* note impact of stripe shift2 later if needed! */
+}
+
+static int send_scan_param (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+ unsigned raw_width = calc_raw_width (mp, s->param);
+ unsigned h, h1, h2, shifting;
+
+ /* TPU scan does not support lineart */
+ if (is_scanning_from_tpu (s) && is_lineart (s))
+ {
+ return PIXMA_ENOTSUP;
+ }
+
+ shifting = calc_shifting (s);
+ h1 = s->param->h + shifting; /* add lines for color shifting */
+ /* PDBG (pixma_dbg (4, "* send_scan_param: height calc (choose lesser) 1 %u \n", h1 )); */
+ if (mp->generation >= 4) /* use most global condition */
+ {
+ /* tested for MP960, 9000F */
+ /* add lines for color shifting */
+ /* otherwise you cannot scan all lines defined for flatbed mode */
+ /* this shouldn't affect TPU mode */
+ h2 = s->cfg->height * s->param->ydpi / 75 + shifting;
+ /* PDBG (pixma_dbg (4, "* send_scan_param: height calc (choose lesser) 2 %u = %u max. lines for scanner + %u lines for color shifting \n", h2, s->cfg->height * s->param->ydpi / 75, shifting )); */
+ }
+ else
+ {
+ /* TODO: Check for other scanners. */
+ h2 = s->cfg->height * s->param->ydpi / 75; /* this might be causing problems for generation 1 devices */
+ /* PDBG (pixma_dbg (4, "* send_scan_param: height calc (choose lesser) 2 %u \n", h2 )); */
+ }
+ h = MIN (h1, h2);
+
+ if (mp->generation <= 2)
+ {
+ data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x30, 0);
+ pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04);
+ pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06);
+ pixma_set_be32 (s->param->x, data + 0x08);
+ if (mp->generation == 2)
+ pixma_set_be32 (s->param->x - s->param->xs, data + 0x08);
+ pixma_set_be32 (s->param->y, data + 0x0c);
+ pixma_set_be32 (raw_width, data + 0x10);
+ pixma_set_be32 (h, data + 0x14);
+ data[0x18] =
+ ((s->param->channels != 1) || is_gray_all (s) || is_lineart (s)) ?
+ 0x08 : 0x04;
+ data[0x19] = ((s->param->software_lineart) ? 8 : s->param->depth)
+ * ((is_gray_all (s) || is_lineart (s)) ? 3 : s->param->channels); /* bits per pixel */
+ data[0x1a] = (is_scanning_from_tpu (s) ? 1 : 0);
+ data[0x20] = 0xff;
+ data[0x23] = 0x81;
+ data[0x26] = 0x02;
+ data[0x27] = 0x01;
+ }
+ else
+ {
+ /* scan parameters:
+ * ================
+ *
+ * byte | # of | mode | value / description
+ * | bytes | |
+ * -----+-------+---------+---------------------------
+ * 0x00 | 1 | default | 0x01
+ * | | adf | 0x02
+ * | | tpu | 0x04
+ * | | tpuir | cs9000f: 0x03
+ * -----+-------+---------+---------------------------
+ * 0x01 | 1 | default | 0x00
+ * | | tpu | 0x02
+ * -----+-------+---------+---------------------------
+ * 0x02 | 1 | default | 0x01
+ * | | adfdup | 0x03
+ * -----+-------+---------+---------------------------
+ * 0x03 | 1 | default | 0x00
+ * | | adfdup | 0x03
+ * -----+-------+---------+---------------------------
+ * 0x04 | 1 | all | 0x00
+ * -----+-------+---------+---------------------------
+ * 0x05 | 1 | all | 0x01: This one also seen at 0. Don't know yet what's used for.
+ * -----+-------+---------+---------------------------
+ * ... | 1 | all | 0x00
+ * -----+-------+---------+---------------------------
+ * 0x08 | 2 | all | xdpi | 0x8000
+ * -----+-------+---------+---------------------------
+ * 0x0a | 2 | all | ydpi | 0x8000: Must be the same as xdpi.
+ * -----+-------+---------+---------------------------
+ * 0x0c | 4 | all | x position of start pixel
+ * -----+-------+---------+---------------------------
+ * 0x10 | 4 | all | y position of start pixel
+ * -----+-------+---------+---------------------------
+ * 0x14 | 4 | all | # of pixels in 1 line
+ * -----+-------+---------+---------------------------
+ * 0x18 | 4 | all | # of scan lines
+ * -----+-------+---------+---------------------------
+ * 0x1c | 1 | all | 0x08
+ * | | | 0x04 = relict from cis scanners?
+ * -----+-------+---------+---------------------------
+ * 0x1d | 1 | all | # of bits per pixel
+ * -----+-------+---------+---------------------------
+ * 0x1e | 1 | default | 0x00: paper
+ * | | tpu | 0x01: positives
+ * | | tpu | 0x02: negatives
+ * | | tpuir | 0x01: positives
+ * -----+-------+---------+---------------------------
+ * 0x1f | 1 | all | 0x01
+ * | | | cs9000f: 0x00: Not sure if that is because of positives.
+ * -----+-------+---------+---------------------------
+ * 0x20 | 1 | all | 0xff
+ * -----+-------+---------+---------------------------
+ * 0x21 | 1 | all | 0x81
+ * -----+-------+---------+---------------------------
+ * 0x22 | 1 | all | 0x00
+ * -----+-------+---------+---------------------------
+ * 0x23 | 1 | all | 0x02
+ * -----+-------+---------+---------------------------
+ * 0x24 | 1 | all | 0x01
+ * -----+-------+---------+---------------------------
+ * 0x25 | 1 | default | 0x00; cs8800f: 0x01
+ * | | tpu | 0x00; cs9000f, mg8200: 0x01
+ * | | tpuir | cs9000f: 0x00
+ * -----+-------+---------+---------------------------
+ * ... | 1 | all | 0x00
+ * -----+-------+---------+---------------------------
+ * 0x30 | 1 | all | 0x01
+ *
+ */
+
+ data = pixma_newcmd (&mp->cb, cmd_scan_param_3, 0x38, 0);
+ data[0x00] = is_scanning_from_adf (s) ? 0x02 : 0x01;
+ data[0x01] = 0x01;
+ if (is_scanning_from_tpu (s))
+ {
+ data[0x00] = is_tpuir (s) ? 0x03 : 0x04;
+ data[0x01] = 0x02;
+ data[0x1e] = 0x02; /* NB: CanoScan 8800F: 0x02->negatives, 0x01->positives, paper->0x00 */
+ }
+ data[0x02] = 0x01;
+ if (is_scanning_from_adfdup (s))
+ {
+ data[0x02] = 0x03;
+ data[0x03] = 0x03;
+ }
+ if (s->cfg->pid != MG8200_PID)
+ data[0x05] = 0x01; /* This one also seen at 0. Don't know yet what's used for */
+ /* the scanner controls the scan */
+ /* no software control needed */
+ pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x08);
+ pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x0a);
+ /*PDBG (pixma_dbg (4, "*send_scan_param***** Setting: xdpi=%hi ydpi=%hi x=%u y=%u w=%u h=%u ***** \n",
+ s->param->xdpi,s->param->ydpi,(s->param->x)-(s->param->xs),s->param->y,raw_width,h));*/
+ pixma_set_be32 (s->param->x - s->param->xs, data + 0x0c);
+ pixma_set_be32 (s->param->y, data + 0x10);
+ pixma_set_be32 (raw_width, data + 0x14);
+ pixma_set_be32 (h, data + 0x18);
+ data[0x1c] = ((s->param->channels != 1) || is_tpuir (s) || is_gray_all (s) || is_lineart (s)) ? 0x08 : 0x04;
+
+#ifdef DEBUG_TPU_48
+ data[0x1d] = 24;
+#else
+ data[0x1d] = (is_scanning_from_tpu (s)) ? 48
+ : (((s->param->software_lineart) ? 8 : s->param->depth)
+ * ((is_tpuir (s) || is_gray_all (s) || is_lineart (s)) ? 3 : s->param->channels)); /* bits per pixel */
+#endif
+
+ data[0x1f] = 0x01; /* for 9000F this appears to be 0x00, not sure if that is because of positives */
+
+ if (s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID || s->cfg->pid == MG8200_PID)
+ {
+ data[0x1f] = 0x00;
+ }
+
+ data[0x20] = 0xff;
+ data[0x21] = 0x81;
+ data[0x23] = 0x02;
+ data[0x24] = 0x01;
+
+ /* MG8200 addition */
+ if (s->cfg->pid == MG8200_PID)
+ {
+ if (is_scanning_from_tpu (s))
+ {
+ data[0x25] = 0x01;
+ }
+ }
+
+ /* CS8800F & CS9000F addition */
+ if (s->cfg->pid == CS8800F_PID || s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID)
+ {
+ if (is_scanning_from_tpu (s))
+ { /* TPU */
+ /* 0x02->negatives, 0x01->positives, paper->0x00
+ * no paper in TPU mode */
+ if (s->param->mode == PIXMA_SCAN_MODE_NEGATIVE_COLOR
+ || s->param->mode == PIXMA_SCAN_MODE_NEGATIVE_GRAY)
+ {
+ PDBG(
+ pixma_dbg (4, "*send_scan_param***** TPU scan negatives *****\n"));
+ data[0x1e] = 0x02;
+ }
+ else
+ {
+ PDBG(
+ pixma_dbg (4, "*send_scan_param***** TPU scan positives *****\n"));
+ data[0x1e] = 0x01;
+ }
+ /* CS8800F: 0x00 for TPU color management */
+ if (s->cfg->pid == CS8800F_PID)
+ data[0x25] = 0x00;
+ /* CS9000F: 0x01 for TPU */
+ if (s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID)
+ data[0x25] = 0x01;
+ if (s->param->mode == PIXMA_SCAN_MODE_TPUIR)
+ data[0x25] = 0x00;
+ }
+ else
+ { /* flatbed and ADF */
+ /* paper->0x00 */
+ data[0x1e] = 0x00;
+ /* CS8800F: 0x01 normally */
+ if (s->cfg->pid == CS8800F_PID)
+ data[0x25] = 0x01;
+ /* CS9000F: 0x00 normally */
+ if (s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID)
+ data[0x25] = 0x00;
+ }
+ }
+
+ data[0x30] = 0x01;
+ }
+ return pixma_exec (s, &mp->cb);
+}
+
+static int query_status_3 (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+ int error, status_len;
+
+ status_len = 8;
+ data = pixma_newcmd (&mp->cb, cmd_status_3, 0, status_len);
+ RET_IF_ERR(pixma_exec (s, &mp->cb));
+ memcpy (mp->current_status, data, status_len);
+ return error;
+}
+
+static int query_status (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+ int error, status_len;
+
+ status_len = (mp->generation == 1) ? 12 : 16;
+ data = pixma_newcmd (&mp->cb, cmd_status, 0, status_len);
+ RET_IF_ERR(pixma_exec (s, &mp->cb));
+ memcpy (mp->current_status, data, status_len);
+ PDBG(
+ pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u busy=%u\n", data[1], data[8], data[7], data[9]));
+ return error;
+}
+
+static int send_time (pixma_t * s)
+{
+ /* Why does a scanner need a time? */
+ time_t now;
+ struct tm *t;
+ uint8_t *data;
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
+ pixma_get_time (&now, NULL);
+ t = localtime (&now);
+ snprintf ((char *) data, 16, "%02d/%02d/%02d %02d:%02d", t->tm_year % 100,
+ t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min);
+ PDBG(pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
+ return pixma_exec (s, &mp->cb);
+}
+
+/* TODO: Simplify this function. Read the whole data packet in one shot. */
+static int read_image_block (pixma_t * s, uint8_t * header, uint8_t * data)
+{
+ uint8_t cmd[16];
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ const int hlen = 8 + 8;
+ int error, datalen;
+
+ memset (cmd, 0, sizeof(cmd));
+ /* PDBG (pixma_dbg (4, "* read_image_block: last_block\n", mp->last_block)); */
+ pixma_set_be16 (cmd_read_image, cmd);
+ if ((mp->last_block & 0x20) == 0)
+ pixma_set_be32 ((IMAGE_BLOCK_SIZE / 65536) * 65536 + 8, cmd + 0xc);
+ else
+ pixma_set_be32 (32 + 8, cmd + 0xc);
+
+ mp->state = state_transfering;
+ mp->cb.reslen = pixma_cmd_transaction (s, cmd, sizeof(cmd), mp->cb.buf, 512); /* read 1st 512 bytes of image block */
+ datalen = mp->cb.reslen;
+ if (datalen < 0)
+ return datalen;
+
+ memcpy (header, mp->cb.buf, hlen);
+ /* PDBG (pixma_dbg (4, "* read_image_block: datalen %i\n", datalen)); */
+ /* PDBG (pixma_dbg (4, "* read_image_block: hlen %i\n", hlen)); */
+ if (datalen >= hlen)
+ {
+ datalen -= hlen;
+ memcpy (data, mp->cb.buf + hlen, datalen);
+ data += datalen;
+ if (mp->cb.reslen == 512)
+ { /* read the rest of the image block */
+ error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen);
+ RET_IF_ERR(error);
+ datalen += error;
+ }
+ }
+
+ mp->state = state_scanning;
+ mp->cb.expected_reslen = 0;
+ RET_IF_ERR(pixma_check_result (&mp->cb));
+ if (mp->cb.reslen < hlen)
+ return PIXMA_EPROTO;
+ return datalen;
+}
+
+static int read_error_info (pixma_t * s, void *buf, unsigned size)
+{
+ unsigned len = 16;
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+ int error;
+
+ data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len);
+ RET_IF_ERR(pixma_exec (s, &mp->cb));
+ if (buf && len < size)
+ {
+ size = len;
+ /* NOTE: I've absolutely no idea what the returned data mean. */
+ memcpy (buf, data, size);
+ error = len;
+ }
+ return error;
+}
+
+/*
+ handle_interrupt() waits until it receives an interrupt packet or times out.
+ It calls send_time() and query_status() if necessary. Therefore, make sure
+ that handle_interrupt() is only called from a safe context for send_time()
+ and query_status().
+
+ Returns:
+ 0 timed out
+ 1 an interrupt packet received
+ PIXMA_ECANCELED interrupted by signal
+ <0 error
+ */
+static int handle_interrupt (pixma_t * s, int timeout)
+{
+ uint8_t buf[64]; /* check max. packet size with 'lsusb -v' for "EP 9 IN" */
+ int len;
+
+ len = pixma_wait_interrupt (s->io, buf, sizeof(buf), timeout);
+ if (len == PIXMA_ETIMEDOUT)
+ return 0;
+ if (len < 0)
+ return len;
+ if (len%16) /* len must be a multiple of 16 bytes */
+ {
+ PDBG(pixma_dbg (1, "WARNING:unexpected interrupt packet length %d\n", len));
+ return PIXMA_EPROTO;
+ }
+
+ /* s->event = 0x0brroott
+ * b: button
+ * oo: original
+ * tt: target
+ * rr: scan resolution
+ * poll event with 'scanimage -A' */
+ if (s->cfg->pid == MG8200_PID)
+ /* button no. in buf[7]
+ * size in buf[10] 01=A4; 02=Letter; 08=10x15; 09=13x18; 0b=auto
+ * format in buf[11] 01=JPEG; 02=TIFF; 03=PDF; 04=Kompakt-PDF
+ * dpi in buf[12] 01=75; 02=150; 03=300; 04=600
+ * target = format; original = size; scan-resolution = dpi */
+ {
+ if (buf[7] & 1)
+ s->events = PIXMA_EV_BUTTON1 | buf[11] | buf[10]<<8 | buf[12]<<16; /* color scan */
+ if (buf[7] & 2)
+ s->events = PIXMA_EV_BUTTON2 | buf[11] | buf[10]<<8 | buf[12]<<16; /* b/w scan */
+ }
+ else if (s->cfg->pid == CS8800F_PID
+ || s->cfg->pid == CS9000F_PID
+ || s->cfg->pid == CS9000F_MII_PID)
+ /* button no. in buf[1]
+ * target = button no.
+ * "Finish PDF" is Button-2, all others are Button-1 */
+ {
+ if ((s->cfg->pid == CS8800F_PID && buf[1] == 0x70)
+ || (s->cfg->pid != CS8800F_PID && buf[1] == 0x50))
+ s->events = PIXMA_EV_BUTTON2 | buf[1] >> 4; /* button 2 = cancel / end scan */
+ else
+ s->events = PIXMA_EV_BUTTON1 | buf[1] >> 4; /* button 1 = start scan */
+ }
+ else
+ /* button no. in buf[0]
+ * original in buf[0]
+ * target in buf[1] */
+ {
+ /* More than one event can be reported at the same time. */
+ if (buf[3] & 1)
+ send_time (s); /* FIXME: some scanners hang here */
+ if (buf[9] & 2)
+ query_status (s);
+
+ if (buf[0] & 2)
+ s->events = PIXMA_EV_BUTTON2 | buf[1] | ((buf[0] & 0xf0) << 4); /* b/w scan */
+ if (buf[0] & 1)
+ s->events = PIXMA_EV_BUTTON1 | buf[1] | ((buf[0] & 0xf0) << 4); /* color scan */
+ }
+ return 1;
+}
+
+static int init_ccd_lamp_3 (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ uint8_t *data;
+ int error, status_len, tmo;
+
+ status_len = 8;
+ RET_IF_ERR(query_status (s));
+ RET_IF_ERR(query_status (s));
+ RET_IF_ERR(send_cmd_start_calibrate_ccd_3 (s));
+ RET_IF_ERR(query_status (s));
+ tmo = 20; /* like Windows driver, CCD lamp adjustment */
+ while (--tmo >= 0)
+ {
+ data = pixma_newcmd (&mp->cb, cmd_end_calibrate_ccd_3, 0, status_len);
+ RET_IF_ERR(pixma_exec (s, &mp->cb));
+ memcpy (mp->current_status, data, status_len);
+ PDBG(pixma_dbg (3, "Lamp status: %u , timeout in: %u\n", data[0], tmo));
+ if (mp->current_status[0] == 3 || !is_scanning_from_tpu (s))
+ break;
+ WAIT_INTERRUPT(1000);
+ }
+ return error;
+}
+
+static int wait_until_ready (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ int error, tmo = 60;
+
+ RET_IF_ERR((mp->generation >= 3) ? query_status_3 (s) : query_status (s));
+ while (!is_calibrated (s))
+ {
+ WAIT_INTERRUPT(1000);
+ if (mp->generation >= 3)
+ RET_IF_ERR(query_status_3 (s));
+ if (--tmo == 0)
+ {
+ PDBG(pixma_dbg (1, "WARNING: Timed out in wait_until_ready()\n"));
+ PDBG(query_status (s));
+ return PIXMA_ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+/* the RGB images are shifted by # of lines */
+/* the R image is shifted by colshift[0] */
+/* the G image is shifted by colshift[1] */
+/* the B image is shifted by colshift[2] */
+/* usually one of the RGB images must not be shifted */
+/* which one depends on the scanner */
+/* some scanners have an additional stripe shift */
+/* e.g. colshift[0]=0, colshift[1]=1, colshift[2]=2 */
+/* R image is OK: RGBRGBRGB... */
+/* ^^ ^^ ^^ */
+/* || || || */
+/* shift G image: RG|RG|RG|... */
+/* | | | */
+/* shift B image: RGBRGBRGB... */
+/* this doesn't affect the G and B images */
+/* G image will become the R image in the next run */
+/* B image will become the G image in the next run */
+/* the next line will become the B image in the next run */
+static uint8_t *
+shift_colors (uint8_t * dptr, uint8_t * sptr, unsigned w, unsigned dpi,
+ unsigned pid, unsigned c, int * colshft, unsigned strshft)
+{
+ unsigned i, sr, sg, sb, st;
+ UNUSED(dpi);
+ UNUSED(pid);
+ sr = colshft[0];
+ sg = colshft[1];
+ sb = colshft[2];
+
+ /* PDBG (pixma_dbg (4, "*shift_colors***** c=%u, w=%i, sr=%u, sg=%u, sb=%u, strshft=%u ***** \n",
+ c, w, sr, sg, sb, strshft)); */
+
+ for (i = 0; i < w; i++)
+ {
+ /* stripes shift for MP970 at 4800 dpi, MP810 at 2400 dpi */
+ st = (i % 2 == 0) ? strshft : 0;
+
+ *sptr++ = *(dptr++ + sr + st);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sr + st);
+ *sptr++ = *(dptr++ + sg + st);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sg + st);
+ *sptr++ = *(dptr++ + sb + st);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sb + st);
+ }
+
+ return dptr;
+}
+
+static uint8_t *
+shift_colorsCS9000 (uint8_t * dptr, uint8_t * sptr, unsigned w, unsigned dpi,
+ unsigned pid, unsigned c, int * colshft, unsigned strshft,
+ unsigned strshft2, unsigned jump)
+
+{
+ unsigned i, sr, sg, sb, st, st2;
+ UNUSED(dpi);
+ UNUSED(pid);
+ sr = colshft[0];
+ sg = colshft[1];
+ sb = colshft[2];
+
+ for (i = 0; i < w; i++)
+ {
+ if (i < (w / 2))
+ {
+ /* stripes shift for 1st 4 images for Canoscan 9000F at 9600dpi */
+ st = (i % 2 == 0) ? strshft : 0;
+ *sptr++ = *(dptr++ + sr + st);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sr + st);
+ *sptr++ = *(dptr++ + sg + st);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sg + st);
+ *sptr++ = *(dptr++ + sb + st);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sb + st);
+ }
+ if (i >= (w / 2))
+ {
+ /* stripes shift for 2nd 4 images for Canoscan 9000F at 9600dpi */
+ st2 = (i % 2 == 0) ? strshft2 : 0;
+ *sptr++ = *(dptr++ + sr + jump + st2);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sr + jump + st2);
+ *sptr++ = *(dptr++ + sg + jump + st2);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sg + jump + st2);
+ *sptr++ = *(dptr++ + sb + jump + st2);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sb + jump + st2);
+ }
+ }
+ return dptr;
+}
+
+static uint8_t *
+shift_colorsCS9000_4800 (uint8_t * dptr, uint8_t * sptr, unsigned w,
+ unsigned dpi, unsigned pid, unsigned c, int * colshft,
+ unsigned strshft, unsigned strshft2, unsigned jump)
+
+{
+ unsigned i, sr, sg, sb, st2;
+ UNUSED(dpi);
+ UNUSED(pid);
+ UNUSED(strshft);
+ sr = colshft[0];
+ sg = colshft[1];
+ sb = colshft[2];
+
+ for (i = 0; i < w; i++)
+ {
+ /* stripes shift for 2nd 4 images
+ * for Canoscan 9000F with 16 bit flatbed scans at 4800dpi */
+ st2 = (i % 2 == 0) ? strshft2 : 0;
+ *sptr++ = *(dptr++ + sr + jump + st2);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sr + jump + st2);
+ *sptr++ = *(dptr++ + sg + jump + st2);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sg + jump + st2);
+ *sptr++ = *(dptr++ + sb + jump + st2);
+ if (c == 6)
+ *sptr++ = *(dptr++ + sb + jump + st2);
+ }
+ return dptr;
+}
+
+/* under some conditions some scanners have sub images in one line */
+/* e.g. doubled image, line size = 8 */
+/* line before reordering: px1 px3 px5 px7 px2 px4 px6 px8 */
+/* line after reordering: px1 px2 px3 px4 px5 px6 px7 px8 */
+static void reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c,
+ unsigned n, unsigned m, unsigned w,
+ unsigned line_size)
+{
+ unsigned i;
+
+ for (i = 0; i < w; i++)
+ { /* process complete line */
+ memcpy (linebuf + c * (n * (i % m) + i / m), sptr + c * i, c);
+ }
+ memcpy (sptr, linebuf, line_size);
+}
+
+/* special reorder matrix for mp960 */
+static void mp960_reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c,
+ unsigned n, unsigned m, unsigned w,
+ unsigned line_size)
+{
+ unsigned i, i2;
+
+ /* try and copy 2 px at once */
+ for (i = 0; i < w; i++)
+ { /* process complete line */
+ i2 = i % 2;
+ if (i < w / 2)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m)), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m)), sptr + c * i, c);
+ }
+ else
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 1), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 1), sptr + c * i, c);
+ }
+ }
+
+ memcpy (sptr, linebuf, line_size);
+}
+
+/* special reorder matrix for mp970 */
+static void mp970_reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c,
+ unsigned w, unsigned line_size)
+{
+ unsigned i, i8;
+
+ for (i = 0; i < w; i++)
+ { /* process complete line */
+ i8 = i % 8;
+ memcpy (linebuf + c * (i + i8 - ((i8 > 3) ? 7 : 0)), sptr + c * i, c);
+ }
+ memcpy (sptr, linebuf, line_size);
+}
+
+/* special reorder matrix for CS9000F */
+static void cs9000f_initial_reorder_pixels (uint8_t * linebuf, uint8_t * sptr,
+ unsigned c, unsigned n, unsigned m,
+ unsigned w, unsigned line_size)
+{
+ unsigned i, i2;
+
+ /* try and copy 2 px at once */
+ for (i = 0; i < w; i++)
+ { /* process complete line */
+ i2 = i % 2;
+ if (i < w / 8)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m)), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m)), sptr + c * i, c);
+ }
+ else if (i >= w / 8 && i < w / 4)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 1), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 1), sptr + c * i, c);
+ }
+ else if (i >= w / 4 && i < 3 * w / 8)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 2), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 2), sptr + c * i, c);
+ }
+ else if (i >= 3 * w / 8 && i < w / 2)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 3), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 3), sptr + c * i, c);
+ }
+ else if (i >= w / 2 && i < 5 * w / 8)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 4), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 4), sptr + c * i, c);
+ }
+ else if (i >= 5 * w / 8 && i < 3 * w / 4)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 5), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 5), sptr + c * i, c);
+ }
+ else if (i >= 3 * w / 4 && i < 7 * w / 8)
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 6), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 6), sptr + c * i, c);
+ }
+ else
+ {
+ if (i2 == 0)
+ memcpy (linebuf + c * (n * ((i) % m) + ((i) / m) + 7), sptr + c * i, c);
+ else
+ memcpy (linebuf + c * (n * ((i - 1) % m) + 1 + ((i) / m) + 7), sptr + c * i, c);
+ }
+ }
+
+ memcpy (sptr, linebuf, line_size);
+}
+
+/* CS9000F 9600dpi reorder: actually 4800dpi since each pixel is doubled */
+/* need to rearrange each sequence of 16 pairs of pixels as follows: */
+/* start px : 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 */
+/* before : p1a p1b p1c p1d p2a p2b p2c p2d p3a p3b p3c p3d p4a p4b p4c p4d */
+/* after : p1a p3a p1b p3b p1c p3c p1d p3d p2a p4a p2b p4b p2c p4c p2d p4d */
+/* start px : 0 16 2 18 4 20 6 22 8 24 10 26 12 28 14 30 */
+/* change : * * * * * * * * * * * * * * */
+/* no change: * * */
+/* so the 1st and the 3rd set are interleaved, followed by the 2nd and 4th sets interleaved */
+static void cs9000f_second_reorder_pixels (uint8_t * linebuf, uint8_t * sptr,
+ unsigned c, unsigned w,
+ unsigned line_size)
+{
+ unsigned i, i8;
+ static const int shifts[8] =
+ { 2, 4, 6, 8, -8, -6, -4, -2 };
+
+ for (i = 0; i < w; i += 2)
+ { /* process complete line */
+ i8 = (i >> 1) & 0x7;
+ /* Copy 2 pixels at once */
+ memcpy (linebuf + c * (i + shifts[i8]), sptr + c * i, c * 2);
+ }
+
+ memcpy (sptr, linebuf, line_size);
+}
+
+#ifndef TPU_48
+static unsigned
+pack_48_24_bpc (uint8_t * sptr, unsigned n)
+{
+ unsigned i;
+ uint8_t *cptr, lsb;
+ static uint8_t offset = 0;
+
+ cptr = sptr;
+ if (n % 2 != 0)
+ PDBG (pixma_dbg (3, "WARNING: misaligned image.\n"));
+ for (i = 0; i < n; i += 2)
+ {
+ /* offset = 1 + (offset % 3); */
+ lsb = *sptr++;
+ *cptr++ = ((*sptr++) << offset) | lsb >> (8 - offset);
+ }
+ return (n / 2);
+}
+#endif
+
+/* This function deals both with PIXMA CCD sensors producing shifted color
+ * planes images, Grayscale CCD scan and Generation >= 3 high dpi images.
+ * Each complete line in mp->imgbuf is processed for shifting CCD sensor
+ * color planes, reordering pixels above 600 dpi for Generation >= 3, and
+ * converting to Grayscale for CCD sensors. */
+static unsigned post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ unsigned c, lines, line_size, n, m, cw, cx, reducelines;
+ uint8_t *sptr, *dptr, *gptr, *cptr;
+ unsigned /*color_shift, stripe_shift, stripe_shift2,*/ jumplines /*, height*/;
+ int test;
+
+ /* For testers: */
+ /* set this to 1 in order to get unprocessed images next to one another at 9600dpi */
+ /* other resolutions should not be affected */
+ /* set this to 2 if you want to see the same with jumplines=0 */
+ test = 0;
+ jumplines = 0;
+
+ c = ((is_tpuir (s) || is_gray_all (s) || is_lineart (s)) ? 3 : s->param->channels)
+ * ((s->param->software_lineart) ? 8 : s->param->depth) / 8;
+ cw = c * s->param->w;
+ cx = c * s->param->xs;
+
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** c = %u, cw = %u, cx = %u *****\n", c, cw, cx)); */
+
+ if (mp->generation >= 3)
+ n = s->param->xdpi / 600;
+ else
+ /* FIXME: maybe need different values for CIS and CCD sensors */
+ n = s->param->xdpi / 2400;
+
+ /* Some exceptions to global rules here */
+ if (s->cfg->pid == MP970_PID || s->cfg->pid == MP990_PID || s->cfg->pid == MG8200_PID
+ || s->cfg->pid == CS8800F_PID || s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID)
+ n = MIN (n, 4);
+
+ /* exception for 9600dpi on Canoscan 9000F */
+ if ((s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) && (s->param->xdpi == 9600))
+ {
+ n = 8;
+ if (test > 0)
+ n = 1; /* test if 8 images are next to one another */
+ }
+
+ /* test if 2 images are next to one another */
+ if ((s->cfg->pid == MP960_PID) && (s->param->xdpi == 4800) && (test > 0))
+ {
+ n = 1;
+ }
+
+ m = (n > 0) ? s->param->wx / n : 1;
+
+ sptr = dptr = gptr = cptr = mp->imgbuf;
+ line_size = get_cis_ccd_line_size (s);
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** ----- Set n=%u, m=%u, line_size=%u ----- ***** \n", n, m, line_size)); */
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** ----- spr=dpr=%u, linebuf=%u ----- ***** \n", sptr, mp->linebuf)); */
+
+ lines = (mp->data_left_ofs - mp->imgbuf) / line_size;
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** lines = %i > 2 * mp->color_shift + mp->stripe_shift = %i ***** \n",
+ lines, 2 * mp->color_shift + mp->stripe_shift)); */
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** mp->color_shift = %u, mp->stripe_shift = %u, , mp->stripe_shift2 = %u ***** \n",
+ mp->color_shift, mp->stripe_shift, mp->stripe_shift2)); */
+
+ /*color_shift = mp->color_shift;*/
+ /*stripe_shift = mp->stripe_shift;*/
+ /*stripe_shift2 = mp->stripe_shift2;*/
+ jumplines = mp->jumplines;
+
+ /* height not needed here! */
+ /* removed to avoid confusion */
+ /* height = MIN (s->param->h + calc_shifting (s),
+ s->cfg->height * s->param->ydpi / 75); */
+
+ /* have to test if rounding down is OK or not -- currently 0.5 lines is rounded down */
+ /* note stripe shifts doubled already in definitions */
+ if ((s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) && (s->param->xdpi == 9600) && (test > 0))
+ {
+ /* using test==2 you can check in GIMP the required offset, and
+ use the below line (uncommented) and replace XXX with that
+ number, and then compile again with test set to 1. */
+
+ jumplines = 32;
+ if (test == 2)
+ jumplines = 0;
+ }
+
+ /* mp960 test */
+ if ((s->cfg->pid == MP960_PID) && (s->param->xdpi == 4800) && (test > 0))
+ {
+ jumplines = 32;
+ if (test == 2)
+ jumplines = 0;
+ }
+
+ reducelines = ((2 * mp->color_shift + mp->stripe_shift) + jumplines);
+ /* PDBG (pixma_dbg (4, "*post_process_image_data: lines %u, reducelines %u \n", lines, reducelines)); */
+ if (lines > reducelines)
+ { /* (line - reducelines) of image lines can be converted */
+ unsigned i;
+
+ lines -= reducelines;
+
+ for (i = 0; i < lines; i++, sptr += line_size)
+ { /* convert only full image lines */
+ /* Color plane and stripes shift needed by e.g. CCD */
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** Processing with c=%u, n=%u, m=%u, w=%i, line_size=%u ***** \n",
+ c, n, m, s->param->wx, line_size)); */
+ if (c >= 3)
+ {
+ if (((s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) && (s->param->xdpi == 9600))
+ || ((s->cfg->pid == MP960_PID) && (s->param->xdpi == 4800))
+ || ((s->cfg->pid == MP810_PID) && (s->param->xdpi == 4800)))
+ {
+ dptr = shift_colorsCS9000 (dptr, sptr, s->param->wx, s->param->xdpi,
+ s->cfg->pid, c, mp->shift,
+ mp->stripe_shift, mp->stripe_shift2,
+ jumplines * line_size);
+ }
+
+ else if ((s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) /* 9000F: 16 bit flatbed scan at 4800dpi */
+ && ((s->param->mode == PIXMA_SCAN_MODE_COLOR_48)
+ || (s->param->mode == PIXMA_SCAN_MODE_GRAY_16))
+ && (s->param->xdpi == 4800)
+ && (s->param->source == PIXMA_SOURCE_FLATBED))
+ dptr = shift_colorsCS9000_4800 (dptr, sptr, s->param->wx,
+ s->param->xdpi, s->cfg->pid, c,
+ mp->shift, mp->stripe_shift,
+ mp->stripe_shift2,
+ jumplines * line_size);
+
+ else
+ /* all except 9000F at 9600dpi */
+ dptr = shift_colors (dptr, sptr, s->param->wx, s->param->xdpi,
+ s->cfg->pid, c, mp->shift, mp->stripe_shift);
+ }
+
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** test = %i *****\n", test)); */
+
+ /*--comment out all between this line and the one below for 9000F tests at 9600dpi or MP960 at 4800dpi ------*/
+ /* if ( 0 ) */
+ if ((((s->cfg->pid != CS9000F_PID && s->cfg->pid != CS9000F_MII_PID) || (s->param->xdpi < 9600))
+ && ((s->cfg->pid != MP960_PID) || (s->param->xdpi < 4800))
+ && ((s->cfg->pid != MP810_PID) || (s->param->xdpi < 4800)))
+ || (test == 0))
+ {
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** MUST GET HERE WHEN TEST == 0 *****\n")); */
+
+ if (!((s->cfg->pid == MP810_PID) && (s->param->xdpi == 4800))
+ && !((s->cfg->pid == MP960_PID) && (s->param->xdpi == 4800))
+ && !((s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) && (s->param->xdpi == 9600)))
+ { /* for both flatbed & TPU */
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** reordering pixels normal n = %i *****\n", n)); */
+ reorder_pixels (mp->linebuf, sptr, c, n, m, s->param->wx, line_size);
+ }
+
+ if ((s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) && (s->param->xdpi == 9600))
+ {
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** cs900f_initial_reorder_pixels n = %i *****\n", n)); */
+ /* this combines pixels from 8 images 2px at a time from left to right: 1122334455667788... */
+ cs9000f_initial_reorder_pixels (mp->linebuf, sptr, c, n, m,
+ s->param->wx, line_size);
+ /* final interleaving */
+ cs9000f_second_reorder_pixels (mp->linebuf, sptr, c, s->param->wx,
+ line_size);
+ }
+
+ /* comment: special image format for MP960 in flatbed mode
+ at 4800dpi. It is actually 2400dpi, with each pixel
+ doubled. The TPU mode has proper pixel ordering */
+ if ((((s->cfg->pid == MP960_PID) || (s->cfg->pid == MP810_PID)) && (s->param->xdpi == 4800))
+ && (n > 0))
+ {
+ /* for both flatbed & TPU */
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** flatbed mp960_reordering pixels n = %i *****\n", n)); */
+ mp960_reorder_pixels (mp->linebuf, sptr, c, n, m, s->param->wx,
+ line_size);
+ }
+
+ /* comment: MP970, CS8800F, CS9000F specific reordering for 4800 dpi */
+ if ((s->cfg->pid == MP970_PID || s->cfg->pid == CS8800F_PID
+ || s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID) && (s->param->xdpi == 4800))
+ {
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** mp970_reordering pixels n = %i *****\n", n)); */
+ mp970_reorder_pixels (mp->linebuf, sptr, c, s->param->wx, line_size);
+ }
+
+ }
+ /*-------------------------------------------------------*/
+
+ /* PDBG (pixma_dbg (4, "*post_process_image_data: sptr=%u, dptr=%u \n", sptr, dptr)); */
+
+ /* Crop line to selected borders */
+ memmove (cptr, sptr + cx, cw);
+ /* PDBG (pixma_dbg (4, "*post_process_image_data***** crop line: cx=%u, cw=%u ***** \n", cx, cw)); */
+
+ /* Color to Lineart convert for CCD sensor */
+ if (is_lineart (s))
+ cptr = gptr = pixma_binarize_line (s->param, gptr, cptr, s->param->w, c);
+#ifndef TPUIR_USE_RGB
+ /* save IR only for CCD sensor */
+ else if (is_tpuir (s))
+ cptr = gptr = pixma_r_to_ir (gptr, cptr, s->param->w, c);
+ /* Color to Grayscale convert for CCD sensor */
+ else if (is_gray_all (s))
+#else
+ /* IR *and* Color to Grayscale convert for CCD sensor */
+ else if (is_tpuir (s) || is_gray_all (s))
+#endif
+ cptr = gptr = pixma_rgb_to_gray (gptr, cptr, s->param->w, c);
+ else
+ cptr += cw;
+ }
+ /* PDBG (pixma_dbg (4, "*post_process_image_data: sptr=%u, dptr=%u \n", sptr, dptr)); */
+ }
+ ib->rptr = mp->imgbuf;
+ ib->rend = cptr;
+ return mp->data_left_ofs - sptr; /* # of non processed bytes */
+ /* contains shift color data for new lines */
+ /* and already received data for the next line */
+}
+
+static int mp810_open (pixma_t * s)
+{
+ mp810_t *mp;
+ uint8_t *buf;
+
+ mp = (mp810_t *) calloc (1, sizeof(*mp));
+ if (!mp)
+ return PIXMA_ENOMEM;
+
+ buf = (uint8_t *) malloc (CMDBUF_SIZE + IMAGE_BLOCK_SIZE);
+ if (!buf)
+ {
+ free (mp);
+ return PIXMA_ENOMEM;
+ }
+
+ s->subdriver = mp;
+ mp->state = state_idle;
+
+ mp->cb.buf = buf;
+ mp->cb.size = CMDBUF_SIZE;
+ mp->cb.res_header_len = 8;
+ mp->cb.cmd_header_len = 16;
+ mp->cb.cmd_len_field_ofs = 14;
+
+ mp->imgbuf = buf + CMDBUF_SIZE;
+
+ /* General rules for setting Pixma protocol generation # */
+ mp->generation = (s->cfg->pid >= MP810_PID) ? 2 : 1; /* no generation 1 devices anyway, but keep similar to pixma_mp150.c file */
+
+ if (s->cfg->pid >= MP970_PID)
+ mp->generation = 3;
+
+ if (s->cfg->pid >= MP990_PID)
+ mp->generation = 4;
+
+ /* And exceptions to be added here */
+ if (s->cfg->pid == CS8800F_PID)
+ mp->generation = 3;
+
+ if (s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID)
+ mp->generation = 4;
+
+ /* TPU info data setup */
+ mp->tpu_datalen = 0;
+
+ if (mp->generation < 4)
+ {
+ /* Canoscan 8800F ignores commands if not initialized */
+ if (s->cfg->pid == CS8800F_PID)
+ abort_session (s);
+ else
+ {
+ query_status (s);
+ handle_interrupt (s, 200);
+ if (mp->generation == 3 && has_ccd_sensor (s))
+ send_cmd_start_calibrate_ccd_3 (s);
+ }
+ }
+ return 0;
+}
+
+static void mp810_close (pixma_t * s)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ mp810_finish_scan (s);
+ free (mp->cb.buf);
+ free (mp);
+ s->subdriver = NULL;
+}
+
+static int mp810_check_param (pixma_t * s, pixma_scan_param_t * sp)
+{
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ unsigned w_max;
+
+ /* PDBG (pixma_dbg (4, "*mp810_check_param***** Initially: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n",
+ sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx)); */
+
+ sp->channels = 3;
+ sp->software_lineart = 0;
+ switch (sp->mode)
+ {
+ /* standard scan modes
+ * 8 bit per channel in color and grayscale mode
+ * 16 bit per channel with TPU */
+ case PIXMA_SCAN_MODE_GRAY:
+ case PIXMA_SCAN_MODE_NEGATIVE_GRAY:
+ case PIXMA_SCAN_MODE_TPUIR:
+ sp->channels = 1;
+ /* fall through */
+ case PIXMA_SCAN_MODE_COLOR:
+ case PIXMA_SCAN_MODE_NEGATIVE_COLOR:
+ sp->depth = 8;
+#ifdef TPU_48
+#ifndef DEBUG_TPU_48
+ if (sp->source == PIXMA_SOURCE_TPU)
+#endif
+ sp->depth = 16; /* TPU in 16 bits mode */
+#endif
+ break;
+ /* extended scan modes for 48 bit flatbed scanners
+ * 16 bit per channel in color and grayscale mode */
+ case PIXMA_SCAN_MODE_GRAY_16:
+ sp->channels = 1;
+ sp->depth = 16;
+ break;
+ case PIXMA_SCAN_MODE_COLOR_48:
+ sp->channels = 3;
+ sp->depth = 16;
+ break;
+ /* software lineart
+ * 1 bit per channel */
+ case PIXMA_SCAN_MODE_LINEART:
+ sp->software_lineart = 1;
+ sp->channels = 1;
+ sp->depth = 1;
+ break;
+ }
+
+ /* for software lineart w must be a multiple of 8
+ * I don't know why is_lineart(s) doesn't work here */
+ if (sp->software_lineart == 1 && sp->w % 8)
+ {
+ sp->w += 8 - (sp->w % 8);
+
+ /* do not exceed the scanner capability */
+ w_max = s->cfg->width * s->cfg->xdpi / 75;
+ w_max -= w_max % 8;
+ if (sp->w > w_max)
+ sp->w = w_max;
+ }
+
+ if (sp->source == PIXMA_SOURCE_TPU && !sp->tpu_offset_added)
+ {
+ unsigned fixed_offset_y; /* TPU offsets for CanoScan 8800F, or other CCD at 300dpi. */
+ unsigned max_y; /* max TPU height for CS9000F at 75 dpi */
+
+ /* CanoScan 8800F and others adding an offset depending on resolution */
+ /* CS9000F and others maximum TPU height */
+ switch (s->cfg->pid)
+ {
+ case CS8800F_PID:
+ fixed_offset_y = 140;
+ max_y = MIN (740, s->cfg->height);
+ break;
+ case CS9000F_PID:
+ case CS9000F_MII_PID:
+ fixed_offset_y = 146;
+ max_y = MIN (740, s->cfg->height);
+ break;
+ default:
+ fixed_offset_y = 0;
+ max_y = s->cfg->height;
+ break;
+ }
+
+ /* cropping y and h to scanable area */
+ max_y *= (sp->ydpi) / 75;
+ sp->y = MIN(sp->y, max_y);
+ sp->h = MIN(sp->h, max_y - sp->y);
+ /* PDBG (pixma_dbg (4, "*mp810_check_param***** Cropping: y=%u, h=%u *****\n",
+ sp->y, sp->h)); */
+ if (!sp->h)
+ return SANE_STATUS_INVAL; /* no lines */
+
+ /* Convert the offsets from 300dpi to actual resolution */
+ fixed_offset_y = fixed_offset_y * (sp->xdpi) / 300;
+
+ /* In TPU mode, the CS9000F appears to always subtract 146 from the
+ vertical starting position, but clamps its at 0. Therefore vertical
+ offsets 0 through 146 (@300 dpi) get all mapped onto the same
+ physical starting position: line 0. Then, starting from 147, the
+ offsets get mapped onto successive physical lines:
+ y line
+ 0 -> 0
+ 1 -> 0
+ 2 -> 0
+ ...
+ 146 -> 0
+ 147 -> 1
+ 148 -> 2
+ ...
+ Since a preview scan is typically made starting from y = 0, but
+ partial image scans usually start at y >> 147, this results in a
+ discontinuity in the y to line mapping, resulting in wrong offsets.
+ To prevent this, we must always add (at least) 146 to the y
+ offset before it is sent to the scanner. The scanner will then
+ map y = 0 (146) to the first line, y = 1 (147) to the second line,
+ and so on. Any distance that is then measured on the preview scan,
+ can be translated without any discontinuity.
+
+ However, there is one complication: during a preview scan, which
+ normally covers the whole scan area of the scanner, we should _not_
+ add the offset because it will result in a reduced number of lines
+ being returned (the scan height is clamped in
+ pixma_check_scan_param()). Since the frontend has no way of telling
+ that the scan area has been reduced, it would derive an incorrect
+ effective scan resolution, and any position calculations based on
+ this would therefore be inaccurate.
+
+ To prevent this, we don't add the offset in case y = 0, which is
+ typically the case during a preview scan (the scanner effectively
+ adds the offset for us, see above). In that way we keep the
+ linearity and we don't affect the scan area during previews.
+ */
+
+ if (sp->y > 0)
+ sp->y += fixed_offset_y;
+
+ /* Prevent repeated corrections as check_param may be called multiple times */
+ sp->tpu_offset_added = 1;
+ }
+
+ if (mp->generation >= 2)
+ {
+ /* mod 32 and expansion of the X scan limits */
+ /* PDBG (pixma_dbg (4, "*mp810_check_param***** (gen>=2) xs=mod32 ----- Initially: x=%u, y=%u, w=%u, h=%u *****\n", sp->x, sp->y, sp->w, sp->h)); */
+ sp->xs = (sp->x) % 32;
+ }
+ else
+ {
+ sp->xs = 0;
+ /* PDBG (pixma_dbg (4, "*mp810_check_param***** (else) xs=0 Selected origin, origin shift: %u, %u *****\n", sp->x, sp->xs)); */
+ }
+ sp->wx = calc_raw_width (mp, sp);
+ sp->line_size = sp->w * sp->channels * (((sp->software_lineart) ? 8 : sp->depth) / 8); /* bytes per line per color after cropping */
+ /* PDBG (pixma_dbg (4, "*mp810_check_param***** (else) Final scan width and line-size: %u, %"PRIu64" *****\n", sp->wx, sp->line_size)); */
+
+ /* highest res is 600, 2400, 4800 or 9600 dpi */
+ {
+ uint8_t k;
+
+ if ((sp->source == PIXMA_SOURCE_ADF || sp->source == PIXMA_SOURCE_ADFDUP)
+ && mp->generation >= 4)
+ /* ADF/ADF duplex mode: max scan res is 600 dpi, at least for generation 4 */
+ k = sp->xdpi / MIN (sp->xdpi, 600);
+ else if (sp->source == PIXMA_SOURCE_TPU && sp->mode == PIXMA_SCAN_MODE_TPUIR)
+ /* TPUIR mode: max scan res is 2400 dpi */
+ k = sp->xdpi / MIN (sp->xdpi, 2400);
+ else if (sp->source == PIXMA_SOURCE_TPU && (s->cfg->pid == CS9000F_PID || s->cfg->pid == CS9000F_MII_PID))
+ /* CS9000F in TPU mode */
+ k = sp->xdpi / MIN (sp->xdpi, 9600);
+ else
+ /* default */
+ k = sp->xdpi / MIN (sp->xdpi, 4800);
+
+ sp->x /= k;
+ sp->xs /= k;
+ sp->y /= k;
+ sp->w /= k;
+ sp->wx /= k;
+ sp->h /= k;
+ sp->xdpi /= k;
+ sp->ydpi = sp->xdpi;
+ }
+
+ /* lowest res is 75, 150, 300 or 600 dpi */
+ {
+ uint8_t k;
+
+ if (sp->source == PIXMA_SOURCE_TPU && sp->mode == PIXMA_SCAN_MODE_TPUIR)
+ /* TPUIR mode */
+ k = MAX (sp->xdpi, 600) / sp->xdpi;
+ else if (sp->source == PIXMA_SOURCE_TPU
+ && ((mp->generation >= 3) || (s->cfg->pid == MP810_PID) || (s->cfg->pid == MP960_PID)))
+ /* TPU mode for generation 3+ scanners
+ * MP810, MP960 appear to have a 200dpi mode for low-res scans, not 150 dpi */
+ k = MAX (sp->xdpi, 300) / sp->xdpi;
+ else if (sp->source == PIXMA_SOURCE_TPU
+ || sp->mode == PIXMA_SCAN_MODE_COLOR_48 || sp->mode == PIXMA_SCAN_MODE_GRAY_16)
+ /* TPU mode and 16 bit flatbed scans
+ * TODO: either the frontend (xsane) cannot handle 48 bit flatbed scans @ 75 dpi (prescan)
+ * or there is a bug in this subdriver */
+ k = MAX (sp->xdpi, 150) / sp->xdpi;
+ else
+ /* default */
+ k = MAX (sp->xdpi, 75) / sp->xdpi;
+
+ sp->x *= k;
+ sp->xs *= k;
+ sp->y *= k;
+ sp->w *= k;
+ sp->wx *= k;
+ sp->h *= k;
+ sp->xdpi *= k;
+ sp->ydpi = sp->xdpi;
+ }
+
+ /* PDBG (pixma_dbg (4, "*mp810_check_param***** Finally: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n",
+ sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx)); */
+
+ return 0;
+}
+
+static int mp810_scan (pixma_t * s)
+{
+ int error = 0, tmo;
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ if (mp->state != state_idle)
+ return PIXMA_EBUSY;
+
+ /* Generation 4: send XML dialog */
+ if (mp->generation == 4 && s->param->adf_pageid == 0)
+ {
+ if (!send_xml_dialog (s, XML_START_1))
+ return PIXMA_EPROTO;
+ if (!send_xml_dialog (s, XML_START_2))
+ return PIXMA_EPROTO;
+ }
+
+ /* clear interrupt packets buffer */
+ while (handle_interrupt (s, 0) > 0)
+ {
+ }
+
+ /* FIXME: Duplex ADF: check paper status only before odd pages (1,3,5,...). */
+ if (is_scanning_from_adf (s))
+ {
+ if ((error = query_status (s)) < 0)
+ return error;
+ tmo = 10;
+ while (!has_paper (s) && --tmo >= 0)
+ {
+ WAIT_INTERRUPT(1000);
+ PDBG(pixma_dbg (2, "No paper in ADF. Timed out in %d sec.\n", tmo));
+ }
+ if (!has_paper (s))
+ return PIXMA_ENO_PAPER;
+ }
+
+ if (has_ccd_sensor (s) && (mp->generation <= 2))
+ {
+ error = send_cmd_e920 (s);
+ switch (error)
+ {
+ case PIXMA_ECANCELED:
+ case PIXMA_EBUSY:
+ PDBG(pixma_dbg (2, "cmd e920 or d520 returned %s\n", pixma_strerror (error)));
+ /* fall through */
+ case 0:
+ query_status (s);
+ break;
+ default:
+ PDBG(pixma_dbg (1, "WARNING: cmd e920 or d520 failed %s\n", pixma_strerror (error)));
+ return error;
+ }
+ tmo = 3; /* like Windows driver, CCD calibration ? */
+ while (--tmo >= 0)
+ {
+ WAIT_INTERRUPT(1000);
+ PDBG(pixma_dbg (2, "CCD Calibration ends in %d sec.\n", tmo));
+ }
+ /* pixma_sleep(2000000); */
+ }
+
+ tmo = 10;
+ if (s->param->adf_pageid == 0 || mp->generation <= 2)
+ {
+ error = start_session (s);
+ while (error == PIXMA_EBUSY && --tmo >= 0)
+ {
+ if (s->cancel)
+ {
+ error = PIXMA_ECANCELED;
+ break;
+ }
+ PDBG(pixma_dbg (2, "Scanner is busy. Timed out in %d sec.\n", tmo + 1));
+ pixma_sleep (1000000);
+ error = start_session (s);
+ }
+ if (error == PIXMA_EBUSY || error == PIXMA_ETIMEDOUT)
+ {
+ /* The scanner maybe hangs. We try to empty output buffer of the
+ * scanner and issue the cancel command. */
+ PDBG(pixma_dbg (2, "Scanner hangs? Sending abort_session command.\n"));
+ drain_bulk_in (s);
+ abort_session (s);
+ pixma_sleep (500000);
+ error = start_session (s);
+ }
+ if ((error >= 0) || (mp->generation >= 3))
+ mp->state = state_warmup;
+ if ((error >= 0) && (mp->generation <= 2))
+ error = select_source (s);
+ if ((error >= 0) && (mp->generation >= 3) && has_ccd_sensor (s))
+ error = init_ccd_lamp_3 (s);
+ if ((error >= 0) && !is_scanning_from_tpu (s))
+ {
+ int i;
+ /* FIXME: 48 bit flatbed scans don't need gamma tables
+ * the code below doesn't run */
+ /*if (is_color_48 (s) || is_gray_16 (s))
+ error = 0;
+ else*/
+ for (i = (mp->generation >= 3) ? 3 : 1; i > 0 && error >= 0; i--)
+ error = send_gamma_table (s);
+ }
+ else if (error >= 0) /* in TPU mode, for gen 1, 2, and 3 */
+ error = send_set_tpu_info (s);
+ }
+ else
+ /* ADF pageid != 0 and gen3 or above */
+ pixma_sleep (1000000);
+
+ if ((error >= 0) || (mp->generation >= 3))
+ mp->state = state_warmup;
+ if (error >= 0)
+ error = send_scan_param (s);
+ if ((error >= 0) && (mp->generation >= 3))
+ error = start_scan_3 (s);
+ if (error < 0)
+ {
+ mp->last_block = 0x38; /* Force abort session if ADF scan */
+ mp810_finish_scan (s);
+ return error;
+ }
+ return 0;
+}
+
+static int mp810_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
+{
+ int error;
+ mp810_t *mp = (mp810_t *) s->subdriver;
+ unsigned block_size, bytes_received, proc_buf_size, line_size;
+ uint8_t header[16];
+
+ if (mp->state == state_warmup)
+ { /* prepare read image data */
+ /* PDBG (pixma_dbg (4, "**mp810_fill_buffer***** warmup *****\n")); */
+
+ RET_IF_ERR(wait_until_ready (s));
+ pixma_sleep (1000000); /* No need to sleep, actually, but Window's driver
+ * sleep 1.5 sec. */
+ mp->state = state_scanning;
+ mp->last_block = 0;
+
+ line_size = get_cis_ccd_line_size (s);
+ proc_buf_size = (2 * calc_shifting (s) + 2) * line_size;
+ mp->cb.buf = realloc (mp->cb.buf, CMDBUF_SIZE + IMAGE_BLOCK_SIZE + proc_buf_size);
+ if (!mp->cb.buf)
+ return PIXMA_ENOMEM;
+ mp->linebuf = mp->cb.buf + CMDBUF_SIZE;
+ mp->imgbuf = mp->data_left_ofs = mp->linebuf + line_size;
+ mp->data_left_len = 0;
+ }
+
+ do
+ { /* read complete image data from the scanner */
+ if (s->cancel)
+ return PIXMA_ECANCELED;
+ if ((mp->last_block & 0x28) == 0x28)
+ { /* end of image */
+ mp->state = state_finished;
+ /* PDBG (pixma_dbg (4, "**mp810_fill_buffer***** end of image *****\n")); */
+ return 0;
+ }
+ /* PDBG (pixma_dbg (4, "*mp810_fill_buffer***** moving %u bytes into buffer *****\n", mp->data_left_len)); */
+ memmove (mp->imgbuf, mp->data_left_ofs, mp->data_left_len);
+ error = read_image_block (s, header, mp->imgbuf + mp->data_left_len);
+ if (error < 0)
+ {
+ if (error == PIXMA_ECANCELED)
+ {
+ /* NOTE: I see this in traffic logs but I don't know its meaning. */
+ read_error_info (s, NULL, 0);
+ }
+ return error;
+ }
+
+ bytes_received = error;
+ /*PDBG (pixma_dbg (4, "*mp810_fill_buffer***** %u bytes received by read_image_block *****\n", bytes_received));*/
+ block_size = pixma_get_be32 (header + 12);
+ mp->last_block = header[8] & 0x38;
+ if ((header[8] & ~0x38) != 0)
+ {
+ PDBG(pixma_dbg (1, "WARNING: Unexpected result header\n"));
+ PDBG(pixma_hexdump (1, header, 16));
+ }
+ PASSERT(bytes_received == block_size);
+
+ if (block_size == 0)
+ { /* no image data at this moment. */
+ pixma_sleep (10000);
+ }
+ /* For TPU at 48 bits/pixel to output at 24 bits/pixel */
+#ifndef DEBUG_TPU_48
+#ifndef TPU_48
+ PDBG (pixma_dbg (1, "WARNING: 9000F using 24 instead of 48 bit processing \n"));
+#ifndef DEBUG_TPU_24
+ if (is_scanning_from_tpu (s))
+#endif
+ bytes_received = pack_48_24_bpc (mp->imgbuf + mp->data_left_len, bytes_received);
+#endif
+#endif
+ /* Post-process the image data */
+ mp->data_left_ofs = mp->imgbuf + mp->data_left_len + bytes_received;
+ mp->data_left_len = post_process_image_data (s, ib);
+ mp->data_left_ofs -= mp->data_left_len;
+ /* PDBG (pixma_dbg (4, "* mp810_fill_buffer: data_left_len %u \n", mp->data_left_len)); */
+ /* PDBG (pixma_dbg (4, "* mp810_fill_buffer: data_left_ofs %u \n", mp->data_left_ofs)); */
+ }
+ while (ib->rend == ib->rptr);
+
+ return ib->rend - ib->rptr;
+}
+
+static void mp810_finish_scan (pixma_t * s)
+{
+ int error;
+ mp810_t *mp = (mp810_t *) s->subdriver;
+
+ switch (mp->state)
+ {
+ case state_transfering:
+ drain_bulk_in (s);
+ /* fall through */
+ case state_scanning:
+ case state_warmup:
+ case state_finished:
+ /* Send the get TPU info message */
+ if (is_scanning_from_tpu (s) && mp->tpu_datalen == 0)
+ send_get_tpu_info_3 (s);
+ /* FIXME: to process several pages ADF scan, must not send
+ * abort_session and start_session between pages (last_block=0x28) */
+ if (mp->generation <= 2 || !is_scanning_from_adf (s)
+ || mp->last_block == 0x38)
+ {
+ error = abort_session (s); /* FIXME: it probably doesn't work in duplex mode! */
+ if (error < 0)
+ PDBG(pixma_dbg (1, "WARNING:abort_session() failed %d\n", error));
+
+ /* Generation 4: send XML end of scan dialog */
+ if (mp->generation == 4)
+ {
+ if (!send_xml_dialog (s, XML_END))
+ PDBG(pixma_dbg (1, "WARNING:XML_END dialog failed \n"));
+ }
+ }
+ mp->state = state_idle;
+ /* fall through */
+ case state_idle:
+ break;
+ }
+}
+
+static void mp810_wait_event (pixma_t * s, int timeout)
+{
+ /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for
+ * instance. */
+ while (s->events == 0 && handle_interrupt (s, timeout) > 0)
+ {
+ }
+}
+
+static int mp810_get_status (pixma_t * s, pixma_device_status_t * status)
+{
+ int error;
+
+ RET_IF_ERR(query_status (s));
+ status->hardware = PIXMA_HARDWARE_OK;
+ status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER;
+ status->cal =
+ (is_calibrated (s)) ? PIXMA_CALIBRATION_OK : PIXMA_CALIBRATION_OFF;
+ return 0;
+}
+
+static const pixma_scan_ops_t pixma_mp810_ops =
+{
+ mp810_open,
+ mp810_close,
+ mp810_scan,
+ mp810_fill_buffer,
+ mp810_finish_scan,
+ mp810_wait_event,
+ mp810_check_param,
+ mp810_get_status
+};
+
+#define DEVICE(name, model, pid, dpi, adftpu_min_dpi, adftpu_max_dpi, tpuir_min_dpi, tpuir_max_dpi, w, h, cap) { \
+ name, /* name */ \
+ model, /* model */ \
+ CANON_VID, pid, /* vid pid */ \
+ 0, /* iface */ \
+ &pixma_mp810_ops, /* ops */ \
+ dpi, 2*(dpi), /* xdpi, ydpi */ \
+ adftpu_min_dpi, adftpu_max_dpi, /* adftpu_min_dpi, adftpu_max_dpi */ \
+ tpuir_min_dpi, tpuir_max_dpi, /* tpuir_min_dpi, tpuir_max_dpi */ \
+ w, h, /* width, height */ \
+ PIXMA_CAP_EASY_RGB| \
+ PIXMA_CAP_GRAY| /* all scanners with software grayscale */ \
+ PIXMA_CAP_LINEART| /* all scanners with software lineart */ \
+ PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \
+}
+
+#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+
+const pixma_config_t pixma_mp810_devices[] =
+{
+ /* Generation 2: CCD */
+ DEVICE ("Canon PIXMA MP810", "MP810", MP810_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP960", "MP960", MP960_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+
+ /* Generation 3 CCD not managed as Generation 2 */
+ DEVICE ("Canon Pixma MP970", "MP970", MP970_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+
+ /* Flatbed scanner CCD (2007) */
+ DEVICE ("Canoscan 8800F", "8800F", CS8800F_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT),
+
+ /* PIXMA 2008 vintage CCD */
+ DEVICE ("Canon MP980 series", "MP980", MP980_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+
+ /* Generation 4 CCD */
+ DEVICE ("Canon MP990 series", "MP990", MP990_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+
+ /* Flatbed scanner (2010) */
+ DEVICE ("Canoscan 9000F", "9000F", CS9000F_PID, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPUIR /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT),
+
+ /* Latest devices (2010) Generation 4 CCD untested */
+ DEVICE ("Canon PIXMA MG8100", "MG8100", MG8100_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+
+ /* Latest devices (2011) Generation 4 CCD untested */
+ DEVICE ("Canon PIXMA MG8200", "MG8200", MG8200_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+
+ /* Flatbed scanner (2013) */
+ DEVICE ("Canoscan 9000F Mark II", "9000FMarkII", CS9000F_MII_PID, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPUIR | PIXMA_CAP_48BIT),
+
+ END_OF_DEVICE_LIST
+};
diff --git a/backend/pixma_rename.h b/backend/pixma_rename.h
new file mode 100644
index 0000000..ce68ed3
--- /dev/null
+++ b/backend/pixma_rename.h
@@ -0,0 +1,105 @@
+/* SANE - Scanner Access Now Easy.
+
+ Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+#ifndef PIXMA_RENAME_H
+#define PIXMA_RENAME_H
+
+
+#undef BACKEND_NAME
+#define BACKEND_NAME pixma
+
+#define pixma_cancel sanei_pixma_cancel
+#define pixma_check_dpi sanei_pixma_check_dpi
+#define pixma_check_result sanei_pixma_check_result
+#define pixma_check_scan_param sanei_pixma_check_scan_param
+#define pixma_cleanup sanei_pixma_cleanup
+#define pixma_close sanei_pixma_close
+#define pixma_cmd_transaction sanei_pixma_cmd_transaction
+#define pixma_collect_devices sanei_pixma_collect_devices
+#define pixma_connect sanei_pixma_connect
+#define pixma_dbg DBG
+#define pixma_disconnect sanei_pixma_disconnect
+#define pixma_dump sanei_pixma_dump
+#define pixma_enable_background sanei_pixma_enable_background
+#define pixma_exec sanei_pixma_exec
+#define pixma_exec_short_cmd sanei_pixma_exec_short_cmd
+#define pixma_fill_gamma_table sanei_pixma_fill_gamma_table
+#define pixma_find_scanners sanei_pixma_find_scanners
+#define pixma_get_be16 sanei_pixma_get_be16
+#define pixma_get_be32 sanei_pixma_get_be32
+#define pixma_get_config sanei_pixma_get_config
+#define pixma_get_device_config sanei_pixma_get_device_config
+#define pixma_get_device_id sanei_pixma_get_device_id
+#define pixma_get_device_model sanei_pixma_get_device_model
+#define pixma_get_device_status sanei_pixma_get_device_status
+#define pixma_get_string sanei_pixma_get_string
+#define pixma_get_time sanei_pixma_get_time
+#define pixma_hexdump sanei_pixma_hexdump
+#define pixma_init sanei_pixma_init
+#define pixma_io_cleanup sanei_pixma_io_cleanup
+#define pixma_io_init sanei_pixma_io_init
+#define pixma_map_status_errno sanei_pixma_map_status_errno
+#define pixma_mp150_devices sanei_pixma_mp150_devices
+#define pixma_mp730_devices sanei_pixma_mp730_devices
+#define pixma_mp750_devices sanei_pixma_mp750_devices
+#define pixma_mp810_devices sanei_pixma_mp810_devices
+#define pixma_iclass_devices sanei_pixma_iclass_devices
+#define pixma_newcmd sanei_pixma_newcmd
+#define pixma_open sanei_pixma_open
+#define pixma_print_supported_devices sanei_pixma_print_supported_devices
+#define pixma_read_image sanei_pixma_read_image
+#define pixma_read sanei_pixma_read
+#define pixma_reset_device sanei_pixma_reset_device
+#define pixma_scan sanei_pixma_scan
+#define pixma_set_be16 sanei_pixma_set_be16
+#define pixma_set_be32 sanei_pixma_set_be32
+#define pixma_set_debug_level sanei_pixma_set_debug_level
+#define pixma_set_interrupt_mode sanei_pixma_set_interrupt_mode
+#define pixma_sleep sanei_pixma_sleep
+#define pixma_strerror sanei_pixma_strerror
+#define pixma_sum_bytes sanei_pixma_sum_bytes
+#define pixma_wait_event sanei_pixma_wait_event
+#define pixma_wait_interrupt sanei_pixma_wait_interrupt
+#define pixma_write sanei_pixma_write
+
+
+#endif
diff --git a/backend/pixma_sane_options.c b/backend/pixma_sane_options.c
new file mode 100644
index 0000000..890b3ff
--- /dev/null
+++ b/backend/pixma_sane_options.c
@@ -0,0 +1,346 @@
+/* Automatically generated from pixma_sane.c */
+static const SANE_Range constraint_gamma_table =
+ { 0,255,0 };
+static const SANE_Range constraint_gamma =
+ { SANE_FIX(0.3),SANE_FIX(5),SANE_FIX(0) };
+static const SANE_Range constraint_threshold =
+ { 0,100,1 };
+static const SANE_Range constraint_threshold_curve =
+ { 0,127,1 };
+
+
+static
+int find_string_in_list(SANE_String_Const str, const SANE_String_Const *list)
+{
+ int i;
+ for (i = 0; list[i] && strcmp(str, list[i]) != 0; i++) {}
+ return i;
+}
+
+static
+int build_option_descriptors(struct pixma_sane_t *ss)
+{
+ SANE_Option_Descriptor *sod;
+ option_descriptor_t *opt;
+
+ memset(OPT_IN_CTX, 0, sizeof(OPT_IN_CTX));
+
+ opt = &(OPT_IN_CTX[opt_opt_num_opts]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_TITLE_NUM_OPTIONS;
+ sod->desc = SANE_DESC_NUM_OPTIONS;
+ sod->name = "";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_DETECT;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_opt_num_opts].info = 0;
+ opt->def.w = opt_last;
+ opt->val.w = opt_last;
+
+ opt = &(OPT_IN_CTX[opt__group_1]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_GROUP;
+ sod->title = SANE_I18N("Scan mode");
+ sod->desc = sod->title;
+
+ opt = &(OPT_IN_CTX[opt_resolution]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_TITLE_SCAN_RESOLUTION;
+ sod->desc = SANE_DESC_SCAN_RESOLUTION;
+ sod->name = "resolution";
+ sod->unit = SANE_UNIT_DPI;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC;
+ sod->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ sod->constraint.word_list = ss->dpi_list;
+ OPT_IN_CTX[opt_resolution].info = SANE_INFO_RELOAD_PARAMS;
+ opt->def.w = 75;
+ opt->val.w = 75;
+
+ opt = &(OPT_IN_CTX[opt_mode]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_STRING;
+ sod->title = SANE_TITLE_SCAN_MODE;
+ sod->desc = SANE_DESC_SCAN_MODE;
+ sod->name = "mode";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 31;
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC;
+ sod->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod->constraint.string_list = ss->mode_list;
+ OPT_IN_CTX[opt_mode].info = SANE_INFO_RELOAD_PARAMS;
+ opt->def.s = SANE_VALUE_SCAN_MODE_COLOR;
+ opt->val.w = find_string_in_list(opt->def.s, sod->constraint.string_list);
+
+ opt = &(OPT_IN_CTX[opt_source]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_STRING;
+ sod->title = SANE_TITLE_SCAN_SOURCE;
+ sod->desc = SANE_I18N("Selects the scan source (such as a document-feeder). Set source before mode and resolution. Resets mode and resolution to auto values.");
+ sod->name = "source";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 31;
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ sod->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod->constraint.string_list = ss->source_list;
+ OPT_IN_CTX[opt_source].info = 0;
+ opt->def.s = SANE_I18N("Flatbed");
+ opt->val.w = find_string_in_list(opt->def.s, sod->constraint.string_list);
+
+ opt = &(OPT_IN_CTX[opt_button_controlled]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_BOOL;
+ sod->title = SANE_I18N("Button-controlled scan");
+ sod->desc = SANE_I18N("When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button.");
+ sod->name = "button-controlled";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_INACTIVE;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_button_controlled].info = 0;
+ opt->def.w = SANE_FALSE;
+ opt->val.w = SANE_FALSE;
+
+ opt = &(OPT_IN_CTX[opt__group_2]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_GROUP;
+ sod->title = SANE_I18N("Gamma");
+ sod->desc = sod->title;
+
+ opt = &(OPT_IN_CTX[opt_custom_gamma]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_BOOL;
+ sod->title = SANE_TITLE_CUSTOM_GAMMA;
+ sod->desc = SANE_DESC_CUSTOM_GAMMA;
+ sod->name = "custom-gamma";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_custom_gamma].info = 0;
+ opt->def.w = SANE_TRUE;
+ opt->val.w = SANE_TRUE;
+
+ opt = &(OPT_IN_CTX[opt_gamma_table]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_TITLE_GAMMA_VECTOR;
+ sod->desc = SANE_DESC_GAMMA_VECTOR;
+ sod->name = "gamma-table";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 4096 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &constraint_gamma_table;
+ OPT_IN_CTX[opt_gamma_table].info = 0;
+
+ opt = &(OPT_IN_CTX[opt_gamma]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_FIXED;
+ sod->title = SANE_I18N("Gamma function exponent");
+ sod->desc = SANE_I18N("Changes intensity of midtones");
+ sod->name = "gamma";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &constraint_gamma;
+ OPT_IN_CTX[opt_gamma].info = 0;
+ opt->def.w = SANE_FIX(AUTO_GAMMA);
+ opt->val.w = SANE_FIX(AUTO_GAMMA);
+
+ opt = &(OPT_IN_CTX[opt__group_3]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_GROUP;
+ sod->title = SANE_I18N("Geometry");
+ sod->desc = sod->title;
+
+ opt = &(OPT_IN_CTX[opt_tl_x]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_FIXED;
+ sod->title = SANE_TITLE_SCAN_TL_X;
+ sod->desc = SANE_DESC_SCAN_TL_X;
+ sod->name = "tl-x";
+ sod->unit = SANE_UNIT_MM;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &ss->xrange;
+ OPT_IN_CTX[opt_tl_x].info = SANE_INFO_RELOAD_PARAMS;
+ opt->def.w = SANE_FIX(0);
+ opt->val.w = SANE_FIX(0);
+
+ opt = &(OPT_IN_CTX[opt_tl_y]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_FIXED;
+ sod->title = SANE_TITLE_SCAN_TL_Y;
+ sod->desc = SANE_DESC_SCAN_TL_Y;
+ sod->name = "tl-y";
+ sod->unit = SANE_UNIT_MM;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &ss->yrange;
+ OPT_IN_CTX[opt_tl_y].info = SANE_INFO_RELOAD_PARAMS;
+ opt->def.w = SANE_FIX(0);
+ opt->val.w = SANE_FIX(0);
+
+ opt = &(OPT_IN_CTX[opt_br_x]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_FIXED;
+ sod->title = SANE_TITLE_SCAN_BR_X;
+ sod->desc = SANE_DESC_SCAN_BR_X;
+ sod->name = "br-x";
+ sod->unit = SANE_UNIT_MM;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &ss->xrange;
+ OPT_IN_CTX[opt_br_x].info = SANE_INFO_RELOAD_PARAMS;
+ opt->def.w = sod->constraint.range->max;
+ opt->val.w = sod->constraint.range->max;
+
+ opt = &(OPT_IN_CTX[opt_br_y]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_FIXED;
+ sod->title = SANE_TITLE_SCAN_BR_Y;
+ sod->desc = SANE_DESC_SCAN_BR_Y;
+ sod->name = "br-y";
+ sod->unit = SANE_UNIT_MM;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &ss->yrange;
+ OPT_IN_CTX[opt_br_y].info = SANE_INFO_RELOAD_PARAMS;
+ opt->def.w = sod->constraint.range->max;
+ opt->val.w = sod->constraint.range->max;
+
+ opt = &(OPT_IN_CTX[opt__group_4]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_GROUP;
+ sod->title = SANE_I18N("Buttons");
+ sod->desc = sod->title;
+
+ opt = &(OPT_IN_CTX[opt_button_update]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_BUTTON;
+ sod->title = SANE_I18N("Update button state");
+ sod->desc = sod->title;
+ sod->name = "button-update";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 0;
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_button_update].info = 0;
+
+ opt = &(OPT_IN_CTX[opt_button_1]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_I18N("Button 1");
+ sod->desc = sod->title;
+ sod->name = "button-1";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_button_1].info = 0;
+ opt->def.w = 0;
+ opt->val.w = 0;
+
+ opt = &(OPT_IN_CTX[opt_button_2]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_I18N("Button 2");
+ sod->desc = sod->title;
+ sod->name = "button-2";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_button_2].info = 0;
+ opt->def.w = 0;
+ opt->val.w = 0;
+
+ opt = &(OPT_IN_CTX[opt_original]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_I18N("Type of original to scan");
+ sod->desc = sod->title;
+ sod->name = "original";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_original].info = 0;
+ opt->def.w = 0;
+ opt->val.w = 0;
+
+ opt = &(OPT_IN_CTX[opt_target]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_I18N("Target operation type");
+ sod->desc = sod->title;
+ sod->name = "target";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_target].info = 0;
+ opt->def.w = 0;
+ opt->val.w = 0;
+
+ opt = &(OPT_IN_CTX[opt_scan_resolution]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_I18N("Scan resolution");
+ sod->desc = sod->title;
+ sod->name = "scan-resolution";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED;
+ sod->constraint_type = SANE_CONSTRAINT_NONE;
+ OPT_IN_CTX[opt_scan_resolution].info = 0;
+ opt->def.w = 0;
+ opt->val.w = 0;
+
+ opt = &(OPT_IN_CTX[opt__group_5]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_GROUP;
+ sod->title = SANE_I18N("Extras");
+ sod->desc = sod->title;
+
+ opt = &(OPT_IN_CTX[opt_threshold]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_TITLE_THRESHOLD;
+ sod->desc = SANE_DESC_THRESHOLD;
+ sod->name = "threshold";
+ sod->unit = SANE_UNIT_PERCENT;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &constraint_threshold;
+ OPT_IN_CTX[opt_threshold].info = 0;
+ opt->def.w = 50;
+ opt->val.w = 50;
+
+ opt = &(OPT_IN_CTX[opt_threshold_curve]);
+ sod = &opt->sod;
+ sod->type = SANE_TYPE_INT;
+ sod->title = SANE_I18N("Threshold curve");
+ sod->desc = SANE_I18N("Dynamic threshold curve, from light to dark, normally 50-65");
+ sod->name = "threshold-curve";
+ sod->unit = SANE_UNIT_NONE;
+ sod->size = 1 * sizeof(SANE_Word);
+ sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE;
+ sod->constraint_type = SANE_CONSTRAINT_RANGE;
+ sod->constraint.range = &constraint_threshold_curve;
+ OPT_IN_CTX[opt_threshold_curve].info = 0;
+
+ return 0;
+
+}
+
diff --git a/backend/pixma_sane_options.h b/backend/pixma_sane_options.h
new file mode 100644
index 0000000..ccc9e34
--- /dev/null
+++ b/backend/pixma_sane_options.h
@@ -0,0 +1,51 @@
+/* Automatically generated from pixma_sane.c */
+
+typedef union {
+ SANE_Word w;
+ SANE_Int i;
+ SANE_Bool b;
+ SANE_Fixed f;
+ SANE_String s;
+ void *ptr;
+} option_value_t;
+
+typedef enum {
+ opt_opt_num_opts,
+ opt__group_1,
+ opt_resolution,
+ opt_mode,
+ opt_source,
+ opt_button_controlled,
+ opt__group_2,
+ opt_custom_gamma,
+ opt_gamma_table,
+ opt_gamma,
+ opt__group_3,
+ opt_tl_x,
+ opt_tl_y,
+ opt_br_x,
+ opt_br_y,
+ opt__group_4,
+ opt_button_update,
+ opt_button_1,
+ opt_button_2,
+ opt_original,
+ opt_target,
+ opt_scan_resolution,
+ opt__group_5,
+ opt_threshold,
+ opt_threshold_curve,
+ opt_last
+} option_t;
+
+
+typedef struct {
+ SANE_Option_Descriptor sod;
+ option_value_t val,def;
+ SANE_Word info;
+} option_descriptor_t;
+
+
+struct pixma_sane_t;
+static int build_option_descriptors(struct pixma_sane_t *ss);
+
diff --git a/backend/plustek-pp.h b/backend/plustek-pp.h
new file mode 100644
index 0000000..3c052f2
--- /dev/null
+++ b/backend/plustek-pp.h
@@ -0,0 +1,640 @@
+/** @file plustek-pp.h
+ * @brief Definitions for the backend.
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on plustek.[ch] file from the SANE package.<br>
+ *
+ * original code taken from sane-0.71<br>
+ * Copyright (C) 1997 Hypercore Software Design, Ltd.<br>
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.43 - bumped up version to reflect the former module code version
+ * - removed Version from ScannerCaps
+ * - added _E_FAULT
+ * - 0.44 - fix UL issues, as Long types default to int32_t now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __PLUSTEKPP_H__
+#define __PLUSTEKPP_H__
+
+/*.............................................................................
+ * the structures for driver communication
+ */
+typedef struct {
+ unsigned short x;
+ unsigned short y;
+} XY, *pXY;
+
+typedef struct {
+ unsigned short x;
+ unsigned short y;
+ unsigned short cx;
+ unsigned short cy;
+} CropRect, *pCropRect;
+
+typedef struct image {
+ unsigned long dwFlag;
+ CropRect crArea;
+ XY xyDpi;
+ unsigned short wDataType;
+} ImgDef, *pImgDef;
+
+typedef struct {
+ unsigned long dwPixelsPerLine;
+ unsigned long dwBytesPerLine;
+ unsigned long dwLinesPerArea;
+ struct image ImgDef;
+} CropInfo, *pCropInfo;
+
+/** definition of gamma maps
+ */
+typedef struct {
+ int len; /**< gamma table len */
+ int depth; /**< entry bit depth */
+ int map_id; /**< what map */
+ void *map; /**< pointer for map */
+} MapDef, *pMapDef;
+
+/** for offset stuff
+ */
+typedef struct {
+ int x;
+ int y;
+} OffsDef, *pOffsDef;
+
+/** useful for description tables
+ */
+typedef struct {
+ int id;
+ char *desc;
+} TabDef, *pTabDef;
+
+/** for defining the scanmodes
+ */
+typedef const struct mode_param
+{
+ int color;
+ int depth;
+ int scanmode;
+} ModeParam, *pModeParam;
+
+/**
+ */
+#define SFLAG_ADF 0x00000010 /* Automatic document feeder */
+#define SFLAG_MFP 0x00000020 /* MF-Keypad support */
+#define SFLAG_SheetFed 0x00000040 /* Sheetfed support */
+#define SFLAG_TPA 0x00000080 /* has transparency adapter */
+#define SFLAG_CUSTOM_GAMMA 0x00000200 /* driver supports custom gamma */
+
+/**
+ */
+#define SCANDEF_Inverse 0x00000001
+#define SCANDEF_UnlimitLength 0x00000002
+#define SCANDEF_StopWhenPaperOut 0x00000004
+#define SCANDEF_BoundaryDWORD 0x00000008
+#define SCANDEF_ColorBGROrder 0x00000010
+#define SCANDEF_BmpStyle 0x00000020
+#define SCANDEF_BoundaryWORD 0x00000040
+#define SCANDEF_NoMap 0x00000080 /* specified this flag will */
+ /* cause system ignores the */
+ /* siBrightness & siContrast */
+#define SCANDEF_Transparency 0x00000100 /* Scanning from transparency*/
+#define SCANDEF_Negative 0x00000200 /* Scanning from negative */
+#define SCANDEF_QualityScan 0x00000400 /* Scanning in quality mode */
+#define SCANDEF_BuildBwMap 0x00000800 /* Set default map */
+#define SCANDEF_ContinuousScan 0x00001000
+#define SCANDEF_DontBackModule 0x00002000 /* module will not back to */
+ /* home after image scanned */
+#define SCANDEF_RightAlign 0x00008000 /* 12-bit */
+
+#define SCANDEF_TPA (SCANDEF_Transparency | SCANDEF_Negative)
+
+#define SCANDEF_Adf 0x00020000 /* Scan from ADF tray */
+
+/* these values will be combined with ScannerInfo.dwFlag */
+#define _SCANNER_SCANNING 0x8000000
+#define _SCANNER_PAPEROUT 0x4000000
+
+/* for GetLensInformation */
+#if 0
+#define SOURCE_Reflection 0
+#define SOURCE_Transparency 1
+#define SOURCE_Negative 2
+#define SOURCE_ADF 3
+#endif
+
+/******************************************************************************
+ * Section 6 - additional definitions
+ */
+
+/* scan modes */
+#define COLOR_BW 0
+#define COLOR_HALFTONE 1
+#define COLOR_256GRAY 2
+#define COLOR_TRUE24 3
+#define COLOR_TRUE32 4
+#define COLOR_TRUE48 4 /* not sure if this should be the same as 32 */
+#define COLOR_TRUE36 5
+
+/* We don't support halftone mode now --> Plustek statement for USB */
+#define COLOR_GRAY16 6
+
+#define _MEASURE_BASE 300UL
+
+/** transparency/negative mode set ranges
+ */
+#define _TPAPageWidth 500U /* org. was 450 = 38.1 mm */
+#define _TPAPageHeight 510U /* org. was 460 = 38.9 mm */
+#define _TPAModeSupportMin COLOR_TRUE24
+#define _TPAModeSupportMax COLOR_TRUE48
+#define _TPAModeSupportDef COLOR_TRUE24
+#define _TPAMinDpi 150
+
+#define _NegativePageWidth 460U /* 38.9 mm */
+#define _NegativePageHeight 350U /* 29.6 mm */
+
+#define _DEF_DPI 50
+
+/*
+ * additional shared stuff between user-world and kernel mode
+ */
+#define _VAR_NOT_USED(x) ((x)=(x))
+
+/*
+ * for Gamma tables
+ */
+#define _MAP_RED 0
+#define _MAP_GREEN 1
+#define _MAP_BLUE 2
+#define _MAP_MASTER 3
+
+/*
+ * generic error codes...
+ */
+#define _OK 0
+
+#define _FIRST_ERR -9000
+
+#define _E_INIT (_FIRST_ERR-1) /* already initialized */
+#define _E_NOT_INIT (_FIRST_ERR-2) /* not initialized */
+#define _E_NULLPTR (_FIRST_ERR-3) /* internal NULL-PTR detected */
+#define _E_ALLOC (_FIRST_ERR-4) /* error allocating memory */
+#define _E_TIMEOUT (_FIRST_ERR-5) /* signals a timeout condition */
+#define _E_INVALID (_FIRST_ERR-6) /* invalid parameter detected */
+#define _E_INTERNAL (_FIRST_ERR-7) /* internal error */
+#define _E_BUSY (_FIRST_ERR-8) /* device is already in use */
+#define _E_ABORT (_FIRST_ERR-9) /* operation aborted */
+#define _E_LOCK (_FIRST_ERR-10) /* can't lock resource */
+#define _E_NOSUPP (_FIRST_ERR-11) /* feature or device not supported */
+#define _E_NORESOURCE (_FIRST_ERR-12) /* out of memo, resource busy... */
+#define _E_VERSION (_FIRST_ERR-19) /* version conflict */
+#define _E_NO_DEV (_FIRST_ERR-20) /* device does not exist */
+#define _E_NO_CONN (_FIRST_ERR-21) /* nothing connected */
+#define _E_PORTSEARCH (_FIRST_ERR-22) /* parport_enumerate failed */
+#define _E_NO_PORT (_FIRST_ERR-23) /* requested port does not exist */
+#define _E_REGISTER (_FIRST_ERR-24) /* cannot register this device */
+#define _E_SEQUENCE (_FIRST_ERR-30) /* caller sequence does not match */
+#define _E_NO_ASIC (_FIRST_ERR-31) /* can't detect ASIC */
+
+#ifdef __KERNEL__
+# define _E_FAULT (-EFAULT)
+#else
+# define _E_FAULT (_E_INTERNAL) /* should never happen in userspace */
+#endif
+
+#define _E_LAMP_NOT_IN_POS (_FIRST_ERR-40)
+#define _E_LAMP_NOT_STABLE (_FIRST_ERR-41)
+#define _E_NODATA (_FIRST_ERR-42)
+#define _E_BUFFER_TOO_SMALL (_FIRST_ERR-43)
+#define _E_DATAREAD (_FIRST_ERR-44)
+
+
+/************************ some definitions ***********************************/
+
+/* NOTE: needs to be kept in sync with table below */
+#define MODELSTR static char *ModelStr[] = { \
+ "unknown", \
+ "Primax 4800", \
+ "Primax 4800 Direct", \
+ "Primax 4800 Direct 30Bit", \
+ "Primax 9600 Direct 30Bit", \
+ "4800P", \
+ "4830P", \
+ "600P/6000P", \
+ "4831P", \
+ "9630P", \
+ "9630PL", \
+ "9636P", \
+ "A3I", \
+ "12000P/96000P", \
+ "9636P+/Turbo", \
+ "9636T/12000T", \
+ "P8", \
+ "P12", \
+ "PT12", \
+ "Genius Colorpage Vivid III V2", \
+ "USB-Device" \
+}
+
+/* the models */
+#define MODEL_OP_UNKNOWN 0 /* unknown */
+#define MODEL_PMX_4800 1 /* Primax Colorado 4800 like OP 4800 */
+#define MODEL_PMX_4800D 2 /* Primax Compact 4800 Direct, OP 600 R->G, G->R */
+#define MODEL_PMX_4800D3 3 /* Primax Compact 4800 Direct 30 */
+#define MODEL_PMX_9600D3 4 /* Primax Compact 9600 Direct 30 */
+#define MODEL_OP_4800P 5 /* 32k, 96001 ASIC, 24 bit, 300x600, 8.5x11.69 */
+#define MODEL_OP_4830P 6 /* 32k, 96003 ASIC, 30 bit, 300x600, 8.5x11.69 */
+#define MODEL_OP_600P 7 /* 32k, 96003 ASIC, 30 bit, 300x600, 8.5x11.69 */
+#define MODEL_OP_4831P 8 /* 128k, 96003 ASIC, 30 bit, 300x600, 8.5x11.69 */
+#define MODEL_OP_9630P 9 /* 128k, 96003 ASIC, 30 bit, 600x1200, 8.5x11.69 */
+#define MODEL_OP_9630PL 10 /* 128k, 96003 ASIC, 30 bit, 600x1200, 8.5x14 */
+#define MODEL_OP_9636P 11 /* 512k, 98001 ASIC, 36 bit, 600x1200, 8.5x11.69 */
+#define MODEL_OP_A3I 12 /* 128k, 96003 ASIC, 30 bit, 400x800, 11.69x17 */
+#define MODEL_OP_12000P 13 /* 128k, 96003 ASIC, 30 bit, 600x1200, 8.5x11.69 */
+#define MODEL_OP_9636PP 14 /* 512k, 98001 ASIC, 36 bit, 600x1200, 8.5x11.69 */
+#define MODEL_OP_9636T 15 /* like OP_9636PP + transparency */
+#define MODEL_OP_P8 16 /* 512k, 98003 ASIC, 36 bit, 300x600, 8.5x11.69 */
+#define MODEL_OP_P12 17 /* 512k, 98003 ASIC, 36 bit, 600x1200, 8.5x11.69 */
+#define MODEL_OP_PT12 18 /* like OP_P12 + transparency */
+#define MODEL_GEN_CPV2 19 /* Genius Colorpage Vivid III V2, ASIC 98003 */
+#define MODEL_UNKNOWN 20 /* not known/supported */
+
+#define _NO_BASE 0xFFFF
+
+/******************** from former plustek-share.h ***************************/
+
+/*
+ * for other OS than Linux, we might have to define the _IO macros
+ */
+#ifndef _IOC
+#define _IOC(dir,type,nr,size) \
+ (((dir) << 30) | \
+ ((type) << 8) | \
+ ((nr) << 0) | \
+ ((size) << 16))
+#endif
+
+#ifndef _IOC_DIR
+#define _IOC_DIR(cmd) (((cmd) >> 30) & 0x3)
+#endif
+
+#ifndef _IOC_SIZE
+#define _IOC_SIZE(cmd) (((cmd) >> 16) & 0x3FFF)
+#endif
+
+#ifndef _IOC_WRITE
+#define _IOC_WRITE 1U
+#endif
+
+#ifndef _IO
+#define _IO(type,nr) _IOC(0U,(type),(nr),0)
+#endif
+
+#ifndef _IOR
+#define _IOR(type,nr,size) _IOC(2U,(type),(nr),((UInt)sizeof(size)))
+#endif
+
+#ifndef _IOW
+#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),((UInt)sizeof(size)))
+#endif
+
+#ifndef _IOWR
+#define _IOWR(type,nr,size) _IOC(3U,(type),(nr),((UInt)sizeof(size)))
+#endif
+
+/*.............................................................................
+ * the ioctl interface
+ */
+#define _PTDRV_OPEN_DEVICE _IOW('x', 1, unsigned short)/* open */
+#define _PTDRV_GET_CAPABILITIES _IOR('x', 2, ScannerCaps) /* get caps */
+#define _PTDRV_GET_LENSINFO _IOR('x', 3, LensInfo) /* get lenscaps */
+#define _PTDRV_PUT_IMAGEINFO _IOW('x', 4, ImgDef) /* put image info*/
+#define _PTDRV_GET_CROPINFO _IOR('x', 5, CropInfo) /* get crop */
+#define _PTDRV_SET_ENV _IOWR('x',6, ScanInfo) /* set env. */
+#define _PTDRV_START_SCAN _IOR('x', 7, StartScan) /* start scan */
+#define _PTDRV_STOP_SCAN _IOWR('x', 8, short) /* stop scan */
+#define _PTDRV_CLOSE_DEVICE _IO('x', 9) /* close */
+#define _PTDRV_ACTION_BUTTON _IOR('x', 10, unsigned char)/* rd act. button*/
+#define _PTDRV_ADJUST _IOR('x', 11, AdjDef) /* adjust driver */
+#define _PTDRV_SETMAP _IOR('x', 12, MapDef) /* download gamma*/
+
+/*
+ * this version MUST match the one inside the driver to make sure, that
+ * both sides use the same structures. This version changes each time
+ * the ioctl interface changes
+ */
+#define _PTDRV_COMPAT_IOCTL_VERSION 0x0102
+#define _PTDRV_IOCTL_VERSION 0x0104
+
+/** for adjusting the parport stuff
+ */
+typedef struct {
+ int lampOff;
+ int lampOffOnEnd;
+ int warmup;
+ int enableTpa;
+
+ OffsDef pos; /* for adjusting normal scan area */
+ OffsDef tpa; /* for adjusting transparency scan area */
+ OffsDef neg; /* for adjusting negative scan area */
+
+ /* for adjusting the default gamma settings */
+ double rgamma;
+ double ggamma;
+ double bgamma;
+
+ double graygamma;
+
+} PPAdjDef, *pPPAdjDef;
+
+/** for adjusting the scanner settings
+ */
+typedef struct {
+
+ int direct_io;
+ int mov;
+
+ int lampOff;
+ int lampOffOnEnd;
+ int warmup;
+
+ OffsDef pos; /* for adjusting normal scan area */
+ OffsDef tpa; /* for adjusting transparency scan area */
+ OffsDef neg; /* for adjusting negative scan area */
+
+ /* for adjusting the default gamma settings */
+ double rgamma;
+ double ggamma;
+ double bgamma;
+
+ double graygamma;
+
+} AdjDef, *pAdjDef;
+
+typedef struct {
+ unsigned long dwFlag; /* refer to SECTION (1.2) */
+ unsigned long dwBytesPerLine;
+ unsigned long dwLinesPerScan;
+} StartScan, *pStartScan;
+
+typedef struct {
+ unsigned short wMin; /* minimum value */
+ unsigned short wDef; /* default value */
+ unsigned short wMax; /* software maximum value */
+ unsigned short wPhyMax; /* hardware maximum value (for DPI only)*/
+} RANGE, *PRANGE;
+
+typedef struct {
+ RANGE rDataType; /* available scan modes */
+ unsigned long dwFlag; /* refer to SECTION (1.2) */
+ unsigned short wIOBase; /* refer to SECTION (1.3) */
+ unsigned short wMaxExtentX; /* scanarea width */
+ unsigned short wMaxExtentY; /* scanarea height */
+ unsigned short AsicID; /* copy of RegAsicID */
+ unsigned short Model; /* model as best we can determine */
+} ScannerCaps, *pScannerCaps;
+
+typedef struct {
+ RANGE rDpiX;
+ RANGE rDpiY;
+ RANGE rExtentX;
+ RANGE rExtentY;
+ unsigned short wBeginX; /* offset from left */
+ unsigned short wBeginY; /* offset from top */
+} LensInfo, *pLensInfo;
+
+typedef struct {
+ unsigned char* pDither;
+ void* pMap;
+ ImgDef ImgDef;
+ unsigned short wMapType; /* refer to SECTION (3.2) */
+ unsigned short wDither; /* refer to SECTION (3.3) */
+ short siBrightness; /* refer to SECTION (3.5) */
+ short siContrast; /* refer to SECTION (3.6) */
+} ScanInfo, *pScanInfo;
+
+
+/* IDs the ASIC returns */
+#define _ASIC_IS_96001 0x0f /* value for 96001 */
+#define _ASIC_IS_96003 0x10 /* value for 96003 */
+#define _ASIC_IS_98001 0x81 /* value for 98001 */
+#define _ASIC_IS_98003 0x83 /* value for 98003 */
+
+#define _Transparency48OriginOffsetX 375
+#define _Transparency48OriginOffsetY 780
+
+#define _Transparency96OriginOffsetX 0x03DB /* org. was 0x0430 */
+#define _Negative96OriginOffsetX 0x03F3 /* org. was 0x0428 */
+
+/** Scanmodes
+ */
+#define _ScanMode_Color 0
+#define _ScanMode_AverageOut 1 /* CCD averaged 2 pixels value for output*/
+#define _ScanMode_Mono 2 /* not color mode */
+
+
+#ifndef __KERNEL__
+
+
+#define PLUSTEK_CONFIG_FILE "plustek_pp.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/*
+ * the default image size
+ */
+#define _DEFAULT_TLX 0 /* 0..216 mm */
+#define _DEFAULT_TLY 0 /* 0..297 mm */
+#define _DEFAULT_BRX 126 /* 0..216 mm*/
+#define _DEFAULT_BRY 76.21 /* 0..297 mm */
+
+#define _DEFAULT_TP_TLX 3.5 /* 0..42.3 mm */
+#define _DEFAULT_TP_TLY 10.5 /* 0..43.1 mm */
+#define _DEFAULT_TP_BRX 38.5 /* 0..42.3 mm */
+#define _DEFAULT_TP_BRY 33.5 /* 0..43.1 mm */
+
+#define _DEFAULT_NEG_TLX 1.5 /* 0..38.9 mm */
+#define _DEFAULT_NEG_TLY 1.5 /* 0..29.6 mm */
+#define _DEFAULT_NEG_BRX 37.5 /* 0..38.9 mm */
+#define _DEFAULT_NEG_BRY 25.5 /* 0..29.6 mm */
+
+/** image sizes for normal, transparent and negative modes
+ */
+#define _TP_X ((double)_TPAPageWidth/300.0 * MM_PER_INCH)
+#define _TP_Y ((double)_TPAPageHeight/300.0 * MM_PER_INCH)
+#define _NEG_X ((double)_NegativePageWidth/300.0 * MM_PER_INCH)
+#define _NEG_Y ((double)_NegativePageHeight/300.0 * MM_PER_INCH)
+
+/************************ some structures ************************************/
+
+enum {
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_EXT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_ENHANCEMENT_GROUP,
+ OPT_HALFTONE,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_CUSTOM_GAMMA,
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ NUM_OPTIONS
+};
+
+/** for compatiblity to version 0x0102 drivers
+ */
+typedef struct {
+
+ int lampOff;
+ int lampOffOnEnd;
+ int warmup;
+
+ OffsDef pos; /* for adjusting normal scan area */
+ OffsDef tpa; /* for adjusting transparency scan area */
+ OffsDef neg; /* for adjusting negative scan area */
+
+} CompatAdjDef, *pCompatAdjDef;
+
+/**
+ */
+typedef struct Plustek_Device
+{
+ SANE_Int initialized; /* device already initialized? */
+ struct Plustek_Device *next; /* pointer to next dev in list */
+ int fd; /* device handle */
+ char *name; /* (to avoid compiler warnings!)*/
+ SANE_Device sane; /* info struct */
+ SANE_Int max_x; /* max XY-extension of the scan-*/
+ SANE_Int max_y; /* area */
+ SANE_Range dpi_range; /* resolution range */
+ SANE_Range x_range; /* x-range of the scan-area */
+ SANE_Range y_range; /* y-range of the scan-area */
+ SANE_Int *res_list; /* to hold the available phys. */
+ SANE_Int res_list_size; /* resolution values */
+ ScannerCaps caps; /* caps reported by the driver */
+ AdjDef adj; /* for driver adjustment */
+
+ /*
+ * each device we support may need other access functions...
+ */
+ int (*open) ( const char*, void* );
+ int (*close) ( struct Plustek_Device* );
+ void (*shutdown) ( struct Plustek_Device* );
+ int (*getCaps) ( struct Plustek_Device* );
+ int (*getLensInfo)( struct Plustek_Device*, pLensInfo );
+ int (*getCropInfo)( struct Plustek_Device*, pCropInfo );
+ int (*putImgInfo) ( struct Plustek_Device*, pImgDef );
+ int (*setScanEnv) ( struct Plustek_Device*, pScanInfo );
+ int (*setMap) ( struct Plustek_Device*, SANE_Word*,
+ SANE_Word, SANE_Word );
+ int (*startScan) ( struct Plustek_Device*, pStartScan );
+ int (*stopScan) ( struct Plustek_Device*, short* );
+ int (*readImage) ( struct Plustek_Device*, SANE_Byte*, unsigned long );
+
+ int (*prepare) ( struct Plustek_Device*, SANE_Byte* );
+ int (*readLine) ( struct Plustek_Device* );
+
+} Plustek_Device, *pPlustek_Device;
+
+#ifndef SANE_OPTION
+/* for compatibility with older versions */
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+} Option_Value;
+#endif
+
+typedef struct Plustek_Scanner
+{
+ struct Plustek_Scanner *next;
+ SANE_Pid reader_pid; /* process id of reader */
+ SANE_Status exit_code; /* status of the reader process */
+ int r_pipe; /* pipe to reader process */
+ int w_pipe; /* pipe from reader process */
+ unsigned long bytes_read; /* number of bytes currently read*/
+ Plustek_Device *hw; /* pointer to current device */
+ Option_Value val[NUM_OPTIONS];
+ SANE_Byte *buf; /* the image buffer */
+ SANE_Bool scanning; /* TRUE during scan-process */
+ SANE_Parameters params; /* for keeping the parameter */
+
+ /************************** gamma tables *********************************/
+
+ SANE_Word gamma_table[4][4096];
+ SANE_Range gamma_range;
+ int gamma_length;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+} Plustek_Scanner, *pPlustek_Scanner;
+
+/** for collecting configuration info...
+ */
+typedef struct {
+
+ char devName[PATH_MAX];
+
+ /* contains the stuff to adjust... */
+ AdjDef adj;
+
+} CnfDef, *pCnfDef;
+#endif /* guard __KERNEL__ */
+
+#endif /* guard __PLUSTEKPP_H__ */
+
+/* END PLUSTEK-PP.H .........................................................*/
diff --git a/backend/plustek-pp_dac.c b/backend/plustek-pp_dac.c
new file mode 100644
index 0000000..dd35ce7
--- /dev/null
+++ b/backend/plustek-pp_dac.c
@@ -0,0 +1,2658 @@
+/* @file plustek-pp_dac.c
+ * @brief all the shading function formerly found in shading.c.
+ * don't ask me why I called this file dac.c...
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - no changes
+ * - 0.33 - added some comments
+ * - 0.34 - slight changes
+ * - 0.35 - removed SetInitialGainRAM from structure pScanData
+ * - 0.36 - added dacP96001WaitForShading and changed dacP96WaitForShading to
+ * dacP96003WaitForShading
+ * - changes, due to define renaming
+ * - 0.37 - removed dacP98FillShadingDarkToShadingRegister()
+ * - removed // comments
+ * - some code cleanup
+ * - 0.38 - added P12 stuff
+ * - 0.39 - no changes
+ * - 0.40 - disabled the A3I stuff
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/************************** local definitions ********************************/
+
+/***************************** global vars ***********************************/
+
+static const Byte a_bCorrectTimesTable[4] = {0, 1, 2, 4};
+
+static ULong dwADCPipeLine = 4 * 4;
+static ULong dwReadyLen;
+
+/*************************** local functions *********************************/
+
+/**
+ */
+static void dacP98AdjustGainAverage( pScanData ps )
+{
+ pUChar pDest, pSrce;
+ ULong dw, dw1;
+ UShort wSum;
+
+ pDest = pSrce = ps->pScanBuffer1;
+
+ for (dw1 = 0; dw1 < (2560 * 3) / 16; dw1++, pDest++) {
+ for (dw = 0, wSum = 0; dw < 16; dw++, pSrce++)
+ wSum += *pSrce;
+
+ *pDest = wSum / 16;
+ }
+}
+
+/**
+ */
+static void dacP98FillDarkDAC( pScanData ps )
+{
+ IODataRegisterToDAC( ps, 0x20, ps->bRedDAC );
+ IODataRegisterToDAC( ps, 0x21, ps->bGreenDAC );
+ IODataRegisterToDAC( ps, 0x22, ps->bBlueDAC );
+}
+
+/**
+ */
+static void dacP98SetReadFBKRegister( pScanData ps )
+{
+ IODataToRegister( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE + _SCAN_1ST_AVERAGE;
+
+ IOSelectLampSource( ps );
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+
+ ps->AsicReg.RD_Motor0Control = _MotorOn;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_Origin = 4;
+ ps->AsicReg.RD_Pixels = 512;
+ ps->AsicReg.RD_Motor1Control = 0;
+ ps->AsicReg.RD_Motor0Control = 0;
+
+ ps->AsicReg.RD_ModelControl = _LED_CONTROL + _LED_ACTIVITY;
+
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut ) {
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_ModelControl += _ModelDpi300;
+
+ } else {
+ ps->AsicReg.RD_Dpi = 600;
+ ps->AsicReg.RD_ModelControl += _ModelDpi600;
+ }
+}
+
+/**
+ */
+static UShort dacP98CalDarkOff( pScanData ps, UShort wChDarkOff,
+ UShort wDACCompareHigh, UShort wDACOffset )
+{
+ UShort wTemp;
+
+ if ((_CCD_518 == ps->Device.bCCDID) || (_CCD_535 == ps->Device.bCCDID)) {
+ wTemp = wChDarkOff + wDACOffset;
+ } else {
+
+ if (_CCD_3797 == ps->Device.bCCDID) {
+ if (wChDarkOff > wDACOffset) {
+ wTemp = wChDarkOff - wDACOffset;
+ } else {
+ wTemp = 0;
+ }
+ } else {
+ if (wChDarkOff > wDACCompareHigh) {
+ wTemp = wChDarkOff - wDACCompareHigh;
+ } else {
+ wTemp = 0;
+ }
+ }
+ }
+ return wTemp;
+}
+
+/**
+ */
+static Bool dacP98AdjustDAC( UShort DarkOff, UShort wHigh,
+ UShort wLow, pUChar pbReg, Bool *fDACStopFlag )
+{
+ if (DarkOff > wHigh) {
+ if ((DarkOff - wHigh) > 10) {
+ if ((DarkOff - wHigh) > 2550)
+ *pbReg += ((DarkOff - wHigh) / 20);
+ else
+ *pbReg += ((DarkOff - wHigh) / 10);
+ } else
+ *pbReg += 1;
+
+ if (!(*pbReg))
+ *pbReg = 0xff;
+
+ *fDACStopFlag = _FALSE;
+ return _FALSE;
+
+ } else {
+ if (DarkOff < wLow) {
+ if (DarkOff > 0)
+ *pbReg -= 2;
+ else
+ *pbReg -= 10;
+
+ *fDACStopFlag = _FALSE;
+ return _FALSE;
+ } else
+ return _TRUE;
+ }
+}
+
+/**
+ */
+static Bool dacP98CheckChannelDarkLevel( pScanData ps )
+{
+ Bool fDACStopFlag = _TRUE;
+
+ dacP98AdjustDAC( ps->Shade.DarkOffset.Colors.Red,
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red,
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red,
+ &ps->bRedDAC, &fDACStopFlag );
+ dacP98AdjustDAC( ps->Shade.DarkOffset.Colors.Green,
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green,
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green,
+ &ps->bGreenDAC, &fDACStopFlag );
+ dacP98AdjustDAC( ps->Shade.DarkOffset.Colors.Blue,
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue,
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue,
+ &ps->bBlueDAC, &fDACStopFlag );
+
+ return fDACStopFlag;
+}
+
+/** Average left offset 30, 16 pixels as each color's dark level
+ */
+static void dacP98FillChannelDarkLevelControl( pScanData ps )
+{
+ DataPointer p;
+ ULong dwPos, dw, dwSum;
+
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut ) {
+ dwPos = 0x10 * 3;
+ } else {
+ dwPos = 0x20 * 2;
+ }
+
+ for (p.pw = (pUShort)(ps->pScanBuffer1 + dwPos), dwSum = 0, dw = 16;
+ dw; dw--, p.pw++) {
+ dwSum += (ULong)(*p.pw);
+ }
+
+ ps->Shade.DarkOffset.Colors.Red = (UShort)(dwSum / 16);
+
+ for (p.pw = (pUShort)(ps->pScanBuffer1 + dwPos + 1024), dwSum = 0, dw = 16;
+ dw; dw--, p.pw++) {
+ dwSum += (ULong)(*p.pw);
+ }
+
+ ps->Shade.DarkOffset.Colors.Green = (UShort)(dwSum / 16);
+
+ for (p.pw = (pUShort)(ps->pScanBuffer1 + dwPos + 1024 * 2), dwSum = 0, dw = 16;
+ dw; dw--, p.pw++) {
+ dwSum += (ULong)(*p.pw);
+ }
+
+ ps->Shade.DarkOffset.Colors.Blue = (UShort)(dwSum / 16);
+}
+
+/**
+ */
+static void dacP98AdjustGain( pScanData ps )
+{
+ DataPointer p;
+ ULong dw;
+ UShort w;
+ Byte b[3];
+ pUChar pbReg[3];
+
+ dacP98AdjustGainAverage( ps );
+
+ pbReg[0] = &ps->bRedGainIndex;
+ pbReg[1] = &ps->bGreenGainIndex;
+ pbReg[2] = &ps->bBlueGainIndex;
+
+ for (w = 0, p.pb = ps->pScanBuffer1; w < 3; w++) {
+
+ for (dw = 2560 / 16, b [w] = 0; dw; dw--, p.pb++) {
+ if (b [w] < *p.pb)
+ b [w] = *p.pb;
+ }
+ if (b[w] < _GAIN_LOW) {
+ if ((_GAIN_P98_HIGH - b[w]) < b[w])
+ *(pbReg[w]) += 1;
+ else
+ *(pbReg[w]) += 4;
+ } else {
+ if (b[w] > _GAIN_P98_HIGH)
+ *(pbReg[w]) -= 1;
+ }
+ }
+}
+
+/**
+ */
+static void dacP98CheckLastGain( pScanData ps )
+{
+ DataPointer p;
+ ULong dw;
+ UShort w;
+ Byte b[3];
+ pUChar pbReg[3];
+
+ dacP98AdjustGainAverage( ps );
+
+ pbReg[0] = &ps->bRedGainIndex;
+ pbReg[1] = &ps->bGreenGainIndex;
+ pbReg[2] = &ps->bBlueGainIndex;
+
+ for (w = 0, p.pb = ps->pScanBuffer1; w < 3; w++) {
+ for (dw = 2560 / 16, b [w] = 0; dw; dw--, p.pb++) {
+ if (b[w] < *p.pb)
+ b[w] = *p.pb;
+ }
+
+ if (b[w] > _GAIN_P98_HIGH) {
+ *(pbReg [w]) -= 1;
+ }
+ }
+}
+
+/**
+ */
+static void dacP98FillGainInitialRestRegister( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegThresholdGapControl, ps->AsicReg.RD_ThresholdGapCtrl );
+ IODataToRegister( ps, ps->RegModelControl, ps->AsicReg.RD_ModelControl );
+
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+static void dacP98SetInitialGainRegister( pScanData ps )
+{
+ DacP98FillGainOutDirectPort( ps ); /* R/G/B GainOut to scanner */
+ dacP98FillGainInitialRestRegister( ps );/* Model Control2, LED, Correct.*/
+}
+
+/** Find the the most ideal intensity for each color (RGB)
+ */
+static void dacP98SetRGBGainRegister( pScanData ps )
+{
+ IOCmdRegisterToScanner( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+ IOSelectLampSource( ps );
+
+ IOCmdRegisterToScanner( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+ dacP98SetInitialGainRegister( ps );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_Motor0Control = _MotorOn + _MotorDirForward + _MotorHEightStep;
+ ps->AsicReg.RD_XStepTime = ps->bSpeed4;
+
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut ) {
+ ps->AsicReg.RD_ModelControl = _LED_CONTROL + _ModelDpi300;
+ ps->AsicReg.RD_Origin = 32 + 60 + 4;
+ } else {
+ ps->AsicReg.RD_ModelControl = _LED_CONTROL + _ModelDpi600;
+ ps->AsicReg.RD_Origin = 64 + 120 + 4;
+ }
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_Pixels = 2560;
+
+ IOPutOnAllRegisters( ps );
+}
+
+/**
+ */
+static void dacP98FillRGBMap( pUChar pBuffer )
+{
+ ULong dw, dw1;
+ pULong pdw = (pULong)(pBuffer);
+
+ for( dw = 256, dw1 = 0; dw; dw--, dw1 += 0x01010101 ) {
+ *pdw++ = dw1;
+ *pdw++ = dw1;
+ *pdw++ = dw1;
+ *pdw++ = dw1;
+ }
+}
+
+/** here we download the current mapping table
+ */
+static void dacP98DownloadMapTable( pScanData ps, pUChar pBuffer )
+{
+ Byte bAddr;
+ ULong i;
+
+ IODataToRegister( ps, ps->RegScanControl,
+ (Byte)((ps->AsicReg.RD_ScanControl & 0xfc) | _SCAN_BYTEMODE));
+
+ for( i = 3, bAddr = _MAP_ADDR_RED; i--; bAddr += _MAP_ADDR_SIZE ) {
+
+ IODataToRegister( ps, ps->RegModeControl, _ModeMappingMem );
+ IODataToRegister( ps, ps->RegMemoryLow, 0);
+ IODataToRegister( ps, ps->RegMemoryHigh, bAddr );
+
+ IOMoveDataToScanner( ps, pBuffer, 4096 );
+ pBuffer += 4096;
+ }
+
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+}
+
+/**
+ */
+static void dacP98DownloadShadingTable( pScanData ps,
+ pUChar pBuffer, ULong size )
+{
+ IODataToRegister( ps, ps->RegModeControl, _ModeShadingMem );
+ IODataToRegister( ps, ps->RegMemoryLow, 0 );
+ IODataToRegister( ps, ps->RegMemoryHigh, 0 );
+
+ /* set 12 bits output color */
+ IODataToRegister( ps, ps->RegScanControl,
+ (Byte)(ps->AsicReg.RD_ScanControl | _SCAN_12BITMODE));
+
+ /* MoveDataToShadingRam() */
+ IOMoveDataToScanner( ps ,pBuffer, size );
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+ else
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+
+ DacP98FillShadingDarkToShadingRegister( ps );
+}
+
+/** Build a linear map for asic (this model is 12-bit scanner, there are 4096
+ * map entries but just generate 256 level output, so the content must be 0 to
+ * 255), then fill the shading buffer (the first 3k), and map table (the last
+ * 1k) to asic for R & G & B channels.
+ *
+ * I need pScanBuffer2 size = 5400 * 2 * 3
+ * pScanBuffer1 size = 256 * 16 * 2
+ */
+static void dacP98SetInitialGainRAM( pScanData ps )
+{
+ memset( ps->pScanBuffer2, 0xff, (5400 * 2 * 3));
+
+ dacP98DownloadShadingTable( ps, ps->pScanBuffer2, (5400 * 2 * 3));
+
+ dacP98FillRGBMap( ps->pScanBuffer1 ); /* Fill 12 Bits R Map */
+ dacP98FillRGBMap( ps->pScanBuffer1 + 4096 ); /* Fill 12 Bits G Map */
+ dacP98FillRGBMap( ps->pScanBuffer1 + 8192 ); /* Fill 12 Bits B Map */
+
+ dacP98DownloadMapTable( ps, ps->pScanBuffer1 );
+}
+
+/** Find the the most ideal intensity for each color (RGB)
+ */
+static void dacP98AdjustRGBGain( pScanData ps )
+{
+ int bCorrectTimes;
+
+ DBG( DBG_LOW, "dacP98AdjustRGBGain()\n" );
+
+ ps->OpenScanPath( ps );
+ dacP98SetInitialGainRAM( ps ); /* set shading ram and read out data to */
+ ps->CloseScanPath( ps );
+
+ ps->bRedGainIndex = 0x02;
+ ps->bGreenGainIndex = 0x02;
+ ps->bBlueGainIndex = 0x02;
+
+ for (bCorrectTimes = 10; bCorrectTimes; bCorrectTimes-- ) {
+
+ dacP98SetRGBGainRegister( ps ); /* shading the most brightness &*/
+ ps->PauseColorMotorRunStates( ps ); /* stop scan states */
+ IOReadOneShadingLine( ps, ps->pScanBuffer1, 2560UL );
+ dacP98AdjustGain( ps );
+ }
+
+ dacP98SetRGBGainRegister( ps ); /* shading the most brightness & */
+ ps->PauseColorMotorRunStates( ps ); /* stop scan states */
+
+ IOReadOneShadingLine( ps, ps->pScanBuffer1, 2560UL );
+
+ dacP98CheckLastGain( ps );
+ DacP98FillGainOutDirectPort( ps );
+}
+
+/**
+ */
+static void dacP98SetAdjustShadingRegister( pScanData ps )
+{
+ DBG( DBG_LOW, "dacP98SetAdjustShadingRegister()\n" );
+
+ IOCmdRegisterToScanner( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE + _SCAN_1ST_AVERAGE;
+ IOSelectLampSource( ps );
+
+ IOCmdRegisterToScanner( ps, ps->RegScanControl,
+ ps->AsicReg.RD_ScanControl );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_Motor0Control = _MotorOn + _MotorHEightStep + _MotorDirForward;
+ ps->AsicReg.RD_XStepTime = ps->bSpeed1;
+ ps->AsicReg.RD_ModelControl = _LED_ACTIVITY + _LED_CONTROL;
+
+ if (ps->bSetScanModeFlag & _ScanMode_AverageOut) {
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_Pixels = 2700;
+ ps->AsicReg.RD_ModelControl += _ModelDpi300;
+ } else {
+ ps->AsicReg.RD_Dpi = 600;
+ ps->AsicReg.RD_Pixels = 5400;
+ ps->AsicReg.RD_ModelControl += _ModelDpi600;
+ }
+ ps->AsicReg.RD_Origin = 4;
+
+ IOPutOnAllRegisters( ps );
+}
+
+/**
+ */
+static void dacP98ReadShadingScanLine( pScanData ps )
+{
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND );
+
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+
+ while((IOReadFifoLength( ps ) < dwReadyLen) &&
+ !MiscCheckTimer(&timer)) {
+ _DO_UDELAY( 1 );
+ }
+
+ IOReadColorData( ps, ps->pScanBuffer2, ps->dwShadingLen );
+}
+
+/**
+ */
+static void dacP98GainResize( pUShort pValue, UShort wResize)
+{
+ DataType Data;
+ Byte bTemp;
+
+ Data.dwValue = ((ULong) *pValue) * (ULong) wResize / 100;
+
+ if (0x1000 <= Data.dwValue)
+ Data.wValue = 0xfff;
+
+ Data.wValue *= 16;
+
+ bTemp = Data.wOverlap.b1st;
+ Data.wOverlap.b1st = Data.wOverlap.b2nd;
+ Data.wOverlap.b2nd = bTemp;
+
+ *pValue = Data.wValue;
+}
+
+/**
+ */
+static void dacP98SortHilightShadow( pScanData ps, pUShort pwData,
+ ULong dwHilightOff, ULong dwShadowOff )
+{
+ ULong dwLines, dwPixels;
+ UShort wVCmp, wTmp;
+ pUShort pw;
+
+ for (dwPixels = 0; dwPixels < (ps->dwShadingPixels - 4); dwPixels++) {
+
+ pw = (pUShort)ps->Shade.pHilight + dwHilightOff + dwPixels;
+ wVCmp = pwData[dwPixels] & 0xfffU;
+
+ for (dwLines = _DEF_BRIGHTEST_SKIP; dwLines--; pw += 5400UL) {
+ if (wVCmp > *pw) {
+ wTmp = wVCmp;
+ wVCmp = *pw;
+ *pw = wTmp;
+ }
+ }
+ }
+ for (dwPixels = 0; dwPixels < (ps->dwShadingPixels - 4); dwPixels++) {
+
+ pw = ps->pwShadow + dwShadowOff + dwPixels;
+ wVCmp = pwData [dwPixels] & 0xfffU;
+
+ for (dwLines = _DEF_DARKEST_SKIP; dwLines--; pw += 5400UL) {
+
+ if (wVCmp < *pw) {
+ wTmp = wVCmp;
+ wVCmp = *pw;
+ *pw = wTmp;
+ }
+ }
+ }
+}
+
+/**
+ */
+static void dacP98WriteBackToShadingRAM( pScanData ps )
+{
+ ULong dw;
+
+ pUShort pBuffer = (pUShort)ps->pScanBuffer2;
+
+ DBG( DBG_LOW, "dacP98WriteBackToShadingRAM()\n" );
+
+ if (ps->DataInf.wPhyDataType >= COLOR_TRUE24) {
+
+ for (dw = 0; dw < 5400; dw++) {
+
+ *pBuffer = ((pUShort)ps->pScanBuffer1)[dw] -
+ ps->Shade.DarkOffset.Colors.Red;
+ dacP98GainResize( pBuffer,
+ ps->Shade.pCcdDac->GainResize.Colors.Red );
+ pBuffer ++;
+
+ *pBuffer = ((pUShort)ps->pScanBuffer1)[dw + 5400] -
+ ps->Shade.DarkOffset.Colors.Green;
+ dacP98GainResize( pBuffer,
+ ps->Shade.pCcdDac->GainResize.Colors.Green );
+ pBuffer ++;
+
+ *pBuffer = ((pUShort)ps->pScanBuffer1)[dw + 5400 * 2] -
+ ps->Shade.DarkOffset.Colors.Blue;
+ dacP98GainResize( pBuffer,
+ ps->Shade.pCcdDac->GainResize.Colors.Blue );
+ pBuffer ++;
+ }
+
+ } else {
+ for (dw = 0; dw < 5400; dw++) {
+
+ DataType Data;
+ Byte bTemp;
+
+ *pBuffer = ((pUShort)ps->pScanBuffer1)[dw + 5400] -
+ ps->Shade.DarkOffset.Colors.Green;
+
+ Data.wValue = (*pBuffer) * 16;
+ bTemp = Data.wOverlap.b1st;
+ Data.wOverlap.b1st = Data.wOverlap.b2nd;
+ Data.wOverlap.b2nd = bTemp;
+ *pBuffer = Data.wValue;
+ pBuffer++;
+ }
+
+ }
+ dacP98DownloadShadingTable( ps, ps->pScanBuffer2, (5400 * 2 * 3));
+}
+
+/**
+ */
+static void dacP98ShadingRunLoop( pScanData ps )
+{
+ int i;
+ DataPointer p;
+
+ p.pb = ps->a_nbNewAdrPointer;
+
+ switch( ps->IO.portMode ) {
+ case _PORT_SPP:
+ case _PORT_BIDI:
+ *p.pw++ = 0;
+ for (i = 0; i < 7; i++)
+ *p.pdw++ = 0x00800700;
+ *p.pw = 0;
+ break;
+
+ default:
+ *p.pb++ = 0;
+ for (i = 0; i < 15; i++)
+ *p.pw++ = 0xf888;
+ *p.pb = 0;
+ }
+
+ IOSetToMotorRegister( ps );
+}
+
+/**
+ */
+static void dacP98Adjust12BitShading( pScanData ps )
+{
+ DataPointer pd, pt;
+ ULong dw, dw1, dwLoop;
+
+ DBG( DBG_LOW, "dacP98Adjust12BitShading()\n" );
+
+ memset( ps->pScanBuffer1, 0, (5400 * 4 * 3));
+
+ if( ps->Shade.pHilight && (_Shading_32Times == ps->bShadingTimeFlag)) {
+
+ memset( ps->Shade.pHilight, 0, (ps->dwHilight * 2UL));
+
+ for (dw = 0; dw < ps->dwShadow; dw++)
+ ps->pwShadow[dw] = 0xfff;
+ }
+
+ /*
+ * in the original code this function does not exist !
+ * (of course the code behind the function does ;-)
+ */
+ dacP98SetAdjustShadingRegister( ps );
+
+ dacP98ShadingRunLoop( ps );
+ _DODELAY( 24 );
+
+ if ((ps->DataInf.dwScanFlag & SCANDEF_TPA ) ||
+ (_Shading_32Times == ps->bShadingTimeFlag)) {
+ dwLoop = 32;
+ } else {
+ if (_Shading_16Times == ps->bShadingTimeFlag) {
+ dwLoop = 16;
+ } else {
+ dwLoop = 4;
+ }
+ }
+
+ for (dw1 = 0; dw1 < dwLoop; dw1++) {
+
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+
+ dacP98ReadShadingScanLine( ps );
+
+ if((_Shading_32Times == ps->bShadingTimeFlag) && ps->Shade.pHilight ) {
+
+ dacP98SortHilightShadow( ps, (pUShort)ps->pScanBuffer2, 0, 0 );
+ dacP98SortHilightShadow( ps, (pUShort)ps->pScanBuffer2 +
+ ps->dwShadingPixels,
+ ps->dwHilightCh, ps->dwShadowCh );
+
+ dacP98SortHilightShadow( ps, (pUShort)ps->pScanBuffer2 +
+ ps->dwShadingPixels * 2,
+ ps->dwHilightCh * 2, ps->dwShadowCh * 2);
+ }
+
+ /* SumAdd12BitShadingR */
+ pd.pw = (pUShort)ps->pScanBuffer2;
+ pt.pdw = (pULong)(ps->pScanBuffer1 + dwADCPipeLine);
+
+ for (dw = 5400 - 4; dw; dw--, pd.pw++, pt.pdw++)
+ *pt.pdw += (ULong)(*pd.pw & 0x0fff);
+
+ /* SumAdd10BitShadingG */
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut )
+ pd.pw = (pUShort)(ps->pScanBuffer2 + 2700 * 2);
+ else
+ pd.pw = (pUShort)(ps->pScanBuffer2 + 5400 * 2);
+
+ pt.pdw = (pULong)(ps->pScanBuffer1 + 5400 * 4 + dwADCPipeLine);
+
+ for (dw = 5400 - 4; dw; dw--, pd.pw++, pt.pdw++)
+ *pt.pdw += (ULong)(*pd.pw & 0x0fff);
+
+ /* SumAdd12BitShadingB */
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut )
+ pd.pw = (pUShort)(ps->pScanBuffer2 + 2700 * 4);
+ else
+ pd.pw = (pUShort)(ps->pScanBuffer2 + 5400 * 4);
+
+ pt.pdw = (pULong)(ps->pScanBuffer1 + 5400 * 8 + dwADCPipeLine);
+
+ for (dw = 5400 - 4; dw; dw--, pd.pw++, pt.pdw++)
+ *pt.pdw += (ULong)(*pd.pw * 94 / 100 & 0x0fff);
+
+ /* one line */
+ if (IOReadFifoLength( ps ) <= 2500)
+ IORegisterDirectToScanner( ps, ps->RegRefreshScanState );
+ }
+
+ TPAP98001AverageShadingData( ps );
+
+ ps->OpenScanPath( ps );
+ dacP98WriteBackToShadingRAM( ps );
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+static Bool dacP98WaitForShading( pScanData ps )
+{
+ Byte oldLineControl;
+
+ DBG( DBG_LOW, "dacP98WaitForShading()\n" );
+
+ /*
+ * before getting the shading data, (re)init the ASIC
+ */
+ ps->InitialSetCurrentSpeed( ps );
+ ps->ReInitAsic( ps, _TRUE );
+
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,
+ ps->AsicReg.RD_LineControl );
+
+ ps->Shade.DarkOffset.Colors.Red = 0;
+ ps->Shade.DarkOffset.Colors.Green = 0;
+ ps->Shade.DarkOffset.Colors.Blue = 0;
+
+ /*
+ * according to the scan mode, switch on the lamp
+ */
+ IOSelectLampSource( ps );
+ IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut ) {
+ ps->AsicReg.RD_ModelControl = _LED_CONTROL + _ModelDpi300;
+ } else {
+ ps->AsicReg.RD_ModelControl = _LED_CONTROL + _ModelDpi600;
+ }
+ IOCmdRegisterToScanner( ps, ps->RegModelControl,
+ ps->AsicReg.RD_ModelControl );
+
+ IOCmdRegisterToScanner( ps, ps->RegModeControl, _ModeScan);
+
+ oldLineControl = ps->AsicReg.RD_LineControl;
+ IOSetXStepLineScanTime( ps, _DEFAULT_LINESCANTIME );
+
+ /* set line control */
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,
+ ps->AsicReg.RD_LineControl );
+
+ /* Wait for Sensor to position */
+ if( !ps->GotoShadingPosition( ps ))
+ return _FALSE;
+
+ ps->AsicReg.RD_LineControl = oldLineControl;
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,
+ ps->AsicReg.RD_LineControl );
+
+ dwADCPipeLine = 4 * 4;
+
+ if( ps->bSetScanModeFlag & _ScanMode_AverageOut ) {
+ dwReadyLen = 2500;
+ ps->dwShadingLen = 2700 * 2;
+ ps->dwShadingPixels = 2700;
+ } else {
+ dwReadyLen = 5000;
+ ps->dwShadingLen = 5400 * 2;
+ ps->dwShadingPixels = 5400;
+ }
+
+ dacP98AdjustRGBGain ( ps );
+ DacP98AdjustDark ( ps );
+ dacP98Adjust12BitShading( ps );
+
+ ps->OpenScanPath( ps );
+ DacP98FillShadingDarkToShadingRegister( ps );
+
+ if ( COLOR_BW != ps->DataInf.wPhyDataType )
+ dacP98DownloadMapTable( ps, ps->a_bMapTable );
+
+ ps->CloseScanPath( ps );
+
+ return _TRUE;
+}
+
+/** Set RAM bank and size, then write the data to it
+ */
+static void dacP96FillWhole4kRAM( pScanData ps, pUChar pBuf )
+{
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegMemAccessControl,
+ ps->Asic96Reg.RD_MemAccessControl );
+
+ ps->AsicReg.RD_ModeControl = _ModeProgram;
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+
+ IOMoveDataToScanner( ps, pBuf, ps->ShadingBankSize );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+static void dacP96FillChannelDarkOffset( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegRedChDarkOffset,
+ ps->Asic96Reg.RD_RedChDarkOff );
+ IODataToRegister( ps, ps->RegGreenChDarkOffset,
+ ps->Asic96Reg.RD_GreenChDarkOff );
+ IODataToRegister( ps, ps->RegBlueChDarkOffset,
+ ps->Asic96Reg.RD_BlueChDarkOff );
+
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+static void dacP96FillEvenOddControl( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegRedChEvenOffset,
+ ps->Asic96Reg.RD_RedChEvenOff );
+ IODataToRegister( ps, ps->RegGreenChEvenOffset,
+ ps->Asic96Reg.RD_GreenChEvenOff );
+ IODataToRegister( ps, ps->RegBlueChEvenOffset,
+ ps->Asic96Reg.RD_BlueChEvenOff );
+ IODataToRegister( ps, ps->RegRedChOddOffset,
+ ps->Asic96Reg.RD_RedChOddOff );
+ IODataToRegister( ps, ps->RegGreenChOddOffset,
+ ps->Asic96Reg.RD_GreenChOddOff );
+ IODataToRegister( ps, ps->RegBlueChOddOffset,
+ ps->Asic96Reg.RD_BlueChOddOff );
+
+ ps->CloseScanPath( ps );
+}
+
+/** Used for capture data to pScanData->pPrescan16.
+ * Used to replace:
+ * 1) ReadFBKScanLine (3 * 1024, 5)
+ * 2) ReadOneScanLine (3 * 2560, 11)
+ * 4) ReadShadingScanLine (3 * 1280, 6)
+ */
+static void dacP96ReadDataWithinOneSecond( pScanData ps,
+ ULong dwLen, Byte bBlks )
+{
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND );
+
+ while((IODataRegisterFromScanner( ps, ps->RegFifoOffset) < bBlks) &&
+ !MiscCheckTimer(&timer));
+
+ IOReadScannerImageData( ps, ps->pPrescan16, dwLen );
+}
+
+/**
+ */
+static void dacP96GetEvenOddOffset( pUChar pb, pWordVal pwv )
+{
+ ULong dw;
+ UShort we, wo;
+
+ for (dw = 8, we = wo = 0; dw; dw-- ) {
+ we += *pb++;
+ wo += *pb++;
+ }
+
+ pwv->b1st = (Byte)(we >> 3);
+ pwv->b2nd = (Byte)(wo >> 3);
+}
+
+/**
+ */
+static void dacP96SumAverageShading( pScanData ps, pUChar pDest, pUChar pSrce )
+{
+ ULong dw;
+ UShort wLeft[6];
+ UShort wSumL, wSumR;
+
+ pSrce += ps->Offset70 + ps->Device.DataOriginX;
+ pDest += ps->Offset70 + ps->Device.DataOriginX;
+
+ wLeft[0] = wLeft[1] = wLeft[2] =
+ wLeft[3] = wLeft[4] = wLeft[5] = (UShort)*pSrce;
+
+ wSumL = wLeft[0] * 6;
+ wSumR = (UShort)pSrce[1] + pSrce[2] + pSrce[3] + pSrce[4] +
+ pSrce[5] + pSrce[6];
+
+/* for (dw = 2772; dw; dw--, pSrce++, pDest++) */
+ for (dw = ps->BufferSizePerModel - 6; dw; dw--, pSrce++, pDest++) {
+
+ *pDest = (Byte)(((UShort)*pSrce * 4 + wSumL + wSumR) / 16);
+ wSumL = wSumL - wLeft [0] + (UShort)*pSrce;
+
+ wLeft[0] = wLeft[1];
+ wLeft[1] = wLeft[2];
+ wLeft[2] = wLeft[3];
+ wLeft[3] = wLeft[4];
+ wLeft[4] = wLeft[5];
+ wLeft[5] = (UShort)*pSrce;
+ wSumR = wSumR - (UShort) *(pSrce + 1) + (UShort) *(pSrce + 7);
+ }
+}
+
+/**
+ */
+static void dacP96WriteLinearGamma( pScanData ps,
+ pUChar pBuf, ULong dwEntries, Byte bBank )
+{
+ ULong dw;
+ pULong pdw = (pULong)(pBuf + ps->ShadingBufferSize);
+
+ for (dw = 0; dwEntries; pdw++, dwEntries--, dw += 0x01010101)
+ *pdw = dw;
+
+ ps->Asic96Reg.RD_MemAccessControl = bBank;
+
+ dacP96FillWhole4kRAM( ps, pBuf );
+}
+
+/**
+ */
+static void dacP96FillChannelShadingOffset( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegRedChShadingOffset,
+ ps->Asic96Reg.u28.RD_RedChShadingOff);
+ IODataToRegister( ps, ps->RegGreenChShadingOffset,
+ ps->Asic96Reg.u29.RD_GreenChShadingOff);
+ IODataToRegister( ps, ps->RegBlueChShadingOffset,
+ ps->Asic96Reg.RD_BlueChShadingOff);
+
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+static void dacP96GetHilightShadow( pScanData ps,
+ pUChar pBuf, pUChar pChOff, pUChar pHigh )
+{
+ ULong dw;
+
+ /* GetShadingImageExtent (ps) */
+ if (ps->DataInf.wAppDataType < COLOR_256GRAY)
+ dw = (ULong)(ps->DataInf.crImage.cx & 0xfff8);
+ else
+ dw = (ULong)ps->DataInf.crImage.cx;
+
+ for( pBuf += ps->DataInf.crImage.x, *pChOff = 0xff, *pHigh = 0;
+ dw; dw--, pBuf++ ) {
+
+ if (*pChOff < *pBuf) {
+ if (*pHigh < *pBuf)
+ *pHigh = *pBuf; /* brightest */
+ } else
+ *pChOff = *pBuf; /* darkest */
+ }
+}
+
+/**
+ */
+static void dacP96ReadColorShadingLine( pScanData ps )
+{
+ Byte b2ndDiscard, b3rdDiscard, b1stReadLines, b2ndReadLines;
+ Byte b3rdReadLines;
+ ULong dw;
+ DataType Data;
+
+ /* ClearScanBuffer1 (ps) */
+ /* buffer to keep sum of data */
+ memset( ps->pScanBuffer1, 0, ps->BufferForDataRead1 );
+
+ b2ndDiscard = ps->b2ndLinesOffset;
+ b3rdDiscard = ps->b1stLinesOffset;
+ b1stReadLines = b2ndReadLines = b3rdReadLines = 8;
+
+ while( _TRUE ) {
+
+ /* ReadShadingScanLine(ps) */
+ dacP96ReadDataWithinOneSecond( ps, ps->ShadingScanLineLen,
+ ps->ShadingScanLineBlks );
+
+ if (b1stReadLines) {
+
+ b1stReadLines--;
+
+ /* SumAdd10BitShadingR */
+ for (dw = 0; dw < ps->BufferSizeBase; dw++)
+ ((pUShort)ps->pScanBuffer1)[dw] += (UShort)ps->pPrescan16 [dw];
+ }
+
+ if (!b2ndDiscard) {
+ if (b2ndReadLines) {
+ b2ndReadLines--;
+ /* SumAdd10BitShadingG */
+ for (dw = ps->BufferSizeBase;dw < (ULong)ps->BufferSizeBase * 2;dw++) {
+ ((pUShort)ps->pScanBuffer1)[dw] +=
+ (UShort)ps->pPrescan16[dw];
+ }
+ }
+ } else
+ b2ndDiscard--;
+
+ if (!b3rdDiscard) {
+ if (b3rdReadLines) {
+ b3rdReadLines--;
+ /* SumAdd10BitShadingB */
+ for (dw = ps->BufferSizeBase * 2;
+ dw < (ULong)ps->BufferSizeBase * 3; dw++) {
+ ((pUShort)ps->pScanBuffer1)[dw] +=
+ (UShort)ps->pPrescan16[dw];
+ }
+ } else
+ break;
+ } else
+ b3rdDiscard--;
+
+ IORegisterDirectToScanner( ps, ps->RegRefreshScanState );
+ }
+
+ for (dw = 0; dw < (ULong)ps->BufferSizeBase * 3; dw++) {
+
+ Data.wOverlap.b1st =
+ Data.wOverlap.b2nd = (Byte)(((pUShort)ps->pScanBuffer1)[dw] / 8);
+ ((pUShort)ps->pPrescan16)[dw] = Data.wValue;
+ }
+}
+
+/**
+ */
+static void dacP96SetShadingGainProc( pScanData ps, Byte bHigh, ULong dwCh )
+{
+ Byte bDark, bGain, bGainX2, bGainX4, bMask;
+ pUChar pbChDark, pbSrce, pbDest;
+ ULong dw;
+
+ pbChDark = NULL, pbSrce = NULL, pbDest = NULL;
+ bDark = 0, bGain = 0, bGainX2 = 0, bGainX4 = 0, bMask = 0;
+
+ switch( dwCh ) {
+
+ case 0: /* red */
+ pbChDark = &ps->Asic96Reg.u28.RD_RedChShadingOff;
+ pbSrce = ps->pPrescan16;
+ pbDest = ps->pScanBuffer1 + ps->Offset70 + ps->Device.DataOriginX;
+ bGainX2 = 1;
+ bGainX4 = 3;
+ bMask = 0x3c;
+ break;
+
+ case 1: /* green */
+ pbChDark = &ps->Asic96Reg.u29.RD_GreenChShadingOff;
+ pbSrce = ps->pPrescan16 + ps->BufferSizePerModel;
+ pbDest = ps->pScanBuffer1 + ps->Offset70 + ps->ShadingBankSize +
+ ps->Device.DataOriginX;
+ bGainX2 = 4;
+ bGainX4 = 0x0c;
+ bMask = 0x33;
+ break;
+
+ case 2:
+ pbChDark = &ps->Asic96Reg.RD_BlueChShadingOff;
+ pbSrce = ps->pPrescan16 + ps->BufferSizePerModel * 2;
+ pbDest = ps->pScanBuffer1 + ps->Offset70 + ps->ShadingBankSize * 2 +
+ ps->Device.DataOriginX;
+ bGainX2 = 0x10;
+ bGainX4 = 0x30;
+ bMask = 0x0f;
+ }
+
+ bDark = *pbChDark;
+ if ((bHigh -= bDark) > 60U) {
+ /* Hilight - Shadow > 60, Quality not so good */
+ bGain = bGainX2; /* Gain x 2 */
+ if (bHigh > 120U)
+ bGain = bGainX4; /* Poor quality, Gain x 4 */
+ } else
+ bGain = 0;
+
+ ps->Asic96Reg.RD_ShadingCorrectCtrl &= bMask;
+ ps->Asic96Reg.RD_ShadingCorrectCtrl |= bGain;
+
+ if (!bGain) {
+ /* GammaGain1 (ps) */
+ for (dw = ps->BufferSizePerModel; dw; dw--, pbSrce++, pbDest++)
+ if (*pbSrce > bDark)
+ *pbDest = (*pbSrce - bDark) * 4;
+ else
+ *pbDest = 0;
+ } else
+ if (bGain == bGainX2) {
+ /* GammaGain2 */
+ for (dw = ps->BufferSizePerModel; dw; dw--, pbSrce++, pbDest++)
+ if (*pbSrce > bDark)
+ *pbDest = (*pbSrce - bDark) * 2;
+ else
+ *pbDest = 0;
+ } else {
+ /* GammaGain4 (ps) */
+ memcpy( pbDest, pbSrce, ps->BufferSizePerModel );
+ *pbChDark = 0;
+ }
+}
+
+/**
+ */
+static void dacP96FillShadingAndGammaTable( pScanData ps )
+{
+ ps->Asic96Reg.RD_MemAccessControl = ps->ShadingBankRed; /* R */
+ dacP96FillWhole4kRAM( ps, ps->pPrescan16 );
+
+ ps->Asic96Reg.RD_MemAccessControl = ps->ShadingBankGreen; /* G */
+ dacP96FillWhole4kRAM( ps, ps->pPrescan16 );
+
+ ps->Asic96Reg.RD_MemAccessControl = ps->ShadingBankBlue; /* B */
+ dacP96FillWhole4kRAM( ps, ps->pPrescan16 );
+}
+
+/**
+ */
+static void dacP96SetInitialGainRAM( pScanData ps )
+{
+ ULong dw, dw1;
+ pULong pdw = (pULong)(ps->pPrescan16 + ps->ShadingBufferSize);
+
+ memset( ps->pPrescan16, 0xff, ps->ShadingBufferSize );
+
+ for (dw = 256, dw1 = 0; dw; dw--, dw1 += 0x01010101, pdw++)
+ *pdw = dw1;
+
+ dacP96FillShadingAndGammaTable( ps );
+}
+
+/**
+ */
+static void dacP96Adjust10BitShading( pScanData ps )
+{
+ pULong pdw;
+ ULong dw;
+ Byte bRedHigh, bGreenHigh, bBlueHigh;
+
+ /* ShadingMotorRunLoop(ps)
+ * set scan states as:
+ * 40h, 00, 00, 00, 40h, 01, 03, 02, ... (repeat)
+ * so, read a R/G/B line every 2 steps
+ */
+ pdw = (pULong)ps->a_nbNewAdrPointer;
+ for (dw = 0; dw < 4; dw++) {
+ *pdw++ = 0x40;
+ *pdw++ = 0x02030140;
+ }
+
+ dacP96SetInitialGainRAM( ps); /* initiates the shading buffers and maps */
+
+ /* SetAdjustShadingRegister(ps) */
+ /* Prepare Physical/2 dpi, 8.5" scanning condition for reading the shading area */
+ ps->AsicReg.RD_ScanControl = ps->bLampOn | _SCAN_BYTEMODE;
+ ps->Asic96Reg.RD_MotorControl = ps->IgnorePF | ps->MotorOn |
+ _MotorDirForward;
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelWhiteIs0;
+ ps->AsicReg.RD_Dpi = ps->PhysicalDpi / 2;
+ ps->AsicReg.RD_Origin = (UShort)(ps->Offset70 +
+ ps->Device.DataOriginX);
+ ps->AsicReg.RD_Pixels = ps->BufferSizeBase;
+ IOPutOnAllRegisters( ps );
+
+ ps->Asic96Reg.RD_ShadingCorrectCtrl = _ShadingRCorrectX4|_ShadingGCorrectX4|
+ _ShadingBCorrectX4;
+ IOCmdRegisterToScanner( ps, ps->RegShadingCorrectCtrl,
+ ps->Asic96Reg.RD_ShadingCorrectCtrl );
+
+ /* Read shaded data and do average */
+ dacP96ReadColorShadingLine( ps );
+
+ /* ExpandAndAverage (ps) ------------------------------------------------*/
+/*
+ for (dw = 0; dw < 1280 * 3; dw++)
+ {
+ Data.wOverlap.b1st =
+ Data.wOverlap.b2nd = (Byte)(((pUShort) ps->pScanBuffer1)[dw] / 8);
+ ((pULong) ps->pPrescan16)[dw] = Data.wValue;
+ }
+*/
+ /* CalculateShadingOffset (ps); */
+ dacP96GetHilightShadow( ps, ps->pPrescan16,
+ &ps->Asic96Reg.u28.RD_RedChShadingOff, &bRedHigh );
+
+ dacP96GetHilightShadow( ps, ps->pPrescan16 + ps->BufferSizePerModel,
+ &ps->Asic96Reg.u29.RD_GreenChShadingOff, &bGreenHigh );
+
+ dacP96GetHilightShadow( ps, ps->pPrescan16 + ps->BufferSizePerModel * 2,
+ &ps->Asic96Reg.RD_BlueChShadingOff, &bBlueHigh );
+
+ /* SubTheImageDataBase (ps) */
+ dacP96SetShadingGainProc( ps, bRedHigh, 0 ); /* red */
+ dacP96SetShadingGainProc( ps, bGreenHigh, 1 ); /* green */
+ dacP96SetShadingGainProc( ps, bBlueHigh, 2 ); /* blue */
+ dacP96FillChannelShadingOffset( ps );
+
+ IOCmdRegisterToScanner( ps, ps->RegShadingCorrectCtrl,
+ ps->Asic96Reg.RD_ShadingCorrectCtrl );
+
+ dacP96SumAverageShading( ps, ps->pPrescan16, ps->pScanBuffer1 );
+ dacP96SumAverageShading( ps, ps->pPrescan16 + ps->ShadingBankSize,
+ ps->pScanBuffer1 + ps->ShadingBankSize );
+ dacP96SumAverageShading( ps, ps->pPrescan16 + ps->ShadingBankSize * 2,
+ ps->pScanBuffer1 + ps->ShadingBankSize * 2 );
+
+ /* --------------------------------------------------------------------- */
+
+ /* PrewriteBackToGammaShadingRAM( ps) */
+ dacP96WriteLinearGamma( ps, ps->pPrescan16, 256, ps->ShadingBankRed );
+ dacP96WriteLinearGamma( ps, ps->pPrescan16 + ps->ShadingBankSize, 256,
+ ps->ShadingBankGreen );
+ dacP96WriteLinearGamma( ps, ps->pPrescan16 + ps->ShadingBankSize * 2,
+ 256, ps->ShadingBankBlue );
+}
+
+/**
+ */
+static Bool dacP96003WaitForShading( pScanData ps )
+{
+ int bCorrectTimes;
+ DataPointer p;
+ ULong dw;
+ UShort w;
+ WordVal wv;
+ pUChar pbReg[3];
+ Byte b[3];
+
+ DBG( DBG_LOW, "dacP96003WaitForShading()\n" );
+
+ /* TurnOnLamp () */
+ ps->AsicReg.RD_ScanControl |= ps->bLampOn;
+ IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+
+ ps->Asic96Reg.RD_LedControl = _LedActControl | _LedMotorActEnable;
+ IOCmdRegisterToScanner(ps, ps->RegLedControl, ps->Asic96Reg.RD_LedControl);
+
+ if ( ps->GotoShadingPosition( ps )) {
+
+ /* AdjustRGBGain () =================================================*/
+ ps->Asic96Reg.RD_RedGainOut = ps->Asic96Reg.RD_GreenGainOut =
+ ps->Asic96Reg.RD_BlueGainOut = 8;
+ ps->Asic96Reg.u26.RD_ModelControl2 = _Model2DirectOutPort;
+
+ IOCmdRegisterToScanner(ps, ps->RegModelControl2, _Model2DirectOutPort);
+
+ for ( bCorrectTimes = 4; bCorrectTimes >= 1; bCorrectTimes-- ) {
+
+ ps->PauseColorMotorRunStates( ps );
+
+ /* SetRGBGainRegister () ----------------------------------------*/
+ ps->AsicReg.RD_ScanControl = ps->bLampOn | _SCAN_BYTEMODE;
+ dacP96SetInitialGainRAM( ps );
+
+ /* SetInitialGainRegister () ++++++++++++++++++++++++++++++++++++*/
+ ps->Asic96Reg.u28.RD_RedChShadingOff =
+ ps->Asic96Reg.u29.RD_GreenChShadingOff =
+ ps->Asic96Reg.RD_BlueChShadingOff =
+ ps->Asic96Reg.RD_RedChDarkOff =
+ ps->Asic96Reg.RD_GreenChDarkOff =
+ ps->Asic96Reg.RD_BlueChDarkOff =
+ ps->Asic96Reg.RD_RedChEvenOff =
+ ps->Asic96Reg.RD_GreenChEvenOff =
+ ps->Asic96Reg.RD_BlueChEvenOff =
+ ps->Asic96Reg.RD_RedChOddOff =
+ ps->Asic96Reg.RD_GreenChOddOff =
+ ps->Asic96Reg.RD_BlueChOddOff = 0;
+ ps->Asic96Reg.RD_ShadingCorrectCtrl = _ShadingRCorrectX4 |
+ _ShadingGCorrectX4 |
+ _ShadingBCorrectX4;
+
+ dacP96FillChannelShadingOffset( ps );
+ dacP96FillChannelDarkOffset( ps );
+ dacP96FillEvenOddControl( ps );
+
+ /* FillGainOutDirectPort (); */
+ ps->OpenScanPath( ps );
+ IODataToRegister( ps, ps->RegRedGainOutDirect,
+ ps->Asic96Reg.RD_RedGainOut );
+ IODataToRegister( ps, ps->RegGreenGainOutDirect,
+ ps->Asic96Reg.RD_GreenGainOut );
+ IODataToRegister( ps, ps->RegBlueGainOutDirect,
+ ps->Asic96Reg.RD_BlueGainOut );
+
+ /* FillGainInitialRestRegister (); */
+ IODataToRegister( ps, ps->RegModelControl2,
+ ps->Asic96Reg.u26.RD_ModelControl2 );
+ IODataToRegister( ps, ps->RegThresholdGapControl,
+ ps->AsicReg.RD_ThresholdGapCtrl );
+ IODataToRegister( ps, ps->RegLedControl,
+ ps->Asic96Reg.RD_LedControl );
+
+ IODataToRegister( ps, ps->RegShadingCorrectCtrl,
+ ps->Asic96Reg.RD_ShadingCorrectCtrl );
+
+ ps->CloseScanPath( ps );
+
+ ps->Asic96Reg.RD_MotorControl = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_ScanControl = ps->bLampOn | _SCAN_BYTEMODE;
+ ps->Asic96Reg.RD_MotorControl = (ps->IgnorePF |
+ ps->MotorOn | _MotorDirForward);
+
+ ps->AsicReg.RD_Origin = 142;
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelWhiteIs0;
+ ps->AsicReg.RD_Dpi = ps->PhysicalDpi;
+ ps->AsicReg.RD_Pixels = ps->BufferSizePerModel;
+
+ IOPutOnAllRegisters( ps );
+
+ /*---------------------------------------------------------------*/
+
+ /* ReadOneScanLine (); */
+ dacP96ReadDataWithinOneSecond( ps, ps->OneScanLineLen, 11 );
+
+ /* AdjustGain () */
+ /* FindTheMaxGain (), AdjustGainOutData (); */
+
+ pbReg[0] = &ps->Asic96Reg.RD_RedGainOut;
+ pbReg[1] = &ps->Asic96Reg.RD_GreenGainOut;
+ pbReg[2] = &ps->Asic96Reg.RD_BlueGainOut;
+
+ for (w = 0, p.pb = ps->pPrescan16; w < 3; w++) {
+/* CHANGE: org was: for (dw = 2560, b[w] = 0; dw; dw--, p.pb++) { */
+ for (dw = (ps->OneScanLineLen/3), b[w] = 0; dw; dw--, p.pb++) {
+
+ if (b[w] < *p.pb)
+ b[w] = *p.pb;
+ }
+
+ if (b[w] < _GAIN_LOW)
+ *(pbReg[w]) += a_bCorrectTimesTable[bCorrectTimes - 1];
+ else
+ if (b[w] > _GAIN_P96_HIGH)
+ *(pbReg[w]) -= a_bCorrectTimesTable[bCorrectTimes - 1];
+ }
+ }
+
+ /*===================================================================*/
+
+ /* SonyFBK ()/ToshibaFBK ()====================================*/
+ /*FillRGBDarkLevel0Table (); */
+ memset( ps->pPrescan16, 0xff, ps->ShadingBankSize );
+
+ for( dw = 0, p.pb = ps->pPrescan16 + ps->ShadingBufferSize;
+ dw <=255; dw++, p.pb++ ) {
+ *p.pb = (Byte) dw;
+ }
+
+ dacP96FillShadingAndGammaTable( ps );
+
+ ps->PauseColorMotorRunStates( ps );
+
+ /* SetReadFBKRegister () */
+ ps->Asic96Reg.RD_MotorControl = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_ScanControl = ps->bLampOn | _SCAN_BYTEMODE;
+ ps->Asic96Reg.RD_MotorControl = ps->IgnorePF | ps->MotorOn |
+ _MotorDirForward;
+
+ ps->AsicReg.RD_Origin = 22;
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelWhiteIs0;
+ ps->AsicReg.RD_Dpi = ps->PhysicalDpi;
+ ps->AsicReg.RD_Pixels = ps->FBKScanLineLenBase;
+
+ IOPutOnAllRegisters( ps );
+
+ /* ReadFBKScanLine () */
+ dacP96ReadDataWithinOneSecond( ps, ps->FBKScanLineLen,
+ ps->FBKScanLineBlks );
+
+ /*===================================================================*/
+ if ( ps->fSonyCCD ) {
+
+ /* FillChannelDarkLevelControl (); */
+ for ( p.pb = ps->pPrescan16 + 32, w = 0, dw = 16;
+ dw; dw--, p.pb++) {
+ w += (UShort) *p.pb;
+ }
+
+ ps->Asic96Reg.RD_RedChDarkOff = (Byte)(w / 16);
+
+ for ( p.pb = ps->pPrescan16 + 32 + ps->FBKScanLineLenBase, w = 0, dw = 16;
+ dw; dw--, p.pb++ ) {
+ w += (UShort)*p.pb;
+ }
+
+ ps->Asic96Reg.RD_GreenChDarkOff = (Byte)(w / 16);
+
+ for ( p.pb = ps->pPrescan16 + 32 + ps->FBKScanLineLenBase * 2, w = 0, dw = 16;
+ dw; dw--, p.pb++) {
+ w += (UShort)*p.pb;
+ }
+
+ ps->Asic96Reg.RD_BlueChDarkOff = (Byte)(w / 16);
+
+ dacP96FillChannelDarkOffset( ps );
+
+ } else {
+
+ /* FillToshibaDarkLevelControl (); */
+ dacP96GetEvenOddOffset( ps->pPrescan16 + 32, &wv );
+ ps->Asic96Reg.RD_RedChEvenOff = wv.b1st;
+ ps->Asic96Reg.RD_RedChOddOff = wv.b2nd;
+
+ dacP96GetEvenOddOffset( ps->pPrescan16 + 32 + ps->FBKScanLineLenBase, &wv );
+ ps->Asic96Reg.RD_GreenChEvenOff = wv.b1st;
+ ps->Asic96Reg.RD_GreenChOddOff = wv.b2nd;
+
+ dacP96GetEvenOddOffset( ps->pPrescan16 + 32 + ps->FBKScanLineLenBase * 2, &wv );
+ ps->Asic96Reg.RD_BlueChEvenOff = wv.b1st;
+ ps->Asic96Reg.RD_BlueChOddOff = wv.b2nd;
+
+ dacP96FillEvenOddControl( ps );
+ }
+
+ /* SetInitialGainRAM (); */
+ dacP96Adjust10BitShading( ps );
+
+ return _TRUE;
+ }
+
+ return _FALSE;
+}
+
+/**
+ */
+static void dacP96001ToSetShadingAddress( pScanData ps, pUChar pData )
+{
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegMemAccessControl,
+ ps->Asic96Reg.RD_MemAccessControl);
+
+ ps->AsicReg.RD_ModeControl = _ModeProgram;
+ IODataToRegister( ps, ps->RegModeControl, _ModeProgram );
+
+ ps->Asic96Reg.RD_MotorControl = ps->FullStep | _MotorDirForward;
+ IODataToRegister( ps, ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl);
+
+ memset( ps->pScanBuffer1, 0, (64 + 8 + ps->Offset70));
+ memcpy( ps->pScanBuffer1 + 64 + 8 + ps->Offset70, pData,
+ _BUF_SIZE_BASE_CONST * 2 );
+
+ IOMoveDataToScanner( ps, ps->pScanBuffer1,
+ _BUF_SIZE_BASE_CONST * 2 + 64 + 8 + ps->Offset70 );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+static void dacP96001WriteBackToColorShadingRam( pScanData ps )
+{
+ ps->Asic96Reg.RD_MemAccessControl = (_MemBankSize4k96001 | 0x3a);
+ dacP96001ToSetShadingAddress( ps, ps->pPrescan16 );
+
+ ps->Asic96Reg.RD_MemAccessControl = (_MemBankSize4k96001 | 0x3e);
+ dacP96001ToSetShadingAddress( ps, ps->pPrescan16 + _BUF_SIZE_BASE_CONST*2);
+
+ ps->Asic96Reg.RD_MemAccessControl = (_MemBankSize4k96001 | 0x3c);
+ dacP96001ToSetShadingAddress( ps, ps->pPrescan16 + _BUF_SIZE_BASE_CONST*4);
+}
+
+/**
+ */
+static void dacP96001ModifyShadingColor( pByte pData, Byte bMul )
+{
+ UShort w;
+ ULong dw;
+
+ for ( dw = 0; dw < _BUF_SIZE_BASE_CONST * 2; dw++ ) {
+
+ w = (UShort)(Byte)(~pData[dw]) * bMul / 100U;
+
+ if (w >= 255U)
+ pData[dw] = 0;
+ else
+ pData[dw] = (Byte)~w;
+ }
+}
+
+/**
+ */
+static Byte dacP96001FBKReading( pScanData ps, Byte bValue,
+ Byte bReg, pByte pbSave, Bool bFBKModify )
+{
+ TimerDef timer;
+ UShort w, wSum;
+ Byte addrSeq[8] = { 0x40, 0x20, 0x10, 0x08, 4, 2, 1, 0 };
+ Byte bTemp, bFBKTemp, bFBKIndex;
+
+ if( bFBKModify ) {
+ bFBKIndex = 3;
+ bFBKTemp = *pbSave;
+ } else {
+ bFBKTemp = 0x80;
+ bFBKIndex = 0;
+ }
+
+ while( _TRUE ) {
+
+ *pbSave = bFBKTemp;
+ IOCmdRegisterToScanner( ps, bReg, bFBKTemp );
+
+ /* SetColorRunTable (BYTE) */
+ memset( ps->a_nbNewAdrPointer, bValue, _SCANSTATE_BYTES );
+ MotorSetConstantMove( ps, 0 );
+
+ /* SetReadFBK (pScanData) */
+ ps->Asic96Reg.RD_MotorControl = ps->FullStep | _MotorDirForward;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE | ps->bLampOn;
+ ps->AsicReg.RD_ModelControl =
+ (_ModelMemSize32k96001 | _ModelDpi300 | _ModelWhiteIs0 );
+
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_Origin = 22;
+ ps->AsicReg.RD_Pixels = 1024;
+ IOPutOnAllRegisters( ps );
+
+ ps->Asic96Reg.RD_MotorControl =
+ (ps->FullStep | ps->MotorOn | _MotorDirForward);
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+
+ MiscStartTimer( &timer, _SECOND );
+ while((IODataRegisterFromScanner( ps, ps->RegFifoOffset) < 1) &&
+ !MiscCheckTimer( &timer ));
+
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+ IOReadScannerImageData( ps, ps->pScanBuffer1, 64 );
+
+ for( w = 26, wSum = 0; w < 42; w++)
+ wSum += ps->pScanBuffer1[w];
+
+ wSum >>= 4;
+ bTemp = addrSeq[bFBKIndex++];
+
+ if( bTemp ) {
+ if( wSum < 0xfe )
+ bFBKTemp += bTemp;
+ else
+ bFBKTemp -= bTemp;
+ } else {
+ return bFBKTemp;
+ }
+ }
+}
+
+/**
+ */
+static Bool dacP96001WaitForShading( pScanData ps )
+{
+ Bool bFBKModify;
+ Byte bRSave;
+ Byte bGSave;
+ Byte bBSave;
+ ULong dw;
+ pULong pdw;
+
+ DBG( DBG_LOW, "dacP96001WaitForShading()\n" );
+
+ ps->AsicReg.RD_ScanControl |= ps->bLampOn;
+ IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+
+ if ( ps->GotoShadingPosition( ps )) {
+
+ _DODELAY( 250 );
+
+ /* AdjustMostWideOffset70 (pScanData) -------------------------------*/
+ /* FillABitGray (pScanData)*/
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ ps->a_nbNewAdrPointer[8] =
+ ps->a_nbNewAdrPointer[24] = 0x30;
+
+ MotorSetConstantMove( ps, 32 );
+
+ /* SetMaxWideRegister (pScanData) */
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE | ps->bLampOn;
+
+ ps->Asic96Reg.RD_MotorControl =
+ (ps->MotorOn | ps->IgnorePF | _MotorDirForward);
+ ps->AsicReg.RD_ModelControl =
+ (_ModelMemSize32k96001 | _ModelDpi300 | _ModelWhiteIs0);
+
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_Origin = 64 + 8;
+ ps->AsicReg.RD_Pixels = 2700;
+ IOPutOnAllRegisters( ps );
+
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+
+ /* ReadMaxWideLine */
+ dacP96ReadDataWithinOneSecond( ps, 2700, 5 );
+
+ /* AdjustOffset70Proc (pScanData)------------------------------------*/
+ {
+ ULong dwSum, dw, dwLeft, dwCenter;
+
+ /* AverageWideBank (pScanData) */
+ for( dwSum = 0, dw = 0; dw < 2700; dw++ )
+ dwSum += (ULong)ps->pPrescan16[dw];
+
+ dwSum /= 2700UL;
+
+ if( dwSum <= 0x80 ) {
+
+ memcpy( ps->pScanBuffer1, ps->pPrescan16, 140 );
+ memcpy( ps->pScanBuffer1 + 140,
+ ps->pPrescan16 + _BUF_SIZE_BASE_CONST * 2, 140);
+
+ /* BlackOffsetCheck (pScanData) */
+ for( dw = dwLeft = 0; dw < 140; dw++ ) {
+ if( ps->pScanBuffer1[dw] >= 0xe0 )
+ dwLeft++;
+ else
+ break;
+ }
+
+ /* WhiteOffsetCheck (pScanData) */
+ for( dw = 140, dwCenter = 0; dw < 280; dw++ ) {
+ if( ps->pScanBuffer1[dw] < 0xe0 )
+ dwCenter++;
+ else
+ break;
+ }
+
+ if (dwLeft) {
+ ps->Offset70 = (dwLeft + dwCenter) / 2 + 14;
+ } else {
+ if (dwCenter == 140)
+ ps->Offset70 = 70;
+ else
+ ps->Offset70 = dwCenter / 2 + 2;
+ }
+ }
+ }
+
+ memset( ps->pPrescan16, 0, ps->BufferSizePerModel * 3 );
+ dacP96001WriteBackToColorShadingRam( ps );
+
+ /* SetFBK */
+ if((IODataRegisterFromScanner(ps, ps->RegReadIOBufBus) & 0x0f) == 0x0f)
+ bFBKModify = 0;
+ else
+ bFBKModify = 1;
+
+ dacP96001FBKReading(ps, 0x10, ps->RegRedDCAdjust, &bRSave,bFBKModify);
+ dacP96001FBKReading(ps, 0x30, ps->RegGreenDCAdjust,&bGSave,bFBKModify);
+ dacP96001FBKReading(ps, 0x20, ps->RegBlueDCAdjust, &bBSave,bFBKModify);
+
+ ps->OpenScanPath( ps );
+ IODataToRegister( ps, ps->RegRedDCAdjust, (Byte)(bRSave + 2));
+ IODataToRegister( ps, ps->RegGreenDCAdjust, (Byte)(bGSave + 2));
+ IODataToRegister( ps, ps->RegBlueDCAdjust, bBSave);
+ ps->CloseScanPath( ps );
+
+ /* Turn off and then turn on motor */
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ (Byte)(ps->Asic96Reg.RD_MotorControl & ~ps->MotorOn));
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+
+ /* FillABitColor (pScanData) */
+ pdw = (pULong)ps->a_nbNewAdrPointer;
+ for( dw = 0; dw < 4; dw++) {
+ *pdw++ = 0x40;
+ *pdw++ = 0x2030140;
+ }
+
+ IOSetToMotorRegister( ps );
+
+ /* SetShadingRegister (pScanData) */
+ ps->Asic96Reg.RD_MotorControl = ps->FullStep | _MotorDirForward;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_LineControl = ps->TimePerLine;
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE | ps->bLampOn;
+ ps->Asic96Reg.RD_MotorControl = ps->FullStep | _MotorDirForward;
+
+ ps->AsicReg.RD_ModelControl =
+ (_ModelMemSize32k96001 | _ModelDpi300 | _ModelWhiteIs0);
+ ps->AsicReg.RD_Dpi = 150;
+ ps->AsicReg.RD_Origin = (UShort)(64 + 8 + ps->Offset70);
+ ps->AsicReg.RD_Pixels = ps->BufferSizeBase;
+ IOPutOnAllRegisters( ps );
+
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ (Byte)(ps->MotorOn | ps->IgnorePF | _MotorDirForward));
+
+ dacP96ReadColorShadingLine( ps );
+
+ /* ModifyShadingColor (pScanData) */
+ dacP96001ModifyShadingColor( ps->pPrescan16, 103 );
+ dacP96001ModifyShadingColor( ps->pPrescan16 +
+ _BUF_SIZE_BASE_CONST * 2 * 2, 97);
+ dacP96001WriteBackToColorShadingRam( ps );
+ return _TRUE;
+ }
+ return _FALSE;
+}
+
+/**
+ */
+static void dacP98003GainOffsetToDAC( pScanData ps, Byte ch, Byte reg, Byte d )
+{
+ if( ps->Device.bDACType == _DA_SAMSUNG8531 ) {
+
+ IODataToRegister( ps, ps->RegADCAddress, 0 );
+ IODataToRegister( ps, ps->RegADCData, ch );
+ IODataToRegister( ps, ps->RegADCSerialOutStr, ch);
+ }
+
+ IODataToRegister( ps, ps->RegADCAddress, reg );
+ IODataToRegister( ps, ps->RegADCData, d );
+ IODataToRegister( ps, ps->RegADCSerialOutStr, d );
+}
+
+/**
+ */
+static void dacP98003AdjustRGBGain( pScanData ps )
+{
+ ULong i;
+ Byte bHi[3];
+
+ DBG( DBG_LOW, "dacP98003AdjustRGBGain()\n" );
+
+ ps->Shade.Gain.Colors.Red =
+ ps->Shade.Gain.Colors.Green =
+ ps->Shade.Gain.Colors.Blue = ps->Shade.bUniGain;
+
+ ps->Shade.Hilight.Colors.Red =
+ ps->Shade.Hilight.Colors.Green =
+ ps->Shade.Hilight.Colors.Blue = 0;
+
+ ps->Shade.bGainHigh = _GAIN_P98003_HIGH;
+ ps->Shade.bGainLow = _GAIN_P98003_LOW;
+
+ ps->Shade.fStop = _FALSE;
+
+ for( i = 10; i-- && !ps->Shade.fStop; ) {
+
+ ps->Shade.fStop = _TRUE;
+
+ /* SetRGBGainRegister () */
+ IODataToRegister( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+ IOSelectLampSource( ps );
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+
+ DacP98003FillToDAC( ps, &ps->Device.RegDACGain, &ps->Shade.Gain );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_Motor0Control = _FORWARD_MOTOR;
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ ps->AsicReg.RD_Origin = (UShort)ps->Device.DataOriginX >> 1;
+ else
+ ps->AsicReg.RD_Origin = (UShort)ps->Device.DataOriginX;
+
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_Pixels = 2560;
+
+ /* PauseColorMotorRunStates () */
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ ps->a_nbNewAdrPointer[1] = 0x77;
+
+ IOPutOnAllRegisters( ps );
+
+ _DODELAY( 70 );
+
+ /* read one shading line and work on it */
+ if( IOReadOneShadingLine( ps, (pUChar)ps->Bufs.b1.pShadingRam, 2560)) {
+
+ if ( ps->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+
+ bHi[1] = DacP98003SumGains(
+ (pUChar)ps->Bufs.b1.pShadingRam + 2560, 2560);
+ if( bHi[1] )
+ DacP98003AdjustGain( ps, _CHANNEL_GREEN, bHi[1] );
+ else {
+ ps->Shade.fStop = _FALSE;
+ }
+ } else {
+ bHi[0] = DacP98003SumGains((pUChar)ps->Bufs.b1.pShadingRam, 2560);
+ bHi[1] = DacP98003SumGains((pUChar)ps->Bufs.b1.pShadingRam + 2560, 2560);
+ bHi[2] = DacP98003SumGains((pUChar)ps->Bufs.b1.pShadingRam + 5120, 2560);
+
+ if (!bHi[0] || !bHi[1] || !bHi[2] ) {
+ ps->Shade.fStop = _FALSE;
+ } else {
+ DacP98003AdjustGain( ps, _CHANNEL_RED, bHi[0] );
+ DacP98003AdjustGain( ps, _CHANNEL_GREEN, bHi[1] );
+ DacP98003AdjustGain( ps, _CHANNEL_BLUE, bHi[2] );
+ }
+ }
+ } else
+ ps->Shade.fStop = _FALSE;
+ }
+
+#ifdef DEBUG
+ if( !ps->Shade.fStop )
+ DBG( DBG_LOW, "dacP98003AdjustRGBGain() - all loops done!!!\n" );
+#endif
+
+ DacP98003FillToDAC( ps, &ps->Device.RegDACGain, &ps->Shade.Gain );
+}
+
+/**
+ */
+static UShort dacP98003SumDarks( pScanData ps, pUShort data )
+{
+ UShort i, loop;
+
+ if( ps->Device.bCCDID == _CCD_3799 ) {
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ data += 0x18;
+ else
+ data += 0x30;
+ } else {
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ data += 0x18;
+ else
+ data += 0x20;
+ }
+
+ for( i = 0, loop = 16; loop--; data++ )
+ i += *data;
+ i >>= 4;
+
+ return i;
+}
+
+/**
+ */
+static void dacP98003AdjustShadingWaveform( pScanData ps )
+{
+ Byte b;
+ UShort count, wR, wG, wB, tmp;
+ DataType var;
+ DataPointer pvar, psum;
+ RBGPtrDef cp;
+ pRGBUShortDef pRGB, pwsum;
+
+ DBG( DBG_LOW, "dacP98003AdjustShadingWaveForm()\n" );
+
+ memset( &cp, 0, sizeof(RBGPtrDef));
+ memset( ps->Bufs.b2.pSumBuf, 0, (5400 * 3 * 2));
+
+ /* SetAdjustShadingRegister () */
+ IODataToRegister( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_LineControl = _LOBYTE(ps->Shade.wExposure);
+ ps->AsicReg.RD_ExtLineControl = _HIBYTE(ps->Shade.wExposure);
+ IODataToRegister( ps, ps->RegExtendedLineControl,
+ ps->AsicReg.RD_ExtLineControl );
+ IODataToRegister( ps, ps->RegLineControl, ps->AsicReg.RD_LineControl );
+
+ ps->AsicReg.RD_XStepTime = _LOBYTE(ps->Shade.wExposure);
+ ps->AsicReg.RD_ExtXStepTime = _HIBYTE(ps->Shade.wExposure);
+ IODataToRegister( ps, ps->RegExtendedXStep, ps->AsicReg.RD_ExtXStepTime );
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_Motor0Control = _FORWARD_MOTOR;
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut ) {
+
+ ps->AsicReg.RD_Dpi = 300;
+ ps->AsicReg.RD_Pixels = 2700;
+ ps->Shade.shadingBytes = 2700 * 2;
+ } else {
+ ps->AsicReg.RD_Dpi = 600;
+ ps->AsicReg.RD_Pixels = 5400;
+ ps->Shade.shadingBytes = 5400 * 2;
+ }
+ ps->AsicReg.RD_Origin = _SHADING_BEGINX;
+
+ for( pvar.pdw = (pULong)ps->a_nbNewAdrPointer,
+ var.dwValue = _SCANSTATE_BYTES >> 2; var.dwValue--; pvar.pdw++) {
+ *pvar.pdw = 0x00f00080;
+ }
+
+ ps->Scan.fRefreshState = _FALSE;
+ IOPutOnAllRegisters( ps );
+ _DODELAY( 55 );
+
+ /* SetupHilightShadow () */
+ if( ps->Shade.pHilight ) {
+
+ memset( ps->Shade.pHilight, 0,
+ ps->Shade.shadingBytes * ps->Shade.skipHilight * 3 );
+
+ memset((pUChar)ps->Shade.pHilight +
+ ps->Shade.shadingBytes * ps->Shade.skipHilight * 3, 0xff,
+ ps->Shade.shadingBytes * ps->Shade.skipShadow * 3 );
+ }
+
+
+ for( count = 32; count--;) {
+
+ IOReadOneShadingLine( ps,
+ ((pUChar)ps->Bufs.b1.pShadingRam)+_SHADING_BEGINX,
+ ps->Shade.shadingBytes );
+
+ /* SaveHilightShadow() */
+ if( ps->Shade.pHilight ) {
+
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ cp.red.usp = ps->Bufs.b1.pShadingRam + _SHADING_BEGINX;
+ cp.green.usp = cp.red.usp + ps->AsicReg.RD_Pixels;
+ cp.blue.usp = cp.green.usp + ps->AsicReg.RD_Pixels;
+ pvar.pusrgb = (pRGBUShortDef)ps->Shade.pHilight +
+ _SHADING_BEGINX;
+
+ for( var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ pRGB = pvar.pusrgb++;
+ wR = *cp.red.usp;
+ wG = *cp.green.usp;
+ wB = *cp.blue.usp;
+
+ for( b = ps->Shade.skipHilight; b--;
+ pRGB += ps->AsicReg.RD_Pixels) {
+
+ if( wR > pRGB->Red ) {
+ tmp = wR;
+ wR = pRGB->Red;
+ pRGB->Red = tmp;
+ }
+ if( wG > pRGB->Green ) {
+ tmp = wG;
+ wG = pRGB->Green;
+ pRGB->Green = tmp;
+ }
+ if( wB > pRGB->Blue ) {
+ tmp = wB;
+ wB = pRGB->Blue;
+ pRGB->Blue = tmp;
+ }
+ }
+
+ wR = *cp.red.usp++;
+ wG = *cp.green.usp++;
+ wB = *cp.blue.usp++;
+ for(b = ps->Shade.skipShadow; b--;
+ pRGB += ps->AsicReg.RD_Pixels) {
+
+ if (wR < pRGB->Red) {
+ tmp = wR;
+ wR = pRGB->Red;
+ pRGB->Red = tmp;
+ }
+ if (wG < pRGB->Green) {
+ tmp = wG;
+ wG = pRGB->Green;
+ pRGB->Green = tmp;
+ }
+ if (wB < pRGB->Blue) {
+ tmp = wB;
+ wB = pRGB->Blue;
+ pRGB->Blue = tmp;
+ }
+ }
+ }
+
+ } else {
+
+ cp.green.usp = ps->Bufs.b1.pShadingRam +
+ ps->AsicReg.RD_Pixels + _SHADING_BEGINX;
+ cp.blue.usp = (pUShort) ps->Shade.pHilight + _SHADING_BEGINX;
+
+ for (var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ cp.red.usp = cp.blue.usp++;
+ wG = *cp.green.usp;
+ for( b = ps->Shade.skipHilight; b--;
+ cp.red.usp += ps->AsicReg.RD_Pixels) {
+ if( wG > *cp.red.usp ) {
+ tmp = wG;
+ wG = *cp.red.usp;
+ *cp.red.usp = tmp;
+ }
+ }
+ wG = *cp.green.usp++;
+ for (b = ps->Shade.skipShadow; b--;
+ cp.red.usp += ps->AsicReg.RD_Pixels) {
+ if( wG < *cp.red.usp ) {
+ tmp = wG;
+ wG = *cp.red.usp;
+ *cp.red.usp = tmp;
+ }
+ }
+ }
+ }
+ }
+
+ /* AddToSumBuffer() */
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ cp.red.usp = ps->Bufs.b1.pShadingRam + _SHADING_BEGINX;
+ cp.green.usp = cp.red.usp + ps->AsicReg.RD_Pixels;
+ cp.blue.usp = cp.green.usp + ps->AsicReg.RD_Pixels;
+
+ pvar.pulrgb = (pRGBULongDef)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = (ULong)ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;
+ pvar.pulrgb++, cp.red.usp++, cp.green.usp++, cp.blue.usp++) {
+ pvar.pulrgb->Red += (ULong)*cp.red.usp;
+ pvar.pulrgb->Green += (ULong)*cp.green.usp;
+ pvar.pulrgb->Blue += (ULong)*cp.blue.usp;
+ }
+
+ } else {
+
+ cp.green.usp = ps->Bufs.b1.pShadingRam + ps->AsicReg.RD_Pixels +
+ _SHADING_BEGINX;
+ pvar.pdw = (pULong)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ for(var.dwValue = (ULong)ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--; pvar.pdw++, cp.green.usp++) {
+ *pvar.pdw += (ULong)*cp.green.usp;
+ }
+ }
+
+ if( IOReadFifoLength( ps ) < ps->AsicReg.RD_Pixels )
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+ }
+
+ /* AverageAfterSubHilightShadow() */
+ if( ps->Shade.pHilight ) {
+
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ psum.pulrgb = (pRGBULongDef)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pwsum = (pRGBUShortDef)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pvar.pusrgb = (pRGBUShortDef)ps->Shade.pHilight + _SHADING_BEGINX;
+
+ for( var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ pRGB = pvar.pusrgb++;
+
+ for( b = ps->Shade.skipHilight + ps->Shade.skipShadow;
+ b--; pRGB += ps->AsicReg.RD_Pixels ) {
+
+ psum.pulrgb->Red -= (ULong)pRGB->Red;
+ psum.pulrgb->Green -= (ULong)pRGB->Green;
+ psum.pulrgb->Blue -= (ULong)pRGB->Blue;
+ }
+
+ pwsum->Red = (UShort)(psum.pulrgb->Red / ps->Shade.dwDiv);
+ pwsum->Green = (UShort)(psum.pulrgb->Green / ps->Shade.dwDiv);
+ pwsum->Blue = (UShort)(psum.pulrgb->Blue / ps->Shade.dwDiv);
+ psum.pulrgb++;
+ pwsum++;
+ }
+ } else {
+ cp.green.ulp = (pULong)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ cp.blue.usp = (pUShort)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pvar.pw = (pUShort)ps->Shade.pHilight + _SHADING_BEGINX;
+
+ for( var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ cp.red.usp = pvar.pw++;
+
+ for( b = ps->Shade.skipHilight + ps->Shade.skipShadow;
+ b--; cp.red.usp += ps->AsicReg.RD_Pixels )
+ *cp.green.ulp -= *cp.red.usp;
+
+ *cp.blue.usp = (UShort)(*cp.green.ulp / ps->Shade.dwDiv);
+ cp.blue.usp++;
+ cp.green.ulp++;
+ }
+ }
+ } else {
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ psum.pulrgb = (pRGBULongDef)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pwsum = (pRGBUShortDef)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for (var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ pwsum->Red = (UShort)(psum.pulrgb->Red >> 5);
+ pwsum->Green = (UShort)(psum.pulrgb->Green >> 5);
+ pwsum->Blue = (UShort)(psum.pulrgb->Blue >> 5);
+ psum.pulrgb++;
+ pwsum++;
+ }
+ } else {
+ cp.green.ulp = (pULong)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+ cp.blue.usp = (pUShort)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for (var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ *cp.blue.usp = (UShort)(*cp.green.ulp >> 5);
+ cp.blue.usp++;
+ cp.green.ulp++;
+ }
+ }
+ }
+
+ /* Process negative & transparency here */
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA )
+ TPAP98003FindCenterPointer( ps );
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative )
+ TPAP98003Reshading( ps );
+
+ pRGB = (pRGBUShortDef)&ps->Shade.pCcdDac->GainResize;
+
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ pwsum = (pRGBUShortDef)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+
+ if ((short)(pwsum->Red -= ps->Shade.DarkOffset.Colors.Red) > 0) {
+ pwsum->Red = pwsum->Red * pRGB->Red / 100U;
+ if( pwsum->Red > 0xfff )
+ pwsum->Red = 0xfff;
+ } else
+ pwsum->Red = 0;
+
+ if((short)(pwsum->Green -= ps->Shade.DarkOffset.Colors.Green) > 0) {
+ pwsum->Green = pwsum->Green * pRGB->Green / 100U;
+ if( pwsum->Green > 0xfff )
+ pwsum->Green = 0xfff;
+ } else
+ pwsum->Green = 0;
+
+ if ((short)(pwsum->Blue -= ps->Shade.DarkOffset.Colors.Blue) > 0) {
+ pwsum->Blue = pwsum->Blue * pRGB->Blue / 100U;
+ if( pwsum->Blue > 0xfff )
+ pwsum->Blue = 0xfff;
+ } else
+ pwsum->Blue = 0;
+
+ wR = (UShort)(pwsum->Red >> 4);
+ pwsum->Red <<= 12;
+ pwsum->Red |= wR;
+ wR = (UShort)(pwsum->Green >> 4);
+ pwsum->Green <<= 12;
+ pwsum->Green |= wR;
+ wR = (UShort)(pwsum->Blue>> 4);
+ pwsum->Blue <<= 12;
+ pwsum->Blue |= wR;
+ pwsum++;
+ }
+ } else {
+
+ cp.green.usp = (pUShort)ps->Bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = ps->AsicReg.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+
+ if((short)(*cp.green.usp -= ps->Shade.DarkOffset.Colors.Green) > 0) {
+
+ *cp.green.usp = *cp.green.usp * pRGB->Green / 100U;
+ if( *cp.green.usp > 0xfff )
+ *cp.green.usp = 0xfff;
+ } else
+ *cp.green.usp = 0;
+
+ wR = (UShort)(*cp.green.usp >> 4);
+ *cp.green.usp <<= 12;
+ *cp.green.usp |= wR;
+
+ cp.green.usp++;
+ }
+ }
+
+ /* DownloadShadingAndSetDark() */
+ dacP98DownloadShadingTable( ps, ps->Bufs.b2.pSumBuf, (5400 * 3 * 2));
+}
+
+/**
+ */
+static void dacP98003AdjustDark( pScanData ps )
+{
+ ULong i;
+ UShort wDarks[3];
+
+ DBG( DBG_LOW, "dacP98003AdjustDark()\n" );
+
+ ps->Shade.DarkDAC.Colors = ps->Shade.pCcdDac->DarkDAC.Colors;
+ ps->Shade.fStop = _FALSE;
+
+ for( i = 16; i-- && !ps->Shade.fStop;) {
+
+ ps->Shade.fStop = _TRUE;
+
+ /* FillDarkToDAC() */
+ DacP98003FillToDAC( ps, &ps->Device.RegDACOffset, &ps->Shade.DarkDAC );
+
+ IODataToRegister( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_ScanControl = (_SCAN_12BITMODE + _SCAN_1ST_AVERAGE);
+ IOSelectLampSource( ps );
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_Motor0Control = _FORWARD_MOTOR;
+
+ ps->AsicReg.RD_Origin = _SHADING_BEGINX;
+ ps->AsicReg.RD_Pixels = 512;
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ ps->AsicReg.RD_Dpi = 300;
+ else
+ ps->AsicReg.RD_Dpi = 600;
+
+
+ /* PauseColorMotorRunStates () */
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ ps->a_nbNewAdrPointer[1] = 0x77;
+
+ IOPutOnAllRegisters( ps );
+ _DODELAY( 70 );
+
+ /* read one shading line and work on it */
+ if( IOReadOneShadingLine(ps, (pUChar)ps->Bufs.b1.pShadingRam, 512*2)) {
+
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ wDarks[0] = dacP98003SumDarks( ps, ps->Bufs.b1.pShadingRam );
+ wDarks[1] = dacP98003SumDarks( ps, ps->Bufs.b1.pShadingRam +
+ ps->AsicReg.RD_Pixels );
+ wDarks[2] = dacP98003SumDarks( ps, ps->Bufs.b1.pShadingRam +
+ ps->AsicReg.RD_Pixels * 2UL);
+
+ if( !wDarks[0] || !wDarks[1] || !wDarks[2] ) {
+ ps->Shade.fStop = _FALSE;
+ } else {
+ ps->Shade.DarkOffset.wColors[0] = wDarks[0];
+ ps->Shade.DarkOffset.wColors[1] = wDarks[1];
+ ps->Shade.DarkOffset.wColors[2] = wDarks[2];
+ (*(ps->Device).fnDACDark)( ps, ps->Shade.pCcdDac,
+ _CHANNEL_RED, wDarks[0] );
+ (*(ps->Device).fnDACDark)( ps, ps->Shade.pCcdDac,
+ _CHANNEL_GREEN, wDarks[1] );
+ (*(ps->Device).fnDACDark)( ps, ps->Shade.pCcdDac,
+ _CHANNEL_BLUE, wDarks[2] );
+ }
+ } else {
+ wDarks[1] = dacP98003SumDarks( ps, ps->Bufs.b1.pShadingRam +
+ ps->AsicReg.RD_Pixels );
+ if (!wDarks[1] )
+ ps->Shade.fStop = _FALSE;
+ else {
+ ps->Shade.DarkOffset.wColors[1] = wDarks[1];
+ (*(ps->Device).fnDACDark)( ps, ps->Shade.pCcdDac,
+ _CHANNEL_GREEN, wDarks[1] );
+ }
+ }
+ } else
+ ps->Shade.fStop = _FALSE;
+ }
+
+ /* CalculateDarkDependOnCCD() */
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+ (*(ps->Device).fnDarkOffset)( ps, ps->Shade.pCcdDac, _CHANNEL_RED );
+ (*(ps->Device).fnDarkOffset)( ps, ps->Shade.pCcdDac, _CHANNEL_GREEN );
+ (*(ps->Device).fnDarkOffset)( ps, ps->Shade.pCcdDac, _CHANNEL_BLUE );
+ } else
+ (*(ps->Device).fnDarkOffset)( ps, ps->Shade.pCcdDac, _CHANNEL_GREEN );
+}
+
+/**
+ */
+static Bool dacP98003WaitForShading( pScanData ps )
+{
+ ULong i, tmp;
+ Byte bScanControl;
+
+ DBG( DBG_LOW, "dacP98003WaitForShading()\n" );
+
+ /*
+ * before getting the shading data, (re)init the ASIC
+ */
+ ps->ReInitAsic( ps, _TRUE );
+
+ ps->Shade.DarkOffset.Colors.Red = 0;
+ ps->Shade.DarkOffset.Colors.Green = 0;
+ ps->Shade.DarkOffset.Colors.Blue = 0;
+
+ IORegisterToScanner( ps, ps->RegResetMTSC );
+
+ IODataToRegister( ps, ps->RegModelControl, ps->AsicReg.RD_ModelControl);
+ IODataToRegister( ps, ps->RegMotorDriverType,
+ ps->AsicReg.RD_MotorDriverType );
+ IODataToRegister( ps, ps->RegScanControl1,
+ (_SCANSTOPONBUFFULL| _MFRC_BY_XSTEP));
+ ps->GotoShadingPosition( ps );
+
+ bScanControl = ps->AsicReg.RD_ScanControl;
+
+ /* SetShadingMapForGainDark */
+ memset( ps->Bufs.b2.pSumBuf, 0xff, (5400 * 3 * 2));
+
+ /* DownloadShadingAndSetDark() */
+ dacP98DownloadShadingTable( ps, ps->Bufs.b2.pSumBuf, (5400 * 3 * 2));
+
+ for( i = 0, tmp = 0; i < 1024; tmp += 0x01010101, i += 4 ) {
+ ps->Bufs.b1.Buf.pdw[i] =
+ ps->Bufs.b1.Buf.pdw[i+1] =
+ ps->Bufs.b1.Buf.pdw[i+2] =
+ ps->Bufs.b1.Buf.pdw[i+3] = tmp;
+ }
+
+ memcpy( ps->Bufs.b1.pShadingMap + 4096, ps->Bufs.b1.pShadingMap, 4096 );
+ memcpy( ps->Bufs.b1.pShadingMap + 8192, ps->Bufs.b1.pShadingMap, 4096 );
+ dacP98DownloadMapTable( ps, ps->Bufs.b1.pShadingMap );
+
+ DBG( DBG_LOW, "wExposure = %u\n", ps->Shade.wExposure);
+ DBG( DBG_LOW, "wXStep = %u\n", ps->Shade.wXStep);
+
+ ps->AsicReg.RD_LineControl = (_LOBYTE(ps->Shade.wExposure));
+ ps->AsicReg.RD_ExtLineControl = (_HIBYTE(ps->Shade.wExposure));
+ IODataToRegister(ps, ps->RegExtendedLineControl,
+ ps->AsicReg.RD_ExtLineControl );
+ IODataToRegister(ps, ps->RegLineControl, ps->AsicReg.RD_LineControl );
+
+ dacP98003AdjustRGBGain( ps );
+ dacP98003AdjustDark( ps );
+ dacP98003AdjustShadingWaveform( ps );
+
+ ps->AsicReg.RD_ScanControl = bScanControl;
+
+ /* here we have to download the table in any case...*/
+ dacP98DownloadMapTable( ps, ps->a_bMapTable );
+
+ MotorP98003BackToHomeSensor( ps );
+
+ return _TRUE;
+}
+
+/************************ exported functions *********************************/
+
+/**
+ */
+_LOC int DacInitialize( pScanData ps )
+{
+ DBG( DBG_HIGH, "DacInitialize()\n" );
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ /*
+ * depending on the asic, we set some functions
+ */
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ ps->WaitForShading = dacP98003WaitForShading;
+
+ } else if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ ps->WaitForShading = dacP98WaitForShading;
+
+ } else if( _ASIC_IS_96003 == ps->sCaps.AsicID ) {
+
+ ps->WaitForShading = dacP96003WaitForShading;
+
+ } else if( _ASIC_IS_96001 == ps->sCaps.AsicID ) {
+
+ ps->WaitForShading = dacP96001WaitForShading;
+
+ } else {
+
+ DBG( DBG_HIGH , "NOT SUPPORTED ASIC !!!\n" );
+ return _E_NOSUPP;
+ }
+ return _OK;
+}
+
+/** Fill out the R/G/B GainOut value
+ */
+_LOC void DacP98FillGainOutDirectPort( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ IODataRegisterToDAC( ps, 0x28, ps->bRedGainIndex );
+ IODataRegisterToDAC( ps, 0x29, ps->bGreenGainIndex );
+ IODataRegisterToDAC( ps, 0x2a, ps->bBlueGainIndex );
+
+ ps->CloseScanPath( ps );
+}
+
+/**
+ */
+_LOC void DacP98FillShadingDarkToShadingRegister( pScanData ps )
+{
+ pUChar pValue;
+ Byte bReg;
+
+ DBG( DBG_LOW, "DacP98FillShadingDarkToShadingRegister()\n" );
+
+ ps->AsicReg.RD_RedDarkOff = ps->Shade.DarkOffset.Colors.Red;
+ ps->AsicReg.RD_GreenDarkOff = ps->Shade.DarkOffset.Colors.Green;
+ ps->AsicReg.RD_BlueDarkOff = ps->Shade.DarkOffset.Colors.Blue;
+
+ pValue = (pUChar)&ps->AsicReg.RD_RedDarkOff;
+ for (bReg = ps->RegRedChDarkOffsetLow; bReg <= ps->RegBlueChDarkOffsetHigh;
+ bReg++, pValue++) {
+
+ IODataToRegister( ps, bReg, *pValue );
+ }
+}
+
+/**
+ */
+_LOC void DacP98AdjustDark( pScanData ps )
+{
+ Byte bCorrectTimes; /* used to be a global var !*/
+
+ DBG( DBG_LOW, "DacP98AdjustDark()\n" );
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = ps->bsPreRedDAC;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = ps->bsPreGreenDAC;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = ps->bsPreBlueDAC;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative ) {
+ bCorrectTimes = 6;
+ } else {
+ bCorrectTimes = 5;
+ }
+
+/* CHANGE
+** original code seems to be buggy : for (bCorrectTimes ; bCorrectTimes--;) {
+*/
+ for (;bCorrectTimes ; bCorrectTimes-- ) {
+
+ ps->OpenScanPath( ps );
+ dacP98FillDarkDAC( ps );
+ dacP98SetReadFBKRegister( ps );
+ ps->CloseScanPath( ps );
+
+ IOPutOnAllRegisters( ps );
+
+ ps->PauseColorMotorRunStates( ps ); /* stop scan states */
+
+ IOReadOneShadingLine( ps, ps->pScanBuffer1, 512*2 );
+
+ dacP98FillChannelDarkLevelControl( ps );
+
+ if(dacP98CheckChannelDarkLevel( ps ))
+ break;
+ }
+
+ ps->Shade.DarkOffset.Colors.Red=
+ dacP98CalDarkOff( ps, ps->Shade.DarkOffset.Colors.Red,
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red,
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red );
+
+ ps->Shade.DarkOffset.Colors.Green =
+ dacP98CalDarkOff( ps, ps->Shade.DarkOffset.Colors.Green,
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green,
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green );
+
+ ps->Shade.DarkOffset.Colors.Blue =
+ dacP98CalDarkOff( ps, ps->Shade.DarkOffset.Colors.Blue,
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue,
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue );
+}
+
+/**
+ */
+_LOC void DacP96WriteBackToGammaShadingRAM( pScanData ps )
+{
+ /* ModifyGammaShadingOffset(ps) */
+ ps->OpenScanPath( ps);
+
+ IODataToRegister( ps, ps->RegRedChShadingOffset,
+ ps->Asic96Reg.u28.RD_RedChShadingOff );
+ IODataToRegister( ps, ps->RegGreenChShadingOffset,
+ (Byte)((ULong)ps->Asic96Reg.u29.RD_GreenChShadingOff * 96UL/100UL));
+
+ IODataToRegister( ps, ps->RegBlueChShadingOffset,
+ (Byte)((ULong)ps->Asic96Reg.RD_BlueChShadingOff * 91UL/100UL));
+
+ ps->CloseScanPath( ps );
+
+ dacP96WriteLinearGamma( ps, ps->pPrescan16, 256, ps->ShadingBankRed);
+ dacP96WriteLinearGamma( ps, ps->pPrescan16 + ps->ShadingBankSize,
+ 256, ps->ShadingBankGreen);
+ dacP96WriteLinearGamma( ps, ps->pPrescan16 + ps->ShadingBankSize * 2,
+ 256, ps->ShadingBankBlue);
+}
+
+/**
+ */
+_LOC void DacP98003FillToDAC( pScanData ps, pRGBByteDef regs, pColorByte data )
+{
+ if ( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ dacP98003GainOffsetToDAC( ps, _DAC_RED, regs->Red, data->Colors.Red );
+ dacP98003GainOffsetToDAC( ps, _DAC_GREENCOLOR,
+ regs->Green, data->Colors.Green );
+ dacP98003GainOffsetToDAC( ps, _DAC_BLUE,
+ regs->Blue, data->Colors.Blue );
+ } else {
+ dacP98003GainOffsetToDAC( ps, _DAC_GREENMONO, regs->Green,
+ data->Colors.Green );
+ }
+}
+
+/**
+ */
+_LOC void DacP98003AdjustGain( pScanData ps, ULong color, Byte hilight )
+{
+ if( hilight < ps->Shade.bGainLow ) {
+
+ if( ps->Shade.Hilight.bColors[color] < ps->Shade.bGainHigh ) {
+
+ ps->Shade.fStop = _FALSE;
+ ps->Shade.Hilight.bColors[color] = hilight;
+
+ if( hilight <= (Byte)(ps->Shade.bGainLow - hilight))
+ ps->Shade.Gain.bColors[color] += ps->Shade.bGainDouble;
+ else
+ ps->Shade.Gain.bColors[color]++;
+ }
+ } else {
+ if( hilight > ps->Shade.bGainHigh ) {
+ ps->Shade.fStop = _FALSE;
+ ps->Shade.Hilight.bColors[color] = hilight;
+ ps->Shade.Gain.bColors[color]--;
+ } else
+ ps->Shade.Hilight.bColors[color] = hilight;
+ }
+
+ if( ps->Shade.Gain.bColors[color] > ps->Shade.bMaxGain ) {
+ ps->Shade.Gain.bColors[color] = ps->Shade.bMaxGain;
+ }
+}
+
+/**
+ */
+_LOC Byte DacP98003SumGains( pUChar pb, ULong pixelsLine )
+{
+ Byte bHilight, tmp;
+ ULong dwPixels, dwAve;
+ UShort sum;
+
+ for( bHilight = 0, dwPixels = pixelsLine >> 4; dwPixels--; ) {
+
+ for( sum = 0, dwAve = 16; dwAve--; pb++)
+ sum += (UShort)*pb;
+
+ sum >>= 4;
+ tmp = (Byte)sum;
+
+ if( tmp > bHilight )
+ bHilight = tmp;
+ }
+ return bHilight;
+}
+
+/* END PLUSTEK-PP_DAC.C .....................................................*/
diff --git a/backend/plustek-pp_dbg.h b/backend/plustek-pp_dbg.h
new file mode 100644
index 0000000..c53d6e6
--- /dev/null
+++ b/backend/plustek-pp_dbg.h
@@ -0,0 +1,101 @@
+/** @file plustek-pp_dbg.c
+ * @brief definition of some debug macros
+ *
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+/* uncomment this to have an SW-simulatet 98001 device - don't expect to scan*/
+/* #define _ASIC_98001_SIM */
+
+/*
+ * the print macros
+ */
+#ifdef __KERNEL__
+# define _PRINT printk
+#endif
+
+/*
+ * some debug definitions
+ */
+#ifdef DEBUG
+# ifndef __KERNEL__
+# include <assert.h>
+# define _ASSERT(x) assert(x)
+# else
+# define _ASSERT(x)
+# endif
+
+# ifndef DBG
+# define DBG(level, msg, args...) if ((dbg_level) & (level)) { \
+ _PRINT(msg, ##args); \
+ }
+# endif
+#else
+# define _ASSERT(x)
+# ifndef DBG
+# define DBG(level, msg, args...)
+# endif
+#endif
+
+/* different debug level */
+#define DBG_LOW 0x01
+#define DBG_MEDIUM 0x02
+#define DBG_HIGH 0x04
+#define DBG_HELPERS 0x08
+#define DBG_TIMEOUT 0x10
+#define DBG_SCAN 0x20
+#define DBG_IO 0x40
+#define DBG_IOF 0x80
+#define DBG_ALL 0xFF
+
+/*
+ * standard debug level
+ */
+#ifdef DEBUG
+static int dbg_level=(DBG_ALL & ~(DBG_IO | DBG_IOF));
+#endif
+
+#endif /* guard __DEBUG_H__ */
+
+/* END PLUSTEK-PP_DBG.H .....................................................*/
diff --git a/backend/plustek-pp_detect.c b/backend/plustek-pp_detect.c
new file mode 100644
index 0000000..51fad69
--- /dev/null
+++ b/backend/plustek-pp_detect.c
@@ -0,0 +1,526 @@
+/* @file plustek-pp_detect.c
+ * @brief automatic scanner detection
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - no changes
+ * - 0.33 - added portmode check
+ * - 0.34 - no changes
+ * - 0.35 - no changes
+ * - 0.36 - added some debug messages
+ * - replace the old _OUTB/_INB macros
+ * - 0.37 - cosmetic changes
+ * - added speed-test for the parallel-port
+ * - 0.38 - added P12 stuff - replaced detectP9636 by detectAsic9800x
+ * - added detectResetPort() function
+ * - 0.39 - fixed problem in ASIC9800x detection
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - cleanup
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/************************** local definitions ********************************/
+
+/*************************** local functions *********************************/
+
+/** as the name says...
+ */
+static void detectResetPort( pScanData ps )
+{
+ UChar control;
+
+ DBG( DBG_HIGH, "ResetPort()\n" );
+
+ control = _INB_CTRL( ps );
+ _DO_UDELAY( 2 );
+
+ _OUTB_CTRL( ps, _CTRL_RESERVED ); /* reset, 0xc0 */
+ _DO_UDELAY( 2 );
+
+ _OUTB_CTRL( ps, control ); /* and restore... */
+ _DO_UDELAY( 2 );
+}
+
+/** Check: will the status port changed between printer/scanner path changed?
+ * Write out data and read in to compare
+ */
+static int detectScannerConnection( pScanData ps )
+{
+ UChar data, control, status;
+ int retval = _E_NO_CONN;
+
+#ifdef __KERNEL__
+ DBG( DBG_LOW, "Dataport = 0x%04x\n", ps->IO.pbSppDataPort );
+ DBG( DBG_LOW, "Ctrlport = 0x%04x\n", ps->IO.pbControlPort );
+#endif
+
+ detectResetPort( ps );
+
+ /*
+ * as we're called during InitPorts, we can be sure
+ * to operate in EPP-mode (hopefuly ;-)
+ */
+ control = _INB_CTRL( ps );
+
+ /*
+ * go ahead and do some checks
+ */
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ _DO_UDELAY( 5 );
+
+ _OUTB_DATA( ps, 0x55 );
+ _DO_UDELAY( 5 );
+
+ data = _INB_DATA( ps );
+
+ if (0x55 == data) {
+
+ DBG( DBG_HIGH, "Test 0x55\n" );
+
+ _OUTB_DATA( ps, 0xAA );
+ _DO_UDELAY( 5 );
+
+ data = _INB_DATA( ps );
+
+ if (0xAA == data) {
+
+ DBG( DBG_HIGH, "Test 0xAA\n" );
+
+ _OUTB_DATA( ps, 0x0 );
+ _DO_UDELAY( 5 );
+
+ data = _INB_STATUS( ps );
+
+ ps->OpenScanPath( ps );
+
+ _OUTB_DATA( ps, 0x0 );
+ _DO_UDELAY( 5 );
+
+ status = _INB_STATUS( ps );
+
+ ps->CloseScanPath( ps );
+
+ /*
+ * so we're done 'til now...
+ */
+ DBG( DBG_HIGH, "Compare data=0x%x and status=0x%x, port=0x%x\n",
+ data, status, ps->IO.portBase );
+
+ if( data != status ) {
+
+ _ASSERT( ps->ReadWriteTest );
+
+ /*
+ * here we try to detect the operation speed of our parallel
+ * port if we have tested all the stuff and had no success,
+ * retval will contain the error-code
+ */
+ for( ps->IO.delay = 0; ps->IO.delay < 5; ps->IO.delay++ ) {
+
+ retval = ps->ReadWriteTest( ps );
+
+ /* break on OK or when the ASIC detection fails */
+ if((_OK == retval) || (_E_NO_ASIC == retval))
+ break;
+ }
+ }
+ }
+ }
+
+ /* work on the result */
+ if ( _OK == retval ) {
+#ifdef __KERNEL__
+ ps->sCaps.wIOBase = ps->IO.pbSppDataPort;
+#else
+ ps->sCaps.wIOBase = ps->pardev;
+#endif
+ ps->PutToIdleMode( ps );
+
+ } else {
+ ps->sCaps.wIOBase = _NO_BASE;
+ }
+
+ /*
+ * restore control port value
+ */
+ _OUTB_CTRL( ps, control );
+ _DO_UDELAY( 5 );
+
+ DBG( DBG_HIGH, "detectScannerConnection() returns %i.\n", retval );
+
+ return retval;
+}
+
+/** we need some memory...
+ */
+static int detectSetupBuffers( pScanData ps )
+{
+ DBG( DBG_LOW, "*** setupBuffers ***\n" );
+
+ /* bad news ?
+ */
+ if ( 0 == ps->TotalBufferRequire ) {
+
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv: asic 0x%x probably not supported\n", ps->sCaps.AsicID);
+
+ return _E_ALLOC; /* Out of memory */
+
+ } else {
+
+ /*
+ * allocate and clear
+ */
+ DBG(DBG_LOW,"Driverbuf(%u bytes) needed !\n", ps->TotalBufferRequire);
+ ps->driverbuf = (pUChar)_VMALLOC(ps->TotalBufferRequire);
+
+ if ( NULL == ps->driverbuf ) {
+
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv: Not enough kernel memory %d\n",
+ ps->TotalBufferRequire);
+ return _E_ALLOC; /* Out of memory */
+ }
+
+ memset( ps->driverbuf, 0, ps->TotalBufferRequire );
+ }
+
+ ps->pPrescan16 = ps->driverbuf;
+ ps->pPrescan8 = ps->pPrescan16 + ps->BufferFor1stColor;
+ ps->pScanBuffer1 = ps->pPrescan8 + ps->BufferFor2ndColor;
+
+/* CHECK: Should we adjust that !!!
+*/
+ ps->pEndBufR = ps->pPrescan8;
+ ps->pEndBufG = ps->pScanBuffer1;
+ ps->pColorRunTable = ps->pScanBuffer1 + ps->BufferForDataRead1;
+
+ DBG( DBG_LOW, "pColorRunTab = 0x%0lx - 0x%0lx\n",
+ (unsigned long)ps->pColorRunTable,
+ (unsigned long)((pUChar)ps->driverbuf + ps->TotalBufferRequire));
+
+ if ( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ DBG( DBG_LOW, "Adjust for 98001 ASIC\n" );
+
+ ps->pScanBuffer2 = ps->pPrescan16;
+ ps->pScanBuffer1 = ps->pScanBuffer2 + _LINE_BUFSIZE1;
+ ps->pColorRunTable = ps->pScanBuffer1 + _LINE_BUFSIZE * 2UL;
+ ps->pProcessingBuf = ps->pColorRunTable + ps->BufferForColorRunTable;
+ DBG( DBG_LOW, "sb2 = 0x%lx, sb1 = 0x%lx, Color = 0x%lx\n",
+ (unsigned long)ps->pScanBuffer2,
+ (unsigned long)ps->pScanBuffer1,
+ (unsigned long)ps->pColorRunTable );
+ DBG( DBG_LOW, "Pro = 0x%lx, size = %d\n",
+ (unsigned long)ps->pProcessingBuf, ps->TotalBufferRequire );
+
+ ps->dwShadow = (_DEF_BRIGHTEST_SKIP + _DEF_DARKEST_SKIP) * 5400UL * 2UL * 3UL;
+
+ ps->Shade.pHilight = _VMALLOC( ps->dwShadow );
+
+ if ( NULL != ps->Shade.pHilight ) {
+
+ memset( ps->Shade.pHilight, 0, ps->dwShadow );
+
+ ps->dwHilight = _DEF_BRIGHTEST_SKIP * 5400UL * 3UL;
+ ps->dwShadow = _DEF_DARKEST_SKIP * 5400UL * 3UL;
+ ps->pwShadow = (pUShort)ps->Shade.pHilight + ps->dwHilight;
+ ps->Shade.dwDiv = 32UL - _DEF_BRIGHTEST_SKIP - _DEF_DARKEST_SKIP;
+
+ ps->dwHilightCh = ps->dwHilight / 3UL;
+ ps->dwShadowCh = ps->dwShadow / 3UL;
+ }
+ } else if ( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ DBG( DBG_LOW, "Adjust for 98003 ASIC\n" );
+
+ ps->Bufs.b1.pReadBuf = ps->driverbuf;
+ ps->Bufs.b2.pSumBuf = ps->Bufs.b1.pReadBuf + _SizeDataBuf;
+ ps->Bufs.TpaBuf.pb = &((pUChar)ps->Bufs.b2.pSumBuf)[_SizeShadingSumBuf];
+
+/* CHECK: We might should play around with these values... */
+ ps->Shade.skipHilight = _DEF_BRIGHTEST_SKIP;
+ ps->Shade.skipShadow = _DEF_DARKEST_SKIP;
+
+ if( ps->Shade.skipHilight && ps->Shade.skipShadow ) {
+
+ ULong skipSize;
+
+ skipSize = (ULong)((ps->Shade.skipHilight + ps->Shade.skipShadow)
+ * _SizeDataBuf * 3);
+
+ ps->Shade.pHilight = _VMALLOC( skipSize );
+
+ if( NULL != ps->Shade.pHilight ) {
+ ps->Shade.dwDiv = (ULong)(32UL - ps->Shade.skipHilight -
+ ps->Shade.skipShadow);
+ }
+ } else
+ ps->Shade.pHilight = NULL;
+ }
+
+ return _OK;
+}
+
+/** model 48xx detection or any other model using the 96001/3 ASIC
+ */
+static int detectP48xx( pScanData ps )
+{
+ int result;
+
+ DBG( DBG_LOW, "************ DETECTP48xx ************\n" );
+
+ /* increase the delay-time */
+ ps->IO.delay = 4;
+
+ ModelSet4800( ps );
+
+ result = P48xxInitAsic( ps );
+ if( _OK != result )
+ return result;
+
+ return detectScannerConnection( ps );
+}
+
+/** ASIC 98003 model detection
+ */
+static int detectAsic98003( pScanData ps )
+{
+ int result;
+
+ DBG( DBG_LOW, "************* ASIC98003 *************\n" );
+
+ /* increase the delay-time */
+ ps->IO.delay = 4;
+
+ ModelSetP12( ps );
+
+ result = P12InitAsic( ps );
+ if( _OK != result )
+ return result;
+
+ IOSoftwareReset( ps );
+
+ return detectScannerConnection( ps );
+}
+
+/** ASIC 98001 model detection
+ */
+static int detectAsic98001( pScanData ps )
+{
+ int result;
+
+ DBG( DBG_LOW, "************* ASIC98001 *************\n" );
+
+ /* increase the delay-time */
+ ps->IO.delay = 4;
+
+ ModelSet9636( ps );
+
+ result = P9636InitAsic( ps );
+#ifndef _ASIC_98001_SIM
+ if( _OK != result )
+ return result;
+
+ return detectScannerConnection( ps );
+#else
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "!!!! WARNING, have a look at function detectAsic98001() !!!!\n" );
+ ps->sCaps.AsicID = _ASIC_IS_98001;
+ ps->sCaps.wIOBase = ps->IO.pbSppDataPort;
+ return _OK;
+#endif
+}
+
+/************************ exported functions *********************************/
+
+/** here we try to find the scanner, depending on the mode
+ */
+_LOC int DetectScanner( pScanData ps, int mode )
+{
+ Byte asic;
+ int result = _E_INTERNAL;
+
+ /*
+ * before doing anything else, check the port-mode
+ */
+ if((ps->IO.portMode != _PORT_EPP) && (ps->IO.portMode != _PORT_SPP) &&
+ (ps->IO.portMode != _PORT_BIDI)) {
+
+ DBG( DBG_LOW, "!!! Portmode (%u)not supported !!!\n", ps->IO.portMode );
+ return _E_INTERNAL;
+ }
+
+ /* autodetection ?
+ */
+ if( 0 == mode ) {
+
+ DBG( DBG_HIGH, "Starting Scanner-Autodetection\n" );
+
+ /* try to find a 48xx Scanner
+ * (or even a scanner based on the 96001/3) ASIC
+ */
+ result = detectP48xx( ps );
+
+ if( _OK != result ) {
+
+ DBG( DBG_LOW, "************* ASIC9800x *************\n" );
+
+ /* get the ASIC ID by using the OpenScanPath stuff from Asic9600x based
+ * models - only difference: change the ReadHigh/ReadLow signals before
+ */
+ ps->CtrlReadHighNibble = _CTRL_GENSIGNAL+_CTRL_AUTOLF+_CTRL_STROBE;
+ ps->CtrlReadLowNibble = _CTRL_GENSIGNAL+_CTRL_AUTOLF;
+
+ /* read Register 0x18 (AsicID Register) of Asic9800x based devices */
+#ifdef _ASIC_98001_SIM
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "!!!! WARNING, SW-Emulation active !!!!\n" );
+ asic = _ASIC_IS_98001;
+#else
+ detectResetPort( ps );
+
+ /* do some presettings to make IODataRegisterFromScanner() work */
+ ps->RegAsicID = 0x18;
+ ps->IO.useEPPCmdMode = _FALSE;
+ ps->sCaps.AsicID = _ASIC_IS_98001;
+ IOInitialize( ps );
+
+ asic = IODataRegisterFromScanner( ps, ps->RegAsicID );
+
+ DBG( DBG_HIGH, "ASIC = 0x%02X\n", asic );
+#endif
+
+ /* depending on what we have found, perform some extra tests */
+ switch( asic ) {
+
+ case _ASIC_IS_98001:
+ result = detectAsic98001( ps );
+ break;
+
+ case _ASIC_IS_98003:
+
+ /* as the reading of the ASIC ID causes trouble,
+ * we reset the device
+ */
+ ps->IO.useEPPCmdMode = _FALSE;
+ ps->sCaps.AsicID = _ASIC_IS_98003;
+ IOInitialize( ps );
+ IOSoftwareReset( ps );
+
+ result = detectAsic98003( ps );
+ break;
+
+ default:
+ DBG( DBG_HIGH, "Unknown ASIC-ID\n" );
+ result = _E_NO_DEV;
+ break;
+ }
+ }
+
+ } else {
+
+ /* this will be called each time before operating on a previously
+ * detected device, to make sure we are still operating on the same one
+ */
+ if( _ASIC_IS_98001 == mode ) {
+
+ DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 98001)\n" );
+ result = detectAsic98001( ps );
+
+ } else if( _ASIC_IS_98003 == mode ) {
+
+ DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 98003)\n" );
+ result = detectAsic98003( ps );
+
+ } else {
+
+ DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 96001/3)\n" );
+ result = detectP48xx( ps );
+ }
+ }
+
+ if( _OK == result ) {
+
+ _ASSERT( ps->SetupScannerVariables );
+ ps->SetupScannerVariables( ps );
+
+ detectSetupBuffers( ps );
+
+ } else {
+/* CHECK - we should not need that anymore - paranoia code ??!!!!
+*/
+ ps->sCaps.wIOBase = _NO_BASE;
+ }
+
+ DBG( DBG_LOW, "*** DETECTION DONE, result: %i ***\n", result );
+ return result;
+}
+
+/* END PLUSTEK-PP_DETECT.C ..................................................*/
diff --git a/backend/plustek-pp_genericio.c b/backend/plustek-pp_genericio.c
new file mode 100644
index 0000000..1a1ebf4
--- /dev/null
+++ b/backend/plustek-pp_genericio.c
@@ -0,0 +1,1463 @@
+/* @file plustek-pp_genericio.c
+ * @brief all i/o functions
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - moved ioP96ReadScannerImageData and ioP98ReadScannerImageData
+ * into this file
+ * - added SPP-read functions
+ * - 0.32 - changes in function ioControlLampOnOff()
+ * - made IOReadingImage a local function -> ioP98ReadingImage()
+ * - rewritten function ioP96ReadScannerImageData()
+ * - moved function IOSetStartStopRegister to p9636.c
+ * - 0.33 - added debug messages to IOPutOnAllRegisters
+ * - fixed a bug in ioP96InitialSetCurrentSpeed
+ * - 0.34 - no changes
+ * - 0.35 - no changes
+ * - 0.36 - removed some warning conditions
+ * - 0.37 - moved functions IOSPPWrite(), IODataToScanner(), IODataToRegister(),
+ * IODataFromRegister() to io.c
+ * - moved the data read functions to io.c
+ * - renamed IOInitialize to IOFuncInitialize
+ * - 0.38 - moved some functions to io.c
+ * - added P12 stuff
+ * - 0.39 - no changes
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - fixed a problem in ioP96InitialSetCurrentSpeed(), for COLOR_BW
+ * at least, used the setting for A3I
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** local vars **************************************/
+
+/* WORK COMMENT THIS */
+typedef void (*pFnSpeed_Set)(pScanData);
+
+static ModeTypeVar a_FilmSettings[18] = {
+ /* SppNegFilmPos */
+ {0xa0, 1782, 96, _QuarterStep, 0, 0},
+ {0xb7, 1782, 96, _QuarterStep, 0, 0},
+ {0xb7, 1782, 96, _QuarterStep, 0, 0},
+ /* BppNegFilmPos */
+ {0xa9, 1782, 96, _QuarterStep, 0, 0},
+ {0xbf, 1782, 96, _QuarterStep, 0, 0},
+ {0xbf, 1782, 96, _QuarterStep, 0, 0},
+ /* EppNegFilmPos */
+ {0x95, 1782, 96, _QuarterStep, 0, 0},
+ {0xa6, 1782, 96, _QuarterStep, 0, 0},
+ {0xa6, 1782, 96, _QuarterStep, 0, 0},
+ /* SppPosFilmPos */
+ {0x50, 1782, 96, _QuarterStep, 0, 0},
+ {0x67, 1782, 96, _QuarterStep, 0, 0},
+ {0x67, 1782, 96, _QuarterStep, 0, 0},
+ /* BppPosFilmPos */
+ {0x59, 1782, 96, _QuarterStep, 0, 0},
+ {0x6f, 1782, 96, _QuarterStep, 0, 0},
+ {0x6f, 1782, 96, _QuarterStep, 0, 0},
+ /* EppPosFilmPos */
+ {0x45, 1782, 96, _QuarterStep, 0, 0},
+ {0x56, 1782, 96, _QuarterStep, 0, 0},
+ {0x56, 1782, 96, _QuarterStep, 0, 0}
+};
+
+static ModeTypeVar a_BwSettings[12] = {
+ {_Home_BE75, 890, 96, _HalfStep, 2, 1},
+ {_Home_BE150, 1780, 88, _QuarterStep, 2, 0},
+ {_Home_BE300, 3542, 96, _QuarterStep, 2, 0},
+ {_Home_BE600, 7070, 96, _QuarterStep, 2, 0},
+ {_Home_BB75, 890, 96, _HalfStep, 2, 1},
+ {_Home_BB150, 1780, 88, _QuarterStep, 2, 0},
+ {_Home_BB300, 3542, 96, _QuarterStep, 2, 0},
+ {_Home_BB600, 7070, 96, _QuarterStep, 2, 0},
+ {_Home_BS75, 890, 96, _HalfStep, 2, 1},
+ {_Home_BS150, 1780, 88, _QuarterStep, 2, 0},
+ {_Home_BS300, 3542, 96, _QuarterStep, 2, 0},
+ {_Home_BS600, 7070, 96, _QuarterStep, 2, 0}
+};
+
+static ModeTypeVar a_GraySettings[12] = {
+ {_Home_GE75, 890, 96, _HalfStep, 2, 0},
+ {_Home_GE150, 1780, 88, _QuarterStep, 2, 0},
+ {_Home_GE300, 3542, 88, _QuarterStep, 2, 0},
+ {_Home_GE600, 7070, 88, _QuarterStep, 2, 0},
+ {_Home_GB75, 890, 96, _HalfStep, 2, 0},
+ {_Home_GB150, 1780, 88, _QuarterStep, 2, 0},
+ {_Home_GB300, 3542, 88, _QuarterStep, 2, 0},
+ {_Home_GB600, 7070, 88, _QuarterStep, 2, 0},
+ {_Home_GS75, 890, 96, _HalfStep, 2, 0},
+ {_Home_GS150, 1782, 96, _QuarterStep, 2, 0},
+ {_Home_GS300, 3549, 88, _QuarterStep, 2, 0},
+ {_Home_GS600, 7070, 88, _QuarterStep, 2, 0}
+};
+
+static ModeTypeVar a_ColorSettings[15] = {
+ {_Home_CE50, 720, 60, _HalfStep, 1, 1},
+ {_Home_CE100, 1782, 48, _QuarterStep, 1, 0},
+ {_Home_CE150, 1782, 88, _QuarterStep, 0, 0},
+ {_Home_CE300, 3549, 96, _QuarterStep, 0, 0},
+ {_Home_CE600, 7082, 96, _QuarterStep, 0, 0},
+ {_Home_CB50, 720, 120, _QuarterStep, 0, 1},
+ {_Home_CB100, 1782, 96, _QuarterStep, 0, 0},
+ {_Home_CB150, 1782, 96, _QuarterStep, 0, 0},
+ {_Home_CB300, 3549, 96, _QuarterStep, 0, 0},
+ {_Home_CB600, 7082, 96, _QuarterStep, 0, 0},
+ {_Home_CS50, 720, 120, _QuarterStep, 0, 1},
+ {_Home_CS100, 1782, 96, _QuarterStep, 0, 0},
+ {_Home_CS150, 1782, 96, _QuarterStep, 0, 0},
+ {_Home_CS300, 3549, 96, _QuarterStep, 0, 0},
+ {_Home_CS600, 7082, 96, _QuarterStep, 0, 0}
+};
+
+static DiffModeVar a_tabDiffParam[] ={
+ /* BPP/EPP B/W */
+ {0, 1, 11}, /* Bpp/Epp B/W, Dpi <= 150 ;(0) */
+ {0, 1, 24}, /* Bpp/Epp B/W, Dpi <= 300 ;(1) */
+ {0, 1, 48}, /* Bpp/Epp B/W, Dpi > 300 ;(2) */
+ /* SPP B/W */
+ {0, 1, 11}, /* Spp B/W, Dpi <= 150 ;(3) */
+ {0, 1, 24}, /* Spp B/W, Dpi <= 300 ;(4) */
+ {0, 1, 48}, /* Spp B/W, Dpi > 300 ;(5) */
+ /* EPP Gray */
+ /* The difference for this DPI:
+ * if pixels <= | 3000 | Others
+ * --------------------+------+-------------
+ * VarFullStateSpeed | 0 | 1
+ * VarCurrentSpeed | 1 | 2
+ * VarStepSpeed | 44 | 88
+ */
+ {0, 1, 12}, /* Epp Gray, Dpi <= 150 ;(6) */
+ {1, 2, 80}, /* Epp Gray, Dpi <= 300 ;(7) */
+ {0, 1, 80}, /* Epp Gray, Dpi > 300, Px <= 3000 ;(8) */
+ {0, 1, 80}, /* Epp Gray, Dpi > 300, Px > 3000 ;(9) */
+
+ /* BPP Gray */
+ {0, 1, 11}, /* Bpp Gray, Dpi <= 150 ; 10 */
+ /* The difference for this DPI:
+ * if pixels <= | 1600 | Others
+ * --------------------+------+-------------
+ * VarFullStateSpeed | 0 | 1
+ * VarCurrentSpeed | 1 | 2
+ * VarStepSpeed | 24 | 48
+ */
+ {0, 1, 24}, /* Bpp Gray, Dpi <= 300, Px <= 1600 ; 11 */
+ {1, 2, 48}, /* Bpp Gray, Dpi <= 300, Px > 1600 ; 12 */
+ /* The difference for this DPI:
+ * if pixels <= | 1600 | 3200 | Others
+ * --------------------+-----+-------+----------------------
+ * VarFullStateSpeed | 0 | 1 | 2
+ * VarCurrentSpeed | 1 | 2 | 4
+ * VarStepSpeed | 44 | 88 | 88
+ */
+ {0, 1, 44}, /* Bpp Gray, Dpi > 300, Px <= 1600 ; 13 */
+ {1, 2, 88}, /* Bpp Gray, Dpi > 300, Px <= 3200 ; 14 */
+ {2, 4, 88}, /* Bpp Gray, Dpi > 300, Px > 3200 ; 15 */
+ /* SPP Gray */
+ /* The difference for this DPI:
+ * if pixels <= | 800 | Others
+ * --------------------+-----+-------------
+ * VarFullStateSpeed | 0 | 1
+ * VarCurrentSpeed | 1 | 2
+ * VarStepSpeed | 12 | 24
+ */
+ {0, 1, 12}, /* Spp Gray, Dpi <= 150, Px <= 800 ; 16 */
+ {1, 2, 24}, /* Spp Gray, Dpi <= 150, Px > 800 ; 17 */
+ /* The difference for this DPI:
+ * if pixels <= | 800 | 1600 | Others
+ * --------------------+-----+-------+----------------------
+ * VarFullStateSpeed | 0 | 1 | 1
+ * VarCurrentSpeed | 1 | 2 | 4
+ * VarStepSpeed | 22 | 88 | 88
+ */
+ {0, 1, 22}, /* Spp Gray, Dpi <= 300, Px <= 800 ; 18 */
+ {1, 2, 88}, /* Spp Gray, Dpi <= 300, Px <= 1600 ; 19 */
+ {1, 4, 88}, /* Spp Gray, Dpi <= 300, Px > 1600 ; 20 */
+ /* The difference for this DPI:
+ * if pixels <= | 800 | 1600 | 3200 | Others
+ * --------------------+-----+------+------+---------------
+ * VarFullStateSpeed | 0 | 1 | 2 | 3
+ * VarCurrentSpeed | 1 | 2 | 4 | 6
+ * VarStepSpeed | 44 | 88 | 88 | 88
+ */
+ {0, 1, 44}, /* Spp Gray, Dpi > 300, Px <= 800 ; 21 */
+ {1, 2, 88}, /* Spp Gray, Dpi > 300, Px <= 1600 ; 22 */
+ {2, 4, 88}, /* Spp Gray, Dpi > 300, Px <= 3200 ; 23 */
+ {3, 6, 88}, /* Spp Gray, Dpi > 300, Px > 3200 ; 24 */
+ /* EPP Color */
+ {0, 1, 6}, /* Epp Color, Dpi <= 60/100 ; 25 */
+ {0, 1, 11}, /* Epp Color, Dpi <= 150 ; 26 */
+ /* The difference for this DPI:
+ * if pixels <= | 1200 | Others
+ * --------------------+------+-------------
+ * VarFullStateSpeed | 0 | 1
+ * VarCurrentSpeed | 1 | 2
+ * VarStepSpeed | 24 | 48
+ */
+ {0, 1, 24}, /* Epp Color, Dpi <= 300, Px <= 1400 ; 27 */
+ {1, 2, 48}, /* Epp Color, Dpi <= 300, Px > 1400 ; 28 */
+ /* The difference for this DPI:
+ * if pixels <= | 1400 | 2800 | 4000 | Others
+ * --------------------+------+------+------+---------------
+ * VarFullStateSpeed | 0 | 1 | 2 | 3
+ * VarCurrentSpeed | 1 | 2 | 4 | 6
+ * VarStepSpeed | 48 | 96 | 88 | 88
+ * VarExposureTime | 96 | 96 | 88 | 88
+ */
+ {0, 1, 48}, /* Epp Color, Dpi > 300, Px <= 1400 ; 29 */
+ {1, 2, 96}, /* Epp Color, Dpi > 300, Px <= 2800 ; 30 */
+ {2, 4, 88}, /* Epp Color, Dpi > 300, Px <= 4000 ; 31 */
+ {4, 8, 88}, /* Epp Color, Dpi > 300, Px > 4000 ; 32 */
+ /* BPP Color */
+ {0, 1, 6}, /* Bpp/Spp Color, Dpi <= 60 ; 33 */
+ {0, 1, 12}, /* Bpp/Spp Color, Dpi <= 100 ; 34 */
+ /* if pixels <= | 800 | Others
+ * --------------------+-----+-------------
+ * VarFullStateSpeed | 0 | 1
+ * VarCurrentSpeed | 1 | 2
+ * VarStepSpeed | 12 | 24
+ */
+ {0, 1, 12}, /* Bpp/Spp Color, Dpi <= 150, Px <= 800 ; 35 */
+ {1, 2, 24}, /* Bpp/Spp Color, Dpi <= 150, Px > 800 ; 36 */
+ /* The difference for this DPI:
+ * if pixels <= | 800 | 1600 | Others
+ * --------------------+-----+-------+----------------------
+ * VarFullStateSpeed | 0 | 1 | 1
+ * VarCurrentSpeed | 1 | 2 | 4
+ * VarStepSpeed | 24 | 48 | 96
+ */
+ {0, 1, 24}, /* Bpp Color, Dpi <= 300, Px <= 800 ; 37 */
+ {1, 2, 48}, /* Bpp Color, Dpi <= 300, Px <= 1600 ; 38 */
+ {1, 4, 96}, /* Bpp Color, Dpi <= 300, Px > 1600 ; 39 */
+ /* The difference for this DPI:
+ * if pixels <= | 800 | 1600 | 3200 | Others
+ * --------------------+-----+------+------+---------------
+ * VarFullStateSpeed | 0 | 1 | 2 | 4
+ * VarCurrentSpeed | 1 | 2 | 4 | 8
+ * VarStepSpeed | 48 | 96 | 96 | 96
+ */
+ {0, 1, 48}, /* Bpp Color, Dpi > 300, Px <= 800 ; 40 */
+ {1, 2, 48}, /* Bpp Color, Dpi > 300, Px <= 1600 ; 41 */
+ {2, 4, 96}, /* Bpp Color, Dpi > 300, Px <= 3200 ; 42 */
+ {4, 8, 96}, /* Bpp Color, Dpi > 300, Px > 3200 ; 43 */
+ /* SPP Color */
+ /* The difference for this DPI:
+ * if pixels <= | 500 | 1000 | 2000 | Others
+ * --------------------+-----+------+------+---------------
+ * VarFullStateSpeed | 0 | 1 | 1 | 2
+ * VarCurrentSpeed | 1 | 2 | 4 | 8
+ * VarStepSpeed | 24 | 48 | 96 | 96
+ */
+ {0, 1, 24}, /* Spp Color, Dpi <= 300, Px <= 500 ; 44 */
+ {1, 2, 48}, /* Spp Color, Dpi <= 300, Px <= 1000 ; 45 */
+ {1, 4, 96}, /* Spp Color, Dpi <= 300, Px <= 2000 ; 46 */
+ {2, 8, 96}, /* Spp Color, Dpi <= 300, Px > 2000 ; 47 */
+ /* The difference for this DPI:
+ * if pixels <= | 500 | 1000 | 2000 | 4000 | Others
+ * --------------------+-----+------+------+------+--------
+ * VarFullStateSpeed | 0 | 1 | 2 | 4 | 5
+ * VarCurrentSpeed | 1 | 2 | 4 | 8 | 10
+ * VarStepSpeed | 48 | 96 | 96 | 96 | 96
+ */
+ {0, 1, 48}, /* Spp Color, Dpi > 300, Px <= 500 ; 48 */
+ {1, 2, 96}, /* Spp Color, Dpi > 300, Px <= 1000 ; 49 */
+ {2, 4, 96}, /* Spp Color, Dpi > 300, Px <= 2000 ; 50 */
+ {4, 8, 96}, /* Spp Color, Dpi > 300, Px <= 4000 ; 51 */
+ {5, 10, 96}, /* Spp Color, Dpi > 300, Px > 4000 ; 52 */
+
+ /* Negative & Transparency */
+ /* EPP/SPP/BPP */
+/* for exposure time = 96 */
+ {0, 1, 12}, /* Spp/EPP Color, Dpi <= 150 ; 60 */
+ {0, 1, 24}, /* Spp Color, Dpi <= 300 ; 61 */
+ {0, 1, 48}, /* Spp Color, Dpi > 300 ; 62 */
+ {0, 1, 12}, /* Bpp/Epp B/W, Dpi <= 75 ; 56 */
+
+/* for exposure time = 144 */
+ {0, 1, 18}, /* Spp/EPP Color, Dpi <= 150 ; 57 */
+ {0, 1, 36}, /* Spp Color, Dpi <= 300 ; 58 */
+ {0, 1, 72}, /* Spp Color, Dpi > 300 ; 59 */
+
+/* for exposure time = 192 */
+ {0, 1, 24}, /* Spp/EPP Color, Dpi <= 150 ; 53 */
+ {0, 1, 48}, /* Spp Color, Dpi <= 300 ; 54 */
+ {0, 1, 96}, /* Spp Color, Dpi > 300 ; 55 */
+
+/* for 48 bits color */
+ {1, 2, 12}, /* Epp Color, Dpi <= 100, Px > 1400 ; 63 */
+ {1, 2, 22}, /* Epp Color, Dpi <= 150, Px > 1900 ; 64 */
+ {2, 4, 48}, /* Epp Color, Dpi <= 300, Px > 4000 ; 65 */
+ {5, 10, 88},/* Epp Color, Dpi > 300, Px > 9600 ; 66 */
+ {3, 12, 96} /* Spp Color, Dpi <= 300, Px > 3000 ; 67 */
+};
+
+
+static pModeTypeVar pModeType;
+static pDiffModeVar pModeDiff;
+
+/*
+ * prototypes for the speed procs (ASIC 98001), EPP, SPP and BIDI
+ */
+static void fnLineArtSpeed( pScanData ps );
+static void fnGraySpeed ( pScanData ps );
+static void fnColorSpeed ( pScanData ps );
+
+static void fnSppLineArtSpeed( pScanData ps );
+static void fnSppGraySpeed ( pScanData ps );
+static void fnSppColorSpeed ( pScanData ps );
+
+static void fnBppLineArtSpeed( pScanData ps );
+static void fnBppGraySpeed ( pScanData ps );
+static void fnBppColorSpeed ( pScanData ps );
+
+/*
+ * some procedures for the different modes
+ */
+static pFnSpeed_Set a_fnSpeedProcs[5] = {
+ fnLineArtSpeed,
+ fnGraySpeed,
+ fnGraySpeed,
+ fnColorSpeed,
+ fnColorSpeed
+};
+
+static pFnSpeed_Set a_fnSppSpeedProcs[5] = {
+ fnSppLineArtSpeed,
+ fnSppGraySpeed,
+ fnSppGraySpeed,
+ fnSppColorSpeed,
+ fnSppColorSpeed
+};
+
+static pFnSpeed_Set a_fnBppSpeedProcs[5] = {
+ fnBppLineArtSpeed,
+ fnBppGraySpeed,
+ fnBppGraySpeed,
+ fnBppColorSpeed,
+ fnBppColorSpeed
+};
+
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ *
+ */
+static void ioP96InitialSetCurrentSpeed( pScanData ps )
+{
+ DBG( DBG_LOW, "ioP96InitialSetCurrentSpeed()\n" );
+
+ switch ( ps->DataInf.wPhyDataType ) {
+
+ case COLOR_BW:
+ ps->bCurrentSpeed = (ps->DataInf.dwAsicPixelsPerPlane >
+ _BUF_SIZE_BASE_CONST * 2) ? 2 : 1;
+ break;
+
+ case COLOR_256GRAY:
+ if ( COLOR_256GRAY == ps->DataInf.wAppDataType ) {
+
+ ps->bCurrentSpeed = (Byte)(ps->a_wGrayInitTime[ps->IO.portMode] /
+ ps->wLinesPer64kTime);
+ if (!ps->bCurrentSpeed)
+ ps->bCurrentSpeed = 1;
+
+ if ((ps->DataInf.dwAsicPixelsPerPlane>=1500) && (ps->bCurrentSpeed==1))
+ ps->bCurrentSpeed = 2;
+
+ if ( ps->DataInf.xyAppDpi.x > 1200) {
+ ps->bCurrentSpeed += 2; /* 1201-2400 */
+
+ if ( ps->DataInf.xyAppDpi.x > 2400 )
+ ps->bCurrentSpeed += 2; /* >= 2401 */
+ }
+
+ MotorP96AdjustCurrentSpeed( ps, ps->bCurrentSpeed );
+
+ } else {
+
+ if ( _PORT_SPP != ps->IO.portMode ) {
+ if( ps->DataInf.dwAsicPixelsPerPlane <= 1280 )
+ ps->bCurrentSpeed = 1; /* <= 1280 pixels */
+
+ else if( ps->DataInf.dwAsicPixelsPerPlane <= 1720 )
+ ps->bCurrentSpeed = 2; /* 1281-1720 */
+
+ else if( ps->DataInf.dwAsicPixelsPerPlane <= 3780 )
+ ps->bCurrentSpeed = 4; /* 1721-3780 */
+
+ else
+ ps->bCurrentSpeed = 6; /* >= 3780 */
+
+ } else {
+
+ if( ps->DataInf.dwAsicPixelsPerPlane <= 400 )
+ ps->bCurrentSpeed = 1; /* <= 400 pixels */
+
+ else if( ps->DataInf.dwAsicPixelsPerPlane <= 853 )
+ ps->bCurrentSpeed = 2; /* 401-853 */
+
+ else if( ps->DataInf.dwAsicPixelsPerPlane <= 1280 )
+ ps->bCurrentSpeed = 4; /* 854-1280 */
+
+ else if( ps->DataInf.dwAsicPixelsPerPlane <= 1728 )
+ ps->bCurrentSpeed = 6; /* 1281-1728 */
+
+ else if( ps->DataInf.dwAsicPixelsPerPlane <= 3780 )
+ ps->bCurrentSpeed = 8; /* 1729-3780 */
+
+ else
+ ps->bCurrentSpeed = 10; /* > 3780 */
+ }
+ }
+ break;
+
+ case COLOR_TRUE24:
+ ps->bCurrentSpeed = (Byte)(ps->a_wColorInitTime[ps->IO.portMode] /
+ ps->wLinesPer64kTime);
+
+ if( 0 == ps->bCurrentSpeed ) {
+ DBG( DBG_LOW, "Initially set to 1\n" );
+ ps->bCurrentSpeed = 1;
+ }
+
+ if (ps->DataInf.xyAppDpi.x > 150) {
+ if (ps->bCurrentSpeed < 4)
+ ps->bCurrentSpeed = 4;
+ } else {
+/*
+// HEINER:A3I
+// if (ps->DataInf.xyAppDpi.x > 100)
+*/
+ if (ps->DataInf.xyAppDpi.x > 75)
+ if (ps->bCurrentSpeed < 2)
+ ps->bCurrentSpeed = 2;
+ }
+
+ if( 1 != ps->bCurrentSpeed )
+ ps->bCurrentSpeed += ps->bExtraAdd;
+
+ if (ps->DataInf.xyAppDpi.x > ps->PhysicalDpi) {
+ if (ps->DataInf.xyAppDpi.x <= 600)
+ ps->bCurrentSpeed += 2;
+ else if (ps->DataInf.xyAppDpi.x <= 1200)
+ ps->bCurrentSpeed += 2;
+ else if (ps->DataInf.xyAppDpi.x <= 2400)
+ ps->bCurrentSpeed += 2;
+ else
+ ps->bCurrentSpeed += 2;
+ }
+
+ MotorP96AdjustCurrentSpeed( ps, ps->bCurrentSpeed );
+ }
+
+ DBG( DBG_LOW, "Current Speed = %u\n", ps->bCurrentSpeed );
+}
+
+/*.............................................................................
+ *
+ */
+static void fnLineArtSpeed( pScanData ps )
+{
+ pModeType = a_BwSettings + _FixParamEppBw;
+ pModeDiff = a_tabDiffParam + _BwEpp75;
+
+ if (ps->DataInf.xyAppDpi.y > 75) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _BwEpp150;
+ }
+
+ if (ps->DataInf.xyAppDpi.y > 150) {
+ if (ps->DataInf.xyAppDpi.y <= 300) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _BwEpp300;
+ } else {
+ pModeType += 2;
+ pModeDiff = a_tabDiffParam + _BwEpp600;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnGraySpeed( pScanData ps )
+{
+ pModeType = a_GraySettings + _FixParamEppGray;
+ pModeDiff = a_tabDiffParam + _GrayEpp75;
+
+ if (ps->DataInf.xyAppDpi.y > 75) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _GrayEpp150;
+ }
+
+ if ( ps->DataInf.xyAppDpi.y > 150) {
+ if (ps->DataInf.xyAppDpi.y <= 300) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _GrayEpp300;
+ } else {
+ pModeType += 2;
+ pModeDiff = a_tabDiffParam + _GrayEpp600;
+ if (ps->DataInf.dwAsicPixelsPerPlane > 3000)
+ pModeDiff++;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnColorSpeed( pScanData ps )
+{
+ DBG( DBG_LOW, "fnColorSpeed();\n" );
+
+ pModeType = a_ColorSettings + _FixParamEppColor;
+
+ if ( ps->DataInf.xyAppDpi.y <= ps->wMinCmpDpi ) {
+ /* DPI <= 60 */
+ pModeDiff = a_tabDiffParam + _ColorEpp60;
+
+ } else {
+
+ if (ps->DataInf.xyAppDpi.y <= 100) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _ColorEpp100;
+
+ if (ps->DataInf.dwAsicBytesPerPlane > 1400)
+ pModeDiff = a_tabDiffParam + _ColorEpp100_1400;
+ } else {
+ if (ps->DataInf.xyAppDpi.y <= 150) {
+ pModeType += 2;
+ pModeDiff = a_tabDiffParam + _ColorEpp150;
+
+ if (ps->DataInf.dwAsicBytesPerPlane > 1900)
+ pModeDiff = a_tabDiffParam + _ColorEpp150_1900;
+ } else {
+ if (ps->DataInf.xyAppDpi.y <= 300) {
+ pModeType += 3;
+ pModeDiff = a_tabDiffParam + _ColorEpp300_1200;
+ if (ps->DataInf.dwAsicBytesPerPlane <= 1200)
+ pModeDiff --;
+ else {
+ if (ps->DataInf.dwAsicBytesPerPlane > 4000)
+ pModeDiff = a_tabDiffParam + _ColorEpp300_4000;
+ }
+ } else {
+ pModeType += 4;
+ pModeDiff = a_tabDiffParam + _ColorEpp600_4000;
+ pModeType->bExposureTime = 88;
+
+ if (ps->DataInf.dwAsicBytesPerPlane <= 4000) {
+ pModeDiff--;
+ if (ps->DataInf.dwAsicBytesPerPlane <= 2800) {
+ pModeType->bExposureTime = 96;
+ pModeDiff--;
+ if (ps->DataInf.dwAsicBytesPerPlane <= 1200)
+ pModeDiff--;
+ }
+ } else {
+ if (ps->DataInf.dwAsicBytesPerPlane >= 9600)
+ pModeDiff = a_tabDiffParam + _ColorEpp600_9600;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnSppLineArtSpeed( pScanData ps )
+{
+ pModeType = a_BwSettings + _FixParamSppBw;
+ pModeDiff = a_tabDiffParam + _BwSpp75;
+
+ if (ps->DataInf.xyAppDpi.y > 75) {
+
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _BwSpp150;
+ }
+
+ if (ps->DataInf.xyAppDpi.y > 150) {
+ if (ps->DataInf.xyAppDpi.y <= 300) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _BwSpp300;
+ } else {
+ pModeType += 2;
+ pModeDiff = a_tabDiffParam + _BwSpp600;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnSppGraySpeed( pScanData ps )
+{
+ pModeType = a_GraySettings + _FixParamSppGray;
+ pModeDiff = a_tabDiffParam + _GraySpp75;
+
+ if (ps->DataInf.xyAppDpi.y > 75) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _GraySpp150_800;
+
+ if (ps->DataInf.xyAppDpi.y > 150) {
+
+ if (ps->DataInf.xyAppDpi.y <= 300) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _GraySpp300_1600;
+ } else {
+
+ pModeType += 2;
+ pModeDiff = a_tabDiffParam + _GraySpp600_3200;
+
+ if (ps->DataInf.dwAsicPixelsPerPlane <= 3200)
+ pModeDiff--;
+ }
+
+ if (ps->DataInf.dwAsicPixelsPerPlane <= 1600)
+ pModeDiff--;
+ }
+
+ if (ps->DataInf.dwAsicPixelsPerPlane <= 800)
+ pModeDiff--;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnSppColorSpeed( pScanData ps )
+{
+ pModeType = a_ColorSettings + _FixParamSppColor;
+ pModeDiff = a_tabDiffParam + _ColorSpp60;
+
+ if (ps->DataInf.xyAppDpi.y > ps->wMinCmpDpi) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorSpp100;
+
+ if (ps->DataInf.xyAppDpi.y > 100) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorSpp150_800;
+
+ if (ps->DataInf.xyAppDpi.y > 150) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorSpp300_2000;
+
+ if (ps->DataInf.xyAppDpi.y > 300) {
+
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorSpp600_4000;
+
+ if (ps->DataInf.dwAsicBytesPerPlane > 4000)
+ return;
+ else
+ pModeDiff--;
+ } else {
+ if (ps->DataInf.dwAsicBytesPerPlane > 3000)
+ pModeDiff = a_tabDiffParam + _ColorSpp300_3000;
+ return;
+ }
+
+ if (ps->DataInf.dwAsicBytesPerPlane <= 2000) {
+ pModeDiff--;
+ if (ps->DataInf.dwAsicBytesPerPlane <= 1000) {
+ pModeDiff--;
+
+ if (ps->DataInf.dwAsicBytesPerPlane <= 500)
+ pModeDiff--;
+ }
+ }
+ } else {
+
+ if (ps->DataInf.dwAsicBytesPerPlane <= 800)
+ pModeDiff--;
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnBppLineArtSpeed( pScanData ps )
+{
+ pModeType = a_BwSettings + _FixParamBppBw;
+/* (+) Micky, 7-14-1998
+ * pModeDiff = a_tabDiffParam + _BwBpp150;
+ */
+ pModeDiff = a_tabDiffParam + _BwBpp75;
+
+ if( ps->DataInf.xyAppDpi.y > 75 ) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _BwBpp150;
+ }
+/* (-) Micky, 7-14-1998 */
+ if( ps->DataInf.xyAppDpi.y > 150 ) {
+ if( ps->DataInf.xyAppDpi.y <= 300 ) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _BwBpp300;
+ } else {
+ pModeType += 2;
+ pModeDiff = a_tabDiffParam + _BwBpp600;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnBppGraySpeed( pScanData ps )
+{
+ pModeType = a_GraySettings + _FixParamBppGray;
+/* (+) Micky, 7-14-1998
+ * pModeDiff = a_tabDiffParam + _GrayBpp150;
+ */
+ pModeDiff = a_tabDiffParam + _GrayBpp75;
+
+ if( ps->DataInf.xyAppDpi.y > 75 ) {
+ pModeType++;
+ pModeDiff = a_tabDiffParam + _GrayBpp150;
+ }
+/* (-) Micky, 7-14-1998 */
+ if( ps->DataInf.xyAppDpi.y > 150 ) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _GrayBpp300_1600;
+ if( ps->DataInf.xyAppDpi.y > 300 ) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _GrayBpp600_3200;
+ if( ps->DataInf.dwAsicPixelsPerPlane <= 3200 )
+ pModeDiff --;
+ }
+ if( ps->DataInf.dwAsicPixelsPerPlane <= 1600 )
+ pModeDiff --;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnBppColorSpeed( pScanData ps )
+{
+ pModeType = a_ColorSettings + _FixParamBppColor;
+ pModeDiff = a_tabDiffParam + _ColorBpp60;
+
+ if (ps->DataInf.xyAppDpi.y > ps->wMinCmpDpi ) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorBpp100;
+
+ if( ps->DataInf.xyAppDpi.y > 100 ) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorBpp150_800;
+ if( ps->DataInf.xyAppDpi.y > 150 ) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorBpp300_1600;
+ if( ps->DataInf.xyAppDpi.y > 300 ) {
+ pModeType ++;
+ pModeDiff = a_tabDiffParam + _ColorBpp600_3200;
+ if( ps->DataInf.dwAsicBytesPerPlane <= 3200 )
+ return;
+ else
+ pModeDiff--;
+ }
+
+ if( ps->DataInf.dwAsicBytesPerPlane <= 1600 )
+ pModeDiff--;
+ }
+
+ if( ps->DataInf.dwAsicBytesPerPlane <= 800 )
+ pModeDiff--;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void ioP98SppNegativeProcs( pScanData ps )
+{
+ if ( ps->DataInf.dwScanFlag & SCANDEF_Negative )
+ pModeType = a_FilmSettings + _FixParamSppNegative;
+ else
+ pModeType = a_FilmSettings + _FixParamSppPositive;
+
+ pModeDiff = a_tabDiffParam + _NegativeSpp150;
+
+ if (ps->DataInf.xyAppDpi.y > 150) {
+ if (ps->DataInf.xyAppDpi.y < 300) {
+ pModeType ++;
+ pModeDiff ++;
+ } else {
+ pModeType += 2;
+ pModeDiff += 2;
+ }
+ }
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_Negative) {
+ if (ps->AsicReg.RD_LineControl == 144)
+ pModeDiff += 4;
+ else
+ if (ps->AsicReg.RD_LineControl == 192)
+ pModeDiff += 7;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void ioP98EppNegativeProcs( pScanData ps )
+{
+ if (ps->DataInf.dwScanFlag & SCANDEF_Negative)
+ pModeType = a_FilmSettings + _FixParamEppNegative;
+ else
+ pModeType = a_FilmSettings + _FixParamEppPositive;
+
+ pModeDiff = a_tabDiffParam + _NegativeEpp150;
+
+ if (ps->DataInf.xyAppDpi.y > 150) {
+ if (ps->DataInf.xyAppDpi.y < 300)
+ {
+ pModeType ++;
+ pModeDiff ++;
+ } else {
+ pModeType += 2;
+ pModeDiff += 2;
+ }
+ }
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_Negative) {
+ if (ps->AsicReg.RD_LineControl == 144)
+ pModeDiff += 4;
+ else
+ if (ps->AsicReg.RD_LineControl == 192)
+ pModeDiff += 7;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void ioP98BppNegativeProcs( pScanData ps )
+{
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative) {
+ pModeType = a_FilmSettings + _FixParamBppNegative;
+ } else {
+ pModeType = a_FilmSettings + _FixParamBppPositive;
+ }
+
+ pModeDiff = a_tabDiffParam + _NegativeBpp150;
+
+ if( ps->DataInf.xyAppDpi.y > 150 ) {
+ if( ps->DataInf.xyAppDpi.y < 300 ) {
+ pModeType ++;
+ pModeDiff ++;
+ } else {
+ pModeType += 2;
+ pModeDiff += 2;
+ }
+ }
+
+ if ( ps->DataInf.dwScanFlag & SCANDEF_Negative ) {
+ if( ps->AsicReg.RD_LineControl == 144 ) {
+ pModeDiff += 4;
+ } else {
+ if( ps->AsicReg.RD_LineControl == 192 )
+ pModeDiff += 7;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void ioControlLampOnOff( pScanData ps )
+{
+ Byte lampStatus;
+
+ ps->fWarmupNeeded = _TRUE;
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ lampStatus = ps->AsicReg.RD_ScanControl & _SCAN_LAMPS_ON;
+
+ if (ps->bLastLampStatus != lampStatus) {
+
+ DBG( DBG_LOW, "Using OTHER Lamp !\n" );
+ ps->bLastLampStatus = lampStatus;
+
+ IOCmdRegisterToScanner( ps, ps->RegScanControl,
+ ps->AsicReg.RD_ScanControl);
+ return;
+ }
+ } else {
+
+ lampStatus = ps->AsicReg.RD_ScanControl & _SCAN_LAMP_ON;
+
+ if (ps->DataInf.dwScanFlag&(SCANDEF_Transparency + SCANDEF_Negative)) {
+ ps->bLampOn = 0;
+ } else {
+ ps->bLampOn = _SCAN_LAMP_ON;
+ }
+
+ if (ps->bLastLampStatus != lampStatus) {
+ DBG( DBG_LOW, "Using OTHER Lamp !\n" );
+ ps->bLastLampStatus = lampStatus;
+ return;
+ }
+ }
+
+ ps->fWarmupNeeded = _FALSE;
+ DBG( DBG_LOW, "Using SAME Lamp !\n" );
+}
+
+/*.............................................................................
+ *
+ */
+static void ioP98InitialSetCurrentSpeed( pScanData ps )
+{
+ DBG( DBG_LOW, "ioP98InitialSetCurrentSpeed()\n" );
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA) {
+
+ switch (ps->IO.portMode)
+ {
+ case _PORT_SPP: ioP98SppNegativeProcs( ps ); break;
+ case _PORT_BIDI: ioP98BppNegativeProcs( ps ); break;
+
+ default: ioP98EppNegativeProcs( ps ); break;
+ }
+ } else {
+
+ switch (ps->IO.portMode) {
+ case _PORT_SPP:
+ a_fnSppSpeedProcs[ps->DataInf.wAppDataType](ps);
+ break;
+
+ case _PORT_BIDI:
+ a_fnBppSpeedProcs[ps->DataInf.wAppDataType](ps);
+ break;
+
+ default:
+ a_fnSpeedProcs[ps->DataInf.wAppDataType](ps);
+ break;
+ }
+ }
+
+ ps->wInitialStep = pModeType->wHomePos;
+ ps->wMaxMoveStep = pModeType->wMaxSteps;
+
+ ps->AsicReg.RD_LineControl = pModeType->bExposureTime;
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_Negative)
+ ps->AsicReg.RD_LineControl = 144;
+
+#ifdef DEBUG
+ if( pModeType->bFlagScanMode != ps->Shade.bIntermediate )
+ DBG( DBG_HIGH, "bSetScanModeFlag != bIntermediate\n" );
+#endif
+
+ ps->bHpMotor = pModeType->bMotorStep;
+ ps->bSetScanModeFlag = pModeType->bFlagScanMode;
+ ps->bShadingTimeFlag = pModeType->bTimesShading;
+
+ ps->dwFullStateSpeed = pModeDiff->dwFullSpeed;
+ ps->bCurrentSpeed = pModeDiff->bCurrentSpeed;
+ ps->bStepSpeed = pModeDiff->bStepSpeed;
+
+ if( ps->DataInf.xyAppDpi.y > 600 ) {
+ if( ps->dwFullStateSpeed )
+ ps->dwFullStateSpeed = 0;
+ else
+ ps->bStepSpeed <<= 1;
+ ps->wMaxMoveStep <<= 1;
+ }
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * here we do some init work
+ */
+_LOC int IOFuncInitialize( pScanData ps )
+{
+ DBG( DBG_HIGH, "IOFuncInitialize()\n" );
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ ps->lpEppColorHomePos = &a_ColorSettings[0];
+ ps->lpEppColorExposure = &a_ColorSettings[4];
+ ps->lpBppColorHomePos = &a_ColorSettings[5];
+ ps->lpSppColorHomePos = &a_ColorSettings[10];
+ ps->a_tabDiffParam = a_tabDiffParam;
+ ps->a_ColorSettings = a_ColorSettings;
+
+ /*
+ * depending on the asic, we set some functions
+ */
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ ps->InitialSetCurrentSpeed = ioP98InitialSetCurrentSpeed;
+
+ } else if( _IS_ASIC96(ps->sCaps.AsicID)) {
+
+ ps->InitialSetCurrentSpeed = ioP96InitialSetCurrentSpeed;
+
+ } else {
+
+ DBG( DBG_HIGH , "NOT SUPPORTED ASIC !!!\n" );
+ return _E_NOSUPP;
+ }
+
+ return _OK;
+}
+
+/*.............................................................................
+ * 1) Fill scan states to asic.
+ * 2) Refresh the scan states if necessary
+ * 3) Wait for motor running within half-second period.
+ */
+_LOC Byte IOSetToMotorRegister( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ IORegisterToScanner( ps, ps->RegInitScanState );
+
+ IODownloadScanStates( ps );
+
+ ps->CloseScanPath( ps );
+
+ if( _ASIC_IS_98001 != ps->sCaps.AsicID ) {
+ return 0;
+ }
+
+ ps->Scan.bOldScanState = IOGetScanState( ps, _FALSE );
+
+ return ps->Scan.bOldScanState;
+}
+
+/*.............................................................................
+ * 1) If scanner path is not established, connect it
+ * 2) Read the recent state count
+ * 3) Disconnect the path if necessary
+ */
+_LOC Byte IOGetScanState( pScanData ps, Bool fOpenned )
+{
+ Byte bScanState, bScanStateNow;
+
+ if( !fOpenned && (_ASIC_IS_98003 != ps->sCaps.AsicID))
+ ps->OpenScanPath( ps );
+
+ bScanState = IODataFromRegister( ps, ps->RegGetScanState );
+ bScanStateNow = IODataFromRegister( ps, ps->RegGetScanState );
+
+ if((bScanState != bScanStateNow)
+ || ((ps->sCaps.AsicID == _ASIC_IS_98001 && bScanState & 0x40))) {
+ bScanState = IODataFromRegister( ps, ps->RegGetScanState);
+ }
+
+ if( !fOpenned && (_ASIC_IS_98003 != ps->sCaps.AsicID))
+ ps->CloseScanPath( ps );
+
+ return bScanState;
+}
+
+/*.............................................................................
+ * ASIC 98003 specific function to read status 2 regiser
+ */
+_LOC Byte IOGetExtendedStatus( pScanData ps )
+{
+ Byte b;
+
+ b = IODataFromRegister( ps, ps->RegStatus2 );
+
+ if( b == 0xff )
+ return 0;
+ return b;
+}
+
+/*.............................................................................
+ * Read the scan state. Return the count with status bit, and count.
+ */
+_LOC void IOGetCurrentStateCount( pScanData ps, pScanState pScanStep )
+{
+ pScanStep->bStatus = IOGetScanState( ps, _FALSE );
+ pScanStep->bStep = pScanStep->bStatus & _SCANSTATE_MASK;
+}
+
+/*.............................................................................
+ * 1) If scanner connection is not established, return error
+ * 2) If paper not ready, return error
+ * 3) If scanning environment is not prepared, return error
+ * 4) Setup the buffers for reassembler the CCD incoming lines.
+ * 5) Initiate the registers of asic.
+ * [NOTE]
+ * This routine combines from SetupAsicDependentVariables & IsReadyForScan
+ * routines in assembly source.
+ */
+_LOC int IOIsReadyForScan( pScanData ps )
+{
+ ULong dw;
+ pULong pdwTable;
+
+ if((_NO_BASE != ps->sCaps.wIOBase) &&
+ (ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY)) {
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ IOSelectLampSource( ps );
+ ioControlLampOnOff( ps );
+ ps->AsicReg.RD_Motor0Control = 0; /* motor off */
+ ps->AsicReg.RD_Motor1Control = 0; /* motor off */
+ ps->AsicReg.RD_ModelControl = (_ModelDpi600 +
+ _LED_ACTIVITY + _LED_CONTROL);
+ ps->AsicReg.RD_Origin = 0;
+ ps->AsicReg.RD_Pixels = 5110;
+
+ } else if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ ps->OpenScanPath( ps );
+ P12SetGeneralRegister( ps );
+ ps->CloseScanPath( ps );
+
+ ioControlLampOnOff( ps );
+
+ } else {
+
+ ioControlLampOnOff( ps );
+
+ /* SetupAsicDependentVariables */
+ ps->pPutBufR = ps->pGetBufR = ps->pPrescan16; /* 1st color plane */
+ ps->pPutBufG = ps->pGetBufG = ps->pPrescan8; /* 2nd color plane */
+
+ ps->AsicReg.RD_ScanControl = ps->bLampOn;
+ ps->Asic96Reg.RD_MotorControl = 0;
+ ps->AsicReg.RD_Origin = 0;
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelWhiteIs0;
+ ps->AsicReg.RD_Pixels = 5110; /* ps->RdPix; */
+ IOPutOnAllRegisters( ps );
+ }
+
+ /*
+ * MotorInitiate
+ */
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID ) {
+ for (dw = _SCANSTATE_BYTES,
+ pdwTable = (pULong)ps->a_wMoveStepTable; dw; dw--, pdwTable++) {
+ *pdwTable = 0x10001;
+ }
+
+ memset( ps->a_bColorByteTable, 0, _NUMBER_OF_SCANSTEPS );
+ }
+
+ return _OK;
+ }
+
+ return _E_SEQUENCE;
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void IOSetXStepLineScanTime( pScanData ps, Byte b )
+{
+ ps->AsicReg.RD_LineControl = b;
+ ps->bSpeed1 = b;
+ ps->bSpeed2 = b >> 1;
+ ps->bSpeed4 = b >> 2;
+ ps->bSpeed8 = b >> 3;
+ ps->bSpeed16 = b >> 4;
+ ps->bSpeed32 = b >> 5;
+ ps->bSpeed24 = b / 24;
+ ps->bSpeed12 = b / 12;
+ ps->bSpeed6 = b / 6;
+ ps->bSpeed3 = b / 3;
+}
+
+/*.............................................................................
+ * 1) Reset and fill all new scan states (Mode = Scan)
+ * 2) Refresh scan state
+ * 3) Wait for motor running within half second.
+ */
+_LOC void IOSetToMotorStepCount( pScanData ps )
+{
+ ULong dw;
+ pUChar pb;
+ TimerDef timer;
+
+ ps->OpenScanPath( ps );
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+ IORegisterToScanner( ps, ps->RegInitScanState );
+ } else {
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+ }
+ IORegisterToScanner( ps, ps->RegScanStateControl );
+
+ for (dw = _SCANSTATE_BYTES, pb = ps->a_nbNewAdrPointer; dw; dw--, pb++)
+ IODataToScanner( ps, *pb );
+
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ MiscStartTimer( &timer, (_SECOND/2));
+ do {
+
+ if (!( IOGetScanState( ps, _TRUE) & _SCANSTATE_STOP))
+ break;
+ }
+ while( !MiscCheckTimer(&timer));
+
+/* CHECK - this line has been added by Rick ? Why ?
+ * return (pScanData->bOldTempScanState = GetScanState (pScanData, FALSE));
+ */
+ ps->Scan.bOldScanState = IOGetScanState( ps, _TRUE );
+
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void IOSelectLampSource( pScanData ps )
+{
+ ps->AsicReg.RD_ScanControl &= (~_SCAN_LAMPS_ON);
+
+ if (ps->DataInf.dwScanFlag & (SCANDEF_TPA)) {
+ ps->AsicReg.RD_ScanControl |= _SCAN_TPALAMP_ON;
+ } else {
+ ps->AsicReg.RD_ScanControl |= _SCAN_NORMALLAMP_ON;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+_LOC Bool IOReadOneShadingLine( pScanData ps, pUChar pBuf, ULong len )
+{
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND );
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+
+ do {
+ if( IOReadFifoLength( ps ) >= ps->AsicReg.RD_Pixels ) {
+
+ IOReadColorData( ps, pBuf, len );
+ return _TRUE;
+ }
+ } while( _OK == MiscCheckTimer( &timer ));
+
+ return _FALSE;
+}
+
+/*.............................................................................
+ *
+ */
+_LOC ULong IOReadFifoLength( pScanData ps )
+{
+ DataType Data;
+
+ Data.dwValue = 0;
+
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID )
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegBitDepth, _BIT0_7 );
+ Data.dwOverlap.w1st.b1st = IODataFromRegister( ps, ps->Scan.bFifoSelect );
+
+ IODataToRegister( ps, ps->RegBitDepth, _BIT8_15 );
+ Data.dwOverlap.w1st.b2nd = IODataFromRegister( ps, ps->Scan.bFifoSelect );
+
+ IODataToRegister( ps, ps->RegBitDepth, _BIT16_20 );
+ Data.dwOverlap.w2nd.b1st = (IODataFromRegister( ps, ps->Scan.bFifoSelect) & 0x0f);
+
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID )
+ ps->CloseScanPath( ps );
+
+ return Data.dwValue;
+}
+
+/*.............................................................................
+ * 1) Initiates the scan states
+ * 2) Write the contents to corresponding registers (from ps->RegModeControl to
+ * ps->RegGreenGainOutDirect (P9363) or from ps->RegModeControl to
+ * ps->RegModeControl2 (48xx)
+ */
+_LOC void IOPutOnAllRegisters( pScanData ps )
+{
+ pUChar pValue;
+ Byte bReg;
+
+ /* setup scan states */
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ IODownloadScanStates( ps );
+ else {
+ IOSetToMotorRegister( ps );
+ ps->OpenScanPath( ps );
+ }
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ IODataToRegister(ps, ps->RegStepControl, ps->AsicReg.RD_StepControl);
+ IODataToRegister(ps, ps->RegMotor0Control,
+ ps->AsicReg.RD_Motor0Control);
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ IODataToRegister(ps,ps->RegLineControl,ps->AsicReg.RD_LineControl);
+
+ IODataToRegister(ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);
+ IODataToRegister(ps, ps->RegModelControl, ps->AsicReg.RD_ModelControl);
+
+ /* the 1st register to write */
+ pValue = (pUChar)&ps->AsicReg.RD_Dpi;
+
+ /* 0x21 - 0x28 */
+ for (bReg = ps->RegDpiLow;
+ bReg <= ps->RegThresholdHigh; bReg++, pValue++) {
+
+ IODataToRegister( ps, bReg, *pValue);
+ }
+
+ IORegisterToScanner( ps, ps->RegInitDataFifo );
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+ else
+ IODataToRegister( ps, ps->RegModeControl, (_ModeScan + _ModeFifoRSel));
+
+ } else {
+
+ /*
+ * the original driver uses a loop, starting at RegModeControl
+ * 0x18 - 0x26
+ * as we use the Asic96Reg structure only for the differences
+ * to the AsicReg struct, we have to write to each register by hand
+ */
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+ IODataToRegister( ps, ps->RegLineControl, ps->AsicReg.RD_LineControl );
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+ IODataToRegister( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+ IODataToRegister( ps, ps->RegModelControl,
+ ps->AsicReg.RD_ModelControl );
+ IODataToRegister( ps, ps->RegMemAccessControl,
+ ps->Asic96Reg.RD_MemAccessControl );
+
+#if 0
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegLineControl, ps->AsicReg.RD_LineControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegModelControl, ps->AsicReg.RD_ModelControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegMemAccessControl, ps->Asic96Reg.RD_MemAccessControl );
+#endif
+
+ pValue = (pUChar)&ps->AsicReg.RD_Dpi;
+
+ /* 0x21 - 0x26 */
+ for (bReg = ps->RegDpiLow;
+ bReg <= ps->RegWidthPixelsHigh; bReg++, pValue++) {
+
+ IODataToRegister( ps, bReg, *pValue );
+#if 0
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n", bReg, *pValue );
+#endif
+ }
+
+ /* the rest */
+ IODataToRegister( ps, ps->RegThresholdControl,
+ (Byte)ps->AsicReg.RD_ThresholdControl );
+
+ IODataToRegister( ps, ps->RegWatchDogControl,
+ (Byte)ps->Asic96Reg.RD_WatchDogControl );
+
+ IODataToRegister( ps, ps->RegModelControl2,
+ ps->Asic96Reg.u26.RD_ModelControl2 );
+
+#if 0
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegThresholdControl, ps->AsicReg.RD_ThresholdControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegWatchDogControl, ps->Asic96Reg.RD_WatchDogControl );
+ DBG( DBG_LOW, "[0x%02x] = 0x%02x\n",
+ ps->RegModelControl2, ps->Asic96Reg.u26.RD_ModelControl2 );
+#endif
+ IORegisterToScanner( ps, ps->RegInitDataFifo );
+ }
+
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID )
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void IOReadColorData( pScanData ps, pUChar pBuf, ULong len )
+{
+ ps->AsicReg.RD_ModeControl = _ModeFifoRSel;
+ IOReadScannerImageData( ps, pBuf, len );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, pBuf + len, len );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+ IOReadScannerImageData( ps, pBuf + len * 2, len );
+}
+
+/* END PLUSTEK-PP_GENERICIO.C ...............................................*/
diff --git a/backend/plustek-pp_hwdefs.h b/backend/plustek-pp_hwdefs.h
new file mode 100644
index 0000000..2672380
--- /dev/null
+++ b/backend/plustek-pp_hwdefs.h
@@ -0,0 +1,1034 @@
+/* @file plustek-pp_hwdefs.h
+ * @brief different definitions for describing the scanner hardware
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson <rick@efn.org>
+ *
+ * History:
+ * 0.30 - initial version
+ * 0.31 - corrected the values of _GAIN_P98_HIGH, _GAIN_P96_HIGH and _GAIN_LOW
+ * 0.32 - removed _LampDelay defines
+ * removed _MODE_xxx defines
+ * 0.33 - cosmetic changes
+ * removed _PORT_BPP from modelist
+ * 0.34 - no changes
+ * 0.35 - no changes
+ * 0.36 - moved struct ScanState to this header
+ * moved some definitions from scandata.h to this file
+ * 0.37 - some cleanup work
+ * added model override defines here
+ * added _A3
+ * changed some _ defines to _ defines
+ * 0.38 - added ASIC98003 stuff
+ * removed the _ASIC_xxxxx definitions
+ * 0.39 - major changes: moved a lot of stuff to this file
+ * 0.40 - no changes
+ * 0.41 - added _OVR_PLUSTEK_4800P definition
+ * 0.42 - added _OVR_PRIMAX_4800D30 definition
+ * 0.43 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __HWDEFS_H__
+#define __HWDEFS_H__
+
+/*
+ * port modes
+ * (WARNING: never change these defines, as they are used as entries
+ * to jump tables !!!)
+ */
+#define _PORT_EPP 0
+#define _PORT_SPP 1
+#define _PORT_BIDI 2
+#define _PORT_ECP 3
+#define _PORT_ESP 4
+#define _PORT_NONE 5
+
+/*
+ * ScannerSize
+ */
+#define _SCANSIZE_A4 0
+#define _SCANSIZE_LETTER 1
+#define _SCANSIZE_LEGAL 2
+#define _SCANSIZE_A3 3
+
+/*
+ * Magic IDs switch printer port to scanner mode
+ */
+#define _ID1ST 0x69
+#define _ID2ND 0x96
+#define _ID3RD 0xa5
+#define _ID4TH 0x5a
+
+/*
+ * Special IDs used to reset scanner (ASIC98003)
+ */
+#define _RESET1ST 0x69
+#define _RESET2ND 0x96
+#define _RESET3RD 0xaa
+#define _RESET4TH 0x55
+
+/*
+ * ID switch to printer mode
+ */
+#define _ID_TO_PRINTER 0
+
+/*
+ * Flags for internal use
+ */
+#define _VF_BUILDMAP 0x0000001
+#define _VF_DATATOUSERBUFFER 0x0000002
+#define _VF_ENVIRONMENT_READY 0x0000004
+#define _VF_FIRSTSCANLINE 0x0000008
+#define _VF_PREVIEW 0x0000020
+
+/*
+ * Printer Control Port: Definitions
+ */
+#define _CTRL_STROBE 0x01
+#define _CTRL_AUTOLF 0x02
+#define _CTRL_NOT_INIT 0x04
+#define _CTRL_SELECT_IN 0x08
+#define _CTRL_ENABLE_IRQ 0x10
+#define _CTRL_DIRECTION 0x20
+#define _CTRL_RESERVED 0xc0
+
+/*
+ * here are the definitions for the different registers
+ * first the ASIC 96001/3 section
+ */
+
+/* Status Register 0x10 */
+#define _FLAG_P96_PAPER 0x01
+#define _FLAG_P96_ADF 0x02
+#define _FLAG_P96_KEY 0x04
+#define _FLAG_P96_EPP 0x08
+#define _FLAG_P96_FIFOFULL 0x10
+#define _FLAG_P96_FIFOEMPTY 0x20
+#define _FLAG_P96_CCDTYPE 0x40
+#define _FLAG_P96_MOTORTYPE 0x80
+
+/*
+ * the 98001 section
+ */
+#define _DEFAULT_LINESCANTIME 96
+
+/* Status Register (Addr: 0x30) */
+#define _FLAG_P98_PAPER 0x01
+#define _FLAG_P98_KEY 0x80
+
+/*
+ * some buffer sizes (for ASIC 98001 based devices)
+ */
+#define _LINE_BUFSIZE (5500 * 6)
+#define _LINE_BUFSIZE1 (5500 * 8)
+#define _PROCESS_BUFSIZE (5120 * 3)
+
+/*
+ * generic equates
+ */
+#define _DEF_BW_THRESHOLD 111 /* default B/W mode threshold value */
+#define _NUMBER_OF_SCANSTEPS 64 /* Asic spec.: up to 64 scan steps */
+#define _SCANSTATE_BYTES (_NUMBER_OF_SCANSTEPS/2)
+
+/* CHECK: Play around with the P98003/1
+ * gain values - maybe we get a brighter picture...
+ */
+#define _GAIN_P98_HIGH 225 /* Volt. max. value, Asic 98001 */
+#define _GAIN_P98003_HIGH 240 /* Volt. max. value, Asic 98003 */
+#define _GAIN_P96_HIGH 240 /* Volt. max. value, Asic 96001/3 */
+#define _GAIN_LOW 210 /* Volt. min. value */
+#define _GAIN_P98003_LOW 220 /* Volt. min. value, Asic 98003 */
+#define _MOTOR_ONE_LINE_TIME 4
+
+/* for ASIC 98001/3 shading */
+#define _DEF_BRIGHTEST_SKIP 3
+#define _DEF_DARKEST_SKIP 5
+
+#define _BUF_SIZE_BASE_CONST 1280
+#define _SCANSTATE_TABLE_SIZE 250 /* was 200 for 4830 */
+#define _P98_OFFSET70 60
+
+/* for Data channel (BYTE) */
+#define _RED_DATA_READY 0x01
+#define _GREEN_DATA_READY 0x02
+#define _BLUE_DATA_READY 0x04
+
+#define _ASIC_REDCOLOR 0x01
+#define _ASIC_GREENCOLOR 0x03
+#define _ASIC_BLUECOLOR 0x02
+
+/*
+ * for Asic I/O signal control
+ */
+#define _CTRL_GENSIGNAL (_CTRL_RESERVED + _CTRL_NOT_INIT) /* 0xc4 */
+
+#define _CTRL_START_REGWRITE (_CTRL_GENSIGNAL + _CTRL_SELECT_IN) /* 0xcc */
+#define _CTRL_END_REGWRITE (_CTRL_GENSIGNAL) /* 0xc4 */
+
+#define _CTRL_START_DATAWRITE (_CTRL_GENSIGNAL + _CTRL_AUTOLF) /* 0xc6 */
+#define _CTRL_END_DATAWRITE (_CTRL_GENSIGNAL) /* 0xc4 */
+
+#define _CTRL_EPPSIGNAL_WRITE (_CTRL_GENSIGNAL + _CTRL_STROBE) /* 0xc5 */
+#define _CTRL_EPPTRIG_REGWRITE (_CTRL_GENSIGNAL + _CTRL_SELECT_IN + _CTRL_STROBE)
+
+#define _CTRL_START_BIDIREAD (_CTRL_GENSIGNAL + _CTRL_DIRECTION + _CTRL_AUTOLF)
+#define _CTRL_END_BIDIREAD (_CTRL_GENSIGNAL + _CTRL_DIRECTION) /* 0xe4 */
+
+
+typedef struct
+{
+ ULong dwFullSpeed;
+ Byte bCurrentSpeed;
+ Byte bStepSpeed;
+} DiffModeVar, *pDiffModeVar;
+
+typedef struct
+{
+ UShort wHomePos; /* scanner's scanning home position */
+ UShort wMaxSteps; /* maximum steps for this scan */
+ Byte bExposureTime; /* exposure time for one line */
+ Byte bMotorStep;
+ Byte bFlagScanMode; /* see below */
+ Byte bTimesShading; /* see below */
+} ModeTypeVar, *pModeTypeVar;
+
+typedef struct {
+ Byte bStep;
+ Byte bStatus;
+} ScanState, *pScanState;
+
+typedef struct {
+ Byte bReg;
+ Byte bParam;
+} RegDef, *pRegDef;
+
+typedef union {
+ RGBByteDef Colors;
+ UChar bColors[3];
+} ColorByte, *pColorByte;
+
+typedef union {
+ RGBUShortDef Colors;
+ UShort wColors[3];
+} ColorWord, *pColorWord;
+
+typedef struct {
+ UShort exposureTime;
+ UShort xStepTime;
+} ExpXStepDef, *pExtXStepDef;
+
+typedef struct {
+ UShort thresholdBW;
+ UShort thresholdGray;
+ UShort thresholdColor;
+} ThreshDef, *pThreshDef;
+
+/* for decription of the DAC specific stuff*/
+typedef struct {
+ ColorWord GainResize;
+ ColorWord DarkCmpHi;
+ ColorWord DarkCmpLo;
+ ColorWord DarkOffSub;
+ ColorByte DarkDAC;
+ UChar Reserved;
+} DACTblDef, *pDACTblDef;
+
+/*
+ * some function types
+ */
+typedef struct scandata *pScanData;
+typedef void (*pFnDataProcess)(pScanData, pVoid, pVoid, ULong);
+typedef Bool (*pFnReadData)(pScanData, pUChar, ULong);
+typedef void (*pFnVoid)(pScanData);
+typedef Bool (*pFnBool)(pScanData);
+typedef void (*pFnDACOffs)(pScanData, pDACTblDef, ULong);
+typedef void (*pFnDACDark)(pScanData, pDACTblDef, ULong, UShort);
+typedef void (*pFnOut)(Byte,UShort);
+typedef Byte (*pFnIn)(UShort);
+
+/*
+ * For Motor step control
+ */
+#define _FullStepStrong 0x10
+#define _HalfStep 0x04
+#define _QuarterStep 0x08
+#define _EightStep 0x0c
+
+/* bTimesShading */
+#define _Shading_32Times 0
+#define _Shading_4Times 1
+#define _Shading_16Times 2
+
+/* Scan.bModuleState */
+#define _MotorInNormalState 0
+#define _MotorGoBackward 1
+#define _MotorInStopState 2
+#define _MotorAdvancing 3
+#define _MotorAdvanced 4
+
+/* bMoveDataOutFlag */
+#define _DataInNormalState 0
+#define _DataAfterRefreshState 1
+#define _DataFromStopState 2
+
+/* bFastMoveFlag */
+#define _FastMove_Fast_C50_G100 0
+#define _FastMove_Middle_C75_G150 1
+#define _FastMove_Low_C75_G150 2
+#define _FastMove_Low_C75_G150_Back 4
+#define _FastMove_Pos_300 5
+#define _FastMove_Film_150 6
+
+/*
+ * ASIC 98003 base models...
+ */
+#define _TPA_P98003_SHADINGORG 2172U
+#define _RFT_SCANNING_ORG 380U
+
+#define _POS_SCANNING_ORG 2840U
+#define _POS_PAGEWIDTH 450U
+#define _POS_ORG_OFFSETX 0x41C
+
+#define _NEG_SCANNING_ORG 3000U
+#define _NEG_PAGEWIDTH 464U
+#define _NEG_PAGEWIDTH600 992U
+#define _NEG_ORG_OFFSETX 0x430
+#define _NEG_SHADING_OFFS 1500U
+
+#define _NEG_EDGE_VALUE 0x800
+
+#define _SHADING_BEGINX 4U
+
+
+/* home positions */
+#define _Home_CE50 0x1f /* 0x1d 1b */
+#define _Home_CE100 0x27 /* 0x34 23 */
+#define _Home_CE150 0x29 /* 0x23 25 */
+#define _Home_CE300 0x34 /* 0x2e 30 */
+#define _Home_CE600 0x3a /* 0x35 36 */
+#define _Home_CB50 0x20 /* 0x22 1c */
+#define _Home_CB100 0x41 /* 0x42 3d */
+#define _Home_CB150 0x41 /* 0x3f 3d */
+#define _Home_CB300 0x4c /* 0x4a 48 */
+#define _Home_CB600 0x53 /* 0x54 4f */
+#define _Home_CS50 0x20 /* 0x1b 1c */
+#define _Home_CS100 0x41 /* 0x40 3d */
+#define _Home_CS150 0x41 /* 0x3e 3d */
+#define _Home_CS300 0x4c /* 0x4b 48 */
+#define _Home_CS600 0x53 /* 0x4f 4f */
+#define _Home_GE75 0x08 /* 7-14-98 00 */
+#define _Home_GE150 0x04 /* 0x22 00 */
+#define _Home_GE300 0x12 /* 0x2d 0e */
+#define _Home_GE600 0x19 /* 0x33 15 */
+#define _Home_GB75 0x40 /* 7-14-98 3e */
+#define _Home_GB150 0x40 /* 0x3f 3e */
+#define _Home_GB300 0x4e /* 0x4c 4a */
+#define _Home_GB600 0x53 /* 0x51 4f */
+#define _Home_GS75 0x40 /* 7-14-98 3c */
+#define _Home_GS150 0x40 /* 0x3f 3c */
+#define _Home_GS300 0x4e /* 0x4d 4a */
+#define _Home_GS600 0x53 /* 0x51 4f */
+#define _Home_BE75 0x30 /* 7-14-98 20 */
+#define _Home_BE150 0x30 /* 0x3c 20 */
+#define _Home_BE300 0x32 /* 0x48 2e */
+#define _Home_BE600 0x37 /* 0x35 33 */
+#define _Home_BB75 0x42 /* 7-14-98 3e */
+#define _Home_BB150 0x42 /* 0x47 3e */
+#define _Home_BB300 0x50 /* 0x51 4c */
+#define _Home_BB600 0x53 /* 0x51 4f */
+#define _Home_BS75 0x42 /* 7-14-98 3e */
+#define _Home_BS150 0x42 /* 0x46 3e */
+#define _Home_BS300 0x50 /* 0x4f 4c */
+#define _Home_BS600 0x53 /* 0x55 4f */
+
+/*
+ * for ModeTypeVar indexes
+ */
+#define _FixParamEppBw 0
+#define _FixParamBppBw _FixParamEppBw + 4
+#define _FixParamSppBw _FixParamBppBw + 4
+#define _FixParamEppGray 0
+#define _FixParamBppGray _FixParamEppGray + 4
+#define _FixParamSppGray _FixParamBppGray + 4
+#define _FixParamEppColor 0
+#define _FixParamBppColor _FixParamEppColor + 5
+#define _FixParamSppColor _FixParamBppColor + 5
+#define _FixParamSppNegative 0
+#define _FixParamBppNegative _FixParamSppNegative + 3
+#define _FixParamEppNegative _FixParamBppNegative + 3
+#define _FixParamSppPositive _FixParamEppNegative + 3
+#define _FixParamBppPositive _FixParamSppPositive + 3
+#define _FixParamEppPositive _FixParamBppPositive + 3
+
+/*
+ * for DiffModeVar indexes
+ */
+#define _BwEpp150 0
+#define _BwEpp300 1
+#define _BwEpp600 2
+#define _BwBpp150 _BwEpp150
+#define _BwBpp300 _BwEpp300
+#define _BwBpp600 _BwEpp600
+#define _BwSpp150 3
+#define _BwSpp300 4
+#define _BwSpp600 5
+#define _GrayEpp150 6
+#define _GrayEpp300 7
+#define _GrayEpp600 8
+#define _GrayEpp600_3000 9 /* > 3000 pixels per channel */
+#define _GrayBpp150 10
+#define _GrayBpp300 11
+#define _GrayBpp300_1600 12
+#define _GrayBpp600 13
+#define _GrayBpp600_1600 14
+#define _GrayBpp600_3200 15
+#define _GraySpp150 16
+#define _GraySpp150_800 17
+#define _GraySpp300 18
+#define _GraySpp300_800 19
+#define _GraySpp300_1600 20
+#define _GraySpp600 21
+#define _GraySpp600_800 22
+#define _GraySpp600_1600 23
+#define _GraySpp600_3200 24
+#define _ColorEpp60 25
+#define _ColorEpp100 _ColorEpp60
+#define _ColorEpp150 26
+#define _ColorEpp300 27
+#define _ColorEpp300_1200 28
+#define _ColorEpp600 29
+#define _ColorEpp600_1400 30
+#define _ColorEpp600_2800 31
+#define _ColorEpp600_4000 32
+#define _ColorBpp60 33
+#define _ColorBpp100 34
+#define _ColorBpp150 35
+#define _ColorBpp150_800 36
+#define _ColorBpp300 37
+#define _ColorBpp300_800 38
+#define _ColorBpp300_1600 39
+#define _ColorBpp600 40
+#define _ColorBpp600_800 41
+#define _ColorBpp600_1600 42
+#define _ColorBpp600_3200 43
+#define _ColorSpp60 _ColorBpp60
+#define _ColorSpp100 _ColorBpp100
+#define _ColorSpp150 _ColorBpp150
+#define _ColorSpp150_800 _ColorBpp150_800
+#define _ColorSpp300 44
+#define _ColorSpp300_500 45
+#define _ColorSpp300_1000 46
+#define _ColorSpp300_2000 47
+#define _ColorSpp600 48
+#define _ColorSpp600_500 49
+#define _ColorSpp600_1000 50
+#define _ColorSpp600_2000 51
+#define _ColorSpp600_4000 52
+
+#define _NegativeSpp150 53
+#define _NegativeSpp300 54
+#define _NegativeSpp600 55
+#define _NegativeBpp150 _NegativeSpp150
+#define _NegativeBpp300 _NegativeSpp300
+#define _NegativeBpp600 _NegativeSpp600
+#define _NegativeEpp150 _NegativeSpp150
+#define _NegativeEpp300 _NegativeSpp300
+#define _NegativeEpp600 _NegativeSpp600
+
+#define _BwEpp75 56
+#define _BwBpp75 56
+#define _BwSpp75 56
+#define _GrayEpp75 56
+#define _GrayBpp75 56
+#define _GraySpp75 56
+
+#define _TransparencySpp150 _NegativeSpp150
+#define _TransparencySpp300 _NegativeSpp300
+#define _TransparencySpp600 _NegativeSpp600
+#define _TransparencyBpp150 _NegativeSpp150
+#define _TransparencyBpp300 _NegativeSpp300
+#define _TransparencyBpp600 _NegativeSpp600
+#define _TransparencyEpp150 _NegativeSpp150
+#define _TransparencyEpp300 _NegativeSpp300
+#define _TransparencyEpp600 _NegativeSpp600
+
+/* for 48 bits color */
+#define _ColorEpp100_1400 63
+#define _ColorEpp150_1900 64
+#define _ColorEpp300_4000 65
+#define _ColorEpp600_9600 66
+#define _ColorSpp300_3000 67
+
+/*
+ * for mirroring parts of the 98001/3 asic register set
+ */
+typedef struct {
+ Byte RD_Motor1Control; /* 0x0b */
+ Byte RD_StepControl; /* 0x14 */
+ Byte RD_Motor0Control; /* 0x15 */
+ Byte RD_XStepTime; /* 0x16 */
+ Byte RD_ModeControl; /* 0x1b */
+ Byte RD_LineControl; /* 0x1c */
+ Byte RD_ScanControl; /* 0x1d, init = 5 */
+ Byte RD_ModelControl; /* 0x1f */
+ Byte RD_Model1Control; /* 0x20 */
+ UShort RD_Dpi; /* 0x21 */
+ UShort RD_Origin; /* 0x23 */
+ UShort RD_Pixels; /* 0x25 */
+ UShort RD_ThresholdControl; /* 0x27 */
+ Byte RD_ThresholdGapCtrl; /* 0x29 */
+ UShort RD_RedDarkOff; /* 0x33 */
+ UShort RD_GreenDarkOff; /* 0x35 */
+ UShort RD_BlueDarkOff; /* 0x37 */
+
+ ULong RD_BufFullSize; /* 0x54, ASIC 98003 */
+ UShort RD_MotorTotalSteps; /* 0x57, ASIC 98003 */
+
+ Byte RD_ScanControl1; /* 0x5b, ASIC 98003 */
+ Byte RD_MotorDriverType; /* 0x64, ASIC 98003 */
+ Byte RD_ExtLineControl; /* 0x6d, ASIC 98003 */
+ Byte RD_ExtXStepTime; /* 0x6e, ASIC 98003 */
+
+} RegData, *pRegData;
+
+/*
+ * for registers, that only exist on the 96001/3
+ */
+typedef struct
+{
+ Byte RD_MotorControl; /* 0x1b, init = 3 */
+ Byte RD_MemAccessControl; /* 0x1d */
+ Byte RD_WatchDogControl; /* 0x25, init = 0x8f */
+
+ union {
+ Byte RD_ModelControl2; /* 0x26, P96003 */
+ } u26;
+
+ union {
+ Byte RD_RedChShadingOff; /* 0x28, P96003 */
+ } u28;
+ union {
+ Byte RD_GreenChShadingOff; /* 0x29, P96003 */
+ } u29;
+ Byte RD_BlueChShadingOff; /* 0x2a, P96003 */
+ Byte RD_RedChDarkOff; /* 0x2b, P96003 */
+ Byte RD_GreenChDarkOff; /* 0x2c, P96003 */
+ Byte RD_BlueChDarkOff; /* 0x2d, P96003 */
+
+ Byte RD_RedChEvenOff; /* 0x31, P96003 */
+ Byte RD_GreenChEvenOff; /* 0x32, P96003 */
+ Byte RD_BlueChEvenOff; /* 0x33, P96003 */
+ Byte RD_RedChOddOff; /* 0x34, P96003 */
+ Byte RD_GreenChOddOff; /* 0x35, P96003 */
+ Byte RD_BlueChOddOff; /* 0x36, P96003 */
+ Byte RD_RedGainOut; /* 0x37, P96003 */
+ Byte RD_GreenGainOut; /* 0x38, P96003 */
+ Byte RD_BlueGainOut; /* 0x39, P96003 */
+ Byte RD_LedControl; /* 0x3a, P96003 */
+ Byte RD_ShadingCorrectCtrl; /* 0x3b, P96003 */
+
+} Reg96, *pReg96;
+
+/*
+ * model override defines
+ */
+#define _OVR_NONE 0
+#define _OVR_PLUSTEK_9630PL 1 /* for legal version of the OP9630 */
+#define _OVR_PRIMAX_4800D 2 /* for the Primax 4800 Direct */
+#define _OVR_PLUSTEK_9636 3 /* for 9636T/P+/Turbo */
+#define _OVR_PLUSTEK_9636P 4 /* for 9636P */
+#define _OVR_PLUSTEK_A3I 5 /* for A3I */
+#define _OVR_PLUSTEK_4800P 6 /* for OpticPro4800 */
+#define _OVR_PRIMAX_4800D30 7 /* for the Primax 4800 Direct 30bit */
+
+/* CHECK: THIS has to be changed and merged with the old code */
+
+/*
+ * structure to hold IO port specific stuff
+ */
+typedef struct {
+
+#ifdef __KERNEL__
+ pFnOut fnOut;
+ pFnIn fnIn;
+
+ UShort pbSppDataPort;
+ UShort pbEppDataPort;
+
+ UShort pbStatusPort;
+ UShort pbControlPort;
+ UShort pbAddrOffsetPort;
+#endif
+
+ UShort portBase;
+ UShort portMode;
+ UShort lastPortMode;
+
+ Byte bOldControlValue;
+ Byte bOldDataValue;
+ Byte bOpenCount;
+ Byte delay; /* to allow various delay on port operations */
+
+ Bool slowIO;
+ UShort forceMode;
+ Bool useEPPCmdMode;
+
+} IODef, *pIODef;
+
+
+/*
+ * structure to hold device specific stuff
+ */
+typedef struct
+{
+#if 0
+ PFNVOID pfnIOWrite;
+ PFNVOID pfnCCDInit;
+ DWORD dwLampOnTicks;
+ DWORD dwLampOnBegin;
+ LONG lLeftPositive;
+ LONG lLeftNegative;
+ UCHAR bLampOnMinutes;
+ DRVBOOL fButton;
+ DRVBOOL fSoftReset;
+ UCHAR bStatusLamp;
+#endif
+
+ pFnReadData ReadData; /* read function, portmode specific */
+
+ ULong dwModelOriginY;
+ Bool f0_8_16;
+ Bool fTpa; /* transparency adapter ? */
+ Bool f2003; /* has 2003 motor driver ? */
+
+ UChar bMotorID; /* the type of the motor drivers */
+ UChar bPCBID; /* which version of the PCB */
+
+ UChar bCCDID; /* what CCD do we have */
+ pRegDef pCCDRegisters; /* pointer to the register descr */
+ UShort wNumCCDRegs; /* number of values to write */
+ UShort DataOriginX;
+
+ UChar bDACType; /* what DAC do we have */
+ pRegDef pDACRegisters; /* pointer to DAC reg descr. */
+ UShort wNumDACRegs; /* number of values to write */
+ pFnDACOffs fnDarkOffset; /* func-ptr to func for DAC */
+ pFnDACDark fnDACDark; /* adjustments */
+
+ RGBByteDef RegDACOffset;
+ RGBByteDef RegDACGain;
+
+ UChar buttons; /* Number of buttons */
+
+ UChar ModelCtrl; /* contents of the model control reg*/
+ UChar Model1Mono; /* for model control 1 in mono mode */
+ UChar Model1Color; /* for model control 1 in color mode*/
+
+ UChar XStepMono;
+ UChar XStepColor;
+ UChar XStepBack;
+
+ long lUpNormal; /* device specific offsets */
+ long lUpPositive;
+ long lUpNegative;
+ long lLeftNormal;
+
+} DeviceDef, *pDeviceDef;
+
+/*
+ * to hold all the shading stuff for calibrating a scanner
+ */
+typedef struct
+{
+ pRGBUShortDef pHilight;
+ ColorByte Hilight;
+ ULong dwDiv;
+ UShort wExposure;
+ UShort wXStep;
+
+ UChar skipHilight;
+ UChar skipShadow;
+ ULong shadingBytes;
+
+ pDACTblDef pCcdDac;
+ ColorByte DarkDAC;
+ ColorWord DarkOffset;
+ UShort wDarkLevels;
+ UChar bIntermediate;
+
+ ColorByte Gain;
+ UChar bGainDouble;
+ UChar bUniGain;
+ Byte bMinGain;
+ Byte bMaxGain;
+ Byte bGainHigh;
+ Byte bGainLow;
+
+ Bool fStop;
+
+} ShadingDef, *pShadingDef;
+
+
+/*
+ * structure to hold scan-specific data
+ */
+typedef struct _SCANDEF
+{
+#if 0
+ DWORD dwLinesRead;
+ LONG lBufAdjust;
+ DRVBOOL fBackmove;
+ DRVBOOL fEsc;
+ UCHAR Reserved;
+#endif
+
+ pFnBool DoSample;
+ pFnDataProcess DataProcess; /* to convert RGB buffers to RGB pixels */
+ pFnBool DataRead; /* function to get data from scanner */
+
+ ULong dwLinesToRead; /* number of images lines to read */
+ Long lBufferAdjust;
+
+ ULong dwScanOrigin; /* where to start the scan */
+ Bool fRefreshState; /* refresh ? */
+ Bool fMotorBackward;
+ UChar motorPower; /* how to drive the motor */
+
+ ULong dwMinReadFifo;
+ ULong dwMaxReadFifo;
+ Byte bFifoSelect; /* defines which FIFO to use */
+
+ UChar bDiscardAll;
+ ULong dwInterval;
+ ULong dwInterlace; /* CHECK: here always 0 - remove ? */
+
+ union {
+ UShort wGreenDiscard;
+ UShort wGreenKeep;
+ } gd_gk;
+ union {
+ UShort wBlueDiscard;
+ UShort wRedKeep;
+ } bd_rk;
+
+ UChar bRefresh; /* for controlling the movement */
+ UChar bOldScanState;
+ UChar bNowScanState;
+ UChar bModuleState;
+
+ DataPointer p48BitBuf; /* for handling 48-bit data */
+ union {
+ pUChar pMonoBuf;
+ DataPointer ColorBuf;
+ } bp;
+
+ RBGPtrDef BufBegin; /* for reading/writing the scan-data */
+ RBGPtrDef BufEnd;
+ RBGPtrDef BufGet;
+ RBGPtrDef BufData;
+ RBGPtrDef BufPut;
+
+ ULong negBegin; /* used while scanning in TPA modes */
+ ULong posBegin;
+
+ ULong dpiIdx; /* index to get/set values in the table */
+ pExtXStepDef negScan; /* reference to exposure/xtep table */
+
+} ScanDef, *pScanDef;
+
+/*
+ * structure to hold buffer pointers
+ */
+typedef struct {
+
+ union {
+ pUChar pReadBuf;
+ pUChar pShadingMap;
+ pUChar pRWTestBuffer;
+ pUShort pShadingRam;
+ DataPointer Buf;
+ } b1;
+
+ union {
+ pUChar pSumBuf;
+ pRGBUShortDef pSumRGB;
+ pUChar pRWTestBuffer1;
+ } b2;
+ DataPointer TpaBuf;
+
+} BufferDef, *pBufferDef;
+
+
+/* Register RegBitDepth (Addr: 0x13) - ASIC 9800x */
+#define _BIT0_7 0x00
+#define _BIT8_15 0x01
+#define _BIT16_20 0x02
+
+/* Register RegStepControl (Addr: 0x14) - ASIC 9800x */
+#define _MOTOR0_ONESTEP 0x01
+#define _MOTOR0_SCANSTATE 0x02
+#define _MOTOR_FREERUN 0x40
+#define _MOTOR_NOFREERUN 0x00
+
+/* Register RegGetScanState (Addr: 0x17, 0x12 on 9600x) - ASIC 9800x */
+#define _SCANSTATE_MASK 0x3f /* bit 0-5 */
+#define _SCANSTATE_STOP 0x80
+
+/* Register RegReadIOBufBus (Addr: 0x17) - ASIC 9600x only */
+#define _IOBUF_BUSMASK 0x0f /* bit 0-3 */
+
+/* Register RegMemoryLow/High (Addr: 0x19/0x1a) - ASIC 9800x */
+#define _MAP_ADDR_RED 0x00
+#define _MAP_ADDR_GREEN 0x40
+#define _MAP_ADDR_BLUE 0x80
+#define _MAP_ADDR_SIZE 0x40 /* 0x4000 */
+
+/* Register RegModeControl (Addr: 0x1b, 0x18 on 9600x) - all ASICs*/
+#define _ModeScan 0x00 /* all ASICs */
+#define _ModeProgram 0x01 /* 9600x def */
+#define _ModeIdle 0x01 /* 9800x defs */
+#define _ModeShadingMem 0x02
+#define _ModeMappingMem 0x03
+#define _ModeReadMappingMem 0x07
+#define _ModeFifoRSel 0x00
+#define _ModeFifoGSel 0x08
+#define _ModeFifoBSel 0x10
+#define _ModeFifoClose 0x18
+
+/* Register RegLineControl (Addr: 0x1c, 0x19 on 9600x) - all ASICs*/
+#define _LINE_SCANTIME_MASK 0x3f /* bit 0-6 */
+#define _LINE_CDSINVERSE 0x80 /* Color Drive Signal */
+
+/* Register RegScanControl (Addr: 0x1d) - all ASICs*/
+#define _SCAN_BITMODE 0x00
+#define _SCAN_BYTEMODE 0x01 /* Gray/Color mode */
+#define _SCAN_1ST_AVERAGE 0x04 /* first pixel is averaged pixel */
+#define _SCAN_BITDIRR2L 0x08 /* bit shift from right to left */
+
+/* ASIC 9600x section */
+#define _P96_SCANDATA_INVERT 0x02
+#define _SCAN_LAMP_ON 0x10
+
+/* ASIC 9800x section */
+#define _SCAN_12BITMODE 0x02
+#define _SCAN_NORMALLAMP_ON 0x10 /* normal Lamp */
+#define _SCAN_TPALAMP_ON 0x20
+#define _P98_SCANDATA_INVERT 0x40
+#define _BITALIGN_LEFT 0x80
+
+#define _SCAN_LAMPS_ON (_SCAN_NORMALLAMP_ON | _SCAN_TPALAMP_ON)
+
+/* Register RegMotor0Control (Addr: 0x15) */
+#define _MotorDirForward 0x01
+#define _MotorDirBackward 0x00
+#define _MotorOn 0x02
+#define _MotorHFullStepH 0x00
+#define _MotorHHalfStep 0x04
+#define _MotorHQuarterStep 0x08
+#define _MotorHEightStep 0x08 /* for 2916 driver */
+#define _MotorPowerEnable 0x40 /* ASIC 98003 specific */
+#define _MotorHHomeStop 0x80
+
+#define _FORWARD_MOTOR (_MotorDirForward + _MotorOn + \
+ _MotorHQuarterStep + _MotorPowerEnable)
+
+/* Register RegConfiguration (Addr: 0x1e), ASIC 9800x */
+#define _P98_CCD_TYPE_ID 0x07
+#define _P98_NEC_MACHINE 0x08
+#define _P98_PCBID 0xF0
+
+#define _CCD_3797 0
+#define _CCD_3717 1
+#define _CCD_3799 1 /* new for 98003 */
+#define _CCD_535 2
+#define _CCD_2556 3
+#define _CCD_518 4
+#define _CCD_539 5 /* default for 98001 */
+#define _CCD_3777 6 /* new for 98003 */
+#define _CCD_548 7 /* new for 98003 */
+
+/* ASIC 98003 section */
+#define _OPTICWORKS2000 0x00
+#define _PLUSTEK_SCANNER 0x10
+#define _SCANNER_WITH_TPA 0x20
+#define _SCANNER4Button 0x30
+#define _SCANNER4ButtonTPA 0x40
+#define _SCANNER5Button 0x50
+#define _SCANNER5ButtonTPA 0x60
+#define _SCANNER1Button 0x70
+#define _SCANNER1ButtonTPA 0x80
+#define _SCANNER2Button 0x90
+#define _AGFA_SCANNER 0xf0
+#define _AGFA_PCB 0x1f
+
+/* Register RegModelControl (Addr: 0x1f), all ASICs */
+#define _ModelSampleAndHold 0x01 /* sample and hold */
+#define _ModelWhiteIs0 0x02 /* white is 0 */
+#define _ModelInvertPF 0x04 /* invert paper flag */
+#define _ModelDpi200 0 /* (400 / 2) */
+#define _ModelDpi300 0x08 /* (600 / 2) */
+#define _ModelDpi400 0x10 /* (800 / 2) */
+#define _ModelDpi600 0x18 /* (1200 / 2) */
+#define _ModelMemSize32k3 0 /* 32k for 300 dpi color */
+#define _ModelMemSize64k3 0x20 /* 64k for 300 dpi color */
+#define _ModelMemSize128k3 0x40 /* 128k for 300 dpi color */
+#define _ModelMemSize64k4 0x60 /* 64k for 400/600 dpi color */
+#define _ModelMemSize128k4 0x80 /* 128k for 400/600 dpi color */
+
+#define _ModelMemSize8k 0 /* for 96001 */
+#define _ModelMemSize8k3 0x20
+#define _ModelMemSize32k96001 0x40
+#define _ModelMemSize128k396001 0x60
+#define _ModelMemSize128k696001 0x80
+
+#define _HOME_SENSOR_POLARITY 0x01 /* 9800x */
+#define _LED_CONTROL 0x02
+#define _LED_ACTIVITY 0x04
+#define _MODEL_DPI800 0x20
+#define _MODEL_DPI1200 0x28
+#define _DUMMY_12BIT 0x40
+
+/* Register RegModel1Control (Addr: 0x20), 9800x */
+#define _SCAN_GRAYTYPE 0x01
+#define _CCD_SHIFT_GATE 0x02
+#define _CCD_SHIFT_PULSE 0x04
+#define _BUTTON_MODE 0x08
+#define _MOTOR_2003 0x00
+#define _MOTOR_2916 0x10
+#define _MOTOR_7042 0x20
+
+/* Register RegThresholdGapControl (Addr: 0x29, 0x27 on 9600x ) - all ASICs */
+#define _THRESHOLDGAP_MASK 0x0f
+
+/* Register RegResetConfig (Addr: 0x2e) */
+#define _ADC_MASK 0x07
+#define _DA_WOLFSON8143 0x00
+#define _DA_ESIC 0x04
+#define _DA_SAMSUNG8531 0x05
+#define _DA_WOLFSON8141 0x06
+#define _DA_SAMSUNG1224 0x07
+#define _MOTOR0_MASK 0x18
+#define _MOTOR0_2003 0x00
+#define _MOTOR0_2916 0x08
+#define _MOTOR0_7042 0x10
+#define _MOTOR1_MASK 0x60
+#define _MOTOR1_2003 0x00
+#define _MOTOR1_2916 0x20
+#define _MOTOR1_7042 0x40
+
+/* Register RegFifoFullLength (Addr: 0x54) */
+#define _RED_FULLSIZE 0x00
+#define _GREEN_FULLSIZE 0x08
+#define _BLUE_FULLSIZE 0x10
+
+/* Register RegScanControl1 (Addr: 0x5b) */
+#define _MTSC_ENABLE 0x01
+#define _SCANSTOPONBUFFULL 0x02
+#define _MFRC_RUNSCANSTATE 0x04
+#define _MFRC_BY_XSTEP 0x08
+
+/* Register RegMotorDriverType (Addr: 0x64) */
+#define _MOTORS_MASK 0x33
+#define _MOTORR_MASK 0xf3
+#define _MOTORR_WEAK 0x04
+#define _MOTORR_MEDIUM 0x08
+#define _MOTORR_STRONG 0x0c
+#define _MOTORT_WEAK 0x40
+#define _MOTORT_MEDIUM 0x80
+#define _MOTORT_STRONG 0xc0
+#define _BUTTON_SELECT1 0x40
+#define _BUTTON_SELECT2 0x80
+#define _BUTTON_DISABLE 0xc0
+
+/* Register RegStatus2 (Addr: 0x66) */
+#define _REFLECTIONLAMP_ON 0x01
+#define _TPALAMP_ON 0x02
+#define _STILL_FREE_RUNNING 0x04
+#define _BUFFER_IS_FULL 0x08
+
+/* Register RegTestMode (Addr: 0xf0) */
+#define _SW_TESTMODE 0x20
+
+/* other stuff... */
+
+/* CHECK: changes this stuff... */
+#define _BytesPerChannel 5500UL
+#define _SizeDataBuf (ULong)(_BytesPerChannel * 3 * 2)
+#define _SizeTpaDataBuf (ULong)(_BytesPerChannel * 3 * 2)
+#define _SizeShadingSumBuf (ULong)(_BytesPerChannel * 3 * 4)
+#define _SizeTotalBuf (ULong)(_SizeDataBuf + _SizeShadingSumBuf)
+#define _SizeTotalBufTpa (ULong)(_SizeTotalBuf + _SizeTpaDataBuf)
+
+/* for DAC programming (ASIC 98003)*/
+#define _VALUE_CONFIG 0x51
+#define _DAC_RED (Byte)(_VALUE_CONFIG | 0x00)
+#define _DAC_GREENCOLOR (Byte)(_VALUE_CONFIG | 0x04)
+#define _DAC_GREENMONO (Byte)(_VALUE_CONFIG | 0x06)
+#define _DAC_BLUE (Byte)(_VALUE_CONFIG | 0x08)
+
+/* internal FIFO buffers (ASIC 9800x) */
+#define _SIZE_REDFIFO 196608UL /* 192k */
+#define _SIZE_GREENFIFO 147456UL /* 144k */
+#define _SIZE_BLUEFIFO 114688UL /* 112k */
+
+#define _SIZE_COLORFIFO _SIZE_BLUEFIFO
+#define _SIZE_GRAYFIFO (_SIZE_REDFIFO + _SIZE_GREENFIFO + _SIZE_BLUEFIFO)
+
+/* Scan State Definitions */
+#define _SS_STEP 0x08
+#define _SS_RED 0x04
+#define _SS_GREEN 0x02
+#define _SS_BLUE 0x01
+
+#define _SS_MONO _SS_GREEN
+#define _SS_COLOR (_SS_RED | _SS_GREEN | _SS_BLUE)
+
+/* for shading */
+#define _CHANNEL_RED 0
+#define _CHANNEL_GREEN 1
+#define _CHANNEL_BLUE 2
+
+#endif /* guard __HWDEFS_H__ */
+
+/* END PLUSTEK-PP_HWDEFS.H ..................................................*/
diff --git a/backend/plustek-pp_image.c b/backend/plustek-pp_image.c
new file mode 100644
index 0000000..c1ef743
--- /dev/null
+++ b/backend/plustek-pp_image.c
@@ -0,0 +1,1652 @@
+/* @file plustek-pp_image.c
+ * @brief functions to convert scanner data into image data
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - no changes
+ * - 0.33 - no changes
+ * - 0.34 - reactivated code in imageP96WaitLineData() to recover from
+ * loosing data
+ * - 0.35 - no changes
+ * - 0.36 - removed comment
+ * - added wDither exchange to imageP9xSetupScanSettings
+ * - added fnHalftoneDirect1 which provides dithering by using random
+ * thresholds
+ * - removed the swapping behaviour for model OP_600 in
+ * fnP96ColorDirect() according to the Primax 4800 Direct tests
+ * - changes, due to define renaming
+ * - removed _ASIC_96001 specific stuff to invert colors
+ * - 0.37 - removed // comments
+ * - corrected output of 12bit/pixel
+ * - 0.38 - added P12 stuff
+ * - renamed WaitLineData functions to ReadOneImageLine
+ * - 0.39 - fixed a problem in imageP98003ReadOneImageLine, that causes
+ * these I/O timeouts...
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - fixed a problem for the 12bit modes fo ASIC9800x based devices
+ * - changed include names
+ * - 0.43 - removed floating point stuff
+ * - cleanup
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/************************ local definitions **********************************/
+
+#define _LINE_TIMEOUT (_SECOND * 5 ) /* max 5 second per line ! */
+
+/*************************** local vars **************************************/
+
+static UShort wPreviewScanned = 0;
+
+static ExpXStepDef posScan[5] = {{128, 8}, {96, 12},
+ {96, 24}, {96, 48}, {96, 96}};
+static ExpXStepDef negScan[5] = {{128, 8}, {96, 12},
+ {96, 24}, {96, 48}, {96, 96}};
+
+static ExpXStepDef nmlScan[4][5] = {
+ {{160, 10}, {96, 12}, {96, 24}, {96, 48}, {96, 96}}, /* EPP */
+ {{160, 10}, {128, 16}, {128, 32}, {192, 96}, {192, 96}}, /* SPP */
+ {{160, 10}, {96, 12}, {96, 24}, {160, 80}, {160, 160}}, /* BPP */
+ {{160, 10}, {96, 12}, {96, 24}, {96, 48}, {96, 96}} /* ECP */
+};
+
+static ThreshDef xferSpeed[4] = {
+ {0, 3200, 2500}, {0, 1200, 800}, {0, 800, 1250}, {0, 3200, 2500}
+};
+
+/*************************** local functions *********************************/
+
+/** return the correct DPI-value
+ * The ASIC 96001/3 models are limited to an optical resolution of 300 Dpi
+ * so it´s necessary to scale in X and Y direction (see scale.c)!
+ */
+static UShort imageGetPhysDPI( pScanData ps, pImgDef pImgInf, Bool fDpiX )
+{
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ if (fDpiX) {
+
+ if (pImgInf->xyDpi.x > ps->LensInf.rDpiX.wPhyMax)
+ return ps->LensInf.rDpiX.wPhyMax;
+ else
+ return pImgInf->xyDpi.x;
+
+ } else {
+ if (pImgInf->xyDpi.y > ps->LensInf.rDpiY.wPhyMax)
+ return ps->LensInf.rDpiY.wPhyMax;
+ else
+ return pImgInf->xyDpi.y;
+ }
+ } else {
+
+ if (fDpiX) {
+
+ if (pImgInf->wDataType >= COLOR_TRUE24) {
+ if (pImgInf->xyDpi.x > ps->LensInf.rDpiX.wPhyMax)
+ return ps->LensInf.rDpiX.wPhyMax;
+ else
+ return pImgInf->xyDpi.x;
+ } else {
+ if (pImgInf->xyDpi.x > (ps->LensInf.rDpiX.wPhyMax * 2))
+ return (ps->LensInf.rDpiX.wPhyMax * 2);
+ else
+ return pImgInf->xyDpi.x;
+ }
+ } else {
+
+ if (pImgInf->wDataType >= COLOR_TRUE24 ) {
+ if (pImgInf->xyDpi.y > (ps->LensInf.rDpiY.wPhyMax / 2))
+ return (ps->LensInf.rDpiY.wPhyMax / 2);
+ else
+ return pImgInf->xyDpi.y;
+ } else {
+ if (pImgInf->xyDpi.y > ps->LensInf.rDpiY.wPhyMax)
+ return ps->LensInf.rDpiY.wPhyMax;
+ else
+ return pImgInf->xyDpi.y;
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ * Sampling stuff for ASIC 98003 *
+ *****************************************************************************/
+
+static Bool fnEveryLines( pScanData ps )
+{
+ _VAR_NOT_USED( ps );
+ return _TRUE;
+}
+
+static Bool fnSampleLines( pScanData ps )
+{
+ ps->DataInf.wYSum += ps->DataInf.xyAppDpi.y;
+
+ if( ps->DataInf.wYSum >= ps->DataInf.xyPhyDpi.y ) {
+
+ ps->DataInf.wYSum -= ps->DataInf.xyPhyDpi.y;
+ return _TRUE;
+ }
+
+ return _FALSE;
+}
+
+static Bool fnSamplePreview( pScanData ps )
+{
+ ps->DataInf.wYSum += wPreviewScanned;
+ if( ps->DataInf.wYSum >= 150 ) {
+
+ ps->DataInf.wYSum -= 150;
+ return _TRUE;
+ }
+
+ return _FALSE;
+}
+
+/*****************************************************************************
+ * Data Processing Routines *
+ *****************************************************************************/
+
+static Bool fnReadToDriver( pScanData ps )
+{
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+ IOReadScannerImageData( ps, ps->Scan.BufPut.blue.bp,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, ps->Scan.BufPut.green.bp,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ if( ps->Scan.gd_gk.wGreenKeep )
+ ps->Scan.gd_gk.wGreenKeep--;
+ else {
+ ps->Scan.BufPut.green.bp += ps->DataInf.dwAsicBytesPerPlane;
+
+ if( ps->Scan.BufPut.green.bp >= ps->Scan.BufEnd.green.bp )
+ ps->Scan.BufPut.green.bp = ps->Scan.BufBegin.green.bp;
+ }
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoRSel;
+ IOReadScannerImageData( ps, ps->Scan.BufPut.red.bp,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ ps->Scan.BufPut.red.bp += ps->DataInf.dwAsicBytesPerPlane;
+ if( ps->Scan.BufPut.red.bp >= ps->Scan.BufEnd.red.bp )
+ ps->Scan.BufPut.red.bp = ps->Scan.BufBegin.red.bp;
+
+ if( ps->Scan.bd_rk.wRedKeep ) {
+ ps->Scan.bd_rk.wRedKeep--;
+ return _FALSE;
+
+ } else {
+
+ ps->Scan.BufData.green.bp = ps->Scan.BufGet.green.bp;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_ColorBGROrder ) {
+ ps->Scan.BufData.red.bp = ps->Scan.BufGet.blue.bp;
+ ps->Scan.BufData.blue.bp = ps->Scan.BufGet.red.bp;
+ } else {
+ ps->Scan.BufData.red.bp = ps->Scan.BufGet.red.bp;
+ ps->Scan.BufData.blue.bp = ps->Scan.BufGet.blue.bp;
+ }
+
+ ps->Scan.BufGet.red.bp += ps->DataInf.dwAsicBytesPerPlane;
+ ps->Scan.BufGet.green.bp += ps->DataInf.dwAsicBytesPerPlane;
+
+ if( ps->Scan.BufGet.red.bp >= ps->Scan.BufEnd.red.bp )
+ ps->Scan.BufGet.red.bp = ps->Scan.BufBegin.red.bp;
+
+ if( ps->Scan.BufGet.green.bp >= ps->Scan.BufEnd.green.bp )
+ ps->Scan.BufGet.green.bp = ps->Scan.BufBegin.green.bp;
+
+ return _TRUE;
+ }
+}
+
+static Bool fnReadOutScanner( pScanData ps )
+{
+ if( ps->Scan.bd_rk.wBlueDiscard ) {
+
+ ps->Scan.bd_rk.wBlueDiscard--;
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+
+ IOReadScannerImageData( ps, ps->Bufs.b1.pReadBuf,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ if( ps->Scan.gd_gk.wGreenDiscard ) {
+ ps->Scan.gd_gk.wGreenDiscard--;
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, ps->Bufs.b1.pReadBuf,
+ ps->DataInf.dwAsicBytesPerPlane);
+ }
+ return _FALSE;
+
+ } else {
+ IOReadColorData( ps, ps->Bufs.b1.pReadBuf,
+ ps->DataInf.dwAsicBytesPerPlane );
+ return _TRUE;
+ }
+}
+
+/** Interpolates the gray data by using averaged the continuous pixels
+ */
+static void fnP96GrayDirect( pScanData ps, pVoid pBuf, pVoid pImg, ULong bl )
+{
+ pUChar src, dest;
+
+ src = (pUChar)pImg;
+ dest = (pUChar)pBuf;
+
+ for (; bl; bl--, src++, dest++ )
+ *dest = ps->pbMapRed [*src];
+}
+
+/** This routine used in the condition:
+ * 1) The data type is B/W or GrayScale.
+ * 2) The required horizontal resolution doesn't exceed the optic spec.
+ * 3) The required vertical resolution exceeds the optic spec.
+ * So, the vertcal lines have to average with previous line to smooth the
+ * image.
+ */
+static void fnDataDirect( pScanData ps, pVoid pBuf, pVoid pImg, ULong bl )
+{
+ _VAR_NOT_USED( ps );
+ memcpy( pBuf, pImg, bl );
+}
+
+/** According to dither matrix to convert the input gray scale data into
+ * one-bit data.
+ */
+static void fnHalftoneDirect0( pScanData ps, pVoid pb, pVoid pImg, ULong bL )
+{
+ pUChar pDither, src, dest;
+ ULong dw;
+
+ src = (pUChar)pImg;
+ dest = (pUChar)pb;
+
+ pDither = &ps->a_bDitherPattern[ps->dwDitherIndex];
+
+ for( ; bL; bL--, dest++, pDither -= 8 ) {
+
+ for( dw = 8; dw; dw--, src++, pDither++ ) {
+
+ if( *src < *pDither ) {
+ *dest = (*dest << 1) | 0x01;
+ } else {
+ *dest <<= 1;
+ }
+ }
+ }
+ ps->dwDitherIndex = (ps->dwDitherIndex + 8) & 0x3f;
+}
+
+/** use random generator to make halftoning
+ */
+static void fnHalftoneDirect1( pScanData ps, pVoid pb, pVoid pImg, ULong bL )
+{
+ pUChar src, dest;
+ UChar threshold;
+ ULong dw;
+
+ _VAR_NOT_USED( ps );
+ src = (pUChar)pImg;
+ dest = (pUChar)pb;
+
+ for (; bL; bL--, dest++ ) {
+
+ for (dw = 8; dw; dw--, src++ ) {
+
+ threshold = (UChar)MiscLongRand();
+
+ if (*src < threshold ) {
+ *dest = (*dest << 1) | 0x01;
+ } else {
+ *dest <<= 1;
+ }
+ }
+ }
+}
+
+/** Merges the color planes to pixels style without enlarge operation.
+ */
+static void fnP98ColorDirect( pScanData ps, pVoid pb, pVoid pImg, ULong bL )
+{
+ pUChar src;
+ pRGBByteDef dest;
+
+ src = (pUChar)pImg;
+ dest = (pRGBByteDef)pb;
+
+ for ( bL = ps->DataInf.dwAsicPixelsPerPlane; bL; bL--, src++, dest++) {
+
+ dest->Red = *src;
+ dest->Green = src[ps->DataInf.dwAsicPixelsPerPlane];
+ dest->Blue = src[ps->DataInf.dwAsicPixelsPerPlane*2];
+ }
+}
+
+static void fnP96ColorDirect( pScanData ps, pVoid pb, pVoid pImg, ULong bL )
+{
+ pUChar src;
+ pRGBByteDef dest;
+
+ src = (pUChar)pImg;
+ dest = (pRGBByteDef)pb;
+
+ for ( bL = ps->DataInf.dwAsicPixelsPerPlane; bL; bL--, dest++, src++) {
+
+ dest->Red =ps->pbMapRed[*src];
+ dest->Green=ps->pbMapGreen[src[ps->DataInf.dwAsicPixelsPerPlane]];
+ dest->Blue =ps->pbMapBlue[src[ps->DataInf.dwAsicPixelsPerPlane*2]];
+ }
+}
+
+/** Merges the color planes to pixels style without enlarge operation.
+ * The scanner returns the pixel data in Motorola-Format, so we have to swap
+ */
+static void fnP98Color48( pScanData ps, pVoid pb, pVoid pImg, ULong bL )
+{
+ pUShort src;
+ pRGBUShortDef dest;
+
+ register ULong i;
+
+ _VAR_NOT_USED( bL );
+ src = (pUShort)pImg;
+ dest = (pRGBUShortDef)pb;
+
+ for ( i = ps->DataInf.dwAsicPixelsPerPlane; i; i--, src++, dest++) {
+
+ dest->Red = *src;
+ dest->Green = src[ps->DataInf.dwAsicPixelsPerPlane];
+ dest->Blue = src[ps->DataInf.dwAsicPixelsPerPlane * 2];
+ }
+}
+
+/** prepare for scanning
+ */
+static int imageP98SetupScanSettings( pScanData ps, pScanInfo pInf )
+{
+ UShort brightness;
+
+ DBG( DBG_LOW, "imageP98SetupScanSettings()\n" );
+
+ ps->DataInf.dwScanFlag = pInf->ImgDef.dwFlag;
+ ps->DataInf.dwVxdFlag = 0;
+ ps->DataInf.crImage = pInf->ImgDef.crArea;
+
+ /* AdjustOriginXByLens
+ * [NOTE]
+ * Here we just simply adjust it to double (600 DPI is two times of
+ * 300 DPI), but if this model is a multi-lens scanner, we should adjust
+ * it according to different lens.
+ */
+ ps->DataInf.crImage.x <<= 1;
+
+ ps->DataInf.xyAppDpi = pInf->ImgDef.xyDpi;
+ ps->DataInf.siBrightness = pInf->siBrightness;
+ ps->DataInf.wDither = pInf->wDither;
+ ps->DataInf.wAppDataType = pInf->ImgDef.wDataType;
+
+ ps->GetImageInfo( ps, &pInf->ImgDef );
+
+ if (ps->DataInf.dwVxdFlag & _VF_DATATOUSERBUFFER) {
+ ps->Scan.DataProcess = fnDataDirect;
+ }
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle) {
+ ps->Scan.lBufferAdjust = -(Long)ps->DataInf.dwAppBytesPerLine;
+ } else {
+ ps->Scan.lBufferAdjust = (Long)ps->DataInf.dwAppBytesPerLine;
+ }
+
+ DBG( DBG_LOW, "Scan settings:\n" );
+ DBG( DBG_LOW, "ImageInfo: (x=%u,y=%u,dx=%u,dy=%u)\n",
+ ps->DataInf.crImage.x, ps->DataInf.crImage.y,
+ ps->DataInf.crImage.cx, ps->DataInf.crImage.cy );
+
+ /*
+ * SetBwBrightness
+ * [NOTE]
+ *
+ * 0 _DEF_BW_THRESHOLD 255
+ * +-------------------------+--------------------------------+
+ * |<------- Black --------->|<----------- White ------------>|
+ * So, if user wish to make image darker, the threshold value should be
+ * higher than _defBwThreshold, otherwise it should lower than the
+ * _DefBwThreshold.
+ * Darker = _DefBwThreshold + White * Input / 127;
+ * Input < 0, and White = 255 - _DefBwThreshold, so
+ * = _DefBwThreshold - (255 - _DefBwThreshold) * Input / 127;
+ * The brighter is the same idea.
+ *
+ * CHECK: it seems that the brightness only works for the binary mode !
+ */
+ if( ps->DataInf.wPhyDataType != COLOR_BW ) {/* if not line art */
+ ps->wBrightness = pInf->siBrightness; /* use internal tables for */
+ ps->wContrast = pInf->siContrast; /* brightness and contrast */
+
+ pInf->siBrightness = 0; /* don't use asic for threshold */
+ }
+
+/* CHECK: We have now two methods for setting the brightness...
+*/
+ DBG( DBG_LOW, "brightness = %i\n", pInf->siBrightness );
+
+ if (ps->DataInf.siBrightness < 0) {
+ brightness = (UShort)(_DEF_BW_THRESHOLD -
+ (255 - _DEF_BW_THRESHOLD) * ps->DataInf.siBrightness /127);
+ } else {
+ brightness = (UShort)(_DEF_BW_THRESHOLD -
+ _DEF_BW_THRESHOLD * ps->DataInf.siBrightness /127);
+ }
+ ps->AsicReg.RD_ThresholdControl = brightness;
+
+ DBG( DBG_LOW, "1. brightness = %i\n", brightness );
+
+ if( ps->DataInf.siBrightness >= 0 ) {
+ brightness = (short)((long)(-(255 - _DEF_BW_THRESHOLD) *
+ ps->DataInf.siBrightness) / 127 + _DEF_BW_THRESHOLD);
+ } else {
+ brightness = (short)((long)(_DEF_BW_THRESHOLD *
+ ps->DataInf.siBrightness) / 127 + _DEF_BW_THRESHOLD);
+ }
+
+ brightness = (brightness ^ 0xff) & 0xff;
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+ ps->AsicReg.RD_ThresholdControl = brightness;
+ DBG( DBG_LOW, "2. brightness = %i\n", brightness );
+ }
+
+ ps->DataInf.pCurrentBuffer = ps->pScanBuffer1;
+
+ return _OK;
+}
+
+/**
+ */
+static void imageP98DoCopyBuffer( pScanData ps, pUChar pImage )
+{
+ memcpy( ps->pFilterBuf, pImage, ps->DataInf.dwAsicBytesPerPlane );
+
+ ps->pFilterBuf += 5120;
+ if (ps->pFilterBuf >= ps->pEndBuf)
+ ps->pFilterBuf = ps->pProcessingBuf;
+}
+
+/**
+ */
+static Bool imageP98CopyToFilterBuffer( pScanData ps, pUChar pImage )
+{
+ if (ps->fDoFilter) {
+
+ if (ps->fFilterFirstLine) {
+
+ imageP98DoCopyBuffer( ps, pImage );
+ imageP98DoCopyBuffer( ps, pImage );
+ ps->dwLinesFilter--;
+ return _FALSE;
+ } else {
+
+ imageP98DoCopyBuffer( ps, pImage );
+ if ((ps->dwLinesFilter--) == 0)
+ imageP98DoCopyBuffer( ps, pImage);
+ }
+ }
+ return _TRUE;
+}
+
+/**
+ */
+static void imageP98UnSharpCompare( pScanData ps, Byte Center,
+ Byte Neighbour, pLong pdwNewValue )
+{
+ Byte b;
+
+ b = (Center >= Neighbour) ? Center - Neighbour : Neighbour - Center ;
+
+ if (b > ps->bOffsetFilter) {
+
+ *pdwNewValue -= (Long)Neighbour;
+ ps->dwDivFilter--;
+ }
+}
+
+/**
+ */
+static void imageP98DoFilter( pScanData ps, pUChar pPut )
+{
+ ULong dw;
+ Long dwNewValue;
+
+ if (ps->fDoFilter && (ps->DataInf.xyAppDpi.x) >= 600UL) {
+
+ /* DoUnsharpMask(); */
+ for (dw = 0; dw < ps->DataInf.dwAsicBytesPerPlane - 2; dw++, pPut++) {
+
+ ps->dwDivFilter = ps->dwMul;
+
+ dwNewValue = ((ULong)ps->pGet2[dw+1]) * ps->dwMul;
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet1[dw], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet1[dw+1], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet1[dw+2], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet2[dw], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet2[dw+2], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet3[dw], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet3[dw+1], &dwNewValue);
+ imageP98UnSharpCompare( ps, ps->pGet2[dw+1], ps->pGet3[dw+2], &dwNewValue);
+
+ if( dwNewValue > 0 ) {
+ if((dwNewValue /= ps->dwDivFilter) < 255) {
+ *pPut = (Byte) dwNewValue;
+ } else {
+ *pPut = 255;
+ }
+ } else {
+ *pPut = 0;
+ }
+ }
+ pPut = ps->pGet1;
+ ps->pGet1 = ps->pGet2;
+ ps->pGet2 = ps->pGet3;
+ ps->pGet3 = pPut;
+ }
+}
+
+/**
+ */
+static Bool imageP98DataIsReady( pScanData ps )
+{
+ Byte b;
+
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bMoveDataOutFlag = _DataAfterRefreshState;
+
+ b = (ps->DataInf.wPhyDataType >= COLOR_TRUE24) ?
+ _BLUE_DATA_READY : _GREEN_DATA_READY;
+ while( _TRUE ) {
+
+ ps->dwColorRunIndex ++;
+
+ if(ps->pColorRunTable[ps->dwColorRunIndex] & b)
+ break;
+ }
+
+ if (b == _GREEN_DATA_READY) {
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, ps->DataInf.pCurrentBuffer,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ imageP98CopyToFilterBuffer( ps, ps->DataInf.pCurrentBuffer );
+ } else {
+
+ /* ReadColorImageData() */
+ if( ps->DataInf.dwScanFlag & SCANDEF_ColorBGROrder ) {
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoRSel;
+ IOReadScannerImageData( ps, ps->pScanBuffer1 +
+ ps->DataInf.dwAsicBytesPerPlane * 2,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, ps->pScanBuffer1 +
+ ps->DataInf.dwAsicBytesPerPlane,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+ IOReadScannerImageData( ps, ps->pScanBuffer1,
+ ps->DataInf.dwAsicBytesPerPlane );
+ } else {
+
+ IOReadColorData( ps, ps->pScanBuffer1,
+ ps->DataInf.dwAsicBytesPerPlane );
+ }
+ }
+
+ if (ps->fFilterFirstLine) {
+ ps->fFilterFirstLine = _FALSE;
+ return _TRUE;
+ }
+
+ imageP98DoFilter( ps, ps->DataInf.pCurrentBuffer );
+
+ (*ps->Scan.DataProcess)( ps, ps->Scan.bp.pMonoBuf,
+ ps->DataInf.pCurrentBuffer,
+ ps->DataInf.dwAppPhyBytesPerLine );
+
+ return _TRUE;
+}
+
+/** here we wait for one data-line
+ */
+static Bool imageP98001ReadOneImageLine( pScanData ps )
+{
+ ULong dwFifoCounter;
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _LINE_TIMEOUT );
+ do {
+
+ ps->Scan.bNowScanState = IOGetScanState( ps, _FALSE );
+ dwFifoCounter = IOReadFifoLength( ps );
+
+ if (!(ps->Scan.bNowScanState & _SCANSTATE_STOP) &&
+ (dwFifoCounter < ps->dwMaxReadFifoData)) {
+
+ if( ps->Scan.bOldScanState != ps->Scan.bNowScanState )
+ ps->UpdateDataCurrentReadLine( ps );
+
+ if( dwFifoCounter >= ps->Scan.dwMinReadFifo )
+ return imageP98DataIsReady( ps );
+
+ } else { /* ScanStateIsStop */
+
+ if (dwFifoCounter >= ps->dwSizeMustProcess)
+ return imageP98DataIsReady( ps );
+
+ ps->UpdateDataCurrentReadLine( ps );
+
+ if( dwFifoCounter >= ps->Scan.dwMinReadFifo )
+ return imageP98DataIsReady( ps );
+ }
+
+ _DODELAY(10); /* delay 10 ms */
+
+ } while (!MiscCheckTimer( &timer ));
+
+ DBG( DBG_HIGH, "Timeout - Scanner malfunction !!\n" );
+ MotorToHomePosition(ps);
+
+ /* timed out, scanner malfunction */
+ return _FALSE;
+}
+
+/** calculate the image properties according to the scanmode
+ */
+static void imageP98GetInfo( pScanData ps, pImgDef pImgInf )
+{
+ DBG( DBG_LOW, "imageP98GetInfo()\n" );
+
+ ps->DataInf.xyPhyDpi.x = imageGetPhysDPI( ps, pImgInf, _TRUE );
+ ps->DataInf.xyPhyDpi.y = imageGetPhysDPI( ps, pImgInf, _FALSE );
+
+ DBG( DBG_LOW, "xyPhyDpi.x = %u, xyPhyDpi.y = %u\n",
+ ps->DataInf.xyPhyDpi.x, ps->DataInf.xyPhyDpi.y );
+
+ DBG( DBG_LOW, "crArea.x = %u, crArea.y = %u\n",
+ pImgInf->crArea.x, pImgInf->crArea.y );
+
+ DBG( DBG_LOW, "crArea.cx = %u, crArea.cy = %u\n",
+ pImgInf->crArea.cx, pImgInf->crArea.cy );
+
+ ps->DataInf.XYRatio = 1000 * ps->DataInf.xyPhyDpi.y/ps->DataInf.xyPhyDpi.x;
+ DBG( DBG_LOW, "xyDpi.x = %u, xyDpi.y = %u, XYRatio = %u\n",
+ pImgInf->xyDpi.x, pImgInf->xyDpi.y, ps->DataInf.XYRatio );
+
+ ps->DataInf.dwAppLinesPerArea = (ULong)pImgInf->crArea.cy *
+ pImgInf->xyDpi.y / _MEASURE_BASE;
+
+ ps->DataInf.dwAppPixelsPerLine = (ULong)pImgInf->crArea.cx *
+ pImgInf->xyDpi.x / _MEASURE_BASE;
+
+ ps->DataInf.dwPhysBytesPerLine = (ULong)pImgInf->crArea.cx *
+ ps->DataInf.xyPhyDpi.x / _MEASURE_BASE;
+
+ if( pImgInf->wDataType <= COLOR_HALFTONE ) {
+ ps->DataInf.dwAsicPixelsPerPlane = (ps->DataInf.dwAppPixelsPerLine+7UL)&
+ 0xfffffff8UL;
+ ps->DataInf.dwAppPhyBytesPerLine =
+ ps->DataInf.dwAppBytesPerLine =
+ ps->DataInf.dwAsicBytesPerLine =
+ ps->DataInf.dwAsicBytesPerPlane = ps->DataInf.dwAsicPixelsPerPlane>>3;
+ } else {
+ ps->DataInf.dwAsicBytesPerPlane =
+ ps->DataInf.dwAsicPixelsPerPlane = ps->DataInf.dwAppPixelsPerLine;
+ }
+
+ if( COLOR_TRUE48 == pImgInf->wDataType ) {
+ ps->DataInf.dwAsicBytesPerPlane *= 2;
+ }
+
+ switch( pImgInf->wDataType ) {
+
+ case COLOR_BW:
+ ps->DataInf.dwVxdFlag |= _VF_DATATOUSERBUFFER;
+ ps->DataInf.wPhyDataType = COLOR_BW;
+ ps->Shade.bIntermediate = _ScanMode_Mono;
+ break;
+
+ case COLOR_HALFTONE:
+ if( ps->DataInf.wDither == 2 ) {
+ ps->Scan.DataProcess = fnHalftoneDirect1;
+ } else {
+ ps->Scan.DataProcess = fnHalftoneDirect0;
+ }
+/*
+ * CHANGE: it seems, that we have to use the same settings as for 256GRAY
+ */
+ ps->DataInf.dwAsicBytesPerPlane =
+ ps->DataInf.dwAsicPixelsPerPlane = ps->DataInf.dwAppPixelsPerLine;
+ ps->DataInf.wPhyDataType = COLOR_256GRAY;
+ ps->Shade.bIntermediate = _ScanMode_Mono;
+ break;
+
+ case COLOR_256GRAY:
+ ps->DataInf.dwVxdFlag |= _VF_DATATOUSERBUFFER;
+ ps->DataInf.dwAsicBytesPerLine =
+ ps->DataInf.dwAppPhyBytesPerLine = ps->DataInf.dwAppPixelsPerLine;
+ ps->DataInf.wPhyDataType = COLOR_256GRAY;
+ ps->Shade.bIntermediate = _ScanMode_Mono;
+ break;
+
+ case COLOR_TRUE24:
+ ps->Scan.DataProcess = fnP98ColorDirect;
+ ps->DataInf.dwAsicBytesPerLine =
+ ps->DataInf.dwAppPhyBytesPerLine = ps->DataInf.dwAppPixelsPerLine * 3;
+ ps->DataInf.wPhyDataType = COLOR_TRUE24;
+ ps->Shade.bIntermediate = _ScanMode_Color;
+ break;
+
+ case COLOR_TRUE48:
+ ps->Scan.DataProcess = fnP98Color48;
+ ps->DataInf.dwAsicBytesPerLine =
+ ps->DataInf.dwAppPhyBytesPerLine = ps->DataInf.dwAppPixelsPerLine * 6;
+ ps->DataInf.wPhyDataType = COLOR_TRUE48;
+ ps->Shade.bIntermediate = _ScanMode_Color;
+ break;
+
+ }
+
+ if (pImgInf->dwFlag & SCANDEF_BoundaryDWORD) {
+ ps->DataInf.dwAppBytesPerLine = (ps->DataInf.dwAppPhyBytesPerLine + 3) &
+ 0xfffffffc;
+ } else {
+ if (pImgInf->dwFlag & SCANDEF_BoundaryWORD) {
+ ps->DataInf.dwAppBytesPerLine = (ps->DataInf.dwAppPhyBytesPerLine + 1) &
+ 0xfffffffe;
+ } else {
+ ps->DataInf.dwAppBytesPerLine = ps->DataInf.dwAppPhyBytesPerLine;
+ }
+ }
+
+ DBG( DBG_LOW, "AppLinesPerArea = %u\n", ps->DataInf.dwAppLinesPerArea );
+ DBG( DBG_LOW, "AppPixelsPerLine = %u\n", ps->DataInf.dwAppPixelsPerLine );
+ DBG( DBG_LOW, "AppPhyBytesPerLine = %u\n", ps->DataInf.dwAppPhyBytesPerLine );
+ DBG( DBG_LOW, "AppBytesPerLine = %u\n", ps->DataInf.dwAppBytesPerLine );
+ DBG( DBG_LOW, "AsicPixelsPerPlane = %u\n", ps->DataInf.dwAsicPixelsPerPlane );
+ DBG( DBG_LOW, "AsicBytesPerPlane = %u\n", ps->DataInf.dwAsicBytesPerPlane );
+ DBG( DBG_LOW, "AsicBytesPerLine = %u\n", ps->DataInf.dwAsicBytesPerLine );
+ DBG( DBG_LOW, "Physical Bytes = %u\n", ps->DataInf.dwPhysBytesPerLine );
+}
+
+/**
+ */
+static void imageP96GetInfo( pScanData ps, pImgDef pImgInf )
+{
+ DBG( DBG_LOW, "imageP96GetInfo()\n" );
+
+ ps->DataInf.xyPhyDpi.x = imageGetPhysDPI( ps, pImgInf, _TRUE );
+ ps->DataInf.xyPhyDpi.y = imageGetPhysDPI( ps, pImgInf, _FALSE );
+
+ DBG( DBG_LOW, "xyPhyDpi.x = %u, xyPhyDpi.y = %u\n",
+ ps->DataInf.xyPhyDpi.x, ps->DataInf.xyPhyDpi.y );
+
+ DBG( DBG_LOW, "crArea.x = %u, crArea.y = %u\n",
+ pImgInf->crArea.x, pImgInf->crArea.y );
+
+ DBG( DBG_LOW, "crArea.cx = %u, crArea.cy = %u\n",
+ pImgInf->crArea.cx, pImgInf->crArea.cy );
+
+ ps->DataInf.XYRatio = 1000 * ps->DataInf.xyPhyDpi.y/ps->DataInf.xyPhyDpi.x;
+ DBG( DBG_LOW, "xyDpi.x = %u, xyDpi.y = %u, XYRatio = %u\n",
+ pImgInf->xyDpi.x, pImgInf->xyDpi.y, ps->DataInf.XYRatio );
+
+ ps->DataInf.dwAppLinesPerArea = (ULong)pImgInf->crArea.cy *
+ pImgInf->xyDpi.y / _MEASURE_BASE;
+ ps->DataInf.dwAsicBytesPerPlane =
+ ps->DataInf.dwAsicPixelsPerPlane = (ULong)ps->DataInf.xyPhyDpi.x *
+ pImgInf->crArea.cx / _MEASURE_BASE;
+
+ ps->DataInf.dwAppPixelsPerLine = (ULong)pImgInf->crArea.cx *
+ pImgInf->xyDpi.x / _MEASURE_BASE;
+
+ ps->DataInf.dwPhysBytesPerLine = (ULong)pImgInf->crArea.cx *
+ ps->DataInf.xyPhyDpi.x / _MEASURE_BASE;
+
+ ps->DataInf.wPhyDataType = ps->DataInf.wAppDataType;
+
+ switch( pImgInf->wDataType ) {
+
+ case COLOR_BW:
+ ps->DataInf.dwAsicBytesPerPlane =
+ (ps->DataInf.dwAsicPixelsPerPlane + 7) >> 3;
+ ps->DataInf.dwAppPhyBytesPerLine =
+ (ps->DataInf.dwAppPixelsPerLine + 7) >> 3;
+ ps->DataInf.dwVxdFlag |= _VF_DATATOUSERBUFFER;
+ ps->Scan.DataProcess = fnDataDirect;
+ break;
+
+ case COLOR_HALFTONE:
+ ps->DataInf.dwAppPhyBytesPerLine =
+ (ps->DataInf.dwAsicPixelsPerPlane + 7) >> 3;
+ if( ps->DataInf.wDither == 2 ) {
+ ps->Scan.DataProcess = fnHalftoneDirect1;
+ } else {
+ ps->Scan.DataProcess = fnHalftoneDirect0;
+ }
+ ps->DataInf.wPhyDataType = COLOR_256GRAY;
+ break;
+
+ case COLOR_256GRAY:
+ ps->DataInf.dwAppPhyBytesPerLine = ps->DataInf.dwAppPixelsPerLine;
+ ps->Scan.DataProcess = fnP96GrayDirect;
+ break;
+
+ case COLOR_TRUE24:
+#ifdef _A3I_EN
+ ps->Scan.DataProcess = fnP98ColorDirect;
+#else
+ ps->Scan.DataProcess = fnP96ColorDirect;
+#endif
+ ps->DataInf.dwAppPhyBytesPerLine = ps->DataInf.dwAppPixelsPerLine * 3;
+ }
+
+ if( pImgInf->dwFlag & SCANDEF_BoundaryDWORD ) {
+ ps->DataInf.dwAppBytesPerLine =
+ (ps->DataInf.dwAppPhyBytesPerLine + 3) & 0xfffffffc;
+ } else {
+ if ( pImgInf->dwFlag & SCANDEF_BoundaryWORD ) {
+ ps->DataInf.dwAppBytesPerLine =
+ (ps->DataInf.dwAppPhyBytesPerLine + 1) & 0xfffffffe;
+ } else {
+ ps->DataInf.dwAppBytesPerLine = ps->DataInf.dwAppPhyBytesPerLine;
+ }
+ }
+
+ if (ps->DataInf.wPhyDataType == COLOR_TRUE24)
+ ps->DataInf.dwAsicBytesPerLine = ps->DataInf.dwAsicBytesPerPlane * 3;
+ else
+ ps->DataInf.dwAsicBytesPerLine = ps->DataInf.dwAsicBytesPerPlane;
+
+/* WORK: AsicBytesPerLine only used for ASIC_98001 based scanners - try to remove
+** that, also try to remove redundant info
+*/
+ DBG( DBG_LOW, "AppLinesPerArea = %u\n", ps->DataInf.dwAppLinesPerArea );
+ DBG( DBG_LOW, "AppPixelsPerLine = %u\n", ps->DataInf.dwAppPixelsPerLine );
+ DBG( DBG_LOW, "AppPhyBytesPerLine = %u\n", ps->DataInf.dwAppPhyBytesPerLine );
+ DBG( DBG_LOW, "AppBytesPerLine = %u\n", ps->DataInf.dwAppBytesPerLine );
+ DBG( DBG_LOW, "AsicPixelsPerPlane = %u\n", ps->DataInf.dwAsicPixelsPerPlane );
+ DBG( DBG_LOW, "AsicBytesPerPlane = %u\n", ps->DataInf.dwAsicBytesPerPlane );
+ DBG( DBG_LOW, "AsicBytesPerLine = %u\n", ps->DataInf.dwAsicBytesPerLine );
+ DBG( DBG_LOW, "Physical Bytes = %u\n", ps->DataInf.dwPhysBytesPerLine );
+}
+
+/** here we wait for one data-line
+ */
+static Bool imageP96ReadOneImageLine( pScanData ps )
+{
+ Bool result = _FALSE;
+ Byte bData, bFifoCount;
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _LINE_TIMEOUT);
+ do {
+
+ bFifoCount = IODataRegisterFromScanner( ps, ps->RegFifoOffset );
+
+/* CHECK ps->bMoveDataOutFlag will never be set to _DataFromStopState !!!*/
+#if 1
+ if ((bFifoCount < ps->bMinReadFifo) &&
+ (ps->bMoveDataOutFlag == _DataFromStopState)) {
+
+ bData = IOGetScanState( ps, _FALSE);
+
+ if (!(bData & _SCANSTATE_STOP)) {
+ if (bData < ps->bCurrentLineCount)
+ bData += _NUMBER_OF_SCANSTEPS;
+ if ((bData - ps->bCurrentLineCount) < _SCANSTATE_BYTES)
+ continue;
+ }
+
+ ps->bMoveDataOutFlag = _DataAfterRefreshState;
+ }
+#endif
+
+/*
+// HEINER:A3I
+// if( ps->bMoveDataOutFlag != _DataFromStopState )
+// ps->UpdateDataCurrentReadLine( ps );
+*/
+ if( bFifoCount >= ps->bMinReadFifo ) {
+
+ /* data is ready */
+ for (; !(*ps->pCurrentColorRunTable &
+ (ps->RedDataReady | ps->GreenDataReady | _BLUE_DATA_READY));
+ ps->pCurrentColorRunTable++);
+
+#ifdef DEBUG
+ if( ps->pCurrentColorRunTable >
+ (ps->pColorRunTable+ps->BufferForColorRunTable))
+ DBG( DBG_LOW, "WARNING: pCurrentColorRunTab>pColorRunTable\n");
+#endif
+
+ if (ps->DataInf.wPhyDataType == COLOR_TRUE24) {
+
+ /* read color planes (either R/G/B or R/B/G sequence that
+ * depends on COLOR CCD, see below
+ */
+ if (*ps->pCurrentColorRunTable & ps->b1stColor) {
+ *ps->pCurrentColorRunTable &= ps->b1stMask;
+ IOReadScannerImageData (ps, ps->pPutBufR,
+ ps->DataInf.dwAsicBytesPerPlane);
+ ps->pPutBufR += ps->BufferSizePerModel;
+ if (ps->pPutBufR == ps->pEndBufR)
+ ps->pPutBufR = ps->pPrescan16;
+ } else
+ if (*ps->pCurrentColorRunTable & ps->b2ndColor) {
+ *ps->pCurrentColorRunTable &= ps->b2ndMask;
+ IOReadScannerImageData( ps, ps->pPutBufG,
+ ps->DataInf.dwAsicBytesPerPlane);
+ ps->pPutBufG += ps->BufferSizePerModel;
+ if (ps->pPutBufG == ps->pEndBufG)
+ ps->pPutBufG = ps->pPrescan8;
+ } else {
+ *ps->pCurrentColorRunTable &= ps->b3rdMask;
+ ps->pCurrentColorRunTable++; /* processed this step */
+
+ /* according to CCD type & image placement method to
+ * read third color into corresponding location.
+ * SONY CCD: Red, Green and Blue.
+ * TOSHIBA CCD: Red, Blue and Green.
+ * SCANDEF_BmpStyle: Blue, Green and Red, Otherwise
+ * Red, Green and Blue.
+ */
+ if (ps->b3rdColor & ps->GreenDataReady) {
+ /* Green always in middle */
+ IOReadScannerImageData (ps,
+ ps->DataInf.pCurrentBuffer +
+ ps->DataInf.dwAsicBytesPerPlane,
+ ps->DataInf.dwAsicBytesPerPlane);
+ } else {
+ /* Blue depends the request style from caller */
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle) {
+ /* BMP style, blue is the first one */
+ IOReadScannerImageData (ps,
+ ps->DataInf.pCurrentBuffer,
+ ps->DataInf.dwAsicBytesPerPlane);
+ } else {
+ /* Blue is the last one */
+ IOReadScannerImageData (ps, ps->DataInf.pCurrentBuffer +
+ ps->DataInf.dwAsicBytesPerPlane * 2,
+ ps->DataInf.dwAsicBytesPerPlane);
+ }
+ }
+
+ /* reassemble 3 color lines for separated RGB value */
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle) {
+ /* BMP style, red is last one */
+ memcpy( ps->DataInf.pCurrentBuffer +
+ ps->DataInf.dwAsicBytesPerPlane * 2,
+ ps->pGetBufR, ps->DataInf.dwAsicBytesPerPlane);
+ } else {
+ /* Red is first one */
+ memcpy( ps->DataInf.pCurrentBuffer,
+ ps->pGetBufR, ps->DataInf.dwAsicBytesPerPlane );
+ }
+
+ if (ps->b2ndColor & ps->GreenDataReady) {
+ /* Green always in middle */
+ memcpy( ps->DataInf.pCurrentBuffer +
+ ps->DataInf.dwAsicBytesPerPlane,
+ ps->pGetBufG, ps->DataInf.dwAsicBytesPerPlane);
+ } else {
+ /* Blue depends the request style from caller */
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle) {
+ /* BMP style, blue is the first one */
+ memcpy( ps->DataInf.pCurrentBuffer,
+ ps->pGetBufG,
+ ps->DataInf.dwAsicBytesPerPlane);
+ } else {
+ /* BMP style, blue is the last one */
+ memcpy( ps->DataInf.pCurrentBuffer +
+ ps->DataInf.dwAsicBytesPerPlane * 2,
+ ps->pGetBufG,
+ ps->DataInf.dwAsicBytesPerPlane );
+ }
+ }
+
+ /* Adjust the get pointers */
+ ps->pGetBufR += ps->BufferSizePerModel;
+ ps->pGetBufG += ps->BufferSizePerModel;
+ if (ps->pGetBufR == ps->pEndBufR)
+ ps->pGetBufR = ps->pPrescan16;
+
+ if (ps->pGetBufG == ps->pEndBufG)
+ ps->pGetBufG = ps->pPrescan8;
+
+ result = _TRUE; /* Line data in buffer */
+ break;
+ }
+
+ /* reset timer for new 10-second interval */
+ MiscStartTimer( &timer, (10 * _SECOND));
+
+ } else {
+ /* Gray Image */
+ *ps->pCurrentColorRunTable &= 0xf0; /* leave high nibble for debug */
+ ps->pCurrentColorRunTable++; /* this step has been processed */
+ IOReadScannerImageData( ps, ps->DataInf.pCurrentBuffer,
+ ps->DataInf.dwAsicBytesPerPlane );
+
+ result = _TRUE;
+ break;
+ }
+ }
+
+/* HEINER:A3I */
+ if( ps->bMoveDataOutFlag != _DataFromStopState )
+ ps->UpdateDataCurrentReadLine( ps );
+
+ } while (!MiscCheckTimer( &timer));
+
+ if( _TRUE == result ) {
+ (*ps->Scan.DataProcess)( ps, ps->Scan.bp.pMonoBuf,
+ ps->DataInf.pCurrentBuffer,
+ ps->DataInf.dwAppPhyBytesPerLine );
+ return _TRUE;
+ }
+
+ DBG( DBG_HIGH, "Timeout - Scanner malfunction !!\n" );
+ MotorToHomePosition(ps);
+
+ return _FALSE;
+}
+
+/** prepare for scanning
+ */
+static int imageP96SetupScanSettings( pScanData ps, pScanInfo pInf )
+{
+ DBG( DBG_LOW, "imageSetupP96ScanSettings()\n" );
+
+ ps->DataInf.dwVxdFlag = 0;
+ if (pInf->ImgDef.dwFlag & SCANDEF_BuildBwMap)
+ ps->DataInf.dwVxdFlag |= _VF_BUILDMAP;
+
+ ps->DataInf.dwScanFlag = pInf->ImgDef.dwFlag;
+
+ ps->DataInf.crImage = pInf->ImgDef.crArea;
+
+ /* scale according to DPI */
+ ps->DataInf.crImage.x *= ps->PhysicalDpi / _MEASURE_BASE;
+ ps->DataInf.crImage.cx *= ps->PhysicalDpi / _MEASURE_BASE;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+ ps->DataInf.crImage.x += _Transparency48OriginOffsetX;
+ ps->DataInf.crImage.y += _Transparency48OriginOffsetY;
+ }
+
+ ps->DataInf.xyAppDpi = pInf->ImgDef.xyDpi;
+ ps->DataInf.wAppDataType = pInf->ImgDef.wDataType;
+ ps->DataInf.wDither = pInf->wDither;
+
+ ps->GetImageInfo( ps, &pInf->ImgDef );
+
+ /* try to get brightness to work */
+ if (ps->DataInf.wPhyDataType != COLOR_BW) { /* if not line art */
+ ps->wBrightness = pInf->siBrightness; /* use internal tables for */
+ ps->wContrast = pInf->siContrast; /* brightness and contrast */
+
+ pInf->siBrightness = 0; /* don't use asic for threshold */
+ }
+ ps->DataInf.siBrightness = pInf->siBrightness;
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle)
+ ps->Scan.lBufferAdjust = -(Long)ps->DataInf.dwAppBytesPerLine;
+ else
+ ps->Scan.lBufferAdjust = (Long)ps->DataInf.dwAppBytesPerLine;
+
+ if (ps->DataInf.siBrightness < 0)
+ ps->DataInf.siBrightness = 255 - (_DEF_BW_THRESHOLD *
+ ps->DataInf.siBrightness / 127 + _DEF_BW_THRESHOLD);
+ else
+ ps->DataInf.siBrightness = 255 - ((255 - _DEF_BW_THRESHOLD) *
+ ps->DataInf.siBrightness / 127 + _DEF_BW_THRESHOLD);
+
+ ps->AsicReg.RD_ThresholdControl = (Byte)ps->DataInf.siBrightness;
+
+ ps->DataInf.pCurrentBuffer = ps->pScanBuffer1;
+
+ return _OK;
+}
+
+/**
+ */
+static Bool imageP98003DataIsReady( pScanData ps )
+{
+ pUChar pb;
+
+ if( ps->Scan.bDiscardAll ) {
+ ps->Scan.bDiscardAll--;
+
+ if( ps->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, ps->Bufs.b1.pReadBuf,
+ ps->DataInf.dwAsicBytesPerPlane );
+ } else {
+ IOReadColorData( ps, ps->Bufs.b1.pReadBuf,
+ ps->DataInf.dwAsicBytesPerPlane );
+ }
+ return _FALSE;
+ }
+
+ if( ps->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ pb = ps->Scan.bp.pMonoBuf;
+
+ /* use a larger buffer during halftone reads...*/
+ if( ps->DataInf.wAppDataType == COLOR_HALFTONE )
+ pb = ps->Scan.BufPut.red.bp;
+
+ IOReadScannerImageData( ps, pb, ps->DataInf.dwAsicBytesPerPlane );
+
+ } else {
+ if( !ps->Scan.DataRead( ps )) {
+ return _FALSE;
+ }
+ }
+
+ if( ps->Scan.DoSample( ps )) {
+
+ if( ps->Scan.dwLinesToRead == 1 &&
+ !(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP))
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ /* direct is done here without copying...*/
+ if( fnDataDirect != ps->Scan.DataProcess ) {
+ (*ps->Scan.DataProcess)(ps, (pVoid)(ps->Scan.bp.pMonoBuf ),
+ (pVoid)(ps->Scan.BufPut.red.bp),
+ ps->DataInf.dwAppPhyBytesPerLine);
+ }
+ return _TRUE;
+ }
+
+ return _FALSE;
+}
+
+/**
+ */
+static Bool imageP98003ReadOneImageLine( pScanData ps )
+{
+ Byte b, state;
+ TimerDef timer, t2;
+
+ MiscStartTimer( &timer, _LINE_TIMEOUT );
+ MiscStartTimer( &t2, _SECOND*2 );
+ do {
+
+ state = IOGetScanState( ps, _TRUE );
+ ps->Scan.bNowScanState = (state & _SCANSTATE_MASK);
+
+ if( state & _SCANSTATE_STOP ) {
+
+ MotorP98003ModuleForwardBackward( ps );
+
+ if( IOReadFifoLength( ps ) >= ps->Scan.dwMinReadFifo )
+ if( imageP98003DataIsReady( ps ))
+ return _TRUE;
+ } else {
+
+ ps->Scan.bModuleState = _MotorInNormalState;
+ b = ps->Scan.bNowScanState - ps->Scan.bOldScanState;
+
+ if((char) b < 0)
+ b += _NUMBER_OF_SCANSTEPS;
+
+ if( b >= ps->Scan.bRefresh ) {
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ ps->Scan.bOldScanState = IOGetScanState( ps, _TRUE );
+ ps->Scan.bOldScanState &= _SCANSTATE_MASK;
+ }
+
+ if( IOReadFifoLength( ps ) >= ps->Scan.dwMaxReadFifo ) {
+ if( imageP98003DataIsReady( ps ))
+ return _TRUE;
+ } else {
+
+ b = ps->Scan.bNowScanState - ps->Scan.bOldScanState;
+
+ if((char) b < 0)
+ b += _NUMBER_OF_SCANSTEPS;
+
+ if( b >= ps->Scan.bRefresh ) {
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ ps->Scan.bOldScanState = IOGetScanState( ps, _TRUE );
+ ps->Scan.bOldScanState &= _SCANSTATE_MASK;
+ }
+
+ if( IOReadFifoLength( ps ) >= ps->Scan.dwMinReadFifo ) {
+ if( imageP98003DataIsReady( ps ))
+ return _TRUE;
+ }
+ }
+ }
+ _DODELAY(5); /* delay 5 ms */
+
+ } while( !MiscCheckTimer( &timer ));
+
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "Timeout - Scanner malfunction !!\n" );
+ MotorToHomePosition(ps);
+
+ /* timed out, scanner malfunction */
+ return _FALSE;
+}
+
+/**
+ */
+static void imageP98003SetupScanStateVariables( pScanData ps, ULong index )
+{
+ DataType var;
+
+ ps->Scan.dpiIdx = index;
+
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
+
+ if(((ps->IO.portMode == _PORT_BIDI) ||
+ (ps->IO.portMode == _PORT_SPP)) &&
+ (ps->DataInf.wPhyDataType > COLOR_TRUE24) &&
+ (ps->DataInf.xyAppDpi.y >= 600)) {
+
+ ps->Shade.wExposure = nmlScan[ps->IO.portMode][index].exposureTime;
+ ps->Shade.wXStep = nmlScan[ps->IO.portMode][index].xStepTime;
+ } else {
+ ps->Shade.wExposure = nmlScan[_PORT_EPP][index].exposureTime;
+ ps->Shade.wXStep = nmlScan[_PORT_EPP][index].xStepTime;
+ }
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut ) {
+ ps->Shade.wExposure >>= 1;
+ ps->Shade.wXStep >>= 1;
+ }
+ } else {
+ if( ps->DataInf.dwScanFlag & SCANDEF_Transparency ) {
+ ps->Shade.wExposure = posScan[index].exposureTime;
+ ps->Shade.wXStep = posScan[index].xStepTime;
+ } else {
+ ps->Shade.wExposure = ps->Scan.negScan[index].exposureTime;
+ ps->Shade.wXStep = ps->Scan.negScan[index].xStepTime;
+ }
+ }
+
+ ps->Scan.dwInterlace = 0;
+ ps->Scan.dwInterval = 1;
+
+ if( ps->DataInf.wPhyDataType == COLOR_BW )
+ var.dwValue = xferSpeed[ps->IO.portMode].thresholdBW;
+ else {
+ if( ps->DataInf.wPhyDataType == COLOR_256GRAY )
+ var.dwValue = xferSpeed[ps->IO.portMode].thresholdGray;
+ else
+ var.dwValue = xferSpeed[ps->IO.portMode].thresholdColor;
+ }
+
+ /* for small size/descreen */
+ if((ps->DataInf.xyAppDpi.y >= 300) && var.dwValue &&
+ (ps->DataInf.dwAsicBytesPerPlane <= var.dwValue)) {
+ ps->Scan.dwInterval <<= 1;
+ }
+
+ if( var.dwValue && ps->DataInf.dwAsicBytesPerPlane > var.dwValue ) {
+ if((var.dwValue << 1) > ps->DataInf.dwAsicBytesPerPlane)
+ ps->Scan.dwInterval <<= 1;
+ else
+ if((var.dwValue << 2) > ps->DataInf.dwAsicBytesPerPlane)
+ ps->Scan.dwInterval <<= 2;
+ else
+ ps->Scan.dwInterval <<= 3;
+ }
+
+ /* 48 bit/600 dpi/Bpp mode will scan failed */
+ if(((ps->IO.portMode == _PORT_BIDI) ||
+ (ps->IO.portMode == _PORT_SPP)) &&
+ (ps->DataInf.wPhyDataType > COLOR_TRUE24) &&
+ (ps->DataInf.xyAppDpi.y >= 600)) {
+ ps->Scan.dwInterval <<= 1;
+ }
+
+ if( ps->DataInf.wPhyDataType >= COLOR_TRUE24 ) {
+
+ if( ps->DataInf.xyPhyDpi.y > 75U ) {
+ if( ps->Device.f0_8_16 ) {
+ ps->Scan.gd_gk.wGreenDiscard = ps->DataInf.xyPhyDpi.y / 75U;
+ } else {
+ ps->Scan.gd_gk.wGreenDiscard = ps->DataInf.xyPhyDpi.y / 150U;
+ }
+ } else {
+ ps->Scan.gd_gk.wGreenDiscard = 1;
+ }
+
+ ps->Scan.bd_rk.wBlueDiscard = ps->Scan.gd_gk.wGreenDiscard << 1;
+ } else {
+ ps->Scan.bd_rk.wBlueDiscard = ps->Scan.gd_gk.wGreenDiscard = 0;
+ }
+}
+
+/** PrepareScanningVariables() !!!
+ */
+static int imageP98003SetupScanSettings( pScanData ps, pScanInfo pInf )
+{
+ DBG( DBG_LOW, "imageP98003SetupScanSettings()\n" );
+
+ /* call the one for ASIC 98001 first */
+ imageP98SetupScanSettings( ps, pInf );
+
+ if( !(ps->DataInf.dwScanFlag & SCANDEF_TPA )) {
+
+ ps->Scan.dwScanOrigin = ps->Device.lUpNormal * 4 + _RFT_SCANNING_ORG;
+
+ } else if( ps->DataInf.dwScanFlag & SCANDEF_Transparency) {
+
+ ps->Scan.dwScanOrigin = ps->Device.lUpPositive * 4 +
+ _POS_SCANNING_ORG;
+ } else {
+ ps->Scan.dwScanOrigin = ps->Device.lUpNegative * 4 +
+ _NEG_SCANNING_ORG;
+ }
+ ps->Scan.dwScanOrigin += ps->Device.dwModelOriginY;
+
+ /* ------- Setup CCD Offset variables ------- */
+ if( ps->DataInf.xyAppDpi.y <= 75 ) {
+
+ if( ps->DataInf.dwVxdFlag & _VF_PREVIEW ) {
+
+ ps->Scan.bDiscardAll = 0;
+ ps->DataInf.xyPhyDpi.y = 150;
+ ps->Shade.bIntermediate |= _ScanMode_AverageOut;
+ imageP98003SetupScanStateVariables( ps, 1 );
+ ps->Scan.gd_gk.wGreenDiscard = 0;
+
+ if( ps->DataInf.xyAppDpi.y >= 38 )
+ ps->Scan.bd_rk.wBlueDiscard = 1;
+ else
+ ps->Scan.bd_rk.wBlueDiscard = 0;
+
+ if( ps->DataInf.wPhyDataType >= COLOR_256GRAY ) {
+ ps->Shade.wXStep = 6;
+ ps->Shade.wExposure = 8 * ps->Shade.wXStep;
+ }
+ } else {
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA) &&
+ (ps->DataInf.xyAppDpi.y <= 50) &&
+ (ps->DataInf.wPhyDataType >= COLOR_TRUE24)) {
+ ps->Shade.bIntermediate |= _ScanMode_AverageOut;
+ }
+
+ if((ps->DataInf.wPhyDataType<COLOR_TRUE24) || ps->Device.f0_8_16 ||
+ (ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ ps->Scan.bDiscardAll = 1;
+ ps->DataInf.xyPhyDpi.y = 75;
+ imageP98003SetupScanStateVariables( ps, 0 );
+ } else {
+ ps->Scan.bDiscardAll = 2;
+ ps->DataInf.xyPhyDpi.y = 150;
+ imageP98003SetupScanStateVariables( ps, 1 );
+ }
+ }
+ } else {
+ if( ps->DataInf.xyAppDpi.y <= 150 ) {
+
+ ps->Scan.bDiscardAll = 2;
+ ps->DataInf.xyPhyDpi.y = 150;
+ imageP98003SetupScanStateVariables( ps, 1 );
+
+ } else if( ps->DataInf.xyAppDpi.y <= 300 ) {
+
+ ps->Scan.bDiscardAll = 4;
+ ps->DataInf.xyPhyDpi.y = 300;
+ imageP98003SetupScanStateVariables( ps, 2 );
+
+ } else if( ps->DataInf.xyAppDpi.y <= 600 ) {
+
+ ps->Scan.bDiscardAll = 8;
+ ps->DataInf.xyPhyDpi.y = 600;
+ imageP98003SetupScanStateVariables( ps, 3 );
+
+ } else {
+
+ ps->Scan.bDiscardAll = 16;
+ ps->DataInf.xyPhyDpi.y = 1200;
+ imageP98003SetupScanStateVariables( ps, 4 );
+ }
+ }
+
+ /* ------- Lines have to sample or not? ------- */
+ if( ps->DataInf.xyAppDpi.y == ps->DataInf.xyPhyDpi.y ) {
+ DBG( DBG_LOW, "Sample every line\n" );
+ ps->Scan.DoSample = fnEveryLines;
+ } else {
+ if( ps->DataInf.dwVxdFlag & _VF_PREVIEW ) {
+
+ DBG( DBG_LOW, "Sample preview\n" );
+ ps->Scan.DoSample = fnSamplePreview;
+ ps->DataInf.wYSum = 150;
+
+ if( ps->DataInf.xyAppDpi.y >= 38 )
+ wPreviewScanned = ps->DataInf.xyAppDpi.y * 2;
+ else if( ps->DataInf.xyAppDpi.y >= 19 )
+ wPreviewScanned = ps->DataInf.xyAppDpi.y * 4;
+ else
+ wPreviewScanned = ps->DataInf.xyAppDpi.y * 8;
+ } else {
+
+ DBG( DBG_LOW, "Sample lines (%u - %u)...\n",
+ ps->DataInf.xyPhyDpi.y, ps->DataInf.xyAppDpi.y );
+ ps->Scan.DoSample = fnSampleLines;
+ ps->DataInf.wYSum = ps->DataInf.xyPhyDpi.y - ps->DataInf.xyAppDpi.y;
+ }
+ }
+
+ /*
+ * now assign the buffer pointers for image aquisition
+ */
+ ps->Scan.p48BitBuf.pb = NULL;
+
+ if( ps->DataInf.wPhyDataType >= COLOR_TRUE24 ) {
+
+ ULong r,g,b;
+
+ r = (ULong)_SIZE_REDFIFO /
+ ps->DataInf.dwAsicBytesPerPlane - ps->Scan.bd_rk.wRedKeep;
+ g = (ULong)_SIZE_GREENFIFO /
+ ps->DataInf.dwAsicBytesPerPlane - ps->Scan.gd_gk.wGreenKeep;
+
+ if((int)r < 16 || (int)g < 16) {
+
+ b = (ULong)(ps->Scan.bd_rk.wRedKeep +
+ ps->Scan.gd_gk.wGreenKeep + 2U) *
+ ps->DataInf.dwAsicBytesPerPlane;
+
+ DBG( DBG_LOW, "48Bit buffer request: len=%u bytes, available=%u\n",
+ b, ps->TotalBufferRequire );
+
+ if( b > ps->TotalBufferRequire )
+ return _E_NORESOURCE;
+
+ ps->Scan.p48BitBuf.pb = ps->Bufs.b1.pReadBuf;
+ }
+ }
+
+ if( ps->Scan.p48BitBuf.pb ){
+ ps->Scan.DataRead = fnReadToDriver;
+ ps->Scan.BufGet.red.bp =
+ ps->Scan.BufPut.red.bp =
+ ps->Scan.BufBegin.red.bp = ps->Scan.p48BitBuf.pb;
+ ps->Scan.BufEnd.red.bp =
+ ps->Scan.BufBegin.green.bp =
+ ps->Scan.BufGet.green.bp =
+ ps->Scan.BufPut.green.bp = ps->Scan.p48BitBuf.pb +
+ ps->DataInf.dwAsicBytesPerLine *
+ (ps->Scan.bd_rk.wRedKeep + 1U);
+
+ ps->Scan.BufEnd.green.bp = ps->Scan.BufBegin.green.bp +
+ ps->DataInf.dwAsicBytesPerLine *
+ (ps->Scan.gd_gk.wGreenKeep + 1U);
+ ps->Scan.BufPut.blue.bp =
+ ps->Scan.BufGet.blue.bp = ps->Bufs.b1.pReadBuf +
+ ps->DataInf.dwAsicBytesPerLine * 2;
+ } else {
+ ps->Scan.DataRead = fnReadOutScanner;
+ ps->Scan.BufPut.red.bp = ps->Bufs.b1.pReadBuf;
+ ps->Scan.BufData.green.bp =
+ ps->Scan.BufPut.green.bp = ps->Scan.BufPut.red.bp +
+ ps->DataInf.dwAsicBytesPerLine;
+ ps->Scan.BufPut.blue.bp = ps->Scan.BufPut.green.bp +
+ ps->DataInf.dwAsicBytesPerLine;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_ColorBGROrder ) {
+ ps->Scan.BufData.red.bp = ps->Scan.BufPut.blue.bp;
+ ps->Scan.BufData.blue.bp = ps->Scan.BufPut.red.bp;
+ } else {
+ ps->Scan.BufData.red.bp = ps->Scan.BufPut.red.bp;
+ ps->Scan.BufData.blue.bp = ps->Scan.BufPut.blue.bp;
+ }
+ }
+
+/* CHECK: maybe remove this stuff */
+ if( ps->DataInf.dwScanFlag & SCANDEF_Transparency) {
+ posScan[1].exposureTime = 96;
+ posScan[1].xStepTime = 12;
+ posScan[2].exposureTime = 96;
+ posScan[2].xStepTime = 24;
+ posScan[3].exposureTime = 96;
+ posScan[3].xStepTime = 48;
+ posScan[4].exposureTime = 96;
+ posScan[4].xStepTime = 96;
+
+ /* Reset shading Exposure Time & xStep Time */
+ ps->Shade.wExposure = posScan[ps->Scan.dpiIdx].exposureTime;
+ ps->Shade.wXStep = posScan[ps->Scan.dpiIdx].xStepTime;
+ }
+ else if( ps->DataInf.dwScanFlag & SCANDEF_Negative) {
+ ps->Scan.negScan[1].exposureTime = 96;
+ ps->Scan.negScan[1].xStepTime = 12;
+ ps->Scan.negScan[2].exposureTime = 96;
+ ps->Scan.negScan[2].xStepTime = 24;
+ ps->Scan.negScan[3].exposureTime = 96;
+ ps->Scan.negScan[3].xStepTime = 48;
+ ps->Scan.negScan[4].exposureTime = 96;
+ ps->Scan.negScan[4].xStepTime = 96;
+
+ /* Reset shading Exposure Time & xStep Time */
+ ps->Shade.wExposure = ps->Scan.negScan[ps->Scan.dpiIdx].exposureTime;
+ ps->Shade.wXStep = ps->Scan.negScan[ps->Scan.dpiIdx].xStepTime;
+ }
+
+ return _OK;
+}
+
+/************************ exported functions *********************************/
+
+/**
+ */
+_LOC int ImageInitialize( pScanData ps )
+{
+ DBG( DBG_HIGH, "ImageInitialize()\n" );
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ ps->Scan.dpiIdx = 0;
+ ps->Scan.negScan = negScan;
+
+ /*
+ * depending on the asic, we set some functions
+ */
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ ps->GetImageInfo = imageP98GetInfo;
+ ps->SetupScanSettings = imageP98SetupScanSettings;
+ ps->ReadOneImageLine = imageP98001ReadOneImageLine;
+
+ } else if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ ps->GetImageInfo = imageP98GetInfo;
+ ps->SetupScanSettings = imageP98003SetupScanSettings;
+ ps->ReadOneImageLine = imageP98003ReadOneImageLine;
+
+ } else if( _IS_ASIC96(ps->sCaps.AsicID)) {
+
+ ps->GetImageInfo = imageP96GetInfo;
+ ps->SetupScanSettings = imageP96SetupScanSettings;
+ ps->ReadOneImageLine = imageP96ReadOneImageLine;
+
+ } else {
+
+ DBG( DBG_HIGH , "NOT SUPPORTED ASIC !!!\n" );
+ return _E_NOSUPP;
+ }
+ return _OK;
+}
+
+/* END PLUSTEK-PP_IMAGE.C ...................................................*/
diff --git a/backend/plustek-pp_io.c b/backend/plustek-pp_io.c
new file mode 100644
index 0000000..ba68599
--- /dev/null
+++ b/backend/plustek-pp_io.c
@@ -0,0 +1,999 @@
+/* @file plustekpp-io.c
+ * @brief as the name says, here we have all the I/O
+ * functions according to the parallel port hardware
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.37 - initial version
+ * - added Kevins' suggestions
+ * - 0.38 - added Asic 98003 stuff and ioP98ReadWriteTest()
+ * - added IODataRegisterToDAC()
+ * - replaced function IOSPPWrite by IOMoveDataToScanner
+ * - modified ioP98OpenScanPath again and reuse V0.36 stuff again
+ * - added IO functions
+ * - 0.39 - added IO functions
+ * - added f97003 stuff from A3I code
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some prototypes *********************************/
+
+static Bool fnEPPRead ( pScanData ps, pUChar pBuffer, ULong ulSize );
+static Bool fnSPPRead ( pScanData ps, pUChar pBuffer, ULong ulSize );
+static Bool fnBiDirRead( pScanData ps, pUChar pBuffer, ULong ulSize );
+
+typedef struct {
+ pFnReadData func;
+ char *name;
+} ioReadFuncDef;
+
+static ioReadFuncDef ioReadFunc[3] = {
+ { fnEPPRead, "fnEPPRead" },
+ { fnSPPRead, "fnSPPRead" },
+ { fnBiDirRead, "fnBiDirRead" }
+};
+
+/*************************** some definitions ********************************/
+
+#define _MEMTEST_SIZE 1280
+
+/*************************** local functions *********************************/
+
+/** we provide some functions to read data from SPP port according to
+ * the speed we have detected (ReadWriteTest!!)
+ */
+static Byte ioDataFromSPPFast( pScanData ps )
+{
+ Byte bData, tmp;
+
+ /* notify asic we will read the high nibble data from status port */
+ if( _FALSE == ps->f97003 ) {
+ _OUTB_CTRL( ps, ps->CtrlReadHighNibble );
+ _DO_UDELAY( 1 );
+ }
+
+ /* read high nibble */
+ bData = _INB_STATUS( ps );
+ bData &= 0xf0;
+
+ _OUTB_CTRL( ps, ps->CtrlReadLowNibble );
+ _DO_UDELAY( 1 );
+
+ /* read low nibble */
+ tmp = _INB_STATUS( ps );
+
+ /* combine with low nibble */
+ bData |= (tmp >> 4);
+
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ _DO_UDELAY( 1 );
+
+ return bData;
+}
+
+static Byte ioDataFromSPPMiddle( pScanData ps )
+{
+ Byte bData, tmp;
+
+ /* notify asic we will read the high nibble data from status port */
+ if( _FALSE == ps->f97003 ) {
+ _OUTB_CTRL( ps, ps->CtrlReadHighNibble );
+ _DO_UDELAY( 1 );
+ }
+
+ /* read high nibble */
+ _INB_STATUS( ps );
+ bData = _INB_STATUS( ps );
+ bData &= 0xf0;
+
+ _OUTB_CTRL( ps, ps->CtrlReadLowNibble );
+ _DO_UDELAY( 1 );
+
+ /* read low nibble */
+ _INB_STATUS( ps );
+ tmp = _INB_STATUS( ps );
+
+ /* combine with low nibble */
+ bData |= (tmp >> 4);
+
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ _DO_UDELAY( 1 );
+
+ return bData;
+}
+
+static UChar ioDataFromSPPSlow( pScanData ps )
+{
+ Byte bData, tmp;
+
+ /* notify asic we will read the high nibble data from status port */
+ if( _FALSE == ps->f97003 ) {
+ _OUTB_CTRL( ps, ps->CtrlReadHighNibble );
+ _DO_UDELAY( 2 );
+ }
+
+ /* read high nibble */
+ _INB_STATUS( ps );
+ _INB_STATUS( ps );
+ bData = _INB_STATUS( ps );
+ bData &= 0xf0;
+
+ _OUTB_CTRL( ps, ps->CtrlReadLowNibble );
+ _DO_UDELAY( 2 );
+
+ /* read low nibble */
+ _INB_STATUS( ps );
+ _INB_STATUS( ps );
+ tmp = _INB_STATUS( ps );
+
+ /* combine with low nibble */
+ bData |= (tmp >> 4);
+
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ _DO_UDELAY( 2 );
+
+ return bData;
+}
+
+static UChar ioDataFromSPPSlowest( pScanData ps )
+{
+ Byte bData, tmp;
+
+ /* notify asic we will read the high nibble data from status port */
+ if( _FALSE == ps->f97003 ) {
+ _OUTB_CTRL( ps, ps->CtrlReadHighNibble );
+ _DO_UDELAY( 3 );
+ }
+
+ /* read high nibble */
+ _INB_STATUS( ps );
+ _INB_STATUS( ps );
+ _INB_STATUS( ps );
+ bData = _INB_STATUS( ps );
+ bData &= 0xf0;
+
+ _OUTB_CTRL( ps, ps->CtrlReadLowNibble );
+ _DO_UDELAY( 3 );
+
+ /* read low nibble */
+ _INB_STATUS( ps );
+ _INB_STATUS( ps );
+ _INB_STATUS( ps );
+ tmp = _INB_STATUS( ps );
+
+ /* combine with low nibble */
+ bData |= (tmp >> 4);
+
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ _DO_UDELAY( 3 );
+
+ return bData;
+}
+
+/** Read data from STATUS port. We have to read twice and combine two nibble
+ * data to one byte.
+ */
+static Bool fnSPPRead( pScanData ps, pUChar pBuffer, ULong ulSize )
+{
+ switch( ps->IO.delay ) {
+
+ case 0:
+ for (; ulSize; ulSize--, pBuffer++)
+ *pBuffer = ioDataFromSPPFast( ps );
+ break;
+
+ case 1:
+ for (; ulSize; ulSize--, pBuffer++)
+ *pBuffer = ioDataFromSPPMiddle( ps );
+ break;
+
+ case 2:
+ for (; ulSize; ulSize--, pBuffer++)
+ *pBuffer = ioDataFromSPPSlow( ps );
+ break;
+
+ default:
+ for (; ulSize; ulSize--, pBuffer++)
+ *pBuffer = ioDataFromSPPSlowest( ps );
+ break;
+ }
+
+ return _TRUE;
+}
+
+
+/** Using buffered I/O to read data from EPP Data Port
+ */
+static Bool fnEPPRead( pScanData ps, pUChar pBuffer, ULong ulSize )
+{
+ register ULong i;
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+#ifndef __KERNEL__
+ sanei_pp_set_datadir( ps->pardev, SANEI_PP_DATAIN );
+#else
+ _OUTB_CTRL( ps, (_CTRL_GENSIGNAL + _CTRL_DIRECTION));
+ _DO_UDELAY( 1 );
+#endif
+ for( i = 0; i < ulSize; i++ )
+ pBuffer[i] = _INB_EPPDATA( ps );
+
+#ifndef __KERNEL__
+ sanei_pp_set_datadir( ps->pardev, SANEI_PP_DATAOUT );
+#else
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ _DO_UDELAY( 1 );
+#endif
+ } else {
+
+ for( i = 0; i < ulSize; i++ )
+ pBuffer[i] = _INB_EPPDATA( ps );
+ }
+
+ return _TRUE;
+}
+
+/**
+ */
+static Bool fnBiDirRead( pScanData ps, pUChar pBuffer, ULong ulSize )
+{
+ UChar start, end;
+
+ start = _CTRL_START_BIDIREAD;
+ end = _CTRL_END_BIDIREAD;
+
+#ifndef __KERNEL__
+ sanei_pp_set_datadir( ps->pardev, SANEI_PP_DATAIN );
+
+ if( !sanei_pp_uses_directio()) {
+ start &= ~_CTRL_DIRECTION;
+ end &= ~_CTRL_DIRECTION;
+ }
+#else
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+ _OUTB_CTRL( ps, (_CTRL_GENSIGNAL + _CTRL_DIRECTION));
+ }
+#endif
+
+ switch( ps->IO.delay ) {
+
+ case 0:
+ for( ; ulSize; ulSize--, pBuffer++ ) {
+ _OUTB_CTRL( ps, start );
+ *pBuffer = _INB_DATA( ps );
+ _OUTB_CTRL( ps, end );
+ }
+ break;
+
+ case 1:
+ _DO_UDELAY( 1 );
+ for(; ulSize; ulSize--, pBuffer++ ) {
+ _OUTB_CTRL( ps, start );
+ _DO_UDELAY( 1 );
+
+ *pBuffer = _INB_DATA( ps );
+
+ _OUTB_CTRL( ps, end );
+ _DO_UDELAY( 1 );
+ }
+ break;
+
+ default:
+ _DO_UDELAY( 2 );
+ for(; ulSize; ulSize--, pBuffer++ ) {
+ _OUTB_CTRL( ps, start );
+ _DO_UDELAY( 2 );
+
+ *pBuffer = _INB_DATA( ps );
+
+ _OUTB_CTRL( ps, end );
+ _DO_UDELAY( 2 );
+ }
+ break;
+
+ }
+
+#ifndef __KERNEL__
+ sanei_pp_set_datadir( ps->pardev, SANEI_PP_DATAOUT );
+#else
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL );
+ }
+#endif
+ return _TRUE;
+}
+
+/** as the name says, we switch to SPP mode
+ */
+static void ioSwitchToSPPMode( pScanData ps )
+{
+ /* save the control and data port value
+ */
+ ps->IO.bOldControlValue = _INB_CTRL( ps );
+ ps->IO.bOldDataValue = _INB_DATA( ps );
+
+ _OUTB_CTRL( ps, _CTRL_GENSIGNAL ); /* 0xc4 */
+ _DO_UDELAY( 2 );
+}
+
+/** restore the port settings
+ */
+static void ioRestoreParallelMode( pScanData ps )
+{
+ _OUTB_CTRL( ps, ps->IO.bOldControlValue & 0x3f );
+ _DO_UDELAY( 1 );
+
+ _OUTB_DATA( ps, ps->IO.bOldDataValue );
+ _DO_UDELAY( 1 );
+}
+
+/** try to connect to scanner (ASIC 9600x and 98001)
+ */
+_LOC void ioP98001EstablishScannerConnection( pScanData ps, ULong delTime )
+{
+ _OUTB_DATA( ps, _ID_TO_PRINTER );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID1ST );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID2ND );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID3RD );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID4TH );
+ _DO_UDELAY( delTime );
+}
+
+/** try to connect to scanner (ASIC 98003)
+ */
+static void ioP98003EstablishScannerConnection( pScanData ps, ULong delTime )
+{
+ _OUTB_DATA( ps, _ID1ST );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID2ND );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID3RD );
+ _DO_UDELAY( delTime );
+
+ _OUTB_DATA( ps, _ID4TH );
+ _DO_UDELAY( delTime );
+}
+
+/** switch the printer interface to scanner
+ */
+static Bool ioP96OpenScanPath( pScanData ps )
+{
+ if( 0 == ps->IO.bOpenCount ) {
+
+ /* not established */
+ ioSwitchToSPPMode( ps );
+
+ /* Scanner command sequence to open scanner path */
+ ioP98001EstablishScannerConnection( ps, 5 );
+ }
+#ifdef DEBUG
+ else
+ DBG( DBG_IO, "!!!! Path already open (%u)!!!!\n", ps->IO.bOpenCount );
+#endif
+
+ ps->IO.bOpenCount++; /* increment the opened count */
+
+/*
+ * CHECK to we really need that !!
+ */
+ ps->IO.useEPPCmdMode = _FALSE;
+ return _TRUE;
+}
+
+/** try to connect to scanner
+ */
+static Bool ioP98OpenScanPath( pScanData ps )
+{
+ Byte tmp;
+ ULong dw;
+ ULong dwTime = 1;
+
+ if( 0 == ps->IO.bOpenCount ) {
+
+ /* not established */
+ ioSwitchToSPPMode( ps );
+
+ for( dw = 10; dw; dw-- ) {
+
+ /*
+ * this seems to be necessary...
+ */
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+ ioP98001EstablishScannerConnection( ps, dw );
+#if 0
+ ioP98001EstablishScannerConnection( ps, dw );
+ ioP98001EstablishScannerConnection( ps, dw );
+#endif
+ } else {
+ ioP98003EstablishScannerConnection( ps, dw );
+ }
+
+ _INB_STATUS( ps );
+ tmp = _INB_STATUS( ps );
+
+ if( 0x50 == ( tmp & 0xf0 )) {
+
+ ps->IO.bOpenCount = 1;
+
+ if( ps->sCaps.AsicID == IODataFromRegister(ps, ps->RegAsicID)) {
+ return _TRUE;
+ }
+ ps->IO.bOpenCount = 0;
+ }
+
+ dwTime++;
+ }
+ DBG( DBG_IO, "ioP98OpenScanPath() failed!\n" );
+ return _FALSE;
+ }
+#ifdef DEBUG
+ else
+ DBG( DBG_IO, "!!!! Path already open (%u)!!!!\n", ps->IO.bOpenCount );
+#endif
+
+ ps->IO.bOpenCount++; /* increment the opened count */
+ return _TRUE;
+}
+
+/** Switch back to printer mode.
+ * Restore the printer control/data port value.
+ */
+static void ioCloseScanPath( pScanData ps )
+{
+ if( ps->IO.bOpenCount && !(--ps->IO.bOpenCount)) {
+
+#ifdef DEBUG
+ ps->IO.bOpenCount = 1;
+#endif
+ IORegisterToScanner( ps, 0xff );
+
+ /*
+ * back to pass-through printer mode
+ */
+ IORegisterToScanner( ps, ps->RegSwitchBus );
+#ifdef DEBUG
+ ps->IO.bOpenCount = 0;
+#endif
+ ps->IO.useEPPCmdMode = _FALSE;
+
+ ioRestoreParallelMode( ps );
+ }
+}
+
+/** check the memory to see that the data-transfers will work.
+ * (ASIC 9800x only)
+ */
+static int ioP98ReadWriteTest( pScanData ps )
+{
+ UChar tmp;
+ ULong ul;
+ pUChar buffer;
+ int retval;
+
+ DBG( DBG_LOW, "ioP98ReadWriteTest()\n" );
+
+ /* _MEMTEST_SIZE: Read, _MEMTEST_SIZE:Write */
+ buffer = _KALLOC( sizeof(UChar) * _MEMTEST_SIZE*2, GFP_KERNEL );
+ if( NULL == buffer )
+ return _E_ALLOC;
+
+ /* prepare content */
+ for( ul = 0; ul < _MEMTEST_SIZE; ul++ )
+ buffer[ul] = (UChar)ul;
+
+ ps->OpenScanPath(ps);
+
+ /* avoid switching to Lamp0, when previously scanned in transp./neg mode */
+ tmp = ps->bLastLampStatus + _SCAN_BYTEMODE;
+ IODataToRegister( ps, ps->RegScanControl, tmp );
+
+ IODataToRegister( ps, ps->RegModelControl, (_LED_ACTIVITY | _LED_CONTROL));
+
+ IODataToRegister( ps, ps->RegModeControl, _ModeMappingMem );
+ IODataToRegister( ps, ps->RegMemoryLow, 0 );
+ IODataToRegister( ps, ps->RegMemoryHigh, 0 );
+
+ /* fill to buffer */
+ IOMoveDataToScanner( ps, buffer, _MEMTEST_SIZE );
+
+ IODataToRegister( ps, ps->RegModeControl, _ModeMappingMem );
+ IODataToRegister( ps, ps->RegMemoryLow, 0 );
+ IODataToRegister( ps, ps->RegMemoryHigh, 0 );
+ IODataToRegister( ps, ps->RegWidthPixelsLow, 0 );
+ IODataToRegister( ps, ps->RegWidthPixelsHigh, 5 );
+
+ ps->AsicReg.RD_ModeControl = _ModeReadMappingMem;
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ ps->CloseScanPath( ps );
+
+ IOReadScannerImageData( ps, buffer + _MEMTEST_SIZE, _MEMTEST_SIZE );
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ ps->CloseScanPath( ps );
+
+ /* check the result ! */
+ retval = _OK;
+
+ for( ul = 0; ul < _MEMTEST_SIZE; ul++ ) {
+ if( buffer[ul] != buffer[ul+_MEMTEST_SIZE] ) {
+ DBG( DBG_HIGH, "Error in memory test at pos %u (%u != %u)\n",
+ ul, buffer[ul], buffer[ul+_MEMTEST_SIZE] );
+ retval = _E_NO_DEV;
+ break;
+ }
+ }
+
+ _KFREE(buffer);
+ return retval;
+}
+
+/** Put data to DATA port and trigger hardware through CONTROL port to read it.
+ */
+static void ioSPPWrite( pScanData ps, pUChar pBuffer, ULong size )
+{
+ DBG( DBG_IO , "Moving %u bytes to scanner, IODELAY = %u...\n",
+ size, ps->IO.delay );
+ switch( ps->IO.delay ) {
+
+ case 0:
+ for (; size; size--, pBuffer++) {
+ _OUTB_DATA( ps, *pBuffer );
+ _OUTB_CTRL( ps, _CTRL_START_DATAWRITE );
+ _OUTB_CTRL( ps, _CTRL_END_DATAWRITE );
+ }
+ break;
+
+ case 1:
+ case 2:
+ for (; size; size--, pBuffer++) {
+ _OUTB_DATA( ps, *pBuffer );
+ _DO_UDELAY( 1 );
+ _OUTB_CTRL( ps, _CTRL_START_DATAWRITE );
+ _DO_UDELAY( 1 );
+ _OUTB_CTRL( ps, _CTRL_END_DATAWRITE );
+ _DO_UDELAY( 2 );
+ }
+ break;
+
+ default:
+ for (; size; size--, pBuffer++) {
+ _OUTB_DATA( ps, *pBuffer );
+ _DO_UDELAY( 1 );
+ _OUTB_CTRL( ps, _CTRL_START_DATAWRITE );
+ _DO_UDELAY( 2 );
+ _OUTB_CTRL( ps, _CTRL_END_DATAWRITE );
+ _DO_UDELAY( 3 );
+ }
+ break;
+ }
+ DBG( DBG_IO , "... done.\n" );
+}
+
+/** set the scanner to "read" data mode
+ */
+static void ioEnterReadMode( pScanData ps )
+{
+ if( ps->IO.portMode != _PORT_SPP ) {
+
+ _DO_UDELAY( 1 );
+ IORegisterToScanner( ps, ps->RegEPPEnable );
+
+ if( _IS_ASIC98( ps->sCaps.AsicID ))
+ ps->IO.useEPPCmdMode = _TRUE;
+ }
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ ps->IO.bOldControlValue = _INB_CTRL( ps );
+
+ /* ask ASIC to enter read mode */
+ IORegisterToScanner( ps, ps->RegReadDataMode );
+}
+
+/************************ exported functions *********************************/
+
+/** here we do some init work
+ */
+_LOC int IOInitialize( pScanData ps )
+{
+ DBG( DBG_HIGH, "IOInitialize()\n" );
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ ps->OpenScanPath = ioP98OpenScanPath;
+ ps->ReadWriteTest = ioP98ReadWriteTest;
+
+ } else if( _IS_ASIC96(ps->sCaps.AsicID)) {
+
+ ps->OpenScanPath = ioP96OpenScanPath;
+
+ } else {
+
+ DBG( DBG_HIGH , "NOT SUPPORTED ASIC !!!\n" );
+ return _E_NOSUPP;
+ }
+
+ ps->CloseScanPath = ioCloseScanPath;
+ ps->Device.ReadData = ioReadFunc[ps->IO.portMode].func;
+ DBG( DBG_HIGH, "* using readfunction >%s<\n",
+ ioReadFunc[ps->IO.portMode].name );
+ return _OK;
+}
+
+/** Write specific length buffer to scanner
+ * The scan path is already established
+ */
+_LOC void IOMoveDataToScanner( pScanData ps, pUChar pBuffer, ULong size )
+{
+#ifdef DEBUG
+ if( 0 == ps->IO.bOpenCount )
+ DBG( DBG_IO, "IOMoveDataToScanner - no connection!\n" );
+#endif
+
+ IORegisterToScanner( ps, ps->RegInitDataFifo );
+ IORegisterToScanner( ps, ps->RegWriteDataMode );
+
+ ioSPPWrite( ps, pBuffer, size );
+}
+
+/** Calling SITUATION: Scanner path is established.
+ * download a scanstate-table
+ */
+_LOC void IODownloadScanStates( pScanData ps )
+{
+ TimerDef timer;
+#ifdef DEBUG
+ if( 0 == ps->IO.bOpenCount )
+ DBG( DBG_IO, "IODownloadScanStates - no connection!\n" );
+#endif
+
+ IORegisterToScanner( ps, ps->RegScanStateControl );
+
+ ioSPPWrite( ps, ps->a_nbNewAdrPointer, _SCANSTATE_BYTES );
+
+ if( ps->Scan.fRefreshState ) {
+
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ MiscStartTimer( &timer, (_SECOND/2));
+ do {
+
+ if (!( IOGetScanState( ps, _TRUE) & _SCANSTATE_STOP))
+ break;
+ }
+ while( !MiscCheckTimer(&timer));
+ }
+}
+
+/** Calling SITUATION: Scanner path is established.
+ * Write a data to asic
+ */
+_LOC void IODataToScanner( pScanData ps, Byte bValue )
+{
+ ULong deltime = 4;
+
+#ifdef DEBUG
+ if( 0 == ps->IO.bOpenCount )
+ DBG( DBG_IO, "IODataToScanner - no connection!\n" );
+#endif
+
+ if( ps->IO.delay < 2 )
+ deltime = 2;
+
+ /* output data */
+ _OUTB_DATA( ps, bValue );
+ _DO_UDELAY( deltime );
+
+ /* notify asic there is data */
+ _OUTB_CTRL( ps, _CTRL_START_DATAWRITE );
+ _DO_UDELAY( deltime );
+
+ /* end write cycle */
+ _OUTB_CTRL( ps, _CTRL_END_DATAWRITE );
+ _DO_UDELAY( deltime-1 );
+}
+
+/** Calling SITUATION: Scanner path is established.
+ * Write a data to specific asic's register
+ */
+_LOC void IODataToRegister( pScanData ps, Byte bReg, Byte bData )
+{
+#ifdef DEBUG
+ if( 0 == ps->IO.bOpenCount )
+ DBG( DBG_IO, "IODataToRegister - no connection!\n" );
+#endif
+
+ /* specify register */
+ IORegisterToScanner( ps, bReg );
+
+ /* then write the content */
+ IODataToScanner( ps, bData );
+}
+
+/** Calling SITUATION: Scanner path is established.
+ * Read the content of specific asic's register
+ */
+_LOC Byte IODataFromRegister( pScanData ps, Byte bReg )
+{
+ IORegisterToScanner( ps, bReg );
+
+ if( 0 == ps->IO.delay )
+ return ioDataFromSPPFast( ps );
+ else if( 1 == ps->IO.delay )
+ return ioDataFromSPPMiddle( ps );
+ else if( 2 == ps->IO.delay )
+ return ioDataFromSPPSlow( ps );
+ else
+ return ioDataFromSPPSlowest( ps );
+}
+
+/** Calling SITUATION: Scanner path is established.
+ * Write a register to asic (used for a command without parameter)
+ */
+_LOC void IORegisterToScanner( pScanData ps, Byte bReg )
+{
+#ifdef DEBUG
+ if( 0 == ps->IO.bOpenCount )
+ DBG( DBG_IO, "IORegisterToScanner - no connection!\n" );
+#endif
+
+ /*
+ * write data to port
+ */
+ _OUTB_DATA( ps, bReg );
+
+ /*
+ * depending on the mode, generate the trigger signals
+ */
+ if( ps->IO.useEPPCmdMode ) {
+
+ _DO_UDELAY( 5 );
+
+ _OUTB_CTRL( ps, _CTRL_EPPSIGNAL_WRITE); /* 0xc5 */
+ _DO_UDELAY( 5 );
+
+ _OUTB_CTRL( ps, _CTRL_EPPTRIG_REGWRITE);/* 0xcd */
+ _DO_UDELAY( 5 );
+
+ _OUTB_CTRL( ps, _CTRL_EPPSIGNAL_WRITE); /* 0xc5 */
+ _DO_UDELAY( 5 );
+
+ _OUTB_CTRL( ps, _CTRL_END_REGWRITE); /* 0xc4 */
+
+ } else {
+ if( ps->IO.delay < 2 ) {
+
+ _DO_UDELAY( 1 );
+ _OUTB_CTRL( ps, _CTRL_START_REGWRITE);
+ _DO_UDELAY( 1 );
+ _OUTB_CTRL( ps, _CTRL_END_REGWRITE);
+ } else {
+
+ _DO_UDELAY( 2 );
+ _OUTB_CTRL( ps, _CTRL_START_REGWRITE);
+ _DO_UDELAY( 2 );
+ _OUTB_CTRL( ps, _CTRL_END_REGWRITE);
+ _DO_UDELAY( 2 );
+ }
+ }
+}
+
+/** write data to the DAC - ASIC 98001/3 only
+ */
+_LOC void IODataRegisterToDAC( pScanData ps, Byte bReg, Byte bData )
+{
+ ULong i;
+
+ IODataToRegister( ps, ps->RegADCAddress, bReg );
+ IODataToRegister( ps, ps->RegADCData, bData );
+ IODataToRegister( ps, ps->RegADCSerialOutStr, bData );
+
+ /* TEST: ORG was 1 ms for ASIC 98001 */
+ _DO_UDELAY( 12 );
+
+ for( i = 4; i; i-- ) {
+
+ _OUTB_CTRL( ps, _CTRL_START_DATAWRITE );
+ _DO_UDELAY( 5 );
+ _OUTB_CTRL( ps, _CTRL_END_DATAWRITE );
+ _DO_UDELAY( 12 );
+ }
+}
+
+/** Calling SITUATION: Scanner path was not established.
+ * Read the content of specific asics' register
+ */
+_LOC Byte IODataRegisterFromScanner( pScanData ps, Byte bReg )
+{
+ Byte bData;
+
+ ps->OpenScanPath( ps );
+ bData = IODataFromRegister( ps, bReg );
+ ps->CloseScanPath( ps );
+
+ return bData;
+}
+
+/** Calling SITUATION: Scanner path not established.
+ * Write a value of register to asic
+ */
+_LOC void IOCmdRegisterToScanner( pScanData ps, Byte bReg, Byte bData )
+{
+ ps->OpenScanPath( ps );
+ IODataToRegister( ps, bReg, bData );
+ ps->CloseScanPath( ps );
+}
+
+/** Calling SITUATION: Scanner path not established.
+ * Write a register to asic (used for a command without parameter)
+ */
+_LOC void IORegisterDirectToScanner( pScanData ps, Byte bReg )
+{
+ ps->OpenScanPath( ps ); /* establish the connection */
+ IORegisterToScanner( ps, bReg ); /* write register to asic */
+ ps->CloseScanPath( ps ); /* disconnect */
+}
+
+/** perform a SW reset of ASIC 98003 models
+ */
+_LOC void IOSoftwareReset( pScanData ps )
+{
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID )
+ return;
+
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegTestMode, _SW_TESTMODE );
+
+ ioSwitchToSPPMode( ps );
+
+ _OUTB_DATA( ps, _RESET1ST );
+ _DODELAY( 5 );
+
+ _OUTB_DATA( ps, _RESET2ND );
+ _DODELAY( 5 );
+
+ _OUTB_DATA( ps, _RESET3RD );
+ _DODELAY( 5 );
+
+ _OUTB_DATA( ps, _RESET4TH );
+ _DODELAY( 5 );
+
+ ioRestoreParallelMode( ps );
+
+ /* reset test mode register */
+ IODataToRegister( ps, ps->RegTestMode, 0 );
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl );
+
+ ps->CloseScanPath( ps );
+}
+
+/** Read specific length data from scanner and the method depends on the
+ * mode defined in registry.
+ */
+_LOC void IOReadScannerImageData( pScanData ps, pUChar pBuf, ULong size )
+{
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID )
+ ps->OpenScanPath( ps);
+
+ if( _IS_ASIC98( ps->sCaps.AsicID))
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+
+ /* enter read mode */
+ ioEnterReadMode( ps );
+
+ /* call corresponding read proc */
+ ps->Device.ReadData( ps, pBuf, size );
+
+ /* Clear EPP/ECP read mode by simply close scanner path and re-open it */
+ ps->CloseScanPath( ps );
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ ps->OpenScanPath( ps );
+}
+
+#ifdef __KERNEL__
+
+/** the wrapper functions to support delayed and non-delayed I/O
+ */
+_LOC void IOOut( Byte data, UShort port )
+{
+ DBG( DBG_IOF, "outb(0x%04x, 0x%02x)\n", port, data );
+ outb( data, port );
+}
+
+_LOC void IOOutDelayed( Byte data, UShort port )
+{
+ DBG( DBG_IOF, "outb_p(0x%04x, 0x%02x)\n", port, data );
+ outb_p( data, port );
+}
+
+_LOC Byte IOIn( UShort port )
+{
+#ifdef DEBUG
+ Byte data = inb( port );
+
+ DBG( DBG_IOF, "inb(0x%04x) = 0x%02x\n", port, data );
+ return data;
+#else
+ return inb( port );
+#endif
+}
+
+_LOC Byte IOInDelayed( UShort port )
+{
+#ifdef DEBUG
+ Byte data = inb_p( port );
+
+ DBG( DBG_IOF, "inb_p(0x%04x) = 0x%02x\n", port, data );
+ return data;
+#else
+ return inb_p( port );
+#endif
+}
+#endif /* guard __KERNEL__ */
+
+/* END PLUSTEK-PP_IO.C ......................................................*/
diff --git a/backend/plustek-pp_map.c b/backend/plustek-pp_map.c
new file mode 100644
index 0000000..8afeb32
--- /dev/null
+++ b/backend/plustek-pp_map.c
@@ -0,0 +1,304 @@
+/* @file plustek-pp_map.c
+ * @brief functions to create and manipulate lookup tables.
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - brightness and contrast is working (see mapAdjust)
+ * - 0.32 - no changes
+ * - 0.33 - disabled a few functions
+ * - 0.34 - added new dither matrix for checking
+ * - 0.35 - no changes
+ * - 0.36 - activated Dithermap 1
+ * - removed some unused functions
+ * - added additional SCANDEF_Inverse check to MapSetupDither()
+ * - fixed the double inversion bug, the map always compensates
+ * - the scanner hw-settings
+ * - 0.37 - code cleanup
+ * - 0.38 - added P12 stuff
+ * - 0.39 - no changes
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - made MapAdjust global
+ * - changed include names
+ * - 0.43 - cleanup, removed floating point stuff
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** local vars **************************************/
+
+/*WORK:
+ * create other thresholds for the dither maps
+ */
+static Byte mapDitherMatrix0[_DITHERSIZE] =
+{
+ 0, 32, 8, 40, 2, 34, 10, 42,
+ 48, 16, 56, 24, 50, 18, 58, 26,
+ 12, 44, 4, 36, 14, 46, 6, 38,
+ 60, 28, 52, 20, 62, 30, 54, 22,
+ 3, 35, 11, 43, 1, 33, 9, 41,
+ 51, 19, 59, 27, 49, 17, 57, 25,
+ 15, 47, 7, 39, 13, 45, 5, 37,
+ 63, 31, 55, 23, 61, 29, 53, 21
+};
+
+static Byte mapDitherMatrix1[_DITHERSIZE] =
+{
+ 2, 60, 16, 56, 3, 57, 13, 53,
+ 34, 18, 48, 32, 35, 19, 45, 29,
+ 10, 50, 6, 63, 11, 51, 7, 61,
+ 42, 26, 38, 22, 43, 27, 39, 23,
+ 4, 58, 14, 54, 1, 59, 15, 55,
+ 36, 20, 46, 30, 33, 17, 47, 31,
+ 12, 52, 8, 62, 9, 49, 5, 63,
+ 44, 28, 40, 24, 41, 25, 37, 21
+};
+
+/*************************** local functions *********************************/
+
+/** set the selected dither maps...
+ */
+static void mapSetDitherMap( pScanData ps )
+{
+ ULong i;
+ pUChar pDitherSource;
+ pUChar pDither = ps->a_bDitherPattern;
+
+ if( 0 == ps->DataInf.wDither ) {
+ DBG( DBG_LOW, "Using Dithermatrix 0\n" );
+ pDitherSource = mapDitherMatrix0;
+ } else {
+ DBG( DBG_LOW, "Using Dithermatrix 1\n" );
+ pDitherSource = mapDitherMatrix1;
+ }
+
+ for( i = 0; i < _DITHERSIZE; i++ ) {
+ pDither[i] = pDitherSource[i];
+ }
+}
+
+/** nothing more to say
+ */
+static void mapInvertMap( pScanData ps )
+{
+ pULong pdw;
+ ULong dw, size;
+
+ DBG( DBG_LOW, "mapInvertMap()\n" );
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+ size = 4096;
+ } else {
+ size = 256;
+ }
+
+ for (pdw = (pULong)ps->a_bMapTable, dw = size * 3 / 4; dw; dw--, pdw++) {
+ *pdw = ~(*pdw);
+ }
+}
+
+/** as the name says...
+ */
+static void mapInvertDitherMap( pScanData ps )
+{
+ if( ps->DataInf.dwScanFlag & SCANDEF_Inverse ) {
+
+ ULong dw;
+ pULong pDither = (pULong)ps->a_bDitherPattern;
+
+ DBG( DBG_LOW, "mapInvertDitherMap()\n" );
+
+ mapInvertMap( ps );
+ for (dw = 0; dw < 16; dw++) {
+ pDither[dw] = ~pDither[dw];
+ }
+ }
+}
+
+/** build linear map...
+ */
+static void mapBuildLinearMap( pScanData ps )
+{
+ ULong i;
+
+ DBG( DBG_LOW, "mapBuildLinearMap()\n" );
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ for( i = 0; i < 4096; i++ ) {
+ ps->a_bMapTable[i] = (UChar)(i >> 4);
+ ps->a_bMapTable[4096+i] = (UChar)(i >> 4);
+ ps->a_bMapTable[8192+i] = (UChar)(i >> 4);
+ }
+
+ } else {
+
+ for( i = 0; i < 256; i++ ) {
+ ps->a_bMapTable[i] = (UChar)(i & 0xff);
+ ps->a_bMapTable[256+i] = (UChar)(i & 0xff);
+ ps->a_bMapTable[512+i] = (UChar)(i & 0xff);
+ }
+ }
+}
+
+/************************ exported functions *********************************/
+
+/** create a mapping table
+ * in the original code this map will be created by the TWAIN application
+ * and the is being downloaded to the driver, as I don't have the code
+ * we have to try to build up such a table here
+ */
+_LOC void MapInitialize( pScanData ps )
+{
+ mapBuildLinearMap( ps );
+ MapAdjust( ps, _MAP_MASTER );
+}
+
+/** setup dither maps
+ */
+_LOC void MapSetupDither( pScanData ps )
+{
+ DBG( DBG_LOW, "MapSetupDither() - %u\n", ps->DataInf.wAppDataType );
+
+ if( COLOR_HALFTONE == ps->DataInf.wAppDataType ) {
+
+ mapSetDitherMap( ps );
+ if (ps->DataInf.dwScanFlag & SCANDEF_Inverse)
+ mapInvertDitherMap( ps );
+ }
+}
+
+/** adjust acording to brightness and contrast
+ */
+_LOC void MapAdjust( pScanData ps, int which )
+{
+ ULong i, tabLen, dw;
+ ULong *pdw;
+ long b, c, tmp;
+
+ DBG( DBG_LOW, "MapAdjust(%u)\n", which );
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+ tabLen = 4096;
+ } else {
+ tabLen = 256;
+ }
+
+ /* adjust brightness (b) and contrast (c) using the function:
+ * s'(x,y) = (s(x,y) + b) * c
+ * b = [-127, 127]
+ * c = [0,2]
+ */
+
+ /* scale brightness and contrast...
+ */
+ b = ps->wBrightness * 192;
+ c = ps->wContrast + 100;
+
+ DBG( DBG_LOW, "brightness = %i -> %i\n", ps->wBrightness, (UChar)(b/100));
+ DBG( DBG_LOW, "contrast*100 = %i -> %i\n", ps->wContrast, (int)(c));
+
+ for( i = 0; i < tabLen; i++ ) {
+
+ if((_MAP_MASTER == which) || (_MAP_RED == which)) {
+ tmp = ((((long)ps->a_bMapTable[i] * 100) + b) *c ) / 10000;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ ps->a_bMapTable[i] = (UChar)tmp;
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_GREEN == which)) {
+ tmp = ((((long)ps->a_bMapTable[tabLen+i] * 100) + b) * c) / 10000;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ ps->a_bMapTable[tabLen+i] = (UChar)tmp;
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_BLUE == which)) {
+ tmp = ((((long)ps->a_bMapTable[tabLen*2+i] * 100) + b) * c) / 10000;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ ps->a_bMapTable[tabLen*2+i] = (UChar)tmp;
+ }
+ }
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative ) {
+ DBG( DBG_LOW, "inverting...\n" );
+
+ if((_MAP_MASTER == which) || (_MAP_RED == which)) {
+
+ DBG( DBG_LOW, "inverting RED map\n" );
+
+ pdw = (pULong)ps->a_bMapTable;
+
+ for( dw = tabLen / 4; dw; dw--, pdw++ )
+ *pdw = ~(*pdw);
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_GREEN == which)) {
+
+ DBG( DBG_LOW, "inverting GREEN map\n" );
+
+ pdw = (pULong)&ps->a_bMapTable[tabLen];
+
+ for( dw = tabLen / 4; dw; dw--, pdw++ )
+ *pdw = ~(*pdw);
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_BLUE == which)) {
+
+ DBG( DBG_LOW, "inverting BLUE map\n" );
+
+ pdw = (pULong)&ps->a_bMapTable[tabLen*2];
+
+ for( dw = tabLen / 4; dw; dw--, pdw++ )
+ *pdw = ~(*pdw);
+ }
+ }
+}
+
+/* END PLUSTEK-PP_MAP.C .....................................................*/
diff --git a/backend/plustek-pp_misc.c b/backend/plustek-pp_misc.c
new file mode 100644
index 0000000..d0c1ce7
--- /dev/null
+++ b/backend/plustek-pp_misc.c
@@ -0,0 +1,823 @@
+/* @file plustek-pp_misc.c
+ * @brief here we have some helpful functions
+*
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - moved the parport functions inside this module
+ * - now using the information, the parport-driver provides
+ * - for selecting the port-mode this driver uses
+ * - 0.33 - added code to use faster portmodes
+ * - 0.34 - added sample code for changing from ECP to PS/2 bidi mode
+ * - 0.35 - added Kevins' changes (new function miscSetFastMode())
+ * - moved function initPageSettings() to module models.c
+ * - 0.36 - added random generator
+ * - added additional debug messages
+ * - changed prototype of MiscInitPorts()
+ * - added miscPreemptionCallback()
+ * - 0.37 - changed inb_p/outb_p to macro calls (kernel-mode)
+ * - added MiscGetModelName()
+ * - added miscShowPortModes()
+ * - 0.38 - fixed a small bug in MiscGetModelName()
+ * - 0.39 - added forceMode support
+ * - 0.40 - no changes
+ * - 0.41 - merged Kevins' patch to make EPP(ECP) work
+ * - 0.42 - changed get_fast_time to _GET_TIME
+ * - changed include names
+ * - 0.43 - added LINUX_26 stuff
+ * - minor fixes
+ * - removed floating point stuff
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some definitions ********************************/
+
+#ifndef __KERNEL__
+# define PPA_PROBE_SPP 0x0001
+# define PPA_PROBE_PS2 0x0002
+# define PPA_PROBE_ECR 0x0010
+# define PPA_PROBE_EPP17 0x0100
+# define PPA_PROBE_EPP19 0x0200
+#else
+
+/* the parport driver in Kernel 2.4 has changed. It does report the
+ * possible modes in a different, more general way. As long, as
+ * we do not use the parport-module change mode facility, I assume
+ * the following correlations
+ */
+#if defined LINUX_24 || defined LINUX_26
+# define PARPORT_MODE_PCPS2 PARPORT_MODE_TRISTATE
+# define PARPORT_MODE_PCEPP PARPORT_MODE_EPP
+# define PARPORT_MODE_PCECPPS2 PARPORT_MODE_TRISTATE
+# define PARPORT_MODE_PCECPEPP PARPORT_MODE_EPP
+# define PARPORT_MODE_PCECR PARPORT_MODE_ECP
+#endif
+#endif
+
+#define _PP_A 16807 /**< multiplier */
+#define _PP_M 2147483647L /**< 2**31 - 1 */
+
+/*************************** some local vars *********************************/
+
+static int port_feature = 0;
+static long randomnum = 1;
+
+#ifdef __KERNEL__
+static int portIsClaimed[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 };
+
+MODELSTR; /**< a static char array (see plustek-pp.h) */
+
+#else
+static int portIsClaimed[_MAX_PTDEVS] = { 0, 0, 0, 0 };
+#endif
+
+/*************************** local functions *********************************/
+
+#ifdef __KERNEL__
+#ifdef LINUX_26
+
+static pScanData __ps = NULL;
+static int __pa = -1;
+
+/** callback from parport driver
+ */
+static void misc_attach(struct parport *port)
+{
+ DBG( DBG_LOW, "misc_attach\n" );
+
+ __ps->pp = NULL;
+ if( port->base == (unsigned long)__pa ) {
+ DBG( DBG_LOW, "Requested port (0x%02x) found\n", __pa );
+ DBG( DBG_LOW, "Port mode reported: (0x%04x)\n", port->modes );
+ __ps->pp = port;
+ }
+}
+
+static void misc_detach( struct parport *port )
+{
+ DBG( DBG_LOW, "misc_detach\n" );
+}
+
+static struct parport_driver pt_drv = {
+ .name = "pt_drv",
+ .attach = misc_attach,
+ .detach = misc_detach,
+};
+#endif
+
+/** display the avaialable port-modes
+ */
+#ifdef DEBUG
+static void miscShowPortModes( int modes )
+{
+ DBG( DBG_LOW, "parport-modi:" );
+
+ if( modes & PARPORT_MODE_PCSPP )
+ DBG( DBG_LOW, " SPP" );
+
+ if( modes & PARPORT_MODE_PCPS2 )
+ DBG( DBG_LOW, " PS/2" );
+
+ if( modes & PARPORT_MODE_PCEPP )
+ DBG( DBG_LOW, " EPP" );
+
+ if( modes & PARPORT_MODE_PCECR )
+ DBG( DBG_LOW, " ECP" );
+
+ if( modes & PARPORT_MODE_PCECPEPP )
+ DBG( DBG_LOW, " EPP(ECP)" );
+
+ if( modes & PARPORT_MODE_PCECPPS2 )
+ DBG( DBG_LOW, " PS/2(ECP)" );
+
+ DBG( DBG_LOW, "\n" );
+}
+#endif
+
+/** probe the parallel port
+ */
+static int initPortProbe( pScanData ps )
+{
+ int retv = 0;
+
+ /* clear the controls */
+ ps->IO.lastPortMode = 0xFFFF;
+
+ if( NULL != ps->pardev )
+ retv = ps->pardev->port->modes;
+ return retv;
+}
+
+/** will be called by the parport module when we already have access, but
+ * another module wants access to the port...
+ */
+static int miscPreemptionCallback( pVoid data )
+{
+ pScanData ps = (pScanData)data;
+
+ if( NULL != ps ) {
+
+ /* never release during scanning */
+ if( ps->DataInf.dwScanFlag & _SCANNER_SCANNING ) {
+ DBG( DBG_LOW, "no way!!!\n" );
+ return 1;
+ }
+ }
+
+ /* let the port go...*/
+ return 0;
+}
+
+/** depending on the reported possible port modes, we try to set a faster mode
+ * than SPP
+ */
+static int miscSetFastMode( pScanData ps )
+{
+ UChar a, b;
+
+ /*
+ * when previously found the EPP mode, break right here
+ */
+ if (( _PORT_EPP == ps->IO.portMode ) && (!(port_feature & PARPORT_MODE_PCECR)))
+ return _OK;
+
+ /* CHECK REMOVE: from here we should have SPP (Paranoia Code !) */
+ if (( _PORT_SPP != ps->IO.portMode ) && (!(port_feature & PARPORT_MODE_PCECR)))
+ return _OK;
+
+ DBG(DBG_LOW, "Trying faster mode...\n" );
+
+ /*
+ * ECP mode usually has sub-modes of EPP and/or PS2.
+ * First we try to set EPP
+ */
+ if((port_feature & PARPORT_MODE_PCECR) &&
+ (port_feature & PARPORT_MODE_PCECPEPP)){
+
+ DBG(DBG_LOW, "Attempting to set EPP from ECP mode.\n" );
+
+ a = _INB_ECTL(ps); /* get current ECR */
+ ps->IO.lastPortMode = a; /* save it for restoring later */
+ a = (a & 0x1F) | 0x80; /* set to EPP */
+ _OUTB_ECTL(ps, a); /* write it back */
+ _DO_UDELAY(1);
+
+ /*
+ * It is probably unnecessary to
+ * do this check but it makes me feel better
+ */
+ b = _INB_ECTL(ps); /* check to see if port set */
+ if( a == b ) {
+ DBG( DBG_LOW, "Port is set to (ECP) EPP mode.\n" );
+ ps->IO.portMode = _PORT_EPP;
+ return _OK;
+
+ } else {
+ DBG( DBG_LOW, "Port could not be set to (ECP) EPP mode. "
+ "Using SPP mode.\n" );
+ _OUTB_ECTL(ps,(Byte)ps->IO.lastPortMode); /* restore */
+ _DO_UDELAY(1);
+ ps->IO.portMode = _PORT_SPP;
+
+ /* go ahead and try with other settings...*/
+ }
+ }
+
+ /* If port cannot be set to EPP, try PS2 */
+ if((port_feature & PARPORT_MODE_PCECR) &&
+ (port_feature & PARPORT_MODE_PCECPPS2)) {
+
+ DBG(DBG_LOW, "Attempting to set PS2 from ECPPS2 mode.\n" );
+
+ a = _INB_ECTL(ps); /* get current ECR */
+ ps->IO.lastPortMode = a; /* save it for restoring later */
+
+ /* set to Fast Centronics/bi-directional/PS2 */
+ a = (a & 0x1F) | 0x20;
+ _OUTB_ECTL(ps,a); /* write it back */
+ _DO_UDELAY(1);
+
+ /*
+ * It is probably unnecessary to do this check
+ * but it makes me feel better
+ */
+ b = _INB_ECTL(ps); /* check to see if port set */
+ if (a == b) {
+ DBG(DBG_LOW, "Port is set to (ECP) PS2 bidirectional mode.\n");
+ ps->IO.portMode = _PORT_BIDI;
+ return _OK;
+ } else {
+ DBG(DBG_LOW, "Port could not be set to (ECP) PS2 mode. "
+ "Using SPP mode.\n");
+ a = ps->IO.lastPortMode & 0x1F;
+ _OUTB_ECTL(ps, a); /* set ECP ctrl to SPP */
+ _DO_UDELAY(1);
+ ps->IO.portMode = _PORT_SPP;
+
+ /* next mode, last attempt... */
+ }
+ }
+
+ /*
+ * Some BIOS/cards have only a Bi-directional/PS2 mode (no EPP).
+ * Make one last attemp to set to PS2 mode.
+ */
+ if ( port_feature & PARPORT_MODE_PCPS2 ){
+
+ DBG(DBG_LOW, "Attempting to set PS2 mode.\n" );
+
+ a = _INB_CTRL(ps); /* get current setting of control register*/
+ ps->IO.lastPortMode = a; /* save it for restoring later */
+ a = a | 0x20; /* set bit 5 of control reg */
+ _OUTB_CTRL(ps,a); /* set to Fast Centronics/bi-directional/PS2 */
+ _DO_UDELAY(1);
+ a = 0;
+
+ _OUTB_DATA(ps,0x55);
+ _DO_UDELAY(1);
+ if ((inb(ps->IO.portBase)) != 0x55) /* read data */
+ a++;
+
+ _OUTB_DATA(ps,0xAA);
+ _DO_UDELAY(1);
+
+ if (_INB_DATA(ps) != 0xAA) /* read data */
+ a++;
+
+ if( 2 == a ) {
+ DBG(DBG_LOW, "Port is set to PS2 bidirectional mode.\n");
+ ps->IO.portMode = _PORT_BIDI;
+ return _OK;
+
+ } else {
+ DBG(DBG_LOW, "Port could not be set to PS2 mode. "
+ "Using SPP mode.\n");
+ _OUTB_CTRL(ps,(Byte)ps->IO.lastPortMode); /* restore */
+ _DO_UDELAY(1);
+ ps->IO.portMode = _PORT_SPP;
+ }
+ }
+
+ /* reaching this point, we're back in SPP mode and there's no need
+ * to restore at shutdown...
+ */
+ ps->IO.lastPortMode = 0xFFFF;
+
+ return _OK;
+}
+
+/** check the state of the par-port and switch to EPP-mode if possible
+ */
+static int miscSetPortMode( pScanData ps )
+{
+ /* try to detect the port settings, SPP seems to work in any case ! */
+ port_feature = initPortProbe( ps );
+
+#ifdef DEBUG
+ miscShowPortModes( port_feature );
+#endif
+
+ switch( ps->IO.forceMode ) {
+
+ case 1:
+ DBG( DBG_LOW, "Use of SPP-mode enforced\n" );
+ ps->IO.portMode = _PORT_SPP;
+ return _OK;
+ break;
+
+ case 2:
+ DBG( DBG_LOW, "Use of EPP-mode enforced\n" );
+ ps->IO.portMode = _PORT_EPP;
+ return _OK;
+ break;
+
+ default:
+ break;
+ }
+
+ if( !(port_feature & PARPORT_MODE_PCEPP)) {
+
+ if( !(port_feature & PARPORT_MODE_PCSPP )) {
+ _PRINT("\nThis Port supports not the SPP- or EPP-Mode\n" );
+ _PRINT("Please activate SPP-Mode, EPP-Mode or\nEPP + ECP-Mode!\n");
+ return _E_NOSUPP;
+ } else {
+ DBG(DBG_LOW, "Using SPP-mode\n" );
+ ps->IO.portMode = _PORT_SPP;
+ }
+ } else {
+ DBG(DBG_LOW, "Using EPP-mode\n" );
+ ps->IO.portMode = _PORT_EPP;
+ }
+
+ /* else try to set to a faster mode than SPP */
+ return miscSetFastMode( ps );
+}
+#endif
+
+/** miscNextLongRand() -- generate 2**31-2 random numbers
+**
+** public domain by Ray Gardner
+**
+** based on "Random Number Generators: Good Ones Are Hard to Find",
+** S.K. Park and K.W. Miller, Communications of the ACM 31:10 (Oct 1988),
+** and "Two Fast Implementations of the 'Minimal Standard' Random
+** Number Generator", David G. Carta, Comm. ACM 33, 1 (Jan 1990), p. 87-88
+**
+** linear congruential generator f(z) = 16807 z mod (2 ** 31 - 1)
+**
+** uses L. Schrage's method to avoid overflow problems
+*/
+static Long miscNextLongRand( Long seed )
+{
+ ULong lo, hi;
+
+ lo = _PP_A * (Long)(seed & 0xFFFF);
+ hi = _PP_A * (Long)((ULong)seed >> 16);
+ lo += (hi & 0x7FFF) << 16;
+ if (lo > _PP_M) {
+
+ lo &= _PP_M;
+ ++lo;
+ }
+ lo += hi >> 15;
+ if (lo > _PP_M) {
+ lo &= _PP_M;
+ ++lo;
+ }
+
+ return (Long)lo;
+}
+
+/** initialize the random number generator
+ */
+static void miscSeedLongRand( long seed )
+{
+ randomnum = seed ? (seed & _PP_M) : 1; /* nonzero seed */
+}
+
+/************************ exported functions *********************************/
+
+/** allocate and initialize some memory for the scanner structure
+ */
+_LOC pScanData MiscAllocAndInitStruct( void )
+{
+ pScanData ps;
+
+ ps = (pScanData)_KALLOC(sizeof(ScanData), GFP_KERNEL);
+
+ if( NULL != ps ) {
+ MiscReinitStruct( ps );
+ }
+
+ DBG( DBG_HIGH, "ScanData = 0x%08lx\n", (unsigned long)ps );
+ return ps;
+}
+
+/** re-initialize the memory for the scanner structure
+ */
+_LOC int MiscReinitStruct( pScanData ps )
+{
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ memset( ps, 0, sizeof(ScanData));
+
+ /* first init all constant stuff in ScanData
+ */
+ ps->bCurrentSpeed = 1;
+ ps->pbMapRed = ps->a_bMapTable;
+ ps->pbMapGreen = &ps->a_bMapTable[256];
+ ps->pbMapBlue = &ps->a_bMapTable[512];
+ ps->sCaps.wIOBase = _NO_BASE;
+
+ /* use memory address to seed the generator */
+ miscSeedLongRand((long)ps);
+
+ DBG( DBG_HIGH, "Init settings done\n" );
+ return _OK;
+}
+
+/** in USER-Mode: probe the specified port and try to get the port-mode
+ * in KERNEL-Mode: only use the modes, the driver returns
+ */
+_LOC int MiscInitPorts( pScanData ps, int port )
+{
+#ifdef __KERNEL__
+ int status;
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ /*
+ * Get access to the ports
+ */
+ ps->IO.portBase = (UShort)port;
+
+ status = miscSetPortMode(ps);
+
+ if( _OK != status ) {
+ ps->sCaps.wIOBase = _NO_BASE;
+ ps->IO.portBase = _NO_BASE;
+ return status;
+ }
+
+ /*
+ * the port settings
+ */
+ ps->IO.pbSppDataPort = (UShort)port;
+ ps->IO.pbStatusPort = (UShort)port+1;
+ ps->IO.pbControlPort = (UShort)port+2;
+ ps->IO.pbEppDataPort = (UShort)port+4;
+
+#else
+ int mode, mts;
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ if( SANE_STATUS_GOOD != sanei_pp_getmodes( ps->pardev, &mode )) {
+ DBG( DBG_HIGH, "Cannot get port mode!\n" );
+ return _E_NO_PORT;
+ }
+
+ ps->IO.portMode = _PORT_NONE;
+ mts = -1;
+ if( mode & SANEI_PP_MODE_SPP ) {
+ DBG( DBG_LOW, "Setting SPP-mode\n" );
+ ps->IO.portMode = _PORT_SPP;
+ mts = SANEI_PP_MODE_SPP;
+ }
+ if( mode & SANEI_PP_MODE_BIDI ) {
+ DBG( DBG_LOW, "Setting PS/2-mode\n" );
+ ps->IO.portMode = _PORT_BIDI;
+ mts = SANEI_PP_MODE_BIDI;
+ }
+ if( mode & SANEI_PP_MODE_EPP ) {
+ DBG( DBG_LOW, "Setting EPP-mode\n" );
+ ps->IO.portMode = _PORT_EPP;
+ mts = SANEI_PP_MODE_EPP;
+ }
+ if( mode & SANEI_PP_MODE_ECP ) {
+ DBG( DBG_HIGH, "ECP detected --> not supported\n" );
+ }
+
+ if( sanei_pp_uses_directio()) {
+ DBG( DBG_LOW, "We're using direct I/O\n" );
+ } else {
+ DBG( DBG_LOW, "We're using libIEEE1284 I/O\n" );
+ }
+
+ if( ps->IO.portMode == _PORT_NONE ) {
+ DBG( DBG_HIGH, "None of the portmodes is supported.\n" );
+ return _E_NOSUPP;
+ }
+
+ sanei_pp_setmode( ps->pardev, mts );
+ _VAR_NOT_USED( port );
+#endif
+ return _OK;
+}
+
+/** Function to restore the port
+ */
+_LOC void MiscRestorePort( pScanData ps )
+{
+#ifdef __KERNEL__
+ if( 0 == ps->IO.pbSppDataPort )
+ return;
+#endif
+
+ DBG(DBG_LOW,"MiscRestorePort()\n");
+
+ /* don't restore if not necessary */
+ if( 0xFFFF == ps->IO.lastPortMode ) {
+ DBG(DBG_LOW,"- no need to restore portmode !\n");
+ return;
+ }
+
+ /*Restore Port-Mode*/
+#ifdef __KERNEL__
+ if( port_feature & PARPORT_MODE_PCECR ){
+ _OUTB_ECTL( ps, (Byte)ps->IO.lastPortMode );
+ _DO_UDELAY(1);
+ } else {
+ _OUTB_CTRL( ps, (Byte)ps->IO.lastPortMode );
+ _DO_UDELAY(1);
+ }
+#else
+ if( port_feature & PPA_PROBE_ECR ){
+ _OUTB_ECTL(ps,ps->IO.lastPortMode);
+ }
+#endif
+}
+
+/** Initializes a timer.
+ * @param timer - pointer to the timer to start
+ * @param us - timeout value in micro-seconds
+ */
+_LOC void MiscStartTimer( TimerDef *timer , unsigned long us)
+{
+ struct timeval start_time;
+
+#ifdef __KERNEL__
+ _GET_TIME( &start_time );
+#else
+ gettimeofday(&start_time, NULL);
+#endif
+
+ *timer = (TimerDef)start_time.tv_sec * 1000000 + (TimerDef)start_time.tv_usec + us;
+}
+
+/** Checks if a timer has been expired or not. In Kernel-mode, the scheduler
+ * will also be triggered, if the timer has not been expired.
+ * @param timer - pointer to the timer to check
+ * @return Function returns _E_TIMEOUT when the timer has been expired,
+ * otherwise _OK;
+ */
+_LOC int MiscCheckTimer( TimerDef *timer )
+{
+ struct timeval current_time;
+
+#ifdef __KERNEL__
+ _GET_TIME( &current_time );
+#else
+ gettimeofday(&current_time, NULL);
+#endif
+
+ if ((TimerDef)current_time.tv_sec * 1000000 + (TimerDef)current_time.tv_usec > *timer) {
+ return _E_TIMEOUT;
+ } else {
+#ifdef __KERNEL__
+ schedule();
+/*#else
+ sched_yield();
+*/
+#endif
+ return _OK;
+ }
+}
+
+/** Checks the function pointers
+ * @param ps - pointer to the scanner data structure.
+ * @return Function returns _TRUE if everything is okay and _FALSE if a NULL
+ * ptr has been detected.
+ */
+#ifdef DEBUG
+_LOC Bool MiscAllPointersSet( pScanData ps )
+{
+ int i;
+ unsigned long *ptr;
+
+ for( ptr = (unsigned long *)&ps->OpenScanPath, i = 1;
+ ptr <= (unsigned long *)&ps->ReadOneImageLine; ptr++, i++ ) {
+
+ if( NULL == (pVoid)*ptr ) {
+ DBG( DBG_HIGH, "Function pointer not set (pos = %d) !\n", i );
+ return _FALSE;
+ }
+ }
+
+ return _TRUE;
+}
+#endif
+
+/** registers this driver to use port "portAddr" (KERNEL-Mode only)
+ * @param ps - pointer to the scanner data structure.
+ * @param portAddr -
+ */
+_LOC int MiscRegisterPort( pScanData ps, int portAddr )
+{
+#ifndef __KERNEL__
+ DBG( DBG_LOW, "Assigning port handle %i\n", portAddr );
+ ps->pardev = portAddr;
+#else
+
+#ifdef LINUX_26
+ __ps = ps;
+ __pa = portAddr;
+
+ DBG( DBG_LOW, "Requested port at 0x%02x\n", portAddr );
+
+ if( parport_register_driver(&pt_drv)) {
+ /* Failed; nothing we can do. */
+ return _E_REGISTER;
+ }
+
+#else
+ struct parport *pp = NULL;
+
+ DBG( DBG_LOW, "Requested port at 0x%02x\n", portAddr );
+
+ pp = parport_enumerate();
+ ps->pardev = NULL;
+
+ if( NULL == pp ) {
+ return _E_PORTSEARCH;
+ }
+
+ /* go through the list
+ */
+ for( ps->pp = NULL; NULL != pp; ) {
+
+ if( pp->base == (unsigned long)portAddr ) {
+ DBG( DBG_LOW, "Requested port (0x%02x) found\n", portAddr );
+ DBG( DBG_LOW, "Port mode reported: (0x%04x)\n", pp->modes );
+ ps->pp = pp;
+ break;
+ }
+ pp = pp->next;
+ }
+#endif
+
+ if( NULL == ps->pp ) {
+ printk("PORT not found!!!\n");
+ return _E_NO_PORT;
+ }
+
+ /*
+ * register this device
+ */
+ ps->pardev = parport_register_device( ps->pp, "Plustek Driver",
+ miscPreemptionCallback, NULL, NULL, 0, (pVoid)ps );
+
+ if( NULL == ps->pardev ) {
+ return _E_REGISTER;
+ }
+
+ DBG( DBG_LOW, "Port for device %u registered\n", ps->devno );
+#endif
+
+ portIsClaimed[ps->devno] = 0;
+ return _OK;
+}
+
+/** unregisters the port from driver
+ */
+_LOC void MiscUnregisterPort( pScanData ps )
+{
+#ifdef __KERNEL__
+ if( NULL != ps->pardev ) {
+ DBG( DBG_LOW, "Port unregistered\n" );
+ parport_unregister_device( ps->pardev );
+ }
+#ifdef LINUX_26
+ parport_unregister_driver( &pt_drv );
+#endif
+#else
+ sanei_pp_close( ps->pardev );
+#endif
+}
+
+/** Try to claim the port
+ * @param ps - pointer to the scanner data structure.
+ * @return Function returns _OK on success, otherwise _E_BUSY.
+ */
+_LOC int MiscClaimPort( pScanData ps )
+{
+ if( 0 == portIsClaimed[ps->devno] ) {
+
+ DBG( DBG_HIGH, "Try to claim the parport\n" );
+#ifdef __KERNEL__
+ if( 0 != parport_claim( ps->pardev )) {
+#else
+ if( SANE_STATUS_GOOD != sanei_pp_claim( ps->pardev )) {
+#endif
+ return _E_BUSY;
+ }
+ }
+ portIsClaimed[ps->devno]++;
+ return _OK;
+}
+
+/** Release previously claimed port
+ * @param ps - pointer to the scanner data structure
+ */
+_LOC void MiscReleasePort( pScanData ps )
+{
+ if( portIsClaimed[ps->devno] > 0 ) {
+ portIsClaimed[ps->devno]--;
+
+ if( 0 == portIsClaimed[ps->devno] ) {
+ DBG( DBG_HIGH, "Releasing parport\n" );
+#ifdef __KERNEL__
+ parport_release( ps->pardev );
+#else
+ sanei_pp_release( ps->pardev );
+#endif
+ }
+ }
+}
+
+/** Get random number
+ * @return a random number.
+ */
+_LOC Long MiscLongRand( void )
+{
+ randomnum = miscNextLongRand( randomnum );
+
+ return randomnum;
+}
+
+/** According to the id, the function returns a pointer to the model name
+ * @param id - internal id of the various scanner models.
+ * @return a pointer to the model-string.
+ */
+_LOC const char *MiscGetModelName( UShort id )
+{
+ DBG( DBG_HIGH, "MiscGetModelName - id = %i\n", id );
+
+ if( MODEL_OP_PT12 < id )
+ return ModelStr[0];
+
+ return ModelStr[id];
+}
+
+/* END PLUSTEK-PP_MISC.C ....................................................*/
diff --git a/backend/plustek-pp_models.c b/backend/plustek-pp_models.c
new file mode 100644
index 0000000..55029db
--- /dev/null
+++ b/backend/plustek-pp_models.c
@@ -0,0 +1,614 @@
+/* @file plustek-pp_models.c
+ * @brief model specific stuff
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - no changes
+ * - 0.33 - no changes
+ * - 0.34 - no changes
+ * - 0.35 - added some comments
+ * - did some fine tuning on the 9630P and 12000P/9600P models
+ * - moved function initPageSettings() to this module
+ * - 0.36 - as the ps->MaxWideLineBlks and ps->MaxWideLineLen are only used
+ * for the OP 4800, it has been removed from pScanData
+ * - changed settings of OP600 according to the Primax Direct 4800 tests
+ * - removed dwPreferSize from struct ScannerCaps
+ * - fixed the 5seconds bed-hit problem for ASIC 96001/3 based models
+ * - changes, due to define renaming
+ * - 0.37 - added ButtonCount init
+ * - added A3I model
+ * - added functions modelInitCaps(), modelInitMotor() and
+ * modelSetBufferSizes()
+ * - 0.38 - added P12 stuff
+ * - code cleanup
+ * - 0.39 - no changes
+ * - 0.40 - changed back to build 0.39-3 (disabled A3I stuff)
+ * - 0.41 - added _OVR_PLUSTEK_4800P switch
+ * - 0.42 - added SFLAG_CUSTOM_GAMMA to capabilities
+ * - added _OVR_PRIMAX_4800D30 switch
+ * - changed include names
+ * - 0.43 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ * initialize the extension according to the page size...
+ */
+static void modelInitPageSettings( pScanData ps )
+{
+ DBG(DBG_LOW, "modelInitPageSettings()\n" );
+
+ if( MODEL_OP_9630PL == ps->sCaps.Model )
+ ps->dwScannerSize = _SCANSIZE_LEGAL;
+ else if( MODEL_OP_A3I == ps->sCaps.Model )
+ ps->dwScannerSize = _SCANSIZE_A3;
+ else
+ ps->dwScannerSize = _SCANSIZE_A4;
+
+ /* default width for all but A3 - 8.5"* 300dpi (_MEASURE_BASE) */
+ ps->sCaps.wMaxExtentX = 2550;
+
+ /* this applies to all scanners but the A3 model */
+ ps->LensInf.rExtentX.wMin = 150;
+ ps->LensInf.rExtentX.wDef = 2550;
+ ps->LensInf.rExtentX.wMax = 2550;
+ ps->LensInf.rExtentX.wPhyMax = 2500;
+
+ ps->LensInf.rExtentY.wMin = 150;
+
+ ps->LensInf.wBeginX = 0;
+ ps->LensInf.wBeginY = 0;
+
+ switch( ps->dwScannerSize ) {
+
+ case _SCANSIZE_A4:
+ /* 11.69 inches */
+ DBG( DBG_LOW, "A4 set\n" );
+ ps->sCaps.wMaxExtentY =
+ ps->LensInf.rExtentY.wDef =
+ ps->LensInf.rExtentY.wMax =
+ ps->LensInf.rExtentY.wPhyMax = _MEASURE_BASE * 11.6934;
+ break;
+
+ case _SCANSIZE_A3:
+ /* 17 inches */
+ DBG( DBG_LOW, "A3 set\n" );
+ ps->sCaps.wMaxExtentY =
+ ps->LensInf.rExtentY.wMax =
+ ps->LensInf.rExtentY.wDef =
+ ps->LensInf.rExtentY.wPhyMax = _MEASURE_BASE * 17;
+
+ /* _MEASURE_BASE * 11.69 */
+ ps->sCaps.wMaxExtentX =
+ ps->LensInf.rExtentX.wDef =
+ ps->LensInf.rExtentX.wMax = 3507;
+ ps->LensInf.rExtentX.wPhyMax = 3500;
+ break;
+
+ case _SCANSIZE_LETTER:
+ /* 11 inches */
+ DBG( DBG_LOW, "Letter set\n" );
+ ps->sCaps.wMaxExtentY =
+ ps->LensInf.rExtentY.wDef =
+ ps->LensInf.rExtentY.wMax =
+ ps->LensInf.rExtentY.wPhyMax = _MEASURE_BASE * 11;
+ break;
+
+ case _SCANSIZE_LEGAL:
+ /* 14 inches */
+ DBG( DBG_LOW, "Legal set\n" );
+ ps->sCaps.wMaxExtentY =
+ ps->LensInf.rExtentY.wDef =
+ ps->LensInf.rExtentY.wMax =
+ ps->LensInf.rExtentY.wPhyMax = _MEASURE_BASE * 14;
+ }
+
+ /*
+ * add this value to avoid the problems in binary mode
+ */
+ ps->LensInf.rExtentY.wMax += 64;
+
+ /* set the DPI stuff */
+ ps->LensInf.rDpiX.wMin = 16;
+ ps->LensInf.rDpiX.wDef = 50;
+ ps->LensInf.rDpiX.wMax = (ps->PhysicalDpi * 16);
+ ps->LensInf.rDpiX.wPhyMax = ps->PhysicalDpi;
+ ps->LensInf.rDpiY.wMin = 16;
+ ps->LensInf.rDpiY.wDef = 50;
+ ps->LensInf.rDpiY.wMax = (ps->PhysicalDpi * 16);
+ ps->LensInf.rDpiY.wPhyMax = (ps->PhysicalDpi * 2);
+}
+
+/*.............................................................................
+ * set the scanner capabilities
+ */
+static void modelInitCaps( pScanData ps )
+{
+ ps->sCaps.wIOBase = _NO_BASE;
+ ps->sCaps.dwFlag = SFLAG_CUSTOM_GAMMA;
+}
+
+/*.............................................................................
+ * set the motor stuff
+ */
+static void modelInitMotor( pScanData ps )
+{
+ if( _ASIC_IS_96001 == ps->sCaps.AsicID ) {
+ ps->FullStep = _MotorFullStep96001;
+ ps->MotorOn = _MotorOn96001;
+ ps->IgnorePF = _MotorIgnorePF96001;
+ ps->StepMask = ~ps->FullStep;
+ } else {
+ ps->FullStep = _Motor1FullStep;
+ ps->MotorOn = _MotorOn;
+ ps->IgnorePF = _MotorIgnorePF;
+ ps->StepMask = _MotorStepMask;
+ }
+
+ ps->BackwardSteps = 4000;
+}
+
+/*.............................................................................
+ * according to the models' capabilities, set the buffer stuff
+ */
+static void modelSetBufferSizes( pScanData ps )
+{
+ /* should depend on the scan-area !!!! */
+ if( 400 == ps->PhysicalDpi ) {
+
+ /* assuming a A3I */
+ ps->BufferSizeBase = 3517;
+ ps->BufferForColorRunTable = (5500 * 4); /* might be 17" * 800dpi !!! */
+
+ } else if( 600 == ps->PhysicalDpi ) {
+
+ ps->BufferSizeBase = 2560;
+ ps->BufferForColorRunTable = (5500 * 4);
+
+ } else {
+ ps->BufferSizeBase = 1280;
+ ps->BufferForColorRunTable = 9000;
+ }
+
+ ps->BufferSizePerModel = ps->BufferSizeBase * 2;
+ ps->BufferForDataRead1 = ps->BufferSizePerModel * 3;
+
+ /* patch that for the 600 DPI models OP9630 etc.*/
+ if(( 300 != ps->PhysicalDpi) && (_ASIC_IS_96003 == ps->sCaps.AsicID))
+ ps->BufferForDataRead1 += 300;
+
+ ps->BufferFor1stColor = (ps->BufferSizePerModel * 17);
+ ps->BufferFor2ndColor = (ps->BufferSizePerModel * 9);
+ ps->TotalBufferRequire = (ps->BufferFor1stColor +
+ ps->BufferFor2ndColor +
+ ps->BufferForDataRead1 +
+ ps->BufferForColorRunTable );
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * set the model to 4800
+ */
+_LOC void ModelSet4800( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSet4800()\n" );
+
+ /* has 96001 ASIC */
+ ps->sCaps.AsicID = _ASIC_IS_96001;
+ ps->sCaps.Model = MODEL_OP_4800P;
+ ps->Device.buttons = 0;
+ ps->Device.ModelCtrl = (_ModelDpi300 | _ModelMemSize32k96001 | _ModelWhiteIs0);
+ ps->Device.DataOriginX = 72;
+
+ ps->PhysicalDpi = 300;
+ ps->TimePerLine = 0x30;
+ ps->Offset70 = 70;
+
+ modelSetBufferSizes( ps );
+
+ ps->a_wGrayInitTime[0] = 220; /* _EppTimeForOthers */
+ ps->a_wGrayInitTime[1] = 720; /* _SppTimeForOthers */
+ ps->a_wGrayInitTime[2] = 360; /* _BidirTimeForOthers */
+ ps->a_wColorInitTime[0] = 500; /* _EppTimeForColor */
+ ps->a_wColorInitTime[1] = 1680; /* _SppTimeForColor */
+ ps->a_wColorInitTime[2] = 1100; /* _BidirTimeForColor */
+
+ ps->AsicRedColor = _ASIC_REDCOLOR;
+ ps->AsicGreenColor = _ASIC_GREENCOLOR;
+ ps->RedDataReady = _RED_DATA_READY;
+ ps->GreenDataReady = _GREEN_DATA_READY;
+
+ /*
+ * used for shading stuff (see dac.c)
+ */
+ ps->FBKScanLineBlks = 5;
+ ps->FBKScanLineLenBase = 1024;
+ ps->FBKScanLineLen = (ps->FBKScanLineLenBase * 3);
+
+ ps->ShadingBufferSize = ps->FBKScanLineLen;
+ ps->ShadingBankSize = (ps->FBKScanLineLenBase * 4);
+ ps->ShadingBankRed = (_MemBankSize4k96001 | 0x3a);
+ ps->ShadingBankGreen = (_MemBankSize4k96001 | 0x3e);
+ ps->ShadingBankBlue = (_MemBankSize4k96001 | 0x3c);
+ ps->ShadingScanLineBlks = 6;
+ ps->ShadingScanLineLen = (ps->BufferSizeBase * 3);
+ ps->OneScanLineLen = (ps->BufferSizePerModel * 3);
+
+ modelInitMotor( ps );
+ modelInitCaps ( ps );
+ modelInitPageSettings( ps );
+
+ DBG( DBG_LOW, "ModelSet4800() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to 4830
+ */
+_LOC void ModelSet4830( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSet4830()\n" );
+
+ /* has 96003 ASIC */
+ ps->sCaps.Model = MODEL_OP_4830P;
+ if( _OVR_PRIMAX_4800D30 == ps->ModelOverride ) {
+ DBG( DBG_LOW, "Model Override --> Primax 4800D 30\n" );
+ ps->sCaps.Model = MODEL_PMX_4800D3;
+ }
+ ps->sCaps.AsicID = _ASIC_IS_96003;
+ ps->Device.buttons = 1;
+ ps->Device.ModelCtrl = (_ModelDpi300 | _ModelMemSize32k3 | _ModelWhiteIs0);
+ ps->Device.DataOriginX = 72;
+
+ ps->PhysicalDpi = 300;
+ ps->TimePerLine = 0x30;
+ ps->Offset70 = 70;
+
+ modelSetBufferSizes( ps );
+
+ ps->a_wGrayInitTime[0] = 220; /* _EppTimeForOthers */
+ ps->a_wGrayInitTime[1] = 720; /* _SppTimeForOthers */
+ ps->a_wGrayInitTime[2] = 360; /* _BidirTimeForOthers */
+ ps->a_wColorInitTime[0] = 500; /* _EppTimeForColor */
+ ps->a_wColorInitTime[1] = 1680; /* _SppTimeForColor */
+ ps->a_wColorInitTime[2] = 1100; /* _BidirTimeForColor */
+
+ ps->AsicRedColor = _ASIC_REDCOLOR;
+ ps->AsicGreenColor = _ASIC_GREENCOLOR;
+ ps->RedDataReady = _RED_DATA_READY;
+ ps->GreenDataReady = _GREEN_DATA_READY;
+
+ /*
+ * used for shading stuff (see dac.c)
+ */
+ ps->FBKScanLineBlks = 5;
+ ps->FBKScanLineLenBase = 1024;
+ ps->FBKScanLineLen = (ps->FBKScanLineLenBase * 3);
+
+ ps->ShadingBufferSize = ps->FBKScanLineLen;
+ ps->ShadingBankSize = (ps->FBKScanLineLenBase * 4);
+ ps->ShadingBankRed = (_MemBankSize4k | 0x3a);
+ ps->ShadingBankGreen = (_MemBankSize4k | 0x3e);
+ ps->ShadingBankBlue = (_MemBankSize4k | 0x3c);
+ ps->ShadingScanLineBlks = 6;
+ ps->ShadingScanLineLen = (ps->BufferSizeBase * 3);
+ ps->OneScanLineLen = (ps->BufferSizePerModel * 3);
+
+ modelInitMotor( ps );
+ modelInitCaps ( ps );
+ modelInitPageSettings( ps );
+
+ DBG( DBG_LOW, "ModelSet4830() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to 600, tested on a Primax Direct 4800 and OP600
+ */
+_LOC void ModelSet600( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSet600()\n" );
+
+ /*
+ * set to 4830 first, then do the differences
+ */
+ ModelSet4830( ps );
+ ps->Device.buttons = 0;
+
+ if( _OVR_PLUSTEK_4800P == ps->ModelOverride ) {
+
+ DBG( DBG_LOW, "Model Override --> OpticPro4800\n" );
+ ps->sCaps.Model = MODEL_OP_4800P;
+
+ } else if( _OVR_PRIMAX_4800D == ps->ModelOverride ) {
+
+ DBG( DBG_LOW, "Model Override --> Primax 4800D\n" );
+ ps->sCaps.Model = MODEL_PMX_4800D;
+
+ } else {
+
+ ps->sCaps.Model = MODEL_OP_600P;
+
+ /* for Plustek OpticPro 600P it's necessary to swap Red and Green
+ * changed by mh moloch@nikocity.de
+ */
+ ps->AsicRedColor = _ASIC_GREENCOLOR;
+ ps->AsicGreenColor = _ASIC_REDCOLOR;
+ }
+
+ DBG( DBG_LOW, "ModelSet600() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to 12000P, 96000P (tested on a OP96000P)
+ */
+_LOC void ModelSet12000( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSet12000()\n" );
+
+ /*
+ * set to 9630 first, then do the differences
+ */
+ ModelSet9630( ps );
+ ps->Device.buttons = 0;
+ ps->sCaps.Model = MODEL_OP_12000P;
+
+ /*
+ * swapped Red and Green for 12000P/96000P
+ */
+ ps->AsicRedColor = _ASIC_GREENCOLOR;
+ ps->AsicGreenColor = _ASIC_REDCOLOR;
+ ps->RedDataReady = _GREEN_DATA_READY;
+ ps->GreenDataReady = _RED_DATA_READY;
+
+ DBG( DBG_LOW, "ModelSet12000() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to A3I
+ */
+_LOC void ModelSetA3I( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSetA3I()\n" );
+
+ /*
+ * has 96003 ASIC
+ */
+ ps->Device.buttons = 1;
+ ps->sCaps.Model = MODEL_OP_A3I;
+ ps->sCaps.AsicID = _ASIC_IS_96003;
+
+ ps->Device.ModelCtrl = (_ModelDpi400 | _ModelMemSize128k4 | _ModelWhiteIs0);
+ ps->Device.DataOriginX = 164;
+
+ ps->PhysicalDpi = 400;
+ ps->TimePerLine = 0x50;
+ ps->Offset70 = 145;
+
+ modelSetBufferSizes( ps );
+
+ ps->a_wGrayInitTime[0] = 133; /* _EppTimeForOthers */
+ ps->a_wGrayInitTime[1] = 720; /* _SppTimeForOthers */
+ ps->a_wGrayInitTime[2] = 300; /* _BidirTimeForOthers */
+ ps->a_wColorInitTime[0] = 400; /* _EppTimeForColor */
+ ps->a_wColorInitTime[1] = 1800; /* _SppTimeForColor */
+ ps->a_wColorInitTime[2] = 800; /* _BidirTimeForColor */
+
+ ps->AsicRedColor = _ASIC_GREENCOLOR;
+ ps->AsicGreenColor = _ASIC_REDCOLOR;
+ ps->RedDataReady = _GREEN_DATA_READY;
+ ps->GreenDataReady = _RED_DATA_READY;
+
+ ps->FBKScanLineBlks = 10;
+ ps->FBKScanLineLenBase = 2048;
+ ps->FBKScanLineLen = (ps->FBKScanLineLenBase * 3);
+
+ ps->ShadingBufferSize = (1024 * 7);
+ ps->ShadingBankSize = 8192;
+ ps->ShadingBankRed = (_MemBankSize8k | 0x34);
+ ps->ShadingBankGreen = (_MemBankSize8k | 0x3c);
+ ps->ShadingBankBlue = (_MemBankSize8k | 0x38);
+ ps->ShadingScanLineBlks = 10;
+ ps->ShadingScanLineLen = (ps->BufferSizeBase * 3);
+ ps->OneScanLineLen = (ps->ShadingScanLineLen * 2);
+
+ modelInitMotor( ps );
+ ps->BackwardSteps = 9000;
+
+ modelInitCaps( ps );
+ modelInitPageSettings( ps );
+
+ /*
+ * need to double the vals
+ */
+ ps->LensInf.rExtentX.wMax *= 2;
+ ps->LensInf.rExtentX.wPhyMax *= 2;
+ ps->LensInf.rExtentY.wMax *= 2;
+ ps->LensInf.rExtentY.wPhyMax *= 2;
+
+ DBG( DBG_LOW, "ModelSetA3I() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to 9630
+ */
+_LOC void ModelSet9630( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSet9360()\n" );
+
+ /*
+ * has 96003 ASIC
+ */
+ if( _OVR_PLUSTEK_9630PL == ps->ModelOverride ) {
+ DBG( DBG_LOW, "Model Override --> 9630PL\n" );
+ ps->sCaps.Model = MODEL_OP_9630PL;
+ } else {
+ ps->sCaps.Model = MODEL_OP_9630P;
+ }
+
+ ps->Device.buttons = 1;
+ ps->sCaps.AsicID = _ASIC_IS_96003;
+
+ ps->Device.ModelCtrl = (_ModelDpi600 | _ModelMemSize128k4 | _ModelWhiteIs0);
+ ps->Device.DataOriginX = 64;
+
+ ps->PhysicalDpi = 600;
+ ps->TimePerLine = 0x5a;
+ ps->Offset70 = 95;
+
+ modelSetBufferSizes( ps );
+
+ ps->a_wGrayInitTime[0] = 133; /* _EppTimeForOthers */
+ ps->a_wGrayInitTime[1] = 720; /* _SppTimeForOthers */
+ ps->a_wGrayInitTime[2] = 300; /* _BidirTimeForOthers */
+ ps->a_wColorInitTime[0] = 400; /* _EppTimeForColor */
+ ps->a_wColorInitTime[1] = 1800; /* _SppTimeForColor */
+ ps->a_wColorInitTime[2] = 800; /* _BidirTimeForColor */
+
+ ps->AsicRedColor = _ASIC_REDCOLOR;
+ ps->AsicGreenColor = _ASIC_GREENCOLOR;
+ ps->RedDataReady = _RED_DATA_READY;
+ ps->GreenDataReady = _GREEN_DATA_READY;
+
+ ps->ShadingBufferSize = (1024 * 7);
+ ps->ShadingBankSize = 8192;
+ ps->ShadingBankRed = (_MemBankSize8k | 0x34);
+ ps->ShadingBankGreen = (_MemBankSize8k | 0x3c);
+ ps->ShadingBankBlue = (_MemBankSize8k | 0x38);
+ ps->ShadingScanLineBlks = 10;
+ ps->ShadingScanLineLen = (2560 * 3);
+
+ ps->FBKScanLineBlks = 10;
+ ps->FBKScanLineLenBase = 2048;
+ ps->FBKScanLineLen = (ps->FBKScanLineLenBase * 6);
+
+ ps->OneScanLineLen = (5120 * 3);
+
+ modelInitMotor( ps );
+ ps->BackwardSteps = 9000;
+
+ modelInitCaps( ps );
+ modelInitPageSettings( ps );
+
+ /*
+ * need to double the vals
+ */
+ ps->LensInf.rExtentX.wMax *= 2;
+ ps->LensInf.rExtentX.wPhyMax *= 2;
+ ps->LensInf.rExtentY.wMax *= 2;
+ ps->LensInf.rExtentY.wPhyMax *= 2;
+
+ DBG( DBG_LOW, "ModelSet9630() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to 9636 (ASIC 98001 models)
+ * works for 9636P Turbo and 9636T /12000T
+ */
+_LOC void ModelSet9636( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSet9636()\n" );
+
+ /*
+ *set to 9630 first, then do the differences
+ */
+ ModelSet9630( ps );
+ ps->Device.buttons = 0;
+
+ /*
+ * has 98001 ASIC
+ */
+ if( _OVR_PLUSTEK_9636 == ps->ModelOverride ) {
+ DBG( DBG_LOW, "Model Override --> 9636P+/Turbo\n" );
+ ps->sCaps.Model = MODEL_OP_9636PP;
+ } else if( _OVR_PLUSTEK_9636P == ps->ModelOverride ) {
+ DBG( DBG_LOW, "Model Override --> 9636P\n" );
+ ps->sCaps.Model = MODEL_OP_9636P;
+ } else {
+ ps->sCaps.Model = MODEL_OP_9636T;
+ ps->sCaps.dwFlag |= SFLAG_TPA;
+ }
+
+ ps->Device.DataOriginX = 72;
+ ps->sCaps.AsicID = _ASIC_IS_98001;
+
+ ps->TotalBufferRequire = _LINE_BUFSIZE * 2 + _LINE_BUFSIZE1 +
+ ps->BufferForColorRunTable + _PROCESS_BUFSIZE;
+
+ /* do it again, as ModelSet9630() changes the result of this function !*/
+ modelInitPageSettings( ps );
+
+ DBG( DBG_LOW, "ModelSet9636() done.\n" );
+}
+
+/*.............................................................................
+ * set the model to P12 (ASIC 98003 models)
+ */
+_LOC void ModelSetP12( pScanData ps )
+{
+ DBG( DBG_LOW, "ModelSetP12()\n" );
+
+ /*
+ * set to 9630 first, then do the differences
+ */
+ ModelSet9630( ps );
+ ps->Device.DataOriginX = 72;
+ ps->sCaps.Model = MODEL_OP_PT12;
+ ps->sCaps.AsicID = _ASIC_IS_98003;
+
+ ps->TotalBufferRequire = _SizeTotalBufTpa;
+
+ /* do it again, as ModelSet9630() changes the result of this function !*/
+ modelInitPageSettings( ps );
+
+ DBG( DBG_LOW, "ModelSetP12() done.\n" );
+}
+
+/* END PLUSTEK-PP_MODEL.C ...................................................*/
diff --git a/backend/plustek-pp_motor.c b/backend/plustek-pp_motor.c
new file mode 100644
index 0000000..f377b7e
--- /dev/null
+++ b/backend/plustek-pp_motor.c
@@ -0,0 +1,3348 @@
+/* @file plustek-pp_motor.c
+ * @brief all functions for motor control
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - slight cosmetic changes
+ * - added function MotorToHomePosition()
+ * - 0.33 - added additional debug-messages
+ * - increased speed for returning to homeposition for Models >= 9630
+ * (and ASIC 96003)
+ * - 0.34 - added FIFO overflow check in motorP96SetSpeed
+ * - removed WaitBack() function from pScanData structure
+ * - removed wStayMaxStep from pScanData structure
+ * - 0.35 - changed motorP96UpdateDataCurrentReadLine() to handle proper
+ * - work of ASIC96003 based 600dpi models
+ * - 0.36 - merged motorP96WaitBack and motorP98WaitBack to motorWaitBack
+ * - merged motorP96SetSpeed and motorP98SetSpedd to motorSetSpeed
+ * - added Sensor-Check in function MotorToHomePosition
+ * - reduced number of forward steps for MotorToHomePosition
+ * - 0.37 - removed function motorP96GetStartStopGap() - no longer used
+ * - removed a_bStepDown1Table and a_bStepUp1Table
+ * - removed // comments
+ * - added A3I stuff
+ * - 0.38 - cosmetic changes
+ * - added P12 stuff
+ * - 0.39 - Did some finetuning in MotorP98003ModuleForwardBackward()
+ * - Fixed a problem, that could cause the driver to throw a
+ * kernel exception
+ * - 0.40 - changed back to build 0.39-3 (disabled A3I stuff)
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some definitons *********************************/
+
+/* #define _A3I_EN */
+
+/*
+ * adjustments for scanning in negative and tranparency mode
+ */
+#define _NEG_SCANNINGPOS 770
+#define _POS_SCANNINGPOS 660 /* original value was 710 */
+
+#define _P98_BACKMOVES 0x3d
+#define _P98_FORWARDMOVES 0x3b /* Origin = 3c */
+
+#define _P98003_BACKSTEPS 120
+#define _P98003_FORWARDSTEPS 120
+
+#define _P96_BACKMOVES 130
+#define _P96_FORWARDMOVES 87 /* 95 */
+#define _P96_FIFOOVERFLOWTHRESH 180
+
+
+#define _COLORRUNTABLE_RED 0x11
+#define _COLORRUNTABLE_GREEN 0x22
+#define _COLORRUNTABLE_BLUE 0x44
+
+#define _BW_ORIGIN 0x0D
+#define _GRAY_ORIGIN 0x0B
+#define _COLOR_ORIGIN 0x0B
+
+#define _P98003_YOFFSET 300
+
+/**************************** local vars *************************************/
+
+static TimerDef p98003MotorTimer;
+
+static UShort a_wMoveStepTable [_NUMBER_OF_SCANSTEPS];
+static Byte a_bScanStateTable[_SCANSTATE_TABLE_SIZE];
+static Byte a_bHalfStepTable [_NUMBER_OF_SCANSTEPS];
+static Byte a_bColorByteTable[_NUMBER_OF_SCANSTEPS];
+static Byte a_bColorsSum[8] = {0, 1, 1, 2, 1, 2, 2, 3};
+
+static pUShort pwEndMoveStepTable = a_wMoveStepTable + _NUMBER_OF_SCANSTEPS;
+static pUChar pbEndColorByteTable = a_bColorByteTable + _NUMBER_OF_SCANSTEPS;
+static pUChar pbEndHalfStepTable = a_bHalfStepTable + _NUMBER_OF_SCANSTEPS;
+
+/*
+ * for the 96001/3 based units
+ */
+static UShort wP96BaseDpi = 0;
+
+static Byte a_bStepDown1Table[20] = {3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+static Byte a_bStepUp1Table[20] = {4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+static Byte a_bMotorDown2Table[20] = {0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
+
+#ifndef _A3I_EN
+static Byte a_bHalfStep2Table[32] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+static Byte a_bHalfStep4Table[16] = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
+static Byte a_bHalfStep6Table[12] = {3,3,3,3,3,3,3,3,3,3,3,3};
+static Byte a_bHalfStep8Table[8] = {4,4,4,4,4,4,4,4};
+static Byte a_bHalfStep10Table[8] = {5,5,5,5,5,5,5,5};
+static Byte a_bHalfStep12Table[6] = {6,6,6,6,6,6};
+static Byte a_bHalfStep14Table[6] = {7,7,7,7,7,7};
+static Byte a_bHalfStep16Table[4] = {8,8,8,8};
+static Byte a_bHalfStep18Table[4] = {9,9,9,9};
+static Byte a_bHalfStep20Table[4] = {10,10,10,10};
+static Byte a_bHalfStep22Table[4] = {11,11,11,11};
+static Byte a_bHalfStep24Table[4] = {12,12,12,12};
+static Byte a_bHalfStep26Table[4] = {13,13,13,13};
+static Byte a_bHalfStep28Table[4] = {14,14,14,14};
+static Byte a_bHalfStep30Table[4] = {15,15,15,15};
+static Byte a_bHalfStep32Table[2] = {16,16};
+static Byte a_bHalfStep34Table[2] = {17,17};
+static Byte a_bHalfStep36Table[2] = {18,18};
+static Byte a_bHalfStep38Table[2] = {19,19};
+static Byte a_bHalfStep40Table[2] = {20,20};
+
+
+static pUChar a_pbHalfStepTables[20] = {
+ a_bHalfStep2Table, a_bHalfStep4Table,
+ a_bHalfStep6Table, a_bHalfStep8Table,
+ a_bHalfStep10Table, a_bHalfStep12Table,
+ a_bHalfStep14Table, a_bHalfStep16Table,
+ a_bHalfStep18Table, a_bHalfStep20Table,
+ a_bHalfStep22Table, a_bHalfStep24Table,
+ a_bHalfStep26Table, a_bHalfStep28Table,
+ a_bHalfStep30Table, a_bHalfStep32Table,
+ a_bHalfStep34Table, a_bHalfStep36Table,
+ a_bHalfStep38Table, a_bHalfStep40Table
+};
+#endif
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ *
+ */
+static void motorP96GetStartStopGap( pScanData ps, Bool fCheckState )
+{
+ UChar bMotorCountDownIndex;
+
+ if( fCheckState ) {
+
+ ps->bMotorStepTableNo = 0xff;
+ if( ps->Scan.bModuleState == _MotorInNormalState )
+ return;
+ }
+
+ bMotorCountDownIndex = ps->bMotorSpeedData / 2;
+
+ if( ps->bCurrentSpeed == 4 && ps->AsicReg.RD_Dpi < 80 )
+ ps->bMotorStepTableNo = 4;
+ else
+ if( ps->Scan.bModuleState == _MotorGoBackward )
+ ps->bMotorStepTableNo = a_bStepUp1Table[bMotorCountDownIndex];
+ else
+ ps->bMotorStepTableNo = a_bStepDown1Table[bMotorCountDownIndex];
+}
+
+
+
+/*.............................................................................
+ * wait for the ScanState stop or ScanState reachs the dwScanStateCount
+ */
+static Bool motorCheckMotorPresetLength( pScanData ps )
+{
+ Byte bScanState;
+ TimerDef timer;
+
+ MiscStartTimer( &timer, (_SECOND * 4));
+ do {
+
+ bScanState = IOGetScanState( ps, _FALSE );
+
+ if (ps->fFullLength) {
+ if (!(bScanState & _SCANSTATE_STOP)) /* still running */
+ if ((ULong)(bScanState & _SCANSTATE_MASK) != ps->dwScanStateCount )
+ continue;
+ return ps->fFullLength;
+ }
+
+ if (bScanState & _SCANSTATE_STOP)
+ break;
+
+ /*
+ * the code may work for all units
+ */
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ if (bScanState < ps->bOldStateCount)
+ bScanState += _NUMBER_OF_SCANSTEPS;
+
+ bScanState -= ps->bOldStateCount;
+
+ if (bScanState >= 40)
+ return ps->fFullLength;
+ }
+
+ } while ( !MiscCheckTimer( &timer ));
+
+ _DODELAY(1); /* delay one ms */
+
+ return ps->fFullLength;
+}
+
+/*.............................................................................
+ * 1) Keep the valid content of a_bColorByteTable, and fill others to 0:
+ * BeginFill = ((bCurrentLineCount + DL) < 64) ? bCurrentLineCount + DL :
+ * bCurrentLineCount + DL - 64;
+ * FillLength = 64 - DL
+ * [NOTE] Keep the content of a_bColorByteTable that begin at
+ * bCurrentLineCount and in DL bytes
+ * 2) Keep the valid content of a_bHalfStepTable, and fill the others to 0:
+ * BeginFill = ((bCurrentLineCount + bCurrentSpeed / 2 + 1) < 64) ?
+ * bCurrentLineCount + bCurrentSpeed / 2 + 1 :
+ * bCurrentLineCount + bCurrentSpeed / 2 + 1 - 64;
+ * FillLength = 64 - (bMotorSpeedData / 2 + 1);
+ */
+static void motorClearColorByteTableLoop0( pScanData ps, Byte bColors )
+{
+ ULong dw;
+ pUChar pb;
+
+ if ((ps->bCurrentLineCount + bColors) >= _NUMBER_OF_SCANSTEPS) {
+ pb = a_bColorByteTable + (ULong)(ps->bCurrentLineCount + bColors -
+ _NUMBER_OF_SCANSTEPS);
+ } else {
+ pb = a_bColorByteTable + (ULong)(ps->bCurrentLineCount + bColors);
+ }
+
+ for (dw = _NUMBER_OF_SCANSTEPS - bColors; dw; dw--) {
+
+ *pb++ = 0;
+ if (pb >= pbEndColorByteTable)
+ pb = a_bColorByteTable;
+ }
+
+ if ((ps->bCurrentLineCount+ps->bCurrentSpeed/2+1) >= _NUMBER_OF_SCANSTEPS) {
+
+ pb = a_bHalfStepTable + (ULong)(ps->bCurrentLineCount +
+ ps->bCurrentSpeed / 2 + 1 - _NUMBER_OF_SCANSTEPS);
+ } else {
+ pb = a_bHalfStepTable +
+ (ULong)(ps->bCurrentLineCount + ps->bCurrentSpeed / 2 + 1);
+ }
+
+ for (dw = _NUMBER_OF_SCANSTEPS - ps->bMotorSpeedData / 2 - 1; dw; dw--) {
+ *pb++ = 0;
+ if (pb >= pbEndHalfStepTable)
+ pb = a_bHalfStepTable;
+ }
+}
+
+/*.............................................................................
+ * motorClearColorByteTableLoop1 ()
+ * 1) Adjust bNewGap:
+ * bNewGap = (bNewGap <= bNewCurrentLineCountGap) ?
+ * 0 : bNewGap - bNewCurrentLineCount - 1;
+ * 2) Fill the 0 to a_bColorByteTable:
+ * FillIndex = ((bCurrentLineCount + bNewGap + 1) < 64) ?
+ * bCurrentLineCount + bNewGap + 1 :
+ * bCurrentLineCount + bNewGap + 1 - 64;
+ * FillCount = 64 - bNewGap - 1;
+ * 3) Adjust bNewGap:
+ * bNewGap = (bCurrentLineCount <= bNewCurrentLineCountGap) ?
+ * 0 : bNewGap - bNewCurrentLineCount - 1;
+ * 4) Fill the a_bHalfStepTable:
+ * FillIndex = ((bCurrentLineCount + bNewGap + 1) < 64) ?
+ * bCurrentLineCount + bNewGap + 1 :
+ * bCurrentLineCount + bNewGap + 1 - 64;
+ * FillCount = 64 - bNewGap - 1;
+ */
+static void motorClearColorByteTableLoop1( pScanData ps )
+{
+ ULong dw = _NUMBER_OF_SCANSTEPS - 1;
+ pUChar pb;
+
+ if (ps->bNewGap > ps->bNewCurrentLineCountGap) {
+ ps->bNewGap = ps->bNewGap - ps->bNewCurrentLineCountGap - 1;
+ dw -= (ULong)ps->bNewGap;
+ } else {
+ ps->bNewGap = 0;
+ }
+
+ if ((ps->bCurrentLineCount + ps->bNewGap + 1) >= _NUMBER_OF_SCANSTEPS) {
+ pb = a_bColorByteTable +
+ (ULong)(ps->bCurrentLineCount+ps->bNewGap+1-_NUMBER_OF_SCANSTEPS);
+ } else {
+ pb = a_bColorByteTable +
+ (ULong)(ps->bCurrentLineCount + ps->bNewGap + 1);
+ }
+
+ for (; dw; dw--) {
+ *pb++ = 0;
+ if (pb >= pbEndColorByteTable)
+ pb = a_bColorByteTable;
+ }
+
+ if (ps->bCurrentSpeed > ps->bNewCurrentLineCountGap) {
+ ps->bNewGap = ps->bCurrentSpeed - ps->bNewCurrentLineCountGap;
+ dw = _NUMBER_OF_SCANSTEPS - 1 - (ULong)ps->bNewGap;
+ } else {
+ dw = _NUMBER_OF_SCANSTEPS - 1;
+ ps->bNewGap = 0;
+ }
+
+ if ((ps->bCurrentLineCount + ps->bNewGap + 1) >= _NUMBER_OF_SCANSTEPS) {
+ pb = a_bHalfStepTable + (ULong)(ps->bCurrentLineCount +
+ ps->bNewGap + 1 - _NUMBER_OF_SCANSTEPS);
+ } else {
+ pb = a_bHalfStepTable + (ULong)(ps->bCurrentLineCount+ps->bNewGap +1);
+ }
+
+ for (; dw; dw--) {
+ *pb++ = 0;
+ if (pb >= pbEndHalfStepTable)
+ pb = a_bHalfStepTable;
+ }
+}
+
+/*.............................................................................
+ * According the flag to set motor direction
+ */
+static void motorSetRunPositionRegister( pScanData ps )
+{
+ Byte bData;
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+ if( ps->Scan.fMotorBackward ) {
+ bData = ps->AsicReg.RD_Motor0Control & ~_MotorDirForward;
+ } else {
+ bData = ps->AsicReg.RD_Motor0Control | _MotorDirForward;
+ }
+
+ IOCmdRegisterToScanner( ps, ps->RegMotor0Control, bData );
+
+ } else {
+
+ if( ps->Scan.fMotorBackward ) {
+ bData = ps->Asic96Reg.RD_MotorControl & ~_MotorDirForward;
+ } else {
+ bData = ps->Asic96Reg.RD_MotorControl | _MotorDirForward;
+ }
+
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, bData );
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorPauseColorMotorRunStates( pScanData ps )
+{
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES);
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ ps->a_nbNewAdrPointer[2] = 0x77; /* Read color at the same time */
+
+ } else {
+ ps->a_nbNewAdrPointer[2] = 1;
+ ps->a_nbNewAdrPointer[3] = 3;
+ ps->a_nbNewAdrPointer[4] = 2;
+ }
+
+ MotorSetConstantMove( ps, 0 );
+}
+
+/*.............................................................................
+ * Setup the a_nbNewAdrPointer for ASIC stepping register
+ */
+static void motorP98FillDataToColorTable( pScanData ps,
+ Byte bIndex, ULong dwSteps)
+{
+ pUChar pb;
+ pUShort pw;
+ Byte bColor;
+ UShort w;
+
+ for ( pb = &a_bColorByteTable[bIndex],
+ pw = &a_wMoveStepTable[bIndex]; dwSteps; dwSteps-- ) {
+
+ if (*pw) { /* valid state */
+
+ if( *pw >= ps->BufferForColorRunTable ) {
+ DBG( DBG_LOW, "*pw = %u > %u !!\n",
+ *pw, ps->BufferForColorRunTable );
+ } else {
+ bColor = ps->pColorRunTable[*pw]; /* get the colors */
+ if (a_bColorsSum[bColor & 7]) /* need to read data */
+ *pb = bColor & 7;
+ }
+ }
+
+ if (++pw >= pwEndMoveStepTable) {
+ pw = a_wMoveStepTable;
+ pb = a_bColorByteTable;
+ } else
+ pb++;
+ }
+
+ /* ToCondense */
+ pb = a_bColorByteTable;
+
+ for (w = 0; w < _SCANSTATE_BYTES; w++, pb += 2)
+ ps->a_nbNewAdrPointer[w] = (Byte)((*pb & 7) + ((*(pb + 1) & 7) << 4));
+
+ /* ToCondenseMotor */
+ for (pb = a_bHalfStepTable, w = 0; w < _SCANSTATE_BYTES; w++) {
+ if (*pb++)
+ ps->a_nbNewAdrPointer [w] |= 8;
+
+ if (*pb++)
+ ps->a_nbNewAdrPointer [w] |= 0x80;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98FillHalfStepTable( pScanData ps )
+{
+ pUChar pbHalfStepTbl, pb;
+ pUShort pwMoveStep;
+ DataType Data;
+ ULong dw;
+
+ if (1 == ps->bMotorSpeedData) {
+ for (dw = 0; dw < _NUMBER_OF_SCANSTEPS; dw++)
+ a_bHalfStepTable [dw] =
+ (a_wMoveStepTable [dw] > ps->wMaxMoveStep) ? 0: 1;
+ } else {
+ pwMoveStep = &a_wMoveStepTable[ps->bCurrentLineCount];
+ pbHalfStepTbl = &a_bHalfStepTable[ps->bCurrentLineCount];
+
+ if (ps->DataInf.wAppDataType >= COLOR_TRUE24)
+ Data.dwValue = _NUMBER_OF_SCANSTEPS - 1;
+ else
+ Data.dwValue = _NUMBER_OF_SCANSTEPS;
+
+ for (; Data.dwValue; Data.dwValue--, pbHalfStepTbl++, pwMoveStep++ ) {
+
+ if (pwMoveStep >= pwEndMoveStepTable) {
+ pbHalfStepTbl = a_bHalfStepTable;
+ pwMoveStep = a_wMoveStepTable;
+ }
+
+ if (*pwMoveStep) { /* need to exposure */
+
+ dw = (ULong)ps->bMotorSpeedData;
+ if (Data.bValue < ps->bMotorSpeedData)
+ *pwMoveStep = 0;
+ else {
+ *pbHalfStepTbl = 1;
+
+ if (ps->dwFullStateSpeed) {
+ dw -= ps->dwFullStateSpeed;
+ for (pb = pbHalfStepTbl; dw;
+ dw -= ps->dwFullStateSpeed) {
+ pb += ps->dwFullStateSpeed;
+ if (pb >= pbEndHalfStepTable)
+ pb -= _NUMBER_OF_SCANSTEPS;
+ *pb = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98FillBackColorDataTable( pScanData ps )
+{
+ Byte bIndex;
+
+ if ((bIndex = ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1) >=
+ _NUMBER_OF_SCANSTEPS) {
+ bIndex -= _NUMBER_OF_SCANSTEPS;
+ }
+
+ motorP98FillDataToColorTable( ps, bIndex, (ULong)(_NUMBER_OF_SCANSTEPS -
+ ps->bNewCurrentLineCountGap));
+}
+
+/*.............................................................................
+ * i/p:
+ * pScanStep = pColorRunTable if forward, a_bScanStateTable if backward
+ * dwState = how many states is requested to process
+ * NOTE:
+ * The content of pScanStep contain:
+ * 0: Idle state
+ * 0xff: End mark
+ * others: The motor speed value
+ */
+static void motorP98FillBackLoop( pScanData ps,
+ pUChar pScanStep, ULong dwStates )
+{
+ for (ps->fFullLength = _FALSE; dwStates; dwStates--) {
+
+ if (*pScanStep == 0xff ) {
+
+ ULong dw = ps->dwScanStateCount;
+
+ for (; dwStates; dwStates--) {
+ ps->a_nbNewAdrPointer [dw / 2] &= ((dw & 1) ? 0x7f: 0xf7);
+ dw = (dw + 1U) & _SCANSTATE_MASK;
+ }
+ if (!ps->dwScanStateCount)
+ ps->dwScanStateCount = _NUMBER_OF_SCANSTEPS;
+
+ ps->dwScanStateCount--;
+ ps->fFullLength = _TRUE;
+ break;
+ } else {
+ ps->a_nbNewAdrPointer [ps->dwScanStateCount / 2] |=
+ ((ps->dwScanStateCount & 1) ? 0x80 : 0x08);
+ if (++ps->dwScanStateCount == _NUMBER_OF_SCANSTEPS)
+ ps->dwScanStateCount = 0; /* reset to begin */
+
+ pScanStep++;
+ }
+ }
+ IOSetToMotorStepCount( ps ); /* put all scan states to ASIC */
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98SetRunFullStep( pScanData ps )
+{
+ ps->OpenScanPath( ps );
+
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ IODataToRegister( ps, ps->RegStepControl,
+ ps->AsicReg.RD_StepControl );
+ IODataToRegister( ps, ps->RegLineControl, 96 );
+
+ if ( ps->bFastMoveFlag == _FastMove_Low_C75_G150_Back ) {
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (_MotorHQuarterStep + _MotorOn + _MotorDirBackward));
+ } else {
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (_MotorHQuarterStep + _MotorOn + _MotorDirForward));
+ }
+
+ if (ps->bFastMoveFlag == _FastMove_Film_150) {
+ ps->AsicReg.RD_XStepTime = 12;
+ } else {
+ if (ps->bFastMoveFlag == _FastMove_Fast_C50_G100) {
+ ps->AsicReg.RD_XStepTime =
+ ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) ? 4 : 8);
+ } else {
+ ps->AsicReg.RD_XStepTime =
+ ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) ? 6 : 12);
+ }
+ }
+
+ DBG( DBG_LOW, "XStepTime = %u\n", ps->AsicReg.RD_XStepTime );
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ * moves the sensor back home...
+ */
+static int motorP98BackToHomeSensor( pScanData ps )
+{
+ int result = _OK;
+ TimerDef timer;
+
+ MotorSetConstantMove( ps, 1 );
+
+ ps->OpenScanPath( ps );
+
+ ps->AsicReg.RD_StepControl =
+ (_MOTOR_FREERUN + _MOTOR0_SCANSTATE+ _MOTOR0_ONESTEP);
+ IODataToRegister( ps, ps->RegStepControl, ps->AsicReg.RD_StepControl);
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+
+ ps->AsicReg.RD_Motor0Control = _MotorHQuarterStep +
+ _MotorOn + _MotorDirBackward;
+ IODataToRegister( ps, ps->RegMotor0Control, ps->AsicReg.RD_Motor0Control );
+
+
+ if( ps->DataInf.wPhyDataType >= COLOR_TRUE24) {
+ ps->AsicReg.RD_XStepTime = ps->bSpeed24;
+ } else {
+ ps->AsicReg.RD_XStepTime = ps->bSpeed12;
+ }
+
+ DBG( DBG_HIGH, "XStepTime = %u\n", ps->AsicReg.RD_XStepTime );
+
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime );
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ /* CHANGE: We allow up to 25 seconds for returning (org. val was 10) */
+ MiscStartTimer( &timer, _SECOND * 25 );
+
+ do {
+ if (IODataFromRegister( ps, ps->RegStatus) & _FLAG_P98_PAPER ) {
+ IODataToRegister( ps, ps->RegModelControl,
+ (Byte)(ps->AsicReg.RD_ModelControl | _HOME_SENSOR_POLARITY));
+ if(!(IODataFromRegister(ps, ps->RegStatus) & _FLAG_P98_PAPER))
+ break;
+ }
+ _DODELAY( 10 ); /* delay 10 ms */
+
+ } while ( !(result = MiscCheckTimer( &timer )));
+
+ ps->CloseScanPath( ps );
+
+ if( _OK != result )
+ return result;
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ IOSetToMotorRegister( ps );
+
+ return _OK;
+}
+
+/*.............................................................................
+ * 1) Clear scan states
+ * 2) Adjust the new scan state
+ */
+static void motorP98FillRunNewAdrPointer1( pScanData ps )
+{
+ ScanState sState;
+ Byte bTemp;
+
+ IOGetCurrentStateCount( ps, &sState);
+ bTemp = sState.bStep;
+ if (sState.bStep < ps->bOldStateCount) {
+ sState.bStep += _NUMBER_OF_SCANSTEPS;/* over table (table just can */
+ } /* holds 64 step, then reset to */
+ /* 0, so if less than means over*/
+ /* the table) */
+ sState.bStep -= ps->bOldStateCount; /* how many states passed */
+ ps->pScanState += sState.bStep;
+
+ /*
+ * if current state != no stepped or stepped a cycle, fill the table with
+ * 1 in NOT STEPPED length. (1 means to this state has to be processing).
+ */
+ ps->bOldStateCount = bTemp;
+ ps->dwScanStateCount = (ULong)((bTemp + 1) & _SCANSTATE_MASK);
+
+ motorP98FillBackLoop( ps, ps->pScanState, _NUMBER_OF_SCANSTEPS );
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98FillRunNewAdrPointer( pScanData ps )
+{
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ motorP98FillRunNewAdrPointer1( ps );
+}
+
+/*.............................................................................
+ * move the sensor to a specific Y-position
+ */
+static void motorP98PositionYProc( pScanData ps, ULong dwStates )
+{
+ ScanState sState;
+
+ memset( ps->pColorRunTable, 1, dwStates );
+ memset( ps->pColorRunTable + dwStates, 0xff, (3800UL - dwStates));
+
+ IOGetCurrentStateCount( ps, &sState);
+
+ ps->bOldStateCount = sState.bStep;
+
+ ps->OpenScanPath( ps );
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (Byte)(_MotorOn + _MotorHEightStep +(ps->Scan.fMotorBackward)?
+ _MotorDirBackward : _MotorDirForward));
+
+ DBG( DBG_LOW, "XStepTime = %u\n", ps->bSpeed6 );
+ IODataToRegister( ps, ps->RegXStepTime, ps->bSpeed6 );
+ ps->CloseScanPath( ps );
+
+ ps->pScanState = ps->pColorRunTable;
+
+ ps->FillRunNewAdrPointer( ps );
+
+ while(!motorCheckMotorPresetLength( ps ))
+ motorP98FillRunNewAdrPointer1( ps );
+}
+
+/*.............................................................................
+ * checks if the sensor is in it´s home position and moves it back if necessary
+ */
+static int motorP98CheckSensorInHome( pScanData ps )
+{
+ int result;
+
+ if (!(IODataRegisterFromScanner(ps,ps->RegStatus) & _FLAG_P98_PAPER)){
+
+ MotorSetConstantMove( ps, 1 );
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bExtraMotorCtrl = 0;
+ motorP98PositionYProc( ps, 20 );
+
+ result = motorP98BackToHomeSensor( ps );
+ if( _OK != result )
+ return result;
+
+ _DODELAY( 250 );
+ }
+
+ return _OK;
+}
+
+/*.............................................................................
+ * move the sensor to the scan-start position
+ */
+static void motorP98WaitForPositionY( pScanData ps )
+{
+ ULong dw;
+ ULong dwBX, dwDX;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+
+ motorP98BackToHomeSensor( ps );
+ _DODELAY( 100 );
+
+/* CHECK do we need this block ? was test code in the original source code */
+ ps->OpenScanPath( ps );
+ IODataToRegister( ps, ps->RegModelControl, ps->AsicReg.RD_ModelControl);
+ IODataToRegister( ps, ps->RegStepControl, (Byte)(_MOTOR_FREERUN +
+ _MOTOR0_SCANSTATE + _MOTOR0_ONESTEP));
+ IODataToRegister( ps, ps->RegMotor0Control, (Byte)(_MotorOn +
+ _MotorHQuarterStep + _MotorDirForward));
+ ps->CloseScanPath( ps );
+
+ for (dw=1000; dw; dw--) {
+ if (IODataRegisterFromScanner( ps, ps->RegStatus) & _FLAG_P98_PAPER) {
+ IORegisterDirectToScanner( ps, ps->RegForceStep );
+ _DODELAY( 1000 / 400 );
+ }
+ }
+/*-*/
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IOCmdRegisterToScanner( ps, ps->RegModeControl,
+ ps->AsicReg.RD_ModeControl );
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bExtraMotorCtrl = 0;
+ ps->bFastMoveFlag = _FastMove_Film_150;
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_Negative) {
+ MotorP98GoFullStep(ps, (ps->DataInf.crImage.y+_NEG_SCANNINGPOS)/2);
+ } else {
+ MotorP98GoFullStep(ps, (ps->DataInf.crImage.y+_POS_SCANNINGPOS)/2);
+ }
+
+ return;
+ }
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+
+ IOCmdRegisterToScanner( ps, ps->RegModeControl,
+ ps->AsicReg.RD_ModeControl );
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bExtraMotorCtrl = 0;
+
+ /* SetStartPoint */
+ dw = ps->wInitialStep + ps->DataInf.crImage.y;
+
+ /*
+ * CHANGE: when checking out the values from the NT registry
+ * I found that the values are NOT 0
+ */
+ switch (ps->DataInf.wPhyDataType) {
+ case COLOR_BW: dw += _BW_ORIGIN; break;
+ case COLOR_256GRAY: dw += _GRAY_ORIGIN; break;
+ default: dw += _COLOR_ORIGIN; break;
+ }
+
+ if (dw & 0x80000000)
+ dw = 0; /* negative */
+
+ if (dw > 180) {
+ if (ps->bSetScanModeFlag & _ScanMode_Mono) {
+ dwBX = 90; /* go via 150 dpi, so 180 / 2 = 90 */
+ dwDX = (dw -180) % 3;
+ dw = (dw -180) / 3; /* 100 dpi */
+ } else {
+ dwBX = 45; /* go via 75 dpi, so 180 / 4 = 45 */
+ dwDX = (dw -180) % 6;
+ dw = (dw -180) / 6; /* 50 dpi */
+ }
+
+ dwDX = (dwDX * 3 + 1) / 2 + dwBX;
+
+ /*
+ * 100/50 dpi lines is 3/2 times of 150/75
+ * eax = (remainder * 3 + 1) / 2 + 180 / (2 or 4) lines
+ */
+ ps->bFastMoveFlag = _FastMove_Low_C75_G150;
+ MotorP98GoFullStep( ps, dwDX);
+
+ if (dw) {
+ DBG( DBG_LOW, "FAST MOVE MODE !!!\n" );
+ ps->bFastMoveFlag = _FastMove_Fast_C50_G100;
+ MotorP98GoFullStep( ps, dw);
+ }
+ } else {
+ dwBX = ((ps->bSetScanModeFlag & _ScanMode_Mono) ? 2: 4);
+ dw = (dw + dwBX/2) / dwBX;
+ ps->bFastMoveFlag = _FastMove_Low_C75_G150;
+
+ MotorP98GoFullStep(ps, dw);
+ }
+}
+
+/*.............................................................................
+ * PreMove/EndMove
+ * PreMove is only in ADF and CFB mode
+ * In ADF version, there is a distance gap between Paper flag and Real initial
+ * position and it need premove to real initial position and turn the motor
+ * Inverse and start Fast move to start scan position
+ * In CFB version , PreMove 1 mm to avoid bouncing of PaperFlag sensor then
+ * fast move
+ * In CIS and CSP although there have not PreMove but there have End move
+ * when paper out there still have several mm need to read,
+ * So it need EndMove 2mm and set Inverse Paper
+ */
+static Bool motorP98GotoShadingPosition( pScanData ps )
+{
+ int result;
+
+ DBG( DBG_LOW, "motorP98GotoShadingPosition()\n" );
+
+ /* Modify Lamp Back to Home step for Scan twice in short time */
+ result = motorP98CheckSensorInHome( ps );
+
+ if( _OK != result )
+ return _FALSE;
+
+ MotorSetConstantMove( ps, 0 ); /* clear scan states */
+
+ IOCmdRegisterToScanner( ps, ps->RegModelControl,
+ ps->AsicReg.RD_ModelControl );
+
+ ps->Scan.fMotorBackward = _FALSE; /* forward */
+ ps->bExtraMotorCtrl = 0;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+
+ ps->bFastMoveFlag = _FastMove_Low_C75_G150;
+ MotorP98GoFullStep( ps, 0x40 );
+
+ ps->bFastMoveFlag = _FastMove_Middle_C75_G150;
+ MotorP98GoFullStep( ps, ps->Device.dwModelOriginY );
+ }
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ IOSetToMotorRegister( ps );
+
+ return _TRUE;
+}
+
+/*.............................................................................
+ * round to the next physical available value
+ */
+static void motorP98SetMaxDpiAndLength( pScanData ps,
+ pUShort wLengthY, pUShort wBaseDpi )
+{
+ if (ps->DataInf.xyAppDpi.y > 600)
+ *wLengthY = ps->LensInf.rExtentY.wMax * 4 + 200;
+ else
+ *wLengthY = ps->LensInf.rExtentY.wMax * 2 + 200;
+
+ if ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) &&
+ (ps->DataInf.xyAppDpi.y <= ps->wMinCmpDpi)) {
+ *wBaseDpi = ps->wMinCmpDpi;
+ } else {
+ if ((ps->DataInf.wPhyDataType < COLOR_TRUE24) &&
+ (ps->DataInf.xyAppDpi.y <= 75)) {
+ *wBaseDpi = 75;
+ } else {
+ if (ps->DataInf.xyAppDpi.y <= 150) {
+ *wBaseDpi = 150;
+ } else {
+ if (ps->DataInf.xyAppDpi.y <= 300) {
+ *wBaseDpi = 300;
+ } else {
+ if (ps->DataInf.xyAppDpi.y <= 600)
+ *wBaseDpi = 600;
+ else
+ *wBaseDpi = 1200;
+ }
+ }
+ }
+ }
+
+ DBG( DBG_LOW, "wBaseDPI = %u, %u\n", *wBaseDpi, ps->wMinCmpDpi );
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98FillGBColorRunTable( pScanData ps, pUChar pTable,
+ Byte bHi, Byte bLo, UShort wBaseDpi )
+{
+
+ if( ps->Device.f0_8_16 ) {
+
+ if (wBaseDpi == ps->wMinCmpDpi) {
+ *pTable |= bHi;
+ *(pTable + 1) |= bLo;
+ } else {
+ switch (wBaseDpi) {
+ case 150:
+ *(pTable + 2) |= bHi;
+ *(pTable + 4) |= bLo;
+ break;
+
+ case 300:
+ *(pTable + 4) |= bHi;
+ *(pTable + 8) |= bLo;
+ break;
+
+ case 600:
+ *(pTable + 8) |= bHi;
+ *(pTable + 16) |= bLo;
+ break;
+
+ default:
+ *(pTable + 16) |= bHi;
+ *(pTable + 32) |= bLo;
+ break;
+ }
+ }
+ } else {
+
+ if (wBaseDpi == ps->wMinCmpDpi) {
+ *pTable |= bHi;
+ *(pTable + 1) |= bLo;
+ } else {
+ switch(wBaseDpi) {
+
+ case 150:
+ *(pTable + 1) |= bHi;
+ *(pTable + 2) |= bLo;
+ break;
+
+ case 300:
+ *(pTable + 2) |= bHi;
+ *(pTable + 4) |= bLo;
+ break;
+
+ case 600:
+ *(pTable + 4) |= bHi;
+ *(pTable + 8) |= bLo;
+ break;
+
+ default:
+ *(pTable + 8) |= bHi;
+ *(pTable + 16) |= bLo;
+ break;
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98SetupRunTable( pScanData ps )
+{
+ UShort wDpi, w, wBaseDpi, wLengthY;
+ pUChar pTable;
+
+ motorP98SetMaxDpiAndLength( ps, &wLengthY, &wBaseDpi );
+
+ /*ClearColorRunTable(); */
+ memset( ps->pColorRunTable, 0, ps->BufferForColorRunTable );
+
+ wDpi = wBaseDpi;
+ w = wLengthY + 1000;
+ pTable = ps->pColorRunTable + (_NUMBER_OF_SCANSTEPS / 4);
+
+ if( ps->DataInf.wPhyDataType >= COLOR_TRUE24) {
+
+ for(; w; w--, pTable++) {
+ if((short)(wDpi -= ps->DataInf.xyPhyDpi.y) <= 0) {
+ wDpi += wBaseDpi;
+ *pTable |= 0x44;
+ motorP98FillGBColorRunTable( ps, pTable, 0x22, 0x11, wBaseDpi );
+ }
+ }
+ } else {
+ for(; w; w--, pTable++) {
+ if((short)(wDpi -= ps->DataInf.xyPhyDpi.y) <= 0) {
+ wDpi += wBaseDpi;
+ *pTable = 0x22;
+ }
+ }
+ }
+ ps->dwColorRunIndex = 0;
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98UpdateDataCurrentReadLine( pScanData ps )
+{
+ if(!(ps->Scan.bNowScanState & _SCANSTATE_STOP)) {
+
+ Byte b;
+
+ if (ps->Scan.bNowScanState >= ps->bCurrentLineCount)
+ b = ps->Scan.bNowScanState - ps->bCurrentLineCount;
+ else
+ b = ps->Scan.bNowScanState + _NUMBER_OF_SCANSTEPS - ps->bCurrentLineCount;
+
+ if (b < 40)
+ return;
+ }
+
+ ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE );
+ IOSetToMotorRegister( ps );
+
+ ps->Scan.bModuleState = _MotorAdvancing;
+}
+
+/*.............................................................................
+ * Byte - Scan State Index (0-63) and StopStep bit
+ * pScanState->bStep - Scan State Index (0-63)
+ * pScanState->bStatus - Scanner Status Register value
+ */
+static void motorP96GetScanStateAndStatus( pScanData ps, pScanState pScanStep )
+{
+ ps->OpenScanPath( ps );
+
+ pScanStep->bStep = IOGetScanState(ps, _TRUE);
+ pScanStep->bStep &= _SCANSTATE_MASK; /* org was. ~_ScanStateStop; */
+ pScanStep->bStatus = IODataFromRegister( ps, ps->RegStatus );
+
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ * Capture the image data and average them.
+ */
+static Byte motorP96ReadDarkData( pScanData ps )
+{
+ Byte bFifoOffset;
+ UShort wSum, w;
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND/2);
+
+ do {
+
+ bFifoOffset = IODataRegisterFromScanner( ps, ps->RegFifoOffset );
+
+ /* stepped 1 block */
+ if( bFifoOffset ) {
+
+ /* read data */
+ IOReadScannerImageData( ps, ps->pScanBuffer1, 512UL);
+
+ /* 320 = 192 + 128 (128 is size to fetch data) */
+ for (w = 192, wSum = 0; w < 320; w++)
+ wSum += (UShort)ps->pScanBuffer1[w];/* average data from */
+ /* offset 192 and size 128*/
+ return (Byte)(wSum >> 7); /* divided by 128 */
+ }
+
+ } while (!MiscCheckTimer(&timer));
+
+ return 0xff; /* timed-out */
+}
+
+/*.............................................................................
+ * move the sensor to a specific Y-position
+ */
+static void motorP96PositionYProc( pScanData ps, ULong dwStates )
+{
+ ScanState sState;
+
+ memset( ps->pColorRunTable, 1, dwStates );
+
+#ifdef DEBUG
+ if( dwStates > 800UL )
+ DBG( DBG_HIGH, "!!!!! RUNTABLE OVERFLOW !!!!!\n" );
+#endif
+ memset( ps->pColorRunTable + dwStates, 0xff, 800UL - dwStates );
+
+ IOGetCurrentStateCount( ps, &sState );
+ ps->bOldStateCount = sState.bStep;
+
+ /* TurnOnMotorAndSetDirection (); */
+ if( ps->Scan.fMotorBackward ) {
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ (Byte)(ps->IgnorePF | ps->MotorOn));
+ } else {
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ (Byte)(ps->IgnorePF | ps->MotorOn | _MotorDirForward));
+ }
+
+ ps->pScanState = ps->pColorRunTable;
+ do {
+ ps->FillRunNewAdrPointer( ps );
+
+ } while (!motorCheckMotorPresetLength( ps ));
+}
+
+/*.............................................................................
+ * move the sensor to the scan-start position
+ */
+static void motorP96WaitForPositionY( pScanData ps )
+{
+/* scheint OKAY zu sein fuer OP4830 */
+#ifdef _A3I_EN
+#warning "compiling for A3I"
+ ULong dw;
+ ScanState sState;
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ ps->Asic96Reg.RD_MotorControl = ps->IgnorePF|ps->MotorOn|_MotorDirForward;
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bExtraMotorCtrl = ps->IgnorePF;
+
+ if( ps->DataInf.xyAppDpi.y <= ps->PhysicalDpi )
+ dw = 30UL;
+ else
+ dw = 46UL;
+
+ dw = (dw + ps->DataInf.crImage.y) * 4 / 3;
+
+ if( ps->DataInf.wPhyDataType == COLOR_TRUE24 )
+ dw += 99; /* dwStepsForColor; */
+
+ else if( ps->DataInf.wPhyDataType == COLOR_256GRAY )
+ dw += 99; /* dwStepsForGray; */
+ else
+ dw += 99; /* dwStepsForBW; */
+
+ if( dw >= 130UL ) {
+
+ dw -= 100UL;
+ dw <<= 1;
+ /* GoFullStep (dw); */
+
+ memset( ps->pColorRunTable, 1, dw );
+ memset( ps->pColorRunTable + dw, 0xff, ps->BufferForColorRunTable - dw );
+
+ IOGetCurrentStateCount( ps, &sState );
+ ps->bOldStateCount = sState.bStep;
+
+ /* AdjustMotorTime () */
+ IOCmdRegisterToScanner( ps, ps->RegLineControl, 31 );
+
+ /* SetRunHalfStep () */
+ if( ps->Scan.fMotorBackward )
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, _Motor1FullStep |
+ ps->IgnorePF | ps->MotorOn );
+ else
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, ps->IgnorePF |
+ ps->MotorOn | _MotorDirForward );
+
+ ps->pScanState = ps->pColorRunTable;
+
+ do {
+ ps->FillRunNewAdrPointer( ps );
+
+ } while (!motorCheckMotorPresetLength(ps));
+
+ /* RestoreMotorTime () */
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,
+ ps->AsicReg.RD_LineControl );
+
+ dw = 100UL;
+ }
+
+ if( ps->DataInf.wPhyDataType != COLOR_TRUE24 )
+ dw += 20;
+
+ motorP96PositionYProc( ps, dw );
+
+#else
+
+ ULong dw;
+ ScanState sState;
+
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND / 4);
+ while (!MiscCheckTimer( &timer ));
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ ps->Asic96Reg.RD_MotorControl = ps->IgnorePF|ps->MotorOn|_MotorDirForward;
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bExtraMotorCtrl = ps->IgnorePF;
+
+ if ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) ||
+ (ps->DataInf.xyAppDpi.y <= 300)) {
+ dw = 6UL;
+
+ } else {
+
+ if (ps->DataInf.xyAppDpi.y <= 600) {
+ /* 50 is from 6/300 */
+ dw = (ULong)ps->DataInf.xyAppDpi.y / 50UL + 3UL;
+ } else
+ dw = 15; /* 6UL * 600UL / 300UL + 3; */
+ }
+
+ dw += ps->DataInf.crImage.y;
+
+ if (dw >= 180UL) {
+
+ dw -= 180UL;
+ /* GoFullStep (ps, dw);----------------------------------------------*/
+ memset( ps->pColorRunTable, 1, dw );
+#ifdef DEBUG
+ if( dw > 8000UL )
+ DBG( DBG_HIGH, "!!!!! RUNTABLE OVERFLOW !!!!!\n" );
+#endif
+ memset( ps->pColorRunTable + dw, 0xff, 8000UL - dw );
+
+ IOGetCurrentStateCount( ps, &sState );
+ ps->bOldStateCount = sState.bStep;
+
+ /* SetRunFullStep (ps) */
+ if( ps->Scan.fMotorBackward ) {
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ (Byte)(ps->FullStep | ps->IgnorePF | ps->MotorOn));
+ } else {
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ (Byte)(ps->FullStep | ps->IgnorePF | ps->MotorOn |
+ _MotorDirForward));
+ }
+
+ ps->pScanState = ps->pColorRunTable;
+
+ do {
+ ps->FillRunNewAdrPointer (ps);
+
+ } while (!motorCheckMotorPresetLength(ps));
+
+ /*-------------------------------------------------------------------*/
+
+ dw = 180UL;
+ }
+
+ if (ps->DataInf.wPhyDataType != COLOR_TRUE24)
+ dw = dw * 2 + 16;
+ else
+ dw *= 2;
+
+ motorP96PositionYProc( ps, dw );
+#endif
+}
+
+/*.............................................................................
+ * Position Scan Module to specified line number (Forward or Backward & wait
+ * for paper flag ON)
+ */
+static void motorP96ConstantMoveProc1( pScanData ps, ULong dwLines )
+{
+ Byte bRemainder, bLastState;
+ UShort wQuotient;
+ ULong dwDelayMaxTime;
+ ScanState StateStatus;
+ TimerDef timer;
+ Bool fTimeout = _FALSE;
+
+ /* state cycles */
+ wQuotient = (UShort)(dwLines / _NUMBER_OF_SCANSTEPS);
+ bRemainder = (Byte)(dwLines % _NUMBER_OF_SCANSTEPS);
+
+ /* 3.3 ms per line */
+#ifdef _A3I_EN
+ dwDelayMaxTime = dwLines * _MOTOR_ONE_LINE_TIME + _SECOND * 2;
+#else
+ dwDelayMaxTime = dwLines * _MOTOR_ONE_LINE_TIME + _SECOND * 20;
+#endif
+
+ /* step every time */
+ MotorSetConstantMove( ps, 1 );
+
+ ps->OpenScanPath( ps );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+
+ ps->Asic96Reg.RD_MotorControl = ps->MotorFreeRun |
+ ps->MotorOn | _MotorDirForward;
+ IODataToRegister( ps, ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl );
+
+ ps->CloseScanPath( ps );
+
+ bLastState = 0;
+
+ MiscStartTimer( &timer, dwDelayMaxTime );
+
+ do {
+
+ /* GetStatusAndScanStateAddr () */
+ motorP96GetScanStateAndStatus( ps, &StateStatus );
+ if (StateStatus.bStatus & _FLAG_P96_PAPER ) {
+ if (wQuotient) {
+
+ if (StateStatus.bStep != bLastState) {
+ bLastState = StateStatus.bStep;
+
+ if (!bLastState)
+ wQuotient--;
+ }
+ } else
+ if (StateStatus.bStep >= bRemainder)
+ break;
+ } else
+ break;
+ } while (!(fTimeout = MiscCheckTimer( &timer )));
+
+ if (!fTimeout) {
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ IOSetToMotorRegister( ps );
+ }
+}
+
+/*.............................................................................
+ * PreMove/EndMove
+ * PreMove is only in ADF and CFB mode
+ * In ADF version, there is a distance gap between Paper flag and Real initial
+ * position and it need premove to real initial position and turn the motor
+ * Inverse and start Fast move to start scan position
+ * In CFB version , PreMove 1 mm to avoid bouncing of PaperFlag sensor then
+ * fast move
+ * In CIS and CSP although there have not PreMove but there have End move
+ * when paper out there still have several mm need to read,
+ * So it need EndMove 2mm and set Inverse Paper
+ */
+static Bool motorP96GotoShadingPosition( pScanData ps )
+{
+ DBG( DBG_LOW, "motorP96GotoShadingPosition()\n" );
+
+ MotorSetConstantMove( ps, 0 ); /* clear scan states */
+ ps->Scan.fMotorBackward = _FALSE; /* forward */
+ ps->bExtraMotorCtrl = ps->IgnorePF;
+
+ MotorP96ConstantMoveProc( ps, 15 * 12 ); /* forward 180 lines */
+
+ if (IODataRegisterFromScanner(ps, ps->RegStatus) & _FLAG_P96_PAPER ) {
+ ps->Asic96Reg.RD_MotorControl = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+
+ DBG( DBG_LOW, "motorP96GotoShadingPosition() failed\n" );
+ return _FALSE;
+ }
+ ps->Scan.fMotorBackward = _TRUE; /* backward */
+ ps->bExtraMotorCtrl = 0; /* no extra action for motor */
+
+ /* backward a few thousand lines to touch sensor */
+ MotorP96ConstantMoveProc( ps, ps->BackwardSteps );
+
+ _DODELAY( 250 );
+
+ IOCmdRegisterToScanner( ps, ps->RegModelControl,
+ (Byte)(ps->AsicReg.RD_ModelControl | _ModelInvertPF));
+
+ ps->Scan.fMotorBackward = _FALSE; /* forward */
+ motorP96ConstantMoveProc1( ps, 14 * 12 * 2); /* ahead 336 lines */
+
+ if( MODEL_OP_A3I == ps->sCaps.Model ) {
+
+ motorP96PositionYProc( ps, 80 );
+
+ } else {
+ /* forward 24 + pScanData->wOverBlue lines */
+ if (!ps->fColorMoreRedFlag)
+ motorP96PositionYProc( ps, ps->wOverBlue + 12 * 2);
+ }
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->bExtraMotorCtrl = ps->IgnorePF;
+ MotorP96ConstantMoveProc( ps, 1200 );
+ }
+
+ IOCmdRegisterToScanner( ps, ps->RegModelControl,
+ ps->AsicReg.RD_ModelControl );
+ return _TRUE;
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP96FillHalfStepTable( pScanData ps )
+{
+#ifdef _A3I_EN
+ if ( ps->Scan.bModuleState == _MotorInStopState ) {
+
+ /* clear the table and get the step value */
+ memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );
+ ps->bMotorStepTableNo = a_bMotorDown2Table[(ps->bMotorSpeedData-1)/2];
+ }
+
+ /* the fastest stepping */
+ if( ps->bMotorSpeedData & 1 ) {
+
+ memset( a_bHalfStepTable,
+ ((ps->Scan.bModuleState != _MotorInStopState) ? 1 : 0),
+ _NUMBER_OF_SCANSTEPS );
+ } else {
+
+ pUChar pbHalfStepTbl;
+ Byte bHalfSteps;
+ pUShort pwMoveStep;
+ DataType Data;
+
+ bHalfSteps = ps->bMotorSpeedData / 2;
+ pwMoveStep = &a_wMoveStepTable[ps->bCurrentLineCount];
+ pbHalfStepTbl = &a_bHalfStepTable[ps->bCurrentLineCount];
+
+ if( ps->DataInf.wAppDataType == COLOR_TRUE24)
+ Data.dwValue = _NUMBER_OF_SCANSTEPS - 1;
+ else
+ Data.dwValue = _NUMBER_OF_SCANSTEPS;
+
+ /* FillDataToHalfStepTable */
+ for(; Data.dwValue; Data.dwValue-- ) {
+
+ if( *pwMoveStep ) { /* need to exposure */
+
+ if( Data.bValue >= bHalfSteps ) {
+
+ pUChar pb = pbHalfStepTbl + bHalfSteps;
+
+ /* AdjustHalfStepStart */
+ if( ps->DataInf.wAppDataType == COLOR_TRUE24 ) {
+
+ if (bHalfSteps >= 2)
+ pb -= (bHalfSteps - 1);
+ }
+ if( pb >= pbEndHalfStepTable )
+ pb -= _NUMBER_OF_SCANSTEPS;
+
+ if( wP96BaseDpi <= ps->PhysicalDpi && *pwMoveStep != 2 )
+ *pb = 1;
+
+ pb += bHalfSteps;
+
+ if( pb >= pbEndHalfStepTable )
+ pb -= _NUMBER_OF_SCANSTEPS;
+
+ *pb = 1;
+ }
+ else
+ *pwMoveStep = 0; /* idle state */
+ }
+ if( ++pwMoveStep >= pwEndMoveStepTable ) {
+ pwMoveStep = a_wMoveStepTable;
+
+ pbHalfStepTbl = a_bHalfStepTable;
+ }
+ else
+ pbHalfStepTbl++;
+ }
+ }
+
+#else
+
+#ifdef DEBUG
+ if( 0 == wP96BaseDpi )
+ DBG( DBG_HIGH, "!!!! WARNING - motorP96FillHalfStepTable(), "
+ "wP96BaseDpi == 0 !!!!\n" );
+#endif
+
+ if ( ps->Scan.bModuleState == _MotorInStopState ) {
+
+ /* clear the table and get the step value */
+ memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );
+ ps->bMotorStepTableNo = a_bMotorDown2Table[(ps->bMotorSpeedData-1)/2];
+ }
+
+ /* the fastest stepping */
+ if( ps->bMotorSpeedData & 1 ) {
+
+ memset( a_bHalfStepTable,
+ ((ps->Scan.bModuleState != _MotorInStopState) ? 1 : 0),
+ _NUMBER_OF_SCANSTEPS );
+ } else {
+
+ pUChar pbHalfStepTbl, pbHalfStepContent;
+ pUShort pwMoveStep;
+ DataType Data;
+
+ pbHalfStepContent = a_pbHalfStepTables[ps->bMotorSpeedData / 2 - 1];
+
+ pwMoveStep = &a_wMoveStepTable[ps->bCurrentLineCount];
+ pbHalfStepTbl = &a_bHalfStepTable[ps->bCurrentLineCount];
+
+ if (ps->DataInf.wAppDataType == COLOR_TRUE24)
+ Data.dwValue = _NUMBER_OF_SCANSTEPS - 1;
+ else
+ Data.dwValue = _NUMBER_OF_SCANSTEPS;
+
+ /* FillDataToHalfStepTable */
+ for (; Data.dwValue; Data.dwValue--) {
+
+ if (*pwMoveStep) { /* need to exposure */
+
+ if (Data.bValue >= *pbHalfStepContent) {
+
+ pUChar pb = pbHalfStepTbl + *pbHalfStepContent;
+
+ if (pb >= pbEndHalfStepTable)
+ pb -= _NUMBER_OF_SCANSTEPS;
+
+ /* JudgeStep1 () */
+ if ((wP96BaseDpi != 600) && (*pwMoveStep != 2)) {
+ if (ps->Scan.bModuleState != _MotorInStopState) {
+ *pb = 1;
+ } else {
+ if (ps->bMotorStepTableNo) {
+ ps->bMotorStepTableNo--;
+ *pb = 1;
+ }
+ }
+ }
+
+ pb += *pbHalfStepContent;
+ if (pb >= pbEndHalfStepTable)
+ pb -= _NUMBER_OF_SCANSTEPS;
+
+ /* JudgeStep2 () */
+ if (ps->Scan.bModuleState == _MotorInStopState) {
+ if (ps->bMotorStepTableNo) {
+ ps->bMotorStepTableNo--;
+ *pb = 1;
+ }
+ } else {
+ *pb = 1;
+ }
+
+ pbHalfStepContent++;
+ } else {
+ *pwMoveStep = 0; /* idle state */
+ }
+ }
+
+ if (++pwMoveStep >= pwEndMoveStepTable) {
+ pwMoveStep = a_wMoveStepTable;
+ pbHalfStepTbl = a_bHalfStepTable;
+ } else {
+ pbHalfStepTbl++;
+ }
+ }
+ }
+#endif
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP96FillDataToColorTable( pScanData ps,
+ Byte bIndex, ULong dwSteps)
+{
+ Byte bColor, bColors;
+ pUChar pb, pb1;
+ pUShort pw;
+ DataType Data;
+
+ for (pb = &a_bColorByteTable[bIndex],
+ pw = &a_wMoveStepTable[bIndex]; dwSteps; dwSteps--) {
+
+ if (*pw) { /* valid state */
+
+ if( *pw >= ps->BufferForColorRunTable ) {
+ DBG( DBG_LOW, "*pw = %u > %u !!\n",
+ *pw, ps->BufferForColorRunTable );
+ } else {
+
+ bColor = ps->pColorRunTable [*pw]; /* get the colors */
+ bColors = a_bColorsSum [bColor & 7];/* number of colors */
+
+ if (bColors) { /* need to read data */
+ if (dwSteps >= bColors) { /* enough room */
+
+ /* separate the colors to byte */
+ pb1 = pb;
+ if (bColor & ps->b1stColor) {
+
+ *pb1 = ps->b1stColorByte;
+ if (++pb1 >= pbEndColorByteTable)
+ pb1 = a_bColorByteTable;
+ }
+
+ if (bColor & ps->b2ndColor) {
+
+ *pb1 = ps->b2ndColorByte;
+ if (++pb1 >= pbEndColorByteTable)
+ pb1 = a_bColorByteTable;
+ }
+
+ if (bColor & ps->b3rdColor)
+ *pb1 = ps->b3rdColorByte;
+ } else
+ *pw = 0;
+ }
+ }
+ }
+
+ if (++pw >= pwEndMoveStepTable) {
+ pw = a_wMoveStepTable;
+ pb = a_bColorByteTable;
+ } else
+ pb++;
+ }
+
+/* ps->bOldSpeed = ps->bMotorRunStatus; non functional */
+
+ /* ToCondense, CondenseColorByteTable */
+ for (dwSteps = _SCANSTATE_BYTES, pw = (pUShort)a_bColorByteTable,
+ pb = ps->a_nbNewAdrPointer; dwSteps; dwSteps--, pw++, pb++) {
+
+ Data.wValue = *pw & 0x0303;
+ *pb = Data.wOverlap.b1st | (Data.wOverlap.b2nd << 4);
+ }
+
+ /* ToCondenseMotor */
+ for (dwSteps = _SCANSTATE_BYTES, pb1 = a_bHalfStepTable,
+ pb = ps->a_nbNewAdrPointer; dwSteps; dwSteps--, pb1++, pb++) {
+
+ if (*pb1++)
+ *pb |= 4;
+
+ if (*pb1)
+ *pb |= 0x40;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP96FillBackColorDataTable( pScanData ps )
+{
+ Byte bIndex;
+ ULong dw;
+
+ if ((ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1) >=
+ _NUMBER_OF_SCANSTEPS){
+ bIndex = ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1 -
+ _NUMBER_OF_SCANSTEPS;
+ } else {
+ bIndex = ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1;
+ }
+ dw = _NUMBER_OF_SCANSTEPS - ps->bNewCurrentLineCountGap;
+
+ motorP96FillDataToColorTable( ps, bIndex, dw );
+}
+
+/*.............................................................................
+ * i/p:
+ * pScanStep = pScanData->pColorRunTable if forward, a_bScanStateTable if backward
+ * dwState = how many states is requested to process
+ * NOTE:
+ * The content of pScanStep contain:
+ * 0: Idle state
+ * 0xff: End mark
+ * others: The motor speed value
+ */
+static void motorP96FillBackLoop( pScanData ps,
+ pUChar pScanStep, ULong dwStates )
+{
+ for (; dwStates; dwStates--) {
+
+ if (*pScanStep == 0xff)
+ break; /* end of states */
+
+ if (*pScanStep) {
+ if (*pScanStep == 1) { /* speed == 1, this state has to step */
+
+ if (ps->dwScanStateCount & 1)
+ ps->a_nbNewAdrPointer[ps->dwScanStateCount / 2] |= 0x40;
+ else
+ ps->a_nbNewAdrPointer[ps->dwScanStateCount / 2] |= 0x04;
+ }
+
+ *pScanStep -= 1; /* speed decrease by 1 */
+
+ if (!(*pScanStep))
+ pScanStep++; /* state processed */
+ } else
+ pScanStep++; /* skip this state */
+
+ if (++ps->dwScanStateCount == _NUMBER_OF_SCANSTEPS)
+ ps->dwScanStateCount = 0; /* reset to begin */
+ }
+
+ if (*pScanStep != 0xff)
+ ps->fFullLength = _FALSE;
+ else
+ ps->fFullLength = _TRUE;
+
+ IOSetToMotorStepCount( ps ); /* put all scan states to ASIC */
+}
+
+/*.............................................................................
+ * 1) Clear scan states
+ * 2) Adjust the new scan state
+ */
+static void motorP96FillRunNewAdrPointer( pScanData ps )
+{
+ ScanState sState;
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES);
+
+ IOGetCurrentStateCount( ps, &sState );
+
+ if (sState.bStep < ps->bOldStateCount )
+ sState.bStep += _NUMBER_OF_SCANSTEPS;/* over table (table just can */
+ /* holds 64 step, then reset to */
+ /* 0, so if less than means over*/
+ /* the table) */
+
+ sState.bStep -= ps->bOldStateCount; /* how many states passed */
+ ps->pScanState += sState.bStep;
+
+ /*
+ * if current state != no stepped or stepped a cycle, fill the table with
+ * 1 in NOT STEPPED length. (1 means to this state has to be processing).
+ */
+ if (sState.bStep && sState.bStep != (_NUMBER_OF_SCANSTEPS - 1))
+ memset( ps->pScanState, 1, _NUMBER_OF_SCANSTEPS - sState.bStep - 1 );
+
+ IOGetCurrentStateCount( ps, &sState);
+ ps->bOldStateCount = sState.bStep; /* update current state */
+ ps->dwScanStateCount = (ULong)((sState.bStep + 1) & (_NUMBER_OF_SCANSTEPS - 1));
+
+ /* fill begin at next step */
+ motorP96FillBackLoop( ps, ps->pScanState, (_NUMBER_OF_SCANSTEPS - 1));
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP96SetupRunTable( pScanData ps )
+{
+ Short siSum;
+ UShort wLoop;
+ UShort wLengthY;
+ DataPointer p;
+
+ DBG( DBG_LOW, "motorP96SetupRunTable()\n" );
+
+ /* SetMaxDpiAndLength (ps) */
+#ifdef _A3I_EN
+ if( ps->DataInf.xyPhyDpi.y > ps->PhysicalDpi ) {
+
+ wLengthY = 6800 * 2;
+ wP96BaseDpi = ps->PhysicalDpi *2;
+ } else {
+ wLengthY = 6800;
+ wP96BaseDpi = ps->LensInf.rDpiY.wPhyMax >> 1;
+ }
+#else
+ if( ps->DataInf.xyPhyDpi.y > (ps->LensInf.rDpiY.wPhyMax / 2)) {
+
+ wLengthY = ps->LensInf.rExtentY.wMax << 1;
+ wP96BaseDpi = ps->LensInf.rDpiY.wPhyMax;
+ } else {
+ wLengthY = ps->LensInf.rExtentY.wMax;
+ wP96BaseDpi = ps->LensInf.rDpiY.wPhyMax >> 1;
+ }
+#endif
+
+ DBG( DBG_LOW, "wLengthY = %u, wP96BaseDpi = %u\n", wLengthY, wP96BaseDpi );
+
+ /* ClearColorRunTable (ps) */
+ memset( ps->pColorRunTable, 0, ps->BufferForColorRunTable ); /*wLengthY + 0x60 ); */
+
+ p.pb = ps->pColorRunTable + _SCANSTATE_BYTES;
+#ifdef _A3I_EN
+ wLoop = wLengthY + 200;
+#else
+ wLoop = wLengthY + 0x20;
+#endif
+ siSum = (Short)wP96BaseDpi;
+
+ if (ps->DataInf.wPhyDataType != COLOR_TRUE24) {
+ for (; wLoop; wLoop--, p.pb++) {
+ if ((siSum -= (Short)ps->DataInf.xyPhyDpi.y) <= 0) {
+ siSum += (Short)wP96BaseDpi;
+ *p.pb = _COLORRUNTABLE_GREEN;
+ }
+ }
+
+#ifdef _A3I_EN
+ memset( ps->pColorRunTable + _NUMBER_OF_SCANSTEPS / 8 + wLengthY,
+ 0x77, 0x100 );
+#endif
+ } else {
+ /* CalColorRunTable */
+ DataType Data;
+
+ if (ps->fSonyCCD) {
+
+ if((ps->sCaps.Model == MODEL_OP_12000P) ||
+ (ps->sCaps.Model == MODEL_OP_A3I)) {
+ Data.wValue = (_COLORRUNTABLE_RED << 8) | _COLORRUNTABLE_BLUE;
+ } else {
+ Data.wValue = (_COLORRUNTABLE_GREEN << 8) | _COLORRUNTABLE_BLUE;
+ }
+ } else {
+ Data.wValue = (_COLORRUNTABLE_BLUE << 8) | _COLORRUNTABLE_GREEN;
+ }
+
+ for (; wLoop; wLoop--, p.pb++) {
+
+ if ((siSum -= (Short)ps->DataInf.xyPhyDpi.y) <= 0) {
+ siSum += (Short)wP96BaseDpi;
+
+ if((ps->sCaps.Model == MODEL_OP_12000P)||
+ (ps->sCaps.Model == MODEL_OP_A3I)) {
+ *p.pb |= _COLORRUNTABLE_GREEN;
+ } else {
+ *p.pb |= _COLORRUNTABLE_RED;
+ }
+
+ /* Sony:Green,Toshiba:Blue */
+ *(p.pb + 8) |= Data.wOverlap.b2nd;
+ *(p.pb + 16) |= Data.wOverlap.b1st;
+ }
+ }
+
+#ifdef _A3I_EN
+ memset( ps->pColorRunTable + _NUMBER_OF_SCANSTEPS / 8 + wLengthY,
+ 0x77, 0x100 );
+#endif
+ if (ps->DataInf.xyPhyDpi.y < 100) {
+
+ Byte bColor;
+
+ /* CheckColorTable () */
+ if (ps->fSonyCCD)
+ Data.wValue = 0xdd22;
+ else
+ Data.wValue = 0xbb44;
+
+ for (wLoop = wLengthY - _SCANSTATE_BYTES,
+ p.pb = ps->pColorRunTable + _SCANSTATE_BYTES;
+ wLoop; wLoop--, p.pb++) {
+ bColor = 0;
+
+ switch (a_bColorsSum[*p.pb & 0x0f]) {
+
+ case 3:
+ if (*(p.pb + 2))
+ bColor = 1;
+ case 2:
+ if (*(p.pb + 1))
+ bColor++;
+ if (bColor == 2) {
+ *p.pb &= ~_COLORRUNTABLE_RED;
+ *(p.pb - 2) = _COLORRUNTABLE_RED;
+ }
+
+ if (bColor) {
+ if (*p.pb & ps->RedDataReady) {
+ *p.pb &= ~_COLORRUNTABLE_RED;
+ *(p.pb - 1) = _COLORRUNTABLE_RED;
+ } else {
+ *p.pb &= Data.wOverlap.b2nd;
+ *(p.pb - 1) = Data.wOverlap.b1st;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP96UpdateDataCurrentReadLine( pScanData ps )
+{
+ ScanState State1, State2;
+ TimerDef timer;
+
+ IOGetCurrentStateCount( ps, &State1 );
+ IOGetCurrentStateCount( ps, &State2 );
+
+ if (State1.bStatus == State2.bStatus) {
+
+ if (!(State2.bStatus & _SCANSTATE_STOP)) {
+
+ /* motor still running */
+ if (State2.bStep < ps->bCurrentLineCount) {
+ State2.bStep = State2.bStep + _NUMBER_OF_SCANSTEPS -
+ ps->bCurrentLineCount;
+ } else
+ State2.bStep -= ps->bCurrentLineCount;
+
+ if (State2.bStep >= (_NUMBER_OF_SCANSTEPS - 3)) {
+
+ MiscStartTimer( &timer, _SECOND );
+
+ do {
+ State2.bStatus = IOGetScanState( ps, _FALSE );
+
+ } while (!(State2.bStatus & _SCANSTATE_STOP) &&
+ !MiscCheckTimer( &timer ));
+ } else
+ if (State2.bStep < 40)
+ return;
+ }
+
+#ifdef _A3I_EN
+ if( ps->bFifoCount >= 140) {
+#else
+ if( ps->bFifoCount >= 20) {
+#endif
+ if( 1 == ps->bCurrentSpeed ) {
+ ps->bCurrentSpeed *= 2;
+ } else {
+ if( COLOR_TRUE24 == ps->DataInf.wPhyDataType )
+ ps->bCurrentSpeed += 4;
+ else
+ ps->bCurrentSpeed += 2;
+ }
+
+ MotorP96AdjustCurrentSpeed( ps, ps->bCurrentSpeed );
+ }
+
+ /*
+ * when using the full-step speed on 600 dpi models, then set
+ * the motor into half-step mode, to avoid that the scanner hits
+ * the back of its bed
+ */
+/* HEINER:A3I */
+#if 1
+ if((600 == ps->PhysicalDpi) && (1 == ps->bCurrentSpeed)) {
+
+ if( ps->Asic96Reg.RD_MotorControl & ps->FullStep ) {
+ ps->Asic96Reg.RD_MotorControl &= ~ps->FullStep;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+ }
+ }
+#endif
+ ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE );
+
+ IOSetToMotorRegister( ps);
+ }
+}
+
+/*.............................................................................
+ * 1) Save the current scan state
+ * 2) Set the motor direction
+ */
+static void motorGoHalfStep1( pScanData ps )
+{
+ ScanState sState;
+
+ IOGetCurrentStateCount( ps, &sState );
+
+ ps->bOldStateCount = sState.bStep;
+
+ motorSetRunPositionRegister(ps);
+ ps->pScanState = a_bScanStateTable;
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ ps->FillRunNewAdrPointer( ps );
+
+ while (!motorCheckMotorPresetLength(ps))
+ motorP98FillRunNewAdrPointer1( ps );
+ } else {
+
+ while (!motorCheckMotorPresetLength( ps ))
+ ps->FillRunNewAdrPointer( ps );
+ }
+}
+
+/*.............................................................................
+ * when loosing data, we use this function to go back some lines and read them
+ * again...
+ */
+static void motorP96WaitBack( pScanData ps )
+{
+ DataPointer p;
+ DataType Data;
+ ULong dw;
+ UShort w;
+ UShort wStayMaxStep;
+
+ /* FindMaxMoveStepIndex () */
+
+ p.pw = a_wMoveStepTable;
+
+ for( Data.dwValue = _NUMBER_OF_SCANSTEPS, wStayMaxStep = 1; Data.dwValue;
+ Data.dwValue--, p.pw++ )
+ if( *p.pw > wStayMaxStep )
+ wStayMaxStep = *p.pw; /* save the largest step number */
+
+ if( ps->DataInf.xyPhyDpi.y > ps->PhysicalDpi )
+ wStayMaxStep -= 40;
+ else
+ wStayMaxStep -= 20;
+
+ IORegisterDirectToScanner( ps, ps->RegInitDataFifo );
+
+ memset( a_bScanStateTable, 1, _P96_BACKMOVES );
+ memset(&a_bScanStateTable[_P96_BACKMOVES], 0xff, 250 - _P96_BACKMOVES );
+
+ ps->Scan.fMotorBackward = _TRUE;
+ motorGoHalfStep1( ps ); /* backward 130 lines */
+
+ _DODELAY(200); /* let the motor stable */
+
+ if( ps->DataInf.xyPhyDpi.y <= ps->PhysicalDpi ) {
+ if( ps->DataInf.wPhyDataType == COLOR_TRUE24 ) {
+ dw = _P96_FORWARDMOVES - 1;
+ } else {
+ dw = _P96_FORWARDMOVES - 2;
+ }
+ } else {
+ dw = _P96_FORWARDMOVES;
+ }
+
+ memset( a_bScanStateTable, 1, dw );
+ memset(&a_bScanStateTable[dw], 0xff, 250 - dw );
+
+ ps->Scan.fMotorBackward = _FALSE;
+ motorGoHalfStep1( ps ); /* move forward */
+
+ /* GetNowStepTable () */
+ ps->bCurrentLineCount = IOGetScanState( ps, _FALSE ) & _SCANSTATE_MASK;
+ ps->bNewCurrentLineCountGap = 0;
+
+ /* ClearColorByteTable () */
+ memset( a_bColorByteTable, 0, _NUMBER_OF_SCANSTEPS );
+
+ /* ClearHalfStepTable () */
+ memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );
+
+ /* FillWaitMoveStepTable () */
+ p.pw = &a_wMoveStepTable[((ps->bCurrentLineCount + 1) & 0x3f)];
+ *p.pw = 1;
+ p.pw++;
+
+ for(w = wStayMaxStep, Data.bValue = ps->bMotorSpeedData, dw = 60;dw;dw--) {
+
+ if( p.pw >= pwEndMoveStepTable ) /* make sure pointer in range */
+ p.pw = a_wMoveStepTable;
+
+ if (--Data.bValue)
+ *p.pw = 0; /* don't step */
+ else {
+ Data.bValue = ps->bMotorSpeedData; /* speed value */
+ *p.pw = w; /* the ptr to pColorRunTable */
+ w++; /* pointer++ */
+ }
+
+ p.pw++; /* to next entry */
+ }
+ motorP96FillHalfStepTable( ps );
+ motorP96FillBackColorDataTable( ps );
+}
+
+/*.............................................................................
+ * when loosing data, we use this function to go back some lines and read them
+ * again...
+ */
+static void motorP98WaitBack( pScanData ps )
+{
+ DataPointer p;
+ DataType Data;
+ ULong dw;
+ UShort w;
+ UShort wStayMaxStep;
+ UShort back, forward;
+
+ p.pw = &a_wMoveStepTable[ps->bCurrentLineCount];
+
+ if (0 == *p.pw) {
+
+ for (w = _NUMBER_OF_SCANSTEPS; w && !*p.pw; w--) {
+ p.pw--;
+ if (p.pw < a_wMoveStepTable)
+ p.pw = &a_wMoveStepTable[_NUMBER_OF_SCANSTEPS - 1];
+ }
+ wStayMaxStep = *p.pw + 1;
+ } else {
+ wStayMaxStep = *p.pw; /* save the largest step number */
+ }
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ forward = _P98_FORWARDMOVES;
+ back = _P98_BACKMOVES;
+ } else {
+ forward = _P96_FORWARDMOVES;
+ back = _P96_BACKMOVES;
+ }
+
+ /*
+ * Off to avoid the problem of block data re-read/lost
+ */
+ memset( a_bScanStateTable, 1, back );
+ memset( &a_bScanStateTable[back], 0xff, (_SCANSTATE_TABLE_SIZE - back));
+ ps->Scan.fMotorBackward = _TRUE;
+ motorGoHalfStep1( ps );
+
+ _DODELAY(200); /* let the motor stable */
+
+ memset(a_bScanStateTable, 1, forward );
+ memset(&a_bScanStateTable[forward], 0xff, (_SCANSTATE_TABLE_SIZE-forward));
+ ps->Scan.fMotorBackward = _FALSE;
+ motorGoHalfStep1( ps );
+
+ ps->bNewCurrentLineCountGap = 0;
+
+ memset( a_bColorByteTable, 0, _NUMBER_OF_SCANSTEPS );
+ memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );
+
+ ps->bCurrentLineCount = (ps->bCurrentLineCount + 1) & 0x3f;
+
+ p.pw = &a_wMoveStepTable[ps->bCurrentLineCount];
+
+ for (w = wStayMaxStep, Data.bValue = ps->bMotorSpeedData,
+ dw = _NUMBER_OF_SCANSTEPS; dw; dw--) {
+ if (--Data.bValue) {
+ *p.pw = 0; /* don't step */
+ } else {
+
+ /* speed value */
+ Data.bValue = ps->bMotorSpeedData;
+ *p.pw = w; /* the pointer to pColorRunTable*/
+ w++; /* pointer++ */
+ }
+ /* make sure pointer in range */
+ if (++p.pw >= pwEndMoveStepTable)
+ p.pw = a_wMoveStepTable;
+ }
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+ motorP98FillHalfStepTable( ps );
+ motorP98FillBackColorDataTable( ps );
+ } else {
+ motorP96FillHalfStepTable( ps );
+ motorP96FillBackColorDataTable( ps );
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorFillMoveStepTable( pScanData ps,
+ UShort wIndex, Byte bStep, pUShort pw )
+{
+ UShort w;
+ Byte b;
+
+ if (++pw >= pwEndMoveStepTable )
+ pw = a_wMoveStepTable;
+
+ wIndex++;
+
+ b = ps->bMotorSpeedData;
+
+ for (w = _NUMBER_OF_SCANSTEPS - bStep; w; w--) {
+ if (b == 1) {
+ b = ps->bMotorSpeedData;
+ *pw = wIndex;
+ wIndex++;
+ } else {
+ b--;
+ *pw = 0;
+ }
+ if (++pw >= pwEndMoveStepTable)
+ pw = a_wMoveStepTable;
+ }
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ motorP98FillHalfStepTable( ps );
+ else
+ motorP96FillHalfStepTable( ps );
+
+ if ((ps->bCurrentLineCount + 1) >= _NUMBER_OF_SCANSTEPS) {
+ b = ps->bCurrentLineCount + 1 - _NUMBER_OF_SCANSTEPS;
+ } else {
+ b = ps->bCurrentLineCount + 1;
+ }
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ motorP98FillDataToColorTable( ps, b, _NUMBER_OF_SCANSTEPS - 1);
+ else
+ motorP96FillDataToColorTable( ps, b, _NUMBER_OF_SCANSTEPS - 1);
+}
+
+/*.............................................................................
+ *
+ */
+static void noMotorRunStatusStop( pScanData ps, Byte bScanState )
+{
+ Byte b, b1, bCur;
+ pUShort pw;
+ pByte pb;
+ UShort w;
+
+ ps->bCurrentLineCount = (bScanState & _SCANSTATE_MASK);
+
+ ps->Scan.fRefreshState = _FALSE;
+
+ IORegisterDirectToScanner( ps, ps->RegRefreshScanState );
+
+ bCur = ps->bCurrentLineCount;
+ pw = &a_wMoveStepTable[bCur];
+ pb = ps->pColorRunTable;
+ b = 0;
+ b1 = 0;
+ w = _NUMBER_OF_SCANSTEPS;
+
+ if (*pw) {
+ b = a_bColorsSum[pb [*pw] >> 4];
+ if (b) {
+ motorClearColorByteTableLoop0( ps, b );
+ ps->bNewCurrentLineCountGap = b;
+
+ motorFillMoveStepTable( ps, *pw, 1, pw );
+ return;
+ }
+ b1++;
+ bCur--;
+
+ if (--pw < a_wMoveStepTable) {
+ pw = &a_wMoveStepTable [_NUMBER_OF_SCANSTEPS - 1];
+ bCur = _NUMBER_OF_SCANSTEPS - 1;
+ }
+ }
+
+ for(; w; w--) {
+ if (*pw) {
+ if (*pw < _SCANSTATE_BYTES) {
+ b = 0;
+ break;
+ } else
+ if ((b = a_bColorsSum [pb [*pw] >> 4]))
+ break;
+ }
+ b1++;
+ bCur--;
+
+ if (--pw < a_wMoveStepTable) {
+ pw = &a_wMoveStepTable [_NUMBER_OF_SCANSTEPS - 1];
+ bCur = _NUMBER_OF_SCANSTEPS - 1;
+ }
+ }
+
+ if (b1 == _NUMBER_OF_SCANSTEPS) {
+ ps->bNewCurrentLineCountGap = 0;
+ ps->bNewGap = 0;
+ } else {
+ ps->bNewCurrentLineCountGap = b1;
+ ps->bNewGap = b;
+ }
+
+ motorClearColorByteTableLoop1( ps );
+
+ motorFillMoveStepTable( ps, *pw, 0, pw);
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP96SetSpeed( pScanData ps, Byte bSpeed, Bool fSetRunState )
+{
+#if 0
+ PUCHAR pb;
+ Byte bScanState;
+#endif
+ Byte bState, bData;
+ UShort wMoveStep;
+ pUShort pw;
+ ULong dw;
+ TimerDef timer;
+
+ if( fSetRunState )
+ ps->Scan.bModuleState = _MotorInNormalState;
+
+ ps->bMotorSpeedData = bSpeed;
+
+ if( ps->bMoveDataOutFlag == _DataAfterRefreshState) {
+
+ ps->bMoveDataOutFlag = _DataInNormalState;
+
+ MiscStartTimer( &timer, (_SECOND /2));
+
+ while (!MiscCheckTimer(&timer)) {
+ if ((bState = IOGetScanState( ps, _FALSE)) & _SCANSTATE_STOP) {
+ ps->bCurrentLineCount = bState & ~_SCANSTATE_STOP;
+ motorP96WaitBack( ps );
+ return;
+ }
+ }
+ }
+
+ bState = IOGetScanState( ps, _FALSE );
+
+ if((ps->Scan.bModuleState != _MotorInStopState) ||
+ !(bState & _SCANSTATE_STOP)) {
+
+ /* Try to find the available step for all rest steps.
+ * 1) if current step is valid (with data request), fill all steps
+ * after it
+ * 2) if current step is NULL (for delay purpose), backward search the
+ * valid entry, then fill all steps after it
+ * 3) if no step is valid, fill all entries
+ */
+ Byte bColors = 0;
+ UShort w;
+
+ /* NoMotorRunStatusStop () */
+ ps->bCurrentLineCount = (bState &= _SCANSTATE_MASK);
+ ps->Scan.fRefreshState = _TRUE;
+
+ IORegisterDirectToScanner( ps, ps->RegRefreshScanState );
+
+ pw = &a_wMoveStepTable[bState];
+ bData = 0;
+ bState = ps->bCurrentLineCount;
+ dw = _NUMBER_OF_SCANSTEPS;
+
+ if( (wMoveStep = *pw) ) {
+
+ bColors = a_bColorsSum[ ps->pColorRunTable[*pw] / 16];
+
+ if( bColors ) {
+
+ motorClearColorByteTableLoop0( ps, bColors );
+ ps->bNewCurrentLineCountGap = bColors;
+ bColors = 1;
+ goto FillMoveStepTable;
+
+ } else {
+
+ bData++;
+ dw--;
+ if( --pw < a_wMoveStepTable ) {
+ pw = a_wMoveStepTable + _NUMBER_OF_SCANSTEPS - 1;
+ bState = _NUMBER_OF_SCANSTEPS - 1;
+ }
+ else
+ bState--;
+ }
+ }
+
+ /* FindNextStep */
+ while( dw-- ) {
+
+ if( (wMoveStep = *pw) ) {
+ if (wMoveStep < (_NUMBER_OF_SCANSTEPS / 2)) {
+ bColors = 0;
+ break;
+ }
+ if((bColors = a_bColorsSum [ps->pColorRunTable[wMoveStep] / 16]))
+ break;
+ }
+
+ bData++;
+ if( --pw < a_wMoveStepTable ) {
+ pw = a_wMoveStepTable + _NUMBER_OF_SCANSTEPS - 1;
+ bState = _NUMBER_OF_SCANSTEPS - 1;
+ }
+ else
+ bState--;
+ }
+
+ if (bData == _NUMBER_OF_SCANSTEPS )
+ bData = bColors = 0;
+
+ ps->bNewCurrentLineCountGap = bData;
+ ps->bNewGap = bColors;
+ motorClearColorByteTableLoop1( ps );
+ bColors = 0;
+
+ /* use pw (new pointer) and new speed to recreate MoveStepTable
+ * wMoveStep = Index number from a_wMoveStepTable ([esi])
+ * bColors = number of steps should be reserved
+ * pw = where to fill
+ */
+
+FillMoveStepTable:
+
+ motorP96GetStartStopGap( ps, _TRUE );
+
+ if( !ps->bMotorStepTableNo )
+ ps->bMotorStepTableNo = 1;
+
+ if( ps->bMotorStepTableNo != 0xff && ps->IO.portMode == _PORT_SPP &&
+ ps->DataInf.xyPhyDpi.y <= 200) {
+ ps->bMotorStepTableNo++;
+ }
+
+ if (++pw >= pwEndMoveStepTable)
+ pw = a_wMoveStepTable;
+
+ for( dw = _NUMBER_OF_SCANSTEPS - bColors, wMoveStep++,
+ bData = ps->bMotorSpeedData; dw; dw-- ) {
+ if( bData == 1 ) {
+
+ bData = ps->bMotorSpeedData;
+ if( ps->bMotorStepTableNo ) {
+
+ ps->bMotorStepTableNo--;
+ w = wMoveStep;
+ wMoveStep++;
+
+ } else {
+ bData--;
+ w = 0;
+ }
+ } else {
+ bData--;
+ w = 0;
+ }
+ *pw = w;
+
+ if (++pw >= pwEndMoveStepTable)
+ pw = a_wMoveStepTable;
+ }
+
+ motorP96FillHalfStepTable( ps );
+
+ /* FillColorBytesTable */
+ if((ps->bCurrentLineCount + 1) < _NUMBER_OF_SCANSTEPS )
+ bState = ps->bCurrentLineCount + 1;
+ else
+ bState = ps->bCurrentLineCount + 1 - _NUMBER_OF_SCANSTEPS;
+
+ motorP96FillDataToColorTable( ps, bState, _NUMBER_OF_SCANSTEPS - 1);
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98SetSpeed( pScanData ps, Byte bSpeed, Bool fSetRunState )
+{
+ static Byte lastFifoState = 0;
+
+ Bool overflow;
+ Byte bOld1ScanState, bData;
+
+ if( fSetRunState )
+ ps->Scan.bModuleState = _MotorInNormalState;
+
+ ps->bMotorSpeedData = bSpeed;
+ overflow = _FALSE;
+
+ if( _ASIC_IS_98001 != ps->sCaps.AsicID ) {
+ ps->bMoveDataOutFlag = _DataInNormalState;
+
+ bData = IODataRegisterFromScanner( ps, ps->RegFifoOffset );
+
+ if((lastFifoState > _P96_FIFOOVERFLOWTHRESH) &&
+ (bData < lastFifoState)) {
+ DBG( DBG_HIGH, "FIFO OVERFLOW, loosing data !!\n" );
+ overflow = _TRUE;
+ }
+ lastFifoState = bData;
+ }
+
+ bOld1ScanState = IOGetScanState( ps, _FALSE );
+
+ if(!(bOld1ScanState & _SCANSTATE_STOP) && !overflow)
+ noMotorRunStatusStop( ps, bOld1ScanState );
+ else {
+ ps->bCurrentLineCount = (bOld1ScanState & 0x3f);
+ ps->Scan.bModuleState = _MotorGoBackward;
+
+ motorP98WaitBack( ps );
+
+ if( overflow )
+ lastFifoState = 0;
+
+ if( _ASIC_IS_98001 != ps->sCaps.AsicID )
+ ps->bMoveDataOutFlag = _DataFromStopState;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98003ModuleFreeRun( pScanData ps, ULong steps )
+{
+ IODataToRegister( ps, ps->RegMotorFreeRunCount1, (_HIBYTE(steps)));
+ IODataToRegister( ps, ps->RegMotorFreeRunCount0, (_LOBYTE(steps)));
+ IORegisterToScanner( ps, ps->RegMotorFreeRunTrigger );
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98003ModuleToHome( pScanData ps )
+{
+ if(!(IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER)) {
+
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (Byte)(ps->AsicReg.RD_Motor0Control|_MotorDirForward));
+
+ MotorP98003PositionYProc( ps, 40 );
+ MotorP98003BackToHomeSensor( ps );
+ _DODELAY( 250 );
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98003DownloadNullScanStates( pScanData ps )
+{
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ IODownloadScanStates( ps );
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98003Force16Steps( pScanData ps )
+{
+ ULong dw;
+
+ IODataToRegister( ps, ps->RegStepControl, _MOTOR0_ONESTEP);
+ IODataToRegister( ps, ps->RegMotor0Control, _FORWARD_MOTOR );
+
+ for(dw = 16; dw; dw--) {
+ IORegisterToScanner( ps, ps->RegForceStep );
+ _DODELAY( 10 );
+ }
+
+ IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
+}
+
+/*.............................................................................
+ *
+ */
+static void motorP98003WaitForPositionY( pScanData ps )
+{
+ Byte bXStep;
+ ULong dwBeginY;
+
+ dwBeginY = (ULong)ps->DataInf.crImage.y * 4 + ps->Scan.dwScanOrigin;
+
+ if( ps->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+ if( ps->Device.f0_8_16 )
+ dwBeginY += 16;
+ else
+ dwBeginY += 8;
+ }
+
+ bXStep = (Byte)((ps->DataInf.wPhyDataType <= COLOR_256GRAY) ?
+ ps->Device.XStepMono : ps->Device.XStepColor);
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ bXStep = 8;
+
+ motorP98003Force16Steps( ps);
+ dwBeginY -= 16;
+
+ if (dwBeginY > (_RFT_SCANNING_ORG + _P98003_YOFFSET) &&
+ bXStep < ps->AsicReg.RD_XStepTime) {
+
+ IODataToRegister( ps, ps->RegMotorDriverType, ps->Scan.motorPower );
+ _DODELAY( 12 );
+ IODataToRegister( ps, ps->RegXStepTime, bXStep);
+ IODataToRegister( ps, ps->RegExtendedXStep, 0 );
+ IODataToRegister( ps, ps->RegScanControl1,
+ (UChar)(ps->AsicReg.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
+ MotorP98003PositionYProc( ps, dwBeginY - 64 );
+ dwBeginY = 64;
+ }
+
+ IODataToRegister( ps, ps->RegFifoFullLength0, _LOBYTE(ps->AsicReg.RD_BufFullSize));
+ IODataToRegister( ps, ps->RegFifoFullLength1, _HIBYTE(ps->AsicReg.RD_BufFullSize));
+ IODataToRegister( ps, ps->RegFifoFullLength2, _LOBYTE(_HIWORD(ps->AsicReg.RD_BufFullSize)));
+
+ IODataToRegister( ps, ps->RegMotorDriverType, ps->AsicReg.RD_MotorDriverType);
+ _DODELAY( 12 );
+
+ if(!ps->Device.f2003 || (ps->Shade.bIntermediate & _ScanMode_AverageOut) ||
+ ( ps->DataInf.xyAppDpi.y <= 75 &&
+ ps->DataInf.wPhyDataType <= COLOR_256GRAY)) {
+ IODataToRegister( ps, ps->RegMotorDriverType,
+ (Byte)(ps->Scan.motorPower & (_MOTORR_MASK | _MOTORR_STRONG)));
+ } else {
+ IODataToRegister( ps, ps->RegMotorDriverType,
+ ps->AsicReg.RD_MotorDriverType );
+ }
+
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime );
+ IODataToRegister( ps, ps->RegExtendedXStep, ps->AsicReg.RD_ExtXStepTime );
+ IODataToRegister( ps, ps->RegScanControl1,
+ (Byte)(ps->AsicReg.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
+
+ if( ps->DataInf.dwVxdFlag & _VF_PREVIEW ) {
+
+ TimerDef timer;
+
+ motorP98003ModuleFreeRun( ps, dwBeginY );
+ _DODELAY( 15 );
+
+ MiscStartTimer( &timer, (_SECOND * 20));
+
+ while(( IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING) &&
+ !MiscCheckTimer(&timer));
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+ } else {
+ MotorP98003PositionYProc( ps, dwBeginY );
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+ }
+}
+
+/*.............................................................................
+ * move the sensor to the appropriate shading position
+ */
+static Bool motorP98003GotoShadingPosition( pScanData ps )
+{
+ motorP98003ModuleToHome( ps );
+
+ /* position to somewhere under the transparency adapter */
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+
+ MotorP98003ForceToLeaveHomePos( ps );
+ motorP98003DownloadNullScanStates( ps );
+
+ IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan);
+ IODataToRegister( ps, ps->RegMotor0Control, _FORWARD_MOTOR );
+ IODataToRegister( ps, ps->RegXStepTime, 6);
+ IODataToRegister( ps, ps->RegExtendedXStep, 0);
+ IODataToRegister( ps, ps->RegScanControl1, _MFRC_BY_XSTEP);
+
+ MotorP98003PositionYProc( ps, _TPA_P98003_SHADINGORG );
+ }
+
+ return _TRUE;
+}
+
+/*.............................................................................
+ * initialize this module and setup the correct function pointer according
+ * to the ASIC
+ */
+static void motorP98003PositionModuleToHome( pScanData ps )
+{
+ Byte save, saveModel;
+
+ saveModel = ps->AsicReg.RD_ModelControl;
+
+ ps->Scan.fRefreshState = _FALSE;
+ motorP98003DownloadNullScanStates( ps );
+
+ _DODELAY( 1000UL / 8UL);
+
+ save = ps->Shade.bIntermediate;
+
+ ps->Shade.bIntermediate = _ScanMode_AverageOut;
+ ps->ReInitAsic( ps, _FALSE );
+ ps->Shade.bIntermediate = save;
+
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+ IORegisterToScanner( ps, ps->RegResetMTSC );
+ IODataToRegister( ps, ps->RegScanControl1, 0 );
+
+ IODataToRegister( ps, ps->RegModelControl,
+ ps->Device.ModelCtrl | _ModelDpi300 );
+
+ IODataToRegister( ps, ps->RegLineControl, 80);
+ IODataToRegister( ps, ps->RegXStepTime, ps->Device.XStepBack);
+ IODataToRegister( ps, ps->RegMotorDriverType, ps->Scan.motorPower);
+
+ _DODELAY( 12 );
+
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (_MotorHHomeStop | _MotorOn | _MotorHQuarterStep |
+ _MotorPowerEnable));
+ IODataToRegister( ps, ps->RegStepControl,
+ (_MOTOR0_SCANSTATE | _MOTOR_FREERUN));
+
+ memset( ps->a_nbNewAdrPointer, 0x88, _SCANSTATE_BYTES );
+ IODownloadScanStates( ps );
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ ps->AsicReg.RD_ModelControl = saveModel;
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * initialize this module and setup the correct function pointer according
+ * to the ASIC
+ */
+_LOC int MotorInitialize( pScanData ps )
+{
+ DBG( DBG_HIGH, "MotorInitialize()\n" );
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ ps->a_wMoveStepTable = a_wMoveStepTable;
+ ps->a_bColorByteTable = a_bColorByteTable;
+ wP96BaseDpi = 0;
+
+ ps->PauseColorMotorRunStates = motorPauseColorMotorRunStates;
+
+ /*
+ * depending on the asic, we set some functions
+ */
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ ps->WaitForPositionY = motorP98WaitForPositionY;
+ ps->GotoShadingPosition = motorP98GotoShadingPosition;
+ ps->FillRunNewAdrPointer = motorP98FillRunNewAdrPointer;
+ ps->SetupMotorRunTable = motorP98SetupRunTable;
+ ps->UpdateDataCurrentReadLine = motorP98UpdateDataCurrentReadLine;
+ ps->SetMotorSpeed = motorP98SetSpeed;
+
+ } else if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ ps->WaitForPositionY = motorP98003WaitForPositionY;
+ ps->GotoShadingPosition = motorP98003GotoShadingPosition;
+ ps->SetMotorSpeed = motorP98SetSpeed;
+
+ } else if( _IS_ASIC96(ps->sCaps.AsicID)) {
+
+ ps->WaitForPositionY = motorP96WaitForPositionY;
+ ps->GotoShadingPosition = motorP96GotoShadingPosition;
+ ps->FillRunNewAdrPointer = motorP96FillRunNewAdrPointer;
+ ps->SetupMotorRunTable = motorP96SetupRunTable;
+ ps->UpdateDataCurrentReadLine = motorP96UpdateDataCurrentReadLine;
+ ps->SetMotorSpeed = motorP96SetSpeed;
+
+ } else {
+
+ DBG( DBG_HIGH , "NOT SUPPORTED ASIC !!!\n" );
+ return _E_NOSUPP;
+ }
+ return _OK;
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorSetConstantMove( pScanData ps, Byte bMovePerStep )
+{
+ DataPointer p;
+ ULong dw;
+
+ p.pb = ps->a_nbNewAdrPointer;
+
+ switch( bMovePerStep )
+ {
+ case 0: /* doesn't move at all */
+ for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++) {
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ *p.pdw &= 0x77777777;
+ else
+ *p.pdw &= 0xbbbbbbbb;
+ }
+ break;
+
+ case 1:
+ for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++) {
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ *p.pdw |= 0x88888888;
+ else
+ *p.pdw |= 0x44444444;
+ }
+ break;
+
+ case 2:
+ for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++) {
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ *p.pdw |= 0x80808080;
+ else
+ *p.pdw |= 0x40404040;
+ }
+ break;
+
+ default:
+ {
+ Byte bMoves = bMovePerStep;
+
+ for (dw = _SCANSTATE_BYTES; dw; dw--, p.pb++) {
+ if (!(--bMoves)) {
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ *p.pb |= 8;
+ else
+ *p.pb |= 4;
+ bMoves = bMovePerStep;
+ }
+ if (!(--bMoves)) {
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID )
+ *p.pb |= 0x80;
+ else
+ *p.pb |= 0x40;
+ bMoves = bMovePerStep;
+ }
+ }
+ }
+ }
+ IOSetToMotorRegister( ps );
+}
+
+/*.............................................................................
+ * function to bring the sensor back home
+ */
+_LOC void MotorToHomePosition( pScanData ps )
+{
+ TimerDef timer;
+ ScanState StateStatus;
+
+ DBG( DBG_HIGH, "Waiting for Sensor to be back in position\n" );
+ _DODELAY( 250 );
+
+ if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
+
+ if (!(IODataRegisterFromScanner(ps,ps->RegStatus) & _FLAG_P98_PAPER)){
+ ps->GotoShadingPosition( ps );
+ }
+ } else if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ ps->OpenScanPath( ps );
+
+ if( !(IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER)) {
+
+ motorP98003PositionModuleToHome( ps );
+
+ MiscStartTimer( &timer, _SECOND * 20);
+ do {
+
+ if( IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER)
+ break;
+ } while( !MiscCheckTimer( &timer));
+ }
+ ps->CloseScanPath( ps );
+
+ } else {
+
+ if( ps->sCaps.Model >= MODEL_OP_9630P ) {
+ if( ps->sCaps.Model == MODEL_OP_A3I )
+ IOCmdRegisterToScanner( ps, ps->RegLineControl, 0x34 );
+ else
+ IOCmdRegisterToScanner( ps, ps->RegLineControl, 0x30 );
+ }
+
+ ps->bExtraMotorCtrl = 0;
+ ps->Scan.fMotorBackward = _FALSE;
+ MotorP96ConstantMoveProc( ps, 25 );
+
+ ps->Scan.fMotorBackward = _TRUE;
+ for(;;) {
+
+ motorP96GetScanStateAndStatus( ps, &StateStatus );
+
+ if ( StateStatus.bStatus & _FLAG_P96_PAPER ) {
+ break;
+ }
+
+ MotorP96ConstantMoveProc( ps, 50000 );
+ }
+
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->Asic96Reg.RD_MotorControl = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ IOSetToMotorRegister( ps );
+ _DODELAY(250);
+
+ ps->Asic96Reg.RD_LedControl = 0;
+ IOCmdRegisterToScanner(ps, ps->RegLedControl, ps->Asic96Reg.RD_LedControl);
+ }
+
+ DBG( DBG_HIGH, "- done !\n" );
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorP98GoFullStep( pScanData ps, ULong dwStep )
+{
+ memset( ps->pColorRunTable, 1, dwStep );
+ memset( ps->pColorRunTable + dwStep, 0xff, 0x40);
+
+ ps->bOldStateCount = IOGetScanState( ps, _FALSE ) & _SCANSTATE_MASK;
+ motorP98SetRunFullStep( ps );
+
+ ps->pScanState = ps->pColorRunTable;
+ ps->FillRunNewAdrPointer( ps );
+
+ while(!motorCheckMotorPresetLength( ps ))
+ motorP98FillRunNewAdrPointer1( ps );
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorP96SetSpeedToStopProc( pScanData ps )
+{
+ Byte bData;
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND);
+ while( !MiscCheckTimer( &timer )) {
+
+ bData = IODataRegisterFromScanner( ps, ps->RegFifoOffset );
+
+ if ((bData > ps->bMinReadFifo) && (bData != ps->bFifoCount))
+ break;
+ }
+ bData = IOGetScanState( ps, _FALSE );
+
+ if (!(bData & _SCANSTATE_STOP)) {
+
+ MiscStartTimer( &timer, (_SECOND / 2));
+
+ while (!MiscCheckTimer( &timer )) {
+
+ if (IOGetScanState( ps, _FALSE) != bData )
+ break;
+ }
+ }
+
+ ps->Scan.bModuleState = _MotorInStopState;
+ ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _FALSE );
+
+ IOSetToMotorRegister( ps );
+}
+
+/*.............................................................................
+ * Position Scan Module to specified line number (Forward or Backward & wait
+ * for paper flag ON)
+ */
+_LOC void MotorP96ConstantMoveProc( pScanData ps, ULong dwLines )
+{
+ Byte bRemainder, bLastState;
+ UShort wQuotient;
+ ULong dwDelayMaxTime;
+ ScanState StateStatus;
+ TimerDef timer;
+ Bool fTimeout = _FALSE;
+
+ wQuotient = (UShort)(dwLines / _NUMBER_OF_SCANSTEPS); /* state cycles */
+ bRemainder = (Byte)(dwLines % _NUMBER_OF_SCANSTEPS);
+
+ /* 3.3 ms per line */
+ dwDelayMaxTime = dwLines * _MOTOR_ONE_LINE_TIME + _SECOND * 2;
+
+ MotorSetConstantMove( ps, 1 ); /* step every time */
+
+ ps->OpenScanPath( ps );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+
+ if( ps->Scan.fMotorBackward ) {
+ ps->Asic96Reg.RD_MotorControl = (ps->MotorFreeRun | ps->MotorOn |
+ ps->FullStep | ps->bExtraMotorCtrl);
+ } else {
+ ps->Asic96Reg.RD_MotorControl = (ps->MotorFreeRun | ps->MotorOn |
+ _MotorDirForward | ps->bExtraMotorCtrl);
+ }
+
+ IODataToRegister( ps, ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl );
+ ps->CloseScanPath( ps );
+
+ bLastState = 0;
+
+ MiscStartTimer( &timer, dwDelayMaxTime );
+
+ do {
+
+ motorP96GetScanStateAndStatus( ps, &StateStatus );
+
+ if( ps->Scan.fMotorBackward && (StateStatus.bStatus&_FLAG_P96_PAPER)) {
+ break;
+ } else {
+
+ /*
+ * 1) Forward will not reach the sensor.
+ * 2) Backwarding, doesn't reach the sensor
+ */
+ if (wQuotient) {
+
+ /* stepped */
+ if (StateStatus.bStep != bLastState) {
+ bLastState = StateStatus.bStep;
+ if (!bLastState) /* done a cycle! */
+ wQuotient--;
+ }
+ } else {
+ if (StateStatus.bStep >= bRemainder) {
+ break;
+ }
+ }
+ }
+
+ fTimeout = MiscCheckTimer( &timer );
+
+ } while ( _OK == fTimeout );
+
+ if ( _OK == fTimeout ) {
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ IOSetToMotorRegister( ps );
+ }
+}
+
+/*.............................................................................
+ *
+ */
+_LOC Bool MotorP96AheadToDarkArea( pScanData ps )
+{
+ Byte bDark;
+ UShort wTL;
+ UShort wTotalLastLine;
+ TimerDef timer;
+
+ ps->fColorMoreRedFlag = _FALSE;
+ ps->fColorMoreBlueFlag = _FALSE;
+ ps->wOverBlue = 0;
+
+ /* FillToDarkCounter () */
+ memset( ps->a_nbNewAdrPointer, 0x30, _SCANSTATE_BYTES);
+ MotorSetConstantMove( ps, 2 );
+
+ /* SetToDarkRegister () */
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_ScanControl = ps->bLampOn | _SCAN_BYTEMODE;
+ ps->Asic96Reg.RD_MotorControl = _MotorDirForward | ps->FullStep;
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelWhiteIs0;
+
+ ps->AsicReg.RD_Dpi = 300;
+ wTL = 296;
+/* if( MODEL_OP_A3I == ps->sCaps.Model ) { */
+ if( ps->PhysicalDpi > 300 ) {
+ wTL = 400;
+ ps->AsicReg.RD_Origin = (UShort)(ps->Offset70 + 64 + 8 + 2048);
+ } else {
+ ps->AsicReg.RD_Origin = (UShort)(ps->Offset70 + 64 + 8 + 1024);
+ }
+ ps->AsicReg.RD_Pixels = 512;
+
+ IOPutOnAllRegisters( ps );
+
+ ps->Asic96Reg.RD_MotorControl = (ps->MotorFreeRun | ps->IgnorePF |
+ ps->MotorOn | _MotorDirForward );
+
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl,
+ ps->Asic96Reg.RD_MotorControl );
+
+ MiscStartTimer( &timer, _SECOND * 2 );
+ wTotalLastLine = 0;
+
+#ifdef _A3I_EN
+ while( !MiscCheckTimer( &timer )) {
+
+ bDark = motorP96ReadDarkData( ps );
+
+ wTotalLastLine++;
+ if((bDark < 0x80) || (wTotalLastLine==wTL)) {
+
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+ return _TRUE;
+ }
+ }
+#else
+ while (!MiscCheckTimer( &timer )) {
+
+ bDark = motorP96ReadDarkData( ps );
+
+ wTotalLastLine++;
+ if (((ps->sCaps.AsicID == _ASIC_IS_96001) && (bDark > 0x80)) ||
+ ((ps->sCaps.AsicID != _ASIC_IS_96001) && (bDark < 0x80)) ||
+ (wTotalLastLine==wTL)) {
+
+ IOCmdRegisterToScanner( ps, ps->RegModeControl, _ModeProgram );
+
+ if (wTotalLastLine <= 24)
+ ps->fColorMoreRedFlag = _TRUE;
+ else
+ if (wTotalLastLine >= 120) {
+ ps->wOverBlue = wTotalLastLine - 80;
+ ps->fColorMoreBlueFlag = _TRUE;
+ }
+
+ return _TRUE;
+ }
+ }
+#endif
+
+ return _FALSE; /* already timed out */
+}
+
+/*.............................................................................
+ * limit the speed settings for 96001/3 based models
+ */
+_LOC void MotorP96AdjustCurrentSpeed( pScanData ps, Byte bSpeed )
+{
+ if (bSpeed != 1) {
+
+ if (bSpeed > 34)
+ ps->bCurrentSpeed = 34;
+ else
+ ps->bCurrentSpeed = (bSpeed + 1) & 0xfe;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorP98003ForceToLeaveHomePos( pScanData ps )
+{
+ TimerDef timer;
+
+ IODataToRegister( ps, ps->RegStepControl, _MOTOR0_ONESTEP );
+ IODataToRegister( ps, ps->RegMotor0Control, _FORWARD_MOTOR );
+
+ MiscStartTimer( &timer, _SECOND );
+
+ do {
+ if( !(IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER))
+ break;
+
+ IORegisterToScanner( ps, ps->RegForceStep );
+ _DODELAY( 10 );
+
+ } while( _OK == MiscCheckTimer( &timer ));
+
+ IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorP98003BackToHomeSensor( pScanData ps )
+{
+ TimerDef timer;
+
+ DBG( DBG_HIGH, "MotorP98003BackToHomeSensor()\n" );
+
+ IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
+ IODataToRegister( ps, ps->RegModeControl, _ModeScan );
+
+ /* stepping every state */
+ memset( ps->a_nbNewAdrPointer, 0x88, _SCANSTATE_BYTES );
+ IODownloadScanStates( ps );
+
+ MiscStartTimer( &timer, _SECOND * 2 );
+
+ while(!(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP) &&
+ !MiscCheckTimer( &timer ));
+
+ _DODELAY( 1000UL );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+
+ if (!(ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
+ IODataToRegister( ps, ps->RegLineControl, _LOBYTE(ps->Shade.wExposure));
+ IODataToRegister( ps, ps->RegXStepTime, _LOBYTE(ps->Shade.wXStep));
+
+ } else {
+ IODataToRegister( ps, ps->RegLineControl, _DEFAULT_LINESCANTIME );
+ IODataToRegister( ps, ps->RegXStepTime, 6);
+ }
+
+ IODataToRegister( ps, ps->RegStepControl,
+ (_MOTOR_FREERUN | _MOTOR0_SCANSTATE));
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (_MotorHQuarterStep | _MotorOn | _MotorDirBackward |
+ _MotorPowerEnable | _MotorHHomeStop));
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+
+ MiscStartTimer( &timer, _SECOND * 5 );
+
+ do {
+ if( IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER )
+ break;
+
+ _DODELAY( 55 );
+
+ } while( !MiscCheckTimer( &timer ));
+
+ IODataToRegister( ps, ps->RegLineControl, ps->AsicReg.RD_LineControl);
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);
+
+ DBG( DBG_HIGH, "LineCtrl=%u, XStepTime=%u\n",
+ ps->AsicReg.RD_LineControl, ps->AsicReg.RD_XStepTime );
+
+ motorP98003DownloadNullScanStates( ps );
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorP98003ModuleForwardBackward( pScanData ps )
+{
+ switch( ps->Scan.bModuleState ) {
+
+ case _MotorInNormalState:
+ ps->Scan.bModuleState = _MotorGoBackward;
+ IODataToRegister( ps, ps->RegScanControl1,
+ (UChar)(ps->AsicReg.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
+ IODataToRegister( ps, ps->RegMotor0Control,
+ (UChar)(ps->AsicReg.RD_Motor0Control & ~_MotorDirForward));
+ motorP98003ModuleFreeRun( ps, _P98003_BACKSTEPS );
+ MiscStartTimer( &p98003MotorTimer, (15 * _MSECOND));
+ break;
+
+ case _MotorGoBackward:
+ if( MiscCheckTimer(& p98003MotorTimer)) {
+ if (!(IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING )) {
+ ps->Scan.bModuleState = _MotorInStopState;
+ MiscStartTimer( &p98003MotorTimer, (50 *_MSECOND));
+ }
+ }
+ break;
+
+ case _MotorInStopState:
+ if( MiscCheckTimer(&p98003MotorTimer)) {
+
+ if( IOReadFifoLength( ps ) < ps->Scan.dwMaxReadFifo ) {
+ ps->Scan.bModuleState = _MotorAdvancing;
+ IODataToRegister( ps, ps->RegScanControl1, ps->AsicReg.RD_ScanControl1);
+ IODataToRegister( ps, ps->RegMotor0Control, ps->AsicReg.RD_Motor0Control);
+ motorP98003ModuleFreeRun( ps, _P98003_FORWARDSTEPS );
+ MiscStartTimer( &p98003MotorTimer, (15 * _MSECOND));
+ }
+ }
+ break;
+
+ case _MotorAdvancing:
+ if( MiscCheckTimer(&p98003MotorTimer)) {
+ if( !(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP))
+ ps->Scan.bModuleState = _MotorInNormalState;
+ else {
+ if (!(IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING )) {
+ IORegisterToScanner( ps, ps->RegRefreshScanState );
+ ps->Scan.bModuleState = _MotorInNormalState;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+_LOC void MotorP98003PositionYProc( pScanData ps, ULong steps)
+{
+ TimerDef timer;
+
+ DBG( DBG_HIGH, "MotorP98003PositionYProc()\n" );
+
+ MiscStartTimer( &timer, _SECOND * 5 );
+
+ while(!(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP) &&
+ (!MiscCheckTimer( &timer )));
+
+ _DODELAY( 12 );
+
+ motorP98003ModuleFreeRun( ps, steps );
+
+ _DODELAY( 15 );
+
+ MiscStartTimer( &timer, _SECOND * 30 );
+
+ do {
+ if (!(IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING) ||
+ !(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP))
+ break;
+
+ } while( !MiscCheckTimer( &timer ));
+
+ DBG( DBG_HIGH, "MotorP98003PositionYProc() - done\n" );
+}
+
+/* END PLUSTEK-PP_MOTOR.C ...................................................*/
diff --git a/backend/plustek-pp_p12.c b/backend/plustek-pp_p12.c
new file mode 100644
index 0000000..6f46e58
--- /dev/null
+++ b/backend/plustek-pp_p12.c
@@ -0,0 +1,778 @@
+/* @file plustek-pp_p12.c
+ * @brief p12 and pt12 specific stuff
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 2000 Plustek Inc.
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.38 - initial version
+ * - 0.39 - added Genius Colorpage Vivid III V2 stuff
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - removed setting of ps->sCaps.dwFlag in p12InitiateComponentModel()
+ * - 0.43 - no changes
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some local vars *********************************/
+
+static RegDef p12CcdStop[] = {
+ {0x41, 0xff}, {0x42, 0xff}, {0x60, 0xff}, {0x61, 0xff},
+ {0x4b, 0xff}, {0x4c, 0xff}, {0x4d, 0xff}, {0x4e, 0xff},
+ {0x2a, 0x01}, {0x2b, 0x00}, {0x2d, 0x00}, {0x1b, 0x19}, {0x15, 0x00}
+};
+
+/*************************** local functions *********************************/
+
+/** init the stuff according to the buttons
+ */
+static void p12ButtonSetup( pScanData ps, Byte nrOfButtons )
+{
+ ps->Device.buttons = nrOfButtons;
+
+ ps->Device.Model1Mono &= ~_BUTTON_MODE;
+ ps->Device.Model1Color &= ~_BUTTON_MODE;
+
+ ps->AsicReg.RD_MotorDriverType |= _BUTTON_DISABLE;
+ ps->Scan.motorPower |= _BUTTON_DISABLE;
+}
+
+/** According to what we have detected, set the other stuff
+ */
+static void p12InitiateComponentModel( pScanData ps )
+{
+ /* preset some stuff and do the differences later */
+ ps->Device.buttons = 0;
+ ps->Device.Model1Mono = _BUTTON_MODE + _CCD_SHIFT_GATE + _SCAN_GRAYTYPE;
+ ps->Device.Model1Color = _BUTTON_MODE + _CCD_SHIFT_GATE;
+ ps->Device.dwModelOriginY = 64;
+ ps->Device.fTpa = _FALSE;
+ ps->Device.ModelCtrl = (_LED_ACTIVITY | _LED_CONTROL);
+
+ /* ps->sCaps.dwFlag should have been set correctly in models.c */
+
+ switch( ps->Device.bPCBID ) {
+
+ case _PLUSTEK_SCANNER:
+ DBG( DBG_LOW, "We have a Plustek Scanner\n" );
+ ps->sCaps.Model = MODEL_OP_P12;
+ break;
+
+ case _SCANNER_WITH_TPA:
+ DBG( DBG_LOW, "Scanner has TPA\n" );
+ ps->Device.fTpa = _TRUE;
+ ps->sCaps.dwFlag |= SFLAG_TPA;
+ break;
+
+ case _SCANNER4Button:
+ DBG( DBG_LOW, "Scanner has 4 Buttons\n" );
+ p12ButtonSetup( ps, 4 );
+ break;
+
+ case _SCANNER4ButtonTPA:
+ DBG( DBG_LOW, "Scanner has 4 Buttons & TPA\n" );
+ ps->Device.fTpa = _TRUE;
+ ps->sCaps.dwFlag |= SFLAG_TPA;
+ p12ButtonSetup( ps, 4 );
+ break;
+
+ case _SCANNER5Button:
+ DBG( DBG_LOW, "Scanner has 5 Buttons\n" );
+ ps->Device.dwModelOriginY = 64 + 20;
+ p12ButtonSetup( ps, 5 );
+ break;
+
+ case _SCANNER5ButtonTPA:
+ DBG( DBG_LOW, "Scanner has 5 Buttons & TPA\n" );
+ ps->Device.dwModelOriginY = 64 + 20;
+ ps->Device.fTpa = _TRUE;
+ ps->sCaps.dwFlag |= SFLAG_TPA;
+ p12ButtonSetup( ps, 5 );
+ break;
+
+ case _SCANNER1Button:
+ DBG( DBG_LOW, "Scanner has 1 Button\n" );
+ p12ButtonSetup( ps, 1 );
+ break;
+
+ case _SCANNER1ButtonTPA:
+ DBG( DBG_LOW, "Scanner has 1 Button & TPA\n" );
+ ps-> Device.fTpa = _TRUE;
+ ps->sCaps.dwFlag |= SFLAG_TPA;
+ p12ButtonSetup( ps, 1 );
+ break;
+
+ case _AGFA_SCANNER:
+ DBG( DBG_LOW, "Agfa Scanner\n" );
+ ps->Device.dwModelOriginY = 24; /* 1200 dpi */
+ break;
+
+ case _SCANNER2Button:
+ DBG( DBG_LOW, "Scanner has 2 Buttons\n" );
+ DBG( DBG_LOW, "Seems we have a Genius Colorpage Vivid III V2\n" );
+ ps->Device.dwModelOriginY = 64 - 33;
+ p12ButtonSetup( ps, 2 );
+ ps->sCaps.Model = MODEL_GEN_CPV2;
+ break;
+
+ default:
+ DBG( DBG_LOW, "Default Model: P12\n" );
+ ps->sCaps.Model = MODEL_OP_P12;
+ break;
+ }
+
+ if( _MOTOR0_2003 == ps->Device.bMotorID ) {
+ ps->Device.f2003 = _TRUE;
+ ps->Device.XStepMono = 10;
+ ps->Device.XStepColor = 6;
+ ps->Device.XStepBack = 5;
+ ps->AsicReg.RD_MotorDriverType |= _MOTORR_STRONG;
+ } else {
+ ps->Device.f2003 = _FALSE;
+ ps->Device.XStepMono = 8;
+ ps->Device.XStepColor = 4;
+ ps->Device.XStepBack = 5;
+ ps->AsicReg.RD_MotorDriverType |= _MOTORR_WEAK;
+ }
+}
+
+/*.............................................................................
+ * prepare all the necessary variables -
+ */
+static void p12SetupScannerVariables( pScanData ps )
+{
+ DBG( DBG_LOW, "p12SetupScannerVariables()\n" );
+
+ /*
+ * these values were originally altered by registry entries (NT-driver)
+ * and used to adjust the picture position...
+ */
+ ps->Device.lUpNormal = 0;
+ ps->Device.lUpNegative = 20;
+ ps->Device.lUpPositive = -30;
+
+ ps->Device.lLeftNormal = 51;
+
+ ps->OpenScanPath( ps );
+ ps->ReInitAsic( ps, _FALSE );
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ *
+ */
+static void p12SetupScanningCondition( pScanData ps )
+{
+ TimerDef timer;
+ ULong channel;
+ Byte bState;
+ pUChar pState = ps->Bufs.b1.pReadBuf;
+
+ DBG( DBG_LOW, "p12SetupScanningCondition()\n" );
+
+ P12SetGeneralRegister( ps );
+
+ IORegisterToScanner( ps, ps->RegResetMTSC );
+
+ /* ------- Setup MinRead/MaxRead Fifo size ------- */
+ if( ps->DataInf.wPhyDataType <= COLOR_TRUE24 ) {
+ ps->Scan.dwMaxReadFifo =
+ ps->Scan.dwMinReadFifo = ps->DataInf.dwAsicBytesPerPlane * 2;
+ } else {
+ ps->Scan.dwMaxReadFifo =
+ ps->Scan.dwMinReadFifo = ps->DataInf.dwAppPixelsPerLine << 1;
+ }
+
+ if( ps->Scan.dwMinReadFifo < 1024)
+ ps->Scan.dwMinReadFifo = ps->Scan.dwMaxReadFifo = 1024;
+
+ ps->Scan.dwMaxReadFifo += (ps->DataInf.dwAsicBytesPerPlane / 2);
+
+
+ DBG( DBG_LOW, "MinReadFifo=%u, MaxReadFifo=%u\n",
+ ps->Scan.dwMinReadFifo, ps->Scan.dwMaxReadFifo );
+
+ /* ------- Set the max. read fifo to asic ------- */
+ if( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ ps->Scan.bFifoSelect = ps->RegBFifoOffset;
+
+ if( !ps->Scan.p48BitBuf.pb ) {
+
+ Long lRed, lGreen;
+
+ lRed = (_SIZE_REDFIFO - _SIZE_BLUEFIFO) /
+ ps->DataInf.dwAsicBytesPerPlane - ps->Scan.bd_rk.wRedKeep;
+
+ lGreen = (_SIZE_GREENFIFO - _SIZE_BLUEFIFO) /
+ ps->DataInf.dwAsicBytesPerPlane - ps->Scan.gd_gk.wGreenKeep;
+
+ if((lRed < 0) || (lGreen < 0)) {
+
+ if( lRed < lGreen ) {
+ channel = _RED_FULLSIZE << 16;
+ ps->AsicReg.RD_BufFullSize = _SIZE_REDFIFO;
+ lGreen = lRed;
+ } else {
+ channel = _GREEN_FULLSIZE << 16;
+ ps->AsicReg.RD_BufFullSize = _SIZE_GREENFIFO;
+ }
+
+ lGreen = (ULong)(-lGreen * ps->DataInf.dwAsicBytesPerPlane);
+
+ if( ps->DataInf.wPhyDataType > COLOR_TRUE24 )
+ lGreen >>= 1;
+
+ ps->Scan.dwMinReadFifo += (ULong)lGreen;
+ ps->Scan.dwMaxReadFifo += (ULong)lGreen;
+
+ } else {
+ channel = _BLUE_FULLSIZE << 16;
+ ps->AsicReg.RD_BufFullSize = _SIZE_BLUEFIFO;
+ }
+ } else {
+ channel = _BLUE_FULLSIZE << 16;
+ ps->AsicReg.RD_BufFullSize = _SIZE_BLUEFIFO;
+ }
+ } else {
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+ channel = _GREEN_FULLSIZE << 16;
+ ps->AsicReg.RD_BufFullSize = _SIZE_GRAYFIFO;
+ }
+
+ ps->AsicReg.RD_BufFullSize -= (ps->DataInf.dwAsicBytesPerPlane << 1);
+
+ if( ps->DataInf.wPhyDataType > COLOR_TRUE24 )
+ ps->AsicReg.RD_BufFullSize >>= 1;
+
+ ps->AsicReg.RD_BufFullSize |= channel;
+
+ ps->Scan.bRefresh = (Byte)(ps->Scan.dwInterval << 1);
+ ps->AsicReg.RD_LineControl = (_LOBYTE (ps->Shade.wExposure));
+ ps->AsicReg.RD_ExtLineControl = (_HIBYTE (ps->Shade.wExposure));
+ ps->AsicReg.RD_XStepTime = (_LOBYTE (ps->Shade.wXStep));
+ ps->AsicReg.RD_ExtXStepTime = (_HIBYTE (ps->Shade.wXStep));
+ ps->AsicReg.RD_Motor0Control = _FORWARD_MOTOR;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_ModeControl = (_ModeScan | _ModeFifoGSel);
+
+ DBG( DBG_LOW, "bRefresh = %i\n", ps->Scan.bRefresh );
+
+ if( ps->DataInf.wPhyDataType == COLOR_BW ) {
+ ps->AsicReg.RD_ScanControl = _SCAN_BITMODE;
+
+ if( !(ps->DataInf.dwScanFlag & SCANDEF_Inverse))
+ ps->AsicReg.RD_ScanControl |= _P98_SCANDATA_INVERT;
+
+ } else if( ps->DataInf.wPhyDataType <= COLOR_TRUE24 )
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+ else {
+ ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE;
+
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_RightAlign))
+ ps->AsicReg.RD_ScanControl |= _BITALIGN_LEFT;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Inverse)
+ ps->AsicReg.RD_ScanControl |= _P98_SCANDATA_INVERT;
+ }
+
+ ps->AsicReg.RD_ScanControl |= _SCAN_1ST_AVERAGE;
+ IOSelectLampSource( ps );
+
+ DBG( DBG_LOW, "RD_ScanControl = 0x%02x\n", ps->AsicReg.RD_ScanControl );
+
+ ps->AsicReg.RD_MotorTotalSteps = (ULong)ps->DataInf.crImage.cy * 4 +
+ ((ps->Device.f0_8_16) ? 32 : 16) +
+ ((ps->Scan.bDiscardAll) ? 32 : 0);
+
+ ps->AsicReg.RD_ScanControl1 = (_MTSC_ENABLE | _SCANSTOPONBUFFULL |
+ _MFRC_RUNSCANSTATE | _MFRC_BY_XSTEP);
+
+ ps->AsicReg.RD_Dpi = ps->DataInf.xyPhyDpi.x;
+
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA )) {
+
+ ps->AsicReg.RD_Origin = (UShort)(ps->Device.lLeftNormal * 2 +
+ ps->Device.DataOriginX +
+ ps->DataInf.crImage.x );
+
+ } else if( ps->DataInf.dwScanFlag & SCANDEF_Transparency ) {
+ ps->AsicReg.RD_Origin =
+ (UShort)(ps->Scan.posBegin + ps->DataInf.crImage.x);
+ } else {
+ ps->AsicReg.RD_Origin =
+ (UShort)(ps->Scan.negBegin + ps->DataInf.crImage.x);
+ }
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ ps->AsicReg.RD_Origin >>= 1;
+
+ if( ps->DataInf.wPhyDataType == COLOR_BW )
+ ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAsicBytesPerPlane;
+ else
+ ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAppPixelsPerLine;
+
+ DBG( DBG_LOW, "RD_Origin = %u, RD_Pixels = %u\n",
+ ps->AsicReg.RD_Origin, ps->AsicReg.RD_Pixels );
+
+ /* ------- Prepare scan states ------- */
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ memset( ps->Bufs.b1.pReadBuf, 0, _NUMBER_OF_SCANSTEPS );
+
+ if( ps->DataInf.wPhyDataType <= COLOR_256GRAY )
+ bState = (_SS_MONO | _SS_STEP);
+ else
+ bState = (_SS_COLOR | _SS_STEP);
+
+ for( channel = _NUMBER_OF_SCANSTEPS;
+ channel; channel -= ps->Scan.dwInterval ) {
+ *pState = bState;
+ if( ps->Scan.dwInterlace )
+ pState[ ps->Scan.dwInterlace] = _SS_STEP;
+ pState += ps->Scan.dwInterval;
+ }
+ for( channel = 0, pState = ps->Bufs.b1.pReadBuf;
+ channel < _SCANSTATE_BYTES; channel++) {
+ ps->a_nbNewAdrPointer[channel] = pState [0] | (pState [1] << 4);
+ pState += 2;
+ }
+
+ /* ------- Wait for scan state stop ------- */
+ MiscStartTimer( &timer, _SECOND * 2 );
+
+ while(!(IOGetScanState( ps, _FALSE ) & _SCANSTATE_STOP) &&
+ !MiscCheckTimer(&timer));
+
+/* CHECK: Replace by IOPutAll.... */
+ IODownloadScanStates( ps );
+ IODataToRegister( ps, ps->RegLineControl, ps->AsicReg.RD_LineControl);
+ IODataToRegister( ps, ps->RegExtendedLineControl,
+ ps->AsicReg.RD_ExtLineControl);
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);
+ IODataToRegister( ps, ps->RegExtendedXStep, ps->AsicReg.RD_ExtXStepTime);
+ IODataToRegister( ps, ps->RegMotorDriverType,
+ ps->AsicReg.RD_MotorDriverType);
+ IODataToRegister( ps, ps->RegStepControl, ps->AsicReg.RD_StepControl);
+ IODataToRegister( ps, ps->RegMotor0Control, ps->AsicReg.RD_Motor0Control);
+ IODataToRegister( ps, ps->RegModelControl,ps->AsicReg.RD_ModelControl);
+ IODataToRegister( ps, ps->RegDpiLow, (_LOBYTE(ps->AsicReg.RD_Dpi)));
+ IODataToRegister( ps, ps->RegDpiHigh, (_HIBYTE(ps->AsicReg.RD_Dpi)));
+ IODataToRegister( ps, ps->RegScanPosLow, (_LOBYTE(ps->AsicReg.RD_Origin)));
+ IODataToRegister( ps, ps->RegScanPosHigh,(_HIBYTE(ps->AsicReg.RD_Origin)));
+ IODataToRegister( ps, ps->RegWidthPixelsLow,
+ (_LOBYTE(ps->AsicReg.RD_Pixels)));
+ IODataToRegister( ps, ps->RegWidthPixelsHigh,
+ (_HIBYTE(ps->AsicReg.RD_Pixels)));
+ IODataToRegister( ps, ps->RegThresholdLow,
+ (_LOBYTE(ps->AsicReg.RD_ThresholdControl)));
+ IODataToRegister( ps, ps->RegThresholdHigh,
+ (_HIBYTE(ps->AsicReg.RD_ThresholdControl)));
+ IODataToRegister( ps, ps->RegMotorTotalStep0,
+ (_LOBYTE(ps->AsicReg.RD_MotorTotalSteps)));
+ IODataToRegister( ps, ps->RegMotorTotalStep1,
+ (_HIBYTE(ps->AsicReg.RD_MotorTotalSteps)));
+ IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+
+ IORegisterToScanner( ps, ps->RegInitDataFifo);
+}
+
+/*.............................................................................
+ * program the CCD relevant stuff
+ */
+static void p12ProgramCCD( pScanData ps)
+{
+ UShort w;
+ pRegDef rp;
+
+ DBG( DBG_IO, "p12ProgramCCD: 0x%08lx[%lu]\n",
+ (unsigned long)ps->Device.pCCDRegisters,
+ ((unsigned long)ps->Device.wNumCCDRegs * ps->Shade.bIntermediate));
+
+ DBG( DBG_IO, " %u regs * %u (intermediate)\n",
+ ps->Device.wNumCCDRegs, ps->Shade.bIntermediate );
+
+ rp = ps->Device.pCCDRegisters +
+ (ULong)ps->Device.wNumCCDRegs * ps->Shade.bIntermediate;
+
+ for( w = ps->Device.wNumCCDRegs; w--; rp++ ) {
+
+ DBG( DBG_IO, "[0x%02x] = 0x%02x\n", rp->bReg, rp->bParam );
+ IODataToRegister( ps, rp->bReg, rp->bParam );
+ }
+}
+
+/*.............................................................................
+ * this initializes the ASIC and prepares the different functions for shading
+ * and scanning
+ */
+static void p12Init98003( pScanData ps, Bool shading )
+{
+ DBG( DBG_LOW, "p12InitP98003(%d)\n", shading );
+
+ /* get DAC and motor stuff */
+ ps->Device.bDACType = IODataFromRegister( ps, ps->RegResetConfig );
+ ps->Device.bMotorID = (Byte)(ps->Device.bDACType & _MOTOR0_MASK);
+
+ ps->AsicReg.RD_MotorDriverType =
+ (Byte)((ps->Device.bDACType & _MOTOR0_MASK) >> 3);
+ ps->AsicReg.RD_MotorDriverType |=
+ (Byte)((ps->Device.bDACType & _MOTOR1_MASK) >> 1);
+
+
+ ps->Scan.motorPower = ps->AsicReg.RD_MotorDriverType | _MOTORR_STRONG;
+
+ ps->Device.bDACType &= _ADC_MASK;
+
+ /*get CCD and PCB ID */
+ ps->Device.bPCBID = IODataFromRegister( ps, ps->RegConfiguration );
+ ps->Device.bCCDID = ps->Device.bPCBID & 0x07;
+ ps->Device.bPCBID &= 0xf0;
+
+ if( _AGFA_SCANNER == ps->Device.bPCBID )
+ ps->Device.bDACType = _DA_WOLFSON8141;
+
+ DBG( DBG_LOW, "PCB-ID=0x%02x, CCD-ID=0x%02x, DAC-TYPE=0x%02x\n",
+ ps->Device.bPCBID, ps->Device.bCCDID, ps->Device.bDACType );
+
+ p12InitiateComponentModel( ps );
+
+ /* encode the CCD-id into the flag parameter */
+ ps->sCaps.dwFlag |= ((ULong)(ps->Device.bCCDID | ps->Device.bPCBID) << 16);
+
+ P12InitCCDandDAC( ps, shading );
+
+ if( ps->Shade.bIntermediate & _ScanMode_Mono )
+ ps->AsicReg.RD_Model1Control = ps->Device.Model1Mono;
+ else
+ ps->AsicReg.RD_Model1Control = ps->Device.Model1Color;
+
+ IODataToRegister( ps, ps->RegPllPredivider, 1 );
+ IODataToRegister( ps, ps->RegPllMaindivider, 0x20 );
+ IODataToRegister( ps, ps->RegPllPostdivider, 2 );
+ IODataToRegister( ps, ps->RegClockSelector, 3 ); /* 2 */
+ IODataToRegister( ps, ps->RegMotorDriverType,
+ ps->AsicReg.RD_MotorDriverType );
+
+ /* this might be changed, def value is 11 */
+ IODataToRegister( ps, ps->RegWaitStateInsert, 11 );
+ IODataToRegister( ps, ps->RegModel1Control, ps->AsicReg.RD_Model1Control );
+
+ p12ProgramCCD( ps );
+}
+
+/*.............................................................................
+ * initialize the register values for the 98003 asic and preset other stuff
+ */
+static void p12InitializeAsicRegister( pScanData ps )
+{
+ memset( &ps->AsicReg, 0, sizeof(RegData));
+}
+
+/*.............................................................................
+ * as the function name says
+ */
+static void p12PutToIdleMode( pScanData ps )
+{
+ ULong i;
+
+ ps->OpenScanPath( ps );
+
+ DBG( DBG_IO, "CCD-Stop\n" );
+
+ for( i = 0; i < 13; i++ ) {
+
+ DBG( DBG_IO, "[0x%02x] = 0x%02x\n",
+ p12CcdStop[i].bReg, p12CcdStop[i].bParam );
+
+ IODataToRegister( ps, p12CcdStop[i].bReg, p12CcdStop[i].bParam );
+ }
+
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ * here we simply call the WaitForShading function which performs this topic
+ */
+static int p12Calibration( pScanData ps )
+{
+ Bool result;
+
+ DBG( DBG_LOW, "p12Calibration()\n" );
+
+ /*
+ * wait for shading to be done
+ */
+ ps->OpenScanPath( ps );
+
+ _ASSERT(ps->WaitForShading);
+ result = ps->WaitForShading( ps );
+ ps->CloseScanPath( ps );
+
+ if( !result )
+ return _E_TIMEOUT;
+
+ return _OK;
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * initialize the register values and function calls for the 98003 asic
+ */
+_LOC int P12InitAsic( pScanData ps )
+{
+ int result;
+
+ DBG( DBG_LOW, "P12InitAsic()\n" );
+
+ /*
+ * preset the asic shadow registers
+ */
+ p12InitializeAsicRegister( ps );
+
+ ps->IO.bOpenCount = 0;
+
+ /*
+ * setup the register values
+ */
+ ps->RegSwitchBus = 0;
+ ps->RegEPPEnable = 1;
+ ps->RegECPEnable = 2;
+ ps->RegReadDataMode = 3;
+ ps->RegWriteDataMode = 4;
+ ps->RegInitDataFifo = 5;
+ ps->RegForceStep = 6;
+ ps->RegInitScanState = 7;
+ ps->RegRefreshScanState = 8;
+ ps->RegWaitStateInsert = 0x0a;
+ ps->RegRFifoOffset = 0x0a;
+ ps->RegGFifoOffset = 0x0b;
+ ps->RegBFifoOffset = 0x0c;
+ ps->RegBitDepth = 0x13;
+ ps->RegStepControl = 0x14;
+ ps->RegMotor0Control = 0x15;
+ ps->RegXStepTime = 0x16;
+ ps->RegGetScanState = 0x17;
+ ps->RegAsicID = 0x18;
+ ps->RegMemoryLow = 0x19;
+ ps->RegMemoryHigh = 0x1a;
+ ps->RegModeControl = 0x1b;
+ ps->RegLineControl = 0x1c;
+ ps->RegScanControl = 0x1d;
+ ps->RegConfiguration = 0x1e;
+ ps->RegModelControl = 0x1f;
+ ps->RegModel1Control = 0x20;
+ ps->RegDpiLow = 0x21;
+ ps->RegDpiHigh = 0x22;
+ ps->RegScanPosLow = 0x23;
+ ps->RegScanPosHigh = 0x24;
+ ps->RegWidthPixelsLow = 0x25;
+ ps->RegWidthPixelsHigh = 0x26;
+ ps->RegThresholdLow = 0x27;
+ ps->RegThresholdHigh = 0x28;
+ ps->RegThresholdGapControl = 0x29;
+ ps->RegADCAddress = 0x2a;
+ ps->RegADCData = 0x2b;
+ ps->RegADCPixelOffset = 0x2c;
+ ps->RegADCSerialOutStr = 0x2d;
+ ps->RegResetConfig = 0x2e;
+ ps->RegLensPosition = 0x2f;
+ ps->RegStatus = 0x30;
+ ps->RegScanStateControl = 0x31;
+ ps->RegRedChDarkOffsetLow = 0x33;
+ ps->RegRedChDarkOffsetHigh = 0x34;
+ ps->RegGreenChDarkOffsetLow = 0x35;
+ ps->RegGreenChDarkOffsetHigh= 0x36;
+ ps->RegBlueChDarkOffsetLow = 0x37;
+ ps->RegBlueChDarkOffsetHigh = 0x38;
+ ps->RegResetPulse0 = 0x39;
+ ps->RegResetPulse1 = 0x3a;
+ ps->RegCCDClampTiming0 = 0x3b;
+ ps->RegCCDClampTiming1 = 0x3c;
+ ps->RegVSMPTiming0 = 0x41;
+ ps->RegVSMPTiming1 = 0x42;
+ ps->RegCCDQ1Timing0 = 0x43;
+ ps->RegCCDQ1Timing1 = 0x44;
+ ps->RegCCDQ1Timing2 = 0x45;
+ ps->RegCCDQ1Timing3 = 0x46;
+ ps->RegCCDQ2Timing0 = 0x47;
+ ps->RegCCDQ2Timing1 = 0x48;
+ ps->RegCCDQ2Timing2 = 0x49;
+ ps->RegCCDQ2Timing3 = 0x4a;
+ ps->RegADCclockTiming0 = 0x4b;
+ ps->RegADCclockTiming1 = 0x4c;
+ ps->RegADCclockTiming2 = 0x4d;
+ ps->RegADCclockTiming3 = 0x4e;
+ ps->RegADCDVTiming0 = 0x50;
+ ps->RegADCDVTiming1 = 0x51;
+ ps->RegADCDVTiming2 = 0x52;
+ ps->RegADCDVTiming3 = 0x53;
+
+ ps->RegFifoFullLength0 = 0x54;
+ ps->RegFifoFullLength1 = 0x55;
+ ps->RegFifoFullLength2 = 0x56;
+
+ ps->RegMotorTotalStep0 = 0x57;
+ ps->RegMotorTotalStep1 = 0x58;
+ ps->RegMotorFreeRunCount0 = 0x59;
+ ps->RegMotorFreeRunCount1 = 0x5a;
+ ps->RegScanControl1 = 0x5b;
+ ps->RegMotorFreeRunTrigger = 0x5c;
+
+ ps->RegResetMTSC = 0x5d;
+
+ ps->RegMotor1Control = 0x62;
+ ps->RegMotor2Control = 0x63;
+ ps->RegMotorDriverType = 0x64;
+
+ ps->RegStatus2 = 0x66;
+
+ ps->RegExtendedLineControl = 0x6d;
+ ps->RegExtendedXStep = 0x6e;
+
+ ps->RegPllPredivider = 0x71;
+ ps->RegPllMaindivider = 0x72;
+ ps->RegPllPostdivider = 0x73;
+ ps->RegClockSelector = 0x74;
+ ps->RegTestMode = 0xf0;
+
+ /*
+ * setup function calls
+ */
+ ps->SetupScannerVariables = p12SetupScannerVariables;
+ ps->SetupScanningCondition = p12SetupScanningCondition;
+ ps->Calibration = p12Calibration;
+ ps->PutToIdleMode = p12PutToIdleMode;
+ ps->ReInitAsic = p12Init98003;
+
+ ps->CtrlReadHighNibble = _CTRL_GENSIGNAL + _CTRL_AUTOLF + _CTRL_STROBE;
+ ps->CtrlReadLowNibble = _CTRL_GENSIGNAL + _CTRL_AUTOLF;
+
+ ps->IO.useEPPCmdMode = _FALSE;
+
+ /*
+ * initialize the other modules and set some
+ * function pointer
+ */
+ result = DacInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = ImageInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = IOFuncInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = IOInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = MotorInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ if( _FALSE == ps->OpenScanPath( ps )) {
+ DBG( DBG_LOW, "P12InitAsic() failed.\n" );
+ return _E_NO_DEV;
+ }
+
+ /*get CCD and PCB ID */
+ ps->Device.bPCBID = IODataFromRegister( ps, ps->RegConfiguration );
+ ps->Device.bCCDID = ps->Device.bPCBID & 0x07;
+ ps->Device.bPCBID &= 0xf0;
+
+ DBG( DBG_LOW, "PCB-ID=0x%02x, CCD-ID=0x%02x\n", ps->Device.bPCBID, ps->Device.bCCDID );
+
+ /* get a more closer model description...*/
+ p12InitiateComponentModel( ps );
+
+ ps->CloseScanPath( ps );
+
+ /* here we check for the OpticWorks 2000, which is not supported */
+ if( _OPTICWORKS2000 == ps->Device.bPCBID ) {
+ DBG( DBG_LOW, "OpticWorks 2000 not supported!\n" );
+ return _E_NOSUPP;
+ }
+
+ DBG( DBG_LOW, "P12InitAsic() done.\n" );
+ return _OK;
+}
+
+/*.............................................................................
+ * set all necessary register contents
+ */
+_LOC void P12SetGeneralRegister( pScanData ps )
+{
+ DBG( DBG_LOW, "P12SetGeneralRegister()\n" );
+
+ ps->Scan.fMotorBackward = _FALSE;
+ ps->Scan.fRefreshState = _FALSE;
+
+ if( COLOR_BW == ps->DataInf.wPhyDataType )
+ ps->AsicReg.RD_ScanControl = _SCAN_BITMODE;
+ else {
+ if( ps->DataInf.wPhyDataType <= COLOR_TRUE24 )
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+ else
+ ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE;
+ }
+
+ IOSelectLampSource( ps );
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelDpi300;
+ else
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelDpi600;
+
+ ps->AsicReg.RD_Motor0Control = _MotorOn | _MotorHQuarterStep | _MotorPowerEnable;
+ ps->AsicReg.RD_ScanControl1 = _SCANSTOPONBUFFULL | _MFRC_BY_XSTEP;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+}
+
+/* END PLUSTEK-PP_P12.C .....................................................*/
diff --git a/backend/plustek-pp_p12ccd.c b/backend/plustek-pp_p12ccd.c
new file mode 100644
index 0000000..178cb2e
--- /dev/null
+++ b/backend/plustek-pp_p12ccd.c
@@ -0,0 +1,1149 @@
+/* @file plustek-pp_p12ccd.c
+ * @brief here we have the whole code to intialize the CCD and DAC stuff
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 2000 Plustek Inc.
+ * Copyright (C) 2001-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.38 - initial version
+ * - 0.39 - using now fnDarkOffsetWolfson3797 instead of fnDarkOffsetWolfson3799
+ * This should provide a better picture quality on the GENIUS Colorpage
+ * Vivid V2
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some definitions ********************************/
+
+#define _REFLECTION 0
+#define _TRANSPARENCY 1
+#define _NEGATIVE 2
+
+#define _NUM_OF_CCDREGS_W8143 25
+#define _NUM_OF_DACREGS_W8143 10
+
+#define _NUM_OF_CCDREGS_S1224 29
+#define _NUM_OF_DACREGS_S1224 7
+
+#define _NUM_OF_CCDREGS_S8531 29
+#define _NUM_OF_DACREGS_S8531 9
+
+#define _NUM_OF_CCDREGS_Max _NUM_OF_CCDREGS_S1224
+#define _NUM_OF_DACREGS_Max _NUM_OF_DACREGS_W8143
+
+
+/*************************** some local vars *********************************/
+
+static pFnVoid p12ccdInitFunc = NULL;
+
+static RegDef W3797CCDParams[4][_NUM_OF_CCDREGS_W8143] = {
+ {
+ {0x2c, 0x02}, {0x39, 0x2a}, {0x3a, 0x0a}, {0x3b, 0x37},
+ {0x3c, 0x16}, {0x41, 0x0e}, {0x42, 0x90}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x03},
+ {0x48, 0x27}, {0x49, 0x2f}, {0x4a, 0x09}, {0x4b, 0x03},
+ {0x4c, 0x07}, {0x4d, 0x06}, {0x4e, 0x06}, {0x67, 0x00},
+ {0x50, 0x08}, {0x51, 0x0e}, {0x52, 0x0c}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x3d}, {0x3a, 0x04}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x1f}, {0x42, 0x8c}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x00},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x01}, {0x51, 0x06}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x04}, {0x42, 0x1e}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xf9}, {0x4b, 0x04},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x03}, {0x67, 0x00},
+ {0x50, 0x06}, {0x51, 0x03}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x18}, {0x3a, 0x04}, {0x3b, 0x1d},
+ {0x3c, 0x03}, {0x41, 0x0c}, {0x42, 0x84}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x04},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x00}, {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03},
+ {0xf0, 0x00}
+ }
+};
+
+static RegDef W3799CCDParams[4][_NUM_OF_CCDREGS_W8143] = {
+ {
+ {0x2c, 0x02}, {0x39, 0x2c}, {0x3a, 0x05}, {0x3b, 0x3c},
+ {0x3c, 0x0e}, {0x41, 0x0e}, {0x42, 0x90}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x02},
+ {0x48, 0x27}, {0x49, 0x2f}, {0x4a, 0x09}, {0x4b, 0x05},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x06}, {0x67, 0x00},
+ {0x50, 0x08}, {0x51, 0x0d}, {0x52, 0x0c}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x3d}, {0x3a, 0x04}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x1f}, {0x42, 0x8c}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x00},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x01}, {0x51, 0x06}, {0x52, 0x12}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x02}, {0x3b, 0x1a},
+ {0x3c, 0x05}, {0x41, 0x04}, {0x42, 0x1e}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xf9}, {0x4b, 0x04},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x03}, {0x67, 0x00},
+ {0x50, 0x06}, {0x51, 0x03}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x18}, {0x3a, 0x04}, {0x3b, 0x1d},
+ {0x3c, 0x03}, {0x41, 0x0c}, {0x42, 0x84}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x03},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x00}, {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03},
+ {0xf0, 0x00}
+ }
+};
+
+/* Genius ColorPage Vivid III */
+static RegDef W548CCDParams[4][_NUM_OF_CCDREGS_W8143] = {
+ {
+ {0x2c, 0x02}, {0x39, 0x2c}, {0x3a, 0x05}, {0x3b, 0x3c},
+ {0x3c, 0x0e}, {0x41, 0x0e}, {0x42, 0x90}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x02},
+ {0x48, 0x27}, {0x49, 0x2f}, {0x4a, 0x09}, {0x4b, 0x05},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x06}, {0x67, 0x00},
+ {0x50, 0x08}, {0x51, 0x0d}, {0x52, 0x0c}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x3d}, {0x3a, 0x04}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x1f}, {0x42, 0x8c}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x00},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x01}, {0x51, 0x06}, {0x52, 0x12}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x02}, {0x3b, 0x1a},
+ {0x3c, 0x05}, {0x41, 0x04}, {0x42, 0x1e}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xf9}, {0x4b, 0x04},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x03}, {0x67, 0x00},
+ {0x50, 0x06}, {0x51, 0x03}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x18}, {0x3a, 0x04}, {0x3b, 0x1d},
+ {0x3c, 0x03}, {0x41, 0x0c}, {0x42, 0x84}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x03},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x00}, {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03},
+ {0xf0, 0x00}
+ }
+};
+
+static RegDef S3797CCDParams[4][_NUM_OF_CCDREGS_S1224] = {
+ {
+ {0x2c, 0x00}, {0x39, 0x2a}, {0x3a, 0x0a}, {0x3b, 0x37},
+ {0x3c, 0x16}, {0x41, 0x2c}, {0x42, 0x9f}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x03},
+ {0x48, 0x27}, {0x49, 0x27}, {0x4a, 0x09}, {0x4b, 0x0b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x13},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x20}, {0x70, 0x06}, {0x60, 0x07}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x3d}, {0x3a, 0x06}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x3d}, {0x42, 0x92}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x1b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x23},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x30}, {0x70, 0x06}, {0x60, 0x17}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x09},
+ {0x4c, 0x13}, {0x4d, 0x14}, {0x4e, 0x09}, {0x50, 0x09},
+ {0x51, 0x14}, {0x52, 0x13}, {0x53, 0x01}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x09},
+ {0x4c, 0x13}, {0x4d, 0x14}, {0x4e, 0x09}, {0x50, 0x09},
+ {0x51, 0x14}, {0x52, 0x13}, {0x53, 0x01}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }
+};
+
+static RegDef S3799CCDParams[4][_NUM_OF_CCDREGS_S1224] = {
+ {
+ {0x2c, 0x00}, {0x39, 0x2a}, {0x3a, 0x0a}, {0x3b, 0x37},
+ {0x3c, 0x16}, {0x41, 0x2c}, {0x42, 0x8f}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x01},
+ {0x48, 0x27}, {0x49, 0x27}, {0x4a, 0x09}, {0x4b, 0x0b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x13},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x20}, {0x70, 0x06}, {0x60, 0x07}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x3d}, {0x3a, 0x06}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x3d}, {0x42, 0x92}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x1b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x23},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x30}, {0x70, 0x06}, {0x60, 0x17}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x09},
+ {0x4c, 0x13}, {0x4d, 0x14}, {0x4e, 0x09}, {0x50, 0x09},
+ {0x51, 0x14}, {0x52, 0x13}, {0x53, 0x01}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x03},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x50, 0x00},
+ {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }
+};
+
+static RegDef WolfsonDAC8143[_NUM_OF_DACREGS_W8143] = {
+ {0x01, 0x01}, {0x02, 0x04}, {0x03, 0x42}, {0x05, 0x10}, {0x20, 0xd0},
+ {0x21, 0xd0}, {0x22, 0xd0}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}
+};
+
+static RegDef SamsungDAC8531[_NUM_OF_DACREGS_S8531] = {
+ {0x00, 0x51}, {0x02, 0x01}, {0x01, 0x80},
+ {0x00, 0x55}, {0x02, 0x01}, {0x01, 0x80},
+ {0x00, 0x59}, {0x02, 0x01}, {0x01, 0x80}
+};
+
+static RegDef SamsungDAC1224[_NUM_OF_DACREGS_S1224] ={
+ {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
+ {0x03, 0x80}, {0x04, 0x06}, {0x05, 0x06}, {0x06, 0x06}
+};
+
+static DACTblDef ShadingVar3797[3] = {
+ {
+ {{99, 100, 94}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x04, 0x00, 0x00}}, {{0xcc, 0xcc, 0xcc}}, 0
+ }, {
+ {{100, 90, 100}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0xcc, 0xcc, 0xcc}}, 0
+ }, {
+ {{90, 90, 90}}, {{0x30, 0x30, 0x30}}, {{0x20, 0x20, 0x20}},
+ {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+static DACTblDef ShadingVar3799[3] = {
+ {
+ {{100, 97, 92}}, {{0x90, 0xe0, 0x80}},
+ {{0x70, 0xc0, 0x60}}, {{0x90, 0x34, 0x3c}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{75, 75, 75}}, {{0x30, 0x30, 0x30}},
+ {{0x10, 0x10, 0x10}}, {{0x20, 0x20, 0x20}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{80, 75, 64}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+/* Genius ColorPage Vivid III */
+static DACTblDef ShadingVar548[3] = {
+ {
+ {{100, 97, 92}}, {{0x90, 0xe0, 0x80}},
+ {{0x70, 0xc0, 0x60}}, {{0x90, 0x34, 0x3c}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{75, 75, 75}}, {{0x30, 0x30, 0x30}},
+ {{0x10, 0x10, 0x10}}, {{0x20, 0x20, 0x20}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{80, 75, 64}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+static DACTblDef ShadingVar3777[3] = {
+ {
+ {{100, 100, 100}}, {{0x90, 0xe0, 0x80}},
+ {{0x70, 0xc0, 0x60}}, {{0x90, 0x34, 0x3c}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{75, 75, 75}}, {{0x30, 0x30, 0x30}},
+ {{0x10, 0x10, 0x10}}, {{0x20, 0x20, 0x20}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{80, 75, 64}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ *
+ */
+static void fnCCDInitWolfson3797( pScanData ps )
+{
+ if( ps->Shade.bIntermediate & _ScanMode_Mono )
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xcc;
+ else
+ if (ps->Shade.bIntermediate & _ScanMode_AverageOut)
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x68;
+ else
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xa0;
+
+ if((ps->Shade.bIntermediate & _ScanMode_AverageOut) ||
+ (ps->DataInf.dwScanFlag & SCANDEF_Negative))
+ WolfsonDAC8143[3].bParam = 0x12;
+ else
+ WolfsonDAC8143[3].bParam = 0x10;
+}
+
+/*.............................................................................
+ *
+ */
+static void fnCCDInitSamsung3797( pScanData ps )
+{
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
+
+ if (!(ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ if( ps->Device.bPCBID == _OPTICWORKS2000 ) {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 102;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 102;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 97;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x40;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x40;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x40;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x40;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x40;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x38;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x40;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 99;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 101;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 94;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x40;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x40;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x40;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x04;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x00;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x00;
+ }
+ } else {
+ if( ps->Device.bPCBID == _OPTICWORKS2000 ) {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 96;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x48;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x48;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x38;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x38;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x48;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x48;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100; /* 98 */
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 103; /* 106 */
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 96; /* 96 */
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x10;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x10;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x110;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x1f0;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x190;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x100;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x1e0;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x180;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x10;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x20;
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnCCDInitWolfson3799( pScanData ps )
+{
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_Negative)) {
+
+ if (!(ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 103;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 102;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 99;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xc8;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xc8;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xc8;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x40;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x18;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x2c;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 98;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 95;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x0;
+ }
+ } else {
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = -0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = -0x108;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = -0x1c8;
+ }
+}
+
+/*.............................................................................
+ * Genius ColorPage VIVID III
+ */
+static void fnCCDInitWolfson548( pScanData ps )
+{
+ if (!(ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 103;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 102;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 99;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xc8;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xc8;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xc8;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x40;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x18;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x2c;
+
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 98;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 95;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x0;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnCCDInitSamsung3777( pScanData ps )
+{
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_Negative)) {
+
+ if (!(ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 109;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 108;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 105;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x4a;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x4a;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x4a;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x3c;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x38;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x38;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x2c;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x3C;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 108;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 107;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 104;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x50;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x50;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x50;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x40;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x40;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x40;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x20;
+ }
+ } else {
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = -0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = -0x108;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = -0x1c8;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnCCDInitSamsung3799( pScanData ps )
+{
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
+
+ if (!(ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ if( ps->Device.bPCBID == _SCANNER2Button ) {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 109;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 109;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 105;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x68;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x68;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x68;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x24; /* 0 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x20; /* 0 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x1c; /* 0 */
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 98;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 97;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 92;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x90;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x90;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x90;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0xc0; /* 0x90 */
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0xc0; /* 0xe0 */
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0xc0; /* 0x80 */
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0xb0; /* 0x70 */
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0xb0; /* 0xc0 */
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0xb0; /* 0x60 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x24; /* 0x90 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x00; /* 0x34 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x0c; /* 0x3c */
+ }
+ } else {
+ if( ps->Device.bPCBID == _SCANNER2Button ) {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 107;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 106;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 103;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x48;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x48;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x28; /* 0 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x18; /* 0 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x20; /* 0 */
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 104;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 107;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 99;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x30; /* 0x80 */
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x30; /* 0x0a0 */
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x150; /* 0x170 */
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x130; /* 0x90 */
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x110; /* 0x130 */
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x140; /* 0x150 */
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x120; /* 0x70 */
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x100; /* 0x120 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0xF0; /* 0x90 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0xD4; /* 0x50 */
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0xCC; /* 0x60 */
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnCCDInitESIC3799( pScanData ps )
+{
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_Negative)) {
+
+ if (!(ps->Shade.bIntermediate & _ScanMode_AverageOut)) {
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 99;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 94;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xc8;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xc8;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xc8;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x58;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x38;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x48;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x48;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x58;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x48;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 98;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 93;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x108;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0xf8;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0xc8;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x100;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0xf0;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0xc0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x108;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0xf8;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0xc8;
+ }
+ } else {
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = -0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = -0x38;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = -0x38;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnDarkOffsetWolfson3797( pScanData ps, pDACTblDef pDacTbl, ULong dwCh )
+{
+ if(( ps->Shade.DarkOffset.wColors[dwCh] -=
+ pDacTbl->DarkOffSub.wColors[dwCh]) > 0xfff ) {
+ ps->Shade.DarkOffset.wColors[dwCh] = 0;
+ }
+}
+
+/*.............................................................................
+ * this function was defined in the original sources, but never used...
+ */
+#if 0
+static void fnDarkOffsetWolfson3799( pScanData ps, pDACTblDef pDacTbl, ULong dwCh )
+{
+ if( ps->Shade.DarkOffset.wColors[dwCh] > pDacTbl->DarkOffSub.wColors[dwCh])
+ ps->Shade.DarkOffset.wColors[dwCh] -= pDacTbl->DarkOffSub.wColors[dwCh];
+ else
+ ps->Shade.DarkOffset.wColors[dwCh] = 0;
+}
+#endif
+
+/*.............................................................................
+ *
+ */
+static void fnDarkOffsetSamsung3777( pScanData ps, pDACTblDef pDacTbl, ULong dwCh )
+{
+ ps->Shade.DarkOffset.wColors[dwCh] += pDacTbl->DarkOffSub.wColors [dwCh];
+}
+
+/*.............................................................................
+ *
+ */
+static void fnDarkOffsetSamsung3797( pScanData ps, pDACTblDef pDacTbl, ULong dwCh )
+{
+ if( ps->Shade.DarkOffset.wColors[dwCh] > pDacTbl->DarkOffSub.wColors[dwCh] )
+ ps->Shade.DarkOffset.wColors[dwCh] -= pDacTbl->DarkOffSub.wColors[dwCh];
+ else
+ ps->Shade.DarkOffset.wColors[dwCh] = 0;
+}
+
+/*.............................................................................
+ *
+ */
+static void fnDarkOffsetSamsung3799( pScanData ps, pDACTblDef pDacTbl, ULong dwCh )
+{
+ if( ps->Shade.DarkOffset.wColors[dwCh] > pDacTbl->DarkOffSub.wColors[dwCh])
+ ps->Shade.DarkOffset.wColors[dwCh] -= pDacTbl->DarkOffSub.wColors[dwCh];
+ else
+ ps->Shade.DarkOffset.wColors[dwCh] = 0;
+}
+
+/*.............................................................................
+ *
+ */
+static void fnDACDarkWolfson( pScanData ps, pDACTblDef pDacTbl,
+ ULong dwCh, UShort wDarkest )
+{
+ UShort w;
+
+ w = ps->Shade.DarkDAC.bColors[dwCh];
+
+ if (wDarkest > pDacTbl->DarkCmpHi.wColors[dwCh] ) {
+
+ wDarkest -= pDacTbl->DarkCmpHi.wColors[dwCh];
+ if (wDarkest > ps->Shade.wDarkLevels)
+ w += (UShort)wDarkest / ps->Shade.wDarkLevels;
+ else
+ w++;
+
+ if (w > 0xff)
+ w = 0xff;
+
+ if(w != (UShort)ps->Shade.DarkDAC.bColors[dwCh] ) {
+ ps->Shade.DarkDAC.bColors[dwCh] = (Byte)w;
+ ps->Shade.fStop = _FALSE;
+ }
+ } else
+ if((wDarkest < pDacTbl->DarkCmpLo.wColors[dwCh]) &&
+ ps->Shade.DarkDAC.bColors[dwCh]) {
+ if (wDarkest)
+ w = (UShort)ps->Shade.DarkDAC.bColors[dwCh] - 2U;
+ else
+ w = (UShort)ps->Shade.DarkDAC.bColors[dwCh] - ps->Shade.wDarkLevels;
+
+ if ((short) w < 0)
+ w = 0;
+ if (w != (UShort)ps->Shade.DarkDAC.bColors[dwCh] ) {
+ ps->Shade.DarkDAC.bColors [dwCh] = (Byte)w;
+ ps->Shade.fStop = _FALSE;
+ }
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void fnDACDarkSamsung( pScanData ps, pDACTblDef pDacTbl,
+ ULong dwCh, UShort wDarkest )
+{
+ UShort w;
+
+ if( wDarkest > pDacTbl->DarkCmpHi.wColors[dwCh] ) {
+
+ wDarkest -= pDacTbl->DarkCmpHi.wColors [dwCh];
+ if (wDarkest > ps->Shade.wDarkLevels)
+ w = (UShort)ps->Shade.DarkDAC.bColors[dwCh] -
+ wDarkest / ps->Shade.wDarkLevels;
+ else
+ w = (UShort)ps->Shade.DarkDAC.bColors[dwCh] - 1U;
+
+ if((short) w < 0)
+ w = 0;
+
+ if(w != (UShort)ps->Shade.DarkDAC.bColors[dwCh]) {
+ ps->Shade.DarkDAC.bColors [dwCh] = (Byte)w;
+ ps->Shade.fStop = _FALSE;
+ }
+ } else
+ if((wDarkest < pDacTbl->DarkCmpLo.wColors[dwCh]) &&
+ ps->Shade.DarkDAC.bColors[dwCh]) {
+ if (wDarkest)
+ w = (UShort)ps->Shade.DarkDAC.bColors[dwCh] + 2U;
+ else
+ w = ps->Shade.wDarkLevels + (UShort)ps->Shade.DarkDAC.bColors [dwCh];
+
+ if (w > 0xff)
+ w = 0xff;
+
+ if(w != (UShort)ps->Shade.DarkDAC.bColors[dwCh]) {
+ ps->Shade.DarkDAC.bColors[dwCh] = (Byte)w;
+ ps->Shade.fStop = _FALSE;
+ }
+ }
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * according to detected CCD and DAC, we set their correct init values
+ * and functions
+ */
+_LOC void P12InitCCDandDAC( pScanData ps, Bool shading )
+{
+ UShort w;
+ pDACTblDef pDAC_CCD;
+
+ /* some presets */
+ ps->Device.f0_8_16 = _FALSE;
+
+ switch( ps->Device.bDACType ) {
+
+ case _DA_WOLFSON8143:
+
+ DBG( DBG_LOW, "WOLFSON 8143\n" );
+ switch( ps->Device.bCCDID ) {
+
+ case _CCD_3797:
+ DBG( DBG_LOW, "CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ p12ccdInitFunc = fnCCDInitWolfson3797;
+ ps->Device.fnDarkOffset = fnDarkOffsetWolfson3797;
+ ps->Device.fnDACDark = fnDACDarkWolfson;
+ ps->Device.pCCDRegisters = (pRegDef)W3797CCDParams;
+ break;
+
+ case _CCD_548:
+ DBG( DBG_LOW, "CCD-548\n" );
+ pDAC_CCD = ShadingVar548;
+ p12ccdInitFunc = fnCCDInitWolfson548;
+ ps->Device.fnDarkOffset = fnDarkOffsetWolfson3797;
+ ps->Device.fnDACDark = fnDACDarkWolfson;
+ ps->Device.pCCDRegisters = (pRegDef)W548CCDParams;
+ break;
+
+
+ default:
+ DBG( DBG_LOW, "CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ p12ccdInitFunc = fnCCDInitWolfson3799;
+/* CHECK: org was to fnDarkOffsetWolfson3797 */
+ ps->Device.fnDarkOffset = fnDarkOffsetWolfson3797;
+ ps->Device.fnDACDark = fnDACDarkWolfson;
+ ps->Device.pCCDRegisters = (pRegDef)W3799CCDParams;
+ }
+
+ ps->Device.wNumCCDRegs = _NUM_OF_CCDREGS_W8143;
+ ps->Device.wNumDACRegs = _NUM_OF_DACREGS_W8143;
+ ps->Device.pDACRegisters = WolfsonDAC8143;
+ ps->Device.RegDACOffset.Red = 0x20;
+ ps->Device.RegDACOffset.Green = 0x21;
+ ps->Device.RegDACOffset.Blue = 0x22;
+ ps->Device.RegDACGain.Red = 0x28;
+ ps->Device.RegDACGain.Green = 0x29;
+ ps->Device.RegDACGain.Blue = 0x2a;
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut ) {
+ ps->Shade.bUniGain = 1;
+ ps->Shade.bGainDouble = 1;
+ } else {
+ ps->Shade.bUniGain = 2;
+ ps->Shade.bGainDouble = 4;
+ }
+ ps->Shade.bMinGain = 1;
+ ps->Shade.bMaxGain = 0x1f;
+ ps->Shade.wDarkLevels = 10;
+
+ if( ps->Shade.bIntermediate == _ScanMode_Color )
+ WolfsonDAC8143[2].bParam = 0x52;
+ else
+ WolfsonDAC8143[2].bParam = 0x42;
+
+ if (ps->Shade.bIntermediate == _ScanMode_Mono )
+ WolfsonDAC8143 [0].bParam = 7;
+ else
+ WolfsonDAC8143 [0].bParam = 3;
+
+ break;
+
+ case _DA_SAMSUNG1224:
+
+ DBG( DBG_LOW, "Samsung 1224\n" );
+
+ switch(ps->Device.bCCDID )
+ {
+ case _CCD_3797:
+ DBG( DBG_LOW, "CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ p12ccdInitFunc = fnCCDInitSamsung3797;
+ ps->Device.fnDarkOffset = fnDarkOffsetSamsung3797;
+ ps->Device.fnDACDark = fnDACDarkSamsung;
+ ps->Device.pCCDRegisters = (pRegDef)S3797CCDParams;
+ break;
+
+ default:
+ DBG( DBG_LOW, "CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ p12ccdInitFunc = fnCCDInitSamsung3799;
+ ps->Device.fnDarkOffset = fnDarkOffsetSamsung3799;
+ ps->Device.fnDACDark = fnDACDarkSamsung;
+ ps->Device.pCCDRegisters = (pRegDef)S3799CCDParams;
+ }
+ ps->Device.wNumCCDRegs = _NUM_OF_CCDREGS_S1224;
+ ps->Device.wNumDACRegs = _NUM_OF_DACREGS_S1224;
+ ps->Device.pDACRegisters = SamsungDAC1224;
+ ps->Device.RegDACOffset.Red = 1;
+ ps->Device.RegDACOffset.Green = 2;
+ ps->Device.RegDACOffset.Blue = 3;
+ ps->Device.RegDACGain.Red = 4;
+ ps->Device.RegDACGain.Green = 5;
+ ps->Device.RegDACGain.Blue = 6;
+ ps->Shade.bGainDouble = 6;
+ ps->Shade.bUniGain = 7;
+ ps->Shade.bMinGain = 0;
+ ps->Shade.bMaxGain = 0x1f;
+ ps->Shade.wDarkLevels = 10;
+
+ if( ps->Shade.bIntermediate & _ScanMode_Mono )
+ SamsungDAC1224[0].bParam = 0x57;
+ else
+ SamsungDAC1224[0].bParam = 0x51;
+ break;
+
+ case _DA_ESIC:
+
+ DBG( DBG_LOW, "ESIC\n" );
+
+ switch( ps->Device.bCCDID ) {
+
+ case _CCD_3797:
+ DBG( DBG_LOW, "CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ p12ccdInitFunc = fnCCDInitWolfson3797;
+ ps->Device.fnDarkOffset = fnDarkOffsetWolfson3797;
+ ps->Device.fnDACDark = fnDACDarkWolfson;
+ ps->Device.pCCDRegisters = (pRegDef)W3797CCDParams;
+ break;
+
+ default:
+ DBG( DBG_LOW, "CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ p12ccdInitFunc = fnCCDInitESIC3799;
+ ps->Device.fnDarkOffset = fnDarkOffsetWolfson3797;
+ ps->Device.fnDACDark = fnDACDarkWolfson;
+ ps->Device.pCCDRegisters = (pRegDef)W3799CCDParams;
+ }
+
+ ps->Device.wNumCCDRegs = _NUM_OF_CCDREGS_W8143;
+ ps->Device.wNumDACRegs = _NUM_OF_DACREGS_W8143;
+ ps->Device.pDACRegisters = WolfsonDAC8143;
+ ps->Device.RegDACOffset.Red = 0x20;
+ ps->Device.RegDACOffset.Green = 0x21;
+ ps->Device.RegDACOffset.Blue = 0x22;
+ ps->Device.RegDACGain.Red = 0x28;
+ ps->Device.RegDACGain.Green = 0x29;
+ ps->Device.RegDACGain.Blue = 0x2a;
+
+ if( ps->Shade.bIntermediate & _ScanMode_AverageOut ) {
+ ps->Shade.bUniGain = 1;
+ ps->Shade.bGainDouble = 1;
+ } else {
+ ps->Shade.bUniGain = 2;
+ ps->Shade.bGainDouble = 4;
+ }
+ ps->Shade.bMinGain = 1;
+ ps->Shade.bMaxGain = 0x1f;
+ ps->Shade.wDarkLevels = 10;
+
+ if( ps->Shade.bIntermediate == _ScanMode_Color )
+ WolfsonDAC8143 [2].bParam = 0x52;
+ else
+ WolfsonDAC8143 [2].bParam = 0x42;
+
+ if(ps->Shade.bIntermediate == _ScanMode_Mono )
+ WolfsonDAC8143[0].bParam = 7;
+ else
+ WolfsonDAC8143[0].bParam = 3;
+ break;
+
+ /* _DA_SAMSUNG8531 */
+ default:
+
+ DBG( DBG_LOW, "SAMSUNG 8531\n" );
+ switch( ps->Device.bCCDID ) {
+
+ case _CCD_3797:
+ DBG( DBG_LOW, "CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ p12ccdInitFunc = fnCCDInitSamsung3797;
+ ps->Device.fnDarkOffset = fnDarkOffsetSamsung3797;
+ ps->Device.fnDACDark = fnDACDarkSamsung;
+ ps->Device.pCCDRegisters = (pRegDef)S3797CCDParams;
+ break;
+
+ case _CCD_3777:
+ DBG( DBG_LOW, "CCD-3777\n" );
+ pDAC_CCD = ShadingVar3777;
+ p12ccdInitFunc = fnCCDInitSamsung3777;
+ ps->Device.fnDarkOffset = fnDarkOffsetSamsung3777;
+ ps->Device.fnDACDark = fnDACDarkSamsung;
+ ps->Device.pCCDRegisters = (pRegDef)S3797CCDParams;
+ ps->Device.f0_8_16 = _TRUE;
+ break;
+
+ default:
+ DBG( DBG_LOW, "CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ p12ccdInitFunc = fnCCDInitSamsung3799;
+ ps->Device.fnDarkOffset = fnDarkOffsetSamsung3799;
+ ps->Device.fnDACDark = fnDACDarkSamsung;
+ ps->Device.pCCDRegisters = (pRegDef)S3799CCDParams;
+ }
+
+ ps->Device.wNumCCDRegs = _NUM_OF_CCDREGS_S8531;
+ ps->Device.wNumDACRegs = _NUM_OF_DACREGS_S8531;
+ ps->Device.pDACRegisters = SamsungDAC8531;
+ ps->Device.RegDACOffset.Red =
+ ps->Device.RegDACOffset.Green =
+ ps->Device.RegDACOffset.Blue = 1;
+ ps->Device.RegDACGain.Red =
+ ps->Device.RegDACGain.Green =
+ ps->Device.RegDACGain.Blue = 2;
+ ps->Shade.bGainDouble = 6;
+ ps->Shade.bMinGain = 1;
+ ps->Shade.bMaxGain = 0x1f;
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA )
+ ps->Shade.bUniGain = 2;
+ else
+ ps->Shade.bUniGain = 7;
+
+ ps->Shade.wDarkLevels = 10;
+
+ if( ps->Shade.bIntermediate & _ScanMode_Mono ) {
+ SamsungDAC8531[0].bParam = 0x57;
+ SamsungDAC8531[3].bParam = 0x57;
+ SamsungDAC8531[6].bParam = 0x57;
+ } else {
+ SamsungDAC8531[0].bParam = 0x51;
+ SamsungDAC8531[3].bParam = 0x55;
+ SamsungDAC8531[6].bParam = 0x59;
+ }
+ }
+
+ if( shading ) {
+
+ if( !(ps->DataInf.dwScanFlag & SCANDEF_TPA))
+ ps->Shade.pCcdDac = &pDAC_CCD[_REFLECTION];
+ else {
+ if( ps->DataInf.dwScanFlag & SCANDEF_Transparency )
+ ps->Shade.pCcdDac = &pDAC_CCD[_TRANSPARENCY];
+ else
+ ps->Shade.pCcdDac = &pDAC_CCD[_NEGATIVE];
+ }
+ } else
+ ps->Shade.pCcdDac = &pDAC_CCD[_REFLECTION];
+
+ /* as we now have the correct init function, call it */
+ p12ccdInitFunc( ps );
+
+ DBG( DBG_IO, "Programming DAC (%u regs)\n", ps->Device.wNumDACRegs );
+
+ for(w = 0; w < ps->Device.wNumDACRegs; w++ ) {
+
+ DBG( DBG_IO, "[0x%02x] = 0x%02x\n", ps->Device.pDACRegisters[w].bReg,
+ ps->Device.pDACRegisters[w].bParam );
+
+ IODataRegisterToDAC( ps, ps->Device.pDACRegisters[w].bReg,
+ ps->Device.pDACRegisters[w].bParam );
+ }
+}
+
+/* END PLUSTEK-PP_P12CCD.C ..................................................*/
diff --git a/backend/plustek-pp_p48xx.c b/backend/plustek-pp_p48xx.c
new file mode 100644
index 0000000..6175a34
--- /dev/null
+++ b/backend/plustek-pp_p48xx.c
@@ -0,0 +1,877 @@
+/* @file plustek-pp_p48xx.c
+ * @brief here we have all functionality according to the ASIC96001/3 based
+ * models.
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - fixed a bug for the return value in p48xxDoTest
+ * - added additional debug messages
+ * - added function p48xxCheck4800Memory
+ * - 0.32 - added debug messages
+ * - fixed a bug in p48xxDoTest
+ * - disabled RD_WatchDogControl, lamp will be controlled by driver
+ * - 0.33 - added function p48xxSetAsicRegisters()
+ * - fixed a bug in p48xxDoTest (reset the ASIC registers)
+ * - removed p48xxPositionLamp
+ * - 0.34 - added some comments
+ * - 0.35 - added some comments
+ * - 0.36 - added function p48xxInitAllModules() to allow reinit of the modules
+ * - switching from Full- to Halfstep at ps->PhysicalDpi now in
+ * - p48xxSetGeneralRegister
+ * - fixed the color-inverse problem for model OP4800
+ * - 0.37 - move p48xxOpenScanPath, p48xxCloseScanPath
+ * and p48xxRegisterToScanner to io.c
+ * - removed // comments
+ * - added override for A3I scanner
+ * - 0.38 - added function p48xxPutToIdleMode()
+ * - added function p48xxCalibration
+ * - 0.39 - added A3I stuff
+ * - 0.40 - disabled A3I stuff
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some definitions ********************************/
+
+#define _TEST_SZ 2048 /* always use 2048 for mem size (= one bank) */
+#define _START_VAL 0x12345678 /* pick a non-zero starting value for our long */
+
+#define _BankAndSizeForTest _MemBankSize2k /* always use 2k for mem test */
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ * 1) Set asic to PROGRAM mode
+ * 2) Select the memory bank and size
+ * 3) Initiate data fifo
+ */
+static void p48xxSetMemoryBankForProgram( pScanData ps , Byte bBankAndSize )
+{
+ /* enter program mode */
+ IODataToRegister( ps, ps->RegModeControl, _ModeProgram );
+
+ /* bank and size */
+ IODataToRegister( ps, ps->RegMemAccessControl, bBankAndSize );
+
+ /* initiate data fifo */
+ IORegisterToScanner( ps, ps->RegInitDataFifo );
+}
+
+/*.............................................................................
+ * use the internal memory of a scanner to find the model
+ */
+static int p48xxDoTest( pScanData ps )
+{
+ UChar tmpByte;
+ int retval;
+ ULong adder, ul, cntr;
+ pULong buffer;
+
+ DBG( DBG_LOW, "p48xxDoTest()\n" );
+
+ buffer = _KALLOC( sizeof(UChar) * _TEST_SZ, GFP_KERNEL );
+ if( NULL == buffer )
+ return _E_ALLOC;
+
+ retval = _E_NO_DEV;
+
+ /*
+ * do a memory test to determine how much memory this unit has, in the
+ * process we can figure out if it's a 4830 or a 9630. NOTE: the ram
+ * seems to be mirrored such that if you have a unit with only 32k it's
+ * mirrored 4 times to fill the 128k (2k * (_MemBankMask + 1)) space,
+ * so we will run a 32 bit incrementing pattern over the entire 128k and
+ * look for the 1st page (2k) to fail
+ */
+ adder = 0;
+ for (cntr = _BankAndSizeForTest;
+ cntr < _BankAndSizeForTest + _MemBanks; cntr++) {
+
+ ps->OpenScanPath( ps );
+
+ p48xxSetMemoryBankForProgram( ps, cntr );
+
+ /* prepare content, incrementing 32 val */
+ for (ul = 0; ul < _TEST_SZ / sizeof(ULong); ul++)
+ buffer[ul] = ul + adder + _START_VAL;
+
+ /* fill to buffer */
+ IOMoveDataToScanner( ps, (pUChar)buffer, _TEST_SZ );
+
+ /*
+ * now check bank 0 to see if it got overwritten
+ * bank 0, size 2k
+ */
+ p48xxSetMemoryBankForProgram( ps, _BankAndSizeForTest );
+
+ ps->CloseScanPath( ps );
+
+ /* read data back */
+ IOReadScannerImageData( ps, (pUChar)buffer, _TEST_SZ );
+
+ /* check */
+ for (ul = 0; ul < _TEST_SZ / sizeof(ULong); ul++) {
+ if (buffer[ul] != ul + _START_VAL) {
+ break;
+ }
+ }
+
+ /* if fail */
+ if (ul != _TEST_SZ / sizeof (ULong)) {
+ DBG( DBG_LOW, "Bank 0 overwritten\n" );
+ break;
+ }
+
+ /* now check current bank */
+ ps->OpenScanPath( ps );
+ p48xxSetMemoryBankForProgram( ps, cntr );
+ ps->CloseScanPath( ps );
+
+ /* read data back */
+ IOReadScannerImageData( ps, (pUChar)buffer, _TEST_SZ);
+
+ /* check if fail */
+ for( ul = 0; ul < _TEST_SZ / sizeof(ULong); ul++ ) {
+ if( buffer[ul] != ul + adder + _START_VAL )
+ break;
+ }
+
+ /* check if fail */
+ if (ul != _TEST_SZ / sizeof(ULong)) {
+ DBG( DBG_LOW, "Bank not present, error at pos %u (%u)\n", ul,
+ (ULong)(_TEST_SZ / sizeof(ULong)));
+ break;
+ }
+
+ adder += _TEST_SZ / sizeof(ULong);
+ }
+
+ _KFREE( buffer );
+
+ DBG( DBG_LOW, "found %d bytes of memory\n",
+ _TEST_SZ * (cntr - _BankAndSizeForTest));
+
+ if( cntr == _BankAndSizeForTest ) {
+ DBG( DBG_LOW, "No memory ! No scanner...\n" );
+ return retval;
+ }
+
+#ifdef DEBUG
+ tmpByte = IODataRegisterFromScanner( ps, 0x18 );
+ DBG( DBG_LOW, "tmpByte[0x18] = 0x%02x\n",tmpByte );
+#endif
+
+ tmpByte = IODataRegisterFromScanner( ps, 0x0e );
+ DBG( DBG_LOW, "tmpByte = 0x%02x, cntr = %u, AsicId = 0x%02x\n",
+ tmpByte, cntr, ps->sCaps.AsicID );
+
+ /* 128k */
+ if ((_TEST_SZ * (cntr - _BankAndSizeForTest) == 1 << 17) &&
+ (ps->sCaps.AsicID == _ASIC_IS_96003)) {
+
+ /*
+ * if 128k then must be a 9630 or above
+ * hack, test for 12000P, The 9630 returns an 0x08
+ */
+ if ( tmpByte == 0x02 ) {
+
+ /*
+ * as we currently can't automagically detect an A3I we have to
+ * use the override switch
+ */
+ if( _OVR_PLUSTEK_A3I == ps->ModelOverride ) {
+
+ DBG( DBG_LOW, "Model Override --> A3I\n" );
+ ModelSetA3I( ps );
+ } else {
+ ModelSet12000( ps );
+ DBG( DBG_LOW, "It seems we have a 12000P/96000P\n" );
+ }
+
+ } else {
+ ModelSet9630( ps );
+ DBG( DBG_LOW, "It seems we have a 9630\n" );
+ }
+
+ retval = _OK;
+
+ } else {
+
+ DBG( DBG_LOW, "Scanner is not a 9630 or above\n");
+
+ if ( tmpByte != 0x0f ) {
+
+ DBG( DBG_LOW, "Looks like a 600!\n" );
+
+ if (( 0x08 == tmpByte ) &&
+ ((_TEST_SZ * (cntr - _BankAndSizeForTest)) == 32768 )) {
+ DBG( DBG_LOW, "But it is a 4830P!!! "
+ "(by mkochano@ee.pw.edu.pl)\n" );
+ ModelSet4830( ps );
+ } else {
+ ModelSet600( ps );
+ }
+ }
+#ifdef DEBUG
+ else
+ DBG( DBG_LOW, "It seems we have a 4830\n" );
+#endif
+
+ retval = _OK;
+ }
+
+ return retval;
+}
+
+/*.............................................................................
+ * setup ASIC registers and clear all scan states (no stepping)
+ */
+static void p48xxSetAsicRegisters( pScanData ps )
+{
+ memset( &ps->AsicReg, 0, sizeof(ps->AsicReg));
+ memset( &ps->Asic96Reg, 0, sizeof(ps->Asic96Reg));
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ ps->AsicReg.RD_LineControl = ps->TimePerLine;
+ ps->AsicReg.RD_ScanControl = _SCAN_LAMP_ON;
+ ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelWhiteIs0;
+ ps->AsicReg.RD_Origin = 0;
+ ps->AsicReg.RD_Pixels = 5110; /*ps->RdPix;*/
+ ps->Asic96Reg.RD_MotorControl = 0;
+ ps->Asic96Reg.RD_WatchDogControl = 0; /* org. val = 0x8f; */
+
+ IOPutOnAllRegisters( ps );
+}
+
+/*.............................................................................
+ * use the internal memory of a scanner to find the model
+ */
+static int p48xxCheck4800Memory( pScanData ps )
+{
+ int retval;
+ ULong ul;
+ pUChar buffer;
+
+ DBG( DBG_LOW, "p48xxCheck4800Memory()\n" );
+
+ buffer = _KALLOC( 2560, GFP_KERNEL ); /* 1280: Read,1280:Write */
+ if( NULL == buffer )
+ return _E_ALLOC;
+
+ retval = _OK;
+
+ /* bank 0, size 2k */
+ ps->OpenScanPath( ps );
+ p48xxSetMemoryBankForProgram( ps, _BankAndSizeForTest );
+
+ for (ul = 0; ul < 1280; ul++)
+ buffer[ul] = (UChar)ul; /* prepare content */
+
+ IOMoveDataToScanner( ps, buffer, 1280 ); /* fill to buffer */
+ p48xxSetMemoryBankForProgram( ps, _BankAndSizeForTest );
+ ps->CloseScanPath( ps );
+
+ /* read data back */
+ IOReadScannerImageData( ps, buffer + 1280, 1280 );
+
+ for( ul = 0; ul < 1280; ul++ ) {
+ if( buffer[ul] != buffer[ul+1280] ) {
+ DBG( DBG_HIGH, "Error in memory test at pos %u (%u != %u)\n",
+ ul, buffer[ul], buffer[ul+1280] );
+ retval = _E_NO_DEV;
+ break;
+ }
+ }
+
+ _KFREE(buffer);
+
+ return retval;
+}
+
+/*.............................................................................
+ * call all other modules, to initialize themselves
+ */
+static int p48xxInitAllModules( pScanData ps )
+{
+ int result;
+
+ result = DacInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = ImageInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = IOFuncInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = IOInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = MotorInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ /*
+ * in debug version, check all function pointers
+ */
+#ifdef DEBUG
+ if( _FALSE == MiscAllPointersSet( ps ))
+ return _E_INTERNAL;
+#endif
+ return _OK;
+}
+
+/*.............................................................................
+ *
+ */
+static int p48xxReadWriteTest( pScanData ps )
+{
+ int retval;
+
+ DBG( DBG_LOW, "p48xxReadWriteTest()\n" );
+
+ /*
+ * determine the model by the ASIC type (except for 4830/9630)
+ * might want to make a SetModelCommon() someday for this...
+ */
+ ps->RedDataReady = 0x01; /* normal for Red and Green */
+ ps->GreenDataReady = 0x02;
+ ps->AsicRedColor = 0x01;
+ ps->AsicGreenColor = 0x03;
+
+ /*
+ * if not already set, try to find ASIC type (96001 or 96003)
+ */
+ if ( _NO_BASE == ps->sCaps.wIOBase ) {
+
+ /* get copy of asic id */
+ ps->sCaps.AsicID = IODataRegisterFromScanner( ps, ps->RegAsicID );
+
+ if ( _ASIC_IS_96003 == ps->sCaps.AsicID ) {
+
+ /* actually either a 4830, 9630, 12000, find out later */
+ DBG( DBG_LOW, "Found a 96003 ASIC at Reg 0x%x\n", ps->RegAsicID );
+ ModelSet4830( ps );
+
+ } else {
+
+ if ( _ASIC_IS_96001 == ps->sCaps.AsicID ) {
+ DBG( DBG_LOW, "Found a 96001 ASIC at Reg 0x%x\n",
+ ps->RegAsicID );
+ ModelSet4800( ps );
+ } else {
+ DBG( DBG_LOW, "Can't find your model, asic = 0x%x\n",
+ ps->sCaps.AsicID );
+ return _E_NO_ASIC;
+ }
+ }
+ }
+
+ /*
+ * set the registers according to the assumptions above
+ */
+ p48xxSetAsicRegisters( ps );
+
+ if ( _ASIC_IS_96003 == ps->sCaps.AsicID ) {
+ retval = p48xxDoTest( ps );
+
+ /*
+ * as we may now have detected another model, we have to set
+ * the registers to their new values...
+ * and maybe the modules have to be reset as well
+ */
+ if( _OK == retval ) {
+ p48xxSetAsicRegisters( ps );
+ retval = p48xxInitAllModules( ps );
+ }
+
+ return retval;
+ }
+
+ /*
+ * this part will be reached only for the 4800 - ASIC 96001
+ * we check only the memory as the original driver does
+ */
+ return p48xxCheck4800Memory( ps );
+}
+
+/*.............................................................................
+ * 1) Setup the registers of asic.
+ * 2) Determine which type of CCD we are using
+ * 3) According to the CCD, prepare the CCD dependent veriables
+ * SONY CCD:
+ * The color exposure sequence: Red, Green (after 11 red lines),
+ * Blue (after 8 green lines)
+ * TOSHIBA CCD:
+ * The color exposure sequence: Red, Blue (after 11 red lines),
+ * Green (after 8 blue lines)
+ */
+static void p48xxSetupScannerVariables( pScanData ps )
+{
+ UChar tmp;
+ TimerDef timer;
+
+ DBG( DBG_LOW, "p48xxSetupScannerVariables()\n" );
+
+ ps->OpenScanPath( ps );
+
+ IODataToRegister( ps, ps->RegModelControl2, _Model2ChannelMult );
+
+ if( 2 == IODataFromRegister( ps, ps->RegWriteIOBusDecode1 )) {
+
+ DBG( DBG_LOW, "Scanner has 97003 ASIC too.\n" );
+ ps->f97003 = _TRUE;
+ ps->b97003DarkR = 8;
+ ps->b97003DarkG = 8;
+ ps->b97003DarkB = 8;
+
+ ps->Asic96Reg.u26.RD_ModelControl2 = _Model2ChannelMult;
+ } else {
+
+ DBG( DBG_LOW, "No ASIC 97003 found.\n" );
+ ps->f97003 = _FALSE;
+ ps->Asic96Reg.u26.RD_ModelControl2 = _Model2DirectOutPort;
+ }
+
+ IODataToRegister( ps, ps->RegModelControl2,
+ ps->Asic96Reg.u26.RD_ModelControl2 );
+
+ tmp = IODataFromRegister( ps, ps->RegStatus );
+ DBG( DBG_LOW, "Status-Register = 0x%02X\n", tmp );
+#ifdef DEBUG
+ if( tmp & _FLAG_P96_MOTORTYPE ) {
+ DBG( DBG_LOW, "Scanner has Full/Half Stepping drive\n" );
+ } else {
+ DBG( DBG_LOW, "Scanner has Micro Stepping drive\n" );
+ }
+#endif
+
+ if( tmp & _FLAG_P96_CCDTYPE) {
+ ps->fSonyCCD = _FALSE;
+ DBG( DBG_LOW, "CCD is NEC/TOSHIBA Type\n" );
+ } else {
+ ps->fSonyCCD = _TRUE;
+ DBG( DBG_LOW, "CCD is SONY Type\n" );
+ }
+
+ ps->CloseScanPath( ps );
+
+ ps->b1stColorByte = ps->AsicRedColor;
+ ps->b1stColor = ps->RedDataReady;
+
+ if (ps->fSonyCCD) {
+
+ ps->b2ndColorByte = ps->AsicGreenColor;
+ ps->b2ndColor = ps->GreenDataReady;
+ ps->b3rdColorByte = _ASIC_BLUECOLOR;
+ ps->b3rdColor = _BLUE_DATA_READY;
+
+ } else { /* NEC/Toshiba CCD */
+
+ ps->b2ndColorByte = _ASIC_BLUECOLOR;
+ ps->b2ndColor = _BLUE_DATA_READY;
+ ps->b3rdColorByte = ps->AsicGreenColor;
+ ps->b3rdColor = ps->GreenDataReady;
+ }
+
+ ps->b1stMask = (Byte)~ps->b1stColor;
+ ps->b2ndMask = (Byte)~ps->b2ndColor;
+ ps->b3rdMask = (Byte)~ps->b3rdColor;
+
+ ps->b1stLinesOffset = 17;
+ ps->b2ndLinesOffset = 9;
+
+ /*
+ * calculate I/O Timer
+ * if we cannot read 200 lines within 1 second, the I/O time has to add 2
+ * CalculateIOTime ()
+ */
+ if( _PORT_SPP != ps->IO.portMode ) {
+
+ UShort wLines = 200;
+ pUChar pBuf;
+
+ pBuf = _KALLOC((_BUF_SIZE_BASE_CONST * 2), GFP_KERNEL );
+
+ if ( NULL != pBuf ) {
+
+ MiscStartTimer( &timer, _SECOND );
+
+ do {
+ IOReadScannerImageData( ps, pBuf, (_BUF_SIZE_BASE_CONST * 2));
+
+ wLines--;
+ } while (!MiscCheckTimer( &timer) && wLines);
+
+ if( !wLines )
+ ps->bExtraAdd = 0;
+ else
+ ps->bExtraAdd = 2;
+
+ _KFREE( pBuf );
+
+ } else {
+ ps->bExtraAdd = 2; /* poor resource */
+ }
+ } else {
+ ps->bExtraAdd = 0;
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void p48xxSetGeneralRegister( pScanData ps )
+{
+ if( MODEL_OP_A3I == ps->sCaps.Model ) {
+ ps->AsicReg.RD_ModelControl = _ModelDpi400 | _ModelWhiteIs0 |
+ _ModelMemSize128k4;
+ }
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+
+/* WORK: ps->PhysicalDpi should be correct, but the we have to work
+ * on motor.c again to use other running-tables
+ * if ( ps->DataInf.xyAppDpi.y <= ps->PhysicalDpi ) {
+ */
+ if ( ps->DataInf.xyAppDpi.y <= 300 ) {
+/* HEINER:A3I
+ if ( ps->DataInf.xyAppDpi.y <= ps->PhysicalDpi ) {
+*/
+ ps->Asic96Reg.RD_MotorControl = (ps->FullStep | ps->IgnorePF |
+ ps->MotorOn | _MotorDirForward);
+ } else {
+ ps->Asic96Reg.RD_MotorControl = (ps->IgnorePF | ps->MotorOn |
+ _MotorDirForward);
+ }
+
+ if ( ps->DataInf.wPhyDataType == COLOR_BW ) {
+ ps->AsicReg.RD_ScanControl = ps->bLampOn;
+
+ if (!(ps->DataInf.dwScanFlag & SCANDEF_Inverse))
+ ps->AsicReg.RD_ScanControl |= _P96_SCANDATA_INVERT;
+
+ } else {
+
+ ps->AsicReg.RD_ScanControl = ps->bLampOn | _SCAN_BYTEMODE;
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_Inverse)
+ ps->AsicReg.RD_ScanControl |= _P96_SCANDATA_INVERT;
+ }
+
+ if (ps->DataInf.xyPhyDpi.x <= 200)
+ ps->AsicReg.RD_ScanControl |= _SCAN_1ST_AVERAGE;
+
+ DBG( DBG_LOW, "RD_ModeControl = 0x%02x\n", ps->AsicReg.RD_ModeControl );
+ DBG( DBG_LOW, "RD_MotorControl = 0x%02x\n", ps->Asic96Reg.RD_MotorControl );
+ DBG( DBG_LOW, "RD_ScanControl = 0x%02x\n", ps->AsicReg.RD_ScanControl );
+}
+
+/*.............................................................................
+ *
+ */
+static void p48xxSetupScanningCondition( pScanData ps )
+{
+ DBG( DBG_LOW, "p48xxSetupScanningCondition()\n" );
+
+ IORegisterDirectToScanner( ps, ps->RegInitDataFifo );
+
+ /* Cal64kTime (); */
+ if( MODEL_OP_A3I == ps->sCaps.Model )
+ ps->wLinesPer64kTime = (UShort)(65555UL / ps->DataInf.dwAsicBytesPerPlane *
+ 5UL);
+ else
+ ps->wLinesPer64kTime = (UShort)(65555UL / ps->DataInf.dwAsicBytesPerPlane *
+ 10UL / 3UL);
+
+ DBG( DBG_LOW, "wLinesPer64kTime = %u\n", ps->wLinesPer64kTime );
+
+ ps->InitialSetCurrentSpeed( ps );
+
+ DBG( DBG_LOW, "Current Speed = %u\n", ps->bCurrentSpeed );
+
+ ps->bMinReadFifo = (Byte)((ps->DataInf.dwAsicBytesPerPlane + 511) / 512);
+ DBG( DBG_LOW, "MinReadFifo = %u\n", ps->bMinReadFifo );
+
+ p48xxSetGeneralRegister( ps );
+
+ /*
+ * if speed is not the fastest and DPI is less than 400, do half steps
+ */
+ if( ps->DataInf.wPhyDataType >= COLOR_256GRAY &&
+ !(ps->bCurrentSpeed & 1) && (ps->DataInf.xyAppDpi.y <= 300)) {
+/* HEINER:A3I
+ if( !(ps->bCurrentSpeed & 1) && (ps->DataInf.xyAppDpi.y <= ps->PhysicalDpi)) {
+*/
+ ps->fHalfStepTableFlag = _TRUE;
+ ps->Asic96Reg.RD_MotorControl &= ps->StepMask;
+ }
+
+ ps->AsicReg.RD_Dpi = ps->DataInf.xyPhyDpi.x;
+ DBG( DBG_LOW, "RD_Dpi = %u\n", ps->AsicReg.RD_Dpi );
+
+ /* SetStartStopRegister (ps) */
+ ps->AsicReg.RD_Origin = (UShort)(ps->Offset70 + ps->Device.DataOriginX +
+ ps->DataInf.crImage.x);
+
+ if (ps->DataInf.wPhyDataType < COLOR_256GRAY) {
+ ps->AsicReg.RD_Pixels =
+ (UShort)(ps->DataInf.dwAsicPixelsPerPlane + 7) & 0xfff8;
+ } else {
+ ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAsicPixelsPerPlane;
+ }
+
+ DBG( DBG_LOW, "RD_Pixels = %u\n", ps->AsicReg.RD_Pixels );
+
+ /* SetupMotorStart () */
+ IORegisterDirectToScanner( ps, ps->RegInitDataFifo);
+ ps->SetupMotorRunTable( ps );
+
+ IOSetToMotorRegister( ps );
+
+ ps->pCurrentColorRunTable = ps->pColorRunTable;
+ ps->bCurrentLineCount = 0;
+
+ IOPutOnAllRegisters( ps );
+
+ ps->OpenScanPath( ps );
+
+ /*
+ * when using the full-step speed on 600 dpi models, then set
+ * the motor into half-step mode, to avoid that the scanner hits
+ * the back of its cover
+ */
+ if((600 == ps->PhysicalDpi) && (1 == ps->bCurrentSpeed)) {
+
+ ps->Asic96Reg.RD_MotorControl &= ~ps->FullStep;
+ }
+
+ IODataToRegister( ps, ps->RegMotorControl,
+ (Byte)(ps->Asic96Reg.RD_MotorControl & ~ps->MotorOn));
+ IODataToRegister( ps, ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl);
+ IORegisterToScanner( ps, ps->RegInitDataFifo );
+
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ * switch the motor off and put the scanner into idle mode
+ */
+static void p48xxPutToIdleMode( pScanData ps )
+{
+ DBG( DBG_LOW, "Putting Scanner (ASIC 96001/3) into Idle-Mode\n" );
+
+ /*
+ * turn off motor
+ */
+ ps->Asic96Reg.RD_MotorControl = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
+}
+
+/*.............................................................................
+ * for P96001/3 ASIC
+ * do all the preliminary stuff here (calibrate the scanner and move the
+ * sensor to it´s start position, also setup the driver for the
+ * current run)
+ */
+static int p48xxCalibration( pScanData ps )
+{
+ DBG( DBG_LOW, "p48xxCalibration()\n" );
+
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+
+ while (_TRUE) {
+
+ _ASSERT(ps->WaitForShading);
+ if (ps->WaitForShading( ps )) {
+
+ if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
+
+/* HEINER:A3I disable !! */
+ MotorP96AheadToDarkArea( ps );
+
+ if( ps->Scan.fRefreshState ) {
+ ps->Scan.fRefreshState = _FALSE;
+
+ if (!ps->fReshaded) {
+ ps->fReshaded = _TRUE;
+
+ if (ps->fColorMoreRedFlag || ps->fColorMoreBlueFlag) {
+ continue;
+ }
+ }
+ }
+ }
+ break;
+
+ } else {
+ ps->fScanningStatus = _FALSE;
+ ps->DataInf.dwAppLinesPerArea = 0;
+ return _E_TIMEOUT;
+ }
+ }
+
+ if((ps->sCaps.AsicID != _ASIC_IS_96001) &&
+ (ps->DataInf.wPhyDataType != COLOR_BW)) {
+ DacP96WriteBackToGammaShadingRAM(ps);
+ }
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+ ps->bExtraMotorCtrl = 0;
+ ps->Scan.fMotorBackward = _TRUE;
+ MotorP96ConstantMoveProc( ps, 4000 );
+ }
+
+ /*
+ * move sensor and setup scanner for grabbing the picture
+ */
+ _ASSERT(ps->WaitForPositionY);
+ ps->WaitForPositionY(ps);
+ return _OK;
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * initialize the register values and function calls for the 96001/3 asic
+ */
+_LOC int P48xxInitAsic( pScanData ps )
+{
+ DBG( DBG_LOW, "P48xxInitAsic()\n" );
+
+ ps->IO.bOpenCount = 0;
+
+ ps->RegSwitchBus = 0;
+ ps->RegReadDataMode = 1;
+ ps->RegWriteDataMode = 2;
+ ps->RegEPPEnable = 3;
+ ps->RegInitDataFifo = 4;
+ ps->RegForceStep = 5;
+ ps->RegInitScanState = 6;
+ ps->RegRefreshScanState = 7;
+ ps->RegStatus = 0x10;
+ ps->RegFifoOffset = 0x11;
+ ps->RegGetScanState = 0x12;
+ ps->RegAsicID = 0x13; /* Determine the asic */
+ ps->RegReadIOBufBus = 0x17;
+ ps->RegModeControl = 0x18;
+ ps->RegLineControl = 0x19;
+ ps->RegScanControl = 0x1a;
+ ps->RegMotorControl = 0x1b;
+ ps->RegModelControl = 0x1c;
+ ps->RegMemAccessControl = 0x1d;
+ ps->RegDpiLow = 0x1e;
+ ps->RegDpiHigh = 0x1f;
+ ps->RegScanPosLow = 0x20;
+ ps->RegScanPosHigh = 0x21;
+ ps->RegWidthPixelsLow = 0x22;
+ ps->RegWidthPixelsHigh = 0x23;
+ ps->RegThresholdControl = 0x24;
+ ps->RegWatchDogControl = 0x25;
+ ps->RegModelControl2 = 0x26;
+ ps->RegThresholdGapControl = 0x27;
+ ps->RegRedChShadingOffset = 0x28;
+ ps->RegGreenChShadingOffset = 0x29;
+ ps->RegRedDCAdjust = 0x27; /* not sure why these are dup's */
+ ps->RegGreenDCAdjust = 0x28;
+ ps->RegBlueDCAdjust = 0x29;
+ ps->RegBlueChShadingOffset = 0x2a;
+ ps->RegRedChDarkOffset = 0x2b;
+ ps->RegGreenChDarkOffset = 0x2c;
+ ps->RegBlueChDarkOffset = 0x2d;
+ ps->RegWriteIOBusDecode1 = 0x2e;
+ ps->RegWriteIOBusDecode2 = 0x2f;
+ ps->RegScanStateControl = 0x30;
+ ps->RegRedChEvenOffset = 0x31;
+ ps->RegGreenChEvenOffset = 0x32;
+ ps->RegBlueChEvenOffset = 0x33;
+ ps->RegRedChOddOffset = 0x34;
+ ps->RegGreenChOddOffset = 0x35;
+ ps->RegBlueChOddOffset = 0x36;
+ ps->RegRedGainOutDirect = 0x37;
+ ps->RegGreenGainOutDirect = 0x38;
+ ps->RegBlueGainOutDirect = 0x39;
+ ps->RegLedControl = 0x3a;
+ ps->RegShadingCorrectCtrl = 0x3b;
+ ps->RegScanStateBegin = 0x40; /* (0, 1) */
+ ps->RegScanStateEnd = 0x5f; /* (62, 63) */
+
+ /*
+ * setup function calls
+ */
+ ps->ReadWriteTest = p48xxReadWriteTest;
+ ps->SetupScannerVariables = p48xxSetupScannerVariables;
+ ps->SetupScanningCondition = p48xxSetupScanningCondition;
+ ps->PutToIdleMode = p48xxPutToIdleMode;
+ ps->Calibration = p48xxCalibration;
+
+ /*
+ * setup misc
+ */
+ ps->CtrlReadHighNibble = _CTRL_GENSIGNAL + _CTRL_AUTOLF;
+ ps->CtrlReadLowNibble = _CTRL_GENSIGNAL + _CTRL_AUTOLF + _CTRL_STROBE;
+
+ ps->MotorFreeRun = 0x80;
+ ps->bLampOn = _SCAN_LAMP_ON;
+ ps->f97003 = _FALSE;
+
+ /*
+ * initialize the other modules
+ */
+ return p48xxInitAllModules( ps );
+}
+
+/* END PLUSTEK-PP_P48xx.C ...................................................*/
diff --git a/backend/plustek-pp_p9636.c b/backend/plustek-pp_p9636.c
new file mode 100644
index 0000000..0c3fcfd
--- /dev/null
+++ b/backend/plustek-pp_p9636.c
@@ -0,0 +1,1088 @@
+/* @file plustek-pp_p9636.c
+ * @brief here we have all functionality according to the p9636t
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - Added some comments
+ * - 0.32 - minor bug-fixes
+ * - change p9636ReconnectScannerPath
+ * - moved function IOSetStartStopRegister into this file
+ * - 0.33 - went back to original three calls to p9636ReconnectScannerPath
+ * to make sure that HP-printers will not start to print during
+ * scan-process
+ * - removed function p9636PositionLamp()
+ * - 0.34 - no changes
+ * - 0.35 - no changes
+ * - 0.36 - changes, due to define renaming
+ * - 0.37 - move p9636OpenScanPath, p9636CloseScanPath
+ * and p9636RegisterToScanner to io.c
+ * - removed skipping of the memory test for OP9636P
+ * - removed // comments
+ * - 0.38 - added function p9636PutToIdleMode()
+ * - moved p9636ReadWriteTest to io.c
+ * - added function p9636Calibration
+ * - renamed function p9636SetP98001Init() to p9636InitP98001()
+ * - 0.39 - no changes
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/*************************** some definitions ********************************/
+
+#define _NUM_OF_DACREGS_W8144 11
+
+/*************************** some local vars *********************************/
+
+static UShort P97ColorModeRegister[] = {
+ 0x022C, 0x2A39, 0x0A3A, 0x373B, 0x163C, 0x0E41, 0x9042, 0x0143,
+ 0x2744, 0x2745, 0x0146, 0x0347, 0x2748, 0x2F49, 0x094A, 0x034B,
+ 0x074C, 0x054D, 0x064E, 0x0850, 0x0D51, 0x0C52, 0x0B53, 0x00F0,
+
+ 0x022C, 0x3D39, 0x043A, 0x463B, 0x063C, 0x1F41, 0x8C42, 0x0143,
+ 0x1344, 0x1345, 0xF246, 0x0247, 0x1348, 0x1349, 0xFA4A, 0x004B,
+ 0x074C, 0x054D, 0x0E4E, 0x0150, 0x0651, 0x1252, 0x0B53, 0x00F0,
+
+ 0x002C, 0x1639, 0x033A, 0x1F3B, 0x073C, 0x0441, 0x1E42, 0x0143,
+ 0x1344, 0x1345, 0xF146, 0x0247, 0x1348, 0x1349, 0xF94A, 0x044B,
+ 0x074C, 0x054D, 0x034E, 0x0650, 0x0351, 0x0952, 0x0B53, 0x00F0,
+
+ 0x022C, 0x1839, 0x043A, 0x1D3B, 0x033C, 0x0C41, 0x8442, 0x0343,
+ 0x0A44, 0x0845, 0xFA46, 0x0447, 0x0A48, 0x0849, 0xF24A, 0x024B,
+ 0x034C, 0x024D, 0x0E4E, 0x0050, 0x0951, 0x0352, 0x0353, 0x00F0
+};
+
+static UShort P17ColorModeRegister[] = {
+ 0x022C, 0x2C39, 0x053A, 0x3c3B, 0x0e3C, 0x0E41, 0x9042, 0x0143,
+ 0x2744, 0x2745, 0x0146, 0x0247, 0x2748, 0x2F49, 0x094A, 0x054B,
+ 0x074C, 0x054D, 0x064E, 0x0850, 0x0D51, 0x0C52, 0x0B53, 0x00F0,
+
+ 0x022C, 0x3D39, 0x043A, 0x463B, 0x063C, 0x1F41, 0x8C42, 0x0143,
+ 0x1344, 0x1345, 0xF246, 0x0147, 0x1348, 0x1349, 0xFA4A, 0x004B,
+ 0x074C, 0x054D, 0x0E4E, 0x0150, 0x0651, 0x1252, 0x0B53, 0x00F0,
+
+ 0x002C, 0x1639, 0x023A, 0x1a3B, 0x053C, 0x0441, 0x1E42, 0x0143,
+ 0x1344, 0x1345, 0xF146, 0x0147, 0x1348, 0x1349, 0xF94A, 0x044B,
+ 0x074C, 0x054D, 0x034E, 0x0650, 0x0351, 0x0952, 0x0B53, 0x00F0,
+
+ 0x022C, 0x1839, 0x043A, 0x1D3B, 0x033C, 0x0C41, 0x8442, 0x0343,
+ 0x0A44, 0x0845, 0xFA46, 0x0347, 0x0A48, 0x0849, 0xF24A, 0x024B,
+ 0x034C, 0x024D, 0x0E4E, 0x0050, 0x0951, 0x0352, 0x0353, 0x00F0
+};
+
+static UShort P535ColorModeRegister[] = {
+ 0x022C, 0x2F39, 0x883A, 0x403B, 0x0F3C, 0x0E41, 0x9042, 0x0143,
+ 0x2744, 0x2745, 0x0146, 0x0147, 0x2748, 0x2F49, 0x094A, 0x034B,
+ 0x074C, 0x054D, 0x064E, 0x0850, 0x0D51, 0x0C52, 0x0B53, 0x00F0,
+
+ 0x022C, 0x3D39, 0x843A, 0x463B, 0x063C, 0x1F41, 0x8C42, 0x0143,
+ 0x1344, 0x1345, 0xF246, 0x0147, 0x1348, 0x1349, 0xFA4A, 0x014B,
+ 0x074C, 0x054D, 0x0E4E, 0x0150, 0x0651, 0x1252, 0x0B53, 0x00F0,
+
+ 0x002C, 0x1639, 0x833A, 0x1F3B, 0x073C, 0x0441, 0x1E42, 0x0143,
+ 0x1344, 0x1345, 0xF146, 0x0147, 0x1348, 0x1349, 0xF94A, 0x044B,
+ 0x074C, 0x054D, 0x034E, 0x0650, 0x0351, 0x0952, 0x0B53, 0x00F0,
+
+ 0x022C, 0x1639, 0x833A, 0x1D3B, 0x033C, 0x0C41, 0x8442, 0x0343,
+ 0x0A44, 0x0845, 0xFA46, 0x0347, 0x0A48, 0x0849, 0xF24A, 0x024B,
+ 0x034C, 0x024D, 0x0E4E, 0x0050, 0x0951, 0x0352, 0x0353, 0x00F0
+};
+
+static UShort P518ColorModeRegister[] = {
+ 0x022C, 0x2F39, 0x883A, 0x403B, 0x0F3C, 0x0E41, 0x9042, 0x0143,
+ 0x2744, 0x2745, 0x0146, 0x0147, 0x2748, 0x2F49, 0x094A, 0x034B,
+ 0x074C, 0x054D, 0x064E, 0x0850, 0x0D51, 0x0C52, 0x0B53, 0x00F0,
+
+ 0x022C, 0x3D39, 0x843A, 0x463B, 0x063C, 0x1F41, 0x8C42, 0x0143,
+ 0x1344, 0x1345, 0xF246, 0x0147, 0x1348, 0x1349, 0xFA4A, 0x014B,
+ 0x074C, 0x054D, 0x0E4E, 0x0150, 0x0651, 0x1252, 0x0B53, 0x00F0,
+
+ 0x002C, 0x1639, 0x853A, 0x1F3B, 0x073C, 0x0441, 0x1E42, 0x0143,
+ 0x1344, 0x1345, 0xF146, 0x0147, 0x1348, 0x1349, 0xF94A, 0x044B,
+ 0x074C, 0x054D, 0x034E, 0x0650, 0x0351, 0x0952, 0x0B53, 0x00F0,
+
+ 0x022C, 0x1639, 0x833A, 0x1D3B, 0x033C, 0x0C41, 0x8442, 0x0343,
+ 0x0A44, 0x0845, 0xFA46, 0x0347, 0x0A48, 0x0849, 0xF24A, 0x024B,
+ 0x034C, 0x024D, 0x0E4E, 0x0050, 0x0951, 0x0352, 0x0353, 0x00F0
+};
+
+static UShort P56ColorModeRegister[] = {
+ 0x022C, 0x2F39, 0x043A, 0x363B, 0x033C, 0x0E41, 0x9042, 0x0143,
+ 0x2744, 0x2745, 0x0146, 0x0247, 0x2748, 0x2F49, 0x094A, 0x034B,
+ 0x074C, 0x054D, 0x064E, 0x0850, 0x0D51, 0x0C52, 0x0B53, 0x00F0,
+
+ 0x022C, 0x3D39, 0x033A, 0x443B, 0x033C, 0x1F41, 0x8C42, 0x0143,
+ 0x1344, 0x1345, 0xF246, 0x0247, 0x1348, 0x1349, 0xFA4A, 0x004B,
+ 0x074C, 0x054D, 0x0E4E, 0x0150, 0x0651, 0x1252, 0x0B53, 0x00F0,
+
+ 0x002C, 0x1439, 0x033A, 0x1D3B, 0x033C, 0x0441, 0x1E42, 0x0143,
+ 0x1344, 0x1345, 0xF146, 0x0247, 0x1348, 0x1349, 0xF94A, 0x044B,
+ 0x074C, 0x054D, 0x034E, 0x0650, 0x0351, 0x0952, 0x0B53, 0x00F0,
+
+ 0x022C, 0x1639, 0x033A, 0x1B3B, 0x023C, 0x0C41, 0x8442, 0x0343,
+ 0x0A44, 0x0845, 0xFA46, 0x0447, 0x0A48, 0x0849, 0xF24A, 0x024B,
+ 0x034C, 0x024D, 0x0E4E, 0x0050, 0x0951, 0x0352, 0x0353, 0x00F0
+};
+
+static UShort P539ColorModeRegister[] = {
+ 0x022C, 0x2F39, 0x883A, 0x403B, 0x0F3C, 0x0E41, 0x9042, 0x0143,
+ 0x2744, 0x2745, 0x0146, 0x0147, 0x2748, 0x2F49, 0x094A, 0x034B,
+ 0x074C, 0x054D, 0x064E, 0x0850, 0x0D51, 0x0C52, 0x0B53, 0x00F0,
+
+ 0x022C, 0x3D39, 0x843A, 0x463B, 0x063C, 0x1F41, 0x8C42, 0x0143,
+ 0x1344, 0x1345, 0xF246, 0x0147, 0x1348, 0x1349, 0xFA4A, 0x014B,
+ 0x074C, 0x054D, 0x0E4E, 0x0150, 0x0651, 0x1252, 0x0B53, 0x00F0,
+
+ 0x002C, 0x1639, 0x833A, 0x1F3B, 0x073C, 0x0441, 0x1E42, 0x0143,
+ 0x1344, 0x1345, 0xF146, 0x0147, 0x1348, 0x1349, 0xF94A, 0x044B,
+ 0x074C, 0x054D, 0x034E, 0x0650, 0x0351, 0x0952, 0x0B53, 0x00F0,
+
+ 0x022C, 0x1639, 0x833A, 0x1D3B, 0x033C, 0x0C41, 0x8442, 0x0343,
+ 0x0A44, 0x0845, 0xFA46, 0x0347, 0x0A48, 0x0849, 0xF24A, 0x024B,
+ 0x034C, 0x024D, 0x0E4E, 0x0050, 0x0951, 0x0352, 0x0353, 0x00F0
+};
+
+static RegDef WolfsonDAC8144[_NUM_OF_DACREGS_W8144] = {
+ {0x01, 0x01}, {0x02, 0x04}, {0x03, 0x42}, {0x05, 0x10}, {0x20, 0xd0},
+ {0x21, 0xd0}, {0x22, 0xd0}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00},
+ {0x02, 0x04}
+};
+
+static RegDef ccdStop[] = {
+ {0x41, 0xff}, {0x42, 0xff}, {0x4b, 0xff}, {0x4c, 0xff},
+ {0x4d, 0xff}, {0x4e, 0xff}, {0x2a, 0x01}, {0x2b, 0x00},
+ {0x2d, 0x00}, {0x1b, 0x19}, {0x14, 0xff}, {0x15, 0x00}
+};
+
+static DACTblDef shadingVar = {
+ {{100, 100, 94}}, {{0x20, 0x20, 0x20}},
+ {{0x10, 0x10, 0x10}}, {{0x00, 0x00, 0x00}}, {{0xd0, 0xd0, 0xd0}}, 0
+};
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ * initialize the register values for the 98001 asic
+ */
+static void p9636InitializeAsicRegister( pScanData ps )
+{
+ memset( &ps->AsicReg, 0, sizeof(RegData));
+ ps->AsicReg.RD_ScanControl = _SCAN_1ST_AVERAGE + _SCAN_BYTEMODE;
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+}
+
+/*.............................................................................
+ * 1) Setup the registers of asic.
+ * 2) Determine which type of CCD we are using
+ * 3) According to the CCD, prepare the CCD dependent veriables
+ * SONY CCD:
+ * The color exposure sequence: Red, Green (after 11 red lines),
+ * Blue (after 8 green lines)
+ * TOSHIBA CCD:
+ * The color exposure sequence: Red, Blue (after 11 red lines),
+ * Green (after 8 blue lines)
+ */
+static void p9636Init98001( pScanData ps, Bool shading )
+{
+ Byte bData;
+ UShort w;
+ ULong dwID;
+ DataType Data;
+ pUShort pw;
+
+ DBG( DBG_LOW, "p9636InitP98001(%d)\n", shading );
+
+ bData = IODataRegisterFromScanner( ps, ps->RegConfiguration );
+
+ ps->Device.bCCDID = bData;
+ ps->Device.bCCDID &= _P98_CCD_TYPE_ID;
+ DBG( DBG_HIGH, "bData = 0x%04X, PCB-ID = 0x%02X\n", bData, (bData >> 4));
+
+ /* encode the CCD-id into the flag parameter */
+ dwID = (ULong)ps->Device.bCCDID;
+ dwID = dwID << 16;
+ ps->sCaps.dwFlag |= dwID;
+
+ /* do the defaults here and the differences below */
+ ps->Device.f0_8_16 = _FALSE;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x10;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x10;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x10;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+
+ /*
+ * yes, I know this function is rather ugly to read, but depending
+ * on the scan-settings and the detected CCD-chip it prepares the
+ * settings for the DAC
+ */
+ ps->Device.dwModelOriginY = 0x50;
+ ps->Device.wNumDACRegs = _NUM_OF_DACREGS_W8144;
+
+ switch( ps->Device.bCCDID ) {
+
+ case _CCD_3797:
+ DBG( DBG_HIGH, "CCD-ID = 0x%02X = _CCD_3797\n", ps->Device.bCCDID );
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 96;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+ if (ps->DataInf.dwScanFlag & SCANDEF_Transparency) {
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 130;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 110;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 112;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xcc;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xcc;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xcc;
+
+ ps->bsPreRedDAC = ps->bsPreGreenDAC = ps->bsPreBlueDAC = 0xcc;
+
+ ps->wsDACCompareHighRed = 0x0B0;
+ ps->wsDACCompareLowRed = 0x0A0;
+ ps->wsDACCompareHighGreen = 0x90;
+ ps->wsDACCompareLowGreen = 0x80;
+ ps->wsDACCompareHighBlue = 0x90;
+ ps->wsDACCompareLowBlue = 0x80;
+ ps->wsDACOffsetRed =
+ ps->wsDACOffsetGreen = ps->wsDACOffsetBlue = 0x30;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 97;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 82;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 100;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+
+ ps->bsPreRedDAC = ps->bsPreGreenDAC = ps->bsPreBlueDAC = 0x80;
+
+ ps->wsDACCompareHighRed = 0x90;
+ ps->wsDACCompareLowRed = 0x80;
+ ps->wsDACCompareHighGreen = 0x1a0;
+ ps->wsDACCompareLowGreen = 0x190;
+ ps->wsDACCompareHighBlue = 0x260;
+ ps->wsDACCompareLowBlue = 0x250;
+ ps->wsDACOffsetRed =
+ ps->wsDACOffsetGreen = ps->wsDACOffsetBlue = 0x20;
+ }
+ } else {
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x50;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x40;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x38;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x18;
+
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x30;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x18;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x08;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xf0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xcc;
+ if (ps->bSetScanModeFlag & _ScanMode_Mono) {
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green =
+ ((ps->bSetScanModeFlag & _ScanMode_AverageOut)? 0xa0:0x68);
+ } else {
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xcc;
+ }
+ }
+
+ if ((ps->bSetScanModeFlag & _ScanMode_Mono) ||
+ (ps->DataInf.dwScanFlag & SCANDEF_Negative)) {
+ WolfsonDAC8144[3].bParam = 0x12;
+ } else {
+ WolfsonDAC8144[3].bParam = 0x10;
+ }
+
+ pw = P97ColorModeRegister;
+ break;
+
+ case _CCD_3717:
+ DBG( DBG_HIGH, "CCD-ID = 0x%02X = _CCD_3717\n", ps->Device.bCCDID );
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 96;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 97;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 100;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x15;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x05;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x10;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x01;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x10;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x01;
+
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x28;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x14;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x10;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green =
+ ((ps->bSetScanModeFlag&_ScanMode_Mono)?0xd0:0xd6);
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xdd;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xd9;
+ ps->Device.f0_8_16 = _TRUE;
+ pw = P17ColorModeRegister;
+ break;
+
+ case _CCD_535:
+ DBG( DBG_HIGH, "CCD-ID = 0x%02X = _CCD_535\n", ps->Device.bCCDID );
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 95;
+
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x2e;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0x28;
+
+ ps->wMinCmpDpi = 75;
+ ps->lpEppColorHomePos->wMaxSteps = 890;
+ ps->lpBppColorHomePos->wMaxSteps = 890;
+ ps->lpSppColorHomePos->wMaxSteps = 890;
+/*
+ * for less 75 Dpi Motor can't moveable in Some machine
+ * ps->lpEppColorHomePos->bExposureTime = 48;
+ */
+ ps->lpEppColorHomePos->bExposureTime = 64;
+ ps->a_tabDiffParam[_ColorEpp60].bStepSpeed = 8;
+ ps->a_ColorSettings [1].bExposureTime = 64;
+ ps->a_tabDiffParam[_ColorEpp100_1400].bStepSpeed = 16;
+ ps->lpBppColorHomePos->bExposureTime = 96;
+ ps->lpSppColorHomePos->bExposureTime = 96;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xf0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xdf;
+
+ if (ps->bSetScanModeFlag & _ScanMode_Mono) {
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 110;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xf0;
+ } else {
+ if (ps->bSetScanModeFlag & _ScanMode_AverageOut) {
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xf6;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xe5;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xe4;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0x18;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0;
+ } else {
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xf0;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xe1;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xdf;
+ }
+ }
+ pw = P535ColorModeRegister;
+ break;
+
+ case _CCD_2556:
+ DBG( DBG_HIGH, "CCD-ID = 0x%02X = _CCD_2556\n", ps->Device.bCCDID );
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 100;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x10;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x10;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x10;
+
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0;
+
+ pw = P56ColorModeRegister;
+ break;
+
+ case _CCD_518:
+ DBG( DBG_HIGH, "CCD-ID = 0x%02X = _CCD_518\n", ps->Device.bCCDID );
+ ps->Device.dwModelOriginY = 0x50-3; /* 0x50-13 */
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 98;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 98;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 98;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xcc;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xcc;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xcc;
+
+ if (ps->bSetScanModeFlag & _ScanMode_Mono) {
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ }
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+ if( ps->DataInf.dwScanFlag & SCANDEF_Transparency ) {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 104;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 92;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 96;
+ ps->bsPreRedDAC = ps->bsPreGreenDAC = ps->bsPreBlueDAC = 0xcc;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = 0x10;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = 0x10;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x20;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x10;
+
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Red = 0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Green = 0;
+ ps->Shade.pCcdDac->DarkOffSub.Colors.Blue = 0;
+
+ ps->wsDACCompareHighRed = 0x80; /* 0x35 */
+ ps->wsDACCompareLowRed = 0x70; /* 0x25 */
+ ps->wsDACCompareHighGreen = 0x80; /* 0x46 */
+ ps->wsDACCompareLowGreen = 0x70; /* 0x36 */
+ ps->wsDACCompareHighBlue = 0x80; /* 0x54 */
+ ps->wsDACCompareLowBlue = 0x70; /* 0x44 */
+ ps->wsDACOffsetRed =
+ ps->wsDACOffsetGreen = ps->wsDACOffsetBlue = 0; /* 0x70 */
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 94;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 80;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 78;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+
+ ps->bsPreRedDAC = ps->bsPreGreenDAC = ps->bsPreBlueDAC = 0x80;
+
+ ps->wsDACCompareHighRed = 0x90;
+ ps->wsDACCompareLowRed = 0x80;
+ ps->wsDACCompareHighGreen = 0x1a0;
+ ps->wsDACCompareLowGreen = 0x190;
+ ps->wsDACCompareHighBlue = 0x160;
+ ps->wsDACCompareLowBlue = 0x150;
+ ps->wsDACOffsetRed = 0xb0; /* 0x180 */
+ ps->wsDACOffsetGreen = 0xcc; /* 0x180 */
+ ps->wsDACOffsetBlue = 0xe2; /* 0x240 */
+ }
+ }
+
+ if ((ps->bSetScanModeFlag & _ScanMode_Mono) ||
+ (ps->DataInf.dwScanFlag & SCANDEF_Negative)) {
+ WolfsonDAC8144[3].bParam = 0x12;
+ } else {
+ WolfsonDAC8144[3].bParam = 0x10;
+ }
+ ps->Device.f0_8_16 = _TRUE;
+ pw = P518ColorModeRegister;
+ break;
+
+ /*
+ * CCD_539
+ */
+ default:
+ DBG( DBG_HIGH, "CCD-ID = 0x%02X = _CCD_539\n", ps->Device.bCCDID );
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 100;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 98;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0xcc;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0xcc;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0xcc;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
+ if( ps->DataInf.dwScanFlag & SCANDEF_Transparency ) {
+
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 80;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 80;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 80;
+ ps->bsPreRedDAC = 0xcc;
+ ps->bsPreGreenDAC = 0xcc;
+ ps->bsPreBlueDAC = 0xcc;
+ ps->wsDACCompareHighRed = 0x90;
+ ps->wsDACCompareLowRed = 0x80;
+ ps->wsDACCompareHighGreen = 0x90;
+ ps->wsDACCompareLowGreen = 0x80;
+ ps->wsDACCompareHighBlue = 0x90;
+ ps->wsDACCompareLowBlue = 0x80;
+ ps->wsDACOffsetRed =
+ ps->wsDACOffsetGreen = ps->wsDACOffsetBlue = 0x70;
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red = 80;
+ ps->Shade.pCcdDac->GainResize.Colors.Green = 96;
+ ps->Shade.pCcdDac->GainResize.Colors.Blue = 95;
+
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = 0x90;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = 0x90;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = 0x90;
+
+ ps->bsPreRedDAC = ps->bsPreGreenDAC = ps->bsPreBlueDAC = 0x90;
+ ps->wsDACCompareHighRed = 0xd0; /* 0x90 */
+ ps->wsDACCompareLowRed = 0xc0; /* 0x80 */
+ ps->wsDACCompareHighGreen = 0x110; /* 0x1a0 */
+ ps->wsDACCompareLowGreen = 0x100; /* 0x190 */
+ ps->wsDACCompareHighBlue = 0x130; /* 0x260 */
+ ps->wsDACCompareLowBlue = 0x120; /* 0x250 */
+ ps->wsDACOffsetRed = 0x70; /* 0x70 */
+ ps->wsDACOffsetGreen = 0x70; /* 0x180 */
+ ps->wsDACOffsetBlue = 0x70; /* 0x240 */
+ }
+ }
+
+ if (ps->bSetScanModeFlag & _ScanMode_Mono ||
+ (ps->DataInf.dwScanFlag & SCANDEF_Negative)) {
+ WolfsonDAC8144[3].bParam = 0x12;
+ } else {
+ WolfsonDAC8144[3].bParam = 0x10;
+ }
+ ps->Device.f0_8_16 = _TRUE;
+ pw = P539ColorModeRegister;
+ break;
+ }
+
+ if( ps->bSetScanModeFlag == _ScanMode_Color )
+ WolfsonDAC8144[2].bParam = 0x52;
+ else
+ WolfsonDAC8144[2].bParam = 0x42;
+
+ if( ps->bSetScanModeFlag == _ScanMode_Mono )
+ WolfsonDAC8144[0].bParam = 7;
+ else
+ WolfsonDAC8144[0].bParam = 3;
+
+ ps->OpenScanPath( ps );
+
+ /*
+ * base init of the DAC
+ */
+ DBG( DBG_IO, "Programming DAC (%u regs)\n", ps->Device.wNumDACRegs );
+
+ for( w = 0; w < ps->Device.wNumDACRegs; w++) {
+
+ DBG( DBG_IO, "*[0x%02x] = 0x%02x\n",
+ WolfsonDAC8144[w].bReg, WolfsonDAC8144[w].bParam );
+ IODataRegisterToDAC( ps,
+ WolfsonDAC8144[w].bReg, WolfsonDAC8144[w].bParam );
+ }
+
+ if( ps->bSetScanModeFlag & _ScanMode_Mono ) {
+ ps->AsicReg.RD_Model1Control = _MOTOR_2916 + _BUTTON_MODE +
+ _CCD_SHIFT_GATE + _SCAN_GRAYTYPE;
+ } else {
+ ps->AsicReg.RD_Model1Control = _MOTOR_2916 +
+ _BUTTON_MODE + _CCD_SHIFT_GATE;
+ }
+ IODataToRegister( ps, ps->RegModel1Control, ps->AsicReg.RD_Model1Control );
+
+ /* Check: THIS IS 11 on the 98003 */
+ IODataToRegister( ps, ps->RegWaitStateInsert, 6 );
+
+ /*
+ * according to the scan mode, program the CCD
+ */
+ pw = pw + (ULong)ps->bSetScanModeFlag * 24;
+ DBG( DBG_LOW, "bSetScanModeFlag = %u\n", ps->bSetScanModeFlag );
+
+ for( w = 24; w--; pw++) {
+ Data.wValue = *pw;
+ IODataToRegister( ps, Data.wOverlap.b1st, Data.wOverlap.b2nd );
+ }
+
+ ps->CloseScanPath( ps );
+}
+
+/*.............................................................................
+ * call the InitAsic function
+ * set LineControl and stop motor
+ */
+static void p9636SetupScannerVariables( pScanData ps )
+{
+ ps->ReInitAsic( ps, _FALSE );
+
+ IOCmdRegisterToScanner(ps, ps->RegLineControl, ps-> AsicReg.RD_LineControl);
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES);
+ IOSetToMotorRegister( ps );
+}
+
+/*.............................................................................
+ * set all necessary register contents
+ */
+static void p9636SetGeneralRegister( pScanData ps )
+{
+ DBG( DBG_LOW, "p9636SetGeneralRegister()\n" );
+
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE + _MOTOR0_ONESTEP;
+ ps->AsicReg.RD_ModeControl = _ModeScan + _ModeFifoRSel;
+ ps->AsicReg.RD_Motor1Control = _MotorOn + _MotorDirForward;
+ ps->AsicReg.RD_Motor0Control = ps->bHpMotor | (_MotorOn+_MotorDirForward);
+ ps->AsicReg.RD_XStepTime = ps->bStepSpeed;
+
+ if( COLOR_BW == ps->DataInf.wPhyDataType ) {
+
+ ps->AsicReg.RD_ScanControl = _SCAN_BITMODE;
+
+ if( !(ps->DataInf.dwScanFlag & SCANDEF_Inverse))
+ ps->AsicReg.RD_ScanControl |= _P98_SCANDATA_INVERT;
+
+ } else {
+
+ if (COLOR_TRUE48 == ps->DataInf.wPhyDataType) {
+ ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE;
+
+ if (!(ps->DataInf.dwScanFlag & SCANDEF_RightAlign))
+ ps->AsicReg.RD_ScanControl |= _BITALIGN_LEFT;
+ } else
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Inverse )
+ ps->AsicReg.RD_ScanControl |= _P98_SCANDATA_INVERT;
+ }
+
+ ps->AsicReg.RD_ScanControl |= _SCAN_1ST_AVERAGE;
+ IOSelectLampSource( ps );
+}
+
+/*.............................................................................
+ * tell the scanner/ASIC where to start scanning and how many pixels
+ */
+static void p9636SetStartStopRegister( pScanData ps )
+{
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 + ps->Device.DataOriginX +
+ ps->DataInf.crImage.x);
+
+ DBG( DBG_LOW, "p9636SetStartStopRegister()\n" );
+
+ if (ps->bSetScanModeFlag & _ScanMode_AverageOut )
+
+ ps->AsicReg.RD_Origin = ps->AsicReg.RD_Origin >> 1;
+
+ if (ps->DataInf.wPhyDataType < COLOR_256GRAY) {
+ ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAsicBytesPerLine;
+ } else {
+ ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAsicPixelsPerPlane;
+ }
+
+ DBG( DBG_LOW, "RD_Origin = %u, RD_Pixels = %u\n",
+ ps->AsicReg.RD_Origin, ps->AsicReg.RD_Pixels );
+}
+
+/*.............................................................................
+ *
+ */
+static void p9636SetupScanningCondition( pScanData ps )
+{
+ ULong dw;
+
+ IORegisterDirectToScanner( ps, ps->RegInitDataFifo );
+
+ ps->InitialSetCurrentSpeed( ps );
+
+ if (ps->DataInf.wPhyDataType > COLOR_TRUE24) {
+ if (ps->DataInf.dwAsicBytesPerPlane < 1024)
+ ps->Scan.dwMinReadFifo = 1024;
+ else
+ ps->Scan.dwMinReadFifo = ps->DataInf.dwAsicBytesPerPlane;
+ } else {
+ if (ps->DataInf.dwAsicBytesPerPlane * 2 < 1024)
+ ps->Scan.dwMinReadFifo = 1024;
+ else
+ ps->Scan.dwMinReadFifo = ps->DataInf.dwAsicBytesPerPlane * 2;
+ }
+
+ p9636SetGeneralRegister( ps );
+
+ IORegisterDirectToScanner( ps, ps->RegInitDataFifo );
+
+ ps->SetupMotorRunTable( ps );
+
+ ps->AsicReg.RD_Dpi = ps->DataInf.xyPhyDpi.x;
+
+ p9636SetStartStopRegister( ps );
+ IOSetToMotorRegister ( ps );
+
+ ps->bCurrentLineCount = 0;
+ IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+
+ IOPutOnAllRegisters( ps );
+
+ ps->OpenScanPath( ps );
+
+ ps->AsicReg.RD_ModeControl &= ~_ModeIdle;
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl);
+
+ ps->AsicReg.RD_ModeControl = _ModeScan + _ModeFifoRSel;
+ IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl);
+
+ IORegisterToScanner( ps, ps->RegInitDataFifo );
+
+ ps->CloseScanPath( ps );
+
+ if (ps->DataInf.wPhyDataType >= COLOR_TRUE24) {
+
+ dw = ps->DataInf.dwAsicPixelsPerPlane;
+ ps->dwMaxReadFifoData = _SIZE_COLORFIFO -
+ ps->DataInf.dwAsicBytesPerPlane * 64UL /
+ (ULong)ps->bCurrentSpeed - dw;
+ } else {
+ dw = ps->DataInf.dwAsicBytesPerPlane;
+ ps->dwMaxReadFifoData = _SIZE_GRAYFIFO -
+ ps->DataInf.dwAsicBytesPerPlane * 64UL /
+ (ULong)ps->bCurrentSpeed - dw;
+ }
+
+ if ((dw = dw * 4UL) > ps->dwMaxReadFifoData) {
+ ps->dwSizeMustProcess = ps->dwMaxReadFifoData;
+ } else {
+ ps->dwSizeMustProcess = dw;
+ }
+
+ if (ps->DataInf.wPhyDataType >= COLOR_TRUE24) {
+
+ if (ps->DataInf.xyPhyDpi.y <= 150) {
+ dw = ps->DataInf.dwAsicPixelsPerPlane;
+ } else {
+ if (ps->DataInf.xyPhyDpi.y <= 300) {
+ dw = ps->DataInf.dwAsicPixelsPerPlane * 2;
+ } else {
+ if (ps->DataInf.xyPhyDpi.y <= 600) {
+ dw = ps->DataInf.dwAsicPixelsPerPlane * 4;
+ } else {
+ dw = ps->DataInf.dwAsicPixelsPerPlane * 8;
+ }
+ }
+ }
+
+ if (ps->Device.f0_8_16 && (ps->DataInf.xyPhyDpi.y >= 150))
+ dw <<= 1;
+
+ ps->dwSizeMustProcess += dw;
+ ps->Scan.dwMinReadFifo += dw;
+ ps->dwMaxReadFifoData += dw;
+ }
+}
+
+/*.............................................................................
+ * switch the scanner into idle mode
+ */
+static void p9636PutToIdleMode( pScanData ps )
+{
+ int i;
+
+ DBG( DBG_LOW, "Putting Scanner (ASIC 98001) into Idle-Mode\n" );
+
+ /*
+ * turn off motor
+ */
+ IOCmdRegisterToScanner( ps, ps->RegMotor0Control, 0x00 );
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,ps->AsicReg.RD_LineControl);
+
+ IOCmdRegisterToScanner( ps, ps->RegModeControl,
+ (_ModeIdle + _ModeFifoClose));
+
+ ps->OpenScanPath(ps);
+
+ DBG( DBG_IO, "CCD-Stop\n" );
+
+ for( i = 0; i < 12; i++ ) {
+
+ DBG(DBG_IO, "*[0x%02x] = 0x%02x\n",ccdStop[i].bReg, ccdStop[i].bParam);
+ IODataToRegister( ps, ccdStop[i].bReg, ccdStop[i].bParam );
+ }
+
+ IODataRegisterToDAC( ps, 0x01, 0x00 ); /* Close ADC */
+
+ ps->CloseScanPath(ps);
+}
+
+/*.............................................................................
+ * do all the preliminary stuff here (calibrate the scanner and move the
+ * sensor to it´s start position, also setup the driver for the
+ * current run)
+ */
+static int p9636Calibration( pScanData ps )
+{
+ DBG( DBG_LOW, "p9636Calibration()\n" );
+
+ ps->Scan.bFifoSelect = ps->RegGFifoOffset;
+
+ /*
+ * wait for shading to be done
+ */
+ _ASSERT(ps->WaitForShading);
+ if( !ps->WaitForShading( ps ))
+ return _E_TIMEOUT;
+
+ IOCmdRegisterToScanner( ps, ps->RegLineControl, _DEFAULT_LINESCANTIME );
+
+ /*
+ * move sensor and setup scanner for grabbing the picture
+ */
+ _ASSERT(ps->WaitForPositionY);
+ ps->WaitForPositionY( ps );
+
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,
+ ps->AsicReg.RD_LineControl );
+
+ ps->fDoFilter = ps->fFilterFirstLine = _FALSE;
+ ps->dwDivFilter = ps->dwMul = 53;
+ ps->bOffsetFilter = 12;
+
+ if (COLOR_256GRAY == ps->DataInf.wPhyDataType) {
+ ps->fDoFilter = _TRUE;
+ ps->pFilterBuf = ps->pGet1 = ps->pProcessingBuf;
+ ps->pGet2 = ps->pGet1 + 5120;
+ ps->pGet3 = ps->pGet2 + 5120;
+ ps->pEndBuf = ps->pGet3 + 5120;
+
+ ps->fFilterFirstLine = _TRUE;
+ ps->dwLinesFilter = ps->DataInf.dwAppLinesPerArea;
+ }
+
+ ps->bCurrentLineCount = 0x3f;
+ _DODELAY(1);
+
+ return _OK;
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * initialize the register values and function calls for the 98001 asic
+ */
+_LOC int P9636InitAsic( pScanData ps )
+{
+ int result;
+
+ DBG( DBG_LOW, "P9636InitAsic()\n" );
+
+ /*
+ * preset the asic shadow registers
+ */
+ p9636InitializeAsicRegister( ps );
+
+ ps->IO.bOpenCount = 0;
+
+ /*
+ * setup the register values
+ */
+ ps->RegSwitchBus = 0;
+ ps->RegEPPEnable = 1;
+ ps->RegECPEnable = 2;
+ ps->RegReadDataMode = 3;
+ ps->RegWriteDataMode = 4;
+ ps->RegInitDataFifo = 5;
+ ps->RegForceStep = 6;
+ ps->RegInitScanState = 7;
+ ps->RegRefreshScanState = 8;
+ ps->RegWaitStateInsert = 0x0a;
+ ps->RegRFifoOffset = 0x0a;
+ ps->RegGFifoOffset = 0x0b;
+ ps->RegBFifoOffset = 0x0c;
+ ps->RegBitDepth = 0x13;
+ ps->RegStepControl = 0x14;
+ ps->RegMotor0Control = 0x15;
+ ps->RegXStepTime = 0x16;
+ ps->RegGetScanState = 0x17;
+ ps->RegAsicID = 0x18;
+ ps->RegMemoryLow = 0x19;
+ ps->RegMemoryHigh = 0x1a;
+ ps->RegModeControl = 0x1b;
+ ps->RegLineControl = 0x1c;
+ ps->RegScanControl = 0x1d;
+ ps->RegConfiguration = 0x1e;
+ ps->RegModelControl = 0x1f;
+ ps->RegModel1Control = 0x20;
+ ps->RegDpiLow = 0x21;
+ ps->RegDpiHigh = 0x22;
+ ps->RegScanPosLow = 0x23;
+ ps->RegScanPosHigh = 0x24;
+ ps->RegWidthPixelsLow = 0x25;
+ ps->RegWidthPixelsHigh = 0x26;
+ ps->RegThresholdLow = 0x27;
+ ps->RegThresholdHigh = 0x28;
+ ps->RegThresholdGapControl = 0x29;
+ ps->RegADCAddress = 0x2a;
+ ps->RegADCData = 0x2b;
+ ps->RegADCPixelOffset = 0x2c;
+ ps->RegADCSerialOutStr = 0x2d;
+ ps->RegResetConfig = 0x2e;
+ ps->RegLensPosition = 0x2f;
+ ps->RegStatus = 0x30;
+ ps->RegScanStateControl = 0x31;
+ ps->RegRedChDarkOffsetLow = 0x33;
+ ps->RegRedChDarkOffsetHigh = 0x34;
+ ps->RegGreenChDarkOffsetLow = 0x35;
+ ps->RegGreenChDarkOffsetHigh= 0x36;
+ ps->RegBlueChDarkOffsetLow = 0x37;
+ ps->RegBlueChDarkOffsetHigh = 0x38;
+ ps->RegResetPulse0 = 0x39;
+ ps->RegResetPulse1 = 0x3a;
+ ps->RegCCDClampTiming0 = 0x3b;
+ ps->RegCCDClampTiming1 = 0x3c;
+ ps->RegVSMPTiming0 = 0x41;
+ ps->RegVSMPTiming1 = 0x42;
+ ps->RegCCDQ1Timing0 = 0x43;
+ ps->RegCCDQ1Timing1 = 0x44;
+ ps->RegCCDQ1Timing2 = 0x45;
+ ps->RegCCDQ1Timing3 = 0x46;
+ ps->RegCCDQ2Timing0 = 0x47;
+ ps->RegCCDQ2Timing1 = 0x48;
+ ps->RegCCDQ2Timing2 = 0x49;
+ ps->RegCCDQ2Timing3 = 0x4a;
+ ps->RegADCclockTiming0 = 0x4b;
+ ps->RegADCclockTiming1 = 0x4c;
+ ps->RegADCclockTiming2 = 0x4d;
+ ps->RegADCclockTiming3 = 0x4e;
+ ps->RegADCDVTiming0 = 0x50;
+ ps->RegADCDVTiming1 = 0x51;
+ ps->RegADCDVTiming2 = 0x52;
+ ps->RegADCDVTiming3 = 0x53;
+
+ /*
+ * setup function calls
+ */
+ ps->SetupScannerVariables = p9636SetupScannerVariables;
+ ps->SetupScanningCondition = p9636SetupScanningCondition;
+ ps->ReInitAsic = p9636Init98001;
+ ps->PutToIdleMode = p9636PutToIdleMode;
+ ps->Calibration = p9636Calibration;
+
+ /*
+ * setup misc
+ */
+ ps->CtrlReadHighNibble = _CTRL_GENSIGNAL + _CTRL_AUTOLF + _CTRL_STROBE;
+ ps->CtrlReadLowNibble = _CTRL_GENSIGNAL + _CTRL_AUTOLF;
+
+ ps->f97003 = _FALSE;
+ ps->IO.useEPPCmdMode = _FALSE;
+ ps->Scan.fRefreshState = _TRUE;
+ ps->wMinCmpDpi = 60;
+ ps->Scan.fMotorBackward = _FALSE;
+
+ IOSetXStepLineScanTime( ps, _DEFAULT_LINESCANTIME );
+
+ ps->Shade.pCcdDac = &shadingVar;
+ ps->bFastMoveFlag = _FastMove_Low_C75_G150;
+ ps->dwOffset70 = _P98_OFFSET70;
+
+ /*
+ * initialize the other modules and set some
+ * function pointer
+ */
+ result = DacInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = ImageInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = IOFuncInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = IOInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ result = MotorInitialize( ps );
+ if( _OK != result )
+ return result;
+
+ /*
+ * in debug version, check all function pointers
+ */
+#ifdef DEBUG
+ if( _FALSE == MiscAllPointersSet( ps ))
+ return _E_INTERNAL;
+#endif
+
+ DBG( DBG_LOW, "0x%02x\n", ps->sCaps.AsicID );
+
+ if( _FALSE == ps->OpenScanPath( ps )) {
+ DBG( DBG_LOW, "P9636InitAsic() failed.\n" );
+ return _E_NO_DEV;
+ }
+
+ /*get CCD ID */
+ ps->Device.bCCDID = IODataFromRegister( ps, ps->RegConfiguration );
+ ps->Device.bCCDID &= _P98_CCD_TYPE_ID;
+ DBG( DBG_HIGH, "CCID = 0x%02X\n", ps->Device.bCCDID);
+
+ ps->CloseScanPath( ps );
+
+ /* as the program parts concerning some CCDs don't handle TPA stuff,
+ * I assume that these devices won't have TPA functionality
+ */
+ switch( ps->Device.bCCDID ) {
+
+ case _CCD_3717:
+ case _CCD_535:
+ case _CCD_2556:
+ DBG( DBG_HIGH, "Seems we have a 9636P\n" );
+ ps->sCaps.Model = MODEL_OP_9636PP;
+ ps->sCaps.dwFlag &= ~SFLAG_TPA;
+ break;
+ }
+
+ DBG( DBG_LOW, "P9636InitAsic() done.\n" );
+ return _OK;
+}
+
+/* END PLUSTEK-PP_P9636.C ...................................................*/
diff --git a/backend/plustek-pp_procfs.c b/backend/plustek-pp_procfs.c
new file mode 100644
index 0000000..ed242a8
--- /dev/null
+++ b/backend/plustek-pp_procfs.c
@@ -0,0 +1,474 @@
+/* @file plustek-pp_procfs.c
+ * @brief this is the interface to the proc filesystem
+ *
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.37 - initial version
+ * - 0.38 - changes according to generic structure changes
+ * - 0.39 - added info about forceMode and slowIO
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - replace _PTDRV_VERx by _PTDRV_VERSTR
+ * - cleanup
+ * - 0.44 - PROC_FS changes for newer kernel
+ * - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifdef __KERNEL__
+#include <linux/proc_fs.h>
+
+#include "plustek-pp_scan.h"
+
+/* toggled by your kernel configuration */
+#ifdef CONFIG_PROC_FS
+
+/****************************** static vars **********************************/
+
+/** for the proc filesystem
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+extern struct proc_dir_entry proc_root;
+#endif
+static struct proc_dir_entry *base = NULL;
+static struct proc_dir_entry *binfo = NULL;
+static ULong devcount;
+
+/** parallel port modes... */
+static char *procfsPortModes[] = {
+ "EPP",
+ "SPP",
+ "BiDi (PS/2)",
+ "ECP"
+ "unknown",
+ NULL
+};
+
+/** CCD-Types (as for ASIC 98001 based series) */
+static TabDef procfsCCDTypes98001[] = {
+
+ { _CCD_3797, "3797" },
+ { _CCD_3717, "3717" },
+ { _CCD_535, "535" },
+ { _CCD_2556, "2556" },
+ { _CCD_518, "518" },
+ { _CCD_539, "539" },
+ { -1 , "unknown" }
+};
+
+/** CCD-Types (as for ASIC 98003 based series) */
+static TabDef procfsCCDTypes98003[] = {
+
+ { _CCD_3797, "3797" },
+ { _CCD_3799, "3799" },
+ { _CCD_535, "535" },
+ { _CCD_2556, "2556" },
+ { _CCD_518, "518" },
+ { _CCD_539, "539" },
+ { _CCD_3777, "3777" },
+ { _CCD_548 , "548" },
+ { -1 , "unknown" }
+};
+
+/****************************** local functions ******************************/
+
+#ifndef LINUX_24
+/** This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ *
+ * Note: only the top-level directory needs to do this; if
+ * a lower level is referenced, the parent will be as well.
+ *
+ * Here simply a dummy function
+ */
+static void procfsFillFunc( struct inode *inode, int fill )
+{
+}
+#endif
+
+/** returns a pointer to the port-mode string
+ */
+static const char* procfsGetMode( int mode )
+{
+ if((mode < _PORT_EPP) || (mode > _PORT_ECP))
+ return procfsPortModes[_PORT_ECP+1];
+
+ return procfsPortModes[mode];
+}
+
+/** determines CCD-Type string
+ */
+static const char* procfsGetCCDType( pScanData ps )
+{
+ int i;
+ int ccd_id = ps->Device.bCCDID;
+ pTabDef tab = procfsCCDTypes98001;
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ if(_ASIC_IS_98003 == ps->sCaps.AsicID)
+ tab = procfsCCDTypes98003;
+
+ /* seek down the description table */
+ for( i = 0; -1 != tab[i].id; i++ ) {
+
+ if( tab[i].id == ccd_id )
+ return tab[i].desc;
+ }
+ } else {
+
+ /* for older scanners only this info is available */
+ if( ps->fSonyCCD )
+ return "SONY Type";
+ else
+ return "NEC/TOSHIBA Type";
+ }
+
+ /* return the last entry if nothing applies! */
+ return tab[(sizeof(procfsCCDTypes98001)/sizeof(TabDef)-1)].desc;
+}
+
+/** will be called when reading the proc filesystem:
+ * cat /proc/pt_drv/info
+ */
+static int procfsBInfoReadProc( char *buf, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ int len = 0;
+
+ len += sprintf( buf, "Plustek Flatbed Scanner Driver version "_PTDRV_VERSTR"\n" );
+ len += sprintf( buf + len, "IOCTL-Version: 0x%08x\n",_PTDRV_IOCTL_VERSION);
+ return len;
+}
+
+/** will be called when reading the proc filesystem:
+ * cat /proc/pt_drv/deviceX/info
+ */
+static int procfsInfoReadProc( char *buf, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ int len = 0;
+ pScanData ps = (pScanData)data;
+
+ /* Tell us something about the device... */
+ if( NULL != ps ) {
+ len += sprintf( buf+len, "Model : %s\n",
+ MiscGetModelName(ps->sCaps.Model));
+ len += sprintf( buf+len, "Portaddress : 0x%X\n", ps->IO.portBase );
+ len += sprintf( buf+len, "Portmode : %s (%s I/O, %s)\n",
+ procfsGetMode(ps->IO.portMode),
+ (ps->IO.slowIO == _TRUE?"delayed":"fast"),
+ (ps->IO.forceMode == 0?"autodetect":"forced"));
+ len += sprintf( buf+len, "Buttons : %u\n", ps->Device.buttons);
+ len += sprintf( buf+len, "Warmuptime : %us\n", ps->warmup );
+ len += sprintf( buf+len, "Lamp timeout: %us\n", ps->lampoff );
+ len += sprintf( buf+len, "mov-switch : %u\n", ps->ModelOverride );
+ len += sprintf( buf+len, "I/O-delay : %u\n", ps->IO.delay );
+ len += sprintf( buf+len, "CCD-Type : %s\n", procfsGetCCDType(ps));
+ len += sprintf( buf+len, "TPA : %s\n",
+ (ps->sCaps.dwFlag & SFLAG_TPA) ? "yes":"no" );
+ }
+
+ return len;
+}
+
+/** will be called when reading the proc filesystem:
+ * cat /proc/pt_drv/devicex/buttony
+ */
+static int procfsButtonsReadProc( char *buf, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ Byte b;
+ int bc = 0;
+ int len = 0;
+ pScanData ps = (pScanData)data;
+
+ if( NULL != ps ) {
+ bc = ps->Device.buttons;
+ }
+
+ /* Check the buttons... */
+ if( 0 != bc ) {
+
+ if ( _ASIC_IS_96003 == ps->sCaps.AsicID ) {
+ MiscClaimPort( ps );
+ b = IODataRegisterFromScanner( ps, ps->RegStatus );
+ if(_FLAG_P96_KEY == (b & _FLAG_P96_KEY))
+ b = 0;
+ else
+ b = 1;
+ MiscReleasePort( ps );
+ len += sprintf( buf + len, "%u\n", b );
+ } else
+ bc = 0;
+ }
+
+ if( 0 == bc )
+ len += sprintf( buf + len, "none\n" );
+
+ return len;
+}
+
+/** create a procfs entry
+ */
+static struct proc_dir_entry *new_entry( const char *name, mode_t mode,
+ struct proc_dir_entry *parent )
+{
+#ifndef LINUX_24
+ int len;
+#endif
+ struct proc_dir_entry *ent;
+
+ if (mode == S_IFDIR)
+ mode |= S_IRUGO | S_IXUGO;
+ else if (mode == 0)
+ mode = S_IFREG | S_IRUGO;
+
+#ifndef LINUX_24
+ len = strlen(name) + 1;
+
+ /* allocate memory for the entry and the name */
+ ent = kmalloc(sizeof(struct proc_dir_entry) + len, GFP_KERNEL);
+ if( NULL == ent )
+ return NULL;
+
+ memset(ent, 0, sizeof(struct proc_dir_entry));
+
+ /* position pointer of name to end of the structure*/
+ ent->name = ((char *) ent) + sizeof(*ent);
+ strcpy((char *)ent->name, name );
+
+ ent->namelen = strlen(name);
+ ent->mode = mode;
+
+ if (S_ISDIR(mode)) {
+ ent->nlink = 2;
+ ent->fill_inode = &procfsFillFunc;
+ } else {
+ ent->nlink = 1;
+ }
+
+ proc_register( parent, ent );
+#else
+ if (mode == S_IFDIR)
+ ent = proc_mkdir( name, parent );
+ else
+ ent = create_proc_entry( name, mode, parent );
+#endif
+
+ return ent;
+}
+
+/** shutdown one proc fs entry
+ */
+static inline void destroy_proc_entry( struct proc_dir_entry *root,
+ struct proc_dir_entry **d )
+{
+#ifndef LINUX_24
+ proc_unregister( root, (*d)->low_ino );
+ kfree(*d);
+#else
+ DBG(DBG_HIGH, "pt_drv: proc del '%s' root='%s'\n", (*d)->name, root->name);
+
+ remove_proc_entry((*d)->name, root );
+#endif
+
+ *d = NULL;
+}
+
+/** shutdown the proc-tree for one device
+ */
+static void destroy_proc_tree( pScanData ps )
+{
+ int i;
+
+ DBG( DBG_HIGH, "pt_drv: destroy_proc_tree !\n" );
+
+ if( ps ) {
+
+ if( ps->procDir.entry ) {
+
+ if( ps->procDir.info )
+ destroy_proc_entry( ps->procDir.entry, &ps->procDir.info );
+
+ for( i = 0; i < ps->Device.buttons; i++ ) {
+
+ if( ps->procDir.buttons[i] )
+ destroy_proc_entry(ps->procDir.entry, &ps->procDir.buttons[i]);
+ }
+
+ destroy_proc_entry( base, &ps->procDir.entry );
+ }
+ }
+}
+
+/*************************** exported functions ******************************/
+
+/** initialize our proc-fs stuff
+ */
+int ProcFsInitialize( void )
+{
+ DBG( DBG_HIGH, "ProcFsInitialize()\n" );
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ base = new_entry( _DRV_NAME, S_IFDIR, &proc_root );
+#else
+ base = new_entry( _DRV_NAME, S_IFDIR, NULL );
+#endif
+
+ if( NULL != base ) {
+
+ devcount = 0;
+
+ binfo = new_entry( "info", 0, base );
+ if( NULL != binfo ) {
+ binfo->read_proc = procfsBInfoReadProc;
+ binfo->data = &devcount;
+ }
+ }
+
+ return _OK;
+}
+
+/** cleanup the base entry
+ */
+void ProcFsShutdown( void )
+{
+ DBG( DBG_HIGH, "ProcFsShutdown()\n" );
+
+ if( NULL != base ) {
+
+ if( NULL != binfo )
+ destroy_proc_entry( base, &binfo );
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ destroy_proc_entry( &proc_root, &base );
+#else
+ destroy_proc_entry( NULL, &base );
+#endif
+ }
+
+ devcount = 0;
+}
+
+/** will be called for each device, that has been found
+ */
+void ProcFsRegisterDevice( pScanData ps )
+{
+ int i;
+ char str[20];
+
+ if( NULL == base ) {
+ printk( KERN_ERR "pt_drv : proc not initialised yet!\n");
+ return;
+ }
+
+ memset( &ps->procDir, 0, sizeof(ProcDirDef));
+
+ sprintf( str, "device%u", ps->devno );
+
+ ps->procDir.entry = new_entry( str, S_IFDIR, base );
+ if( NULL == ps->procDir.entry )
+ goto error_exit;
+
+ ps->procDir.info = new_entry( "info", 0, ps->procDir.entry );
+ if( NULL == ps->procDir.info )
+ goto error_exit;
+
+ ps->procDir.info->read_proc = procfsInfoReadProc;
+ ps->procDir.info->data = ps;
+
+ for( i = 0; i < ps->Device.buttons; i++ ) {
+
+ sprintf( str, "button%u", i );
+
+ ps->procDir.buttons[i] = new_entry( str, 0, ps->procDir.entry );
+ if( NULL == ps->procDir.buttons[i] )
+ goto error_exit;
+
+ ps->procDir.buttons[i]->read_proc = procfsButtonsReadProc;
+ ps->procDir.buttons[i]->data = ps;
+ }
+
+ devcount++;
+ return;
+
+
+error_exit:
+
+ printk(KERN_ERR "pt_drv: failure registering /proc/ entry %s.\n", str );
+ destroy_proc_tree( ps );
+}
+
+/** cleanup the proc-fs for a certain device
+ */
+void ProcFsUnregisterDevice( pScanData ps )
+{
+ destroy_proc_tree( ps );
+}
+
+#else /* CONFIG_PROC_FS */
+
+int ProcFsInitialize( void )
+{
+ return _OK;
+}
+
+void ProcFsShutdown( void )
+{
+}
+
+void ProcFsRegisterDevice( pScanData ps )
+{
+}
+
+void ProcFsUnregisterDevice( pScanData ps )
+{
+}
+
+#endif
+
+#endif /* guard __KERNEL__ */
+
+/* END PLUSTEK-PP_PROCFS.C ..................................................*/
diff --git a/backend/plustek-pp_procs.h b/backend/plustek-pp_procs.h
new file mode 100644
index 0000000..aff67c8
--- /dev/null
+++ b/backend/plustek-pp_procs.h
@@ -0,0 +1,255 @@
+/** @file plustek-pp_procs.h
+ * @brief here are the prototypes of all exported functions
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson <rick@efn.org>
+ *
+ * History:
+ * 0.30 - initial version
+ * 0.31 - no changes
+ * 0.32 - no changes
+ * 0.33 - no changes
+ * 0.34 - added this history
+ * 0.35 - added Kevins´ changes to MiscRestorePort
+ * changed prototype of MiscReinitStruct
+ * added prototype for function PtDrvLegalRequested()
+ * 0.36 - added prototype for function MiscLongRand()
+ * removed PtDrvLegalRequested()
+ * changed prototype of function MiscInitPorts()
+ * 0.37 - added io.c and procfs.c
+ * added MiscGetModelName()
+ * added ModelSetA3I()
+ * 0.38 - added P12 stuff
+ * removed prototype of IOScannerIdleMode()
+ * removed prototype of IOSPPWrite()
+ * 0.39 - moved prototypes for the user space stuff to plustek-share.h
+ * 0.40 - no changes
+ * 0.41 - no changes
+ * 0.42 - added MapAdjust
+ * 0.43 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __PROCS_H__
+#define __PROCS_H__
+
+#ifdef _BACKEND_ENABLED
+# define _LOC static
+#else
+# define _LOC
+#endif
+
+/*
+ * implementation in plustek-pp_misc.c
+ */
+_LOC pScanData MiscAllocAndInitStruct( void );
+_LOC int MiscReinitStruct ( pScanData ps );
+
+_LOC int MiscInitPorts ( pScanData ps, int port );
+_LOC void MiscRestorePort( pScanData ps );
+_LOC void MiscStartTimer ( pTimerDef timer, unsigned long us );
+_LOC int MiscCheckTimer ( pTimerDef timer );
+
+_LOC int MiscRegisterPort ( pScanData ps, int portAddr );
+_LOC void MiscUnregisterPort ( pScanData ps );
+_LOC int MiscClaimPort ( pScanData ps );
+_LOC void MiscReleasePort ( pScanData ps );
+_LOC Long MiscLongRand ( void );
+_LOC const char *MiscGetModelName( UShort id );
+
+#ifdef DEBUG
+_LOC Bool MiscAllPointersSet( pScanData ps );
+#endif
+
+/*
+ * implementation in plustek-pp_detect.c
+ */
+_LOC int DetectScanner( pScanData ps, int mode );
+
+/*
+ * implementation in plustek-pp_p48xx.c
+ */
+_LOC int P48xxInitAsic( pScanData ps );
+
+/*
+ * implementation in plustek-pp_p9636.c
+ */
+_LOC int P9636InitAsic( pScanData ps );
+
+/*
+ * implementation in plustek-pp_p12.c
+ */
+_LOC int P12InitAsic ( pScanData ps );
+_LOC void P12SetGeneralRegister( pScanData ps );
+
+/*
+ * implementation in plustek-pp_p12ccd.c
+ */
+_LOC void P12InitCCDandDAC( pScanData ps, Bool shading );
+
+/*
+ * implementation in plustek-pp_models.c
+ */
+_LOC void ModelSet4800 ( pScanData ps );
+_LOC void ModelSet4830 ( pScanData ps );
+_LOC void ModelSet600 ( pScanData ps );
+_LOC void ModelSet12000( pScanData ps );
+_LOC void ModelSetA3I ( pScanData ps );
+_LOC void ModelSet9630 ( pScanData ps );
+_LOC void ModelSet9636 ( pScanData ps );
+_LOC void ModelSetP12 ( pScanData ps );
+
+/*
+ * implementation in plustek-pp_dac.c
+ */
+_LOC int DacInitialize( pScanData ps );
+
+_LOC void DacP98AdjustDark ( pScanData ps );
+_LOC void DacP98FillGainOutDirectPort ( pScanData ps );
+_LOC void DacP98FillShadingDarkToShadingRegister( pScanData ps );
+
+_LOC void DacP96WriteBackToGammaShadingRAM( pScanData ps );
+
+_LOC void DacP98003FillToDAC (pScanData ps, pRGBByteDef regs, pColorByte data);
+_LOC void DacP98003AdjustGain(pScanData ps, ULong color, Byte hilight );
+_LOC Byte DacP98003SumGains ( pUChar pb, ULong pixelsLine );
+
+/*
+ * implementation in plustek-pp_motor.c
+ */
+_LOC int MotorInitialize ( pScanData ps );
+_LOC void MotorSetConstantMove( pScanData ps, Byte bMovePerStep );
+_LOC void MotorToHomePosition ( pScanData ps );
+
+_LOC void MotorP98GoFullStep ( pScanData ps, ULong dwStep );
+
+_LOC void MotorP96SetSpeedToStopProc( pScanData ps );
+_LOC void MotorP96ConstantMoveProc ( pScanData ps, ULong dwLines );
+_LOC Bool MotorP96AheadToDarkArea ( pScanData ps );
+_LOC void MotorP96AdjustCurrentSpeed( pScanData ps, Byte bSpeed );
+
+_LOC void MotorP98003BackToHomeSensor ( pScanData ps );
+_LOC void MotorP98003ModuleForwardBackward( pScanData ps );
+_LOC void MotorP98003ForceToLeaveHomePos ( pScanData ps );
+_LOC void MotorP98003PositionYProc ( pScanData ps, ULong steps);
+
+/*
+ * implementation in plustek-pp_map.c
+ */
+_LOC void MapInitialize ( pScanData ps );
+_LOC void MapSetupDither( pScanData ps );
+_LOC void MapAdjust ( pScanData ps, int which );
+
+/*
+ * implementation in plustek-pp_image.c
+ */
+_LOC int ImageInitialize( pScanData ps );
+
+/*
+ * implementation in plustek-pp_genericio.c
+ */
+_LOC int IOFuncInitialize ( pScanData ps );
+_LOC Byte IOSetToMotorRegister ( pScanData ps );
+_LOC Byte IOGetScanState ( pScanData ps, Bool fOpenned );
+_LOC Byte IOGetExtendedStatus ( pScanData ps );
+_LOC void IOGetCurrentStateCount( pScanData, pScanState pScanStep);
+_LOC int IOIsReadyForScan ( pScanData ps );
+
+_LOC void IOSetXStepLineScanTime( pScanData ps, Byte b );
+_LOC void IOSetToMotorStepCount ( pScanData ps );
+_LOC void IOSelectLampSource ( pScanData ps );
+_LOC Bool IOReadOneShadingLine ( pScanData ps, pUChar pBuf, ULong len );
+_LOC ULong IOReadFifoLength ( pScanData ps );
+_LOC void IOPutOnAllRegisters ( pScanData ps );
+_LOC void IOReadColorData ( pScanData ps, pUChar pBuf, ULong len );
+
+/*
+ * implementation in plustek-pp_io.c
+ */
+_LOC int IOInitialize ( pScanData ps );
+_LOC void IOMoveDataToScanner ( pScanData ps, pUChar pBuffer, ULong size );
+_LOC void IODownloadScanStates( pScanData ps );
+_LOC void IODataToScanner ( pScanData, Byte bValue );
+_LOC void IODataToRegister ( pScanData ps, Byte bReg, Byte bData );
+_LOC Byte IODataFromRegister ( pScanData ps, Byte bReg );
+_LOC void IORegisterToScanner ( pScanData ps, Byte bReg );
+_LOC void IODataRegisterToDAC ( pScanData ps, Byte bReg, Byte bData );
+
+_LOC Byte IODataRegisterFromScanner( pScanData ps, Byte bReg );
+_LOC void IOCmdRegisterToScanner ( pScanData ps, Byte bReg, Byte bData );
+_LOC void IORegisterDirectToScanner( pScanData, Byte bReg );
+_LOC void IOSoftwareReset ( pScanData ps );
+_LOC void IOReadScannerImageData ( pScanData ps, pUChar pBuf, ULong size );
+
+#ifdef __KERNEL__
+_LOC void IOOut ( Byte data, UShort port );
+_LOC void IOOutDelayed( Byte data, UShort port );
+_LOC Byte IOIn ( UShort port );
+_LOC Byte IOInDelayed ( UShort port );
+#endif
+
+/*
+ * implementation in plustek-pp_tpa.c
+ */
+_LOC void TPAP98001AverageShadingData( pScanData ps );
+_LOC void TPAP98003FindCenterPointer ( pScanData ps );
+_LOC void TPAP98003Reshading ( pScanData ps );
+
+/*
+ * implementation in plustek-pp_scale.c
+ */
+_LOC void ScaleX( pScanData ps, pUChar inBuf, pUChar outBuf );
+
+/*
+ * implementation in plustek-pp_procfs.c (Kernel-mode only)
+ */
+#ifdef __KERNEL__
+int ProcFsInitialize ( void );
+void ProcFsShutdown ( void );
+void ProcFsRegisterDevice ( pScanData ps );
+void ProcFsUnregisterDevice( pScanData ps );
+#endif
+
+#endif /* guard __PROCS_H__ */
+
+/* END PLUSTEK-PP_PROCS.H ...................................................*/
diff --git a/backend/plustek-pp_ptdrv.c b/backend/plustek-pp_ptdrv.c
new file mode 100644
index 0000000..9409e56
--- /dev/null
+++ b/backend/plustek-pp_ptdrv.c
@@ -0,0 +1,1987 @@
+/* @file plustek-pp_ptdrv.c
+ * @brief this is the driver interface
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - Added some comments
+ * - added claiming/release of parallel port resources for this driver
+ * - added scaling function for high resolution modes where dpix < dpiy
+ * - 0.32 - Revised lamp-off behaviour
+ * - removed function ptdrvIsLampOn
+ * - fixed misbehaviour when using cat /dev/pt_drv
+ * - moved parport-functions to module misc.c
+ * - 0.33 - added parameter lOffonEnd
+ * - revised parport concurrency
+ * - removed calls to ps->PositionLamp
+ * - 0.34 - no changes
+ * - 0.35 - removed _PTDRV_PUT_SCANNER_MODEL from ioctl interface
+ * - added Kevins' changes (MiscRestorePort)
+ * - added parameter legal and function PtDrvLegalRequested()
+ * - 0.36 - removed a bug in the shutdown function
+ * - removed all OP600P specific stuff because of the Primax tests
+ * - added version code to ioctl interface
+ * - added new parameter mov - model override
+ * - removed parameter legal
+ * - removed function PtDrvLegalRequested
+ * - changes, due to define renaming
+ * - patch for OpticPro 4800P
+ * - added multiple device support
+ * - added proc fs support/also for Kernel2.4
+ * - 0.37 - cleanup work, moved the procfs stuff to file procfs.c
+ * - and some definitions to plustek_scan.h
+ * - moved MODELSTR to misc.c
+ * - output of the error-code after initialization
+ * - 0.38 - added P12 stuff
+ * - removed function ptdrvIdleMode
+ * - moved function ptdrvP96Calibration() to p48xxCalibration
+ * - moved function ptdrvP98Calibration() to p9636Calibration
+ * - added devfs support (patch by Gordon Heydon <gjheydon@bigfoot.com>)
+ * - 0.39 - added schedule stuff after reading one line to have a better
+ * system response in SPP modes
+ * - added forceMode switch
+ * - 0.40 - added MODULE_LICENSE stuff
+ * - 0.41 - added _PTDRV_ADJUST functionality
+ * - changed ioctl call to PutImage
+ * - 0.42 - added _PTDRV_SETMAP functionality
+ * - improved the cancel functionality
+ * - 0.43 - added LINUX_26 stuff
+ * - changed include names
+ * - changed version string stuff
+ * - 0.44 - added support for more recent kernels
+ * - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifdef __KERNEL__
+# include <linux/module.h>
+# include <linux/version.h>
+
+# ifdef CONFIG_DEVFS_FS
+# include <linux/devfs_fs_kernel.h>
+# if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,69))
+# define DEVFS_26_STYLE
+# endif
+# endif
+#endif
+
+#include "plustek-pp_scan.h"
+
+#ifdef __KERNEL__
+# include <linux/param.h>
+#endif
+
+/****************************** static vars **********************************/
+
+/* default port is at 0x378 */
+static int port[_MAX_PTDEVS] = { 0x378, 0, 0, 0 };
+
+#ifdef __KERNEL__
+static pScanData PtDrvDevices[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = NULL};
+
+/* default is 180 secs for lamp switch off */
+static int lampoff[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 180 };
+
+/* warmup period for lamp (30 secs) */
+static int warmup[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 30 };
+
+/* switch lamp off on unload (default = no)*/
+static int lOffonEnd[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 };
+
+/* model override (0-->none) */
+static UShort mov[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 };
+
+/* forceMode (0--> auto, 1: SPP, 2:EPP, others: auto) */
+static UShort forceMode[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 };
+
+/* to use delayed I/O for each device */
+static Bool slowIO[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = _FALSE };
+
+#else
+
+static pScanData PtDrvDevices[_MAX_PTDEVS]= { NULL, NULL, NULL, NULL };
+static int lampoff[_MAX_PTDEVS] = { 180, 180, 180, 180 };
+static int warmup[_MAX_PTDEVS] = { 30, 30, 30, 30 };
+static int lOffonEnd[_MAX_PTDEVS] = { 0, 0, 0, 0 };
+static UShort mov[_MAX_PTDEVS] = { 0, 0, 0, 0 };
+static UShort forceMode[_MAX_PTDEVS] = { 0, 0, 0, 0 };
+
+#endif
+
+/* timers for warmup checks */
+static TimerDef toTimer[_MAX_PTDEVS];
+
+#ifndef __KERNEL__
+static Bool PtDrvInitialized = _FALSE;
+#ifdef HAVE_SETITIMER
+static struct itimerval saveSettings;
+#endif
+#else
+static Bool deviceScanning = _FALSE;
+
+static struct timer_list tl[_MAX_PTDEVS];
+
+/* for calculation of the timer expiration */
+extern volatile unsigned long jiffies;
+
+/* the parameter interface
+ */
+#if ((LINUX_VERSION_CODE > 0x020111) && defined(MODULE))
+MODULE_AUTHOR("Gerhard Jaeger <gerhard@gjaeger.de>");
+MODULE_DESCRIPTION("Plustek parallelport-scanner driver");
+
+/* addresses this 'new' license feature... */
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13))
+MODULE_PARM(port, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+MODULE_PARM(lampoff, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+MODULE_PARM(warmup,"1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+MODULE_PARM(lOffonEnd, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+MODULE_PARM(mov, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+MODULE_PARM(slowIO,"1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+MODULE_PARM(forceMode,"1-" __MODULE_STRING(_MAX_PTDEVS) "i");
+
+#else
+
+static int array_len = _MAX_PTDEVS;
+
+module_param_array(port, int, &array_len, 0);
+module_param_array(lampoff, int, &array_len, 0);
+module_param_array(warmup, int, &array_len, 0);
+module_param_array(lOffonEnd, int, &array_len, 0);
+module_param_array(mov, ushort, &array_len, 0);
+module_param_array(slowIO, int, &array_len, 0);
+module_param_array(forceMode, ushort, &array_len, 0);
+
+#endif
+
+
+MODULE_PARM_DESC(port, "I/O base address of parport");
+MODULE_PARM_DESC(lampoff, "Lamp-Off timer preset in seconds");
+MODULE_PARM_DESC(warmup, "Minimum warmup time in seconds");
+MODULE_PARM_DESC(lOffonEnd, "1 - switchoff lamp on unload");
+MODULE_PARM_DESC(mov, "Modell-override switch");
+MODULE_PARM_DESC(slowIO, "0 = Fast I/O, 1 = Delayed I/O");
+MODULE_PARM_DESC(forceMode, "0 = use auto detection, "
+ "1 = use SPP mode, 2 = use EPP mode");
+#endif
+
+#if defined (CONFIG_DEVFS_FS)
+# ifndef (DEVFS_26_STYLE)
+ static devfs_handle_t devfs_handle = NULL;
+# endif
+#else
+# ifdef LINUX_26
+ static class_t *ptdrv_class;
+# endif
+#endif
+
+/*
+ * the module interface
+ */
+static int pt_drv_open ( struct inode *, struct file *);
+static CLOSETYPE pt_drv_close( struct inode *, struct file *);
+
+#ifdef LINUX_20
+ static int pt_drv_read( struct inode*, struct file*, char*, int );
+ static int pt_drv_write( struct inode*, struct file*, const char*, int );
+#else
+ static ssize_t pt_drv_read ( struct file *file,
+ char *buffer, size_t count, loff_t *);
+ static ssize_t pt_drv_write( struct file *file,
+ const char *buffer, size_t tmp,loff_t *count);
+#endif
+
+#ifdef NOLOCK_IOCTL
+ static long pt_drv_ioctl( struct file *, UInt, unsigned long );
+#else
+ static int pt_drv_ioctl( struct inode *, struct file *, UInt, unsigned long );
+#endif
+
+
+/*
+ * the driver interface
+ */
+#ifdef LINUX_20
+
+static struct file_operations pt_drv_fops =
+{
+ NULL, /* seek */
+ pt_drv_read, /* read */
+ pt_drv_write, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ pt_drv_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pt_drv_open, /* open */
+ pt_drv_close, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL /* revalidate */
+};
+
+#else /* 2.2.x and higher stuff */
+
+static struct file_operations pt_drv_fops = {
+#ifdef LINUX_24
+ owner: THIS_MODULE,
+#endif
+ read: pt_drv_read,
+ write: pt_drv_write,
+ IOCTL: pt_drv_ioctl,
+ open: pt_drv_open,
+ release: pt_drv_close,
+};
+
+#endif
+
+#endif /* guard __KERNEL */
+
+/****************************** some prototypes ******************************/
+
+static void ptdrvStartLampTimer( pScanData ps );
+
+/****************************** local functions ******************************/
+
+#ifdef __KERNEL__
+/** depending on the device, return the data structure
+ */
+static pScanData get_pt_from_inode(struct inode *ip)
+{
+ int minor = _MINOR(ip);
+
+ /*
+ * unit out of range
+ */
+ if (minor >= _MAX_PTDEVS )
+ return NULL;
+
+ return( PtDrvDevices[minor] );
+}
+#endif
+
+/** copy user-space data into kernel memory
+ */
+static int getUserPtr(const pVoid useraddr, pVoid where, UInt size )
+{
+ int err = _OK;
+
+ /* do parameter checks */
+ if((NULL == useraddr) || ( 0 == size))
+ return _E_INVALID;
+
+#ifdef __KERNEL__
+ if ((err = verify_area_20(VERIFY_READ, useraddr, size)))
+ return err;
+#endif
+
+ switch (size) {
+#ifdef __KERNEL__
+ case sizeof(u_char):
+ GET_USER_RET(*(u_char *)where, (u_char *) useraddr, -EFAULT);
+ break;
+
+ case sizeof(u_short):
+ GET_USER_RET(*(u_short *)where, (u_short *) useraddr, -EFAULT);
+ break;
+
+ case sizeof(u_long):
+ GET_USER_RET(*(u_long *)where, (u_long *) useraddr, -EFAULT);
+ break;
+
+ default:
+ if (copy_from_user(where, useraddr, size))
+ return -EFAULT;
+#else
+ default:
+ memcpy( where, useraddr, size );
+#endif
+ }
+ return err;
+}
+
+/** copy kernel data into user mode address space
+ */
+static int putUserPtr( const pVoid ptr, pVoid useraddr, UInt size )
+{
+ int err = _OK;
+
+ if (NULL == useraddr)
+ return _E_INVALID;
+
+#ifdef __KERNEL__
+ if ((err = verify_area_20(VERIFY_WRITE, useraddr, size)))
+ return err;
+
+ if (copy_to_user(useraddr, ptr, size ))
+ return -EFAULT;
+#else
+ memcpy( useraddr, ptr, size );
+#endif
+
+ return err;
+}
+
+#ifndef __KERNEL__
+static unsigned long copy_from_user( pVoid dest, pVoid src, unsigned long len )
+{
+ memcpy( dest, src, len );
+ return 0;
+}
+
+static unsigned long copy_to_user( pVoid dest, pVoid src, unsigned long len )
+{
+ memcpy( dest, src, len );
+ return 0;
+}
+#endif
+
+/**
+ */
+static int putUserVal(const ULong value, pVoid useraddr, UInt size)
+{
+#ifdef __KERNEL__
+ int err;
+#endif
+
+ if (NULL == useraddr)
+ return _E_INVALID;
+
+#ifdef __KERNEL__
+ if ((err = verify_area_20(VERIFY_WRITE, useraddr, size)))
+ return err;
+#endif
+
+ switch (size) {
+
+#ifdef __KERNEL__
+ case sizeof(u_char):
+ PUT_USER_RET((u_char)value, (u_char *) useraddr, -EFAULT);
+ break;
+ case sizeof(u_short):
+ PUT_USER_RET((u_short)value, (u_short *) useraddr, -EFAULT);
+ break;
+ case sizeof(u_long):
+ PUT_USER_RET((u_long)value, (u_long *) useraddr, -EFAULT);
+ break;
+#else
+ case sizeof(UChar):
+ *(pUChar)useraddr = (UChar)value;
+ break;
+ case sizeof(UShort):
+ *(pUShort)useraddr = (UShort)value;
+ break;
+ case sizeof(ULong):
+ *(pULong)useraddr = (ULong)value;
+ break;
+
+#endif
+ default:
+ return _E_INVALID;
+ }
+ return 0;
+}
+
+/** switch lamp 0 on
+ */
+static void ptDrvSwitchLampOn( pScanData ps )
+{
+ DBG( DBG_LOW, "Switching lamp 0 on.\n" );
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+
+ ps->AsicReg.RD_ScanControl |= _SCAN_NORMALLAMP_ON;
+
+ ps->bLastLampStatus = _SCAN_NORMALLAMP_ON;
+
+ } else {
+
+ ps->AsicReg.RD_ScanControl |= ps->bLampOn;
+ ps->bLastLampStatus = ps->bLampOn;
+ }
+
+ IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+}
+
+/** check the lamp warmup
+ */
+static void ptdrvLampWarmup( pScanData ps )
+{
+ Bool warmupNeeded;
+ TimerDef timer;
+
+ if( 0 == ps->warmup )
+ return;
+
+ warmupNeeded = _FALSE;
+
+ /*
+ * do we have to warmup again ? Timer has not elapsed...
+ */
+ if( _OK == MiscCheckTimer( &toTimer[ps->devno] )) {
+
+ DBG( DBG_LOW, "Startup warmup needed!\n" );
+ warmupNeeded = _TRUE;
+ } else {
+
+ warmupNeeded = ps->fWarmupNeeded;
+ }
+
+ if( warmupNeeded ) {
+
+ /*
+ * correct lamp should have been switched on but
+ * before doing anything else wait until warmup has been done
+ */
+ DBG( DBG_LOW, "Waiting on warmup - %u s\n", ps->warmup );
+
+ MiscStartTimer( &timer, _SECOND * ps->warmup );
+ while( !MiscCheckTimer( &timer )) {
+
+ /* on break, we setup the initial timer again... */
+ if( _FALSE == ps->fScanningStatus ) {
+ MiscStartTimer( &toTimer[ps->devno], (_SECOND * ps->warmup));
+ return;
+ }
+ };
+
+ }
+#ifdef DEBUG
+ else {
+ DBG( DBG_LOW, "No warm-up needed \n" );
+ }
+#endif
+
+ /*
+ * start a timer here again with only a second timeout
+ * because we need this one only for startup (Force timeout!!)
+ */
+ MiscStartTimer( &toTimer[ps->devno], _SECOND );
+}
+
+/**
+ */
+#ifdef __KERNEL__
+static void ptdrvLampTimerIrq( unsigned long ptr )
+#else
+static void ptdrvLampTimerIrq( int sig_num )
+#endif
+{
+ pScanData ps;
+
+ DBG( DBG_HIGH, "!! IRQ !! Lamp-Timer stopped.\n" );
+
+#ifdef __KERNEL__
+ ps = (pScanData)ptr;
+#else
+ _VAR_NOT_USED( sig_num );
+ ps = PtDrvDevices[0];
+#endif
+
+ /*
+ * paranoia check!
+ */
+ if( NULL == ps )
+ return;
+
+ if( _NO_BASE == ps->sCaps.wIOBase )
+ return;
+
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+ ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMPS_ON;
+ } else {
+ ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMP_ON;
+ }
+
+ /* force warmup... */
+ ps->bLastLampStatus = 0xFF;
+
+ /*
+ * claim parallel port if necessary...
+ * if the port is busy, restart the timer
+ */
+ if( _OK != MiscClaimPort(ps)) {
+ ptdrvStartLampTimer( ps );
+ return;
+ }
+
+ IOCmdRegisterToScanner( ps, ps->RegScanControl,
+ ps->AsicReg.RD_ScanControl );
+ MiscReleasePort(ps);
+}
+
+/**
+ */
+static void ptdrvStartLampTimer( pScanData ps )
+{
+#ifndef __KERNEL__
+ sigset_t block, pause_mask;
+ struct sigaction s;
+#ifdef HAVE_SETITIMER
+ struct itimerval interval;
+#endif
+
+ /* block SIGALRM */
+ sigemptyset( &block );
+ sigaddset ( &block, SIGALRM );
+ sigprocmask( SIG_BLOCK, &block, &pause_mask );
+
+ /* setup handler */
+ sigemptyset( &s.sa_mask );
+ sigaddset ( &s.sa_mask, SIGINT );
+ s.sa_flags = 0;
+ s.sa_handler = ptdrvLampTimerIrq;
+
+ if( sigaction( SIGALRM, &s, NULL ) < 0 ) {
+ DBG(DBG_HIGH,"pt_drv%u: Can't setup timer-irq handler\n",ps->devno);
+ }
+
+ sigprocmask( SIG_UNBLOCK, &block, &pause_mask );
+
+#ifdef HAVE_SETITIMER
+ /*
+ * define a one-shot timer
+ */
+ interval.it_value.tv_usec = 0;
+ interval.it_value.tv_sec = ps->lampoff;
+ interval.it_interval.tv_usec = 0;
+ interval.it_interval.tv_sec = 0;
+
+ if( 0 != ps->lampoff )
+ setitimer( ITIMER_REAL, &interval, &saveSettings );
+#else
+ alarm( ps->lampoff );
+#endif
+#else
+ init_timer( &tl[ps->devno] );
+
+ /* timeout val in seconds */
+ tl[ps->devno].expires = jiffies + ps->lampoff * HZ;
+ tl[ps->devno].data = (unsigned long)ps;
+ tl[ps->devno].function = ptdrvLampTimerIrq;
+
+ if( 0 != ps->lampoff )
+ add_timer( &tl[ps->devno] );
+#endif
+
+ DBG( DBG_HIGH, "Lamp-Timer started!\n" );
+}
+
+/**
+ */
+static void ptdrvStopLampTimer( pScanData ps )
+{
+#ifndef __KERNEL__
+ sigset_t block, pause_mask;
+
+ /* block SIGALRM */
+ sigemptyset( &block );
+ sigaddset ( &block, SIGALRM );
+ sigprocmask( SIG_BLOCK, &block, &pause_mask );
+#ifdef HAVE_SETITIMER
+ if( 0 != ps->lampoff )
+ setitimer( ITIMER_REAL, &saveSettings, NULL );
+#else
+ _VAR_NOT_USED( ps );
+ alarm(0);
+#endif
+#else
+ if( 0 != ps->lampoff )
+ del_timer( &tl[ps->devno] );
+#endif
+
+ DBG( DBG_HIGH, "Lamp-Timer stopped!\n" );
+}
+
+/** claim and initialize the requested port
+ */
+static int ptdrvOpen( pScanData ps, int portBase )
+{
+ int retval;
+
+ DBG( DBG_HIGH, "ptdrvOpen(port=0x%x)\n", (int32_t)portBase );
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ /*
+ * claim port resources...
+ */
+ retval = MiscClaimPort(ps);
+
+ if( _OK != retval )
+ return retval;
+
+ return MiscInitPorts( ps, portBase );
+}
+
+/** free used memory (if necessary)
+ * restore the parallel port settings and release the port
+ */
+static int ptdrvClose( pScanData ps )
+{
+ DBG( DBG_HIGH, "ptdrvClose()\n" );
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ /*
+ * should be cleared by ioctl(close)
+ */
+ if ( NULL != ps->driverbuf ) {
+ DBG( DBG_LOW, "*** cleanup buffers ***\n" );
+ _VFREE( ps->driverbuf );
+ ps->driverbuf = NULL;
+ }
+
+ if ( NULL != ps->Shade.pHilight ) {
+ _VFREE( ps->Shade.pHilight );
+ ps->Shade.pHilight = NULL;
+ }
+
+ /*
+ * restore/release port resources...
+ */
+ MiscRestorePort( ps );
+ MiscReleasePort( ps );
+
+ return _OK;
+}
+
+/** will be called during OPEN_DEVICE ioctl call
+ */
+static int ptdrvOpenDevice( pScanData ps )
+{
+ int retval, iobase;
+ UShort asic;
+ UChar lastStat;
+ UShort lastMode;
+ ULong devno;
+
+#ifdef __KERNEL__
+ UShort flags;
+ struct pardevice *pd;
+ struct parport *pp;
+ ProcDirDef procDir;
+#else
+ int pd;
+#endif
+
+ /*
+ * push some values from the struct
+ */
+#ifdef __KERNEL__
+ flags = ps->flags;
+ pp = ps->pp;
+ procDir = ps->procDir;
+#endif
+ pd = ps->pardev;
+ iobase = ps->sCaps.wIOBase;
+ asic = ps->sCaps.AsicID;
+ lastStat = ps->bLastLampStatus;
+ lastMode = ps->IO.lastPortMode;
+ devno = ps->devno;
+
+ /*
+ * reinit the show
+ */
+ ptdrvStopLampTimer( ps );
+ MiscReinitStruct ( ps );
+
+ /*
+ * pop the val(s)
+ */
+#ifdef __KERNEL__
+ ps->flags = flags;
+ ps->pp = pp;
+ ps->procDir = procDir;
+#endif
+ ps->pardev = pd;
+ ps->bLastLampStatus = lastStat;
+ ps->IO.lastPortMode = lastMode;
+ ps->devno = devno;
+
+#ifdef __KERNEL__
+ if( _TRUE == slowIO[devno] ) {
+ DBG( DBG_LOW, "Using slow I/O\n" );
+ ps->IO.slowIO = _TRUE;
+ ps->IO.fnOut = IOOutDelayed;
+ ps->IO.fnIn = IOInDelayed;
+ } else {
+ DBG( DBG_LOW, "Using fast I/O\n" );
+ ps->IO.slowIO = _FALSE;
+ ps->IO.fnOut = IOOut;
+ ps->IO.fnIn = IOIn;
+ }
+#endif
+ ps->ModelOverride = mov[devno];
+ ps->warmup = warmup[devno];
+ ps->lampoff = lampoff[devno];
+ ps->lOffonEnd = lOffonEnd[devno];
+ ps->IO.forceMode = forceMode[devno];
+
+ /*
+ * try to find scanner again
+ */
+ retval = ptdrvOpen( ps, iobase );
+
+ if( _OK == retval )
+ retval = DetectScanner( ps, asic );
+ else
+ ptdrvStartLampTimer( ps );
+
+ return retval;
+}
+
+/*.............................................................................
+ * initialize the driver
+ * allocate memory for the ScanData structure and do some presets
+ */
+static int ptdrvInit( int devno )
+{
+ int retval;
+ pScanData ps;
+
+ DBG( DBG_HIGH, "ptdrvInit(%u)\n", devno );
+
+ if( devno >= _MAX_PTDEVS )
+ return _E_NO_DEV;
+
+ /*
+ * allocate memory for our large ScanData-structure
+ */
+ ps = MiscAllocAndInitStruct();
+ if( NULL == ps ) {
+ return _E_ALLOC;
+ }
+
+#ifdef __KERNEL__
+ if( _TRUE == slowIO[devno] ) {
+ DBG( DBG_LOW, "Using slow I/O\n" );
+ ps->IO.slowIO = _TRUE;
+ ps->IO.fnOut = IOOutDelayed;
+ ps->IO.fnIn = IOInDelayed;
+ } else {
+ DBG( DBG_LOW, "Using fast I/O\n" );
+ ps->IO.slowIO = _FALSE;
+ ps->IO.fnOut = IOOut;
+ ps->IO.fnIn = IOIn;
+ }
+#endif
+ ps->ModelOverride = mov[devno];
+ ps->warmup = warmup[devno];
+ ps->lampoff = lampoff[devno];
+ ps->lOffonEnd = lOffonEnd[devno];
+ ps->IO.forceMode = forceMode[devno];
+ ps->devno = devno;
+
+ /* assign it right here, to allow correct shutdown */
+ PtDrvDevices[devno] = ps;
+
+ /*
+ * try to register the port
+ */
+ retval = MiscRegisterPort( ps, port[devno] );
+
+ if( _OK == retval ) {
+ retval = ptdrvOpen( ps, port[devno] );
+ }
+
+ /*
+ * try to detect a scanner...
+ */
+ if( _OK == retval ) {
+ retval = DetectScanner( ps, 0 );
+
+ /* do this here before releasing the port */
+ if( _OK == retval ) {
+ ptDrvSwitchLampOn( ps );
+ }
+ ptdrvClose( ps );
+ }
+
+ if( _OK == retval ) {
+
+#ifdef __KERNEL__
+ _PRINT( "pt_drv%u: %s found on port 0x%04x\n",
+ devno, MiscGetModelName(ps->sCaps.Model), ps->IO.pbSppDataPort );
+#else
+ DBG( DBG_LOW, "pt_drv%u: %s found\n",
+ devno, MiscGetModelName(ps->sCaps.Model));
+#endif
+
+ /*
+ * initialize the timespan timer
+ */
+ MiscStartTimer( &toTimer[ps->devno], (_SECOND * ps->warmup));
+
+ if( 0 == ps->lampoff )
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_LOW,
+#endif
+ "pt_drv%u: Lamp-Timer switched off.\n", devno );
+ else {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_LOW,
+#endif
+ "pt_drv%u: Lamp-Timer set to %u seconds.\n",
+ devno, ps->lampoff );
+ }
+
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_LOW,
+#endif
+ "pt_drv%u: WarmUp period set to %u seconds.\n",
+ devno, ps->warmup );
+
+ if( 0 == ps->lOffonEnd ) {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_LOW,
+#endif
+ "pt_drv%u: Lamp untouched on driver unload.\n", devno );
+ } else {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_LOW,
+#endif
+ "pt_drv%u: Lamp switch-off on driver unload.\n", devno );
+ }
+
+ ptdrvStartLampTimer( ps );
+ }
+
+ return retval;
+}
+
+/*.............................................................................
+ * shutdown the driver:
+ * switch the lights out
+ * stop the motor
+ * free memory
+ */
+static int ptdrvShutdown( pScanData ps )
+{
+ int devno;
+
+ DBG( DBG_HIGH, "ptdrvShutdown()\n" );
+
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ devno = ps->devno;
+
+ DBG( DBG_HIGH, "cleanup device %u\n", devno );
+
+ if( _NO_BASE != ps->sCaps.wIOBase ) {
+
+ ptdrvStopLampTimer( ps );
+
+ if( _OK == MiscClaimPort(ps)) {
+
+ ps->PutToIdleMode( ps );
+
+ if( 0 != ps->lOffonEnd ) {
+ if( _IS_ASIC98(ps->sCaps.AsicID)) {
+ ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMPS_ON;
+ } else {
+ ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMP_ON;
+ }
+ IOCmdRegisterToScanner( ps, ps->RegScanControl,
+ ps->AsicReg.RD_ScanControl );
+ }
+ }
+ MiscReleasePort( ps );
+ }
+
+ /* unregister the driver
+ */
+ MiscUnregisterPort( ps );
+
+ _KFREE( ps );
+ if( devno < _MAX_PTDEVS )
+ PtDrvDevices[devno] = NULL;
+
+ return _OK;
+}
+
+/*.............................................................................
+ * the IOCTL interface
+ */
+static int ptdrvIoctl( pScanData ps, UInt cmd, pVoid arg )
+{
+ UShort dir;
+ UShort version;
+ UInt size;
+ ULong argVal;
+ int cancel;
+ int retval;
+
+ /*
+ * do the preliminary stuff here
+ */
+ if( NULL == ps )
+ return _E_NULLPTR;
+
+ retval = _OK;
+
+ dir = _IOC_DIR(cmd);
+ size = _IOC_SIZE(cmd);
+
+ if ((_IOC_WRITE == dir) && size && (size <= sizeof(ULong))) {
+
+ if (( retval = getUserPtr( arg, &argVal, size))) {
+ DBG( DBG_HIGH, "ioctl() failed - result = %i\n", retval );
+ return retval;
+ }
+ }
+
+ switch( cmd ) {
+
+ /* open */
+ case _PTDRV_OPEN_DEVICE:
+ DBG( DBG_LOW, "ioctl(_PTDRV_OPEN_DEVICE)\n" );
+ if (copy_from_user(&version, arg, sizeof(UShort)))
+ return _E_FAULT;
+
+ if( _PTDRV_IOCTL_VERSION != version ) {
+ DBG( DBG_HIGH, "Version mismatch: Backend=0x%04X(0x%04X)",
+ version, _PTDRV_IOCTL_VERSION );
+ return _E_VERSION;
+ }
+
+ retval = ptdrvOpenDevice( ps );
+ break;
+
+ /* close */
+ case _PTDRV_CLOSE_DEVICE:
+ DBG( DBG_LOW, "ioctl(_PTDRV_CLOSE_DEVICE)\n" );
+
+ if ( NULL != ps->driverbuf ) {
+ DBG( DBG_LOW, "*** cleanup buffers ***\n" );
+ _VFREE( ps->driverbuf );
+ ps->driverbuf = NULL;
+ }
+
+ if ( NULL != ps->Shade.pHilight ) {
+ _VFREE( ps->Shade.pHilight );
+ ps->Shade.pHilight = NULL;
+ }
+
+ ps->PutToIdleMode( ps );
+ ptdrvStartLampTimer( ps );
+ break;
+
+ /* get caps - no scanner connection necessary */
+ case _PTDRV_GET_CAPABILITIES:
+ DBG( DBG_LOW, "ioctl(_PTDRV_GET_CAPABILITES)\n" );
+
+ return putUserPtr( &ps->sCaps, arg, size);
+ break;
+
+ /* get lens-info - no scanner connection necessary */
+ case _PTDRV_GET_LENSINFO:
+ DBG( DBG_LOW, "ioctl(_PTDRV_GET_LENSINFO)\n" );
+
+ return putUserPtr( &ps->LensInf, arg, size);
+ break;
+
+ /* put the image info - no scanner connection necessary */
+ case _PTDRV_PUT_IMAGEINFO:
+ {
+ short tmpcx, tmpcy;
+ ImgDef img;
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_PUT_IMAGEINFO)\n" );
+ if (copy_from_user( &img, (pImgDef)arg, size))
+ return _E_FAULT;
+
+ tmpcx = (short)img.crArea.cx;
+ tmpcy = (short)img.crArea.cy;
+
+ if(( 0 >= tmpcx ) || ( 0 >= tmpcy )) {
+ DBG( DBG_LOW, "CX or CY <= 0!!\n" );
+ return _E_INVALID;
+ }
+
+ _ASSERT( ps->GetImageInfo );
+ ps->GetImageInfo( ps, &img );
+ }
+ break;
+
+ /* get crop area - no scanner connection necessary */
+ case _PTDRV_GET_CROPINFO:
+ {
+ CropInfo outBuffer;
+ pCropInfo pcInf = &outBuffer;
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_GET_CROPINFO)\n" );
+
+ memset( pcInf, 0, sizeof(CropInfo));
+
+ pcInf->dwPixelsPerLine = ps->DataInf.dwAppPixelsPerLine;
+ pcInf->dwBytesPerLine = ps->DataInf.dwAppBytesPerLine;
+ pcInf->dwLinesPerArea = ps->DataInf.dwAppLinesPerArea;
+ return putUserPtr( pcInf, arg, size );
+ }
+ break;
+
+ /* adjust the driver settings */
+ case _PTDRV_ADJUST:
+ {
+ PPAdjDef adj;
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_ADJUST)\n" );
+
+ if (copy_from_user(&adj, (pPPAdjDef)arg, sizeof(PPAdjDef)))
+ return _E_FAULT;
+
+ DBG( DBG_LOW, "Adjusting device %u\n", ps->devno );
+ DBG( DBG_LOW, "warmup: %i\n", adj.warmup );
+ DBG( DBG_LOW, "lampOff: %i\n", adj.lampOff );
+ DBG( DBG_LOW, "lampOffOnEnd: %i\n", adj.lampOffOnEnd );
+
+ if( ps->devno < _MAX_PTDEVS ) {
+
+ if( adj.warmup >= 0 ) {
+ warmup[ps->devno] = adj.warmup;
+ ps->warmup = adj.warmup;
+ }
+
+ if( adj.lampOff >= 0 ) {
+ lampoff[ps->devno] = adj.lampOff;
+ ps->lampoff = adj.lampOff;
+ }
+
+ if( adj.lampOffOnEnd >= 0 ) {
+ lOffonEnd[ps->devno] = adj.lampOffOnEnd;
+ ps->lOffonEnd = adj.lampOffOnEnd;
+ }
+ }
+ }
+ break;
+
+ /* set a specific map (r,g,b or gray) */
+ case _PTDRV_SETMAP:
+ {
+ int i, x_len;
+ MapDef map;
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_SETMAP)\n" );
+
+ if (copy_from_user( &map, (pMapDef)arg, sizeof(MapDef)))
+ return _E_FAULT;
+
+ DBG( DBG_LOW, "maplen=%u, mapid=%u, addr=0x%08lx\n",
+ map.len, map.map_id, (u_long)map.map );
+
+ x_len = 256;
+ if( _IS_ASIC98(ps->sCaps.AsicID))
+ x_len = 4096;
+
+ /* check for 0 pointer and len */
+ if((NULL == map.map) || (x_len != map.len)) {
+ DBG( DBG_LOW, "map pointer == 0, or map len invalid!!\n" );
+ return _E_INVALID;
+ }
+
+ if( _MAP_MASTER == map.map_id ) {
+
+ for( i = 0; i < 3; i++ ) {
+ if (copy_from_user((pVoid)&ps->a_bMapTable[x_len * i],
+ map.map, x_len )) {
+ return _E_FAULT;
+ }
+ }
+ } else {
+
+ u_long idx = 0;
+ if( map.map_id == _MAP_GREEN )
+ idx = 1;
+ if( map.map_id == _MAP_BLUE )
+ idx = 2;
+
+ if (copy_from_user((pVoid)&ps->a_bMapTable[x_len * idx],
+ map.map, x_len )) {
+ return _E_FAULT;
+ }
+ }
+
+ /* here we adjust the maps according to
+ * the brightness and contrast settings
+ */
+ MapAdjust( ps, map.map_id );
+ }
+ break;
+
+ /* set environment - no scanner connection necessary */
+ case _PTDRV_SET_ENV:
+ {
+ ScanInfo sInf;
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_SET_ENV)\n" );
+
+ if (copy_from_user(&sInf, (pScanInfo)arg, sizeof(ScanInfo)))
+ return _E_FAULT;
+
+ /*
+ * to make the OpticPro 4800P work, we need to invert the
+ * Inverse flag
+ */
+ if( _ASIC_IS_96001 == ps->sCaps.AsicID ) {
+ if( SCANDEF_Inverse & sInf.ImgDef.dwFlag )
+ sInf.ImgDef.dwFlag &= ~SCANDEF_Inverse;
+ else
+ sInf.ImgDef.dwFlag |= SCANDEF_Inverse;
+ }
+
+ _ASSERT( ps->SetupScanSettings );
+ retval = ps->SetupScanSettings( ps, &sInf );
+
+ /* CHANGE preset map here */
+ if( _OK == retval ) {
+ MapInitialize ( ps );
+ MapSetupDither( ps );
+
+ ps->DataInf.dwVxdFlag |= _VF_ENVIRONMENT_READY;
+
+ if (copy_to_user((pScanInfo)arg, &sInf, sizeof(ScanInfo)))
+ return _E_FAULT;
+ }
+ }
+ break;
+
+ /* start scan */
+ case _PTDRV_START_SCAN:
+ {
+ StartScan outBuffer;
+ pStartScan pstart = (pStartScan)&outBuffer;
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_START_SCAN)\n" );
+
+ retval = IOIsReadyForScan( ps );
+ if( _OK == retval ) {
+
+ ps->dwDitherIndex = 0;
+ ps->fScanningStatus = _TRUE;
+ pstart->dwBytesPerLine = ps->DataInf.dwAppBytesPerLine;
+ pstart->dwLinesPerScan = ps->DataInf.dwAppLinesPerArea;
+ pstart->dwFlag = ps->DataInf.dwScanFlag;
+
+ ps->DataInf.dwVxdFlag |= _VF_FIRSTSCANLINE;
+ ps->DataInf.dwScanFlag&=~(_SCANNER_SCANNING|_SCANNER_PAPEROUT);
+
+ if (copy_to_user((pStartScan)arg, pstart, sizeof(StartScan)))
+ return _E_FAULT;
+ }
+ }
+ break;
+
+ /* stop scan */
+ case _PTDRV_STOP_SCAN:
+
+ DBG( DBG_LOW, "ioctl(_PTDRV_STOP_SCAN)\n" );
+
+ if (copy_from_user(&cancel, arg, sizeof(short)))
+ return _E_FAULT;
+
+ /* we may use this to abort scanning! */
+ ps->fScanningStatus = _FALSE;
+
+ /* when using this to cancel, then that's all */
+ if( _FALSE == cancel ) {
+
+ MotorToHomePosition( ps );
+
+ ps->DataInf.dwAppLinesPerArea = 0;
+ ps->DataInf.dwScanFlag &= ~_SCANNER_SCANNING;
+
+ /* if environment was never set */
+ if (!(ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY))
+ retval = _E_SEQUENCE;
+
+ ps->DataInf.dwVxdFlag &= ~_VF_ENVIRONMENT_READY;
+
+ } else {
+ DBG( DBG_LOW, "CANCEL Mode set\n" );
+ }
+ retval = putUserVal(retval, arg, size);
+ break;
+
+ /* read the flag status register, when reading the action button, you must
+ * only do this call and none of the other ioctl's
+ * like open, etc or it will always show up as "1"
+ */
+ case _PTDRV_ACTION_BUTTON:
+ DBG( DBG_LOW, "ioctl(_PTDRV_ACTION_BUTTON)\n" );
+ IODataRegisterFromScanner( ps, ps->RegStatus );
+ retval = putUserVal( argVal, arg, size );
+ break;
+
+ default:
+ retval = _E_NOSUPP;
+ break;
+ }
+
+ return retval;
+}
+
+/*.............................................................................
+ * read the data
+ */
+static int ptdrvRead( pScanData ps, pUChar buffer, int count )
+{
+ pUChar scaleBuf;
+ ULong dwLinesRead = 0;
+ int retval = _OK;
+
+#ifdef _ASIC_98001_SIM
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_LOW,
+#endif
+ "pt_drv : Software-Emulation active, can't read!\n" );
+ return _E_INVALID;
+#endif
+
+ if((NULL == buffer) || (NULL == ps)) {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv : Internal NULL-pointer!\n" );
+ return _E_NULLPTR;
+ }
+
+ if( 0 == count ) {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv%u: reading 0 bytes makes no sense!\n", ps->devno );
+ return _E_INVALID;
+ }
+
+ if( _FALSE == ps->fScanningStatus )
+ return _E_ABORT;
+
+ /*
+ * has the environment been set ?
+ * this should prevent the driver from causing a seg-fault
+ * when using the cat /dev/pt_drv command!
+ */
+ if (!(ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY)) {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv%u: Cannot read, driver not initialized!\n",ps->devno);
+ return _E_SEQUENCE;
+ }
+
+ /*
+ * get some memory
+ */
+ ps->Scan.bp.pMonoBuf = _KALLOC( ps->DataInf.dwAppPhyBytesPerLine, GFP_KERNEL);
+
+ if ( NULL == ps->Scan.bp.pMonoBuf ) {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv%u: Not enough memory available!\n", ps->devno );
+ return _E_ALLOC;
+ }
+
+ /* if we have to do some scaling, we need another buffer... */
+ if( ps->DataInf.XYRatio > 1000 ) {
+
+ scaleBuf = _KALLOC( ps->DataInf.dwAppPhyBytesPerLine, GFP_KERNEL);
+ if ( NULL == scaleBuf ) {
+ _KFREE( ps->Scan.bp.pMonoBuf );
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv%u: Not enough memory available!\n", ps->devno );
+ return _E_ALLOC;
+ }
+ } else {
+ scaleBuf = NULL;
+ }
+
+ DBG( DBG_LOW, "PtDrvRead(%u bytes)*****************\n", count );
+ DBG( DBG_LOW, "MonoBuf = 0x%08lx[%u], scaleBuf = 0x%lx\n",
+ (unsigned long)ps->Scan.bp.pMonoBuf,
+ ps->DataInf.dwAppPhyBytesPerLine, (unsigned long)scaleBuf );
+
+ /*
+ * in case of a previous problem, move the sensor back home
+ */
+ MotorToHomePosition( ps );
+
+ if( _FALSE == ps->fScanningStatus ) {
+ retval = _E_ABORT;
+ goto ReadFinished;
+ }
+
+ dwLinesRead = 0;
+
+ /*
+ * first of all calibrate the show
+ */
+ ps->bMoveDataOutFlag = _DataInNormalState;
+ ps->fHalfStepTableFlag = _FALSE;
+ ps->fReshaded = _FALSE;
+ ps->fScanningStatus = _TRUE;
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ ps->Scan.fRefreshState = _FALSE;
+ else
+ ps->Scan.fRefreshState = _TRUE;
+
+ ptdrvLampWarmup( ps );
+
+ if( _FALSE == ps->fScanningStatus ) {
+ retval = _E_ABORT;
+ goto ReadFinished;
+ }
+
+ retval = ps->Calibration( ps );
+ if( _OK != retval ) {
+#ifdef __KERNEL__
+ _PRINT(
+#else
+ DBG( DBG_HIGH,
+#endif
+ "pt_drv%u: calibration failed, result = %i\n",
+ ps->devno, retval );
+ goto ReadFinished;
+ }
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
+
+ ps->OpenScanPath( ps );
+
+ MotorP98003ForceToLeaveHomePos( ps );
+ }
+
+ _ASSERT(ps->SetupScanningCondition);
+ ps->SetupScanningCondition(ps);
+
+ if( _ASIC_IS_98003 != ps->sCaps.AsicID ) {
+ ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE );
+ IOSetToMotorRegister( ps );
+ } else {
+
+ ps->WaitForPositionY( ps );
+ _DODELAY( 70 );
+ ps->Scan.bOldScanState = IOGetScanState( ps, _TRUE ) & _SCANSTATE_MASK;
+ }
+
+ ps->DataInf.dwScanFlag |= _SCANNER_SCANNING;
+
+ if( _FALSE == ps->fScanningStatus ) {
+ DBG( DBG_HIGH, "read aborted!\n" );
+ retval = _E_ABORT;
+ goto ReadFinished;
+ }
+
+ /*
+ * now get the picture data
+ */
+ DBG( DBG_HIGH, "dwAppLinesPerArea = %d\n", ps->DataInf.dwAppLinesPerArea);
+ DBG( DBG_HIGH, "dwAppBytesPerLine = %d\n", ps->DataInf.dwAppBytesPerLine);
+
+/* HEINER: A3I
+ ps->bMoveDataOutFlag = _DataFromStopState;
+*/
+ if ( 0 != ps->DataInf.dwAppLinesPerArea ) {
+
+ ps->Scan.dwLinesToRead = count / ps->DataInf.dwAppBytesPerLine;
+
+ if( ps->Scan.dwLinesToRead ) {
+
+ DBG( DBG_HIGH, "dwLinesToRead = %d\n", ps->Scan.dwLinesToRead );
+
+ if( ps->Scan.dwLinesToRead > ps->DataInf.dwAppLinesPerArea )
+ ps->Scan.dwLinesToRead = ps->DataInf.dwAppLinesPerArea;
+
+ ps->DataInf.dwAppLinesPerArea -= ps->Scan.dwLinesToRead;
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle)
+ buffer += ((ps->Scan.dwLinesToRead - 1) *
+ ps->DataInf.dwAppBytesPerLine);
+
+ if (ps->DataInf.dwVxdFlag & _VF_DATATOUSERBUFFER)
+ ps->DataInf.pCurrentBuffer = ps->Scan.bp.pMonoBuf;
+
+ while(ps->fScanningStatus && ps->Scan.dwLinesToRead) {
+
+ _ASSERT(ps->ReadOneImageLine);
+ if (!ps->ReadOneImageLine(ps)) {
+ ps->fScanningStatus = _FALSE;
+ DBG( DBG_HIGH, "ReadOneImageLine() failed at line %u!\n",
+ dwLinesRead );
+ break;
+ }
+
+ /*
+ * as we might scan images that exceed the CCD-capabilities
+ * in x-resolution, we have to enlarge the line data
+ * i.e.: scanning at 1200dpi generates on a P9636 600 dpi in
+ * x-direction but 1200dpi in y-direction...
+ */
+ if( NULL != scaleBuf ) {
+ ScaleX( ps, ps->Scan.bp.pMonoBuf, scaleBuf );
+ if (copy_to_user( buffer, scaleBuf,
+ ps->DataInf.dwAppPhyBytesPerLine)) {
+ return _E_FAULT;
+ }
+ } else {
+ if (copy_to_user( buffer, ps->Scan.bp.pMonoBuf,
+ ps->DataInf.dwAppPhyBytesPerLine)) {
+ return _E_FAULT;
+ }
+ }
+
+ buffer += ps->Scan.lBufferAdjust;
+ dwLinesRead++;
+ ps->Scan.dwLinesToRead--;
+
+ /* needed, esp. to avoid freezing the system in SPP mode */
+#ifdef __KERNEL__
+ schedule();
+/*#else
+ sched_yield();
+*/
+#endif
+ }
+
+ if (ps->fScanningStatus) {
+
+ if( _IS_ASIC96(ps->sCaps.AsicID))
+ MotorP96SetSpeedToStopProc(ps);
+
+ } else {
+ if (ps->DataInf.dwScanFlag & (SCANDEF_StopWhenPaperOut |
+ SCANDEF_UnlimitLength)) {
+ ps->DataInf.dwAppLinesPerArea = 0;
+ } else {
+ if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle)
+ buffer -= (ps->DataInf.dwAppBytesPerLine *
+ (ps->Scan.dwLinesToRead - 1));
+ memset( buffer, 0xff,
+ ps->Scan.dwLinesToRead * ps->DataInf.dwAppBytesPerLine );
+ dwLinesRead += ps->Scan.dwLinesToRead;
+ }
+ }
+
+ } else {
+ retval = _E_INTERNAL;
+ }
+ }
+
+ if( _FALSE == ps->fScanningStatus ) {
+ DBG( DBG_HIGH, "read aborted!\n" );
+ retval = _E_ABORT;
+ }
+
+ReadFinished:
+
+
+ if( _ASIC_IS_98003 == ps->sCaps.AsicID )
+ ps->CloseScanPath( ps );
+
+ if( NULL != ps->Scan.bp.pMonoBuf )
+ _KFREE( ps->Scan.bp.pMonoBuf );
+
+ if( NULL != scaleBuf )
+ _KFREE( scaleBuf );
+
+ /*
+ * on success return number of bytes red
+ */
+ if ( _OK == retval )
+ return (ps->DataInf.dwAppPhyBytesPerLine * dwLinesRead);
+
+ return retval;
+}
+
+/*************************** the module interface ****************************/
+
+#ifdef __KERNEL__ /* the kernel module interface */
+
+/* Designed to be used as a module */
+#ifdef MODULE
+
+/*.............................................................................
+ * gets called upon module initialization
+ */
+#ifdef LINUX_26
+static int __init ptdrv_init( void )
+#else
+int init_module( void )
+#endif
+{
+ UInt devCount;
+ UInt i;
+ int retval = _OK;
+ int result = _OK;
+#if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE))
+ char controlname[24];
+#endif
+# ifdef LINUX_26
+ char devname[20];
+#endif
+
+ DBG( DBG_HIGH, "*********************************************\n" );
+ DBG( DBG_HIGH, "pt_drv: init_module()\n" );
+
+#if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE))
+ devfs_handle = devfs_mk_dir(NULL, "scanner", NULL);
+ if( devfs_register_chrdev(_PTDRV_MAJOR, _DRV_NAME, &pt_drv_fops)) {
+#else
+ if( register_chrdev(_PTDRV_MAJOR, _DRV_NAME, &pt_drv_fops)) {
+#endif
+
+ _PRINT(KERN_INFO "pt_drv: unable to get major %d for pt_drv devices\n",
+ _PTDRV_MAJOR);
+ return -EIO;
+ }
+ printk( KERN_INFO "pt_drv : driver version "_PTDRV_VERSTR"\n" );
+
+#if !defined (CONFIG_DEVFS_FS) && defined (LINUX_26)
+ ptdrv_class = class_create(THIS_MODULE, "scanner");
+ if (IS_ERR(ptdrv_class))
+ goto out_devfs;
+#endif
+
+ /* register the proc_fs */
+ ProcFsInitialize();
+
+ /* go through the list of defined ports and try to find a device
+ */
+ devCount = 0;
+ for( i = 0; i < _MAX_PTDEVS; i++ ) {
+
+ if( 0 != port[i] ) {
+ result = ptdrvInit( i );
+
+ if ( _OK == result ) {
+ PtDrvDevices[i]->flags |= _PTDRV_INITALIZED;
+
+#ifdef CONFIG_DEVFS_FS
+# ifndef DEVFS_26_STYLE
+ sprintf( controlname, "scanner/pt_drv%d", devCount );
+ devfs_register( NULL, controlname,
+ DEVFS_FL_DEFAULT, _PTDRV_MAJOR, 0,
+ (S_IFCHR | S_IRUGO | S_IWUGO | S_IFCHR),
+ &pt_drv_fops, NULL );
+# else /* DEVFS_26_STYLE */
+ devfs_mk_cdev(MKDEV(_PTDRV_MAJOR, devCount),
+ (S_IFCHR | S_IRUGO | S_IWUGO | S_IFCHR),
+ "scanner/pt_drv%d", devCount);
+# endif
+#else
+# ifdef LINUX_26
+ sprintf(devname, "pt_drv%d", devCount);
+ CLASS_DEV_CREATE(ptdrv_class,
+ MKDEV(_PTDRV_MAJOR, devCount), NULL,
+ devname);
+
+# endif /* LINUX_26 */
+#endif /* CONFIG_DEVFS_FS */
+ ProcFsRegisterDevice( PtDrvDevices[i] );
+ devCount++;
+ } else {
+ retval = result;
+ ptdrvShutdown( PtDrvDevices[i] );
+ PtDrvDevices[i] = NULL;
+ }
+ }
+ }
+
+ /* * if something went wrong, shutdown all... */
+ if( devCount == 0 ) {
+
+#if !defined (CONFIG_DEVFS_FS) && defined (LINUX_26)
+out_devfs:
+ class_destroy(ptdrv_class);
+#endif
+
+#if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE))
+ devfs_unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME );
+#else
+ unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME );
+#endif
+ ProcFsShutdown();
+
+#ifdef __KERNEL__
+ _PRINT( KERN_INFO "pt_drv : no device(s) detected, (%i)\n", retval );
+#endif
+
+ } else {
+
+ DBG( DBG_HIGH, "pt_drv : init done, %u device(s) found\n", devCount );
+ retval = _OK;
+ }
+ DBG( DBG_HIGH, "---------------------------------------------\n" );
+
+ deviceScanning = _FALSE;
+ return retval;
+}
+
+/*.............................................................................
+ * cleanup the show
+ */
+#ifdef LINUX_26
+static void __exit ptdrv_exit( void )
+#else
+void cleanup_module( void )
+#endif
+{
+ UInt i;
+ pScanData ps;
+#if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE))
+ char controlname[24];
+ devfs_handle_t master;
+#endif
+
+ DBG( DBG_HIGH, "pt_drv: cleanup_module()\n" );
+
+ for ( i = 0; i < _MAX_PTDEVS; i++ ) {
+
+ ps = PtDrvDevices[i];
+ PtDrvDevices[i] = NULL;
+
+ if ( NULL != ps ) {
+#ifdef CONFIG_DEVFS_FS
+# ifndef DEVFS_26_STYLE
+ sprintf( controlname, "scanner/pt_drv%d", i );
+ master = devfs_find_handle( NULL,controlname, 0, 0,
+ DEVFS_SPECIAL_CHR, 0 );
+ devfs_unregister( master );
+# else
+ devfs_remove("scanner/pt_drv%d", i);
+# endif
+#else
+# ifdef LINUX_26
+ CLASS_DEV_DESTROY(ptdrv_class, MKDEV(_PTDRV_MAJOR, i));
+# endif /* LINUX_26 */
+#endif /* CONFIG_DEVFS_FS */
+ ptdrvShutdown( ps );
+ ProcFsUnregisterDevice( ps );
+ }
+ }
+
+#if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE))
+ devfs_unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME );
+#else
+ unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME );
+#endif
+ ProcFsShutdown();
+
+#if !defined (CONFIG_DEVFS_FS) && defined (LINUX_26)
+ class_destroy(ptdrv_class);
+#endif
+
+ DBG( DBG_HIGH, "pt_drv: cleanup done.\n" );
+ DBG( DBG_HIGH, "*********************************************\n" );
+}
+
+#ifdef LINUX_26
+module_init(ptdrv_init);
+module_exit(ptdrv_exit);
+#endif
+
+#endif /*MODULE*/
+
+
+/*.............................................................................
+ * device open...
+ */
+static int pt_drv_open(struct inode *inode, struct file *file)
+{
+ pScanData ps;
+
+ DBG( DBG_HIGH, "pt_drv_open()\n" );
+
+ ps = get_pt_from_inode(inode);
+
+ if ( NULL == ps ) {
+ return(-ENXIO);
+ }
+
+ /* device not found ? */
+ if (!(ps->flags & _PTDRV_INITALIZED)) {
+ return(-ENXIO);
+ }
+
+ /* device is busy ? */
+ if (ps->flags & _PTDRV_OPEN) {
+ return(-EBUSY);
+ }
+
+#ifdef LINUX_26
+ if (!try_module_get(THIS_MODULE))
+ return -EAGAIN;
+#else
+ MOD_INC_USE_COUNT;
+#endif
+ ps->flags |= _PTDRV_OPEN;
+
+ return _OK;
+}
+
+/*.............................................................................
+ * device close...
+ */
+static CLOSETYPE pt_drv_close(struct inode * inode, struct file * file)
+{
+ pScanData ps;
+
+ DBG( DBG_HIGH, "pt_drv_close()\n" );
+
+ if ((ps = get_pt_from_inode(inode)) ) {
+
+ ptdrvClose( ps );
+
+ ps->flags &= ~_PTDRV_OPEN;
+#ifdef LINUX_26
+ module_put(THIS_MODULE);
+#else
+ MOD_DEC_USE_COUNT;
+#endif
+ CLOSERETURN(0);
+ } else {
+
+ DBG( DBG_HIGH, "pt_drv: - close failed!\n" );
+ CLOSERETURN(-ENXIO);
+ }
+}
+
+/*.............................................................................
+ * read data from device
+ */
+#ifdef LINUX_20
+static int pt_drv_read(struct inode *inode, struct file *file,
+ char *buffer, int count)
+{
+ int result;
+ pScanData ps;
+
+ if ( !(ps = get_pt_from_inode(inode)))
+ return(-ENXIO);
+#else
+static ssize_t pt_drv_read( struct file *file,
+ char *buffer, size_t count, loff_t *tmp )
+{
+ int result;
+ pScanData ps;
+
+ if ( !(ps = get_pt_from_inode(file->f_dentry->d_inode)) )
+ return(-ENXIO);
+#endif
+ if ((result = verify_area_20(VERIFY_WRITE, buffer, count)))
+ return result;
+
+ /*
+ * as the driver contains some global vars, it is not
+ * possible to scan simultaenously with two or more devices
+ */
+ if( _TRUE == deviceScanning ) {
+ printk( KERN_INFO "pt_drv: device %u busy!!!\n", ps->devno );
+ return(-EBUSY);
+ }
+
+ deviceScanning = _TRUE;
+
+ result = ptdrvRead( ps, buffer, count );
+
+ deviceScanning = _FALSE;
+ return result;
+}
+
+/*.............................................................................
+ * writing makes no sense
+ */
+#ifdef LINUX_20
+static int pt_drv_write(struct inode * inode, struct file * file,
+ const char * buffer, int count)
+{
+ return -EPERM;
+}
+#else
+ static ssize_t pt_drv_write( struct file * file,const char * buffer,
+ size_t tmp,loff_t* count)
+{
+ return -EPERM;
+}
+#endif
+
+/*.............................................................................
+ * the ioctl interface
+ */
+#ifdef NOLOCK_IOCTL
+static long pt_drv_ioctl( struct file *file, UInt cmd, unsigned long arg )
+{
+ pScanData ps;
+
+ if ( !(ps = get_pt_from_inode(file->f_dentry->d_inode)) )
+ return(-ENXIO);
+
+ return ptdrvIoctl( ps, cmd, (pVoid)arg);
+}
+#else
+static int pt_drv_ioctl( struct inode *inode, struct file *file,
+ UInt cmd, unsigned long arg )
+{
+ pScanData ps;
+
+ if ( !(ps = get_pt_from_inode(inode)) )
+ return(-ENXIO);
+
+ return ptdrvIoctl( ps, cmd, (pVoid)arg);
+}
+#endif
+
+#else /* the user-mode interface */
+
+/*.............................................................................
+ * here we only have wrapper functions
+ */
+static int PtDrvInit( const char *dev_name, UShort model_override )
+{
+ int fd;
+ int result = _OK;
+
+ if( _TRUE == PtDrvInitialized )
+ return _OK;
+
+ result = sanei_pp_open( dev_name, &fd );
+ if( SANE_STATUS_GOOD != result )
+ return result;
+
+ port[0] = fd;
+ mov[0] = model_override;
+
+ result = ptdrvInit( 0 );
+
+ if( _OK == result ) {
+ PtDrvInitialized = _TRUE;
+ } else {
+ ptdrvShutdown( PtDrvDevices[0] );
+ }
+
+ return result;
+}
+
+static int PtDrvShutdown( void )
+{
+ int result;
+
+ if( _FALSE == PtDrvInitialized )
+ return _E_NOT_INIT;
+
+ result = ptdrvShutdown( PtDrvDevices[0] );
+
+ PtDrvInitialized = _FALSE;
+
+ return result;
+}
+
+static int PtDrvOpen( void )
+{
+ if( _FALSE == PtDrvInitialized )
+ return _E_NOT_INIT;
+
+ return _OK;
+}
+
+static int PtDrvClose( void )
+{
+ if( _FALSE == PtDrvInitialized )
+ return _E_NOT_INIT;
+
+ return ptdrvClose( PtDrvDevices[0] );
+}
+
+static int PtDrvIoctl( UInt cmd, pVoid arg )
+{
+ if( _FALSE == PtDrvInitialized )
+ return _E_NOT_INIT;
+
+ return ptdrvIoctl( PtDrvDevices[0], cmd, arg);
+}
+
+static int PtDrvRead ( pUChar buffer, int count )
+{
+ if( _FALSE == PtDrvInitialized )
+ return _E_NOT_INIT;
+
+ return ptdrvRead( PtDrvDevices[0], buffer, count );
+}
+
+#endif /* guard __KERNEL__ */
+
+/* END PLUSTEK-PP_PTDRV.C ...................................................*/
diff --git a/backend/plustek-pp_scale.c b/backend/plustek-pp_scale.c
new file mode 100644
index 0000000..295a566
--- /dev/null
+++ b/backend/plustek-pp_scale.c
@@ -0,0 +1,149 @@
+/* @file plustek-pp_scale.c
+ * @brief Scaling functionality
+ *
+ * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.32 - initial version
+ * - 0.33 - no changes
+ * - 0.34 - no changes
+ * - 0.35 - no changes
+ * - 0.36 - no changes
+ * - 0.37 - no changes
+ * - 0.38 - no changes
+ * - 0.39 - no changes
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - cleanup, removed floating point stuff
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/************************ exported functions *********************************/
+
+/** scaling picture data in x-direction, using a DDA algo
+ * (digital differential analyzer).
+ */
+_LOC void ScaleX( pScanData ps, pUChar inBuf, pUChar outBuf )
+{
+ UChar tmp;
+ int step;
+ int izoom;
+ int ddax;
+ register ULong i, j, x;
+
+#ifdef DEBUG
+ _VAR_NOT_USED( dbg_level );
+#endif
+
+ /* scale... */
+ izoom = (int)(1000000/ps->DataInf.XYRatio);
+
+ switch( ps->DataInf.wAppDataType ) {
+
+ case COLOR_BW : step = 0; break;
+ case COLOR_HALFTONE: step = 0; break;
+ case COLOR_256GRAY : step = 1; break;
+ case COLOR_TRUE24 : step = 3; break; /*NOTE: COLOR_TRUE32 is the same !*/
+ case COLOR_TRUE48 : step = 6; break;
+ default : step = 99; break;
+ }
+
+ /* when not supported, only copy the data
+ */
+ if( 99 == step ) {
+ memcpy( outBuf, inBuf, ps->DataInf.dwAppBytesPerLine );
+ return;
+ }
+
+ /* now scale...
+ */
+ ddax = 0;
+ x = 0;
+ if( 0 == step ) {
+
+ /* binary scaling
+ */
+ memset( outBuf, 0, ps->DataInf.dwAppBytesPerLine );
+
+ for( i = 0; i < ps->DataInf.dwPhysBytesPerLine*8; i++ ) {
+
+ ddax -= 1000;
+
+ while( ddax < 0 ) {
+
+ tmp = inBuf[(i>>3)];
+
+ if((x>>3) < ps->DataInf.dwAppBytesPerLine ) {
+ if( 0 != (tmp &= (1 << ((~(i & 0x7))&0x7))))
+ outBuf[x>>3] |= (1 << ((~(x & 0x7))&0x7));
+ }
+ x++;
+ ddax += izoom;
+ }
+ }
+
+ } else {
+
+ /* color and gray scaling
+ */
+ for( i = 0; i < ps->DataInf.dwPhysBytesPerLine*step; i+=step ) {
+
+ ddax -= 1000;
+
+ while( ddax < 0 ) {
+
+ for( j = 0; j < (ULong)step; j++ ) {
+
+ if((x+j) < ps->DataInf.dwAppBytesPerLine ) {
+ outBuf[x+j] = inBuf[i+j];
+ }
+ }
+ x += step;
+ ddax += izoom;
+ }
+ }
+ }
+}
+
+/* END PLUSTEK-PP_SCALE.C ...................................................*/
diff --git a/backend/plustek-pp_scan.h b/backend/plustek-pp_scan.h
new file mode 100644
index 0000000..3b977bf
--- /dev/null
+++ b/backend/plustek-pp_scan.h
@@ -0,0 +1,202 @@
+/* @file plustek-pp_scan.h
+ * @brief the global header for the plustek driver
+ *
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * 0.30 - initial version
+ * 0.31 - no changes
+ * 0.32 - changed _DODELAY macro to work properly for delays > 5 ms
+ * 0.33 - no changes
+ * 0.34 - no changes
+ * 0.35 - removed _PTDRV_PUT_SCANNER_MODEL from ioctl interface
+ * 0.36 - now including plustek-share.h from the backend path
+ * changed _INB/_OUTB to _INB_STATUS, _INB_CTRL, _INB_DATA ...
+ * 0.37 - added _OUTB_ECTL/_INB_ECTL, _MAX_PTDEVS, _DRV_NAME and _MAX_BTNS
+ * added _OUTB_STATUS
+ * 0.38 - added _IS_ASIC96() and _IS_ASIC98() macros
+ * 0.39 - no changes
+ * 0.40 - no changes
+ * 0.41 - no changes
+ * 0.42 - changed include names
+ * 0.43 - no changes
+ * 0.44 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __PLUSTEK_SCAN_H__
+#define __PLUSTEK_SCAN_H__
+
+#ifndef __KERNEL__
+
+# include <stdlib.h>
+# include <stdarg.h>
+# include <string.h>
+# include <stdio.h>
+# include <unistd.h>
+# include <sys/time.h>
+# ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+# else
+# include <signal.h>
+# endif
+# include <sys/ioctl.h>
+# ifdef HAVE_SYS_IO_H
+# include <sys/io.h>
+# endif
+#else
+# include <linux/kernel.h>
+# include <linux/init.h>
+# include <linux/version.h>
+# include "plustek-pp_sysdep.h"
+# include <linux/delay.h>
+# include <linux/parport.h>
+
+#ifdef LINUX_24
+# include <linux/parport_pc.h>
+#endif /* LINUX_24 */
+
+#endif /* __KERNEL__ */
+
+/*.............................................................................
+ * driver properties
+ */
+#define _DRV_NAME "pt_drv" /**< driver's name */
+#define _MAX_PTDEVS 4 /**< support for 4 devices */
+#define _MAX_BTNS 6 /**< support for 6 buttons */
+#define _PTDRV_MAJOR 40 /**< our major number */
+
+/*.............................................................................
+ * for port operations
+ */
+# define _OPF ps->IO.fnOut
+# define _IPF ps->IO.fnIn
+
+#ifdef __KERNEL__
+
+#define _OUTB_CTRL(pSD,port_value) _OPF(port_value,pSD->IO.pbControlPort)
+#define _OUTB_DATA(pSD,port_value) _OPF(port_value,pSD->IO.pbSppDataPort)
+#define _OUTB_ECTL(pSD,port_value) _OPF(port_value,(pSD->IO.portBase+0x402))
+
+#define _INB_CTRL(pSD) _IPF(pSD->IO.pbControlPort)
+#define _INB_DATA(pSD) _IPF(pSD->IO.pbSppDataPort)
+#define _INB_EPPDATA(pSD) _IPF(pSD->IO.pbEppDataPort)
+#define _INB_STATUS(pSD) _IPF(pSD->IO.pbStatusPort)
+#define _INB_ECTL(pSD) _IPF((pSD->IO.portBase+0x402))
+
+#else
+
+#define _OUTB_CTRL(pSD,port_value) sanei_pp_outb_ctrl(pSD->pardev, port_value)
+#define _OUTB_DATA(pSD,port_value) sanei_pp_outb_data(pSD->pardev, port_value)
+#define _OUTB_ECTL(pSD,port_value)
+
+#define _INB_CTRL(pSD) sanei_pp_inb_ctrl(pSD->pardev)
+#define _INB_DATA(pSD) sanei_pp_inb_data(pSD->pardev)
+#define _INB_EPPDATA(pSD) sanei_pp_inb_epp(pSD->pardev)
+#define _INB_STATUS(pSD) sanei_pp_inb_stat(pSD->pardev)
+
+#endif
+
+/*.............................................................................
+ * for memory allocation
+ */
+#ifndef __KERNEL__
+# define _KALLOC(x,y) malloc(x)
+# define _KFREE(x) free(x)
+# define _VMALLOC(x) malloc(x)
+# define _VFREE(x) free(x)
+#else
+# define _KALLOC(x,y) kmalloc(x,y)
+# define _KFREE(x) kfree(x)
+# define _VMALLOC(x) vmalloc(x)
+# define _VFREE(x) vfree(x)
+#endif
+
+/*
+ * WARNING - never use the _SECOND define with the _DODELAY macro !!
+ * they are for use the the MiscStartTimer function and the _DO_UDELAY macro
+ */
+#ifndef __KERNEL__
+typedef double TimerDef, *pTimerDef;
+#else
+typedef long long TimerDef, *pTimerDef;
+#endif
+
+#define _MSECOND 1000 /* based on 1 us */
+#define _SECOND (1000*_MSECOND)
+
+/*.............................................................................
+ * timer topics
+ */
+#ifndef __KERNEL__
+# define _DO_UDELAY(usecs) sanei_pp_udelay(usecs)
+# define _DODELAY(msecs) { int i; for( i = msecs; i--; ) _DO_UDELAY(1000); }
+#else
+# define _DO_UDELAY(usecs) udelay(usecs)
+# define _DODELAY(msecs) mdelay(msecs)
+#endif
+
+/*.............................................................................
+ * include the shared stuff right here, this concerns the ioctl interface
+ * and the communication stuff
+ */
+#include "plustek-pp.h"
+
+/*.............................................................................
+ * WARNING: don't move the following headers above the previous defines !!!!!!!
+ *
+ * the include files for user-mode and kernel-mode program
+ */
+#include "plustek-pp_types.h"
+#include "plustek-pp_hwdefs.h"
+#include "plustek-pp_scandata.h"
+#include "plustek-pp_procs.h"
+#include "plustek-pp_dbg.h"
+
+/*.............................................................................
+ * some macros for convenience
+ */
+#define _IS_ASIC96(aid) ((_ASIC_IS_96001 == aid) || (_ASIC_IS_96003 == aid))
+#define _IS_ASIC98(aid) ((_ASIC_IS_98001 == aid) || (_ASIC_IS_98003 == aid))
+
+#endif /* guard __PLUSTEK_SCAN_H__ */
+
+/* END PLUSTEK-PP_SCAN.H ....................................................*/
diff --git a/backend/plustek-pp_scandata.h b/backend/plustek-pp_scandata.h
new file mode 100644
index 0000000..1ba2c05
--- /dev/null
+++ b/backend/plustek-pp_scandata.h
@@ -0,0 +1,642 @@
+/* @file plustek-pp_scandata.h
+ * @brief here we define the ScanData structure...
+ * and a lot of register settings
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson <rick@efn.org>
+ *.............................................................................
+ * History:
+ * 0.30 - initial version
+ * 0.31 - no changes
+ * 0.32 - added fWarmupNeeded to struct ScanData
+ * - removed function FillDataToColorTable from struct ScanData
+ * - removed dwLampDelay from struct ScanData
+ * 0.33 - cosmetic changes
+ * - removed PositionLamp from structure
+ * - added dwLastPortMode to struct ScanData
+ * 0.34 - removed WaitBack() function from pScanData structure
+ * - removed wStayMaxStep from pScanData structure
+ * 0.35 - removed SetInitialGainRAM from pScanData structure
+ * - changed ModelStr list
+ * 0.36 - added some defines for the ASIC 96001 (model 4800)
+ * - added wDither to DataInfo structure
+ * - removed dwPreferSize from struct ScannerCaps
+ * - cleanup
+ * - moved all stuff that is used by the backend and the driver
+ * to plustek-share.h which is in the backend directory
+ * - added ModelOverride parameter to struct
+ * - added strcut pardevice to struct
+ * 0.37 - added bIODelay for SPP/BIDI port operations
+ * - added ReadData to struct
+ * - added ProcDirDef
+ * - added ButtonCount
+ * - removed RegisterToScanner from struct
+ * - removed MaxDpiByInterpolation from struct
+ * 0.38 - added function PutToIdleMode() to struct
+ * - added function Calibration() to struct
+ * - changed interface of the ReInitAsic() function
+ * - major changes: moved a lot of stuff to hwdefs.h
+ * - added IO, Device, Shade, Scan and Bufs to struct
+ * 0.39 - added forceMode to struct
+ * - added f97003, b97003DarkR, b97003DarkB, b97003DarkG to struct
+ * 0.40 - no changes
+ * 0.41 - no changes
+ * 0.42 - no changes
+ * 0.43 - changed type of XYRatio from double to long
+ * - cleanup
+ * 0.44 - changes as Long defaults now to int32_t
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __SCANDATA_H__
+#define __SCANDATA_H__
+
+/*
+ *Directory information for the /proc interface
+ */
+typedef struct {
+ struct proc_dir_entry *entry; /* Directory /proc/pt_drv/X */
+ struct proc_dir_entry *info; /* .../info */
+ struct proc_dir_entry *buttons[_MAX_BTNS]; /* .../buttons */
+} ProcDirDef, *pProcDirDef;
+
+/*
+ * here we have some structs internally used
+ */
+typedef struct {
+ ULong dwVxdFlag;
+ ULong dwScanFlag;
+
+/*
+ * CHECK: why there are dups ?
+ */
+ ULong dwAppLinesPerArea;
+ ULong dwAppPixelsPerLine;
+ ULong dwAppPhyBytesPerLine;
+ ULong dwAppBytesPerLine;
+ ULong dwAsicPixelsPerPlane;
+ ULong dwAsicBytesPerPlane;
+ ULong dwAsicBytesPerLine;
+ CropRect crImage;
+ XY xyAppDpi;
+ XY xyPhyDpi;
+ pUChar pCurrentBuffer;
+ UShort wPhyDataType;
+ UShort wAppDataType;
+ UShort wYSum;
+
+ short siBrightness;
+
+/* CHANGE added these vars for scaling
+ */
+ Long XYRatio;
+ ULong dwPhysBytesPerLine;
+
+/*
+ * CHANGE added this for selecting dither method
+ */
+ UShort wDither;
+
+} DataInfo, *pDataInfo;
+
+
+/*
+ * here it is, the great structure
+ */
+typedef struct scandata
+{
+#ifdef __KERNEL__
+ UInt flags; /* as follows: */
+#define _PTDRV_INITALIZED 0x00000001
+#define _PTDRV_OPEN 0x00000002
+
+ struct pardevice *pardev; /* for accessing parport... */
+ struct parport *pp;
+ ProcDirDef procDir;
+#else
+ int pardev; /* parport handle in user-space */
+#endif
+
+ /*
+ * device control
+ */
+ ULong devno;
+ int lampoff;
+ int warmup;
+ int lOffonEnd;
+
+ /*
+ * CHECK for controlling the ECP-mode (not used now)
+ */
+#if 0
+ Byte bOldECR;
+ Bool fECPReadWriteTest;
+ Bool fSkipEcpFlag;
+ Bool fECPFlag;
+ Bool fECPtoEPP;
+ Bool fECRFIFO;
+#endif
+
+ /*
+ * the following stuff gets changed on a per model basis
+ */
+ UShort ModelOverride; /* for non-auto detection stuff */
+
+ UShort Offset70; /* CHECK: --> to Device */
+ UShort BufferSizeBase; /* --> to Device */
+ UShort BufferSizePerModel; /* --> to Device */
+ UShort TimePerLine; /* --> to Device */
+
+ /*
+ * scanner properties
+ */
+ RegData AsicReg; /* here we have the 98001/3 register set */
+ Reg96 Asic96Reg; /* here we hold the 96001/3 specific regs */
+
+ LensInfo LensInf;
+ ScannerCaps sCaps;
+ ULong dwScannerSize;
+ Byte bCurrentSpeed;
+ pUChar pbMapRed;
+ pUChar pbMapGreen;
+ pUChar pbMapBlue;
+
+ ULong TotalBufferRequire;
+ ULong BufferForColorRunTable;
+ UShort PhysicalDpi;
+ UShort RdPix; /* for ASIC 96003 devices */
+
+ Byte a_bMapTable[4096 * 3]; /* pre 98001 was 256 * 3 */
+ Byte a_nbNewAdrPointer[_SCANSTATE_BYTES];
+
+ /*
+ * for P9600x ASIC based scanners
+ */
+ Bool fColorMoreRedFlag;
+ Bool fColorMoreBlueFlag;
+ Bool fSonyCCD;
+ Bool f97003;
+ Byte AsicRedColor;
+ Byte AsicGreenColor;
+ Byte RedDataReady;
+ Byte GreenDataReady;
+ Byte b1stColorByte;
+ Byte b1stColor;
+ Byte b1stMask;
+ Byte b2ndColorByte;
+ Byte b2ndColor;
+ Byte b2ndMask;
+ Byte b3rdColorByte;
+ Byte b3rdColor;
+ Byte b3rdMask;
+ Byte b1stLinesOffset;
+ Byte b2ndLinesOffset;
+ Byte bLampOn;
+ Byte bExtraAdd;
+ Byte bFifoCount;
+ Byte bMinReadFifo;
+ Byte FullStep;
+ Byte StepMask;
+ Byte MotorOn;
+ Byte MotorFreeRun;
+ Byte IgnorePF;
+ Byte bMotorStepTableNo;
+
+ /* for ASIC 97003... */
+ Byte b97003DarkR;
+ Byte b97003DarkG;
+ Byte b97003DarkB;
+
+/* CHECK: to Scan!!!! */
+ pUChar pGetBufR; /* NOTE: these aren't actually Red/Green buffer */
+ pUChar pGetBufG; /* pointers but instead are used */
+ pUChar pPutBufR; /* generically to point to the first 2 */
+ pUChar pPutBufG; /* color buffers as temp storage */
+
+ pUChar pCurrentColorRunTable;
+ UShort a_wGrayInitTime[3];
+ UShort a_wColorInitTime[3];
+ UShort BackwardSteps;
+ UShort wLinesPer64kTime;
+ UShort ShadingBufferSize;
+ UShort ShadingBankSize;
+ UShort ShadingBankRed;
+ UShort ShadingBankGreen;
+ UShort ShadingBankBlue;
+ UShort ShadingScanLineBlks;
+ UShort ShadingScanLineLen;
+ UShort wOverBlue;
+ UShort FBKScanLineBlks;
+ UShort FBKScanLineLenBase;
+ UShort FBKScanLineLen;
+ UShort OneScanLineLen;
+
+ /*
+ * the DAC part - to Shade !!!
+ */
+ UShort wsDACCompareHighRed, wsDACCompareLowRed;
+ UShort wsDACCompareHighGreen, wsDACCompareLowGreen;
+ UShort wsDACCompareHighBlue, wsDACCompareLowBlue;
+ UShort wsDACOffsetRed, wsDACOffsetGreen, wsDACOffsetBlue;
+ Byte bsPreRedDAC, bsPreGreenDAC, bsPreBlueDAC;
+ Byte bRedDAC, bGreenDAC, bBlueDAC;
+ Byte bRedGainIndex, bGreenGainIndex, bBlueGainIndex;
+
+ /*
+ * for image description
+ */
+ DataInfo DataInf;
+ Bool fReshaded;
+ ULong dwDitherIndex;
+ Bool fDoFilter, fFilterFirstLine;
+ ULong dwDivFilter;
+ ULong dwMul;
+ Byte bOffsetFilter;
+ ULong dwLinesFilter;
+ pUChar pFilterBuf, pEndBuf;
+ pUChar pGet1, pGet2, pGet3;
+
+ Byte bSetScanModeFlag; /* see Section 5 - Scanmodes --> ps->Shade.bIntermediate*/
+
+ /*
+ * some admin vals (they used to be global vars in the original driver)
+ */
+ Bool fScanningStatus;
+ Byte bLastLampStatus;
+ Bool fWarmupNeeded;
+ ULong dwOffset70;
+ ULong dwMaxReadFifoData;
+
+ /*
+ *
+ */
+ pUChar pColorRunTable;
+ pUChar pPrescan16;
+ pUChar pPrescan8;
+ UShort BufferForDataRead1;
+ ULong BufferFor1stColor;
+ ULong BufferFor2ndColor;
+ pUChar driverbuf;
+ pUChar pEndBufR;
+ pUChar pEndBufG;
+ pUChar pProcessingBuf;
+
+ /*
+ * formerly used as global vars in ioproc.c, now in genericio.c
+ */
+ pUChar pScanBuffer1;
+ pUChar pScanBuffer2;
+
+ pModeTypeVar lpEppColorHomePos;
+ pModeTypeVar lpEppColorExposure;
+ pModeTypeVar lpBppColorHomePos;
+ pModeTypeVar lpSppColorHomePos;
+ UShort wMinCmpDpi;
+ pModeTypeVar a_ColorSettings;
+ pDiffModeVar a_tabDiffParam;
+
+ Byte bSpeed48;
+ Byte bSpeed32;
+ Byte bSpeed24;
+ Byte bSpeed16;
+ Byte bSpeed12;
+ Byte bSpeed8;
+ Byte bSpeed6;
+ Byte bSpeed4;
+ Byte bSpeed3;
+ Byte bSpeed2;
+ Byte bSpeed1;
+
+ Byte bHpMotor;
+ Byte bStepSpeed;
+ ULong dwFullStateSpeed;
+
+ /*
+ * reference to globals from motor.c
+ */
+ Bool fHalfStepTableFlag;
+ Bool fFullLength;
+ Byte bMoveDataOutFlag;
+ Byte bExtraMotorCtrl;
+ Byte bFastMoveFlag;
+ Byte bOldStateCount;
+ Byte bMotorSpeedData;
+ Byte bCurrentLineCount;
+ Byte bNewGap;
+ Byte bNewCurrentLineCountGap;
+ UShort wMaxMoveStep;
+ ULong dwScanStateCount;
+ ULong dwColorRunIndex;
+ pByte a_bColorByteTable;
+ pUChar pScanState;
+ pUShort a_wMoveStepTable;
+
+ /*
+ * for shading - dac.c
+ * CHECK: move to ps->Shade
+ */
+ Byte bShadingTimeFlag;
+ ULong dwShadow, dwShadowCh;
+ ULong dwHilight, dwHilightCh;
+ ULong dwShadingLen, dwShadingPixels;
+ pUShort pwShadow;
+
+ /*
+ * from transform.c
+ */
+ Byte bRedHigh, bGreenHigh, bBlueHigh;
+ UShort wPosAdjustX;
+ UShort wNegAdjustX;
+ UShort wReduceRedFactor;
+ UShort wReduceGreenFactor;
+ UShort wReduceBlueFactor;
+ ULong dwOffsetNegative;
+
+ /*
+ * reference to globals from map.c
+ */
+#define _DITHERSIZE 64
+ Byte a_bDitherPattern[_DITHERSIZE];
+ Short wBrightness;
+ Short wContrast;
+ UShort wInitialStep;
+ ULong dwSizeMustProcess;
+
+ /*
+ * here we have pointers to the functions to call
+ */
+ Bool (*OpenScanPath) (pScanData);
+ void (*CloseScanPath) (pScanData);
+ int (*ReadWriteTest) (pScanData);
+ void (*PutToIdleMode) (pScanData);
+ int (*Calibration) (pScanData);
+ void (*SetupScannerVariables) (pScanData);
+ int (*SetupScanSettings) (pScanData, pScanInfo pInf );
+ void (*GetImageInfo) (pScanData, pImgDef pInf );
+ Bool (*WaitForShading) (pScanData);
+ void (*WaitForPositionY) (pScanData);
+ void (*InitialSetCurrentSpeed) (pScanData);
+ Bool (*GotoShadingPosition) (pScanData);
+ void (*SetupScanningCondition) (pScanData);
+ void (*SetMotorSpeed) (pScanData,Byte bSpeed,Bool fSetRunState);
+ void (*FillRunNewAdrPointer) (pScanData);
+ void (*SetupMotorRunTable) (pScanData);
+ void (*PauseColorMotorRunStates) (pScanData);
+ void (*UpdateDataCurrentReadLine)(pScanData);
+ Bool (*ReadOneImageLine) (pScanData);
+
+ /* used only by ASIC9800x Part of the driver ! */
+ void (*ReInitAsic) (pScanData, Bool shading);
+
+ /* value used to read nibble's */
+ Byte CtrlReadHighNibble;
+ Byte CtrlReadLowNibble;
+
+ /*
+ * asic register offset values
+ */
+ Byte RegSwitchBus;
+ Byte RegEPPEnable;
+ Byte RegECPEnable;
+ Byte RegReadDataMode;
+ Byte RegWriteDataMode;
+ Byte RegInitDataFifo;
+ Byte RegForceStep;
+ Byte RegInitScanState;
+ Byte RegRefreshScanState;
+ Byte RegThresholdGapControl;
+ Byte RegADCAddress;
+ Byte RegADCData;
+ Byte RegADCPixelOffset;
+ Byte RegADCSerialOutStr;
+ Byte RegResetConfig;
+ Byte RegLensPosition;
+ Byte RegStatus;
+ Byte RegWaitStateInsert;
+ Byte RegFifoOffset;
+ Byte RegRFifoOffset;
+ Byte RegGFifoOffset;
+ Byte RegBFifoOffset;
+ Byte RegBitDepth;
+ Byte RegStepControl;
+ Byte RegMotor0Control;
+ Byte RegXStepTime;
+ Byte RegGetScanState;
+ Byte RegAsicID;
+ Byte RegReadIOBufBus;
+ Byte RegMemoryLow;
+ Byte RegMemoryHigh;
+ Byte RegModeControl;
+ Byte RegLineControl;
+ Byte RegScanControl;
+ Byte RegMotorControl;
+#define _MotorDirForward 0x01 /* go forward */
+#define _MotorOn 0x02 /* turn on motor */
+#define _MotorIgnorePF 0x04 /* motor rolling don't care */
+ /* paper define flag */
+#define _MotorFreeRun 0x80 /*ScanState count don't stop */
+ /* Following bits (bit 3 & 4 are depended on StatusPort */
+ /* bit-7:MotorType when it is 1: */
+#define _Motor1FullStep 0x08 /* bit 4 is ignored */
+ /* When it is 0: */
+#define _Motor0FullStepWeak 0 /* Full step (driving weak) */
+#define _Motor0HalfStep 0x10 /* 1/2 step */
+#define _Motor0QuarterStep 0x08 /* 1/4 step */
+#define _Motor0FullStepStrong 0x18 /* Full step (driving strong)*/
+#define _MotorStepMask 0xe7
+ /* for 96001 */
+#define _MotorFullStep96001 0x02
+#define _MotorOn96001 0x04
+#define _MotorIgnorePF96001 0x08
+
+ Byte RegConfiguration;
+ Byte RegModelControl;
+ Byte RegModel1Control;
+ Byte RegMemAccessControl;
+#define _MemBanks 64 /* the number of banks, 5 ls bits */
+#define _MemBankMask (_MemBanks - 1)
+#define _MemBankSize1k 0
+#define _MemBankSize2k 0x40
+#define _MemBankSize4k 0x80
+#define _MemBankSize8k 0xc0
+ /* 96001 specific */
+#define _MemBankSize2k96001 0x00
+#define _MemBankSize4k96001 0x40
+#define _MemBankSize8k96001 0x80
+
+ Byte RegDpiLow;
+ Byte RegDpiHigh;
+ Byte RegScanPosLow;
+ Byte RegScanPosHigh;
+ Byte RegWidthPixelsLow;
+ Byte RegWidthPixelsHigh;
+ Byte RegThresholdLow;
+ Byte RegThresholdHigh;
+ Byte RegThresholdControl;
+ Byte RegWatchDogControl;
+#define _WDOnIntervalMask 0x0f /* WD * 8192 scan lines to turn
+ off Lamp */
+#define _WDMotorLongInterval 0x40 /* short = 8192 lines time
+ long = 32768 lines time */
+#define _WDEnable 0x80
+ Byte RegModelControl2;
+#define _Model2ChannelSlct 0
+#define _Model2ChannelMult 0x01 /* bit on/off accords to JONES */
+#define _Model2CCSInvert 0x02
+#define _Model2DirectOutPort 0x04
+#define _Model2PipeLineDelayN 0x08
+#define _Model2ShiftGapTiming10 0x10
+#define _Model2BtnKeyPassThrough 0x20
+ Byte RegRedDCAdjust;
+ Byte RegGreenDCAdjust;
+ Byte RegBlueDCAdjust;
+ Byte RegRedChShadingOffset;
+ Byte RegGreenChShadingOffset;
+ Byte RegBlueChShadingOffset;
+ Byte RegRedChDarkOffset;
+ Byte RegGreenChDarkOffset;
+ Byte RegBlueChDarkOffset;
+ Byte RegWriteIOBusDecode1;
+ Byte RegWriteIOBusDecode2;
+ Byte RegScanStateControl;
+#define _ScanStateEvenMask 0x0f
+#define _ScanStateOddMask 0xf0
+ Byte RegRedChEvenOffset;
+ Byte RegGreenChEvenOffset;
+ Byte RegBlueChEvenOffset;
+ Byte RegRedChOddOffset;
+ Byte RegGreenChOddOffset;
+ Byte RegBlueChOddOffset;
+ Byte RegRedGainOutDirect;
+ Byte RegGreenGainOutDirect;
+ Byte RegBlueGainOutDirect;
+ Byte RegLedControl;
+#define _LedCmdActEnable 0x04
+#define _LedMotorActEnable 0x08
+#define _LedClrChActEnable 0x10 /* Color Channel Action */
+#define _LedLightOnActEnable 0x20
+#define _LedHostTurnOnEnable 0x40
+#define _LedActControl 0x80
+ Byte RegShadingCorrectCtrl;
+#define _ShadingRCorrectX1 0
+#define _ShadingRCorrectX2 0x01
+#define _ShadingRCorrectX3 0x02
+#define _ShadingRCorrectX4 0x03
+#define _ShadingGCorrectX1 0
+#define _ShadingGCorrectX2 0x04
+#define _ShadingGCorrectX3 0x08
+#define _ShadingGCorrectX4 0x0c
+#define _ShadingBCorrectX1 0
+#define _ShadingBCorrectX2 0x10
+#define _ShadingBCorrectX3 0x20
+#define _ShadingBCorrectX4 0x30
+ Byte RegScanStateBegin;
+ Byte RegRedChDarkOffsetLow;
+ Byte RegRedChDarkOffsetHigh;
+ Byte RegGreenChDarkOffsetLow;
+ Byte RegGreenChDarkOffsetHigh;
+ Byte RegBlueChDarkOffsetLow;
+ Byte RegBlueChDarkOffsetHigh;
+ Byte RegResetPulse0;
+ Byte RegResetPulse1;
+ Byte RegCCDClampTiming0;
+ Byte RegCCDClampTiming1;
+ Byte RegVSMPTiming0;
+ Byte RegVSMPTiming1;
+ Byte RegCCDQ1Timing0;
+ Byte RegCCDQ1Timing1;
+ Byte RegCCDQ1Timing2;
+ Byte RegCCDQ1Timing3;
+ Byte RegCCDQ2Timing0;
+ Byte RegCCDQ2Timing1;
+ Byte RegCCDQ2Timing2;
+ Byte RegCCDQ2Timing3;
+ Byte RegADCclockTiming0;
+ Byte RegADCclockTiming1;
+ Byte RegADCclockTiming2;
+ Byte RegADCclockTiming3;
+ Byte RegADCDVTiming0;
+ Byte RegADCDVTiming1;
+ Byte RegADCDVTiming2;
+ Byte RegADCDVTiming3;
+ Byte RegScanStateEnd;
+
+ /* ASIC 98003 specific*/
+ Byte RegFifoFullLength0;
+ Byte RegFifoFullLength1;
+ Byte RegFifoFullLength2;
+
+ Byte RegMotorTotalStep0;
+ Byte RegMotorTotalStep1;
+ Byte RegMotorFreeRunCount0;
+ Byte RegMotorFreeRunCount1;
+ Byte RegScanControl1;
+ Byte RegMotorFreeRunTrigger;
+
+ Byte RegResetMTSC;
+
+ Byte RegMotor1Control;
+ Byte RegMotor2Control;
+ Byte RegMotorDriverType;
+ Byte RegStatus2;
+ Byte RegExtendedLineControl;
+ Byte RegExtendedXStep;
+
+ Byte RegPllPredivider;
+ Byte RegPllMaindivider;
+ Byte RegPllPostdivider;
+ Byte RegClockSelector;
+ Byte RegTestMode;
+
+/* CHECK: subject to change */
+ IODef IO;
+ DeviceDef Device;
+ ShadingDef Shade;
+ ScanDef Scan;
+ BufferDef Bufs;
+
+} ScanData;
+
+#endif /* guard __SCANDATA_H__ */
+
+/* END PLUTSEK-PP_SCANDATA.H ................................................*/
diff --git a/backend/plustek-pp_sysdep.h b/backend/plustek-pp_sysdep.h
new file mode 100644
index 0000000..9e064d2
--- /dev/null
+++ b/backend/plustek-pp_sysdep.h
@@ -0,0 +1,362 @@
+/* @file plustek-pp_sysdep.h
+ * @brief a trial to centralize changes between the different
+ * kernel-versions some stuff is maybe not relevant, but anyway...
+ *
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * 0.30 - initial version
+ * 0.38 - added this header
+ * 0.39 - added kernel 2.4 stuff
+ * 0.40 - added slab.h/malloc.h stuff for kernel >= 2.4.17
+ * 0.41 - no changes
+ * 0.42 - added _GET_TIME
+ * - added LINUX_26 for new kernel
+ * - added _MINOR
+ * 0.43 - added class functions
+ * 0.44 - added support for kernel >= 2.6.35 and 3.x
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef _SYSDEP_H_
+#define _SYSDEP_H_
+
+#ifndef LINUX_VERSION_CODE
+# include <linux/version.h>
+#endif
+
+#ifndef VERSION_CODE
+# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
+#endif
+
+/* only allow > 2.0.x */
+#if LINUX_VERSION_CODE < VERSION_CODE(2,0,0)
+# error "This kernel is too old: not supported by this file"
+#endif
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,0)
+# define LINUX_20
+#elif LINUX_VERSION_CODE < VERSION_CODE(2,4,0)
+# define LINUX_21
+#elif LINUX_VERSION_CODE < VERSION_CODE(2,6,0)
+# define LINUX_24
+#else
+# define LINUX_24
+# define LINUX_26
+# include <linux/device.h>
+# if LINUX_VERSION_CODE > VERSION_CODE(2,6,35)
+# define NOLOCK_IOCTL
+# define IOCTL unlocked_ioctl
+# else
+# define IOCTL ioctl
+# endif
+# if LINUX_VERSION_CODE > VERSION_CODE(3,0,0)
+# include <linux/sched.h>
+# endif
+#endif
+
+#include <linux/types.h> /* used later in this header */
+
+
+/* Modularization issues */
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,18)
+# define __USE_OLD_SYMTAB__
+# define EXPORT_NO_SYMBOLS register_symtab(NULL);
+# define REGISTER_SYMTAB(tab) register_symtab(tab)
+#else
+# define REGISTER_SYMTAB(tab) /* nothing */
+#endif
+
+#ifdef __USE_OLD_SYMTAB__
+# define __MODULE_STRING(s) /* nothing */
+# define MODULE_PARM(v,t) /* nothing */
+# define MODULE_PARM_DESC(v,t) /* nothing */
+# define MODULE_AUTHOR(n) /* nothing */
+# define MODULE_DESCRIPTION(d) /* nothing */
+# define MODULE_SUPPORTED_DEVICE(n) /* nothing */
+#endif
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,31)
+# define CLOSETYPE void
+# define CLOSERETURN(arg)
+#else
+# define CLOSETYPE int
+# define CLOSERETURN(arg) return arg
+#endif
+
+/*
+ * "select" changed in 2.1.23. The implementation is twin, but this
+ * header is new
+ */
+#if LINUX_VERSION_CODE > VERSION_CODE(2,1,22)
+# include <linux/poll.h>
+#else
+# define __USE_OLD_SELECT__
+#endif
+
+/* Other change in the fops are solved using pseudo-types */
+#if defined(LINUX_21) || defined(LINUX_24) || defined(LINUX_26)
+# define lseek_t long long
+# define lseek_off_t long long
+#else
+# define lseek_t int
+# define lseek_off_t off_t
+#endif
+
+/* changed the prototype of read/write */
+#if defined(LINUX_21) || defined (LINUX_24) || defined(LINUX_26) || defined(__alpha__)
+# define count_t unsigned long
+# define read_write_t long
+#else
+# define count_t int
+# define read_write_t int
+#endif
+
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,31)
+# define release_t void
+# define release_return(x) return
+#else
+# define release_t int
+# define release_return(x) return (x)
+#endif
+
+/*
+ * access to user space: use the 2.1 functions,
+ * and implement them as macros for 2.0
+ */
+
+#ifdef LINUX_20
+# include <asm/segment.h>
+# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1)
+# define verify_area_20 verify_area
+# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0)
+# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n))
+# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n))
+# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0)
+# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n))
+# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n))
+# define PUT_USER(val,add) (put_user((val),(add)), 0)
+# define __PUT_USER(val,add) PUT_USER((val),(add))
+# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add))
+# define GET_USER(dest,add) ((dest)=get_user((add)), 0)
+# define __GET_USER(dest,add) GET_USER((dest),(add))
+# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add))
+#else
+# include <asm/uaccess.h>
+# include <asm/io.h>
+# define verify_area_20(t,a,sz) (0) /* == success */
+# define PUT_USER put_user
+# define __PUT_USER __put_user
+# define PUT_USER_RET put_user_ret
+# define GET_USER get_user
+# define __GET_USER __get_user
+# define GET_USER_RET get_user_ret
+
+/* starting with 2.4.0-test8, they removed the put_user_ret and get_user_ret
+ * macros, so we recode'em
+ */
+#if defined(LINUX_24) || defined(LINUX_26)
+#ifndef put_user_ret
+# define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; })
+#endif
+
+#ifndef get_user_ret
+# define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; })
+#endif
+#endif
+
+#endif
+
+/* ioremap */
+#ifdef LINUX_20
+# define ioremap vremap
+# define iounmap vfree
+#endif
+
+/* The use_count of exec_domain and binfmt changed in 2.1.23 */
+
+#ifdef LINUX_20
+# define INCRCOUNT(p) ((p)->module ? __MOD_INC_USE_COUNT((p)->module) : 0)
+# define CURRCOUNT(p) ((p)->module && (p)->module->usecount)
+# define DECRCOUNT(p) ((p)->module ? __MOD_DEC_USE_COUNT((p)->module) : 0)
+#else
+# define INCRCOUNT(p) ((p)->use_count++)
+# define CURRCOUNT(p) ((p)->use_count)
+# define DECRCOUNT(p) ((p)->use_count--)
+#endif
+
+/* register_dynamic no more existent -- just have 0 as inum */
+#if LINUX_VERSION_CODE >= VERSION_CODE(2,1,29)
+# define proc_register_dynamic proc_register
+#endif
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,37)
+# define test_and_set_bit(nr,addr) test_bit((nr),(addr))
+# define test_and_clear_bit(nr,addr) clear_bit((nr),(addr))
+# define test_and_change_bit(nr,addr) change_bit((nr),(addr))
+#endif
+
+/* 2.1.30 removed these functions. Let's define them, just in case */
+#if LINUX_VERSION_CODE > VERSION_CODE(2,1,29)
+# define queue_task_irq queue_task
+# define queue_task_irq_off queue_task
+#endif
+
+/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10)
+
+# include <asm/byteorder.h>
+# ifdef __LITTLE_ENDIAN
+# define cpu_to_le16(x) (x)
+# define cpu_to_le32(x) (x)
+# define cpu_to_be16(x) htons((x))
+# define cpu_to_be32(x) htonl((x))
+# else
+# define cpu_to_be16(x) (x)
+# define cpu_to_be32(x) (x)
+ extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
+ extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) |
+ ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));}
+# endif
+
+# define le16_to_cpu(x) cpu_to_le16(x)
+# define le32_to_cpu(x) cpu_to_le32(x)
+# define be16_to_cpu(x) cpu_to_be16(x)
+# define be32_to_cpu(x) cpu_to_be32(x)
+
+#endif
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43)
+# define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
+# define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
+# define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
+# define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
+
+ extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
+ extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
+ extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
+ extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
+
+# define le16_to_cpup(x) cpu_to_le16p(x)
+# define le32_to_cpup(x) cpu_to_le32p(x)
+# define be16_to_cpup(x) cpu_to_be16p(x)
+# define be32_to_cpup(x) cpu_to_be32p(x)
+
+# define le16_to_cpus(x) cpu_to_le16s(x)
+# define le32_to_cpus(x) cpu_to_le32s(x)
+# define be16_to_cpus(x) cpu_to_be16s(x)
+# define be32_to_cpus(x) cpu_to_be32s(x)
+#endif
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,15)
+# define __USE_OLD_REBUILD_HEADER__
+#endif
+
+#if LINUX_VERSION_CODE < VERSION_CODE(2,1,30)
+# define in_interrupt() (intr_count!=0)
+#endif
+
+/*
+ * from 2.4.17 on, they decided to use slab.h instead of malloc.h... so what...
+ * somewhere from 2.4.18-pre9 they skipped get_fast_time...
+ */
+#if LINUX_VERSION_CODE < VERSION_CODE(2,4,17)
+# include "linux/malloc.h"
+# define _GET_TIME get_fast_time
+#else
+# include "linux/slab.h"
+# define _GET_TIME do_gettimeofday
+#endif
+
+#ifdef LINUX_26
+# define _MINOR(p) iminor(p)
+#else
+# define _MINOR(p) minor(p->i_rdev)
+#endif
+
+/* Basic class macros */
+#ifdef LINUX_26
+#if LINUX_VERSION_CODE >= VERSION_CODE(2,6,15)
+
+typedef struct class class_t;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define CLASS_DEV_CREATE(class, devt, device, name) \
+ device_create(class, device, devt, NULL, "%s", name)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#define CLASS_DEV_CREATE(class, devt, device, name) \
+ device_create(class, device, devt, name)
+#else
+#define CLASS_DEV_CREATE(class, devt, device, name) \
+ class_device_create(class, NULL, devt, device, name)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#define CLASS_DEV_DESTROY device_destroy
+#else
+#define CLASS_DEV_DESTROY class_device_destroy
+#endif
+
+#else /* LINUX 2.6.0 - 2.6.14 */
+
+#if LINUX_VERSION_CODE >= VERSION_CODE(2,6,13) /* LINUX 2.6.13 - 2.6.14 */
+typedef struct class class_t;
+#define CLASS_DEVICE_CREATE class_device_create
+#define CLASS_DEV_DESTROY(class, devt) class_device_destroy(class, devt)
+
+#else /* LINUX 2.6.0 - 2.6.12, class_simple */
+
+typedef struct class_simple class_t;
+#define CLASS_DEVICE_CREATE class_simple_device_add
+#define CLASS_DEV_DESTROY(class, devt) class_simple_device_remove(class, devt)
+
+#define class_create class_simple_create
+#define class_destroy class_simple_destroy
+#define class_device_destroy(a, b) class_simple_device_remove(b)
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) */
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+#endif
+
+#endif /* _SYSDEP_H_ */
+
+/* END PLUSTEK-PP_SYSDEP.H ..................................................*/
diff --git a/backend/plustek-pp_tpa.c b/backend/plustek-pp_tpa.c
new file mode 100644
index 0000000..021bad6
--- /dev/null
+++ b/backend/plustek-pp_tpa.c
@@ -0,0 +1,1174 @@
+/* @file plustek-pp_tpa.c
+ * @brief Here we find some adjustments according to the scan source.
+ * This file is ASIC P9800x specific
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 1998 Plustek Inc.
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ * also based on the work done by Rick Bronson
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - Added some comments
+ * - 0.32 - no changes
+ * - 0.33 - new header
+ * - 0.34 - no changes
+ * - 0.35 - no changes
+ * - 0.36 - no changes
+ * - 0.37 - cosmetic changes
+ * - 0.38 - Replaced AllPointer by DataPointer
+ * - renamed this file from transform.c tpa.c
+ * - 0.39 - no changes
+ * - 0.40 - no changes
+ * - 0.41 - no changes
+ * - 0.42 - changed include names
+ * - 0.43 - no changes
+ * - 0.44 - fix format string issues, as Long types default to int32_t
+ * now
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#include "plustek-pp_scan.h"
+
+/***************************** local vars ************************************/
+
+static UShort a_wGainString [] = {
+ 50, 75, 100, 125, 150, 175, 200, 225,
+ 250, 275, 300, 325, 350, 375, 400, 425,
+ 450, 475, 500, 525, 550, 575, 600, 625,
+ 650, 675, 700, 725, 750, 775, 800, 825
+};
+
+/*************************** local functions *********************************/
+
+/*.............................................................................
+ *
+ */
+static void tpaP98SubNoise( pScanData ps, pULong pdwSum, pUShort pwShading,
+ ULong dwHilightOff, ULong dwShadowOff )
+{
+ ULong dwPixels, dwLines, dwSum;
+ pUShort pw;
+
+ for (dwPixels = 4; dwPixels--; pdwSum++, pwShading++)
+ *pwShading = (UShort)(*pdwSum >> 5);
+
+ for (dwPixels = 0; dwPixels < (ps->dwShadingPixels - 4); dwPixels++,
+ pdwSum++, pwShading++) {
+
+ pw = (pUShort)ps->Shade.pHilight + dwHilightOff + dwPixels;
+ dwSum = 0;
+
+ for (dwLines = _DEF_BRIGHTEST_SKIP; dwLines--; pw += 5400UL)
+ dwSum += (ULong) *pw;
+
+ pw = ps->pwShadow + dwShadowOff + dwPixels;
+
+ for (dwLines = _DEF_DARKEST_SKIP; dwLines--; pw += 5400UL)
+ dwSum += (ULong) *pw;
+
+ *pwShading = (UShort)((*pdwSum - dwSum) / ps->Shade.dwDiv);
+ }
+ if (ps->dwShadingPixels != 5400UL) {
+ for (dwPixels = 2700UL; dwPixels--; pdwSum++, pwShading++)
+ *pwShading = (UShort)(*pdwSum >> 5);
+ }
+}
+
+/*.............................................................................
+ *
+ */
+static void tpaP98ShadingWaveformSum( pScanData ps )
+{
+ DataPointer pd, pt;
+ ULong dw;
+
+ pd.pdw = (pULong)ps->pScanBuffer1;
+ pt.pw = (pUShort)ps->pScanBuffer1;
+
+ if ((ps->DataInf.dwScanFlag & SCANDEF_TPA ) ||
+ (0 == ps->bShadingTimeFlag)) {
+
+ if( ps->Shade.pHilight ) {
+
+ tpaP98SubNoise( ps, (pULong)ps->pScanBuffer1,
+ (pUShort)ps->pScanBuffer1, 0, 0);
+
+ tpaP98SubNoise( ps ,(pULong)ps->pScanBuffer1 + 5400UL,
+ (pUShort)ps->pScanBuffer1 + 5400UL,
+ ps->dwHilightCh, ps->dwShadowCh);
+
+ tpaP98SubNoise( ps, (pULong)ps->pScanBuffer1 + 5400UL * 2UL,
+ (pUShort)ps->pScanBuffer1 + 5400UL * 2UL,
+ ps->dwHilightCh * 2, ps->dwShadowCh * 2);
+ } else {
+
+ for (dw = 5400 * 3; dw; dw--, pt.pw++, pd.pdw++)
+ *pt.pw = (UShort)(*pd.pdw / 32); /* shift 5 bits */
+ }
+
+ } else {
+
+ if (02 == ps->bShadingTimeFlag ) {
+ for (dw = 5400 * 3; dw; dw--, pt.pw++, pd.pdw++)
+ *pt.pw = (UShort)(*pd.pdw / 16); /* shift 4 bits */
+ } else {
+ for (dw = 5400 * 3; dw; dw--, pt.pw++, pd.pdw++)
+ *pt.pw = (UShort)(*pd.pdw / 4); /* shift 2 bits */
+ }
+ }
+}
+
+/*.............................................................................
+ * get wReduceRedFactor, wReduceGreenFactor, wReduceBlueFactor
+ */
+static void tpaP98GetNegativeTempRamData( pScanData ps )
+{
+ UShort wRedTemp, wGreenTemp, wBlueTemp;
+ UShort wRedShadingTemp, wGreenShadingTemp, wBlueShadingTemp;
+ ULong dw, dw1;
+ DataPointer p;
+ pULong pdwNegativeSumTemp;
+ pUShort pNegativeTempRam, pNegativeTempRam2;
+
+ ps->bFastMoveFlag = _FastMove_Low_C75_G150;
+
+ MotorP98GoFullStep( ps ,80 );
+
+ pNegativeTempRam = (pUShort)(ps->pScanBuffer1 + 5400 * 6);
+ pdwNegativeSumTemp = (pULong)(pNegativeTempRam + 960 * 3 * 2);
+ pNegativeTempRam2 = (pUShort)(pdwNegativeSumTemp + 960 * 3 * 4);
+
+ /* ClearNegativeSumBuffer() */
+ memset( pdwNegativeSumTemp, 0, (960 * 3 * 4));
+
+ /* SetReadNegativeTempRegister() */
+ ps->AsicReg.RD_Motor0Control = 0;
+
+ IOCmdRegisterToScanner( ps, ps->RegMotor0Control,
+ ps->AsicReg.RD_Motor0Control );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_Motor0Control = _MotorOn + _MotorHEightStep + _MotorDirForward;
+ ps->AsicReg.RD_ModelControl = _ModelDpi600 + _LED_CONTROL + _LED_ACTIVITY;
+ ps->AsicReg.RD_Dpi = ps->PhysicalDpi;
+
+ if (!ps->wNegAdjustX) {
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 + ps->Device.DataOriginX +
+ _Negative96OriginOffsetX * 2);
+ } else {
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 + ps->Device.DataOriginX +
+ ps->wNegAdjustX);
+ }
+
+ ps->AsicReg.RD_Pixels = 960;
+ ps->AsicReg.RD_XStepTime = 32;
+
+ IOPutOnAllRegisters( ps );
+
+ /* NegativeMotorRunLoop() */
+ p.pb = ps->a_nbNewAdrPointer;
+ for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++)
+ *p.pdw = 0x87808780;
+
+ IOSetToMotorRegister( ps );
+
+ for (dw1 = 16; dw1; dw1--) {
+
+ TimerDef timer;
+
+ MiscStartTimer( &timer, _SECOND );
+
+ while((IOReadFifoLength( ps ) < 960) && !MiscCheckTimer( &timer )) {
+
+ _DO_UDELAY(1);
+ }
+
+ /* ReadColorDataIn() - Read 1 RGB line */
+ ps->AsicReg.RD_ModeControl = _ModeFifoRSel;
+ IOReadScannerImageData( ps, (pUChar)pNegativeTempRam, 960 );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, (pUChar)(pNegativeTempRam + 960), 960 );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+ IOReadScannerImageData( ps, (pUChar)(pNegativeTempRam + 960 * 2), 960 );
+
+ /* fillNegativeSum() */
+ for (dw = 0; dw < 960 * 3; dw++)
+ pdwNegativeSumTemp[dw] += ((pUShort) pNegativeTempRam)[dw];
+
+ /* one line */
+ if (IOReadFifoLength( ps ) <= (960 * 2))
+ IORegisterDirectToScanner( ps, ps->RegRefreshScanState );
+ }
+
+ /* AverageAndShift() */
+ for( dw = 0, dw1 = 0; dw < 240 * 3; dw++, dw1+=4 ) {
+
+ pNegativeTempRam[dw] = (UShort)((pdwNegativeSumTemp[dw1] +
+ pdwNegativeSumTemp[dw1+1] +
+ pdwNegativeSumTemp[dw1+2] +
+ pdwNegativeSumTemp[dw1+3]) / 128);
+ /* shift 6 bits */
+ }
+
+ /* NegativeAdd1() */
+ if (!ps->wNegAdjustX) {
+ dw1 = (ps->dwOffsetNegative + _Negative96OriginOffsetX * 2 * 2) / 2;
+ } else {
+ dw1 = (ps->dwOffsetNegative + ps->wNegAdjustX * 2) / 2;
+ }
+
+ /* do R shading average */
+ for (dw = 0; dw < 240; dw++, dw1 += 4) {
+ pNegativeTempRam2[dw] = (UShort)(
+ (((pUShort)ps->pScanBuffer1)[dw1] +
+ ((pUShort)ps->pScanBuffer1)[dw1+1] +
+ ((pUShort)ps->pScanBuffer1)[dw1+2] +
+ ((pUShort)ps->pScanBuffer1)[dw1+3]) / 4);
+ }
+
+ /* NegativeAdd1() */
+ if (!ps->wNegAdjustX)
+ dw1 = (ps->dwOffsetNegative + 5400 * 2 + _Negative96OriginOffsetX * 2 * 2) / 2;
+ else
+ dw1 = (ps->dwOffsetNegative + 5400 * 2 + ps->wNegAdjustX * 2) / 2;
+
+ /* do G shading average */
+ for (; dw < 240 * 2; dw++, dw1 += 4) {
+ pNegativeTempRam2[dw] = (UShort)(
+ (((pUShort)ps->pScanBuffer1)[dw1] +
+ ((pUShort)ps->pScanBuffer1)[dw1+1] +
+ ((pUShort)ps->pScanBuffer1)[dw1+2] +
+ ((pUShort)ps->pScanBuffer1)[dw1+3]) / 4);
+ }
+
+ /* NegativeAdd1() */
+ if (!ps->wNegAdjustX)
+ dw1 = (ps->dwOffsetNegative + 5400 * 4 + _Negative96OriginOffsetX * 2 * 2) / 2;
+ else
+ dw1 = (ps->dwOffsetNegative + 5400 * 4 + ps->wNegAdjustX * 2) / 2;
+
+ /* do B shading average */
+ for (; dw < 240 * 3; dw++, dw1 += 4) {
+ pNegativeTempRam2 [dw] = (UShort)(
+ (((pUShort)ps->pScanBuffer1)[dw1] +
+ ((pUShort)ps->pScanBuffer1)[dw1+1] +
+ ((pUShort)ps->pScanBuffer1)[dw1+2] +
+ ((pUShort)ps->pScanBuffer1)[dw1+3]) / 4);
+ }
+
+ wRedTemp = wGreenTemp = wBlueTemp = 0;
+ wRedShadingTemp = wGreenShadingTemp = wBlueShadingTemp = 0;
+
+ /* FindMaxNegValue -- find R */
+ for (dw = 0; dw < 240; dw++) {
+ if (pNegativeTempRam[dw] >= wRedTemp &&
+ pNegativeTempRam[dw + 240] >= wGreenTemp &&
+ pNegativeTempRam[dw + 480] > wBlueTemp) {
+
+ wRedTemp = pNegativeTempRam[dw];
+ wGreenTemp = pNegativeTempRam[dw + 240];
+ wBlueTemp = pNegativeTempRam[dw + 480];
+
+ wRedShadingTemp = pNegativeTempRam2[dw];
+ wGreenShadingTemp = pNegativeTempRam2[dw + 240];
+ wBlueShadingTemp = pNegativeTempRam2[dw + 480];
+ }
+ }
+
+ /* GainAddX = (1/4)*DoubleX + 1/ 2 */
+ if ((ps->bRedGainIndex += (Byte)((wRedShadingTemp / wRedTemp) * 100 - 50) / 25) > 32)
+ ps->bRedGainIndex = 31;
+
+ if ((ps->bGreenGainIndex += (Byte)((wGreenShadingTemp / wGreenTemp) * 100 - 50) / 25) > 32)
+ ps->bGreenGainIndex = 31;
+
+ if ((ps->bBlueGainIndex += (Byte)((wBlueShadingTemp / wBlueTemp) * 100 - 50) / 25) > 32)
+ ps->bBlueGainIndex = 31;
+
+}
+
+/*.............................................................................
+ *
+ */
+static void tpaP98RecalculateNegativeShadingGain( pScanData ps )
+{
+ Byte b[3];
+ UShort wSum, counter;
+ UShort w, w1, w2;
+ ULong dw, dw1;
+ pUChar pDest, pSrce, pNegativeTempRam;
+ pUChar pbReg[3];
+ TimerDef timer;
+ DataPointer p;
+
+ pNegativeTempRam = (pUChar)(ps->pScanBuffer1 + 5400 * 6);
+
+ /* AdjustDarkCondition () */
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = ps->bsPreRedDAC;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = ps->bsPreGreenDAC;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = ps->bsPreBlueDAC;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = ps->wsDACCompareHighRed;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = ps->wsDACCompareLowRed;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = ps->wsDACCompareHighGreen;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = ps->wsDACCompareLowGreen;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = ps->wsDACCompareHighBlue;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = ps->wsDACCompareLowBlue;
+
+ DacP98FillGainOutDirectPort( ps );
+
+ /* ClearNegativeTempBuffer () */
+ memset( pNegativeTempRam, 0, (960 * 3 * 4));
+
+ /* GetNegGainValue () */
+ ps->PauseColorMotorRunStates( ps );
+
+ /* SetScanMode () set scan mode to Byte mode */
+ ps->AsicReg.RD_ScanControl |= _SCAN_BYTEMODE;
+ ps->AsicReg.RD_ScanControl &= 0xfd;
+ IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
+ DacP98FillGainOutDirectPort( ps );
+
+ /* SetReadNegativeTempRegister() */
+ ps->AsicReg.RD_Motor0Control = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotor0Control,
+ ps->AsicReg.RD_Motor0Control );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_Motor0Control = _MotorOn + _MotorHEightStep + _MotorDirForward;
+ ps->AsicReg.RD_ModelControl = _ModelDpi600 + _LED_CONTROL + _LED_ACTIVITY;
+ ps->AsicReg.RD_Dpi = ps->PhysicalDpi;
+
+ if (!ps->wNegAdjustX) {
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 +
+ ps->Device.DataOriginX +
+ _Negative96OriginOffsetX * 2);
+ } else {
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 + ps->Device.DataOriginX +
+ ps->wNegAdjustX);
+ }
+ ps->AsicReg.RD_Pixels = 960;
+ ps->AsicReg.RD_XStepTime = 32;
+ IOPutOnAllRegisters( ps );
+
+ /* ReReadNegativeTemp */
+ MiscStartTimer( &timer, _SECOND );
+
+ while((IOReadFifoLength( ps) < 960) && !MiscCheckTimer( &timer )) {
+
+ _DO_UDELAY(1); /* 1 us delay */
+ }
+
+ /* ReadColorDataIn() - Read 1 RGB line */
+ ps->AsicReg.RD_ModeControl = _ModeFifoRSel;
+ IOReadScannerImageData( ps, pNegativeTempRam, 960);
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, pNegativeTempRam + 960, 960);
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+ IOReadScannerImageData( ps, pNegativeTempRam + 960 * 2, 960);
+
+ /* FindRGBGainValue(); */
+ pDest = pSrce = pNegativeTempRam;
+
+ /* ReAdjustGainAverage() */
+ for (dw1 = 0; dw1 < (960 * 3) / 16; dw1++, pDest++) {
+ for (dw = 0, wSum = 0; dw < 16; dw++, pSrce++)
+ wSum += *pSrce;
+
+ *pDest = wSum / 16;
+ }
+
+ /* FindTheMaxGainValue */
+ for (w = 0, p.pb = pNegativeTempRam; w < 3; w++) {
+ for (dw = 960 / 16, b[w] = 0; dw; dw--, p.pb++) {
+ if (b[w] < *p.pb)
+ b[w] = *p.pb;
+ }
+ }
+ ps->bRedHigh = b[0];
+ ps->bGreenHigh = b[1];
+ ps->bBlueHigh = b[2];
+
+ /* ModifyExposureTime () */
+ if ((ps->bRedHigh < _GAIN_LOW) ||
+ (ps->bGreenHigh < _GAIN_LOW) || (ps->bBlueHigh < _GAIN_LOW)) {
+ ps->AsicReg.RD_LineControl = 192;
+ }
+
+ IOCmdRegisterToScanner( ps, ps->RegLineControl,
+ ps->AsicReg.RD_LineControl );
+ counter = 0;
+
+ /* ReAdjustRGBGain () */
+ for (w1 = 0; w1 < 16; w1++) {
+
+ ps->PauseColorMotorRunStates( ps );
+ DacP98FillGainOutDirectPort( ps );
+
+ /* SetReadNegativeTempRegister () */
+ ps->AsicReg.RD_Motor0Control = 0;
+ IOCmdRegisterToScanner( ps, ps->RegMotor0Control,
+ ps->AsicReg.RD_Motor0Control );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_Motor0Control = _MotorOn + _MotorHEightStep + _MotorDirForward;
+ ps->AsicReg.RD_ModelControl = _ModelDpi600 + _LED_CONTROL + _LED_ACTIVITY;
+ ps->AsicReg.RD_Dpi = ps->PhysicalDpi;
+
+ if (!ps->wNegAdjustX) {
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 +
+ ps->Device.DataOriginX +
+ _Negative96OriginOffsetX * 2);
+ } else {
+ ps->AsicReg.RD_Origin = (UShort)(ps->dwOffset70 +
+ ps->Device.DataOriginX +
+ ps->wNegAdjustX);
+ }
+
+ ps->AsicReg.RD_Pixels = 960;
+ ps->AsicReg.RD_XStepTime = 32;
+ IOPutOnAllRegisters( ps );
+
+ /* ReReadNegativeTemp () */
+ MiscStartTimer( &timer, _SECOND );
+ while((IOReadFifoLength( ps ) < 960) && !MiscCheckTimer( &timer)) {
+
+ _DO_UDELAY(1);
+ }
+
+ /* ReadColorDataIn() - Read 1 RGB line */
+ ps->AsicReg.RD_ModeControl = _ModeFifoRSel;
+ IOReadScannerImageData( ps, pNegativeTempRam, 960 );
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoGSel;
+ IOReadScannerImageData( ps, pNegativeTempRam + 960, 960);
+
+ ps->AsicReg.RD_ModeControl = _ModeFifoBSel;
+ IOReadScannerImageData( ps ,pNegativeTempRam + 960 * 2, 960);
+
+
+ /* ReAdjustGainAverage() */
+ pDest = pSrce = pNegativeTempRam;
+ for( dw1 = 0; dw1 < (960 * 3) / 16; dw1++, pDest++ ) {
+ for( dw = 0, wSum = 0; dw < 16; dw++, pSrce++ )
+ wSum += *pSrce;
+ *pDest = wSum / 16;
+ }
+
+ /* FindTheMaxGainValue */
+ pbReg[0] = &ps->bRedGainIndex;
+ pbReg[1] = &ps->bGreenGainIndex;
+ pbReg[2] = &ps->bBlueGainIndex;
+ for (w = 0, p.pb = pNegativeTempRam; w < 3; w++) {
+
+ for (dw = 960 / 16, b [w] = 0; dw; dw--, p.pb++) {
+ if (b[w] < *p.pb)
+ b[w] = *p.pb;
+ }
+ if (b [w] < _GAIN_LOW) {
+ if (( _GAIN_P98_HIGH - b [w]) < b [w])
+ *(pbReg [w]) += 1;
+ else
+ *(pbReg [w]) += 4;
+ } else {
+
+ if (b [w] > _GAIN_P98_HIGH)
+ *(pbReg [w]) -= 1;
+ }
+ }
+
+ for (w2 = 0; w2 < 3; w2++) {
+ if (*(pbReg[w2]) > 31)
+ (*(pbReg[w2])) = 31;
+ }
+ if ((b[0] == 0) || (b[1] == 0) || (b[2] == 0)) {
+ counter++;
+
+ if (counter < 16) {
+ w1--;
+ ps->bRedGainIndex -= 4;
+ ps->bGreenGainIndex -= 4;
+ ps->bBlueGainIndex -= 4;
+ }
+ }
+ }
+
+ DacP98FillGainOutDirectPort( ps );
+
+ ps->Shade.DarkOffset.Colors.Red = 0;
+ ps->Shade.DarkOffset.Colors.Green = 0;
+ ps->Shade.DarkOffset.Colors.Blue = 0;
+
+ ps->OpenScanPath( ps );
+ DacP98FillShadingDarkToShadingRegister( ps );
+ ps->CloseScanPath( ps );
+
+ DacP98AdjustDark( ps );
+}
+
+/*.............................................................................
+ *
+ */
+static void tpaP98RecalculateShadingGainandData( pScanData ps )
+{
+ DataPointer p;
+ ULong dw;
+ UShort filmAdjustX;
+ UShort wOldRedGain, wOldGreenGain, wOldBlueGain;
+ UShort wNewRedGain, wNewGreenGain, wNewBlueGain;
+
+ /* AdjustDarkCondition () */
+ ps->Shade.pCcdDac->DarkDAC.Colors.Red = ps->bsPreRedDAC;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Green = ps->bsPreGreenDAC;
+ ps->Shade.pCcdDac->DarkDAC.Colors.Blue = ps->bsPreBlueDAC;
+
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Red = ps->wsDACCompareHighRed;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Red = ps->wsDACCompareLowRed;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Green = ps->wsDACCompareHighGreen;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Green = ps->wsDACCompareLowGreen;
+ ps->Shade.pCcdDac->DarkCmpHi.Colors.Blue = ps->wsDACCompareHighBlue;
+ ps->Shade.pCcdDac->DarkCmpLo.Colors.Blue = ps->wsDACCompareLowBlue;
+
+ wOldRedGain = a_wGainString[ps->bRedGainIndex] * 100/ps->wReduceRedFactor;
+
+ /* SearchNewGain() */
+ for (ps->bRedGainIndex = 0; ps->bRedGainIndex < 32; ps->bRedGainIndex++) {
+ if (wOldRedGain < a_wGainString[ps->bRedGainIndex])
+ break;
+ }
+
+ if (0 == ps->bRedGainIndex)
+ ps->bRedGainIndex ++;
+
+ wNewRedGain = a_wGainString[--ps->bRedGainIndex];
+
+ wOldGreenGain = a_wGainString[ps->bGreenGainIndex]*100/
+ ps->wReduceGreenFactor;
+
+ /* SearchNewGain() */
+ for (ps->bGreenGainIndex = 0;
+ ps->bGreenGainIndex < 32; ps->bGreenGainIndex++) {
+
+ if (wOldGreenGain < a_wGainString[ps->bGreenGainIndex])
+ break;
+ }
+
+ if (0 == ps->bGreenGainIndex)
+ ps->bGreenGainIndex ++;
+
+ wNewGreenGain = a_wGainString[--ps->bGreenGainIndex];
+
+ wOldBlueGain = a_wGainString[ps->bBlueGainIndex]*100/ps->wReduceBlueFactor;
+
+ /* SearchNewGain() */
+ for (ps->bBlueGainIndex = 0;ps->bBlueGainIndex < 32;ps->bBlueGainIndex++) {
+ if (wOldBlueGain < a_wGainString[ps->bBlueGainIndex])
+ break;
+ }
+ if (0 == ps->bBlueGainIndex)
+ ps->bBlueGainIndex ++;
+
+ wNewBlueGain = a_wGainString[--ps->bBlueGainIndex];
+
+ DacP98FillGainOutDirectPort( ps );
+
+ ps->Shade.DarkOffset.Colors.Red = 0;
+ ps->Shade.DarkOffset.Colors.Green = 0;
+ ps->Shade.DarkOffset.Colors.Blue = 0;
+
+ ps->OpenScanPath( ps );
+ DacP98FillShadingDarkToShadingRegister( ps );
+ ps->CloseScanPath( ps );
+
+ DacP98AdjustDark( ps );
+
+ /* RecalculateTransparencyImage() */
+ if (ps->DataInf.dwScanFlag & SCANDEF_Transparency) {
+ filmAdjustX = ps->wPosAdjustX;
+ } else {
+ filmAdjustX = ps->wNegAdjustX;
+ }
+
+ if (!filmAdjustX) {
+ p.pw = (pUShort)(ps->pScanBuffer1 + ps->dwOffsetNegative +
+ _Negative96OriginOffsetX * 2);
+ } else {
+ p.pw = (pUShort)(ps->pScanBuffer1 +
+ ps->dwOffsetNegative + filmAdjustX);
+ }
+
+ /* RecalculateData() */
+ for (dw= 0; dw < _NegativePageWidth * 2 + 132; dw++, p.pw++)
+ *p.pw = *p.pw * wNewRedGain / wOldRedGain;
+
+ if (!ps->wNegAdjustX) {
+ p.pw = (pUShort)(ps->pScanBuffer1 + 5400 * 2 +
+ ps->dwOffsetNegative + _Negative96OriginOffsetX * 2);
+ } else {
+ p.pw = (pUShort)(ps->pScanBuffer1 + 5400 * 2 +
+ ps->dwOffsetNegative + ps->wNegAdjustX);
+ }
+
+ /* RecalculateData() */
+ for (dw= 0; dw < _NegativePageWidth * 2 + 132; dw++, p.pw++)
+ *p.pw = *p.pw * wNewGreenGain / wOldGreenGain;
+
+ if (!ps->wNegAdjustX) {
+ p.pw = (pUShort)(ps->pScanBuffer1 + 5400 * 4 +
+ ps->dwOffsetNegative + _Negative96OriginOffsetX * 2);
+ } else {
+ p.pw = (pUShort)(ps->pScanBuffer1 + 5400 * 4 +
+ ps->dwOffsetNegative + ps->wNegAdjustX);
+ }
+
+ /* RecalculateData() - 64 + dwoffset70 */
+ for (dw= 0; dw < _NegativePageWidth * 2 + 132; dw++, p.pw++)
+ *p.pw = *p.pw * wNewBlueGain / wOldBlueGain;
+}
+
+/************************ exported functions *********************************/
+
+/*.............................................................................
+ * perform some adjustments according to the source (normal, transparency etc)
+ */
+_LOC void TPAP98001AverageShadingData( pScanData ps )
+{
+ DBG( DBG_LOW, "TPAP98001AverageShadingData()\n" );
+
+ ps->wNegAdjustX = 0;
+ ps->wPosAdjustX = 0;
+ ps->dwOffsetNegative = 0;
+
+ tpaP98ShadingWaveformSum( ps );
+
+ /*
+ * CHANGE: to support Grayscale images in transparency and negative mode
+ * original code: if ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) &&
+ */
+ if((ps->DataInf.wPhyDataType >= COLOR_256GRAY) &&
+ (ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
+
+ if (((ps->DataInf.dwScanFlag & SCANDEF_Negative) && !ps->wNegAdjustX) ||
+ ((ps->DataInf.dwScanFlag & SCANDEF_Transparency) && !ps->wPosAdjustX)) {
+
+ Long dwLeft, dwRight;
+ pUShort pw = (pUShort)ps->pScanBuffer1;
+
+ for (dwLeft = 0; dwLeft < 5400; dwLeft++)
+ if (pw[dwLeft] >= 600)
+ break;
+
+ for (dwRight = 4600; dwRight; dwRight--)
+ if (pw[dwRight] >= 600)
+ break;
+
+ DBG( DBG_LOW, "_TPAPageWidth = %u, _NegativePageWidth = %u\n"
+ "right = %d, left = %d --> right = %d\n",
+ _TPAPageWidth, _NegativePageWidth,
+ dwRight, dwLeft, (((Long)dwRight-(Long)dwLeft)/2));
+
+ dwRight = (dwRight - dwLeft) / 2;
+
+ if (ps->DataInf.dwScanFlag & SCANDEF_Negative) {
+
+ if (dwRight >= (Long)_NegativePageWidth) {
+
+ ps->wNegAdjustX = (UShort)(dwRight - _NegativePageWidth +
+ dwLeft - ps->dwOffset70 -
+ ps->Device.DataOriginX + 4U);
+ if( ps->wNegAdjustX > (_Negative96OriginOffsetX * 2U))
+ ps->wNegAdjustX = (_Negative96OriginOffsetX * 2U);
+
+ ps->DataInf.crImage.x += ps->wNegAdjustX;
+ } else {
+ ps->DataInf.crImage.x += (_Negative96OriginOffsetX * 2U);
+ }
+ } else {
+ if (dwRight >= (Long)_TPAPageWidth) {
+
+ ps->wPosAdjustX = (UShort)(dwRight - _TPAPageWidth +
+ dwLeft - ps->dwOffset70 -
+ ps->Device.DataOriginX + 4U);
+
+ if( ps->wPosAdjustX > (_Transparency96OriginOffsetX * 2U))
+ ps->wPosAdjustX = (_Transparency96OriginOffsetX * 2U);
+
+ ps->DataInf.crImage.x += ps->wPosAdjustX;
+
+ } else {
+ ps->DataInf.crImage.x += (_Transparency96OriginOffsetX * 2U);
+ }
+ }
+ }
+#if 0
+ else {
+ /* CHANGE: as we always reset the values, we can ignore this code..*/
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative )
+ ps->DataInf.crImage.x += ps->wNegAdjustX;
+ else
+ ps->DataInf.crImage.x += ps->wPosAdjustX;
+ }
+#endif
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative ) {
+
+ ps->dwOffsetNegative = (ps->dwOffset70 + 64+4) * 2;
+
+ tpaP98GetNegativeTempRamData( ps );
+ tpaP98RecalculateNegativeShadingGain( ps );
+
+ } else {
+ ps->wReduceRedFactor = 0x3e;
+ ps->wReduceGreenFactor = 0x39;
+ ps->wReduceBlueFactor = 0x42;
+
+ if( ps->Device.bCCDID == _CCD_518 ) {
+ ps->wReduceRedFactor = 55;
+ ps->wReduceGreenFactor = 55;
+ ps->wReduceBlueFactor = 55;
+ }
+ if( ps->Device.bCCDID == _CCD_3797 ) {
+ ps->wReduceRedFactor = 42;
+ ps->wReduceGreenFactor = 50;
+ ps->wReduceBlueFactor = 50;
+ }
+
+ tpaP98RecalculateShadingGainandData( ps );
+ }
+ }
+}
+
+/*.............................................................................
+ * perform some adjustments according to the source (normal, transparency etc)
+ */
+_LOC void TPAP98003FindCenterPointer( pScanData ps )
+{
+ ULong i;
+ ULong width;
+ ULong left;
+ ULong right;
+ pRGBUShortDef pwSum = ps->Bufs.b2.pSumRGB;
+
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative )
+ width = _NEG_PAGEWIDTH600;
+ else
+ width = _NEG_PAGEWIDTH600 - 94;
+
+ /* 2.54 cm tolerance */
+ left = ps->Device.DataOriginX + _NEG_ORG_OFFSETX * 2 - 600;
+ right = ps->Device.DataOriginX + _NEG_ORG_OFFSETX * 2 +
+ _NEG_PAGEWIDTH600 + 600;
+
+ for( i = 5400UL - left, pwSum = ps->Bufs.b2.pSumRGB; i--; left++)
+ if( pwSum[left].Red > _NEG_EDGE_VALUE &&
+ pwSum[left].Green > _NEG_EDGE_VALUE &&
+ pwSum[left].Blue > _NEG_EDGE_VALUE)
+ break;
+
+ for( i = 5400UL - left, pwSum = ps->Bufs.b2.pSumRGB; i--; right--)
+ if( pwSum[right].Red > _NEG_EDGE_VALUE &&
+ pwSum[right].Green > _NEG_EDGE_VALUE &&
+ pwSum[right].Blue > _NEG_EDGE_VALUE)
+ break;
+
+ if((right <= left) || ((right - left) < width)) {
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative )
+ ps->Scan.negBegin = ps->Device.DataOriginX + _NEG_ORG_OFFSETX * 2;
+ else
+ ps->Scan.posBegin = ps->Device.DataOriginX + _POS_ORG_OFFSETX * 2;
+ } else {
+ if( ps->DataInf.dwScanFlag & SCANDEF_Negative )
+ ps->Scan.negBegin = (right + left) / 2UL - _NEG_PAGEWIDTH;
+ else
+ ps->Scan.posBegin = (right + left) / 2UL - _POS_PAGEWIDTH;
+ }
+}
+
+/*.............................................................................
+ * this function does some reshading, when scanning negatives on an ASIC 98003
+ * based scanner
+ */
+_LOC void TPAP98003Reshading( pScanData ps )
+{
+ Byte bHi[3], bHiLeft[3], bHiRight[3];
+ ULong i, dwR, dwG, dwB, dwSum;
+ ULong dwIndex, dwIndexRight, dwIndexLeft;
+ DataPointer RedPtr, GreenPtr, BluePtr;
+ TimerDef timer;
+
+ bHi[0] = bHi[1] = bHi[2] = 0;
+
+/* CHECK: Why this ??? */
+#if 1
+ ps->Scan.negScan[1].exposureTime = 144;
+ ps->Scan.negScan[1].xStepTime = 18;
+ ps->Scan.negScan[2].exposureTime = 144;
+ ps->Scan.negScan[2].xStepTime = 36;
+ ps->Scan.negScan[3].exposureTime = 144;
+ ps->Scan.negScan[3].xStepTime = 72;
+ ps->Scan.negScan[4].exposureTime = 144;
+ ps->Scan.negScan[4].xStepTime = 144;
+#endif
+
+ ps->Shade.wExposure = ps->Scan.negScan[ps->Scan.dpiIdx].exposureTime;
+ ps->Shade.wXStep = ps->Scan.negScan[ps->Scan.dpiIdx].xStepTime;
+
+ MiscStartTimer( &timer, _SECOND );
+
+ while(!(IOGetScanState(ps, _TRUE) & _SCANSTATE_STOP) &&
+ (_OK == MiscCheckTimer(&timer)));
+
+ IODataToRegister( ps, ps->RegXStepTime,
+ (Byte)(ps->AsicReg.RD_LineControl >> 4));
+ _DODELAY( 12 );
+ MotorP98003PositionYProc( ps, _NEG_SHADING_OFFS );
+
+ IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime );
+
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+ IOSelectLampSource( ps );
+
+ IODataToRegister( ps, ps->RegLineControl, _LOBYTE(ps->Shade.wExposure));
+ IODataToRegister( ps, ps->RegXStepTime, _LOBYTE(ps->Shade.wXStep));
+
+ ps->AsicReg.RD_LineControl = _LOBYTE(ps->Shade.wExposure);
+ ps->AsicReg.RD_ExtLineControl = _HIBYTE(ps->Shade.wExposure);
+ ps->AsicReg.RD_XStepTime = (Byte)(ps->Shade.wExposure);
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_Motor0Control = _FORWARD_MOTOR;
+
+ ps->AsicReg.RD_Origin = (UShort)ps->Scan.negBegin;
+ ps->AsicReg.RD_Pixels = _NEG_PAGEWIDTH600;
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+
+ /* put 9 scan states to make sure there are 8 lines available at least */
+ for( i = 0; i <= 12; i++)
+ ps->a_nbNewAdrPointer[i] = 0x8f;
+
+ IOPutOnAllRegisters( ps );
+ _DODELAY( 70 );
+
+ /* prepare the buffers... */
+ memset( ps->Bufs.TpaBuf.pb, 0, _SizeTpaDataBuf );
+
+ RedPtr.pb = ps->Bufs.b1.pShadingMap;
+ GreenPtr.pb = RedPtr.pb + _NEG_PAGEWIDTH600;
+ BluePtr.pb = GreenPtr.pb + _NEG_PAGEWIDTH600;
+
+ for( dwSum = 8; dwSum--; ) {
+
+ IOReadOneShadingLine( ps, ps->Bufs.b1.pShadingMap, _NEG_PAGEWIDTH600 );
+
+ for( i = 0; i < _NEG_PAGEWIDTH600; i++) {
+
+ ps->Bufs.TpaBuf.pusrgb[i].Red += RedPtr.pb[i];
+ ps->Bufs.TpaBuf.pusrgb[i].Green += GreenPtr.pb[i];
+ ps->Bufs.TpaBuf.pusrgb[i].Blue += BluePtr.pb[i];
+ }
+ }
+
+ for( i = 0; i < (_NEG_PAGEWIDTH600 * 3UL); i++ )
+ ps->Bufs.TpaBuf.pb[i] = ps->Bufs.TpaBuf.pw[i] >> 3;
+
+ RedPtr.pb = ps->Bufs.TpaBuf.pb;
+
+ /* Convert RGB to gray scale (Brightness), and average 16 pixels */
+ for( bHiRight[1] = 0, i = dwIndexRight = 0;
+ i < _NEG_PAGEWIDTH600 / 2; i += 16 ) {
+ bHiRight [0] =
+ (Byte)(((((ULong) RedPtr.pbrgb [i].Red +
+ (ULong) RedPtr.pbrgb[i + 1].Red +
+ (ULong) RedPtr.pbrgb[i + 2].Red +
+ (ULong) RedPtr.pbrgb[i + 3].Red +
+ (ULong) RedPtr.pbrgb[i + 4].Red +
+ (ULong) RedPtr.pbrgb[i + 5].Red +
+ (ULong) RedPtr.pbrgb[i + 6].Red +
+ (ULong) RedPtr.pbrgb[i + 7].Red +
+ (ULong) RedPtr.pbrgb[i + 8].Red +
+ (ULong) RedPtr.pbrgb[i + 9].Red +
+ (ULong) RedPtr.pbrgb[i + 10].Red +
+ (ULong) RedPtr.pbrgb[i + 11].Red +
+ (ULong) RedPtr.pbrgb[i + 12].Red +
+ (ULong) RedPtr.pbrgb[i + 13].Red +
+ (ULong) RedPtr.pbrgb[i + 14].Red +
+ (ULong) RedPtr.pbrgb[i + 15].Red) >> 4) * 30UL +
+ (((ULong) RedPtr.pbrgb[i].Green +
+ (ULong) RedPtr.pbrgb[i + 1].Green +
+ (ULong) RedPtr.pbrgb[i + 2].Green +
+ (ULong) RedPtr.pbrgb[i + 3].Green +
+ (ULong) RedPtr.pbrgb[i + 4].Green +
+ (ULong) RedPtr.pbrgb[i + 5].Green +
+ (ULong) RedPtr.pbrgb[i + 6].Green +
+ (ULong) RedPtr.pbrgb[i + 7].Green +
+ (ULong) RedPtr.pbrgb[i + 8].Green +
+ (ULong) RedPtr.pbrgb[i + 9].Green +
+ (ULong) RedPtr.pbrgb[i + 10].Green +
+ (ULong) RedPtr.pbrgb[i + 11].Green +
+ (ULong) RedPtr.pbrgb[i + 12].Green +
+ (ULong) RedPtr.pbrgb[i + 13].Green +
+ (ULong) RedPtr.pbrgb[i + 14].Green +
+ (ULong) RedPtr.pbrgb[i + 15].Green) >> 4) * 59UL +
+ (((ULong) RedPtr.pbrgb[i].Blue +
+ (ULong) RedPtr.pbrgb[i + 1].Blue +
+ (ULong) RedPtr.pbrgb[i + 2].Blue +
+ (ULong) RedPtr.pbrgb[i + 3].Blue +
+ (ULong) RedPtr.pbrgb[i + 4].Blue +
+ (ULong) RedPtr.pbrgb[i + 5].Blue +
+ (ULong) RedPtr.pbrgb[i + 6].Blue +
+ (ULong) RedPtr.pbrgb[i + 7].Blue +
+ (ULong) RedPtr.pbrgb[i + 8].Blue +
+ (ULong) RedPtr.pbrgb[i + 9].Blue +
+ (ULong) RedPtr.pbrgb[i + 10].Blue +
+ (ULong) RedPtr.pbrgb[i + 11].Blue +
+ (ULong) RedPtr.pbrgb[i + 12].Blue +
+ (ULong) RedPtr.pbrgb[i + 13].Blue +
+ (ULong) RedPtr.pbrgb[i + 14].Blue +
+ (ULong) RedPtr.pbrgb[i + 15].Blue) >> 4) * 11UL) / 100UL);
+
+ if( bHiRight[1] < bHiRight[0] ) {
+ bHiRight[1] = bHiRight[0];
+ dwIndexRight = i;
+ }
+ }
+
+ /* Convert RGB to gray scale (Brightness), and average 16 pixels */
+ for( bHiLeft[1] = 0, i = dwIndexLeft = _NEG_PAGEWIDTH / 2;
+ i < _NEG_PAGEWIDTH600; i += 16 ) {
+ bHiLeft [0] =
+ (Byte)(((((ULong) RedPtr.pbrgb[i].Red +
+ (ULong) RedPtr.pbrgb[i + 1].Red +
+ (ULong) RedPtr.pbrgb[i + 2].Red +
+ (ULong) RedPtr.pbrgb[i + 3].Red +
+ (ULong) RedPtr.pbrgb[i + 4].Red +
+ (ULong) RedPtr.pbrgb[i + 5].Red +
+ (ULong) RedPtr.pbrgb[i + 6].Red +
+ (ULong) RedPtr.pbrgb[i + 7].Red +
+ (ULong) RedPtr.pbrgb[i + 8].Red +
+ (ULong) RedPtr.pbrgb[i + 9].Red +
+ (ULong) RedPtr.pbrgb[i + 10].Red +
+ (ULong) RedPtr.pbrgb[i + 11].Red +
+ (ULong) RedPtr.pbrgb[i + 12].Red +
+ (ULong) RedPtr.pbrgb[i + 13].Red +
+ (ULong) RedPtr.pbrgb[i + 14].Red +
+ (ULong) RedPtr.pbrgb[i + 15].Red) >> 4) * 30UL +
+ (((ULong) RedPtr.pbrgb[i].Green +
+ (ULong) RedPtr.pbrgb[i + 1].Green +
+ (ULong) RedPtr.pbrgb[i + 2].Green +
+ (ULong) RedPtr.pbrgb[i + 3].Green +
+ (ULong) RedPtr.pbrgb[i + 4].Green +
+ (ULong) RedPtr.pbrgb[i + 5].Green +
+ (ULong) RedPtr.pbrgb[i + 6].Green +
+ (ULong) RedPtr.pbrgb[i + 7].Green +
+ (ULong) RedPtr.pbrgb[i + 8].Green +
+ (ULong) RedPtr.pbrgb[i + 9].Green +
+ (ULong) RedPtr.pbrgb[i + 10].Green +
+ (ULong) RedPtr.pbrgb[i + 11].Green +
+ (ULong) RedPtr.pbrgb[i + 12].Green +
+ (ULong) RedPtr.pbrgb[i + 13].Green +
+ (ULong) RedPtr.pbrgb[i + 14].Green +
+ (ULong) RedPtr.pbrgb[i + 15].Green) >> 4) * 59UL +
+ (((ULong) RedPtr.pbrgb[i].Blue +
+ (ULong) RedPtr.pbrgb[i + 1].Blue +
+ (ULong) RedPtr.pbrgb[i + 2].Blue +
+ (ULong) RedPtr.pbrgb[i + 3].Blue +
+ (ULong) RedPtr.pbrgb[i + 4].Blue +
+ (ULong) RedPtr.pbrgb[i + 5].Blue +
+ (ULong) RedPtr.pbrgb[i + 6].Blue +
+ (ULong) RedPtr.pbrgb[i + 7].Blue +
+ (ULong) RedPtr.pbrgb[i + 8].Blue +
+ (ULong) RedPtr.pbrgb[i + 9].Blue +
+ (ULong) RedPtr.pbrgb[i + 10].Blue +
+ (ULong) RedPtr.pbrgb[i + 11].Blue +
+ (ULong) RedPtr.pbrgb[i + 12].Blue +
+ (ULong) RedPtr.pbrgb[i + 13].Blue +
+ (ULong) RedPtr.pbrgb[i + 14].Blue +
+ (ULong) RedPtr.pbrgb[i + 15].Blue) >> 4) * 11UL) / 100UL);
+
+ if( bHiLeft[1] < bHiLeft[0] ) {
+ bHiLeft[1] = bHiLeft[0];
+ dwIndexLeft = i;
+ }
+ }
+
+ if((bHiLeft[1] < 200) && (bHiRight[1] < 200)) {
+
+ if( bHiLeft[1] < bHiRight[1] )
+ dwIndex = dwIndexRight;
+ else
+ dwIndex = dwIndexLeft;
+ } else {
+ if( bHiLeft[1] > 200 )
+ dwIndex = dwIndexRight;
+ else
+ dwIndex = dwIndexLeft;
+ }
+
+ /* Get the hilight */
+ RedPtr.pusrgb = ps->Bufs.b2.pSumRGB + dwIndex +
+ ps->AsicReg.RD_Origin + _SHADING_BEGINX;
+
+ for( dwR = dwG = dwB = 0, i = 16; i--; RedPtr.pusrgb++ ) {
+ dwR += RedPtr.pusrgb->Red;
+ dwG += RedPtr.pusrgb->Green;
+ dwB += RedPtr.pusrgb->Blue;
+ }
+
+ dwR >>= 8;
+ dwG >>= 8;
+ dwB >>= 8;
+
+ if( dwR > dwG && dwR > dwB )
+ ps->Shade.bGainHigh = (Byte)dwR; /* >> 4 for average, >> 4 to 8-bit */
+ else {
+ if( dwG > dwR && dwG > dwB )
+ ps->Shade.bGainHigh = (Byte)dwG;
+ else
+ ps->Shade.bGainHigh = (Byte)dwB;
+ }
+
+ ps->Shade.bGainHigh = (Byte)(ps->Shade.bGainHigh - 0x18);
+ ps->Shade.bGainLow = (Byte)(ps->Shade.bGainHigh - 0x10);
+
+ /* Reshading to get the new gain */
+ ps->Shade.Hilight.Colors.Red = 0;
+ ps->Shade.Hilight.Colors.Green = 0;
+ ps->Shade.Hilight.Colors.Blue = 0;
+ ps->Shade.Gain.Colors.Red++;
+ ps->Shade.Gain.Colors.Green++;
+ ps->Shade.Gain.Colors.Blue++;
+ ps->Shade.fStop = _FALSE;
+
+ RedPtr.pb = ps->Bufs.b1.pShadingMap + dwIndex;
+ GreenPtr.pb = RedPtr.pb + _NEG_PAGEWIDTH600;
+ BluePtr.pb = GreenPtr.pb + _NEG_PAGEWIDTH600;
+
+ for( i = 16; i-- && !ps->Shade.fStop;) {
+
+ ps->Shade.fStop = _TRUE;
+
+ DacP98003FillToDAC( ps, &ps->Device.RegDACGain, &ps->Shade.Gain );
+
+ IODataToRegister( ps, ps->RegModeControl, _ModeIdle );
+
+ ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
+ IOSelectLampSource( ps );
+
+ ps->AsicReg.RD_ModeControl = _ModeScan;
+ ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
+ ps->AsicReg.RD_Motor0Control = _FORWARD_MOTOR;
+
+ memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
+ ps->a_nbNewAdrPointer[1] = 0x77;
+
+ IOPutOnAllRegisters( ps );
+ _DODELAY( 50 );
+
+ if(IOReadOneShadingLine(ps,ps->Bufs.b1.pShadingMap,_NEG_PAGEWIDTH600)) {
+
+ bHi[0] = DacP98003SumGains( RedPtr.pb, 32 );
+ bHi[1] = DacP98003SumGains( GreenPtr.pb, 32 );
+ bHi[2] = DacP98003SumGains( BluePtr.pb, 32 );
+
+ if( !bHi[0] || !bHi[1] || !bHi[2]) {
+ ps->Shade.fStop = _FALSE;
+ } else {
+
+ DacP98003AdjustGain( ps, _CHANNEL_RED, bHi[0] );
+ DacP98003AdjustGain( ps, _CHANNEL_GREEN, bHi[1] );
+ DacP98003AdjustGain( ps, _CHANNEL_BLUE, bHi[2] );
+ }
+ } else
+ ps->Shade.fStop = _FALSE;
+ }
+
+ DacP98003FillToDAC( ps, &ps->Device.RegDACGain, &ps->Shade.Gain );
+
+ /* Set RGB Gain */
+ if( dwR && dwG && dwB ) {
+
+ if(ps->Device.bCCDID == _CCD_3797 || ps->Device.bDACType == _DA_ESIC) {
+ ps->Shade.pCcdDac->GainResize.Colors.Red =
+ (UShort)((ULong)bHi[0] * 100UL / dwR);
+ ps->Shade.pCcdDac->GainResize.Colors.Green =
+ (UShort)((ULong)bHi[1] * 100UL / dwG);
+ ps->Shade.pCcdDac->GainResize.Colors.Blue =
+ (UShort)((ULong)bHi[2] * 100UL / dwB);
+ } else {
+ ps->Shade.pCcdDac->GainResize.Colors.Red =
+ (UShort)((ULong)bHi[0] * 90UL / dwR);
+ ps->Shade.pCcdDac->GainResize.Colors.Green =
+ (UShort)((ULong)bHi[1] * 77UL / dwG);
+ ps->Shade.pCcdDac->GainResize.Colors.Blue =
+ (UShort)((ULong)bHi[2] * 73UL / dwB);
+ }
+ ps->Shade.DarkOffset.Colors.Red +=
+ (UShort)((dwR > bHi[0]) ? dwR - bHi[0] : 0);
+ ps->Shade.DarkOffset.Colors.Green +=
+ (UShort)((dwG > bHi[1]) ? dwG - bHi[1] : 0);
+ ps->Shade.DarkOffset.Colors.Blue +=
+ (UShort)((dwB > bHi[2]) ? dwB - bHi[2] : 0);
+
+ if( ps->Device.bDACType != _DA_ESIC && ps->Device.bCCDID != _CCD_3799 ) {
+ ps->Shade.DarkOffset.Colors.Red =
+ (UShort)(ps->Shade.DarkOffset.Colors.Red *
+ ps->Shade.pCcdDac->GainResize.Colors.Red / 100UL);
+ ps->Shade.DarkOffset.Colors.Green =
+ (UShort)(ps->Shade.DarkOffset.Colors.Green *
+ ps->Shade.pCcdDac->GainResize.Colors.Green / 100UL);
+ ps->Shade.DarkOffset.Colors.Blue =
+ (UShort)(ps->Shade.DarkOffset.Colors.Blue *
+ ps->Shade.pCcdDac->GainResize.Colors.Blue / 100UL);
+ }
+ }
+
+ /* AdjustDark () */
+ ps->AsicReg.RD_Origin = _SHADING_BEGINX;
+ ps->AsicReg.RD_Pixels = 5400;
+}
+
+/* END PLUSTEK-PP_TPA.C .....................................................*/
diff --git a/backend/plustek-pp_types.h b/backend/plustek-pp_types.h
new file mode 100644
index 0000000..8cc1f8b
--- /dev/null
+++ b/backend/plustek-pp_types.h
@@ -0,0 +1,191 @@
+/* @file plustek-pp_types.h
+ * @brief some typedefs and error codes
+ *
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * 0.30 - initial version
+ * 0.31 - no changes
+ * 0.32 - added _VAR_NOT_USED()
+ * 0.33 - no changes
+ * 0.34 - no changes
+ * 0.35 - no changes
+ * 0.36 - added _E_ABORT and _E_VERSION
+ * 0.37 - moved _MAX_DEVICES to plustek_scan.h
+ * added pChar and TabDef
+ * 0.38 - comment change for _E_NOSUPP
+ * added RGBByteDef, RGBWordDef and RGBULongDef
+ * replaced AllPointer by DataPointer
+ * replaced AllType by DataType
+ * added _LOBYTE and _HIBYTE stuff
+ * added _E_NO_ASIC and _E_NORESOURCE
+ * 0.39 - no changes
+ * 0.40 - moved _VAR_NOT_USED and TabDef to plustek-share.h
+ * 0.41 - no changes
+ * 0.42 - moved errorcodes to plustek-share.h
+ * 0.43 - no changes
+ * 0.44 - define Long and ULong types to use int32_t, so
+ * the code should still work on 64 bit machines
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __DRV_TYPES_H__
+#define __DRV_TYPES_H__
+
+/* define some useful types */
+typedef int Bool;
+typedef char Char;
+typedef char *pChar;
+typedef unsigned char UChar;
+typedef UChar *pUChar;
+typedef unsigned char Byte;
+typedef Byte *pByte;
+
+typedef short Short;
+typedef unsigned short UShort;
+typedef UShort *pUShort;
+
+typedef unsigned int UInt;
+typedef UInt *pUInt;
+
+/* these definitions will fail for 64 bit machines! */
+#if 0
+typedef long Long;
+typedef long *pLong;
+typedef unsigned long ULong;
+#endif
+
+typedef int32_t Long;
+typedef int32_t *pLong;
+typedef uint32_t ULong;
+typedef ULong *pULong;
+
+typedef void *pVoid;
+
+/*
+ * the boolean values
+ */
+#ifndef _TRUE
+# define _TRUE 1
+#endif
+#ifndef _FALSE
+# define _FALSE 0
+#endif
+
+#define _LOWORD(x) ((UShort)(x & 0xffff))
+#define _HIWORD(x) ((UShort)(x >> 16))
+#define _LOBYTE(x) ((Byte)((x) & 0xFF))
+#define _HIBYTE(x) ((Byte)((x) >> 8))
+
+/*
+ * some useful things...
+ */
+typedef struct
+{
+ Byte b1st;
+ Byte b2nd;
+} WordVal, *pWordVal;
+
+typedef struct
+{
+ WordVal w1st;
+ WordVal w2nd;
+} DWordVal, *pDWordVal;
+
+/* useful for RGB-values */
+typedef struct {
+ Byte Red;
+ Byte Green;
+ Byte Blue;
+} RGBByteDef, *pRGBByteDef;
+
+typedef struct {
+ UShort Red;
+ UShort Green;
+ UShort Blue;
+} RGBUShortDef, *pRGBUShortDef;
+
+typedef struct {
+
+ union {
+ pUChar bp;
+ pUShort usp;
+ pULong ulp;
+ } red;
+ union {
+ pUChar bp;
+ pUShort usp;
+ pULong ulp;
+ } green;
+ union {
+ pUChar bp;
+ pUShort usp;
+ pULong ulp;
+ } blue;
+
+} RBGPtrDef;
+
+typedef struct {
+ ULong Red;
+ ULong Green;
+ ULong Blue;
+} RGBULongDef, *pRGBULongDef;
+
+typedef union {
+ pUChar pb;
+ pUShort pw;
+ pULong pdw;
+ pRGBByteDef pbrgb;
+ pRGBUShortDef pusrgb;
+ pRGBULongDef pulrgb;
+} DataPointer, *pDataPointer;
+
+typedef union {
+ WordVal wOverlap;
+ DWordVal dwOverlap;
+ ULong dwValue;
+ UShort wValue;
+ Byte bValue;
+} DataType, *pDataType;
+
+#endif /* guard __DRV_TYPES_H__ */
+
+/* END PLUSTEK-PP_TYPES.H ...................................................*/
diff --git a/backend/plustek-pp_wrapper.c b/backend/plustek-pp_wrapper.c
new file mode 100644
index 0000000..ba635ef
--- /dev/null
+++ b/backend/plustek-pp_wrapper.c
@@ -0,0 +1,361 @@
+/** @file plustek-pp_wrapper.c
+ * @brief The interface to the parport driver-code and the kernel module.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - initial version
+ * - 0.41 - added _PTDRV_ADJUST call
+ * - 0.42 - added setmap function
+ * - fixed the stopscan problem, that causes a crash in the kernel module
+ * - 0.43 - added initialized setting
+ * - cleanup
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/******************* wrapper functions for parport device ********************/
+
+#ifndef _BACKEND_ENABLED
+
+static int PtDrvInit( char *dev_name, unsigned short model_override )
+{
+ _VAR_NOT_USED( dev_name );
+ _VAR_NOT_USED( model_override );
+ DBG( _DBG_ERROR, "Backend does not support direct I/O!\n" );
+ return -1;
+}
+
+static int PtDrvShutdown( void )
+{
+ return 0;
+}
+
+static int PtDrvOpen( void )
+{
+ DBG( _DBG_ERROR, "Backend does not support direct I/O!\n" );
+ return -1;
+}
+
+static int PtDrvClose( void )
+{
+ DBG( _DBG_ERROR, "Backend does not support direct I/O!\n" );
+ return 0;
+}
+
+static int PtDrvIoctl( unsigned int cmd, void *arg )
+{
+ DBG( _DBG_ERROR, "Backend does not support direct I/O!\n" );
+ _VAR_NOT_USED( cmd );
+ if( arg == NULL )
+ return -2;
+ return -1;
+}
+
+static int PtDrvRead( unsigned char *buffer, int count )
+{
+ DBG( _DBG_ERROR, "Backend does not support direct I/O!\n" );
+ _VAR_NOT_USED( count );
+ if( buffer == NULL )
+ return -2;
+ return -1;
+}
+#endif /* _BACKEND_ENABLED */
+
+/**
+ */
+static int ppDev_open( const char *dev_name, void *misc )
+{
+ int result;
+ int handle;
+ CompatAdjDef compatAdj;
+ PPAdjDef adj;
+ unsigned short version = _PTDRV_IOCTL_VERSION;
+ Plustek_Device *dev = (Plustek_Device *)misc;
+
+ if( dev->adj.direct_io ) {
+ result = PtDrvInit( dev_name, dev->adj.mov );
+ if( 0 != result ) {
+ DBG( _DBG_ERROR, "open: PtDrvInit failed: %d\n", result );
+ return -1;
+ }
+ }
+
+ if( dev->adj.direct_io )
+ handle = PtDrvOpen();
+ else
+ handle = open( dev_name, O_RDONLY );
+
+ if ( handle < 0 ) {
+ DBG( _DBG_ERROR, "open: can't open %s as a device\n", dev_name );
+ return handle;
+ }
+
+ if( dev->adj.direct_io )
+ result = PtDrvIoctl( _PTDRV_OPEN_DEVICE, &version );
+ else
+ result = ioctl( handle, _PTDRV_OPEN_DEVICE, &version );
+
+ if( result < 0 ) {
+
+ if( -9019 == result ) {
+
+ DBG( _DBG_INFO, "Version 0x%04x not supported, trying "
+ "compatibility version 0x%04x\n",
+ _PTDRV_IOCTL_VERSION, _PTDRV_COMPAT_IOCTL_VERSION);
+
+ version = _PTDRV_COMPAT_IOCTL_VERSION;
+
+ if( dev->adj.direct_io )
+ result = PtDrvIoctl( _PTDRV_OPEN_DEVICE, &version );
+ else
+ result = ioctl( handle, _PTDRV_OPEN_DEVICE, &version );
+
+ if( result < 0 ) {
+
+ if( dev->adj.direct_io )
+ PtDrvClose();
+ else
+ close( dev->fd );
+
+ DBG( _DBG_ERROR,
+ "ioctl PT_DRV_OPEN_DEVICE failed(%d)\n", result );
+
+ if( -9019 == result ) {
+ DBG( _DBG_ERROR,
+ "Version problem, please recompile driver!\n" );
+ }
+ } else {
+
+ DBG( _DBG_INFO, "Using compatibility version\n" );
+
+ compatAdj.lampOff = dev->adj.lampOff;
+ compatAdj.lampOffOnEnd = dev->adj.lampOffOnEnd;
+ compatAdj.warmup = dev->adj.warmup;
+
+ memcpy( &compatAdj.pos, &dev->adj.pos, sizeof(OffsDef));
+ memcpy( &compatAdj.neg, &dev->adj.neg, sizeof(OffsDef));
+ memcpy( &compatAdj.tpa, &dev->adj.tpa, sizeof(OffsDef));
+
+ if( dev->adj.direct_io )
+ PtDrvIoctl( _PTDRV_ADJUST, &compatAdj );
+ else
+ ioctl( handle, _PTDRV_ADJUST, &compatAdj );
+ return handle;
+ }
+ }
+ return result;
+ }
+
+ memset( &adj, 0, sizeof(PPAdjDef));
+
+ adj.lampOff = dev->adj.lampOff;
+ adj.lampOffOnEnd = dev->adj.lampOffOnEnd;
+ adj.warmup = dev->adj.warmup;
+
+ memcpy( &adj.pos, &dev->adj.pos, sizeof(OffsDef));
+ memcpy( &adj.neg, &dev->adj.neg, sizeof(OffsDef));
+ memcpy( &adj.tpa, &dev->adj.tpa, sizeof(OffsDef));
+
+ adj.rgamma = dev->adj.rgamma;
+ adj.ggamma = dev->adj.ggamma;
+ adj.bgamma = dev->adj.bgamma;
+ adj.graygamma = dev->adj.graygamma;
+
+ if( dev->adj.direct_io )
+ PtDrvIoctl( _PTDRV_ADJUST, &adj );
+ else
+ ioctl( handle, _PTDRV_ADJUST, &adj );
+
+ dev->initialized = SANE_TRUE;
+ return handle;
+}
+
+/**
+ */
+static int ppDev_close( Plustek_Device *dev )
+{
+ if( dev->adj.direct_io )
+ return PtDrvClose();
+ else
+ return close( dev->fd );
+}
+
+/**
+ */
+static int ppDev_getCaps( Plustek_Device *dev )
+{
+ if( dev->adj.direct_io )
+ return PtDrvIoctl( _PTDRV_GET_CAPABILITIES, &dev->caps );
+ else
+ return ioctl( dev->fd, _PTDRV_GET_CAPABILITIES, &dev->caps );
+}
+
+/**
+ */
+static int ppDev_getLensInfo( Plustek_Device *dev, pLensInfo lens )
+{
+ if( dev->adj.direct_io )
+ return PtDrvIoctl( _PTDRV_GET_LENSINFO, lens );
+ else
+ return ioctl( dev->fd, _PTDRV_GET_LENSINFO, lens );
+}
+
+/**
+ */
+static int ppDev_getCropInfo( Plustek_Device *dev, pCropInfo crop )
+{
+ if( dev->adj.direct_io )
+ return PtDrvIoctl( _PTDRV_GET_CROPINFO, crop );
+ else
+ return ioctl( dev->fd, _PTDRV_GET_CROPINFO, crop );
+}
+
+/**
+ */
+static int ppDev_putImgInfo( Plustek_Device *dev, pImgDef img )
+{
+ if( dev->adj.direct_io )
+ return PtDrvIoctl( _PTDRV_PUT_IMAGEINFO, img );
+ else
+ return ioctl( dev->fd, _PTDRV_PUT_IMAGEINFO, img );
+}
+
+/**
+ */
+static int ppDev_setScanEnv( Plustek_Device *dev, pScanInfo sinfo )
+{
+ if( dev->adj.direct_io )
+ return PtDrvIoctl( _PTDRV_SET_ENV, sinfo );
+ else
+ return ioctl( dev->fd, _PTDRV_SET_ENV, sinfo );
+}
+
+/**
+ */
+static int ppDev_startScan( Plustek_Device *dev, pStartScan start )
+{
+ if( dev->adj.direct_io )
+ return PtDrvIoctl( _PTDRV_START_SCAN, start );
+ else
+ return ioctl( dev->fd, _PTDRV_START_SCAN, start );
+}
+
+/** function to send a gamma table to the kernel module. As the default table
+ * entry is 16-bit, but the maps are 8-bit, we have to copy the values...
+ */
+static int ppDev_setMap( Plustek_Device *dev, SANE_Word *map,
+ SANE_Word length, SANE_Word channel )
+{
+ SANE_Byte *buf;
+ SANE_Word i;
+ MapDef m;
+
+ m.len = length;
+ m.map_id = channel;
+
+ m.map = (void *)map;
+
+ DBG(_DBG_INFO,"Setting map[%u] at 0x%08lx\n", channel, (unsigned long)map);
+
+ buf = (SANE_Byte*)malloc( m.len );
+
+ if( !buf )
+ return _E_ALLOC;
+
+ for( i = 0; i < m.len; i++ ) {
+ buf[i] = (SANE_Byte)map[i];
+
+ if( map[i] > 0xFF )
+ buf[i] = 0xFF;
+ }
+
+ m.map = buf;
+
+ if( dev->adj.direct_io )
+ PtDrvIoctl( _PTDRV_SETMAP, &m );
+ else
+ ioctl( dev->fd, _PTDRV_SETMAP, &m );
+
+ /* we ignore the return values */
+ free( buf );
+ return 0;
+}
+
+/**
+ */
+static int ppDev_stopScan( Plustek_Device *dev, short *mode )
+{
+ int retval, tmp;
+
+ /* save this one... */
+ tmp = *mode;
+
+ if( dev->adj.direct_io )
+ retval = PtDrvIoctl( _PTDRV_STOP_SCAN, mode );
+ else
+ retval = ioctl( dev->fd, _PTDRV_STOP_SCAN, mode );
+
+ /* ... and use it here */
+ if( 0 == tmp ) {
+ if( dev->adj.direct_io )
+ PtDrvIoctl( _PTDRV_CLOSE_DEVICE, 0 );
+ else
+ ioctl( dev->fd, _PTDRV_CLOSE_DEVICE, 0);
+ }else
+ sleep( 1 );
+
+ return retval;
+}
+
+/**
+ */
+static int ppDev_readImage( Plustek_Device *dev,
+ SANE_Byte *buf, unsigned long data_length )
+{
+ if( dev->adj.direct_io )
+ return PtDrvRead( buf, data_length );
+ else
+ return read( dev->fd, buf, data_length );
+}
+
+/* END PLUSTEK-PP_WRAPPER.C .................................................*/
diff --git a/backend/plustek-usb.c b/backend/plustek-usb.c
new file mode 100644
index 0000000..6c9e67a
--- /dev/null
+++ b/backend/plustek-usb.c
@@ -0,0 +1,1526 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usb.c
+ * @brief The interface functions to the USB driver stuff.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - removed CHECK
+ * - added Canon to the manufacturer list
+ * - 0.42 - added warmup stuff
+ * - added setmap function
+ * - changed detection stuff, so we first check whether
+ * - the vendor and product Ids match with the ones in our list
+ * - 0.43 - cleanup
+ * - 0.44 - changes to integration CIS based devices
+ * - 0.45 - added skipFine assignment
+ * - added auto device name detection if only product and vendor id<br>
+ * has been specified
+ * - made 16-bit gray mode work
+ * - added special handling for Genius devices
+ * - added TPA autodetection for EPSON Photo
+ * - fixed bug that causes warmup each time autodetected<br>
+ * TPA on EPSON is used
+ * - removed Genius from PCB-Id check
+ * - added Compaq to the list
+ * - removed the scaler stuff for CIS devices
+ * - removed homeing stuff from readline function
+ * - fixed flag setting in usbDev_startScan()
+ * - 0.46 - added additional branch to support alternate calibration
+ * - 0.47 - added special handling with 0x400 vendor ID and model override
+ * - removed PATH_MAX
+ * - change usbDev_stopScan and usbDev_open prototype
+ * - cleanup
+ * - 0.48 - added function usb_CheckAndCopyAdjs()
+ * - 0.49 - changed autodetection
+ * - added support for LiDE25 (pid 0x2220)
+ * - 0.50 - minor fix for startup reset
+ * removed unnecessary calls to usbio_ResetLM983x()
+ * 1200DPI CIS devices don't use GrayFromColor any longer
+ * - 0.51 - added Syscan to the vendor list
+ * - added SCANFLAG_Calibration handling
+ * - 0.52 - added _WAF_LOFF_ON_START and _WAF_INC_DARKTGT
+ * handling in usbDev_startScan()
+ * added Visioneer
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/** useful for description tables
+ */
+typedef struct {
+ int id;
+ char *desc;
+ char *desc_alt;
+} TabDef, *pTabDef;
+
+/** to allow different vendors...
+ */
+static TabDef usbVendors[] = {
+
+ { 0x07B3, "Plustek", NULL },
+ { 0x0400, "NSC", "Mustek" },
+ { 0x0458, "KYE/Genius", NULL },
+ { 0x03F0, "Hewlett-Packard", NULL },
+ { 0x04B8, "Epson", NULL },
+ { 0x04A7, "Visioneer", NULL },
+ { 0x04A9, "Canon", NULL },
+ { 0x1606, "UMAX", NULL },
+ { 0x049F, "Compaq", NULL },
+ { 0x0A82, "Syscan", NULL },
+ { 0x0A53, "PandP Co., Ltd.", NULL },
+ { 0xFFFF, NULL, NULL }
+};
+
+/** we use at least 8 megs for scanning... */
+#define _SCANBUF_SIZE (8 * 1024 * 1024)
+
+/********************** the USB scanner interface ****************************/
+
+/** remove the slash out of the model-name to obtain a valid filename
+ */
+static SANE_Bool usb_normFileName( char *fname, char* buffer, u_long max_len )
+{
+ char *src, *dst;
+
+ if( NULL == fname )
+ return SANE_FALSE;
+
+ if( strlen( fname ) >= max_len )
+ return SANE_FALSE;
+
+ src = fname;
+ dst = buffer;
+ while( *src != '\0' ) {
+
+ if((*src == '/') || isspace(*src) || ispunct(*src))
+ *dst = '_';
+ else
+ *dst = *src;
+
+ dst++;
+ src++;
+ }
+ *dst = '\0';
+
+ return SANE_TRUE;
+}
+
+/** do some range checking and copy the adjustment values from the
+ * frontend to our internal structures, so that the backend can take
+ * care of them.
+ */
+static void usb_CheckAndCopyAdjs( Plustek_Device *dev )
+{
+ if( dev->adj.lampOff >= 0 )
+ dev->usbDev.dwLampOnPeriod = dev->adj.lampOff;
+
+ if( dev->adj.lampOffOnEnd >= 0 )
+ dev->usbDev.bLampOffOnEnd = dev->adj.lampOffOnEnd;
+
+ if( dev->adj.skipCalibration > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_BYPASS_CALIBRATION;
+
+ if( dev->adj.skipFine > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_SKIP_FINE;
+
+ if( dev->adj.skipFineWhite > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_SKIP_WHITEFINE;
+
+ if( dev->adj.incDarkTgt > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_INC_DARKTGT;
+
+ if( dev->adj.skipDarkStrip > 0 )
+ dev->usbDev.Caps.Normal.DarkShadOrgY = -1;
+
+ if( dev->adj.invertNegatives > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_INV_NEGATIVE_MAP;
+}
+
+/**
+ * assign the values to the structures used by the currently found scanner
+ */
+static void
+usb_initDev( Plustek_Device *dev, int idx, int handle, int vendor )
+{
+ char *ptr;
+ char tmp_str1[PATH_MAX];
+ char tmp_str2[PATH_MAX];
+ int i;
+ ScanParam sParam;
+ u_short tmp = 0;
+
+ DBG( _DBG_INFO, "usb_initDev(%d,0x%04x,%i)\n",
+ idx, vendor, dev->initialized );
+ /* save capability flags... */
+ if( dev->initialized >= 0 ) {
+ tmp = DEVCAPSFLAG_TPA;
+ }
+
+ /* copy the original values... */
+ memcpy( &dev->usbDev.Caps, Settings[idx].pDevCaps, sizeof(DCapsDef));
+ memcpy( &dev->usbDev.HwSetting, Settings[idx].pHwDef, sizeof(HWDef));
+
+ /* restore capability flags... */
+ if( dev->initialized >= 0 ) {
+ dev->usbDev.Caps.wFlags |= tmp;
+ }
+
+ usb_CheckAndCopyAdjs( dev );
+ DBG( _DBG_INFO, "Device WAF : 0x%08lx\n", dev->usbDev.Caps.workaroundFlag );
+ DBG( _DBG_INFO, "Transferrate: %lu Bytes/s\n", dev->transferRate );
+
+ /* adjust data origin
+ */
+ dev->usbDev.Caps.Positive.DataOrigin.x -= dev->adj.tpa.x;
+ dev->usbDev.Caps.Positive.DataOrigin.y -= dev->adj.tpa.y;
+
+ dev->usbDev.Caps.Negative.DataOrigin.x -= dev->adj.neg.x;
+ dev->usbDev.Caps.Negative.DataOrigin.y -= dev->adj.neg.y;
+
+ dev->usbDev.Caps.Normal.DataOrigin.x -= dev->adj.pos.x;
+ dev->usbDev.Caps.Normal.DataOrigin.y -= dev->adj.pos.y;
+
+ /** adjust shading position
+ */
+ if( dev->adj.posShadingY >= 0 )
+ dev->usbDev.Caps.Normal.ShadingOriginY = dev->adj.posShadingY;
+
+ if( dev->adj.tpaShadingY >= 0 )
+ dev->usbDev.Caps.Positive.ShadingOriginY = dev->adj.tpaShadingY;
+
+ if( dev->adj.negShadingY >= 0 )
+ dev->usbDev.Caps.Negative.ShadingOriginY = dev->adj.negShadingY;
+
+ /* adjust the gamma settings... */
+ if( dev->adj.rgamma == 1.0 )
+ dev->adj.rgamma = dev->usbDev.HwSetting.gamma;
+ if( dev->adj.ggamma == 1.0 )
+ dev->adj.ggamma = dev->usbDev.HwSetting.gamma;
+ if( dev->adj.bgamma == 1.0 )
+ dev->adj.bgamma = dev->usbDev.HwSetting.gamma;
+ if( dev->adj.graygamma == 1.0 )
+ dev->adj.graygamma = dev->usbDev.HwSetting.gamma;
+
+ /* the following you normally get from the registry...
+ */
+ bMaxITA = 0; /* Maximum integration time adjust */
+
+ dev->usbDev.ModelStr = Settings[idx].pModelString;
+
+ dev->fd = handle;
+
+ if( dev->initialized < 0 ) {
+ if( usb_HasTPA( dev ))
+ dev->usbDev.Caps.wFlags |= DEVCAPSFLAG_TPA;
+ }
+ DBG( _DBG_INFO, "Device Flags: 0x%08x\n", dev->usbDev.Caps.wFlags );
+
+ /* well now we patch the vendor string...
+ * if not found, the default vendor will be Plustek
+ */
+ for( i = 0; usbVendors[i].desc != NULL; i++ ) {
+
+ if( usbVendors[i].id == vendor ) {
+ dev->sane.vendor = usbVendors[i].desc;
+ if (dev->usbDev.Caps.workaroundFlag & _WAF_USE_ALT_DESC )
+ if (usbVendors[i].desc_alt )
+ dev->sane.vendor = usbVendors[i].desc_alt;
+ DBG( _DBG_INFO, "Vendor adjusted to: >%s<\n", dev->sane.vendor );
+ break;
+ }
+ }
+
+ dev->usbDev.dwTicksLampOn = 0;
+ dev->usbDev.currentLamp = usb_GetLampStatus( dev );
+ usb_ResetRegisters( dev );
+
+ if( dev->initialized >= 0 )
+ return;
+
+ usb_IsScannerReady( dev );
+
+ sParam.bBitDepth = 8;
+ sParam.bCalibration = PARAM_Scan;
+ sParam.bChannels = 3;
+ sParam.bDataType = SCANDATATYPE_Color;
+ sParam.bSource = SOURCE_Reflection;
+ sParam.Origin.x = 0;
+ sParam.Origin.y = 0;
+ sParam.UserDpi.x = 150;
+ sParam.UserDpi.y = 150;
+ sParam.dMCLK = 4;
+ sParam.Size.dwPixels = 0;
+
+ /* create calibration-filename */
+ sprintf( tmp_str2, "%s-%s",
+ dev->sane.vendor, dev->usbDev.ModelStr );
+
+ if( !usb_normFileName( tmp_str2, tmp_str1, PATH_MAX )) {
+ strcpy( tmp_str1, "plustek-default" );
+ }
+
+ ptr = getenv ("HOME");
+ if( NULL == ptr ) {
+ sprintf( tmp_str2, "/tmp/%s", tmp_str1 );
+ } else {
+ sprintf( tmp_str2, "%s/.sane/%s", ptr, tmp_str1 );
+ }
+ dev->calFile = strdup( tmp_str2 );
+ DBG( _DBG_INFO, "Calibration file-names set to:\n" );
+ DBG( _DBG_INFO, ">%s-coarse.cal<\n", dev->calFile );
+ DBG( _DBG_INFO, ">%s-fine.cal<\n", dev->calFile );
+
+ /* initialize the ASIC registers */
+ usb_SetScanParameters( dev, &sParam );
+
+ /* check and move sensor to its home position */
+ usb_ModuleToHome( dev, SANE_FALSE );
+
+ /* set the global flag, that we are initialized so far */
+ dev->initialized = idx;
+}
+
+/**
+ * will be used for retrieving a Plustek device
+ */
+static int usb_CheckForPlustekDevice( int handle, Plustek_Device *dev )
+{
+ char tmp[50];
+ char pcbStr[10];
+ u_char reg59[3], reg59s[3], pcbID;
+ int i, result;
+
+ /*
+ * Plustek uses the misc IO 12 to get the PCB ID
+ * (PCB = printed circuit board), so it's possible to have one
+ * product ID and up to 7 different devices...
+ */
+ DBG( _DBG_INFO, "Trying to get the pcbID of a Plustek device...\n" );
+
+ /* get the PCB-ID */
+ result = sanei_lm983x_read( handle, 0x59, reg59s, 3, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ reg59[0] = 0x22; /* PIO1: Input, PIO2: Input */
+ reg59[1] = 0x02; /* PIO3: Input, PIO4: Output as low */
+ reg59[2] = 0x03;
+
+ result = sanei_lm983x_write( handle, 0x59, reg59, 3, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ result = sanei_lm983x_read ( handle, 0x02, &pcbID, 1, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ pcbID = (u_char)((pcbID >> 2) & 0x07);
+
+ result = sanei_lm983x_read( handle, 0x59, reg59s, 3, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ DBG( _DBG_INFO, "pcbID=0x%02x\n", pcbID );
+
+ /* now roam through the setting list... */
+ strncpy( tmp, dev->usbId, 13 );
+ tmp[13] = '\0';
+
+ sprintf( pcbStr, "-%u", pcbID );
+ strcat ( tmp, pcbStr );
+
+ DBG( _DBG_INFO, "Checking for device >%s<\n", tmp );
+
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ if( 0 == strcmp( Settings[i].pIDString, tmp )) {
+ DBG(_DBG_INFO, "Device description for >%s< found.\n", tmp );
+ usb_initDev( dev, i, handle, dev->usbDev.vendor );
+ return handle;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * will be called upon sane_exit
+ */
+static void usbDev_shutdown( Plustek_Device *dev )
+{
+ SANE_Int handle;
+
+ DBG( _DBG_INFO, "Shutdown called (dev->fd=%d, %s)\n",
+ dev->fd, dev->sane.name );
+ if( NULL == dev->usbDev.ModelStr ) {
+ DBG( _DBG_INFO, "Function ignored!\n" );
+ return;
+ }
+
+ if( SANE_STATUS_GOOD == sanei_usb_open( dev->sane.name, &handle )) {
+
+ dev->fd = handle;
+
+ DBG( _DBG_INFO, "Waiting for scanner-ready...\n" );
+ usb_IsScannerReady( dev );
+
+ if( 0 != dev->usbDev.bLampOffOnEnd ) {
+
+ DBG( _DBG_INFO, "Switching lamp off...\n" );
+ usb_LampOn( dev, SANE_FALSE, SANE_FALSE );
+ }
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ }
+ usb_StopLampTimer( dev );
+}
+
+/**
+ * This function checks wether a device, described by a given
+ * string(vendor and product ID), is support by this backend or not
+ *
+ * @param usbIdStr - sting consisting out of product and vendor ID
+ * format: "0xVVVVx0xPPPP" VVVV = Vendor ID, PPP = Product ID
+ * @returns; SANE_TRUE if supported, SANE_FALSE if not
+ */
+static SANE_Bool usb_IsDeviceInList( char *usbIdStr )
+{
+ int i;
+
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ if( 0 == strncmp( Settings[i].pIDString, usbIdStr, 13 ))
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+/** get last valid entry of a list
+ */
+static DevList*
+getLast( DevList *l )
+{
+ if( l == NULL )
+ return NULL;
+
+ while( l->next != NULL )
+ l = l->next;
+ return l;
+}
+
+/** add a new entry to our internal device list, when a device is detected
+ */
+static SANE_Status usb_attach( SANE_String_Const dev_name )
+{
+ int len;
+ DevList *tmp, *last;
+
+ /* get some memory for the entry... */
+ len = sizeof(DevList) + strlen(dev_name) + 1;
+ tmp = (DevList*)malloc(len);
+
+ /* initialize and set the values */
+ memset(tmp, 0, len );
+
+ /* the device name is part of our structure space */
+ tmp->dev_name = &((char*)tmp)[sizeof(DevList)];
+ strcpy( tmp->dev_name, dev_name );
+ tmp->attached = SANE_FALSE;
+
+ /* root set ? */
+ if( usbDevs == NULL ) {
+ usbDevs = tmp;
+ } else {
+ last = getLast(usbDevs); /* append... */
+ last->next = tmp;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static void
+usbGetList( DevList **devs )
+{
+ int i;
+ SANE_Bool il;
+ SANE_Word v, p;
+ DevList *tmp;
+
+ DBG( _DBG_INFO, "Retrieving all supported and conntected devices\n" );
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ v = strtol( &(Settings[i].pIDString)[0], 0, 0 );
+ p = strtol( &(Settings[i].pIDString)[7], 0, 0 );
+
+ /* check if this vendor- and product-ID has already been added, needed
+ * for Plustek devices - here one product-ID is used for more than one
+ * device type...
+ */
+ il = SANE_FALSE;
+ for( tmp = *devs; tmp ; tmp = tmp->next ) {
+ if( tmp->device_id == p && tmp->vendor_id == v ) {
+ il = SANE_TRUE;
+ break;
+ }
+ }
+ if( il ) {
+ DBG( _DBG_INFO2, "Already in list: 0x%04x-0x%04x\n", v, p );
+ continue;
+ }
+
+ /* get the last entry... */
+ tmp = getLast(*devs);
+ DBG( _DBG_INFO2, "Checking for 0x%04x-0x%04x\n", v, p );
+ sanei_usb_find_devices( v, p, usb_attach );
+
+ if( getLast(*devs) != tmp ) {
+
+ if( tmp == NULL )
+ tmp = *devs;
+ else
+ tmp = tmp->next;
+
+ while( tmp != NULL ) {
+ tmp->vendor_id = v;
+ tmp->device_id = p;
+ tmp = tmp->next;
+ }
+ }
+ }
+
+ DBG( _DBG_INFO, "Available and supported devices:\n" );
+ if( *devs == NULL )
+ DBG( _DBG_INFO, "NONE.\n" );
+
+ for( tmp = *devs; tmp; tmp = tmp->next ) {
+ DBG( _DBG_INFO, "Device: >%s< - 0x%04xx0x%04x\n",
+ tmp->dev_name, tmp->vendor_id, tmp->device_id );
+ }
+}
+
+/**
+ */
+static int usbDev_open( Plustek_Device *dev, DevList *devs, int keep_lock )
+{
+ char dn[512];
+ char devStr[50];
+ int result;
+ int i;
+ int lc;
+ SANE_Int handle;
+ SANE_Byte version;
+ SANE_Word vendor, product;
+ SANE_Bool was_empty;
+ SANE_Status status;
+ DevList *tmp;
+
+ DBG( _DBG_INFO, "usbDev_open(%s,%s) - %p\n",
+ dev->name, dev->usbId, (void*)devs );
+
+ /* preset our internal usb device structure */
+ memset( &dev->usbDev, 0, sizeof(DeviceDef));
+
+ /* devs is NULL, when called from sane_start */
+ if( devs ) {
+
+ dn[0] = '\0';
+ if( !strcmp( dev->name, "auto" )) {
+
+ /* use the first "unattached"... */
+ for( tmp = devs; tmp; tmp = tmp->next ) {
+ if( !tmp->attached ) {
+ tmp->attached = SANE_TRUE;
+ strcpy( dn, tmp->dev_name );
+ break;
+ }
+ }
+ } else {
+
+ vendor = strtol( &dev->usbId[0], 0, 0 );
+ product = strtol( &dev->usbId[7], 0, 0 );
+
+ /* check the first match... */
+ for( tmp = devs; tmp; tmp = tmp->next ) {
+ if( tmp->vendor_id == vendor && tmp->device_id == product ) {
+ if( !tmp->attached ) {
+ tmp->attached = SANE_TRUE;
+ strcpy( dn, tmp->dev_name );
+ break;
+ }
+ }
+ }
+ }
+
+ if( !dn[0] ) {
+ DBG( _DBG_ERROR, "No supported device found!\n" );
+ return -1;
+ }
+
+ status = sanei_access_lock( dn, 5 );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_access_lock failed: %d\n", status );
+ return -1;
+ }
+
+ status = sanei_usb_open( dn, &handle );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_usb_open failed: %s (%d)\n",
+ strerror(errno), errno);
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+
+ /* replace the old devname, so we are able to have multiple
+ * auto-detected devices
+ */
+ free( dev->name );
+ dev->name = strdup(dn);
+ dev->sane.name = dev->name;
+
+ } else {
+
+ status = sanei_access_lock( dev->sane.name, 5 );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_access_lock failed: %d\n", status );
+ return -1;
+ }
+
+ status = sanei_usb_open( dev->name, &handle );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_usb_open failed: %s (%d)\n",
+ strerror(errno), errno);
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+ }
+
+ was_empty = SANE_FALSE;
+
+ result = sanei_usb_get_vendor_product( handle, &vendor, &product );
+
+ if( SANE_STATUS_GOOD == result ) {
+
+ sprintf( devStr, "0x%04X-0x%04X", vendor, product );
+
+ DBG(_DBG_INFO,"Vendor ID=0x%04X, Product ID=0x%04X\n",vendor,product);
+
+ if( dev->usbId[0] != '\0' ) {
+
+ if( 0 != strcmp( dev->usbId, devStr )) {
+ DBG( _DBG_ERROR, "Specified Vendor and Product ID "
+ "doesn't match with the ones\n"
+ "in the config file\n" );
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ return -1;
+ }
+ } else {
+ sprintf( dev->usbId, "0x%04X-0x%04X", vendor, product );
+ was_empty = SANE_TRUE;
+ }
+
+ } else {
+
+ DBG( _DBG_INFO, "Can't get vendor & product ID from driver...\n" );
+
+ /* if the ioctl stuff is not supported by the kernel and we have
+ * nothing specified, we have to give up...
+ */
+ if( dev->usbId[0] == '\0' ) {
+ DBG( _DBG_ERROR, "Cannot autodetect Vendor an Product ID, "
+ "please specify in config file.\n" );
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ vendor = strtol( &dev->usbId[0], 0, 0 );
+ product = strtol( &dev->usbId[7], 0, 0 );
+ DBG( _DBG_INFO, "... using the specified: "
+ "0x%04X-0x%04X\n", vendor, product );
+ }
+
+ /* before accessing the scanner, check if supported!
+ */
+ if( !usb_IsDeviceInList( dev->usbId )) {
+ DBG( _DBG_ERROR, "Device >%s<, is not supported!\n", dev->usbId );
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ status = usbio_DetectLM983x( handle, &version );
+ if( SANE_STATUS_GOOD != status ) {
+ sanei_usb_close( handle );
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+
+ if ((version < 3) || (version > 4)) {
+ DBG( _DBG_ERROR, "This is not a LM9831 or LM9832 chip based scanner.\n" );
+ sanei_usb_close( handle );
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+
+ /* need to set the handle and the detected chiptype... */
+ dev->fd = handle;
+ dev->usbDev.HwSetting.chip = (version==3 ? _LM9831:_LM9832);
+ usbio_ResetLM983x( dev );
+ dev->fd = -1;
+
+ dev->usbDev.vendor = vendor;
+ dev->usbDev.product = product;
+
+ DBG( _DBG_INFO, "Detected vendor & product ID: "
+ "0x%04X-0x%04X\n", vendor, product );
+
+ /*
+ * Plustek uses the misc IO 1/2 to get the PCB ID
+ * (PCB = printed circuit board), so it's possible to have one
+ * product ID and up to 7 different devices...
+ */
+ if( 0x07B3 == vendor ) {
+
+ handle = usb_CheckForPlustekDevice( handle, dev );
+
+ if( was_empty )
+ dev->usbId[0] = '\0';
+
+ if( handle >= 0 ) {
+ if( !keep_lock )
+ sanei_access_unlock( dev->sane.name );
+ return handle;
+ }
+
+ } else {
+
+ /* now roam through the setting list... */
+ lc = 13;
+ strncpy( devStr, dev->usbId, lc );
+ devStr[lc] = '\0';
+
+ if( 0x400 == vendor ) {
+ if((dev->adj.mov < 0) || (dev->adj.mov > 1)) {
+ DBG(_DBG_INFO, "BearPaw MOV out of range: %d\n", dev->adj.mov);
+ dev->adj.mov = 0;
+ }
+ sprintf( devStr, "%s-%d", dev->usbId, dev->adj.mov );
+ lc = strlen(devStr);
+ DBG( _DBG_INFO, "BearPaw device: %s (%d)\n", devStr, lc );
+ }
+
+ if( was_empty )
+ dev->usbId[0] = '\0';
+
+ /* if we don't use the PCD ID extension...
+ */
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ if( 0 == strncmp( Settings[i].pIDString, devStr, lc )) {
+ DBG( _DBG_INFO, "Device description for >%s< found.\n", devStr );
+ usb_initDev( dev, i, handle, vendor );
+ if( !keep_lock )
+ sanei_access_unlock( dev->sane.name );
+ return handle;
+ }
+ }
+ }
+
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ DBG( _DBG_ERROR, "No matching device found >%s<\n", devStr );
+ return -1;
+}
+
+/**
+ */
+static int usbDev_close( Plustek_Device *dev )
+{
+ DBG( _DBG_INFO, "usbDev_close()\n" );
+ sanei_usb_close( dev->fd );
+ dev->fd = -1;
+ return 0;
+}
+
+/** convert the stuff
+ */
+static int usbDev_getCaps( Plustek_Device *dev )
+{
+ DCapsDef *scaps = &dev->usbDev.Caps;
+
+ DBG( _DBG_INFO, "usbDev_getCaps()\n" );
+
+ dev->caps.dwFlag = 0;
+
+ if(((DEVCAPSFLAG_Positive & scaps->wFlags) &&
+ (DEVCAPSFLAG_Negative & scaps->wFlags)) ||
+ (DEVCAPSFLAG_TPA & scaps->wFlags)) {
+ dev->caps.dwFlag |= SFLAG_TPA;
+ }
+
+ dev->caps.wMaxExtentX = scaps->Normal.Size.x;
+ dev->caps.wMaxExtentY = scaps->Normal.Size.y;
+ return 0;
+}
+
+/** usbDev_getCropInfo
+ * function to set the image relevant stuff
+ */
+static int usbDev_getCropInfo( Plustek_Device *dev, CropInfo *ci )
+{
+ WinInfo size;
+
+ DBG( _DBG_INFO, "usbDev_getCropInfo()\n" );
+
+ usb_GetImageInfo( dev, &ci->ImgDef, &size );
+
+ ci->dwPixelsPerLine = size.dwPixels;
+ ci->dwLinesPerArea = size.dwLines;
+ ci->dwBytesPerLine = size.dwBytes;
+
+ if( ci->ImgDef.dwFlag & SCANFLAG_DWORDBoundary )
+ ci->dwBytesPerLine = (ci->dwBytesPerLine + 3UL) & 0xfffffffcUL;
+
+ DBG( _DBG_INFO, "PPL = %lu\n", ci->dwPixelsPerLine );
+ DBG( _DBG_INFO, "LPA = %lu\n", ci->dwLinesPerArea );
+ DBG( _DBG_INFO, "BPL = %lu\n", ci->dwBytesPerLine );
+
+ return 0;
+}
+
+/**
+ */
+static int usbDev_setMap( Plustek_Device *dev, SANE_Word *map,
+ SANE_Word length, SANE_Word channel )
+{
+ SANE_Word i, idx;
+
+ DBG(_DBG_INFO,"Setting map[%u] at 0x%08lx\n",channel,(unsigned long)map);
+
+ _VAR_NOT_USED( dev );
+
+ if( channel == _MAP_MASTER ) {
+
+ for( i = 0; i < length; i++ ) {
+ a_bMap[i] = (SANE_Byte)map[i];
+ a_bMap[length +i] = (SANE_Byte)map[i];
+ a_bMap[(length*2)+i] = (SANE_Byte)map[i];
+ }
+
+ } else {
+
+ idx = 0;
+ if( channel == _MAP_GREEN )
+ idx = 1;
+ if( channel == _MAP_BLUE )
+ idx = 2;
+
+ for( i = 0; i < length; i++ ) {
+ a_bMap[(length * idx)+i] = (SANE_Byte)map[i];
+ }
+ }
+
+ return 0;
+}
+
+/**
+ */
+static int
+usbDev_setScanEnv( Plustek_Device *dev, ScanInfo *si )
+{
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *caps = &dev->usbDev.Caps;
+
+ DBG( _DBG_INFO, "usbDev_setScanEnv()\n" );
+
+ /* clear all the stuff */
+ memset( scan, 0, sizeof(ScanDef));
+
+ if((si->ImgDef.dwFlag & SCANDEF_Adf) &&
+ (si->ImgDef.dwFlag & SCANDEF_ContinuousScan)) {
+ scan->sParam.dMCLK = dMCLK_ADF;
+ }
+
+ /* Save necessary informations */
+ scan->fGrayFromColor = 0;
+
+ /* for some devices and settings, we tweak the physical settings
+ * how to get the image - but not in calibration mode
+ */
+ if((si->ImgDef.dwFlag & SCANFLAG_Calibration) == 0) {
+
+ if( si->ImgDef.wDataType == COLOR_256GRAY ) {
+
+ if( !(si->ImgDef.dwFlag & SCANDEF_Adf) && !usb_IsCISDevice(dev) &&
+ (caps->OpticDpi.x == 1200 && si->ImgDef.xyDpi.x <= 300)) {
+ scan->fGrayFromColor = 2;
+ si->ImgDef.wDataType = COLOR_TRUE24;
+ DBG( _DBG_INFO, "* Gray from color set!\n" );
+ }
+
+ if( caps->workaroundFlag & _WAF_GRAY_FROM_COLOR ) {
+ DBG( _DBG_INFO, "* Gray(8-bit) from color set!\n" );
+ scan->fGrayFromColor = 2;
+ si->ImgDef.wDataType = COLOR_TRUE24;
+ }
+
+ } else if ( si->ImgDef.wDataType == COLOR_GRAY16 ) {
+ if( caps->workaroundFlag & _WAF_GRAY_FROM_COLOR ) {
+ DBG( _DBG_INFO, "* Gray(16-bit) from color set!\n" );
+ scan->fGrayFromColor = 2;
+ si->ImgDef.wDataType = COLOR_TRUE48;
+ }
+
+ } else if ( si->ImgDef.wDataType == COLOR_BW ) {
+ if( caps->workaroundFlag & _WAF_BIN_FROM_COLOR ) {
+ DBG( _DBG_INFO, "* Binary from color set!\n" );
+ scan->fGrayFromColor = 10;
+ si->ImgDef.wDataType = COLOR_TRUE24;
+ }
+ }
+ }
+
+ usb_SaveImageInfo( dev, &si->ImgDef );
+ usb_GetImageInfo ( dev, &si->ImgDef, &scan->sParam.Size );
+
+ /* mask the flags */
+ scan->dwFlag = si->ImgDef.dwFlag &
+ (SCANFLAG_bgr | SCANFLAG_BottomUp | SCANFLAG_Calibration |
+ SCANFLAG_DWORDBoundary | SCANFLAG_RightAlign |
+ SCANFLAG_StillModule | SCANDEF_Adf | SCANDEF_ContinuousScan);
+
+ if( !(SCANDEF_QualityScan & si->ImgDef.dwFlag)) {
+ DBG( _DBG_INFO, "* Preview Mode set!\n" );
+ } else {
+ DBG( _DBG_INFO, "* Preview Mode NOT set!\n" );
+ scan->dwFlag |= SCANDEF_QualityScan;
+ }
+
+ scan->sParam.brightness = si->siBrightness;
+ scan->sParam.contrast = si->siContrast;
+
+ if( scan->sParam.bBitDepth <= 8 )
+ scan->dwFlag &= ~SCANFLAG_RightAlign;
+
+ if( scan->dwFlag & SCANFLAG_DWORDBoundary ) {
+ if( scan->fGrayFromColor && scan->fGrayFromColor < 10)
+ scan->dwBytesLine = (scan->sParam.Size.dwBytes / 3 + 3) & 0xfffffffcUL;
+ else
+ scan->dwBytesLine = (scan->sParam.Size.dwBytes + 3UL) & 0xfffffffcUL;
+
+ } else {
+
+ if( scan->fGrayFromColor && scan->fGrayFromColor < 10)
+ scan->dwBytesLine = scan->sParam.Size.dwBytes / 3;
+ else
+ scan->dwBytesLine = scan->sParam.Size.dwBytes;
+ }
+
+ /* on CIS based devices we have to reconfigure the illumination
+ * settings for the gray modes
+ */
+ usb_AdjustCISLampSettings( dev, SANE_TRUE );
+
+ if( scan->dwFlag & SCANFLAG_BottomUp)
+ scan->lBufAdjust = -(long)scan->dwBytesLine;
+ else
+ scan->lBufAdjust = scan->dwBytesLine;
+
+ /* LM9831 has a BUG in 16-bit mode,
+ * so we generate pseudo 16-bit data from 8-bit
+ */
+ if( scan->sParam.bBitDepth > 8 ) {
+
+ if( _LM9831 == dev->usbDev.HwSetting.chip ) {
+
+ scan->sParam.bBitDepth = 8;
+ scan->dwFlag |= SCANFLAG_Pseudo48;
+ scan->sParam.Size.dwBytes >>= 1;
+ }
+ }
+
+ /* Source selection */
+ if( scan->sParam.bSource == SOURCE_Reflection ) {
+
+ dev->usbDev.pSource = &caps->Normal;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Normal.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Normal.lUp;
+
+ } else if( scan->sParam.bSource == SOURCE_Transparency ) {
+
+ dev->usbDev.pSource = &caps->Positive;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Positive.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Positive.lUp;
+
+ } else if( scan->sParam.bSource == SOURCE_Negative ) {
+
+ dev->usbDev.pSource = &caps->Negative;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Negative.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Negative.lUp;
+
+ } else {
+
+ dev->usbDev.pSource = &dev->usbDev.Caps.Adf;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Normal.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Normal.lUp;
+ }
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+
+ if( scan->dwFlag & SCANDEF_ContinuousScan )
+ dev->usbDev.fLastScanIsAdf = SANE_TRUE;
+ else
+ dev->usbDev.fLastScanIsAdf = SANE_FALSE;
+ }
+
+ return 0;
+}
+
+/**
+ */
+static int
+usbDev_stopScan( Plustek_Device *dev )
+{
+ DBG( _DBG_INFO, "usbDev_stopScan()\n" );
+
+ /* in cancel-mode we first stop the motor */
+ usb_ScanEnd( dev );
+
+ dev->scanning.dwFlag = 0;
+
+ if( NULL != dev->scanning.pScanBuffer ) {
+
+ free( dev->scanning.pScanBuffer );
+ dev->scanning.pScanBuffer = NULL;
+ usb_StartLampTimer( dev );
+ }
+ return 0;
+}
+
+/**
+ */
+static int
+usbDev_startScan( Plustek_Device *dev )
+{
+ ScanDef *scan = &dev->scanning;
+ DBG( _DBG_INFO, "usbDev_startScan()\n" );
+
+/* HEINER: Preview currently not working correctly */
+#if 0
+ if( scan->dwFlag & SCANDEF_QualityScan )
+ dev->usbDev.a_bRegs[0x0a] = 0;
+ else
+ dev->usbDev.a_bRegs[0x0a] = 1;
+#endif
+ dev->usbDev.a_bRegs[0x0a] = 0;
+
+ if((scan->dwFlag & SCANDEF_Adf) && (scan->dwFlag & SCANDEF_ContinuousScan)) {
+ scan->fCalibrated = SANE_TRUE;
+ } else {
+ scan->fCalibrated = SANE_FALSE;
+ }
+
+ scan->sParam.PhyDpi.x = usb_SetAsicDpiX(dev,scan->sParam.UserDpi.x);
+ scan->sParam.PhyDpi.y = usb_SetAsicDpiY(dev,scan->sParam.UserDpi.y);
+
+ /* Allocate shading/scan buffer */
+ scan->pScanBuffer = (u_long*)malloc( _SCANBUF_SIZE );
+
+ if( scan->pScanBuffer == NULL )
+ return _E_ALLOC;
+
+ scan->dwFlag |= SCANFLAG_StartScan;
+
+ /* some devices (esp. BearPaw) do need a lamp switch off before
+ * switching it on again. Otherwise it might happen that the lamp
+ * remains off
+ */
+ if(dev->usbDev.Caps.workaroundFlag & _WAF_LOFF_ON_START) {
+ if (usb_GetLampStatus(dev))
+ usb_LampOn( dev, SANE_FALSE, SANE_TRUE );
+ }
+
+ usb_LampOn( dev, SANE_TRUE, SANE_TRUE );
+
+ m_fStart = m_fFirst = SANE_TRUE;
+ m_fAutoPark = (scan->dwFlag&SCANFLAG_StillModule)?SANE_FALSE:SANE_TRUE;
+
+ if( usb_IsSheetFedDevice(dev))
+ if(usb_InCalibrationMode(dev))
+ m_fAutoPark = SANE_FALSE;
+
+ usb_StopLampTimer( dev );
+ return 0;
+}
+
+/**
+ * do the reading stuff here...
+ * first we perform the calibration step, and then we read the image
+ * line for line
+ */
+static int
+usbDev_Prepare( Plustek_Device *dev, SANE_Byte *buf )
+{
+ int result;
+ SANE_Bool use_alt_cal = SANE_FALSE;
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ DBG( _DBG_INFO, "usbDev_PrepareScan()\n" );
+
+ /* check the current position of the sensor and move it back
+ * to it's home position if necessary...
+ */
+ if( !usb_IsSheetFedDevice(dev))
+ usb_SensorStatus( dev );
+
+ /* CIS devices need special handling... */
+ if( usb_IsCISDevice(dev)) {
+ use_alt_cal = SANE_TRUE;
+
+ } else {
+
+ if( dev->adj.altCalibrate )
+ use_alt_cal = SANE_TRUE;
+ }
+
+ /* for the skip functionality use the "old" calibration functions */
+ if( dev->usbDev.Caps.workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
+ use_alt_cal = SANE_FALSE;
+ }
+
+ if( use_alt_cal ) {
+ result = cano_DoCalibration( dev );
+ } else {
+ result = usb_DoCalibration( dev );
+ }
+
+ if( SANE_TRUE != result ) {
+ DBG( _DBG_ERROR, "calibration failed!!!\n" );
+ return _E_ABORT;
+ }
+
+ if( dev->adj.cacheCalData )
+ usb_SaveCalData( dev );
+
+ DBG( _DBG_INFO, "calibration done.\n" );
+ if( usb_InCalibrationMode(dev))
+ return 0;
+
+ if( !(scan->dwFlag & SCANFLAG_Scanning)) {
+
+ usleep( 10 * 1000 );
+
+ /* need to preset that here, as we need it during parameter setting
+ */
+ scan->bLinesToSkip = (u_char)(scan->sParam.PhyDpi.y / 50);
+
+ scan->dwLinesDiscard = 0;
+ if( scan->sParam.bChannels == 3 ) {
+ scan->dwLinesDiscard = (u_long)scaps->bSensorDistance *
+ scan->sParam.PhyDpi.y / scaps->OpticDpi.y;
+ scan->dwLinesDiscard <<= 1;
+ }
+
+ if( !usb_SetScanParameters( dev, &scan->sParam )) {
+ DBG( _DBG_ERROR, "Setting Scan Parameters failed!\n" );
+ return 0;
+ }
+
+ /* if we bypass the calibration step, we wait on lamp warmup here...
+ */
+ if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
+ if( !usb_Wait4Warmup( dev )) {
+ DBG( _DBG_INFO, "usbDev_Prepare() - Cancel detected...\n" );
+ return 0;
+ }
+ }
+
+ scan->pbScanBufBegin = (u_char*)scan->pScanBuffer;
+
+ if((dev->caps.dwFlag & SFLAG_ADF) && (scaps->OpticDpi.x == 600))
+ scan->dwLinesScanBuf = 8;
+ else
+ scan->dwLinesScanBuf = 32;
+
+ scan->dwBytesScanBuf = scan->dwLinesScanBuf *
+ scan->sParam.Size.dwPhyBytes;
+
+ scan->dwNumberOfScanBufs = _SCANBUF_SIZE / scan->dwBytesScanBuf;
+ scan->dwLinesPerScanBufs = scan->dwNumberOfScanBufs *
+ scan->dwLinesScanBuf;
+ scan->pbScanBufEnd = scan->pbScanBufBegin +
+ scan->dwLinesPerScanBufs *
+ scan->sParam.Size.dwPhyBytes;
+ scan->dwRedShift = 0;
+ scan->dwBlueShift = 0;
+ scan->dwGreenShift = 0;
+
+ /* CCD scanner */
+ if( scan->sParam.bChannels == 3 ) {
+
+ scan->dwLinesDiscard = (u_long)scaps->bSensorDistance *
+ scan->sParam.PhyDpi.y / scaps->OpticDpi.y;
+
+ switch( scaps->bSensorOrder ) {
+
+ case SENSORORDER_rgb:
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_rbg:
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_gbr:
+ scan->Green.pb = scan->pbScanBufBegin;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_grb:
+ scan->Green.pb = scan->pbScanBufBegin;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_brg:
+ scan->Blue.pb = scan->pbScanBufBegin;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_bgr:
+ scan->Blue.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ }
+
+ /* double it for last channel */
+ scan->dwLinesDiscard <<= 1;
+ scan->dwGreenShift = (7UL+scan->sParam.bBitDepth) >> 3;
+ scan->Green.pb += scan->dwGreenShift;
+ scan->Blue.pb += (scan->dwGreenShift * 2);
+
+ if( scan->dwFlag & SCANFLAG_bgr) {
+
+ u_char *pb = scan->Blue.pb;
+
+ scan->Blue.pb = scan->Red.pb;
+ scan->Red.pb = pb;
+ scan->dwBlueShift = 0;
+ scan->dwRedShift = scan->dwGreenShift << 1;
+ } else {
+ scan->dwRedShift = 0;
+ scan->dwBlueShift = scan->dwGreenShift << 1;
+ }
+
+ } else {
+
+ /* CIS section */
+
+ /* this might be a simple gray operation or AFE 1 channel op */
+ scan->dwLinesDiscard = 0;
+ scan->Green.pb = scan->pbScanBufBegin;
+
+ if(( scan->sParam.bDataType == SCANDATATYPE_Color ) &&
+ ( hw->bReg_0x26 & _ONE_CH_COLOR )) {
+
+ u_char so;
+ u_long len = scan->sParam.Size.dwPhyBytes / 3;
+
+ so = scaps->bSensorOrder;
+ if (_WAF_RESET_SO_TO_RGB & scaps->workaroundFlag) {
+ if (scaps->bPCB != 0) {
+ if (scan->sParam.PhyDpi.x > scaps->bPCB) {
+ so = SENSORORDER_rgb;
+ DBG(_DBG_INFO, "* Resetting sensororder to RGB\n");
+ }
+ }
+ }
+
+ switch( so ) {
+
+ case SENSORORDER_rgb:
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + len;
+ scan->Blue.pb = scan->pbScanBufBegin + len * 2UL;
+ break;
+
+ case SENSORORDER_gbr:
+ scan->Green.pb = scan->pbScanBufBegin;
+ scan->Blue.pb = scan->pbScanBufBegin + len;
+ scan->Red.pb = scan->pbScanBufBegin + len * 2UL;
+ break;
+ default:
+ DBG( _DBG_ERROR, "CIS: This bSensorOrder "
+ "is not defined\n" );
+ return _E_INTERNAL;
+ }
+ }
+ }
+
+ /* set a funtion to process the RAW data... */
+ usb_GetImageProc( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF )
+ scan->dwFlag |= SCANFLAG_StillModule;
+
+ DBG( _DBG_INFO, "* scan->dwFlag=0x%08lx\n", scan->dwFlag );
+
+ if( !usb_ScanBegin( dev,
+ (scan->dwFlag&SCANFLAG_StillModule) ? SANE_FALSE:SANE_TRUE)) {
+
+ return _E_INTERNAL;
+ }
+
+ scan->dwFlag |= SCANFLAG_Scanning;
+
+ if( scan->sParam.UserDpi.y != scan->sParam.PhyDpi.y ) {
+
+ if( scan->sParam.UserDpi.y < scan->sParam.PhyDpi.y ) {
+
+ scan->wSumY = scan->sParam.PhyDpi.y - scan->sParam.UserDpi.y;
+ scan->dwFlag |= SCANFLAG_SampleY;
+ DBG( _DBG_INFO, "SampleY Flag set (%u != %u, wSumY=%u)\n",
+ scan->sParam.UserDpi.y, scan->sParam.PhyDpi.y, scan->wSumY );
+ }
+ }
+ }
+
+ dumpPicInit( &scan->sParam, "plustek-pic.raw" );
+
+ /* here the NT driver uses an extra reading thread...
+ * as the SANE stuff already forked the driver to read data, I think
+ * we should only read data by using a function...
+ */
+ scan->dwLinesUser = scan->sParam.Size.dwLines;
+ if( !scan->dwLinesUser )
+ return _E_BUFFER_TOO_SMALL;
+
+ if( scan->sParam.Size.dwLines < scan->dwLinesUser )
+ scan->dwLinesUser = scan->sParam.Size.dwLines;
+
+ scan->sParam.Size.dwLines -= scan->dwLinesUser;
+ if( scan->dwFlag & SCANFLAG_BottomUp )
+ scan->UserBuf.pb = buf + (scan->dwLinesUser - 1) * scan->dwBytesLine;
+ else
+ scan->UserBuf.pb = buf;
+
+ DBG(_DBG_INFO,"Reading the data now!\n" );
+ DBG(_DBG_INFO,"PhyDpi.x = %u\n", scan->sParam.PhyDpi.x );
+ DBG(_DBG_INFO,"PhyDpi.y = %u\n", scan->sParam.PhyDpi.y );
+ DBG(_DBG_INFO,"UserDpi.x = %u\n", scan->sParam.UserDpi.x );
+ DBG(_DBG_INFO,"UserDpi.y = %u\n", scan->sParam.UserDpi.y );
+ DBG(_DBG_INFO,"NumberOfScanBufs = %lu\n",scan->dwNumberOfScanBufs);
+ DBG(_DBG_INFO,"LinesPerScanBufs = %lu\n",scan->dwLinesPerScanBufs);
+ DBG(_DBG_INFO,"dwPhyBytes = %lu\n",scan->sParam.Size.dwPhyBytes);
+ DBG(_DBG_INFO,"dwPhyPixels = %lu\n",scan->sParam.Size.dwPhyPixels);
+ DBG(_DBG_INFO,"dwTotalBytes = %lu\n",scan->sParam.Size.dwTotalBytes);
+ DBG(_DBG_INFO,"dwPixels = %lu\n",scan->sParam.Size.dwPixels);
+ DBG(_DBG_INFO,"dwBytes = %lu\n",scan->sParam.Size.dwBytes);
+ DBG(_DBG_INFO,"dwValidPixels = %lu\n",scan->sParam.Size.dwValidPixels);
+ DBG(_DBG_INFO,"dwBytesScanBuf = %lu\n",scan->dwBytesScanBuf );
+ DBG(_DBG_INFO,"dwLinesDiscard = %lu\n",scan->dwLinesDiscard );
+ DBG(_DBG_INFO,"dwLinesToSkip = %u\n", scan->bLinesToSkip );
+ DBG(_DBG_INFO,"dwLinesUser = %lu\n",scan->dwLinesUser );
+ DBG(_DBG_INFO,"dwBytesLine = %lu\n",scan->dwBytesLine );
+
+ scan->pbGetDataBuf = scan->pbScanBufBegin;
+
+ scan->dwLinesToProcess = usb_ReadData( dev );
+ if( 0 == scan->dwLinesToProcess )
+ return _E_DATAREAD;
+
+ return 0;
+}
+
+/** as the name says, read one line...
+ */
+static int
+usbDev_ReadLine( Plustek_Device *dev )
+{
+ int wrap;
+ u_long cur;
+ ScanDef *scan = &dev->scanning;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ cur = scan->dwLinesUser;
+
+ /* we stay within this sample loop until one line has been processed for
+ * the user...
+ */
+ while( cur == scan->dwLinesUser ) {
+
+ if( usb_IsEscPressed()) {
+ DBG( _DBG_INFO, "readLine() - Cancel detected...\n" );
+ return _E_ABORT;
+ }
+
+ if( !(scan->dwFlag & SCANFLAG_SampleY)) {
+
+ scan->pfnProcess( dev );
+
+ /* Adjust user buffer pointer */
+ scan->UserBuf.pb += scan->lBufAdjust;
+ scan->dwLinesUser--;
+
+ } else {
+
+ scan->wSumY += scan->sParam.UserDpi.y;
+
+ if( scan->wSumY >= scan->sParam.PhyDpi.y ) {
+ scan->wSumY -= scan->sParam.PhyDpi.y;
+
+ scan->pfnProcess( dev );
+
+ /* Adjust user buffer pointer */
+ scan->UserBuf.pb += scan->lBufAdjust;
+ scan->dwLinesUser--;
+ }
+ }
+
+ /* Adjust get buffer pointers */
+ wrap = 0;
+
+ if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
+
+ scan->Red.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Red.pb >= scan->pbScanBufEnd ) {
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwRedShift;
+ wrap = 1;
+ }
+
+ scan->Green.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Green.pb >= scan->pbScanBufEnd ) {
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwGreenShift;
+ wrap = 1;
+ }
+
+ scan->Blue.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Blue.pb >= scan->pbScanBufEnd ) {
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwBlueShift;
+ wrap = 1;
+ }
+ } else {
+ scan->Green.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Green.pb >= scan->pbScanBufEnd )
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwGreenShift;
+ }
+
+ /* on any wrap-around of the get pointers in one channel mode
+ * we have to reset them
+ */
+ if( wrap ) {
+
+ u_long len = scan->sParam.Size.dwPhyBytes;
+
+ if( hw->bReg_0x26 & _ONE_CH_COLOR ) {
+
+ if(scan->sParam.bDataType == SCANDATATYPE_Color) {
+ len /= 3;
+ }
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + len;
+ scan->Blue.pb = scan->pbScanBufBegin + len * 2UL;
+ }
+ }
+
+ /* line processed, check if we have to get more...
+ */
+ scan->dwLinesToProcess--;
+
+ if( 0 == scan->dwLinesToProcess ) {
+
+ scan->dwLinesToProcess = usb_ReadData( dev );
+ if( 0 == scan->dwLinesToProcess ) {
+
+ if( usb_IsEscPressed())
+ return _E_ABORT;
+ }
+ }
+ }
+ return 0;
+}
+
+/* END PLUSTEK-USB.C ........................................................*/
diff --git a/backend/plustek-usb.h b/backend/plustek-usb.h
new file mode 100644
index 0000000..0f001c9
--- /dev/null
+++ b/backend/plustek-usb.h
@@ -0,0 +1,725 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usb.h
+ * @brief Main defines for the USB devices.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - added workaround flag to struct DevCaps
+ * - 0.42 - added MODEL_NOPLUSTEK
+ * - replaced fLM9831 by chip (valid entries: _LM9831, _LM9832, _LM9833)
+ * - added _WAF_MISC_IO3_LAMP for UMAX 3400
+ * - 0.43 - added _WAF_MISC_IOx_LAMP (x=1,2,4,5)
+ * - added CLKDef
+ * - 0.44 - added vendor and product ID to struct DeviceDef
+ * - added _WAF_BYPASS_CALIBRATION
+ * - added _WAF_INV_NEGATIVE_MAP
+ * - 0.45 - added _WAF_SKIP_FINE for skipping fine calibration
+ * - added _WAF_SKIP_WHITEFINE for skipping fine white calibration
+ * - added MCLK setting for 16 bit modes
+ * - added _WAF_FIX_GAIN and _WAF_FIX_OFS
+ * - 0.46 - added UMAX1200 for 5400 model
+ * - removed _WAF_FIX_GAIN and _WAF_FIX_OFS
+ * - added skipCoarseCalib to ScanDef
+ * - added additional defines for cis and epson-ccd sensor
+ * - 0.47 - cleanup work
+ * - added gamma to struct HWDefault
+ * - 0.48 - added DEVCAPSFLAG_LargeTPA
+ * - added _WAF_BIN_FROM_COLOR and _WAF_GRAY_FROM_COLOR
+ * - added dHighSpeed to struct HwDefault
+ * - 0.49 - added a_bRegs, fModFirstHome and fLastScanIsAdf
+ * to struct DeviceDef
+ * - added CRYSTAL_FREQ
+ * - added IPCDef
+ * - 0.50 - cleanup
+ * - removed obsolete _WAF_BLACKFINE
+ * - added MODEL_CANON_LIDE25
+ * - 0.51 - added _WAF_MISC_IO_BUTTONS plus _BUTTON stuff
+ * - added _WAF_USE_ALT_DESC
+ * - added DEVCAPSFLAG_SheetFed
+ * - added dpi_thresh and lineend to motor structure
+ * - 0.52 - added MODEL_QSCAN
+ * - added MODEL_QSCAN_A6 (thanks to Hiroshi Miura)
+ * - changed DCapsDef, lamp -> misc_io
+ * - bPCB is now ushort to be "missused" by non Plustek
+ * devices (as threshhold for resetting sensor order)
+ * - added _WAF_LOFF_ON_START and _WAF_ONLY_8BIT
+ * - added MODEL_TSCAN_A4
+ * - added attribute packed for data access structs
+ * - added _WAF_INC_DARKTGT
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __PLUSTEK_USB_H__
+#define __PLUSTEK_USB_H__
+
+/** CCD ID (PCB ID): total 3 bits (on Plustek devices) */
+#define kNEC3799 0
+#define kSONY518 1
+#define kSONY548 2
+#define kNEC8861 3
+#define kNEC3778 4
+#define kNECSLIM 5
+#define kCIS650 6
+#define kCIS670 7
+#define kCIS1220 8
+#define kCIS1240 9
+#define kEPSON 10
+
+/** 48MHz Quartz */
+#define CRYSTAL_FREQ 48000000UL
+
+/*********************************** plustek_types.h!!! ************************/
+
+/* makes trouble with gcc3
+#define _SWAP(x,y) (x)^=(y)^=(x)^=(y)
+*/
+#define _SWAP(x,y) { (x)^=(y); (x)^=((y)^=(x));}
+
+#define _LOWORD(x) ((u_short)(x & 0xffff))
+#define _HIWORD(x) ((u_short)(x >> 16))
+#define _LOBYTE(x) ((u_char)((x) & 0xFF))
+#define _HIBYTE(x) ((u_char)((x) >> 8))
+
+#define _HILO2WORD(x) ((u_short)x.bHi * 256U + x.bLo)
+#define _LOHI2WORD(x) ((u_short)x.bLo * 256U + x.bHi)
+
+#define _PHILO2WORD(x) ((u_short)x->bHi * 256U + x->bLo)
+#define _PLOHI2WORD(x) ((u_short)x->bLo * 256U + x->bHi)
+
+#define PACKED8 __attribute__ ((packed,aligned(1)))
+#define PACKED16 __attribute__ ((packed,aligned(2)))
+
+/* useful for RGB-values */
+typedef struct {
+ u_char Red;
+ u_char Green;
+ u_char Blue;
+} PACKED8 RGBByteDef;
+
+typedef struct {
+ u_short Red;
+ u_short Green;
+ u_short Blue;
+} PACKED16 RGBUShortDef;
+
+typedef struct {
+ u_long Red;
+ u_long Green;
+ u_long Blue;
+} RGBULongDef;
+
+typedef struct {
+ u_char a_bColor[3];
+} PACKED8 ColorByteDef;
+
+typedef struct {
+ u_char bHi;
+ u_char bLo;
+} PACKED8 HiLoDef;
+
+typedef union {
+ HiLoDef HiLo[3];
+ u_short Colors[3];
+} PACKED16 ColorWordDef;
+
+typedef union {
+ HiLoDef HiLo;
+ u_short Mono;
+} PACKED16 MonoWordDef;
+
+typedef union {
+
+ u_char *pb;
+ u_short *pw;
+ MonoWordDef *pmw;
+ ColorByteDef *pcb;
+ ColorWordDef *pcw;
+ RGBByteDef *pb_rgb;
+ RGBUShortDef *pw_rgb;
+ HiLoDef *philo;
+
+} __attribute__ ((aligned(4))) AnyPtr;
+
+typedef struct {
+ unsigned short x;
+ unsigned short y;
+} XY;
+
+#define _VAR_NOT_USED(x) ((x)=(x))
+
+/*****************************************************************************/
+
+#define IDEAL_GainNormal 0xf000UL /* 240 */
+#define IDEAL_GainPositive 0xfe00UL /* 254 */
+#define IDEAL_Offset 0x1000UL /* 20 */
+
+#define GAIN_Target 65535UL
+
+/** Chip-types */
+typedef enum _CHIPSET
+{
+ _LM9831,
+ _LM9832,
+ _LM9833
+} eChipDef;
+
+/** ScanParam.bCalibration */
+enum _SHADINGID
+{
+ PARAM_Scan,
+ PARAM_Gain,
+ PARAM_DarkShading,
+ PARAM_WhiteShading,
+ PARAM_Offset
+};
+
+/** ScanParam.bDataType */
+enum _SCANDATATYPE
+{
+ SCANDATATYPE_BW,
+ SCANDATATYPE_Gray,
+ SCANDATATYPE_Color
+};
+
+/** DCapsDef.bSensorColor */
+enum _SENSORCOLOR
+{
+ SENSORORDER_rgb,
+ SENSORORDER_rbg,
+ SENSORORDER_gbr,
+ SENSORORDER_grb,
+ SENSORORDER_brg,
+ SENSORORDER_bgr
+};
+
+/** DCapsDef.wFlags */
+enum _DEVCAPSFLAG
+{
+ DEVCAPSFLAG_Normal = 0x0001,
+ DEVCAPSFLAG_Positive = 0x0002,
+ DEVCAPSFLAG_Negative = 0x0004,
+ DEVCAPSFLAG_TPA = 0x0006,
+ DEVCAPSFLAG_Adf = 0x0008,
+ DEVCAPSFLAG_LargeTPA = 0x0010,
+ DEVCAPSFLAG_SheetFed = 0x0020
+};
+
+/** to allow some workarounds */
+enum _WORKAROUNDS
+{
+ _WAF_NONE = 0x00000000, /* no fix anywhere needed */
+ _WAF_BSHIFT7_BUG = 0x00000001, /* to fix U12 bug in 14bit mode */
+ _WAF_MISC_IO_LAMPS = 0x00000002, /* special lamp switching */
+ _WAF_BLACKFINE = 0x00000004, /* use black calibration strip */
+ _WAF_BYPASS_CALIBRATION = 0x00000008, /* no calibration,use linear gamma */
+ _WAF_INV_NEGATIVE_MAP = 0x00000010, /* the backend does the neg. stuff */
+ _WAF_SKIP_FINE = 0x00000020, /* skip the fine calbration */
+ _WAF_SKIP_WHITEFINE = 0x00000040, /* skip the fine white calbration */
+ _WAF_BIN_FROM_COLOR = 0x00000080, /* generate binary & gray images */
+ _WAF_GRAY_FROM_COLOR = 0x00000100, /* from color scans */
+ _WAF_MISC_IO_BUTTONS = 0x00000200, /* special handling for buttons */
+ _WAF_USE_ALT_DESC = 0x00000400, /* use alternate manufacturer */
+ _WAF_RESET_SO_TO_RGB = 0x00000800, /* set sensororder to RGB(CIS only)*/
+ _WAF_LOFF_ON_START = 0x00001000, /* switch lamp off before scanning */
+ _WAF_ONLY_8BIT = 0x00002000, /* scanner allows only 8 bit modes */
+ _WAF_INC_DARKTGT = 0x00004000 /* allow tgt darklevel adjustment */
+};
+
+/** for lamps connected to the misc I/O pins*/
+enum _LAMPS
+{
+ _NO_MIO = 0,
+ _MIO1 = 0x0001,
+ _MIO2 = 0x0002,
+ _MIO3 = 0x0004,
+ _MIO4 = 0x0008,
+ _MIO5 = 0x0010,
+ _MIO6 = 0x0020
+};
+
+#define _PSENSE_SHIFT 24
+#define _PSENSE_MASK 0xFF000000
+#define _BUTTON_SHIFT 16
+#define _BUTTON_MASK 0xFF0000
+#define _TPA_SHIFT 8
+#define _TPA_MASK 0xFF00
+
+enum _BUTTONS
+{
+ _NO_BUTTON = 0,
+ _PORT0 = ((_MIO1 | _MIO2) << _BUTTON_SHIFT),
+ _PORT1 = ((_MIO3 | _MIO4) << _BUTTON_SHIFT),
+ _PORT2 = ((_MIO5 | _MIO6) << _BUTTON_SHIFT)
+};
+
+enum _PAPER_SENSE
+{
+ _PS_INP1 = (0x01 << _PSENSE_SHIFT),
+ _PS_INP2 = (0x02 << _PSENSE_SHIFT),
+ _PS_INP_MIO1 = (_MIO1 << (_PSENSE_SHIFT+2)),
+ _PS_INP_MIO2 = (_MIO2 << (_PSENSE_SHIFT+2)),
+ _PS_INP_MIO3 = (_MIO3 << (_PSENSE_SHIFT+2)),
+ _PS_INP_MIO4 = (_MIO4 << (_PSENSE_SHIFT+2)),
+ _PS_INP_MIO5 = (_MIO5 << (_PSENSE_SHIFT+2)),
+ _PS_INP_MIO6 = (_MIO6 << (_PSENSE_SHIFT+2))
+};
+
+/** for encoding a misc I/O register as TPA */
+#define _TPA(register) ((u_long)(register << _TPA_SHIFT))
+
+/** Mask to check for available TPA */
+#define _HAS_TPA(flag) (flag & _TPA_MASK)
+
+/** Get the TPA misc I/O register */
+#define _GET_TPALAMP(flag) ((flag >> _TPA_SHIFT) & 0xFF)
+
+/** Get the Papersense port*/
+#define _GET_PAPERSENSE_PORT(flag) ((flag >> _PSENSE_SHIFT) & 0xFF)
+
+/** motor types */
+typedef enum
+{
+ MODEL_KaoHsiung = 0,
+ MODEL_HuaLien,
+ MODEL_Tokyo600,
+ MODEL_EPSON, /**< for EPSON1250/1260 */
+ MODEL_MUSTEK600, /**< for BearPaw 1200 */
+ MODEL_MUSTEK1200, /**< for BearPaw 2400 */
+ MODEL_HP, /**< for HP2x00 */
+ MODEL_CANON600 , /**< for CanoScan 600dpi models */
+ MODEL_CANON1200, /**< for Canon 1200dpi models */
+ MODEL_CANONCCD1200, /**< for Canon CCD 1200dpi models */
+ MODEL_CANON_LIDE25, /**< for CanoScan LiDE25 */
+ MODEL_UMAX, /**< for UMAX 3400/3450 */
+ MODEL_UMAX1200, /**< for UMAX 5400 */
+ MODEL_TSCAN, /**< for Syscan TravelScan A6 */
+ MODEL_TSCAN_A4, /**< for Syscan TravelScan A4 */
+ MODEL_QSCAN, /**< for PandP Q-Scan A4 */
+ MODEL_QSCAN_A6, /**< for PandP Q-Scan A6 */
+ MODEL_LAST
+} eModelDef;
+
+/** to distinguish between Plustek and other devices */
+#define _IS_PLUSTEKMOTOR(x) (x<=MODEL_Tokyo600)
+
+/** Generic usage */
+enum _CHANNEL
+{
+ CHANNEL_red,
+ CHANNEL_green,
+ CHANNEL_blue,
+ CHANNEL_rgb
+};
+
+/** motor movement */
+enum MODULEMOVE
+{
+ MOVE_Forward,
+ MOVE_Backward,
+ MOVE_Both,
+ MOVE_ToPaperSensor,
+ MOVE_EjectAllPapers,
+ MOVE_SkipPaperSensor,
+ MOVE_ToShading
+};
+
+/** SCANDEF.dwFlags */
+enum SCANFLAG
+{
+ SCANDEF_Transparency = 0x00000100, /* Scanning from transparency*/
+ SCANDEF_Negative = 0x00000200, /* Scanning from negative */
+ SCANDEF_QualityScan = 0x00000400, /* Scanning in quality mode */
+ SCANDEF_ContinuousScan = 0x00001000,
+ SCANDEF_Adf = 0x00002000, /* Scan from ADF tray */
+
+ SCANFLAG_bgr = 0x00004000,
+ SCANFLAG_BottomUp = 0x00008000,
+ SCANFLAG_DWORDBoundary = 0x00020000,
+ SCANFLAG_RightAlign = 0x00040000,
+ SCANFLAG_StillModule = 0x00080000,
+ SCANFLAG_Pseudo48 = 0x08000000,
+ SCANFLAG_SampleY = 0x04000000,
+ SCANFLAG_Calibration = 0x10000000,
+ SCANFLAG_Scanning = 0x20020000,
+ SCANFLAG_StartScan = 0x40000000
+};
+
+typedef struct Origins
+{
+ long lLeft; /* How many pix to move the scanning org left, in optic res */
+ long lUp; /* How many pix to move the scanning or up, in optic res */
+} OrgDef;
+
+typedef struct SrcAttr
+{
+ XY DataOrigin; /**< The origin x is from visible pixel not CCD */
+ /* pixel 0, in 300 DPI base. */
+ /* The origin y is from visible top */
+ /* (glass area), in 300 DPI */
+ short ShadingOriginY; /**< The origin y is from top of scanner body */
+ short DarkShadOrgY; /**< if the device has a dark calibration strip */
+ XY Size; /**< Scanning width/height, in 300 DPI base. */
+ XY MinDpi; /**< Minimum dpi supported for scanning */
+
+} SrcAttrDef;
+
+typedef struct DevCaps
+{
+ SrcAttrDef Normal; /**< Reflection */
+ SrcAttrDef Positive; /**< Positive film */
+ SrcAttrDef Negative; /**< Negative film */
+ SrcAttrDef Adf; /**< Adf device */
+ XY OpticDpi; /**< Maximum DPI */
+ u_short wFlags; /**< Flag to indicate what kinds of elements */
+ /* are available */
+ u_char bSensorOrder; /**< CCD color sequences, see _SENSORORDER */
+ u_char bSensorDistance; /**< CCD Color distance */
+ u_char bButtons; /**< Number of buttons */
+ u_char bCCD; /**< CCD ID */
+ u_short bPCB; /**< PCB ID/or threshold (only CIS) */
+ u_long workaroundFlag; /**< Flag to allow special work arounds, see */
+ /* _WORKAROUNDS */
+ u_long misc_io; /**< for lamp, papersense and buttons */
+
+} DCapsDef;
+
+/**
+ * for keeping intial illumination settings
+ */
+typedef struct
+{
+ u_char mode;
+
+ u_short red_lamp_on;
+ u_short red_lamp_off;
+ u_short green_lamp_on;
+ u_short green_lamp_off;
+ u_short blue_lamp_on;
+ u_short blue_lamp_off;
+
+} IllumiDef;
+
+
+/** basic register settings
+ */
+typedef struct HWDefault
+{
+ double dMaxMotorSpeed; /* Inches/second, max. scan speed */
+ double dMaxMoveSpeed; /* Inches/second, max. move speed */
+ double dHighSpeed; /* for speeding up the sensor */
+ double dIntegrationTimeLowLamp;
+ double dIntegrationTimeHighLamp;
+ u_short wMotorDpi; /* Full step DPI */
+ u_short wDRAMSize; /* in KB */
+ double dMinIntegrationTimeLowres; /*in ms. */
+ double dMinIntegrationTimeHighres; /* in ms. */
+ u_short wGreenPWMDutyCycleLow;
+ u_short wGreenPWMDutyCycleHigh;
+ /* Registers */
+ u_char bSensorConfiguration; /* 0x0b */
+ /* Sensor control settings */
+ u_char bReg_0x0c;
+ u_char bReg_0x0d;
+ u_char bReg_0x0e;
+ u_char bReg_0x0f_Mono [10]; /* 0x0f to 0x18 */
+ u_char bReg_0x0f_Color [10]; /* 0x0f to 0x18 */
+
+ /* color mode settings */
+ u_char bReg_0x26;
+ u_char bReg_0x27;
+
+ /* illumination mode reg 0x29 (runtime) */
+ u_char bReg_0x29;
+
+ /* initial illumination settings */
+ IllumiDef illu_mono;
+ IllumiDef illu_color;
+
+ /* 0x1a & 0x1b, remember the u_char order is not Intel
+ * format, you have to pay your attention when you
+ * write this value to register.
+ */
+ u_short StepperPhaseCorrection;
+
+ /* Sensor Pixel Configuration
+ * Actually, the wActivePixelsStart will be set to 0 for shading purpose.
+ * We have to keep these values to adjust the origins when user does the
+ * scan. These settings are based on optic resolution.
+ */
+ u_char bOpticBlackStart; /* 0x1c */
+ u_char bOpticBlackEnd; /* 0x1d */
+ u_short wActivePixelsStart; /* 0x1e & 0x1f */
+ u_short wLineEnd; /* 0x20 & 0x21 */
+
+ /* illumination settings (runtime) */
+ u_short red_lamp_on; /* 0x2c & 0x2d */
+ u_short red_lamp_off; /* 0x2e & 0x2f */
+ u_short green_lamp_on; /* 0x30 & 0x31 */
+ u_short green_lamp_off; /* 0x32 & 0x33 */
+ u_short blue_lamp_on; /* 0x34 & 0x35 */
+ u_short blue_lamp_off; /* 0x36 & 0x37 */
+
+ /* Misc */
+ u_char bReg_0x45;
+ u_short wStepsAfterPaperSensor2;/* 0x4c & 0x4d */
+ u_char bStepsToReverse; /* 0x50 */
+ u_char bReg_0x51;
+ u_char bReg_0x54;
+ u_char bReg_0x55;
+ u_char bReg_0x56;
+ u_char bReg_0x57;
+ u_char bReg_0x58;
+ u_char bReg_0x59;
+ u_char bReg_0x5a;
+ u_char bReg_0x5b;
+ u_char bReg_0x5c;
+ u_char bReg_0x5d;
+ u_char bReg_0x5e;
+
+ eChipDef chip; /* chiptype */
+ eModelDef motorModel; /* to identify used motor */
+ double gamma; /* default gamma setting */
+} HWDef;
+
+/** device description during runtime
+ */
+typedef struct DeviceDef
+{
+ char* ModelStr; /**< pointer to our model string */
+ int vendor; /**< vendor ID */
+ int product; /**< product ID */
+ DCapsDef Caps; /**< pointer to the attribute of current dev */
+ HWDef HwSetting; /**< Pointer to the characteristics of device */
+ SrcAttrDef *pSource; /**< Scanning src, it's equal to Caps.Normal */
+ /**< on the source that the user specified. */
+ OrgDef Normal; /**< Reflection - Pix to adjust scanning orgs */
+ OrgDef Positive; /**< Pos film - Pix to adjust scanning orgs */
+ OrgDef Negative; /**< Neg film - Pix to adjust scanning orgs */
+ OrgDef Adf; /**< Adf - Pixels to adjust scanning origins */
+ u_long dwTicksLampOn; /**< The ticks when lamp turns on */
+ u_long dwLampOnPeriod;/**< How many seconds to keep lamp on */
+ SANE_Bool bLampOffOnEnd; /**< switch lamp off on end or keep cur. state*/
+ int currentLamp; /**< The lamp ID of the currently used lamp */
+ SANE_Bool fModFirstHome; /**< */
+ SANE_Bool fLastScanIsAdf;/**< */
+ u_char a_bRegs[0x80]; /**< our global register file */
+
+} DeviceDef;
+
+
+typedef struct Settings
+{
+ char *pIDString;
+ DCapsDef *pDevCaps;
+ HWDef *pHwDef;
+ char *pModelString;
+
+} SetDef;
+
+/**
+ */
+typedef struct
+{
+ /** User Information */
+ u_long dwBytes; /**< bytes per line */
+ u_long dwPixels; /**< pixels per line */
+ u_long dwLines; /**< lines */
+
+ /** Driver Info */
+ u_long dwValidPixels; /**< only valid pixels, not incl. pad pix(B/W,Gray)*/
+ u_long dwPhyPixels; /**< inlcude pad pixels for ASIC (B/W, Gray) */
+ u_long dwPhyBytes; /**< bytes to read from ASIC */
+ u_long dwPhyLines; /**< should include the extra lines accord to the */
+ /* request dpi (CCD lines distance) */
+ u_long dwTotalBytes; /**< Total bytes per scan */
+
+} WinInfo;
+
+/**
+ */
+typedef struct
+{
+ /* OUTPUT - Driver returned area. All are based on physical
+ * scanning conditions. */
+ WinInfo Size; /* i/p:
+ * dwPixels, dwBytes(without u_long boundary factor)
+ * dwLines in user specified dpi
+ * o/p:
+ * dwPhyPixels, dwPhyBytes, dwPhyLines
+ * so after called, caller have to change it */
+ XY PhyDpi; /* Driver DPI */
+
+ /* INPUT - User info. All sizes and coordinates are specified in the
+ * unit based on 300 DPI */
+ XY UserDpi; /**< User specified DPI */
+ XY Origin; /**< Scanning origin in optic dpi */
+ double dMCLK; /**< for positive & negative & Adf */
+ short brightness;
+ short contrast;
+ u_char bSource; /**< Reflection/Positive/Negative/Adf(SOURCE_xxx)*/
+ u_char bDataType; /**< Bw, Gray or Color (see _SCANDATATYPE) */
+ u_char bBitDepth; /**< 1/8/14 */
+ u_char bChannels; /**< Color or Gray */
+ u_char bCalibration; /**< 1 or 2: the origin.x is from CCD pixel 0 and
+ * the origin.y is from Top of scanner.
+ * In this case, the WININFO.dwPhyLines
+ * will not included the extra lines for
+ * color distance factor.
+ * 0: normal scan, the both directions have to
+ * add the distance */
+ int swOffset[3]; /**< for calibration adjustment */
+ int swGain[3]; /**< for calibration adjustment */
+
+} ScanParam;
+
+struct Plustek_Device;
+
+/** structure to hold all necessary buffer informations for current scan
+ */
+typedef struct ScanDef
+{
+ SANE_Bool fCalibrated; /**< calibrated or not */
+ SANE_Bool skipCoarseCalib;/**< skip coarse calibration or not */
+ u_long dwFlag; /**< scan attributes */
+
+ ScanParam sParam; /**< all we need to scan */
+
+ AnyPtr UserBuf; /**< pointer to the user buffer */
+ u_long dwLinesUser; /**< Number of lines of user buffer */
+ u_long dwBytesLine; /**< Bytes per line of user buffer. */
+ u_long dwLinesToProcess;
+
+ /** Image processing routine according to the scan mode */
+ void (*pfnProcess)(struct Plustek_Device*);
+
+ u_long* pScanBuffer; /**< our scan buffer */
+
+ u_long dwLinesPerScanBufs;
+ u_long dwNumberOfScanBufs;
+ u_long dwLinesScanBuf;
+
+ u_char* pbScanBufBegin;
+ u_char* pbScanBufEnd;
+ u_char* pbGetDataBuf;
+ u_long dwBytesScanBuf;
+ u_long dwLinesDiscard;
+
+ u_long dwRedShift;
+ u_long dwGreenShift;
+ u_long dwBlueShift;
+
+ AnyPtr Green;
+ AnyPtr Red;
+ AnyPtr Blue;
+
+ long lBufAdjust; /**< bytes to adjust buffer pointer */
+ /* after a image line processed */
+ u_short wSumY; /**< for line sampling */
+
+ u_char bLineDistance; /**< Color offset in specific dpi y */
+ int fGrayFromColor; /**< channel to use for gray mode */
+
+ u_char bLinesToSkip; /**< how many lines to skip at start */
+
+} ScanDef;
+
+
+/** max number of different colck settings */
+#define _MAX_CLK 10
+
+/** structure to hold PWN settings
+ */
+typedef struct
+{
+ u_char pwm; /**< PWM */
+ u_char pwm_duty; /**< PWM duty cycles */
+ u_char scan_lines_per_line; /**< lines to scan to obtain 1 real line
+ will be used in 16bit color modes only */
+} MDef;
+
+/** according to the CCD and motor, we provide various settings
+ */
+typedef struct {
+
+ eModelDef motorModel; /**< the motor ID */
+
+ u_char pwm_fast; /**< PWM during fast movement */
+ u_char pwm_duty_fast; /**< PWM duty during fast movement */
+ u_char mclk_fast; /**< MCLK during fast movement */
+
+ u_short dpi_thresh;
+ u_short lineend;
+
+ /**
+ * here we define some ranges for better supporting
+ * non-Plustek devices with it's different hardware
+ * we can set the MCLK and the motor PWM stuff for color
+ * and gray modes (8bit and 14/16bit modes)
+ * 0 1 2 3 4 5 6 7 8 9
+ * <= 75 <=100 <=150 <=200 <=300 <=400 <=600 <= 800 <=1200 <=2400DPI
+ */
+ MDef motor_sets[_MAX_CLK]; /**< motor PWM settings during scan */
+ double color_mclk_8[_MAX_CLK]; /**< MCLK settings for color scan */
+ double color_mclk_16[_MAX_CLK]; /**< MCLK settings for color (16bit) scan*/
+ double gray_mclk_8[_MAX_CLK]; /**< MCLK settings for gray scan */
+ double gray_mclk_16[_MAX_CLK]; /**< MCLK settings for gray (16bit) scan */
+
+} ClkMotorDef;
+
+/** for transferring some info between child and parent after calibration
+ */
+#define _MAX_SHAD 0x4000
+#define _SHADING_BUF (_MAX_SHAD*3) /**< max size of the shading buffer */
+
+typedef struct {
+ u_long transferRate;
+} IPCDef;
+
+#endif /* guard __PLUSTEK_USB_H__ */
+
+/* END PLUSTEK-USB.H ........................................................*/
diff --git a/backend/plustek-usbcal.c b/backend/plustek-usbcal.c
new file mode 100644
index 0000000..f2e7608
--- /dev/null
+++ b/backend/plustek-usbcal.c
@@ -0,0 +1,1388 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners; canoscan calibration
+ *.............................................................................
+ */
+
+/** @file plustek-usbcal.c
+ * @brief Calibration routines for CanoScan CIS devices.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de><br>
+ * Large parts Copyright (C) 2003 Christopher Montgomery <monty@xiph.org>
+ *
+ * Montys' comment:
+ * The basic premise: The stock Plustek-usbshading.c in the plustek
+ * driver is effectively nonfunctional for Canon CanoScan scanners.
+ * These scanners rely heavily on all calibration steps, especially
+ * fine white, to produce acceptible scan results. However, to make
+ * autocalibration work and make it work well involves some
+ * substantial mucking aobut in code that supports thirty other
+ * scanners with widely varying characteristics... none of which I own
+ * or can test.
+ *
+ * Therefore, I'm splitting out a few calibration functions I need
+ * to modify for the CanoScan which allows me to simplify things
+ * greatly for the CanoScan without worrying about breaking other
+ * scanners, as well as reuse the vast majority of the Plustek
+ * driver infrastructure without forking.
+ *
+ * History:
+ * - 0.45m - birth of the file; tested extensively with the LiDE 20
+ * - 0.46 - renamed to plustek-usbcal.c
+ * - fixed problems with LiDE30, works now with 650, 1220, 670, 1240
+ * - cleanup
+ * - added CCD calibration capability
+ * - added the usage of the swGain and swOffset values, to allow
+ * tweaking the calibration results on a sensor base
+ * - 0.47 - moved usb_HostSwap() to plustek_usbhw.c
+ * - fixed problem in cano_AdjustLightsource(), so that it won't
+ * stop too early.
+ * - 0.48 - cleanup
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - fixed lampsetting in cano_AdjustLightsource()
+ * - 0.50 - tried to use the settings from SANE-1.0.13
+ * - added _TWEAK_GAIN to allow increasing GAIN during
+ * lamp coarse calibration
+ * - added also speedtest
+ * - fixed segfault in fine calibration
+ * - 0.51 - added fine calibration cache
+ * - usb_SwitchLamp() now really switches off the sensor
+ * - 0.52 - fixed setting for frontend values (gain/offset)
+ * - added 0 pixel detection for offset calculation
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/* un-/comment the following to en-/disable lamp coarse calibration to tweak
+ * the initial AFE gain settings
+ */
+#define _TWEAK_GAIN 1
+
+/* set the threshold for 0 pixels (in percent if pixels per line) */
+#define _DARK_TGT_THRESH 1
+
+/** 0 for not ready, 1 pos white lamp on, 2 lamp off */
+static int strip_state = 0;
+
+/** depending on the strip state, the sensor is moved to the shading position
+ * and the lamp ist switched on
+ */
+static int
+cano_PrepareToReadWhiteCal( Plustek_Device *dev, SANE_Bool mv2shading_pos )
+{
+ SANE_Bool goto_shading_pos = SANE_TRUE;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ switch (strip_state) {
+ case 0:
+ if( !usb_IsSheetFedDevice(dev)) {
+ if(!usb_ModuleToHome( dev, SANE_TRUE )) {
+ DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ } else {
+ goto_shading_pos = mv2shading_pos;
+ }
+
+ if( goto_shading_pos ) {
+ if( !usb_ModuleMove(dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->ShadingOriginY)) {
+ DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ }
+ break;
+ case 2:
+ dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+ if( !usbio_WriteReg( dev->fd, 0x29, dev->usbDev.a_bRegs[0x29])) {
+ DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ break;
+ }
+
+ strip_state = 1;
+ return 0;
+}
+
+/** also here, depending on the strip state, the sensor will be moved to
+ * the shading position and the lamp will be switched off
+ */
+static int
+cano_PrepareToReadBlackCal( Plustek_Device *dev )
+{
+ if( strip_state == 0 )
+ if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE))
+ return SANE_FALSE;
+
+ if( strip_state != 2 ) {
+ /*
+ * if we have a dark shading strip, there's no need to switch
+ * the lamp off, leave in on a go to that strip
+ */
+ if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
+
+ if( !usb_IsSheetFedDevice(dev))
+ usb_ModuleToHome( dev, SANE_TRUE );
+ usb_ModuleMove ( dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->DarkShadOrgY );
+ dev->usbDev.a_bRegs[0x45] &= ~0x10;
+ strip_state = 0;
+
+ } else {
+ /* switch lamp off to read dark data... */
+ dev->usbDev.a_bRegs[0x29] = 0;
+ usb_switchLamp( dev, SANE_FALSE );
+ strip_state = 2;
+ }
+ }
+ return 0;
+}
+
+/** according to the strip-state we switch the lamp on
+ */
+static int
+cano_LampOnAfterCalibration( Plustek_Device *dev )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ switch (strip_state) {
+ case 2:
+ dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+ if( !usbio_WriteReg( dev->fd, 0x29, dev->usbDev.a_bRegs[0x29])) {
+ DBG( _DBG_ERROR, "cano_LampOnAfterCalibration() failed\n" );
+ return _E_LAMP_NOT_IN_POS;
+ }
+ strip_state = 1;
+ break;
+ }
+ return 0;
+}
+
+/** function to adjust the CIS lamp-off setting for a given channel.
+ * @param min - pointer to the min OFF point for the CIS-channel
+ * @param max - pointer to the max OFF point for the CIS-channel
+ * @param off - pointer to the current OFF point of the CIS-channel
+ * @param val - current value to check
+ * @return returns 0 if the value is fine, 1, if we need to adjust
+ */
+static int
+cano_adjLampSetting( u_short *min, u_short *max, u_short *off, u_short val )
+{
+ u_long newoff = *off;
+
+ /* perfect value, no need to adjust
+ * val [53440..61440] is perfect
+ */
+ if((val < (IDEAL_GainNormal)) && (val > (IDEAL_GainNormal-8000)))
+ return 0;
+
+ if(val >= (IDEAL_GainNormal-4000)) {
+ DBG(_DBG_INFO2, "* TOO BRIGHT --> reduce\n" );
+ *max = newoff;
+ *off = ((newoff + *min)>>1);
+
+ } else {
+
+ u_short bisect = (newoff + *max)>>1;
+ u_short twice = newoff*2;
+
+ DBG(_DBG_INFO2, "* TOO DARK --> up\n" );
+ *min = newoff;
+ *off = twice<bisect?twice:bisect;
+
+ /* as we have already set the maximum value, there's no need
+ * for this channel to recalibrate.
+ */
+ if( *off > 0x3FFF ) {
+ DBG( _DBG_INFO, "* lamp off limited (0x%04x --> 0x3FFF)\n", *off);
+ *off = 0x3FFF;
+ return 10;
+ }
+ }
+ if((*min+1) >= *max )
+ return 0;
+
+ return 1;
+}
+
+/** cano_AdjustLightsource
+ * coarse calibration step 0
+ * [Monty changes]: On the CanoScan at least, the default lamp
+ * settings are several *hundred* percent too high and vary from
+ * scanner-to-scanner by 20-50%. This is only for CIS devices
+ * where the lamp_off parameter is adjustable; I'd make it more general,
+ * but I only have the CIS hardware to test.
+ */
+static int
+cano_AdjustLightsource( Plustek_Device *dev )
+{
+ char tmp[40];
+ int i;
+ int res_r, res_g, res_b;
+ u_long dw, dwR, dwG, dwB, dwDiv, dwLoop1, dwLoop2;
+ RGBUShortDef max_rgb, min_rgb, tmp_rgb;
+ u_long *scanbuf = dev->scanning.pScanBuffer;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ DBG( _DBG_INFO, "cano_AdjustLightsource()\n" );
+
+ if( !usb_IsCISDevice(dev)) {
+ DBG( _DBG_INFO, "- function skipped, CCD device!\n" );
+
+ /* HEINER: we might have to tweak the PWM for the lamps */
+ return SANE_TRUE;
+ }
+
+ /* define the strip to scan for coarse calibration
+ * done at optical resolution.
+ */
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *=3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Gain;
+
+ DBG( _DBG_INFO2, "* Coarse Calibration Strip:\n" );
+ DBG( _DBG_INFO2, "* Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "* Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "* Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "* Origin.X = %u\n", m_ScanParam.Origin.x );
+
+ /* init... */
+ max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0x3fff;
+ min_rgb.Red = hw->red_lamp_on;
+ min_rgb.Green = hw->green_lamp_on;
+ min_rgb.Blue = hw->blue_lamp_on;
+
+ if((dev->adj.rlampoff != -1) &&
+ (dev->adj.glampoff != -1) && (dev->adj.rlampoff != -1)) {
+ DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
+ return SANE_TRUE;
+ }
+
+ /* we probably should preset gain to some reasonably good value
+ * i.e. 0x0a as it's done by Canon within their Windoze driver!
+ */
+#ifdef _TWEAK_GAIN
+ for( i=0x3b; i<0x3e; i++ )
+ dev->usbDev.a_bRegs[i] = 0x0a;
+#endif
+ for( i = 0; ; i++ ) {
+
+ m_ScanParam.dMCLK = dMCLK;
+ if( !usb_SetScanParameters( dev, &m_ScanParam )) {
+ return SANE_FALSE;
+ }
+
+ if( !usb_ScanBegin( dev, SANE_FALSE) ||
+ !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "* cano_AdjustLightsource() failed\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "* PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "* PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels);
+
+ sprintf( tmp, "coarse-lamp-%u.raw", i );
+
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+
+ sprintf( tmp, "coarse-lamp-swap%u.raw", i );
+
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+ dwDiv = 10;
+ dwLoop1 = m_ScanParam.Size.dwPhyPixels/dwDiv;
+
+ tmp_rgb.Red = tmp_rgb.Green = tmp_rgb.Blue = 0;
+
+ /* find out the max pixel value for R, G, B */
+ for( dw = 0; dwLoop1; dwLoop1-- ) {
+
+ /* do some averaging... */
+ for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0; dwLoop2; dwLoop2--, dw++) {
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ if( usb_IsCISDevice(dev)) {
+ dwR += ((u_short*)scanbuf)[dw];
+ dwG += ((u_short*)scanbuf)
+ [dw+m_ScanParam.Size.dwPhyPixels+1];
+ dwB += ((u_short*)scanbuf)
+ [dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+ } else {
+ dwR += ((RGBUShortDef*)scanbuf)[dw].Red;
+ dwG += ((RGBUShortDef*)scanbuf)[dw].Green;
+ dwB += ((RGBUShortDef*)scanbuf)[dw].Blue;
+ }
+ } else {
+ dwG += ((u_short*)scanbuf)[dw];
+ }
+ }
+
+ dwR = dwR / dwDiv;
+ dwG = dwG / dwDiv;
+ dwB = dwB / dwDiv;
+
+ if( tmp_rgb.Red < dwR )
+ tmp_rgb.Red = dwR;
+ if( tmp_rgb.Green < dwG )
+ tmp_rgb.Green = dwG;
+ if( tmp_rgb.Blue < dwB )
+ tmp_rgb.Blue = dwB;
+ }
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ DBG( _DBG_INFO2, "red_lamp_off = %u/%u/%u\n",
+ min_rgb.Red ,hw->red_lamp_off, max_rgb.Red );
+ }
+
+ DBG( _DBG_INFO2, "green_lamp_off = %u/%u/%u\n",
+ min_rgb.Green, hw->green_lamp_off, max_rgb.Green );
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ DBG( _DBG_INFO2, "blue_lamp_off = %u/%u/%u\n",
+ min_rgb.Blue, hw->blue_lamp_off, max_rgb.Blue );
+ }
+
+ DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ tmp_rgb.Red, tmp_rgb.Red, tmp_rgb.Green,
+ tmp_rgb.Green, tmp_rgb.Blue, tmp_rgb.Blue );
+ res_r = 0;
+ res_g = 0;
+ res_b = 0;
+
+ /* bisect */
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ res_r = cano_adjLampSetting( &min_rgb.Red, &max_rgb.Red,
+ &hw->red_lamp_off, tmp_rgb.Red );
+ res_b = cano_adjLampSetting( &min_rgb.Blue, &max_rgb.Blue,
+ &hw->blue_lamp_off,tmp_rgb.Blue );
+ }
+
+ res_g = cano_adjLampSetting( &min_rgb.Green, &max_rgb.Green,
+ &hw->green_lamp_off, tmp_rgb.Green );
+
+ /* nothing adjusted, so stop here */
+ if((res_r == 0) && (res_g == 0) && (res_b == 0))
+ break;
+
+ /* no need to adjust more, we have already reached the limit
+ * without tweaking the gain.
+ */
+ if((res_r == 10) && (res_g == 10) && (res_b == 10))
+ break;
+
+ /* we raise the gain for channels, that have been limited */
+#ifdef _TWEAK_GAIN
+ if( res_r == 10 ) {
+ if( dev->usbDev.a_bRegs[0x3b] < 0xf)
+ dev->usbDev.a_bRegs[0x3b]++;
+ }
+ if( res_g == 10 ) {
+ if( dev->usbDev.a_bRegs[0x3c] < 0x0f)
+ dev->usbDev.a_bRegs[0x3c]++;
+ }
+ if( res_b == 10 ) {
+ if( dev->usbDev.a_bRegs[0x3d] < 0x0f)
+ dev->usbDev.a_bRegs[0x3d]++;
+ }
+#endif
+
+ /* now decide what to do:
+ * if we were too bright, we have to rerun the loop in any
+ * case
+ * if we're too dark, we should rerun it too, but we can
+ * compensate that using higher gain values later
+ */
+ if( i >= 10 ) {
+ DBG(_DBG_INFO, "* 10 times limit reached, still too dark!!!\n");
+ break;
+ }
+ usb_AdjustLamps(dev, SANE_TRUE);
+ }
+
+ DBG( _DBG_INFO, "* red_lamp_on = %u\n", hw->red_lamp_on );
+ DBG( _DBG_INFO, "* red_lamp_off = %u\n", hw->red_lamp_off );
+ DBG( _DBG_INFO, "* green_lamp_on = %u\n", hw->green_lamp_on );
+ DBG( _DBG_INFO, "* green_lamp_off = %u\n", hw->green_lamp_off );
+ DBG( _DBG_INFO, "* blue_lamp_on = %u\n", hw->blue_lamp_on );
+ DBG( _DBG_INFO, "* blue_lamp_off = %u\n", hw->blue_lamp_off );
+
+ DBG( _DBG_INFO, "cano_AdjustLightsource() done.\n" );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static int
+cano_adjGainSetting( u_char *min, u_char *max, u_char *gain,u_long val )
+{
+ u_long newgain = *gain;
+
+ if((val < IDEAL_GainNormal) && (val > (IDEAL_GainNormal-8000)))
+ return 0;
+
+ if(val > (IDEAL_GainNormal-4000)) {
+ *max = newgain;
+ *gain = (newgain + *min)>>1;
+ } else {
+ *min = newgain;
+ *gain = (newgain + *max)>>1;
+ }
+
+ if((*min+1) >= *max)
+ return 0;
+
+ return 1;
+}
+
+/** cano_AdjustGain
+ * function to perform the "coarse calibration step" part 1.
+ * We scan reference image pixels to determine the optimum coarse gain settings
+ * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
+ * applied at the line rate during normal scanning.
+ * The scanned line should contain a white strip with some black at the
+ * beginning. The function searches for the maximum value which corresponds to
+ * the maximum white value.
+ * Affects register 0x3b, 0x3c and 0x3d
+ *
+ * adjLightsource, above, steals most of this function's thunder.
+ */
+static SANE_Bool
+cano_AdjustGain( Plustek_Device *dev )
+{
+ char tmp[40];
+ int i = 0, adj = 1;
+ u_long dw;
+ u_long *scanbuf = dev->scanning.pScanBuffer;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ unsigned char max[3], min[3];
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ bMaxITA = 0xff;
+
+ max[0] = max[1] = max[2] = 0x3f;
+ min[0] = min[1] = min[2] = 1;
+
+ DBG( _DBG_INFO, "cano_AdjustGain()\n" );
+ if( !usb_InCalibrationMode(dev)) {
+ if((dev->adj.rgain != -1) &&
+ (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
+ setAdjGain( dev->adj.rgain, &dev->usbDev.a_bRegs[0x3b] );
+ setAdjGain( dev->adj.ggain, &dev->usbDev.a_bRegs[0x3c] );
+ setAdjGain( dev->adj.bgain, &dev->usbDev.a_bRegs[0x3d] );
+ DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
+ return SANE_TRUE;
+ }
+ }
+
+ /* define the strip to scan for coarse calibration
+ * done at 300dpi
+ */
+ m_ScanParam.Size.dwLines = 1; /* for gain */
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+
+ if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color)
+ m_ScanParam.Size.dwBytes *=3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Gain;
+
+ DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" );
+ DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
+
+ while( adj ) {
+
+ m_ScanParam.dMCLK = dMCLK;
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam ))
+ return SANE_FALSE;
+
+ if( !usb_ScanBegin( dev, SANE_FALSE) ||
+ !usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "cano_AdjustGain() failed\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+
+ sprintf( tmp, "coarse-gain-%u.raw", i++ );
+
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ RGBUShortDef max_rgb;
+ u_long dwR, dwG, dwB;
+ u_long dwDiv = 10;
+ u_long dwLoop1 = m_ScanParam.Size.dwPhyPixels/dwDiv, dwLoop2;
+
+ max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0;
+
+ /* find out the max pixel value for R, G, B */
+ for( dw = 0; dwLoop1; dwLoop1-- ) {
+
+ /* do some averaging... */
+ for (dwLoop2 = dwDiv, dwR=dwG=dwB=0; dwLoop2; dwLoop2--, dw++) {
+
+ if( usb_IsCISDevice(dev)) {
+ dwR += ((u_short*)scanbuf)[dw];
+ dwG += ((u_short*)scanbuf)
+ [dw+m_ScanParam.Size.dwPhyPixels+1];
+ dwB += ((u_short*)scanbuf)
+ [dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+ } else {
+ dwR += ((RGBUShortDef*)scanbuf)[dw].Red;
+ dwG += ((RGBUShortDef*)scanbuf)[dw].Green;
+ dwB += ((RGBUShortDef*)scanbuf)[dw].Blue;
+ }
+ }
+ dwR = dwR / dwDiv;
+ dwG = dwG / dwDiv;
+ dwB = dwB / dwDiv;
+
+ if(max_rgb.Red < dwR)
+ max_rgb.Red = dwR;
+ if(max_rgb.Green < dwG)
+ max_rgb.Green = dwG;
+ if(max_rgb.Blue < dwB)
+ max_rgb.Blue = dwB;
+ }
+
+ DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ max_rgb.Red, max_rgb.Red, max_rgb.Green,
+ max_rgb.Green, max_rgb.Blue, max_rgb.Blue );
+
+ adj = cano_adjGainSetting(min , max ,dev->usbDev.a_bRegs+0x3b,max_rgb.Red );
+ adj += cano_adjGainSetting(min+1, max+1,dev->usbDev.a_bRegs+0x3c,max_rgb.Green);
+ adj += cano_adjGainSetting(min+2, max+2,dev->usbDev.a_bRegs+0x3d,max_rgb.Blue );
+
+ } else {
+
+ u_short w_max = 0;
+
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
+ if( w_max < ((u_short*)scanbuf)[dw])
+ w_max = ((u_short*)scanbuf)[dw];
+ }
+
+ adj = cano_adjGainSetting(min,max,dev->usbDev.a_bRegs+0x3c,w_max);
+ dev->usbDev.a_bRegs[0x3b] = (dev->usbDev.a_bRegs[0x3d] = dev->usbDev.a_bRegs[0x3c]);
+
+ DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max );
+
+ }
+ DBG( _DBG_INFO2, "REG[0x3b] = %u\n", dev->usbDev.a_bRegs[0x3b] );
+ DBG( _DBG_INFO2, "REG[0x3c] = %u\n", dev->usbDev.a_bRegs[0x3c] );
+ DBG( _DBG_INFO2, "REG[0x3d] = %u\n", dev->usbDev.a_bRegs[0x3d] );
+ }
+ DBG( _DBG_INFO, "cano_AdjustGain() done.\n" );
+ return SANE_TRUE;
+}
+
+static int tweak_offset[3];
+
+/**
+ */
+static int
+cano_GetNewOffset(Plustek_Device *dev, u_long *val, int channel, signed char *low,
+ signed char *now, signed char *high, u_long *zc)
+{
+ DCapsDef *scaps = &dev->usbDev.Caps;
+
+ if (tweak_offset[channel]) {
+
+ /* if we're too black, we're likely off the low end */
+ if( val[channel] <= 16 ) {
+ low[channel] = now[channel];
+ now[channel] = (now[channel]+high[channel])/2;
+
+ dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f);
+
+ if( low[channel]+1 >= high[channel] )
+ return 0;
+ return 1;
+
+ } else if ( val[channel]>=2048 ) {
+ high[channel]=now[channel];
+ now[channel]=(now[channel]+low[channel])/2;
+
+ dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f);
+
+ if(low[channel]+1>=high[channel])
+ return 0;
+ return 1;
+ }
+ }
+
+ if (!(scaps->workaroundFlag & _WAF_INC_DARKTGT)) {
+ DBG( _DBG_INFO, "0 Pixel adjustment not active!\n");
+ return 0;
+ }
+
+ /* reaching this point, our black level should be okay, but
+ * we also should check the percentage of 0 level pixels.
+ * It turned out, that when having a lot of 0 level pixels,
+ * the calibration will be bad and the resulting scans show up
+ * stripes...
+ */
+ if (zc[channel] > _DARK_TGT_THRESH) {
+ DBG( _DBG_INFO2, "More than %u%% 0 pixels detected, raise offset!\n",
+ _DARK_TGT_THRESH);
+ high[channel]=now[channel];
+ now[channel]=(now[channel]+low[channel])/2;
+
+ /* no more value checks, the goal to set the black level < 2048
+ * will cause stripes...
+ */
+ tweak_offset[channel] = 0;
+
+ dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f);
+
+ if( low[channel]+1 >= high[channel] )
+ return 0;
+ return 1;
+
+ }
+#if 0
+ else if ( val[channel]>=4096 ) {
+ low[channel] = now[channel];
+ now[channel] = (now[channel]+high[channel])/2;
+
+ dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f);
+
+ if( low[channel]+1 >= high[channel] )
+ return 0;
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/** cano_AdjustOffset
+ * function to perform the "coarse calibration step" part 2.
+ * We scan reference image pixels to determine the optimum coarse offset settings
+ * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
+ * applied at the line rate during normal scanning.
+ * On CIS based devices, we switch the light off, on CCD devices, we use the optical
+ * black pixels.
+ * Affects register 0x38, 0x39 and 0x3a
+ */
+
+/* Move this to a bisection-based algo and correct some fenceposts;
+ Plustek's example code disagrees with NatSemi's docs; going by the
+ docs works better, I will assume the docs are correct. --Monty */
+
+static int
+cano_AdjustOffset( Plustek_Device *dev )
+{
+ char tmp[40];
+ int i, adj;
+ u_short r, g, b;
+ u_long dw, dwPixels;
+ u_long dwSum[3], zCount[3];
+
+ signed char low[3] = {-32,-32,-32 };
+ signed char now[3] = { 0, 0, 0 };
+ signed char high[3] = { 31, 31, 31 };
+
+ u_long *scanbuf = dev->scanning.pScanBuffer;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ DBG( _DBG_INFO, "cano_AdjustOffset()\n" );
+ if( !usb_InCalibrationMode(dev)) {
+ if((dev->adj.rofs != -1) &&
+ (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
+ dev->usbDev.a_bRegs[0x38] = (dev->adj.rofs & 0x3f);
+ dev->usbDev.a_bRegs[0x39] = (dev->adj.gofs & 0x3f);
+ dev->usbDev.a_bRegs[0x3a] = (dev->adj.bofs & 0x3f);
+ DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
+ return SANE_TRUE;
+ }
+ }
+
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x*scaps->OpticDpi.x/300UL;
+
+ if( usb_IsCISDevice(dev))
+ dwPixels = m_ScanParam.Size.dwPixels;
+ else
+ dwPixels = (u_long)(hw->bOpticBlackEnd - hw->bOpticBlackStart);
+
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2;
+
+ if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color)
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL /
+ dev->usbDev.Caps.OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Offset;
+ m_ScanParam.dMCLK = dMCLK;
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam )) {
+ DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "S.dwPixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "dwPixels = %lu\n", dwPixels );
+ DBG( _DBG_INFO2, "dwPhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+
+ tweak_offset[0] =
+ tweak_offset[1] =
+ tweak_offset[2] = 1;
+
+ for( i = 0, adj = 1; adj != 0; i++ ) {
+
+ if((!usb_ScanBegin(dev, SANE_FALSE)) ||
+ (!usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes)) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" );
+ return SANE_FALSE;
+ }
+
+ sprintf( tmp, "coarse-off-%u.raw", i );
+
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ dwSum[0] = dwSum[1] = dwSum[2] = 0;
+ zCount[0] = zCount[1] = zCount[2] = 0;
+
+ for (dw = 0; dw < dwPixels; dw++) {
+
+ if( usb_IsCISDevice(dev)) {
+
+ r = ((u_short*)scanbuf)[dw];
+ g = ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
+ b = ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+
+ } else {
+ r = ((RGBUShortDef*)scanbuf)[dw].Red;
+ g = ((RGBUShortDef*)scanbuf)[dw].Green;
+ b = ((RGBUShortDef*)scanbuf)[dw].Blue;
+ }
+
+ dwSum[0] += r;
+ dwSum[1] += g;
+ dwSum[2] += b;
+
+ if (r==0) zCount[0]++;
+ if (g==0) zCount[1]++;
+ if (b==0) zCount[2]++;
+ }
+
+ DBG( _DBG_INFO2, "RedSum = %lu, ave = %lu, ZC=%lu, %lu%%\n",
+ dwSum[0], dwSum[0]/dwPixels,
+ zCount[0], (zCount[0]*100)/dwPixels);
+ DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu, ZC=%lu, %lu%%\n",
+ dwSum[1], dwSum[1]/dwPixels,
+ zCount[1], (zCount[1]*100)/dwPixels);
+ DBG( _DBG_INFO2, "BlueSum = %lu, ave = %lu, ZC=%lu, %lu%%\n",
+ dwSum[2], dwSum[2]/dwPixels,
+ zCount[2], (zCount[2]*100)/dwPixels);
+
+ /* do averaging for each channel */
+ dwSum[0] /= dwPixels;
+ dwSum[1] /= dwPixels;
+ dwSum[2] /= dwPixels;
+
+ zCount[0] = (zCount[0] * 100)/ dwPixels;
+ zCount[1] = (zCount[1] * 100)/ dwPixels;
+ zCount[2] = (zCount[2] * 100)/ dwPixels;
+
+ adj = cano_GetNewOffset(dev, dwSum, 0, low, now, high, zCount);
+ adj |= cano_GetNewOffset(dev, dwSum, 1, low, now, high, zCount);
+ adj |= cano_GetNewOffset(dev, dwSum, 2, low, now, high, zCount);
+
+ DBG( _DBG_INFO2, "RedOff = %d/%d/%d\n",
+ (int)low[0],(int)now[0],(int)high[0]);
+ DBG( _DBG_INFO2, "GreenOff = %d/%d/%d\n",
+ (int)low[1],(int)now[1],(int)high[1]);
+ DBG( _DBG_INFO2, "BlueOff = %d/%d/%d\n",
+ (int)low[2],(int)now[2],(int)high[2]);
+
+ } else {
+ dwSum[0] = 0;
+ zCount[0] = 0;
+
+ for( dw = 0; dw < dwPixels; dw++ ) {
+ dwSum[0] += ((u_short*)scanbuf)[dw];
+
+ if (((u_short*)scanbuf)[dw] == 0)
+ zCount[0]++;
+ }
+
+ DBG( _DBG_INFO2, "Sum=%lu, ave=%lu, ZC=%lu, %lu%%\n",
+ dwSum[0],dwSum[0]/dwPixels,
+ zCount[0], (zCount[0]*100)/dwPixels);
+
+ dwSum[0] /= dwPixels;
+ zCount[0] = (zCount[0] * 100)/ dwPixels;
+
+ adj = cano_GetNewOffset(dev, dwSum, 0, low, now, high, zCount);
+
+ dev->usbDev.a_bRegs[0x3a] =
+ dev->usbDev.a_bRegs[0x39] = dev->usbDev.a_bRegs[0x38];
+
+ DBG( _DBG_INFO2, "GrayOff = %d/%d/%d\n",
+ (int)low[0],(int)now[0],(int)high[0]);
+ }
+
+ DBG( _DBG_INFO2, "REG[0x38] = %u\n", dev->usbDev.a_bRegs[0x38] );
+ DBG( _DBG_INFO2, "REG[0x39] = %u\n", dev->usbDev.a_bRegs[0x39] );
+ DBG( _DBG_INFO2, "REG[0x3a] = %u\n", dev->usbDev.a_bRegs[0x3a] );
+
+ _UIO(sanei_lm983x_write(dev->fd, 0x38, &dev->usbDev.a_bRegs[0x38], 3, SANE_TRUE));
+ }
+
+ /* is that really needed?! */
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ dev->usbDev.a_bRegs[0x38] = now[0] & 0x3f;
+ dev->usbDev.a_bRegs[0x39] = now[1] & 0x3f;
+ dev->usbDev.a_bRegs[0x3a] = now[2] & 0x3f;
+ } else {
+ dev->usbDev.a_bRegs[0x38] =
+ dev->usbDev.a_bRegs[0x39] =
+ dev->usbDev.a_bRegs[0x3a] = now[0] & 0x3f;
+ }
+
+ DBG( _DBG_INFO, "cano_AdjustOffset() done.\n" );
+ return SANE_TRUE;
+}
+
+/** usb_AdjustDarkShading
+ * fine calibration part 1
+ */
+static SANE_Bool
+cano_AdjustDarkShading( Plustek_Device *dev, u_short cal_dpi )
+{
+ char tmp[40];
+ ScanParam *param = &dev->scanning.sParam;
+ ScanDef *scan = &dev->scanning;
+ u_long *scanbuf = scan->pScanBuffer;
+ u_short *bufp;
+ unsigned int i, j;
+ int step, stepW, val;
+ u_long red, green, blue, gray;
+
+ DBG( _DBG_INFO, "cano_AdjustDarkShading()\n" );
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ usb_PrepareFineCal( dev, &m_ScanParam, cal_dpi );
+ m_ScanParam.bCalibration = PARAM_DarkShading;
+
+ sprintf( tmp, "fine-dark.raw" );
+ dumpPicInit(&m_ScanParam, tmp);
+
+ usb_SetScanParameters( dev, &m_ScanParam );
+ if( usb_ScanBegin( dev, SANE_FALSE ) &&
+ usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwTotalBytes)) {
+
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwTotalBytes, 0);
+
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwTotalBytes);
+ }
+ if (!usb_ScanEnd( dev )){
+ DBG( _DBG_ERROR, "cano_AdjustDarkShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+ /* average the n lines, compute reg values */
+ if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
+
+ stepW = m_ScanParam.Size.dwPhyPixels;
+ if( usb_IsCISDevice(dev))
+ step = m_ScanParam.Size.dwPhyPixels + 1;
+ else
+ step = (m_ScanParam.Size.dwPhyPixels*3) + 1;
+
+ for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ) {
+
+ red = 0;
+ green = 0;
+ blue = 0;
+ if( usb_IsCISDevice(dev))
+ bufp = ((u_short *)scanbuf)+i;
+ else
+ bufp = ((u_short *)scanbuf)+(i*3);
+
+ for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) {
+
+ if( usb_IsCISDevice(dev)) {
+ red += *bufp; bufp+=step;
+ green += *bufp; bufp+=step;
+ blue += *bufp; bufp+=step;
+ } else {
+
+ red += bufp[0];
+ green += bufp[1];
+ blue += bufp[2];
+
+ bufp += step;
+ }
+ }
+
+ val = ((int)(red/m_ScanParam.Size.dwPhyLines) + param->swOffset[0]);
+ if( val < 0 ) {
+ DBG( _DBG_INFO, "val < 0!!!!\n" );
+ val = 0;
+ }
+ a_wDarkShading[i] = (u_short)val;
+
+ val = ((int)(green/m_ScanParam.Size.dwPhyLines) + param->swOffset[1]);
+ if( val < 0 ) {
+ DBG( _DBG_INFO, "val < 0!!!!\n" );
+ val = 0;
+ }
+ a_wDarkShading[i+stepW] = (u_short)val;
+
+ val = ((int)(blue/m_ScanParam.Size.dwPhyLines) + param->swOffset[2]);
+ if( val < 0 ) {
+ DBG( _DBG_INFO, "val < 0!!!!\n" );
+ val = 0;
+ }
+ a_wDarkShading[i+stepW*2] = (u_short)val;
+ }
+
+ } else {
+
+ step = m_ScanParam.Size.dwPhyPixels + 1;
+ for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ) {
+
+ gray = 0;
+ bufp = ((u_short *)scanbuf)+i;
+
+ for( j=0; j < m_ScanParam.Size.dwPhyLines; j++ ) {
+ gray += *bufp;
+ bufp += step;
+ }
+ a_wDarkShading[i]= gray/j + param->swOffset[0];
+ }
+
+ memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
+ a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2);
+ memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
+ a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2);
+ }
+
+ if(usb_HostSwap())
+ usb_Swap(a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 * 3);
+
+ usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
+ scan->sParam.bDataType == SCANDATATYPE_Color?1:0);
+
+ DBG( _DBG_INFO, "cano_AdjustDarkShading() done\n" );
+ return SANE_TRUE;
+}
+
+/** usb_AdjustWhiteShading
+ * fine calibration part 2 - read the white calibration area and calculate
+ * the gain coefficient for each pixel
+ */
+static SANE_Bool
+cano_AdjustWhiteShading( Plustek_Device *dev, u_short cal_dpi )
+{
+ char tmp[40];
+ ScanParam *param = &dev->scanning.sParam;
+ ScanDef *scan = &dev->scanning;
+ u_long *scanbuf = scan->pScanBuffer;
+ u_short *bufp;
+ unsigned int i, j;
+ int step, stepW;
+ u_long red, green, blue, gray;
+
+ DBG( _DBG_INFO, "cano_AdjustWhiteShading()\n" );
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ usb_PrepareFineCal( dev, &m_ScanParam, cal_dpi );
+ m_ScanParam.bCalibration = PARAM_WhiteShading;
+
+ sprintf( tmp, "fine-white.raw" );
+ DBG( _DBG_INFO2, "FINE WHITE Calibration Strip: %s\n", tmp );
+ DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
+ dumpPicInit(&m_ScanParam, tmp);
+
+ if( usb_SetScanParameters( dev, &m_ScanParam ) &&
+ usb_ScanBegin( dev, SANE_FALSE ) &&
+ usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwTotalBytes)) {
+
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwTotalBytes, 0);
+
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwTotalBytes);
+
+ if (!usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" );
+ return SANE_FALSE;
+ }
+ } else {
+ DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+ /* average the n lines, compute reg values */
+ if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
+
+ stepW = m_ScanParam.Size.dwPhyPixels;
+ if( usb_IsCISDevice(dev))
+ step = m_ScanParam.Size.dwPhyPixels + 1;
+ else
+ step = (m_ScanParam.Size.dwPhyPixels*3) + 1;
+
+ for( i=0; i < m_ScanParam.Size.dwPhyPixels; i++ ) {
+
+ red = 0;
+ green = 0;
+ blue = 0;
+ if( usb_IsCISDevice(dev))
+ bufp = ((u_short *)scanbuf)+i;
+ else
+ bufp = ((u_short *)scanbuf)+(i*3);
+
+ for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) {
+
+ if( usb_IsCISDevice(dev)) {
+ red += *bufp; bufp+=step;
+ green += *bufp; bufp+=step;
+ blue += *bufp; bufp+=step;
+ } else {
+ red += bufp[0];
+ green += bufp[1];
+ blue += bufp[2];
+ bufp += step;
+ }
+ }
+
+ /* tweaked by the settings in swGain --> 1000/swGain[r,g,b] */
+ red = (65535.*1000./(double)param->swGain[0]) * 16384.*j/red;
+ green = (65535.*1000./(double)param->swGain[1]) * 16384.*j/green;
+ blue = (65535.*1000./(double)param->swGain[2]) * 16384.*j/blue;
+
+ a_wWhiteShading[i] = (red > 65535 ? 65535:red );
+ a_wWhiteShading[i+stepW] = (green > 65535 ? 65535:green);
+ a_wWhiteShading[i+stepW*2] = (blue > 65535 ? 65535:blue );
+ }
+
+ } else {
+
+ step = m_ScanParam.Size.dwPhyPixels + 1;
+ for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ){
+ gray = 0;
+ bufp = ((u_short *)scanbuf)+i;
+
+ for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) {
+ gray += *bufp;
+ bufp += step;
+ }
+
+ gray = (65535.*1000./(double)param->swGain[0]) * 16384.*j/gray;
+
+ a_wWhiteShading[i]= (gray > 65535 ? 65535:gray);
+ }
+
+ memcpy( a_wWhiteShading + m_ScanParam.Size.dwPhyPixels,
+ a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2);
+ memcpy( a_wWhiteShading + m_ScanParam.Size.dwPhyPixels * 2,
+ a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2);
+ }
+
+ if(usb_HostSwap())
+ usb_Swap(a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2 * 3 );
+
+ usb_SaveCalSetShading( dev, &m_ScanParam );
+
+ usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
+ scan->sParam.bDataType == SCANDATATYPE_Color?1:0);
+
+ DBG( _DBG_INFO, "cano_AdjustWhiteShading() done\n" );
+ return SANE_TRUE;
+}
+
+/** the entry function for the CIS calibration stuff.
+ */
+static int
+cano_DoCalibration( Plustek_Device *dev )
+{
+ u_short dpi, idx, idx_end;
+ u_long save_waf;
+ SANE_Bool skip_fine;
+ ScanDef *scan = &dev->scanning;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+
+ if( SANE_TRUE == scan->fCalibrated )
+ return SANE_TRUE;
+
+ DBG( _DBG_INFO, "cano_DoCalibration()\n" );
+
+ if( _IS_PLUSTEKMOTOR(hw->motorModel)){
+ DBG( _DBG_ERROR, "altCalibration can't work with this "
+ "Plustek motor control setup\n" );
+ return SANE_FALSE; /* can't cal this */
+ }
+
+ /* Don't allow calibration settings from the other driver to confuse our
+ * use of a few of its functions.
+ */
+ save_waf = scaps->workaroundFlag;
+ scaps->workaroundFlag &= ~_WAF_SKIP_WHITEFINE;
+ scaps->workaroundFlag &= ~_WAF_SKIP_FINE;
+ scaps->workaroundFlag &= ~_WAF_BYPASS_CALIBRATION;
+
+ if( !dev->adj.cacheCalData && !usb_IsSheetFedDevice(dev))
+ usb_SpeedTest( dev );
+
+ /* here we handle that warmup stuff for CCD devices */
+ if( !usb_AutoWarmup( dev ))
+ return SANE_FALSE;
+
+ /* Set the shading position to undefined */
+ strip_state = 0;
+ usb_PrepareCalibration( dev );
+
+ usb_SetMCLK( dev, &scan->sParam );
+
+ if( !scan->skipCoarseCalib ) {
+
+ if( !usb_Wait4ScanSample( dev ))
+ return SANE_FALSE;
+
+ DBG( _DBG_INFO2, "###### ADJUST LAMP (COARSE)#######\n" );
+ if( cano_PrepareToReadWhiteCal(dev, SANE_TRUE))
+ return SANE_FALSE;
+
+ dev->usbDev.a_bRegs[0x45] &= ~0x10;
+ if( !cano_AdjustLightsource(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" );
+ if(cano_PrepareToReadBlackCal(dev))
+ return SANE_FALSE;
+
+ if( !cano_AdjustOffset(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" );
+ if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE))
+ return SANE_FALSE;
+
+ if( !cano_AdjustGain(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return SANE_FALSE;
+ }
+ } else {
+ strip_state = 1;
+ DBG( _DBG_INFO2, "###### COARSE calibration skipped #######\n" );
+ }
+
+ skip_fine = SANE_FALSE;
+ idx_end = 2;
+ if( dev->adj.cacheCalData || usb_IsSheetFedDevice(dev)) {
+
+ skip_fine = usb_FineShadingFromFile(dev);
+
+ /* we recalibrate in any case ! */
+ if( usb_InCalibrationMode(dev)) {
+ skip_fine = SANE_FALSE;
+ idx_end = DIVIDER+1;
+
+ /* did I say any case? */
+ if (scan->sParam.bBitDepth != 8) {
+ skip_fine = SANE_TRUE;
+ DBG( _DBG_INFO2, "No fine calibration for non-8bit modes!\n" );
+ }
+
+ } else if( usb_IsSheetFedDevice(dev)) {
+
+ /* we only do the calibration upon request !*/
+ if( !skip_fine ) {
+ DBG( _DBG_INFO2, "SHEET-FED device, skip fine calibration!\n" );
+ skip_fine = SANE_TRUE;
+ scaps->workaroundFlag |= _WAF_BYPASS_CALIBRATION;
+ }
+ }
+ }
+
+ if( !skip_fine ) {
+
+ for( idx = 1; idx < idx_end; idx++ ) {
+
+ dpi = 0;
+ if( usb_InCalibrationMode(dev)) {
+ dpi = usb_get_res( scaps->OpticDpi.x, idx );
+
+ /* we might should check against device specific limit */
+ if(dpi < 50)
+ continue;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" );
+ if(cano_PrepareToReadBlackCal(dev))
+ return SANE_FALSE;
+
+ dev->usbDev.a_bRegs[0x45] |= 0x10;
+ if( !cano_AdjustDarkShading(dev, dpi)) {
+ DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" );
+ if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE))
+ return SANE_FALSE;
+
+ if( !usb_IsSheetFedDevice(dev)) {
+ if(!usb_ModuleToHome( dev, SANE_TRUE ))
+ return SANE_FALSE;
+
+ if( !usb_ModuleMove(dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->ShadingOriginY)) {
+ return SANE_FALSE;
+ }
+ }
+ if( !cano_AdjustWhiteShading(dev, dpi)) {
+ DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
+ return SANE_FALSE;
+ }
+
+ /* force to go back */
+ strip_state = 0;
+ }
+ } else {
+ DBG( _DBG_INFO2, "###### FINE calibration skipped #######\n" );
+
+ dev->usbDev.a_bRegs[0x45] |= 0x10;
+ strip_state = 2;
+
+ m_ScanParam = scan->sParam;
+ usb_GetPhyPixels( dev, &m_ScanParam );
+
+ usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
+ m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
+ usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
+ m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
+ }
+
+ /* Lamp on if it's not */
+ cano_LampOnAfterCalibration(dev);
+ strip_state = 0;
+
+ /* home the sensor after calibration
+ */
+ if( !usb_IsSheetFedDevice(dev))
+ usb_ModuleToHome( dev, SANE_TRUE );
+ scan->fCalibrated = SANE_TRUE;
+
+ DBG( _DBG_INFO, "cano_DoCalibration() done\n" );
+ DBG( _DBG_INFO, "-------------------------\n" );
+ DBG( _DBG_INFO, "Static Gain:\n" );
+ DBG( _DBG_INFO, "REG[0x3b] = %u\n", dev->usbDev.a_bRegs[0x3b] );
+ DBG( _DBG_INFO, "REG[0x3c] = %u\n", dev->usbDev.a_bRegs[0x3c] );
+ DBG( _DBG_INFO, "REG[0x3d] = %u\n", dev->usbDev.a_bRegs[0x3d] );
+ DBG( _DBG_INFO, "Static Offset:\n" );
+ DBG( _DBG_INFO, "REG[0x38] = %i\n", dev->usbDev.a_bRegs[0x38] );
+ DBG( _DBG_INFO, "REG[0x39] = %i\n", dev->usbDev.a_bRegs[0x39] );
+ DBG( _DBG_INFO, "REG[0x3a] = %i\n", dev->usbDev.a_bRegs[0x3a] );
+ DBG( _DBG_INFO, "-------------------------\n" );
+
+ scaps->workaroundFlag |= save_waf;
+
+ return SANE_TRUE;
+}
+
+/* END PLUSTEK-USBCAL.C .....................................................*/
diff --git a/backend/plustek-usbcalfile.c b/backend/plustek-usbcalfile.c
new file mode 100644
index 0000000..5638b2e
--- /dev/null
+++ b/backend/plustek-usbcalfile.c
@@ -0,0 +1,845 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbcalfile.c
+ * @brief Functions for saving/restoring calibration settings
+ *
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.46 - first version
+ * - 0.47 - no changes
+ * - 0.48 - no changes
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - 0.50 - cleanup
+ * - 0.51 - added functions for saving, reading and restoring
+ * fine calibration data
+ * - 0.52 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+typedef struct {
+ u_long red_light_on;
+ u_long red_light_off;
+ u_long green_light_on;
+ u_long green_light_off;
+ u_long blue_light_on;
+ u_long blue_light_off;
+ u_long green_pwm_duty;
+
+} LightCtrl;
+
+typedef struct {
+ u_short version;
+
+ u_short red_gain;
+ u_short green_gain;
+ u_short blue_gain;
+
+ u_short red_offs;
+ u_short green_offs;
+ u_short blue_offs;
+
+ LightCtrl light;
+
+} CalData;
+
+/* our shading buffers */
+static u_short a_wWhiteShading[_SHADING_BUF] = {0};
+static u_short a_wDarkShading[_SHADING_BUF] = {0};
+
+/* the version the the calibration files */
+#define _PT_CF_VERSION 0x0002
+
+/** function to read a text file and returns the string which starts which
+ * 'id' string.
+ * no duplicate entries where detected, always the first occurance will be
+ * red.
+ * @param fp - file pointer of file to read
+ * @param id - what to search for
+ * @param res - where to store the result upon success
+ * @return SANE_TRUE on success, SANE_FALSE on any error
+ */
+static SANE_Bool
+usb_ReadSpecLine( FILE *fp, char *id, char* res )
+{
+ char tmp[1024];
+ char *ptr;
+
+ /* rewind file pointer */
+ if( 0 != fseek( fp, 0L, SEEK_SET)) {
+ DBG( _DBG_ERROR, "fseek: %s\n", strerror(errno));
+ return SANE_FALSE;
+ }
+
+ /* roam through the file and examine each line... */
+ while( !feof( fp )) {
+
+ memset( tmp, 0, sizeof(tmp));
+ if( NULL != fgets( tmp, 1024, fp )) {
+
+ if( 0 == strncmp( tmp, id, strlen(id))) {
+
+ ptr = &tmp[strlen(id)];
+ if( '\0' == *ptr )
+ break;
+
+ strcpy( res, ptr );
+ res[strlen(res)-1] = '\0';
+ return SANE_TRUE;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+/** function to read data from a file and excluding certain stuff like
+ * the version lines
+ * @param fp - file pointer of file to read
+ * @param except - what to exclude
+ * @return Pointer to the allocated memory for the data, NULL on any error.
+ */
+static char*
+usb_ReadOtherLines( FILE *fp, char *except )
+{
+ char tmp[1024];
+ char *ptr, *ptr_base;
+ int ignore;
+ int len;
+
+ if( 0 != fseek( fp, 0L, SEEK_END))
+ return NULL;
+
+ len = ftell(fp);
+
+ /* rewind file pointer */
+ if( 0 != fseek( fp, 0L, SEEK_SET))
+ return NULL;
+
+ if( len == 0 )
+ return NULL;
+
+ ptr = (char*)malloc(len);
+ if( NULL == ptr )
+ return NULL;
+
+ ptr_base = ptr;
+ *ptr = '\0';
+ ignore = 0;
+
+ /* roam through the file and examine each line... */
+ while( !feof( fp )) {
+
+ if( NULL != fgets( tmp, 1024, fp )) {
+
+ /* we ignore the version line... */
+ if( 0 == strncmp( tmp, "version=", 8 ))
+ continue;
+
+ if( !ignore ) {
+ if(0 != strncmp(tmp, except, strlen(except))) {
+
+ if( strlen( tmp ) > 0 ) {
+ strcpy( ptr, tmp );
+ ptr += strlen(tmp);
+ *ptr = '\0';
+ }
+ } else {
+ ignore = 1;
+ }
+ }
+
+ /* newline in tmp string resets ignore flag */
+ if( strrchr(tmp, '\n')) {
+ ignore = 0;
+ }
+ }
+ }
+ return ptr_base;
+}
+
+/**
+ */
+static SANE_Bool
+usb_ReadSamples( FILE *fp, char *which, u_long *dim, u_short *buffer )
+{
+ char *p, *next, *rb;
+ char tmp[1024+30];
+ int ignore, diml, c;
+ u_long val;
+
+ /* rewind file pointer */
+ if( 0 != fseek( fp, 0L, SEEK_SET))
+ return SANE_FALSE;
+
+ ignore = 0;
+ diml = 0;
+ c = 0;
+ rb = tmp;
+ *dim = 0;
+
+ /* roam through the file and examine each line... */
+ while( !feof( fp )) {
+
+ if( NULL != fgets( rb, 1024, fp )) {
+
+ /* we ignore the version line... */
+ if( 0 == strncmp( tmp, "version=", 8 ))
+ continue;
+
+ p = tmp;
+ if( !ignore && diml == 0) {
+ if(0 == strncmp(tmp, which, strlen(which))) {
+
+ /* get dimension */
+ diml = strtol(&tmp[strlen(which)], NULL, 10);
+ p = strchr( &tmp[strlen(which)], ':' );
+ p++;
+ } else {
+ ignore = 1;
+ }
+ }
+
+ /* parse the values... */
+ if( !ignore ) {
+
+ rb = tmp;
+ while( *p ) {
+ val = strtoul( p, &next, 10 );
+
+ /* check for error condition */
+ if( val == 0 ) {
+ if( p == next ) {
+ if( c+1 == diml ) {
+ *dim = diml;
+ return SANE_TRUE;
+ }
+ break;
+ }
+ }
+
+ buffer[c] = (u_short)val;
+
+ /* more values? */
+ if( *next == ',') {
+ p = next+1;
+ c++;
+ } else {
+ p = next;
+ }
+ /* reached the end? */
+ if( *next == '\0' ) {
+
+ /* we probably have only parsed a part of a value
+ * so we copy that back to the input buffer and
+ * parse it the next time...
+ */
+ if( c < diml ) {
+ sprintf( tmp, "%u", buffer[c] );
+ rb = &tmp[strlen(tmp)];
+ }
+ }
+ }
+ }
+
+ /* newline in tmp string resets ignore flag */
+ if( strrchr(tmp, '\n')) {
+ ignore = 0;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+/**
+ */
+static void
+usb_RestoreCalData( Plustek_Device *dev, CalData *cal )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ regs[0x3b] = (u_char)cal->red_gain;
+ regs[0x3c] = (u_char)cal->green_gain;
+ regs[0x3d] = (u_char)cal->blue_gain;
+ regs[0x38] = (u_char)cal->red_offs;
+ regs[0x39] = (u_char)cal->green_offs;
+ regs[0x3a] = (u_char)cal->blue_offs;
+
+ regs[0x2a] = _HIBYTE((u_short)cal->light.green_pwm_duty);
+ regs[0x2b] = _LOBYTE((u_short)cal->light.green_pwm_duty);
+
+ regs[0x2c] = _HIBYTE((u_short)cal->light.red_light_on);
+ regs[0x2d] = _LOBYTE((u_short)cal->light.red_light_on);
+ regs[0x2e] = _HIBYTE((u_short)cal->light.red_light_off);
+ regs[0x2f] = _LOBYTE((u_short)cal->light.red_light_off);
+
+ regs[0x30] = _HIBYTE((u_short)cal->light.green_light_on);
+ regs[0x31] = _LOBYTE((u_short)cal->light.green_light_on);
+ regs[0x32] = _HIBYTE((u_short)cal->light.green_light_off);
+ regs[0x33] = _LOBYTE((u_short)cal->light.green_light_off);
+
+ regs[0x34] = _HIBYTE((u_short)cal->light.blue_light_on);
+ regs[0x35] = _LOBYTE((u_short)cal->light.blue_light_on);
+ regs[0x36] = _HIBYTE((u_short)cal->light.blue_light_off);
+ regs[0x37] = _LOBYTE((u_short)cal->light.blue_light_off);
+
+ hw->red_lamp_on = (u_short)cal->light.red_light_on;
+ hw->red_lamp_off = (u_short)cal->light.red_light_off;
+ hw->green_lamp_on = (u_short)cal->light.green_light_on;
+ hw->green_lamp_off = (u_short)cal->light.green_light_off;
+ hw->blue_lamp_on = (u_short)cal->light.blue_light_on;
+ hw->blue_lamp_off = (u_short)cal->light.blue_light_off;
+}
+
+/**
+ */
+static void
+usb_CreatePrefix( Plustek_Device *dev, char *pfx, SANE_Bool add_bitdepth )
+{
+ char bd[5];
+ ScanDef *scanning = &dev->scanning;
+ ScanParam *param = &scanning->sParam;
+
+ switch( scanning->sParam.bSource ) {
+
+ case SOURCE_Transparency: strcpy( pfx, "tpa-" ); break;
+ case SOURCE_Negative: strcpy( pfx, "neg-" ); break;
+ case SOURCE_ADF: strcpy( pfx, "adf-" ); break;
+ default: pfx[0] = '\0'; break;
+ }
+
+ sprintf( bd, "%u=", param->bBitDepth );
+ if( param->bDataType == SCANDATATYPE_Color )
+ strcat( pfx, "color" );
+ else
+ strcat( pfx, "gray" );
+
+ if( add_bitdepth )
+ strcat( pfx, bd );
+}
+
+/** function to read and set the calibration data from external file
+ */
+static SANE_Bool
+usb_ReadAndSetCalData( Plustek_Device *dev )
+{
+ char pfx[20];
+ char tmp[1024];
+ u_short version;
+ int res;
+ FILE *fp;
+ CalData cal;
+ SANE_Bool ret;
+
+ DBG( _DBG_INFO, "usb_ReadAndSetCalData()\n" );
+
+ if( usb_InCalibrationMode(dev)) {
+ DBG( _DBG_INFO, "- we are in calibration mode!\n" );
+ return SANE_FALSE;
+ }
+
+ if( NULL == dev->calFile ) {
+ DBG( _DBG_ERROR, "- No calibration filename set!\n" );
+ return SANE_FALSE;
+ }
+
+ sprintf( tmp, "%s-coarse.cal", dev->calFile );
+ DBG( _DBG_INFO, "- Reading coarse calibration data from file\n");
+ DBG( _DBG_INFO, " %s\n", tmp );
+
+ fp = fopen( tmp, "r" );
+ if( NULL == fp ) {
+ DBG( _DBG_ERROR, "File %s not found\n", tmp );
+ return SANE_FALSE;
+ }
+
+ /* check version */
+ if( !usb_ReadSpecLine( fp, "version=", tmp )) {
+ DBG( _DBG_ERROR, "Could not find version info!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO, "- Calibration file version: %s\n", tmp );
+ if( 1 != sscanf( tmp, "0x%04hx", &version )) {
+ DBG( _DBG_ERROR, "Could not decode version info!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ if( version != _PT_CF_VERSION ) {
+ DBG( _DBG_ERROR, "Versions do not match!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ usb_CreatePrefix( dev, pfx, SANE_TRUE );
+
+ ret = SANE_FALSE;
+ if( usb_ReadSpecLine( fp, pfx, tmp )) {
+ DBG( _DBG_INFO, "- Calibration data: %s\n", tmp );
+
+ res = sscanf( tmp, "%hu,%hu,%hu,%hu,%hu,%hu,"
+ "%lu,%lu,%lu,%lu,%lu,%lu,%lu\n",
+ &cal.red_gain, &cal.red_offs,
+ &cal.green_gain, &cal.green_offs,
+ &cal.blue_gain, &cal.blue_offs,
+ &cal.light.red_light_on, &cal.light.red_light_off,
+ &cal.light.green_light_on, &cal.light.green_light_off,
+ &cal.light.blue_light_on, &cal.light.blue_light_off,
+ &cal.light.green_pwm_duty );
+
+ if( 13 == res ) {
+ usb_RestoreCalData( dev, &cal );
+ ret = SANE_TRUE;
+ } else {
+ DBG( _DBG_ERROR, "Error reading coarse-calibration data, only "
+ "%d elements available!\n", res );
+ }
+ } else {
+ DBG( _DBG_ERROR, "Error reading coarse-calibration data for "
+ "PFX: >%s<!\n", pfx );
+ }
+
+ fclose( fp );
+ DBG( _DBG_INFO, "usb_ReadAndSetCalData() done -> %u\n", ret );
+
+ return ret;
+}
+
+/**
+ */
+static void
+usb_PrepCalData( Plustek_Device *dev, CalData *cal )
+{
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ memset( cal, 0, sizeof(CalData));
+ cal->version = _PT_CF_VERSION;
+
+ cal->red_gain = (u_short)regs[0x3b];
+ cal->green_gain = (u_short)regs[0x3c];
+ cal->blue_gain = (u_short)regs[0x3d];
+ cal->red_offs = (u_short)regs[0x38];
+ cal->green_offs = (u_short)regs[0x39];
+ cal->blue_offs = (u_short)regs[0x3a];
+
+ cal->light.green_pwm_duty = regs[0x2a] * 256 + regs[0x2b];
+
+ cal->light.red_light_on = regs[0x2c] * 256 + regs[0x2d];
+ cal->light.red_light_off = regs[0x2e] * 256 + regs[0x2f];
+ cal->light.green_light_on = regs[0x30] * 256 + regs[0x31];
+ cal->light.green_light_off = regs[0x32] * 256 + regs[0x33];
+ cal->light.blue_light_on = regs[0x34] * 256 + regs[0x35];
+ cal->light.blue_light_off = regs[0x36] * 256 + regs[0x37];
+}
+
+/** function to save/update the calibration data
+ */
+static void
+usb_SaveCalData( Plustek_Device *dev )
+{
+ char pfx[20];
+ char fn[1024];
+ char tmp[1024];
+ char set_tmp[1024];
+ char *other_tmp;
+ u_short version;
+ FILE *fp;
+ CalData cal;
+ ScanDef *scanning = &dev->scanning;
+
+ DBG( _DBG_INFO, "usb_SaveCalData()\n" );
+
+ /* no new data, so skip this step too */
+ if( SANE_TRUE == scanning->skipCoarseCalib ) {
+ DBG( _DBG_INFO, "- No calibration data to save!\n" );
+ return;
+ }
+
+ if( NULL == dev->calFile ) {
+ DBG( _DBG_ERROR, "- No calibration filename set!\n" );
+ return;
+ }
+
+ sprintf( fn, "%s-coarse.cal", dev->calFile );
+ DBG( _DBG_INFO, "- Saving coarse calibration data to file\n" );
+ DBG( _DBG_INFO, " %s\n", fn );
+
+ usb_PrepCalData ( dev, &cal );
+ usb_CreatePrefix( dev, pfx, SANE_TRUE );
+ DBG( _DBG_INFO2, "- PFX: >%s<\n", pfx );
+
+ sprintf( set_tmp, "%s%u,%u,%u,%u,%u,%u,"
+ "%lu,%lu,%lu,%lu,%lu,%lu,%lu\n", pfx,
+ cal.red_gain, cal.red_offs,
+ cal.green_gain, cal.green_offs,
+ cal.blue_gain, cal.blue_offs,
+ cal.light.red_light_on, cal.light.red_light_off,
+ cal.light.green_light_on, cal.light.green_light_off,
+ cal.light.blue_light_on, cal.light.blue_light_off,
+ cal.light.green_pwm_duty );
+
+ /* read complete old file if compatible... */
+ other_tmp = NULL;
+ fp = fopen( fn, "r+" );
+ if( NULL != fp ) {
+
+ if( usb_ReadSpecLine( fp, "version=", tmp )) {
+ DBG( _DBG_INFO, "- Calibration file version: %s\n", tmp );
+
+ if( 1 == sscanf( tmp, "0x%04hx", &version )) {
+
+ if( version == cal.version ) {
+
+ DBG( _DBG_INFO, "- Versions do match\n" );
+
+ /* read the rest... */
+ other_tmp = usb_ReadOtherLines( fp, pfx );
+ } else {
+ DBG( _DBG_INFO2, "- Versions do not match (0x%04x)\n", version );
+ }
+ } else {
+ DBG( _DBG_INFO2, "- cannot decode version\n" );
+ }
+ } else {
+ DBG( _DBG_INFO2, "- Version not found\n" );
+ }
+ fclose( fp );
+ }
+ fp = fopen( fn, "w+" );
+ if( NULL == fp ) {
+ DBG( _DBG_ERROR, "- Cannot create file %s\n", fn );
+ DBG( _DBG_ERROR, "- -> %s\n", strerror(errno));
+ if( other_tmp )
+ free( other_tmp );
+ return;
+ }
+
+ /* rewrite the file again... */
+ fprintf( fp, "version=0x%04X\n", cal.version );
+ if( strlen( set_tmp ))
+ fprintf( fp, "%s", set_tmp );
+
+ if( other_tmp ) {
+ fprintf( fp, "%s", other_tmp );
+ free( other_tmp );
+ }
+ fclose( fp );
+ DBG( _DBG_INFO, "usb_SaveCalData() done.\n" );
+}
+
+/**
+ */
+static void
+usb_SaveFineCalData( Plustek_Device *dev, int dpi,
+ u_short *dark, u_short *white, u_long vals )
+{
+ char pfx[30];
+ char fn[1024];
+ char tmp[1024];
+ char *other_tmp;
+ u_short version;
+ u_long i;
+ FILE *fp;
+
+ if( NULL == dev->calFile ) {
+ DBG( _DBG_ERROR, "- No calibration filename set!\n" );
+ return;
+ }
+
+ sprintf( fn, "%s-fine.cal", dev->calFile );
+ DBG( _DBG_INFO, "- Saving fine calibration data to file\n" );
+ DBG( _DBG_INFO, " %s\n", fn );
+
+ usb_CreatePrefix( dev, pfx, SANE_FALSE );
+ sprintf( tmp, "%s:%u", pfx, dpi );
+ strcpy( pfx, tmp );
+ DBG( _DBG_INFO2, "- PFX: >%s<\n", pfx );
+
+ /* read complete old file if compatible... */
+ other_tmp = NULL;
+ fp = fopen( fn, "r+" );
+ if( NULL != fp ) {
+
+ if( usb_ReadSpecLine( fp, "version=", tmp )) {
+ DBG( _DBG_INFO, "- Calibration file version: %s\n", tmp );
+
+ if( 1 == sscanf( tmp, "0x%04hx", &version )) {
+
+ if( version == _PT_CF_VERSION ) {
+ DBG( _DBG_INFO, "- Versions do match\n" );
+
+ /* read the rest... */
+ other_tmp = usb_ReadOtherLines( fp, pfx );
+ } else {
+ DBG( _DBG_INFO2, "- Versions do not match (0x%04x)\n", version );
+ }
+ } else {
+ DBG( _DBG_INFO2, "- cannot decode version\n" );
+ }
+ } else {
+ DBG( _DBG_INFO2, "- Version not found\n" );
+ }
+ fclose( fp );
+ }
+
+ fp = fopen( fn, "w+" );
+ if( NULL == fp ) {
+ DBG( _DBG_ERROR, "- Cannot create file %s\n", fn );
+ return;
+ }
+
+ /* rewrite the file again... */
+ fprintf( fp, "version=0x%04X\n", _PT_CF_VERSION );
+
+ if( other_tmp ) {
+ fprintf( fp, "%s", other_tmp );
+ free( other_tmp );
+ }
+
+ fprintf( fp, "%s:dark:dim=%lu:", pfx, vals );
+ for( i=0; i<vals-1; i++ )
+ fprintf( fp, "%u,", dark[i]);
+ fprintf( fp, "%u\n", dark[vals-1]);
+
+ fprintf( fp, "%s:white:dim=%lu:", pfx, vals );
+ for( i=0; i<vals-1; i++ )
+ fprintf( fp, "%u,", white[i]);
+ fprintf( fp, "%u\n", white[vals-1]);
+
+ fclose( fp );
+}
+
+/** function to read and set the calibration data from external file
+ */
+static SANE_Bool
+usb_ReadFineCalData( Plustek_Device *dev, int dpi,
+ u_long *dim_d, u_short *dark,
+ u_long *dim_w, u_short *white )
+{
+ char pfx[30];
+ char tmp[1024];
+ u_short version;
+ FILE *fp;
+
+ DBG( _DBG_INFO, "usb_ReadFineCalData()\n" );
+ if( usb_InCalibrationMode(dev)) {
+ DBG( _DBG_INFO, "- we are in calibration mode!\n" );
+ return SANE_FALSE;
+ }
+
+ if( NULL == dev->calFile ) {
+ DBG( _DBG_ERROR, "- No calibration filename set!\n" );
+ return SANE_FALSE;
+ }
+
+ sprintf( tmp, "%s-fine.cal", dev->calFile );
+ DBG( _DBG_INFO, "- Reading fine calibration data from file\n");
+ DBG( _DBG_INFO, " %s\n", tmp );
+
+ *dim_d = *dim_w = 0;
+
+ fp = fopen( tmp, "r" );
+ if( NULL == fp ) {
+ DBG( _DBG_ERROR, "File %s not found\n", tmp );
+ return SANE_FALSE;
+ }
+
+ /* check version */
+ if( !usb_ReadSpecLine( fp, "version=", tmp )) {
+ DBG( _DBG_ERROR, "Could not find version info!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO, "- Calibration file version: %s\n", tmp );
+ if( 1 != sscanf( tmp, "0x%04hx", &version )) {
+ DBG( _DBG_ERROR, "Could not decode version info!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ if( version != _PT_CF_VERSION ) {
+ DBG( _DBG_ERROR, "Versions do not match!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ usb_CreatePrefix( dev, pfx, SANE_FALSE );
+
+ sprintf( tmp, "%s:%u:%s:dim=", pfx, dpi, "dark" );
+ if( !usb_ReadSamples( fp, tmp, dim_d, dark )) {
+ DBG( _DBG_ERROR, "Error reading dark-calibration data!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ sprintf( tmp, "%s:%u:%s:dim=", pfx, dpi, "white" );
+ if( !usb_ReadSamples( fp, tmp, dim_w, white )) {
+ DBG( _DBG_ERROR, "Error reading white-calibration data!\n" );
+ fclose( fp );
+ return SANE_FALSE;
+ }
+
+ fclose( fp );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static void
+usb_get_shading_part(u_short *buf, u_long offs, u_long src_len, int dst_len)
+{
+ u_short *p_src, *p_dst;
+ int i, j;
+
+ if (src_len == 0 || dst_len == 0)
+ return;
+
+ p_dst = buf;
+ for (i=0; i<3; i++) {
+
+ p_src = buf + src_len * i + offs;
+
+ for (j=0; j<dst_len; j++) {
+
+ *(p_dst++) = *(p_src++);
+ }
+ }
+}
+
+/** function to read the fine calibration results from file
+ * and to set the correct part of the calibration buffers for
+ * storing in the device
+ * @param dev - the almigthy device structure
+ * @returns SANE_FALSE when the reading fails or SANE_TRUE on success
+ */
+static SANE_Bool
+usb_FineShadingFromFile( Plustek_Device *dev )
+{
+ ScanDef *scan = &dev->scanning;
+ ScanParam *sp = &scan->sParam;
+ u_short xdpi;
+ u_long dim_w, dim_d, offs;
+
+ xdpi = usb_SetAsicDpiX( dev, sp->UserDpi.x );
+
+ if( !usb_ReadFineCalData( dev, xdpi, &dim_d, a_wDarkShading,
+ &dim_w, a_wWhiteShading)) {
+ return SANE_FALSE;
+ }
+
+ /* now we need to get the correct part of the line... */
+ dim_d /= 3;
+ dim_w /= 3;
+
+ offs = ((u_long)sp->Origin.x * xdpi) / 300;
+
+ usb_GetPhyPixels( dev, sp );
+
+ DBG( _DBG_INFO2, "FINE Calibration from file:\n" );
+ DBG( _DBG_INFO2, "XDPI = %u\n", xdpi );
+ DBG( _DBG_INFO2, "Dim = %lu\n", dim_d );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", sp->Size.dwPixels );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", sp->Size.dwPhyPixels );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", sp->Origin.x );
+ DBG( _DBG_INFO2, "Offset = %lu\n", offs );
+
+ usb_get_shading_part(a_wDarkShading, offs, dim_d, sp->Size.dwPhyPixels);
+ usb_get_shading_part(a_wWhiteShading, offs, dim_w, sp->Size.dwPhyPixels);
+
+ return SANE_TRUE;
+}
+
+/** function to save the fine calibration results and to set the correct part
+ * of the calibration buffers for storing in the device
+ * @param dev - the almigthy device structure
+ * @param tmp_sp - intermediate scan parameter
+ */
+static void
+usb_SaveCalSetShading( Plustek_Device *dev, ScanParam *tmp_sp )
+{
+ ScanParam *sp = &dev->scanning.sParam;
+ u_short xdpi;
+ u_long offs;
+
+ if( !dev->adj.cacheCalData )
+ return;
+
+ /* save the values */
+ xdpi = usb_SetAsicDpiX( dev, tmp_sp->UserDpi.x );
+
+ usb_SaveFineCalData( dev, xdpi, a_wDarkShading,
+ a_wWhiteShading, tmp_sp->Size.dwPixels*3 );
+
+ /* now we need to get the correct part of the line... */
+ xdpi = usb_SetAsicDpiX( dev, sp->UserDpi.x );
+ offs = ((u_long)sp->Origin.x * xdpi) / 300;
+ usb_GetPhyPixels( dev, sp );
+
+ DBG( _DBG_INFO2, "FINE Calibration area after saving:\n" );
+ DBG( _DBG_INFO2, "XDPI = %u\n", xdpi );
+ DBG( _DBG_INFO2, "Dim = %lu\n", tmp_sp->Size.dwPixels );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", sp->Size.dwPixels );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", sp->Size.dwPhyPixels );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", sp->Origin.x );
+ DBG( _DBG_INFO2, "Offset = %lu\n", offs );
+
+ if (!usb_InCalibrationMode(dev)) {
+
+ usb_get_shading_part( a_wDarkShading, offs,
+ tmp_sp->Size.dwPixels, sp->Size.dwPhyPixels );
+ usb_get_shading_part( a_wWhiteShading, offs,
+ tmp_sp->Size.dwPixels, sp->Size.dwPhyPixels );
+
+ memcpy( tmp_sp, sp, sizeof(ScanParam));
+ tmp_sp->bBitDepth = 16;
+
+ usb_GetPhyPixels( dev, tmp_sp );
+ }
+}
+
+/* END PLUSTEK-USBCALFILE.C .................................................*/
diff --git a/backend/plustek-usbdevs.c b/backend/plustek-usbdevs.c
new file mode 100644
index 0000000..4f53863
--- /dev/null
+++ b/backend/plustek-usbdevs.c
@@ -0,0 +1,3095 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbdevs.c
+ * @brief Here we have our USB device definitions.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - added EPSON1250 entries
+ * - changed reg 0x58 of EPSON Hw0x04B8_0x010F to 0x0d
+ * - reduced memory size of EPSON to 512
+ * - adjusted tpa origin of UT24
+ * - 0.42 - added register 0x27, 0x2c-0x37
+ * - tweaked EPSON1250 settings according to Gene and Reinhard
+ * - tweaked HP2200 settings according to Stefan
+ * - added UMAX 3400 entries
+ * - added HP2100 settings according to Craig Smoothey
+ * - added LM9832 based U24
+ * - added Canon N650U entry
+ * - 0.43 - tweaked HP 2200C entries
+ * - added _WAF_MISC_IO5 for HP lamp switching
+ * - added motor profiles
+ * - cleanup
+ * - 0.44 - added EPSON 1260 and 660
+ * - added Genius Model strings
+ * - added Canon N670U entry
+ * - added bStepsToReverse to the HwDesc structure
+ * - tweaked EPSON1250 settings for TPA (thanks to Till Kamppeter)
+ * - 0.45 - added UMAX motor settings
+ * - added UMAX 5400 settings
+ * - added CanoScan1240 settings (thanks to Johann Philipp)
+ * - tweaked EPSON 1260 settings
+ * - removed EPSON 660 stuff
+ * - added Canon 1220U entry
+ * - added entry for Compaq S4-100
+ * - 0.46 - fine-tuning for the CanoScan devices
+ * - fixed HP2200 shading position
+ * - renamed to plustek-usbdevs.c
+ * - 0.47 - added BearPaw 1200 settings for PID 0x4001 (LM9832)
+ * - tweaked the LiDE20 MCLK setting for 75DPI grayscale
+ * - enlarged the scan-area of the UMAX3400 to 11.7"
+ * - added CanoScan D660U
+ * - 0.48 - added another incarnation of the UMAX 3400
+ * - added parameters for UMAX 3450 TPA
+ * - parameter tuning for CanoScan D660U
+ * - cleanup
+ * - 0.49 - tweaked motor settings for EPSON and CANON1200
+ * - added support for CanoScan LiDE25
+ * - 0.50 - cleanup
+ * - removed obsolete _WAF_BLACKFINE
+ * - LiDE20 does not seem to have a reliable black calibration area
+ * so the devices now will switch off the lamp for dark calibration
+ * - added Stephan Februarys' <stephanf@singnet.com.sg> LiDE25 changes
+ * - fixed high-speed feature of CanoScan D660U
+ * - tweaked LiDE25, LiDE30 and N1220U settings
+ * - changed high-speed setting for UMAX 3400, due to bugreport #302317
+ * - fixed CanoScan N650U settings
+ * - fixed CanoScan N670U settings, see (bugreport #302738)
+ * - added high-speed setting for HP2200
+ * - 0.51 - tweaked CanoScan N1220U settings again
+ * - added settings for Syscan TravelScan 662
+ * - tweaked settings for Bearpaw 1200
+ * - fixed CanoScan LiDE20 settings, cause of various reports, seems
+ * Canon has built-in different motortypes
+ * - also fixed Motorsettings for LiDE30
+ * - 0.52 - added Q-Scan USB001 settings
+ * - added Q-Scan USB201 settings (thanks to Hiroshi Miura)
+ * - tweaked motor settings for Bearpaw 1200
+ * - added TravelScan 464 settings
+ * - tweaked highspeed motor settings for Epson 1260
+ * - tweaked CanoScan N650U motor settings
+ *
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/* the other stuff is included by plustek.c ...*/
+#include "plustek-usb.h"
+
+/** for Register 0x26
+ */
+#define _ONE_CH_COLOR 0x04
+#define _RED_CH 0x00
+#define _GREEN_CH 0x08
+#define _BLUE_CH 0x10
+
+
+/* Plustek Model: UT12/UT16
+ * KH: NS9831 + TPA + Button + NEC3799
+ */
+static DCapsDef Cap0x07B3_0x0017_0 =
+{
+ { /* Normal */
+ {0, 93}, /* DataOrigin (X: 0, Y: 8mm from home) */
+ 0, -1, /* ShadingOriginY, DarkShadOrgY */
+ {2550, 3508}, /* Size */
+ {50, 50} /* MinDpi */
+ },
+ { /* Positive */
+ {1040 + 15, 744 - 32},/* DataOrigin (X: 7cm + 1.8cm, Y: 8mm + 5.5cm)*/
+ 543, -1, /* ShadingOriginY (Y: 8mm + 3.8cm) */
+ {473, 414}, /* Size (X: 4cm, Y: 3.5cm) */
+ {150, 150} /* MinDpi */
+ },
+ { /* Negative */
+ {1004 + 55, 744 + 12}, /* DataOrigin (X: 7cm + 1.5cm, Y: 8mm + 5.5cm)*/
+
+ /* 533 blaustichig */
+ 537 /* hell */
+ /* 543 gruenstichig */
+
+ /*543*/, -1, /* ShadingOriginY (Y: 8mm + 3.8cm) */
+ {567, 414}, /* Size (X: 4.8cm, Y: 3.5cm) */
+ {150, 150} /* MinDpi */
+ },
+ { /* Adf */
+ {0, 95}, /* DataOrigin (X: 0, Y: 8mm from home) */
+ 0, -1, /* ShadingOriginY, DarkShadOrgY */
+ {2550, 3508}, /* Size */
+ {50, 50} /* MinDpi */
+ },
+ {600, 600}, /* OpticDpi */
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative, /* wFlags */
+ SENSORORDER_rgb, /* bSensorOrder */
+ 4, /* bSensorDistance */
+ 4, /* bButtons */
+ kNEC3799, /* bCCD */
+ 0x07, /* bPCB */
+ _WAF_NONE, /* no workarounds or other special stuff needed */
+ _NO_MIO /* does not use misc I/O for lamp */
+};
+
+/* Plustek Model: U24
+ * Description of the entries, see above...
+ */
+static DCapsDef Cap0x07B3_0x0015_0 =
+{
+ {{0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, {473, 414}, {150, 150}},
+ {{1004 + 20, 744 + 32}, 543, -1, {567, 414}, {150, 150}},
+ {{0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 4, kNEC3799, 0x05, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + TPA + Button + NEC3799
+ */
+static DCapsDef Cap0x07B3_0x0014_0 =
+{
+ {{0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, {473, 414}, {150, 150}},
+ {{1004 + 20, 744 + 32}, 543, -1, {567, 414}, {150, 150}},
+ {{0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 0, kNEC3799, 0x04, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ??? and Genius ColorPage-HR6 V2
+ * KH: NS9831 + TPA + Button + NEC3799
+*/
+static DCapsDef Cap0x07B3_0x0007_0 =
+{
+ {{0, 124}, 36, -1, {2550, 3508}, { 50, 50 }},
+ {{1040, 744}, 543, -1, { 473, 414 }, {150, 150}},
+ {{1004, 744}, 543, -1, { 567, 414 }, {150, 150}},
+ {{0, 95}, 0, -1, {2550, 3508}, { 50, 50 }},
+ {600, 600},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 4, 5, kNEC3799, 0x07, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * Tokyo: NS9832 + Button + SONY548
+ */
+static DCapsDef Cap0x07B3_0x0005_2 =
+{
+ {{ 0, 64}, 0, -1, {2550, 3508}, { 50, 50 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 8, 2, kSONY548, 0x05, _WAF_NONE, _NO_MIO
+};
+
+/* Genius ColorPage-HR7
+ * Hualien: NS9832 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0007_4 =
+{
+ {{ 0, 111 - 4 }, 0, -1, {2550, 3508}, { 50, 50 }},
+ {{1040 + 5, 744 - 32}, 543, -1, { 473, 414 }, {150, 150}},
+ {{1040 - 20, 768 }, 543, -1, { 567, 414 }, {150, 150}},
+ {{ 0, 95 }, 0, -1, {2550, 3508}, { 50, 50 }},
+ {1200, 1200},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 12, 5, kNEC3778, 0x07, _WAF_NONE, _NO_MIO
+};
+
+/* Genius ColorPage-HR7LE and ColorPage-HR6X
+ * Hualien: NS9832 + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0005_4 =
+{
+ {{ 0, 111 - 4 }, 0, -1, {2550, 3508}, {50, 50}},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 12, 5, kNEC3778, 0x05, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???, Genius ColorPage HR6A
+ * KH: NS9831 + TPA + Button + NEC3799
+ */
+static DCapsDef Cap0x07B3_0x000F_0 =
+{
+ {{ 0, 130}, 12, -1, {2550, 3508}, { 50, 50 }},
+ {{1040, 744}, 543, -1, { 473, 414 }, {150, 150}},
+ {{1004, 744}, 543, -1, { 567, 414 }, {150, 150}},
+ {{ 0, 244}, 12, -1, {2550, 4200}, { 50, 50 }},
+ {600, 600},
+ DEVCAPSFLAG_Normal + DEVCAPSFLAG_Adf,
+ SENSORORDER_rgb,
+ 4, 5, kNEC3799, 0x0F, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: UT12
+ * KH: NS9831 + TPA + Button + NEC3799
+ */
+static DCapsDef Cap0x07B3_0x0013_0 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 30, 744 + 32}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 4, 4, kNEC3799, 0x03, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: U24
+ * KH: NS9831 + Button + NEC3799
+// */
+static DCapsDef Cap0x07B3_0x0011_0 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 + 32}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 4, kNEC3799, 0x01, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: U12
+ * KH: NS9831 + TPA + Button + NEC3799
+ */
+static DCapsDef Cap0x07B3_0x0010_0 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 + 32}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 0, kNEC3799, 0x00, _WAF_BSHIFT7_BUG, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0013_4 =
+{
+ {{ 0, 99 /*114*/}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 1055, 744 - 84}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 - 20}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 12, 4, kNEC3778, 0x03, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0011_4 =
+{
+ {{ 0, 99 /*114*/}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 1055, 744 - 84}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 - 20}, 543, -1 ,{ 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 12, 4, kNEC3778, 0x01, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0010_4 =
+{
+ {{ 0, 99 /*114*/}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 1055, 744 - 84}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 - 20}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 12, 0, kNEC3778, 0x00, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: UA18?
+ * KH: NS9831 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x000F_4 =
+{
+ {{ 0, 107}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 1040 + 5, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1040 - 20, 768}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 244}, 0, -1, {2550, 4200}, { 50, 50}},
+ {1200, 1200},
+ DEVCAPSFLAG_Normal + DEVCAPSFLAG_Adf,
+ SENSORORDER_rgb,
+ 12, 5, kNEC3778, 0x0F, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0016_4 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 954, 422}, 272, -1, { 624, 1940}, {150, 150}},
+ {{1120, 438}, 275, -1, { 304, 1940}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 12, 4, kNEC3778, 0x06, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: UT24
+ * KH: NS9832 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0017_4 =
+{
+ {{ 0, 99 - 6}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1025 /*1055*/, 744 - 84}, 543, -1, { 473, 414}, {150, 150}},
+ {{1048 /*1024*/, 754/*724*/}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 12, 4, kNEC3778, 0x07, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: U24
+ * KH: NS9832 + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0015_4 =
+{
+ {{ 0, 99 - 6}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 1055, 744 - 84}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 - 20}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 12, 4, kNEC3778, 0x05, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9832 + TPA + Button + NEC3778
+ */
+static DCapsDef Cap0x07B3_0x0014_4 =
+{
+ {{ 0, 99 - 6}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{ 1055, 744 - 84}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 - 20}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 12, 0, kNEC3778, 0x04, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ??? A3 model
+ * KH: NS9831 + TPA + Button + SONY518
+ */
+static DCapsDef Cap0x07B3_0x0014_1 =
+{
+ {{ 0, 93}, 0, -1, {3600, 5100}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 + 32}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {400, 400},
+ 0,
+ SENSORORDER_rgb,
+ 8, 0, kSONY518, 0x04, _WAF_NONE, _NO_MIO
+};
+
+/* Model: ???
+ * KH: NS9832 + NEC3799 + 600 DPI Motor (for Brother demo only)
+ */
+static DCapsDef Cap0x07B3_0x0012_0 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 20, 744 + 32}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 0, kNEC3799, 0x02, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + TPA + Button + SONY548
+ */
+static DCapsDef Cap0x07B3_0x0017_2 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{ 1004, 744}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_bgr,
+ 8, 4, kSONY548, 0x07, _WAF_NONE, _NO_MIO
+};
+
+/* Plustek Model: ???
+ * KH: NS9831 + TPA + Button + NEC3799
+ */
+static DCapsDef Cap0x07B3_0x0017_3 =
+{
+ {{ 0, 93}, 0, -1, {2550, 3508}, { 50, 50}},
+ {{1040 + 15, 744 - 32}, 543, -1, { 473, 414}, {150, 150}},
+ {{1004 + 30, 744 + 32}, 543, -1, { 567, 414}, {150, 150}},
+ {{ 0, 95}, 0, -1, {2550, 3508}, { 50, 50}},
+ {600, 600},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 8, 4, kNEC8861, 0x07, _WAF_NONE, _NO_MIO
+};
+
+/* Model: HP Scanjet 2100c */
+static DCapsDef Cap0x03F0_0x0505 =
+{
+ {{ 0, 65}, 10, -1, {2550, 3508}, { 50, 50}},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 0, kNECSLIM, 0x00, _WAF_NONE, _NO_MIO
+};
+
+/* Model: HP Scanjet 2200c (thanks to Stefan Nilsen)
+ * NS9832 + 2 Buttons + NEC3799 + 600 DPI Motor
+ */
+static DCapsDef Cap0x03F0_0x0605 =
+{
+ /* DataOrigin (x, y), ShadingOriginY */
+ {{ 0, 209}, 40, -1, {2550, 3508}, { 50, 50}},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 4, 2, kNECSLIM, 0x00, _WAF_NONE, _NO_MIO
+};
+
+/* Mustek BearPaw 1200 (thanks to Henning Meier-Geinitz)
+ * NS9831 + 5 Buttons + NEC3798
+ */
+static DCapsDef Cap0x0400_0x1000_0 =
+{
+ {{ 0, 130}, 20, -1, {2550, 3508}, { 50, 50 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 8,
+ 5, kNEC8861, 0x00,
+ _WAF_MISC_IO_LAMPS | _WAF_LOFF_ON_START | _WAF_USE_ALT_DESC,
+ _NO_MIO
+};
+
+/* Mustek BearPaw 2400
+ * NS9832 + 5 Buttons + SONY548
+ */
+static DCapsDef Cap0x0400_0x1001_0 =
+{
+ {{ 0, 130/*209*/}, 35/*20*/, -1, {2550, 3508}, { 50, 50 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ { 600, 600 }, /*{ 1200, 1200 }, */
+ 0,
+ SENSORORDER_rgb,
+ 4,/*16*/ /* sensor distance */
+ 5, /* number of buttons */
+ kSONY548, /* CCD type */
+ 0, _WAF_USE_ALT_DESC, _NO_MIO
+};
+
+/* Epson Perfection/Photo1250 (thanks to Gene Heskett and Reinhard Max)
+ * Epson Perfection/Photo1260 (thanks to Till Kamppeter)
+ * NS9832 + 4 Buttons + CCD????
+ */
+static DCapsDef Cap0x04B8_0x010F =
+{
+ /* Normal */
+ {{ 25, 85}, 10, -1, {2550, 3508}, { 100, 100 }},
+ /* Positive */
+ {{ 1100, 972}, 720, -1, { 473, 414}, { 150, 150 }},
+ /* Negative */
+ {{ 1116, 1049}, 720, -1, { 567, 414}, { 150, 150 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 8, /* sensor distance */
+ 4, /* number of buttons */
+ kEPSON, /* use default settings during calibration */
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, /* use miscio 6 for lamp switching */
+ _MIO6 + _TPA(_MIO1) /* and miscio 1 for optional TPA */
+};
+
+/* Umax 3400/3450
+ */
+static DCapsDef Cap0x1606_0x0060 =
+{
+ /* Normal */
+ {{ 30, 105 }, 15, -1, {2550, 3508}, { 100, 100 }},
+ /* Positive */
+ {{ 700, 760 }, 650, -1, {1200, 1500}, { 150, 150 }},
+ /* Negative */
+ {{ 700, 760 }, 650, -1, {1200, 1500}, { 150, 150 }},
+ {{ 0, 0 }, 0, -1, {0, 0}, { 0, 0 }},
+ {600, 600},
+ DEVCAPSFLAG_LargeTPA,
+ SENSORORDER_bgr,
+ 8, /* sensor distance */
+ 4, /* number of buttons */
+ kNEC8861, /* use default settings during calibration */
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, /* use miscio 3 for lamp switching */
+ _MIO3 + _TPA(_MIO6) /* and miscio 6 for optional TPA */
+};
+
+/* Umax 5400
+ */
+static DCapsDef Cap0x1606_0x0160 =
+{
+ {{ 30, 165}, 0, -1, {2550, 3508}, {100, 100}},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, -1, {0, 0}, { 0, 0 }},
+ {1200, 1200},
+ 0,
+ SENSORORDER_bgr,
+ 12, /* sensor distance */
+ 4, /* number of buttons */
+ kNEC3778, /* use default settings during calibration */
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, /* use miscio 3 for lamp switching */
+ _MIO3
+};
+
+/* Canon N650U/N656U
+ */
+static DCapsDef Cap0x04A9_0x2206 =
+{
+ {{ 0, 90}, 45, 10, {2550, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 8, /* sensor distance */
+ 1, /* number of buttons */
+ kCIS650, /* use default settings during calibration */
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, _NO_MIO
+};
+
+/* Canon N1220U
+ */
+static DCapsDef Cap0x04A9_0x2207 =
+{
+ {{ 0, 85}, 45, 10, {2550, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 16, /* sensor distance */
+ 1, /* number of buttons */
+ kCIS1220, /* use default settings during calibration */
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, _NO_MIO
+};
+
+/* Canon D660U
+ */
+static DCapsDef Cap0x04A9_0x2208 =
+{
+ {{ 45, 125}, 15, -1, {2550, 3508}, { 50, 50}},
+ {{1060, 744}, 510, -1, { 473, 414}, {150, 150}},
+ {{1082, 842}, 610, -1, { 567, 414}, {150, 150}},
+ {{ 0, 0}, 0, 0, { 0, 0}, { 0, 0}},
+ {600, 600},
+ DEVCAPSFLAG_Positive + DEVCAPSFLAG_Negative,
+ SENSORORDER_rgb,
+ 4,
+ 1,
+ kNEC8861, /* use default settings during calibration */
+ 0,
+ (_WAF_MISC_IO_LAMPS | _WAF_BIN_FROM_COLOR | _WAF_GRAY_FROM_COLOR),
+ _MIO5 + _TPA(_MIO6)
+};
+
+/* Canon N670U/N676U/LiDE20
+ */
+static DCapsDef Cap0x04A9_0x220D =
+{
+ {{ 0, 110}, 45, -1, {2550, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {600, 600},
+ 0,
+ SENSORORDER_rgb,
+ 8, /* sensor distance */
+ 3, /* number of buttons */
+ kCIS670,
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, _NO_MIO
+};
+
+/* Canon N1240U/LiDE30
+ */
+static DCapsDef Cap0x04A9_0x220E =
+{
+ {{ 0, 100}, 50, 10, {2550, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 16, /* sensor distance */
+ 3, /* number of buttons */
+ kCIS1240,
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, _NO_MIO
+};
+
+/* Canon LiDE25
+ */
+static DCapsDef Cap0x04A9_0x2220 =
+{
+ {{ 0, 100}, 50, 10, {2550, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {1200, 1200},
+ 0,
+ SENSORORDER_rgb,
+ 16, /* sensor distance */
+ 3, /* number of buttons */
+ kCIS1240,
+ 0, /* not used here... */
+ _WAF_MISC_IO_LAMPS, _NO_MIO
+};
+
+/* Syscan TravelScan 662 A6 sheet-fed scanner
+ */
+static DCapsDef Cap0x0A82_0x6620 =
+{
+ {{ 0, 0}, 100, -1, {1226, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {600, 600},
+ DEVCAPSFLAG_SheetFed,
+ SENSORORDER_rgb,
+ 8,
+ 1,
+ kNEC8861, /* use default settings during calibration */
+ 0, /* not used here... */
+ (_WAF_MISC_IO_LAMPS | _WAF_MISC_IO_BUTTONS |
+ _WAF_BIN_FROM_COLOR | _WAF_GRAY_FROM_COLOR),
+ _MIO5 + _PORT1 + _PS_INP_MIO2
+};
+
+/* Syscan TravelScan 464 A4 sheet-fed scanner
+ */
+static DCapsDef Cap0x0A82_0x4600 =
+{
+ {{ 0, 0}, 150, -1, {2550, 3508}, {75, 75}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {600, 600},
+ DEVCAPSFLAG_SheetFed,
+ SENSORORDER_rgb,
+ 8,
+ 1,
+ kNEC8861, /* use default settings during calibration */
+ 0, /* not used here... */
+ (_WAF_MISC_IO_LAMPS | _WAF_MISC_IO_BUTTONS |
+ _WAF_BIN_FROM_COLOR | _WAF_GRAY_FROM_COLOR),
+ _MIO5 + _PORT1 + _PS_INP_MIO2
+};
+
+/* IRIScan/Q-Scan USB001 A4 sheet-fed scanner
+ */
+static DCapsDef Cap0x0A53_0x1000 =
+{
+ {{ 0, 0}, 150, -1, {2550, 3508}, {150, 150}},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {300, 300},
+ DEVCAPSFLAG_SheetFed,
+ SENSORORDER_gbr,
+ 2, /* sensor distance */
+ 0, /* number of buttons */
+ kNEC8861, /* use default settings during calibration */
+ 200, /* threshold for resetting sensor-order */
+ (_WAF_MISC_IO_LAMPS | _WAF_RESET_SO_TO_RGB | _WAF_ONLY_8BIT),
+ _PS_INP1
+};
+
+/* PandP USB201 Q-Scan A6 Scanner
+ */
+static DCapsDef Cap0x0A53_0x2000 =
+{
+ {{ 0, 0}, 0, -1, {1226, 3508}, { 50, 50 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {{ 0, 0}, 0, 0, {0, 0}, { 0, 0 }},
+ {600, 600},
+ DEVCAPSFLAG_SheetFed,
+ SENSORORDER_rgb,
+ 4,
+ 0,
+ kNEC8861,
+ 0,
+ _WAF_NONE,
+ _PS_INP1
+};
+
+/******************* additional Hardware descriptions ************************/
+
+/** U24, UT12 and UT16
+ */
+static HWDef Hw0x07B3_0x0017_0 =
+{
+ 1.5, /* dMaxMotorSpeed */
+ 1.2, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 9, /* dIntegrationTimeLowLamp */
+ 9, /* dIntegrationTimeHighLamp */
+ 300, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 4, /* dMinIntegrationTimeLowres (ms) */
+ 5, /* dMinIntegrationTimeHighres (ms) */
+ 3000, /* wGreenPWMDutyCycleLow */
+ 4095, /* wGreenPWMDutyCycleHigh */
+ 0x02, /* bSensorConfiguration (0x0b) */
+ 0x04, /* bReg_0x0c */
+ 0x37, /* bReg_0x0d */
+ 0x13, /* bReg_0x0e */
+ /* bReg_0x0f_Mono [10] (0x0f to 0x18) */
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ /* bReg_0x0f_Color [10] (0x0f to 0x18) */
+ {5, 23, 1, 3, 0, 0, 0, 12, 10, 22},
+
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0, /* bReg 0x27 color mode */
+
+ 1, /* bReg 0x29 illumination mode */
+
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* StepperPhaseCorrection (0x1a & 0x1b) */
+ 14, /* 15, bOpticBlackStart (0x1c) */
+ 62, /* 60, bOpticBlackEnd (0x1d) */
+ 110, /* 65, wActivePixelsStart (0x1e & 0x1f) */
+ 5400, /* 5384 ,wLineEnd (0x20 & 0x21) */
+
+ 0, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 0, /* green lamp on (reg 0x30 + 0x31) */
+ 0, /* green lamp off (reg 0x32 + 0x33) */
+ 0, /* blue lamp on (reg 0x34 + 0x35) */
+ 16383, /* blue lamp off (reg 0x36 + 0x37) */
+
+ /* Misc */
+ 3, /* bReg_0x45 */
+ 0, /* wStepsAfterPaperSensor2 (0x4c & 0x4d) */
+ 0x1e, /* bstepsToReverse reg 0x50) */
+ 0xa8, /* 0xfc -bReg_0x51 */
+ 0, /* bReg_0x54 */
+ 0xff, /* 0xa3 - bReg_0x55 */
+ 64, /* bReg_0x56 */
+ 20, /* bReg_0x57 */
+ 0x0d, /* bReg_0x58 */
+ 0x22, /* bReg_0x59 */
+ 0x82, /* bReg_0x5a */
+ 0x88, /* bReg_0x5b */
+ 0, /* bReg_0x5c */
+ 0, /* bReg_0x5d */
+ 0, /* bReg_0x5e */
+ _LM9832, /* chip type */
+ MODEL_KaoHsiung,/* motorModel */
+ 1.0
+};
+
+/** Genius ColorPage-HR6 V2 and ColorPage-HR6X
+ */
+static HWDef Hw0x07B3_0x0007_0 =
+{
+ 1.5, 1.2, 0.0,
+ 9, 9,
+ 300,
+ 512,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x14, 0x27, 0x13,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 23, 1, 3, 0, 0, 0, 6, 10, 22},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 14,
+ 62,
+ 110,
+ 5384,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x88, 0x28, 0x3b,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_HuaLien,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0007_2 =
+{
+ 1.4, 1.2, 0.0,
+ 9, 9,
+ 600,
+ 512,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x3f, 0x2f, 0x36,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {7, 20, 1, 4, 7, 10, 0, 6, 12, 0},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 16,
+ 64,
+ 152,
+ 5416,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xfc,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x88, 0x28, 0x3b,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_Tokyo600,
+ 1.0
+};
+
+/** Genius ColorPage-HR7 and ColorPage-HR7LE
+ */
+static HWDef Hw0x07B3_0x0007_4 =
+{
+ 1.1, 0.9, 0.0,
+ 12, 12,
+ 600,
+ 2048,
+ 8, 8,
+ 4095, 4095,
+ 0x06, 0x30, 0x2f, 0x2a,
+ {2, 7, 5, 6, 6, 7, 0, 0, 0, 5},
+ {20, 4, 13, 16, 19, 22, 0, 6, 23, 11},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 13,
+ 62,
+ 304,
+ 10684,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 24,
+ 40,
+ 0x0d, 0x88, 0x28, 0x3b,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_HuaLien,
+ 1.0
+};
+
+/** Genius ColorPage-HR6A
+ */
+static HWDef Hw0x07B3_0x000F_0 =
+{
+ 1.5, 1.0, 0.0,
+ 9, 9,
+ 300,
+ 512,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x14, 0x27, 0x13,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 23, 1, 3, 0, 0, 0, 6, 10, 22},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 14,
+ 62,
+ 110,
+ 5384,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x05, 0x88, 0x08, 0x3b,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_HuaLien,
+ 1.0
+};
+
+/** U12/UT12 and U24
+ */
+static HWDef Hw0x07B3_0x0013_0 =
+{
+ 1.5, 1.2, 0.0,
+ 9, 9,
+ 300,
+ 512,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x04, 0x37, 0x13,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 23, 1, 3, 0, 0, 0, 12, 10, 22},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 14,
+ 62,
+ 110,
+ 5400,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9831,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0013_4 =
+{
+ 1.0, 0.9, 0.0,
+ 12, 12,
+ 600,
+ 2048,
+ 8, 8,
+ 4095, 4095,
+ 0x06, 0x20, 0x2f, 0x2a,
+ {2, 7, 5, 6, 6, 7, 0, 0, 0, 5},
+ {20, 4, 13, 16, 19, 22, 0, 0, 23, 11},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 13,
+ 62,
+ 320,
+ 10684,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 10,
+ 48,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9831,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x000F_4 =
+{
+ 1.1, 0.9, 0.0,
+ 12, 12,
+ 600,
+ 2048,
+ 8, 8,
+ 4095, 4095,
+ 0x06, 0x30, 0x2f, 0x2a,
+ {2, 7, 5, 6, 6, 7, 0, 0, 0, 5},
+ {20, 4, 13, 16, 19, 22, 0, 6, 23, 11},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 13,
+ 62,
+ 304,
+ 10684,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 24,
+ 40,
+ 0x05, 0x88, 0x08, 0x3b,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_HuaLien,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0016_4 =
+{
+ 1.0, 0.9, 0.0,
+ 12, 12,
+ 600,
+ 2048,
+ 8, 8,
+ 4095, 4095,
+ 0x06, 0x20, 0x2f, 0x2a,
+ {2, 7, 5, 6, 6, 7, 0, 0, 0, 5},
+ {20, 4, 13, 16, 19, 22, 0, 0, 23, 11},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 13,
+ 62,
+ 320,
+ 10684,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 10,
+ 48,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** Plustek OpticPro UT24 and others...
+ */
+static HWDef Hw0x07B3_0x0017_4 =
+{
+ 1.0, 0.9, 0.0,
+ 12, 12,
+ 600,
+ 2048,
+ 8, 8,
+ 4095, 4095,
+ 0x06, 0x20, 0x2f, 0x2a,
+ {2, 7, 5, 6, 6, 7, 0, 0, 0, 5},
+ {20, 4, 13, 16, 19, 22, 0, 0, 23, 11},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 13,
+ 62,
+ 320,
+ 10684,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 10,
+ 48,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0017_1 =
+{
+ 1.5, 1.5, 0.0,
+ 9, 9,
+ 200,
+ 2048,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x08, 0x2f, 0x36,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 23, 1, 4, 7, 10, 0, 0, 10, 12},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 15,
+ 60,
+ 110,
+ 5415,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0012_0 =
+{
+ 1.5, 1.4, 0.0,
+ 9, 9,
+ 600,
+ 2048,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x04, 0x37, 0x13,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 23, 1, 3, 0, 0, 0, 12, 10, 22},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 14,
+ 62,
+ 110,
+ 5400,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0017_2 =
+{
+ 1.5, 1.2, 0.0,
+ 9, 9,
+ 300,
+ 512,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0, 0x2f, 0x36,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 0, 1, 4, 7, 10, 0, 0, 12, 0},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 16,
+ 64,
+ 110,
+ 5416,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** unknown
+ */
+static HWDef Hw0x07B3_0x0017_3 =
+{
+ 1.5, 1.2, 0.0,
+ 9, 9,
+ 300,
+ 512,
+ 4, 5,
+ 3000, 4095,
+ 0x02, 0x04, 0x37, 0x13,
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+ {5, 23, 1, 4, 7, 10, 0, 0, 11, 23},
+ _GREEN_CH,
+ 0,
+ 1,
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 1,
+ 14,
+ 62,
+ 110,
+ 5400,
+ 0,
+ 16383,
+ 0,
+ 0,
+ 0,
+ 16383,
+ 3,
+ 0,
+ 0x1e,
+ 0xa8,
+ 0,
+ 0xff,
+ 64,
+ 20,
+ 0x0d, 0x22, 0x82, 0x88,
+ 0, 0, 0,
+ _LM9832,
+ MODEL_KaoHsiung,
+ 1.0
+};
+
+/** HP Scanjet 2100C
+ */
+static HWDef Hw0x03F0_0x0505 =
+{
+ 1.05, /* dMaxMotorSpeed */
+ 1.05, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 6, /* dIntegrationTimeLowLamp */
+ 8, /* dIntegrationTimeHighLamp */
+ 600, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 6, /* dMinIntegrationTimeLowres (ms) */
+ 6, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow */
+ 0, /* wGreenPWMDutyCycleHigh */
+ 0x02, /* bSensorConfiguration (0x0b) */
+ 0x00, /* bReg_0x0c */
+ 0x2F, /* bReg_0x0d */
+ 0x13, /* bReg_0x0e */
+ /* bReg_0x0f_Mono[10] (0x0f to 0x18) */
+
+ { 0x02, 0x07, 0x01, 0x02, 0x02, 0x03, 0x00, 0x00, 0x04, 0x07 },
+
+ /* bReg_0x0f_Color[10] (0x0f to 0x18) */
+ { 0x08, 0x17, 0x00, 0x03, 0x08, 0x0b, 0x00, 0x00, 0x0a, 0x14 },
+
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0, /* bReg 0x27 color mode */
+
+ 1, /* bReg 0x29 illumination mode */
+
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* StepperPhaseCorrection (0x1a & 0x1b) */
+ 15, /* bOpticBlackStart (0x1c) */
+ 50, /* bOpticBlackEnd (0x1d) */
+ 140, /* wActivePixelsStart (0x1e & 0x1f) */
+ 5414, /* wLineEnd=(0x20 & 0x21) */
+
+ 1, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 16383, /* green lamp on (reg 0x30 + 0x31) */
+ 1, /* green lamp off (reg 0x32 + 0x33) */
+ 16383, /* blue lamp on (reg 0x34 + 0x35) */
+ 1, /* blue lamp off (reg 0x36 + 0x37) */
+
+ /* Misc */
+ 0x13, /* bReg_0x45 */
+ 0, /* wStepsAfterPaperSensor2 (0x4c & 0x4d) */
+ 0x1e, /* steps to reverse on buffer full reg 0x50 */
+ 0xfc, /* 0xa8 -bReg_0x51 */
+ 0, /* bReg_0x54 */
+ 0x18, /* bReg_0x55 */
+ 8, /* bReg_0x56 */
+ 60, /* bReg_0x57 */
+ 0x0d, /* bReg_0x58 */
+ 0xaa, /* bReg_0x59 */
+ 0xba, /* bReg_0x5a */
+ 0xbb, /* bReg_0x5b */
+ 0, /* bReg_0x5c */
+ 0, /* bReg_0x5d */
+ 0, /* bReg_0x5e */
+ _LM9831,
+ MODEL_HP,
+ 1.0
+};
+
+/** HP Scanjet 2200C */
+static HWDef Hw0x03F0_0x0605 =
+{
+ 1.05, /* dMaxMotorSpeed */
+ 1.05, /* dMaxMoveSpeed */
+ 2.2, /* dHighSpeed */
+ 6, /* dIntegrationTimeLowLamp */
+ 8, /* dIntegrationTimeHighLamp */
+ 600, /* ok wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 6, /* dMinIntegrationTimeLowres (ms) */
+ 6, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow */
+ 0, /* wGreenPWMDutyCycleHigh */
+ 0x02, /* bSensorConfiguration (0x0b) */
+ 0x04, /* bReg_0x0c */
+ 0x2F, /* bReg_0x0d */
+ 0x1F, /* bReg_0x0e */
+
+ /* bReg_0x0f_Mono[10] (0x0f to 0x18) */
+ { 0x02, 0x07, 0x01, 0x02, 0x02, 0x03, 0x00, 0x00, 0x04, 0x07 },
+
+ /* bReg_0x0f_Color[10] (0x0f to 0x18) */
+ { 0x08, 0x17, 0x00, 0x03, 0x08, 0x0b, 0x00, 0x00, 0x0a, 0x14 },
+
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+
+ /* illumination mode settings (not used for CCD devices)*/
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* StepperPhaseCorrection (0x1a & 0x1b) */
+ 14, /* bOpticBlackStart (0x1c) */
+ 63, /* bOpticBlackEnd (0x1d) */
+ 140, /* wActivePixelsStart (0x1e & 0x1f) */
+ 5367, /* wLineEnd=(0x20 & 0x21) */
+
+ 1, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 16383, /* green lamp on (reg 0x30 + 0x31) */
+ 1, /* green lamp off (reg 0x32 + 0x33) */
+ 16383, /* blue lamp on (reg 0x34 + 0x35) */
+ 1, /* blue lamp off (reg 0x36 + 0x37) */
+
+ /* Misc */
+ 0x13, /* bReg_0x45 */
+ 0, /* wStepsAfterPaperSensor2 (0x4c & 0x4d) */
+ 0x1e, /* steps to reverse on buffer full (0x50) */
+ 0xfc, /* 0xa8 -bReg_0x51 */
+ 0, /* bReg_0x54 */
+ 0x18, /* bReg_0x55 */
+ 8, /* bReg_0x56 */
+ 60, /* bReg_0x57 */
+ 0x0d, /* bReg_0x58 */
+ 0xcc, /* bReg_0x59 */
+ 0xbc, /* bReg_0x5a */
+ 0xbb, /* bReg_0x5b */
+ 0, /* bReg_0x5c */
+ 0, /* bReg_0x5d */
+ 0, /* bReg_0x5e */
+ _LM9832,
+ MODEL_HP,
+ 1.0
+};
+
+/** Mustek BearPaw 1200 */
+static HWDef Hw0x0400_0x1000_0 =
+{
+ 1.75, /* ok dMaxMotorSpeed */
+ 1.25, /* ok dMaxMoveSpeed */
+ 0.0, /* ok dHighSpeed */
+ 12, /* ok dIntegrationTimeLowLamp */
+ 12, /* ok dIntegrationTimeHighLamp */
+ 600, /* ok wMotorDpi (Full step DPI) */
+ 512, /* ok wRAMSize (KB) */
+ 9, /* ok dMinIntegrationTimeLowres (ms) */
+ 9, /* ok dMinIntegrationTimeHighres (ms) */
+ 1169, /* ok wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 1169, /* ok wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+ 0x02, /* ok bSensorConfiguration (0x0b) */
+ 0x7c, /* ok sensor control settings (reg 0x0c) */
+ 0x3f, /* ok sensor control settings (reg 0x0d) */
+ 0x15, /* ok sensor control settings (reg 0x0e) */
+ /* ok mono (reg 0x0f to 0x18) */
+ { 0x04, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x03, 0x06},
+ /* ok color (reg 0x0f to 0x18) */
+ { 0x04, 0x16, 0x01, 0x02, 0x05, 0x06, 0x00, 0x00, 0x0a, 0x16},
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+ /* illumination mode settings (not used for CCD devices)*/
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 257, /* ok StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0x0e, /* ok bOpticBlackStart (reg 0x1c) */
+ 0x1d, /* ok bOpticBlackEnd (reg 0x1d) */
+ 140, /* ok wActivePixelsStart (reg 0x1e + 0x1f) */
+ 5369, /* ok wLineEnd (reg 0x20 + 0x21) */
+ 0, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 0, /* green lamp on (reg 0x30 + 0x31) */
+ 0, /* green lamp off (reg 0x32 + 0x33) */
+ 0, /* blue lamp on (reg 0x34 + 0x35) */
+ 16383, /* blue lamp off (reg 0x36 + 0x37) */
+ 0x13, /* ok stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x1e, /* steps to reverse on buffer full (reg 0x50) */
+ 0xfc, /* ok acceleration profile (reg 0x51) */
+ 0, /* ok lines to process (reg 0x54) */
+ 0x13, /* ok kickstart (reg 0x55) */
+ 0x03, /* ok pwm freq (reg 0x56) */
+ 0x20, /* ok pwm duty cycle (reg 0x57) */
+ 0x0d, /* ok Paper sense (reg 0x58) */
+ 0x44, /* ok misc io12 (reg 0x59) */
+ 0x44, /* ok misc io34 (reg 0x5a) */
+ 0x16, /* ok misc io56 (reg 0x5b) */
+ 0, /* ok test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* ok test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* ok test mode (reg 0x5e) */
+ _LM9831,
+ MODEL_MUSTEK600,
+ 1.5
+};
+
+/** Mustek BearPaw 1200 (LM9832) */
+static HWDef Hw0x0400_0x1001_1 =
+{
+ 1.25, /* ok dMaxMotorSpeed */
+ 1.25, /* ok dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 12, /* ok dIntegrationTimeLowLamp */
+ 12, /* ok dIntegrationTimeHighLamp */
+ 600, /* ok wMotorDpi (Full step DPI) */
+ 512, /* ok wRAMSize (KB) */
+ 9, /* ok dMinIntegrationTimeLowres (ms) */
+ 9, /* ok dMinIntegrationTimeHighres (ms) */
+ 1169, /* ok wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 1169, /* ok wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+ 0x02, /* ok bSensorConfiguration (0x0b) */
+ 0x7c, /* ok sensor control settings (reg 0x0c) */
+ 0x3f, /* ok sensor control settings (reg 0x0d) */
+ 0x15, /* ok sensor control settings (reg 0x0e) */
+ /* ok mono (reg 0x0f to 0x18) */
+ { 0x04, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x03, 0x06},
+ /* ok color (reg 0x0f to 0x18) */
+ { 0x04, 0x16, 0x01, 0x02, 0x05, 0x06, 0x00, 0x00, 0x0a, 0x16},
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+ /* illumination mode settings (not used for CCD devices)*/
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ 257, /* ok StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0x0e, /* ok bOpticBlackStart (reg 0x1c) */
+ 0x1d, /* ok bOpticBlackEnd (reg 0x1d) */
+ 140, /* ok wActivePixelsStart (reg 0x1e + 0x1f) */
+ 5369, /* ok wLineEnd (reg 0x20 + 0x21) */
+ 0, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 0, /* green lamp on (reg 0x30 + 0x31) */
+ 0, /* green lamp off (reg 0x32 + 0x33) */
+ 0, /* blue lamp on (reg 0x34 + 0x35) */
+ 16383, /* blue lamp off (reg 0x36 + 0x37) */
+ 0x13, /* ok stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x1e, /* steps to reverse on buffer full (reg 0x50) */
+ 0xfc, /* ok acceleration profile (reg 0x51) */
+ 0, /* ok lines to process (reg 0x54) */
+ 0x13, /* ok kickstart (reg 0x55) */
+ 0x03, /* ok pwm freq (reg 0x56) */
+ 0x20, /* ok pwm duty cycle (reg 0x57) */
+ 0x0d, /* ok Paper sense (reg 0x58) */
+ 0x44, /* ok misc io12 (reg 0x59) */
+ 0x44, /* ok misc io34 (reg 0x5a) */
+ 0x16, /* ok misc io56 (reg 0x5b) */
+ 0, /* ok test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* ok test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* ok test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_MUSTEK600,
+ 1.5
+};
+
+/** BearPaw 2400 */
+static HWDef Hw0x0400_0x1001_0 =
+{
+ 1.0/*1.8*/, /* ok dMaxMotorSpeed */
+ 0.9/*1.8*/, /* ok dMaxMoveSpeed */
+ 0.0, /* ok dHighSpeed */
+ 12, /* ok dIntegrationTimeLowLamp */
+ 12, /* ok dIntegrationTimeHighLamp */
+ 1200 /* 600*/ , /* ok wMotorDpi (Full step DPI) */
+ 2048, /* ok wRAMSize (KB) */
+ 9, /* ok dMinIntegrationTimeLowres (ms) */
+ 9, /* ok dMinIntegrationTimeHighres (ms) */
+ 1169, /* ok wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 1169, /* ok wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x02 /*0x06*/, /* ok bSensorConfiguration (0x0b) */
+ 0x3c, /* ok sensor control settings (reg 0x0c) */
+ 0x3f, /* ok sensor control settings (reg 0x0d) */
+ 0x11, /* ok sensor control settings (reg 0x0e) */
+ /* ok mono (reg 0x0f to 0x18) */
+
+ {2, 7, 0, 1, 0, 0, 0, 0, 4, 0},
+
+/* {5, 14, 12, 15, 18, 21, 0, 0, 0, 9 },*/
+ {1, 4, 4, 5, 6, 7, 0, 0, 0, 3 },
+
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+ /* illumination mode settings (not used for CCD devices)*/
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 257, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 13, /* bOpticBlackStart (reg 0x1c) */
+ 60, /* bOpticBlackEnd (reg 0x1d) */
+ 10, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+5416 /* 11000*/, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 1, /* ok red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* ok red lamp off (reg 0x2e + 0x2f) */
+ 1, /* ok green lamp on (reg 0x30 + 0x31) */
+ 16383, /* ok green lamp off (reg 0x32 + 0x33) */
+ 1, /* ok blue lamp on (reg 0x34 + 0x35) */
+ 16383, /* ok blue lamp off (reg 0x36 + 0x37) */
+
+ 0x03, /* ok stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x1e, /* steps to reverse on buffer full (reg 0x50) */
+ 0xfc, /* ok acceleration profile (reg 0x51) */
+ 0x03, /* ok lines to process (reg 0x54) */
+ 0x13, /* Kickstart 0x55 */
+ 2, /* PWM frequency 0x56 */
+ 32, /* PWM duty cycle 0x57 */
+ 0x15, /* paper sense 0x58 */
+ 0x44, /* misc I/O 0x59 */
+ 0x44, /* misc I/O 0x5a, */
+ 0x46, /* misc I/O 0x5b */
+ 0, 0, 0,/* test registers, set to 0 (0x5c, 0x5d, 0x5e) */
+ _LM9832,
+ MODEL_MUSTEK1200,
+ 1.0
+};
+
+/** EPSON Perfection/Photo 1250 */
+static HWDef Hw0x04B8_0x010F =
+{
+ 0.8, /* dMaxMotorSpeed */
+ 0.8, /* dMaxMoveSpeed */
+ 4.1, /* dHighSpeed */
+ 12, /* dIntegrationTimeLowLamp */
+ 12, /* dIntegrationTimeHighLamp */
+ 600, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 4, /* dMinIntegrationTimeLowres (ms) */
+ 5, /* dMinIntegrationTimeHighres (ms) */
+ 1, /* ok wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 1, /* ok wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x02, /* ok bSensorConfiguration (0x0b) */
+ 0x04, /* ok sensor control settings (reg 0x0c) */
+ 0x7d, /* ok sensor control settings (reg 0x0d) */
+ 0x37, /* ok sensor control settings (reg 0x0e) */
+
+ {0x02, 0x07, 0x00, 0x01, 0x04, 0x07, 0x00, 0x00, 0x03, 0x07},
+ /* ok mono (reg 0x0f to 0x18) */
+ {0x06, 0x16, 0x00, 0x05, 0x0c, 0x17, 0x00, 0x00, 0x0a, 0x17},
+ /* ok color (reg 0x0f to 0x18) */
+ _GREEN_CH, /* ok bReg_0x26 color mode - bits 4 and 5 */
+ 0x40, /* ok bReg 0x27 color mode */
+ 3, /* bReg 0x29 illumination mode */
+ /* illumination mode settings (not used for CCD devices)*/
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* ok StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0x00, /* ok bOpticBlackStart (reg 0x1c) */
+ 0x42, /* ok bOpticBlackEnd (reg 0x1d) */
+ 69, /* ok wActivePixelsStart (reg 0x1e + 0x1f) */
+ 10758, /* ok wLineEnd (reg 0x20 + 0x21) */
+
+ 16383, /* ok red lamp on (reg 0x2c + 0x2d) */
+ 0, /* ok red lamp off (reg 0x2e + 0x2f) */
+ 16383, /* ok green lamp on (reg 0x30 + 0x31) */
+ 0, /* ok green lamp off (reg 0x32 + 0x33) */
+ 16383, /* ok blue lamp on (reg 0x34 + 0x35) */
+ 0, /* ok blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* ok stepper motor control (reg 0x45) */
+ 0, /* ok wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x1e, /* steps to reverse on buffer full (reg 0x50) */
+ 0x0c, /* ok acceleration profile (reg 0x51) */
+ 0, /* ok lines to process (reg 0x54) */
+ 0x0f, /* ok kickstart (reg 0x55) */
+ 0x02, /* ok pwm freq (reg 0x56) */
+ 1, /* ok pwm duty cycle (reg 0x57) */
+
+ 0x0d, /* ok Paper sense (reg 0x58) */
+
+ 0x41, /* ok misc io12 (reg 0x59) */
+ 0x44, /* ok misc io34 (reg 0x5a) */
+ 0x14, /* ok misc io56 (reg 0x5b) */
+ 0, /* ok test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* ok test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* ok test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_EPSON,
+ 1.0
+};
+
+/** EPSON Perfection/Photo 1260 */
+static HWDef Hw0x04B8_0x011D =
+{
+ 0.9, /* dMaxMotorSpeed */
+ 0.8, /* dMaxMoveSpeed */
+ 3.0, /* dHighSpeed */
+ 12, /* dIntegrationTimeLowLamp */
+ 12, /* dIntegrationTimeHighLamp */
+ 600, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 4, /* dMinIntegrationTimeLowres (ms) */
+ 5, /* dMinIntegrationTimeHighres (ms) */
+ 1, /* ok wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 1, /* ok wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x02, /* ok bSensorConfiguration (0x0b) */
+ 0x04, /* ok sensor control settings (reg 0x0c) */
+ 0x7d, /* ok sensor control settings (reg 0x0d) */
+ 0x37, /* ok sensor control settings (reg 0x0e) */
+
+ {0x02, 0x07, 0x00, 0x01, 0x04, 0x07, 0x00, 0x00, 0x03, 0x07},
+ /* ok mono (reg 0x0f to 0x18) */
+ {0x06, 0x0b, 0x00, 0x05, 0x0c, 0x17, 0x00, 0x00, 0x0a, 0x17},
+ /* ok color (reg 0x0f to 0x18) */
+ _GREEN_CH, /* ok bReg_0x26 color mode - bits 4 and 5 */
+ 0x42, /* ok bReg 0x27 color mode */
+ 3, /* bReg 0x29 illumination mode */
+ /* illumination mode settings (not used for CCD devices)*/
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* ok StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0x00, /* ok bOpticBlackStart (reg 0x1c) */
+ 0x42, /* ok bOpticBlackEnd (reg 0x1d) */
+ 69, /* ok wActivePixelsStart (reg 0x1e + 0x1f) */
+ 10766, /* ok wLineEnd (reg 0x20 + 0x21) */
+
+ 16383, /* ok red lamp on (reg 0x2c + 0x2d) */
+ 0, /* ok red lamp off (reg 0x2e + 0x2f) */
+ 16383, /* ok green lamp on (reg 0x30 + 0x31) */
+ 0, /* ok green lamp off (reg 0x32 + 0x33) */
+ 16383, /* ok blue lamp on (reg 0x34 + 0x35) */
+ 0, /* ok blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* ok stepper motor control (reg 0x45) */
+ 0, /* ok wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x1e, /* steps to reverse on buffer full (reg 0x50) */
+ 0x0c, /* ok acceleration profile (reg 0x51) */
+ 0, /* ok lines to process (reg 0x54) */
+ 0x0f, /* ok kickstart (reg 0x55) */
+ 0x02, /* ok pwm freq (reg 0x56) */
+ 1, /* ok pwm duty cycle (reg 0x57) */
+
+ 0x0d, /* ok Paper sense (reg 0x58) */
+
+ 0x41, /* ok misc io12 (reg 0x59) */
+ 0x44, /* ok misc io34 (reg 0x5a) */
+ 0x14, /* ok misc io56 (reg 0x5b) */
+ 0, /* ok test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* ok test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* ok test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_EPSON,
+ 1.5
+};
+
+/** Umax 3400/3450 */
+static HWDef Hw0x1606_0x0060 =
+{
+ 1.5, /* dMaxMotorSpeed */
+ 0.8, /* dMaxMoveSpeed */
+ 2.75, /* dHighSpeed */
+ 9, /* dIntegrationTimeLowLamp */
+ 9, /* dIntegrationTimeHighLamp */
+ 600, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 8, /* dMinIntegrationTimeLowres (ms) */
+ 8, /* dMinIntegrationTimeHighres (ms) */
+ 4095, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 4095, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x06, /* bSensorConfiguration (0x0b) */
+ 0x73, /* sensor control settings (reg 0x0c) */
+ 0x77, /* sensor control settings (reg 0x0d) */
+ 0x15, /* sensor control settings (reg 0x0e) */
+
+ {0x00, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x01, 0x0c, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x0c},
+ /* color (reg 0x0f to 0x18) */
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0x40, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0x2f, /* bOpticBlackStart (reg 0x1c) */
+ 0x3e, /* bOpticBlackEnd (reg 0x1d) */
+ 110, /* ? wActivePixelsStart (reg 0x1e + 0x1f) */
+ 5469, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 1, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 0, /* green lamp on (reg 0x30 + 0x31) */
+ 0, /* green lamp off (reg 0x32 + 0x33) */
+ 32, /* blue lamp on (reg 0x34 + 0x35) */
+ 48, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 11, /* steps to reverse on buffer full (reg 0x50)*/
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 3, /* lines to process (reg 0x54) */
+ 0xcb, /* kickstart (reg 0x55) */
+ 0x05, /* pwm freq (reg 0x56) */
+ 5, /* pwm duty cycle (reg 0x57) */
+
+ 0x0d, /* Paper sense (reg 0x58) */
+
+ 0x44, /* misc io12 (reg 0x59) */
+ 0x45, /* misc io34 (reg 0x5a) */
+ 0x74, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_UMAX,
+ 1.0
+};
+
+/** Umax 5400 */
+static HWDef Hw0x1606_0x0160 =
+{
+ 1.1, /* dMaxMotorSpeed */
+ 0.9, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 9, /* dIntegrationTimeLowLamp */
+ 9, /* dIntegrationTimeHighLamp */
+ 600, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 8, /* dMinIntegrationTimeLowres (ms) */
+ 8, /* dMinIntegrationTimeHighres (ms) */
+ 4095, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 4095, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x06, /* bSensorConfiguration (0x0b) */
+ 0x73, /* sensor control settings (reg 0x0c) */
+ 0x77, /* sensor control settings (reg 0x0d) */
+ 0x25, /* sensor control settings (reg 0x0e) */
+
+ /* mono (reg 0x0f to 0x18) */
+ {0x00, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03},
+
+ /* color (reg 0x0f to 0x18) */
+ {0x01, 0x0c, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x0c},
+
+ _GREEN_CH, /* bReg_0x26 color mode - bits 4 and 5 */
+ 0x40, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 20, /* bOpticBlackStart (reg 0x1c) */
+ 45, /* bOpticBlackEnd (reg 0x1d) */
+ 110, /* ? wActivePixelsStart (reg 0x1e + 0x1f) */
+ 10669, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 1, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 0, /* green lamp on (reg 0x30 + 0x31) */
+ 0, /* green lamp off (reg 0x32 + 0x33) */
+ 32, /* blue lamp on (reg 0x34 + 0x35) */
+ 48, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 11, /* steps to reverse on buffer full (reg 0x50)*/
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 3, /* lines to process (reg 0x54) */
+ 0xcb, /* kickstart (reg 0x55) */
+ 0x05, /* pwm freq (reg 0x56) */
+ 5, /* pwm duty cycle (reg 0x57) */
+
+ 0x0d, /* Paper sense (reg 0x58) */
+
+ 0x44, /* misc io12 (reg 0x59) */
+ 0x45, /* misc io34 (reg 0x5a) */
+ 0x7c, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_UMAX1200,
+ 1.0
+};
+
+/** Canon N650U/N656U */
+static HWDef Hw0x04A9_0x2206 =
+{
+ 0.76, /* dMaxMotorSpeed */
+ 0.243, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* dIntegrationTimeLowLamp */
+ 100, /* dIntegrationTimeHighLamp */
+ 1200, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 0, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x15, /* bSensorConfiguration (0x0b) */
+ 0x4c, /* sensor control settings (reg 0x0c) */
+ 0x2f, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ /* mono & color (reg 0x0f to 0x18) the
+ same for CIS devices */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode (runtime) */
+ /* illumination mode settings */
+ { 3, 0, 0, 23, 850, 0, 0 },
+ { 2, 23, 2500, 23, 1800, 23, 950 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0, /* bOpticBlackStart (reg 0x1c) */
+ 0, /* bOpticBlackEnd (reg 0x1d) */
+ 89, /* ? wActivePixelsStart (reg 0x1e + 0x1f) */
+ 6074, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 23, /* red lamp on (reg 0x2c + 0x2d) */
+ 2500, /* red lamp off (reg 0x2e + 0x2f) */
+ 23, /* green lamp on (reg 0x30 + 0x31) */
+ 1800, /* green lamp off (reg 0x32 + 0x33) */
+ 23, /* blue lamp on (reg 0x34 + 0x35) */
+ 950, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x3f, /* steps to reverse when buffer is full reg 0x50) */
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x0f, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x1f, /* pwm duty cycle (reg 0x57) */
+
+ 0x05, /* Paper sense (reg 0x58) */
+
+ 0x66, /* misc io12 (reg 0x59) */
+ 0x16, /* misc io34 (reg 0x5a) */
+ 0x91, /* misc io56 (reg 0x5b) */
+ 0x01, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_CANON600,
+ 2.0
+};
+
+/** Canon N1220U */
+static HWDef Hw0x04A9_0x2207 =
+{
+ 0.72, /* dMaxMotorSpeed */
+ 0.36, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 1200, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 0, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x15, /* bSensorConfiguration (0x0b) */
+ 0x4c, /* sensor control settings (reg 0x0c) */
+ 0x2f, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ /* mono (reg 0x0f to 0x18) */
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+
+ /* color (reg 0x0f to 0x18) */
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+ { 3, 0, 0, 23, 2200, 0, 0 },
+ { 2, 23, 16383, 23, 15000, 23, 6600 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0, /* bOpticBlackStart (reg 0x1c) */
+ 0, /* bOpticBlackEnd (reg 0x1d) */
+ 124, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 10586, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 23, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 23, /* green lamp on (reg 0x30 + 0x31) */
+ 15000, /* green lamp off (reg 0x32 + 0x33) */
+ 23, /* blue lamp on (reg 0x34 + 0x35) */
+ 6600, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0, /* steps to reverse when buffer is full reg 0x50) */
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x0f, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x1f, /* pwm duty cycle (reg 0x57) */
+
+ 0x05, /* Paper sense (reg 0x58) */
+
+ 0x66, /* misc io12 (reg 0x59) */
+ 0x16, /* misc io34 (reg 0x5a) */
+ 0x91, /* misc io56 (reg 0x5b) */
+ 0x01, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_CANON1200,
+ 2.0
+};
+
+/** Canon D660U */
+static HWDef Hw0x04A9_0x2208 =
+{
+ 1.2, /* dMaxMotorSpeed */
+ 1.1, /* dMaxMoveSpeed */
+ 2.75, /* dHighSpeed */
+ 9, /* dIntegrationTimeLowLamp */
+ 9, /* dIntegrationTimeHighLamp */
+ 300, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 8, /* dMinIntegrationTimeLowres (ms) */
+ 8, /* dMinIntegrationTimeHighres (ms) */
+ 4095, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 4095, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x02, /* bSensorConfiguration (0x0b) */
+ 0x60, /* sensor control settings (reg 0x0c) */
+ 0x2f, /* sensor control settings (reg 0x0d) */
+ 0x13, /* sensor control settings (reg 0x0e) */
+
+ {0x06, 0x17, 0x01, 0x03, 0x05, 0x07, 0x00, 0x00, 0x0b },
+ {0x06, 0x17, 0x01, 0x03, 0x05, 0x07, 0x00, 0x00, 0x0b },
+ _GREEN_CH,
+ 0, /* bReg 0x27 color mode */
+ 1, /* bReg 0x29 illumination mode */
+
+ /* illumination mode settings (not used for CCD devices) */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+
+ 0, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 13, /* bOpticBlackStart (reg 0x1c) */
+ 33, /* bOpticBlackEnd (reg 0x1d) */
+ 64, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 5440, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 16383, /* red lamp on (reg 0x2c + 0x2d) */
+ 1, /* red lamp off (reg 0x2e + 0x2f) */
+ 0, /* green lamp on (reg 0x30 + 0x31) */
+ 0, /* green lamp off (reg 0x32 + 0x33) */
+ 0, /* blue lamp on (reg 0x34 + 0x35) */
+ 0, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x20, /* steps to reverse when buffer is full reg 0x50) */
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0, /* kickstart (reg 0x55) */
+ 0x02, /* pwm freq (reg 0x56) */
+ 0x1f, /* pwm duty cycle (reg 0x57) */
+ 0x05, /* Paper sense (reg 0x58) */
+ 0x44, /* misc io12 (reg 0x59) */
+ 0x14, /* misc io34 (reg 0x5a) */
+ 0x11, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_CANONCCD1200,
+ 1.5
+};
+
+/** Canon 670/676/LiDE20 */
+static HWDef Hw0x04A9_0x220D =
+{
+ 0.72, /* dMaxMotorSpeed */
+ 0.23, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* dIntegrationTimeLowLamp */
+ 100, /* dIntegrationTimeHighLamp */
+ 1200, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 0, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x15, /* bSensorConfiguration (0x0b) */
+ 0x4c, /* sensor control settings (reg 0x0c) */
+ 0x2f, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode (runtime) */
+
+ { 3, 0, 0, 23, 1800, 0, 0 },
+ { 2, 23, 3800, 23, 3300, 23, 2700 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0, /* bOpticBlackStart (reg 0x1c) */
+ 0, /* bOpticBlackEnd (reg 0x1d) */
+ 75, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 6074, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 23, /* red lamp on (reg 0x2c + 0x2d) */
+ 3800, /* red lamp off (reg 0x2e + 0x2f) */
+ 23, /* green lamp on (reg 0x30 + 0x31) */
+ 3300, /* green lamp off (reg 0x32 + 0x33) */
+ 23, /* blue lamp on (reg 0x34 + 0x35) */
+ 2700, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x3f, /* steps to reverse when buffer is full reg 0x50) */
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x0f, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x1f, /* pwm duty cycle (reg 0x57) */
+
+ 0x04, /* Paper sense (reg 0x58) */
+
+ 0x66, /* misc io12 (reg 0x59) */
+ 0x16, /* misc io34 (reg 0x5a) */
+ 0x91, /* misc io56 (reg 0x5b) */
+ 0x01, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9833,
+ MODEL_CANON600,
+ 2.0
+};
+
+/** Canon N1240U/LiDE30 */
+static HWDef Hw0x04A9_0x220E =
+{
+ 0.72, /* dMaxMotorSpeed */
+ 0.30, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 1200, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 0, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x15, /* bSensorConfiguration (0x0b) */
+ 0x4c, /* sensor control settings (reg 0x0c) */
+ 0x2f, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x05},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+
+ { 3, 0, 0, 23, 3000, 0, 0 },
+ { 2, 23, 16383, 23, 6500, 23, 4900 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0, /* bOpticBlackStart (reg 0x1c) */
+ 0, /* bOpticBlackEnd (reg 0x1d) */
+ 52, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 10586, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 23, /* red lamp on (reg 0x2c + 0x2d) */
+ 16383, /* red lamp off (reg 0x2e + 0x2f) */
+ 23, /* green lamp on (reg 0x30 + 0x31) */
+ 6500, /* green lamp off (reg 0x32 + 0x33) */
+ 23, /* blue lamp on (reg 0x34 + 0x35) */
+ 4900, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x20, /* steps to reverse when buffer is full reg 0x50) */
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x0f, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x1f, /* pwm duty cycle (reg 0x57) */
+
+ 0x04, /* Paper sense (reg 0x58) */
+
+ 0x66, /* misc io12 (reg 0x59) */
+ 0x16, /* misc io34 (reg 0x5a) */
+ 0x91, /* misc io56 (reg 0x5b) */
+ 0x01, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9833,
+ MODEL_CANON1200,
+ 2.0
+};
+
+/** Canon LiDE25 */
+static HWDef Hw0x04A9_0x2220 =
+{
+ 0.70, /* dMaxMotorSpeed */
+ 0.25, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 1200, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 0, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 0, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x11, /* bSensorConfiguration (0x0b) */
+ 0x4d, /* sensor control settings (reg 0x0c) */
+ 0x2f, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x07},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x00, 0x00, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x07},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+
+ { 3, 0, 0, 50, 3700, 0, 0 },
+ { 2, 50, 5600, 50, 7100, 50, 4600 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0, /* bOpticBlackStart (reg 0x1c) */
+ 0, /* bOpticBlackEnd (reg 0x1d) */
+ 0x7f, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 10559, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 50, /* red lamp on (reg 0x2c + 0x2d) */
+ 5600, /* red lamp off (reg 0x2e + 0x2f) */
+ 50, /* green lamp on (reg 0x30 + 0x31) */
+ 7100, /* green lamp off (reg 0x32 + 0x33) */
+ 50, /* blue lamp on (reg 0x34 + 0x35) */
+ 4600, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+ 0x20, /* steps to reverse when buffer is full reg 0x50) */
+ 0xfc, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x0f, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x1f, /* pwm duty cycle (reg 0x57) */
+
+ 0x04, /* Paper sense (reg 0x58) */
+
+ 0x66, /* misc io12 (reg 0x59) */
+ 0x16, /* misc io34 (reg 0x5a) */
+ 0x91, /* misc io56 (reg 0x5b) */
+ 0x01, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9833,
+ MODEL_CANON_LIDE25,
+ 2.0
+};
+
+/** TravelScan 662 */
+static HWDef Hw0x0A82_0x6620 =
+{
+ 0.72, /* dMaxMotorSpeed */
+ 0.188, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 740, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 3000, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 3000, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x0d, /* bSensorConfiguration (0x0b) */
+ 0x00, /* sensor control settings (reg 0x0c) */
+ 0x25, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ {0x18, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x18, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+
+ { 3, 0, 0, 10, 450, 0, 0 },
+ { 2, 10, 1000, 10, 880, 10, 630 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 1, /* bOpticBlackStart (reg 0x1c) */
+ 2, /* bOpticBlackEnd (reg 0x1d) */
+ 0x17, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 2600, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 10, /* red lamp on (reg 0x2c + 0x2d) */
+ 1000, /* red lamp off (reg 0x2e + 0x2f) */
+ 10, /* green lamp on (reg 0x30 + 0x31) */
+ 880, /* green lamp off (reg 0x32 + 0x33) */
+ 10, /* blue lamp on (reg 0x34 + 0x35) */
+ 630, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+
+ 0, /* steps to reverse when buffer is full reg 0x50) */
+ 0, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x09, /* kickstart (reg 0x55) */
+ 0x02, /* pwm freq (reg 0x56) */
+ 0x16, /* pwm duty cycle (reg 0x57) */
+
+ 0x01, /* Paper sense (reg 0x58) */
+
+ 0x0e, /* misc io12 (reg 0x59) */
+ 0x96, /* misc io34 (reg 0x5a) */
+ 0x01, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9833,
+ MODEL_TSCAN,
+ 1.8
+};
+
+/** TravelScan 464 */
+static HWDef Hw0x0A82_0x4600 =
+{
+ 0.72, /* dMaxMotorSpeed */
+ 0.26, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 740, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 3000, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 3000, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x0d, /* bSensorConfiguration (0x0b) */
+ 0x00, /* sensor control settings (reg 0x0c) */
+ 0x25, /* sensor control settings (reg 0x0d) */
+ 0x00, /* sensor control settings (reg 0x0e) */
+
+ {0x18, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x18, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+
+ { 3, 500, 1045, 500, 990, 500, 990 },
+ { 2, 500, 1045, 500, 990, 500, 990 },
+
+ 1, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 1, /* bOpticBlackStart (reg 0x1c) */
+ 2, /* bOpticBlackEnd (reg 0x1d) */
+ 0x17, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 5300, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 500, /* red lamp on (reg 0x2c + 0x2d) */
+ 1045, /* red lamp off (reg 0x2e + 0x2f) */
+ 500, /* green lamp on (reg 0x30 + 0x31) */
+ 990, /* green lamp off (reg 0x32 + 0x33) */
+ 500, /* blue lamp on (reg 0x34 + 0x35) */
+ 990, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+
+ 0, /* steps to reverse when buffer is full reg 0x50) */
+ 0, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x09, /* kickstart (reg 0x55) */
+ 0x02, /* pwm freq (reg 0x56) */
+ 0x16, /* pwm duty cycle (reg 0x57) */
+
+ 0x01, /* Paper sense (reg 0x58) */
+
+ 0x0e, /* misc io12 (reg 0x59) */
+ 0x96, /* misc io34 (reg 0x5a) */
+ 0x01, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9833,
+ MODEL_TSCAN_A4,
+ 1.8
+};
+
+/** IRIScan/Q-Scan USB001 - Portable Peripheral Co., Ltd. */
+static HWDef Hw0x0A53_0x1000 =
+{
+ 0.50, /* dMaxMotorSpeed */
+ 0.40, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 300, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 4095, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 4095, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x09, /* bSensorConfiguration (0x0b) */
+ 0x00, /* sensor control settings (reg 0x0c) */
+ 0x65, /* sensor control settings (reg 0x0d) */
+ 0x13, /* sensor control settings (reg 0x0e) */
+
+ {0x02, 0x07, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x02, 0x07, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+
+ { 3, 0, 0, 2593, 4600, 0, 0 },
+ { 2, 2593, 7100, 2593, 4600, 2593, 4480 },
+
+ 256, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0, /* bOpticBlackStart (reg 0x1c) */
+ 0x15, /* bOpticBlackEnd (reg 0x1d) */
+ 0x15, /* wActivePixelsStart (reg 0x1e + 0x1f) */
+ 5500, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 2593, /* red lamp on (reg 0x2c + 0x2d) */
+ 7100, /* red lamp off (reg 0x2e + 0x2f) */
+ 2593, /* green lamp on (reg 0x30 + 0x31) */
+ 4600, /* green lamp off (reg 0x32 + 0x33) */
+ 2593, /* blue lamp on (reg 0x34 + 0x35) */
+ 4480, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 3, /* stepper motor control (reg 0x45) */
+ 0, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+
+ 0, /* steps to reverse when buffer is full reg 0x50) */
+ 0, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x1b, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x15, /* pwm duty cycle (reg 0x57) */
+
+ 0x00, /* Paper sense (reg 0x58) */
+
+ 0x00, /* misc io12 (reg 0x59) */
+ 0x00, /* misc io34 (reg 0x5a) */
+ 0x00, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_QSCAN,
+ 1.1
+};
+
+/** PandP USB201 Q-Scan A6 Scanner */
+static HWDef Hw0x0A53_0x2000 =
+{
+ 0.5, /* dMaxMotorSpeed */
+ 0.2, /* dMaxMoveSpeed */
+ 0.0, /* dHighSpeed */
+ 100, /* wIntegrationTimeLowLamp */
+ 100, /* wIntegrationTimeHighLamp */
+ 570, /* wMotorDpi (Full step DPI) */
+ 512, /* wRAMSize (KB) */
+ 3.75, /* dMinIntegrationTimeLowres (ms) */
+ 5.75, /* dMinIntegrationTimeHighres (ms) */
+ 0x0fff, /* wGreenPWMDutyCycleLow (reg 0x2a + 0x2b) */
+ 0x0fff, /* wGreenPWMDutyCycleHigh (reg 0x2a + 0x2b) */
+
+ 0x02, /* bSensorConfiguration (0x0b) */
+ 0x00, /* sensor control settings (reg 0x0c) */
+ 0x25, /* sensor control settings (reg 0x0d) */
+ 0x06, /* sensor control settings (reg 0x0e) */
+
+ {0x05, 0x0a, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x04, 0x07},
+ /* mono (reg 0x0f to 0x18) */
+
+ {0x05, 0x0a, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x04, 0x07},
+ /* color (reg 0x0f to 0x18) */
+
+ (_BLUE_CH | _ONE_CH_COLOR), /* bReg_0x26 color mode */
+
+ 0x00, /* bReg 0x27 color mode */
+ 2, /* bReg 0x29 illumination mode */
+
+ { 3, 0, 0, 10, 450, 0, 0 },
+ /* initial illumination settings - mono */
+ { 2, 10, 1000, 10, 880, 10, 630 },
+ /* initial illumination settings - color */
+
+ 0x0101, /* StepperPhaseCorrection (reg 0x1a + 0x1b) */
+ 0x004d, /* bOpticBlackStart (reg 0x22 + 0x23) */
+ 0x11, /* bOpticBlackEnd */
+ 0x0011, /* wActivePixelsStart */
+ 0x0bb8, /* wLineEnd (reg 0x20 + 0x21) */
+
+ 10, /* red lamp on (reg 0x2c + 0x2d) */
+ 1000, /* red lamp off (reg 0x2e + 0x2f) */
+ 10, /* green lamp on (reg 0x30 + 0x31) */
+ 880, /* green lamp off (reg 0x32 + 0x33) */
+ 10, /* blue lamp on (reg 0x34 + 0x35) */
+ 630, /* blue lamp off (reg 0x36 + 0x37) */
+
+ 0x13, /* stepper motor control (reg 0x45) */
+ 0x0000, /* wStepsAfterPaperSensor2 (reg 0x4c + 0x4d) */
+
+ 0, /* steps to reverse when buffer is full reg 0x50) */
+ 0, /* acceleration profile (reg 0x51) */
+ 0, /* lines to process (reg 0x54) */
+ 0x1b, /* kickstart (reg 0x55) */
+ 0x08, /* pwm freq (reg 0x56) */
+ 0x15, /* pwm duty cycle (reg 0x57) */
+
+ 0x00, /* Paper sense (reg 0x58) */
+
+ 0x20, /* misc io12 (reg 0x59) */
+ 0x02, /* misc io34 (reg 0x5a) */
+ 0x90, /* misc io56 (reg 0x5b) */
+ 0, /* test mode ADC Output CODE MSB (reg 0x5c) */
+ 0, /* test mode ADC Output CODE LSB (reg 0x5d) */
+ 0, /* test mode (reg 0x5e) */
+ _LM9832,
+ MODEL_QSCAN_A6,
+ 1.8
+};
+
+/******************** all available combinations *****************************/
+
+/** here we have all supported devices and their settings...
+ */
+static SetDef Settings[] =
+{
+ /* Plustek devices... */
+ /* LM9831 based */
+ {"0x07B3-0x0010-0", &Cap0x07B3_0x0010_0, &Hw0x07B3_0x0013_0, "OpticPro U12" },
+ {"0x07B3-0x0011-0", &Cap0x07B3_0x0011_0, &Hw0x07B3_0x0013_0, "OpticPro U24" },
+ {"0x07B3-0x0013-0", &Cap0x07B3_0x0013_0, &Hw0x07B3_0x0013_0, "OpticPro UT12" },
+
+ /* LM9832 based */
+ {"0x07B3-0x0017-0", &Cap0x07B3_0x0017_0, &Hw0x07B3_0x0017_0, "OpticPro UT12/UT16" },
+ {"0x07B3-0x0015-0", &Cap0x07B3_0x0015_0, &Hw0x07B3_0x0017_0, "OpticPro U24" },
+ {"0x07B3-0x0015-4", &Cap0x07B3_0x0015_4, &Hw0x07B3_0x0017_4, "OpticPro U24" },
+ {"0x07B3-0x0017-4", &Cap0x07B3_0x0017_4, &Hw0x07B3_0x0017_4, "OpticPro UT24" },
+
+ /* never seen yet */
+ {"0x07B3-0x0013-4", &Cap0x07B3_0x0013_4, &Hw0x07B3_0x0013_4, "Unknown device" },
+ {"0x07B3-0x0011-4", &Cap0x07B3_0x0011_4, &Hw0x07B3_0x0013_4, "Unknown device" },
+ {"0x07B3-0x0010-4", &Cap0x07B3_0x0010_4, &Hw0x07B3_0x0013_4, "Unknown device" },
+ {"0x07B3-0x0014-0", &Cap0x07B3_0x0014_0, &Hw0x07B3_0x0017_0, "Unknown device" },
+ {"0x07B3-0x0014-4", &Cap0x07B3_0x0014_4, &Hw0x07B3_0x0017_4, "Unknown device" },
+ {"0x07B3-0x0016-4", &Cap0x07B3_0x0016_4, &Hw0x07B3_0x0016_4, "Unknown device" },
+ {"0x07B3-0x0017-2", &Cap0x07B3_0x0017_2, &Hw0x07B3_0x0017_2, "Unknown device" },
+ {"0x07B3-0x0017-3", &Cap0x07B3_0x0017_3, &Hw0x07B3_0x0017_3, "Unknown device" },
+ {"0x07B3-0x0007", &Cap0x07B3_0x0007_0, &Hw0x07B3_0x0007_0, "Unknown device" },
+ {"0x07B3-0x000F", &Cap0x07B3_0x000F_0, &Hw0x07B3_0x000F_0, "Unknown device" },
+ {"0x07B3-0x000F-4", &Cap0x07B3_0x000F_4, &Hw0x07B3_0x000F_4, "Unknown device" },
+ {"0x07B3-0x0005-2", &Cap0x07B3_0x0005_2, &Hw0x07B3_0x0007_2, "Unknown device" }, /* TOKYO 600 */
+ {"0x07B3-0x0014-1", &Cap0x07B3_0x0014_1, &Hw0x07B3_0x0017_1, "Unknown device" }, /* A3 */
+ {"0x07B3-0x0012-0", &Cap0x07B3_0x0012_0, &Hw0x07B3_0x0012_0, "Unknown device" }, /* Brother Demo */
+
+ /* Mustek BearPaw...*/
+ {"0x0400-0x1000-0", &Cap0x0400_0x1000_0, &Hw0x0400_0x1000_0, "BearPaw 1200" },
+ {"0x0400-0x1001-1", &Cap0x0400_0x1000_0, &Hw0x0400_0x1001_1, "BearPaw 1200" },
+ {"0x0400-0x1001-0", &Cap0x0400_0x1001_0, &Hw0x0400_0x1001_0, "BearPaw 2400" },
+
+ /* Genius devices... */
+ {"0x0458-0x2007", &Cap0x07B3_0x0007_0, &Hw0x07B3_0x0007_0, "ColorPage-HR6 V2" },
+ {"0x0458-0x2008", &Cap0x07B3_0x0007_0, &Hw0x07B3_0x0007_0, "ColorPage-HR6 V2" },
+ {"0x0458-0x2009", &Cap0x07B3_0x000F_0, &Hw0x07B3_0x000F_0, "ColorPage-HR6A" },
+ {"0x0458-0x2013", &Cap0x07B3_0x0007_4, &Hw0x07B3_0x0007_4, "ColorPage-HR7" },
+ {"0x0458-0x2015", &Cap0x07B3_0x0005_4, &Hw0x07B3_0x0007_4, "ColorPage-HR7LE" },
+ {"0x0458-0x2016", &Cap0x07B3_0x0005_4, &Hw0x07B3_0x0007_0, "ColorPage-HR6X" },
+
+ /* Hewlett Packard... */
+ {"0x03F0-0x0505", &Cap0x03F0_0x0505, &Hw0x03F0_0x0505, "Scanjet 2100c" },
+ {"0x03F0-0x0605", &Cap0x03F0_0x0605, &Hw0x03F0_0x0605, "Scanjet 2200c" },
+
+ /* EPSON... */
+ {"0x04B8-0x010F", &Cap0x04B8_0x010F, &Hw0x04B8_0x010F, "Perfection 1250/Photo" },
+ {"0x04B8-0x011D", &Cap0x04B8_0x010F, &Hw0x04B8_0x011D, "Perfection 1260/Photo" },
+
+ /* UMAX... */
+ {"0x1606-0x0050", &Cap0x1606_0x0060, &Hw0x1606_0x0060, "3400" },
+ {"0x1606-0x0060", &Cap0x1606_0x0060, &Hw0x1606_0x0060, "3400" },
+ {"0x1606-0x0160", &Cap0x1606_0x0160, &Hw0x1606_0x0160, "5400" },
+
+ /* COMPAQ... */
+ {"0x049F-0x001A", &Cap0x1606_0x0060, &Hw0x1606_0x0060, "S4-100" },
+
+ /* CANON... */
+ {"0x04A9-0x2206", &Cap0x04A9_0x2206, &Hw0x04A9_0x2206, "CanoScan N650U/N656U" },
+ {"0x04A9-0x2207", &Cap0x04A9_0x2207, &Hw0x04A9_0x2207, "CanoScan N1220U" },
+ {"0x04A9-0x2208", &Cap0x04A9_0x2208, &Hw0x04A9_0x2208, "CanoScan D660U" },
+ {"0x04A9-0x220D", &Cap0x04A9_0x220D, &Hw0x04A9_0x220D, "CanoScan N670U/N676U/LiDE20" },
+ {"0x04A9-0x220E", &Cap0x04A9_0x220E, &Hw0x04A9_0x220E, "CanoScan N1240U/LiDE30" },
+ {"0x04A9-0x2220", &Cap0x04A9_0x2220, &Hw0x04A9_0x2220, "CanoScan LiDE25" },
+
+ /* SYSCAN... */
+ {"0x0A82-0x4600", &Cap0x0A82_0x4600, &Hw0x0A82_0x4600, "TravelScan 460/464" },
+ {"0x0A82-0x6620", &Cap0x0A82_0x6620, &Hw0x0A82_0x6620, "TravelScan 662" },
+
+ /* Visioneer... */
+ {"0x04A7-0x0427", &Cap0x0A82_0x4600, &Hw0x0A82_0x4600, "Strobe XP100" },
+
+ /* Portable Peripheral Co., Ltd. */
+ {"0x0A53-0x1000", &Cap0x0A53_0x1000, &Hw0x0A53_0x1000, "Q-Scan USB001" },
+ {"0x0A53-0x2000", &Cap0x0A53_0x2000, &Hw0x0A53_0x2000, "Q-Scan USB201" },
+
+ /* Please add other devices here...
+ * The first entry is a string, composed out of the vendor and product id,
+ * it's used by the driver to select the device settings. For other devices
+ * than those of Plustek, you'll not need to add the second '-' part
+ *
+ * The second entry decribes the capabilities of the device, you may find
+ * one suitable for your scanner, for a better description of the entries
+ * have a look at the beginning of this file at Cap0x07B3_0x0017_0 for
+ * the UT12
+ *
+ * The third entry is for the default setting of the LM983x register
+ * settings, you can often find these in your Windoze driver ini
+ * Have a look at the Hw0x0400_0x1000_0 or Hw0x07B3_0x0017_0 for further
+ * description
+ *
+ * The fourth entry is simply the name of the device, which will be
+ * displayed by the frontend
+ */
+ { NULL, NULL, NULL, NULL } /* last entry, never remove... */
+};
+
+/**
+ * tables for the motor settings
+ * The models KaoHsiung, HuaLien and Tokyo600 are currently set
+ * within the code in conjunction with some CCD combinations.
+ * NOTE: the touples PWM and PWM_Duty are used to set the registers
+ * 0x56 and 0x57, the recommended setting is 8,10
+ * if you notice a whining noise and the motor does not move,
+ * you might increase the MCLK variable.
+ */
+static ClkMotorDef Motors[] = {
+
+ { MODEL_KaoHsiung,
+ 64, 20, 6, /* PWM, PWM_Duty, MCLK for fast move */
+ 0, 0, /* for lineend adjustment, here disabled */
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
+ { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }},
+ /* Color mode MCLK settings */
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ /* Gray mode MCLK settings */
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 }
+ },
+
+ { MODEL_HuaLien, 64, 20, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
+ { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }},
+ /* Color mode MCLK settings */
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ /* Gray mode MCLK settings */
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 }
+ },
+
+ { MODEL_Tokyo600, 4, 4, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
+ { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }},
+ /* Color mode MCLK settings */
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ /* Gray mode MCLK settings */
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 }
+ },
+
+ { MODEL_MUSTEK600, 4, 4, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 },
+ { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }},
+ /* Color mode MCLK settings */
+ { 4.0, 3.5, 3.5, 4.0, 4.0, 5.0, 5.0, 7.5, 7.5, 7.5 },
+ { 4.0, 3.5, 3.5, 4.0, 4.0, 5.0, 5.0, 7.5, 7.5, 7.5 },
+ /* Gray mode MCLK settings */
+ { 7.5, 7.0, 6.5, 5.5, 5.5, 5.5, 7.0, 7.0, 7.0, 7.0 },
+ { 7.5, 7.0, 6.5, 5.5, 5.5, 5.5, 7.0, 7.0, 7.0, 7.0 }
+ },
+
+ { MODEL_MUSTEK1200, 2, 32, 3, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 2, 32, 1 }, { 2, 32, 1 }, { 2, 32, 1 }, { 2, 32, 1 }, { 2, 32, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 2, 32, 1 }, { 2, 32, 1 }, { 2, 32, 1 }, { 2, 32, 1 }, { 2, 32, 1 }},
+ /* Color mode MCLK settings */
+ { 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5 },
+ { 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5 },
+ /* Gray mode MCLK settings */
+ { 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0 },
+ { 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0 }
+ },
+
+ /* settings good for the HP models (tested with 2200)*/
+ { MODEL_HP, 8, 60, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 8, 60, 1 }, { 8, 60, 1 }, { 8, 60, 1 }, { 8, 60, 1 }, { 8, 60, 1 },
+ { 8, 60, 1 }, { 8, 60, 1 }, { 8, 60, 1 }, { 8, 60, 1 }, { 8, 60, 1 }},
+ /* Color mode MCLK settings */
+ { 4.0, 4.0, 4.0, 4.0, 3.0, 4.0, 6.0, 6.0, 6.0, 6.0 },
+ { 4.0, 4.0, 4.0, 4.0, 3.0, 4.0, 6.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 8.0, 8.0, 8.0, 8.0, 8.0, 13.0, 13.0, 13.0, 13.0, 13.0 },
+ { 8.0, 8.0, 8.0, 8.0, 8.0, 13.0, 13.0, 13.0, 13.0, 13.0 }
+ },
+
+ { MODEL_CANON600, 8, 31, 12, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }},
+ /* Color mode MCLK settings */
+ { 7.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 7.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 15.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0 },
+ { 15.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0 }
+ },
+
+ { MODEL_CANON1200, 8, 31, 8, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }},
+ /* Color mode MCLK settings */
+ { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 },
+ { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 5.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 8.5, 7.0, 5.0, 4.0, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5 },
+ { 8.5, 6.5, 6.0, 6.0, 6.0, 6.0, 8.0, 12.0, 12.0, 12.0 }
+ },
+
+ /* settings good for the UMAX models (tested with 3400) */
+ { MODEL_UMAX, 16, 4, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 },
+ { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }},
+ /* Color mode MCLK settings */
+ { 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5 },
+ { 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5 },
+ /* Gray mode MCLK settings */
+ { 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5 },
+ { 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5, 10.5 }
+ },
+
+ { MODEL_UMAX1200, 16, 4, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ {{ 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 },
+ { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }, { 16, 4, 1 }},
+ /* Color mode MCLK settings */
+ { 3.0, 3.0, 3.0, 3.0, 3.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0 },
+ /* Gray mode MCLK settings */
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 13.0, 13.0, 13.0, 13.0, 13.0 },
+ { 13.0, 13.0, 13.0, 13.0, 13.0, 13.0, 13.0, 13.0, 13.0, 13.0 }
+ },
+
+ /* settings good for the EPSON models */
+ { MODEL_EPSON, 2, 1, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 2, 1, 1 }, { 2, 1, 1 }, { 2, 1, 1 }, { 2, 1, 1 }, { 2, 1, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 2, 1, 1 }, { 2, 1, 1 }, { 2, 1, 1 }, { 2, 1, 1 }, { 2, 1, 1 }},
+ /* Color mode MCLK settings */
+ { 2.0, 2.0, 2.5, 2.5, 2.5, 2.5, 4.0, 4.0, 6.0, 6.0 },
+ { 2.0, 2.0, 2.5, 2.5, 3.0, 3.0, 3.0, 4.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 9.0, 9.0, 18.0, 18.0 },
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 8.0, 9.0, 9.0, 18.0, 18.0 }
+ },
+
+ { MODEL_CANONCCD1200, 2, 31, 3, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 2, 31, 1 }, { 2, 31, 1 }, { 2, 31, 1 }, { 2, 31, 1 }, { 2, 31, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 2, 31, 1 }, { 2, 31, 1 }, { 2, 31, 1 }, { 2, 31, 1 }, { 2, 31, 1 }},
+ /* Color mode MCLK settings */
+ { 2.0, 2.0, 2.0, 2.5, 2.5, 2.5, 3.0, 3.5, 5.0, 5.0 },
+ { 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 5.0, 5.0 },
+ /* Gray mode MCLK settings */
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 6.5, 6.5, 6.5, 7.0, 8.0, 8.0, 8.0, 10.0, 10.0, 10.0 }
+ },
+
+ { MODEL_CANON_LIDE25, 8, 31, 3, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }, { 8, 31, 1 }},
+ /* Color mode MCLK settings */
+ { 3.0, 3.0, 3.0, 3.0, 3.5, 3.0, 3.0, 3.0, 3.0, 3.0 },
+ { 3.0, 3.0, 3.0, 3.0, 3.5, 3.5, 5.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 8.5, 7.0, 5.0, 4.0, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5 },
+ { 7.5, 6.5, 6.0, 6.0, 6.0, 6.0, 8.0, 12.0, 12.0, 12.0 }
+ },
+
+ { MODEL_TSCAN, 2, 22, 6, 75, 4000,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }},
+ /* Color mode MCLK settings */
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0 },
+ { 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0 },
+ },
+
+ { MODEL_TSCAN_A4, 2, 22, 6, 75, 8400,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }, { 2, 22, 1 }},
+ /* Color mode MCLK settings */
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0 },
+ { 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0, 16.0 },
+ },
+
+ { MODEL_QSCAN, 8, 21, 6, 300, 4600,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 8, 21, 1 }, { 8, 21, 1 }, { 8, 21, 1 }, { 8, 21, 1 }, { 8, 21, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 8, 21, 1 }, { 8, 21, 1 }, { 8, 21, 1 }, { 8, 21, 1 }, { 8, 21, 1 }},
+ /* Color mode MCLK settings */
+ { 6.5, 6.5, 6.5, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 6.5, 6.5, 6.5, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ /* Gray mode MCLK settings */
+ { 6.5, 6.5, 6.5, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ { 6.5, 6.5, 6.5, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 },
+ },
+
+ { MODEL_QSCAN_A6, 8, 15, 6, 0, 0,
+ /* Motor settings (PWM and PWM_Duty) */
+ /* <=75dpi <=100dpi <=150dpi <=200dpi <=300dpi */
+ {{ 8, 15, 1}, { 8, 15, 1 }, { 8, 15, 1 }, { 8, 15, 1 }, { 8, 15, 1 },
+ /* <=400dpi <=600dpi <=800dpi <=1200dpi <=2400dpi */
+ { 8, 15, 1 }, { 8, 15, 1 }, { 8, 15, 1 }, { 8, 15, 1 }, { 8, 15, 1 }},
+ /* Color mode MCLK settings */
+ { 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5 },
+ { 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5 },
+ /* Gray mode MCLK settings */
+ { 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0 },
+ { 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0 },
+ }
+};
+
+/* END PLUSTEK-USBDEVS.C ....................................................*/
diff --git a/backend/plustek-usbhw.c b/backend/plustek-usbhw.c
new file mode 100644
index 0000000..a1dae68
--- /dev/null
+++ b/backend/plustek-usbhw.c
@@ -0,0 +1,1848 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbhw.c
+ * @brief Functions to control the scanner hardware.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - added EPSON1250 specific stuff
+ * - added alternative usb_IsScannerReady function
+ * - 0.42 - added warmup stuff
+ * - added UMAX 3400 stuff
+ * - fixed problem with minimum wait time...
+ * - 0.43 - added usb_switchLamp for non-Plustek devices
+ * - 0.44 - added bStepsToReverse and active Pixelstart values
+ * - to resetRegister function
+ * - modified getLampStatus function for CIS devices
+ * - added usb_Wait4Warmup()
+ * - moved usb_IsEscPressed to this file
+ * - added usb_switchLampX
+ * - do now not reinitialized MISC I/O pins upon reset registers
+ * - 0.45 - added function usb_AdjustLamps() to tweak CIS lamp settings
+ * - fixed NULL pointer problem in lamp-off ISR
+ * - added usb_AdjustCISLampSettings()
+ * - skipping warmup for CIS devices
+ * - 0.46 - fixed problem in usb_GetLampStatus for CIS devices, as we
+ * read back reg[0x29] to wrong position
+ * made it compile without itimer definitions
+ * - 0.47 - moved usb_HostSwap() and usb_Swap() to this file.
+ * - fixed lampOff timer for systems w/o setitimer
+ * - added lamp off adjustment for CIS devices
+ * - 0.48 - added usb_IsCISDevice()
+ * - added usb_HasTPA()
+ * - changed usb_Wait4Warmup()
+ * - added usb_WaitPos()
+ * - added usb_FillLampRegs() - sets also PWMDutyCylce now
+ * - added UMAX3450 TPA autodetection
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - fixed problem in backtracking, when speedup is enabled
+ * - added usb_UpdateButtonStatus()
+ * - 0.50 - added button support for Plustek/Genius devices
+ * - changed behaviour of usb_IsScannerReady
+ * - added special misc I/O setup for CIS devices (usb_ResetRegisters)
+ * - 0.51 - change usb_AdjustLamps() and use it now in usb_switchLamp()
+ * - added usb_Wait4ScanSample() and usb_InCalibrationMode()
+ * - tweaked EjectPaper to work correctly with the supported sheet-fed
+ * devices
+ * - fixed button handling for Plustek/Genius devices and added
+ * some more debug output to that code path
+ * - 0.52 - changed DCapsDef, lamp -> misc_io
+ * - hammer in output bit, when using misc io pins for lamp switching
+ * - increased wait time for sheet-fed scanner (needed for Q-SCAN A6,
+ * patch by Hiroshi Miura)
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#define DEV_LampReflection 1
+#define DEV_LampTPA 2
+#define DEV_LampAll 3
+#define DEV_LampPositive 4
+#define DEV_LampNegative 5
+
+#define WAIT_TIME_FOR_SCAN_SAMPLE 20 /* 20 seconds maximum wait time */
+
+
+/** the NatSemi 983x is a big endian chip, and the line protocol data all
+ * arrives big-endian. This determines if we need to swap to host-order
+ */
+static SANE_Bool
+usb_HostSwap( void )
+{
+ u_short pattern = 0xfeed; /* deadbeef */
+ unsigned char *bytewise = (unsigned char *)&pattern;
+
+ if ( bytewise[0] == 0xfe ) {
+ DBG( _DBG_READ, "We're big-endian! No need to swap!\n" );
+ return 0;
+ }
+ DBG( _DBG_READ, "We're little-endian! NatSemi LM983x is big!\n" );
+ DBG( _DBG_READ, "--> Must swap data!\n" );
+ return 1;
+}
+
+/** as the name says..
+ */
+static void
+usb_Swap( u_short *pw, u_long dwBytes )
+{
+ for( dwBytes /= 2; dwBytes--; pw++ )
+ _SWAP(((u_char*) pw)[0], ((u_char*)pw)[1]);
+}
+
+/**
+ * This function is used to detect a cancel condition,
+ * our ESC key is the SIGUSR1 signal. It is sent by the backend when the
+ * cancel button has been pressed
+ *
+ * @param - none
+ * @return the function returns SANE_TRUE if a cancel condition has been
+ * detected, if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+usb_IsEscPressed( void )
+{
+ sigset_t sigs;
+
+ sigpending( &sigs );
+ if( sigismember( &sigs, SIGUSR1 )) {
+ DBG( _DBG_INFO, "SIGUSR1 is pending --> Cancel detected\n" );
+ return SANE_TRUE;
+ }
+
+ return SANE_FALSE;
+}
+
+/** usb_GetMotorSet
+ * according to the model, the function returns the address of
+ * the corresponding entry of the Motor table
+ */
+static ClkMotorDef*
+usb_GetMotorSet( eModelDef model )
+{
+ int i;
+
+ for( i = 0; i < MODEL_LAST; i++ ) {
+ if( model == Motors[i].motorModel ) {
+ return &(Motors[i]);
+ }
+ }
+
+ return NULL;
+}
+
+/** switch motor on or off
+ * @param handle - handle to open USB device
+ * @param fOn - SANE_TRUE means motor on, SANE_FALSE means motor off
+ * @return always SANE_TRUE
+ */
+static SANE_Bool
+usb_MotorOn( Plustek_Device *dev, SANE_Bool fOn )
+{
+ if( fOn )
+ dev->usbDev.a_bRegs[0x45] |= 0x10;
+ else
+ dev->usbDev.a_bRegs[0x45] &= ~0x10;
+
+ usbio_WriteReg( dev->fd, 0x45, dev->usbDev.a_bRegs[0x45] );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static SANE_Bool
+usb_IsCISDevice( Plustek_Device *dev )
+{
+ return ( dev->usbDev.HwSetting.bReg_0x26 & _ONE_CH_COLOR );
+}
+
+/**
+ */
+static SANE_Bool
+usb_IsSheetFedDevice( Plustek_Device *dev )
+{
+ return ( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_SheetFed );
+}
+
+/**
+ */
+static SANE_Bool
+usb_InCalibrationMode( Plustek_Device *dev )
+{
+ if((dev->scanning.dwFlag & SCANFLAG_Calibration) == 0)
+ return SANE_FALSE;
+
+ return SANE_TRUE;
+}
+
+/** check if scanner is ready
+ */
+static SANE_Bool
+usb_IsScannerReady( Plustek_Device *dev )
+{
+ u_char value;
+ double len;
+ long timeout;
+ struct timeval t;
+ SANE_Status res;
+
+ /* time in s = 1000*scanner length in inches/max step speed/in */
+ len = (dev->usbDev.Caps.Normal.Size.y/(double)_MEASURE_BASE) + 5;
+ len = (1000.0 * len)/dev->usbDev.HwSetting.dMaxMoveSpeed;
+ len /= 1000.0;
+
+ /* wait at least 10 seconds... */
+ if( len < 10 )
+ len = 10;
+
+ gettimeofday( &t, NULL);
+ timeout = t.tv_sec + len;
+
+ do {
+ res = usbio_ReadReg( dev->fd, 7, &value);
+ if( res != SANE_STATUS_GOOD ) {
+ sleep(1);
+ } else {
+ if( value == 0 ) {
+ _UIO( usbio_ResetLM983x( dev ));
+ return SANE_TRUE;
+ }
+
+ if((value == 0) || (value >= 0x20) || (value == 0x03)) {
+
+ if( !usbio_WriteReg( dev->fd, 0x07, 0 )) {
+ DBG( _DBG_ERROR, "Scanner not ready!!!\n" );
+ return SANE_FALSE;
+ }
+ else {
+ return SANE_TRUE;
+ }
+ }
+ }
+ gettimeofday( &t, NULL);
+
+ } while( t.tv_sec < timeout );
+
+ DBG( _DBG_ERROR, "Scanner not ready!!!\n" );
+ return SANE_FALSE;
+}
+
+/**
+ */
+static SANE_Bool
+usb_SensorAdf( Plustek_Device *dev )
+{
+ u_char value;
+
+ if( usb_IsSheetFedDevice(dev))
+ return SANE_FALSE;
+
+ usbio_ReadReg( dev->fd, 0x02, &value );
+
+ return (value & 0x20);
+}
+
+/**
+ */
+static SANE_Bool
+usb_SensorPaper( Plustek_Device *dev )
+{
+ DCapsDef *sc = &dev->usbDev.Caps;
+ u_char val, mask = 0x02;
+
+ usbio_ReadReg( dev->fd, 0x02, &val );
+
+ if( usb_IsSheetFedDevice(dev))
+ mask = _GET_PAPERSENSE_PORT(sc->misc_io);
+
+ return (val & mask);
+}
+
+/** function for sheet-fed devices, to make sure, that there's
+ * something to scan
+ */
+static SANE_Bool
+usb_Wait4ScanSample( Plustek_Device *dev )
+{
+ struct timeval start_time, t2;
+
+ if( !usb_IsSheetFedDevice(dev))
+ return SANE_TRUE;
+
+ DBG( _DBG_INFO2, "Waiting for something to scan...\n" );
+ gettimeofday( &start_time, NULL );
+ do {
+
+ gettimeofday( &t2, NULL );
+ if( t2.tv_sec > start_time.tv_sec + WAIT_TIME_FOR_SCAN_SAMPLE ) {
+ DBG( _DBG_ERROR, "Nothing to scan!!!\n" );
+ return SANE_FALSE;
+ }
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+ }
+ while( !usb_SensorPaper( dev ));
+
+ /* just a little delay, to make sure the paper is taken by the scanner */
+ usleep(100* 1000);
+ DBG( _DBG_INFO2, "... okay, scanning now!\n" );
+ return SANE_TRUE;
+}
+
+/** function to move the sensor and to speed it up to a certain speed until
+ * the position is reached
+ */
+static SANE_Bool
+usb_WaitPos( Plustek_Device *dev, u_long to, SANE_Bool stay )
+{
+ SANE_Bool retval;
+ u_char value, mclk_div, mch;
+ u_char r[2];
+ u_short ffs, step, min_ffs;
+ long dwTicks;
+ double maxf, fac, speed;
+ struct timeval start_time, t2;
+
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* get current master clock divider */
+ usbio_ReadReg( dev->fd, 0x08, &value );
+ mclk_div = (value >> 1) + 1;
+
+ /* get current channel mode */
+ usbio_ReadReg( dev->fd, 0x26, &value );
+ mch = ((value & 0x07) > 1) ? 1:3;
+
+ /* calculate the current speed */
+ ffs = regs[0x48] * 256 + regs[0x49];
+ speed = ((double)CRYSTAL_FREQ) /(double)((u_long)mclk_div * 32UL *
+ (u_long)mch * (u_long)ffs * hw->wMotorDpi);
+
+ /* disabled ? */
+ if((hw->dHighSpeed == 0.0) || (dev->adj.disableSpeedup != 0)) {
+ min_ffs = 0xffff;
+ maxf = 0.0;
+ if( !stay )
+ return SANE_TRUE;
+
+ } else {
+ min_ffs = (u_short)(CRYSTAL_FREQ /((u_long)mclk_div * 32UL *
+ (u_long)mch * hw->dHighSpeed * hw->wMotorDpi));
+ maxf = (ffs - min_ffs)/4;
+ if( maxf > 100.0 )
+ maxf = 100.0;
+ if( maxf < 5.0 )
+ maxf = 5.0;
+ DBG( _DBG_INFO2, ">>>> CURRENT MCLK_DIV = %u\n", mclk_div );
+ DBG( _DBG_INFO2, ">>>> MCH = %u\n", mch );
+ DBG( _DBG_INFO2, ">>>> FFS = %u\n", ffs );
+ DBG( _DBG_INFO2, ">>>> HIGH-SPEED = %.3f (%.3f)\n",
+ speed, hw->dHighSpeed);
+ DBG( _DBG_INFO2, ">>>> MIN_FFS = %u (%.3f)\n", min_ffs, maxf);
+ }
+
+ gettimeofday( &start_time, NULL );
+ dwTicks = start_time.tv_sec + to;
+ step = 1;
+ retval = SANE_FALSE;
+
+ for(;;) {
+
+ usleep( 1000 );
+ _UIO( usbio_ReadReg( dev->fd, 0x07, &value ));
+
+ if (!value)
+ return SANE_TRUE;
+
+ gettimeofday(&t2, NULL);
+ if( t2.tv_sec > dwTicks )
+ break;
+
+ if( min_ffs != 0xffff ) {
+
+ fac = maxf/step;
+ if( ffs ) {
+ if((u_short)fac < ffs ) {
+ ffs -= fac;
+ if( ffs < min_ffs )
+ ffs = min_ffs;
+ } else {
+ if(ffs != min_ffs )
+ ffs = min_ffs;
+ else
+ ffs = 0;
+ }
+ }
+
+ if( ffs >= min_ffs ) {
+ if((int)fac > 25 )
+ usleep( 150 * 1000 );
+
+ r[0] = (u_char)(ffs >> 8);
+ r[1] = (u_char)(ffs & 0xFF);
+ sanei_lm983x_write(dev->fd, 0x48, r, 2, SANE_TRUE);
+ if(ffs == min_ffs )
+ ffs = 0;
+ } else {
+
+ if( !stay ) {
+ retval = SANE_TRUE;
+ break;
+ }
+ }
+ step++;
+ }
+ }
+ return retval;
+}
+
+/** function to move the sensor or if sheet-fed device, to move the paper.
+ * In moving backward-mode, the home sensor is always turned on.
+ * @param action - what to do
+ * @param steps - steps to move, based on 300dpi, 0 means move forever
+ */
+static SANE_Bool
+usb_ModuleMove( Plustek_Device *dev, u_char action, u_long dwStep )
+{
+ SANE_Bool retval, ejected;
+ u_char bReg2, reg7, mclk_div;
+ u_short wFastFeedStepSize;
+ double dMaxMoveSpeed;
+ ClkMotorDef *clk;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if( action != MOVE_ToPaperSensor &&
+ action != MOVE_EjectAllPapers &&
+ action != MOVE_SkipPaperSensor &&
+ action != MOVE_ToShading && !dwStep ) {
+
+ return SANE_TRUE;
+ }
+
+ if( !usb_IsScannerReady( dev )) {
+ DBG( _DBG_ERROR, "Sensor-position NOT reached\n" );
+ return SANE_FALSE;
+ }
+
+ if( action == MOVE_EjectAllPapers ) {
+
+ double d = hw->dMaxMoveSpeed;
+
+ /* FIXME */
+ if (hw->motorModel == MODEL_QSCAN_A6){
+ DBG( _DBG_INFO2, "Q-SCAN-A6 may not be able to detect ejected papers\n");
+ return SANE_TRUE;
+ }
+
+ hw->dMaxMoveSpeed += 0.8; /* was 0.6 */
+
+ DBG( _DBG_INFO2, "Ejecting paper...\n" );
+ retval = SANE_TRUE;
+ ejected = SANE_FALSE;
+ do {
+ if( usb_SensorPaper(dev)) {
+ if (!usb_ModuleMove(dev,MOVE_SkipPaperSensor, 0 )) {
+ hw->dMaxMoveSpeed = d;
+ return SANE_FALSE;
+ }
+ ejected = SANE_TRUE;
+ }
+
+ if( usb_SensorAdf(dev) &&
+ !usb_ModuleMove(dev,MOVE_ToPaperSensor, 0 )) {
+ hw->dMaxMoveSpeed = d;
+ return SANE_FALSE;
+ }
+
+ if( usb_IsEscPressed()) {
+ retval = SANE_FALSE;
+ break;
+ }
+ } while( usb_SensorPaper(dev));
+
+ /* when the paper is beyond the sensor, we move another 300 steps
+ * to make sure, that the scanned sheet is out of the scanner
+ * BUT: not at startup
+ */
+ if (dev->initialized >= 0 || ejected) {
+ DBG(_DBG_INFO2, "... MORE EJECT...\n");
+ if(!usb_ModuleMove( dev, MOVE_Forward, 300 /* *3 */)) {
+ hw->dMaxMoveSpeed = d;
+ return SANE_FALSE;
+ }
+ }
+
+ usbio_WriteReg( dev->fd, 0x07, 0);
+ usbio_WriteReg( dev->fd, 0x58, regs[0x58]);
+
+ usbio_ReadReg( dev->fd, 0x02, &bReg2 );
+ hw->dMaxMoveSpeed = d;
+ DBG( _DBG_INFO2, "...done\n" );
+ return retval;
+ }
+
+ usbio_WriteReg( dev->fd, 0x0a, 0 );
+
+ /* Compute fast feed step size, use equation 3 and equation 8 */
+ dMaxMoveSpeed = hw->dMaxMoveSpeed;
+
+ if( action == MOVE_ToShading ) {
+ if( hw->dMaxMoveSpeed > 0.5 )
+ dMaxMoveSpeed = hw->dMaxMoveSpeed - 0.5;
+ }
+
+ clk = usb_GetMotorSet( hw->motorModel );
+
+ mclk_div = clk->mclk_fast;
+
+ wFastFeedStepSize = (u_short)(CRYSTAL_FREQ /
+ ((u_long)mclk_div * 8UL * 1 *
+ dMaxMoveSpeed * 4 * hw->wMotorDpi));
+
+ regs[0x48] = (u_char)(wFastFeedStepSize >> 8);
+ regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF);
+
+ dwStep = dwStep * hw->wMotorDpi / 300UL;
+ regs[0x4a] = _HIBYTE(_LOWORD(dwStep));
+ regs[0x4b] = _LOBYTE(_LOWORD(dwStep));
+
+ regs[0x45] |= 0x10;
+
+ DBG( _DBG_INFO2,"MotorDPI=%u, MaxMoveSpeed=%.3f, "
+ "FFStepSize=%u, Steps=%lu\n", hw->wMotorDpi,
+ hw->dMaxMoveSpeed, wFastFeedStepSize, dwStep );
+ DBG( _DBG_INFO2, "MOTOR: "
+ "PWM=0x%02x, PWM_DUTY=0x%02x 0x45=0x%02x "
+ "0x48=0x%02x, 0x49=0x%02x \n",
+ regs[0x56], regs[0x57], regs[0x45],
+ regs[0x48], regs[0x49] );
+
+ DBG( _DBG_INFO2,"MCLK_FFW = %u --> 0x%02x\n", mclk_div, (mclk_div-1)*2 );
+
+ /* The setting for chassis moving is:
+ * MCLK divider = 6, 8 bits/pixel, HDPI divider = 12,
+ * no integration time adjustment and 1 channel grayscale
+ */
+
+ /* MCLK divider = 6 */
+ if( !usbio_WriteReg(dev->fd, 0x08, (mclk_div-1)*2 /*0x0A*/))
+ return SANE_FALSE;
+
+ /* 8 bits/pixel, HDPI divider = 12 */
+ if( !usbio_WriteReg(dev->fd, 0x09, 0x1F))
+ return SANE_FALSE;
+
+ /* Turn off integration time adjustment */
+ if( !usbio_WriteReg(dev->fd, 0x19, 0))
+ return SANE_FALSE;
+
+ /* 1 channel grayscale, green channel */
+ if( !usbio_WriteReg(dev->fd, 0x26, 0x0C))
+ return SANE_FALSE;
+
+ _UIO(sanei_lm983x_write(dev->fd, 0x48, &regs[0x48], 2, SANE_TRUE));
+ _UIO(sanei_lm983x_write(dev->fd, 0x4A, &regs[0x4A], 2, SANE_TRUE));
+
+ /* disable home */
+ if( !usbio_WriteReg(dev->fd, 0x58, regs[0x58] & ~7))
+ return SANE_FALSE;
+
+ if( !usbio_WriteReg(dev->fd, 0x45, regs[0x45] ))
+ return SANE_FALSE;
+
+ if( action == MOVE_Forward || action == MOVE_ToShading )
+ reg7 = 5;
+ else if( action == MOVE_Backward )
+ reg7 = 6;
+ else if( action == MOVE_ToPaperSensor || action == MOVE_EjectAllPapers ||
+ action == MOVE_SkipPaperSensor ) {
+ reg7 = 1;
+ } else {
+ return SANE_TRUE;
+ }
+
+ retval = SANE_FALSE;
+
+ /* start the sensor... */
+ if( usbio_WriteReg( dev->fd, 0x07, reg7 )) {
+
+ long secs;
+ struct timeval start_time, t2;
+
+ /* at least we move 20 seconds before timeout... */
+ gettimeofday( &start_time, NULL );
+ secs = start_time.tv_sec + 20;
+
+ if( action == MOVE_ToPaperSensor ) {
+
+ for(;;) {
+
+ if( usb_SensorPaper( dev )) {
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+ usbio_WriteReg( dev->fd, 0x58, regs[0x58] );
+ usbio_ReadReg ( dev->fd, 0x02, &bReg2 );
+ return SANE_TRUE;
+ }
+
+ gettimeofday(&t2, NULL);
+ if( t2.tv_sec > secs )
+ break;
+ }
+ } else if( action == MOVE_SkipPaperSensor ) {
+
+ for(;;) {
+
+ if( !usb_SensorPaper( dev )) {
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+ usbio_WriteReg( dev->fd, 0x58, regs[0x58] );
+ usbio_ReadReg ( dev->fd, 0x02, &bReg2 );
+ return SANE_TRUE;
+ }
+
+ gettimeofday(&t2, NULL);
+ if( t2.tv_sec > secs )
+ break;
+ }
+ } else {
+
+ retval = usb_WaitPos( dev, 200, SANE_TRUE );
+ }
+
+ usbio_WriteReg( dev->fd, 0x58, regs[0x58] );
+ usbio_ReadReg ( dev->fd, 0x02, &bReg2 );
+ }
+
+ if( !retval )
+ DBG( _DBG_ERROR, "Position NOT reached\n" );
+ return retval;
+}
+
+/**
+ */
+static SANE_Bool
+usb_ModuleToHome( Plustek_Device *dev, SANE_Bool fWait )
+{
+ u_char mclk_div;
+ u_char value;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if( usb_IsSheetFedDevice(dev)) {
+ return usb_ModuleMove( dev, MOVE_EjectAllPapers, 0 );
+ }
+
+ /* Check if merlin is ready for setting command */
+ usbio_WriteReg( dev->fd, 0x58, hw->bReg_0x58 );
+ usbio_ReadReg ( dev->fd, 2, &value );
+ if( value & 1 ) {
+ dev->usbDev.fModFirstHome = SANE_FALSE;
+ return SANE_TRUE;
+ }
+
+ _UIO( usbio_ReadReg( dev->fd, 0x07, &value ));
+
+ if( dev->usbDev.fModFirstHome ) {
+ dev->usbDev.fModFirstHome = SANE_FALSE;
+ if( hw->motorModel != MODEL_Tokyo600 )
+ usb_ModuleMove( dev, MOVE_Forward, hw->wMotorDpi / 2);
+ }
+
+ /* if not homing, do it... */
+ if( value != 2 ) {
+
+ u_short wFastFeedStepSize;
+
+ if( hw->motorModel == MODEL_Tokyo600 ) {
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+ } else {
+ _UIO( usbio_ResetLM983x( dev ));
+ usleep(200*1000);
+ }
+
+ if(!_IS_PLUSTEKMOTOR(hw->motorModel)) {
+
+ ClkMotorDef *clk;
+
+ clk = usb_GetMotorSet( hw->motorModel );
+
+ regs[0x56] = clk->pwm_fast;
+ regs[0x57] = clk->pwm_duty_fast;
+ mclk_div = clk->mclk_fast;
+
+ } else {
+
+ mclk_div = 6;
+
+ if( scaps->OpticDpi.x == 1200 || scaps->bPCB == 2) {
+ switch( hw->motorModel ) {
+
+ case MODEL_KaoHsiung:
+ case MODEL_HuaLien:
+ default:
+ regs[0x56] = 1;
+ regs[0x57] = 63;
+ break;
+ }
+ } else { /* if(Device.Caps.OpticDpi.x == 600) */
+
+ switch( hw->motorModel ) {
+
+ case MODEL_Tokyo600:
+ regs[0x56] = 4;
+ regs[0x57] = 4; /* 2; */
+ break;
+ case MODEL_HuaLien:
+ if( dev->caps.dwFlag & SFLAG_ADF ) {
+ regs[0x56] = 64; /* 32; */
+ regs[0x57] = 4; /* 16; */
+ } else {
+ regs[0x56] = 32;
+ regs[0x57] = 16;
+ }
+ break;
+
+ case MODEL_KaoHsiung:
+ default:
+ regs[0x56] = 64;
+ regs[0x57] = 20;
+ break;
+ }
+ }
+ }
+
+ /* Compute fast feed step size, use equation 3 and equation 8
+ * assumptions: MCLK = 6, Lineratemode (CM=1)
+ */
+ wFastFeedStepSize = (u_short)(CRYSTAL_FREQ / (mclk_div * 8 * 1 *
+ hw->dMaxMotorSpeed * 4 * hw->wMotorDpi));
+ regs[0x48] = (u_char)(wFastFeedStepSize >> 8);
+ regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF);
+ regs[0x4a] = 0;
+ regs[0x4b] = 0;
+
+ regs[0x45] |= 0x10;
+
+ DBG( _DBG_INFO2,"MotorDPI=%u, MaxMotorSpeed=%.3f, FFStepSize=%u\n",
+ hw->wMotorDpi, hw->dMaxMotorSpeed, wFastFeedStepSize );
+ DBG( _DBG_INFO, "MOTOR: "
+ "PWM=0x%02x, PWM_DUTY=0x%02x 0x45=0x%02x "
+ "0x48=0x%02x, 0x49=0x%02x\n",
+ regs[0x56], regs[0x57],
+ regs[0x45], regs[0x48], regs[0x49] );
+
+ /* The setting for chassis moving is:
+ * MCLK divider = 6, 8 bits/pixel, HDPI divider = 12,
+ * no integration time adjustment and 1 channel grayscale
+ */
+ /* MCLK divider = 6 */
+ value = (u_char)((mclk_div-1) * 2);
+
+ DBG( _DBG_INFO, "MCLK_FFW = %u --> 0x%02x\n", mclk_div, value );
+
+ if( !usbio_WriteReg(dev->fd, 0x08, value))
+ return SANE_FALSE;
+
+ /* 8 bits/pixel, HDPI divider = 12 */
+ if( !usbio_WriteReg(dev->fd, 0x09, 0x1F))
+ return SANE_FALSE;
+
+ /* Turn off integration time adjustment */
+ if( !usbio_WriteReg(dev->fd, 0x19, 0))
+ return SANE_FALSE;
+
+ /* 1 channel grayscale, green channel */
+ if( !usbio_WriteReg(dev->fd, 0x26, 0x8C))
+ return SANE_FALSE;
+
+ _UIO(sanei_lm983x_write(dev->fd, 0x48, &regs[0x48], 4, SANE_TRUE));
+ _UIO(sanei_lm983x_write(dev->fd, 0x56, &regs[0x56], 3, SANE_TRUE));
+
+ if( !usbio_WriteReg(dev->fd, 0x45, regs[0x45]))
+ return SANE_FALSE;
+
+ usbio_WriteReg(dev->fd, 0x0a, 0);
+
+ if( hw->motorModel == MODEL_HuaLien && scaps->OpticDpi.x == 600 )
+ usleep(100 * 1000);
+
+ if( !usbio_WriteReg(dev->fd, 0x07, 2))
+ return SANE_FALSE;
+
+#if 0
+ if( hw->motorModel == MODEL_Tokyo600) {
+
+ u_long dwSpeedUp = GetTickCount () + 250;
+
+ /* while(GetTickCount () < dwSpeedUp) */
+ while((int)(dwSpeedUp - GetTickCount ()) > 0)
+ {
+ Sleep (10);
+ if (!ReadRegister (0x07, &value))
+ return FALSE;
+ if (!value)
+ return TRUE;
+ }
+ wFastFeedStepSize = (WORD)(CRYSTAL_FREQ /
+ (6UL * 8UL * 1 * Device.HwSetting.dMaxMotorSpeed * 4 *
+ Device.HwSetting.wMotorDpi) * 60 / 78);
+ regs[0x48] = (u_char)(wFastFeedStepSize >> 8);
+ regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF);
+ WriteRegisters(0x48, &regs[0x48], 2);
+ }
+#endif
+ }
+ return usb_WaitPos( dev, 150, fWait );
+}
+
+/**
+ */
+static SANE_Bool
+usb_MotorSelect( Plustek_Device *dev, SANE_Bool fADF )
+{
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if(!_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ return SANE_TRUE;
+ }
+
+ if( fADF ) {
+
+ if( sCaps->bCCD == kNEC3778 ) {
+
+ hw->wMotorDpi = 300;
+ hw->dMaxMotorSpeed = 1.5;
+ hw->dMaxMoveSpeed = 1.5;
+ sCaps->OpticDpi.y = 600;
+ }
+ regs[0x5b] |= 0x80;
+
+ } else {
+
+ if( sCaps->bCCD == kNEC3778 ) {
+
+ hw->wMotorDpi = 600;
+ hw->dMaxMotorSpeed = 1.1;
+ hw->dMaxMoveSpeed = 0.9;
+ sCaps->OpticDpi.y = 1200;
+ }
+ regs[0x5b] &= ~0x80;
+ }
+
+ /* To stop the motor moving */
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+ usleep(10 * 1000);
+
+ usbio_WriteReg( dev->fd, 0x5b, regs[0x5b] );
+ return SANE_TRUE;
+}
+
+/** function to adjust the lamp settings of a CIS device without tweaking
+ * the driver-device settings
+ * @param dev - our almitghty device structure
+ * @param on - switch the lamp on or off
+ */
+static SANE_Bool
+usb_AdjustLamps( Plustek_Device *dev, SANE_Bool on )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if( !usb_IsCISDevice(dev))
+ return SANE_TRUE;
+
+ DBG(_DBG_INFO2, "usb_AdjustLamps(%u)\n", on );
+
+ if( on ) {
+ regs[0x2c] = _HIBYTE(hw->red_lamp_on);
+ regs[0x2d] = _LOBYTE(hw->red_lamp_on);
+ regs[0x2e] = _HIBYTE(hw->red_lamp_off);
+ regs[0x2f] = _LOBYTE(hw->red_lamp_off);
+
+ regs[0x30] = _HIBYTE(hw->green_lamp_on);
+ regs[0x31] = _LOBYTE(hw->green_lamp_on);
+ regs[0x32] = _HIBYTE(hw->green_lamp_off);
+ regs[0x33] = _LOBYTE(hw->green_lamp_off);
+
+ regs[0x34] = _HIBYTE(hw->blue_lamp_on);
+ regs[0x35] = _LOBYTE(hw->blue_lamp_on);
+ regs[0x36] = _HIBYTE(hw->blue_lamp_off);
+ regs[0x37] = _LOBYTE(hw->blue_lamp_off);
+
+ } else {
+ memset( &regs[0x2c], 0, 12 );
+
+ regs[0x2c] = 0x3f;
+ regs[0x2d] = 0xff;
+ regs[0x30] = 0x3f;
+ regs[0x31] = 0xff;
+ regs[0x34] = 0x3f;
+ regs[0x35] = 0xff;
+ }
+
+ return sanei_lm983x_write( dev->fd, 0x2c,
+ &regs[0x2c], 0x37-0x2c+1, SANE_TRUE );
+}
+
+/**
+ */
+static void
+usb_AdjustCISLampSettings( Plustek_Device *dev, SANE_Bool on )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ if( !usb_IsCISDevice(dev))
+ return;
+
+ DBG( _DBG_INFO2, "AdjustCISLamps(%u)\n", on );
+
+ if((dev->scanning.sParam.bDataType == SCANDATATYPE_Gray) ||
+ (dev->scanning.sParam.bDataType == SCANDATATYPE_BW)) {
+
+ DBG( _DBG_INFO2, " * setting mono mode\n" );
+ hw->bReg_0x29 = hw->illu_mono.mode;
+
+ memcpy( &hw->red_lamp_on,
+ &hw->illu_mono.red_lamp_on, sizeof(u_short) * 6 );
+
+ } else {
+
+ DBG( _DBG_INFO2, " * setting color mode\n" );
+ hw->bReg_0x29 = hw->illu_color.mode;
+
+ memcpy( &hw->red_lamp_on,
+ &hw->illu_color.red_lamp_on, sizeof(u_short) * 6 );
+ }
+
+ if( !on ) {
+
+ hw->red_lamp_on = 0x3fff;
+ hw->red_lamp_off = 0;
+ hw->green_lamp_on = 0x3fff;
+ hw->green_lamp_off = 0;
+ hw->blue_lamp_on = 0x3fff;
+ hw->blue_lamp_off = 0;
+ } else {
+
+ if( dev->adj.rlampoff > 0 ) {
+ hw->red_lamp_off = dev->adj.rlampoff;
+
+ if( hw->red_lamp_off > 0x3fff )
+ hw->red_lamp_off = 0x3fff;
+ DBG( _DBG_INFO2,
+ " * red_lamp_off adjusted: %u\n", hw->red_lamp_off );
+ }
+
+ if( dev->adj.glampoff > 0 ) {
+ hw->green_lamp_off = dev->adj.glampoff;
+
+ if( hw->green_lamp_off > 0x3fff )
+ hw->green_lamp_off = 0x3fff;
+ DBG( _DBG_INFO2,
+ " * green_lamp_off adjusted: %u\n", hw->green_lamp_off );
+ }
+
+ if( dev->adj.blampoff > 0 ) {
+ hw->blue_lamp_off = dev->adj.blampoff;
+
+ if( hw->blue_lamp_off > 0x3fff )
+ hw->blue_lamp_off = 0x3fff;
+ DBG( _DBG_INFO2,
+ " * blue_lamp_off adjusted: %u\n", hw->blue_lamp_off );
+ }
+ }
+
+ dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29;
+ usb_AdjustLamps( dev, on );
+}
+
+/** according to the flag field, we return the register and
+ * it's mask to turn on/off the lamp.
+ * @param flag - field to check
+ * @param reg - pointer to a var to receive the register value
+ * @param msk - pointer to a var to receive the mask value
+ * @return Nothing
+ */
+static void
+usb_GetLampRegAndMask( u_long flag, SANE_Byte *reg, SANE_Byte *msk )
+{
+ if( _MIO6 == ( _MIO6 & flag )) {
+ *reg = 0x5b;
+ *msk = 0x80;
+
+ } else if( _MIO5 == ( _MIO5 & flag )) {
+ *reg = 0x5b;
+ *msk = 0x08;
+
+ } else if( _MIO4 == ( _MIO4 & flag )) {
+ *reg = 0x5a;
+ *msk = 0x80;
+
+ } else if( _MIO3 == ( _MIO3 & flag )) {
+ *reg = 0x5a;
+ *msk = 0x08;
+
+ } else if( _MIO2 == ( _MIO2 & flag )) {
+ *reg = 0x59;
+ *msk = 0x80;
+
+ } else if( _MIO1 == ( _MIO1 & flag )) {
+ *reg = 0x59;
+ *msk = 0x08;
+
+ } else {
+ *reg = 0;
+ *msk = 0;
+ }
+}
+
+/** usb_GetLampStatus
+ * This function returns the current lamp in use.
+ * For non Plustek devices, it always returns DEV_LampReflection.
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @return - 0 if the scanner hasn't been used before, DEV_LampReflection
+ * for the normal lamp, or DEV_LampTPA for negative/transparency
+ * lamp
+ */
+static int
+usb_GetLampStatus( Plustek_Device *dev )
+{
+ int iLampStatus = 0;
+ u_char *regs = dev->usbDev.a_bRegs;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ DCapsDef *sc = &dev->usbDev.Caps;
+ SANE_Byte reg, msk, val;
+
+ if( NULL == hw ) {
+ DBG( _DBG_ERROR, "NULL-Pointer detected: usb_GetLampStatus()\n" );
+ return -1;
+ }
+
+ /* do we use the misc I/O pins for switching the lamp ? */
+ if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag ) {
+
+ usb_GetLampRegAndMask( sc->misc_io, &reg, &msk );
+
+ if( 0 == reg ) {
+ usbio_ReadReg( dev->fd, 0x29, &reg );
+ if( reg & 3 )
+ iLampStatus |= DEV_LampReflection;
+
+ } else {
+
+ /* check if the lamp is on */
+ usbio_ReadReg( dev->fd, reg, &val );
+
+ DBG( _DBG_INFO2, "LAMP-REG[0x%02x] = 0x%02x (msk=0x%02x)\n",
+ reg,val,msk);
+ if( val & msk )
+ iLampStatus |= DEV_LampReflection;
+
+ /* if the device supports a TPA, we check this here */
+ if( sc->wFlags & DEVCAPSFLAG_TPA ) {
+
+ usb_GetLampRegAndMask( _GET_TPALAMP(sc->misc_io), &reg, &msk );
+ if (reg) {
+ usbio_ReadReg( dev->fd, reg, &val );
+ DBG( _DBG_INFO2, "TPA-REG[0x%02x] = 0x%02x (msk=0x%02x)\n",
+ reg,val,msk);
+ if( val & msk )
+ iLampStatus |= DEV_LampTPA;
+ }
+ }
+
+ /* CanoScan D660U extra vaganza... */
+ if((dev->usbDev.vendor == 0x04A9) && (dev->usbDev.product==0x2208)) {
+ sanei_lm983x_read( dev->fd, 0x29, &regs[0x29], 3, SANE_TRUE );
+ DBG( _DBG_INFO, "[29]=0x%02x, [2A]=0x%02x, [2B]=0x%02x\n",
+ regs[0x29], regs[0x2a], regs[0x2b] );
+ }
+ }
+ } else {
+ sanei_lm983x_read(dev->fd, 0x29,&regs[0x29],0x37-0x29+1,SANE_TRUE);
+
+ if((regs[0x29] & 3) == 1) {
+
+/* HEINER: BETTER define register to check ! */
+
+ if(!_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ iLampStatus |= DEV_LampReflection;
+
+ } else {
+
+ if((regs[0x2e] * 256 + regs[0x2f]) > hw->wLineEnd )
+ iLampStatus |= DEV_LampReflection;
+
+ if((regs[0x36] * 256 + regs[0x37]) > hw->wLineEnd )
+ iLampStatus |= DEV_LampTPA;
+ }
+ }
+ }
+
+ DBG( _DBG_INFO, "LAMP-STATUS: 0x%08x (%s)\n",
+ iLampStatus, iLampStatus?"on":"off" );
+ return iLampStatus;
+}
+
+/** usb_switchLampX
+ * used for all devices that use some misc I/O pins to switch the lamp
+ */
+static SANE_Bool
+usb_switchLampX( Plustek_Device *dev, SANE_Bool on, SANE_Bool tpa )
+{
+ SANE_Byte reg, msk;
+ DCapsDef *sc = &dev->usbDev.Caps;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if( tpa )
+ usb_GetLampRegAndMask( _GET_TPALAMP(sc->misc_io), &reg, &msk );
+ else
+ usb_GetLampRegAndMask( sc->misc_io, &reg, &msk );
+
+ if( 0 == reg )
+ return SANE_FALSE; /* no need to switch something */
+
+ DBG( _DBG_INFO, "usb_switchLampX(ON=%u,TPA=%u)\n", on, tpa );
+
+ if( on ) {
+ /* in fact the output bit should be set by the default settings
+ * but we make sure, that it is set anyway...
+ */
+ if (msk & 0x08)
+ msk |= 0x01;
+ else
+ msk |= 0x10;
+ regs[reg] |= msk;
+ } else {
+ regs[reg] &= ~msk;
+ }
+
+ DBG( _DBG_INFO, "Switch Lamp: %u, regs[0x%02x] = 0x%02x\n",
+ on, reg, regs[reg] );
+ usbio_WriteReg( dev->fd, reg, regs[reg] );
+ return SANE_TRUE;
+}
+
+/** usb_switchLamp
+ * used for all devices that use some misc I/O pins to switch the lamp
+ */
+static SANE_Bool
+usb_switchLamp( Plustek_Device *dev, SANE_Bool on )
+{
+ SANE_Bool result;
+
+ if((dev->scanning.sParam.bSource == SOURCE_Negative) ||
+ (dev->scanning.sParam.bSource == SOURCE_Transparency)) {
+ result = usb_switchLampX( dev, on, SANE_TRUE );
+ } else {
+ result = usb_switchLampX( dev, on, SANE_FALSE );
+ }
+
+ /* to switch off CIS, we need to tweak the lampoff/on regs */
+ usb_AdjustLamps( dev, on );
+ return result;
+}
+
+/** usb_LedOn
+ */
+static void
+usb_LedOn( Plustek_Device *dev, SANE_Bool fOn )
+{
+ u_char value;
+
+ if( dev->usbDev.HwSetting.motorModel != MODEL_HuaLien )
+ return;
+
+ value = dev->usbDev.a_bRegs[0x0d];
+
+ if( fOn )
+ value |= 0x10;
+ else
+ value &= ~0x10;
+
+ dev->usbDev.a_bRegs[0x0d] = value;
+ usbio_WriteReg( dev->fd, 0x0d, value );
+}
+
+/** usb_FillLampRegs
+ * set all the registers controlling the lamps
+ */
+static void
+usb_FillLampRegs( Plustek_Device *dev )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleLow );
+ regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleLow );
+
+ regs[0x2c] = _HIBYTE( hw->red_lamp_on );
+ regs[0x2d] = _LOBYTE( hw->red_lamp_on );
+ regs[0x2e] = _HIBYTE( hw->red_lamp_off);
+ regs[0x2f] = _LOBYTE( hw->red_lamp_off);
+
+ regs[0x30] = _HIBYTE( hw->green_lamp_on );
+ regs[0x31] = _LOBYTE( hw->green_lamp_on );
+ regs[0x32] = _HIBYTE( hw->green_lamp_off);
+ regs[0x33] = _LOBYTE( hw->green_lamp_off);
+
+ regs[0x34] = _HIBYTE( hw->blue_lamp_on );
+ regs[0x35] = _LOBYTE( hw->blue_lamp_on );
+ regs[0x36] = _HIBYTE( hw->blue_lamp_off);
+ regs[0x37] = _LOBYTE( hw->blue_lamp_off);
+}
+
+/** usb_LampOn
+ */
+static SANE_Bool
+usb_LampOn( Plustek_Device *dev, SANE_Bool fOn, SANE_Bool fResetTimer )
+{
+ DCapsDef *sc = &dev->usbDev.Caps;
+ ScanDef *scanning = &dev->scanning;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+ int iLampStatus = usb_GetLampStatus( dev );
+ int lampId = -1;
+ struct timeval t;
+
+ if( NULL == scanning ) {
+ DBG( _DBG_ERROR, "NULL-Pointer detected: usb_LampOn()\n" );
+ return SANE_FALSE;
+ }
+
+ switch( scanning->sParam.bSource ) {
+
+ case SOURCE_Reflection:
+ case SOURCE_ADF:
+ lampId = DEV_LampReflection;
+ break;
+
+ case SOURCE_Transparency:
+ case SOURCE_Negative:
+ lampId = DEV_LampTPA;
+ break;
+ }
+
+ if( fOn ) {
+
+ if( iLampStatus != lampId ) {
+
+ DBG( _DBG_INFO, "Switching Lamp on\n" );
+
+/* here we might have to switch off the TPA/Main lamp before
+ * using the other one
+ */
+ if( lampId != dev->usbDev.currentLamp ) {
+ if( dev->usbDev.currentLamp == DEV_LampReflection )
+ usb_switchLampX( dev, SANE_FALSE, SANE_FALSE );
+ else
+ usb_switchLampX( dev, SANE_FALSE, SANE_TRUE );
+ }
+
+ memset( &regs[0x29], 0, (0x37-0x29+1));
+
+ regs[0x29] = hw->bReg_0x29;
+
+ if( !usb_switchLamp(dev, SANE_TRUE )) {
+
+ if( lampId == DEV_LampReflection ) {
+ regs[0x2e] = 16383 / 256;
+ regs[0x2f] = 16383 % 256;
+ }
+
+ if( lampId == DEV_LampTPA ) {
+ regs[0x36] = 16383 / 256;
+ regs[0x37] = 16383 % 256;
+ }
+ }
+
+ if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag )
+ usb_FillLampRegs( dev );
+
+ sanei_lm983x_write( dev->fd, 0x29,
+ &regs[0x29], 0x37-0x29+1, SANE_TRUE );
+ if( lampId != dev->usbDev.currentLamp ) {
+
+ dev->usbDev.currentLamp = lampId;
+
+ if( fResetTimer ) {
+
+ gettimeofday( &t, NULL );
+ dev->usbDev.dwTicksLampOn = t.tv_sec;
+ DBG( _DBG_INFO, "Warmup-Timer started\n" );
+ }
+ }
+ }
+
+ } else {
+
+ int iStatusChange = iLampStatus & ~lampId;
+
+ if( iStatusChange != iLampStatus ) {
+
+ DBG( _DBG_INFO, "Switching Lamp off\n" );
+
+ memset( &regs[0x29], 0, 0x37-0x29+1 );
+ if( !usb_switchLamp(dev, SANE_FALSE )) {
+
+ if( iStatusChange & DEV_LampReflection ) {
+ regs[0x2e] = 16383 / 256;
+ regs[0x2f] = 16383 % 256;
+ }
+
+ if( iStatusChange & DEV_LampTPA ) {
+ regs[0x36] = 16383 / 256;
+ regs[0x37] = 16383 % 256;
+ }
+ }
+
+ if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag )
+ usb_FillLampRegs( dev );
+
+ sanei_lm983x_write( dev->fd, 0x29,
+ &regs[0x29], 0x37-0x29+1, SANE_TRUE );
+ }
+ }
+ if( usb_GetLampStatus(dev))
+ usb_LedOn( dev, SANE_TRUE );
+ else
+ usb_LedOn( dev, SANE_FALSE );
+ return SANE_TRUE;
+}
+
+/** Function to preset the registers for the specific device, which
+ * should never change during the whole operation
+ * Affected registers:<br>
+ * 0x0b - 0x0e - Sensor settings - directly from the HWDef<br>
+ * 0x0f - 0x18 - Sensor Configuration - directly from the HwDef<br>
+ * 0x1a - 0x1b - Stepper Phase Correction<br>
+ * 0x20 - 0x21 - Line End<br>
+ * 0x21 - 0x22 - Data Pixel start<br>
+ * 0x23 - 0x24 - Data Pixel end<br>
+ * 0x45 - Stepper Motor Mode<br>
+ * 0x4c - 0x4d - Full Steps to Scan after PAPER SENSE 2 trips<br>
+ * 0x50 - Steps to reverse when buffer is full<br>
+ * 0x51 - Acceleration Profile<br>
+ * 0x54 - 0x5e - Motor Settings, Paper-Sense Settings and Misc I/O<br>
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @return - Nothing
+ */
+static void
+usb_ResetRegisters( Plustek_Device *dev )
+{
+ int linend;
+
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "RESETTING REGISTERS(%i) - 0x%02x\n",
+ dev->initialized, (int) sizeof(dev->usbDev.a_bRegs));
+ memset( regs, 0, sizeof(dev->usbDev.a_bRegs));
+
+ memcpy( regs+0x0b, &hw->bSensorConfiguration, 4 );
+ memcpy( regs+0x0f, &hw->bReg_0x0f_Color, 10 );
+ regs[0x1a] = _HIBYTE( hw->StepperPhaseCorrection );
+ regs[0x1b] = _LOBYTE( hw->StepperPhaseCorrection );
+
+/* HEINER: CHECK WHY THIS has been disabled*/
+#if 0
+ regs[0x1c] = hw->bOpticBlackStart;
+ regs[0x1d] = hw->bOpticBlackEnd;
+
+ regs[0x1e] = _HIBYTE( hw->wActivePixelsStart );
+ regs[0x1f] = _LOBYTE( hw->wActivePixelsStart );
+#endif
+ regs[0x20] = _HIBYTE( hw->wLineEnd );
+ regs[0x21] = _LOBYTE( hw->wLineEnd );
+
+ regs[0x22] = _HIBYTE( hw->bOpticBlackStart );
+ regs[0x23] = _LOBYTE( hw->bOpticBlackStart );
+
+ linend = hw->bOpticBlackStart + hw->wLineEnd;
+ if( linend < (hw->wLineEnd-20))
+ linend = hw->wLineEnd-20;
+
+ regs[0x24] = _HIBYTE( linend );
+ regs[0x25] = _LOBYTE( linend );
+
+ regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleHigh );
+ regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleHigh );
+
+ regs[0x45] = hw->bReg_0x45;
+ regs[0x4c] = _HIBYTE( hw->wStepsAfterPaperSensor2 );
+ regs[0x4d] = _LOBYTE( hw->wStepsAfterPaperSensor2 );
+ regs[0x50] = hw->bStepsToReverse;
+ regs[0x51] = hw->bReg_0x51;
+
+ /* if already initialized, we ignore the MISC I/O settings as
+ * they are used to determine the current lamp settings...
+ */
+ if( dev->initialized >= 0 ) {
+
+ DBG( _DBG_INFO2, "USING MISC I/O settings\n" );
+ memcpy( regs+0x54, &hw->bReg_0x54, 0x58 - 0x54 + 1 );
+ regs[0x5c] = hw->bReg_0x5c;
+ regs[0x5d] = hw->bReg_0x5d;
+ regs[0x5e] = hw->bReg_0x5e;
+ sanei_lm983x_read( dev->fd, 0x59, &regs[0x59], 3, SANE_TRUE );
+
+ } else {
+
+ DBG( _DBG_INFO2, "SETTING THE MISC I/Os\n" );
+ memcpy( regs+0x54, &hw->bReg_0x54, 0x5e - 0x54 + 1 );
+
+ if( usb_IsCISDevice( dev )) {
+
+ /* this sequence seems to be needed at least for the
+ * CanoScan devices to work properly after power-up
+ */
+ sanei_lm983x_write_byte( dev->fd, 0x5b, regs[0x5b] );
+ sanei_lm983x_write_byte( dev->fd, 0x59, regs[0x59] );
+ sanei_lm983x_write_byte( dev->fd, 0x5a, regs[0x5a] );
+ } else {
+ sanei_lm983x_write( dev->fd, 0x59, &regs[0x59], 3, SANE_TRUE );
+ }
+ }
+ DBG( _DBG_INFO, "MISC I/O after RESET: 0x%02x, 0x%02x, 0x%02x\n",
+ regs[0x59], regs[0x5a], regs[0x5b] );
+}
+
+/** function which checks if we are already in home position or not.
+ *
+ */
+static SANE_Bool
+usb_SensorStatus( Plustek_Device *dev )
+{
+ u_char value;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+/* HEINER: Maybe needed to avoid recalibration!!! */
+#if 0
+ if( dev->scanning.fCalibrated )
+ return SANE_TRUE;
+#endif
+
+ /* read the status register */
+ _UIO( usbio_ReadReg( dev->fd, 2, &value ));
+ if( value & 1 ) {
+
+ _UIO( usbio_ReadReg( dev->fd, 0x7, &value));
+ if( value ) {
+
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+ usbio_WriteReg( dev->fd, 0x07, 0x20 );
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+
+ sanei_lm983x_write( dev->fd, 0x58,
+ &hw->bReg_0x58, 0x5b-0x58+1, SANE_TRUE );
+ usbio_ReadReg( dev->fd, 2, &value );
+ usbio_ReadReg( dev->fd, 2, &value );
+ }
+
+ usb_MotorOn( dev, SANE_FALSE );
+ return SANE_TRUE;
+ }
+
+ _UIO( usbio_ReadReg( dev->fd, 0x7, &value ));
+
+ if( !(value & 2)) {
+ usb_ModuleToHome( dev, SANE_FALSE );
+ }
+
+ return SANE_FALSE;
+}
+
+/**
+ */
+static void
+usb_LampSwitch( Plustek_Device *dev, SANE_Bool sw )
+{
+ int handle = -1;
+
+ if( -1 == dev->fd ) {
+
+ if( SANE_STATUS_GOOD == sanei_usb_open(dev->sane.name, &handle)) {
+ dev->fd = handle;
+ }
+ }
+
+ /* needs to be recalibrated */
+ dev->scanning.fCalibrated = SANE_FALSE;
+
+ if( -1 != dev->fd )
+ usb_LampOn( dev, sw, SANE_FALSE );
+
+ if( -1 != handle ) {
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ }
+}
+
+/* HEINER: replace!!! */
+static Plustek_Device *dev_xxx = NULL;
+
+/** ISR to switch lamp off after time has elapsed
+ */
+static void
+usb_LampTimerIrq( int sig )
+{
+ if( NULL == dev_xxx )
+ return;
+
+ _VAR_NOT_USED( sig );
+ DBG( _DBG_INFO, "LAMP OFF!!!\n" );
+
+ usb_LampSwitch( dev_xxx, SANE_FALSE );
+}
+
+/** usb_StartLampTimer
+ */
+static void
+usb_StartLampTimer( Plustek_Device *dev )
+{
+ sigset_t block, pause_mask;
+ struct sigaction s;
+#ifdef HAVE_SETITIMER
+ struct itimerval interval;
+#endif
+ /* block SIGALRM */
+ sigemptyset( &block );
+ sigaddset ( &block, SIGALRM );
+ sigprocmask( SIG_BLOCK, &block, &pause_mask );
+
+ /* setup handler */
+ sigemptyset( &s.sa_mask );
+ sigaddset ( &s.sa_mask, SIGALRM );
+ s.sa_flags = 0;
+ s.sa_handler = usb_LampTimerIrq;
+
+ if( sigaction( SIGALRM, &s, NULL ) < 0 )
+ DBG( _DBG_ERROR, "Can't setup timer-irq handler\n" );
+
+ sigprocmask( SIG_UNBLOCK, &block, &pause_mask );
+
+#ifdef HAVE_SETITIMER
+ /* define a one-shot timer */
+ interval.it_value.tv_usec = 0;
+ interval.it_value.tv_sec = dev->usbDev.dwLampOnPeriod;
+ interval.it_interval.tv_usec = 0;
+ interval.it_interval.tv_sec = 0;
+
+ if( 0 != dev->usbDev.dwLampOnPeriod ) {
+ dev_xxx = dev;
+ setitimer( ITIMER_REAL, &interval, &dev->saveSettings );
+ DBG( _DBG_INFO, "Lamp-Timer started (using ITIMER)\n" );
+ }
+#else
+ if( 0 != dev->usbDev.dwLampOnPeriod ) {
+ dev_xxx = dev;
+ alarm( dev->usbDev.dwLampOnPeriod );
+ DBG( _DBG_INFO, "Lamp-Timer started (using ALARM)\n" );
+ }
+#endif
+}
+
+/** usb_StopLampTimer
+ */
+static void
+usb_StopLampTimer( Plustek_Device *dev )
+{
+ sigset_t block, pause_mask;
+
+ /* block SIGALRM */
+ sigemptyset( &block );
+ sigaddset ( &block, SIGALRM );
+ sigprocmask( SIG_BLOCK, &block, &pause_mask );
+
+ dev_xxx = NULL;
+
+#ifdef HAVE_SETITIMER
+ if( 0 != dev->usbDev.dwLampOnPeriod )
+ setitimer( ITIMER_REAL, &dev->saveSettings, NULL );
+#else
+ _VAR_NOT_USED( dev );
+ alarm( 0 );
+#endif
+ DBG( _DBG_INFO, "Lamp-Timer stopped\n" );
+}
+
+/** wait until warmup has been done
+ */
+static SANE_Bool
+usb_Wait4Warmup( Plustek_Device *dev )
+{
+ u_long dw;
+ struct timeval t;
+
+ if( usb_IsCISDevice(dev)) {
+ DBG(_DBG_INFO,"Warmup: skipped for CIS devices\n" );
+ return SANE_TRUE;
+ }
+
+ if( dev->adj.warmup < 0 )
+ return SANE_TRUE;
+
+ /*
+ * wait until warmup period has been elapsed
+ */
+ gettimeofday( &t, NULL);
+ dw = t.tv_sec - dev->usbDev.dwTicksLampOn;
+ if( dw < (u_long)dev->adj.warmup )
+ DBG(_DBG_INFO,"Warmup: Waiting %d seconds\n", dev->adj.warmup );
+
+ do {
+
+ gettimeofday( &t, NULL);
+
+ dw = t.tv_sec - dev->usbDev.dwTicksLampOn;
+
+ if( usb_IsEscPressed()) {
+ return SANE_FALSE;
+ }
+
+ } while( dw < (u_long)dev->adj.warmup );
+
+ return SANE_TRUE;
+}
+
+/** function for TPA autodection (EPSON & UMAX devices)
+ */
+static SANE_Bool
+usb_HasTPA( Plustek_Device *dev )
+{
+ static char model[] = { "3450" };
+ u_char val;
+
+ if( dev->usbDev.vendor == 0x04B8 ) { /* the EPSON section */
+
+ usb_switchLampX( dev, SANE_FALSE, SANE_TRUE );
+ usbio_WriteReg ( dev->fd, 0x58, 0x1d );
+ usbio_WriteReg ( dev->fd, 0x59, 0x49 );
+ usbio_ReadReg ( dev->fd, 0x02, &val );
+ usbio_WriteReg ( dev->fd, 0x58, dev->usbDev.HwSetting.bReg_0x58 );
+ usbio_WriteReg ( dev->fd, 0x59, dev->usbDev.HwSetting.bReg_0x59 );
+
+ DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val );
+
+ if( val & 0x02 ) {
+ DBG( _DBG_INFO, "EPSON-TPA detected\n" );
+ return SANE_TRUE;
+ } else
+ DBG( _DBG_INFO, "EPSON-TPA NOT detected\n" );
+
+ if( dev->adj.enableTpa ) {
+ DBG( _DBG_INFO, "EPSON-TPA usage forced\n" );
+ return SANE_TRUE;
+ }
+
+ } else if( dev->usbDev.vendor == 0x1606 ) { /* the UMAX section */
+
+ if((dev->usbDev.product == 0x0050) || (dev->usbDev.product == 0x0060)) {
+
+ usbio_ReadReg ( dev->fd, 0x02, &val );
+ DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val );
+
+ usbio_WriteReg ( dev->fd, 0x58, dev->usbDev.HwSetting.bReg_0x58 );
+ usbio_WriteReg ( dev->fd, 0x5a, dev->usbDev.HwSetting.bReg_0x5a );
+ usbio_WriteReg ( dev->fd, 0x5b, dev->usbDev.HwSetting.bReg_0x5b );
+
+ usbio_ReadReg ( dev->fd, 0x02, &val );
+ DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val );
+
+ if( val & 0x02 ) {
+ DBG( _DBG_INFO, "UMAX-TPA detected\n" );
+ dev->usbDev.ModelStr = model;
+ return SANE_TRUE;
+ } else
+ DBG( _DBG_INFO, "UMAX-TPA NOT detected\n" );
+
+ if( dev->adj.enableTpa ) {
+ DBG( _DBG_INFO, "UMAX-TPA usage forced\n" );
+ dev->usbDev.ModelStr = model;
+ return SANE_TRUE;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+/** function for reading the button states
+ */
+static SANE_Bool
+usb_UpdateButtonStatus( Plustek_Scanner *s )
+{
+ u_char mio[3];
+ SANE_Byte val, mask;
+ int i, j, bc;
+ int handle = -1;
+ SANE_Status status;
+ Plustek_Device *dev = s->hw;
+ DCapsDef *caps = &dev->usbDev.Caps;
+
+ if (caps->bButtons == 0)
+ return SANE_FALSE;
+
+ status = sanei_access_lock( dev->sane.name, 3 );
+ if( SANE_STATUS_GOOD != status )
+ return SANE_FALSE;
+
+ if( -1 == dev->fd ) {
+
+ status = sanei_usb_open(dev->sane.name, &handle);
+ if( SANE_STATUS_GOOD != status ) {
+ sanei_access_unlock( dev->sane.name );
+ return SANE_FALSE;
+ }
+ dev->fd = handle;
+ }
+
+ /* we have to check all 6 misc I/O ports for input configuration*/
+ mio[0] = dev->usbDev.HwSetting.bReg_0x59;
+ mio[1] = dev->usbDev.HwSetting.bReg_0x5a;
+ mio[2] = dev->usbDev.HwSetting.bReg_0x5b;
+
+ usbio_ReadReg( dev->fd, 0x07, &val );
+ if( val == 0 ) {
+
+ /* first read clears the status... */
+ usbio_ReadReg( dev->fd, 0x02, &val );
+
+ /* Plustek and KYE/Genius use altnernative button handling */
+ if((dev->usbDev.vendor == 0x07B3) || (dev->usbDev.vendor == 0x0458)) {
+
+ DBG( _DBG_INFO2, "Button Value=0x%02x\n", val );
+
+ /* no button pressed so far */
+ for( i = 0; i < caps->bButtons; i++ )
+ s->val[OPT_BUTTON_0 + i].w = 0;
+
+ if (caps->bButtons == 2 || caps->bButtons == 5) {
+ val >>= 2;
+ val &= 0x07;
+ DBG( _DBG_INFO2, "Button Value=0x%02x (2/5)\n", val );
+
+ switch( val ) {
+ case 1: s->val[OPT_BUTTON_1].w = 1; break;
+ case 2: s->val[OPT_BUTTON_0].w = 1; break;
+ case 3: s->val[OPT_BUTTON_2].w = 1; break;
+ case 4: s->val[OPT_BUTTON_3].w = 1; break;
+ case 6: s->val[OPT_BUTTON_4].w = 1; break;
+ }
+ } else if (caps->bButtons == 4 ) {
+ val >>= 5;
+ val &= 0x07;
+ DBG( _DBG_INFO2, "Button Value=0x%02x (4)\n", val );
+
+ switch( val ) {
+ case 1: s->val[OPT_BUTTON_0].w = 1; break;
+ case 2: s->val[OPT_BUTTON_1].w = 1; break;
+ case 4: s->val[OPT_BUTTON_2].w = 1; break;
+ case 6: s->val[OPT_BUTTON_3].w = 1; break;
+ }
+ } else {
+ DBG( _DBG_INFO2, "Hmm, could not handle this!\n" );
+ }
+
+ } else {
+
+ /* the generic section... */
+ val >>= 2;
+ bc = 0;
+
+ /* only use the "valid" ports, that reflect a button */
+ if( _WAF_MISC_IO_BUTTONS & caps->workaroundFlag ) {
+ if((caps->misc_io & _PORT0) == 0)
+ mio[0] = 0xff;
+ if((caps->misc_io & _PORT1) == 0)
+ mio[1] = 0xff;
+ if((caps->misc_io & _PORT2) == 0)
+ mio[2] = 0xff;
+ }
+
+ for( i = 0; i < 3; i++ ) {
+
+ DBG( _DBG_INFO2, "Checking MISC IO[%u]=0x%02x\n", i, mio[i] );
+ mask = 0x01;
+
+ for( j = 0; j < 2; j++ ) {
+
+ if((mio[i] & mask) == 0) {
+ DBG( _DBG_INFO2, "* port %u configured as input,"
+ " status: %s (%u)\n", (i*2)+j+1,
+ ((val & 1)?"PRESSED":"RELEASED"), (OPT_BUTTON_0 + bc));
+ s->val[OPT_BUTTON_0 + bc].w = val & 1;
+ bc++;
+ }
+ val >>= 1;
+ mask <<= 4;
+ }
+ }
+ }
+ } else {
+ DBG( _DBG_INFO2, "Scanner NOT idle: 0x%02x\n", val );
+ }
+
+ if( -1 != handle ) {
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ }
+
+ sanei_access_unlock( dev->sane.name );
+ return SANE_TRUE;
+}
+
+/* END PLUSTEK-USBHW.C ......................................................*/
diff --git a/backend/plustek-usbimg.c b/backend/plustek-usbimg.c
new file mode 100644
index 0000000..41c0207
--- /dev/null
+++ b/backend/plustek-usbimg.c
@@ -0,0 +1,1984 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbimg.c
+ * @brief Image processing functions for copying and scaling image lines.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - fixed the 14bit problem for LM9831 devices
+ * - 0.42 - no changes
+ * - 0.43 - no changes
+ * - 0.44 - added CIS parts and dumpPic function
+ * - 0.45 - added gray scaling functions for CIS devices
+ * - fixed usb_GrayScale16 function
+ * - fixed a bug in usb_ColorScale16_2 function
+ * - fixed endless loop bug
+ * - fixed a bug in usb_GrayScalePseudo16 function
+ * - fixed a bug in usb_GrayDuplicatePseudo16 function
+ * - removed the scaler stuff for CIS devices
+ * - 0.46 - minor fixes
+ * - 0.47 - added big-endian/little endian stuff
+ * - 0.48 - fixed usb_ColorDuplicateGray16() and
+ * usb_ColorScaleGray16()
+ * - added usb_BWScaleFromColor() and usb_BWDuplicateFromColor()
+ * - cleanup
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - 0.50 - cleanup
+ * - 0.51 - added usb_ColorDuplicateGray16_2(), usb_ColorScaleGray16_2()
+ * usb_BWScaleFromColor_2() and usb_BWDuplicateFromColor_2()
+ * - 0.52 - cleanup
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#define _SCALER 1000
+
+static u_char bShift, Shift;
+static u_short wSum, Mask;
+
+/*
+ */
+static u_char BitTable[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static u_char BitsReverseTable[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+/************************ some helper functions ******************************/
+
+static void ReverseBits( int b, u_char **pTar, int *iByte, int *iWeightSum,
+ int iSrcWeight, int iTarWeight, int cMax )
+{
+ int bit;
+
+ cMax = (1 << cMax);
+ if(iSrcWeight == iTarWeight) {
+ for( bit = 1; bit < cMax; bit <<= 1) {
+ *iByte <<= 1;
+ if(b & bit)
+ *iByte |= 1;
+ if(*iByte >= 0x100) {
+ **pTar++ = (u_char)*iByte;
+ *iByte = 1;
+ }
+ }
+ } else {
+ for( bit = 1; bit < cMax; bit <<= 1 ) {
+
+ *iWeightSum += iTarWeight;
+ while(*iWeightSum >= iSrcWeight)
+ {
+ *iWeightSum -= iSrcWeight;
+ *iByte <<= 1;
+ if(b & bit)
+ *iByte |= 1;
+ if(*iByte >= 0x100)
+ {
+ **pTar++ = (u_char)*iByte;
+ *iByte = 1;
+ }
+ }
+ }
+ }
+}
+
+static void usb_ReverseBitStream( u_char *pSrc, u_char *pTar, int iPixels,
+ int iBufSize, int iSrcWeight/* = 0*/,
+ int iTarWeight/* = 0*/, int iPadBit/* = 1*/)
+{
+ int i;
+ int iByte = 1;
+ int cBytes = iPixels / 8;
+ int cBits = iPixels % 8;
+ u_char bPad = iPadBit? 0xff: 0;
+ u_char *pTarget = pTar;
+ int iWeightSum = 0;
+
+ if(iSrcWeight == iTarWeight) {
+ if(cBits)
+ {
+ int cShift = 8 - cBits;
+ for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--, pTar++)
+ *pTar = BitsReverseTable[(u_char)((*pSrc << cBits) | (*(pSrc+1) >> cShift))];
+ ReverseBits(*(pSrc+1) >> cShift, &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, cBits);
+ }
+ else /* byte boundary */
+ {
+ for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--, pTar++)
+ *pTar = BitsReverseTable[*pSrc];
+ }
+ }
+ else /* To shrink or enlarge */
+ {
+ if(cBits) {
+ int cShift = 8 - cBits;
+ for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--)
+ ReverseBits((*pSrc << cBits) | (*(pSrc+1) >> cShift), &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, 8);
+ ReverseBits(*(pSrc+1) >> cShift, &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, cBits);
+ }
+ else /* byte boundary */
+ {
+ for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--)
+ ReverseBits(*pSrc, &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, 8);
+ }
+ }
+
+ if(iByte != 1) {
+ while(iByte < 0x100)
+ {
+ iByte <<= 1;
+ iByte |= iPadBit;
+ }
+ *pTar++ = (u_char)iByte;
+ }
+
+ cBytes = (int)(pTar - pTarget);
+
+ for(i = iBufSize - cBytes; i > 0; i--, pTar++)
+ *pTar = bPad;
+}
+
+/**
+ */
+static void usb_AverageColorByte( Plustek_Device *dev )
+{
+ u_long dw;
+ ScanDef *scan = &dev->scanning;
+
+ if((scan->sParam.bSource == SOURCE_Negative ||
+ scan->sParam.bSource == SOURCE_Transparency) &&
+ scan->sParam.PhyDpi.x > 800) {
+
+ for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
+ {
+ scan->Red.pcb[dw].a_bColor[0] =
+ (u_char)(((u_short)scan->Red.pcb[dw].a_bColor[0] +
+ (u_short)scan->Red.pcb[dw + 1].a_bColor[0]) / 2);
+
+ scan->Green.pcb[dw].a_bColor[0] =
+ (u_char)(((u_short)scan->Green.pcb[dw].a_bColor[0] +
+ (u_short)scan->Green.pcb[dw + 1].a_bColor[0]) / 2);
+
+ scan->Blue.pcb[dw].a_bColor[0] =
+ (u_char)(((u_short)scan->Blue.pcb[dw].a_bColor[0] +
+ (u_short)scan->Blue.pcb[dw + 1].a_bColor[0]) / 2);
+ }
+ }
+}
+
+/**
+ */
+static void usb_AverageColorWord( Plustek_Device *dev )
+{
+ u_char ls = 2;
+ u_long dw;
+ ScanDef *scan = &dev->scanning;
+
+ if((scan->sParam.bSource == SOURCE_Negative ||
+ scan->sParam.bSource == SOURCE_Transparency) &&
+ scan->sParam.PhyDpi.x > 800) {
+
+ scan->Red.pcw[0].Colors[0] = _HILO2WORD(scan->Red.pcw[0].HiLo[0]) >> ls;
+ scan->Green.pcw[0].Colors[0] = _HILO2WORD(scan->Green.pcw[0].HiLo[0]) >> ls;
+ scan->Blue.pcw[0].Colors[0] = _HILO2WORD(scan->Blue.pcw[0].HiLo[0]) >> ls;
+
+ for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
+ {
+ scan->Red.pcw[dw + 1].Colors[0] = _HILO2WORD(scan->Red.pcw[dw + 1].HiLo[0]) >> ls;
+ scan->Green.pcw[dw + 1].Colors[0] = _HILO2WORD(scan->Green.pcw[dw + 1].HiLo[0]) >> ls;
+ scan->Blue.pcw[dw + 1].Colors[0] = _HILO2WORD(scan->Blue.pcw[dw + 1].HiLo[0]) >> ls;
+
+ scan->Red.pcw[dw].Colors[0] = (u_short)(((u_long)scan->Red.pcw[dw].Colors[0] +
+ (u_long)scan->Red.pcw[dw + 1].Colors[0]) / 2);
+ scan->Green.pcw[dw].Colors[0] = (u_short)(((u_long)scan->Green.pcw[dw].Colors[0] +
+ (u_long)scan->Green.pcw[dw + 1].Colors[0]) / 2);
+ scan->Blue.pcw[dw].Colors[0] = (u_short)(((u_long)scan->Blue.pcw[dw].Colors[0] +
+ (u_long)scan->Blue.pcw[dw + 1].Colors[0]) / 2);
+
+ scan->Red.pcw[dw].Colors[0] = _HILO2WORD(scan->Red.pcw[dw].HiLo[0]) << ls;
+ scan->Green.pcw[dw].Colors[0] = _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) << ls;
+ scan->Blue.pcw[dw].Colors[0] = _HILO2WORD(scan->Blue.pcw[dw].HiLo[0]) << ls;
+ }
+
+ scan->Red.pcw[dw].Colors[0] = _HILO2WORD(scan->Red.pcw[dw].HiLo[0]) << ls;
+ scan->Green.pcw[dw].Colors[0] = _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) << ls;
+ scan->Blue.pcw[dw].Colors[0] = _HILO2WORD(scan->Blue.pcw[dw].HiLo[0]) << ls;
+ }
+}
+
+/**
+ */
+static void usb_AverageGrayByte( Plustek_Device *dev )
+{
+ u_long dw;
+ ScanDef *scan = &dev->scanning;
+
+ if((scan->sParam.bSource == SOURCE_Negative ||
+ scan->sParam.bSource == SOURCE_Transparency) &&
+ scan->sParam.PhyDpi.x > 800)
+ {
+ for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
+ scan->Green.pb[dw] = (u_char)(((u_short)scan->Green.pb[dw]+
+ (u_short)scan->Green.pb[dw+1]) / 2);
+ }
+}
+
+/**
+ */
+static void usb_AverageGrayWord( Plustek_Device *dev )
+{
+ u_long dw;
+ ScanDef *scan = &dev->scanning;
+
+ if((scan->sParam.bSource == SOURCE_Negative ||
+ scan->sParam.bSource == SOURCE_Transparency) &&
+ scan->sParam.PhyDpi.x > 800)
+ {
+ scan->Green.pw[0] = _HILO2WORD(scan->Green.philo[0]) >> 2;
+
+ for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
+ {
+ scan->Green.pw[dw + 1] = _HILO2WORD(scan->Green.philo[dw+1]) >> 2;
+ scan->Green.pw[dw] = (u_short)(((u_long)scan->Green.pw[dw]+
+ (u_long)scan->Green.pw[dw+1]) / 2);
+ scan->Green.pw[dw] = _HILO2WORD(scan->Green.philo[dw]) << 2;
+ }
+
+ scan->Green.pw[dw] = _HILO2WORD(scan->Green.philo[dw]) << 2;
+ }
+}
+
+/**
+ * returns the zoom value, used for our scaling algorithm (DDA algo
+ * digital differential analyzer).
+ */
+static int usb_GetScaler( ScanDef *scan )
+{
+ double ratio;
+
+ ratio = (double)scan->sParam.UserDpi.x/
+ (double)scan->sParam.PhyDpi.x;
+
+ return (int)(1.0/ratio * _SCALER);
+}
+
+/******************************* the copy functions **************************/
+
+/** do a simple memcopy from scan-buffer to user buffer
+ */
+static void usb_ColorDuplicate8( Plustek_Device *dev )
+{
+ int next;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ for( dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next ) {
+
+ scan->UserBuf.pb_rgb[pixels].Red = scan->Red.pcb[dw].a_bColor[0];
+ scan->UserBuf.pb_rgb[pixels].Green = scan->Green.pcb[dw].a_bColor[0];
+ scan->UserBuf.pb_rgb[pixels].Blue = scan->Blue.pcb[dw].a_bColor[0];
+ }
+}
+
+/** reorder from rgb line to rgb pixel (CIS scanner)
+ */
+static void usb_ColorDuplicate8_2( Plustek_Device *dev )
+{
+ int next;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ for( dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next ) {
+
+ scan->UserBuf.pb_rgb[pixels].Red = (u_char)scan->Red.pb[dw];
+ scan->UserBuf.pb_rgb[pixels].Green = (u_char)scan->Green.pb[dw];
+ scan->UserBuf.pb_rgb[pixels].Blue = (u_char)scan->Blue.pb[dw];
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicate16( Plustek_Device *dev )
+{
+ int next;
+ u_char ls;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+ SANE_Bool swap = usb_HostSwap();
+
+ usb_AverageColorWord( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+
+ if( swap ) {
+ scan->UserBuf.pw_rgb[pixels].Red =
+ _HILO2WORD(scan->Red.pcw[dw].HiLo[0]) >> ls;
+ scan->UserBuf.pw_rgb[pixels].Green =
+ _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) >> ls;
+ scan->UserBuf.pw_rgb[pixels].Blue =
+ _HILO2WORD(scan->Blue.pcw[dw].HiLo[0]) >> ls;
+ } else {
+ scan->UserBuf.pw_rgb[pixels].Red = scan->Red.pw[dw] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Green= scan->Green.pw[dw] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Blue = scan->Blue.pw[dw] >> ls;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicate16_2( Plustek_Device *dev )
+{
+ int next;
+ u_char ls;
+ HiLoDef tmp;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+ SANE_Bool swap = usb_HostSwap();
+
+ usb_AverageColorWord( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ for( dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+
+ if( swap ) {
+ tmp = *((HiLoDef*)&scan->Red.pw[dw]);
+ scan->UserBuf.pw_rgb[pixels].Red = _HILO2WORD(tmp) >> ls;
+
+ tmp = *((HiLoDef*)&scan->Green.pw[dw]);
+ scan->UserBuf.pw_rgb[pixels].Green = _HILO2WORD(tmp) >> ls;
+
+ tmp = *((HiLoDef*)&scan->Blue.pw[dw]);
+ scan->UserBuf.pw_rgb[pixels].Blue = _HILO2WORD(tmp) >> ls;
+
+ } else {
+
+ scan->UserBuf.pw_rgb[pixels].Red = scan->Red.pw[dw] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Green = scan->Green.pw[dw] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Blue = scan->Blue.pw[dw] >> ls;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicatePseudo16( Plustek_Device *dev )
+{
+ int next;
+ u_short wR, wG, wB;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ wR = (u_short)scan->Red.pcb[0].a_bColor[0];
+ wG = (u_short)scan->Green.pcb[0].a_bColor[0];
+ wB = (u_short)scan->Blue.pcb[0].a_bColor[0];
+
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+
+ scan->UserBuf.pw_rgb[pixels].Red =
+ (wR + scan->Red.pcb[dw].a_bColor[0]) << bShift;
+ scan->UserBuf.pw_rgb[pixels].Green =
+ (wG + scan->Green.pcb[dw].a_bColor[0]) << bShift;
+ scan->UserBuf.pw_rgb[pixels].Blue =
+ (wB + scan->Blue.pcb[dw].a_bColor[0]) << bShift;
+
+ wR = (u_short)scan->Red.pcb[dw].a_bColor[0];
+ wG = (u_short)scan->Green.pcb[dw].a_bColor[0];
+ wB = (u_short)scan->Blue.pcb[dw].a_bColor[0];
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicateGray( Plustek_Device *dev )
+{
+ int next;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ switch(scan->fGrayFromColor) {
+
+ case 1:
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pb[pixels] = scan->Red.pcb[dw].a_bColor[0];
+ break;
+ case 2:
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pb[pixels] = scan->Green.pcb[dw].a_bColor[0];
+ break;
+ case 3:
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pb[pixels] = scan->Blue.pcb[dw].a_bColor[0];
+ break;
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicateGray_2( Plustek_Device *dev )
+{
+ int next;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ switch(scan->fGrayFromColor)
+ {
+ case 1:
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pb[pixels] = scan->Red.pb[dw];
+ break;
+ case 3:
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pb[pixels] = scan->Blue.pb[dw];
+ break;
+ default:
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pb[pixels] = scan->Green.pb[dw];
+ break;
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicateGray16( Plustek_Device *dev )
+{
+ int next;
+ u_char ls;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+ SANE_Bool swap = usb_HostSwap();
+
+ usb_AverageColorWord( dev );
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ switch(scan->fGrayFromColor) {
+
+ case 1:
+ if( swap ) {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pw[pixels] =
+ _HILO2WORD(scan->Red.pcw[dw].HiLo[0]) >> ls;
+ } else {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pw[pixels] = scan->Red.pw[dw] >> ls;
+ }
+ break;
+ case 2:
+ if( swap ) {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pw[pixels] =
+ _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) >> ls;
+ } else {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pw[pixels] = scan->Green.pw[dw] >> ls;
+ }
+ break;
+ case 3:
+ if( swap ) {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pw[pixels] =
+ _HILO2WORD(scan->Blue.pcw[dw].HiLo[0]) >> ls;
+ } else {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
+ scan->UserBuf.pw[pixels] = scan->Blue.pw[dw] >> ls;
+ }
+ break;
+ }
+}
+
+/**
+ */
+static void usb_ColorDuplicateGray16_2( Plustek_Device *dev )
+{
+ int next;
+ u_char ls;
+ u_long dw, pixels;
+ HiLoDef tmp;
+ ScanDef *scan = &dev->scanning;
+ SANE_Bool swap = usb_HostSwap();
+
+ usb_AverageColorWord( dev );
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ switch(scan->fGrayFromColor) {
+ case 1:
+ if( swap ) {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+ tmp = *((HiLoDef*)&scan->Red.pw[dw]);
+ scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
+ }
+ } else {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+ scan->UserBuf.pw[pixels] = scan->Red.pw[dw] >> ls;
+ }
+ }
+ break;
+ case 2:
+ if( swap ) {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+ tmp = *((HiLoDef*)&scan->Green.pw[dw]);
+ scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
+ }
+ } else {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+ scan->UserBuf.pw[pixels] = scan->Green.pw[dw] >> ls;
+ }
+ }
+ break;
+ case 3:
+ if( swap ) {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+ tmp = *((HiLoDef*)&scan->Blue.pw[dw]);
+ scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
+ }
+ } else {
+ for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
+ scan->UserBuf.pw[pixels] = scan->Blue.pw[dw] >> ls;
+ }
+ }
+ break;
+ }
+}
+
+/**
+ */
+static void usb_GrayDuplicate8( Plustek_Device *dev )
+{
+ u_char *dest, *src;
+ u_long pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageGrayByte( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+
+ pixels = scan->sParam.Size.dwPixels;
+ src = scan->Green.pb;
+ dest = scan->UserBuf.pb + pixels - 1;
+
+ for(; pixels; pixels--, src++, dest--)
+ *dest = *src;
+ } else {
+ memcpy( scan->UserBuf.pb, scan->Green.pb, scan->sParam.Size.dwBytes );
+ }
+}
+
+/**
+ */
+static void usb_GrayDuplicate16( Plustek_Device *dev )
+{
+ int next;
+ u_char ls;
+ u_short *dest;
+ u_long pixels;
+ HiLoDef *pwm;
+ ScanDef *scan = &dev->scanning;
+ SANE_Bool swap = usb_HostSwap();
+
+ usb_AverageGrayWord( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ dest = scan->UserBuf.pw;
+ }
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ pwm = scan->Green.philo;
+ for( pixels=scan->sParam.Size.dwPixels; pixels--; pwm++, dest += next ) {
+ if( swap )
+ *dest = (_PHILO2WORD(pwm)) >> ls;
+ else
+ *dest = (_PLOHI2WORD(pwm)) >> ls;
+ }
+}
+
+/**
+ */
+static void usb_GrayDuplicatePseudo16( Plustek_Device *dev )
+{
+ u_char *src;
+ int next;
+ u_short g;
+ u_short *dest;
+ u_long pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageGrayByte( dev );
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ next = -1;
+ dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ dest = scan->UserBuf.pw;
+ }
+
+ src = scan->Green.pb;
+ g = (u_short)*src;
+
+ for( pixels=scan->sParam.Size.dwPixels; pixels--; src++, dest += next ) {
+
+ *dest = (g + *src) << bShift;
+ g = (u_short)*src;
+ }
+}
+
+/** copy binary data to the user buffer
+ */
+static void usb_BWDuplicate( Plustek_Device *dev )
+{
+ ScanDef *scan = &dev->scanning;
+
+ if(scan->sParam.bSource == SOURCE_ADF)
+ {
+ usb_ReverseBitStream( scan->Green.pb, scan->UserBuf.pb,
+ scan->sParam.Size.dwValidPixels,
+ scan->dwBytesLine, 0, 0, 1 );
+ } else {
+ memcpy( scan->UserBuf.pb, scan->Green.pb, scan->sParam.Size.dwBytes );
+ }
+}
+
+/** generate binary data from one of the three color inputs according to the
+ * value in fGrayFromColor (CCD version)
+ */
+static void usb_BWDuplicateFromColor( Plustek_Device *dev )
+{
+ int next;
+ u_char d, s, *dest;
+ u_short j;
+ u_long pixels;
+ ColorByteDef *src;
+ ScanDef *scan = &dev->scanning;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
+ next = -1;
+ } else {
+ dest = scan->UserBuf.pb;
+ next = 1;
+ }
+
+ switch(scan->fGrayFromColor) {
+ case 1: src = scan->Red.pcb; break;
+ case 3: src = scan->Blue.pcb; break;
+ default: src = scan->Green.pcb; break;
+ }
+
+ d = j = 0;
+ for( pixels = scan->sParam.Size.dwPixels; pixels; pixels--, src++ ) {
+
+ s = src->a_bColor[0];
+ if( s != 0 )
+ d |= BitTable[j];
+ j++;
+ if( j == 8 ) {
+ *dest = d;
+ dest += next;
+
+ d = j = 0;
+ }
+ }
+}
+
+/** generate binary data from one of the three color inputs according to the
+ * value in fGrayFromColor (CIS version)
+ */
+static void usb_BWDuplicateFromColor_2( Plustek_Device *dev )
+{
+ int next;
+ u_char d, *dest, *src;
+ u_short j;
+ u_long pixels;
+ ScanDef *scan = &dev->scanning;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
+ next = -1;
+ } else {
+ dest = scan->UserBuf.pb;
+ next = 1;
+ }
+
+ switch(scan->fGrayFromColor) {
+ case 1: src = scan->Red.pb; break;
+ case 3: src = scan->Blue.pb; break;
+ default: src = scan->Green.pb; break;
+ }
+
+ d = j = 0;
+ for( pixels = scan->sParam.Size.dwPixels; pixels; pixels--, src++ ) {
+
+ if( *src != 0 )
+ d |= BitTable[j];
+ j++;
+ if( j == 8 ) {
+ *dest = d;
+ dest += next;
+
+ d = j = 0;
+ }
+ }
+}
+
+/************************** the scaling functions ****************************/
+
+/**
+ */
+static void usb_ColorScaleGray( Plustek_Device *dev )
+{
+ int izoom, ddax, next;
+ u_long dw, pixels;
+ ColorByteDef *src;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ switch(scan->fGrayFromColor) {
+ case 1: src = scan->Red.pcb; break;
+ case 3: src = scan->Blue.pcb; break;
+ default: src = scan->Green.pcb; break;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ for( ddax = 0; dw; src++ ) {
+
+ ddax -= _SCALER;
+ while((ddax < 0) && (dw > 0)) {
+
+ scan->UserBuf.pb[pixels] = src->a_bColor[0];
+
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorScaleGray_2( Plustek_Device *dev )
+{
+ u_char *src;
+ int izoom, ddax, next;
+ u_long dw, pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ switch(scan->fGrayFromColor) {
+ case 1: src = scan->Red.pb; break;
+ case 3: src = scan->Blue.pb; break;
+ default: src = scan->Green.pb; break;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ for( ddax = 0; dw; src++ ) {
+
+ ddax -= _SCALER;
+ while((ddax < 0) && (dw > 0)) {
+
+ scan->UserBuf.pb[pixels] = *src;
+
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorScaleGray16( Plustek_Device *dev )
+{
+ u_char ls;
+ int izoom, ddax, next;
+ u_long dw, pixels, bitsput;
+ SANE_Bool swap = usb_HostSwap();
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ switch( scan->fGrayFromColor ) {
+
+ case 1:
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+ if( swap ) {
+ scan->UserBuf.pw[pixels] =
+ _HILO2WORD(scan->Red.pcw[bitsput].HiLo[0]) >> ls;
+ } else {
+ scan->UserBuf.pw[pixels] = scan->Red.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+ break;
+
+ case 2:
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+ if( swap ) {
+ scan->UserBuf.pw[pixels] =
+ _HILO2WORD(scan->Green.pcw[bitsput].HiLo[0]) >> ls;
+ } else {
+ scan->UserBuf.pw[pixels] = scan->Green.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+ break;
+
+ case 3:
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+ if( swap ) {
+ scan->UserBuf.pw[pixels] =
+ _HILO2WORD(scan->Blue.pcw[bitsput].HiLo[0]) >> ls;
+ } else {
+ scan->UserBuf.pw[pixels] = scan->Blue.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+ break;
+ }
+}
+
+/**
+ */
+static void usb_ColorScaleGray16_2( Plustek_Device *dev )
+{
+ u_char ls;
+ int izoom, ddax, next;
+ u_long dw, pixels, bitsput;
+ HiLoDef tmp;
+ SANE_Bool swap = usb_HostSwap();
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ switch( scan->fGrayFromColor ) {
+
+ case 1:
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+ if( swap ) {
+ tmp = *((HiLoDef*)&scan->Red.pw[bitsput]);
+ scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
+ } else {
+ scan->UserBuf.pw[pixels] = scan->Red.pw[dw] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+ break;
+
+ case 2:
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+ if( swap ) {
+ tmp = *((HiLoDef*)&scan->Green.pw[bitsput]);
+ scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
+ } else {
+ scan->UserBuf.pw[pixels] = scan->Green.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+ break;
+
+ case 3:
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+ if( swap ) {
+ tmp = *((HiLoDef*)&scan->Blue.pw[bitsput]);
+ scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
+ } else {
+ scan->UserBuf.pw[pixels] = scan->Blue.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+ break;
+ }
+}
+
+/** here we copy and scale from scanner world to user world...
+ */
+static void usb_ColorScale8( Plustek_Device *dev )
+{
+ int izoom, ddax, next;
+ u_long dw, pixels, bitsput;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+
+ scan->UserBuf.pb_rgb[pixels].Red =
+ scan->Red.pcb[bitsput].a_bColor[0];
+ scan->UserBuf.pb_rgb[pixels].Green =
+ scan->Green.pcb[bitsput].a_bColor[0];
+ scan->UserBuf.pb_rgb[pixels].Blue =
+ scan->Blue.pcb[bitsput].a_bColor[0];
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+}
+
+static void usb_ColorScale8_2( Plustek_Device *dev )
+{
+ int izoom, ddax, next;
+ u_long dw, pixels, bitsput;
+ ScanDef *scan = &dev->scanning;
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+
+ scan->UserBuf.pb_rgb[pixels].Red = scan->Red.pb[bitsput];
+ scan->UserBuf.pb_rgb[pixels].Green = scan->Green.pb[bitsput];
+ scan->UserBuf.pb_rgb[pixels].Blue = scan->Blue.pb[bitsput];
+
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorScale16( Plustek_Device *dev )
+{
+ u_char ls;
+ int izoom, ddax, next;
+ u_long dw, pixels, bitsput;
+ SANE_Bool swap = usb_HostSwap();
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorWord( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+
+ if( swap ) {
+
+ scan->UserBuf.pw_rgb[pixels].Red =
+ _HILO2WORD(scan->Red.pcw[bitsput].HiLo[0]) >> ls;
+
+ scan->UserBuf.pw_rgb[pixels].Green =
+ _HILO2WORD(scan->Green.pcw[bitsput].HiLo[0]) >> ls;
+
+ scan->UserBuf.pw_rgb[pixels].Blue =
+ _HILO2WORD(scan->Blue.pcw[bitsput].HiLo[0]) >> ls;
+
+ } else {
+
+ scan->UserBuf.pw_rgb[pixels].Red = scan->Red.pw[bitsput]>>ls;
+ scan->UserBuf.pw_rgb[pixels].Green = scan->Green.pw[bitsput] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Blue = scan->Blue.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorScale16_2( Plustek_Device *dev )
+{
+ u_char ls;
+ HiLoDef tmp;
+ int izoom, ddax, next;
+ u_long dw, pixels, bitsput;
+ SANE_Bool swap = usb_HostSwap();
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorWord( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+
+ if( swap ) {
+
+ tmp = *((HiLoDef*)&scan->Red.pw[bitsput]);
+ scan->UserBuf.pw_rgb[pixels].Red = _HILO2WORD(tmp) >> ls;
+
+ tmp = *((HiLoDef*)&scan->Green.pw[bitsput]);
+ scan->UserBuf.pw_rgb[pixels].Green = _HILO2WORD(tmp) >> ls;
+
+ tmp = *((HiLoDef*)&scan->Blue.pw[bitsput]);
+ scan->UserBuf.pw_rgb[pixels].Blue = _HILO2WORD(tmp) >> ls;
+
+ } else {
+
+ scan->UserBuf.pw_rgb[pixels].Red = scan->Red.pw[bitsput] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Green = scan->Green.pw[bitsput] >> ls;
+ scan->UserBuf.pw_rgb[pixels].Blue = scan->Blue.pw[bitsput] >> ls;
+ }
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_ColorScalePseudo16( Plustek_Device *dev )
+{
+ int izoom, ddax, next;
+ u_short wR, wG, wB;
+ u_long dw, pixels, bitsput;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageColorByte( dev );
+
+ dw = scan->sParam.Size.dwPixels;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ pixels = scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ pixels = 0;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ wR = (u_short)scan->Red.pcb[0].a_bColor[0];
+ wG = (u_short)scan->Green.pcb[0].a_bColor[1];
+ wB = (u_short)scan->Blue.pcb[0].a_bColor[2];
+
+ for( bitsput = 0, ddax = 0; dw; bitsput++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (dw > 0)) {
+
+ scan->UserBuf.pw_rgb[pixels].Red =
+ (wR + scan->Red.pcb[bitsput].a_bColor[0]) << bShift;
+
+ scan->UserBuf.pw_rgb[pixels].Green =
+ (wG + scan->Green.pcb[bitsput].a_bColor[0]) << bShift;
+
+ scan->UserBuf.pw_rgb[pixels].Blue =
+ (wB + scan->Blue.pcb[bitsput].a_bColor[0]) << bShift;
+
+ pixels += next;
+ ddax += izoom;
+ dw--;
+ }
+
+ wR = (u_short)scan->Red.pcb[bitsput].a_bColor[0];
+ wG = (u_short)scan->Green.pcb[bitsput].a_bColor[0];
+ wB = (u_short)scan->Blue.pcb[bitsput].a_bColor[0];
+ }
+}
+
+/**
+ */
+static void usb_BWScale( Plustek_Device *dev )
+{
+ u_char tmp, *dest, *src;
+ int izoom, ddax;
+ u_long i, dw;
+ ScanDef *scan = &dev->scanning;
+
+ src = scan->Green.pb;
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ int iSum = wSum;
+ usb_ReverseBitStream(scan->Green.pb, scan->UserBuf.pb,
+ scan->sParam.Size.dwValidPixels,
+ scan->dwBytesLine, scan->sParam.PhyDpi.x,
+ scan->sParam.UserDpi.x, 1 );
+ wSum = iSum;
+ return;
+ } else {
+ dest = scan->UserBuf.pb;
+ }
+
+ izoom = usb_GetScaler( scan );
+
+ memset( dest, 0, scan->dwBytesLine );
+ ddax = 0;
+ dw = 0;
+
+ for( i = 0; i < scan->sParam.Size.dwValidPixels; i++ ) {
+
+ ddax -= _SCALER;
+
+ while( ddax < 0 ) {
+
+ tmp = src[(i>>3)];
+
+ if((dw>>3) < scan->sParam.Size.dwValidPixels ) {
+
+ if( 0 != (tmp &= (1 << ((~(i & 0x7))&0x7))))
+ dest[dw>>3] |= (1 << ((~(dw & 0x7))&0x7));
+ }
+ dw++;
+ ddax += izoom;
+ }
+ }
+}
+
+/**
+ */
+static void usb_BWScaleFromColor( Plustek_Device *dev )
+{
+ u_char d, s, *dest;
+ u_short j;
+ u_long pixels;
+ int izoom, ddax, next;
+ ColorByteDef *src;
+ ScanDef *scan = &dev->scanning;
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
+ next = -1;
+ } else {
+ dest = scan->UserBuf.pb;
+ next = 1;
+ }
+
+ /* setup the source buffer */
+ switch(scan->fGrayFromColor) {
+ case 1: src = scan->Red.pcb; break;
+ case 3: src = scan->Blue.pcb; break;
+ default: src = scan->Green.pcb; break;
+ }
+
+ izoom = usb_GetScaler( scan );
+ ddax = 0;
+
+ d = j = 0;
+ for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (pixels > 0)) {
+
+ s = src->a_bColor[0];
+ if( s != 0 )
+ d |= BitTable[j];
+ j++;
+ if( j == 8 ) {
+ *dest = d;
+ dest += next;
+ d = j = 0;
+ }
+ ddax += izoom;
+ pixels--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_BWScaleFromColor_2( Plustek_Device *dev )
+{
+ u_char d, *dest, *src;
+ u_short j;
+ u_long pixels;
+ int izoom, ddax, next;
+ ScanDef *scan = &dev->scanning;
+
+ if (scan->sParam.bSource == SOURCE_ADF) {
+ dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
+ next = -1;
+ } else {
+ dest = scan->UserBuf.pb;
+ next = 1;
+ }
+
+ /* setup the source buffer */
+ switch(scan->fGrayFromColor) {
+ case 1: src = scan->Red.pb; break;
+ case 3: src = scan->Blue.pb; break;
+ default: src = scan->Green.pb; break;
+ }
+
+ izoom = usb_GetScaler( scan );
+ ddax = 0;
+
+ d = j = 0;
+ for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (pixels > 0)) {
+
+ if( *src != 0 )
+ d |= BitTable[j];
+ j++;
+ if( j == 8 ) {
+ *dest = d;
+ dest += next;
+ d = j = 0;
+ }
+ ddax += izoom;
+ pixels--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_GrayScale8( Plustek_Device *dev )
+{
+ u_char *dest, *src;
+ int izoom, ddax, next;
+ u_long pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageGrayByte( dev );
+
+ src = scan->Green.pb;
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
+ next = -1;
+ } else {
+ dest = scan->UserBuf.pb;
+ next = 1;
+ }
+
+ izoom = usb_GetScaler( scan );
+ ddax = 0;
+
+ for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (pixels > 0)) {
+
+ *dest = *src;
+ dest += next;
+ ddax += izoom;
+ pixels--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_GrayScale16( Plustek_Device *dev )
+{
+ u_char ls;
+ int izoom, ddax, next;
+ u_short *dest;
+ u_long pixels;
+ HiLoDef *pwm;
+ ScanDef *scan = &dev->scanning;
+ SANE_Bool swap = usb_HostSwap();
+
+ usb_AverageGrayWord( dev);
+
+ pwm = scan->Green.philo;
+ wSum = scan->sParam.PhyDpi.x;
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ dest = scan->UserBuf.pw;
+ }
+
+ izoom = usb_GetScaler( scan );
+ ddax = 0;
+
+ if( scan->dwFlag & SCANFLAG_RightAlign )
+ ls = Shift;
+ else
+ ls = 0;
+
+ for( pixels = scan->sParam.Size.dwPixels; pixels; pwm++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (pixels > 0)) {
+
+ if( swap )
+ *dest = _PHILO2WORD(pwm) >> ls;
+ else
+ *dest = _PLOHI2WORD(pwm) >> ls;
+
+ dest += next;
+ ddax += izoom;
+ pixels--;
+ }
+ }
+}
+
+/**
+ */
+static void usb_GrayScalePseudo16( Plustek_Device *dev )
+{
+ u_char *src;
+ int izoom, ddax, next;
+ u_short *dest, g;
+ u_long pixels;
+ ScanDef *scan = &dev->scanning;
+
+ usb_AverageGrayByte( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+ next = -1;
+ dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
+ } else {
+ next = 1;
+ dest = scan->UserBuf.pw;
+ }
+
+ src = scan->Green.pb;
+ g = (u_short)*src;
+
+ izoom = usb_GetScaler( scan );
+ ddax = 0;
+
+ for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {
+
+ ddax -= _SCALER;
+
+ while((ddax < 0) && (pixels > 0)) {
+
+ *dest = (g + *src) << bShift;
+ dest += next;
+ ddax += izoom;
+ pixels--;
+ }
+ g = (u_short)*src;
+ }
+}
+
+/** function to select the apropriate pixel copy function
+ */
+static void usb_GetImageProc( Plustek_Device *dev )
+{
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *sc = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ bShift = 0;
+
+ if( scan->sParam.UserDpi.x != scan->sParam.PhyDpi.x ) {
+
+ /* Pixel scaling... */
+ switch( scan->sParam.bDataType ) {
+
+ case SCANDATATYPE_Color:
+ if (scan->sParam.bBitDepth > 8) {
+
+ if( usb_IsCISDevice(dev)){
+ scan->pfnProcess = usb_ColorScale16_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorScale16_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorScale16;
+ DBG( _DBG_INFO, "ImageProc is: ColorScale16\n" );
+ }
+ if (scan->fGrayFromColor) {
+ if( usb_IsCISDevice(dev)){
+ scan->pfnProcess = usb_ColorScaleGray16_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorScaleGray16_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorScaleGray16;
+ DBG( _DBG_INFO, "ImageProc is: ColorScaleGray16\n" );
+ }
+ }
+ } else if (scan->dwFlag & SCANFLAG_Pseudo48) {
+ scan->pfnProcess = usb_ColorScalePseudo16;
+ DBG( _DBG_INFO, "ImageProc is: ColorScalePseudo16\n" );
+
+ } else if (scan->fGrayFromColor) {
+
+ if( usb_IsCISDevice(dev)){
+ if (scan->fGrayFromColor > 7 ) {
+ scan->pfnProcess = usb_BWScaleFromColor_2;
+ DBG( _DBG_INFO, "ImageProc is: BWScaleFromColor_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorScaleGray_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorScaleGray_2\n" );
+ }
+ } else {
+ if (scan->fGrayFromColor > 7 ) {
+ scan->pfnProcess = usb_BWScaleFromColor;
+ DBG( _DBG_INFO, "ImageProc is: BWScaleFromColor\n" );
+ } else {
+ scan->pfnProcess = usb_ColorScaleGray;
+ DBG( _DBG_INFO, "ImageProc is: ColorScaleGray\n" );
+ }
+ }
+ } else {
+
+ if( usb_IsCISDevice(dev)){
+ scan->pfnProcess = usb_ColorScale8_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorScale8_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorScale8;
+ DBG( _DBG_INFO, "ImageProc is: ColorScale8\n" );
+ }
+ }
+ break;
+
+ case SCANDATATYPE_Gray:
+ if (scan->sParam.bBitDepth > 8) {
+ scan->pfnProcess = usb_GrayScale16;
+ DBG( _DBG_INFO, "ImageProc is: GrayScale16\n" );
+ } else {
+
+ if (scan->dwFlag & SCANFLAG_Pseudo48) {
+ scan->pfnProcess = usb_GrayScalePseudo16;
+ DBG( _DBG_INFO, "ImageProc is: GrayScalePseudo16\n" );
+ } else {
+ scan->pfnProcess = usb_GrayScale8;
+ DBG( _DBG_INFO, "ImageProc is: GrayScale8\n" );
+ }
+ }
+ break;
+
+ default:
+ scan->pfnProcess = usb_BWScale;
+ DBG( _DBG_INFO, "ImageProc is: BWScale\n" );
+ break;
+ }
+
+ } else {
+
+ /* Pixel copy */
+ switch( scan->sParam.bDataType ) {
+
+ case SCANDATATYPE_Color:
+ if (scan->sParam.bBitDepth > 8) {
+ if( usb_IsCISDevice(dev)){
+ scan->pfnProcess = usb_ColorDuplicate16_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicate16_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorDuplicate16;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicate16\n" );
+ }
+ if (scan->fGrayFromColor) {
+ if( usb_IsCISDevice(dev)){
+ scan->pfnProcess = usb_ColorDuplicateGray16_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray16_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorDuplicateGray16;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray16\n" );
+ }
+ }
+ } else if (scan->dwFlag & SCANFLAG_Pseudo48) {
+ scan->pfnProcess = usb_ColorDuplicatePseudo16;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicatePseudo16\n" );
+ } else if (scan->fGrayFromColor) {
+ if( usb_IsCISDevice(dev)){
+ if (scan->fGrayFromColor > 7 ) {
+ scan->pfnProcess = usb_BWDuplicateFromColor_2;
+ DBG( _DBG_INFO, "ImageProc is: BWDuplicateFromColor_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorDuplicateGray_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray_2\n" );
+ }
+ } else {
+ if (scan->fGrayFromColor > 7 ) {
+ scan->pfnProcess = usb_BWDuplicateFromColor;
+ DBG( _DBG_INFO, "ImageProc is: BWDuplicateFromColor\n" );
+ } else {
+ scan->pfnProcess = usb_ColorDuplicateGray;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray\n" );
+ }
+ }
+ } else {
+ if( usb_IsCISDevice(dev)){
+ scan->pfnProcess = usb_ColorDuplicate8_2;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicate8_2\n" );
+ } else {
+ scan->pfnProcess = usb_ColorDuplicate8;
+ DBG( _DBG_INFO, "ImageProc is: ColorDuplicate8\n" );
+ }
+ }
+ break;
+
+ case SCANDATATYPE_Gray:
+ if (scan->sParam.bBitDepth > 8) {
+ scan->pfnProcess = usb_GrayDuplicate16;
+ DBG( _DBG_INFO, "ImageProc is: GrayDuplicate16\n" );
+ } else {
+ if (scan->dwFlag & SCANFLAG_Pseudo48) {
+ scan->pfnProcess = usb_GrayDuplicatePseudo16;
+ DBG( _DBG_INFO, "ImageProc is: GrayDuplicatePseudo16\n" );
+ } else {
+ scan->pfnProcess = usb_GrayDuplicate8;
+ DBG( _DBG_INFO, "ImageProc is: GrayDuplicate8\n" );
+ }
+ }
+ break;
+
+ default:
+ scan->pfnProcess = usb_BWDuplicate;
+ DBG( _DBG_INFO, "ImageProc is: BWDuplicate\n" );
+ break;
+ }
+ }
+
+ if( scan->sParam.bBitDepth == 8 ) {
+
+ if( scan->dwFlag & SCANFLAG_Pseudo48 ) {
+ if( scan->dwFlag & SCANFLAG_RightAlign ) {
+ bShift = 5;
+ } else {
+
+ /* this should fix the Bearpaw/U12 discrepancy
+ * in general the fix is needed, but not for the U12
+ * why? - no idea!
+ */
+ if(_WAF_BSHIFT7_BUG == (_WAF_BSHIFT7_BUG & sc->workaroundFlag))
+ bShift = 0; /* Holger Bischof 16.12.2001 */
+ else
+ bShift = 7;
+ }
+ DBG( _DBG_INFO, "bShift adjusted: %u\n", bShift );
+ }
+ }
+
+ if( _LM9833 == hw->chip ) {
+ Shift = 0;
+ Mask = 0xFFFF;
+ } else {
+ Shift = 2;
+ Mask = 0xFFFC;
+ }
+}
+
+/**
+ * here we read the image data into our intermediate buffer (in the NT version
+ * the function was implemented as thread)
+ */
+static SANE_Int usb_ReadData( Plustek_Device *dev )
+{
+ u_long dw, dwRet, dwBytes, pl;
+ ScanDef *scan = &dev->scanning;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ DBG( _DBG_READ, "usb_ReadData()\n" );
+
+ pl = dev->usbDev.a_bRegs[0x4e] * hw->wDRAMSize/128;
+
+ while( scan->sParam.Size.dwTotalBytes ) {
+
+ if( usb_IsEscPressed()) {
+ DBG( _DBG_INFO, "usb_ReadData() - Cancel detected...\n" );
+ return 0;
+ }
+
+ if( scan->sParam.Size.dwTotalBytes > scan->dwBytesScanBuf )
+ dw = scan->dwBytesScanBuf;
+ else
+ dw = scan->sParam.Size.dwTotalBytes;
+
+ scan->sParam.Size.dwTotalBytes -= dw;
+
+ if(!scan->sParam.Size.dwTotalBytes && dw < (pl * 1024))
+ {
+ if(!(dev->usbDev.a_bRegs[0x4e] = (u_char)ceil((double)dw /
+ (4.0 * hw->wDRAMSize)))) {
+ dev->usbDev.a_bRegs[0x4e] = 1;
+ }
+ dev->usbDev.a_bRegs[0x4f] = 0;
+
+ sanei_lm983x_write( dev->fd, 0x4e, &dev->usbDev.a_bRegs[0x4e], 2, SANE_TRUE );
+ }
+
+ while( scan->bLinesToSkip ) {
+
+ DBG( _DBG_READ, "Skipping %u lines\n", scan->bLinesToSkip );
+
+ dwBytes = scan->bLinesToSkip * scan->sParam.Size.dwPhyBytes;
+
+ if (dwBytes > scan->dwBytesScanBuf) {
+
+ dwBytes = scan->dwBytesScanBuf;
+ scan->bLinesToSkip -= scan->dwLinesScanBuf;
+ } else {
+ scan->bLinesToSkip = 0;
+ }
+
+ if( !usb_ScanReadImage( dev, scan->pbGetDataBuf, dwBytes ))
+ return 0;
+ }
+
+ if( usb_ScanReadImage( dev, scan->pbGetDataBuf, dw )) {
+
+ dumpPic("plustek-pic.raw", scan->pbGetDataBuf, dw, 0);
+
+ if( scan->dwLinesDiscard ) {
+
+ DBG(_DBG_READ, "Discarding %lu lines\n", scan->dwLinesDiscard);
+
+ dwRet = dw / scan->sParam.Size.dwPhyBytes;
+
+ if (scan->dwLinesDiscard > dwRet) {
+ scan->dwLinesDiscard -= dwRet;
+ dwRet = 0;
+ } else {
+ dwRet -= scan->dwLinesDiscard;
+ scan->dwLinesDiscard = 0;
+ }
+ } else {
+
+ dwRet = dw / scan->sParam.Size.dwPhyBytes;
+ }
+
+ scan->pbGetDataBuf += scan->dwBytesScanBuf;
+ if( scan->pbGetDataBuf >= scan->pbScanBufEnd ) {
+ scan->pbGetDataBuf = scan->pbScanBufBegin;
+ }
+
+ if( dwRet )
+ return dwRet;
+ }
+ }
+ return 0;
+}
+
+/* END PLUSTEK-USBIMG.C .....................................................*/
diff --git a/backend/plustek-usbio.c b/backend/plustek-usbio.c
new file mode 100644
index 0000000..cae0e0d
--- /dev/null
+++ b/backend/plustek-usbio.c
@@ -0,0 +1,351 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbio.c
+ * @brief Some I/O stuff.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - moved some functions to a sane library (sanei_lm983x.c)
+ * - 0.42 - no changes
+ * - 0.43 - no changes
+ * - 0.44 - added dump registers and dumpPic functions
+ * - beautyfied output of ASIC detection
+ * - 0.45 - fixed dumpRegs
+ * - added dimension stuff to dumpPic
+ * - 0.46 - disabled reset prior to the detection of Merlin
+ * - 0.47 - no changes
+ * - 0.48 - cleanup
+ * - 0.49 - no changes
+ * - 0.50 - usbio_DetectLM983x() now returns error if register
+ * could not be red
+ * - usbio_ResetLM983x() checks for reg7 value before writing
+ * - 0.51 - allow dumpRegs to be called without valid fd
+ * - 0.52 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_lm983x.h"
+
+#define _UIO(func) \
+ { \
+ SANE_Status status; \
+ status = func; \
+ if (status != SANE_STATUS_GOOD) { \
+ DBG( _DBG_ERROR, "UIO error\n" ); \
+ return SANE_FALSE; \
+ } \
+ }
+
+#define usbio_ReadReg(fd, reg, value) \
+ sanei_lm983x_read (fd, reg, value, 1, 0)
+
+typedef struct {
+
+ u_char depth;
+ u_long x;
+ u_long y;
+} PicDef, *pPicDef;
+
+static PicDef dPix;
+
+/**
+ */
+static void dumpPic( char* name, SANE_Byte *buffer, u_long len, int is_gray )
+{
+ u_short type;
+ FILE *fp;
+
+ if( DBG_LEVEL < _DBG_DPIC )
+ return;
+
+ if( NULL == buffer ) {
+
+ DBG( _DBG_DPIC, "Creating file '%s'\n", name );
+
+ fp = fopen( name, "w+b" );
+
+ if( NULL != fp ) {
+
+ if( 0 != dPix.x ) {
+
+ if (is_gray)
+ type = 5;
+ else
+ type = 6;
+
+ DBG( _DBG_DPIC, "> X=%lu, Y=%lu, depth=%u\n",
+ dPix.x, dPix.y, dPix.depth );
+ if( dPix.depth > 8 )
+ fprintf( fp, "P%u\n%lu %lu\n65535\n", type, dPix.x, dPix.y);
+ else
+ fprintf( fp, "P%u\n%lu %lu\n255\n", type, dPix.x, dPix.y);
+ }
+ }
+ } else {
+ fp = fopen( name, "a+b" );
+ }
+
+ if( NULL == fp ) {
+ DBG( _DBG_DPIC, "Can not open file '%s'\n", name );
+ return;
+ }
+
+ fwrite( buffer, 1, len, fp );
+ fclose( fp );
+}
+
+/**
+ */
+static void dumpPicInit( ScanParam *sd, char* name )
+{
+ dPix.x = sd->Size.dwPhyBytes;
+
+ if( sd->bDataType == SCANDATATYPE_Color )
+ dPix.x /= 3;
+
+ if( sd->bBitDepth > 8 )
+ dPix.x /= 2;
+
+ dPix.y = sd->Size.dwLines;
+ dPix.depth = sd->bBitDepth;
+
+ if( sd->bDataType == SCANDATATYPE_Color )
+ dumpPic(name, NULL, 0, 0);
+ else
+ dumpPic(name, NULL, 0, 1);
+}
+
+/**
+ * dump the LM983x registers
+ */
+static void dumpregs( int fd, SANE_Byte *cmp )
+{
+ char buf[256], b2[10];
+ SANE_Byte regs[0x80];
+ int i;
+
+ if( DBG_LEVEL < _DBG_DREGS )
+ return;
+
+ buf[0] = '\0';
+
+ if( fd >= 0 ) {
+ usbio_ReadReg(fd, 0x01, &regs[0x01]);
+ usbio_ReadReg(fd, 0x02, &regs[0x02]);
+ usbio_ReadReg(fd, 0x03, &regs[0x03]);
+ usbio_ReadReg(fd, 0x04, &regs[0x04]);
+ usbio_ReadReg(fd, 0x07, &regs[0x07]);
+
+ sanei_lm983x_read( fd, 0x08, &regs[0x8], 0x80-0x8, SANE_TRUE );
+
+ for( i = 0x0; i < 0x80; i++ ) {
+
+ if((i%16) ==0 ) {
+
+ if( buf[0] )
+ DBG( _DBG_DREGS, "%s\n", buf );
+ sprintf( buf, "0x%02x:", i );
+ }
+
+ if((i%8)==0)
+ strcat( buf, " ");
+
+ /* the dataport read returns with "0 Bytes read", of course. */
+ if((i == 0) || (i == 5) || (i == 6))
+ strcat( buf, "XX ");
+ else {
+
+ sprintf( b2, "%02x ", regs[i]);
+ strcat( buf, b2 );
+ }
+ }
+ DBG( _DBG_DREGS, "%s\n", buf );
+ }
+
+ if( cmp ) {
+
+ buf[0] = '\0';
+
+ DBG( _DBG_DREGS, "Internal setting:\n" );
+ for( i = 0x0; i < 0x80; i++ ) {
+
+ if((i%16) ==0 ) {
+
+ if( buf[0] )
+ DBG( _DBG_DREGS, "%s\n", buf );
+ sprintf( buf, "0x%02x:", i );
+ }
+
+ if((i%8)==0)
+ strcat( buf, " ");
+
+ if((i == 0) || (i == 5) || (i == 6))
+ strcat( buf, "XX ");
+ else {
+ sprintf( b2, "%02x ", cmp[i]);
+ strcat( buf, b2 );
+ }
+ }
+ DBG( _DBG_DREGS, "%s\n", buf );
+ }
+}
+
+/**
+ * function to read the contents of a LM983x register and regarding some
+ * extra stuff, like flushing register 2 when writing register 0x58, etc
+ *
+ * @param handle -
+ * @param reg -
+ * @param value -
+ * @return
+ */
+static SANE_Bool usbio_WriteReg( SANE_Int handle,
+ SANE_Byte reg, SANE_Byte value )
+{
+ int i;
+ SANE_Byte data;
+
+ /* retry loop... */
+ for( i = 0; i < 100; i++ ) {
+
+ sanei_lm983x_write_byte( handle, reg, value );
+
+ /* Flush register 0x02 when register 0x58 is written */
+ if( 0x58 == reg ) {
+ _UIO( usbio_ReadReg( handle, 2, &data ));
+ _UIO( usbio_ReadReg( handle, 2, &data ));
+ }
+
+ if( reg != 7 )
+ return SANE_TRUE;
+
+ /* verify register 7 */
+ _UIO( usbio_ReadReg( handle, 7, &data ));
+ if( data == value ) {
+ return SANE_TRUE;
+ }
+ }
+
+ return SANE_FALSE;
+}
+
+/** try and read register 0x69 from a LM983x to find out which version we have.
+ */
+static SANE_Status usbio_DetectLM983x( SANE_Int fd, SANE_Byte *version )
+{
+ char buf[256];
+ SANE_Byte value;
+ SANE_Status res;
+
+ DBG( _DBG_INFO, "usbio_DetectLM983x\n");
+
+ res = usbio_ReadReg(fd, 0x69, &value);
+ if( res != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, " * could not read version register!\n");
+ return res;
+ }
+
+ value &= 7;
+ if (version)
+ *version = value;
+
+ res = SANE_STATUS_GOOD;
+
+ sprintf( buf, "usbio_DetectLM983x: found " );
+
+ switch((SANE_Int)value ) {
+
+ case 4: strcat( buf, "LM9832/3" ); break;
+ case 3: strcat( buf, "LM9831" ); break;
+ case 2: strcat( buf, "LM9830 --> unsupported!!!" );
+ res = SANE_STATUS_INVAL;
+ break;
+ default: DBG( _DBG_INFO, "Unknown chip v%d", value );
+ res = SANE_STATUS_INVAL;
+ break;
+ }
+
+ DBG( _DBG_INFO, "%s\n", buf );
+ return res;
+}
+
+/** well, this is more or less a reset function, for LM9831 based devices
+ * we issue a real reset command, while for LM9832/3 based devices, checking
+ * and resetting register 7 will be enough...
+ */
+static SANE_Status usbio_ResetLM983x( Plustek_Device *dev )
+{
+ SANE_Byte value;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ if( _LM9831 == hw->chip ) {
+
+ DBG( _DBG_INFO," * resetting LM9831 device!\n");
+ _UIO( sanei_lm983x_write_byte( dev->fd, 0x07, 0));
+ _UIO( sanei_lm983x_write_byte( dev->fd, 0x07,0x20));
+ _UIO( sanei_lm983x_write_byte( dev->fd, 0x07, 0));
+ _UIO( usbio_ReadReg( dev->fd, 0x07, &value));
+ if (value != 0) {
+ DBG( _DBG_ERROR, "usbio_ResetLM983x: reset was not "
+ "successful, status=%d\n", value );
+ return SANE_STATUS_INVAL;
+ }
+
+ } else {
+ _UIO( usbio_ReadReg( dev->fd, 0x07, &value));
+ if (value != 0 ) {
+ DBG( _DBG_INFO," * setting device to idle state!\n");
+ _UIO( sanei_lm983x_write_byte( dev->fd, 0x07, 0));
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* END PLUSTEK-USBIO.C ......................................................*/
diff --git a/backend/plustek-usbmap.c b/backend/plustek-usbmap.c
new file mode 100644
index 0000000..1e54789
--- /dev/null
+++ b/backend/plustek-usbmap.c
@@ -0,0 +1,210 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbmap.c
+ * @brief Creating and manipulating lookup tables.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - fixed brightness problem for lineart mode
+ * - 0.42 - removed preset of linear gamma tables
+ * - 0.43 - no changes
+ * - 0.44 - map inversion for negatatives now only upon user request
+ * - 0.45 - no changes
+ * - 0.46 - no changes
+ * - 0.47 - cleanup work
+ * - 0.48 - added support for binary from color scans
+ * - 0.49 - changed usb_MapDownload
+ * - 0.50 - cleanup
+ * - 0.51 - no changes
+ * - 0.52 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#define _MAP_SIZE 4096
+
+static SANE_Byte a_bMap[_MAP_SIZE * 3];
+
+/** adjust acording to brightness and contrast
+ */
+static void usb_MapAdjust( Plustek_Device *dev )
+{
+ int i, tabLen;
+ double b, c, tmp;
+
+ tabLen = _MAP_SIZE;
+
+ /* adjust brightness (b) and contrast (c) using the function:
+ *
+ * s(x,y) = (s(x,y) + b) * c
+ * b = [-127, 127]
+ * c = [0,2]
+ */
+ b = ((double)dev->scanning.sParam.brightness * 192.0)/100.0;
+ c = ((double)dev->scanning.sParam.contrast + 100.0)/100.0;
+
+ DBG( _DBG_INFO, "* brightness = %i -> %i\n",
+ dev->scanning.sParam.brightness, (u_char)b);
+ DBG( _DBG_INFO, "* contrast = %i -> %.3f\n",
+ dev->scanning.sParam.contrast, c);
+
+ if( dev->scanning.sParam.brightness == 0 && dev->scanning.sParam.contrast )
+ return;
+
+ for( i = 0; i < tabLen; i++ ) {
+
+ tmp = ((double)(a_bMap[i] + b)) * c;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ a_bMap[i] = (u_char)tmp;
+
+ tmp = ((double)(a_bMap[tabLen+i] + b)) * c;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ a_bMap[tabLen+i] = (u_char)tmp;
+
+ tmp = ((double)(a_bMap[tabLen*2+i] + b)) * c;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ a_bMap[tabLen*2+i] = (u_char)tmp;
+ }
+}
+
+/**
+ */
+static SANE_Bool usb_MapDownload( Plustek_Device *dev )
+{
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *sc = &dev->usbDev.Caps;
+
+ int color;
+ int i, threshold;
+ SANE_Byte value;
+ SANE_Bool fInverse = 0;
+
+ DBG( _DBG_INFO, "usb_MapDownload()\n" );
+
+ /* the maps are have been already set */
+
+ /* do the brightness and contrast adjustment ... */
+ if( scanning->sParam.bDataType != SCANDATATYPE_BW )
+ usb_MapAdjust( dev );
+
+ if( !usbio_WriteReg( dev->fd, 7, 0))
+ return SANE_FALSE;
+
+ /* we download all the time all three color maps, as we run
+ * into trouble elsewhere on CanoScan models using gray mode
+ */
+ for( color = 0; color < 3; color++) {
+
+ /* select color */
+ value = (color << 2)+2;
+
+ /* set gamma color selector */
+ usbio_WriteReg( dev->fd, 0x03, value );
+ usbio_WriteReg( dev->fd, 0x04, 0 );
+ usbio_WriteReg( dev->fd, 0x05, 0 );
+
+ /* write the gamma table entry to merlin */
+ if((scanning->sParam.bDataType == SCANDATATYPE_BW) ||
+ (scanning->fGrayFromColor > 7 )) {
+
+ threshold = (int)((double)scanning->sParam.brightness *
+ (_MAP_SIZE/200.0)) + (_MAP_SIZE/2);
+ threshold = _MAP_SIZE - threshold;
+ if(threshold < 0)
+ threshold = 0;
+ if(threshold > (int)_MAP_SIZE)
+ threshold = _MAP_SIZE;
+
+ DBG(_DBG_INFO, "* Threshold is at %u brightness=%i\n",
+ threshold, scanning->sParam.brightness );
+
+ for(i = 0; i < threshold; i++)
+ a_bMap[color*_MAP_SIZE + i] = 0;
+
+ for(i = threshold; i < _MAP_SIZE; i++)
+ a_bMap[color*_MAP_SIZE + i] = 255;
+
+ fInverse = 1;
+
+ } else {
+ fInverse = 0;
+ }
+
+ if( /*scanning->dwFlag & SCANFLAG_Pseudo48 && */
+ (scanning->sParam.bSource == SOURCE_Negative) &&
+ (sc->workaroundFlag &_WAF_INV_NEGATIVE_MAP)) {
+ fInverse ^= 1;
+ }
+
+ if( fInverse ) {
+
+ u_char map[_MAP_SIZE];
+ u_char *pMap = a_bMap+color*_MAP_SIZE;
+
+ DBG( _DBG_INFO, "* Inverting Map\n" );
+
+ for( i = 0; i < _MAP_SIZE; i++, pMap++ )
+ map[i] = ~*pMap;
+
+ sanei_lm983x_write( dev->fd, 0x06, map, _MAP_SIZE, SANE_FALSE );
+
+ } else {
+ DBG( _DBG_INFO, "* downloading map %u...\n", color );
+ sanei_lm983x_write( dev->fd, 0x06, a_bMap+color*_MAP_SIZE,
+ _MAP_SIZE, SANE_FALSE );
+ }
+
+ } /* for each color */
+
+ DBG( _DBG_INFO, "usb_MapDownload() done.\n" );
+ return SANE_TRUE;
+}
+
+/* END PLUSTEK-USBMAP.C .....................................................*/
diff --git a/backend/plustek-usbscan.c b/backend/plustek-usbscan.c
new file mode 100644
index 0000000..c8824fc
--- /dev/null
+++ b/backend/plustek-usbscan.c
@@ -0,0 +1,1666 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbscan.c
+ * @brief Scanning...
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - minor fixes
+ * - 0.42 - added some stuff for CIS devices
+ * - 0.43 - no changes
+ * - 0.44 - added CIS specific settings and calculations
+ * - removed usb_IsEscPressed
+ * - 0.45 - fixed the special setting stuff for CanoScan
+ * - fixed pixel calculation for binary modes
+ * - fixed cancel hang problem
+ * - fixed CIS PhyBytes adjustment
+ * - removed CanoScan specific setting stuff
+ * - 0.46 - fixed problem in usb_SetScanParameters()
+ * - 0.47 - no changes
+ * - 0.48 - minor fixes
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - using now PhyDpi.y as selector for the motor MCLK range
+ * - changed usb_MapDownload prototype
+ * - 0.50 - cleanup
+ * - 0.51 - added usb_get_res() and usb_GetPhyPixels()
+ * - 0.52 - removed stuff, that will most probably never be used
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#define DIVIDER 8
+
+/** array used to get motor-settings and mclk-settings
+ */
+static int dpi_ranges[] = { 75,100,150,200,300,400,600,800,1200,2400 };
+
+static u_char bMaxITA;
+
+static SANE_Bool m_fAutoPark;
+static SANE_Bool m_fFirst;
+static double m_dHDPIDivider;
+static double m_dMCLKDivider;
+static ScanParam *m_pParam;
+static u_char m_bLineRateColor;
+static u_char m_bCM;
+static u_char m_bIntTimeAdjust;
+static u_char m_bOldScanData;
+static u_short m_wFastFeedStepSize;
+static u_short m_wLineLength;
+static u_short m_wStepSize;
+static u_long m_dwPauseLimit;
+
+static SANE_Bool m_fStart = SANE_FALSE;
+
+/* Prototype... */
+static SANE_Bool usb_DownloadShadingData( Plustek_Device*, u_char );
+
+/** returns the min of the two values val1 and val2
+ * @param val1 - first parameter
+ * @param val2 - second parameter
+ * @return val1 if val1 < val2, else val1
+ */
+static u_long
+usb_min( u_long val1, u_long val2 )
+{
+ if( val1 > val2 )
+ return val2;
+ return val1;
+}
+
+/** returns the max of the two values val1 and val2
+ * @param val1 - first parameter
+ * @param val2 - second parameter
+ * @return val1 if val1 > val2, else val2
+ */
+static u_long
+usb_max( u_long val1, u_long val2 )
+{
+ if( val1 > val2 )
+ return val1;
+ return val2;
+}
+
+/** function to get the possible resolution for a specific base resolution,
+ * according to the dividers a LM983x offers.
+ * @param base - scanners optical resolution
+ * @param idx - which divider to use
+ */
+static u_short
+usb_get_res( u_short base, u_short idx )
+{
+ double div_list[DIVIDER] = { 12.0, 8.0, 6.0, 4.0, 3.0, 2.0, 1.5, 1.0 };
+
+ if( idx == 0 || idx > DIVIDER )
+ return 0;
+
+ return (u_short)((double)base/div_list[idx-1]);
+}
+
+/** Set the horizontal DPI divider.
+ * Affected registers:<br>
+ * 0x09 - Horizontal DPI divider HDPI_DIV<br>
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param xdpi - user specified horizontal resolution
+ * @return - the function returns the "normalized" horizontal resolution.
+ */
+static u_short
+usb_SetAsicDpiX( Plustek_Device *dev, u_short xdpi )
+{
+ u_short res;
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* limit xdpi to lower value for certain devices...
+ */
+ if( scaps->OpticDpi.x == 1200 &&
+ scanning->sParam.bDataType != SCANDATATYPE_Color &&
+ xdpi < 150 &&
+ scanning->sParam.bDataType == SCANDATATYPE_BW ) {
+ xdpi = 150;
+ DBG( _DBG_INFO2, "* LIMIT XDPI to %udpi\n", xdpi );
+ }
+
+ m_dHDPIDivider = (double)scaps->OpticDpi.x / xdpi;
+
+ if (m_dHDPIDivider < 1.5)
+ {
+ m_dHDPIDivider = 1.0;
+ regs[0x09] = 0;
+ }
+ else if (m_dHDPIDivider < 2.0)
+ {
+ m_dHDPIDivider = 1.5;
+ regs[0x09] = 1;
+ }
+ else if (m_dHDPIDivider < 3.0)
+ {
+ m_dHDPIDivider = 2.0;
+ regs[0x09] = 2;
+ }
+ else if (m_dHDPIDivider < 4.0)
+ {
+ m_dHDPIDivider = 3.0;
+ regs[0x09] = 3;
+ }
+ else if (m_dHDPIDivider < 6.0)
+ {
+ m_dHDPIDivider = 4.0;
+ regs[0x09] = 4;
+ }
+ else if (m_dHDPIDivider < 8.0)
+ {
+ m_dHDPIDivider = 6.0;
+ regs[0x09] = 5;
+ }
+ else if (m_dHDPIDivider < 12.0)
+ {
+ m_dHDPIDivider = 8.0;
+ regs[0x09] = 6;
+ }
+ else
+ {
+ m_dHDPIDivider = 12.0;
+ regs[0x09] = 7;
+ }
+
+ /* adjust, if any turbo/preview mode is set, should be 0 here... */
+ if( regs[0x0a] )
+ regs[0x09] -= ((regs[0x0a] >> 2) + 2);
+
+ DBG( _DBG_INFO2, "* HDPI: %.3f\n", m_dHDPIDivider );
+ res = (u_short)((double)scaps->OpticDpi.x / m_dHDPIDivider);
+
+ DBG( _DBG_INFO2, "* XDPI=%u, HDPI=%.3f\n", res, m_dHDPIDivider );
+ return res;
+}
+
+/**
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param ydpi - user specified vertical resolution
+ * @return -
+ */
+static u_short
+usb_SetAsicDpiY( Plustek_Device *dev, u_short ydpi )
+{
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ u_short wMinDpi, wDpi;
+
+ if(0 != sCaps->bSensorDistance )
+ wMinDpi = sCaps->OpticDpi.y / sCaps->bSensorDistance;
+ else
+ wMinDpi = 75;
+
+ /* Here we might have to check against the MinDpi value ! */
+ wDpi = (ydpi + wMinDpi - 1) / wMinDpi * wMinDpi;
+
+ /*
+ * HEINER: added '*2'
+ */
+ if( wDpi > sCaps->OpticDpi.y * 2 )
+ wDpi = sCaps->OpticDpi.y * 2;
+
+ if( (hw->motorModel == MODEL_Tokyo600) ||
+ !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ /* return wDpi; */
+ } else if( sCaps->wFlags & DEVCAPSFLAG_Adf && sCaps->OpticDpi.x == 600 ) {
+ /* for ADF scanner color mode 300 dpi big noise */
+ if( scanning->sParam.bDataType == SCANDATATYPE_Color &&
+ scanning->sParam.bBitDepth > 8 && wDpi < 300 ) {
+ wDpi = 300;
+ }
+ } else if( sCaps->OpticDpi.x == 1200 ) {
+ if( scanning->sParam.bDataType != SCANDATATYPE_Color && wDpi < 200) {
+ wDpi = 200;
+ }
+ }
+
+ DBG( _DBG_INFO2, "* YDPI=%u, MinDPIY=%u\n", wDpi, wMinDpi );
+ return wDpi;
+}
+
+/** set color mode and sensor configuration stuff, according to the data mode
+ * Affected registers:<br>
+ * 0x26 - 0x27 - Color Mode settings<br>
+ * 0x0f - 0x18 - Sensor Configuration - directly from the HwDefs<br>
+ * 0x09 - add Data Mode and Pixel Packing<br>
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param pParam - pointer to the current scan parameters
+ * @return - Nothing
+ */
+static void
+usb_SetColorAndBits( Plustek_Device *dev, ScanParam *pParam )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* Set pixel packing, data mode and AFE operation */
+ switch( pParam->bDataType ) {
+ case SCANDATATYPE_Color:
+ m_bCM = 3;
+ regs[0x26] = hw->bReg_0x26 & 0x7;
+
+ /* if set to one channel color, we select the blue channel
+ * as input source, this is the default, but I don't know
+ * what happens, if we deselect this
+ */
+ if( regs[0x26] & _ONE_CH_COLOR )
+ regs[0x26] |= (_BLUE_CH | 0x01);
+
+ memcpy( &regs[0x0f], hw->bReg_0x0f_Color, 10 );
+ break;
+
+ case SCANDATATYPE_Gray:
+ m_bCM = 1;
+ regs[0x26] = (hw->bReg_0x26 & 0x18) | 0x04;
+ memcpy( &regs[0x0f], hw->bReg_0x0f_Mono, 10 );
+ break;
+
+ case SCANDATATYPE_BW:
+ m_bCM = 1;
+ regs[0x26] = (hw->bReg_0x26 & 0x18) | 0x04;
+ memcpy( &regs[0x0f], hw->bReg_0x0f_Mono, 10 );
+ break;
+ }
+
+ regs[0x27] = hw->bReg_0x27;
+
+ if( pParam->bBitDepth > 8 ) {
+ regs[0x09] |= 0x20; /* 14/16bit image data */
+
+ } else if( pParam->bBitDepth == 8 ) {
+ regs[0x09] |= 0x18; /* 8bits/per pixel */
+ }
+}
+
+/**
+ * Data output from NS983X should be times of 2-byte and every line
+ * will append 2 status bytes
+ */
+static void
+usb_GetPhyPixels( Plustek_Device *dev, ScanParam *sp )
+{
+ sp->Size.dwValidPixels = sp->Size.dwPixels * sp->PhyDpi.x / sp->UserDpi.x;
+
+ if (sp->bBitDepth == 1) {
+
+ /* Pixels should be times of 16 */
+ sp->Size.dwPhyPixels =
+ (sp->Size.dwValidPixels + 15UL) & 0xfffffff0UL;
+ sp->Size.dwPhyBytes = sp->Size.dwPhyPixels / 8UL + 2UL;
+
+ } else if (sp->bBitDepth == 8) {
+
+ /* Pixels should be times of 2 */
+ sp->Size.dwPhyPixels = (sp->Size.dwValidPixels + 1UL) & 0xfffffffeUL;
+ sp->Size.dwPhyBytes = sp->Size.dwPhyPixels * sp->bChannels + 2UL;
+
+ /* need to be adjusted fo CIS devices in color mode */
+ if(usb_IsCISDevice( dev ) && (sp->bDataType == SCANDATATYPE_Color)) {
+ sp->Size.dwPhyBytes *= 3;
+ }
+ }
+ else /* sp->bBitDepth == 16 */
+ {
+ sp->Size.dwPhyPixels = sp->Size.dwValidPixels;
+ sp->Size.dwPhyBytes = sp->Size.dwPhyPixels * 2 * sp->bChannels + 2UL;
+
+ /* need to be adjusted fo CIS devices in color mode */
+ if(usb_IsCISDevice( dev ) && (sp->bDataType == SCANDATATYPE_Color)) {
+ sp->Size.dwPhyBytes *= 3;
+ }
+ }
+}
+
+/** Calculate basic image settings like the number of physical bytes per line
+ * etc...
+ * Affected registers:<br>
+ * 0x22/0x23 - Data Pixels Start<br>
+ * 0x24/0x25 - Data Pixels End<br>
+ * 0x4a/0x4b - Full Steps to Skip at Start of Scan
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param pParam - pointer to the current scan parameters
+ * @return - Nothing
+ */
+static void
+usb_GetScanRect( Plustek_Device *dev, ScanParam *sp )
+{
+ u_short wDataPixelStart, wLineEnd;
+
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* Convert pixels to physical dpi based */
+ usb_GetPhyPixels( dev, sp );
+
+ /* Compute data start pixel */
+#if 0
+/* HEINER: check ADF stuff... */
+ if(sp->bCalibration != PARAM_Gain &&
+ sp->bCalibration != PARAM_Offset && ScanInf.m_fADF)
+ wDataPixelStart = 2550 * sCaps->OpticDpi.x / 300UL -
+ (u_short)(m_dHDPIDivider * sp->Size.dwValidPixels + 0.5);
+ else
+#endif
+ wDataPixelStart = (u_short)((u_long)sp->Origin.x *
+ sCaps->OpticDpi.x / 300UL);
+
+ /* during the calibration steps, we read the entire CCD data
+ */
+ if((sp->bCalibration != PARAM_Gain)&&(sp->bCalibration != PARAM_Offset)) {
+/* HEINER: check ADF stuff... */
+#if 0
+ if(ScanInf.m_fADF) {
+ wDataPixelStart = 2550 * sCaps->OpticDpi.x / 300UL -
+ (u_short)(m_dHDPIDivider * sp->Size.dwValidPixels + 0.5);
+ }
+#endif
+ wDataPixelStart += hw->wActivePixelsStart;
+ }
+
+ wLineEnd = wDataPixelStart + (u_short)(m_dHDPIDivider *
+ sp->Size.dwPhyPixels + 0.5);
+
+ DBG( _DBG_INFO2, "* DataPixelStart=%u, LineEnd=%u\n",
+ wDataPixelStart, wLineEnd );
+ if( wDataPixelStart & 1 ) {
+
+ wDataPixelStart++;
+ wLineEnd++;
+
+ DBG( _DBG_INFO2, "* DataPixelStart=%u, LineEnd=%u (ADJ)\n",
+ wDataPixelStart, wLineEnd );
+ }
+
+ regs[0x22] = _HIBYTE( wDataPixelStart );
+ regs[0x23] = _LOBYTE( wDataPixelStart );
+
+ /* should match: wLineEnd-wDataPixelStart%(m_dHDPIDivider*2) = 0!! */
+ regs[0x24] = _HIBYTE( wLineEnd );
+ regs[0x25] = _LOBYTE( wLineEnd );
+
+ DBG( _DBG_INFO2, ">> End-Start=%u, HDPI=%.2f\n",
+ wLineEnd-wDataPixelStart, m_dHDPIDivider);
+
+ /* Y origin */
+ if( sp->bCalibration == PARAM_Scan ) {
+
+ if( hw->motorModel == MODEL_Tokyo600 ) {
+
+ if(sp->PhyDpi.x <= 75)
+ sp->Origin.y += 20;
+ else if(sp->PhyDpi.x <= 100)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y += 0;
+ else
+ sp->Origin.y -= 6;
+ }
+ else if(sp->PhyDpi.x <= 150)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y -= 0;
+ }
+ else if(sp->PhyDpi.x <= 200)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y -= 10;
+ else
+ sp->Origin.y -= 4;
+ }
+ else if(sp->PhyDpi.x <= 300)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y += 16;
+ else
+ sp->Origin.y -= 18;
+ }
+ else if(sp->PhyDpi.x <= 400)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y += 15;
+ else if(sp->bDataType == SCANDATATYPE_BW)
+ sp->Origin.y += 4;
+ }
+ else /* if(sp->PhyDpi.x <= 600) */
+ {
+ if (sp->bDataType == SCANDATATYPE_Gray)
+ sp->Origin.y += 4;
+ }
+ }
+
+ /* Add gray mode offset (Green offset, we assume the CCD are
+ * always be RGB or BGR order).
+ */
+ if (sp->bDataType != SCANDATATYPE_Color)
+ sp->Origin.y += (u_long)(300UL *
+ sCaps->bSensorDistance / sCaps->OpticDpi.y);
+ }
+
+ sp->Origin.y=(u_short)((u_long)sp->Origin.y * hw->wMotorDpi/300UL);
+
+ /* Something wrong, but I can not find it. */
+ if( hw->motorModel == MODEL_HuaLien && sCaps->OpticDpi.x == 600)
+ sp->Origin.y = sp->Origin.y * 297 / 298;
+
+ DBG(_DBG_INFO2,"* Full Steps to Skip at Start = 0x%04x\n",
+ sp->Origin.y);
+
+ regs[0x4a] = _HIBYTE( sp->Origin.y );
+ regs[0x4b] = _LOBYTE( sp->Origin.y );
+}
+
+/** preset scan stepsize and fastfeed stepsize
+ */
+static void
+usb_PresetStepSize( Plustek_Device *dev, ScanParam *pParam )
+{
+ u_short ssize;
+ double mclkdiv = pParam->dMCLK;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ ssize = (u_short)((double)CRYSTAL_FREQ / ( mclkdiv * 8.0 *
+ (double)m_bCM * hw->dMaxMotorSpeed * 4.0 * (double)hw->wMotorDpi));
+
+ regs[0x46] = _HIBYTE( ssize );
+ regs[0x47] = _LOBYTE( ssize );
+ regs[0x48] = _HIBYTE( ssize );
+ regs[0x49] = _LOBYTE( ssize );
+
+ DBG( _DBG_INFO2, "* StepSize(Preset) = %u (0x%04x)\n", ssize, ssize );
+}
+
+/** calculate default phase difference DPD
+ */
+static void
+usb_GetDPD( Plustek_Device *dev )
+{
+ int qtcnt; /* quarter speed count count reg 51 b2..3 */
+ int hfcnt; /* half speed count reg 51 b0..1 */
+ int strev; /* steps to reverse reg 50 */
+ int dpd; /* calculated dpd reg 52:53 */
+ int st; /* step size reg 46:47 */
+
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ qtcnt = (regs[0x51] & 0x30) >> 4; /* quarter speed count */
+ hfcnt = (regs[0x51] & 0xc0) >> 6; /* half speed count */
+
+ if( _LM9831 == hw->chip )
+ strev = regs[0x50] & 0x3f; /* steps to reverse */
+ else /* LM9832/3 */
+ {
+ if (qtcnt == 3)
+ qtcnt = 8;
+ if (hfcnt == 3)
+ hfcnt = 8;
+ strev = regs[0x50]; /* steps to reverse */
+ }
+
+ st = regs[0x46] * 256 + regs[0x47]; /* scan step size */
+
+ if (m_wLineLength == 0)
+ dpd = 0;
+ else
+ {
+ dpd = (((qtcnt * 4) + (hfcnt * 2) + strev) * 4 * st) %
+ (m_wLineLength * m_bLineRateColor);
+ DBG( _DBG_INFO2, "* DPD =%u (0x%04x)\n", dpd, dpd );
+ dpd = m_wLineLength * m_bLineRateColor - dpd;
+ }
+
+ DBG( _DBG_INFO2, "* DPD =%u (0x%04x), step size=%u, steps2rev=%u\n",
+ dpd, dpd, st, strev);
+ DBG( _DBG_INFO2, "* llen=%u, lineRateColor=%u, qtcnt=%u, hfcnt=%u\n",
+ m_wLineLength, m_bLineRateColor, qtcnt, hfcnt );
+
+ regs[0x51] |= (u_char)((dpd >> 16) & 0x03);
+ regs[0x52] = (u_char)(dpd >> 8);
+ regs[0x53] = (u_char)(dpd & 0xFF);
+}
+
+#define MCLKDIV_SCALING 2
+#define _MIN(a,b) ((a) < (b) ? (a) : (b))
+#define _MAX(a,b) ((a) > (b) ? (a) : (b))
+
+/**
+ */
+static int
+usb_GetMCLKDiv( Plustek_Device *dev )
+{
+ int j, pixelbits, pixelsperline, r;
+ int minmclk, maxmclk, mclkdiv;
+ double hdpi, min_int_time;
+ u_char *regs = dev->usbDev.a_bRegs;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ DBG( _DBG_INFO, "usb_GetMCLKDiv()\n" );
+
+ r = 8; /* line rate */
+ if ((regs[0x26] & 7) == 0)
+ r = 24; /* pixel rate */
+
+ /* use high or low res min integration time */
+ min_int_time = ((regs[0x9]&7) > 2 ? hw->dMinIntegrationTimeLowres:
+ hw->dMinIntegrationTimeHighres);
+
+ minmclk = (int)ceil((double) MCLKDIV_SCALING * CRYSTAL_FREQ *
+ min_int_time /((double)1000. * r * m_wLineLength));
+ minmclk = _MAX(minmclk,MCLKDIV_SCALING);
+
+ maxmclk = (int)(32.5*MCLKDIV_SCALING + .5);
+
+ DBG(_DBG_INFO2,"- lower mclkdiv limit=%f\n",(double)minmclk/MCLKDIV_SCALING);
+ DBG(_DBG_INFO2,"- upper mclkdiv limit=%f\n",(double)maxmclk/MCLKDIV_SCALING);
+
+ /* get the bits per pixel */
+ switch (regs[0x9] & 0x38) {
+ case 0: pixelbits=1; break;
+ case 0x8: pixelbits=2; break;
+ case 0x10: pixelbits=4; break;
+ case 0x18: pixelbits=8; break;
+ default: pixelbits=16;break;
+ }
+
+ /* compute the horizontal dpi (pixels per inch) */
+ j = regs[0x9] & 0x7;
+ hdpi = ((j&1)*.5+1)*(j&2?2:1)*(j&4?4:1);
+
+ pixelsperline = (int)((256*regs[0x24]+regs[0x25]-256*regs[0x22]-regs[0x23])
+ *pixelbits/(hdpi * 8));
+ mclkdiv = (int)ceil((double)MCLKDIV_SCALING * pixelsperline * CRYSTAL_FREQ
+ /((double) 8. * m_wLineLength * dev->transferRate));
+
+ DBG( _DBG_INFO2, "- hdpi = %.3f\n", hdpi );
+ DBG( _DBG_INFO2, "- pixelbits = %u\n", pixelbits );
+ DBG( _DBG_INFO2, "- pixelsperline = %u\n", pixelsperline );
+ DBG( _DBG_INFO2, "- linelen = %u\n", m_wLineLength );
+ DBG( _DBG_INFO2, "- transferrate = %lu\n", dev->transferRate );
+ DBG( _DBG_INFO2, "- MCLK Divider = %u\n", mclkdiv/MCLKDIV_SCALING );
+
+ mclkdiv = _MAX(mclkdiv,minmclk);
+ mclkdiv = _MIN(mclkdiv,maxmclk);
+ DBG( _DBG_INFO2, "- Current MCLK Divider = %u\n", mclkdiv/MCLKDIV_SCALING );
+
+ if( dev->transferRate == 2000000 ) {
+ while (mclkdiv * hdpi < 6.*MCLKDIV_SCALING) {
+ mclkdiv++;
+ }
+ DBG( _DBG_INFO2, "- HIGHSPEED MCLK Divider = %u\n",
+ mclkdiv/MCLKDIV_SCALING );
+ }
+
+ return mclkdiv;
+}
+
+/** Plusteks' poor-man MCLK calculation...
+ * at least we give the master clock divider and adjust the step size
+ * and integration time (for 14/16 bit modes)
+ */
+static double
+usb_GetMCLKDivider( Plustek_Device *dev, ScanParam *pParam )
+{
+ double dMaxIntegrationTime;
+ double dMaxMCLKDivider;
+
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "usb_GetMCLKDivider()\n" );
+
+ if( dev->transferRate == 2000000 ) {
+ int mclkdiv = usb_GetMCLKDiv(dev);
+ pParam->dMCLK = (double)mclkdiv/MCLKDIV_SCALING;
+ }
+
+ m_dMCLKDivider = pParam->dMCLK;
+
+ if (m_dHDPIDivider*m_dMCLKDivider >= 5.3/*6*/)
+ m_bIntTimeAdjust = 0;
+ else
+ m_bIntTimeAdjust = ceil( 5.3/*6.0*/ / (m_dHDPIDivider*m_dMCLKDivider));
+
+ if( pParam->bCalibration == PARAM_Scan ) {
+
+ usb_GetMCLKDiv(dev);
+
+ /* Compare Integration with USB speed to find the best ITA value */
+ if( pParam->bBitDepth > 8 ) {
+
+ while( pParam->Size.dwPhyBytes >
+ (m_dMCLKDivider * m_bCM * m_wLineLength / 6 * 9 / 10) *
+ (1 + m_bIntTimeAdjust)) {
+ m_bIntTimeAdjust++;
+ }
+
+ if( hw->motorModel == MODEL_HuaLien &&
+ sCaps->bCCD == kNEC3799 && m_bIntTimeAdjust > bMaxITA) {
+ m_bIntTimeAdjust = bMaxITA;
+ }
+
+ if(((hw->motorModel == MODEL_HP) && (sCaps->bCCD == kNECSLIM))/* ||
+ ( regs[0x26] & _ONE_CH_COLOR )*/) {
+
+ bMaxITA = (u_char)floor((m_dMCLKDivider + 1) / 2.0);
+
+ DBG( _DBG_INFO2, "* MaxITA (HP) = %u\n", bMaxITA );
+ if( m_bIntTimeAdjust > bMaxITA ) {
+ DBG( _DBG_INFO, "* ITA (%u) limited\n", m_bIntTimeAdjust );
+ m_bIntTimeAdjust = bMaxITA;
+ }
+ }
+ }
+ }
+ DBG( _DBG_INFO2, "* Integration Time Adjust = %u (HDPI=%.3f,MCLKD=%.3f)\n",
+ m_bIntTimeAdjust, m_dHDPIDivider, m_dMCLKDivider );
+
+ regs[0x08] = (u_char)((m_dMCLKDivider - 1) * 2);
+ regs[0x19] = m_bIntTimeAdjust;
+
+ if( m_bIntTimeAdjust != 0 ) {
+
+ m_wStepSize = (u_short)((u_long) m_wStepSize *
+ (m_bIntTimeAdjust + 1) / m_bIntTimeAdjust);
+ if( m_wStepSize < 2 )
+ m_wStepSize = 2;
+
+ regs[0x46] = _HIBYTE(m_wStepSize);
+ regs[0x47] = _LOBYTE(m_wStepSize);
+
+ DBG( _DBG_INFO2, "* Stepsize = %u, 0x46=0x%02x 0x47=0x%02x\n",
+ m_wStepSize, regs[0x46], regs[0x47] );
+ usb_GetDPD( dev );
+ }
+
+ /* Compute maximum MCLK divider base on maximum integration time for
+ * high lamp PWM, use equation 4
+ */
+ dMaxIntegrationTime = hw->dIntegrationTimeHighLamp;
+ dMaxMCLKDivider = (double)CRYSTAL_FREQ * dMaxIntegrationTime /
+ (1000 * 8 * m_bCM * m_wLineLength);
+
+ /* Determine lamp PWM setting */
+ if( m_dMCLKDivider > dMaxMCLKDivider ) {
+
+ DBG( _DBG_INFO2, "* Setting GreenPWMDutyCycleLow\n" );
+ regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleLow );
+ regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleLow );
+
+ } else {
+
+ DBG( _DBG_INFO2, "* Setting GreenPWMDutyCycleHigh\n" );
+ regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleHigh );
+ regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleHigh );
+ }
+
+ DBG( _DBG_INFO2, "* Current MCLK Divider = %f\n", m_dMCLKDivider );
+ return m_dMCLKDivider;
+}
+
+/** calculate the step size of each scan step
+ */
+static void
+usb_GetStepSize( Plustek_Device *dev, ScanParam *pParam )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* Compute step size using equation 1 */
+ if (m_bIntTimeAdjust != 0) {
+ m_wStepSize = (u_short)(((u_long) pParam->PhyDpi.y * m_wLineLength *
+ m_bLineRateColor * (m_bIntTimeAdjust + 1)) /
+ (4 * hw->wMotorDpi * m_bIntTimeAdjust));
+ } else {
+ m_wStepSize = (u_short)(((u_long) pParam->PhyDpi.y * m_wLineLength *
+ m_bLineRateColor) / (4 * hw->wMotorDpi));
+ }
+
+ if (m_wStepSize < 2)
+ m_wStepSize = 2;
+
+ m_wStepSize = m_wStepSize * 298 / 297;
+
+ regs[0x46] = _HIBYTE( m_wStepSize );
+ regs[0x47] = _LOBYTE( m_wStepSize );
+
+ DBG( _DBG_INFO2, "* Stepsize = %u, 0x46=0x%02x 0x47=0x%02x\n",
+ m_wStepSize, regs[0x46], regs[0x47] );
+}
+
+/**
+ */
+static void
+usb_GetLineLength( Plustek_Device *dev, ScanParam *param )
+{
+/* [note]
+ * The ITA in this moment is always 0, it will be changed later when we
+ * calculate MCLK. This is very strange why this routine will not call
+ * again to get all new value after ITA was changed? If this routine
+ * never call again, maybe we remove all factor with ITA here.
+ */
+ int tr;
+ int tpspd; /* turbo/preview mode speed reg 0a b2..3 */
+ int tpsel; /* turbo/preview mode select reg 0a b0..1 */
+ int gbnd; /* guardband duration reg 0e b4..7 */
+ int dur; /* pulse duration reg 0e b0..3 */
+ int ntr; /* number of tr pulses reg 0d b7 */
+ int afeop; /* scan mode, 0=pixel rate, 1=line rate, */
+ /* 4=1 channel mode a, 5=1 channel mode b, reg 26 b0..2 */
+ int ctmode; /* CIS tr timing mode reg 19 b0..1 */
+ int tp; /* tpspd or 1 if tpsel=0 */
+ int b; /* if ctmode=0, (ntr+1)*((2*gbnd)+dur+1), otherwise 1 */
+ int tradj; /* ITA */
+ int en_tradj;
+ u_short le;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ ClkMotorDef *motor = usb_GetMotorSet( hw->motorModel );
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ tpspd = (regs[0x0a] & 0x0c) >> 2; /* turbo/preview mode speed */
+ tpsel = regs[0x0a] & 3; /* turbo/preview mode select */
+
+ gbnd = (regs[0x0e] & 0xf0) >> 4; /* TR fi1 guardband duration */
+ dur = (regs[0x0e] & 0xf); /* TR pulse duration */
+
+ ntr = regs[0x0d] / 128; /* number of tr pulses */
+
+ afeop = regs[0x26] & 7; /* afe op - 3 channel or 1 channel */
+
+ tradj = regs[0x19] & 0x7f; /* integration time adjust */
+ en_tradj = (tradj) ? 1 : 0;
+
+ ctmode = (regs[0x0b] >> 3) & 3; /* cis tr timing mode */
+
+ m_bLineRateColor = 1;
+ if (afeop == 1 || afeop == 5) /* if 3 channel line or 1 channel mode b */
+ m_bLineRateColor = 3;
+
+ /* according to turbo/preview mode to set value */
+ if( tpsel == 0 ) {
+ tp = 1;
+ } else {
+ tp = tpspd + 2;
+ if( tp == 5 )
+ tp++;
+ }
+
+ b = 1;
+ if( ctmode == 0 ) { /* CCD mode scanner*/
+
+ b = (ntr + 1) * ((2 * gbnd) + dur + 1);
+ b += (1 - ntr) * en_tradj;
+ }
+ if( ctmode == 2 ) /* CIS mode scanner */
+ b = 3;
+
+ /* it might be necessary to tweak this value, to keep the
+ * MCLK constant - see some sheet-fed devices
+ */
+ le = hw->wLineEnd;
+ if (motor->dpi_thresh != 0) {
+ if( param->PhyDpi.y <= motor->dpi_thresh) {
+ le = motor->lineend;
+ DBG( _DBG_INFO2, "* Adjusting lineend: %u\n", le);
+ }
+ regs[0x20] = _HIBYTE( le );
+ regs[0x21] = _LOBYTE( le );
+ }
+
+ tr = m_bLineRateColor * (le + tp * (b + 3 - ntr));
+
+ if( tradj == 0 ) {
+ if( ctmode == 0 )
+ tr += m_bLineRateColor;
+ } else {
+
+ int le_phi, num_byteclk, num_mclkf, tr_fast_pix, extra_pix;
+
+ /* Line color or gray mode */
+ if( afeop != 0 ) {
+
+ le_phi = (tradj + 1) / 2 + 1 + 6;
+ num_byteclk = ((le_phi + 8 * le + 8 * b + 4) /
+ (8 * tradj)) + 1;
+ num_mclkf = 8 * tradj * num_byteclk;
+ tr_fast_pix = num_byteclk;
+ extra_pix = (num_mclkf - le_phi) % 8;
+ }
+ else /* 3 channel pixel rate color */
+ {
+ le_phi = (tradj + 1) / 2 + 1 + 10 + 12;
+ num_byteclk = ((le_phi + 3 * 8 * le + 3 * 8 * b + 3 * 4) /
+ (3 * 8 * tradj)) + 1;
+ num_mclkf = 3 * 8 * tradj * num_byteclk;
+ tr_fast_pix = num_byteclk;
+ extra_pix = (num_mclkf - le_phi) % (3 * 8);
+ }
+
+ tr = b + le + 4 + tr_fast_pix;
+ if (extra_pix == 0)
+ tr++;
+ tr *= m_bLineRateColor;
+ }
+ m_wLineLength = tr / m_bLineRateColor;
+
+ DBG( _DBG_INFO2, "* LineLength=%d, LineRateColor=%u\n",
+ m_wLineLength, m_bLineRateColor );
+}
+
+/** usb_GetMotorParam
+ * registers 0x56, 0x57
+ */
+static void
+usb_GetMotorParam( Plustek_Device *dev, ScanParam *pParam )
+{
+ int idx, i;
+ ClkMotorDef *clk;
+ MDef *md;
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if (!_IS_PLUSTEKMOTOR(hw->motorModel)) {
+
+ clk = usb_GetMotorSet( hw->motorModel );
+ md = clk->motor_sets;
+ idx = 0;
+ for( i = 0; i < _MAX_CLK; i++ ) {
+ if( pParam->PhyDpi.y <= dpi_ranges[i] )
+ break;
+ idx++;
+ }
+ if( idx >= _MAX_CLK )
+ idx = _MAX_CLK - 1;
+
+ regs[0x56] = md[idx].pwm;
+ regs[0x57] = md[idx].pwm_duty;
+
+ regs[0x43] = 0;
+ regs[0x44] = 0;
+
+ if( md[idx].scan_lines_per_line > 1 ) {
+
+ if((pParam->bBitDepth > 8) &&
+ (pParam->bDataType == SCANDATATYPE_Color)) {
+
+ regs[0x43] = 0xff;
+ regs[0x44] = md[idx].scan_lines_per_line;
+
+ DBG( _DBG_INFO2, "* Line Skipping : 0x43=0x%02x, 0x44=0x%02x\n",
+ regs[0x43], regs[0x44] );
+ }
+ }
+ } else {
+
+ if( sCaps->OpticDpi.x == 1200 ) {
+
+ switch( hw->motorModel ) {
+
+ case MODEL_HuaLien:
+ case MODEL_KaoHsiung:
+ default:
+ if(pParam->PhyDpi.x <= 200)
+ {
+ regs[0x56] = 1;
+ regs[0x57] = 48; /* 63; */
+ }
+ else if(pParam->PhyDpi.x <= 300)
+ {
+ regs[0x56] = 2; /* 8; */
+ regs[0x57] = 48; /* 56; */
+ }
+ else if(pParam->PhyDpi.x <= 400)
+ {
+ regs[0x56] = 8;
+ regs[0x57] = 48;
+ }
+ else if(pParam->PhyDpi.x <= 600)
+ {
+ regs[0x56] = 2; /* 10; */
+ regs[0x57] = 48; /* 56; */
+ }
+ else /* pParam->PhyDpi.x == 1200) */
+ {
+ regs[0x56] = 1; /* 8; */
+ regs[0x57] = 48; /* 56; */
+ }
+ break;
+ }
+ } else {
+ switch ( hw->motorModel ) {
+
+ case MODEL_Tokyo600:
+ regs[0x56] = 16;
+ regs[0x57] = 4; /* 2; */
+ break;
+ case MODEL_HuaLien:
+ {
+ if(pParam->PhyDpi.x <= 200)
+ {
+ regs[0x56] = 64; /* 24; */
+ regs[0x57] = 4; /* 16; */
+ }
+ else if(pParam->PhyDpi.x <= 300)
+ {
+ regs[0x56] = 64; /* 16; */
+ regs[0x57] = 4; /* 16; */
+ }
+ else if(pParam->PhyDpi.x <= 400)
+ {
+ regs[0x56] = 64; /* 16; */
+ regs[0x57] = 4; /* 16; */
+ }
+ else /* if(pParam->PhyDpi.x <= 600) */
+ {
+/* HEINER: check ADF stuff... */
+#if 0
+ if(ScanInf.m_fADF)
+ {
+ regs[0x56] = 8;
+ regs[0x57] = 48;
+ }
+ else
+#endif
+ {
+ regs[0x56] = 64; /* 2; */
+ regs[0x57] = 4; /* 48; */
+ }
+ }
+ }
+ break;
+ case MODEL_KaoHsiung:
+ default:
+ if(pParam->PhyDpi.x <= 200)
+ {
+ regs[0x56] = 24;
+ regs[0x57] = 16;
+ }
+ else if(pParam->PhyDpi.x <= 300)
+ {
+ regs[0x56] = 16;
+ regs[0x57] = 16;
+ }
+ else if(pParam->PhyDpi.x <= 400)
+ {
+ regs[0x56] = 16;
+ regs[0x57] = 16;
+ }
+ else /* if(pParam->PhyDpi.x <= 600) */
+ {
+ regs[0x56] = 2;
+ regs[0x57] = 48;
+ }
+ break;
+ }
+ }
+ }
+
+ DBG( _DBG_INFO2, "* MOTOR-Settings: PWM=0x%02x, PWM_DUTY=0x%02x\n",
+ regs[0x56], regs[0x57] );
+}
+
+/**
+ */
+static void
+usb_GetPauseLimit( Plustek_Device *dev, ScanParam *pParam )
+{
+ int coeffsize, scaler;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ scaler = 1;
+ if( hw->bReg_0x26 & _ONE_CH_COLOR ) {
+ if( pParam->bDataType == SCANDATATYPE_Color ) {
+ scaler = 3;
+ }
+ }
+
+ /* compute size of coefficient ram */
+ coeffsize = 4 + 16 + 16; /* gamma and shading and offset */
+
+ /* if 16 bit, then not all is used */
+ if( regs[0x09] & 0x20 ) {
+ coeffsize = 16 + 16; /* no gamma */
+ }
+ coeffsize *= (2*3); /* 3 colors and 2 bytes/word */
+
+
+ /* Get available buffer size in KB
+ * for 512kb this will be 296
+ * for 2Mb this will be 1832
+ */
+ m_dwPauseLimit = (u_long)(hw->wDRAMSize - (u_long)(coeffsize));
+ m_dwPauseLimit -= ((pParam->Size.dwPhyBytes*scaler) / 1024 + 1);
+
+ /* If not reversing, take into account the steps to reverse */
+ if( regs[0x50] == 0 )
+ m_dwPauseLimit -= ((regs[0x54] & 7) *
+ (pParam->Size.dwPhyBytes * scaler) + 1023) / 1024;
+
+ DBG( _DBG_INFO2, "* PL=%lu, coeffsize=%u, scaler=%u\n",
+ m_dwPauseLimit, coeffsize, scaler );
+
+ m_dwPauseLimit = usb_max( usb_min(m_dwPauseLimit,
+ (u_long)ceil(pParam->Size.dwTotalBytes / 1024.0)), 2);
+
+ regs[0x4e] = (u_char)floor((m_dwPauseLimit*512.0)/(2.0*hw->wDRAMSize));
+
+ if( regs[0x4e] > 1 ) {
+ regs[0x4e]--;
+ if(regs[0x4e] > 1)
+ regs[0x4e]--;
+ } else
+ regs[0x4e] = 1;
+
+ /* resume, when buffer is 2/8 kbytes full (512k/2M memory)
+ */
+ regs[0x4f] = 1;
+
+ DBG( _DBG_INFO2, "* PauseLimit = %lu, [0x4e] = 0x%02x, [0x4f] = 0x%02x\n",
+ m_dwPauseLimit, regs[0x4e], regs[0x4f] );
+}
+
+/** usb_GetScanLinesAndSize
+ */
+static void
+usb_GetScanLinesAndSize( Plustek_Device *dev, ScanParam *pParam )
+{
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+
+ pParam->Size.dwPhyLines = (u_long)ceil((double) pParam->Size.dwLines *
+ pParam->PhyDpi.y / pParam->UserDpi.y);
+
+ /* Calculate color offset */
+ if (pParam->bCalibration == PARAM_Scan && pParam->bChannels == 3) {
+
+ dev->scanning.bLineDistance = sCaps->bSensorDistance *
+ pParam->PhyDpi.y / sCaps->OpticDpi.x;
+ pParam->Size.dwPhyLines += (dev->scanning.bLineDistance << 1);
+ }
+ else
+ dev->scanning.bLineDistance = 0;
+
+ pParam->Size.dwTotalBytes = pParam->Size.dwPhyBytes * pParam->Size.dwPhyLines;
+
+ DBG( _DBG_INFO, "* PhyBytes = %lu\n", pParam->Size.dwPhyBytes );
+ DBG( _DBG_INFO, "* PhyLines = %lu\n", pParam->Size.dwPhyLines );
+ DBG( _DBG_INFO, "* TotalBytes = %lu\n", pParam->Size.dwTotalBytes );
+}
+
+/** function to preset/reset the merlin registers
+ */
+static SANE_Bool
+usb_SetScanParameters( Plustek_Device *dev, ScanParam *pParam )
+{
+ static u_char reg8, reg38[6], reg48[2];
+
+ ScanDef *scan = &dev->scanning;
+ ScanParam *pdParam = &dev->scanning.sParam;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ m_pParam = pParam;
+
+ DBG( _DBG_INFO, "usb_SetScanParameters()\n" );
+
+ if( !usb_IsScannerReady(dev))
+ return SANE_FALSE;
+
+ if(pParam->bCalibration == PARAM_Scan && pParam->bSource == SOURCE_ADF) {
+/* HEINER: dSaveMoveSpeed is only used in func EjectPaper!!!
+ dSaveMoveSpeed = hw->dMaxMoveSpeed;
+*/
+ hw->dMaxMoveSpeed = 1.0;
+ usb_MotorSelect( dev, SANE_TRUE );
+ usb_MotorOn( dev, SANE_TRUE );
+ }
+
+ /*
+ * calculate the basic settings...
+ */
+ pParam->PhyDpi.x = usb_SetAsicDpiX( dev, pParam->UserDpi.x );
+ pParam->PhyDpi.y = usb_SetAsicDpiY( dev, pParam->UserDpi.y );
+
+ usb_SetColorAndBits( dev, pParam );
+ usb_GetScanRect ( dev, pParam );
+
+ usb_PresetStepSize( dev, pParam );
+
+ if( dev->caps.dwFlag & SFLAG_ADF ) {
+
+ if( pParam->bCalibration == PARAM_Scan ) {
+
+ if( pdParam->bSource == SOURCE_ADF ) {
+ regs[0x50] = 0;
+ regs[0x51] = 0x40;
+ if( pParam->PhyDpi.x <= 300)
+ regs[0x54] = (regs[0x54] & ~7) | 4; /* 3; */
+ else
+ regs[0x54] = (regs[0x54] & ~7) | 5; /* 4; */
+ } else {
+ regs[0x50] = hw->bStepsToReverse;
+ regs[0x51] = hw->bReg_0x51;
+ regs[0x54] &= ~7;
+ }
+ } else
+ regs[0x50] = 0;
+ } else {
+ if( pParam->bCalibration == PARAM_Scan )
+ regs[0x50] = hw->bStepsToReverse;
+ else
+ regs[0x50] = 0;
+ }
+
+ /* Assume we will not use ITA */
+ regs[0x19] = m_bIntTimeAdjust = 0;
+
+ /* Get variables from calculation algorithms */
+ if(!(pParam->bCalibration == PARAM_Scan &&
+ pParam->bSource == SOURCE_ADF && dev->usbDev.fLastScanIsAdf )) {
+
+ DBG( _DBG_INFO2, "* Scan calculations...\n" );
+ usb_GetLineLength ( dev, pParam );
+ usb_GetStepSize ( dev, pParam );
+ usb_GetDPD ( dev );
+ usb_GetMCLKDivider( dev, pParam );
+ usb_GetMotorParam ( dev, pParam );
+ }
+
+ /* Compute fast feed step size, use equation 3 and equation 8 */
+ if( m_dMCLKDivider < 1.0)
+ m_dMCLKDivider = 1.0;
+
+ m_wFastFeedStepSize = (u_short)(CRYSTAL_FREQ /
+ (m_dMCLKDivider * 8 * m_bCM * hw->dMaxMoveSpeed *
+ 4 * hw->wMotorDpi));
+ /* CIS special ;-) */
+ if((hw->bReg_0x26 & _ONE_CH_COLOR) && (m_bCM == 1)) {
+ DBG( _DBG_INFO2, "* CIS FFStep-Speedup\n" );
+ m_wFastFeedStepSize /= 3;
+ }
+
+ if( m_bIntTimeAdjust != 0 )
+ m_wFastFeedStepSize /= m_bIntTimeAdjust;
+
+ if(regs[0x0a])
+ m_wFastFeedStepSize *= ((regs[0x0a] >> 2) + 2);
+ regs[0x48] = _HIBYTE( m_wFastFeedStepSize );
+ regs[0x49] = _LOBYTE( m_wFastFeedStepSize );
+
+ DBG( _DBG_INFO2, "* FFStepSize = %u, [0x48] = 0x%02x, [0x49] = 0x%02x\n",
+ m_wFastFeedStepSize, regs[0x48], regs[0x49] );
+
+ /* Compute the number of lines to scan using actual Y resolution */
+ usb_GetScanLinesAndSize( dev, pParam );
+
+ /* Pause limit should be bounded by total bytes to read
+ * so that the chassis will not move too far.
+ */
+ usb_GetPauseLimit( dev, pParam );
+
+ /* For ADF .... */
+ if(pParam->bCalibration == PARAM_Scan && pParam->bSource == SOURCE_ADF) {
+
+ if( dev->usbDev.fLastScanIsAdf ) {
+
+ regs[0x08] = reg8;
+ memcpy( &regs[0x38], reg38, sizeof(reg38));
+ memcpy( &regs[0x48], reg48, sizeof(reg48));
+
+ } else {
+
+ reg8 = regs[0x08];
+ memcpy( reg38, &regs[0x38], sizeof(reg38));
+ memcpy( reg48, &regs[0x48], sizeof(reg48));
+ }
+ usb_MotorSelect( dev, SANE_TRUE );
+ }
+
+ /* Reset LM983x's state machine before setting register values */
+ if( !usbio_WriteReg( dev->fd, 0x18, 0x18 ))
+ return SANE_FALSE;
+
+ usleep(200 * 1000); /* Need to delay at least xxx microseconds */
+
+ if( !usbio_WriteReg( dev->fd, 0x07, 0x20 ))
+ return SANE_FALSE;
+
+ if( !usbio_WriteReg( dev->fd, 0x19, 6 ))
+ return SANE_FALSE;
+
+ regs[0x07] = 0;
+ regs[0x28] = 0;
+
+ /* set unused registers to 0 */
+ memset( &regs[0x03], 0, 3 );
+ memset( &regs[0x5f], 0, 0x7f-0x5f+1 );
+
+ if( !usb_IsSheetFedDevice(dev)) {
+ /* we limit the possible scansteps to avoid, that the sensors bumps
+ * against the scanbed - not necessary for sheet-fed devices
+ */
+ if(pParam->bCalibration==PARAM_Scan && pParam->bSource!=SOURCE_ADF) {
+
+ u_long lines = pParam->Size.dwPhyLines + scan->bLinesToSkip +
+ scan->dwLinesDiscard + 5;
+ u_short scansteps = (u_short)ceil((double)lines*
+ hw->wMotorDpi / pParam->PhyDpi.y);
+ DBG( _DBG_INFO, "* Scansteps=%u (%lu*%u/%u)\n", scansteps, lines,
+ hw->wMotorDpi, pParam->PhyDpi.y );
+ regs[0x4c] = _HIBYTE(scansteps);
+ regs[0x4d] = _LOBYTE(scansteps);
+ }
+ }
+
+ /* set the merlin registers */
+ _UIO(sanei_lm983x_write( dev->fd, 0x03, &regs[0x03], 3, SANE_TRUE));
+ _UIO(sanei_lm983x_write( dev->fd, 0x08, &regs[0x08], 0x7f - 0x08+1, SANE_TRUE));
+
+ usleep(100);
+
+ if( !usbio_WriteReg( dev->fd, 0x07, 0 ))
+ return SANE_FALSE;
+
+ DBG( _DBG_INFO, "usb_SetScanParameters() done.\n" );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static SANE_Bool
+usb_ScanBegin( Plustek_Device *dev, SANE_Bool auto_park )
+{
+ u_char value;
+ u_short inches;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ DCapsDef *sc = &dev->usbDev.Caps;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "usb_ScanBegin()\n" );
+
+ if( !usb_Wait4ScanSample( dev ))
+ return SANE_FALSE;
+
+ /* save the request for usb_ScanEnd () */
+ m_fAutoPark = auto_park;
+
+ /* Disable home sensor during scan, or the chassis cannot move */
+ value = ((m_pParam->bCalibration == PARAM_Scan &&
+ m_pParam->bSource == SOURCE_ADF)? (regs[0x58] & ~7): 0);
+
+ if(!usbio_WriteReg( dev->fd, 0x58, value ))
+ return SANE_FALSE;
+
+ /* Check if scanner is ready for receiving command */
+ if( !usb_IsScannerReady(dev))
+ return SANE_FALSE;
+
+ /* Flush cache - only LM9831 (Source: National Semiconductors) */
+ if( _LM9831 == hw->chip ) {
+
+ for(;;) {
+
+ if( SANE_TRUE == cancelRead ) {
+ DBG( _DBG_INFO, "ScanBegin() - Cancel detected...\n" );
+ return SANE_FALSE;
+ }
+
+ _UIO(usbio_ReadReg( dev->fd, 0x01, &m_bOldScanData ));
+ if( m_bOldScanData ) {
+
+ u_long dwBytesToRead = m_bOldScanData * hw->wDRAMSize * 4;
+ u_char *pBuffer = malloc( sizeof(u_char) * dwBytesToRead );
+
+ DBG(_DBG_INFO,"Flushing cache - %lu bytes (bOldScanData=%u)\n",
+ dwBytesToRead, m_bOldScanData );
+
+ _UIO(sanei_lm983x_read( dev->fd, 0x00, pBuffer,
+ dwBytesToRead, SANE_FALSE ));
+ free( pBuffer );
+
+ } else
+ break;
+ }
+ }
+
+ /* Download map & Shading data */
+ if(( m_pParam->bCalibration == PARAM_Scan && !usb_MapDownload( dev )) ||
+ !usb_DownloadShadingData( dev, m_pParam->bCalibration )) {
+ return SANE_FALSE;
+ }
+
+ /* Move chassis and start to read image data */
+ if (!usbio_WriteReg( dev->fd, 0x07, 3 ))
+ return SANE_FALSE;
+
+ usbio_ReadReg( dev->fd, 0x01, &m_bOldScanData );
+ m_bOldScanData = 0; /* No data at all */
+
+ m_fStart = m_fFirst = SANE_TRUE; /* Prepare to read */
+
+ DBG( _DBG_DREGS, "Register Dump before reading data:\n" );
+ dumpregs( dev->fd, NULL );
+
+ inches = (u_short)((m_pParam->Origin.y *300UL)/hw->wMotorDpi);
+ DBG( _DBG_INFO2, ">>> INCH=%u, DOY=%u\n", inches, sc->Normal.DataOrigin.y );
+ if( inches > sc->Normal.DataOrigin.y )
+ usb_WaitPos( dev, 150, SANE_FALSE );
+
+ DBG( _DBG_INFO, "usb_ScanBegin() done.\n" );
+ return SANE_TRUE;
+}
+
+/** usb_ScanEnd
+ * stop all the processing stuff and reposition sensor back home
+ */
+static SANE_Bool
+usb_ScanEnd( Plustek_Device *dev )
+{
+ u_char value;
+
+ DBG( _DBG_INFO, "usbDev_ScanEnd(), start=%u, park=%u\n",
+ m_fStart, m_fAutoPark );
+ usbio_ReadReg( dev->fd, 0x07, &value );
+ if( value == 3 || value != 2 )
+ usbio_WriteReg( dev->fd, 0x07, 0 );
+
+ if( m_fStart ) {
+ m_fStart = SANE_FALSE;
+
+ if( m_fAutoPark )
+ usb_ModuleToHome( dev, SANE_FALSE );
+ }
+ else if( SANE_TRUE == cancelRead ) {
+
+ usb_ModuleToHome( dev, SANE_FALSE );
+ }
+ return SANE_TRUE;
+}
+
+/**
+ */
+static SANE_Bool
+usb_IsDataAvailableInDRAM( Plustek_Device *dev )
+{
+ /* Compute polling timeout
+ * Height (Inches) / MaxScanSpeed (Inches/Second) = Seconds to move the
+ * module from top to bottom. Then convert the seconds to miliseconds
+ * by multiply 1000. We add extra 2 seconds to get some tolerance.
+ */
+ u_char a_bBand[3];
+ long dwTicks;
+ struct timeval t;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "usb_IsDataAvailableInDRAM()\n" );
+
+ gettimeofday( &t, NULL);
+ dwTicks = t.tv_sec + 30;
+
+ for(;;) {
+
+ _UIO( sanei_lm983x_read( dev->fd, 0x01, a_bBand, 3, SANE_FALSE ));
+
+ gettimeofday( &t, NULL);
+ if( t.tv_sec > dwTicks )
+ break;
+
+ if( usb_IsEscPressed()) {
+ DBG(_DBG_INFO,"usb_IsDataAvailableInDRAM() - Cancel detected...\n");
+ return SANE_FALSE;
+ }
+
+ /* It is not stable for read */
+ if((a_bBand[0] != a_bBand[1]) && (a_bBand[1] != a_bBand[2]))
+ continue;
+
+ if( a_bBand[0] > m_bOldScanData ) {
+
+ if( m_pParam->bSource != SOURCE_Reflection )
+
+ usleep(1000*(30 * regs[0x08] * dev->usbDev.Caps.OpticDpi.x / 600));
+ else
+ usleep(1000*(20 * regs[0x08] * dev->usbDev.Caps.OpticDpi.x / 600));
+
+ DBG( _DBG_INFO, "Data is available\n" );
+ return SANE_TRUE;
+ }
+ }
+
+ DBG( _DBG_INFO, "NO Data available\n" );
+ return SANE_FALSE;
+}
+
+/**
+ */
+static SANE_Bool
+usb_ScanReadImage( Plustek_Device *dev, void *pBuf, u_long dwSize )
+{
+ u_char *regs = dev->usbDev.a_bRegs;
+ SANE_Status res;
+
+ DBG( _DBG_READ, "usb_ScanReadImage(%lu)\n", dwSize );
+
+ if( m_fFirst ) {
+
+ m_fFirst = SANE_FALSE;
+
+ /* Wait for data band ready */
+ if (!usb_IsDataAvailableInDRAM( dev )) {
+ DBG( _DBG_ERROR, "Nothing to read...\n" );
+ return SANE_FALSE;
+ }
+
+ /* restore the fast forward stepsize...*/
+ sanei_lm983x_write(dev->fd, 0x48, &regs[0x48], 2, SANE_TRUE);
+ }
+ res = sanei_lm983x_read(dev->fd, 0x00, (u_char *)pBuf, dwSize, SANE_FALSE);
+
+ /* check for pressed ESC button, as sanei_lm983x_read() may take some time
+ */
+ if( usb_IsEscPressed()) {
+ DBG(_DBG_INFO,"usb_ScanReadImage() - Cancel detected...\n");
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_READ, "usb_ScanReadImage() done, result: %d\n", res );
+ if( SANE_STATUS_GOOD == res ) {
+ return SANE_TRUE;
+ }
+
+ DBG( _DBG_ERROR, "usb_ScanReadImage() failed\n" );
+ return SANE_FALSE;
+}
+
+/** calculate the number of pixels per line and lines out of a given
+ * crop-area. The size of the area is given on a 300dpi base!
+ */
+static void
+usb_GetImageInfo( Plustek_Device *dev, ImgDef *pInfo, WinInfo *pSize )
+{
+ DBG( _DBG_INFO, "usb_GetImageInfo()\n" );
+
+ pSize->dwPixels = (u_long)pInfo->crArea.cx * pInfo->xyDpi.x / 300UL;
+ pSize->dwLines = (u_long)pInfo->crArea.cy * pInfo->xyDpi.y / 300UL;
+
+ DBG( _DBG_INFO2,"Area: cx=%u, cy=%u\n",pInfo->crArea.cx,pInfo->crArea.cy);
+
+ switch( pInfo->wDataType ) {
+
+ case COLOR_TRUE48:
+ pSize->dwBytes = pSize->dwPixels * 6UL;
+ break;
+
+ case COLOR_TRUE24:
+ if( dev->scanning.fGrayFromColor > 7 ){
+ pSize->dwBytes = (pSize->dwPixels + 7UL) >> 3;
+ pSize->dwPixels = pSize->dwBytes * 8;
+ } else {
+ pSize->dwBytes = pSize->dwPixels * 3UL;
+ }
+ break;
+
+ case COLOR_GRAY16:
+ pSize->dwBytes = pSize->dwPixels << 1;
+ break;
+
+ case COLOR_256GRAY:
+ pSize->dwBytes = pSize->dwPixels;
+ break;
+
+ default:
+ pSize->dwBytes = (pSize->dwPixels + 7UL) >> 3;
+ pSize->dwPixels = pSize->dwBytes * 8;
+ break;
+ }
+}
+
+/**
+ */
+static void
+usb_SaveImageInfo( Plustek_Device *dev, ImgDef *pInfo )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ ScanParam *pParam = &dev->scanning.sParam;
+
+ DBG( _DBG_INFO, "usb_SaveImageInfo()\n" );
+
+ /* Dpi & Origins */
+ pParam->UserDpi = pInfo->xyDpi;
+ pParam->Origin.x = pInfo->crArea.x;
+ pParam->Origin.y = pInfo->crArea.y;
+
+ /* Source & Bits */
+ pParam->bBitDepth = 8;
+
+ switch( pInfo->wDataType ) {
+
+ case COLOR_TRUE48:
+ pParam->bBitDepth = 16;
+ /* fall through... */
+
+ case COLOR_TRUE24:
+ pParam->bDataType = SCANDATATYPE_Color;
+
+ /* AFE operation: one or 3 channels ! */
+ if( hw->bReg_0x26 & _ONE_CH_COLOR )
+ pParam->bChannels = 1;
+ else
+ pParam->bChannels = 3;
+ break;
+
+ case COLOR_GRAY16:
+ pParam->bBitDepth = 16;
+ /* fall through... */
+
+ case COLOR_256GRAY:
+ pParam->bDataType = SCANDATATYPE_Gray;
+ pParam->bChannels = 1;
+ break;
+
+ default:
+ pParam->bBitDepth = 1;
+ pParam->bDataType = SCANDATATYPE_BW;
+ pParam->bChannels = 1;
+ }
+
+ DBG( _DBG_INFO, "* dwFlag = 0x%08lx\n", pInfo->dwFlag );
+
+ if( pInfo->dwFlag & SCANDEF_Transparency )
+ pParam->bSource = SOURCE_Transparency;
+ else if( pInfo->dwFlag & SCANDEF_Negative )
+ pParam->bSource = SOURCE_Negative;
+ else if( pInfo->dwFlag & SCANDEF_Adf )
+ pParam->bSource = SOURCE_ADF;
+ else
+ pParam->bSource = SOURCE_Reflection;
+
+ /* it seems, that we need to adjust the Origin.x when we have a
+ * sheetfed device to avoid stripes in the resulting pictures
+ */
+ if( usb_IsSheetFedDevice(dev)) {
+
+ int step, div, org, xdpi;
+
+ xdpi = usb_SetAsicDpiX( dev, pParam->UserDpi.x );
+
+ if ((xdpi * 2) <= 300)
+ div = 300;
+ else if ((xdpi * 2) <= 600)
+ div = 600;
+ else if ((xdpi * 2) <= 1200)
+ div = 1200;
+ else
+ div = 2400;
+
+ step = div / xdpi;
+ org = pParam->Origin.x;
+
+ pParam->Origin.x = (pParam->Origin.x / step) * step;
+
+ if (org != pParam->Origin.x)
+ DBG(_DBG_INFO, "* Origin.x adjusted: %i -> %i\n",
+ org, pParam->Origin.x);
+ }
+}
+
+/* END PLUSTEK-USBSCAN.C ....................................................*/
diff --git a/backend/plustek-usbshading.c b/backend/plustek-usbshading.c
new file mode 100644
index 0000000..6d08dc7
--- /dev/null
+++ b/backend/plustek-usbshading.c
@@ -0,0 +1,3215 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbshading.c
+ * @brief Calibration routines.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - minor fixes
+ * - added workaround stuff for EPSON1250
+ * - 0.42 - added workaround stuff for UMAX 3400
+ * - 0.43 - added call to usb_switchLamp before reading dark data
+ * - 0.44 - added more debug output and bypass calibration
+ * - added dump of shading data
+ * - 0.45 - added coarse calibration for CIS devices
+ * - added _WAF_SKIP_FINE to skip the results of fine calibration
+ * - CanoScan fixes and fine-tuning
+ * - 0.46 - CanoScan will now be calibrated by code in plustek-usbcal.c
+ * - added functions to save and restore calibration data from a file
+ * - fixed some TPA issues
+ * - 0.47 - made calibration work on big-endian machines
+ * - 0.48 - added more debug output
+ * - added usb_AutoWarmup()
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - using now PhyDpi.y as selector for the motor MCLK range
+ * - 0.50 - readded kCIS670 to add 5% extra to LiDE20 fine calibration
+ * - fixed line statistics and added data output
+ * - 0.51 - added fine calibration cache
+ * - 0.52 - added get_ptrs to let various sensororders work
+ * correctly
+ * - fixed warning condition
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/************** global stuff - I hate it, but we need it... ******************/
+
+#define _MAX_AUTO_WARMUP 60 /**< number of loops */
+#define _AUTO_SLEEP 1 /**< time to sleep, when lamp is not stable */
+#define _AUTO_THRESH 60 /**< threshold for stop waiting (normal lamp) */
+#define _AUTO_TPA_THRESH 40 /**< threshold for stop waiting (TPA lamp) */
+
+#define _MAX_GAIN_LOOPS 10 /**< max number of loops for coarse calibration */
+#define _TLOOPS 3 /**< test loops for transfer rate measurement */
+
+#define SWAP_COARSE
+#define SWAP_FINE
+
+/************************** static variables *********************************/
+
+static RGBUShortDef Gain_Hilight;
+static RGBUShortDef Gain_NegHilight;
+static RGBByteDef Gain_Reg;
+
+static u_long m_dwPixels;
+static ScanParam m_ScanParam;
+static u_long m_dwIdealGain;
+
+static double dMCLK, dExpect, dMax;
+static double dMCLK_ADF;
+
+/** do some statistics...
+ */
+static void usb_line_statistics( char *cmt, u_short* buf,
+ u_long dim_x, SANE_Bool color )
+{
+ char fn[50];
+ int i, channel;
+ u_long dw, imad, imid, alld, cld, cud;
+ u_short mid, mad, aved, lbd, ubd, tmp;
+ MonoWordDef *pvd, *pvd2;
+ FILE *fp;
+ SANE_Bool swap = usb_HostSwap();
+
+ pvd = pvd2 = (MonoWordDef*)buf;
+
+ if( color )
+ channel = 3;
+ else
+ channel = 1;
+
+ for( i = 0; i < channel; i++ ) {
+
+ mid = 0xFFFF;
+ mad = 0;
+ imid = 0;
+ imad = 0;
+ alld = 0;
+
+ fp = NULL;
+ if( DBG_LEVEL >= _DBG_DCALDATA ) {
+ sprintf( fn, "%scal%u.dat", cmt, i );
+ fp = fopen( fn, "w+b" );
+ if( fp == NULL )
+ DBG( _DBG_ERROR, "Could not open %s\n", fn );
+ }
+
+ /* do the standard min/max stuff */
+ for( dw = 0; dw < dim_x; pvd++, dw++ ) {
+
+ if( swap )
+ tmp = _LOBYTE(pvd->Mono) * 256 + _HIBYTE(pvd->Mono);
+ else
+ tmp = pvd->Mono;
+
+ if( tmp > mad ) {
+ mad = tmp;
+ imad = dw;
+ }
+
+ if( tmp < mid ) {
+ mid = tmp;
+ imid = dw;
+ }
+
+ if( fp )
+ fprintf(fp, "%u\n", tmp );
+
+ alld += tmp;
+ }
+
+ if( fp )
+ fclose(fp);
+
+ /* calculate average and 5% limit */
+ aved = (u_short)(alld/dim_x);
+ lbd = aved - 0.05*aved;
+ ubd = aved + 0.05*aved;
+ cld = 0;
+ cud = 0;
+
+ /* find the number of values beyond the 5% limits */
+ for( dw = 0; dw < dim_x; pvd2++, dw++ ) {
+
+ if( swap )
+ tmp = _LOBYTE(pvd2->Mono) * 256 + _HIBYTE(pvd2->Mono);
+ else
+ tmp = pvd2->Mono;
+
+ if( tmp > ubd ) cud++;
+ if( tmp < lbd ) cld++;
+ }
+
+ DBG( _DBG_INFO2, "Color[%u] (%s): %lu all "
+ "min=%u(%lu) max=%u(%lu) ave=%u\n",
+ i, cmt, dim_x, mid, imid, mad, imad, aved);
+ DBG( _DBG_INFO2, "5%%: low@%u (count=%lu), upper@%u (count=%lu)\n",
+ lbd, cld, ubd, cud);
+ }
+}
+
+/**
+ */
+static void
+usb_PrepareFineCal( Plustek_Device *dev, ScanParam *tmp_sp, u_short cal_dpi )
+{
+ ScanParam *sp = &dev->scanning.sParam;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+
+ *tmp_sp = *sp;
+
+ if( dev->adj.cacheCalData ) {
+
+ DBG( _DBG_INFO2, "* Cal-cache active, tweaking scanparams"
+ " - DPI=%u!\n", cal_dpi );
+
+ tmp_sp->UserDpi.x = usb_SetAsicDpiX(dev, sp->UserDpi.x );
+ if( cal_dpi != 0 )
+ tmp_sp->UserDpi.x = cal_dpi;
+
+ tmp_sp->PhyDpi = scaps->OpticDpi;
+ tmp_sp->Origin.x = 0;
+ tmp_sp->Size.dwPixels = scaps->Normal.Size.x *
+ usb_SetAsicDpiX(dev, tmp_sp->UserDpi.x)/ 300UL;
+ }
+
+#if 0
+ if( tmp_sp->PhyDpi.x > 75)
+ tmp_sp->Size.dwLines = 64;
+ else
+#endif
+ tmp_sp->Size.dwLines = 32;
+
+ tmp_sp->Origin.y = 0;
+ tmp_sp->bBitDepth = 16;
+
+ tmp_sp->UserDpi.y = scaps->OpticDpi.y;
+ tmp_sp->Size.dwBytes = tmp_sp->Size.dwPixels * 2 * tmp_sp->bChannels;
+
+ if( usb_IsCISDevice(dev) && (tmp_sp->bDataType == SCANDATATYPE_Color)) {
+ tmp_sp->Size.dwBytes *= 3;
+ }
+
+ tmp_sp->dMCLK = dMCLK;
+}
+
+/**
+ */
+static double usb_GetMCLK( Plustek_Device *dev, ScanParam *param )
+{
+ int idx, i;
+ double mclk;
+ ClkMotorDef *clk;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ clk = usb_GetMotorSet( hw->motorModel );
+ idx = 0;
+ for( i = 0; i < _MAX_CLK; i++ ) {
+ if( param->PhyDpi.y <= dpi_ranges[i] )
+ break;
+ idx++;
+ }
+ if( idx >= _MAX_CLK )
+ idx = _MAX_CLK - 1;
+
+ if( param->bDataType != SCANDATATYPE_Color ) {
+
+ if( param->bBitDepth > 8 )
+ mclk = clk->gray_mclk_16[idx];
+ else
+ mclk = clk->gray_mclk_8[idx];
+ } else {
+ if( param->bBitDepth > 8 )
+ mclk = clk->color_mclk_16[idx];
+ else
+ mclk = clk->color_mclk_8[idx];
+ }
+
+ DBG( _DBG_INFO, "GETMCLK[%u/%u], using entry %u: %.3f, %u\n",
+ hw->motorModel, param->bDataType, idx, mclk, param->PhyDpi.x );
+ return mclk;
+}
+
+/** usb_SetMCLK
+ * get the MCLK out of our table
+ */
+static void usb_SetMCLK( Plustek_Device *dev, ScanParam *param )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ dMCLK = usb_GetMCLK( dev, param );
+ param->dMCLK = dMCLK;
+
+ DBG( _DBG_INFO, "SETMCLK[%u/%u]: %.3f\n",
+ hw->motorModel, param->bDataType, dMCLK );
+}
+
+/** usb_SetDarkShading
+ * download the dark shading data to Merlins' DRAM
+ */
+static SANE_Bool usb_SetDarkShading( Plustek_Device *dev, u_char channel,
+ void *coeff_buffer, u_short wCount )
+{
+ int res;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ regs[0x03] = 0;
+ if( channel == CHANNEL_green )
+ regs[0x03] |= 4;
+ else
+ if( channel == CHANNEL_blue )
+ regs[0x03] |= 8;
+
+ if( usbio_WriteReg( dev->fd, 0x03, regs[0x03] )) {
+
+ /* Dataport address is always 0 for setting offset coefficient */
+ regs[0x04] = 0;
+ regs[0x05] = 0;
+
+ res = sanei_lm983x_write( dev->fd, 0x04, &regs[0x04], 2, SANE_TRUE );
+
+ /* Download offset coefficients */
+ if( SANE_STATUS_GOOD == res ) {
+
+ res = sanei_lm983x_write( dev->fd, 0x06,
+ (u_char*)coeff_buffer, wCount, SANE_FALSE );
+ if( SANE_STATUS_GOOD == res )
+ return SANE_TRUE;
+ }
+ }
+
+ DBG( _DBG_ERROR, "usb_SetDarkShading() failed\n" );
+ return SANE_FALSE;
+}
+
+/** usb_SetWhiteShading
+ * download the white shading data to Merlins' DRAM
+ */
+static SANE_Bool usb_SetWhiteShading( Plustek_Device *dev, u_char channel,
+ void *data_buffer, u_short wCount )
+{
+ int res;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ regs[0x03] = 1;
+ if (channel == CHANNEL_green)
+ regs [0x03] |= 4;
+ else
+ if (channel == CHANNEL_blue)
+ regs[0x03] |= 8;
+
+ if( usbio_WriteReg( dev->fd, 0x03, regs[0x03] )) {
+
+ /* Dataport address is always 0 for setting offset coefficient */
+ regs[0x04] = 0;
+ regs[0x05] = 0;
+
+ res = sanei_lm983x_write( dev->fd, 0x04, &regs[0x04], 2, SANE_TRUE );
+
+ /* Download offset coefficients */
+ if( SANE_STATUS_GOOD == res ) {
+ res = sanei_lm983x_write(dev->fd, 0x06,
+ (u_char*)data_buffer, wCount, SANE_FALSE );
+ if( SANE_STATUS_GOOD == res )
+ return SANE_TRUE;
+ }
+ }
+
+ DBG( _DBG_ERROR, "usb_SetWhiteShading() failed\n" );
+ return SANE_FALSE;
+}
+
+/** usb_GetSWOffsetGain4TPA
+ * preset the offset and gain parameter for fine calibration for
+ * TPA/ADF scanning
+ */
+static void usb_GetSWOffsetGain4TPA( Plustek_Device *dev )
+{
+ ScanParam *param = &dev->scanning.sParam;
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+
+ switch( sCaps->bCCD ) {
+ case kEPSON:
+ DBG( _DBG_INFO2, "kEPSON TPA adjustments\n" );
+ param->swGain[0] = 1000;
+ param->swGain[1] = 1000;
+ param->swGain[2] = 1000;
+ break;
+ }
+}
+
+/** usb_GetSWOffsetGain
+ * preset the offset and gain parameter for fine calibration for normal
+ * scanning
+ */
+static void usb_GetSWOffsetGain( Plustek_Device *dev )
+{
+ ScanParam *param = &dev->scanning.sParam;
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ param->swOffset[0] = 0;
+ param->swOffset[1] = 0;
+ param->swOffset[2] = 0;
+
+ param->swGain[0] = 1000;
+ param->swGain[1] = 1000;
+ param->swGain[2] = 1000;
+
+ if( param->bSource != SOURCE_Reflection ) {
+ usb_GetSWOffsetGain4TPA( dev );
+ return;
+ }
+
+ /* only valid for normal scanning... */
+ switch( sCaps->bCCD ) {
+
+ case kEPSON:
+ DBG( _DBG_INFO2, "kEPSON adjustments\n" );
+#if 0
+ param->swGain[0] = 800;
+ param->swGain[1] = 800;
+ param->swGain[2] = 800;
+#endif
+ break;
+
+ case kNECSLIM:
+ DBG( _DBG_INFO2, "kNECSLIM adjustments\n" );
+ if( param->PhyDpi.x <= 150 ) {
+ param->swOffset[0] = 600;
+ param->swOffset[1] = 500;
+ param->swOffset[2] = 300;
+ param->swGain[0] = 960;
+ param->swGain[1] = 970;
+ param->swGain[2] = 1000;
+ } else if (param->PhyDpi.x <= 300) {
+ param->swOffset[0] = 700;
+ param->swOffset[1] = 600;
+ param->swOffset[2] = 400;
+ param->swGain[0] = 967;
+ param->swGain[1] = 980;
+ param->swGain[2] = 1000;
+ } else {
+ param->swOffset[0] = 900;
+ param->swOffset[1] = 850;
+ param->swOffset[2] = 620;
+ param->swGain[0] = 965;
+ param->swGain[1] = 980;
+ param->swGain[2] = 1000;
+ }
+ break;
+
+ case kNEC8861:
+ DBG( _DBG_INFO2, "kNEC8861 adjustments\n" );
+ break;
+
+ case kCIS670:
+ DBG( _DBG_INFO2, "kCIS670 adjustments\n" );
+ if(param->bDataType == SCANDATATYPE_Color) {
+
+ param->swGain[0] =
+ param->swGain[1] =
+ param->swGain[2] = 952;
+
+ param->swOffset[0] =
+ param->swOffset[1] =
+ param->swOffset[2] = 1000;
+ }
+ break;
+#if 0
+ case kCIS650:
+ case kCIS1220:
+
+ DBG( _DBG_INFO2, "kCIS adjustments\n" );
+ if(param->bDataType == SCANDATATYPE_Color) {
+
+ param->swGain[0] =
+ param->swGain[1] =
+ param->swGain[2] = 952;
+
+ param->swOffset[0] =
+ param->swOffset[1] =
+ param->swOffset[2] = 1000;
+ }
+ break;
+ case kCIS1240:
+ DBG( _DBG_INFO2, "kCIS1240 adjustments\n" );
+ if(param->bDataType == SCANDATATYPE_Color) {
+
+ param->swGain[0] = 950;
+ param->swGain[1] = 950;
+ param->swGain[2] = 900;
+
+ param->swOffset[0] =
+ param->swOffset[1] =
+ param->swOffset[2] = 0; /*1000;*/
+ }
+ break;
+#endif
+
+ case kNEC3799:
+ DBG( _DBG_INFO2, "kNEC3799 adjustments\n" );
+ if( sCaps->bPCB == 2 ) {
+ if( param->PhyDpi.x <= 150 ) {
+ param->swOffset[0] = 600;
+ param->swOffset[1] = 500;
+ param->swOffset[2] = 300;
+ param->swGain[0] = 960;
+ param->swGain[1] = 970;
+ param->swGain[2] = 1000;
+ } else if (param->PhyDpi.x <= 300) {
+ param->swOffset[0] = 700;
+ param->swOffset[1] = 600;
+ param->swOffset[2] = 400;
+ param->swGain[0] = 967;
+ param->swGain[1] = 980;
+ param->swGain[2] = 1000;
+ } else {
+ param->swOffset[0] = 900;
+ param->swOffset[1] = 850;
+ param->swOffset[2] = 620;
+ param->swGain[0] = 965;
+ param->swGain[1] = 980;
+ param->swGain[2] = 1000;
+ }
+ } else if( hw->motorModel == MODEL_KaoHsiung ) {
+ param->swOffset[0] = 1950;
+ param->swOffset[1] = 1700;
+ param->swOffset[2] = 1250;
+ param->swGain[0] = 955;
+ param->swGain[1] = 950;
+ param->swGain[2] = 1000;
+ } else { /* MODEL_Hualien */
+ if( param->PhyDpi.x <= 300 ) {
+ if( param->bBitDepth > 8 ) {
+ param->swOffset[0] = 0;
+ param->swOffset[1] = 0;
+ param->swOffset[2] = -300;
+ param->swGain[0] = 970;
+ param->swGain[1] = 985;
+ param->swGain[2] = 1050;
+ } else {
+ param->swOffset[0] = -485;
+ param->swOffset[1] = -375;
+ param->swOffset[2] = -628;
+ param->swGain[0] = 970;
+ param->swGain[1] = 980;
+ param->swGain[2] = 1050;
+ }
+ } else {
+ if( param->bBitDepth > 8 ) {
+ param->swOffset[0] = 1150;
+ param->swOffset[1] = 1000;
+ param->swOffset[2] = 700;
+ param->swGain[0] = 990;
+ param->swGain[1] = 1000;
+ param->swGain[2] = 1050;
+ } else {
+ param->swOffset[0] = -30;
+ param->swOffset[1] = 0;
+ param->swOffset[2] = -250;
+ param->swGain[0] = 985;
+ param->swGain[1] = 995;
+ param->swGain[2] = 1050;
+ }
+ }
+ }
+ break;
+
+ case kSONY548:
+ DBG( _DBG_INFO2, "kSONY548 adjustments\n" );
+ if(param->bDataType == SCANDATATYPE_Color)
+ {
+ if(param->PhyDpi.x <= 75)
+ {
+ param->swOffset[0] = 650;
+ param->swOffset[1] = 850;
+ param->swOffset[2] = 500;
+ param->swGain[0] = 980;
+ param->swGain[1] = 1004;
+ param->swGain[2] = 1036;
+ }
+ else if(param->PhyDpi.x <= 300)
+ {
+ param->swOffset[0] = 700;
+ param->swOffset[1] = 900;
+ param->swOffset[2] = 550;
+ param->swGain[0] = 970;
+ param->swGain[1] = 995;
+ param->swGain[2] = 1020;
+ }
+ else if(param->PhyDpi.x <= 400)
+ {
+ param->swOffset[0] = 770;
+ param->swOffset[1] = 1010;
+ param->swOffset[2] = 600;
+ param->swGain[0] = 970;
+ param->swGain[1] = 993;
+ param->swGain[2] = 1023;
+ }
+ else
+ {
+ param->swOffset[0] = 380;
+ param->swOffset[1] = 920;
+ param->swOffset[2] = 450;
+ param->swGain[0] = 957;
+ param->swGain[1] = 980;
+ param->swGain[2] = 1008;
+ }
+ }
+ else
+ {
+ if(param->PhyDpi.x <= 75)
+ {
+ param->swOffset[1] = 1250;
+ param->swGain[1] = 950;
+ }
+ else if(param->PhyDpi.x <= 300)
+ {
+ param->swOffset[1] = 1250;
+ param->swGain[1] = 950;
+ }
+ else if(param->PhyDpi.x <= 400)
+ {
+ param->swOffset[1] = 1250;
+ param->swGain[1] = 950;
+ }
+ else
+ {
+ param->swOffset[1] = 1250;
+ param->swGain[1] = 950;
+ }
+ }
+ break;
+
+ case kNEC3778:
+ DBG( _DBG_INFO2, "kNEC3778 adjustments\n" );
+ if((_LM9831 == hw->chip) && (param->PhyDpi.x <= 300))
+ {
+ param->swOffset[0] = 0;
+ param->swOffset[1] = 0;
+ param->swOffset[2] = 0;
+ param->swGain[0] = 900;
+ param->swGain[1] = 920;
+ param->swGain[2] = 980;
+ }
+ else if( hw->motorModel == MODEL_HuaLien && param->PhyDpi.x > 800)
+ {
+ param->swOffset[0] = 0;
+ param->swOffset[1] = 0;
+ param->swOffset[2] = -200;
+ param->swGain[0] = 980;
+ param->swGain[1] = 930;
+ param->swGain[2] = 1080;
+ }
+ else
+ {
+ param->swOffset[0] = -304;
+ param->swOffset[1] = -304;
+ param->swOffset[2] = -304;
+ param->swGain[0] = 910;
+ param->swGain[1] = 920;
+ param->swGain[2] = 975;
+ }
+
+ if(param->bDataType == SCANDATATYPE_BW && param->PhyDpi.x <= 300)
+ {
+ param->swOffset[1] = 1000;
+ param->swGain[1] = 1000;
+ }
+ break;
+ }
+}
+
+/** according to the pixel values,
+ */
+static u_char usb_GetNewGain( Plustek_Device *dev, u_short wMax, int channel )
+{
+ double dRatio, dAmp;
+ u_long dwInc, dwDec;
+ u_char bGain;
+
+ if( !wMax )
+ wMax = 1;
+
+ dAmp = 0.93 + 0.067 * dev->usbDev.a_bRegs[0x3b+channel];
+
+ if((m_dwIdealGain / (wMax / dAmp)) < 3) {
+
+ dRatio = ((double) m_dwIdealGain * dAmp / wMax - 0.93) / 0.067;
+ if(ceil(dRatio) > 0x3f)
+ return 0x3f;
+
+ dwInc = (u_long)((0.93 + ceil (dRatio) * 0.067) * wMax / dAmp);
+ dwDec = (u_long)((0.93 + floor (dRatio) * 0.067) * wMax / dAmp);
+ if((dwInc >= 0xff00) ||
+ (labs (dwInc - m_dwIdealGain) > labs(dwDec - m_dwIdealGain))) {
+ bGain = (u_char)floor(dRatio);
+ } else {
+ bGain = (u_char)ceil(dRatio);
+ }
+
+ } else {
+
+ dRatio = m_dwIdealGain / (wMax / dAmp);
+ dAmp = floor((dRatio / 3 - 0.93)/0.067);
+
+ if( dAmp > 31 )
+ dAmp = 31;
+
+ bGain = (u_char)dAmp + 32;
+ }
+
+ if( bGain > 0x3f ) {
+ DBG( _DBG_INFO, "* GAIN Overflow!!!\n" );
+ bGain = 0x3f;
+ }
+ return bGain;
+}
+
+/** limit and set register given by address
+ * @param gain -
+ * @param reg -
+ */
+static void setAdjGain( int gain, u_char *reg )
+{
+ if( gain >= 0 ) {
+
+ if( gain > 0x3f )
+ *reg = 0x3f;
+ else
+ *reg = gain;
+ }
+}
+
+/**
+ * @param channel - 0 = red, 1 = green, 2 = blue
+ * @param max -
+ * @param ideal -
+ * @param l_on -
+ * @param l_off -
+ * @return
+ */
+static SANE_Bool adjLampSetting( Plustek_Device *dev, int channel, u_long max,
+ u_long ideal, u_short l_on, u_short *l_off )
+{
+ SANE_Bool adj = SANE_FALSE;
+ u_long lamp_on;
+
+ /* so if the image was too bright, we dim the lamps by 3% */
+ if( max > ideal ) {
+
+ lamp_on = (*l_off) - l_on;
+ lamp_on = (lamp_on * 97)/100;
+ *l_off = l_on + lamp_on;
+ DBG( _DBG_INFO2,
+ "lamp(%u) adjust (-3%%): %i %i\n", channel, l_on, *l_off );
+ adj = SANE_TRUE;
+ }
+
+ /* if the image was too dull, increase lamp by 1% */
+ if( dev->usbDev.a_bRegs[0x3b + channel] == 63 ) {
+
+ lamp_on = (*l_off) - l_on;
+ lamp_on += (lamp_on/100);
+ *l_off = l_on + lamp_on;
+ DBG( _DBG_INFO2,
+ "lamp(%u) adjust (+1%%): %i %i\n", channel, l_on, *l_off );
+ adj = SANE_TRUE;
+ }
+
+ return adj;
+}
+
+/** usb_AdjustGain
+ * function to perform the "coarse calibration step" part 1.
+ * We scan reference image pixels to determine the optimum coarse gain settings
+ * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
+ * applied at the line rate during normal scanning.
+ * The scanned line should contain a white strip with some black at the
+ * beginning. The function searches for the maximum value which corresponds to
+ * the maximum white value.
+ * Affects register 0x3b, 0x3c and 0x3d
+ *
+ */
+static SANE_Bool usb_AdjustGain( Plustek_Device *dev, int fNegative )
+{
+ char tmp[40];
+ int i;
+ double min_mclk;
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_long *scanbuf = scanning->pScanBuffer;
+ u_char *regs = dev->usbDev.a_bRegs;
+ u_long dw, start, end, len;
+ SANE_Bool fRepeatITA = SANE_TRUE;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ bMaxITA = 0xff;
+
+ DBG( _DBG_INFO, "#########################\n" );
+ DBG( _DBG_INFO, "usb_AdjustGain()\n" );
+ if((dev->adj.rgain != -1) &&
+ (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
+ setAdjGain( dev->adj.rgain, &regs[0x3b] );
+ setAdjGain( dev->adj.ggain, &regs[0x3c] );
+ setAdjGain( dev->adj.bgain, &regs[0x3d] );
+ DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
+ return SANE_TRUE;
+ }
+
+ min_mclk = usb_GetMCLK( dev, &scanning->sParam );
+
+ /* define the strip to scan for coarse calibration */
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
+ 2 * m_ScanParam.bChannels;
+
+ if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color)
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Gain;
+
+ start = 0;
+ len = m_ScanParam.Size.dwPixels;
+
+ if( scanning->sParam.bSource == SOURCE_Transparency ) {
+ start = scaps->Positive.DataOrigin.x * scaps->OpticDpi.x / 300UL;
+ len = scaps->Positive.Size.x * scaps->OpticDpi.x / 300UL;
+ }
+ else if( scanning->sParam.bSource == SOURCE_Negative ) {
+ start = scaps->Negative.DataOrigin.x * scaps->OpticDpi.x / 300UL;
+ len = scaps->Negative.Size.x * scaps->OpticDpi.x / 300UL;
+ }
+ end = start + len;
+
+ start = ((u_long)dev->usbDev.pSource->DataOrigin.x*scaps->OpticDpi.x/300UL);
+ len = ((u_long)dev->usbDev.pSource->Size.x * scaps->OpticDpi.x / 300UL);
+
+ DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" );
+ DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
+ DBG( _DBG_INFO2, "Start = %lu\n", start );
+ DBG( _DBG_INFO2, "Len = %lu\n", len );
+ DBG( _DBG_INFO2, "End = %lu\n", end );
+ DBG( _DBG_INFO, "MIN MCLK = %.2f\n", min_mclk );
+
+ i = 0;
+TOGAIN:
+ m_ScanParam.dMCLK = dMCLK;
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam ))
+ return SANE_FALSE;
+
+ if( !usb_ScanBegin( dev, SANE_FALSE) ||
+ !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "usb_AdjustGain() failed\n" );
+ return SANE_FALSE;
+ }
+
+ DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+
+ if( end > m_ScanParam.Size.dwPhyPixels )
+ end = m_ScanParam.Size.dwPhyPixels;
+
+ sprintf( tmp, "coarse-gain-%u.raw", i++ );
+
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+#ifdef SWAP_COARSE
+ if(usb_HostSwap())
+#endif
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+
+ if( fNegative ) {
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ RGBULongDef rgb, rgbSum;
+ u_long dwLoop = len / 20 * 20;
+ u_long dw10, dwGray, dwGrayMax;
+
+ rgb.Red = rgb.Green = rgb.Blue = dwGrayMax = 0;
+
+ for( dw = start; dwLoop; dwLoop-- ) {
+
+ rgbSum.Red = rgbSum.Green = rgbSum.Blue = 0;
+ for( dw10 = 20; dw10--; dw++ ) {
+ rgbSum.Red += (u_long)(((RGBULongDef*)scanbuf)[dw].Red);
+ rgbSum.Green += (u_long)(((RGBULongDef*)scanbuf)[dw].Green);
+ rgbSum.Blue += (u_long)(((RGBULongDef*)scanbuf)[dw].Blue);
+ }
+
+ /* do some weighting of the color planes for negatives */
+ dwGray = (rgbSum.Red * 30UL + rgbSum.Green * 59UL + rgbSum.Blue * 11UL) / 100UL;
+
+ if( fNegative == 1 || rgbSum.Red > rgbSum.Green) {
+ if( dwGray > dwGrayMax ) {
+ dwGrayMax = dwGray;
+ rgb.Red = rgbSum.Red;
+ rgb.Green = rgbSum.Green;
+ rgb.Blue = rgbSum.Blue;
+ }
+ }
+ }
+
+ Gain_Hilight.Red = (u_short)(rgb.Red / 20UL);
+ Gain_Hilight.Green = (u_short)(rgb.Green / 20UL);
+ Gain_Hilight.Blue = (u_short)(rgb.Blue / 20UL);
+ DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ Gain_Hilight.Red, Gain_Hilight.Red, Gain_Hilight.Green,
+ Gain_Hilight.Green, Gain_Hilight.Blue, Gain_Hilight.Blue );
+
+ regs[0x3b] = usb_GetNewGain(dev,Gain_Hilight.Red, 0 );
+ regs[0x3c] = usb_GetNewGain(dev,Gain_Hilight.Green, 1 );
+ regs[0x3d] = usb_GetNewGain(dev,Gain_Hilight.Blue, 2 );
+
+ } else {
+
+ u_long dwMax = 0, dwSum;
+ u_long dwLoop = len / 20 * 20;
+ u_long dw10;
+
+ for( dw = start; dwLoop; dwLoop-- ) {
+
+ dwSum = 0;
+ for( dw10 = 20; dw10--; dw++ )
+ dwSum += (u_long)((u_short*)scanbuf)[dw];
+
+ if((fNegative == 1) || (dwSum < 0x6000 * 20)) {
+ if( dwMax < dwSum )
+ dwMax = dwSum;
+ }
+ }
+ Gain_Hilight.Red = Gain_Hilight.Green =
+ Gain_Hilight.Blue = (u_short)(dwMax / 20UL);
+
+ Gain_Reg.Red = Gain_Reg.Green =
+ Gain_Reg.Blue = regs[0x3b] =
+ regs[0x3c] = regs[0x3d] = usb_GetNewGain(dev,Gain_Hilight.Green,1);
+ }
+ } else {
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ RGBUShortDef max_rgb, min_rgb, tmp_rgb;
+ u_long dwR, dwG, dwB;
+ u_long dwDiv = 10;
+ u_long dwLoop1 = len / dwDiv, dwLoop2;
+
+ max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0;
+ min_rgb.Red = min_rgb.Green = min_rgb.Blue = 0xffff;
+
+ /* find out the max pixel value for R, G, B */
+ for( dw = start; dwLoop1; dwLoop1-- ) {
+
+ /* do some averaging... */
+ for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0;
+ dwLoop2; dwLoop2--, dw++) {
+ if( usb_IsCISDevice(dev)) {
+ dwR += ((u_short*)scanbuf)[dw];
+ dwG += ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
+ dwB += ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+ } else {
+ dwR += ((RGBUShortDef*)scanbuf)[dw].Red;
+ dwG += ((RGBUShortDef*)scanbuf)[dw].Green;
+ dwB += ((RGBUShortDef*)scanbuf)[dw].Blue;
+ }
+ }
+ dwR = dwR / dwDiv;
+ dwG = dwG / dwDiv;
+ dwB = dwB / dwDiv;
+
+ if(max_rgb.Red < dwR)
+ max_rgb.Red = dwR;
+ if(max_rgb.Green < dwG)
+ max_rgb.Green = dwG;
+ if(max_rgb.Blue < dwB)
+ max_rgb.Blue = dwB;
+
+ if(min_rgb.Red > dwR)
+ min_rgb.Red = dwR;
+ if(min_rgb.Green > dwG)
+ min_rgb.Green = dwG;
+ if(min_rgb.Blue > dwB)
+ min_rgb.Blue = dwB;
+ }
+
+ DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ max_rgb.Red, max_rgb.Red, max_rgb.Green,
+ max_rgb.Green, max_rgb.Blue, max_rgb.Blue );
+ DBG(_DBG_INFO2, "MIN(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ min_rgb.Red, min_rgb.Red, min_rgb.Green,
+ min_rgb.Green, min_rgb.Blue, min_rgb.Blue );
+
+ /* on CIS scanner, we use the min value, on CCD the max value
+ * for adjusting the gain
+ */
+ tmp_rgb = max_rgb;
+ if( usb_IsCISDevice(dev))
+ tmp_rgb = min_rgb;
+
+ DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
+ tmp_rgb.Red, tmp_rgb.Red, tmp_rgb.Green,
+ tmp_rgb.Green, tmp_rgb.Blue, tmp_rgb.Blue);
+
+/* m_dwIdealGain = IDEAL_GainNormal;
+*/ /* min(min(rgb.wRed, rgb.wGreen), rgb.wBlue) */
+
+ regs[0x3b] = usb_GetNewGain( dev, tmp_rgb.Red, 0 );
+ regs[0x3c] = usb_GetNewGain( dev, tmp_rgb.Green, 1 );
+ regs[0x3d] = usb_GetNewGain( dev, tmp_rgb.Blue, 2 );
+
+ if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+
+ SANE_Bool adj = SANE_FALSE;
+
+ /* on CIS devices, we can control the lamp off settings */
+ if( usb_IsCISDevice(dev)) {
+
+/* m_dwIdealGain = IDEAL_GainNormal;
+ */
+ if( adjLampSetting( dev, CHANNEL_red, tmp_rgb.Red, m_dwIdealGain,
+ hw->red_lamp_on, &hw->red_lamp_off )) {
+ adj = SANE_TRUE;
+ }
+
+ if( adjLampSetting( dev, CHANNEL_green, tmp_rgb.Green, m_dwIdealGain,
+ hw->green_lamp_on, &hw->green_lamp_off )) {
+ adj = SANE_TRUE;
+ }
+
+ if( adjLampSetting( dev, CHANNEL_blue, tmp_rgb.Blue, m_dwIdealGain,
+ hw->blue_lamp_on, &hw->blue_lamp_off)){
+ adj = SANE_TRUE;
+ }
+
+ /* on any adjustment, set the registers... */
+ if( adj ) {
+ usb_AdjustLamps( dev, SANE_TRUE );
+
+ if( i < _MAX_GAIN_LOOPS )
+ goto TOGAIN;
+ }
+
+ } else {
+
+ if((!regs[0x3b] ||
+ !regs[0x3c] || !regs[0x3d]) && dMCLK > min_mclk) {
+
+ scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ adj = SANE_TRUE;
+
+ } else if(((regs[0x3b] == 63) || (regs[0x3c] == 63) ||
+ (regs[0x3d] == 63)) && (dMCLK < 10)) {
+
+ scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ adj = SANE_TRUE;
+ }
+
+ if( adj ) {
+ if( i < _MAX_GAIN_LOOPS )
+ goto TOGAIN;
+ }
+ }
+
+ } else {
+
+ /* for MODEL KaoHsiung 1200 scanner multi-straight-line bug at
+ * 1200 dpi color mode
+ */
+ if( hw->motorModel == MODEL_KaoHsiung &&
+ scaps->bCCD == kNEC3778 && dMCLK>= 5.5 && !regs[0x3c]){
+
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+ scanning->sParam.dMCLK = dMCLK = dMCLK - 1.5;
+ goto TOGAIN;
+
+ } else if( hw->motorModel == MODEL_HuaLien &&
+ scaps->bCCD == kNEC3799 && fRepeatITA ) {
+
+ if((!regs[0x3b] ||
+ !regs[0x3c] || !regs[0x3d]) && dMCLK > 3.0) {
+
+ scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+ goto TOGAIN;
+
+ } else if(((regs[0x3b] == 63) || (regs[0x3c] == 63) ||
+ (regs[0x3d] == 63)) && (dMCLK < 10)) {
+
+ scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+ goto TOGAIN;
+ }
+ bMaxITA = (u_char)floor((dMCLK + 1) / 2);
+ fRepeatITA = SANE_FALSE;
+ }
+ }
+
+ } else {
+
+ u_short w_max = 0, w_min = 0xffff, w_tmp;
+
+ for( dw = start; dw < end; dw++ ) {
+ if( w_max < ((u_short*)scanbuf)[dw])
+ w_max = ((u_short*)scanbuf)[dw];
+ if( w_min > ((u_short*)scanbuf)[dw])
+ w_min = ((u_short*)scanbuf)[dw];
+ }
+
+ w_tmp = w_max;
+ if( usb_IsCISDevice(dev))
+ w_tmp = w_min;
+
+ regs[0x3b] =
+ regs[0x3c] =
+ regs[0x3d] = usb_GetNewGain(dev, w_tmp, 0);
+
+ DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max );
+ DBG(_DBG_INFO2, "MIN(G)= 0x%04x(%u)\n", w_min, w_min );
+ DBG(_DBG_INFO2, "CUR(G)= 0x%04x(%u)\n", w_tmp, w_tmp );
+
+/* m_dwIdealGain = IDEAL_GainNormal;
+ */
+ if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+
+ SANE_Bool adj = SANE_FALSE;
+
+ /* on CIS devices, we can control the lamp off settings */
+ if( usb_IsCISDevice(dev)) {
+
+ if( adjLampSetting( dev, CHANNEL_green, w_tmp, m_dwIdealGain,
+ hw->green_lamp_on, &hw->green_lamp_off )) {
+ adj = SANE_TRUE;
+ }
+
+ /* on any adjustment, set the registers... */
+ if( adj ) {
+ usb_AdjustLamps( dev, SANE_TRUE );
+
+ if( i < _MAX_GAIN_LOOPS )
+ goto TOGAIN;
+ }
+
+ } else {
+
+ if( !regs[0x3b] && (dMCLK > min_mclk)) {
+
+ scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ adj = SANE_TRUE;
+
+ } else if((regs[0x3b] == 63) && (dMCLK < 20)) {
+
+ scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ adj = SANE_TRUE;
+ }
+
+ if( adj ) {
+ if( i < _MAX_GAIN_LOOPS )
+ goto TOGAIN;
+ }
+ }
+ }
+ }
+ }
+
+ DBG( _DBG_INFO2, "REG[0x3b] = %u\n", regs[0x3b] );
+ DBG( _DBG_INFO2, "REG[0x3c] = %u\n", regs[0x3c] );
+ DBG( _DBG_INFO2, "REG[0x3d] = %u\n", regs[0x3d] );
+
+ DBG( _DBG_INFO2, "red_lamp_on = %u\n", hw->red_lamp_on );
+ DBG( _DBG_INFO2, "red_lamp_off = %u\n", hw->red_lamp_off );
+ DBG( _DBG_INFO2, "green_lamp_on = %u\n", hw->green_lamp_on );
+ DBG( _DBG_INFO2, "green_lamp_off = %u\n", hw->green_lamp_off );
+ DBG( _DBG_INFO2, "blue_lamp_on = %u\n", hw->blue_lamp_on );
+ DBG( _DBG_INFO2, "blue_lamp_off = %u\n", hw->blue_lamp_off );
+
+ DBG( _DBG_INFO, "usb_AdjustGain() done.\n" );
+ return SANE_TRUE;
+}
+
+/** usb_GetNewOffset
+ * @param pdwSum -
+ * @param pdwDiff -
+ * @param pcOffset -
+ * @param pIdeal -
+ * @param channel -
+ * @param cAdjust -
+ */
+static void usb_GetNewOffset( Plustek_Device *dev, u_long *pdwSum, u_long *pdwDiff,
+ signed char *pcOffset, u_char *pIdeal,
+ u_long channel, signed char cAdjust )
+{
+ /* IDEAL_Offset is currently set to 0x1000 = 4096 */
+ u_long dwIdealOffset = IDEAL_Offset;
+
+ if( pdwSum[channel] > dwIdealOffset ) {
+
+ /* Over ideal value */
+ pdwSum[channel] -= dwIdealOffset;
+ if( pdwSum[channel] < pdwDiff[channel] ) {
+ /* New offset is better than old one */
+ pdwDiff[channel] = pdwSum[channel];
+ pIdeal[channel] = dev->usbDev.a_bRegs[0x38 + channel];
+ }
+ pcOffset[channel] -= cAdjust;
+
+ } else {
+
+ /* Below than ideal value */
+ pdwSum[channel] = dwIdealOffset - pdwSum [channel];
+ if( pdwSum[channel] < pdwDiff[channel] ) {
+ /* New offset is better than old one */
+ pdwDiff[channel] = pdwSum[channel];
+ pIdeal[channel] = dev->usbDev.a_bRegs[0x38 + channel];
+ }
+ pcOffset[channel] += cAdjust;
+ }
+
+ if( pcOffset[channel] >= 0 )
+ dev->usbDev.a_bRegs[0x38 + channel] = pcOffset[channel];
+ else
+ dev->usbDev.a_bRegs[0x38 + channel] = (u_char)(32 - pcOffset[channel]);
+}
+
+/** usb_AdjustOffset
+ * function to perform the "coarse calibration step" part 2.
+ * We scan reference image pixels to determine the optimum coarse offset settings
+ * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
+ * applied at the line rate during normal scanning.
+ * On CIS based devices, we switch the light off, on CCD devices, we use the optical
+ * black pixels.
+ * Affects register 0x38, 0x39 and 0x3a
+ */
+static SANE_Bool usb_AdjustOffset( Plustek_Device *dev )
+{
+ char tmp[40];
+ signed char cAdjust = 16;
+ signed char cOffset[3];
+ u_char bExpect[3];
+ int i;
+ u_long dw, dwPixels;
+ u_long dwDiff[3], dwSum[3];
+
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+ u_long *scanbuf = dev->scanning.pScanBuffer;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ DBG( _DBG_INFO, "#########################\n" );
+ DBG( _DBG_INFO, "usb_AdjustOffset()\n" );
+ if((dev->adj.rofs != -1) &&
+ (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
+ regs[0x38] = (dev->adj.rofs & 0x3f);
+ regs[0x39] = (dev->adj.gofs & 0x3f);
+ regs[0x3a] = (dev->adj.bofs & 0x3f);
+ DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
+ return SANE_TRUE;
+ }
+
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = 2550;
+
+ if( usb_IsCISDevice(dev))
+ dwPixels = m_ScanParam.Size.dwPixels;
+ else
+ dwPixels = (u_long)(hw->bOpticBlackEnd - hw->bOpticBlackStart );
+
+ m_ScanParam.Size.dwPixels = 2550;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2 *
+ m_ScanParam.bChannels;
+ if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL /
+ dev->usbDev.Caps.OpticDpi.x);
+ m_ScanParam.bCalibration = PARAM_Offset;
+ m_ScanParam.dMCLK = dMCLK;
+
+ dwDiff[0] = dwDiff[1] = dwDiff[2] = 0xffff;
+ cOffset[0] = cOffset[1] = cOffset[2] = 0;
+ bExpect[0] = bExpect[1] = bExpect[2] = 0;
+
+ regs[0x38] = regs[0x39] = regs[0x3a] = 0;
+
+ if( usb_IsCISDevice(dev)) {
+ /*
+ * if we have dark shading strip, there's no need to switch
+ * the lamp off
+ */
+ if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
+
+ usb_ModuleToHome( dev, SANE_TRUE );
+ usb_ModuleMove ( dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->DarkShadOrgY );
+
+ regs[0x45] &= ~0x10;
+
+ } else {
+
+ /* switch lamp off to read dark data... */
+ regs[0x29] = 0;
+ usb_switchLamp( dev, SANE_FALSE );
+ }
+ }
+
+ if( 0 == dwPixels ) {
+ DBG( _DBG_ERROR, "OpticBlackEnd = OpticBlackStart!!!\n" );
+ return SANE_FALSE;
+ }
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam )) {
+ DBG( _DBG_ERROR, "usb_AdjustOffset() failed\n" );
+ return SANE_FALSE;
+ }
+
+ i = 0;
+
+ DBG( _DBG_INFO2, "S.dwPixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "dwPixels = %lu\n", dwPixels );
+ DBG( _DBG_INFO2, "dwPhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+
+ while( cAdjust ) {
+
+ /*
+ * read data (a white calibration strip - hopefully ;-)
+ */
+ if((!usb_ScanBegin(dev, SANE_FALSE)) ||
+ (!usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes)) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "usb_AdjustOffset() failed\n" );
+ return SANE_FALSE;
+ }
+
+ sprintf( tmp, "coarse-off-%u.raw", i++ );
+
+#ifdef SWAP_COARSE
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+#endif
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ dwSum[0] = dwSum[1] = dwSum[2] = 0;
+
+ for (dw = 0; dw < dwPixels; dw++) {
+#ifndef SWAP_COARSE
+ dwSum[0] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[0]);
+ dwSum[1] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[1]);
+ dwSum[2] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[2]);
+#else
+ dwSum[0] += ((RGBUShortDef*)scanbuf)[dw].Red;
+ dwSum[1] += ((RGBUShortDef*)scanbuf)[dw].Green;
+ dwSum[2] += ((RGBUShortDef*)scanbuf)[dw].Blue;
+#endif
+ }
+
+ DBG( _DBG_INFO2, "RedSum = %lu, ave = %lu\n",
+ dwSum[0], dwSum[0] /dwPixels );
+ DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu\n",
+ dwSum[1], dwSum[1] /dwPixels );
+ DBG( _DBG_INFO2, "BlueSum = %lu, ave = %lu\n",
+ dwSum[2], dwSum[2] /dwPixels );
+
+ /* do averaging for each channel */
+ dwSum[0] /= dwPixels;
+ dwSum[1] /= dwPixels;
+ dwSum[2] /= dwPixels;
+
+ usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 0, cAdjust );
+ usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 1, cAdjust );
+ usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 2, cAdjust );
+
+ DBG( _DBG_INFO2, "RedExpect = %u\n", bExpect[0] );
+ DBG( _DBG_INFO2, "GreenExpect = %u\n", bExpect[1] );
+ DBG( _DBG_INFO2, "BlueExpect = %u\n", bExpect[2] );
+
+ } else {
+ dwSum[0] = 0;
+
+ for( dw = 0; dw < dwPixels; dw++ ) {
+#ifndef SWAP_COARSE
+ dwSum[0] += (u_long)_HILO2WORD(((HiLoDef*)scanbuf)[dw]);
+#else
+ dwSum[0] += ((u_short*)scanbuf)[dw];
+#endif
+ }
+ dwSum [0] /= dwPixels;
+ usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 0, cAdjust );
+ regs[0x3a] = regs[0x39] = regs[0x38];
+
+ DBG(_DBG_INFO2,"Sum = %lu, ave = %lu\n",dwSum[0],dwSum[0]/dwPixels);
+ DBG(_DBG_INFO2,"Expect = %u\n", bExpect[0]);
+ }
+
+ _UIO(sanei_lm983x_write(dev->fd, 0x38, &regs[0x38], 3, SANE_TRUE));
+ cAdjust >>= 1;
+ }
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ regs[0x38] = bExpect[0];
+ regs[0x39] = bExpect[1];
+ regs[0x3a] = bExpect[2];
+ } else {
+
+ regs[0x38] = regs[0x39] = regs[0x3a] = bExpect[0];
+ }
+
+ DBG( _DBG_INFO2, "REG[0x38] = %u\n", regs[0x38] );
+ DBG( _DBG_INFO2, "REG[0x39] = %u\n", regs[0x39] );
+ DBG( _DBG_INFO2, "REG[0x3a] = %u\n", regs[0x3a] );
+ DBG( _DBG_INFO, "usb_AdjustOffset() done.\n" );
+
+ /* switch it on again on CIS based scanners */
+ if( usb_IsCISDevice(dev)) {
+
+ if( dev->usbDev.pSource->DarkShadOrgY < 0 ) {
+ regs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+ usbio_WriteReg( dev->fd, 0x29, regs[0x29]);
+ }
+ }
+
+ return SANE_TRUE;
+}
+
+/** this function tries to find out some suitable values for the dark
+ * fine calibration. If the device owns a black calibration strip
+ * the data is simply copied. If not, then the white strip is read
+ * with the lamp switched off...
+ */
+static void usb_GetDarkShading( Plustek_Device *dev, u_short *pwDest,
+ HiLoDef *pSrce, u_long dwPixels,
+ u_long dwAdd, int iOffset )
+{
+ u_long dw;
+ u_long dwSum[2];
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
+
+ u_short w;
+ int wtmp;
+
+ /* here we use the source buffer + a static offset */
+ for (dw = 0; dw < dwPixels; dw++, pSrce += dwAdd)
+ {
+#ifndef SWAP_FINE
+ wtmp = ((int)_PHILO2WORD(pSrce) + iOffset);
+#else
+ wtmp = ((int)_PLOHI2WORD(pSrce) + iOffset);
+#endif
+ if( wtmp < 0 )
+ wtmp = 0;
+
+ if( wtmp > 0xffff )
+ wtmp = 0xffff;
+
+ w = (u_short)wtmp;
+
+#ifndef SWAP_FINE
+ pwDest[dw] = _LOBYTE(w) * 256 + _HIBYTE(w);
+#else
+ pwDest[dw] = w;
+#endif
+ }
+ }
+ else
+ {
+ dwSum[0] = dwSum[1] = 0;
+ if( hw->bSensorConfiguration & 0x04 ) {
+
+ /* Even/Odd CCD */
+ for( dw = 0; dw < dwPixels; dw++, pSrce += dwAdd ) {
+#ifndef SWAP_FINE
+ dwSum[dw & 1] += (u_long)_PHILO2WORD(pSrce);
+#else
+ dwSum[dw & 1] += (u_long)_PLOHI2WORD(pSrce);
+#endif
+ }
+ dwSum[0] /= ((dwPixels + 1UL) >> 1);
+ dwSum[1] /= (dwPixels >> 1);
+
+ if( /*Registry.GetEvenOdd() == 1 ||*/ scaps->bPCB == 2)
+ {
+ dwSum[0] = dwSum[1] = (dwSum[0] + dwSum[1]) / 2;
+ }
+
+ dwSum[0] = (int)dwSum[0] + iOffset;
+ dwSum[1] = (int)dwSum[1] + iOffset;
+
+ if((int)dwSum[0] < 0)
+ dwSum[0] = 0;
+
+ if((int)dwSum[1] < 0)
+ dwSum[1] = 0;
+#ifndef SWAP_FINE
+ dwSum[0] = (u_long)_LOBYTE(_LOWORD(dwSum[0])) * 256UL +
+ _HIBYTE(_LOWORD(dwSum[0]));
+ dwSum[1] = (u_long)_LOBYTE(_LOWORD(dwSum[1])) * 256UL +
+ _HIBYTE(_LOWORD(dwSum[1]));
+#else
+ dwSum[0] = (u_long)_LOWORD(dwSum[0]);
+ dwSum[1] = (u_long)_LOWORD(dwSum[1]);
+#endif
+
+ for( dw = 0; dw < dwPixels; dw++ )
+ pwDest[dw] = (u_short)dwSum[dw & 1];
+ } else {
+
+ /* Standard CCD */
+
+ /* do some averaging on the line */
+ for( dw = 0; dw < dwPixels; dw++, pSrce += dwAdd ) {
+#ifndef SWAP_FINE
+ dwSum[0] += (u_long)_PHILO2WORD(pSrce);
+#else
+ dwSum[0] += (u_long)_PLOHI2WORD(pSrce);
+#endif
+ }
+
+ dwSum[0] /= dwPixels;
+
+ /* add our offset... */
+ dwSum[0] = (int)dwSum[0] + iOffset;
+ if((int)dwSum[0] < 0)
+ dwSum[0] = 0;
+#ifndef SWAP_FINE
+ dwSum[0] = (u_long)_LOBYTE(_LOWORD(dwSum[0])) * 256UL +
+ _HIBYTE(_LOWORD(dwSum[0]));
+#else
+ dwSum[0] = (u_long)_LOWORD(dwSum[0]);
+#endif
+
+ /* fill the shading data */
+ for( dw = 0; dw < dwPixels; dw++ )
+ pwDest[dw] = (u_short)dwSum[0];
+ }
+ }
+#ifdef SWAP_FINE
+ if(usb_HostSwap())
+ usb_Swap( pwDest, dwPixels *2 );
+#endif
+}
+
+/** usb_AdjustDarkShading
+ * fine calibration part 1 - read the black calibration area and write
+ * the black line data to the offset coefficient data in Merlins' DRAM
+ * If there's no black line available, we can use the min pixel value
+ * from coarse calibration...
+ */
+static SANE_Bool usb_AdjustDarkShading( Plustek_Device *dev )
+{
+ char tmp[40];
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_long *scanbuf = scanning->pScanBuffer;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ if( scaps->workaroundFlag & _WAF_SKIP_FINE )
+ return SANE_TRUE;
+
+ DBG( _DBG_INFO, "#########################\n" );
+ DBG( _DBG_INFO, "usb_AdjustDarkShading()\n" );
+ DBG( _DBG_INFO2, "* MCLK = %f (scanparam-MCLK=%f)\n",
+ dMCLK, scanning->sParam.dMCLK );
+
+ usb_PrepareFineCal( dev, &m_ScanParam, 0 );
+
+ m_ScanParam.Size.dwLines = 1; /* for gain */
+ m_ScanParam.bCalibration = PARAM_DarkShading;
+
+ if( _LM9831 == hw->chip ) {
+
+ m_ScanParam.UserDpi.x = usb_SetAsicDpiX( dev, m_ScanParam.UserDpi.x);
+ if( m_ScanParam.UserDpi.x < 100)
+ m_ScanParam.UserDpi.x = 150;
+
+ /* Now DPI X is physical */
+ m_ScanParam.Origin.x = m_ScanParam.Origin.x %
+ (u_short)m_dHDPIDivider;
+ m_ScanParam.Size.dwPixels = (u_long)scaps->Normal.Size.x *
+ m_ScanParam.UserDpi.x / 300UL;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
+ 2UL * m_ScanParam.bChannels;
+ m_dwPixels = scanning->sParam.Size.dwPixels *
+ m_ScanParam.UserDpi.x / scanning->sParam.UserDpi.x;
+
+ if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *= 3;
+ }
+
+ /* if we have dark shading strip, there's no need to switch
+ * the lamp off
+ */
+ if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
+
+ usb_ModuleToHome( dev, SANE_TRUE );
+ usb_ModuleMove ( dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->DarkShadOrgY );
+ } else {
+
+ /* switch lamp off to read dark data... */
+ regs[0x29] = 0;
+ usb_switchLamp( dev, SANE_FALSE );
+ }
+
+ usb_SetScanParameters( dev, &m_ScanParam );
+
+ if((!usb_ScanBegin(dev, SANE_FALSE)) ||
+ (!usb_ScanReadImage(dev,scanbuf, m_ScanParam.Size.dwPhyBytes)) ||
+ (!usb_ScanEnd( dev ))) {
+
+ /* on error, reset the lamp settings*/
+ regs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+ usbio_WriteReg( dev->fd, 0x29, regs[0x29] );
+
+ DBG( _DBG_ERROR, "usb_AdjustDarkShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+ /* set illumination mode and switch lamp on again
+ */
+ regs[0x29] = hw->bReg_0x29;
+ usb_switchLamp( dev, SANE_TRUE );
+
+ if( !usbio_WriteReg( dev->fd, 0x29, regs[0x29])) {
+ DBG( _DBG_ERROR, "usb_AdjustDarkShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+#ifdef SWAP_FINE
+ if(usb_HostSwap())
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+#endif
+
+ sprintf( tmp, "fine-black.raw" );
+
+ dumpPicInit(&m_ScanParam, tmp);
+ dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
+
+ usleep(500 * 1000); /* Warm up lamp again */
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ if( usb_IsCISDevice(dev)) {
+
+ usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
+ m_ScanParam.Size.dwPhyPixels, 1,
+ scanning->sParam.swOffset[0]);
+
+ usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
+ (HiLoDef*)scanbuf + m_ScanParam.Size.dwPhyPixels,
+ m_ScanParam.Size.dwPhyPixels, 1, scanning->sParam.swOffset[1]);
+
+ usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
+ (HiLoDef*)scanbuf + m_ScanParam.Size.dwPhyPixels * 2,
+ m_ScanParam.Size.dwPhyPixels, 1, scanning->sParam.swOffset[2]);
+
+ } else {
+
+ usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
+ m_ScanParam.Size.dwPhyPixels, 3,
+ scanning->sParam.swOffset[0]);
+ usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
+ (HiLoDef*)scanbuf + 1, m_ScanParam.Size.dwPhyPixels,
+ 3, scanning->sParam.swOffset[1]);
+ usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
+ (HiLoDef*)scanbuf + 2, m_ScanParam.Size.dwPhyPixels,
+ 3, scanning->sParam.swOffset[2]);
+ }
+ } else {
+
+ usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
+ m_ScanParam.Size.dwPhyPixels, 1,
+ scanning->sParam.swOffset[1]);
+
+ memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
+ a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
+ memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
+ a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
+ }
+
+ regs[0x45] |= 0x10;
+
+ usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
+ scanning->sParam.bDataType == SCANDATATYPE_Color?1:0);
+ return SANE_TRUE;
+}
+
+/** function to remove the brightest values out of each row
+ * @param dev - the almighty device structure.
+ * @param sp - is a pointer to the scanparam structure used for
+ * scanning the shading lines.
+ * @param hilight - defines the number of values to skip.
+ * @param shading_lines - defines the overall number of shading lines.
+ */
+static void usb_CalSortHighlight( Plustek_Device *dev, ScanParam *sp,
+ u_long hilight, u_long shading_lines )
+{
+ ScanDef *scan = &dev->scanning;
+ u_short r, g, b;
+ u_long lines, w, x;
+ RGBUShortDef *pw, *rgb;
+
+ if( hilight == 0 )
+ return;
+
+ rgb = (RGBUShortDef*)scan->pScanBuffer;
+
+ /* do it for all relevant lines */
+ for( lines = hilight, rgb = rgb + sp->Size.dwPhyPixels * lines;
+ lines < shading_lines; lines++, rgb += sp->Size.dwPhyPixels ) {
+
+ /* scan the complete line */
+ for( x = 0; x < sp->Size.dwPhyPixels; x++ ) {
+
+ /* reference is the first scanline */
+ pw = (RGBUShortDef*)scan->pScanBuffer;
+ r = rgb[x].Red;
+ g = rgb[x].Green;
+ b = rgb[x].Blue;
+
+ for( w = 0; w < hilight; w++, pw += sp->Size.dwPhyPixels ) {
+
+ if( r > pw[x].Red )
+ _SWAP( r, pw[x].Red );
+
+ if( g > pw[x].Green )
+ _SWAP( g, pw[x].Green );
+
+ if( b > pw[x].Blue )
+ _SWAP( b, pw[x].Blue );
+ }
+ rgb[x].Red = r;
+ rgb[x].Green = g;
+ rgb[x].Blue = b;
+ }
+ }
+}
+
+/** function to remove the brightest values out of each row
+ * @param dev - the almighty device structure.
+ * @param sp - is a pointer to the scanparam structure used for
+ * scanning the shading lines.
+ * @param hilight - defines the number of values to skip.
+ * @param shading_lines - defines the overall number of shading lines.
+ */
+static void usb_CalSortShadow( Plustek_Device *dev, ScanParam *sp,
+ u_long hilight, u_long shadow, u_long shading_lines )
+{
+ ScanDef *scan = &dev->scanning;
+ u_short r, g, b;
+ u_long lines, w, x;
+ RGBUShortDef *pw, *rgb;
+
+ if( shadow == 0 )
+ return;
+
+ rgb = (RGBUShortDef*)scan->pScanBuffer;
+
+ for( lines = hilight, rgb = rgb + sp->Size.dwPhyPixels * lines;
+ lines < shading_lines-shadow; lines++, rgb += sp->Size.dwPhyPixels ) {
+
+ for (x = 0; x < sp->Size.dwPhyPixels; x++) {
+
+ pw = ((RGBUShortDef*)scan->pScanBuffer) + (shading_lines - shadow) *
+ sp->Size.dwPhyPixels;
+ r = rgb[x].Red;
+ g = rgb[x].Green;
+ b = rgb[x].Blue;
+
+ for( w = 0; w < shadow; w++, pw += sp->Size.dwPhyPixels ) {
+ if( r < pw[x].Red )
+ _SWAP( r, pw[x].Red );
+ if( g < pw[x].Green )
+ _SWAP( g, pw [x].Green );
+ if( b > pw[x].Blue )
+ _SWAP( b, pw[x].Blue );
+ }
+ rgb[x].Red = r;
+ rgb[x].Green = g;
+ rgb[x].Blue = b;
+ }
+ }
+}
+
+static void usb_procHighlightAndShadow( Plustek_Device *dev, ScanParam *sp,
+ u_long hilight, u_long shadow, u_long shading_lines )
+{
+ ScanDef *scan = &dev->scanning;
+ u_long lines, x;
+ u_long *pr, *pg, *pb;
+ RGBUShortDef *rgb;
+
+ pr = (u_long*)((u_char*)scan->pScanBuffer + sp->Size.dwPhyBytes * shading_lines);
+ pg = pr + sp->Size.dwPhyPixels;
+ pb = pg + sp->Size.dwPhyPixels;
+
+ memset(pr, 0, sp->Size.dwPhyPixels * 4UL * 3UL);
+
+ /* Sort hilight */
+ usb_CalSortHighlight(dev, sp, hilight, shading_lines);
+
+ /* Sort shadow */
+ usb_CalSortShadow(dev, sp, hilight, shadow, shading_lines);
+
+ rgb = (RGBUShortDef*)scan->pScanBuffer;
+ rgb += sp->Size.dwPhyPixels * hilight;
+
+ /* Sum */
+ for( lines = hilight; lines < (shading_lines-shadow); lines++ ) {
+
+ for( x = 0; x < sp->Size.dwPhyPixels; x++ ) {
+ pr[x] += rgb[x].Red;
+ pg[x] += rgb[x].Green;
+ pb[x] += rgb[x].Blue;
+ }
+
+ rgb += sp->Size.dwPhyPixels;
+ }
+}
+
+/** usb_AdjustWhiteShading
+ * fine calibration part 2 - read the white calibration area and calculate
+ * the gain coefficient for each pixel
+ */
+static SANE_Bool usb_AdjustWhiteShading( Plustek_Device *dev )
+{
+ char tmp[40];
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_long *pBuf = scan->pScanBuffer;
+ u_long dw, dwLines, dwRead;
+ u_long shading_lines;
+ MonoWordDef *pValue;
+ u_short *m_pAvMono;
+ u_long *pdw, *m_pSum;
+ u_short hilight, shadow;
+ int i;
+ SANE_Bool swap = usb_HostSwap();
+
+ if( scaps->workaroundFlag & _WAF_SKIP_FINE )
+ return SANE_TRUE;
+
+ DBG( _DBG_INFO, "#########################\n" );
+ DBG( _DBG_INFO, "usb_AdjustWhiteShading()\n" );
+
+ m_pAvMono = (u_short*)scan->pScanBuffer;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ usb_PrepareFineCal( dev, &m_ScanParam, 0 );
+
+ if( m_ScanParam.PhyDpi.x > 75)
+ shading_lines = 64;
+ else
+ shading_lines = 32;
+
+ /* NOTE: hilight + shadow < shading_lines */
+ hilight = 4;
+ shadow = 4;
+
+ m_ScanParam.bCalibration = PARAM_WhiteShading;
+ m_ScanParam.Size.dwLines = shading_lines;
+
+ if( _LM9831 == hw->chip ) {
+
+ m_ScanParam.UserDpi.x = usb_SetAsicDpiX( dev, m_ScanParam.UserDpi.x);
+ if( m_ScanParam.UserDpi.x < 100 )
+ m_ScanParam.UserDpi.x = 150;
+
+ /* Now DPI X is physical */
+ m_ScanParam.Origin.x = m_ScanParam.Origin.x % (u_short)m_dHDPIDivider;
+ m_ScanParam.Size.dwPixels = (u_long)scaps->Normal.Size.x * m_ScanParam.UserDpi.x / 300UL;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2UL * m_ScanParam.bChannels;
+ if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_dwPixels = scan->sParam.Size.dwPixels * m_ScanParam.UserDpi.x /
+ scan->sParam.UserDpi.x;
+
+ dw = (u_long)(hw->wDRAMSize - 196 /*192 KiB*/) * 1024UL;
+ for( dwLines = dw / m_ScanParam.Size.dwBytes;
+ dwLines < m_ScanParam.Size.dwLines; m_ScanParam.Size.dwLines>>=1);
+ }
+
+ /* goto the correct position again... */
+ if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
+
+ usb_ModuleToHome( dev, SANE_TRUE );
+ usb_ModuleMove ( dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->ShadingOriginY );
+ }
+
+ sprintf( tmp, "fine-white.raw" );
+ DBG( _DBG_INFO2, "FINE WHITE Calibration Strip: %s\n", tmp );
+ DBG( _DBG_INFO2, "Shad.-Lines = %lu\n", shading_lines );
+ DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
+
+ for( dw = shading_lines, dwRead = 0; dw; dw -= m_ScanParam.Size.dwLines ) {
+
+ if( usb_SetScanParameters( dev, &m_ScanParam ) &&
+ usb_ScanBegin( dev, SANE_FALSE )) {
+
+ DBG(_DBG_INFO2,"TotalBytes = %lu\n",m_ScanParam.Size.dwTotalBytes);
+ if( _LM9831 == hw->chip ) {
+ /* Delay for white shading hold for 9831-1200 scanner */
+ usleep(900000);
+ }
+
+ if( usb_ScanReadImage( dev, (u_char*)pBuf + dwRead,
+ m_ScanParam.Size.dwTotalBytes)) {
+
+ if( _LM9831 == hw->chip ) {
+ /* Delay for white shading hold for 9831-1200 scanner */
+ usleep(10000);
+ }
+
+ if( 0 == dwRead ) {
+ dumpPicInit(&m_ScanParam, tmp);
+ }
+
+ dumpPic(tmp, (u_char*)pBuf + dwRead, m_ScanParam.Size.dwTotalBytes, 0);
+
+ if( usb_ScanEnd( dev )) {
+ dwRead += m_ScanParam.Size.dwTotalBytes;
+ continue;
+ }
+ }
+ }
+
+ DBG( _DBG_ERROR, "usb_AdjustWhiteShading() failed\n" );
+ return SANE_FALSE;
+ }
+
+ m_pSum = (u_long*)((u_char*)pBuf + m_ScanParam.Size.dwPhyBytes * shading_lines);
+
+ /*
+ * do some reordering on CIS based devices:
+ * from RRRRRRR.... GGGGGGGG.... BBBBBBBBB, create RGB RGB RGB ...
+ * to use the following code, originally written for CCD devices...
+ */
+ if( usb_IsCISDevice(dev)) {
+
+ u_short *dest, *src;
+ u_long dww;
+
+ src = (u_short*)pBuf;
+
+ DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
+ DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
+ DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
+ DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
+ DBG( _DBG_INFO2, "Channels = %u\n", m_ScanParam.bChannels );
+
+ for( dwLines = shading_lines; dwLines; dwLines-- ) {
+
+ dest = a_wWhiteShading;
+
+ for( dw=dww=0; dw < m_ScanParam.Size.dwPhyPixels; dw++, dww+=3 ) {
+
+ dest[dww] = src[dw];
+ dest[dww + 1] = src[m_ScanParam.Size.dwPhyPixels + dw];
+ dest[dww + 2] = src[m_ScanParam.Size.dwPhyPixels * 2 + dw];
+ }
+
+ /* copy line back ... */
+ memcpy( src, dest, m_ScanParam.Size.dwPhyPixels * 3 * 2 );
+ src = &src[m_ScanParam.Size.dwPhyPixels * 3];
+ }
+
+ m_ScanParam.bChannels = 3;
+ }
+
+ if( _LM9831 == hw->chip ) {
+
+ u_short *pwDest = (u_short*)pBuf;
+ HiLoDef *pwSrce = (HiLoDef*)pBuf;
+
+ pwSrce += ((u_long)(scan->sParam.Origin.x-m_ScanParam.Origin.x) /
+ (u_short)m_dHDPIDivider) *
+ (scaps->OpticDpi.x / 300UL) * m_ScanParam.bChannels;
+
+ for( dwLines = shading_lines; dwLines; dwLines--) {
+
+#ifdef SWAP_FINE
+ if(usb_HostSwap()) {
+#endif
+ for( dw = 0; dw < m_dwPixels * m_ScanParam.bChannels; dw++ )
+ pwDest[dw] = _HILO2WORD(pwSrce[dw]);
+#ifdef SWAP_FINE
+ } else {
+ for( dw = 0; dw < m_dwPixels * m_ScanParam.bChannels; dw++ )
+ pwDest[dw] = _LOHI2WORD(pwSrce[dw]);
+ }
+#endif
+ pwDest += (u_long)m_dwPixels * m_ScanParam.bChannels;
+ pwSrce = (HiLoDef*)((u_char*)pwSrce + m_ScanParam.Size.dwPhyBytes);
+ }
+
+ _SWAP(m_ScanParam.Size.dwPhyPixels, m_dwPixels);
+ } else {
+ /* Discard the status word and conv. the hi-lo order to intel format */
+ u_short *pwDest = (u_short*)pBuf;
+ HiLoDef *pwSrce = (HiLoDef*)pBuf;
+
+ for( dwLines = shading_lines; dwLines; dwLines-- ) {
+
+#ifdef SWAP_FINE
+ if(usb_HostSwap()) {
+#endif
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels *
+ m_ScanParam.bChannels; dw++) {
+ pwDest[dw] = _HILO2WORD(pwSrce[dw]);
+ }
+#ifdef SWAP_FINE
+ } else {
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels *
+ m_ScanParam.bChannels; dw++) {
+ pwDest[dw] = _LOHI2WORD(pwSrce[dw]);
+ }
+ }
+#endif
+ pwDest += m_ScanParam.Size.dwPhyPixels * m_ScanParam.bChannels;
+ pwSrce = (HiLoDef*)((u_char*)pwSrce + m_ScanParam.Size.dwPhyBytes);
+ }
+ }
+
+ if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
+
+ usb_procHighlightAndShadow(dev, &m_ScanParam, hilight, shadow, shading_lines);
+
+ pValue = (MonoWordDef*)a_wWhiteShading;
+ pdw = (u_long*)m_pSum;
+
+ /* Software gain */
+ if( scan->sParam.bSource != SOURCE_Negative ) {
+
+ for( i = 0; i < 3; i++ ) {
+
+ for(dw=m_ScanParam.Size.dwPhyPixels; dw; dw--,pValue++,pdw++) {
+
+ *pdw = *pdw * 1000 / ((shading_lines - hilight - shadow) *
+ scan->sParam.swGain[i]);
+ if(*pdw > 65535U)
+ pValue->Mono = 65535U;
+ else
+ pValue->Mono = (u_short)*pdw;
+
+ if (pValue->Mono > 16384U)
+ pValue->Mono = (u_short)(GAIN_Target * 16384U / pValue->Mono);
+ else
+ pValue->Mono = GAIN_Target;
+
+#ifdef SWAP_FINE
+ if( swap )
+#endif
+ _SWAP(pValue->HiLo.bHi, pValue->HiLo.bLo);
+ }
+ }
+ } else {
+ for( dw = m_ScanParam.Size.dwPhyPixels*3; dw; dw--,pValue++,pdw++)
+ pValue->Mono=(u_short)(*pdw/(shading_lines-hilight-shadow));
+
+ /* swapping will be done later in usb_ResizeWhiteShading() */
+ }
+ } else {
+
+ /* gray mode */
+ u_short *pwAv, *pw;
+ u_short w, wV;
+
+ memset( m_pSum, 0, m_ScanParam.Size.dwPhyPixels << 2 );
+ if( hilight ) {
+ for( dwLines = hilight,
+ pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
+ dwLines < shading_lines;
+ dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels) {
+
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
+
+ pw = m_pAvMono;
+ wV = pwAv [dw];
+ for( w = 0; w < hilight; w++,
+ pw += m_ScanParam.Size.dwPhyPixels ) {
+ if( wV > pw[dw] )
+ _SWAP( wV, pw[dw] );
+ }
+ pwAv[dw] = wV;
+ }
+ }
+ }
+
+ /* Sort shadow */
+ if (shadow) {
+ for (dwLines = hilight, pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
+ dwLines < (shading_lines - shadow); dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels)
+ for (dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++)
+ {
+ pw = m_pAvMono + (shading_lines - shadow) * m_ScanParam.Size.dwPhyPixels;
+ wV = pwAv [dw];
+ for (w = 0; w < shadow; w++, pw += m_ScanParam.Size.dwPhyPixels)
+ if (wV < pw [dw])
+ _SWAP (wV, pw[dw]);
+ pwAv [dw] = wV;
+ }
+ }
+
+ /* Sum */
+ pdw = (u_long*)m_pSum;
+
+ for (dwLines = hilight,
+ pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
+ dwLines < (shading_lines - shadow);
+ dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels) {
+ for (dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++)
+ pdw[dw] += pwAv[dw];
+ }
+
+ /* Software gain */
+ pValue = (MonoWordDef*)a_wWhiteShading;
+ if( scan->sParam.bSource != SOURCE_Negative ) {
+
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++) {
+
+ pdw[dw] = pdw[dw] * 1000 /((shading_lines-hilight-shadow) *
+ scan->sParam.swGain[1]);
+ if( pdw[dw] > 65535U )
+ pValue[dw].Mono = 65535;
+ else
+ pValue[dw].Mono = (u_short)pdw[dw];
+
+ if( pValue[dw].Mono > 16384U ) {
+ pValue[dw].Mono = (u_short)(GAIN_Target * 16384U / pValue[dw].Mono);
+ } else {
+ pValue[dw].Mono = GAIN_Target;
+ }
+
+#ifdef SWAP_FINE
+ if( swap )
+#endif
+ _SWAP(pValue[dw].HiLo.bHi, pValue[dw].HiLo.bLo);
+ }
+
+ } else{
+
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
+ pValue[dw].Mono = (u_short)(pdw[dw] /
+ (shading_lines - hilight - shadow));
+
+ /* swapping will be done later in usb_ResizeWhiteShading() */
+ }
+ }
+ }
+
+ usb_SaveCalSetShading( dev, &m_ScanParam );
+
+ if( scan->sParam.bSource != SOURCE_Negative ) {
+ usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
+ scan->sParam.bDataType == SCANDATATYPE_Color?1:0);
+ }
+ return SANE_TRUE;
+}
+
+/** for negative film only
+ * we need to resize the gain to obtain bright white...
+ */
+static void usb_ResizeWhiteShading( double dAmp, u_short *pwShading, int iGain )
+{
+ u_long dw, dwAmp;
+ u_short w;
+
+ DBG( _DBG_INFO2, "ResizeWhiteShading: dAmp=%.3f, iGain=%i\n", dAmp, iGain );
+
+ for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
+
+ dwAmp = (u_long)(GAIN_Target * 0x4000 /
+ (pwShading[dw] + 1) * dAmp) * iGain / 1000;
+
+ if( dwAmp <= GAIN_Target)
+ w = (u_short)dwAmp;
+ else
+ w = GAIN_Target;
+
+#ifndef SWAP_FINE
+ pwShading[dw] = (u_short)_LOBYTE(w) * 256 + _HIBYTE(w);
+#else
+ pwShading[dw] = w;
+#endif
+ }
+
+#ifdef SWAP_FINE
+ if( usb_HostSwap())
+ usb_Swap( pwShading, m_ScanParam.Size.dwPhyPixels );
+#endif
+}
+
+/** do the base settings for calibration scans
+ */
+static void
+usb_PrepareCalibration( Plustek_Device *dev )
+{
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ usb_GetSWOffsetGain( dev );
+
+ memset( &m_ScanParam, 0, sizeof(ScanParam));
+
+ m_ScanParam.UserDpi = scaps->OpticDpi;
+ m_ScanParam.PhyDpi = scaps->OpticDpi;
+ m_ScanParam.bChannels = scan->sParam.bChannels;
+ m_ScanParam.bBitDepth = 16;
+ m_ScanParam.bSource = scan->sParam.bSource;
+ m_ScanParam.Origin.y = 0;
+
+ if( scan->sParam.bDataType == SCANDATATYPE_Color )
+ m_ScanParam.bDataType = SCANDATATYPE_Color;
+ else
+ m_ScanParam.bDataType = SCANDATATYPE_Gray;
+
+ usb_SetMCLK( dev, &m_ScanParam );
+
+ /* preset these registers offset/gain */
+ regs[0x38] = regs[0x39] = regs[0x3a] = 0;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+ regs[0x45] &= ~0x10;
+
+ memset( a_wWhiteShading, 0, _SHADING_BUF );
+ memset( a_wDarkShading, 0, _SHADING_BUF );
+
+ scan->skipCoarseCalib = SANE_FALSE;
+
+ if( dev->adj.cacheCalData )
+ if( usb_ReadAndSetCalData( dev ))
+ scan->skipCoarseCalib = SANE_TRUE;
+
+ /* as sheet-fed device we use the cached values, or
+ * perform the calibration upon request
+ */
+ if( usb_IsSheetFedDevice(dev)) {
+ if( !scan->skipCoarseCalib && !usb_InCalibrationMode(dev)) {
+
+ DBG(_DBG_INFO2,"SHEET-FED device, skip coarse calibration!\n");
+ scan->skipCoarseCalib = SANE_TRUE;
+
+ regs[0x3b] = 0x0a;
+ regs[0x3c] = 0x0a;
+ regs[0x3d] = 0x0a;
+
+ /* use frontend values... */
+ if((dev->adj.rofs != -1) &&
+ (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
+ regs[0x38] = (dev->adj.rofs & 0x3f);
+ regs[0x39] = (dev->adj.gofs & 0x3f);
+ regs[0x3a] = (dev->adj.bofs & 0x3f);
+ }
+
+ if((dev->adj.rgain != -1) &&
+ (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
+ setAdjGain( dev->adj.rgain, &regs[0x3b] );
+ setAdjGain( dev->adj.ggain, &regs[0x3c] );
+ setAdjGain( dev->adj.bgain, &regs[0x3d] );
+ }
+ }
+ }
+}
+
+/**
+ */
+static SANE_Bool
+usb_SpeedTest( Plustek_Device *dev )
+{
+ int i;
+ double s, e, r, tr;
+ struct timeval start, end;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+ u_long *scanbuf = dev->scanning.pScanBuffer;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ bMaxITA = 0xff;
+
+ DBG( 1, "#########################\n" );
+ DBG( 1, "usb_SpeedTest(%d,%lu)\n", dev->initialized, dev->transferRate );
+ if( dev->transferRate != DEFAULT_RATE ) {
+ DBG( 1, "* skipped, using already detected speed: %lu Bytes/s\n",
+ dev->transferRate );
+ return SANE_TRUE;
+ }
+
+ usb_PrepareCalibration( dev );
+ regs[0x38] = regs[0x39] = regs[0x3a] = 0;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ /* define the strip to scan for warming up the lamp, in the end
+ * we always scan the full line, even for TPA
+ */
+ m_ScanParam.bDataType = SCANDATATYPE_Color;
+ m_ScanParam.bCalibration = PARAM_Gain;
+ m_ScanParam.dMCLK = dMCLK;
+ m_ScanParam.bBitDepth = 8;
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
+ 2 * m_ScanParam.bChannels;
+
+ if( usb_IsCISDevice(dev))
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+ r = 0.0;
+ dev->transferRate = 2000000;
+
+ for( i = 0; i < _TLOOPS ; i++ ) {
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam ))
+ return SANE_FALSE;
+
+ if( !usb_ScanBegin( dev, SANE_FALSE )) {
+ DBG( _DBG_ERROR, "usb_SpeedTest() failed\n" );
+ return SANE_FALSE;
+ }
+ if (!usb_IsDataAvailableInDRAM( dev ))
+ return SANE_FALSE;
+
+ m_fFirst = SANE_FALSE;
+ gettimeofday( &start, NULL );
+ usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes );
+ gettimeofday( &end, NULL );
+ usb_ScanEnd( dev );
+ s = (double)start.tv_sec * 1000000.0 + (double)start.tv_usec;
+ e = (double)end.tv_sec * 1000000.0 + (double)end.tv_usec;
+
+ if( e > s )
+ r += (e - s);
+ else
+ r += (s - e);
+ }
+
+ tr = ((double)m_ScanParam.Size.dwPhyBytes * _TLOOPS * 1000000.0)/r;
+ dev->transferRate = (u_long)tr;
+ DBG( 1, "usb_SpeedTest() done - %u loops, %.4fus --> %.4f B/s, %lu\n",
+ _TLOOPS, r, tr, dev->transferRate );
+ return SANE_TRUE;
+}
+
+/** read the white calibration strip until the lamp seems to be stable
+ * the timed warmup will be used, when the warmup time is set to -1
+ */
+static SANE_Bool
+usb_AutoWarmup( Plustek_Device *dev )
+{
+ int i, stable_count;
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_long *scanbuf = scanning->pScanBuffer;
+ u_char *regs = dev->usbDev.a_bRegs;
+ u_long dw, start, end, len;
+ u_long curR, curG, curB;
+ u_long lastR, lastG, lastB;
+ long diffR, diffG, diffB;
+ long thresh = _AUTO_THRESH;
+
+ if( usb_IsEscPressed())
+ return SANE_FALSE;
+
+ bMaxITA = 0xff;
+
+ DBG( _DBG_INFO, "#########################\n" );
+ DBG( _DBG_INFO, "usb_AutoWarmup()\n" );
+
+ if( usb_IsCISDevice(dev)) {
+ DBG( _DBG_INFO, "- function skipped, CIS device!\n" );
+ return SANE_TRUE;
+ }
+
+ if( dev->adj.warmup >= 0 ) {
+ DBG( _DBG_INFO, "- using timed warmup: %ds\n", dev->adj.warmup );
+ if( !usb_Wait4Warmup( dev )) {
+ DBG( _DBG_ERROR, "- CANCEL detected\n" );
+ return SANE_FALSE;
+ }
+ return SANE_TRUE;
+ }
+
+ usb_PrepareCalibration( dev );
+ regs[0x38] = regs[0x39] = regs[0x3a] = 0;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ /* define the strip to scan for warming up the lamp, in the end
+ * we always scan the full line, even for TPA
+ */
+ m_ScanParam.bDataType = SCANDATATYPE_Color;
+ m_ScanParam.bCalibration = PARAM_Gain;
+ m_ScanParam.dMCLK = dMCLK;
+ m_ScanParam.Size.dwLines = 1;
+ m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
+ scaps->OpticDpi.x / 300UL;
+ m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
+ 2 * m_ScanParam.bChannels;
+
+ if( usb_IsCISDevice(dev))
+ m_ScanParam.Size.dwBytes *= 3;
+
+ m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
+ 300UL / scaps->OpticDpi.x);
+
+ stable_count = 0;
+ start = 500;
+ len = m_ScanParam.Size.dwPixels;
+
+ if( scanning->sParam.bSource == SOURCE_Transparency ) {
+ start = scaps->Positive.DataOrigin.x * scaps->OpticDpi.x / 300UL;
+ len = scaps->Positive.Size.x * scaps->OpticDpi.x / 300UL;
+ thresh = _AUTO_TPA_THRESH;
+ }
+ else if( scanning->sParam.bSource == SOURCE_Negative ) {
+ start = scaps->Negative.DataOrigin.x * scaps->OpticDpi.x / 300UL;
+ len = scaps->Negative.Size.x * scaps->OpticDpi.x / 300UL;
+ thresh = _AUTO_TPA_THRESH;
+ }
+ end = start + len;
+ DBG( _DBG_INFO2, "Start=%lu, End=%lu, Len=%lu, Thresh=%li\n",
+ start, end, len, thresh );
+
+ lastR = lastG = lastB = 0;
+ for( i = 0; i < _MAX_AUTO_WARMUP + 1 ; i++ ) {
+
+ if( !usb_SetScanParameters( dev, &m_ScanParam ))
+ return SANE_FALSE;
+
+ if( !usb_ScanBegin( dev, SANE_FALSE ) ||
+ !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
+ !usb_ScanEnd( dev )) {
+ DBG( _DBG_ERROR, "usb_AutoWarmup() failed\n" );
+ return SANE_FALSE;
+ }
+
+#ifdef SWAP_COARSE
+ if(usb_HostSwap())
+#endif
+ usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
+
+ if( end > m_ScanParam.Size.dwPhyPixels )
+ end = m_ScanParam.Size.dwPhyPixels;
+
+ curR = curG = curB = 0;
+ for( dw = start; dw < end; dw++ ) {
+
+ if( usb_IsCISDevice(dev)) {
+ curR += ((u_short*)scanbuf)[dw];
+ curG += ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
+ curB += ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
+ } else {
+ curR += ((RGBUShortDef*)scanbuf)[dw].Red;
+ curG += ((RGBUShortDef*)scanbuf)[dw].Green;
+ curB += ((RGBUShortDef*)scanbuf)[dw].Blue;
+ }
+ }
+ curR /= len;
+ curG /= len;
+ curB /= len;
+
+ diffR = curR - lastR; lastR = curR;
+ diffG = curG - lastG; lastG = curG;
+ diffB = curB - lastB; lastB = curB;
+ DBG( _DBG_INFO2, "%i/%i-AVE(R,G,B)= %lu(%ld), %lu(%ld), %lu(%ld)\n",
+ i, stable_count, curR, diffR, curG, diffG, curB, diffB );
+
+ /* we consider the lamp to be stable,
+ * when the diffs are less than thresh for at least 3 loops
+ */
+ if((diffR < thresh) && (diffG < thresh) && (diffB < thresh)) {
+ if( stable_count > 3 )
+ break;
+ stable_count++;
+ } else {
+ stable_count = 0;
+ }
+
+ /* no need to sleep in the first loop */
+ if((i != 0) && (stable_count == 0))
+ sleep( _AUTO_SLEEP );
+ }
+
+ DBG( _DBG_INFO, "usb_AutoWarmup() done - %u loops\n", i+1 );
+ DBG( _DBG_INFO, "* AVE(R,G,B)= %lu(%ld), %lu(%ld), %lu(%ld)\n",
+ curR, diffR, curG, diffG, curB, diffB );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static int
+usb_DoIt( Plustek_Device *dev )
+{
+ SANE_Bool skip_fine;
+ ScanDef *scan = &dev->scanning;
+
+ DBG( _DBG_INFO, "Settings done, so start...\n" );
+ if( !scan->skipCoarseCalib ) {
+ DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" );
+ if( !usb_AdjustGain(dev, 0)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+ DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" );
+ if( !usb_AdjustOffset(dev)) {
+ DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+ } else {
+ DBG( _DBG_INFO2, "Coarse Calibration skipped, using saved data\n" );
+ }
+
+ skip_fine = SANE_FALSE;
+ if( dev->adj.cacheCalData ) {
+ skip_fine = usb_FineShadingFromFile(dev);
+ }
+
+ if( !skip_fine ) {
+ DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" );
+ if( !usb_AdjustDarkShading(dev)) {
+ DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+ DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" );
+ if( !usb_AdjustWhiteShading(dev)) {
+ DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
+ return _E_INTERNAL;
+ }
+ } else {
+ DBG( _DBG_INFO2, "###### FINE calibration skipped #######\n" );
+
+ m_ScanParam = scan->sParam;
+ usb_GetPhyPixels( dev, &m_ScanParam );
+
+ usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
+ m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
+ usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
+ m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
+
+/* dev->usbDev.a_bRegs[0x45] &= ~0x10;*/
+ }
+ return 0;
+}
+
+/** usb_DoCalibration
+ */
+static int
+usb_DoCalibration( Plustek_Device *dev )
+{
+ int result;
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+ double dRed, dGreen, dBlue;
+
+ DBG( _DBG_INFO, "usb_DoCalibration()\n" );
+
+ if( SANE_TRUE == scanning->fCalibrated )
+ return SANE_TRUE;
+
+ /* Go to shading position
+ */
+ DBG( _DBG_INFO, "...goto shading position\n" );
+
+ /* HEINER: Currently not clear why Plustek didn't use the ShadingOriginY
+ * for all modes
+ * It should be okay to remove this and reference to the ShadingOriginY
+ */
+#if 0
+ if( scanning->sParam.bSource == SOURCE_Negative ) {
+
+ DBG( _DBG_INFO, "DataOrigin.x=%u, DataOrigin.y=%u\n",
+ dev->usbDev.pSource->DataOrigin.x, dev->usbDev.pSource->DataOrigin.y);
+ if(!usb_ModuleMove( dev, MOVE_Forward,
+ (dev->usbDev.pSource->DataOrigin.y +
+ dev->usbDev.pSource->Size.y / 2))) {
+ return _E_LAMP_NOT_IN_POS;
+ }
+
+ } else {
+#endif
+ DBG( _DBG_INFO, "ShadingOriginY=%lu\n",
+ (u_long)dev->usbDev.pSource->ShadingOriginY );
+
+ if((hw->motorModel == MODEL_HuaLien) && (scaps->OpticDpi.x==600)) {
+ if (!usb_ModuleMove(dev, MOVE_ToShading,
+ (u_long)dev->usbDev.pSource->ShadingOriginY)) {
+ return _E_LAMP_NOT_IN_POS;
+ }
+ } else {
+ if( !usb_ModuleMove(dev, MOVE_Forward,
+ (u_long)dev->usbDev.pSource->ShadingOriginY)) {
+ return _E_LAMP_NOT_IN_POS;
+ }
+ }
+/* }*/
+
+ DBG( _DBG_INFO, "shading position reached\n" );
+
+ usb_SpeedTest( dev );
+
+ if( !usb_AutoWarmup( dev ))
+ return SANE_FALSE;
+
+ usb_PrepareCalibration( dev );
+
+ /** this won't work for Plustek devices!!!
+ */
+#if 0
+ if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ||
+ !(SCANDEF_QualityScan & dev->scanning.dwFlag)) {
+#else
+ if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
+#endif
+
+ DBG( _DBG_INFO, "--> BYPASS\n" );
+ regs[0x38] = regs[0x39] = regs[0x3a] = 0;
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ setAdjGain( dev->adj.rgain, &regs[0x3b] );
+ setAdjGain( dev->adj.ggain, &regs[0x3c] );
+ setAdjGain( dev->adj.bgain, &regs[0x3d] );
+
+ regs[0x45] |= 0x10;
+ usb_SetMCLK( dev, &scanning->sParam );
+
+ dumpregs( dev->fd, regs );
+ DBG( _DBG_INFO, "<-- BYPASS\n" );
+
+ } else {
+
+ switch( scanning->sParam.bSource ) {
+
+ case SOURCE_Negative:
+ DBG( _DBG_INFO, "NEGATIVE Shading\n" );
+ m_dwIdealGain = IDEAL_GainNormal;
+
+ if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ DBG( _DBG_INFO, "No Plustek model: %udpi\n",
+ scanning->sParam.PhyDpi.x );
+ usb_SetMCLK( dev, &scanning->sParam );
+ } else {
+
+ if( dev->usbDev.Caps.OpticDpi.x == 600 )
+ dMCLK = 7;
+ else
+ dMCLK = 8;
+ }
+
+ for(;;) {
+ if( usb_AdjustGain( dev, 2)) {
+ if( regs[0x3b] && regs[0x3c] && regs[0x3d]) {
+ break;
+ } else {
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+ dMCLK--;
+ }
+ } else {
+ return _E_LAMP_NOT_STABLE;
+ }
+ }
+ scanning->sParam.dMCLK = dMCLK;
+ Gain_Reg.Red = regs[0x3b];
+ Gain_Reg.Green = regs[0x3c];
+ Gain_Reg.Blue = regs[0x3d];
+ Gain_NegHilight = Gain_Hilight;
+
+ DBG( _DBG_INFO, "MCLK = %.3f\n", dMCLK );
+ DBG( _DBG_INFO, "GainRed = %u\n", regs[0x3b] );
+ DBG( _DBG_INFO, "GainGreen = %u\n", regs[0x3c] );
+ DBG( _DBG_INFO, "GainBlue = %u\n", regs[0x3d] );
+
+#if 0
+ if( !usb_ModuleMove( dev, MOVE_Backward,
+ dev->usbDev.pSource->DataOrigin.y +
+ dev->usbDev.pSource->Size.y / 2 -
+ dev->usbDev.pSource->ShadingOriginY)) {
+ return _E_LAMP_NOT_IN_POS;
+ }
+#endif
+ regs[0x45] &= ~0x10;
+
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ if(!usb_AdjustGain( dev, 1 ))
+ return _E_INTERNAL;
+
+ regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
+
+ DBG( _DBG_INFO, "Settings done, so start...\n" );
+ if( !usb_AdjustOffset(dev) || !usb_AdjustDarkShading(dev) ||
+ !usb_AdjustWhiteShading(dev)) {
+ return _E_INTERNAL;
+ }
+
+ dRed = 0.93 + 0.067 * Gain_Reg.Red;
+ dGreen = 0.93 + 0.067 * Gain_Reg.Green;
+ dBlue = 0.93 + 0.067 * Gain_Reg.Blue;
+ dExpect = 2.85;
+ if( dBlue >= dGreen && dBlue >= dRed )
+ dMax = dBlue;
+ else
+ if( dGreen >= dRed && dGreen >= dBlue )
+ dMax = dGreen;
+ else
+ dMax = dRed;
+
+ dMax = dExpect / dMax;
+ dRed *= dMax;
+ dGreen *= dMax;
+ dBlue *= dMax;
+
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+ usb_ResizeWhiteShading( dRed, a_wWhiteShading,
+ scanning->sParam.swGain[0]);
+ usb_ResizeWhiteShading( dGreen, a_wWhiteShading +
+ m_ScanParam.Size.dwPhyPixels,
+ scanning->sParam.swGain[1]);
+ usb_ResizeWhiteShading( dBlue, a_wWhiteShading +
+ m_ScanParam.Size.dwPhyPixels*2,
+ scanning->sParam.swGain[2]);
+ }
+ usb_line_statistics( "White", a_wWhiteShading,
+ m_ScanParam.Size.dwPhyPixels, SANE_TRUE);
+ break;
+
+ case SOURCE_ADF:
+ DBG( _DBG_INFO, "ADF Shading\n" );
+ m_dwIdealGain = IDEAL_GainPositive;
+ if( scanning->sParam.bDataType == SCANDATATYPE_BW ) {
+ if( scanning->sParam.PhyDpi.x <= 200 ) {
+ scanning->sParam.dMCLK = 4.5;
+ dMCLK = 4.0;
+ } else if ( scanning->sParam.PhyDpi.x <= 300 ) {
+ scanning->sParam.dMCLK = 4.0;
+ dMCLK = 3.5;
+ } else if( scanning->sParam.PhyDpi.x <= 400 ) {
+ scanning->sParam.dMCLK = 5.0;
+ dMCLK = 4.0;
+ } else {
+ scanning->sParam.dMCLK = 6.0;
+ dMCLK = 4.0;
+ }
+ } else { /* Gray */
+
+ if( scanning->sParam.PhyDpi.x <= 400 ) {
+ scanning->sParam.dMCLK = 6.0;
+ dMCLK = 4.5;
+ } else {
+ scanning->sParam.dMCLK = 9.0;
+ dMCLK = 7.0;
+ }
+ }
+ dMCLK_ADF = dMCLK;
+
+ result = usb_DoIt( dev );
+ if( result != 0 )
+ return result;
+ break;
+
+ case SOURCE_Transparency:
+ DBG( _DBG_INFO, "TRANSPARENCY Shading\n" );
+ m_dwIdealGain = IDEAL_GainPositive;
+
+ if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ DBG( _DBG_INFO, "No Plustek model: %udpi\n",
+ scanning->sParam.PhyDpi.x );
+ usb_SetMCLK( dev, &scanning->sParam );
+
+ } else {
+ if( dev->usbDev.Caps.OpticDpi.x == 600 ) {
+ scanning->sParam.dMCLK = 8;
+ dMCLK = 4;
+ } else {
+ scanning->sParam.dMCLK = 8;
+ dMCLK = 6;
+ }
+ }
+ result = usb_DoIt( dev );
+ if( result != 0 )
+ return result;
+ break;
+
+ default:
+ if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ DBG( _DBG_INFO, "No Plustek model: %udpi\n",
+ scanning->sParam.PhyDpi.x );
+ usb_SetMCLK( dev, &scanning->sParam );
+
+ } else if( dev->usbDev.Caps.OpticDpi.x == 600 ) {
+
+ DBG( _DBG_INFO, "Default Shading (600dpi)\n" );
+
+ if( dev->usbDev.Caps.bCCD == kSONY548 ) {
+
+ DBG( _DBG_INFO, "CCD - SONY548\n" );
+ if( scanning->sParam.PhyDpi.x <= 75 ) {
+ if( scanning->sParam.bDataType == SCANDATATYPE_Color )
+ scanning->sParam.dMCLK = dMCLK = 2.5;
+ else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
+ scanning->sParam.dMCLK = dMCLK = 7.0;
+ else
+ scanning->sParam.dMCLK = dMCLK = 7.0;
+
+ } else if( scanning->sParam.PhyDpi.x <= 300 ) {
+ if( scanning->sParam.bDataType == SCANDATATYPE_Color )
+ scanning->sParam.dMCLK = dMCLK = 3.0;
+ else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
+ scanning->sParam.dMCLK = dMCLK = 6.0;
+ else {
+ if( scanning->sParam.PhyDpi.x <= 100 )
+ scanning->sParam.dMCLK = dMCLK = 6.0;
+ else if( scanning->sParam.PhyDpi.x <= 200 )
+ scanning->sParam.dMCLK = dMCLK = 5.0;
+ else
+ scanning->sParam.dMCLK = dMCLK = 4.5;
+ }
+ } else if( scanning->sParam.PhyDpi.x <= 400 ) {
+ if( scanning->sParam.bDataType == SCANDATATYPE_Color )
+ scanning->sParam.dMCLK = dMCLK = 4.0;
+ else if( scanning->sParam.bDataType == SCANDATATYPE_Gray )
+ scanning->sParam.dMCLK = dMCLK = 6.0;
+ else
+ scanning->sParam.dMCLK = dMCLK = 4.0;
+ } else {
+ if(scanning->sParam.bDataType == SCANDATATYPE_Color)
+ scanning->sParam.dMCLK = dMCLK = 6.0;
+ else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
+ scanning->sParam.dMCLK = dMCLK = 7.0;
+ else
+ scanning->sParam.dMCLK = dMCLK = 6.0;
+ }
+
+ } else if( dev->usbDev.Caps.bPCB == 0x02 ) {
+ DBG( _DBG_INFO, "PCB - 0x02\n" );
+ if( scanning->sParam.PhyDpi.x > 300 )
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 16);
+ else if( scanning->sParam.PhyDpi.x > 150 )
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?4.5: 13.5);
+ else
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 8);
+
+ } else if( dev->usbDev.Caps.bButtons ) { /* with lens Shading piece (with gobo) */
+
+ DBG( _DBG_INFO, "CAPS - Buttons\n" );
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
+ if( dev->usbDev.HwSetting.motorModel == MODEL_KaoHsiung ) {
+ if( dev->usbDev.Caps.bCCD == kNEC3799 ) {
+ if( scanning->sParam.PhyDpi.x > 300 )
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 13);
+ else if(scanning->sParam.PhyDpi.x > 150)
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?4.5:13.5);
+ else
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
+ } else {
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
+ }
+ } else { /* motorModel == MODEL_Hualien */
+ /* IMPORTANT !!!!
+ * for Hualien 600 dpi scanner big noise
+ */
+ hw->wLineEnd = 5384;
+ if(scanning->sParam.bDataType == SCANDATATYPE_Color &&
+ ((scanning->sParam.bBitDepth == 8 &&
+ (scanning->sParam.PhyDpi.x == 200 ||scanning->sParam.PhyDpi.x == 300))))
+ hw->wLineEnd = 7000;
+ regs[0x20] = _HIBYTE(hw->wLineEnd);
+ regs[0x21] = _LOBYTE(hw->wLineEnd);
+
+ if( scanning->sParam.PhyDpi.x > 300 ) {
+ if (scanning->sParam.bBitDepth > 8)
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 5: 13);
+ else
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 13);
+ } else {
+ if( scanning->sParam.bBitDepth > 8 )
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 5: 13);
+ else
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
+ }
+ }
+ } else { /* without lens Shading piece (without gobo) - Model U12 only */
+ DBG( _DBG_INFO, "Default trunc (U12)\n" );
+ if( scanning->sParam.PhyDpi.x > 300 )
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 3: 9);
+ else
+ scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 2: 6);
+ }
+ } else { /* Device.Caps.OpticDpi.x == 1200 */
+
+ DBG( _DBG_INFO, "Default Shading (1200dpi)\n" );
+ if( scanning->sParam.bDataType != SCANDATATYPE_Color ) {
+ if( scanning->sParam.PhyDpi.x > 300 )
+ scanning->sParam.dMCLK = dMCLK = 6.0;
+ else {
+ scanning->sParam.dMCLK = dMCLK = 5.0;
+ regs[0x0a] = 1;
+ }
+ } else {
+ if( scanning->sParam.PhyDpi.x <= 300)
+ scanning->sParam.dMCLK = dMCLK = 2.0;
+ else if( scanning->sParam.PhyDpi.x <= 800 )
+ scanning->sParam.dMCLK = dMCLK = 4.0;
+ else
+ scanning->sParam.dMCLK = dMCLK = 5.5;
+ }
+ }
+
+ if (m_ScanParam.bSource == SOURCE_ADF)
+ m_dwIdealGain = IDEAL_GainPositive;
+ else
+ m_dwIdealGain = IDEAL_GainNormal;
+
+ result = usb_DoIt( dev );
+ if( result != 0 )
+ return result;
+ break;
+ }
+ }
+
+ /* home the sensor after calibration */
+ if( _IS_PLUSTEKMOTOR(hw->motorModel)) {
+ if( hw->motorModel != MODEL_Tokyo600 ) {
+ usb_ModuleMove ( dev, MOVE_Forward, hw->wMotorDpi / 5 );
+ usb_ModuleToHome( dev, SANE_TRUE );
+ }
+ } else {
+ usb_ModuleMove( dev, MOVE_Forward, 10 );
+ usleep( 1500 );
+ usb_ModuleToHome( dev, SANE_TRUE );
+ }
+
+ if( scanning->sParam.bSource == SOURCE_ADF ) {
+
+ if( scaps->bCCD == kNEC3778 )
+ usb_ModuleMove( dev, MOVE_Forward, 1000 );
+
+ else /* if( scaps->bCCD == kNEC3799) */
+ usb_ModuleMove( dev, MOVE_Forward, 3 * 300 + 38 );
+
+ usb_MotorOn( dev, SANE_FALSE );
+ }
+
+ scanning->fCalibrated = SANE_TRUE;
+ DBG( _DBG_INFO, "Calibration done\n" );
+ DBG( _DBG_INFO, "-----------------------\n" );
+ DBG( _DBG_INFO, "Static Gain:\n" );
+ DBG( _DBG_INFO, "REG[0x3b] = %u\n", regs[0x3b] );
+ DBG( _DBG_INFO, "REG[0x3c] = %u\n", regs[0x3c] );
+ DBG( _DBG_INFO, "REG[0x3d] = %u\n", regs[0x3d] );
+ DBG( _DBG_INFO, "Static Offset:\n" );
+ DBG( _DBG_INFO, "REG[0x38] = %i\n", regs[0x38] );
+ DBG( _DBG_INFO, "REG[0x39] = %i\n", regs[0x39] );
+ DBG( _DBG_INFO, "REG[0x3a] = %i\n", regs[0x3a] );
+ DBG( _DBG_INFO, "MCLK = %.2f\n", scanning->sParam.dMCLK );
+ DBG( _DBG_INFO, "-----------------------\n" );
+ return SANE_TRUE;
+}
+
+/* on different sensor orders, we need to adjust the shading buffer
+ * pointer, otherwise we correct the wrong channels
+ */
+static void
+get_ptrs(Plustek_Device *dev, u_short *buf, u_long offs,
+ u_short **r, u_short **g, u_short **b)
+{
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ u_char so = scaps->bSensorOrder;
+
+ if (_WAF_RESET_SO_TO_RGB & scaps->workaroundFlag) {
+ if (scaps->bPCB != 0) {
+ if (scan->sParam.PhyDpi.x > scaps->bPCB)
+ so = SENSORORDER_rgb;
+ }
+ }
+
+ switch( so ) {
+ case SENSORORDER_gbr:
+ *g = buf;
+ *b = buf + offs;
+ *r = buf + offs * 2;
+ break;
+
+ case SENSORORDER_bgr:
+ *b = buf;
+ *g = buf + offs;
+ *r = buf + offs * 2;
+ break;
+
+ case SENSORORDER_rgb:
+ default:
+ *r = buf;
+ *g = buf + offs;
+ *b = buf + offs * 2;
+ break;
+ }
+}
+
+/** usb_DownloadShadingData
+ * according to the job id, different registers or DRAM areas are set
+ * in preparation for calibration or scanning
+ */
+static SANE_Bool
+usb_DownloadShadingData( Plustek_Device *dev, u_char what )
+{
+ u_char channel;
+ u_short *r, *g, *b;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ ScanDef *scan = &dev->scanning;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ ScanParam *param = &dev->scanning.sParam;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "usb_DownloadShadingData(%u)\n", what );
+
+ channel = CHANNEL_green;
+ if( usb_IsCISDevice(dev))
+ channel = CHANNEL_blue;
+
+ switch( what ) {
+
+ case PARAM_WhiteShading:
+ if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
+
+ usb_SetDarkShading( dev, CHANNEL_red, a_wDarkShading,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ usb_SetDarkShading( dev, CHANNEL_green, a_wDarkShading +
+ m_ScanParam.Size.dwPhyPixels,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ usb_SetDarkShading( dev, CHANNEL_blue, a_wDarkShading +
+ m_ScanParam.Size.dwPhyPixels * 2,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ } else {
+ usb_SetDarkShading( dev, channel, a_wDarkShading +
+ m_ScanParam.Size.dwPhyPixels,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ }
+ regs[0x40] = 0x40;
+ regs[0x41] = 0x00;
+
+ /* set RAM configuration AND
+ * Gain = Multiplier Coefficient/16384
+ * CFG Register 0x40/0x41 for Multiplier Coefficient Source
+ * External DRAM for Offset Coefficient Source
+ */
+ regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x64: 0x24);
+ _UIO(sanei_lm983x_write( dev->fd, 0x40,
+ &regs[0x40], 0x42-0x40+1, SANE_TRUE ));
+ break;
+
+ case PARAM_Scan:
+ {
+#if 0
+ if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ||
+ !(SCANDEF_QualityScan & dev->scanning.dwFlag)) {
+#else
+ if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
+#endif
+ DBG( _DBG_INFO, "--> BYPASS\n" );
+ /* set RAM configuration AND
+ * Bypass Multiplier
+ * CFG Register 0x40/0x41 for Multiplier Coefficient Source
+ * CFG Register 0x3e/0x3f for Offset Coefficient Source
+ */
+ regs[0x03] = 0;
+ regs[0x40] = 0x40;
+ regs[0x41] = 0x00;
+ regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x61:0x21);
+ if( !usbio_WriteReg( dev->fd, 0x03, regs[0x03]))
+ return SANE_FALSE;
+
+ _UIO(sanei_lm983x_write( dev->fd, 0x40,
+ &regs[0x40], 3, SANE_TRUE));
+ break;
+ }
+
+ if( _LM9831 != hw->chip )
+ m_dwPixels = m_ScanParam.Size.dwPhyPixels;
+
+ if( scaps->workaroundFlag & _WAF_SKIP_FINE ) {
+ DBG( _DBG_INFO, "Skipping fine calibration\n" );
+ regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x60: 0x20);
+ if (scan->skipCoarseCalib) {
+
+ DBG( _DBG_INFO, "...cleaning shading buffer\n" );
+ memset( a_wWhiteShading, 0, _SHADING_BUF );
+ memset( a_wDarkShading, 0, _SHADING_BUF );
+
+ regs[0x40] = 0x3f;
+ regs[0x41] = 0xff;
+
+ _UIO(sanei_lm983x_write( dev->fd, 0x40,
+ &regs[0x40], 3, SANE_TRUE));
+ } else {
+ if( !usbio_WriteReg( dev->fd, 0x42, regs[0x42]))
+ return SANE_FALSE;
+ }
+ break;
+ }
+
+ DBG( _DBG_INFO, "Downloading %lu pixels\n", m_dwPixels );
+ /* Download the dark & white shadings to LM983x */
+ if( param->bDataType == SCANDATATYPE_Color ) {
+
+ get_ptrs(dev, a_wDarkShading, m_dwPixels, &r, &g, &b);
+
+ usb_SetDarkShading( dev, CHANNEL_red, r,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ usb_SetDarkShading( dev, CHANNEL_green, g,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ usb_SetDarkShading( dev, CHANNEL_blue, b,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ } else {
+ usb_SetDarkShading( dev, channel,
+ a_wDarkShading + m_dwPixels,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ }
+ if( param->bDataType == SCANDATATYPE_Color ) {
+
+ get_ptrs(dev, a_wWhiteShading,
+ m_ScanParam.Size.dwPhyPixels, &r, &g, &b);
+
+ usb_SetWhiteShading( dev, CHANNEL_red, r,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ usb_SetWhiteShading( dev, CHANNEL_green, g,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ usb_SetWhiteShading( dev, CHANNEL_blue, b,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ } else {
+ usb_SetWhiteShading( dev, channel, a_wWhiteShading,
+ (u_short)m_ScanParam.Size.dwPhyPixels * 2);
+ }
+
+ /* set RAM configuration AND
+ * Gain = Multiplier Coefficient/16384
+ * External DRAM for Multiplier Coefficient Source
+ * External DRAM for Offset Coefficient Source
+ */
+ regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x66: 0x26);
+
+ if( scaps->workaroundFlag & _WAF_SKIP_WHITEFINE ) {
+ DBG( _DBG_INFO,"Skipping fine white calibration result\n");
+ regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x64: 0x24);
+ }
+
+ if( !usbio_WriteReg( dev->fd, 0x42, regs[0x42]))
+ return SANE_FALSE;
+ }
+ break;
+
+ default:
+ /* for coarse calibration and "black fine" */
+ regs[0x3e] = 0;
+ regs[0x3f] = 0;
+ regs[0x40] = 0x40;
+ regs[0x41] = 0x00;
+
+ /* set RAM configuration AND
+ * GAIN = Multiplier Coefficient/16384
+ * CFG Register 0x40/0x41 for Multiplier Coefficient Source
+ * CFG Register 0x3e/0x3f for Offset Coefficient Source
+ */
+ regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x60: 0x20);
+ _UIO(sanei_lm983x_write( dev->fd, 0x3e, &regs[0x3e],
+ 0x42 - 0x3e + 1, SANE_TRUE ));
+ break;
+ }
+ return SANE_TRUE;
+}
+
+/* END PLUSTEK-USBSHADING.C .................................................*/
diff --git a/backend/plustek.c b/backend/plustek.c
new file mode 100644
index 0000000..cd83c26
--- /dev/null
+++ b/backend/plustek.c
@@ -0,0 +1,2806 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek.c
+ * @brief SANE backend for Plustek scanner
+ *
+ * Based on Kazuhiro Sasayama previous work on
+ * plustek.[ch] file from the SANE package.<br>
+ * Original code taken from sane-0.71<br>
+ * Copyright (C) 1997 Hypercore Software Design, Ltd.<br>
+ * Also based on the work done by Rick Bronson<br>
+ * Copyright (C) 2000-2007 Gerhard Jaeger <gerhard@gjaeger.de><br>
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - no changes
+ * - 0.33 - no changes
+ * - 0.34 - moved some definitions and typedefs to plustek.h
+ * - 0.35 - removed Y-correction for 12000P model<br>
+ * - getting Y-size of scan area from driver
+ * - 0.36 - disabled Dropout, as this does currently not work<br>
+ * - enabled Halftone selection only for Halftone-mode<br>
+ * - made the cancel button work by using a child process during read<br>
+ * - added version code to driver interface<br>
+ * - cleaned up the code<br>
+ * - fixed sane compatibility problems<br>
+ * - added multiple device support<br>
+ * - 12bit color-depth are now available for scanimage
+ * - 0.37 - removed X/Y autocorrection, now correcting the stuff
+ * before scanning
+ * - applied Michaels' patch to solve the sane_get_parameter problem<br>
+ * - getting X-size of scan area from driver<br>
+ * - applied Michaels patch for OPT_RESOLUTION (SANE_INFO_INEXACT stuff)
+ * - 0.38 - now using the information from the driver
+ * - some minor fixes<br>
+ * - removed dropout stuff<br>
+ * - removed some warning conditions
+ * - 0.39 - added stuff to use the backend completely in user mode<br>
+ * - fixed a potential buffer problem<br>
+ * - removed function attach_one()<br>
+ * - added USB interface stuff
+ * - 0.40 - USB scanning works now
+ * - 0.41 - added some configuration stuff and also changed .conf file<br>
+ * - added call to sanei_usb_init() and sanei_lm983x_init()
+ * - 0.42 - added adjustment stuff<br>
+ * - added custom gamma tables<br>
+ * - fixed a problem with the "size-sliders"<br>
+ * - fixed a bug that causes segfault when using the autodetection for
+ * USB devices<br>
+ * - added OS/2 switch to disable the USB stuff for OS/2
+ * - 0.43 - added support for PREVIEW flag
+ * - 0.44 - added _DBG_DUMP debug level<br>
+ * - fixed a bug, that stops our lamp timer
+ * - 0.45 - added additional flags
+ * - added WIFSIGNALED to check result of child termination
+ * - changed readImage interface for USB devices
+ * - homeing of USB scanner is now working correctly
+ * - 0.46 - added plustek-usbcal.c for extra CIS device calibration
+ * based on Montys' great work
+ * - added altCalibration option
+ * - removed parallelport support --> new backend: plustek_pp
+ * - cleanup
+ * - added sanei_thread support
+ * - 0.47 - added mov-option (model override)
+ * - removed drvOpen
+ * - added call to usb_StartLampTimer, when we're using
+ * SIGALRM for lamp timer
+ * - closing now writer pipe, when reader_process is done
+ * - 0.48 - added additional options
+ * split scanmode and bit-depth
+ * - 0.49 - improved multi-device capability
+ * - tweaked some device settings
+ * - added button support
+ * - moved AFE stuff to enhanced options
+ * - 0.50 - cleanup
+ * - activated IPC stuff
+ * - added _DBG_DCALDATA for fine calibration data logging
+ * - added OPT_SPEEDUP handling
+ * - fixed constraint_type for OPT_BUTTON
+ * - 0.51 - added fine calibration caching
+ * - removed #define _PLUSTEK_USB
+ * - 0.52 - added skipDarkStrip and OPT_LOFF4DARK to frontend options
+ * - fixed batch scanning
+ *.
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/** @mainpage
+ * @verbinclude Plustek-USB.txt
+ */
+
+#ifdef _AIX
+# include "../include/lalloca.h"
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_VERSION "0.52-12"
+
+#define BACKEND_NAME plustek
+#include "../include/sane/sanei_access.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#define USE_IPC
+
+#include "plustek-usb.h"
+#include "plustek.h"
+
+/*********************** the debug levels ************************************/
+
+#define _DBG_FATAL 0
+#define _DBG_ERROR 1
+#define _DBG_WARNING 3
+#define _DBG_INFO 5
+#define _DBG_PROC 7
+#define _DBG_SANE_INIT 10
+#define _DBG_INFO2 15
+#define _DBG_DREGS 20
+#define _DBG_DCALDATA 22
+#define _DBG_DPIC 25
+#define _DBG_READ 30
+
+/*****************************************************************************/
+
+#define _SECTION "[usb]"
+#define _DEFAULT_DEVICE "auto"
+
+/** to disable the backend... */
+
+/* declare it here, as it's used in plustek-usbscan.c too :-( */
+static SANE_Bool cancelRead;
+static DevList *usbDevs;
+
+/* the USB-stuff... I know this is in general no good idea, but it works */
+#include "plustek-usbio.c"
+#include "plustek-usbdevs.c"
+#include "plustek-usbhw.c"
+#include "plustek-usbmap.c"
+#include "plustek-usbscan.c"
+#include "plustek-usbimg.c"
+#include "plustek-usbcalfile.c"
+#include "plustek-usbshading.c"
+#include "plustek-usbcal.c"
+#include "plustek-usb.c"
+
+/************************** global vars **************************************/
+
+static int num_devices;
+static Plustek_Device *first_dev;
+static Plustek_Scanner *first_handle;
+static const SANE_Device **devlist = 0;
+static unsigned long tsecs = 0;
+static Plustek_Scanner *sc = NULL;
+
+static const SANE_Int bpp_lm9832_list [] = { 2, 8, 14 };
+static const SANE_Int bpp_lm9833_list [] = { 2, 8, 16 };
+
+static const SANE_String_Const mode_list[] =
+{
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static const SANE_String_Const source_list[] =
+{
+ SANE_I18N("Normal"),
+ SANE_I18N("Transparency"),
+ SANE_I18N("Negative"),
+ NULL
+};
+
+static const SANE_Range percentage_range =
+{
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static const SANE_Range warmup_range = { -1, 999, 1 };
+static const SANE_Range offtimer_range = { 0, 999, 1 };
+static const SANE_Range gain_range = { -1, 63, 1 };
+static const SANE_Range loff_range = { -1, 16363, 1 };
+
+/* authorization stuff */
+static SANE_Auth_Callback auth = NULL;
+
+/* prototype... */
+static SANE_Status local_sane_start(Plustek_Scanner *, int);
+
+/****************************** the backend... *******************************/
+
+#define _YN(x) (x?"yes":"no")
+
+/** function to display the configuration options for the current device
+ * @param cnf - pointer to the configuration structure whose content should be
+ * displayed
+ */
+static void show_cnf( CnfDef *cnf )
+{
+ DBG( _DBG_SANE_INIT,"Device configuration:\n" );
+ DBG( _DBG_SANE_INIT,"device name : >%s<\n",cnf->devName );
+ DBG( _DBG_SANE_INIT,"USB-ID : >%s<\n",cnf->usbId );
+ DBG( _DBG_SANE_INIT,"model ovr. : %d\n", cnf->adj.mov );
+ DBG( _DBG_SANE_INIT,"warmup : %ds\n", cnf->adj.warmup );
+ DBG( _DBG_SANE_INIT,"lampOff : %d\n", cnf->adj.lampOff );
+ DBG( _DBG_SANE_INIT,"lampOffOnEnd : %s\n", _YN(cnf->adj.lampOffOnEnd ));
+ DBG( _DBG_SANE_INIT,"cacheCalData : %s\n", _YN(cnf->adj.cacheCalData ));
+ DBG( _DBG_SANE_INIT,"altCalibrate : %s\n", _YN(cnf->adj.altCalibrate ));
+ DBG( _DBG_SANE_INIT,"skipCalibr. : %s\n", _YN(cnf->adj.skipCalibration));
+ DBG( _DBG_SANE_INIT,"skipFine : %s\n", _YN(cnf->adj.skipFine ));
+ DBG( _DBG_SANE_INIT,"skipFineWhite: %s\n", _YN(cnf->adj.skipFineWhite ));
+ DBG( _DBG_SANE_INIT,"skipDarkStrip: %s\n", _YN(cnf->adj.skipDarkStrip ));
+ DBG( _DBG_SANE_INIT,"incDarkTarget: %s\n", _YN(cnf->adj.incDarkTgt ));
+ DBG( _DBG_SANE_INIT,"invertNegs. : %s\n", _YN(cnf->adj.invertNegatives));
+ DBG( _DBG_SANE_INIT,"dis.Speedup : %s\n", _YN(cnf->adj.disableSpeedup ));
+ DBG( _DBG_SANE_INIT,"pos_x : %d\n", cnf->adj.pos.x );
+ DBG( _DBG_SANE_INIT,"pos_y : %d\n", cnf->adj.pos.y );
+ DBG( _DBG_SANE_INIT,"pos_shading_y: %d\n", cnf->adj.posShadingY );
+ DBG( _DBG_SANE_INIT,"neg_x : %d\n", cnf->adj.neg.x );
+ DBG( _DBG_SANE_INIT,"neg_y : %d\n", cnf->adj.neg.y );
+ DBG( _DBG_SANE_INIT,"neg_shading_y: %d\n", cnf->adj.negShadingY );
+ DBG( _DBG_SANE_INIT,"tpa_x : %d\n", cnf->adj.tpa.x );
+ DBG( _DBG_SANE_INIT,"tpa_y : %d\n", cnf->adj.tpa.y );
+ DBG( _DBG_SANE_INIT,"tpa_shading_y: %d\n", cnf->adj.tpaShadingY );
+ DBG( _DBG_SANE_INIT,"red gain : %d\n", cnf->adj.rgain );
+ DBG( _DBG_SANE_INIT,"green gain : %d\n", cnf->adj.ggain );
+ DBG( _DBG_SANE_INIT,"blue gain : %d\n", cnf->adj.bgain );
+ DBG( _DBG_SANE_INIT,"red offset : %d\n", cnf->adj.rofs );
+ DBG( _DBG_SANE_INIT,"green offset : %d\n", cnf->adj.gofs );
+ DBG( _DBG_SANE_INIT,"blue offset : %d\n", cnf->adj.bofs );
+ DBG( _DBG_SANE_INIT,"red lampoff : %d\n", cnf->adj.rlampoff );
+ DBG( _DBG_SANE_INIT,"green lampoff: %d\n", cnf->adj.glampoff );
+ DBG( _DBG_SANE_INIT,"blue lampoff : %d\n", cnf->adj.blampoff );
+ DBG( _DBG_SANE_INIT,"red Gamma : %.2f\n",cnf->adj.rgamma );
+ DBG( _DBG_SANE_INIT,"green Gamma : %.2f\n",cnf->adj.ggamma );
+ DBG( _DBG_SANE_INIT,"blue Gamma : %.2f\n",cnf->adj.bgamma );
+ DBG( _DBG_SANE_INIT,"gray Gamma : %.2f\n",cnf->adj.graygamma );
+ DBG( _DBG_SANE_INIT,"---------------------\n" );
+}
+
+/** Calls the device specific stop and close functions.
+ * @param dev - pointer to the device specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static SANE_Status
+drvclose( Plustek_Device *dev )
+{
+ if( dev->fd >= 0 ) {
+
+ DBG( _DBG_INFO, "drvclose()\n" );
+
+ if( 0 != tsecs ) {
+ DBG( _DBG_INFO, "TIME END 1: %lus\n", time(NULL)-tsecs);
+ }
+
+ /* don't check the return values, simply do it */
+ usbDev_stopScan( dev );
+ usbDev_close ( dev );
+ sanei_access_unlock( dev->sane.name );
+ }
+ dev->fd = -1;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** according to the mode and source we return the corresponding scanmode and
+ * bit-depth per pixel
+ */
+static int
+getScanMode( Plustek_Scanner *scanner )
+{
+ int mode;
+ int scanmode;
+
+ /* are we in TPA-mode? */
+ mode = scanner->val[OPT_MODE].w;
+ if( scanner->val[OPT_EXT_MODE].w != 0 )
+ mode += 2;
+
+ scanner->params.depth = scanner->val[OPT_BIT_DEPTH].w;
+
+ if( mode == 0 ) {
+ scanmode = COLOR_BW;
+ scanner->params.depth = 1;
+ } else if( scanner->params.depth == 8 ) {
+
+ if( mode == 1 )
+ scanmode = COLOR_256GRAY;
+ else
+ scanmode = COLOR_TRUE24;
+ } else {
+ scanner->params.depth = 16;
+ if( mode == 1 )
+ scanmode = COLOR_GRAY16;
+ else
+ scanmode = COLOR_TRUE48;
+ }
+ return scanmode;
+}
+
+/** return the len of the largest string in the array
+ */
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+/** shutdown open pipes
+ */
+static SANE_Status
+close_pipe( Plustek_Scanner *scanner )
+{
+ if( scanner->r_pipe >= 0 ) {
+
+ DBG( _DBG_PROC, "close_pipe (r_pipe)\n" );
+ close( scanner->r_pipe );
+ scanner->r_pipe = -1;
+ }
+ if( scanner->w_pipe >= 0 ) {
+
+ DBG( _DBG_PROC, "close_pipe (w_pipe)\n" );
+ close( scanner->w_pipe );
+ scanner->w_pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+
+/** will be called when a child is going down
+ */
+static void
+sig_chldhandler( int signo )
+{
+ DBG( _DBG_PROC, "(SIG) Child is down (signal=%d)\n", signo );
+ if (sc) {
+ sc->calibrating = SANE_FALSE;
+ sc = NULL;
+ }
+}
+
+/** signal handler to kill the child process
+ */
+static RETSIGTYPE
+reader_process_sigterm_handler( int signo )
+{
+ DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo );
+ _exit( SANE_STATUS_GOOD );
+}
+
+static RETSIGTYPE
+usb_reader_process_sigterm_handler( int signo )
+{
+ DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo );
+ cancelRead = SANE_TRUE;
+}
+
+static RETSIGTYPE
+sigalarm_handler( int signo )
+{
+ _VAR_NOT_USED( signo );
+ DBG( _DBG_PROC, "ALARM!!!\n" );
+}
+
+/**
+ */
+static void
+thread_entry(void)
+{
+ struct SIGACTION act;
+ sigset_t ignore_set;
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGTERM );
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset ( &ignore_set, SIGUSR2 );
+#endif
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset(&act, 0, sizeof (act));
+ sigaction( SIGTERM, &act, 0 );
+
+ cancelRead = SANE_FALSE;
+
+ /* install the signal handler */
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = reader_process_sigterm_handler;
+ sigaction( SIGTERM, &act, 0 );
+
+ act.sa_handler = usb_reader_process_sigterm_handler;
+ sigaction( SIGUSR1, &act, 0 );
+}
+
+/** executed as a child process
+ * read the data from the driver and send them to the parent process
+ */
+static int
+reader_process( void *args )
+{
+ int line, lerrn;
+ unsigned char *buf;
+ unsigned long status;
+ unsigned long data_length;
+ Plustek_Scanner *scanner = (Plustek_Scanner *)args;
+ Plustek_Device *dev = scanner->hw;
+#ifdef USE_IPC
+ IPCDef ipc;
+#endif
+
+ if( sanei_thread_is_forked()) {
+ DBG( _DBG_PROC, "reader_process started (forked)\n" );
+ close( scanner->r_pipe );
+ scanner->r_pipe = -1;
+ } else {
+ DBG( _DBG_PROC, "reader_process started (as thread)\n" );
+ }
+
+ thread_entry();
+
+ data_length = scanner->params.lines * scanner->params.bytes_per_line;
+
+ DBG( _DBG_PROC, "reader_process:"
+ "starting to READ data (%lu bytes)\n", data_length );
+ DBG( _DBG_PROC, "buf = 0x%08lx\n", (unsigned long)scanner->buf );
+
+ if( NULL == scanner->buf ) {
+ DBG( _DBG_FATAL, "NULL Pointer !!!!\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* prepare for scanning: speed-test, warmup, calibration */
+ buf = scanner->buf;
+ status = usbDev_Prepare( scanner->hw, buf );
+
+#ifdef USE_IPC
+ /* prepare IPC structure */
+ memset(&ipc, 0, sizeof(ipc));
+ ipc.transferRate = DEFAULT_RATE;
+
+ if( dev->transferRate > 0 && dev->transferRate != DEFAULT_RATE )
+ ipc.transferRate = dev->transferRate;
+
+ /* write ipc back to parent in any case... */
+ write( scanner->w_pipe, &ipc, sizeof(ipc));
+#endif
+
+ /* on success, we read all data from the driver... */
+ if( 0 == status ) {
+
+ if( !usb_InCalibrationMode(dev)) {
+
+ DBG( _DBG_INFO, "reader_process: READING....\n" );
+
+ for( line = 0; line < scanner->params.lines; line++ ) {
+
+ status = usbDev_ReadLine( scanner->hw );
+ if((int)status < 0 ) {
+ break;
+ }
+ write( scanner->w_pipe, buf, scanner->params.bytes_per_line );
+ buf += scanner->params.bytes_per_line;
+ }
+ }
+ }
+ /* on error, there's no need to clean up, as this is done by the parent */
+ lerrn = errno;
+
+ close( scanner->w_pipe );
+ scanner->w_pipe = -1;
+
+ if((int)status < 0 ) {
+ DBG( _DBG_ERROR,"reader_process: read failed, status = %i, errno %i\n",
+ (int)status, lerrn );
+ if( _E_ABORT == (int)status )
+ return SANE_STATUS_CANCELLED;
+
+ if( lerrn == EBUSY )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG( _DBG_PROC, "reader_process: finished reading data\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** stop the current scan process
+ */
+static SANE_Status
+do_cancel( Plustek_Scanner *scanner, SANE_Bool closepipe )
+{
+ struct SIGACTION act;
+ SANE_Pid res;
+
+ DBG( _DBG_PROC,"do_cancel\n" );
+ scanner->scanning = SANE_FALSE;
+
+ if( scanner->reader_pid != -1 ) {
+
+ DBG( _DBG_PROC, ">>>>>>>> killing reader_process <<<<<<<<\n" );
+
+ cancelRead = SANE_TRUE;
+ scanner->calibrating = SANE_FALSE;
+
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+ sigaction( SIGALRM, &act, 0 );
+
+ /* kill our child process and wait until done */
+ sanei_thread_sendsig( scanner->reader_pid, SIGUSR1 );
+
+ /* give'em 10 seconds 'til done...*/
+ alarm(10);
+ res = sanei_thread_waitpid( scanner->reader_pid, 0 );
+ alarm(0);
+
+ if( res != scanner->reader_pid ) {
+ DBG( _DBG_PROC,"sanei_thread_waitpid() failed !\n");
+
+ /* do it the hard way...*/
+#ifdef USE_PTHREAD
+ sanei_thread_kill( scanner->reader_pid );
+#else
+ sanei_thread_sendsig( scanner->reader_pid, SIGKILL );
+#endif
+ }
+
+ scanner->reader_pid = -1;
+ DBG( _DBG_PROC,"reader_process killed\n");
+#ifndef HAVE_SETITIMER
+ usb_StartLampTimer( scanner->hw );
+#endif
+ }
+ scanner->calibrating = SANE_FALSE;
+
+ if( SANE_TRUE == closepipe ) {
+ close_pipe( scanner );
+ }
+
+ drvclose( scanner->hw );
+
+ if( tsecs != 0 ) {
+ DBG( _DBG_INFO, "TIME END 2: %lus\n", time(NULL)-tsecs);
+ tsecs = 0;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/** As we support only LM9831/2/3 chips we use the same
+ * sizes for each device...
+ * @param s - pointer to the scanner specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static SANE_Status
+initGammaSettings( Plustek_Scanner *s )
+{
+ int i, j, val;
+ double gamma;
+
+ s->gamma_length = 4096;
+ s->gamma_range.min = 0;
+ s->gamma_range.max = 255;
+ s->gamma_range.quant = 0;
+
+ DBG( _DBG_INFO, "Presetting Gamma tables (len=%u)\n", s->gamma_length );
+
+ /* preset the gamma maps
+ */
+ for( i = 0; i < 4; i++ ) {
+
+ switch( i ) {
+ case 1: gamma = s->hw->adj.rgamma; break;
+ case 2: gamma = s->hw->adj.ggamma; break;
+ case 3: gamma = s->hw->adj.bgamma; break;
+ default: gamma = s->hw->adj.graygamma; break;
+ }
+ DBG( _DBG_INFO, "* Channel[%u], gamma %.3f\n", i, gamma );
+
+ for( j = 0; j < s->gamma_length; j++ ) {
+
+ val = (s->gamma_range.max *
+ pow((double)j / (double)(s->gamma_length-1.0),
+ 1.0 / gamma ));
+
+ if( val > s->gamma_range.max )
+ val = s->gamma_range.max;
+
+ s->gamma_table[i][j] = val;
+ }
+ }
+ DBG( _DBG_INFO, "----------------------------------\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** Check the gamma vectors we got back and limit if necessary
+ * @param s - pointer to the scanner specific structure
+ * @return nothing
+ */
+static void
+checkGammaSettings( Plustek_Scanner *s )
+{
+ int i, j;
+
+ DBG( _DBG_INFO, "Maps changed...\n" );
+ for( i = 0; i < 4 ; i++ ) {
+ for( j = 0; j < s->gamma_length; j++ ) {
+ if( s->gamma_table[i][j] > s->gamma_range.max ) {
+ s->gamma_table[i][j] = s->gamma_range.max;
+ }
+ }
+ }
+}
+
+/** initialize the options for the backend according to the device we have
+ */
+static SANE_Status
+init_options( Plustek_Scanner *s )
+{
+ int i;
+ Plustek_Device *dev = s->hw;
+ AdjDef *adj = &dev->adj;
+ DCapsDef *caps = &dev->usbDev.Caps;
+
+ memset(s->opt, 0, sizeof(s->opt));
+
+ for( i = 0; i < NUM_OPTIONS; ++i ) {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[i].unit = SANE_UNIT_NONE;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size(mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 2; /* Color */
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
+ s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ if( _LM9833 == dev->usbDev.HwSetting.chip )
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bpp_lm9833_list;
+ else
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bpp_lm9832_list;
+ s->val[OPT_BIT_DEPTH].w = 8;
+
+ if (caps->workaroundFlag & _WAF_ONLY_8BIT)
+ _DISABLE(OPT_BIT_DEPTH);
+
+ /* scan source */
+ s->opt[OPT_EXT_MODE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_EXT_MODE].size = max_string_size(source_list);
+ s->opt[OPT_EXT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_EXT_MODE].constraint.string_list = source_list;
+ s->val[OPT_EXT_MODE].w = 0; /* Normal */
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ initGammaSettings( s );
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR].wa = &(s->gamma_table[0][0]);
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &(s->gamma_table[1][0]);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_R].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &(s->gamma_table[2][0]);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_G].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &(s->gamma_table[3][0]);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* GAMMA stuff is disabled per default */
+ _DISABLE(OPT_GAMMA_VECTOR);
+ _DISABLE(OPT_GAMMA_VECTOR_R);
+ _DISABLE(OPT_GAMMA_VECTOR_G);
+ _DISABLE(OPT_GAMMA_VECTOR_B);
+
+ /* disable extended mode list for devices without TPA */
+ if( 0 == (s->hw->caps.dwFlag & SFLAG_TPA))
+ _DISABLE(OPT_EXT_MODE);
+
+ /* "Device settings" group: */
+ s->opt[OPT_DEVICE_GROUP].title = SANE_I18N("Device-Settings");
+ s->opt[OPT_DEVICE_GROUP].desc = "";
+ s->opt[OPT_DEVICE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_DEVICE_GROUP].cap = 0;
+ s->opt[OPT_DEVICE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_LAMPSWITCH].name = "lamp-switch";
+ s->opt[OPT_LAMPSWITCH].title = SANE_I18N("Lampswitch");;
+ s->opt[OPT_LAMPSWITCH].desc = SANE_I18N("Manually switching the lamp(s).");
+ s->opt[OPT_LAMPSWITCH].type = SANE_TYPE_BOOL;
+ s->val[OPT_LAMPSWITCH].w = SANE_FALSE;
+
+ s->opt[OPT_LOFF4DARK].name = "lamp-off-during-dcal";
+ s->opt[OPT_LOFF4DARK].title = SANE_I18N("Lamp off during dark calibration");;
+ s->opt[OPT_LOFF4DARK].desc = SANE_I18N("Always switches lamp off when doing dark calibration.");
+ s->opt[OPT_LOFF4DARK].type = SANE_TYPE_BOOL;
+ s->val[OPT_LOFF4DARK].w = adj->skipDarkStrip;
+
+ if (dev->usbDev.Caps.Normal.DarkShadOrgY < 0)
+ _DISABLE(OPT_LOFF4DARK);
+
+ s->opt[OPT_CACHECAL].name = "calibration-cache";
+ s->opt[OPT_CACHECAL].title = SANE_I18N("Calibration data cache");;
+ s->opt[OPT_CACHECAL].desc = SANE_I18N("Enables or disables calibration data cache.");
+ s->opt[OPT_CACHECAL].type = SANE_TYPE_BOOL;
+ s->val[OPT_CACHECAL].w = adj->cacheCalData;
+
+ s->opt[OPT_CALIBRATE].name = "calibrate";
+ s->opt[OPT_CALIBRATE].title = SANE_I18N("Calibrate");;
+ s->opt[OPT_CALIBRATE].desc = SANE_I18N("Performs calibration");
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CALIBRATE].size = sizeof (SANE_Word);
+ s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_CALIBRATE].constraint.range = 0;
+ s->opt[OPT_CALIBRATE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT |
+ SANE_CAP_AUTOMATIC;
+ _ENABLE(OPT_CALIBRATE);
+ if( !adj->cacheCalData )
+ _DISABLE(OPT_CALIBRATE);
+ s->val[OPT_CALIBRATE].w = 0;
+
+ /* it's currently not available for CCD devices */
+ if( !usb_IsCISDevice(dev) && !dev->adj.altCalibrate)
+ _DISABLE(OPT_CALIBRATE);
+
+ s->opt[OPT_SPEEDUP].name = "speedup-switch";
+ s->opt[OPT_SPEEDUP].title = SANE_I18N("Speedup sensor");;
+ s->opt[OPT_SPEEDUP].desc = SANE_I18N("Enables or disables speeding up sensor movement.");
+ s->opt[OPT_SPEEDUP].type = SANE_TYPE_BOOL;
+ s->val[OPT_SPEEDUP].w = !(adj->disableSpeedup);
+
+ if( s->hw->usbDev.HwSetting.dHighSpeed == 0.0 )
+ _DISABLE(OPT_SPEEDUP);
+
+ s->opt[OPT_LAMPOFF_ONEND].name = SANE_NAME_LAMP_OFF_AT_EXIT;
+ s->opt[OPT_LAMPOFF_ONEND].title = SANE_TITLE_LAMP_OFF_AT_EXIT;
+ s->opt[OPT_LAMPOFF_ONEND].desc = SANE_DESC_LAMP_OFF_AT_EXIT;
+ s->opt[OPT_LAMPOFF_ONEND].type = SANE_TYPE_BOOL;
+ s->val[OPT_LAMPOFF_ONEND].w = adj->lampOffOnEnd;
+
+ s->opt[OPT_WARMUPTIME].name = "warmup-time";
+ s->opt[OPT_WARMUPTIME].title = SANE_I18N("Warmup-time");;
+ s->opt[OPT_WARMUPTIME].desc = SANE_I18N("Warmup-time in seconds.");
+ s->opt[OPT_WARMUPTIME].type = SANE_TYPE_INT;
+ s->opt[OPT_WARMUPTIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_WARMUPTIME].constraint.range = &warmup_range;
+ s->val[OPT_WARMUPTIME].w = adj->warmup;
+ /* only available for CCD devices*/
+ if( usb_IsCISDevice( dev )) {
+ _DISABLE(OPT_WARMUPTIME);
+ s->val[OPT_WARMUPTIME].w = 0;
+ }
+
+ s->opt[OPT_LAMPOFF_TIMER].name = "lampoff-time";
+ s->opt[OPT_LAMPOFF_TIMER].title = SANE_I18N("Lampoff-time");;
+ s->opt[OPT_LAMPOFF_TIMER].desc = SANE_I18N("Lampoff-time in seconds.");
+ s->opt[OPT_LAMPOFF_TIMER].type = SANE_TYPE_INT;
+ s->opt[OPT_LAMPOFF_TIMER].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_LAMPOFF_TIMER].constraint.range = &offtimer_range;
+ s->val[OPT_LAMPOFF_TIMER].w = adj->lampOff;
+
+ /* "Analog Frontend" group*/
+ s->opt[OPT_AFE_GROUP].title = SANE_I18N("Analog frontend");
+ s->opt[OPT_AFE_GROUP].desc = "";
+ s->opt[OPT_AFE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_AFE_GROUP].cap = SANE_CAP_ADVANCED;
+
+ s->opt[OPT_OVR_REDGAIN].name = "red-gain";
+ s->opt[OPT_OVR_REDGAIN].title = SANE_I18N("Red gain");
+ s->opt[OPT_OVR_REDGAIN].desc = SANE_I18N("Red gain value of the AFE");
+ s->opt[OPT_OVR_REDGAIN].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_REDGAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_REDGAIN].constraint.range = &gain_range;
+ s->val[OPT_OVR_REDGAIN].w = adj->rgain;
+
+ s->opt[OPT_OVR_REDOFS].name = "red-offset";
+ s->opt[OPT_OVR_REDOFS].title = SANE_I18N("Red offset");
+ s->opt[OPT_OVR_REDOFS].desc = SANE_I18N("Red offset value of the AFE");
+ s->opt[OPT_OVR_REDOFS].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_REDOFS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_REDOFS].constraint.range = &gain_range;
+ s->val[OPT_OVR_REDOFS].w = adj->rofs;
+
+ s->opt[OPT_OVR_GREENGAIN].name = "green-gain";
+ s->opt[OPT_OVR_GREENGAIN].title = SANE_I18N("Green gain");
+ s->opt[OPT_OVR_GREENGAIN].desc = SANE_I18N("Green gain value of the AFE");
+ s->opt[OPT_OVR_GREENGAIN].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_GREENGAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_GREENGAIN].constraint.range = &gain_range;
+ s->val[OPT_OVR_GREENGAIN].w = adj->ggain;
+
+ s->opt[OPT_OVR_GREENOFS].name = "green-offset";
+ s->opt[OPT_OVR_GREENOFS].title = SANE_I18N("Green offset");
+ s->opt[OPT_OVR_GREENOFS].desc = SANE_I18N("Green offset value of the AFE");
+ s->opt[OPT_OVR_GREENOFS].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_GREENOFS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_GREENOFS].constraint.range = &gain_range;
+ s->val[OPT_OVR_GREENOFS].w = adj->gofs;
+
+ s->opt[OPT_OVR_BLUEGAIN].name = "blue-gain";
+ s->opt[OPT_OVR_BLUEGAIN].title = SANE_I18N("Blue gain");
+ s->opt[OPT_OVR_BLUEGAIN].desc = SANE_I18N("Blue gain value of the AFE");
+ s->opt[OPT_OVR_BLUEGAIN].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_BLUEGAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_BLUEGAIN].constraint.range = &gain_range;
+ s->val[OPT_OVR_BLUEGAIN].w = adj->bgain;
+
+ s->opt[OPT_OVR_BLUEOFS].name = "blue-offset";
+ s->opt[OPT_OVR_BLUEOFS].title = SANE_I18N("Blue offset");
+ s->opt[OPT_OVR_BLUEOFS].desc = SANE_I18N("Blue offset value of the AFE");
+ s->opt[OPT_OVR_BLUEOFS].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_BLUEOFS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_BLUEOFS].constraint.range = &gain_range;
+ s->val[OPT_OVR_BLUEOFS].w = adj->bofs;
+
+ s->opt[OPT_OVR_RED_LOFF].name = "redlamp-off";
+ s->opt[OPT_OVR_RED_LOFF].title = SANE_I18N("Red lamp off");
+ s->opt[OPT_OVR_RED_LOFF].desc = SANE_I18N("Defines red lamp off parameter");
+ s->opt[OPT_OVR_RED_LOFF].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_RED_LOFF].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_RED_LOFF].constraint.range = &loff_range;
+ s->val[OPT_OVR_RED_LOFF].w = adj->rlampoff;
+
+ s->opt[OPT_OVR_GREEN_LOFF].name = "greenlamp-off";
+ s->opt[OPT_OVR_GREEN_LOFF].title = SANE_I18N("Green lamp off");
+ s->opt[OPT_OVR_GREEN_LOFF].desc = SANE_I18N("Defines green lamp off parameter");
+ s->opt[OPT_OVR_GREEN_LOFF].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_GREEN_LOFF].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_GREEN_LOFF].constraint.range = &loff_range;
+ s->val[OPT_OVR_GREEN_LOFF].w = adj->glampoff;
+
+ s->opt[OPT_OVR_BLUE_LOFF].name = "bluelamp-off";
+ s->opt[OPT_OVR_BLUE_LOFF].title = SANE_I18N("Blue lamp off");
+ s->opt[OPT_OVR_BLUE_LOFF].desc = SANE_I18N("Defines blue lamp off parameter");
+ s->opt[OPT_OVR_BLUE_LOFF].type = SANE_TYPE_INT;
+ s->opt[OPT_OVR_BLUE_LOFF].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_OVR_BLUE_LOFF].constraint.range = &loff_range;
+ s->val[OPT_OVR_BLUE_LOFF].w = adj->blampoff;
+
+ /* only available for CIS devices*/
+ if( !usb_IsCISDevice( dev )) {
+ _DISABLE(OPT_OVR_RED_LOFF);
+ _DISABLE(OPT_OVR_GREEN_LOFF);
+ _DISABLE(OPT_OVR_BLUE_LOFF);
+ }
+
+ /* "Button" group*/
+ s->opt[OPT_BUTTON_GROUP].title = SANE_I18N("Buttons");
+ s->opt[OPT_BUTTON_GROUP].desc = "";
+ s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* scanner buttons */
+ for( i = OPT_BUTTON_0; i <= OPT_BUTTON_LAST; i++ ) {
+
+ char name [12];
+ char title [128];
+
+ sprintf (name, "button %d", i - OPT_BUTTON_0);
+ sprintf (title, "Scanner button %d", i - OPT_BUTTON_0);
+
+ s->opt[i].name = strdup(name);
+ s->opt[i].title = strdup(title);
+ s->opt[i].desc = SANE_I18N("This option reflects the status "
+ "of the scanner buttons.");
+ s->opt[i].type = SANE_TYPE_BOOL;
+ s->opt[i].cap = SANE_CAP_SOFT_DETECT |
+ SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if (i - OPT_BUTTON_0 >= dev->usbDev.Caps.bButtons )
+ _DISABLE(i);
+
+ s->opt[i].unit = SANE_UNIT_NONE;
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[i].constraint.range = 0;
+ s->val[i].w = SANE_FALSE;
+ }
+
+ usb_UpdateButtonStatus( s );
+ return SANE_STATUS_GOOD;
+}
+
+/** Function to retrieve the vendor and product id from a given string
+ * @param src - string, that should be investigated
+ * @param dest - pointer to a string to receive the USB ID
+ */
+static void
+decodeUsbIDs( char *src, char **dest )
+{
+ const char *name;
+ char *tmp = *dest;
+ int len = strlen(_SECTION);
+
+ if( isspace(src[len])) {
+ strncpy( tmp, &src[len+1], (strlen(src)-(len+1)));
+ tmp[(strlen(src)-(len+1))] = '\0';
+ }
+
+ name = tmp;
+ name = sanei_config_skip_whitespace( name );
+
+ if( '\0' == name[0] ) {
+ DBG( _DBG_SANE_INIT, "next device uses autodetection\n" );
+ } else {
+
+ u_short pi = 0, vi = 0;
+
+ if( *name ) {
+
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+ vi = strtol( tmp, 0, 0 );
+ free( tmp );
+ }
+ }
+
+ name = sanei_config_skip_whitespace( name );
+ if( *name ) {
+
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+ pi = strtol( tmp, 0, 0 );
+ free( tmp );
+ }
+ }
+
+ /* create what we need to go through our device list...*/
+ sprintf( *dest, "0x%04X-0x%04X", vi, pi );
+ DBG( _DBG_SANE_INIT, "next device is a USB device (%s)\n", *dest );
+ }
+}
+
+#define _INT 0
+#define _FLOAT 1
+
+/** function to decode an value and give it back to the caller.
+ * @param src - pointer to the source string to check
+ * @param opt - string that keeps the option name to check src for
+ * @param what - _FLOAT or _INT
+ * @param result - pointer to the var that should receive our result
+ * @param def - default value that result should be in case of any error
+ * @return The function returns SANE_TRUE if the option has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+decodeVal( char *src, char *opt, int what, void *result, void *def )
+{
+ char *tmp, *tmp2;
+ const char *name;
+
+ /* skip the option string */
+ name = (const char*)&src[strlen("option")];
+
+ /* get the name of the option */
+ name = sanei_config_get_string( name, &tmp );
+
+ if( tmp ) {
+
+ /* on success, compare wiht the given one */
+ if( 0 == strcmp( tmp, opt )) {
+
+ DBG( _DBG_SANE_INIT, "Decoding option >%s<\n", opt );
+
+ if( _INT == what ) {
+
+ /* assign the default value for this option... */
+ *((int*)result) = *((int*)def);
+
+ if( *name ) {
+
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string( name, &tmp2 );
+
+ if( tmp2 ) {
+ *((int*)result) = strtol( tmp2, 0, 0 );
+ free( tmp2 );
+ }
+ }
+ free( tmp );
+ return SANE_TRUE;
+
+ } else if( _FLOAT == what ) {
+
+ /* assign the default value for this option... */
+ *((double*)result) = *((double*)def);
+
+ if( *name ) {
+
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string( name, &tmp2 );
+
+ if( tmp2 ) {
+ *((double*)result) = strtod( tmp2, 0 );
+ free( tmp2 );
+ }
+ }
+ free( tmp );
+ return SANE_TRUE;
+ }
+ }
+ free( tmp );
+ }
+ return SANE_FALSE;
+}
+
+/** function to retrive the device name of a given string
+ * @param src - string that keeps the option name to check src for
+ * @param dest - pointer to the string, that should receive the detected
+ * devicename
+ * @return The function returns SANE_TRUE if the devicename has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+decodeDevName( char *src, char *dest )
+{
+ char *tmp;
+ const char *name;
+
+ if( 0 == strncmp( "device", src, 6 )) {
+
+ name = (const char*)&src[strlen("device")];
+ name = sanei_config_skip_whitespace( name );
+
+ DBG( _DBG_SANE_INIT, "Decoding device name >%s<\n", name );
+
+ if( *name ) {
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+
+ strcpy( dest, tmp );
+ free( tmp );
+ return SANE_TRUE;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+/** attach a device to the backend
+ */
+static SANE_Status
+attach( const char *dev_name, CnfDef *cnf, Plustek_Device **devp )
+{
+ int cntr;
+ int result;
+ int handle;
+ Plustek_Device *dev;
+
+ DBG( _DBG_SANE_INIT, "attach (%s, %p, %p)\n",
+ dev_name, (void *)cnf, (void *)devp);
+ /* already attached ?*/
+ for( dev = first_dev; dev; dev = dev->next ) {
+
+ if( 0 == strcmp( dev->sane.name, dev_name )) {
+ if( devp )
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* allocate some memory for the device */
+ dev = malloc( sizeof (*dev));
+ if( NULL == dev )
+ return SANE_STATUS_NO_MEM;
+
+ /* assign all the stuff we need fo this device... */
+
+ memset(dev, 0, sizeof (*dev));
+
+ dev->fd = -1;
+ dev->name = strdup(dev_name); /* hold it double to avoid */
+ dev->sane.name = dev->name; /* compiler warnings */
+ dev->sane.vendor = "Plustek";
+ dev->initialized = -1; /* will be used as index too */
+ dev->calFile = NULL;
+ dev->transferRate = DEFAULT_RATE;
+
+ memcpy( &dev->adj, &cnf->adj, sizeof(AdjDef));
+
+ show_cnf( cnf );
+
+ strncpy( dev->usbId, cnf->usbId, _MAX_ID_LEN );
+
+ if( cnf->adj.lampOff >= 0 )
+ dev->usbDev.dwLampOnPeriod = cnf->adj.lampOff;
+
+ if( cnf->adj.lampOffOnEnd >= 0 )
+ dev->usbDev.bLampOffOnEnd = cnf->adj.lampOffOnEnd;
+
+ /* go ahead and open the scanner device */
+ handle = usbDev_open( dev, usbDevs, SANE_FALSE );
+ if( handle < 0 ) {
+ DBG( _DBG_ERROR,"open failed: %d\n", handle );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* okay, so assign the handle and the scanner type */
+ dev->fd = handle;
+ if( usb_IsSheetFedDevice( dev ))
+ dev->sane.type = SANE_I18N("sheetfed scanner");
+ else
+ dev->sane.type = SANE_I18N("flatbed scanner");
+
+ result = usbDev_getCaps( dev );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "usbDev_getCaps() failed(%d)\n", result);
+ usbDev_close(dev);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* save the info we got from the driver */
+ DBG( _DBG_INFO, "Scanner information:\n" );
+ if( NULL != dev->usbDev.ModelStr )
+ dev->sane.model = dev->usbDev.ModelStr;
+ else
+ dev->sane.model = "USB-Device";
+
+ DBG( _DBG_INFO, "Vendor : %s\n", dev->sane.vendor );
+ DBG( _DBG_INFO, "Model : %s\n", dev->sane.model );
+ DBG( _DBG_INFO, "Flags : 0x%08lx\n", dev->caps.dwFlag );
+
+ dev->max_x = dev->caps.wMaxExtentX*MM_PER_INCH/_MEASURE_BASE;
+ dev->max_y = dev->caps.wMaxExtentY*MM_PER_INCH/_MEASURE_BASE;
+
+ /* calculate the size of the resolution list +
+ * one more to avoid a buffer overflow, then allocate it...
+ */
+ dev->res_list = (SANE_Int *)
+ calloc((((dev->usbDev.Caps.OpticDpi.x*16)-_DEF_DPI)/25+1),
+ sizeof (SANE_Int));
+
+ if (NULL == dev->res_list) {
+ DBG( _DBG_ERROR, "calloc failed: %s\n", strerror(errno));
+ usbDev_close(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* build up the resolution table */
+ dev->res_list_size = 0;
+ for(cntr = _DEF_DPI; cntr <= (dev->usbDev.Caps.OpticDpi.x*16); cntr += 25){
+ dev->res_list_size++;
+ dev->res_list[dev->res_list_size - 1] = (SANE_Int)cntr;
+ }
+
+ /* set the limits */
+ dev->dpi_range.min = _DEF_DPI;
+ dev->dpi_range.max = dev->usbDev.Caps.OpticDpi.x * 2;
+ dev->x_range.max = SANE_FIX(dev->max_x);
+ dev->y_range.max = SANE_FIX(dev->max_y);
+
+ dev->fd = handle;
+ drvclose( dev );
+
+ DBG( _DBG_SANE_INIT, "attach: model = >%s<\n", dev->sane.model );
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** function to preset a configuration structure
+ * @param cnf - pointer to the structure that should be initialized
+ */
+static void
+init_config_struct( CnfDef *cnf )
+{
+ memset(cnf, 0, sizeof(CnfDef));
+
+ cnf->adj.warmup = -1;
+ cnf->adj.lampOff = -1;
+ cnf->adj.lampOffOnEnd = -1;
+
+ cnf->adj.posShadingY = -1;
+ cnf->adj.tpaShadingY = -1;
+ cnf->adj.negShadingY = -1;
+ cnf->adj.rgain = -1;
+ cnf->adj.ggain = -1;
+ cnf->adj.bgain = -1;
+ cnf->adj.rofs = -1;
+ cnf->adj.gofs = -1;
+ cnf->adj.bofs = -1;
+ cnf->adj.rlampoff = -1;
+ cnf->adj.glampoff = -1;
+ cnf->adj.blampoff = -1;
+
+ cnf->adj.incDarkTgt = 1;
+
+ cnf->adj.graygamma = 1.0;
+ cnf->adj.rgamma = 1.0;
+ cnf->adj.ggamma = 1.0;
+ cnf->adj.bgamma = 1.0;
+}
+
+/** intialize the backend
+ */
+SANE_Status
+sane_init( SANE_Int *version_code, SANE_Auth_Callback authorize )
+{
+ char str[PATH_MAX] = _DEFAULT_DEVICE;
+ CnfDef config;
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT();
+
+ sanei_usb_init();
+ sanei_lm983x_init();
+ sanei_thread_init();
+ sanei_access_init(STRINGIFY(BACKEND_NAME));
+
+#if defined PACKAGE && defined VERSION
+ DBG( _DBG_INFO, "Plustek backend V"BACKEND_VERSION", part of "
+ PACKAGE " " VERSION "\n");
+#else
+ DBG( _DBG_INFO, "Plustek backend V"BACKEND_VERSION"\n" );
+#endif
+
+ /* do some presettings... */
+ auth = authorize;
+ first_dev = NULL;
+ first_handle = NULL;
+ num_devices = 0;
+ usbDevs = NULL;
+
+ /* initialize the configuration structure */
+ init_config_struct( &config );
+
+ /* try and get a list of all connected AND supported devices */
+ usbGetList( &usbDevs );
+
+ if( version_code != NULL )
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open( PLUSTEK_CONFIG_FILE );
+
+ /* default to _DEFAULT_DEVICE instead of insisting on config file */
+ if( NULL == fp ) {
+ return attach( _DEFAULT_DEVICE, &config, 0 );
+ }
+
+ while( sanei_config_read( str, sizeof(str), fp)) {
+
+ DBG( _DBG_SANE_INIT, ">%s<\n", str );
+ if( str[0] == '#') /* ignore line comments */
+ continue;
+
+ len = strlen(str);
+ if( 0 == len )
+ continue; /* ignore empty lines */
+
+ /* check for options */
+ if( 0 == strncmp(str, "option", 6)) {
+
+ int ival;
+ double dval;
+
+ ival = -1;
+ decodeVal( str, "warmup", _INT, &config.adj.warmup, &ival);
+ decodeVal( str, "lampOff", _INT, &config.adj.lampOff, &ival);
+ decodeVal( str, "lOffOnEnd", _INT, &config.adj.lampOffOnEnd,&ival);
+ decodeVal( str, "posShadingY",_INT, &config.adj.posShadingY,&ival);
+ decodeVal( str, "tpaShadingY",_INT, &config.adj.tpaShadingY,&ival);
+ decodeVal( str, "negShadingY",_INT, &config.adj.negShadingY,&ival);
+ decodeVal( str, "red_gain", _INT, &config.adj.rgain, &ival);
+ decodeVal( str, "green_gain", _INT, &config.adj.ggain, &ival);
+ decodeVal( str, "blue_gain", _INT, &config.adj.bgain, &ival);
+ decodeVal( str, "red_offset", _INT, &config.adj.rofs, &ival);
+ decodeVal( str, "green_offset" , _INT, &config.adj.gofs, &ival);
+ decodeVal( str, "blue_offset", _INT, &config.adj.bofs, &ival);
+ decodeVal( str, "red_lampoff", _INT, &config.adj.rlampoff,&ival);
+ decodeVal( str, "green_lampoff", _INT, &config.adj.glampoff,&ival);
+ decodeVal( str, "blue_lampoff", _INT, &config.adj.blampoff,&ival);
+
+ ival = 0;
+ decodeVal( str, "enableTPA", _INT, &config.adj.enableTpa, &ival);
+ decodeVal( str, "cacheCalData",
+ _INT, &config.adj.cacheCalData,&ival);
+ decodeVal( str, "altCalibration",
+ _INT, &config.adj.altCalibrate,&ival);
+ decodeVal( str, "skipCalibration",
+ _INT, &config.adj.skipCalibration,&ival);
+ decodeVal( str, "skipFine",
+ _INT, &config.adj.skipFine,&ival);
+ decodeVal( str, "skipFineWhite",
+ _INT, &config.adj.skipFineWhite,&ival);
+ decodeVal( str, "skipDarkStrip",
+ _INT, &config.adj.skipDarkStrip,&ival);
+ decodeVal( str, "incDarkTarget",
+ _INT, &config.adj.incDarkTgt,&ival);
+ decodeVal( str, "invertNegatives",
+ _INT, &config.adj.invertNegatives,&ival);
+ decodeVal( str, "disableSpeedup",
+ _INT, &config.adj.disableSpeedup,&ival);
+
+ decodeVal( str, "posOffX", _INT, &config.adj.pos.x, &ival );
+ decodeVal( str, "posOffY", _INT, &config.adj.pos.y, &ival );
+
+ decodeVal( str, "negOffX", _INT, &config.adj.neg.x, &ival );
+ decodeVal( str, "negOffY", _INT, &config.adj.neg.y, &ival );
+
+ decodeVal( str, "tpaOffX", _INT, &config.adj.tpa.x, &ival );
+ decodeVal( str, "tpaOffY", _INT, &config.adj.tpa.y, &ival );
+
+ decodeVal( str, "mov", _INT, &config.adj.mov, &ival);
+
+ dval = 1.0;
+ decodeVal( str, "grayGamma", _FLOAT, &config.adj.graygamma,&dval);
+ decodeVal( str, "redGamma", _FLOAT, &config.adj.rgamma, &dval );
+ decodeVal( str, "greenGamma", _FLOAT, &config.adj.ggamma, &dval );
+ decodeVal( str, "blueGamma", _FLOAT, &config.adj.bgamma, &dval );
+ continue;
+
+ /* check for sections: */
+ } else if( 0 == strncmp( str, _SECTION, strlen(_SECTION))) {
+
+ char *tmp;
+
+ /* new section, try and attach previous device */
+ if( config.devName[0] != '\0' ) {
+ attach( config.devName, &config, 0 );
+ } else {
+ if( first_dev != NULL ) {
+ DBG( _DBG_WARNING, "section contains no device name,"
+ " ignored!\n" );
+ }
+ }
+
+ /* re-initialize the configuration structure */
+ init_config_struct( &config );
+
+ tmp = config.usbId;
+ decodeUsbIDs( str, &tmp );
+
+ DBG( _DBG_SANE_INIT, "... next device\n" );
+ continue;
+
+ } else if( SANE_TRUE == decodeDevName( str, config.devName )) {
+ continue;
+ }
+
+ /* ignore other stuff... */
+ DBG( _DBG_SANE_INIT, "ignoring >%s<\n", str );
+ }
+ fclose (fp);
+
+ /* try to attach the last device in the config file... */
+ if( config.devName[0] != '\0' )
+ attach( config.devName, &config, 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+/** cleanup the backend...
+ */
+void
+sane_exit( void )
+{
+ DevList *tmp;
+ Plustek_Device *dev, *next;
+
+ DBG( _DBG_SANE_INIT, "sane_exit\n" );
+
+ for( dev = first_dev; dev; ) {
+
+ next = dev->next;
+
+ /* call the shutdown function of each device... */
+ usbDev_shutdown( dev );
+
+ /* we're doin' this to avoid compiler warnings as dev->sane.name
+ * is defined as const char*
+ */
+ if( dev->sane.name )
+ free( dev->name );
+
+ if( dev->calFile )
+ free( dev->calFile );
+
+ if( dev->res_list )
+ free( dev->res_list );
+ free( dev );
+
+ dev = next;
+ }
+
+ if( devlist )
+ free( devlist );
+
+ while( usbDevs ) {
+ tmp = usbDevs->next;
+ free( usbDevs );
+ usbDevs = tmp;
+ }
+
+ usbDevs = NULL;
+ devlist = NULL;
+ auth = NULL;
+ first_dev = NULL;
+ first_handle = NULL;
+}
+
+/** return a list of all devices
+ */
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only )
+{
+ int i;
+ Plustek_Device *dev;
+
+ DBG(_DBG_SANE_INIT, "sane_get_devices (%p, %ld)\n",
+ (void *)device_list, (long) local_only);
+
+ /* already called, so cleanup */
+ if( devlist )
+ free( devlist );
+
+ devlist = malloc((num_devices + 1) * sizeof (devlist[0]));
+ if ( NULL == devlist )
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+/** open the sane device
+ */
+SANE_Status
+sane_open( SANE_String_Const devicename, SANE_Handle* handle )
+{
+ SANE_Status status;
+ Plustek_Device *dev;
+ Plustek_Scanner *s;
+ CnfDef config;
+
+ DBG( _DBG_SANE_INIT, "sane_open - %s\n", devicename );
+
+ if( devicename[0] ) {
+ for( dev = first_dev; dev; dev = dev->next ) {
+ if( strcmp( dev->sane.name, devicename ) == 0 )
+ break;
+ }
+
+ if( !dev ) {
+
+ memset(&config, 0, sizeof(CnfDef));
+
+ status = attach( devicename, &config, &dev );
+ if( SANE_STATUS_GOOD != status )
+ return status;
+ }
+ } else {
+ /* empty devicename -> use first device */
+ dev = first_dev;
+ }
+
+ if( !dev )
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if( NULL == s )
+ return SANE_STATUS_NO_MEM;
+
+ memset(s, 0, sizeof (*s));
+ s->r_pipe = -1;
+ s->w_pipe = -1;
+ s->hw = dev;
+ s->scanning = SANE_FALSE;
+ s->calibrating = SANE_FALSE;
+
+ init_options(s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+void
+sane_close( SANE_Handle handle )
+{
+ Plustek_Scanner *prev, *s = handle;
+
+ DBG( _DBG_SANE_INIT, "sane_close\n" );
+
+ if( s->calibrating )
+ do_cancel( s, SANE_FALSE );
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+
+ for( s = first_handle; s; s = s->next ) {
+ if( s == handle )
+ break;
+ prev = s;
+ }
+
+ if (!s) {
+ DBG( _DBG_ERROR, "close: invalid handle %p\n", handle);
+ return;
+ }
+
+ close_pipe( s );
+
+ if( NULL != s->buf )
+ free(s->buf);
+
+ drvclose( s->hw );
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free(s);
+}
+
+/** goes through a string list and returns the start-address of the string
+ * that has been found, or NULL on error
+ */
+static const SANE_String_Const*
+search_string_list( const SANE_String_Const *list, SANE_String value )
+{
+ while( *list != NULL && strcmp(value, *list) != 0 )
+ ++list;
+
+ if( *list == NULL )
+ return NULL;
+
+ return list;
+}
+
+/**
+ */
+static int
+do_calibration( void *args )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)args;
+ Plustek_Device *dev = s->hw;
+ DCapsDef *caps = &dev->usbDev.Caps;
+ int scanmode, rc;
+ int modes[] = { COLOR_BW, COLOR_256GRAY, COLOR_GRAY16,
+ COLOR_TRUE24, COLOR_TRUE48 };
+
+ thread_entry();
+
+ /* if the device does only support color scanning, there's no need
+ * to calibrate the gray modes
+ */
+ if (caps->workaroundFlag & _WAF_GRAY_FROM_COLOR)
+ scanmode = 3;
+ else
+ scanmode = 0;
+
+ for ( ; scanmode < 5; scanmode++ ) {
+
+ if (caps->workaroundFlag & _WAF_ONLY_8BIT) {
+
+ if ((modes[scanmode] == COLOR_GRAY16) ||
+ (modes[scanmode] == COLOR_TRUE48)) {
+ continue;
+ }
+ }
+
+ dev->scanning.dwFlag |= SCANFLAG_Calibration;
+
+ if (SANE_STATUS_GOOD == local_sane_start(s, modes[scanmode])) {
+
+ /* prepare for scanning: speed-test, warmup, calibration */
+ rc = usbDev_Prepare( dev, s->buf );
+ if( rc != 0 || scanmode == 4) {
+ if (rc != 0 )
+ DBG(_DBG_INFO,"Calibration canceled!\n");
+ m_fStart = SANE_TRUE;
+ m_fAutoPark = SANE_TRUE;
+ }
+
+ drvclose( dev );
+ if( rc != 0 )
+ break;
+ } else {
+ DBG(_DBG_ERROR, "local_sane_start() failed!\n");
+ break;
+ }
+ }
+
+ /* restore the settings */
+ dev->scanning.dwFlag &= ~SCANFLAG_Calibration;
+ s->calibrating = SANE_FALSE;
+ return 0;
+}
+
+/** return or set the parameter values, also do some checks
+ */
+SANE_Status
+sane_control_option( SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int *info )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+ Plustek_Device *dev = s->hw;
+ AdjDef *adj = &dev->adj;
+ DCapsDef *caps = &dev->usbDev.Caps;
+ SANE_Status status;
+ const SANE_String_Const *optval;
+ int scanmode;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ /* in calibration mode, we do not allow setting any value! */
+ if(s->calibrating) {
+ if (action == SANE_ACTION_SET_VALUE) {
+ if (option == OPT_CALIBRATE) {
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ do_cancel(s, SANE_TRUE);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* okay, we need some exceptions */
+ switch (option) {
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y: break;
+ default: return SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+ }
+
+ if ((option < 0) || (option >= NUM_OPTIONS))
+ return SANE_STATUS_INVAL;
+
+ if (NULL != info)
+ *info = 0;
+
+ switch( action ) {
+
+ case SANE_ACTION_GET_VALUE:
+
+ switch (option) {
+ case OPT_PREVIEW:
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_LAMPSWITCH:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_LAMPOFF_ONEND:
+ case OPT_LOFF4DARK:
+ case OPT_CACHECAL:
+ case OPT_SPEEDUP:
+ case OPT_OVR_REDGAIN:
+ case OPT_OVR_GREENGAIN:
+ case OPT_OVR_BLUEGAIN:
+ case OPT_OVR_REDOFS:
+ case OPT_OVR_GREENOFS:
+ case OPT_OVR_BLUEOFS:
+ case OPT_OVR_RED_LOFF:
+ case OPT_OVR_GREEN_LOFF:
+ case OPT_OVR_BLUE_LOFF:
+ case OPT_LAMPOFF_TIMER:
+ case OPT_WARMUPTIME:
+ *(SANE_Word *)value = s->val[option].w;
+ break;
+
+ case OPT_BUTTON_0:
+ if(!s->calibrating)
+ usb_UpdateButtonStatus(s);
+ case OPT_BUTTON_1:
+ case OPT_BUTTON_2:
+ case OPT_BUTTON_3:
+ case OPT_BUTTON_4:
+ /* copy the button state */
+ *(SANE_Word*)value = s->val[option].w;
+ /* clear the button state */
+ s->val[option].w = SANE_FALSE;
+ break;
+
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *)value =
+ (s->val[option].w << SANE_FIXED_SCALE_SHIFT);
+ break;
+
+ case OPT_MODE:
+ case OPT_EXT_MODE:
+ strcpy ((char *) value,
+ s->opt[option].constraint.string_list[s->val[option].w]);
+ break;
+
+ /* word array options: */
+ case OPT_GAMMA_VECTOR:
+ DBG( _DBG_INFO, "Reading MASTER gamma.\n" );
+ memcpy( value, s->val[option].wa, s->opt[option].size );
+ break;
+
+ case OPT_GAMMA_VECTOR_R:
+ DBG( _DBG_INFO, "Reading RED gamma.\n" );
+ memcpy( value, s->val[option].wa, s->opt[option].size );
+ break;
+
+ case OPT_GAMMA_VECTOR_G:
+ DBG( _DBG_INFO, "Reading GREEN gamma.\n" );
+ memcpy( value, s->val[option].wa, s->opt[option].size );
+ break;
+
+ case OPT_GAMMA_VECTOR_B:
+ DBG( _DBG_INFO, "Reading BLUE gamma.\n" );
+ memcpy( value, s->val[option].wa, s->opt[option].size );
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ status = sanei_constrain_value( s->opt + option, value, info );
+ if( SANE_STATUS_GOOD != status )
+ return status;
+
+ optval = NULL;
+ if( SANE_CONSTRAINT_STRING_LIST == s->opt[option].constraint_type ) {
+
+ optval = search_string_list( s->opt[option].constraint.string_list,
+ (char *) value);
+ if( NULL == optval )
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (option) {
+
+ case OPT_RESOLUTION: {
+ int n;
+ int min_d = dev->res_list[dev->res_list_size - 1];
+ int v = *(SANE_Word *)value;
+ int best = v;
+
+ for( n = 0; n < dev->res_list_size; n++ ) {
+ int d = abs(v - dev->res_list[n]);
+
+ if( d < min_d ) {
+ min_d = d;
+ best = dev->res_list[n];
+ }
+ }
+
+ s->val[option].w = (SANE_Word)best;
+
+ if( v != best )
+ *(SANE_Word *)value = best;
+
+ if( NULL != info ) {
+ if( v != best )
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+ }
+
+ case OPT_PREVIEW:
+ case OPT_BIT_DEPTH:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *)value;
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_CACHECAL:
+ s->val[option].w = *(SANE_Word *)value;
+ dev->adj.cacheCalData = s->val[option].w;
+ if( !dev->adj.cacheCalData )
+ _DISABLE(OPT_CALIBRATE);
+ else {
+ if( usb_IsCISDevice(dev) || dev->adj.altCalibrate)
+ _ENABLE(OPT_CALIBRATE);
+ }
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_CALIBRATE:
+ if (s->calibrating) {
+ do_cancel( s, SANE_FALSE );
+ s->calibrating = SANE_FALSE;
+ } else {
+ sc = s;
+ s->r_pipe = -1;
+ s->w_pipe = -1;
+ s->reader_pid = sanei_thread_begin(do_calibration, s);
+ s->calibrating = SANE_TRUE;
+ signal( SIGCHLD, sig_chldhandler );
+ }
+ if (NULL != info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_SPEEDUP:
+ s->val[option].w = *(SANE_Word *)value;
+ dev->adj.disableSpeedup = !(s->val[option].w);
+ break;
+
+ case OPT_LOFF4DARK:
+ s->val[option].w = *(SANE_Word *)value;
+ dev->adj.skipDarkStrip = !(s->val[option].w);
+ break;
+
+ case OPT_LAMPSWITCH:
+ s->val[option].w = *(SANE_Word *)value;
+ usb_LampSwitch( dev, s->val[option].w );
+ if( s->val[option].w == 0 )
+ usb_StopLampTimer( dev );
+ else
+ usb_StartLampTimer( dev );
+ break;
+
+ case OPT_LAMPOFF_ONEND:
+ s->val[option].w = *(SANE_Word *)value;
+ dev->adj.lampOffOnEnd = s->val[option].w;
+ usb_CheckAndCopyAdjs( dev );
+ break;
+
+ case OPT_CUSTOM_GAMMA:
+ s->val[option].w = *(SANE_Word *)value;
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ scanmode = getScanMode( s );
+
+ _DISABLE(OPT_GAMMA_VECTOR);
+ _DISABLE(OPT_GAMMA_VECTOR_R);
+ _DISABLE(OPT_GAMMA_VECTOR_G);
+ _DISABLE(OPT_GAMMA_VECTOR_B);
+
+ if( SANE_TRUE == s->val[option].w ) {
+ DBG( _DBG_INFO, "Using custom gamma settings.\n" );
+ if((scanmode == COLOR_256GRAY) ||
+ (scanmode == COLOR_GRAY16)) {
+ _ENABLE(OPT_GAMMA_VECTOR);
+ } else {
+ _ENABLE(OPT_GAMMA_VECTOR_R);
+ _ENABLE(OPT_GAMMA_VECTOR_G);
+ _ENABLE(OPT_GAMMA_VECTOR_B);
+ }
+ } else {
+
+ DBG( _DBG_INFO, "NOT using custom gamma settings.\n" );
+ initGammaSettings( s );
+
+ if((scanmode == COLOR_256GRAY) ||
+ (scanmode == COLOR_GRAY16)) {
+ _DISABLE(OPT_GAMMA_VECTOR);
+ } else {
+ _DISABLE(OPT_GAMMA_VECTOR_R);
+ _DISABLE(OPT_GAMMA_VECTOR_G);
+ _DISABLE(OPT_GAMMA_VECTOR_B);
+ }
+ }
+ break;
+
+ case OPT_LAMPOFF_TIMER:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->lampOff = (*(SANE_Word *)value);
+ usb_CheckAndCopyAdjs( dev );
+ break;
+
+ case OPT_WARMUPTIME:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->warmup = (*(SANE_Word *)value);
+ usb_CheckAndCopyAdjs( dev );
+ break;
+
+ case OPT_OVR_REDGAIN:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->rgain = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_GREENGAIN:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->ggain = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_BLUEGAIN:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->bgain = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_REDOFS:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->rofs = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_GREENOFS:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->gofs = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_BLUEOFS:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->bofs = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_RED_LOFF:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->rlampoff = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_GREEN_LOFF:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->glampoff = (*(SANE_Word *)value);
+ break;
+ case OPT_OVR_BLUE_LOFF:
+ s->val[option].w = (*(SANE_Word *)value);
+ adj->blampoff = (*(SANE_Word *)value);
+ break;
+
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ s->val[option].w =
+ ((*(SANE_Word *)value) >> SANE_FIXED_SCALE_SHIFT);
+ break;
+
+ case OPT_MODE:
+ s->val[option].w = optval - s->opt[option].constraint.string_list;
+ scanmode = getScanMode( s );
+
+ _ENABLE(OPT_CONTRAST);
+ _ENABLE(OPT_BIT_DEPTH);
+ _ENABLE(OPT_CUSTOM_GAMMA);
+ if (scanmode == COLOR_BW) {
+ _DISABLE(OPT_CONTRAST);
+ _DISABLE(OPT_CUSTOM_GAMMA);
+ _DISABLE(OPT_BIT_DEPTH);
+ }
+
+ if (caps->workaroundFlag & _WAF_ONLY_8BIT)
+ _DISABLE(OPT_BIT_DEPTH);
+
+ _DISABLE(OPT_GAMMA_VECTOR);
+ _DISABLE(OPT_GAMMA_VECTOR_R);
+ _DISABLE(OPT_GAMMA_VECTOR_G);
+ _DISABLE(OPT_GAMMA_VECTOR_B);
+
+ if( s->val[OPT_CUSTOM_GAMMA].w &&
+ !(s->opt[OPT_CUSTOM_GAMMA].cap & SANE_CAP_INACTIVE)) {
+
+ if((scanmode == COLOR_256GRAY) ||
+ (scanmode == COLOR_GRAY16)) {
+ _ENABLE(OPT_GAMMA_VECTOR);
+ } else {
+ _ENABLE(OPT_GAMMA_VECTOR_R);
+ _ENABLE(OPT_GAMMA_VECTOR_G);
+ _ENABLE(OPT_GAMMA_VECTOR_B);
+ }
+ }
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_EXT_MODE: {
+ s->val[option].w = optval - s->opt[option].constraint.string_list;
+
+ /* change the area and mode_list when changing the source
+ */
+ if( s->val[option].w == 0 ) {
+ dev->scanning.sParam.bSource = SOURCE_Reflection;
+
+ dev->dpi_range.min = _DEF_DPI;
+
+ dev->x_range.max = SANE_FIX(dev->max_x);
+ dev->y_range.max = SANE_FIX(dev->max_y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
+
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 2; /* HEINER COLOR_TRUE24;*/
+
+ } else {
+
+ dev->dpi_range.min = _TPAMinDpi;
+
+ if( s->val[option].w == 1 ) {
+
+ dev->scanning.sParam.bSource = SOURCE_Transparency;
+ if( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_LargeTPA ) {
+ dev->x_range.max = SANE_FIX(_SCALE(_TPALargePageWidth));
+ dev->y_range.max = SANE_FIX(_SCALE(_TPALargePageHeight));
+ } else {
+ dev->x_range.max = SANE_FIX(_SCALE(_TPAPageWidth));
+ dev->y_range.max = SANE_FIX(_SCALE(_TPAPageHeight));
+ }
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TP_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TP_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_TP_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_TP_BRY);
+
+ } else {
+ dev->scanning.sParam.bSource = SOURCE_Negative;
+ if( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_LargeTPA ) {
+ dev->x_range.max = SANE_FIX(_SCALE(_NegLargePageWidth));
+ dev->y_range.max = SANE_FIX(_SCALE(_NegLargePageHeight));
+ } else {
+ dev->x_range.max = SANE_FIX(_SCALE(_NegPageWidth));
+ dev->y_range.max = SANE_FIX(_SCALE(_NegPageHeight));
+ }
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_NEG_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_NEG_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_NEG_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_NEG_BRY);
+ }
+ s->opt[OPT_MODE].constraint.string_list = &mode_list[2];
+ s->val[OPT_MODE].w = 0; /* COLOR_24 is the default */
+ }
+ if( s->val[OPT_LAMPSWITCH].w != 0 ) {
+ usb_LampSwitch( dev, s->val[OPT_LAMPSWITCH].w );
+ if( s->val[OPT_LAMPSWITCH].w == 0 )
+ usb_StopLampTimer( dev );
+ else
+ usb_StartLampTimer( dev );
+ }
+
+ _ENABLE(OPT_CONTRAST);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+ case OPT_GAMMA_VECTOR:
+ DBG( _DBG_INFO, "Setting MASTER gamma.\n" );
+ memcpy( s->val[option].wa, value, s->opt[option].size );
+ checkGammaSettings(s);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_GAMMA_VECTOR_R:
+ DBG( _DBG_INFO, "Setting RED gamma.\n" );
+ memcpy( s->val[option].wa, value, s->opt[option].size );
+ checkGammaSettings(s);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_GAMMA_VECTOR_G:
+ DBG( _DBG_INFO, "Setting GREEN gamma.\n" );
+ memcpy( s->val[option].wa, value, s->opt[option].size );
+ checkGammaSettings(s);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_GAMMA_VECTOR_B:
+ DBG( _DBG_INFO, "Setting BLUE gamma.\n" );
+ memcpy( s->val[option].wa, value, s->opt[option].size );
+ checkGammaSettings(s);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** according to the option number, return a pointer to a descriptor
+ */
+const SANE_Option_Descriptor*
+sane_get_option_descriptor( SANE_Handle handle, SANE_Int option )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ if((option < 0) || (option >= NUM_OPTIONS))
+ return NULL;
+
+ return &(s->opt[option]);
+}
+
+/** return the current parameter settings
+ */
+SANE_Status
+sane_get_parameters( SANE_Handle handle, SANE_Parameters *params )
+{
+ int ndpi;
+ int scanmode;
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ /* if we're calling from within, calc best guess
+ * do the same, if sane_get_parameters() is called
+ * by a frontend before sane_start() is called
+ */
+ if((NULL == params) || (s->scanning != SANE_TRUE)) {
+
+ memset(&s->params, 0, sizeof (SANE_Parameters));
+
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ s->params.pixels_per_line = SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH * ndpi;
+
+ s->params.lines = SANE_UNFIX( s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH * ndpi;
+
+ /* pixels_per_line seems to be 8 * n. */
+ /* s->params.pixels_per_line = s->params.pixels_per_line & ~7; debug only */
+
+ s->params.last_frame = SANE_TRUE;
+ scanmode = getScanMode( s );
+
+ if( scanmode == COLOR_TRUE24 || scanmode == COLOR_TRUE48 ) {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ } else {
+ s->params.format = SANE_FRAME_GRAY;
+ if (s->params.depth == 1)
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ else
+ s->params.bytes_per_line = s->params.pixels_per_line *
+ s->params.depth / 8;
+ }
+
+ /* if sane_get_parameters() was called before sane_start() */
+ /* pass new values to the caller */
+ if ((NULL != params) && (s->scanning != SANE_TRUE))
+ *params = s->params;
+ } else {
+ *params = s->params;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** initiate the scan process
+ */
+static SANE_Status
+local_sane_start(Plustek_Scanner *s, int scanmode )
+{
+ Plustek_Device *dev;
+
+ int result;
+ int ndpi;
+ int left, top;
+ int width, height;
+ double dpi_x, dpi_y;
+ CropInfo crop;
+ ScanInfo sinfo;
+ SANE_Word tmp;
+
+ /* clear it out just in case */
+ memset(&crop, 0, sizeof(crop));
+
+ dev = s->hw;
+
+ /* check if we're called from the option dialog! */
+ if (usb_InCalibrationMode(dev))
+ crop.ImgDef.dwFlag = SCANFLAG_Calibration;
+
+ /* open the driver and get some information about the scanner
+ */
+ dev->fd = usbDev_open( dev, NULL, SANE_TRUE );
+ if( dev->fd < 0 ) {
+ DBG( _DBG_ERROR, "sane_start: open failed: %d\n", errno);
+
+ if( errno == EBUSY )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ result = usbDev_getCaps( dev );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "usbDev_getCaps() failed(%d)\n", result);
+ sanei_access_unlock( dev->sane.name );
+ usbDev_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* All ready to go. Set image def and see what the scanner
+ * says for crop info.
+ */
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ /* exchange the values as we can't deal with
+ * negative heights and so on...*/
+ tmp = s->val[OPT_TL_X].w;
+ if( tmp > s->val[OPT_BR_X].w ) {
+ DBG( _DBG_INFO, "exchanging BR-X - TL-X\n" );
+ s->val[OPT_TL_X].w = s->val[OPT_BR_X].w;
+ s->val[OPT_BR_X].w = tmp;
+ }
+
+ tmp = s->val[OPT_TL_Y].w;
+ if( tmp > s->val[OPT_BR_Y].w ) {
+ DBG( _DBG_INFO, "exchanging BR-Y - TL-Y\n" );
+ s->val[OPT_TL_Y].w = s->val[OPT_BR_Y].w;
+ s->val[OPT_BR_Y].w = tmp;
+ }
+
+ /* position and extent are always relative to 300 dpi */
+ dpi_x = (double)dev->usbDev.Caps.OpticDpi.x;
+ dpi_y = (double)dev->usbDev.Caps.OpticDpi.x * 2;
+
+ left = (int)(SANE_UNFIX (s->val[OPT_TL_X].w)*dpi_x/
+ (MM_PER_INCH*(dpi_x/300.0)));
+ top = (int)(SANE_UNFIX (s->val[OPT_TL_Y].w)*dpi_y/
+ (MM_PER_INCH*(dpi_y/300.0)));
+ width = (int)(SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) *
+ dpi_x / (MM_PER_INCH *(dpi_x/300.0)));
+ height = (int)(SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) *
+ dpi_y / (MM_PER_INCH *(dpi_y/300.0)));
+
+ /* adjust mode list according to the model we use and the
+ * source we have
+ */
+ DBG( _DBG_INFO, "scanmode = %u\n", scanmode );
+
+ crop.ImgDef.xyDpi.x = ndpi;
+ crop.ImgDef.xyDpi.y = ndpi;
+ crop.ImgDef.crArea.x = left; /* offset from left edge to area you want to scan */
+ crop.ImgDef.crArea.y = top; /* offset from top edge to area you want to scan */
+ crop.ImgDef.crArea.cx = width; /* always relative to 300 dpi */
+ crop.ImgDef.crArea.cy = height;
+ crop.ImgDef.wDataType = scanmode;
+ crop.ImgDef.dwFlag |= SCANDEF_QualityScan;
+
+ switch( s->val[OPT_EXT_MODE].w ) {
+ case 1: crop.ImgDef.dwFlag |= SCANDEF_Transparency; break;
+ case 2: crop.ImgDef.dwFlag |= SCANDEF_Negative; break;
+ default: break;
+ }
+
+ result = usbDev_getCropInfo( dev, &crop );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "usbDev_getCropInfo() failed(%d)\n", result );
+ usbDev_close( dev );
+ sanei_access_unlock( dev->sane.name );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* DataInf.dwAppPixelsPerLine = crop.dwPixelsPerLine; */
+ s->params.pixels_per_line = crop.dwPixelsPerLine;
+ s->params.bytes_per_line = crop.dwBytesPerLine;
+ s->params.lines = crop.dwLinesPerArea;
+
+ /* build a SCANINFO block and get ready to scan it */
+ crop.ImgDef.dwFlag |= SCANDEF_QualityScan;
+
+ /* remove that for preview scans */
+ if( s->val[OPT_PREVIEW].w )
+ crop.ImgDef.dwFlag &= (~SCANDEF_QualityScan);
+
+ /* set adjustments for brightness and contrast */
+ sinfo.siBrightness = s->val[OPT_BRIGHTNESS].w;
+ sinfo.siContrast = s->val[OPT_CONTRAST].w;
+
+ memcpy( &sinfo.ImgDef, &crop.ImgDef, sizeof(ImgDef));
+
+ DBG( _DBG_SANE_INIT, "brightness %i, contrast %i\n",
+ sinfo.siBrightness, sinfo.siContrast );
+
+ result = usbDev_setScanEnv( dev, &sinfo );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "usbDev_setScanEnv() failed(%d)\n", result );
+ usbDev_close( dev );
+ sanei_access_unlock( dev->sane.name );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* download gamma correction tables... */
+ if( scanmode <= COLOR_GRAY16 ) {
+ usbDev_setMap( dev, s->gamma_table[0], s->gamma_length, _MAP_MASTER);
+ } else {
+ usbDev_setMap( dev, s->gamma_table[1], s->gamma_length, _MAP_RED );
+ usbDev_setMap( dev, s->gamma_table[2], s->gamma_length, _MAP_GREEN );
+ usbDev_setMap( dev, s->gamma_table[3], s->gamma_length, _MAP_BLUE );
+ }
+
+ tsecs = 0; /* reset timer */
+
+ result = usbDev_startScan( dev );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "usbDev_startScan() failed(%d)\n", result );
+ usbDev_close( dev );
+ sanei_access_unlock( dev->sane.name );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG( _DBG_SANE_INIT, "dwflag = 0x%lx dwBytesLine = %ld\n",
+ dev->scanning.dwFlag, dev->scanning.dwBytesLine );
+ DBG( _DBG_SANE_INIT, "Lines = %d\n", s->params.lines);
+ DBG( _DBG_SANE_INIT, "Bytes per Line = %d\n", s->params.bytes_per_line );
+ DBG( _DBG_SANE_INIT, "Bitdepth = %d\n", s->params.depth );
+
+ if (usb_InCalibrationMode(dev)) {
+ if (s->buf)
+ free(s->buf);
+ s->buf = NULL;
+ } else {
+
+ if (s->params.lines == 0 || s->params.bytes_per_line == 0) {
+ DBG( _DBG_ERROR, "nothing to scan!\n" );
+ usbDev_close( dev );
+ sanei_access_unlock( dev->sane.name );
+ return SANE_STATUS_INVAL;
+ }
+
+ s->buf = realloc( s->buf, (s->params.lines) * s->params.bytes_per_line );
+ if( NULL == s->buf ) {
+ DBG( _DBG_ERROR, "realloc failed\n" );
+ usbDev_close( dev );
+ sanei_access_unlock( dev->sane.name );
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ tsecs = (unsigned long)time(NULL);
+ DBG( _DBG_INFO, "TIME START\n" );
+
+ DBG( _DBG_SANE_INIT, "local_sane_start done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** initiate the scan process
+ */
+SANE_Status
+sane_start( SANE_Handle handle )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+ Plustek_Device *dev = s->hw;
+ SANE_Status status;
+ int fds[2];
+
+ DBG( _DBG_SANE_INIT, "sane_start\n" );
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ /* in the end we wait until the calibration is done... */
+ if (s->calibrating) {
+ while (s->calibrating) {
+ sleep(1);
+ }
+
+ /* we have been cancelled? */
+ if (cancelRead)
+ return SANE_STATUS_CANCELLED;
+ }
+
+ status = sane_get_parameters (handle, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DBG( _DBG_ERROR, "sane_get_parameters failed\n" );
+ return status;
+ }
+
+ status = local_sane_start(s, getScanMode(s));
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+
+ s->scanning = SANE_TRUE;
+
+ /*
+ * everything prepared, so start the child process and a pipe to communicate
+ * pipe --> fds[0]=read-fd, fds[1]=write-fd
+ */
+ if( pipe(fds) < 0 ) {
+ DBG( _DBG_ERROR, "ERROR: could not create pipe\n" );
+ s->scanning = SANE_FALSE;
+ usbDev_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* create reader routine as new process */
+ s->bytes_read = 0;
+ s->r_pipe = fds[0];
+ s->w_pipe = fds[1];
+ s->ipc_read_done = SANE_FALSE;
+ s->reader_pid = sanei_thread_begin( reader_process, s );
+
+ cancelRead = SANE_FALSE;
+
+ if( s->reader_pid == -1 ) {
+ DBG( _DBG_ERROR, "ERROR: could not start reader task\n" );
+ s->scanning = SANE_FALSE;
+ usbDev_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ signal( SIGCHLD, sig_chldhandler );
+
+ if( sanei_thread_is_forked()) {
+ close( s->w_pipe );
+ s->w_pipe = -1;
+ }
+
+ DBG( _DBG_SANE_INIT, "sane_start done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** function to read the data from our child process
+ */
+SANE_Status
+sane_read( SANE_Handle handle, SANE_Byte *data,
+ SANE_Int max_length, SANE_Int *length )
+{
+ Plustek_Scanner *s = (Plustek_Scanner*)handle;
+ ssize_t nread;
+#ifdef USE_IPC
+ static IPCDef ipc;
+ unsigned char *buf;
+ static unsigned long c = 0;
+#endif
+
+ *length = 0;
+
+#ifdef USE_IPC
+ /* first try and read IPC... */
+ if( !s->ipc_read_done ) {
+
+ buf = (unsigned char*)&ipc;
+ for( c = 0; c < sizeof(ipc); ) {
+ nread = read( s->r_pipe, buf, sizeof(ipc));
+ if( nread < 0 ) {
+ if( EAGAIN != errno ) {
+ do_cancel( s, SANE_TRUE );
+ return SANE_STATUS_IO_ERROR;
+ } else {
+ return SANE_STATUS_GOOD;
+ }
+ } else {
+ c += nread;
+ buf += nread;
+ if( c == sizeof(ipc)) {
+ s->ipc_read_done = SANE_TRUE;
+ break;
+ }
+ }
+ }
+ s->hw->transferRate = ipc.transferRate;
+ DBG( _DBG_INFO, "IPC: Transferrate = %lu Bytes/s\n",
+ ipc.transferRate );
+ }
+#endif
+ /* here we read all data from the driver... */
+ nread = read( s->r_pipe, data, max_length );
+ DBG( _DBG_READ, "sane_read - read %ld bytes\n", (long)nread );
+ if (!(s->scanning)) {
+ return do_cancel( s, SANE_TRUE );
+ }
+
+ if( nread < 0 ) {
+
+ if( EAGAIN == errno ) {
+
+ /* if we already had red the picture, so it's okay and stop */
+ if( s->bytes_read ==
+ (unsigned long)(s->params.lines * s->params.bytes_per_line)) {
+ sanei_thread_waitpid( s->reader_pid, 0 );
+ s->reader_pid = -1;
+ s->scanning = SANE_FALSE;
+ drvclose( s->hw );
+ return close_pipe(s);
+ }
+
+ /* else force the frontend to try again*/
+ return SANE_STATUS_GOOD;
+
+ } else {
+ DBG( _DBG_ERROR, "ERROR: errno=%d\n", errno );
+ do_cancel( s, SANE_TRUE );
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *length = nread;
+ s->bytes_read += nread;
+
+ /* nothing red means that we're finished OR we had a problem... */
+ if( 0 == nread ) {
+
+ drvclose( s->hw );
+ s->exit_code = sanei_thread_get_status( s->reader_pid );
+
+ if( SANE_STATUS_GOOD != s->exit_code ) {
+ close_pipe(s);
+ return s->exit_code;
+ }
+ s->reader_pid = -1;
+ s->scanning = SANE_FALSE;
+ return close_pipe(s);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** cancel the scanning process
+ */
+void
+sane_cancel( SANE_Handle handle )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_cancel\n" );
+
+ if (s->scanning || s->calibrating)
+ do_cancel( s, SANE_FALSE );
+}
+
+/** set the pipe to blocking/non blocking mode
+ */
+SANE_Status
+sane_set_io_mode( SANE_Handle handle, SANE_Bool non_blocking )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_set_io_mode: non_blocking=%d\n",non_blocking );
+
+ if ( !s->scanning ) {
+ DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ if( -1 == s->r_pipe ) {
+ DBG( _DBG_ERROR, "ERROR: not supported !\n" );
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if( fcntl (s->r_pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) {
+ DBG( _DBG_ERROR, "ERROR: could not set to non-blocking mode !\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG( _DBG_SANE_INIT, "sane_set_io_mode done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** return the descriptor if available
+ */
+SANE_Status
+sane_get_select_fd( SANE_Handle handle, SANE_Int * fd )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_get_select_fd\n" );
+
+ if( !s->scanning ) {
+ DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->r_pipe;
+
+ DBG( _DBG_SANE_INIT, "sane_get_select_fd done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/* END PLUSTEK.C ............................................................*/
diff --git a/backend/plustek.conf.in b/backend/plustek.conf.in
new file mode 100644
index 0000000..e16823e
--- /dev/null
+++ b/backend/plustek.conf.in
@@ -0,0 +1,184 @@
+# Plustek-SANE Backend configuration file
+# For use with LM9831/2/3 based USB scanners
+#
+
+# each device needs at least two lines:
+# - [usb] vendor-ID and product-ID
+# - device devicename
+# i.e. for Plustek (0x07B3) UT12/16/24 (0x0017)
+# [usb] 0x07B3 0x0017
+# device /dev/usbscanner
+# or
+# device libusb:bbb:ddd
+# where bbb is the busnumber and ddd the device number
+# make sure that your user has access to /proc/bus/usb/bbb/ddd
+#
+# additionally you can specify some options
+# warmup, lOffOnEnd, lampOff
+#
+# For autodetection use
+# [usb]
+# device /dev/usbscanner
+#
+# or simply
+# [usb]
+#
+# or if you want a specific device but you have no idea about the
+# device node or you use libusb, simply set vendor- and product-ID
+# [usb] 0x07B3 0x0017
+# device auto
+#
+# NOTE: autodetection is safe, as it uses the info it got
+# from the USB subsystem. If you're not using the
+# autodetection, you MUST have attached that device
+# at your USB-port, that you have specified...
+#
+
+[usb]
+
+#
+# options for the previous USB entry
+#
+# switch lamp off after xxx secs, 0 disables the feature
+# (can also be set via frontend)
+option lampOff 300
+
+# warmup period in seconds, 0 means no warmup, -1 means auto-warmup
+# (can also be set via frontend)
+option warmup -1
+
+# 0 means leave lamp-status untouched, not 0 means switch off
+# on sane_close
+# (can also be set via frontend)
+option lOffOnEnd 1
+
+#
+# options to tweak the image start-position
+# (WARNING: there's no internal range check!!!)
+#
+# for the normal scan area
+#
+option posOffX 0
+option posOffY 0
+
+# for transparencies
+option tpaOffX 0
+option tpaOffY 0
+
+# for negatives
+option negOffX 0
+option negOffY 0
+
+#
+# for setting the calibration strip position
+# (WARNING: there's no internal range check!!!)
+# -1 means use built in
+# (can also be set via frontend)
+option posShadingY -1
+option tpaShadingY -1
+option negShadingY -1
+
+#
+# to invert the negatives, 0 disables the feature
+#
+option invertNegatives 0
+
+#
+# to disable the internal sensor speedup function,
+# 1 disables the feature
+#
+option disableSpeedup 0
+
+#
+# to save/restore coarse calibration data
+# (can also be set via frontend)
+option cacheCalData 0
+
+#
+# use alternate calibration routines
+#
+option altCalibration 0
+
+#
+# for skipping whole calibration step
+#
+option skipCalibration 0
+
+#
+# for skipping entire fine calibration step
+# coarse calibration is done
+#
+option skipFine 0
+
+#
+# discard the result of the fine white calibration
+#
+option skipFineWhite 0
+
+#
+# some scanners have a dark calibration strip, in
+# general this one should be used for calibration.
+# As this could cause some trouble, this option
+# overrides that and the dark calibration will be
+# done by switching the lamp off
+#
+option skipDarkStrip 0
+
+# for replacing the gain values found during coarse
+# calibration
+# (can also be set via frontend)
+option red_gain -1
+option green_gain -1
+option blue_gain -1
+
+# for replacing the offset values found during coarse
+# calibration
+# (can also be set via frontend)
+option red_offset -1
+option green_offset -1
+option blue_offset -1
+
+#
+# for replacing the default lampoff settings, this
+# works only for CIS devices like CanoScan LiDE20
+# (can also be set via frontend)
+option red_lampoff -1
+option green_lampoff -1
+option blue_lampoff -1
+
+#
+# for adjusting the default gamma values
+# (can also be set via frontend)
+option redGamma 1.0
+option greenGamma 1.0
+option blueGamma 1.0
+option grayGamma 1.0
+
+#
+# to enable TPA (EPSON or UMAX, if autodetection fails)
+# 0 means default behaviour as specified in the internal tables
+# 1 means enable (needed for UMAX 3450)
+option enableTPA 0
+
+#
+# model override functionality, currently only available for
+# Mustek devices, using NSCs' vendor ID: 0x0400 and
+# also their product ID: 0x1000 (LM9831) 0x1001 (LM9832)
+#
+# mov/PID | 0x1000 | 0x1001
+# ---------------------------------------
+# 0 (default)| BearPaw1200 | BearPaw 2400
+# 1 | ignored | BearPaw 1200
+#
+option mov 0
+
+#
+# and of course the device-name
+#
+# device /dev/usbscanner
+device auto
+
+#
+# to define a new device, start with a new section:
+# [usb]
+#
diff --git a/backend/plustek.h b/backend/plustek.h
new file mode 100644
index 0000000..be657c1
--- /dev/null
+++ b/backend/plustek.h
@@ -0,0 +1,431 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek.h
+ * @brief Definitions for the backend.
+ *
+ * Based on Kazuhiro Sasayama previous
+ * work on plustek.[ch] file from the SANE package.<br>
+ *
+ * original code taken from sane-0.71<br>
+ * Copyright (C) 1997 Hypercore Software Design, Ltd.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.30 - initial version
+ * - 0.31 - no changes
+ * - 0.32 - no changes
+ * - 0.33 - no changes
+ * - 0.34 - moved some definitions and typedefs from plustek.c
+ * - 0.35 - removed OPT_MODEL from options list
+ * - added max_y to struct Plustek_Scan
+ * - 0.36 - added reader_pid, pipe and bytes_read to struct Plustek_Scanner
+ * - removed unused variables from struct Plustek_Scanner
+ * - moved fd from struct Plustek_Scanner to Plustek_Device
+ * - added next members to struct Plustek_Scanner and Plustek_Device
+ * - 0.37 - added max_x to struct Plustek_Device
+ * - 0.38 - added caps to struct Plustek_Device
+ * - added exit code to struct Plustek_Scanner
+ * - removed dropout stuff
+ * - 0.39 - PORTTYPE enum
+ * - added function pointers to control a scanner device
+ * (Parport and USB)
+ * - 0.40 - added USB stuff
+ * - 0.41 - added configuration stuff
+ * - 0.42 - added custom gamma tables
+ * - changed usbId to static array
+ * - added _MAX_ID_LEN
+ * - 0.43 - no changes
+ * - 0.44 - added flag initialized
+ * - 0.45 - added readLine function
+ * - 0.46 - flag initialized is now used as device index
+ * - added calFile to Plustek_Device
+ * - removed _OPT_HALFTONE
+ * - 0.47 - added mov to adjustment
+ * - changed stopScan function definition
+ * - removed open function
+ * - added OPT_LAMPSWITCH and OPT_WARMUPTIME
+ * - 0.48 - added OPT_CACHECAL, OPT_OVR_*, OPT_LAMPOFF_TIMER and
+ * OPT_LAMPOFF_ONEND, also did some cleanup
+ * - moved SCANDEF definitions to plustek-usb.h
+ * - removed function pointer
+ * - added OPT_BIT_DEPTH
+ * - 0.49 - added typedef struct DevList
+ * - added button stuff
+ * - added transferRate to struct Plustek_Device
+ * - 0.50 - cleanup
+ * - added OPT_SPEEDUP
+ * - 0.51 - added OPT_CALIBRATE
+ * - 0.52 - added skipDarkStrip and incDarkTgt to struct AdjDef
+ * - added OPT_LOFF4DARK
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __PLUSTEK_H__
+#define __PLUSTEK_H__
+
+/************************ some definitions ***********************************/
+
+
+#define PLUSTEK_CONFIG_FILE "plustek.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define _MEASURE_BASE 300UL
+#define _DEF_DPI 50
+#define DEFAULT_RATE 1000000
+
+/** the default image size
+ */
+#define _DEFAULT_TLX 0 /* 0..216 mm */
+#define _DEFAULT_TLY 0 /* 0..297 mm */
+#define _DEFAULT_BRX 103 /* 0..216 mm */
+#define _DEFAULT_BRY 76.21 /* 0..297 mm */
+
+#define _DEFAULT_TP_TLX 3.5 /* 0..42.3 mm */
+#define _DEFAULT_TP_TLY 10.5 /* 0..43.1 mm */
+#define _DEFAULT_TP_BRX 38.5 /* 0..42.3 mm */
+#define _DEFAULT_TP_BRY 33.5 /* 0..43.1 mm */
+
+#define _DEFAULT_NEG_TLX 1.5 /* 0..38.9 mm */
+#define _DEFAULT_NEG_TLY 1.5 /* 0..29.6 mm */
+#define _DEFAULT_NEG_BRX 37.5 /* 0..38.9 mm */
+#define _DEFAULT_NEG_BRY 25.5 /* 0..29.6 mm */
+
+/** image sizes for normal, transparent and negative modes
+ */
+#define _TPAPageWidth 500UL
+#define _TPAPageHeight 510UL
+#define _TPALargePageWidth 1270UL
+#define _TPALargePageHeight 1570UL
+#define _TPAMinDpi 150
+
+#define _NegPageWidth 460UL
+#define _NegPageHeight 350UL
+#define _NegLargePageWidth 1270UL
+#define _NegLargePageHeight 1570UL
+
+#define _SCALE(X) ((double)(X)/300.0 * MM_PER_INCH)
+
+/** scan modes
+ */
+#define COLOR_BW 0
+#define COLOR_256GRAY 1
+#define COLOR_GRAY16 2
+#define COLOR_TRUE24 3
+#define COLOR_TRUE48 4
+
+/** usb id buffer
+ */
+#define _MAX_ID_LEN 20
+
+/**
+ */
+#define SFLAG_ADF 0x00000010 /* Automatic document feeder */
+#define SFLAG_TPA 0x00000080 /* has transparency adapter */
+
+/**
+ */
+#define SOURCE_Reflection 0
+#define SOURCE_Transparency 1
+#define SOURCE_Negative 2
+#define SOURCE_ADF 3
+
+/** for Gamma tables
+ */
+#define _MAP_RED 0
+#define _MAP_GREEN 1
+#define _MAP_BLUE 2
+#define _MAP_MASTER 3
+
+/** generic error codes...
+ */
+#define _FIRST_ERR -9000
+
+#define _E_ALLOC (_FIRST_ERR-1) /**< error allocating memory */
+#define _E_INVALID (_FIRST_ERR-2) /**< invalid parameter detected */
+#define _E_INTERNAL (_FIRST_ERR-3) /**< internal error */
+#define _E_ABORT (_FIRST_ERR-4) /**< operation aborted */
+
+#define _E_LAMP_NOT_IN_POS (_FIRST_ERR-10)
+#define _E_LAMP_NOT_STABLE (_FIRST_ERR-11)
+#define _E_NODATA (_FIRST_ERR-12)
+#define _E_BUFFER_TOO_SMALL (_FIRST_ERR-13)
+#define _E_DATAREAD (_FIRST_ERR-14)
+
+/************************ some structures ************************************/
+
+#define _ENABLE(option) s->opt[option].cap &= ~SANE_CAP_INACTIVE
+#define _DISABLE(option) s->opt[option].cap |= SANE_CAP_INACTIVE
+
+enum {
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_BIT_DEPTH,
+ OPT_EXT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_CUSTOM_GAMMA,
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_DEVICE_GROUP,
+ OPT_LAMPSWITCH,
+ OPT_LAMPOFF_TIMER,
+ OPT_LAMPOFF_ONEND,
+ OPT_WARMUPTIME,
+ OPT_LOFF4DARK,
+ OPT_CACHECAL,
+ OPT_SPEEDUP,
+ OPT_CALIBRATE,
+ OPT_AFE_GROUP,
+ OPT_OVR_REDGAIN,
+ OPT_OVR_GREENGAIN,
+ OPT_OVR_BLUEGAIN,
+ OPT_OVR_REDOFS,
+ OPT_OVR_GREENOFS,
+ OPT_OVR_BLUEOFS,
+ OPT_OVR_RED_LOFF,
+ OPT_OVR_GREEN_LOFF,
+ OPT_OVR_BLUE_LOFF,
+ OPT_BUTTON_GROUP,
+ OPT_BUTTON_0,
+ OPT_BUTTON_1,
+ OPT_BUTTON_2,
+ OPT_BUTTON_3,
+ OPT_BUTTON_4,
+ OPT_BUTTON_LAST = OPT_BUTTON_4,
+ NUM_OPTIONS
+};
+
+/**
+ */
+typedef struct {
+ int x;
+ int y;
+} OffsDef, *pOffsDef;
+
+/** for adjusting the scanner settings
+ */
+typedef struct {
+ int mov; /**< model override */
+ int lampOff;
+ int lampOffOnEnd;
+ int warmup;
+ int enableTpa;
+ int skipCalibration; /**< skip entire calibration */
+ int skipFine;
+ int skipFineWhite;
+ int skipDarkStrip;
+ int incDarkTgt;
+ int disableSpeedup;
+ int invertNegatives;
+ int cacheCalData;
+ int altCalibrate; /* force use of the alternate canoscan autocal;
+ perhaps other Canon scanners require the
+ alternate autocalibration as well */
+ /* AFE adjustemnts, gain and offset */
+ int rgain;
+ int ggain;
+ int bgain;
+ int rofs;
+ int gofs;
+ int bofs;
+
+ int rlampoff; /* for red lamp off setting (CIS-scanner) */
+ int glampoff; /* for green lamp off setting (CIS-scanner) */
+ int blampoff; /* for blue lamp off setting (CIS-scanner) */
+
+ OffsDef pos; /* for adjusting normal scan area */
+ OffsDef tpa; /* for adjusting transparency scan area */
+ OffsDef neg; /* for adjusting negative scan area */
+
+ int posShadingY;
+ int tpaShadingY;
+ int negShadingY;
+
+ /* for adjusting the default gamma settings */
+ double rgamma;
+ double ggamma;
+ double bgamma;
+
+ double graygamma;
+
+} AdjDef;
+
+typedef struct {
+ unsigned short x;
+ unsigned short y;
+ unsigned short cx;
+ unsigned short cy;
+} CropRect;
+
+typedef struct image {
+ unsigned long dwFlag;
+ CropRect crArea;
+ XY xyDpi;
+ unsigned short wDataType;
+} ImgDef;
+
+typedef struct {
+ unsigned long dwPixelsPerLine;
+ unsigned long dwBytesPerLine;
+ unsigned long dwLinesPerArea;
+ ImgDef ImgDef;
+} CropInfo;
+
+typedef struct {
+ ImgDef ImgDef;
+ short siBrightness;
+ short siContrast;
+} ScanInfo;
+
+typedef struct {
+ unsigned long dwFlag;
+ unsigned short wMaxExtentX; /**< scanarea width */
+ unsigned short wMaxExtentY; /**< scanarea height */
+} ScannerCaps;
+
+typedef struct Plustek_Device
+{
+ SANE_Int initialized; /* device already initialized? */
+ struct Plustek_Device *next; /* pointer to next dev in list */
+ int fd; /* device handle */
+ char *name; /* (to avoid compiler warnings!)*/
+ char *calFile; /* for saving calibration data */
+ unsigned long transferRate; /* detected USB-Speed in Bytes/s*/
+ SANE_Device sane; /* info struct */
+ SANE_Int max_x; /* max XY-extension of the scan-*/
+ SANE_Int max_y; /* area */
+ SANE_Range dpi_range; /* resolution range */
+ SANE_Range x_range; /* x-range of the scan-area */
+ SANE_Range y_range; /* y-range of the scan-area */
+ SANE_Int *res_list; /* to hold the available phys. */
+ SANE_Int res_list_size; /* resolution values */
+ ScannerCaps caps; /* caps reported by the driver */
+ AdjDef adj; /* for driver adjustment */
+
+ /**************************** USB-stuff **********************************/
+ char usbId[_MAX_ID_LEN];/* to keep Vendor and product */
+ /* ID string (from conf) file */
+ struct ScanDef scanning; /* here we hold all stuff for */
+ /* the USB-scanner */
+ struct DeviceDef usbDev;
+#ifdef HAVE_SETITIMER
+ struct itimerval saveSettings; /* for lamp timer */
+#endif
+
+} Plustek_Device;
+
+#ifndef SANE_OPTION
+/* for compatibility with older versions */
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa;
+ SANE_String s;
+} Option_Value;
+#endif
+
+typedef struct Plustek_Scanner
+{
+ struct Plustek_Scanner *next;
+ SANE_Pid reader_pid; /* process id of reader */
+ SANE_Status exit_code; /* status of the reader process */
+ int r_pipe; /* pipe to reader process */
+ int w_pipe; /* pipe from reader process */
+ unsigned long bytes_read; /* number of bytes currently read*/
+ Plustek_Device *hw; /* pointer to current device */
+ Option_Value val[NUM_OPTIONS];
+ SANE_Byte *buf; /* the image buffer */
+ SANE_Bool scanning; /* TRUE during scan-process */
+ SANE_Bool calibrating; /* TRUE during calibration */
+ SANE_Bool ipc_read_done; /* TRUE after ipc has been red */
+ SANE_Parameters params; /* for keeping the parameter */
+
+ /************************** gamma tables *********************************/
+
+ SANE_Word gamma_table[4][4096];
+ SANE_Range gamma_range;
+ int gamma_length;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+} Plustek_Scanner;
+
+/** for collecting configuration info...
+ */
+typedef struct {
+
+ char devName[PATH_MAX];
+ char usbId[_MAX_ID_LEN];
+
+ /* contains the stuff to adjust... */
+ AdjDef adj;
+
+} CnfDef;
+
+/** for supported device list
+ */
+typedef struct DevList {
+ SANE_Word vendor_id;
+ SANE_Word device_id;
+ SANE_Bool attached;
+ SANE_Char *dev_name;
+ struct DevList *next;
+} DevList;
+#endif /* guard __PLUSTEK_H__ */
+
+/* END PLUSTEK.H.............................................................*/
diff --git a/backend/plustek_pp.c b/backend/plustek_pp.c
new file mode 100644
index 0000000..bf4c0d0
--- /dev/null
+++ b/backend/plustek_pp.c
@@ -0,0 +1,2180 @@
+/** @file plustek_pp.c
+ * @brief SANE backend for Plustek parallelport scanner
+ *
+ * Based on Kazuhiro Sasayama previous work on
+ * plustek.[ch] file from the SANE package.<br>
+ * Original code taken from sane-0.71<br>
+ * Copyright (C) 1997 Hypercore Software Design, Ltd.<br>
+ * Also based on the work done by Rick Bronson<br>
+ * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de><br>
+ *
+ * History:
+ * - 0.01 - initial version, imported from the kernel-module 0.42-11
+ * - 0.43 - bumped up version to reflect the former module code version
+ * - 0.44 - bumped up version to reflect the recent changes
+ * - minor cleanup
+ *.
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/** @mainpage
+ * @verbinclude Plustek-PARPORT.txt
+ */
+
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+#include <stdint.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_VERSION "0.44-1"
+#define BACKEND_NAME plustek_pp
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+#include "../include/sane/sanei_pp.h"
+
+#define _BACKEND_ENABLED
+#define _USER_MODE
+
+#include "plustek-pp.h"
+
+/*********************** the debug levels ************************************/
+
+#define _DBG_FATAL 0
+#define _DBG_ERROR 1
+#define _DBG_WARNING 3
+#define _DBG_INFO 5
+#define _DBG_PROC 7
+#define _DBG_SANE_INIT 10
+#define _DBG_INFO2 13
+#define _DBG_DUMP 20
+#define _DBG_READ 25
+
+/*****************************************************************************/
+
+/*
+ * see plustek-share.h
+ */
+MODELSTR;
+
+#ifdef _BACKEND_ENABLED
+#ifndef NDEBUG
+# define DEBUG
+#endif
+
+/* I know this is in general no good idea, but it works */
+# include "plustek-pp_io.c"
+# include "plustek-pp_dac.c"
+# include "plustek-pp_detect.c"
+# include "plustek-pp_genericio.c"
+# include "plustek-pp_image.c"
+# include "plustek-pp_map.c"
+# include "plustek-pp_misc.c"
+# include "plustek-pp_models.c"
+# include "plustek-pp_motor.c"
+# include "plustek-pp_p12.c"
+# include "plustek-pp_p12ccd.c"
+# include "plustek-pp_p48xx.c"
+# include "plustek-pp_p9636.c"
+# include "plustek-pp_procfs.c"
+# include "plustek-pp_scale.c"
+# include "plustek-pp_tpa.c"
+
+#define _DEFAULT_DEVICE "0x378"
+#else
+#define _DEFAULT_DEVICE "/dev/pt_drv"
+#endif
+
+#ifdef _BACKEND_ENABLED
+# include "plustek-pp_ptdrv.c"
+#endif
+
+#include "plustek-pp_wrapper.c"
+
+
+/************************** global vars **************************************/
+
+static int num_devices;
+static Plustek_Device *first_dev;
+static Plustek_Scanner *first_handle;
+static const SANE_Device **devlist = 0;
+static unsigned long tsecs = 0;
+
+static ModeParam mode_params[] =
+{
+ {0, 1, COLOR_BW},
+ {0, 1, COLOR_HALFTONE},
+ {0, 8, COLOR_256GRAY},
+ {1, 8, COLOR_TRUE24},
+ {1, 16, COLOR_TRUE32},
+ {1, 16, COLOR_TRUE36}
+};
+
+static ModeParam mode_9800x_params[] =
+{
+ {0, 1, COLOR_BW},
+ {0, 1, COLOR_HALFTONE},
+ {0, 8, COLOR_256GRAY},
+ {1, 8, COLOR_TRUE24},
+ {1, 16, COLOR_TRUE48}
+};
+
+static const SANE_String_Const mode_list[] =
+{
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static const SANE_String_Const mode_9800x_list[] =
+{
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_I18N("Color36"),
+ NULL
+};
+
+static const SANE_String_Const ext_mode_list[] =
+{
+ SANE_I18N("Normal"),
+ SANE_I18N("Transparency"),
+ SANE_I18N("Negative"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list[] =
+{
+ SANE_I18N("Dithermap 1"),
+ SANE_I18N("Dithermap 2"),
+ SANE_I18N("Randomize"),
+ NULL
+};
+
+static const SANE_Range percentage_range =
+{
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+/*
+ * lens info
+ */
+static LensInfo lens = {{0,0,0,0,},{0,0,0,0,},{0,0,0,0,},{0,0,0,0,},0,0};
+
+/* authorization stuff */
+static SANE_Auth_Callback auth = NULL;
+
+/****************************** the backend... *******************************/
+
+#define _YN(x) (x?"yes":"no")
+
+/**
+ * function to display the configuration options for the current device
+ * @param cnf - pointer to the configuration structure whose content should be
+ * displayed
+ */
+static void show_cnf( pCnfDef cnf )
+{
+ DBG( _DBG_SANE_INIT,"Device configuration:\n" );
+ DBG( _DBG_SANE_INIT,"device name : >%s<\n", cnf->devName );
+ DBG( _DBG_SANE_INIT,"direct I/O : %s\n", _YN(cnf->adj.direct_io ));
+ DBG( _DBG_SANE_INIT,"warmup : %ds\n", cnf->adj.warmup );
+ DBG( _DBG_SANE_INIT,"lampOff : %d\n", cnf->adj.lampOff );
+ DBG( _DBG_SANE_INIT,"lampOffOnEnd : %s\n", _YN(cnf->adj.lampOffOnEnd ));
+ DBG( _DBG_SANE_INIT,"model override: %d\n", cnf->adj.mov );
+ DBG( _DBG_SANE_INIT,"---------------------\n" );
+}
+
+/** open the device specific driver and reset the internal timing stuff
+ * @param dev - pointer to the device specific structure
+ * @return the function returns the result of the open call, on success
+ * of course the handle
+ */
+static int drvopen( Plustek_Device *dev )
+{
+ int handle;
+
+ DBG( _DBG_INFO, "drvopen()\n" );
+
+ handle = dev->open((const char*)dev->name, (void *)dev );
+
+ tsecs = 0;
+
+ return handle;
+}
+
+/** Calls the device specific stop and close functions.
+ * @param dev - pointer to the device specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static SANE_Status drvclose( Plustek_Device *dev )
+{
+ short int_cnt;
+
+ if( dev->fd >= 0 ) {
+
+ DBG( _DBG_INFO, "drvclose()\n" );
+
+ if( 0 != tsecs ) {
+ DBG( _DBG_INFO, "TIME END 1: %lus\n", time(NULL)-tsecs);
+ }
+
+ /*
+ * don't check the return values, simply do it and close the driver
+ */
+ int_cnt = 0;
+ dev->stopScan( dev, &int_cnt );
+ dev->close( dev );
+ }
+ dev->fd = -1;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** according to the mode and source we return the corresponding mode list
+ */
+static pModeParam getModeList( Plustek_Scanner *scanner )
+{
+ pModeParam mp;
+
+ if((_ASIC_IS_98003 == scanner->hw->caps.AsicID) ||
+ (_ASIC_IS_98001 == scanner->hw->caps.AsicID)) {
+ mp = mode_9800x_params;
+ } else {
+ mp = mode_params;
+ }
+
+ /*
+ * the transparency/negative mode supports only GRAY/COLOR/COLOR32/COLOR48
+ */
+ if( 0 != scanner->val[OPT_EXT_MODE].w ) {
+ mp = &mp[_TPAModeSupportMin];
+ }
+
+ return mp;
+}
+
+/**
+ */
+static SANE_Status close_pipe( Plustek_Scanner *scanner )
+{
+ if( scanner->r_pipe >= 0 ) {
+
+ DBG( _DBG_PROC, "close r_pipe\n" );
+ close( scanner->r_pipe );
+ scanner->r_pipe = -1;
+ }
+ if( scanner->w_pipe >= 0 ) {
+
+ DBG( _DBG_PROC, "close w_pipe\n" );
+ close( scanner->w_pipe );
+ scanner->w_pipe = -1;
+ }
+
+ return SANE_STATUS_EOF;
+}
+
+/**
+ */
+static void sig_chldhandler( int signo )
+{
+ DBG( _DBG_PROC, "Child is down (signal=%d)\n", signo );
+}
+
+/** signal handler to kill the child process
+ */
+static RETSIGTYPE reader_process_sigterm_handler( int signo )
+{
+ DBG( _DBG_PROC, "reader_process: terminated by signal %d\n", signo );
+ _exit( SANE_STATUS_GOOD );
+}
+
+static RETSIGTYPE sigalarm_handler( int signo )
+{
+ _VAR_NOT_USED( signo );
+ DBG( _DBG_PROC, "ALARM!!!\n" );
+}
+
+/** executed as a child process
+ * read the data from the driver and send them to the parent process
+ */
+static int reader_process( void *args )
+{
+ int line;
+ unsigned long status;
+ unsigned long data_length;
+ struct SIGACTION act;
+ sigset_t ignore_set;
+ Plustek_Scanner *scanner = (Plustek_Scanner *)args;
+
+ if( sanei_thread_is_forked()) {
+ DBG( _DBG_PROC, "reader_process started (forked)\n" );
+ close( scanner->r_pipe );
+ scanner->r_pipe = -1;
+ } else {
+ DBG( _DBG_PROC, "reader_process started (as thread)\n" );
+ }
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGTERM );
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset ( &ignore_set, SIGUSR2 );
+#endif
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset ( &act, 0, sizeof (act));
+ sigaction( SIGTERM, &act, 0 );
+
+ /* install the signal handler */
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = reader_process_sigterm_handler;
+ sigaction( SIGTERM, &act, 0 );
+
+ data_length = scanner->params.lines * scanner->params.bytes_per_line;
+
+ DBG( _DBG_PROC, "reader_process:"
+ "starting to READ data (%lu bytes)\n", data_length );
+ DBG( _DBG_PROC, "buf = 0x%08lx\n", (unsigned long)scanner->buf );
+
+ if( NULL == scanner->buf ) {
+ DBG( _DBG_FATAL, "NULL Pointer !!!!\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* here we read all data from the driver... */
+ if( scanner->hw->readImage ) {
+
+ status = (unsigned long)scanner->hw->readImage( scanner->hw,
+ scanner->buf, data_length);
+ } else {
+
+ unsigned char *buf = scanner->buf;
+
+
+ status = scanner->hw->prepare( scanner->hw, buf );
+
+ if( 0 == status ) {
+
+ for( line = 0; line < scanner->params.lines; line++ ) {
+
+ status = scanner->hw->readLine( scanner->hw );
+ if((int)status < 0 ) {
+ break;
+ }
+
+ write( scanner->w_pipe, buf, scanner->params.bytes_per_line );
+
+ buf += scanner->params.bytes_per_line;
+ }
+ }
+ }
+
+ /* on error, there's no need to clean up, as this is done by the parent */
+ if((int)status < 0 ) {
+ DBG( _DBG_ERROR, "read failed, status = %i, errno %i\n",
+ (int)status, errno );
+ if( -9009 == (int)status )
+ return SANE_STATUS_CANCELLED;
+
+ if( errno == EBUSY )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* send to parent */
+ if( scanner->hw->readImage ) {
+ DBG( _DBG_PROC, "sending %lu bytes to parent\n", status );
+ write( scanner->w_pipe, scanner->buf, status );
+ }
+
+ DBG( _DBG_PROC, "reader_process: finished reading data\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** stop the current scan process
+ */
+static SANE_Status do_cancel( Plustek_Scanner *scanner, SANE_Bool closepipe )
+{
+ struct SIGACTION act;
+ SANE_Pid res;
+ short int_cnt;
+
+ DBG( _DBG_PROC,"do_cancel\n" );
+
+ scanner->scanning = SANE_FALSE;
+
+ if( scanner->reader_pid != -1 ) {
+
+ DBG( _DBG_PROC, ">>>>>>>> killing reader_process <<<<<<<<\n" );
+
+ /* tell the driver to stop scanning */
+ if( -1 != scanner->hw->fd ) {
+ int_cnt = 1;
+ scanner->hw->stopScan( scanner->hw, &int_cnt );
+ }
+
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+ sigaction( SIGALRM, &act, 0 );
+
+ /* kill our child process and wait until done */
+ sanei_thread_kill( scanner->reader_pid );
+
+ /* give'em 10 seconds 'til done...*/
+ alarm(10);
+ res = sanei_thread_waitpid( scanner->reader_pid, 0 );
+ alarm(0);
+
+ if( res != scanner->reader_pid ) {
+ DBG( _DBG_PROC,"sanei_thread_waitpid() failed !\n");
+
+ /* do it the hard way...*/
+#ifdef USE_PTHREAD
+ sanei_thread_kill( scanner->reader_pid );
+#else
+ sanei_thread_sendsig( scanner->reader_pid, SIGKILL );
+#endif
+ }
+
+ scanner->reader_pid = -1;
+ DBG( _DBG_PROC,"reader_process killed\n");
+ }
+
+ if( SANE_TRUE == closepipe ) {
+ close_pipe( scanner );
+ }
+
+ drvclose( scanner->hw );
+
+ if( tsecs != 0 ) {
+ DBG( _DBG_INFO, "TIME END 2: %lus\n", time(NULL)-tsecs);
+ tsecs = 0;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/** because of some internal problems (inside the parport driver), we have to
+ * limit the max resolution to optical resolution. This is done by this
+ * function
+ * @param dev - pointer to the device specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static void limitResolution( Plustek_Device *dev )
+{
+ dev->dpi_range.min = _DEF_DPI;
+ if( dev->dpi_range.min < _DEF_DPI )
+ dev->dpi_range.min = _DEF_DPI;
+
+ /*
+ * CHANGE: limit resolution to max. physical available one
+ * Note: the resolution for the Asic 96001/3 models is limited to
+ * the X-Resolution
+ */
+ if((_ASIC_IS_96003 == dev->caps.AsicID) ||
+ (_ASIC_IS_96001 == dev->caps.AsicID)) {
+ dev->dpi_range.max = lens.rDpiX.wPhyMax;
+ } else {
+ dev->dpi_range.max = lens.rDpiY.wPhyMax;
+ }
+
+ dev->dpi_range.quant = 0;
+ dev->x_range.min = 0;
+ dev->x_range.max = SANE_FIX(dev->max_x);
+ dev->x_range.quant = 0;
+ dev->y_range.min = 0;
+ dev->y_range.max = SANE_FIX(dev->max_y);
+ dev->y_range.quant = 0;
+}
+
+/** Currently we support only LM9831/2/3 chips and these use the same
+ * sizes...
+ * @param s - pointer to the scanner specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static SANE_Status initGammaSettings( Plustek_Scanner *s )
+{
+ int i, j, val;
+ double gamma;
+
+ /*
+ * this setting is common to the ASIC98001/3 and
+ * LM9831/2/3 based devices
+ * older parallelport devices use 256 entries
+ */
+ s->gamma_length = 4096;
+ s->gamma_range.min = 0;
+ s->gamma_range.max = 255;
+ s->gamma_range.quant = 0;
+
+ if((_ASIC_IS_96003 == s->hw->caps.AsicID) ||
+ (_ASIC_IS_96001 == s->hw->caps.AsicID)) {
+
+ s->gamma_length = 256;
+ }
+
+ DBG( _DBG_INFO, "Presetting Gamma tables (len=%u)\n", s->gamma_length );
+ DBG( _DBG_INFO, "----------------------------------\n" );
+
+ /*
+ * preset the gamma maps
+ */
+ for( i = 0; i < 4; i++ ) {
+
+ switch( i ) {
+ case 1: gamma = s->hw->adj.rgamma; break;
+ case 2: gamma = s->hw->adj.ggamma; break;
+ case 3: gamma = s->hw->adj.bgamma; break;
+ default: gamma = s->hw->adj.graygamma; break;
+ }
+
+ for( j = 0; j < s->gamma_length; j++ ) {
+
+ val = (s->gamma_range.max *
+ pow((double) j / ((double)s->gamma_length - 1.0),
+ 1.0 / gamma ));
+
+ if( val > s->gamma_range.max )
+ val = s->gamma_range.max;
+
+ s->gamma_table[i][j] = val;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** Check the gamma vectors we got back and limit if necessary
+ * @param s - pointer to the scanner specific structure
+ * @return nothing
+ */
+static void checkGammaSettings( Plustek_Scanner *s )
+{
+ int i, j;
+
+ for( i = 0; i < 4 ; i++ ) {
+ for( j = 0; j < s->gamma_length; j++ ) {
+ if( s->gamma_table[i][j] > s->gamma_range.max ) {
+ s->gamma_table[i][j] = s->gamma_range.max;
+ }
+ }
+ }
+}
+
+/** initialize the options for the backend according to the device we have
+ */
+static SANE_Status init_options( Plustek_Scanner *s )
+{
+ int i;
+
+ memset( s->opt, 0, sizeof(s->opt));
+
+ for( i = 0; i < NUM_OPTIONS; ++i ) {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+ s->opt[OPT_MODE_GROUP].name = "scanmode-group";
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = 32;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+
+ if((_ASIC_IS_98001 == s->hw->caps.AsicID) ||
+ (_ASIC_IS_98003 == s->hw->caps.AsicID)) {
+ s->opt[OPT_MODE].constraint.string_list = mode_9800x_list;
+ } else {
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ }
+ s->val[OPT_MODE].w = 3; /* Color */
+
+ /* scan source */
+ s->opt[OPT_EXT_MODE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_EXT_MODE].size = 32;
+ s->opt[OPT_EXT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_EXT_MODE].constraint.string_list = ext_mode_list;
+ s->val[OPT_EXT_MODE].w = 0; /* Normal */
+
+ /* halftone */
+ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ s->opt[OPT_HALFTONE].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE].size = 32;
+ s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
+ s->val[OPT_HALFTONE].w = 0; /* Standard dithermap */
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = "geometry-group";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ initGammaSettings( s );
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR].wa = &(s->gamma_table[0][0]);
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &(s->gamma_table[1][0]);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_R].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &(s->gamma_table[2][0]);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_G].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &(s->gamma_table[3][0]);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof(SANE_Word);
+
+ /* GAMMA stuff is disabled per default */
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ /* disable extended mode list for devices without TPA */
+ if( 0 == (s->hw->caps.dwFlag & SFLAG_TPA)) {
+ s->opt[OPT_EXT_MODE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* disable custom gamma, if not supported by the driver... */
+ if( 0 == (s->hw->caps.dwFlag & SFLAG_CUSTOM_GAMMA)) {
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#define _INT 0
+#define _FLOAT 1
+
+/** function to decode an value and give it back to the caller.
+ * @param src - pointer to the source string to check
+ * @param opt - string that keeps the option name to check src for
+ * @param what - _FLOAT or _INT
+ * @param result - pointer to the var that should receive our result
+ * @param def - default value that result should be in case of any error
+ * @return The function returns SANE_TRUE if the option has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool decodeVal( char *src, char *opt,
+ int what, void *result, void *def )
+{
+ char *tmp, *tmp2;
+ const char *name;
+
+ /* skip the option string */
+ name = (const char*)&src[strlen("option")];
+
+ /* get the name of the option */
+ name = sanei_config_get_string( name, &tmp );
+
+ if( tmp ) {
+
+ /* on success, compare wiht the given one */
+ if( 0 == strcmp( tmp, opt )) {
+
+ DBG( _DBG_SANE_INIT, "Decoding option >%s<\n", opt );
+
+ if( _INT == what ) {
+
+ /* assign the default value for this option... */
+ *((int*)result) = *((int*)def);
+
+ if( *name ) {
+
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string( name, &tmp2 );
+
+ if( tmp2 ) {
+ *((int*)result) = strtol( tmp2, 0, 0 );
+ free( tmp2 );
+ }
+ }
+ free( tmp );
+ return SANE_TRUE;
+
+ } else if( _FLOAT == what ) {
+
+ /* assign the default value for this option... */
+ *((double*)result) = *((double*)def);
+
+ if( *name ) {
+
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string( name, &tmp2 );
+
+ if( tmp2 ) {
+ *((double*)result) = strtod( tmp2, 0 );
+ free( tmp2 );
+ }
+ }
+ free( tmp );
+ return SANE_TRUE;
+ }
+ }
+ free( tmp );
+ }
+
+ return SANE_FALSE;
+}
+
+/** function to retrive the device name of a given string
+ * @param src - string that keeps the option name to check src for
+ * @param dest - pointer to the string, that should receive the detected
+ * devicename
+ * @return The function returns SANE_TRUE if the devicename has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool decodeDevName( char *src, char *dest )
+{
+ char *tmp;
+ const char *name;
+
+ if( 0 == strncmp( "device", src, 6 )) {
+
+ name = (const char*)&src[strlen("device")];
+ name = sanei_config_skip_whitespace( name );
+
+ DBG( _DBG_SANE_INIT, "Decoding device name >%s<\n", name );
+
+ if( *name ) {
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+
+ strcpy( dest, tmp );
+ free( tmp );
+ return SANE_TRUE;
+ }
+ }
+ }
+
+ return SANE_FALSE;
+}
+
+/** attach a device to the backend
+ */
+static SANE_Status attach( const char *dev_name, pCnfDef cnf,
+ Plustek_Device **devp )
+{
+ int cntr;
+ int result;
+ int handle;
+ Plustek_Device *dev;
+
+ DBG( _DBG_SANE_INIT, "attach (%s, %p, %p)\n",
+ dev_name, (void *)cnf, (void *)devp);
+
+ /* already attached ?*/
+ for( dev = first_dev; dev; dev = dev->next ) {
+
+ if( 0 == strcmp( dev->sane.name, dev_name )) {
+ if( devp )
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* allocate some memory for the device */
+ dev = malloc( sizeof (*dev));
+ if( NULL == dev )
+ return SANE_STATUS_NO_MEM;
+
+ /* assign all the stuff we need fo this device... */
+
+ memset(dev, 0, sizeof (*dev));
+
+ dev->fd = -1;
+ dev->name = strdup(dev_name); /* hold it double to avoid */
+ dev->sane.name = dev->name; /* compiler warnings */
+ dev->sane.vendor = "Plustek";
+ dev->initialized = -1; /* will be used as index too */
+
+ memcpy( &dev->adj, &cnf->adj, sizeof(AdjDef));
+
+ show_cnf( cnf );
+
+ dev->sane.type = SANE_I18N ("flatbed scanner");
+ dev->open = ppDev_open;
+ dev->close = ppDev_close;
+ dev->getCaps = ppDev_getCaps;
+ dev->getLensInfo = ppDev_getLensInfo;
+ dev->getCropInfo = ppDev_getCropInfo;
+ dev->putImgInfo = ppDev_putImgInfo;
+ dev->setScanEnv = ppDev_setScanEnv;
+ dev->startScan = ppDev_startScan;
+ dev->stopScan = ppDev_stopScan;
+ dev->setMap = ppDev_setMap;
+ dev->readImage = ppDev_readImage;
+ dev->shutdown = NULL;
+ dev->readLine = NULL;
+ dev->prepare = NULL;
+
+ /*
+ * go ahead and open the scanner device
+ */
+ handle = drvopen( dev );
+ if( handle < 0 ) {
+ DBG( _DBG_ERROR,"open failed: %d\n", handle );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* okay, so assign the handle... */
+ dev->fd = handle;
+
+ result = dev->getCaps( dev );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->getCaps() failed(%d)\n", result);
+ dev->close(dev);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ result = dev->getLensInfo( dev, &lens );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->getLensInfo() failed(%d)\n", result );
+ dev->close(dev);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* did we fail on connection? */
+ if( _NO_BASE == dev->caps.wIOBase ) {
+ DBG( _DBG_ERROR, "failed to find Plustek scanner\n" );
+ dev->close(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* save the info we got from the driver */
+ DBG( _DBG_INFO, "Scanner information:\n" );
+ if( dev->caps.Model < MODEL_UNKNOWN ) {
+ dev->sane.model = ModelStr[dev->caps.Model];
+ } else {
+ dev->sane.model = ModelStr[0];
+ }
+
+ DBG( _DBG_INFO, "Vendor : %s\n", dev->sane.vendor );
+ DBG( _DBG_INFO, "Model : %s\n", dev->sane.model );
+ DBG( _DBG_INFO, "Asic : 0x%02x\n", dev->caps.AsicID );
+ DBG( _DBG_INFO, "Flags : 0x%08lx\n", dev->caps.dwFlag );
+
+ dev->max_x = dev->caps.wMaxExtentX*MM_PER_INCH/_MEASURE_BASE;
+ dev->max_y = dev->caps.wMaxExtentY*MM_PER_INCH/_MEASURE_BASE;
+
+ dev->res_list = (SANE_Int *)calloc(((lens.rDpiX.wMax -_DEF_DPI)/25 + 1),
+ sizeof (SANE_Int)); /* one more to avoid a buffer overflow */
+
+ if (NULL == dev->res_list) {
+ DBG( _DBG_ERROR, "alloc fail, resolution problem\n" );
+ dev->close(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* build up the resolution table */
+ dev->res_list_size = 0;
+ for( cntr = _DEF_DPI; cntr <= lens.rDpiX.wMax; cntr += 25 ) {
+ dev->res_list_size++;
+ dev->res_list[dev->res_list_size - 1] = (SANE_Int)cntr;
+ }
+
+ limitResolution( dev );
+
+ dev->fd = handle;
+ drvclose( dev );
+
+ DBG( _DBG_SANE_INIT, "attach: model = >%s<\n", dev->sane.model );
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** function to preset a configuration structure
+ * @param cnf - pointer to the structure that should be initialized
+ */
+static void init_config_struct( pCnfDef cnf, SANE_Bool direct_io )
+{
+ memset( cnf, 0, sizeof(CnfDef));
+
+ cnf->adj.direct_io = direct_io;
+
+ cnf->adj.warmup = -1;
+ cnf->adj.lampOff = -1;
+ cnf->adj.lampOffOnEnd = -1;
+
+ cnf->adj.graygamma = 1.0;
+ cnf->adj.rgamma = 1.0;
+ cnf->adj.ggamma = 1.0;
+ cnf->adj.bgamma = 1.0;
+}
+
+/** intialize the backend
+ */
+SANE_Status sane_init( SANE_Int *version_code, SANE_Auth_Callback authorize )
+{
+ char str[PATH_MAX] = _DEFAULT_DEVICE;
+ CnfDef config;
+ size_t len;
+ FILE *fp;
+ SANE_Status res;
+
+ DBG_INIT();
+ sanei_thread_init();
+
+ res = sanei_pp_init();
+ if( SANE_STATUS_GOOD != res ) {
+ DBG( _DBG_ERROR, "Could not initialize Parport library!\n" );
+ return res;
+ }
+
+#if defined PACKAGE && defined VERSION
+ DBG( _DBG_SANE_INIT, "PlustekPP backend V"BACKEND_VERSION", part of "
+ PACKAGE " " VERSION "\n");
+#else
+ DBG( _DBG_INFO, "PlustekPP backend V"BACKEND_VERSION"\n" );
+#endif
+
+ /* do some presettings... */
+ auth = authorize;
+ first_dev = NULL;
+ first_handle = NULL;
+ num_devices = 0;
+
+ /* initialize the configuration structure */
+#ifdef _BACKEND_ENABLED
+ init_config_struct( &config, SANE_TRUE );
+#else
+ init_config_struct( &config, SANE_FALSE );
+#endif
+
+ if( version_code != NULL )
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open( PLUSTEK_CONFIG_FILE );
+
+ /* default to _DEFAULT_DEVICE instead of insisting on config file */
+ if( NULL == fp ) {
+ return attach( _DEFAULT_DEVICE, &config, 0 );
+ }
+
+ while( sanei_config_read( str, sizeof(str), fp)) {
+
+ DBG( _DBG_SANE_INIT, ">%s<\n", str );
+ if( str[0] == '#') /* ignore line comments */
+ continue;
+
+ len = strlen(str);
+ if( 0 == len )
+ continue; /* ignore empty lines */
+
+ /* check for options */
+ if( 0 == strncmp(str, "option", 6)) {
+
+ int ival;
+
+ ival = -1;
+ decodeVal( str, "warmup", _INT, &config.adj.warmup, &ival);
+ decodeVal( str, "lampOff", _INT, &config.adj.lampOff, &ival);
+ decodeVal( str, "lOffOnEnd", _INT, &config.adj.lampOffOnEnd,&ival);
+
+ ival = 0;
+ decodeVal( str, "mov", _INT, &config.adj.mov, &ival );
+ continue;
+
+ /* check for sections: */
+ } else if( 0 == strncmp( str, "[direct]", 8)) {
+
+ /* new section, try and attach previous device */
+ if( config.devName[0] != '\0' )
+ attach( config.devName, &config, 0 );
+
+ /* re-initialize the configuration structure */
+ init_config_struct( &config, SANE_TRUE );
+ continue;
+
+ } else if( 0 == strncmp( str, "[kernel]", 8 )) {
+
+ /* new section, try and attach previous device */
+ if( config.devName[0] != '\0' )
+ attach( config.devName, &config, 0 );
+
+ /* re-initialize the configuration structure */
+ init_config_struct( &config, SANE_FALSE );
+ continue;
+
+ } else if( SANE_TRUE == decodeDevName( str, config.devName )) {
+ continue;
+ }
+
+ /* ignore other stuff... */
+ DBG( _DBG_SANE_INIT, "ignoring >%s<\n", str );
+ }
+ fclose (fp);
+
+ /* try to attach the last device in the config file... */
+ if( config.devName[0] != '\0' )
+ attach( config.devName, &config, 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+/** cleanup the backend...
+ */
+void sane_exit( void )
+{
+ Plustek_Device *dev, *next;
+
+ DBG( _DBG_SANE_INIT, "sane_exit\n" );
+
+ for( dev = first_dev; dev; ) {
+
+ next = dev->next;
+
+ /* call the shutdown function of each device... */
+ if( dev->shutdown )
+ dev->shutdown( dev );
+
+ /*
+ * we're doin' this to avoid compiler warnings as dev->sane.name
+ * is defined as const char*
+ */
+ if( dev->sane.name )
+ free( dev->name );
+
+ if( dev->res_list )
+ free( dev->res_list );
+ free( dev );
+
+ dev = next;
+ }
+
+ if( devlist )
+ free( devlist );
+
+ /* call driver specific shutdown function... */
+ PtDrvShutdown();
+
+ devlist = NULL;
+ auth = NULL;
+ first_dev = NULL;
+ first_handle = NULL;
+}
+
+/** return a list of all devices
+ */
+SANE_Status sane_get_devices(const SANE_Device ***device_list,
+ SANE_Bool local_only )
+{
+ int i;
+ Plustek_Device *dev;
+
+ DBG(_DBG_SANE_INIT, "sane_get_devices (%p, %ld)\n",
+ (void *)device_list, (long) local_only);
+
+ /* already called, so cleanup */
+ if( devlist )
+ free( devlist );
+
+ devlist = malloc((num_devices + 1) * sizeof (devlist[0]));
+ if ( NULL == devlist )
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+/** open the sane device
+ */
+SANE_Status sane_open( SANE_String_Const devicename, SANE_Handle* handle )
+{
+ SANE_Status status;
+ Plustek_Device *dev;
+ Plustek_Scanner *s;
+ CnfDef config;
+
+ DBG( _DBG_SANE_INIT, "sane_open - %s\n", devicename );
+
+ if( devicename[0] ) {
+ for( dev = first_dev; dev; dev = dev->next ) {
+ if( strcmp( dev->sane.name, devicename ) == 0 )
+ break;
+ }
+
+ if( !dev ) {
+
+ memset( &config, 0, sizeof(CnfDef));
+
+ /* check if a valid parport-device is meant... */
+ status = attach( devicename, &config, &dev );
+ if( SANE_STATUS_GOOD != status )
+ return status;
+ }
+ } else {
+ /* empty devicename -> use first device */
+ dev = first_dev;
+ }
+
+ if( !dev )
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if( NULL == s )
+ return SANE_STATUS_NO_MEM;
+
+ memset(s, 0, sizeof (*s));
+ s->r_pipe = -1;
+ s->w_pipe = -1;
+ s->hw = dev;
+ s->scanning = SANE_FALSE;
+
+ init_options(s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+void sane_close (SANE_Handle handle)
+{
+ Plustek_Scanner *prev, *s;
+
+ DBG( _DBG_SANE_INIT, "sane_close\n" );
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+
+ for( s = first_handle; s; s = s->next ) {
+ if( s == handle )
+ break;
+ prev = s;
+ }
+
+ if (!s) {
+ DBG( _DBG_ERROR, "close: invalid handle %p\n", handle);
+ return;
+ }
+
+ close_pipe( s );
+
+ if( NULL != s->buf )
+ free(s->buf);
+
+ drvclose( s->hw );
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free(s);
+}
+
+/** goes through a string list and returns the start-address of the string
+ * that has been found, or NULL on error
+ */
+static const SANE_String_Const *search_string_list(
+ const SANE_String_Const *list, SANE_String value )
+{
+ while( *list != NULL && strcmp(value, *list) != 0 )
+ ++list;
+
+ if( *list == NULL )
+ return NULL;
+
+ return list;
+}
+
+/** return or set the parameter values, also do some checks
+ */
+SANE_Status sane_control_option( SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value,
+ SANE_Int * info)
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+ SANE_Status status;
+ const SANE_String_Const *optval;
+ pModeParam mp;
+ int scanmode;
+
+ if ( s->scanning )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if((option < 0) || (option >= NUM_OPTIONS))
+ return SANE_STATUS_INVAL;
+
+ if( NULL != info )
+ *info = 0;
+
+ switch( action ) {
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option) {
+ case OPT_PREVIEW:
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_CUSTOM_GAMMA:
+ *(SANE_Word *)value = s->val[option].w;
+ break;
+
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *)value =
+ (s->val[option].w << SANE_FIXED_SCALE_SHIFT);
+ break;
+
+ case OPT_MODE:
+ case OPT_EXT_MODE:
+ case OPT_HALFTONE:
+ strcpy ((char *) value,
+ s->opt[option].constraint.string_list[s->val[option].w]);
+ break;
+
+ /* word array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy( value, s->val[option].wa, s->opt[option].size );
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ status = sanei_constrain_value( s->opt + option, value, info );
+ if( SANE_STATUS_GOOD != status )
+ return status;
+
+ optval = NULL;
+ if( SANE_CONSTRAINT_STRING_LIST == s->opt[option].constraint_type ) {
+
+ optval = search_string_list( s->opt[option].constraint.string_list,
+ (char *) value);
+ if( NULL == optval )
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (option) {
+
+ case OPT_RESOLUTION: {
+ int n;
+ int min_d = s->hw->res_list[s->hw->res_list_size - 1];
+ int v = *(SANE_Word *)value;
+ int best = v;
+
+ for( n = 0; n < s->hw->res_list_size; n++ ) {
+ int d = abs(v - s->hw->res_list[n]);
+
+ if( d < min_d ) {
+ min_d = d;
+ best = s->hw->res_list[n];
+ }
+ }
+
+ s->val[option].w = (SANE_Word)best;
+
+ if( v != best )
+ *(SANE_Word *)value = best;
+
+ if( NULL != info ) {
+ if( v != best )
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+
+ }
+
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *)value;
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_CUSTOM_GAMMA:
+ s->val[option].w = *(SANE_Word *)value;
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ mp = getModeList( s );
+ scanmode = mp[s->val[OPT_MODE].w].scanmode;
+
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ if( SANE_TRUE == s->val[option].w ) {
+
+ if((scanmode == COLOR_256GRAY) ||
+ (scanmode == COLOR_GRAY16)) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ } else {
+
+ initGammaSettings( s );
+
+ if((scanmode == COLOR_256GRAY) ||
+ (scanmode == COLOR_GRAY16)) {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ break;
+
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ s->val[option].w =
+ ((*(SANE_Word *)value) >> SANE_FIXED_SCALE_SHIFT);
+ break;
+
+ case OPT_MODE: {
+
+ int idx = (optval - mode_list);
+
+ if((_ASIC_IS_98001 == s->hw->caps.AsicID) ||
+ (_ASIC_IS_98003 == s->hw->caps.AsicID)) {
+ idx = optval - mode_9800x_list;
+ }
+
+ mp = getModeList( s );
+
+ if( mp[idx].scanmode != COLOR_HALFTONE ){
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if( mp[idx].scanmode == COLOR_BW ) {
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ if( s->val[OPT_CUSTOM_GAMMA].w &&
+ !(s->opt[OPT_CUSTOM_GAMMA].cap & SANE_CAP_INACTIVE)) {
+
+ if((mp[idx].scanmode == COLOR_256GRAY) ||
+ (mp[idx].scanmode == COLOR_GRAY16)) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ /* fall through to OPT_HALFTONE */
+ case OPT_HALFTONE:
+ s->val[option].w = optval - s->opt[option].constraint.string_list;
+ break;
+
+ case OPT_EXT_MODE: {
+ s->val[option].w = optval - s->opt[option].constraint.string_list;
+
+ /*
+ * change the area and mode_list when changing the source
+ */
+ if( s->val[option].w == 0 ) {
+
+ s->hw->dpi_range.min = _DEF_DPI;
+
+ s->hw->x_range.max = SANE_FIX(s->hw->max_x);
+ s->hw->y_range.max = SANE_FIX(s->hw->max_y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
+ s->val[OPT_MODE].w = 3; /* COLOR_TRUE24 */
+
+ if((_ASIC_IS_98001 == s->hw->caps.AsicID) ||
+ (_ASIC_IS_98003 == s->hw->caps.AsicID)) {
+ s->opt[OPT_MODE].constraint.string_list = mode_9800x_list;
+ } else {
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ }
+
+ } else {
+
+ s->hw->dpi_range.min = _TPAMinDpi;
+
+ if( s->val[option].w == 1 ) {
+ s->hw->x_range.max = SANE_FIX(_TP_X);
+ s->hw->y_range.max = SANE_FIX(_TP_Y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TP_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TP_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_TP_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_TP_BRY);
+
+ } else {
+ s->hw->x_range.max = SANE_FIX(_NEG_X);
+ s->hw->y_range.max = SANE_FIX(_NEG_Y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_NEG_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_NEG_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_NEG_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_NEG_BRY);
+ }
+
+ if( s->hw->caps.dwFlag & SFLAG_TPA ) {
+
+ s->opt[OPT_MODE].constraint.string_list =
+ &mode_9800x_list[_TPAModeSupportMin];
+ } else {
+ s->opt[OPT_MODE].constraint.string_list =
+ &mode_list[_TPAModeSupportMin];
+ }
+ s->val[OPT_MODE].w = 0; /* COLOR_24 is the default */
+ }
+
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy( s->val[option].wa, value, s->opt[option].size );
+ checkGammaSettings(s);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** according to the option number, return a pointer to a descriptor
+ */
+const SANE_Option_Descriptor *
+ sane_get_option_descriptor( SANE_Handle handle, SANE_Int option )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ if((option < 0) || (option >= NUM_OPTIONS))
+ return NULL;
+
+ return &(s->opt[option]);
+}
+
+/** return the current parameter settings
+ */
+SANE_Status sane_get_parameters( SANE_Handle handle, SANE_Parameters *params )
+{
+ int ndpi;
+ pModeParam mp;
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ /* if we're calling from within, calc best guess
+ * do the same, if sane_get_parameters() is called
+ * by a frontend before sane_start() is called
+ */
+ if((NULL == params) || (s->scanning != SANE_TRUE)) {
+
+ mp = getModeList( s );
+
+ memset( &s->params, 0, sizeof (SANE_Parameters));
+
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ s->params.pixels_per_line = SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH * ndpi;
+
+ s->params.lines = SANE_UNFIX( s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH * ndpi;
+
+ /* pixels_per_line seems to be 8 * n. */
+ /* s->params.pixels_per_line = s->params.pixels_per_line & ~7; debug only */
+
+ s->params.last_frame = SANE_TRUE;
+ s->params.depth = mp[s->val[OPT_MODE].w].depth;
+
+ if( mp[s->val[OPT_MODE].w].color ) {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ } else {
+ s->params.format = SANE_FRAME_GRAY;
+ if (s->params.depth == 1)
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ else
+ s->params.bytes_per_line = s->params.pixels_per_line *
+ s->params.depth / 8;
+ }
+
+ /* if sane_get_parameters() was called before sane_start() */
+ /* pass new values to the caller */
+ if ((NULL != params) && (s->scanning != SANE_TRUE))
+ *params = s->params;
+ } else
+ *params = s->params;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** initiate the scan process
+ */
+SANE_Status sane_start( SANE_Handle handle )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *) handle;
+ pModeParam mp;
+
+ int result;
+ int ndpi;
+ int left, top;
+ int width, height;
+ int scanmode;
+ int fds[2];
+ StartScan start;
+ CropInfo crop;
+ ScanInfo sinfo;
+ SANE_Status status;
+ SANE_Word tmp;
+
+ DBG( _DBG_SANE_INIT, "sane_start\n" );
+
+ if( s->scanning ) {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = sane_get_parameters (handle, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DBG( _DBG_ERROR, "sane_get_parameters failed\n" );
+ return status;
+ }
+
+ /*
+ * open the driver and get some information about the scanner
+ */
+ s->hw->fd = drvopen( s->hw );
+ if( s->hw->fd < 0 ) {
+ DBG( _DBG_ERROR,"sane_start: open failed: %d\n", errno );
+
+ if( errno == EBUSY )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ result = s->hw->getCaps( s->hw );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->getCaps() failed(%d)\n", result);
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ result = s->hw->getLensInfo( s->hw, &lens );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->getLensInfo() failed(%d)\n", result );
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* did we fail on connection? */
+ if ( s->hw->caps.wIOBase == _NO_BASE ) {
+ DBG( _DBG_ERROR, "failed to find Plustek scanner\n" );
+ s->hw->close( s->hw );
+ return SANE_STATUS_INVAL;
+ }
+
+ /* All ready to go. Set image def and see what the scanner
+ * says for crop info.
+ */
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ /* exchange the values as we can't deal with negative heights and so on...*/
+ tmp = s->val[OPT_TL_X].w;
+ if( tmp > s->val[OPT_BR_X].w ) {
+ DBG( _DBG_INFO, "exchanging BR-X - TL-X\n" );
+ s->val[OPT_TL_X].w = s->val[OPT_BR_X].w;
+ s->val[OPT_BR_X].w = tmp;
+ }
+
+ tmp = s->val[OPT_TL_Y].w;
+ if( tmp > s->val[OPT_BR_Y].w ) {
+ DBG( _DBG_INFO, "exchanging BR-Y - TL-Y\n" );
+ s->val[OPT_TL_Y].w = s->val[OPT_BR_Y].w;
+ s->val[OPT_BR_Y].w = tmp;
+ }
+
+ /* position and extent are always relative to 300 dpi */
+ left = (int)(SANE_UNFIX (s->val[OPT_TL_X].w)*(double)lens.rDpiX.wPhyMax/
+ (MM_PER_INCH*((double)lens.rDpiX.wPhyMax/300.0)));
+ top = (int)(SANE_UNFIX (s->val[OPT_TL_Y].w)*(double)lens.rDpiY.wPhyMax/
+ (MM_PER_INCH*((double)lens.rDpiY.wPhyMax/300.0)));
+ width = (int)(SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) *
+ (double)lens.rDpiX.wPhyMax /
+ (MM_PER_INCH *((double)lens.rDpiX.wPhyMax/300.0)));
+ height = (int)(SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) *
+ (double)lens.rDpiY.wPhyMax /
+ (MM_PER_INCH *((double)lens.rDpiY.wPhyMax/300.0)));
+
+ /*
+ * adjust mode list according to the model we use and the
+ * source we have
+ */
+ mp = getModeList( s );
+
+ scanmode = mp[s->val[OPT_MODE].w].scanmode;
+ DBG( _DBG_INFO, "scanmode = %u\n", scanmode );
+
+ /* clear it out just in case */
+ memset (&sinfo, 0, sizeof(sinfo));
+ sinfo.ImgDef.xyDpi.x = ndpi;
+ sinfo.ImgDef.xyDpi.y = ndpi;
+ sinfo.ImgDef.crArea.x = left; /* offset from left edge to area you want to scan */
+ sinfo.ImgDef.crArea.y = top; /* offset from top edge to area you want to scan */
+ sinfo.ImgDef.crArea.cx = width; /* always relative to 300 dpi */
+ sinfo.ImgDef.crArea.cy = height;
+ sinfo.ImgDef.wDataType = scanmode;
+
+/*
+ * CHECK: what about the 10 bit mode?
+ */
+#if 0
+ if( COLOR_TRUE48 == scanmode )
+ sinfo.ImgDef.wBits = OUTPUT_12Bits;
+ else if( COLOR_TRUE32 == scanmode )
+ sinfo.ImgDef.wBits = OUTPUT_10Bits;
+ else
+ sinfo.ImgDef.wBits = OUTPUT_8Bits;
+#endif
+ sinfo.ImgDef.dwFlag = SCANDEF_QualityScan;
+
+ switch( s->val[OPT_EXT_MODE].w ) {
+ case 1: sinfo.ImgDef.dwFlag |= SCANDEF_Transparency; break;
+ case 2: sinfo.ImgDef.dwFlag |= SCANDEF_Negative; break;
+ default: break;
+ }
+
+ /* only for parallel-port devices */
+ if( s->hw->putImgInfo ) {
+ result = s->hw->putImgInfo( s->hw, &sinfo.ImgDef );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->putImgInfo failed(%d)\n", result );
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+ } else {
+
+ memcpy( &(crop.ImgDef), &sinfo.ImgDef, sizeof(ImgDef));
+
+ }
+
+ result = s->hw->getCropInfo( s->hw, &crop );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->getCropInfo() failed(%d)\n", result );
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* DataInf.dwAppPixelsPerLine = crop.dwPixelsPerLine; */
+ s->params.pixels_per_line = crop.dwPixelsPerLine;
+ s->params.bytes_per_line = crop.dwBytesPerLine;
+ s->params.lines = crop.dwLinesPerArea;
+
+ /* build a SCANINFO block and get ready to scan it */
+ sinfo.ImgDef.dwFlag |= (SCANDEF_BuildBwMap | SCANDEF_QualityScan);
+
+ /* set adjustments for brightness and contrast */
+ sinfo.siBrightness = s->val[OPT_BRIGHTNESS].w;
+ sinfo.siContrast = s->val[OPT_CONTRAST].w;
+ sinfo.wDither = s->val[OPT_HALFTONE].w;
+
+ DBG( _DBG_SANE_INIT, "bright %i contrast %i\n", sinfo.siBrightness,
+ sinfo.siContrast);
+
+ result = s->hw->setScanEnv( s->hw, &sinfo );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->setEnv() failed(%d)\n", result );
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* download gamma correction tables... */
+ if( scanmode <= COLOR_256GRAY || scanmode == COLOR_GRAY16 ) {
+ s->hw->setMap( s->hw, s->gamma_table[0], s->gamma_length, _MAP_MASTER);
+ } else {
+ s->hw->setMap( s->hw, s->gamma_table[1], s->gamma_length, _MAP_RED );
+ s->hw->setMap( s->hw, s->gamma_table[2], s->gamma_length, _MAP_GREEN );
+ s->hw->setMap( s->hw, s->gamma_table[3], s->gamma_length, _MAP_BLUE );
+ }
+ /* work-around for USB... */
+ start.dwLinesPerScan = s->params.lines;
+
+ result = s->hw->startScan( s->hw, &start );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "dev->startScan() failed(%d)\n", result );
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG( _DBG_SANE_INIT, "dwflag = 0x%lx dwBytesPerLine = %ld, "
+ "dwLinesPerScan = %ld\n",
+ start.dwFlag, start.dwBytesPerLine, start.dwLinesPerScan);
+
+ s->buf = realloc( s->buf, (s->params.lines) * s->params.bytes_per_line );
+ if( NULL == s->buf ) {
+ DBG( _DBG_ERROR, "realloc failed\n" );
+ s->hw->close( s->hw );
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->scanning = SANE_TRUE;
+
+ tsecs = (unsigned long)time(NULL);
+ DBG( _DBG_INFO, "TIME START\n" );
+
+ /*
+ * everything prepared, so start the child process and a pipe to communicate
+ * pipe --> fds[0]=read-fd, fds[1]=write-fd
+ */
+ if( pipe(fds) < 0 ) {
+ DBG( _DBG_ERROR, "ERROR: could not create pipe\n" );
+ s->scanning = SANE_FALSE;
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* create reader routine as new process */
+ s->bytes_read = 0;
+ s->r_pipe = fds[0];
+ s->w_pipe = fds[1];
+ s->reader_pid = sanei_thread_begin( reader_process, s );
+
+ if( s->reader_pid == -1 ) {
+ DBG( _DBG_ERROR, "ERROR: could not create child process\n" );
+ s->scanning = SANE_FALSE;
+ s->hw->close( s->hw );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* reader_pid = 0 ===> child process */
+#if 0
+ if( 0 == s->reader_pid ) {
+
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ DBG( _DBG_SANE_INIT, "reader process...\n" );
+
+ close(fds[0]);
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGTERM );
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset ( &act, 0, sizeof (act));
+ sigaction( SIGTERM, &act, 0 );
+
+ status = reader_process( s, fds[1] );
+
+ DBG( _DBG_SANE_INIT, "reader process done, status = %i\n", status );
+
+ /* don't use exit() since that would run the atexit() handlers */
+ _exit( status );
+ }
+#endif
+ signal( SIGCHLD, sig_chldhandler );
+
+ if( sanei_thread_is_forked()) {
+ close( s->w_pipe );
+ s->w_pipe = -1;
+ }
+
+ DBG( _DBG_SANE_INIT, "sane_start done\n" );
+
+ return SANE_STATUS_GOOD;
+}
+
+/** function to read the data from our child process
+ */
+SANE_Status sane_read( SANE_Handle handle, SANE_Byte *data,
+ SANE_Int max_length, SANE_Int *length )
+{
+ Plustek_Scanner *s = (Plustek_Scanner*)handle;
+ ssize_t nread;
+
+ *length = 0;
+
+ /* here we read all data from the driver... */
+ nread = read( s->r_pipe, data, max_length );
+ DBG( _DBG_READ, "sane_read - read %ld bytes\n", (long)nread );
+ if (!(s->scanning)) {
+ return do_cancel( s, SANE_TRUE );
+ }
+
+ if( nread < 0 ) {
+
+ if( EAGAIN == errno ) {
+
+ /* if we had already red the picture, so it's okay and stop */
+ if( s->bytes_read ==
+ (unsigned long)(s->params.lines * s->params.bytes_per_line)) {
+ sanei_thread_waitpid( s->reader_pid, 0 );
+ s->reader_pid = -1;
+ drvclose( s->hw );
+ return close_pipe(s);
+ }
+
+ /* else force the frontend to try again*/
+ return SANE_STATUS_GOOD;
+
+ } else {
+ DBG( _DBG_ERROR, "ERROR: errno=%d\n", errno );
+ do_cancel( s, SANE_TRUE );
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *length = nread;
+ s->bytes_read += nread;
+
+ /* nothing red means that we're finished OR we had a problem...*/
+ if( 0 == nread ) {
+
+ drvclose( s->hw );
+ s->exit_code = sanei_thread_get_status( s->reader_pid );
+
+ if( SANE_STATUS_GOOD != s->exit_code ) {
+ close_pipe(s);
+ return s->exit_code;
+ }
+ s->reader_pid = -1;
+ return close_pipe(s);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** cancel the scanning process
+ */
+void sane_cancel (SANE_Handle handle)
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_cancel\n" );
+
+ if( s->scanning )
+ do_cancel( s, SANE_FALSE );
+}
+
+/** set the pipe to blocking/non blocking mode
+ */
+SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_set_io_mode: non_blocking=%d\n",non_blocking );
+
+ if ( !s->scanning ) {
+ DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ if( -1 == s->r_pipe ) {
+ DBG( _DBG_ERROR, "ERROR: not supported !\n" );
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if( fcntl (s->r_pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) {
+ DBG( _DBG_ERROR, "ERROR: can´t set to non-blocking mode !\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG( _DBG_SANE_INIT, "sane_set_io_mode done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** return the descriptor if available
+ */
+SANE_Status sane_get_select_fd( SANE_Handle handle, SANE_Int * fd )
+{
+ Plustek_Scanner *s = (Plustek_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_get_select_fd\n" );
+
+ if( !s->scanning ) {
+ DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->r_pipe;
+
+ DBG( _DBG_SANE_INIT, "sane_get_select_fd done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/* END PLUSTEK_PP.C .........................................................*/
diff --git a/backend/plustek_pp.conf.in b/backend/plustek_pp.conf.in
new file mode 100644
index 0000000..a000343
--- /dev/null
+++ b/backend/plustek_pp.conf.in
@@ -0,0 +1,40 @@
+# Plustek-PP SANE Backend configuration file
+# For use with Plustek parallel-port scanners
+#
+
+#
+# user either [direct] or [kernel] to access the scanner
+# when using [kernel], device specifies the device-node, which is created
+# by the kernel-module loader (applies only to Linux)
+# when using [direct], device is used to set the parallel-port base address
+# or a device-name suitable for libieee1284, i.e. parport0
+#
+#[direct]
+#device 0x378
+
+#
+# example for accessing the scanner via libieee1284
+#
+[direct]
+device parport0
+
+#
+# leave the default values as specified in /etc/modules.conf
+#
+option warmup -1
+option lOffOnEnd -1
+option lampOff -1
+
+# model override switch, mostly for cosmetic changes, if the autodetection
+# does not work or could not work correctly
+#option mov 7
+
+#
+# example for accessing the scanner via the kernel module
+#
+#[kernel]
+#device /dev/pt_drv
+#
+#option warmup -1
+#option lOffOnEnd -1
+#option lampOff -1
diff --git a/backend/pnm.c b/backend/pnm.c
new file mode 100644
index 0000000..88b6e4a
--- /dev/null
+++ b/backend/pnm.c
@@ -0,0 +1,1395 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 Andreas Beck
+ Copyright (C) 2000, 2001 Michael Herder <crapsite@gmx.net>
+ Copyright (C) 2001, 2002 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2008 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#define BUILD 9
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME pnm
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define MAGIC (void *)0xab730324
+
+static int is_open = 0;
+static int rgb_comp = 0;
+static int three_pass = 0;
+static int hand_scanner = 0;
+static int pass = 0;
+static char filename[PATH_MAX] = "/tmp/input.ppm";
+static SANE_Word status_none = SANE_TRUE;
+static SANE_Word status_eof = SANE_FALSE;
+static SANE_Word status_jammed = SANE_FALSE;
+static SANE_Word status_nodocs = SANE_FALSE;
+static SANE_Word status_coveropen = SANE_FALSE;
+static SANE_Word status_ioerror = SANE_FALSE;
+static SANE_Word status_nomem = SANE_FALSE;
+static SANE_Word status_accessdenied = SANE_FALSE;
+static SANE_Word test_option = 0;
+#ifdef SANE_STATUS_WARMING_UP
+static SANE_Word warming_up = SANE_FALSE;
+static struct timeval start;
+#endif
+
+static SANE_Fixed bright = 0;
+static SANE_Word res = 75;
+static SANE_Fixed contr = 0;
+static SANE_Bool gray = SANE_FALSE;
+static SANE_Bool usegamma = SANE_FALSE;
+static SANE_Word gamma[4][256];
+static enum
+{
+ ppm_bitmap,
+ ppm_greyscale,
+ ppm_color
+}
+ppm_type = ppm_color;
+static FILE *infile = NULL;
+static const SANE_Word resbit_list[] = {
+ 17,
+ 75, 90, 100, 120, 135, 150, 165, 180, 195,
+ 200, 210, 225, 240, 255, 270, 285, 300
+};
+static const SANE_Range percentage_range = {
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 0 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+typedef enum
+{
+ opt_num_opts = 0,
+ opt_source_group,
+ opt_filename,
+ opt_resolution,
+ opt_enhancement_group,
+ opt_brightness,
+ opt_contrast,
+ opt_grayify,
+ opt_three_pass,
+ opt_hand_scanner,
+ opt_default_enhancements,
+ opt_read_only,
+ opt_gamma_group,
+ opt_custom_gamma,
+ opt_gamma,
+ opt_gamma_r,
+ opt_gamma_g,
+ opt_gamma_b,
+ opt_status_group,
+ opt_status,
+ opt_status_eof,
+ opt_status_jammed,
+ opt_status_nodocs,
+ opt_status_coveropen,
+ opt_status_ioerror,
+ opt_status_nomem,
+ opt_status_accessdenied,
+
+ /* must come last: */
+ num_options
+}
+pnm_opts;
+
+static SANE_Option_Descriptor sod[] = {
+ { /* opt_num_opts */
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_source_group */
+ "",
+ SANE_I18N ("Source Selection"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_filename */
+ SANE_NAME_FILE,
+ SANE_TITLE_FILE,
+ SANE_DESC_FILE,
+ SANE_TYPE_STRING,
+ SANE_UNIT_NONE,
+ sizeof (filename),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ {
+ /* opt_resolution */
+ SANE_NAME_SCAN_RESOLUTION,
+ SANE_TITLE_SCAN_RESOLUTION,
+ SANE_DESC_SCAN_RESOLUTION,
+ SANE_TYPE_INT,
+ SANE_UNIT_DPI,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC,
+ SANE_CONSTRAINT_WORD_LIST,
+ {(SANE_String_Const *) resbit_list}
+ }
+ ,
+ { /* opt_enhancement_group */
+ "",
+ SANE_I18N ("Image Enhancement"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_brightness */
+ SANE_NAME_BRIGHTNESS,
+ SANE_TITLE_BRIGHTNESS,
+ SANE_DESC_BRIGHTNESS,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_PERCENT,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & percentage_range} /* this is ANSI conformant! */
+ }
+ ,
+ { /* opt_contrast */
+ SANE_NAME_CONTRAST,
+ SANE_TITLE_CONTRAST,
+ SANE_DESC_CONTRAST,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_PERCENT,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & percentage_range} /* this is ANSI conformant! */
+ }
+ ,
+ { /* opt_grayify */
+ "grayify",
+ SANE_I18N ("Grayify"),
+ SANE_I18N ("Load the image as grayscale."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_three_pass */
+ "three-pass",
+ SANE_I18N ("Three-Pass Simulation"),
+ SANE_I18N
+ ("Simulate a three-pass scanner by returning 3 separate frames. "
+ "For kicks, it returns green, then blue, then red."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_hand_scanner */
+ "hand-scanner",
+ SANE_I18N ("Hand-Scanner Simulation"),
+ SANE_I18N ("Simulate a hand-scanner. Hand-scanners often do not know the "
+ "image height a priori. Instead, they return a height of -1. "
+ "Setting this option allows to test whether a frontend can "
+ "handle this correctly."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_default_enhancements */
+ "default-enhancements",
+ SANE_I18N ("Defaults"),
+ SANE_I18N ("Set default values for enhancement controls (brightness & "
+ "contrast)."),
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_read_only */
+ "read-only",
+ SANE_I18N ("Read only test-option"),
+ SANE_I18N ("Let's see whether frontends can treat this right"),
+ SANE_TYPE_INT,
+ SANE_UNIT_PERCENT,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_gamma_group */
+ "",
+ SANE_I18N ("Gamma Tables"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ 0,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_custom_gamma */
+ SANE_NAME_CUSTOM_GAMMA,
+ SANE_TITLE_CUSTOM_GAMMA,
+ SANE_DESC_CUSTOM_GAMMA,
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_gamma */
+ SANE_NAME_GAMMA_VECTOR,
+ SANE_TITLE_GAMMA_VECTOR,
+ SANE_DESC_GAMMA_VECTOR,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_gamma_r */
+ SANE_NAME_GAMMA_VECTOR_R,
+ SANE_TITLE_GAMMA_VECTOR_R,
+ SANE_DESC_GAMMA_VECTOR_R,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_gamma_g */
+ SANE_NAME_GAMMA_VECTOR_G,
+ SANE_TITLE_GAMMA_VECTOR_G,
+ SANE_DESC_GAMMA_VECTOR_G,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_gamma_b */
+ SANE_NAME_GAMMA_VECTOR_B,
+ SANE_TITLE_GAMMA_VECTOR_B,
+ SANE_DESC_GAMMA_VECTOR_B,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word) * 256,
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE,
+ SANE_CONSTRAINT_RANGE,
+ {(SANE_String_Const *) & gamma_range}
+ }
+ ,
+ { /* opt_status_group */
+ "",
+ SANE_I18N ("Status Code Simulation"),
+ "",
+ SANE_TYPE_GROUP,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_ADVANCED,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status */
+ "status",
+ SANE_I18N ("Do not force status code"),
+ SANE_I18N ("Do not force the backend to return a status code."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_eof */
+ "status-eof",
+ SANE_I18N ("Return SANE_STATUS_EOF"),
+ SANE_I18N ("Force the backend to return the status code SANE_STATUS_EOF "
+ "after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_jammed */
+ "status-jammed",
+ SANE_I18N ("Return SANE_STATUS_JAMMED"),
+ SANE_I18N
+ ("Force the backend to return the status code SANE_STATUS_JAMMED "
+ "after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_nodocs */
+ "status-nodocs",
+ SANE_I18N ("Return SANE_STATUS_NO_DOCS"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_NO_DOCS after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_coveropen */
+ "status-coveropen",
+ SANE_I18N ("Return SANE_STATUS_COVER_OPEN"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_COVER_OPEN after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_ioerror */
+ "status-ioerror",
+ SANE_I18N ("Return SANE_STATUS_IO_ERROR"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_IO_ERROR after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_nomem */
+ "status-nomem",
+ SANE_I18N ("Return SANE_STATUS_NO_MEM"),
+ SANE_I18N
+ ("Force the backend to return the status code SANE_STATUS_NO_MEM "
+ "after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+ ,
+ { /* opt_status_accessdenied */
+ "status-accessdenied",
+ SANE_I18N ("Return SANE_STATUS_ACCESS_DENIED"),
+ SANE_I18N ("Force the backend to return the status code "
+ "SANE_STATUS_ACCESS_DENIED after sane_read() has been called."),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Bool),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+ }
+};
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+/* This library is a demo implementation of a SANE backend. It
+ implements a virtual device, a PNM file-filter. */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+
+ DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+ version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+ DBG (1, "sane_init: SANE pnm backend version %d.%d.%d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ DBG (2, "sane_exit\n");
+ return;
+}
+
+/* Device select/open/close */
+
+static const SANE_Device dev[] = {
+ {
+ "0",
+ "Noname",
+ "PNM file reader",
+ "virtual device"},
+ {
+ "1",
+ "Noname",
+ "PNM file reader",
+ "virtual device"},
+#ifdef SANE_STATUS_HW_LOCKED
+ {
+ "locked",
+ "Noname",
+ "Hardware locked",
+ "virtual device"},
+#endif
+#ifdef SANE_STATUS_WARMING_UP
+ {
+ "warmup",
+ "Noname",
+ "Always warming up",
+ "virtual device"},
+#endif
+};
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device *devlist[] = {
+ dev + 0, dev + 1,
+#ifdef SANE_STATUS_HW_LOCKED
+ dev + 2,
+#endif
+#ifdef SANE_STATUS_WARMING_UP
+ dev + 3,
+#endif
+ 0
+ };
+
+ DBG (2, "sane_get_devices: local_only = %d\n", local_only);
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ int i;
+
+ if (!devicename)
+ return SANE_STATUS_INVAL;
+ DBG (2, "sane_open: devicename = \"%s\"\n", devicename);
+
+ if (!devicename[0])
+ i = 0;
+ else
+ for (i = 0; i < NELEMS (dev); ++i)
+ if (strcmp (devicename, dev[i].name) == 0)
+ break;
+ if (i >= NELEMS (dev))
+ return SANE_STATUS_INVAL;
+
+ if (is_open)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ is_open = 1;
+ *handle = MAGIC;
+ for (i = 0; i < 256; i++)
+ {
+ gamma[0][i] = i;
+ gamma[1][i] = i;
+ gamma[2][i] = i;
+ gamma[3][i] = i;
+ }
+
+#ifdef SANE_STATUS_HW_LOCKED
+ if(strncmp(devicename,"locked",6)==0)
+ return SANE_STATUS_HW_LOCKED;
+#endif
+
+#ifdef SANE_STATUS_WARMING_UP
+ if(strncmp(devicename,"warmup",6)==0)
+ {
+ warming_up = SANE_TRUE;
+ start.tv_sec = 0;
+ }
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (2, "sane_close\n");
+ if (handle == MAGIC)
+ is_open = 0;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ DBG (2, "sane_get_option_descriptor: option = %d\n", option);
+ if (handle != MAGIC || !is_open)
+ return NULL; /* wrong device */
+ if (option < 0 || option >= NELEMS (sod))
+ return NULL;
+ return &sod[option];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Int myinfo = 0;
+ SANE_Status status;
+ int v;
+ v = 75;
+
+ DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ handle, option, action, value, info);
+
+ if (handle != MAGIC || !is_open)
+ {
+ DBG (1, "sane_control_option: unknown handle or not open\n");
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+ }
+
+ if (option < 0 || option >= NELEMS (sod))
+ {
+ DBG (1, "sane_control_option: option %d < 0 or >= number of options\n",
+ option);
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap))
+ {
+ DBG (4, "sane_control_option: option is inactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ if (!SANE_OPTION_IS_SETTABLE (sod[option].cap))
+ {
+ DBG (4, "sane_control_option: option is not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = sanei_constrain_value (sod + option, (void *) &v, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ switch (option)
+ {
+ case opt_resolution:
+ res = 75;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (sod[option].cap))
+ {
+ DBG (4, "sane_control_option: option is not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = sanei_constrain_value (sod + option, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ switch (option)
+ {
+ case opt_filename:
+ if ((strlen (value) + 1) > sizeof (filename))
+ return SANE_STATUS_NO_MEM;
+ strcpy (filename, value);
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case opt_resolution:
+ res = *(SANE_Word *) value;
+ break;
+ case opt_brightness:
+ bright = *(SANE_Word *) value;
+ break;
+ case opt_contrast:
+ contr = *(SANE_Word *) value;
+ break;
+ case opt_grayify:
+ gray = !!*(SANE_Word *) value;
+ if (usegamma)
+ {
+ if (gray)
+ {
+ sod[opt_gamma].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_three_pass:
+ three_pass = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case opt_hand_scanner:
+ hand_scanner = !!*(SANE_Word *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case opt_default_enhancements:
+ bright = contr = 0;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_custom_gamma:
+ usegamma = *(SANE_Word *) value;
+ /* activate/deactivate gamma */
+ if (usegamma)
+ {
+ test_option = 100;
+ if (gray)
+ {
+ sod[opt_gamma].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap &= ~SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ test_option = 0;
+ sod[opt_gamma].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_r].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_g].cap |= SANE_CAP_INACTIVE;
+ sod[opt_gamma_b].cap |= SANE_CAP_INACTIVE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma:
+ memcpy (&gamma[0][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma_r:
+ memcpy (&gamma[1][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma_g:
+ memcpy (&gamma[2][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_gamma_b:
+ memcpy (&gamma[3][0], (SANE_Word *) value,
+ 256 * sizeof (SANE_Word));
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ /* status */
+ case opt_status:
+ status_none = *(SANE_Word *) value;
+ if (status_none)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_eof:
+ status_eof = *(SANE_Word *) value;
+ if (status_eof)
+ {
+ status_none = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_jammed:
+ status_jammed = *(SANE_Word *) value;
+ if (status_jammed)
+ {
+ status_eof = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_nodocs:
+ status_nodocs = *(SANE_Word *) value;
+ if (status_nodocs)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_coveropen:
+ status_coveropen = *(SANE_Word *) value;
+ if (status_coveropen)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_ioerror:
+ status_ioerror = *(SANE_Word *) value;
+ if (status_ioerror)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_nomem:
+ status_nomem = *(SANE_Word *) value;
+ if (status_nomem)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_none = SANE_FALSE;
+ status_accessdenied = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case opt_status_accessdenied:
+ status_accessdenied = *(SANE_Word *) value;
+ if (status_accessdenied)
+ {
+ status_eof = SANE_FALSE;
+ status_jammed = SANE_FALSE;
+ status_nodocs = SANE_FALSE;
+ status_coveropen = SANE_FALSE;
+ status_ioerror = SANE_FALSE;
+ status_nomem = SANE_FALSE;
+ status_none = SANE_FALSE;
+ }
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ case opt_num_opts:
+ *(SANE_Word *) value = NELEMS (sod);
+ break;
+ case opt_filename:
+ strcpy (value, filename);
+ break;
+ case opt_resolution:
+ *(SANE_Word *) value = res;
+ break;
+ case opt_brightness:
+ *(SANE_Word *) value = bright;
+ break;
+ case opt_contrast:
+ *(SANE_Word *) value = contr;
+ break;
+ case opt_grayify:
+ *(SANE_Word *) value = gray;
+ break;
+ case opt_three_pass:
+ *(SANE_Word *) value = three_pass;
+ break;
+ case opt_hand_scanner:
+ *(SANE_Word *) value = hand_scanner;
+ break;
+ case opt_read_only:
+ *(SANE_Word *) value = test_option;
+ break;
+ case opt_custom_gamma:
+ *(SANE_Word *) value = usegamma;
+ break;
+ case opt_gamma:
+ memcpy ((SANE_Word *) value, &gamma[0][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_gamma_r:
+ memcpy ((SANE_Word *) value, &gamma[1][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_gamma_g:
+ memcpy ((SANE_Word *) value, &gamma[2][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_gamma_b:
+ memcpy ((SANE_Word *) value, &gamma[3][0],
+ 256 * sizeof (SANE_Word));
+ break;
+ case opt_status:
+ *(SANE_Word *) value = status_none;
+ break;
+ case opt_status_eof:
+ *(SANE_Word *) value = status_eof;
+ break;
+ case opt_status_jammed:
+ *(SANE_Word *) value = status_jammed;
+ break;
+ case opt_status_nodocs:
+ *(SANE_Word *) value = status_nodocs;
+ break;
+ case opt_status_coveropen:
+ *(SANE_Word *) value = status_coveropen;
+ break;
+ case opt_status_ioerror:
+ *(SANE_Word *) value = status_ioerror;
+ break;
+ case opt_status_nomem:
+ *(SANE_Word *) value = status_nomem;
+ break;
+ case opt_status_accessdenied:
+ *(SANE_Word *) value = status_accessdenied;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ }
+ if (info)
+ *info = myinfo;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+get_line (char *buf, int len, FILE * f)
+{
+ do
+ fgets (buf, len, f);
+ while (*buf == '#');
+}
+
+static int
+getparmfromfile (void)
+{
+ FILE *fn;
+ int x, y;
+ char buf[1024];
+
+ parms.depth = 8;
+ parms.bytes_per_line = parms.pixels_per_line = parms.lines = 0;
+ if ((fn = fopen (filename, "rb")) == NULL)
+ {
+ DBG (1, "getparmfromfile: unable to open file \"%s\"\n", filename);
+ return -1;
+ }
+
+ /* Skip comments. */
+ do
+ get_line (buf, sizeof (buf), fn);
+ while (*buf == '#');
+ if (!strncmp (buf, "P4", 2))
+ {
+ /* Binary monochrome. */
+ parms.depth = 1;
+ ppm_type = ppm_bitmap;
+ }
+ else if (!strncmp (buf, "P5", 2))
+ {
+ /* Grayscale. */
+ parms.depth = 8;
+ ppm_type = ppm_greyscale;
+ }
+ else if (!strncmp (buf, "P6", 2))
+ {
+ /* Color. */
+ parms.depth = 8;
+ ppm_type = ppm_color;
+ }
+ else
+ {
+ DBG (1, "getparmfromfile: %s is not a recognized PPM\n", filename);
+ fclose (fn);
+ return -1;
+ }
+
+ /* Skip comments. */
+ do
+ get_line (buf, sizeof (buf), fn);
+ while (*buf == '#');
+ sscanf (buf, "%d %d", &x, &y);
+
+ parms.last_frame = SANE_TRUE;
+ parms.bytes_per_line = (ppm_type == ppm_bitmap) ? (x + 7) / 8 : x;
+ parms.pixels_per_line = x;
+ if (hand_scanner)
+ parms.lines = -1;
+ else
+ parms.lines = y;
+ if ((ppm_type == ppm_greyscale) || (ppm_type == ppm_bitmap) || gray)
+ parms.format = SANE_FRAME_GRAY;
+ else
+ {
+ if (three_pass)
+ {
+ parms.format = SANE_FRAME_RED + (pass + 1) % 3;
+ parms.last_frame = (pass >= 2);
+ }
+ else
+ {
+ parms.format = SANE_FRAME_RGB;
+ parms.bytes_per_line *= 3;
+ }
+ }
+ fclose (fn);
+ return 0;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int rc = SANE_STATUS_GOOD;
+
+ DBG (2, "sane_get_parameters\n");
+ if (handle != MAGIC || !is_open)
+ rc = SANE_STATUS_INVAL; /* Unknown handle ... */
+ else if (getparmfromfile ())
+ rc = SANE_STATUS_INVAL;
+ *params = parms;
+ return rc;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char buf[1024];
+ int nlines;
+#ifdef SANE_STATUS_WARMING_UP
+ struct timeval current;
+#endif
+
+ DBG (2, "sane_start\n");
+ rgb_comp = 0;
+ if (handle != MAGIC || !is_open)
+ return SANE_STATUS_INVAL; /* Unknown handle ... */
+
+#ifdef SANE_STATUS_WARMING_UP
+ if(warming_up == SANE_TRUE)
+ {
+ gettimeofday(&current,NULL);
+ if(current.tv_sec-start.tv_sec>5)
+ {
+ start.tv_sec = current.tv_sec;
+ return SANE_STATUS_WARMING_UP;
+ }
+ if(current.tv_sec-start.tv_sec<5)
+ return SANE_STATUS_WARMING_UP;
+ }
+#endif
+
+ if (infile != NULL)
+ {
+ fclose (infile);
+ infile = NULL;
+ if (!three_pass || ++pass >= 3)
+ return SANE_STATUS_EOF;
+ }
+
+ if (getparmfromfile ())
+ return SANE_STATUS_INVAL;
+
+ if ((infile = fopen (filename, "rb")) == NULL)
+ {
+ DBG (1, "sane_start: unable to open file \"%s\"\n", filename);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Skip the header (only two lines for a bitmap). */
+ nlines = (ppm_type == ppm_bitmap) ? 1 : 0;
+ while (nlines < 3)
+ {
+ /* Skip comments. */
+ get_line (buf, sizeof (buf), infile);
+ if (*buf != '#')
+ nlines++;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Int rgblength = 0;
+static SANE_Byte *rgbbuf = 0;
+static SANE_Byte rgbleftover[3] = { 0, 0, 0 };
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ int len, x, hlp;
+
+ DBG (2, "sane_read: max_length = %d, rgbleftover = {%d, %d, %d}\n",
+ max_length, rgbleftover[0], rgbleftover[1], rgbleftover[2]);
+ if (!length)
+ {
+ DBG (1, "sane_read: length == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+ *length = 0;
+ if (!data)
+ {
+ DBG (1, "sane_read: data == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (handle != MAGIC)
+ {
+ DBG (1, "sane_read: unknown handle\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!is_open)
+ {
+ DBG (1, "sane_read: call sane_open first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!infile)
+ {
+ DBG (1, "sane_read: scan was cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ if (feof (infile))
+ {
+ DBG (2, "sane_read: EOF reached\n");
+ return SANE_STATUS_EOF;
+ }
+
+ if (status_jammed == SANE_TRUE)
+ return SANE_STATUS_JAMMED;
+ if (status_eof == SANE_TRUE)
+ return SANE_STATUS_EOF;
+ if (status_nodocs == SANE_TRUE)
+ return SANE_STATUS_NO_DOCS;
+ if (status_coveropen == SANE_TRUE)
+ return SANE_STATUS_COVER_OPEN;
+ if (status_ioerror == SANE_TRUE)
+ return SANE_STATUS_IO_ERROR;
+ if (status_nomem == SANE_TRUE)
+ return SANE_STATUS_NO_MEM;
+ if (status_accessdenied == SANE_TRUE)
+ return SANE_STATUS_ACCESS_DENIED;
+
+ /* Allocate a buffer for the RGB values. */
+ if (ppm_type == ppm_color && (gray || three_pass))
+ {
+ SANE_Byte *p, *q, *rgbend;
+ if (rgbbuf == 0 || rgblength < 3 * max_length)
+ {
+ /* Allocate a new rgbbuf. */
+ free (rgbbuf);
+ rgblength = 3 * max_length;
+ rgbbuf = malloc (rgblength);
+ if (rgbbuf == 0)
+ return SANE_STATUS_NO_MEM;
+ }
+ else
+ rgblength = 3 * max_length;
+
+ /* Copy any leftovers into the buffer. */
+ q = rgbbuf;
+ p = rgbleftover + 1;
+ while (p - rgbleftover <= rgbleftover[0])
+ *q++ = *p++;
+
+ /* Slurp in the RGB buffer. */
+ len = fread (q, 1, rgblength - rgbleftover[0], infile);
+ rgbend = rgbbuf + len;
+
+ q = data;
+ if (gray)
+ {
+ /* Zip through the buffer, converting color data to grayscale. */
+ for (p = rgbbuf; p < rgbend; p += 3)
+ *q++ = ((long) p[0] + p[1] + p[2]) / 3;
+ }
+ else
+ {
+ /* Zip through the buffer, extracting data for this pass. */
+ for (p = (rgbbuf + (pass + 1) % 3); p < rgbend; p += 3)
+ *q++ = *p;
+ }
+
+ /* Save any leftovers in the array. */
+ rgbleftover[0] = len % 3;
+ p = rgbbuf + (len - rgbleftover[0]);
+ q = rgbleftover + 1;
+ while (p < rgbend)
+ *q++ = *p++;
+
+ len /= 3;
+ }
+ else
+ /* Suck in as much of the file as possible, since it's already in the
+ correct format. */
+ len = fread (data, 1, max_length, infile);
+
+ if (len == 0)
+ {
+ if (feof (infile))
+ {
+ DBG (2, "sane_read: EOF reached\n");
+ return SANE_STATUS_EOF;
+ }
+ else
+ {
+ DBG (1, "sane_read: error while reading file (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (parms.depth == 8)
+ {
+ /* Do the transformations ... DEMO ONLY ! THIS MAKES NO SENSE ! */
+ for (x = 0; x < len; x++)
+ {
+ hlp = *((unsigned char *) data + x) - 128;
+ hlp *= (contr + (100 << SANE_FIXED_SCALE_SHIFT));
+ hlp /= 100 << SANE_FIXED_SCALE_SHIFT;
+ hlp += (bright >> SANE_FIXED_SCALE_SHIFT) + 128;
+ if (hlp < 0)
+ hlp = 0;
+ if (hlp > 255)
+ hlp = 255;
+ *(data + x) = hlp;
+ }
+ /*gamma */
+ if (usegamma)
+ {
+ unsigned char uc;
+ if (gray)
+ {
+ for (x = 0; x < len; x++)
+ {
+ uc = *((unsigned char *) data + x);
+ uc = gamma[0][uc];
+ *(data + x) = uc;
+ }
+ }
+ else
+ {
+ for (x = 0; x < len; x++)
+ {
+ if (parms.format == SANE_FRAME_RGB)
+ {
+ uc = *((unsigned char *) (data + x));
+ uc = (unsigned char) gamma[rgb_comp + 1][(int) uc];
+ *((unsigned char *) data + x) = uc;
+ rgb_comp += 1;
+ if (rgb_comp > 2)
+ rgb_comp = 0;
+ }
+ else
+ {
+ int f = 0;
+ if (parms.format == SANE_FRAME_RED)
+ f = 1;
+ if (parms.format == SANE_FRAME_GREEN)
+ f = 2;
+ if (parms.format == SANE_FRAME_BLUE)
+ f = 3;
+ if (f)
+ {
+ uc = *((unsigned char *) (data + x));
+ uc = (unsigned char) gamma[f][(int) uc];
+ *((unsigned char *) data + x) = uc;
+ }
+ }
+ }
+ }
+ }
+ }
+ *length = len;
+ DBG (2, "sane_read: read %d bytes\n", len);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (2, "sane_cancel: handle = %p\n", handle);
+ pass = 0;
+ if (infile != NULL)
+ {
+ fclose (infile);
+ infile = NULL;
+ }
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
+ non_blocking);
+ if (!infile)
+ {
+ DBG (1, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
+ fd ? "!=" : "=");
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/qcam.c b/backend/qcam.c
new file mode 100644
index 0000000..0148bea
--- /dev/null
+++ b/backend/qcam.c
@@ -0,0 +1,2264 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for the Connectix QuickCam. At
+ present, only the color camera is supported though the driver
+ should be able to easily accommodate black and white cameras.
+
+ Portions of this code are derived from Scott Laird's qcam driver.
+ It's copyright notice is reproduced here:
+
+ Copyright (C) 1996 by Scott Laird
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifdef _AIX
+# include "lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "lalloca.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+
+#define BACKEND_NAME qcam
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define QCAM_CONFIG_FILE "qcam.conf"
+
+#include "qcam.h"
+
+/* status bits */
+#define NeedRamTable (1 << 1)
+#define BlackBalanceInProgress (1 << 6)
+#define CameraNotReady (1 << 7)
+
+/* lpdata bits: */
+#define Cmd0_7 0xff
+#define CamRdy2 ( 1 << 0) /* byte mode */
+#define Data0_6 (0x7f << 1) /* byte mode */
+
+/* lpstatus bits: */
+#define CamRdy1 ( 1 << 3) /* nibble mode */
+#define Nibble0_3 (0x0f << 4) /* nibble mode */
+#define Data7_11 (0x1f << 3) /* byte mode */
+
+/* lpcontrol bits: */
+#define Strobe ( 1 << 0) /* unused */
+#define Autofeed ( 1 << 1)
+#define Reset_N ( 1 << 2)
+#define PCAck ( 1 << 3)
+#define BiDir ( 1 << 5)
+
+static int num_devices;
+static QC_Device *first_dev;
+static QC_Scanner *first_handle;
+
+static const SANE_String_Const resolution_list[] = {
+ "Low", /* million-mode */
+ "High", /* billion-mode */
+ 0
+};
+
+static const SANE_Int mono_depth_list[] = {
+ 2, /* # of elements */
+ 4, 6
+};
+
+static const SANE_Int color_depth_list[] = {
+ /*2 */ 1,
+ /* # of elements */
+ /*16, */ 24
+ /* "thousand" mode not implemented yet */
+};
+
+static const SANE_Int xfer_scale_list[] = {
+ 3, /* # of elements */
+ 1, 2, 4
+};
+
+static const SANE_Range u8_range = {
+ /* min, max, quantization */
+ 0, 255, 0
+};
+
+static const SANE_Range brightness_range = {
+ /* min, max, quantization */
+ 0, 254, 0 /* 255 is bulb mode! */
+};
+
+static const SANE_Range x_range[] = {
+ /* min, max, quantization */
+ {0, 338, 2}, /* million mode */
+ {0, 676, 4}, /* billion mode */
+};
+
+static const SANE_Range odd_x_range[] = {
+ /* min, max, quantization */
+ {1, 339, 2}, /* million mode */
+ {3, 683, 4}, /* billion mode */
+};
+
+static const SANE_Range y_range[] = {
+ /* min, max, quantization */
+ {0, 249, 1}, /* million mode */
+ {0, 498, 2}, /* billion mode */
+};
+
+static const SANE_Range odd_y_range[] = {
+ /* min, max, quantization */
+ {0, 249, 1}, /* million mode */
+ {1, 499, 2}, /* billion mode */
+};
+
+static const SANE_Range bw_x_range = { 0, 334, 2 };
+static const SANE_Range odd_bw_x_range = { 1, 335, 2 };
+static const SANE_Range bw_y_range = { 0, 241, 1 };
+static const SANE_Range odd_bw_y_range = { 1, 242, 1 };
+
+#if defined(HAVE_SYS_IO_H) || defined(HAVE_ASM_IO_H) || defined (HAVE_SYS_HW_H)
+
+#ifdef HAVE_SYS_IO_H
+# include <sys/io.h> /* GNU libc based OS */
+#elif HAVE_ASM_IO_H
+# include <asm/io.h> /* older Linux */
+#elif HAVE_SYS_HW_H
+# include <sys/hw.h> /* OS/2 */
+#endif
+
+#endif /* <sys/io.h> || <asm/io.h> || <sys/hw.h> */
+
+#define read_lpdata(d) inb ((d)->port)
+#define read_lpstatus(d) inb ((d)->port + 1)
+#define read_lpcontrol(d) inb ((d)->port + 2)
+#define write_lpdata(d,v) outb ((v), (d)->port)
+#define write_lpcontrol(d,v) outb ((v), (d)->port + 2)
+
+
+static SANE_Status
+enable_ports (QC_Device * q)
+{
+ /* better safe than sorry */
+ if (q->port < 0x278 || q->port > 0x3bc)
+ return SANE_STATUS_INVAL;
+
+ if (ioperm (q->port, 3, 1) < 0)
+ return SANE_STATUS_INVAL;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+disable_ports (QC_Device * q)
+{
+ if (ioperm (q->port, 3, 0) < 0)
+ return SANE_STATUS_INVAL;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* We need a short delay loop -- somthing well under a millisecond.
+ Unfortunately, adding 2 usleep(1)'s to qc_command slowed it down by
+ a factor of over 1000 over the same loop with 2 usleep(0)'s, and
+ that's too slow -- qc_start was taking over a second to run. This
+ seems to help, but if anyone has a good speed-independent pause
+ routine, please tell me. -- Scott
+
+ If you're worried about hogging the CPU: don't worry, the qcam
+ interface leaves you no choice, so this doesn't make the situation
+ any worse... */
+
+static int
+qc_wait (QC_Device * q)
+{
+ return read_lpstatus (q);
+}
+
+
+/* This function uses POSIX fcntl-style locking on a file created in
+ the /tmp directory. Because it uses the Unix record locking
+ facility, locks are relinquished automatically on process
+ termination, so "dead locks" are not a problem. (FYI, the lock
+ file will remain after process termination, but this is actually
+ desired so that the next process need not re-creat(2)e it... just
+ lock it.) The wait argument indicates whether or not this funciton
+ should "block" waiting for the previous lock to be relinquished.
+ This is ideal so that multiple processes (eg. qcam) taking
+ "snapshots" can peacefully coexist.
+
+ -- Dave Plonka (plonka@carroll1.cc.edu) */
+static SANE_Status
+qc_lock_wait (QC_Device * q, int wait)
+{
+#ifdef F_SETLK
+
+#ifndef HAVE_STRUCT_FLOCK
+ struct flock
+ {
+ off_t l_start;
+ off_t l_len;
+ pid_t l_pid;
+ short l_type;
+ short l_whence;
+ };
+#endif /* !HAVE_STRUCT_FLOCK */
+ struct flock sfl;
+#endif
+
+ DBG (3, "qc_lock_wait: acquiring lock for 0x%x\n", q->port);
+
+#ifdef F_SETLK
+ memset (&sfl, 0, sizeof (sfl));
+#endif
+
+ if (q->lock_fd < 0)
+ {
+ char lockfile[128];
+
+ sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
+ q->lock_fd = open (lockfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (q->lock_fd < 0)
+ {
+ DBG (1, "qc_lock_wait: failed to open %s (%s)\n",
+ lockfile, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ }
+
+#ifdef F_SETLK
+ sfl.l_type = F_WRLCK;
+ if (fcntl (q->lock_fd, wait ? F_SETLKW : F_SETLK, &sfl) != 0)
+ {
+ DBG (1, "qc_lock_wait: failed to acquire lock (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+#endif
+
+ DBG (3, "qc_lock_wait: got lock for 0x%x\n", q->port);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+qc_unlock (QC_Device * q)
+{
+ SANE_Status status;
+ char lockfile[128];
+#ifdef F_SETLK
+#ifndef HAVE_STRUCT_FLOCK
+ struct flock
+ {
+ off_t l_start;
+ off_t l_len;
+ pid_t l_pid;
+ short l_type;
+ short l_whence;
+ };
+#endif /* !HAVE_STRUCT_FLOCK */
+ struct flock sfl;
+#endif
+
+ DBG (3, "qc_unlock: releasing lock for 0x%x\n", q->port);
+
+#ifdef F_SETLK
+ memset (&sfl, 0, sizeof (sfl));
+#endif
+ if (q->lock_fd < 0)
+ {
+ DBG (3, "qc_unlock; port was not locked\n");
+ return SANE_STATUS_INVAL;
+ }
+ /* clear the exclusive lock */
+
+#ifdef F_SETLK
+ sfl.l_type = F_UNLCK;
+
+ if (fcntl (q->lock_fd, F_SETLK, &sfl) != 0)
+ {
+ DBG (3, "qc_unlock: failed to release lock (%s)\n", strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
+ DBG (1, "qc_unlock: /tmp/LOCK.qcam.0x%x\n", q->port);
+ unlink (lockfile);
+ close (q->lock_fd);
+ q->lock_fd = -1;
+ DBG (1, "qc_unlock: exit\n");
+ status = SANE_STATUS_GOOD;
+ return status;
+}
+
+static SANE_Status
+qc_lock (QC_Device * q)
+{
+ return qc_lock_wait (q, 1);
+}
+
+/* Busy-waits for a handshake signal from the QuickCam. Almost all
+ communication with the camera requires handshaking. This is why
+ qcam is a CPU hog. */
+static int
+qc_waithand (QC_Device * q, int val)
+{
+ int status;
+
+ while (((status = read_lpstatus (q)) & CamRdy1) != val);
+ return status;
+}
+
+/* This is used when the qcam is in bidirectional ("byte") mode, and
+ the handshaking signal is CamRdy2 (bit 0 of data reg) instead of
+ CamRdy1 (bit 3 of status register). It also returns the last value
+ read, since this data is useful. */
+static unsigned int
+qc_waithand2 (QC_Device * q, int val)
+{
+ unsigned int status;
+
+ do
+ {
+ status = read_lpdata (q);
+ }
+ while ((status & CamRdy2) != (unsigned int) val);
+ return status;
+}
+
+static unsigned int
+qc_send (QC_Device * q, unsigned int byte)
+{
+ unsigned int echo;
+ int n1, n2;
+
+ write_lpdata (q, byte);
+ qc_wait (q);
+ write_lpcontrol (q, Autofeed | Reset_N);
+ qc_wait (q);
+
+ n1 = qc_waithand (q, CamRdy1);
+
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck);
+ qc_wait (q);
+ n2 = qc_waithand (q, 0);
+
+ echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+#ifndef NDEBUG
+ if (echo != byte)
+ {
+ DBG (1, "qc_send: sent 0x%02x, camera echoed 0x%02x\n", byte, echo);
+ n2 = read_lpstatus (q);
+ echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ if (echo != byte)
+ DBG (1, "qc_send: (re-read does not help)\n");
+ else
+ DBG (1, "qc_send: (fixed on re-read)\n");
+ }
+#endif
+ return echo;
+}
+
+static int
+qc_readparam (QC_Device * q)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpcontrol (q, Autofeed | Reset_N); /* clear PCAck */
+ n1 = qc_waithand (q, CamRdy1);
+
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck); /* set PCAck */
+ n2 = qc_waithand (q, 0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ return cmd;
+}
+
+static unsigned int
+qc_getstatus (QC_Device * q)
+{
+ unsigned int status;
+
+ qc_send (q, QC_SEND_STATUS);
+ status = qc_readparam (q);
+ DBG (3, "qc_getstatus: status=0x%02x\n", status);
+ return status;
+}
+
+static void
+qc_setscanmode (QC_Scanner * s, u_int * modep)
+{
+ QC_Device *q = s->hw;
+ u_int mode = 0;
+
+ if (q->version != QC_COLOR)
+ {
+ switch (s->val[OPT_XFER_SCALE].w)
+ {
+ case 1:
+ mode = 0;
+ break;
+ case 2:
+ mode = 4;
+ break;
+ case 4:
+ mode = 8;
+ break;
+ }
+ switch (s->val[OPT_DEPTH].w)
+ {
+ case 4:
+ break;
+ case 6:
+ mode += 2;
+ break;
+ }
+ }
+ else
+ {
+ switch (s->val[OPT_XFER_SCALE].w)
+ {
+ case 1:
+ mode = 0;
+ break;
+ case 2:
+ mode = 2;
+ break;
+ case 4:
+ mode = 4;
+ break;
+ }
+ if (s->resolution == QC_RES_LOW)
+ mode |= 0x18; /* millions mode */
+ else
+ mode |= 0x10; /* billions mode */
+ }
+ if (s->val[OPT_TEST].w)
+ mode |= 0x40; /* test mode */
+
+ if (q->port_mode == QC_BIDIR)
+ mode |= 1;
+
+ DBG (2, "scanmode (before increment): 0x%x\n", mode);
+
+ if (q->version == QC_COLOR)
+ ++mode;
+
+ *modep = mode;
+}
+
+/* Read data bytes from the camera. The number of bytes read is
+ returned as the function result. Depending on the mode, it may be
+ either 1, 3 or 6. On failure, 0 is returned. If buffer is 0, the
+ internal state-machine is reset. */
+static size_t
+qc_readbytes (QC_Scanner * s, unsigned char buffer[])
+{
+ QC_Device *q = s->hw;
+ unsigned int hi, lo;
+ unsigned int hi2, lo2;
+ size_t bytes = 0;
+
+ if (!buffer)
+ {
+ s->readbytes_state = 0;
+ return 0;
+ }
+
+ switch (q->port_mode)
+ {
+ case QC_BIDIR:
+ /* bi-directional port */
+
+ /* read off 24 bits: */
+ write_lpcontrol (q, Autofeed | Reset_N | BiDir);
+ lo = qc_waithand2 (q, 1) >> 1;
+ hi = (read_lpstatus (q) >> 3) & 0x1f;
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
+ lo2 = qc_waithand2 (q, 0) >> 1;
+ hi2 = (read_lpstatus (q) >> 3) & 0x1f;
+ if (q->version == QC_COLOR)
+ {
+ /* is Nibble3 inverted for color quickcams only? */
+ hi ^= 0x10;
+ hi2 ^= 0x10;
+ }
+ switch (s->val[OPT_DEPTH].w)
+ {
+ case 4:
+ buffer[0] = lo & 0xf;
+ buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
+ buffer[2] = (hi & 0x1e) >> 1;
+ buffer[3] = lo2 & 0xf;
+ buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
+ buffer[5] = (hi2 & 0x1e) >> 1;
+ bytes = 6;
+ break;
+
+ case 6:
+ buffer[0] = lo & 0x3f;
+ buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
+ buffer[2] = lo2 & 0x3f;
+ buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
+ bytes = 4;
+ break;
+
+ case 24:
+ buffer[0] = lo | ((hi & 0x1) << 7);
+ buffer[1] = ((hi2 & 0x1e) >> 1) | ((hi & 0x1e) << 3);
+ buffer[2] = lo2 | ((hi2 & 0x1) << 7);
+ bytes = 3;
+ break;
+ }
+ break;
+
+ case QC_UNIDIR: /* Unidirectional Port */
+ write_lpcontrol (q, Autofeed | Reset_N);
+ lo = (qc_waithand (q, CamRdy1) & 0xf0) >> 4;
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck);
+ hi = (qc_waithand (q, 0) & 0xf0) >> 4;
+
+ if (q->version == QC_COLOR)
+ {
+ /* invert Nibble3 */
+ hi ^= 8;
+ lo ^= 8;
+ }
+
+ switch (s->val[OPT_DEPTH].w)
+ {
+ case 4:
+ buffer[0] = lo;
+ buffer[1] = hi;
+ bytes = 2;
+ break;
+
+ case 6:
+ switch (s->readbytes_state)
+ {
+ case 0:
+ buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
+ s->saved_bits = (hi & 3) << 4;
+ s->readbytes_state = 1;
+ bytes = 1;
+ break;
+
+ case 1:
+ buffer[0] = lo | s->saved_bits;
+ s->saved_bits = hi << 2;
+ s->readbytes_state = 2;
+ bytes = 1;
+ break;
+
+ case 2:
+ buffer[0] = ((lo & 0xc) >> 2) | s->saved_bits;
+ buffer[1] = ((lo & 3) << 4) | hi;
+ s->readbytes_state = 0;
+ bytes = 2;
+ break;
+
+ default:
+ DBG (1, "qc_readbytes: bad unidir 6-bit stat %d\n",
+ s->readbytes_state);
+ break;
+ }
+ break;
+
+ case 24:
+ buffer[0] = (lo << 4) | hi;
+ bytes = 1;
+ break;
+
+ default:
+ DBG (1, "qc_readbytes: bad unidir bit depth %d\n",
+ s->val[OPT_DEPTH].w);
+ break;
+ }
+ break;
+
+ default:
+ DBG (1, "qc_readbytes: bad port_mode %d\n", q->port_mode);
+ break;
+ }
+ return bytes;
+}
+
+static void
+qc_reset (QC_Device * q)
+{
+ write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
+ qc_wait (q);
+ write_lpcontrol (q, Strobe | Autofeed | PCAck);
+ qc_wait (q);
+ write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
+}
+
+/* This function is executed as a child process. The reason this is
+ executed as a subprocess is because the qcam interface directly reads
+ off of a I/O port (rather than a filedescriptor). Thus, to have
+ something to select() on, we transfer the data through a pipe.
+
+ WARNING: Since this is executed as a subprocess, it's NOT possible
+ to update any of the variables in the main process (in particular
+ the scanner state cannot be updated). */
+
+static jmp_buf env;
+
+static void
+sighandler (int signal)
+{
+ DBG (3, "sighandler: got signal %d\n", signal);
+ longjmp (env, 1);
+}
+
+/* Original despeckling code by Patrick Reynolds <patrickr@virginia.edu> */
+
+static void
+despeckle (int width, int height, SANE_Byte * in, SANE_Byte * out)
+{
+ long x, i;
+ /* The light-check threshold. Higher numbers remove more lights but
+ blur the image more. 30 is good for indoor lighting. */
+# define NO_LIGHTS 30
+
+ /* macros to make the code a little more readable, p=previous, n=next */
+# define R in[i*3]
+# define G in[i*3+1]
+# define B in[i*3+2]
+# define pR in[i*3-3]
+# define pG in[i*3-2]
+# define pB in[i*3-1]
+# define nR in[i*3+3]
+# define nG in[i*3+4]
+# define nB in[i*3+5]
+
+ DBG (1, "despeckle: width=%d, height=%d\n", width, height);
+
+ for (x = i = 0; i < width * height; ++i)
+ {
+ if (x == 0 || x == width - 1)
+ memcpy (&out[i * 3], &in[i * 3], 3);
+ else
+ {
+ if (R - (G + B) / 2 >
+ NO_LIGHTS + ((pR - (pG + pB) / 2) + (nR - (nG + nB) / 2)))
+ out[i * 3] = (pR + nR) / 2;
+ else
+ out[i * 3] = R;
+
+ if (G - (R + B) / 2 >
+ NO_LIGHTS + ((pG - (pR + pB) / 2) + (nG - (nR + nB) / 2)))
+ out[i * 3 + 1] = (pG + nG) / 2;
+ else
+ out[i * 3 + 1] = G;
+
+ if (B - (G + R) / 2 >
+ NO_LIGHTS + ((pB - (pG + pR) / 2) + (nB - (nG + nR) / 2)))
+ out[i * 3 + 2] = (pB + nB) / 2;
+ else
+ out[i * 3 + 2] = B;
+ }
+ if (++x >= width)
+ x = 0;
+ }
+# undef R
+# undef G
+# undef B
+# undef pR
+# undef pG
+# undef pB
+# undef nR
+# undef nG
+# undef nB
+}
+
+static void
+despeckle32 (int width, int height, SANE_Byte * in, SANE_Byte * out)
+{
+ long x, i;
+ /* macros to make the code a little more readable, p=previous, n=next */
+# define B in[i*4]
+# define Ga in[i*4 + 1]
+# define Gb in[i*4 + 1] /* ignore Gb and use Ga instead---Gb is weird */
+# define R in[i*4 + 3]
+# define pB in[i*4 - 4]
+# define pGa in[i*4 - 3]
+# define pGb in[i*4 - 1] /* ignore Gb and use Ga instead---Gb is weird */
+# define pR in[i*4 - 1]
+# define nB in[i*4 + 4]
+# define nGa in[i*4 + 5]
+# define nGb in[i*4 + 5] /* ignore Gb and use Ga instead---Gb is weird */
+# define nR in[i*4 + 7]
+
+ DBG (1, "despeckle32: width=%d, height=%d\n", width, height);
+
+ for (x = i = 0; i < width * height; ++i)
+ {
+ if (x == 0 || x == width - 1)
+ memcpy (&out[i * 4], &in[i * 4], 4);
+ else
+ {
+ if (x >= width - 2)
+ /* the last red pixel seems to be black at all times, use
+ R instead: */
+ nR = R;
+
+ if (R - ((Ga + Gb) / 2 + B) / 2 >
+ NO_LIGHTS + ((pR - ((pGa + pGb) / 2 + pB) / 2) +
+ (nR - ((nGa + nGb) / 2 + nB) / 2)))
+ out[i * 4 + 3] = (pR + nR) / 2;
+ else
+ out[i * 4 + 3] = R;
+
+ if (Ga - (R + B) / 2 > NO_LIGHTS + ((pGa - (pR + pB) / 2) +
+ (nGa - (nR + nB) / 2)))
+ out[i * 4 + 1] = (pGa + nGa) / 2;
+ else
+ out[i * 4 + 1] = Ga;
+
+ if (Gb - (R + B) / 2 > NO_LIGHTS + ((pGb - (pR + pB) / 2) +
+ (nGb - (nR + nB) / 2)))
+ out[i * 4 + 2] = (pGb + nGb) / 2;
+ else
+ out[i * 4 + 2] = Gb;
+
+ if (B - ((Ga + Gb) / 2 + R) / 2 >
+ NO_LIGHTS + ((pB - ((pGa + pGb) / 2 + pR) / 2) +
+ (nB - ((nGa + nGb) / 2 + nR) / 2)))
+ out[i * 4 + 0] = (pB + nB) / 2;
+ else
+ out[i * 4 + 0] = B;
+ }
+ if (++x >= width)
+ x = 0;
+ }
+# undef R
+# undef Ga
+# undef Gb
+# undef B
+# undef pR
+# undef pGa
+# undef pGb
+# undef pB
+# undef nR
+# undef nGa
+# undef nGb
+# undef nB
+}
+
+static int
+reader_process (QC_Scanner * s, int in_fd, int out_fd)
+{
+ static SANE_Byte *buffer = 0, *extra = 0;
+ static size_t buffer_size = 0;
+ size_t count, len, num_bytes;
+ QC_Device *q = s->hw;
+ QC_Scan_Request req;
+ int width, height;
+ SANE_Byte *src;
+ FILE *ofp;
+
+ DBG (5, "reader_process: enter\n");
+
+ enable_ports (q);
+
+ ofp = fdopen (out_fd, "w");
+ if (!ofp)
+ return 1;
+
+ while (1)
+ {
+ if (setjmp (env))
+ {
+ char ch;
+
+ /* acknowledge the signal: */
+ DBG (1, "reader_process: sending signal ACK\n");
+ fwrite (&ch, 1, 1, ofp);
+ fflush (ofp); /* force everything out the pipe */
+ continue;
+ }
+ signal (SIGINT, sighandler);
+
+ /* the main process gets us started by writing a size_t giving
+ the number of bytes we should expect: */
+ if (read (in_fd, &req, sizeof (req)) != sizeof (req))
+ {
+ perror ("read");
+ return 1;
+ }
+ num_bytes = req.num_bytes;
+
+ DBG (3, "reader_process: got request for %lu bytes\n",
+ (u_long) num_bytes);
+
+ /* Don't do this in sane_start() since there may be a long
+ timespan between it and the first sane_read(), which would
+ result in poor images. */
+ qc_send (q, QC_SEND_VIDEO_FRAME);
+ qc_send (q, req.mode);
+
+ if (req.despeckle
+ && (!extra || buffer_size < num_bytes
+ || buffer_size >= 2 * num_bytes))
+ {
+ if (extra)
+ extra = realloc (extra, num_bytes);
+ else
+ extra = malloc (num_bytes);
+ if (!extra)
+ {
+ DBG (1, "reader_process: malloc(%ld) failed\n",
+ (long) num_bytes);
+ exit (1);
+ }
+ }
+
+ if (buffer_size < num_bytes || buffer_size >= 2 * num_bytes)
+ {
+ if (buffer)
+ buffer = realloc (buffer, num_bytes);
+ else
+ buffer = malloc (num_bytes);
+ if (!buffer)
+ {
+ DBG (1, "reader_process: malloc(%ld) failed\n",
+ (long) num_bytes);
+ exit (1);
+ }
+ buffer_size = num_bytes;
+ }
+
+ if (q->port_mode == QC_BIDIR)
+ {
+ /* turn port into input port */
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
+ usleep (3);
+ write_lpcontrol (q, Autofeed | Reset_N | BiDir);
+ qc_waithand (q, CamRdy1);
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
+ qc_waithand (q, 0);
+ }
+
+ if (q->version == QC_COLOR)
+ for (len = 0; len < num_bytes; len += count)
+ count = qc_readbytes (s, buffer + len);
+ else
+ {
+ /* strange -- should be 15:63 below, but 4bpp is odd */
+ int shift, invert;
+ unsigned int i;
+ u_char val;
+
+ switch (s->val[OPT_DEPTH].w)
+ {
+ case 4:
+ invert = 16;
+ shift = 4;
+ break;
+
+ case 6:
+ invert = 63;
+ shift = 2;
+ break;
+
+ default:
+ DBG (1, "reader_process: unexpected depth %d\n",
+ s->val[OPT_DEPTH].w);
+ return 1;
+ }
+
+ for (len = 0; len < num_bytes; len += count)
+ {
+ count = qc_readbytes (s, buffer + len);
+ for (i = 0; i < count; ++i)
+ {
+ /* 4bpp is odd (again) -- inverter is 16, not 15,
+ but output must be 0-15 */
+ val = buffer[len + i];
+ if (val > 0 || invert != 16)
+ val = invert - val;
+ buffer[len + i] = (val << shift) | (val >> (8 - 2 * shift));
+ }
+ }
+ qc_readbytes (s, 0); /* reset state machine */
+ }
+ /* we're done reading this frame: */
+ DBG (2, "reader_process: frame complete\n");
+
+ if (q->port_mode == QC_BIDIR)
+ {
+ /* return port to output mode */
+ write_lpcontrol (q, Autofeed);
+ usleep (3);
+ write_lpcontrol (q, Autofeed | Reset_N);
+ usleep (3);
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck);
+ }
+
+ if (req.resolution == QC_RES_HIGH)
+ {
+ SANE_Byte buf[6];
+ int x, y;
+
+ /* in billions mode, we need to oversample the data: */
+ src = buffer;
+ width = req.params.pixels_per_line;
+ height = req.params.lines;
+
+ if (req.despeckle)
+ {
+ despeckle32 (width / 2, req.params.lines / 2, buffer, extra);
+ src = extra;
+ }
+
+ assert (!(width & 1)); /* width must be even */
+
+ for (y = 0; y < height; ++y)
+ {
+ /* even line */
+
+ for (x = 0; x < width; x += 2)
+ {
+ int red1, green1, blue1, green2, blue2;
+
+ blue1 = src[0];
+ green1 = src[1];
+ red1 = src[3];
+ if (x >= width - 2)
+ {
+ red1 = src[-1]; /* last red seems to be missing */
+ blue2 = blue1;
+ green2 = green1;
+ }
+ else
+ {
+ blue2 = src[4];
+ green2 = src[5];
+ }
+ src += 4;
+
+ buf[0] = red1;
+ buf[1] = green1;
+ buf[2] = blue1;
+ buf[3] = red1;
+ buf[4] = green2;
+ buf[5] = blue2;
+ if (fwrite (buf, 1, 6, ofp) != 6)
+ {
+ perror ("fwrite: short write");
+ return 1;
+ }
+ }
+ if (++y >= height)
+ break;
+
+ src -= 2 * width; /* 4 bytes/pixel -> 2 pixels of 3 bytes each */
+
+ /* odd line */
+ for (x = 0; x < width; x += 2)
+ {
+ int red1, green3, blue3, green4, blue4;
+ int yoff;
+
+ if (x >= width - 2)
+ red1 = src[-1]; /* last red seems to be missing */
+ else
+ red1 = src[3];
+ yoff = 2 * width;
+ if (y >= height - 1)
+ yoff = 0;
+ green3 = src[yoff + 1];
+ blue3 = src[yoff + 0];
+ if (x >= width - 2)
+ {
+ blue4 = blue3;
+ green4 = green3;
+ }
+ else
+ {
+ blue4 = src[yoff + 4];
+ green4 = src[yoff + 5];
+ }
+ src += 4;
+
+ buf[0] = red1;
+ buf[1] = green3;
+ buf[2] = blue3;
+ buf[3] = red1;
+ buf[4] = green4;
+ buf[5] = blue4;
+ if (fwrite (buf, 1, 6, ofp) != 6)
+ {
+ perror ("fwrite: short write");
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ src = buffer;
+ if (req.despeckle)
+ {
+ despeckle (req.params.pixels_per_line, req.params.lines,
+ buffer, extra);
+ src = extra;
+ }
+
+ /* now write the whole thing to the main process: */
+ if (fwrite (src, 1, num_bytes, ofp) != num_bytes)
+ {
+ perror ("fwrite: short write");
+ return 1;
+ }
+ }
+ fflush (ofp);
+ }
+ assert (SANE_FALSE); /* not reached */
+ DBG (5, "reader_process: exit\n");
+ return 1;
+}
+
+static SANE_Status
+attach (const char *devname, QC_Device ** devp)
+{
+ int i, n1, n2, s1, s2, cmd, port, force_unidir;
+ SANE_Status result, status;
+ QC_Device *q;
+ char *endp;
+
+ DBG (3, "attach: enter\n");
+ errno = 0;
+ force_unidir = 0;
+ if (devname[0] == 'u')
+ {
+ force_unidir = 1;
+ ++devname;
+ }
+ port = strtol (devname, &endp, 0);
+ if (endp == devname || errno == ERANGE)
+ {
+ DBG (1, "attach: invalid port address `%s'\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (q = first_dev; q; q = q->next)
+ if (port == q->port)
+ {
+ if (devp)
+ *devp = q;
+ return SANE_STATUS_GOOD;
+ }
+
+ q = malloc (sizeof (*q));
+ if (!q)
+ return SANE_STATUS_NO_MEM;
+
+ memset (q, 0, sizeof (*q));
+ q->port = port;
+ q->lock_fd = -1;
+
+ result = enable_ports (q);
+ if (result != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: cannot enable ports (%s)\n", strerror (errno));
+ free (q);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* lock camera while we determine its version: */
+ qc_lock (q);
+
+ qc_reset (q);
+
+ write_lpdata (q, QC_SEND_VERSION);
+ qc_wait (q);
+ write_lpcontrol (q, Autofeed | Reset_N); /* make PCAck inactive */
+ qc_wait (q);
+
+ for (i = 0; (i < 1000) && !(s1 = (n1 = read_lpstatus (q)) & CamRdy1); i++);
+ if (!s1)
+ {
+ DBG (2, "attach: failed to get CamRdy1 at port 0x%x\n", q->port);
+ goto unlock_and_fail;
+ }
+
+ write_lpcontrol (q, Autofeed | Reset_N | PCAck);
+ qc_wait (q);
+
+ for (i = 0; (i < 1000) && (s2 = (n2 = read_lpstatus (q)) & CamRdy1); i++);
+ if (s2)
+ {
+ DBG (2, "attach: CamRdy1 failed to clear at port 0x%x\n", q->port);
+ goto unlock_and_fail;
+ }
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+
+ if (cmd != QC_SEND_VERSION)
+ {
+ DBG (2, "attach: got 0x%02x instead of 0x%02x\n", cmd, QC_SEND_VERSION);
+ goto unlock_and_fail;
+ }
+
+ q->version = qc_readparam (q);
+ DBG (1, "attach: found QuickCam version 0x%02x\n", q->version);
+
+ q->port_mode = QC_UNIDIR;
+ if (!force_unidir)
+ {
+ write_lpcontrol (q, BiDir);
+ write_lpdata (q, 0x75);
+ if (read_lpdata (q) != 0x75)
+ q->port_mode = QC_BIDIR;
+ }
+
+ /* For some reason the color quickcam needs two set-black commands
+ after a reset. Thus, we now set the black-level to some
+ reasonable value (0) so that the next set-black level command
+ will really go through. */
+ if (q->version == QC_COLOR)
+ {
+ qc_send (q, QC_SET_BLACK);
+ qc_send (q, 0);
+
+ DBG (3, "attach: resetting black_level\n");
+
+ /* wait for set black level command to finish: */
+ while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
+ usleep (10000);
+ }
+
+ status = qc_unlock (q);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: status qc_unlock NOK\n");
+ /* status = SANE_STATUS_GOOD; */
+ }
+ q->sane.name = strdup (devname);
+ q->sane.vendor = "Connectix";
+ q->sane.model =
+ (q->version == QC_COLOR) ? "Color QuickCam" : "B&W QuickCam";
+ q->sane.type = "video camera";
+
+ ++num_devices;
+ q->next = first_dev;
+ first_dev = q;
+
+ if (devp)
+ *devp = q;
+ DBG (3, "attach: exit status OK\n");
+ status = SANE_STATUS_GOOD;
+ return status;
+
+
+unlock_and_fail:
+ status = qc_unlock (q);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: unlock_and_fail status qc_unlock NOK\n");
+ }
+ free (q);
+ DBG (3, "attach: exit status NOK\n");
+ status = SANE_STATUS_INVAL;
+ return status;
+}
+
+static SANE_Status
+init_options (QC_Scanner * s)
+{
+ int i;
+
+ DBG (3, "init_options: enter\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT);
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_RESOLUTION].size = 5; /* sizeof("High") */
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_NONE;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_RESOLUTION].constraint.string_list = resolution_list;
+ s->val[OPT_RESOLUTION].s = strdup (resolution_list[QC_RES_LOW]);
+
+ /* bit-depth */
+ s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_DEPTH].title = "Pixel depth";
+ s->opt[OPT_DEPTH].desc = "Number of bits per pixel.";
+ s->opt[OPT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
+ s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_DEPTH].constraint.word_list = color_depth_list;
+ s->val[OPT_DEPTH].w = color_depth_list[NELEMS (color_depth_list) - 1];
+
+ /* test */
+ s->opt[OPT_TEST].name = "test-image";
+ s->opt[OPT_TEST].title = "Force test image";
+ s->opt[OPT_TEST].desc =
+ "Acquire a test-image instead of the image seen by the camera. "
+ "The test image consists of red, green, and blue squares of "
+ "32x32 pixels each. Use this to find out whether the "
+ "camera is connected properly.";
+ s->opt[OPT_TEST].type = SANE_TYPE_BOOL;
+ s->val[OPT_TEST].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range[QC_RES_LOW];
+ s->val[OPT_TL_X].w = 10;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range[QC_RES_LOW];
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &odd_x_range[QC_RES_LOW];
+ s->val[OPT_BR_X].w = 339;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &odd_y_range[QC_RES_LOW];
+ s->val[OPT_BR_Y].w = 245;
+
+ /* xfer-scale */
+ s->opt[OPT_XFER_SCALE].name = "transfer-scale";
+ s->opt[OPT_XFER_SCALE].title = "Transfer scale";
+ s->opt[OPT_XFER_SCALE].desc =
+ "The transferscale determines how many of the acquired pixels actually "
+ "get sent to the computer. For example, a transfer scale of 2 would "
+ "request that every other acquired pixel would be omitted. That is, "
+ "when scanning a 200 pixel wide and 100 pixel tall area, the resulting "
+ "image would be only 100x50 pixels large. Using a large transfer scale "
+ "improves acquisition speed, but reduces resolution.";
+ s->opt[OPT_XFER_SCALE].type = SANE_TYPE_INT;
+ s->opt[OPT_XFER_SCALE].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_XFER_SCALE].constraint.word_list = xfer_scale_list;
+ s->val[OPT_XFER_SCALE].w = xfer_scale_list[1];
+
+ /* despeckle */
+ s->opt[OPT_DESPECKLE].name = "despeckle";
+ s->opt[OPT_DESPECKLE].title = "Speckle filter";
+ s->opt[OPT_DESPECKLE].desc = "Turning on this filter will remove the "
+ "christmas lights that are typically present in dark images.";
+ s->opt[OPT_DESPECKLE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DESPECKLE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DESPECKLE].w = 0;
+
+
+ /* "Enhancement" group: */
+
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
+ " In a conventional camera, this control corresponds to the "
+ "exposure time.";
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_AUTOMATIC;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ s->val[OPT_BRIGHTNESS].w = 135;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &u8_range;
+ s->val[OPT_CONTRAST].w = 104;
+
+ /* black-level */
+ s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL
+ " This value should be selected so that black areas just start "
+ "to look really black (not gray).";
+ s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BLACK_LEVEL].constraint.range = &u8_range;
+ s->val[OPT_BLACK_LEVEL].w = 0;
+
+ /* white-level */
+ s->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
+ s->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
+ s->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL
+ " This value should be selected so that white areas just start "
+ "to look really white (not gray).";
+ s->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_WHITE_LEVEL].constraint.range = &u8_range;
+ s->val[OPT_WHITE_LEVEL].w = 150;
+
+ /* hue */
+ s->opt[OPT_HUE].name = SANE_NAME_HUE;
+ s->opt[OPT_HUE].title = SANE_TITLE_HUE;
+ s->opt[OPT_HUE].desc = SANE_DESC_HUE;
+ s->opt[OPT_HUE].type = SANE_TYPE_INT;
+ s->opt[OPT_HUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_HUE].constraint.range = &u8_range;
+ s->val[OPT_HUE].w = 128;
+
+ /* saturation */
+ s->opt[OPT_SATURATION].name = SANE_NAME_SATURATION;
+ s->opt[OPT_SATURATION].title = SANE_TITLE_SATURATION;
+ s->opt[OPT_SATURATION].desc = SANE_DESC_SATURATION;
+ s->opt[OPT_SATURATION].type = SANE_TYPE_INT;
+ s->opt[OPT_SATURATION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SATURATION].constraint.range = &u8_range;
+ s->val[OPT_SATURATION].w = 100;
+
+ DBG (3, "init_options: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX], *str;
+ size_t len;
+ FILE *fp;
+ authorize = authorize; /* silence compilation warnings */
+
+ DBG_INIT ();
+
+ DBG (1, "sane_init: enter\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (QCAM_CONFIG_FILE);
+ if (!fp)
+ {
+ DBG (1, "sane_init: file `%s' not accessible\n", QCAM_CONFIG_FILE);
+ return SANE_STATUS_INVAL;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ for (str = dev_name; *str && !isspace (*str) && *str != '#'; ++str);
+ *str = '\0';
+
+ attach (dev_name, 0);
+ }
+ fclose (fp);
+
+ DBG (1, "sane_init: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ QC_Device *dev, *next;
+ static const SANE_Device **devlist;
+
+ DBG (5, "sane_exit: enter\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ disable_ports (dev);
+ free (dev);
+ }
+ if (devlist) {
+ free (devlist);
+ devlist = NULL;
+ }
+ DBG (5, "sane_exit: exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ QC_Device *dev;
+ int i;
+
+ DBG (5, "sane_get_devices: enter\n");
+
+ local_only = local_only; /* silence compilation warnings */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (5, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status;
+ QC_Device *dev;
+ QC_Scanner *s;
+
+ DBG (5, "sane_open: enter: (devicename = %s)\n", devicename);
+ if (devicename[0])
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ else
+ /* empty devicname -> use first device */
+ dev = first_dev;
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->user_corner = 0;
+ s->hw = dev;
+ s->value_changed = ~0; /* ensure all options get updated */
+ s->reader_pid = -1;
+ s->to_child = -1;
+ s->from_child = -1;
+ s->read_fd = -1;
+
+ init_options (s);
+
+ /* The contrast option seems to have an effect for b&w cameras only,
+ so don't give the user the impression that this is a useful thing
+ to set... */
+ if (s->hw->version == QC_COLOR)
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ else
+ {
+ /* Black level, Hue and Saturation are things the b&w cameras
+ know nothing about. Despeckle might be useful, but this code
+ seems to work for color cameras only right now. The framesize
+ seems to work better in these ranges. */
+ s->opt[OPT_DESPECKLE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_HUE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SATURATION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_DEPTH].constraint.word_list = mono_depth_list;
+ s->val[OPT_DEPTH].w = mono_depth_list[NELEMS (mono_depth_list) - 1];
+ s->opt[OPT_TL_X].constraint.range = &bw_x_range;
+ s->val[OPT_TL_X].w = 14;
+ s->opt[OPT_TL_Y].constraint.range = &bw_y_range;
+ s->val[OPT_TL_Y].w = 0;
+ s->opt[OPT_BR_X].constraint.range = &odd_bw_x_range;
+ s->val[OPT_BR_X].w = 333;
+ s->opt[OPT_BR_Y].constraint.range = &odd_bw_y_range;
+ s->val[OPT_BR_Y].w = 239;
+
+ s->val[OPT_BRIGHTNESS].w = 170;
+ s->val[OPT_CONTRAST].w = 150;
+ s->val[OPT_WHITE_LEVEL].w = 150;
+ }
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (5, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ QC_Scanner *prev, *s;
+
+ DBG (5, "sane_close: enter\n");
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_close: bad handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ if (s->scanning)
+ sane_cancel (handle);
+
+ if (s->reader_pid >= 0)
+ {
+ kill (s->reader_pid, SIGTERM);
+ waitpid (s->reader_pid, 0, 0);
+ s->reader_pid = 0;
+ }
+ if (s->to_child >= 0)
+ close (s->to_child);
+ if (s->from_child >= 0)
+ close (s->from_child);
+ if (s->read_fd >= 0)
+ close (s->read_fd);
+
+ free (s);
+
+ DBG (5, "sane_close: exit\n");
+
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ QC_Scanner *s = handle;
+
+ DBG (5, "sane_get_option_descriptor: enter\n");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+
+ DBG (5, "sane_get_option_descriptor: exit\n");
+
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ QC_Scanner *s = handle;
+ QC_Resolution old_res;
+ SANE_Status status;
+ SANE_Word cap;
+ char *old_val;
+ int i;
+
+ DBG (5, "sane_control_option: enter\n");
+
+ if (info)
+ *info = 0;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_DEPTH:
+ case OPT_DESPECKLE:
+ case OPT_TEST:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_XFER_SCALE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_BLACK_LEVEL:
+ case OPT_WHITE_LEVEL:
+ case OPT_HUE:
+ case OPT_SATURATION:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_RESOLUTION:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ default:
+ DBG (1, "control_option: option %d unknown\n", option);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (option >= OPT_TL_X && option <= OPT_BR_Y)
+ s->user_corner |= 1 << (option - OPT_TL_X);
+
+ assert (option <= 31);
+ s->value_changed |= 1 << option;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_XFER_SCALE:
+ case OPT_DEPTH:
+ if (!s->scanning && info && s->val[option].w != *(SANE_Word *) val)
+ /* only signal the reload params if we're not scanning---no point
+ in creating the frontend useless work */
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_TEST:
+ case OPT_DESPECKLE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_BLACK_LEVEL:
+ case OPT_WHITE_LEVEL:
+ case OPT_HUE:
+ case OPT_SATURATION:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* options with side-effects: */
+ case OPT_RESOLUTION:
+ old_val = s->val[OPT_RESOLUTION].s;
+
+ if (strcmp (old_val, val) != 0)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (!s->scanning)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ free (old_val);
+ s->val[OPT_RESOLUTION].s = strdup (val);
+
+ /* low-resolution mode: */
+ old_res = s->resolution;
+ s->resolution = QC_RES_LOW;
+ if (strcmp (val, resolution_list[QC_RES_HIGH]) == 0)
+ /* high-resolution mode: */
+ s->resolution = QC_RES_HIGH;
+ s->opt[OPT_TL_X].constraint.range = &x_range[s->resolution];
+ s->opt[OPT_BR_X].constraint.range = &odd_x_range[s->resolution];
+ s->opt[OPT_TL_Y].constraint.range = &y_range[s->resolution];
+ s->opt[OPT_BR_Y].constraint.range = &odd_y_range[s->resolution];
+
+ if (old_res == QC_RES_LOW && s->resolution == QC_RES_HIGH)
+ {
+ for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
+ s->val[i].w *= 2;
+ s->val[OPT_BR_X].w += 1;
+ s->val[OPT_BR_Y].w += 1;
+ s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (old_res == QC_RES_HIGH && s->resolution == QC_RES_LOW)
+ {
+ for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
+ s->val[i].w /= 2;
+ s->opt[OPT_TEST].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (!(s->user_corner & 0x4))
+ s->val[OPT_BR_X].w = odd_x_range[s->resolution].max;
+ if (!(s->user_corner & 0x8))
+ s->val[OPT_BR_Y].w = odd_y_range[s->resolution].max - 4;
+
+ /* make sure the affected options have valid values: */
+ for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
+ if (s->val[i].w > s->opt[i].constraint.range->max)
+ s->val[i].w = s->opt[i].constraint.range->max;
+
+ DBG (5, "sane_control_option: exit\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_AUTO)
+ {
+ switch (option)
+ {
+ case OPT_BRIGHTNESS:
+ /* not implemented yet */
+ DBG (5, "sane_control_option: exit\n");
+ return SANE_STATUS_GOOD;
+
+ default:
+ break;
+ }
+ }
+
+ DBG (5, "sane_control_option: NOK exit\n");
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ QC_Scanner *s = handle;
+ QC_Device *q = s->hw;
+ int xfer_scale;
+ size_t Bpp = 3; /* # of bytes per pixel */
+
+ DBG (5, "sane_get_parameters: enter\n");
+
+ if (!s->scanning)
+ {
+ /* Only compute new parameters when not scanning---allows
+ changing width/height etc while scan is in progress. */
+ xfer_scale = s->val[OPT_XFER_SCALE].w;
+
+ s->params.format = SANE_FRAME_RGB;
+ if (q->version != QC_COLOR)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ Bpp = 1;
+ }
+ s->params.last_frame = SANE_TRUE;
+
+ s->params.pixels_per_line = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w + 1;
+ s->params.pixels_per_line /= xfer_scale;
+ s->params.pixels_per_line &= ~1UL; /* ensure it's even */
+ if (s->params.pixels_per_line < 2)
+ s->params.pixels_per_line = 2;
+
+ s->params.lines = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w + 1;
+ s->params.lines /= xfer_scale;
+ if (s->params.lines < 1)
+ s->params.lines = 1;
+
+ s->params.bytes_per_line = Bpp * s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ if (params)
+ *params = s->params;
+
+ DBG (5, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int top, left, width, height, undecimated_width, undecimated_height;
+ QC_Scanner *s = handle;
+ QC_Device *q = s->hw;
+ QC_Scan_Request req;
+
+ DBG (5, "sane_start: enter\n");
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (s->reader_pid < 0)
+ {
+ int p2c_pipe[2]; /* parent->child pipe */
+ int c2p_pipe[2]; /* child->parent pipe */
+
+ if (pipe (p2c_pipe) < 0 || pipe (c2p_pipe) < 0)
+ {
+ DBG (3, "start: failed to create pipes\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->reader_pid = fork ();
+ if (s->reader_pid == 0)
+ {
+ /* this is the child */
+ signal (SIGHUP, SIG_DFL);
+ signal (SIGINT, SIG_DFL);
+ signal (SIGPIPE, SIG_DFL);
+ signal (SIGTERM, SIG_DFL);
+ _exit (reader_process (s, p2c_pipe[0], c2p_pipe[1]));
+ }
+ close (p2c_pipe[0]);
+ close (c2p_pipe[1]);
+ s->to_child = p2c_pipe[1];
+ s->from_child = c2p_pipe[0];
+ }
+
+ s->read_fd = dup (s->from_child);
+ sane_get_parameters (s, 0); /* ensure uptodate parameters */
+
+ qc_lock (q);
+ s->holding_lock = SANE_TRUE;
+
+ if (q->version == QC_COLOR)
+ {
+ qc_send (q, QC_SET_SPEED);
+ qc_send (q, 2);
+
+ /* wait for camera to become ready: */
+ while (qc_getstatus (q) & CameraNotReady)
+ usleep (10000);
+
+ /* Only send black_level if necessary; this optimization may
+ fail if two applications access the camera in an interleaved
+ fashion; but the black-level command is slow enough that it
+ cannot be issued for every image acquisition. */
+ if (s->value_changed & (1 << OPT_BLACK_LEVEL))
+ {
+ s->value_changed &= ~(1 << OPT_BLACK_LEVEL);
+
+ qc_send (q, QC_SET_BLACK);
+ qc_send (q, s->val[OPT_BLACK_LEVEL].w);
+
+ DBG (3, "start: black_level=%d\n", s->val[OPT_BLACK_LEVEL].w);
+
+ /* wait for set black level command to finish: */
+ while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
+ usleep (10000);
+ }
+
+ if (s->value_changed & (1 << OPT_HUE))
+ {
+ s->value_changed &= ~(1 << OPT_HUE);
+ qc_send (q, QC_COL_SET_HUE);
+ qc_send (q, s->val[OPT_HUE].w);
+ }
+
+ if (s->value_changed & (1 << OPT_SATURATION))
+ {
+ s->value_changed &= ~(1 << OPT_SATURATION);
+ qc_send (q, QC_SET_SATURATION);
+ qc_send (q, s->val[OPT_SATURATION].w);
+ }
+ }
+
+ if (q->version != QC_COLOR)
+ qc_reset (q);
+
+ if (s->value_changed & (1 << OPT_CONTRAST))
+ {
+ s->value_changed &= ~(1 << OPT_CONTRAST);
+ qc_send (q, ((q->version == QC_COLOR)
+ ? QC_COL_SET_CONTRAST : QC_MONO_SET_CONTRAST));
+ qc_send (q, s->val[OPT_CONTRAST].w);
+ }
+
+ if (s->value_changed & (1 << OPT_BRIGHTNESS))
+ {
+ s->value_changed &= ~(1 << OPT_BRIGHTNESS);
+ qc_send (q, QC_SET_BRIGHTNESS);
+ qc_send (q, s->val[OPT_BRIGHTNESS].w);
+ }
+
+ width = s->params.pixels_per_line;
+ height = s->params.lines;
+ if (s->resolution == QC_RES_HIGH)
+ {
+ width /= 2; /* the expansion occurs through oversampling */
+ height /= 2; /* we acquire only half the lines that we generate */
+ }
+ undecimated_width = width * s->val[OPT_XFER_SCALE].w;
+ undecimated_height = height * s->val[OPT_XFER_SCALE].w;
+
+ s->num_bytes = 0;
+ s->bytes_per_frame = s->params.lines * s->params.bytes_per_line;
+
+ qc_send (q, QC_SET_NUM_V);
+ qc_send (q, undecimated_height);
+
+ if (q->version == QC_COLOR)
+ {
+ qc_send (q, QC_SET_NUM_H);
+ qc_send (q, undecimated_width / 2);
+ }
+ else
+ {
+ int val, val2;
+
+ if (q->port_mode == QC_UNIDIR && s->val[OPT_DEPTH].w == 6)
+ {
+ val = undecimated_width;
+ val2 = s->val[OPT_XFER_SCALE].w * 4;
+ }
+ else
+ {
+ val = undecimated_width * s->val[OPT_DEPTH].w;
+ val2 =
+ ((q->port_mode == QC_BIDIR) ? 24 : 8) * s->val[OPT_XFER_SCALE].w;
+ }
+ val = (val + val2 - 1) / val2;
+ qc_send (q, QC_SET_NUM_H);
+ qc_send (q, val);
+ }
+
+ left = s->val[OPT_TL_X].w / 2;
+ top = s->val[OPT_TL_Y].w;
+ if (s->resolution == QC_RES_HIGH)
+ {
+ left /= 2;
+ top /= 2;
+ }
+
+ DBG (3, "sane_start: top=%d, left=%d, white=%d, bright=%d, contr=%d\n",
+ top, left, s->val[OPT_WHITE_LEVEL].w, s->val[OPT_BRIGHTNESS].w,
+ s->val[OPT_CONTRAST].w);
+
+ qc_send (q, QC_SET_LEFT);
+ qc_send (q, left);
+
+ qc_send (q, QC_SET_TOP);
+ qc_send (q, top + 1); /* not sure why this is so... ;-( */
+
+ if (s->value_changed & (1 << OPT_WHITE_LEVEL))
+ {
+ s->value_changed &= ~(1 << OPT_WHITE_LEVEL);
+ qc_send (q, QC_SET_WHITE);
+ qc_send (q, s->val[OPT_WHITE_LEVEL].w);
+ }
+
+ DBG (2, "start: %s %d lines of %d pixels each (%ld bytes) => %dx%d\n",
+ (q->port_mode == QC_BIDIR) ? "bidir" : "unidir",
+ height, width, (long) s->bytes_per_frame,
+ s->params.pixels_per_line, s->params.lines);
+
+ /* send scan request to reader process: */
+ qc_setscanmode (s, &req.mode);
+ req.num_bytes = width * height;
+ if (q->version == QC_COLOR)
+ {
+ if (s->resolution == QC_RES_LOW)
+ req.num_bytes *= 3;
+ else
+ req.num_bytes *= 4;
+ }
+ req.resolution = s->resolution;
+ req.params = s->params;
+ req.despeckle = s->val[OPT_DESPECKLE].w;
+ write (s->to_child, &req, sizeof (req));
+
+ s->scanning = SANE_TRUE;
+ s->deliver_eof = 0;
+
+ DBG (5, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * lenp)
+{
+ SANE_Status status;
+ QC_Scanner *s = handle;
+ QC_Device *q = s->hw;
+ ssize_t nread;
+ size_t len;
+
+ DBG (5, "sane_read: enter\n");
+
+ *lenp = 0;
+
+ if (s->deliver_eof)
+ {
+ s->deliver_eof = 0;
+ return SANE_STATUS_EOF;
+ }
+
+ if (!s->scanning)
+ return SANE_STATUS_CANCELLED;
+
+ len = max_len;
+ if (s->num_bytes + len > s->bytes_per_frame)
+ len = s->bytes_per_frame - s->num_bytes;
+
+ DBG (8, "read(buf=%p,num_bytes=%ld,max_len=%d,len=%ld)\n",
+ buf, (long) s->num_bytes, max_len, (long) len);
+
+ nread = read (s->read_fd, buf, len);
+ if (nread <= 0)
+ {
+ if (nread == 0 || errno == EAGAIN)
+ {
+ DBG (3, "read: no more data available\n");
+ return SANE_STATUS_GOOD;
+ }
+ DBG (3, "read: short read (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (nread > 0 && s->holding_lock)
+ {
+ status = qc_unlock (q); /* now we can unlock the camera */
+ if (status != SANE_STATUS_GOOD)
+ DBG(3, "sane_read: qc_unlock error\n");
+ s->holding_lock = SANE_FALSE;
+ }
+
+ s->num_bytes += nread;
+ if (s->num_bytes >= s->bytes_per_frame)
+ {
+ s->scanning = SANE_FALSE;
+ close (s->read_fd);
+ s->read_fd = -1;
+ s->deliver_eof = 1;
+ }
+
+ if (lenp)
+ *lenp = nread;
+
+ DBG (5, "sane_read: exit, read got %d bytes\n", *lenp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ QC_Scanner *s = handle;
+ SANE_Bool was_scanning;
+ SANE_Status status;
+
+ DBG (5, "sane_cancel: enter\n");
+
+ was_scanning = s->scanning;
+ s->scanning = SANE_FALSE;
+ s->deliver_eof = 0;
+ if (s->read_fd >= 0)
+ {
+ close (s->read_fd);
+ s->read_fd = -1;
+ }
+
+ if (s->reader_pid >= 0 && was_scanning)
+ {
+ char buf[1024];
+ ssize_t nread;
+ int flags;
+
+ DBG (1, "cancel: cancelling read request\n");
+
+ kill (s->reader_pid, SIGINT); /* tell reader to stop reading */
+
+ /* save non-blocking i/o flags: */
+ flags = fcntl (s->from_child, F_GETFL, 0);
+
+ /* block until we read at least one byte: */
+ read (s->from_child, buf, 1);
+
+ /* put descriptor in non-blocking i/o: */
+ fcntl (s->from_child, F_SETFL, O_NONBLOCK);
+
+ /* read what's left over in the pipe/file buffer: */
+ do
+ {
+ while ((nread = read (s->from_child, buf, sizeof (buf))) > 0);
+ usleep (100000);
+ nread = read (s->from_child, buf, sizeof (buf));
+ }
+ while (nread > 0);
+
+ /* now restore non-blocking i/o flag: */
+ fcntl (s->from_child, F_SETFL, flags & O_NONBLOCK);
+
+ waitpid (s->reader_pid, 0, 0);
+ s->reader_pid = 0;
+
+ DBG (1, "cancel: cancellation completed\n");
+ }
+ if (s->holding_lock)
+ {
+ status = qc_unlock (s->hw);
+ if (status != SANE_STATUS_GOOD)
+ DBG(3, "sane_cancel: qc_unlock error\n");
+ s->holding_lock = SANE_FALSE;
+ }
+ DBG (5, "sane_cancel: exit\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ QC_Scanner *s = handle;
+
+ DBG (5, "sane_set_io_mode: enter\n");
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (fcntl (s->read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+ DBG (5, "sane_set_io_mode: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ QC_Scanner *s = handle;
+
+ DBG (5, "sane_get_select_fd: enter\n");
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ *fd = s->read_fd;
+ DBG (5, "sane_get_select_fd: exit\n");
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/qcam.conf.in b/backend/qcam.conf.in
new file mode 100644
index 0000000..f7de79a
--- /dev/null
+++ b/backend/qcam.conf.in
@@ -0,0 +1,12 @@
+#
+# In order to use the qcam backend, you'll need to enable to line with
+# the port address for your scanner. You can enable multiple lines if
+# you really have a QuickCam connect to each port.
+#
+#u0x37b # /dev/lp0 forced in unidir mode
+#u0x378 # /dev/lp1 forced in unidir mode
+#u0x278 # /dev/lp2 forced in unidir mode
+#0x37b # /dev/lp0
+#0x378 # /dev/lp1
+#0x278 # /dev/lp2
+0x3bc # /dev/lp0
diff --git a/backend/qcam.h b/backend/qcam.h
new file mode 100644
index 0000000..417ca5a
--- /dev/null
+++ b/backend/qcam.h
@@ -0,0 +1,190 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Portions of this code are derived from Scott Laird's qcam driver.
+ It's copyright notice is reproduced here:
+
+ Copyright (C) 1996 by Scott Laird
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifndef qcam_h
+#define qcam_h
+
+#include "../include/sane/sane.h"
+
+typedef enum
+ {
+ QC_MONO = 0x01,
+ QC_COLOR = 0x10
+ }
+QC_Model;
+
+typedef enum
+ {
+ QC_RES_LOW = 0,
+ QC_RES_HIGH
+ }
+QC_Resolution;
+
+/* commands common to all quick-cameras: */
+typedef enum
+ {
+ QC_SEND_VIDEO_FRAME = 7,
+ QC_SET_BRIGHTNESS = 11,
+ QC_SET_TOP = 13,
+ QC_SET_LEFT = 15,
+ QC_SET_NUM_V = 17,
+ QC_SET_NUM_H = 19,
+ QC_SEND_VERSION = 23,
+ QC_SET_BLACK = 29,
+ QC_SET_WHITE = 31,
+ QC_SET_SATURATION = 35,
+ QC_SEND_STATUS = 41,
+ QC_SET_SPEED = 45
+ }
+QC_Command;
+
+/* commands for grayscale camera: */
+typedef enum
+ {
+ QC_MONO_SET_CONTRAST = 25,
+ QC_MONO_AUTO_ADJUST_OFFSET = 27,
+ QC_MONO_GET_OFFSET = 33
+ }
+QC_Mono_Command;
+
+/* commands for color camera: */
+typedef enum
+ {
+ QC_COL_LOAD_RAM = 27,
+ QC_COL_SET_HUE = 33,
+ QC_COL_SET_CONTRAST = 37
+ }
+QC_Col_Command;
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_DEPTH, /* 4 or 6 (b&w) or 24 (color) */
+ OPT_RESOLUTION, /* resolution in pixels */
+ OPT_XFER_SCALE, /* transfer-scale */
+ OPT_DESPECKLE, /* turn on despeckling? */
+ OPT_TEST, /* test image */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_BLACK_LEVEL,
+ OPT_WHITE_LEVEL,
+ OPT_HUE,
+ OPT_SATURATION,
+
+ /* must come last: */
+ NUM_OPTIONS
+ }
+QC_Option;
+
+typedef enum
+ {
+ QC_UNIDIR,
+ QC_BIDIR
+ } QC_Port_Mode;
+
+typedef struct
+ {
+ size_t num_bytes; /* # of bytes to read */
+ QC_Resolution resolution; /* high-resolution? */
+ SANE_Parameters params; /* other parameters */
+ u_int mode; /* qcam scan code (get video data command) */
+ int despeckle; /* apply despeckling filter? */
+ }
+QC_Scan_Request;
+
+typedef struct QC_Device
+ {
+ struct QC_Device * next;
+ SANE_Device sane;
+ QC_Port_Mode port_mode;
+ int port; /* i/o port address */
+ int version; /* camera version */
+ int lock_fd; /* used for locking protocol */
+ }
+QC_Device;
+
+typedef struct QC_Scanner
+ {
+ struct QC_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ QC_Resolution resolution;
+ SANE_Parameters params;
+ QC_Device *hw;
+ SANE_Int user_corner; /* bitmask of user-selected coordinates */
+ SANE_Int value_changed; /* bitmask of options that were set */
+ SANE_Bool scanning;
+ SANE_Bool deliver_eof;
+ SANE_Bool holding_lock; /* are we holding the lock? */
+ /* state for reading a frame: */
+ size_t num_bytes; /* # of bytes read so far */
+ size_t bytes_per_frame; /* total number of bytes in frame */
+ /* state relating to the reader-process */
+ int reader_pid; /* -1 if there is no reader process (yet) */
+ int from_child; /* fd to read from child process*/
+ int to_child; /* fd to write to child */
+ int read_fd; /* used to read data */
+ /* internal state for qc_readbytes(): */
+ int readbytes_state;
+ unsigned int saved_bits;
+ }
+QC_Scanner;
+
+#endif /* qcam_h */
diff --git a/backend/ricoh-scsi.c b/backend/ricoh-scsi.c
new file mode 100644
index 0000000..72fd70b
--- /dev/null
+++ b/backend/ricoh-scsi.c
@@ -0,0 +1,413 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998 F.W. Dillema (dillema@acm.org)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/*
+ $Id$
+ This file implements the low-level scsi-commands.
+*/
+
+
+
+
+/* SCSI commands that the Ricoh scanners understand: */
+#define RICOH_SCSI_TEST_UNIT_READY 0x00
+#define RICOH_SCSI_SET_WINDOW 0x24
+#define RICOH_SCSI_GET_WINDOW 0x25
+#define RICOH_SCSI_READ_SCANNED_DATA 0x28
+#define RICOH_SCSI_INQUIRY 0x12
+#define RICOH_SCSI_MODE_SELECT 0x15
+#define RICOH_SCSI_START_SCAN 0x1b
+#define RICOH_SCSI_MODE_SENSE 0x1a
+#define RICOH_SCSI_GET_BUFFER_STATUS 0x34
+#define RICOH_SCSI_OBJECT_POSITION 0x31
+
+/* How long do we wait for scanner to have data for us */
+#define MAX_WAITING_TIME 15
+
+struct scsi_window_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte reserved[4];
+ SANE_Byte len[3];
+ SANE_Byte control;
+};
+
+struct scsi_mode_select_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ SANE_Byte page_code; /* for mode_sense, reserved for mode_select */
+ SANE_Byte unused[1];
+ SANE_Byte len;
+ SANE_Byte control;
+};
+
+struct scsi_mode_header {
+ SANE_Byte data_length; /* Sense data length */
+ SANE_Byte medium_type;
+ SANE_Byte dev_spec;
+ SANE_Byte blk_desc_len;
+};
+
+struct scsi_get_buffer_status_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte res[5];
+ SANE_Byte len[2];
+ SANE_Byte control;
+};
+
+struct scsi_status_desc {
+ SANE_Byte window_id;
+ SANE_Byte byte2;
+ SANE_Byte available[3];
+ SANE_Byte filled[3];
+};
+
+struct scsi_status_data {
+ SANE_Byte len[3];
+ SANE_Byte byte4;
+ struct scsi_status_desc desc;
+};
+
+struct scsi_start_scan_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte unused[2];
+ SANE_Byte len;
+ SANE_Byte control;
+};
+
+struct scsi_read_scanner_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte data_type;
+ SANE_Byte byte3;
+ SANE_Byte data_type_qualifier[2];
+ SANE_Byte len[3];
+ SANE_Byte control;
+};
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (11, ">> test_unit_ready\n");
+
+ cmd[0] = RICOH_SCSI_TEST_UNIT_READY;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< test_unit_ready\n");
+ return (status);
+}
+
+static SANE_Status
+inquiry (int fd, void *buf, size_t * buf_size)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (11, ">> inquiry\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = RICOH_SCSI_INQUIRY;
+ cmd[4] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, "<< inquiry\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select (int fd, struct mode_pages *mp)
+{
+ static struct {
+ struct scsi_mode_select_cmd cmd;
+ struct scsi_mode_header smh;
+ struct mode_pages mp;
+ } select_cmd;
+ SANE_Status status;
+ DBG (11, ">> mode_select\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd.opcode = RICOH_SCSI_MODE_SELECT;
+ select_cmd.cmd.byte2 |= SMS_PF;
+ select_cmd.cmd.len = sizeof(select_cmd.smh) + sizeof(select_cmd.mp);
+ memcpy (&select_cmd.mp, mp, sizeof(*mp));
+ status = sanei_scsi_cmd (fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ DBG (11, "<< mode_select\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+mode_sense (int fd, struct mode_pages *mp, SANE_Byte page_code)
+{
+ static struct scsi_mode_select_cmd cmd; /* no type, we can reuse it for sensing */
+ static struct {
+ struct scsi_mode_header smh;
+ struct mode_pages mp;
+ } select_data;
+ static size_t select_size = sizeof(select_data);
+ SANE_Status status;
+ DBG (11, ">> mode_sense\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = RICOH_SCSI_MODE_SENSE;
+ cmd.page_code = page_code;
+ cmd.len = sizeof(select_data);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &select_data, &select_size);
+ memcpy (mp, &select_data.mp, sizeof(*mp));
+
+ DBG (11, "<< mode_sense\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+trigger_scan (int fd)
+{
+ static struct scsi_start_scan_cmd cmd;
+ static char window_id_list[1] = { '\0' }; /* scan start data out */
+ static size_t wl_size = 1;
+ SANE_Status status;
+ DBG (11, ">> trigger scan\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = RICOH_SCSI_START_SCAN;
+ cmd.len = wl_size;
+ if (wl_size)
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &window_id_list, &wl_size);
+ else
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< trigger scan\n");
+ return (status);
+}
+
+static SANE_Status
+set_window (int fd, struct ricoh_window_data *rwd)
+{
+
+ static struct {
+ struct scsi_window_cmd cmd;
+ struct ricoh_window_data rwd;
+ } win;
+
+ SANE_Status status;
+ DBG (11, ">> set_window\n");
+
+ memset (&win, 0, sizeof (win));
+ win.cmd.opcode = RICOH_SCSI_SET_WINDOW;
+ _lto3b(sizeof(*rwd), win.cmd.len);
+ memcpy (&win.rwd, rwd, sizeof(*rwd));
+ status = sanei_scsi_cmd (fd, &win, sizeof (win), 0, 0);
+
+ DBG (11, "<< set_window\n");
+ return (status);
+}
+
+static SANE_Status
+get_window (int fd, struct ricoh_window_data *rwd)
+{
+
+ static struct scsi_window_cmd cmd;
+ static size_t rwd_size;
+ SANE_Status status;
+
+ rwd_size = sizeof(*rwd);
+ DBG (11, ">> get_window datalen = %lu\n", (unsigned long) rwd_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = RICOH_SCSI_GET_WINDOW;
+#if 0
+ cmd.byte2 |= (SANE_Byte)0x01; /* set Single bit to get one window desc. */
+#endif
+ _lto3b(rwd_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), rwd, &rwd_size);
+
+ DBG (11, "<< get_window, datalen = %lu\n", (unsigned long) rwd_size);
+ return (status);
+}
+
+static SANE_Status
+read_data (int fd, void *buf, size_t * buf_size)
+{
+ static struct scsi_read_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (11, ">> read_data %lu\n", (unsigned long) *buf_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = RICOH_SCSI_READ_SCANNED_DATA;
+ _lto3b(*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, "<< read_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+
+static SANE_Status
+object_position (int fd)
+{
+ static SANE_Byte cmd[10];
+ SANE_Status status;
+ DBG (11, ">> object_position\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = RICOH_SCSI_OBJECT_POSITION;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< object_position\n");
+ return (status);
+}
+
+static SANE_Status
+get_data_status (int fd, struct scsi_status_desc *dbs)
+{
+ static struct scsi_get_buffer_status_cmd cmd;
+ static struct scsi_status_data ssd;
+ size_t ssd_size = sizeof(ssd);
+ SANE_Status status;
+ DBG (11, ">> get_data_status %lu\n", (unsigned long) ssd_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = RICOH_SCSI_GET_BUFFER_STATUS;
+ _lto2b(ssd_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &ssd, &ssd_size);
+
+ memcpy (dbs, &ssd.desc, sizeof(*dbs));
+ if (status == SANE_STATUS_GOOD &&
+ (((unsigned int) _3btol(ssd.len)) <= sizeof(*dbs) || _3btol(ssd.desc.filled) == 0)) {
+ DBG (11, "get_data_status: busy\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ DBG (11, "<< get_data_status %lu\n", (unsigned long) ssd_size);
+ return (status);
+}
+
+#if 0
+static SANE_Status
+ricoh_wait_ready_tur (int fd)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG(3, "scsi_wait_ready: sending TEST_UNIT_READY\n");
+
+ status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0);
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG(1, "scsi_wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG(1, "ricoh_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+#endif
+
+static SANE_Status
+ricoh_wait_ready (Ricoh_Scanner * s)
+{
+ struct scsi_status_desc dbs;
+ time_t now, start;
+ SANE_Status status;
+
+ start = time(NULL);
+
+ while (1)
+ {
+ status = get_data_status (s->fd, &dbs);
+
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG(1, "scsi_wait_ready: get datat status failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ now = time(NULL);
+ if (now - start >= MAX_WAITING_TIME)
+ {
+ DBG(1, "ricoh_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now - start));
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_STATUS_GOOD:
+ DBG(11, "ricoh_wait_ready: %d bytes ready\n", _3btol(dbs.filled));
+ return status;
+ break;
+ }
+ usleep (1000000); /* retry after 100ms */
+ }
+ return SANE_STATUS_INVAL;
+}
diff --git a/backend/ricoh.c b/backend/ricoh.c
new file mode 100644
index 0000000..a349179
--- /dev/null
+++ b/backend/ricoh.c
@@ -0,0 +1,1025 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998, Feico W. Dillema
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ This file implements a SANE backend for Ricoh flatbed scanners.
+*/
+
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+#define BACKEND_NAME ricoh
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define RICOH_CONFIG_FILE "ricoh.conf"
+
+#include "ricoh.h"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+static int num_devices = 0;
+static Ricoh_Device *first_dev = NULL;
+static Ricoh_Scanner *first_handle = NULL;
+static int is50 = 0;
+
+#include "ricoh-scsi.c"
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (11, ">> max_string_size\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (11, "<< max_string_size\n");
+ return max_size;
+}
+
+static SANE_Status
+attach (const char *devnam, Ricoh_Device ** devp)
+{
+ SANE_Status status;
+ Ricoh_Device *dev;
+
+ int fd;
+ struct inquiry_data ibuf;
+ struct measurements_units_page mup;
+ struct ricoh_window_data wbuf;
+ size_t buf_size;
+ char *str;
+ DBG (11, ">> attach\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ DBG (3, "attach: opening %s\n", devnam);
+ status = sanei_scsi_open (devnam, &fd, NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (3, "attach: sending INQUIRY\n");
+ memset (&ibuf, 0, sizeof (ibuf));
+ buf_size = sizeof(ibuf);
+ status = inquiry (fd, &ibuf, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ if (ibuf.devtype != 6
+ || strncmp ((char *)ibuf.vendor, "RICOH", 5) != 0
+ || (strncmp ((char *)ibuf.product, "IS50", 4) != 0
+ && strncmp ((char *)ibuf.product, "IS60", 4) != 0))
+ {
+ DBG (1, "attach: device doesn't look like the Ricoh scanner I know\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+ is50 = (strncmp ((char *)ibuf.product, "IS50", 4) == 0);
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ DBG (3, "attach: sending OBJECT POSITION\n");
+ status = object_position (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: OBJECT POSTITION failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ memset (&mup, 0, sizeof (mup));
+ mup.page_code = MEASUREMENTS_PAGE;
+ mup.parameter_length = 0x06;
+ mup.bmu = INCHES;
+ mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff;
+ mup.mud[1] = (DEFAULT_MUD & 0xff);
+
+#if 0
+ DBG (3, "attach: sending MODE SELECT\n");
+ status = mode_select (fd, (struct mode_pages *) &mup);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+#endif
+
+#if 0
+ DBG (3, "attach: sending MODE SENSE\n");
+ memset (&mup, 0, sizeof (mup));
+ status = mode_sense (fd, (struct mode_pages *) &mup, PC_CURRENT | MEASUREMENTS_PAGE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+#endif
+
+ DBG (3, "attach: sending GET WINDOW\n");
+ memset (&wbuf, 0, sizeof (wbuf));
+ status = get_window (fd, &wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: GET_WINDOW failed %d\n", status);
+ sanei_scsi_close (fd);
+ DBG (11, "<< attach\n");
+ return (SANE_STATUS_INVAL);
+ }
+
+ sanei_scsi_close (fd);
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devnam);
+ dev->sane.vendor = "RICOH";
+ str = malloc (16 + 1);
+ memset (str, 0, sizeof (str));
+ strncpy (str, (char *)ibuf.product, sizeof(ibuf.product));
+ strncpy (str + sizeof(ibuf.revision), (char *)ibuf.revision, sizeof(ibuf.revision));
+ str[sizeof(ibuf.product) + sizeof(ibuf.revision)] = '\0';
+ dev->sane.model = str;
+ dev->sane.type = "flatbed scanner";
+
+ DBG (5, "dev->sane.name = %s\n", dev->sane.name);
+ DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor);
+ DBG (5, "dev->sane.model = %s\n", dev->sane.model);
+ DBG (5, "dev->sane.type = %s\n", dev->sane.type);
+
+ dev->info.xres_default = _2btol(wbuf.x_res);
+ dev->info.yres_default = _2btol(wbuf.y_res);
+ dev->info.image_mode_default = wbuf.image_comp;
+
+ /* if you throw the MRIF bit the brighness control reverses too */
+ /* so I reverse the reversal in software for symmetry's sake */
+ /* I should make this into an option */
+
+ if (wbuf.image_comp == RICOH_GRAYSCALE || wbuf.image_comp == RICOH_DITHERED_MONOCHROME)
+ {
+ dev->info.brightness_default = 256 - wbuf.brightness;
+ if (is50)
+ dev->info.contrast_default = wbuf.contrast;
+ else
+ dev->info.contrast_default = 256 - wbuf.contrast;
+ }
+ else /* wbuf.image_comp == RICOH_BINARY_MONOCHROME */
+ {
+ dev->info.brightness_default = wbuf.brightness;
+ dev->info.contrast_default = wbuf.contrast;
+ }
+
+#if 1
+ dev->info.bmu = mup.bmu;
+ dev->info.mud = _2btol(mup.mud);
+ if (dev->info.mud == 0) {
+ /* The RICOH says it uses points as default Basic Measurement Unit */
+ /* but gives a Measurement Unit Divisor of zero */
+ /* So, we set it to the default (SCSI-standard) of 1200 */
+ /* with BMU in inches, i.e. 1200 points equal 1 inch */
+ dev->info.bmu = INCHES;
+ dev->info.mud = DEFAULT_MUD;
+ }
+#else
+ dev->info.bmu = INCHES;
+ dev->info.mud = DEFAULT_MUD;
+#endif
+
+ DBG (5, "xres_default=%d\n", dev->info.xres_default);
+ DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max);
+ DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min);
+
+ DBG (5, "yres_default=%d\n", dev->info.yres_default);
+ DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max);
+ DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min);
+
+ DBG (5, "x_range.max=%d\n", dev->info.x_range.max);
+ DBG (5, "y_range.max=%d\n", dev->info.y_range.max);
+
+ DBG (5, "image_mode=%d\n", dev->info.image_mode_default);
+
+ DBG (5, "brightness=%d\n", dev->info.brightness_default);
+ DBG (5, "contrast=%d\n", dev->info.contrast_default);
+
+ DBG (5, "bmu=%d\n", dev->info.bmu);
+ DBG (5, "mud=%d\n", dev->info.mud);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (11, "<< attach\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+attach_one(const char *devnam)
+{
+ attach (devnam, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (Ricoh_Scanner * s)
+{
+ int i;
+ DBG (11, ">> init_options\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[s->hw->info.image_mode_default]);
+
+ /* x resolution */
+ s->opt[OPT_X_RESOLUTION].name = "X" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = "X " SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &is60_res_range;
+ s->val[OPT_X_RESOLUTION].w = s->hw->info.xres_default;
+ if (is50)
+ s->opt[OPT_X_RESOLUTION].constraint.range = &is50_res_range;
+ else
+ s->opt[OPT_X_RESOLUTION].constraint.range = &is60_res_range;
+
+ /* y resolution */
+ s->opt[OPT_Y_RESOLUTION].name = "Y" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = "Y " SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_Y_RESOLUTION].w = s->hw->info.yres_default;
+ if (is50)
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &is50_res_range;
+ else
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &is60_res_range;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &default_x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &default_y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &default_x_range;
+ s->val[OPT_BR_X].w = default_x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &default_y_range;
+ s->val[OPT_BR_Y].w = default_y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range;
+ s->val[OPT_BRIGHTNESS].w = s->hw->info.brightness_default;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &u8_range;
+ s->val[OPT_CONTRAST].w = s->hw->info.contrast_default;
+
+ DBG (11, "<< init_options\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (Ricoh_Scanner * s)
+{
+ SANE_Status status;
+ DBG (11, ">> do_cancel\n");
+
+ DBG (3, "cancel: sending OBJECT POSITION\n");
+ status = object_position (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "cancel: OBJECT POSTITION failed\n");
+ }
+
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (11, "<< do_cancel\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ FILE *fp;
+
+ authorize = authorize; /* silence gcc */
+
+ DBG_INIT ();
+ DBG (11, ">> sane_init\n");
+
+#if defined PACKAGE && defined VERSION
+ DBG (2, "sane_init: " PACKAGE " " VERSION "\n");
+#endif
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open(RICOH_CONFIG_FILE);
+ if (fp)
+ {
+ char line[PATH_MAX], *lp;
+ size_t len;
+
+ /* read config file */
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ /* skip white space: */
+ for (lp = line; isspace(*lp); ++lp);
+ strcpy (devnam, lp);
+ }
+ fclose (fp);
+ }
+ sanei_config_attach_matching_devices (devnam, attach_one);
+ DBG (11, "<< sane_init\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Ricoh_Device *dev, *next;
+ DBG (11, ">> sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+
+ DBG (11, "<< sane_exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Ricoh_Device *dev;
+ int i;
+
+ local_only = local_only; /* silence gcc */
+
+ DBG (11, ">> sane_get_devices\n");
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (11, "<< sane_get_devices\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ Ricoh_Device *dev;
+ Ricoh_Scanner *s;
+ DBG (11, ">> sane_open\n");
+
+ if (devnam[0] == '\0')
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->hw = dev;
+
+ init_options (s);
+
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (11, "<< sane_open\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Ricoh_Scanner *s = (Ricoh_Scanner *) handle;
+ DBG (11, ">> sane_close\n");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+ free (s);
+
+ DBG (11, ">> sane_close\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Ricoh_Scanner *s = handle;
+ DBG (11, ">> sane_get_option_descriptor\n");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (11, "<< sane_get_option_descriptor\n");
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Ricoh_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ DBG (11, ">> sane_control_option\n");
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ *(SANE_Word *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_MODE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ DBG (11, "<< sane_control_option\n");
+ return (SANE_STATUS_INVAL);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Ricoh_Scanner *s = handle;
+ DBG (11, ">> sane_get_parameters\n");
+
+ if (!s->scanning)
+ {
+ int width, length, xres, yres;
+ const char *mode;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w;
+ length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w;
+ xres = s->val[OPT_X_RESOLUTION].w;
+ yres = s->val[OPT_Y_RESOLUTION].w;
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (xres > 0 && yres > 0 && width > 0 && length > 0)
+ {
+ s->params.pixels_per_line = width * xres / s->hw->info.mud;
+ s->params.lines = length * yres / s->hw->info.mud;
+ }
+
+ mode = s->val[OPT_MODE].s;
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0 || strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ /* the Ricoh truncates to the byte boundary, so: chop! */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ s->params.depth = 1;
+ }
+ else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ s->params.last_frame = SANE_TRUE;
+ }
+
+ if (params)
+ *params = s->params;
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w);
+
+ DBG (11, "<< sane_get_parameters\n");
+ return (SANE_STATUS_GOOD);
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char *mode_str;
+ Ricoh_Scanner *s = handle;
+ SANE_Status status;
+ struct ricoh_window_data wbuf;
+ struct measurements_units_page mup;
+
+ DBG (11, ">> sane_start\n");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return (status);
+ }
+
+ mode_str = s->val[OPT_MODE].s;
+ s->xres = s->val[OPT_X_RESOLUTION].w;
+ s->yres = s->val[OPT_Y_RESOLUTION].w;
+ s->ulx = s->val[OPT_TL_X].w;
+ s->uly = s->val[OPT_TL_Y].w;
+ s->width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w;
+ s->length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w;
+ s->brightness = s->val[OPT_BRIGHTNESS].w;
+ s->contrast = s->val[OPT_CONTRAST].w;
+ s->bpp = s->params.depth;
+ if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->image_composition = RICOH_BINARY_MONOCHROME;
+ }
+ else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ {
+ s->image_composition = RICOH_DITHERED_MONOCHROME;
+ }
+ else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ s->image_composition = RICOH_GRAYSCALE;
+ }
+
+ memset (&wbuf, 0, sizeof (wbuf));
+ _lto2b(sizeof(wbuf) - 8, wbuf.len);
+ _lto2b(s->xres, wbuf.x_res);
+ _lto2b(s->yres, wbuf.y_res);
+ _lto4b(s->ulx, wbuf.x_org);
+ _lto4b(s->uly, wbuf.y_org);
+ _lto4b(s->width, wbuf.width);
+ _lto4b(s->length, wbuf.length);
+
+ wbuf.image_comp = s->image_composition;
+ /* if you throw the MRIF bit the brighness control reverses too */
+ /* so I reverse the reversal in software for symmetry's sake */
+ if (wbuf.image_comp == RICOH_GRAYSCALE || wbuf.image_comp == RICOH_DITHERED_MONOCHROME)
+ {
+ if (wbuf.image_comp == RICOH_GRAYSCALE)
+ wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x90;
+ if (wbuf.image_comp == RICOH_DITHERED_MONOCHROME)
+ wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x10;
+ wbuf.brightness = 256 - (SANE_Byte) s->brightness;
+ if (is50)
+ wbuf.contrast = (SANE_Byte) s->contrast;
+ else
+ wbuf.contrast = 256 - (SANE_Byte) s->contrast;
+ }
+ else /* wbuf.image_comp == RICOH_BINARY_MONOCHROME */
+ {
+ wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x00;
+ wbuf.brightness = (SANE_Byte) s->brightness;
+ wbuf.contrast = (SANE_Byte) s->contrast;
+ }
+
+ wbuf.threshold = 0;
+ wbuf.bits_per_pixel = s->bpp;
+
+ wbuf.halftone_pattern[0] = 2;
+ wbuf.halftone_pattern[1] = 0;
+ wbuf.pad_type = 3;
+ wbuf.bit_ordering[0] = 0;
+ wbuf.bit_ordering[1] = 3;
+
+ DBG (5, "xres=%d\n", _2btol(wbuf.x_res));
+ DBG (5, "yres=%d\n", _2btol(wbuf.y_res));
+ DBG (5, "ulx=%d\n", _4btol(wbuf.x_org));
+ DBG (5, "uly=%d\n", _4btol(wbuf.y_org));
+ DBG (5, "width=%d\n", _4btol(wbuf.width));
+ DBG (5, "length=%d\n", _4btol(wbuf.length));
+ DBG (5, "image_comp=%d\n", wbuf.image_comp);
+
+ DBG (11, "sane_start: sending SET WINDOW\n");
+ status = set_window (s->fd, &wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (11, "sane_start: sending GET WINDOW\n");
+ memset (&wbuf, 0, sizeof (wbuf));
+ status = get_window (s->fd, &wbuf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ DBG (5, "xres=%d\n", _2btol(wbuf.x_res));
+ DBG (5, "yres=%d\n", _2btol(wbuf.y_res));
+ DBG (5, "ulx=%d\n", _4btol(wbuf.x_org));
+ DBG (5, "uly=%d\n", _4btol(wbuf.y_org));
+ DBG (5, "width=%d\n", _4btol(wbuf.width));
+ DBG (5, "length=%d\n", _4btol(wbuf.length));
+ DBG (5, "image_comp=%d\n", wbuf.image_comp);
+
+ DBG (11, "sane_start: sending MODE SELECT\n");
+ memset (&mup, 0, sizeof (mup));
+ mup.page_code = MEASUREMENTS_PAGE;
+ mup.parameter_length = 0x06;
+ mup.bmu = INCHES;
+ mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff;
+ mup.mud[1] = (DEFAULT_MUD & 0xff);
+
+ status = mode_select (s->fd, (struct mode_pages *) &mup);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT failed\n");
+ return (SANE_STATUS_INVAL);
+ }
+
+ status = trigger_scan (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ /* Wait for scanner to become ready to transmit data */
+ status = ricoh_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w);
+
+ s->scanning = SANE_TRUE;
+
+ DBG (11, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Ricoh_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ DBG (11, ">> sane_read\n");
+
+ *len = 0;
+
+ DBG (11, "sane_read: bytes left to read: %ld\n", (u_long) s->bytes_to_read);
+
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning) {
+ DBG (11, "sane_read: scanning is false!\n");
+ return (do_cancel (s));
+ }
+
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+
+ DBG (11, "sane_read: read %ld bytes\n", (u_long) nread);
+ status = read_data (s->fd, buf, &nread);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (11, "sane_read: read error\n");
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+
+ DBG (11, "<< sane_read\n");
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Ricoh_Scanner *s = handle;
+ DBG (11, ">> sane_cancel\n");
+
+ s->scanning = SANE_FALSE;
+
+ DBG (11, "<< sane_cancel\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ handle = handle; /* silence gcc */
+ non_blocking = non_blocking; /* silence gcc */
+
+ DBG (5, ">> sane_set_io_mode\n");
+ DBG (5, "<< sane_set_io_mode\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle; /* silence gcc */
+ fd = fd; /* silence gcc */
+
+ DBG (5, ">> sane_get_select_fd\n");
+ DBG (5, "<< sane_get_select_fd\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/ricoh.conf.in b/backend/ricoh.conf.in
new file mode 100644
index 0000000..779cd7e
--- /dev/null
+++ b/backend/ricoh.conf.in
@@ -0,0 +1,2 @@
+scsi RICOH IS60
+/dev/scanner
diff --git a/backend/ricoh.h b/backend/ricoh.h
new file mode 100644
index 0000000..f9bb36e
--- /dev/null
+++ b/backend/ricoh.h
@@ -0,0 +1,330 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998, Feico W. Dillema
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+/*
+ $Id$
+*/
+
+#ifndef ricoh_h
+#define ricoh_h 1
+
+#include <sys/types.h>
+
+#include "../include/sane/config.h"
+
+/* defines for scan_image_mode field */
+#define RICOH_BINARY_MONOCHROME 0
+#define RICOH_DITHERED_MONOCHROME 1
+#define RICOH_GRAYSCALE 2
+
+/* sizes for mode parameter's base_measurement_unit */
+#define INCHES 0
+#define MILLIMETERS 1
+#define POINTS 2
+#define DEFAULT_MUD 1200
+#define MEASUREMENTS_PAGE (SANE_Byte)(0x03)
+
+/* Mode Page Control */
+#define PC_CURRENT 0x00
+#define PC_CHANGE 0x40
+#define PC_DEFAULT 0x80
+#define PC_SAVED 0xc0
+
+static const SANE_String_Const mode_list[] =
+ {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ 0
+ };
+
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+static const SANE_Range is50_res_range =
+ {
+ 75, /* minimum */
+ 400, /* maximum */
+ 0 /* quantization */
+ };
+
+static const SANE_Range is60_res_range =
+ {
+ 100, /* minimum */
+ 600, /* maximum */
+ 0 /* quantization */
+ };
+
+static const SANE_Range default_x_range =
+ {
+ 0, /* minimum */
+ (SANE_Word) (8 * DEFAULT_MUD), /* maximum */
+ 2 /* quantization */
+ };
+
+static const SANE_Range default_y_range =
+ {
+ 0, /* minimum */
+ (SANE_Word) (14 * DEFAULT_MUD), /* maximum */
+ 2 /* quantization */
+ };
+
+
+
+static inline void
+_lto2b(SANE_Int val, SANE_Byte *bytes)
+{
+
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static inline void
+_lto3b(SANE_Int val, SANE_Byte *bytes)
+{
+
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static inline void
+_lto4b(SANE_Int val, SANE_Byte *bytes)
+{
+
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static inline SANE_Int
+_2btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 8) |
+ bytes[1];
+ return (rv);
+}
+
+static inline SANE_Int
+_3btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 16) |
+ (bytes[1] << 8) |
+ bytes[2];
+ return (rv);
+}
+
+static inline SANE_Int
+_4btol(SANE_Byte *bytes)
+{
+ SANE_Int rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ /* must come last: */
+ NUM_OPTIONS
+ }
+Ricoh_Option;
+
+typedef struct Ricoh_Info
+ {
+ SANE_Range xres_range;
+ SANE_Range yres_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range brightness_range;
+ SANE_Range contrast_range;
+
+ SANE_Int xres_default;
+ SANE_Int yres_default;
+ SANE_Int image_mode_default;
+ SANE_Int brightness_default;
+ SANE_Int contrast_default;
+
+ SANE_Int bmu;
+ SANE_Int mud;
+ }
+Ricoh_Info;
+
+typedef struct Ricoh_Device
+ {
+ struct Ricoh_Device *next;
+ SANE_Device sane;
+ Ricoh_Info info;
+ }
+Ricoh_Device;
+
+typedef struct Ricoh_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct Ricoh_Scanner *next;
+ int fd; /* SCSI filedescriptor */
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+ /* scanner dependent/low-level state: */
+ Ricoh_Device *hw;
+
+ SANE_Int xres;
+ SANE_Int yres;
+ SANE_Int ulx;
+ SANE_Int uly;
+ SANE_Int width;
+ SANE_Int length;
+ SANE_Int brightness;
+ SANE_Int contrast;
+ SANE_Int image_composition;
+ SANE_Int bpp;
+ SANE_Bool reverse;
+
+ size_t bytes_to_read;
+ int scanning;
+ }
+Ricoh_Scanner;
+
+struct inquiry_data {
+ SANE_Byte devtype;
+ SANE_Byte byte2;
+ SANE_Byte byte3;
+ SANE_Byte byte4;
+ SANE_Byte byte5;
+ SANE_Byte res1[2];
+ SANE_Byte flags;
+ SANE_Byte vendor[8];
+ SANE_Byte product[8];
+ SANE_Byte revision[4];
+ SANE_Byte byte[60];
+};
+
+#define RICOH_WINDOW_DATA_SIZE 328
+struct ricoh_window_data {
+ /* header */
+ SANE_Byte reserved[6];
+ SANE_Byte len[2];
+ /* data */
+ SANE_Byte window_id; /* must be zero */
+ SANE_Byte auto_bit;
+ SANE_Byte x_res[2];
+ SANE_Byte y_res[2];
+ SANE_Byte x_org[4];
+ SANE_Byte y_org[4];
+ SANE_Byte width[4];
+ SANE_Byte length[4];
+ SANE_Byte brightness;
+ SANE_Byte threshold;
+ SANE_Byte contrast;
+ SANE_Byte image_comp; /* image composition (data type) */
+ SANE_Byte bits_per_pixel;
+ SANE_Byte halftone_pattern[2];
+ SANE_Byte pad_type;
+ SANE_Byte bit_ordering[2];
+ SANE_Byte compression_type;
+ SANE_Byte compression_arg;
+ SANE_Byte res3[6];
+
+ /* Vendor Specific parameter byte(s) */
+ /* Ricoh specific, follow the scsi2 standard ones */
+ SANE_Byte byte1;
+ SANE_Byte byte2;
+ SANE_Byte mrif_filtering_gamma_id;
+ SANE_Byte byte3;
+ SANE_Byte byte4;
+ SANE_Byte binary_filter;
+ SANE_Byte reserved2[18];
+
+ SANE_Byte reserved3[256];
+};
+
+struct measurements_units_page {
+ SANE_Byte page_code; /* 0x03 */
+ SANE_Byte parameter_length; /* 0x06 */
+ SANE_Byte bmu;
+ SANE_Byte res1;
+ SANE_Byte mud[2];
+ SANE_Byte res2[2]; /* anybody know what `COH' may mean ??? */
+#if 0
+ SANE_Byte more_pages[243]; /* maximum size 255 bytes (incl header) */
+#endif
+};
+
+struct mode_pages {
+ SANE_Byte page_code;
+ SANE_Byte parameter_length;
+ SANE_Byte rest[6];
+#if 0
+ SANE_Byte more_pages[243]; /* maximum size 255 bytes (incl header) */
+#endif
+};
+
+
+#endif /* ricoh_h */
diff --git a/backend/rts8891.c b/backend/rts8891.c
new file mode 100644
index 0000000..d86347b
--- /dev/null
+++ b/backend/rts8891.c
@@ -0,0 +1,7817 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2013 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device, adding a handle
+ to the opened device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device, freeing scanner handle
+ - sane_exit() : terminate use of backend, freeing all resources for attached
+ devices
+
+ BACKEND USB locking policy
+ - interface is released at the end of sane_open() to allow
+ multiple usage by fronteds
+ - if free, interface is claimed at sane_start(), and held until
+ sane_cancel() is called
+ - if free interface is claimed then released just during the access
+ to buttons registers
+*/
+
+/* ------------------------------------------------------------------------- */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_LIBC_H
+# include <libc.h>
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+
+#define DARK_TARGET 3.1 /* 3.5 target average for dark calibration */
+#define DARK_MARGIN 0.3 /* acceptable margin for dark average */
+
+#define OFFSET_TARGET 3.5 /* target average for offset calibration */
+#define OFFSET_MARGIN 0.3 /* acceptable margin for offset average */
+
+#define RED_GAIN_TARGET 170 /* target average for gain calibration for blue color */
+#define GREEN_GAIN_TARGET 170 /* target average for gain calibration for blue color */
+#define BLUE_GAIN_TARGET 180 /* target average for gain calibration for blue color */
+#define GAIN_MARGIN 2 /* acceptable margin for gain average */
+
+#define MARGIN_LEVEL 128 /* white level for margin detection */
+
+/* width used for calibration */
+#define CALIBRATION_WIDTH 637
+
+/* data size for calibration: one RGB line*/
+#define CALIBRATION_SIZE CALIBRATION_WIDTH*3
+
+/* #define FAST_INIT 1 */
+
+#define BUILD 2401
+
+#define MOVE_DPI 100
+
+#include "rts8891.h"
+#include "rts88xx_lib.h"
+#include "rts8891_low.c"
+#include "rts8891_devices.c"
+
+
+#define DEEP_DEBUG 1
+
+/**
+ * Pointer to the first Rts8891_Session in the linked list of
+ * opened device. Sessions are inserted here on sane_open() and
+ * removed on sane_close().
+ */
+static Rts8891_Session *first_handle = NULL;
+
+
+/* pointer to the first device attached to the backend
+ * the same device may be opened several time
+ * entry are inserted here by attach_rts8891 */
+static Rts8891_Device *first_device = NULL;
+static SANE_Int num_devices = 0;
+
+/*
+ * needed by sane_get_devices
+ */
+static const SANE_Device **devlist = 0;
+
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_LINEART,
+ 0
+};
+
+static SANE_Range x_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (216.0), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range y_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (299.0), /* maximum */
+ SANE_FIX (0.0) /* no quantization */
+};
+
+/* model number ranges from 0 to 2, must be changed if
+ * Rts8891_USB_Device_Entry changes */
+static const SANE_Range model_range = {
+ 0, /* minimum */
+ 2, /* maximum */
+ 0 /* no quantization */
+};
+
+/* sensor number ranges from 0 to SENSOR_TYPE_MAX, must be changed if */
+static const SANE_Range sensor_range = {
+ 0, /* minimum */
+ SENSOR_TYPE_MAX, /* maximum */
+ 0 /* no quantization */
+};
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* no quantization */
+};
+
+static const SANE_Range threshold_percentage_range = {
+ SANE_FIX (0), /* minimum */
+ SANE_FIX (100), /* maximum */
+ SANE_FIX (1) /* quantization */
+};
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+ /**> placeholders for decoded configuration values */
+static Rts8891_Config rtscfg;
+
+/* ------------------------------------------------------------------------- */
+static SANE_Status probe_rts8891_devices (void);
+static SANE_Status config_attach_rts8891 (SANEI_Config * config,
+ const char *devname);
+static SANE_Status attach_rts8891 (const char *name);
+static SANE_Status set_lamp_brightness (struct Rts8891_Device *dev,
+ int level);
+static SANE_Status init_options (struct Rts8891_Session *session);
+#ifndef FAST_INIT
+static SANE_Status init_device (struct Rts8891_Device *dev);
+#else
+static SANE_Status detect_device (struct Rts8891_Device *dev);
+static SANE_Status initialize_device (struct Rts8891_Device *dev);
+#endif
+static SANE_Status init_lamp (struct Rts8891_Device *dev);
+static SANE_Status find_origin (struct Rts8891_Device *dev,
+ SANE_Bool * changed);
+static SANE_Status find_margin (struct Rts8891_Device *dev);
+static SANE_Status dark_calibration (struct Rts8891_Device *dev, int mode,
+ int light);
+static SANE_Status gain_calibration (struct Rts8891_Device *dev, int mode,
+ int light);
+static SANE_Status offset_calibration (struct Rts8891_Device *dev, int mode,
+ int light);
+static SANE_Status shading_calibration (struct Rts8891_Device *dev,
+ SANE_Bool color, int mode, int light);
+static SANE_Status send_calibration_data (struct Rts8891_Session *session);
+static SANE_Status write_scan_registers (struct Rts8891_Session *session);
+static SANE_Status read_data (struct Rts8891_Session *session,
+ SANE_Byte * dest, SANE_Int length);
+static SANE_Status compute_parameters (struct Rts8891_Session *session);
+static SANE_Status move_to_scan_area (struct Rts8891_Session *session);
+static SANE_Status park_head (struct Rts8891_Device *dev, SANE_Bool wait);
+static SANE_Status update_button_status (struct Rts8891_Session *session);
+static SANE_Status set_lamp_state (struct Rts8891_Session *session, int on);
+
+
+/* ------------------------------------------------------------------------- */
+/* writes gray data to a pnm file */
+static void
+write_gray_data (unsigned char *image, char *name, SANE_Int width,
+ SANE_Int height)
+{
+ FILE *fdbg = NULL;
+
+ fdbg = fopen (name, "wb");
+ if (fdbg == NULL)
+ return;
+ fprintf (fdbg, "P5\n%d %d\n255\n", width, height);
+ fwrite (image, width, height, fdbg);
+ fclose (fdbg);
+}
+
+/* ------------------------------------------------------------------------- */
+/* writes rgb data to a pnm file */
+static void
+write_rgb_data (char *name, unsigned char *image, SANE_Int width,
+ SANE_Int height)
+{
+ FILE *fdbg = NULL;
+
+ fdbg = fopen (name, "wb");
+ if (fdbg == NULL)
+ return;
+ fprintf (fdbg, "P6\n%d %d\n255\n", width, height);
+ fwrite (image, width * 3, height, fdbg);
+ fclose (fdbg);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * SANE Interface
+ */
+
+
+/**
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Status status;
+
+ authorize = authorize; /* get rid of compiler warning */
+
+ /* init ASIC libraries */
+ sanei_rts88xx_lib_init ();
+ rts8891_low_init ();
+
+ /* init backend debug */
+ DBG_INIT ();
+ DBG (DBG_info, "SANE Rts8891 backend version %d.%d-%d\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ DBG (DBG_proc, "sane_init: start\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ /* cold-plugging case : probe for already plugged devices */
+ status = probe_rts8891_devices ();
+
+ DBG (DBG_proc, "sane_init: exit\n");
+ return status;
+}
+
+
+/**
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Int dev_num;
+ struct Rts8891_Device *device;
+ SANE_Device *sane_device;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ /* hot-plugging case : probe for devices plugged since sane_init called */
+ probe_rts8891_devices ();
+
+ /* reset devlist first if needed */
+ if (devlist)
+ {
+ for (i = 0; i < num_devices; i++)
+ free ((char *) devlist[i]);
+ free (devlist);
+ devlist = NULL;
+ }
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ *device_list = devlist;
+
+ dev_num = 0;
+
+ /* we build a list of SANE_Device from the list of attached devices */
+ for (device = first_device; dev_num < num_devices; device = device->next)
+ {
+ sane_device = malloc (sizeof (*sane_device));
+ if (!sane_device)
+ return SANE_STATUS_NO_MEM;
+ sane_device->name = device->file_name;
+ sane_device->vendor = device->model->vendor;
+ sane_device->model = device->model->product;
+ sane_device->type = device->model->type;
+ devlist[dev_num++] = sane_device;
+ }
+ devlist[dev_num++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Called to establish connection with the session. This function will
+ * also establish meaningful defaults and initialize the options.
+ *
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ * TODO handle SANE_STATUS_BUSY
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct Rts8891_Session *session = NULL;
+ struct Rts8891_Device *device = NULL;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: start (devicename=%s)\n", name);
+ if (name[0] == 0 || strncmp (name, "rts8891", 7) == 0)
+ {
+ DBG (DBG_info, "sane_open: no device requested, using default\n");
+ if (first_device)
+ {
+ device = first_device;
+ DBG (DBG_info, "sane_open: device %s used as default device\n",
+ device->file_name);
+ }
+ }
+ else
+ {
+ DBG (DBG_info, "sane_open: device %s requested\n", name);
+ /* walk the device list until we find a matching name */
+ device = first_device;
+ while (device && strcmp (device->file_name, name) != 0)
+ {
+ DBG (DBG_info, "sane_open: device %s doesn't match\n",
+ device->file_name);
+ device = device->next;
+ }
+ }
+
+ /* check wether we have found a match or reach the end of the device list */
+ if (!device)
+ {
+ DBG (DBG_info, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now we have a device, duplicate it and return it in handle */
+ DBG (DBG_info, "sane_open: device %s found\n", name);
+
+ if (device->model->flags & RTS8891_FLAG_UNTESTED)
+ {
+ DBG (DBG_error0,
+ "WARNING: Your scanner is not fully supported or at least \n");
+ DBG (DBG_error0,
+ " had only limited testing. Please be careful and \n");
+ DBG (DBG_error0, " report any failure/success to \n");
+ DBG (DBG_error0,
+ " sane-devel@lists.alioth.debian.org. Please provide as many\n");
+ DBG (DBG_error0,
+ " details as possible, e.g. the exact name of your\n");
+ DBG (DBG_error0, " scanner and what does (not) work.\n");
+
+ }
+
+ /* open USB link */
+ status = sanei_usb_open (device->file_name, &device->devnum);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "sane_open: couldn't open device `%s': %s\n",
+ device->file_name, sane_strstatus (status));
+ return status;
+ }
+
+ /* device initialization */
+ if (device->initialized == SANE_FALSE)
+ {
+#ifdef FAST_INIT
+ status = detect_device (device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_open: detect_device failed\n");
+ DBG (DBG_proc, "sane_open: exit on error\n");
+ return status;
+ }
+ status = initialize_device (device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_open: initialize_device failed\n");
+ DBG (DBG_proc, "sane_open: exit on error\n");
+ return status;
+ }
+#else
+ status = init_device (device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_open: init_device failed\n");
+ DBG (DBG_proc, "sane_open: exit on error\n");
+ return status;
+ }
+ device->initialized = SANE_TRUE;
+#endif
+ }
+
+ /* prepare handle to return */
+ session = (Rts8891_Session *) malloc (sizeof (Rts8891_Session));
+
+ session->scanning = SANE_FALSE;
+ session->dev = device;
+
+ init_options (session);
+ session->scanning = SANE_FALSE;
+ session->non_blocking = SANE_FALSE;
+ *handle = session;
+
+ /* add the handle to the list */
+ session->next = first_handle;
+ first_handle = session;
+
+ /* release the interface to allow device sharing */
+ if (session->dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (device->devnum, 0);
+ }
+
+ DBG (DBG_proc, "sane_open: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Set non blocking mode. In this mode, read return immediatly when
+ * no data is available, instead of polling the scanner.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Rts8891_Session *session = (Rts8891_Session *) handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: start\n");
+ if (session->scanning != SANE_TRUE)
+ {
+ DBG (DBG_error, "sane_set_io_mode: called out of a scan\n");
+ return SANE_STATUS_INVAL;
+ }
+ session->non_blocking = non_blocking;
+ DBG (DBG_warn, "sane_set_io_mode: I/O mode set to %sblocking.\n",
+ non_blocking ? "non " : " ");
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fdp)
+{
+
+ DBG (DBG_proc, "sane_get_select_fd: start\n");
+ if(handle==0 || fdp==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ DBG (DBG_warn, "sane_get_select_fd: unsupported ...\n");
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+/**
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct Rts8891_Session *session = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: start\n");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ DBG (DBG_info, "sane_get_option_descriptor: \"%s\"\n",
+ session->opt[option].name);
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+ return &(session->opt[option]);
+}
+
+/**
+ * sets automatic value for an option , called by sane_control_option after
+ * all checks have been done */
+static SANE_Status
+set_automatic_value (Rts8891_Session * s, int option, SANE_Int * myinfo)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int i, min;
+ SANE_Word *dpi_list;
+
+ switch (option)
+ {
+ case OPT_TL_X:
+ s->val[OPT_TL_X].w = x_range.min;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_TL_Y:
+ s->val[OPT_TL_Y].w = y_range.min;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BR_X:
+ s->val[OPT_BR_X].w = x_range.max;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BR_Y:
+ s->val[OPT_BR_Y].w = y_range.max;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_RESOLUTION:
+ /* we set up to the lowest available dpi value */
+ dpi_list = (SANE_Word *) s->opt[OPT_RESOLUTION].constraint.word_list;
+ min = 65536;
+ for (i = 1; i < dpi_list[0]; i++)
+ {
+ if (dpi_list[i] < min)
+ min = dpi_list[i];
+ }
+ s->val[OPT_RESOLUTION].w = min;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_THRESHOLD:
+ s->val[OPT_THRESHOLD].w = SANE_FIX (50);
+ break;
+ case OPT_PREVIEW:
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_MODE:
+ if (s->val[OPT_MODE].s)
+ free (s->val[OPT_MODE].s);
+ s->val[OPT_MODE].s = strdup (mode_list[0]);
+ *myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_CUSTOM_GAMMA:
+ s->val[option].b = SANE_FALSE;
+ DISABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ break;
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ if (s->dev->model->gamma != s->val[option].wa)
+ free (s->val[option].wa);
+ s->val[option].wa = s->dev->model->gamma;
+ break;
+ default:
+ DBG (DBG_warn, "set_automatic_value: can't set unknown option %d\n",
+ option);
+ }
+
+ return status;
+}
+
+/**
+ * sets an option , called by sane_control_option after all
+ * checks have been done */
+static SANE_Status
+set_option_value (Rts8891_Session * s, int option, void *val,
+ SANE_Int * myinfo)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int i;
+ SANE_Word tmpw;
+
+ switch (option)
+ {
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ /* we ensure geometry is coherent */
+ /* this happens when user drags TL corner right or below the BR point */
+ if (s->val[OPT_BR_Y].w < s->val[OPT_TL_Y].w)
+ {
+ tmpw = s->val[OPT_BR_Y].w;
+ s->val[OPT_BR_Y].w = s->val[OPT_TL_Y].w;
+ s->val[OPT_TL_Y].w = tmpw;
+ }
+ if (s->val[OPT_BR_X].w < s->val[OPT_TL_X].w)
+ {
+ tmpw = s->val[OPT_BR_X].w;
+ s->val[OPT_BR_X].w = s->val[OPT_TL_X].w;
+ s->val[OPT_TL_X].w = tmpw;
+ }
+
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_RESOLUTION:
+ case OPT_THRESHOLD:
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, LINEART_MODE) == 0)
+ {
+ ENABLE (OPT_THRESHOLD);
+ }
+ else
+ {
+ DISABLE (OPT_THRESHOLD);
+ }
+
+ /* if custom gamma, toggle gamma table options according to the mode */
+ if (s->val[OPT_CUSTOM_GAMMA].b == SANE_TRUE)
+ {
+ if (strcmp (s->val[option].s, COLOR_MODE) == 0)
+ {
+ DISABLE (OPT_GAMMA_VECTOR);
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ else
+ {
+ ENABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ }
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ case OPT_CUSTOM_GAMMA:
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ s->val[OPT_CUSTOM_GAMMA].b = *(SANE_Bool *) val;
+
+ if (s->val[OPT_CUSTOM_GAMMA].b == SANE_TRUE)
+ {
+ if (strcmp (s->val[OPT_MODE].s, COLOR_MODE) == 0)
+ {
+ DISABLE (OPT_GAMMA_VECTOR);
+ ENABLE (OPT_GAMMA_VECTOR_R);
+ ENABLE (OPT_GAMMA_VECTOR_G);
+ ENABLE (OPT_GAMMA_VECTOR_B);
+ }
+ else
+ {
+ ENABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ }
+ else
+ {
+ DISABLE (OPT_GAMMA_VECTOR);
+ DISABLE (OPT_GAMMA_VECTOR_R);
+ DISABLE (OPT_GAMMA_VECTOR_G);
+ DISABLE (OPT_GAMMA_VECTOR_B);
+ }
+ break;
+
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ /* sanity checks */
+ for (i = 0; i < (int) (s->opt[option].size / sizeof (SANE_Word)); i++)
+ {
+ /* avoid 0xaa values since they will be problematic */
+ if (((SANE_Int *) val)[i] == 0xaa)
+ ((SANE_Int *) val)[i] = 0xab;
+ }
+
+ /* free memory from previous set */
+ if (s->dev->model->gamma != s->val[option].wa)
+ free (s->val[option].wa);
+
+ /* then alloc memory */
+ s->val[option].wa = (SANE_Word *) malloc (256 * sizeof (SANE_Word));
+ if (s->val[option].wa == NULL)
+ {
+ s->val[option].wa = s->dev->model->gamma;
+ DBG (DBG_error0,
+ "set_option_value: not enough memory for %lu bytes!\n",
+ (u_long) (256 * sizeof (SANE_Word)));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* data copy */
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ break;
+
+ case OPT_LAMP_ON:
+ return set_lamp_state (s, 1);
+ break;
+
+ case OPT_LAMP_OFF:
+ return set_lamp_state (s, 0);
+ break;
+
+ default:
+ DBG (DBG_warn, "set_option_value: can't set unknown option %d\n",
+ option);
+ }
+ return status;
+}
+
+/**
+ * gets an option , called by sane_control_option after all checks
+ * have been done */
+static SANE_Status
+get_option_value (Rts8891_Session * s, int option, void *val)
+{
+ switch (option)
+ {
+ /* word or word equivalent options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_THRESHOLD:
+ case OPT_CUSTOM_GAMMA:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ case OPT_BUTTON_1:
+ case OPT_BUTTON_2:
+ case OPT_BUTTON_3:
+ case OPT_BUTTON_4:
+ case OPT_BUTTON_5:
+ case OPT_BUTTON_6:
+ case OPT_BUTTON_7:
+ case OPT_BUTTON_8:
+ case OPT_BUTTON_9:
+ case OPT_BUTTON_10:
+ case OPT_BUTTON_11:
+ /* no button pressed by default */
+ *(SANE_Word *) val = SANE_FALSE;
+ if (option - OPT_BUTTON_1 > s->dev->model->buttons)
+ {
+ DBG (DBG_warn,
+ "get_option_value: invalid button option %d\n", option);
+ }
+ else
+ {
+ update_button_status (s);
+ /* copy the button state */
+ *(SANE_Word *) val = s->val[option].w;
+ /* clear the button state */
+ s->val[option].w = SANE_FALSE;
+ DBG (DBG_io,
+ "get_option_value: button option %d=%d\n", option,
+ *(SANE_Word *) val);
+ }
+ break;
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ break;
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ break;
+ default:
+ DBG (DBG_warn, "get_option_value: can't get unknown option %d\n",
+ option);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ * action is SANE_ACTION_GET_VALUE, SANE_ACTION_SET_VALUE or SANE_ACTION_SET_AUTO
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Rts8891_Session *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (DBG_io2,
+ "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
+ SANE_ACTION_SET_VALUE) ?
+ "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->opt[option].name, option);
+
+ if (info)
+ *info = 0;
+
+ /* do checks before trying to apply action */
+
+ if (s->scanning)
+ {
+ DBG (DBG_warn, "sane_control_option: don't call this function while "
+ "scanning (option = %s (%d))\n", s->opt[option].name, option);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* option must be within existing range */
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (DBG_warn,
+ "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* don't access an inactive option */
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (DBG_warn, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* now checks have been done, apply action */
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ status = get_option_value (s, option, val);
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_warn, "sane_control_option: option %d is not settable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn,
+ "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* return immediatly if no change */
+ if (s->opt[option].type == SANE_TYPE_INT
+ && *(SANE_Word *) val == s->val[option].w)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ { /* apply change */
+ status = set_option_value (s, option, val, &myinfo);
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ /* sets automatic values */
+ if (!(cap & SANE_CAP_AUTOMATIC))
+ {
+ DBG (DBG_warn,
+ "sane_control_option: option %d is not autosettable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = set_automatic_value (s, option, &myinfo);
+ break;
+
+ default:
+ DBG (DBG_error, "sane_control_option: invalid action %d\n", action);
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (info)
+ *info = myinfo;
+
+ DBG (DBG_io2, "sane_control_option: exit\n");
+ return status;
+}
+
+/**
+ * returns the name of the sensor
+ * @param sensor sensor numnber
+ * @return string holding the name of the sensor
+ */
+static char *sensor_name (int sensor)
+{
+ switch (sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ return "SENSOR_TYPE_BARE";
+ case SENSOR_TYPE_XPA:
+ return "SENSOR_TYPE_XPA";
+ case SENSOR_TYPE_4400:
+ return "SENSOR_TYPE_4400";
+ case SENSOR_TYPE_4400_BARE:
+ return "SENSOR_TYPE_4400_BARE";
+ default:
+ return "BOGUS";
+ }
+}
+
+/**
+ * Called by SANE when a page acquisition operation is to be started.
+ * @param handle opaque handle to a frontend session
+ * @return SANE_STATUS_GOOD on success, SANE_STATUS_BUSY if the device is
+ * in use by another session or SANE_STATUS_WARMING_UP if the device is
+ * warming up. In this case the fronted as to call sane_start again until
+ * warming up is done. Any other values returned are error status.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct Rts8891_Session *session = handle;
+ int light, mode;
+ struct Rts8891_Device *dev = session->dev;
+ SANE_Status status;
+ SANE_Bool changed;
+#ifdef HAVE_SYS_TIME_H
+ struct timeval current;
+#endif
+
+ DBG (DBG_proc, "sane_start: start\n");
+
+ /* if already scanning, tell we're busy */
+ if (session->scanning == SANE_TRUE)
+ {
+ DBG (DBG_warn, "sane_start: device is already scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* claim the interface reserve device */
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ status = sanei_usb_claim_interface (dev->devnum, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "sane_start: cannot claim usb interface\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ /* check if we need warming-up */
+ sanei_rts88xx_get_lamp_status (dev->devnum, dev->regs);
+ if ((dev->regs[0x8e] & 0x60) != 0x60)
+ {
+ DBG (DBG_info, "sane_start: lamp needs warming (0x%02x)\n",
+ dev->regs[0x8e]);
+ dev->needs_warming = SANE_TRUE;
+#ifdef HAVE_SYS_TIME_H
+ dev->start_time.tv_sec = current.tv_sec;
+ dev->last_scan.tv_sec = current.tv_sec;
+#endif
+ }
+
+#ifdef HAVE_SYS_TIME_H
+ /* if last scan time is more than 10 minutes ago, scanner also needs
+ * warming-up */
+ gettimeofday (&current, NULL);
+ if ((current.tv_sec - dev->last_scan.tv_sec) / 60 > 10)
+ {
+ DBG (DBG_info,
+ "sane_start: more than 10 minutes without scanning, lamp needs warming\n");
+ dev->needs_warming = SANE_TRUE;
+ dev->start_time.tv_sec = current.tv_sec;
+ dev->last_scan.tv_sec = current.tv_sec;
+ }
+#endif
+
+ /* if parking, wait for head to stop */
+ if(dev->parking==SANE_TRUE)
+ {
+ rts8891_wait_for_home (dev, dev->regs);
+ }
+
+ /* reinit registers to start values */
+ rts8891_set_default_regs (dev->regs);
+
+ /* step 0: light up */
+ init_lamp (dev);
+
+ /* do warming up if needed: just detected or at sane_open() */
+ if (dev->needs_warming == SANE_TRUE)
+ {
+ DBG (DBG_info, "sane_start: warming lamp ...\n");
+#ifdef HAVE_SYS_TIME_H
+ /* check if current time is >15s after warming-up start */
+ gettimeofday (&current, NULL);
+ if ((current.tv_sec - dev->start_time.tv_sec) < 15)
+ {
+#ifdef SANE_STATUS_WARMING_UP
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ return SANE_STATUS_WARMING_UP;
+#else
+ DBG (DBG_info,
+ "sane_start: waiting to let lamp get warm enough ...\n");
+ sleep (15 - (current.tv_sec - dev->start_time.tv_sec));
+#endif
+ }
+#else
+ DBG (DBG_info,
+ "sane_start: waiting 15s to let lamp get warm enough ...\n");
+ sleep (15);
+#endif
+ }
+ dev->needs_warming = SANE_FALSE;
+
+ /* restore default brightness */
+ dev->regs[LAMP_BRIGHT_REG] = 0xA7;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_BRIGHT_REG,
+ dev->regs + LAMP_BRIGHT_REG);
+ DBG (DBG_info, "sane_start: sensor initial type is %s (%d)\n",
+ sensor_name (dev->sensor), dev->sensor);
+
+ /* step 1: locate scan area by doing a scan, then goto calibration area */
+ /* we also detect if the sensor type is inadequate and then change it */
+ do
+ {
+ changed = SANE_FALSE;
+ status = find_origin (dev, &changed);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to find origin!\n");
+ return status;
+ }
+
+ /* in case find_origin detected we need to change sensor */
+ if (changed)
+ {
+ /* for a sensor 'pair', we switch */
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_info,
+ "sane_start: sensor changed to type 'SENSOR_TYPE_XPA'!\n");
+ dev->sensor = SENSOR_TYPE_XPA;
+ break;
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_info,
+ "sane_start: sensor changed to type 'SENSOR_TYPE_BARE'!\n");
+ dev->sensor = SENSOR_TYPE_BARE;
+ break;
+ case SENSOR_TYPE_4400:
+ DBG (DBG_info,
+ "sane_start: sensor changed to type '4400 SENSOR_TYPE_4400_BARE'!\n");
+ dev->sensor = SENSOR_TYPE_4400_BARE;
+ break;
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_info,
+ "sane_start: sensor changed to type 'SENSOR_TYPE_4400'!\n");
+ dev->sensor = SENSOR_TYPE_4400;
+ break;
+ }
+ }
+ }
+ while (changed);
+
+ /* light source to use */
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_XPA:
+ light = 0x3f;
+ mode = 0x20;
+ break;
+ case SENSOR_TYPE_BARE:
+ light = 0x3b;
+ mode = 0x20;
+ break;
+ case SENSOR_TYPE_4400:
+ case SENSOR_TYPE_4400_BARE:
+ light = 0x2a;
+ mode = 0x10;
+ break;
+ default:
+ light = 0x3b;
+ mode = 0x20;
+ break;
+ }
+ DBG (DBG_info, "sane_start: sensor final type is %s (%d)\n",
+ sensor_name (dev->sensor), dev->sensor);
+ DBG (DBG_info, "sane_start: mode=0x%02x, light=0x%02x\n", mode, light);
+
+ /* step 2: dark calibration */
+ status = dark_calibration (dev, mode, light);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to do dark calibration!\n");
+ return status;
+ }
+
+ /* step 3: find left margin */
+ status = find_margin (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed find left margin!\n");
+ return status;
+ }
+
+ /* restore brightness */
+ dev->regs[LAMP_BRIGHT_REG] = 0xA7;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_BRIGHT_REG,
+ dev->regs + LAMP_BRIGHT_REG);
+
+ /* step 4: gain calibration */
+ status = gain_calibration (dev, mode, light);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to do gain calibration!\n");
+ return status;
+ }
+
+ /* step 5: fine offset calibration */
+ status = offset_calibration (dev, mode, light);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to do offset calibration!\n");
+ return status;
+ }
+
+ /* we compute all the scan parameters so that */
+ /* we will be able to set up the registers correctly */
+ compute_parameters (session);
+
+ /* allocate buffer and compute start pointer */
+ if (dev->scanned_data != NULL)
+ free (dev->scanned_data);
+ dev->scanned_data =
+ (SANE_Byte *) malloc (dev->data_size + dev->lds_max + dev->ripple);
+ dev->start = dev->scanned_data + dev->lds_max + dev->ripple;
+
+ /* computes data end pointer */
+ dev->end = dev->start + dev->data_size;
+
+ /* no bytes sent yet to frontend */
+ session->sent = 0;
+
+ sanei_rts88xx_set_offset (dev->regs, dev->red_offset, dev->green_offset,
+ dev->blue_offset);
+ sanei_rts88xx_set_gain (dev->regs, dev->red_gain, dev->green_gain,
+ dev->blue_gain);
+
+ /* step 6: shading calibration */
+ status =
+ shading_calibration (dev, session->params.format == SANE_FRAME_RGB
+ || session->emulated_gray == SANE_TRUE, mode, light);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to do shading calibration!\n");
+ return status;
+ }
+
+ /* step 6b: move at dev->model->min_ydpi to target area */
+ if (dev->ydpi > dev->model->min_ydpi
+ && (dev->ystart * MOVE_DPI) / dev->ydpi > 150)
+ {
+ status = move_to_scan_area (session);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to move to scan area!\n");
+ return status;
+ }
+ }
+
+ /* step 7: effective scan start */
+ /* build register set and send it */
+ status = write_scan_registers (session);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to write scan registers!\n");
+ return status;
+ }
+
+ /* step 8: send calibration data */
+ status = send_calibration_data (session);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+ DBG (DBG_error, "sane_start: failed to send calibration data!\n");
+ return status;
+ }
+
+ /* commit the scan */
+ /* TODO send_calibration seems to embbed its commit */
+ sanei_rts88xx_cancel (dev->devnum);
+ sanei_rts88xx_write_control (dev->devnum, 0x08);
+ status = sanei_rts88xx_write_control (dev->devnum, 0x08);
+
+ session->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+ return status;
+}
+
+/**
+ * This function computes two set of parameters. The one for the SANE's standard
+ * and the other for the hardware. Among these parameters are the bit depth, total
+ * number of lines, total number of columns, extra line to read for data reordering...
+ */
+static SANE_Status
+compute_parameters (Rts8891_Session * session)
+{
+ Rts8891_Device *dev = session->dev;
+ SANE_Int dpi; /* dpi for scan */
+ SANE_String mode;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int yshift;
+ SANE_Int xshift;
+
+ int tl_x, tl_y, br_x, br_y;
+
+ mode = session->val[OPT_MODE].s;
+ dpi = session->val[OPT_RESOLUTION].w;
+
+ /* scan coordinates */
+ tl_x = SANE_UNFIX (session->val[OPT_TL_X].w);
+ tl_y = SANE_UNFIX (session->val[OPT_TL_Y].w);
+ br_x = SANE_UNFIX (session->val[OPT_BR_X].w);
+ br_y = SANE_UNFIX (session->val[OPT_BR_Y].w);
+
+ /* only single pass scanning supported */
+ session->params.last_frame = SANE_TRUE;
+ session->emulated_gray = SANE_FALSE;
+
+ /* gray modes */
+ dev->threshold = (255 * SANE_UNFIX (session->val[OPT_THRESHOLD].w)) / 100;
+ if (strcmp (mode, GRAY_MODE) == 0 || strcmp (mode, LINEART_MODE) == 0)
+ {
+ session->params.format = SANE_FRAME_GRAY;
+ if (dev->model->flags & RTS8891_FLAG_EMULATED_GRAY_MODE)
+ session->emulated_gray = SANE_TRUE;
+ }
+ else
+ {
+ /* Color */
+ session->params.format = SANE_FRAME_RGB;
+ }
+
+ /* SANE level values */
+ session->params.lines = ((br_y - tl_y) * dpi) / MM_PER_INCH;
+ if (session->params.lines == 0)
+ session->params.lines = 1;
+ session->params.pixels_per_line = ((br_x - tl_x) * dpi) / MM_PER_INCH;
+ if (session->params.pixels_per_line == 0)
+ session->params.pixels_per_line = 1;
+
+ DBG (DBG_data, "compute_parameters: pixels_per_line =%d\n",
+ session->params.pixels_per_line);
+
+ if (strcmp (mode, LINEART_MODE) == 0)
+ {
+ session->params.depth = 1;
+ /* in lineart, having pixels multiple of 8 avoids a costly test */
+ /* at each bit to see we must go to the next byte */
+ /* TODO : implement this requirement in sane_control_option */
+ session->params.pixels_per_line =
+ ((session->params.pixels_per_line + 7) / 8) * 8;
+ }
+ else
+ session->params.depth = 8;
+
+ /* width needs to be even */
+ if (session->params.pixels_per_line & 1)
+ session->params.pixels_per_line++;
+
+ /* Hardware settings : they can differ from the ones at SANE level */
+ /* for instance the effective DPI used by a sensor may be higher */
+ /* than the one needed for the SANE scan parameters */
+ dev->lines = session->params.lines;
+ dev->pixels = session->params.pixels_per_line;
+
+ /* motor and sensor DPI */
+ dev->xdpi = dpi;
+ dev->ydpi = dpi;
+
+ /* handle bounds of motor's dpi range */
+ if (dev->ydpi > dev->model->max_ydpi)
+ {
+ dev->ydpi = dev->model->max_ydpi;
+ dev->lines = (dev->lines * dev->model->max_ydpi) / dpi;
+ if (dev->lines == 0)
+ dev->lines = 1;
+
+ /* round number of lines */
+ session->params.lines =
+ (session->params.lines / dev->lines) * dev->lines;
+ if (session->params.lines == 0)
+ session->params.lines = 1;
+ }
+ if (dev->ydpi < dev->model->min_ydpi)
+ {
+ dev->ydpi = dev->model->min_ydpi;
+ dev->lines = (dev->lines * dev->model->min_ydpi) / dpi;
+ }
+
+ /* hardware values */
+ dev->xstart =
+ ((SANE_UNFIX (dev->model->x_offset) + tl_x) * dev->xdpi) / MM_PER_INCH;
+ dev->ystart =
+ ((SANE_UNFIX (dev->model->y_offset) + tl_y) * dev->ydpi) / MM_PER_INCH;
+
+ /* xstart needs to be even */
+ if (dev->xstart & 1)
+ dev->xstart++;
+
+ /* computes bytes per line */
+ session->params.bytes_per_line = session->params.pixels_per_line;
+ dev->bytes_per_line = dev->pixels;
+ if (session->params.format == SANE_FRAME_RGB
+ || session->emulated_gray == SANE_TRUE)
+ {
+ if (session->emulated_gray != SANE_TRUE)
+ {
+ session->params.bytes_per_line *= 3;
+ }
+ dev->bytes_per_line *= 3;
+ }
+ session->to_send = session->params.bytes_per_line * session->params.lines;
+
+ /* in lineart mode we adjust bytes_per_line needed by frontend */
+ /* we do that here because we needed sent/to_send to be as if */
+ /* there was no lineart */
+ if (session->params.depth == 1)
+ {
+ session->params.bytes_per_line =
+ (session->params.bytes_per_line + 7) / 8;
+ }
+
+ /* compute how many extra bytes we need to reorder data */
+ dev->ripple = 0;
+ if (session->params.format == SANE_FRAME_RGB
+ || session->emulated_gray == SANE_TRUE)
+ {
+ dev->lds_r
+ = ((dev->model->ld_shift_r * dev->ydpi) / dev->model->max_ydpi)
+ * dev->bytes_per_line;
+ dev->lds_g
+ = ((dev->model->ld_shift_g * dev->ydpi) / dev->model->max_ydpi)
+ * dev->bytes_per_line;
+ dev->lds_b
+ = ((dev->model->ld_shift_b * dev->ydpi) / dev->model->max_ydpi)
+ * dev->bytes_per_line;
+ if (dev->xdpi == dev->model->max_xdpi)
+ {
+ dev->ripple = 2 * dev->bytes_per_line;
+ }
+ }
+ else
+ {
+ dev->lds_r = 0;
+ dev->lds_g = 0;
+ dev->lds_b = 0;
+ }
+
+ /* pick max lds value */
+ dev->lds_max = dev->lds_r;
+ if (dev->lds_g > dev->lds_max)
+ dev->lds_max = dev->lds_g;
+ if (dev->lds_b > dev->lds_max)
+ dev->lds_max = dev->lds_b;
+
+ /* since the extra lines for reordering are before data */
+ /* we substract lds_max */
+ dev->lds_r -= dev->lds_max;
+ dev->lds_g -= dev->lds_max;
+ dev->lds_b -= dev->lds_max;
+
+ /* we need to add extra line to handle line distance reordering */
+ /* highest correction value is the blue one for our case */
+ /* decrease y start to take these extra lines into account */
+ dev->lines += (dev->lds_max + dev->ripple) / dev->bytes_per_line;
+
+ /* shading calibration is allways 66 lines regardless of ydpi, so */
+ /* we take this into account to compute ystart */
+ if (dev->ydpi > dev->model->min_ydpi)
+ {
+ /* no that clean, but works ... */
+ switch (dev->ydpi)
+ {
+ case 300:
+ yshift = 0;
+ break;
+ case 600:
+ yshift = 33;
+ break;
+ default:
+ yshift = 0;
+ }
+ dev->ystart += yshift;
+ }
+ dev->ystart -= (dev->lds_max + dev->ripple) / dev->bytes_per_line;
+
+ /* 1200 and 600 dpi scans seems to have other timings that
+ * need their own xstart */
+ /* no that clean, but works ... */
+ switch (dev->xdpi)
+ {
+ case 600:
+ xshift = -38;
+ break;
+ case 1200:
+ xshift = -76;
+ break;
+ default:
+ xshift = 0;
+ }
+ dev->xstart += xshift;
+
+ dev->read = 0;
+ dev->to_read = dev->lines * dev->bytes_per_line;
+
+ /* compute best size for scanned data buffer */
+ /* we must have a round number of lines, with */
+ /* enough space to handle line distance shift */
+ if (dev->xdpi < dev->model->max_ydpi)
+ {
+ dev->data_size =
+ (PREFERED_BUFFER_SIZE / dev->bytes_per_line) * (dev->bytes_per_line);
+ }
+ else
+ {
+ dev->data_size =
+ (((PREFERED_BUFFER_SIZE / 2) - dev->lds_max -
+ dev->ripple) / dev->bytes_per_line) * dev->bytes_per_line;
+ }
+
+ /* 32 lines minimum in buffer */
+ if (dev->data_size < 32 * dev->bytes_per_line)
+ dev->data_size = 32 * dev->bytes_per_line;
+
+ /* buffer must be smaller than total amount to read from session */
+ if (dev->data_size > dev->to_read)
+ dev->data_size = dev->to_read;
+
+ DBG (DBG_data, "compute_parameters: bytes_per_line =%d\n",
+ session->params.bytes_per_line);
+ DBG (DBG_data, "compute_parameters: depth =%d\n",
+ session->params.depth);
+ DBG (DBG_data, "compute_parameters: lines =%d\n",
+ session->params.lines);
+ DBG (DBG_data, "compute_parameters: pixels_per_line =%d\n",
+ session->params.pixels_per_line);
+ DBG (DBG_data, "compute_parameters: image size =%d\n",
+ session->to_send);
+
+ DBG (DBG_data, "compute_parameters: xstart =%d\n", dev->xstart);
+ DBG (DBG_data, "compute_parameters: ystart =%d\n", dev->ystart);
+ DBG (DBG_data, "compute_parameters: dev lines =%d\n", dev->lines);
+ DBG (DBG_data, "compute_parameters: dev extra lines =%d\n",
+ (dev->lds_max + dev->ripple) / dev->bytes_per_line);
+ DBG (DBG_data, "compute_parameters: dev bytes per line=%d\n",
+ dev->bytes_per_line);
+ DBG (DBG_data, "compute_parameters: dev pixels =%d\n", dev->pixels);
+ DBG (DBG_data, "compute_parameters: data size =%d\n",
+ dev->data_size);
+ DBG (DBG_data, "compute_parameters: to read =%d\n", dev->to_read);
+ DBG (DBG_data, "compute_parameters: threshold =%d\n",
+ dev->threshold);
+
+ return status;
+}
+
+
+/**
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle of the
+ * device for which the parameters should be obtained and a pointer
+ * to a parameter structure.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Status status;
+ struct Rts8891_Session *session = (struct Rts8891_Session *) handle;
+
+ DBG (DBG_proc, "sane_get_parameters: start\n");
+
+ /* call parameters computing function */
+ status = compute_parameters (session);
+ if (status == SANE_STATUS_GOOD && params)
+ *params = session->params;
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+ return status;
+}
+
+
+/**
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ struct Rts8891_Session *session;
+ struct Rts8891_Device *dev;
+ SANE_Status status;
+ SANE_Int length;
+ SANE_Int data_size;
+ SANE_Byte val = 0;
+ SANE_Int bit;
+
+ DBG (DBG_proc, "sane_read: start\n");
+ DBG (DBG_io, "sane_read: up to %d bytes required by frontend\n", max_len);
+
+ /* some sanity checks first to protect from would be buggy frontends */
+ if (!handle)
+ {
+ DBG (DBG_error, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ session = (struct Rts8891_Session *) handle;
+ if (!session)
+ {
+ DBG (DBG_error, "sane_read: session is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ dev = session->dev;
+
+ if (!buf)
+ {
+ DBG (DBG_error, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!len)
+ {
+ DBG (DBG_error, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* no data read yet */
+ *len = 0;
+ buf[*len] = 0;
+
+ /* check if session is scanning */
+ if (!session->scanning)
+ {
+ DBG (DBG_warn,
+ "sane_read: scan was cancelled, is over or has not been initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* check for EOF, must be done before any physical read */
+ if (session->sent >= session->to_send)
+ {
+ DBG (DBG_io, "sane_read: end of scan reached\n");
+
+ /* signal end of scanning */
+ session->scanning=SANE_FALSE;
+
+ /* send asynchronous head parking command then return */
+ park_head (session->dev, SANE_FALSE);
+ return SANE_STATUS_EOF;
+ }
+
+ dev->regs[LAMP_REG] = 0xad;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &(dev->regs[LAMP_REG]));
+
+ /* byte length for high dpi mode */
+ length = (session->params.bytes_per_line * 8) / session->params.depth;
+
+ /* checks if buffer has been pre filled with the extra data needed for */
+ /* line distance reordering */
+ if (dev->read == 0
+ && (session->params.format == SANE_FRAME_RGB
+ || session->emulated_gray == SANE_TRUE))
+ {
+ /* the data need if the size of the highest line distance shift */
+ /* we must only read what's needed */
+ data_size = dev->data_size + dev->lds_max + dev->ripple;
+ if (dev->to_read - dev->read < data_size)
+ data_size = dev->to_read - dev->read;
+
+ status = read_data (session, dev->scanned_data, data_size);
+ if (status == SANE_STATUS_DEVICE_BUSY)
+ {
+ DBG (DBG_io, "sane_read: no data currently available\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_read: failed to read data from scanner\n");
+ return status;
+ }
+
+ /* now buffer is filled with data and an extra amount for line reordering */
+ dev->current = dev->start;
+ }
+
+ /* if all buffer has been used, get the next block of data from scanner */
+ if (dev->read == 0 || dev->current >= dev->end)
+ {
+ /* effective buffer filling */
+ /* we must only read what's needed */
+ data_size = dev->data_size;
+ if (dev->to_read - dev->read < data_size)
+ data_size = dev->to_read - dev->read;
+
+ status = read_data (session, dev->start, data_size);
+ if (status == SANE_STATUS_DEVICE_BUSY)
+ {
+ DBG (DBG_io, "sane_read: no data currently available\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_read: failed to read data from scanner\n");
+ return status;
+ }
+
+ /* reset current pointer */
+ dev->current = dev->start;
+ }
+
+ dev->regs[LAMP_REG] = 0x8d;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &(dev->regs[LAMP_REG]));
+
+ /* it seems there is no gray or lineart hardware mode for the rts8891 */
+ if (session->params.format == SANE_FRAME_GRAY
+ && session->emulated_gray == SANE_FALSE)
+ {
+ DBG (DBG_error0, "sane_read: unimplemented native gray scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ else /* hardware data is 3 bytes/per pixels */
+ {
+ /* we return as much data as possible from current buffer */
+ /* if buffer is smaller than what is asked, we only return */
+
+ /* here we have made sure there is something to read from data buffers */
+ /* data from scanner is in R,G,B format, but we have to cope with line */
+ /* distance correction. We also take care that motor resolution may be */
+ /* higher than the scan resolution asked for some modes. */
+ /* We also handle emulated color mode */
+
+ /* we loop until we reach max_len or end of data buffer */
+ /* or we sent what we advised to frontend */
+ while (dev->current < dev->end && *len < max_len
+ && session->sent < session->to_send)
+ {
+ /* data is received in RGB format */
+ /* these switches are there to handle reads not aligned
+ * on pixels that are allowed by the SANE standard */
+ if (dev->xdpi == dev->model->max_xdpi)
+ { /* at max xdpi, data received is distorted and ydpi is half of xdpi */
+ if (session->emulated_gray == SANE_TRUE)
+ {
+ /* in emulated gray mode we are allways reading 3 bytes of raw data */
+ /* at a time */
+ switch (((session->sent * 3) % dev->bytes_per_line) % 6)
+ {
+ case 0:
+ case 1:
+ case 2:
+ val = dev->current[dev->lds_g];
+ break;
+ case 3:
+ case 4:
+ case 5:
+ val = dev->current[dev->lds_g - dev->ripple];
+ break;
+ }
+
+ if (session->params.depth == 1)
+ {
+ bit = 7 - (session->sent) % 8;
+ if (val <= dev->threshold)
+ {
+ buf[*len] |= 1 << bit;
+ }
+ if (bit == 0)
+ {
+ (*len)++;
+ buf[*len] = 0;
+ }
+ }
+ else
+ {
+ buf[*len] = val;
+ (*len)++;
+ }
+ session->sent++;
+ dev->current += 3;
+ }
+ else
+ {
+ switch ((session->sent % dev->bytes_per_line) % 6)
+ {
+ case 0:
+ buf[*len] = dev->current[dev->lds_r];
+ break;
+ case 1:
+ buf[*len] = dev->current[dev->lds_g];
+ break;
+ case 2:
+ buf[*len] = dev->current[dev->lds_b];
+ break;
+ case 3:
+ buf[*len] = dev->current[dev->lds_r - dev->ripple];
+ break;
+ case 4:
+ buf[*len] = dev->current[dev->lds_g - dev->ripple];
+ break;
+ case 5:
+ buf[*len] = dev->current[dev->lds_b - dev->ripple];
+ break;
+ }
+ (*len)++;
+ session->sent++;
+ dev->current++;
+ }
+
+ /* we currently double lines to have match xdpy */
+ /* so we logically rewind for each odd line */
+ /* we test at start of a scanned picture line */
+ if (((session->sent / length) % 2 == 1)
+ && (session->sent % length == 0))
+ {
+ DBG (DBG_io,
+ "sane_read: rewind by %d bytes after %d bytes sent\n",
+ dev->bytes_per_line, session->sent);
+ dev->current -= dev->bytes_per_line;
+ }
+ }
+ else if (dev->ydpi == session->val[OPT_RESOLUTION].w)
+ {
+ if (session->emulated_gray == SANE_TRUE)
+ {
+ /* in emulated gray mode we are allways reading 3 bytes of raw data */
+ /* at a time, so we know where we are */
+ val = dev->current[dev->lds_g];
+ if (session->params.depth == 1)
+ {
+ bit = 7 - (session->sent) % 8;
+ if (val <= dev->threshold)
+ {
+ buf[*len] |= 1 << bit;
+ }
+ else
+ {
+ buf[*len] &= ~(1 << bit);
+ }
+ if (bit == 0)
+ {
+ (*len)++;
+ }
+ }
+ else
+ {
+ buf[*len] = val;
+ (*len)++;
+ }
+ session->sent++;
+ dev->current += 3;
+ }
+ else
+ {
+ switch ((dev->current - dev->start) % 3)
+ {
+ case 0:
+ buf[*len] = dev->current[dev->lds_r];
+ break;
+ case 1:
+ buf[*len] = dev->current[dev->lds_g];
+ break;
+ case 2:
+ buf[*len] = dev->current[dev->lds_b];
+ break;
+ }
+ (*len)++;
+ session->sent++;
+ dev->current++;
+ }
+ }
+ else
+ {
+ /* we currently handle ydi=2*dpi */
+ if (session->emulated_gray == SANE_TRUE)
+ {
+ /* in emulated gray mode we are allways reading 3 bytes of raw data */
+ /* at a time, so we know where we are */
+ val = (dev->current[dev->lds_g]
+ + dev->current[dev->lds_g +
+ dev->bytes_per_line]) / 2;
+ if (session->params.depth == 1)
+ {
+ bit = 7 - (session->sent) % 8;
+ if (val <= dev->threshold)
+ {
+ buf[*len] |= 1 << bit;
+ }
+ else
+ {
+ buf[*len] &= ~(1 << bit);
+ }
+ if (bit == 0)
+ {
+ (*len)++;
+ }
+ }
+ else
+ {
+ buf[*len] = val;
+ (*len)++;
+ }
+ dev->current += 3;
+ }
+ else
+ {
+ switch ((dev->current - dev->start) % 3)
+ {
+ case 0:
+ buf[*len] = (dev->current[dev->lds_r]
+ + dev->current[dev->lds_r +
+ dev->bytes_per_line]) / 2;
+ break;
+ case 1:
+ buf[*len] = (dev->current[dev->lds_g]
+ + dev->current[dev->lds_g +
+ dev->bytes_per_line]) / 2;
+ break;
+ case 2:
+ buf[*len] = (dev->current[dev->lds_b]
+ + dev->current[dev->lds_b +
+ dev->bytes_per_line]) / 2;
+ break;
+ }
+ (*len)++;
+ dev->current++;
+ }
+ session->sent++;
+
+ /* at the end of each line, we must count another one because */
+ /* 2 lines are used to produce one */
+ if ((dev->current - dev->start) % dev->bytes_per_line == 0)
+ dev->current += dev->bytes_per_line;
+ }
+ }
+ }
+
+ /* if we exhausted data buffer, we prepare for the next read */
+ /* in color mode, we need to copy the remainder of the */
+ /* buffer because of line distance handling, when blue data */
+ /* is exhausted, read and green haven't been fully read yet */
+ if (dev->current >= dev->end
+ && (session->params.format == SANE_FRAME_RGB
+ || session->emulated_gray == SANE_TRUE))
+ {
+ memcpy (dev->scanned_data, dev->end - (dev->lds_max + dev->ripple),
+ dev->lds_max + dev->ripple);
+ }
+
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_io, "sane_read: sent %d bytes to frontend\n", *len);
+ DBG (DBG_proc, "sane_read: exit\n");
+ return status;
+}
+
+
+/**
+ * Cancels a scan.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ Rts8891_Session *session = handle;
+ struct Rts8891_Device *dev = session->dev;
+#ifdef HAVE_SYS_TIME_H
+ struct timeval current;
+#endif
+
+ DBG (DBG_proc, "sane_cancel: start\n");
+
+#ifdef HAVE_SYS_TIME_H
+ /* store last scan time for the device */
+ gettimeofday (&current, NULL);
+ dev->last_scan.tv_sec = current.tv_sec;
+#endif
+
+ /* if scanning, abort and park head */
+ if (session->scanning == SANE_TRUE)
+ {
+ /* canceling while all data hasn't bee read */
+ if (dev->read < dev->to_read)
+ {
+ sanei_rts88xx_cancel (dev->devnum);
+ usleep (500000);
+ sanei_rts88xx_cancel (dev->devnum);
+ }
+ session->scanning = SANE_FALSE;
+
+ /* head parking */
+ if (park_head (dev, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_cancel: failed to park head!\n");
+ }
+ }
+
+ /* free ressources used by scanning */
+ if (dev->scanned_data != NULL)
+ {
+ free (dev->scanned_data);
+ dev->scanned_data = NULL;
+ }
+ if (dev->shading_data != NULL)
+ {
+ free (dev->shading_data);
+ dev->shading_data = NULL;
+ }
+
+ /* release the interface to allow device sharing */
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (dev->devnum, 0);
+ }
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+
+/**
+ * Ends use of the session.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ *
+ * Handle resources are free'd before disposing the handle. But devices
+ * resources must not be mdofied, since it could be used or reused until
+ * sane_exit() is called.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ Rts8891_Session *prev, *session;
+ Rts8891_Device *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_close: start\n");
+
+ /* remove handle from list of open handles: */
+ prev = NULL;
+ for (session = first_handle; session; session = session->next)
+ {
+ if (session == handle)
+ break;
+ prev = session;
+ }
+ if (!session)
+ {
+ DBG (DBG_error, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ dev=session->dev;
+
+ /* cancel any active scan */
+ if (session->scanning == SANE_TRUE)
+ {
+ sane_cancel (handle);
+ }
+ /* if head is parking, wait for completion */
+ if(dev->parking==SANE_TRUE)
+ {
+ rts8891_wait_for_home (dev, dev->regs);
+ }
+ set_lamp_brightness (dev, 0);
+
+
+ if (prev)
+ prev->next = session->next;
+ else
+ first_handle = session->next;
+
+ /* switch off lamp and close usb */
+ if (dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_claim_interface (dev->devnum, 0);
+ }
+ set_lamp_state (session, 0);
+ sanei_usb_close (dev->devnum);
+
+ /* free per session data */
+ if (session->dev->model->gamma != session->val[OPT_GAMMA_VECTOR].wa)
+ free (session->val[OPT_GAMMA_VECTOR].wa);
+ if (session->dev->model->gamma != session->val[OPT_GAMMA_VECTOR_R].wa)
+ free (session->val[OPT_GAMMA_VECTOR_R].wa);
+ if (session->dev->model->gamma != session->val[OPT_GAMMA_VECTOR_G].wa)
+ free (session->val[OPT_GAMMA_VECTOR_G].wa);
+ if (session->dev->model->gamma != session->val[OPT_GAMMA_VECTOR_B].wa)
+ free (session->val[OPT_GAMMA_VECTOR_B].wa);
+ free (session->val[OPT_MODE].s);
+ free (session->opt[OPT_RESOLUTION].constraint.word_list);
+ for (i = OPT_BUTTON_1; i <= OPT_BUTTON_11; i++)
+ {
+ free (session->opt[i].name);
+ free (session->opt[i].title);
+ }
+
+ free (session);
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+
+/**
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct Rts8891_Session *session, *next;
+ struct Rts8891_Device *dev, *nextdev;
+ int i;
+
+ DBG (DBG_proc, "sane_exit: start\n");
+
+ /* free session structs */
+ for (session = first_handle; session; session = next)
+ {
+ next = session->next;
+ sane_close ((SANE_Handle *) session);
+ free (session);
+ }
+ first_handle = NULL;
+
+ /* free devices structs */
+ for (dev = first_device; dev; dev = nextdev)
+ {
+ nextdev = dev->next;
+ free (dev->file_name);
+ free (dev);
+ }
+ first_device = NULL;
+
+ /* now list of devices */
+ if (devlist)
+ {
+ for (i = 0; i < num_devices; i++)
+ {
+ free ((SANE_Device *) devlist[i]);
+ }
+ free (devlist);
+ devlist = NULL;
+ }
+ num_devices = 0;
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
+
+
+/** This function tries to find plugged relevant devices
+ */
+static SANE_Status
+probe_rts8891_devices (void)
+{
+ /**> configuration structure used during attach */
+ SANEI_Config config;
+ /**> list of configuration options */
+ SANE_Option_Descriptor *options[NUM_CFG_OPTIONS];
+ /**> placeholders pointers for option values */
+ void *values[NUM_CFG_OPTIONS];
+ int i;
+ SANE_Status status;
+
+ DBG (DBG_proc, "probe_rts8891_devices: start\n");
+
+ /* sharing is off by default and no model option */
+ rtscfg.allowsharing = SANE_FALSE;
+ rtscfg.modelnumber = -1;
+ rtscfg.sensornumber = -1;
+
+ /* initialize configuration options */
+ options[CFG_MODEL_NUMBER] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_MODEL_NUMBER]->name = "modelnumber";
+ options[CFG_MODEL_NUMBER]->desc =
+ "user provided scanner's internal model number";
+ options[CFG_MODEL_NUMBER]->type = SANE_TYPE_INT;
+ options[CFG_MODEL_NUMBER]->unit = SANE_UNIT_NONE;
+ options[CFG_MODEL_NUMBER]->size = sizeof (SANE_Word);
+ options[CFG_MODEL_NUMBER]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_MODEL_NUMBER]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_MODEL_NUMBER]->constraint.range = &model_range;
+ values[CFG_MODEL_NUMBER] = &rtscfg.modelnumber;
+
+ options[CFG_ALLOW_SHARING] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_ALLOW_SHARING]->name = "allowsharing";
+ options[CFG_ALLOW_SHARING]->desc =
+ "allow sharing of the scanner by several frontends";
+ options[CFG_ALLOW_SHARING]->type = SANE_TYPE_BOOL;
+ options[CFG_ALLOW_SHARING]->unit = SANE_UNIT_NONE;
+ options[CFG_ALLOW_SHARING]->size = sizeof (SANE_Bool);
+ options[CFG_ALLOW_SHARING]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_ALLOW_SHARING]->constraint_type = SANE_CONSTRAINT_NONE;
+ values[CFG_ALLOW_SHARING] = &rtscfg.allowsharing;
+
+ /* sensor type override */
+ options[CFG_SENSOR_NUMBER] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_SENSOR_NUMBER]->name = "sensornumber";
+ options[CFG_SENSOR_NUMBER]->desc =
+ "user provided scanner's internal sensor number";
+ options[CFG_SENSOR_NUMBER]->type = SANE_TYPE_INT;
+ options[CFG_SENSOR_NUMBER]->unit = SANE_UNIT_NONE;
+ options[CFG_SENSOR_NUMBER]->size = sizeof (SANE_Word);
+ options[CFG_SENSOR_NUMBER]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_SENSOR_NUMBER]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_SENSOR_NUMBER]->constraint.range = &sensor_range;
+ values[CFG_SENSOR_NUMBER] = &rtscfg.sensornumber;
+
+ /* set configuration options structure */
+ config.descriptors = options;
+ config.values = values;
+ config.count = NUM_CFG_OPTIONS;
+
+ /* init usb use */
+ sanei_usb_init ();
+
+ /* generic configure and attach function */
+ status = sanei_configure_attach (RTS8891_CONFIG_FILE, &config,
+ config_attach_rts8891);
+ /* free allocated options */
+ for (i = 0; i < NUM_CFG_OPTIONS; i++)
+ {
+ free (options[i]);
+ }
+
+ DBG (DBG_proc, "probe_rts8891_devices: end\n");
+ return status;
+}
+
+/** This function is called by sanei_configure_attach to try
+ * to attach the backend to a device specified by the configuration file.
+ *
+ * @param config configuration structure filled with values read
+ * from configuration file
+ * @param devname name of the device to try to attach to, it is
+ * the unprocessed line of the configuration file
+ *
+ * @return status SANE_STATUS_GOOD if no errors (even if no matching
+ * devices found)
+ * SANE_STATUS_INVAL in case of error
+ */
+static SANE_Status
+config_attach_rts8891 (SANEI_Config * config, const char *devname)
+{
+ /* currently, the config is a global variable so config is useless here */
+ /* the correct thing would be to have a generic sanei_attach_matching_devices
+ * using an attach function with a config parameter */
+ if(config==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ /* the devname has been processed and is ready to be used
+ * directly. Since the backend is an USB only one, we can
+ * call sanei_usb_attach_matching_devices straight */
+ sanei_usb_attach_matching_devices (devname, attach_rts8891);
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * The attach tries to open the given usb device and match it
+ * with devices handled by the backend. The configuration parameter
+ * contains the values of the allready parsed configuration options
+ * from the conf file.
+ * @param config configuration structure filled with values read
+ * from configuration file
+ * @param devicename name of the device to try to attach to, it is
+ * the unprocessed line of the configuration file
+ *
+ * @return status SANE_STATUS_GOOD if no errors (even if no matching
+ * devices found)
+ * SANE_STATUS_NOM_MEM if there isn't enough memory to allocate the
+ * device structure
+ * SANE_STATUS_UNSUPPORTED if the device if unknown by the backend
+ * SANE_STATUS_INVAL in case of other error
+ */
+static SANE_Status
+attach_rts8891 (const char *devicename)
+{
+ struct Rts8891_Device *device;
+ SANE_Int dn, vendor, product;
+ SANE_Status status;
+
+ DBG (DBG_proc, "attach_rts8891(%s): start\n", devicename);
+
+ /* search if we already have it attached */
+ for (device = first_device; device; device = device->next)
+ {
+ if (strcmp (device->file_name, devicename) == 0)
+ {
+ DBG (DBG_warn,
+ "attach_rts8891: device already attached (is ok)!\n");
+ DBG (DBG_proc, "attach_rts8891: exit\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* open usb device */
+ status = sanei_usb_open (devicename, &dn);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "attach_rts8891: couldn't open device `%s': %s\n",
+ devicename, sane_strstatus (status));
+ return status;
+ }
+ else
+ {
+ DBG (DBG_info, "attach_rts8891: device `%s' successfully opened\n",
+ devicename);
+ }
+
+ /* get vendor and product id */
+ status = sanei_usb_get_vendor_product (dn, &vendor, &product);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "attach_rts8891: couldn't get vendor and product ids of device `%s': %s\n",
+ devicename, sane_strstatus (status));
+ sanei_usb_close (dn);
+ DBG (DBG_proc, "attach_rts8891: exit\n");
+ return status;
+ }
+ sanei_usb_close (dn);
+
+ /* get the index of the device in the device description table */
+ /* if the value is provided by configuration option, just use it */
+ if (rtscfg.modelnumber < 0)
+ {
+ /* walk the list of devices to find matching entry */
+ dn = 0;
+ while ((vendor != rts8891_usb_device_list[dn].vendor_id
+ || product != rts8891_usb_device_list[dn].product_id)
+ && (rts8891_usb_device_list[dn].vendor_id != 0))
+ dn++;
+ /* if we reach the list termination entry, the device is unknown */
+ if (rts8891_usb_device_list[dn].vendor_id == 0)
+ {
+ DBG (DBG_info,
+ "attach_rts8891: unknown device `%s': 0x%04x:0x%04x\n",
+ devicename, vendor, product);
+ DBG (DBG_proc, "attach_rts8891: exit\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else
+ {
+ /* user provided value */
+ dn = rtscfg.modelnumber;
+ }
+
+ /* allocate device struct */
+ device = malloc (sizeof (*device));
+ if (device == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (device, 0, sizeof (*device));
+
+ /* struct describing low lvel device */
+ device->model = rts8891_usb_device_list[dn].model;
+
+ /* name of the device */
+ device->file_name = strdup (devicename);
+
+ DBG (DBG_info, "attach_rts8891: found %s %s %s at %s\n",
+ device->model->vendor,
+ device->model->product, device->model->type, device->file_name);
+
+ /* we insert new device at start of the chained list */
+ /* head of the list becomes the next, and start is replaced */
+ /* with the new session struct */
+ num_devices++;
+ device->next = first_device;
+ first_device = device;
+
+ device->reg_count = 244;
+ /* intialization is done at sane_open */
+ device->initialized = SANE_FALSE;
+ device->needs_warming = SANE_TRUE;
+ device->parking = SANE_FALSE;
+#ifdef HAVE_SYS_TIME_H
+ device->last_scan.tv_sec = 0;
+ device->start_time.tv_sec = 0;
+#endif
+
+ /* in case autodection au sensor doesn't work, use the given override */
+ device->sensor = rtscfg.sensornumber;
+
+ /* copy configuration settings to device */
+ device->conf.modelnumber = dn;
+ device->conf.allowsharing = rtscfg.allowsharing;
+
+ DBG (DBG_proc, "attach_rts8891: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/* set initial value for the scanning options */
+static SANE_Status
+init_options (struct Rts8891_Session *session)
+{
+ SANE_Int option, count, i, min, idx;
+ SANE_Word *dpi_list;
+ Rts8891_Model *model = session->dev->model;
+
+ DBG (DBG_proc, "init_options: start\n");
+
+ /* we first initialize each options with a default value */
+ memset (session->opt, 0, sizeof (session->opt));
+ memset (session->val, 0, sizeof (session->val));
+
+ for (option = 0; option < NUM_OPTIONS; option++)
+ {
+ session->opt[option].size = sizeof (SANE_Word);
+ session->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* we set up all the options listed in the Rts8891_Option enum */
+
+ /* last option / end of list marker */
+ session->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ session->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ session->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ session->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ session->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ session->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Standard" group: */
+ session->opt[OPT_STANDARD_GROUP].title = SANE_TITLE_STANDARD;
+ session->opt[OPT_STANDARD_GROUP].name = SANE_NAME_STANDARD;
+ session->opt[OPT_STANDARD_GROUP].desc = SANE_DESC_STANDARD;
+ session->opt[OPT_STANDARD_GROUP].type = SANE_TYPE_GROUP;
+ session->opt[OPT_STANDARD_GROUP].size = 0;
+ session->opt[OPT_STANDARD_GROUP].cap = 0;
+ session->opt[OPT_STANDARD_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ session->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ session->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ session->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ session->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ session->opt[OPT_MODE].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ session->opt[OPT_MODE].size = max_string_size (mode_list);
+ session->opt[OPT_MODE].constraint.string_list = mode_list;
+ session->val[OPT_MODE].s = strdup (mode_list[0]);
+
+ /* preview */
+ session->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ session->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ session->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ session->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ session->opt[OPT_PREVIEW].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ session->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ session->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* build resolution list */
+ /* TODO we build it from xdpi, one could prefer building it from
+ * ydpi list, or even allow for independent X and Y dpi (with 2 options then)
+ */
+ for (count = 0; model->xdpi_values[count] != 0; count++);
+ dpi_list = malloc ((count + 1) * sizeof (SANE_Word));
+ if (!dpi_list)
+ return SANE_STATUS_NO_MEM;
+ dpi_list[0] = count;
+ for (count = 0; model->xdpi_values[count] != 0; count++)
+ dpi_list[count + 1] = model->xdpi_values[count];
+
+ session->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ session->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ session->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ session->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ session->opt[OPT_RESOLUTION].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ session->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ session->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+
+ /* initial value is lowest available dpi */
+ min = 65536;
+ for (i = 1; i < count; i++)
+ {
+ if (dpi_list[i] < min)
+ min = dpi_list[i];
+ }
+ session->val[OPT_RESOLUTION].w = min;
+
+ /* "Geometry" group: */
+ session->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY;
+ session->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
+ session->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY;
+ session->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ session->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ session->opt[OPT_GEOMETRY_GROUP].size = 0;
+ session->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* adapt the constraint range to the detected model */
+ x_range.max = model->x_size;
+ y_range.max = model->y_size;
+
+ /* top-left x */
+ session->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ session->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ session->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ session->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ session->opt[OPT_TL_X].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ session->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_TL_X].constraint.range = &x_range;
+ session->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ session->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ session->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ session->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ session->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ session->opt[OPT_TL_Y].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ session->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_TL_Y].constraint.range = &y_range;
+ session->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ session->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ session->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ session->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ session->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ session->opt[OPT_BR_X].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ session->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_BR_X].constraint.range = &x_range;
+ session->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ session->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ session->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ session->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ session->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ session->opt[OPT_BR_Y].cap |= SANE_CAP_AUTOMATIC;
+ session->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ session->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_BR_Y].constraint.range = &y_range;
+ session->val[OPT_BR_Y].w = y_range.max;
+
+ /* "Enhancement" group: */
+ session->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT;
+ session->opt[OPT_ENHANCEMENT_GROUP].name = SANE_NAME_ENHANCEMENT;
+ session->opt[OPT_ENHANCEMENT_GROUP].desc = SANE_DESC_ENHANCEMENT;
+ session->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ session->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ session->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ session->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* BW threshold */
+ session->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ session->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ session->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ session->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ session->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ session->opt[OPT_THRESHOLD].cap |=
+ SANE_CAP_INACTIVE | SANE_CAP_AUTOMATIC | SANE_CAP_EMULATED;
+ session->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_THRESHOLD].constraint.range = &threshold_percentage_range;
+ session->val[OPT_THRESHOLD].w = SANE_FIX (50);
+
+ /* custom-gamma table */
+ session->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ session->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ session->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ session->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ session->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED;
+ session->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ session->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ session->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ session->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ session->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ session->opt[OPT_GAMMA_VECTOR].cap |=
+ SANE_CAP_INACTIVE | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
+ session->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ session->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ session->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ session->val[OPT_GAMMA_VECTOR].wa = model->gamma;
+
+ /* red gamma vector */
+ session->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ session->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ session->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ session->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ session->opt[OPT_GAMMA_VECTOR_R].cap |=
+ SANE_CAP_INACTIVE | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
+ session->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ session->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ session->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ session->val[OPT_GAMMA_VECTOR_R].wa = model->gamma;
+
+ /* green gamma vector */
+ session->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ session->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ session->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ session->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ session->opt[OPT_GAMMA_VECTOR_G].cap |=
+ SANE_CAP_INACTIVE | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
+ session->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ session->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ session->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ session->val[OPT_GAMMA_VECTOR_G].wa = model->gamma;
+
+ /* blue gamma vector */
+ session->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ session->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ session->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ session->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ session->opt[OPT_GAMMA_VECTOR_B].cap |=
+ SANE_CAP_INACTIVE | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
+ session->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ session->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ session->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ session->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ session->val[OPT_GAMMA_VECTOR_B].wa = model->gamma;
+
+ /* "Sensors" group */
+ session->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS;
+ session->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS;
+ session->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS;
+ session->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP;
+ session->opt[OPT_SENSOR_GROUP].cap = SANE_CAP_ADVANCED;
+ session->opt[OPT_SENSOR_GROUP].size = 0;
+ session->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scanner buttons */
+ for (i = OPT_BUTTON_1; i <= OPT_BUTTON_11; i++)
+ {
+ char name[39];
+ char title[64];
+
+ idx = i - OPT_BUTTON_1;
+
+ if (idx < model->buttons)
+ {
+ sprintf (name, "button-%s", model->button_name[idx]);
+ sprintf (title, "%s", model->button_title[idx]);
+
+ session->opt[i].name = strdup (name);
+ session->opt[i].title = strdup (title);
+ session->opt[i].desc = SANE_I18N ("This option reflects the status "
+ "of a scanner button.");
+ session->opt[i].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ }
+ else
+ {
+ session->opt[i].name = strdup ("unused");
+ session->opt[i].title = strdup ("unused button");
+ session->opt[i].cap |= SANE_CAP_INACTIVE;
+ }
+
+ session->opt[i].type = SANE_TYPE_BOOL;
+ session->opt[i].unit = SANE_UNIT_NONE;
+ session->opt[i].size = sizeof (SANE_Bool);
+ session->opt[i].constraint_type = SANE_CONSTRAINT_NONE;
+ session->opt[i].constraint.range = 0;
+ session->val[i].w = SANE_FALSE;
+ }
+
+ update_button_status (session);
+
+ /* "Advanced" group: */
+ session->opt[OPT_ADVANCED_GROUP].title = SANE_I18N ("Advanced");
+ session->opt[OPT_ADVANCED_GROUP].desc = "";
+ session->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ session->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ session->opt[OPT_ADVANCED_GROUP].size = 0;
+ session->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* lamp on */
+ session->opt[OPT_LAMP_ON].name = "lamp-on";
+ session->opt[OPT_LAMP_ON].title = SANE_I18N ("Lamp on");
+ session->opt[OPT_LAMP_ON].desc = SANE_I18N ("Turn on scanner lamp");
+ session->opt[OPT_LAMP_ON].type = SANE_TYPE_BUTTON;
+ session->opt[OPT_LAMP_ON].cap |= SANE_CAP_ADVANCED;
+ session->opt[OPT_LAMP_ON].unit = SANE_UNIT_NONE;
+ session->opt[OPT_LAMP_ON].size = 0;
+ session->opt[OPT_LAMP_ON].constraint_type = SANE_CONSTRAINT_NONE;
+ session->val[OPT_LAMP_ON].w = 0;
+
+ /* lamp off */
+ session->opt[OPT_LAMP_OFF].name = "lamp-off";
+ session->opt[OPT_LAMP_OFF].title = SANE_I18N ("Lamp off");
+ session->opt[OPT_LAMP_OFF].desc = SANE_I18N ("Turn off scanner lamp");
+ session->opt[OPT_LAMP_OFF].type = SANE_TYPE_BUTTON;
+ session->opt[OPT_LAMP_OFF].cap |= SANE_CAP_ADVANCED;
+ session->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE;
+ session->opt[OPT_LAMP_OFF].size = 0;
+ session->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
+ session->val[OPT_LAMP_OFF].w = 0;
+
+ DBG (DBG_proc, "init_options: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * average scanned rgb data, returns the global average. Each channel avearge is also
+ * returned.
+ */
+static float
+average_area (int color, SANE_Byte * data, int width, int height,
+ float *ra, float *ga, float *ba)
+{
+ int x, y;
+ float global, pixels;
+ float rc, gc, bc;
+ pixels = width * height;
+ *ra = 0;
+ *ga = 0;
+ *ba = 0;
+ rc = 0;
+ gc = 0;
+ bc = 0;
+ if (color == SANE_TRUE)
+ {
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ rc += data[3 * width * y + x];
+ gc += data[3 * width * y + x + 1];
+ bc += data[3 * width * y + x + 2];
+ }
+ }
+ global = (rc + gc + bc) / (3 * pixels);
+ *ra = rc / pixels;
+ *ga = gc / pixels;
+ *ba = bc / pixels;
+ }
+ else
+ {
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ gc += data[width * y + x];
+ }
+ }
+ global = gc / pixels;
+ *ga = gc / pixels;
+ }
+ DBG (7, "average_area: global=%.2f, red=%.2f, green=%.2f, blue=%.2f\n",
+ global, *ra, *ga, *ba);
+ return global;
+}
+
+
+/**
+ * Sets lamp brightness (hum, maybe some timing before light off)
+ */
+static SANE_Status
+set_lamp_brightness (struct Rts8891_Device *dev, int level)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte reg;
+
+ reg = 0xA0 | (level & 0x0F);
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_BRIGHT_REG, &reg);
+ switch (level)
+ {
+ case 0:
+ reg = 0x8d;
+ break;
+ case 7:
+ reg = 0x82;
+ break;
+ default:
+ reg = 0x8d;
+ break;
+ }
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ reg = (reg & 0xF0) | 0x20 | ((~reg) & 0x0F);
+ dev->regs[LAMP_REG] = reg;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_get_status (dev->devnum, dev->regs);
+ DBG (DBG_io, "set_lamp_brightness: status=0x%02x 0x%02x\n", dev->regs[0x10],
+ dev->regs[0x11]);
+ if (dev->sensor != SENSOR_TYPE_4400)
+ {
+ dev->regs[0x10] = 0x28;
+ dev->regs[0x11] = 0x3f;
+ }
+ else
+ {
+ dev->regs[0x10] = 0x10;
+ dev->regs[0x11] = 0x2f;
+ }
+ reg = dev->regs[LAMP_REG];
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ status = sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &reg);
+ if (reg != 0x00)
+ {
+ DBG (DBG_warn,
+ "set_lamp_brightness: unexpected CONTROL_REG value, 0x%02x instead of 0x00\n",
+ reg);
+ }
+ return status;
+}
+
+
+/**
+ * Sets the lamp to half brightness and standard parameters
+ */
+static SANE_Status
+init_lamp (struct Rts8891_Device *dev)
+{
+ SANE_Byte reg;
+
+ sanei_rts88xx_write_control (dev->devnum, 0x01);
+ sanei_rts88xx_write_control (dev->devnum, 0x01);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_cancel (dev->devnum);
+ dev->regs[0x12] = 0xff;
+ dev->regs[0x13] = 0x20;
+ sanei_rts88xx_write_regs (dev->devnum, 0x12, dev->regs + 0x12, 2);
+ /* 0xF8/0x28 expected */
+ sanei_rts88xx_write_regs (dev->devnum, 0x14, dev->regs + 0x14, 2);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ if (dev->sensor != SENSOR_TYPE_4400 && dev->sensor != SENSOR_TYPE_4400_BARE)
+ {
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x28, 0x3f);
+ dev->regs[0x11] = 0x3f;
+ }
+ else
+ {
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x10, 0x22);
+ dev->regs[0x11] = 0x22;
+ }
+ reg = 0x8d;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ dev->regs[LAMP_REG] = 0xa2;
+ dev->regs[LAMP_BRIGHT_REG] = 0xa0;
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+ return set_lamp_brightness (dev, 7);
+}
+
+/**
+ * This function detects physical start of scanning area find finding a black area below the "roof"
+ * if during scan we detect that sensor is inadequate, changed is set to SANE_TRUE
+ */
+static SANE_Status
+find_origin (struct Rts8891_Device *dev, SANE_Bool * changed)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte control, reg;
+ unsigned int max = 0;
+ unsigned char *image = NULL;
+ unsigned char *data = NULL;
+ SANE_Word total;
+ int startx = 300;
+ int width = 1200;
+ int x, y, sum, current;
+ int starty = 18;
+ int height = 180;
+ int timing;
+
+ DBG (DBG_proc, "find_origin: start\n");
+
+ /* check if head is at home
+ * once sensor is correctly set up, we are allways park here,
+ * but in case sensor has just changed, we are not so we park head */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROLER_REG, &reg);
+ if ((reg & 0x02) == 0)
+ {
+ if (park_head (dev, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "find_origin: failed to park head!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_info, "find_origin: head parked\n");
+ }
+
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_warn, "find_origin: unexpected control value 0x%02x\n",
+ control);
+ }
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_warn, "find_origin: unexpected control value 0x%02x\n",
+ control);
+ }
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+
+ /* set lamp to standard settings */
+ init_lamp (dev);
+
+ /* set scan parameters */
+ dev->regs[0x00] = 0xe5;
+ dev->regs[0x32] = 0x80;
+ dev->regs[0x33] = 0x81;
+ dev->regs[0x39] = 0x02;
+ dev->regs[0x64] = 0x01;
+ dev->regs[0x65] = 0x20;
+ dev->regs[0x79] = 0x20;
+ dev->regs[0x7a] = 0x01;
+ dev->regs[0x90] = 0x1c;
+
+ dev->regs[0x35] = 0x1b;
+ dev->regs[0x36] = 0x29;
+ dev->regs[0x3a] = 0x1b;
+ timing=0x32;
+ dev->regs[0x85] = 0x00;
+ dev->regs[0x86] = 0x06;
+ dev->regs[0x87] = 0x00;
+ dev->regs[0x88] = 0x06;
+ dev->regs[0x8d] = 0x80;
+ dev->regs[0x8e] = 0x68;
+
+ dev->regs[0xb2] = 0x02; /* 0x04 -> no data */
+
+ dev->regs[0xc0] = 0xff;
+ dev->regs[0xc1] = 0x0f;
+ dev->regs[0xc3] = 0xff;
+ dev->regs[0xc4] = 0xff;
+ dev->regs[0xc5] = 0xff;
+ dev->regs[0xc6] = 0xff;
+ dev->regs[0xc7] = 0xff;
+ dev->regs[0xc8] = 0xff;
+ dev->regs[0xc9] = 0x00;
+ dev->regs[0xca] = 0x0e;
+ dev->regs[0xcb] = 0x00;
+ dev->regs[0xcd] = 0xf0;
+ dev->regs[0xce] = 0xff;
+ dev->regs[0xcf] = 0xf5;
+ dev->regs[0xd0] = 0xf7;
+ dev->regs[0xd1] = 0xea;
+ dev->regs[0xd2] = 0x0b;
+ dev->regs[0xd3] = 0x03;
+ dev->regs[0xd4] = 0x05;
+ dev->regs[0xd6] = 0xab;
+ dev->regs[0xd7] = 0x30;
+ dev->regs[0xd8] = 0xf6;
+ dev->regs[0xe2] = 0x01;
+ dev->regs[0xe3] = 0x00;
+ dev->regs[0xe4] = 0x00;
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 4124);
+ /* dev->regs[0xe5] = 0x1c;
+ dev->regs[0xe6] = 0x10; 101c=4124 */
+ dev->regs[0xe7] = 0x00;
+ dev->regs[0xe8] = 0x00;
+ dev->regs[0xe9] = 0x00;
+
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0xc3] = 0x00;
+ dev->regs[0xc4] = 0xf0;
+ dev->regs[0xc7] = 0x0f;
+ dev->regs[0xc8] = 0x00;
+ dev->regs[0xc9] = 0xff;
+ dev->regs[0xca] = 0xf1;
+ dev->regs[0xcb] = 0xff;
+ dev->regs[0xd7] = 0x10;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400 || dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ /* 4400 values / 'XPA' values */
+ dev->regs[0x13] = 0x39; /* 0x20 */
+ dev->regs[0x14] = 0xf0; /* 0xf8 */
+ dev->regs[0x15] = 0x29; /* 0x28 */
+ dev->regs[0x16] = 0x0f; /* 0x07 */
+ dev->regs[0x17] = 0x10; /* 0x00 */
+
+ dev->regs[0x23] = 0x00; /* 0xff */
+
+ dev->regs[0x39] = 0x00; /* 0x02 */
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ dev->regs[0xc3] = 0xff; /* 0x00 */
+ dev->regs[0xc4] = 0xff; /* 0xf0 */
+ dev->regs[0xc7] = 0xff; /* 0x0f */
+ dev->regs[0xc8] = 0xff; /* 0x00 */
+ dev->regs[0xc9] = 0x00; /* 0xff */
+ dev->regs[0xca] = 0x0e; /* 0xf1 */
+ dev->regs[0xcb] = 0x00; /* 0xff */
+ dev->regs[0xd7] = 0x30; /* 0x10 */
+ dev->regs[0xda] = 0xa7; /* 0xa0 */
+ }
+ SET_DOUBLE (dev->regs, TIMING_REG, timing);
+ SET_DOUBLE (dev->regs, TIMING1_REG, timing+1);
+ SET_DOUBLE (dev->regs, TIMING2_REG, timing+2);
+
+
+ /* allocate memory for the data */
+ total = width * height;
+ data = (unsigned char *) malloc (total);
+ if (data == NULL)
+ {
+ DBG (DBG_error, "find_origin: failed to allocate %d bytes\n", total);
+ return SANE_STATUS_NO_MEM;
+ }
+ image = (unsigned char *) malloc (total);
+ if (image == NULL)
+ {
+ free(data);
+ DBG (DBG_error, "find_origin: failed to allocate %d bytes\n", total);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* set vertical and horizontal start/end positions */
+ sanei_rts88xx_set_scan_area (dev->regs, starty, starty + height, startx,
+ startx + width);
+ sanei_rts88xx_set_offset (dev->regs, 0x7f, 0x7f, 0x7f);
+ sanei_rts88xx_set_gain (dev->regs, 0x10, 0x10, 0x10);
+
+ /* gray level scan */
+ dev->regs[LAMP_REG] = 0xad;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, dev->regs + LAMP_REG);
+ if (dev->sensor != SENSOR_TYPE_4400
+ && (dev->sensor != SENSOR_TYPE_4400_BARE))
+ {
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x20, 0x3b);
+ }
+ else
+ {
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x10, 0x22);
+ }
+
+ status =
+ rts8891_simple_scan (dev->devnum, dev->regs, dev->reg_count, 0x03,
+ total, data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(image);
+ free(data);
+ DBG (DBG_error, "find_origin: failed to scan\n");
+ return status;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(image);
+ free(data);
+ DBG (DBG_error, "find_origin: failed to wait for data\n");
+ return status;
+ }
+
+ if (DBG_LEVEL > DBG_io2)
+ {
+ write_gray_data (data, "find_origin.pnm", width, height);
+ }
+
+ /* now we have the data, search for the black area so that we can */
+ /* deduce start of scan area */
+ /* we apply an Y direction sobel filter to get reliable edge detection */
+ /* Meanwhile we check that picture is correct, if not, sensor needs to */
+ /* be changed . */
+ /* TODO possibly check for insufficient warming */
+ for (y = 1; y < height - 1; y++)
+ for (x = 1; x < width - 1; x++)
+ {
+ /* if sensor is wrong, picture is black, so max will be low */
+ if (data[y * width + x] > max)
+ max = data[y * width + x];
+
+ /* sobel y filter */
+ current =
+ -data[(y - 1) * width + x + 1] - 2 * data[(y - 1) * width + x] -
+ data[(y - 1) * width + x - 1] + data[(y + 1) * width + x + 1] +
+ 2 * data[(y + 1) * width + x] + data[(y + 1) * width + x - 1];
+ if (current < 0)
+ current = -current;
+ image[y * width + x] = current;
+ }
+ if (DBG_LEVEL > DBG_io2)
+ {
+ write_gray_data (image, "find_origin_ysobel.pnm", width, height);
+ }
+
+ /* sensor test */
+ if (max < 10)
+ {
+ DBG (DBG_info,
+ "find_origin: sensor type needs to be changed (max=%d)\n", max);
+ *changed = SANE_TRUE;
+ }
+ else
+ {
+ *changed = SANE_FALSE;
+ }
+
+ /* the edge will be where the white area stops */
+ /* we average y position of white->black transition on each column */
+ sum = 0;
+ for (x = 1; x < width - 1; x++)
+ {
+ for (y = 1; y < height - 2; y++)
+ {
+ /* egde detection on each line */
+ if (image[x + (y + 1) * width] - image[x + y * width] >= 20)
+ {
+ sum += y;
+ break;
+ }
+ }
+ }
+ sum /= width;
+ DBG (DBG_info, "find_origin: scan area offset=%d lines\n", sum);
+
+ /* convert the detected value into max ydpi */
+ dev->top_offset = (48 * dev->model->max_ydpi) / 300;
+
+ /* no we're done */
+ free (image);
+ free (data);
+
+ /* safe guard test against moving to far toward the top */
+ if (sum > 11)
+ {
+ /* now go back to the white area so that calibration can work on it */
+ dev->regs[0x35] = 0x0e;
+ dev->regs[0x3a] = 0x0e;
+
+ dev->regs[0xb2] = 0x06; /* no data (0x04) */
+
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0x36] = 0x21; /* direction reverse (& ~0x08) */
+
+ dev->regs[0xe2] = 0x03; /* 0x01 */
+
+ /* dev->regs[0xe5] = 0x0d; 0x1c
+ dev->regs[0xe6] = 0x08; 0x10 080d=2061=1030*2+1 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 2061);
+ }
+ else
+ {
+ dev->regs[0x11] = 0x3f; /* 0x3b */
+
+ dev->regs[0x36] = 0x24; /* direction reverse (& ~0x08) */
+
+ dev->regs[0xe2] = 0x07;
+
+ /*
+ dev->regs[0xe5] = 0x06;
+ dev->regs[0xe6] = 0x04; 406=1030 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 1030);
+ }
+
+ /* move by a fixed amount relative to the 'top' of the scanner */
+ sanei_rts88xx_set_scan_area (dev->regs, height - sum + 10,
+ height - sum + 11, 637, 893);
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+ rts8891_commit (dev->devnum, 0x03);
+
+ /* wait for the motor to stop */
+ do
+ {
+ status = sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &reg);
+ }
+ while ((reg & 0x08) == 0x08);
+ }
+
+ DBG (DBG_proc, "find_origin: exit\n");
+ return status;
+}
+
+/**
+ * This function detects the left margin (ie x offset of scanning area) by doing
+ * a grey scan of the very beginning of the sensor and finding the start of the
+ * white area of calibration zone, which is start of usable pixels.
+ */
+static SANE_Status
+find_margin (struct Rts8891_Device *dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char *data = NULL;
+ SANE_Word total;
+ int startx = 33;
+ int width = 250;
+ int x;
+ int starty = 1;
+ int height = 1;
+ SANE_Byte reg = 0xa2;
+ int timing=0;
+
+ DBG (DBG_proc, "find_margin: start\n");
+
+ /* increase brightness to have better margin detection */
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_BRIGHT_REG, &reg);
+
+ /* maximum gain, offsets untouched */
+ if (dev->sensor == SENSOR_TYPE_4400 || dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x10, 0x23);
+ }
+ else
+ {
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x28, 0x3b);
+ }
+
+ sanei_rts88xx_set_gain (dev->regs, 0x3f, 0x3f, 0x3f);
+
+ /* set scan parameters */
+ dev->regs[0x33] = 0x01;
+ dev->regs[0x34] = 0x10;
+ dev->regs[0x35] = 0x1b;
+ dev->regs[0x3a] = 0x1b;
+
+ dev->regs[0x72] = 0x3a;
+ dev->regs[0x73] = 0x15;
+ dev->regs[0x74] = 0x62;
+
+ timing=0;
+
+ dev->regs[0xc0] = 0xff;
+ dev->regs[0xc1] = 0xff;
+ dev->regs[0xc2] = 0xff;
+ dev->regs[0xc3] = 0x00;
+ dev->regs[0xc4] = 0xf0;
+ dev->regs[0xc7] = 0x0f;
+ dev->regs[0xc8] = 0x00;
+ dev->regs[0xcb] = 0xe0;
+ dev->regs[0xcc] = 0xff;
+ dev->regs[0xcd] = 0xff;
+ dev->regs[0xce] = 0xff;
+ dev->regs[0xcf] = 0xe9;
+ dev->regs[0xd0] = 0xeb;
+ dev->regs[0xd3] = 0x0c;
+ dev->regs[0xd4] = 0x0e;
+ dev->regs[0xd6] = 0xab;
+ dev->regs[0xd7] = 0x14;
+ dev->regs[0xd8] = 0xf6;
+
+ dev->regs[0xda] = 0xa7; /* XXX STEF XXX à l'origine, pas 'bare' */
+
+ dev->regs[0xe2] = 0x01;
+
+ /* dev->regs[0xe5] = 0x7b;
+ dev->regs[0xe6] = 0x15; 157b=5499 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 5499);
+
+ dev->regs[0xe7] = 0x00;
+ dev->regs[0xe8] = 0x00;
+ dev->regs[0xe9] = 0x00;
+ dev->regs[0xea] = 0x00;
+ dev->regs[0xeb] = 0x00;
+ dev->regs[0xec] = 0x00;
+ dev->regs[0xed] = 0x00;
+ dev->regs[0xef] = 0x00;
+ dev->regs[0xf0] = 0x00;
+ dev->regs[0xf2] = 0x00;
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0xc0] = 0x00;
+ dev->regs[0xc1] = 0xf8;
+ dev->regs[0xc2] = 0x7f;
+ dev->regs[0xc4] = 0xf8;
+ dev->regs[0xc5] = 0x7f;
+ dev->regs[0xc6] = 0x00;
+ dev->regs[0xc7] = 0xf8;
+ dev->regs[0xc8] = 0x7f;
+ dev->regs[0xc9] = 0xff;
+ dev->regs[0xca] = 0xff;
+ dev->regs[0xcb] = 0x8f;
+ dev->regs[0xcd] = 0x07;
+ dev->regs[0xce] = 0x80;
+ dev->regs[0xcf] = 0xea;
+ dev->regs[0xd0] = 0xec;
+ dev->regs[0xd1] = 0xf7;
+ dev->regs[0xd2] = 0x00;
+ dev->regs[0xd3] = 0x10;
+ dev->regs[0xd4] = 0x12;
+ dev->regs[0xd7] = 0x31;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ dev->regs[0x13] = 0x39; /* 0x20 */
+ dev->regs[0x14] = 0xf0; /* 0xf8 */
+ dev->regs[0x15] = 0x29; /* 0x28 */
+ dev->regs[0x16] = 0x0f; /* 0x07 */
+ dev->regs[0x17] = 0x10; /* 0x00 */
+ dev->regs[0x23] = 0x00; /* 0xff */
+ dev->regs[0x39] = 0x00; /* 0x02 */
+ dev->regs[0x85] = 0x46; /* 0x00 */
+ dev->regs[0x86] = 0x0b; /* 0x06 */
+ dev->regs[0x87] = 0x8c; /* 0x00 */
+ dev->regs[0x88] = 0x10; /* 0x06 */
+ dev->regs[0x8d] = 0x3b; /* 0x00 */
+ timing=0x00b0;
+ }
+ SET_DOUBLE (dev->regs, TIMING_REG, timing);
+ SET_DOUBLE (dev->regs, TIMING1_REG, timing+1);
+ SET_DOUBLE (dev->regs, TIMING2_REG, timing+2);
+
+ /* set vertical and horizontal start/end positions */
+ sanei_rts88xx_set_scan_area (dev->regs, starty, starty + height, startx,
+ startx + width);
+
+ /* allocate memory for the data */
+ total = width * height;
+ data = (unsigned char *) malloc (total);
+ if (data == NULL)
+ {
+ DBG (DBG_error, "find_margin: failed to allocate %d bytes\n", total);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* gray level scan */
+ status =
+ rts8891_simple_scan (dev->devnum, dev->regs, dev->reg_count, 0x0c,
+ total, data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free(data);
+ DBG (DBG_error, "find_margin: failed to scan\n");
+ return status;
+ }
+
+ if (DBG_LEVEL > DBG_io2)
+ {
+ write_gray_data (data, "find_margin.pnm", width, height);
+ }
+
+ /* we search from left to right the first white pixel */
+ x = 0;
+ while (x < width && data[x] < MARGIN_LEVEL)
+ x++;
+ if (x == width)
+ {
+ DBG (DBG_warn, "find_margin: failed to find left margin!\n");
+ DBG (DBG_warn, "find_margin: using default...\n");
+ x = 48 + 40;
+ }
+ DBG (DBG_info, "find_margin: scan area margin=%d pixels\n", x);
+
+ /* convert the detected value into max ydpi */
+ dev->left_offset = ((x - 40) * dev->model->max_xdpi) / 150;
+ DBG (DBG_info, "find_margin: left_offset=%d pixels\n", x);
+
+ /* no we're done */
+ free (data);
+
+ DBG (DBG_proc, "find_margin: exit\n");
+ return status;
+}
+
+#ifdef FAST_INIT
+/*
+ * This function intializes the device:
+ * - initial registers values
+ * - test if at home
+ * - head parking if needed
+ */
+static SANE_Status
+initialize_device (struct Rts8891_Device *dev)
+{
+ SANE_Status status;
+ SANE_Byte reg, control;
+
+ DBG (DBG_proc, "initialize_device: start\n");
+ if (dev->initialized == SANE_TRUE)
+ return SANE_STATUS_GOOD;
+
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x20, 0x28);
+ sanei_rts88xx_write_control (dev->devnum, 0x01);
+ sanei_rts88xx_write_control (dev->devnum, 0x01);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+
+ sanei_rts88xx_read_reg (dev->devnum, 0xb0, &control);
+ DBG (DBG_io, "initialize_device: reg[0xb0]=0x%02x\n", control);
+
+ /* we expect to get 0x80 */
+ if (control != 0x80)
+ {
+ DBG (DBG_warn,
+ "initialize_device: expected reg[0xb0]=0x80, got 0x%02x\n",
+ control);
+ /* TODO fail here ??? */
+ }
+
+ /* get lamp status */
+ status = sanei_rts88xx_get_lamp_status (dev->devnum, dev->regs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "initialize_device: failed to read lamp status!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_io, "initialize_device: lamp status=0x%02x\n", dev->regs[0x8e]);
+
+ /* sensor type the one for 4470c sold with XPA is slightly different
+ * than those sold bare, for this model we allways start with xpa type sensor,
+ * and change it later if we detect black scans in find_origin(). In case the
+ * attach function set up the sensor type, we don't modify it */
+ if (dev->sensor == -1)
+ {
+ dev->sensor = device->model->sensor;
+ }
+ DBG (DBG_info, "initialize_device: initial sensor type is %d\n", dev->sensor);
+ DBG (DBG_info, "initialize_device: reg[8e]=0x%02x\n", dev->regs[0x8e]);
+
+ /* detects if warming up is needed */
+ if ((dev->regs[0x8e] & 0x60) != 0x60)
+ {
+ DBG (DBG_info, "initialize_device: lamp needs warming\n");
+ dev->needs_warming = SANE_TRUE;
+ }
+ else
+ {
+ dev->needs_warming = SANE_FALSE;
+ }
+
+ sanei_rts88xx_read_reg (dev->devnum, LAMP_REG, &reg);
+
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+
+ /* read scanner present register */
+ sanei_rts88xx_read_reg (dev->devnum, LINK_REG, &control);
+ if (control != 0x00 && control != 0x01)
+ {
+ DBG (DBG_warn,
+ "initialize_device: unexpected LINK_REG=0x%02x\n", control);
+ }
+
+ /* head parking if needed */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROLER_REG, &control);
+ if (!(control & 0x02))
+ {
+ if (park_head (dev, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "initialize_device: failed to park head!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* writes initial register set */
+ dev->regs[0x00] = 0xe5; /* 0xf5 */
+ dev->regs[0x02] = 0x1f; /* 0x00 */
+ dev->regs[0x03] = 0x1f; /* 0x00 */
+ dev->regs[0x04] = 0x1f; /* 0x00 */
+ dev->regs[0x05] = 0x1f; /* 0x00 */
+ dev->regs[0x06] = 0x1f; /* 0x00 */
+ dev->regs[0x07] = 0x1f; /* 0x00 */
+ dev->regs[0x08] = 0x0a; /* 0x00 */
+ dev->regs[0x09] = 0x0a; /* 0x00 */
+ dev->regs[0x0a] = 0x0a; /* 0x00 */
+ dev->regs[0x10] = 0x28; /* 0x60 */
+ dev->regs[0x11] = 0x28; /* 0x1b */
+ dev->regs[0x13] = 0x20; /* 0x3e */
+ dev->regs[0x14] = 0xf8; /* 0x00 */
+ dev->regs[0x15] = 0x28; /* 0x00 */
+ dev->regs[0x16] = 0x07; /* 0xff */
+ dev->regs[0x17] = 0x00; /* 0x3e */
+ dev->regs[0x18] = 0xff; /* 0x00 */
+ dev->regs[0x1d] = 0x20; /* 0x22 */
+
+ /* LCD display */
+ dev->regs[0x20] = 0x3a; /* 0x00 */
+ dev->regs[0x21] = 0xf2; /* 0x00 */
+ dev->regs[0x24] = 0xff; /* 0x00 */
+ dev->regs[0x25] = 0x00; /* 0xff */
+
+ dev->regs[0x30] = 0x00; /* 0x01 */
+ dev->regs[0x31] = 0x00; /* 0x42 */
+ dev->regs[0x36] = 0x07; /* 0x00 */
+ dev->regs[0x39] = 0x00; /* 0x02 */
+ dev->regs[0x40] = 0x20; /* 0x80 */
+ dev->regs[0x44] = 0x8c; /* 0x18 */
+ dev->regs[0x45] = 0x76; /* 0x00 */
+ dev->regs[0x50] = 0x00; /* 0x20 */
+ dev->regs[0x51] = 0x00; /* 0x24 */
+ dev->regs[0x52] = 0x00; /* 0x04 */
+ dev->regs[0x64] = 0x00; /* 0x10 */
+ dev->regs[0x68] = 0x00; /* 0x10 */
+ dev->regs[0x6a] = 0x00; /* 0x01 */
+ dev->regs[0x70] = 0x00; /* 0x20 */
+ dev->regs[0x71] = 0x00; /* 0x20 */
+ dev->regs[0x72] = 0xe1; /* 0x00 */
+ dev->regs[0x73] = 0x14; /* 0x00 */
+ dev->regs[0x74] = 0x18; /* 0x00 */
+ dev->regs[0x75] = 0x15; /* 0x00 */
+ dev->regs[0x76] = 0x00; /* 0x20 */
+ dev->regs[0x77] = 0x00; /* 0x01 */
+ dev->regs[0x79] = 0x00; /* 0x02 */
+ dev->regs[0x81] = 0x00; /* 0x41 */
+ dev->regs[0x83] = 0x00; /* 0x10 */
+ dev->regs[0x84] = 0x00; /* 0x21 */
+ dev->regs[0x85] = 0x00; /* 0x20 */
+ dev->regs[0x87] = 0x00; /* 0x20 */
+ dev->regs[0x88] = 0x00; /* 0x81 */
+ dev->regs[0x89] = 0x00; /* 0x20 */
+ dev->regs[0x8a] = 0x00; /* 0x01 */
+ dev->regs[0x8b] = 0x00; /* 0x01 */
+ dev->regs[0x8d] = 0x80; /* 0x22 */
+ dev->regs[0x8e] = 0x68; /* 0x00 */
+ dev->regs[0x8f] = 0x00; /* 0x40 */
+ dev->regs[0x90] = 0x00; /* 0x05 */
+ dev->regs[0x93] = 0x02; /* 0x01 */
+ dev->regs[0x94] = 0x0e; /* 0x1e */
+ dev->regs[0xb0] = 0x00; /* 0x80 */
+ dev->regs[0xb2] = 0x02; /* 0x06 */
+ dev->regs[0xc0] = 0xff; /* 0x00 */
+ dev->regs[0xc1] = 0x0f; /* 0x00 */
+ dev->regs[0xc3] = 0xff; /* 0x00 */
+ dev->regs[0xc4] = 0xff; /* 0x00 */
+ dev->regs[0xc5] = 0xff; /* 0x00 */
+ dev->regs[0xc6] = 0xff; /* 0x00 */
+ dev->regs[0xc7] = 0xff; /* 0x00 */
+ dev->regs[0xc8] = 0xff; /* 0x00 */
+ dev->regs[0xca] = 0x0e; /* 0x00 */
+ dev->regs[0xcd] = 0xf0; /* 0x00 */
+ dev->regs[0xce] = 0xff; /* 0x00 */
+ dev->regs[0xcf] = 0xf5; /* 0x00 */
+ dev->regs[0xd0] = 0xf7; /* 0x00 */
+ dev->regs[0xd1] = 0xea; /* 0x00 */
+ dev->regs[0xd2] = 0x0b; /* 0x00 */
+ dev->regs[0xd3] = 0x03; /* 0x00 */
+ dev->regs[0xd4] = 0x05; /* 0x01 */
+ dev->regs[0xd5] = 0x86; /* 0x06 */
+ dev->regs[0xd7] = 0x30; /* 0x10 */
+ dev->regs[0xd8] = 0xf6; /* 0x7a */
+ dev->regs[0xd9] = 0x80; /* 0x00 */
+ dev->regs[0xda] = 0x00; /* 0x15 */
+ dev->regs[0xe2] = 0x01; /* 0x00 */
+ /* dev->regs[0xe5] = 0x14; 0x0f */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 20);
+
+ status = rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+
+ DBG (DBG_proc, "initialize_device: exit\n");
+ dev->initialized = SANE_TRUE;
+
+ return status;
+}
+#else /* FAST_INIT */
+
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+init_registers (struct Rts8891_Device *dev)
+{
+int i;
+
+ /* initial set written to scanner
+ * 0xe5 0x41 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x0a 0x0a 0x0a 0x70 0x00 0x00 0x00 0x00 0x60 0x1b 0x08 0x20 0x00 0x20 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x20 0x00 0x00 0x3a 0xf2 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x00 0x07 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x20 0x00 0x00 0x00 0x8c 0x76 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x68 0x00 0x00 0x00 0x00 0x02 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xcc 0x27 0x64 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 ---- 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xe0 0x00 0x00 0x00 0x00 0x86 0x1b 0x00 0xff 0x00 0x27 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x14 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ */
+ /* lengthy set up to make register content obvious, may be some day I will
+ * group corresponding values between models */
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ case SENSOR_TYPE_XPA:
+ dev->regs[0x01] = 0x41;
+ dev->regs[0x0b] = 0x70;
+ dev->regs[0x0c] = 0x00;
+ dev->regs[0x0d] = 0x00;
+ dev->regs[0x0e] = 0x00;
+ dev->regs[0x0f] = 0x00;
+
+ dev->regs[0x12] = 0x00;
+ dev->regs[0x13] = 0x20;
+ dev->regs[0x14] = 0x00;
+ dev->regs[0x15] = 0x20;
+ dev->regs[0x16] = 0x00;
+ dev->regs[0x17] = 0x00;
+ dev->regs[0x18] = 0x00;
+ dev->regs[0x19] = 0x00;
+ dev->regs[0x1a] = 0x00;
+ dev->regs[0x1b] = 0x00;
+ dev->regs[0x1c] = 0x00;
+ dev->regs[0x1d] = 0x20;
+ dev->regs[0x1e] = 0x00;
+ dev->regs[0x1f] = 0x00;
+
+ /* LCD display */
+ dev->regs[0x20] = 0x3a;
+ dev->regs[0x21] = 0xf2;
+ dev->regs[0x22] = 0x00;
+
+ dev->regs[0x23] = 0x00;
+ dev->regs[0x24] = 0x00;
+ dev->regs[0x25] = 0x00;
+ dev->regs[0x26] = 0x00;
+ dev->regs[0x27] = 0x00;
+ dev->regs[0x28] = 0x00;
+ dev->regs[0x29] = 0x00;
+ dev->regs[0x2a] = 0x00;
+ dev->regs[0x2b] = 0x00;
+ dev->regs[0x2c] = 0x00;
+ dev->regs[0x2d] = 0x00;
+ dev->regs[0x2e] = 0x00;
+ dev->regs[0x2f] = 0x00;
+ dev->regs[0x30] = 0x00;
+ dev->regs[0x31] = 0x00;
+ dev->regs[0x32] = 0x00;
+ dev->regs[0x33] = 0x00;
+ dev->regs[0x34] = 0x10;
+ dev->regs[0x35] = 0x00;
+ dev->regs[0x36] = 0x07;
+ dev->regs[0x37] = 0x00;
+ dev->regs[0x38] = 0x00;
+ dev->regs[0x39] = 0x00;
+ dev->regs[0x3a] = 0x00;
+ dev->regs[0x3b] = 0x00;
+ dev->regs[0x3c] = 0x00;
+ dev->regs[0x3d] = 0x00;
+ dev->regs[0x3e] = 0x00;
+ dev->regs[0x3f] = 0x00;
+ dev->regs[0x40] = 0x20;
+ dev->regs[0x41] = 0x00;
+ dev->regs[0x42] = 0x00;
+ dev->regs[0x43] = 0x00;
+ dev->regs[0x44] = 0x8c;
+ dev->regs[0x45] = 0x76;
+ dev->regs[0x46] = 0x00;
+ dev->regs[0x47] = 0x00;
+ dev->regs[0x48] = 0x00;
+ dev->regs[0x49] = 0x00;
+ dev->regs[0x4a] = 0x00;
+ dev->regs[0x4b] = 0x00;
+ dev->regs[0x4c] = 0x00;
+ dev->regs[0x4d] = 0x00;
+ dev->regs[0x4e] = 0x00;
+ dev->regs[0x4f] = 0x00;
+ dev->regs[0x50] = 0x00;
+ dev->regs[0x51] = 0x00;
+ dev->regs[0x52] = 0x00;
+ dev->regs[0x53] = 0x00;
+ dev->regs[0x54] = 0x00;
+ dev->regs[0x55] = 0x00;
+ dev->regs[0x56] = 0x00;
+ dev->regs[0x57] = 0x00;
+ dev->regs[0x58] = 0x00;
+ dev->regs[0x59] = 0x00;
+ dev->regs[0x5a] = 0x00;
+ dev->regs[0x5b] = 0x00;
+ dev->regs[0x5c] = 0x00;
+ dev->regs[0x5d] = 0x00;
+ dev->regs[0x5e] = 0x00;
+ dev->regs[0x5f] = 0x00;
+ dev->regs[0x64] = 0x00;
+ dev->regs[0x65] = 0x00;
+ dev->regs[0x66] = 0x00;
+ dev->regs[0x67] = 0x00;
+ dev->regs[0x68] = 0x00;
+ dev->regs[0x69] = 0x00;
+ dev->regs[0x6a] = 0x00;
+ dev->regs[0x6b] = 0x00;
+ dev->regs[0x6e] = 0x00;
+ dev->regs[0x6f] = 0x00;
+ dev->regs[0x70] = 0x00;
+ dev->regs[0x71] = 0x00;
+ dev->regs[0x72] = 0x00;
+ dev->regs[0x73] = 0x00;
+ dev->regs[0x74] = 0x00;
+ dev->regs[0x75] = 0x00;
+ dev->regs[0x76] = 0x00;
+ dev->regs[0x77] = 0x00;
+ dev->regs[0x78] = 0x00;
+ dev->regs[0x79] = 0x00;
+ dev->regs[0x7a] = 0x00;
+ dev->regs[0x7b] = 0x00;
+ dev->regs[0x7c] = 0x00;
+ dev->regs[0x7d] = 0x00;
+ dev->regs[0x7e] = 0x00;
+ dev->regs[0x7f] = 0x00;
+ dev->regs[0x80] = 0x00;
+ dev->regs[0x81] = 0x00;
+ dev->regs[0x82] = 0x00;
+ dev->regs[0x83] = 0x00;
+ dev->regs[0x84] = 0x00;
+ dev->regs[0x85] = 0x00;
+ dev->regs[0x86] = 0x00;
+ dev->regs[0x87] = 0x00;
+ dev->regs[0x88] = 0x00;
+ dev->regs[0x89] = 0x00;
+ dev->regs[0x8a] = 0x00;
+ dev->regs[0x8b] = 0x00;
+ dev->regs[0x8c] = 0x00;
+ dev->regs[0x8d] = 0x80;
+ dev->regs[0x8e] = 0x68;
+ dev->regs[0x8f] = 0x00;
+ dev->regs[0x90] = 0x00;
+ dev->regs[0x91] = 0x00;
+ dev->regs[0x92] = 0x00;
+ dev->regs[0x93] = 0x02;
+ dev->regs[0x94] = 0x0e;
+ dev->regs[0x95] = 0x00;
+ dev->regs[0x96] = 0x00;
+ dev->regs[0x97] = 0x00;
+ dev->regs[0x98] = 0x00;
+ dev->regs[0x99] = 0x00;
+ dev->regs[0x9a] = 0x00;
+ dev->regs[0x9b] = 0x00;
+ dev->regs[0x9c] = 0x00;
+ dev->regs[0x9d] = 0x00;
+ dev->regs[0x9e] = 0x00;
+ dev->regs[0x9f] = 0x00;
+ dev->regs[0xa0] = 0x00;
+ dev->regs[0xa1] = 0x00;
+ dev->regs[0xa2] = 0x00;
+ dev->regs[0xa3] = 0xcc;
+ dev->regs[0xa4] = 0x27;
+ dev->regs[0xa5] = 0x64;
+ dev->regs[0xa6] = 0x00;
+ dev->regs[0xa7] = 0x00;
+ dev->regs[0xa8] = 0x00;
+ dev->regs[0xa9] = 0x00;
+ dev->regs[0xaa] = 0x00;
+ dev->regs[0xab] = 0x00;
+ dev->regs[0xac] = 0x00;
+ dev->regs[0xad] = 0x00;
+ dev->regs[0xae] = 0x00;
+ dev->regs[0xaf] = 0x00;
+ dev->regs[0xb0] = 0x00;
+ dev->regs[0xb1] = 0x00;
+ dev->regs[0xb2] = 0x02;
+ dev->regs[0xb4] = 0x00;
+ dev->regs[0xb5] = 0x00;
+ dev->regs[0xb6] = 0x00;
+ dev->regs[0xb7] = 0x00;
+ dev->regs[0xb8] = 0x00;
+ dev->regs[0xb9] = 0x00;
+ dev->regs[0xba] = 0x00;
+ dev->regs[0xbb] = 0x00;
+ dev->regs[0xbc] = 0x00;
+ dev->regs[0xbd] = 0x00;
+ dev->regs[0xbe] = 0x00;
+ dev->regs[0xbf] = 0x00;
+ dev->regs[0xc0] = 0x00;
+ dev->regs[0xc1] = 0x00;
+ dev->regs[0xc2] = 0x00;
+ dev->regs[0xc3] = 0x00;
+ dev->regs[0xc4] = 0x00;
+ dev->regs[0xc5] = 0x00;
+ dev->regs[0xc6] = 0x00;
+ dev->regs[0xc7] = 0x00;
+ dev->regs[0xc8] = 0x00;
+ dev->regs[0xc9] = 0x00;
+ dev->regs[0xca] = 0x00;
+ dev->regs[0xcb] = 0x00;
+ dev->regs[0xcc] = 0x00;
+ dev->regs[0xcd] = 0x00;
+ dev->regs[0xce] = 0x00;
+ dev->regs[0xcf] = 0x00;
+ dev->regs[0xd0] = 0x00;
+ dev->regs[0xd1] = 0x00;
+ dev->regs[0xd2] = 0x00;
+ dev->regs[0xd3] = 0x00;
+ dev->regs[0xd4] = 0x00;
+ dev->regs[0xd5] = 0x86;
+ dev->regs[0xd6] = 0x1b;
+ dev->regs[0xd7] = 0x00;
+ dev->regs[0xd8] = 0xff;
+ dev->regs[0xd9] = 0x00;
+ dev->regs[0xda] = 0x27;
+ dev->regs[0xdb] = 0x00;
+ dev->regs[0xdc] = 0x00;
+ dev->regs[0xdd] = 0x00;
+ dev->regs[0xde] = 0x00;
+ dev->regs[0xdf] = 0x00;
+ dev->regs[0xe0] = 0x00;
+ dev->regs[0xe1] = 0x00;
+ dev->regs[0xe2] = 0x01;
+ dev->regs[0xe3] = 0x00;
+ dev->regs[0xe4] = 0x00;
+
+ /*dev->regs[0xe5] = 0x14;
+ dev->regs[0xe6] = 0x00; 14=20 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 20);
+
+ dev->regs[0xe7] = 0x00;
+ dev->regs[0xe8] = 0x00;
+ dev->regs[0xe9] = 0x00;
+ dev->regs[0xea] = 0x00;
+ dev->regs[0xeb] = 0x00;
+ dev->regs[0xec] = 0x00;
+ dev->regs[0xed] = 0x00;
+ dev->regs[0xee] = 0x00;
+ dev->regs[0xef] = 0x00;
+ dev->regs[0xf0] = 0x00;
+ dev->regs[0xf1] = 0x00;
+ dev->regs[0xf2] = 0x00;
+ dev->regs[0xf3] = 0x00;
+ break;
+ case SENSOR_TYPE_4400:
+ case SENSOR_TYPE_4400_BARE:
+ for (i = 0; i < dev->reg_count; i++)
+ dev->regs[i] = 0x00;
+ /* 2d may be 0x20 or 0x22: signals something */
+ dev->regs[0x00] = 0xf5;
+ dev->regs[0x01] = 0x41;
+ dev->regs[0x0b] = 0x70;
+ dev->regs[0x10] = 0x50;
+ dev->regs[0x11] = 0x03;
+ dev->regs[0x12] = 0xff;
+ dev->regs[0x13] = 0x3e;
+ dev->regs[0x16] = 0xff;
+ dev->regs[0x17] = 0x3e;
+ dev->regs[0x1d] = 0x22;
+ dev->regs[0x22] = 0x01;
+ dev->regs[0x23] = 0xff;
+ dev->regs[0x25] = 0xfe;
+ dev->regs[0x34] = 0x10;
+ dev->regs[0x39] = 0x02;
+ dev->regs[0x40] = 0x80;
+ dev->regs[0x44] = 0x8c;
+ dev->regs[0x45] = 0x76;
+ dev->regs[0x50] = 0x20;
+ dev->regs[0x8b] = 0xff;
+ dev->regs[0x8c] = 0x3f;
+ dev->regs[0x8d] = 0x80;
+ dev->regs[0x8e] = 0x68;
+ dev->regs[0x93] = 0x01;
+ dev->regs[0x94] = 0x1e;
+ dev->regs[0xa3] = 0xcc;
+ dev->regs[0xa4] = 0x27;
+ dev->regs[0xa5] = 0x64;
+ dev->regs[0xb0] = 0x80;
+ dev->regs[0xb1] = 0x01;
+ dev->regs[0xb2] = 0x06;
+ dev->regs[0xd4] = 0x01;
+ dev->regs[0xd5] = 0x06;
+ dev->regs[0xd6] = 0x1b;
+ dev->regs[0xd7] = 0x10;
+ dev->regs[0xd8] = 0x7a;
+ dev->regs[0xda] = 0xa7;
+ dev->regs[0xe5] = 0x0f;
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ dev->regs[0x30] = 0x40; /* 0x00 */
+ dev->regs[0x31] = 0x80; /* 0x00 */
+ dev->regs[0x44] = 0x00; /* 0x8c */
+ dev->regs[0x45] = 0x00; /* 0x76 */
+ dev->regs[0x52] = 0x04; /* 0x00 */
+ dev->regs[0x69] = 0x01; /* 0x00 */
+ dev->regs[0x6a] = 0x10; /* 0x00 */
+ dev->regs[0x6e] = 0x08; /* 0x00 */
+ dev->regs[0x70] = 0x40; /* 0x00 */
+ dev->regs[0x71] = 0x08; /* 0x00 */
+ dev->regs[0x78] = 0x08; /* 0x00 */
+ dev->regs[0x79] = 0x11; /* 0x00 */
+ dev->regs[0x82] = 0x40; /* 0x00 */
+ dev->regs[0x83] = 0x30; /* 0x00 */
+ dev->regs[0x84] = 0x45; /* 0x00 */
+ dev->regs[0x87] = 0x02; /* 0x00 */
+ dev->regs[0x88] = 0x05; /* 0x00 */
+ dev->regs[0x8b] = 0x01; /* 0xff */
+ dev->regs[0x8c] = 0x80; /* 0x3f */
+ dev->regs[0x8d] = 0x04; /* 0x80 */
+ dev->regs[0x8e] = 0x10; /* 0x68 */
+ dev->regs[0x8f] = 0x01; /* 0x00 */
+ dev->regs[0x90] = 0x80; /* 0x00 */
+ }
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function intializes the device:
+ * - initial registers values
+ * - test if at home
+ * - head parking if needed
+ */
+static SANE_Status
+init_device (struct Rts8891_Device *dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte control, reg, id;
+ SANE_Int i, page;
+ SANE_Byte buffer[2072];
+ char message[256 * 6];
+ SANE_Int val;
+
+ /* these commands are used to acces NVRAM through a serial manner */
+ /* we ignore NVRAM settingsd for now */
+ SANE_Byte nv_cmd1[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x28, 0x38,
+ 0x08, 0x18, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x08
+ };
+ SANE_Byte nv_cmd2[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x28, 0x38,
+ 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x08
+ };
+ SANE_Byte nv_cmd3[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x28, 0x38,
+ 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x28, 0x38, 0x08
+ };
+ SANE_Byte nv_cmd4[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x08, 0x18,
+ 0x08, 0x18, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x08
+ };
+ SANE_Byte nv_cmd5[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x08, 0x18,
+ 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x08
+ };
+ SANE_Byte nv_cmd6[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x08, 0x18,
+ 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x28, 0x38, 0x08
+ };
+ SANE_Byte nv_cmd7[21] =
+ { 0x28, 0x38, 0x28, 0x38, 0x08, 0x18, 0x28, 0x38, 0x28, 0x38, 0x08, 0x18,
+ 0x28, 0x38, 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, 0x08
+ };
+
+ DBG (DBG_proc, "init_device: start\n");
+ if (dev->initialized == SANE_TRUE)
+ return SANE_STATUS_GOOD;
+
+ /* read control register, if busy, something will have to be done ... */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ DBG (DBG_io, "init_device: control=0x%02x\n", control);
+
+ /* when just plugged, we expect to get 0x04 */
+ if (control != 0x04)
+ {
+ DBG (DBG_warn, "init_device: expected control=0x04, got 0x%02x\n",
+ control);
+ }
+
+ /* then read "link" register */
+ sanei_rts88xx_read_reg (dev->devnum, 0xb0, &control);
+ DBG (DBG_io, "init_device: link=0x%02x\n", control);
+
+ /* we expect to get 0x80 */
+ if (control != 0x80)
+ {
+ DBG (DBG_warn, "init_device: expected link=0x80, got 0x%02x\n",
+ control);
+ }
+
+ /* reads scanner status */
+ sanei_rts88xx_get_status (dev->devnum, dev->regs);
+ DBG (DBG_io, "init_device: status=0x%02x 0x%02x\n", dev->regs[0x10],
+ dev->regs[0x11]);
+
+ /* reads lamp status and sensor information */
+ sanei_rts88xx_get_lamp_status (dev->devnum, dev->regs);
+ DBG (DBG_io, "init_device: lamp status=0x%02x\n", dev->regs[0x8e]);
+
+ /* initalize sensor with default from model */
+ dev->sensor = dev->model->sensor;
+ DBG (DBG_info, "init_device: reg[8e]=0x%02x\n", dev->regs[0x8e]);
+
+ /* reset lamp */
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+ if ((dev->regs[0x8e] & 0x60) != 0x60)
+ {
+ DBG (DBG_info, "init_device: lamp needs warming\n");
+ dev->needs_warming = SANE_TRUE;
+ }
+ else
+ {
+ dev->needs_warming = SANE_FALSE;
+ }
+
+ /* reads lcd panel status */
+ sanei_rts88xx_get_lcd (dev->devnum, dev->regs);
+ DBG (DBG_io, "init_device: lcd panel=0x%02x 0x%02x 0x%02x\n",
+ dev->regs[0x20], dev->regs[0x21], dev->regs[0x22]);
+
+ /* read mainboard ID/scanner present register */
+ sanei_rts88xx_read_reg (dev->devnum, LINK_REG, &id);
+ DBG (DBG_io, "init_device: link=0x%02x\n", id);
+
+ /* only known ID is currently 0x00 or 0x01 */
+ if (id != 0x00 && id != 0x01)
+ {
+ DBG (DBG_warn, "init_device: expected id=0x00 or 0x01, got 0x%02x\n",
+ id);
+ }
+
+ /* write 0x00 twice to control */
+ control = 0x00;
+ sanei_rts88xx_write_control (dev->devnum, control);
+ sanei_rts88xx_write_control (dev->devnum, control);
+
+ /* read initial register set */
+ sanei_rts88xx_read_regs (dev->devnum, 0, dev->regs, dev->reg_count);
+ if (DBG_LEVEL > DBG_io2)
+ {
+ sprintf (message, "init_device: initial register settings: ");
+ for (i = 0; i < dev->reg_count; i++)
+ sprintf (message + strlen (message), "0x%02x ", dev->regs[i]);
+
+ DBG (DBG_io2, "%s\n", message);
+ }
+
+ /* initial sensor guess */
+ val = dev->regs[0x44] + 256 * dev->regs[0x45];
+ DBG (DBG_io, "init_device: R44/45=0x%04x\n", val);
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ if(val != 0x00)
+ {
+ DBG (DBG_info, "init_device: SENSOR_TYPE_4400 detected\n");
+ }
+ else
+ {
+ DBG (DBG_info, "init_device: SENSOR_TYPE_4400_BARE detected\n");
+ dev->sensor = SENSOR_TYPE_4400_BARE;
+ }
+ }
+
+ init_registers(dev);
+
+ sanei_rts88xx_set_offset (dev->regs, 31, 31, 31);
+ sanei_rts88xx_set_gain (dev->regs, 10, 10, 10);
+ dev->regs[0] = dev->regs[0] & 0xef;
+
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+ /* now read button status read_reg(0x1a,2)=0x00 0x00 */
+ sanei_rts88xx_read_regs (dev->devnum, 0x1a, dev->regs + 0x1a, 2);
+ /* we expect 0x00 0x00 here */
+ DBG (DBG_io, "init_device: 0x%02x 0x%02x\n", dev->regs[0x1a],
+ dev->regs[0x1b]);
+
+ dev->regs[0xcf] = 0x00;
+ dev->regs[0xd0] = 0xe0;
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+ sanei_rts88xx_read_regs (dev->devnum, 0x1a, dev->regs + 0x1a, 2);
+ /* we expect 0x08 0x00 here */
+ DBG (DBG_io, "init_device: 0x%02x 0x%02x\n", dev->regs[0x1a],
+ dev->regs[0x1b]);
+
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x20, 0x28);
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x28, 0x28);
+ sanei_rts88xx_cancel (dev->devnum);
+ sanei_rts88xx_read_reg (dev->devnum, LAMP_REG, &control);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_read_reg (dev->devnum, LINK_REG, &id);
+ if (id != 0x00 && id != 0x01)
+ {
+ DBG (DBG_warn, "init_device: got unexpected id 0x%02x\n", id);
+ }
+
+
+ dev->regs[0x12] = 0xff;
+ dev->regs[0x14] = 0xf8;
+ dev->regs[0x15] = 0x28;
+ dev->regs[0x16] = 0x07;
+ dev->regs[0x18] = 0xff;
+ dev->regs[0x23] = 0xff;
+ dev->regs[0x24] = 0xff;
+
+ dev->regs[0x72] = 0xe1;
+ dev->regs[0x73] = 0x14;
+ dev->regs[0x74] = 0x18;
+ dev->regs[0x75] = 0x15;
+ dev->regs[0xc0] = 0xff;
+ dev->regs[0xc1] = 0x0f;
+ dev->regs[0xc3] = 0xff;
+ dev->regs[0xc4] = 0xff;
+ dev->regs[0xc5] = 0xff;
+ dev->regs[0xc6] = 0xff;
+ dev->regs[0xc7] = 0xff;
+ dev->regs[0xc8] = 0xff;
+ dev->regs[0xca] = 0x0e;
+ dev->regs[0xcd] = 0xf0;
+ dev->regs[0xce] = 0xff;
+ dev->regs[0xcf] = 0xf5;
+ dev->regs[0xd0] = 0xf7;
+ dev->regs[0xd1] = 0xea;
+ dev->regs[0xd2] = 0x0b;
+ dev->regs[0xd3] = 0x03;
+ dev->regs[0xd4] = 0x05;
+ dev->regs[0xd7] = 0x30;
+ dev->regs[0xd8] = 0xf6;
+ dev->regs[0xd9] = 0x80;
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+
+ /* now we are writing and reading back from memory, it is surely a memory test since the written data
+ * don't look usefull at first glance
+ */
+ reg = 0x06;
+ sanei_rts88xx_write_reg (dev->devnum, 0x93, &reg);
+ for (i = 0; i < 2072; i++)
+ {
+ buffer[i] = i % 97;
+ }
+ sanei_rts88xx_set_mem (dev->devnum, 0x81, 0x00, 2072, buffer);
+ sanei_rts88xx_get_mem (dev->devnum, 0x81, 0x00, 2072, buffer);
+ /* check the data returned */
+ for (i = 0; i < 2072; i++)
+ {
+ if (buffer[i] != id)
+ {
+ DBG (DBG_error,
+ "init_device: memory at %d is not 0x%02d (0x%02x)\n", i, id,
+ buffer[i]);
+ /* XXX STEF XXX return SANE_STATUS_IO_ERROR; */
+ }
+ }
+ DBG (DBG_info, "init_device: memory set #1 passed\n");
+
+ /* now test (or set?) #2 */
+ reg = 0x02;
+ sanei_rts88xx_write_reg (dev->devnum, 0x93, &reg);
+ for (i = 0; i < 2072; i++)
+ {
+ buffer[i] = i % 97;
+ }
+ sanei_rts88xx_set_mem (dev->devnum, 0x81, 0x00, 2072, buffer);
+ sanei_rts88xx_get_mem (dev->devnum, 0x81, 0x00, 2072, buffer);
+ /* check the data returned */
+ for (i = 0; i < 970; i++)
+ {
+ if (buffer[i] != (i + 0x36) % 97)
+ {
+ DBG (DBG_error,
+ "init_device: memory at %d is not 0x%02x (0x%02x)\n", i,
+ (i + 0x36) % 97, buffer[i]);
+ /* XXX STEF XXX return SANE_STATUS_IO_ERROR; */
+ }
+ }
+ for (i = 993; i < 2072; i++)
+ {
+ if (buffer[i] != i % 97)
+ {
+ DBG (DBG_error,
+ "init_device: memory at %d is invalid 0x%02x, instead of 0x%02x\n",
+ i, buffer[i], i % 97);
+ /* XXX STEF XXX return SANE_STATUS_IO_ERROR; */
+ }
+ }
+ DBG (DBG_info, "init_device: memory set #2 passed\n");
+
+
+ /* now test (or set?) #3 */
+ reg = 0x01;
+ sanei_rts88xx_write_reg (dev->devnum, 0x93, &reg);
+ for (i = 0; i < 2072; i++)
+ {
+ buffer[i] = i % 97;
+ }
+ sanei_rts88xx_set_mem (dev->devnum, 0x81, 0x00, 2072, buffer);
+ sanei_rts88xx_get_mem (dev->devnum, 0x81, 0x00, 2072, buffer);
+ /* check the data returned */
+ for (i = 0; i < 2072; i++)
+ {
+ if (buffer[i] != i % 97)
+ {
+ DBG (DBG_error,
+ "init_device: memory at %d is invalid 0x%02x, instead of 0x%02x\n",
+ i, buffer[i], i % 97);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ DBG (DBG_info, "init_device: memory set #3 passed\n");
+
+ /* we are writing page after page the same pattern, and reading at page 0 until we find we have 'wrapped' */
+ /* this is surely some memory amount/number pages detection */
+ dev->regs[0x91] = 0x00; /* page 0 ? */
+ dev->regs[0x92] = 0x00;
+ dev->regs[0x93] = 0x01;
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+
+ /* write pattern at page 0 */
+ for (i = 0; i < 32; i += 2)
+ {
+ buffer[i] = i;
+ buffer[i + 1] = 0x00;
+ }
+ sanei_rts88xx_set_mem (dev->devnum, 0x00, 0x00, 32, buffer);
+
+ page = 0;
+ do
+ {
+ /* next page */
+ page++;
+
+ /* fill buffer with pattern for page */
+ for (i = 0; i < 32; i += 2)
+ {
+ buffer[i] = i;
+ buffer[i + 1] = page;
+ }
+ /* write it to memory */
+ sanei_rts88xx_set_mem (dev->devnum, 0x00, page * 16, 32, buffer);
+ /* read memory from page 0 */
+ sanei_rts88xx_get_mem (dev->devnum, 0x00, 0x00, 32, buffer);
+ for (i = 0; i < 32; i += 2)
+ {
+ if (buffer[i] != i)
+ {
+ DBG (DBG_error,
+ "init_device: memory at %d is invalid: 0x%02x instead of 0x%02x\n",
+ i, buffer[i], i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (buffer[i + 1] != page && buffer[i + 1] != 0)
+ {
+ DBG (DBG_error,
+ "init_device: page %d, memory at %d is invalid: 0x%02x instead of 0x%02x\n",
+ page, i + 1, buffer[i + 1], 0);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+ while (buffer[1] == 0);
+ DBG (DBG_info, "init_device: %d pages detected\n", page - 1);
+
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ sanei_rts88xx_read_reg (dev->devnum, CONTROLER_REG, &control);
+ if (!(control & 0x02))
+ {
+ if (park_head (dev, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "init_device: failed to park head!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ reg = 0x80;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ reg = 0xad;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ sanei_rts88xx_read_regs (dev->devnum, 0x14, dev->regs + 0x14, 2);
+ /* we expect 0xF8 0x28 here */
+ dev->regs[0x14] = dev->regs[0x14] & 0x7F;
+ sanei_rts88xx_write_regs (dev->devnum, 0x14, dev->regs + 0x14, 2);
+
+ /* reads scanner status */
+ sanei_rts88xx_get_status (dev->devnum, dev->regs);
+ DBG (DBG_io, "init_device: status=0x%02x 0x%02x\n", dev->regs[0x10],
+ dev->regs[0x11]);
+ sanei_rts88xx_read_reg (dev->devnum, LINK_REG, &control);
+ DBG (DBG_io, "init_device: link=0x%02x\n", control);
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+ reg = 0x8d;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ reg = 0xad;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+
+ /* here, we are in iddle state */
+
+ /* check link status (scanner still plugged) */
+ sanei_rts88xx_read_reg (dev->devnum, LINK_REG, &control);
+ DBG (DBG_io, "init_device: link=0x%02x\n", control);
+
+ /* this block appears twice */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_warn, "init_device: unexpected control value 0x%02x\n",
+ control);
+ }
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+ sanei_rts88xx_read_reg (dev->devnum, 0x40, &reg);
+ reg |= 0x80;
+ sanei_rts88xx_read_reg (dev->devnum, 0x40, &reg);
+ sanei_rts88xx_read_reg (dev->devnum, 0x10, &reg);
+ sanei_rts88xx_read_reg (dev->devnum, 0x14, &reg);
+ reg = 0x78;
+ sanei_rts88xx_write_reg (dev->devnum, 0x10, &reg);
+ reg = 0x38;
+ sanei_rts88xx_write_reg (dev->devnum, 0x14, &reg);
+ reg = 0x78;
+ sanei_rts88xx_write_reg (dev->devnum, 0x10, &reg);
+ reg = 0x01;
+ sanei_rts88xx_write_reg (dev->devnum, CONTROLER_REG, &reg);
+
+ /* now we init nvram */
+ /* this is highly dangerous and thus desactivated
+ * in sanei_rts88xx_setup_nvram (HAZARDOUS_EXPERIMENT #define) */
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd1);
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd2);
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd3);
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x28, 0x28);
+
+ /* second occurence of this block */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_warn, "init_device: unexpected control value 0x%02x\n",
+ control);
+ }
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+ sanei_rts88xx_read_reg (dev->devnum, 0x40, &reg);
+ reg |= 0x80;
+ sanei_rts88xx_read_reg (dev->devnum, 0x40, &reg);
+ sanei_rts88xx_read_reg (dev->devnum, 0x10, &reg);
+ sanei_rts88xx_read_reg (dev->devnum, 0x14, &reg);
+ reg = 0x78;
+ sanei_rts88xx_write_reg (dev->devnum, 0x10, &reg);
+ reg = 0x38;
+ sanei_rts88xx_write_reg (dev->devnum, 0x14, &reg);
+ reg = 0x78;
+ sanei_rts88xx_write_reg (dev->devnum, 0x10, &reg);
+ reg = 0x01;
+ sanei_rts88xx_write_reg (dev->devnum, CONTROLER_REG, &reg);
+
+ /* nvram again */
+ /* this is highly dangerous and commented out in rts88xx_lib */
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd4);
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd5);
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd6);
+ sanei_rts88xx_setup_nvram (dev->devnum, 21, nv_cmd7);
+
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x28, 0x28);
+
+ /* then we do a simple scan to detect a black zone to locate scan area */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_warn, "init_device: unexpected control value 0x%02x\n",
+ control);
+ }
+ sanei_rts88xx_reset_lamp (dev->devnum, dev->regs);
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_warn, "init_device: unexpected control value 0x%02x\n",
+ control);
+ }
+ sanei_rts88xx_write_control (dev->devnum, 0x01);
+ sanei_rts88xx_write_control (dev->devnum, 0x01);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ dev->regs[0x12] = 0xff;
+ dev->regs[0x13] = 0x20;
+ sanei_rts88xx_write_regs (dev->devnum, 0x12, dev->regs + 0x12, 2);
+ dev->regs[0x14] = 0xf8;
+ dev->regs[0x15] = 0x28;
+ sanei_rts88xx_write_regs (dev->devnum, 0x14, dev->regs + 0x14, 2);
+ reg = 0;
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_write_control (dev->devnum, 0x00);
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, 0x28, 0x28);
+ reg = 0x80;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+
+ dev->regs[0x8b] = 0xff;
+ dev->regs[0x8c] = 0x3f;
+ dev->regs[LAMP_REG] = 0xad;
+ rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+
+ set_lamp_brightness (dev, 0);
+ dev->initialized = SANE_TRUE;
+ DBG (DBG_proc, "init_device: exit\n");
+ return status;
+}
+#endif /* FAST_INIT */
+
+#ifdef FAST_INIT
+/*
+ * This function detects the scanner
+ */
+static SANE_Status
+detect_device (struct Rts8891_Device *dev)
+{
+/*
+--- 5 74945 bulk_out len 4 wrote 0x80 0xb0 0x00 0x01
+--- 6 74946 bulk_in len 1 read 0x80
+--- 7 74947 bulk_out len 4 wrote 0x80 0xb3 0x00 0x01
+--- 8 74948 bulk_in len 1 read 0x04
+--- 9 74949 bulk_out len 4 wrote 0x80 0xb1 0x00 0x01
+--- 10 74950 bulk_in len 1 read 0x00
+--- 11 74951 bulk_out len 5 wrote 0x88 0xb3 0x00 0x01 0x00
+--- 12 74952 bulk_out len 5 wrote 0x88 0xb3 0x00 0x01 0x00
+--- 13 74953 bulk_out len 4 wrote 0x80 0x00 0x00 0xf4
+--- 14 74954 bulk_in len 192 read 0xf5 0x41 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x70 0x00 0x00 0x00 0x00 0x60 0x1b 0xff 0x3e 0x00 0x00 0xff 0x3e 0x00 0x00 0x00 0x00 0x00 0x22 0x00 0x00 0x00 0x00 0x00 0xff 0x00 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x42 0x00 0x00 0x10 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x00 0x00 0x00 0x18 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x20 0x24 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x10 0x00 0x80 0x00 0x10 0x00 0x01 0x00 0x00 0x10 0x00 0x00 0x20 0x20 0x00 0x00 0x00 0x00 0x20 0x01 0x00 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x41 0x00 0x10 0x21 0x20 0x00 0x20 0x81 0x20 0x01 0x01 0x00 0x22 0x00 0x40 0x05 0x00 0x00 0x01 0x1e 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xcc 0x27 0x64 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x00 0x06 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+--- 15 74955 bulk_in len 52 read 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x06 0x1b 0x10 0x7a 0x00 0x15 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+*/
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte control;
+ char message[256 * 5];
+ int i;
+
+ DBG (DBG_proc, "detect_device: start\n");
+
+ /* read "b0" register */
+ sanei_rts88xx_read_reg (dev->devnum, 0xb0, &control);
+ DBG (DBG_io, "detect_device: reg[b0]=0x%02x\n", control);
+
+ /* we expect to get 0x80 */
+ if (control != 0x80)
+ {
+ DBG (DBG_warn, "detect_device: expected reg[b0]=0x80, got 0x%02x\n",
+ control);
+ /* TODO : fail here ? */
+ }
+
+ /* read control register, if busy, something will have to be done ... */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+
+ /* when just plugged, we expect to get 0x04 */
+ if (control != 0x04)
+ {
+ DBG (DBG_warn, "detect_device: expected control=0x04, got 0x%02x\n",
+ control);
+ /* TODO ok if re-run without plugging */
+ }
+
+ /* read "LINK" register */
+ sanei_rts88xx_read_reg (dev->devnum, LINK_REG, &control);
+ DBG (DBG_io, "detect_device: LINK_REG=0x%02x\n", control);
+
+ /* we expect to get 0x00 */
+ if (control != 0x00)
+ {
+ DBG (DBG_warn, "detect_device: expected LINK_REG=0x00, got 0x%02x\n",
+ control);
+ }
+
+ /* write 0x00 twice to control: cancel/stop ? */
+ control = 0x00;
+ sanei_rts88xx_write_control (dev->devnum, control);
+ sanei_rts88xx_write_control (dev->devnum, control);
+
+ /* read initial register set */
+ sanei_rts88xx_read_regs (dev->devnum, 0, dev->regs, dev->reg_count);
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sprintf (message, "init_device: initial register settings: ");
+ for (i = 0; i < dev->reg_count; i++)
+ sprintf (message + strlen (message), "0x%02x ", dev->regs[i]);
+ sprintf (message + strlen (message), "\n");
+ DBG (DBG_io2, message);
+ }
+
+ /* initial sensor guess */
+ val = dev->regs[0x44] + 256 * dev->regs[0x45];
+ DBG (DBG_io, "detect_device: R44/45=0x%04x\n", val);
+ if (dev->sensor == SENSOR_TYPE_4400 && val == 0x00)
+ {
+ dev->sensor = SENSOR_TYPE_4400_BARE;
+ DBG (DBG_info, "detect_device: changing to SENSOR_TYPE_4400_BARE\n");
+ }
+
+ DBG (DBG_io, "detect_device: status=0x%02x 0x%02x\n", dev->regs[0x10],
+ dev->regs[0x11]);
+ DBG (DBG_io, "detect_device: lamp status=0x%02x\n", dev->regs[0x8e]);
+
+ dev->initialized = SANE_FALSE;
+ DBG (DBG_proc, "detect_device: exit\n");
+ return status;
+}
+#endif
+
+/**
+ * Do dark calibration. We scan a well defined area until average pixel value
+ * of the black area is about 0x03 for each color channel. This calibration is
+ * currently done at 75 dpi regardless of the final scan dpi.
+ */
+static SANE_Status
+dark_calibration (struct Rts8891_Device *dev, int mode, int light)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+/* red, green and blue offset, within a 't'op and 'b'ottom value */
+ int ro = 250, tro = 250, bro = 0;
+ int bo = 250, tbo = 250, bbo = 0;
+ int go = 250, tgo = 250, bgo = 0;
+ unsigned char image[CALIBRATION_SIZE];
+ float global, ra, ga, ba;
+ int num = 0;
+ char name[32];
+
+ DBG (DBG_proc, "dark_calibration: start\n");
+
+ /* set up sensor specific bottom values */
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ bro = 128;
+ bgo = 128;
+ bbo = 128;
+ }
+
+ /* set up starting values */
+ sanei_rts88xx_set_gain (dev->regs, 0, 0, 0);
+ sanei_rts88xx_set_scan_area (dev->regs, 1, 2, 4, 4 + CALIBRATION_WIDTH);
+
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, mode, light);
+
+ dev->regs[0x00] = 0xe5; /* scan */
+ dev->regs[0x32] = 0x00;
+ dev->regs[0x33] = 0x03;
+ dev->regs[0x35] = 0x45;
+ dev->regs[0x36] = 0x22;
+ dev->regs[0x3a] = 0x43;
+
+ dev->regs[0x8d] = 0x00;
+ dev->regs[0x8e] = 0x60;
+
+ dev->regs[0xb2] = 0x02;
+
+ dev->regs[0xc0] = 0x06;
+ dev->regs[0xc1] = 0xe6;
+ dev->regs[0xc2] = 0x67;
+ dev->regs[0xc9] = 0x07;
+ dev->regs[0xca] = 0x00;
+ dev->regs[0xcb] = 0xfe;
+ dev->regs[0xcc] = 0xf9;
+ dev->regs[0xcd] = 0x19;
+ dev->regs[0xce] = 0x98;
+ dev->regs[0xcf] = 0xe8;
+ dev->regs[0xd0] = 0xea;
+ dev->regs[0xd1] = 0xf3;
+ dev->regs[0xd2] = 0x14;
+ dev->regs[0xd3] = 0x02;
+ dev->regs[0xd4] = 0x04;
+ dev->regs[0xd6] = 0x0f;
+ dev->regs[0xd8] = 0x52;
+ dev->regs[0xe2] = 0x1f;
+
+ /*dev->regs[0xe5] = 0x28; 28=40
+ dev->regs[0xe6] = 0x00; */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 40);
+
+ dev->regs[0xe7] = 0x75;
+ dev->regs[0xe8] = 0x01;
+ dev->regs[0xe9] = 0x0b;
+ dev->regs[0xea] = 0x54;
+ dev->regs[0xeb] = 0x01;
+ dev->regs[0xec] = 0x04;
+ dev->regs[0xed] = 0xb8;
+ dev->regs[0xef] = 0x03;
+ dev->regs[0xf0] = 0x70;
+ dev->regs[0xf2] = 0x01;
+
+ /* handling of different sensors */
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0xc0] = 0x67;
+ dev->regs[0xc1] = 0x06;
+ dev->regs[0xc2] = 0xe6;
+ dev->regs[0xc3] = 0x98;
+ dev->regs[0xc4] = 0xf9;
+ dev->regs[0xc5] = 0x19;
+ dev->regs[0xc6] = 0x67;
+ dev->regs[0xc7] = 0x06;
+ dev->regs[0xc8] = 0xe6;
+ dev->regs[0xc9] = 0x01;
+ dev->regs[0xca] = 0xf8;
+ dev->regs[0xcb] = 0xff;
+ dev->regs[0xcc] = 0x98;
+ dev->regs[0xcd] = 0xf9;
+ dev->regs[0xce] = 0x19;
+ dev->regs[0xcf] = 0xe0;
+ dev->regs[0xd0] = 0xe2;
+ dev->regs[0xd1] = 0xeb;
+ dev->regs[0xd2] = 0x0c;
+ dev->regs[0xd7] = 0x10;
+ dev->regs[0xda] = 0xa7;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0x13] = 0x39; /* 0x20 */
+ dev->regs[0x14] = 0xf0; /* 0xf8 */
+ dev->regs[0x15] = 0x29; /* 0x28 */
+ dev->regs[0x16] = 0x0f; /* 0x07 */
+ dev->regs[0x17] = 0x10; /* 0x00 */
+ dev->regs[0x23] = 0x00; /* 0xff */
+ dev->regs[0x35] = 0x48; /* 0x45 */
+ dev->regs[0x39] = 0x00; /* 0x02 */
+ dev->regs[0xe2] = 0x0f; /* 0x1f */
+ /* dev->regs[0xe5] = 0x52; 0x28 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 82); /* 2*40+2 */
+ dev->regs[0xe7] = 0x0e; /* 0x75 */
+ dev->regs[0xe9] = 0x0a; /* 0x0b */
+ dev->regs[0xea] = 0xc2; /* 0x54 */
+ dev->regs[0xed] = 0xf6; /* 0xb8 */
+ dev->regs[0xef] = 0x02; /* 0x03 */
+ dev->regs[0xf0] = 0xa8; /* 0x70 */
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ dev->regs[0x13] = 0x39; /* 0x20 */
+ dev->regs[0x14] = 0xf0; /* 0xf8 */
+ dev->regs[0x15] = 0x29; /* 0x28 */
+ dev->regs[0x16] = 0x0f; /* 0x07 */
+ dev->regs[0x17] = 0x10; /* 0x00 */
+ dev->regs[0x23] = 0x00; /* 0xff */
+ dev->regs[0x35] = 0x48; /* 0x45 */
+ dev->regs[0x39] = 0x00; /* 0x02 */
+ dev->regs[0xda] = 0xa7; /* 0xa0 */
+ dev->regs[0xe2] = 0x0f; /* 0x1f */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 82);
+ dev->regs[0xe7] = 0x0e; /* 0x75 */
+ dev->regs[0xe9] = 0x0a; /* 0x0b */
+ dev->regs[0xea] = 0xc2; /* 0x54 */
+ dev->regs[0xed] = 0xf6; /* 0xb8 */
+ dev->regs[0xef] = 0x02; /* 0x03 */
+ dev->regs[0xf0] = 0xa8; /* 0x70 */
+ }
+
+ /* we loop scanning a 637 (1911 bytes) pixels wide area in color mode
+ * until each black average reaches the desired value */
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, mode, light);
+ do
+ {
+ /* set scan with the values to try */
+ sanei_rts88xx_set_offset (dev->regs, ro, go, bo);
+ DBG (DBG_info, "dark_calibration: trying offsets=(%d,%d,%d)\n", ro, go,
+ bo);
+
+ /* do scan */
+ status =
+ rts8891_simple_scan (dev->devnum, dev->regs, dev->reg_count, 0x02,
+ CALIBRATION_SIZE, image);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "dark_calibration: failed to scan\n");
+ return status;
+ }
+
+ /* save scanned picture for data debugging */
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sprintf (name, "dark%03d.pnm", num);
+ write_rgb_data (name, image, CALIBRATION_WIDTH, 1);
+ num++;
+ }
+
+ /* we now compute the average of red pixels from the first 15 pixels */
+ global = average_area (SANE_TRUE, image, 15, 1, &ra, &ga, &ba);
+ DBG (DBG_info,
+ "dark_calibration: global=%.2f, channels=(%.2f,%.2f,%.2f)\n",
+ global, ra, ga, ba);
+
+ /* dichotomie ... */
+ if (abs (ra - DARK_TARGET) < DARK_MARGIN)
+ {
+ /* offset is OK */
+ tro = ro;
+ bro = ro;
+ }
+ else
+ { /* NOK */
+ if (ra > DARK_TARGET)
+ {
+ tro = ro;
+ ro = (tro + bro) / 2;
+ }
+ if (ra < DARK_TARGET)
+ {
+ bro = ro;
+ ro = (tro + bro + 1) / 2;
+ }
+
+ }
+
+ /* same for blue channel */
+ if (abs (ba - DARK_TARGET) < DARK_MARGIN)
+ {
+ bbo = bo;
+ tbo = bo;
+ }
+ else
+ { /* NOK */
+ if (ba > DARK_TARGET)
+ {
+ tbo = bo;
+ bo = (tbo + bbo) / 2;
+ }
+ if (ba < DARK_TARGET)
+ {
+ bbo = bo;
+ bo = (tbo + bbo + 1) / 2;
+ }
+
+ }
+
+ /* and for green channel */
+ if (abs (ga - DARK_TARGET) < DARK_MARGIN)
+ {
+ tgo = go;
+ bgo = go;
+ }
+ else
+ { /* NOK */
+ if (ga > DARK_TARGET)
+ {
+ tgo = go;
+ go = (tgo + bgo) / 2;
+ }
+ if (ga < DARK_TARGET)
+ {
+ bgo = go;
+ go = (tgo + bgo + 1) / 2;
+ }
+ }
+ }
+ while ((tro != bro) || (tgo != bgo) || (tbo != bbo));
+
+ /* store detected values in device struct */
+ dev->red_offset = bro;
+ dev->green_offset = bgo;
+ dev->blue_offset = bbo;
+
+ DBG (DBG_info, "dark_calibration: final offsets=(%d,%d,%d)\n", bro, bgo,
+ bbo);
+ DBG (DBG_proc, "dark_calibration: exit\n");
+ return status;
+}
+
+/*
+ * do gain calibration. We do scans until averaged values of the area match
+ * the target code. We're doing a dichotomy again.
+ */
+static SANE_Status
+gain_calibration (struct Rts8891_Device *dev, int mode, int light)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ float global, ra, ga, ba;
+ /* previous values */
+ int xrg, xbg, xgg;
+/* current gains */
+ int rg, bg, gg;
+/* intervals for dichotomy */
+ int trg, tbg, tgg, bgg, brg, bbg;
+ int num = 0;
+ char name[32];
+ int width = CALIBRATION_WIDTH;
+ int length = CALIBRATION_SIZE;
+ unsigned char image[CALIBRATION_SIZE];
+ int pass = 0;
+ int timing=0;
+
+ int xstart = (dev->left_offset * 75) / dev->model->max_xdpi;
+
+ DBG (DBG_proc, "gain_calibration: start\n");
+
+ /* set up starting values */
+ sanei_rts88xx_set_scan_area (dev->regs, 1, 2, xstart, xstart + width);
+ sanei_rts88xx_set_offset (dev->regs, dev->red_offset, dev->green_offset,
+ dev->blue_offset);
+
+ /* same register set than dark calibration */
+ dev->regs[0x32] = 0x00;
+ dev->regs[0x33] = 0x03;
+ dev->regs[0x35] = 0x45;
+ dev->regs[0x36] = 0x22;
+ dev->regs[0x3a] = 0x43;
+
+ dev->regs[0x8d] = 0x00;
+ dev->regs[0x8e] = 0x60;
+
+ dev->regs[0xb2] = 0x02;
+
+ dev->regs[0xc0] = 0x06;
+ dev->regs[0xc1] = 0xe6;
+ dev->regs[0xc2] = 0x67;
+ dev->regs[0xc9] = 0x07;
+ dev->regs[0xca] = 0x00;
+ dev->regs[0xcb] = 0xfe;
+ dev->regs[0xcc] = 0xf9;
+ dev->regs[0xcd] = 0x19;
+ dev->regs[0xce] = 0x98;
+ dev->regs[0xcf] = 0xe8;
+ dev->regs[0xd0] = 0xea;
+ dev->regs[0xd1] = 0xf3;
+ dev->regs[0xd2] = 0x14;
+ dev->regs[0xd3] = 0x02;
+ dev->regs[0xd4] = 0x04;
+ dev->regs[0xd6] = 0x0f;
+ dev->regs[0xd8] = 0x52;
+ dev->regs[0xe2] = 0x1f;
+
+ /* dev->regs[0xe5] = 0x28;
+ dev->regs[0xe6] = 0x00; 28=40 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 40);
+
+ dev->regs[0xe7] = 0x75;
+ dev->regs[0xe8] = 0x01;
+ dev->regs[0xe9] = 0x0b;
+ dev->regs[0xea] = 0x54;
+ dev->regs[0xeb] = 0x01;
+ dev->regs[0xec] = 0x04;
+ dev->regs[0xed] = 0xb8;
+ dev->regs[0xef] = 0x03;
+ dev->regs[0xf0] = 0x70;
+ dev->regs[0xf2] = 0x01;
+
+ dev->regs[0x72] = 0xe1;
+ dev->regs[0x73] = 0x14;
+ dev->regs[0x74] = 0x18;
+
+ dev->regs[0xc3] = 0xff;
+ dev->regs[0xc4] = 0xff;
+ dev->regs[0xc7] = 0xff;
+ dev->regs[0xc8] = 0xff;
+
+ dev->regs[0xd7] = 0x30;
+
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0xc0] = 0x67;
+ dev->regs[0xc1] = 0x06;
+ dev->regs[0xc2] = 0xe6;
+ dev->regs[0xc3] = 0x98;
+ dev->regs[0xc4] = 0xf9;
+ dev->regs[0xc5] = 0x19;
+ dev->regs[0xc6] = 0x67;
+ dev->regs[0xc7] = 0x06;
+ dev->regs[0xc8] = 0xe6;
+ dev->regs[0xc9] = 0x01;
+ dev->regs[0xca] = 0xf8;
+ dev->regs[0xcb] = 0xff;
+ dev->regs[0xcc] = 0x98;
+ dev->regs[0xcd] = 0xf9;
+ dev->regs[0xce] = 0x19;
+ dev->regs[0xcf] = 0xe0;
+ dev->regs[0xd0] = 0xe2;
+ dev->regs[0xd1] = 0xeb;
+ dev->regs[0xd2] = 0x0c;
+ dev->regs[0xd3] = 0x02;
+ dev->regs[0xd4] = 0x04;
+ dev->regs[0xd6] = 0x0f;
+ dev->regs[0xd7] = 0x10;
+ dev->regs[0xd8] = 0x52;
+ dev->regs[0xe2] = 0x1f;
+ /* dev->regs[0xe5] = 0x28; 0028 -> 40
+ dev->regs[0xe6] = 0x00; */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 40);
+ dev->regs[0xe7] = 0x75;
+ dev->regs[0xe8] = 0x01;
+ dev->regs[0xe9] = 0x0b;
+ dev->regs[0xea] = 0x54;
+ dev->regs[0xeb] = 0x01;
+ dev->regs[0xec] = 0x04;
+ dev->regs[0xed] = 0xb8;
+ dev->regs[0xef] = 0x03;
+ dev->regs[0xf0] = 0x70;
+ dev->regs[0xf2] = 0x01;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0x35] = 0x48; /* 0x45 */
+ /* c5, c6 ? : untouched from previous scan ... */
+ dev->regs[0xe2] = 0x0f; /* 0x1f */
+ /* dev->regs[0xe5] = 0x52; */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 82); /* 2*40+2 */
+ dev->regs[0xe7] = 0x0e; /* 0x75 */
+ dev->regs[0xe9] = 0x0a; /* 0x0b */
+ dev->regs[0xea] = 0xc2; /* 0x54 */
+ dev->regs[0xed] = 0xf6; /* 0xb8 */
+ dev->regs[0xef] = 0x02; /* 0x03 */
+ dev->regs[0xf0] = 0xa8; /* 0x70 */
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ dev->regs[0x13] = 0x39;
+ dev->regs[0x14] = 0xf0;
+ dev->regs[0x15] = 0x29;
+ dev->regs[0x16] = 0x0f;
+ dev->regs[0x17] = 0x10;
+ dev->regs[0x23] = 0x00;
+ dev->regs[0x35] = 0x48;
+ dev->regs[0x39] = 0x00;
+ dev->regs[0xe2] = 0x0f;
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 82);
+ dev->regs[0xe7] = 0x0e;
+ dev->regs[0xe9] = 0x0a;
+ dev->regs[0xea] = 0xc2;
+ dev->regs[0xed] = 0xf6;
+ dev->regs[0xef] = 0x02;
+ dev->regs[0xf0] = 0xa8;
+
+ timing=0x32;
+ dev->regs[0x85] = 0x00;
+ dev->regs[0x86] = 0x06;
+ dev->regs[0x87] = 0x00;
+ dev->regs[0x88] = 0x06;
+ }
+ SET_DOUBLE (dev->regs, TIMING_REG, timing);
+ SET_DOUBLE (dev->regs, TIMING1_REG, timing+1);
+ SET_DOUBLE (dev->regs, TIMING2_REG, timing+2);
+
+
+ /* we loop scanning a 637 (1911 bytes) pixels wide area in color mode until each white average
+ * reaches the desired value, doing a dichotomy */
+ rg = 0x1f;
+ bg = 0x1f;
+ gg = 0x1f;
+ /* dichotomy interval */
+ /* bottom values */
+ brg = 0;
+ bgg = 0;
+ bbg = 0;
+ /* top values */
+ trg = 0x1f;
+ tgg = 0x1f;
+ tbg = 0x1f;
+
+ /* loop on gain calibration until we find until we find stable value
+ * or we do more than 20 tries */
+ do
+ {
+ /* store current values as previous */
+ xrg = rg;
+ xbg = bg;
+ xgg = gg;
+
+ /* set up for scan */
+ DBG (DBG_info,
+ "gain_calibration: trying gains=(0x%02x,0x%02x,0x%02x)\n", rg, gg,
+ bg);
+ sanei_rts88xx_set_gain (dev->regs, rg, gg, bg);
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, mode, light);
+
+ /* scan on line in RGB */
+ status =
+ rts8891_simple_scan (dev->devnum, dev->regs, dev->reg_count, 0x02,
+ length, image);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "gain_calibration: failed scan data\n");
+ return status;
+ }
+
+ /* save scanned picture for data debugging */
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sprintf (name, "gain%03d.pnm", num);
+ write_rgb_data (name, image, width, 1);
+ num++;
+ }
+
+ /* we now compute the average pixel value on the scanned line */
+ /* TODO use computed left margin */
+ global = average_area (SANE_TRUE, image, width, 1, &ra, &ga, &ba);
+ DBG (DBG_info,
+ "gain_calibration: global=%.2f, channels=(%.2f,%.2f,%.2f)\n",
+ global, ra, ga, ba);
+
+ /* dichotomy again ... */
+ if (abs (ra - RED_GAIN_TARGET) < GAIN_MARGIN)
+ {
+ /* gain is OK, it is whitin the tolerance margin */
+ trg = rg;
+ brg = rg;
+ }
+ else
+ { /* average is higher than the desired value */
+ if (ra > RED_GAIN_TARGET)
+ {
+ /* changing gain for another channel alters other output */
+ /* a stable value is shown by top value == bottom value */
+ if (trg == brg)
+ {
+ brg--;
+ rg = brg;
+ }
+ else
+ {
+ /* since we'ra above target value, current gain becomes top value */
+ trg = rg;
+ rg = (trg + brg) / 2;
+ }
+ }
+ else /* below target value */
+ {
+ if (trg == brg)
+ {
+ trg++;
+ rg = trg;
+ }
+ else
+ {
+ /* since we are below target, current value is assigned to bottom value */
+ brg = rg;
+ rg = (trg + brg + 1) / 2;
+ }
+ }
+ }
+
+ /* same for blue channel */
+ if (abs (ba - BLUE_GAIN_TARGET) < GAIN_MARGIN)
+ {
+ bbg = bg;
+ tbg = bg;
+ }
+ else
+ { /* NOK */
+ if (ba > BLUE_GAIN_TARGET)
+ {
+ if (tbg == bbg)
+ {
+ bbg--;
+ bg = bbg;
+ }
+ else
+ {
+ tbg = bg;
+ bg = (tbg + bbg) / 2;
+ }
+ }
+ else
+ {
+ if (tbg == bbg)
+ {
+ tbg++;
+ bg = tbg;
+ }
+ else
+ {
+ bbg = bg;
+ bg = (tbg + bbg + 1) / 2;
+ }
+ }
+ }
+
+ /* and for green channel */
+ if (abs (ga - GREEN_GAIN_TARGET) < GAIN_MARGIN)
+ {
+ tgg = gg;
+ bgg = gg;
+ }
+ else
+ { /* NOK */
+ if (ga > GREEN_GAIN_TARGET)
+ {
+ if (tgg == bgg)
+ {
+ bgg--;
+ gg = bgg;
+ }
+ else
+ {
+ tgg = gg;
+ gg = (tgg + bgg) / 2;
+ }
+ }
+ else
+ {
+ if (tgg == bgg)
+ {
+ tgg++;
+ gg = tgg;
+ }
+ else
+ {
+ bgg = gg;
+ gg = (tgg + bgg + 1) / 2;
+ }
+ }
+ }
+ pass++;
+ }
+ while ((pass < 20)
+ && ((trg != brg && xrg != rg)
+ || (tgg != bgg && xgg != gg) || (tbg != bbg && xbg != bg)));
+
+ /* store detected values in device struct */
+ dev->red_gain = brg;
+ dev->green_gain = bgg;
+ dev->blue_gain = bbg;
+ DBG (DBG_info, "gain_calibration: gain=(0x%02x,0x%02x,0x%02x)\n", brg, bgg,
+ bbg);
+
+ DBG (DBG_proc, "gain_calibration: exit\n");
+ return status;
+}
+
+/**
+ * Do fine offset calibration. Scans are done with gains from gain calibration
+ * at 75 dpi regardless of the dpi of the final scan. We loop scanning a 637
+ * (1911 bytes) pixels wide area in color mode until each black average
+ * reaches the desired value (OFFSET_TARGET).
+ */
+static SANE_Status
+offset_calibration (struct Rts8891_Device *dev, int mode, int light)
+{
+
+ SANE_Status status = SANE_STATUS_GOOD;
+ /* red, green and blue offset, within a 't'op and 'b'ottom value */
+ int ro = 250, tro = 250, bro = 123;
+ int go = 250, tgo = 250, bgo = 123;
+ int bo = 250, tbo = 250, bbo = 123;
+ unsigned char image[CALIBRATION_SIZE];
+ float global, ra, ga, ba;
+ int num = 0;
+ char name[32];
+
+ DBG (DBG_proc, "offset_calibration: start\n");
+
+ /* set up starting values */
+ /* gains from previous step are a little higher than the one used */
+ sanei_rts88xx_set_gain (dev->regs, dev->red_gain, dev->green_gain,
+ dev->blue_gain);
+ sanei_rts88xx_set_scan_area (dev->regs, 1, 2, 4, 4 + CALIBRATION_WIDTH);
+
+ dev->regs[0x32] = 0x00;
+ dev->regs[0x33] = 0x03;
+ dev->regs[0x35] = 0x45;
+ dev->regs[0x36] = 0x22;
+ dev->regs[0x3a] = 0x43;
+
+ dev->regs[0x8d] = 0x00;
+ dev->regs[0x8e] = 0x60;
+ dev->regs[0xb2] = 0x02;
+ dev->regs[0xc0] = 0x06;
+ dev->regs[0xc1] = 0xe6;
+ dev->regs[0xc2] = 0x67;
+ dev->regs[0xc9] = 0x07;
+ dev->regs[0xca] = 0x00;
+ dev->regs[0xcb] = 0xfe;
+ dev->regs[0xcc] = 0xf9;
+ dev->regs[0xcd] = 0x19;
+ dev->regs[0xce] = 0x98;
+ dev->regs[0xcf] = 0xe8;
+ dev->regs[0xd0] = 0xea;
+ dev->regs[0xd1] = 0xf3;
+ dev->regs[0xd2] = 0x14;
+ dev->regs[0xd3] = 0x02;
+ dev->regs[0xd4] = 0x04;
+ dev->regs[0xd6] = 0x0f;
+ dev->regs[0xd8] = 0x52;
+ dev->regs[0xe2] = 0x1f;
+ /* dev->regs[0xe5] = 0x28;
+ dev->regs[0xe6] = 0x00; 0x0028=40 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 40);
+ dev->regs[0xe7] = 0x75;
+ dev->regs[0xe8] = 0x01;
+ dev->regs[0xe9] = 0x0b;
+ dev->regs[0xea] = 0x54;
+ dev->regs[0xeb] = 0x01;
+ dev->regs[0xec] = 0x04;
+ dev->regs[0xed] = 0xb8;
+ dev->regs[0xef] = 0x03;
+ dev->regs[0xf0] = 0x70;
+ dev->regs[0xf2] = 0x01;
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0x72] = 0xe1;
+ dev->regs[0x73] = 0x14;
+ dev->regs[0x74] = 0x18;
+
+ dev->regs[0xc0] = 0x67;
+ dev->regs[0xc1] = 0x06;
+ dev->regs[0xc2] = 0xe6;
+ dev->regs[0xc3] = 0x98;
+ dev->regs[0xc4] = 0xf9;
+ dev->regs[0xc5] = 0x19;
+ dev->regs[0xc6] = 0x67;
+ dev->regs[0xc7] = 0x06;
+ dev->regs[0xc8] = 0xe6;
+ dev->regs[0xc9] = 0x01;
+ dev->regs[0xca] = 0xf8;
+ dev->regs[0xcb] = 0xff;
+ dev->regs[0xcc] = 0x98;
+ dev->regs[0xcd] = 0xf9;
+ dev->regs[0xce] = 0x19;
+ dev->regs[0xcf] = 0xe0;
+ dev->regs[0xd0] = 0xe2;
+ dev->regs[0xd1] = 0xeb;
+ dev->regs[0xd2] = 0x0c;
+ dev->regs[0xd7] = 0x10;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ dev->regs[0x35] = 0x48; /* 0x45 */
+ /* c5,c6 ?? */
+ dev->regs[0xe2] = 0x0f; /* 0x1f */
+ /* dev->regs[0xe5] = 0x52; 0x28 */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 82); /* 2*40+2 */
+ dev->regs[0xe7] = 0x0e; /* 0x75 */
+ dev->regs[0xe9] = 0x0a; /* 0x0b */
+ dev->regs[0xea] = 0xc2; /* 0x54 */
+ dev->regs[0xed] = 0xf6; /* 0xb8 */
+ dev->regs[0xef] = 0x02; /* 0x03 */
+ dev->regs[0xf0] = 0xa8; /* 0x70 */
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ dev->regs[0x13] = 0x39; /* 0x20 */
+ dev->regs[0x14] = 0xf0; /* 0xf8 */
+ dev->regs[0x15] = 0x29; /* 0x28 */
+ dev->regs[0x16] = 0x0f; /* 0x07 */
+ dev->regs[0x17] = 0x10; /* 0x00 */
+ dev->regs[0x23] = 0x00; /* 0xff */
+ dev->regs[0x35] = 0x48; /* 0x45 */
+ dev->regs[0x39] = 0x00; /* 0x02 */
+ dev->regs[0xe2] = 0x0f; /* 0x1f */
+ SET_DOUBLE (dev->regs, EXPOSURE_REG, 82); /* 2*40+2 */
+ dev->regs[0xe7] = 0x0e; /* 0x75 */
+ dev->regs[0xe9] = 0x0a; /* 0x0b */
+ dev->regs[0xea] = 0xc2; /* 0x54 */
+ dev->regs[0xed] = 0xf6; /* 0xb8 */
+ dev->regs[0xef] = 0x02; /* 0x03 */
+ dev->regs[0xf0] = 0xa8; /* 0x70 */
+ }
+
+ /* we loop scanning a 637 (1911 bytes) pixels wide area in color mode until each black average
+ * reaches the desired value */
+ do
+ {
+ DBG (DBG_info, "offset_calibration: trying offsets=(%d,%d,%d) ...\n",
+ ro, go, bo);
+ sanei_rts88xx_set_offset (dev->regs, ro, go, bo);
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, mode, light);
+ rts8891_simple_scan (dev->devnum, dev->regs, dev->reg_count, 0x02,
+ CALIBRATION_SIZE, image);
+
+ /* save scanned picture for data debugging */
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sprintf (name, "offset%03d.pnm", num);
+ write_rgb_data (name, image, CALIBRATION_WIDTH, 1);
+ num++;
+ }
+
+ /* we now compute the average of red pixels from the first 15 pixels */
+ global = average_area (SANE_TRUE, image, 15, 1, &ra, &ga, &ba);
+ DBG (DBG_info,
+ "offset_calibration: global=%.2f, channels=(%.2f,%.2f,%.2f)\n",
+ global, ra, ga, ba);
+
+ /* dichotomie ... */
+ if (abs (ra - OFFSET_TARGET) < OFFSET_MARGIN)
+ {
+ /* offset is OK */
+ tro = ro;
+ bro = ro;
+ }
+ else
+ { /* NOK */
+ if (ra > OFFSET_TARGET)
+ {
+ tro = ro;
+ ro = (tro + bro) / 2;
+ }
+ if (ra < OFFSET_TARGET)
+ {
+ bro = ro;
+ ro = (tro + bro + 1) / 2;
+ }
+
+ }
+
+ /* same for blue channel */
+ if (abs (ba - OFFSET_TARGET) < OFFSET_MARGIN)
+ {
+ bbo = bo;
+ tbo = bo;
+ }
+ else
+ {
+ if (ba > OFFSET_TARGET)
+ {
+ tbo = bo;
+ bo = (tbo + bbo) / 2;
+ }
+ if (ba < OFFSET_TARGET)
+ {
+ bbo = bo;
+ bo = (tbo + bbo + 1) / 2;
+ }
+
+ }
+
+ /* and for green channel */
+ if (abs (ga - OFFSET_TARGET) < OFFSET_MARGIN)
+ {
+ tgo = go;
+ bgo = go;
+ }
+ else
+ {
+ if (ga > OFFSET_TARGET)
+ {
+ tgo = go;
+ go = (tgo + bgo) / 2;
+ }
+ if (ga < OFFSET_TARGET)
+ {
+ bgo = go;
+ go = (tgo + bgo + 1) / 2;
+ }
+ }
+ }
+ while ((tro != bro) || (tgo != bgo) || (tbo != bbo));
+
+ /* store detected values in device struct */
+ dev->red_offset = bro;
+ dev->green_offset = bgo;
+ dev->blue_offset = bbo;
+
+ DBG (DBG_proc, "offset_calibration: exit\n");
+ return status;
+}
+
+/*
+ * do shading calibration
+ * We scan a 637 pixels by 66 linesxoffset=24 , xend=661, pixels=637
+y offset=1 , yend=67, lines =66
+ */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+setup_shading_calibration (struct Rts8891_Device *dev, int mode, int *light, int *status1, SANE_Byte * regs)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int lines = 66;
+ int exposure=0;
+ int timing=0;
+
+ DBG (DBG_proc, "setup_shading_calibration: start\n");
+ DBG (DBG_info, "setup_shading_calibration: sensor type is %s (%d)\n", sensor_name (dev->sensor), dev->sensor);
+
+ /* set up registers */
+ /* 0x20/0x28 0x3b/0x3f seen in logs */
+ *status1 = mode;
+ if (dev->xdpi > 300)
+ {
+ *status1 |= 0x08;
+ }
+
+ /* we default to 75 dpi then override needed registers */
+ timing=0x00b0;
+ regs[0x32] = 0x20;
+ regs[0x33] = 0x83;
+ regs[0x35] = 0x0e;
+ regs[0x36] = 0x2c;
+ regs[0x3a] = 0x0e;
+
+ regs[0xe2] = 0x05;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ /* regs[0xe5] = 0xdd; */
+
+ exposure=221;
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ regs[0xc0] = 0x67;
+ regs[0xc1] = 0x06;
+ regs[0xc2] = 0xe6;
+ regs[0xc3] = 0x98;
+ regs[0xc4] = 0xf9;
+ regs[0xc5] = 0x19;
+ regs[0xc6] = 0x67;
+ regs[0xc7] = 0x06;
+ regs[0xc8] = 0xe6;
+ regs[0xc9] = 0x01;
+ regs[0xca] = 0xf8;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x98;
+ regs[0xcd] = 0xf9;
+ regs[0xce] = 0x19;
+ regs[0xcf] = 0xe0;
+ regs[0xd0] = 0xe2;
+
+ regs[0xd1] = 0xeb;
+ regs[0xd2] = 0x0c;
+
+ regs[0xd7] = 0x10;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ *light &= 0xf7; /* clear bit 3 */
+ regs[0x36] = 0x29; /* 0x2c */
+ timing=0x0032;
+ regs[0x85] = 0x00; /* 0x8c */
+ regs[0x86] = 0x06; /* 0x10 */
+ regs[0x87] = 0x00; /* 0x18 */
+ regs[0x88] = 0x06; /* 0x1b */
+ regs[0x8d] = 0x00; /* 0x77 */
+ /* c5,c6 ?? */
+ /* regs[0xd3] = 0x02; 0x0e */
+ regs[0xd4] = 0x04; /* 0x10 */
+ regs[0xe2] = 0x02; /* 0x05 */
+ /* regs[0xe5] = 0xbb;
+ regs[0xe6] = 0x01; 1bb =443 */
+ exposure=443;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ regs[0x13] = 0x39; /* 0x20 */
+ regs[0x14] = 0xf0; /* 0xf8 */
+ regs[0x15] = 0x29; /* 0x28 */
+ regs[0x16] = 0x0f; /* 0x07 */
+ regs[0x17] = 0x10; /* 0x00 */
+ regs[0x23] = 0x00; /* 0xff */
+ regs[0x36] = 0x29; /* 0x2c */
+ regs[0x39] = 0x00; /* 0x02 */
+ regs[0xe2] = 0x02; /* 0x05 */
+ exposure=443;
+ }
+
+ switch (dev->xdpi)
+ {
+ case 75:
+ break;
+
+ case 150:
+ /* X resolution related registers */
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_XPA for 150 dpi\n");
+ timing=0x022b;
+ regs[0x85] = 0x18;
+ regs[0x86] = 0x1b;
+ regs[0x87] = 0x30;
+ regs[0x88] = 0x30;
+ regs[0x8d] = 0xef;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0x8e;
+ regs[0xc2] = 0xff;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x71;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0x8e;
+ regs[0xc8] = 0xff;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0x1f;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x71;
+ regs[0xce] = 0x00;
+ regs[0xcf] = 0xe6;
+ regs[0xd0] = 0xe8;
+ regs[0xd1] = 0xf6;
+ regs[0xd2] = 0x17;
+ /* regs[0xd3] = 0x0b; */
+ regs[0xd4] = 0x0d;
+ /* regs[0xe5] = 0xe4; */
+ exposure=228;
+ break;
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_BARE for 150 dpi\n");
+ timing=0x012e;
+ regs[0x85] = 0x8c;
+ regs[0x86] = 0x10;
+ regs[0x87] = 0x18;
+ regs[0x88] = 0x1b;
+ regs[0x8d] = 0x77;
+ regs[0xc0] = 0x80;
+ regs[0xc1] = 0x87;
+ regs[0xc2] = 0x7f;
+ regs[0xc9] = 0x00;
+ regs[0xcb] = 0x78;
+ regs[0xcc] = 0x7f;
+ regs[0xcd] = 0x78;
+ regs[0xce] = 0x80;
+ regs[0xcf] = 0xe6;
+ regs[0xd0] = 0xe8;
+
+ regs[0xd1] = 0xf7;
+ regs[0xd2] = 0x00;
+
+ /* regs[0xd3] = 0x0e; */
+ regs[0xd4] = 0x10;
+
+ /* regs[0xe5] = 0xe4; */
+ exposure=228;
+ break;
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400 for 150 dpi\n");
+ timing=0x012e;
+ regs[0x85] = 0x8c;
+ regs[0x86] = 0x10;
+ regs[0x87] = 0x18;
+ regs[0x88] = 0x1b;
+ regs[0x8d] = 0x77;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0x8e;
+ regs[0xc2] = 0xff;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x71;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0x8e;
+ regs[0xc8] = 0xff;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0x1f;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x71;
+ regs[0xce] = 0x00;
+ regs[0xcf] = 0xe6;
+ regs[0xd0] = 0xe8;
+ regs[0xd1] = 0xf6;
+ regs[0xd2] = 0x17;
+ /* regs[0xd3] = 0x0b; */
+ regs[0xd4] = 0x0d;
+ exposure=457;
+ break;
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400_BARE for 150 dpi\n");
+ regs[0x13] = 0x39; /* 0x20 */
+ regs[0x14] = 0xf0; /* 0xf8 */
+ regs[0x15] = 0x29; /* 0x28 */
+ regs[0x16] = 0x0f; /* 0x07 */
+ regs[0x17] = 0x10; /* 0x00 */
+ regs[0x23] = 0x00; /* 0xff */
+ regs[0x36] = 0x29; /* 0x2c */
+ regs[0x39] = 0x00; /* 0x02 */
+ timing=0x00b0;
+ regs[0x85] = 0x46; /* 0x00 */
+ regs[0x86] = 0x0b; /* 0x06 */
+ regs[0x87] = 0x8c; /* 0x00 */
+ regs[0x88] = 0x10; /* 0x06 */
+ regs[0x8d] = 0x3b; /* 0x00 */
+ regs[0xc0] = 0xff; /* 0x06 */
+ regs[0xc1] = 0x0f; /* 0xe6 */
+ regs[0xc2] = 0x00; /* 0x67 */
+ regs[0xc9] = 0x00; /* 0x07 */
+ regs[0xca] = 0x0e; /* 0x00 */
+ regs[0xcb] = 0x00; /* 0xfe */
+ regs[0xcc] = 0x00; /* 0xf9 */
+ regs[0xcd] = 0xf0; /* 0x19 */
+ regs[0xce] = 0xff; /* 0x98 */
+ regs[0xcf] = 0xf5; /* 0xe8 */
+ regs[0xd0] = 0xf7; /* 0xea */
+ regs[0xd1] = 0xea; /* 0xf3 */
+ regs[0xd2] = 0x0b; /* 0x14 */
+ /* regs[0xd3] = 0x17; 0x02 */
+ regs[0xd4] = 0x01; /* 0x04 */
+ regs[0xe2] = 0x02; /* 0x05 */
+ /* regs[0xe5] = 0x93; regs[0xe6] = 0x03; */
+ exposure=915;
+ break;
+ }
+ break;
+
+ case 300:
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_XPA for 300 dpi\n");
+ timing=0x00b0;
+ regs[0x85] = 0x46;
+ regs[0x86] = 0x0b;
+ regs[0x87] = 0x8c;
+ regs[0x88] = 0x10;
+ regs[0x8d] = 0x3b;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0x0f;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xf0;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x0f;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0xf1;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0xf0;
+ regs[0xcf] = 0xed;
+ regs[0xd0] = 0xef;
+ regs[0xd1] = 0xe2;
+ regs[0xd2] = 0x03;
+ /* regs[0xd3] = 0x17; */
+ regs[0xd4] = 0x01;
+ /* regs[0xe5] = 0xc9;
+ regs[0xe6] = 0x01; 0x01c9=457 */
+ exposure=457;
+ break;
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_BARE for 300 dpi\n");
+ timing=0x012e;
+ regs[0x85] = 0x8c;
+ regs[0x86] = 0x10;
+ regs[0x87] = 0x18;
+ regs[0x88] = 0x1b;
+
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61; /* low nibble of 8e and 8d are proportional to
+ the scanned width 1de => 5100 wide scan */
+
+ regs[0xc0] = 0xff;
+ regs[0xc1] = 0x0f;
+ regs[0xc2] = 0x00;
+ regs[0xc9] = 0x00;
+ regs[0xca] = 0x0e;
+ regs[0xcb] = 0x00;
+ regs[0xcc] = 0x00;
+ regs[0xcd] = 0xf0;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+
+ regs[0xd1] = 0xea;
+ regs[0xd2] = 0x0b;
+
+ /* regs[0xd3] = 0x17; */
+ regs[0xd4] = 0x01;
+ /* regs[0xe5] = 0xc9;
+ regs[0xe6] = 0x01; 0x1c9=457 */
+ exposure=457;
+ break;
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400 for 300 dpi\n");
+ timing=0x022b;
+ regs[0x85] = 0x18;
+ regs[0x86] = 0x1b;
+ regs[0x87] = 0x30;
+ regs[0x88] = 0x30;
+ regs[0x8d] = 0xef;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0x0f;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xf0;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x0f;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0xf1;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0xf0;
+ regs[0xcf] = 0xed;
+ regs[0xd0] = 0xef;
+ regs[0xd1] = 0xe2;
+ regs[0xd2] = 0x03;
+ /* regs[0xd3] = 0x17; */
+ regs[0xd4] = 0x01;
+ /* 0x0393 = 915 = 2*457+1 */
+ exposure=915;
+ break;
+
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400_BARE for 300 dpi\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+
+ case 600:
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_BARE for 600 dpi\n");
+ regs[0x34] = 0x10;
+
+ regs[0x72] = 0x3a;
+ regs[0x73] = 0x15;
+ regs[0x74] = 0x62;
+
+ timing=0x022b;
+
+ regs[0x85] = 0x18;
+ regs[0x86] = 0x1b;
+
+ regs[0x87] = 0x30;
+ regs[0x88] = 0x30;
+
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61; /* low nibble of 8e and 8d are proportional to
+ the scanned width 1de => 5100 wide scan */
+ regs[0xc0] = 0xff;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0xff;
+ regs[0xc3] = 0x00;
+ regs[0xc4] = 0xf0;
+ regs[0xc7] = 0x0f;
+ regs[0xc8] = 0x00;
+ regs[0xcb] = 0xe0;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0xff;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xe9;
+ regs[0xd0] = 0xeb;
+
+ regs[0xd7] = 0x14;
+
+ /* regs[0xe5] = 0x93;
+ regs[0xe6] = 0x03; 0x393=915 = 457*2+1 */
+ exposure=915;
+ break;
+
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_XPA for 600 dpi\n");
+ *light &= 0xfb; /* clear bit 2 */
+ regs[0x16] = 0x07;
+ regs[0x33] = 0x83; /* 0x86 */
+ regs[0x34] = 0x10;
+ regs[0x50] = 0x00; /* 0x18 */
+ regs[0x64] = 0x01; /* 0x02 */
+ regs[0x65] = 0x20; /* 0x10 */
+
+ regs[0x72] = 0x3a;
+ regs[0x73] = 0x15;
+ regs[0x74] = 0x62;
+
+ regs[0x85] = 0x00;
+ regs[0x86] = 0x06;
+
+ regs[0x87] = 0x00;
+ regs[0x88] = 0x06;
+
+ regs[0x8d] = 0x00;
+ regs[0x8e] = 0x60; /* XXX STEF XXX */
+
+ regs[0xc0] = 0xf8;
+ regs[0xc1] = 0x7f;
+ regs[0xc2] = 0x00;
+ regs[0xc3] = 0xf8;
+ regs[0xc4] = 0x7f;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0xf8;
+ regs[0xc7] = 0x7f;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0x8f;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x07;
+ regs[0xcd] = 0x80;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf2;
+ regs[0xd0] = 0xf4;
+ regs[0xd1] = 0xe7;
+ regs[0xd2] = 0x08;
+ regs[0xd3] = 0x0e;
+ regs[0xd4] = 0x10;
+ regs[0xd7] = 0x31;
+
+ timing=0x0032;
+ exposure=915;
+ break;
+
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400 for 600 dpi\n");
+ *light = 0x23;
+
+ regs[0x13] = 0x39;
+ regs[0x14] = 0xf0;
+ regs[0x15] = 0x29;
+ regs[0x16] = 0x0f;
+ regs[0x17] = 0x10;
+ regs[0x23] = 0x00;
+ regs[0x34] = 0xf0;
+ regs[0x36] = 0x29;
+ regs[0x39] = 0x00;
+ regs[0x72] = 0x3a;
+ regs[0x73] = 0x15;
+ regs[0x74] = 0x62;
+ timing=0x0426;
+ regs[0x85] = 0x30;
+ regs[0x86] = 0x30;
+ regs[0x87] = 0x60;
+ regs[0x88] = 0x5a;
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61;
+ regs[0xc0] = 0xf8;
+ regs[0xc1] = 0x7f;
+ regs[0xc2] = 0x00;
+ regs[0xc3] = 0xf8;
+ regs[0xc4] = 0x7f;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0xf8;
+ regs[0xc7] = 0x7f;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0x8f;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x07;
+ regs[0xcd] = 0x80;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf2;
+ regs[0xd0] = 0xf4;
+ regs[0xd1] = 0xe7;
+ regs[0xd2] = 0x08;
+ regs[0xd3] = 0x0e;
+ regs[0xd4] = 0x10;
+ regs[0xd7] = 0x31;
+ regs[0xe2] = 0x02;
+ exposure=1832;
+
+ break;
+
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400_BARE for 600 dpi\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+
+ case 1200:
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_BARE for 1200 dpi\n");
+ regs[0x34] = 0xf0;
+ regs[0x40] = 0xa0;
+
+ timing=0x0426;
+
+ regs[0x85] = 0x30;
+ regs[0x86] = 0x30;
+
+ regs[0x87] = 0x60;
+ regs[0x88] = 0x5a;
+
+ regs[0x8d] = 0xbd;
+ regs[0x8e] = 0x63;
+
+ regs[0xc0] = 0xff;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0xff;
+ regs[0xc4] = 0x0f;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0xf0;
+ regs[0xc9] = 0x00;
+ regs[0xca] = 0x0e;
+ regs[0xcb] = 0x00;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0xff;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+ regs[0xd1] = 0xea;
+ regs[0xd2] = 0x0b;
+ /* regs[0xd3] = 0x17; */
+ regs[0xd4] = 0xc1;
+ regs[0xd7] = 0x14;
+ regs[0xd8] = 0xa4;
+ /* regs[0xe5] = 0x28;
+ regs[0xe6] = 0x07; 0x728=1832=915*2+2 */
+ exposure=1832;
+ break;
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_XPA for 1200 dpi\n");
+ regs[0x34] = 0x10;
+ regs[0x40] = 0xa0;
+
+ timing=0x00b0;
+ exposure=1832;
+
+ /* XXX STEF XXX */
+ regs[0x85] = 0x46;
+ regs[0x86] = 0x0b;
+
+ regs[0x87] = 0x8c;
+ regs[0x88] = 0x10;
+
+ regs[0x8e] = 0x3b;
+ regs[0x8d] = 0x60;
+
+ regs[0xc1] = 0xff;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+ regs[0x33] = 0x83;
+ regs[0x50] = 0x00;
+ regs[0x64] = 0x01;
+ regs[0x65] = 0x20;
+ regs[0x8d] = 0xbc;
+ regs[0xc0] = 0xe0;
+ regs[0xc2] = 0x01;
+ regs[0xc3] = 0x1f;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xfe;
+ regs[0xc6] = 0xff;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0x3f;
+ regs[0xca] = 0xfe;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x00;
+ regs[0xcd] = 0x00;
+ regs[0xd1] = 0xec;
+ regs[0xd2] = 0x0d;
+ regs[0xd3] = 0x05;
+ regs[0xd4] = 0x67;
+ regs[0xd7] = 0x10;
+ regs[0xd8] = 0x52;
+ break;
+
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400 for 1200 dpi\n");
+ regs[0x13] = 0x39;
+ regs[0x14] = 0xf0;
+ regs[0x15] = 0x29;
+ regs[0x16] = 0x0f;
+ regs[0x17] = 0x10;
+ regs[0x23] = 0x00;
+ regs[0x34] = 0xf0;
+ regs[0x36] = 0x29;
+ regs[0x39] = 0x00;
+ regs[0x40] = 0xa0;
+ timing=0x0426;
+ regs[0x85] = 0x30;
+ regs[0x86] = 0x30;
+ regs[0x87] = 0x60;
+ regs[0x88] = 0x5a;
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61;
+ regs[0xc0] = 0xe0;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0x01;
+ regs[0xc3] = 0x1f;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xfe;
+ regs[0xc6] = 0xff;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0x3f;
+ regs[0xca] = 0xfe;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x00;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+ regs[0xd1] = 0xec;
+ regs[0xd2] = 0x0d;
+ /* regs[0xd3] = 0x05; */
+ regs[0xd4] = 0x67;
+ regs[0xd7] = 0x10;
+ regs[0xd8] = 0x52;
+ regs[0xe2] = 0x02;
+ *light = 0x23;
+ exposure=3665;
+ break;
+
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_io,
+ "setup_shading_calibration: setting up SENSOR_TYPE_4400_BARE for 1200 dpi\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+ }
+
+ /* apply computed settings */
+ SET_DOUBLE (regs, EXPOSURE_REG, exposure);
+ SET_DOUBLE (regs, TIMING_REG, timing);
+ SET_DOUBLE (regs, TIMING1_REG, timing+1);
+ SET_DOUBLE (regs, TIMING2_REG, timing+2);
+
+ /* in logs, the driver use the computed offset minus 2 */
+ sanei_rts88xx_set_offset (regs, dev->red_offset, dev->green_offset, dev->blue_offset);
+ sanei_rts88xx_set_gain (regs, dev->red_gain, dev->green_gain, dev->blue_gain);
+ sanei_rts88xx_set_scan_area (regs, 1, 1 + lines, dev->xstart, dev->xstart + dev->pixels);
+
+ DBG (DBG_proc, "setup_shading_calibration: exit\n");
+ return status;
+}
+
+/*
+ * do shading calibration
+ * We scan a 637 pixels by 66 linesxoffset=24 , xend=661, pixels=637
+y offset=1 , yend=67, lines =66
+ */
+static SANE_Status
+shading_calibration (struct Rts8891_Device *dev, SANE_Bool color, int mode, int light)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int width;
+ int lines = 66;
+ SANE_Byte format;
+ int size;
+ int status1;
+ int x, y, sum;
+ SANE_Byte *image = NULL;
+ FILE *dbg = NULL;
+
+ DBG (DBG_proc, "shading_calibration: start\n");
+ DBG (DBG_info, "shading_calibration: sensor type is %s (%d)\n",
+ sensor_name (dev->sensor), dev->sensor);
+
+ width = dev->pixels;
+ size = lines * dev->bytes_per_line;
+
+ image = (SANE_Byte *) malloc (size);
+ if (image == NULL)
+ {
+ DBG (DBG_error,
+ "shading_calibration: failed to allocate memory for image\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (dev->shading_data != NULL)
+ free (dev->shading_data);
+ dev->shading_data = (unsigned char *) malloc (dev->bytes_per_line);
+ if (dev->shading_data == NULL)
+ {
+ free (image);
+ DBG (DBG_error,
+ "shading_calibration: failed to allocate memory for data\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* set up registers */
+ status=setup_shading_calibration(dev, mode, &light, &status1, dev->regs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "shading_calibration: failed to set up registers\n");
+ free (dev->shading_data);
+ dev->shading_data = NULL;
+ free (image);
+ return status;
+ }
+
+ /* scan shading area */
+ sanei_rts88xx_set_status (dev->devnum, dev->regs, status1, light);
+ format = rts8891_data_format (dev->xdpi, dev->sensor);
+ status = rts8891_simple_scan (dev->devnum, dev->regs, dev->reg_count, format, size, image);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "shading_calibration: failed scan shading area\n");
+ free (dev->shading_data);
+ dev->shading_data = NULL;
+ free (image);
+ return status;
+ }
+
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ dbg = fopen ("shading.pnm", "wb");
+ if (color)
+ {
+ fprintf (dbg, "P6\n%d %d\n255\n", width, lines);
+ fwrite (image, width * 3, lines, dbg);
+ }
+ else
+ {
+ fprintf (dbg, "P5\n%d %d\n255\n", width, lines);
+ fwrite (image, width, lines, dbg);
+ }
+ fclose (dbg);
+ }
+
+ /* now we can compute shading calibration data */
+ /* we average each row */
+ /* we take the maximum of each row */
+ /* we skip first 3 lines and last 10 lines due to some bugs */
+ if (color)
+ {
+ width = width * 3;
+ }
+ for (x = 0; x < width; x++)
+ {
+ sum = 0;
+ for (y = 3; y < lines - 10; y++)
+ {
+ sum += image[x + y * width];
+ }
+ dev->shading_data[x] = sum / (lines - 13);
+ }
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ dbg = fopen ("shading_data.pnm", "wb");
+ if (color)
+ {
+ fprintf (dbg, "P6\n%d %d\n255\n", width / 3, 1);
+ fwrite (dev->shading_data, width, 1, dbg);
+ }
+ else
+ {
+ fprintf (dbg, "P5\n%d %d\n255\n", width, 1);
+ fwrite (dev->shading_data, width, 1, dbg);
+ }
+ fclose (dbg);
+ }
+
+ free (image);
+ DBG (DBG_proc, "shading_calibration: exit\n");
+ return status;
+}
+
+static void
+fill_gamma (SANE_Byte * calibration, int *idx, SANE_Word * gamma)
+{
+ int i;
+
+ calibration[*idx] = 0;
+ (*idx)++;
+ for (i = 0; i < 255; i++)
+ {
+ calibration[*idx] = gamma[i];
+ /* escape 0xaa with 0 */
+ if (calibration[*idx] == 0xaa)
+ {
+ (*idx)++;
+ calibration[*idx] = 0;
+ }
+ (*idx)++;
+ calibration[*idx] = gamma[i];
+ /* escape 0xaa with 0 */
+ if (calibration[*idx] == 0xaa)
+ {
+ (*idx)++;
+ calibration[*idx] = 0;
+ }
+ (*idx)++;
+ }
+ calibration[*idx] = 0xff;
+ (*idx)++;
+}
+
+/*
+ * build and send calibration data which contains gamma table and
+ * shading correction coefficient
+ *
+ */
+static SANE_Status
+send_calibration_data (struct Rts8891_Session *session)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ int size, width, data_size;
+ SANE_Byte *calibration = NULL, format, val;
+ struct Rts8891_Device *dev = session->dev;
+ int i, idx;
+ unsigned int value, red_code, blue_code, green_code;
+ FILE *calib = NULL;
+ SANE_Word *gamma_r, *gamma_g, *gamma_b;
+
+ DBG (DBG_proc, "send_calibration_data: start\n");
+
+ /* 675 pixels at 75 DPI, 16 bits values, 3 color channels */
+ /* 5400 pixels at max sensor 600 dpi */
+ /* 3 16bits 256 value gamma tables plus start/end markers */
+ /* must multple of 32 */
+ data_size = (675 * dev->xdpi) / 75;
+
+ width = dev->pixels;
+
+ /* effective data calibration size */
+ size = data_size * 2 * 3 + 3 * (512 + 2);
+ size = ((size + 31) / 32) * 32;
+
+ DBG (DBG_io, "send_calibration_data: size=%d\n", size);
+
+ /*
+ * FORMAT:
+ * 00
+ * 512 bytes gamma table (256 16 bit entry)
+ * FF
+ * 00
+ * 512 bytes gamma table (256 16 bit entry)
+ * FF
+ * 00
+ * 512 bytes gamma table (256 16 bit entry)
+ * FF
+ * 5400 max shading coefficients at 600 dpi repeated 3 times
+ * overall size rounded at 32 bytes multiple
+ * 675 CCD elements at 75 DPI. 16 bit per element. 1 or 3 channels.
+ * there is a 0xFF marker at end of each coefficients row.
+ * a gamma table comes first
+ * 75 DPI: 5600=1542+(675)*2*3+8 ->size rounded to 'upper 32'
+ * 150 DPI: 9664=675*2*2*3=1542+(675*2)*2*3+22
+ * 17760 4 +18
+ * 33952 8 +10
+ * 65472+896=66368= 16 +26
+ *
+ * COEFFICIENT 16 bit value
+ * first is 00, 0x40, 0x80 or 0xC0 => 10 significant bit
+ * coeff*average=K => coeff=K/average
+ */
+
+ calibration = (SANE_Byte *) malloc (size);
+ if (calibration == NULL)
+ {
+ DBG (DBG_error,
+ "send_calibration_data: failed to allocate memory for calibration data\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (calibration, 0x00, size);
+
+ /* fill gamma tables first, markers and static gamma table */
+ idx = 0;
+
+ /* select red gamma table */
+ if (session->params.format == SANE_FRAME_RGB)
+ {
+ /* 3 different gamma table */
+ gamma_r = session->val[OPT_GAMMA_VECTOR_R].wa;
+ gamma_g = session->val[OPT_GAMMA_VECTOR_G].wa;
+ gamma_b = session->val[OPT_GAMMA_VECTOR_B].wa;
+ }
+ else
+ {
+ /* 3 time the same gamma table */
+ gamma_r = session->val[OPT_GAMMA_VECTOR].wa;
+ gamma_g = session->val[OPT_GAMMA_VECTOR].wa;
+ gamma_b = session->val[OPT_GAMMA_VECTOR].wa;
+ }
+
+ fill_gamma (calibration, &idx, gamma_r);
+ fill_gamma (calibration, &idx, gamma_g);
+ fill_gamma (calibration, &idx, gamma_b);
+
+ /* compute calibration coefficients */
+ /* real witdh != 675 --> 637
+ * shading data calibration starts at 1542. There are 3 rows of 16 bits values
+ * first row is green calibration
+ */
+
+ /* to avoid problems with 0xaa values which must be escaped, we change them
+ * into 0xab values, which unnoticeable on scans */
+ for (i = 0; i < width; i++)
+ {
+ /* average TARGET CODE 3431046 */
+/* #define RED_SHADING_TARGET_CODE 3000000
+ #define GREEN_SHADING_TARGET_CODE 300000
+ #define BLUE_SHADING_TARGET_CODE 2800000*/
+#define RED_SHADING_TARGET_CODE 2800000
+#define GREEN_SHADING_TARGET_CODE 2800000
+#define BLUE_SHADING_TARGET_CODE 2700000
+
+ red_code = RED_SHADING_TARGET_CODE;
+ green_code = GREEN_SHADING_TARGET_CODE;
+ blue_code = BLUE_SHADING_TARGET_CODE;
+
+ /* target code debug, will be removed for the release */
+ if (getenv ("RED_CODE") != NULL)
+ {
+ red_code = atoi (getenv ("RED_CODE"));
+ }
+ if (getenv ("GREEN_CODE") != NULL)
+ {
+ blue_code = atoi (getenv ("GREEN_CODE"));
+ }
+ if (getenv ("BLUE_CODE") != NULL)
+ {
+ green_code = atoi (getenv ("BLUE_CODE"));
+ }
+
+ /* correction coefficient is target code divided by average scanned value
+ * but it is put in a 16 bits number. Only 10 first bits are significants.
+ */
+ /* first color component red data */
+ if (gamma_r[dev->shading_data[i * 3]] < 5)
+ value = 0x8000;
+ else
+ value = red_code / gamma_r[dev->shading_data[i * 3]];
+ val = (SANE_Byte) (value / 256);
+ if (val == 0xaa)
+ val++;
+ calibration[idx + i * 2 + 1] = val;
+ calibration[idx + i * 2] = (SANE_Byte) (value % 256) & 0xC0;
+
+ /* second color component: green data */
+ if (gamma_r[dev->shading_data[i * 3 + 1]] < 5)
+ value = 0x8000;
+ else
+ value = green_code / gamma_g[dev->shading_data[i * 3 + 1]];
+ val = (SANE_Byte) (value / 256);
+ if (val == 0xaa)
+ val++;
+ calibration[idx + data_size * 2 + i * 2 + 1] = val;
+ calibration[idx + data_size * 2 + i * 2] =
+ (SANE_Byte) (value % 256) & 0xC0;
+
+ /* third color component: blue data */
+ if (gamma_r[dev->shading_data[i * 3 + 2]] < 5)
+ value = 0x8000;
+ else
+ value = blue_code / gamma_b[dev->shading_data[i * 3 + 2]];
+ val = (SANE_Byte) (value / 256);
+ if (val == 0xaa)
+ val++;
+ calibration[idx + data_size * 4 + i * 2 + 1] = val;
+ calibration[idx + data_size * 4 + i * 2] =
+ (SANE_Byte) (value % 256) & 0xC0;
+ }
+
+ /* DEEP TRACING */
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ calib = fopen ("calibration.hex", "wb");
+ fprintf (calib, "shading_data(%d)=", width);
+ for (i = 0; i < width * 3; i++)
+ {
+ fprintf (calib, "%02x ", dev->shading_data[i]);
+ }
+ fprintf (calib, "\n");
+ fprintf (calib, "write_mem(0x00,%d)=", size);
+ for (i = 0; i < size; i++)
+ {
+ fprintf (calib, "%02x ", calibration[i]);
+ }
+ fclose (calib);
+ }
+
+ /* signals color format/divisor from hardware */
+ format = rts8891_data_format (dev->xdpi, dev->sensor);
+ status = sanei_rts88xx_write_reg (dev->devnum, 0xd3, &format);
+
+ /* for some reason, we have to add 6 to the size for the first write */
+ /* related to the 6 0xaa in gamma table ? */
+ if (size > RTS88XX_MAX_XFER_SIZE)
+ {
+ status =
+ sanei_rts88xx_write_mem (dev->devnum, RTS88XX_MAX_XFER_SIZE, 6,
+ calibration);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "send_calibration_data: failed to write calibration data (part 1)\n");
+ return status;
+ }
+ size -= RTS88XX_MAX_XFER_SIZE;
+ status =
+ sanei_rts88xx_write_mem (dev->devnum, size, 0,
+ calibration + RTS88XX_MAX_XFER_SIZE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "send_calibration_data: failed to write calibration data (part 2)\n");
+ return status;
+ }
+ }
+ else
+ {
+ status = sanei_rts88xx_write_mem (dev->devnum, size, 6, calibration);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "send_calibration_data: failed to write calibration data\n");
+ return status;
+ }
+ }
+
+ /* set mem start */
+ dev->regs[0x91] = 0x00;
+ dev->regs[0x92] = 0x00;
+ sanei_rts88xx_write_regs (dev->devnum, 0x91, dev->regs + 0x91, 2);
+
+ free (calibration);
+ DBG (DBG_proc, "send_calibration_data: exit\n");
+ return status;
+}
+
+/* move at dev->model->min_ydpi dpi up to the scanning area. Which speeds
+ * up scanning
+ */
+static SANE_Status
+move_to_scan_area (struct Rts8891_Session *session)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte control;
+ SANE_Byte regs[RTS8891_MAX_REGISTERS];
+ struct Rts8891_Device *dev = session->dev;
+ SANE_Int distance;
+
+ DBG (DBG_proc, "move_to_scan_area: start\n");
+
+ /* compute line number to move and fix scan values */
+ distance = ((dev->ystart - 1) * MOVE_DPI) / dev->ydpi;
+ dev->ystart = dev->ystart - (distance * dev->ydpi) / MOVE_DPI;
+ /* extra lines to let head stop */
+ distance -= 30;
+
+ DBG (DBG_proc, "move_to_scan_area: distance=%d, ystart=%d\n", distance,
+ dev->ystart);
+
+ /* then send move command */
+ rts8891_move (dev, regs, distance, SANE_TRUE);
+
+ /* wait for completion */
+ do
+ {
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ }
+ while ((control & 0x08) == 0x08);
+
+ DBG (DBG_proc, "move_to_scan_area: exit\n");
+ return status;
+}
+
+/* set up the shadow registers for scan, depending on scan parameters */
+/* the ultimate goal is to have no direct access to registers, but to */
+/* set them through helper functions */
+/* NOTE : I couldn't manage to get scans that really uses gray settings. */
+/* The windows driver is allways scanning in color, so we do the same. */
+/* For now, the only mode that could be done would be 300 dpi gray scan, */
+/* based on the register settings of find_origin() */
+#ifndef UNIT_TESTING
+static
+#endif
+SANE_Status
+setup_scan_registers (struct Rts8891_Session *session, SANE_Byte *status1, SANE_Byte *status2, SANE_Byte *regs)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct Rts8891_Device *dev = session->dev;
+ int exposure=0;
+ int timing=0;
+
+ /* only software gray modes for now */
+ if (session->params.format == SANE_FRAME_GRAY
+ && (session->dev->model->flags & RTS8891_FLAG_EMULATED_GRAY_MODE) == 0)
+ {
+ DBG (DBG_warn,
+ "setup_scan_registers: native gray modes not implemented for this model, failure expected\n");
+ }
+ sanei_rts88xx_set_scan_area (regs, dev->ystart, dev->ystart + dev->lines, dev->xstart, dev->xstart + dev->pixels);
+ DBG (DBG_info, "setup_scan_registers: xstart=%d, pixels=%d\n", dev->xstart, dev->pixels);
+ DBG (DBG_info, "setup_scan_registers: ystart=%d, lines =%d\n", dev->ystart, dev->lines);
+
+ /* this is full register set from a color preview */
+ regs[0x00] = regs[0x00] & 0xef;
+ regs[0x01] = 0x41;
+
+ sanei_rts88xx_set_offset (regs, dev->red_offset, dev->green_offset, dev->blue_offset);
+ sanei_rts88xx_set_gain (regs, dev->red_gain, dev->green_gain, dev->blue_gain);
+
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_4400:
+ case SENSOR_TYPE_4400_BARE:
+ *status1 = 0x10;
+ *status2 = 0x2a;
+ break;
+ case SENSOR_TYPE_XPA:
+ *status1 = 0x20;
+ *status2 = 0x3f;
+ break;
+ default:
+ *status1 = 0x20;
+ *status2 = 0x3b;
+ }
+
+ /* default to 75 dpi color scan */
+ regs[0x0b] = 0x70;
+ regs[0x0c] = 0x00;
+ regs[0x0d] = 0x00;
+ regs[0x0e] = 0x00;
+ regs[0x0f] = 0x00;
+
+ regs[0x12] = 0xff;
+ regs[0x13] = 0x20;
+ regs[0x14] = 0xf8;
+ regs[0x15] = 0x28;
+ regs[0x16] = 0x01;
+ regs[0x17] = 0x00;
+ regs[0x18] = 0xff;
+ regs[0x19] = 0x00;
+ regs[0x1a] = 0x00;
+ regs[0x1b] = 0x00;
+ regs[0x1c] = 0x00;
+ regs[0x1d] = 0x20;
+ regs[0x1e] = 0x00;
+ regs[0x1f] = 0x00;
+
+ /* LCD display */
+ regs[0x20] = 0x3a;
+ regs[0x21] = 0xf2;
+ regs[0x22] = 0x00;
+
+ regs[0x23] = 0x80;
+ regs[0x24] = 0xff;
+ regs[0x25] = 0x00;
+ regs[0x26] = 0x00;
+ regs[0x27] = 0x00;
+ regs[0x28] = 0x00;
+ regs[0x29] = 0x00;
+ regs[0x2a] = 0x00;
+ regs[0x2b] = 0x00;
+ regs[0x2c] = 0x00;
+ regs[0x2d] = 0x00;
+ regs[0x2e] = 0x00;
+ regs[0x2f] = 0x00;
+ regs[0x30] = 0x00;
+ regs[0x31] = 0x00;
+ regs[0x32] = 0x20;
+ regs[0x33] = 0x83;
+ regs[0x34] = 0x10;
+ regs[0x35] = 0x47;
+ regs[0x36] = 0x2c;
+ regs[0x37] = 0x00;
+ regs[0x38] = 0x00;
+ regs[0x39] = 0x02;
+ regs[0x3a] = 0x43;
+ regs[0x3b] = 0x00;
+ regs[0x3c] = 0x00;
+ regs[0x3d] = 0x00;
+ regs[0x3e] = 0x00;
+ regs[0x3f] = 0x00;
+ regs[0x40] = 0x2c; /* 0x0c -> use shading data */
+ regs[0x41] = 0x00;
+ regs[0x42] = 0x00;
+ regs[0x43] = 0x00;
+ regs[0x44] = 0x8c;
+ regs[0x45] = 0x76;
+ regs[0x46] = 0x00;
+ regs[0x47] = 0x00;
+ regs[0x48] = 0x00;
+ regs[0x49] = 0x00;
+ regs[0x4a] = 0x00;
+ regs[0x4b] = 0x00;
+ regs[0x4c] = 0x00;
+ regs[0x4d] = 0x00;
+ regs[0x4e] = 0x00;
+ regs[0x4f] = 0x00;
+ regs[0x50] = 0x00;
+ regs[0x51] = 0x00;
+ regs[0x52] = 0x00;
+ regs[0x53] = 0x00;
+ regs[0x54] = 0x00;
+ regs[0x55] = 0x00;
+ regs[0x56] = 0x00;
+ regs[0x57] = 0x00;
+ regs[0x58] = 0x00;
+ regs[0x59] = 0x00;
+ regs[0x5a] = 0x00;
+ regs[0x5b] = 0x00;
+ regs[0x5c] = 0x00;
+ regs[0x5d] = 0x00;
+ regs[0x5e] = 0x00;
+ regs[0x5f] = 0x00;
+
+ regs[0x64] = 0x01;
+ regs[0x65] = 0x20;
+
+ regs[0x68] = 0x00;
+ regs[0x69] = 0x00;
+ regs[0x6a] = 0x00;
+ regs[0x6b] = 0x00;
+
+ regs[0x6e] = 0x00;
+ regs[0x6f] = 0x00;
+ regs[0x70] = 0x00;
+ regs[0x71] = 0x00;
+
+ regs[0x72] = 0xe1;
+ regs[0x73] = 0x14;
+ regs[0x74] = 0x18;
+ regs[0x75] = 0x15;
+
+ regs[0x76] = 0x00;
+ regs[0x77] = 0x00;
+ regs[0x78] = 0x00;
+ regs[0x79] = 0x20;
+ regs[0x7a] = 0x01;
+ regs[0x7b] = 0x00;
+ regs[0x7c] = 0x00;
+ regs[0x7d] = 0x00;
+ regs[0x7e] = 0x00;
+ regs[0x7f] = 0x00;
+ timing=0x00af;
+ regs[0x84] = 0x00;
+ regs[0x85] = 0x46;
+ regs[0x86] = 0x0b;
+ regs[0x87] = 0x8c;
+ regs[0x88] = 0x10;
+ regs[0x8b] = 0xff;
+ regs[0x8c] = 0x3f;
+ regs[0x8d] = 0x3b;
+
+ regs[0x8e] = 0x60;
+ regs[0x8f] = 0x00;
+ regs[0x90] = 0x18; /* 0x1c when shading calibration */
+
+ /* overwritten when calibration data is sent */
+ regs[0x91] = 0x00;
+ regs[0x92] = 0x00;
+
+ regs[0x93] = 0x01;
+
+ regs[0x94] = 0x0e;
+ regs[0x95] = 0x00;
+ regs[0x96] = 0x00;
+ regs[0x97] = 0x00;
+ regs[0x98] = 0x00;
+ regs[0x99] = 0x00;
+ regs[0x9a] = 0x00;
+ regs[0x9b] = 0x00;
+ regs[0x9c] = 0x00;
+ regs[0x9d] = 0x00;
+ regs[0x9e] = 0x00;
+ regs[0x9f] = 0x00;
+ regs[0xa0] = 0x00;
+ regs[0xa1] = 0x00;
+ regs[0xa2] = 0x00;
+ regs[0xa3] = 0xcc;
+ regs[0xa4] = 0x27;
+ regs[0xa5] = 0x64;
+ regs[0xa6] = 0x00;
+ regs[0xa7] = 0x00;
+ regs[0xa8] = 0x00;
+ regs[0xa9] = 0x00;
+ regs[0xaa] = 0x00;
+ regs[0xab] = 0x00;
+ regs[0xac] = 0x00;
+ regs[0xad] = 0x00;
+ regs[0xae] = 0x00;
+ regs[0xaf] = 0x00;
+ regs[0xb0] = 0x00;
+ regs[0xb1] = 0x00;
+ regs[0xb2] = 0x02;
+
+ regs[0xb4] = 0x00;
+ regs[0xb5] = 0x00;
+ regs[0xb6] = 0x00;
+ regs[0xb7] = 0x00;
+ regs[0xb8] = 0x00;
+ regs[0xb9] = 0x00;
+ regs[0xba] = 0x00;
+ regs[0xbb] = 0x00;
+ regs[0xbc] = 0x00;
+ regs[0xbd] = 0x00;
+ regs[0xbe] = 0x00;
+ regs[0xbf] = 0x00;
+ regs[0xc0] = 0x06;
+ regs[0xc1] = 0xe6;
+ regs[0xc2] = 0x67;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0xff;
+ regs[0xc5] = 0xff;
+ regs[0xc6] = 0xff;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0xff;
+ regs[0xc9] = 0x07;
+ regs[0xca] = 0x00;
+ regs[0xcb] = 0xfe;
+ regs[0xcc] = 0xf9;
+ regs[0xcd] = 0x19;
+ regs[0xce] = 0x98;
+ regs[0xcf] = 0xe8;
+ regs[0xd0] = 0xea;
+
+ regs[0xd1] = 0xf3;
+ regs[0xd2] = 0x14;
+
+ regs[0xd3] = 0x02;
+ regs[0xd4] = 0x04;
+ regs[0xd5] = 0x86;
+
+ regs[0xd6] = 0x0f;
+ regs[0xd7] = 0x30; /* 12303 */
+
+ regs[0xd8] = 0x52; /* 0x5230=21040 */
+
+ regs[0xd9] = 0xad;
+ regs[0xda] = 0xa7;
+ regs[0xdb] = 0x00;
+ regs[0xdc] = 0x00;
+ regs[0xdd] = 0x00;
+ regs[0xde] = 0x00;
+ regs[0xdf] = 0x00;
+ regs[0xe0] = 0x00;
+ regs[0xe1] = 0x00;
+ regs[0xe2] = 0x0f;
+ regs[0xe3] = 0x85;
+ regs[0xe4] = 0x03;
+
+ /* regs[0xe5] = 0x52;
+ regs[0xe6] = 0x00; exposure time 0x0052=82 */
+ exposure=82;
+
+ regs[0xe7] = 0x75;
+ regs[0xe8] = 0x01;
+ regs[0xe9] = 0x0b;
+ regs[0xea] = 0x54;
+ regs[0xeb] = 0x01;
+ regs[0xec] = 0x04;
+ regs[0xed] = 0xb8;
+ regs[0xee] = 0x00;
+ regs[0xef] = 0x03;
+ regs[0xf0] = 0x70;
+ regs[0xf1] = 0x00;
+ regs[0xf2] = 0x01;
+ regs[0xf3] = 0x00;
+ if (dev->sensor == SENSOR_TYPE_XPA || dev->sensor == SENSOR_TYPE_4400)
+ {
+ regs[0xc0] = 0x67;
+ regs[0xc1] = 0x06;
+ regs[0xc2] = 0xe6;
+ regs[0xc3] = 0x98;
+ regs[0xc4] = 0xf9;
+ regs[0xc5] = 0x19;
+ regs[0xc6] = 0x67;
+ regs[0xc7] = 0x06;
+ regs[0xc8] = 0xe6;
+ regs[0xc9] = 0x01;
+ regs[0xca] = 0xf8;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x98;
+ regs[0xcd] = 0xf9;
+ regs[0xce] = 0x19;
+ regs[0xcf] = 0xe0;
+ regs[0xd0] = 0xe2;
+
+ regs[0xd1] = 0xeb;
+ regs[0xd2] = 0x0c;
+
+ regs[0xd7] = 0x10;
+ }
+ if (dev->sensor == SENSOR_TYPE_4400)
+ {
+ regs[0x13] = 0x39; /* 0x20 */
+ regs[0x14] = 0xf0; /* 0xf8 */
+ regs[0x15] = 0x29; /* 0x28 */
+ regs[0x16] = 0x00; /* 0x01 */
+ regs[0x17] = 0x10; /* 0x00 */
+ regs[0x23] = 0x00; /* 0x80 */
+ regs[0x35] = 0x47; /* 0x45 */
+ regs[0x36] = 0x29; /* 0x2c */
+ regs[0x39] = 0x00; /* 0x02 */
+ timing=0x00af;
+ regs[0x85] = 0x46; /* 0x8c */
+ regs[0x86] = 0x0b; /* 0x10 */
+ regs[0x87] = 0x8c; /* 0x18 */
+ regs[0x88] = 0x10; /* 0x1b */
+ regs[0x8d] = 0x3b; /* 0x77 */
+
+ regs[0xd3] = 0x02; /* 0x0e */
+ regs[0xd4] = 0x04; /* 0x10 */
+ regs[0xe2] = 0x07; /* 0x0f */
+ regs[0xe3] = 0x84; /* 0x87 */
+ /*regs[0xe5] = 0xa5; 0x54 */
+ exposure=165;
+ regs[0xe7] = 0x0e; /* 0xa8 */
+ regs[0xe8] = 0x01; /* 0x00 */
+ regs[0xe9] = 0x0a; /* 0x0b */
+ regs[0xea] = 0xc2; /* 0x56 */
+ regs[0xed] = 0xf6; /* 0xba */
+ regs[0xef] = 0x02; /* 0x03 */
+ regs[0xf0] = 0xa8; /* 0x72 */
+ }
+ if (dev->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ regs[0x13] = 0x39; /* 0x20 */
+ regs[0x14] = 0xf0; /* 0xf8 */
+ regs[0x15] = 0x29; /* 0x28 */
+ regs[0x16] = 0x00; /* 0x07 */
+ regs[0x17] = 0x10; /* 0x00 */
+ regs[0x23] = 0x00; /* 0xff */
+ regs[0x35] = 0x47; /* 0x0e */
+ regs[0x36] = 0x29; /* 0x2c */
+ regs[0x39] = 0x00; /* 0x02 */
+ regs[0x3a] = 0x43; /* 0x0e */
+ regs[0x40] = 0x2c; /* 0x20 */
+ regs[0x85] = 0x46; /* 0x00 */
+ regs[0x86] = 0x0b; /* 0x06 */
+ regs[0x87] = 0x8c; /* 0x00 */
+ regs[0x88] = 0x10; /* 0x06 */
+ regs[0x8d] = 0x3b; /* 0x00 */
+ regs[0x90] = 0x18; /* 0x1c */
+ regs[0xe2] = 0x07; /* 0x05 */
+ regs[0xe3] = 0x84; /* 0x00 */
+ regs[0xe4] = 0x03; /* 0x00 */
+ timing=0x00af;
+ exposure=165;
+ regs[0xe7] = 0x0e; /* 0x00 */
+ regs[0xe8] = 0x01; /* 0x00 */
+ regs[0xe9] = 0x0a; /* 0x00 */
+ regs[0xea] = 0xc2; /* 0x00 */
+ regs[0xeb] = 0x01; /* 0x00 */
+ regs[0xec] = 0x04; /* 0x00 */
+ regs[0xed] = 0xf6; /* 0x00 */
+ regs[0xef] = 0x02; /* 0x00 */
+ regs[0xf0] = 0xa8; /* 0x00 */
+ regs[0xf2] = 0x01; /* 0x00 */
+ }
+ switch (dev->xdpi)
+ {
+ case 75:
+ break;
+ case 150:
+ regs[0x35] = 0x45;
+
+
+ regs[0x85] = 0x8c;
+ regs[0x86] = 0x10;
+
+ regs[0x87] = 0x18;
+ regs[0x88] = 0x1b;
+
+ regs[0x8d] = 0x77;
+
+ regs[0xe3] = 0x87;
+
+ /* regs[0xe5] = 0x54;
+ regs[0xe6] = 0x00; exposure time 0x0054=84 */
+ exposure=84;
+ timing=0x012e;
+
+ regs[0xe7] = 0xa8;
+ regs[0xe8] = 0x00;
+ regs[0xea] = 0x56;
+ regs[0xed] = 0xba;
+
+ regs[0xf0] = 0x72;
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_XPA for 150 dpi\n");
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0x8e;
+ regs[0xc2] = 0xff;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x71;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0x8e;
+ regs[0xc8] = 0xff;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0x1f;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x71;
+ regs[0xce] = 0x00;
+ regs[0xcf] = 0xe6;
+ regs[0xd0] = 0xe8;
+ regs[0xd1] = 0xf6;
+ regs[0xd2] = 0x17;
+ regs[0xd3] = 0x0b;
+ regs[0xd4] = 0x0d;
+ break;
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_BARE for 150 dpi\n");
+ regs[0xc0] = 0x80;
+ regs[0xc1] = 0x87;
+ regs[0xc2] = 0x7f;
+ regs[0xc9] = 0x00;
+ regs[0xcb] = 0x78;
+ regs[0xcc] = 0x7f;
+ regs[0xcd] = 0x78;
+ regs[0xce] = 0x80;
+ regs[0xcf] = 0xe6;
+ regs[0xd0] = 0xe8;
+
+ regs[0xd1] = 0xf7;
+ regs[0xd2] = 0x00;
+
+ regs[0xd3] = 0x0e;
+ regs[0xd4] = 0x10;
+ break;
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_4400 for 150 dpi\n");
+ regs[0x35] = 0x47;
+
+ exposure=170;
+ timing=0x012e;
+ regs[0x85] = 0x8c;
+ regs[0x86] = 0x10;
+ regs[0x87] = 0x18;
+ regs[0x88] = 0x1b;
+ regs[0x8d] = 0x77;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0x8e;
+ regs[0xc2] = 0xff;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x71;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0x8e;
+ regs[0xc8] = 0xff;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0x1f;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x71;
+ regs[0xce] = 0x00;
+ regs[0xcf] = 0xe6;
+ regs[0xd0] = 0xe8;
+ regs[0xd1] = 0xf6;
+ regs[0xd2] = 0x17;
+ regs[0xd3] = 0x0b;
+ regs[0xd4] = 0x0d;
+ regs[0xe3] = 0x86;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x1c;
+ regs[0xe9] = 0x01;
+ regs[0xea] = 0x0a;
+ regs[0xeb] = 0xc4;
+ regs[0xec] = 0x01;
+ regs[0xed] = 0x04;
+ regs[0xee] = 0xf8;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x02;
+ regs[0xf2] = 0x00;
+ break;
+ }
+ break;
+
+ case 300:
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_XPA for 300 dpi\n");
+ regs[0x35] = 0x0e; /* fast ? */
+ regs[0x3a] = 0x0e;
+
+
+ regs[0x85] = 0x18;
+ regs[0x86] = 0x1b;
+
+ regs[0x87] = 0x30;
+ regs[0x88] = 0x30;
+
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0x8d] = 0xef;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0x0f;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xf0;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x0f;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0xf1;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0xf0;
+ regs[0xcf] = 0xed;
+ regs[0xd0] = 0xef;
+ regs[0xd1] = 0xe2;
+ regs[0xd2] = 0x03;
+ regs[0xd3] = 0x17;
+ regs[0xd4] = 0x01;
+ regs[0xe2] = 0x07;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ timing=0x022b;
+ exposure=342;
+ regs[0xf2] = 0x00;
+ break;
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_BARE for 300 dpi\n");
+ regs[0x35] = 0x0e; /* fast ? */
+ regs[0x3a] = 0x0e;
+
+ timing=0x022b;
+
+ regs[0x85] = 0x18;
+ regs[0x86] = 0x1b;
+
+ regs[0x87] = 0x30;
+ regs[0x88] = 0x30;
+
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0x8d] = 0xf0;
+ regs[0x8e] = 0x60; /* low nibble of 8e and 8d are proportional to
+ the scanned width 1de => 5100 wide scan */
+
+ regs[0xc0] = 0xff;
+ regs[0xc1] = 0x0f;
+ regs[0xc2] = 0x00;
+ regs[0xc9] = 0x00;
+ regs[0xca] = 0x0e;
+ regs[0xcb] = 0x00;
+ regs[0xcc] = 0x00;
+ regs[0xcd] = 0xf0;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+ regs[0xd1] = 0xea;
+ regs[0xd2] = 0x0b;
+
+ regs[0xd3] = 0x17;
+ regs[0xd4] = 0x01;
+
+ regs[0xe2] = 0x07;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+
+ exposure=342;
+ break;
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_4400 for 300 dpi\n");
+ regs[0x11] = 0x22;
+ regs[0x35] = 0x0e;
+ regs[0x3a] = 0x0e;
+ regs[0x85] = 0x18;
+ regs[0x86] = 0x1b;
+ regs[0x87] = 0x30;
+ regs[0x88] = 0x30;
+ regs[0x8d] = 0xef;
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0x0f;
+ regs[0xc3] = 0xff;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xf0;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x0f;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0xff;
+ regs[0xcb] = 0xf1;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0xf0;
+ regs[0xcf] = 0xed;
+ regs[0xd0] = 0xef;
+ regs[0xd1] = 0xe2;
+ regs[0xd2] = 0x03;
+ regs[0xd3] = 0x17;
+ regs[0xd4] = 0x01;
+ regs[0xe2] = 0x03;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ timing=0x022b;
+ exposure=686;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ break;
+ }
+ break;
+ case 600:
+ *status1 = 0x28;
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_BARE for 600 dpi\n");
+
+ regs[0x34] = 0xf0;
+ regs[0x35] = 0x1b;
+ regs[0x36] = 0x29;
+ regs[0x3a] = 0x1b;
+
+ regs[0x72] = 0x3a;
+ regs[0x73] = 0x15;
+ regs[0x74] = 0x62;
+
+ timing=0x0425;
+ regs[0x85] = 0x30;
+ regs[0x86] = 0x30;
+ regs[0x87] = 0x60;
+ regs[0x88] = 0x5a;
+
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61; /* low nibble of 8e and 8d are proportional to
+ the scanned width 1de => 5100 wide scan */
+
+ regs[0xc0] = 0xff;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0xff;
+ regs[0xc3] = 0x00;
+ regs[0xc4] = 0xf0;
+ regs[0xc7] = 0x0f;
+ regs[0xc8] = 0x00;
+ regs[0xcb] = 0xe0;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0xff;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xe9;
+ regs[0xd0] = 0xeb;
+
+ regs[0xd7] = 0x14;
+
+ regs[0xe2] = 0x01;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ /* regs[0xe5] = 0xbd;
+ regs[0xe6] = 0x0a; exposure time = 0x0abd=2749 (5500/2-1) */
+ exposure=2749;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ break;
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_XPA for 600 dpi\n");
+
+ *status2 = 0x3b;
+ regs[0x33] = 0x83; /* 0x86 */
+ regs[0x34] = 0xf0;
+ regs[0x35] = 0x1b;
+ regs[0x36] = 0x29;
+ regs[0x3a] = 0x1b;
+ regs[0x40] = 0x2c; /* 0x24 */
+ regs[0x50] = 0x00; /* 0x18 */
+
+ regs[0x64] = 0x01; /* 0x02 */
+ regs[0x65] = 0x20; /* 0x10 */
+
+ regs[0x72] = 0x3a;
+ regs[0x73] = 0x15;
+ regs[0x74] = 0x62;
+
+
+ regs[0x85] = 0x30;
+ regs[0x86] = 0x30;
+
+ regs[0x87] = 0x60;
+ regs[0x88] = 0x5a;
+
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61; /* 25054 */
+
+ regs[0xc0] = 0xf8;
+ regs[0xc1] = 0x7f;
+ regs[0xc2] = 0x00;
+ regs[0xc3] = 0xf8;
+ regs[0xc4] = 0x7f;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0xf8;
+ regs[0xc7] = 0x7f;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0x8f;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x07;
+ regs[0xcd] = 0x80;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf2;
+ regs[0xd0] = 0xf4;
+ regs[0xd1] = 0xe7;
+ regs[0xd2] = 0x08;
+ regs[0xd3] = 0x02;
+ regs[0xd4] = 0x10;
+ regs[0xd7] = 0x31;
+ regs[0xe2] = 0x01;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ exposure=2749;
+ timing=0x0425;
+ break;
+
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_4400 for 600 dpi\n");
+ *status1 = 0x10;
+ *status2 = 0x23;
+
+ regs[0x13] = 0x39;
+ regs[0x14] = 0xf0;
+ regs[0x15] = 0x29;
+ regs[0x16] = 0x00;
+ regs[0x17] = 0x10;
+ regs[0x23] = 0x00;
+ regs[0x34] = 0xf0;
+ regs[0x35] = 0x1b;
+ regs[0x36] = 0x29;
+ regs[0x39] = 0x00;
+ regs[0x3a] = 0x1b;
+ regs[0x72] = 0x3a;
+ regs[0x73] = 0x15;
+ regs[0x74] = 0x62;
+ regs[0x85] = 0x30;
+ regs[0x86] = 0x30;
+ regs[0x87] = 0x60;
+ regs[0x88] = 0x5a;
+ regs[0x8d] = 0xde;
+ regs[0x8e] = 0x61;
+ regs[0xc0] = 0xf8;
+ regs[0xc1] = 0x7f;
+ regs[0xc2] = 0x00;
+ regs[0xc3] = 0xf8;
+ regs[0xc4] = 0x7f;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0xf8;
+ regs[0xc7] = 0x7f;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0xff;
+ regs[0xca] = 0x8f;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x07;
+ regs[0xcd] = 0x80;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf2;
+ regs[0xd0] = 0xf4;
+ regs[0xd1] = 0xe7;
+ regs[0xd2] = 0x08;
+ regs[0xd3] = 0x0e;
+ regs[0xd4] = 0x10;
+ regs[0xd7] = 0x31;
+ regs[0xe2] = 0x01;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ timing=0x0425;
+ exposure=2749;
+ break;
+
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_4400_BARE for 600 dpi\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+ case 1200:
+ *status1 = 0x28;
+ switch (dev->sensor)
+ {
+ case SENSOR_TYPE_BARE:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_BARE for 1200 dpi\n");
+
+ regs[0x34] = 0xf0;
+ regs[0x35] = 0x1b;
+ regs[0x36] = 0x29;
+ regs[0x3a] = 0x1b;
+ regs[0x40] = 0xac;
+ timing=0x081a;
+ regs[0x85] = 0x60;
+ regs[0x86] = 0x5a;
+ regs[0x87] = 0xc0;
+ regs[0x88] = 0xae;
+
+ regs[0x8d] = 0xbd; /* about twice the 600 dpi values */
+ regs[0x8e] = 0x63; /* low nibble of 8e and 8d are proportional to
+ the scanned width 3b5 => 10124 wide scan */
+ regs[0xc0] = 0xff;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0xff;
+ regs[0xc4] = 0x0f;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0xf0;
+ regs[0xc9] = 0x00;
+ regs[0xca] = 0x0e;
+ regs[0xcb] = 0x00;
+ regs[0xcc] = 0xff;
+ regs[0xcd] = 0xff;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+ regs[0xd1] = 0xea;
+ regs[0xd2] = 0x0b;
+ regs[0xd3] = 0x17;
+ regs[0xd4] = 0xc1;
+
+ regs[0xd7] = 0x14;
+ regs[0xd8] = 0xa4;
+
+ regs[0xe2] = 0x01;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ /* regs[0xe5] = 0x7b;
+ regs[0xe6] = 0x15; exposure time = 0x157b=5499 */
+ exposure=5499;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ break;
+ case SENSOR_TYPE_XPA:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_XPA for 1200 dpi\n");
+ *status2 = 0x3f;
+
+ regs[0x34] = 0xf0;
+ regs[0x35] = 0x1b;
+ regs[0x36] = 0x29;
+ regs[0x3a] = 0x1b;
+ regs[0x85] = 0x60;
+ regs[0x86] = 0x5a;
+ regs[0x87] = 0xc0;
+ regs[0x8a] = 0x08; /* 81c=2076 */
+
+ regs[0xc1] = 0xff;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+
+ regs[0xe2] = 0x01;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ timing=0x081a;
+ exposure=5499;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ regs[0x33] = 0x83;
+ regs[0x40] = 0xac;
+ regs[0x50] = 0x00;
+ regs[0x64] = 0x01;
+ regs[0x65] = 0x20;
+ regs[0x88] = 0xae;
+ regs[0x8d] = 0xbc;
+ regs[0x8e] = 0x63;
+ regs[0xc0] = 0xe0;
+ regs[0xc2] = 0x01;
+ regs[0xc3] = 0x1f;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xfe;
+ regs[0xc6] = 0xff;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0x3f;
+ regs[0xca] = 0xfe;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x00;
+ regs[0xcd] = 0x00;
+ regs[0xd1] = 0xec;
+ regs[0xd2] = 0x0d;
+ regs[0xd3] = 0x05;
+ regs[0xd4] = 0x67;
+ regs[0xd7] = 0x10;
+ regs[0xd8] = 0x52;
+ break;
+
+ case SENSOR_TYPE_4400:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_4400 for 1200 dpi\n");
+ regs[0x13] = 0x39;
+ regs[0x14] = 0xf0;
+ regs[0x15] = 0x29;
+ regs[0x16] = 0x00;
+ regs[0x17] = 0x10;
+ regs[0x23] = 0x00;
+ regs[0x33] = 0x86;
+ regs[0x34] = 0xf0;
+ regs[0x35] = 0x0e;
+ regs[0x39] = 0x00;
+ regs[0x3a] = 0x0e;
+ regs[0x40] = 0xac;
+ regs[0x64] = 0x02;
+ regs[0x65] = 0x10;
+ timing=0x081a;
+ regs[0x85] = 0x60;
+ regs[0x86] = 0x5a;
+ regs[0x87] = 0xc0;
+ regs[0x88] = 0xae;
+ regs[0x8d] = 0xbc;
+ regs[0x8e] = 0x63;
+ regs[0xc0] = 0xe0;
+ regs[0xc1] = 0xff;
+ regs[0xc2] = 0x01;
+ regs[0xc3] = 0x1f;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0xfe;
+ regs[0xc6] = 0xff;
+ regs[0xc7] = 0xff;
+ regs[0xc8] = 0x00;
+ regs[0xc9] = 0x3f;
+ regs[0xca] = 0xfe;
+ regs[0xcb] = 0xff;
+ regs[0xcc] = 0x00;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0xff;
+ regs[0xcf] = 0xf5;
+ regs[0xd0] = 0xf7;
+ regs[0xd1] = 0xec;
+ regs[0xd2] = 0x0d;
+ regs[0xd3] = 0x05;
+ regs[0xd4] = 0x67;
+ regs[0xd7] = 0x10;
+ regs[0xd8] = 0x52;
+ regs[0xe2] = 0x00;
+ regs[0xe3] = 0x00;
+ regs[0xe4] = 0x00;
+ regs[0xe7] = 0x00;
+ regs[0xe8] = 0x00;
+ regs[0xe9] = 0x00;
+ regs[0xea] = 0x00;
+ regs[0xeb] = 0x00;
+ regs[0xec] = 0x00;
+ regs[0xed] = 0x00;
+ regs[0xef] = 0x00;
+ regs[0xf0] = 0x00;
+ regs[0xf2] = 0x00;
+ exposure=10999;
+ *status1 = 0x10;
+ *status2 = 0x23;
+ break;
+
+ case SENSOR_TYPE_4400_BARE:
+ DBG (DBG_io, "setup_scan_registers: setting up SENSOR_TYPE_4400_BARE for 1200 dpi\n");
+ return SANE_STATUS_INVAL;
+ break;
+ }
+ break;
+ }
+
+ /* apply computed settings */
+ SET_DOUBLE (regs, EXPOSURE_REG, exposure);
+ SET_DOUBLE (regs, TIMING_REG, timing);
+ SET_DOUBLE (regs, TIMING1_REG, timing+1);
+ SET_DOUBLE (regs, TIMING2_REG, timing+2);
+
+ /* sets divisor */
+ regs[0xd3] = rts8891_data_format (dev->xdpi, dev->sensor);
+
+ /* toggle front panel light to signal gray scan */
+ if (session->params.format == SANE_FRAME_GRAY)
+ {
+ *status1 = (*status1 & 0x0F) | 0x10;
+ }
+
+ return status;
+}
+
+/* set up the shadow registers for scan, depending on scan parameters */
+/* the ultimate goal is to have no direct access to registers, but to */
+/* set them through helper functions */
+/* NOTE : I couldn't manage to get scans that really uses gray settings. */
+/* The windows driver is allways scanning in color, so we do the same. */
+/* For now, the only mode that could be done would be 300 dpi gray scan, */
+/* based on the register settings of find_origin() */
+static SANE_Status
+write_scan_registers (struct Rts8891_Session *session)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte control;
+ SANE_Byte status1, status2;
+ struct Rts8891_Device *dev = session->dev;
+
+ /* setup registers for scan */
+ status=setup_scan_registers (session, &status1, &status2, dev->regs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error0, "write_scan_registers: failed to setup registers\n");
+ return status;
+ }
+
+ /* check if session is idle */
+ control = 0x00;
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if (control != 0)
+ {
+ DBG (DBG_error0, "write_scan_registers: scanner is not idle!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* effective write of register set for scan */
+ status = rts8891_write_all (dev->devnum, dev->regs, dev->reg_count);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error0, "write_scan_registers: failed to write registers\n");
+ }
+ return status;
+}
+
+/**
+ * This function parks head relying by moving head backward by a
+ * very large amount without scanning
+ */
+static SANE_Status
+park_head (struct Rts8891_Device *dev, SANE_Bool wait)
+{
+ SANE_Status status;
+ SANE_Byte reg, control;
+ /* the hammer way : set all regs */
+ SANE_Byte regs[244];
+
+ DBG (DBG_proc, "park_head: start\n");
+
+ reg = 0x8d;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ reg = 0xad;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+
+ status = sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+
+ reg = 0xff;
+ sanei_rts88xx_write_reg (dev->devnum, 0x23, &reg);
+
+ /* TODO create write_double_reg */
+ if (dev->sensor != SENSOR_TYPE_4400)
+ {
+ dev->regs[0x16] = 0x07;
+ dev->regs[0x17] = 0x00;
+ }
+ else
+ {
+ dev->regs[0x16] = 0x0f;
+ dev->regs[0x17] = 0x10;
+ }
+ sanei_rts88xx_write_regs (dev->devnum, 0x16, dev->regs + 0x16, 2);
+
+ reg = 0x8d;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+ reg = 0xad;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG, &reg);
+
+ /* 0x20 expected */
+ sanei_rts88xx_read_reg (dev->devnum, CONTROLER_REG, &reg);
+ if (reg != 0x20)
+ {
+ DBG (DBG_warn, "park_head: unexpected controler value 0x%02x\n", reg);
+ }
+
+ /* head parking */
+ status = rts8891_park (dev, regs, wait);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "park_head: failed to park head!\n");
+ }
+
+ DBG (DBG_proc, "park_head: end\n");
+ return status;
+}
+
+/* update button status
+ * button access is allowed during scan, which is usefull for 'cancel' button
+ */
+static SANE_Status
+update_button_status (struct Rts8891_Session *session)
+{
+ SANE_Int mask = 0, i;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool lock = SANE_FALSE;
+
+ /* while scanning, interface is reserved, so don't claim/release it */
+ if (session->scanning != SANE_TRUE)
+ {
+ lock = SANE_TRUE;
+
+ /* claim the interface to reserve device */
+ if (session->dev->conf.allowsharing == SANE_TRUE)
+ {
+ status = sanei_usb_claim_interface (session->dev->devnum, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn,
+ "update_button_status: cannot claim usb interface\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+ }
+
+ /* effective button reading */
+ status = rts8891_read_buttons (session->dev->devnum, &mask);
+
+ /* release interface if needed */
+ if (lock == SANE_TRUE)
+ {
+ if (session->dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (session->dev->devnum, 0);
+ }
+ }
+
+ for (i = 0; i < session->dev->model->buttons; i++)
+ {
+ if (mask & (1 << i))
+ {
+ session->val[OPT_BUTTON_1 + i].w = SANE_TRUE;
+ DBG (DBG_io2, "update_button_status: setting button %d to TRUE\n",
+ i + 1);
+ }
+ }
+ return status;
+}
+
+/* set lamp status, 0 for lamp off
+ * other values set lamp on */
+static SANE_Status
+set_lamp_state (struct Rts8891_Session *session, int on)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte reg;
+
+ /* claim the interface reserve device */
+ if (session->dev->conf.allowsharing == SANE_TRUE)
+ {
+ status = sanei_usb_claim_interface (session->dev->devnum, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_warn, "set_lamp_state: cannot claim usb interface\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ status = sanei_rts88xx_read_reg (session->dev->devnum, LAMP_REG, &reg);
+ if (on)
+ {
+ DBG (DBG_info, "set_lamp_state: lamp on\n");
+ reg = session->dev->regs[LAMP_REG] | 0x80;
+ }
+ else
+ {
+ DBG (DBG_info, "set_lamp_state: lamp off\n");
+ reg = session->dev->regs[LAMP_REG] & 0x7F;
+#ifdef HAVE_SYS_TIME_H
+ /* if lamp is switched off, warming up will be needed */
+ session->dev->last_scan.tv_sec = 0;
+#endif
+ }
+ status = sanei_rts88xx_write_reg (session->dev->devnum, LAMP_REG, &reg);
+
+ /* release interface and return status from lamp setting */
+ if (session->dev->conf.allowsharing == SANE_TRUE)
+ {
+ sanei_usb_release_interface (session->dev->devnum, 0);
+ }
+ return status;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/rts8891.conf.in b/backend/rts8891.conf.in
new file mode 100644
index 0000000..8f011ae
--- /dev/null
+++ b/backend/rts8891.conf.in
@@ -0,0 +1,9 @@
+# rts8891.conf: Configuration file for the rts8891 backend
+
+# MODEL
+# HP scanjet 4400c
+usb 0x03f0 0x0705
+# HP scanjet 4470c
+usb 0x03f0 0x0805
+# UMAX Astra 4400/4450
+usb 0x1606 0x0070
diff --git a/backend/rts8891.h b/backend/rts8891.h
new file mode 100644
index 0000000..5fbf8b5
--- /dev/null
+++ b/backend/rts8891.h
@@ -0,0 +1,155 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2012 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef RTS8891_H
+#define RTS8891_H
+
+#define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE
+#define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE
+#define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0)
+
+#define RTS8891_CONFIG_FILE "rts8891.conf"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif /* */
+
+#define COLOR_MODE SANE_VALUE_SCAN_MODE_COLOR
+#define GRAY_MODE SANE_VALUE_SCAN_MODE_GRAY
+#define LINEART_MODE SANE_VALUE_SCAN_MODE_LINEART
+
+/* preferred number of bytes to keep in buffer */
+#define PREFERED_BUFFER_SIZE 2097152 /* all scanner memory */
+
+/** List of SANE options
+ */
+enum Rts8891_Option
+{ OPT_NUM_OPTS = 0,
+ OPT_STANDARD_GROUP,
+ OPT_MODE,
+ OPT_PREVIEW,
+ OPT_RESOLUTION,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ /* advanced image enhancement options */
+ OPT_ENHANCEMENT_GROUP,
+ OPT_THRESHOLD,
+ OPT_CUSTOM_GAMMA, /* toggle to enable custom gamma tables */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ /* advanced options */
+ OPT_ADVANCED_GROUP,
+ OPT_LAMP_ON,
+ OPT_LAMP_OFF,
+
+ /* button group */
+ OPT_SENSOR_GROUP,
+ OPT_BUTTON_1,
+ OPT_BUTTON_2,
+ OPT_BUTTON_3,
+ OPT_BUTTON_4,
+ OPT_BUTTON_5,
+ OPT_BUTTON_6,
+ OPT_BUTTON_7,
+ OPT_BUTTON_8,
+ OPT_BUTTON_9,
+ OPT_BUTTON_10,
+ OPT_BUTTON_11,
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+/**
+ * enumeration of configuration options
+ */
+enum Rts8891_Configure_Option
+{
+ CFG_MODEL_NUMBER = 0, /* first option number must be zero */
+ CFG_SENSOR_NUMBER,
+ CFG_ALLOW_SHARING,
+ NUM_CFG_OPTIONS /* MUST be last */
+};
+
+/** Scanner object. This struct holds informations usefull for
+ * the functions defined in SANE's standard. Informations closer
+ * to the hardware are in the Rts8891_Device structure. There is
+ * as many session structure than frontends using the scanner.
+ */
+typedef struct Rts8891_Session
+{
+
+ /**< Next handle in linked list */
+ struct Rts8891_Session *next;
+
+ /**< Low-level device object */
+ struct Rts8891_Device *dev;
+
+ /* SANE data */
+
+ /**< We are currently scanning */
+ SANE_Bool scanning;
+ /**< Data read is in non blocking mode */
+ SANE_Bool non_blocking;
+ /**< Gray scans are emulated */
+ SANE_Bool emulated_gray;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ /**< Option descriptors */
+ Option_Value val[NUM_OPTIONS]; /**< Option values */
+ SANE_Parameters params; /**< SANE Parameters */
+
+ /**< bytes to send to frontend for the scan */
+ SANE_Int to_send;
+
+ /**< bytes currently sent to frontend during the scan */
+ SANE_Int sent;
+} Rts8891_Session;
+
+#endif /* not RTS8891_H */
diff --git a/backend/rts8891_devices.c b/backend/rts8891_devices.c
new file mode 100644
index 0000000..151b283
--- /dev/null
+++ b/backend/rts8891_devices.c
@@ -0,0 +1,248 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2012 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+
+/* here we have the various device settings...
+ */
+static Rts8891_Model hp4400c_model = {
+ "HP4400c", /* Name */
+ "Hewlett-Packard", /* Device vendor string */
+ "4400c", /* Device model name */
+ "flatbed scanner", /* Device type */
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 0}, /* possible y-resolutions */
+ 1200, /* max physical x dpi */
+ 600, /* max physical y dpi */
+ 150, /* min physical y dpi */
+
+ SANE_FIX (8.3), /* Start of scan area in mm (x) */
+ SANE_FIX (3.0), /* Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (298.1), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ 24, 12, 0, /* R, G, and B CCD Line-distance correction in lines at
+ max motor resolution */
+ /* default sensor */
+ SENSOR_TYPE_4400,
+
+ /* default gamma table */
+ {0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c,
+ 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c, 0x50, 0x52, 0x53, 0x55,
+ 0x57, 0x58, 0x5a, 0x5c, 0x5d, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x66, 0x67,
+ 0x68, 0x6a, 0x6b, 0x6c, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8d, 0x8e,
+ 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac,
+ 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb1, 0xb1, 0xb2, 0xb3, 0xb4, 0xb4,
+ 0xb5, 0xb6, 0xb6, 0xb7, 0xb8, 0xb8, 0xb9, 0xba, 0xba, 0xbb, 0xbc, 0xbc,
+ 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc3, 0xc3, 0xc4,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xcb, 0xcb,
+ 0xcc, 0xcc, 0xcd, 0xce, 0xce, 0xcf, 0xcf, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2,
+ 0xd3, 0xd3, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd9, 0xd9,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec,
+ 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2,
+ 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xff, 0xff},
+
+ /* number of buttons */
+ 5,
+
+ /* button names */
+ { "copy", "mail", "image-copy", "scan", "power"},
+
+ /* button titles */
+ { "copy", "mail", "image copy", "scan", "power"},
+
+ /* flags */
+ RTS8891_FLAG_EMULATED_GRAY_MODE
+};
+
+
+static Rts8891_Model hp4470c_model = {
+ "HP4470c", /* Name */
+ "Hewlett-Packard", /* Device vendor string */
+ "4470c", /* Device model name */
+ "flatbed scanner", /* Device type */
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 0}, /* possible y-resolutions */
+ 1200, /* max physical x dpi */
+ 600, /* max physical y dpi */
+ 150, /* min physical y dpi */
+
+ SANE_FIX (8.3), /* Start of scan area in mm (x) */
+ SANE_FIX (3.0), /* Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (298.1), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ 24, 12, 0, /* R, G, and B CCD Line-distance correction in lines at
+ max motor resolution */
+ /* default sensor */
+ SENSOR_TYPE_XPA,
+
+ /* default gamma table */
+ {0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c,
+ 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c, 0x50, 0x52, 0x53, 0x55,
+ 0x57, 0x58, 0x5a, 0x5c, 0x5d, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x66, 0x67,
+ 0x68, 0x6a, 0x6b, 0x6c, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8d, 0x8e,
+ 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac,
+ 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb1, 0xb1, 0xb2, 0xb3, 0xb4, 0xb4,
+ 0xb5, 0xb6, 0xb6, 0xb7, 0xb8, 0xb8, 0xb9, 0xba, 0xba, 0xbb, 0xbc, 0xbc,
+ 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc3, 0xc3, 0xc4,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xcb, 0xcb,
+ 0xcc, 0xcc, 0xcd, 0xce, 0xce, 0xcf, 0xcf, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2,
+ 0xd3, 0xd3, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd9, 0xd9,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec,
+ 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2,
+ 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xff, 0xff},
+
+ /* number of buttons */
+ 11,
+
+ /* button names */
+ {"plus", "minus", "copy", "mail", "image-copy", "www", "scan", "power",
+ "cancel", "options", "toggle-mode"},
+
+ /* button titles */
+ {"plus", "minus", "copy", "mail", "image copy", "www", "scan", "power",
+ "cancel", "options", "toggle color/gray mode"},
+
+ /* flags */
+ RTS8891_FLAG_EMULATED_GRAY_MODE
+};
+
+static Rts8891_Model astra4400_model = {
+ "Astra 4400", /* Name */
+ "UMAX", /* Device vendor string */
+ "Astra 4400", /* Device model name */
+ "flatbed scanner", /* Device type */
+
+ {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
+ {600, 300, 150, 0}, /* possible y-resolutions */
+ 1200, /* max physical x dpi */
+ 600, /* max physical y dpi */
+ 150, /* min physical y dpi */
+
+ SANE_FIX (8.3), /* Start of scan area in mm (x) */
+ SANE_FIX (3.0), /* Start of scan area in mm (y) */
+ SANE_FIX (215.9), /* Size of scan area in mm (x) */
+ SANE_FIX (298.1), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
+
+ 24, 12, 0, /* R, G, and B CCD Line-distance correction in lines at
+ max motor resolution */
+ /* default sensor */
+ SENSOR_TYPE_XPA,
+
+ /* default gamma table */
+ {0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c,
+ 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c, 0x50, 0x52, 0x53, 0x55,
+ 0x57, 0x58, 0x5a, 0x5c, 0x5d, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x66, 0x67,
+ 0x68, 0x6a, 0x6b, 0x6c, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8d, 0x8e,
+ 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac,
+ 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb1, 0xb1, 0xb2, 0xb3, 0xb4, 0xb4,
+ 0xb5, 0xb6, 0xb6, 0xb7, 0xb8, 0xb8, 0xb9, 0xba, 0xba, 0xbb, 0xbc, 0xbc,
+ 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc3, 0xc3, 0xc4,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xcb, 0xcb,
+ 0xcc, 0xcc, 0xcd, 0xce, 0xce, 0xcf, 0xcf, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2,
+ 0xd3, 0xd3, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd9, 0xd9,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec,
+ 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2,
+ 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xff, 0xff},
+
+ /* number of buttons */
+ 11,
+
+ /* button names */
+ {"plus", "minus", "copy", "mail", "image-copy", "www", "scan", "power",
+ "cancel", "options", "toggle-mode"},
+
+ /* button titles */
+ {"plus", "minus", "copy", "mail", "image copy", "www", "scan", "power",
+ "cancel", "options", "toggle color/gray mode"},
+
+ /* flags */
+ RTS8891_FLAG_EMULATED_GRAY_MODE
+};
+
+/* full list of supported devices: vendor, product, description structure */
+static Rts8891_USB_Device_Entry rts8891_usb_device_list[] = {
+ {0x03f0, 0x0705, &hp4400c_model},
+ {0x03f0, 0x0805, &hp4470c_model},
+ {0x1606, 0x0070, &astra4400_model},
+ {0, 0, NULL}
+};
diff --git a/backend/rts8891_low.c b/backend/rts8891_low.c
new file mode 100644
index 0000000..917d514
--- /dev/null
+++ b/backend/rts8891_low.c
@@ -0,0 +1,869 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2013 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* this file contains all the low level functions needed for the higher level
+ * functions of the sane standard. They are put there to keep files smaller
+ * and separate functions with different goals.
+ */
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+
+#include <stdio.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include "rts8891_low.h"
+
+#define RTS8891_BUILD 30
+#define RTS8891_MAX_REGISTERS 244
+
+/* init rts8891 library */
+static void
+rts8891_low_init (void)
+{
+ DBG_INIT ();
+ DBG (DBG_info, "RTS8891 low-level functions, version %d.%d-%d\n",
+ SANE_CURRENT_MAJOR, V_MINOR, RTS8891_BUILD);
+}
+
+
+/****************************************************************/
+/* ASIC specific functions */
+/****************************************************************/
+
+/* write all registers, taking care of the special 0xaa value which
+ * must be escaped with a zero
+ */
+static SANE_Status
+rts8891_write_all (SANE_Int devnum, SANE_Byte * regs, SANE_Int count)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte local_regs[RTS8891_MAX_REGISTERS];
+ size_t size = 0;
+ SANE_Byte buffer[260];
+ unsigned int i, j;
+ char message[256 * 5];
+
+ if (DBG_LEVEL > DBG_io)
+ {
+ for (i = 0; i < (unsigned int) count; i++)
+ {
+ if (i != 0xb3)
+ sprintf (message + 5 * i, "0x%02x ", regs[i]);
+ else
+ sprintf (message + 5 * i, "---- ");
+ }
+ DBG (DBG_io, "rts8891_write_all : write_all(0x00,%d)=%s\n", count,
+ message);
+ }
+
+ /* copy register set and escaping 0xaa values */
+ /* b0, b1 abd b3 values may be scribled, but that isn't important */
+ /* since they are read-only registers */
+ j = 0;
+ for (i = 0; i < 0xb3; i++)
+ {
+ local_regs[j] = regs[i];
+ if (local_regs[j] == 0xaa && i < 0xb3)
+ {
+ j++;
+ local_regs[j] = 0x00;
+ }
+ j++;
+ }
+ buffer[0] = 0x88;
+ buffer[1] = 0;
+ buffer[2] = 0x00;
+ buffer[3] = 0xb3;
+ for (i = 0; i < j; i++)
+ buffer[i + 4] = local_regs[i];
+ /* the USB block is size + 4 bytes of header long */
+ size = j + 4;
+ if (sanei_usb_write_bulk (devnum, buffer, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "rts88xx_write_all : write registers part 1 failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ size = count - 0xb4; /* we need to substract one reg since b3 won't be written */
+ buffer[0] = 0x88;
+ buffer[1] = 0xb4;
+ buffer[2] = 0x00;
+ buffer[3] = size;
+ for (i = 0; i < size; i++)
+ buffer[i + 4] = regs[0xb4 + i];
+ /* the USB block is size + 4 bytes of header long */
+ size += 4;
+ if (sanei_usb_write_bulk (devnum, buffer, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "rts88xx_write_all : write registers part 2 failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
+
+
+/* this functions "commits" pending scan command */
+static SANE_Status
+rts8891_commit (SANE_Int devnum, SANE_Byte value)
+{
+ SANE_Status status;
+ SANE_Byte reg;
+
+ reg = value;
+ sanei_rts88xx_write_reg (devnum, 0xd3, &reg);
+ sanei_rts88xx_cancel (devnum);
+ sanei_rts88xx_write_control (devnum, 0x08);
+ status = sanei_rts88xx_write_control (devnum, 0x08);
+ return status;
+}
+
+/* this functions reads button status */
+static SANE_Status
+rts8891_read_buttons (SANE_Int devnum, SANE_Int * mask)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte reg;
+
+ /* check CONTROL_REG */
+ sanei_rts88xx_read_reg (devnum, CONTROL_REG, &reg);
+
+ /* read 'base' button status */
+ sanei_rts88xx_read_reg (devnum, 0x25, &reg);
+ DBG (DBG_io, "rts8891_read_buttons: r25=0x%02x\n", reg);
+ *mask |= reg;
+
+ /* read 'extended' button status */
+ sanei_rts88xx_read_reg (devnum, 0x1a, &reg);
+ DBG (DBG_io, "rts8891_read_buttons: r1a=0x%02x\n", reg);
+ *mask |= reg << 8;
+
+ /* clear register r25 */
+ reg = 0x00;
+ sanei_rts88xx_write_reg (devnum, 0x25, &reg);
+
+ /* clear register r1a */
+ sanei_rts88xx_read_reg (devnum, 0x1a, &reg);
+ reg = 0x00;
+ status = sanei_rts88xx_write_reg (devnum, 0x1a, &reg);
+
+ DBG (DBG_info, "rts8891_read_buttons: mask=0x%04x\n", *mask);
+ return status;
+}
+
+/*
+ * Does a simple scan based on the given register set, returning data in a
+ * preallocated buffer of the claimed size.
+ * sanei_rts88xx_data_count cannot be made reliable, when the announced data
+ * amount is read, it may no be ready, leading to errors. To work around
+ * it, we read data count one more time before reading.
+ */
+static SANE_Status
+rts8891_simple_scan (SANE_Int devnum, SANE_Byte * regs, int regcount,
+ SANE_Int format, SANE_Word total, unsigned char *image)
+{
+ SANE_Word count, read, len, dummy;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte control;
+
+ rts8891_write_all (devnum, regs, regcount);
+ rts8891_commit (devnum, format);
+
+ read = 0;
+ count = 0;
+ while (count == 0)
+ {
+ status = sanei_rts88xx_data_count (devnum, &count);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "simple_scan: failed to wait for data\n");
+ return status;
+ }
+ if (count == 0)
+ {
+ status = sanei_rts88xx_read_reg (devnum, CONTROL_REG, &control);
+ if (((control & 0x08) == 0) || (status != SANE_STATUS_GOOD))
+ {
+ DBG (DBG_error, "simple_scan: failed to wait for data\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ /* data reading */
+ read = 0;
+ while ((read < total) && (count != 0 || (control & 0x08) == 0x08))
+ {
+ /* sync ? */
+ status = sanei_rts88xx_data_count (devnum, &dummy);
+
+ /* read */
+ if (count > 0)
+ {
+ len = count;
+ /* read even size unless last chunk */
+ if ((len & 1) && (read + len < total))
+ {
+ len++;
+ }
+ if (len > RTS88XX_MAX_XFER_SIZE)
+ {
+ len = RTS88XX_MAX_XFER_SIZE;
+ }
+ if (len > 0)
+ {
+ status = sanei_rts88xx_read_data (devnum, &len, image + read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "simple_scan: failed to read from scanner\n");
+ return status;
+ }
+ read += len;
+ }
+ }
+
+ /* don't try to read data count if we have enough data */
+ if (read < total)
+ {
+ status = sanei_rts88xx_data_count (devnum, &count);
+ }
+ else
+ {
+ count = 0;
+ }
+ if (count == 0)
+ {
+ sanei_rts88xx_read_reg (devnum, CONTROL_REG, &control);
+ }
+ }
+
+ /* sanity check */
+ if (read < total)
+ {
+ DBG (DBG_io2, "simple_scan: ERROR, %d bytes missing ... \n",
+ total - read);
+ }
+
+ /* wait for the motor to stop */
+ do
+ {
+ sanei_rts88xx_read_reg (devnum, CONTROL_REG, &control);
+ }
+ while ((control & 0x08) == 0x08);
+
+ return status;
+}
+
+ /**
+ * set the data format. Is part of the commit sequence. Then returned
+ * value is the value used in d3 register for a scan.
+ */
+static SANE_Int
+rts8891_data_format (SANE_Int dpi, int sensor)
+{
+ SANE_Byte reg = 0x00;
+
+ /* it seems that lower nibble is a divisor */
+ if (sensor == SENSOR_TYPE_BARE || sensor == SENSOR_TYPE_XPA)
+ {
+ switch (dpi)
+ {
+ case 75:
+ reg = 0x02;
+ break;
+ case 150:
+ if (sensor == SENSOR_TYPE_BARE)
+ reg = 0x0e;
+ else
+ reg = 0x0b;
+ break;
+ case 300:
+ reg = 0x17;
+ break;
+ case 600:
+ if (sensor == SENSOR_TYPE_BARE)
+ reg = 0x02;
+ else
+ reg = 0x0e;
+ break;
+ case 1200:
+ if (sensor == SENSOR_TYPE_BARE)
+ reg = 0x17;
+ else
+ reg = 0x05;
+ break;
+ }
+ }
+ if (sensor == SENSOR_TYPE_4400 || sensor == SENSOR_TYPE_4400_BARE)
+ {
+ switch (dpi)
+ {
+ case 75:
+ reg = 0x02;
+ break;
+ case 150:
+ if (sensor == SENSOR_TYPE_4400)
+ reg = 0x0b;
+ else
+ reg = 0x17;
+ break;
+ case 300:
+ reg = 0x17;
+ break;
+ case 600:
+ if (sensor == SENSOR_TYPE_4400)
+ reg = 0x0e;
+ else
+ reg = 0x02;
+ break;
+ case 1200:
+ if (sensor == SENSOR_TYPE_4400)
+ reg = 0x05;
+ else
+ reg = 0x17;
+ break;
+ }
+ }
+ return reg;
+}
+
+/**
+ * set up default values for a 75xdpi, 150 ydpi scan
+ */
+static void
+rts8891_set_default_regs (SANE_Byte * scanner_regs)
+{
+ SANE_Byte default_75[RTS8891_MAX_REGISTERS] =
+ { 0xe5, 0x41, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x0a, 0x0a, 0x0a, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x3f, 0xff, 0x20, 0xf8, 0x28, 0x07, 0x00,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x00, 0x3a, 0xf2, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x8c,
+ 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x14, 0x18, 0x15, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x3f, 0x80, 0x68, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc,
+ 0x27, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x0e, 0x00, 0x00, 0xf0, 0xff, 0xf5, 0xf7, 0xea, 0x0b, 0x03, 0x05, 0x86,
+ 0x1b, 0x30, 0xf6, 0xa2, 0x27, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ unsigned int i;
+ for (i = 0; i < RTS8891_MAX_REGISTERS; i++)
+ scanner_regs[i] = default_75[i];
+}
+
+static SANE_Status
+rts8891_move (struct Rts8891_Device *device, SANE_Byte * regs,
+ SANE_Int distance, SANE_Bool forward)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte regs10, regs11;
+
+ DBG (DBG_proc, "rts8891_move: start\n");
+ DBG (DBG_io, "rts8891_move: %d lines %s, sensor=%d\n", distance,
+ forward == SANE_TRUE ? "forward" : "backward", device->sensor);
+
+ /* prepare scan */
+ rts8891_set_default_regs (regs);
+ if (device->sensor != SENSOR_TYPE_4400
+ && device->sensor != SENSOR_TYPE_4400_BARE)
+ {
+ regs10 = 0x20;
+ regs11 = 0x28;
+ }
+ else
+ {
+ regs10 = 0x10;
+ regs11 = 0x2a;
+ }
+
+ regs[0x32] = 0x80;
+ regs[0x33] = 0x81;
+ regs[0x35] = 0x10;
+ regs[0x36] = 0x24;
+ regs[0x39] = 0x02;
+ regs[0x3a] = 0x0e;
+
+ regs[0x64] = 0x01;
+ regs[0x65] = 0x20;
+
+ regs[0x79] = 0x20;
+ regs[0x7a] = 0x01;
+
+ regs[0x80] = 0x32;
+ regs[0x82] = 0x33;
+ regs[0x85] = 0x46;
+ regs[0x86] = 0x0b;
+ regs[0x87] = 0x8c;
+ regs[0x88] = 0x10;
+ regs[0x89] = 0xb2;
+ regs[0x8d] = 0x3b;
+ regs[0x8e] = 0x60;
+
+ regs[0x90] = 0x1c;
+ regs[0xb2] = 0x16; /* 0x10 : stop when at home, 0x04: no data */
+ regs[0xc0] = 0x00;
+ regs[0xc1] = 0x00;
+ regs[0xc3] = 0x00;
+ regs[0xc4] = 0x00;
+ regs[0xc5] = 0x00;
+ regs[0xc6] = 0x00;
+ regs[0xc7] = 0x00;
+ regs[0xc8] = 0x00;
+ regs[0xca] = 0x00;
+ regs[0xcd] = 0x00;
+ regs[0xce] = 0x00;
+ regs[0xcf] = 0x00;
+ regs[0xd0] = 0x00;
+ regs[0xd1] = 0x00;
+ regs[0xd2] = 0x00;
+ regs[0xd3] = 0x00;
+ regs[0xd4] = 0x00;
+ regs[0xd6] = 0x6b;
+ regs[0xd7] = 0x00;
+ regs[0xd8] = 0x00;
+ regs[0xd9] = 0xad;
+ regs[0xda] = 0xa7;
+ regs[0xe2] = 0x17;
+ regs[0xe3] = 0x0d;
+ regs[0xe4] = 0x06;
+ regs[0xe5] = 0xf9;
+ regs[0xe7] = 0x53;
+ regs[0xe8] = 0x02;
+ regs[0xe9] = 0x02;
+
+ /* hp4400 sensors */
+ if (device->sensor == SENSOR_TYPE_4400
+ || device->sensor == SENSOR_TYPE_4400_BARE)
+ {
+ regs[0x13] = 0x39; /* 0x20 */
+ regs[0x14] = 0xf0; /* 0xf8 */
+ regs[0x15] = 0x29; /* 0x28 */
+ regs[0x16] = 0x0f; /* 0x07 */
+ regs[0x17] = 0x10; /* 0x00 */
+ regs[0x23] = 0x00; /* 0xff */
+ regs[0x35] = 0x29; /* 0x10 */
+ regs[0x36] = 0x21; /* 0x24 */
+ regs[0x39] = 0x00; /* 0x02 */
+ regs[0x80] = 0xb0; /* 0x32 */
+ regs[0x82] = 0xb1; /* 0x33 */
+ regs[0xe2] = 0x0b; /* 0x17 */
+ regs[0xe5] = 0xf3; /* 0xf9 */
+ regs[0xe6] = 0x01; /* 0x00 */
+ }
+
+ /* disable CCD */
+ regs[0] = 0xf5;
+
+ sanei_rts88xx_set_status (device->devnum, regs, regs10, regs11);
+ sanei_rts88xx_set_scan_area (regs, distance, distance + 1, 100, 200);
+ sanei_rts88xx_set_gain (regs, 16, 16, 16);
+ sanei_rts88xx_set_offset (regs, 127, 127, 127);
+
+ /* forward/backward */
+ /* 0x2c is forward, 0x24 backward */
+ if (forward == SANE_TRUE)
+ { /* forward */
+ regs[0x36] = regs[0x36] | 0x08;
+ }
+ else
+ { /* backward */
+ regs[0x36] = regs[0x36] & 0xf7;
+ }
+
+ /* write regiters values */
+ status = rts8891_write_all (device->devnum, regs, RTS8891_MAX_REGISTERS);
+
+ /* commit it */
+ rts8891_commit (device->devnum, 0x00);
+
+ return status;
+}
+
+ /**
+ * wait for the scanning head to reach home position
+ */
+static SANE_Status
+rts8891_wait_for_home (struct Rts8891_Device *device, SANE_Byte * regs)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte motor, sensor, reg;
+
+ DBG (DBG_proc, "rts8891_wait_for_home: start\n");
+
+ /* wait for controller home bit to raise, no timeout */
+ /* at each loop we check that motor is on, then that the sensor bit it cleared */
+ do
+ {
+ sanei_rts88xx_read_reg (device->devnum, CONTROL_REG, &motor);
+ sanei_rts88xx_read_reg (device->devnum, CONTROLER_REG, &sensor);
+ }
+ while ((motor & 0x08) && ((sensor & 0x02) == 0));
+
+ /* flag that device has finished parking */
+ device->parking=SANE_FALSE;
+
+ /* check for error */
+ if (((motor & 0x08) == 0x00) && ((sensor & 0x02) == 0))
+ {
+ DBG (DBG_error,
+ "rts8891_wait_for_home: error, motor stopped before head parked\n");
+ status = SANE_STATUS_INVAL;
+ }
+
+ /* re-enable CCD */
+ regs[0] = regs[0] & 0xef;
+
+ sanei_rts88xx_cancel (device->devnum);
+
+ /* reset ? so we don't need to read data */
+ reg = 0;
+ /* b7: movement on/off, b3-b0 : movement divisor */
+ sanei_rts88xx_write_reg (device->devnum, 0x33, &reg);
+ sanei_rts88xx_write_reg (device->devnum, 0x33, &reg);
+ /* movement direction */
+ sanei_rts88xx_write_reg (device->devnum, 0x36, &reg);
+ sanei_rts88xx_cancel (device->devnum);
+
+ DBG (DBG_proc, "rts8891_wait_for_home: end\n");
+ return status;
+}
+
+ /**
+ * move the head backward by a huge line number then poll home sensor until
+ * head has get back home. We have our own copy of the registers to avoid
+ * messing scanner status
+ */
+static SANE_Status
+rts8891_park (struct Rts8891_Device *device, SANE_Byte *regs, SANE_Bool wait)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DBG_proc, "rts8891_park: start\n");
+
+ device->parking=SANE_TRUE;
+ rts8891_move (device, regs, 8000, SANE_FALSE);
+
+ if(wait==SANE_TRUE)
+ {
+ status = rts8891_wait_for_home (device,regs);
+ }
+
+ DBG (DBG_proc, "rts8891_park: end\n");
+ return status;
+}
+
+/* reads data from scanner.
+ * First we wait for some data to be available and then loop reading
+ * from scanner until the required amount is reached.
+ * We handle non blocking I/O by returning immediatly (with SANE_STATUS_BUSY)
+ * if there is no data available from scanner. But once read is started,
+ * all the required amount is read. Once wait for data succeeded, we still poll
+ * for data in order no to read it too fast, but we don' take care of non blocking
+ * mode since we cope with it on first data wait.
+ */
+static SANE_Status
+read_data (struct Rts8891_Session *session, SANE_Byte * dest, SANE_Int length)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int count, read, len, dummy;
+ struct Rts8891_Device *dev = session->dev;
+ static FILE *raw = NULL; /* for debugging purpose we need it static */
+ SANE_Byte control = 0x08;
+ unsigned char buffer[RTS88XX_MAX_XFER_SIZE];
+
+ DBG (DBG_proc, "read_data: start\n");
+ DBG (DBG_proc, "read_data: requiring %d bytes\n", length);
+
+ /* wait for data being available and handle non blocking mode */
+ /* only when data reading hasn't produce any data yet */
+ if (dev->read == 0)
+ {
+ do
+ {
+ status = sanei_rts88xx_data_count (dev->devnum, &count);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_data: failed to wait for data\n");
+ return status;
+ }
+ if (count == 0)
+ {
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if ((control & 0x08) == 0 && (count == 0))
+ {
+ DBG (DBG_error,
+ "read_data: scanner stopped being busy before data are available\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* in case there is no data, we return BUSY since this mean */
+ /* that scanning head hasn't reach is position and data hasn't */
+ /* come yet */
+ if (session->non_blocking && count == 0)
+ {
+
+ dev->regs[LAMP_REG] = 0x8d;
+ sanei_rts88xx_write_reg (dev->devnum, LAMP_REG,
+ &(dev->regs[LAMP_REG]));
+ DBG (DBG_io, "read_data: no data vailable\n");
+ DBG (DBG_proc, "read_data: end\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+ while (count == 0);
+ }
+ else
+ { /* start of read for a new block */
+ status = sanei_rts88xx_data_count (dev->devnum, &count);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_data: failed to wait for data\n");
+ return status;
+ }
+ if (count == 0)
+ {
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if ((control & 0x08) == 0 && (count == 0))
+ {
+ DBG (DBG_error,
+ "read_data: scanner stopped being busy before data are available\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ /* fill scanned data buffer */
+ read = 0;
+
+ /* now loop reading data until we have the amount requested */
+ /* we also take care of not reading too much data */
+ while (read < length && dev->read < dev->to_read
+ && ((control & 0x08) == 0x08))
+ {
+ /* used to sync */
+ if (dev->read == 0)
+ {
+ status = sanei_rts88xx_data_count (dev->devnum, &dummy);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_data: failed to read data count\n");
+ return status;
+ }
+ }
+
+ /* if there is data to read, read it */
+ if (count > 0)
+ {
+ len = count;
+
+ if (len > RTS88XX_MAX_XFER_SIZE)
+ {
+ len = RTS88XX_MAX_XFER_SIZE;
+ }
+
+ /* we only read even size blocks of data */
+ if (len & 1)
+ {
+ DBG (DBG_io, "read_data: round to next even number\n");
+ len++;
+ }
+
+ if (len > length - read)
+ {
+ len = length - read;
+ }
+
+ status = sanei_rts88xx_read_data (dev->devnum, &len, dest + read);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_data: failed to read from scanner\n");
+ return status;
+ }
+
+ /* raw data tracing */
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ /* open a new file only when no data scanned */
+ if (dev->read == 0)
+ {
+ raw = fopen ("raw_data.pnm", "wb");
+ if (raw != NULL)
+ {
+ /* PNM header */
+ fprintf (raw, "P%c\n%d %d\n255\n",
+ session->params.format ==
+ SANE_FRAME_RGB
+ || session->emulated_gray ==
+ SANE_TRUE ? '6' : '5', dev->pixels,
+ dev->lines);
+ }
+ }
+ if (raw != NULL)
+ {
+ fwrite (dest + read, 1, len, raw);
+ }
+ }
+
+ /* move pointer and counter */
+ read += len;
+ dev->read += len;
+ DBG (DBG_io2, "read_data: %d/%d\n", dev->read, dev->to_read);
+ }
+
+ /* in fast scan mode, read data count
+ * in slow scan, head moves by the amount of data read */
+ status = sanei_rts88xx_data_count (dev->devnum, &count);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_data: failed to read data count\n");
+ return status;
+ }
+
+ /* if no data, check if still scanning */
+ if (count == 0 && dev->read < dev->to_read)
+ {
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ if ((control & 0x08) == 0x00)
+ {
+ DBG (DBG_error,
+ "read_data: scanner stopped being busy before data are available\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ /* end of physical reads */
+ if (dev->read >= dev->to_read)
+ {
+ /* check there is no more data in case of a bug */
+ sanei_rts88xx_data_count (dev->devnum, &count);
+ if (count > 0)
+ {
+ DBG (DBG_warn,
+ "read_data: %d bytes are still available from scanner\n",
+ count);
+
+ /* flush left-over data */
+ while (count > 0)
+ {
+ len = count;
+ if (len > RTS88XX_MAX_XFER_SIZE)
+ {
+ len = RTS88XX_MAX_XFER_SIZE;
+ }
+
+ /* we only read even size blocks of data */
+ if (len & 1)
+ {
+ len++;
+ }
+ sanei_rts88xx_read_data (dev->devnum, &len, buffer);
+ sanei_rts88xx_data_count (dev->devnum, &count);
+ }
+ }
+
+ /* wait for motor to stop at the end of the scan */
+ do
+ {
+ sanei_rts88xx_read_reg (dev->devnum, CONTROL_REG, &control);
+ }
+ while ((control & 0x08) != 0);
+
+ /* close log file if needed */
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ if (raw != NULL)
+ {
+ fclose (raw);
+ raw = NULL;
+ }
+ }
+ }
+
+ DBG (DBG_io, "read_data: read %d bytes from scanner\n", length);
+ DBG (DBG_proc, "read_data: end\n");
+ return status;
+}
+
+ /**
+ * set scanner idle before leaving xxx_quiet()
+write_reg(0x33,1)=0x00
+write_reg(0x33,1)=0x00
+write_reg(0x36,1)=0x00
+prepare();
+------
+write_reg(LAMP_REG,1)=0x80
+write_reg(LAMP_REG,1)=0xad
+read_reg(0x14,2)=0xf8 0x28
+write_reg(0x14,2)=0x78 0x28
+get_status()=0x20 0x3f
+read_reg(0xb1,1)=0x00
+read_control()=0x00
+reset_lamp()=(0x20,0x3f)
+write_reg(LAMP_REG,1)=0x8d
+write_reg(LAMP_REG,1)=0xad
+ */
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
diff --git a/backend/rts8891_low.h b/backend/rts8891_low.h
new file mode 100644
index 0000000..a6df802
--- /dev/null
+++ b/backend/rts8891_low.h
@@ -0,0 +1,301 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2012 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef RTS8891_LOW_H
+#define RTS8891_LOW_H
+
+#include <stddef.h>
+#include "../include/sane/sane.h"
+
+#define DBG_error0 0 /* errors/warnings printed even with devuglevel 0 */
+#define DBG_error 1 /* fatal errors */
+#define DBG_init 2 /* initialization and scanning time messages */
+#define DBG_warn 3 /* warnings and non-fatal errors */
+#define DBG_info 4 /* informational messages */
+#define DBG_proc 5 /* starting/finishing functions */
+#define DBG_io 6 /* io functions */
+#define DBG_io2 7 /* io functions that are called very often */
+#define DBG_data 8 /* log image data */
+
+
+/* Flags */
+#define RTS8891_FLAG_UNTESTED (1 << 0) /* Print a warning for these scanners */
+#define RTS8891_FLAG_EMULATED_GRAY_MODE (2 << 0) /* gray scans are emulated using comor modes */
+
+#define LOWORD(x) ((uint16_t)(x & 0xffff))
+#define HIWORD(x) ((uint16_t)(x >> 16))
+#define LOBYTE(x) ((uint8_t)((x) & 0xFF))
+#define HIBYTE(x) ((uint8_t)((x) >> 8))
+
+#define MAX_SCANNERS 32
+#define MAX_RESOLUTIONS 16
+
+#define SENSOR_TYPE_BARE 0 /* sensor for hp4470 sold bare */
+#define SENSOR_TYPE_XPA 1 /* sensor for hp4470 sold with XPA */
+#define SENSOR_TYPE_4400 2 /* sensor for hp4400 */
+#define SENSOR_TYPE_4400_BARE 3 /* sensor for hp4400 */
+#define SENSOR_TYPE_MAX 3 /* maximum sensor number value */
+
+/* Forward typedefs */
+typedef struct Rts8891_Device Rts8891_Device;
+
+#define SET_DOUBLE(regs,idx,value) regs[idx]=(SANE_Byte)((value)>>8); regs[idx-1]=(SANE_Byte)((value) & 0xff);
+/*
+ * defines for RTS8891 registers name
+ */
+#define BUTTONS_REG2 0x1a
+#define LINK_REG 0xb1
+#define LAMP_REG 0xd9
+#define LAMP_BRIGHT_REG 0xda
+
+/* double reg (E6,E5) -> timing doubles when y resolution doubles
+ * E6 is high byte, possibly exposure */
+#define EXPOSURE_REG 0xe6
+
+
+#define TIMING_REG 0x81
+#define TIMING1_REG 0x83 /* holds REG8180+1 */
+#define TIMING2_REG 0x8a /* holds REG8180+2 */
+
+
+/* this struc describes a particular model which is handled by the backend */
+/* available resolutions, physical goemetry, scanning area, ... */
+typedef struct Rts8891_Model
+{
+ SANE_String_Const name;
+ SANE_String_Const vendor;
+ SANE_String_Const product;
+ SANE_String_Const type;
+
+ SANE_Int xdpi_values[MAX_RESOLUTIONS]; /* possible x resolutions */
+ SANE_Int ydpi_values[MAX_RESOLUTIONS]; /* possible y resolutions */
+
+ SANE_Int max_xdpi; /* physical maximum x dpi */
+ SANE_Int max_ydpi; /* physical maximum y dpi */
+ SANE_Int min_ydpi; /* physical minimum y dpi */
+
+ SANE_Fixed x_offset; /* Start of scan area in mm */
+ SANE_Fixed y_offset; /* Start of scan area in mm */
+ SANE_Fixed x_size; /* Size of scan area in mm */
+ SANE_Fixed y_size; /* Size of scan area in mm */
+
+ SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */
+ SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */
+ SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */
+
+ /* Line-distance correction (in pixel at max optical dpi) for CCD scanners */
+ SANE_Int ld_shift_r; /* red */
+ SANE_Int ld_shift_g; /* green */
+ SANE_Int ld_shift_b; /* blue */
+
+ /* default sensor type */
+ SANE_Int sensor;
+
+ /* default gamma table */
+ SANE_Word gamma[256];
+ SANE_Int buttons; /* number of buttons for the scanner */
+ char *button_name[11]; /* option names for buttons */
+ char *button_title[11]; /* option titles for buttons */
+ SANE_Word flags; /* allow per model behaviour control */
+} Rts8891_Model;
+
+
+/**
+ * device specific configuration structure to hold option values */
+typedef struct Rts8891_Config
+{
+ /**< index number in device table to override detection */
+ SANE_Word modelnumber;
+
+ /**< id of the snedor type, must match SENSOR_TYPE_* defines */
+ SANE_Word sensornumber;
+
+ /**< if true, use release/acquire to allow the same device
+ * to be used by several frontends */
+ SANE_Bool allowsharing;
+} Rts8891_Config;
+
+
+/**
+ * device descriptor
+ */
+struct Rts8891_Device
+{
+ /**< Next device in linked list */
+ struct Rts8891_Device *next;
+
+ /**< USB device number for libusb */
+ SANE_Int devnum;
+ SANE_String file_name;
+ Rts8891_Model *model; /* points to a structure that decribes model specifics */
+
+ SANE_Int sensor; /* sensor id */
+
+ SANE_Bool initialized; /* true if device has been intialized */
+ SANE_Bool needs_warming; /* true if device needs warming up */
+ SANE_Bool parking; /* true if device is parking head */
+
+ /* values detected during find origin */
+ /* TODO these are currently unused after detection */
+ SANE_Int left_offset; /* pixels to skip to be on left start of the scanning area */
+ SANE_Int top_offset; /* lines to skip to be at top of the scanning area */
+
+ /* gains from calibration */
+ SANE_Int red_gain;
+ SANE_Int green_gain;
+ SANE_Int blue_gain;
+
+ /* offsets from calibration */
+ SANE_Int red_offset;
+ SANE_Int green_offset;
+ SANE_Int blue_offset;
+
+ /* actual dpi used at hardware level may differ from the one
+ * at SANE level */
+ SANE_Int xdpi;
+ SANE_Int ydpi;
+
+ /* the effective scan area at hardware level may be different from
+ * the one at the SANE level*/
+ SANE_Int lines; /* lines to scan */
+ SANE_Int pixels; /* width of scan area */
+ SANE_Int bytes_per_line; /* number of bytes per line */
+ SANE_Int xstart; /* x start coordinate */
+ SANE_Int ystart; /* y start coordinate */
+
+ /* line distance shift for the active scan */
+ SANE_Int lds_r;
+ SANE_Int lds_g;
+ SANE_Int lds_b;
+
+ /* threshold to give 0/1 nit in lineart */
+ SANE_Int threshold;
+
+ /* max value from lds_r, lds_g and lds_b */
+ SANE_Int lds_max;
+
+ /* amount of data needed to correct ripple effect at highest dpi */
+ SANE_Int ripple;
+
+ /* register set of the scanner */
+ SANE_Int reg_count;
+ SANE_Byte regs[255];
+
+ /* shading calibration data */
+ SANE_Byte *shading_data;
+
+ /* data buffer read from scanner */
+ SANE_Byte *scanned_data;
+
+ /* size of the buffer */
+ SANE_Int data_size;
+
+ /* start of the data within scanned data */
+ SANE_Byte *start;
+
+ /* current pointer within scanned data */
+ SANE_Byte *current;
+
+ /* end of the data buffer */
+ SANE_Byte *end;
+
+ /**
+ * amount of bytes read from scanner
+ */
+ SANE_Int read;
+
+ /**
+ * total amount of bytes to read for the scan
+ */
+ SANE_Int to_read;
+
+#ifdef HAVE_SYS_TIME_H
+ /**
+ * last scan time, used to detect if warming-up is needed
+ */
+ struct timeval last_scan;
+
+ /**
+ * warming-up start time
+ */
+ struct timeval start_time;
+#endif
+
+ /**
+ * device configuration options
+ */
+ Rts8891_Config conf;
+};
+
+/*
+ * This struct is used to build a static list of USB IDs and link them
+ * to a struct that describes the corresponding model.
+ */
+typedef struct Rts8891_USB_Device_Entry
+{
+ SANE_Word vendor_id; /**< USB vendor identifier */
+ SANE_Word product_id; /**< USB product identifier */
+ Rts8891_Model *model; /**< Scanner model information */
+} Rts8891_USB_Device_Entry;
+
+/* this function init the rts8891 library */
+void rts8891_lib_init (void);
+
+ /***********************************/
+ /* RTS8891 ASIC specific functions */
+ /***********************************/
+
+ /* this functions commits pending scan command */
+static SANE_Status rts8891_commit (SANE_Int devnum, SANE_Byte value);
+
+/* wait for head to park to home position */
+static SANE_Status rts8891_wait_for_home (struct Rts8891_Device *device, SANE_Byte * regs);
+
+ /**
+ * move the head backward by a huge line number then poll home sensor until
+ * head has get back home
+ */
+static SANE_Status rts8891_park (struct Rts8891_Device *device, SANE_Byte * regs, SANE_Bool wait);
+
+#endif /* not RTS8891_LOW_H */
diff --git a/backend/rts88xx_lib.c b/backend/rts88xx_lib.c
new file mode 100644
index 0000000..bbbc83a
--- /dev/null
+++ b/backend/rts88xx_lib.c
@@ -0,0 +1,870 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2012 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* this file contains functions common to rts88xx ASICs */
+
+#undef BACKEND_NAME
+#define BACKEND_NAME rts88xx_lib
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "rts88xx_lib.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "../include/_stdint.h"
+
+#define RTS88XX_LIB_BUILD 30
+
+/* init rts88xx library */
+void
+sanei_rts88xx_lib_init (void)
+{
+ DBG_INIT ();
+ DBG (DBG_info, "RTS88XX library, version %d.%d-%d\n", SANE_CURRENT_MAJOR, V_MINOR,
+ RTS88XX_LIB_BUILD);
+}
+
+/*
+ * registers helpers to avoid direct access
+ */
+SANE_Bool
+sanei_rts88xx_is_color (SANE_Byte * regs)
+{
+ if ((regs[0x2f] & 0x11) == 0x11)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+void
+sanei_rts88xx_set_gray_scan (SANE_Byte * regs)
+{
+ regs[0x2f] = (regs[0x2f] & 0x0f) | 0x20;
+}
+
+void
+sanei_rts88xx_set_color_scan (SANE_Byte * regs)
+{
+ regs[0x2f] = (regs[0x2f] & 0x0f) | 0x10;
+}
+
+void
+sanei_rts88xx_set_offset (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
+ SANE_Byte blue)
+{
+ /* offset for odd pixels */
+ regs[0x02] = red;
+ regs[0x03] = green;
+ regs[0x04] = blue;
+
+ /* offset for even pixels */
+ regs[0x05] = red;
+ regs[0x06] = green;
+ regs[0x07] = blue;
+}
+
+void
+sanei_rts88xx_set_gain (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
+ SANE_Byte blue)
+{
+ regs[0x08] = red;
+ regs[0x09] = green;
+ regs[0x0a] = blue;
+}
+
+void
+sanei_rts88xx_set_scan_frequency (SANE_Byte * regs, int frequency)
+{
+ regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f);
+}
+
+/*
+ * read one register at given index
+ */
+SANE_Status
+sanei_rts88xx_read_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char cmd[] = { 0x80, 0x00, 0x00, 0x01 };
+ size_t size;
+
+ cmd[1] = index;
+
+ size = 4;
+ status = sanei_usb_write_bulk (devnum, cmd, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_reg: bulk write failed\n");
+ return status;
+ }
+ size = 1;
+ status = sanei_usb_read_bulk (devnum, reg, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_reg: bulk read failed\n");
+ return status;
+ }
+ DBG (DBG_io2, "sanei_rts88xx_read_reg: reg[0x%02x]=0x%02x\n", index, *reg);
+ return status;
+}
+
+/*
+ * write one register at given index
+ */
+SANE_Status
+sanei_rts88xx_write_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char cmd[] = { 0x88, 0x00, 0x00, 0x01, 0xff };
+ size_t size;
+
+ cmd[1] = index;
+ cmd[4] = *reg;
+
+ size = 5;
+ status = sanei_usb_write_bulk (devnum, cmd, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_write_reg: bulk write failed\n");
+ return status;
+ }
+ DBG (DBG_io2, "sanei_rts88xx_write_reg: reg[0x%02x]=0x%02x\n", index, *reg);
+ return status;
+}
+
+/*
+ * write length consecutive registers, starting at index
+ * register 0xb3 is never wrote in bulk register write, so we split
+ * write if it belongs to the register set sent
+ */
+SANE_Status
+sanei_rts88xx_write_regs (SANE_Int devnum, SANE_Int start,
+ SANE_Byte * source, SANE_Int length)
+{
+ size_t size = 0;
+ size_t i;
+ SANE_Byte buffer[260];
+ char message[256 * 5];
+
+ if (DBG_LEVEL > DBG_io)
+ {
+ for (i = 0; i < (size_t) length; i++)
+ {
+ sprintf (message + 5 * i, "0x%02x ", source[i]);
+ }
+ DBG (DBG_io, "sanei_rts88xx_write_regs : write_regs(0x%02x,%d)=%s\n",
+ start, length, message);
+ }
+
+ /* when writing several registers at a time, we avoid writing the 0xb3 register
+ * which is used to control the status of the scanner */
+ if ((start + length > 0xb3) && (length > 1))
+ {
+ size = 0xb3 - start;
+ buffer[0] = 0x88;
+ buffer[1] = start;
+ buffer[2] = 0x00;
+ buffer[3] = size;
+ for (i = 0; i < size; i++)
+ buffer[i + 4] = source[i];
+ /* the USB block is size + 4 bytes of header long */
+ size += 4;
+ if (sanei_usb_write_bulk (devnum, buffer, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_write_regs : write registers part 1 failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* skip 0xb3 register */
+ size -= 3;
+ start = 0xb4;
+ source = source + size;
+ }
+ size = length - size;
+ buffer[0] = 0x88;
+ buffer[1] = start;
+ buffer[2] = 0x00;
+ buffer[3] = size;
+ for (i = 0; i < size; i++)
+ buffer[i + 4] = source[i];
+ /* the USB block is size + 4 bytes of header long */
+ size += 4;
+ if (sanei_usb_write_bulk (devnum, buffer, &size) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_write_regs : write registers part 2 failed ...\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/* read several registers starting at the given index */
+SANE_Status
+sanei_rts88xx_read_regs (SANE_Int devnum, SANE_Int start,
+ SANE_Byte * dest, SANE_Int length)
+{
+ SANE_Status status;
+ static SANE_Byte command_block[] = { 0x80, 0, 0x00, 0xFF };
+ size_t size, i;
+ char message[256 * 5];
+
+ if (start + length > 255)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_read_regs: start and length must be within [0..255]\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* write header */
+ size = 4;
+ command_block[1] = start;
+ command_block[3] = length;
+ status = sanei_usb_write_bulk (devnum, command_block, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_regs: failed to write header\n");
+ return status;
+ }
+
+ /* read data */
+ size = length;
+ status = sanei_usb_read_bulk (devnum, dest, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_regs: failed to read data\n");
+ return status;
+ }
+ if (size != (size_t) length)
+ {
+ DBG (DBG_warn, "sanei_rts88xx_read_regs: read got only %lu bytes\n",
+ (u_long) size);
+ }
+ if (DBG_LEVEL >= DBG_io)
+ {
+ for (i = 0; i < size; i++)
+ sprintf (message + 5 * i, "0x%02x ", dest[i]);
+ DBG (DBG_io, "sanei_rts88xx_read_regs: read_regs(0x%02x,%d)=%s\n",
+ start, length, message);
+ }
+ return status;
+}
+
+/*
+ * get status by reading registers 0x10 and 0x11
+ */
+SANE_Status
+sanei_rts88xx_get_status (SANE_Int devnum, SANE_Byte * regs)
+{
+ SANE_Status status;
+ status = sanei_rts88xx_read_regs (devnum, 0x10, regs + 0x10, 2);
+ DBG (DBG_io, "sanei_rts88xx_get_status: get_status()=0x%02x 0x%02x\n",
+ regs[0x10], regs[0x11]);
+ return status;
+}
+
+/*
+ * set status by writing registers 0x10 and 0x11
+ */
+SANE_Status
+sanei_rts88xx_set_status (SANE_Int devnum, SANE_Byte * regs,
+ SANE_Byte reg10, SANE_Byte reg11)
+{
+ SANE_Status status;
+
+ regs[0x10] = reg10;
+ regs[0x11] = reg11;
+ status = sanei_rts88xx_write_regs (devnum, 0x10, regs + 0x10, 2);
+ DBG (DBG_io, "sanei_rts88xx_set_status: 0x%02x 0x%02x\n", regs[0x10],
+ regs[0x11]);
+ return status;
+}
+
+/*
+ * get lamp status by reading registers 0x84 to 0x8f, only 0x8F is currently usefull
+ * 0x84 and following could "on" timers
+ */
+SANE_Status
+sanei_rts88xx_get_lamp_status (SANE_Int devnum, SANE_Byte * regs)
+{
+ SANE_Status status;
+ status = sanei_rts88xx_read_regs (devnum, 0x84, regs + 0x84, 11);
+ return status;
+}
+
+/* resets lamp */
+SANE_Status
+sanei_rts88xx_reset_lamp (SANE_Int devnum, SANE_Byte * regs)
+{
+ SANE_Status status;
+ SANE_Byte reg;
+
+ /* read the 0xda register, then clear lower nibble and write it back */
+ status = sanei_rts88xx_read_reg (devnum, 0xda, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_reset_lamp: failed to read 0xda register\n");
+ return status;
+ }
+ reg = 0xa0;
+ status = sanei_rts88xx_write_reg (devnum, 0xda, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_reset_lamp: failed to write 0xda register\n");
+ return status;
+ }
+
+ /* on cleared, get status */
+ status = sanei_rts88xx_get_status (devnum, regs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_reset_lamp: failed to get status\n");
+ return status;
+ }
+ DBG (DBG_io, "sanei_rts88xx_reset_lamp: status=0x%02x 0x%02x\n", regs[0x10],
+ regs[0x11]);
+
+ /* set low nibble to 7 and write it */
+ reg = reg | 0x07;
+ status = sanei_rts88xx_write_reg (devnum, 0xda, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_reset_lamp: failed to write 0xda register\n");
+ return status;
+ }
+ status = sanei_rts88xx_read_reg (devnum, 0xda, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_reset_lamp: failed to read 0xda register\n");
+ return status;
+ }
+ if (reg != 0xa7)
+ {
+ DBG (DBG_warn,
+ "sanei_rts88xx_reset_lamp: expected reg[0xda]=0xa7, got 0x%02x\n",
+ reg);
+ }
+
+ /* store read value in shadow register */
+ regs[0xda] = reg;
+
+ return status;
+}
+
+/*
+ * get lcd status by reading registers 0x20, 0x21 and 0x22
+ */
+SANE_Status
+sanei_rts88xx_get_lcd (SANE_Int devnum, SANE_Byte * regs)
+{
+ SANE_Status status;
+ status = sanei_rts88xx_read_regs (devnum, 0x20, regs + 0x20, 3);
+ DBG (DBG_io, "sanei_rts88xx_get_lcd: 0x%02x 0x%02x 0x%02x\n", regs[0x20],
+ regs[0x21], regs[0x22]);
+ return status;
+}
+
+/*
+ * write to special control register CONTROL_REG=0xb3
+ */
+SANE_Status
+sanei_rts88xx_write_control (SANE_Int devnum, SANE_Byte value)
+{
+ SANE_Status status;
+ status = sanei_rts88xx_write_reg (devnum, CONTROL_REG, &value);
+ return status;
+}
+
+/*
+ * send the cancel control sequence
+ */
+SANE_Status
+sanei_rts88xx_cancel (SANE_Int devnum)
+{
+ SANE_Status status;
+
+ status = sanei_rts88xx_write_control (devnum, 0x02);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ status = sanei_rts88xx_write_control (devnum, 0x02);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ status = sanei_rts88xx_write_control (devnum, 0x00);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ status = sanei_rts88xx_write_control (devnum, 0x00);
+ return status;
+}
+
+/*
+ * write the given number of bytes pointed by value into memory
+ * length is payload length
+ * extra is number of bytes to add to the usb write length
+ */
+SANE_Status
+sanei_rts88xx_write_mem (SANE_Int devnum, SANE_Int length, SANE_Int extra,
+ SANE_Byte * value)
+{
+ SANE_Status status;
+ SANE_Byte *buffer;
+ size_t i, size;
+ char message[(0xFFC0 + 10) * 3] = "";
+
+ buffer = (SANE_Byte *) malloc (length + 10);
+ if (buffer == NULL)
+ return SANE_STATUS_NO_MEM;
+ memset (buffer, 0, length + 10);
+
+ buffer[0] = 0x89;
+ buffer[1] = 0x00;
+ buffer[2] = HIBYTE (length);
+ buffer[3] = LOBYTE (length);
+ for (i = 0; i < (size_t) length; i++)
+ {
+ buffer[i + 4] = value[i];
+
+ if (DBG_LEVEL > DBG_io2)
+ {
+ sprintf (message + 3 * i, "%02x ", buffer[i + 4]);
+ }
+ }
+ DBG (DBG_io, "sanei_rts88xx_write_mem: %02x %02x %02x %02x -> %s\n",
+ buffer[0], buffer[1], buffer[2], buffer[3], message);
+
+ size = length + 4 + extra;
+ status = sanei_usb_write_bulk (devnum, buffer, &size);
+ free (buffer);
+ if ((status == SANE_STATUS_GOOD) && (size != (size_t) length + 4 + extra))
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_write_mem: only wrote %lu bytes out of %d\n",
+ (u_long) size, length + 4);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
+
+/*
+ * set memory with the given data
+ */
+SANE_Status
+sanei_rts88xx_set_mem (SANE_Int devnum, SANE_Byte ctrl1,
+ SANE_Byte ctrl2, SANE_Int length, SANE_Byte * value)
+{
+ SANE_Status status;
+ SANE_Byte regs[2];
+ regs[0] = ctrl1;
+ regs[1] = ctrl2;
+
+ status = sanei_rts88xx_write_regs (devnum, 0x91, regs, 2);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_set_mem: failed to write 0x91/0x92 registers\n");
+ return status;
+ }
+ status = sanei_rts88xx_write_mem (devnum, length, 0, value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_set_mem: failed to write memory\n");
+ }
+ return status;
+}
+
+/*
+ * read length bytes of memory into area pointed by value
+ */
+SANE_Status
+sanei_rts88xx_read_mem (SANE_Int devnum, SANE_Int length, SANE_Byte * value)
+{
+ SANE_Status status;
+ size_t size, read, want;
+ SANE_Byte header[4];
+
+ /* build and write length header */
+ header[0] = 0x81;
+ header[1] = 0x00;
+ header[2] = HIBYTE (length);
+ header[3] = LOBYTE (length);
+ size = 4;
+ status = sanei_usb_write_bulk (devnum, header, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_read_mem: failed to write length header\n");
+ return status;
+ }
+ DBG (DBG_io, "sanei_rts88xx_read_mem: %02x %02x %02x %02x -> ...\n",
+ header[0], header[1], header[2], header[3]);
+ read = 0;
+ while (length > 0)
+ {
+ if (length > 2048)
+ want = 2048;
+ else
+ want = length;
+ size = want;
+ status = sanei_usb_read_bulk (devnum, value + read, &size);
+ if (size != want)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_read_mem: only read %lu bytes out of %lu\n",
+ (u_long) size, (u_long) want);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ length -= size;
+ read += size;
+ }
+ return status;
+}
+
+/*
+ * set memory with the given data
+ */
+SANE_Status
+sanei_rts88xx_get_mem (SANE_Int devnum, SANE_Byte ctrl1,
+ SANE_Byte ctrl2, SANE_Int length, SANE_Byte * value)
+{
+ SANE_Status status;
+ SANE_Byte regs[2];
+ regs[0] = ctrl1;
+ regs[1] = ctrl2;
+
+ status = sanei_rts88xx_write_regs (devnum, 0x91, regs, 2);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_get_mem: failed to write 0x91/0x92 registers\n");
+ return status;
+ }
+ status = sanei_rts88xx_read_mem (devnum, length, value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_get_mem: failed to read memory\n");
+ }
+ return status;
+}
+
+/*
+ * write to the nvram controler
+ */
+SANE_Status
+sanei_rts88xx_nvram_ctrl (SANE_Int devnum, SANE_Int length, SANE_Byte * value)
+{
+ SANE_Status status;
+ SANE_Int i;
+ char message[60 * 5];
+#ifdef HAZARDOUS_EXPERIMENT
+ SANE_Int size = 0;
+ SANE_Byte buffer[60];
+#endif
+
+ if (DBG_LEVEL > DBG_io)
+ {
+ for (i = 0; i < length; i++)
+ {
+ sprintf (message + 5 * i, "0x%02x ", value[i]);
+ }
+ DBG (DBG_io, "sanei_rts88xx_nvram_ctrl : devnum=%d, nvram_ctrl(0x00,%d)=%s\n",
+ devnum, length, message);
+ }
+
+#ifdef HAZARDOUS_EXPERIMENT
+ buffer[0] = 0x8a;
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = length;
+ for (i = 0; i < size; i++)
+ buffer[i + 4] = value[i];
+ /* the USB block is size + 4 bytes of header long */
+ size = length + 4;
+ status = sanei_usb_write_bulk (devnum, buffer, &size);
+#else
+ status = SANE_STATUS_GOOD;
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_nvram_ctrl : write failed ...\n");
+ }
+ return status;
+}
+
+/*
+ * setup nvram
+ */
+SANE_Status
+sanei_rts88xx_setup_nvram (SANE_Int devnum, SANE_Int length,
+ SANE_Byte * value)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte local[2], reg;
+ int i;
+
+ status = sanei_rts88xx_nvram_ctrl (devnum, length, value);
+
+#ifndef HAZARDOUS_EXPERIMENT
+ return SANE_STATUS_GOOD;
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_setup_nvram : failed step #1 ...\n");
+ return status;
+ }
+ local[0] = 0x18;
+ local[1] = 0x08;
+ for (i = 0; i < 8; i++)
+ {
+ status = sanei_rts88xx_nvram_ctrl (devnum, 2, local);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_setup_nvram : failed loop #%d ...\n",
+ i);
+ return status;
+ }
+ status = sanei_rts88xx_read_reg (devnum, 0x10, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_setup_nvram : register reading failed loop #%d ...\n",
+ i);
+ return status;
+ }
+ DBG (DBG_io, "sanei_rts88xx_setup_nvram: reg[0x10]=0x%02x\n", reg);
+ }
+ reg = 0;
+ status = sanei_rts88xx_write_reg (devnum, CONTROLER_REG, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_setup_nvram : controler register write failed\n");
+ return status;
+ }
+ reg = 1;
+ status = sanei_rts88xx_write_reg (devnum, CONTROLER_REG, &reg);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_setup_nvram : controler register write failed\n");
+ return status;
+ }
+ return status;
+}
+
+/*
+ * Sets scan area, no checks are being done, so watch your steps
+ */
+void
+sanei_rts88xx_set_scan_area (SANE_Byte * regs, SANE_Int ystart,
+ SANE_Int yend, SANE_Int xstart, SANE_Int xend)
+{
+ /* vertical lines to move before scan */
+ regs[START_LINE] = LOBYTE (ystart);
+ regs[START_LINE + 1] = HIBYTE (ystart);
+
+ /* total number of line to move */
+ regs[END_LINE] = LOBYTE (yend);
+ regs[END_LINE + 1] = HIBYTE (yend);
+
+ /* set horizontal start position */
+ regs[START_PIXEL] = LOBYTE (xstart);
+ regs[START_PIXEL + 1] = HIBYTE (xstart);
+
+ /* set horizontal end position */
+ regs[END_PIXEL] = LOBYTE (xend);
+ regs[END_PIXEL + 1] = HIBYTE (xend);
+}
+
+/**
+ * read available data count from scanner
+ * from tests it appears that advertised data
+ * may not be really available, and that a pause must be made
+ * before reading data so that it is really there.
+ * Such as reading data twice.
+ */
+SANE_Status
+sanei_rts88xx_data_count (SANE_Int devnum, SANE_Word * count)
+{
+ SANE_Status status;
+ size_t size;
+ static SANE_Byte header[4] = { 0x90, 0x00, 0x00, 3 };
+ SANE_Byte result[3];
+
+ /* set count in case of failure */
+ *count = 0;
+
+ size = 4;
+ status = sanei_usb_write_bulk (devnum, header, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_data_count : failed to write header\n");
+ return status;
+ }
+ size = 3;
+ status = sanei_usb_read_bulk (devnum, result, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_data_count : failed to read data count\n");
+ return status;
+ }
+ *count = result[0] + (result[1] << 8) + (result[2] << 16);
+ DBG (DBG_io2, "sanei_rts88xx_data_count: %d bytes available (0x%06x)\n",
+ *count, *count);
+ return status;
+}
+
+/**
+ * Waits for data being available while optionally polling motor. There is a timeout
+ * to prevent scanner waiting forever non coming data.
+ */
+SANE_Status
+sanei_rts88xx_wait_data (SANE_Int devnum, SANE_Bool busy, SANE_Word * count)
+{
+ SANE_Status status;
+ SANE_Byte control;
+
+ /* poll the available byte count until not 0 */
+ while (SANE_TRUE)
+ {
+ status = sanei_rts88xx_data_count (devnum, count);
+ if (*count != 0)
+ {
+ DBG (DBG_io, "sanei_rts88xx_wait_data: %d bytes available\n",
+ *count);
+ return status;
+ }
+
+ /* check that the scanner is busy scanning */
+ if (busy)
+ {
+ sanei_rts88xx_read_reg (devnum, CONTROL_REG, &control);
+ if ((control & 0x08) == 0 && (*count == 0))
+ {
+ DBG (DBG_error,
+ "sanei_rts88xx_wait_data: scanner stopped being busy before data are available\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ /* we hit timeout */
+ return SANE_STATUS_IO_ERROR;
+}
+
+/*
+ * read scanned data from scanner up to the size given. The actual length read is returned.
+ */
+SANE_Status
+sanei_rts88xx_read_data (SANE_Int devnum, SANE_Word * length,
+ unsigned char *dest)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte header[4];
+ size_t size, len, remain, read;
+
+ /* do not read too much data */
+ if (*length > RTS88XX_MAX_XFER_SIZE)
+ len = RTS88XX_MAX_XFER_SIZE;
+ else
+ len = *length;
+
+ /* write command header first */
+ header[0] = 0x91;
+ header[1] = 0x00;
+ header[2] = HIBYTE (len);
+ header[3] = LOBYTE (len);
+ size = 4;
+
+ status = sanei_usb_write_bulk (devnum, header, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_data: failed to write header\n");
+ }
+ read = 0;
+
+ /* first read blocks aligned on 64 bytes boundary */
+ while (len - read > 64)
+ {
+ size = (len - read) & 0xFFC0;
+ status = sanei_usb_read_bulk (devnum, dest + read, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_data: failed to read data\n");
+ return status;
+ }
+ DBG (DBG_io2, "sanei_rts88xx_read_data: read %lu bytes\n",
+ (u_long) size);
+ read += size;
+ }
+
+ /* then read remainder */
+ remain = len - read;
+ if (remain > 0)
+ {
+ status = sanei_usb_read_bulk (devnum, dest + read, &remain);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_rts88xx_read_data: failed to read data\n");
+ return status;
+ }
+ DBG (DBG_io2, "sanei_rts88xx_read_data: read %lu bytes\n",
+ (u_long) remain);
+ read += remain;
+ }
+
+ /* update actual read length */
+ DBG (DBG_io, "sanei_rts88xx_read_data: read %lu bytes, %d required\n",
+ (u_long) read, *length);
+ *length = read;
+ return status;
+}
diff --git a/backend/rts88xx_lib.h b/backend/rts88xx_lib.h
new file mode 100644
index 0000000..71bd72a
--- /dev/null
+++ b/backend/rts88xx_lib.h
@@ -0,0 +1,213 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2012 stef.dev@free.fr
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef RTS88XX_LIB_H
+#define RTS88XX_LIB_H
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_usb.h"
+#include <unistd.h>
+#include <string.h>
+
+/* TODO put his in a place where it can be reused */
+
+#define DBG_error0 0 /* errors/warnings printed even with debuglevel 0 */
+#define DBG_error 1 /* fatal errors */
+#define DBG_init 2 /* initialization and scanning time messages */
+#define DBG_warn 3 /* warnings and non-fatal errors */
+#define DBG_info 4 /* informational messages */
+#define DBG_proc 5 /* starting/finishing functions */
+#define DBG_io 6 /* io functions */
+#define DBG_io2 7 /* io functions that are called very often */
+#define DBG_data 8 /* log data sent and received */
+
+/*
+ * defines for registers name
+ */
+#define CONTROL_REG 0xb3
+#define CONTROLER_REG 0x1d
+
+/* geometry registers */
+#define START_LINE 0x60
+#define END_LINE 0x62
+#define START_PIXEL 0x66
+#define END_PIXEL 0x6c
+
+#define RTS88XX_MAX_XFER_SIZE 0xFFC0
+
+#define LOBYTE(x) ((uint8_t)((x) & 0xFF))
+#define HIBYTE(x) ((uint8_t)((x) >> 8))
+
+/* this function init the rts88xx library */
+void sanei_rts88xx_lib_init (void);
+SANE_Bool sanei_rts88xx_is_color (SANE_Byte * regs);
+
+void sanei_rts88xx_set_gray_scan (SANE_Byte * regs);
+void sanei_rts88xx_set_color_scan (SANE_Byte * regs);
+void sanei_rts88xx_set_offset (SANE_Byte * regs, SANE_Byte red,
+ SANE_Byte green, SANE_Byte blue);
+void sanei_rts88xx_set_gain (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
+ SANE_Byte blue);
+void sanei_rts88xx_set_scan_frequency (SANE_Byte * regs, int frequency);
+
+/*
+ * set scan area
+ */
+void sanei_rts88xx_set_scan_area (SANE_Byte * reg, SANE_Int ystart,
+ SANE_Int yend, SANE_Int xstart,
+ SANE_Int xend);
+
+/*
+ * read one register at given index
+ */
+SANE_Status sanei_rts88xx_read_reg (SANE_Int devnum, SANE_Int index,
+ SANE_Byte * reg);
+
+/*
+ * read scanned data from scanner up to the size given. The actual length read is returned.
+ */
+SANE_Status sanei_rts88xx_read_data (SANE_Int devnum, SANE_Word * length,
+ unsigned char *dest);
+
+/*
+ * write one register at given index
+ */
+SANE_Status sanei_rts88xx_write_reg (SANE_Int devnum, SANE_Int index,
+ SANE_Byte * reg);
+
+/*
+ * write length consecutive registers, starting at index
+ * register 0xb3 is never wrote in bulk register write, so we split
+ * write if it belongs to the register set sent
+ */
+SANE_Status sanei_rts88xx_write_regs (SANE_Int devnum, SANE_Int start,
+ SANE_Byte * source, SANE_Int length);
+
+/* read several registers starting at the given index */
+SANE_Status sanei_rts88xx_read_regs (SANE_Int devnum, SANE_Int start,
+ SANE_Byte * dest, SANE_Int length);
+
+/*
+ * get status by reading registers 0x10 and 0x11
+ */
+SANE_Status sanei_rts88xx_get_status (SANE_Int devnum, SANE_Byte * regs);
+/*
+ * set status by writing registers 0x10 and 0x11
+ */
+SANE_Status sanei_rts88xx_set_status (SANE_Int devnum, SANE_Byte * regs,
+ SANE_Byte reg10, SANE_Byte reg11);
+
+/*
+ * get lamp status by reading registers 0x84 to 0x8d
+ */
+SANE_Status sanei_rts88xx_get_lamp_status (SANE_Int devnum, SANE_Byte * regs);
+
+/* reset lamp */
+SANE_Status sanei_rts88xx_reset_lamp (SANE_Int devnum, SANE_Byte * regs);
+
+/* get lcd panel status */
+SANE_Status sanei_rts88xx_get_lcd (SANE_Int devnum, SANE_Byte * regs);
+
+/*
+ * write to special control register CONTROL_REG=0xb3
+ */
+SANE_Status sanei_rts88xx_write_control (SANE_Int devnum, SANE_Byte value);
+
+/*
+ * send the cancel control sequence
+ */
+SANE_Status sanei_rts88xx_cancel (SANE_Int devnum);
+
+/*
+ * read available data count from scanner
+ */
+SANE_Status sanei_rts88xx_data_count (SANE_Int devnum, SANE_Word * count);
+
+/*
+ * wait for scanned data to be available, if busy is true, check is scanner is busy
+ * while waiting. The number of data bytes of available data is returned in 'count'.
+ */
+SANE_Status sanei_rts88xx_wait_data (SANE_Int devnum, SANE_Bool busy,
+ SANE_Word * count);
+
+ /*
+ * write the given number of bytes pointed by value into memory
+ */
+SANE_Status sanei_rts88xx_write_mem (SANE_Int devnum, SANE_Int length,
+ SANE_Int extra, SANE_Byte * value);
+
+ /*
+ * set memory with the given data
+ */
+SANE_Status sanei_rts88xx_set_mem (SANE_Int devnum, SANE_Byte ctrl1,
+ SANE_Byte ctrl2, SANE_Int length,
+ SANE_Byte * value);
+
+ /*
+ * read the given number of bytes from memory into buffer
+ */
+SANE_Status sanei_rts88xx_read_mem (SANE_Int devnum, SANE_Int length,
+ SANE_Byte * value);
+
+ /*
+ * get memory
+ */
+SANE_Status sanei_rts88xx_get_mem (SANE_Int devnum, SANE_Byte ctrl1,
+ SANE_Byte ctrl2, SANE_Int length,
+ SANE_Byte * value);
+
+ /*
+ * write to the nvram controler
+ */
+SANE_Status sanei_rts88xx_nvram_ctrl (SANE_Int devnum, SANE_Int length,
+ SANE_Byte * value);
+
+ /*
+ * setup nvram
+ */
+SANE_Status sanei_rts88xx_setup_nvram (SANE_Int devnum, SANE_Int length,
+ SANE_Byte * value);
+
+
+ /* does a simple scan, putting data in image */
+/* SANE_Status sanei_rts88xx_simple_scan (SANE_Int devnum, SANE_Byte * regs, int regcount, SANE_Word size, unsigned char *image); */
+#endif /* not RTS88XX_LIB_H */
diff --git a/backend/s9036.c b/backend/s9036.c
new file mode 100644
index 0000000..9313622
--- /dev/null
+++ b/backend/s9036.c
@@ -0,0 +1,1341 @@
+/* sane - Scanner Access Now Easy.
+
+ This file (C) 1997 Ingo Schneider
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for Siemens 9036 flatbed scanners. */
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "s9036.h"
+
+#define BACKEND_NAME s9036
+#include "../include/sane/sanei_backend.h"
+
+
+#undef Byte
+#define Byte SANE_Byte
+
+static int num_devices;
+static S9036_Device *s9036_devices;
+
+
+/* sets loc_s bytes long value at offset loc in scsi command to value size */
+static void
+set_size (Byte * loc, int loc_s, size_t size)
+{
+ int i;
+
+ for (i = 0; i < loc_s; i++)
+ {
+ loc[loc_s - i - 1] = (size >> (i * 8)) & 0xff;
+ }
+}
+
+static long
+reserve_unit (int fd)
+{
+ const Byte scsi_reserve[] =
+ {
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ DBG (3, "reserve_unit()\n");
+ return sanei_scsi_cmd (fd, scsi_reserve, sizeof (scsi_reserve), 0, 0);
+}
+
+static long
+release_unit (int fd)
+{
+ const Byte scsi_release[] =
+ {
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ DBG (3, "release_unit()\n");
+ return sanei_scsi_cmd (fd, scsi_release, sizeof (scsi_release), 0, 0);
+}
+
+static SANE_Status
+test_ready (int fd)
+{
+ static const Byte scsi_test_ready[] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ SANE_Status status;
+ int try;
+
+ for (try = 0; try < 1000; ++try)
+ {
+ DBG (3, "test_ready: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd (fd, scsi_test_ready, sizeof (scsi_test_ready),
+ 0, 0);
+
+ switch (status)
+ {
+ case SANE_STATUS_DEVICE_BUSY:
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+
+ default:
+ DBG (1, "test_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ DBG (1, "test_ready: timed out after %d attempts\n", try);
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char *result, void *arg)
+{
+ scsi_fd = scsi_fd;
+ arg = arg; /* silence compilation warnings */
+
+ if (result[0])
+ {
+ DBG (0, "sense_handler() : sense code = %02x\n", result[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+}
+
+static SANE_Status
+stop_scan (int fd)
+{
+ fd = fd; /* silence compilation warnings */
+
+ /* XXX don't know how to stop the scanner. To be tested ! */
+#if 0
+ const Byte scsi_rewind[] =
+ {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ DBG (1, "Trying to stop scanner...\n");
+ return sanei_scsi_cmd (fd, scsi_rewind, sizeof (scsi_rewind), 0, 0);
+#else
+ return SANE_STATUS_GOOD;
+#endif
+}
+
+
+static SANE_Status
+start_scan (int fd, SANE_Bool cont)
+{
+ struct
+ {
+ /* Command */
+ Byte cmd;
+ Byte lun;
+ Byte res[2];
+ Byte tr_len;
+ Byte ctrl;
+
+ /* Data */
+ Byte wid;
+ }
+ scsi_start_scan;
+
+ memset (&scsi_start_scan, 0, sizeof (scsi_start_scan));
+ scsi_start_scan.cmd = 0x1b;
+ scsi_start_scan.tr_len = 1;
+ scsi_start_scan.wid = 0;
+ scsi_start_scan.ctrl = (cont == SANE_TRUE) ? 0x80 : 0x00;
+
+ DBG (1, "Starting scanner ...\n");
+ return sanei_scsi_cmd (fd, &scsi_start_scan, sizeof (scsi_start_scan), 0, 0);
+}
+
+static void
+wait_ready (int fd)
+{
+# define WAIT_READY_READ_SIZE 4
+ const Byte scsi_read[] =
+ {
+ 0x28, 0x00, /* opcode, lun */
+ 0x80, /* data type 80 == read time left */
+ 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, WAIT_READY_READ_SIZE, /* transfer length */
+ 0x00, /* control byte */
+ };
+
+ Byte result[WAIT_READY_READ_SIZE];
+ size_t size = WAIT_READY_READ_SIZE;
+ SANE_Status status;
+
+ while (1)
+ {
+ status = sanei_scsi_cmd (fd, scsi_read, sizeof (scsi_read),
+ result, &size);
+
+ if (status != SANE_STATUS_GOOD || size != WAIT_READY_READ_SIZE)
+ {
+ /*
+ Command failed, the assembler code of the windows scan library
+ ignores this condition, and so do I
+ */
+ break;
+ }
+ else
+ {
+ /* left is the amount of seconds left till the scanner is
+ ready * 100 */
+ int left = result[2] * 256 + result[3];
+
+ DBG (1, "wait_ready() : %d left...\n", left);
+
+ if (!left)
+ break;
+ /* We delay only for half the given time */
+ else if (left < 200)
+ usleep (left * 5000);
+ else
+ sleep (left / 200);
+ }
+ }
+
+ return;
+}
+
+static SANE_Status
+get_read_sizes (int fd, int *lines_available, int *bpl, int *total_lines)
+{
+# define GET_READ_SIZES_READ_SIZE 24
+
+ const Byte scsi_read[] =
+ {
+ 0x28, 0x00, /* opcode, lun */
+ 0x81, /* data type 81 == read time left */
+ 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, GET_READ_SIZES_READ_SIZE, /* transfer length */
+ 0x00, /* control byte */
+ };
+
+ Byte result[GET_READ_SIZES_READ_SIZE];
+ size_t size = GET_READ_SIZES_READ_SIZE;
+ SANE_Status status;
+
+ status = sanei_scsi_cmd (fd, scsi_read, sizeof (scsi_read), result, &size);
+
+ if (status != SANE_STATUS_GOOD || size != GET_READ_SIZES_READ_SIZE)
+ {
+ /* Command failed */
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ *lines_available = result[14] * 256 + result[15];
+ *bpl = result[12] * 256 + result[13];
+ if (total_lines)
+ *total_lines = result[10] * 256 + result[11];
+ }
+
+ DBG (1, "get_read_sizes() : %d of %d, %d\n",
+ *lines_available, total_lines ? *total_lines : -1, *bpl);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+set_window (S9036_Scanner * s)
+/* This function sets and sends the window for scanning */
+{
+ double pixels_per_mm = (double) s->val[OPT_RESOLUTION] / MM_PER_INCH;
+
+ SANE_Bool auto_bright = !(s->opt[OPT_BRIGHT_ADJUST].cap & SANE_CAP_INACTIVE);
+ SANE_Bool auto_contr = !(s->opt[OPT_CONTR_ADJUST].cap & SANE_CAP_INACTIVE);
+
+ /* ranges down 255 (dark) down to 1(bright) */
+ int brightness = auto_bright ? 0 : (SANE_UNFIX (s->val[OPT_BRIGHTNESS])
+ * -1.27 + 128.5);
+ /* ranges from 1 (little contrast) up to 255 (much contrast) */
+ int contrast = auto_contr ? 0 : (SANE_UNFIX (s->val[OPT_CONTRAST])
+ * 1.27 + 128.5);
+
+ /* ranges from 40 (dark) down to 0 (bright) */
+ int bright_adjust = auto_bright ? 20 - s->val[OPT_BRIGHT_ADJUST] : 0;
+ /* ranges from 20 (little contrast) down to -20 = 235 (much contrast) */
+ int contr_adjust = auto_contr ? (256 - s->val[OPT_CONTR_ADJUST]) % 256 : 0;
+
+ /* Warning ! The following structur SEEMS to be an valid SCSI-2
+ SET_WINDOW command. But e.g. the limits for the window are only
+ 2 Bytes instead of 4. The scanner was built at about 1990, so
+ SCSI-2 wasn't available for development...
+ */
+
+ struct
+ {
+ Byte cmd;
+ Byte lun;
+ Byte re1[4];
+ Byte tr_len[3];
+ Byte ctrl;
+
+ Byte re2[6];
+ Byte wd_len[2];
+
+ struct
+ {
+ Byte wid;
+ Byte autobit;
+ Byte x_axis_res[2];
+ Byte y_axis_res[2];
+
+ Byte x_axis_ul[2];
+ Byte y_axis_ul[2];
+
+ Byte wwidth[2];
+ Byte wlength[2];
+
+ Byte contrast;
+ Byte threshold;
+ Byte brightness;
+
+ Byte image_comp;
+ Byte bpp;
+
+ Byte ht_pattern;
+ Byte rif_padding;
+ Byte three;
+
+ Byte null1[2];
+ Byte null2[8];
+
+ Byte null_eins;
+ Byte eins_null;
+
+ Byte contr_adjust;
+ Byte bright_adjust;
+
+ Byte null3;
+
+ }
+ wd;
+
+ }
+ cmd;
+
+ DBG (3,
+ "Setting parameters: bpp %d, res %d, bri %d, con %d, bad %d, cad %d\n",
+ s->val[OPT_DEPTH], s->val[OPT_RESOLUTION],
+ brightness, contrast, bright_adjust, contr_adjust);
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ /* Commands and sizes. Original comment in German: Kommando und Groessen. */
+ cmd.cmd = 0x24;
+ set_size (cmd.tr_len, 3, 37 + 8);
+ set_size (cmd.wd_len, 2, 37);
+
+ /* Resolution. Original comment in German: Aufloesung */
+ set_size (cmd.wd.x_axis_res, 2, s->val[OPT_RESOLUTION]);
+ set_size (cmd.wd.y_axis_res, 2, s->val[OPT_RESOLUTION]);
+
+ /* Scan window position/size. Original comment in German:
+ Fensterposition / Groesse */
+ set_size (cmd.wd.x_axis_ul, 2,
+ SANE_UNFIX (s->val[OPT_TL_X]) * pixels_per_mm + 0.5);
+ set_size (cmd.wd.y_axis_ul, 2,
+ SANE_UNFIX (s->val[OPT_TL_Y]) * pixels_per_mm + 0.5);
+ set_size (cmd.wd.wwidth, 2, SANE_UNFIX (s->val[OPT_BR_X] - s->val[OPT_TL_X])
+ * pixels_per_mm + 0.5);
+ set_size (cmd.wd.wlength, 2, SANE_UNFIX (s->val[OPT_BR_Y] - s->val[OPT_TL_Y])
+ * pixels_per_mm + 0.5);
+
+ cmd.wd.contrast = contrast;
+ cmd.wd.threshold = 0x00;
+ cmd.wd.brightness = brightness;
+
+ cmd.wd.image_comp = (s->val[OPT_DEPTH] == 1) ? 0 : 2;
+ cmd.wd.bpp = s->val[OPT_DEPTH];
+
+ cmd.wd.ht_pattern = 0;
+ cmd.wd.rif_padding = 0x00;
+ cmd.wd.three = 3;
+
+ cmd.wd.null_eins = (s->val[OPT_DEPTH] == 1) ? 0 : 1;
+ cmd.wd.eins_null = (s->val[OPT_DEPTH] == 1) ? 1 : 0;
+
+ cmd.wd.contr_adjust = contr_adjust;
+ cmd.wd.bright_adjust = bright_adjust;
+
+ return sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
+}
+
+/* Tell scanner to scan more data. Original comment in German:
+ Fordert Scanner auf, weiter zu scannen... */
+static SANE_Status
+request_more_data (S9036_Scanner * s)
+{
+ SANE_Status status;
+ int lines_available;
+ int bytes_per_line;
+
+ status = start_scan (s->fd, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ wait_ready (s->fd);
+
+ status = get_read_sizes (s->fd, &lines_available, &bytes_per_line, 0);
+
+ if (!lines_available || bytes_per_line != s->params.bytes_per_line)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->lines_read + lines_available > s->params.lines)
+ return SANE_STATUS_INVAL;
+
+ s->lines_in_scanner = lines_available;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* May only be called when there is at least one row of data to
+ be read.
+
+ Original comment in German: Darf nur aufgerufen werden, wenn
+ wirklich noch Zeilen zu scannen/lesen sind ! */
+static SANE_Status
+read_more_data (S9036_Scanner * s)
+{
+
+ static Byte cmd[] =
+ {
+ 0x28, 0x00, /* opcode, lun */
+ 0x00, /* data type 80 == read time left */
+ 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, 0x00, /* transfer length */
+ 0x00, /* control byte */
+ };
+
+ SANE_Status status;
+ size_t size;
+ int lines_read;
+ int bpl = s->params.bytes_per_line;
+ unsigned int i;
+
+ if (s->lines_in_scanner == 0)
+ {
+ /* No lines in scanner ? scan some more */
+ status = request_more_data (s);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ }
+
+ /* We try this 3 times */
+ while (1)
+ {
+
+ /* Request as much lines as would fit into the buffer ... */
+ lines_read = s->bufsize / bpl;
+
+ /* buffer is too small for one line: we can't handle this */
+ if (!lines_read)
+ return SANE_STATUS_INVAL;
+
+ /* We only request as many lines as there are already scanned */
+ if (lines_read > s->lines_in_scanner)
+ lines_read = s->lines_in_scanner;
+
+ set_size (&cmd[6], 3, lines_read);
+ size = lines_read * s->params.bytes_per_line;
+
+ DBG (1, "Requesting %d lines, in scanner: %d, total: %d\n", lines_read,
+ s->lines_in_scanner, s->params.lines);
+
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), s->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (s->bufsize > 4096)
+ {
+ DBG (1, "sanei_scsi_cmd(): using 4k buffer\n");
+ s->bufsize = 4096;
+ continue;
+ }
+
+ DBG (1, "sanei_scsi_cmd() = %d\n", status);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (size != (unsigned int) lines_read * s->params.bytes_per_line)
+ {
+ DBG (1, "sanei_scsi_cmd(): got %lu bytes, expected %d\n",
+ (u_long) size, lines_read * s->params.bytes_per_line);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (1, "Got %lu bytes\n", (u_long) size);
+ break;
+ }
+
+
+ /* Reverse: */
+ if (s->params.depth != 1)
+ for (i = 0; i < size; i++)
+ s->buffer[i] = (255 - s->buffer[i]);
+
+ s->in_buffer += size;
+ s->lines_in_scanner -= lines_read;
+ s->lines_read += lines_read;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+attach (const char *devname, S9036_Device ** devp)
+{
+#define ATTACH_SCSI_INQ_LEN 55
+ const Byte scsi_inquiry[] =
+ {
+ 0x12, 0x00, 0x00, 0x00, ATTACH_SCSI_INQ_LEN, 0x00
+ };
+ Byte result[ATTACH_SCSI_INQ_LEN];
+
+ int fd;
+ S9036_Device *dev;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ for (dev = s9036_devices; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "attach: opening %s\n", devname);
+ status = sanei_scsi_open (devname, &fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed (%s)\n", sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ status = sanei_scsi_cmd (fd, scsi_inquiry, sizeof (scsi_inquiry),
+ result, &size);
+ if (status != SANE_STATUS_GOOD || size != ATTACH_SCSI_INQ_LEN)
+ {
+ DBG (1, "attach: inquiry failed (%s)\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ status = test_ready (fd);
+ sanei_scsi_close (fd);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* The structure send by the scanner after inquiry is not SCSI-2
+ compatible. The standard manufacturer/model fields are no ASCII
+ strings, but ? At offset 36 my SIEMENS scanner identifies as an
+ AGFA one ?! */
+
+ if (result[0] != 6 || strncmp ((char *)result + 36, "AGFA03", 6))
+ {
+ DBG (1, "attach: device doesn't look like a Siemens 9036 scanner\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "Inquiry data:\n");
+ for (i = 5; i < 55; i += 10)
+ DBG (3, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ result[i], result[i + 1], result[i + 2], result[i + 3], result[i + 4],
+ result[i + 5], result[i + 6], result[i + 7], result[i + 8],
+ result[i + 9]);
+
+ dev = malloc (sizeof (*dev));
+
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor = "Siemens";
+ dev->sane.model = "9036";
+ dev->sane.type = "flatbed scanner";
+
+ dev->handle = 0;
+
+ DBG (3, "attach: found S9036 scanner model\n");
+
+ ++num_devices;
+ dev->next = s9036_devices;
+ s9036_devices = dev;
+
+ if (devp)
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (S9036_Scanner * s)
+{
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ stop_scan (s->fd);
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ if (s->buffer)
+ {
+ free (s->buffer);
+ s->buffer = 0;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+
+static SANE_Status
+init_options (S9036_Scanner * s)
+{
+ int i;
+
+ /* Hardware Limitations: must be static ! */
+ static const SANE_Int depth_list[] =
+ {2, 1, 8};
+
+ static const SANE_Int dpi_list[] =
+ {8, 100, 200, 300, 400, 500, 600, 700, 800};
+
+ static const SANE_Range percentage_range =
+ {
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+ };
+
+ static const SANE_Range automatic_adjust_range =
+ {-20, 20, 1};
+
+ static const SANE_Range x_range =
+ {0, SANE_FIX (8.27 * MM_PER_INCH), 0};
+ static const SANE_Range y_range =
+ {0, SANE_FIX (12.72 * MM_PER_INCH), 0};
+
+ /* ------ */
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS] = NUM_OPTIONS;
+
+ /* "Mode" group: */
+
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* depth */
+ s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
+ s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_DEPTH].constraint.word_list = depth_list;
+ s->val[OPT_DEPTH] = 1;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+ s->val[OPT_RESOLUTION] = 100;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+ s->val[OPT_TL_X] = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y] = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X] = x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y] = y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_AUTOMATIC;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS] = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_AUTOMATIC;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST] = 0;
+
+ /* brightness automatic correct */
+ s->opt[OPT_BRIGHT_ADJUST].name = "adjust-bright";
+ s->opt[OPT_BRIGHT_ADJUST].title = "Automatic brightness adjust";
+ s->opt[OPT_BRIGHT_ADJUST].desc = "Controls the automatic brightness of the "
+ "acquired image. This option is active for automatic brightness only.";
+ s->opt[OPT_BRIGHT_ADJUST].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHT_ADJUST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHT_ADJUST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHT_ADJUST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHT_ADJUST].constraint.range = &automatic_adjust_range;
+ s->val[OPT_BRIGHT_ADJUST] = 0;
+
+ /* contrast automatic correct */
+ s->opt[OPT_CONTR_ADJUST].name = "adjust-contr";
+ s->opt[OPT_CONTR_ADJUST].title = "Automatic contrast adjust";
+ s->opt[OPT_CONTR_ADJUST].desc = "Controls the automatic contrast of the "
+ " acquired image. This option is active for automatic contrast only.";
+ s->opt[OPT_CONTR_ADJUST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTR_ADJUST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTR_ADJUST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTR_ADJUST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTR_ADJUST].constraint.range = &automatic_adjust_range;
+ s->val[OPT_CONTR_ADJUST] = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach (dev, 0);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* silence compilation warnings */
+
+ DBG_INIT ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open ("s9036.conf");
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ S9036_Device *dev, *next;
+
+ for (dev = s9036_devices; dev; dev = next)
+ {
+ next = dev->next;
+ if (dev->handle)
+ sane_close (dev->handle);
+ free (dev);
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ S9036_Device *dev;
+ int i;
+
+ local_only = local_only; /* silence compilation warnings */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = s9036_devices, i = 0; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ S9036_Device *dev;
+ SANE_Status status;
+ S9036_Scanner *s;
+
+ if (devicename[0])
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = s9036_devices;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (dev->handle)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+
+ memset (s, 0, sizeof (*s));
+ s->scanning = SANE_FALSE;
+ s->fd = -1;
+ s->hw = dev;
+ s->hw->handle = s;
+
+ init_options (s);
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ S9036_Scanner *s = handle;
+
+ if (s->scanning)
+ do_cancel (handle);
+
+ s->hw->handle = 0;
+
+ free (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ S9036_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ S9036_Scanner *s = handle;
+ SANE_Status status;
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS || !SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ case OPT_DEPTH:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHT_ADJUST:
+ case OPT_CONTR_ADJUST:
+ *(SANE_Word *) val = s->val[option];
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ case OPT_DEPTH:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_BRIGHT_ADJUST:
+ case OPT_CONTR_ADJUST:
+ s->val[option] = *(SANE_Word *) val;
+ break;
+ case OPT_BRIGHTNESS:
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BRIGHT_ADJUST].cap))
+ {
+ s->opt[OPT_BRIGHT_ADJUST].cap |= SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ s->val[option] = *(SANE_Word *) val;
+ break;
+ case OPT_CONTRAST:
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_CONTR_ADJUST].cap))
+ {
+ s->opt[OPT_CONTR_ADJUST].cap |= SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ s->val[option] = *(SANE_Word *) val;
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ }
+ else if (action == SANE_ACTION_SET_AUTO)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap))
+ return SANE_STATUS_UNSUPPORTED;
+
+ switch (option)
+ {
+ case OPT_BRIGHTNESS:
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[OPT_BRIGHT_ADJUST].cap))
+ {
+ s->opt[OPT_BRIGHT_ADJUST].cap &= ~SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ case OPT_CONTRAST:
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[OPT_CONTR_ADJUST].cap))
+ {
+ s->opt[OPT_CONTR_ADJUST].cap &= ~SANE_CAP_INACTIVE;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ }
+ else
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ S9036_Scanner *s = handle;
+
+ if (!s->scanning)
+ {
+ double width, height, dpi;
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.last_frame = SANE_TRUE;
+
+ s->params.depth = s->val[OPT_DEPTH];
+
+ width = SANE_UNFIX (s->val[OPT_BR_X] - s->val[OPT_TL_X]);
+ height = SANE_UNFIX (s->val[OPT_BR_Y] - s->val[OPT_TL_Y]);
+ dpi = s->val[OPT_RESOLUTION];
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (dpi > 0.0 && width > 0.0 && height > 0.0)
+ {
+ double dots_per_mm = dpi / MM_PER_INCH;
+
+ s->params.pixels_per_line = width * dots_per_mm + 0.5;
+ s->params.lines = height * dots_per_mm + 0.5;
+ }
+
+ s->params.bytes_per_line =
+ (s->params.pixels_per_line + (8 - s->params.depth))
+ / (8 / s->params.depth);
+ }
+
+ if (params)
+ *params = s->params;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ S9036_Scanner *s = handle;
+ SANE_Status status;
+
+ if (s->scanning)
+ do_cancel (s);
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (s->fd < 0)
+ {
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ s->fd = -1;
+ return status;
+ }
+ }
+
+ status = test_ready (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: test_ready() failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ status = reserve_unit (s->fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: reserve_unit() failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ status = set_window (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: set_window() failed: %s\n", sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+ s->scanning = SANE_TRUE;
+
+ status = start_scan (s->fd, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: start_scan() failed: %s\n", sane_strstatus (status));
+ do_cancel (s);
+ return status;
+ }
+
+ wait_ready (s->fd);
+
+ {
+ int lines_available = 0, bytes_per_line = 0, total_lines = 0;
+
+ status = get_read_sizes (s->fd, &lines_available, &bytes_per_line,
+ &total_lines);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open: get_read_sizes() failed: %s\n",
+ sane_strstatus (status));
+ do_cancel (s);
+ return status;
+ }
+
+ if (!lines_available || !bytes_per_line || !total_lines)
+ {
+ DBG (1, "open: invalid_sizes(): %d, %d, %d\n",
+ lines_available, bytes_per_line, total_lines);
+ do_cancel (s);
+ return SANE_STATUS_INVAL;
+ }
+
+ s->params.lines = total_lines;
+ s->params.bytes_per_line = bytes_per_line;
+ s->params.pixels_per_line = bytes_per_line * (8 / s->params.depth);
+
+ s->lines_in_scanner = lines_available;
+ s->lines_read = 0;
+
+ /* Buffer must be at least 4k */
+ s->bufsize = (sanei_scsi_max_request_size < 4096) ?
+ 4096 : sanei_scsi_max_request_size;
+
+ s->buffer = (Byte *) malloc (s->bufsize * sizeof (Byte));
+
+ if (!s->buffer)
+ {
+ DBG (1, "open malloc(%lu) failed.\n", (u_long) s->bufsize);
+ do_cancel (s);
+ return SANE_STATUS_NO_MEM;
+ }
+ s->bufstart = s->buffer;
+ s->in_buffer = 0;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+copy_buffer (S9036_Scanner * s, SANE_Byte ** buf, SANE_Int * max_len,
+ SANE_Int * len)
+{
+ if (*max_len > (SANE_Int) s->in_buffer)
+ {
+ memcpy (*buf, s->bufstart, s->in_buffer);
+ *buf += s->in_buffer;
+ *len += s->in_buffer;
+ *max_len -= s->in_buffer;
+
+ s->bufstart = s->buffer;
+ s->in_buffer = 0;
+ }
+ else
+ {
+ memcpy (*buf, s->bufstart, *max_len);
+ s->bufstart += *max_len;
+ s->in_buffer -= *max_len;
+
+ *buf += *max_len;
+ *len += *max_len;
+ *max_len = 0;
+ }
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ S9036_Scanner *s = handle;
+ SANE_Status status;
+
+ if (s->scanning != SANE_TRUE || max_len == 0)
+ return SANE_STATUS_INVAL;
+
+ *len = 0;
+
+ DBG (3, "sane_read(%d) : lines_read %d\n", max_len, s->lines_read);
+
+ while (max_len > (SANE_Int) s->in_buffer && s->lines_read < s->params.lines)
+ {
+
+ if (s->in_buffer == 0)
+ {
+ status = read_more_data (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_read: read_more_data() failed (%s)\n",
+ sane_strstatus (status));
+ do_cancel (s);
+ return status;
+ }
+ }
+
+ copy_buffer (s, &buf, &max_len, len);
+
+ if (!max_len || s->lines_read >= s->params.lines)
+ return SANE_STATUS_GOOD;
+ }
+
+ /* If we reached this point, there are either enough bytes in the buffer,
+ or, if the buffer is empty, we already reached the end of the page */
+
+ if (s->in_buffer > 0)
+ {
+ copy_buffer (s, &buf, &max_len, len);
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel (s);
+ DBG (1, "EOF\n");
+ return SANE_STATUS_EOF;
+ }
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ S9036_Scanner *s = handle;
+ do_cancel (s);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ handle = handle; /* silence compilation warnings */
+
+ DBG (1, "sane_set_io_mode(%d)\n", non_blocking);
+
+ return (non_blocking == SANE_TRUE) ?
+ SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle;
+ fd = fd; /* silence compilation warnings */
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/s9036.conf.in b/backend/s9036.conf.in
new file mode 100644
index 0000000..caa0783
--- /dev/null
+++ b/backend/s9036.conf.in
@@ -0,0 +1 @@
+/dev/scanner
diff --git a/backend/s9036.h b/backend/s9036.h
new file mode 100644
index 0000000..094f94e
--- /dev/null
+++ b/backend/s9036.h
@@ -0,0 +1,83 @@
+/* sane - Scanner Access Now Easy.
+
+ This file (C) 1997 Ingo Schneider
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+#ifndef s9036_h
+#define s9036_h
+
+enum S9036_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_DEPTH,
+ OPT_RESOLUTION,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_BRIGHT_ADJUST,
+ OPT_CONTR_ADJUST,
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+typedef struct S9036_Device
+ {
+ struct S9036_Device *next;
+ SANE_Device sane;
+ SANE_Handle handle;
+ }
+S9036_Device;
+
+typedef struct S9036_Scanner
+ {
+ /* all the state needed to define a scan request: */
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ SANE_Word val[NUM_OPTIONS];
+
+ /* Parsed option values and variables that are valid only during
+ actual scanning: */
+ SANE_Bool scanning;
+ SANE_Parameters params;
+
+ size_t bufsize; /* about SCSI_MAX_REQUEST_SIZE */
+ SANE_Byte *buffer; /* buffer of size 'bufsize' */
+ SANE_Byte *bufstart; /* Start of data for next read */
+ size_t in_buffer; /* bytes already in buffer */
+
+ int lines_in_scanner; /* Lines in scanner memory */
+ int lines_read; /* Total lines read for now */
+
+ int fd; /* SCSI filedescriptor */
+
+ /* scanner dependent/low-level state: */
+ S9036_Device *hw;
+
+ }
+S9036_Scanner;
+
+#endif /* s9036_h */
diff --git a/backend/sane_strstatus.c b/backend/sane_strstatus.c
new file mode 100644
index 0000000..1fc2220
--- /dev/null
+++ b/backend/sane_strstatus.c
@@ -0,0 +1,109 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Beck
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the backend-independent parts of SANE. */
+
+#include <stdio.h>
+
+#include "../include/sane/sane.h"
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+SANE_String_Const
+sane_strstatus (SANE_Status status)
+{
+ static char buf[80];
+
+ switch (status)
+ {
+ case SANE_STATUS_GOOD:
+ return SANE_I18N("Success");
+
+ case SANE_STATUS_UNSUPPORTED:
+ return SANE_I18N("Operation not supported");
+
+ case SANE_STATUS_CANCELLED:
+ return SANE_I18N("Operation was cancelled");
+
+ case SANE_STATUS_DEVICE_BUSY:
+ return SANE_I18N("Device busy");
+
+ case SANE_STATUS_INVAL:
+ return SANE_I18N("Invalid argument");
+
+ case SANE_STATUS_EOF:
+ return SANE_I18N("End of file reached");
+
+ case SANE_STATUS_JAMMED:
+ return SANE_I18N("Document feeder jammed");
+
+ case SANE_STATUS_NO_DOCS:
+ return SANE_I18N("Document feeder out of documents");
+
+ case SANE_STATUS_COVER_OPEN:
+ return SANE_I18N("Scanner cover is open");
+
+ case SANE_STATUS_IO_ERROR:
+ return SANE_I18N("Error during device I/O");
+
+ case SANE_STATUS_NO_MEM:
+ return SANE_I18N("Out of memory");
+
+ case SANE_STATUS_ACCESS_DENIED:
+ return SANE_I18N("Access to resource has been denied");
+
+#ifdef SANE_STATUS_WARMING_UP
+ case SANE_STATUS_WARMING_UP:
+ return SANE_I18N("Lamp not ready, please retry");
+#endif
+
+#ifdef SANE_STATUS_HW_LOCKED
+ case SANE_STATUS_HW_LOCKED:
+ return SANE_I18N("Scanner mechanism locked for transport");
+#endif
+
+ default:
+ /* non-reentrant, but better than nothing */
+ sprintf (buf, "Unknown SANE status code %d", status);
+ return buf;
+ }
+}
diff --git a/backend/saned.conf.in b/backend/saned.conf.in
new file mode 100644
index 0000000..664e459
--- /dev/null
+++ b/backend/saned.conf.in
@@ -0,0 +1,31 @@
+# saned.conf
+# Configuration for the saned daemon
+
+## Daemon options
+# Port range for the data connection. Choose a range inside [1024 - 65535].
+# Avoid specifying too large a range, for performance reasons.
+#
+# ONLY use this if your saned server is sitting behind a firewall. If your
+# firewall is a Linux machine, we strongly recommend using the
+# Netfilter nf_conntrack_sane connection tracking module instead.
+#
+# data_portrange = 10000 - 10100
+
+
+## Access list
+# A list of host names, IP addresses or IP subnets (CIDR notation) that
+# are permitted to use local SANE devices. IPv6 addresses must be enclosed
+# in brackets, and should always be specified in their compressed form.
+#
+# The hostname matching is not case-sensitive.
+
+#scan-client.somedomain.firm
+#192.168.0.1
+#192.168.0.1/29
+#[2001:db8:185e::42:12]
+#[2001:db8:185e::42:12]/64
+
+# NOTE: /etc/inetd.conf (or /etc/xinetd.conf) and
+# /etc/services must also be properly configured to start
+# the saned daemon as documented in saned(8), services(4)
+# and inetd.conf(4) (or xinetd.conf(5)).
diff --git a/backend/sceptre.c b/backend/sceptre.c
new file mode 100644
index 0000000..fa0da20
--- /dev/null
+++ b/backend/sceptre.c
@@ -0,0 +1,2094 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+ Copyright (C) 2002 Other SANE contributors
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ Sceptre S1200 SCSI scanner (sometimes also called S120)
+*/
+
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 10 /* 2002-03-21 */
+#define BACKEND_NAME sceptre
+#define SCEPTRE_CONFIG_FILE "sceptre.conf"
+
+/*--------------------------------------------------------------------------*/
+
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "sceptre.h"
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_String scan_mode_list[] = { LINEART_STR, HALFTONE_STR,
+ GRAY_STR, COLOR_STR, NULL
+};
+
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range threshold_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range halftone_range = {
+ 1, /* minimum */
+ 4, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+#define NUM_OF_RES 15
+/* Table of supported resolution and number of lines of color shifting. */
+static const SANE_Word resolutions_list[NUM_OF_RES + 1] = {
+ NUM_OF_RES, 10, 25, 30, 45, 75, 90, 150, 300, 450, 600, 750, 900, 1050,
+ 1125, 1200
+};
+
+static const SANE_Word color_shift_list[NUM_OF_RES + 1] = {
+ NUM_OF_RES, 0, 0, 0, 0, 1, 1, 2, 4, 6, 8, 10, 12, 14, 15, 16
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define the supported scanners and their characteristics. */
+static const struct scanners_supported scanners[] = {
+ /* { 6, "KINPO ", "Vividscan S600 ", "KINPO", "S600" }, */
+ {6, "KINPO ", "Vividscan S120 ", "Sceptre", "S1200"}
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of scanner attached. */
+static Sceptre_Scanner *first_dev = NULL;
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+
+
+/* Local functions. */
+
+/* Display a buffer in the log. */
+static void
+hexdump (int level, const char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/* Initialize a scanner entry. Return an allocated scanner with some
+ * preset values. */
+static Sceptre_Scanner *
+sceptre_init (void)
+{
+ Sceptre_Scanner *dev;
+
+ DBG (DBG_proc, "sceptre_init: enter\n");
+
+ /* Allocate a new scanner entry. */
+ dev = malloc (sizeof (Sceptre_Scanner));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ memset (dev, 0, sizeof (Sceptre_Scanner));
+
+ /* Allocate the buffer used to transfer the SCSI data. */
+ dev->buffer_size = 64 * 1024;
+ dev->buffer = malloc (dev->buffer_size);
+ if (dev->buffer == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+ dev->sfd = -1;
+
+ DBG (DBG_proc, "sceptre_init: exit\n");
+
+ return (dev);
+}
+
+/* Closes an open scanner. */
+static void
+sceptre_close (Sceptre_Scanner * dev)
+{
+ DBG (DBG_proc, "sceptre_close: enter\n");
+
+ if (dev->sfd != -1)
+ {
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+ }
+
+ DBG (DBG_proc, "sceptre_close: exit\n");
+}
+
+/* Frees the memory used by a scanner. */
+static void
+sceptre_free (Sceptre_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "sceptre_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ sceptre_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+
+ free (dev);
+
+ DBG (DBG_proc, "sceptre_free: exit\n");
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+sceptre_identify_scanner (Sceptre_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "sceptre_identify_scanner: enter\n");
+
+ size = 36;
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "sceptre_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ if (size < 36)
+ {
+ DBG (DBG_error,
+ "sceptre_identify_scanner: not enough data to identify device\n");
+ return (SANE_FALSE);
+ }
+
+ dev->scsi_type = dev->buffer[0] & 0x1f;
+ memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08);
+ dev->scsi_vendor[0x08] = 0;
+ memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010);
+ dev->scsi_product[0x10] = 0;
+ memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04);
+ dev->scsi_version[0x04] = 0;
+
+ DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\"\n",
+ dev->scsi_vendor, dev->scsi_product, dev->scsi_version);
+
+ /* Lookup through the supported scanners table to find if this
+ * backend supports that one. */
+ for (i = 0; i < NELEMS (scanners); i++)
+ {
+ if (dev->scsi_type == scanners[i].scsi_type &&
+ strcmp (dev->scsi_vendor, scanners[i].scsi_vendor) == 0 &&
+ strcmp (dev->scsi_product, scanners[i].scsi_product) == 0)
+ {
+
+ DBG (DBG_error, "sceptre_identify_scanner: scanner supported\n");
+
+ dev->scnum = i;
+
+ return (SANE_TRUE);
+ }
+ }
+
+ DBG (DBG_proc, "sceptre_identify_scanner: exit\n");
+
+ return (SANE_FALSE);
+}
+
+/* Return the number of bytes left to read. */
+static SANE_Status
+sceptre_get_status (Sceptre_Scanner * dev, size_t * data_left)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sceptre_get_status: enter\n");
+
+ /* Get status. */
+ size = 0x10;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sceptre_get_status: cannot get buffer status\n");
+ *data_left = 0;
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (size != 16)
+ {
+ DBG (DBG_error,
+ "sceptre_get_status: invalid data size returned (%ld)\n",
+ (long) size);
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ hexdump (DBG_info2, "GET BUFFER STATUS result", dev->buffer, 16);
+
+ /* Read the size left. The scanner returns the rest of the
+ * bytes to read, not just what's in its buffers. */
+ *data_left = B32TOI (&dev->buffer[8]);
+
+ if (dev->raster_real == 0)
+ {
+ /* First call. Set the correct parameters. */
+ dev->raster_real = B16TOI (&dev->buffer[12]) * 3;
+ dev->params.lines = B16TOI (&dev->buffer[12]);
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ }
+
+ DBG (DBG_proc, "sceptre_get_status: exit, data_left=%ld\n",
+ (long) *data_left);
+
+ return (SANE_STATUS_GOOD);
+}
+
+/*
+ * Adjust the rasters. This function is used during a color scan,
+ * because the scanner does not present a format sane can interpret
+ * directly.
+ *
+ * The scanner sends the colors by rasters (R then G then B), whereas
+ * sane is waiting for a group of 3 bytes per color. To make things
+ * funnier, the rasters are shifted. This shift factor depends on the
+ * resolution used. The format of those raster is:
+ * R...R RG...RG RGB...RGB BG...GB B...B
+ *
+ * So this function reorders all that mess. It gets the input from
+ * dev->buffer and write the output in dev->image. size_in the the
+ * length of the valid data in dev->buffer.
+ */
+static void
+sceptre_adjust_raster (Sceptre_Scanner * dev, size_t size_in)
+{
+ int nb_rasters; /* number of rasters in dev->buffer */
+
+ int raster; /* current raster number in buffer */
+ int line; /* line number for that raster */
+ int colour; /* colour for that raster */
+ size_t offset;
+
+ DBG (DBG_proc, "sceptre_adjust_raster: enter\n");
+
+ assert (dev->scan_mode == SCEPTRE_COLOR);
+ assert ((size_in % dev->params.bytes_per_line) == 0);
+
+ if (size_in == 0)
+ {
+ return;
+ }
+
+ /*
+ * The color coding is one line for each color (in the RGB order).
+ * Recombine that stuff to create a RGB value for each pixel.
+ */
+
+ nb_rasters = size_in / dev->raster_size;
+
+ for (raster = 0; raster < nb_rasters; raster++)
+ {
+
+ /*
+ * Find the color to which this raster belongs to.
+ * 0 = red
+ * 1 = green
+ * 2 = blue
+ *
+ * When blue comes, it always finishes the current line;
+ */
+ line = 0;
+ if (dev->raster_num < dev->color_shift)
+ {
+ colour = 0; /* Red */
+ line = dev->raster_num;
+ }
+ else if (dev->raster_num < (3 * dev->color_shift))
+ {
+ /* even = red, odd = green */
+ colour = (dev->raster_num - dev->color_shift) % 2;
+ if (colour)
+ {
+ /* Green */
+ line = (dev->raster_num - dev->color_shift) / 2;
+ }
+ else
+ {
+ /* Red */
+ line = (dev->raster_num + dev->color_shift) / 2;
+ }
+ }
+ else if (dev->raster_num >= dev->raster_real - dev->color_shift)
+ {
+ /* Blue */
+ colour = 2;
+ line = dev->line;
+ }
+ else if (dev->raster_num >= dev->raster_real - 3 * dev->color_shift)
+ {
+ /* Green or Blue */
+ colour =
+ (dev->raster_real - dev->raster_num - dev->color_shift) % 2 + 1;
+ if (colour == 1)
+ {
+ /* Green */
+ line = dev->line + dev->color_shift;
+ }
+ else
+ {
+ /* Blue */
+ line = dev->line;
+ }
+ }
+ else
+ {
+ colour = (dev->raster_num - 3 * dev->color_shift) % 3;
+ switch (colour)
+ {
+ case 0:
+ /* Red */
+ line = (dev->raster_num + 3 * dev->color_shift) / 3;
+ break;
+ case 1:
+ /* Green */
+ line = dev->raster_num / 3;
+ break;
+ case 2:
+ /* Blue */
+ line = (dev->raster_num - 3 * dev->color_shift) / 3;
+ break;
+ }
+ }
+
+ /* Adjust the line number relative to the image. */
+ line -= dev->line;
+
+ offset = dev->image_end + line * dev->params.bytes_per_line;
+
+ assert (offset <= (dev->image_size - dev->raster_size));
+
+ /* Copy the raster to the temporary image. */
+ {
+ int i;
+ unsigned char *src = dev->buffer + raster * dev->raster_size;
+ unsigned char *dest = dev->image + offset + colour;
+
+ for (i = 0; i < dev->raster_size; i++)
+ {
+ *dest = *src;
+ src++;
+ dest += 3;
+ }
+ }
+
+ if (colour == 2)
+ {
+ /* This blue raster completes a new line */
+ dev->line++;
+ dev->image_end += dev->params.bytes_per_line;
+ }
+
+ dev->raster_num++;
+ }
+
+ DBG (DBG_proc, "sceptre_adjust_raster: exit\n");
+}
+
+/* SCSI sense handler. Callback for SANE.
+ *
+ * Since this scanner does not have REQUEST SENSE, it is always an
+ * error if this function is called.*/
+static SANE_Status
+sceptre_sense_handler (int scsi_fd, unsigned char __sane_unused__ *result, void __sane_unused__ *arg)
+{
+ DBG (DBG_proc, "sceptre_sense_handler (scsi_fd = %d)\n", scsi_fd);
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Attach a scanner to this backend. */
+static SANE_Status
+attach_scanner (const char *devicename, Sceptre_Scanner ** devp)
+{
+ Sceptre_Scanner *dev;
+ int sfd;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new scanner entry. */
+ dev = sceptre_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+ if (sanei_scsi_open (devicename, &sfd, sceptre_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_scanner: open failed\n");
+ sceptre_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Now, check that it is a scanner we support. */
+ if (sceptre_identify_scanner (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ sceptre_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ sceptre_close (dev);
+
+ /* Set the default options for that scanner. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = scanners[dev->scnum].real_vendor;
+ dev->sane.model = scanners[dev->scnum].real_product;
+ dev->sane.type = SANE_I18N ("flatbed scanner");
+
+ dev->resolution_range.min = SANE_FIX (50);
+ dev->resolution_range.max = SANE_FIX (1200);
+ dev->resolution_range.quant = SANE_FIX (1);
+
+ /*
+ * The S1200 has an area of 8.5 inches / 11.7 inches. (A4 like)
+ * That's roughly 215*297 mm
+ * The values are coded by
+ * size in inch * 600 dpi.
+ * The maximums are:
+ * X: 8.5 inches * 600 = 5100 dots
+ * Y: 11.7 inches * 600 = 7020
+ * (although the windows driver stops at 7019)
+ *
+ * The values are stored in mm. Inches sucks anyway.
+ * X: 5078 dots (22 dots lost)
+ * Y: 7015 dots (5 dots lost)
+ *
+ * There seems to be a minimum area, but yet to be determined.
+ */
+ dev->x_range.min = SANE_FIX (0);
+ dev->x_range.max = SANE_FIX (215.90); /* in mm */
+ dev->x_range.quant = 0;
+
+ dev->y_range.min = SANE_FIX (0);
+ dev->y_range.max = SANE_FIX (297.14); /* in mm */
+ dev->y_range.quant = SANE_FIX (0);
+
+ /* Link the scanner with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_scanner: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that scanner. */
+static void
+sceptre_init_options (Sceptre_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "sceptre_init_options: enter\n");
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = 30; /* should define yet another max_string_size() */
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list =
+ (SANE_String_Const *) scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (scan_mode_list[0]);
+
+ /* Common resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list = resolutions_list;
+ dev->val[OPT_RESOLUTION].w = 150;
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &(dev->x_range);
+ dev->val[OPT_TL_X].w = dev->x_range.min;
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &(dev->y_range);
+ dev->val[OPT_TL_Y].w = dev->y_range.min;
+
+ /* bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &(dev->x_range);
+ dev->val[OPT_BR_X].w = dev->x_range.max;
+
+ /* bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &(dev->y_range);
+ dev->val[OPT_BR_Y].w = dev->y_range.max;
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* custom-gamma table */
+ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R;
+
+ /* green gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G;
+
+ /* blue gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B;
+
+ /* Threshold */
+ dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_THRESHOLD].size = sizeof (SANE_Int);
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_THRESHOLD].constraint.range = &threshold_range;
+ dev->val[OPT_THRESHOLD].w = 128;
+
+ /* Halftone pattern */
+ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT;
+ dev->opt[OPT_HALFTONE_PATTERN].size = sizeof (SANE_Int);
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_HALFTONE_PATTERN].constraint.range = &halftone_range;
+ dev->val[OPT_HALFTONE_PATTERN].w = 1;
+
+ /* preview */
+ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Lastly, set the default mode. This might change some values
+ * previously set here. */
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String *) COLOR_STR, NULL);
+
+ DBG (DBG_proc, "sceptre_init_options: leave\n");
+}
+
+/* Wait until the scanner is ready.
+ *
+ * The only reason I know the scanner is not ready is because it is
+ * moving the CCD.
+ */
+static SANE_Status
+sceptre_wait_scanner (Sceptre_Scanner * dev)
+{
+ SANE_Status status;
+ int timeout;
+ CDB cdb;
+ size_t size;
+
+ DBG (DBG_proc, "sceptre_wait_scanner: enter\n");
+
+ MKSCSI_TEST_UNIT_READY (cdb);
+ cdb.data[4] = 1; /* returns one byte. Non standard SCSI. */
+
+ /* Set the timeout to 120 seconds. */
+ timeout = 120;
+
+ while (timeout > 0)
+ {
+
+ /* test unit ready */
+ size = 1; /* read one info byte */
+ status =
+ sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD || size != 1)
+ {
+ DBG (DBG_error, "sceptre_wait_scanner: TUR failed\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ /* Apparently the scanner returns only 2 values:
+ * 0x00 - ready
+ * 0xff - not ready
+ */
+ if (dev->buffer[0] != 0x00)
+ {
+ sleep (1); /* wait 1 seconds */
+ timeout--;
+ }
+ else
+ {
+ return (SANE_STATUS_GOOD);
+ }
+ };
+
+ DBG (DBG_proc, "sceptre_wait_scanner: scanner not ready\n");
+ return (SANE_STATUS_IO_ERROR);
+}
+
+/* Diagnostic the scanner. */
+static SANE_Status
+sceptre_do_diag (Sceptre_Scanner * dev)
+{
+ SANE_Status status;
+ CDB cdb;
+ size_t size;
+
+ DBG (DBG_proc, "sceptre_receive_diag enter\n");
+
+ /* SEND DIAGNOSTIC. */
+ MKSCSI_SEND_DIAG (cdb, 0);
+
+ /* The windows driver sets that field. This is non standard. */
+ cdb.data[2] = 0x80;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sceptre_do_diag: exit, status=%d\n", status);
+ return (status);
+ }
+
+ /* RECEIVE DIAGNOSTIC */
+
+ /* The windows driver ask for 3 byte. This is non standard
+ * SCSI. The page returned should be at least 4 bytes. */
+ size = 3;
+ MKSCSI_RECEIVE_DIAG (cdb, 0, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sceptre_do_diag: exit, status=%d\n", status);
+ return (status);
+ }
+
+ DBG (DBG_proc, "sceptre_receive_diag exit\n");
+
+ return (status);
+}
+
+/* I'm not sure if the command sent is really set mode. The SCSI
+ * command used is MODE SELECT, but no data is sent. Again, this is
+ * not standard. */
+static SANE_Status
+sceptre_set_mode (Sceptre_Scanner * dev)
+{
+ SANE_Status status;
+ CDB cdb;
+ size_t size;
+
+ DBG (DBG_proc, "sceptre_set_mode: enter\n");
+
+ size = 0x18;
+ MKSCSI_MODE_SELECT (cdb, 1, 0, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "sceptre_set_mode: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Start a scan. */
+static SANE_Status
+sceptre_scan (Sceptre_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sceptre_scan: enter\n");
+
+ MKSCSI_SCAN (cdb);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "sceptre_scan: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Set a window. */
+static SANE_Status
+sceptre_set_window (Sceptre_Scanner * dev)
+{
+ size_t size;
+ CDB cdb;
+ unsigned char window[82];
+ SANE_Status status;
+
+ DBG (DBG_proc, "sceptre_set_window: enter\n");
+
+ size = sizeof (window);
+ MKSCSI_SET_WINDOW (cdb, size);
+
+ memset (window, 0, size);
+
+ /* size of the parameters (74 = 0x4a bytes) */
+ window[7] = sizeof (window) - 8;
+
+ /* X and Y resolution */
+ Ito16 (dev->resolution, &window[10]);
+ Ito16 (dev->resolution, &window[12]);
+
+ /* Upper Left (X,Y) */
+ Ito32 (dev->x_tl, &window[14]);
+ Ito32 (dev->y_tl, &window[18]);
+
+ /* Width and length */
+ Ito32 (dev->width, &window[22]);
+ Ito32 (dev->length, &window[26]);
+
+ /* Image Composition, Halftone and Depth */
+ switch (dev->scan_mode)
+ {
+ case SCEPTRE_LINEART:
+ window[31] = dev->val[OPT_THRESHOLD].w;
+ window[33] = 0;
+ window[34] = 1;
+ window[36] = 0;
+ break;
+ case SCEPTRE_HALFTONE:
+ window[31] = 0x80;
+ window[33] = 0;
+ window[34] = 1;
+ window[36] = dev->val[OPT_HALFTONE_PATTERN].w;
+ break;
+ case SCEPTRE_GRAYSCALE:
+ window[31] = 0x80;
+ window[33] = 2;
+ window[34] = 8;
+ window[36] = 0;
+ break;
+ case SCEPTRE_COLOR:
+ window[31] = 0x80;
+ window[33] = 5;
+ window[34] = 24;
+ window[36] = 0;
+ break;
+ }
+
+ /* Unknown parameters. They look constant in the windows driver. */
+ window[30] = 0x04;
+ window[32] = 0x04;
+ window[37] = 0x80; /* RIF, although it looks unused. */
+
+ hexdump (DBG_info2, "windows", window, sizeof (window));
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ window, sizeof (window), NULL, NULL);
+
+ DBG (DBG_proc, "sceptre_set_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Read the image from the scanner and fill the temporary buffer with it. */
+static SANE_Status
+sceptre_fill_image (Sceptre_Scanner * dev)
+{
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+ size_t data_left;
+
+ DBG (DBG_proc, "sceptre_fill_image: enter\n");
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ /* Copy the complete lines, plus the imcompletes
+ * ones. We don't keep the real end of data used
+ * in image, so we copy the biggest possible.
+ *
+ * This is a no-op for non color images.
+ */
+ memmove (dev->image, dev->image + dev->image_begin, dev->raster_ahead);
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ while (dev->real_bytes_left)
+ {
+
+ if ((status = sceptre_get_status (dev, &data_left)) != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+
+ /*
+ * Try to read the maximum number of bytes.
+ */
+ size = data_left;
+ if (size > dev->real_bytes_left)
+ {
+ size = dev->real_bytes_left;
+ }
+ if (size > dev->image_size - dev->raster_ahead - dev->image_end)
+ {
+ size = dev->image_size - dev->raster_ahead - dev->image_end;
+ }
+ if (size > dev->buffer_size)
+ {
+ size = dev->buffer_size;
+ }
+
+ /* Round down to a multiple of line size. */
+ size = size - (size % dev->params.bytes_per_line);
+
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_info, "sceptre_fill_image: to read = %ld bytes (bpl=%d)\n",
+ (long) size, dev->params.bytes_per_line);
+
+ MKSCSI_READ_10 (cdb, 0, 0, size);
+
+ hexdump (DBG_info2, "sceptre_fill_image: READ_10 CDB", cdb.data, 10);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sceptre_fill_image: cannot read from the scanner\n");
+ return status;
+ }
+
+ DBG (DBG_info, "sceptre_fill_image: real bytes left = %ld\n",
+ (long)dev->real_bytes_left);
+
+ switch (dev->scan_mode)
+ {
+ case SCEPTRE_COLOR:
+ sceptre_adjust_raster (dev, size);
+ break;
+ case SCEPTRE_LINEART:
+ case SCEPTRE_HALFTONE:
+ {
+ /* Invert black and white. */
+ unsigned char *src = dev->buffer;
+ unsigned char *dest = dev->image + dev->image_end;
+ size_t i;
+ for (i = 0; i < size; i++)
+ {
+ *dest = *src ^ 0xff;
+ dest++;
+ src++;
+ }
+ dev->image_end += size;
+ }
+ break;
+ default:
+ memcpy (dev->image + dev->image_end, dev->buffer, size);
+ dev->image_end += size;
+ }
+
+ dev->real_bytes_left -= size;
+ }
+
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+/* Copy from the raw buffer to the buffer given by the backend.
+ *
+ * len in input is the maximum length available in buf, and, in
+ * output, is the length written into buf.
+ */
+static void
+sceptre_copy_raw_to_frontend (Sceptre_Scanner * dev, SANE_Byte * buf,
+ size_t * len)
+{
+ size_t size;
+
+ size = dev->image_end - dev->image_begin;
+ if (size > *len)
+ {
+ size = *len;
+ }
+ *len = size;
+
+ memcpy (buf, dev->image + dev->image_begin, size);
+
+ dev->image_begin += size;
+}
+
+/* Stop a scan. */
+static SANE_Status
+do_cancel (Sceptre_Scanner * dev)
+{
+ DBG (DBG_sane_proc, "do_cancel enter\n");
+
+ if (dev->scanning == SANE_TRUE)
+ {
+
+ /* Reposition the CCD. */
+ dev->x_tl = 0;
+ dev->x_tl = 0;
+ dev->width = 0;
+ dev->length = 0;
+ sceptre_set_window (dev);
+ sceptre_scan (dev);
+
+ sceptre_close (dev);
+ }
+
+ dev->scanning = SANE_FALSE;
+
+ DBG (DBG_sane_proc, "do_cancel exit\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/* Start a scan. */
+static const SANE_Word gamma_init[GAMMA_LENGTH] = {
+ 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,
+ 0x21, 0x23, 0x25, 0x27,
+ 0x28, 0x2A, 0x2C, 0x2D, 0x2F, 0x30, 0x32, 0x33, 0x35, 0x36, 0x38, 0x39,
+ 0x3A, 0x3C, 0x3D, 0x3F,
+ 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4D, 0x4E,
+ 0x4F, 0x50, 0x51, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60,
+ 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x80,
+ 0x81, 0x82, 0x83, 0x84,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x92,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
+ 0x9E, 0x9F, 0x9F, 0xA0,
+ 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xAA,
+ 0xAB, 0xAC, 0xAD, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB1, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB9, 0xB9, 0xBA,
+ 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, 0xC3,
+ 0xC4, 0xC5, 0xC6, 0xC6,
+ 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD2,
+ 0xD3, 0xD4, 0xD5, 0xD5, 0xD6, 0xD7, 0xD7, 0xD8, 0xD9, 0xDA, 0xDA, 0xDB,
+ 0xDC, 0xDC, 0xDD, 0xDE,
+ 0xDF, 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE4, 0xE4, 0xE5, 0xE6, 0xE6,
+ 0xE7, 0xE8, 0xE8, 0xE9,
+ 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1,
+ 0xF2, 0xF3, 0xF4, 0xF4,
+ 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC,
+ 0xFD, 0xFE, 0xFE, 0xFF
+};
+
+static SANE_Status
+sceptre_send_gamma (Sceptre_Scanner * dev)
+{
+ CDB cdb;
+ int i;
+ struct
+ {
+ unsigned char gamma_R[GAMMA_LENGTH];
+ unsigned char gamma_G[GAMMA_LENGTH];
+ unsigned char gamma_B[GAMMA_LENGTH];
+ }
+ param;
+ size_t size;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sceptre_send_gamma: enter\n");
+
+ size = sizeof (param);
+
+ assert (size == 0x300);
+
+ MKSCSI_SEND_10 (cdb, 0x03, 0x02, size);
+
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* Use the custom gamma. */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = dev->gamma_R[i];
+ param.gamma_G[i] = dev->gamma_G[i];
+ param.gamma_B[i] = dev->gamma_B[i];
+ }
+ }
+ else
+ {
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = gamma_init[i];
+ param.gamma_G[i] = gamma_init[i];
+ param.gamma_B[i] = gamma_init[i];
+ }
+ }
+
+ hexdump (DBG_info2, "gamma", param.gamma_R, 3 * GAMMA_LENGTH);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ &param, sizeof (param), NULL, NULL);
+
+ DBG (DBG_proc, "sceptre_send_gamma: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ char dev_name[PATH_MAX];
+ size_t len;
+
+ DBG_INIT ();
+
+ DBG (DBG_proc, "sane_init: enter\n");
+
+ DBG (DBG_error, "This is sane-sceptre version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2002 by Frank Zago\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ fp = sanei_config_open (SCEPTRE_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach_scanner ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Sceptre_Scanner *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Sceptre_Scanner *dev;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No scanner found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ sceptre_init_options (dev);
+
+ /* Initialize the gamma table. */
+ memcpy (dev->gamma_R, gamma_init, dev->opt[OPT_GAMMA_VECTOR_R].size);
+ memcpy (dev->gamma_G, gamma_init, dev->opt[OPT_GAMMA_VECTOR_G].size);
+ memcpy (dev->gamma_B, gamma_init, dev->opt[OPT_GAMMA_VECTOR_B].size);
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Sceptre_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Sceptre_Scanner *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_THRESHOLD:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_PREVIEW:
+
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, dev->val[option].wa, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Side-effect options */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_RESOLUTION:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Side-effect free options */
+ case OPT_THRESHOLD:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_PREVIEW:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ /* Set default options for the scan modes. */
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, LINEART_STR) == 0)
+ {
+ dev->scan_mode = SCEPTRE_LINEART;
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, HALFTONE_STR) == 0)
+ {
+ dev->scan_mode = SCEPTRE_HALFTONE;
+ dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, GRAY_STR) == 0)
+ {
+ dev->scan_mode = SCEPTRE_GRAYSCALE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_STR) == 0)
+ {
+ dev->scan_mode = SCEPTRE_COLOR;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (dev->val[option].wa, val, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* use custom_gamma_table */
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Sceptre_Scanner *dev = handle;
+ int x_dpi; /* X-Resolution */
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ if (dev->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ dev->resolution = 30; /* Windows TWAIN does 32 */
+ dev->x_tl = 0;
+ dev->y_tl = 0;
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->x_range.max));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->y_range.max));
+ }
+ else
+ {
+ /* Setup the parameters for the scan. These values will be re-used
+ * in the SET WINDOWS command. */
+ dev->resolution = dev->val[OPT_RESOLUTION].w;
+
+ dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ }
+
+ /* Check the corners are OK. */
+ if (dev->x_tl > dev->x_br)
+ {
+ int s;
+ s = dev->x_tl;
+ dev->x_tl = dev->x_br;
+ dev->x_br = s;
+ }
+ if (dev->y_tl > dev->y_br)
+ {
+ int s;
+ s = dev->y_tl;
+ dev->y_tl = dev->y_br;
+ dev->y_br = s;
+ }
+
+ dev->width = dev->x_br - dev->x_tl;
+ dev->length = dev->y_br - dev->y_tl;
+
+ /*
+ * Adjust the "X Resolution". The sceptre S1200 ignores the
+ * Y-Resolution parameter in the windows block. X-Resolution
+ * is used instead. However the limits are not the same for X
+ * (600 dpi) and Y (1200 dpi).
+ */
+ x_dpi = dev->resolution;
+ if (x_dpi > 600)
+ {
+ x_dpi = 600;
+ }
+
+ /* Set depth */
+ switch (dev->scan_mode)
+ {
+ case SCEPTRE_LINEART:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->depth = 1;
+ break;
+ case SCEPTRE_HALFTONE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->depth = 1;
+ break;
+ case SCEPTRE_GRAYSCALE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->depth = 8;
+ break;
+ case SCEPTRE_COLOR:
+ dev->params.format = SANE_FRAME_RGB;
+ dev->depth = 8;
+ break;
+ }
+
+ /* this scanner does only one pass */
+ dev->params.last_frame = SANE_TRUE;
+ dev->params.depth = dev->depth;
+
+ /* Compute the number of pixels, bytes per lines and lines. */
+ switch (dev->scan_mode)
+ {
+ case SCEPTRE_LINEART:
+ case SCEPTRE_HALFTONE:
+ dev->params.pixels_per_line = (dev->width * x_dpi) / 600;
+ dev->params.pixels_per_line &= ~0x7; /* round down to 8 */
+
+ dev->params.bytes_per_line = (dev->params.pixels_per_line) / 8;
+
+ dev->params.lines = ((dev->length * dev->resolution) / 600);
+ if ((dev->params.lines) * 600 != (dev->length * dev->resolution))
+ {
+ /* Round up lines to 2. */
+ dev->params.lines &= ~1;
+ dev->params.lines += 2;
+ }
+
+ break;
+
+ case SCEPTRE_GRAYSCALE:
+ case SCEPTRE_COLOR:
+ /* pixels_per_line rounding rules:
+ * 2n + [0.0 .. 1.0] -> round to 2n
+ * 2n + ]1.0 .. 2.0] -> round to 2n + 2
+ */
+ dev->params.pixels_per_line = (dev->width * x_dpi) / 600;
+ if (dev->params.pixels_per_line & 1)
+ {
+ if ((dev->params.pixels_per_line * 600) == (dev->width * x_dpi))
+ {
+ /* 2n */
+ dev->params.pixels_per_line--;
+ }
+ else
+ {
+ /* 2n+2 */
+ dev->params.pixels_per_line++;
+ }
+ }
+
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ if (dev->scan_mode == SCEPTRE_COLOR)
+ dev->params.bytes_per_line *= 3;
+
+ /* lines number rounding rules:
+ * 2n + [0.0 .. 2.0[ -> round to 2n
+ *
+ * Note: the rounding is often incorrect at high
+ * resolution (ag more than 300dpi)
+ */
+ dev->params.lines = (dev->length * dev->resolution) / 600;
+ dev->params.lines &= ~1;
+
+ break;
+ }
+
+ /* Find the proper color shifting parameter. */
+ if (dev->scan_mode == SCEPTRE_COLOR)
+ {
+ int i = 1;
+ while (resolutions_list[i] != dev->resolution)
+ {
+ i++;
+ }
+ dev->color_shift = color_shift_list[i];
+ }
+ else
+ {
+ dev->color_shift = 0;
+ }
+
+ DBG (DBG_proc, "color_shift = %d\n", dev->color_shift);
+
+ dev->bytes_left = dev->params.lines * dev->params.bytes_per_line;
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Sceptre_Scanner *dev = handle;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ sane_get_parameters (dev, NULL);
+
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ /* Compute the length necessary in image. The first part will store
+ * the complete lines, and the rest is used to stored ahead
+ * rasters.
+ */
+ dev->raster_ahead =
+ (2 * dev->color_shift + 1) * dev->params.bytes_per_line;
+ dev->image_size = dev->buffer_size + dev->raster_ahead;
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ dev->raster_size = dev->params.bytes_per_line / 3;
+ dev->raster_num = 0;
+ dev->raster_real = 0;
+ dev->line = 0;
+
+ /* Open again the scanner. */
+ if (sanei_scsi_open
+ (dev->devicename, &(dev->sfd), sceptre_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* The scanner must be ready. */
+ status = sceptre_wait_scanner (dev);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ status = sceptre_do_diag (dev);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ status = sceptre_set_mode (dev);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ status = sceptre_set_window (dev);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ status = sceptre_send_gamma (dev);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ status = sceptre_scan (dev);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ status = sceptre_get_status (dev, &dev->real_bytes_left);
+ if (status)
+ {
+ sceptre_close (dev);
+ return status;
+ }
+
+ }
+
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Sceptre_Scanner *dev = handle;
+ size_t size;
+ int buf_offset; /* offset into buf */
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning */
+ return do_cancel (dev);
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ buf_offset = 0;
+
+ do
+ {
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = sceptre_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Copy the data to the frontend buffer. */
+ size = max_len - buf_offset;
+ if (size > dev->bytes_left)
+ {
+ size = dev->bytes_left;
+ }
+ sceptre_copy_raw_to_frontend (dev, buf + buf_offset, &size);
+
+ buf_offset += size;
+
+ dev->bytes_left -= size;
+ *len += size;
+
+ }
+ while ((buf_offset != max_len) && dev->bytes_left);
+
+ DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long)dev->bytes_left);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ SANE_Status status;
+ Sceptre_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ if (dev->scanning == SANE_FALSE)
+ {
+ return (SANE_STATUS_INVAL);
+ }
+
+ if (non_blocking == SANE_FALSE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Sceptre_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ do_cancel (dev);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Sceptre_Scanner *dev = handle;
+ Sceptre_Scanner *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+ do_cancel (dev);
+ sceptre_close (dev);
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ sceptre_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/sceptre.conf.in b/backend/sceptre.conf.in
new file mode 100644
index 0000000..69e3a96
--- /dev/null
+++ b/backend/sceptre.conf.in
@@ -0,0 +1,2 @@
+scsi "KINPO " "Vividscan S120 "
+/dev/scanner
diff --git a/backend/sceptre.h b/backend/sceptre.h
new file mode 100644
index 0000000..726c131
--- /dev/null
+++ b/backend/sceptre.h
@@ -0,0 +1,414 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+*/
+
+/* Commands supported by the Sceptre S1200 scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_GET_STATUS 0x02
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT 0x15
+#define SCSI_MODE_SENSE 0x1a
+#define SCSI_SCAN 0x1b
+#define SCSI_RECEIVE_DIAG 0x1c
+#define SCSI_SEND_DIAG 0x1D
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_READ_10 0x28
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_SEND_10 0x2a
+#define SCSI_GET_DATA_BUFFER_STATUS 0x34
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+}
+CDB;
+
+
+/* Set a specific bit depending on a boolean.
+ * MKSCSI_BIT(TRUE, 3) will generate 0x08. */
+#define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0)
+
+/* Set a value in a range of bits.
+ * MKSCSI_I2B(5, 3, 5) will generate 0x28 */
+#define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1))
+
+/* Store an integer in 2, 3 or 4 byte in an array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+#define MKSCSI_TEST_UNIT_READY(cdb) \
+ cdb.data[0] = SCSI_TEST_UNIT_READY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_GET_STATUS(cdb, buflen) \
+ cdb.data[0] = SCSI_GET_STATUS; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_INQUIRY(cdb, buflen) \
+ cdb.data[0] = SCSI_INQUIRY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_MODE_SELECT(cdb, pf, sp, buflen) \
+ cdb.data[0] = SCSI_MODE_SELECT; \
+ cdb.data[1] = MKSCSI_BIT(pf, 4) | MKSCSI_BIT(sp, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_MODE_SENSE(cdb, pc, page_code, buflen) \
+ cdb.data[0] = SCSI_MODE_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = MKSCSI_I2B(pc, 6, 7) | MKSCSI_I2B(page_code, 0, 5); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SCAN(cdb) \
+ cdb.data[0] = SCSI_SCAN; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_RECEIVE_DIAG(cdb, pc, buflen) \
+ cdb.data[0] = SCSI_RECEIVE_DIAG; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = pc; \
+ cdb.data[3] = (((buflen) >> 8) & 0xff); \
+ cdb.data[4] = (((buflen) >> 0) & 0xff); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SEND_DIAG(cdb, buflen) \
+ cdb.data[0] = SCSI_SEND_DIAG; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = (((buflen) >> 8) & 0xff); \
+ cdb.data[4] = (((buflen) >> 0) & 0xff); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SET_WINDOW(cdb, buflen) \
+ cdb.data[0] = SCSI_SET_WINDOW; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_READ_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_REQUEST_SENSE(cdb, buflen) \
+ cdb.data[0] = SCSI_REQUEST_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (buflen); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SEND_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_SEND_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_GET_DATA_BUFFER_STATUS(cdb, wait, buflen) \
+ cdb.data[0] = SCSI_GET_DATA_BUFFER_STATUS; \
+ cdb.data[1] = MKSCSI_BIT(wait, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+/*--------------------------------------------------------------------------*/
+
+#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 600))
+#define mmToIlu(mm) ((mm) / length_quant)
+#define iluToMm(ilu) ((ilu) * length_quant)
+
+/*--------------------------------------------------------------------------*/
+
+#define GAMMA_LENGTH 0x100 /* number of value per color */
+
+/*--------------------------------------------------------------------------*/
+
+enum Sceptre_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CUSTOM_GAMMA, /* Use the custom gamma tables */
+ OPT_GAMMA_VECTOR_R, /* Custom Red gamma table */
+ OPT_GAMMA_VECTOR_G, /* Custom Green Gamma table */
+ OPT_GAMMA_VECTOR_B, /* Custom Blue Gamma table */
+ OPT_THRESHOLD, /* Threshold */
+ OPT_HALFTONE_PATTERN, /* Halftone pattern (1 to 4) */
+
+ OPT_PREVIEW, /* preview mode */
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Scanner supported by this backend.
+ */
+struct scanners_supported
+{
+ /* From scsi inquiry */
+ int scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+
+ char *real_vendor;
+ char *real_product;
+};
+
+/*--------------------------------------------------------------------------*/
+
+#define LINEART_STR SANE_VALUE_SCAN_MODE_LINEART
+#define HALFTONE_STR SANE_VALUE_SCAN_MODE_HALFTONE
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+/*--------------------------------------------------------------------------*/
+
+/* Define a scanner occurence. */
+typedef struct Sceptre_Scanner
+{
+ struct Sceptre_Scanner *next;
+ SANE_Device sane;
+
+ char *devicename;
+ int sfd; /* device handle */
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+ char scsi_version[5];
+
+ /* Scanner infos. */
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range resolution_range;
+ int scnum; /* index of that scanner in
+ * scanners_supported */
+
+ /* SCSI handling */
+ SANE_Byte *buffer; /* for SCSI transfer. */
+ size_t buffer_size; /* allocated size of buffer */
+
+ /* Scanning handling. */
+ int scanning; /* TRUE is a scan is running. */
+ int resolution; /* scan resolution */
+ int x_tl; /* X top left */
+ int y_tl; /* Y top left */
+ int x_br; /* X bottom right */
+ int y_br; /* Y bottom right */
+ int width; /* width of the scan area in mm */
+ int length; /* length of the scan area in mm */
+
+ enum
+ {
+ SCEPTRE_LINEART,
+ SCEPTRE_HALFTONE,
+ SCEPTRE_GRAYSCALE,
+ SCEPTRE_COLOR
+ }
+ scan_mode;
+
+ int depth; /* depth per color */
+ int halftone_param; /* haltone number, valid for SCEPTRE_HALFTONE */
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+
+ size_t real_bytes_left; /* number of bytes left the scanner will return. */
+
+ SANE_Byte *image; /* keep the raw image here */
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+
+ int color_shift; /* for color scan: number of lines to
+ * shift the colors. The higher the
+ * resolution, the higher this
+ * number. */
+
+ int raster_size; /* size of a raster */
+ int raster_num; /* for colour scan, current raster read */
+ int raster_real; /* real number of raster in the
+ * scan. This is necessary since I
+ * don't know how to reliably compute
+ * the number of lines */
+
+ int raster_ahead; /* max size of the incomplete lines */
+
+ int line; /* current line of the scan */
+
+
+ SANE_Parameters params;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+
+ /* Gamma table. 1 array per colour. */
+ SANE_Word gamma_R[GAMMA_LENGTH];
+ SANE_Word gamma_G[GAMMA_LENGTH];
+ SANE_Word gamma_B[GAMMA_LENGTH];
+
+}
+Sceptre_Scanner;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
+
+/* 16 bits from an array to an integer (eg ntohs). */
+#define B16TOI(buf) \
+ ((((unsigned char *)buf)[0] << 8) | \
+ (((unsigned char *)buf)[1] << 0))
diff --git a/backend/sharp.c b/backend/sharp.c
new file mode 100644
index 0000000..1225a57
--- /dev/null
+++ b/backend/sharp.c
@@ -0,0 +1,4213 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998, 1999
+ Kazuya Fukuda, Abel Deuring based on BYTEC GmbH Germany
+ Written by Helmut Koeberle previous Work on canon.c file from the
+ SANE package.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Sharp flatbed scanners. */
+
+/*
+ Version 0.32
+ changes to version 0.31:
+ - support for JX320 added (Thanks to Isaac Wilcox for providind the
+ patch)
+
+ Version 0.31
+ changes to version 0.30:
+ - support for JX350 added (Thanks to Shuhei Tomita for providind the
+ patch)
+
+ changes to version 0.20
+ - support for the proposed extended open function in sanei_scsi.c added
+ - support for ADF and FSU (transparency adapter) added
+ - simple sense handler added
+ - preview added
+ - added several missing statements "s->fd = -1;" after
+ "sanei_scsi_close(s->fd)" to error returns in sane_start();
+ - maximum scan sizes are read from the scanner, if a JX330 or JX250
+ is used. (this avoids the guessing of scan sizes for the JX330)
+ - gamma table support added
+ - "Fixed gamma selection (1.0/2.2)", available for JX330 and JX610,
+ is now implemented for the JX250 by downloading a gamma table
+ - changed the calls to free() and strdup() in sane_control_option to
+ strcpy.
+ (I don't like too frequent unchecked malloc()s and strdups :) Abel)
+ - cleaned up some quirks in option handling, eg, that "threshold"
+ was initially enabled, while the initial scan mode is "color"
+ - cleaned up setting SANE_INFO_RELOAD_OPTIONS and SANE_INFO_RELOAD_PARAMS
+ bits in sane_control_option
+ - bi-level color scans now give useful (8 bit) output
+ - separate thresholds for red, green, blue (bi-level color scan) added
+*/
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+/* QUEUEDEBUG should be undefined unless you want to play
+ with the sanei_scsi.c under Linux and/or with the Linux's SG driver,
+ or your suspect problems with command queueing
+*/
+#if 0
+#define QUEUEDEBUG
+#define DEBUG
+#ifdef DEBUG
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+#endif
+
+/* USE_FORK: fork a special reader process
+*/
+
+#ifdef HAVE_SYS_SHM_H
+#ifndef HAVE_OS2_H
+#define USE_FORK
+#endif
+#endif
+
+#ifdef USE_FORK
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#endif /* USE_FORK */
+
+/* xxx I'm not sure, if I understood the JX610 and JX330 manuals right,
+ that the data for the SEND command should be in ASCII format...
+ SEND commands with a data bock are used, if USE_CUSTOM_GAMMA
+ and / or USE_COLOR_THRESHOLD are enabled.
+ Abel
+*/
+#define USE_CUSTOM_GAMMA
+#define USE_COLOR_THRESHOLD
+/* enable a short list of some standard resolutions. XSane provides
+ its own resolution list; therefore its is generally not reasonable
+ to enable this list, if you mainly using XSane. But it might be handy
+ if you are working with xscanimage
+*/
+/* #define USE_RESOLUTION_LIST */
+
+/* enable separate specification of resolution in X and Y direction.
+ XSane will show the Y-resolution at a quite different place than
+ the X-resolution
+*/
+/* #define USE_SEPARATE_Y_RESOLUTION */
+
+
+#define BACKEND_NAME sharp
+#include "../include/sane/sanei_backend.h"
+
+#include <sharp.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define DEFAULT_MUD_JX610 25
+#define DEFAULT_MUD_JX320 25
+#define DEFAULT_MUD_JX330 1200
+#define DEFAULT_MUD_JX250 1200
+
+#define PIX_TO_MM(x, mud) ((x) * 25.4 / mud)
+#define MM_TO_PIX(x, mud) ((x) * mud / 25.4)
+
+#include "../include/sane/sanei_config.h"
+#define SHARP_CONFIG_FILE "sharp.conf"
+
+static int num_devices = 0;
+static SHARP_Device *first_dev = NULL;
+static SHARP_Scanner *first_handle = NULL;
+
+typedef enum
+ {
+ MODES_LINEART = 0,
+ MODES_GRAY,
+ MODES_LINEART_COLOR,
+ MODES_COLOR
+ }
+Modes;
+
+#define M_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define M_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define M_LINEART_COLOR SANE_VALUE_SCAN_MODE_COLOR_LINEART
+#define M_COLOR SANE_VALUE_SCAN_MODE_COLOR
+static const SANE_String_Const mode_list[] =
+{
+ M_LINEART, M_GRAY, M_LINEART_COLOR, M_COLOR,
+ 0
+};
+
+#define M_BILEVEL "none"
+#define M_BAYER "Dither Bayer"
+#define M_SPIRAL "Dither Spiral"
+#define M_DISPERSED "Dither Dispersed"
+#define M_ERRDIFFUSION "Error Diffusion"
+
+static const SANE_String_Const halftone_list[] =
+{
+ M_BILEVEL, M_BAYER, M_SPIRAL, M_DISPERSED, M_ERRDIFFUSION,
+ 0
+};
+
+#define LIGHT_GREEN "green"
+#define LIGHT_RED "red"
+#define LIGHT_BLUE "blue"
+#define LIGHT_WHITE "white"
+
+#define MAX_RETRIES 50
+
+static const SANE_String_Const light_color_list[] =
+{
+ LIGHT_GREEN, LIGHT_RED, LIGHT_BLUE, LIGHT_WHITE,
+ 0
+};
+
+/* possible values for ADF/FSU selection */
+static SANE_String use_adf = "Automatic Document Feeder";
+static SANE_String use_fsu = "Transparency Adapter";
+static SANE_String use_simple = "Flatbed";
+
+/* auto selection of ADF and FSU, as described in the JX330 manual,
+ is a nice idea -- but I assume that the possible scan window
+ sizes depend not only for the JX250, but also for JX330 on the
+ usage of ADF or FSU. Thus, the user might be able to select scan
+ windows of an "illegal" size, which would have to be automatically
+ corrected, and I don't see, how the user could be informed about
+ this "window clipping". More important, I don't see, how the
+ frontend could be informed that the ADF is automatically enabled.
+
+ Insert a "#define ALLOW_AUTO_SELECT_ADF", if you want to play
+ with this feature.
+*/
+#ifdef ALLOW_AUTO_SELECT_ADF
+static SANE_String_Const use_auto = "AutoSelection";
+#endif
+
+#define HAVE_FSU 1
+#define HAVE_ADF 2
+
+/* The follow #defines are used in SHARP_Scanner.adf_fsu_mode
+ and as indexes for the arrays x_ranges, y_ranges in SHARP_Device
+*/
+#define SCAN_SIMPLE 0
+#define SCAN_WITH_FSU 1
+#define SCAN_WITH_ADF 2
+#ifdef ALLOW_AUTO_SELECT_ADF
+#define SCAN_ADF_FSU_AUTO 3
+#endif
+#define LOAD_PAPER 1
+#define UNLOAD_PAPER 0
+
+#define PAPER_MAX 10
+#define W_LETTER "11\"x17\""
+#define INVOICE "8.5\"x5.5\""
+static const SANE_String_Const paper_list_jx610[] =
+{
+ "A3", "A4", "A5", "A6", "B4", "B5",
+ W_LETTER, "Legal", "Letter", INVOICE,
+ 0
+};
+
+static const SANE_String_Const paper_list_jx330[] =
+{
+ "A4", "A5", "A6", "B5",
+ 0
+};
+
+#define GAMMA10 "1.0"
+#define GAMMA22 "2.2"
+
+static const SANE_String_Const gamma_list[] =
+{
+ GAMMA10, GAMMA22,
+ 0
+};
+
+#if 0
+#define SPEED_NORMAL "Normal"
+#define SPEED_FAST "Fast"
+static const SANE_String_Const speed_list[] =
+{
+ SPEED_NORMAL, SPEED_FAST,
+ 0
+};
+#endif
+
+#ifdef USE_RESOLUTION_LIST
+#define RESOLUTION_MAX_JX610 8
+static const SANE_String_Const resolution_list_jx610[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "600", "Select",
+ 0
+};
+
+#define RESOLUTION_MAX_JX250 7
+static const SANE_String_Const resolution_list_jx250[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "Select",
+ 0
+};
+#endif
+
+#define EDGE_NONE "None"
+#define EDGE_MIDDLE "Middle"
+#define EDGE_STRONG "Strong"
+#define EDGE_BLUR "Blur"
+static const SANE_String_Const edge_emphasis_list[] =
+{
+ EDGE_NONE, EDGE_MIDDLE, EDGE_STRONG, EDGE_BLUR,
+ 0
+};
+
+#ifdef USE_CUSTOM_GAMMA
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+#endif
+
+static SANE_Status
+sense_handler(int __sane_unused__ fd, u_char *sense_buffer, void *s)
+{
+ int sense_key;
+ SHARP_Sense_Data *sdat = (SHARP_Sense_Data *) s;
+
+#define add_sense_code sense_buffer[12]
+#define add_sense_qual sense_buffer[13]
+
+ memcpy(sdat->sb, sense_buffer, 16);
+
+ DBG(10, "sense code: %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ sense_buffer[0], sense_buffer[1], sense_buffer[2], sense_buffer[3],
+ sense_buffer[4], sense_buffer[5], sense_buffer[6], sense_buffer[7],
+ sense_buffer[8], sense_buffer[9], sense_buffer[10], sense_buffer[11],
+ sense_buffer[12], sense_buffer[13], sense_buffer[14], sense_buffer[15]);
+
+ sense_key = sense_buffer[2] & 0x0F;
+ /* do we have additional information ? */
+ if (sense_buffer[7] >= 5)
+ {
+ if (sdat->model == JX610)
+ {
+ /* The JX610 uses somewhat different error codes */
+ switch (add_sense_code)
+ {
+ case 0x04:
+ DBG(5, "error: scanner not ready\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x08:
+ DBG(5, "error: scanner communication failure (time out?)\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x1A:
+ DBG(10, "error: parameter list length error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x20:
+ DBG(10, "error: invalid command code\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x24:
+ DBG(10, "error: invalid field in CDB\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x25:
+ DBG(10, "error: LUN not supported\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x26:
+ DBG(10, "error: invalid field in parameter list\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x29:
+ DBG(10, "note: reset occured\n");
+ return SANE_STATUS_GOOD;
+ case 0x2a:
+ DBG(10, "note: mode parameter change\n");
+ return SANE_STATUS_GOOD;
+ case 0x37:
+ DBG(10, "note: rounded parameter\n");
+ return SANE_STATUS_GOOD;
+ case 0x39:
+ DBG(10, "error: saving parameter not supported\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x47:
+ DBG(10, "SCSI parity error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x48:
+ DBG(10, "initiator detected error message received\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x60:
+ DBG(1, "error: lamp failure\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x62:
+ DBG(1, "scan head positioning error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ }
+ else if (sdat->model == JX250 || sdat->model == JX330 ||
+ sdat->model == JX350 || sdat->model == JX320)
+ {
+ switch (sense_key)
+ {
+ case 0x02: /* not ready */
+ switch (add_sense_code)
+ {
+ case 0x80:
+ switch (add_sense_qual)
+ {
+ case 0:
+ DBG(1, "Scanner not ready: ADF cover open\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_COVER_OPEN;
+ else
+ return SANE_STATUS_GOOD;
+ case 1:
+ DBG(1, "Scanner not ready: ADF maintenance "
+ "cover open\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_COVER_OPEN;
+ else
+ return SANE_STATUS_GOOD;
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x81:
+ /* NOT TESTED -- I don't have a FSU */
+ switch (add_sense_qual)
+ {
+ case 0:
+ DBG(1, "Scanner not ready: FSU cover open\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_FSU_ERROR)
+ return SANE_STATUS_COVER_OPEN;
+ else
+ return SANE_STATUS_GOOD;
+ case 1:
+ DBG(1, "Scanner not ready: FSU light dispersion "
+ "error\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_FSU_ERROR)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ return SANE_STATUS_GOOD;
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x03: /* medium error */
+ switch (add_sense_code)
+ {
+ case 0x3a:
+ DBG(1, "ADF is empty\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_NO_DOCS;
+ else
+ return SANE_STATUS_GOOD;
+ case 0x53:
+ DBG(1, "ADF paper jam\n"
+ "Open and close the maintenance cover to clear "
+ "this error\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_JAMMED;
+ else
+ return SANE_STATUS_GOOD;
+ default:
+ DBG(5, "medium error: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x04: /* hardware error */
+ switch (add_sense_code)
+ {
+ case 0x08:
+ DBG(1, "hardware error: scanner communication failed\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x60:
+ DBG(1, "hardware error: lamp failure\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x62:
+ DBG(1, "hardware error: scan head positioning failed\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG(1, "general hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x05: /* illegal request */
+ DBG(10, "error: illegal request\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x06: /* unit attention */
+ switch (add_sense_code)
+ {
+ case 0x29:
+ DBG(5, "unit attention: reset occured\n");
+ return SANE_STATUS_GOOD;
+ case 0x2a:
+ DBG(5, "unit attention: parameter changed by "
+ "another initiator\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG(5, "unit attention: exact reason not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x09: /* data remains */
+ DBG(5, "error: data remains\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG(5, "error: sense code not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static u_char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< test_unit_ready ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+request_sense (int fd, void *sense_buf, size_t *sense_size)
+{
+ static u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, SENSE_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< request_sense ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_buf, sense_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+inquiry (int fd, void *inq_buf, size_t *inq_size)
+{
+ static u_char cmd[] = {INQUIRY, 0, 0, 0, INQUIRY_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< inquiry ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), inq_buf, inq_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select_mud (int fd, int mud)
+{
+ static u_char cmd[6 + MODEPARAM_LEN] =
+ {MODE_SELECT6, 0x10, 0, 0, MODEPARAM_LEN, 0};
+ mode_select_param *mp;
+ SANE_Status status;
+ DBG (11, "<< mode_select_mud ");
+
+ mp = (mode_select_param *)(cmd + 6);
+ memset (mp, 0, MODEPARAM_LEN);
+ mp->page_code = 3;
+ mp->page_length = 6;
+ mp->mud[0] = mud >> 8;
+ mp->mud[1] = mud & 0xFF;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select_adf_fsu (int fd, int mode)
+{
+ static u_char cmd[6 + MODE_SUBDEV_LEN] =
+ {MODE_SELECT6, 0x10, 0, 0, MODE_SUBDEV_LEN, 0};
+ mode_select_subdevice *mp;
+ SANE_Status status;
+ DBG (11, "<< mode_select_adf_fsu ");
+
+ mp = (mode_select_subdevice *)(cmd + 6);
+ memset (mp, 0, MODE_SUBDEV_LEN);
+ mp->page_code = 0x20;
+ mp->page_length = 26;
+ switch (mode)
+ {
+ case SCAN_SIMPLE:
+ mp->a_mode = 0x40;
+ mp->f_mode = 0x40;
+ break;
+ case SCAN_WITH_FSU:
+ mp->a_mode = 0;
+ mp->f_mode = 0x40;
+ break;
+ case SCAN_WITH_ADF:
+ mp->a_mode = 0x40;
+ mp->f_mode = 0;
+ break;
+#ifdef ALLOW_AUTO_SELECT_ADF
+ case: SCAN_ADF_FSU_AUTO:
+ mp->a_mode = 0;
+ mp->f_mode = 0;
+ break;
+#endif
+ }
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status wait_ready(int fd);
+
+static SANE_Status
+object_position(int fd, int load)
+{
+ static u_char cmd[] = {OBJECT_POSITION, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< object_position ");
+
+ cmd[1] = load;
+
+ wait_ready(fd);
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+reserve_unit (int fd)
+{
+ static u_char cmd[] = {RESERVE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< reserve_unit ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+release_unit (int fd)
+{
+ static u_char cmd[] = {RELEASE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< release_unit ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+mode_sense (int fd, void *modeparam_buf, size_t * modeparam_size,
+ int page)
+{
+ static u_char cmd[6];
+ SANE_Status status;
+ DBG (11, "<< mode_sense ");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = 0x1a;
+ cmd[2] = page;
+ cmd[4] = *modeparam_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), modeparam_buf,
+ modeparam_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+scan (int fd)
+{
+ static u_char cmd[] = {SCAN, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< scan ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+send_diagnostics (int fd)
+{
+ static u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< send_diagnostics ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+send (int fd, SHARP_Send * ss)
+{
+ static u_char cmd[] = {SEND, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< send ");
+
+ cmd[2] = ss->dtc;
+ cmd[4] = ss->dtq >> 8;
+ cmd[5] = ss->dtq;
+ cmd[6] = ss->length >> 16;
+ cmd[7] = ss->length >> 8;
+ cmd[8] = ss->length >> 0;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+
+}
+
+static SANE_Status
+set_window (int fd, window_param *wp, int len)
+{
+ static u_char cmd[10 + WINDOW_LEN] =
+ {SET_WINDOW, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ window_param *winp;
+ SANE_Status status;
+ DBG (11, "<< set_window ");
+
+ cmd[8] = len;
+ winp = (window_param *)(cmd + 10);
+ memset (winp, 0, WINDOW_LEN);
+ memcpy (winp, wp, len);
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+
+}
+
+static SANE_Status
+get_window (int fd, void *buf, size_t * buf_size)
+{
+
+ static u_char cmd[10] = {GET_WINDOW, 0, 0, 0, 0, 0, 0, 0, WINDOW_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< get_window ");
+
+ cmd[8] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#ifdef USE_FORK
+
+/* the following four functions serve simply the purpose
+ to avoid "over-optimised" code when reader_process and
+ read_data wait for the buffer to become ready. The simple
+ while-loops in these functions which check the buffer
+ status may be optimised so that the machine code only
+ operates with registers instead of using the variable
+ values stored in memory. (This is only a workaround -
+ it would be better to set a compiler pragma, which ensures
+ that the program looks into the RAM in these while loops --
+ but unfortunately I could not find appropriate information
+ about this at least for gcc, not to speak about other
+ compilers...
+ Abel)
+*/
+
+static int
+cancel_requested(SHARP_Scanner *s)
+{
+ return s->rdr_ctl->cancel;
+}
+
+static SANE_Status
+rdr_status(SHARP_Scanner *s)
+{
+ return s->rdr_ctl->status;
+}
+
+static int
+buf_status(SHARP_shmem_ctl *s)
+{
+ return s->shm_status;
+}
+
+static int
+reader_running(SHARP_Scanner *s)
+{
+ return s->rdr_ctl->running;
+}
+
+static int
+reader_process(SHARP_Scanner *s)
+{
+ SANE_Status status;
+ sigset_t sigterm_set;
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int full_count = 0, counted;
+ size_t waitindex, cmdindex;
+ size_t bytes_to_queue;
+ size_t nread;
+ size_t max_bytes_per_read;
+ int max_queue;
+ int i, retries = MAX_RETRIES;
+ SHARP_shmem_ctl *bc;
+
+ s->rdr_ctl->running = 1;
+ DBG(11, "<< reader_process\n");
+
+ sigemptyset (&sigterm_set);
+
+ bytes_to_queue = s->bytes_to_read;
+
+ /* it seems that some carriage stops can be avoided with the
+ JX-250, if the data of an integral number of scan lines is
+ read with one SCSI command
+ */
+ max_bytes_per_read = s->dev->info.bufsize / s->params.bytes_per_line;
+ if (max_bytes_per_read)
+ max_bytes_per_read *= s->params.bytes_per_line;
+ else
+ /* this is a really tiny buffer..*/
+ max_bytes_per_read = s->dev->info.bufsize;
+
+ /* wait_ready(s->fd); */
+
+ if (s->dev->info.queued_reads <= s->dev->info.buffers)
+ max_queue = s->dev->info.queued_reads;
+ else
+ max_queue = s->dev->info.buffers;
+ if (max_queue <= 0)
+ max_queue = 1;
+ for (i = 0; i < max_queue; i++)
+ {
+ bc = &s->rdr_ctl->buf_ctl[i];
+ if (bytes_to_queue)
+ {
+ nread = bytes_to_queue;
+ if (nread > max_bytes_per_read)
+ nread = max_bytes_per_read;
+ bc->used = nread;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_enter...\n");
+#endif
+ status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
+ bc->buffer,
+ &bc->used,
+ &bc->qid);
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_enter ok\n");
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ bc->shm_status = SHM_BUSY;
+ bc->nreq = bc->used;
+ bytes_to_queue -= bc->nreq;
+ }
+ else
+ {
+ bc->used = 0;
+ bc->shm_status = SHM_EMPTY;
+ }
+ }
+ waitindex = 0;
+ cmdindex = i % s->dev->info.buffers;
+
+ while(s->bytes_to_read > 0)
+ {
+ if (cancel_requested(s))
+ {
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: flushing requests...\n");
+#endif
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: flushing requests ok\n");
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+
+ bc = &s->rdr_ctl->buf_ctl[waitindex];
+ if (bc->shm_status == SHM_BUSY)
+ {
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: waiting for data %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_wait...\n");
+#endif
+ status = sanei_scsi_req_wait(bc->qid);
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_wait ok\n");
+#endif
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: data received %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (status == SANE_STATUS_DEVICE_BUSY && retries)
+ {
+ bc->used = 0;
+ retries--;
+ DBG(11, "reader: READ command returned BUSY\n");
+ status = SANE_STATUS_GOOD;
+ usleep(10000);
+ }
+ else if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s\n",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ else
+ {
+ retries = MAX_RETRIES;
+ }
+#if 1
+ s->bytes_to_read -= bc->used;
+ bytes_to_queue += bc->nreq - bc->used;
+#else
+ /* xxxxxxxxxxxxxxxxxxx TEST xxxxxxxxxxxxxxx */
+ s->bytes_to_read -= bc->nreq;
+ /* memset(bc->buffer + bc->used, 0, bc->nreq - bc->used); */
+ bc->used = bc->nreq;
+ /* bytes_to_queue += bc->nreq - bc->used; */
+ DBG(1, "btr: %i btq: %i nreq: %i nrcv: %i\n",
+ s->bytes_to_read, bytes_to_queue, bc->nreq, bc->used);
+#endif
+ bc->start = 0;
+ bc->shm_status = SHM_FULL;
+
+ waitindex++;
+ if (waitindex == s->dev->info.buffers)
+ waitindex = 0;
+
+ }
+
+ if (bytes_to_queue)
+ {
+ /* wait until the next buffer is completely read via read_data */
+ bc = &s->rdr_ctl->buf_ctl[cmdindex];
+ counted = 0;
+ while (buf_status(bc) != SHM_EMPTY)
+ {
+ if (!counted)
+ {
+ counted = 1;
+ full_count++;
+ }
+ if (cancel_requested(s))
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+ }
+
+ nread = bytes_to_queue;
+ if (nread > max_bytes_per_read)
+ nread = max_bytes_per_read;
+ bc->used = nread;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+ status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
+ bc->buffer, &bc->used, &bc->qid);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ bc->shm_status = SHM_BUSY;
+ bc->nreq = nread;
+ bytes_to_queue -= nread;
+
+ cmdindex++;
+ if (cmdindex == s->dev->info.buffers)
+ cmdindex = 0;
+ }
+
+ if (cancel_requested(s))
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+ }
+
+ DBG(1, "buffer full conditions: %i\n", full_count);
+ DBG(11, " reader_process>>\n");
+
+ s->rdr_ctl->running = 0;
+ return 0;
+}
+
+static SANE_Status
+read_data (SHARP_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ size_t copysize, copied = 0;
+ SHARP_shmem_ctl *bc;
+
+ DBG(11, "<< read_data ");
+
+ bc = &s->rdr_ctl->buf_ctl[s->read_buff];
+
+ while (copied < *buf_size)
+ {
+ /* wait until the reader process delivers data or a scanner error occurs: */
+ while ( buf_status(bc) != SHM_FULL
+ && rdr_status(s) == SANE_STATUS_GOOD)
+ {
+ usleep(10); /* could perhaps be longer. make this user configurable?? */
+ }
+
+ if (rdr_status(s) != SANE_STATUS_GOOD)
+ {
+ return rdr_status(s);
+ DBG(11, ">>\n");
+ }
+
+ copysize = bc->used - bc->start;
+
+ if (copysize > *buf_size - copied )
+ copysize = *buf_size - copied;
+
+ memcpy(buf, &(bc->buffer[bc->start]), copysize);
+
+ copied += copysize;
+ buf = &buf[copysize];
+
+ bc->start += copysize;
+ if (bc->start >= bc->used)
+ {
+ bc->start = 0;
+ bc->shm_status = SHM_EMPTY;
+ s->read_buff++;
+ if (s->read_buff == s->dev->info.buffers)
+ s->read_buff = 0;
+ bc = &s->rdr_ctl->buf_ctl[s->read_buff];
+ }
+ }
+
+ DBG(11, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+#else /* don't USE_FORK: */
+
+static SANE_Status
+read_data (SHARP_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t remain = *buf_size;
+ size_t nread;
+ int retries = MAX_RETRIES;
+ DBG (11, "<< read_data ");
+
+ /* sane_read_shuffled requires that read_data returns
+ exactly *buf_size bytes, so it must be guaranteed here.
+ Further make sure that not more bytes are read in than
+ sanei_scsi_max_request_size allows, to avoid a failure
+ of the read command
+ */
+ while (remain > 0)
+ {
+ nread = remain;
+ if (nread > s->dev->info.bufsize)
+ nread = s->dev->info.bufsize;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd),
+ &buf[*buf_size - remain], &nread);
+ if (status == SANE_STATUS_DEVICE_BUSY && retries)
+ {
+ retries--;
+ nread = 0;
+ usleep(10000);
+ }
+ else if (status != SANE_STATUS_GOOD)
+ {
+ DBG(11, ">>\n");
+ return(status);
+ }
+ else
+ {
+ retries = MAX_RETRIES;
+ }
+ remain -= nread;
+ }
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (10, "<< max_string_size ");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (10, ">>\n");
+ return max_size;
+}
+
+static SANE_Status
+wait_ready(int fd)
+{
+ SANE_Status status;
+ int retry = 0;
+
+ while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "wait_ready failed (%d)\n", retry);
+ if (retry++ > 15){
+ return SANE_STATUS_IO_ERROR;
+ }
+ sleep(3);
+ }
+ return (status);
+
+}
+
+/* ask the scanner for the maximum scan sizes with/without ADF and
+ FSU. The JX330 manual does mention the sizes.
+*/
+static SANE_Status
+get_max_scan_size(int fd, SHARP_Device *dev, int mode)
+{
+ SANE_Status status;
+ mode_sense_subdevice m_subdev;
+ size_t buf_size;
+
+ status = mode_select_adf_fsu(fd, mode);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "get_scan_sizes: MODE_SELECT/subdevice page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "get_scan_sizes: sending MODE SENSE/subdevice page\n");
+ memset (&m_subdev, 0, sizeof (m_subdev));
+ buf_size = sizeof (m_subdev);
+ status = mode_sense (fd, &m_subdev, &buf_size, 0x20);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "get_scan_sizes: MODE_SENSE/subdevice page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ dev->info.tl_x_ranges[mode].min = 0;
+ dev->info.tl_x_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_x[0] << 24) + (m_subdev.max_x[1] << 16) +
+ (m_subdev.max_x[2] << 8) + m_subdev.max_x[3] - 1, dev->info.mud));
+ dev->info.tl_x_ranges[mode].quant = 0;
+
+ dev->info.br_x_ranges[mode].min = SANE_FIX(PIX_TO_MM(1, dev->info.mud));
+ dev->info.br_x_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_x[0] << 24) + (m_subdev.max_x[1] << 16) +
+ (m_subdev.max_x[2] << 8) + m_subdev.max_x[3], dev->info.mud));
+ dev->info.br_x_ranges[mode].quant = 0;
+
+ dev->info.tl_y_ranges[mode].min = 0;
+ if ((dev->sensedat.model != JX250 && dev->sensedat.model != JX350) ||
+ mode != SCAN_WITH_FSU)
+ dev->info.tl_y_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_y[0] << 24) + (m_subdev.max_y[1] << 16) +
+ (m_subdev.max_y[2] << 8) + m_subdev.max_y[3] - 1, dev->info.mud));
+ else
+ /* The manual for the JX250 states on page 62 that the maximum
+ value for tl_y in FSU mode is 13199, while the max value for
+ br_y is 13900, which is (probably -- I don't have a FSU) returned
+ by mode sense/subdevice page. Therefore, we cannot simply
+ decrement that value and store it as max(tl_y).
+ */
+ dev->info.tl_y_ranges[mode].max = 13199;
+ dev->info.tl_y_ranges[mode].quant = 0;
+
+ dev->info.br_y_ranges[mode].min = SANE_FIX(PIX_TO_MM(1, dev->info.mud));
+ dev->info.br_y_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_y[0] << 24) + (m_subdev.max_y[1] << 16) +
+ (m_subdev.max_y[2] << 8) + m_subdev.max_y[3], dev->info.mud));
+ dev->info.br_y_ranges[mode].quant = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (const char *devnam, SHARP_Device ** devp)
+{
+ SANE_Status status;
+ SHARP_Device *dev;
+ SHARP_Sense_Data sensedat;
+
+ int fd;
+ char inquiry_data[INQUIRY_LEN];
+ const char *model_name;
+ mode_sense_param msp;
+ mode_sense_subdevice m_subdev;
+ size_t buf_size;
+ DBG (10, "<< attach ");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ sensedat.model = unknown;
+ sensedat.complain_on_errors = 0;
+ DBG (3, "attach: opening %s\n", devnam);
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ {
+ int bufsize = 4096;
+ status = sanei_scsi_open_extended (devnam, &fd, &sense_handler, &sensedat, &bufsize);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ if (bufsize < 4096)
+ {
+ DBG(1, "attach: open failed. no memory\n");
+ sanei_scsi_close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+#else
+ status = sanei_scsi_open (devnam, &fd, &sense_handler, &sensedat);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+#endif
+
+ DBG (3, "attach: sending INQUIRY\n");
+ memset (inquiry_data, 0, sizeof (inquiry_data));
+ buf_size = sizeof (inquiry_data);
+ status = inquiry (fd, inquiry_data, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ if (inquiry_data[0] == 6 && strncmp (inquiry_data + 8, "SHARP", 5) == 0)
+ {
+ if (strncmp (inquiry_data + 16, "JX610", 5) == 0)
+ sensedat.model = JX610;
+ else if (strncmp (inquiry_data + 16, "JX250", 5) == 0)
+ sensedat.model = JX250;
+ else if (strncmp (inquiry_data + 16, "JX350", 5) == 0)
+ sensedat.model = JX350;
+ else if ( strncmp (inquiry_data + 16, "JX320", 5) == 0
+ || strncmp (inquiry_data + 16, "JX325", 5) == 0)
+ sensedat.model = JX320;
+ else if (strncmp (inquiry_data + 16, "JX330", 5) == 0)
+ sensedat.model = JX330;
+ }
+
+ if (sensedat.model == unknown)
+ {
+ DBG (1, "attach: device doesn't look like a Sharp scanner\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ DBG (3, "attach: sending MODE SELECT\n");
+ /* JX-610 probably supports only 25 MUD size
+ JX-320 only supports 25 MUD size
+ */
+ if (strncmp (inquiry_data + 16, "JX610", 5) == 0)
+ status = mode_select_mud (fd, DEFAULT_MUD_JX610);
+ else if (strncmp (inquiry_data + 16, "JX320", 5) == 0)
+ status = mode_select_mud (fd, DEFAULT_MUD_JX320);
+ else
+ status = mode_select_mud (fd, DEFAULT_MUD_JX330);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT6 failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending MODE SENSE/MUP page\n");
+ memset (&msp, 0, sizeof (msp));
+ buf_size = sizeof (msp);
+ status = mode_sense (fd, &msp, &buf_size, 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE/MUP page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devnam);
+ dev->sane.vendor = "SHARP";
+ model_name = (char*) inquiry_data + 16;
+ dev->sane.model = strndup (model_name, 10);
+ dev->sane.type = "flatbed scanner";
+
+ dev->sensedat.model = sensedat.model;
+
+ DBG (5, "dev->sane.name = %s\n", dev->sane.name);
+ DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor);
+ DBG (5, "dev->sane.model = %s\n", dev->sane.model);
+ DBG (5, "dev->sane.type = %s\n", dev->sane.type);
+
+ dev->info.xres_range.quant = 0;
+ dev->info.yres_range.quant = 0;
+
+ dev->info.tl_x_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
+ dev->info.br_x_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
+ dev->info.br_y_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.br_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.br_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+
+ dev->info.xres_default = 150;
+ dev->info.yres_default = 150;
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(209);
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(296);
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297);
+
+ dev->info.bmu = msp.bmu;
+ dev->info.mud = (msp.mud[0] << 8) + msp.mud[1];
+
+ dev->info.adf_fsu_installed = 0;
+ if (dev->sensedat.model == JX610)
+ {
+ dev->info.xres_range.max = 600;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 600;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(303); /* 304.8mm is the real max */
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(304); /* 304.8mm is the real max */
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(430); /* 431.8 is the real max */
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(431); /* 431.8 is the real max */
+ }
+ else if (dev->sensedat.model == JX320)
+ {
+ dev->info.xres_range.max = 600;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 600;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(212);
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(213);
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(292);
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(293);
+ }
+ else
+ {
+ /* ask the scanner, if ADF or FSU are installed, and ask for
+ the maximum scan sizes with/without ADF and FSU.
+ */
+
+ DBG (3, "attach: sending MODE SENSE/subdevice page\n");
+ memset (&m_subdev, 0, sizeof (m_subdev));
+ buf_size = sizeof (m_subdev);
+ status = mode_sense (fd, &m_subdev, &buf_size, 0x20);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE/subdevice page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* The JX330 manual is not very clear about the ADF- und FSU-Bits
+ returned by a JX320 and JX325 for the mode sense command:
+ Are these bits set to zero or not? To be on the safe side, let's
+ clear them.
+ */
+
+ if ( strncmp(inquiry_data + 16, "JX320", 5) == 0
+ || strncmp(inquiry_data + 16, "JX325", 5) == 0)
+ {
+ m_subdev.f_mode_type = 0;
+ m_subdev.a_mode_type = 0;
+ }
+
+ get_max_scan_size(fd, dev, SCAN_SIMPLE);
+
+ if (m_subdev.a_mode_type & 0x03)
+ {
+ dev->info.adf_fsu_installed = HAVE_ADF;
+ get_max_scan_size(fd, dev, SCAN_WITH_ADF);
+ }
+ if (m_subdev.f_mode_type & 0x07)
+ {
+ dev->info.adf_fsu_installed |= HAVE_FSU;
+ get_max_scan_size(fd, dev, SCAN_WITH_FSU);
+ }
+
+ if ( dev->sensedat.model == JX320
+ || dev->sensedat.model == JX330
+ || dev->sensedat.model == JX350)
+ {
+ dev->info.xres_range.max = 600;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 600;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.y_default = SANE_FIX(297);
+ }
+ else if (dev->sensedat.model == JX250)
+ {
+ dev->info.xres_range.max = 400;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 400;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.y_default = SANE_FIX(297);
+ }
+ }
+ sanei_scsi_close (fd);
+
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 0;
+
+ DBG (5, "xres_default=%d\n", dev->info.xres_default);
+ DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max);
+ DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min);
+ DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
+ DBG (5, "yres_default=%d\n", dev->info.yres_default);
+ DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max);
+ DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min);
+ DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
+
+ DBG (5, "x_default=%f\n", SANE_UNFIX(dev->info.x_default));
+ DBG (5, "tl_x_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].max));
+ DBG (5, "tl_x_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].min));
+ DBG (5, "tl_x_range[0].quant=%d\n", dev->info.tl_x_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "br_x_range[0].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].max));
+ DBG (5, "br_x_range[0].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].min));
+ DBG (5, "br_x_range[0].quant=%d\n", dev->info.br_x_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "y_default=%f\n", SANE_UNFIX(dev->info.y_default));
+ DBG (5, "tl_y_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].max));
+ DBG (5, "tl_y_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].min));
+ DBG (5, "tl_y_range[0].quant=%d\n", dev->info.tl_y_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "br_y_range[0].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].max));
+ DBG (5, "br_y_range[0].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].min));
+ DBG (5, "br_y_range[0].quant=%d\n", dev->info.br_y_ranges[SCAN_SIMPLE].quant);
+
+ if (dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ DBG (5, "tl_x_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "tl_x_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "tl_x_range[1].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "br_x_range[1].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "br_x_range[1].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "br_x_range[1].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "tl_y_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "tl_y_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "tl_y_range[1].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "br_y_range[1].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "br_y_range[1].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "br_y_range[1].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_FSU].quant);
+ }
+
+ if (dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ DBG (5, "tl_x_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "tl_x_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "tl_x_range[2].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "br_x_range[2].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "br_x_range[2].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "br_x_range[2].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "tl_y_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "tl_y_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "tl_y_range[2].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "br_y_range[2].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "br_y_range[2].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "br_y_range[2].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_ADF].quant);
+ }
+
+ DBG (5, "bmu=%d\n", dev->info.bmu);
+ DBG (5, "mud=%d\n", dev->info.mud);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/* Enabling / disabling of gamma options.
+ Depends on many user settable options, so lets put it into
+ one function to be called by init_options and by sane_control_option
+
+*/
+#ifdef USE_CUSTOM_GAMMA
+static void
+set_gamma_caps(SHARP_Scanner *s)
+{
+ /* neither fixed nor custom gamma for line art modes */
+ if ( strcmp(s->val[OPT_MODE].s, M_LINEART) == 0
+ || strcmp(s->val[OPT_MODE].s, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp(s->val[OPT_MODE].s, M_GRAY) == 0)
+ {
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+}
+#endif /* USE_CUSTOM_GAMMA */
+
+/* The next function is a slightly modified version of sanei_constrain_value
+ Instead of returning status information like STATUS_INVAL, it adjusts
+ an invaild value to the nearest allowed one.
+*/
+static void
+clip_value (const SANE_Option_Descriptor * opt, void * value)
+{
+ const SANE_String_Const * string_list;
+ const SANE_Word * word_list;
+ int i, num_matches, match;
+ const SANE_Range * range;
+ SANE_Word w, v;
+ size_t len;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ w = *(SANE_Word *) value;
+ range = opt->constraint.range;
+
+ if (w < range->min)
+ w = range->min;
+ else if (w > range->max)
+ w = range->max;
+
+ if (range->quant)
+ {
+ v = (w - range->min + range->quant/2) / range->quant;
+ w = v * range->quant + range->min;
+ *(SANE_Word*) value = w;
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ w = *(SANE_Word *) value;
+ word_list = opt->constraint.word_list;
+ for (i = 1; w != word_list[i]; ++i)
+ if (i >= word_list[0])
+ /* somewhat arbitrary... Would be better to have a default value
+ explicitly defined.
+ */
+ *(SANE_Word*) value = word_list[1];
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ /* Matching algorithm: take the longest unique match ignoring
+ case. If there is an exact match, it is admissible even if
+ the same string is a prefix of a longer option name. */
+ string_list = opt->constraint.string_list;
+ len = strlen (value);
+
+ /* count how many matches of length LEN characters we have: */
+ num_matches = 0;
+ match = -1;
+ for (i = 0; string_list[i]; ++i)
+ if (strncasecmp (value, string_list[i], len) == 0
+ && len <= strlen (string_list[i]))
+ {
+ match = i;
+ if (len == strlen (string_list[i]))
+ {
+ /* exact match... */
+ if (strcmp (value, string_list[i]) != 0)
+ /* ...but case differs */
+ strcpy (value, string_list[match]);
+ }
+ ++num_matches;
+ }
+
+ if (num_matches > 1)
+ /* xxx quite arbitrary... We could also choose the first match
+ */
+ strcpy(value, string_list[match]);
+ else if (num_matches == 1)
+ strcpy (value, string_list[match]);
+ else
+ strcpy (value, string_list[0]);
+
+ default:
+ break;
+ }
+}
+
+/* make sure that enough memory is allocated for each string,
+ so that the strcpy in sane_control_option / set value cannot
+ write behind the end of the allocated memory.
+*/
+static SANE_Status
+init_string_option(SHARP_Scanner *s, SANE_String_Const name,
+ SANE_String_Const title, SANE_String_Const desc,
+ const SANE_String_Const *string_list, int option, int default_index)
+{
+ int i;
+
+ s->opt[option].name = name;
+ s->opt[option].title = title;
+ s->opt[option].desc = desc;
+ s->opt[option].type = SANE_TYPE_STRING;
+ s->opt[option].size = max_string_size (string_list);
+ s->opt[option].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[option].constraint.string_list = string_list;
+ s->val[option].s = malloc(s->opt[option].size);
+ if (s->val[option].s == 0)
+ {
+ for (i = 1; i < NUM_OPTIONS; i++)
+ {
+ if (s->val[i].s && s->opt[i].type == SANE_TYPE_STRING)
+ free(s->val[i].s);
+ }
+ return SANE_STATUS_NO_MEM;
+ }
+ strcpy(s->val[option].s, string_list[default_index]);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (SHARP_Scanner * s)
+{
+ int i, default_source, sourcename_index = 0;
+ SANE_Word scalar;
+ DBG (10, "<< init_options ");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->val[i].s = 0;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* Mode group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+#if 0
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[3]); /* color scan */
+#endif
+ init_string_option(s, SANE_NAME_SCAN_MODE, SANE_TITLE_SCAN_MODE,
+ SANE_DESC_SCAN_MODE, mode_list, OPT_MODE, 3);
+
+ /* half tone */
+#if 0
+ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE].desc = SANE_DESC_HALFTONE " (JX-330 only)";
+ s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE].size = max_string_size (halftone_list);
+ s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
+ s->val[OPT_HALFTONE].s = strdup (halftone_list[0]);
+#endif
+ init_string_option(s, SANE_NAME_HALFTONE_PATTERN, SANE_TITLE_HALFTONE_PATTERN,
+ SANE_DESC_HALFTONE " (JX-330 only)", halftone_list, OPT_HALFTONE, 0);
+
+ if (s->dev->sensedat.model == JX250 || s->dev->sensedat.model == JX350 ||
+ s->dev->sensedat.model == JX610 || s->dev->sensedat.model == JX320)
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+
+ i = 0;
+ default_source = s->dev->info.default_scan_mode;
+
+#ifdef ALLOW_AUTO_SELECT_ADF
+ /* The JX330, but nut not the JX250 supports auto selection of ADF/FSU: */
+ if (s->dev->info.adf_fsu_installed && (s->dev->sensedat.model == JX330))
+ s->dev->info->scansources[i++] = use_auto;
+#endif
+ if (s->dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ if (default_source == -1)
+ default_source = SCAN_WITH_ADF;
+ if (default_source == SCAN_WITH_ADF)
+ sourcename_index = i;
+ s->dev->info.scansources[i++] = use_adf;
+ }
+ else
+ {
+ if (default_source == SCAN_WITH_ADF)
+ default_source = SCAN_SIMPLE;
+ }
+ if (s->dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ if (default_source == -1)
+ default_source = SCAN_WITH_FSU;
+ if (default_source == SCAN_WITH_FSU)
+ sourcename_index = i;
+ s->dev->info.scansources[i++] = use_fsu;
+ }
+ else
+ {
+ if (default_source == SCAN_WITH_FSU)
+ default_source = SCAN_SIMPLE;
+ }
+ if (default_source < 0)
+ default_source = SCAN_SIMPLE;
+ if (default_source == SCAN_SIMPLE)
+ sourcename_index = i;
+ s->dev->info.scansources[i++] = use_simple;
+ s->dev->info.scansources[i] = 0;
+
+#if 0
+ s->opt[OPT_SCANSOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SCANSOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SCANSOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SCANSOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCANSOURCE].size = max_string_size (s->dev->info.scansources);
+ s->opt[OPT_SCANSOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCANSOURCE].constraint.string_list = (SANE_String_Const*)s->dev->info.scansources;
+ s->val[OPT_SCANSOURCE].s = strdup (s->dev->info.scansources[0]);
+#endif
+
+ init_string_option(s, SANE_NAME_SCAN_SOURCE, SANE_TITLE_SCAN_SOURCE,
+ SANE_DESC_SCAN_SOURCE, (SANE_String_Const*)s->dev->info.scansources,
+ OPT_SCANSOURCE, sourcename_index);
+
+ if (i < 2)
+ s->opt[OPT_SCANSOURCE].cap |= SANE_CAP_INACTIVE;
+
+#if 0
+ s->opt[OPT_PAPER].name = "Paper size";
+ s->opt[OPT_PAPER].title = "Paper size";
+ s->opt[OPT_PAPER].desc = "Paper size";
+ s->opt[OPT_PAPER].type = SANE_TYPE_STRING;
+ /* xxx the possible values for the paper size should be changeable,
+ to reflect the different maximum scan sizes with/without ADF and FSU
+ */
+ if (s->dev->sensedat.model == JX610)
+ {
+ s->opt[OPT_PAPER].size = max_string_size (paper_list_jx610);
+ s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER].constraint.string_list = paper_list_jx610;
+ s->val[OPT_PAPER].s = strdup (paper_list_jx610[1]);
+ }
+ else
+ {
+ s->opt[OPT_PAPER].size = max_string_size (paper_list_jx330);
+ s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER].constraint.string_list = paper_list_jx330;
+ s->val[OPT_PAPER].s = strdup (paper_list_jx330[0]);
+ }
+#endif
+
+ if (s->dev->sensedat.model == JX610)
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_jx610, OPT_PAPER, 1);
+ else
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_jx330, OPT_PAPER, 0);
+
+ /* gamma */
+#if 0
+ s->opt[OPT_GAMMA].name = "Gamma";
+ s->opt[OPT_GAMMA].title = "Gamma";
+ s->opt[OPT_GAMMA].desc = "Gamma";
+ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA].size = max_string_size (gamma_list);
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GAMMA].constraint.string_list = gamma_list;
+ s->val[OPT_GAMMA].s = strdup (gamma_list[1]);
+#endif
+
+ init_string_option(s, "Gamma", "Gamma", "Gamma", gamma_list, OPT_GAMMA, 1);
+
+ /* scan speed */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = "Scan speed [fast]";
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ s->opt[OPT_SPEED].type = SANE_TYPE_BOOL;
+ s->val[OPT_SPEED].w = SANE_TRUE;
+
+ /* Resolution Group */
+ s->opt[OPT_RESOLUTION_GROUP].title = "Resolution";
+ s->opt[OPT_RESOLUTION_GROUP].desc = "";
+ s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_RESOLUTION_GROUP].cap = 0;
+ s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* select resolution */
+#ifdef USE_RESOLUTION_LIST
+ if (s->dev->sensedat.model == JX610 || s->dev->sensedat.model == JX330 ||
+ s->dev->sensedat.model == JX350 || s->dev->sensedat.model == JX320)
+ init_string_option(s, "ResolutionList", "ResolutionList", "ResolutionList",
+ resolution_list_jx610, OPT_RESOLUTION_LIST, RESOLUTION_MAX_JX610);
+ else
+ init_string_option(s, "ResolutionList", "ResolutionList", "ResolutionList",
+ resolution_list_jx250, OPT_RESOLUTION_LIST, RESOLUTION_MAX_JX250);
+#endif
+ /* x resolution */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &s->dev->info.xres_range;
+ s->val[OPT_X_RESOLUTION].w = s->dev->info.xres_default;
+
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ /* y resolution */
+ s->opt[OPT_Y_RESOLUTION].name = "Y" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = "Y " SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &s->dev->info.yres_range;
+ s->val[OPT_Y_RESOLUTION].w = s->dev->info.yres_default;
+#endif
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->dev->info.tl_x_ranges[default_source];
+ s->val[OPT_TL_X].w = s->dev->info.tl_x_ranges[default_source].min;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->dev->info.tl_y_ranges[default_source];
+ s->val[OPT_TL_Y].w = s->dev->info.tl_y_ranges[default_source].min;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->dev->info.br_x_ranges[default_source];
+ scalar = s->dev->info.x_default;
+ clip_value (&s->opt[OPT_BR_X], &scalar);
+ s->val[OPT_BR_X].w = scalar;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->dev->info.br_y_ranges[default_source];
+ /* The FSU for JX250 allows a maximum scan length of 11.5 inch,
+ which is less than the default value of 297 mm
+ */
+ scalar = s->dev->info.y_default;
+ clip_value (&s->opt[OPT_BR_X], &scalar);
+ s->val[OPT_BR_Y].w = scalar;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* edge emphasis */
+#if 0
+ s->opt[OPT_EDGE_EMPHASIS].name = "Edge emphasis";
+ s->opt[OPT_EDGE_EMPHASIS].title = "Edge emphasis";
+ s->opt[OPT_EDGE_EMPHASIS].desc = "Edge emphasis";
+ s->opt[OPT_EDGE_EMPHASIS].type = SANE_TYPE_STRING;
+ s->opt[OPT_EDGE_EMPHASIS].size = max_string_size (edge_emphasis_list);
+ s->opt[OPT_EDGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_EDGE_EMPHASIS].constraint.string_list = edge_emphasis_list;
+ s->val[OPT_EDGE_EMPHASIS].s = strdup (edge_emphasis_list[0]);
+#endif
+ init_string_option(s, "Edge emphasis", "Edge emphasis",
+ "Edge emphasis", edge_emphasis_list,
+ OPT_EDGE_EMPHASIS, 0);
+
+ if ( s->dev->sensedat.model == JX250 || s->dev->sensedat.model == JX350
+ || s->dev->sensedat.model == JX320)
+ s->opt[OPT_EDGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD].w = 128;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].name = SANE_NAME_THRESHOLD "-red";
+ /* xxx the titles and decriptions are confusing:
+ "set white point (red)"
+ Any idea? maybe "threshold to get the red component on"
+ */
+ s->opt[OPT_THRESHOLD_R].title = SANE_TITLE_THRESHOLD " (red)";
+ s->opt[OPT_THRESHOLD_R].desc = SANE_DESC_THRESHOLD " (red)";
+ s->opt[OPT_THRESHOLD_R].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_R].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_R].w = 128;
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_THRESHOLD_G].name = SANE_NAME_THRESHOLD "-green";
+ s->opt[OPT_THRESHOLD_G].title = SANE_TITLE_THRESHOLD " (green)";
+ s->opt[OPT_THRESHOLD_G].desc = SANE_DESC_THRESHOLD " (green)";
+ s->opt[OPT_THRESHOLD_G].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_G].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_G].w = 128;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_THRESHOLD_B].name = SANE_NAME_THRESHOLD "-blue";
+ s->opt[OPT_THRESHOLD_B].title = SANE_TITLE_THRESHOLD " (blue)";
+ s->opt[OPT_THRESHOLD_B].desc = SANE_DESC_THRESHOLD " (blue)";
+ s->opt[OPT_THRESHOLD_B].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_B].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_B].w = 128;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+
+#endif
+
+ /* light color (for gray scale and line art scans) */
+#if 0
+ s->opt[OPT_LIGHTCOLOR].name = "LightColor";
+ s->opt[OPT_LIGHTCOLOR].title = "Light Color";
+ s->opt[OPT_LIGHTCOLOR].desc = "Light Color";
+ s->opt[OPT_LIGHTCOLOR].type = SANE_TYPE_STRING;
+ s->opt[OPT_LIGHTCOLOR].size = max_string_size (light_color_list);
+ s->opt[OPT_LIGHTCOLOR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_LIGHTCOLOR].constraint.string_list = light_color_list;
+ s->val[OPT_LIGHTCOLOR].s = strdup (light_color_list[3]);
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+#endif
+ init_string_option(s, "LightColor", "LightColor", "LightColor",
+ light_color_list, OPT_LIGHTCOLOR, 3);
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+
+#ifdef USE_CUSTOM_GAMMA
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+ set_gamma_caps(s);
+#endif
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (SHARP_Scanner * s)
+{
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 2, 0, 0, 0, 0};
+
+ DBG (10, "<< do_cancel ");
+
+#ifdef USE_FORK
+ if (s->reader_pid > 0)
+ {
+ int exit_status;
+ int count = 0;
+ /* ensure child knows it's time to stop: */
+
+ DBG(11, "stopping reader process\n");
+ s->rdr_ctl->cancel = 1;
+ while(reader_running(s) && count < 100)
+ {
+ usleep(100000);
+ count++;
+ };
+ if (reader_running(s))
+ {
+ /* be brutal...
+ !! The waiting time of 10 seconds might be far too short
+ !! if the resolution limit of the JX 250 is increased to
+ !! to more than 400 dpi: for these (interpolated) resolutions,
+ !! the JX 250 is awfully slow.
+ */
+ kill(s->reader_pid, SIGKILL);
+ }
+ wait(&exit_status);
+ DBG(11, "reader process stopped\n");
+
+ s->reader_pid = 0;
+ }
+
+#endif
+ if (s->scanning == SANE_TRUE)
+ {
+ wait_ready(s->fd);
+ sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
+ /* if (s->adf_scan) */
+ if ( s->dev->sensedat.model != JX610
+ && s->dev->sensedat.model != JX320)
+ object_position(s->fd, UNLOAD_PAPER);
+ }
+
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ if (s->shmid != -1)
+ shmctl(s->shmid, IPC_RMID, &ds);
+ s->shmid = -1;
+ }
+#endif
+ if (s->buffer)
+ free(s->buffer);
+ s->buffer = 0;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+static SHARP_New_Device *new_devs = 0;
+static SHARP_New_Device *new_dev_pool = 0;
+
+static SANE_Status
+attach_and_list(const char *devnam)
+{
+ SANE_Status res;
+ SHARP_Device *devp;
+ SHARP_New_Device *np;
+
+ res = attach(devnam, &devp);
+ if (res == SANE_STATUS_GOOD)
+ {
+ if (new_dev_pool)
+ {
+ np = new_dev_pool;
+ new_dev_pool = np->next;
+ }
+ else
+ {
+ np = malloc(sizeof(SHARP_New_Device));
+ if (np == 0)
+ return SANE_STATUS_NO_MEM;
+ }
+ np->next =new_devs;
+ np->dev = devp;
+ new_devs = np;
+ }
+ return res;
+}
+
+static int buffers[2] = {DEFAULT_BUFFERS, DEFAULT_BUFFERS};
+static int bufsize[2] = {DEFAULT_BUFSIZE, DEFAULT_BUFSIZE};
+static int queued_reads[2] = {DEFAULT_QUEUED_READS, DEFAULT_QUEUED_READS};
+static int stop_on_fsu_error[2] = {COMPLAIN_ON_FSU_ERROR | COMPLAIN_ON_ADF_ERROR,
+ COMPLAIN_ON_FSU_ERROR | COMPLAIN_ON_ADF_ERROR};
+static int default_scan_mode[2] = {-1, -1};
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ char line[PATH_MAX];
+ const char *lp;
+ char *word;
+ char *end;
+ FILE *fp;
+ int opt_index = 0;
+ int linecount = 0;
+#if 1
+ SHARP_Device sd;
+ SHARP_Device *dp = &sd;
+#else
+ SHARP_Device *dp;
+#endif
+ SHARP_New_Device *np;
+ int i;
+
+ DBG_INIT ();
+ DBG (10, "<< sane_init ");
+
+#if defined PACKAGE && defined VERSION
+ DBG (2, "sane_init: " PACKAGE " " VERSION "\n");
+#endif
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (SHARP_CONFIG_FILE);
+ if (!fp)
+ {
+ /* use "/dev/scanner" as the default device name if no
+ config file is available
+ */
+ attach (devnam, &dp);
+ /* make sure that there are at least two buffers */
+ if (DEFAULT_BUFFERS < 2)
+ dp->info.buffers = DEFAULT_BUFFERS;
+ else
+ dp->info.buffers = 2;
+ dp->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ dp->info.queued_reads = DEFAULT_QUEUED_READS;
+ dp->info.complain_on_errors = COMPLAIN_ON_ADF_ERROR | COMPLAIN_ON_FSU_ERROR;
+ dp->info.default_scan_mode = -1;
+ return SANE_STATUS_GOOD;
+ }
+
+ while (fgets(line, PATH_MAX, fp))
+ {
+ linecount++;
+ word = 0;
+ lp = sanei_config_get_string(line, &word);
+ if (word)
+ {
+ if (word[0] != '#')
+ {
+ if (strcmp(word, "option") == 0)
+ {
+ free(word);
+ word = 0;
+ lp = sanei_config_get_string(lp, &word);
+ if (strcmp(word, "buffers") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (end == word)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ if (i > 2)
+ buffers[opt_index] = i;
+ else
+ buffers[opt_index] = 2;
+ }
+ else if (strcmp(word, "buffersize") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ bufsize[opt_index] = i;
+ }
+ else if (strcmp(word, "readqueue") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ queued_reads[opt_index] = i;
+ }
+ else if (strcmp(word, "stop_on_fsu_error") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ stop_on_fsu_error[opt_index]
+ = i ? COMPLAIN_ON_FSU_ERROR : 0;
+ }
+ else if (strcmp(word, "default_scan_source") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ if (strcmp(word, "auto") == 0)
+ default_scan_mode[opt_index] = -1;
+ else if (strcmp(word, "fsu") == 0)
+ default_scan_mode[opt_index] = SCAN_WITH_FSU;
+ else if (strcmp(word, "adf") == 0)
+ default_scan_mode[opt_index] = SCAN_WITH_ADF;
+ else if (strcmp(word, "flatbed") == 0)
+ default_scan_mode[opt_index] = SCAN_SIMPLE;
+ else
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ }
+ else
+ {
+ DBG(1, "error in config file, line %i: unknown option\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ }
+ else
+ {
+ while (new_devs)
+ {
+ if (buffers[1] >= 2)
+ new_devs->dev->info.buffers = buffers[1];
+ else
+ new_devs->dev->info.buffers = 2;
+ if (bufsize[1] > 0)
+ new_devs->dev->info.wanted_bufsize = bufsize[1];
+ else
+ new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ if (queued_reads[1] >= 0)
+ new_devs->dev->info.queued_reads = queued_reads[1];
+ else
+ new_devs->dev->info.queued_reads = 0;
+ new_devs->dev->info.complain_on_errors = stop_on_fsu_error[1];
+ new_devs->dev->info.default_scan_mode = default_scan_mode[1];
+ np = new_devs->next;
+ new_devs->next = new_dev_pool;
+ new_dev_pool = new_devs;
+ new_devs = np;
+ }
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ sanei_config_attach_matching_devices(line, &attach_and_list);
+ buffers[1] = buffers[0];
+ bufsize[1] = bufsize[0];
+ queued_reads[1] = queued_reads[0];
+ stop_on_fsu_error[1] = stop_on_fsu_error[0];
+ default_scan_mode[1] = default_scan_mode[0];
+ opt_index = 1;
+ }
+ }
+ if (word) free(word);
+ }
+ }
+
+ while (new_devs)
+ {
+ if (buffers[1] >= 2)
+ new_devs->dev->info.buffers = buffers[1];
+ else
+ new_devs->dev->info.buffers = 2;
+ if (bufsize[1] > 0)
+ new_devs->dev->info.wanted_bufsize = bufsize[1];
+ else
+ new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ if (queued_reads[1] >= 0)
+ new_devs->dev->info.queued_reads = queued_reads[1];
+ else
+ new_devs->dev->info.queued_reads = 0;
+ new_devs->dev->info.complain_on_errors = stop_on_fsu_error[1];
+ new_devs->dev->info.default_scan_mode = default_scan_mode[1];
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ np = new_devs->next;
+ free(new_devs);
+ new_devs = np;
+ }
+ while (new_dev_pool)
+ {
+ np = new_dev_pool->next;
+ free(new_dev_pool);
+ new_dev_pool = np;
+ }
+ fclose(fp);
+ DBG (10, "sane_init >>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static const SANE_Device **devlist = 0;
+void
+sane_exit (void)
+{
+ SHARP_Device *dev, *next;
+ DBG (10, "<< sane_exit ");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+
+ if (devlist)
+ free(devlist);
+ devlist = 0;
+ first_dev = 0;
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ SHARP_Device *dev;
+ int i;
+ DBG (10, "<< sane_get_devices ");
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ SHARP_Device *dev;
+ SHARP_Scanner *s;
+#ifdef USE_CUSTOM_GAMMA
+ int i, j;
+#endif
+
+ DBG (10, "<< sane_open ");
+
+ if (devnam[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ dev->info.buffers = buffers[0];
+ dev->info.wanted_bufsize = bufsize[0];
+ dev->info.queued_reads = queued_reads[0];
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->dev = dev;
+
+ s->buffer = 0;
+#ifdef USE_CUSTOM_GAMMA
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 256; ++j)
+ s->gamma_table[i][j] = j;
+#endif
+ status = init_options (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* xxx clean up mallocs */
+ return status;
+ }
+
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ SHARP_Scanner *s = (SHARP_Scanner *) handle;
+ DBG (10, "<< sane_close ");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ if (s->shmid != -1)
+ shmctl(s->shmid, IPC_RMID, &ds);
+ }
+#endif
+ if (s->buffer)
+ free(s->buffer);
+ free (s);
+
+ DBG (10, ">>\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ SHARP_Scanner *s = handle;
+ DBG (10, "<< sane_get_option_descriptor ");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (10, ">>\n");
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+#ifdef USE_CUSTOM_GAMMA
+ SANE_Word w, cap;
+#else
+ SANE_Word cap;
+#endif
+#ifdef USE_RESOLUTION_LIST
+ int i;
+#endif
+ int range_index;
+ DBG (10, "<< sane_control_option %i", option);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_X_RESOLUTION:
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ case OPT_Y_RESOLUTION:
+#endif
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_SPEED:
+ case OPT_PREVIEW:
+#ifdef USE_CUSTOM_GAMMA
+ case OPT_CUSTOM_GAMMA:
+#endif
+ *(SANE_Word *) val = s->val[option].w;
+#if 0 /* here, values are read; reload should not be necessary */
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+#endif
+ return (SANE_STATUS_GOOD);
+
+#ifdef USE_CUSTOM_GAMMA
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+#endif
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_HALFTONE:
+ case OPT_PAPER:
+ case OPT_GAMMA:
+#ifdef USE_RESOLUTION_LIST
+ case OPT_RESOLUTION_LIST:
+#endif
+ case OPT_EDGE_EMPHASIS:
+ case OPT_LIGHTCOLOR:
+ case OPT_SCANSOURCE:
+ strcpy (val, s->val[option].s);
+#if 0
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+#endif
+
+ return (SANE_STATUS_GOOD);
+
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_X_RESOLUTION:
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ case OPT_Y_RESOLUTION:
+#endif
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+ /* xxx theoretically, we could use OPT_THRESHOLD in
+ bi-level color mode to adjust all three other
+ threshold together. But this would require to set
+ the bit SANE_INFO_RELOAD_OPTIONS in *info, and that
+ would unfortunately cause a crash in both xscanimage
+ and xsane... Therefore, OPT_THRESHOLD is disabled
+ for bi-level color scan right now.
+ */
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_SPEED:
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+
+ case OPT_MODE:
+ if (strcmp (val, M_LINEART) == 0)
+ {
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ if (s->dev->sensedat.model == JX330)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap &= ~SANE_CAP_INACTIVE;
+#endif
+ if (s->dev->sensedat.model == JX330)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ if ( strcmp (val, M_LINEART) == 0
+ || strcmp (val, M_GRAY) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ }
+
+ strcpy(s->val[option].s, val);
+#ifdef USE_CUSTOM_GAMMA
+ set_gamma_caps(s);
+#endif
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_GAMMA:
+ case OPT_HALFTONE:
+ case OPT_EDGE_EMPHASIS:
+ case OPT_LIGHTCOLOR:
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_SCANSOURCE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ if (strcmp(val, use_fsu) == 0)
+ range_index = SCAN_WITH_FSU;
+ else if (strcmp(val, use_adf) == 0)
+ range_index = SCAN_WITH_ADF;
+ else
+ range_index = SCAN_SIMPLE;
+
+ s->opt[OPT_TL_X].constraint.range
+ = &s->dev->info.tl_x_ranges[range_index];
+ clip_value (&s->opt[OPT_TL_X], &s->val[OPT_TL_X].w);
+
+ s->opt[OPT_TL_Y].constraint.range
+ = &s->dev->info.tl_y_ranges[range_index];
+ clip_value (&s->opt[OPT_TL_Y], &s->val[OPT_TL_Y].w);
+
+ s->opt[OPT_BR_X].constraint.range
+ = &s->dev->info.br_x_ranges[range_index];
+ clip_value (&s->opt[OPT_BR_X], &s->val[OPT_BR_X].w);
+
+ s->opt[OPT_BR_Y].constraint.range
+ = &s->dev->info.br_y_ranges[range_index];
+ clip_value (&s->opt[OPT_BR_Y], &s->val[OPT_BR_Y].w);
+
+ return (SANE_STATUS_GOOD);
+
+ case OPT_PAPER:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ s->val[OPT_TL_X].w = SANE_FIX(0);
+ s->val[OPT_TL_Y].w = SANE_FIX(0);
+ if (strcmp (s->val[option].s, "A3") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(297);
+ s->val[OPT_BR_Y].w = SANE_FIX(420);
+ }else if (strcmp (s->val[option].s, "A4") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(210);
+ s->val[OPT_BR_Y].w = SANE_FIX(297);
+ }else if (strcmp (s->val[option].s, "A5") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(148.5);
+ s->val[OPT_BR_Y].w = SANE_FIX(210);
+ }else if (strcmp (s->val[option].s, "A6") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(105);
+ s->val[OPT_BR_Y].w = SANE_FIX(148.5);
+ }else if (strcmp (s->val[option].s, "B4") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(250);
+ s->val[OPT_BR_Y].w = SANE_FIX(353);
+ }else if (strcmp (s->val[option].s, "B5") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(182);
+ s->val[OPT_BR_Y].w = SANE_FIX(257);
+ }else if (strcmp (s->val[option].s, W_LETTER) == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(279.4);
+ s->val[OPT_BR_Y].w = SANE_FIX(431.8);
+ }else if (strcmp (s->val[option].s, "Legal") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(355.6);
+ }else if (strcmp (s->val[option].s, "Letter") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(279.4);
+ }else if (strcmp (s->val[option].s, INVOICE) == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(139.7);
+ }else{
+ }
+ return (SANE_STATUS_GOOD);
+
+#ifdef USE_RESOLUTION_LIST
+ case OPT_RESOLUTION_LIST:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ for (i = 0; s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]; i++) {
+ if (strcmp (val,
+ s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]) == 0){
+ s->val[OPT_X_RESOLUTION].w
+ = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]);
+ s->val[OPT_Y_RESOLUTION].w
+ = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+#endif
+#ifdef USE_CUSTOM_GAMMA
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ set_gamma_caps(s);
+ return SANE_STATUS_GOOD;
+#endif
+ }
+ }
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_INVAL);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int width, length, xres, yres;
+ const char *mode;
+ SHARP_Scanner *s = handle;
+ DBG (10, "<< sane_get_parameters ");
+
+ xres = s->val[OPT_X_RESOLUTION].w;
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ yres = s->val[OPT_Y_RESOLUTION].w;
+#else
+ yres = xres;
+#endif
+ if (!s->scanning)
+ {
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_X].w)
+ - SANE_UNFIX(s->val[OPT_TL_X].w),
+ s->dev->info.mud);
+ length = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_Y].w)
+ - SANE_UNFIX(s->val[OPT_TL_Y].w),
+ s->dev->info.mud);
+
+ s->width = width;
+ s->length = length;
+ s->params.pixels_per_line = width * xres / s->dev->info.mud;
+ s->params.lines = length * yres / s->dev->info.mud;
+ s->unscanned_lines = s->params.lines;
+ }
+ else
+ {
+ static u_char cmd[] = {READ, 0, 0x81, 0, 0, 0, 0, 0, 4, 0};
+ static u_char buf[4];
+ size_t len = 4;
+ SANE_Status status;
+
+ /* if async reads are used, )ie. if USE_FORK is defined,
+ this command may only be issued immediately after the
+ "start scan" command. Later calls will confuse the
+ read queue.
+ */
+ if (!s->get_params_called)
+ {
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), buf, &len);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel(s);
+ return (status);
+ }
+ s->params.pixels_per_line = (buf[1] << 8) + buf[0];
+ s->params.lines = (buf[3] << 8) + buf[2];
+ s->get_params_called = 1;
+ }
+ }
+
+ xres = s->val[OPT_X_RESOLUTION].w;
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ yres = s->val[OPT_Y_RESOLUTION].w;
+#else
+ yres = xres;
+#endif
+
+ mode = s->val[OPT_MODE].s;
+
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ s->modes = MODES_LINEART;
+ }
+ else if (strcmp (mode, M_GRAY) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->modes = MODES_GRAY;
+ }
+ else
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->modes = MODES_COLOR;
+ }
+ s->params.last_frame = SANE_TRUE;
+
+ if (params)
+ *params = s->params;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+#ifdef USE_CUSTOM_GAMMA
+
+static int
+sprint_gamma(Option_Value val, SANE_Byte *dst)
+{
+ int i;
+ SANE_Byte *p = dst;
+
+ p += sprintf((char *) p, "%i", val.wa[0] > 255 ? 255 : val.wa[0]);
+ /* val.wa[i] is over 255, so val.wa[i] is limitied to 255 */
+ for (i = 1; i < 256; i++)
+ p += sprintf((char *) p, ",%i", val.wa[i] > 255 ? 255 : val.wa[i]);
+ return p - dst;
+}
+
+static SANE_Status
+send_ascii_gamma_tables (SHARP_Scanner *s)
+{
+ SANE_Status status;
+ int i;
+
+ DBG(11, "<< send_ascii_gamma_tables ");
+
+ /* we need: 4 bytes for each gamma value (3 digits + delimiter)
+ + 10 bytes for the command header
+ i.e. 4 * 4 * 256 + 10 = 4106 bytes
+ */
+
+ if (s->dev->info.bufsize < 4106)
+ return SANE_STATUS_NO_MEM;
+
+ memset(s->buffer, 0, 4106);
+
+ i = sprint_gamma(s->val[OPT_GAMMA_VECTOR_R], &s->buffer[10]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_G], &s->buffer[10+i]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_B], &s->buffer[10+i]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR], &s->buffer[10+i]);
+
+ DBG(11, "%s\n", &s->buffer[10]);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[7] = i >> 8;
+ s->buffer[8] = i & 0xff;
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+#endif
+
+static SANE_Status
+send_binary_g_table(SHARP_Scanner *s, SANE_Word *a, int dtq)
+{
+ SANE_Status status;
+ int i;
+
+ DBG(11, "<< send_binary_g_table\n");
+
+ memset(s->buffer, 0, 522);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[5] = dtq;
+ s->buffer[7] = 2;
+ s->buffer[8] = 0;
+
+ for (i = 0; i < 256; i++)
+ {
+ s->buffer[2*i+11] = a[i] > 255 ? 255 : a[i];
+ }
+
+ for (i = 0; i < 256; i += 16)
+ {
+ DBG(11, "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ a[i ], a[i+1], a[i+2], a[i+3],
+ a[i+4], a[i+5], a[i+6], a[i+7],
+ a[i+8], a[i+9], a[i+10], a[i+11],
+ a[i+12], a[i+13], a[i+14], a[i+15]);
+ }
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, s->buffer, 2*i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+
+#ifdef USE_CUSTOM_GAMMA
+static SANE_Status
+send_binary_gamma_tables (SHARP_Scanner *s)
+{
+ SANE_Status status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR].wa, 0x10);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_R].wa, 0x11);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_G].wa, 0x12);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_B].wa, 0x13);
+
+ return status;
+}
+
+static SANE_Status
+send_gamma_tables (SHARP_Scanner *s)
+{
+ if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350)
+ {
+ return send_ascii_gamma_tables(s);
+ }
+ else
+ {
+ return send_binary_gamma_tables(s);
+ }
+
+}
+#endif
+
+#ifdef USE_COLOR_THRESHOLD
+static SANE_Status
+send_threshold_data(SHARP_Scanner *s)
+{
+ SANE_Status status;
+ SANE_Byte cmd[26] = {SEND, 0, 0x82, 0, 0, 0, 0, 0, 0, 0};
+ int len;
+
+ memset(cmd, 0, sizeof(cmd));
+ /* maximum string length: 3 bytes for each number (they are
+ restricted to the range 0..255), 3 '/' and the null-byte,
+ total: 16 bytes.
+ */
+ len = sprintf((char *) &cmd[10], "%i/%i/%i/%i",
+ s->val[OPT_THRESHOLD_R].w,
+ s->val[OPT_THRESHOLD_G].w,
+ s->val[OPT_THRESHOLD_B].w,
+ s->val[OPT_THRESHOLD].w);
+ cmd[8] = len;
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd(s->fd, cmd, len + 10, 0, 0);
+ return status;
+}
+#endif
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char *mode, *halftone, *paper, *gamma, *edge, *lightcolor, *adf_fsu;
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+ size_t buf_size;
+ SHARP_Send ss;
+ window_param wp;
+ mode_sense_subdevice m_subdev;
+
+ DBG (10, "<< sane_start ");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->dev->sensedat.complain_on_errors
+ = COMPLAIN_ON_ADF_ERROR | s->dev->info.complain_on_errors;
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ s->dev->info.bufsize = s->dev->info.wanted_bufsize;
+ if (s->dev->info.bufsize < 32 * 1024)
+ s->dev->info.bufsize = 32 * 1024;
+ {
+ int bsize = s->dev->info.bufsize;
+ status = sanei_scsi_open_extended (s->dev->sane.name, &s->fd,
+ &sense_handler, &s->dev->sensedat, &bsize);
+ s->dev->info.bufsize = bsize;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ return (status);
+ }
+
+ /* make sure that we got at least 32 kB. Even then, the scan will be
+ awfully slow.
+
+ NOTE: If you need to decrease this value, remember that s->buffer
+ is used in send_ascii_gamma_tables (JX330/JX610) and in
+ send_binary_g_table (JX250/JX350). send_ascii_gamma_tables needs 4106
+ bytes, and send_binary_g_table needs 522 bytes.
+ */
+ if (s->dev->info.bufsize < 32 * 1024)
+ {
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+#else
+ status = sanei_scsi_open(s->dev->sane.name, &s->fd, &sense_handler,
+ &s->dev->sensedat);
+ if (s->dev->info.wanted_bufsize < sanei_scsi_max_request_size)
+ s->dev->info.bufsize = s->dev->info.wanted_bufsize;
+ else
+ s->dev->info.bufsize = sanei_scsi_max_request_size;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ return (status);
+ }
+#endif
+
+ s->buffer = malloc(s->dev->info.bufsize);
+ if (!s->buffer) {
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ free(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ size_t n;
+
+ s->shmid = shmget(IPC_PRIVATE,
+ sizeof(SHARP_rdr_ctl)
+ + s->dev->info.buffers *
+ (sizeof(SHARP_shmem_ctl) + s->dev->info.bufsize),
+ IPC_CREAT | 0600);
+ if (s->shmid == -1)
+ {
+ free(s->buffer);
+ s->buffer = 0;
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+ s->rdr_ctl = (SHARP_rdr_ctl*) shmat(s->shmid, 0, 0);
+ if (s->rdr_ctl == (void *) -1)
+ {
+ shmctl(s->shmid, IPC_RMID, &ds);
+ free(s->buffer);
+ s->buffer = 0;
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->rdr_ctl->buf_ctl = (SHARP_shmem_ctl*) &s->rdr_ctl[1];
+ for (n = 0; n < s->dev->info.buffers; n++)
+ {
+ s->rdr_ctl->buf_ctl[n].buffer =
+ (SANE_Byte*) &s->rdr_ctl->buf_ctl[s->dev->info.buffers]
+ + n * s->dev->info.bufsize;
+ }
+ }
+#endif /* USE_FORK */
+
+ DBG (5, "start: TEST_UNIT_READY\n");
+ status = test_unit_ready (s->fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "TEST UNIT READY failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ DBG (3, "start: sending MODE SELECT\n");
+ status = mode_select_mud (s->fd, s->dev->info.mud);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start: MODE_SELECT6 failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ mode = s->val[OPT_MODE].s;
+ halftone = s->val[OPT_HALFTONE].s;
+ paper = s->val[OPT_PAPER].s;
+ gamma = s->val[OPT_GAMMA].s;
+ edge = s->val[OPT_EDGE_EMPHASIS].s;
+ lightcolor = s->val[OPT_LIGHTCOLOR].s;
+ adf_fsu = s->val[OPT_SCANSOURCE].s;
+ s->speed = s->val[OPT_SPEED].w;
+
+ s->xres = s->val[OPT_X_RESOLUTION].w;
+ if (s->val[OPT_PREVIEW].w == SANE_FALSE)
+ {
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ s->yres = s->val[OPT_Y_RESOLUTION].w;
+#else
+ s->yres = s->val[OPT_X_RESOLUTION].w;
+#endif
+ s->speed = s->val[OPT_SPEED].w;
+ }
+ else
+ {
+ s->yres = s->val[OPT_X_RESOLUTION].w;
+ s->speed = SANE_TRUE;
+ }
+
+ s->ulx = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_X].w), s->dev->info.mud);
+ s->uly = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_Y].w), s->dev->info.mud);
+ s->threshold = s->val[OPT_THRESHOLD].w;
+ s->bpp = s->params.depth;
+
+ s->adf_fsu_mode = SCAN_SIMPLE; /* default: scan without ADF and FSU */
+#ifdef ALLOW_AUTO_SELECT_ADF
+ if (strcmp (adf_fsu, use_auto) == 0)
+ s->adf_fsu_mode = SCAN_ADF_FSU_AUTO;
+ else
+#endif
+ if (strcmp(adf_fsu, use_fsu) == 0)
+ s->adf_fsu_mode = SCAN_WITH_FSU;
+ else if (strcmp(adf_fsu, use_adf) == 0)
+ s->adf_fsu_mode = SCAN_WITH_ADF;
+ else if (strcmp(adf_fsu, use_adf) == 0)
+ s->adf_fsu_mode = SCAN_SIMPLE;
+
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->reverse = 0;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->halftone = 1;
+ s->image_composition = 0;
+ }
+ else if (strcmp(halftone, M_BAYER) == 0)
+ {
+ s->halftone = 2;
+ s->image_composition = 1;
+ }
+ else if (strcmp(halftone, M_SPIRAL) == 0)
+ {
+ s->halftone = 3;
+ s->image_composition = 1;
+ }
+ else if (strcmp(halftone, M_DISPERSED) == 0)
+ {
+ s->halftone = 4;
+ s->image_composition = 1;
+ }
+ else if (strcmp(halftone, M_ERRDIFFUSION) == 0)
+ {
+ s->halftone = 5;
+ s->image_composition = 1;
+ }
+ }
+ else if (strcmp (mode, M_GRAY) == 0)
+ {
+ s->image_composition = 2;
+ s->reverse = 1;
+ }
+ else if (strcmp (mode, M_LINEART_COLOR) == 0)
+ {
+ s->reverse = 1;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->halftone = 1;
+ s->image_composition = 3;
+ }
+ else if (strcmp(halftone, M_BAYER) == 0)
+ {
+ s->halftone = 2;
+ s->image_composition = 4;
+ }
+ else if (strcmp(halftone, M_SPIRAL) == 0)
+ {
+ s->halftone = 3;
+ s->image_composition = 4;
+ }
+ else if (strcmp(halftone, M_DISPERSED) == 0)
+ {
+ s->halftone = 4;
+ s->image_composition = 4;
+ }
+ else if (strcmp(halftone, M_ERRDIFFUSION) == 0)
+ {
+ s->halftone = 5;
+ s->image_composition = 4;
+ }
+ }
+ else if (strcmp (mode, M_COLOR) == 0)
+ {
+ s->image_composition = 5;
+ s->reverse = 1;
+ }
+
+ if (strcmp (edge, EDGE_NONE) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS NONE\n");
+ s->edge = 0;
+ }
+ else if (strcmp (edge, EDGE_MIDDLE) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS MIDDLE\n");
+ s->edge = 1;
+ }
+ else if (strcmp (edge, EDGE_STRONG) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS STRONG\n");
+ s->edge = 2;
+ }
+ else if (strcmp (edge, EDGE_BLUR) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS BLUR\n");
+ s->edge = 3;
+ }
+
+ s->lightcolor = 3;
+ if (strcmp(lightcolor, LIGHT_GREEN) == 0)
+ s->lightcolor = 0;
+ else if (strcmp(lightcolor, LIGHT_RED) == 0)
+ s->lightcolor = 1;
+ else if (strcmp(lightcolor, LIGHT_BLUE) == 0)
+ s->lightcolor = 2;
+ else if (strcmp(lightcolor, LIGHT_WHITE) == 0)
+ s->lightcolor = 3;
+
+ s->adf_scan = 0;
+ if ( s->dev->sensedat.model != JX610
+ && s->dev->sensedat.model != JX320)
+ {
+ status = mode_select_adf_fsu(s->fd, s->adf_fsu_mode);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (10, "sane_start: mode_select_adf_fsu failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ /* if the ADF is selected, check if it is ready */
+ memset (&m_subdev, 0, sizeof (m_subdev));
+ buf_size = sizeof (m_subdev);
+ status = mode_sense (s->fd, &m_subdev, &buf_size, 0x20);
+ DBG(11, "mode sense result a_mode: %x f_mode: %x\n",
+ m_subdev.a_mode_type, m_subdev.f_mode_type);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (10, "sane_start: MODE_SENSE/subdevice page failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ if (s->adf_fsu_mode == SCAN_WITH_ADF)
+ s->adf_scan = 1;
+#ifdef ALLOW_AUTO_SELECT_ADF
+ else if (s->adf_fsu_mode == SCAN_ADF_FSU_AUTO)
+ {
+ if (m_subdev.a_mode_type & 0x80)
+ s->adf_scan = 1;
+ }
+#endif
+ }
+
+
+#ifdef USE_CUSTOM_GAMMA
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+#endif
+ if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350)
+ {
+ ss.dtc = 0x03;
+ if (strcmp (gamma, GAMMA10) == 0)
+ {
+ ss.dtq = 0x01;
+ }else{
+ ss.dtq = 0x02;
+ }
+ ss.length = 0;
+ DBG (5, "start: SEND\n");
+ status = send (s->fd, &ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ }
+ else
+ {
+ /* the JX250 does not support the "fixed gamma selection",
+ therefore, lets calculate & send gamma values
+ */
+ int i;
+ SANE_Word gtbl[256];
+ if (strcmp (gamma, GAMMA10) == 0)
+ for (i = 0; i < 256; i++)
+ gtbl[i] = i;
+ else
+ {
+ gtbl[0] = 0;
+ for (i = 1; i < 256; i++)
+ gtbl[i] = 255 * exp(0.45 * log(i/255.0));
+ }
+ send_binary_g_table(s, gtbl, 0x10);
+ send_binary_g_table(s, gtbl, 0x11);
+ send_binary_g_table(s, gtbl, 0x12);
+ send_binary_g_table(s, gtbl, 0x13);
+ }
+#ifdef USE_CUSTOM_GAMMA
+ }
+ else
+ status = send_gamma_tables(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#endif
+
+ if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350)
+ {
+ ss.dtc = 0x86;
+ ss.dtq = 0x05;
+ ss.length = 0;
+ DBG (5, "start: SEND\n");
+ status = send (s->fd, &ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+#ifdef USE_COLOR_THRESHOLD
+ status = send_threshold_data(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send threshold data failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#endif
+ }
+
+ memset (&wp, 0, sizeof (wp));
+ /* every Sharp scanner seems to have a different
+ window descriptor block...
+ */
+ if ( s->dev->sensedat.model == JX610
+ || s->dev->sensedat.model == JX320)
+ {
+ buf_size = sizeof(WDB);
+ }
+ else if (s->dev->sensedat.model == JX330)
+ {
+ buf_size = sizeof (WDB) + sizeof(WDBX330);
+ }
+ else
+ {
+ buf_size = sizeof (WDB) + sizeof(WDBX330) + sizeof(WDBX250);
+ }
+
+ wp.wpdh.wdl[0] = buf_size >> 8;
+ wp.wpdh.wdl[1] = buf_size;
+ wp.wdb.x_res[0] = s->xres >> 8;
+ wp.wdb.x_res[1] = s->xres;
+ wp.wdb.y_res[0] = s->yres >> 8;
+ wp.wdb.y_res[1] = s->yres;
+ wp.wdb.x_ul[0] = s->ulx >> 24;
+ wp.wdb.x_ul[1] = s->ulx >> 16;
+ wp.wdb.x_ul[2] = s->ulx >> 8;
+ wp.wdb.x_ul[3] = s->ulx;
+ wp.wdb.y_ul[0] = s->uly >> 24;
+ wp.wdb.y_ul[1] = s->uly >> 16;
+ wp.wdb.y_ul[2] = s->uly >> 8;
+ wp.wdb.y_ul[3] = s->uly;
+ wp.wdb.width[0] = s->width >> 24;
+ wp.wdb.width[1] = s->width >> 16;
+ wp.wdb.width[2] = s->width >> 8;
+ wp.wdb.width[3] = s->width;
+ wp.wdb.length[0] = s->length >> 24;
+ wp.wdb.length[1] = s->length >> 16;
+ wp.wdb.length[2] = s->length >> 8;
+ wp.wdb.length[3] = s->length;
+ wp.wdb.brightness = 0;
+ wp.wdb.threshold = s->threshold;
+ wp.wdb.image_composition = s->image_composition;
+ if (s->image_composition <= 2 || s->image_composition >= 5)
+ wp.wdb.bpp = s->bpp;
+ else
+ wp.wdb.bpp = 1;
+ wp.wdb.ht_pattern[0] = 0;
+ if ( s->dev->sensedat.model == JX610
+ || s->dev->sensedat.model == JX320)
+ {
+ wp.wdb.ht_pattern[1] = 0;
+ }else{
+ wp.wdb.ht_pattern[1] = s->halftone;
+ }
+ wp.wdb.rif_padding = (s->reverse * 128) + 0;
+ wp.wdb.eletu = (!s->speed << 2) + (s->edge << 6) + (s->lightcolor << 4);
+
+ if (s->dev->sensedat.model == JX250 || s->dev->sensedat.model == JX350)
+ {
+ wp.wdbx250.threshold_red = s->val[OPT_THRESHOLD_R].w;
+ wp.wdbx250.threshold_green = s->val[OPT_THRESHOLD_G].w;
+ wp.wdbx250.threshold_blue = s->val[OPT_THRESHOLD_B].w;
+ }
+
+
+ DBG (5, "wdl=%d\n", (wp.wpdh.wdl[0] << 8) + wp.wpdh.wdl[1]);
+ DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
+ DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
+ DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
+ (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
+ DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
+ (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
+ DBG (5, "width=%d\n", (wp.wdb.width[0] << 8) + (wp.wdb.width[1] << 16) +
+ (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
+ DBG (5, "length=%d\n", (wp.wdb.length[0] << 16) + (wp.wdb.length[1] << 16) +
+ (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
+
+ DBG (5, "threshold=%d\n", wp.wdb.threshold);
+ DBG (5, "image_composition=%d\n", wp.wdb.image_composition);
+ DBG (5, "bpp=%d\n", wp.wdb.bpp);
+ DBG (5, "rif_padding=%d\n", wp.wdb.rif_padding);
+ DBG (5, "eletu=%d\n", wp.wdb.eletu);
+
+#if 0
+ {
+ unsigned char *p = (unsigned char*) &wp.wdb;
+ int i;
+ DBG(11, "set window:\n");
+ for (i = 0; i < sizeof(wp.wdb) + + sizeof(wp.wdbx330) + sizeof(wp.wdbx250); i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+
+ buf_size += sizeof(WPDH);
+ DBG (5, "start: SET WINDOW\n");
+ status = set_window (s->fd, &wp, buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ memset (&wp, 0, buf_size);
+ DBG (5, "start: GET WINDOW\n");
+ status = get_window (s->fd, &wp, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
+ DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
+ DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
+ (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
+ DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
+ (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
+ DBG (5, "width=%d\n", (wp.wdb.width[0] << 24) + (wp.wdb.width[1] << 16) +
+ (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
+ DBG (5, "length=%d\n", (wp.wdb.length[0] << 24) + (wp.wdb.length[1] << 16) +
+ (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
+
+ if (s->adf_scan)
+ {
+ status = object_position(s->fd, LOAD_PAPER);
+ if (status != SANE_STATUS_GOOD)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ s->busy = SANE_FALSE;
+ s->cancel = SANE_FALSE;
+ return (status);
+ }
+ }
+
+ DBG (5, "start: SCAN\n");
+ s->scanning = SANE_TRUE;
+ s->busy = SANE_TRUE;
+ s->cancel = SANE_FALSE;
+ s->get_params_called = 0;
+
+ wait_ready(s->fd);
+ status = scan (s->fd);
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: scan started %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
+ do_cancel(s);
+ return (status);
+ }
+
+ /* ask the scanner for the scan size */
+ /* wait_ready(s->fd); */
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: wait_ready ok %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ sane_get_parameters(s, 0);
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: get_params ok %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (strcmp (mode, M_LINEART_COLOR) != 0)
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ else
+ {
+ s->bytes_to_read = (s->params.pixels_per_line+7) / 8;
+ s->bytes_to_read *= 3 * s->params.lines;
+ }
+
+#ifdef USE_FORK
+ {
+ size_t i;
+ for (i = 0; i < s->dev->info.buffers; i++)
+ s->rdr_ctl->buf_ctl[i].shm_status = SHM_EMPTY;
+ s->read_buff = 0;
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->running = 0;
+ s->rdr_ctl->status = SANE_STATUS_GOOD;
+ }
+ s->reader_pid = fork();
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: forked %li.%06li %i\n", t.tv_sec, t.tv_usec,
+ s->reader_pid);
+ }
+#endif
+ if (s->reader_pid == 0)
+ {
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ /* don't use exit() since that would run the atexit() handlers... */
+ _exit (reader_process (s));
+ }
+ else if (s->reader_pid == -1)
+ {
+ s->busy = SANE_FALSE;
+ do_cancel(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+#endif /* USE_FORK */
+
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_X_RESOLUTION].w);
+
+ s->busy = SANE_FALSE;
+ s->buf_used = 0;
+ s->buf_pos = 0;
+
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ DBG (10, ">>\n");
+ return(SANE_STATUS_CANCELLED);
+ }
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+
+}
+
+static SANE_Status
+sane_read_direct (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ DBG (10, "<< sane_read_direct ");
+
+ DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
+ *len = 0;
+
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ return (do_cancel (s));
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ if (nread > s->dev->info.bufsize)
+ nread = s->dev->info.bufsize;
+#ifdef USE_FORK
+ status = read_data(s, dst_buf, &nread);
+#else
+ wait_ready(s->fd);
+ status = read_data (s, dst_buf, &nread);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+ DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+sane_read_shuffled (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len, int eight_bit_data)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Byte *dest, *red, *green, *blue, mask;
+ SANE_Int transfer;
+ size_t nread, ntest, pixel, max_pixel, line, max_line;
+ size_t start_input, bytes_per_line_in;
+ DBG (10, "<< sane_read_shuffled ");
+
+ *len = 0;
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ {
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ {
+ DBG (10, ">>\n");
+ return(do_cancel(s));
+ }
+
+ if (s->buf_pos < s->buf_used)
+ {
+ transfer = s->buf_used - s->buf_pos;
+ if (transfer > max_len)
+ transfer = max_len;
+
+ memcpy(dst_buf, &(s->buffer[s->buf_pos]), transfer);
+ s->buf_pos += transfer;
+ max_len -= transfer;
+ *len = transfer;
+ }
+
+ while (max_len > 0 && s->bytes_to_read > 0)
+ {
+ if (eight_bit_data)
+ {
+ nread = s->dev->info.bufsize / s->params.bytes_per_line - 1;
+ nread *= s->params.bytes_per_line;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ max_line = nread / s->params.bytes_per_line;
+ start_input = s->params.bytes_per_line;
+ bytes_per_line_in = s->params.bytes_per_line;
+ }
+ else
+ {
+ bytes_per_line_in = (s->params.pixels_per_line + 7) / 8;
+ bytes_per_line_in *= 3;
+ max_line = s->params.bytes_per_line + bytes_per_line_in;
+ max_line = s->dev->info.bufsize / max_line;
+ nread = max_line * bytes_per_line_in;
+ if (nread > s->bytes_to_read)
+ {
+ nread = s->bytes_to_read;
+ max_line = nread / bytes_per_line_in;
+ }
+ start_input = s->dev->info.bufsize - nread;
+ }
+ ntest = nread;
+
+#ifdef USE_FORK
+ status = read_data (s, &(s->buffer[start_input]), &nread);
+#else
+ wait_ready(s->fd);
+ status = read_data (s, &(s->buffer[start_input]), &nread);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (nread != ntest)
+ {
+ /* if this happens, something is wrong in the input buffer
+ management...
+ */
+ DBG(1, "Warning: could not read an integral number of scan lines\n");
+ DBG(1, " image will be scrambled\n");
+ }
+
+
+ s->buf_used = max_line * s->params.bytes_per_line;
+ s->buf_pos = 0;
+ s->bytes_to_read -= nread;
+ dest = s->buffer;
+ max_pixel = s->params.pixels_per_line;
+
+ if (eight_bit_data)
+ for (line = 1; line <= max_line; line++)
+ {
+ red = &(s->buffer[line * s->params.bytes_per_line]);
+ green = &(red[max_pixel]);
+ blue = &(green[max_pixel]);
+ for (pixel = 0; pixel < max_pixel; pixel++)
+ {
+ *dest++ = *red++;
+ *dest++ = *green++;
+ *dest++ = *blue++;
+ }
+ }
+ else
+ for (line = 0; line < max_line; line++)
+ {
+ red = &(s->buffer[start_input + line * bytes_per_line_in]);
+ green = &(red[(max_pixel+7)/8]);
+ blue = &(green[(max_pixel+7)/8]);
+ mask = 0x80;
+ for (pixel = 0; pixel < max_pixel; pixel++)
+ {
+ *dest++ = (*red & mask) ? 0xff : 0;
+ *dest++ = (*green & mask) ? 0xff : 0;
+ *dest++ = (*blue & mask) ? 0xff : 0;
+ mask = mask >> 1;
+ if (mask == 0)
+ {
+ mask = 0x80;
+ red++;
+ green++;
+ blue++;
+ }
+ }
+ }
+
+ transfer = max_len;
+ if (transfer > s->buf_used)
+ transfer = s->buf_used;
+ memcpy(&(dst_buf[*len]), s->buffer, transfer);
+
+ max_len -= transfer;
+ s->buf_pos += transfer;
+ *len += transfer;
+ }
+
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+
+ s->busy = SANE_TRUE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ *len = 0;
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ /* RGB scans with a JX 250 and bi-level color scans
+ must be handled differently: */
+ if (s->image_composition <= 2)
+ status = sane_read_direct(handle, dst_buf, max_len, len);
+ else if (s->image_composition <= 4)
+ status = sane_read_shuffled(handle, dst_buf, max_len, len, 0);
+ else if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350 )
+ status = sane_read_direct(handle, dst_buf, max_len, len);
+ else
+ status = sane_read_shuffled(handle, dst_buf, max_len, len, 1);
+
+ s->busy = SANE_FALSE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ return (status);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ SHARP_Scanner *s = handle;
+ DBG (10, "<< sane_cancel ");
+
+ s->cancel = SANE_TRUE;
+ if (s->busy == SANE_FALSE)
+ do_cancel(s);
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ DBG (10, "<< sane_set_io_mode");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ * fd)
+{
+ DBG (10, "<< sane_get_select_fd");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/sharp.conf.in b/backend/sharp.conf.in
new file mode 100644
index 0000000..0541240
--- /dev/null
+++ b/backend/sharp.conf.in
@@ -0,0 +1,42 @@
+# The options are only meaningful if the backend was
+# compiled with USE_FORK defined
+#
+# option buffersize: size of one buffer allocated as shared
+# memory for data transfer between the reader process
+# and the parent process
+# option buffers: number of these buffers
+# The minimum is 2
+# option readqueue: number of queued read requests. This is
+# with the current SANE version (1.01) only useful for
+# Linux, since queued read requests are not supported
+# for other operating systems.
+#
+# For Linux, a value of 2 is recommended, at least if a
+# JX-250 is used. Bigger values are only a waste of memory.
+#
+# For other operationg systems, set this value to zero
+#
+# option stop_on_fsu_error: should be 0 or 1. If set to 0,
+# the "FSU light dispersion error" does not cause the abortion
+# of a scan.
+#
+# option default_scan_source: valid values are "auto", "fsu", "adf"
+# and "flatbed". For "auto", the backend will enable an ADF or
+# or FSU, if installed.
+#
+# global options:
+option buffers 4
+option buffersize 131072
+option readqueue 2
+option stop_on_fsu_error 1
+option default_scan_source auto
+# look for all devices with vendor ID "SHARP" and type "Scanner"
+scsi SHARP * Scanner
+# no options specific to these devices listed -> use global options
+/dev/scanner
+# options specific to /dev/scanner
+ option buffers 6
+ option buffersize 262144
+ option readqueue 2
+# example for another (Linux) device name:
+#/dev/sg1
diff --git a/backend/sharp.h b/backend/sharp.h
new file mode 100644
index 0000000..6797224
--- /dev/null
+++ b/backend/sharp.h
@@ -0,0 +1,462 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998, 1999 Kazuya Fukuda, Abel Deuring
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#ifndef sharp_h
+#define sharp_h 1
+
+#include <sys/types.h>
+
+/* default values for configurable options.
+ Though these options are only meaningful if USE_FORK is defined,
+ they are
+ DEFAULT_BUFFERS: number of buffers allocated as shared memory
+ for the data transfer from reader_process to
+ read_data. The minimum value is 2
+ DEFAULT_BUFSIZE: default size of one buffer. Must be greater
+ than zero.
+ DEFAULT_QUEUED_READS: number of read requests queued by
+ sanei_scsi_req_enter. Since queued read requests
+ are currently only supported for Linux and
+ DomainOS, this value should automatically be set
+ dependent on the target OS...
+ For Linux, 2 is the optimum; for DomainOS, I
+ don't have any recommendation; other OS
+ should use the value zero.
+
+ The value for DEFAULT_BUFSIZE is probably too Linux-oriented...
+*/
+
+#define DEFAULT_BUFFERS 12
+#define DEFAULT_BUFSIZE 128 * 1024
+#define DEFAULT_QUEUED_READS 2
+
+typedef enum
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_HALFTONE,
+ OPT_PAPER,
+ OPT_SCANSOURCE,
+ OPT_GAMMA,
+#ifdef USE_CUSTOM_GAMMA
+ OPT_CUSTOM_GAMMA,
+#endif
+ OPT_SPEED,
+
+ OPT_RESOLUTION_GROUP,
+#ifdef USE_RESOLUTION_LIST
+ OPT_RESOLUTION_LIST,
+#endif
+ OPT_X_RESOLUTION,
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ OPT_Y_RESOLUTION,
+#endif
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_EDGE_EMPHASIS,
+ OPT_THRESHOLD,
+#ifdef USE_COLOR_THRESHOLD
+ OPT_THRESHOLD_R,
+ OPT_THRESHOLD_G,
+ OPT_THRESHOLD_B,
+#endif
+ OPT_LIGHTCOLOR,
+ OPT_PREVIEW,
+
+#ifdef USE_CUSTOM_GAMMA
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+#endif
+ /* must come last: */
+ NUM_OPTIONS
+ }
+SHARP_Option;
+
+#ifdef USE_FORK
+
+/* status defines for a buffer:
+ buffer not used / read request queued / buffer contains data
+*/
+#define SHM_EMPTY 0
+#define SHM_BUSY 1
+#define SHM_FULL 2
+typedef struct SHARP_shmem_ctl
+ {
+ int shm_status; /* can be SHM_EMPTY, SHM_BUSY, SHM_FULL */
+ size_t used; /* number of bytes successfully read from scanner */
+ size_t nreq; /* number of bytes requested from scanner */
+ size_t start; /* index of the begin of used area of the buffer */
+ void *qid;
+ SANE_Byte *buffer;
+ }
+SHARP_shmem_ctl;
+
+typedef struct SHARP_rdr_ctl
+ {
+ int cancel; /* 1 = flag for the reader process to cancel */
+ int running; /* 1 indicates that the reader process is alive */
+ SANE_Status status; /* return status of the reader process */
+ SHARP_shmem_ctl *buf_ctl;
+ }
+SHARP_rdr_ctl;
+#endif /* USE_FORK */
+
+typedef enum
+ {
+ /* JX250, JX330, JX350, JX610 are used as array indices, so the
+ corresponding numbers should start at 0
+ */
+ unknown = -1,
+ JX250,
+ JX320,
+ JX330,
+ JX350,
+ JX610
+ }
+SHARP_Model;
+
+typedef struct SHARP_Info
+ {
+ SANE_Range xres_range;
+ SANE_Range yres_range;
+ SANE_Range tl_x_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range br_x_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range tl_y_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range br_y_ranges[3]; /* normal / FSU / ADF */
+ SANE_Range threshold_range;
+
+ SANE_Int xres_default;
+ SANE_Int yres_default;
+ SANE_Int x_default;
+ SANE_Int y_default;
+ SANE_Int bmu;
+ SANE_Int mud;
+ SANE_Int adf_fsu_installed;
+ SANE_String_Const scansources[5];
+ size_t buffers;
+ size_t bufsize;
+ int wanted_bufsize;
+ size_t queued_reads;
+ int complain_on_errors;
+ /* default scan mode:
+ -1 -> "automatic": Use the ADF, if installed,
+ else use the FSU, if installed.
+ or: SCAN_ADF, SCAN_FSU, SCAN_SIMPLE
+ */
+ int default_scan_mode;
+ }
+SHARP_Info;
+
+#define COMPLAIN_ON_FSU_ERROR 2
+#define COMPLAIN_ON_ADF_ERROR 1
+typedef struct SHARP_Sense_Data
+ {
+ SHARP_Model model;
+ /* flag, if conditions like "paper jam" or "cover open"
+ are considered as an error. Should be 0 for attach, else
+ a frontend might refuse to start, if the scanner returns
+ these errors.
+ */
+ int complain_on_errors;
+ /* Linux returns only 16 bytes of sense data... */
+ u_char sb[16];
+ }
+SHARP_Sense_Data;
+
+typedef struct SHARP_Device
+ {
+ struct SHARP_Device *next;
+ SANE_Device sane;
+ SHARP_Info info;
+ /* xxx now part of sense data SHARP_Model model; */
+ SHARP_Sense_Data sensedat;
+ }
+SHARP_Device;
+
+typedef struct SHARP_New_Device
+ {
+ struct SHARP_Device *dev;
+ struct SHARP_New_Device *next;
+ }
+SHARP_New_Device;
+
+typedef struct SHARP_Scanner
+ {
+ struct SHARP_Scanner *next;
+ int fd;
+ SHARP_Device *dev;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters params;
+
+ int get_params_called;
+ SANE_Byte *buffer; /* for color data re-ordering, required for JX 250 */
+ SANE_Int buf_used;
+ SANE_Int buf_pos;
+ SANE_Int modes;
+ SANE_Int xres;
+ SANE_Int yres;
+ SANE_Int ulx;
+ SANE_Int uly;
+ SANE_Int width;
+ SANE_Int length;
+ SANE_Int threshold;
+ SANE_Int image_composition;
+ SANE_Int bpp;
+ SANE_Int halftone;
+ SANE_Bool reverse;
+ SANE_Bool speed;
+ SANE_Int gamma;
+ SANE_Int edge;
+ SANE_Int lightcolor;
+ SANE_Int adf_fsu_mode; /* mode selected by user */
+ SANE_Int adf_scan; /* flag, if the actual scan is an ADF scan */
+
+ size_t bytes_to_read;
+ size_t max_lines_to_read;
+ size_t unscanned_lines;
+ SANE_Bool scanning;
+ SANE_Bool busy;
+ SANE_Bool cancel;
+#ifdef USE_CUSTOM_GAMMA
+ SANE_Int gamma_table[4][256];
+#endif
+#ifdef USE_FORK
+ pid_t reader_pid;
+ SHARP_rdr_ctl *rdr_ctl;
+ int shmid;
+ size_t read_buff; /* index of the buffer actually used by read_data */
+#endif /* USE_FORK */
+ }
+SHARP_Scanner;
+
+typedef struct SHARP_Send
+{
+ SANE_Int dtc;
+ SANE_Int dtq;
+ SANE_Int length;
+ SANE_Byte *data;
+}
+SHARP_Send;
+
+typedef struct WPDH
+{
+ u_char wpdh[6];
+ u_char wdl[2];
+}
+WPDH;
+
+typedef struct WDB
+{
+ SANE_Byte wid;
+ SANE_Byte autobit;
+ SANE_Byte x_res[2];
+ SANE_Byte y_res[2];
+
+ SANE_Byte x_ul[4];
+ SANE_Byte y_ul[4];
+ SANE_Byte width[4];
+ SANE_Byte length[4];
+
+ SANE_Byte brightness;
+ SANE_Byte threshold;
+ SANE_Byte null_1;
+
+ SANE_Byte image_composition;
+ SANE_Byte bpp;
+
+ SANE_Byte ht_pattern[2];
+ SANE_Byte rif_padding;
+ SANE_Byte null_2[4];
+ SANE_Byte null_3[6];
+ SANE_Byte eletu;
+ SANE_Byte zooming_x[2];
+ SANE_Byte zooming_y[2];
+ SANE_Byte lightness_r[2];
+ SANE_Byte lightness_g[2];
+ SANE_Byte lightness_b[2];
+ SANE_Byte lightness_bw[2];
+
+}
+WDB;
+
+/* "extension" of the window descriptor block for the JX 330 */
+typedef struct WDBX330
+ {
+ SANE_Byte moire_reduction[2];
+ }
+WDBX330;
+
+/* "extension" of the window descriptor block for the JX 250 */
+typedef struct XWDBX250
+ {
+ SANE_Byte threshold_red;
+ SANE_Byte threshold_green;
+ SANE_Byte threshold_blue;
+ SANE_Byte draft;
+ SANE_Byte scanning_time[4];
+ SANE_Byte fixed_gamma;
+ SANE_Byte x_axis_res_qualifier[2];
+ SANE_Byte y_axis_res_qualifier[2];
+ }
+WDBX250;
+
+typedef struct window_param
+{
+ WPDH wpdh;
+ WDB wdb;
+ WDBX330 wdbx330;
+ WDBX250 wdbx250;
+}
+window_param;
+
+typedef struct mode_sense_param
+{
+ SANE_Byte mode_data_length;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_desciptor_length;
+ SANE_Byte resereved[5];
+ SANE_Byte blocklength[3];
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 6 */
+ SANE_Byte bmu;
+ SANE_Byte res2;
+ SANE_Byte mud[2];
+ SANE_Byte res3;
+ SANE_Byte res4;
+}
+mode_sense_param;
+
+typedef struct mode_sense_subdevice
+{
+ /* This definition reflects the JX250. The JX330 would need a slightly
+ different definition, but the bytes used right now (for ADF and FSU)
+ are identical.
+ */
+ SANE_Byte mode_data_length;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_desciptor_length;
+ SANE_Byte res1[5];
+ SANE_Byte blocklength[3];
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 0x1a */
+ SANE_Byte a_mode_type;
+ SANE_Byte f_mode_type;
+ SANE_Byte res2;
+ SANE_Byte max_x[4];
+ SANE_Byte max_y[4];
+ SANE_Byte res3[2];
+ SANE_Byte x_basic_resolution[2];
+ SANE_Byte y_basic_resolution[2];
+ SANE_Byte x_max_resolution[2];
+ SANE_Byte y_max_resolution[2];
+ SANE_Byte x_min_resolution[2];
+ SANE_Byte y_min_resolution[2];
+ SANE_Byte res4;
+}
+mode_sense_subdevice;
+
+typedef struct mode_select_param
+{
+ SANE_Byte mode_param_header1;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_param_header4;
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 6 */
+ SANE_Byte res1;
+ SANE_Byte res2;
+ SANE_Byte mud[2];
+ SANE_Byte res3;
+ SANE_Byte res4;
+}
+mode_select_param;
+
+typedef struct mode_select_subdevice
+{
+ SANE_Byte mode_param_header1;
+ SANE_Byte mode_param_header2;
+ SANE_Byte mode_param_header3;
+ SANE_Byte mode_param_header4;
+ SANE_Byte page_code;
+ SANE_Byte page_length; /* 0x1A */
+ SANE_Byte a_mode;
+ SANE_Byte f_mode;
+ SANE_Byte res[24];
+}
+mode_select_subdevice;
+
+/* SCSI commands */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define MODE_SELECT6 0x15
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define MODE_SENSE6 0x1a
+#define SCAN 0x1b
+#define SEND_DIAGNOSTIC 0x1d
+#define SET_WINDOW 0x24
+#define GET_WINDOW 0x25
+#define READ 0x28
+#define SEND 0x2a
+#define OBJECT_POSITION 0x31
+
+#define SENSE_LEN 18
+#define INQUIRY_LEN 36
+#define MODEPARAM_LEN 12
+#define MODE_SUBDEV_LEN 32
+#define WINDOW_LEN 76
+
+#endif /* not sharp_h */
diff --git a/backend/sm3600-color.c b/backend/sm3600-color.c
new file mode 100644
index 0000000..9b3c540
--- /dev/null
+++ b/backend/sm3600-color.c
@@ -0,0 +1,313 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ======================================================================
+
+Userspace scan tool for the Microtek 3600 scanner
+
+color scan routine
+
+(C) Marian Eichholz 2001
+
+====================================================================== */
+
+#include "sm3600-scantool.h"
+
+#define ORDER_RGB "012"
+#define ORDER_BRG "120"
+
+/* **********************************************************************
+
+ReadNextColorLine()
+
+********************************************************************** */
+
+static TState ReadNextColorLine(PTInstance this)
+{
+ int iWrite,i;
+ int iRead; /* read position in raw line */
+ int nInterpolator;
+ int iOffsetR,iOffsetG,iOffsetB;
+ short *pchLineSwap;
+ TBool bVisible;
+
+ bVisible=false;
+ do {
+ iWrite=0;
+ while (iWrite<3*this->state.cxMax) /* max 1 time in reality */
+ {
+ while (iWrite<3*this->state.cxMax &&
+ this->state.iBulkReadPos<this->state.cchBulk)
+ this->state.ppchLines[0][iWrite++] =
+ this->state.pchBuf[this->state.iBulkReadPos++];
+ if (iWrite<3*this->state.cxMax) /* we need an additional chunk */
+ {
+ if (this->state.bLastBulk)
+ return SANE_STATUS_EOF;
+ this->state.cchBulk=BulkReadBuffer(this,this->state.pchBuf,
+ USB_CHUNK_SIZE);
+ dprintf(DEBUG_SCAN,"bulk read: %d byte(s), line #%d\n",
+ this->state.cchBulk, this->state.iLine);
+ if (this->bWriteRaw)
+ fwrite(this->state.pchBuf,1,this->state.cchBulk,this->fhScan);
+ INST_ASSERT();
+ if (this->state.cchBulk!=USB_CHUNK_SIZE)
+ this->state.bLastBulk=true;
+ this->state.iBulkReadPos=0;
+ }
+ } /* while raw line buffer acquiring */
+ this->state.iLine++;
+ if (this->state.iLine>2*this->state.ySensorSkew)
+ {
+ bVisible=true;
+ iOffsetR=(this->state.szOrder[0]-'0')*this->state.cxMax;
+ iOffsetG=(this->state.szOrder[1]-'0')*this->state.cxMax;
+ iOffsetB=(this->state.szOrder[2]-'0')*this->state.cxMax;
+ for (nInterpolator=100, iWrite=0, iRead=0;
+ iRead<3*this->state.cxMax && iWrite<this->state.cchLineOut;
+ iRead++)
+ {
+ nInterpolator+=this->state.nFixAspect;
+ if (nInterpolator<100) continue; /* res. reduction */
+ nInterpolator-=100;
+ /* dprintf(DEBUG_SCAN," i=%d",iTo); */
+ /* the first scan lines only fill the line backlog buffer */
+ {
+ /* dprintf(DEBUG_SCAN,"assembling line %d\n",++this->state.iLine); */
+ this->state.pchLineOut[iWrite++]=
+ this->state.ppchLines[2*this->state.ySensorSkew][iRead+iOffsetR];
+ this->state.pchLineOut[iWrite++]=
+ this->state.ppchLines[1*this->state.ySensorSkew][iRead+iOffsetG];
+ this->state.pchLineOut[iWrite++]=
+ this->state.ppchLines[0*this->state.ySensorSkew][iRead+iOffsetB];
+ }
+ }
+ } /* if visible line */
+ /* cycle backlog buffers */
+ pchLineSwap=this->state.ppchLines[this->state.cBacklog-1];
+ for (i=this->state.cBacklog-2; i>=0; i--)
+ this->state.ppchLines[i+1]=this->state.ppchLines[i];
+ this->state.ppchLines[0]=pchLineSwap;
+ } while (!bVisible);
+ return SANE_STATUS_GOOD;
+}
+
+/* ======================================================================
+
+StartScanColor()
+
+====================================================================== */
+
+/* Parameter are in resolution units! */
+__SM3600EXPORT__
+TState StartScanColor(TInstance *this)
+{
+
+ /* live could be easy: Simple calculate a window, start the scan,
+ get the data and get off. It dimply does not work. We have to
+ deal with the fact, that the pixels are reported with color scan
+ line by color scan line, and have to be rearranged to RGB
+ triples. They even ar enot always in RGB order, but mostly in BRG
+ order. And they have a skew of 2/300 inch , given by the slider
+ construction. Thus, we have to deal with several buffers, some
+ interpolation, and related management stuff. */
+ int i;
+ if (this->state.bScanning)
+ return SetError(this,SANE_STATUS_DEVICE_BUSY,"scan active");
+ memset(&(this->state),0,sizeof(this->state));
+ this->state.ReadProc =ReadNextColorLine;
+ this->state.ySensorSkew=0;
+
+ GetAreaSize(this);
+
+ switch (this->param.res)
+ {
+ case 200: this->state.ySensorSkew=1; break;
+ case 300: this->state.ySensorSkew=2; break;
+ case 600: this->state.ySensorSkew=4; break;
+ default: break;
+ }
+ /* since we need 2*this->state.ySensorSkew additional scan lines for de-skewing of
+ the sensor lines, we enlarge the window and shorten the initial movement
+ accordingly */
+ this->state.cyTotalPath =
+ this->param.y/2-(2*this->state.ySensorSkew)*600/this->param.res;
+ DoJog(this,this->state.cyTotalPath); INST_ASSERT();
+ this->state.cyTotalPath +=
+ (this->state.cyPixel+2*this->state.ySensorSkew)
+ *600/this->param.res; /* for jogging back */
+
+ /*
+ regular scan is asynchronously, that is,
+ the scanning is issued, and the driver does bulk reads,
+ until there are no data left.
+ Each line has a full R, G and B subline, 8bit each sample.
+ */
+ {
+ unsigned char uchRegs[]={
+ 0xFC /*!!R_SPOS!!*/, 0x00 /*R_SPOSH*/, 0x24 /*!!0x03!!*/,
+ 0xB0 /*!!R_SWID!!*/, 0xC4 /*!!R_SWIDH!!*/,
+ 1,0,
+ 0xFF /*!!0x08!!*/, 0xFF /*!!0x09!!*/,
+ 0x22 /*!!R_LEN!!*/, 0x07 /*!!R_LENH!!*/, 0x6D /*0x0C*/,
+ 0x70 /*0x0D*/, 0x69 /*0x0E*/, 0xD0 /*0x0F*/,
+ 0x00 /*0x10*/, 0x00 /*0x11*/, 0x42 /*!!0x12!!*/,
+ 0x15 /*0x13*/, 0x84 /*!!0x14!!*/, 0x2A /*0x15*/,
+ 0xC5 /*!!0x16!!*/, 0x40 /*0x17*/, 0xC5 /*!!0x18!!*/,
+ 0x40 /*0x19*/, 0xFF /*0x1A*/, 0x01 /*0x1B*/,
+ 0x88 /*0x1C*/, 0x40 /*0x1D*/, 0x4C /*0x1E*/,
+ 0x50 /*0x1F*/, 0x00 /*0x20*/, 0x0C /*0x21*/,
+ 0x21 /*0x22*/, 0xF0 /*0x23*/, 0x40 /*0x24*/,
+ 0x00 /*0x25*/, 0x0A /*0x26*/, 0xF0 /*0x27*/,
+ 0x00 /*0x28*/, 0x00 /*0x29*/, 0x4E /*0x2A*/,
+ 0xF0 /*0x2B*/, 0x00 /*0x2C*/, 0x00 /*0x2D*/,
+ 0x4E /*0x2E*/, 0x80 /*R_CCAL*/, 0x80 /*R_CCAL2*/,
+ 0x80 /*R_CCAL3*/, 0x0B /*0x32*/, 0x2D /*0x33*/,
+ 0x43 /*!!0x34!!*/, 0x29 /*0x35*/, 0x00 /*0x36*/,
+ 0x00 /*0x37*/, 0x00 /*0x38*/, 0x00 /*0x39*/,
+ 0x00 /*0x3A*/, 0x00 /*0x3B*/, 0xFF /*0x3C*/,
+ 0x0F /*0x3D*/, 0x00 /*0x3E*/, 0x00 /*0x3F*/,
+ 0x01 /*0x40*/, 0x00 /*0x41*/, 0x80 /*R_CSTAT*/,
+ 0x03 /*0x43*/, 0x01 /*R_LMP*/, 0x00 /*0x45*/,
+ 0x39 /*R_CTL*/, 0xC5 /*!!0x47!!*/, 0x40 /*0x48*/,
+ 0x9E /*0x49*/, 0x8C /*0x4A*/ };
+ RegWriteArray(this,R_ALL, NUM_SCANREGS, uchRegs);
+ RegWrite(this,R_SPOS, 2, this->param.x/2 + this->calibration.xMargin);
+ RegWrite(this,R_SLEN, 2,
+ this->state.cyWindow+
+ (2*this->state.ySensorSkew)*600/this->param.res);
+ this->state.szOrder=ORDER_BRG;
+ RegWrite(this,R_CCAL, 3, this->calibration.rgbBias); INST_ASSERT(); /* 0xBBGGRR */
+ switch (this->param.res)
+ {
+ case 75:
+ RegWrite(this,R_XRES,1, 0x20); /* ups, can do only 100 dpi horizontal */
+ RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
+ RegWrite(this,0x34, 1, 0x83); /* halfs the vertical resolution */
+ RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
+ break;
+ case 100:
+ RegWrite(this,R_XRES,1, 0x20);
+ RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
+ RegWrite(this,0x34, 1, 0x63); /* halfs the vertical resolution */
+ RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
+ /* I have no idea, what these differences are good for. The seem to produce
+ a slight blue presence.
+ RegWrite(this,0x16, 1, 0xC0); RegWrite(this,0x18, 1, 0xC0);
+ RegWrite(this,0x12, 1, 0x40); RegWrite(this,0x10, 2, 0x0728);
+ RegWrite(this,0x14, 1, 0x80); */
+ break;
+ case 200:
+ RegWrite(this,R_XRES,1, 0x24);
+ RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
+ break;
+ case 300:
+ RegWrite(this,0x08,2, 0x6A6A);
+ RegWrite(this,R_XRES,1, 0x2A);
+ RegWrite(this,R_SWID, 2, 0x4000 | this->state.cxWindow);
+ RegWrite(this,0x34, 1, 0x03); /* halfs the vertical resolution */
+ RegWrite(this,0x47,1,0xC0); /* reduces the speed a bit */
+ this->state.szOrder=ORDER_RGB;
+ break;
+ case 600:
+ RegWrite(this,R_XRES,1, 0x3F);
+ RegWrite(this,R_SWID, 2, 0xC000 | this->state.cxWindow);
+ RegWrite(this,0x34, 1, 0x03); /* halfs the vertical resolution */
+ RegWrite(this,0x47,1,0xC2); /* reduces the speed a bit */
+ break;
+ case 1200:
+ /* not supported, since the driver supports only 600 dpi in color */
+ break;
+ }
+ }
+
+ /* setup gamma tables */
+ RegWrite(this,0x41,1,0x03); /* gamma, RGB */
+ RegWrite(this,0x40,1,0x28); /* offset FIFO 8*3 (GAMMA)+16 KB(gain) spared */
+ /*
+ hey, surprise: Although the color lines are sent in a strange order,
+ the gamma tables are mapped constantly to the sensors (i.e. RGB)
+ */
+ UploadGammaTable(this,0x0000,this->agammaR);
+ UploadGammaTable(this,0x2000,this->agammaG);
+ UploadGammaTable(this,0x4000,this->agammaB);
+ INST_ASSERT();
+
+ UploadGainCorrection(this,0x6000);
+ INST_ASSERT();
+
+ /* enough for 1/100 inch sensor distance */
+ this->state.cBacklog=1+2*this->state.ySensorSkew;
+
+ /* allocate raw line buffers */
+ this->state.ppchLines=calloc(this->state.cBacklog,sizeof(short*));
+ this->state.pchBuf=malloc(0x8000);
+ if (!this->state.ppchLines || !this->state.pchBuf)
+ return FreeState(this,SetError(this,
+ SANE_STATUS_NO_MEM,"no buffers available"));
+
+ for (i=0; i<this->state.cBacklog; i++)
+ {
+ this->state.ppchLines[i]=calloc(1,3*this->state.cxMax*sizeof(short)); /* must be less than 0x8000 */
+ if (!this->state.ppchLines[i])
+ return FreeState(this,
+ SetError(this,SANE_STATUS_NO_MEM,
+ "no line buffer available"));
+ }
+
+ /* calculate and prepare intermediate line transfer buffer */
+
+ this->state.cchLineOut=3*this->state.cxPixel;
+ this->state.pchLineOut = malloc(this->state.cchLineOut);
+ if (!this->state.pchLineOut)
+ return FreeState(this,SetError(this,
+ SANE_STATUS_NO_MEM,
+ "no buffers available"));
+
+ RegWrite(this,R_CTL, 1, 0x39); /* #1532[005.0] */
+ RegWrite(this,R_CTL, 1, 0x79); /* #1533[005.0] */
+ RegWrite(this,R_CTL, 1, 0xF9); /* #1534[005.0] */
+ INST_ASSERT();
+
+ this->state.bScanning = true;
+ return SANE_STATUS_GOOD;
+}
+
diff --git a/backend/sm3600-gray.c b/backend/sm3600-gray.c
new file mode 100644
index 0000000..d9fda86
--- /dev/null
+++ b/backend/sm3600-gray.c
@@ -0,0 +1,389 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ======================================================================
+
+Userspace scan tool for the Microtek 3600 scanner
+
+grayscale scan routine
+
+(C) Marian Eichholz 2001
+
+====================================================================== */
+
+#include "sm3600-scantool.h"
+
+/* **********************************************************************
+
+DoScanGray()
+
+********************************************************************** */
+
+#define LINE_THRESHOLD 0x800
+
+static unsigned char uchRegs075[]={
+ /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x20,
+ /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
+ /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
+ /*R_LEN*/ 0x28, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
+ /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
+ /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
+ /*0x13*/ 0x15, /*0x14*/ 0x80, /*0x15*/ 0x2A,
+ /*0x16*/ 0xC0, /*0x17*/ 0x40, /*0x18*/ 0xC0,
+ /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
+ /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
+ /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
+ /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
+ /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
+ /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
+ /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
+ /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
+ /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
+ /*0x34*/ 0x83, /*0x35*/ 0x29, /*0x36*/ 0x00,
+ /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
+ /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
+ /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
+ /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x00,
+ /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
+ /*R_CTL*/ 0x39, /*0x47*/ 0xC0, /*0x48*/ 0x40,
+ /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
+
+static unsigned char uchRegs100[]={
+ /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x20,
+ /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
+ /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
+ /*R_LEN*/ 0x34, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
+ /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
+ /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
+ /*0x13*/ 0x15, /*0x14*/ 0x84, /*0x15*/ 0x2A,
+ /*0x16*/ 0xC2, /*0x17*/ 0x40, /*0x18*/ 0xC2,
+ /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
+ /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
+ /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
+ /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
+ /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
+ /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
+ /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
+ /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
+ /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
+ /*0x34*/ 0x63, /*0x35*/ 0x29, /*0x36*/ 0x00,
+ /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
+ /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
+ /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
+ /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
+ /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
+ /*R_CTL*/ 0x39, /*0x47*/ 0xC2, /*0x48*/ 0x40,
+ /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
+
+static unsigned char uchRegs200[]={
+ /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x24,
+ /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
+ /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
+ /*R_LEN*/ 0x22, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
+ /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
+ /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
+ /*0x13*/ 0x15, /*0x14*/ 0x42, /*0x15*/ 0x15,
+ /*0x16*/ 0x42, /*0x17*/ 0x15, /*0x18*/ 0x42,
+ /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
+ /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
+ /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
+ /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
+ /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
+ /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
+ /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
+ /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
+ /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
+ /*0x34*/ 0x43, /*0x35*/ 0x29, /*0x36*/ 0x00,
+ /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
+ /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
+ /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
+ /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
+ /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
+ /*R_CTL*/ 0x39, /*0x47*/ 0x42, /*0x48*/ 0x15,
+ /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
+
+static unsigned char uchRegs300[]={
+ /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x2A,
+ /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
+ /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x2A,
+ /*R_LEN*/ 0x16, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
+ /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
+ /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
+ /*0x13*/ 0x15, /*0x14*/ 0x40, /*0x15*/ 0x15,
+ /*0x16*/ 0x40, /*0x17*/ 0x15, /*0x18*/ 0x40,
+ /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
+ /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
+ /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
+ /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
+ /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
+ /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
+ /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
+ /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
+ /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
+ /*0x34*/ 0x03, /*0x35*/ 0x29, /*0x36*/ 0x00,
+ /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
+ /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
+ /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
+ /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
+ /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
+ /*R_CTL*/ 0x39, /*0x47*/ 0x40, /*0x48*/ 0x15,
+ /*0x49*/ 0x96, /*0x4A*/ 0x8C };
+
+static unsigned char uchRegs600[]={
+ /*R_SPOS*/ 0xFC, /*R_SPOSH*/ 0x00, /*0x03*/ 0x3F,
+ /*R_SWID*/ 0xB0, /*R_SWIDH*/ 0x04, /*R_STPS*/ 0x06,
+ /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x3F,
+ /*R_LEN*/ 0x16, /*R_LENH*/ 0x07, /*0x0C*/ 0x6D,
+ /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
+ /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x42,
+ /*0x13*/ 0x15, /*0x14*/ 0x42, /*0x15*/ 0x15,
+ /*0x16*/ 0x42, /*0x17*/ 0x15, /*0x18*/ 0x42,
+ /*0x19*/ 0x15, /*0x1A*/ 0x07, /*0x1B*/ 0x00,
+ /*0x1C*/ 0x08, /*0x1D*/ 0x12, /*0x1E*/ 0x4C,
+ /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
+ /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
+ /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
+ /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
+ /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
+ /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
+ /*R_CCAL3*/ 0x80, /*0x32*/ 0xC9, /*0x33*/ 0x20,
+ /*0x34*/ 0x03, /*0x35*/ 0x29, /*0x36*/ 0x00,
+ /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
+ /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
+ /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
+ /*0x40*/ 0x01, /*0x41*/ 0x00, /*R_CSTAT*/ 0x80,
+ /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
+ /*R_CTL*/ 0x39, /*0x47*/ 0x42, /*0x48*/ 0x15,
+ /*0x49*/ 0x96, /*0x4A*/ 0x8C };
+
+/* ======================================================================
+
+ReadNextGrayLine()
+
+====================================================================== */
+
+static TState ReadNextGrayLine(PTInstance this)
+{
+ int iWrite;
+ int iDot;
+ unsigned char chBits;
+ int iRead; /* read position in raw line */
+ int nInterpolator;
+
+ iWrite=0;
+
+ while (iWrite<this->state.cxMax) /* max 1 time in reality */
+ {
+ while (iWrite<this->state.cxMax &&
+ this->state.iBulkReadPos<this->state.cchBulk)
+ this->state.ppchLines[0][iWrite++] += /* add! */
+ this->state.pchBuf[this->state.iBulkReadPos++]<<4;
+ if (iWrite<this->state.cxMax) /* we need an additional chunk */
+ {
+ if (this->state.bLastBulk)
+ return SANE_STATUS_EOF;
+ this->state.cchBulk=BulkReadBuffer(this,this->state.pchBuf,
+ USB_CHUNK_SIZE);
+ dprintf(DEBUG_SCAN,"bulk read: %d byte(s), line #%d\n",
+ this->state.cchBulk, this->state.iLine);
+ if (this->bWriteRaw)
+ fwrite(this->state.pchBuf,1,this->state.cchBulk,this->fhScan);
+ INST_ASSERT();
+ if (this->state.cchBulk!=USB_CHUNK_SIZE)
+ this->state.bLastBulk=true;
+ this->state.iBulkReadPos=0;
+ }
+ } /* while raw line buffer acquiring */
+ this->state.iLine++;
+ iDot=0; chBits=0; /* init pixelbuffer */
+ for (nInterpolator=50, iWrite=0, iRead=0;
+ iRead<this->state.cxMax;
+ iRead++)
+ {
+ nInterpolator+=this->state.nFixAspect;
+ if (nInterpolator<100) continue; /* res. reduction */
+ nInterpolator-=100;
+ if (iWrite>=this->state.cchLineOut) continue;
+ /* dprintf(DEBUG_SCAN," i=%d",iTo); */
+ if (this->mode==gray)
+ this->state.pchLineOut[iWrite++]=
+ this->state.ppchLines[0][iRead]>>4;
+ else
+ {
+ unsigned char chBit; /* 1=white */
+ if (this->mode==line)
+ chBit=(this->state.ppchLines[0][iRead]<LINE_THRESHOLD);
+ else
+ {
+ short nError=this->state.ppchLines[0][iRead];
+ /* printf("(%d)",nError); */
+ if (nError>=0xFF0)
+ {
+ nError-=0xFF0;
+ chBit=0;
+ }
+ else
+ chBit=1;
+ /* since I sketched the Floyd-Steinberg
+ algorithm from heart, I have no idea, if
+ there is room for improvement in the
+ coefficients. If You know, please drop
+ me a line (eichholz@computer.org, 1.4.2001) */
+#define FASTDITHER
+#ifdef FASTDITHER
+ this->state.ppchLines[0][iRead+1]+=(nError>>2); /* 8/16 */
+ this->state.ppchLines[1][iRead+1]+=(nError>>1);
+ this->state.ppchLines[1][iRead] +=(nError>>2); /* 8/16 */
+#else
+ this->state.ppchLines[0][iRead+1]+=(nError*5)>>4;
+ this->state.ppchLines[1][iRead+1]+=(nError*8)>>4;
+ this->state.ppchLines[1][iRead] +=(nError*3)>>4;
+#endif
+ }
+ chBits=(chBits<<1)|chBit;
+ iDot++;
+ if (iDot==8 && iWrite<this->state.cchLineOut)
+ {
+ this->state.pchLineOut[iWrite++]=chBits;
+ iDot=0; chBits=0;
+ }
+ } /* gray pixel postprocessing */
+ } /* line postprocessing */
+ if (iDot && iWrite<this->state.cchLineOut)
+ this->state.pchLineOut[iWrite++]=chBits;
+ /* cycle the history lines and clear the preread buffer*/
+ {
+ short *p=this->state.ppchLines[0];
+ this->state.ppchLines[0]=this->state.ppchLines[1];
+ this->state.ppchLines[1]=p;
+ memset(this->state.ppchLines[1],0,(this->state.cxMax+1)*sizeof(short));
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* ======================================================================
+
+StartScanGray()
+
+====================================================================== */
+
+__SM3600EXPORT__
+TState StartScanGray(TInstance *this)
+{
+ unsigned char *puchRegs;
+ int i;
+ if (this->state.bScanning)
+ return SetError(this,SANE_STATUS_DEVICE_BUSY,"scan active");
+ memset(&(this->state),0,sizeof(TScanState));
+ this->state.ReadProc =ReadNextGrayLine;
+ puchRegs=NULL;
+ switch (this->param.res)
+ {
+ case 75: puchRegs=uchRegs075; break;
+ case 100: puchRegs=uchRegs100; break;
+ case 200: puchRegs=uchRegs200; break;
+ case 300: puchRegs=uchRegs300; break;
+ case 600: puchRegs=uchRegs600; break;
+ }
+ GetAreaSize(this);
+ this->state.cyTotalPath = this->param.y/2;
+ DoJog(this,this->state.cyTotalPath);
+ INST_ASSERT();
+ this->state.cyTotalPath += this->param.cy/2; /* for jogging back */
+
+ /*
+ regular scan is asynchronously, that is,
+ the scanning is issued, and the driver does bulk reads,
+ until there are no data left.
+ */
+ RegWriteArray(this,R_ALL, NUM_SCANREGS, puchRegs); INST_ASSERT();
+ RegWrite(this,R_SPOS, 2,
+ this->param.x/2+this->calibration.xMargin); INST_ASSERT();
+ RegWrite(this,R_SLEN, 2, this->state.cyWindow); INST_ASSERT();
+ RegWrite(this,R_SWID, 2, this->state.cxWindow); INST_ASSERT();
+ RegWrite(this,R_STPS, 2, 0); INST_ASSERT();
+
+ /* upload gamma table */
+ RegWrite(this,0x41,1,0x01); /* gamma, gray */
+ RegWrite(this,0x40,1,0x20); /* FIFO at 0x08000 */
+ UploadGammaTable(this,0,this->agammaY); INST_ASSERT();
+
+ UploadGainCorrection(this, 0x2000);
+ INST_ASSERT();
+
+ /* for halftone dithering we need one history line */
+ this->state.pchBuf=malloc(USB_CHUNK_SIZE);
+ this->state.cBacklog=2;
+ this->state.ppchLines=calloc(this->state.cBacklog,sizeof(short *));
+ if (!this->state.pchBuf || !this->state.ppchLines)
+ return FreeState(this,SetError(this,
+ SANE_STATUS_NO_MEM,"no buffers available"));
+ for (i=0; i<this->state.cBacklog; i++)
+ {
+ this->state.ppchLines[i]=calloc(this->state.cxMax+1,
+ sizeof(short)); /* 1 dummy at right edge */
+ if (!this->state.ppchLines[i])
+ return FreeState(this,SetError(this,
+ SANE_STATUS_NO_MEM,"no buffers available"));
+ }
+
+ /* calculate and prepare intermediate line transfer buffer */
+
+ this->state.cchLineOut=(this->mode==gray)
+ ? this->state.cxPixel
+ : (this->state.cxPixel+7)/8;
+
+ this->state.pchLineOut = malloc(this->state.cchLineOut);
+ if (!this->state.pchLineOut)
+ return FreeState(this,SetError(this,
+ SANE_STATUS_NO_MEM,
+ "no buffers available"));
+
+ /* start the unit, when all buffers are available */
+
+ RegWrite(this,R_CTL, 1, 0x39); INST_ASSERT();
+ RegWrite(this,R_CTL, 1, 0x79); INST_ASSERT();
+ RegWrite(this,R_CTL, 1, 0xF9); INST_ASSERT();
+
+ this->state.bScanning = true;
+ return SANE_STATUS_GOOD;
+}
+
diff --git a/backend/sm3600-homerun.c b/backend/sm3600-homerun.c
new file mode 100644
index 0000000..fe647ef
--- /dev/null
+++ b/backend/sm3600-homerun.c
@@ -0,0 +1,499 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ======================================================================
+
+Userspace scan tool for the Microtek 3600 scanner
+
+slider movement
+
+(C) Marian Eichholz 2001
+
+====================================================================== */
+
+#include "sm3600-scantool.h"
+
+#include <math.h>
+
+/* tuning constants for DoOriginate */
+#define CCH_BONSAI 60
+#define BLACK_HOLE_GRAY 30
+#define BLACK_BED_LEVEL 10
+
+/* changed by user request from 100, there are probably darker stripes */
+#define CHASSIS_GRAY_LEVEL 75
+
+typedef enum { ltHome, ltUnknown, ltBed, ltError } TLineType;
+
+#define INST_ASSERT2() { if (this->nErrorState) return ltError; }
+
+static unsigned char auchRegsSingleLine[]={
+ 0x00 /*0x01*/, 0x00 /*0x02*/, 0x3F /*0x03*/,
+ 0xB4 /*!!0x04!!*/, 0x14 /*!!0x05!!*/, 0,0,
+ 0x00 /*0x08*/, 0x3F /*!!0x09!!*/,
+ 1,0,
+ 0x6D /*0x0C*/,
+ 0x70 /*0x0D*/, 0x69 /*0x0E*/, 0xD0 /*0x0F*/,
+ 0x00 /*0x10*/, 0x00 /*0x11*/, 0x40 /*0x12*/,
+ 0x15 /*0x13*/, 0x80 /*0x14*/, 0x2A /*0x15*/,
+ 0xC0 /*0x16*/, 0x40 /*0x17*/, 0xC0 /*0x18*/,
+ 0x40 /*0x19*/, 0xFF /*0x1A*/, 0x01 /*0x1B*/,
+ 0x88 /*0x1C*/, 0x40 /*0x1D*/, 0x4C /*0x1E*/,
+ 0x50 /*0x1F*/, 0x00 /*0x20*/, 0x0C /*0x21*/,
+ 0x21 /*0x22*/, 0xF0 /*0x23*/, 0x40 /*0x24*/,
+ 0x00 /*0x25*/, 0x0A /*0x26*/, 0xF0 /*0x27*/,
+ 0x00 /*0x28*/, 0x00 /*0x29*/, 0x4E /*0x2A*/,
+ 0xF0 /*0x2B*/, 0x00 /*0x2C*/, 0x00 /*0x2D*/,
+ 0x4E /*0x2E*/, 0x88 /*R_CCAL*/, 0x88 /*R_CCAL2*/,
+ 0x84 /*R_CCAL3*/, 0xEA /*R_LEN*/, 0x24 /*R_LENH*/,
+ 0x63 /*0x34*/, 0x29 /*0x35*/, 0x00 /*0x36*/,
+ 0x00 /*0x37*/, 0x00 /*0x38*/, 0x00 /*0x39*/,
+ 0x00 /*0x3A*/, 0x00 /*0x3B*/, 0xFF /*0x3C*/,
+ 0x0F /*0x3D*/, 0x00 /*0x3E*/, 0x00 /*0x3F*/,
+ 0x01 /*0x40*/, 0x00 /*0x41*/, 0x00 /*R_CSTAT*/,
+ 0x03 /*R_SPD*/, 0x01 /*0x44*/, 0x00 /*0x45*/,
+ 0x59 /*!!R_CTL!!*/, 0xC0 /*0x47*/, 0x40 /*0x48*/,
+ 0x96 /*!!0x49!!*/, 0xD8 /*0x4A*/ };
+
+/* ======================================================================
+
+GetLineType()
+
+Reads a scan line at the actual position and classifies it as
+"on the flatbed area" or "at home position" or "elsewhere".
+This can be used to calculate the proper stepping width
+
+====================================================================== */
+
+static TLineType GetLineType(TInstance *this)
+{
+ unsigned char achLine[CCH_BONSAI+1];
+ unsigned char *puchBuffer;
+ int cchBulk,i,iHole;
+ int axHoles[3];
+ long lSum;
+ TBool bHolesOk;
+ int lMedian;
+ bHolesOk=false;
+ RegWriteArray(this,R_ALL, 74, auchRegsSingleLine);
+ INST_ASSERT2();
+ /* dprintf(DEBUG_SCAN,"originate-%d...",iStripe); */
+ RegWrite(this,R_CTL, 1, 0x59); /* #2496[062.5] */
+ RegWrite(this,R_CTL, 1, 0xD9); /* #2497[062.5] */
+ i=WaitWhileScanning(this,5); if (i) return i;
+
+ cchBulk=MAX_PIXEL_PER_SCANLINE;
+ /*
+ cchBulk=RegRead(this,R_STAT, 2);
+ if (cchBulk!=MAX_PIXEL_PER_SCANLINE)
+ return SetError(this,SANE_STATUS_INVAL,
+ "illegal scan line width reported (%d)",cchBulk);
+ */
+ puchBuffer=(unsigned char*)calloc(1,cchBulk);
+ CHECK_POINTER(puchBuffer);
+ if (BulkReadBuffer(this,puchBuffer, cchBulk)!=cchBulk)
+ {
+ free(puchBuffer);
+ return SetError(this,SANE_STATUS_IO_ERROR,"truncated bulk");
+ }
+ lSum=0;
+ for (i=0; i<cchBulk; i++)
+ lSum+=puchBuffer[i]; /* gives total white level */
+ for (i=0; i<CCH_BONSAI; i++)
+ {
+ int iBulk=i*(cchBulk)/CCH_BONSAI;
+ achLine[i]=puchBuffer[iBulk+40]; /* simple, basta */
+ }
+ /* the bonsai line is supported only for curiosity */
+ for (i=0; i<CCH_BONSAI; i++)
+ achLine[i]=achLine[i]/26+'0'; /* '0'...'9' */
+ achLine[CCH_BONSAI]='\0';
+
+ i=200;
+ iHole=0;
+ dprintf(DEBUG_ORIG,"");
+ while (i<MAX_PIXEL_PER_SCANLINE && iHole<3)
+ {
+ int c;
+ while (i<MAX_PIXEL_PER_SCANLINE && puchBuffer[i]>BLACK_HOLE_GRAY) i++; /* not very black */
+ c=0;
+ dprintf(DEBUG_ORIG,"~ i=%d",i);
+ while (i<MAX_PIXEL_PER_SCANLINE && puchBuffer[i]<=BLACK_HOLE_GRAY) { i++; c++; }
+ dprintf(DEBUG_ORIG,"~ c=%d",c);
+ if (c>90) /* 90% of min hole diameter */
+ {
+ axHoles[iHole]=i-c/2; /* store the middle of the hole */
+ dprintf(DEBUG_ORIG,"~ #%d=%d",iHole,axHoles[iHole]);
+ iHole++;
+ i+=10; /* some hysteresis */
+ }
+ }
+ if (iHole==3)
+ {
+ bHolesOk=true;
+ for (i=0; i<2; i++)
+ {
+ int xDistance=axHoles[i+1]-axHoles[i];
+ if (xDistance<1050 || xDistance>1400)
+ bHolesOk=false;
+ }
+ if (axHoles[0]<350 || axHoles[0]>900) /* >2 cm tolerance */
+ bHolesOk=false;
+ }
+ else
+ bHolesOk=false;
+ lMedian=lSum/cchBulk;
+ /* this is *definitly* dirty style. We should pass the information
+ by other means... */
+ if (bHolesOk)
+ {
+ /* black reference */
+ this->calibration.nHoleGray=puchBuffer[axHoles[0]];
+ switch (this->model)
+ {
+ case sm3600:
+ /* bed corner */
+ this->calibration.xMargin=axHoles[0]-480;
+ this->calibration.yMargin=413;
+ break;
+ case sm3700:
+ case sm3750: /* basically unknown sub-brand */
+ default:
+ this->calibration.xMargin=axHoles[0]-462;
+ this->calibration.yMargin=330;
+ break;
+ } /* switch */
+ }
+ dprintf(DEBUG_ORIG,"~ %s - %d\n",
+ achLine,
+ lMedian);
+ free(puchBuffer);
+ i=WaitWhileBusy(this,2); if (i) return i;
+ if (bHolesOk && lMedian>CHASSIS_GRAY_LEVEL)
+ return ltHome;
+ if (lMedian<=BLACK_BED_LEVEL)
+ return ltBed;
+ return ltUnknown;
+}
+
+#ifdef INSANE_VERSION
+
+/* **********************************************************************
+
+FakeCalibration()
+
+If DoOriginate() and this Calibration code is skipped,
+we should at least provide for some fake measurements.
+Thus a test scan of the scanner's inside is possible.
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState FakeCalibration(TInstance *this)
+{
+ if (this->calibration.bCalibrated)
+ return SANE_STATUS_GOOD;
+ this->calibration.bCalibrated=true;
+ if (!this->calibration.achStripeY)
+ {
+ this->calibration.achStripeY=calloc(1,MAX_PIXEL_PER_SCANLINE);
+ if (!this->calibration.achStripeY)
+ return SetError(this,SANE_STATUS_NO_MEM,"no memory for calib Y");
+ }
+ memset(this->calibration.achStripeY,0xC0,MAX_PIXEL_PER_SCANLINE);
+ /* scan *every* nonsense */
+ this->calibration.xMargin=this->calibration.yMargin=0;
+ return SANE_STATUS_GOOD;
+}
+
+#endif
+
+/* **********************************************************************
+
+DoCalibration() and friends
+
+********************************************************************** */
+
+#define SM3600_CALIB_USE_MEDIAN
+#define SM3600_CALIB_APPLY_HANNING_WINDOW
+
+#ifdef SM3600_CALIB_USE_MEDIAN
+typedef int (*TQSortProc)(const void *, const void *);
+
+static
+int CompareProc(const unsigned char *p1, const unsigned char *p2)
+{
+ return *p1 - *p2;
+}
+#endif
+
+#define MAX_CALIB_STRIPES 8
+
+__SM3600EXPORT__
+TState DoCalibration(TInstance *this)
+{
+#ifdef SM3600_CALIB_USE_RMS
+ long aulSum[MAX_PIXEL_PER_SCANLINE];
+#endif
+#ifdef SM3600_CALIB_USE_MEDIAN
+ unsigned char aauchY[MAX_CALIB_STRIPES][MAX_PIXEL_PER_SCANLINE];
+ unsigned char auchRow[MAX_CALIB_STRIPES];
+#endif
+#ifdef SM3600_CALIB_APPLY_HANNING_WINDOW
+ unsigned char auchHanning[MAX_PIXEL_PER_SCANLINE];
+#endif
+
+ int iLine,i;
+ int yStart,cStripes,cyGap;
+ TState rc;
+ if (this->calibration.bCalibrated)
+ return SANE_STATUS_GOOD;
+
+ switch (this->model)
+ {
+ case sm3600:
+ yStart=200;
+ cStripes=MAX_CALIB_STRIPES;
+ cyGap=10;
+ break;
+ case sm3700: /* in fact, the 3600 calibration should do!!! */
+ case sm3750:
+ default:
+ yStart=100; /* 54 is perimeter */
+ cStripes=MAX_CALIB_STRIPES;
+ cyGap=10;
+ break;
+ } /* switch */
+
+ DoJog(this,yStart);
+ /* scan a gray line at 600 DPI */
+ if (!this->calibration.achStripeY)
+ {
+ this->calibration.achStripeY=calloc(1,MAX_PIXEL_PER_SCANLINE);
+ if (!this->calibration.achStripeY)
+ return SetError(this,SANE_STATUS_NO_MEM,"no memory for calib Y");
+ }
+#ifdef SM3600_CALIB_USE_RMS
+ memset(aulSum,0,sizeof(aulSum));
+#endif
+ for (iLine=0; iLine<cStripes; iLine++)
+ {
+ dprintf(DEBUG_CALIB,"calibrating %i...\n",iLine);
+ RegWriteArray(this,R_ALL, 74, auchRegsSingleLine);
+ INST_ASSERT();
+ RegWrite(this,R_CTL, 1, 0x59); /* #2496[062.5] */
+ RegWrite(this,R_CTL, 1, 0xD9); /* #2497[062.5] */
+ rc=WaitWhileScanning(this,5); if (rc) { return rc; }
+ if (BulkReadBuffer(this,
+#ifdef SM3600_CALIB_USE_RMS
+ this->calibration.achStripeY,
+#endif
+#ifdef SM3600_CALIB_USE_MEDIAN
+ aauchY[iLine],
+#endif
+ MAX_PIXEL_PER_SCANLINE)
+ !=MAX_PIXEL_PER_SCANLINE)
+ return SetError(this,SANE_STATUS_IO_ERROR,"truncated bulk");
+#ifdef SM3600_CALIB_USE_RMS
+ for (i=0; i<MAX_PIXEL_PER_SCANLINE; i++)
+ aulSum[i]+=(long)this->calibration.achStripeY[i]*
+ (long)this->calibration.achStripeY[i];
+#endif
+ DoJog(this,cyGap);
+ }
+#ifdef SM3600_CALIB_USE_RMS
+ for (i=0; i<MAX_PIXEL_PER_SCANLINE; i++)
+ this->calibration.achStripeY[i]=(unsigned char)(int)sqrt(aulSum[i]/cStripes);
+#endif
+#ifdef SM3600_CALIB_USE_MEDIAN
+ /* process the collected lines rowwise. Use intermediate buffer for qsort */
+ for (i=0; i<MAX_PIXEL_PER_SCANLINE; i++)
+ {
+ for (iLine=0; iLine<cStripes; iLine++)
+ auchRow[iLine]=aauchY[iLine][i];
+ qsort(auchRow,cStripes, sizeof(unsigned char), (TQSortProc)CompareProc);
+ this->calibration.achStripeY[i]=auchRow[(cStripes-1)/2];
+ }
+#endif
+#ifdef SM3600_CALIB_APPLY_HANNING_WINDOW
+ memcpy(auchHanning,this->calibration.achStripeY,sizeof(auchHanning));
+ for (i=1; i<MAX_PIXEL_PER_SCANLINE-1; i++)
+ this->calibration.achStripeY[i]=(unsigned char)
+ ((2*(int)auchHanning[i]+auchHanning[i-1]+auchHanning[i+1])/4);
+#endif
+
+ DoJog(this,-yStart-cStripes*cyGap);
+ INST_ASSERT();
+ this->calibration.bCalibrated=true;
+ return SANE_STATUS_GOOD;
+}
+
+/* **********************************************************************
+
+DoOriginate()
+
+*shall* one time move the slider safely back to its origin.
+No idea, hoiw to achieve this, for now...
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState DoOriginate(TInstance *this, TBool bStepOut)
+{
+ TLineType lt;
+ if (this->bVerbose)
+ fprintf(stderr,"carriage return...\n");
+ DBG(DEBUG_INFO,"DoOriginate()\n");
+ INST_ASSERT();
+ lt=GetLineType(this);
+ /* if we are already at home, fine. If not, first jump a bit forward */
+ DBG(DEBUG_JUNK,"lt1=%d\n",(int)lt);
+ if (lt!=ltHome && bStepOut) DoJog(this,150);
+ while (lt!=ltHome && !this->state.bCanceled)
+ {
+ lt=GetLineType(this);
+ DBG(DEBUG_JUNK,"lt2=%d\n",(int)lt);
+ INST_ASSERT();
+ switch (lt)
+ {
+ case ltHome: continue;
+ case ltBed: DoJog(this,-240); break; /* worst case: 1 cm */
+ default: DoJog(this,-15); break; /* 0.X mm */
+ }
+ }
+ DoJog(this,1); INST_ASSERT(); /* Correction for 1 check line */
+ DBG(DEBUG_JUNK,"lt3=%d\n",(int)lt);
+ if (this->state.bCanceled)
+ return SANE_STATUS_CANCELLED;
+ return DoCalibration(this);
+}
+
+/* **********************************************************************
+
+DoJog(nDistance)
+
+The distance is given in 600 DPI.
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState DoJog(TInstance *this, int nDistance)
+{
+ int cSteps;
+ int nSpeed,nRest;
+ dprintf(DEBUG_SCAN,"jogging %d units...\n",nDistance);
+ if (!nDistance) return 0;
+ RegWrite(this,0x34, 1, 0x63);
+ RegWrite(this,0x49, 1, 0x96);
+ WaitWhileBusy(this,2);
+ RegWrite(this,0x34, 1, 0x63);
+ RegWrite(this,0x49, 1, 0x9E); /* that is a difference! */
+ INST_ASSERT();
+ cSteps=(nDistance>0) ? nDistance : -nDistance;
+ {
+ unsigned char uchRegs2587[]={
+ 0x00 /*0x01*/, 0x00 /*0x02*/, 0x3F /*0x03*/,
+ 0x40 /*!!0x04!!*/, 0x00 /*!!0x05!!*/,
+ 0,0, /* steps */
+ 0x00 /*0x08*/, 0x00 /*!!0x09!!*/,
+ 0,0, /* y count */
+ 0x6D /*0x0C*/,
+ 0x70 /*0x0D*/, 0x69 /*0x0E*/, 0xD0 /*0x0F*/,
+ 0x00 /*0x10*/, 0x00 /*0x11*/, 0x40 /*0x12*/,
+ 0x15 /*0x13*/, 0x80 /*0x14*/, 0x2A /*0x15*/,
+ 0xC0 /*0x16*/, 0x40 /*0x17*/, 0xC0 /*0x18*/,
+ 0x40 /*0x19*/, 0xFF /*0x1A*/, 0x01 /*0x1B*/,
+ 0x88 /*0x1C*/, 0x40 /*0x1D*/, 0x4C /*0x1E*/,
+ 0x50 /*0x1F*/, 0x00 /*0x20*/, 0x0C /*0x21*/,
+ 0x21 /*0x22*/, 0xF0 /*0x23*/, 0x40 /*0x24*/,
+ 0x00 /*0x25*/, 0x0A /*0x26*/, 0xF0 /*0x27*/,
+ 0x00 /*0x28*/, 0x00 /*0x29*/, 0x4E /*0x2A*/,
+ 0xF0 /*0x2B*/, 0x00 /*0x2C*/, 0x00 /*0x2D*/,
+ 0x4E /*0x2E*/, 0x88 /*R_CCAL*/, 0x88 /*R_CCAL2*/,
+ 0x84 /*R_CCAL3*/, 0xEA /*R_LEN*/, 0x24 /*R_LENH*/,
+ 0x63 /*0x34*/, 0x29 /*0x35*/, 0x00 /*0x36*/,
+ 0x00 /*0x37*/, 0x00 /*0x38*/, 0x00 /*0x39*/,
+ 0x00 /*0x3A*/, 0x00 /*0x3B*/, 0xFF /*0x3C*/,
+ 0x0F /*0x3D*/, 0x00 /*0x3E*/, 0x00 /*0x3F*/,
+ 0x01 /*0x40*/, 0x00 /*0x41*/, 0x80 /*R_CSTAT*/,
+ 0x03 /*R_SPD*/, 0x01 /*0x44*/, 0x00 /*0x45*/,
+ 0x79 /*!!R_CTL!!*/, 0xC0 /*0x47*/, 0x40 /*0x48*/,
+ 0x9E /*!!0x49!!*/, 0xD8 /*0x4A*/ };
+ RegWriteArray(this,R_ALL, 74, uchRegs2587);
+ } /* #2587[065.4] */
+ INST_ASSERT();
+ RegWrite(this,R_STPS,2,cSteps);
+ /* do some magic for slider acceleration */
+ if (cSteps>600) /* only large movements are accelerated */
+ {
+ RegWrite(this,0x34, 1, 0xC3);
+ RegWrite(this,0x47, 2, 0xA000); /* initial speed */
+ }
+ /* start back or forth movement */
+ if (nDistance>0)
+ {
+ RegWrite(this,R_CTL, 1, 0x39); /* #2588[065.4] */
+ RegWrite(this,R_CTL, 1, 0x79); /* #2589[065.4] */
+ RegWrite(this,R_CTL, 1, 0xF9); /* #2590[065.4] */
+ }
+ else
+ {
+ RegWrite(this,R_CTL, 1, 0x59);
+ RegWrite(this,R_CTL, 1, 0xD9);
+ }
+ INST_ASSERT();
+ /* accelerate the slider each 100 us */
+ if (cSteps>600)
+ {
+ nRest=cSteps;
+ for (nSpeed=0x9800; nRest>600 && nSpeed>=0x4000; nSpeed-=0x800)
+ {
+ nRest=RegRead(this,R_POS, 2);
+ usleep(100);
+ /* perhaps 40C0 is the fastest possible value */
+ RegWrite(this,0x47, 2, nSpeed>0x4000 ? nSpeed : 0x40C0);
+ }
+ }
+ INST_ASSERT();
+ usleep(100);
+ return WaitWhileBusy(this,1000); /* thanks Mattias Ellert */
+}
+
diff --git a/backend/sm3600-scanmtek.c b/backend/sm3600-scanmtek.c
new file mode 100644
index 0000000..4aa4f1b
--- /dev/null
+++ b/backend/sm3600-scanmtek.c
@@ -0,0 +1,310 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ======================================================================
+
+Userspace scan tool for the Microtek 3600 scanner
+
+$Id$
+
+====================================================================== */
+
+#include "sm3600-scantool.h"
+
+static struct {
+ TModel model;
+ unsigned short idProduct;
+ } aScanners[]={
+ { sm3600, 0x40B3 },
+ { sm3600, 0x40CA },
+ { sm3600, 0x40FF },
+ { sm3700, 0x40B8 },
+ { sm3700, 0x40CB },
+ { sm3750, 0x40dd },
+ { sm3600, 0x40FF }, /* unknown */
+ { unknown, 0x0000 } };
+
+__SM3600EXPORT__
+TModel GetScannerModel(unsigned short idVendor,
+ unsigned short idProduct)
+{
+ int i;
+ if (idVendor!=SCANNER_VENDOR) return unknown;
+ for (i=0; aScanners[i].model!=unknown; i++)
+ if (aScanners[i].idProduct==idProduct)
+ return aScanners[i].model;
+ return unknown;
+}
+
+/* **********************************************************************
+
+DoInit()
+
+Replay the first initialisation block (no slider movement).
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState DoInit(TInstance *this)
+{
+ unsigned char uchRegs2466[]={
+ 0x00 /*0x01*/, 0x00 /*0x02*/, 0x3F /*0x03*/,
+ 0x10 /*0x04*/, 0xC0 /*0x05*/, 0x00 /*0x06*/,
+ 0x00 /*0x07*/, 0xFF /*0x08*/, 0xFF /*0x09*/,
+ 0x20 /*0x0A*/, 0x00 /*0x0B*/, 0x6D /*0x0C*/,
+ 0x70 /*0x0D*/, 0x69 /*0x0E*/, 0xD0 /*0x0F*/,
+ 0x00 /*0x10*/, 0x00 /*0x11*/, 0x40 /*0x12*/,
+ 0x15 /*0x13*/, 0x80 /*0x14*/, 0x2A /*0x15*/,
+ 0xC0 /*0x16*/, 0x40 /*0x17*/, 0xC0 /*0x18*/,
+ 0x40 /*0x19*/, 0xFF /*0x1A*/, 0x01 /*0x1B*/,
+ 0x88 /*0x1C*/, 0x40 /*0x1D*/, 0x4C /*0x1E*/,
+ 0x50 /*0x1F*/, 0x00 /*0x20*/, 0x0C /*0x21*/,
+ 0x21 /*0x22*/, 0xF0 /*0x23*/, 0x40 /*0x24*/,
+ 0x00 /*0x25*/, 0x0A /*0x26*/, 0xF0 /*0x27*/,
+ 0x00 /*0x28*/, 0x00 /*0x29*/, 0x4E /*0x2A*/,
+ 0xF0 /*0x2B*/, 0x00 /*0x2C*/, 0x00 /*0x2D*/,
+ 0x4E /*0x2E*/, 0x80 /*R_CCAL*/, 0x80 /*R_CCAL2*/,
+ 0x80 /*R_CCAL3*/, 0x29 /* */, 0x35 /* */,
+ 0x63 /*0x34*/, 0x29 /*0x35*/, 0x00 /*0x36*/,
+ 0x00 /*0x37*/, 0x00 /*0x38*/, 0x00 /*0x39*/,
+ 0x00 /*0x3A*/, 0x00 /*0x3B*/, 0xFF /*0x3C*/,
+ 0x0F /*0x3D*/, 0x00 /*0x3E*/, 0x00 /*0x3F*/,
+ 0x01 /*0x40*/, 0x00 /*0x41*/, 0x00 /*R_CSTAT*/,
+ 0x03 /*R_SPD*/, 0x01 /*0x44*/, 0x00 /*0x45*/,
+ 0x39 /*R_CTL*/, 0xC0 /*0x47*/, 0x40 /*0x48*/,
+ 0x96 /*0x49*/, 0xD8 /*0x4A*/ };
+ dprintf(DEBUG_SCAN,"general init...\n");
+ return RegWriteArray(this, R_ALL, 74, uchRegs2466);
+}
+
+/* **********************************************************************
+
+DoReset()
+
+Resets Scanner after CANCEL in current scan job.
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState DoReset(TInstance *this)
+{
+ RegWrite(this,0x43, 1, 0x03); /* #1533[038.1] */
+ RegWrite(this,0x43, 1, 0x03); /* #1534[038.1] */
+ RegRead(this,R_POS, 2); /*=0x1375*/ /* #1535[038.6] */
+ RegWrite(this,R_CTL, 1, 0x39); /* #1536[038.6] */
+ {
+ unsigned char uchRegs1537[]={
+ /*R_SPOS*/ 0x00, /*R_SPOSH*/ 0x00, /*0x03*/ 0x00,
+ /*R_SWID*/ 0x00, /*R_SWIDH*/ 0x00, /*R_STPS*/ 0x00,
+ /*R_STPSH*/ 0x00, /*0x08*/ 0x00, /*0x09*/ 0x00,
+ /*R_LEN*/ 0x00, /*R_LENH*/ 0x00, /*0x0C*/ 0x6D,
+ /*0x0D*/ 0x70, /*0x0E*/ 0x69, /*0x0F*/ 0xD0,
+ /*0x10*/ 0x00, /*0x11*/ 0x00, /*0x12*/ 0x40,
+ /*0x13*/ 0x15, /*0x14*/ 0x80, /*0x15*/ 0x2A,
+ /*0x16*/ 0xC0, /*0x17*/ 0x40, /*0x18*/ 0xC0,
+ /*0x19*/ 0x40, /*0x1A*/ 0xFF, /*0x1B*/ 0x01,
+ /*0x1C*/ 0x88, /*0x1D*/ 0x40, /*0x1E*/ 0x4C,
+ /*0x1F*/ 0x50, /*0x20*/ 0x00, /*0x21*/ 0x0C,
+ /*0x22*/ 0x21, /*0x23*/ 0xF0, /*0x24*/ 0x40,
+ /*0x25*/ 0x00, /*0x26*/ 0x0A, /*0x27*/ 0xF0,
+ /*0x28*/ 0x00, /*0x29*/ 0x00, /*0x2A*/ 0x4E,
+ /*0x2B*/ 0xF0, /*0x2C*/ 0x00, /*0x2D*/ 0x00,
+ /*0x2E*/ 0x4E, /*R_CCAL*/ 0x80, /*R_CCAL2*/ 0x80,
+ /*R_CCAL3*/ 0x80, /*0x32*/ 0x4D, /*0x33*/ 0x35,
+ /*0x34*/ 0x83, /*0x35*/ 0x29, /*0x36*/ 0x00,
+ /*0x37*/ 0x00, /*0x38*/ 0x00, /*0x39*/ 0x00,
+ /*0x3A*/ 0x00, /*0x3B*/ 0x00, /*0x3C*/ 0xFF,
+ /*0x3D*/ 0x0F, /*0x3E*/ 0x00, /*0x3F*/ 0x00,
+ /*0x40*/ 0x01, /*0x41*/ 0x80, /*R_CSTAT*/ 0x00,
+ /*0x43*/ 0x03, /*R_LMP*/ 0x01, /*0x45*/ 0x00,
+ /*R_CTL*/ 0x39, /*0x47*/ 0xC0, /*0x48*/ 0x40,
+ /*0x49*/ 0x9E, /*0x4A*/ 0x8C };
+ RegWriteArray(this,R_ALL, 74, uchRegs1537);
+ } /* #1537[038.6] */
+ INST_ASSERT();
+ RegWrite(this,R_CTL, 1, 0x39); /* #1538[038.6] */
+ RegWrite(this,R_CTL, 1, 0x79); /* #1539[038.7] */
+ RegWrite(this,R_CTL, 1, 0xF9); /* #1540[038.7] */
+ WaitWhileScanning(this,2);
+ INST_ASSERT();
+ RegWrite(this,R_CTL, 1, 0x39); /* #1542[038.7] */
+ RegWrite(this,0x43, 1, 0x07); /* #1543[038.7] */
+ WaitWhileBusy(this,2);
+ INST_ASSERT();
+ RegWrite(this,0x32, 2, 0x354D); /* #1545[038.7] */
+ RegWrite(this,0x34, 1, 0xC3); /* #1546[038.7] */
+ RegWrite(this,0x49, 1, 0x9E); /* #1547[038.7] */
+ INST_ASSERT();
+ return SANE_STATUS_GOOD;
+}
+
+/* **********************************************************************
+
+WaitWhileBusy()
+
+NOTE: Semantics changed: 0 on success, -1 else
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState WaitWhileBusy(TInstance *this, int cSecs)
+{
+ int cTimeOut=cSecs*10;
+ int value;
+ INST_ASSERT();
+ while (cTimeOut--)
+ {
+ if ((value=(int)RegRead(this,R_CTL,1)) & 0x80)
+ usleep(50);
+ else
+ return 0;
+ }
+ return SetError(this,SANE_STATUS_IO_ERROR,"Timeout while waiting for CTL");
+}
+
+/* **********************************************************************
+
+WaitWhileScanning()
+
+NOTE: Semantics changed: 0 on success, -1 else
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState WaitWhileScanning(TInstance *this, int cSecs)
+{
+ int cTimeOut=cSecs*10;
+ int value;
+ INST_ASSERT();
+ while (cTimeOut--)
+ {
+ if ((value=(int)RegRead(this,R_CSTAT, 1)) & 0x80)
+ return 0;
+ else
+ usleep(50);
+ }
+ return SetError(this,SANE_STATUS_IO_ERROR,"Timeout while waiting for CSTAT");
+}
+
+#ifdef INSANE_VERSION
+
+/* **********************************************************************
+
+DoLampSwitch(nRegister)
+
+0x01 should switch the lamp ON
+0x02 should swuitch it OFF
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState DoLampSwitch(TInstance *this, int nPattern)
+{
+ return RegWrite(this, R_LMP, 1, nPattern);
+}
+
+#endif
+
+/* **********************************************************************
+
+UploadGammaTable()
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState UploadGammaTable(TInstance *this, int iByteAddress, SANE_Int *pnGamma)
+{
+ unsigned char *puchGamma;
+ TState rc;
+ int i;
+ rc=SANE_STATUS_GOOD;
+ INST_ASSERT();
+ puchGamma=malloc(0x2000);
+ if (!puchGamma) return SetError(this,SANE_STATUS_NO_MEM,"gamma buffer");
+ DBG(DEBUG_INFO,"uploading gamma to %d\n",iByteAddress);
+ for (i=0; i<0x1000; i++)
+ {
+ int nVal=pnGamma[i];
+ /* nVal=i; */
+ puchGamma[2*i+1]=nVal>>8;
+ puchGamma[2*i+0]=nVal&0xFF;
+ }
+ for (i=0; rc==SANE_STATUS_GOOD && i<0x2000; i+=0x1000)
+ rc=MemWriteArray(this,(i+iByteAddress)>>1,0x1000,puchGamma+i);
+ free(puchGamma);
+ return rc;
+}
+
+/* **********************************************************************
+
+UploadGainCorrection()
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState UploadGainCorrection(TInstance *this, int iTableOffset)
+{
+ {
+ struct TGain {
+ unsigned char uchLow;
+ unsigned char uchHigh;
+ } aGain[0x2000]; /* 16 KB */
+ int i,iOff;
+ unsigned short uwGain;
+
+ /*
+ Oopsi: correction data starts at the left of the scanning window!
+ */
+ iOff=this->param.x/2+this->calibration.xMargin;
+ memset(aGain,0xFF,sizeof(aGain));
+ RegWrite(this,0x3D,1,0x0F | 0x80); /* 10XXXXXX : one offset table */
+ RegWrite(this,0x3F,1, iTableOffset==0x6000 ? 0x18 : 0x08); /* 16KB gain at 0x06000 or 0x02000 */
+ for (i=iOff; i<MAX_PIXEL_PER_SCANLINE; i++)
+ {
+ uwGain=this->calibration.achStripeY[i]<<4;
+ aGain[i-iOff].uchLow =(unsigned char)(uwGain&0xFF);
+ aGain[i-iOff].uchHigh=(unsigned char)(uwGain>>8);
+ }
+ for (i=0; i<0x4000; i+=0x1000)
+ MemWriteArray(this,(iTableOffset+i)>>1,0x1000,((unsigned char*)aGain)+i);
+ }
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/sm3600-scantool.h b/backend/sm3600-scantool.h
new file mode 100644
index 0000000..94a4875
--- /dev/null
+++ b/backend/sm3600-scantool.h
@@ -0,0 +1,134 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef SCANTOOL_H
+#define SCANTOOL_H
+
+/* ======================================================================
+
+ common declarations and definitions.
+
+ (C) Marian Eichholz 2001
+
+====================================================================== */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <errno.h>
+
+#include <usb.h>
+
+#ifdef INSANE_VERSION
+
+/* make real exports */
+#define __SM3600EXPORT__
+
+typedef enum { SANE_STATUS_GOOD,
+ SANE_STATUS_CANCELLED,
+ SANE_STATUS_UNSUPPORTED,
+ SANE_STATUS_EOF,
+ SANE_STATUS_NO_MEM,
+ SANE_STATUS_IO_ERROR,
+ SANE_STATUS_ACCESS_DENIED,
+ SANE_STATUS_INVAL,
+ SANE_STATUS_DEVICE_BUSY,
+} SANE_Status;
+
+typedef int SANE_Int;
+
+#endif
+
+#include "sm3600.h"
+
+extern char *achErrorMessages[];
+
+#ifdef INSANE_VERSION
+
+void DBG(int nLevel, const char *szFormat, ...);
+
+/* ====================================================================== */
+
+#ifdef INSTANTIATE_VARIABLES
+#define GLOBAL
+char *achErrorMessages[]={ "everything fine",
+ "operation canceled",
+ "unsupported function",
+ "end of scan or file",
+ "memory overflow",
+ "input/output error",
+ "permission problem",
+ "invalid parameter",
+ "device busy",
+};
+#else
+#define GLOBAL extern
+#endif
+
+#define PFMT_DEFAULT 0
+#define PFMT_PBM 1
+#define PFMT_PCL 2
+#define PFMT_TIFFG4 3
+
+/* ====================================================================== */
+
+GLOBAL unsigned long ulDebugMask;
+GLOBAL TBool bVerbose;
+GLOBAL TBool bInteractive;
+GLOBAL char *szLogFile;
+GLOBAL char *szScanFile;
+GLOBAL int idOutputFormat;
+
+GLOBAL TInstance devInstance;
+
+/* ====================================================================== */
+
+#endif /* INSANE_VERSION */
+
+/* ====================================================================== */
+
+#endif
diff --git a/backend/sm3600-scanusb.c b/backend/sm3600-scanusb.c
new file mode 100644
index 0000000..3ca9a8a
--- /dev/null
+++ b/backend/sm3600-scanusb.c
@@ -0,0 +1,443 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ======================================================================
+
+Userspace scan tool for the Microtek 3600 scanner
+
+$Id$
+
+(C) Marian Eichholz 2001
+
+26.4.2001: Added an abstraction layer for TransferControlMsg.
+
+====================================================================== */
+
+#include "sm3600-scantool.h"
+
+/* **********************************************************************
+
+TransferControlMsg()
+
+********************************************************************** */
+
+static int TransferControlMsg(TInstance *this,
+ int nReqType,
+ int nRequest,
+ int nValue,
+ int nIndex,
+ void *pBuffer,
+ int cchBuffer,
+ int cJiffiesTimeout)
+{
+ SANE_Status err;
+
+ cJiffiesTimeout = cJiffiesTimeout;
+
+ err = sanei_usb_control_msg (this->hScanner,
+ nReqType,
+ nRequest,
+ nValue,
+ nIndex,
+ cchBuffer,
+ pBuffer);
+ if (err)
+ return err;
+ return cchBuffer;
+}
+
+/* **********************************************************************
+
+cch=BulkRead()
+
+********************************************************************** */
+
+static int TransferBulkRead(TInstance *this,
+ int nEndPoint,
+ void *pBuffer,
+ int cchMax,
+ int cJiffiesTimeout)
+{
+ int err;
+ size_t sz = cchMax;
+
+ nEndPoint = nEndPoint;
+ cJiffiesTimeout = cJiffiesTimeout;
+
+ err = sanei_usb_read_bulk(this->hScanner,
+ pBuffer,
+ &sz);
+ if (err)
+ return err;
+ return sz;
+}
+
+/* **********************************************************************
+
+RegWrite(iRegister, cb, ulValue)
+RegWriteArray(iRegister, cb, unsigned char uchValues)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState RegWrite(TInstance *this, int iRegister, int cb, unsigned long ulValue)
+{
+ char *pchBuffer;
+ int i;
+ TBool bOk=true;
+ INST_ASSERT();
+ /* some rough assertions */
+ if (cb<1 || cb>4)
+ return SetError(this,SANE_STATUS_INVAL,"unsupported control transfer size %d",cb);
+ pchBuffer=malloc(cb);
+ CHECK_POINTER(pchBuffer);
+ for (i=0; i<cb; i++)
+ {
+ pchBuffer[i]=(char)(ulValue&0xFF);
+ ulValue=ulValue>>8;
+ }
+ if (!bOk)
+ {
+ free(pchBuffer);
+ return SetError(this,SANE_STATUS_IO_ERROR,
+ "error in reg out: %d,%d,%08X",iRegister,cb,ulValue);
+ }
+ i=TransferControlMsg(this, /* handle */
+ 0x40, /* request type */
+ 0x08, /* request */
+ iRegister, /* value */
+ 0, /* index */
+ pchBuffer, cb, /* bytes, size */
+ USB_TIMEOUT_JIFFIES); /* TO, jiffies... */
+ free(pchBuffer);
+ if (i<0)
+ return SetError(this,SANE_STATUS_IO_ERROR,"error during register write");
+ return SANE_STATUS_GOOD;
+}
+
+__SM3600EXPORT__
+TState RegWriteArray(TInstance *this, int iRegister, int cb, unsigned char *pchBuffer)
+{
+ int i;
+ INST_ASSERT();
+ /* some rough assertions */
+ i=TransferControlMsg(this, /* handle */
+ 0x40, /* request type */
+ 0x08, /* request */
+ iRegister, /* value */
+ 0, /* index */
+ pchBuffer, cb, /* bytes, size */
+ USB_TIMEOUT_JIFFIES); /* TO, jiffies... */
+ if (i<0)
+ return SetError(this,SANE_STATUS_IO_ERROR,"error during register write");
+ return SANE_STATUS_GOOD;
+}
+
+/* **********************************************************************
+
+MemWriteArray(iAddress, cb, ulValue)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState MemWriteArray(TInstance *this, int iAddress,
+ int cb, unsigned char *pchBuffer)
+{
+ int i;
+ INST_ASSERT();
+ /* some rough assertions */
+ i=TransferControlMsg(this,
+ 0x40, /* request type */
+ 0x09, /* request */
+ iAddress, /* value */
+ 0, /* index */
+ pchBuffer, cb, /* bytes, size */
+ 10000); /* TO, jiffies... */
+ if (i<0)
+ return SetError(this,SANE_STATUS_IO_ERROR,"error during memory write");
+ return SANE_STATUS_GOOD;
+}
+
+/* **********************************************************************
+
+MemReadArray(iRegister, cb, ulValue)
+
+********************************************************************** */
+
+#ifdef INSANE_VERSION
+
+__SM3600EXPORT__
+TState MemReadArray(TInstance *this, int iAddress, int cb, unsigned char *pchBuffer)
+{
+ int i;
+ INST_ASSERT();
+ /* some rough assertions */
+ i=TransferControlMsg(this,
+ 0xC0, /* request type */
+ 0x01, /* request */
+ iAddress, /* value */
+ 0, /* index */
+ pchBuffer, cb, /* bytes, size */
+ USB_TIMEOUT_JIFFIES); /* TO, jiffies... */
+ if (i<0)
+ return SetError(this,SANE_STATUS_IO_ERROR,"error during memory read");
+ return SANE_STATUS_GOOD;
+}
+
+/* **********************************************************************
+
+RegCheck(iRegister, cb, ulValue)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState RegCheck(TInstance *this, int iRegister, int cch, unsigned long ulValue)
+{
+ char *pchBuffer,*pchTransfer;
+ int i,rcCode;
+ TBool bOk;
+ INST_ASSERT();
+ if (cch<1 || cch>3)
+ return SetError(this,SANE_STATUS_INVAL,"unsupported control transfer size %d",cch);
+ pchBuffer=malloc(cch);
+ pchTransfer=calloc(1,cch);
+ rcCode=SANE_STATUS_GOOD;
+ if (!pchBuffer || !pchTransfer)
+ {
+ if (pchBuffer) free(pchBuffer);
+ if (pchTransfer) free(pchTransfer);
+ rcCode=SetError(this, SANE_STATUS_NO_MEM, "no memory in RegCheck()");
+ }
+ bOk=true;
+ for (i=0; !rcCode && i<cch; i++)
+ {
+ pchBuffer[i]=(char)(ulValue&0x00FF);
+ ulValue=(ulValue>>8);
+ }
+ if (!rcCode)
+ {
+ if (!bOk)
+ rcCode=SetError(this,SANE_STATUS_IO_ERROR,
+ "error in reg out: %d,%d,%08X",iRegister,cch,ulValue);
+ else
+ {
+ i=TransferControlMsg(this, /* handle */
+ 0xC0, /* request type */
+ 0x00, /* request */
+ iRegister, /* value */
+ 0, /* index */
+ pchTransfer, cch, /* bytes, size */
+ USB_TIMEOUT_JIFFIES); /* TO, jiffies... */
+ if (i<0)
+ rcCode=SetError(this,SANE_STATUS_IO_ERROR,
+ "error during register check");
+ }
+ }
+ if (!rcCode && memcmp(pchTransfer,pchBuffer,cch))
+ {
+ DumpBuffer(stdout,pchTransfer,cch);
+ rcCode=SetError(this,SANE_STATUS_IO_ERROR,
+ "check register failed for %d,%d,%08X",
+ iRegister,cch,ulValue);
+ }
+ free(pchTransfer); free(pchBuffer);
+ return rcCode;
+}
+
+/* **********************************************************************
+
+cchRead=BulkRead(fh,cchBulk)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+int BulkRead(TInstance *this, FILE *fhOut, unsigned int cchBulk)
+{
+ int cchRead,rc;
+ char *pchBuffer;
+ INST_ASSERT();
+ pchBuffer=(char*)malloc(cchBulk);
+ CHECK_POINTER(pchBuffer);
+ cchRead=0;
+ rc=0;
+ while (!rc && cchBulk)
+ {
+ int cchChunk;
+ int cchReal;
+
+ cchChunk=cchBulk;
+ if (cchChunk>0x1000)
+ cchChunk=0x1000;
+ cchReal=TransferBulkRead(this,
+ 0x82,
+ pchBuffer+cchRead,
+ cchChunk,
+ USB_TIMEOUT_JIFFIES);
+ dprintf(DEBUG_COMM,"bulk read: %d -> %d\n",cchChunk,cchReal);
+ if (cchReal>=0)
+ {
+ cchBulk-=cchReal;
+ cchRead+=cchReal;
+ if (cchReal<cchChunk) /* last Chunk of a series */
+ break;
+ }
+ else
+ {
+ rc=SetError(this,SANE_STATUS_IO_ERROR,
+ "bulk read of %d bytes failed: %s",
+ cchChunk,
+ "I/O error"
+ );
+ continue;
+ }
+ }
+ dprintf(DEBUG_COMM,"writing %d bytes\n",cchRead);
+ if (fhOut && !rc)
+ {
+ fwrite(pchBuffer,1,cchRead,fhOut);
+ if (ferror(fhOut))
+ rc=SetError(this,SANE_STATUS_IO_ERROR,
+ "scan file write failed: %s",
+ strerror(errno));
+ }
+ free(pchBuffer);
+ return rc ? -1 : cchRead;
+}
+
+#endif
+
+/* **********************************************************************
+
+cchRead=BulkReadBuffer(puchBuffer, cchBulk)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+int BulkReadBuffer(TInstance *this,
+ unsigned char *puchBufferOut,
+ unsigned int cchBulk)
+{
+ int cchRead,rc;
+ char *pchBuffer;
+ INST_ASSERT();
+ pchBuffer=(char*)malloc(cchBulk);
+ CHECK_POINTER(pchBuffer);
+ cchRead=0;
+ rc=0;
+ while (!rc && cchBulk)
+ {
+ int cchChunk;
+ int cchReal;
+
+ cchChunk=cchBulk;
+ if (cchChunk>0x1000)
+ cchChunk=0x1000;
+ cchReal=TransferBulkRead(this,
+ 0x82,
+ pchBuffer+cchRead,
+ cchChunk,
+ USB_TIMEOUT_JIFFIES);
+ dprintf(DEBUG_COMM,"bulk read: %d -> %d\n",cchChunk,cchReal);
+ if (cchReal>=0)
+ {
+ cchBulk-=cchReal;
+ cchRead+=cchReal;
+ if (cchReal<cchChunk) /* last Chunk of a series */
+ break;
+ }
+ else
+ rc=SetError(this,SANE_STATUS_IO_ERROR,
+ "bulk read of %d bytes failed: %s",
+ cchChunk,
+ "I/O error"
+ );
+ }
+ dprintf(DEBUG_COMM,"writing %d bytes\n",cchRead);
+
+ if (!rc && puchBufferOut)
+ memcpy(puchBufferOut,pchBuffer,cchRead);
+ free(pchBuffer);
+ return rc ? -1 : cchRead;
+}
+
+/* **********************************************************************
+
+RegRead(iRegister, int cch)
+
+Read register in big endian (INTEL-) format.
+
+********************************************************************** */
+
+__SM3600EXPORT__
+unsigned int RegRead(TInstance *this, int iRegister, int cch)
+{
+ char *pchTransfer;
+ int i;
+ unsigned int n;
+ INST_ASSERT();
+ if (cch<1 || cch>4)
+ {
+ SetError(this,SANE_STATUS_INVAL,
+ "unsupported control read size %d",cch);
+ return 0;
+ }
+ pchTransfer=calloc(1,cch);
+ CHECK_POINTER(pchTransfer);
+ i=TransferControlMsg(this, /* handle */
+ 0xC0, /* request type */
+ 0x00, /* request */
+ iRegister, /* value */
+ 0, /* index */
+ pchTransfer, cch, /* bytes, size */
+ USB_TIMEOUT_JIFFIES); /* TO, jiffies... */
+ if (i>=0)
+ {
+ n=0;
+ for (i=cch-1; i>=0; i--)
+ n=(n<<8)|(unsigned char)pchTransfer[i];
+ free(pchTransfer);
+ return n;
+ }
+ free(pchTransfer);
+ SetError(this,SANE_STATUS_IO_ERROR,"error during register read");
+ return 0;
+}
+
diff --git a/backend/sm3600-scanutil.c b/backend/sm3600-scanutil.c
new file mode 100644
index 0000000..62ba0f1
--- /dev/null
+++ b/backend/sm3600-scanutil.c
@@ -0,0 +1,440 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/* ======================================================================
+
+Userspace scan tool for the Microtek 3600 scanner
+
+$Id$
+
+====================================================================== */
+
+#include <unistd.h>
+#include "sm3600-scantool.h"
+
+/* **********************************************************************
+
+dprintf(DEBUG_XXXX, format, ...)
+
+Put a debug message on STDERR (or whatever). The message is prefixed with
+a "debug:" and given, if the current debugging flags contain the given
+flag "ulType".
+
+********************************************************************** */
+
+#ifdef INSANE_VERSION
+void DBG(int nLevel, const char *szFormat, ...)
+{
+ szFormat++;
+}
+#endif
+
+__SM3600EXPORT__
+void debug_printf(unsigned long ulType, const char *szFormat, ...)
+{
+ va_list ap;
+ if ((ulDebugMask & ulType)!=ulType) return;
+ if (*szFormat=='~')
+ szFormat++;
+ else
+ fprintf(stderr,"debug:");
+ va_start(ap,szFormat);
+ vfprintf(stderr,szFormat,ap);
+ va_end(ap);
+}
+
+/* **********************************************************************
+
+SetError(error, format, ...)
+
+The program is aborted, all handles and ressources are freed (this
+being global) and the user gets a nice panic screen :-)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+int SetError(TInstance *this, int nError, const char *szFormat, ...)
+{
+ va_list ap;
+ if (this->nErrorState) return 0; /* do not overwrite error state */
+ this->nErrorState=nError;
+ this->szErrorReason=malloc(500);
+
+ if (szFormat!=NULL && this->szErrorReason)
+ {
+ va_start(ap,szFormat);
+ vsnprintf(this->szErrorReason,499,szFormat,ap);
+ va_end(ap);
+ this->szErrorReason[499]='\0';
+ }
+ return nError;
+}
+
+#ifdef INSANE_VERSION
+
+/* **********************************************************************
+
+DumpBuffer(fh,pch,cch)
+
+********************************************************************** */
+
+__SM3600EXPORT__
+void DumpBuffer(FILE *fh, const char *pch, int cch)
+{
+ int i=0;
+ while (i<cch)
+ {
+ if (!(i & 15))
+ {
+ if (i) fprintf(fh,"\n");
+ fprintf(fh,"%04X:",i);
+ }
+ fprintf(fh," %02X",(unsigned char)pch[i]);
+ i++;
+ }
+ fprintf(fh,"\n");
+}
+
+#endif
+
+/* **********************************************************************
+
+FreeState()
+
+Frees all dynamical memory for scan buffering.
+
+********************************************************************** */
+
+__SM3600EXPORT__
+TState FreeState(TInstance *this, TState nReturn)
+{
+ if (this->state.ppchLines)
+ {
+ int i;
+ for (i=0; i<this->state.cBacklog; i++)
+ {
+ if (this->state.ppchLines[i])
+ free(this->state.ppchLines[i]);
+ }
+ free(this->state.ppchLines);
+ }
+ if (this->state.pchLineOut) free(this->state.pchLineOut);
+ if (this->state.pchBuf) free(this->state.pchBuf);
+ this->state.pchBuf =NULL;
+ this->state.pchLineOut=NULL;
+ this->state.ppchLines =NULL;
+ return nReturn;
+}
+
+/* ======================================================================
+
+EndScan()
+
+====================================================================== */
+
+__SM3600EXPORT__
+TState EndScan(TInstance *this)
+{
+ if (!this->state.bScanning) return SANE_STATUS_GOOD;
+ /* move slider back to start */
+ this->state.bScanning=false;
+ FreeState(this,0);
+ INST_ASSERT();
+ return DoJog(this,-this->state.cyTotalPath);
+}
+
+/* ======================================================================
+
+TState CancelScan(TInstance *this)
+
+====================================================================== */
+
+__SM3600EXPORT__
+TState CancelScan(TInstance *this)
+{
+ TBool bCanceled;
+ DBG(DEBUG_INFO,"CancelScan() called\n");
+
+ this->state.cyTotalPath-=RegRead(this,R_POS,2);
+ DBG(DEBUG_JUNK,"stepping back %d steps\n",this->state.cyTotalPath);
+ /* this->state.cyTotalPath=0; */
+
+ usleep(200);
+ DoReset(this);
+ EndScan(this); /* and step back! */
+
+ DBG(DEBUG_JUNK,"cs4: %d\n",(int)this->nErrorState);
+ bCanceled=this->state.bCanceled;
+ this->state.bCanceled=false; /* re-enable Origination! */
+ if (!this->bOptSkipOriginate)
+ DoOriginate(this,false); /* have an error here... */
+ this->state.bCanceled=bCanceled;
+ DBG(DEBUG_JUNK,"cs5: %d\n",(int)this->nErrorState);
+ INST_ASSERT();
+ DBG(DEBUG_INFO,"cs6: ok.\n");
+ return SANE_STATUS_CANCELLED; /* or shall be say GOOD? */
+}
+
+
+/* ======================================================================
+
+ReadChunk()
+
+====================================================================== */
+
+__SM3600EXPORT__
+TState ReadChunk(TInstance *this, unsigned char *achOut,
+ int cchMax, int *pcchRead)
+{
+ /* have we to copy more than we have? */
+ /* can the current line fill the buffer ? */
+ int rc;
+ *pcchRead=0;
+ INST_ASSERT();
+ if (!this->state.bScanning)
+ return SANE_STATUS_CANCELLED; /* deferred cancel? */
+ if (this->state.bCanceled) /* deferred cancellation? */
+ return CancelScan(this);
+ INST_ASSERT();
+ /* 22.4.2001: This took me hard, harder, hardest:*/
+
+ /* We need to fill the line buffer with at least a *rest* of a
+ line. A single line will do. */
+ /* Thus, "iLine>0" is a suitable condition. */
+ /* Without the preread, there will a dummy line be read, if the
+ target buffer is large enough.*/
+ if (this->state.iLine)
+ rc=SANE_STATUS_GOOD;
+ else
+ rc=(*(this->state.ReadProc))(this); /* preread one line */
+ if (rc!=SANE_STATUS_GOOD) return rc;
+ dprintf(DEBUG_BUFFER,"Chunk-Init: cchMax = %d\n",cchMax);
+ while (this->state.iReadPos + cchMax > this->state.cchLineOut)
+ {
+ int cch;
+ /* copy rest of the line into target */
+ cch = this->state.cchLineOut - this->state.iReadPos;
+ memcpy(achOut,
+ this->state.pchLineOut+this->state.iReadPos,
+ cch);
+ cchMax-=cch; /* advance parameters */
+ achOut+=cch;
+ (*pcchRead)+=cch;
+ this->state.iReadPos=0;
+ rc=(*(this->state.ReadProc))(this);
+ dprintf(DEBUG_BUFFER,"Chunk-Read: cchMax = %d\n",cchMax);
+ if (rc!=SANE_STATUS_GOOD)
+ return rc; /* should be NOT(!) EOF, but then: good and away! */
+ }
+ dprintf(DEBUG_BUFFER,"Chunk-Exit: cchMax = %d\n",cchMax);
+ if (!cchMax) return SANE_STATUS_GOOD; /* now everything fits! */
+ (*pcchRead) += cchMax;
+ memcpy(achOut,
+ this->state.pchLineOut+this->state.iReadPos,
+ cchMax);
+ this->state.iReadPos += cchMax;
+ return SANE_STATUS_GOOD;
+}
+
+/* ======================================================================
+
+GetAreaSize()
+
+====================================================================== */
+
+__SM3600EXPORT__
+void GetAreaSize(TInstance *this)
+{
+ /* this->state.cxPixel : pixels, we *want* (after interpolation)
+ this->state.cxMax : pixels, we *need* (before interpolation) */
+ int nRefResX,nRefResY;
+ nRefResX=nRefResY=this->param.res;
+ switch (this->param.res)
+ {
+ case 75: nRefResX=100; this->state.nFixAspect=75; break;
+ default: this->state.nFixAspect=100; break;
+ }
+ this->state.cxPixel =this->param.cx*this->param.res/1200;
+ this->state.cyPixel =this->param.cy*this->param.res/1200;
+ this->state.cxMax =this->state.cxPixel*100/this->state.nFixAspect;
+ this->state.cxWindow =this->state.cxMax*600/nRefResX;
+ this->state.cyWindow =this->state.cyPixel*600/nRefResY;
+ dprintf(DEBUG_SCAN,"requesting %d[600] %d[real] %d[raw]\n",
+ this->state.cxWindow,this->state.cxPixel,this->state.cxMax);
+}
+
+/* ======================================================================
+
+ResetCalibration()
+
+Free calibration data. The Instance can be safely released afterwards.
+
+====================================================================== */
+
+__SM3600EXPORT__
+void ResetCalibration(TInstance *this)
+{
+ if (this->calibration.achStripeY)
+ free(this->calibration.achStripeY);
+ if (this->calibration.achStripeR)
+ free(this->calibration.achStripeR);
+ if (this->calibration.achStripeG)
+ free(this->calibration.achStripeG);
+ if (this->calibration.achStripeB)
+ free(this->calibration.achStripeB);
+ /* reset all handles, pointers, flags */
+ memset(&(this->calibration),0,sizeof(this->calibration));
+ /* TODO: type specific margins */
+ this->calibration.xMargin=200;
+ this->calibration.yMargin=0x019D;
+ this->calibration.nHoleGray=10;
+ this->calibration.rgbBias=0x888884;
+ this->calibration.nBarGray=0xC0;
+}
+
+/* ======================================================================
+
+InitGammaTables()
+
+Init gammy tables and gain tables within controller memory.
+
+====================================================================== */
+
+__SM3600EXPORT__
+TState InitGammaTables(TInstance *this, int nBrightness, int nContrast)
+{
+ long i;
+ long lOffset;
+ long lScale;
+ /* the rescaling is done with temporary zero translation to 2048 */
+ lOffset=(nBrightness-128)*16; /* signed! */
+ lScale=(nContrast+128)*100; /* in percent */
+ for (i=0; i<4096; i++)
+ {
+ int n=(int)((i+lOffset)*lScale/12800L+2048L);
+ if (n<0) n=0;
+ else if (n>4095) n=4095;
+ this->agammaY[i]=n;
+ this->agammaR[i]=n;
+ this->agammaG[i]=n;
+ this->agammaB[i]=n;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef INSANE_VERSION
+
+/* ======================================================================
+
+DoScanFile()
+
+Top level caller for scantool.
+
+====================================================================== */
+
+#define APP_CHUNK_SIZE 0x8000
+
+__SM3600EXPORT__
+TState DoScanFile(TInstance *this)
+{
+ int cx,cy;
+ long lcchRead;
+ TState rc;
+ char *achBuf;
+
+ achBuf=malloc(APP_CHUNK_SIZE);
+ rc=SANE_STATUS_GOOD; /* make compiler happy */
+ rc=InitGammaTables(this, this->param.nBrightness, this->param.nContrast);
+ if (rc!=SANE_STATUS_GOOD) return rc;
+ if (this->mode==color)
+ rc=StartScanColor(this);
+ else
+ rc=StartScanGray(this);
+ cx=this->state.cxPixel;
+ cy=this->state.cyPixel;
+ if (this->bVerbose)
+ fprintf(stderr,"scanning %d by %d\n",cx,cy);
+ if (this->fhScan && !this->bWriteRaw && !this->pchPageBuffer)
+ {
+ switch (this->mode)
+ {
+ case color: fprintf(this->fhScan,"P6\n%d %d\n255\n",cx,cy);
+ break;
+ case gray: fprintf(this->fhScan,"P5\n%d %d\n255\n",cx,cy);
+ break;
+ default: fprintf(this->fhScan,"P4\n%d %d\n",cx,cy);
+ break;
+ }
+ }
+ lcchRead=0L;
+ while (!rc)
+ {
+ int cch;
+ cch=0;
+ rc=ReadChunk(this,achBuf,APP_CHUNK_SIZE,&cch);
+ if (cch>0 && this->fhScan && cch<=APP_CHUNK_SIZE)
+ {
+ if (this->pchPageBuffer)
+ {
+#ifdef SM3600_DEBUGPAGEBUFFER
+ if (this->bVerbose)
+ fprintf(stderr,"ichPageBuffer:%d, cch:%d, cchPageBuffer:%d\n",
+ this->ichPageBuffer,cch,this->cchPageBuffer);
+#endif
+ CHECK_ASSERTION(this->ichPageBuffer+cch<=this->cchPageBuffer);
+ memcpy(this->pchPageBuffer+this->ichPageBuffer,
+ achBuf,cch);
+ this->ichPageBuffer+=cch;
+ }
+ else if (!this->bWriteRaw)
+ fwrite(achBuf,1,cch,this->fhScan);
+ lcchRead+=cch;
+ }
+ }
+ free(achBuf);
+ if (this->bVerbose)
+ fprintf(stderr,"read %ld image byte(s)\n",lcchRead);
+ EndScan(this);
+ INST_ASSERT();
+ return SANE_STATUS_GOOD;
+}
+
+#endif
diff --git a/backend/sm3600.c b/backend/sm3600.c
new file mode 100644
index 0000000..dd1f31f
--- /dev/null
+++ b/backend/sm3600.c
@@ -0,0 +1,804 @@
+/* sane - Scanner Access Now Easy.
+ (C) Marian Matthias Eichholz 2001
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements SANE backend for Microtek scanners with M011 USB
+ chip like the Microtek ScanMaker 3600, 3700 and 3750. */
+
+
+/* ======================================================================
+
+sm3600.c
+
+SANE backend master module
+
+(C) Marian Matthias Eichholz 2001
+
+Start: 2.4.2001
+
+====================================================================== */
+
+#include "../include/sane/config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define BUILD 6
+
+#ifndef BACKEND_NAME
+#define BACKEND_NAME sm3600
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+
+#undef HAVE_LIBUSB
+
+/* prevent inclusion of scantool.h */
+#define SCANTOOL_H
+/* make no real function export, since we include the modules */
+#define __SM3600EXPORT__ static
+
+/* if defined, *before* sm3600.h inclusion */
+#define SM3600_SUPPORT_EXPOSURE
+
+#include "sm3600.h"
+
+static unsigned long ulDebugMask;
+
+static int num_devices;
+static TDevice *pdevFirst;
+static TInstance *pinstFirst;
+
+/* ====================================================================== */
+
+#include "sm3600-scanutil.c"
+#include "sm3600-scanusb.c"
+#include "sm3600-scanmtek.c"
+#include "sm3600-homerun.c"
+#include "sm3600-gray.c"
+#include "sm3600-color.c"
+
+/* ======================================================================
+
+Initialise SANE options
+
+====================================================================== */
+
+typedef enum { optCount,
+ optGroupMode, optMode, optResolution,
+#ifdef SM3600_SUPPORT_EXPOSURE
+ optBrightness, optContrast,
+#endif
+ optPreview, optGrayPreview,
+ optGroupGeometry,optTLX, optTLY, optBRX, optBRY,
+ optGroupEnhancement,
+ optGammaY, optGammaR,optGammaG,optGammaB,
+ optLast } TOptionIndex;
+
+static const SANE_String_Const aScanModes[]= { "color", "gray", "lineart",
+ "halftone", NULL };
+
+static const SANE_Range rangeXmm = {
+ SANE_FIX(0),
+ SANE_FIX(220),
+ SANE_FIX(0.1) };
+
+static const SANE_Range rangeYmm = {
+ SANE_FIX(0),
+ SANE_FIX(300),
+ SANE_FIX(0.1) };
+
+#ifdef SM3600_SUPPORT_EXPOSURE
+static const SANE_Range rangeLumi = {
+ SANE_FIX(-100.0),
+ SANE_FIX(100.0),
+ SANE_FIX(1.0) };
+#endif
+
+static const SANE_Range rangeGamma = { 0, 4095, 1 };
+
+static const SANE_Int setResolutions[] = { 5, 75,100,200,300,600 };
+
+static
+SANE_Status
+InitOptions(TInstance *this)
+{
+ TOptionIndex iOpt;
+ if (optLast!=NUM_OPTIONS)
+ {
+ DBG(1,"NUM_OPTIONS does not fit!");
+ return SANE_STATUS_INVAL;
+ }
+ memset(this->aoptDesc,0,sizeof(this->aoptDesc));
+ memset(this->aoptVal,0,sizeof(this->aoptVal));
+ InitGammaTables(this,0,0);
+ for (iOpt=optCount; iOpt!=optLast; iOpt++)
+ {
+ static char *achNamesXY[]= {
+ SANE_NAME_SCAN_TL_X, SANE_NAME_SCAN_TL_Y,
+ SANE_NAME_SCAN_BR_X, SANE_NAME_SCAN_BR_Y };
+ static char *achTitlesXY[]= {
+ SANE_TITLE_SCAN_TL_X, SANE_TITLE_SCAN_TL_Y,
+ SANE_TITLE_SCAN_BR_X, SANE_TITLE_SCAN_BR_Y };
+ static char *achDescXY[]= {
+ SANE_DESC_SCAN_TL_X, SANE_DESC_SCAN_TL_Y,
+ SANE_DESC_SCAN_BR_X, SANE_DESC_SCAN_BR_Y };
+ static double afFullBed[] = { 22.0,30.0, 50.0, 80.0 }; /* TODO: calculate exactly! */
+ static const SANE_Range *aRangesXY[] = { &rangeXmm,&rangeYmm,&rangeXmm,&rangeYmm };
+ SANE_Option_Descriptor *pdesc;
+ Option_Value *pval;
+ /* shorthands */
+ pdesc=this->aoptDesc+iOpt;
+ pval=this->aoptVal+iOpt;
+ /* default */
+ pdesc->size=sizeof(SANE_Word);
+ pdesc->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ /*
+ Some hints:
+ *every* field needs a constraint, elseway there will be a warning.
+ */
+
+ switch (iOpt)
+ {
+ case optCount:
+ pdesc->title =SANE_TITLE_NUM_OPTIONS;
+ pdesc->desc =SANE_DESC_NUM_OPTIONS;
+ pdesc->type =SANE_TYPE_INT;
+ pdesc->cap =SANE_CAP_SOFT_DETECT;
+ pval->w =(SANE_Word)optLast;
+ break;
+ case optGroupMode:
+ pdesc->title="Mode";
+ pdesc->desc ="";
+ pdesc->type = SANE_TYPE_GROUP;
+ pdesc->cap = SANE_CAP_ADVANCED;
+ break;
+ case optMode:
+ pdesc->name =SANE_NAME_SCAN_MODE;
+ pdesc->title =SANE_TITLE_SCAN_MODE;
+ pdesc->desc ="Select the scan mode";
+ pdesc->type =SANE_TYPE_STRING;
+ pdesc->size =20;
+ pdesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pdesc->constraint.string_list = aScanModes;
+ pval->s = strdup(aScanModes[color]);
+ break;
+ case optResolution:
+ pdesc->name =SANE_NAME_SCAN_RESOLUTION;
+ pdesc->title =SANE_TITLE_SCAN_RESOLUTION;
+ pdesc->desc =SANE_DESC_SCAN_RESOLUTION;
+ pdesc->type =SANE_TYPE_INT;
+ pdesc->unit =SANE_UNIT_DPI;
+ pdesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pdesc->constraint.word_list = setResolutions;
+ pval->w =75;
+ break;
+#ifdef SM3600_SUPPORT_EXPOSURE
+ case optBrightness:
+ pdesc->name =SANE_NAME_BRIGHTNESS;
+ pdesc->title =SANE_TITLE_BRIGHTNESS;
+ pdesc->desc =SANE_DESC_BRIGHTNESS;
+ pdesc->type =SANE_TYPE_FIXED;
+ pdesc->unit =SANE_UNIT_PERCENT;
+ pdesc->constraint_type =SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range=&rangeLumi;
+ pval->w =SANE_FIX(0);
+ break;
+ case optContrast:
+ pdesc->name =SANE_NAME_CONTRAST;
+ pdesc->title =SANE_TITLE_CONTRAST;
+ pdesc->desc =SANE_DESC_CONTRAST;
+ pdesc->type =SANE_TYPE_FIXED;
+ pdesc->unit =SANE_UNIT_PERCENT;
+ pdesc->constraint_type =SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range=&rangeLumi;
+ pval->w =SANE_FIX(0);
+ break;
+#endif
+ case optPreview:
+ pdesc->name =SANE_NAME_PREVIEW;
+ pdesc->title =SANE_TITLE_PREVIEW;
+ pdesc->desc =SANE_DESC_PREVIEW;
+ pdesc->type =SANE_TYPE_BOOL;
+ pval->w =SANE_FALSE;
+ break;
+ case optGrayPreview:
+ pdesc->name =SANE_NAME_GRAY_PREVIEW;
+ pdesc->title =SANE_TITLE_GRAY_PREVIEW;
+ pdesc->desc =SANE_DESC_GRAY_PREVIEW;
+ pdesc->type =SANE_TYPE_BOOL;
+ pval->w =SANE_FALSE;
+ break;
+ case optGroupGeometry:
+ pdesc->title="Geometry";
+ pdesc->desc ="";
+ pdesc->type = SANE_TYPE_GROUP;
+ pdesc->constraint_type=SANE_CONSTRAINT_NONE;
+ pdesc->cap = SANE_CAP_ADVANCED;
+ break;
+ case optTLX: case optTLY: case optBRX: case optBRY:
+ pdesc->name =achNamesXY[iOpt-optTLX];
+ pdesc->title =achTitlesXY[iOpt-optTLX];
+ pdesc->desc =achDescXY[iOpt-optTLX];
+ pdesc->type =SANE_TYPE_FIXED;
+ pdesc->unit =SANE_UNIT_MM; /* arghh */
+ pdesc->constraint_type =SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range=aRangesXY[iOpt-optTLX];
+ pval->w =SANE_FIX(afFullBed[iOpt-optTLX]);
+ break;
+ case optGroupEnhancement:
+ pdesc->title="Enhancement";
+ pdesc->desc ="";
+ pdesc->type = SANE_TYPE_GROUP;
+ pdesc->constraint_type=SANE_CONSTRAINT_NONE;
+ pdesc->cap = SANE_CAP_ADVANCED;
+ break;
+ case optGammaY:
+ pdesc->name = SANE_NAME_GAMMA_VECTOR;
+ pdesc->title = SANE_TITLE_GAMMA_VECTOR;
+ pdesc->desc = SANE_DESC_GAMMA_VECTOR;
+ pdesc->type = SANE_TYPE_INT;
+ pdesc->unit = SANE_UNIT_NONE;
+ pdesc->size = 4096*sizeof(SANE_Int);
+ pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range = &rangeGamma;
+ pval->wa = this->agammaY;
+ break;
+ case optGammaR:
+ pdesc->name = SANE_NAME_GAMMA_VECTOR_R;
+ pdesc->title = SANE_TITLE_GAMMA_VECTOR_R;
+ pdesc->desc = SANE_DESC_GAMMA_VECTOR_R;
+ pdesc->type = SANE_TYPE_INT;
+ pdesc->unit = SANE_UNIT_NONE;
+ pdesc->size = 4096*sizeof(SANE_Int);
+ pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range = &rangeGamma;
+ pval->wa = this->agammaR;
+ break;
+ case optGammaG:
+ pdesc->name = SANE_NAME_GAMMA_VECTOR_G;
+ pdesc->title = SANE_TITLE_GAMMA_VECTOR_G;
+ pdesc->desc = SANE_DESC_GAMMA_VECTOR_G;
+ pdesc->type = SANE_TYPE_INT;
+ pdesc->unit = SANE_UNIT_NONE;
+ pdesc->size = 4096*sizeof(SANE_Int);
+ pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range = &rangeGamma;
+ pval->wa = this->agammaG;
+ break;
+ case optGammaB:
+ pdesc->name = SANE_NAME_GAMMA_VECTOR_B;
+ pdesc->title = SANE_TITLE_GAMMA_VECTOR_B;
+ pdesc->desc = SANE_DESC_GAMMA_VECTOR_B;
+ pdesc->type = SANE_TYPE_INT;
+ pdesc->unit = SANE_UNIT_NONE;
+ pdesc->size = 4096*sizeof(SANE_Int);
+ pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pdesc->constraint.range = &rangeGamma;
+ pval->wa = this->agammaB;
+ break;
+ case optLast: /* not reached */
+ break;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+RegisterSaneDev (TModel model, SANE_String_Const szName)
+{
+ TDevice * q;
+
+ errno = 0;
+
+ q = malloc (sizeof (*q));
+ if (!q)
+ return SANE_STATUS_NO_MEM;
+
+ memset (q, 0, sizeof (*q)); /* clear every field */
+ q->szSaneName = strdup (szName);
+ q->sane.name = (SANE_String_Const) q->szSaneName;
+ q->sane.vendor = "Microtek";
+ q->sane.model = "ScanMaker 3600";
+ q->sane.type = "flatbed scanner";
+
+ q->model=model;
+
+ ++num_devices;
+ q->pNext = pdevFirst; /* link backwards */
+ pdevFirst = q;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+sm_usb_attach (SANE_String_Const dev_name)
+{
+ int fd;
+ SANE_Status err;
+ SANE_Word v, p;
+ TModel model;
+
+ err = sanei_usb_open(dev_name, &fd);
+ if (err)
+ return err;
+ err = sanei_usb_get_vendor_product (fd, &v, &p);
+ if (err)
+ {
+ sanei_usb_close (fd);
+ return err;
+ }
+ DBG (DEBUG_JUNK, "found dev %04X/%04X, %s\n", v, p, dev_name);
+ model = GetScannerModel (v, p);
+ if (model != unknown)
+ RegisterSaneDev (model, dev_name);
+
+ sanei_usb_close(fd);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authCB)
+{
+ int i;
+
+ DBG_INIT();
+
+ authCB=authCB; /* compiler */
+
+ DBG(DEBUG_VERBOSE,"SM3600 init\n");
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ DBG(DEBUG_VERBOSE,"SM3600 version: %x\n",
+ SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, BUILD));
+ }
+
+ pdevFirst=NULL;
+
+ sanei_usb_init();
+ for (i = 0; aScanners[i].idProduct; i++)
+ {
+ sanei_usb_find_devices(SCANNER_VENDOR, aScanners[i].idProduct, sm_usb_attach);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static const SANE_Device ** devlist = 0; /* only pseudo-statical */
+
+void
+sane_exit (void)
+{
+ TDevice *dev, *pNext;
+
+ /* free all bound resources and instances */
+ while (pinstFirst)
+ sane_close((SANE_Handle)pinstFirst); /* free all resources */
+
+ /* free all device descriptors */
+ for (dev = pdevFirst; dev; dev = pNext)
+ {
+ pNext = dev->pNext;
+ free (dev->szSaneName);
+ free (dev);
+ }
+ if (devlist) free(devlist);
+ devlist=NULL;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only)
+{
+ TDevice *dev;
+ int i;
+
+ local_only = TRUE; /* Avoid compile warning */
+
+ if (devlist) free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = pdevFirst; i < num_devices; dev = dev->pNext)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle *handle)
+{
+ TDevice *pdev;
+ TInstance *this;
+ SANE_Status rc;
+ DBG(DEBUG_VERBOSE,"opening %s\n",devicename);
+ if (devicename[0]) /* selected */
+ {
+ for (pdev=pdevFirst; pdev; pdev=pdev->pNext)
+{
+DBG(DEBUG_VERBOSE,"%s<>%s\n",devicename, pdev->sane.name);
+ if (!strcmp(devicename,pdev->sane.name))
+ break;
+}
+ /* no dynamic post-registration */
+ }
+ else
+ pdev=pdevFirst;
+ if (!pdev)
+ return SANE_STATUS_INVAL;
+ this = (TInstance*) calloc(1,sizeof(TInstance));
+ if (!this) return SANE_STATUS_NO_MEM;
+
+ *handle = (SANE_Handle)this;
+
+ ResetCalibration(this); /* do not release memory */
+ this->pNext=pinstFirst; /* register open handle */
+ pinstFirst=this;
+ this->model=pdev->model; /* memorize model */
+ /* open and prepare USB scanner handle */
+
+ if (sanei_usb_open (devicename, &this->hScanner) != SANE_STATUS_GOOD)
+ return SetError (this, SANE_STATUS_IO_ERROR, "cannot open scanner device");
+
+ rc = SANE_STATUS_GOOD;
+
+ this->quality=fast;
+ return InitOptions(this);
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ TInstance *this,*pParent,*p;
+ this=(TInstance*)handle;
+ DBG(DEBUG_VERBOSE,"closing scanner\n");
+ if (this->hScanner)
+ {
+ if (this->state.bScanning)
+ EndScan(this);
+
+ sanei_usb_close(this->hScanner);
+ this->hScanner=-1;
+ }
+ ResetCalibration(this); /* release calibration data */
+ /* unlink active device entry */
+ pParent=NULL;
+ for (p=pinstFirst; p; p=p->pNext)
+ {
+ if (p==this) break;
+ pParent=p;
+ }
+
+ if (!p)
+ {
+ DBG(1,"invalid handle in close()\n");
+ return;
+ }
+ /* delete instance from instance list */
+ if (pParent)
+ pParent->pNext=this->pNext;
+ else
+ pinstFirst=this->pNext; /* NULL with last entry */
+ /* free resources */
+ if (this->pchPageBuffer)
+ free(this->pchPageBuffer);
+ if (this->szErrorReason)
+ {
+ DBG(DEBUG_VERBOSE,"Error status: %d, %s",
+ this->nErrorState, this->szErrorReason);
+ free(this->szErrorReason);
+ }
+ free(this);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int iOpt)
+{
+ TInstance *this=(TInstance*)handle;
+ if (iOpt<NUM_OPTIONS)
+ return this->aoptDesc+iOpt;
+ return NULL;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int iOpt,
+ SANE_Action action, void *pVal,
+ SANE_Int *pnInfo)
+{
+ SANE_Word cap;
+ SANE_Status rc;
+ TInstance *this;
+ this=(TInstance*)handle;
+ rc=SANE_STATUS_GOOD;
+ if (pnInfo)
+ *pnInfo=0;
+
+ if (this->state.bScanning)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (iOpt>=NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap=this->aoptDesc[iOpt].cap;
+
+ switch (action)
+ {
+
+ /* ------------------------------------------------------------ */
+
+ case SANE_ACTION_GET_VALUE:
+ switch ((TOptionIndex)iOpt)
+ {
+ case optCount:
+ case optPreview:
+ case optGrayPreview:
+ case optResolution:
+#ifdef SM3600_SUPPORT_EXPOSURE
+ case optBrightness:
+ case optContrast:
+#endif
+ case optTLX: case optTLY: case optBRX: case optBRY:
+ *(SANE_Word*)pVal = this->aoptVal[iOpt].w;
+ break;
+ case optMode:
+ strcpy(pVal,this->aoptVal[iOpt].s);
+ break;
+ case optGammaY:
+ case optGammaR:
+ case optGammaG:
+ case optGammaB:
+ DBG(DEBUG_INFO,"getting gamma\n");
+ memcpy(pVal,this->aoptVal[iOpt].wa, this->aoptDesc[iOpt].size);
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ /* ------------------------------------------------------------ */
+
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE(cap))
+ return SANE_STATUS_INVAL;
+ rc=sanei_constrain_value(this->aoptDesc+iOpt,pVal,pnInfo);
+ if (rc!=SANE_STATUS_GOOD)
+ return rc;
+ switch ((TOptionIndex)iOpt)
+ {
+ case optResolution:
+ case optTLX: case optTLY: case optBRX: case optBRY:
+ if (pnInfo) (*pnInfo) |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through side effect free */
+#ifdef SM3600_SUPPORT_EXPOSURE
+ case optBrightness:
+ case optContrast:
+#endif
+ case optPreview:
+ case optGrayPreview:
+ this->aoptVal[iOpt].w = *(SANE_Word*)pVal;
+ break;
+ case optMode:
+ if (pnInfo)
+ (*pnInfo) |= SANE_INFO_RELOAD_PARAMS
+ | SANE_INFO_RELOAD_OPTIONS;
+ strcpy(this->aoptVal[iOpt].s,pVal);
+ break;
+ case optGammaY:
+ case optGammaR: case optGammaG: case optGammaB:
+ DBG(DEBUG_INFO,"setting gamma #%d\n",iOpt);
+ memcpy(this->aoptVal[iOpt].wa, pVal, this->aoptDesc[iOpt].size);
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_UNSUPPORTED;
+ } /* switch action */
+ return rc; /* normally GOOD */
+}
+
+static SANE_Status
+SetupInternalParameters(TInstance *this)
+{
+ int i;
+ this->param.res=(int)this->aoptVal[optResolution].w;
+#ifdef SM3600_SUPPORT_EXPOSURE
+ this->param.nBrightness=(int)(this->aoptVal[optBrightness].w>>SANE_FIXED_SCALE_SHIFT);
+ this->param.nContrast=(int)(this->aoptVal[optContrast].w>>SANE_FIXED_SCALE_SHIFT);
+#else
+ this->param.nBrightness=0;
+ this->param.nContrast=0;
+#endif
+ this->param.x=(int)(SANE_UNFIX(this->aoptVal[optTLX].w)*1200.0/25.4);
+ this->param.y=(int)(SANE_UNFIX(this->aoptVal[optTLY].w)*1200.0/25.4);
+ this->param.cx=(int)(SANE_UNFIX(this->aoptVal[optBRX].w-this->aoptVal[optTLX].w)*1200.0/25.4)+1;
+ this->param.cy=(int)(SANE_UNFIX(this->aoptVal[optBRY].w-this->aoptVal[optTLY].w)*1200.0/25.4)+1;
+ for (i=0; aScanModes[i]; i++)
+ if (!strcasecmp(this->aoptVal[optMode].s,aScanModes[i]))
+ {
+ this->mode=(TMode)i;
+ break;
+ }
+ DBG(DEBUG_INFO,"mode=%d, res=%d, BC=[%d,%d], xywh=[%d,%d,%d,%d]\n",
+ this->mode, this->param.res,
+ this->param.nBrightness, this->param.nContrast,
+ this->param.x,this->param.y,this->param.cx,this->param.cy);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *p)
+{
+ /* extremly important for xscanimage */
+ TInstance *this;
+ this=(TInstance*)handle;
+ SetupInternalParameters(this);
+ GetAreaSize(this);
+ p->pixels_per_line=this->state.cxPixel;
+ /* TODO: we need a more stable cyPixel prediction */
+ p->lines=this->state.cyPixel;
+ p->last_frame=SANE_TRUE;
+ switch (this->mode)
+ {
+ case color:
+ p->format=SANE_FRAME_RGB;
+ p->depth=8;
+ p->bytes_per_line=p->pixels_per_line*3;
+ break;
+ case gray:
+ p->format=SANE_FRAME_GRAY;
+ p->depth=8;
+ p->bytes_per_line=p->pixels_per_line;
+ break;
+ case halftone:
+ case line:
+ p->format=SANE_FRAME_GRAY;
+ p->depth=1;
+ p->bytes_per_line=(p->pixels_per_line+7)/8;
+ break;
+ }
+ DBG(DEBUG_INFO,"getting parameters (%d,%d)...\n",p->bytes_per_line,p->lines);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ TInstance *this;
+ SANE_Status rc;
+ this=(TInstance*)handle;
+ DBG(DEBUG_VERBOSE,"starting scan...\n");
+ if (this->state.bScanning) return SANE_STATUS_DEVICE_BUSY;
+ rc=SetupInternalParameters(this);
+ this->state.bCanceled=false;
+ if (!rc) rc=DoInit(this); /* oopsi, we should initalise :-) */
+ if (!rc && !this->bOptSkipOriginate) rc=DoOriginate(this,true);
+ if (!rc) rc=DoJog(this,this->calibration.yMargin);
+ if (rc) return rc;
+ this->state.bEOF=false;
+ switch (this->mode)
+ {
+ case color: rc=StartScanColor(this); break;
+ default: rc=StartScanGray(this); break;
+ }
+ if (this->state.bCanceled) return SANE_STATUS_CANCELLED;
+ return rc;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *puchBuffer,
+ SANE_Int cchMax,
+ SANE_Int *pcchRead)
+{
+ SANE_Status rc;
+ TInstance *this;
+ this=(TInstance*)handle;
+ DBG(DEBUG_INFO,"reading chunk %d...\n",(int)cchMax);
+ *pcchRead=0;
+ if (this->state.bEOF)
+ return SANE_STATUS_EOF;
+ rc=ReadChunk(this,puchBuffer,cchMax,pcchRead);
+ DBG(DEBUG_INFO,"... line %d (%d/%d)...\n",this->state.iLine,*pcchRead,rc);
+ switch (rc)
+ {
+ case SANE_STATUS_EOF:
+ this->state.bEOF=true; /* flag EOF on next read() */
+ rc=SANE_STATUS_GOOD; /* we do not flag THIS block! */
+ break;
+ case SANE_STATUS_GOOD:
+ if (!*pcchRead) rc=SANE_STATUS_EOF;
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ TInstance *this;
+ this=(TInstance*)handle;
+ DBG(DEBUG_VERBOSE,"cancel called...\n");
+ if (this->state.bScanning)
+ {
+ this->state.bCanceled=true;
+ if (this->state.bEOF) /* regular (fast) cancel */
+ {
+ DBG(DEBUG_INFO,"regular end cancel\n");
+ EndScan(this);
+ DoJog(this,-this->calibration.yMargin);
+ }
+ else
+ {
+ /* since Xsane does not continue scanning,
+ we cannot defer cancellation */
+ DBG(DEBUG_INFO,"hard cancel called...\n");
+ CancelScan(this);
+ }
+ }
+}
+
+SANE_Status
+sane_set_io_mode(SANE_Handle h, SANE_Bool m)
+{
+ h=h;
+ if (m==SANE_TRUE) /* no non-blocking-mode */
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd(SANE_Handle handle, SANE_Int *fd)
+{
+ handle=handle; fd=fd;
+ return SANE_STATUS_UNSUPPORTED; /* we have no file IO */
+}
diff --git a/backend/sm3600.h b/backend/sm3600.h
new file mode 100644
index 0000000..58e6907
--- /dev/null
+++ b/backend/sm3600.h
@@ -0,0 +1,315 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) Marian Matthias Eichholz 2001
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef _H_SM3600
+#define _H_SM3600
+
+/* ======================================================================
+
+sm3600.h
+
+SANE backend master module.
+
+Definitions ported from "scantool.h" 5.4.2001.
+
+(C) Marian Matthias Eichholz 2001
+
+Start: 2.4.2001
+
+====================================================================== */
+
+#define DEBUG_SCAN 0x0001
+#define DEBUG_COMM 0x0002
+#define DEBUG_ORIG 0x0004
+#define DEBUG_BASE 0x0011
+#define DEBUG_DEVSCAN 0x0012
+#define DEBUG_REPLAY 0x0014
+#define DEBUG_BUFFER 0x0018
+#define DEBUG_SIGNALS 0x0020
+#define DEBUG_CALIB 0x0040
+
+#define DEBUG_CRITICAL 1
+#define DEBUG_VERBOSE 2
+#define DEBUG_INFO 3
+#define DEBUG_JUNK 5
+
+#define USB_TIMEOUT_JIFFIES 2000
+
+#define SCANNER_VENDOR 0x05DA
+
+#define MAX_PIXEL_PER_SCANLINE 5300
+
+/* ====================================================================== */
+
+typedef enum { false, true } TBool;
+
+typedef SANE_Status TState;
+
+typedef enum { unknown, sm3600, sm3700, sm3750 } TModel;
+
+typedef struct {
+ TBool bCalibrated;
+ int xMargin; /* in 1/600 inch */
+ int yMargin; /* in 1/600 inch */
+ unsigned char nHoleGray;
+ unsigned char nBarGray;
+ long rgbBias;
+ unsigned char *achStripeY;
+ unsigned char *achStripeR;
+ unsigned char *achStripeG;
+ unsigned char *achStripeB;
+} TCalibration;
+
+typedef struct {
+ int x;
+ int y;
+ int cx;
+ int cy;
+ int res; /* like all parameters in 1/1200 inch */
+ int nBrightness; /* -255 ... 255 */
+ int nContrast; /* -128 ... 127 */
+} TScanParam;
+
+typedef enum { fast, high, best } TQuality;
+typedef enum { color, gray, line, halftone } TMode;
+
+#define INST_ASSERT() { if (this->nErrorState) return this->nErrorState; }
+
+#define CHECK_ASSERTION(a) if (!(a)) return SetError(this,SANE_STATUS_INVAL,"assertion failed in %s %d",__FILE__,__LINE__)
+
+#define CHECK_POINTER(p) \
+if (!(p)) return SetError(this,SANE_STATUS_NO_MEM,"memory failed in %s %d",__FILE__,__LINE__)
+
+#define dprintf debug_printf
+
+typedef struct TInstance *PTInstance;
+typedef TState (*TReadLineCB)(PTInstance);
+
+typedef struct TScanState {
+ TBool bEOF; /* EOF marker for sane_read */
+ TBool bCanceled;
+ TBool bScanning; /* block is active? */
+ TBool bLastBulk; /* EOF announced */
+ int iReadPos; /* read() interface */
+ int iBulkReadPos; /* bulk read pos */
+ int iLine; /* log no. line */
+ int cchBulk; /* available bytes in bulk buffer */
+ int cchLineOut; /* buffer size */
+ int cxPixel,cyPixel; /* real pixel */
+ int cxMax; /* uninterpolated in real pixels */
+ int cxWindow; /* Window with in 600 DPI */
+ int cyWindow; /* Path length in 600 DPI */
+ int cyTotalPath; /* from bed start to window end in 600 dpi */
+ int nFixAspect; /* aspect ratio in percent, 75-100 */
+ int cBacklog; /* depth of ppchLines */
+ int ySensorSkew; /* distance in pixel between sensors */
+ char *szOrder; /* 123 or 231 or whatever */
+ unsigned char *pchBuf; /* bulk transfer buffer */
+ short **ppchLines; /* for error diffusion and color corr. */
+ unsigned char *pchLineOut; /* read() interface */
+ TReadLineCB ReadProc; /* line getter callback */
+} TScanState;
+
+
+#ifndef INSANE_VERSION
+
+#ifdef SM3600_SUPPORT_EXPOSURE
+#define NUM_OPTIONS 18
+#else
+#define NUM_OPTIONS 16
+#endif
+
+
+typedef struct TDevice {
+ struct TDevice *pNext;
+ struct usb_device *pdev;
+ TModel model;
+ SANE_Device sane;
+ char *szSaneName;
+} TDevice;
+
+#endif
+
+typedef struct TInstance {
+#ifndef INSANE_VERSION
+ struct TInstance *pNext;
+ SANE_Option_Descriptor aoptDesc[NUM_OPTIONS];
+ Option_Value aoptVal[NUM_OPTIONS];
+#endif
+ SANE_Int agammaY[4096];
+ SANE_Int agammaR[4096];
+ SANE_Int agammaG[4096];
+ SANE_Int agammaB[4096];
+ TScanState state;
+ TCalibration calibration;
+ TState nErrorState;
+ char *szErrorReason;
+ TBool bSANE;
+ TScanParam param;
+ TBool bWriteRaw;
+ TBool bVerbose;
+ TBool bOptSkipOriginate;
+ TQuality quality;
+ TMode mode;
+ TModel model;
+ int hScanner;
+ FILE *fhLog;
+ FILE *fhScan;
+ int ichPageBuffer; /* write position in full page buffer */
+ int cchPageBuffer; /* total size of '' */
+ unsigned char *pchPageBuffer; /* the humble buffer */
+} TInstance;
+
+#define TRUE 1
+#define FALSE 0
+
+/* ====================================================================== */
+
+#define ERR_FAILED -1
+#define OK 0
+
+#define NUM_SCANREGS 74
+
+/* ====================================================================== */
+
+/* note: The first register has address 0x01 */
+
+#define R_ALL 0x01
+
+/* have to become an enumeration */
+
+typedef enum { none, hpos, hposH, hres } TRegIndex;
+
+/* WORD */
+#define R_SPOS 0x01
+#define R_XRES 0x03
+/* WORD */
+#define R_SWID 0x04
+/* WORD */
+#define R_STPS 0x06
+/* WORD */
+#define R_YRES 0x08
+/* WORD */
+#define R_SLEN 0x0A
+/* WORD*/
+#define R_INIT 0x12
+#define RVAL_INIT 0x1540
+/* RGB */
+#define R_CCAL 0x2F
+
+/* WORD */
+#define R_CSTAT 0x42
+#define R_CTL 0x46
+/* WORD */
+#define R_POS 0x52
+/* WORD */
+#define R_LMP 0x44
+#define R_QLTY 0x4A
+#define R_STAT 0x54
+
+#define LEN_MAGIC 0x24EA
+
+/* ====================================================================== */
+#define USB_CHUNK_SIZE 0x8000
+
+/* sm3600-scanutil.c */
+__SM3600EXPORT__ int SetError(TInstance *this, int nError, const char *szFormat, ...);
+__SM3600EXPORT__ void debug_printf(unsigned long ulType, const char *szFormat, ...);
+__SM3600EXPORT__ TState FreeState(TInstance *this, TState nReturn);
+__SM3600EXPORT__ TState EndScan(TInstance *this);
+__SM3600EXPORT__ TState ReadChunk(TInstance *this, unsigned char *achOut,
+ int cchMax, int *pcchRead);
+#ifdef INSANE_VERSION
+__SM3600EXPORT__ void DumpBuffer(FILE *fh, const char *pch, int cch);
+__SM3600EXPORT__ TState DoScanFile(TInstance *this);
+#endif
+
+__SM3600EXPORT__ void GetAreaSize(TInstance *this);
+__SM3600EXPORT__ void ResetCalibration(TInstance *this);
+
+__SM3600EXPORT__ TState InitGammaTables(TInstance *this,
+ int nBrightness,
+ int nContrast);
+__SM3600EXPORT__ TState CancelScan(TInstance *this);
+
+/* sm3600-scanmtek.c */
+extern unsigned short aidProduct[];
+__SM3600EXPORT__ TState DoInit(TInstance *this);
+__SM3600EXPORT__ TState DoReset(TInstance *this);
+__SM3600EXPORT__ TState WaitWhileBusy(TInstance *this,int cSecs);
+__SM3600EXPORT__ TState WaitWhileScanning(TInstance *this,int cSecs);
+__SM3600EXPORT__ TModel GetScannerModel(unsigned short idVendor, unsigned short idProduct);
+
+#ifdef INSANE_VERSION
+__SM3600EXPORT__ TState DoLampSwitch(TInstance *this,int nPattern);
+#endif
+__SM3600EXPORT__ TState DoCalibration(TInstance *this);
+__SM3600EXPORT__ TState UploadGammaTable(TInstance *this, int iByteAddress, SANE_Int *pnGamma);
+__SM3600EXPORT__ TState UploadGainCorrection(TInstance *this, int iTableOffset);
+
+/* sm3600-scanusb.c */
+__SM3600EXPORT__ TState RegWrite(TInstance *this,int iRegister, int cb, unsigned long ulValue);
+__SM3600EXPORT__ TState RegWriteArray(TInstance *this,int iRegister, int cb, unsigned char *pchBuffer);
+#ifdef INSANE_VERSIONx
+__SM3600EXPORT__ TState RegCheck(TInstance *this,int iRegister, int cch, unsigned long ulValue);
+__SM3600EXPORT__ int BulkRead(TInstance *this,FILE *fhOut, unsigned int cchBulk);
+__SM3600EXPORT__ TState MemReadArray(TInstance *this, int iAddress, int cb, unsigned char *pchBuffer);
+#endif
+__SM3600EXPORT__ int BulkReadBuffer(TInstance *this,unsigned char *puchBufferOut, unsigned int cchBulk); /* gives count */
+__SM3600EXPORT__ unsigned int RegRead(TInstance *this,int iRegister, int cch);
+__SM3600EXPORT__ TState MemWriteArray(TInstance *this, int iAddress, int cb, unsigned char *pchBuffer);
+
+/* sm3600-gray.c */
+__SM3600EXPORT__ TState StartScanGray(TInstance *this);
+/* sm3600-color.c */
+__SM3600EXPORT__ TState StartScanColor(TInstance *this);
+
+/* sm3600-homerun.c */
+#ifdef INSANE_VERSION
+__SM3600EXPORT__ TState FakeCalibration(TInstance *this);
+#endif
+
+__SM3600EXPORT__ TState DoOriginate(TInstance *this, TBool bStepOut);
+__SM3600EXPORT__ TState DoJog(TInstance *this,int nDistance);
+
+/* ====================================================================== */
+
+#endif
diff --git a/backend/sm3840.c b/backend/sm3840.c
new file mode 100755
index 0000000..41b72ec
--- /dev/null
+++ b/backend/sm3840.c
@@ -0,0 +1,846 @@
+/* sane - Scanner Access Now Easy.
+
+ ScanMaker 3840 Backend
+ Copyright (C) 2005-7 Earle F. Philhower, III
+ earle@ziplabel.com - http://www.ziplabel.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+
+
+
+#include "../include/sane/config.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKENDNAME sm3840
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_config.h"
+
+#include "sm3840.h"
+
+#include "sm3840_scan.c"
+#include "sm3840_lib.c"
+
+static double sm3840_unit_convert (SANE_Int val);
+
+static int num_devices;
+static SM3840_Device *first_dev;
+static SM3840_Scan *first_handle;
+static const SANE_Device **devlist = 0;
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ 0
+};
+
+static const SANE_Word resolution_list[] = {
+ 4, 1200, 600, 300, 150
+};
+
+static const SANE_Word bpp_list[] = {
+ 2, 8, 16
+};
+
+static const SANE_Range x_range = {
+ SANE_FIX (0),
+ SANE_FIX (215.91), /* 8.5 inches */
+ SANE_FIX (0)
+};
+
+static const SANE_Range y_range = {
+ SANE_FIX (0),
+ SANE_FIX (297.19), /* 11.7 inches */
+ SANE_FIX (0)
+};
+
+static const SANE_Range brightness_range = {
+ 1,
+ 4096,
+ 1.0
+};
+
+static const SANE_Range contrast_range = {
+ SANE_FIX (0.1),
+ SANE_FIX (9.9),
+ SANE_FIX (0.1)
+};
+
+static const SANE_Range lamp_range = {
+ 1,
+ 15,
+ 1
+};
+
+static const SANE_Range threshold_range = {
+ 0,
+ 255,
+ 1
+};
+
+/*--------------------------------------------------------------------------*/
+static int
+min (int a, int b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SM3840_Scan *s = handle;
+ unsigned char c, d;
+ int i;
+
+ DBG (2, "+sane-read:%p %p %d %p\n", (unsigned char *) s, buf, max_len,
+ (unsigned char *) len);
+ DBG (2,
+ "+sane-read:remain:%lu offset:%lu linesleft:%d linebuff:%p linesread:%d\n",
+ (u_long)s->remaining, (u_long)s->offset, s->linesleft, s->line_buffer, s->linesread);
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (!s->remaining)
+ {
+ if (!s->linesleft)
+ {
+ *len = 0;
+ s->scanning = 0;
+ /* Move to home position */
+ reset_scanner ((p_usb_dev_handle)s->udev);
+ /* Send lamp timeout */
+ set_lamp_timer ((p_usb_dev_handle)s->udev, s->sm3840_params.lamp);
+
+ /* Free memory */
+ if (s->save_scan_line)
+ free (s->save_scan_line);
+ s->save_scan_line = NULL;
+ if (s->save_dpi1200_remap)
+ free (s->save_dpi1200_remap);
+ s->save_dpi1200_remap = NULL;
+ if (s->save_color_remap)
+ free (s->save_color_remap);
+ s->save_color_remap = NULL;
+
+ return SANE_STATUS_EOF;
+ }
+
+ record_line ((s->linesread == 0) ? 1 : 0,
+ (p_usb_dev_handle) s->udev,
+ s->line_buffer,
+ s->sm3840_params.dpi,
+ s->sm3840_params.scanpix,
+ s->sm3840_params.gray,
+ (s->sm3840_params.bpp == 16) ? 1 : 0,
+ &s->save_i,
+ &s->save_scan_line,
+ &s->save_dpi1200_remap, &s->save_color_remap);
+ s->remaining = s->sm3840_params.linelen;
+ s->offset = 0;
+ s->linesread++;
+ s->linesleft--;
+ }
+
+ /* Need to software emulate 1-bpp modes, simple threshold and error */
+ /* diffusion dither implemented. */
+ if (s->sm3840_params.lineart || s->sm3840_params.halftone)
+ {
+ d = 0;
+ for (i = 0; i < min (max_len * 8, s->remaining); i++)
+ {
+ d = d << 1;
+ if (s->sm3840_params.halftone)
+ {
+ c = (*(unsigned char *) (s->offset + s->line_buffer + i));
+ if (c + s->save_dither_err < 128)
+ {
+ d |= 1;
+ s->save_dither_err += c;
+ }
+ else
+ {
+ s->save_dither_err += c - 255;
+ }
+ }
+ else
+ {
+ if ((*(unsigned char *) (s->offset + s->line_buffer + i)) < s->threshold )
+ d |= 1;
+ }
+ if (i % 8 == 7)
+ *(buf++) = d;
+ }
+ *len = i / 8;
+ s->offset += i;
+ s->remaining -= i;
+ }
+ else
+ {
+ memcpy (buf, s->offset + s->line_buffer, min (max_len, s->remaining));
+ *len = min (max_len, s->remaining);
+ s->offset += min (max_len, s->remaining);
+ s->remaining -= min (max_len, s->remaining);
+ }
+
+ DBG (2, "-sane_read\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*--------------------------------------------------------------------------*/
+void
+sane_cancel (SANE_Handle h)
+{
+ SM3840_Scan *s = h;
+
+ DBG (2, "trying to cancel...\n");
+ if (s->scanning)
+ {
+ if (!s->cancelled)
+ {
+ /* Move to home position */
+ reset_scanner ((p_usb_dev_handle) s->udev);
+ /* Send lamp timeout */
+ set_lamp_timer ((p_usb_dev_handle) s->udev, s->sm3840_params.lamp);
+
+ /* Free memory */
+ if (s->save_scan_line)
+ free (s->save_scan_line);
+ s->save_scan_line = NULL;
+ if (s->save_dpi1200_remap)
+ free (s->save_dpi1200_remap);
+ s->save_dpi1200_remap = NULL;
+ if (s->save_color_remap)
+ free (s->save_color_remap);
+ s->save_color_remap = NULL;
+
+ s->scanning = 0;
+ s->cancelled = SANE_TRUE;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ SM3840_Scan *s = handle;
+ SANE_Status status;
+
+ /* First make sure we have a current parameter set. Some of the
+ * parameters will be overwritten below, but that's OK. */
+ DBG (2, "sane_start\n");
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ DBG (1, "Got params again...\n");
+
+ s->scanning = SANE_TRUE;
+ s->cancelled = 0;
+
+ s->line_buffer = malloc (s->sm3840_params.linelen);
+ s->remaining = 0;
+ s->offset = 0;
+ s->linesleft = s->sm3840_params.scanlines;
+ s->linesread = 0;
+
+ s->save_i = 0;
+ s->save_scan_line = NULL;
+ s->save_dpi1200_remap = NULL;
+ s->save_color_remap = NULL;
+
+ s->save_dither_err = 0;
+ s->threshold = s->sm3840_params.threshold;
+
+ setup_scan ((p_usb_dev_handle) s->udev, &(s->sm3840_params));
+
+ return (SANE_STATUS_GOOD);
+}
+
+static double
+sm3840_unit_convert (SANE_Int val)
+{
+ double d;
+ d = SANE_UNFIX (val);
+ d /= MM_PER_INCH;
+ return d;
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SM3840_Scan *s = handle;
+
+ DBG (2, "sane_get_parameters\n");
+ if (!s->scanning)
+ {
+ memset (&s->sane_params, 0, sizeof (s->sane_params));
+ /* Copy from options to sm3840_params */
+ s->sm3840_params.gray =
+ (!strcasecmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) ? 1 : 0;
+ s->sm3840_params.halftone =
+ (!strcasecmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_HALFTONE)) ? 1 : 0;
+ s->sm3840_params.lineart =
+ (!strcasecmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) ? 1 : 0;
+
+ s->sm3840_params.dpi = s->value[OPT_RESOLUTION].w;
+ s->sm3840_params.bpp = s->value[OPT_BIT_DEPTH].w;
+ s->sm3840_params.gain = SANE_UNFIX (s->value[OPT_CONTRAST].w);
+ s->sm3840_params.offset = s->value[OPT_BRIGHTNESS].w;
+ s->sm3840_params.lamp = s->value[OPT_LAMP_TIMEOUT].w;
+ s->sm3840_params.threshold = s->value[OPT_THRESHOLD].w;
+
+ if (s->sm3840_params.lineart || s->sm3840_params.halftone)
+ {
+ s->sm3840_params.gray = 1;
+ s->sm3840_params.bpp = 8;
+ }
+
+ s->sm3840_params.top = sm3840_unit_convert (s->value[OPT_TL_Y].w);
+ s->sm3840_params.left = sm3840_unit_convert (s->value[OPT_TL_X].w);
+ s->sm3840_params.width =
+ sm3840_unit_convert (s->value[OPT_BR_X].w) - s->sm3840_params.left;
+ s->sm3840_params.height =
+ sm3840_unit_convert (s->value[OPT_BR_Y].w) - s->sm3840_params.top;
+
+ /* Legalize and calculate pixel sizes */
+ prepare_params (&(s->sm3840_params));
+
+ /* Copy into sane_params */
+ s->sane_params.pixels_per_line = s->sm3840_params.scanpix;
+ s->sane_params.lines = s->sm3840_params.scanlines;
+ s->sane_params.format =
+ s->sm3840_params.gray ? SANE_FRAME_GRAY : SANE_FRAME_RGB;
+ s->sane_params.bytes_per_line = s->sm3840_params.linelen;
+ s->sane_params.depth = s->sm3840_params.bpp;
+
+ if (s->sm3840_params.lineart || s->sm3840_params.halftone)
+ {
+ s->sane_params.bytes_per_line += 7;
+ s->sane_params.bytes_per_line /= 8;
+ s->sane_params.depth = 1;
+ s->sane_params.pixels_per_line = s->sane_params.bytes_per_line * 8;
+ }
+
+ s->sane_params.last_frame = SANE_TRUE;
+ } /*!scanning */
+
+ if (params)
+ *params = s->sane_params;
+
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ SM3840_Scan *s = handle;
+ SANE_Status status = 0;
+ SANE_Word cap;
+ DBG (2, "sane_control_option\n");
+ if (info)
+ *info = 0;
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+ cap = s->options_list[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (1, "sane_control_option %d, get value\n", option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ case OPT_LAMP_TIMEOUT:
+ case OPT_THRESHOLD:
+ *(SANE_Word *) val = s->value[option].w;
+ return (SANE_STATUS_GOOD);
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->value[option].s);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (1, "sane_control_option %d, set value\n", option);
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ status = sanei_constrain_value (s->options_list + option, val, info);
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ case OPT_LAMP_TIMEOUT:
+ case OPT_THRESHOLD:
+ s->value[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+ case OPT_MODE:
+ if (s->value[option].s)
+ free (s->value[option].s);
+ s->value[option].s = strdup (val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ return (SANE_STATUS_INVAL);
+}
+
+/*--------------------------------------------------------------------------*/
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ SM3840_Scan *s = handle;
+ DBG (2, "sane_get_option_descriptor\n");
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+ return (&s->options_list[option]);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void
+sane_close (SANE_Handle handle)
+{
+ SM3840_Scan *prev, *s;
+
+ DBG (2, "sane_close\n");
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ {
+ sane_cancel (handle);
+ }
+
+ sanei_usb_close (s->udev);
+
+ if (s->line_buffer)
+ free (s->line_buffer);
+ if (s->save_scan_line)
+ free (s->save_scan_line);
+ if (s->save_dpi1200_remap)
+ free (s->save_dpi1200_remap);
+ if (s->save_color_remap)
+ free (s->save_color_remap);
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s;
+ free (handle);
+}
+
+/*--------------------------------------------------------------------------*/
+void
+sane_exit (void)
+{
+ SM3840_Device *next;
+ DBG (2, "sane_exit\n");
+ while (first_dev != NULL)
+ {
+ next = first_dev->next;
+ free (first_dev);
+ first_dev = next;
+ }
+ if (devlist)
+ free (devlist);
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+ if (authorize)
+ DBG (2, "Unused authorize\n");
+
+ sanei_usb_init ();
+
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+
+
+static SANE_Status
+add_sm_device (SANE_String_Const devname, SANE_String_Const modname)
+{
+ SM3840_Device *dev;
+
+ dev = calloc (sizeof (*dev), 1);
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+
+ memset (dev, 0, sizeof (*dev));
+ dev->sane.name = strdup (devname);
+ dev->sane.model = modname;
+ dev->sane.vendor = "Microtek";
+ dev->sane.type = "flatbed scanner";
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+add_sm3840_device (SANE_String_Const devname)
+{
+ return add_sm_device (devname, "ScanMaker 3840");
+}
+
+static SANE_Status
+add_sm4800_device (SANE_String_Const devname)
+{
+ return add_sm_device (devname, "ScanMaker 4800");
+}
+
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ SM3840_Device *dev;
+ int i;
+
+ DBG (3, "sane_get_devices (local_only = %d)\n", local_only);
+
+ while (first_dev)
+ {
+ dev = first_dev->next;
+ free (first_dev);
+ first_dev = dev;
+ }
+ first_dev = NULL;
+ num_devices = 0;
+
+ /* If we get enough scanners should use an array, but for now */
+ /* do it one-by-one... */
+ sanei_usb_find_devices (0x05da, 0x30d4, add_sm3840_device);
+ sanei_usb_find_devices (0x05da, 0x30cf, add_sm4800_device);
+
+ if (devlist)
+ free (devlist);
+ devlist = calloc ((num_devices + 1) * sizeof (devlist[0]), 1);
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+ if (device_list)
+ *device_list = devlist;
+ return (SANE_STATUS_GOOD);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return (max_size);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void
+initialize_options_list (SM3840_Scan * s)
+{
+
+ SANE_Int option;
+ DBG (2, "initialize_options_list\n");
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->options_list[option].size = sizeof (SANE_Word);
+ s->options_list[option].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->options_list[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->options_list[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_NUM_OPTS].size = sizeof (SANE_Word);
+ s->options_list[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->options_list[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->value[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ s->options_list[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->options_list[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->options_list[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->options_list[OPT_MODE].type = SANE_TYPE_STRING;
+ s->options_list[OPT_MODE].size = max_string_size (mode_list);
+ s->options_list[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->options_list[OPT_MODE].constraint.string_list = mode_list;
+ s->value[OPT_MODE].s = strdup (mode_list[1]);
+
+ s->options_list[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->options_list[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->options_list[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->options_list[OPT_RESOLUTION].constraint.word_list = resolution_list;
+ s->value[OPT_RESOLUTION].w = 300;
+
+ s->options_list[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->options_list[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->options_list[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->options_list[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->options_list[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->options_list[OPT_BIT_DEPTH].constraint.word_list = bpp_list;
+ s->value[OPT_BIT_DEPTH].w = 8;
+
+ s->options_list[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->options_list[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->options_list[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->options_list[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->options_list[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_TL_X].constraint.range = &x_range;
+ s->value[OPT_TL_X].w = s->options_list[OPT_TL_X].constraint.range->min;
+ s->options_list[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->options_list[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_TL_Y].constraint.range = &y_range;
+ s->value[OPT_TL_Y].w = s->options_list[OPT_TL_Y].constraint.range->min;
+ s->options_list[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->options_list[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->options_list[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->options_list[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->options_list[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BR_X].constraint.range = &x_range;
+ s->value[OPT_BR_X].w = s->options_list[OPT_BR_X].constraint.range->max;
+ s->options_list[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->options_list[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BR_Y].constraint.range = &y_range;
+ s->value[OPT_BR_Y].w = s->options_list[OPT_BR_Y].constraint.range->max;
+
+ s->options_list[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->options_list[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->options_list[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->options_list[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_CONTRAST].constraint.range = &contrast_range;
+ s->value[OPT_CONTRAST].w = SANE_FIX (3.5);
+
+ s->options_list[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->options_list[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ s->value[OPT_BRIGHTNESS].w = 1800;
+
+ s->options_list[OPT_LAMP_TIMEOUT].name = "lamp-timeout";
+ s->options_list[OPT_LAMP_TIMEOUT].title = SANE_I18N ("Lamp timeout");
+ s->options_list[OPT_LAMP_TIMEOUT].desc =
+ SANE_I18N ("Minutes until lamp is turned off after scan");
+ s->options_list[OPT_LAMP_TIMEOUT].type = SANE_TYPE_INT;
+ s->options_list[OPT_LAMP_TIMEOUT].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_LAMP_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_LAMP_TIMEOUT].constraint.range = &lamp_range;
+ s->value[OPT_LAMP_TIMEOUT].w = 15;
+
+ s->options_list[OPT_THRESHOLD].name = "threshold";
+ s->options_list[OPT_THRESHOLD].title = SANE_I18N ("Threshold");
+ s->options_list[OPT_THRESHOLD].desc =
+ SANE_I18N ("Threshold value for lineart mode");
+ s->options_list[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->options_list[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_THRESHOLD].constraint.range = &threshold_range;
+ s->value[OPT_THRESHOLD].w = 128;
+
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status;
+ SM3840_Device *dev;
+ SM3840_Scan *s;
+ DBG (2, "sane_open\n");
+
+ /* Make sure we have first_dev */
+ sane_get_devices (NULL, 0);
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+ }
+ else
+ {
+ /* empty devicename -> use first device */
+ dev = first_dev;
+ }
+ DBG (2, "using device: %s %p\n", dev->sane.name, (unsigned char *) dev);
+ if (!dev)
+ return SANE_STATUS_INVAL;
+ s = calloc (sizeof (*s), 1);
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+
+ status = sanei_usb_open (dev->sane.name, &(s->udev));
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ initialize_options_list (s);
+ s->scanning = 0;
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ SM3840_Scan *s = handle;
+ DBG (2, "sane_set_io_mode( %p, %d )\n", handle, non_blocking);
+ if (s->scanning)
+ {
+ if (non_blocking == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+ else
+ return (SANE_STATUS_UNSUPPORTED);
+ }
+ else
+ return SANE_STATUS_INVAL;
+}
+
+/*---------------------------------------------------------------------------*/
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd( %p, %p )\n", (void *) handle, (void *) fd);
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/sm3840.conf.in b/backend/sm3840.conf.in
new file mode 100644
index 0000000..177abd5
--- /dev/null
+++ b/backend/sm3840.conf.in
@@ -0,0 +1,5 @@
+# usb vendor product
+# Microtek ScanMaker 3840 ID
+usb 0x05da 0x30d4
+# Microtek ScanMaker 4800 ID
+usb 0x05da 0x30cf
diff --git a/backend/sm3840.h b/backend/sm3840.h
new file mode 100755
index 0000000..abcd1b7
--- /dev/null
+++ b/backend/sm3840.h
@@ -0,0 +1,125 @@
+/* sane - Scanner Access Now Easy.
+
+ ScanMaker 3840 Backend
+ Copyright (C) 2005-7 Earle F. Philhower, III
+ earle@ziplabel.com - http://www.ziplabel.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+#ifndef sm3840_h
+#define sm3840_h
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+
+
+typedef enum SM3840_Option
+{
+ OPT_NUM_OPTS = 0,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_BIT_DEPTH,
+
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+
+ OPT_LAMP_TIMEOUT,
+ OPT_THRESHOLD,
+
+ /* must come last */
+ NUM_OPTIONS
+} SM3840_Option;
+
+#include "sm3840_params.h"
+
+
+typedef struct SM3840_Device
+{
+ struct SM3840_Device *next;
+ SANE_Device sane;
+} SM3840_Device;
+
+
+
+typedef struct SM3840_Scan
+{
+ struct SM3840_Scan *next;
+ SANE_Option_Descriptor options_list[NUM_OPTIONS];
+ Option_Value value[NUM_OPTIONS];
+
+ SANE_Int udev;
+
+ SANE_Bool scanning;
+ SANE_Bool cancelled;
+ SANE_Parameters sane_params;
+ SM3840_Params sm3840_params;
+
+ SANE_Byte *line_buffer; /* One remapped/etc line */
+ size_t remaining; /* How much of line_buffer is still good? */
+ size_t offset; /* Offset in line_buffer where unread data lives */
+ int linesleft; /* How many lines to read from scanner? */
+ int linesread; /* Total lines returned to SANE */
+
+ /* record_line state parameters */
+ int save_i;
+ unsigned char *save_scan_line;
+ unsigned char *save_dpi1200_remap;
+ unsigned char *save_color_remap;
+ unsigned char threshold;
+ int save_dither_err;
+
+} SM3840_Scan;
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define SM3840_CONFIG_FILE "sm3840.conf"
+
+
+#define SCAN_BUF_SIZE 65536
+
+#endif /* sm3840_h */
diff --git a/backend/sm3840_lib.c b/backend/sm3840_lib.c
new file mode 100755
index 0000000..485003c
--- /dev/null
+++ b/backend/sm3840_lib.c
@@ -0,0 +1,1004 @@
+/* sane - Scanner Access Now Easy.
+
+ ScanMaker 3840 Backend
+ Copyright (C) 2005-7 Earle F. Philhower, III
+ earle@ziplabel.com - http://www.ziplabel.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <math.h>
+#include "sm3840_lib.h"
+
+#ifndef BACKENDNAME
+/* For standalone compilation */
+static void
+DBG (int ignored, const char *fmt, ...)
+{
+ va_list a;
+ va_start (a, fmt);
+ vfprintf (stderr, fmt, a);
+ va_end (a);
+}
+#else
+/* For SANE compilation, convert libusb to sanei_usb */
+static int
+my_usb_bulk_write (p_usb_dev_handle dev, int ep,
+ unsigned char *bytes, int size, int timeout)
+{
+ SANE_Status status;
+ size_t my_size;
+
+ timeout = timeout;
+ ep = ep;
+ my_size = size;
+ status =
+ sanei_usb_write_bulk ((SANE_Int) dev, (SANE_Byte *) bytes, &my_size);
+ if (status != SANE_STATUS_GOOD)
+ my_size = -1;
+ return my_size;
+}
+
+static int
+my_usb_bulk_read (p_usb_dev_handle dev, int ep,
+ unsigned char *bytes, int size, int timeout)
+{
+ SANE_Status status;
+ size_t my_size;
+
+ timeout = timeout;
+ ep = ep;
+ my_size = size;
+ status =
+ sanei_usb_read_bulk ((SANE_Int) dev, (SANE_Byte *) bytes, &my_size);
+ if (status != SANE_STATUS_GOOD)
+ my_size = -1;
+ return my_size;
+}
+
+static int
+my_usb_control_msg (p_usb_dev_handle dev, int requesttype,
+ int request, int value, int index,
+ unsigned char *bytes, int size, int timeout)
+{
+ SANE_Status status;
+
+ timeout = timeout;
+ status = sanei_usb_control_msg ((SANE_Int) dev, (SANE_Int) requesttype,
+ (SANE_Int) request, (SANE_Int) value,
+ (SANE_Int) index, (SANE_Int) size,
+ (SANE_Byte *) bytes);
+ return status;
+}
+#endif
+
+
+/* Sanitize and convert scan parameters from inches to pixels */
+static void
+prepare_params (SM3840_Params * params)
+{
+ if (params->gray)
+ params->gray = 1;
+ if (params->lineart) {
+ params->gray = 1;
+ params->lineart = 1;
+ }
+ if (params->halftone) {
+ params->gray = 1;
+ params->halftone = 1;
+ }
+ if (params->dpi != 1200 && params->dpi != 600 && params->dpi != 300
+ && params->dpi != 150)
+ params->dpi = 150;
+ if (params->bpp != 8 && params->bpp != 16)
+ params->bpp = 8;
+
+ /* Sanity check input sizes */
+ if (params->top < 0)
+ params->top = 0;
+ if (params->left < 0)
+ params->left = 0;
+ if (params->width < 0)
+ params->width = 0;
+ if (params->height < 0)
+ params->height = 0;
+ if ((params->top + params->height) > 11.7)
+ params->height = 11.7 - params->top;
+ if ((params->left + params->width) > 8.5)
+ params->width = 8.5 - params->left;
+
+ params->topline = params->top * params->dpi;
+ params->scanlines = params->height * params->dpi;
+ params->leftpix = params->left * params->dpi;
+ params->leftpix &= ~1; /* Always start on even pixel boundary for remap */
+ /* Scanpix always a multiple of 128 */
+ params->scanpix = params->width * params->dpi;
+ params->scanpix = (params->scanpix + 127) & ~127;
+
+ /* Sanity check outputs... */
+ if (params->topline < 0)
+ params->topline = 0;
+ if (params->scanlines < 1)
+ params->scanlines = 1;
+ if (params->leftpix < 0)
+ params->leftpix = 0;
+ if (params->scanpix < 128)
+ params->scanpix = 128;
+
+ /* Some handy calculations */
+ params->linelen =
+ params->scanpix * (params->bpp / 8) * (params->gray ? 1 : 3);
+}
+
+#ifndef BACKENDNAME
+usb_dev_handle *
+find_device (unsigned int idVendor, unsigned int idProduct)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_init ();
+ usb_find_busses ();
+ usb_find_devices ();
+
+ for (bus = usb_get_busses (); bus; bus = bus->next)
+ for (dev = bus->devices; dev; dev = dev->next)
+ if (dev->descriptor.idVendor == idVendor &&
+ dev->descriptor.idProduct == idProduct)
+ return usb_open (dev);
+
+ return NULL;
+}
+#endif
+
+static void
+idle_ab (p_usb_dev_handle udev)
+{
+ int len, i;
+ unsigned char buff[8] = { 0x64, 0x65, 0x16, 0x17, 0x64, 0x65, 0x44, 0x45 };
+ for (i = 0; i < 8; i++)
+ {
+ len = usb_control_msg (udev, 0x40, 0x0c, 0x0090, 0x0000, buff + i,
+ 0x0001, wr_timeout);
+ }
+}
+
+/* CW: 40 04 00b0 0000 <len> : <reg1> <value1> <reg2> <value2> ... */
+static void
+write_regs (p_usb_dev_handle udev, int regs, unsigned char reg1,
+ unsigned char val1,
+ ... /*unsigned char reg, unsigned char val, ... */ )
+{
+ unsigned char buff[512];
+ va_list marker;
+ int len, i;
+
+ va_start (marker, val1);
+ buff[0] = reg1;
+ buff[1] = val1;
+ for (i = 1; i < regs; i++)
+ {
+ buff[i * 2] = va_arg (marker, int);
+ buff[i * 2 + 1] = va_arg (marker, int);
+ }
+ va_end (marker);
+
+ len = usb_control_msg (udev, 0x40, 0x04, 0x00b0, 0x0000, buff,
+ regs * 2, wr_timeout);
+}
+
+static int
+write_vctl (p_usb_dev_handle udev, int request, int value,
+ int index, unsigned char byte)
+{
+ return usb_control_msg (udev, 0x40, request, value, index,
+ &byte, 1, wr_timeout);
+}
+
+static int
+read_vctl (p_usb_dev_handle udev, int request, int value,
+ int index, unsigned char *byte)
+{
+ return usb_control_msg (udev, 0xc0, request, value, index,
+ byte, 1, wr_timeout);
+}
+
+#ifndef BACKENDNAME
+/* Copy from a USB data pipe to a file with optional header */
+static void
+record_head (p_usb_dev_handle udev, char *fname, int bytes, char *header)
+{
+ FILE *fp;
+ unsigned char buff[65536];
+ int len;
+ int toread;
+
+ fp = fopen (fname, "wb");
+ if (header)
+ fprintf (fp, "%s", header);
+ do
+ {
+ toread = (bytes > 65536) ? 65536 : bytes;
+ len = usb_bulk_read (udev, 1, buff, toread, rd_timeout);
+ if (len > 0)
+ {
+ fwrite (buff, len, 1, fp);
+ bytes -= len;
+ }
+ else
+ {
+ DBG (2, "TIMEOUT\n");
+ }
+ }
+ while (bytes);
+ fclose (fp);
+}
+
+/* Copy from a USB data pipe to a file without header */
+static void
+record (p_usb_dev_handle udev, char *fname, int bytes)
+{
+ record_head (udev, fname, bytes, "");
+}
+#endif
+
+static int
+max (int a, int b)
+{
+ return (a > b) ? a : b;
+}
+
+
+#define DPI1200SHUFFLE 6
+static void
+record_line (int reset,
+ p_usb_dev_handle udev,
+ unsigned char *storeline,
+ int dpi, int scanpix, int gray, int bpp16,
+ int *save_i,
+ unsigned char **save_scan_line,
+ unsigned char **save_dpi1200_remap,
+ unsigned char **save_color_remap)
+{
+ int len;
+ unsigned char *scan_line, *dpi1200_remap;
+ unsigned char *color_remap;
+ int i;
+ int red_delay, blue_delay, green_delay;
+ int j;
+ int linelen;
+ int pixsize;
+ unsigned char *ptrcur, *ptrprev;
+ unsigned char *savestoreline;
+ int lineptr, outline, bufflines;
+
+ i = *save_i;
+ scan_line = *save_scan_line;
+ dpi1200_remap = *save_dpi1200_remap;
+ color_remap = *save_color_remap;
+
+ pixsize = (gray ? 1 : 3) * (bpp16 ? 2 : 1);
+ linelen = scanpix * pixsize;
+
+ if (gray)
+ {
+ red_delay = 0;
+ blue_delay = 0;
+ green_delay = 0;
+ }
+ else if (dpi == 150)
+ {
+ red_delay = 0;
+ blue_delay = 6;
+ green_delay = 3;
+ }
+ else if (dpi == 300)
+ {
+ red_delay = 0;
+ blue_delay = 12;
+ green_delay = 6;
+ }
+ else if (dpi == 600)
+ {
+ red_delay = 0;
+ blue_delay = 24;
+ green_delay = 12;
+ }
+ else /*(dpi==1200) */
+ {
+ red_delay = 0;
+ blue_delay = 48;
+ green_delay = 24;
+ }
+
+ bufflines = max (max (red_delay, blue_delay), green_delay) + 1;
+
+ if (reset)
+ {
+ if (dpi1200_remap)
+ free (dpi1200_remap);
+ if (scan_line)
+ free (scan_line);
+ if (color_remap)
+ free (color_remap);
+
+ *save_color_remap = color_remap =
+ (unsigned char *) malloc (bufflines * linelen);
+
+ *save_scan_line = scan_line = (unsigned char *) calloc (linelen, 1);
+ if (dpi == 1200)
+ *save_dpi1200_remap = dpi1200_remap =
+ (unsigned char *) calloc (linelen, DPI1200SHUFFLE);
+ else
+ *save_dpi1200_remap = dpi1200_remap = NULL;
+
+ *save_i = i = 0; /* i is our linenumber */
+ }
+
+ while (1)
+ { /* We'll exit inside the loop... */
+ len = usb_bulk_read (udev, 1, scan_line, linelen, rd_timeout);
+ if (dpi == 1200)
+ {
+ ptrcur = dpi1200_remap + (linelen * (i % DPI1200SHUFFLE));
+ ptrprev =
+ dpi1200_remap +
+ (linelen * ((i - (DPI1200SHUFFLE - 2)) % DPI1200SHUFFLE));
+ }
+ else
+ {
+ ptrcur = scan_line;
+ ptrprev = NULL;
+ }
+ if (gray)
+ {
+ memcpy (ptrcur, scan_line, linelen);
+ }
+ else
+ {
+ int pixsize = bpp16 ? 2 : 1;
+ int pix = linelen / (3 * pixsize);
+ unsigned char *outbuff = ptrcur;
+
+ outline = i;
+ lineptr = i % bufflines;
+
+ memcpy (color_remap + linelen * lineptr, scan_line, linelen);
+
+ outline++;
+ if (outline > bufflines)
+ {
+ int redline = (outline + red_delay) % bufflines;
+ int greenline = (outline + green_delay) % bufflines;
+ int blueline = (outline + blue_delay) % bufflines;
+ unsigned char *rp, *gp, *bp;
+
+ rp = color_remap + linelen * redline;
+ gp = color_remap + linelen * greenline + 1 * pixsize;
+ bp = color_remap + linelen * blueline + 2 * pixsize;
+
+ for (j = 0; j < pix; j++)
+ {
+ if (outbuff)
+ {
+ *(outbuff++) = *rp;
+ if (pixsize == 2)
+ *(outbuff++) = *(rp + 1);
+ *(outbuff++) = *gp;
+ if (pixsize == 2)
+ *(outbuff++) = *(gp + 1);
+ *(outbuff++) = *bp;
+ if (pixsize == 2)
+ *(outbuff++) = *(bp + 1);
+ }
+ rp += 3 * pixsize;
+ gp += 3 * pixsize;
+ bp += 3 * pixsize;
+ }
+ }
+ lineptr = (lineptr + 1) % bufflines;
+ }
+
+ if (dpi != 1200)
+ {
+ if (i > blue_delay)
+ {
+ savestoreline = storeline;
+ for (j = 0; j < scanpix; j++)
+ {
+ memcpy (storeline, ptrcur + linelen - (j + 1) * pixsize,
+ pixsize);
+ storeline += pixsize;
+ }
+ if (dpi == 150)
+ {
+ /* 150 DPI skips every 4th returned line */
+ if (i % 4)
+ {
+ i++;
+ *save_i = i;
+ if (bpp16)
+ fix_endian_short ((unsigned short *) storeline,
+ scanpix);
+ return;
+ }
+ storeline = savestoreline;
+ }
+ else
+ {
+ i++;
+ *save_i = i;
+ if (bpp16)
+ fix_endian_short ((unsigned short *) storeline, scanpix);
+ return;
+ }
+ }
+ }
+ else /* dpi==1200 */
+ {
+ if (i > (DPI1200SHUFFLE + blue_delay))
+ {
+ for (j = 0; j < scanpix; j++)
+ {
+ if (1 == (j & 1))
+ memcpy (storeline, ptrcur + linelen - (j + 1) * pixsize,
+ pixsize);
+ else
+ memcpy (storeline, ptrprev + linelen - (j + 1) * pixsize,
+ pixsize);
+ storeline += pixsize;
+ } /* end for */
+ i++;
+ *save_i = i;
+ if (bpp16)
+ fix_endian_short ((unsigned short *) storeline, scanpix);
+ return;
+ } /* end if >dpi1200shuffle */
+ } /* end if dpi1200 */
+ i++;
+ } /* end for */
+}
+
+
+#ifndef BACKENDNAME
+/* Record an image to a file with remapping/reordering/etc. */
+void
+record_image (p_usb_dev_handle udev, char *fname, int dpi,
+ int scanpix, int scanlines, int gray, char *head, int bpp16)
+{
+ FILE *fp;
+ int i;
+ int save_i;
+ unsigned char *save_scan_line;
+ unsigned char *save_dpi1200_remap;
+ unsigned char *save_color_remap;
+ unsigned char *storeline;
+
+ save_i = 0;
+ save_scan_line = save_dpi1200_remap = save_color_remap = NULL;
+ storeline =
+ (unsigned char *) malloc (scanpix * (gray ? 1 : 3) * (bpp16 ? 2 : 1));
+
+ fp = fopen (fname, "wb");
+ if (head)
+ fprintf (fp, "%s", head);
+
+ for (i = 0; i < scanlines; i++)
+ {
+ record_line ((i == 0) ? 1 : 0, udev, storeline, dpi, scanpix, gray,
+ bpp16, &save_i, &save_scan_line, &save_dpi1200_remap,
+ &save_color_remap);
+ fwrite (storeline, scanpix * (gray ? 1 : 3) * (bpp16 ? 2 : 1), 1, fp);
+ }
+ fclose (fp);
+ if (save_scan_line)
+ free (save_scan_line);
+ if (save_dpi1200_remap)
+ free (save_dpi1200_remap);
+ if (save_color_remap)
+ free (save_color_remap);
+ free (storeline);
+}
+#endif
+
+static void
+record_mem (p_usb_dev_handle udev, unsigned char **dest, int bytes)
+{
+ unsigned char *mem;
+ unsigned char buff[65536];
+ int len;
+
+ mem = (unsigned char *) malloc (bytes);
+ *dest = mem;
+ do
+ {
+ len =
+ usb_bulk_read (udev, 1, buff, bytes > 65536 ? 65536 : bytes,
+ rd_timeout);
+ if (len > 0)
+ {
+ memcpy (mem, buff, len);
+ bytes -= len;
+ mem += len;
+ }
+ }
+ while (bytes);
+}
+
+
+static void
+reset_scanner (p_usb_dev_handle udev)
+{
+ unsigned char rd_byte;
+
+ DBG (2, "+reset_scanner\n");
+ write_regs (udev, 5, 0x83, 0x00, 0xa3, 0xff, 0xa4, 0xff, 0xa1, 0xff,
+ 0xa2, 0xff);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+ write_regs (udev, 2, 0xbe, 0x00, 0x84, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00,
+ 0x88, 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00,
+ 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc5, 0x00,
+ 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc0, 0x00);
+ write_regs (udev, 16, 0x84, 0x94, 0x80, 0xd1, 0x80, 0xc1, 0x82, 0x7f,
+ 0xcf, 0x04, 0xc1, 0x02, 0xc2, 0x00, 0xc3, 0x06,
+ 0xc4, 0xff, 0xc5, 0x40, 0xc6, 0x8c, 0xc7, 0xdc,
+ 0xc8, 0x20, 0xc0, 0x72, 0x89, 0xff, 0x86, 0xff);
+ write_regs (udev, 7, 0xa8, 0x80, 0x83, 0xa2, 0x85, 0xc8, 0x83, 0x82,
+ 0x85, 0xaf, 0x83, 0x00, 0x93, 0x00);
+ write_regs (udev, 3, 0xa8, 0x0a, 0x8c, 0x08, 0x94, 0x00);
+ write_regs (udev, 4, 0x83, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0x97, 0x0a);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0b);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0f);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x05);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ DBG (2, "-reset_scanner\n");
+}
+
+static void
+poll1 (p_usb_dev_handle udev)
+{
+ unsigned char rd_byte;
+ DBG (2, "+poll1\n");
+ do
+ {
+ write_regs (udev, 1, 0x97, 0x00);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ }
+ while (0 == (rd_byte & 0x40));
+ DBG (2, "-poll1\n");
+}
+
+static void
+poll2 (p_usb_dev_handle udev)
+{
+ unsigned char rd_byte;
+ DBG (2, "+poll2\n");
+ do
+ {
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ }
+ while (0 == (rd_byte & 0x02));
+ DBG (2, "-poll2\n");
+}
+
+#ifndef BACKENDNAME
+static void
+check_buttons (p_usb_dev_handle udev, int *scan, int *print, int *mail)
+{
+ unsigned char rd_byte;
+
+ write_regs (udev, 4, 0x83, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0x97, 0x0a);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ if (scan)
+ {
+ if (0 == (rd_byte & 1))
+ *scan = 1;
+ else
+ *scan = 0;
+ }
+ if (print)
+ {
+ if (0 == (rd_byte & 2))
+ *print = 1;
+ else
+ *print = 0;
+ }
+ if (mail)
+ {
+ if (0 == (rd_byte & 4))
+ *mail = 1;
+ else
+ *mail = 0;
+ }
+ write_regs (udev, 1, 0x97, 0x0b);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ idle_ab (udev);
+ write_regs (udev, 1, 0x97, 0x0f);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ idle_ab (udev);
+ write_regs (udev, 1, 0x97, 0x05);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ idle_ab (udev);
+}
+#endif
+
+static int
+lut (int val, double gain, int offset)
+{
+ /* int offset = 1800; */
+ /* double exponent = 3.5; */
+ return (offset + 8192.0 * (pow ((8192.0 - val) / 8192.0, gain)));
+}
+
+
+static void
+calc_lightmap (unsigned short *buff,
+ unsigned short *storage, int index, int dpi,
+ double gain, int offset)
+{
+ int val, line;
+ int px = 5632;
+ int i;
+
+ line = px * 3;
+
+ for (i = 0; i < px; i++)
+ {
+ if ((i >= 2) && (i <= (px - 2)))
+ {
+ val = 0;
+ val += 1 * buff[i * 3 + index - 3 * 2];
+ val += 3 * buff[i * 3 + index - 3 * 1];
+ val += 5 * buff[i * 3 + index + 3 * 0];
+ val += 3 * buff[i * 3 + index + 3 * 1];
+ val += 1 * buff[i * 3 + index + 3 * 2];
+ val += 2 * buff[i * 3 + index - 3 * 1 + line];
+ val += 3 * buff[i * 3 + index + 3 * 0 + line];
+ val += 2 * buff[i * 3 + index + 3 * 1 + line];
+ val += 1 * buff[i * 3 + index + 3 * 0 + 2 * line];
+ val /= 21;
+ }
+ else
+ {
+ val = 1 * buff[i * 3 + index];
+ }
+
+ val = val >> 3;
+ if (val > 8191)
+ val = 8191;
+ val = lut (val, gain, offset);
+
+ if (val > 8191)
+ val = 8191;
+ if (val < 0)
+ val = 0;
+ storage[i * (dpi == 1200 ? 2 : 1)] = val;
+ if (dpi == 1200)
+ storage[i * 2 + 1] = val;
+ }
+
+ fix_endian_short (storage, (dpi == 1200) ? i * 2 : i);
+}
+
+
+
+
+/*#define VALMAP 0x1fff */
+/*#define SCANMAP 0x2000*/
+#define RAWPIXELS600 7320
+#define RAWPIXELS1200 14640
+
+static void
+select_pixels (unsigned short *map, int dpi, int start, int end)
+{
+ int i;
+ int skip, offset;
+ unsigned short VALMAP = 0x1fff;
+ unsigned short SCANMAP = 0x2000;
+
+ fix_endian_short (&VALMAP, 1);
+ fix_endian_short (&SCANMAP, 1);
+
+ /* Clear the pixel-on flags for all bits */
+ for (i = 0; i < ((dpi == 1200) ? RAWPIXELS1200 : RAWPIXELS600); i++)
+ map[i] &= VALMAP;
+
+ /* 300 and 150 have subsampling */
+ if (dpi == 300)
+ skip = -2;
+ else if (dpi == 150)
+ skip = -4;
+ else
+ skip = -1;
+
+ /* 1200 has 2x pixels so start at 2x the offset */
+ if (dpi == 1200)
+ offset = 234 + (8.5 * 1200);
+ else
+ offset = 117 + (8.5 * 600);
+
+ if (0 == (offset & 1))
+ offset++;
+
+ DBG (2, "offset=%d start=%d skip=%d\n", offset, start, skip);
+
+ for (i = 0; i < end; i++)
+ {
+ int x;
+ x = offset + (start * skip) + (i * skip);
+ if (x < 0 || x > ((dpi == 1200) ? RAWPIXELS1200 : RAWPIXELS600))
+ DBG (2, "ERR %d\n", x);
+ else
+ map[x] |= SCANMAP;
+ }
+}
+
+
+static void
+set_lightmap_white (unsigned short *map, int dpi, int color)
+{
+ int i;
+ unsigned short VALMAP = 0x1fff;
+ unsigned short SCANMAP = 0x2000;
+
+ fix_endian_short (&VALMAP, 1);
+ fix_endian_short (&SCANMAP, 1);
+
+ if (dpi != 1200)
+ {
+ memset (map, 0, RAWPIXELS600 * 2);
+ if (color != 0)
+ return; /* Only 1st has enables... */
+ for (i = 0; i < 22; i++)
+ map[7 + i] = SCANMAP; /* Get some black... */
+ for (i = 0; i < 1024; i++)
+ map[2048 + i] = SCANMAP; /* And some white... */
+ }
+ else
+ {
+ memset (map, 0, RAWPIXELS1200 * 2);
+ if (color != 0)
+ return;
+ for (i = 16; i < 61; i++)
+ map[i] = SCANMAP;
+ for (i = 4076; i < 6145; i++)
+ map[i] = SCANMAP;
+ /* 3rd is all clear */
+ }
+}
+
+
+static void
+set_lamp_timer (p_usb_dev_handle udev, int timeout_in_mins)
+{
+ write_regs (udev, 7, 0xa8, 0x80, 0x83, 0xa2, 0x85, 0xc8, 0x83, 0x82,
+ 0x85, 0xaf, 0x83, 0x00, 0x93, 0x00);
+ write_regs (udev, 3, 0xa8, timeout_in_mins * 2, 0x8c, 0x08, 0x94, 0x00);
+ idle_ab (udev);
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0x26, 0x83, 0x00, 0x8d, 0xff);
+}
+
+
+static void
+calculate_lut8 (double *poly, int skip, unsigned char *dest)
+{
+ int i;
+ double sum, x;
+ if (!poly || !dest)
+ return;
+ for (i = 0; i < 8192; i += skip)
+ {
+ sum = poly[0];
+ x = (double) i;
+ sum += poly[1] * x;
+ x = x * i;
+ sum += poly[2] * x;
+ x = x * i;
+ sum += poly[3] * x;
+ x = x * i;
+ sum += poly[4] * x;
+ x = x * i;
+ sum += poly[5] * x;
+ x = x * i;
+ sum += poly[6] * x;
+ x = x * i;
+ sum += poly[7] * x;
+ x = x * i;
+ sum += poly[8] * x;
+ x = x * i;
+ sum += poly[9] * x;
+ x = x * i;
+ if (sum < 0)
+ sum = 0;
+ if (sum > 255)
+ sum = 255;
+ *dest = (unsigned char) sum;
+ dest++;
+ }
+}
+
+static void
+download_lut8 (p_usb_dev_handle udev, int dpi, int incolor)
+{
+ double color[10] = { 0.0, 1.84615261590892E-01, -2.19613868292657E-04,
+ 1.79549523214101E-07, -8.69378513113239E-11,
+ 2.56694911984996E-14, -4.67288886157239E-18,
+ 5.11622894146250E-22, -3.08729724411991E-26,
+ 7.88581670873938E-31
+ };
+ double gray[10] = { 0.0, 1.73089945056694E-01, -1.39794924306080E-04,
+ 9.70266873814926E-08, -4.57503008236968E-11,
+ 1.37092321631794E-14, -2.54395068387198E-18,
+ 2.82432084125966E-22, -1.71787408822688E-26,
+ 4.40306800664567E-31
+ };
+ unsigned char *lut;
+ int len;
+
+ DBG (2, "+download_lut8\n");
+ switch (dpi)
+ {
+ case 150:
+ case 300:
+ case 600:
+ lut = (unsigned char *) malloc (4096);
+ if (!lut)
+ return;
+
+ if (!incolor)
+ {
+ calculate_lut8 (gray, 2, lut);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x20, 0xb2, 0x07, 0xb3, 0xff,
+ 0xb4, 0x2f, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, 0x1000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 4096, wr_timeout);
+ }
+ else
+ {
+ calculate_lut8 (color, 2, lut);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x10, 0xb2, 0x07, 0xb3, 0xff,
+ 0xb4, 0x1f, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, 0x1000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 4096, wr_timeout);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x20, 0xb2, 0x07, 0xb3, 0xff,
+ 0xb4, 0x2f, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, 0x1000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 4096, wr_timeout);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x30, 0xb2, 0x07, 0xb3, 0xff,
+ 0xb4, 0x3f, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, 0x1000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 4096, wr_timeout);
+ }
+ break;
+
+ case 1200:
+ default:
+ lut = (unsigned char *) malloc (8192);
+ if (!lut)
+ return;
+
+ if (!incolor)
+ {
+ calculate_lut8 (gray, 1, lut);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x40, 0xb2, 0x06, 0xb3, 0xff,
+ 0xb4, 0x5f, 0xb5, 0x06);
+ write_vctl (udev, 0x0c, 0x0002, 0x2000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 8192, wr_timeout);
+ }
+ else
+ {
+ calculate_lut8 (color, 1, lut);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x20, 0xb2, 0x06, 0xb3, 0xff,
+ 0xb4, 0x3f, 0xb5, 0x06);
+ write_vctl (udev, 0x0c, 0x0002, 0x2000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 8192, wr_timeout);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x40, 0xb2, 0x06, 0xb3, 0xff,
+ 0xb4, 0x5f, 0xb5, 0x06);
+ write_vctl (udev, 0x0c, 0x0002, 0x2000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 8192, wr_timeout);
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x60, 0xb2, 0x06, 0xb3, 0xff,
+ 0xb4, 0x7f, 0xb5, 0x06);
+ write_vctl (udev, 0x0c, 0x0002, 0x2000, 0x00);
+ len = usb_bulk_write (udev, 2, lut, 8192, wr_timeout);
+ }
+ break;
+ }
+
+ free (lut);
+ DBG (2, "-download_lut8\n");
+}
+
+
+static void
+set_gain_black (p_usb_dev_handle udev,
+ int r_gain, int g_gain, int b_gain,
+ int r_black, int g_black, int b_black)
+{
+ write_regs (udev, 1, 0x80, 0x00);
+ write_regs (udev, 1, 0x80, 0x01);
+ write_regs (udev, 1, 0x04, 0x80);
+ write_regs (udev, 1, 0x04, 0x00);
+ write_regs (udev, 1, 0x00, 0x00);
+ write_regs (udev, 1, 0x01, 0x03);
+ write_regs (udev, 1, 0x02, 0x04);
+ write_regs (udev, 1, 0x03, 0x11);
+ write_regs (udev, 1, 0x05, 0x00);
+ write_regs (udev, 1, 0x28, r_gain);
+ write_regs (udev, 1, 0x29, g_gain);
+ write_regs (udev, 1, 0x2a, b_gain);
+ write_regs (udev, 1, 0x20, r_black);
+ write_regs (udev, 1, 0x21, g_black);
+ write_regs (udev, 1, 0x22, b_black);
+ write_regs (udev, 1, 0x24, 0x00);
+ write_regs (udev, 1, 0x25, 0x00);
+ write_regs (udev, 1, 0x26, 0x00);
+}
+
+/* Handle short endianness issues */
+static void
+fix_endian_short (unsigned short *data, int count)
+{
+ unsigned short testvalue = 255;
+ unsigned char *firstbyte = (unsigned char *) &testvalue;
+ int i;
+
+ if (*firstbyte == 255)
+ return; /* INTC endianness */
+
+ DBG (2, "swapping endiannes...\n");
+ for (i = 0; i < count; i++)
+ data[i] = ((data[i] >> 8) & 0x00ff) | ((data[i] << 8) & 0xff00);
+}
diff --git a/backend/sm3840_lib.h b/backend/sm3840_lib.h
new file mode 100755
index 0000000..2ca65a8
--- /dev/null
+++ b/backend/sm3840_lib.h
@@ -0,0 +1,152 @@
+/* sane - Scanner Access Now Easy.
+
+ ScanMaker 3840 Backend
+ Copyright (C) 2005-7 Earle F. Philhower, III
+ earle@ziplabel.com - http://www.ziplabel.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifndef BACKENDNAME
+#include <usb.h>
+#include <string.h>
+
+#ifndef DEFINED_USB_HANDLE
+#define DEFINED_USB_HANDLE
+typedef usb_dev_handle *p_usb_dev_handle;
+#endif
+
+static p_usb_dev_handle find_device (unsigned int idVendor,
+ unsigned int idProduct);
+#else
+#include "../include/sane/sanei_usb.h"
+
+#ifndef USBWRAPPER
+#define USBWRAPPER
+
+typedef SANE_Int p_usb_dev_handle;
+#define usb_control_msg my_usb_control_msg
+#define usb_bulk_read my_usb_bulk_read
+#define usb_bulk_write my_usb_bulk_write
+static int my_usb_bulk_write (p_usb_dev_handle dev, int ep,
+ unsigned char *bytes,
+ int size, int timeout);
+static int my_usb_bulk_read (p_usb_dev_handle dev, int ep,
+ unsigned char *bytes,
+ int size, int timeout);
+static int my_usb_control_msg (p_usb_dev_handle dev, int requesttype,
+ int request, int value, int index,
+ unsigned char *bytes,
+ int size, int timeout);
+#endif /* USBWRAPPER */
+
+#endif
+
+#include "sm3840_params.h"
+
+static void idle_ab (p_usb_dev_handle udev);
+static void write_regs (p_usb_dev_handle udev, int regs, unsigned char reg1,
+ unsigned char val1,
+ ... /*unsigned char reg, unsigned char val, ... */ );
+static int write_vctl (p_usb_dev_handle udev, int request, int value,
+ int index, unsigned char byte);
+static int read_vctl (p_usb_dev_handle udev, int request, int value,
+ int index, unsigned char *byte);
+
+#ifndef BACKENDNAME
+static void record (p_usb_dev_handle udev, char *fname, int bytes);
+static void record_image (p_usb_dev_handle udev, char *fname, int dpi,
+ int scanpix, int scanlines, int gray, char *head,
+ int bpp16);
+static void check_buttons (p_usb_dev_handle udev, int *scan, int *print,
+ int *mail);
+static void record_head (p_usb_dev_handle udev, char *fname, int bytes,
+ char *header);
+#endif
+
+static void poll1 (p_usb_dev_handle udev);
+static void poll2 (p_usb_dev_handle udev);
+
+static void reset_scanner (p_usb_dev_handle udev);
+
+
+static void set_lightmap_white (unsigned short *map, int dpi, int color);
+
+static void calc_lightmap (unsigned short *buff,
+ unsigned short *storage, int index, int dpi,
+ double gain, int offset);
+static void select_pixels (unsigned short *map, int dpi, int start, int end);
+
+static void record_mem (p_usb_dev_handle udev, unsigned char **dest,
+ int bytes);
+static void set_lamp_timer (p_usb_dev_handle udev, int timeout_in_mins);
+
+static void set_gain_black (p_usb_dev_handle udev,
+ int r_gain, int g_gain, int b_gain,
+ int r_black, int g_black, int b_black);
+
+static void idle_ab (p_usb_dev_handle udev);
+static void write_regs (p_usb_dev_handle udev, int regs, unsigned char reg1,
+ unsigned char val1,
+ ... /*unsigned char reg, unsigned char val, ... */ );
+static int write_vctl (p_usb_dev_handle udev, int request, int value,
+ int index, unsigned char byte);
+static int read_vctl (p_usb_dev_handle udev, int request, int value,
+ int index, unsigned char *byte);
+
+static void download_lut8 (p_usb_dev_handle udev, int dpi, int incolor);
+
+static void record_line (int reset,
+ p_usb_dev_handle udev,
+ unsigned char *storeline,
+ int dpi, int scanpix, int gray, int bpp16,
+ int *save_i,
+ unsigned char **save_scan_line,
+ unsigned char **save_dpi1200_remap,
+ unsigned char **save_color_remap);
+
+
+static void prepare_params (SM3840_Params * params);
+
+static void fix_endian_short (unsigned short *data, int count);
+
+#define rd_timeout 10000
+#define wr_timeout 10000
diff --git a/backend/sm3840_params.h b/backend/sm3840_params.h
new file mode 100644
index 0000000..78640a9
--- /dev/null
+++ b/backend/sm3840_params.h
@@ -0,0 +1,74 @@
+/* sane - Scanner Access Now Easy.
+
+ ScanMaker 3840 Backend
+ Copyright (C) 2005-7 Earle F. Philhower, III
+ earle@ziplabel.com - http://www.ziplabel.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+#ifndef _SM3840_Params
+#define _SM3840_Params
+typedef struct SM3840_Params
+{
+ int gray; /* 0, 1 */
+ int halftone; /* 0, 1 (also set gray=1) */
+ int lineart; /* 0, 1 (also set gray=1) */
+
+ int dpi; /* 150, 300, 600, 1200 */
+ int bpp; /* 8, 16 */
+
+ double gain; /* 0.01...9.9 */
+ int offset; /* 0..4095 */
+
+ int lamp; /* 1..15 mins */
+
+ int threshold; /* 0...255 luminosity */
+
+ /* Input to configure(), in inches */
+ double top, left;
+ double width, height;
+
+ /* Output from configure(), in pixels */
+ int topline;
+ int scanlines;
+ int leftpix;
+ int scanpix;
+ int linelen; /* bytes per line */
+} SM3840_Params;
+#endif
diff --git a/backend/sm3840_scan.c b/backend/sm3840_scan.c
new file mode 100755
index 0000000..e9442a7
--- /dev/null
+++ b/backend/sm3840_scan.c
@@ -0,0 +1,946 @@
+/* sane - Scanner Access Now Easy.
+
+ ScanMaker 3840 Backend
+ Copyright (C) 2005 Earle F. Philhower, III
+ earle@ziplabel.com - http://www.ziplabel.com
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "sm3840_lib.h"
+
+#ifndef BACKENDNAME
+static void setup_scan (p_usb_dev_handle udev, SM3840_Params * p,
+ char *stname, int raw, int nohead);
+#else
+static void setup_scan (p_usb_dev_handle udev, SM3840_Params * p);
+#endif
+
+
+#ifndef BACKENDNAME
+
+#include "sm3840_lib.c"
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+
+ int gray = 0;
+ int dpi = 1200;
+ int bpp16 = 0;
+ int raw = 0;
+ int nohead = 0;
+ double gain = 3.5;
+ int offset = 1800;
+ usb_dev_handle *udev;
+
+ char *stname;
+ double topin, botin, leftin, rightin;
+ int topline, scanlines, leftpix, scanpix;
+
+ stname = NULL;
+ topin = 0.0;
+ botin = 11.7;
+ leftin = 0.0;
+ rightin = 8.5;
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "-300"))
+ dpi = 300;
+ else if (!strcmp (argv[i], "-600"))
+ dpi = 600;
+ else if (!strcmp (argv[i], "-1200"))
+ dpi = 1200;
+ else if (!strcmp (argv[i], "-150"))
+ dpi = 150;
+ else if (!strcmp (argv[i], "-top"))
+ topin = atof (argv[++i]);
+ else if (!strcmp (argv[i], "-bot"))
+ botin = atof (argv[++i]);
+ else if (!strcmp (argv[i], "-left"))
+ leftin = atof (argv[++i]);
+ else if (!strcmp (argv[i], "-right"))
+ rightin = atof (argv[++i]);
+ else if (!strcmp (argv[i], "-gain"))
+ gain = atof (argv[++i]);
+ else if (!strcmp (argv[i], "-offset"))
+ offset = atoi (argv[++i]);
+ else if (!strcmp (argv[i], "-gray"))
+ gray = 1;
+ else if (!strcmp (argv[i], "-16bpp"))
+ bpp16 = 1;
+ else if (!strcmp (argv[i], "-raw"))
+ raw = 1;
+ else if (!strcmp (argv[i], "-nohead"))
+ nohead = 1;
+ else
+ stname = argv[i];
+ }
+
+ SM3840_Params params;
+ params.gray = gray;
+ params.dpi = dpi;
+ params.bpp = bpp16 ? 16 : 8;
+ params.gain = gain;
+ params.offset = offset;
+ params.lamp = 15;
+ params.top = topin;
+ params.left = leftin;
+ params.height = botin - topin;
+ params.width = rightin - leftin;
+
+ prepare_params (&params);
+ udev = find_device (0x05da, 0x30d4); /* 3840 */
+ if (!udev)
+ udev = find_device (0x05da, 0x30cf); /* 4800 */
+ if (!udev)
+ fprintf (stderr, "Unable to open scanner.\n");
+ else
+ setup_scan (udev, &params, stname, raw, nohead);
+
+ return 0;
+}
+#endif
+
+#ifndef BACKENDNAME
+static void
+setup_scan (p_usb_dev_handle udev, SM3840_Params * p,
+ char *stname, int raw, int nohead)
+#else
+static void
+setup_scan (p_usb_dev_handle udev, SM3840_Params * p)
+#endif
+{
+ int gray = p->gray ? 1 : 0;
+ int dpi = p->dpi;
+ int bpp16 = (p->bpp == 16) ? 1 : 0;
+ double gain = p->gain;
+ int offset = p->offset;
+ int topline = p->topline;
+ int scanlines = p->scanlines;
+ int leftpix = p->leftpix;
+ int scanpix = p->scanpix;
+ int len;
+ unsigned char hello[2] = { 0x55, 0xaa };
+ unsigned char howdy[3];
+ unsigned short *whitebalance;
+ int whitebalancesize = (dpi == 1200) ? 12672 : 6528;
+ unsigned short *whitemap;
+ int whitemapsize = (dpi == 1200) ? 29282 : 14642;
+ unsigned short *whitescan;
+ unsigned short *lightmap;
+ unsigned int topreg, botreg;
+ int redreg, greenreg, bluereg, donered, donegreen, doneblue;
+ int rgreg = 0x00;
+ int ggreg = 0x00;
+ int bgreg = 0x00;
+ int i, j;
+ int red, green, blue;
+ unsigned char rd_byte;
+ unsigned short GRAYMASK = 0xc000;
+
+
+#ifndef BACKENDNAME
+ char fname[64];
+ char head[128];
+
+ len = usb_set_configuration (udev, 1);
+ len = usb_claim_interface (udev, 0);
+ len = usb_clear_halt (udev, 1);
+ len = usb_clear_halt (udev, 2);
+ len = usb_clear_halt (udev, 3);
+#endif
+ DBG (2, "params.gray = %d;\n", p->gray);
+ DBG (2, "params.dpi = %d\n", p->dpi);
+ DBG (2, "params.bpp = %d\n", p->bpp);
+ DBG (2, "params.gain = %f\n", p->gain);
+ DBG (2, "params.offset = %d\n", p->offset);
+ DBG (2, "params.lamp = %d\n", p->lamp);
+ DBG (2, "params.top = %f\n", p->top);
+ DBG (2, "params.left = %f\n", p->left);
+ DBG (2, "params.height = %f\n", p->height);
+ DBG (2, "params.width = %f\n", p->width);
+
+ DBG (2, "params.topline = %d\n", p->topline);
+ DBG (2, "params.scanlines = %d\n", p->scanlines);
+ DBG (2, "params.leftpix = %d\n", p->leftpix);
+ DBG (2, "params.scanpix = %d\n", p->scanpix);
+
+ DBG (2, "params.linelen = %d\n", p->linelen);
+
+ reset_scanner (udev);
+
+ idle_ab (udev);
+ write_regs (udev, 4, 0x83, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0x97, 0x0a);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0b);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0f);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x05);
+ write_vctl (udev, 0x0b, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 7, 0xa8, 0x80, 0x83, 0xa2, 0x85, 0x01, 0x83, 0x82, 0x85,
+ 0x00, 0x83, 0x00, 0x93, 0x00);
+ write_regs (udev, 1, 0xa8, 0x80);
+ write_regs (udev, 4, 0x83, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0x97, 0x0a);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0b);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0f);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x05);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0xfe, 0x83, 0x00, 0x8d, 0xff);
+ write_regs (udev, 1, 0x97, 0x00);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 8, 0x80, 0x00, 0x84, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0x86,
+ 0x00, 0x89, 0x00, 0x94, 0x00, 0x01, 0x02);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ write_regs (udev, 1, 0x94, 0x51);
+ write_regs (udev, 1, 0xb0, 0x00);
+ write_regs (udev, 1, 0xb1, 0x00);
+ write_regs (udev, 1, 0xb2, 0x00);
+ write_regs (udev, 1, 0xb3, 0x00);
+ write_regs (udev, 1, 0xb4, 0x10);
+ write_regs (udev, 1, 0xb5, 0x1f);
+ write_regs (udev, 1, 0xb0, 0x00);
+ write_regs (udev, 1, 0xb1, 0x00);
+ write_regs (udev, 1, 0xb2, 0x00);
+ write_vctl (udev, 0x0c, 0x0002, 0x0002, 0x00);
+ len = usb_bulk_write (udev, 2, hello, 2, wr_timeout);
+ write_regs (udev, 1, 0xb0, 0x00);
+ write_regs (udev, 1, 0xb1, 0x00);
+ write_regs (udev, 1, 0xb2, 0x00);
+ write_vctl (udev, 0x0c, 0x0003, 0x0003, 0x00);
+ len = usb_bulk_read (udev, 1, howdy, 3, rd_timeout);
+ write_regs (udev, 4, 0x83, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0x97, 0x0a);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0b);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0f);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x05);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0xfe, 0x83, 0x00, 0x8d, 0xff);
+ write_regs (udev, 1, 0x97, 0x00);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 8, 0x80, 0x00, 0x84, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0x86,
+ 0x00, 0x89, 0x00, 0x94, 0x00, 0x01, 0x02);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00, 0x88,
+ 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+ 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8,
+ 0x00, 0xc0, 0x00);
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0xff, 0x83, 0x00, 0x8d, 0xff);
+ write_regs (udev, 5, 0x83, 0x00, 0xa3, 0xff, 0xa4, 0xff, 0xa1, 0xff, 0xa2,
+ 0xf7);
+ write_regs (udev, 4, 0x83, 0x22, 0x87, 0x01, 0x83, 0x02, 0x87, 0x16);
+ write_regs (udev, 11, 0xa0, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0x9d, 0x00, 0x9e,
+ 0x00, 0xa0, 0x00, 0xce, 0x0c, 0x83, 0x20, 0xa5, 0x00, 0xa6,
+ 0x00, 0xa7, 0x00);
+
+ set_gain_black (udev, 0x01, 0x01, 0x01, 0xaa, 0xaa, 0xaa);
+
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x02, 0x98,
+ 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x03, 0x98, 0x00, 0x99,
+ 0x00, 0x9a, 0x00);
+ write_regs (udev, 4, 0x83, 0xa2, 0x85, 0x98, 0x83, 0x82, 0x85, 0x3a);
+ write_regs (udev, 1, 0x9d, 0x80);
+ write_regs (udev, 1, 0x9d, 0x00);
+
+ if (dpi == 1200)
+ write_regs (udev, 1, 0x94, 0x51);
+ else
+ write_regs (udev, 1, 0x94, 0x61);
+
+ whitemap = (unsigned short *) malloc (whitemapsize);
+
+ set_lightmap_white (whitemap, dpi, 0);
+ if (dpi == 1200)
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x80, 0xb2, 0x06, 0xb3, 0xff, 0xb4,
+ 0xff, 0xb5, 0x06);
+ else
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x40, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0x7f, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, whitemapsize, 0x00);
+ len =
+ usb_bulk_write (udev, 2, (unsigned char *) whitemap, whitemapsize,
+ wr_timeout);
+
+ set_lightmap_white (whitemap, dpi, 1);
+ if (dpi == 1200)
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0x7f, 0xb5, 0x07);
+ else
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x80, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0xbf, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, whitemapsize, 0x00);
+ len =
+ usb_bulk_write (udev, 2, (unsigned char *) whitemap, whitemapsize,
+ wr_timeout);
+
+ set_lightmap_white (whitemap, dpi, 2);
+ if (dpi == 1200)
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x80, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0xff, 0xb5, 0x07);
+ else
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0xc0, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0xff, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, whitemapsize, 0x00);
+ len =
+ usb_bulk_write (udev, 2, (unsigned char *) whitemap, whitemapsize,
+ wr_timeout);
+
+ free (whitemap);
+
+ /* Move to head... */
+ idle_ab (udev);
+ write_regs (udev, 1, 0x97, 0x00);
+ idle_ab (udev);
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00, 0x88,
+ 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+ 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8,
+ 0x00, 0xc0, 0x00);
+ idle_ab (udev);
+ write_regs (udev, 16, 0x84, 0x94, 0x80, 0xd1, 0x80, 0xc1, 0x82, 0x7f, 0xcf,
+ 0x04, 0xc1, 0x02, 0xc2, 0x00, 0xc3, 0x06, 0xc4, 0xff, 0xc5,
+ 0x40, 0xc6, 0x8c, 0xc7, 0xdc, 0xc8, 0x20, 0xc0, 0x72, 0x89,
+ 0xff, 0x86, 0xff);
+ poll1 (udev);
+
+ write_regs (udev, 4, 0x83, 0xa2, 0x85, 0x01, 0x83, 0x82, 0x85, 0x00);
+ write_regs (udev, 1, 0x9d, 0x80);
+ write_regs (udev, 1, 0x9d, 0x00);
+ if (dpi == 1200)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8, 0x77,
+ 0xb9, 0x1e);
+ else
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8, 0x3b,
+ 0xb9, 0x1f);
+ write_regs (udev, 5, 0xc0, 0x00, 0x84, 0x00, 0x80, 0xa1, 0xcf, 0x04, 0x82,
+ 0x00);
+ if (dpi == 1200)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x02, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x01, 0xbd, 0x01, 0x88, 0xa4, 0xc1, 0x02, 0xc2, 0x00,
+ 0xc3, 0x02, 0xc4, 0x01, 0xc5, 0x01, 0xc6, 0xa3, 0xc7, 0xa4,
+ 0xc8, 0x04, 0xc0, 0xd2, 0x89, 0x05, 0x86, 0x00);
+ else
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x04, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x01, 0xbd, 0x01, 0x88, 0xd0, 0xc1, 0x01, 0xc2, 0x00,
+ 0xc3, 0x04, 0xc4, 0x01, 0xc5, 0x01, 0xc6, 0xcf, 0xc7, 0xd0,
+ 0xc8, 0x14, 0xc0, 0xd1, 0x89, 0x0a, 0x86, 0x00);
+ write_regs (udev, 8, 0xbb, 0x01, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80, 0xbf,
+ 0x00, 0x90, 0x40, 0x91, 0x00, 0x83, 0x82);
+ write_regs (udev, 1, 0xbe, 0x0d);
+ write_vctl (udev, 0x0c, 0x0003, 0x0001, 0x00);
+ whitebalance = (unsigned short *) malloc (whitebalancesize);
+ len = usb_bulk_read (udev, 1, &rd_byte, 1, rd_timeout);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+ len =
+ usb_bulk_read (udev, 1, (unsigned char *) whitebalance, whitebalancesize,
+ rd_timeout);
+ write_regs (udev, 2, 0xbe, 0x00, 0x84, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ redreg = greenreg = bluereg = 0x80;
+ red = green = blue = 0;
+ donered = donegreen = doneblue = 0;
+ DBG (2, "setting blackpoint\n");
+ for (j = 0; (j < 16) && !(donered && donegreen && doneblue); j++)
+ {
+ set_gain_black (udev, 0x01, 0x01, 0x01, redreg, greenreg, bluereg);
+
+ if (dpi == 1200)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0x77, 0xb9, 0x1e);
+ else
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0x3b, 0xb9, 0x1f);
+ write_regs (udev, 8, 0xbb, 0x01, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x40, 0x91, 0x00, 0x83, 0x82);
+ write_regs (udev, 1, 0xbe, 0x0d);
+ write_vctl (udev, 0x0c, 0x0003, 0x0001, 0x00);
+ len = usb_bulk_read (udev, 1, &rd_byte, 1, rd_timeout);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+ len =
+ usb_bulk_read (udev, 1, (unsigned char *) whitebalance,
+ whitebalancesize, rd_timeout);
+ fix_endian_short (whitebalance, whitebalancesize/2);
+ if (!donered)
+ {
+ red = (whitebalance[0] + whitebalance[3] + whitebalance[6]) / 3;
+ if (red > 0x1000)
+ redreg += 0x10;
+ else if (red > 0x500)
+ redreg += 0x08;
+ else if (red > 0x0010)
+ redreg++;
+ else
+ donered = 1;
+ }
+ if (!donegreen)
+ {
+ green = (whitebalance[1] + whitebalance[4] + whitebalance[7]) / 3;
+ if (green > 0x1000)
+ greenreg += 0x10;
+ else if (green > 0x0500)
+ greenreg += 0x08;
+ else if (green > 0x0010)
+ greenreg++;
+ else
+ donegreen = 1;
+ }
+ if (!doneblue)
+ {
+ blue = (whitebalance[2] + whitebalance[5] + whitebalance[8]) / 3;
+ if (blue > 0x1000)
+ bluereg += 0x10;
+ else if (blue > 0x0500)
+ bluereg += 0x08;
+ else if (blue > 0x0010)
+ bluereg++;
+ else
+ doneblue = 1;
+ }
+ DBG (2, "red=%d(%d)%02x, green=%d(%d)%02x, blue=%d(%d)%02x\n",
+ red, donered, redreg, green, donegreen, greenreg,
+ blue, doneblue, bluereg);
+ write_regs (udev, 2, 0xbe, 0x00, 0x84, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ }
+ DBG (2, "setting whitepoint\n");
+ donegreen = donered = doneblue = 0;
+ for (j = 0; (j < 16) && !(donered && donegreen && doneblue); j++)
+ {
+ set_gain_black (udev, rgreg, ggreg, bgreg, redreg, greenreg, bluereg);
+
+ if (dpi == 1200)
+ idle_ab (udev);
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8, 0x3b,
+ 0xb9, 0x1f);
+ if (dpi == 1200)
+ idle_ab (udev);
+ write_regs (udev, 8, 0xbb, 0x01, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x40, 0x91, 0x00, 0x83, 0x82);
+ write_regs (udev, 1, 0xbe, 0x0d);
+ write_vctl (udev, 0x0c, 0x0003, 0x0001, 0x00);
+ len = usb_bulk_read (udev, 1, &rd_byte, 1, rd_timeout);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+ len =
+ usb_bulk_read (udev, 1, (unsigned char *) whitebalance,
+ whitebalancesize, rd_timeout);
+ fix_endian_short (whitebalance, whitebalancesize/2);
+ if (!donered)
+ {
+ red =
+ (whitebalance[180 * 3 + 0] + whitebalance[180 * 3 + 3] +
+ whitebalance[180 * 3 + 6]) / 3;
+ if (red < 0x5000)
+ rgreg += 0x02;
+ else if (red < 0x8000)
+ rgreg += 0x01;
+ else
+ donered = 1;
+ }
+ if (!donegreen)
+ {
+ green =
+ (whitebalance[180 * 3 + 1] + whitebalance[180 * 3 + 4] +
+ whitebalance[180 * 3 + 7]) / 3;
+ if (green < 0x5000)
+ ggreg += 0x02;
+ else if (green < 0x8000)
+ ggreg += 0x01;
+ else
+ donegreen = 1;
+ }
+ if (!doneblue)
+ {
+ blue =
+ (whitebalance[180 * 3 + 2] + whitebalance[180 * 3 + 5] +
+ whitebalance[180 * 3 + 8]) / 3;
+ if (blue < 0x5000)
+ bgreg += 0x02;
+ else if (blue < 0x8000)
+ bgreg += 0x01;
+ else
+ doneblue = 1;
+ }
+ DBG (2, "red=%d(%d)%02x, green=%d(%d)%02x, blue=%d(%d)%02x\n",
+ red, donered, rgreg, green, donegreen, ggreg, blue, doneblue,
+ bgreg);
+ write_regs (udev, 2, 0xbe, 0x00, 0x84, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ }
+ free (whitebalance);
+
+ /* One step down for optimal contrast... */
+ if (rgreg)
+ rgreg--;
+ if (bgreg)
+ bgreg--;
+ if (ggreg)
+ ggreg--;
+
+
+ write_regs (udev, 8, 0x80, 0x00, 0x84, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0x86,
+ 0x00, 0x89, 0x00, 0x94, 0x00, 0x01, 0x02);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00, 0x88,
+ 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+ 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8,
+ 0x00, 0xc0, 0x00);
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0xff, 0x83, 0x00, 0x8d, 0xff);
+ write_regs (udev, 5, 0x83, 0x00, 0xa3, 0xff, 0xa4, 0xff, 0xa1, 0xff, 0xa2,
+ 0xf7);
+ write_regs (udev, 4, 0x83, 0x22, 0x87, 0x01, 0x83, 0x02, 0x87, 0x16);
+ write_regs (udev, 11, 0xa0, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0x9d, 0x00, 0x9e,
+ 0x00, 0xa0, 0x00, 0xce, 0x0c, 0x83, 0x20, 0xa5, 0x00, 0xa6,
+ 0x00, 0xa7, 0x00);
+ set_gain_black (udev, rgreg, ggreg, bgreg, redreg, greenreg, bluereg);
+
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x02, 0x98,
+ 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x03, 0x98, 0x00, 0x99,
+ 0x00, 0x9a, 0x00);
+ write_regs (udev, 4, 0x83, 0xa2, 0x85, 0x98, 0x83, 0x82, 0x85, 0x3a);
+ write_regs (udev, 1, 0x9d, 0x80);
+ write_regs (udev, 1, 0x9d, 0x00);
+ if (dpi == 1200)
+ write_regs (udev, 1, 0x94, 0x71);
+ else
+ write_regs (udev, 1, 0x94, 0x61);
+
+ write_regs (udev, 4, 0x83, 0xa2, 0x85, 0x01, 0x83, 0x82, 0x85, 0x00);
+ write_regs (udev, 1, 0x9d, 0x80);
+ write_regs (udev, 1, 0x9d, 0x00);
+ if (dpi == 1200)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8, 0xbf,
+ 0xb9, 0x17);
+ else
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8, 0xdf,
+ 0xb9, 0x1b);
+ write_regs (udev, 6, 0xc0, 0x00, 0x84, 0x00, 0x84, 0xb4, 0x80, 0xe1, 0xcf,
+ 0x04, 0x82, 0x00);
+ if (dpi == 1200)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x02, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x08, 0x88, 0xa4, 0xc1, 0x02, 0xc2, 0x00,
+ 0xc3, 0x02, 0xc4, 0x20, 0xc5, 0x08, 0xc6, 0x96, 0xc7, 0xa4,
+ 0xc8, 0x06, 0xc0, 0xd2, 0x89, 0x24, 0x86, 0x01);
+ else
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x04, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x10, 0x88, 0xd0, 0xc1, 0x01, 0xc2, 0x00,
+ 0xc3, 0x04, 0xc4, 0x20, 0xc5, 0x10, 0xc6, 0xc3, 0xc7, 0xd0,
+ 0xc8, 0x1c, 0xc0, 0xd1, 0x89, 0x24, 0x86, 0x01);
+ write_regs (udev, 8, 0xbb, 0x05, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80, 0xbf,
+ 0x00, 0x90, 0x40, 0x91, 0x00, 0x83, 0x82);
+ write_regs (udev, 1, 0xbe, 0x1d);
+ write_vctl (udev, 0x0c, 0x0003, 0x0001, 0x00);
+ len = usb_bulk_read (udev, 1, &rd_byte, 1, rd_timeout);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+ record_mem (udev, (unsigned char **) (void *)&whitescan,
+ (5632 * 2 * 3 * (dpi == 1200 ? 2 : 1)) * 4);
+ fix_endian_short (whitescan, (5632 * 2 * 3 * (dpi == 1200 ? 2 : 1)) * 2);
+ write_regs (udev, 5, 0x83, 0x00, 0xa3, 0xff, 0xa4, 0xff, 0xa1, 0xff, 0xa2,
+ 0xff);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+ write_regs (udev, 2, 0xbe, 0x00, 0x84, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ write_regs (udev, 4, 0x83, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0x97, 0x0a);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0b);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x0f);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x05);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0xff, 0x83, 0x00, 0x8d, 0xff);
+ write_regs (udev, 1, 0x97, 0x00);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 1, 0x97, 0x00);
+ write_vctl (udev, 0x0c, 0x0004, 0x008b, 0x00);
+ read_vctl (udev, 0x0c, 0x0007, 0x0000, &rd_byte);
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00, 0x88,
+ 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+ 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8,
+ 0x00, 0xc0, 0x00);
+ write_regs (udev, 16, 0x84, 0x94, 0x80, 0xd1, 0x80, 0xc1, 0x82, 0x7f, 0xcf,
+ 0x04, 0xc1, 0x02, 0xc2, 0x00, 0xc3, 0x06, 0xc4, 0xff, 0xc5,
+ 0x40, 0xc6, 0x8c, 0xc7, 0xdc, 0xc8, 0x20, 0xc0, 0x72, 0x89,
+ 0xff, 0x86, 0xff);
+ poll1 (udev);
+
+ /* ready scan position */
+ /* 1/3" of unscannable area at top... */
+ if (dpi == 300)
+ topreg = 120 * 4;
+ else if (dpi == 600)
+ topreg = 139 * 4;
+ else if (dpi == 1200)
+ topreg = 152 * 4;
+ else /*if (dpi == 150) */
+ topreg = 120 * 4;
+ topreg += topline * (1200 / dpi);
+
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00, 0x88,
+ 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+ 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8,
+ 0x00, 0xc0, 0x00);
+ write_regs (udev, 14, 0x84, 0xb4, 0x80, 0xe1, 0xcf, 0x04, 0xc1, 0x02, 0xc2,
+ 0x00, 0xc3, 0x07, 0xc4, 0xff, 0xc5, 0x40, 0xc6, 0x8c, 0xc7,
+ 0xdc, 0xc8, 0x20, 0xc0, 0x72, 0x89, topreg & 255, 0x86,
+ 255 & (topreg >> 8));
+ write_regs (udev, 1, 0x97, 0x00);
+ poll2 (udev);
+
+ write_regs (udev, 8, 0x80, 0x00, 0x84, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0x86,
+ 0x00, 0x89, 0x00, 0x94, 0x00, 0x01, 0x02);
+ write_vctl (udev, 0x0c, 0x00c0, 0x8406, 0x00);
+ write_vctl (udev, 0x0c, 0x00c0, 0x0406, 0x00);
+ write_regs (udev, 16, 0xbe, 0x18, 0x80, 0x00, 0x84, 0x00, 0x89, 0x00, 0x88,
+ 0x00, 0x86, 0x00, 0x90, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+ 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8,
+ 0x00, 0xc0, 0x00);
+ if (dpi == 1200)
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0x24, 0x83, 0x00, 0x8d, 0xff);
+ else
+ write_regs (udev, 4, 0x83, 0x20, 0x8d, 0xff, 0x83, 0x00, 0x8d, 0xff);
+ if (dpi != 1200)
+ write_regs (udev, 5, 0x83, 0x00, 0xa3, 0xff, 0xa4, 0xff, 0xa1, 0xff, 0xa2,
+ 0xf7);
+ if (dpi == 1200)
+ write_regs (udev, 4, 0x83, 0x22, 0x87, 0x01, 0x83, 0x02, 0x87, 0x2c);
+ else
+ write_regs (udev, 4, 0x83, 0x22, 0x87, 0x01, 0x83, 0x02, 0x87, 0x16);
+ if (dpi == 1200)
+ write_regs (udev, 11, 0xa0, 0x00, 0x9c, 0x00, 0x9f, 0x40, 0x9d, 0x00,
+ 0x9e, 0x00, 0xa0, 0x00, 0xce, 0x0c, 0x83, 0x20, 0xa5, 0x00,
+ 0xa6, 0x00, 0xa7, 0x00);
+ else
+ write_regs (udev, 11, 0xa0, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0x9d, 0x00,
+ 0x9e, 0x00, 0xa0, 0x00, 0xce, 0x0c, 0x83, 0x20, 0xa5, 0x00,
+ 0xa6, 0x00, 0xa7, 0x00);
+
+ set_gain_black (udev, rgreg, ggreg, bgreg, redreg, greenreg, bluereg);
+ if (!bpp16)
+ {
+ if (dpi == 1200)
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0xc7, 0x99, 0x99, 0x9a, 0xd5,
+ 0x9b, 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x02, 0x98, 0xc8, 0x99, 0x99, 0x9a, 0xd3, 0x9b, 0x03,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00);
+ else if (dpi == 150)
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0x94, 0x99, 0x67, 0x9a, 0x83,
+ 0x9b, 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x02, 0x98, 0x7e, 0x99, 0x5d, 0x9a, 0x7d, 0x9b, 0x03,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00);
+ else
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0xb3, 0x99, 0x72, 0x9a, 0x9d,
+ 0x9b, 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x02, 0x98, 0xa3, 0x99, 0x6f, 0x9a, 0x94, 0x9b, 0x03,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00);
+ }
+ else
+ {
+ if (dpi == 1200)
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0xb9, 0x99, 0x7a, 0x9a, 0xd6,
+ 0x9b, 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x02, 0x98, 0xbc, 0x99, 0x7c, 0x9a, 0xd3, 0x9b, 0x03,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00);
+ else if (dpi == 150)
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0x9c, 0x99, 0x5f, 0x9a, 0x87,
+ 0x9b, 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x02, 0x98, 0x97, 0x99, 0x58, 0x9a, 0x81, 0x9b, 0x03,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00);
+ else
+ write_regs (udev, 16, 0x9b, 0x00, 0x98, 0x9d, 0x99, 0x79, 0x9a, 0x8e,
+ 0x9b, 0x01, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b,
+ 0x02, 0x98, 0x89, 0x99, 0x71, 0x9a, 0x80, 0x9b, 0x03,
+ 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00);
+ }
+ write_regs (udev, 4, 0x83, 0xa2, 0x85, 0x98, 0x83, 0x82, 0x85, 0x3a);
+ write_regs (udev, 1, 0x9d, 0x80);
+ write_regs (udev, 1, 0x9d, 0x00);
+ if (!bpp16)
+ {
+ if (dpi == 1200)
+ write_regs (udev, 1, 0x94, 0x51);
+ else
+ write_regs (udev, 1, 0x94, 0x41);
+ }
+ else
+ {
+ if (dpi == 1200)
+ write_regs (udev, 1, 0x94, 0x71);
+ else
+ write_regs (udev, 1, 0x94, 0x61);
+ }
+ lightmap = (unsigned short *) malloc (whitemapsize);
+ calc_lightmap (whitescan, lightmap, 0, dpi, gain, offset);
+ select_pixels (lightmap, dpi, leftpix, scanpix);
+ if (dpi == 1200)
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x80, 0xb2, 0x06, 0xb3, 0xff, 0xb4,
+ 0xff, 0xb5, 0x06);
+ else
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x40, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0x7f, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, whitemapsize, 0x00);
+ len =
+ usb_bulk_write (udev, 2, (unsigned char *) lightmap, whitemapsize,
+ wr_timeout);
+
+ calc_lightmap (whitescan, lightmap, 1, dpi, gain, offset);
+ if (dpi == 1200)
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0x7f, 0xb5, 0x07);
+ else
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x80, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0xbf, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, whitemapsize, 0x00);
+ fix_endian_short (&GRAYMASK, 1);
+ if (gray)
+ for (i = 0; i < whitemapsize / 2; i++)
+ lightmap[i] |= GRAYMASK;
+ len =
+ usb_bulk_write (udev, 2, (unsigned char *) lightmap, whitemapsize,
+ wr_timeout);
+
+ calc_lightmap (whitescan, lightmap, 2, dpi, gain, offset);
+ if (dpi == 1200)
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0x80, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0xff, 0xb5, 0x07);
+ else
+ write_regs (udev, 6, 0xb0, 0x00, 0xb1, 0xc0, 0xb2, 0x07, 0xb3, 0xff, 0xb4,
+ 0xff, 0xb5, 0x07);
+ write_vctl (udev, 0x0c, 0x0002, whitemapsize, 0x00);
+ len =
+ usb_bulk_write (udev, 2, (unsigned char *) lightmap, whitemapsize,
+ wr_timeout);
+
+ free (whitescan);
+ free (lightmap);
+
+ if (!bpp16)
+ download_lut8 (udev, dpi, gray ? 0 : 1);
+
+ write_regs (udev, 4, 0x83, 0xa2, 0x85, 0x01, 0x83, 0x82, 0x85, 0x00);
+ write_regs (udev, 1, 0x9d, 0x80);
+ write_regs (udev, 1, 0x9d, 0x00);
+
+ if (!bpp16)
+ {
+ if (dpi == 1200)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0x1f, 0xb5, 0x06, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0x43, 0xb9, 0x2d);
+ else if (dpi == 150)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0x0f, 0xb5, 0x07, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0xe0, 0xb9, 0x37);
+ else
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0x0f, 0xb5, 0x07, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0x90, 0xb9, 0x37);
+ }
+ else
+ {
+ if (dpi == 1200)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0x87, 0xb9, 0x18);
+ else if (dpi == 150)
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0xc1, 0xb9, 0x1e);
+ else
+ write_regs (udev, 10, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0xff,
+ 0xb4, 0xff, 0xb5, 0x03, 0xb6, 0x01, 0xb7, 0x00, 0xb8,
+ 0x21, 0xb9, 0x1e);
+ }
+
+ /* [86,89] controls number of 300dpi steps to scan */
+ botreg = scanlines * (1200 / dpi) + 400;
+ write_regs (udev, 6, 0xc0, 0x00, 0x84, 0x00, 0x84, 0xb4, 0x80, 0xe1, 0xcf,
+ 0x04, 0x82, 0x00);
+
+ if (!bpp16)
+ {
+ if (dpi == 300)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x04, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x10, 0x88, 0xd0, 0xc1, 0x01, 0xc2,
+ 0x00, 0xc3, 0x04, 0xc4, 0x20, 0xc5, 0x10, 0xc6, 0xc3,
+ 0xc7, 0xd0, 0xc8, 0x12, 0xc0, 0xd1, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+ else if (dpi == 600)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x04, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x10, 0x88, 0xd0, 0xc1, 0x01, 0xc2,
+ 0x00, 0xc3, 0x04, 0xc4, 0x20, 0xc5, 0x10, 0xc6, 0xc3,
+ 0xc7, 0xd0, 0xc8, 0x16, 0xc0, 0xd1, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+ else if (dpi == 1200)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x02, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x08, 0x88, 0xa4, 0xc1, 0x02, 0xc2,
+ 0x00, 0xc3, 0x02, 0xc4, 0x20, 0xc5, 0x08, 0xc6, 0x96,
+ 0xc7, 0xa4, 0xc8, 0x06, 0xc0, 0xd2, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+ else if (dpi == 150)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x06, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x1c, 0xbd, 0x08, 0x88, 0xe0, 0xc1, 0x01, 0xc2,
+ 0x00, 0xc3, 0x03, 0xc4, 0x1c, 0xc5, 0x08, 0xc6, 0xd7,
+ 0xc7, 0xe0, 0xc8, 0x11, 0xc0, 0xd1, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+
+ if (dpi == 300)
+ write_regs (udev, 8, 0xbb, 0x01, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x20, 0x91, 0x00, 0x83, 0x82);
+ else if (dpi == 600)
+ write_regs (udev, 8, 0xbb, 0x02, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x20, 0x91, 0x00, 0x83, 0x82);
+ else if (dpi == 1200)
+ write_regs (udev, 8, 0xbb, 0x02, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x20, 0x91, 0x00, 0x83, 0x82);
+ else if (dpi == 150)
+ write_regs (udev, 8, 0xbb, 0x00, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x20, 0x91, 0x00, 0x83, 0x82);
+ }
+ else
+ {
+ if (dpi == 300)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x04, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x10, 0x88, 0xd0, 0xc1, 0x01, 0xc2,
+ 0x00, 0xc3, 0x04, 0xc4, 0x20, 0xc5, 0x10, 0xc6, 0xc3,
+ 0xc7, 0xd0, 0xc8, 0x13, 0xc0, 0xd1, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+ else if (dpi == 150)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x06, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x1c, 0xbd, 0x08, 0x88, 0xe0, 0xc1, 0x01, 0xc2,
+ 0x00, 0xc3, 0x03, 0xc4, 0x1c, 0xc5, 0x08, 0xc6, 0xd7,
+ 0xc7, 0xe0, 0xc8, 0x12, 0xc0, 0xd1, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+ else if (dpi == 1200)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x02, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x08, 0x88, 0xa4, 0xc1, 0x02, 0xc2,
+ 0x00, 0xc3, 0x02, 0xc4, 0x20, 0xc5, 0x08, 0xc6, 0x96,
+ 0xc7, 0xa4, 0xc8, 0x0c, 0xc0, 0xd2, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+ else if (dpi == 600)
+ write_regs (udev, 18, 0x83, 0xa2, 0x85, 0x04, 0x83, 0x82, 0x85, 0x00,
+ 0xbc, 0x20, 0xbd, 0x10, 0x88, 0xd0, 0xc1, 0x01, 0xc2,
+ 0x00, 0xc3, 0x04, 0xc4, 0x20, 0xc5, 0x10, 0xc6, 0xc3,
+ 0xc7, 0xd0, 0xc8, 0x1a, 0xc0, 0xd1, 0x89, botreg & 255,
+ 0x86, 255 & (botreg >> 8));
+
+ if (dpi == 300)
+ write_regs (udev, 8, 0xbb, 0x02, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x70, 0x91, 0x00, 0x83, 0x82);
+ else if (dpi == 150)
+ write_regs (udev, 8, 0xbb, 0x01, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x70, 0x91, 0x00, 0x83, 0x82);
+ else if (dpi == 1200)
+ write_regs (udev, 8, 0xbb, 0x05, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x70, 0x91, 0x00, 0x83, 0x82);
+ else if (dpi == 600)
+ write_regs (udev, 8, 0xbb, 0x04, 0x9b, 0x24, 0x8b, 0x00, 0x8e, 0x80,
+ 0xbf, 0x00, 0x90, 0x70, 0x91, 0x00, 0x83, 0x82);
+
+
+ }
+
+ if (gray)
+ write_regs (udev, 1, 0xbe, 0x05);
+ else
+ write_regs (udev, 1, 0xbe, 0x0d);
+ write_vctl (udev, 0x0c, 0x0003, 0x0001, 0x00);
+ len = usb_bulk_read (udev, 1, &rd_byte, 1, rd_timeout);
+ write_vctl (udev, 0x0c, 0x0001, 0x0000, 0x00);
+
+#ifndef BACKENDNAME
+ sprintf (fname, "%d.%s", dpi, gray ? "pgm" : "ppm");
+ if (stname)
+ strcpy (fname, stname);
+ sprintf (head, "P%d\n%d %d\n%d\n", gray ? 5 : 6, scanpix, scanlines,
+ bpp16 ? 65535 : 255);
+ if (nohead)
+ head[0] = 0;
+ if (!raw)
+ record_image (udev, fname, dpi, scanpix, scanlines, gray, head, bpp16);
+ else
+ record_head (udev, fname,
+ scanpix * (gray ? 1 : 3) * (bpp16 ? 2 : 1) * scanlines, "");
+
+ reset_scanner (udev);
+ idle_ab (udev);
+ set_lamp_timer (udev, 5);
+#endif
+}
diff --git a/backend/snapscan-data.c b/backend/snapscan-data.c
new file mode 100644
index 0000000..1fd1a53
--- /dev/null
+++ b/backend/snapscan-data.c
@@ -0,0 +1,4150 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2006 Oliver Schwartz
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file contains static calibration data for the Benq 5150C scanner.
+ Once it is known how calibration for this scanner works this file
+ becomes obsolete.
+ */
+
+
+/* $Id$
+ SANE SnapScan backend */
+
+static const SANE_Byte calibration_data_5150[] =
+{
+ 0x78,0x01,0xcc,0x47,0xca,0x53,0xcd,0x4a,0xd0,0x57,0xd5,0x46,0xda,0x56,0xdd,0x49,
+ 0xe2,0x50,0xe1,0x47,0xe0,0x51,0xdf,0x4b,0xdc,0x4f,0xd4,0x52,0xd0,0x52,0xcd,0x4a,
+ 0xcd,0x4e,0xcf,0x46,0xd1,0x50,0xd8,0x4d,0xdc,0x53,0xdf,0x4c,0xe5,0x4f,0xe2,0x45,
+ 0xe2,0x53,0xe3,0x4b,0xde,0x50,0xd8,0x4b,0xd4,0x55,0xcf,0x4a,0xd0,0x50,0xd0,0x49,
+ 0xd4,0x4f,0xd8,0x49,0xe0,0x4d,0xe3,0x4a,0xe4,0x55,0xe7,0x47,0xe7,0x4f,0xe5,0x46,
+ 0xe1,0x51,0xdd,0x4a,0xd6,0x4d,0xd2,0x46,0xcd,0x51,0xcd,0x44,0xd3,0x54,0xd6,0x48,
+ 0xdb,0x51,0xe2,0x4a,0xe5,0x4f,0xe7,0x4b,0xe8,0x54,0xe5,0x4e,0xe0,0x4e,0xdc,0x47,
+ 0xd9,0x50,0xd4,0x48,0xd2,0x4c,0xd3,0x46,0xd6,0x4f,0xd9,0x4a,0xdf,0x4e,0xe1,0x47,
+ 0xe6,0x4e,0xe6,0x4d,0xe7,0x4e,0xe5,0x4a,0xe2,0x55,0xdc,0x47,0xd7,0x54,0xd2,0x4a,
+ 0xd1,0x50,0xce,0x45,0xd2,0x4e,0xd6,0x4a,0xd9,0x50,0xde,0x4a,0xe2,0x4d,0xe2,0x45,
+ 0xe5,0x4f,0xe3,0x4c,0xe0,0x4c,0xdc,0x4d,0xd4,0x51,0xd0,0x47,0xce,0x4d,0xcc,0x45,
+ 0xcd,0x50,0xd1,0x48,0xd4,0x4c,0xd8,0x48,0xda,0x55,0xde,0x4c,0xdd,0x4d,0xdb,0x46,
+ 0xda,0x52,0xd6,0x4b,0xd1,0x4f,0xcc,0x45,0xc9,0x4e,0xc7,0x48,0xc8,0x4e,0xcb,0x47,
+ 0xd0,0x51,0xd4,0x4b,0xd8,0x50,0xda,0x4a,0xdc,0x55,0xdb,0x45,0xd8,0x50,0xd4,0x4c,
+ 0xcf,0x53,0xca,0x45,0xc7,0x4b,0xc6,0x4a,0xc5,0x56,0xc7,0x47,0xcb,0x50,0xd2,0x4a,
+ 0xd5,0x55,0xd7,0x4a,0xda,0x4f,0xda,0x4a,0xd7,0x52,0xd4,0x4b,0xcf,0x54,0xca,0x46,
+ 0xc6,0x51,0xc5,0x4a,0xc5,0x51,0xc7,0x46,0xca,0x54,0xd0,0x4b,0xd3,0x51,0xd6,0x4a,
+ 0xd9,0x53,0xd9,0x47,0xd8,0x4d,0xd5,0x48,0xd0,0x53,0xca,0x47,0xc8,0x4c,0xc5,0x48,
+ 0xc5,0x52,0xc6,0x49,0xcb,0x4f,0xcd,0x47,0xd1,0x51,0xd6,0x49,0xd8,0x52,0xd7,0x43,
+ 0xd7,0x4d,0xd5,0x4a,0xcf,0x53,0xca,0x49,0xc7,0x50,0xc3,0x47,0xc1,0x51,0xc5,0x46,
+ 0xca,0x4d,0xcc,0x49,0xd2,0x52,0xd6,0x4c,0xd8,0x52,0xd8,0x47,0xd7,0x50,0xd4,0x49,
+ 0xd2,0x4c,0xcc,0x48,0xc9,0x4d,0xc6,0x47,0xc4,0x54,0xc6,0x45,0xc8,0x50,0xcd,0x46,
+ 0xd1,0x51,0xd4,0x44,0xd7,0x51,0xd7,0x46,0xd7,0x53,0xd5,0x4a,0xd2,0x4d,0xcd,0x45,
+ 0xcb,0x4f,0xc8,0x4c,0xc7,0x4d,0xc8,0x48,0xca,0x51,0xcd,0x45,0xd2,0x50,0xd6,0x48,
+ 0xda,0x50,0xdb,0x48,0xdb,0x4f,0xd8,0x47,0xd5,0x53,0xd1,0x4b,0xcc,0x51,0xc9,0x49,
+ 0xc9,0x4f,0xc7,0x4f,0xc9,0x53,0xcf,0x4a,0xd2,0x55,0xd7,0x49,0xda,0x4e,0xdc,0x48,
+ 0xdc,0x52,0xda,0x48,0xd7,0x4e,0xd3,0x4d,0xce,0x51,0xcc,0x4a,0xc8,0x4e,0xc9,0x47,
+ 0xcb,0x51,0xd1,0x49,0xd6,0x4a,0xd9,0x47,0xdd,0x53,0xdd,0x46,0xdf,0x4e,0xdc,0x4b,
+ 0xd8,0x51,0xd3,0x4c,0xd0,0x4c,0xcd,0x4a,0xc9,0x52,0xc8,0x4a,0xca,0x50,0xd0,0x4a,
+ 0xd5,0x51,0xd9,0x49,0xde,0x4d,0xe0,0x48,0xe0,0x54,0xe0,0x4b,0xda,0x50,0xd7,0x48,
+ 0xd2,0x50,0xce,0x49,0xcc,0x50,0xcb,0x45,0xcd,0x55,0xd0,0x49,0xd5,0x4f,0xda,0x49,
+ 0xde,0x4f,0xdf,0x49,0xdf,0x4e,0xdf,0x48,0xdd,0x52,0xd8,0x4c,0xd3,0x51,0xcd,0x49,
+ 0xcc,0x52,0xc9,0x44,0xca,0x4f,0xce,0x4c,0xd4,0x54,0xd7,0x48,0xdc,0x50,0xe0,0x49,
+ 0xe0,0x52,0xdf,0x49,0xdc,0x4c,0xd7,0x4b,0xd3,0x50,0xd0,0x48,0xca,0x50,0xca,0x4a,
+ 0xcc,0x50,0xcf,0x47,0xd2,0x51,0xd8,0x47,0xdd,0x53,0xe0,0x49,0xdf,0x51,0xde,0x46,
+ 0xdc,0x52,0xd9,0x46,0xd4,0x4e,0xce,0x45,0xcc,0x52,0xc9,0x49,0xc9,0x55,0xcc,0x46,
+ 0xd0,0x4d,0xd6,0x48,0xd9,0x4d,0xda,0x48,0xdf,0x52,0xdb,0x4a,0xde,0x50,0xd7,0x48,
+ 0xd6,0x50,0xcd,0x47,0xca,0x50,0xc8,0x48,0xc8,0x52,0xcb,0x48,0xcf,0x50,0xd2,0x46,
+ 0xd8,0x51,0xdb,0x49,0xde,0x4e,0xdd,0x48,0xda,0x52,0xd8,0x49,0xd3,0x4e,0xce,0x49,
+ 0xc9,0x50,0xc8,0x4b,0xc9,0x4f,0xcb,0x46,0xcc,0x52,0xd2,0x4b,0xda,0x4f,0xdc,0x45,
+ 0xdb,0x4f,0xdc,0x48,0xde,0x4e,0xda,0x44,0xd4,0x4f,0xcb,0x46,0xca,0x4e,0xc9,0x4b,
+ 0xc3,0x52,0xc6,0x46,0xcb,0x4e,0xd2,0x4c,0xd5,0x52,0xd8,0x49,0xda,0x51,0xdc,0x49,
+ 0xdc,0x51,0xd6,0x45,0xd3,0x4f,0xcd,0x49,0xc9,0x51,0xc5,0x4a,0xc5,0x54,0xc6,0x46,
+ 0xc8,0x54,0xd0,0x48,0xd5,0x4c,0xd6,0x46,0xdb,0x51,0xdd,0x48,0xda,0x50,0xd7,0x44,
+ 0xd3,0x4d,0xce,0x47,0xc9,0x52,0xc6,0x46,0xc5,0x56,0xc7,0x48,0xcb,0x53,0xce,0x48,
+ 0xd5,0x4e,0xd9,0x4b,0xdc,0x4f,0xdc,0x48,0xdd,0x54,0xdb,0x49,0xd3,0x52,0xce,0x49,
+ 0xcc,0x50,0xc6,0x4a,0xc5,0x4e,0xc7,0x46,0xcb,0x50,0xce,0x46,0xd4,0x4e,0xd7,0x46,
+ 0xdb,0x4b,0xdc,0x47,0xdc,0x51,0xda,0x49,0xd5,0x56,0xd2,0x4a,0xcd,0x4b,0xc9,0x49,
+ 0xc8,0x4f,0xc8,0x48,0xca,0x4c,0xce,0x47,0xd3,0x52,0xd8,0x4a,0xdc,0x51,0xdc,0x46,
+ 0xdc,0x4c,0xdc,0x48,0xd9,0x4d,0xd1,0x47,0xcc,0x58,0xc8,0x46,0xc7,0x54,0xc6,0x48,
+ 0xc8,0x4a,0xca,0x47,0xd2,0x4c,0xd4,0x45,0xd7,0x50,0xd9,0x4a,0xda,0x4e,0xd8,0x46,
+ 0xd5,0x51,0xd0,0x43,0xcb,0x4f,0xc6,0x45,0xc4,0x52,0xc7,0x43,0xc8,0x52,0xcb,0x45,
+ 0xcf,0x51,0xd5,0x46,0xd9,0x4f,0xda,0x4b,0xda,0x53,0xd9,0x46,0xd6,0x4e,0xd1,0x48,
+ 0xcb,0x51,0xc6,0x46,0xc6,0x4b,0xc4,0x40,0xc5,0x51,0xcb,0x47,0xd0,0x4d,0xd4,0x48,
+ 0xd7,0x52,0xd1,0x4d,0xdc,0x34,0xdb,0x2f,0xd9,0x37,0xd4,0x30,0xce,0x40,0xcb,0x33,
+ 0xca,0x3e,0xca,0x34,0xcc,0x32,0xd0,0x2f,0xd5,0x37,0xd7,0x30,0xda,0x37,0xdd,0x2d,
+ 0xdc,0x34,0xd9,0x30,0xd7,0x34,0xd3,0x2d,0xcc,0x35,0xca,0x2c,0xc7,0x33,0xc8,0x2e,
+ 0xca,0x37,0xcd,0x30,0xd1,0x35,0xd6,0x2d,0xd8,0x38,0xda,0x2c,0xda,0x37,0xd8,0x2d,
+ 0xd6,0x36,0xd3,0x2e,0xc8,0x32,0xc7,0x2f,0xc5,0x32,0xc6,0x2a,0xc8,0x38,0xca,0x2a,
+ 0xd0,0x36,0xd5,0x2e,0xd6,0x37,0xd9,0x2c,0xd9,0x31,0xd7,0x2e,0xd4,0x35,0xcf,0x30,
+ 0xcb,0x34,0xc9,0x2c,0xc4,0x32,0xc3,0x2e,0xc6,0x35,0xc9,0x2d,0xcc,0x34,0xd1,0x2d,
+ 0xd3,0x32,0xd5,0x2d,0xd6,0x36,0xd4,0x2d,0xd0,0x32,0xcd,0x30,0xc8,0x35,0xc5,0x2f,
+ 0xc2,0x36,0xc2,0x2d,0xc2,0x35,0xc4,0x2b,0xc9,0x39,0xcf,0x2f,0xd1,0x32,0xd3,0x2c,
+ 0xd7,0x38,0xd4,0x2f,0xd0,0x30,0xcd,0x2d,0xc9,0x39,0xc5,0x30,0xc3,0x35,0xc2,0x31,
+ 0xc3,0x35,0xc5,0x28,0xc8,0x33,0xcc,0x30,0xd0,0x37,0xd3,0x2e,0xd3,0x39,0xd0,0x2a,
+ 0xd0,0x34,0xcc,0x2e,0xc8,0x35,0xc3,0x2d,0xc0,0x35,0xbe,0x2d,0xbe,0x33,0xc2,0x2d,
+ 0xc5,0x33,0xca,0x2b,0xcd,0x36,0xcf,0x2a,0xd3,0x36,0xd2,0x29,0xd0,0x36,0xce,0x2e,
+ 0xca,0x38,0xc5,0x2a,0xc1,0x34,0xbf,0x2c,0xbf,0x35,0xc3,0x2d,0xc5,0x36,0xcb,0x2a,
+ 0xd0,0x37,0xd2,0x31,0xd5,0x3a,0xd5,0x2c,0xd2,0x37,0xd1,0x2d,0xcd,0x35,0xc9,0x2c,
+ 0xc7,0x35,0xc4,0x2d,0xc3,0x34,0xc4,0x2c,0xc8,0x3a,0xcd,0x2e,0xd0,0x37,0xd4,0x2e,
+ 0xd5,0x32,0xd6,0x2d,0xd3,0x35,0xd0,0x2e,0xcd,0x34,0xc9,0x2d,0xc6,0x38,0xc3,0x2c,
+ 0xc3,0x33,0xc5,0x2d,0xc8,0x33,0xcc,0x2a,0xcf,0x33,0xd4,0x2d,0xd6,0x34,0xd5,0x2e,
+ 0xd4,0x35,0xd0,0x28,0xcc,0x35,0xc8,0x2c,0xc6,0x36,0xc3,0x2d,0xc1,0x38,0xc2,0x2d,
+ 0xc4,0x35,0xc9,0x2f,0xcd,0x31,0xcf,0x2d,0xd4,0x35,0xd5,0x2f,0xd4,0x36,0xd2,0x2e,
+ 0xce,0x36,0xc8,0x2a,0xc5,0x36,0xc1,0x2a,0xc1,0x36,0xc2,0x2f,0xc6,0x3a,0xca,0x2f,
+ 0xce,0x3a,0xd2,0x2f,0xd2,0x33,0xd4,0x2f,0xd5,0x39,0xd3,0x2d,0xce,0x33,0xca,0x2b,
+ 0xc8,0x3c,0xc4,0x2f,0xc0,0x33,0xc2,0x2c,0xc6,0x35,0xc9,0x2f,0xcb,0x32,0xd0,0x2a,
+ 0xd3,0x37,0xd5,0x2f,0xd3,0x31,0xd1,0x33,0xd0,0x35,0xcd,0x2c,0xc8,0x34,0xc6,0x2c,
+ 0xc4,0x32,0xc4,0x31,0xc5,0x35,0xc7,0x30,0xcc,0x33,0xd2,0x2e,0xd6,0x35,0xd6,0x2e,
+ 0xd6,0x36,0xd5,0x2f,0xd2,0x38,0xcc,0x2f,0xc9,0x36,0xc4,0x30,0xc4,0x33,0xc2,0x27,
+ 0xc5,0x34,0xc9,0x2d,0xcc,0x32,0xcf,0x2c,0xd4,0x33,0xd6,0x30,0xd6,0x34,0xd4,0x2d,
+ 0xd3,0x39,0xce,0x30,0xca,0x36,0xc6,0x2e,0xc3,0x36,0xc2,0x30,0xc4,0x32,0xc8,0x2d,
+ 0xcd,0x35,0xd1,0x2f,0xd2,0x33,0xd3,0x2c,0xd6,0x36,0xd3,0x2f,0xcf,0x36,0xcd,0x2e,
+ 0xc9,0x36,0xc5,0x2c,0xc2,0x32,0xc2,0x2e,0xc3,0x34,0xc7,0x2e,0xca,0x33,0xd0,0x2e,
+ 0xd3,0x39,0xd3,0x30,0xd4,0x34,0xd4,0x2d,0xd2,0x35,0xcd,0x32,0xc9,0x33,0xc6,0x2f,
+ 0xc4,0x37,0xc2,0x2c,0xc2,0x35,0xc5,0x2d,0xc9,0x35,0xcd,0x2f,0xce,0x36,0xd2,0x2d,
+ 0xd2,0x36,0xd3,0x2f,0xcf,0x38,0xcc,0x2f,0xc8,0x37,0xc3,0x30,0xc1,0x36,0xbf,0x2f,
+ 0xc1,0x39,0xc3,0x30,0xc7,0x32,0xcc,0x2a,0xd0,0x38,0xd0,0x30,0xd1,0x36,0xd1,0x2c,
+ 0xcf,0x37,0xca,0x30,0xc5,0x37,0xc2,0x2c,0xc1,0x37,0xc0,0x2f,0xbf,0x38,0xc1,0x2e,
+ 0xc6,0x38,0xca,0x2e,0xce,0x35,0xd1,0x2b,0xd3,0x35,0xd3,0x2c,0xd0,0x39,0xcd,0x2a,
+ 0xca,0x36,0xc6,0x2e,0xc1,0x34,0xbe,0x31,0xc0,0x36,0xc1,0x2f,0xc4,0x39,0xc8,0x2a,
+ 0xce,0x35,0xcf,0x2d,0xcf,0x31,0xd0,0x2d,0xce,0x35,0xcb,0x2c,0xc7,0x36,0xc4,0x29,
+ 0xc2,0x35,0xbf,0x2f,0xbe,0x32,0xc0,0x2e,0xc3,0x38,0xc8,0x2e,0xca,0x36,0xcd,0x30,
+ 0xcf,0x33,0xd0,0x2d,0xcd,0x34,0xca,0x2f,0xc7,0x35,0xc0,0x30,0xc1,0x35,0xbd,0x2e,
+ 0xbc,0x32,0xbe,0x2d,0xc0,0x38,0xc5,0x31,0xca,0x37,0xcd,0x2d,0xce,0x37,0xce,0x2d,
+ 0xce,0x37,0xcb,0x30,0xc8,0x36,0xc3,0x2f,0xc0,0x35,0xbc,0x32,0xbb,0x35,0xbc,0x2c,
+ 0xc0,0x38,0xc5,0x2d,0xc7,0x33,0xc8,0x2f,0xcc,0x37,0xcd,0x2d,0xca,0x37,0xc8,0x31,
+ 0xc5,0x36,0xc2,0x2e,0xbe,0x35,0xba,0x30,0xbb,0x39,0xba,0x30,0xbd,0x34,0xc2,0x2f,
+ 0xc6,0x38,0xc8,0x2e,0xca,0x32,0xcb,0x30,0xc9,0x33,0xc7,0x2e,0xc4,0x35,0xbf,0x2c,
+ 0xbd,0x37,0xb9,0x2c,0xb7,0x37,0xb8,0x2f,0xbb,0x36,0xbf,0x2c,0xc3,0x33,0xc4,0x31,
+ 0xc7,0x31,0xc9,0x31,0xc8,0x34,0xc4,0x30,0xc2,0x34,0xc1,0x2b,0xbb,0x38,0xb8,0x30,
+ 0xb8,0x34,0xb8,0x2e,0xb9,0x33,0xbd,0x2e,0xc2,0x37,0xc7,0x2b,0xc9,0x34,0xc8,0x2e,
+ 0xc9,0x31,0xc9,0x2c,0xc4,0x33,0xbf,0x30,0xbb,0x35,0xba,0x2d,0xb9,0x39,0xb9,0x2c,
+ 0xba,0x32,0xbc,0x30,0xc2,0x35,0xc5,0x2a,0xc8,0x36,0xc7,0x2e,0xc7,0x33,0xc7,0x2c,
+ 0xc4,0x38,0xc0,0x2e,0xbb,0x32,0xb8,0x2d,0xb7,0x37,0xb6,0x2e,0xb8,0x36,0xbb,0x2f,
+ 0xc1,0x37,0xc4,0x2f,0xc6,0x32,0xc8,0x29,0xc8,0x37,0xc8,0x2f,0xc4,0x37,0xc0,0x2c,
+ 0xbc,0x32,0xbb,0x2d,0xb8,0x36,0xb6,0x2a,0xb7,0x32,0xbb,0x2f,0xbf,0x33,0xc2,0x30,
+ 0xc6,0x38,0xc8,0x2c,0xc8,0x32,0xc6,0x2e,0xc5,0x37,0xc1,0x2c,0xbd,0x39,0xba,0x2d,
+ 0xb8,0x32,0xb7,0x2b,0xb8,0x39,0xba,0x2f,0xbf,0x33,0xc3,0x2f,0xc6,0x34,0xc8,0x2c,
+ 0xc9,0x36,0xc7,0x32,0xc2,0x30,0xbe,0x2f,0xbd,0x34,0xb9,0x2b,0xb4,0x34,0xb5,0x2e,
+ 0xb7,0x37,0xb9,0x2b,0xbb,0x35,0xc1,0x2b,0xc5,0x39,0xc7,0x2f,0xc6,0x32,0xc6,0x2a,
+ 0xc6,0x37,0xbe,0x30,0xbc,0x36,0xb8,0x31,0xb7,0x38,0xb4,0x29,0xb4,0x30,0xb8,0x25,
+ 0xbb,0x30,0xbf,0x2c,0xc3,0x36,0xc5,0x2e,0xc5,0x38,0xbc,0x32,0xc2,0x51,0xc0,0x4c,
+ 0xbd,0x52,0xb9,0x57,0xb6,0x61,0xb4,0x57,0xb6,0x5b,0xb9,0x51,0xbd,0x56,0xc0,0x50,
+ 0xc3,0x56,0xc4,0x51,0xc6,0x56,0xc4,0x54,0xc2,0x55,0xbf,0x51,0xba,0x5a,0xb8,0x4f,
+ 0xb6,0x57,0xb4,0x4f,0xb5,0x54,0xb8,0x4f,0xbd,0x57,0xbd,0x50,0xc2,0x56,0xc2,0x51,
+ 0xc2,0x56,0xc0,0x50,0xc1,0x59,0xbb,0x4f,0xb7,0x58,0xb4,0x52,0xb2,0x59,0xb1,0x51,
+ 0xb2,0x5a,0xb5,0x52,0xbc,0x58,0xbf,0x4e,0xbe,0x55,0xc0,0x4e,0xc6,0x56,0xc2,0x4f,
+ 0xbc,0x58,0xbe,0x4f,0xb9,0x56,0xb6,0x4f,0xb2,0x56,0xb2,0x4e,0xb3,0x5a,0xb5,0x4f,
+ 0xb6,0x54,0xbc,0x4c,0xbf,0x55,0xbf,0x52,0xc1,0x55,0xc2,0x4f,0xc0,0x57,0xbd,0x52,
+ 0xb8,0x58,0xb5,0x50,0xb4,0x54,0xb0,0x50,0xb2,0x5a,0xb4,0x52,0xb6,0x59,0xb9,0x4e,
+ 0xbd,0x57,0xbf,0x4f,0xbd,0x53,0xbe,0x4f,0xbe,0x59,0xbb,0x4e,0xb7,0x57,0xb4,0x4f,
+ 0xb0,0x55,0xad,0x4b,0xae,0x58,0xad,0x4d,0xb3,0x58,0xb6,0x50,0xbb,0x5b,0xbd,0x50,
+ 0xbf,0x56,0xc0,0x4b,0xbc,0x57,0xba,0x4f,0xb7,0x55,0xb2,0x4e,0xae,0x5f,0xae,0x4d,
+ 0xb0,0x55,0xb0,0x51,0xb3,0x55,0xb7,0x4f,0xbb,0x59,0xbb,0x53,0xbe,0x58,0xbd,0x50,
+ 0xbb,0x59,0xb8,0x50,0xb6,0x54,0xb1,0x50,0xae,0x55,0xaa,0x4c,0xac,0x55,0xb0,0x4e,
+ 0xb2,0x5b,0xb4,0x4e,0xba,0x59,0xbd,0x4f,0xbf,0x56,0xbe,0x50,0xbe,0x58,0xb9,0x51,
+ 0xb8,0x59,0xb4,0x4f,0xb0,0x56,0xae,0x4f,0xad,0x56,0xb1,0x4d,0xb3,0x55,0xb5,0x4f,
+ 0xb9,0x5b,0xbc,0x51,0xbe,0x58,0xbc,0x51,0xbd,0x5e,0xbb,0x51,0xb6,0x57,0xb2,0x50,
+ 0xae,0x59,0xab,0x51,0xab,0x53,0xaa,0x50,0xaf,0x5a,0xb2,0x50,0xb6,0x56,0xba,0x50,
+ 0xbd,0x59,0xbb,0x55,0xba,0x57,0xb9,0x4d,0xb8,0x58,0xb1,0x52,0xaf,0x58,0xad,0x4e,
+ 0xac,0x57,0xad,0x4c,0xaf,0x55,0xb1,0x4c,0xb4,0x59,0xb6,0x51,0xbb,0x59,0xba,0x4d,
+ 0xba,0x59,0xba,0x4f,0xb6,0x59,0xb1,0x52,0xaf,0x56,0xac,0x4f,0xaa,0x56,0xab,0x51,
+ 0xae,0x59,0xaf,0x51,0xb0,0x57,0xb8,0x53,0xba,0x5a,0xb8,0x4d,0xba,0x57,0xb9,0x51,
+ 0xb7,0x58,0xb1,0x52,0xae,0x59,0xac,0x51,0xac,0x56,0xaa,0x4c,0xad,0x59,0xb1,0x4f,
+ 0xb4,0x56,0xb7,0x52,0xb9,0x57,0xbb,0x4d,0xb9,0x57,0xb9,0x52,0xb8,0x5a,0xb1,0x50,
+ 0xae,0x57,0xae,0x4e,0xac,0x58,0xaa,0x4f,0xad,0x57,0xb2,0x4f,0xb4,0x57,0xb7,0x4b,
+ 0xb9,0x55,0xb9,0x50,0xba,0x55,0xb5,0x4e,0xb3,0x58,0xb3,0x4f,0xae,0x55,0xa9,0x4e,
+ 0xa9,0x53,0xab,0x4f,0xad,0x56,0xb0,0x52,0xb2,0x54,0xb6,0x52,0xba,0x5b,0xba,0x51,
+ 0xba,0x55,0xba,0x50,0xb6,0x56,0xb3,0x4d,0xb1,0x57,0xad,0x50,0xab,0x5a,0xac,0x4f,
+ 0xac,0x54,0xae,0x4f,0xb1,0x57,0xb4,0x51,0xb7,0x56,0xb8,0x50,0xb9,0x59,0xbb,0x4e,
+ 0xb8,0x57,0xad,0x4e,0xa6,0x55,0xac,0x4f,0xa8,0x58,0xa0,0x4d,0xa3,0x57,0xac,0x50,
+ 0xb0,0x55,0xb3,0x52,0xb6,0x5a,0xb7,0x4d,0xb9,0x58,0xb8,0x4d,0xb5,0x59,0xb4,0x4f,
+ 0xaf,0x56,0xac,0x4f,0xa9,0x57,0xa8,0x4d,0xa9,0x57,0xac,0x4f,0xaf,0x59,0xb1,0x50,
+ 0xb7,0x56,0xb6,0x4e,0xb4,0x5a,0xb6,0x4f,0xb5,0x59,0xaf,0x50,0xac,0x58,0xa9,0x4e,
+ 0xa7,0x5a,0xa6,0x54,0xa7,0x57,0xa9,0x4b,0xad,0x59,0xaf,0x50,0xb4,0x58,0xb3,0x4f,
+ 0xb5,0x54,0xb8,0x4f,0xb4,0x53,0xb0,0x4d,0xae,0x5a,0xac,0x50,0xa9,0x5a,0xa7,0x51,
+ 0xa7,0x57,0xa8,0x51,0xad,0x56,0xb1,0x54,0xb5,0x5a,0xb6,0x51,0xbb,0x59,0xb8,0x52,
+ 0xb6,0x56,0xb3,0x4d,0xb1,0x54,0xae,0x54,0xa9,0x55,0xa6,0x4f,0xa8,0x55,0xab,0x4f,
+ 0xae,0x5c,0xaf,0x53,0xb4,0x58,0xb6,0x51,0xb7,0x5f,0xb7,0x4f,0xb6,0x56,0xb3,0x50,
+ 0xaf,0x56,0xad,0x4f,0xa9,0x57,0xa7,0x4f,0xa9,0x59,0xaa,0x55,0xad,0x57,0xaf,0x50,
+ 0xb3,0x54,0xb7,0x52,0xb9,0x57,0xb6,0x54,0xb5,0x58,0xb7,0x4e,0xb1,0x57,0xab,0x50,
+ 0xaa,0x57,0xa6,0x53,0xa6,0x56,0xa8,0x51,0xa9,0x5a,0xac,0x50,0xb1,0x56,0xb3,0x4d,
+ 0xb8,0x5a,0xb7,0x50,0xb4,0x55,0xb4,0x4e,0xb4,0x59,0xaf,0x54,0xab,0x57,0xab,0x4d,
+ 0xaa,0x5a,0xab,0x4f,0xab,0x59,0xae,0x51,0xb3,0x5b,0xb5,0x52,0xb7,0x58,0xb7,0x50,
+ 0xba,0x5a,0xb6,0x4e,0xb3,0x54,0xae,0x53,0xac,0x59,0xac,0x4f,0xa7,0x57,0xa6,0x51,
+ 0xab,0x56,0xad,0x51,0xb1,0x57,0xb3,0x4f,0xb9,0x5a,0xb7,0x4d,0xb6,0x5a,0xb7,0x4f,
+ 0xb5,0x59,0xb1,0x54,0xac,0x57,0xa8,0x51,0xa6,0x55,0xa5,0x50,0xa8,0x57,0xac,0x4f,
+ 0xae,0x56,0xb3,0x4f,0xb6,0x58,0xb8,0x4f,0xb8,0x59,0xb4,0x4e,0xb1,0x58,0xad,0x50,
+ 0xac,0x54,0xa6,0x4b,0xa5,0x57,0xa5,0x4e,0xa8,0x58,0xaa,0x52,0xae,0x59,0xaf,0x52,
+ 0xb4,0x59,0xb4,0x52,0xb4,0x55,0xb2,0x54,0xb2,0x57,0xad,0x4d,0xa8,0x55,0xa8,0x4e,
+ 0xa6,0x55,0xa4,0x53,0xa5,0x56,0xa9,0x4d,0xae,0x58,0xb1,0x4d,0xb2,0x58,0xb4,0x51,
+ 0xb6,0x5a,0xb5,0x51,0xb1,0x53,0xae,0x51,0xab,0x58,0xa8,0x51,0xa4,0x58,0xa4,0x4c,
+ 0xa7,0x58,0xaa,0x4e,0xac,0x51,0xb0,0x4f,0xb4,0x59,0xb6,0x54,0xb5,0x56,0xb5,0x50,
+ 0xb5,0x57,0xb0,0x52,0xac,0x50,0xaa,0x4b,0xa5,0x58,0xa2,0x50,0xa7,0x58,0xa8,0x4d,
+ 0xaa,0x5b,0xad,0x50,0xb3,0x56,0xb7,0x4f,0xb6,0x57,0xb4,0x54,0xb2,0x55,0xb0,0x50,
+ 0xad,0x5a,0xa9,0x51,0xa5,0x54,0xa6,0x4f,0xa8,0x54,0xa8,0x54,0xab,0x58,0xae,0x50,
+ 0xb4,0x5a,0xb6,0x51,0xb6,0x56,0xad,0x4c,0xb2,0x58,0xb2,0x4f,0xa8,0x5d,0xa2,0x4b,
+ 0xa7,0x54,0xa7,0x50,0xa5,0x57,0xa9,0x4e,0xac,0x57,0xae,0x53,0xb3,0x5a,0xb4,0x50,
+ 0xb7,0x59,0xb6,0x50,0xb4,0x54,0xb1,0x4e,0xad,0x5c,0xa8,0x50,0xa5,0x57,0xa4,0x4f,
+ 0xa2,0x5b,0xa5,0x4a,0xa8,0x4e,0xad,0x48,0xb1,0x54,0xb4,0x53,0xb4,0x57,0xb2,0x51,
+ 0xb4,0x5a,0xa7,0x53,0xac,0x44,0xac,0x42,0xa9,0x46,0xa7,0x43,0xaa,0x4e,0xab,0x48,
+ 0xaf,0x4f,0xb1,0x41,0xb5,0x48,0xb8,0x3e,0xb8,0x49,0xb7,0x43,0xb5,0x4c,0xb2,0x3e,
+ 0xb0,0x45,0xa9,0x41,0xa9,0x47,0xa7,0x3d,0xa8,0x46,0xaa,0x41,0xae,0x49,0xb1,0x42,
+ 0xb4,0x48,0xb8,0x3f,0xb7,0x43,0xb6,0x3d,0xb5,0x44,0xb2,0x40,0xaf,0x49,0xaa,0x40,
+ 0xa8,0x4a,0xa6,0x40,0xa7,0x42,0xa8,0x43,0xaa,0x45,0xb0,0x42,0xb3,0x49,0xb4,0x40,
+ 0xb6,0x4a,0xb5,0x41,0xb3,0x45,0xaf,0x42,0xaf,0x43,0xa9,0x41,0xa6,0x49,0xa5,0x43,
+ 0xa4,0x49,0xa5,0x3e,0xa8,0x46,0xad,0x42,0xb1,0x44,0xb2,0x45,0xb4,0x48,0xb3,0x42,
+ 0xb3,0x4b,0xb2,0x3e,0xac,0x47,0xa8,0x40,0xa7,0x49,0xa3,0x41,0xa3,0x46,0xa4,0x42,
+ 0xa5,0x47,0xaa,0x3f,0xaf,0x45,0xb0,0x3c,0xb1,0x4b,0xb3,0x40,0xb2,0x46,0xad,0x42,
+ 0xac,0x46,0xa9,0x45,0xa4,0x49,0xa2,0x42,0xa4,0x49,0xa4,0x3d,0xa8,0x4a,0xa8,0x41,
+ 0xae,0x48,0xb0,0x40,0xb1,0x43,0xb0,0x42,0xb1,0x49,0xae,0x43,0xaa,0x4c,0xa7,0x41,
+ 0xa6,0x4b,0xa3,0x40,0xa2,0x4c,0xa1,0x3b,0xa4,0x49,0xa6,0x41,0xab,0x48,0xad,0x44,
+ 0xb1,0x4a,0xb0,0x42,0xb1,0x44,0xaf,0x44,0xad,0x46,0xa6,0x42,0xa4,0x46,0xa1,0x40,
+ 0xa2,0x4b,0x9f,0x41,0xa2,0x46,0xa4,0x42,0xa9,0x49,0xad,0x42,0xae,0x4b,0xae,0x40,
+ 0xaf,0x49,0xab,0x40,0xa9,0x42,0xa6,0x3d,0xa3,0x45,0xa0,0x43,0xa0,0x4c,0x9e,0x40,
+ 0xa1,0x44,0xa6,0x3f,0xa8,0x42,0xab,0x41,0xaf,0x4a,0xaf,0x3c,0xb0,0x48,0xae,0x3f,
+ 0xac,0x47,0xa8,0x3f,0xa3,0x43,0xa0,0x41,0xa0,0x49,0x9f,0x3e,0xa1,0x49,0xa3,0x3d,
+ 0xa7,0x48,0xa9,0x40,0xad,0x4a,0xae,0x40,0xaf,0x4e,0xad,0x42,0xac,0x4a,0xa7,0x41,
+ 0xa5,0x49,0xa2,0x43,0x9f,0x46,0x9f,0x3e,0xa0,0x49,0xa2,0x45,0xa5,0x47,0xa8,0x3c,
+ 0xab,0x45,0xac,0x45,0xac,0x47,0xab,0x40,0xa9,0x4c,0xa5,0x40,0xa2,0x4a,0xa0,0x3c,
+ 0x9d,0x4b,0x9b,0x3d,0x9d,0x46,0xa0,0x3e,0xa5,0x4d,0xa6,0x3f,0xaa,0x4d,0xac,0x3c,
+ 0xad,0x49,0xab,0x3f,0xaa,0x4a,0xa6,0x43,0xa3,0x4e,0x9d,0x41,0x9b,0x48,0x9a,0x3f,
+ 0x9d,0x49,0x9c,0x40,0xa1,0x48,0xa4,0x40,0xa8,0x4d,0xa8,0x42,0xaa,0x4d,0xaa,0x42,
+ 0xa8,0x48,0xa5,0x41,0xa2,0x46,0x9f,0x3e,0x9c,0x4c,0x9b,0x42,0x9b,0x44,0x9c,0x43,
+ 0x9f,0x47,0xa4,0x3f,0xa5,0x49,0xa8,0x3e,0xaa,0x4a,0xa8,0x42,0xa8,0x4c,0xa3,0x3c,
+ 0x9f,0x48,0x9d,0x3f,0x9c,0x44,0x97,0x41,0x98,0x49,0x9c,0x42,0x9f,0x46,0xa2,0x42,
+ 0xa5,0x46,0xa7,0x3f,0xaa,0x44,0xa7,0x41,0xa7,0x4d,0xa5,0x40,0xa1,0x49,0x9b,0x42,
+ 0x9c,0x4c,0x9a,0x3f,0x9a,0x47,0x9b,0x3f,0x9e,0x46,0xa2,0x44,0xa4,0x46,0xa6,0x3f,
+ 0xa8,0x48,0xa9,0x3e,0xa6,0x46,0xa4,0x40,0xa1,0x4b,0x9e,0x3f,0x9c,0x48,0x99,0x3d,
+ 0x99,0x47,0x99,0x41,0x9d,0x4b,0xa2,0x3d,0xa4,0x4c,0xa7,0x40,0xaa,0x4b,0xa8,0x3f,
+ 0xa9,0x4a,0xa8,0x3d,0xa2,0x4a,0x9e,0x41,0x9f,0x4e,0x9a,0x42,0x98,0x4d,0x98,0x44,
+ 0x9e,0x49,0x9f,0x41,0xa2,0x48,0xa7,0x44,0xaa,0x4d,0xa7,0x40,0xa9,0x4a,0xa8,0x3e,
+ 0xa3,0x47,0xa0,0x3e,0x9d,0x4b,0x99,0x40,0x9a,0x49,0x9a,0x3d,0x9d,0x4a,0x9f,0x3d,
+ 0xa2,0x4d,0xa5,0x41,0xa8,0x4d,0xa4,0x41,0xa5,0x4c,0xa4,0x43,0xa2,0x4a,0x9c,0x3b,
+ 0x9a,0x4a,0x98,0x42,0x97,0x48,0x96,0x44,0x99,0x4d,0x9d,0x41,0xa2,0x49,0xa3,0x3f,
+ 0xa5,0x47,0xa6,0x3f,0xa5,0x45,0xa3,0x3e,0xa1,0x49,0x9d,0x42,0x9a,0x48,0x98,0x3e,
+ 0x97,0x49,0x98,0x3f,0x98,0x47,0x9d,0x3f,0xa0,0x4b,0xa5,0x3e,0xa7,0x44,0xa5,0x40,
+ 0xa7,0x49,0xa5,0x41,0xa3,0x47,0xa0,0x44,0x9b,0x4a,0x97,0x40,0x98,0x4a,0x97,0x40,
+ 0x9b,0x49,0x9d,0x41,0xa1,0x49,0xa5,0x3f,0xa8,0x47,0xa6,0x40,0xa7,0x48,0xa7,0x3e,
+ 0xa5,0x4b,0x9f,0x40,0x9b,0x48,0x97,0x40,0x96,0x4c,0x98,0x3f,0x97,0x4e,0x97,0x41,
+ 0x9f,0x49,0xa0,0x3d,0xa0,0x49,0xa2,0x42,0xa7,0x49,0xa3,0x44,0x9f,0x4a,0x9e,0x41,
+ 0x9b,0x4c,0x96,0x3e,0x98,0x48,0x96,0x3d,0x98,0x4d,0x9a,0x3e,0x9f,0x46,0xa2,0x41,
+ 0xa3,0x4c,0xa7,0x3f,0xa5,0x45,0xa4,0x41,0xa4,0x4b,0xa2,0x40,0x9c,0x47,0x99,0x43,
+ 0x98,0x45,0x97,0x3f,0x98,0x47,0x99,0x41,0x9d,0x4f,0xa1,0x41,0xa5,0x48,0xa6,0x3f,
+ 0xa6,0x4b,0xa5,0x3e,0xa5,0x44,0xa2,0x3d,0x9d,0x4f,0x9a,0x3e,0x98,0x47,0x96,0x41,
+ 0x99,0x48,0x9c,0x3d,0x9e,0x49,0xa2,0x3f,0xa5,0x48,0xa6,0x41,0xa7,0x4c,0xa5,0x3f,
+ 0xa4,0x48,0xa3,0x3d,0x9e,0x49,0x9a,0x3d,0x99,0x49,0x97,0x43,0x99,0x4b,0x9a,0x3f,
+ 0x9e,0x46,0xa1,0x43,0xa4,0x45,0xa6,0x42,0xa7,0x4b,0xa6,0x3e,0xa6,0x46,0xa2,0x46,
+ 0x9f,0x47,0x99,0x42,0x96,0x46,0x98,0x3d,0x96,0x4b,0x97,0x41,0x9b,0x4a,0x9f,0x3f,
+ 0xa2,0x47,0xa4,0x42,0xa5,0x49,0xa3,0x40,0xa4,0x4b,0xa2,0x3f,0x9e,0x49,0x9a,0x3f,
+ 0x97,0x4b,0x96,0x42,0x96,0x4b,0x97,0x3b,0x99,0x49,0x9e,0x40,0xa2,0x48,0xa4,0x43,
+ 0xa6,0x49,0xa5,0x40,0xa5,0x47,0xa2,0x3f,0xa0,0x4c,0x9b,0x40,0x97,0x49,0x95,0x40,
+ 0x93,0x4a,0x96,0x43,0x99,0x45,0x9c,0x3f,0xa0,0x46,0xa3,0x40,0xa5,0x4a,0xa4,0x41,
+ 0xa6,0x4c,0xa2,0x42,0x9f,0x46,0x9b,0x3f,0x9a,0x4e,0x97,0x41,0x97,0x48,0x97,0x41,
+ 0x9b,0x47,0x9e,0x3e,0xa1,0x49,0xa2,0x41,0xa5,0x4a,0xa6,0x3f,0xa5,0x4a,0xa3,0x3f,
+ 0xa1,0x46,0x9c,0x3b,0x96,0x45,0x96,0x3f,0x97,0x4b,0x94,0x3f,0x96,0x49,0x9c,0x3f,
+ 0xa1,0x4c,0xa4,0x43,0xa4,0x48,0xa6,0x3e,0xa5,0x4a,0xa2,0x3d,0x9f,0x47,0x9d,0x3e,
+ 0x9a,0x48,0x96,0x40,0x94,0x47,0x94,0x47,0x98,0x4f,0x9a,0x40,0xa0,0x41,0xa2,0x37,
+ 0xa5,0x48,0xa6,0x3f,0xa4,0x44,0xa1,0x41,0xa1,0x4c,0x95,0x41,0x99,0x53,0x98,0x54,
+ 0x99,0x58,0x9b,0x55,0x9e,0x60,0xa3,0x57,0xa5,0x63,0xa7,0x55,0xa9,0x5c,0xa8,0x54,
+ 0xa8,0x56,0xa6,0x4e,0xa1,0x55,0x9e,0x50,0x9d,0x5c,0x9a,0x54,0x9b,0x57,0x9a,0x52,
+ 0x9e,0x56,0xa2,0x4e,0xa4,0x5d,0xa6,0x50,0xaa,0x5e,0xa8,0x4d,0xa6,0x59,0xa7,0x4c,
+ 0xa4,0x59,0x9e,0x4e,0x9d,0x55,0x97,0x51,0x99,0x5c,0x99,0x50,0x9a,0x53,0x9e,0x4f,
+ 0xa3,0x57,0xa6,0x54,0xa6,0x57,0xa6,0x4d,0xa6,0x58,0xa6,0x4f,0xa2,0x56,0x9c,0x54,
+ 0x9b,0x5d,0x99,0x54,0x96,0x55,0x97,0x4f,0x99,0x59,0x9c,0x51,0xa1,0x5d,0xa5,0x4d,
+ 0xa6,0x5b,0xa6,0x50,0xa7,0x54,0xa5,0x4d,0xa2,0x5e,0x9e,0x52,0x9b,0x56,0x98,0x50,
+ 0x98,0x56,0x98,0x4f,0x9a,0x58,0x9d,0x4e,0xa2,0x57,0xa4,0x51,0xa7,0x60,0xa7,0x51,
+ 0xa8,0x59,0xa6,0x50,0xa3,0x55,0x9d,0x53,0x9b,0x5b,0x98,0x4f,0x96,0x58,0x96,0x53,
+ 0x99,0x5a,0x9a,0x54,0x9f,0x5a,0xa2,0x52,0xa5,0x5c,0xa7,0x4d,0xa8,0x5a,0xa4,0x51,
+ 0xa3,0x58,0x9f,0x51,0x99,0x54,0x96,0x50,0x96,0x5e,0x94,0x51,0x96,0x5a,0x99,0x52,
+ 0x9d,0x55,0xa0,0x50,0xa4,0x58,0xa4,0x54,0xa6,0x5f,0xa6,0x4e,0xa2,0x57,0x9f,0x4f,
+ 0x9e,0x5b,0x98,0x51,0x97,0x57,0x97,0x53,0x98,0x5a,0x9a,0x52,0x9c,0x55,0xa1,0x52,
+ 0xa3,0x5a,0xa4,0x4d,0xa4,0x57,0xa4,0x54,0xa2,0x5a,0x9c,0x53,0x9c,0x5a,0x98,0x4e,
+ 0x97,0x58,0x95,0x51,0x98,0x58,0x99,0x54,0x9d,0x5a,0x9f,0x51,0xa2,0x57,0xa5,0x4d,
+ 0xa5,0x58,0xa3,0x51,0xa2,0x58,0x9f,0x4f,0x9b,0x58,0x97,0x4d,0x95,0x59,0x93,0x52,
+ 0x95,0x5c,0x97,0x52,0x9a,0x5a,0x9d,0x54,0xa0,0x5d,0xa1,0x50,0xa4,0x5b,0xa2,0x53,
+ 0x9f,0x56,0x9d,0x52,0x9b,0x58,0x96,0x51,0x94,0x5d,0x93,0x50,0x94,0x59,0x96,0x4f,
+ 0x99,0x57,0x9d,0x51,0xa0,0x58,0xa0,0x52,0xa3,0x60,0xa3,0x52,0xa1,0x57,0x9d,0x52,
+ 0x9d,0x5a,0x99,0x4f,0x94,0x54,0x94,0x52,0x94,0x5a,0x95,0x4e,0x99,0x55,0x9b,0x51,
+ 0xa0,0x5d,0xa1,0x4d,0xa2,0x56,0xa1,0x52,0xa0,0x5a,0x9d,0x52,0x99,0x5b,0x97,0x50,
+ 0x94,0x5f,0x90,0x52,0x92,0x58,0x94,0x50,0x98,0x5a,0x9a,0x4f,0x9d,0x57,0xa0,0x55,
+ 0xa0,0x59,0xa0,0x4f,0x9f,0x5b,0x9d,0x4f,0x9a,0x5b,0x95,0x51,0x93,0x56,0x90,0x51,
+ 0x91,0x5d,0x93,0x52,0x95,0x55,0x9a,0x4f,0x9d,0x60,0x9f,0x55,0xa0,0x57,0xa0,0x4d,
+ 0xa1,0x5a,0x9d,0x52,0x99,0x56,0x97,0x56,0x95,0x5e,0x92,0x4e,0x92,0x58,0x92,0x51,
+ 0x95,0x59,0x98,0x50,0x9c,0x59,0x9e,0x50,0xa0,0x5d,0xa0,0x4e,0xa0,0x5a,0x9c,0x50,
+ 0x99,0x55,0x95,0x50,0x94,0x59,0x8e,0x50,0x8f,0x5a,0x91,0x50,0x94,0x59,0x96,0x51,
+ 0x9a,0x59,0x9e,0x56,0x9f,0x5a,0xa0,0x53,0x9f,0x5a,0x9d,0x53,0x9a,0x59,0x96,0x51,
+ 0x93,0x5a,0x90,0x51,0x8f,0x54,0x91,0x4f,0x92,0x56,0x93,0x50,0x98,0x57,0x9c,0x4c,
+ 0x9c,0x59,0x9d,0x51,0x9e,0x5d,0x9c,0x4f,0x9a,0x61,0x97,0x4a,0x94,0x5b,0x8f,0x55,
+ 0x8f,0x57,0x8e,0x50,0x91,0x5b,0x95,0x4e,0x98,0x5c,0x9c,0x4f,0x9d,0x55,0x9f,0x4d,
+ 0x9e,0x5d,0x9b,0x53,0x99,0x57,0x96,0x50,0x92,0x5d,0x8f,0x52,0x8d,0x57,0x8d,0x50,
+ 0x90,0x5e,0x92,0x53,0x97,0x5a,0x99,0x52,0x9c,0x5a,0x9d,0x53,0x9d,0x58,0x9b,0x53,
+ 0x99,0x5a,0x95,0x51,0x93,0x59,0x90,0x53,0x8f,0x5e,0x8d,0x53,0x8e,0x5a,0x91,0x52,
+ 0x96,0x5b,0x98,0x52,0x9b,0x5a,0x9d,0x4f,0x9c,0x5c,0x98,0x51,0x97,0x58,0x96,0x54,
+ 0x91,0x58,0x8c,0x51,0x8c,0x59,0x8c,0x56,0x8e,0x5c,0x8e,0x52,0x94,0x58,0x97,0x56,
+ 0x99,0x57,0x9a,0x53,0x9c,0x54,0x98,0x51,0x97,0x5b,0x95,0x4e,0x93,0x5a,0x8d,0x54,
+ 0x8c,0x5b,0x8c,0x4f,0x8e,0x53,0x8e,0x52,0x93,0x5b,0x95,0x4e,0x99,0x59,0x98,0x51,
+ 0x99,0x5a,0x99,0x52,0x98,0x55,0x94,0x4d,0x92,0x5d,0x8d,0x53,0x8b,0x5b,0x88,0x53,
+ 0x8c,0x5c,0x8d,0x52,0x8f,0x5a,0x94,0x50,0x98,0x5f,0x98,0x53,0x9a,0x5c,0x99,0x51,
+ 0x99,0x60,0x95,0x55,0x92,0x5a,0x8d,0x53,0x8b,0x5b,0x8a,0x50,0x8b,0x57,0x8d,0x4f,
+ 0x90,0x5c,0x94,0x53,0x98,0x59,0x97,0x50,0x98,0x5e,0x99,0x51,0x99,0x5a,0x94,0x50,
+ 0x91,0x5b,0x8f,0x51,0x8d,0x56,0x8a,0x51,0x8b,0x5b,0x8d,0x51,0x91,0x57,0x94,0x52,
+ 0x99,0x5d,0x99,0x52,0x9c,0x58,0x9d,0x4e,0x9c,0x5b,0x99,0x4f,0x97,0x5a,0x92,0x51,
+ 0x8f,0x5b,0x8d,0x55,0x8e,0x52,0x90,0x4f,0x92,0x5e,0x95,0x53,0x99,0x56,0x9b,0x51,
+ 0x9e,0x58,0x9c,0x52,0x9c,0x58,0x9a,0x4d,0x97,0x5c,0x92,0x53,0x90,0x5c,0x8c,0x50,
+ 0x8c,0x59,0x8e,0x52,0x92,0x59,0x94,0x53,0x97,0x5a,0x9a,0x54,0x9c,0x58,0x9c,0x4f,
+ 0x9b,0x5c,0x9a,0x54,0x96,0x57,0x92,0x58,0x8f,0x5e,0x8c,0x50,0x8c,0x59,0x8d,0x50,
+ 0x90,0x54,0x95,0x52,0x98,0x55,0x9a,0x52,0x9c,0x5c,0x9c,0x51,0x9c,0x58,0x98,0x51,
+ 0x97,0x57,0x95,0x54,0x8f,0x57,0x8b,0x55,0x8b,0x5c,0x8b,0x4e,0x8e,0x5c,0x93,0x4f,
+ 0x97,0x5b,0x99,0x50,0x9c,0x5a,0x9c,0x50,0x9c,0x59,0x99,0x52,0x97,0x55,0x93,0x54,
+ 0x91,0x5a,0x8d,0x52,0x8c,0x5d,0x8d,0x4d,0x91,0x5c,0x93,0x54,0x98,0x5b,0x9a,0x52,
+ 0x9c,0x59,0x9d,0x51,0x9e,0x59,0x9b,0x4f,0x99,0x5d,0x95,0x51,0x92,0x59,0x8d,0x52,
+ 0x8e,0x58,0x90,0x4f,0x90,0x57,0x94,0x55,0x98,0x59,0x9b,0x56,0x9f,0x57,0x9e,0x52,
+ 0x9e,0x58,0x9e,0x4f,0x9c,0x5a,0x95,0x50,0x91,0x5d,0x8d,0x50,0x8c,0x55,0x8d,0x4b,
+ 0x90,0x5d,0x92,0x52,0x98,0x5f,0x9b,0x55,0x9f,0x5f,0x9e,0x50,0x9f,0x53,0x9e,0x50,
+ 0x9e,0x59,0x9a,0x4e,0x96,0x59,0x92,0x54,0x92,0x5f,0x90,0x50,0x92,0x5c,0x95,0x54,
+ 0x98,0x59,0x9b,0x4f,0x9f,0x4f,0x9e,0x48,0xa1,0x59,0xa0,0x4e,0x9d,0x58,0x9a,0x50,
+ 0x96,0x5a,0x8c,0x55,0x94,0x4d,0x95,0x45,0x97,0x52,0x9c,0x4e,0xa0,0x5b,0xa2,0x50,
+ 0xa5,0x52,0xa6,0x4c,0xa3,0x50,0xa2,0x47,0xa0,0x56,0x9b,0x49,0x98,0x4f,0x96,0x4b,
+ 0x94,0x54,0x96,0x46,0x98,0x53,0x99,0x48,0x9e,0x56,0xa1,0x48,0xa1,0x52,0xa3,0x4a,
+ 0xa5,0x4e,0xa1,0x47,0x9d,0x52,0x9b,0x48,0x97,0x52,0x94,0x48,0x94,0x4f,0x95,0x43,
+ 0x96,0x50,0x9b,0x47,0x9e,0x51,0xa1,0x4a,0xa3,0x54,0xa5,0x45,0xa4,0x51,0xa2,0x47,
+ 0x9f,0x53,0x9d,0x48,0x99,0x4d,0x94,0x48,0x94,0x54,0x94,0x48,0x97,0x4f,0x9a,0x49,
+ 0x9e,0x4d,0xa0,0x49,0xa2,0x50,0xa3,0x4c,0xa3,0x51,0xa3,0x49,0xa0,0x50,0x9e,0x4b,
+ 0x99,0x4f,0x95,0x4b,0x93,0x4f,0x93,0x48,0x94,0x53,0x97,0x48,0x9c,0x4b,0xa0,0x48,
+ 0xa0,0x53,0xa3,0x49,0xa3,0x4d,0xa0,0x50,0xa0,0x55,0x9c,0x4d,0x97,0x50,0x95,0x4c,
+ 0x93,0x4c,0x92,0x4b,0x94,0x52,0x96,0x4c,0x9a,0x56,0xa0,0x48,0xa1,0x4f,0xa2,0x47,
+ 0xa3,0x51,0xa2,0x49,0xa1,0x4e,0x9c,0x4b,0x99,0x55,0x95,0x47,0x93,0x52,0x93,0x4a,
+ 0x93,0x52,0x96,0x4c,0x99,0x53,0x9e,0x46,0x9f,0x53,0xa2,0x4a,0xa3,0x51,0xa1,0x47,
+ 0x9f,0x50,0x9c,0x49,0x9a,0x4d,0x94,0x4b,0x91,0x52,0x91,0x48,0x92,0x52,0x94,0x48,
+ 0x97,0x50,0x9c,0x48,0x9f,0x50,0x9f,0x4b,0xa0,0x54,0xa2,0x47,0x9f,0x4d,0x9a,0x49,
+ 0x99,0x50,0x94,0x4a,0x93,0x4e,0x91,0x47,0x92,0x55,0x94,0x4a,0x97,0x50,0x9a,0x49,
+ 0x9e,0x4f,0xa0,0x4a,0xa1,0x4e,0xa0,0x47,0xa0,0x58,0x9d,0x4a,0x99,0x53,0x95,0x45,
+ 0x92,0x51,0x91,0x48,0x91,0x53,0x93,0x4a,0x95,0x53,0x9a,0x49,0x9e,0x52,0x9f,0x49,
+ 0xa0,0x50,0xa1,0x48,0x9f,0x50,0x9d,0x4b,0x99,0x52,0x95,0x47,0x93,0x51,0x92,0x4c,
+ 0x91,0x51,0x92,0x49,0x96,0x4f,0x99,0x49,0x9c,0x51,0x9e,0x46,0xa1,0x52,0xa0,0x4c,
+ 0x9e,0x53,0x9c,0x4b,0x9a,0x4d,0x95,0x48,0x93,0x55,0x92,0x4e,0x91,0x55,0x93,0x48,
+ 0x95,0x53,0x99,0x4c,0x9c,0x51,0x9e,0x4a,0xa0,0x56,0xa1,0x4c,0x9f,0x4f,0x9d,0x45,
+ 0x9a,0x52,0x94,0x49,0x90,0x50,0x8f,0x4a,0x8f,0x53,0x90,0x48,0x91,0x50,0x96,0x4e,
+ 0x9a,0x54,0x9e,0x48,0x9e,0x50,0x9e,0x4a,0x9e,0x4e,0x9d,0x48,0x9a,0x56,0x95,0x49,
+ 0x91,0x50,0x90,0x47,0x8d,0x52,0x90,0x49,0x93,0x56,0x98,0x4b,0x9a,0x51,0x9d,0x4b,
+ 0x9e,0x51,0x9f,0x4b,0x9f,0x4e,0x9c,0x48,0x99,0x54,0x95,0x4b,0x91,0x52,0x90,0x47,
+ 0x8e,0x50,0x8f,0x48,0x92,0x51,0x95,0x4a,0x99,0x56,0x9c,0x49,0x9f,0x55,0x9f,0x4c,
+ 0x9f,0x50,0x9d,0x49,0x9a,0x53,0x96,0x49,0x92,0x52,0x8f,0x48,0x8e,0x4f,0x8f,0x47,
+ 0x92,0x4f,0x94,0x46,0x97,0x50,0x9a,0x47,0x9c,0x51,0x9f,0x45,0x9e,0x54,0x9c,0x4a,
+ 0x99,0x50,0x97,0x48,0x93,0x4f,0x8f,0x4b,0x8d,0x52,0x8e,0x48,0x90,0x4c,0x93,0x49,
+ 0x97,0x51,0x9a,0x49,0x9b,0x52,0x9c,0x46,0x9e,0x4e,0x9b,0x49,0x98,0x53,0x95,0x4b,
+ 0x90,0x55,0x8d,0x4d,0x8c,0x54,0x8c,0x47,0x8e,0x57,0x91,0x4d,0x95,0x53,0x9a,0x48,
+ 0x9c,0x53,0x9c,0x4c,0x9c,0x52,0x9c,0x46,0x99,0x54,0x96,0x4d,0x92,0x4e,0x8f,0x49,
+ 0x8e,0x4f,0x8e,0x4e,0x8f,0x53,0x91,0x49,0x95,0x50,0x96,0x50,0x9b,0x4f,0x9a,0x4b,
+ 0x9c,0x53,0x9b,0x48,0x9a,0x4f,0x95,0x49,0x92,0x56,0x8d,0x4d,0x8d,0x52,0x8c,0x4a,
+ 0x8d,0x4f,0x90,0x49,0x94,0x4f,0x97,0x4a,0x9b,0x56,0x9c,0x4c,0x9a,0x53,0x9b,0x4b,
+ 0x9b,0x52,0x97,0x4d,0x93,0x51,0x8f,0x49,0x8d,0x54,0x8b,0x4a,0x8b,0x53,0x90,0x4a,
+ 0x94,0x51,0x96,0x49,0x99,0x51,0x9b,0x49,0x9d,0x58,0x9c,0x48,0x99,0x51,0x98,0x49,
+ 0x93,0x50,0x90,0x4b,0x8d,0x52,0x8d,0x4c,0x8c,0x56,0x8f,0x4b,0x93,0x55,0x97,0x47,
+ 0x99,0x52,0x9d,0x4a,0x9c,0x50,0x9b,0x4e,0x99,0x54,0x99,0x4a,0x95,0x4d,0x8f,0x47,
+ 0x8d,0x57,0x8d,0x47,0x8c,0x52,0x8f,0x48,0x94,0x5a,0x96,0x4b,0x99,0x50,0x9b,0x4b,
+ 0x9d,0x55,0x9e,0x4a,0x9b,0x50,0x99,0x49,0x95,0x51,0x92,0x4b,0x8f,0x50,0x8d,0x4b,
+ 0x8e,0x51,0x8f,0x49,0x92,0x52,0x96,0x4c,0x9a,0x4d,0x9c,0x4c,0x9d,0x54,0x9c,0x4b,
+ 0x9d,0x4e,0x9a,0x4a,0x97,0x53,0x91,0x4a,0x8c,0x51,0x8b,0x47,0x8e,0x52,0x8e,0x4c,
+ 0x91,0x53,0x97,0x49,0x9a,0x50,0x9f,0x48,0x9e,0x50,0x9d,0x4a,0x9d,0x53,0x9b,0x47,
+ 0x99,0x51,0x94,0x4b,0x92,0x55,0x8f,0x48,0x8f,0x54,0x91,0x4d,0x92,0x56,0x97,0x4c,
+ 0x9a,0x53,0x9d,0x4a,0x9e,0x50,0x9d,0x48,0x9e,0x53,0x9d,0x4b,0x9a,0x54,0x96,0x4b,
+ 0x93,0x57,0x91,0x4d,0x91,0x4e,0x92,0x49,0x95,0x53,0x98,0x4b,0x9c,0x53,0x9f,0x49,
+ 0xa0,0x55,0xa1,0x4c,0x9f,0x51,0x9f,0x46,0x9c,0x4f,0x97,0x46,0x94,0x54,0x91,0x4a,
+ 0x90,0x55,0x91,0x4a,0x93,0x4d,0x97,0x47,0x9b,0x55,0x9d,0x4a,0xa0,0x4f,0xa0,0x4a,
+ 0x9f,0x56,0x9f,0x4e,0x9b,0x4e,0x97,0x49,0x94,0x51,0x92,0x4d,0x8e,0x55,0x90,0x49,
+ 0x93,0x53,0x98,0x4e,0x9b,0x55,0xa0,0x49,0xa0,0x55,0xa2,0x4a,0xa1,0x50,0x9f,0x49,
+ 0x9c,0x53,0x99,0x4b,0x94,0x53,0x92,0x4d,0x92,0x4f,0x91,0x49,0x93,0x4f,0x97,0x49,
+ 0x9b,0x55,0x9e,0x4d,0xa0,0x4e,0xa0,0x49,0xa1,0x56,0x9e,0x48,0x9d,0x57,0x99,0x48,
+ 0x96,0x56,0x91,0x47,0x93,0x4f,0x93,0x4d,0x93,0x56,0x95,0x4c,0x97,0x53,0x9a,0x4a,
+ 0xa0,0x51,0x9f,0x4d,0x9e,0x4f,0x9d,0x4d,0x9f,0x4e,0x97,0x4a,0x93,0x51,0x92,0x45,
+ 0x92,0x52,0x93,0x4a,0x93,0x52,0x95,0x4b,0x99,0x55,0x9e,0x49,0xa0,0x52,0xa1,0x4d,
+ 0xa1,0x55,0xa0,0x4c,0x9e,0x51,0x9c,0x4b,0x97,0x52,0x94,0x4c,0x93,0x54,0x92,0x4a,
+ 0x93,0x54,0x97,0x4e,0x99,0x4f,0x9c,0x4d,0xa0,0x53,0xa1,0x49,0xa0,0x4b,0xa1,0x40,
+ 0xa0,0x4c,0x9d,0x4a,0x9a,0x4e,0x93,0x49,0x91,0x53,0x8c,0x4e,0x98,0x35,0x9c,0x33,
+ 0xa2,0x38,0xa5,0x3a,0xa6,0x47,0xa7,0x38,0xa8,0x43,0xa7,0x3b,0xa2,0x3b,0x9e,0x35,
+ 0x9c,0x3e,0x98,0x34,0x95,0x3b,0x95,0x33,0x9a,0x3e,0x9d,0x35,0xa1,0x3c,0xa3,0x32,
+ 0xa7,0x40,0xa8,0x35,0xa8,0x3c,0xa6,0x34,0xa5,0x3d,0xa1,0x35,0x9c,0x3a,0x99,0x3a,
+ 0x97,0x39,0x98,0x37,0x9c,0x3a,0x9e,0x34,0xa3,0x3d,0xa7,0x35,0xa9,0x38,0xaa,0x31,
+ 0xac,0x3c,0xab,0x34,0xa7,0x37,0xa3,0x33,0xa0,0x3c,0x9d,0x3b,0x9b,0x3c,0x9c,0x36,
+ 0x9c,0x3f,0x9d,0x37,0xa2,0x38,0xa6,0x31,0xa9,0x3e,0xaa,0x35,0xaa,0x3c,0xa9,0x36,
+ 0xa8,0x3c,0xa4,0x36,0x9e,0x3c,0x9b,0x31,0x9c,0x3c,0x9c,0x35,0x9c,0x3d,0x9d,0x33,
+ 0xa2,0x39,0xa5,0x33,0xa9,0x3a,0xa9,0x35,0xab,0x3a,0xab,0x32,0xa6,0x38,0xa3,0x32,
+ 0xa1,0x3d,0x9e,0x33,0x9a,0x3a,0x9a,0x34,0x9c,0x41,0x9f,0x32,0xa3,0x3d,0xa7,0x32,
+ 0xaa,0x3d,0xab,0x35,0xac,0x3d,0xab,0x35,0xab,0x3f,0xa6,0x33,0xa2,0x3d,0x9e,0x35,
+ 0x9e,0x3d,0x9b,0x37,0x9b,0x3b,0x9e,0x3a,0xa2,0x3e,0xa7,0x36,0xaa,0x3b,0xaa,0x37,
+ 0xac,0x3d,0xaa,0x36,0xa9,0x3b,0xa6,0x36,0xa3,0x3d,0x9c,0x34,0x9a,0x3c,0x99,0x38,
+ 0x9b,0x3f,0x9b,0x33,0xa0,0x3c,0xa4,0x37,0xa7,0x3a,0xaa,0x39,0xab,0x34,0xa9,0x39,
+ 0xa6,0x41,0xa5,0x33,0xa1,0x3b,0x9b,0x33,0x9a,0x3c,0x97,0x34,0x9a,0x3b,0x9a,0x34,
+ 0x9e,0x3c,0xa3,0x34,0xa5,0x3d,0xa8,0x33,0xa9,0x3d,0xaa,0x2f,0xa8,0x3b,0xa6,0x36,
+ 0xa2,0x41,0x9e,0x34,0x9b,0x3a,0x99,0x34,0x98,0x41,0x97,0x35,0x9c,0x3e,0xa3,0x34,
+ 0xa4,0x3b,0xa6,0x33,0xa8,0x3b,0xaa,0x35,0xa7,0x3e,0xa4,0x32,0xa3,0x39,0x9e,0x36,
+ 0x9c,0x3f,0x9a,0x35,0x99,0x39,0x99,0x32,0x9e,0x40,0xa1,0x35,0xa6,0x3c,0xa8,0x36,
+ 0xab,0x3f,0xaa,0x36,0xaa,0x3e,0xa6,0x32,0xa4,0x3c,0xa1,0x33,0x9d,0x39,0x9a,0x37,
+ 0x9a,0x3b,0x9b,0x38,0x9e,0x3b,0xa3,0x36,0xa6,0x3d,0xa8,0x37,0xab,0x3d,0xab,0x33,
+ 0xaa,0x3e,0xa9,0x33,0xa6,0x3d,0xa0,0x32,0x9d,0x42,0x9b,0x30,0x9b,0x3b,0x9b,0x3a,
+ 0x9e,0x3b,0xa0,0x35,0xa5,0x3b,0xa8,0x36,0xab,0x43,0xab,0x33,0xab,0x3c,0xa8,0x31,
+ 0xa4,0x3f,0xa0,0x37,0x9e,0x3a,0x9a,0x31,0x98,0x40,0x99,0x34,0x9d,0x3e,0xa0,0x35,
+ 0xa5,0x3d,0xa8,0x37,0xab,0x3c,0xaa,0x36,0xab,0x3c,0xab,0x34,0xa8,0x3c,0xa2,0x33,
+ 0xa0,0x3d,0x9a,0x34,0x9a,0x3e,0x9b,0x34,0x9e,0x3d,0x9f,0x33,0xa6,0x3c,0xa7,0x33,
+ 0xab,0x3b,0xab,0x33,0xac,0x3d,0xa8,0x3a,0xa5,0x3a,0xa2,0x35,0x9d,0x39,0x9a,0x34,
+ 0x98,0x39,0x99,0x30,0x9b,0x3a,0x9e,0x32,0xa2,0x40,0xa6,0x36,0xa7,0x3d,0xaa,0x38,
+ 0xac,0x3c,0xa9,0x35,0xa6,0x3b,0xa2,0x37,0x9f,0x3f,0x9a,0x32,0x9a,0x3a,0x98,0x34,
+ 0x9b,0x39,0x9e,0x34,0xa2,0x3a,0xa5,0x34,0xa9,0x3f,0xa9,0x33,0xaa,0x3d,0xa8,0x30,
+ 0xa8,0x3b,0xa4,0x36,0xa0,0x3b,0x9c,0x36,0x9a,0x3e,0x99,0x34,0x9b,0x40,0x9d,0x35,
+ 0xa1,0x42,0xa5,0x35,0xa8,0x37,0xa8,0x35,0xac,0x3e,0xa9,0x35,0xa7,0x3d,0xa4,0x3a,
+ 0xa2,0x3b,0x9a,0x39,0x9a,0x3b,0x98,0x34,0x9a,0x3e,0x9c,0x38,0x9f,0x40,0xa4,0x35,
+ 0xa8,0x3e,0xa8,0x37,0xa9,0x3a,0xa9,0x34,0xa8,0x44,0xa4,0x39,0xa0,0x3b,0x9c,0x37,
+ 0x9a,0x3f,0x98,0x34,0x9a,0x3e,0x9a,0x37,0x9e,0x3f,0xa3,0x38,0xa6,0x3f,0xa8,0x38,
+ 0xa9,0x3c,0xa9,0x35,0xa7,0x3b,0xa3,0x35,0x9f,0x41,0x9c,0x3a,0x99,0x3f,0x98,0x37,
+ 0x99,0x42,0x9a,0x32,0x9f,0x3d,0xa2,0x36,0xa8,0x3f,0xa5,0x37,0xa7,0x3c,0xa8,0x36,
+ 0xa7,0x3e,0xa0,0x37,0x9e,0x3d,0x9c,0x33,0x97,0x40,0x95,0x38,0x96,0x40,0x9b,0x36,
+ 0x9d,0x3f,0x9f,0x36,0xa4,0x3e,0xa5,0x33,0xa9,0x3f,0xa8,0x3a,0xa5,0x3e,0xa2,0x35,
+ 0xa0,0x3f,0x99,0x35,0x99,0x3a,0x96,0x37,0x96,0x3c,0x97,0x34,0x9b,0x3c,0x9f,0x32,
+ 0xa4,0x3d,0xa6,0x39,0xa7,0x3d,0xa7,0x38,0xa7,0x3f,0xa4,0x3a,0xa0,0x38,0x9d,0x36,
+ 0x9b,0x3f,0x98,0x38,0x97,0x3d,0x98,0x34,0x9b,0x3e,0x9e,0x36,0xa2,0x3c,0xa4,0x37,
+ 0xa7,0x3d,0xa7,0x3a,0xa6,0x3a,0xa3,0x37,0xa2,0x40,0x9d,0x37,0x99,0x3d,0x97,0x37,
+ 0x99,0x3e,0x98,0x36,0x9c,0x39,0x9e,0x36,0xa4,0x3f,0xa6,0x38,0xa8,0x3f,0xa8,0x33,
+ 0xa9,0x3f,0xa5,0x34,0xa3,0x3e,0xa0,0x36,0x9c,0x3c,0x99,0x35,0x97,0x3d,0x98,0x32,
+ 0x9b,0x40,0x9e,0x31,0xa1,0x3b,0xa3,0x33,0xa7,0x41,0xa8,0x37,0xa5,0x41,0xa5,0x35,
+ 0xa4,0x3c,0x9e,0x38,0x9c,0x3c,0x99,0x35,0x97,0x40,0x98,0x37,0x99,0x42,0x9e,0x35,
+ 0xa2,0x3f,0xa6,0x35,0xa9,0x3f,0xa9,0x37,0xa9,0x41,0xa6,0x37,0xa5,0x3e,0xa1,0x37,
+ 0x9f,0x3e,0x9b,0x37,0x98,0x3b,0x98,0x32,0x9c,0x3f,0x9d,0x34,0xa3,0x3b,0xa5,0x36,
+ 0xa8,0x3c,0xa8,0x31,0xa8,0x40,0xa7,0x2e,0xa5,0x40,0xa1,0x34,0x9e,0x3f,0x9b,0x35,
+ 0x9a,0x3f,0x99,0x33,0x99,0x41,0x9d,0x38,0xa1,0x40,0xa5,0x39,0xa9,0x3d,0xaa,0x35,
+ 0xaa,0x3d,0xa7,0x34,0xa6,0x3f,0xa2,0x39,0x9e,0x3f,0x9a,0x36,0x99,0x3c,0x98,0x35,
+ 0x9b,0x3f,0x9e,0x31,0xa2,0x3c,0xa6,0x34,0xa8,0x3b,0xa8,0x38,0xa9,0x3d,0xa9,0x36,
+ 0xa6,0x40,0xa2,0x3b,0x9f,0x3e,0x9b,0x34,0x98,0x3b,0x98,0x36,0x98,0x41,0x9b,0x32,
+ 0xa1,0x3d,0xa4,0x34,0xa5,0x3c,0xa6,0x33,0xa8,0x41,0xa7,0x37,0xa4,0x39,0xa2,0x35,
+ 0xa1,0x3d,0x9b,0x35,0x9a,0x41,0x99,0x32,0x97,0x3e,0x99,0x35,0xa1,0x40,0xa3,0x35,
+ 0xa3,0x3a,0xa7,0x39,0xaa,0x3a,0xa7,0x37,0xa7,0x3e,0xa3,0x36,0xa1,0x3f,0x9e,0x36,
+ 0x9c,0x3e,0x9a,0x36,0x9c,0x3e,0x9d,0x35,0xa1,0x3f,0xa4,0x32,0xa8,0x3f,0xa7,0x3c,
+ 0xa9,0x45,0xa9,0x35,0xa7,0x31,0xa5,0x2c,0xa3,0x38,0x9e,0x33,0x9b,0x3c,0x99,0x36,
+ 0x98,0x42,0x93,0x3b,0xa3,0x4d,0xa8,0x3f,0xad,0x4d,0xad,0x4c,0xad,0x57,0xac,0x51,
+ 0xaa,0x51,0xa6,0x4b,0xa3,0x4d,0xa0,0x48,0x9d,0x4f,0x9c,0x45,0xa0,0x4a,0xa3,0x48,
+ 0xa7,0x4b,0xaa,0x43,0xac,0x52,0xae,0x40,0xb0,0x48,0xae,0x46,0xad,0x4b,0xa9,0x48,
+ 0xa5,0x52,0xa1,0x46,0x9f,0x4f,0x9e,0x46,0x9f,0x50,0xa2,0x4a,0xa7,0x4b,0xab,0x46,
+ 0xae,0x4b,0xb0,0x44,0xb1,0x4e,0xae,0x4a,0xae,0x4e,0xaa,0x47,0xa7,0x4c,0xa3,0x45,
+ 0xa1,0x50,0x9f,0x42,0xa1,0x52,0xa2,0x42,0xa4,0x50,0xaa,0x45,0xab,0x4c,0xae,0x46,
+ 0xac,0x52,0xae,0x47,0xab,0x4b,0xa9,0x41,0xa7,0x4d,0xa2,0x46,0x9f,0x50,0x9d,0x46,
+ 0xa1,0x4e,0x9f,0x44,0xa3,0x4d,0xa8,0x47,0xab,0x50,0xae,0x44,0xb2,0x4d,0xb0,0x46,
+ 0xac,0x4d,0xa8,0x45,0xa8,0x4d,0xa0,0x45,0x9c,0x4f,0x9c,0x47,0xa0,0x47,0xa0,0x44,
+ 0xa5,0x4c,0xaa,0x48,0xad,0x51,0xaf,0x4a,0xb0,0x4f,0xb0,0x45,0xaf,0x4c,0xac,0x45,
+ 0xa8,0x51,0xa4,0x45,0xa3,0x49,0x9e,0x45,0xa0,0x4e,0xa1,0x42,0xa5,0x4e,0xa8,0x45,
+ 0xaa,0x50,0xae,0x46,0xb0,0x4c,0xad,0x44,0xae,0x51,0xae,0x48,0xa9,0x4c,0xa5,0x46,
+ 0xa4,0x4b,0xa1,0x41,0xa2,0x4e,0xa1,0x45,0xa6,0x51,0xab,0x47,0xae,0x4d,0xaf,0x43,
+ 0xb4,0x4c,0xb4,0x46,0xb3,0x4a,0xb1,0x46,0xab,0x4b,0xa7,0x47,0xa5,0x4e,0xa2,0x48,
+ 0x9e,0x4e,0xa0,0x48,0xa5,0x4c,0xa7,0x48,0xa9,0x50,0xb1,0x49,0xb3,0x4f,0xb2,0x45,
+ 0xb4,0x4b,0xb0,0x48,0xae,0x47,0xa9,0x42,0xa6,0x4f,0xa5,0x44,0xa5,0x50,0xa4,0x48,
+ 0xa6,0x50,0xab,0x44,0xae,0x4d,0xb2,0x46,0xb3,0x4e,0xb2,0x44,0xb4,0x4b,0xb2,0x44,
+ 0xb0,0x4e,0xaa,0x45,0xa8,0x4e,0xa6,0x42,0xa4,0x51,0xa3,0x48,0xa8,0x4d,0xaa,0x4b,
+ 0xae,0x4d,0xaf,0x48,0xb2,0x4f,0xb2,0x46,0xb1,0x50,0xaf,0x48,0xaf,0x4d,0xaa,0x47,
+ 0xa7,0x4b,0xa6,0x45,0xa3,0x51,0xa3,0x42,0xa6,0x54,0xa8,0x49,0xad,0x51,0xb1,0x43,
+ 0xb3,0x50,0xb4,0x4a,0xb5,0x4f,0xb4,0x47,0xb0,0x50,0xac,0x48,0xa9,0x4f,0xa4,0x49,
+ 0xa4,0x51,0xa1,0x47,0xa4,0x4a,0xa8,0x47,0xaf,0x4f,0xaf,0x47,0xb3,0x4d,0xb3,0x49,
+ 0xb5,0x4b,0xb2,0x47,0xae,0x4a,0xaa,0x47,0xa7,0x4f,0xa2,0x45,0xa0,0x4b,0xa0,0x48,
+ 0xa4,0x4e,0xa7,0x44,0xac,0x4b,0xaf,0x49,0xb4,0x4d,0xb5,0x47,0xb6,0x4d,0xb3,0x44,
+ 0xaf,0x4e,0xad,0x48,0xa9,0x4b,0xa4,0x45,0xa3,0x51,0xa3,0x45,0xa5,0x4d,0xa8,0x46,
+ 0xab,0x4f,0xaf,0x44,0xb4,0x4c,0xb3,0x47,0xb4,0x50,0xb4,0x45,0xb4,0x4d,0xae,0x43,
+ 0xa9,0x52,0xa8,0x43,0xa7,0x4a,0xa1,0x44,0xa5,0x4d,0xa9,0x46,0xac,0x4c,0xae,0x45,
+ 0xb4,0x4e,0xb5,0x49,0xb5,0x4b,0xb4,0x42,0xb3,0x50,0xaf,0x48,0xac,0x4d,0xa8,0x46,
+ 0xa4,0x50,0xa4,0x46,0xa4,0x4e,0xa5,0x47,0xa9,0x4b,0xac,0x48,0xb1,0x4c,0xb3,0x48,
+ 0xb4,0x52,0xb4,0x44,0xb3,0x4b,0xaf,0x42,0xac,0x4c,0xa8,0x48,0xa3,0x4b,0xa1,0x47,
+ 0xa3,0x4b,0xa4,0x43,0xa8,0x4c,0xac,0x48,0xb1,0x51,0xb2,0x49,0xb3,0x4a,0xb4,0x4b,
+ 0xb3,0x4f,0xac,0x46,0xab,0x4e,0xa8,0x46,0xa3,0x51,0xa0,0x47,0xa2,0x4d,0xa6,0x49,
+ 0xa9,0x4e,0xad,0x4d,0xb2,0x4c,0xb2,0x4a,0xb3,0x50,0xb4,0x4a,0xb3,0x48,0xaf,0x47,
+ 0xad,0x4d,0xa8,0x47,0xa6,0x4d,0xa2,0x46,0xa2,0x51,0xa5,0x46,0xa7,0x4d,0xab,0x42,
+ 0xaf,0x50,0xb1,0x47,0xb3,0x51,0xb2,0x42,0xb3,0x53,0xb0,0x4b,0xad,0x52,0xa9,0x46,
+ 0xa6,0x4f,0xa6,0x4a,0xa6,0x4f,0xa6,0x45,0xa8,0x4b,0xac,0x45,0xb0,0x4d,0xaf,0x47,
+ 0xb3,0x4f,0xb2,0x49,0xaf,0x4e,0xac,0x45,0xac,0x4e,0xa8,0x4a,0xa4,0x4c,0xa3,0x48,
+ 0xa1,0x4e,0xa1,0x44,0xa5,0x51,0xa9,0x44,0xab,0x4f,0xac,0x43,0xb2,0x52,0xb1,0x46,
+ 0xb0,0x4c,0xaf,0x46,0xac,0x4e,0xa7,0x45,0xa4,0x54,0xa2,0x47,0xa0,0x4c,0x9f,0x45,
+ 0xa4,0x53,0xa8,0x49,0xac,0x4f,0xad,0x46,0xb0,0x50,0xb0,0x47,0xaf,0x4b,0xab,0x43,
+ 0xab,0x4e,0xa6,0x47,0xa5,0x4d,0xa2,0x46,0xa1,0x52,0xa2,0x4c,0xa4,0x4d,0xa8,0x43,
+ 0xac,0x51,0xae,0x46,0xb1,0x4b,0xb3,0x46,0xb2,0x52,0xb1,0x47,0xaf,0x4b,0xa9,0x45,
+ 0xa7,0x4d,0xa2,0x49,0xa0,0x4a,0xa2,0x48,0xa2,0x50,0xa3,0x43,0xaa,0x4d,0xac,0x47,
+ 0xaf,0x4b,0xaf,0x4d,0xb0,0x4c,0xaf,0x43,0xad,0x53,0xaa,0x49,0xa6,0x4f,0xa2,0x47,
+ 0xa2,0x53,0xa3,0x46,0xa4,0x4c,0xa5,0x46,0xaa,0x55,0xae,0x48,0xb1,0x4e,0xb0,0x42,
+ 0xb1,0x4d,0xb0,0x49,0xae,0x4f,0xa9,0x46,0xa5,0x54,0xa2,0x47,0xa1,0x4b,0xa0,0x49,
+ 0xa1,0x4e,0xa5,0x48,0xaa,0x4e,0xad,0x45,0xb0,0x52,0xb3,0x49,0xb3,0x4d,0xb1,0x4a,
+ 0xb0,0x4c,0xae,0x47,0xa7,0x4e,0xa3,0x47,0xa3,0x50,0xa3,0x45,0xa4,0x4f,0xa5,0x45,
+ 0xaa,0x4b,0xad,0x48,0xb0,0x4b,0xb1,0x48,0xb1,0x55,0xb0,0x48,0xaf,0x4f,0xac,0x43,
+ 0xa9,0x53,0xa5,0x48,0xa3,0x4b,0xa1,0x45,0x9b,0x54,0x9d,0x46,0xa8,0x4d,0xaa,0x45,
+ 0xaa,0x50,0xae,0x45,0xb4,0x4d,0xb2,0x43,0xb0,0x4e,0xaf,0x44,0xaa,0x4b,0xa7,0x46,
+ 0xa7,0x4d,0xa3,0x48,0x9a,0x4d,0x9d,0x43,0xab,0x52,0xa4,0x43,0xa2,0x52,0xb1,0x47,
+ 0xb5,0x4f,0xae,0x48,0xb0,0x4d,0xae,0x43,0xaa,0x55,0xa6,0x42,0xa3,0x50,0xa4,0x48,
+ 0xa4,0x4e,0xa6,0x48,0xa8,0x4b,0xab,0x46,0xae,0x52,0xb2,0x49,0xb4,0x4d,0xb1,0x46,
+ 0xb1,0x4b,0xaf,0x49,0xaa,0x48,0xa5,0x45,0xa3,0x4d,0xa3,0x46,0xa1,0x50,0xa2,0x49,
+ 0xa6,0x4b,0xa9,0x44,0xac,0x4b,0xaf,0x49,0xb1,0x50,0xb0,0x45,0xaf,0x4f,0xac,0x46,
+ 0xaa,0x4f,0xa7,0x49,0xa2,0x4d,0xa0,0x46,0xa2,0x53,0xa3,0x46,0xa7,0x49,0xa9,0x42,
+ 0xad,0x4d,0xb0,0x45,0xb0,0x51,0xaf,0x49,0xaf,0x53,0xad,0x3f,0xa8,0x45,0xa5,0x3f,
+ 0xa3,0x4f,0x9f,0x43,0xa0,0x4e,0xa1,0x47,0xa3,0x50,0x9f,0x4b,0xb1,0x1e,0xb3,0x10,
+ 0xb6,0x1a,0xb5,0x15,0xb4,0x28,0xaf,0x1a,0xad,0x1e,0xa7,0x12,0xa4,0x1d,0xa2,0x17,
+ 0xa4,0x1e,0xa4,0x14,0xa5,0x1b,0xaa,0x15,0xb0,0x1c,0xaf,0x14,0xb1,0x1f,0xb3,0x17,
+ 0xb2,0x1d,0xad,0x14,0xaa,0x1b,0xa6,0x12,0xa3,0x20,0xa0,0x12,0xa1,0x20,0xa3,0x17,
+ 0xa8,0x17,0xab,0x12,0xaf,0x1d,0xb0,0x16,0xb3,0x21,0xb3,0x16,0xb2,0x18,0xaf,0x16,
+ 0xab,0x1f,0xa5,0x11,0xa0,0x1a,0x9e,0x12,0x9f,0x1d,0xa0,0x17,0xa2,0x1a,0xa7,0x16,
+ 0xad,0x19,0xaf,0x13,0xb0,0x1b,0xb0,0x16,0xb0,0x1e,0xad,0x13,0xa9,0x1b,0xa4,0x15,
+ 0xa1,0x19,0x9f,0x13,0x9f,0x1b,0xa1,0x12,0xa4,0x1e,0xa4,0x13,0xaa,0x19,0xac,0x14,
+ 0xad,0x1a,0xad,0x13,0xad,0x1e,0xa7,0x17,0xa5,0x1d,0xa1,0x1a,0x9c,0x1c,0x99,0x19,
+ 0x97,0x1a,0x9a,0x11,0xa0,0x17,0x9f,0x10,0xa4,0x1e,0xa9,0x13,0xad,0x1a,0xae,0x17,
+ 0xac,0x1b,0xab,0x16,0xa8,0x1f,0xa3,0x13,0xa1,0x1b,0x9d,0x18,0x9e,0x1d,0x9e,0x12,
+ 0xa0,0x1d,0xa1,0x14,0xa9,0x1d,0xa9,0x12,0xab,0x20,0xad,0x12,0xae,0x1c,0xa8,0x13,
+ 0xa8,0x1a,0xa5,0x13,0xa0,0x1d,0x9b,0x13,0x9d,0x1f,0x9b,0x16,0x9e,0x1d,0xa1,0x14,
+ 0xa5,0x1c,0xa8,0x11,0xab,0x1c,0xac,0x15,0xab,0x20,0xa9,0x16,0xa7,0x19,0xa3,0x16,
+ 0x9f,0x16,0x9b,0x12,0x99,0x19,0x9a,0x16,0x9d,0x1d,0x9e,0x12,0xa3,0x1b,0xa6,0x15,
+ 0xaa,0x1d,0xab,0x14,0xab,0x1c,0xaa,0x12,0xa8,0x1b,0xa5,0x13,0xa2,0x1a,0x9e,0x11,
+ 0x9b,0x1a,0x9b,0x14,0x9b,0x1b,0x9d,0x11,0xa3,0x1a,0xa7,0x18,0xa9,0x1d,0xaa,0x17,
+ 0xac,0x1c,0xa9,0x11,0xa8,0x1e,0xa3,0x15,0x9f,0x21,0x9c,0x13,0x9a,0x21,0x99,0x13,
+ 0x9d,0x1d,0x9d,0x16,0xa1,0x1f,0xa4,0x11,0xa9,0x1e,0xaa,0x15,0xab,0x1b,0xab,0x17,
+ 0xa7,0x16,0xa6,0x14,0xa3,0x1e,0x9e,0x16,0x9c,0x1e,0x9b,0x13,0x9c,0x1a,0x9c,0x14,
+ 0xa0,0x1f,0xa3,0x11,0xa6,0x1c,0xa5,0x10,0xa7,0x20,0xa9,0x18,0xa6,0x17,0xa4,0x15,
+ 0xa2,0x1a,0x9f,0x19,0x9d,0x1d,0x9b,0x16,0x9d,0x1f,0x9b,0x14,0xa0,0x1b,0xa1,0x10,
+ 0xa5,0x15,0xa4,0x18,0xaa,0x1e,0xa5,0x16,0xa6,0x1e,0xa4,0x0e,0xa2,0x1c,0x9c,0x11,
+ 0x9a,0x20,0x9a,0x19,0x9a,0x1d,0x9d,0x13,0xa0,0x20,0xa3,0x13,0xa8,0x1c,0xa9,0x18,
+ 0xab,0x1a,0xa8,0x12,0xa9,0x1a,0xa6,0x11,0xa2,0x20,0x9d,0x12,0x9a,0x1d,0x97,0x11,
+ 0x9a,0x1e,0x9b,0x14,0x9e,0x1d,0xa1,0x15,0xa5,0x1d,0xa7,0x15,0xaa,0x1c,0xa9,0x14,
+ 0xa8,0x19,0xa5,0x17,0xa2,0x1b,0x9e,0x14,0x9d,0x1d,0x9a,0x13,0x99,0x1d,0x9b,0x18,
+ 0x9f,0x1e,0xa1,0x18,0xa5,0x1d,0xa7,0x14,0xaa,0x1f,0xa7,0x16,0xa9,0x1c,0xa5,0x13,
+ 0xa2,0x1b,0x9f,0x13,0x9c,0x1b,0x9a,0x17,0x9a,0x1e,0x99,0x10,0x9d,0x1c,0x9d,0x13,
+ 0xa2,0x1e,0xa4,0x12,0xa8,0x17,0xa6,0x13,0xa5,0x24,0xa6,0x13,0xa2,0x1e,0x9d,0x13,
+ 0x9b,0x17,0x99,0x18,0x99,0x17,0x9b,0x18,0x9b,0x21,0x9f,0x14,0xa4,0x1e,0xa6,0x15,
+ 0xa9,0x1c,0xa9,0x16,0xa8,0x1d,0xa5,0x11,0xa2,0x22,0xa0,0x16,0x9c,0x1e,0x99,0x14,
+ 0x9b,0x1f,0x99,0x12,0x9a,0x1f,0x9f,0x1a,0xa1,0x1a,0xa2,0x12,0xa6,0x1c,0xa6,0x15,
+ 0xa6,0x20,0xa3,0x14,0xa1,0x1d,0x9d,0x10,0x9a,0x22,0x97,0x15,0x96,0x1d,0x95,0x14,
+ 0x99,0x1f,0x9c,0x15,0xa2,0x1e,0xa3,0x18,0xa5,0x20,0xa7,0x19,0xa6,0x1b,0xa3,0x15,
+ 0xa4,0x1e,0x9e,0x16,0x9b,0x19,0x97,0x13,0x97,0x24,0x96,0x15,0x98,0x1e,0x9a,0x17,
+ 0x9f,0x1b,0xa3,0x17,0xa5,0x1b,0xa4,0x14,0xa6,0x1d,0xa5,0x19,0xa2,0x18,0x9d,0x13,
+ 0x9a,0x1f,0x97,0x16,0x96,0x1d,0x94,0x16,0x95,0x23,0x9a,0x14,0x9d,0x1b,0x9f,0x17,
+ 0xa2,0x22,0xa4,0x17,0xa4,0x1f,0xa4,0x1b,0xa3,0x20,0x9e,0x13,0x99,0x20,0x98,0x16,
+ 0x97,0x1f,0x95,0x15,0x94,0x20,0x98,0x18,0x9d,0x21,0xa0,0x15,0xa2,0x21,0xa5,0x1a,
+ 0xa6,0x1e,0xa3,0x17,0xa1,0x1c,0x9e,0x16,0x9b,0x1c,0x98,0x18,0x96,0x1e,0x95,0x13,
+ 0x96,0x1c,0x97,0x15,0x9c,0x1b,0x9e,0x16,0x9f,0x21,0xa0,0x17,0xa3,0x1d,0xa3,0x15,
+ 0xa0,0x1e,0x9e,0x18,0x9c,0x1e,0x97,0x18,0x96,0x21,0x94,0x16,0x97,0x1e,0x9a,0x15,
+ 0x9c,0x1b,0x9f,0x16,0xa1,0x1e,0xa2,0x14,0xa4,0x1f,0xa4,0x17,0xa2,0x1e,0xa0,0x17,
+ 0x9c,0x20,0x99,0x19,0x96,0x1e,0x94,0x14,0x94,0x21,0x94,0x15,0x99,0x1f,0x9c,0x16,
+ 0x9f,0x1e,0xa0,0x19,0xa2,0x1d,0xa2,0x15,0xa0,0x22,0x9e,0x15,0x9b,0x1c,0x96,0x16,
+ 0x94,0x22,0x92,0x18,0x93,0x1c,0x94,0x12,0x97,0x20,0x9c,0x1a,0xa1,0x1e,0xa1,0x18,
+ 0xa4,0x19,0xa3,0x1a,0xa2,0x20,0x9e,0x19,0x9b,0x25,0x97,0x14,0x96,0x1c,0x94,0x17,
+ 0x95,0x1c,0x95,0x14,0x98,0x1f,0x9b,0x19,0x9f,0x1e,0xa0,0x16,0xa2,0x1d,0xa2,0x19,
+ 0xa0,0x1d,0x9c,0x19,0x9a,0x20,0x96,0x19,0x93,0x20,0x91,0x12,0x93,0x1f,0x92,0x12,
+ 0x95,0x20,0x99,0x1c,0x9c,0x1d,0x9f,0x14,0xa0,0x23,0xa1,0x17,0x9f,0x20,0x9d,0x1c,
+ 0x9a,0x22,0x97,0x18,0x94,0x1c,0x92,0x16,0x91,0x23,0x93,0x15,0x97,0x1d,0x97,0x13,
+ 0x9b,0x20,0x9f,0x14,0xa0,0x1d,0x9f,0x17,0x9f,0x21,0x9e,0x18,0x9b,0x1a,0x98,0x1a,
+ 0x95,0x21,0x93,0x17,0x92,0x1c,0x92,0x18,0x93,0x23,0x96,0x1b,0x9a,0x1f,0x9a,0x16,
+ 0x9e,0x23,0x9f,0x18,0x9d,0x1e,0x9b,0x18,0x9b,0x1f,0x97,0x17,0x94,0x1f,0x90,0x19,
+ 0x91,0x1d,0x91,0x17,0x93,0x1b,0x96,0x17,0x9b,0x1f,0x9d,0x15,0x9f,0x1d,0xa0,0x17,
+ 0x9f,0x21,0x9c,0x15,0x9d,0x1f,0x97,0x16,0x95,0x1f,0x93,0x16,0x92,0x1b,0x92,0x15,
+ 0x94,0x1b,0x96,0x15,0x9a,0x1a,0x9d,0x12,0xa0,0x21,0x9f,0x12,0x9f,0x24,0x9e,0x19,
+ 0x9b,0x24,0x97,0x17,0x95,0x15,0x93,0x0f,0x92,0x1e,0x91,0x16,0x93,0x1d,0x95,0x18,
+ 0x99,0x20,0x96,0x1c,0xa2,0x60,0xa3,0x60,0xa4,0x6c,0xa1,0x6a,0x9e,0x77,0x9a,0x6e,
+ 0x98,0x74,0x94,0x66,0x95,0x6a,0x95,0x62,0x98,0x6e,0x9c,0x5f,0x9f,0x6e,0xa0,0x61,
+ 0xa3,0x6e,0xa3,0x62,0xa2,0x6b,0x9f,0x5c,0x9d,0x6e,0x9a,0x65,0x96,0x66,0x93,0x63,
+ 0x96,0x6b,0x95,0x64,0x98,0x6a,0x9a,0x63,0x9f,0x6e,0xa1,0x60,0xa2,0x67,0xa3,0x61,
+ 0xa5,0x68,0xa2,0x64,0x9e,0x6a,0x9b,0x62,0x97,0x6a,0x95,0x62,0x94,0x6c,0x93,0x60,
+ 0x96,0x67,0x99,0x61,0x9c,0x68,0x9e,0x64,0xa4,0x6b,0xa4,0x61,0xa2,0x66,0xa2,0x64,
+ 0xa0,0x69,0x9b,0x64,0x98,0x6c,0x95,0x68,0x95,0x71,0x94,0x65,0x96,0x6c,0x98,0x61,
+ 0x9d,0x6c,0x9e,0x64,0xa1,0x66,0xa1,0x62,0xa2,0x6d,0xa1,0x61,0x9e,0x6c,0x9a,0x60,
+ 0x97,0x6d,0x92,0x64,0x91,0x67,0x92,0x65,0x94,0x6d,0x96,0x61,0x9a,0x69,0x9d,0x62,
+ 0xa2,0x68,0xa0,0x61,0xa0,0x6a,0x9f,0x63,0x9f,0x6f,0x9a,0x5d,0x98,0x66,0x94,0x60,
+ 0x93,0x70,0x94,0x63,0x95,0x67,0x97,0x62,0x9c,0x6b,0x9f,0x5e,0xa0,0x6c,0xa1,0x63,
+ 0xa1,0x71,0x9f,0x61,0x9d,0x6b,0x99,0x61,0x97,0x6c,0x95,0x63,0x94,0x6b,0x92,0x62,
+ 0x96,0x6b,0x97,0x61,0x9b,0x6a,0x9d,0x5f,0xa1,0x6c,0xa0,0x63,0xa0,0x69,0x9f,0x5d,
+ 0x9e,0x6d,0x9b,0x63,0x98,0x69,0x94,0x64,0x92,0x6a,0x91,0x5d,0x93,0x6d,0x93,0x61,
+ 0x96,0x6a,0x9b,0x63,0x9b,0x65,0x93,0x62,0x97,0x6b,0x9c,0x62,0x91,0x67,0x8b,0x5e,
+ 0x93,0x6d,0x92,0x63,0x8e,0x6d,0x91,0x64,0x94,0x6f,0x94,0x63,0x97,0x68,0x9a,0x60,
+ 0x9e,0x69,0x9f,0x63,0x9f,0x68,0x9e,0x62,0x9e,0x6e,0x99,0x61,0x96,0x67,0x93,0x5f,
+ 0x90,0x66,0x8f,0x5e,0x90,0x6c,0x90,0x61,0x95,0x6c,0x97,0x64,0x9a,0x6a,0x9b,0x66,
+ 0x9d,0x6a,0x9c,0x62,0x9b,0x6c,0x98,0x62,0x96,0x6d,0x91,0x64,0x8e,0x69,0x8b,0x62,
+ 0x8e,0x6a,0x8f,0x62,0x91,0x65,0x95,0x62,0x9a,0x70,0x9b,0x61,0x9d,0x6a,0x9c,0x64,
+ 0x9c,0x69,0x98,0x62,0x96,0x6b,0x93,0x66,0x8f,0x6a,0x8d,0x64,0x8e,0x6a,0x8f,0x63,
+ 0x93,0x6b,0x96,0x62,0x99,0x6c,0x9b,0x65,0x9e,0x6e,0x9b,0x62,0x9c,0x6a,0x99,0x62,
+ 0x97,0x68,0x93,0x65,0x91,0x6a,0x8f,0x61,0x8f,0x6f,0x8f,0x61,0x93,0x6f,0x95,0x63,
+ 0x99,0x70,0x9b,0x60,0x9d,0x68,0x9d,0x60,0x9e,0x6c,0x99,0x61,0x97,0x6c,0x94,0x62,
+ 0x91,0x6c,0x8e,0x68,0x8e,0x67,0x8f,0x61,0x93,0x6e,0x95,0x63,0x98,0x6b,0x99,0x65,
+ 0x9e,0x6a,0x9c,0x62,0x9c,0x6b,0x9b,0x66,0x98,0x73,0x95,0x5f,0x92,0x6e,0x8e,0x62,
+ 0x8f,0x6b,0x90,0x63,0x91,0x6c,0x94,0x68,0x97,0x6a,0x9a,0x68,0x9d,0x6b,0x9b,0x61,
+ 0x9b,0x6e,0x98,0x62,0x96,0x65,0x91,0x66,0x91,0x6e,0x8e,0x63,0x8f,0x6b,0x8e,0x5f,
+ 0x91,0x6d,0x92,0x60,0x96,0x6c,0x99,0x60,0x9b,0x6d,0x9c,0x62,0x9c,0x69,0x99,0x63,
+ 0x99,0x6c,0x94,0x61,0x92,0x67,0x8f,0x63,0x8b,0x68,0x85,0x61,0x8b,0x67,0x94,0x66,
+ 0x8f,0x6f,0x92,0x68,0x9b,0x68,0x9c,0x61,0x9c,0x6e,0x9a,0x67,0x98,0x6b,0x94,0x65,
+ 0x90,0x68,0x8e,0x65,0x8d,0x68,0x8c,0x62,0x8f,0x6d,0x91,0x60,0x94,0x67,0x97,0x67,
+ 0x9b,0x6d,0x9a,0x66,0x9a,0x68,0x99,0x62,0x96,0x70,0x94,0x68,0x91,0x6c,0x8d,0x62,
+ 0x8c,0x6b,0x8b,0x65,0x8d,0x6a,0x8d,0x62,0x92,0x70,0x95,0x66,0x96,0x68,0x98,0x5e,
+ 0x99,0x6e,0x97,0x66,0x96,0x6c,0x92,0x64,0x8f,0x6d,0x8a,0x64,0x89,0x6a,0x89,0x62,
+ 0x8c,0x6a,0x8c,0x66,0x90,0x69,0x95,0x66,0x96,0x68,0x98,0x63,0x98,0x6d,0x97,0x63,
+ 0x95,0x70,0x92,0x64,0x90,0x6c,0x8e,0x66,0x8d,0x6f,0x8b,0x64,0x8f,0x6c,0x8e,0x62,
+ 0x93,0x69,0x96,0x61,0x9a,0x6f,0x9a,0x66,0x9b,0x70,0x9a,0x65,0x99,0x68,0x95,0x63,
+ 0x94,0x68,0x8f,0x63,0x8d,0x6c,0x8d,0x63,0x8f,0x70,0x90,0x68,0x92,0x68,0x97,0x64,
+ 0x98,0x70,0x9a,0x66,0x9c,0x6b,0x9a,0x63,0x9a,0x71,0x97,0x5f,0x93,0x71,0x8f,0x63,
+ 0x8e,0x6b,0x8d,0x63,0x8c,0x6d,0x8f,0x68,0x90,0x70,0x95,0x64,0x98,0x70,0x9b,0x5f,
+ 0x9b,0x6c,0x9b,0x65,0x99,0x6a,0x96,0x63,0x94,0x6f,0x90,0x64,0x8d,0x6b,0x8c,0x63,
+ 0x8d,0x6c,0x8d,0x62,0x90,0x66,0x93,0x66,0x98,0x70,0x9a,0x64,0x9b,0x6c,0x9a,0x65,
+ 0x9a,0x6c,0x97,0x65,0x94,0x6b,0x91,0x66,0x90,0x6f,0x8d,0x60,0x8d,0x6d,0x8e,0x67,
+ 0x93,0x6e,0x94,0x66,0x97,0x72,0x99,0x63,0x9c,0x71,0x9b,0x66,0x99,0x6c,0x97,0x61,
+ 0x97,0x6d,0x91,0x63,0x8e,0x6c,0x8c,0x66,0x8c,0x75,0x8e,0x63,0x91,0x6d,0x92,0x63,
+ 0x97,0x6f,0x9a,0x63,0x9b,0x6b,0x9a,0x62,0x9a,0x70,0x99,0x65,0x96,0x6a,0x91,0x63,
+ 0x8f,0x6e,0x8c,0x63,0x8c,0x6b,0x8d,0x60,0x8f,0x70,0x92,0x66,0x96,0x6e,0x99,0x66,
+ 0x9a,0x70,0x9b,0x62,0x99,0x72,0x99,0x68,0x97,0x6e,0x91,0x60,0x8e,0x6c,0x8e,0x67,
+ 0x8c,0x6c,0x8b,0x65,0x8e,0x6c,0x90,0x67,0x95,0x6f,0x97,0x69,0x99,0x6d,0x9a,0x68,
+ 0x9a,0x6f,0x98,0x64,0x96,0x6c,0x92,0x63,0x90,0x71,0x8d,0x64,0x8b,0x71,0x8c,0x65,
+ 0x8e,0x6f,0x92,0x69,0x94,0x6a,0x97,0x64,0x9a,0x71,0x9a,0x67,0x9a,0x6c,0x98,0x67,
+ 0x96,0x6d,0x92,0x65,0x8f,0x6c,0x8d,0x64,0x8b,0x72,0x8c,0x61,0x8e,0x6f,0x90,0x66,
+ 0x94,0x6f,0x96,0x69,0x98,0x6d,0x99,0x68,0x99,0x73,0x98,0x69,0x97,0x6f,0x93,0x64,
+ 0x8f,0x70,0x8d,0x66,0x8d,0x6b,0x8a,0x65,0x8c,0x6d,0x8f,0x66,0x92,0x70,0x95,0x69,
+ 0x99,0x6f,0x9a,0x67,0x9a,0x6d,0x97,0x63,0x95,0x70,0x93,0x63,0x8f,0x6b,0x8c,0x66,
+ 0x8b,0x6a,0x8a,0x61,0x8c,0x6c,0x8d,0x65,0x93,0x6f,0x96,0x63,0x99,0x6b,0x99,0x64,
+ 0x9b,0x6d,0x99,0x64,0x96,0x69,0x93,0x64,0x90,0x72,0x8c,0x61,0x8b,0x63,0x8b,0x60,
+ 0x8d,0x69,0x8e,0x65,0x93,0x6c,0x96,0x61,0x98,0x70,0x93,0x6b,0x9b,0x5b,0x9a,0x50,
+ 0x99,0x55,0x95,0x5b,0x92,0x66,0x8f,0x5e,0x8e,0x60,0x8f,0x56,0x91,0x5e,0x92,0x55,
+ 0x99,0x5d,0x9b,0x54,0x9d,0x5e,0x9d,0x54,0x9e,0x65,0x9c,0x55,0x9a,0x62,0x97,0x58,
+ 0x94,0x5a,0x90,0x53,0x90,0x59,0x8e,0x53,0x91,0x60,0x92,0x53,0x95,0x5c,0x98,0x57,
+ 0x9d,0x5c,0x9c,0x57,0x9d,0x5a,0x9c,0x54,0x9c,0x5e,0x97,0x54,0x95,0x5c,0x91,0x53,
+ 0x8f,0x60,0x8e,0x53,0x90,0x59,0x91,0x58,0x94,0x60,0x98,0x52,0x9b,0x5d,0x9d,0x54,
+ 0x9f,0x5c,0x9b,0x52,0x9b,0x5f,0x96,0x57,0x93,0x5b,0x90,0x57,0x8e,0x5d,0x8d,0x59,
+ 0x8f,0x60,0x91,0x52,0x95,0x5c,0x97,0x52,0x9b,0x5e,0x9c,0x53,0x9d,0x5e,0x9b,0x57,
+ 0x99,0x5b,0x96,0x53,0x93,0x5e,0x90,0x55,0x8d,0x5e,0x8d,0x58,0x8d,0x5d,0x8e,0x54,
+ 0x93,0x5d,0x97,0x55,0x99,0x5b,0x9a,0x51,0x9b,0x62,0x9b,0x52,0x9a,0x5f,0x96,0x50,
+ 0x94,0x5c,0x90,0x54,0x8e,0x5a,0x8c,0x51,0x8e,0x5d,0x8f,0x58,0x92,0x5d,0x94,0x51,
+ 0x99,0x5f,0x9b,0x53,0x9a,0x5d,0x99,0x53,0x97,0x60,0x95,0x56,0x92,0x5e,0x8f,0x5b,
+ 0x8e,0x5d,0x8a,0x50,0x8b,0x5b,0x8d,0x54,0x90,0x5f,0x93,0x54,0x98,0x5f,0x98,0x54,
+ 0x99,0x5b,0x99,0x54,0x99,0x5d,0x95,0x53,0x93,0x5d,0x8f,0x52,0x8d,0x5f,0x8a,0x57,
+ 0x8c,0x5d,0x8c,0x53,0x8f,0x5c,0x92,0x57,0x96,0x5e,0x98,0x55,0x99,0x5e,0x99,0x55,
+ 0x98,0x5b,0x97,0x53,0x93,0x5b,0x8e,0x54,0x8b,0x5c,0x89,0x52,0x8a,0x5b,0x8a,0x55,
+ 0x8e,0x5b,0x91,0x55,0x94,0x5b,0x96,0x50,0x99,0x5e,0x97,0x51,0x97,0x5b,0x96,0x58,
+ 0x93,0x5d,0x8e,0x55,0x8a,0x59,0x88,0x53,0x89,0x62,0x89,0x55,0x8b,0x5c,0x8f,0x57,
+ 0x94,0x5d,0x96,0x55,0x97,0x5f,0x96,0x54,0x97,0x5f,0x95,0x59,0x93,0x5b,0x8f,0x53,
+ 0x8d,0x5b,0x8a,0x54,0x8a,0x5d,0x8a,0x55,0x8d,0x5e,0x8f,0x55,0x93,0x5c,0x96,0x58,
+ 0x98,0x5e,0x96,0x54,0x98,0x5f,0x95,0x53,0x93,0x62,0x8f,0x53,0x8e,0x5e,0x8a,0x53,
+ 0x8a,0x5c,0x8b,0x5b,0x8c,0x5f,0x8e,0x54,0x92,0x5f,0x94,0x58,0x97,0x5b,0x97,0x55,
+ 0x97,0x5f,0x96,0x57,0x93,0x5c,0x8f,0x58,0x8b,0x61,0x8a,0x55,0x88,0x60,0x89,0x56,
+ 0x8b,0x5e,0x8e,0x57,0x91,0x59,0x94,0x54,0x96,0x5c,0x96,0x56,0x97,0x60,0x94,0x54,
+ 0x93,0x60,0x90,0x58,0x8e,0x5f,0x89,0x57,0x88,0x60,0x87,0x54,0x89,0x60,0x8c,0x55,
+ 0x90,0x62,0x93,0x5b,0x95,0x5c,0x96,0x55,0x96,0x5f,0x94,0x5a,0x94,0x62,0x90,0x5a,
+ 0x8d,0x5d,0x8a,0x59,0x88,0x5d,0x86,0x53,0x87,0x62,0x8a,0x5c,0x8d,0x5f,0x91,0x5a,
+ 0x93,0x63,0x94,0x57,0x96,0x5f,0x95,0x58,0x93,0x5f,0x90,0x59,0x8d,0x62,0x89,0x53,
+ 0x89,0x5e,0x88,0x5b,0x89,0x59,0x8a,0x57,0x8d,0x60,0x90,0x5c,0x93,0x5f,0x95,0x59,
+ 0x95,0x66,0x95,0x5a,0x93,0x5f,0x91,0x59,0x8c,0x62,0x8b,0x57,0x88,0x61,0x87,0x57,
+ 0x88,0x5e,0x8a,0x5b,0x8e,0x5f,0x91,0x5b,0x93,0x64,0x95,0x58,0x95,0x64,0x94,0x59,
+ 0x94,0x66,0x92,0x58,0x8d,0x65,0x8b,0x59,0x88,0x63,0x87,0x5d,0x87,0x62,0x8a,0x5e,
+ 0x8c,0x66,0x91,0x5a,0x91,0x61,0x94,0x5d,0x94,0x65,0x94,0x59,0x92,0x61,0x92,0x5f,
+ 0x8e,0x65,0x8a,0x5d,0x88,0x61,0x88,0x5b,0x87,0x67,0x89,0x59,0x8b,0x63,0x8f,0x5f,
+ 0x93,0x65,0x94,0x5d,0x95,0x62,0x94,0x5b,0x94,0x68,0x91,0x5c,0x8e,0x67,0x8b,0x63,
+ 0x88,0x6c,0x87,0x5d,0x86,0x5e,0x87,0x60,0x8a,0x68,0x8b,0x62,0x90,0x64,0x91,0x64,
+ 0x93,0x68,0x92,0x63,0x92,0x67,0x8f,0x62,0x8e,0x6a,0x8a,0x63,0x87,0x67,0x85,0x64,
+ 0x86,0x6e,0x86,0x63,0x89,0x6a,0x8b,0x5f,0x8f,0x6d,0x90,0x64,0x92,0x6a,0x91,0x6b,
+ 0x91,0x71,0x8f,0x67,0x8c,0x68,0x88,0x60,0x86,0x6a,0x85,0x67,0x84,0x6e,0x85,0x66,
+ 0x87,0x75,0x88,0x68,0x8d,0x6c,0x8e,0x64,0x8f,0x72,0x90,0x67,0x91,0x6f,0x8d,0x65,
+ 0x8c,0x73,0x89,0x63,0x86,0x6c,0x83,0x67,0x82,0x73,0x82,0x67,0x84,0x70,0x88,0x66,
+ 0x8b,0x70,0x8d,0x69,0x8e,0x70,0x8f,0x65,0x8e,0x71,0x8d,0x66,0x8a,0x70,0x89,0x66,
+ 0x82,0x71,0x82,0x66,0x82,0x6a,0x81,0x66,0x84,0x6e,0x86,0x63,0x8b,0x6d,0x8c,0x63,
+ 0x8f,0x6f,0x8f,0x64,0x8f,0x6c,0x8e,0x69,0x8c,0x70,0x87,0x67,0x85,0x6c,0x82,0x64,
+ 0x80,0x70,0x82,0x68,0x83,0x68,0x84,0x66,0x89,0x6c,0x8b,0x62,0x8f,0x6b,0x8f,0x6a,
+ 0x90,0x6b,0x8e,0x66,0x8c,0x69,0x88,0x65,0x86,0x6e,0x82,0x63,0x80,0x68,0x80,0x63,
+ 0x84,0x6f,0x84,0x63,0x87,0x6a,0x8a,0x67,0x8c,0x70,0x8d,0x62,0x8d,0x6f,0x8c,0x62,
+ 0x89,0x70,0x88,0x63,0x86,0x6a,0x82,0x5f,0x81,0x6d,0x80,0x64,0x82,0x6d,0x82,0x63,
+ 0x85,0x6f,0x88,0x65,0x8b,0x69,0x8a,0x66,0x8b,0x70,0x8c,0x63,0x8c,0x6c,0x87,0x66,
+ 0x85,0x72,0x82,0x66,0x80,0x69,0x7f,0x66,0x7f,0x75,0x80,0x67,0x85,0x6f,0x86,0x66,
+ 0x88,0x70,0x8a,0x67,0x8b,0x70,0x8a,0x68,0x88,0x76,0x87,0x6f,0x84,0x75,0x81,0x6c,
+ 0x7e,0x74,0x7e,0x6b,0x7d,0x74,0x7e,0x6b,0x80,0x74,0x83,0x68,0x86,0x77,0x88,0x6f,
+ 0x88,0x7a,0x89,0x6e,0x87,0x71,0x85,0x6d,0x82,0x79,0x80,0x6e,0x7d,0x7b,0x7a,0x72,
+ 0x7a,0x78,0x7c,0x70,0x7d,0x73,0x82,0x6f,0x84,0x77,0x86,0x6e,0x87,0x78,0x87,0x70,
+ 0x87,0x76,0x85,0x71,0x83,0x77,0x7f,0x70,0x7f,0x76,0x7a,0x72,0x7a,0x78,0x7b,0x6f,
+ 0x7e,0x79,0x81,0x6e,0x83,0x78,0x86,0x6f,0x88,0x76,0x88,0x6f,0x88,0x76,0x86,0x72,
+ 0x84,0x7c,0x81,0x74,0x7e,0x76,0x7c,0x72,0x7c,0x7b,0x7c,0x72,0x7e,0x76,0x81,0x6c,
+ 0x84,0x79,0x86,0x71,0x89,0x7c,0x88,0x71,0x89,0x7d,0x88,0x71,0x85,0x7b,0x82,0x72,
+ 0x81,0x7a,0x7e,0x73,0x7d,0x7b,0x7c,0x75,0x80,0x7e,0x83,0x7a,0x86,0x7f,0x88,0x7b,
+ 0x89,0x80,0x83,0x80,0x69,0x59,0x9c,0x9a,0x9a,0x9b,0x9d,0x9e,0xa0,0xa1,0xa0,0xa2,
+ 0xa4,0xa4,0xa7,0xa7,0xa7,0xa8,0xa9,0xa9,0xa8,0xa7,0xa6,0xa4,0xa3,0xa2,0xa1,0x9f,
+ 0x9f,0x9c,0x9b,0x9a,0x9a,0x9a,0x9b,0x9b,0x9c,0x9e,0xa0,0xa2,0xa3,0xa3,0xa4,0xa4,
+ 0xa7,0xa6,0xa7,0xa7,0xa7,0xa6,0xa4,0xa3,0xa2,0xa0,0xa0,0x9d,0x9c,0x9a,0x9a,0x99,
+ 0x97,0x99,0x9a,0x9a,0x9b,0x9c,0x9d,0x9f,0xa3,0xa3,0xa5,0xa6,0xa6,0xa6,0xa6,0xa6,
+ 0xa5,0xa6,0xa5,0xa4,0xa2,0xa2,0xa0,0x9e,0x9c,0x9c,0x9b,0x9a,0x9a,0x9b,0x9a,0x9c,
+ 0x9d,0x9e,0x9e,0xa0,0xa2,0xa2,0xa1,0xa4,0xa4,0xa4,0xa5,0xa6,0xa5,0xa6,0xa4,0xa4,
+ 0xa3,0xa1,0xa1,0x9f,0x9d,0x9b,0x99,0x9a,0x99,0x99,0x99,0x98,0x9a,0x9b,0x9e,0x9d,
+ 0x9f,0x9f,0xa3,0xa4,0xa5,0xa6,0xa4,0xa3,0xa2,0xa5,0xa6,0xa6,0xa5,0xa3,0xa1,0x9f,
+ 0x9d,0x9c,0x9b,0x9b,0x99,0x98,0x97,0x97,0x9a,0x9b,0x9e,0x9e,0x9f,0x9f,0xa1,0xa2,
+ 0xa5,0xa4,0xa5,0xa6,0xa6,0xa7,0xa5,0xa4,0xa3,0xa3,0xa2,0xa1,0x9e,0x9d,0x9d,0x99,
+ 0x9a,0x99,0x99,0x98,0x9a,0x9c,0x9c,0x9f,0x9f,0xa1,0xa3,0xa2,0xa5,0xa7,0xa8,0xa8,
+ 0xa7,0xa6,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x9e,0x9c,0x9b,0x9a,0x99,0x9b,0x99,0x9a,
+ 0x9b,0x9b,0x9e,0xa0,0xa0,0xa1,0xa2,0xa3,0xa4,0xa6,0xa6,0xa7,0xa7,0xa8,0xa8,0xa7,
+ 0xa4,0xa5,0xa3,0xa1,0xa0,0x9e,0x9e,0x9c,0x9d,0x9b,0x9a,0x9a,0x9b,0x9b,0x9f,0xa0,
+ 0xa0,0xa1,0xa2,0xa3,0xa5,0xa6,0xa8,0xa8,0xa8,0xa7,0xa8,0xa6,0xa6,0xa5,0xa4,0xa3,
+ 0xa1,0x9f,0x9b,0x9b,0x99,0x98,0x99,0x9b,0x9c,0x9b,0x9d,0x9d,0x9f,0xa1,0xa3,0xa5,
+ 0xa6,0xaa,0xaa,0xa9,0xa9,0xa7,0xa7,0xa7,0xa6,0xa7,0xa6,0xa6,0xa4,0xa2,0xa0,0x9f,
+ 0x9f,0x9c,0x9c,0x9b,0x9c,0x9d,0x9f,0x9f,0x9f,0xa3,0xa4,0xa6,0xa7,0xa8,0xa9,0xa9,
+ 0xaa,0xa9,0xa9,0xaa,0xa9,0xa7,0xa8,0xa8,0xa5,0xa4,0xa3,0xa1,0xa0,0x9f,0x9e,0x9d,
+ 0x9f,0x9f,0xa0,0xa0,0xa3,0xa3,0xa5,0xa7,0xa9,0xa9,0xab,0xab,0xac,0xad,0xad,0xab,
+ 0xaa,0xab,0xaa,0xa7,0xa7,0xa4,0xa3,0xa3,0xa1,0x9f,0x9e,0x9e,0x9e,0x9e,0x9f,0x9f,
+ 0xa2,0xa3,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xab,0xab,0xaa,0xaa,0xab,0xa9,0xa7,
+ 0xa5,0xa4,0xa3,0xa1,0xa2,0xa1,0x9f,0x9d,0x9c,0x9f,0x9f,0xa0,0xa2,0xa4,0xa6,0xa6,
+ 0xa8,0xaa,0xac,0xac,0xac,0xac,0xad,0xad,0xab,0xab,0xab,0xa8,0xa9,0xa6,0xa5,0xa3,
+ 0xa3,0xa1,0x9f,0x9f,0x9f,0x9e,0x9f,0xa1,0xa1,0xa3,0xa5,0xa6,0xa9,0xa9,0xaa,0xab,
+ 0xaa,0xaa,0xac,0xad,0xac,0xa9,0xaa,0xa9,0xa8,0xa8,0xa4,0xa4,0xa2,0x9f,0x9f,0xa0,
+ 0x9f,0xa0,0xa2,0xa1,0xa2,0xa3,0xa4,0xa5,0xa4,0xa5,0xa8,0xaa,0xae,0xaa,0xaa,0xa8,
+ 0xa8,0xa8,0xa9,0xaa,0xa9,0xa5,0xa2,0xa2,0xa2,0xa1,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1,
+ 0xa1,0xa2,0xa3,0xa5,0xa7,0xa9,0xaa,0xac,0xac,0xad,0xad,0xac,0xac,0xab,0xac,0xab,
+ 0xa9,0xa8,0xa8,0xa5,0xa3,0xa2,0xa0,0xa1,0xa0,0xa0,0x9f,0xa0,0xa2,0xa4,0xa4,0xa5,
+ 0xa4,0xa6,0xa9,0xab,0xae,0xae,0xad,0xab,0xad,0xac,0xad,0xab,0xab,0xa9,0xa7,0xa7,
+ 0xa5,0xa1,0xa0,0x9e,0x9e,0x9d,0x98,0xa6,0xa9,0xab,0xac,0xae,0xaf,0xb1,0xb2,0xb3,
+ 0xb2,0xb4,0xb3,0xb4,0xb4,0xb5,0xb2,0xaf,0xae,0xad,0xab,0xab,0xa9,0xa9,0xa6,0xa3,
+ 0xa5,0xa5,0xa6,0xa8,0xa8,0xab,0xab,0xae,0xaf,0xb0,0xb2,0xb3,0xb5,0xb4,0xb4,0xb4,
+ 0xb3,0xb2,0xb2,0xb1,0xb0,0xaf,0xae,0xab,0xaa,0xa8,0xa8,0xa6,0xa6,0xa5,0xa8,0xaa,
+ 0xaa,0xaa,0xad,0xae,0xb0,0xb4,0xb5,0xb4,0xb6,0xb7,0xb6,0xb8,0xb7,0xb7,0xb7,0xb5,
+ 0xb3,0xb0,0xb1,0xaf,0xad,0xab,0xac,0xaa,0xaa,0xab,0xac,0xab,0xac,0xab,0xad,0xaf,
+ 0xb1,0xb4,0xb4,0xb6,0xb5,0xb6,0xb6,0xb6,0xb6,0xb7,0xb5,0xb6,0xb5,0xb3,0xb1,0xaf,
+ 0xab,0xab,0xaa,0xaa,0xaa,0xab,0xaa,0xaa,0xab,0xac,0xac,0xaf,0xb0,0xb1,0xb2,0xb4,
+ 0xb7,0xb7,0xb5,0xb5,0xb7,0xb9,0xb5,0xb4,0xb2,0xb1,0xaf,0xaf,0xad,0xad,0xab,0xa9,
+ 0xa8,0xa9,0xaa,0xac,0xac,0xad,0xae,0xaf,0xb0,0xb4,0xb6,0xb7,0xb5,0xb5,0xb7,0xb7,
+ 0xb9,0xb9,0xb7,0xb9,0xb6,0xb5,0xb2,0xb1,0xae,0xae,0xae,0xac,0xac,0xaa,0xa8,0xa9,
+ 0xab,0xad,0xae,0xae,0xb0,0xb2,0xb4,0xb6,0xb5,0xb6,0xb7,0xb9,0xb8,0xb7,0xb5,0xb4,
+ 0xb5,0xb3,0xb4,0xb2,0xad,0xac,0xaa,0xa9,0xa9,0xa8,0xa8,0xa8,0xa8,0xa9,0xaa,0xab,
+ 0xae,0xaf,0xb2,0xb3,0xb3,0xb2,0xb7,0xb8,0xb5,0xb5,0xb6,0xb3,0xb2,0xb3,0xb2,0xaf,
+ 0xae,0xab,0xa9,0xaa,0xa7,0xa5,0xa7,0xa7,0xa9,0xa9,0xa9,0xaa,0xab,0xae,0xb1,0xb1,
+ 0xb2,0xb4,0xb5,0xb6,0xb3,0xb5,0xb5,0xb4,0xb4,0xb5,0xb1,0xaf,0xac,0xab,0xab,0xaa,
+ 0xaa,0xa9,0xa7,0xa8,0xa6,0xa6,0xa6,0xa8,0xaa,0xaf,0xb1,0xb1,0xaf,0xb0,0xb3,0xb3,
+ 0xb4,0xb5,0xb5,0xb3,0xb1,0xb0,0xb2,0xaf,0xaf,0xae,0xab,0xaa,0xa8,0xa8,0xa8,0xa7,
+ 0xa7,0xa7,0xa9,0xac,0xad,0xad,0xae,0xaf,0xb3,0xb3,0xb5,0xb5,0xb6,0xb5,0xb6,0xb6,
+ 0xb4,0xb3,0xb2,0xb1,0xb0,0xae,0xae,0xac,0xaa,0xaa,0xa8,0xa9,0xa7,0xa8,0xaa,0xab,
+ 0xae,0xb0,0xb0,0xb1,0xb2,0xb2,0xb4,0xb5,0xb6,0xb9,0xb6,0xb5,0xb5,0xb4,0xb4,0xb1,
+ 0xb1,0xb0,0xad,0xac,0xab,0xab,0xaa,0xa8,0xa9,0xa9,0xaa,0xab,0xac,0xac,0xac,0xb0,
+ 0xb2,0xb3,0xb5,0xb6,0xb6,0xb6,0xb5,0xb7,0xb5,0xb6,0xb4,0xb1,0xb0,0xae,0xac,0xac,
+ 0xac,0xab,0xa8,0xa8,0xa7,0xa8,0xa7,0xaa,0xac,0xad,0xaf,0xb1,0xb1,0xb2,0xb5,0xb6,
+ 0xb7,0xb6,0xb5,0xb6,0xb5,0xb6,0xb7,0xb5,0xb4,0xb2,0xae,0xaf,0xac,0xaa,0xa8,0xa8,
+ 0xa7,0xa8,0xab,0xab,0xac,0xad,0xad,0xb1,0xb3,0xb3,0xb6,0xb6,0xb6,0xb5,0xb8,0xb6,
+ 0xb7,0xb5,0xb4,0xb2,0xb1,0xb0,0xad,0xab,0xaa,0xa9,0xa7,0xa7,0xa5,0xa7,0xa8,0xa8,
+ 0xa9,0xac,0xad,0xaf,0xb1,0xb3,0xb3,0xb3,0xb3,0xb5,0xb7,0xb7,0xb7,0xb5,0xb5,0xb1,
+ 0xb3,0xb0,0xb0,0xad,0xac,0xaa,0xa8,0xa9,0xa7,0xa8,0xa8,0xa9,0xa9,0xab,0xae,0xae,
+ 0xb0,0xb2,0xb4,0xb4,0xb6,0xb6,0xb5,0xb6,0xb6,0xb7,0xb4,0xb5,0xb4,0xb2,0xb0,0xaf,
+ 0xae,0xac,0xab,0xab,0xa8,0xa9,0xa8,0xa9,0xab,0xac,0xae,0xaf,0xad,0xb1,0xb3,0xb5,
+ 0xb5,0xb5,0xb6,0xb7,0xb7,0xb7,0xb4,0xb5,0xb3,0xb2,0xb0,0xb0,0xae,0xaa,0xa8,0xa8,
+ 0xa9,0xa8,0xa7,0xa9,0xaa,0xaa,0xac,0xac,0xaf,0xb0,0xb3,0xb5,0xb5,0xb5,0xb6,0xb6,
+ 0xb7,0xb7,0xb7,0xb6,0xb5,0xb2,0xb0,0xad,0xad,0xac,0xaa,0xa9,0xa9,0xa9,0xa8,0xa9,
+ 0xa9,0xaa,0xaa,0xac,0xad,0xaf,0xb1,0xb1,0xb3,0xb3,0xb5,0xb5,0xb5,0xb5,0xb6,0xb3,
+ 0xb3,0xb1,0xb0,0xac,0xad,0xab,0xab,0xa9,0xa8,0xa9,0xa8,0xa8,0xa8,0xa9,0xaa,0xab,
+ 0xaf,0xb0,0xb1,0xb3,0xb4,0xb2,0xb3,0xb2,0xb4,0xb5,0xb3,0xb3,0xb3,0xb0,0xad,0xaa,
+ 0xaa,0xaa,0xab,0xa8,0xa4,0xa4,0xa4,0xa4,0xa7,0xab,0xaa,0xaa,0xac,0xac,0xad,0xb0,
+ 0xb0,0xb2,0xb3,0xb4,0xb5,0xb4,0xb3,0xb1,0xb1,0xb0,0xaf,0xae,0xad,0xa9,0xa7,0xa9,
+ 0xa8,0xa6,0xa6,0xa5,0xa5,0xa5,0xa8,0xa8,0xab,0xac,0xae,0xaf,0xb2,0xb1,0xb3,0xb2,
+ 0xb3,0xb4,0xb4,0xb3,0xb4,0xb1,0xaf,0xae,0xad,0xac,0xad,0xab,0xa9,0xa7,0xa7,0xa5,
+ 0xa7,0xa7,0xa9,0xa8,0xa9,0xaa,0xac,0xae,0xaf,0xb0,0xb1,0xb0,0xb4,0xb3,0xb3,0xb3,
+ 0xb0,0xaf,0xb0,0xaf,0xae,0xad,0xa9,0xa8,0xa6,0xa6,0xa7,0xa7,0xa8,0xa7,0xa9,0xaa,
+ 0xab,0xac,0xad,0xaf,0xb1,0xb2,0xb4,0xb4,0xb4,0xb4,0xb5,0xb5,0xb5,0xb3,0xb0,0xb0,
+ 0xb1,0xaf,0xac,0xaa,0xaa,0xa9,0xa8,0xa6,0xa6,0xa9,0xa8,0xa9,0xab,0xac,0xad,0xae,
+ 0xae,0xaf,0xb1,0xb3,0xb2,0xb4,0xb4,0xb1,0xb1,0xb0,0xb2,0xb1,0xb0,0xac,0xac,0xaa,
+ 0xab,0xaa,0xa8,0xa8,0xa7,0xa8,0xa8,0xa8,0xaa,0xac,0xad,0xae,0xae,0xb2,0xb3,0xb5,
+ 0xb5,0xb7,0xb5,0xb6,0xb6,0xb4,0xb3,0xb3,0xb1,0xae,0xaf,0xad,0xae,0xaa,0xaa,0xa8,
+ 0xa6,0xa8,0xa8,0xa8,0xaa,0xaa,0xac,0xae,0xb0,0xb0,0xb2,0xb2,0xb4,0xb5,0xb4,0xb3,
+ 0xb5,0xb2,0xb2,0xb1,0xb1,0xae,0xad,0xab,0xaa,0xaa,0xa9,0xa7,0xa8,0xa7,0xa9,0xa8,
+ 0xa9,0xac,0xab,0xad,0xb1,0xb0,0xb2,0xb4,0xb5,0xb6,0xb5,0xb5,0xb5,0xb4,0xb3,0xb2,
+ 0xb2,0xb0,0xaf,0xad,0xab,0xab,0xa8,0xa8,0xa8,0xa7,0xa9,0xa8,0xaa,0xab,0xad,0xac,
+ 0xb0,0xb2,0xb3,0xb4,0xb4,0xb5,0xb4,0xb5,0xb6,0xb5,0xb6,0xb4,0xb2,0xaf,0xae,0xad,
+ 0xac,0xaa,0xa9,0xa6,0xa6,0xa7,0xa6,0xa6,0xa6,0xa7,0xa9,0xaa,0xae,0xaf,0xb1,0xb1,
+ 0xb1,0xb3,0xb3,0xb3,0xb4,0xb4,0xb2,0xb1,0xb1,0xb1,0xaf,0xae,0xae,0xaa,0xaa,0xa9,
+ 0xa9,0xaa,0xa8,0xa6,0xa5,0xa7,0xa9,0xad,0xb0,0xb1,0xaf,0xae,0xb0,0xb3,0xb4,0xb7,
+ 0xb6,0xb5,0xb4,0xb4,0xb3,0xb2,0xb1,0xae,0xae,0xad,0xad,0xaa,0xa8,0xa9,0xa9,0xa9,
+ 0xac,0xab,0xad,0xae,0xb0,0xb1,0xb1,0xb2,0xb5,0xb3,0xb5,0xb5,0xb6,0xb5,0xb6,0xb4,
+ 0xb3,0xb2,0xb1,0xb0,0xae,0xae,0xad,0xab,0xa9,0xa9,0xa6,0xa5,0xa7,0xa4,0xa1,0xb0,
+ 0xb4,0xb4,0xb7,0xba,0xb9,0xba,0xb8,0xba,0xba,0xba,0xb9,0xb8,0xb8,0xb6,0xb3,0xb2,
+ 0xb1,0xb2,0xb0,0xad,0xac,0xac,0xad,0xae,0xb0,0xb0,0xb3,0xb2,0xb6,0xb7,0xb8,0xb8,
+ 0xbb,0xbb,0xbb,0xbb,0xba,0xbe,0xb9,0xb9,0xba,0xb7,0xb6,0xb5,0xb3,0xb2,0xb0,0xaf,
+ 0xaf,0xae,0xaf,0xaf,0xaf,0xb0,0xb3,0xb2,0xb8,0xb8,0xb9,0xba,0xbb,0xbb,0xbd,0xbd,
+ 0xbd,0xbb,0xbb,0xba,0xba,0xb9,0xb7,0xb5,0xb5,0xb3,0xb2,0xb2,0xb1,0xaf,0xb0,0xb0,
+ 0xb2,0xb2,0xb1,0xb2,0xb4,0xb7,0xb8,0xb7,0xb8,0xbb,0xba,0xb8,0xb9,0xbb,0xba,0xb8,
+ 0xb8,0xb8,0xb7,0xb7,0xb3,0xb1,0xb3,0xb0,0xb0,0xac,0xaf,0xae,0xb1,0xae,0xaf,0xaf,
+ 0xb2,0xb4,0xb6,0xb7,0xb8,0xb9,0xba,0xbc,0xbe,0xbd,0xbb,0xb9,0xb7,0xb6,0xb5,0xb5,
+ 0xb7,0xb2,0xae,0xac,0xad,0xac,0xae,0xb0,0xb0,0xb1,0xb0,0xb1,0xb5,0xb6,0xb9,0xbb,
+ 0xbc,0xbd,0xbe,0xbc,0xbe,0xbe,0xbe,0xbc,0xbc,0xbc,0xb8,0xb8,0xb6,0xb4,0xb4,0xb5,
+ 0xb4,0xb0,0xb0,0xae,0xb0,0xb0,0xb1,0xb2,0xb4,0xb6,0xb6,0xb6,0xb8,0xba,0xbc,0xbc,
+ 0xbe,0xbb,0xb9,0xba,0xbb,0xbb,0xbc,0xb8,0xb5,0xb4,0xb4,0xb3,0xb3,0xb1,0xb1,0xb1,
+ 0xb2,0xb2,0xb4,0xb7,0xb7,0xb8,0xba,0xbb,0xbc,0xbd,0xbf,0xbf,0xc2,0xc1,0xc1,0xc1,
+ 0xbe,0xc0,0xbd,0xba,0xb8,0xb7,0xb6,0xb6,0xb6,0xb2,0xb3,0xaf,0xae,0xaf,0xb1,0xb5,
+ 0xb6,0xb5,0xb8,0xb8,0xb9,0xbd,0xbe,0xbe,0xc0,0xc0,0xbf,0xc1,0xc0,0xbe,0xbe,0xbd,
+ 0xbd,0xbb,0xb9,0xb7,0xb6,0xb5,0xb5,0xb7,0xb6,0xb4,0xb5,0xb6,0xb8,0xba,0xbb,0xbb,
+ 0xbe,0xbf,0xc2,0xc1,0xc1,0xc0,0xc1,0xc2,0xc1,0xc1,0xc1,0xc0,0xbc,0xbc,0xb9,0xb8,
+ 0xb9,0xb9,0xb6,0xb6,0xb4,0xb4,0xb4,0xb7,0xb9,0xb8,0xbb,0xbc,0xbc,0xbd,0xbd,0xbe,
+ 0xc1,0xc2,0xc0,0xbf,0xbf,0xbf,0xbd,0xbc,0xbe,0xbb,0xb9,0xb8,0xb7,0xb7,0xb6,0xb4,
+ 0xb4,0xb5,0xb4,0xb6,0xb7,0xb8,0xb8,0xbc,0xbd,0xbe,0xc1,0xc1,0xc2,0xc3,0xc2,0xc4,
+ 0xc5,0xc5,0xc3,0xc2,0xbe,0xbe,0xbd,0xbb,0xb9,0xb8,0xb4,0xb6,0xb6,0xb3,0xb4,0xb6,
+ 0xb5,0xb7,0xb9,0xbc,0xbe,0xbf,0xbe,0xc2,0xbf,0xc0,0xc0,0xc3,0xc2,0xc1,0xbe,0xbd,
+ 0xbb,0xb9,0xb7,0xb6,0xb6,0xb5,0xb3,0xb2,0xb1,0xb1,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,
+ 0xbe,0xbe,0xbf,0xc2,0xc2,0xc3,0xc3,0xc4,0xc4,0xc1,0xc2,0xbf,0xbc,0xbe,0xbb,0xba,
+ 0xb9,0xb7,0xb4,0xb5,0xb5,0xb4,0xb4,0xb5,0xb7,0xb7,0xb9,0xb9,0xbb,0xbd,0xbe,0xc1,
+ 0xc3,0xc2,0xc0,0xc0,0xc2,0xc2,0xc1,0xc2,0xc1,0xc0,0xbc,0xba,0xb9,0xb9,0xb9,0xb9,
+ 0xb6,0xb2,0xb3,0xb3,0xb6,0xb8,0xbb,0xba,0xbd,0xbd,0xbd,0xc1,0xc2,0xc2,0xc3,0xc2,
+ 0xc3,0xc2,0xc2,0xc2,0xc0,0xbe,0xbd,0xbc,0xbb,0xb9,0xb9,0xb4,0xb4,0xb5,0xb4,0xb3,
+ 0xb5,0xb5,0xb6,0xb6,0xb8,0xba,0xbb,0xbe,0xc0,0xc2,0xc1,0xc2,0xc2,0xc3,0xc3,0xc1,
+ 0xc1,0xbf,0xbe,0xbb,0xb9,0xb8,0xb7,0xb3,0xb3,0xb2,0xb2,0xb2,0xb3,0xb4,0xb5,0xb5,
+ 0xb8,0xb9,0xbd,0xbf,0xc0,0xbf,0xc0,0xbf,0xc0,0xc2,0xc4,0xc1,0xbf,0xbd,0xb9,0xb9,
+ 0xba,0xba,0xb7,0xb6,0xb2,0xb2,0xb2,0xb2,0xb4,0xb7,0xb8,0xb7,0xb8,0xbb,0xbc,0xbe,
+ 0xc1,0xc1,0xc1,0xc2,0xc0,0xc2,0xc2,0xc2,0xc0,0xbf,0xbe,0xbc,0xbc,0xba,0xb8,0xb7,
+ 0xb5,0xb1,0xb4,0xb3,0xb3,0xb4,0xb4,0xb5,0xb7,0xb8,0xbb,0xbb,0xbc,0xbe,0xbe,0xc0,
+ 0xbf,0xc0,0xc1,0xc1,0xbf,0xbf,0xbe,0xbd,0xbd,0xba,0xb8,0xb6,0xb6,0xb6,0xb6,0xb6,
+ 0xb5,0xb5,0xb7,0xb6,0xb8,0xba,0xbb,0xbb,0xc0,0xbd,0xbd,0xc0,0xc1,0xbf,0xc0,0xbd,
+ 0xbd,0xbb,0xbb,0xbb,0xb9,0xb8,0xb7,0xb5,0xb4,0xb6,0xb4,0xb3,0xb1,0xb2,0xb3,0xb4,
+ 0xb8,0xb9,0xb8,0xb9,0xb9,0xb9,0xbb,0xc0,0xbf,0xc0,0xbe,0xbe,0xbc,0xbd,0xbc,0xbc,
+ 0xb7,0xb8,0xb6,0xb4,0xb6,0xb5,0xb3,0xb1,0xb1,0xb1,0xb1,0xb4,0xb6,0xb7,0xb7,0xb9,
+ 0xba,0xba,0xbd,0xbd,0xbd,0xbd,0xbd,0xbc,0xbc,0xba,0xb9,0xb8,0xb8,0xb6,0xb5,0xb5,
+ 0xb3,0xb2,0xb2,0xb2,0xb2,0xb3,0xb4,0xb3,0xb4,0xb7,0xb7,0xb9,0xbc,0xbc,0xbd,0xbe,
+ 0xbf,0xc0,0xc2,0xc0,0xbf,0xbe,0xbf,0xbe,0xbc,0xba,0xb8,0xb7,0xb6,0xb3,0xb2,0xb0,
+ 0xb0,0xb2,0xb4,0xb2,0xb3,0xb2,0xb4,0xb5,0xba,0xba,0xbc,0xbb,0xba,0xbb,0xbc,0xbc,
+ 0xbd,0xbc,0xbc,0xbb,0xba,0xba,0xb8,0xb6,0xb6,0xb4,0xb2,0xb2,0xb2,0xb2,0xb4,0xb5,
+ 0xb6,0xb5,0xb6,0xb7,0xba,0xbb,0xbd,0xbe,0xbe,0xbd,0xbe,0xbe,0xbe,0xbf,0xbc,0xbc,
+ 0xbb,0xba,0xb7,0xb6,0xb4,0xb4,0xb2,0xb2,0xb1,0xb0,0xb0,0xb1,0xb2,0xb3,0xb6,0xb8,
+ 0xb8,0xbb,0xbd,0xbd,0xbe,0xbf,0xc1,0xbf,0xc0,0xbf,0xbe,0xbd,0xbd,0xbe,0xbb,0xb8,
+ 0xb7,0xb5,0xb3,0xb5,0xb4,0xb4,0xb4,0xb3,0xb4,0xb5,0xb5,0xb7,0xb8,0xbb,0xba,0xbc,
+ 0xbe,0xbe,0xbf,0xbe,0xbf,0xbe,0xbd,0xbb,0xbd,0xbb,0xba,0xb9,0xb7,0xb6,0xb5,0xb4,
+ 0xb3,0xb1,0xb1,0xad,0xaa,0xac,0xaf,0xb3,0xb9,0xb8,0xb9,0xb8,0xb7,0xb8,0xbd,0xc1,
+ 0xc0,0xbf,0xbe,0xbd,0xbd,0xbe,0xbd,0xba,0xb9,0xb8,0xb6,0xb7,0xb8,0xb6,0xb0,0xaa,
+ 0xa9,0xa9,0xb0,0xb8,0xbb,0xb4,0xae,0xad,0xb1,0xba,0xc0,0xc1,0xbf,0xbc,0xba,0xbc,
+ 0xbe,0xbb,0xbc,0xb9,0xb8,0xb7,0xb5,0xb3,0xb4,0xb4,0xb4,0xb2,0xb4,0xb3,0xb6,0xb6,
+ 0xb7,0xb9,0xb8,0xbb,0xbb,0xbe,0xc0,0xc1,0xbf,0xbe,0xbc,0xbd,0xbb,0xbc,0xbc,0xb9,
+ 0xb6,0xb5,0xb4,0xb3,0xb3,0xb4,0xb2,0xb2,0xb1,0xb1,0xb3,0xb3,0xb6,0xb5,0xb8,0xb8,
+ 0xba,0xbc,0xbb,0xbd,0xbe,0xbd,0xbc,0xbc,0xbb,0xba,0xb9,0xb9,0xb7,0xb6,0xb6,0xb3,
+ 0xb1,0xb1,0xb1,0xb2,0xb2,0xb2,0xb3,0xb5,0xb6,0xb7,0xb7,0xb9,0xbb,0xbe,0xbd,0xbe,
+ 0xbd,0xbb,0xbc,0xbc,0xbb,0xbb,0xb9,0xb7,0xb4,0xb4,0xb2,0xb3,0xb0,0xb0,0xad,0xaf,
+ 0xad,0xb1,0xb1,0xb1,0xb1,0xaf,0xad,0xbc,0xc2,0xc3,0xc1,0xc3,0xc1,0xc4,0xc2,0xc3,
+ 0xc1,0xc0,0xbc,0xbb,0xbb,0xb9,0xb7,0xb5,0xb5,0xb3,0xb3,0xb3,0xb4,0xb5,0xb5,0xb3,
+ 0xb6,0xb8,0xb9,0xbe,0xbd,0xbc,0xbc,0xbd,0xbd,0xbf,0xbf,0xbe,0xbe,0xbc,0xbb,0xba,
+ 0xb8,0xb8,0xb5,0xb6,0xb2,0xb1,0xb0,0xb1,0xb2,0xb2,0xb4,0xb6,0xb7,0xb8,0xb9,0xbc,
+ 0xbd,0xbd,0xbf,0xc1,0xc0,0xc0,0xc1,0xbe,0xbf,0xbe,0xbb,0xba,0xb8,0xb7,0xb4,0xb3,
+ 0xb0,0xb0,0xaf,0xb0,0xae,0xaf,0xaf,0xb1,0xb2,0xb6,0xb6,0xb9,0xbb,0xbd,0xbd,0xbb,
+ 0xbe,0xbf,0xbc,0xbe,0xbc,0xbb,0xbb,0xb9,0xb5,0xb4,0xb4,0xb2,0xb2,0xb1,0xaf,0xaf,
+ 0xaf,0xb1,0xb3,0xb5,0xb3,0xb4,0xb4,0xb7,0xb9,0xbb,0xba,0xba,0xb9,0xba,0xbb,0xba,
+ 0xba,0xb4,0xb4,0xb3,0xb2,0xaf,0xae,0xac,0xaa,0xa9,0xa8,0xa7,0xa6,0xa9,0xab,0xb0,
+ 0xaf,0xb0,0xae,0xb0,0xb4,0xb6,0xb8,0xb9,0xba,0xbc,0xba,0xba,0xb9,0xb8,0xba,0xb7,
+ 0xb5,0xb5,0xb1,0xb1,0xaf,0xae,0xae,0xae,0xae,0xae,0xae,0xaf,0xaf,0xaf,0xb1,0xb5,
+ 0xb8,0xb8,0xb7,0xb9,0xb8,0xba,0xbb,0xbb,0xba,0xb8,0xb5,0xb5,0xb5,0xb4,0xb2,0xb0,
+ 0xae,0xad,0xab,0xac,0xab,0xaa,0xac,0xac,0xae,0xb0,0xb0,0xb3,0xb3,0xb6,0xb6,0xb6,
+ 0xb8,0xb8,0xb8,0xb8,0xb7,0xb8,0xb5,0xb5,0xb2,0xb1,0xb0,0xaf,0xad,0xac,0xaa,0xa8,
+ 0xa9,0xaa,0xaa,0xac,0xad,0xae,0xae,0xb1,0xb2,0xb4,0xb4,0xb6,0xb6,0xb7,0xb7,0xb7,
+ 0xb8,0xb8,0xb7,0xb7,0xb5,0xb4,0xb2,0xb2,0xae,0xae,0xad,0xaa,0xaa,0xab,0xab,0xaa,
+ 0xaa,0xac,0xae,0xb0,0xb3,0xb4,0xb4,0xb5,0xb6,0xb8,0xb8,0xb9,0xba,0xb8,0xb8,0xb6,
+ 0xb6,0xb3,0xb0,0xb0,0xae,0xac,0xab,0xab,0xaa,0xa8,0xa9,0xaa,0xab,0xab,0xac,0xad,
+ 0xaf,0xb0,0xb3,0xb4,0xb5,0xb5,0xb7,0xb8,0xb8,0xb7,0xb8,0xb5,0xb4,0xb5,0xb4,0xb3,
+ 0xb1,0xb0,0xae,0xac,0xab,0xac,0xab,0xac,0xac,0xac,0xab,0xac,0xae,0xb1,0xb2,0xb3,
+ 0xb2,0xb2,0xb4,0xb4,0xb6,0xb6,0xb5,0xb3,0xb2,0xb2,0xb2,0xb2,0xaf,0xaf,0xae,0xad,
+ 0xac,0xab,0xab,0xac,0xab,0xaa,0xac,0xaf,0xaf,0xaf,0xae,0xb0,0xb2,0xb1,0xb2,0xb5,
+ 0xb6,0xb4,0xb1,0xb2,0xb2,0xb2,0xb0,0xb0,0xaf,0xae,0xab,0xaa,0xab,0xab,0xaa,0xa9,
+ 0xaa,0xab,0xaf,0xb0,0xb1,0xb2,0xb1,0xb4,0xb5,0xb7,0xb7,0xb9,0xb7,0xb6,0xb5,0xb6,
+ 0xb5,0xb5,0xb4,0xb2,0xaf,0xad,0xab,0xaa,0xa9,0xaa,0xa7,0xa9,0xa9,0xa9,0xac,0xac,
+ 0xae,0xb0,0xb0,0xb1,0xb4,0xb3,0xb5,0xb5,0xb7,0xb5,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,
+ 0xaf,0xaf,0xad,0xae,0xab,0xaa,0xa9,0xa8,0xa9,0xaa,0xab,0xac,0xae,0xad,0xaf,0xaf,
+ 0xb3,0xb4,0xb4,0xb6,0xb4,0xb3,0xb3,0xb5,0xb5,0xb3,0xb2,0xb0,0xad,0xac,0xad,0xab,
+ 0xab,0xaa,0xa9,0xa8,0xaa,0xaa,0xa9,0xac,0xac,0xac,0xac,0xae,0xb0,0xb2,0xb1,0xb4,
+ 0xb3,0xb3,0xb2,0xb2,0xb2,0xb4,0xb2,0xb1,0xae,0xad,0xab,0xaa,0xa9,0xa8,0xa6,0xa8,
+ 0xa9,0xaa,0xa9,0xa9,0xaa,0xad,0xae,0xb0,0xb1,0xb4,0xb3,0xb5,0xb4,0xb5,0xb5,0xb5,
+ 0xb3,0xb3,0xb2,0xb1,0xb0,0xaf,0xae,0xab,0xab,0xa8,0xa9,0xa7,0xaa,0xa8,0xa8,0xa8,
+ 0xaa,0xac,0xae,0xaf,0xaf,0xaf,0xb0,0xb1,0xb3,0xb4,0xb3,0xb4,0xb1,0xaf,0xaf,0xae,
+ 0xac,0xab,0xaa,0xa9,0xa7,0xa6,0xa6,0xa3,0xa5,0xa4,0xa5,0xa7,0xa7,0xaa,0xac,0xad,
+ 0xaf,0xb0,0xaf,0xb1,0xb2,0xb4,0xb3,0xb1,0xb2,0xb0,0xaf,0xb0,0xaf,0xab,0xab,0xa9,
+ 0xa8,0xa7,0xa6,0xa6,0xa6,0xa5,0xa6,0xa6,0xa6,0xa7,0xaa,0xab,0xac,0xaf,0xb1,0xb2,
+ 0xb1,0xb1,0xb0,0xb1,0xb1,0xb2,0xb1,0xb0,0xad,0xac,0xa8,0xa8,0xa7,0xa7,0xa6,0xa6,
+ 0xa4,0xa3,0xa3,0xa4,0xa4,0xa8,0xa7,0xa9,0xab,0xac,0xad,0xae,0xb0,0xb0,0xb0,0xae,
+ 0xaf,0xaf,0xb0,0xaf,0xaf,0xad,0xa8,0xa8,0xa8,0xa7,0xa7,0xa7,0xa4,0xa2,0xa5,0xa3,
+ 0xa5,0xa6,0xa7,0xa9,0xac,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb2,0xb0,0xaf,0xaf,
+ 0xac,0xab,0xaa,0xa9,0xa7,0xa7,0xa5,0xa3,0xa5,0xa5,0xa3,0xa3,0xa4,0xa5,0xa6,0xa6,
+ 0xaa,0xaa,0xab,0xab,0xa9,0xaa,0xab,0xad,0xae,0xae,0xad,0xac,0xaa,0xaa,0xab,0xa9,
+ 0xa8,0xa6,0xa5,0xa6,0xa4,0xa3,0xa3,0xa4,0xa5,0xa8,0xaa,0xa8,0xaa,0xab,0xac,0xac,
+ 0xaf,0xaf,0xae,0xae,0xb0,0xaf,0xb0,0xaf,0xad,0xae,0xaa,0xa9,0xa8,0xa8,0xa6,0xa4,
+ 0xa3,0xa2,0xa3,0xa2,0xa3,0xa3,0xa4,0xa5,0xa5,0xa8,0xa9,0xaa,0xac,0xac,0xac,0xad,
+ 0xad,0xad,0xad,0xac,0xab,0xab,0xaa,0xa8,0xa8,0xa5,0xa3,0xa3,0xa2,0xa1,0xa0,0x9f,
+ 0xa2,0xa3,0xa2,0xa4,0xa5,0xa7,0xa9,0xaa,0xad,0xad,0xad,0xae,0xaf,0xaf,0xaf,0xaf,
+ 0xad,0xac,0xaa,0xa9,0xa7,0xa5,0xa5,0xa4,0xa3,0xa3,0xa3,0xa2,0xa2,0xa3,0xa4,0xa4,
+ 0xa6,0xa7,0xa8,0xaa,0xab,0xab,0xab,0xab,0xac,0xad,0xac,0xab,0xab,0xa9,0xa8,0xa6,
+ 0xa6,0xa4,0xa3,0xa2,0xa1,0xa0,0x9f,0x9f,0xa1,0xa1,0xa1,0xa2,0xa4,0xa5,0xa6,0xa7,
+ 0xaa,0xaa,0xac,0xab,0xac,0xad,0xad,0xac,0xac,0xa9,0xab,0xa8,0xa7,0xa6,0xa3,0xa2,
+ 0xa2,0xa2,0xa0,0x9f,0xa0,0xa1,0xa2,0xa3,0xa5,0xa5,0xa4,0xa5,0xa8,0xab,0xab,0xab,
+ 0xaa,0xaa,0xaa,0xaa,0xab,0xab,0xaa,0xa7,0xa7,0xa5,0xa6,0xa4,0xa3,0xa2,0xa2,0xa2,
+ 0x9f,0xa0,0xa1,0x9f,0xa1,0xa2,0xa4,0xa6,0xa6,0xa5,0xa6,0xa8,0xab,0xab,0xab,0xa9,
+ 0xa7,0xa7,0xa7,0xa9,0xa6,0xa5,0xa3,0xa1,0xa1,0x9f,0x9e,0x9e,0x9f,0x9f,0x9f,0x9f,
+ 0xa0,0xa2,0xa4,0xa6,0xa7,0xa8,0xaa,0xaa,0xab,0xab,0xac,0xac,0xab,0xa9,0xa8,0xa8,
+ 0xa8,0xa5,0xa5,0xa3,0xa1,0xa2,0xa1,0x9f,0xa0,0xa1,0xa0,0xa0,0xa1,0xa2,0xa4,0xa4,
+ 0xa7,0xa9,0xa9,0xaa,0xab,0xab,0xab,0xaa,0xaa,0xa9,0xa9,0xa8,0xa7,0xa4,0xa4,0xa4,
+ 0xa3,0xa1,0xa1,0xa0,0x9f,0x9e,0x9f,0xa1,0xa2,0xa2,0xa3,0xa5,0xa3,0xa6,0xa0,0xae,
+ 0xb0,0xb1,0xb0,0xb0,0xaf,0xad,0xad,0xac,0xaa,0xa7,0xa7,0xa7,0xa6,0xa4,0xa2,0xa2,
+ 0xa4,0xa4,0xa5,0xa5,0xa6,0xa8,0xa9,0xab,0xac,0xac,0xae,0xae,0xae,0xae,0xae,0xae,
+ 0xaf,0xab,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa3,0xa3,0xa3,0xa3,0xa4,0xa4,0xa4,0xa6,
+ 0xa6,0xa8,0xa8,0xab,0xac,0xb0,0xae,0xae,0xae,0xb0,0xb0,0xb2,0xb0,0xaf,0xad,0xab,
+ 0xab,0xaa,0xa8,0xa6,0xa5,0xa5,0xa3,0xa3,0xa2,0xa3,0xa2,0xa4,0xa4,0xa8,0xa7,0xa7,
+ 0xaa,0xac,0xac,0xaf,0xb0,0xb1,0xaf,0xaf,0xae,0xaf,0xaf,0xad,0xab,0xaa,0xa8,0xa6,
+ 0xa5,0xa5,0xa3,0xa3,0xa4,0xa3,0xa2,0xa3,0xa4,0xa5,0xa6,0xa8,0xa9,0xaa,0xac,0xad,
+ 0xac,0xad,0xae,0xad,0xac,0xae,0xad,0xab,0xa9,0xa9,0xa7,0xa7,0xa4,0xa2,0xa0,0x9f,
+ 0x9e,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa5,0xa7,0xa8,0xab,0xad,0xad,0xad,0xac,0xac,
+ 0xac,0xab,0xab,0xab,0xab,0xa9,0xa6,0xa6,0xa5,0xa4,0xa3,0xa1,0xa2,0xa3,0xa4,0xa4,
+ 0xa4,0xa6,0xa5,0xa7,0xaa,0xab,0xaa,0xab,0xac,0xad,0xad,0xad,0xab,0xac,0xaa,0xaa,
+ 0xa7,0xa8,0xa4,0xa6,0xa3,0xa4,0xa2,0xa2,0xa1,0xa1,0xa1,0xa3,0xa3,0xa5,0xa4,0xa8,
+ 0xa7,0xa7,0xa8,0xac,0xac,0xac,0xac,0xaa,0xac,0xab,0xab,0xaa,0xa8,0xa9,0xa6,0xa4,
+ 0xa4,0xa5,0xa1,0xa2,0x9f,0xa0,0xa0,0xa1,0xa1,0xa2,0xa1,0xa2,0xa3,0xa6,0xa9,0xaa,
+ 0xa2,0xa1,0x9c,0x9e,0xa5,0xa9,0xa7,0xa0,0x98,0x96,0x98,0x9f,0xa1,0xa1,0x9c,0x99,
+ 0x9a,0x9e,0x9f,0x9f,0xa2,0xa2,0xa2,0xa4,0xa4,0xa5,0xa8,0xa8,0xa9,0xaa,0xaa,0xaa,
+ 0xaa,0xaa,0xab,0xa9,0xa9,0xa7,0xa5,0xa5,0xa2,0xa2,0x9f,0x9e,0x9d,0x9d,0x9c,0x9e,
+ 0x9e,0x9e,0xa0,0xa1,0xa1,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa7,0xa8,0xa8,0xa7,0xa7,
+ 0xa6,0xa6,0xa3,0xa4,0xa1,0xa0,0x9e,0x9c,0x9c,0x9b,0x9a,0x9b,0x9d,0x9e,0x9e,0x9e,
+ 0x9f,0xa1,0xa4,0xa5,0xa8,0xa8,0xa7,0xa8,0xa8,0xa9,0xa8,0xa7,0xa8,0xa5,0xa4,0xa2,
+ 0xa3,0xa2,0xa0,0x9d,0x9c,0x9b,0x9c,0x9b,0x9d,0x9e,0x9e,0xa0,0xa1,0xa2,0xa3,0xa4,
+ 0xa5,0xa6,0xa8,0xa9,0xa8,0xa8,0xa7,0xa7,0xa8,0xa5,0xa5,0xa4,0xa2,0xa0,0x9f,0x9f,
+ 0x9e,0x9e,0x9d,0x9c,0x9c,0x9e,0x9e,0x9f,0xa2,0xa3,0xa3,0xa4,0xa6,0xa8,0xa8,0xaa,
+ 0xa7,0xa9,0xa9,0xa7,0xa9,0xa7,0xa4,0xa4,0xa2,0xa4,0xa0,0xa0,0x9d,0x9c,0x9c,0x9d,
+ 0x9d,0x9e,0x9d,0x9f,0xa0,0xa2,0xa3,0xa3,0xa4,0xa5,0xa6,0xa9,0xaa,0xa8,0xa7,0xa6,
+ 0xa7,0xa6,0xa6,0xa4,0xa3,0xa0,0xa1,0x9f,0xa0,0x9d,0x9d,0x9d,0x9d,0x9e,0x9f,0x9f,
+ 0xa0,0xa1,0xa3,0xa3,0xa3,0xa5,0xa7,0xa9,0xa7,0xa7,0xa5,0xa7,0xa6,0xa4,0xa5,0xa3,
+ 0xa2,0xa0,0x9e,0x9e,0x9e,0x9d,0x9b,0x9c,0x9c,0x9b,0x9c,0x9e,0xa0,0xa1,0x9f,0xa1,
+ 0xa3,0xa6,0xa5,0xa6,0xa7,0xa7,0xa7,0xa9,0xa7,0xa8,0xa5,0xa6,0xa3,0xa2,0xa1,0x9f,
+ 0x9f,0x9e,0x9e,0x9e,0x96,0x94,0x94,0x96,0x9b,0xa3,0xa1,0x9c,0x9a,0x9b,0x9f,0xa6,
+ 0xa7,0xa8,0xa8,0xa7,0xa8,0xa6,0xa5,0xa5,0xa5,0xa2,0xa1,0x9f,0x9d,0x9e,0x9c,0x9b,
+ 0x9b,0x9a,0x9b,0x9c,0x9e,0xa0,0x9d,0x9f,0xa1,0xa3,0xa3,0xa5,0xa6,0xa6,0xa5,0xa4,
+ 0xa6,0xa7,0xa3,0xa2,0xa1,0xa1,0x9e,0x9e,0x9d,0x9d,0x9b,0x9a,0x99,0x9a,0x99,0x9a,
+ 0x9c,0x9c,0x9a,0x9e,0x9f,0x9f,0xa2,0xa1,0xa0,0xa2,0xa3,0xa3,0xa3,0xa2,0xa0,0xa0,
+ 0xa0,0x9e,0x9e,0x9c,0x9a,0x99,0x96,0x96,0x95,0x96,0x97,0x98,0x99,0x99,0x99,0x9b,
+ 0x9c,0xa0,0xa2,0xa1,0xa1,0xa2,0xa3,0xa3,0xa3,0xa2,0xa3,0xa1,0xa1,0x9f,0x9e,0x9d,
+ 0x9d,0x9b,0x9d,0x9b,0x9a,0x99,0x9a,0x9b,0x9c,0x9b,0x9e,0x9e,0x9f,0xa1,0xa3,0xa4,
+ 0xa4,0xa4,0xa6,0xa6,0xa7,0xa6,0xa5,0xa5,0xa4,0xa2,0xa2,0xa1,0xa0,0x9e,0x9d,0x9b,
+ 0x9b,0x9b,0x9c,0x9d,0x9e,0x9d,0x9f,0x9f,0xa1,0xa4,0xa4,0xa4,0xa6,0xa7,0xa6,0xa7,
+ 0xa8,0xa7,0xa6,0xa6,0xa6,0xa3,0xa2,0xa1,0xa0,0x9f,0x9d,0x9d,0x9b,0x9b,0x9c,0x9a,
+ 0x9a,0x9d,0x9d,0x9e,0xa0,0xa0,0xa3,0xa3,0xa5,0xa5,0xa6,0xa6,0xa5,0xa7,0xa6,0xa6,
+ 0xa3,0xa2,0xa3,0xa1,0x9e,0x9d,0x9d,0x9a,0x9a,0x9a,0x9a,0x9a,0x9b,0x9b,0x9c,0x9d,
+ 0x9f,0x9f,0xa1,0xa4,0xa7,0xa6,0xa6,0xa7,0xa5,0xa7,0xa6,0xa7,0xa4,0xa5,0xa2,0xa1,
+ 0xa0,0xa0,0x9e,0x9c,0x9e,0x9c,0x9b,0x99,0x9b,0x9b,0x9d,0x9f,0x9f,0x9f,0xa1,0xa3,
+ 0xa5,0xa5,0xa6,0xa7,0xa8,0xa7,0xa6,0xa4,0xa4,0xa3,0xa3,0xa3,0xa3,0x9f,0x9e,0x9c,
+ 0x9c,0x9b,0x9b,0x9a,0x9b,0x9c,0x9e,0x9f,0x9f,0x9e,0xa0,0xa2,0xa3,0xa5,0xa6,0xa6,
+ 0xa6,0xa5,0xa6,0xa6,0xa5,0xa5,0xa5,0xa3,0xa1,0x9d,0x9e,0x9d,0x9d,0x9b,0x9b,0x99,
+ 0x9a,0x9b,0x9c,0x9c,0x9d,0x9d,0xa0,0xa2,0xa1,0xa4,0xa5,0xa5,0xa6,0xa5,0xa6,0xa6,
+ 0xa4,0xa5,0xa5,0xa4,0xa1,0xa0,0x9d,0x9c,0x9b,0x9c,0x9c,0x9a,0x9a,0x99,0x9a,0x9b,
+ 0x9c,0x9d,0xa0,0xa1,0xa1,0xa3,0xa3,0xa4,0xa5,0xa6,0xa6,0xa5,0xa5,0xa5,0xa3,0xa3,
+ 0xa2,0x9f,0x9f,0x9f,0x9e,0x9d,0x9c,0x9a,0x99,0x9b,0x9b,0x9b,0x9d,0xa0,0x9e,0x9f,
+ 0xa2,0xa3,0xa5,0xa6,0xa6,0xa4,0xa6,0xa6,0xa5,0xa6,0xa4,0xa2,0xa1,0x9f,0xa0,0x9e,
+ 0x9d,0x9c,0x9b,0x9a,0x98,0x9a,0x9b,0x9b,0x9c,0x9c,0x9e,0x9e,0xa1,0xa1,0xa2,0xa2,
+ 0xa4,0xa4,0xa4,0xa5,0xa4,0xa4,0xa4,0xa2,0xa3,0xa1,0x9e,0x9d,0x9b,0x9b,0x9b,0x9b,
+ 0x99,0x99,0x97,0x98,0x99,0x9c,0x9d,0x9e,0x9f,0xa1,0xa1,0xa2,0xa5,0xa6,0xa5,0xa6,
+ 0xa4,0xa3,0xa3,0xa2,0xa1,0xa0,0x9f,0x9d,0x9c,0x9a,0x9a,0x99,0x98,0x98,0x99,0x9a,
+ 0x9a,0x9b,0x9c,0x9d,0xa0,0xa1,0xa1,0xa2,0xa3,0xa4,0xa4,0xa5,0xa6,0xa5,0xa3,0xa2,
+ 0xa1,0xa1,0x9f,0x9e,0x9d,0x9b,0x99,0x98,0x9a,0x99,0x99,0x99,0x9b,0x9b,0x9c,0x9f,
+ 0x9f,0xa2,0xa3,0xa2,0xa2,0xa1,0x9e,0xa7,0xa9,0xa8,0xa7,0xa6,0xa5,0xa3,0xa1,0xa0,
+ 0xa0,0xa0,0x9d,0x9e,0x9d,0x9d,0x9f,0xa0,0x9f,0x9f,0xa1,0xa4,0xa5,0xa8,0xa7,0xa7,
+ 0xa9,0xa9,0xaa,0xac,0xaa,0xaa,0xa7,0xa8,0xa7,0xa6,0xa5,0xa3,0xa1,0xa1,0x9f,0x9f,
+ 0x9f,0x9e,0x9d,0xa0,0xa1,0xa2,0xa0,0xa1,0xa2,0xa4,0xa6,0xa9,0xa8,0xa8,0xa7,0xa8,
+ 0xa9,0xa9,0xa7,0xa8,0xa7,0xa5,0xa4,0xa4,0xa3,0xa1,0x9f,0x9f,0x9d,0x9d,0x9c,0x9f,
+ 0x9f,0xa1,0xa0,0xa2,0xa2,0xa5,0xa5,0xa7,0xa7,0xa9,0xa9,0xaa,0xaa,0xa9,0xa6,0xa8,
+ 0xa6,0xa5,0xa3,0xa1,0xa0,0x9f,0x9e,0x9e,0x9c,0x9b,0x9b,0x9e,0x9e,0x9f,0xa0,0xa1,
+ 0xa3,0xa4,0xa4,0xa7,0xa6,0xa9,0xa6,0xa9,0xa7,0xa8,0xa6,0xa6,0xa4,0xa4,0xa2,0xa1,
+ 0x9f,0xa0,0x9d,0x9c,0x9b,0x9c,0x9b,0x9b,0x9d,0x9e,0x9e,0xa0,0x9f,0xa4,0xa4,0xa4,
+ 0xa6,0xa7,0xa6,0xa7,0xa6,0xa8,0xa6,0xa6,0xa5,0xa4,0xa2,0xa1,0x9f,0x9f,0x9e,0x9e,
+ 0x9b,0x9b,0x9b,0x9b,0x9b,0x9c,0x9e,0x9e,0x9f,0xa1,0xa1,0xa3,0xa5,0xa7,0xa6,0xa5,
+ 0xa5,0xa5,0xa4,0xa4,0xa3,0xa4,0xa1,0x9f,0x9d,0x9d,0x9c,0x9c,0x9b,0x9a,0x98,0x99,
+ 0x99,0x9b,0x9d,0x9d,0x9c,0x9f,0x9f,0xa1,0xa4,0xa4,0xa3,0xa4,0xa4,0xa5,0xa4,0xa4,
+ 0xa3,0xa4,0x9f,0xa1,0x9e,0x9e,0x9c,0x9c,0x9a,0x99,0x99,0x9a,0x9a,0x9b,0x99,0x9c,
+ 0x9c,0x9e,0xa1,0xa0,0xa1,0xa4,0xa4,0xa3,0xa4,0xa5,0xa4,0xa3,0xa2,0xa3,0xa3,0x9f,
+ 0x9e,0x9c,0x9b,0x9a,0x98,0x99,0x97,0x97,0x97,0x98,0x98,0x9b,0x9b,0x9d,0x9e,0x9f,
+ 0xa0,0xa2,0xa3,0xa4,0xa3,0xa3,0xa3,0xa3,0xa2,0xa4,0xa1,0xa0,0x9e,0x9c,0x9a,0x9a,
+ 0x97,0x97,0x95,0x97,0x97,0x97,0x98,0x98,0x98,0x9a,0x9d,0x9e,0x9f,0xa2,0xa2,0xa2,
+ 0xa2,0xa2,0xa1,0xa3,0xa2,0xa1,0xa0,0xa0,0x9e,0x9d,0x9b,0x9b,0x9a,0x98,0x98,0x97,
+ 0x98,0x9a,0x98,0x9a,0x9c,0x9e,0x9c,0x9f,0x9f,0xa1,0xa2,0xa4,0xa3,0xa3,0xa1,0xa3,
+ 0xa3,0xa3,0xa2,0xa1,0x9e,0x9d,0x9d,0x9c,0x9b,0x99,0x97,0x97,0x97,0x99,0x99,0x9a,
+ 0x99,0x9c,0x9c,0x9e,0x9e,0xa0,0xa1,0xa3,0xa2,0xa2,0xa3,0xa3,0xa2,0xa4,0xa1,0x9f,
+ 0x9e,0x9e,0x9b,0x9c,0x98,0x98,0x97,0x96,0x96,0x98,0x97,0x97,0x98,0x9b,0x9a,0x9c,
+ 0x9e,0xa0,0xa0,0xa0,0xa2,0xa1,0xa1,0xa1,0xa2,0xa2,0x9f,0x9f,0x9f,0x9c,0x9b,0x9b,
+ 0x9a,0x98,0x97,0x97,0x96,0x95,0x96,0x96,0x98,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,0xa0,
+ 0xa1,0xa1,0xa1,0xa2,0xa0,0xa0,0xa0,0xa2,0x9f,0x9c,0x9b,0x9a,0x9a,0x97,0x98,0x95,
+ 0x95,0x95,0x96,0x97,0x95,0x96,0x97,0x97,0x9a,0x9c,0x9d,0x9e,0x9e,0x9e,0x9f,0xa1,
+ 0xa0,0xa0,0xa0,0x9f,0x9d,0x9c,0x9a,0x9a,0x99,0x97,0x97,0x94,0x96,0x95,0x95,0x96,
+ 0x97,0x97,0x99,0x98,0x9b,0x9c,0x9c,0x9e,0x9f,0xa0,0xa1,0xa0,0xa0,0xa0,0xa0,0x9f,
+ 0x9e,0x9d,0x9d,0x99,0x99,0x99,0x98,0x95,0x97,0x96,0x94,0x96,0x95,0x97,0x98,0x9a,
+ 0x9a,0x9c,0x9d,0x9d,0x9e,0xa0,0xa0,0x9f,0xa1,0xa0,0x9e,0x9f,0x9f,0x9e,0x9b,0x9a,
+ 0x98,0x98,0x98,0x97,0x93,0x93,0x94,0x94,0x95,0x96,0x98,0x98,0x9a,0x9c,0x9d,0x9c,
+ 0x9d,0x9e,0x9e,0xa0,0x9f,0x9f,0x9e,0x9d,0x9d,0x9e,0x9b,0x9b,0x99,0x97,0x96,0x96,
+ 0x95,0x96,0x96,0x95,0x95,0x95,0x97,0x98,0x99,0x9b,0x9c,0x9e,0x9f,0x9e,0xa0,0xa1,
+ 0xa0,0xa0,0x9f,0x9f,0x9d,0x9d,0x9c,0x9c,0x99,0x99,0x96,0x95,0x94,0x95,0x93,0x93,
+ 0x92,0x94,0x96,0x96,0x97,0x96,0x97,0x9b,0x9c,0x9d,0x9d,0x9d,0x9d,0x9e,0x9d,0x9e,
+ 0x9c,0x9b,0x9a,0x9a,0x98,0x98,0x96,0x96,0x93,0x93,0x92,0x94,0x93,0x94,0x94,0x95,
+ 0x96,0x97,0x96,0x99,0x9b,0x9b,0x9b,0x9c,0x9c,0x9b,0x9b,0x9d,0x9c,0x9b,0x9a,0x99,
+ 0x96,0x95,0x94,0x94,0x93,0x93,0x91,0x91,0x93,0x92,0x92,0x93,0x93,0x93,0x95,0x97,
+ 0x99,0x99,0x99,0x99,0x99,0x9b,0x9c,0x9c,0x9b,0x98,0x98,0x96,0x97,0x97,0x94,0x93,
+ 0x92,0x90,0x8f,0x8e,0x8f,0x8f,0x90,0x91,0x92,0x92,0x95,0x96,0x98,0x97,0x98,0x9a,
+ 0x99,0x99,0x99,0x9a,0x99,0x97,0x97,0x97,0x95,0x95,0x92,0x8f,0x8e,0x8f,0x8e,0x8c,
+ 0x8f,0x8e,0x8d,0x90,0x91,0x92,0x94,0x96,0x97,0x98,0x97,0x99,0x99,0x98,0x99,0x99,
+ 0x99,0x99,0x98,0x96,0x97,0x93,0x92,0x92,0x91,0x8f,0x8e,0x8c,0x8d,0x8d,0x8e,0x90,
+ 0x90,0x90,0x91,0x93,0x94,0x96,0x96,0x98,0x98,0x99,0x99,0x9a,0x99,0x9a,0x98,0x98,
+ 0x96,0x95,0x93,0x93,0x91,0x90,0x8f,0x8d,0x8c,0x8d,0x8e,0x90,0x90,0x8f,0x90,0x91,
+ 0x93,0x96,0x96,0x97,0x97,0x98,0x97,0x97,0x97,0x98,0x97,0x95,0x93,0x93,0x93,0x92,
+ 0x91,0x90,0x8e,0x8e,0x8e,0x8c,0x8d,0x8e,0x8e,0x90,0x8e,0x8f,0x91,0x93,0x93,0x94,
+ 0x94,0x95,0x95,0x96,0x95,0x95,0x95,0x95,0x94,0x93,0x90,0x90,0x90,0x8f,0x8d,0x8d,
+ 0x8b,0x8c,0x8b,0x8b,0x8d,0x8c,0x8d,0x8e,0x8f,0x90,0x92,0x91,0x93,0x93,0x95,0x95,
+ 0x95,0x94,0x95,0x93,0x92,0x91,0x91,0x8e,0x8e,0x8d,0x8b,0x8b,0x8a,0x8a,0x89,0x89,
+ 0x89,0x89,0x8a,0x8b,0x8c,0x8c,0x8f,0x91,0x90,0x92,0x92,0x92,0x92,0x93,0x92,0x92,
+ 0x91,0x90,0x8f,0x8f,0x8b,0x8c,0x8a,0x89,0x88,0x87,0x85,0x86,0x85,0x88,0x88,0x88,
+ 0x89,0x8c,0x8d,0x8e,0x8f,0x90,0x90,0x91,0x91,0x91,0x92,0x91,0x91,0x91,0x8f,0x8f,
+ 0x8c,0x8d,0x8a,0x8b,0x8a,0x86,0x86,0x86,0x85,0x86,0x87,0x89,0x8a,0x8b,0x8a,0x8c,
+ 0x8e,0x90,0x90,0x92,0x90,0x90,0x92,0x93,0x90,0x91,0x90,0x90,0x8d,0x8d,0x8c,0x8a,
+ 0x8a,0x89,0x87,0x88,0x87,0x89,0x87,0x8a,0x8a,0x8b,0x8c,0x8e,0x8d,0x90,0x8f,0x91,
+ 0x92,0x92,0x92,0x92,0x91,0x92,0x90,0x90,0x8e,0x8f,0x8c,0x8c,0x8b,0x8b,0x89,0x88,
+ 0x89,0x88,0x89,0x8a,0x8c,0x8b,0x8e,0x8d,0x90,0x90,0x92,0x91,0x91,0x8f,0x8b,0x6f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf6,0x01,0x17,0x0f,0xd0,0x10,0x08,0x01,
+ 0xb8,0xa1,0x34,0x01,0x78,0x01,0x34,0x01,0x42,0x46,0x41,0x45,0x43,0x48,0x43,0x48,
+ 0x3d,0x44,0x40,0x44,0x41,0x49,0x3e,0x4d,0x43,0x44,0x3f,0x44,0x3e,0x47,0x41,0x49,
+ 0x3f,0x44,0x3d,0x46,0x42,0x45,0x40,0x47,0x40,0x47,0x3f,0x44,0x3e,0x47,0x40,0x4a,
+ 0x3d,0x49,0x3d,0x45,0x3f,0x4a,0x3f,0x49,0x3c,0x44,0x3f,0x42,0x3f,0x46,0x3f,0x49,
+ 0x3f,0x47,0x3e,0x43,0x40,0x46,0x3e,0x49,0x3e,0x45,0x3e,0x4a,0x40,0x47,0x3d,0x48,
+ 0x41,0x47,0x3e,0x45,0x3e,0x46,0x3e,0x45,0x42,0x46,0x3e,0x44,0x41,0x47,0x41,0x47,
+ 0x3f,0x44,0x3d,0x44,0x3f,0x47,0x3a,0x48,0x40,0x46,0x3e,0x45,0x3f,0x48,0x3e,0x49,
+ 0x41,0x45,0x3d,0x48,0x3d,0x47,0x41,0x4c,0x3f,0x43,0x41,0x45,0x41,0x47,0x3f,0x46,
+ 0x42,0x44,0x3f,0x44,0x3f,0x47,0x3e,0x44,0x41,0x47,0x41,0x47,0x42,0x46,0x3c,0x47,
+ 0x41,0x45,0x3e,0x44,0x40,0x45,0x3f,0x47,0x3d,0x45,0x3b,0x44,0x3f,0x42,0x3c,0x48,
+ 0x41,0x46,0x3c,0x45,0x3d,0x49,0x3d,0x48,0x40,0x48,0x40,0x44,0x41,0x47,0x3d,0x47,
+ 0x3d,0x41,0x3f,0x49,0x3f,0x48,0x3c,0x45,0x3d,0x44,0x3e,0x46,0x3e,0x47,0x3f,0x48,
+ 0x3f,0x43,0x41,0x44,0x40,0x49,0x3d,0x47,0x3e,0x47,0x3e,0x46,0x40,0x44,0x40,0x48,
+ 0x41,0x49,0x3e,0x43,0x40,0x44,0x3d,0x46,0x3e,0x47,0x41,0x42,0x40,0x48,0x3f,0x4a,
+ 0x40,0x44,0x3e,0x45,0x3d,0x48,0x3f,0x46,0x3f,0x45,0x41,0x43,0x40,0x46,0x3d,0x47,
+ 0x3e,0x46,0x3d,0x47,0x42,0x47,0x3e,0x44,0x3e,0x43,0x3e,0x44,0x41,0x47,0x3f,0x46,
+ 0x40,0x41,0x3f,0x44,0x3f,0x48,0x3d,0x49,0x3d,0x43,0x3d,0x44,0x40,0x45,0x3e,0x47,
+ 0x40,0x46,0x3c,0x46,0x40,0x46,0x3f,0x48,0x41,0x45,0x3f,0x46,0x43,0x45,0x3a,0x46,
+ 0x3f,0x49,0x3c,0x43,0x40,0x44,0x3d,0x46,0x3e,0x48,0x3d,0x45,0x41,0x45,0x3b,0x49,
+ 0x3f,0x44,0x40,0x4a,0x40,0x47,0x3e,0x4b,0x3d,0x44,0x40,0x43,0x3e,0x4b,0x3b,0x47,
+ 0x40,0x47,0x3c,0x46,0x3f,0x49,0x3c,0x48,0x3e,0x44,0x3a,0x44,0x3f,0x47,0x3f,0x49,
+ 0x3d,0x44,0x3e,0x47,0x3c,0x47,0x3a,0x46,0x3f,0x42,0x40,0x44,0x3e,0x46,0x3b,0x43,
+ 0x3f,0x44,0x3d,0x44,0x40,0x48,0x3d,0x49,0x3c,0x43,0x3c,0x43,0x41,0x44,0x3e,0x49,
+ 0x3f,0x41,0x3e,0x41,0x3d,0x44,0x3f,0x48,0x42,0x44,0x3d,0x44,0x40,0x45,0x3e,0x44,
+ 0x3f,0x43,0x3d,0x44,0x3b,0x43,0x3e,0x47,0x41,0x48,0x3c,0x44,0x3e,0x47,0x3e,0x45,
+ 0x3d,0x45,0x3d,0x41,0x3d,0x44,0x3d,0x44,0x3d,0x43,0x3d,0x45,0x3f,0x44,0x3b,0x47,
+ 0x3e,0x42,0x3c,0x45,0x3f,0x44,0x39,0x4a,0x3d,0x42,0x3c,0x46,0x3f,0x45,0x3a,0x44,
+ 0x3e,0x45,0x39,0x44,0x3d,0x45,0x3e,0x48,0x3d,0x42,0x3e,0x44,0x3f,0x43,0x3b,0x48,
+ 0x3f,0x43,0x3b,0x44,0x3f,0x46,0x3b,0x44,0x3f,0x41,0x3e,0x43,0x40,0x46,0x3c,0x45,
+ 0x3d,0x47,0x3e,0x44,0x3f,0x49,0x3e,0x48,0x3e,0x45,0x3d,0x46,0x3f,0x46,0x3d,0x47,
+ 0x3e,0x46,0x3b,0x47,0x41,0x46,0x3b,0x48,0x3f,0x47,0x40,0x45,0x3e,0x47,0x40,0x48,
+ 0x3f,0x43,0x3d,0x48,0x3e,0x44,0x3d,0x46,0x3b,0x43,0x3e,0x47,0x3c,0x45,0x3c,0x47,
+ 0x3c,0x47,0x3e,0x45,0x3e,0x45,0x3f,0x45,0x39,0x45,0x3c,0x47,0x3e,0x45,0x3e,0x47,
+ 0x3d,0x46,0x3e,0x47,0x3f,0x45,0x3a,0x49,0x3b,0x43,0x3e,0x45,0x3f,0x46,0x3d,0x43,
+ 0x3d,0x44,0x3d,0x42,0x41,0x46,0x3d,0x44,0x41,0x44,0x3d,0x44,0x40,0x45,0x3c,0x45,
+ 0x3d,0x45,0x3d,0x45,0x3c,0x45,0x3e,0x48,0x3e,0x43,0x3d,0x45,0x3f,0x46,0x3e,0x46,
+ 0x3c,0x45,0x3e,0x49,0x3c,0x43,0x3a,0x44,0x3e,0x46,0x40,0x48,0x3f,0x45,0x3c,0x47,
+ 0x3d,0x47,0x3e,0x47,0x40,0x41,0x3e,0x47,0x3c,0x44,0x3e,0x41,0x3c,0x45,0x3d,0x45,
+ 0x3c,0x43,0x3f,0x46,0x40,0x42,0x3d,0x45,0x3f,0x45,0x3b,0x46,0x3c,0x46,0x3d,0x47,
+ 0x3d,0x43,0x3c,0x46,0x3d,0x43,0x3d,0x44,0x3f,0x43,0x3d,0x45,0x3e,0x45,0x3b,0x49,
+ 0x3e,0x46,0x3e,0x46,0x3e,0x44,0x3e,0x47,0x3e,0x45,0x3d,0x44,0x3e,0x43,0x3d,0x44,
+ 0x3e,0x44,0x3c,0x46,0x3c,0x44,0x3c,0x49,0x3d,0x41,0x3e,0x47,0x3d,0x43,0x3b,0x46,
+ 0x3a,0x44,0x3b,0x46,0x3f,0x43,0x3e,0x48,0x3b,0x43,0x3b,0x46,0x40,0x42,0x3b,0x46,
+ 0x3d,0x42,0x3f,0x45,0x3e,0x45,0x41,0x44,0x41,0x43,0x3a,0x46,0x3d,0x46,0x40,0x46,
+ 0x3c,0x42,0x3e,0x47,0x3f,0x45,0x3d,0x44,0x3d,0x44,0x38,0x42,0x3d,0x42,0x3d,0x45,
+ 0x3d,0x46,0x3f,0x44,0x3e,0x44,0x3b,0x43,0x3c,0x47,0x3d,0x46,0x3e,0x40,0x3c,0x45,
+ 0x3c,0x43,0x3d,0x43,0x42,0x43,0x39,0x45,0x3d,0x47,0x3d,0x44,0x3e,0x44,0x3e,0x48,
+ 0x3d,0x43,0x3d,0x46,0x3c,0x46,0x3d,0x45,0x39,0x43,0x3d,0x42,0x3e,0x45,0x3a,0x45,
+ 0x3a,0x44,0x3b,0x43,0x3e,0x44,0x3c,0x45,0x40,0x44,0x3e,0x45,0x3f,0x3f,0x3d,0x46,
+ 0x3e,0x44,0x3c,0x45,0x3b,0x44,0x3d,0x43,0x3e,0x42,0x3f,0x44,0x40,0x47,0x3c,0x44,
+ 0x3e,0x44,0x3b,0x45,0x3c,0x47,0x3b,0x41,0x3b,0x43,0x3b,0x44,0x3e,0x43,0x3d,0x42,
+ 0x3c,0x44,0x3d,0x43,0x39,0x43,0x3c,0x47,0x3e,0x44,0x3d,0x46,0x3f,0x45,0x3c,0x41,
+ 0x38,0x48,0x3c,0x44,0x3c,0x46,0x3d,0x44,0x3b,0x44,0x3b,0x44,0x3c,0x47,0x3b,0x44,
+ 0x3e,0x43,0x3c,0x43,0x3c,0x45,0x3b,0x46,0x3f,0x45,0x3a,0x43,0x43,0x44,0x3a,0x46,
+ 0x3e,0x44,0x3d,0x44,0x3c,0x41,0x3e,0x42,0x3d,0x44,0x3b,0x43,0x3e,0x43,0x3e,0x40,
+ 0x3e,0x44,0x3b,0x46,0x3d,0x42,0x3b,0x41,0x3f,0x42,0x3b,0x45,0x3d,0x42,0x39,0x47,
+ 0x3c,0x44,0x3e,0x46,0x3e,0x43,0x3b,0x46,0x3b,0x43,0x3d,0x43,0x3c,0x42,0x3a,0x45,
+ 0x3d,0x45,0x39,0x45,0x3d,0x43,0x3d,0x44,0x40,0x43,0x3d,0x46,0x40,0x46,0x3a,0x3b,
+ 0x35,0x3e,0x3c,0x46,0x3d,0x43,0x43,0x48,0x42,0x47,0x46,0x50,0x4a,0x4c,0x40,0x4a,
+ 0x43,0x46,0x3f,0x4a,0x43,0x48,0x41,0x48,0x41,0x46,0x44,0x47,0x42,0x4a,0x43,0x4a,
+ 0x43,0x48,0x41,0x46,0x41,0x47,0x41,0x47,0x44,0x44,0x42,0x47,0x42,0x46,0x3f,0x46,
+ 0x41,0x47,0x43,0x48,0x42,0x48,0x43,0x49,0x41,0x48,0x41,0x48,0x45,0x43,0x40,0x47,
+ 0x43,0x47,0x41,0x48,0x43,0x49,0x41,0x4a,0x41,0x49,0x41,0x47,0x3f,0x44,0x41,0x4a,
+ 0x43,0x42,0x3f,0x48,0x41,0x47,0x42,0x4b,0x41,0x46,0x41,0x45,0x40,0x49,0x3f,0x48,
+ 0x42,0x49,0x3e,0x4a,0x41,0x45,0x40,0x49,0x40,0x48,0x3e,0x48,0x40,0x45,0x41,0x46,
+ 0x43,0x49,0x3f,0x48,0x3d,0x48,0x41,0x4a,0x43,0x47,0x3f,0x48,0x3f,0x46,0x40,0x4a,
+ 0x40,0x48,0x3e,0x49,0x42,0x46,0x41,0x49,0x40,0x46,0x3f,0x47,0x42,0x4a,0x40,0x45,
+ 0x41,0x45,0x41,0x47,0x41,0x48,0x41,0x4a,0x3e,0x49,0x3e,0x49,0x42,0x48,0x3f,0x48,
+ 0x42,0x49,0x42,0x48,0x42,0x46,0x3f,0x48,0x42,0x48,0x43,0x49,0x42,0x49,0x40,0x4a,
+ 0x44,0x45,0x3e,0x48,0x41,0x49,0x41,0x48,0x42,0x45,0x41,0x47,0x41,0x45,0x41,0x45,
+ 0x40,0x48,0x3e,0x48,0x41,0x47,0x3d,0x4c,0x42,0x47,0x40,0x49,0x41,0x48,0x3f,0x4a,
+ 0x42,0x48,0x3c,0x4c,0x43,0x45,0x3c,0x47,0x44,0x48,0x3d,0x48,0x3d,0x48,0x3e,0x49,
+ 0x41,0x45,0x40,0x48,0x42,0x4a,0x42,0x4d,0x44,0x44,0x40,0x48,0x43,0x46,0x3f,0x4a,
+ 0x42,0x46,0x42,0x46,0x43,0x49,0x3e,0x48,0x41,0x49,0x40,0x4c,0x43,0x45,0x41,0x47,
+ 0x46,0x46,0x43,0x46,0x42,0x47,0x3e,0x48,0x43,0x48,0x41,0x46,0x42,0x48,0x3e,0x4b,
+ 0x43,0x49,0x3f,0x46,0x42,0x47,0x40,0x49,0x43,0x44,0x41,0x47,0x3d,0x47,0x3e,0x4a,
+ 0x3e,0x48,0x41,0x4b,0x42,0x44,0x40,0x4c,0x3f,0x46,0x42,0x48,0x42,0x49,0x40,0x47,
+ 0x44,0x47,0x40,0x48,0x40,0x49,0x41,0x49,0x3f,0x49,0x40,0x46,0x41,0x4b,0x3f,0x46,
+ 0x41,0x48,0x41,0x48,0x41,0x47,0x3c,0x48,0x45,0x45,0x3d,0x46,0x44,0x47,0x42,0x4b,
+ 0x44,0x4a,0x40,0x48,0x42,0x47,0x42,0x4a,0x3f,0x48,0x3f,0x47,0x42,0x48,0x3c,0x4a,
+ 0x3e,0x45,0x3f,0x48,0x46,0x49,0x3f,0x49,0x42,0x44,0x40,0x48,0x42,0x46,0x42,0x45,
+ 0x3e,0x45,0x3f,0x4c,0x40,0x46,0x41,0x48,0x42,0x4a,0x44,0x49,0x3d,0x48,0x3f,0x48,
+ 0x40,0x47,0x3f,0x47,0x3f,0x47,0x43,0x4a,0x42,0x49,0x41,0x44,0x42,0x49,0x3f,0x49,
+ 0x42,0x46,0x42,0x46,0x41,0x47,0x43,0x4c,0x44,0x47,0x43,0x49,0x41,0x49,0x41,0x49,
+ 0x41,0x45,0x41,0x46,0x44,0x45,0x41,0x47,0x40,0x47,0x3f,0x48,0x45,0x49,0x3e,0x47,
+ 0x42,0x46,0x42,0x47,0x44,0x48,0x40,0x4b,0x43,0x47,0x40,0x49,0x42,0x49,0x42,0x45,
+ 0x43,0x44,0x41,0x48,0x40,0x46,0x42,0x49,0x42,0x46,0x41,0x4c,0x43,0x47,0x41,0x4a,
+ 0x43,0x46,0x40,0x47,0x42,0x47,0x3d,0x49,0x40,0x48,0x40,0x47,0x45,0x49,0x3e,0x47,
+ 0x44,0x48,0x42,0x4b,0x44,0x4b,0x40,0x4b,0x41,0x4b,0x40,0x49,0x40,0x47,0x43,0x49,
+ 0x42,0x47,0x42,0x4a,0x43,0x48,0x3f,0x4a,0x43,0x48,0x3f,0x4b,0x44,0x4a,0x40,0x48,
+ 0x43,0x47,0x42,0x47,0x45,0x4a,0x42,0x4c,0x43,0x47,0x41,0x46,0x45,0x4d,0x41,0x49,
+ 0x41,0x49,0x43,0x49,0x44,0x4a,0x42,0x4a,0x40,0x49,0x3e,0x49,0x44,0x4c,0x42,0x4a,
+ 0x44,0x47,0x40,0x4a,0x42,0x49,0x43,0x4f,0x41,0x49,0x42,0x49,0x44,0x49,0x42,0x48,
+ 0x46,0x48,0x40,0x4a,0x41,0x48,0x41,0x4b,0x43,0x45,0x3f,0x4a,0x3e,0x4b,0x3e,0x4b,
+ 0x44,0x48,0x42,0x48,0x44,0x47,0x43,0x48,0x43,0x4b,0x40,0x49,0x45,0x47,0x42,0x4a,
+ 0x45,0x45,0x40,0x45,0x42,0x4a,0x3f,0x48,0x42,0x48,0x43,0x48,0x41,0x4a,0x3d,0x4a,
+ 0x40,0x48,0x3f,0x44,0x43,0x4c,0x42,0x47,0x43,0x47,0x42,0x49,0x41,0x4a,0x3e,0x4a,
+ 0x40,0x48,0x43,0x46,0x40,0x49,0x42,0x49,0x43,0x47,0x42,0x48,0x42,0x4c,0x3f,0x4a,
+ 0x41,0x49,0x3f,0x46,0x3f,0x48,0x42,0x4b,0x44,0x48,0x44,0x48,0x43,0x48,0x41,0x4a,
+ 0x42,0x45,0x40,0x48,0x43,0x4a,0x42,0x4b,0x40,0x49,0x41,0x47,0x42,0x4b,0x41,0x48,
+ 0x42,0x48,0x3d,0x4a,0x41,0x48,0x42,0x48,0x41,0x45,0x43,0x48,0x43,0x4a,0x40,0x4a,
+ 0x44,0x49,0x43,0x48,0x40,0x4c,0x40,0x4c,0x43,0x46,0x42,0x4a,0x46,0x48,0x41,0x47,
+ 0x42,0x48,0x41,0x49,0x44,0x47,0x41,0x49,0x42,0x4b,0x42,0x45,0x41,0x49,0x3f,0x49,
+ 0x43,0x47,0x3e,0x4a,0x43,0x46,0x42,0x4b,0x44,0x46,0x42,0x48,0x42,0x47,0x3e,0x4a,
+ 0x40,0x4a,0x40,0x47,0x42,0x4c,0x43,0x47,0x43,0x47,0x40,0x47,0x43,0x48,0x40,0x48,
+ 0x45,0x49,0x42,0x47,0x41,0x47,0x41,0x49,0x43,0x49,0x3f,0x48,0x41,0x4a,0x3c,0x4a,
+ 0x40,0x49,0x3e,0x46,0x40,0x47,0x42,0x4c,0x40,0x46,0x41,0x47,0x42,0x4a,0x40,0x4e,
+ 0x44,0x47,0x41,0x47,0x42,0x48,0x42,0x4b,0x42,0x44,0x40,0x46,0x43,0x47,0x41,0x4a,
+ 0x41,0x49,0x3f,0x4a,0x41,0x49,0x43,0x4b,0x40,0x4a,0x41,0x46,0x42,0x47,0x41,0x47,
+ 0x42,0x49,0x44,0x4a,0x3e,0x4a,0x40,0x49,0x41,0x44,0x41,0x49,0x42,0x4a,0x42,0x4b,
+ 0x41,0x46,0x40,0x44,0x42,0x49,0x40,0x48,0x41,0x47,0x3e,0x48,0x41,0x49,0x44,0x48,
+ 0x3f,0x49,0x45,0x49,0x44,0x49,0x42,0x4d,0x41,0x48,0x40,0x48,0x40,0x48,0x40,0x4b,
+ 0x43,0x48,0x41,0x46,0x41,0x47,0x3f,0x4b,0x44,0x48,0x42,0x45,0x42,0x4b,0x42,0x4a,
+ 0x43,0x47,0x3e,0x45,0x44,0x49,0x45,0x49,0x40,0x49,0x3f,0x48,0x3f,0x47,0x40,0x48,
+ 0x42,0x4c,0x40,0x47,0x42,0x46,0x42,0x49,0x43,0x44,0x41,0x47,0x45,0x48,0x42,0x48,
+ 0x40,0x46,0x3f,0x48,0x45,0x4a,0x40,0x4c,0x42,0x4a,0x42,0x49,0x42,0x4a,0x40,0x4b,
+ 0x42,0x49,0x3e,0x47,0x45,0x4c,0x3b,0x42,0x3b,0x42,0x41,0x46,0x47,0x47,0x47,0x44,
+ 0x40,0x45,0x42,0x4d,0x42,0x4d,0x42,0x49,0x43,0x46,0x3f,0x43,0x3f,0x45,0x40,0x47,
+ 0x3e,0x43,0x3f,0x45,0x40,0x49,0x3e,0x48,0x3f,0x48,0x42,0x45,0x42,0x48,0x3d,0x4c,
+ 0x41,0x46,0x3d,0x47,0x3f,0x46,0x3f,0x46,0x42,0x42,0x3e,0x45,0x3f,0x49,0x3e,0x47,
+ 0x3f,0x4a,0x3d,0x46,0x3e,0x45,0x3e,0x48,0x41,0x45,0x3f,0x45,0x41,0x48,0x3e,0x4a,
+ 0x41,0x47,0x3e,0x48,0x42,0x4a,0x3e,0x47,0x41,0x44,0x42,0x44,0x43,0x47,0x3f,0x47,
+ 0x40,0x49,0x3f,0x45,0x42,0x48,0x3e,0x48,0x41,0x45,0x3f,0x48,0x41,0x48,0x3b,0x4a,
+ 0x41,0x45,0x42,0x42,0x40,0x44,0x40,0x4a,0x3e,0x47,0x40,0x43,0x3f,0x48,0x3c,0x45,
+ 0x41,0x48,0x40,0x46,0x3f,0x47,0x40,0x47,0x40,0x44,0x3e,0x44,0x42,0x49,0x3f,0x48,
+ 0x42,0x47,0x3d,0x47,0x40,0x45,0x40,0x47,0x40,0x47,0x3d,0x45,0x42,0x49,0x3f,0x48,
+ 0x42,0x45,0x3e,0x44,0x3f,0x46,0x3c,0x49,0x40,0x43,0x3e,0x47,0x3f,0x47,0x3d,0x4c,
+ 0x3e,0x48,0x40,0x45,0x3c,0x47,0x3d,0x48,0x40,0x43,0x3d,0x43,0x41,0x48,0x40,0x4a,
+ 0x41,0x45,0x3e,0x45,0x3f,0x48,0x40,0x49,0x3f,0x48,0x3e,0x48,0x3e,0x49,0x40,0x49,
+ 0x40,0x45,0x40,0x48,0x41,0x46,0x44,0x49,0x41,0x48,0x40,0x48,0x3d,0x45,0x3d,0x47,
+ 0x40,0x46,0x3f,0x49,0x45,0x4a,0x3e,0x4b,0x40,0x47,0x40,0x47,0x41,0x46,0x42,0x49,
+ 0x40,0x46,0x40,0x45,0x42,0x46,0x3e,0x4a,0x40,0x43,0x41,0x44,0x41,0x45,0x3f,0x44,
+ 0x40,0x44,0x40,0x4b,0x43,0x48,0x40,0x4b,0x42,0x47,0x41,0x47,0x42,0x48,0x41,0x47,
+ 0x40,0x46,0x40,0x48,0x3f,0x49,0x3c,0x48,0x3e,0x47,0x3e,0x45,0x3f,0x48,0x3f,0x48,
+ 0x3f,0x44,0x40,0x47,0x40,0x4a,0x41,0x48,0x43,0x44,0x3f,0x43,0x41,0x48,0x41,0x4b,
+ 0x3f,0x45,0x3e,0x44,0x3d,0x46,0x3d,0x47,0x40,0x46,0x3d,0x47,0x3e,0x4a,0x3f,0x4b,
+ 0x40,0x44,0x41,0x44,0x43,0x47,0x3f,0x4c,0x40,0x49,0x40,0x44,0x40,0x46,0x3f,0x47,
+ 0x3f,0x46,0x43,0x46,0x44,0x49,0x3b,0x46,0x45,0x47,0x40,0x46,0x43,0x47,0x40,0x48,
+ 0x3f,0x46,0x3e,0x48,0x41,0x49,0x41,0x4b,0x41,0x45,0x3f,0x44,0x40,0x46,0x3f,0x49,
+ 0x40,0x46,0x40,0x43,0x3f,0x4a,0x3f,0x47,0x42,0x44,0x3e,0x46,0x40,0x49,0x3f,0x48,
+ 0x42,0x47,0x3d,0x44,0x40,0x48,0x3d,0x49,0x42,0x46,0x3d,0x47,0x3d,0x47,0x3e,0x49,
+ 0x42,0x43,0x3e,0x45,0x41,0x47,0x3f,0x45,0x44,0x46,0x40,0x46,0x40,0x45,0x3f,0x46,
+ 0x41,0x45,0x3f,0x42,0x41,0x46,0x3f,0x4b,0x41,0x48,0x3f,0x44,0x3f,0x46,0x3f,0x47,
+ 0x41,0x45,0x3e,0x46,0x3f,0x4a,0x3f,0x49,0x3e,0x43,0x40,0x45,0x3e,0x47,0x3f,0x4a,
+ 0x40,0x44,0x40,0x46,0x3d,0x4a,0x3d,0x48,0x3f,0x47,0x41,0x44,0x41,0x47,0x3d,0x49,
+ 0x3e,0x47,0x3f,0x45,0x3f,0x4a,0x3d,0x48,0x3e,0x46,0x3d,0x45,0x40,0x45,0x3f,0x49,
+ 0x40,0x46,0x3c,0x43,0x40,0x47,0x43,0x49,0x44,0x46,0x40,0x48,0x3e,0x48,0x3c,0x4b,
+ 0x3d,0x47,0x40,0x47,0x40,0x47,0x40,0x49,0x42,0x48,0x3d,0x47,0x40,0x47,0x3f,0x48,
+ 0x43,0x47,0x3f,0x46,0x41,0x47,0x3f,0x4a,0x44,0x43,0x41,0x45,0x43,0x47,0x42,0x47,
+ 0x40,0x48,0x40,0x47,0x41,0x49,0x41,0x4b,0x40,0x43,0x3c,0x46,0x43,0x48,0x40,0x48,
+ 0x40,0x48,0x3c,0x45,0x40,0x46,0x42,0x49,0x41,0x47,0x3f,0x49,0x41,0x48,0x40,0x4a,
+ 0x41,0x45,0x40,0x47,0x41,0x49,0x41,0x4b,0x3f,0x47,0x41,0x46,0x41,0x48,0x41,0x4a,
+ 0x42,0x46,0x40,0x47,0x40,0x49,0x3e,0x48,0x3f,0x47,0x3f,0x44,0x42,0x46,0x3e,0x48,
+ 0x40,0x46,0x3f,0x4c,0x41,0x49,0x3f,0x4d,0x41,0x48,0x40,0x4a,0x43,0x46,0x3f,0x4a,
+ 0x40,0x49,0x3f,0x48,0x40,0x48,0x3e,0x4b,0x42,0x45,0x3f,0x45,0x45,0x47,0x42,0x46,
+ 0x44,0x45,0x3f,0x45,0x3e,0x4a,0x40,0x4c,0x3f,0x46,0x43,0x48,0x41,0x48,0x3f,0x4a,
+ 0x41,0x48,0x3f,0x46,0x42,0x4a,0x43,0x4a,0x42,0x49,0x42,0x47,0x41,0x49,0x41,0x4b,
+ 0x41,0x44,0x40,0x48,0x40,0x48,0x40,0x48,0x3f,0x48,0x3b,0x46,0x42,0x4b,0x40,0x4b,
+ 0x3f,0x46,0x40,0x47,0x44,0x46,0x3f,0x49,0x43,0x48,0x41,0x48,0x42,0x4c,0x3f,0x49,
+ 0x43,0x47,0x42,0x46,0x45,0x46,0x3f,0x47,0x3f,0x48,0x43,0x48,0x41,0x46,0x3d,0x47,
+ 0x3f,0x48,0x3e,0x46,0x42,0x45,0x40,0x48,0x3f,0x47,0x3f,0x4b,0x41,0x46,0x3e,0x49,
+ 0x41,0x47,0x41,0x4c,0x3e,0x47,0x41,0x4a,0x42,0x46,0x41,0x48,0x3f,0x48,0x3f,0x48,
+ 0x43,0x47,0x41,0x47,0x42,0x4a,0x3e,0x49,0x42,0x46,0x3f,0x47,0x40,0x47,0x3f,0x48,
+ 0x42,0x44,0x42,0x46,0x40,0x46,0x3c,0x4b,0x3f,0x46,0x43,0x47,0x40,0x4b,0x3f,0x4b,
+ 0x42,0x47,0x3e,0x44,0x46,0x46,0x42,0x49,0x40,0x47,0x42,0x48,0x3f,0x46,0x3f,0x48,
+ 0x41,0x46,0x3e,0x48,0x3e,0x4b,0x40,0x46,0x41,0x43,0x41,0x47,0x42,0x45,0x3e,0x48,
+ 0x42,0x48,0x42,0x44,0x43,0x47,0x3f,0x47,0x40,0x46,0x42,0x46,0x42,0x46,0x3f,0x4a,
+ 0x41,0x44,0x3e,0x47,0x41,0x44,0x41,0x46,0x41,0x46,0x3e,0x47,0x41,0x49,0x40,0x48,
+ 0x42,0x48,0x3e,0x47,0x41,0x48,0x40,0x44,0x40,0x43,0x3f,0x48,0x3f,0x46,0x41,0x48,
+ 0x41,0x45,0x3b,0x49,0x44,0x47,0x3d,0x48,0x41,0x46,0x3e,0x47,0x3f,0x48,0x3f,0x49,
+ 0x40,0x46,0x3f,0x49,0x40,0x47,0x41,0x45,0x43,0x46,0x3e,0x45,0x42,0x4a,0x40,0x49,
+ 0x43,0x46,0x42,0x47,0x42,0x47,0x40,0x4a,0x41,0x47,0x40,0x49,0x42,0x48,0x40,0x4b,
+ 0x42,0x44,0x3e,0x47,0x41,0x46,0x41,0x47,0x44,0x48,0x3f,0x48,0x40,0x46,0x42,0x4a,
+ 0x42,0x45,0x43,0x47,0x44,0x48,0x3e,0x4a,0x43,0x46,0x40,0x4a,0x40,0x45,0x41,0x46,
+ 0x41,0x46,0x3e,0x46,0x42,0x45,0x42,0x48,0x42,0x46,0x41,0x4b,0x42,0x4d,0x3d,0x40,
+ 0x39,0x43,0x3c,0x47,0x41,0x45,0x44,0x41,0x3c,0x40,0x43,0x4e,0x43,0x45,0x3d,0x46,
+ 0x3c,0x44,0x3a,0x43,0x3e,0x40,0x39,0x45,0x3e,0x41,0x39,0x44,0x3d,0x43,0x41,0x45,
+ 0x3d,0x42,0x3a,0x41,0x3a,0x42,0x3c,0x42,0x3d,0x43,0x3a,0x44,0x3b,0x46,0x3a,0x46,
+ 0x3b,0x43,0x3c,0x45,0x3c,0x41,0x3c,0x41,0x3b,0x3e,0x39,0x41,0x3a,0x40,0x3a,0x46,
+ 0x3b,0x42,0x3a,0x42,0x3c,0x42,0x39,0x43,0x3b,0x43,0x3e,0x41,0x3c,0x3f,0x3e,0x43,
+ 0x3b,0x42,0x3a,0x43,0x3e,0x43,0x3c,0x43,0x3e,0x43,0x3b,0x43,0x3c,0x40,0x3e,0x45,
+ 0x3b,0x43,0x3b,0x43,0x3e,0x41,0x3b,0x47,0x3a,0x46,0x3a,0x43,0x3b,0x44,0x3d,0x48,
+ 0x3c,0x42,0x3c,0x40,0x40,0x42,0x37,0x42,0x3d,0x41,0x3b,0x47,0x3f,0x44,0x3c,0x42,
+ 0x3b,0x43,0x39,0x44,0x39,0x43,0x3b,0x44,0x3b,0x42,0x38,0x46,0x3b,0x40,0x3c,0x42,
+ 0x3b,0x45,0x3b,0x41,0x3b,0x43,0x3c,0x43,0x3d,0x42,0x3d,0x42,0x3d,0x3f,0x39,0x45,
+ 0x3b,0x43,0x3b,0x44,0x39,0x44,0x3a,0x44,0x3a,0x44,0x3c,0x42,0x3a,0x42,0x3b,0x47,
+ 0x3e,0x42,0x3a,0x44,0x3c,0x41,0x3a,0x48,0x3d,0x3d,0x3d,0x44,0x3d,0x42,0x3d,0x44,
+ 0x3b,0x44,0x3c,0x43,0x39,0x41,0x3c,0x42,0x3d,0x43,0x3d,0x40,0x3a,0x40,0x3d,0x42,
+ 0x3e,0x44,0x38,0x45,0x3e,0x3f,0x3c,0x45,0x3c,0x41,0x3d,0x44,0x3c,0x42,0x3b,0x43,
+ 0x3d,0x43,0x3b,0x45,0x3c,0x41,0x38,0x44,0x3a,0x42,0x38,0x42,0x3a,0x43,0x3c,0x49,
+ 0x3c,0x42,0x39,0x44,0x3b,0x44,0x37,0x44,0x3b,0x44,0x3a,0x44,0x3d,0x43,0x38,0x45,
+ 0x3b,0x44,0x37,0x42,0x39,0x40,0x3a,0x43,0x3b,0x41,0x3c,0x41,0x3d,0x40,0x39,0x43,
+ 0x40,0x44,0x38,0x43,0x3b,0x42,0x3b,0x44,0x3e,0x41,0x3d,0x44,0x3c,0x44,0x3b,0x45,
+ 0x3b,0x44,0x3b,0x43,0x3e,0x43,0x3d,0x41,0x3d,0x40,0x3c,0x43,0x3a,0x45,0x3e,0x43,
+ 0x3c,0x42,0x3b,0x42,0x3d,0x44,0x3e,0x47,0x3c,0x41,0x3c,0x43,0x3a,0x44,0x39,0x43,
+ 0x3f,0x3f,0x3a,0x43,0x3e,0x40,0x3a,0x46,0x3b,0x41,0x39,0x44,0x3a,0x42,0x3a,0x45,
+ 0x3e,0x40,0x3c,0x41,0x3b,0x41,0x3d,0x45,0x3e,0x44,0x39,0x43,0x3c,0x42,0x3c,0x45,
+ 0x3b,0x43,0x3d,0x44,0x3b,0x43,0x3a,0x43,0x3e,0x3f,0x3d,0x42,0x3d,0x44,0x39,0x47,
+ 0x3f,0x44,0x3c,0x42,0x3d,0x45,0x3b,0x43,0x3c,0x3e,0x3d,0x43,0x3f,0x44,0x3b,0x44,
+ 0x3a,0x44,0x3e,0x42,0x3c,0x41,0x3a,0x43,0x3e,0x3e,0x3c,0x44,0x3d,0x44,0x3d,0x47,
+ 0x3e,0x41,0x3a,0x45,0x3d,0x41,0x3d,0x43,0x3b,0x45,0x39,0x42,0x3b,0x45,0x39,0x45,
+ 0x39,0x44,0x3f,0x44,0x3b,0x43,0x3b,0x44,0x3d,0x47,0x38,0x43,0x3e,0x42,0x3b,0x45,
+ 0x3e,0x43,0x38,0x43,0x40,0x41,0x39,0x41,0x3e,0x43,0x36,0x42,0x3b,0x44,0x3c,0x41,
+ 0x3b,0x43,0x3c,0x43,0x3b,0x40,0x3d,0x44,0x3c,0x43,0x3a,0x45,0x3d,0x42,0x38,0x44,
+ 0x39,0x41,0x3a,0x42,0x3c,0x45,0x3a,0x47,0x3d,0x43,0x39,0x41,0x3f,0x41,0x3d,0x46,
+ 0x3e,0x45,0x39,0x45,0x3b,0x41,0x3c,0x45,0x3f,0x44,0x3a,0x44,0x41,0x44,0x3b,0x46,
+ 0x3f,0x43,0x3d,0x43,0x3d,0x43,0x3e,0x45,0x3d,0x46,0x3b,0x44,0x3f,0x43,0x3c,0x46,
+ 0x3e,0x42,0x3a,0x42,0x3b,0x44,0x3a,0x47,0x3d,0x42,0x3d,0x45,0x39,0x42,0x38,0x46,
+ 0x3c,0x44,0x3a,0x48,0x3e,0x42,0x3a,0x48,0x40,0x44,0x3c,0x43,0x3c,0x44,0x3b,0x42,
+ 0x40,0x41,0x3a,0x44,0x3f,0x42,0x3e,0x45,0x3d,0x44,0x3b,0x42,0x3e,0x46,0x3b,0x44,
+ 0x40,0x42,0x3a,0x45,0x3f,0x45,0x3b,0x44,0x3b,0x41,0x3c,0x44,0x3c,0x45,0x3a,0x45,
+ 0x3c,0x44,0x39,0x43,0x3c,0x44,0x3a,0x44,0x3d,0x43,0x37,0x45,0x3c,0x43,0x3c,0x41,
+ 0x3b,0x42,0x39,0x44,0x3d,0x45,0x3a,0x44,0x3d,0x43,0x40,0x42,0x3c,0x43,0x3d,0x44,
+ 0x3b,0x42,0x3a,0x42,0x3e,0x44,0x3b,0x47,0x3b,0x41,0x3b,0x43,0x3b,0x42,0x3d,0x45,
+ 0x3b,0x40,0x3c,0x43,0x40,0x43,0x3c,0x43,0x3f,0x42,0x3d,0x42,0x40,0x44,0x3a,0x44,
+ 0x40,0x41,0x3e,0x47,0x3c,0x40,0x3e,0x44,0x40,0x44,0x3f,0x46,0x3b,0x44,0x3d,0x43,
+ 0x3f,0x44,0x3d,0x47,0x42,0x43,0x39,0x46,0x3d,0x41,0x3b,0x44,0x3e,0x43,0x38,0x44,
+ 0x3f,0x45,0x3d,0x46,0x3e,0x42,0x3d,0x43,0x3c,0x43,0x3a,0x45,0x3b,0x46,0x3c,0x48,
+ 0x39,0x45,0x3c,0x45,0x39,0x41,0x3b,0x45,0x3d,0x43,0x3d,0x45,0x3e,0x42,0x3c,0x45,
+ 0x3d,0x43,0x3b,0x45,0x3e,0x42,0x3e,0x41,0x3c,0x42,0x39,0x48,0x3e,0x46,0x3a,0x43,
+ 0x3e,0x45,0x3a,0x46,0x3a,0x41,0x3c,0x46,0x3f,0x42,0x3b,0x44,0x3c,0x46,0x3a,0x47,
+ 0x3a,0x44,0x3a,0x43,0x3e,0x45,0x39,0x44,0x3c,0x42,0x3a,0x44,0x3d,0x45,0x3b,0x47,
+ 0x3c,0x43,0x3b,0x44,0x3a,0x44,0x3c,0x41,0x3e,0x43,0x3c,0x43,0x3c,0x41,0x3d,0x47,
+ 0x3b,0x44,0x3e,0x41,0x3e,0x46,0x3c,0x43,0x3f,0x45,0x3a,0x43,0x3a,0x46,0x3b,0x46,
+ 0x3f,0x42,0x3a,0x44,0x3b,0x47,0x3a,0x47,0x3b,0x41,0x3c,0x42,0x3e,0x44,0x3b,0x45,
+ 0x3e,0x40,0x3b,0x42,0x3d,0x45,0x3d,0x48,0x3b,0x42,0x3a,0x43,0x3d,0x46,0x38,0x42,
+ 0x3f,0x43,0x3d,0x40,0x3c,0x47,0x3b,0x47,0x3c,0x40,0x3d,0x42,0x3d,0x42,0x3d,0x46,
+ 0x3c,0x41,0x3d,0x42,0x3c,0x43,0x3e,0x44,0x3d,0x3f,0x3c,0x42,0x37,0x47,0x3b,0x47,
+ 0x3c,0x44,0x3d,0x44,0x3f,0x43,0x3d,0x44,0x3c,0x42,0x3a,0x44,0x3e,0x44,0x3c,0x45,
+ 0x3b,0x44,0x3c,0x42,0x3b,0x44,0x3a,0x47,0x3c,0x45,0x38,0x42,0x3d,0x43,0x3c,0x46,
+ 0x3e,0x41,0x3e,0x43,0x3a,0x44,0x37,0x46,0x3c,0x45,0x39,0x43,0x3c,0x47,0x3a,0x48,
+ 0x3b,0x43,0x3c,0x44,0x3d,0x44,0x3e,0x48,0x3b,0x43,0x3a,0x44,0x3c,0x43,0x3a,0x48,
+ 0x3b,0x41,0x3c,0x43,0x3d,0x45,0x38,0x48,0x3a,0x42,0x3f,0x43,0x3c,0x48,0x3b,0x48,
+ 0x3c,0x40,0x3b,0x44,0x41,0x47,0x3a,0x3d,0x32,0x3e,0x3d,0x41,0x3e,0x43,0x41,0x48,
+ 0x3f,0x46,0x48,0x52,0x4b,0x4e,0x45,0x4d,0x43,0x47,0x41,0x4a,0x45,0x4b,0x43,0x4a,
+ 0x47,0x4b,0x43,0x47,0x41,0x4a,0x43,0x49,0x41,0x47,0x41,0x48,0x45,0x4b,0x46,0x4c,
+ 0x43,0x47,0x45,0x49,0x41,0x4b,0x40,0x4d,0x47,0x47,0x43,0x49,0x44,0x49,0x42,0x4c,
+ 0x42,0x46,0x40,0x48,0x43,0x4c,0x42,0x4c,0x41,0x4a,0x45,0x48,0x43,0x4b,0x42,0x4b,
+ 0x43,0x4a,0x42,0x48,0x41,0x4a,0x44,0x4b,0x44,0x47,0x41,0x45,0x42,0x4b,0x40,0x4b,
+ 0x43,0x4a,0x43,0x4a,0x44,0x4e,0x42,0x49,0x41,0x49,0x43,0x47,0x43,0x49,0x44,0x4d,
+ 0x47,0x48,0x42,0x49,0x45,0x4a,0x41,0x49,0x45,0x48,0x3e,0x49,0x42,0x4b,0x44,0x4b,
+ 0x42,0x4a,0x42,0x49,0x42,0x49,0x42,0x4c,0x46,0x48,0x42,0x45,0x44,0x4b,0x41,0x4f,
+ 0x46,0x4a,0x43,0x4a,0x45,0x49,0x43,0x4a,0x43,0x48,0x42,0x45,0x44,0x4a,0x40,0x4c,
+ 0x43,0x47,0x42,0x49,0x43,0x4a,0x45,0x4d,0x44,0x48,0x42,0x4b,0x41,0x4a,0x44,0x4d,
+ 0x40,0x49,0x43,0x4c,0x45,0x47,0x42,0x4b,0x43,0x48,0x41,0x49,0x43,0x4a,0x43,0x49,
+ 0x45,0x4a,0x43,0x49,0x40,0x4b,0x40,0x4c,0x43,0x4a,0x41,0x4b,0x42,0x48,0x3f,0x4d,
+ 0x43,0x4b,0x41,0x4c,0x45,0x4a,0x42,0x4d,0x43,0x49,0x43,0x46,0x43,0x4b,0x44,0x4b,
+ 0x44,0x49,0x45,0x49,0x41,0x47,0x41,0x4c,0x43,0x4b,0x42,0x4a,0x43,0x48,0x44,0x4d,
+ 0x42,0x49,0x43,0x4a,0x43,0x4c,0x42,0x50,0x43,0x4b,0x44,0x49,0x45,0x4c,0x42,0x4a,
+ 0x43,0x47,0x41,0x4a,0x43,0x4b,0x41,0x4b,0x42,0x46,0x43,0x48,0x40,0x4c,0x3e,0x4d,
+ 0x44,0x47,0x41,0x4d,0x43,0x4c,0x41,0x4d,0x00,0x22,0x80,0x09,0x00,0x10,0x00,0x01,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xb6,0x48,0xb4,0x52,0xb6,0x4a,0xb7,0x56,0xba,0x48,0xbe,0x56,0xc1,0x49,
+ 0xc4,0x50,0xc3,0x48,0xc3,0x4f,0xc1,0x4b,0xc0,0x4e,0xba,0x50,0xb8,0x52,0xb8,0x49,
+ 0xb8,0x4f,0xb9,0x46,0xba,0x4f,0xbe,0x4c,0xc1,0x52,0xc4,0x4a,0xc8,0x51,0xc6,0x44,
+ 0xc6,0x52,0xc7,0x4c,0xc5,0x4f,0xbe,0x49,0xbd,0x53,0xba,0x4b,0xbd,0x4f,0xbb,0x4a,
+ 0xbc,0x51,0xbf,0x48,0xc6,0x4d,0xc7,0x4c,0xc9,0x53,0xcb,0x47,0xcb,0x50,0xcb,0x48,
+ 0xc7,0x51,0xc4,0x4a,0xc1,0x4e,0xbd,0x47,0xbb,0x4d,0xbb,0x43,0xbf,0x56,0xc0,0x49,
+ 0xc4,0x50,0xca,0x4b,0xcc,0x4f,0xcd,0x4b,0xce,0x54,0xcd,0x4c,0xc9,0x4d,0xc7,0x47,
+ 0xc4,0x51,0xc2,0x47,0xc1,0x4c,0xc2,0x48,0xc2,0x50,0xc5,0x49,0xca,0x4f,0xcb,0x4a,
+ 0xcf,0x4e,0xcf,0x4d,0xd1,0x4e,0xcf,0x4a,0xcc,0x53,0xc8,0x49,0xc4,0x55,0xc2,0x4b,
+ 0xc2,0x51,0xc0,0x46,0xc3,0x4f,0xc4,0x4b,0xc7,0x4f,0xcb,0x4a,0xcd,0x50,0xcd,0x46,
+ 0xd0,0x51,0xcf,0x4c,0xcd,0x4e,0xc9,0x4c,0xc3,0x50,0xc1,0x49,0xc1,0x4d,0xbf,0x47,
+ 0xbf,0x53,0xc1,0x49,0xc3,0x4a,0xc6,0x49,0xc8,0x54,0xcb,0x48,0xca,0x4e,0xc9,0x45,
+ 0xc8,0x51,0xc5,0x4a,0xc0,0x50,0xbe,0x44,0xbd,0x4f,0xbc,0x4b,0xbb,0x51,0xbd,0x49,
+ 0xc0,0x50,0xc3,0x4b,0xc6,0x52,0xc7,0x48,0xc9,0x54,0xc8,0x46,0xc6,0x51,0xc3,0x49,
+ 0xbf,0x51,0xbd,0x48,0xbb,0x4d,0xbb,0x4a,0xba,0x53,0xba,0x4a,0xbd,0x53,0xc2,0x4b,
+ 0xc4,0x55,0xc7,0x49,0xc9,0x4e,0xc8,0x49,0xc6,0x4f,0xc4,0x4e,0xc0,0x51,0xbc,0x46,
+ 0xba,0x52,0xba,0x4b,0xbb,0x4e,0xbb,0x45,0xbd,0x52,0xc1,0x4b,0xc3,0x54,0xc6,0x48,
+ 0xc8,0x55,0xc8,0x47,0xc7,0x4f,0xc5,0x45,0xc0,0x51,0xbc,0x47,0xbc,0x4d,0xba,0x48,
+ 0xba,0x50,0xb9,0x48,0xbd,0x4f,0xbf,0x45,0xc1,0x51,0xc5,0x47,0xc7,0x52,0xc6,0x46,
+ 0xc7,0x4e,0xc4,0x4b,0xc0,0x53,0xbb,0x49,0xba,0x51,0xb9,0x4a,0xb7,0x4f,0xb9,0x48,
+ 0xbc,0x4f,0xbe,0x4b,0xc1,0x53,0xc4,0x4d,0xc7,0x53,0xc6,0x49,0xc6,0x50,0xc4,0x47,
+ 0xc2,0x4d,0xbd,0x4a,0xbc,0x4c,0xba,0x48,0xba,0x53,0xba,0x45,0xbb,0x50,0xbe,0x46,
+ 0xc0,0x53,0xc3,0x46,0xc6,0x51,0xc5,0x48,0xc5,0x53,0xc3,0x48,0xc1,0x4e,0xbd,0x44,
+ 0xbd,0x4f,0xbb,0x4c,0xbb,0x4e,0xba,0x49,0xbb,0x51,0xbc,0x46,0xc0,0x51,0xc4,0x49,
+ 0xc7,0x50,0xc8,0x48,0xc8,0x4d,0xc5,0x46,0xc3,0x54,0xbf,0x49,0xbc,0x50,0xbb,0x4c,
+ 0xbb,0x52,0xba,0x4f,0xbb,0x53,0xbd,0x4a,0xc0,0x53,0xc4,0x49,0xc6,0x4f,0xc8,0x49,
+ 0xc8,0x53,0xc6,0x4b,0xc3,0x50,0xc0,0x4d,0xbd,0x54,0xbc,0x4d,0xb9,0x52,0xba,0x4a,
+ 0xbb,0x54,0xbe,0x49,0xc2,0x4b,0xc4,0x47,0xc6,0x55,0xc6,0x47,0xc8,0x51,0xc6,0x4a,
+ 0xc2,0x52,0xbe,0x4b,0xbd,0x4a,0xbb,0x49,0xb9,0x53,0xb9,0x49,0xb9,0x52,0xbd,0x4c,
+ 0xc1,0x52,0xc4,0x49,0xc8,0x4e,0xc9,0x48,0xc8,0x50,0xc9,0x4a,0xc5,0x4f,0xc1,0x47,
+ 0xbe,0x53,0xbc,0x4c,0xbb,0x51,0xba,0x45,0xbb,0x54,0xbc,0x4a,0xbe,0x51,0xc2,0x4b,
+ 0xc5,0x50,0xc7,0x4a,0xc7,0x53,0xc7,0x46,0xc5,0x53,0xc1,0x4d,0xbe,0x52,0xba,0x48,
+ 0xba,0x54,0xb8,0x45,0xb8,0x4f,0xba,0x4a,0xbe,0x56,0xc0,0x48,0xc4,0x52,0xc7,0x4a,
+ 0xc6,0x54,0xc6,0x4b,0xc3,0x50,0xbf,0x49,0xbd,0x51,0xbb,0x4e,0xb9,0x4d,0xb8,0x49,
+ 0xb9,0x51,0xba,0x47,0xbc,0x50,0xc1,0x48,0xc4,0x51,0xc7,0x48,0xc6,0x51,0xc5,0x4a,
+ 0xc4,0x52,0xc1,0x49,0xbc,0x51,0xb8,0x47,0xb8,0x52,0xb7,0x4b,0xb7,0x55,0xb8,0x47,
+ 0xba,0x50,0xbe,0x48,0xc0,0x50,0xc2,0x4a,0xc6,0x52,0xc2,0x4a,0xc4,0x51,0xbe,0x48,
+ 0xc0,0x4f,0xb7,0x47,0xb7,0x4e,0xb6,0x46,0xb6,0x54,0xb7,0x47,0xba,0x4e,0xba,0x46,
+ 0xbf,0x53,0xc2,0x4a,0xc4,0x4f,0xc3,0x44,0xc1,0x55,0xc0,0x4a,0xbb,0x50,0xb8,0x4a,
+ 0xb6,0x52,0xb6,0x4e,0xb6,0x51,0xb6,0x45,0xb6,0x51,0xb9,0x4c,0xc1,0x4f,0xc3,0x48,
+ 0xc1,0x51,0xc3,0x48,0xc5,0x4d,0xc1,0x45,0xbd,0x4e,0xb6,0x47,0xb7,0x4c,0xb7,0x4c,
+ 0xb3,0x51,0xb3,0x47,0xb7,0x4e,0xbb,0x4c,0xbe,0x51,0xc0,0x46,0xc1,0x51,0xc3,0x49,
+ 0xc2,0x52,0xbe,0x45,0xbb,0x51,0xb8,0x4c,0xb6,0x52,0xb4,0x47,0xb4,0x53,0xb5,0x48,
+ 0xb4,0x52,0xba,0x49,0xbd,0x4d,0xbe,0x47,0xc1,0x54,0xc4,0x49,0xc2,0x4f,0xbf,0x45,
+ 0xbb,0x4f,0xb7,0x47,0xb5,0x53,0xb4,0x48,0xb4,0x58,0xb5,0x46,0xb7,0x53,0xb8,0x49,
+ 0xbf,0x4f,0xc2,0x49,0xc3,0x50,0xc2,0x49,0xc4,0x53,0xc2,0x49,0xbc,0x51,0xb9,0x48,
+ 0xb7,0x52,0xb5,0x4a,0xb4,0x4f,0xb5,0x47,0xb7,0x51,0xb8,0x44,0xbe,0x4f,0xbf,0x4b,
+ 0xc2,0x4c,0xc3,0x48,0xc3,0x51,0xc1,0x49,0xbe,0x56,0xbb,0x49,0xb8,0x4d,0xb6,0x4a,
+ 0xb7,0x4e,0xb7,0x4b,0xb7,0x4c,0xb9,0x4a,0xbc,0x54,0xc0,0x49,0xc3,0x53,0xc3,0x45,
+ 0xc3,0x4e,0xc2,0x4a,0xbf,0x50,0xbb,0x47,0xb7,0x54,0xb5,0x48,0xb6,0x53,0xb5,0x49,
+ 0xb5,0x4c,0xb5,0x47,0xbb,0x4c,0xbd,0x47,0xbf,0x51,0xc1,0x49,0xc1,0x4e,0xc0,0x48,
+ 0xbd,0x54,0xba,0x45,0xb7,0x4f,0xb4,0x46,0xb3,0x52,0xb6,0x42,0xb6,0x52,0xb7,0x47,
+ 0xb9,0x55,0xbe,0x47,0xc0,0x4f,0xc2,0x4b,0xc1,0x51,0xc1,0x4a,0xbe,0x50,0xbb,0x49,
+ 0xb7,0x4e,0xb4,0x45,0xb4,0x4a,0xb3,0x3f,0xb3,0x4e,0xb5,0x48,0xb8,0x50,0xbc,0x49,
+ 0xc0,0x54,0xbe,0x4e,0xc6,0x35,0xc3,0x32,0xc2,0x37,0xbe,0x30,0xbc,0x3f,0xb9,0x34,
+ 0xb9,0x3b,0xb9,0x33,0xba,0x33,0xbc,0x2e,0xbf,0x36,0xc0,0x30,0xc3,0x38,0xc5,0x2f,
+ 0xc4,0x34,0xc3,0x2f,0xc0,0x35,0xbd,0x2d,0xb9,0x35,0xb8,0x2d,0xb7,0x36,0xb8,0x2e,
+ 0xb8,0x34,0xb9,0x32,0xbb,0x38,0xc0,0x2e,0xc1,0x38,0xc3,0x2c,0xc3,0x38,0xc2,0x2d,
+ 0xc0,0x36,0xbf,0x2d,0xb7,0x33,0xb6,0x31,0xb6,0x35,0xb7,0x2a,0xb7,0x39,0xb7,0x2a,
+ 0xbb,0x36,0xbf,0x2d,0xc0,0x36,0xc2,0x2c,0xc2,0x36,0xc1,0x2e,0xbe,0x37,0xbb,0x2d,
+ 0xb7,0x35,0xb7,0x2c,0xb5,0x31,0xb5,0x2f,0xb6,0x35,0xb7,0x2b,0xb8,0x35,0xbc,0x2d,
+ 0xbd,0x30,0xbf,0x2d,0xc0,0x38,0xbf,0x2d,0xbb,0x36,0xb9,0x30,0xb5,0x35,0xb5,0x2d,
+ 0xb3,0x37,0xb3,0x2a,0xb3,0x32,0xb4,0x2e,0xb6,0x39,0xbb,0x2e,0xbc,0x32,0xbe,0x2f,
+ 0xc2,0x37,0xbf,0x31,0xbc,0x31,0xbb,0x2f,0xb8,0x39,0xb6,0x2f,0xb4,0x37,0xb6,0x2e,
+ 0xb5,0x34,0xb5,0x2a,0xb7,0x34,0xba,0x2f,0xbc,0x3a,0xbf,0x2d,0xbe,0x38,0xbc,0x2b,
+ 0xbc,0x37,0xb9,0x2e,0xb6,0x33,0xb3,0x2e,0xb2,0x34,0xb0,0x2b,0xb1,0x34,0xb2,0x2b,
+ 0xb3,0x34,0xb7,0x2d,0xb9,0x35,0xbc,0x2c,0xbe,0x37,0xbe,0x2a,0xbd,0x36,0xba,0x2e,
+ 0xb8,0x38,0xb3,0x2b,0xb3,0x32,0xb2,0x2a,0xb2,0x37,0xb3,0x2e,0xb4,0x37,0xb9,0x2b,
+ 0xbe,0x38,0xbf,0x32,0xc1,0x3c,0xc1,0x2c,0xbf,0x3a,0xbe,0x2d,0xbb,0x35,0xb9,0x2c,
+ 0xb8,0x34,0xb7,0x2e,0xb7,0x34,0xb6,0x2e,0xb8,0x3c,0xbb,0x2b,0xbe,0x39,0xc1,0x2f,
+ 0xc1,0x31,0xc2,0x2b,0xc0,0x37,0xbe,0x2e,0xbb,0x35,0xb9,0x2f,0xb7,0x37,0xb7,0x2e,
+ 0xb6,0x35,0xb7,0x2d,0xb8,0x36,0xba,0x2a,0xbc,0x35,0xc0,0x2e,0xc1,0x34,0xc1,0x2e,
+ 0xbf,0x37,0xbd,0x29,0xb9,0x34,0xb7,0x2c,0xb7,0x39,0xb5,0x30,0xb4,0x3b,0xb4,0x2e,
+ 0xb5,0x35,0xb7,0x30,0xbb,0x32,0xbd,0x2f,0xc1,0x37,0xc2,0x2f,0xc0,0x37,0xbf,0x2e,
+ 0xbb,0x35,0xb7,0x2b,0xb5,0x36,0xb4,0x2c,0xb5,0x3a,0xb5,0x2f,0xb6,0x38,0xb8,0x2e,
+ 0xbb,0x38,0xc0,0x2f,0xbf,0x32,0xc0,0x2f,0xc1,0x38,0xbf,0x2e,0xbb,0x33,0xb9,0x2d,
+ 0xb7,0x3a,0xb5,0x2f,0xb3,0x32,0xb4,0x2b,0xb5,0x37,0xb7,0x30,0xb9,0x32,0xbd,0x2a,
+ 0xbf,0x39,0xc1,0x2e,0xc0,0x2e,0xbe,0x36,0xbd,0x35,0xbb,0x2d,0xb7,0x34,0xb7,0x2d,
+ 0xb7,0x35,0xb6,0x30,0xb6,0x33,0xb7,0x30,0xba,0x35,0xbf,0x2f,0xc2,0x36,0xc3,0x2e,
+ 0xc2,0x35,0xc1,0x2f,0xbf,0x37,0xb9,0x30,0xb8,0x36,0xb6,0x32,0xb6,0x35,0xb4,0x2a,
+ 0xb6,0x33,0xb8,0x2d,0xb9,0x31,0xbc,0x2d,0xc0,0x3b,0xc2,0x2c,0xc1,0x34,0xc0,0x2d,
+ 0xbe,0x38,0xba,0x30,0xb8,0x35,0xb8,0x2d,0xb6,0x3c,0xb5,0x32,0xb5,0x34,0xb7,0x2d,
+ 0xba,0x37,0xbd,0x2d,0xbf,0x33,0xbf,0x2c,0xc1,0x39,0xbf,0x2f,0xbc,0x33,0xbb,0x2f,
+ 0xb8,0x35,0xb6,0x2d,0xb4,0x34,0xb4,0x30,0xb4,0x35,0xb4,0x31,0xb8,0x34,0xbd,0x2e,
+ 0xbf,0x3a,0xbe,0x31,0xbf,0x36,0xc0,0x2f,0xbd,0x38,0xb9,0x30,0xb6,0x37,0xb4,0x30,
+ 0xb4,0x37,0xb4,0x2d,0xb3,0x36,0xb4,0x2c,0xb6,0x38,0xba,0x2e,0xba,0x35,0xbd,0x2c,
+ 0xbd,0x35,0xbc,0x32,0xbb,0x3b,0xb9,0x2e,0xb6,0x39,0xb3,0x31,0xb3,0x35,0xb1,0x2e,
+ 0xb2,0x39,0xb3,0x30,0xb5,0x33,0xba,0x2b,0xbc,0x38,0xbc,0x31,0xbc,0x37,0xbc,0x2d,
+ 0xbb,0x36,0xb6,0x30,0xb3,0x39,0xb2,0x2c,0xb1,0x37,0xb2,0x2f,0xb1,0x38,0xb1,0x2c,
+ 0xb4,0x39,0xb7,0x2c,0xba,0x37,0xbd,0x2a,0xbf,0x34,0xbf,0x2e,0xbc,0x3a,0xba,0x2a,
+ 0xb7,0x34,0xb4,0x2c,0xb2,0x33,0xb0,0x30,0xb2,0x3a,0xb1,0x2f,0xb2,0x3b,0xb6,0x2a,
+ 0xbb,0x34,0xbb,0x2d,0xbc,0x2f,0xbc,0x2f,0xba,0x38,0xb7,0x2c,0xb5,0x36,0xb4,0x2d,
+ 0xb3,0x34,0xb2,0x2f,0xb1,0x30,0xb2,0x2d,0xb3,0x38,0xb7,0x2f,0xb8,0x36,0xb9,0x30,
+ 0xbb,0x35,0xbc,0x30,0xbb,0x35,0xb7,0x30,0xb5,0x37,0xb1,0x2f,0xb2,0x33,0xb0,0x2d,
+ 0xae,0x34,0xb1,0x2e,0xb1,0x36,0xb5,0x30,0xb8,0x3b,0xbb,0x2f,0xbb,0x35,0xbb,0x2d,
+ 0xbb,0x37,0xb9,0x30,0xb7,0x35,0xb4,0x2d,0xb2,0x37,0xb1,0x2f,0xb0,0x38,0xae,0x2b,
+ 0xb0,0x39,0xb3,0x2c,0xb5,0x33,0xb7,0x2e,0xba,0x3b,0xbb,0x2c,0xb8,0x39,0xb6,0x30,
+ 0xb3,0x35,0xb2,0x2d,0xaf,0x35,0xb0,0x2d,0xaf,0x3c,0xae,0x2d,0xaf,0x36,0xb1,0x2e,
+ 0xb5,0x37,0xb7,0x2f,0xb7,0x36,0xb8,0x32,0xb8,0x35,0xb5,0x2c,0xb3,0x36,0xb1,0x2c,
+ 0xb0,0x38,0xae,0x2d,0xad,0x36,0xad,0x2b,0xae,0x37,0xb0,0x29,0xb3,0x35,0xb4,0x31,
+ 0xb6,0x35,0xb7,0x31,0xb7,0x33,0xb5,0x2f,0xb3,0x39,0xb1,0x2c,0xad,0x37,0xac,0x30,
+ 0xae,0x35,0xae,0x2e,0xae,0x31,0xb0,0x2e,0xb3,0x39,0xb7,0x2a,0xb8,0x35,0xb7,0x2e,
+ 0xb8,0x34,0xb8,0x2e,0xb5,0x36,0xb2,0x2f,0xae,0x36,0xae,0x2e,0xae,0x3a,0xae,0x2c,
+ 0xae,0x34,0xaf,0x2f,0xb3,0x36,0xb6,0x2a,0xb8,0x39,0xb7,0x2e,0xb7,0x33,0xb7,0x2d,
+ 0xb5,0x35,0xb1,0x30,0xaf,0x33,0xac,0x30,0xad,0x3a,0xad,0x2b,0xad,0x34,0xad,0x32,
+ 0xb2,0x36,0xb5,0x2f,0xb6,0x31,0xb7,0x29,0xb8,0x38,0xb6,0x31,0xb4,0x36,0xb1,0x2d,
+ 0xaf,0x34,0xaf,0x2f,0xae,0x33,0xac,0x2c,0xac,0x35,0xae,0x30,0xb1,0x32,0xb3,0x30,
+ 0xb7,0x38,0xb8,0x2f,0xb9,0x34,0xb7,0x2d,0xb6,0x38,0xb4,0x2d,0xb1,0x38,0xaf,0x2f,
+ 0xae,0x34,0xae,0x2b,0xae,0x36,0xae,0x30,0xb1,0x36,0xb5,0x2f,0xb8,0x37,0xb8,0x2b,
+ 0xba,0x37,0xb8,0x32,0xb4,0x32,0xb1,0x30,0xb1,0x36,0xae,0x2c,0xab,0x32,0xac,0x32,
+ 0xae,0x37,0xaf,0x2c,0xaf,0x37,0xb3,0x2b,0xb7,0x39,0xb7,0x31,0xb7,0x33,0xb7,0x2b,
+ 0xb7,0x38,0xb2,0x2f,0xb1,0x36,0xaf,0x2f,0xb0,0x3a,0xac,0x29,0xac,0x30,0xad,0x25,
+ 0xae,0x31,0xb1,0x2b,0xb4,0x35,0xb6,0x2e,0xb7,0x3a,0xb2,0x33,0xb8,0x52,0xb3,0x50,
+ 0xb2,0x51,0xb0,0x58,0xae,0x61,0xad,0x58,0xaf,0x5c,0xb0,0x50,0xb2,0x58,0xb5,0x4d,
+ 0xb7,0x57,0xb7,0x52,0xba,0x54,0xb9,0x54,0xb6,0x58,0xb5,0x4f,0xb0,0x59,0xae,0x50,
+ 0xaf,0x56,0xad,0x4f,0xad,0x55,0xaf,0x4d,0xb1,0x5b,0xb1,0x51,0xb5,0x55,0xb6,0x50,
+ 0xb6,0x56,0xb6,0x4f,0xb5,0x57,0xb1,0x4e,0xae,0x5a,0xac,0x50,0xac,0x5c,0xab,0x50,
+ 0xab,0x58,0xac,0x51,0xb2,0x56,0xb3,0x4f,0xb2,0x57,0xb5,0x4d,0xba,0x56,0xb5,0x52,
+ 0xb1,0x58,0xb3,0x4f,0xb0,0x59,0xae,0x4f,0xac,0x5a,0xad,0x4c,0xac,0x5a,0xac,0x50,
+ 0xae,0x55,0xb3,0x4d,0xb4,0x56,0xb5,0x55,0xb7,0x58,0xb7,0x4e,0xb5,0x56,0xb4,0x50,
+ 0xaf,0x58,0xad,0x50,0xae,0x56,0xac,0x50,0xad,0x5e,0xac,0x51,0xae,0x58,0xaf,0x4f,
+ 0xb3,0x56,0xb4,0x50,0xb3,0x57,0xb4,0x4f,0xb4,0x59,0xb1,0x4f,0xae,0x59,0xac,0x4c,
+ 0xaa,0x56,0xa9,0x4e,0xa9,0x5b,0xa7,0x4d,0xab,0x58,0xad,0x53,0xb1,0x59,0xb3,0x52,
+ 0xb5,0x57,0xb6,0x4e,0xb3,0x57,0xb1,0x4d,0xae,0x57,0xaa,0x52,0xa9,0x5c,0xaa,0x4d,
+ 0xab,0x55,0xaa,0x54,0xab,0x57,0xae,0x51,0xb2,0x59,0xb1,0x51,0xb4,0x56,0xb4,0x4f,
+ 0xb2,0x58,0xaf,0x51,0xad,0x58,0xaa,0x50,0xa8,0x5c,0xa6,0x4e,0xa8,0x54,0xa9,0x50,
+ 0xab,0x5c,0xaa,0x50,0xb1,0x58,0xb2,0x54,0xb5,0x5a,0xb4,0x52,0xb4,0x56,0xb1,0x50,
+ 0xaf,0x58,0xad,0x4f,0xaa,0x57,0xa9,0x4f,0xa9,0x58,0xab,0x4f,0xac,0x57,0xac,0x4f,
+ 0xb0,0x5b,0xb2,0x52,0xb4,0x59,0xb2,0x52,0xb4,0x5f,0xb1,0x52,0xad,0x57,0xab,0x4e,
+ 0xa8,0x58,0xa8,0x51,0xa7,0x55,0xa7,0x4f,0xa9,0x57,0xaa,0x51,0xad,0x56,0xb0,0x50,
+ 0xb3,0x5a,0xb0,0x52,0xb0,0x57,0xb0,0x4d,0xaf,0x5a,0xa8,0x53,0xa8,0x57,0xa7,0x50,
+ 0xa8,0x56,0xa8,0x4c,0xa8,0x55,0xa9,0x4f,0xab,0x5b,0xad,0x52,0xb1,0x5a,0xb0,0x4f,
+ 0xaf,0x57,0xb0,0x4f,0xac,0x57,0xa9,0x53,0xa8,0x5a,0xa6,0x51,0xa5,0x58,0xa6,0x53,
+ 0xa7,0x5b,0xa6,0x50,0xa7,0x57,0xaf,0x53,0xb1,0x5d,0xae,0x4e,0xb1,0x58,0xaf,0x52,
+ 0xae,0x5a,0xa9,0x52,0xa7,0x59,0xa6,0x52,0xa7,0x59,0xa5,0x4d,0xa6,0x5b,0xa8,0x50,
+ 0xab,0x54,0xad,0x51,0xaf,0x55,0xb1,0x4f,0xaf,0x57,0xaf,0x51,0xae,0x5c,0xa9,0x4f,
+ 0xa7,0x56,0xa7,0x4f,0xa6,0x59,0xa5,0x4f,0xa6,0x59,0xa7,0x50,0xaa,0x59,0xac,0x4b,
+ 0xae,0x55,0xb0,0x4f,0xaf,0x55,0xab,0x4e,0xa9,0x5a,0xa9,0x4c,0xa6,0x58,0xa3,0x4e,
+ 0xa4,0x53,0xa4,0x50,0xa6,0x57,0xa8,0x54,0xa9,0x58,0xac,0x51,0xb0,0x5a,0xb0,0x4f,
+ 0xae,0x56,0xaf,0x51,0xac,0x57,0xa9,0x4e,0xa8,0x59,0xa6,0x4e,0xa5,0x58,0xa6,0x4f,
+ 0xa6,0x52,0xa5,0x50,0xa8,0x53,0xaa,0x50,0xac,0x58,0xad,0x51,0xae,0x5a,0xaf,0x4e,
+ 0xad,0x5a,0xa4,0x4f,0x9e,0x55,0xa4,0x4e,0xa3,0x5c,0x9b,0x4c,0x9e,0x58,0xa4,0x50,
+ 0xa7,0x54,0xa9,0x52,0xab,0x59,0xac,0x50,0xae,0x5b,0xad,0x4d,0xab,0x57,0xa9,0x52,
+ 0xa6,0x57,0xa2,0x50,0xa3,0x56,0xa2,0x4d,0xa2,0x59,0xa4,0x4f,0xa5,0x5b,0xa7,0x50,
+ 0xac,0x56,0xab,0x4e,0xa9,0x5a,0xab,0x50,0xab,0x5a,0xa5,0x50,0xa2,0x58,0xa1,0x4e,
+ 0xa0,0x59,0xa0,0x54,0xa1,0x56,0xa1,0x4b,0xa4,0x5c,0xa5,0x50,0xa8,0x57,0xa8,0x4e,
+ 0xaa,0x54,0xac,0x4f,0xa9,0x55,0xa6,0x4f,0xa4,0x5b,0xa4,0x4f,0xa1,0x5b,0xa2,0x4e,
+ 0xa0,0x5a,0xa1,0x4f,0xa4,0x55,0xa7,0x54,0xaa,0x5c,0xab,0x52,0xaf,0x59,0xad,0x51,
+ 0xab,0x58,0xa9,0x4c,0xa7,0x57,0xa5,0x53,0xa2,0x59,0xa1,0x4f,0xa2,0x55,0xa4,0x4d,
+ 0xa5,0x5c,0xa5,0x53,0xa9,0x5a,0xab,0x52,0xab,0x61,0xac,0x4f,0xab,0x56,0xa9,0x4f,
+ 0xa6,0x58,0xa4,0x4e,0xa2,0x58,0xa2,0x4f,0xa3,0x5b,0xa4,0x55,0xa4,0x57,0xa5,0x50,
+ 0xa9,0x53,0xac,0x51,0xae,0x59,0xab,0x56,0xaa,0x56,0xab,0x51,0xa7,0x59,0xa2,0x50,
+ 0xa2,0x5a,0xa0,0x54,0xa0,0x56,0xa1,0x53,0xa2,0x5a,0xa4,0x4f,0xa7,0x59,0xa9,0x4d,
+ 0xad,0x58,0xac,0x50,0xa9,0x57,0xaa,0x4f,0xaa,0x5a,0xa6,0x53,0xa3,0x58,0xa4,0x50,
+ 0xa5,0x5b,0xa5,0x4f,0xa3,0x5a,0xa5,0x52,0xa9,0x5c,0xab,0x51,0xac,0x57,0xac,0x50,
+ 0xaf,0x5b,0xac,0x54,0xa9,0x54,0xa5,0x52,0xa5,0x5c,0xa4,0x50,0xa2,0x58,0xa0,0x51,
+ 0xa4,0x56,0xa4,0x50,0xa7,0x5a,0xa9,0x4d,0xae,0x5b,0xac,0x4c,0xac,0x5b,0xac,0x4f,
+ 0xab,0x58,0xa8,0x53,0xa4,0x58,0xa2,0x51,0xa1,0x56,0xa0,0x54,0xa3,0x59,0xa4,0x4f,
+ 0xa5,0x57,0xa8,0x50,0xac,0x57,0xae,0x4f,0xad,0x58,0xa9,0x50,0xa7,0x58,0xa4,0x50,
+ 0xa4,0x56,0x9f,0x4b,0xa0,0x58,0xa1,0x4d,0xa2,0x57,0xa3,0x4f,0xa5,0x5b,0xa7,0x4f,
+ 0xab,0x59,0xaa,0x50,0xaa,0x58,0xa8,0x54,0xa8,0x58,0xa4,0x4e,0xa1,0x58,0xa2,0x4c,
+ 0xa1,0x55,0x9f,0x56,0x9f,0x52,0xa2,0x4e,0xa5,0x56,0xa8,0x4e,0xa9,0x58,0xaa,0x50,
+ 0xac,0x59,0xab,0x51,0xa9,0x55,0xa5,0x50,0xa4,0x58,0xa2,0x51,0xa0,0x58,0xa0,0x4c,
+ 0xa2,0x5b,0xa3,0x4f,0xa4,0x54,0xa8,0x4f,0xab,0x59,0xac,0x52,0xac,0x56,0xab,0x50,
+ 0xac,0x58,0xa8,0x53,0xa5,0x4f,0xa4,0x4b,0xa1,0x57,0x9e,0x51,0xa2,0x58,0xa1,0x4d,
+ 0xa2,0x5c,0xa6,0x4e,0xaa,0x53,0xac,0x51,0xac,0x56,0xaa,0x50,0xa9,0x55,0xa8,0x4e,
+ 0xa5,0x5b,0xa2,0x53,0xa0,0x52,0xa0,0x50,0xa3,0x57,0xa2,0x53,0xa3,0x59,0xa7,0x52,
+ 0xac,0x5c,0xac,0x52,0xac,0x58,0xa5,0x4f,0xa8,0x5b,0xa8,0x51,0xa0,0x5b,0x9c,0x4b,
+ 0xa2,0x56,0xa3,0x4f,0xa1,0x57,0xa2,0x50,0xa5,0x55,0xa6,0x54,0xaa,0x5b,0xac,0x4e,
+ 0xad,0x59,0xab,0x50,0xaa,0x56,0xa8,0x4f,0xa5,0x59,0xa1,0x51,0xa0,0x56,0xa0,0x53,
+ 0x9e,0x5a,0xa0,0x4c,0xa1,0x4f,0xa5,0x49,0xa8,0x51,0xaa,0x53,0xaa,0x56,0xa9,0x4f,
+ 0xab,0x59,0xa3,0x55,0xa6,0x45,0xa8,0x3f,0xa6,0x48,0xa4,0x44,0xa6,0x4d,0xa6,0x48,
+ 0xa8,0x4d,0xaa,0x41,0xad,0x47,0xae,0x40,0xaf,0x4a,0xae,0x44,0xad,0x49,0xab,0x3c,
+ 0xa9,0x46,0xa5,0x3f,0xa5,0x48,0xa4,0x3d,0xa5,0x47,0xa5,0x40,0xa8,0x48,0xaa,0x41,
+ 0xad,0x48,0xb0,0x3f,0xaf,0x43,0xae,0x3c,0xae,0x45,0xac,0x3f,0xa8,0x4b,0xa6,0x3f,
+ 0xa4,0x48,0xa4,0x3f,0xa3,0x43,0xa3,0x43,0xa5,0x45,0xa9,0x41,0xaa,0x47,0xac,0x3f,
+ 0xad,0x4c,0xac,0x42,0xab,0x45,0xa8,0x42,0xa7,0x43,0xa3,0x43,0xa1,0x48,0xa2,0x40,
+ 0xa2,0x4b,0xa0,0x41,0xa2,0x46,0xa6,0x43,0xaa,0x49,0xaa,0x42,0xad,0x4a,0xab,0x40,
+ 0xab,0x4a,0xaa,0x40,0xa6,0x47,0xa2,0x44,0xa2,0x4b,0xa0,0x42,0xa0,0x43,0xa0,0x43,
+ 0xa0,0x46,0xa3,0x40,0xa7,0x47,0xa8,0x3d,0xa9,0x4b,0xab,0x40,0xab,0x42,0xa7,0x41,
+ 0xa6,0x47,0xa3,0x42,0xa0,0x4a,0xa0,0x43,0xa1,0x49,0xa1,0x3e,0xa3,0x4a,0xa2,0x40,
+ 0xa7,0x47,0xa9,0x42,0xa9,0x43,0xa8,0x42,0xa9,0x47,0xa7,0x44,0xa4,0x49,0xa2,0x3f,
+ 0xa1,0x4a,0xa0,0x40,0xa0,0x4b,0x9f,0x3d,0xa0,0x4a,0xa1,0x41,0xa5,0x47,0xa7,0x44,
+ 0xaa,0x49,0xa9,0x43,0xa9,0x46,0xa8,0x42,0xa7,0x46,0xa1,0x41,0xa0,0x46,0x9e,0x40,
+ 0xa0,0x4c,0x9d,0x41,0x9e,0x48,0x9e,0x42,0xa4,0x4a,0xa7,0x42,0xa7,0x48,0xa7,0x41,
+ 0xa8,0x49,0xa5,0x40,0xa4,0x44,0xa0,0x3c,0x9f,0x44,0x9d,0x43,0x9e,0x4d,0x9c,0x40,
+ 0x9d,0x46,0x9f,0x42,0xa2,0x45,0xa4,0x41,0xa8,0x49,0xa8,0x3e,0xa9,0x48,0xa7,0x3e,
+ 0xa5,0x46,0xa2,0x3e,0x9e,0x45,0x9d,0x42,0x9d,0x4a,0x9c,0x40,0x9e,0x4b,0x9f,0x3d,
+ 0xa1,0x49,0xa2,0x42,0xa6,0x4a,0xa6,0x40,0xa8,0x4a,0xa6,0x41,0xa5,0x4a,0xa1,0x41,
+ 0xa0,0x49,0x9e,0x42,0x9d,0x47,0x9d,0x3f,0x9c,0x45,0x9e,0x46,0x9f,0x48,0xa3,0x3f,
+ 0xa4,0x46,0xa6,0x43,0xa6,0x47,0xa5,0x3f,0xa3,0x49,0x9f,0x41,0x9d,0x49,0x9d,0x3e,
+ 0x9b,0x4b,0x99,0x3e,0x9a,0x46,0x9b,0x3d,0x9f,0x4c,0xa1,0x3f,0xa4,0x4b,0xa5,0x3d,
+ 0xa7,0x47,0xa5,0x42,0xa4,0x4a,0xa1,0x41,0x9e,0x4c,0x9b,0x3e,0x99,0x45,0x97,0x40,
+ 0x9a,0x47,0x98,0x42,0x9c,0x48,0x9f,0x40,0xa3,0x49,0xa2,0x40,0xa4,0x4b,0xa4,0x42,
+ 0xa2,0x4a,0x9f,0x41,0x9e,0x46,0x9c,0x3f,0x9a,0x4a,0x9a,0x43,0x9a,0x46,0x99,0x45,
+ 0x9b,0x4a,0x9d,0x41,0xa0,0x48,0xa3,0x3d,0xa4,0x49,0xa1,0x44,0xa3,0x4d,0x9e,0x3c,
+ 0x9a,0x49,0x98,0x42,0x99,0x47,0x97,0x3e,0x96,0x48,0x99,0x43,0x9b,0x45,0x9e,0x42,
+ 0xa0,0x46,0xa2,0x3f,0xa3,0x47,0xa1,0x43,0xa1,0x4b,0xa0,0x3f,0x9c,0x48,0x98,0x41,
+ 0x9a,0x4a,0x99,0x3d,0x98,0x46,0x99,0x3d,0x9a,0x45,0x9d,0x42,0x9e,0x47,0xa0,0x41,
+ 0xa2,0x47,0xa2,0x40,0xa0,0x46,0x9f,0x3f,0x9c,0x4a,0x9a,0x3f,0x99,0x48,0x97,0x3f,
+ 0x98,0x46,0x96,0x45,0x9a,0x4b,0x9c,0x3f,0x9f,0x45,0xa2,0x41,0xa4,0x4a,0xa1,0x42,
+ 0xa3,0x4b,0xa2,0x3f,0x9d,0x48,0x9a,0x40,0x9c,0x4c,0x99,0x40,0x96,0x4c,0x96,0x41,
+ 0x9b,0x49,0x9b,0x41,0x9e,0x49,0xa1,0x44,0xa4,0x4a,0xa3,0x3f,0xa4,0x4a,0xa2,0x3e,
+ 0x9e,0x46,0x9c,0x3e,0x9a,0x4a,0x98,0x43,0x99,0x48,0x99,0x3d,0x9a,0x48,0x9a,0x40,
+ 0x9d,0x4d,0xa1,0x3f,0xa2,0x4b,0x9f,0x40,0xa0,0x4b,0x9f,0x44,0x9d,0x46,0x98,0x3b,
+ 0x97,0x48,0x96,0x41,0x96,0x46,0x95,0x45,0x96,0x4b,0x99,0x42,0x9d,0x49,0x9d,0x40,
+ 0x9f,0x47,0x9f,0x42,0xa0,0x47,0x9c,0x40,0x9c,0x48,0x99,0x43,0x97,0x4a,0x97,0x3f,
+ 0x97,0x4a,0x96,0x3d,0x96,0x48,0x99,0x3e,0x9c,0x49,0x9f,0x41,0xa1,0x44,0xa1,0x3f,
+ 0xa2,0x48,0xa0,0x41,0x9f,0x48,0x9c,0x43,0x98,0x47,0x96,0x40,0x97,0x48,0x97,0x40,
+ 0x99,0x4a,0x9a,0x41,0x9d,0x48,0xa0,0x3f,0xa3,0x44,0xa2,0x42,0xa2,0x4a,0xa2,0x3f,
+ 0xa0,0x49,0x9d,0x3f,0x98,0x49,0x97,0x3f,0x96,0x4b,0x97,0x43,0x96,0x4b,0x95,0x41,
+ 0x9b,0x49,0x9c,0x3e,0x9b,0x48,0x9e,0x43,0xa1,0x47,0x9e,0x43,0x9b,0x4a,0x9b,0x44,
+ 0x97,0x4d,0x95,0x3d,0x97,0x4b,0x95,0x3c,0x96,0x4c,0x97,0x3f,0x9a,0x49,0x9d,0x43,
+ 0x9e,0x4b,0xa2,0x3e,0xa0,0x47,0x9f,0x42,0xa0,0x4a,0x9e,0x40,0x99,0x47,0x97,0x43,
+ 0x98,0x47,0x95,0x40,0x97,0x48,0x96,0x40,0x99,0x4b,0x9d,0x3e,0xa0,0x48,0xa0,0x41,
+ 0xa1,0x4b,0x9f,0x40,0xa0,0x44,0x9e,0x3d,0x99,0x4d,0x97,0x40,0x98,0x47,0x97,0x43,
+ 0x98,0x48,0x9a,0x3e,0x9b,0x49,0x9d,0x3c,0xa1,0x47,0xa1,0x42,0xa2,0x48,0xa0,0x3e,
+ 0x9f,0x48,0x9d,0x40,0x9b,0x49,0x98,0x3f,0x98,0x47,0x98,0x41,0x99,0x49,0x98,0x42,
+ 0x9a,0x46,0x9d,0x42,0xa0,0x46,0xa0,0x41,0xa2,0x4a,0xa1,0x3e,0xa1,0x48,0x9e,0x46,
+ 0x9b,0x47,0x97,0x43,0x96,0x47,0x97,0x3d,0x96,0x4a,0x95,0x40,0x97,0x4a,0x9a,0x41,
+ 0x9e,0x48,0x9f,0x41,0xa1,0x4b,0x9e,0x40,0x9f,0x48,0x9e,0x3f,0x9a,0x4a,0x98,0x3d,
+ 0x96,0x4b,0x96,0x43,0x96,0x4a,0x95,0x3d,0x97,0x48,0x9c,0x3f,0x9f,0x47,0xa0,0x42,
+ 0xa2,0x49,0xa0,0x42,0xa0,0x47,0x9c,0x40,0x9c,0x48,0x99,0x41,0x96,0x49,0x95,0x40,
+ 0x94,0x49,0x95,0x43,0x97,0x47,0x9a,0x3d,0x9d,0x45,0xa0,0x42,0xa2,0x49,0xa1,0x42,
+ 0xa2,0x4b,0x9f,0x41,0x9c,0x47,0x98,0x3d,0x99,0x4c,0x97,0x40,0x97,0x45,0x97,0x42,
+ 0x99,0x46,0x9c,0x3e,0x9e,0x49,0x9f,0x3f,0xa1,0x48,0xa1,0x42,0xa0,0x4a,0x9f,0x40,
+ 0x9e,0x48,0x99,0x3c,0x95,0x46,0x96,0x3f,0x97,0x47,0x94,0x40,0x95,0x49,0x99,0x41,
+ 0x9e,0x4a,0xa0,0x44,0xa0,0x47,0xa2,0x3d,0xa2,0x4b,0x9e,0x3e,0x9c,0x44,0x99,0x40,
+ 0x98,0x49,0x95,0x40,0x95,0x45,0x94,0x46,0x98,0x4b,0x99,0x3f,0x9d,0x41,0x9e,0x38,
+ 0xa1,0x46,0xa1,0x40,0xa0,0x46,0x9d,0x40,0x9e,0x4b,0x96,0x44,0x9b,0x55,0x9a,0x52,
+ 0x9b,0x58,0x9c,0x56,0x9d,0x60,0xa1,0x57,0xa4,0x60,0xa5,0x56,0xa6,0x5c,0xa6,0x53,
+ 0xa6,0x57,0xa4,0x4e,0xa0,0x54,0x9e,0x4f,0x9c,0x5a,0x9b,0x52,0x9d,0x56,0x9b,0x50,
+ 0x9d,0x58,0xa0,0x4d,0xa2,0x5b,0xa4,0x52,0xa7,0x5d,0xa6,0x4f,0xa4,0x57,0xa6,0x4d,
+ 0xa2,0x59,0x9e,0x4e,0x9d,0x57,0x99,0x51,0x9a,0x5a,0x9b,0x4c,0x9a,0x55,0x9c,0x4f,
+ 0xa0,0x59,0xa4,0x53,0xa4,0x58,0xa4,0x4f,0xa3,0x54,0xa4,0x4d,0x9f,0x57,0x9b,0x55,
+ 0x9b,0x5a,0x9a,0x54,0x98,0x54,0x99,0x4e,0x99,0x58,0x9b,0x51,0xa0,0x5a,0xa2,0x50,
+ 0xa4,0x5a,0xa5,0x4f,0xa5,0x56,0xa3,0x4d,0xa0,0x5b,0x9c,0x53,0x9b,0x57,0x99,0x50,
+ 0x9b,0x58,0x99,0x4f,0x9a,0x5a,0x9b,0x50,0xa1,0x55,0xa2,0x51,0xa5,0x5d,0xa5,0x52,
+ 0xa6,0x5a,0xa4,0x50,0xa1,0x57,0x9c,0x50,0x9b,0x58,0x98,0x50,0x98,0x59,0x98,0x53,
+ 0x99,0x5a,0x9a,0x54,0x9d,0x5a,0xa0,0x50,0xa2,0x59,0xa4,0x4e,0xa5,0x5b,0xa1,0x52,
+ 0xa0,0x5a,0x9d,0x50,0x99,0x56,0x97,0x4f,0x97,0x5b,0x96,0x52,0x96,0x58,0x98,0x53,
+ 0x9b,0x57,0x9f,0x4f,0xa1,0x58,0xa2,0x53,0xa3,0x5a,0xa4,0x4c,0xa0,0x59,0x9d,0x51,
+ 0x9d,0x5b,0x99,0x50,0x98,0x57,0x99,0x53,0x99,0x58,0x99,0x55,0x9a,0x56,0x9f,0x52,
+ 0xa1,0x5a,0xa2,0x4c,0xa2,0x58,0xa2,0x55,0xa1,0x59,0x9b,0x52,0x9b,0x58,0x98,0x51,
+ 0x99,0x57,0x99,0x4e,0x99,0x5a,0x99,0x53,0x9c,0x5e,0x9d,0x53,0xa0,0x58,0xa3,0x4d,
+ 0xa3,0x5a,0xa1,0x51,0xa0,0x56,0x9d,0x50,0x9a,0x5a,0x97,0x4c,0x96,0x57,0x95,0x53,
+ 0x96,0x5c,0x97,0x53,0x99,0x59,0x9c,0x50,0x9e,0x5b,0xa0,0x52,0xa2,0x5c,0xa1,0x55,
+ 0x9e,0x56,0x9b,0x51,0x9a,0x57,0x97,0x51,0x97,0x5b,0x95,0x51,0x96,0x5c,0x95,0x50,
+ 0x99,0x57,0x9c,0x53,0x9e,0x5a,0x9e,0x53,0xa1,0x5d,0xa1,0x51,0xa0,0x56,0x9c,0x54,
+ 0x9b,0x5a,0x97,0x51,0x95,0x55,0x95,0x50,0x95,0x5a,0x95,0x4e,0x98,0x55,0x9a,0x51,
+ 0x9e,0x5e,0x9f,0x4f,0xa0,0x55,0x9f,0x54,0x9f,0x58,0x9c,0x52,0x99,0x5b,0x97,0x54,
+ 0x95,0x5e,0x92,0x51,0x94,0x58,0x95,0x52,0x98,0x58,0x9a,0x4c,0x9c,0x56,0x9e,0x57,
+ 0x9e,0x5a,0x9e,0x53,0x9e,0x5c,0x9b,0x4f,0x98,0x58,0x95,0x52,0x94,0x56,0x92,0x52,
+ 0x93,0x61,0x93,0x53,0x95,0x54,0x99,0x4e,0x9c,0x5d,0x9d,0x54,0x9d,0x59,0x9d,0x50,
+ 0x9f,0x5f,0x9b,0x53,0x99,0x56,0x96,0x54,0x96,0x5e,0x94,0x4e,0x94,0x58,0x93,0x53,
+ 0x96,0x5b,0x97,0x53,0x9a,0x58,0x9c,0x50,0x9e,0x5c,0x9e,0x4f,0x9e,0x59,0x99,0x52,
+ 0x97,0x53,0x94,0x51,0x95,0x5c,0x90,0x50,0x91,0x59,0x92,0x54,0x94,0x58,0x95,0x51,
+ 0x99,0x58,0x9c,0x57,0x9d,0x58,0x9e,0x52,0x9d,0x5a,0x9b,0x53,0x98,0x58,0x95,0x51,
+ 0x93,0x5b,0x92,0x52,0x92,0x54,0x93,0x4f,0x93,0x56,0x92,0x51,0x97,0x57,0x9b,0x4f,
+ 0x9a,0x57,0x9a,0x54,0x9c,0x59,0x99,0x51,0x99,0x5e,0x95,0x4e,0x94,0x5b,0x91,0x56,
+ 0x90,0x5b,0x90,0x54,0x91,0x58,0x94,0x4f,0x96,0x57,0x99,0x51,0x9a,0x55,0x9c,0x4e,
+ 0x9c,0x5d,0x99,0x55,0x97,0x58,0x94,0x51,0x91,0x5b,0x91,0x55,0x8f,0x5a,0x8f,0x52,
+ 0x90,0x5e,0x92,0x54,0x95,0x58,0x97,0x52,0x9a,0x5b,0x9a,0x52,0x9b,0x59,0x99,0x53,
+ 0x97,0x5a,0x94,0x52,0x92,0x55,0x91,0x54,0x90,0x5b,0x8f,0x56,0x8f,0x5d,0x90,0x51,
+ 0x95,0x5a,0x96,0x57,0x98,0x5c,0x9b,0x4d,0x9a,0x5a,0x96,0x53,0x95,0x5b,0x95,0x56,
+ 0x90,0x58,0x8d,0x53,0x8d,0x58,0x8d,0x56,0x8e,0x5b,0x8e,0x53,0x92,0x59,0x95,0x56,
+ 0x98,0x55,0x98,0x52,0x99,0x57,0x96,0x52,0x95,0x5a,0x92,0x50,0x91,0x5b,0x8e,0x54,
+ 0x8d,0x5a,0x8c,0x51,0x8e,0x57,0x8e,0x55,0x91,0x5c,0x93,0x4d,0x97,0x5b,0x96,0x51,
+ 0x97,0x5a,0x97,0x53,0x96,0x55,0x93,0x4b,0x91,0x5a,0x8d,0x53,0x8c,0x5c,0x8a,0x51,
+ 0x8d,0x5c,0x8d,0x55,0x8f,0x59,0x92,0x53,0x96,0x5d,0x96,0x52,0x98,0x5c,0x97,0x51,
+ 0x97,0x60,0x94,0x56,0x92,0x5b,0x8d,0x52,0x8d,0x58,0x8d,0x4e,0x8d,0x5b,0x8d,0x51,
+ 0x8f,0x5c,0x93,0x55,0x96,0x58,0x95,0x50,0x96,0x5a,0x97,0x54,0x97,0x59,0x92,0x51,
+ 0x90,0x5e,0x90,0x53,0x8e,0x58,0x8c,0x52,0x8d,0x59,0x8e,0x54,0x90,0x58,0x93,0x52,
+ 0x97,0x5a,0x98,0x50,0x9a,0x5a,0x9b,0x4e,0x9a,0x5a,0x97,0x51,0x95,0x5b,0x92,0x53,
+ 0x90,0x58,0x90,0x53,0x90,0x53,0x90,0x4f,0x92,0x5c,0x94,0x53,0x97,0x59,0x99,0x50,
+ 0x9b,0x5a,0x9a,0x52,0x9a,0x59,0x97,0x4e,0x95,0x59,0x91,0x51,0x90,0x5d,0x8f,0x4e,
+ 0x8f,0x5a,0x8f,0x53,0x92,0x5b,0x93,0x52,0x96,0x5b,0x97,0x52,0x9a,0x59,0x9a,0x4f,
+ 0x9a,0x5c,0x98,0x57,0x95,0x57,0x91,0x56,0x8f,0x5b,0x8d,0x51,0x8e,0x5b,0x8f,0x4f,
+ 0x90,0x56,0x93,0x53,0x96,0x57,0x97,0x52,0x99,0x5a,0x9a,0x50,0x9a,0x5a,0x96,0x51,
+ 0x96,0x58,0x94,0x52,0x8f,0x58,0x8d,0x52,0x8d,0x5a,0x8c,0x4f,0x8f,0x5b,0x92,0x50,
+ 0x97,0x5b,0x98,0x4f,0x99,0x5c,0x9a,0x50,0x99,0x56,0x98,0x54,0x96,0x58,0x92,0x53,
+ 0x92,0x59,0x8f,0x51,0x8e,0x5a,0x8f,0x4f,0x92,0x5a,0x93,0x54,0x98,0x5a,0x99,0x54,
+ 0x9b,0x59,0x9c,0x51,0x9d,0x5c,0x9a,0x4f,0x97,0x5e,0x94,0x50,0x91,0x5b,0x8f,0x50,
+ 0x90,0x56,0x91,0x4e,0x91,0x59,0x93,0x55,0x96,0x5a,0x9a,0x56,0x9d,0x59,0x9d,0x53,
+ 0x9d,0x57,0x9b,0x50,0x99,0x5d,0x95,0x52,0x90,0x5d,0x8f,0x4f,0x8e,0x5a,0x8f,0x4a,
+ 0x92,0x5b,0x92,0x52,0x97,0x5e,0x9b,0x53,0x9d,0x5f,0x9c,0x53,0x9e,0x56,0x9c,0x50,
+ 0x9c,0x5b,0x99,0x4e,0x95,0x5a,0x93,0x50,0x94,0x5d,0x92,0x52,0x93,0x5a,0x95,0x55,
+ 0x98,0x5a,0x9a,0x4f,0x9d,0x51,0x9c,0x48,0x9e,0x5a,0x9c,0x50,0x9b,0x57,0x99,0x4f,
+ 0x97,0x59,0x90,0x57,0x99,0x4c,0x98,0x46,0x98,0x51,0x9c,0x4e,0xa0,0x5c,0xa2,0x4e,
+ 0xa4,0x53,0xa4,0x4d,0xa2,0x52,0xa0,0x46,0x9f,0x56,0x9c,0x47,0x98,0x51,0x99,0x4b,
+ 0x97,0x51,0x99,0x48,0x99,0x52,0x99,0x48,0x9e,0x53,0xa0,0x47,0xa0,0x50,0xa2,0x47,
+ 0xa3,0x50,0x9f,0x46,0x9d,0x51,0x9b,0x4a,0x98,0x52,0x96,0x4b,0x97,0x4f,0x97,0x46,
+ 0x97,0x50,0x9b,0x48,0x9d,0x52,0x9f,0x48,0xa2,0x52,0xa4,0x46,0xa3,0x54,0xa1,0x48,
+ 0x9e,0x52,0x9c,0x46,0x99,0x4c,0x96,0x47,0x97,0x53,0x96,0x4c,0x98,0x4d,0x9a,0x4a,
+ 0x9c,0x4f,0x9e,0x49,0xa0,0x54,0xa2,0x4c,0xa2,0x50,0xa2,0x4a,0xa0,0x50,0x9d,0x4b,
+ 0x99,0x51,0x96,0x4a,0x94,0x51,0x96,0x49,0x95,0x50,0x97,0x48,0x9b,0x4f,0x9e,0x49,
+ 0xa0,0x4f,0xa1,0x48,0xa2,0x4f,0xa1,0x4f,0x9f,0x55,0x9b,0x4c,0x98,0x50,0x96,0x4c,
+ 0x96,0x4a,0x95,0x4b,0x96,0x52,0x96,0x4d,0x9a,0x53,0x9f,0x46,0xa0,0x51,0xa1,0x47,
+ 0xa4,0x4f,0xa2,0x49,0xa1,0x4f,0x9c,0x4c,0x99,0x56,0x96,0x48,0x96,0x51,0x95,0x49,
+ 0x97,0x4f,0x97,0x4c,0x99,0x54,0x9d,0x45,0x9e,0x53,0xa1,0x4a,0xa3,0x4e,0xa0,0x48,
+ 0x9e,0x52,0x9c,0x4b,0x9a,0x4d,0x95,0x4d,0x94,0x52,0x94,0x4a,0x95,0x4d,0x95,0x49,
+ 0x97,0x50,0x9c,0x49,0x9e,0x51,0x9d,0x4a,0x9f,0x54,0xa1,0x4a,0x9e,0x51,0x9a,0x46,
+ 0x99,0x50,0x95,0x48,0x95,0x51,0x95,0x47,0x94,0x54,0x95,0x4a,0x97,0x51,0x9a,0x49,
+ 0x9c,0x50,0xa0,0x4a,0xa0,0x4f,0x9e,0x48,0x9f,0x57,0x9c,0x4a,0x99,0x53,0x96,0x46,
+ 0x95,0x51,0x95,0x47,0x93,0x52,0x95,0x4b,0x96,0x50,0x9a,0x48,0x9d,0x54,0x9e,0x4a,
+ 0xa0,0x51,0xa0,0x49,0x9e,0x52,0x9c,0x4a,0x99,0x56,0x97,0x49,0x95,0x50,0x95,0x4b,
+ 0x94,0x51,0x94,0x4a,0x97,0x4c,0x99,0x4a,0x9d,0x4f,0x9e,0x46,0xa0,0x53,0x9f,0x4e,
+ 0x9f,0x55,0x9c,0x4d,0x9a,0x4e,0x97,0x45,0x94,0x53,0x95,0x4d,0x94,0x53,0x95,0x47,
+ 0x96,0x54,0x99,0x4b,0x9c,0x51,0x9d,0x48,0xa0,0x58,0xa0,0x4d,0x9e,0x4f,0x9c,0x46,
+ 0x9a,0x53,0x95,0x4a,0x92,0x4f,0x91,0x49,0x91,0x54,0x92,0x48,0x92,0x52,0x95,0x4e,
+ 0x99,0x54,0x9c,0x49,0x9d,0x51,0x9d,0x48,0x9d,0x50,0x9d,0x4a,0x9a,0x53,0x95,0x4a,
+ 0x93,0x50,0x92,0x46,0x90,0x51,0x92,0x4a,0x94,0x58,0x97,0x4c,0x99,0x51,0x9d,0x4c,
+ 0x9d,0x50,0x9e,0x4a,0x9e,0x4d,0x9b,0x47,0x98,0x54,0x95,0x4d,0x93,0x52,0x92,0x48,
+ 0x91,0x51,0x91,0x47,0x93,0x51,0x96,0x48,0x99,0x56,0x9b,0x4c,0x9d,0x55,0x9e,0x4a,
+ 0x9d,0x50,0x9c,0x46,0x9a,0x4f,0x96,0x49,0x94,0x57,0x92,0x47,0x91,0x4e,0x91,0x47,
+ 0x94,0x4e,0x95,0x47,0x97,0x50,0x9a,0x47,0x9c,0x52,0x9e,0x47,0x9d,0x55,0x9c,0x4b,
+ 0x99,0x51,0x97,0x49,0x93,0x50,0x91,0x4b,0x90,0x55,0x91,0x4a,0x91,0x4e,0x93,0x4b,
+ 0x98,0x4f,0x99,0x48,0x9a,0x54,0x9b,0x45,0x9d,0x4e,0x9a,0x46,0x97,0x54,0x94,0x4a,
+ 0x91,0x55,0x8f,0x4c,0x8e,0x54,0x8f,0x46,0x90,0x59,0x91,0x4c,0x96,0x53,0x99,0x47,
+ 0x9b,0x53,0x9c,0x4c,0x9b,0x53,0x9a,0x48,0x98,0x55,0x96,0x4b,0x92,0x4f,0x90,0x49,
+ 0x90,0x52,0x91,0x4f,0x91,0x52,0x92,0x4c,0x95,0x52,0x96,0x52,0x99,0x51,0x99,0x49,
+ 0x9b,0x54,0x9a,0x4a,0x98,0x52,0x95,0x45,0x92,0x57,0x8f,0x4c,0x8f,0x54,0x8f,0x4a,
+ 0x8e,0x51,0x91,0x4b,0x92,0x50,0x97,0x4b,0x9a,0x56,0x9b,0x4c,0x99,0x56,0x9b,0x4c,
+ 0x9a,0x53,0x96,0x4c,0x93,0x52,0x90,0x48,0x8f,0x57,0x8e,0x49,0x8e,0x51,0x91,0x4c,
+ 0x93,0x50,0x95,0x4a,0x99,0x4f,0x9a,0x49,0x9c,0x58,0x9b,0x47,0x99,0x4d,0x98,0x4a,
+ 0x94,0x50,0x91,0x49,0x8f,0x53,0x8f,0x4c,0x8e,0x55,0x91,0x4a,0x94,0x56,0x96,0x49,
+ 0x98,0x52,0x9c,0x4b,0x9c,0x53,0x9b,0x4c,0x99,0x56,0x99,0x48,0x95,0x4e,0x90,0x48,
+ 0x8f,0x56,0x90,0x49,0x90,0x4f,0x92,0x49,0x95,0x5c,0x96,0x4c,0x99,0x4f,0x9a,0x4b,
+ 0x9c,0x52,0x9d,0x4c,0x9b,0x4f,0x99,0x4a,0x95,0x53,0x93,0x4b,0x92,0x4f,0x90,0x4c,
+ 0x90,0x53,0x92,0x47,0x94,0x54,0x96,0x4b,0x9a,0x4e,0x9c,0x4e,0x9d,0x54,0x9b,0x49,
+ 0x9b,0x4f,0x99,0x4c,0x96,0x53,0x92,0x4c,0x8e,0x54,0x8f,0x48,0x91,0x54,0x91,0x4a,
+ 0x92,0x53,0x97,0x4a,0x9b,0x50,0x9e,0x4a,0x9f,0x4f,0x9c,0x4a,0x9b,0x54,0x9b,0x47,
+ 0x99,0x52,0x95,0x4b,0x93,0x55,0x92,0x4a,0x92,0x55,0x93,0x4d,0x93,0x54,0x98,0x4c,
+ 0x9a,0x52,0x9d,0x4b,0x9e,0x51,0x9d,0x48,0x9e,0x57,0x9c,0x4c,0x9a,0x53,0x97,0x4b,
+ 0x95,0x54,0x95,0x4e,0x94,0x4d,0x95,0x48,0x96,0x56,0x98,0x4a,0x9c,0x53,0x9f,0x47,
+ 0xa0,0x54,0xa0,0x4c,0x9e,0x51,0x9f,0x49,0x9b,0x51,0x98,0x4a,0x96,0x52,0x94,0x4a,
+ 0x93,0x55,0x94,0x4b,0x95,0x4b,0x98,0x46,0x9b,0x57,0x9d,0x4a,0xa0,0x4f,0x9f,0x49,
+ 0x9e,0x54,0x9d,0x50,0x9a,0x50,0x97,0x4c,0x96,0x55,0x95,0x4b,0x93,0x53,0x94,0x49,
+ 0x95,0x53,0x99,0x4c,0x9c,0x54,0xa0,0x45,0xa1,0x56,0xa1,0x4c,0xa1,0x4f,0x9f,0x4b,
+ 0x9c,0x56,0x9a,0x4b,0x96,0x50,0x95,0x4e,0x95,0x55,0x94,0x4b,0x96,0x4e,0x98,0x49,
+ 0x9b,0x53,0x9e,0x4d,0x9f,0x51,0xa0,0x48,0xa1,0x5b,0x9e,0x46,0x9e,0x59,0x9a,0x47,
+ 0x97,0x56,0x94,0x46,0x96,0x4d,0x96,0x4a,0x96,0x59,0x97,0x4c,0x98,0x52,0x9b,0x4c,
+ 0xa0,0x53,0x9f,0x4d,0x9e,0x4f,0x9d,0x4c,0x9e,0x52,0x99,0x4a,0x95,0x53,0x95,0x42,
+ 0x96,0x55,0x96,0x4d,0x96,0x52,0x96,0x48,0x9b,0x58,0x9e,0x4a,0xa1,0x53,0xa2,0x4b,
+ 0xa1,0x55,0xa0,0x49,0x9f,0x4f,0x9d,0x4d,0x98,0x54,0x96,0x4d,0x96,0x54,0x95,0x4a,
+ 0x96,0x55,0x98,0x4e,0x9a,0x4f,0x9d,0x4d,0xa1,0x55,0xa2,0x45,0xa1,0x4a,0xa0,0x41,
+ 0x9f,0x4f,0x9c,0x47,0x9a,0x4f,0x95,0x48,0x95,0x57,0x93,0x4f,0x9c,0x33,0x9e,0x33,
+ 0xa3,0x38,0xa6,0x3b,0xa7,0x48,0xa8,0x3a,0xa8,0x48,0xa8,0x3a,0xa3,0x3b,0x9f,0x33,
+ 0x9d,0x40,0x9c,0x35,0x99,0x3c,0x9a,0x34,0x9d,0x3f,0x9f,0x35,0xa3,0x3d,0xa4,0x34,
+ 0xa8,0x41,0xa8,0x34,0xa8,0x3e,0xa7,0x36,0xa5,0x41,0xa2,0x36,0x9e,0x3c,0x9c,0x39,
+ 0x9c,0x3a,0x9c,0x36,0x9f,0x39,0xa0,0x34,0xa4,0x3f,0xa8,0x35,0xaa,0x39,0xaa,0x33,
+ 0xac,0x3e,0xab,0x33,0xa9,0x37,0xa4,0x32,0xa3,0x3e,0xa1,0x3b,0xa0,0x3d,0xa1,0x35,
+ 0xa0,0x3e,0xa0,0x37,0xa4,0x3a,0xa8,0x31,0xa9,0x43,0xab,0x35,0xab,0x3d,0xaa,0x35,
+ 0xaa,0x3b,0xa5,0x35,0xa1,0x3c,0x9f,0x32,0xa0,0x3f,0xa1,0x37,0xa0,0x3e,0x9f,0x32,
+ 0xa3,0x38,0xa5,0x32,0xaa,0x3a,0xa9,0x35,0xab,0x3b,0xab,0x34,0xa7,0x38,0xa4,0x32,
+ 0xa3,0x3d,0xa1,0x35,0x9f,0x3a,0x9f,0x34,0xa1,0x42,0xa1,0x35,0xa4,0x3e,0xa8,0x32,
+ 0xab,0x3b,0xaa,0x36,0xad,0x3a,0xac,0x35,0xab,0x41,0xa8,0x31,0xa4,0x3f,0xa1,0x36,
+ 0xa2,0x3b,0x9f,0x36,0xa0,0x3b,0xa1,0x38,0xa3,0x40,0xa7,0x36,0xaa,0x3b,0xaa,0x39,
+ 0xad,0x3e,0xaa,0x35,0xaa,0x3c,0xa7,0x37,0xa4,0x3f,0x9f,0x33,0x9e,0x3b,0x9d,0x37,
+ 0x9f,0x3d,0x9e,0x33,0xa2,0x3f,0xa5,0x37,0xa7,0x3b,0xaa,0x37,0xab,0x35,0xaa,0x38,
+ 0xa7,0x41,0xa6,0x34,0xa3,0x3e,0x9f,0x35,0x9e,0x3b,0x9c,0x32,0x9d,0x40,0x9d,0x34,
+ 0xa0,0x3c,0xa4,0x35,0xa6,0x3a,0xa9,0x38,0xa9,0x40,0xa9,0x30,0xa9,0x3e,0xa7,0x36,
+ 0xa4,0x3f,0xa0,0x37,0x9f,0x3a,0x9d,0x33,0x9d,0x3d,0x9b,0x36,0x9f,0x3e,0xa4,0x33,
+ 0xa5,0x3c,0xa6,0x37,0xa9,0x3b,0xaa,0x35,0xa7,0x40,0xa6,0x32,0xa4,0x37,0xa1,0x39,
+ 0x9f,0x3d,0x9e,0x35,0x9e,0x37,0x9d,0x32,0xa0,0x43,0xa2,0x34,0xa6,0x3d,0xa9,0x38,
+ 0xaa,0x41,0xaa,0x38,0xaa,0x3e,0xa7,0x35,0xa5,0x3d,0xa2,0x31,0xa0,0x3a,0x9e,0x36,
+ 0x9e,0x3b,0x9f,0x38,0xa1,0x3a,0xa4,0x36,0xa6,0x3f,0xa8,0x35,0xab,0x3b,0xab,0x32,
+ 0xab,0x3e,0xa9,0x33,0xa6,0x3e,0xa2,0x33,0xa0,0x43,0x9f,0x32,0x9f,0x3b,0x9f,0x39,
+ 0xa0,0x3c,0xa0,0x36,0xa6,0x39,0xa8,0x37,0xab,0x42,0xaa,0x35,0xaa,0x3c,0xa8,0x31,
+ 0xa5,0x3d,0xa1,0x37,0xa0,0x3c,0x9e,0x32,0x9d,0x40,0x9d,0x34,0x9f,0x3f,0xa2,0x33,
+ 0xa5,0x3a,0xa8,0x39,0xab,0x39,0xab,0x35,0xaa,0x40,0xaa,0x34,0xa9,0x3d,0xa3,0x35,
+ 0xa3,0x3f,0x9e,0x35,0x9e,0x3e,0x9f,0x34,0x9f,0x43,0xa0,0x32,0xa7,0x3f,0xa8,0x31,
+ 0xab,0x3d,0xab,0x32,0xab,0x41,0xa9,0x38,0xa5,0x3d,0xa2,0x38,0x9f,0x3b,0x9c,0x35,
+ 0x9c,0x3b,0x9d,0x32,0x9d,0x3d,0x9f,0x34,0xa3,0x43,0xa7,0x37,0xa8,0x3c,0xaa,0x38,
+ 0xab,0x3d,0xa9,0x34,0xa6,0x3b,0xa4,0x37,0x9f,0x40,0x9c,0x33,0x9d,0x39,0x9c,0x34,
+ 0x9d,0x3d,0xa0,0x32,0xa3,0x3a,0xa6,0x37,0xa9,0x42,0xaa,0x35,0xaa,0x3e,0xa8,0x31,
+ 0xa8,0x3d,0xa5,0x36,0xa2,0x3c,0xa0,0x37,0x9e,0x3d,0x9e,0x36,0x9e,0x43,0xa0,0x35,
+ 0xa2,0x43,0xa6,0x37,0xa9,0x39,0xa9,0x34,0xaa,0x40,0xa9,0x36,0xa8,0x3d,0xa5,0x3a,
+ 0xa3,0x3d,0x9d,0x38,0x9d,0x3d,0x9d,0x34,0x9e,0x40,0x9e,0x38,0xa1,0x42,0xa4,0x34,
+ 0xa9,0x3e,0xa8,0x37,0xaa,0x3b,0xaa,0x35,0xa9,0x45,0xa4,0x38,0xa1,0x3c,0x9f,0x36,
+ 0x9e,0x3f,0x9d,0x35,0x9e,0x3e,0x9e,0x38,0x9f,0x41,0xa4,0x36,0xa6,0x3e,0xa8,0x38,
+ 0xaa,0x3b,0xaa,0x37,0xa7,0x3e,0xa4,0x38,0xa0,0x41,0x9e,0x39,0x9d,0x3c,0x9d,0x38,
+ 0x9c,0x44,0x9d,0x34,0xa1,0x3c,0xa4,0x35,0xa8,0x42,0xa6,0x38,0xa8,0x3f,0xa8,0x37,
+ 0xa8,0x3e,0xa2,0x36,0x9f,0x3f,0x9e,0x38,0x9b,0x42,0x99,0x36,0x9a,0x41,0x9e,0x37,
+ 0x9e,0x40,0xa1,0x39,0xa4,0x3d,0xa6,0x37,0xa8,0x42,0xa8,0x3a,0xa5,0x3e,0xa3,0x37,
+ 0xa1,0x3f,0x9b,0x34,0x9c,0x38,0x9a,0x39,0x9a,0x40,0x9a,0x35,0x9e,0x3c,0xa0,0x33,
+ 0xa5,0x3d,0xa6,0x3a,0xa7,0x3c,0xa8,0x38,0xa7,0x40,0xa4,0x3a,0xa1,0x38,0x9f,0x35,
+ 0x9d,0x41,0x9d,0x39,0x9c,0x3c,0x9b,0x34,0x9c,0x40,0x9f,0x36,0xa3,0x3c,0xa5,0x37,
+ 0xa7,0x3d,0xa7,0x37,0xa6,0x39,0xa3,0x38,0xa3,0x41,0x9f,0x38,0x9c,0x3f,0x9c,0x37,
+ 0x9e,0x3f,0x9d,0x38,0x9e,0x3b,0xa0,0x38,0xa4,0x43,0xa7,0x37,0xa8,0x3f,0xa8,0x35,
+ 0xa9,0x40,0xa6,0x37,0xa5,0x3e,0xa1,0x35,0x9e,0x3e,0x9c,0x35,0x9b,0x40,0x9c,0x33,
+ 0x9d,0x40,0x9f,0x32,0xa2,0x3c,0xa3,0x34,0xa7,0x45,0xa8,0x37,0xa5,0x41,0xa5,0x34,
+ 0xa5,0x3c,0x9f,0x38,0x9e,0x3b,0x9c,0x36,0x9c,0x42,0x9b,0x3a,0x9b,0x40,0x9f,0x37,
+ 0xa2,0x41,0xa6,0x37,0xa9,0x3d,0xaa,0x36,0xa9,0x42,0xa7,0x38,0xa5,0x40,0xa1,0x37,
+ 0xa0,0x41,0x9e,0x36,0x9c,0x3b,0x9d,0x30,0x9e,0x3f,0x9e,0x34,0xa3,0x3d,0xa5,0x36,
+ 0xa7,0x40,0xa7,0x32,0xa8,0x3d,0xa6,0x30,0xa4,0x42,0xa1,0x33,0x9e,0x41,0x9e,0x36,
+ 0x9e,0x3d,0x9d,0x35,0x9d,0x41,0xa0,0x37,0xa3,0x40,0xa6,0x3b,0xaa,0x3e,0xaa,0x37,
+ 0xaa,0x3e,0xa8,0x32,0xa5,0x43,0xa3,0x37,0x9f,0x3f,0x9d,0x37,0x9d,0x3c,0x9c,0x35,
+ 0x9d,0x3f,0x9f,0x32,0xa2,0x3e,0xa6,0x35,0xa8,0x3f,0xa8,0x36,0xaa,0x3d,0xa9,0x36,
+ 0xa6,0x40,0xa2,0x3b,0xa1,0x3c,0x9d,0x36,0x9c,0x3e,0x9c,0x36,0x9a,0x43,0x9c,0x35,
+ 0xa0,0x41,0xa4,0x35,0xa5,0x3c,0xa6,0x34,0xa8,0x46,0xa7,0x38,0xa5,0x3a,0xa3,0x36,
+ 0xa1,0x42,0x9e,0x37,0x9e,0x3f,0x9e,0x34,0x9a,0x40,0x9c,0x36,0xa2,0x42,0xa4,0x35,
+ 0xa4,0x3c,0xa8,0x37,0xab,0x3c,0xa8,0x39,0xa6,0x42,0xa4,0x37,0xa1,0x40,0xa1,0x3a,
+ 0x9f,0x3e,0x9e,0x36,0xa0,0x3e,0xa0,0x35,0xa2,0x42,0xa4,0x34,0xa8,0x3e,0xa8,0x3d,
+ 0xaa,0x44,0xaa,0x37,0xa7,0x31,0xa5,0x2c,0xa3,0x38,0xa0,0x32,0x9f,0x3b,0x9d,0x37,
+ 0x9c,0x40,0x9a,0x3d,0xa7,0x4d,0xaa,0x3f,0xaf,0x4f,0xae,0x4b,0xaf,0x58,0xad,0x52,
+ 0xab,0x51,0xa9,0x48,0xa6,0x4f,0xa4,0x49,0xa1,0x51,0xa2,0x45,0xa4,0x4a,0xa6,0x49,
+ 0xa9,0x4a,0xac,0x45,0xae,0x4f,0xb0,0x41,0xb1,0x4d,0xaf,0x44,0xad,0x4a,0xaa,0x47,
+ 0xa7,0x52,0xa4,0x46,0xa4,0x4e,0xa4,0x44,0xa4,0x54,0xa6,0x48,0xa9,0x4c,0xac,0x47,
+ 0xaf,0x4a,0xb1,0x47,0xb2,0x4f,0xaf,0x4d,0xae,0x52,0xac,0x47,0xa9,0x4c,0xa6,0x46,
+ 0xa6,0x50,0xa4,0x44,0xa7,0x4e,0xa5,0x42,0xa7,0x50,0xac,0x44,0xad,0x4c,0xaf,0x45,
+ 0xad,0x50,0xaf,0x46,0xac,0x50,0xab,0x41,0xaa,0x4f,0xa5,0x45,0xa4,0x50,0xa3,0x46,
+ 0xa6,0x4e,0xa3,0x43,0xa6,0x4e,0xaa,0x46,0xac,0x50,0xaf,0x47,0xb2,0x4e,0xb0,0x44,
+ 0xae,0x4d,0xaa,0x43,0xab,0x4c,0xa4,0x45,0xa2,0x4e,0xa2,0x46,0xa5,0x48,0xa4,0x47,
+ 0xa8,0x4c,0xac,0x4a,0xb0,0x4e,0xb1,0x48,0xb2,0x4e,0xb2,0x48,0xb1,0x4d,0xae,0x47,
+ 0xaa,0x52,0xa8,0x46,0xa8,0x49,0xa4,0x42,0xa4,0x50,0xa5,0x43,0xa8,0x4d,0xaa,0x46,
+ 0xac,0x4f,0xaf,0x49,0xb1,0x4a,0xae,0x47,0xaf,0x50,0xb0,0x48,0xab,0x4c,0xa8,0x46,
+ 0xa8,0x4e,0xa7,0x3f,0xa7,0x4d,0xa6,0x44,0xab,0x4e,0xad,0x47,0xb0,0x4e,0xb1,0x43,
+ 0xb5,0x4d,0xb5,0x47,0xb5,0x4a,0xb3,0x44,0xae,0x4a,0xaa,0x47,0xaa,0x4d,0xa7,0x49,
+ 0xa4,0x4f,0xa4,0x46,0xa9,0x4c,0xaa,0x47,0xac,0x50,0xb2,0x49,0xb4,0x4f,0xb3,0x46,
+ 0xb5,0x4d,0xb2,0x45,0xb0,0x4a,0xac,0x42,0xaa,0x4c,0xaa,0x41,0xac,0x4e,0xa9,0x47,
+ 0xab,0x4e,0xad,0x46,0xb0,0x4f,0xb4,0x47,0xb5,0x4d,0xb4,0x45,0xb5,0x4c,0xb4,0x46,
+ 0xb2,0x4e,0xad,0x47,0xac,0x4c,0xab,0x45,0xaa,0x50,0xa8,0x45,0xac,0x4f,0xad,0x4b,
+ 0xb0,0x4d,0xb0,0x4a,0xb3,0x4c,0xb4,0x48,0xb3,0x4e,0xb1,0x43,0xb1,0x4e,0xac,0x46,
+ 0xab,0x4c,0xaa,0x44,0xa9,0x50,0xa8,0x43,0xaa,0x54,0xab,0x49,0xb0,0x4f,0xb2,0x47,
+ 0xb6,0x4e,0xb6,0x4b,0xb7,0x4f,0xb6,0x4a,0xb3,0x4f,0xaf,0x47,0xac,0x4c,0xa9,0x47,
+ 0xaa,0x50,0xa7,0x47,0xa9,0x49,0xaa,0x46,0xaf,0x50,0xb2,0x45,0xb4,0x4b,0xb4,0x48,
+ 0xb6,0x4c,0xb2,0x47,0xaf,0x4b,0xab,0x47,0xaa,0x4f,0xa7,0x45,0xa6,0x4b,0xa7,0x49,
+ 0xa9,0x4f,0xaa,0x46,0xaf,0x49,0xb1,0x4a,0xb5,0x4e,0xb6,0x47,0xb7,0x4f,0xb4,0x44,
+ 0xb1,0x4e,0xaf,0x4b,0xac,0x4a,0xa8,0x43,0xaa,0x4f,0xa9,0x46,0xab,0x4f,0xaa,0x46,
+ 0xad,0x50,0xb0,0x44,0xb5,0x4b,0xb4,0x46,0xb5,0x52,0xb5,0x46,0xb6,0x4c,0xb0,0x45,
+ 0xac,0x50,0xac,0x44,0xac,0x4a,0xa7,0x43,0xa9,0x4e,0xac,0x47,0xae,0x4c,0xb0,0x45,
+ 0xb5,0x4f,0xb6,0x49,0xb6,0x4e,0xb5,0x41,0xb5,0x4e,0xb0,0x46,0xae,0x4b,0xab,0x46,
+ 0xa8,0x53,0xa9,0x43,0xa8,0x4e,0xa8,0x45,0xab,0x49,0xad,0x48,0xb2,0x4d,0xb5,0x4a,
+ 0xb5,0x50,0xb5,0x44,0xb4,0x4f,0xb0,0x42,0xad,0x4a,0xaa,0x46,0xa7,0x4b,0xa6,0x49,
+ 0xa7,0x4d,0xa7,0x43,0xa9,0x4d,0xad,0x45,0xb2,0x50,0xb2,0x48,0xb4,0x4b,0xb5,0x49,
+ 0xb4,0x4e,0xad,0x46,0xab,0x50,0xaa,0x45,0xa7,0x4f,0xa6,0x44,0xa7,0x4d,0xa9,0x4a,
+ 0xaa,0x50,0xae,0x4a,0xb3,0x4d,0xb3,0x4a,0xb4,0x4e,0xb4,0x49,0xb3,0x4a,0xb0,0x49,
+ 0xae,0x4c,0xab,0x46,0xa9,0x4c,0xa6,0x45,0xa7,0x51,0xa8,0x47,0xa9,0x4c,0xac,0x43,
+ 0xb0,0x4f,0xb2,0x47,0xb3,0x50,0xb3,0x40,0xb3,0x53,0xb2,0x4a,0xaf,0x51,0xab,0x46,
+ 0xaa,0x4c,0xab,0x49,0xab,0x4f,0xa9,0x47,0xaa,0x4d,0xae,0x46,0xb1,0x4d,0xb0,0x44,
+ 0xb3,0x52,0xb2,0x4b,0xb0,0x4f,0xad,0x45,0xad,0x4f,0xaa,0x46,0xa6,0x4c,0xa7,0x49,
+ 0xa6,0x4e,0xa4,0x45,0xa7,0x55,0xab,0x46,0xac,0x4e,0xad,0x41,0xb2,0x54,0xb2,0x47,
+ 0xb1,0x4e,0xaf,0x46,0xad,0x4f,0xa8,0x45,0xa7,0x50,0xa6,0x48,0xa5,0x4a,0xa4,0x45,
+ 0xa7,0x52,0xa9,0x47,0xad,0x4d,0xae,0x47,0xb1,0x4d,0xb0,0x47,0xaf,0x4c,0xab,0x44,
+ 0xaa,0x50,0xa8,0x47,0xa7,0x4b,0xa5,0x47,0xa8,0x4e,0xa7,0x49,0xa7,0x4b,0xaa,0x44,
+ 0xae,0x4f,0xaf,0x46,0xb2,0x4d,0xb3,0x45,0xb2,0x50,0xb1,0x47,0xb0,0x4c,0xab,0x47,
+ 0xa9,0x4c,0xa6,0x48,0xa5,0x4c,0xa6,0x49,0xa6,0x4e,0xa6,0x44,0xac,0x4c,0xae,0x49,
+ 0xaf,0x4b,0xb0,0x49,0xb1,0x4c,0xaf,0x41,0xad,0x52,0xab,0x47,0xa9,0x4f,0xa6,0x48,
+ 0xa6,0x50,0xa8,0x46,0xa8,0x4b,0xa7,0x46,0xab,0x53,0xaf,0x47,0xb2,0x4d,0xb1,0x43,
+ 0xb2,0x4e,0xb1,0x47,0xae,0x50,0xaa,0x45,0xa7,0x53,0xa5,0x47,0xa6,0x4d,0xa4,0x4a,
+ 0xa5,0x4d,0xa7,0x47,0xab,0x50,0xae,0x44,0xb0,0x50,0xb4,0x47,0xb3,0x4f,0xb1,0x49,
+ 0xb1,0x4d,0xaf,0x46,0xaa,0x4c,0xa6,0x47,0xa8,0x4b,0xa7,0x45,0xa7,0x4f,0xa7,0x48,
+ 0xab,0x4c,0xad,0x49,0xb0,0x4b,0xb1,0x48,0xb0,0x53,0xb0,0x47,0xaf,0x4f,0xad,0x44,
+ 0xaa,0x50,0xa7,0x46,0xa7,0x4a,0xa6,0x47,0xa0,0x50,0xa1,0x46,0xaa,0x4b,0xac,0x45,
+ 0xac,0x4e,0xae,0x46,0xb5,0x4a,0xb2,0x41,0xb1,0x4e,0xaf,0x46,0xac,0x4b,0xa9,0x45,
+ 0xab,0x4f,0xa7,0x48,0x9e,0x4a,0x9f,0x43,0xad,0x4e,0xa5,0x45,0xa2,0x52,0xb1,0x47,
+ 0xb5,0x4d,0xaf,0x48,0xb1,0x4d,0xae,0x42,0xab,0x51,0xa8,0x43,0xa8,0x4f,0xa9,0x47,
+ 0xa9,0x4d,0xa8,0x48,0xaa,0x4c,0xab,0x47,0xaf,0x4f,0xb2,0x49,0xb4,0x4c,0xb0,0x46,
+ 0xb1,0x4c,0xb0,0x49,0xab,0x4a,0xa7,0x44,0xa7,0x4e,0xa7,0x46,0xa6,0x4c,0xa5,0x47,
+ 0xa8,0x4a,0xaa,0x44,0xac,0x4b,0xaf,0x49,0xb1,0x51,0xb0,0x46,0xb0,0x4e,0xac,0x46,
+ 0xab,0x4f,0xa9,0x49,0xa5,0x4d,0xa5,0x43,0xa7,0x52,0xa6,0x46,0xa8,0x4a,0xa9,0x42,
+ 0xae,0x4c,0xb0,0x46,0xb1,0x50,0xaf,0x48,0xb1,0x4f,0xad,0x42,0xa9,0x45,0xa6,0x3f,
+ 0xa4,0x50,0xa2,0x47,0xa3,0x4e,0xa4,0x46,0xa6,0x4d,0xa4,0x4a,0xb5,0x1c,0xb6,0x0f,
+ 0xb7,0x1d,0xb6,0x17,0xb6,0x28,0xb1,0x19,0xae,0x20,0xab,0x13,0xaa,0x1d,0xa7,0x14,
+ 0xa9,0x1e,0xa8,0x16,0xa8,0x1c,0xab,0x12,0xb1,0x1c,0xb0,0x14,0xb2,0x1e,0xb3,0x17,
+ 0xb2,0x20,0xaf,0x17,0xac,0x1a,0xa9,0x13,0xa8,0x1f,0xa5,0x12,0xa7,0x1f,0xa7,0x17,
+ 0xaa,0x1b,0xad,0x11,0xb1,0x1b,0xb1,0x15,0xb4,0x20,0xb4,0x14,0xb2,0x1c,0xb0,0x14,
+ 0xac,0x20,0xa8,0x15,0xa4,0x1a,0xa3,0x15,0xa4,0x1b,0xa3,0x19,0xa5,0x19,0xa9,0x14,
+ 0xae,0x1d,0xb0,0x13,0xb1,0x1d,0xb1,0x15,0xb1,0x1e,0xae,0x13,0xaa,0x16,0xa6,0x15,
+ 0xa6,0x1d,0xa4,0x10,0xa4,0x1a,0xa5,0x16,0xa7,0x1d,0xa6,0x14,0xab,0x19,0xae,0x14,
+ 0xad,0x1c,0xad,0x11,0xae,0x1d,0xa7,0x14,0xa7,0x19,0xa3,0x18,0x9f,0x1b,0x9e,0x18,
+ 0x9c,0x1a,0x9f,0x0f,0xa3,0x18,0xa2,0x0f,0xa6,0x19,0xaa,0x15,0xae,0x19,0xaf,0x17,
+ 0xae,0x19,0xac,0x15,0xaa,0x1f,0xa6,0x13,0xa4,0x19,0xa2,0x16,0xa4,0x1d,0xa3,0x13,
+ 0xa3,0x1d,0xa3,0x11,0xaa,0x1b,0xaa,0x12,0xad,0x1e,0xae,0x14,0xaf,0x1c,0xaa,0x14,
+ 0xaa,0x1c,0xa6,0x13,0xa3,0x1d,0x9f,0x13,0xa1,0x1d,0xa0,0x15,0xa1,0x1d,0xa3,0x13,
+ 0xa7,0x1d,0xa9,0x12,0xac,0x1c,0xac,0x15,0xac,0x1e,0xab,0x14,0xa8,0x18,0xa5,0x14,
+ 0xa2,0x17,0x9f,0x10,0x9e,0x1b,0x9f,0x17,0xa1,0x1e,0xa1,0x15,0xa4,0x1a,0xa7,0x18,
+ 0xab,0x1e,0xab,0x14,0xac,0x1c,0xaa,0x12,0xaa,0x19,0xa6,0x16,0xa4,0x1d,0xa1,0x13,
+ 0x9f,0x1b,0xa0,0x14,0x9f,0x1f,0x9f,0x12,0xa5,0x1a,0xa8,0x19,0xaa,0x1d,0xab,0x17,
+ 0xad,0x1c,0xab,0x12,0xaa,0x1b,0xa4,0x14,0xa3,0x1f,0x9f,0x13,0x9e,0x22,0x9e,0x15,
+ 0x9f,0x20,0x9f,0x18,0xa3,0x1d,0xa5,0x12,0xa9,0x1b,0xaa,0x15,0xab,0x1b,0xab,0x18,
+ 0xa8,0x1a,0xa7,0x15,0xa4,0x20,0xa1,0x14,0xa1,0x1d,0xa0,0x10,0xa1,0x19,0x9e,0x15,
+ 0xa1,0x1f,0xa5,0x14,0xa7,0x1a,0xa6,0x14,0xa9,0x1f,0xa9,0x17,0xa7,0x19,0xa5,0x16,
+ 0xa4,0x1b,0xa2,0x19,0xa1,0x1e,0x9f,0x12,0xa1,0x1e,0x9f,0x15,0xa2,0x1c,0xa3,0x12,
+ 0xa6,0x16,0xa5,0x17,0xaa,0x1d,0xa6,0x15,0xa6,0x1e,0xa4,0x10,0xa4,0x1e,0x9f,0x11,
+ 0x9e,0x22,0x9f,0x18,0x9e,0x1e,0xa0,0x13,0xa2,0x20,0xa4,0x15,0xa8,0x1c,0xaa,0x17,
+ 0xab,0x1d,0xa9,0x13,0xa9,0x18,0xa6,0x12,0xa5,0x1f,0x9e,0x11,0x9d,0x1b,0x9c,0x11,
+ 0x9e,0x1e,0x9e,0x14,0xa0,0x1e,0xa1,0x12,0xa5,0x1c,0xa8,0x15,0xaa,0x1c,0xa9,0x14,
+ 0xa9,0x1c,0xa6,0x17,0xa4,0x1c,0xa0,0x13,0xa0,0x1c,0x9e,0x13,0x9e,0x1d,0x9f,0x17,
+ 0xa2,0x1f,0xa2,0x17,0xa6,0x1e,0xa6,0x16,0xaa,0x1e,0xa8,0x18,0xa9,0x18,0xa6,0x14,
+ 0xa3,0x19,0xa0,0x13,0xa0,0x1d,0x9e,0x16,0x9e,0x1f,0x9d,0x10,0xa0,0x1c,0x9f,0x16,
+ 0xa2,0x20,0xa5,0x12,0xa8,0x1b,0xa6,0x15,0xa6,0x21,0xa6,0x16,0xa3,0x1f,0x9f,0x13,
+ 0x9e,0x18,0x9c,0x19,0x9e,0x17,0x9e,0x15,0x9d,0x20,0xa1,0x16,0xa5,0x20,0xa7,0x14,
+ 0xa9,0x1e,0xa9,0x16,0xa8,0x1d,0xa6,0x13,0xa3,0x22,0xa2,0x18,0x9f,0x1e,0x9d,0x14,
+ 0x9f,0x1f,0x9d,0x16,0x9d,0x1d,0xa0,0x1a,0xa3,0x19,0xa3,0x12,0xa6,0x1d,0xa6,0x16,
+ 0xa7,0x1e,0xa3,0x14,0xa2,0x1e,0x9d,0x12,0x9c,0x20,0x9b,0x16,0x9a,0x20,0x9a,0x16,
+ 0x9c,0x1f,0x9f,0x19,0xa3,0x1d,0xa3,0x19,0xa5,0x21,0xa7,0x18,0xa7,0x1d,0xa3,0x15,
+ 0xa4,0x1f,0x9f,0x15,0x9c,0x19,0x9a,0x12,0x9b,0x21,0x9a,0x16,0x9b,0x1c,0x9b,0x17,
+ 0xa0,0x1b,0xa3,0x17,0xa5,0x1f,0xa5,0x14,0xa5,0x1c,0xa5,0x1a,0xa2,0x1b,0x9e,0x12,
+ 0x9b,0x20,0x9a,0x18,0x9b,0x1f,0x98,0x17,0x97,0x20,0x9b,0x15,0x9e,0x1c,0xa0,0x18,
+ 0xa2,0x22,0xa4,0x14,0xa2,0x20,0xa4,0x1a,0xa3,0x20,0x9e,0x14,0x9c,0x1d,0x9a,0x17,
+ 0x9b,0x1d,0x99,0x15,0x99,0x1e,0x9a,0x17,0xa0,0x1f,0xa1,0x16,0xa2,0x21,0xa5,0x1a,
+ 0xa6,0x1e,0xa3,0x17,0xa2,0x1b,0x9e,0x13,0x9c,0x1d,0x9a,0x19,0x9a,0x1d,0x99,0x15,
+ 0x99,0x1d,0x99,0x14,0x9d,0x1d,0x9d,0x16,0x9f,0x1d,0xa0,0x18,0xa3,0x1f,0xa2,0x15,
+ 0x9f,0x1d,0x9e,0x18,0x9d,0x1e,0x99,0x19,0x99,0x20,0x99,0x15,0x9a,0x1f,0x9c,0x17,
+ 0x9d,0x1d,0x9f,0x18,0xa2,0x1f,0xa2,0x15,0xa4,0x1f,0xa4,0x13,0xa2,0x1d,0xa0,0x18,
+ 0x9d,0x20,0x9b,0x1a,0x98,0x20,0x97,0x15,0x96,0x20,0x97,0x14,0x99,0x1f,0x9d,0x16,
+ 0x9e,0x20,0xa0,0x19,0xa2,0x1d,0xa2,0x14,0xa0,0x22,0x9e,0x16,0x9c,0x1d,0x98,0x16,
+ 0x97,0x21,0x96,0x17,0x96,0x1e,0x96,0x12,0x98,0x20,0x9c,0x1b,0xa0,0x1f,0xa0,0x14,
+ 0xa3,0x1a,0xa3,0x19,0xa1,0x21,0x9e,0x17,0x9c,0x23,0x99,0x17,0x99,0x1e,0x98,0x17,
+ 0x99,0x1d,0x98,0x12,0x99,0x1d,0x9b,0x16,0xa0,0x1c,0xa0,0x18,0xa1,0x1e,0xa1,0x19,
+ 0xa0,0x1c,0x9d,0x19,0x9b,0x1e,0x97,0x19,0x97,0x1d,0x95,0x12,0x97,0x1f,0x95,0x10,
+ 0x97,0x20,0x9a,0x18,0x9c,0x20,0x9f,0x11,0xa2,0x1e,0xa1,0x18,0xa1,0x1d,0x9d,0x1a,
+ 0x9b,0x22,0x98,0x17,0x96,0x1c,0x95,0x17,0x95,0x22,0x96,0x15,0x98,0x1c,0x98,0x14,
+ 0x9c,0x1f,0x9f,0x17,0x9f,0x1b,0x9e,0x19,0xa0,0x1e,0x9f,0x18,0x9b,0x1b,0x99,0x19,
+ 0x97,0x21,0x97,0x15,0x96,0x1b,0x95,0x19,0x95,0x1e,0x96,0x1d,0x9a,0x20,0x9a,0x17,
+ 0x9e,0x24,0x9f,0x15,0x9d,0x1c,0x9b,0x17,0x9c,0x1d,0x98,0x16,0x96,0x1d,0x94,0x19,
+ 0x95,0x1e,0x94,0x16,0x94,0x1b,0x97,0x17,0x9b,0x1c,0x9d,0x16,0x9f,0x1f,0xa0,0x18,
+ 0x9f,0x20,0x9c,0x15,0x9c,0x21,0x98,0x16,0x97,0x1c,0x96,0x19,0x95,0x1a,0x96,0x14,
+ 0x95,0x1b,0x97,0x16,0x9a,0x1a,0x9c,0x13,0xa0,0x1e,0x9f,0x11,0x9f,0x25,0x9e,0x18,
+ 0x9c,0x24,0x98,0x16,0x97,0x12,0x96,0x0d,0x96,0x1c,0x94,0x17,0x95,0x1d,0x96,0x16,
+ 0x9a,0x1f,0x9a,0x1e,0xa4,0x5d,0xa4,0x60,0xa4,0x6b,0xa1,0x6b,0x9f,0x75,0x9b,0x6e,
+ 0x9a,0x73,0x98,0x66,0x99,0x6c,0x98,0x63,0x9a,0x6b,0x9c,0x63,0xa0,0x6e,0xa1,0x61,
+ 0xa3,0x6f,0xa3,0x63,0xa3,0x6a,0x9f,0x5f,0x9e,0x6d,0x9b,0x65,0x99,0x69,0x97,0x62,
+ 0x9a,0x6c,0x98,0x63,0x9a,0x6c,0x9a,0x61,0xa0,0x6b,0xa1,0x61,0xa2,0x65,0xa3,0x63,
+ 0xa5,0x6a,0xa2,0x64,0x9f,0x67,0x9c,0x61,0x99,0x6a,0x98,0x60,0x98,0x6b,0x96,0x60,
+ 0x98,0x6a,0x9b,0x62,0x9c,0x69,0x9f,0x62,0xa4,0x6b,0xa4,0x62,0xa2,0x68,0xa2,0x63,
+ 0xa0,0x6a,0x9c,0x61,0x9a,0x6d,0x99,0x67,0x9a,0x6d,0x98,0x63,0x99,0x6c,0x99,0x61,
+ 0x9e,0x6e,0x9f,0x64,0xa1,0x66,0xa1,0x64,0xa2,0x6a,0xa1,0x62,0x9f,0x6b,0x9b,0x64,
+ 0x99,0x6c,0x96,0x67,0x95,0x66,0x96,0x65,0x97,0x69,0x98,0x63,0x9a,0x6a,0x9d,0x62,
+ 0xa1,0x68,0xa0,0x64,0xa0,0x6b,0x9f,0x61,0x9f,0x70,0x9c,0x5d,0x99,0x66,0x98,0x60,
+ 0x98,0x6f,0x99,0x63,0x98,0x67,0x99,0x61,0x9d,0x69,0x9f,0x60,0xa0,0x6c,0xa1,0x64,
+ 0xa1,0x71,0x9f,0x61,0x9e,0x6c,0x9a,0x61,0x99,0x6a,0x97,0x62,0x98,0x69,0x96,0x61,
+ 0x99,0x6c,0x99,0x62,0x9c,0x6a,0x9c,0x60,0xa1,0x6b,0xa0,0x60,0xa0,0x6a,0xa0,0x5e,
+ 0x9f,0x6f,0x9c,0x62,0x99,0x69,0x96,0x65,0x96,0x69,0x96,0x5d,0x97,0x6c,0x97,0x5f,
+ 0x98,0x6d,0x9c,0x62,0x9c,0x65,0x94,0x61,0x98,0x69,0x9c,0x62,0x92,0x68,0x8d,0x5f,
+ 0x95,0x6d,0x95,0x5f,0x91,0x69,0x95,0x65,0x97,0x6d,0x96,0x64,0x98,0x68,0x9b,0x61,
+ 0x9e,0x6e,0x9f,0x63,0x9f,0x67,0x9f,0x62,0x9d,0x72,0x9a,0x62,0x98,0x67,0x94,0x60,
+ 0x93,0x69,0x93,0x5f,0x93,0x69,0x93,0x63,0x95,0x6b,0x98,0x64,0x9a,0x6a,0x9b,0x62,
+ 0x9d,0x6b,0x9d,0x5f,0x9c,0x6a,0x99,0x64,0x97,0x6d,0x93,0x64,0x91,0x6b,0x8f,0x64,
+ 0x92,0x6c,0x92,0x65,0x92,0x64,0x95,0x62,0x9c,0x6e,0x9c,0x5f,0x9d,0x6b,0x9c,0x65,
+ 0x9c,0x68,0x99,0x62,0x97,0x6a,0x94,0x66,0x92,0x69,0x91,0x64,0x92,0x6c,0x91,0x63,
+ 0x94,0x6e,0x97,0x62,0x99,0x6d,0x9c,0x62,0x9e,0x6d,0x9d,0x5f,0x9d,0x6a,0x9a,0x61,
+ 0x98,0x68,0x94,0x64,0x94,0x68,0x93,0x63,0x93,0x6f,0x92,0x63,0x95,0x6a,0x96,0x60,
+ 0x9a,0x70,0x9b,0x62,0x9d,0x68,0x9d,0x60,0x9e,0x6d,0x99,0x60,0x98,0x6d,0x95,0x63,
+ 0x93,0x6b,0x92,0x67,0x92,0x67,0x92,0x63,0x94,0x6d,0x97,0x63,0x98,0x6c,0x9a,0x64,
+ 0x9e,0x6a,0x9d,0x61,0x9c,0x6b,0x9b,0x66,0x99,0x6f,0x96,0x5f,0x95,0x6b,0x92,0x64,
+ 0x93,0x6e,0x93,0x63,0x94,0x6a,0x96,0x67,0x97,0x6b,0x9b,0x66,0x9c,0x6a,0x9a,0x62,
+ 0x9b,0x6b,0x98,0x64,0x97,0x68,0x93,0x62,0x93,0x6d,0x92,0x64,0x93,0x6d,0x91,0x60,
+ 0x93,0x6c,0x95,0x5f,0x98,0x6c,0x99,0x61,0x9b,0x6e,0x9c,0x63,0x9c,0x67,0x9a,0x64,
+ 0x99,0x6c,0x95,0x62,0x93,0x6a,0x93,0x64,0x8f,0x68,0x89,0x63,0x8e,0x67,0x96,0x67,
+ 0x91,0x6f,0x92,0x65,0x9b,0x69,0x9c,0x61,0x9c,0x6e,0x9a,0x68,0x99,0x6c,0x95,0x67,
+ 0x92,0x67,0x91,0x64,0x90,0x69,0x90,0x63,0x92,0x6f,0x92,0x62,0x95,0x6a,0x97,0x67,
+ 0x9a,0x6e,0x9a,0x63,0x9a,0x6c,0x99,0x63,0x97,0x6d,0x94,0x68,0x92,0x6c,0x90,0x63,
+ 0x8f,0x6b,0x8e,0x63,0x8f,0x6a,0x8e,0x60,0x93,0x6f,0x95,0x66,0x96,0x69,0x97,0x5e,
+ 0x98,0x6d,0x96,0x65,0x95,0x6a,0x92,0x65,0x90,0x6f,0x8b,0x62,0x8b,0x6a,0x8c,0x63,
+ 0x8d,0x6e,0x8d,0x64,0x90,0x67,0x95,0x68,0x96,0x6c,0x97,0x61,0x98,0x6c,0x97,0x62,
+ 0x96,0x70,0x93,0x62,0x91,0x6b,0x90,0x63,0x90,0x6f,0x8e,0x65,0x91,0x6b,0x90,0x62,
+ 0x93,0x69,0x97,0x5f,0x99,0x6d,0x9a,0x64,0x9c,0x6d,0x9a,0x66,0x99,0x6a,0x96,0x64,
+ 0x95,0x69,0x91,0x62,0x90,0x6c,0x91,0x64,0x93,0x6f,0x92,0x66,0x94,0x6c,0x98,0x62,
+ 0x9a,0x6f,0x9b,0x66,0x9c,0x6a,0x9b,0x64,0x9a,0x71,0x98,0x5f,0x94,0x70,0x91,0x62,
+ 0x91,0x68,0x91,0x63,0x90,0x6a,0x91,0x67,0x92,0x71,0x96,0x63,0x98,0x73,0x9b,0x5f,
+ 0x9b,0x6a,0x9b,0x67,0x99,0x6b,0x96,0x63,0x95,0x6f,0x91,0x66,0x8f,0x6c,0x90,0x63,
+ 0x90,0x6e,0x90,0x63,0x92,0x67,0x94,0x66,0x9a,0x6f,0x9a,0x64,0x9b,0x6e,0x9b,0x64,
+ 0x9a,0x6d,0x98,0x65,0x95,0x6c,0x93,0x64,0x92,0x6f,0x91,0x63,0x90,0x6c,0x91,0x65,
+ 0x94,0x6f,0x94,0x66,0x97,0x70,0x9a,0x62,0x9c,0x73,0x9b,0x67,0x99,0x6d,0x97,0x62,
+ 0x98,0x6f,0x93,0x67,0x91,0x6c,0x90,0x63,0x90,0x73,0x91,0x66,0x93,0x6e,0x92,0x63,
+ 0x97,0x70,0x9a,0x62,0x9b,0x6e,0x9a,0x61,0x9a,0x6f,0x99,0x64,0x96,0x6d,0x92,0x63,
+ 0x91,0x6d,0x90,0x64,0x90,0x6b,0x90,0x63,0x91,0x70,0x93,0x66,0x95,0x70,0x98,0x66,
+ 0x9a,0x70,0x9a,0x65,0x98,0x72,0x99,0x67,0x96,0x70,0x92,0x5e,0x90,0x6d,0x91,0x67,
+ 0x90,0x6c,0x8e,0x63,0x90,0x6c,0x92,0x67,0x95,0x73,0x97,0x69,0x99,0x6c,0x9a,0x67,
+ 0x99,0x70,0x98,0x63,0x97,0x6c,0x93,0x63,0x92,0x75,0x90,0x64,0x8e,0x71,0x8f,0x67,
+ 0x90,0x71,0x93,0x68,0x95,0x6b,0x98,0x66,0x9a,0x71,0x99,0x69,0x9b,0x6f,0x99,0x67,
+ 0x97,0x6f,0x93,0x66,0x92,0x69,0x90,0x64,0x8f,0x71,0x91,0x5d,0x8f,0x70,0x91,0x64,
+ 0x94,0x6e,0x96,0x6a,0x98,0x6f,0x99,0x69,0x98,0x75,0x98,0x67,0x96,0x73,0x93,0x66,
+ 0x91,0x6f,0x8f,0x66,0x90,0x6a,0x8d,0x68,0x8e,0x6e,0x90,0x67,0x93,0x6f,0x95,0x69,
+ 0x99,0x6e,0x9a,0x65,0x9a,0x6f,0x97,0x64,0x97,0x6d,0x94,0x63,0x91,0x6b,0x8e,0x66,
+ 0x8e,0x6c,0x8e,0x62,0x8f,0x6e,0x8f,0x65,0x93,0x70,0x95,0x66,0x97,0x6b,0x98,0x64,
+ 0x9a,0x6e,0x98,0x63,0x97,0x69,0x95,0x66,0x92,0x74,0x8e,0x61,0x8e,0x65,0x8f,0x5f,
+ 0x8e,0x6a,0x8f,0x64,0x93,0x6e,0x96,0x61,0x97,0x72,0x97,0x6a,0x9c,0x5a,0x9a,0x50,
+ 0x9a,0x58,0x96,0x5a,0x94,0x67,0x93,0x5e,0x93,0x64,0x93,0x56,0x94,0x5d,0x93,0x57,
+ 0x99,0x5d,0x9b,0x52,0x9d,0x5d,0x9e,0x54,0x9f,0x66,0x9c,0x54,0x9b,0x61,0x99,0x56,
+ 0x96,0x5c,0x94,0x54,0x94,0x5c,0x93,0x53,0x95,0x60,0x95,0x54,0x96,0x5f,0x99,0x56,
+ 0x9e,0x5a,0x9c,0x55,0x9d,0x5b,0x9c,0x55,0x9c,0x62,0x98,0x53,0x97,0x5d,0x94,0x51,
+ 0x93,0x61,0x93,0x54,0x94,0x5b,0x94,0x58,0x96,0x62,0x9a,0x4f,0x9c,0x5c,0x9d,0x55,
+ 0x9f,0x5c,0x9c,0x53,0x9b,0x62,0x98,0x54,0x95,0x5d,0x93,0x58,0x93,0x5f,0x91,0x59,
+ 0x93,0x5f,0x93,0x54,0x97,0x5f,0x98,0x51,0x9c,0x5e,0x9c,0x52,0x9d,0x5f,0x9b,0x53,
+ 0x9a,0x5a,0x97,0x56,0x95,0x5f,0x93,0x55,0x91,0x5c,0x91,0x56,0x91,0x5e,0x91,0x55,
+ 0x94,0x5c,0x98,0x56,0x99,0x59,0x9b,0x53,0x9b,0x62,0x9b,0x50,0x9a,0x60,0x97,0x50,
+ 0x95,0x5e,0x92,0x55,0x92,0x5c,0x91,0x53,0x91,0x62,0x91,0x56,0x94,0x5c,0x95,0x53,
+ 0x99,0x5c,0x9a,0x54,0x9a,0x5f,0x99,0x54,0x97,0x63,0x96,0x55,0x92,0x60,0x91,0x57,
+ 0x91,0x5d,0x8e,0x52,0x8f,0x5a,0x90,0x55,0x91,0x60,0x93,0x57,0x98,0x5e,0x98,0x52,
+ 0x9a,0x5c,0x9a,0x54,0x99,0x5c,0x96,0x55,0x94,0x5f,0x91,0x53,0x8f,0x62,0x8e,0x55,
+ 0x90,0x5e,0x8e,0x52,0x90,0x5c,0x94,0x58,0x95,0x60,0x99,0x54,0x99,0x5d,0x98,0x55,
+ 0x98,0x59,0x97,0x55,0x94,0x5c,0x90,0x56,0x8e,0x5e,0x8d,0x53,0x8e,0x5a,0x8d,0x53,
+ 0x8f,0x5c,0x92,0x57,0x94,0x5d,0x97,0x51,0x99,0x5e,0x97,0x53,0x97,0x59,0x97,0x55,
+ 0x93,0x5e,0x8f,0x55,0x8d,0x5b,0x8c,0x56,0x8d,0x62,0x8c,0x52,0x8c,0x5d,0x90,0x55,
+ 0x93,0x60,0x96,0x53,0x97,0x5c,0x96,0x55,0x97,0x60,0x95,0x58,0x94,0x5a,0x90,0x51,
+ 0x8f,0x5c,0x8e,0x56,0x8e,0x5b,0x8e,0x55,0x8f,0x60,0x90,0x57,0x93,0x5c,0x96,0x58,
+ 0x99,0x5f,0x96,0x55,0x98,0x5e,0x96,0x52,0x94,0x61,0x90,0x54,0x90,0x5f,0x8d,0x53,
+ 0x8e,0x5a,0x8e,0x5b,0x8e,0x60,0x90,0x55,0x93,0x5f,0x94,0x58,0x97,0x5c,0x97,0x58,
+ 0x97,0x60,0x96,0x58,0x93,0x5c,0x90,0x56,0x8e,0x61,0x8d,0x58,0x8c,0x63,0x8c,0x57,
+ 0x8e,0x5d,0x8e,0x56,0x92,0x5b,0x94,0x54,0x96,0x5d,0x96,0x58,0x97,0x5e,0x94,0x56,
+ 0x93,0x63,0x91,0x57,0x90,0x5f,0x8d,0x56,0x8c,0x60,0x8b,0x56,0x8c,0x60,0x8e,0x54,
+ 0x90,0x60,0x93,0x5e,0x95,0x5d,0x96,0x57,0x95,0x61,0x95,0x5a,0x94,0x62,0x90,0x5a,
+ 0x8e,0x61,0x8c,0x59,0x8b,0x5c,0x8a,0x58,0x8a,0x63,0x8a,0x5c,0x8d,0x5f,0x90,0x58,
+ 0x92,0x63,0x93,0x59,0x96,0x5e,0x95,0x59,0x94,0x5f,0x90,0x5a,0x8d,0x61,0x8b,0x56,
+ 0x8a,0x60,0x8b,0x5c,0x8c,0x5b,0x8c,0x58,0x8e,0x61,0x90,0x5b,0x92,0x63,0x95,0x5b,
+ 0x94,0x64,0x95,0x59,0x93,0x5d,0x91,0x5a,0x8d,0x63,0x8d,0x57,0x8c,0x5f,0x8b,0x58,
+ 0x8a,0x5d,0x8c,0x5b,0x8e,0x5f,0x91,0x5a,0x92,0x64,0x95,0x5a,0x94,0x64,0x94,0x58,
+ 0x94,0x65,0x92,0x5b,0x8e,0x66,0x8c,0x59,0x8a,0x68,0x8a,0x5d,0x8a,0x60,0x8c,0x5f,
+ 0x8d,0x66,0x91,0x59,0x91,0x60,0x94,0x5d,0x94,0x65,0x93,0x59,0x92,0x61,0x92,0x5c,
+ 0x8e,0x63,0x8b,0x5c,0x8a,0x61,0x8b,0x5b,0x8b,0x67,0x8b,0x5b,0x8c,0x63,0x8e,0x61,
+ 0x93,0x69,0x94,0x5a,0x95,0x62,0x94,0x5b,0x93,0x6a,0x91,0x5c,0x8f,0x66,0x8c,0x60,
+ 0x8a,0x6a,0x8a,0x5e,0x88,0x60,0x89,0x60,0x8b,0x6a,0x8b,0x61,0x90,0x68,0x91,0x62,
+ 0x93,0x68,0x92,0x61,0x93,0x68,0x8f,0x61,0x8e,0x6d,0x8b,0x62,0x8a,0x69,0x88,0x62,
+ 0x8a,0x6b,0x89,0x63,0x8a,0x6c,0x8b,0x5f,0x8e,0x70,0x90,0x66,0x91,0x6a,0x91,0x6b,
+ 0x91,0x72,0x8f,0x66,0x8d,0x68,0x88,0x61,0x88,0x6e,0x87,0x66,0x88,0x6d,0x87,0x68,
+ 0x88,0x75,0x89,0x69,0x8d,0x6c,0x8e,0x62,0x8e,0x74,0x90,0x66,0x90,0x6d,0x8d,0x65,
+ 0x8b,0x74,0x89,0x65,0x86,0x6c,0x85,0x66,0x85,0x72,0x84,0x69,0x86,0x72,0x88,0x6a,
+ 0x8b,0x72,0x8d,0x68,0x8f,0x6e,0x8e,0x68,0x8e,0x72,0x8c,0x65,0x8a,0x71,0x88,0x67,
+ 0x84,0x72,0x84,0x66,0x84,0x6c,0x84,0x68,0x85,0x70,0x87,0x63,0x8b,0x70,0x8c,0x65,
+ 0x8e,0x70,0x8e,0x62,0x8f,0x6b,0x8e,0x6a,0x8c,0x71,0x88,0x67,0x87,0x6c,0x84,0x63,
+ 0x83,0x71,0x84,0x67,0x85,0x68,0x85,0x66,0x89,0x6d,0x8b,0x62,0x8e,0x6c,0x8e,0x68,
+ 0x8f,0x6d,0x8d,0x65,0x8c,0x69,0x88,0x64,0x86,0x71,0x85,0x64,0x83,0x68,0x83,0x64,
+ 0x86,0x6f,0x84,0x64,0x87,0x6b,0x8b,0x65,0x8c,0x72,0x8c,0x62,0x8c,0x70,0x8c,0x62,
+ 0x8a,0x6f,0x88,0x65,0x86,0x6c,0x84,0x60,0x84,0x71,0x83,0x64,0x84,0x6e,0x84,0x65,
+ 0x85,0x6e,0x88,0x64,0x8a,0x68,0x8a,0x66,0x8b,0x74,0x8b,0x65,0x8a,0x70,0x87,0x65,
+ 0x85,0x71,0x83,0x64,0x83,0x6b,0x82,0x64,0x83,0x77,0x83,0x68,0x84,0x70,0x87,0x66,
+ 0x88,0x71,0x8a,0x66,0x8a,0x71,0x8a,0x68,0x88,0x7a,0x87,0x6e,0x83,0x74,0x82,0x6c,
+ 0x81,0x77,0x80,0x6e,0x80,0x73,0x80,0x6c,0x81,0x77,0x83,0x6c,0x87,0x73,0x88,0x6d,
+ 0x88,0x79,0x89,0x6e,0x87,0x74,0x86,0x6c,0x83,0x7d,0x80,0x72,0x7f,0x7a,0x7d,0x71,
+ 0x7d,0x78,0x7d,0x72,0x7e,0x73,0x82,0x70,0x84,0x77,0x86,0x6d,0x86,0x77,0x86,0x71,
+ 0x87,0x76,0x86,0x6f,0x83,0x77,0x80,0x71,0x80,0x7a,0x7d,0x71,0x7d,0x78,0x7c,0x71,
+ 0x7f,0x79,0x80,0x72,0x83,0x76,0x85,0x72,0x87,0x7a,0x86,0x70,0x87,0x76,0x85,0x72,
+ 0x84,0x7d,0x81,0x75,0x7f,0x75,0x7e,0x74,0x7f,0x7b,0x7e,0x73,0x7f,0x76,0x81,0x6f,
+ 0x84,0x79,0x85,0x74,0x87,0x7b,0x88,0x75,0x88,0x7f,0x87,0x75,0x85,0x79,0x83,0x75,
+ 0x81,0x78,0x80,0x73,0x80,0x7b,0x7f,0x74,0x80,0x82,0x82,0x77,0x83,0x82,0x87,0x7c,
+ 0x88,0x81,0x85,0x7f,0x6a,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xb4,0x47,0xb1,0x52,0xb4,0x4b,0xb6,0x55,0xb8,0x46,0xbc,0x55,0xbe,0x47,
+ 0xc1,0x50,0xc1,0x4a,0xc1,0x4e,0xbf,0x4b,0xbd,0x50,0xba,0x4e,0xb7,0x52,0xb5,0x4b,
+ 0xb4,0x51,0xb7,0x48,0xb9,0x4f,0xbc,0x4b,0xc0,0x4f,0xc2,0x4b,0xc5,0x50,0xc4,0x45,
+ 0xc5,0x52,0xc5,0x4d,0xc2,0x50,0xbe,0x48,0xbc,0x53,0xb9,0x49,0xb9,0x50,0xb9,0x4a,
+ 0xbb,0x52,0xbe,0x4a,0xc4,0x4c,0xc6,0x4b,0xc7,0x51,0xca,0x46,0xc9,0x51,0xc9,0x46,
+ 0xc5,0x51,0xc4,0x48,0xc0,0x4c,0xbc,0x46,0xba,0x4d,0xb9,0x42,0xbe,0x51,0xc0,0x4b,
+ 0xc2,0x50,0xc8,0x4a,0xca,0x4c,0xcb,0x4a,0xcc,0x51,0xcb,0x4a,0xc8,0x4e,0xc5,0x46,
+ 0xc4,0x51,0xc1,0x48,0xbf,0x4b,0xbf,0x47,0xc2,0x4f,0xc5,0x4a,0xc8,0x50,0xca,0x48,
+ 0xce,0x4f,0xcd,0x4b,0xcf,0x4c,0xcd,0x49,0xcb,0x50,0xc7,0x47,0xc5,0x53,0xc1,0x4b,
+ 0xc0,0x52,0xbe,0x45,0xc3,0x4d,0xc4,0x4b,0xc8,0x4c,0xcb,0x49,0xcd,0x4f,0xcc,0x45,
+ 0xce,0x53,0xce,0x4b,0xcc,0x4a,0xc9,0x4b,0xc5,0x4e,0xc1,0x48,0xc0,0x4f,0xbe,0x45,
+ 0xbf,0x53,0xc2,0x49,0xc4,0x4b,0xc6,0x49,0xc8,0x54,0xcb,0x4c,0xc9,0x4f,0xc9,0x45,
+ 0xc7,0x51,0xc5,0x4a,0xc2,0x4f,0xbf,0x43,0xbd,0x4d,0xbb,0x4a,0xbb,0x51,0xbe,0x48,
+ 0xc1,0x50,0xc4,0x4d,0xc8,0x4f,0xc7,0x47,0xc9,0x52,0xc8,0x47,0xc8,0x4f,0xc3,0x4b,
+ 0xc1,0x52,0xbe,0x46,0xbb,0x4d,0xba,0x48,0xba,0x53,0xbb,0x49,0xbf,0x4f,0xc3,0x48,
+ 0xc4,0x58,0xc6,0x49,0xc8,0x4d,0xc8,0x4a,0xc7,0x4e,0xc4,0x4d,0xc2,0x4e,0xbd,0x47,
+ 0xba,0x52,0xba,0x4b,0xba,0x50,0xbc,0x42,0xbd,0x51,0xc1,0x4c,0xc3,0x52,0xc5,0x49,
+ 0xc7,0x52,0xc7,0x47,0xc6,0x4d,0xc4,0x4a,0xc1,0x50,0xbd,0x49,0xbb,0x4c,0xb9,0x46,
+ 0xb9,0x50,0xb9,0x47,0xbe,0x4f,0xbf,0x48,0xc1,0x4f,0xc5,0x48,0xc6,0x53,0xc5,0x46,
+ 0xc6,0x4d,0xc3,0x4c,0xbf,0x52,0xbb,0x4a,0xba,0x50,0xb8,0x48,0xb5,0x51,0xb9,0x45,
+ 0xbc,0x4c,0xbd,0x4a,0xc0,0x50,0xc3,0x4b,0xc5,0x51,0xc4,0x47,0xc5,0x4f,0xc2,0x47,
+ 0xc0,0x4e,0xbc,0x47,0xbb,0x4d,0xb8,0x49,0xb7,0x52,0xb7,0x44,0xba,0x50,0xbc,0x47,
+ 0xbe,0x51,0xc0,0x43,0xc4,0x4f,0xc2,0x43,0xc2,0x51,0xc1,0x47,0xbd,0x50,0xbb,0x46,
+ 0xbb,0x4f,0xb8,0x4c,0xb7,0x4d,0xb7,0x49,0xb9,0x50,0xba,0x44,0xbd,0x52,0xc1,0x47,
+ 0xc3,0x50,0xc4,0x49,0xc4,0x4c,0xc1,0x49,0xbf,0x52,0xbc,0x4a,0xb9,0x50,0xb6,0x48,
+ 0xb5,0x52,0xb5,0x4b,0xb6,0x52,0xba,0x48,0xbb,0x50,0xbf,0x49,0xc0,0x4f,0xc2,0x4b,
+ 0xc2,0x51,0xc0,0x49,0xbe,0x50,0xbc,0x4a,0xb9,0x51,0xb7,0x4c,0xb3,0x50,0xb5,0x4a,
+ 0xb6,0x51,0xba,0x48,0xbd,0x4a,0xbe,0x45,0xc1,0x51,0xc1,0x48,0xc2,0x51,0xc0,0x4c,
+ 0xbd,0x51,0xb9,0x4b,0xb8,0x49,0xb5,0x46,0xb2,0x51,0xb3,0x47,0xb4,0x52,0xb8,0x4c,
+ 0xbb,0x51,0xbd,0x47,0xc2,0x4e,0xc2,0x49,0xc1,0x50,0xc1,0x4b,0xbe,0x4e,0xba,0x48,
+ 0xb8,0x51,0xb6,0x48,0xb4,0x50,0xb3,0x45,0xb5,0x52,0xb6,0x49,0xba,0x4f,0xbd,0x4b,
+ 0xbe,0x50,0xc0,0x4a,0xbf,0x50,0xbf,0x49,0xbe,0x54,0xbb,0x4b,0xb8,0x51,0xb4,0x48,
+ 0xb3,0x51,0xb1,0x46,0xb2,0x4d,0xb4,0x49,0xb7,0x53,0xba,0x47,0xbd,0x51,0xc0,0x4a,
+ 0xbf,0x53,0xbe,0x49,0xbd,0x4e,0xb9,0x4a,0xb7,0x51,0xb5,0x4c,0xb1,0x50,0xb0,0x47,
+ 0xb2,0x50,0xb4,0x48,0xb5,0x50,0xb9,0x46,0xbd,0x4e,0xbf,0x49,0xbf,0x4f,0xbd,0x48,
+ 0xbd,0x51,0xba,0x47,0xb7,0x4e,0xb3,0x44,0xb2,0x54,0xb0,0x4a,0xb1,0x55,0xb2,0x47,
+ 0xb4,0x4e,0xb8,0x4a,0xba,0x4f,0xb9,0x48,0xbe,0x51,0xba,0x4b,0xbd,0x51,0xb8,0x46,
+ 0xb8,0x50,0xb2,0x47,0xb1,0x4d,0xae,0x4a,0xae,0x51,0xb1,0x4a,0xb4,0x4d,0xb4,0x44,
+ 0xb8,0x50,0xba,0x48,0xbd,0x4f,0xbb,0x44,0xba,0x54,0xb9,0x48,0xb5,0x50,0xb3,0x4b,
+ 0xaf,0x53,0xaf,0x4e,0xae,0x50,0xb0,0x46,0xb1,0x52,0xb4,0x4c,0xb9,0x50,0xbc,0x46,
+ 0xba,0x51,0xbc,0x48,0xbe,0x4f,0xbb,0x42,0xb7,0x4e,0xb1,0x48,0xb2,0x4c,0xb0,0x4c,
+ 0xac,0x50,0xae,0x48,0xb2,0x4d,0xb6,0x4b,0xb7,0x51,0xba,0x47,0xba,0x50,0xbd,0x48,
+ 0xbc,0x50,0xb8,0x43,0xb6,0x4f,0xb3,0x48,0xb1,0x50,0xaf,0x47,0xae,0x56,0xaf,0x49,
+ 0xb0,0x51,0xb5,0x4b,0xb8,0x4a,0xb9,0x46,0xbd,0x51,0xbe,0x4b,0xbd,0x4f,0xbb,0x42,
+ 0xb7,0x4d,0xb4,0x47,0xb1,0x51,0xaf,0x48,0xae,0x54,0xb0,0x46,0xb3,0x52,0xb5,0x4a,
+ 0xba,0x4e,0xbd,0x4a,0xbe,0x4f,0xbd,0x49,0xbf,0x52,0xbd,0x4c,0xb8,0x54,0xb5,0x47,
+ 0xb5,0x4e,0xb0,0x48,0xb0,0x4e,0xb1,0x47,0xb4,0x51,0xb5,0x45,0xba,0x4f,0xbc,0x47,
+ 0xbe,0x4c,0xbf,0x47,0xbf,0x51,0xbd,0x49,0xba,0x54,0xb8,0x49,0xb7,0x4b,0xb3,0x4a,
+ 0xb1,0x50,0xb3,0x49,0xb4,0x4d,0xb7,0x47,0xb9,0x51,0xbd,0x4a,0xc0,0x50,0xc0,0x47,
+ 0xc0,0x4d,0xc0,0x48,0xbe,0x4f,0xb8,0x47,0xb6,0x53,0xb3,0x46,0xb2,0x53,0xb1,0x49,
+ 0xb4,0x4c,0xb5,0x48,0xba,0x4c,0xbb,0x46,0xbd,0x50,0xbf,0x49,0xbf,0x4e,0xbe,0x45,
+ 0xbc,0x52,0xb9,0x44,0xb6,0x4e,0xb3,0x45,0xb1,0x52,0xb4,0x44,0xb5,0x51,0xb7,0x46,
+ 0xb9,0x52,0xbd,0x46,0xbe,0x50,0xc1,0x4a,0xc1,0x51,0xc0,0x45,0xbd,0x51,0xbb,0x4b,
+ 0xb8,0x4f,0xb5,0x44,0xb4,0x48,0xb2,0x40,0xb3,0x4f,0xb7,0x48,0xb9,0x4f,0xbc,0x46,
+ 0xbe,0x53,0xbb,0x4d,0xc5,0x35,0xc3,0x30,0xc3,0x36,0xbf,0x31,0xbc,0x41,0xb9,0x32,
+ 0xb9,0x3e,0xb9,0x34,0xbb,0x33,0xbe,0x2f,0xc0,0x36,0xc1,0x30,0xc4,0x38,0xc5,0x2d,
+ 0xc4,0x36,0xc3,0x30,0xc2,0x36,0xbf,0x2d,0xbb,0x33,0xba,0x2d,0xb7,0x35,0xb8,0x2d,
+ 0xb9,0x36,0xbb,0x30,0xbe,0x37,0xc1,0x2f,0xc3,0x38,0xc4,0x2b,0xc4,0x37,0xc3,0x2f,
+ 0xc2,0x35,0xc1,0x2e,0xb9,0x32,0xb9,0x2e,0xb6,0x35,0xb8,0x2c,0xb9,0x38,0xbb,0x2a,
+ 0xbe,0x37,0xc1,0x2f,0xc2,0x36,0xc4,0x2a,0xc4,0x32,0xc4,0x2f,0xc1,0x35,0xbe,0x2c,
+ 0xbb,0x37,0xba,0x2d,0xb7,0x31,0xb6,0x2c,0xb9,0x36,0xba,0x2a,0xbc,0x32,0xc0,0x2e,
+ 0xc1,0x32,0xc2,0x2e,0xc3,0x37,0xc2,0x2b,0xbe,0x33,0xbd,0x2e,0xba,0x35,0xb8,0x2d,
+ 0xb5,0x37,0xb5,0x2e,0xb6,0x34,0xb8,0x2b,0xba,0x37,0xbe,0x30,0xbf,0x32,0xc2,0x2c,
+ 0xc5,0x36,0xc3,0x2d,0xc0,0x31,0xbe,0x2e,0xbc,0x3b,0xba,0x2d,0xb8,0x34,0xb8,0x2f,
+ 0xb8,0x37,0xb9,0x2c,0xbb,0x34,0xbe,0x2f,0xc0,0x39,0xc3,0x2c,0xc2,0x39,0xc0,0x2b,
+ 0xbf,0x36,0xbd,0x2f,0xba,0x34,0xb7,0x2c,0xb5,0x36,0xb4,0x2b,0xb4,0x31,0xb6,0x2b,
+ 0xb8,0x35,0xbb,0x2d,0xbd,0x36,0xc0,0x2c,0xc2,0x36,0xc2,0x2d,0xc1,0x39,0xbf,0x2f,
+ 0xbd,0x37,0xb8,0x2c,0xb6,0x33,0xb5,0x2c,0xb5,0x34,0xb8,0x2e,0xba,0x35,0xbe,0x2d,
+ 0xc2,0x3a,0xc3,0x32,0xc5,0x3b,0xc6,0x2c,0xc4,0x38,0xc3,0x2d,0xc1,0x35,0xbf,0x2c,
+ 0xbc,0x34,0xba,0x2e,0xba,0x34,0xbb,0x2e,0xbd,0x3a,0xc0,0x2a,0xc3,0x38,0xc4,0x31,
+ 0xc7,0x2f,0xc6,0x2c,0xc4,0x36,0xc2,0x2f,0xc0,0x34,0xbf,0x2f,0xbc,0x39,0xba,0x2f,
+ 0xba,0x33,0xbc,0x2d,0xbe,0x35,0xc0,0x2a,0xc1,0x32,0xc5,0x2b,0xc6,0x33,0xc4,0x30,
+ 0xc4,0x36,0xc2,0x2c,0xbf,0x33,0xbd,0x2c,0xbc,0x36,0xba,0x31,0xb8,0x39,0xb9,0x2d,
+ 0xbb,0x34,0xbc,0x30,0xc0,0x31,0xc2,0x2d,0xc6,0x34,0xc7,0x2f,0xc5,0x37,0xc4,0x2f,
+ 0xc1,0x37,0xbd,0x2a,0xbc,0x36,0xba,0x2b,0xba,0x38,0xbb,0x2f,0xbd,0x38,0xbe,0x2f,
+ 0xc2,0x38,0xc6,0x2f,0xc5,0x33,0xc4,0x30,0xc7,0x37,0xc6,0x2e,0xc1,0x32,0xbf,0x2e,
+ 0xbe,0x3b,0xbb,0x2d,0xb8,0x31,0xba,0x2d,0xbc,0x38,0xbe,0x30,0xbf,0x30,0xc3,0x29,
+ 0xc5,0x38,0xc7,0x2d,0xc6,0x31,0xc4,0x33,0xc4,0x34,0xc3,0x2e,0xbf,0x34,0xbe,0x2d,
+ 0xbc,0x32,0xbc,0x31,0xbd,0x34,0xbf,0x2e,0xc1,0x33,0xc6,0x2e,0xc8,0x36,0xc9,0x2f,
+ 0xc8,0x36,0xc6,0x31,0xc6,0x37,0xc0,0x2f,0xc0,0x36,0xbd,0x2f,0xbb,0x34,0xba,0x2a,
+ 0xbc,0x33,0xbe,0x30,0xc0,0x30,0xc2,0x2c,0xc6,0x35,0xc9,0x2f,0xc7,0x34,0xc6,0x2d,
+ 0xc5,0x38,0xc2,0x32,0xbf,0x34,0xbe,0x2b,0xbc,0x36,0xba,0x31,0xbc,0x33,0xbf,0x2d,
+ 0xc2,0x38,0xc5,0x2d,0xc5,0x35,0xc5,0x2c,0xc8,0x37,0xc6,0x2f,0xc3,0x35,0xc2,0x2e,
+ 0xc0,0x37,0xbd,0x2c,0xba,0x33,0xb9,0x30,0xba,0x33,0xbc,0x30,0xbf,0x34,0xc3,0x2e,
+ 0xc5,0x3a,0xc4,0x30,0xc6,0x36,0xc6,0x2e,0xc4,0x37,0xc0,0x30,0xbf,0x35,0xbd,0x2f,
+ 0xbb,0x3a,0xba,0x2c,0xb9,0x35,0xbb,0x2b,0xbe,0x38,0xc0,0x30,0xc2,0x36,0xc4,0x2f,
+ 0xc4,0x36,0xc4,0x32,0xc2,0x36,0xbf,0x30,0xbe,0x38,0xbb,0x31,0xba,0x36,0xb6,0x30,
+ 0xb9,0x3a,0xbb,0x30,0xbd,0x33,0xc1,0x2c,0xc4,0x3a,0xc4,0x31,0xc4,0x38,0xc4,0x2d,
+ 0xc2,0x37,0xbe,0x30,0xbb,0x37,0xba,0x2f,0xb9,0x36,0xb8,0x30,0xb8,0x36,0xba,0x2f,
+ 0xbd,0x36,0xc0,0x2e,0xc2,0x36,0xc4,0x2a,0xc6,0x33,0xc6,0x2b,0xc5,0x3a,0xc2,0x2c,
+ 0xbf,0x35,0xbd,0x2c,0xba,0x33,0xb7,0x31,0xb9,0x38,0xba,0x2f,0xbc,0x3a,0xbe,0x28,
+ 0xc3,0x37,0xc3,0x2d,0xc3,0x31,0xc3,0x30,0xc2,0x36,0xc0,0x2d,0xbd,0x36,0xbd,0x2e,
+ 0xba,0x36,0xb8,0x30,0xb8,0x33,0xba,0x2d,0xbc,0x36,0xbe,0x30,0xc0,0x38,0xc1,0x31,
+ 0xc4,0x35,0xc5,0x2d,0xc3,0x35,0xbf,0x30,0xbd,0x34,0xbb,0x2f,0xba,0x35,0xb7,0x2e,
+ 0xb6,0x33,0xb9,0x2d,0xba,0x36,0xbd,0x32,0xc0,0x39,0xc3,0x2d,0xc3,0x36,0xc3,0x2d,
+ 0xc3,0x37,0xc2,0x2f,0xc0,0x36,0xbc,0x2f,0xba,0x37,0xb9,0x2e,0xb7,0x36,0xb6,0x2d,
+ 0xb9,0x38,0xbd,0x2e,0xbe,0x31,0xbf,0x2e,0xc2,0x37,0xc3,0x2b,0xc0,0x37,0xbf,0x32,
+ 0xbd,0x35,0xbb,0x2e,0xb8,0x38,0xb7,0x2f,0xb7,0x38,0xb7,0x2f,0xb8,0x37,0xb9,0x30,
+ 0xbe,0x37,0xbf,0x2b,0xc0,0x32,0xc1,0x33,0xc0,0x34,0xbe,0x2f,0xbc,0x34,0xb9,0x2b,
+ 0xb9,0x36,0xb6,0x2c,0xb4,0x36,0xb5,0x2d,0xb7,0x37,0xb9,0x2c,0xbc,0x36,0xbe,0x2f,
+ 0xbe,0x32,0xbf,0x32,0xbf,0x34,0xbd,0x2e,0xbb,0x36,0xba,0x2b,0xb7,0x36,0xb4,0x30,
+ 0xb6,0x35,0xb5,0x2b,0xb6,0x33,0xb9,0x2d,0xbb,0x36,0xc1,0x2a,0xc1,0x35,0xc0,0x2e,
+ 0xc1,0x32,0xc1,0x2c,0xbe,0x35,0xbb,0x2e,0xb7,0x37,0xb7,0x2d,0xb6,0x3b,0xb6,0x2d,
+ 0xb7,0x33,0xb9,0x2f,0xbc,0x37,0xbf,0x2b,0xc0,0x36,0xbf,0x2c,0xbf,0x35,0xc0,0x2e,
+ 0xbe,0x39,0xb9,0x30,0xb8,0x30,0xb5,0x2f,0xb4,0x38,0xb4,0x2c,0xb6,0x36,0xb7,0x30,
+ 0xbc,0x34,0xbe,0x2d,0xc0,0x32,0xc0,0x2a,0xc1,0x37,0xbf,0x30,0xbd,0x36,0xba,0x2b,
+ 0xb9,0x34,0xb8,0x2c,0xb6,0x33,0xb4,0x2b,0xb5,0x33,0xb7,0x30,0xb9,0x33,0xbd,0x2f,
+ 0xbf,0x36,0xc1,0x2d,0xc2,0x35,0xc0,0x2d,0xbf,0x39,0xbc,0x2c,0xba,0x39,0xb8,0x2e,
+ 0xb6,0x32,0xb5,0x28,0xb7,0x3a,0xb8,0x2f,0xba,0x34,0xbd,0x31,0xc1,0x37,0xc1,0x2b,
+ 0xc2,0x37,0xc1,0x30,0xbd,0x32,0xba,0x2e,0xba,0x37,0xb7,0x2c,0xb3,0x31,0xb3,0x31,
+ 0xb6,0x36,0xb8,0x2d,0xb9,0x36,0xbd,0x29,0xbf,0x39,0xc1,0x2f,0xc0,0x32,0xc0,0x2c,
+ 0xc1,0x39,0xba,0x31,0xb9,0x37,0xb7,0x31,0xb7,0x38,0xb4,0x29,0xb5,0x2f,0xb7,0x26,
+ 0xb8,0x31,0xbb,0x2d,0xbd,0x36,0xbe,0x2f,0xbe,0x36,0xb8,0x34,0xbf,0x53,0xbd,0x4f,
+ 0xbc,0x52,0xb9,0x57,0xb6,0x61,0xb4,0x57,0xb7,0x5b,0xb9,0x50,0xba,0x57,0xbd,0x4d,
+ 0xbe,0x58,0xc0,0x50,0xc1,0x56,0xc0,0x55,0xbf,0x56,0xbc,0x50,0xb9,0x59,0xb7,0x50,
+ 0xb8,0x57,0xb6,0x4f,0xb6,0x54,0xb8,0x4c,0xbb,0x59,0xbb,0x50,0xbe,0x56,0xbf,0x51,
+ 0xbf,0x55,0xbe,0x51,0xbe,0x5a,0xb9,0x4c,0xb8,0x59,0xb5,0x52,0xb4,0x5b,0xb3,0x50,
+ 0xb3,0x5a,0xb6,0x53,0xbb,0x56,0xbc,0x4f,0xba,0x57,0xbd,0x4e,0xc2,0x56,0xbe,0x4f,
+ 0xb9,0x5a,0xbc,0x4e,0xb8,0x56,0xb6,0x50,0xb5,0x58,0xb5,0x4e,0xb5,0x5b,0xb5,0x52,
+ 0xb7,0x54,0xbb,0x4e,0xbd,0x56,0xbe,0x53,0xbf,0x55,0xc0,0x4e,0xbe,0x56,0xbd,0x52,
+ 0xb9,0x55,0xb7,0x51,0xb6,0x56,0xb5,0x4e,0xb6,0x5a,0xb6,0x51,0xb8,0x59,0xb7,0x51,
+ 0xbc,0x59,0xbd,0x52,0xbc,0x54,0xbc,0x4c,0xbc,0x58,0xba,0x4e,0xb7,0x57,0xb6,0x4f,
+ 0xb4,0x57,0xb1,0x4c,0xb1,0x58,0xb0,0x4f,0xb4,0x5a,0xb6,0x51,0xba,0x5a,0xbb,0x52,
+ 0xbe,0x56,0xbf,0x4a,0xbc,0x55,0xba,0x4c,0xb7,0x57,0xb3,0x51,0xb1,0x5c,0xb2,0x4d,
+ 0xb3,0x53,0xb4,0x54,0xb5,0x57,0xb8,0x51,0xbb,0x5a,0xba,0x52,0xbd,0x55,0xbd,0x4f,
+ 0xbb,0x58,0xb9,0x4f,0xb6,0x56,0xb4,0x50,0xb0,0x5a,0xae,0x4b,0xb0,0x55,0xb3,0x4f,
+ 0xb4,0x5d,0xb4,0x51,0xba,0x5a,0xbb,0x50,0xbd,0x5a,0xbd,0x51,0xbd,0x57,0xb9,0x50,
+ 0xb8,0x56,0xb7,0x4f,0xb3,0x55,0xb2,0x4f,0xb1,0x58,0xb4,0x4e,0xb6,0x54,0xb6,0x4e,
+ 0xb9,0x5b,0xbc,0x50,0xbd,0x59,0xbb,0x51,0xbd,0x5d,0xbb,0x51,0xb6,0x57,0xb5,0x4f,
+ 0xb2,0x57,0xb0,0x50,0xaf,0x53,0xaf,0x50,0xb3,0x5a,0xb5,0x4f,0xb6,0x55,0xb9,0x50,
+ 0xbc,0x5b,0xb9,0x56,0xb8,0x57,0xb9,0x4c,0xb8,0x59,0xb2,0x51,0xb1,0x57,0xb0,0x51,
+ 0xb0,0x55,0xb0,0x4c,0xb3,0x56,0xb3,0x4e,0xb4,0x5b,0xb6,0x52,0xba,0x5a,0xb9,0x4c,
+ 0xb8,0x59,0xb9,0x4d,0xb5,0x57,0xb3,0x54,0xb2,0x58,0xb0,0x4c,0xae,0x59,0xaf,0x51,
+ 0xb1,0x5b,0xb0,0x50,0xb0,0x56,0xb9,0x52,0xb9,0x5c,0xb7,0x4b,0xb9,0x56,0xb9,0x4e,
+ 0xb7,0x5a,0xb2,0x52,0xb0,0x58,0xaf,0x51,0xae,0x58,0xad,0x4d,0xaf,0x58,0xb1,0x50,
+ 0xb4,0x54,0xb6,0x52,0xb8,0x55,0xba,0x4e,0xb7,0x58,0xb8,0x53,0xb7,0x5c,0xb1,0x50,
+ 0xb0,0x56,0xb0,0x4d,0xae,0x5a,0xad,0x4f,0xaf,0x58,0xb1,0x50,0xb3,0x57,0xb6,0x4b,
+ 0xb6,0x56,0xb7,0x4f,0xb7,0x55,0xb3,0x4d,0xb2,0x57,0xb2,0x4f,0xaf,0x56,0xab,0x4d,
+ 0xab,0x55,0xac,0x50,0xaf,0x57,0xb1,0x51,0xb1,0x56,0xb5,0x51,0xb8,0x5a,0xb8,0x50,
+ 0xb7,0x55,0xb7,0x51,0xb4,0x57,0xb1,0x4d,0xb1,0x58,0xaf,0x4d,0xad,0x5a,0xae,0x4e,
+ 0xae,0x52,0xaf,0x4f,0xb1,0x54,0xb2,0x51,0xb5,0x58,0xb6,0x50,0xb6,0x58,0xb8,0x4e,
+ 0xb5,0x58,0xad,0x4f,0xa7,0x55,0xac,0x4f,0xaa,0x58,0xa2,0x4f,0xa6,0x56,0xac,0x50,
+ 0xb0,0x55,0xb2,0x52,0xb4,0x5a,0xb4,0x4d,0xb6,0x5a,0xb5,0x4c,0xb3,0x59,0xb2,0x50,
+ 0xaf,0x57,0xac,0x4f,0xac,0x57,0xaa,0x4f,0xab,0x58,0xac,0x50,0xae,0x5a,0xb1,0x4e,
+ 0xb5,0x57,0xb3,0x4d,0xb2,0x59,0xb4,0x4f,0xb3,0x5c,0xae,0x4e,0xab,0x58,0xaa,0x4d,
+ 0xa9,0x58,0xa8,0x52,0xa8,0x56,0xaa,0x4c,0xad,0x5b,0xae,0x4e,0xb1,0x57,0xb0,0x4c,
+ 0xb2,0x54,0xb4,0x4f,0xb1,0x56,0xad,0x50,0xad,0x5b,0xad,0x4f,0xa9,0x56,0xa9,0x4d,
+ 0xa8,0x58,0xa9,0x53,0xad,0x55,0xb0,0x53,0xb2,0x5c,0xb3,0x54,0xb6,0x56,0xb4,0x50,
+ 0xb2,0x5a,0xb1,0x4d,0xaf,0x57,0xae,0x53,0xaa,0x59,0xa8,0x4e,0xa9,0x55,0xac,0x4c,
+ 0xad,0x5c,0xae,0x50,0xb1,0x58,0xb4,0x51,0xb4,0x5d,0xb3,0x50,0xb3,0x56,0xb1,0x4f,
+ 0xaf,0x59,0xad,0x4e,0xac,0x57,0xaa,0x4e,0xab,0x5c,0xac,0x54,0xae,0x56,0xae,0x50,
+ 0xb2,0x53,0xb4,0x53,0xb7,0x56,0xb3,0x57,0xb3,0x57,0xb5,0x4f,0xb0,0x58,0xab,0x53,
+ 0xab,0x58,0xa8,0x52,0xa8,0x55,0xa9,0x52,0xab,0x5b,0xad,0x4f,0xb0,0x59,0xb1,0x4e,
+ 0xb5,0x59,0xb3,0x51,0xb1,0x57,0xb1,0x52,0xb3,0x5b,0xaf,0x55,0xab,0x59,0xab,0x52,
+ 0xac,0x5e,0xac,0x4e,0xac,0x5b,0xae,0x52,0xb1,0x5d,0xb3,0x53,0xb5,0x59,0xb4,0x52,
+ 0xb6,0x5c,0xb4,0x4e,0xb1,0x55,0xaf,0x52,0xae,0x5d,0xad,0x4e,0xa9,0x55,0xa8,0x4f,
+ 0xac,0x58,0xae,0x4f,0xaf,0x58,0xb1,0x4f,0xb6,0x5d,0xb4,0x4d,0xb4,0x59,0xb4,0x4e,
+ 0xb3,0x59,0xb0,0x54,0xae,0x58,0xab,0x51,0xa8,0x58,0xa8,0x52,0xab,0x58,0xad,0x4e,
+ 0xae,0x58,0xb1,0x51,0xb4,0x58,0xb6,0x4f,0xb6,0x5a,0xb2,0x4f,0xb0,0x58,0xae,0x4f,
+ 0xae,0x58,0xa8,0x4b,0xa8,0x58,0xa9,0x4e,0xab,0x5b,0xac,0x52,0xae,0x5a,0xaf,0x4e,
+ 0xb3,0x58,0xb2,0x53,0xb2,0x54,0xb1,0x51,0xb1,0x59,0xae,0x4c,0xab,0x57,0xab,0x4f,
+ 0xa9,0x53,0xa7,0x52,0xa7,0x56,0xaa,0x4e,0xae,0x58,0xb1,0x4f,0xb1,0x59,0xb1,0x53,
+ 0xb4,0x5a,0xb3,0x52,0xb0,0x55,0xaf,0x4d,0xad,0x5b,0xaa,0x50,0xa7,0x59,0xa7,0x4c,
+ 0xa9,0x5b,0xac,0x4d,0xad,0x52,0xb0,0x4f,0xb4,0x5c,0xb5,0x54,0xb5,0x53,0xb4,0x52,
+ 0xb4,0x58,0xb0,0x52,0xad,0x50,0xad,0x4b,0xa9,0x59,0xa6,0x52,0xab,0x56,0xaa,0x4e,
+ 0xab,0x5c,0xad,0x50,0xb2,0x53,0xb5,0x4e,0xb4,0x5d,0xb2,0x50,0xb1,0x55,0xb0,0x4e,
+ 0xae,0x5a,0xab,0x51,0xa8,0x52,0xa8,0x50,0xaa,0x59,0xaa,0x54,0xac,0x58,0xae,0x52,
+ 0xb4,0x59,0xb4,0x50,0xb4,0x56,0xad,0x4d,0xb0,0x5b,0xb1,0x4e,0xa9,0x5d,0xa5,0x4b,
+ 0xa9,0x57,0xa9,0x50,0xa7,0x59,0xaa,0x50,0xad,0x5b,0xae,0x53,0xb2,0x5a,0xb4,0x4e,
+ 0xb5,0x5c,0xb3,0x50,0xb2,0x54,0xaf,0x50,0xad,0x5d,0xab,0x4f,0xa9,0x55,0xa7,0x52,
+ 0xa6,0x59,0xa7,0x4d,0xaa,0x4f,0xae,0x48,0xb0,0x57,0xb2,0x54,0xb2,0x56,0xaf,0x50,
+ 0xb2,0x58,0xa9,0x55,0xaf,0x44,0xaf,0x41,0xad,0x4b,0xab,0x43,0xad,0x4d,0xad,0x49,
+ 0xb0,0x4d,0xb2,0x41,0xb4,0x48,0xb7,0x3f,0xb8,0x4d,0xb6,0x44,0xb5,0x4a,0xb2,0x3d,
+ 0xb1,0x44,0xac,0x40,0xac,0x47,0xaa,0x3d,0xac,0x4a,0xac,0x41,0xaf,0x49,0xb1,0x42,
+ 0xb4,0x4a,0xb7,0x3f,0xb6,0x45,0xb6,0x3a,0xb5,0x46,0xb2,0x41,0xb0,0x49,0xad,0x41,
+ 0xac,0x49,0xaa,0x3e,0xaa,0x43,0xab,0x45,0xad,0x49,0xb0,0x42,0xb2,0x47,0xb2,0x40,
+ 0xb5,0x4a,0xb4,0x40,0xb2,0x45,0xb0,0x40,0xaf,0x48,0xab,0x42,0xa9,0x49,0xa8,0x41,
+ 0xa8,0x4a,0xa7,0x42,0xaa,0x47,0xae,0x42,0xb2,0x4a,0xb2,0x44,0xb3,0x4a,0xb3,0x3f,
+ 0xb2,0x4b,0xb1,0x41,0xae,0x44,0xab,0x41,0xaa,0x4f,0xa6,0x42,0xa6,0x45,0xa7,0x43,
+ 0xa8,0x46,0xaa,0x42,0xaf,0x44,0xaf,0x3e,0xb0,0x4b,0xb3,0x3f,0xb2,0x45,0xae,0x40,
+ 0xae,0x48,0xac,0x42,0xa8,0x48,0xa7,0x42,0xa8,0x4b,0xa8,0x3d,0xab,0x4a,0xab,0x3e,
+ 0xaf,0x45,0xb1,0x41,0xb1,0x44,0xb0,0x44,0xb1,0x4b,0xaf,0x43,0xac,0x4a,0xaa,0x3f,
+ 0xa9,0x49,0xa7,0x3f,0xa6,0x4b,0xa6,0x3d,0xa8,0x4c,0xa9,0x41,0xac,0x47,0xae,0x44,
+ 0xb1,0x49,0xb0,0x43,0xb1,0x46,0xb0,0x43,0xaf,0x48,0xa9,0x40,0xa8,0x44,0xa7,0x3f,
+ 0xa6,0x4b,0xa4,0x40,0xa6,0x47,0xa7,0x43,0xab,0x4b,0xaf,0x44,0xaf,0x4c,0xb0,0x3f,
+ 0xaf,0x4a,0xad,0x3f,0xab,0x43,0xa9,0x3e,0xa7,0x46,0xa5,0x40,0xa5,0x4e,0xa3,0x40,
+ 0xa4,0x46,0xa7,0x42,0xa9,0x45,0xac,0x41,0xaf,0x4a,0xaf,0x3d,0xaf,0x49,0xaf,0x3b,
+ 0xac,0x47,0xaa,0x3e,0xa7,0x44,0xa5,0x44,0xa4,0x4b,0xa3,0x41,0xa6,0x49,0xa7,0x3d,
+ 0xa8,0x48,0xaa,0x42,0xae,0x4a,0xaf,0x3f,0xb0,0x4b,0xad,0x40,0xac,0x4b,0xa8,0x43,
+ 0xa9,0x49,0xa6,0x42,0xa4,0x48,0xa3,0x42,0xa4,0x4b,0xa6,0x45,0xa7,0x46,0xaa,0x3d,
+ 0xac,0x46,0xad,0x46,0xad,0x46,0xad,0x3f,0xab,0x4b,0xa7,0x41,0xa5,0x4b,0xa4,0x3e,
+ 0xa2,0x48,0xa0,0x3c,0xa2,0x46,0xa3,0x3e,0xa7,0x4c,0xa7,0x42,0xab,0x4c,0xac,0x3d,
+ 0xae,0x48,0xac,0x41,0xab,0x49,0xa8,0x43,0xa6,0x4b,0xa1,0x42,0xa0,0x47,0x9e,0x3e,
+ 0xa1,0x49,0xa0,0x41,0xa3,0x48,0xa7,0x3f,0xaa,0x4b,0xa9,0x41,0xab,0x4c,0xab,0x43,
+ 0xaa,0x4a,0xa7,0x42,0xa6,0x43,0xa4,0x3e,0xa2,0x4d,0xa0,0x41,0xa1,0x45,0xa1,0x44,
+ 0xa2,0x47,0xa6,0x3e,0xa7,0x49,0xaa,0x3f,0xab,0x4d,0xa8,0x44,0xaa,0x4b,0xa5,0x3c,
+ 0xa2,0x48,0xa0,0x41,0xa0,0x46,0x9b,0x40,0x9d,0x4c,0xa0,0x41,0xa3,0x48,0xa5,0x43,
+ 0xa7,0x45,0xa9,0x3f,0xaa,0x45,0xa7,0x43,0xa8,0x4d,0xa6,0x40,0xa3,0x4a,0xa0,0x41,
+ 0xa0,0x4b,0x9f,0x3e,0x9e,0x47,0xa0,0x3e,0xa2,0x47,0xa5,0x42,0xa5,0x46,0xa6,0x40,
+ 0xa9,0x48,0xaa,0x3f,0xa7,0x47,0xa5,0x41,0xa4,0x4c,0xa2,0x3e,0xa1,0x48,0x9e,0x3f,
+ 0x9e,0x49,0x9e,0x44,0xa1,0x4c,0xa3,0x41,0xa6,0x48,0xa9,0x40,0xab,0x4b,0xa8,0x40,
+ 0xaa,0x4b,0xaa,0x3f,0xa5,0x48,0xa3,0x41,0xa4,0x4e,0x9f,0x40,0x9d,0x4a,0x9d,0x41,
+ 0xa2,0x49,0xa2,0x43,0xa5,0x46,0xa9,0x40,0xab,0x4f,0xa9,0x42,0xab,0x49,0xa9,0x3d,
+ 0xa6,0x4a,0xa4,0x3e,0xa1,0x48,0x9f,0x42,0x9f,0x4b,0xa0,0x3d,0xa2,0x4a,0xa3,0x3f,
+ 0xa5,0x4c,0xa7,0x40,0xa9,0x4b,0xa7,0x41,0xa7,0x4b,0xa7,0x41,0xa5,0x4a,0xa0,0x3c,
+ 0x9f,0x4a,0x9d,0x42,0x9c,0x49,0x9d,0x45,0x9e,0x4b,0xa1,0x41,0xa4,0x49,0xa4,0x40,
+ 0xa7,0x46,0xa7,0x3f,0xa7,0x46,0xa5,0x3e,0xa4,0x4a,0xa1,0x44,0x9f,0x49,0x9d,0x40,
+ 0x9e,0x48,0x9d,0x40,0x9e,0x48,0xa2,0x3e,0xa4,0x4b,0xa6,0x41,0xa9,0x45,0xa8,0x40,
+ 0xa9,0x48,0xa8,0x41,0xa6,0x48,0xa3,0x43,0xa0,0x4b,0x9e,0x40,0x9e,0x48,0x9d,0x41,
+ 0xa1,0x49,0xa1,0x40,0xa4,0x49,0xa7,0x3f,0xa9,0x49,0xa8,0x42,0xaa,0x4a,0xaa,0x3f,
+ 0xa8,0x4b,0xa4,0x40,0xa0,0x47,0x9d,0x41,0x9c,0x4c,0x9c,0x42,0x9d,0x4c,0x9c,0x40,
+ 0xa3,0x49,0xa2,0x40,0xa2,0x48,0xa4,0x46,0xa8,0x4c,0xa6,0x43,0xa2,0x49,0xa2,0x42,
+ 0xa0,0x4c,0x9c,0x3d,0x9d,0x47,0x9c,0x3e,0x9d,0x4b,0x9f,0x3f,0xa2,0x48,0xa5,0x44,
+ 0xa5,0x4b,0xa8,0x3e,0xa7,0x45,0xa8,0x3f,0xa7,0x4b,0xa5,0x43,0xa1,0x47,0x9e,0x45,
+ 0x9e,0x46,0x9c,0x3f,0x9d,0x48,0x9f,0x3e,0xa0,0x51,0xa3,0x42,0xa7,0x4a,0xa7,0x41,
+ 0xa8,0x4c,0xa7,0x3f,0xa8,0x46,0xa5,0x3e,0xa1,0x4f,0x9f,0x41,0x9e,0x46,0x9c,0x41,
+ 0x9f,0x4a,0xa1,0x3d,0xa2,0x4b,0xa5,0x3f,0xa7,0x4a,0xa8,0x41,0xa8,0x4b,0xa7,0x3b,
+ 0xa7,0x48,0xa5,0x40,0xa3,0x47,0x9f,0x3e,0x9e,0x4a,0x9d,0x40,0x9f,0x4b,0x9f,0x42,
+ 0xa2,0x45,0xa4,0x42,0xa7,0x44,0xa7,0x41,0xa9,0x4b,0xa7,0x40,0xa8,0x49,0xa6,0x45,
+ 0xa3,0x47,0x9e,0x42,0x9d,0x48,0x9c,0x41,0x9c,0x4b,0x9d,0x41,0x9f,0x4b,0xa1,0x40,
+ 0xa6,0x48,0xa7,0x41,0xa8,0x4c,0xa6,0x3e,0xa7,0x4b,0xa6,0x3d,0xa2,0x48,0xa0,0x3f,
+ 0x9e,0x4b,0x9d,0x43,0x9c,0x49,0x9d,0x3d,0x9f,0x4b,0xa3,0x3f,0xa6,0x49,0xa7,0x43,
+ 0xa8,0x4b,0xa8,0x41,0xa8,0x48,0xa5,0x40,0xa4,0x4d,0xa1,0x41,0x9d,0x47,0x9b,0x42,
+ 0x9a,0x48,0x9c,0x43,0x9f,0x46,0x9f,0x40,0xa4,0x47,0xa6,0x42,0xa8,0x49,0xa8,0x42,
+ 0xa9,0x4b,0xa7,0x3f,0xa3,0x47,0xa0,0x3e,0xa0,0x4e,0x9d,0x40,0x9c,0x45,0x9d,0x43,
+ 0xa1,0x48,0xa3,0x3f,0xa4,0x46,0xa7,0x3f,0xa8,0x4d,0xa8,0x40,0xa8,0x4b,0xa6,0x3f,
+ 0xa5,0x47,0xa1,0x3c,0x9d,0x45,0x9c,0x3d,0x9d,0x4c,0x9b,0x3f,0x9d,0x4c,0xa1,0x3f,
+ 0xa5,0x4c,0xa7,0x43,0xa7,0x47,0xa9,0x3f,0xa9,0x4b,0xa5,0x3e,0xa3,0x48,0xa2,0x3e,
+ 0xa0,0x47,0x9c,0x40,0x9a,0x49,0x9b,0x46,0x9e,0x50,0xa0,0x3f,0xa4,0x41,0xa5,0x38,
+ 0xa8,0x47,0xa8,0x40,0xa6,0x43,0xa4,0x40,0xa3,0x4d,0x9a,0x44,0xa1,0x54,0x9f,0x52,
+ 0xa0,0x56,0xa2,0x56,0xa4,0x62,0xa8,0x57,0xaa,0x62,0xab,0x55,0xac,0x59,0xac,0x55,
+ 0xac,0x56,0xab,0x4e,0xa7,0x55,0xa5,0x4e,0xa3,0x5d,0xa0,0x54,0xa2,0x56,0xa1,0x51,
+ 0xa4,0x57,0xa7,0x4f,0xa9,0x5a,0xaa,0x53,0xad,0x5e,0xac,0x4c,0xaa,0x58,0xac,0x4d,
+ 0xa8,0x55,0xa5,0x4f,0xa4,0x54,0x9f,0x53,0xa0,0x5d,0xa0,0x50,0xa1,0x56,0xa3,0x4f,
+ 0xa7,0x58,0xaa,0x54,0xaa,0x58,0xaa,0x4e,0xaa,0x58,0xab,0x4e,0xa6,0x56,0xa2,0x54,
+ 0xa3,0x5c,0xa0,0x54,0x9d,0x54,0x9d,0x50,0xa1,0x58,0xa2,0x54,0xa6,0x5c,0xa9,0x4d,
+ 0xaa,0x5b,0xaa,0x51,0xab,0x55,0xaa,0x4b,0xa7,0x5b,0xa4,0x52,0xa2,0x5a,0x9f,0x51,
+ 0xa0,0x56,0x9f,0x4f,0xa1,0x59,0xa2,0x51,0xa7,0x59,0xa8,0x53,0xab,0x5e,0xab,0x51,
+ 0xab,0x59,0xaa,0x53,0xa8,0x56,0xa4,0x4f,0xa2,0x5a,0x9f,0x4e,0x9d,0x59,0x9e,0x52,
+ 0xa0,0x5b,0xa1,0x52,0xa4,0x5a,0xa7,0x4f,0xa9,0x5d,0xaa,0x4f,0xac,0x5c,0xa7,0x50,
+ 0xa7,0x59,0xa4,0x51,0xa0,0x55,0x9d,0x50,0x9d,0x5d,0x9b,0x50,0x9d,0x58,0x9e,0x54,
+ 0xa2,0x57,0xa6,0x4f,0xa8,0x56,0xa8,0x54,0xa9,0x5d,0xaa,0x4d,0xa6,0x59,0xa3,0x51,
+ 0xa4,0x5c,0xa0,0x50,0x9d,0x58,0x9e,0x55,0x9f,0x5b,0xa1,0x54,0xa1,0x58,0xa5,0x52,
+ 0xa8,0x59,0xa7,0x4f,0xa8,0x58,0xa8,0x54,0xa7,0x5c,0xa2,0x53,0xa2,0x59,0x9f,0x4e,
+ 0x9e,0x58,0x9e,0x4f,0x9f,0x59,0xa0,0x54,0xa3,0x5f,0xa4,0x50,0xa7,0x57,0xa8,0x50,
+ 0xa9,0x58,0xa7,0x50,0xa7,0x57,0xa4,0x4f,0xa0,0x5b,0x9e,0x4f,0x9d,0x59,0x9a,0x50,
+ 0x9c,0x5b,0x9e,0x54,0xa0,0x59,0xa2,0x53,0xa5,0x5f,0xa6,0x52,0xa8,0x5d,0xa6,0x52,
+ 0xa4,0x56,0xa1,0x51,0xa0,0x58,0x9e,0x54,0x9d,0x5c,0x9a,0x51,0x9c,0x5e,0x9d,0x51,
+ 0x9f,0x57,0xa2,0x52,0xa5,0x5a,0xa4,0x54,0xa8,0x5e,0xa7,0x52,0xa6,0x58,0xa2,0x54,
+ 0xa2,0x5d,0x9e,0x51,0x9c,0x57,0x9c,0x51,0x9c,0x5c,0x9b,0x4d,0x9f,0x58,0xa2,0x4f,
+ 0xa5,0x5d,0xa5,0x4d,0xa7,0x57,0xa6,0x53,0xa5,0x57,0xa2,0x55,0x9f,0x5a,0x9e,0x52,
+ 0x9c,0x5e,0x98,0x50,0x99,0x5b,0x9b,0x53,0x9f,0x58,0xa0,0x4e,0xa2,0x55,0xa4,0x54,
+ 0xa4,0x59,0xa4,0x51,0xa4,0x5c,0xa1,0x52,0x9f,0x59,0x9c,0x51,0x9b,0x55,0x99,0x4f,
+ 0x99,0x5c,0x99,0x54,0x9b,0x54,0x9e,0x51,0xa3,0x5e,0xa3,0x55,0xa3,0x58,0xa4,0x4e,
+ 0xa6,0x5f,0xa1,0x52,0x9f,0x59,0x9d,0x53,0x9d,0x5e,0x99,0x50,0x99,0x59,0x99,0x51,
+ 0x9c,0x59,0x9d,0x51,0xa0,0x58,0xa2,0x52,0xa4,0x5b,0xa5,0x4d,0xa5,0x5a,0xa0,0x52,
+ 0x9f,0x55,0x9c,0x51,0x9b,0x59,0x96,0x4f,0x96,0x59,0x98,0x52,0x9a,0x58,0x9c,0x51,
+ 0x9f,0x5a,0xa2,0x56,0xa4,0x59,0xa3,0x51,0xa3,0x57,0xa1,0x52,0x9e,0x55,0x9b,0x54,
+ 0x9a,0x5a,0x97,0x54,0x97,0x55,0x97,0x50,0x99,0x55,0x99,0x51,0x9d,0x55,0xa1,0x4d,
+ 0xa0,0x5a,0xa1,0x55,0xa2,0x5c,0x9f,0x51,0x9f,0x5c,0x9c,0x4a,0x9b,0x5b,0x96,0x54,
+ 0x95,0x5c,0x95,0x52,0x98,0x5a,0x9a,0x51,0x9d,0x58,0xa0,0x4f,0xa0,0x53,0xa1,0x51,
+ 0xa2,0x5c,0x9f,0x55,0x9d,0x59,0x9b,0x50,0x98,0x5c,0x96,0x55,0x94,0x58,0x94,0x52,
+ 0x96,0x60,0x98,0x55,0x9c,0x58,0x9e,0x53,0xa1,0x5a,0xa0,0x53,0xa0,0x5a,0x9f,0x54,
+ 0x9e,0x5b,0x9a,0x50,0x99,0x57,0x96,0x54,0x95,0x5c,0x94,0x55,0x95,0x5c,0x98,0x52,
+ 0x9b,0x5c,0x9c,0x56,0x9e,0x59,0xa1,0x4e,0x9f,0x5c,0x9c,0x51,0x9c,0x5a,0x9b,0x55,
+ 0x97,0x58,0x93,0x53,0x93,0x5a,0x92,0x55,0x94,0x5a,0x94,0x52,0x98,0x5d,0x9c,0x57,
+ 0x9d,0x56,0x9e,0x51,0x9f,0x58,0x9c,0x53,0x9b,0x5b,0x98,0x50,0x98,0x5b,0x94,0x55,
+ 0x92,0x5a,0x93,0x4f,0x95,0x55,0x94,0x54,0x98,0x5a,0x9a,0x4f,0x9d,0x58,0x9c,0x4f,
+ 0x9d,0x58,0x9c,0x52,0x9c,0x54,0x99,0x4f,0x97,0x5b,0x93,0x52,0x91,0x5b,0x90,0x50,
+ 0x92,0x5b,0x94,0x52,0x95,0x5a,0x98,0x50,0x9c,0x5e,0x9c,0x56,0x9e,0x5c,0x9c,0x51,
+ 0x9e,0x5f,0x9a,0x55,0x98,0x5a,0x93,0x51,0x92,0x5a,0x92,0x4f,0x92,0x58,0x93,0x50,
+ 0x96,0x5c,0x99,0x54,0x9c,0x58,0x9b,0x51,0x9c,0x5a,0x9c,0x53,0x9d,0x59,0x98,0x51,
+ 0x96,0x5c,0x96,0x51,0x94,0x59,0x91,0x51,0x92,0x5a,0x95,0x52,0x97,0x59,0x99,0x53,
+ 0x9d,0x5c,0x9d,0x51,0xa0,0x5b,0xa0,0x50,0xa0,0x5b,0x9d,0x50,0x9c,0x5b,0x98,0x52,
+ 0x96,0x5a,0x95,0x55,0x95,0x51,0x95,0x50,0x99,0x5b,0x9a,0x55,0x9d,0x5a,0x9f,0x52,
+ 0xa1,0x5b,0xa0,0x53,0xa0,0x57,0x9e,0x4e,0x9b,0x5a,0x98,0x52,0x97,0x5d,0x94,0x4f,
+ 0x94,0x59,0x95,0x53,0x98,0x59,0x99,0x50,0x9c,0x59,0x9d,0x53,0xa0,0x59,0x9f,0x50,
+ 0xa0,0x5a,0x9e,0x54,0x9c,0x57,0x97,0x58,0x96,0x5b,0x94,0x4f,0x93,0x58,0x93,0x51,
+ 0x97,0x56,0x9a,0x52,0x9c,0x57,0x9d,0x50,0x9f,0x5a,0xa0,0x50,0xa0,0x59,0x9c,0x51,
+ 0x9c,0x57,0x9a,0x53,0x96,0x55,0x92,0x54,0x92,0x59,0x91,0x52,0x95,0x5a,0x99,0x4e,
+ 0x9c,0x5a,0x9d,0x51,0xa0,0x59,0x9f,0x50,0x9f,0x58,0x9e,0x54,0x9b,0x57,0x98,0x52,
+ 0x98,0x59,0x94,0x51,0x93,0x5b,0x94,0x4f,0x98,0x5a,0x99,0x52,0x9d,0x5c,0x9f,0x53,
+ 0xa0,0x5a,0xa1,0x50,0xa2,0x5b,0x9e,0x50,0x9d,0x5d,0x9b,0x54,0x98,0x59,0x95,0x4f,
+ 0x94,0x55,0x95,0x50,0x97,0x55,0x99,0x53,0x9c,0x56,0xa0,0x54,0xa3,0x58,0xa2,0x52,
+ 0xa2,0x58,0xa2,0x4e,0xa0,0x5c,0x9a,0x4f,0x96,0x5b,0x93,0x51,0x92,0x58,0x93,0x4a,
+ 0x97,0x5c,0x98,0x53,0x9c,0x60,0xa0,0x54,0xa3,0x5a,0xa2,0x51,0xa3,0x57,0xa3,0x4e,
+ 0xa2,0x5b,0x9e,0x4d,0x9c,0x5a,0x99,0x51,0x99,0x5d,0x98,0x4e,0x9a,0x5a,0x9b,0x54,
+ 0x9d,0x5a,0xa0,0x4f,0xa3,0x53,0xa1,0x46,0xa3,0x58,0xa2,0x4d,0xa1,0x5a,0x9d,0x51,
+ 0x9b,0x5b,0x94,0x55,0x9c,0x4b,0x9c,0x46,0x9e,0x4f,0xa2,0x4d,0xa5,0x5c,0xa7,0x4f,
+ 0xa9,0x54,0xaa,0x4c,0xa8,0x51,0xa5,0x48,0xa4,0x52,0xa1,0x47,0x9f,0x4f,0x9d,0x4d,
+ 0x9c,0x52,0x9d,0x46,0x9f,0x53,0x9f,0x45,0xa4,0x51,0xa5,0x47,0xa5,0x51,0xa8,0x48,
+ 0xa9,0x4f,0xa5,0x47,0xa4,0x4f,0xa1,0x49,0x9e,0x55,0x9b,0x4c,0x9a,0x50,0x9c,0x47,
+ 0x9d,0x4f,0xa1,0x47,0xa3,0x51,0xa5,0x49,0xa7,0x50,0xa9,0x47,0xa8,0x52,0xa6,0x47,
+ 0xa4,0x53,0xa2,0x47,0x9f,0x4f,0x9b,0x48,0x9b,0x51,0x9b,0x4d,0x9e,0x4e,0x9f,0x49,
+ 0xa2,0x4e,0xa4,0x48,0xa6,0x52,0xa7,0x4b,0xa7,0x50,0xa8,0x48,0xa5,0x50,0xa3,0x4c,
+ 0x9f,0x52,0x9c,0x4b,0x99,0x51,0x9a,0x4a,0x9a,0x52,0x9d,0x48,0xa0,0x50,0xa4,0x49,
+ 0xa6,0x4f,0xa7,0x48,0xa8,0x4b,0xa5,0x4d,0xa5,0x52,0xa1,0x4b,0x9e,0x51,0x9c,0x4d,
+ 0x9b,0x4f,0x99,0x4a,0x9c,0x50,0x9c,0x4c,0xa0,0x52,0xa5,0x48,0xa5,0x51,0xa6,0x49,
+ 0xa9,0x4f,0xa7,0x49,0xa6,0x4f,0xa1,0x49,0x9f,0x56,0x9d,0x45,0x9b,0x4f,0x9a,0x4c,
+ 0x9b,0x51,0x9d,0x4b,0x9f,0x53,0xa3,0x46,0xa4,0x54,0xa6,0x4b,0xa8,0x4e,0xa5,0x49,
+ 0xa3,0x50,0xa1,0x49,0xa0,0x4d,0x9b,0x4b,0x98,0x52,0x98,0x4b,0x9a,0x4d,0x9b,0x4a,
+ 0x9d,0x50,0xa1,0x48,0xa4,0x50,0xa2,0x49,0xa4,0x53,0xa6,0x4a,0xa3,0x50,0x9f,0x4a,
+ 0x9f,0x50,0x9b,0x48,0x9a,0x51,0x99,0x48,0x99,0x54,0x9c,0x4a,0x9e,0x4e,0xa0,0x48,
+ 0xa3,0x4f,0xa5,0x49,0xa5,0x4f,0xa4,0x47,0xa4,0x54,0xa2,0x4a,0x9f,0x52,0x9c,0x44,
+ 0x99,0x52,0x99,0x4a,0x98,0x50,0x9a,0x49,0x9b,0x51,0x9f,0x49,0xa3,0x52,0xa3,0x4a,
+ 0xa5,0x50,0xa5,0x4a,0xa3,0x52,0xa2,0x4b,0x9e,0x52,0x9c,0x48,0x9a,0x52,0x99,0x4c,
+ 0x98,0x53,0x99,0x48,0x9d,0x4e,0x9e,0x48,0xa1,0x52,0xa3,0x46,0xa5,0x52,0xa5,0x4e,
+ 0xa3,0x52,0xa2,0x4c,0xa0,0x4d,0x9d,0x46,0x9a,0x52,0x99,0x4c,0x99,0x54,0x9b,0x49,
+ 0x9c,0x54,0x9e,0x49,0xa2,0x52,0xa2,0x4b,0xa4,0x53,0xa5,0x4d,0xa3,0x52,0xa2,0x47,
+ 0xa0,0x54,0x9a,0x4b,0x97,0x52,0x97,0x47,0x95,0x51,0x97,0x48,0x98,0x50,0x9a,0x51,
+ 0x9f,0x55,0xa2,0x4a,0xa2,0x51,0xa2,0x47,0xa2,0x4e,0xa2,0x4c,0xa0,0x54,0x9b,0x49,
+ 0x9a,0x4f,0x98,0x49,0x95,0x51,0x97,0x49,0x99,0x54,0x9d,0x4c,0x9f,0x51,0xa2,0x4b,
+ 0xa2,0x53,0xa3,0x49,0xa3,0x4d,0xa1,0x47,0x9e,0x50,0x9c,0x4e,0x99,0x54,0x97,0x4a,
+ 0x96,0x50,0x96,0x48,0x99,0x50,0x9b,0x48,0x9e,0x55,0xa1,0x4c,0xa3,0x55,0xa3,0x4c,
+ 0xa3,0x52,0xa1,0x4b,0xa0,0x4f,0x9b,0x48,0x9a,0x54,0x97,0x49,0x94,0x50,0x96,0x44,
+ 0x99,0x50,0x9a,0x48,0x9c,0x51,0x9f,0x46,0xa1,0x50,0xa3,0x46,0xa2,0x55,0xa1,0x4b,
+ 0x9e,0x50,0x9c,0x49,0x99,0x50,0x95,0x49,0x94,0x53,0x95,0x48,0x97,0x4d,0x9a,0x4b,
+ 0x9d,0x51,0x9f,0x47,0x9f,0x51,0xa0,0x46,0xa2,0x4e,0xa0,0x46,0x9c,0x53,0x9b,0x4a,
+ 0x96,0x56,0x95,0x4d,0x93,0x54,0x94,0x48,0x95,0x56,0x97,0x4d,0x9b,0x54,0x9e,0x47,
+ 0xa0,0x53,0xa0,0x4b,0xa0,0x52,0xa0,0x45,0x9d,0x54,0x9b,0x4c,0x97,0x50,0x96,0x4a,
+ 0x94,0x52,0x95,0x4e,0x96,0x52,0x98,0x4a,0x9b,0x4f,0x9b,0x52,0x9e,0x50,0x9f,0x49,
+ 0xa0,0x55,0x9f,0x49,0x9e,0x4e,0x9a,0x49,0x98,0x56,0x94,0x4c,0x95,0x53,0x93,0x4a,
+ 0x93,0x51,0x97,0x4c,0x98,0x50,0x9c,0x4a,0xa0,0x56,0xa0,0x4c,0x9d,0x56,0x9f,0x4e,
+ 0xa0,0x52,0x9c,0x4b,0x98,0x53,0x96,0x48,0x94,0x54,0x92,0x49,0x93,0x53,0x98,0x4a,
+ 0x99,0x51,0x9b,0x48,0x9e,0x51,0x9f,0x49,0xa1,0x57,0xa0,0x47,0x9e,0x51,0x9d,0x4b,
+ 0x99,0x50,0x98,0x4a,0x94,0x53,0x93,0x4b,0x93,0x52,0x96,0x4b,0x99,0x55,0x9c,0x48,
+ 0x9d,0x53,0xa2,0x4c,0xa1,0x53,0x9f,0x4d,0x9e,0x56,0x9f,0x4c,0x9b,0x4f,0x96,0x46,
+ 0x95,0x57,0x94,0x47,0x94,0x52,0x97,0x4b,0x9b,0x5b,0x9c,0x4b,0x9e,0x51,0xa0,0x4d,
+ 0xa1,0x52,0xa2,0x4d,0xa0,0x50,0x9e,0x49,0x9b,0x52,0x99,0x4b,0x97,0x50,0x95,0x4c,
+ 0x95,0x53,0x98,0x4a,0x9a,0x52,0x9c,0x49,0x9f,0x4c,0xa0,0x50,0xa2,0x53,0xa1,0x4b,
+ 0xa0,0x50,0x9f,0x4b,0x9c,0x52,0x97,0x4b,0x94,0x51,0x93,0x49,0x96,0x54,0x96,0x4b,
+ 0x99,0x51,0x9c,0x4a,0xa0,0x52,0xa3,0x49,0xa3,0x4e,0xa1,0x49,0xa0,0x52,0xa0,0x48,
+ 0x9e,0x54,0x9b,0x4c,0x99,0x54,0x96,0x4a,0x96,0x53,0x98,0x4b,0x99,0x55,0x9d,0x4c,
+ 0xa0,0x51,0xa2,0x49,0xa3,0x51,0xa2,0x4b,0xa3,0x54,0xa2,0x4d,0x9f,0x53,0x9d,0x4a,
+ 0x9a,0x55,0x9a,0x4c,0x99,0x4d,0x9a,0x4a,0x9c,0x56,0x9e,0x46,0xa1,0x53,0xa4,0x48,
+ 0xa5,0x55,0xa6,0x4c,0xa4,0x50,0xa4,0x48,0xa0,0x50,0x9e,0x48,0x9c,0x54,0x99,0x48,
+ 0x98,0x56,0x99,0x4b,0x9b,0x4d,0x9e,0x46,0xa0,0x53,0xa2,0x4b,0xa5,0x4e,0xa5,0x49,
+ 0xa4,0x55,0xa3,0x50,0xa0,0x50,0x9d,0x4a,0x9c,0x51,0x9a,0x4b,0x97,0x55,0x99,0x4b,
+ 0x9b,0x54,0x9f,0x4c,0xa1,0x54,0xa4,0x45,0xa5,0x56,0xa6,0x4a,0xa6,0x50,0xa5,0x4d,
+ 0xa2,0x54,0xa0,0x4a,0x9d,0x4f,0x9a,0x4c,0x9a,0x51,0x99,0x4a,0x9b,0x50,0x9e,0x48,
+ 0xa1,0x55,0xa4,0x4e,0xa5,0x50,0xa5,0x49,0xa7,0x59,0xa3,0x48,0xa3,0x58,0xa0,0x48,
+ 0x9d,0x57,0x99,0x49,0x9a,0x4f,0x9b,0x4b,0x9c,0x55,0x9d,0x4d,0x9e,0x54,0xa1,0x4b,
+ 0xa6,0x54,0xa4,0x4c,0xa2,0x51,0xa2,0x4e,0xa4,0x4e,0x9e,0x4b,0x9b,0x51,0x9a,0x48,
+ 0x9a,0x54,0x9b,0x4c,0x9b,0x50,0x9c,0x4a,0xa0,0x55,0xa3,0x4b,0xa6,0x53,0xa6,0x4b,
+ 0xa6,0x56,0xa5,0x4b,0xa4,0x54,0xa2,0x4c,0x9e,0x52,0x9c,0x4d,0x9b,0x52,0x99,0x49,
+ 0x9b,0x56,0x9e,0x4e,0x9f,0x4f,0xa2,0x4b,0xa6,0x54,0xa7,0x47,0xa6,0x48,0xa6,0x43,
+ 0xa5,0x4e,0xa2,0x49,0xa1,0x4e,0x9b,0x48,0x98,0x52,0x95,0x4f,0xa1,0x35,0xa4,0x30,
+ 0xa9,0x3b,0xab,0x3b,0xad,0x46,0xac,0x3b,0xad,0x44,0xad,0x3b,0xa9,0x3a,0xa6,0x35,
+ 0xa5,0x3e,0xa1,0x35,0x9e,0x39,0x9f,0x33,0xa3,0x3c,0xa5,0x33,0xa9,0x3d,0xaa,0x35,
+ 0xad,0x42,0xae,0x33,0xae,0x3b,0xac,0x33,0xab,0x3d,0xa8,0x37,0xa5,0x3a,0xa2,0x38,
+ 0xa0,0x3b,0xa1,0x36,0xa4,0x38,0xa5,0x35,0xa9,0x3d,0xad,0x33,0xae,0x3a,0xaf,0x32,
+ 0xb1,0x3c,0xb0,0x35,0xae,0x36,0xaa,0x33,0xa8,0x3b,0xa5,0x3a,0xa4,0x3b,0xa4,0x36,
+ 0xa5,0x3d,0xa6,0x36,0xaa,0x39,0xad,0x32,0xae,0x40,0xb0,0x34,0xb0,0x3b,0xaf,0x35,
+ 0xaf,0x3d,0xab,0x35,0xa8,0x3d,0xa4,0x32,0xa4,0x3c,0xa5,0x36,0xa4,0x3c,0xa5,0x32,
+ 0xa9,0x37,0xab,0x32,0xaf,0x3b,0xae,0x33,0xb0,0x3c,0xb0,0x32,0xad,0x3a,0xaa,0x33,
+ 0xa9,0x3d,0xa7,0x35,0xa4,0x3b,0xa2,0x34,0xa6,0x42,0xa7,0x32,0xaa,0x3f,0xae,0x32,
+ 0xb0,0x3c,0xb0,0x36,0xb3,0x3d,0xb1,0x32,0xb1,0x3d,0xad,0x31,0xa9,0x40,0xa7,0x36,
+ 0xa6,0x3d,0xa3,0x33,0xa4,0x3b,0xa7,0x38,0xa9,0x3e,0xad,0x37,0xb0,0x39,0xaf,0x38,
+ 0xb2,0x3d,0xb0,0x38,0xaf,0x3b,0xad,0x35,0xa9,0x3e,0xa5,0x33,0xa3,0x3c,0xa2,0x39,
+ 0xa3,0x3e,0xa4,0x31,0xa7,0x3d,0xaa,0x33,0xac,0x3a,0xaf,0x38,0xb0,0x34,0xaf,0x39,
+ 0xac,0x40,0xac,0x33,0xa9,0x3b,0xa4,0x35,0xa2,0x3a,0xa0,0x36,0xa2,0x3d,0xa3,0x33,
+ 0xa6,0x3e,0xaa,0x34,0xac,0x3c,0xae,0x36,0xae,0x3f,0xaf,0x2f,0xaf,0x3d,0xac,0x34,
+ 0xa9,0x3f,0xa6,0x35,0xa4,0x39,0xa1,0x35,0xa1,0x40,0xa0,0x35,0xa4,0x3d,0xa9,0x33,
+ 0xaa,0x3a,0xab,0x35,0xae,0x3b,0xaf,0x33,0xac,0x3e,0xaa,0x34,0xaa,0x3a,0xa7,0x39,
+ 0xa3,0x40,0xa2,0x35,0xa2,0x39,0xa1,0x33,0xa7,0x3f,0xa7,0x33,0xab,0x3e,0xae,0x38,
+ 0xaf,0x42,0xaf,0x35,0xb0,0x3d,0xac,0x33,0xaa,0x3e,0xa8,0x35,0xa6,0x39,0xa3,0x38,
+ 0xa2,0x39,0xa4,0x37,0xa7,0x3c,0xa9,0x34,0xac,0x3c,0xae,0x37,0xb0,0x3d,0xb0,0x34,
+ 0xb0,0x3e,0xae,0x33,0xab,0x3e,0xa7,0x32,0xa5,0x41,0xa3,0x32,0xa3,0x3d,0xa4,0x38,
+ 0xa6,0x3b,0xa6,0x34,0xac,0x39,0xae,0x38,0xb0,0x41,0xaf,0x33,0xb0,0x3b,0xad,0x31,
+ 0xaa,0x3e,0xa7,0x34,0xa6,0x3a,0xa2,0x33,0xa2,0x3f,0xa2,0x34,0xa5,0x3e,0xa8,0x35,
+ 0xab,0x3e,0xae,0x37,0xb0,0x3a,0xaf,0x36,0xb0,0x3d,0xb0,0x32,0xae,0x3a,0xa9,0x34,
+ 0xa9,0x3d,0xa4,0x35,0xa3,0x3e,0xa4,0x37,0xa6,0x3f,0xa7,0x33,0xad,0x3b,0xad,0x32,
+ 0xb0,0x3c,0xb0,0x34,0xb1,0x3f,0xae,0x38,0xab,0x3b,0xa8,0x35,0xa5,0x39,0xa1,0x34,
+ 0xa0,0x3b,0xa1,0x31,0xa3,0x3b,0xa5,0x34,0xa8,0x40,0xad,0x37,0xad,0x3e,0xaf,0x37,
+ 0xb1,0x3c,0xae,0x33,0xab,0x3b,0xa9,0x38,0xa6,0x3d,0xa3,0x32,0xa2,0x3d,0xa1,0x32,
+ 0xa4,0x3b,0xa6,0x33,0xa9,0x3c,0xac,0x36,0xae,0x40,0xaf,0x34,0xb0,0x3e,0xae,0x32,
+ 0xae,0x3c,0xab,0x35,0xa9,0x3b,0xa5,0x36,0xa3,0x3b,0xa2,0x35,0xa3,0x42,0xa7,0x37,
+ 0xa7,0x42,0xac,0x37,0xae,0x37,0xae,0x35,0xb1,0x3c,0xae,0x37,0xae,0x3d,0xab,0x39,
+ 0xa9,0x3d,0xa4,0x3c,0xa3,0x3a,0xa2,0x35,0xa3,0x41,0xa5,0x38,0xa7,0x41,0xaa,0x34,
+ 0xaf,0x3e,0xae,0x38,0xb0,0x3d,0xaf,0x34,0xae,0x46,0xaa,0x38,0xa8,0x39,0xa5,0x36,
+ 0xa4,0x3e,0xa2,0x34,0xa3,0x3e,0xa4,0x36,0xa6,0x40,0xaa,0x38,0xad,0x3e,0xad,0x3a,
+ 0xaf,0x3b,0xaf,0x37,0xad,0x3d,0xaa,0x36,0xa6,0x42,0xa4,0x38,0xa2,0x40,0xa1,0x36,
+ 0xa2,0x43,0xa3,0x30,0xa7,0x3e,0xaa,0x36,0xad,0x44,0xac,0x3a,0xad,0x3e,0xad,0x38,
+ 0xac,0x40,0xa8,0x36,0xa5,0x3d,0xa5,0x36,0xa0,0x45,0x9e,0x37,0xa0,0x3f,0xa4,0x33,
+ 0xa5,0x3f,0xa7,0x35,0xaa,0x3d,0xac,0x35,0xae,0x40,0xae,0x3a,0xab,0x3f,0xa9,0x35,
+ 0xa8,0x3f,0xa2,0x34,0xa2,0x37,0xa0,0x38,0x9f,0x40,0xa0,0x33,0xa4,0x3f,0xa6,0x36,
+ 0xab,0x3e,0xac,0x38,0xad,0x3d,0xae,0x39,0xaf,0x3f,0xaa,0x3a,0xa7,0x3a,0xa6,0x35,
+ 0xa4,0x41,0xa1,0x3b,0xa1,0x3c,0xa1,0x33,0xa2,0x40,0xa4,0x37,0xa8,0x3b,0xaa,0x36,
+ 0xad,0x3e,0xac,0x38,0xab,0x3b,0xa9,0x37,0xa9,0x43,0xa5,0x38,0xa2,0x3e,0xa0,0x38,
+ 0xa2,0x3f,0xa2,0x35,0xa4,0x3c,0xa6,0x37,0xaa,0x44,0xac,0x37,0xad,0x41,0xad,0x33,
+ 0xb0,0x3e,0xac,0x35,0xaa,0x3c,0xa7,0x38,0xa4,0x3e,0xa2,0x35,0xa1,0x3c,0xa1,0x34,
+ 0xa5,0x3f,0xa5,0x33,0xa8,0x3d,0xa9,0x35,0xac,0x46,0xad,0x38,0xab,0x3f,0xab,0x32,
+ 0xab,0x3c,0xa6,0x39,0xa5,0x3b,0xa3,0x37,0xa1,0x41,0xa1,0x38,0xa2,0x41,0xa6,0x36,
+ 0xa8,0x3e,0xac,0x37,0xae,0x40,0xb0,0x37,0xae,0x45,0xad,0x39,0xac,0x3e,0xa8,0x38,
+ 0xa7,0x3e,0xa3,0x36,0xa1,0x3c,0xa1,0x32,0xa3,0x42,0xa4,0x37,0xa9,0x3a,0xaa,0x37,
+ 0xac,0x42,0xad,0x32,0xad,0x42,0xab,0x31,0xab,0x41,0xa7,0x35,0xa5,0x40,0xa4,0x35,
+ 0xa1,0x3e,0xa2,0x33,0xa3,0x3f,0xa5,0x39,0xa9,0x3e,0xab,0x38,0xae,0x3f,0xaf,0x36,
+ 0xaf,0x3c,0xad,0x34,0xac,0x40,0xa9,0x38,0xa6,0x3e,0xa3,0x37,0xa2,0x3e,0xa1,0x34,
+ 0xa3,0x40,0xa6,0x34,0xa8,0x3c,0xac,0x35,0xae,0x3f,0xad,0x37,0xaf,0x3e,0xae,0x38,
+ 0xae,0x3f,0xa8,0x3d,0xa7,0x3d,0xa4,0x35,0xa1,0x3c,0xa1,0x36,0xa0,0x42,0xa2,0x35,
+ 0xa7,0x3e,0xaa,0x36,0xab,0x3a,0xab,0x34,0xac,0x44,0xad,0x37,0xab,0x38,0xa9,0x36,
+ 0xa8,0x40,0xa2,0x36,0xa2,0x40,0xa2,0x33,0x9f,0x40,0xa1,0x35,0xa8,0x41,0xaa,0x35,
+ 0xa9,0x3c,0xad,0x3a,0xaf,0x3d,0xad,0x3a,0xac,0x3f,0xaa,0x37,0xa8,0x3f,0xa6,0x36,
+ 0xa2,0x40,0xa2,0x37,0xa4,0x3e,0xa5,0x34,0xa8,0x42,0xaa,0x33,0xae,0x3d,0xad,0x3b,
+ 0xaf,0x45,0xaf,0x36,0xad,0x32,0xab,0x2a,0xa9,0x39,0xa6,0x34,0xa4,0x3d,0xa0,0x37,
+ 0x9f,0x41,0x9d,0x3b,0xad,0x4e,0xaf,0x3d,0xb3,0x4f,0xb3,0x4e,0xb3,0x57,0xb3,0x4e,
+ 0xb1,0x51,0xae,0x4a,0xac,0x4e,0xaa,0x4a,0xa7,0x4f,0xa6,0x44,0xa9,0x4b,0xab,0x4a,
+ 0xae,0x4d,0xb1,0x44,0xb3,0x53,0xb4,0x43,0xb5,0x4f,0xb4,0x46,0xb3,0x4a,0xb0,0x49,
+ 0xad,0x51,0xaa,0x46,0xa8,0x4b,0xa8,0x45,0xa9,0x52,0xab,0x48,0xaf,0x4b,0xb1,0x49,
+ 0xb4,0x4a,0xb6,0x45,0xb7,0x4e,0xb5,0x49,0xb3,0x51,0xb2,0x47,0xaf,0x4c,0xac,0x44,
+ 0xab,0x52,0xa9,0x43,0xab,0x51,0xab,0x41,0xac,0x54,0xb2,0x45,0xb2,0x4c,0xb4,0x48,
+ 0xb3,0x4f,0xb4,0x46,0xb2,0x4c,0xb0,0x41,0xaf,0x4e,0xab,0x45,0xa9,0x50,0xa7,0x48,
+ 0xaa,0x4d,0xa8,0x44,0xab,0x4c,0xaf,0x46,0xb2,0x54,0xb4,0x43,0xb7,0x4c,0xb6,0x46,
+ 0xb3,0x4c,0xb0,0x48,0xb1,0x4c,0xaa,0x47,0xa6,0x53,0xa6,0x46,0xaa,0x47,0xaa,0x44,
+ 0xad,0x4a,0xb2,0x48,0xb5,0x4f,0xb7,0x4a,0xb7,0x4f,0xb7,0x45,0xb6,0x4a,0xb3,0x45,
+ 0xb0,0x51,0xae,0x46,0xae,0x4b,0xa9,0x44,0xa9,0x50,0xab,0x45,0xad,0x4e,0xaf,0x44,
+ 0xb0,0x50,0xb4,0x47,0xb7,0x48,0xb4,0x45,0xb4,0x51,0xb5,0x46,0xb1,0x4b,0xae,0x45,
+ 0xae,0x4d,0xab,0x40,0xac,0x4c,0xac,0x44,0xb0,0x51,0xb3,0x47,0xb6,0x4f,0xb7,0x42,
+ 0xbb,0x4c,0xbb,0x46,0xba,0x4b,0xb8,0x47,0xb3,0x4d,0xb0,0x48,0xb1,0x4e,0xac,0x49,
+ 0xa9,0x4e,0xaa,0x46,0xaf,0x4e,0xb0,0x47,0xb2,0x50,0xb7,0x48,0xb9,0x4c,0xb9,0x46,
+ 0xbb,0x4d,0xb8,0x44,0xb6,0x48,0xb3,0x41,0xb0,0x4d,0xaf,0x43,0xb1,0x4d,0xaf,0x48,
+ 0xb2,0x4f,0xb4,0x43,0xb6,0x4d,0xba,0x46,0xba,0x4f,0xb9,0x44,0xbb,0x4b,0xba,0x46,
+ 0xb8,0x4d,0xb4,0x46,0xb3,0x4c,0xb1,0x43,0xae,0x52,0xad,0x48,0xb1,0x50,0xb3,0x4a,
+ 0xb6,0x4c,0xb6,0x46,0xb9,0x4c,0xb9,0x47,0xb9,0x4f,0xb7,0x46,0xb7,0x4e,0xb3,0x46,
+ 0xb2,0x4b,0xb0,0x45,0xad,0x50,0xad,0x42,0xb0,0x55,0xb2,0x48,0xb7,0x4e,0xb8,0x44,
+ 0xba,0x52,0xbb,0x4b,0xbd,0x4e,0xbd,0x49,0xb8,0x52,0xb6,0x48,0xb3,0x4f,0xaf,0x47,
+ 0xb0,0x4e,0xad,0x47,0xaf,0x4a,0xb1,0x44,0xb6,0x50,0xb8,0x45,0xba,0x4c,0xb9,0x47,
+ 0xbc,0x4c,0xb8,0x45,0xb5,0x4d,0xb1,0x48,0xaf,0x50,0xac,0x45,0xab,0x4b,0xab,0x4a,
+ 0xaf,0x4f,0xb2,0x44,0xb5,0x49,0xb7,0x47,0xbc,0x4e,0xbc,0x46,0xbe,0x4c,0xbb,0x45,
+ 0xb6,0x51,0xb6,0x4a,0xb3,0x4c,0xae,0x46,0xae,0x52,0xae,0x45,0xb0,0x4e,0xb1,0x46,
+ 0xb4,0x4f,0xb6,0x43,0xbc,0x4b,0xba,0x45,0xbb,0x50,0xbb,0x45,0xbc,0x4d,0xb6,0x44,
+ 0xb3,0x52,0xb2,0x45,0xb1,0x4a,0xac,0x46,0xae,0x52,0xb3,0x46,0xb5,0x4d,0xb6,0x43,
+ 0xbb,0x4f,0xbc,0x4a,0xbc,0x4d,0xbc,0x41,0xba,0x51,0xb7,0x46,0xb5,0x4c,0xb2,0x45,
+ 0xad,0x51,0xae,0x44,0xae,0x4f,0xaf,0x46,0xb1,0x4d,0xb3,0x45,0xb9,0x4d,0xba,0x49,
+ 0xbb,0x50,0xbc,0x42,0xbb,0x4c,0xb8,0x44,0xb4,0x4e,0xb1,0x47,0xae,0x4d,0xab,0x47,
+ 0xad,0x4d,0xad,0x41,0xb0,0x4c,0xb3,0x48,0xb8,0x52,0xb8,0x49,0xb9,0x4d,0xbb,0x4c,
+ 0xba,0x4f,0xb4,0x45,0xb4,0x4f,0xb2,0x46,0xad,0x50,0xab,0x45,0xac,0x4d,0xb0,0x4a,
+ 0xb2,0x4e,0xb5,0x4a,0xb8,0x50,0xba,0x4a,0xbb,0x4e,0xbb,0x4a,0xba,0x4a,0xb7,0x4a,
+ 0xb6,0x4b,0xb2,0x46,0xaf,0x4e,0xac,0x46,0xad,0x54,0xad,0x46,0xb0,0x4e,0xb2,0x41,
+ 0xb6,0x4e,0xb7,0x48,0xb9,0x53,0xb9,0x43,0xb9,0x56,0xb8,0x4a,0xb5,0x52,0xb2,0x45,
+ 0xb1,0x4f,0xb0,0x4a,0xb0,0x4e,0xb0,0x46,0xb1,0x4e,0xb5,0x43,0xb7,0x4f,0xb6,0x45,
+ 0xba,0x4f,0xb8,0x47,0xb7,0x4e,0xb4,0x45,0xb3,0x50,0xb1,0x4a,0xae,0x4c,0xad,0x46,
+ 0xaa,0x50,0xab,0x45,0xaf,0x52,0xb2,0x48,0xb2,0x51,0xb3,0x43,0xb8,0x51,0xb8,0x47,
+ 0xb7,0x4e,0xb6,0x46,0xb3,0x4e,0xb0,0x46,0xae,0x54,0xad,0x46,0xab,0x4c,0xaa,0x45,
+ 0xaf,0x50,0xb0,0x48,0xb3,0x4d,0xb4,0x46,0xb6,0x4e,0xb6,0x47,0xb5,0x4b,0xb2,0x45,
+ 0xb1,0x4d,0xae,0x48,0xaf,0x4b,0xac,0x48,0xac,0x53,0xad,0x48,0xae,0x4d,0xb1,0x44,
+ 0xb4,0x50,0xb5,0x46,0xb8,0x4b,0xb9,0x45,0xb8,0x55,0xb8,0x44,0xb6,0x4c,0xb2,0x46,
+ 0xb1,0x4c,0xac,0x4a,0xaa,0x4b,0xac,0x47,0xac,0x50,0xad,0x43,0xb2,0x4c,0xb4,0x47,
+ 0xb5,0x4b,0xb6,0x49,0xb6,0x4d,0xb5,0x42,0xb4,0x55,0xb3,0x47,0xb0,0x4f,0xad,0x46,
+ 0xab,0x51,0xac,0x46,0xaf,0x4b,0xaf,0x47,0xb2,0x55,0xb5,0x45,0xb8,0x4c,0xb7,0x43,
+ 0xb7,0x50,0xb7,0x44,0xb5,0x50,0xb1,0x47,0xae,0x55,0xac,0x45,0xab,0x4c,0xa9,0x4b,
+ 0xac,0x4c,0xae,0x47,0xb1,0x50,0xb5,0x45,0xb7,0x52,0xb9,0x46,0xb9,0x4f,0xb7,0x49,
+ 0xb8,0x4b,0xb6,0x47,0xb1,0x4c,0xad,0x46,0xae,0x4d,0xad,0x44,0xac,0x50,0xae,0x46,
+ 0xb1,0x4b,0xb4,0x4a,0xb6,0x4b,0xb7,0x47,0xb7,0x54,0xb7,0x45,0xb6,0x4e,0xb4,0x45,
+ 0xb2,0x53,0xae,0x47,0xad,0x49,0xab,0x47,0xa6,0x54,0xa8,0x46,0xb0,0x4d,0xb1,0x46,
+ 0xb1,0x51,0xb4,0x48,0xba,0x4c,0xb8,0x44,0xb8,0x4d,0xb6,0x46,0xb3,0x4d,0xb0,0x46,
+ 0xb2,0x4d,0xac,0x48,0xa4,0x4b,0xa7,0x44,0xb3,0x51,0xab,0x44,0xa8,0x52,0xb7,0x45,
+ 0xbb,0x4e,0xb5,0x46,0xb7,0x4c,0xb5,0x44,0xb3,0x55,0xb0,0x42,0xae,0x4f,0xae,0x45,
+ 0xae,0x4d,0xaf,0x49,0xb1,0x4e,0xb2,0x47,0xb4,0x50,0xb8,0x48,0xba,0x4e,0xb6,0x45,
+ 0xb7,0x4b,0xb6,0x47,0xb1,0x4a,0xae,0x45,0xad,0x4e,0xad,0x47,0xac,0x4e,0xac,0x48,
+ 0xaf,0x4a,0xb0,0x44,0xb2,0x4b,0xb5,0x4a,0xb7,0x50,0xb6,0x44,0xb6,0x4e,0xb3,0x46,
+ 0xb1,0x50,0xb0,0x47,0xac,0x4c,0xaa,0x43,0xac,0x53,0xac,0x44,0xaf,0x4a,0xb0,0x43,
+ 0xb4,0x4b,0xb6,0x47,0xb6,0x50,0xb5,0x49,0xb5,0x51,0xb3,0x40,0xaf,0x45,0xad,0x3e,
+ 0xac,0x4f,0xa8,0x46,0xa8,0x4e,0xa9,0x48,0xab,0x4f,0xa7,0x4b,0xba,0x1e,0xbb,0x10,
+ 0xbc,0x1d,0xbc,0x16,0xbb,0x2b,0xb7,0x18,0xb4,0x21,0xb1,0x15,0xaf,0x1c,0xac,0x16,
+ 0xae,0x1d,0xae,0x13,0xae,0x1a,0xb2,0x15,0xb6,0x20,0xb6,0x12,0xb6,0x20,0xb8,0x15,
+ 0xb8,0x1d,0xb5,0x17,0xb2,0x19,0xb0,0x13,0xad,0x20,0xaa,0x14,0xab,0x20,0xac,0x17,
+ 0xb1,0x1c,0xb3,0x11,0xb6,0x1c,0xb6,0x16,0xb9,0x20,0xba,0x15,0xb8,0x1a,0xb6,0x16,
+ 0xb3,0x1f,0xad,0x11,0xab,0x1b,0xa8,0x12,0xa9,0x1e,0xa9,0x18,0xac,0x19,0xaf,0x15,
+ 0xb4,0x1b,0xb6,0x13,0xb6,0x1f,0xb6,0x16,0xb6,0x20,0xb3,0x12,0xb0,0x19,0xac,0x14,
+ 0xac,0x1c,0xa8,0x13,0xa9,0x19,0xab,0x14,0xad,0x1f,0xac,0x13,0xb2,0x1c,0xb3,0x13,
+ 0xb3,0x19,0xb3,0x13,0xb3,0x1f,0xad,0x16,0xad,0x1f,0xa9,0x18,0xa5,0x1c,0xa2,0x1b,
+ 0xa1,0x1c,0xa3,0x12,0xa9,0x1a,0xa9,0x0f,0xab,0x1e,0xb0,0x16,0xb3,0x1a,0xb5,0x18,
+ 0xb4,0x18,0xb3,0x16,0xb0,0x20,0xac,0x14,0xab,0x1c,0xa7,0x18,0xa8,0x1e,0xa7,0x15,
+ 0xaa,0x1c,0xaa,0x12,0xb0,0x1c,0xb0,0x14,0xb1,0x21,0xb4,0x13,0xb4,0x1c,0xaf,0x16,
+ 0xaf,0x1b,0xac,0x14,0xa9,0x1c,0xa5,0x15,0xa6,0x1c,0xa5,0x14,0xa7,0x1c,0xaa,0x15,
+ 0xad,0x1d,0xaf,0x12,0xb1,0x1d,0xb2,0x15,0xb2,0x1f,0xb0,0x14,0xae,0x18,0xaa,0x16,
+ 0xa8,0x19,0xa5,0x13,0xa3,0x1d,0xa4,0x16,0xa7,0x1e,0xa7,0x14,0xab,0x1c,0xad,0x17,
+ 0xb1,0x1d,0xb2,0x13,0xb1,0x1d,0xb0,0x13,0xb0,0x1c,0xad,0x17,0xab,0x1e,0xa7,0x16,
+ 0xa4,0x1b,0xa5,0x15,0xa5,0x1e,0xa6,0x11,0xab,0x1c,0xae,0x17,0xb0,0x1d,0xb1,0x19,
+ 0xb3,0x1a,0xb1,0x11,0xb0,0x1a,0xaa,0x15,0xa8,0x21,0xa5,0x16,0xa4,0x22,0xa2,0x15,
+ 0xa5,0x1f,0xa6,0x17,0xa9,0x1e,0xab,0x12,0xae,0x21,0xb0,0x15,0xb2,0x1c,0xb1,0x18,
+ 0xaf,0x19,0xae,0x15,0xac,0x1e,0xa8,0x18,0xa5,0x20,0xa5,0x13,0xa6,0x1c,0xa5,0x16,
+ 0xa8,0x1c,0xac,0x14,0xad,0x1e,0xac,0x14,0xad,0x22,0xaf,0x17,0xac,0x1c,0xab,0x17,
+ 0xaa,0x19,0xa9,0x18,0xa7,0x1e,0xa5,0x12,0xa6,0x21,0xa5,0x15,0xa9,0x1c,0xa8,0x10,
+ 0xab,0x18,0xab,0x18,0xb0,0x1d,0xab,0x12,0xab,0x20,0xaa,0x10,0xaa,0x1e,0xa6,0x13,
+ 0xa4,0x20,0xa4,0x1a,0xa4,0x1d,0xa7,0x14,0xaa,0x1f,0xab,0x15,0xaf,0x1c,0xb1,0x18,
+ 0xb1,0x1d,0xaf,0x13,0xaf,0x17,0xad,0x14,0xaa,0x22,0xa6,0x12,0xa4,0x1c,0xa2,0x11,
+ 0xa3,0x1f,0xa5,0x16,0xa7,0x1d,0xa8,0x15,0xab,0x20,0xae,0x15,0xb0,0x1e,0xaf,0x15,
+ 0xaf,0x1c,0xac,0x16,0xaa,0x1d,0xa7,0x15,0xa5,0x21,0xa4,0x12,0xa3,0x1c,0xa4,0x17,
+ 0xa9,0x1f,0xa8,0x16,0xac,0x1e,0xad,0x16,0xae,0x20,0xae,0x16,0xaf,0x1a,0xac,0x14,
+ 0xaa,0x1c,0xa7,0x15,0xa5,0x1c,0xa3,0x17,0xa3,0x1e,0xa2,0x11,0xa6,0x1e,0xa6,0x18,
+ 0xa8,0x20,0xab,0x14,0xae,0x18,0xac,0x15,0xac,0x23,0xad,0x14,0xaa,0x1e,0xa6,0x12,
+ 0xa4,0x19,0xa2,0x19,0xa3,0x19,0xa4,0x16,0xa4,0x23,0xa8,0x15,0xab,0x20,0xad,0x14,
+ 0xaf,0x1f,0xaf,0x18,0xae,0x1f,0xac,0x12,0xaa,0x21,0xa9,0x19,0xa5,0x21,0xa3,0x15,
+ 0xa4,0x1f,0xa3,0x14,0xa4,0x1e,0xa7,0x1a,0xaa,0x1b,0xa9,0x10,0xab,0x1e,0xac,0x15,
+ 0xad,0x1e,0xa9,0x14,0xa8,0x1e,0xa4,0x13,0xa2,0x22,0xa1,0x17,0x9f,0x1f,0x9f,0x14,
+ 0xa3,0x1e,0xa5,0x17,0xa9,0x1d,0xaa,0x1a,0xac,0x23,0xad,0x18,0xad,0x1b,0xa9,0x16,
+ 0xaa,0x1f,0xa5,0x16,0xa3,0x1a,0xa0,0x14,0xa0,0x25,0x9f,0x14,0xa1,0x1d,0xa2,0x18,
+ 0xa6,0x1e,0xa9,0x16,0xac,0x1c,0xaa,0x14,0xaa,0x21,0xab,0x19,0xa8,0x1c,0xa4,0x11,
+ 0xa3,0x1e,0xa1,0x16,0xa0,0x1d,0x9e,0x19,0x9e,0x20,0xa1,0x14,0xa5,0x1f,0xa6,0x18,
+ 0xa8,0x20,0xab,0x17,0xa9,0x1d,0xaa,0x1b,0xab,0x1f,0xa5,0x13,0xa3,0x1d,0xa1,0x15,
+ 0xa1,0x1c,0x9e,0x15,0x9f,0x1f,0xa1,0x17,0xa5,0x21,0xa7,0x17,0xa9,0x24,0xaa,0x19,
+ 0xac,0x1f,0xa9,0x19,0xa8,0x1e,0xa4,0x14,0xa2,0x1e,0xa0,0x19,0x9f,0x1e,0x9d,0x16,
+ 0x9f,0x1e,0x9f,0x15,0xa2,0x1a,0xa3,0x17,0xa5,0x1f,0xa5,0x19,0xa8,0x1e,0xa7,0x15,
+ 0xa4,0x20,0xa5,0x1a,0xa4,0x1e,0xa0,0x1a,0xa0,0x1f,0x9e,0x15,0xa0,0x1e,0xa3,0x17,
+ 0xa4,0x1e,0xa6,0x19,0xa8,0x1e,0xa7,0x15,0xa9,0x1d,0xa9,0x17,0xa8,0x1f,0xa6,0x19,
+ 0xa3,0x20,0xa2,0x19,0x9e,0x1e,0x9c,0x14,0x9c,0x22,0x9d,0x15,0xa0,0x1e,0xa3,0x15,
+ 0xa6,0x1e,0xa6,0x1b,0xa7,0x1e,0xa7,0x12,0xa6,0x21,0xa4,0x18,0xa2,0x1f,0x9e,0x15,
+ 0x9d,0x20,0x9b,0x1a,0x9c,0x1f,0x9c,0x12,0x9f,0x20,0xa2,0x18,0xa5,0x21,0xa7,0x17,
+ 0xa8,0x1a,0xa8,0x1a,0xa7,0x22,0xa5,0x18,0xa2,0x23,0x9f,0x16,0x9f,0x1d,0x9d,0x18,
+ 0x9d,0x1d,0x9e,0x15,0xa0,0x1d,0xa1,0x18,0xa5,0x1e,0xa5,0x17,0xa7,0x1d,0xa7,0x19,
+ 0xa6,0x1c,0xa3,0x19,0xa0,0x21,0x9e,0x18,0x9d,0x1d,0x9a,0x0f,0x9c,0x1f,0x9b,0x14,
+ 0x9e,0x21,0xa0,0x1b,0xa3,0x1d,0xa4,0x15,0xa6,0x22,0xa7,0x17,0xa6,0x1d,0xa3,0x1b,
+ 0xa1,0x23,0x9e,0x16,0x9c,0x1d,0x9b,0x1a,0x9a,0x21,0x9b,0x18,0x9f,0x1d,0x9e,0x14,
+ 0xa0,0x20,0xa5,0x14,0xa5,0x1c,0xa4,0x17,0xa5,0x1f,0xa5,0x18,0xa2,0x19,0x9f,0x18,
+ 0x9e,0x23,0x9c,0x16,0x9b,0x1a,0x9b,0x19,0x9b,0x20,0x9c,0x1d,0xa1,0x1f,0xa0,0x17,
+ 0xa4,0x25,0xa5,0x17,0xa3,0x1b,0xa1,0x17,0xa2,0x1e,0x9e,0x17,0x9c,0x1d,0x99,0x1a,
+ 0x99,0x21,0x9a,0x18,0x9a,0x1b,0x9d,0x17,0xa1,0x1e,0xa3,0x15,0xa5,0x1d,0xa6,0x18,
+ 0xa5,0x22,0xa2,0x15,0xa2,0x21,0x9f,0x18,0x9d,0x1b,0x9c,0x17,0x9b,0x1d,0x9b,0x14,
+ 0x9c,0x1c,0x9e,0x15,0xa0,0x1b,0xa2,0x14,0xa5,0x1f,0xa4,0x13,0xa4,0x25,0xa3,0x19,
+ 0xa1,0x25,0x9e,0x15,0x9e,0x14,0x9a,0x12,0x9b,0x1e,0x99,0x18,0x9c,0x1d,0x9c,0x18,
+ 0x9f,0x1d,0x9d,0x1d,0xa9,0x5e,0xaa,0x5f,0xaa,0x6b,0xa7,0x6a,0xa4,0x78,0xa1,0x6d,
+ 0xa1,0x73,0x9d,0x66,0x9e,0x6b,0x9e,0x61,0xa0,0x6c,0xa2,0x61,0xa6,0x6e,0xa7,0x63,
+ 0xa8,0x70,0xa8,0x65,0xa9,0x69,0xa6,0x5e,0xa4,0x6b,0xa1,0x67,0x9f,0x68,0x9c,0x65,
+ 0x9f,0x6e,0x9e,0x63,0xa0,0x69,0xa0,0x60,0xa6,0x6c,0xa7,0x60,0xa8,0x68,0xa9,0x62,
+ 0xaa,0x68,0xa8,0x63,0xa5,0x67,0xa2,0x61,0xa0,0x6c,0x9e,0x63,0x9d,0x69,0x9b,0x62,
+ 0x9f,0x69,0xa1,0x61,0xa2,0x66,0xa4,0x63,0xaa,0x6a,0xa9,0x63,0xa8,0x68,0xa7,0x64,
+ 0xa6,0x69,0xa3,0x63,0xa0,0x6d,0x9e,0x67,0x9e,0x70,0x9d,0x63,0x9e,0x6c,0xa0,0x60,
+ 0xa4,0x6c,0xa5,0x63,0xa7,0x68,0xa6,0x64,0xa7,0x6c,0xa7,0x5f,0xa5,0x6e,0xa2,0x63,
+ 0x9f,0x6c,0x9b,0x64,0x9a,0x67,0x9b,0x63,0x9c,0x6c,0x9e,0x63,0xa1,0x6a,0xa3,0x63,
+ 0xa7,0x6a,0xa5,0x64,0xa6,0x6b,0xa5,0x62,0xa5,0x71,0xa2,0x5d,0x9f,0x68,0x9e,0x62,
+ 0x9c,0x70,0x9d,0x64,0x9e,0x66,0x9f,0x61,0xa3,0x6a,0xa4,0x60,0xa6,0x6d,0xa7,0x63,
+ 0xa7,0x70,0xa6,0x5f,0xa4,0x6b,0xa0,0x61,0xa0,0x6d,0x9d,0x62,0x9c,0x6c,0x9b,0x61,
+ 0x9e,0x6d,0xa0,0x5f,0xa2,0x6b,0xa2,0x5f,0xa7,0x6b,0xa5,0x62,0xa6,0x6a,0xa5,0x5d,
+ 0xa4,0x6e,0xa2,0x64,0xa0,0x68,0x9c,0x63,0x9b,0x68,0x9b,0x5f,0x9c,0x6c,0x9c,0x63,
+ 0x9e,0x6d,0xa2,0x63,0xa1,0x66,0x99,0x60,0x9d,0x69,0xa2,0x62,0x97,0x66,0x92,0x60,
+ 0x9b,0x6d,0x9a,0x60,0x95,0x6a,0x98,0x62,0x9c,0x6b,0x9c,0x65,0x9f,0x69,0xa1,0x61,
+ 0xa4,0x6a,0xa4,0x64,0xa5,0x69,0xa5,0x5e,0xa4,0x6b,0xa0,0x63,0x9e,0x66,0x9c,0x5f,
+ 0x98,0x66,0x97,0x61,0x99,0x67,0x99,0x61,0x9b,0x6a,0x9d,0x65,0xa0,0x6a,0xa1,0x65,
+ 0xa3,0x6b,0xa2,0x61,0xa1,0x6c,0x9f,0x64,0x9d,0x6d,0x99,0x65,0x97,0x69,0x94,0x63,
+ 0x97,0x6a,0x98,0x64,0x99,0x63,0x9c,0x62,0xa1,0x6d,0xa1,0x61,0xa3,0x68,0xa2,0x63,
+ 0xa2,0x6b,0x9f,0x62,0x9d,0x69,0x9b,0x64,0x98,0x69,0x96,0x64,0x97,0x6b,0x97,0x63,
+ 0x9b,0x6b,0x9d,0x62,0x9f,0x6c,0xa1,0x65,0xa4,0x6b,0xa2,0x61,0xa2,0x6d,0xa0,0x61,
+ 0x9e,0x68,0x9b,0x65,0x9a,0x68,0x98,0x61,0x98,0x6d,0x98,0x60,0x9b,0x6c,0x9e,0x5f,
+ 0xa0,0x70,0xa1,0x60,0xa3,0x68,0xa4,0x5f,0xa3,0x69,0xa0,0x5f,0x9f,0x6d,0x9c,0x62,
+ 0x9a,0x6b,0x98,0x68,0x98,0x68,0x97,0x64,0x9b,0x6d,0x9d,0x62,0x9e,0x6c,0xa0,0x63,
+ 0xa4,0x6a,0xa2,0x63,0xa1,0x6b,0xa0,0x66,0x9f,0x6f,0x9b,0x60,0x9b,0x6c,0x98,0x64,
+ 0x98,0x6d,0x99,0x62,0x9a,0x69,0x9c,0x67,0x9d,0x67,0xa0,0x69,0xa2,0x6a,0xa0,0x61,
+ 0xa1,0x6c,0x9e,0x63,0x9d,0x66,0x99,0x61,0x99,0x6a,0x97,0x63,0x97,0x6a,0x96,0x60,
+ 0x9a,0x6d,0x9a,0x60,0x9e,0x6a,0x9f,0x62,0xa1,0x6b,0xa1,0x61,0xa2,0x6a,0xa0,0x64,
+ 0x9f,0x6b,0x9b,0x62,0x9a,0x6b,0x98,0x63,0x94,0x66,0x8e,0x63,0x94,0x67,0x9c,0x65,
+ 0x97,0x6d,0x98,0x65,0xa1,0x6a,0xa1,0x62,0xa2,0x6c,0xa0,0x64,0x9f,0x6c,0x9c,0x67,
+ 0x99,0x6b,0x97,0x64,0x95,0x66,0x94,0x63,0x98,0x69,0x99,0x61,0x9b,0x6a,0x9e,0x68,
+ 0xa0,0x6e,0xa0,0x65,0xa0,0x69,0x9e,0x61,0x9d,0x6d,0x9a,0x68,0x98,0x6a,0x96,0x62,
+ 0x95,0x6c,0x94,0x65,0x96,0x6c,0x94,0x61,0x9a,0x6d,0x9b,0x69,0x9b,0x69,0x9b,0x61,
+ 0x9e,0x6f,0x9c,0x63,0x9b,0x6b,0x98,0x63,0x96,0x6d,0x91,0x62,0x91,0x6c,0x91,0x62,
+ 0x94,0x6c,0x93,0x64,0x96,0x69,0x9b,0x68,0x9c,0x69,0x9c,0x62,0x9e,0x6d,0x9c,0x64,
+ 0x9d,0x6f,0x99,0x62,0x98,0x6b,0x96,0x65,0x96,0x6e,0x94,0x64,0x97,0x6c,0x97,0x63,
+ 0x9a,0x6b,0x9c,0x60,0x9f,0x6f,0x9f,0x67,0xa1,0x6c,0xa0,0x66,0x9f,0x6b,0x9c,0x63,
+ 0x9c,0x6b,0x98,0x64,0x96,0x69,0x95,0x63,0x98,0x6d,0x98,0x65,0x9b,0x69,0x9e,0x64,
+ 0x9f,0x71,0xa0,0x66,0xa2,0x6a,0xa0,0x65,0xa0,0x71,0x9e,0x5f,0x9b,0x6e,0x98,0x64,
+ 0x98,0x69,0x96,0x65,0x95,0x6d,0x97,0x67,0x9a,0x6e,0x9b,0x63,0x9e,0x72,0xa0,0x5f,
+ 0xa1,0x6a,0xa1,0x64,0x9f,0x6b,0x9c,0x63,0x9b,0x6f,0x98,0x64,0x95,0x6c,0x94,0x63,
+ 0x95,0x6d,0x96,0x63,0x98,0x66,0x9a,0x64,0xa0,0x6f,0xa0,0x64,0xa1,0x6d,0xa0,0x64,
+ 0xa0,0x6c,0x9e,0x66,0x9c,0x6d,0x99,0x65,0x99,0x6d,0x96,0x61,0x96,0x6d,0x96,0x65,
+ 0x9a,0x6e,0x9b,0x65,0x9e,0x71,0x9f,0x62,0xa2,0x71,0xa1,0x66,0x9f,0x6b,0x9d,0x64,
+ 0x9e,0x6e,0x9a,0x66,0x97,0x6c,0x95,0x63,0x95,0x72,0x97,0x65,0x9a,0x6c,0x99,0x63,
+ 0x9d,0x70,0xa0,0x63,0xa1,0x6b,0x9f,0x61,0xa0,0x6d,0xa0,0x68,0x9d,0x6c,0x98,0x64,
+ 0x98,0x6e,0x96,0x65,0x95,0x6a,0x96,0x63,0x99,0x6c,0x99,0x68,0x9c,0x70,0x9e,0x65,
+ 0xa1,0x6f,0xa0,0x64,0x9f,0x71,0xa0,0x67,0x9d,0x71,0x99,0x5f,0x97,0x6b,0x97,0x6b,
+ 0x96,0x6d,0x95,0x65,0x97,0x6e,0x99,0x67,0x9b,0x70,0x9d,0x6d,0x9f,0x6d,0xa0,0x68,
+ 0x9f,0x71,0x9e,0x64,0x9d,0x6c,0x99,0x63,0x99,0x74,0x96,0x65,0x93,0x72,0x95,0x65,
+ 0x96,0x70,0x9a,0x68,0x9b,0x6e,0x9e,0x66,0xa1,0x6f,0x9f,0x67,0xa0,0x6c,0x9f,0x66,
+ 0x9c,0x6f,0x9a,0x65,0x98,0x69,0x96,0x65,0x95,0x6f,0x94,0x63,0x95,0x71,0x98,0x66,
+ 0x9b,0x6b,0x9c,0x69,0x9d,0x6e,0x9e,0x67,0x9e,0x77,0x9e,0x67,0x9c,0x72,0x9a,0x66,
+ 0x97,0x71,0x96,0x68,0x96,0x6c,0x92,0x67,0x95,0x6e,0x98,0x68,0x99,0x6e,0x9b,0x6b,
+ 0x9e,0x70,0xa0,0x65,0xa0,0x6c,0x9d,0x65,0x9d,0x6f,0x9a,0x63,0x97,0x69,0x94,0x68,
+ 0x93,0x6d,0x93,0x62,0x95,0x6f,0x95,0x63,0x9a,0x6e,0x9c,0x65,0x9d,0x6a,0x9e,0x64,
+ 0xa1,0x6e,0x9e,0x64,0x9c,0x68,0x9a,0x64,0x99,0x74,0x94,0x62,0x93,0x64,0x94,0x60,
+ 0x95,0x69,0x96,0x65,0x99,0x6e,0x9c,0x61,0x9c,0x73,0x9a,0x6b,0xa3,0x59,0xa2,0x4f,
+ 0xa1,0x58,0x9d,0x59,0x9c,0x66,0x99,0x5e,0x97,0x60,0x99,0x58,0x9a,0x5c,0x9a,0x57,
+ 0xa0,0x5e,0xa2,0x55,0xa3,0x5d,0xa3,0x56,0xa5,0x64,0xa2,0x53,0xa1,0x62,0x9f,0x59,
+ 0x9c,0x5b,0x9a,0x52,0x99,0x5c,0x98,0x55,0x9b,0x62,0x9c,0x56,0x9c,0x60,0x9f,0x54,
+ 0xa3,0x5b,0xa2,0x58,0xa3,0x5b,0xa2,0x51,0xa2,0x60,0x9f,0x53,0x9e,0x5b,0x9a,0x56,
+ 0x99,0x5f,0x97,0x55,0x99,0x5b,0x9b,0x56,0x9d,0x5f,0x9f,0x51,0xa2,0x5d,0xa3,0x55,
+ 0xa5,0x5d,0xa2,0x54,0xa1,0x61,0x9e,0x59,0x9c,0x5e,0x99,0x57,0x98,0x5e,0x96,0x59,
+ 0x99,0x5d,0x99,0x51,0x9d,0x5d,0x9e,0x53,0xa2,0x5e,0xa1,0x50,0xa3,0x5e,0xa1,0x54,
+ 0xa0,0x5b,0x9d,0x56,0x9a,0x60,0x98,0x56,0x96,0x5b,0x95,0x59,0x96,0x5e,0x97,0x53,
+ 0x9a,0x5c,0x9e,0x54,0x9f,0x5c,0xa0,0x54,0xa2,0x5f,0xa1,0x51,0xa0,0x60,0x9d,0x50,
+ 0x9c,0x5d,0x99,0x54,0x97,0x5a,0x95,0x50,0x96,0x60,0x98,0x56,0x9a,0x5a,0x9b,0x52,
+ 0x9f,0x5d,0xa1,0x54,0x9f,0x5e,0x9f,0x53,0x9d,0x60,0x9c,0x54,0x9a,0x5c,0x97,0x59,
+ 0x97,0x5b,0x94,0x52,0x94,0x5a,0x95,0x57,0x98,0x5f,0x9a,0x56,0x9e,0x5a,0x9e,0x53,
+ 0x9f,0x5b,0x9f,0x53,0x9f,0x5d,0x9b,0x52,0x9b,0x5c,0x97,0x52,0x95,0x61,0x94,0x56,
+ 0x95,0x5b,0x95,0x56,0x97,0x5e,0x9a,0x56,0x9c,0x5e,0x9f,0x57,0x9f,0x5b,0x9e,0x55,
+ 0x9e,0x5a,0x9d,0x55,0x9a,0x5d,0x96,0x57,0x94,0x5e,0x93,0x51,0x92,0x5a,0x93,0x54,
+ 0x97,0x5b,0x99,0x55,0x9b,0x5b,0x9c,0x51,0x9f,0x5d,0x9d,0x53,0x9d,0x5c,0x9d,0x57,
+ 0x9a,0x5e,0x96,0x55,0x94,0x5b,0x91,0x52,0x92,0x61,0x92,0x57,0x93,0x5c,0x96,0x58,
+ 0x9a,0x5d,0x9c,0x56,0x9d,0x5c,0x9c,0x55,0x9e,0x5e,0x9b,0x59,0x9a,0x5b,0x97,0x52,
+ 0x96,0x5d,0x93,0x57,0x93,0x59,0x93,0x57,0x95,0x60,0x97,0x56,0x9a,0x5a,0x9d,0x58,
+ 0x9f,0x5d,0x9d,0x56,0x9e,0x5d,0x9c,0x53,0x9a,0x62,0x97,0x52,0x97,0x5f,0x93,0x52,
+ 0x92,0x5c,0x93,0x5c,0x95,0x5e,0x96,0x56,0x99,0x5e,0x9a,0x56,0x9d,0x5b,0x9d,0x57,
+ 0x9d,0x5f,0x9c,0x58,0x99,0x5c,0x96,0x58,0x95,0x5f,0x92,0x56,0x90,0x60,0x91,0x55,
+ 0x93,0x5c,0x95,0x56,0x98,0x58,0x9a,0x56,0x9c,0x5b,0x9c,0x56,0x9c,0x60,0x9b,0x57,
+ 0x99,0x61,0x96,0x58,0x96,0x5e,0x92,0x56,0x91,0x61,0x90,0x57,0x93,0x5e,0x94,0x56,
+ 0x97,0x62,0x9a,0x5c,0x9b,0x5e,0x9b,0x57,0x9c,0x5d,0x9a,0x5b,0x9b,0x5f,0x96,0x5a,
+ 0x94,0x61,0x93,0x59,0x90,0x5c,0x8f,0x56,0x91,0x63,0x92,0x5a,0x93,0x61,0x97,0x59,
+ 0x99,0x62,0x99,0x5a,0x9c,0x5d,0x9b,0x57,0x9a,0x5d,0x97,0x5a,0x95,0x5f,0x92,0x52,
+ 0x91,0x61,0x91,0x5c,0x92,0x5a,0x92,0x58,0x94,0x61,0x97,0x5c,0x98,0x60,0x9b,0x5b,
+ 0x9a,0x64,0x9a,0x5a,0x9a,0x5d,0x97,0x59,0x94,0x61,0x94,0x58,0x91,0x62,0x90,0x57,
+ 0x90,0x5c,0x93,0x5b,0x94,0x60,0x97,0x5d,0x99,0x62,0x9b,0x5a,0x9a,0x64,0x9a,0x59,
+ 0x99,0x68,0x98,0x5b,0x94,0x64,0x93,0x56,0x90,0x64,0x8f,0x5d,0x90,0x61,0x93,0x5f,
+ 0x94,0x64,0x97,0x5b,0x98,0x5f,0x9a,0x5e,0x9a,0x65,0x99,0x59,0x98,0x60,0x98,0x5d,
+ 0x95,0x63,0x91,0x5c,0x90,0x61,0x90,0x5d,0x8f,0x65,0x91,0x5c,0x93,0x62,0x96,0x5f,
+ 0x99,0x68,0x99,0x5a,0x9b,0x61,0x99,0x5d,0x99,0x68,0x97,0x5c,0x95,0x67,0x92,0x62,
+ 0x90,0x6c,0x90,0x5f,0x8d,0x61,0x8f,0x62,0x91,0x67,0x91,0x61,0x96,0x66,0x97,0x64,
+ 0x98,0x68,0x98,0x64,0x98,0x69,0x95,0x61,0x94,0x6c,0x92,0x64,0x90,0x69,0x8d,0x63,
+ 0x8f,0x6d,0x8e,0x61,0x90,0x6b,0x92,0x5f,0x95,0x6d,0x96,0x65,0x97,0x6c,0x96,0x6b,
+ 0x96,0x72,0x94,0x65,0x93,0x67,0x8f,0x61,0x8e,0x6a,0x8d,0x69,0x8d,0x6d,0x8d,0x67,
+ 0x8e,0x75,0x8f,0x6a,0x93,0x6c,0x94,0x62,0x94,0x73,0x96,0x65,0x96,0x6e,0x93,0x65,
+ 0x92,0x72,0x90,0x64,0x8d,0x6d,0x8b,0x65,0x8a,0x70,0x8b,0x67,0x8c,0x71,0x8e,0x6a,
+ 0x92,0x6d,0x93,0x66,0x94,0x6d,0x94,0x68,0x94,0x70,0x92,0x67,0x91,0x71,0x8e,0x67,
+ 0x8a,0x71,0x8a,0x65,0x89,0x6c,0x89,0x66,0x8b,0x6f,0x8d,0x65,0x91,0x6f,0x91,0x62,
+ 0x94,0x6f,0x94,0x65,0x94,0x69,0x93,0x6a,0x92,0x71,0x8e,0x63,0x8d,0x6c,0x8a,0x62,
+ 0x88,0x6f,0x89,0x67,0x8b,0x68,0x8b,0x63,0x8e,0x70,0x91,0x61,0x94,0x6b,0x93,0x68,
+ 0x94,0x6d,0x93,0x65,0x92,0x67,0x8e,0x64,0x8d,0x6d,0x8a,0x63,0x88,0x68,0x89,0x65,
+ 0x8b,0x71,0x8b,0x65,0x8d,0x6a,0x90,0x65,0x92,0x6f,0x92,0x62,0x91,0x70,0x91,0x61,
+ 0x8f,0x72,0x8e,0x64,0x8d,0x6a,0x89,0x61,0x89,0x6c,0x88,0x62,0x8a,0x6e,0x8a,0x65,
+ 0x8b,0x6e,0x8d,0x62,0x90,0x67,0x8f,0x64,0x91,0x6f,0x90,0x64,0x90,0x6f,0x8c,0x66,
+ 0x8b,0x73,0x89,0x68,0x88,0x6b,0x86,0x64,0x87,0x76,0x88,0x69,0x8b,0x6f,0x8b,0x66,
+ 0x8d,0x70,0x8f,0x65,0x90,0x6e,0x8e,0x68,0x8d,0x78,0x8d,0x6d,0x89,0x77,0x87,0x6c,
+ 0x86,0x76,0x84,0x6c,0x84,0x73,0x85,0x6e,0x87,0x74,0x88,0x6b,0x8b,0x75,0x8d,0x6f,
+ 0x8d,0x79,0x8e,0x6c,0x8c,0x74,0x8a,0x6b,0x88,0x77,0x85,0x70,0x84,0x79,0x81,0x71,
+ 0x81,0x7a,0x83,0x72,0x83,0x74,0x87,0x6e,0x89,0x77,0x8b,0x6e,0x8b,0x78,0x8b,0x71,
+ 0x8c,0x76,0x8a,0x70,0x88,0x78,0x86,0x6f,0x85,0x7a,0x81,0x72,0x80,0x75,0x81,0x71,
+ 0x84,0x78,0x85,0x71,0x87,0x76,0x8a,0x70,0x8b,0x78,0x8c,0x6e,0x8b,0x77,0x8b,0x6f,
+ 0x89,0x7d,0x86,0x76,0x85,0x77,0x83,0x73,0x82,0x7e,0x82,0x73,0x84,0x77,0x85,0x71,
+ 0x89,0x7a,0x8a,0x72,0x8c,0x7c,0x8c,0x70,0x8d,0x7e,0x8b,0x70,0x8a,0x7b,0x88,0x72,
+ 0x87,0x7a,0x84,0x73,0x83,0x7c,0x83,0x74,0x85,0x80,0x87,0x76,0x89,0x80,0x8b,0x7b,
+ 0x8b,0x83,0x88,0x7d,0x6c,0x56,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,00
+};
diff --git a/backend/snapscan-mutex.c b/backend/snapscan-mutex.c
new file mode 100644
index 0000000..0321741
--- /dev/null
+++ b/backend/snapscan-mutex.c
@@ -0,0 +1,183 @@
+/*
+ Mutex implementation for SnapScan backend
+
+ Copyright (C) 2000, 2004 Henrik Johansson, Oliver Schwartz
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.*/
+
+#if defined __BEOS__
+
+#include <OS.h>
+#define snapscan_mutex_t sem_id
+
+static int snapscani_mutex_open(snapscan_mutex_t* a_sem, const char* dev UNUSEDARG)
+{
+ *a_sem = create_sem(1, "snapscan_mutex");
+ return 1;
+}
+
+static void snapscani_mutex_close(snapscan_mutex_t* a_sem)
+{
+ delete_sem(*a_sem);
+}
+
+static void snapscani_mutex_lock(snapscan_mutex_t* a_sem)
+{
+ acquire_sem(*a_sem);
+}
+
+static void snapscani_mutex_unlock(snapscan_mutex_t* a_sem)
+{
+ release_sem(*a_sem);
+}
+
+
+
+#elif defined USE_PTHREAD || defined HAVE_OS2_H
+
+#include <pthread.h>
+#define snapscan_mutex_t pthread_mutex_t
+
+static int snapscani_mutex_open(snapscan_mutex_t* sem_id, const char* dev UNUSEDARG)
+{
+ pthread_mutex_init(sem_id, NULL);
+ return 1;
+}
+
+static void snapscani_mutex_close(snapscan_mutex_t* sem_id)
+{
+ pthread_mutex_destroy(sem_id);
+}
+
+static void snapscani_mutex_lock(snapscan_mutex_t* sem_id)
+{
+ pthread_mutex_lock(sem_id);
+}
+
+static void snapscani_mutex_unlock(snapscan_mutex_t* sem_id)
+{
+ pthread_mutex_unlock(sem_id);
+}
+
+#else /* defined USE_PTHREAD || defined HAVE_OS2_H */
+
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define snapscan_mutex_t int
+
+/* check for union semun */
+#if defined(HAVE_UNION_SEMUN)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short int *array; /* array for GETALL, SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif /* defined HAVE_UNION_SEMUN */
+
+static struct sembuf sem_wait = { 0, -1, 0 };
+static struct sembuf sem_signal = { 0, 1, 0 };
+
+static unsigned int snapscani_bernstein(const unsigned char* str)
+{
+ unsigned int hash = 5381; /* some arbitrary number */
+ int c;
+
+ while (*str)
+ {
+ c = *str++;
+ hash = ((hash << 5) + hash) + c;
+ }
+ return hash;
+}
+
+static int snapscani_mutex_open(snapscan_mutex_t* sem_id, const char* dev)
+{
+ static const char *me = "snapscani_mutex_open";
+ key_t ipc_key = -1;
+
+ if (strstr(dev, "libusb:") == dev)
+ {
+ key_t ipc_key = (key_t) snapscani_bernstein((const unsigned char*) dev+7);
+ DBG (DL_INFO, "%s: using IPC key 0x%08x for device %s\n",
+ me, ipc_key, dev);
+ }
+ else
+ {
+ ipc_key = ftok(dev, 0x12);
+
+ if (ipc_key == -1)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: could not obtain IPC key for device %s: %s\n", me, dev, strerror(errno));
+ return 0;
+ }
+ }
+
+ *sem_id = semget( ipc_key, 1, IPC_CREAT | 0660 );
+ if (*sem_id == -1)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: semget failed: %s\n", me, strerror(errno));
+ return 0;
+ }
+
+ semop(*sem_id, &sem_signal, 1);
+ return 1;
+}
+
+static void snapscani_mutex_close(snapscan_mutex_t* sem_id)
+{
+ static union semun dummy_semun_arg;
+ semctl(*sem_id, 0, IPC_RMID, dummy_semun_arg);
+}
+
+static void snapscani_mutex_lock(snapscan_mutex_t* sem_id)
+{
+ semop(*sem_id, &sem_wait, 1);
+}
+
+static void snapscani_mutex_unlock(snapscan_mutex_t* sem_id)
+{
+ semop(*sem_id, &sem_signal, 1);
+}
+
+#endif /* defined USE_PTHREAD || defined HAVE_OS2_H */
diff --git a/backend/snapscan-options.c b/backend/snapscan-options.c
new file mode 100644
index 0000000..15f3a4a
--- /dev/null
+++ b/backend/snapscan-options.c
@@ -0,0 +1,1922 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998, 2001, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Mikael Magnusson,
+ Andrew Goodbody, Oliver Schwartz and Kevin Charter
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+/* $Id$
+ SANE SnapScan backend */
+
+/* default option values */
+
+#define DEFAULT_RES 300
+#define DEFAULT_PREVIEW SANE_FALSE
+#define DEFAULT_HIGHQUALITY SANE_FALSE
+#define DEFAULT_BRIGHTNESS 0
+#define DEFAULT_CONTRAST 0
+#define DEFAULT_GAMMA SANE_FIX(1.8)
+#define DEFAULT_HALFTONE SANE_FALSE
+#define DEFAULT_NEGATIVE SANE_FALSE
+#define DEFAULT_THRESHOLD 50
+#define DEFAULT_QUALITY SANE_TRUE
+#define DEFAULT_CUSTOM_GAMMA SANE_FALSE
+#define DEFAULT_GAMMA_BIND SANE_FALSE
+
+static SANE_Int def_rgb_lpr = 4;
+static SANE_Int def_gs_lpr = 12;
+static SANE_Int def_bpp = 8;
+static SANE_Int def_frame_no = 1;
+
+
+/* predefined preview mode name */
+static char md_auto[] = "Auto";
+
+/* predefined focus mode name */
+static char md_manual[] = "Manual";
+
+/* predefined scan mode names */
+static char md_colour[] = SANE_VALUE_SCAN_MODE_COLOR;
+static char md_bilevelcolour[] = SANE_VALUE_SCAN_MODE_HALFTONE;
+static char md_greyscale[] = SANE_VALUE_SCAN_MODE_GRAY;
+static char md_lineart[] = SANE_VALUE_SCAN_MODE_LINEART;
+
+/* predefined scan source names */
+static char src_flatbed[] = SANE_I18N("Flatbed");
+static char src_tpo[] = SANE_I18N("Transparency Adapter");
+static char src_adf[] = SANE_I18N("Document Feeder");
+
+/* predefined scan window setting names */
+static char pdw_none[] = SANE_I18N("None");
+static char pdw_6X4[] = SANE_I18N("6x4 (inch)");
+static char pdw_8X10[] = SANE_I18N("8x10 (inch)");
+static char pdw_85X11[] = SANE_I18N("8.5x11 (inch)");
+
+/* predefined dither matrix names */
+static char dm_none[] = SANE_I18N("Halftoning Unsupported");
+static char dm_dd8x8[] = SANE_I18N("DispersedDot8x8");
+static char dm_dd16x16[] = SANE_I18N("DispersedDot16x16");
+
+/* strings */
+static char lpr_desc[] = SANE_I18N(
+ "Number of scan lines to request in a SCSI read. "
+ "Changing this parameter allows you to tune the speed at which "
+ "data is read from the scanner during scans. If this is set too "
+ "low, the scanner will have to stop periodically in the middle of "
+ "a scan; if it's set too high, X-based frontends may stop responding "
+ "to X events and your system could bog down.");
+
+static char frame_desc[] = SANE_I18N(
+ "Frame number of media holder that should be scanned.");
+
+static char focus_mode_desc[] = SANE_I18N(
+ "Use manual or automatic selection of focus point.");
+
+static char focus_desc[] = SANE_I18N(
+ "Focus point for scanning.");
+
+/* ranges */
+static const SANE_Range x_range_fb =
+{
+ SANE_FIX (0.0), SANE_FIX (216.0), 0
+}; /* mm */
+static const SANE_Range y_range_fb =
+{
+ SANE_FIX (0.0), SANE_FIX (297.0), 0
+}; /* mm */
+
+/* default TPO range (shortest y_range
+ to avoid tray collision.
+*/
+static const SANE_Range x_range_tpo_default =
+{
+ SANE_FIX (0.0), SANE_FIX (129.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_default =
+{
+ SANE_FIX (0.0), SANE_FIX (180.0), 0
+}; /* mm */
+
+/* TPO range for the Agfa 1236 */
+static const SANE_Range x_range_tpo_1236 =
+{
+ SANE_FIX (0.0), SANE_FIX (203.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_1236 =
+{
+ SANE_FIX (0.0), SANE_FIX (254.0), 0
+}; /* mm */
+
+/* TPO range for the Agfa e50 */
+static const SANE_Range x_range_tpo_e50 =
+{
+ SANE_FIX (0.0), SANE_FIX (40.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_e50 =
+{
+ SANE_FIX (0.0), SANE_FIX (240.0), 0
+}; /* mm */
+
+/* TPO range for the Epson 1670 */
+static const SANE_Range x_range_tpo_1670 =
+{
+ SANE_FIX (0.0), SANE_FIX (101.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_1670 =
+{
+ SANE_FIX (0.0), SANE_FIX (228.0), 0
+}; /* mm */
+
+/* TPO range for the Epson 2480 */
+static const SANE_Range x_range_tpo_2480 =
+{
+ SANE_FIX (0.0), SANE_FIX (55.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_2480 =
+{
+ SANE_FIX (0.0), SANE_FIX (125.0), 0
+}; /* mm */
+/* TPO range for the Epson 2580 */
+static const SANE_Range x_range_tpo_2580 =
+{
+ SANE_FIX (0.0), SANE_FIX (55.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_2580 =
+{
+ SANE_FIX (0.0), SANE_FIX (80.0), 0
+}; /* mm */
+
+/* TPO range for the Scanwit 2720S */
+static const SANE_Range x_range_tpo_2720s =
+{
+ SANE_FIX (0.0), SANE_FIX (23.6), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_2720s =
+{
+ SANE_FIX (0.0), SANE_FIX (35.7), 0
+}; /* mm */
+
+/* TPO range for the Epson 3490 */
+static const SANE_Range x_range_tpo_3490 =
+{
+ SANE_FIX (0.0), SANE_FIX (33.0), 0
+}; /* mm */
+static const SANE_Range y_range_tpo_3490 =
+{
+ SANE_FIX (0.0), SANE_FIX (162.0), 0
+}; /* mm */
+
+static SANE_Range x_range_tpo;
+static SANE_Range y_range_tpo;
+static const SANE_Range gamma_range =
+{
+ SANE_FIX (0.0), SANE_FIX (4.0), 0
+};
+static const SANE_Range gamma_vrange =
+{
+ 0, 65535, 1
+};
+static const SANE_Range lpr_range =
+{
+ 1, 50, 1
+};
+static const SANE_Range frame_range =
+{
+ 1, 6, 1
+};
+static const SANE_Range focus_range =
+{
+ 0, 0x300, 6
+};
+
+static const SANE_Range brightness_range =
+{
+ -400 << SANE_FIXED_SCALE_SHIFT,
+ 400 << SANE_FIXED_SCALE_SHIFT,
+ 1 << SANE_FIXED_SCALE_SHIFT
+};
+
+static const SANE_Range contrast_range =
+{
+ -100 << SANE_FIXED_SCALE_SHIFT,
+ 400 << SANE_FIXED_SCALE_SHIFT,
+ 1 << SANE_FIXED_SCALE_SHIFT
+};
+
+static const SANE_Range positive_percent_range =
+{
+ 0 << SANE_FIXED_SCALE_SHIFT,
+ 100 << SANE_FIXED_SCALE_SHIFT,
+ 1 << SANE_FIXED_SCALE_SHIFT
+};
+
+static void control_options(SnapScan_Scanner *pss);
+
+/* init_options -- initialize the option set for a scanner; expects the
+ scanner structure's hardware configuration byte (hconfig) to be valid.
+
+ ARGS: a pointer to an existing scanner structure
+ RET: nothing
+ SIDE: the option set of *ps is initialized; this includes both
+ the option descriptors and the option values themselves */
+static void init_options (SnapScan_Scanner * ps)
+{
+ static SANE_Word resolutions_300[] =
+ {6, 50, 75, 100, 150, 200, 300};
+ static SANE_Word resolutions_600[] =
+ {8, 50, 75, 100, 150, 200, 300, 450, 600};
+ static SANE_Word resolutions_1200[] =
+ {10, 50, 75, 100, 150, 200, 300, 450, 600, 900, 1200};
+ static SANE_Word resolutions_1200_5000e[] =
+ {9, 50, 75, 100, 150, 200, 300, 450, 600, 1200};
+ static SANE_Word resolutions_1600[] =
+ {10, 50, 75, 100, 150, 200, 300, 400, 600, 800, 1600};
+ static SANE_Word resolutions_2400[] =
+ {10, 50, 75, 100, 150, 200, 300, 400, 600, 1200, 2400};
+ static SANE_Word resolutions_2700[] =
+ {4, 337, 675, 1350, 2700};
+ static SANE_Word resolutions_3200[] =
+ {15, 50, 150, 200, 240, 266, 300, 350, 360, 400, 600, 720, 800, 1200, 1600, 3200};
+ static SANE_String_Const names_all[] =
+ {md_colour, md_bilevelcolour, md_greyscale, md_lineart, NULL};
+ static SANE_String_Const names_basic[] =
+ {md_colour, md_greyscale, md_lineart, NULL};
+ static SANE_String_Const preview_names_all[] =
+ {md_auto, md_colour, md_bilevelcolour, md_greyscale, md_lineart, NULL};
+ static SANE_String_Const preview_names_basic[] =
+ {md_auto, md_colour, md_greyscale, md_lineart, NULL};
+ static SANE_String_Const focus_modes[] =
+ {md_auto, md_manual, NULL};
+ static SANE_Int bit_depth_list[4];
+ int bit_depths;
+ SANE_Option_Descriptor *po = ps->options;
+
+ /* Initialize TPO range */
+ switch (ps->pdev->model)
+ {
+ case SNAPSCAN1236:
+ x_range_tpo = x_range_tpo_1236;
+ y_range_tpo = y_range_tpo_1236;
+ break;
+ case SNAPSCANE20:
+ case SNAPSCANE50:
+ case SNAPSCANE52:
+ x_range_tpo = x_range_tpo_e50;
+ y_range_tpo = y_range_tpo_e50;
+ break;
+ case PERFECTION1270:
+ case PERFECTION1670:
+ x_range_tpo = x_range_tpo_1670;
+ y_range_tpo = y_range_tpo_1670;
+ break;
+ case PERFECTION2480:
+ if (ps->hconfig_epson & 0x20)
+ {
+ x_range_tpo = x_range_tpo_2580;
+ y_range_tpo = y_range_tpo_2580;
+ }
+ else
+ {
+ x_range_tpo = x_range_tpo_2480;
+ y_range_tpo = y_range_tpo_2480;
+ }
+ break;
+ case SCANWIT2720S:
+ x_range_tpo = x_range_tpo_2720s;
+ y_range_tpo = y_range_tpo_2720s;
+ break;
+ case PERFECTION3490:
+ x_range_tpo = x_range_tpo_3490;
+ y_range_tpo = y_range_tpo_3490;
+ break;
+ default:
+ x_range_tpo = x_range_tpo_default;
+ y_range_tpo = y_range_tpo_default;
+ break;
+ }
+
+ /* Initialize option descriptors */
+ po[OPT_COUNT].name = SANE_NAME_NUM_OPTIONS;
+ po[OPT_COUNT].title = SANE_TITLE_NUM_OPTIONS;
+ po[OPT_COUNT].desc = SANE_DESC_NUM_OPTIONS;
+ po[OPT_COUNT].type = SANE_TYPE_INT;
+ po[OPT_COUNT].unit = SANE_UNIT_NONE;
+ po[OPT_COUNT].size = sizeof (SANE_Word);
+ po[OPT_COUNT].cap = SANE_CAP_SOFT_DETECT;
+ {
+ static SANE_Range count_range =
+ {NUM_OPTS, NUM_OPTS, 0};
+ po[OPT_COUNT].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_COUNT].constraint.range = &count_range;
+ }
+
+ po[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ po[OPT_MODE_GROUP].desc = "";
+ po[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ po[OPT_MODE_GROUP].cap = 0;
+ po[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ ps->res = DEFAULT_RES;
+ po[OPT_SCANRES].name = SANE_NAME_SCAN_RESOLUTION;
+ po[OPT_SCANRES].title = SANE_TITLE_SCAN_RESOLUTION;
+ po[OPT_SCANRES].desc = SANE_DESC_SCAN_RESOLUTION;
+ po[OPT_SCANRES].type = SANE_TYPE_INT;
+ po[OPT_SCANRES].unit = SANE_UNIT_DPI;
+ po[OPT_SCANRES].size = sizeof (SANE_Word);
+ po[OPT_SCANRES].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_SCANRES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ switch (ps->pdev->model)
+ {
+ case SNAPSCAN310:
+ case PRISA310: /* WG changed */
+ po[OPT_SCANRES].constraint.word_list = resolutions_300;
+ break;
+ case SNAPSCANE50:
+ case SNAPSCANE52:
+ case PRISA5300:
+ case PRISA1240:
+ case ARCUS1200:
+ po[OPT_SCANRES].constraint.word_list = resolutions_1200;
+ break;
+ case PRISA5000E:
+ case PRISA5000:
+ case PRISA5150:
+ po[OPT_SCANRES].constraint.word_list = resolutions_1200_5000e;
+ break;
+ case PERFECTION1670:
+ po[OPT_SCANRES].constraint.word_list = resolutions_1600;
+ break;
+ case PERFECTION2480:
+ po[OPT_SCANRES].constraint.word_list = resolutions_2400;
+ break;
+ case PERFECTION3490:
+ po[OPT_SCANRES].constraint.word_list = resolutions_3200;
+ break;
+ case SCANWIT2720S:
+ po[OPT_SCANRES].constraint.word_list = resolutions_2700;
+ ps->val[OPT_SCANRES].w = 1350;
+ ps->res = 1350;
+ break;
+ default:
+ po[OPT_SCANRES].constraint.word_list = resolutions_600;
+ break;
+ }
+ DBG (DL_OPTION_TRACE,
+ "sane_init_options resolution is %d\n", ps->res);
+
+ po[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ po[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ po[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ po[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ po[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ po[OPT_PREVIEW].size = sizeof (SANE_Word);
+ po[OPT_PREVIEW].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ ps->preview = DEFAULT_PREVIEW;
+
+ po[OPT_HIGHQUALITY].name = "high-quality";
+ po[OPT_HIGHQUALITY].title = SANE_I18N("Quality scan");
+ po[OPT_HIGHQUALITY].desc = SANE_I18N("Highest quality but lower speed");
+ po[OPT_HIGHQUALITY].type = SANE_TYPE_BOOL;
+ po[OPT_HIGHQUALITY].unit = SANE_UNIT_NONE;
+ po[OPT_HIGHQUALITY].size = sizeof (SANE_Word);
+ po[OPT_HIGHQUALITY].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_HIGHQUALITY].constraint_type = SANE_CONSTRAINT_NONE;
+ ps->highquality = DEFAULT_HIGHQUALITY;
+ if (ps->pdev->model == PERFECTION1270)
+ {
+ po[OPT_HIGHQUALITY].cap |= SANE_CAP_INACTIVE;
+ ps->val[OPT_HIGHQUALITY].b = SANE_TRUE;
+ ps->highquality=SANE_TRUE;
+ }
+
+ po[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ po[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ po[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ po[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ po[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ po[OPT_BRIGHTNESS].size = sizeof (int);
+ po[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ po[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ ps->bright = DEFAULT_BRIGHTNESS;
+
+ po[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ po[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ po[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ po[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ po[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ po[OPT_CONTRAST].size = sizeof (int);
+ po[OPT_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ po[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_CONTRAST].constraint.range = &contrast_range;
+ ps->contrast = DEFAULT_CONTRAST;
+
+ po[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ po[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ po[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ po[OPT_MODE].type = SANE_TYPE_STRING;
+ po[OPT_MODE].unit = SANE_UNIT_NONE;
+ po[OPT_MODE].size = 32;
+ po[OPT_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ switch (ps->pdev->model)
+ {
+ case SNAPSCAN310:
+ case PRISA310:
+ case PERFECTION3490:
+ po[OPT_MODE].constraint.string_list = names_basic;
+ break;
+ default:
+ po[OPT_MODE].constraint.string_list = names_all;
+ break;
+ }
+ ps->mode_s = md_colour;
+ ps->mode = MD_COLOUR;
+
+ po[OPT_PREVIEW_MODE].name = "preview-mode";
+ po[OPT_PREVIEW_MODE].title = SANE_I18N("Preview mode");
+ po[OPT_PREVIEW_MODE].desc = SANE_I18N(
+ "Select the mode for previews. Greyscale previews usually give "
+ "the best combination of speed and detail.");
+ po[OPT_PREVIEW_MODE].type = SANE_TYPE_STRING;
+ po[OPT_PREVIEW_MODE].unit = SANE_UNIT_NONE;
+ po[OPT_PREVIEW_MODE].size = 32;
+ po[OPT_PREVIEW_MODE].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT
+ | SANE_CAP_ADVANCED
+ | SANE_CAP_AUTOMATIC;
+ po[OPT_PREVIEW_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ switch (ps->pdev->model)
+ {
+ case SNAPSCAN310:
+ case PRISA310:
+ case PERFECTION3490:
+ po[OPT_PREVIEW_MODE].constraint.string_list = preview_names_basic;
+ break;
+ default:
+ po[OPT_PREVIEW_MODE].constraint.string_list = preview_names_all;
+ break;
+ }
+ ps->preview_mode_s = md_auto;
+ ps->preview_mode = ps->mode;
+
+ /* source */
+ po[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ po[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ po[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ po[OPT_SOURCE].type = SANE_TYPE_STRING;
+ po[OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT
+ | SANE_CAP_INACTIVE
+ | SANE_CAP_AUTOMATIC;
+ po[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ {
+ static SANE_String_Const source_list[3];
+ int i = 0;
+
+ source_list[i++]= src_flatbed;
+ if (ps->hconfig & HCFG_TPO)
+ {
+ source_list[i++] = src_tpo;
+ po[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (ps->hconfig & HCFG_ADF)
+ {
+ source_list[i++] = src_adf;
+ po[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ source_list[i] = 0;
+ po[OPT_SOURCE].size = max_string_size(source_list);
+ po[OPT_SOURCE].constraint.string_list = source_list;
+ if (ps->pdev->model == SCANWIT2720S)
+ {
+ ps->source = SRC_TPO;
+ ps->source_s = (SANE_Char *) strdup(src_tpo);
+ ps->pdev->x_range.max = x_range_tpo.max;
+ ps->pdev->y_range.max = y_range_tpo.max;
+ }
+ else
+ {
+ ps->source = SRC_FLATBED;
+ ps->source_s = (SANE_Char *) strdup(src_flatbed);
+ }
+ }
+
+ po[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ po[OPT_GEOMETRY_GROUP].desc = "";
+ po[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ po[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ po[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ po[OPT_TLX].name = SANE_NAME_SCAN_TL_X;
+ po[OPT_TLX].title = SANE_TITLE_SCAN_TL_X;
+ po[OPT_TLX].desc = SANE_DESC_SCAN_TL_X;
+ po[OPT_TLX].type = SANE_TYPE_FIXED;
+ po[OPT_TLX].unit = SANE_UNIT_MM;
+ po[OPT_TLX].size = sizeof (SANE_Word);
+ po[OPT_TLX].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_TLX].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_TLX].constraint.range = &(ps->pdev->x_range);
+ ps->tlx = ps->pdev->x_range.min;
+
+ po[OPT_TLY].name = SANE_NAME_SCAN_TL_Y;
+ po[OPT_TLY].title = SANE_TITLE_SCAN_TL_Y;
+ po[OPT_TLY].desc = SANE_DESC_SCAN_TL_Y;
+ po[OPT_TLY].type = SANE_TYPE_FIXED;
+ po[OPT_TLY].unit = SANE_UNIT_MM;
+ po[OPT_TLY].size = sizeof (SANE_Word);
+ po[OPT_TLY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_TLY].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_TLY].constraint.range = &(ps->pdev->y_range);
+ ps->tly = ps->pdev->y_range.min;
+
+ po[OPT_BRX].name = SANE_NAME_SCAN_BR_X;
+ po[OPT_BRX].title = SANE_TITLE_SCAN_BR_X;
+ po[OPT_BRX].desc = SANE_DESC_SCAN_BR_X;
+ po[OPT_BRX].type = SANE_TYPE_FIXED;
+ po[OPT_BRX].unit = SANE_UNIT_MM;
+ po[OPT_BRX].size = sizeof (SANE_Word);
+ po[OPT_BRX].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_BRX].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_BRX].constraint.range = &(ps->pdev->x_range);
+ ps->brx = ps->pdev->x_range.max;
+
+ po[OPT_BRY].name = SANE_NAME_SCAN_BR_Y;
+ po[OPT_BRY].title = SANE_TITLE_SCAN_BR_Y;
+ po[OPT_BRY].desc = SANE_DESC_SCAN_BR_Y;
+ po[OPT_BRY].type = SANE_TYPE_FIXED;
+ po[OPT_BRY].unit = SANE_UNIT_MM;
+ po[OPT_BRY].size = sizeof (SANE_Word);
+ po[OPT_BRY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
+ po[OPT_BRY].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_BRY].constraint.range = &(ps->pdev->y_range);
+ ps->bry = ps->pdev->y_range.max;
+
+ po[OPT_PREDEF_WINDOW].name = "predef-window";
+ po[OPT_PREDEF_WINDOW].title = SANE_I18N("Predefined settings");
+ po[OPT_PREDEF_WINDOW].desc = SANE_I18N(
+ "Provides standard scanning areas for photographs, printed pages "
+ "and the like.");
+ po[OPT_PREDEF_WINDOW].type = SANE_TYPE_STRING;
+ po[OPT_PREDEF_WINDOW].unit = SANE_UNIT_NONE;
+ po[OPT_PREDEF_WINDOW].size = 32;
+ po[OPT_PREDEF_WINDOW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ {
+ static SANE_String_Const names[] =
+ {pdw_none, pdw_6X4, pdw_8X10, pdw_85X11, NULL};
+ po[OPT_PREDEF_WINDOW].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ po[OPT_PREDEF_WINDOW].constraint.string_list = names;
+ }
+ ps->predef_window = pdw_none;
+
+ po[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ po[OPT_ENHANCEMENT_GROUP].desc = "";
+ po[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ po[OPT_ENHANCEMENT_GROUP].cap = 0;
+ po[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* bit depth */
+ po[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ po[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ po[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ po[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ po[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
+ po[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
+ po[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ po[OPT_BIT_DEPTH].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ bit_depths = 0;
+ bit_depth_list[++bit_depths] = def_bpp;
+ switch (ps->pdev->model)
+ {
+ case PERFECTION2480:
+ case PERFECTION3490:
+ bit_depth_list[++bit_depths] = 16;
+ break;
+ case SCANWIT2720S:
+ bit_depth_list[bit_depths] = 12;
+ break;
+ default:
+ break;
+ }
+ bit_depth_list[0] = bit_depths;
+ po[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
+ if (ps->pdev->model == SCANWIT2720S)
+ {
+ ps->val[OPT_BIT_DEPTH].w = 12;
+ ps->bpp_scan = 12;
+ }
+ else
+ {
+ ps->val[OPT_BIT_DEPTH].w = def_bpp;
+ ps->bpp_scan = def_bpp;
+ }
+
+ po[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL;
+ po[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL;
+ po[OPT_QUALITY_CAL].desc = SANE_DESC_QUALITY_CAL;
+ po[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL;
+ po[OPT_QUALITY_CAL].unit = SANE_UNIT_NONE;
+ po[OPT_QUALITY_CAL].size = sizeof (SANE_Bool);
+ po[OPT_QUALITY_CAL].constraint_type = SANE_CONSTRAINT_NONE;
+ po[OPT_QUALITY_CAL].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ ps->val[OPT_QUALITY_CAL].b = DEFAULT_QUALITY;
+ /* Disable quality calibration option if not supported
+ Note: Snapscan e52 and Prisa5300 do not support quality calibration,
+ although HCFG_CAL_ALLOWED is set. */
+ if ((!(ps->hconfig & HCFG_CAL_ALLOWED))
+ || (ps->pdev->model == SNAPSCANE52)
+ || (ps->pdev->model == PERFECTION1670)
+ || (ps->pdev->model == PRISA5150)
+ || (ps->pdev->model == PRISA5300)) {
+ po[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE;
+ ps->val[OPT_QUALITY_CAL].b = SANE_FALSE;
+ }
+
+ if ((ps->pdev->model == PRISA5150) ||
+ (ps->pdev->model == STYLUS_CX1500))
+ {
+ po[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE;
+ ps->val[OPT_QUALITY_CAL].b = SANE_TRUE;
+ }
+
+ po[OPT_GAMMA_BIND].name = SANE_NAME_ANALOG_GAMMA_BIND;
+ po[OPT_GAMMA_BIND].title = SANE_TITLE_ANALOG_GAMMA_BIND;
+ po[OPT_GAMMA_BIND].desc = SANE_DESC_ANALOG_GAMMA_BIND;
+ po[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ po[OPT_GAMMA_BIND].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_BIND].size = sizeof (SANE_Bool);
+ po[OPT_GAMMA_BIND].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ po[OPT_GAMMA_BIND].constraint_type = SANE_CONSTRAINT_NONE;
+ ps->val[OPT_GAMMA_BIND].b = DEFAULT_GAMMA_BIND;
+
+ po[OPT_GAMMA_GS].name = SANE_NAME_ANALOG_GAMMA;
+ po[OPT_GAMMA_GS].title = SANE_TITLE_ANALOG_GAMMA;
+ po[OPT_GAMMA_GS].desc = SANE_DESC_ANALOG_GAMMA;
+ po[OPT_GAMMA_GS].type = SANE_TYPE_FIXED;
+ po[OPT_GAMMA_GS].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_GS].size = sizeof (SANE_Word);
+ po[OPT_GAMMA_GS].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ po[OPT_GAMMA_GS].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_GS].constraint.range = &gamma_range;
+ ps->gamma_gs = DEFAULT_GAMMA;
+
+ po[OPT_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ po[OPT_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ po[OPT_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ po[OPT_GAMMA_R].type = SANE_TYPE_FIXED;
+ po[OPT_GAMMA_R].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_R].size = sizeof (SANE_Word);
+ po[OPT_GAMMA_R].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_R].constraint.range = &gamma_range;
+ ps->gamma_r = DEFAULT_GAMMA;
+
+ po[OPT_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ po[OPT_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ po[OPT_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ po[OPT_GAMMA_G].type = SANE_TYPE_FIXED;
+ po[OPT_GAMMA_G].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_G].size = sizeof (SANE_Word);
+ po[OPT_GAMMA_G].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_G].constraint.range = &gamma_range;
+ ps->gamma_g = DEFAULT_GAMMA;
+
+ po[OPT_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ po[OPT_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ po[OPT_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ po[OPT_GAMMA_B].type = SANE_TYPE_FIXED;
+ po[OPT_GAMMA_B].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_B].size = sizeof (SANE_Word);
+ po[OPT_GAMMA_B].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_B].constraint.range = &gamma_range;
+ ps->gamma_b = DEFAULT_GAMMA;
+
+ po[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ po[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ po[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ po[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ po[OPT_CUSTOM_GAMMA].unit = SANE_UNIT_NONE;
+ po[OPT_CUSTOM_GAMMA].size = sizeof (SANE_Bool);
+ po[OPT_CUSTOM_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ ps->val[OPT_CUSTOM_GAMMA].b = DEFAULT_CUSTOM_GAMMA;
+
+ po[OPT_GAMMA_VECTOR_GS].name = SANE_NAME_GAMMA_VECTOR;
+ po[OPT_GAMMA_VECTOR_GS].title = SANE_TITLE_GAMMA_VECTOR;
+ po[OPT_GAMMA_VECTOR_GS].desc = SANE_DESC_GAMMA_VECTOR;
+ po[OPT_GAMMA_VECTOR_GS].type = SANE_TYPE_INT;
+ po[OPT_GAMMA_VECTOR_GS].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_VECTOR_GS].size = ps->gamma_length * sizeof (SANE_Word);
+ po[OPT_GAMMA_VECTOR_GS].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_VECTOR_GS].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_VECTOR_GS].constraint.range = &gamma_vrange;
+ ps->val[OPT_GAMMA_VECTOR_GS].wa = ps->gamma_table_gs;
+
+ po[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ po[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ po[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ po[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ po[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_VECTOR_R].size = ps->gamma_length * sizeof (SANE_Word);
+ po[OPT_GAMMA_VECTOR_R].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_vrange;
+ ps->val[OPT_GAMMA_VECTOR_R].wa = ps->gamma_table_r;
+
+ po[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ po[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ po[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ po[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ po[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_VECTOR_G].size = ps->gamma_length * sizeof (SANE_Word);
+ po[OPT_GAMMA_VECTOR_G].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_vrange;
+ ps->val[OPT_GAMMA_VECTOR_G].wa = ps->gamma_table_g;
+
+ po[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ po[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ po[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ po[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ po[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ po[OPT_GAMMA_VECTOR_B].size = ps->gamma_length * sizeof (SANE_Word);
+ po[OPT_GAMMA_VECTOR_B].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_vrange;
+ ps->val[OPT_GAMMA_VECTOR_B].wa = ps->gamma_table_b;
+
+ po[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
+ po[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ po[OPT_HALFTONE].desc = SANE_DESC_HALFTONE;
+ po[OPT_HALFTONE].type = SANE_TYPE_BOOL;
+ po[OPT_HALFTONE].unit = SANE_UNIT_NONE;
+ po[OPT_HALFTONE].size = sizeof (SANE_Bool);
+ po[OPT_HALFTONE].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_NONE;
+ ps->halftone = DEFAULT_HALFTONE;
+
+ po[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ po[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ po[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ po[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ po[OPT_HALFTONE_PATTERN].unit = SANE_UNIT_NONE;
+ po[OPT_HALFTONE_PATTERN].size = 32;
+ po[OPT_HALFTONE_PATTERN].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ switch (ps->hconfig & HCFG_HT)
+ {
+ case HCFG_HT:
+ /* both 16x16, 8x8 matrices */
+ {
+ static SANE_String_Const names[] = {dm_dd8x8, dm_dd16x16, NULL};
+
+ po[OPT_HALFTONE_PATTERN].constraint.string_list = names;
+ ps->dither_matrix = dm_dd8x8;
+ }
+ break;
+ case HCFG_HT16:
+ /* 16x16 matrices only */
+ {
+ static SANE_String_Const names[] = {dm_dd16x16, NULL};
+
+ po[OPT_HALFTONE_PATTERN].constraint.string_list = names;
+ ps->dither_matrix = dm_dd16x16;
+ }
+ break;
+ case HCFG_HT8:
+ /* 8x8 matrices only */
+ {
+ static SANE_String_Const names[] = {dm_dd8x8, NULL};
+
+ po[OPT_HALFTONE_PATTERN].constraint.string_list = names;
+ ps->dither_matrix = dm_dd8x8;
+ }
+ break;
+ default:
+ /* no halftone matrices */
+ {
+ static SANE_String_Const names[] = {dm_none, NULL};
+
+ po[OPT_HALFTONE_PATTERN].constraint.string_list = names;
+ ps->dither_matrix = dm_none;
+ }
+ }
+
+ po[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ po[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ po[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ po[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ po[OPT_NEGATIVE].unit = SANE_UNIT_NONE;
+ po[OPT_NEGATIVE].size = sizeof (SANE_Bool);
+ po[OPT_NEGATIVE].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | SANE_CAP_AUTOMATIC;
+ po[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
+ ps->negative = DEFAULT_NEGATIVE;
+
+ po[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ po[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ po[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ po[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ po[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ po[OPT_THRESHOLD].size = sizeof (SANE_Int);
+ po[OPT_THRESHOLD].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
+ po[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_THRESHOLD].constraint.range = &positive_percent_range;
+ ps->threshold = DEFAULT_THRESHOLD;
+
+ po[OPT_FRAME_NO].name = SANE_I18N("Frame");
+ po[OPT_FRAME_NO].title = SANE_I18N("Frame to be scanned");
+ po[OPT_FRAME_NO].desc = frame_desc;
+ po[OPT_FRAME_NO].type = SANE_TYPE_INT;
+ po[OPT_FRAME_NO].unit = SANE_UNIT_NONE;
+ po[OPT_FRAME_NO].size = sizeof (SANE_Int);
+ po[OPT_FRAME_NO].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT
+ | SANE_CAP_INACTIVE;
+ po[OPT_FRAME_NO].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_FRAME_NO].constraint.range = &frame_range;
+ ps->frame_no = def_frame_no;
+
+ po[OPT_FOCUS_MODE].name = SANE_I18N("Focus-mode");
+ po[OPT_FOCUS_MODE].title = SANE_I18N("Auto or manual focus");
+ po[OPT_FOCUS_MODE].desc = focus_mode_desc;
+ po[OPT_FOCUS_MODE].type = SANE_TYPE_STRING;
+ po[OPT_FOCUS_MODE].unit = SANE_UNIT_NONE;
+ po[OPT_FOCUS_MODE].size = 16;
+ po[OPT_FOCUS_MODE].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT
+ | SANE_CAP_INACTIVE;
+ po[OPT_FOCUS_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ po[OPT_FOCUS_MODE].constraint.string_list = focus_modes;
+ ps->focus_mode_s= md_auto;
+ ps->focus_mode = MD_AUTO;
+
+ po[OPT_FOCUS_POINT].name = SANE_I18N("Focus-point");
+ po[OPT_FOCUS_POINT].title = SANE_I18N("Focus point");
+ po[OPT_FOCUS_POINT].desc = focus_desc;
+ po[OPT_FOCUS_POINT].type = SANE_TYPE_INT;
+ po[OPT_FOCUS_POINT].unit = SANE_UNIT_NONE;
+ po[OPT_FOCUS_POINT].size = sizeof (SANE_Int);
+ po[OPT_FOCUS_POINT].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT
+ | SANE_CAP_INACTIVE;
+ po[OPT_FOCUS_POINT].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_FOCUS_POINT].constraint.range = &focus_range;
+
+ po[OPT_ADVANCED_GROUP].title = SANE_I18N("Advanced");
+ po[OPT_ADVANCED_GROUP].desc = "";
+ po[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ po[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ po[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ po[OPT_RGB_LPR].name = "rgb-lpr";
+ po[OPT_RGB_LPR].title = SANE_I18N("Colour lines per read");
+ po[OPT_RGB_LPR].desc = lpr_desc;
+ po[OPT_RGB_LPR].type = SANE_TYPE_INT;
+ po[OPT_RGB_LPR].unit = SANE_UNIT_NONE;
+ po[OPT_RGB_LPR].size = sizeof (SANE_Word);
+ po[OPT_RGB_LPR].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED | SANE_CAP_AUTOMATIC;
+ po[OPT_RGB_LPR].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_RGB_LPR].constraint.range = &lpr_range;
+ ps->rgb_lpr = def_rgb_lpr;
+
+ po[OPT_GS_LPR].name = "gs-lpr";
+ po[OPT_GS_LPR].title = SANE_I18N("Greyscale lines per read");
+ po[OPT_GS_LPR].desc = lpr_desc;
+ po[OPT_GS_LPR].type = SANE_TYPE_INT;
+ po[OPT_GS_LPR].unit = SANE_UNIT_NONE;
+ po[OPT_GS_LPR].size = sizeof (SANE_Word);
+ po[OPT_GS_LPR].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT
+ | SANE_CAP_ADVANCED
+ | SANE_CAP_INACTIVE
+ | SANE_CAP_AUTOMATIC;
+ po[OPT_GS_LPR].constraint_type = SANE_CONSTRAINT_RANGE;
+ po[OPT_GS_LPR].constraint.range = &lpr_range;
+ ps->gs_lpr = def_gs_lpr;
+ control_options(ps);
+}
+
+const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle h,
+ SANE_Int n)
+{
+ DBG (DL_OPTION_TRACE,
+ "sane_snapscan_get_option_descriptor (%p, %ld)\n",
+ (void *) h,
+ (long) n);
+
+ if ((n >= 0) && (n < NUM_OPTS))
+ return ((SnapScan_Scanner *) h)->options + n;
+ return NULL;
+}
+
+/* Activates or deactivates options depending on mode */
+static void control_options(SnapScan_Scanner *pss)
+{
+ /* first deactivate all options */
+ pss->options[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_GS].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_VECTOR_GS].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ if ((pss->mode == MD_COLOUR) ||
+ ((pss->mode == MD_BILEVELCOLOUR) && (pss->hconfig & HCFG_HT) &&
+ pss->halftone))
+ {
+ pss->options[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if (pss->val[OPT_CUSTOM_GAMMA].b)
+ {
+ if (pss->val[OPT_GAMMA_BIND].b)
+ {
+ pss->options[OPT_GAMMA_VECTOR_GS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->options[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ pss->options[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ if (pss->val[OPT_GAMMA_BIND].b)
+ {
+ pss->options[OPT_GAMMA_GS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->options[OPT_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ else if ((pss->mode == MD_GREYSCALE) ||
+ ((pss->mode == MD_LINEART) && (pss->hconfig & HCFG_HT) &&
+ pss->halftone))
+ {
+ pss->options[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+
+ if (pss->val[OPT_CUSTOM_GAMMA].b)
+ {
+ pss->options[OPT_GAMMA_VECTOR_GS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->options[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GAMMA_GS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ if ((pss->mode == MD_GREYSCALE) || (pss->mode == MD_COLOUR))
+ {
+ switch(pss->pdev->model)
+ {
+ case PERFECTION2480:
+ case PERFECTION3490:
+ pss->options[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ break;
+ default:
+ break;
+ }
+ }
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ pss->options[OPT_FRAME_NO].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_FOCUS_MODE].cap &= ~SANE_CAP_INACTIVE;
+ if (pss->focus_mode == MD_MANUAL)
+ {
+ pss->options[OPT_FOCUS_POINT].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+}
+
+SANE_Status sane_control_option (SANE_Handle h,
+ SANE_Int n,
+ SANE_Action a,
+ void *v,
+ SANE_Int *i)
+{
+ static const char *me = "sane_snapscan_control_option";
+ SnapScan_Scanner *pss = h;
+ SnapScan_Device *pdev = pss->pdev;
+ static SANE_Status status;
+
+ DBG (DL_OPTION_TRACE,
+ "%s (%p, %ld, %ld, %p, %p)\n",
+ me,
+ (void *) h,
+ (long) n,
+ (long) a,
+ v,
+ (void *) i);
+
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+ /* prevent getting of inactive options */
+ if (!SANE_OPTION_IS_ACTIVE(pss->options[n].cap)) {
+ return SANE_STATUS_INVAL;
+ }
+ switch (n)
+ {
+ case OPT_COUNT:
+ *(SANE_Int *) v = NUM_OPTS;
+ break;
+ case OPT_SCANRES:
+ *(SANE_Int *) v = pss->res;
+ break;
+ case OPT_PREVIEW:
+ *(SANE_Bool *) v = pss->preview;
+ break;
+ case OPT_HIGHQUALITY:
+ *(SANE_Bool *) v = pss->highquality;
+ break;
+ case OPT_MODE:
+ DBG (DL_VERBOSE,
+ "%s: writing \"%s\" to location %p\n",
+ me,
+ pss->mode_s,
+ (SANE_String) v);
+ strcpy ((SANE_String) v, pss->mode_s);
+ break;
+ case OPT_PREVIEW_MODE:
+ DBG (DL_VERBOSE,
+ "%s: writing \"%s\" to location %p\n",
+ me,
+ pss->preview_mode_s,
+ (SANE_String) v);
+ strcpy ((SANE_String) v, pss->preview_mode_s);
+ break;
+ case OPT_SOURCE:
+ strcpy (v, pss->source_s);
+ break;
+ case OPT_TLX:
+ *(SANE_Fixed *) v = pss->tlx;
+ break;
+ case OPT_TLY:
+ *(SANE_Fixed *) v = pss->tly;
+ break;
+ case OPT_BRX:
+ *(SANE_Fixed *) v = pss->brx;
+ break;
+ case OPT_BRY:
+ *(SANE_Fixed *) v = pss->bry;
+ break;
+ case OPT_BRIGHTNESS:
+ *(SANE_Int *) v = pss->bright << SANE_FIXED_SCALE_SHIFT;
+ break;
+ case OPT_CONTRAST:
+ *(SANE_Int *) v = pss->contrast << SANE_FIXED_SCALE_SHIFT;
+ break;
+ case OPT_PREDEF_WINDOW:
+ DBG (DL_VERBOSE,
+ "%s: writing \"%s\" to location %p\n",
+ me,
+ pss->predef_window,
+ (SANE_String) v);
+ strcpy ((SANE_String) v, pss->predef_window);
+ break;
+ case OPT_GAMMA_GS:
+ *(SANE_Fixed *) v = pss->gamma_gs;
+ break;
+ case OPT_GAMMA_R:
+ *(SANE_Fixed *) v = pss->gamma_r;
+ break;
+ case OPT_GAMMA_G:
+ *(SANE_Fixed *) v = pss->gamma_g;
+ break;
+ case OPT_GAMMA_B:
+ *(SANE_Fixed *) v = pss->gamma_b;
+ break;
+ case OPT_CUSTOM_GAMMA:
+ case OPT_GAMMA_BIND:
+ case OPT_QUALITY_CAL:
+ *(SANE_Bool *) v = pss->val[n].b;
+ break;
+
+ case OPT_GAMMA_VECTOR_GS:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (v, pss->val[n].wa, pss->options[n].size);
+ break;
+ case OPT_HALFTONE:
+ *(SANE_Bool *) v = pss->halftone;
+ break;
+ case OPT_HALFTONE_PATTERN:
+ DBG (DL_VERBOSE,
+ "%s: writing \"%s\" to location %p\n",
+ me,
+ pss->dither_matrix,
+ (SANE_String) v);
+ strcpy ((SANE_String) v, pss->dither_matrix);
+ break;
+ case OPT_NEGATIVE:
+ *(SANE_Bool *) v = pss->negative;
+ break;
+ case OPT_THRESHOLD:
+ *(SANE_Int *) v = pss->threshold << SANE_FIXED_SCALE_SHIFT;
+ break;
+ case OPT_RGB_LPR:
+ *(SANE_Int *) v = pss->rgb_lpr;
+ break;
+ case OPT_GS_LPR:
+ *(SANE_Int *) v = pss->gs_lpr;
+ break;
+ case OPT_BIT_DEPTH:
+ *(SANE_Int *) v = pss->val[OPT_BIT_DEPTH].w;
+ break;
+ case OPT_FRAME_NO:
+ *(SANE_Int *) v = pss->frame_no;
+ break;
+ case OPT_FOCUS_MODE:
+ strcpy ((SANE_String) v, pss->focus_mode_s);
+ break;
+ case OPT_FOCUS_POINT:
+ *(SANE_Int *) v = pss->focus;
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: invalid option number %ld\n",
+ me,
+ (long) n);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ break;
+ case SANE_ACTION_SET_VALUE:
+ if (i)
+ *i = 0;
+ /* prevent setting of inactive options */
+ if ((!SANE_OPTION_IS_SETTABLE(pss->options[n].cap)) ||
+ (!SANE_OPTION_IS_ACTIVE(pss->options[n].cap))) {
+ return SANE_STATUS_INVAL;
+ }
+ /* prevent setting of options during a scan */
+ if ((pss->state==ST_SCAN_INIT) || (pss->state==ST_SCANNING)) {
+ DBG(DL_INFO,
+ "set value for option %s ignored: scanner is still scanning (status %d)\n",
+ pss->options[n].name,
+ pss->state
+ );
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ status = sanei_constrain_value(&pss->options[n], v, i);
+ if (status != SANE_STATUS_GOOD) {
+ return status;
+ }
+ switch (n)
+ {
+ case OPT_COUNT:
+ return SANE_STATUS_UNSUPPORTED;
+ case OPT_SCANRES:
+ pss->res = *(SANE_Int *) v;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_PREVIEW:
+ pss->preview = *(SANE_Bool *) v;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_HIGHQUALITY:
+ pss->highquality = *(SANE_Bool *) v;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_MODE:
+ {
+ char *s = (SANE_String) v;
+ if (strcmp (s, md_colour) == 0)
+ {
+ pss->mode_s = md_colour;
+ pss->mode = MD_COLOUR;
+ if (pss->preview_mode_s == md_auto)
+ pss->preview_mode = MD_COLOUR;
+ pss->options[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GS_LPR].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_RGB_LPR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (s, md_bilevelcolour) == 0)
+ {
+ int ht_cap = pss->hconfig & HCFG_HT;
+ pss->mode_s = md_bilevelcolour;
+ pss->mode = MD_BILEVELCOLOUR;
+ if (pss->preview_mode_s == md_auto)
+ pss->preview_mode = MD_BILEVELCOLOUR;
+ if (ht_cap)
+ pss->options[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ if (ht_cap && pss->halftone)
+ {
+ pss->options[OPT_HALFTONE_PATTERN].cap &=
+ ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->options[OPT_HALFTONE_PATTERN].cap |=
+ SANE_CAP_INACTIVE;
+ }
+ pss->options[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GS_LPR].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_RGB_LPR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (s, md_greyscale) == 0)
+ {
+ pss->mode_s = md_greyscale;
+ pss->mode = MD_GREYSCALE;
+ if (pss->preview_mode_s == md_auto)
+ pss->preview_mode = MD_GREYSCALE;
+ pss->options[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GS_LPR].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_RGB_LPR].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (s, md_lineart) == 0)
+ {
+ int ht_cap = pss->hconfig & HCFG_HT;
+ pss->mode_s = md_lineart;
+ pss->mode = MD_LINEART;
+ if (pss->preview_mode_s == md_auto)
+ pss->preview_mode = MD_LINEART;
+ if (ht_cap)
+ pss->options[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ if (ht_cap && pss->halftone)
+ {
+ pss->options[OPT_HALFTONE_PATTERN].cap &=
+ ~SANE_CAP_INACTIVE;
+ pss->options[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->options[OPT_HALFTONE_PATTERN].cap |=
+ SANE_CAP_INACTIVE;
+ pss->options[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ pss->options[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_GS_LPR].cap &= ~SANE_CAP_INACTIVE;
+ pss->options[OPT_RGB_LPR].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: internal error: given illegal mode "
+ "string \"%s\"\n",
+ me,
+ s);
+ }
+ }
+ control_options (pss);
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_PREVIEW_MODE:
+ {
+ char *s = (SANE_String) v;
+ if (strcmp (s, md_auto) == 0)
+ {
+ pss->preview_mode_s = md_auto;
+ pss->preview_mode = pss->mode;
+ }
+ else if (strcmp (s, md_colour) == 0)
+ {
+ pss->preview_mode_s = md_colour;
+ pss->preview_mode = MD_COLOUR;
+ }
+ else if (strcmp (s, md_bilevelcolour) == 0)
+ {
+ pss->preview_mode_s = md_bilevelcolour;
+ pss->preview_mode = MD_BILEVELCOLOUR;
+ }
+ else if (strcmp (s, md_greyscale) == 0)
+ {
+ pss->preview_mode_s = md_greyscale;
+ pss->preview_mode = MD_GREYSCALE;
+ }
+ else if (strcmp (s, md_lineart) == 0)
+ {
+ pss->preview_mode_s = md_lineart;
+ pss->preview_mode = MD_LINEART;
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: internal error: given illegal mode string "
+ "\"%s\"\n",
+ me,
+ s);
+ }
+ break;
+ }
+ case OPT_SOURCE:
+ if (strcmp(v, src_flatbed) == 0)
+ {
+ pss->source = SRC_FLATBED;
+ pss->pdev->x_range.max = x_range_fb.max;
+ pss->pdev->y_range.max = y_range_fb.max;
+ }
+ else if (strcmp(v, src_tpo) == 0)
+ {
+ pss->source = SRC_TPO;
+ pss->pdev->x_range.max = x_range_tpo.max;
+ pss->pdev->y_range.max = y_range_tpo.max;
+ }
+ else if (strcmp(v, src_adf) == 0)
+ {
+ pss->source = SRC_ADF;
+ pss->pdev->x_range.max = x_range_fb.max;
+ pss->pdev->y_range.max = y_range_fb.max;
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: internal error: given illegal source string "
+ "\"%s\"\n",
+ me,
+ (char *) v);
+ }
+ /* Adjust actual range values to new max values */
+ if (pss->brx > pss->pdev->x_range.max)
+ pss->brx = pss->pdev->x_range.max;
+ if (pss->bry > pss->pdev->y_range.max)
+ pss->bry = pss->pdev->y_range.max;
+ pss->predef_window = pdw_none;
+ if (pss->source_s)
+ free (pss->source_s);
+ pss->source_s = (SANE_Char *) strdup(v);
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_TLX:
+ pss->tlx = *(SANE_Fixed *) v;
+ pss->predef_window = pdw_none;
+ if (pss->tlx > pdev->x_range.max) {
+ pss->tlx = pdev->x_range.max;
+ }
+ if (pss->brx < pss->tlx) {
+ pss->brx = pss->tlx;
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_TLY:
+ pss->tly = *(SANE_Fixed *) v;
+ pss->predef_window = pdw_none;
+ if (pss->tly > pdev->y_range.max){
+ pss->tly = pdev->y_range.max;
+ }
+ if (pss->bry < pss->tly) {
+ pss->bry = pss->tly;
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BRX:
+ pss->brx = *(SANE_Fixed *) v;
+ pss->predef_window = pdw_none;
+ if (pss->brx < pdev->x_range.min) {
+ pss->brx = pdev->x_range.min;
+ }
+ if (pss->brx < pss->tlx) {
+ pss->tlx = pss->brx;
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BRY:
+ pss->bry = *(SANE_Fixed *) v;
+ pss->predef_window = pdw_none;
+ if (pss->bry < pdev->y_range.min) {
+ pss->bry = pdev->y_range.min;
+ }
+ if (pss->bry < pss->tly) {
+ pss->tly = pss->bry;
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BRIGHTNESS:
+ pss->bright = *(SANE_Int *) v >> SANE_FIXED_SCALE_SHIFT;
+ break;
+ case OPT_CONTRAST:
+ pss->contrast = *(SANE_Int *) v >> SANE_FIXED_SCALE_SHIFT;
+ break;
+ case OPT_PREDEF_WINDOW:
+ {
+ char *s = (SANE_String) v;
+ if (strcmp (s, pdw_none) != 0)
+ {
+ pss->tlx = 0;
+ pss->tly = 0;
+
+ if (strcmp (s, pdw_6X4) == 0)
+ {
+ pss->predef_window = pdw_6X4;
+ pss->brx = SANE_FIX (6.0*MM_PER_IN);
+ pss->bry = SANE_FIX (4.0*MM_PER_IN);
+ }
+ else if (strcmp (s, pdw_8X10) == 0)
+ {
+ pss->predef_window = pdw_8X10;
+ pss->brx = SANE_FIX (8.0*MM_PER_IN);
+ pss->bry = SANE_FIX (10.0*MM_PER_IN);
+ }
+ else if (strcmp (s, pdw_85X11) == 0)
+ {
+ pss->predef_window = pdw_85X11;
+ pss->brx = SANE_FIX (8.5*MM_PER_IN);
+ pss->bry = SANE_FIX (11.0*MM_PER_IN);
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: trying to set predef window with "
+ "garbage value.", me);
+ pss->predef_window = pdw_none;
+ pss->brx = SANE_FIX (6.0*MM_PER_IN);
+ pss->bry = SANE_FIX (4.0*MM_PER_IN);
+ }
+ }
+ else
+ {
+ pss->predef_window = pdw_none;
+ }
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_GAMMA_GS:
+ pss->gamma_gs = *(SANE_Fixed *) v;
+ break;
+ case OPT_GAMMA_R:
+ pss->gamma_r = *(SANE_Fixed *) v;
+ break;
+ case OPT_GAMMA_G:
+ pss->gamma_g = *(SANE_Fixed *) v;
+ break;
+ case OPT_GAMMA_B:
+ pss->gamma_b = *(SANE_Fixed *) v;
+ break;
+ case OPT_QUALITY_CAL:
+ pss->val[n].b = *(SANE_Bool *)v;
+ break;
+
+ case OPT_CUSTOM_GAMMA:
+ case OPT_GAMMA_BIND:
+ {
+ SANE_Bool b = *(SANE_Bool *) v;
+ if (b == pss->val[n].b) { break; }
+ pss->val[n].b = b;
+ control_options (pss);
+ if (i)
+ {
+ *i |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ }
+
+ case OPT_GAMMA_VECTOR_GS:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy(pss->val[n].wa, v, pss->options[n].size);
+ break;
+ case OPT_HALFTONE:
+ pss->halftone = *(SANE_Bool *) v;
+ if (pss->halftone)
+ {
+ switch (pss->mode)
+ {
+ case MD_BILEVELCOLOUR:
+ break;
+ case MD_LINEART:
+ pss->options[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ break;
+ default:
+ break;
+ }
+ pss->options[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->options[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ if (pss->mode == MD_LINEART)
+ pss->options[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ control_options (pss);
+ if (i)
+ *i = SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_HALFTONE_PATTERN:
+ {
+ char *s = (SANE_String) v;
+ if (strcmp (s, dm_dd8x8) == 0)
+ {
+ pss->dither_matrix = dm_dd8x8;
+ }
+ else if (strcmp (s, dm_dd16x16) == 0)
+ {
+ pss->dither_matrix = dm_dd16x16;
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: internal error: given illegal halftone pattern "
+ "string \"%s\"\n",
+ me,
+ s);
+ }
+ }
+ break;
+ case OPT_NEGATIVE:
+ pss->negative = *(SANE_Bool *) v;
+ break;
+ case OPT_THRESHOLD:
+ pss->threshold = *(SANE_Int *) v >> SANE_FIXED_SCALE_SHIFT;
+ break;
+ case OPT_RGB_LPR:
+ pss->rgb_lpr = *(SANE_Int *) v;
+ break;
+ case OPT_GS_LPR:
+ pss->gs_lpr = *(SANE_Int *) v;
+ break;
+ case OPT_BIT_DEPTH:
+ pss->val[OPT_BIT_DEPTH].w = *(SANE_Int *) v;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_FRAME_NO:
+ pss->frame_no = *(SANE_Int *) v;
+ break;
+ case OPT_FOCUS_MODE:
+ {
+ char *s = (SANE_String) v;
+ if (strcmp (s, md_manual) == 0)
+ {
+ pss->focus_mode_s = md_manual;
+ pss->focus_mode = MD_MANUAL;
+ pss->options[OPT_FOCUS_POINT].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ pss->focus_mode_s = md_auto;
+ pss->focus_mode = MD_AUTO;
+ pss->options[OPT_FOCUS_POINT].cap |= SANE_CAP_INACTIVE;
+ }
+ if (i)
+ *i = SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+ case OPT_FOCUS_POINT:
+ pss->focus = *(SANE_Int *) v;
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: invalid option number %ld\n",
+ me,
+ (long) n);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ DBG (DL_OPTION_TRACE, "%s: option %s set to value ",
+ me, pss->options[n].name);
+ switch (pss->options[n].type)
+ {
+ case SANE_TYPE_INT:
+ DBG (DL_OPTION_TRACE, "%ld\n", (long) (*(SANE_Int *) v));
+ break;
+ case SANE_TYPE_BOOL:
+ {
+ char *valstr = (*(SANE_Bool *) v == SANE_TRUE) ? "TRUE" : "FALSE";
+ DBG (DL_OPTION_TRACE, "%s\n", valstr);
+ }
+ break;
+ default:
+ DBG (DL_OPTION_TRACE, "other than an integer or boolean.\n");
+ break;
+ }
+ break;
+ case SANE_ACTION_SET_AUTO:
+ if (i)
+ *i = 0;
+ switch (n)
+ {
+ case OPT_SCANRES:
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ pss->res = 1350;
+ }
+ else
+ {
+ pss->res = 300;
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_PREVIEW:
+ pss->preview = SANE_FALSE;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_HIGHQUALITY:
+ pss->highquality = SANE_FALSE;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_MODE:
+ pss->mode_s = md_colour;
+ pss->mode = MD_COLOUR;
+ pss->options[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_GS_LPR].cap |= SANE_CAP_INACTIVE;
+ pss->options[OPT_RGB_LPR].cap &= ~SANE_CAP_INACTIVE;
+ control_options (pss);
+ if (i)
+ *i = SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_PREVIEW_MODE:
+ pss->preview_mode_s = md_greyscale;
+ pss->preview_mode = MD_GREYSCALE;
+ break;
+ case OPT_SOURCE:
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ pss->source = SRC_TPO;
+ pss->pdev->x_range.max = x_range_tpo.max;
+ pss->pdev->y_range.max = y_range_tpo.max;
+ pss->predef_window = pdw_none;
+ if (pss->source_s)
+ free (pss->source_s);
+ pss->source_s = (SANE_Char *) strdup(src_tpo);
+ }
+ else
+ {
+ pss->source = SRC_FLATBED;
+ pss->pdev->x_range.max = x_range_fb.max;
+ pss->pdev->y_range.max = y_range_fb.max;
+ pss->predef_window = pdw_none;
+ if (pss->source_s)
+ free (pss->source_s);
+ pss->source_s = (SANE_Char *) strdup(src_flatbed);
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_TLX:
+ pss->tlx = pss->pdev->x_range.min;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_TLY:
+ pss->tly = pss->pdev->y_range.min;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BRX:
+ pss->brx = pss->pdev->x_range.max;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BRY:
+ pss->bry = pss->pdev->y_range.max;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_NEGATIVE:
+ pss->negative = DEFAULT_NEGATIVE;
+ break;
+ case OPT_RGB_LPR:
+ pss->rgb_lpr = def_rgb_lpr;
+ break;
+ case OPT_GS_LPR:
+ pss->gs_lpr = def_gs_lpr;
+ break;
+ case OPT_BIT_DEPTH:
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ pss->val[OPT_BIT_DEPTH].w = 12;
+ }
+ else
+ {
+ pss->val[OPT_BIT_DEPTH].w = def_bpp;
+ }
+ break;
+ case OPT_FRAME_NO:
+ pss->frame_no = def_frame_no;
+ break;
+ case OPT_FOCUS_MODE:
+ pss->focus_mode_s = md_auto;
+ pss->focus_mode = MD_AUTO;
+ if (i)
+ *i = SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_FOCUS_POINT:
+ pss->focus = 0x13e;
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: invalid option number %ld\n",
+ me,
+ (long) n);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: invalid action code %ld\n", me, (long) a);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * $Log$
+ * Revision 1.35 2006/01/06 20:59:17 oliver-guest
+ * Some fixes for the Epson Stylus CX 1500
+ *
+ * Revision 1.34 2006/01/01 22:57:01 oliver-guest
+ * Added calibration data for Benq 5150 / 5250, preliminary support for Epson Stylus CX 1500
+ *
+ * Revision 1.33 2005/12/04 15:03:00 oliver-guest
+ * Some fixes for Benq 5150
+ *
+ * Revision 1.32 2005/11/23 20:57:01 oliver-guest
+ * Disable bilevel colour / halftoning for Epson 3490
+ *
+ * Revision 1.31 2005/11/17 23:47:10 oliver-guest
+ * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
+ *
+ * Revision 1.30 2005/11/15 20:11:18 oliver-guest
+ * Enabled quality calibration for the Epson 3490
+ *
+ * Revision 1.29 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.28 2005/10/24 19:46:40 oliver-guest
+ * Preview and range fix for Epson 2480/2580
+ *
+ * Revision 1.27 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.26 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.25 2005/09/28 22:09:26 oliver-guest
+ * Reenabled enhanced inquiry command for Epson scanners (duh\!)
+ *
+ * Revision 1.24 2005/09/28 21:33:10 oliver-guest
+ * Added 16 bit option for Epson scanners (untested)
+ *
+ * Revision 1.23 2005/08/16 20:15:10 oliver-guest
+ * Removed C++-style comment
+ *
+ * Revision 1.22 2005/08/15 18:06:37 oliver-guest
+ * Added support for Epson 3490/3590 (thanks to Matt Judge)
+ *
+ * Revision 1.21 2005/07/20 21:37:29 oliver-guest
+ * Changed TPO scanning area for 2480/2580, reenabled 2400 DPI for 2480/2580
+ *
+ * Revision 1.20 2005/05/22 11:50:24 oliver-guest
+ * Disabled 2400 DPI for Epson 2480
+ *
+ * Revision 1.19 2004/12/09 23:21:47 oliver-guest
+ * Added quality calibration for Epson 2480 (by Simon Munton)
+ *
+ * Revision 1.18 2004/12/01 22:12:02 oliver-guest
+ * Added support for Epson 1270
+ *
+ * Revision 1.17 2004/09/02 20:59:11 oliver-guest
+ * Added support for Epson 2480
+ *
+ * Revision 1.16 2004/04/08 21:53:10 oliver-guest
+ * Use sanei_thread in snapscan backend
+ *
+ * Revision 1.15 2004/04/02 20:19:23 oliver-guest
+ * Various bugfixes for gamma corretion (thanks to Robert Tsien)
+ *
+ * Revision 1.14 2004/02/01 13:32:26 oliver-guest
+ * Fixed resolutions for Epson 1670
+ *
+ * Revision 1.13 2003/11/28 23:23:18 oliver-guest
+ * Correct length of wordlist for resolutions_1600
+ *
+ * Revision 1.12 2003/11/09 21:43:45 oliver-guest
+ * Disabled quality calibration for Epson Perfection 1670
+ *
+ * Revision 1.11 2003/11/08 09:50:27 oliver-guest
+ * Fix TPO scanning range for Epson 1670
+ *
+ * Revision 1.10 2003/10/21 20:43:25 oliver-guest
+ * Bugfixes for SnapScan backend
+ *
+ * Revision 1.9 2003/10/07 18:29:20 oliver-guest
+ * Initial support for Epson 1670, minor bugfix
+ *
+ * Revision 1.8 2003/08/19 21:05:08 oliverschwartz
+ * Scanner ID cleanup
+ *
+ * Revision 1.7 2003/04/30 20:49:39 oliverschwartz
+ * SnapScan backend 1.4.26
+ *
+ * Revision 1.8 2003/04/30 20:42:18 oliverschwartz
+ * Added support for Agfa Arcus 1200 (supplied by Valtteri Vuorikoski)
+ *
+ * Revision 1.7 2003/04/02 21:17:12 oliverschwartz
+ * Fix for 1200 DPI with Acer 5000
+ *
+ * Revision 1.6 2002/07/12 23:23:06 oliverschwartz
+ * Disable quality calibration for 5300
+ *
+ * Revision 1.5 2002/06/06 20:40:00 oliverschwartz
+ * Changed default scan area for transparancy unit of SnapScan e50
+ *
+ * Revision 1.4 2002/05/02 18:28:44 oliverschwartz
+ * Added ADF support
+ *
+ * Revision 1.3 2002/04/27 14:43:59 oliverschwartz
+ * - Remove SCSI debug options
+ * - Fix option handling (errors detected by tstbackend)
+ *
+ * Revision 1.2 2002/04/23 22:50:24 oliverschwartz
+ * Improve handling of scan area options
+ *
+ * Revision 1.1 2002/03/24 12:07:15 oliverschwartz
+ * Moved option functions from snapscan.c to snapscan-options.c
+ *
+ *
+ * */
diff --git a/backend/snapscan-scsi.c b/backend/snapscan-scsi.c
new file mode 100644
index 0000000..ac0efea
--- /dev/null
+++ b/backend/snapscan-scsi.c
@@ -0,0 +1,1948 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998, 2001, 2002, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Mikael Magnusson,
+ Max Ushakov, Andrew Goodbody, Oliver Schwartz and Kevin Charter
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+
+/* $Id$
+ SnapScan backend scsi command functions */
+
+/* scanner scsi commands */
+
+static SANE_Status download_firmware(SnapScan_Scanner * pss);
+static SANE_Status wait_scanner_ready (SnapScan_Scanner * pss);
+
+#include "snapscan-usb.h"
+
+/* find model by SCSI ID string or USB vendor/product ID */
+static SnapScan_Model snapscani_get_model_id(char* model_str, int fd, SnapScan_Bus bus_type)
+{
+ static char me[] = "snapscani_get_model_id";
+ SnapScan_Model model_id = UNKNOWN;
+ SANE_Word vendor_id, product_id;
+ int i;
+
+ DBG(DL_CALL_TRACE, "%s(%s, %d, %d)\n",me, model_str, fd, bus_type);
+ for (i = 0; i < known_scanners; i++)
+ {
+ if (0 == strcasecmp (model_str, scanners[i].scsi_name))
+ {
+ model_id = scanners[i].id;
+ break;
+ }
+ }
+ /* Also test USB vendor and product ID numbers, since some USB models use
+ identical model names.
+ */
+ if ((bus_type == USB) &&
+ (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) == SANE_STATUS_GOOD))
+ {
+ DBG(DL_MINOR_INFO,
+ "%s: looking up scanner for ID 0x%04x,0x%04x.\n",
+ me, vendor_id, product_id);
+ for (i = 0; i < known_usb_scanners; i++)
+ {
+ if ((usb_scanners[i].vendor_id == vendor_id) &&
+ (usb_scanners[i].product_id == product_id))
+ {
+ model_id = usb_scanners[i].id;
+ DBG(DL_MINOR_INFO, "%s: scanner identified\n", me);
+ break;
+ }
+ }
+ }
+ return model_id;
+}
+
+/* a sensible sense handler, courtesy of Franck;
+ the last argument is expected to be a pointer to the associated
+ SnapScan_Scanner structure */
+static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ static char me[] = "sense_handler";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) arg;
+ u_char sense, asc, ascq;
+ char *sense_str = NULL, *as_str = NULL;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DL_CALL_TRACE, "%s(%ld, %p, %p)\n", me, (long) scsi_fd,
+ (void *) result, (void *) arg);
+
+ sense = result[2] & 0x0f;
+ asc = result[12];
+ ascq = result[13];
+ if (pss)
+ {
+ pss->asi1 = result[18];
+ pss->asi2 = result[19];
+ }
+ if ((result[0] & 0x80) == 0)
+ {
+ DBG (DL_DATA_TRACE, "%s: sense key is invalid.\n", me);
+ return SANE_STATUS_GOOD; /* sense key invalid */
+ } else {
+ DBG (DL_DATA_TRACE, "%s: sense key: 0x%02x, asc: 0x%02x, ascq: 0x%02x, i1: 0x%02x, i2: 0x%02x\n",
+ me, sense, asc, ascq, result[18], result[19]);
+ }
+
+ switch (sense)
+ {
+ case 0x00:
+ /* no sense */
+ sense_str = "No sense.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ break;
+ case 0x02:
+ /* not ready */
+ sense_str = "Not ready.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ if (asc == 0x04 && ascq == 0x01)
+ {
+ /* warming up; byte 18 contains remaining seconds */
+ as_str = "Logical unit is in process of becoming ready.";
+ DBG (DL_MINOR_INFO, "%s: %s (%d seconds)\n", me, as_str, result[18]);
+ status = SANE_STATUS_DEVICE_BUSY;
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ }
+ break;
+ case 0x04:
+ /* hardware error */
+ sense_str = "Hardware error.";
+ /* byte 18 and 19 detail the hardware problems */
+ DBG (DL_MINOR_INFO, "%s: %s (0x%02x, 0x%02x)\n", me, sense_str, result[18],
+ result[19]);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x05:
+ /* illegal request */
+ sense_str = "Illegal request.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ if (asc == 0x25 && ascq == 0x00)
+ as_str = "Logical unit not supported.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x09:
+ /* process error */
+ sense_str = "Process error.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ if (asc == 0x00 && ascq == 0x05)
+ {
+ /* no documents in ADF */
+ as_str = "End of data detected.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_NO_DOCS;
+ }
+ else if (asc == 0x3b && ascq == 0x05)
+ {
+ /* paper jam in ADF */
+ as_str = "Paper jam.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_JAMMED;
+ }
+ else if (asc == 0x3b && ascq == 0x09)
+ {
+ /* scanning area exceeds end of paper in ADF */
+ as_str = "Read past end of medium.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_EOF;
+ }
+ break;
+ case 0x0b:
+ /* Aborted command */
+ sense_str = "Aborted Command.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default:
+ DBG (DL_MINOR_ERROR, "%s: no handling for sense %x.\n", me, sense);
+ break;
+ }
+
+ if (pss)
+ {
+ pss->sense_str = sense_str;
+ pss->as_str = as_str;
+ }
+ return status;
+}
+
+
+static SANE_Status open_scanner (SnapScan_Scanner *pss)
+{
+ SANE_Status status;
+ DBG (DL_CALL_TRACE, "open_scanner\n");
+ if (!pss->opens)
+ {
+ if(pss->pdev->bus == SCSI)
+ {
+ status = sanei_scsi_open (pss->devname, &(pss->fd),
+ sense_handler, (void *) pss);
+ }
+ else
+ {
+ status = snapscani_usb_open (pss->devname, &(pss->fd),
+ sense_handler, (void *) pss);
+ }
+ }
+ else
+ {
+ /* already open */
+ status = SANE_STATUS_GOOD;
+ }
+ if (status == SANE_STATUS_GOOD)
+ pss->opens++;
+ return status;
+}
+
+static void close_scanner (SnapScan_Scanner *pss)
+{
+ static char me[] = "close_scanner";
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ if (pss->opens)
+ {
+ pss->opens--;
+ if (!pss->opens)
+ {
+ if(pss->pdev->bus == SCSI)
+ {
+ sanei_scsi_close (pss->fd);
+ }
+ else if(pss->pdev->bus == USB)
+ {
+ snapscani_usb_close (pss->fd);
+ }
+ } else {
+ DBG(DL_INFO, "%s: handles left: %d\n,",me, pss->opens);
+ }
+ }
+}
+
+static SANE_Status snapscan_cmd(SnapScan_Bus bus, int fd, const void *src,
+ size_t src_size, void *dst, size_t * dst_size)
+{
+ SANE_Status status;
+ DBG (DL_CALL_TRACE, "snapscan_cmd\n");
+ if(bus == USB)
+ {
+ status = snapscani_usb_cmd(fd, src, src_size, dst, dst_size);
+ }
+ else
+ {
+ status = sanei_scsi_cmd(fd, src, src_size, dst, dst_size);
+ }
+ return status;
+}
+
+/* SCSI commands */
+#define TEST_UNIT_READY 0x00
+#define INQUIRY 0x12
+#define SEND 0x2A
+#define SET_WINDOW 0x24
+#define SCAN 0x1B
+#define READ 0x28
+#define REQUEST_SENSE 0x03
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define SEND_DIAGNOSTIC 0x1D
+#define OBJECT_POSITION 0x31
+#define GET_DATA_BUFFER_STATUS 0x34
+
+#define SCAN_LEN 6
+#define READ_LEN 10
+
+/* buffer tools */
+
+static void zero_buf (u_char * buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = 0x00;
+}
+
+
+#define BYTE_SIZE 8
+
+static u_short u_char_to_u_short (u_char * pc)
+{
+ u_short r = 0;
+ r |= pc[0];
+ r = r << BYTE_SIZE;
+ r |= pc[1];
+ return r;
+}
+
+static void u_short_to_u_charp (u_short x, u_char * pc)
+{
+ pc[0] = (0xFF00 & x) >> BYTE_SIZE;
+ pc[1] = (0x00FF & x);
+}
+
+static void u_int_to_u_char3p (u_int x, u_char * pc)
+{
+ pc[0] = (0xFF0000 & x) >> 2 * BYTE_SIZE;
+ pc[1] = (0x00FF00 & x) >> BYTE_SIZE;
+ pc[2] = (0x0000FF & x);
+}
+
+static void u_int_to_u_char4p (u_int x, u_char * pc)
+{
+ pc[0] = (0xFF000000 & x) >> 3 * BYTE_SIZE;
+ pc[1] = (0x00FF0000 & x) >> 2 * BYTE_SIZE;
+ pc[2] = (0x0000FF00 & x) >> BYTE_SIZE;
+ pc[3] = (0x000000FF & x);
+}
+
+/* Convert 'STRING ' to 'STRING' by adding 0 after last non-space */
+static void remove_trailing_space (char *s)
+{
+ int position;
+
+ if (NULL == s)
+ return;
+
+ for (position = strlen (s);
+ position > 0 && ' ' == s[position - 1];
+ position--)
+ {
+ /* dummy */;
+ }
+ s[position] = 0;
+}
+
+static void check_range (int *v, SANE_Range r)
+{
+ if (*v < r.min)
+ *v = r.min;
+ if (*v > r.max)
+ *v = r.max;
+}
+
+#define INQUIRY_LEN 6
+#define INQUIRY_RET_LEN 120
+#define INQUIRY_RET_LEN_EPSON 139
+#define INQUIRY_RET_LEN_5000 138
+
+#define INQUIRY_VENDOR 8 /* Offset in reply data to vendor name */
+#define INQUIRY_PRODUCT 16 /* Offset in reply data to product id */
+#define INQUIRY_REV 32 /* Offset in reply data to revision level */
+#define INQUIRY_PRL2 36 /* Product Revision Level 2 (AGFA) */
+#define INQUIRY_HCFG 37 /* Hardware Configuration (AGFA) */
+#define INQUIRY_HWST 40 /* Hardware status */
+#define INQUIRY_HWMI 41 /* HARDWARE Model ID */
+#define INQUIRY_PIX_PER_LINE 42 /* Pixels per scan line (AGFA) */
+#define INQUIRY_BYTE_PER_LINE 44 /* Bytes per scan line (AGFA) */
+#define INQUIRY_NUM_LINES 46 /* number of scan lines (AGFA) */
+#define INQUIRY_OPT_RES 48 /* optical resolution (AGFA) */
+#define INQUIRY_SCAN_SPEED 51 /* scan speed (AGFA) */
+#define INQUIRY_EXPTIME1 52 /* exposure time, first digit (AGFA) */
+#define INQUIRY_EXPTIME2 53 /* exposure time, second digit (AGFA) */
+#define INQUIRY_G2R_DIFF 54 /* green to red difference (AGFA) */
+#define INQUIRY_B2R_DIFF 55 /* green to red difference (AGFA) */
+#define INQUIRY_FIRMWARE 96 /* firmware date and time (AGFA) */
+#define INQUIRY_BYTE_PER_LINE_MSB 132 /* ?? top byte of bytes per scan line - epson 2480 */
+#define INQUIRY_EPSON_HCFG 138 /* ?? Hardware configuration (Epson) */
+
+
+/* a mini-inquiry reads only the first 36 bytes of inquiry data, and
+ returns the vendor(7 chars) and model(16 chars); vendor and model
+ must point to character buffers of size at least 8 and 17
+ respectively */
+
+static SANE_Status mini_inquiry (SnapScan_Bus bus, int fd, char *vendor, char *model)
+{
+ static const char *me = "mini_inquiry";
+ size_t read_bytes;
+ char cmd[] = {INQUIRY, 0, 0, 0, 36, 0};
+ char data[36];
+ SANE_Status status;
+
+ read_bytes = 36;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (bus, fd, cmd, sizeof (cmd), data, &read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ memcpy (vendor, data + 8, 7);
+ vendor[7] = 0;
+ memcpy (model, data + 16, 16);
+ model[16] = 0;
+
+ remove_trailing_space (vendor);
+ remove_trailing_space (model);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* TODO: Remove */
+static char *snapscani_debug_data(char *str,const SANE_Byte *data, int len) {
+ char tmpstr[10];
+ int i;
+
+ str[0]=0;
+ for(i=0; i < (len < 20 ? len : 20); i++) {
+ sprintf(tmpstr," 0x%02x",((int)data[i]) & 0xff);
+ if(i%16 == 0 && i != 0)
+ strcat(str,"\n");
+ strcat(str,tmpstr);
+ }
+ if(i < len)
+ strcat(str," ...");
+ return str;
+}
+
+static SANE_Status inquiry (SnapScan_Scanner *pss)
+{
+ static const char *me = "inquiry";
+ char tmpstr[150]; /* TODO: Remove */
+ SANE_Status status;
+ switch (pss->pdev->model)
+ {
+ case PERFECTION2480:
+ case PERFECTION3490:
+ if (pss->firmware_loaded)
+ pss->read_bytes = INQUIRY_RET_LEN_EPSON;
+ else
+ pss->read_bytes = INQUIRY_RET_LEN;
+ break;
+ case PRISA5000:
+ case PRISA5150:
+ pss->read_bytes = INQUIRY_RET_LEN_5000;
+ break;
+ default:
+ pss->read_bytes = INQUIRY_RET_LEN;
+ break;
+ }
+
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = INQUIRY;
+ pss->cmd[4] = pss->read_bytes;
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus,
+ pss->fd,
+ pss->cmd,
+ INQUIRY_LEN,
+ pss->buf,
+ &pss->read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ /* record current parameters */
+ {
+ char exptime[4] = {' ', '.', ' ', 0};
+ exptime[0] = (char) (pss->buf[INQUIRY_EXPTIME1] + '0');
+ exptime[2] = (char) (pss->buf[INQUIRY_EXPTIME2] + '0');
+ pss->ms_per_line = atof (exptime)*(float) pss->buf[INQUIRY_SCAN_SPEED];
+ DBG (DL_DATA_TRACE, "%s: exposure time: %s ms\n", me, exptime);
+ DBG (DL_DATA_TRACE, "%s: ms per line: %f\n", me, pss->ms_per_line);
+ }
+
+ switch (pss->pdev->model)
+ {
+ case SNAPSCAN:
+ case ACER300F:
+ pss->chroma_offset[R_CHAN] =
+ pss->chroma_offset[G_CHAN] =
+ pss->chroma_offset[B_CHAN] = 0;
+ pss->chroma = 0;
+ break;
+ case PERFECTION2480:
+ case PERFECTION3490:
+ if (pss->firmware_loaded)
+ {
+ snapscani_debug_data(tmpstr, pss->buf+120, 19);
+ DBG (DL_DATA_TRACE, "%s: Epson additional inquiry data:\n%s\n", me, tmpstr);
+ pss->hconfig_epson = pss->buf[INQUIRY_EPSON_HCFG];
+ }
+ /* fall through */
+ default:
+ {
+ signed char min_diff;
+ u_char r_off, g_off, b_off;
+ signed char g = (pss->buf[INQUIRY_G2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_G2R_DIFF] & 0x7F) : pss->buf[INQUIRY_G2R_DIFF];
+ signed char b = (pss->buf[INQUIRY_B2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_B2R_DIFF] & 0x7F) : pss->buf[INQUIRY_B2R_DIFF];
+ DBG (DL_DATA_TRACE, "%s: G2R_DIFF: %d\n", me, pss->buf[INQUIRY_G2R_DIFF]);
+ DBG (DL_DATA_TRACE, "%s: B2R_DIFF: %d\n", me, pss->buf[INQUIRY_B2R_DIFF]);
+
+ min_diff = MIN (MIN (b, g), 0);
+ r_off = (u_char) (0 - min_diff);
+ g_off = (u_char) (g - min_diff);
+ b_off = (u_char) (b - min_diff);
+ pss->chroma_offset[R_CHAN] = r_off;
+ pss->chroma_offset[G_CHAN] = g_off;
+ pss->chroma_offset[B_CHAN] = b_off;
+ pss->chroma = MAX(MAX(r_off, g_off), b_off);
+ DBG (DL_DATA_TRACE,
+ "%s: Chroma offsets=%d; Red=%u, Green:=%u, Blue=%u\n",
+ me, pss->chroma,
+ pss->chroma_offset[R_CHAN],
+ pss->chroma_offset[G_CHAN],
+ pss->chroma_offset[B_CHAN]);
+ }
+ break;
+ }
+
+ pss->actual_res =
+ u_char_to_u_short (pss->buf + INQUIRY_OPT_RES);
+
+ pss->pixels_per_line =
+ u_char_to_u_short (pss->buf + INQUIRY_PIX_PER_LINE);
+ pss->bytes_per_line =
+ u_char_to_u_short (pss->buf + INQUIRY_BYTE_PER_LINE);
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
+ pss->bytes_per_line += pss->buf[INQUIRY_BYTE_PER_LINE_MSB] << 16;
+ pss->lines =
+ u_char_to_u_short (pss->buf + INQUIRY_NUM_LINES) - pss->chroma;
+ /* effective buffer size must be a whole number of scan lines */
+ if (pss->lines)
+ pss->buf_sz = (pss->phys_buf_sz/pss->bytes_per_line)*pss->bytes_per_line;
+ else
+ pss->buf_sz = 0;
+ pss->bytes_remaining = pss->bytes_per_line * (pss->lines + pss->chroma);
+ pss->expected_read_bytes = 0;
+ pss->read_bytes = 0;
+ pss->hwst = pss->buf[INQUIRY_HWST];
+ if ((pss->pdev->bus == USB) && !(pss->hwst & 0x02))
+ {
+ pss->firmware_loaded = SANE_TRUE;
+ }
+ pss->hconfig = pss->buf[INQUIRY_HCFG];
+ switch (pss->pdev->model)
+ {
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ case PRISA5150:
+ case PRISA5000:
+ pss->bpp = 14;
+ break;
+ case STYLUS_CX1500:
+ case SCANWIT2720S:
+ pss->bpp = 12;
+ break;
+ default:
+ pss->bpp = 8;
+ if (pss->hconfig & HCFG_ADC)
+ pss->bpp = 10;
+ break;
+ }
+ DBG (DL_DATA_TRACE,
+ "%s: hardware config = 0x%02x\n",
+ me,
+ pss->hconfig);
+ DBG (DL_DATA_TRACE,
+ "%s: bits per pixel = %lu\n",
+ me,
+ (u_long) pss->bpp);
+ DBG (DL_DATA_TRACE,
+ "%s: pixels per scan line = %lu\n",
+ me,
+ (u_long) pss->pixels_per_line);
+ DBG (DL_DATA_TRACE,
+ "%s: bytes per scan line = %lu\n",
+ me,
+ (u_long) pss->bytes_per_line);
+ DBG (DL_DATA_TRACE,
+ "%s: number of scan lines = %lu\n",
+ me,
+ (u_long) pss->lines);
+ DBG (DL_DATA_TRACE,
+ "%s: effective buffer size = %lu bytes, %lu lines\n",
+ me,
+ (u_long) pss->buf_sz,
+ (u_long) (pss->lines ? pss->buf_sz / pss->lines : 0));
+ DBG (DL_DATA_TRACE,
+ "%s: expected total scan data: %lu bytes\n",
+ me,
+ (u_long) pss->bytes_remaining);
+
+ return status;
+}
+
+static SANE_Status test_unit_ready (SnapScan_Scanner *pss)
+{
+ static const char *me = "test_unit_ready";
+ char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static void reserve_unit (SnapScan_Scanner *pss)
+{
+ static const char *me = "reserve_unit";
+ char cmd[] = {RESERVE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: scsi command error: %s\n",
+ me,
+ sane_strstatus (status));
+ }
+}
+
+static void release_unit (SnapScan_Scanner *pss)
+{
+ static const char *me = "release_unit";
+ char cmd[] = {RELEASE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: scsi command error: %s\n",
+ me, sane_strstatus (status));
+ }
+}
+
+#define SEND_LENGTH 10
+#define DTC_HALFTONE 0x02
+#define DTC_GAMMA 0x03
+#define DTC_GAMMA2 0x04
+#define DTC_SPEED 0x81
+#define DTC_CALIBRATION 0x82
+#define DTC_CALIBRATION_BLACK 0x89
+#define DTCQ_HALFTONE_BW8 0x00
+#define DTCQ_HALFTONE_COLOR8 0x01
+#define DTCQ_HALFTONE_BW16 0x80
+#define DTCQ_HALFTONE_COLOR16 0x81
+#define DTCQ_GAMMA_GRAY8 0x00
+#define DTCQ_GAMMA_RED8 0x01
+#define DTCQ_GAMMA_GREEN8 0x02
+#define DTCQ_GAMMA_BLUE8 0x03
+#define DTCQ_GAMMA_GRAY10 0x80
+#define DTCQ_GAMMA_RED10 0x81
+#define DTCQ_GAMMA_GREEN10 0x82
+#define DTCQ_GAMMA_BLUE10 0x83
+#define DTCQ_GAMMA_GRAY12 0x90
+#define DTCQ_GAMMA_RED12 0x91
+#define DTCQ_GAMMA_GREEN12 0x92
+#define DTCQ_GAMMA_BLUE12 0x93
+#define DTCQ_GAMMA_GRAY14 0x95 /* ? */
+#define DTCQ_GAMMA_RED14 0x96
+#define DTCQ_GAMMA_GREEN14 0x97
+#define DTCQ_GAMMA_BLUE14 0x98
+#define DTCQ_GAMMA_GRAY12_16BIT 0xa0
+#define DTCQ_GAMMA_RED12_16BIT 0xa1
+#define DTCQ_GAMMA_GREEN12_16BIT 0xa2
+#define DTCQ_GAMMA_BLUE12_16BIT 0xa3
+#define DTCQ_GAMMA_GRAY14_16BIT 0xa5 /* ? */
+#define DTCQ_GAMMA_RED14_16BIT 0xa6
+#define DTCQ_GAMMA_GREEN14_16BIT 0xa7
+#define DTCQ_GAMMA_BLUE14_16BIT 0xa8
+
+static SANE_Status send (SnapScan_Scanner *pss, u_char dtc, u_char dtcq)
+{
+ static char me[] = "send";
+ SANE_Status status;
+ u_short tl; /* transfer length */
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ zero_buf (pss->buf, SEND_LENGTH);
+
+ switch (dtc)
+ {
+ case DTC_HALFTONE: /* halftone mask */
+ switch (dtcq)
+ {
+ case DTCQ_HALFTONE_BW8:
+ tl = 64; /* bw 8x8 table */
+ break;
+ case DTCQ_HALFTONE_COLOR8:
+ tl = 3 * 64; /* rgb 8x8 tables */
+ break;
+ case DTCQ_HALFTONE_BW16:
+ tl = 256; /* bw 16x16 table */
+ break;
+ case DTCQ_HALFTONE_COLOR16:
+ tl = 3 * 256; /* rgb 16x16 tables */
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: bad halftone data type qualifier 0x%x\n",
+ me, dtcq);
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case DTC_GAMMA: /* gamma function */
+ case DTC_GAMMA2:
+ switch (dtcq)
+ {
+ case DTCQ_GAMMA_GRAY8: /* 8-bit tables */
+ case DTCQ_GAMMA_RED8:
+ case DTCQ_GAMMA_GREEN8:
+ case DTCQ_GAMMA_BLUE8:
+ tl = 256;
+ break;
+ case DTCQ_GAMMA_GRAY10: /* 10-bit tables */
+ case DTCQ_GAMMA_RED10:
+ case DTCQ_GAMMA_GREEN10:
+ case DTCQ_GAMMA_BLUE10:
+ tl = 1024;
+ break;
+ case DTCQ_GAMMA_GRAY12: /* 12-bit tables */
+ case DTCQ_GAMMA_RED12:
+ case DTCQ_GAMMA_GREEN12:
+ case DTCQ_GAMMA_BLUE12:
+ tl = 4096;
+ break;
+ case DTCQ_GAMMA_GRAY12_16BIT: /* 12-bit tables with 16 bit data */
+ case DTCQ_GAMMA_RED12_16BIT:
+ case DTCQ_GAMMA_GREEN12_16BIT:
+ case DTCQ_GAMMA_BLUE12_16BIT:
+ tl = 8192;
+ break;
+ case DTCQ_GAMMA_GRAY14: /* 14-bit tables */
+ case DTCQ_GAMMA_RED14:
+ case DTCQ_GAMMA_GREEN14:
+ case DTCQ_GAMMA_BLUE14:
+ tl = 16384;
+ break;
+ case DTCQ_GAMMA_GRAY14_16BIT: /* 14-bit tables with 16 bit data */
+ case DTCQ_GAMMA_RED14_16BIT:
+ case DTCQ_GAMMA_GREEN14_16BIT:
+ case DTCQ_GAMMA_BLUE14_16BIT:
+ tl = 32768;
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: bad gamma data type qualifier 0x%x\n",
+ me, dtcq);
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case DTC_SPEED: /* static transfer speed */
+ tl = 2;
+ break;
+ case DTC_CALIBRATION:
+ tl = calibration_line_length(pss);
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: unsupported data type code 0x%x\n",
+ me, (unsigned) dtc);
+ return SANE_STATUS_INVAL;
+ }
+
+ pss->buf[0] = SEND;
+ pss->buf[2] = dtc;
+ pss->buf[5] = dtcq;
+ pss->buf[7] = (tl >> 8) & 0xff;
+ pss->buf[8] = tl & 0xff;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
+ NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+#include "snapscan-data.c"
+static SANE_Status send_calibration_5150(SnapScan_Scanner *pss)
+{
+ static const int length = sizeof(calibration_data_5150);
+ SANE_Byte* buf = malloc (length + SEND_LENGTH);
+ SANE_Status status;
+ zero_buf (buf, SEND_LENGTH);
+ *buf = SEND;
+ *(buf + 2) = DTC_CALIBRATION;
+ *(buf + 6) = (length >> 16) & 0xff;
+ *(buf + 7) = (length >> 8) & 0xff;
+ *(buf + 8) = length & 0xff;
+ memcpy(buf + SEND_LENGTH, calibration_data_5150, length);
+ status = snapscan_cmd (
+ pss->pdev->bus, pss->fd, buf, SEND_LENGTH + length, NULL, NULL);
+ free (buf);
+ return status;
+}
+
+#define SET_WINDOW_LEN 10
+#define SET_WINDOW_HEADER 10 /* header starts */
+#define SET_WINDOW_HEADER_LEN 8
+#define SET_WINDOW_DESC 18 /* window descriptor starts */
+#define SET_WINDOW_DESC_LEN 48
+#define SET_WINDOW_TRANSFER_LEN 56
+#define SET_WINDOW_TOTAL_LEN 66
+#define SET_WINDOW_RET_LEN 0 /* no returned data */
+
+#define SET_WINDOW_P_TRANSFER_LEN 6
+#define SET_WINDOW_P_DESC_LEN 6
+
+#define SET_WINDOW_P_WIN_ID 0
+#define SET_WINDOW_P_XRES 2
+#define SET_WINDOW_P_YRES 4
+#define SET_WINDOW_P_TLX 6
+#define SET_WINDOW_P_TLY 10
+#define SET_WINDOW_P_WIDTH 14
+#define SET_WINDOW_P_LENGTH 18
+#define SET_WINDOW_P_BRIGHTNESS 22
+#define SET_WINDOW_P_THRESHOLD 23
+#define SET_WINDOW_P_CONTRAST 24
+#define SET_WINDOW_P_COMPOSITION 25
+#define SET_WINDOW_P_BITS_PER_PIX 26
+#define SET_WINDOW_P_HALFTONE_PATTERN 27
+#define SET_WINDOW_P_PADDING_TYPE 29
+#define SET_WINDOW_P_BIT_ORDERING 30
+#define SET_WINDOW_P_COMPRESSION_TYPE 32
+#define SET_WINDOW_P_COMPRESSION_ARG 33
+#define SET_WINDOW_P_HALFTONE_FLAG 35
+#define SET_WINDOW_P_DEBUG_MODE 40
+#define SET_WINDOW_P_GAMMA_NO 41
+#define SET_WINDOW_P_OPERATION_MODE 42
+#define SET_WINDOW_P_RED_UNDER_COLOR 43
+#define SET_WINDOW_P_BLUE_UNDER_COLOR 45
+#define SET_WINDOW_P_GREEN_UNDER_COLOR 44
+
+static SANE_Status prepare_set_window (SnapScan_Scanner *pss)
+{
+ static const char *me = "prepare_set_window";
+ u_char *pc;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+
+ /* basic command */
+ pc = pss->cmd;
+ pc[0] = SET_WINDOW;
+ u_int_to_u_char3p ((u_int) SET_WINDOW_TRANSFER_LEN,
+ pc + SET_WINDOW_P_TRANSFER_LEN);
+
+ /* header; we support only one window */
+ pc += SET_WINDOW_LEN;
+ u_short_to_u_charp (SET_WINDOW_DESC_LEN, pc + SET_WINDOW_P_DESC_LEN);
+
+ /* the sole window descriptor */
+ pc += SET_WINDOW_HEADER_LEN;
+ pc[SET_WINDOW_P_WIN_ID] = 0;
+ u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_XRES);
+ u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_YRES);
+ DBG (DL_CALL_TRACE, "%s Resolution: %d\n", me, pss->res);
+
+ pc[SET_WINDOW_P_BRIGHTNESS] = 128;
+ pc[SET_WINDOW_P_THRESHOLD] =
+ (u_char) (255.0*(pss->threshold / 100.0));
+ pc[SET_WINDOW_P_CONTRAST] = 128;
+
+ {
+ SnapScan_Mode mode = pss->mode;
+ pss->bpp_scan = pss->val[OPT_BIT_DEPTH].w;
+
+ if (pss->preview)
+ {
+ mode = pss->preview_mode;
+ if (pss->pdev->model != SCANWIT2720S)
+ pss->bpp_scan = 8;
+ }
+
+ DBG (DL_MINOR_INFO, "%s Mode: %d\n", me, mode);
+ switch (mode)
+ {
+ case MD_COLOUR:
+ pc[SET_WINDOW_P_COMPOSITION] = 0x05; /* multi-level RGB */
+ break;
+ case MD_BILEVELCOLOUR:
+ if (pss->halftone)
+ pc[SET_WINDOW_P_COMPOSITION] = 0x04; /* halftone RGB */
+ else
+ pc[SET_WINDOW_P_COMPOSITION] = 0x03; /* bi-level RGB */
+ pss->bpp_scan = 1;
+ break;
+ case MD_GREYSCALE:
+ pc[SET_WINDOW_P_COMPOSITION] = 0x02; /* grayscale */
+ break;
+ case MD_LINEART:
+ if (pss->halftone)
+ pc[SET_WINDOW_P_COMPOSITION] = 0x01; /* b&w halftone */
+ else
+ pc[SET_WINDOW_P_COMPOSITION] = 0x00; /* b&w (lineart) */
+ pss->bpp_scan = 1;
+ break;
+ default:
+ break;
+ }
+
+ pc[SET_WINDOW_P_BITS_PER_PIX] = pss->bpp_scan;
+ DBG (DL_INFO, "%s: bits-per-pixel set to %d\n", me, (int) pss->bpp_scan);
+ }
+ /* the RIF bit is the high bit of the padding type */
+ pc[SET_WINDOW_P_PADDING_TYPE] = 0x00 /*| (pss->negative ? 0x00 : 0x80) */ ;
+ pc[SET_WINDOW_P_HALFTONE_PATTERN] = 0;
+ pc[SET_WINDOW_P_HALFTONE_FLAG] = 0x80; /* always set; image
+ composition
+ determines whether
+ halftone is
+ actually used */
+
+ u_short_to_u_charp (0x0000, pc + SET_WINDOW_P_BIT_ORDERING); /* used? */
+ pc[SET_WINDOW_P_COMPRESSION_TYPE] = 0; /* none */
+ pc[SET_WINDOW_P_COMPRESSION_ARG] = 0; /* none applicable */
+ if(pss->pdev->model != ACER300F
+ &&
+ pss->pdev->model != SNAPSCAN310
+ &&
+ pss->pdev->model != PRISA310
+ &&
+ pss->pdev->model != PRISA610
+ ) {
+ pc[SET_WINDOW_P_DEBUG_MODE] = 2; /* use full 128k buffer */
+ if (pss->mode != MD_LINEART)
+ {
+ pc[SET_WINDOW_P_GAMMA_NO] = 0x01; /* downloaded gamma table */
+ }
+ }
+
+ pc[SET_WINDOW_P_RED_UNDER_COLOR] = 0xff; /* defaults */
+ pc[SET_WINDOW_P_BLUE_UNDER_COLOR] = 0xff;
+ pc[SET_WINDOW_P_GREEN_UNDER_COLOR] = 0xff;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status set_window (SnapScan_Scanner *pss)
+{
+ static const char *me = "set_window";
+ SANE_Status status;
+ unsigned char source;
+ u_char *pc;
+ int pos_factor;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = prepare_set_window(pss);
+ CHECK_STATUS (status, me, "prepare_set_window");
+
+ pc = pss->cmd;
+
+ /* header; we support only one window */
+ pc += SET_WINDOW_LEN;
+
+ /* the sole window descriptor */
+ pc += SET_WINDOW_HEADER_LEN;
+
+ switch (pss->pdev->model)
+ {
+ case PRISA5000:
+ case PRISA5000E:
+ case PRISA5150:
+ pos_factor = (pss->res > 600) ? 1200 : 600;
+ break;
+ case PERFECTION1270:
+ case PERFECTION1670:
+ pos_factor = (pss->res > 800) ? 1600 : 800;
+ break;
+ case PERFECTION2480:
+ pos_factor = (pss->res > 1200) ? 2400 : 1200;
+ break;
+ case PERFECTION3490:
+ pos_factor = (pss->res > 1600) ? 3200 : 1600;
+ break;
+ default:
+ pos_factor = pss->actual_res;
+ break;
+ }
+ /* it's an ugly sound if the scanner drives against the rear
+ wall, and with changing max values we better be sure */
+ check_range(&(pss->brx), pss->pdev->x_range);
+ check_range(&(pss->bry), pss->pdev->y_range);
+ {
+ int tlxp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tlx));
+ int tlyp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tly));
+ int brxp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->brx));
+ int bryp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->bry));
+
+ /* Check for brx > tlx and bry > tly */
+ if (brxp <= tlxp) {
+ tlxp = MAX (0, brxp - 75);
+ }
+ if (bryp <= tlyp) {
+ tlyp = MAX (0, bryp - 75);
+ }
+
+ u_int_to_u_char4p (tlxp, pc + SET_WINDOW_P_TLX);
+ u_int_to_u_char4p (tlyp, pc + SET_WINDOW_P_TLY);
+ u_int_to_u_char4p (MAX (((unsigned) (brxp - tlxp)), 75),
+ pc + SET_WINDOW_P_WIDTH);
+ u_int_to_u_char4p (MAX (((unsigned) (bryp - tlyp)), 75),
+ pc + SET_WINDOW_P_LENGTH);
+ DBG (DL_INFO, "%s Width: %d\n", me, brxp-tlxp);
+ DBG (DL_INFO, "%s Length: %d\n", me, bryp-tlyp);
+ }
+
+ source = 0x0;
+ if (pss->preview) {
+ source |= 0x80; /* no high quality in preview */
+ }
+ else {
+ source |= 0x40; /* no preview */
+ }
+ if (!pss->highquality) {
+ source |= 0x80; /* no high quality */
+ }
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490)) {
+ source |= 0x40; /* 2480/3490 always need no_preview bit */
+ }
+ if (pss->source == SRC_TPO) {
+ source |= 0x08;
+ }
+ if (pss->source == SRC_ADF) {
+ source |= 0x10;
+ }
+ pc[SET_WINDOW_P_OPERATION_MODE] = source;
+ DBG (DL_MINOR_INFO, "%s: operation mode set to 0x%02x\n", me, (int) source);
+
+ do {
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ SET_WINDOW_TOTAL_LEN, NULL, NULL);
+ if (status == SANE_STATUS_DEVICE_BUSY) {
+ DBG (DL_MINOR_INFO, "%s: waiting for scanner to warm up\n", me);
+ wait_scanner_ready (pss);
+ }
+ } while (status == SANE_STATUS_DEVICE_BUSY);
+
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Status set_window_autofocus (SnapScan_Scanner *copy)
+{
+ static char me[] = "set_window_autofocus";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s(%p)\n", me, (void*)copy);
+
+ copy->res = copy->actual_res;
+ status = prepare_set_window (copy);
+ CHECK_STATUS (status, me, "prepare_set_window");
+
+ u_int_to_u_char4p (1700, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_TLY);
+ /* fill in width & height */
+ u_int_to_u_char4p (2550, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_WIDTH);
+ u_int_to_u_char4p (128, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_LENGTH);
+
+ copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_BITS_PER_PIX] = 12;
+ copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_OPERATION_MODE] = 0x49; /* focusing mode */
+ return snapscan_cmd (copy->pdev->bus, copy->fd, copy->cmd,
+ SET_WINDOW_TOTAL_LEN, NULL, NULL);
+}
+
+#define SET_FRAME_LEN 10
+
+static SANE_Status set_frame (SnapScan_Scanner *pss, SANE_Byte frame_no)
+{
+ static const char *me = "set_frame";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ DBG (DL_VERBOSE, "%s setting frame to %d\n", me, frame_no);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = OBJECT_POSITION;
+ pss->cmd[1] = 2; /* Absolute position used here to select the frame */
+ pss->cmd[4] = frame_no;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ return status;
+}
+
+static SANE_Status set_focus (SnapScan_Scanner *pss, SANE_Int focus)
+{
+ static const char *me = "set_focus";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s(%d)\n", me, focus);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = OBJECT_POSITION;
+ pss->cmd[1] = 4; /* Rotate object but here it sets the focus point */
+ pss->cmd[3] = (focus >> 8) & 0xFF;
+ pss->cmd[4] = focus & 0xFF;
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Int get_8 (u_char *p)
+{
+ SANE_Int b;
+ b = p[0] | (p[1] << 8);
+ return b;
+}
+
+static double get_val (u_char *p, SANE_Int len, SANE_Int x)
+{
+ return get_8 (p + ((x + len) << 1)) / 255.0;
+}
+
+static double sum_pixel_differences (u_char *p, int len)
+{
+ double v, m, s;
+ SANE_Int i;
+
+ s = 0;
+ for (i = 0; i < len-1; i++) {
+ v = get_val (p, len, i);
+ m = get_val (p, len, i+1);
+ s += fabs (v - m);
+ }
+ return s;
+}
+
+static SANE_Status scan (SnapScan_Scanner *pss)
+{
+ static const char *me = "scan";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = SCAN;
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SCAN_LEN, NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+/* supported read operations */
+
+#define READ_IMAGE 0x00
+#define READ_TRANSTIME 0x80
+
+/* number of bytes expected must be in pss->expected_read_bytes */
+static SANE_Status scsi_read (SnapScan_Scanner *pss, u_char read_type)
+{
+ static const char *me = "scsi_read";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = READ;
+ pss->cmd[2] = read_type;
+ if (read_type == READ_TRANSTIME && pss->pdev->model == PERFECTION2480)
+ pss->cmd[5] = 1;
+
+ u_int_to_u_char3p (pss->expected_read_bytes, pss->cmd + 6);
+
+ pss->read_bytes = pss->expected_read_bytes;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ READ_LEN, pss->buf, &pss->read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Status get_focus (SnapScan_Scanner *pss)
+{
+ static const char *me = "get_focus";
+ SANE_Int focus_point, max_focus_point;
+ double sum, max;
+ SANE_Status status;
+ SnapScan_Scanner copy;
+
+ copy = *pss;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ reserve_unit(&copy);
+
+ status = set_window_autofocus (&copy);
+ CHECK_STATUS (status, me, "set_window_autofocus");
+
+ status = inquiry (&copy);
+ CHECK_STATUS (status, me, "inquiry");
+
+ status = scan (&copy);
+ CHECK_STATUS (status, me, "scan");
+
+ status = set_frame (&copy, copy.frame_no);
+ CHECK_STATUS (status, me, "set_frame");
+
+ DBG (DL_VERBOSE, "%s: Expected number of bytes for each read %d\n", me, (int)copy.bytes_per_line);
+ DBG (DL_VERBOSE, "%s: Expected number of pixels per line %d\n", me, (int)copy.pixels_per_line);
+ max_focus_point = -1;
+ max = -1;
+ for (focus_point = 0; focus_point <= 0x300; focus_point += 6) {
+ status = set_focus (&copy, focus_point);
+ CHECK_STATUS (status, me, "set_focus");
+
+ copy.expected_read_bytes = copy.bytes_per_line;
+ status = scsi_read (&copy, READ_IMAGE);
+ CHECK_STATUS (status, me, "scsi_read");
+
+ sum = sum_pixel_differences (copy.buf, copy.pixels_per_line);
+
+ if (sum > max) {
+ max = sum;
+ max_focus_point = focus_point;
+ }
+ }
+ pss->focus = max_focus_point;
+ DBG (DL_VERBOSE, "%s: Focus point found to be at 0x%x\n", me, max_focus_point);
+ release_unit (&copy);
+
+ wait_scanner_ready (&copy);
+ CHECK_STATUS (status, me, "wait_scanner_ready");
+
+ return status;
+}
+
+/*
+static SANE_Status request_sense (SnapScan_Scanner *pss)
+{
+ static const char *me = "request_sense";
+ size_t read_bytes = 0;
+ u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, 20, 0};
+ u_char data[20];
+ SANE_Status status;
+
+ read_bytes = 20;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd),
+ data, &read_bytes);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: scsi command error: %s\n",
+ me, sane_strstatus (status));
+ }
+ else
+ {
+ status = sense_handler (pss->fd, data, (void *) pss);
+ }
+ return status;
+}
+*/
+
+static SANE_Status send_diagnostic (SnapScan_Scanner *pss)
+{
+ static const char *me = "send_diagnostic";
+ u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0}; /* self-test */
+ SANE_Status status;
+
+ if (pss->pdev->model == PRISA620
+ ||
+ pss->pdev->model == PRISA610
+ ||
+ pss->pdev->model == SNAPSCAN1236
+ ||
+ pss->pdev->model == SCANWIT2720S
+ ||
+ pss->pdev->model == ARCUS1200)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Status wait_scanner_ready (SnapScan_Scanner *pss)
+{
+ static char me[] = "wait_scanner_ready";
+ SANE_Status status;
+ int retries;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ /* if the tray is returning to the start position
+ no time to wait is returned by the scanner. We'll
+ try several times and sleep 1 second between each try. */
+ for (retries = 20; retries; retries--)
+ {
+ status = test_unit_ready (pss);
+ switch (status)
+ {
+ case SANE_STATUS_GOOD:
+ return status;
+ case SANE_STATUS_DEVICE_BUSY:
+ /* first additional sense byte contains time to wait */
+ {
+ int delay = pss->asi1;
+ if (delay > 0)
+ {
+ DBG (0,
+ "Scanner warming up - waiting %d seconds.\n",
+ delay);
+ sleep (delay);
+ }
+ else
+ {
+ /* This seems to happen for Epson scanners. Return
+ SANE_STATUS_GOOD and hope the scanner accepts the
+ next command... */
+ DBG (DL_CALL_TRACE, "%s: No timeout specified, returning immediately\n", me);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ break;
+ case SANE_STATUS_IO_ERROR:
+ /* hardware error; bail */
+ DBG (DL_MAJOR_ERROR, "%s: hardware error detected.\n", me);
+ return status;
+ case SANE_STATUS_JAMMED:
+ case SANE_STATUS_NO_DOCS:
+ return status;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: unhandled request_sense result; trying again.\n",
+ me);
+ break;
+ }
+ }
+
+ return status;
+}
+
+#define READ_CALIBRATION 0x82
+#define READ_CALIBRATION_BLACK 0x89
+#define NUM_CALIBRATION_LINES 16
+#define NUM_CALIBRATION_LINES_EPSON 48
+#define NUM_CALIBRATION_LINES_EPSON_BLACK 128
+
+static SANE_Status calibrate_epson (SnapScan_Scanner *pss)
+{
+ u_char *buf, *pbuf;
+ int *bins;
+ static const char *me = "calibrate_epson";
+ /* pixels_per_line = 8.5 inch * resolution of one sensor */
+ int pixels_per_line = pss->actual_res * 17/4;
+ int num_bins = pixels_per_line;
+ int i, j, k, loop_inc, tl;
+ int r, g, b;
+ size_t expected_read_bytes;
+ size_t read_bytes;
+ SANE_Status status;
+ int pass;
+ int cal_lines = NUM_CALIBRATION_LINES_EPSON;
+ int dtc = READ_CALIBRATION;
+ int bytes_per_bin = 1;
+
+ /* in 16 bit mode we get two bytes of cal data per bin */
+ if (pss->bpp_scan == 16)
+ bytes_per_bin = 2;
+
+ /* calculate number of bins depending on mode and resolution
+ * colour mode requires bins for each of rgb
+ * full resolution doubles it because of second sensor line */
+ if (is_colour_mode(actual_mode(pss))) {
+ num_bins *= 3;
+ }
+ if (pss->res >= (SANE_Int)pss->actual_res) {
+ num_bins *= 2;
+ }
+
+ /* allocate memory for bins, all the red, then green, then blue */
+ bins = (int *) malloc (num_bins * sizeof (int));
+ if (!bins) {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating bins, %ld bytes.", me, (u_long)num_bins * sizeof (int));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* allocate buffer for receive data */
+ expected_read_bytes = pixels_per_line * 3 * 4;
+ buf = (u_char *) malloc (expected_read_bytes);
+ if (!buf) {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %ld bytes.", me, (u_long)expected_read_bytes);
+ free (bins);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ loop_inc = expected_read_bytes / (num_bins * bytes_per_bin);
+
+ /* do two passes, first pass does basic calibration, second does transparency adaptor if in use */
+ for (pass = 0; pass < 2; pass++) {
+ if (pass == 1) {
+ if (pss->source == SRC_TPO) {
+ /* pass 1 is for black level calibration of transparency adaptor */
+ cal_lines = NUM_CALIBRATION_LINES_EPSON_BLACK;
+ dtc = READ_CALIBRATION_BLACK;
+ } else
+ continue;
+ }
+
+ /* empty the bins */
+ for (i = 0; i < num_bins; i++)
+ bins[i] = 0;
+
+ for (i = 0; i < cal_lines; i += loop_inc) {
+ /* get the calibration data */
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = READ;
+ pss->cmd[2] = dtc;
+ pss->cmd[5] = cal_lines;
+ if (cal_lines - i > loop_inc)
+ expected_read_bytes = loop_inc * (num_bins * bytes_per_bin);
+ else
+ expected_read_bytes = (cal_lines - i) * (num_bins * bytes_per_bin);
+
+ u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
+ read_bytes = expected_read_bytes;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ READ_LEN, buf, &read_bytes);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "read_cal_2480", sane_strstatus(status));
+ free (bins);
+ free (buf);
+ return status;
+ }
+
+ if (read_bytes != expected_read_bytes) {
+ DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
+ free (bins);
+ free (buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* add calibration results into bins */
+ pbuf = buf;
+ for (j = 0; j < (int)expected_read_bytes / (num_bins * bytes_per_bin); j++) {
+ for (k = 0; k < num_bins; k++) {
+ bins[k] += *pbuf++;
+ /* add in second byte for 16bit mode */
+ if (bytes_per_bin == 2) {
+ bins[k] += *pbuf++ * 256;
+ }
+ }
+ }
+ }
+
+ /* now make averages */
+ for (k = 0; k < num_bins; k++) {
+ bins[k] /= cal_lines;
+ /* also divide by 64 for 16bit mode */
+ if (bytes_per_bin == 2)
+ bins[k] /= 64;
+ }
+ /* now fill up result buffer */
+ r = g = b = 0;
+
+ /* create data to send back: start with r g b values, and follow with differences
+ * to previous value */
+ pbuf = pss->buf + SEND_LENGTH;
+ if (is_colour_mode(actual_mode(pss))) {
+ for (k = 0; k < num_bins / 3; k++) {
+ *pbuf++ = bins[k] - r;
+ r = bins[k];
+ *pbuf++ = bins[k + num_bins/3] - g;
+ g = bins[k + num_bins/3];
+ *pbuf++ = bins[k + 2*num_bins/3] - b;
+ b = bins[k + 2*num_bins/3];
+ }
+ } else {
+ for (k = 0; k < num_bins; k++) {
+ *pbuf++ = bins[k] - g;
+ g = bins[k];
+ }
+ }
+
+ /* send back the calibration data; round up transfer length (to match windows driver) */
+ zero_buf (pss->buf, SEND_LENGTH);
+ pss->buf[0] = SEND;
+ pss->buf[2] = dtc;
+ tl = (num_bins + 0xff) & ~0xff;
+ u_int_to_u_char3p (tl, pss->buf + 6);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
+ NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "send_cal_2480", sane_strstatus(status));
+ free (bins);
+ free (buf);
+ return status;
+ }
+ }
+
+ free (bins);
+ free (buf);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status read_calibration_data (SnapScan_Scanner *pss, void *buf, u_char num_lines)
+{
+ static const char *me = "read_calibration_data";
+ SANE_Status status;
+ size_t expected_read_bytes = num_lines * calibration_line_length(pss);
+ size_t read_bytes;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = READ;
+ pss->cmd[2] = READ_CALIBRATION;
+ pss->cmd[5] = num_lines;
+ u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
+ read_bytes = expected_read_bytes;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ READ_LEN, buf, &read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ if(read_bytes != expected_read_bytes) {
+ DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status calibrate (SnapScan_Scanner *pss)
+{
+ int r;
+ int c;
+ u_char *buf;
+ static const char *me = "calibrate";
+ SANE_Status status;
+ int line_length = calibration_line_length(pss);
+
+ if ((pss->pdev->model == PERFECTION2480) ||
+ (pss->pdev->model == PERFECTION3490)) {
+ return calibrate_epson (pss);
+ }
+
+ if (pss->pdev->model == PRISA5150)
+ {
+ return send_calibration_5150(pss);
+ }
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ if (line_length) {
+ int num_lines = pss->phys_buf_sz / line_length;
+ if (num_lines > NUM_CALIBRATION_LINES)
+ {
+ num_lines = NUM_CALIBRATION_LINES;
+ } else if (num_lines == 0)
+ {
+ DBG(DL_MAJOR_ERROR, "%s: scsi request size underflow (< %d bytes)", me, line_length);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf = (u_char *) malloc(num_lines * line_length);
+ if (!buf)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %d bytes.", me, num_lines * line_length);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DL_MAJOR_ERROR, "%s: reading calibration data (%d lines)\n", me, num_lines);
+ status = read_calibration_data(pss, buf, num_lines);
+ CHECK_STATUS(status, me, "read_calibration_data");
+
+ for(c=0; c < line_length; c++) {
+ u_int sum = 0;
+ for(r=0; r < num_lines; r++) {
+ sum += buf[c + r * line_length];
+ }
+ pss->buf[c + SEND_LENGTH] = sum / num_lines;
+ }
+
+ status = send (pss, DTC_CALIBRATION, 1);
+ CHECK_STATUS(status, me, "send calibration");
+ free(buf);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status download_firmware(SnapScan_Scanner * pss)
+{
+ static const char *me = "download_firmware";
+ unsigned char *pFwBuf, *pCDB;
+ char* firmware = NULL;
+ FILE *fd;
+ size_t bufLength,cdbLength;
+ SANE_Status status = SANE_STATUS_GOOD;
+ char cModelName[8], cModel[255];
+ unsigned char bModelNo;
+
+ bModelNo =*(pss->buf + INQUIRY_HWMI);
+ zero_buf((unsigned char *)cModel, 255);
+ sprintf(cModelName, "%d", bModelNo);
+ DBG(DL_INFO, "Looking up %s\n", cModelName);
+ if (pss->pdev->firmware_filename) {
+ firmware = pss->pdev->firmware_filename;
+ } else if (default_firmware_filename) {
+ firmware = default_firmware_filename;
+ } else {
+ DBG (0,
+ "%s: No firmware entry found in config file %s.\n",
+ me,
+ SNAPSCAN_CONFIG_FILE
+ );
+ status = SANE_STATUS_INVAL;
+ }
+ if (status == SANE_STATUS_GOOD)
+ {
+ cdbLength = 10;
+ DBG(DL_INFO, "Downloading %s\n", firmware);
+ fd = fopen(firmware,"rb");
+ if(fd == NULL)
+ {
+ DBG (0, "Cannot open firmware file %s.\n", firmware);
+ DBG (0, "Edit the firmware file entry in %s.\n", SNAPSCAN_CONFIG_FILE);
+ status = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ switch (pss->pdev->model)
+ {
+ case PRISA610:
+ case PRISA310:
+ case PRISA620:
+ case PRISA1240:
+ case PRISA640:
+ case PRISA4300:
+ case PRISA4300_2:
+ case PRISA5000:
+ case PRISA5000E:
+ case PRISA5150:
+ case PRISA5300:
+ case STYLUS_CX1500:
+ /* ACER firmware files do not contain an info block */
+ fseek(fd, 0, SEEK_END);
+ bufLength = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+ break;
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ /* Epson firmware files contain an info block which
+ specifies the length of the firmware data. The
+ length information is stored at offset 0x64 from
+ end of file */
+ {
+ unsigned char size_l, size_h;
+ fseek(fd, -0x64, SEEK_END);
+ fread(&size_l, 1, 1, fd);
+ fread(&size_h, 1, 1, fd);
+ fseek(fd, 0, SEEK_SET);
+ bufLength = (size_h << 8) + size_l;
+ }
+ break;
+ default:
+ /* AGFA firmware files contain an info block which
+ specifies the length of the firmware data. The
+ length information is stored at offset 0x5e from
+ end of file */
+ {
+ unsigned char size_l, size_h;
+ fseek(fd, -0x5e, SEEK_END);
+ fread(&size_l, 1, 1, fd);
+ fread(&size_h, 1, 1, fd);
+ fseek(fd, 0, SEEK_SET);
+ bufLength = (size_h << 8) + size_l;
+ }
+ break;
+ }
+
+ DBG(DL_INFO, "Size of firmware: %lu\n", (u_long) bufLength);
+ pCDB = (unsigned char *)malloc(bufLength + cdbLength);
+ pFwBuf = pCDB + cdbLength;
+ zero_buf (pCDB, cdbLength);
+ (void)fread(pFwBuf,1,bufLength,fd);
+
+ *pCDB = SEND;
+ *(pCDB + 2) = 0x87;
+ *(pCDB + 6) = (bufLength >> 16) & 0xff;
+ *(pCDB + 7) = (bufLength >> 8) & 0xff;
+ *(pCDB + 8) = bufLength & 0xff;
+
+ status = snapscan_cmd (
+ pss->pdev->bus, pss->fd, pCDB, bufLength+cdbLength, NULL, NULL);
+ pss->firmware_loaded = SANE_TRUE;
+
+ free(pCDB);
+ fclose(fd);
+ }
+ }
+ return status;
+}
+
+/*
+ * $Log$
+ * Revision 1.58 2006/09/03 10:00:11 oliver-guest
+ * Bugfix for firmware download by Paul Smedley
+ *
+ * Revision 1.57 2006/03/20 18:20:10 oliver-guest
+ * Limit inquiry length to 120 bytes if firmware is not yet loaded
+ *
+ * Revision 1.56 2006/01/10 19:32:16 oliver-guest
+ * Added 12 bit gamma tables for Epson Stylus CX-1500
+ *
+ * Revision 1.55 2006/01/06 20:59:17 oliver-guest
+ * Some fixes for the Epson Stylus CX 1500
+ *
+ * Revision 1.54 2006/01/01 22:57:01 oliver-guest
+ * Added calibration data for Benq 5150 / 5250, preliminary support for Epson Stylus CX 1500
+ *
+ * Revision 1.53 2005/12/05 20:38:22 oliver-guest
+ * Small bugfix for Benq 5150
+ *
+ * Revision 1.52 2005/12/04 15:03:00 oliver-guest
+ * Some fixes for Benq 5150
+ *
+ * Revision 1.51 2005/11/26 18:53:03 oliver-guest
+ * Fix inquiry bug for Benq 5000
+ *
+ * Revision 1.50 2005/11/17 23:47:10 oliver-guest
+ * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
+ *
+ * Revision 1.49 2005/11/17 23:32:22 oliver-guest
+ * Fixes for Epson 3490 @ 2400 DPI
+ *
+ * Revision 1.48 2005/11/15 20:11:19 oliver-guest
+ * Enabled quality calibration for the Epson 3490
+ *
+ * Revision 1.47 2005/11/02 19:22:06 oliver-guest
+ * Fixes for Benq 5000
+ *
+ * Revision 1.46 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.45 2005/10/24 19:46:40 oliver-guest
+ * Preview and range fix for Epson 2480/2580
+ *
+ * Revision 1.44 2005/10/23 21:28:58 oliver-guest
+ * Fix for buffer size in high res modes, fixes for delay code
+ *
+ * Revision 1.43 2005/10/20 21:23:53 oliver-guest
+ * Fixes for 16 bit quality calibration by Simon Munton
+ *
+ * Revision 1.42 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.41 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.40 2005/09/28 22:09:26 oliver-guest
+ * Reenabled enhanced inquiry command for Epson scanners (duh\!)
+ *
+ * Revision 1.39 2005/09/28 21:33:10 oliver-guest
+ * Added 16 bit option for Epson scanners (untested)
+ *
+ * Revision 1.38 2005/09/25 08:19:12 oliver-guest
+ * Removed debugging code for Epson scanners
+ *
+ * Revision 1.37 2005/09/03 11:31:31 oliver-guest
+ * Another small bugfix
+ *
+ * Revision 1.36 2005/09/03 10:52:11 oliver-guest
+ * Fixed debugging code for epson scanners
+ *
+ * Revision 1.35 2005/08/16 17:19:20 oliver-guest
+ * Make compileable again
+ *
+ * Revision 1.34 2005/08/15 18:56:55 oliver-guest
+ * Added temporary debug code for 2480/2580 distinction
+ *
+ * Revision 1.33 2005/08/15 18:06:37 oliver-guest
+ * Added support for Epson 3490/3590 (thanks to Matt Judge)
+ *
+ * Revision 1.32 2005/07/18 17:37:37 oliver-guest
+ * ZETA changes for SnapScan backend
+ *
+ * Revision 1.31 2004/12/09 23:21:48 oliver-guest
+ * Added quality calibration for Epson 2480 (by Simon Munton)
+ *
+ * Revision 1.30 2004/12/01 22:12:03 oliver-guest
+ * Added support for Epson 1270
+ *
+ * Revision 1.29 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.28 2004/09/02 20:59:11 oliver-guest
+ * Added support for Epson 2480
+ *
+ * Revision 1.27 2004/04/02 20:19:24 oliver-guest
+ * Various bugfixes for gamma corretion (thanks to Robert Tsien)
+ *
+ * Revision 1.26 2003/11/07 23:26:49 oliver-guest
+ * Final bugfixes for bascic support of Epson 1670
+ *
+ * Revision 1.25 2003/10/21 20:43:25 oliver-guest
+ * Bugfixes for SnapScan backend
+ *
+ * Revision 1.24 2003/10/07 19:41:34 oliver-guest
+ * Updates for Epson Perfection 1670
+ *
+ * Revision 1.23 2003/08/19 21:05:08 oliverschwartz
+ * Scanner ID cleanup
+ *
+ * Revision 1.22 2003/04/30 20:49:39 oliverschwartz
+ * SnapScan backend 1.4.26
+ *
+ * Revision 1.37 2003/04/30 20:42:19 oliverschwartz
+ * Added support for Agfa Arcus 1200 (supplied by Valtteri Vuorikoski)
+ *
+ * Revision 1.36 2003/04/02 21:17:13 oliverschwartz
+ * Fix for 1200 DPI with Acer 5000
+ *
+ * Revision 1.35 2003/02/08 10:45:09 oliverschwartz
+ * Use 600 DPI as optical resolution for Benq 5000
+ *
+ * Revision 1.34 2002/12/10 20:14:12 oliverschwartz
+ * Enable color offset correction for SnapScan300
+ *
+ * Revision 1.33 2002/09/24 16:07:48 oliverschwartz
+ * Added support for Benq 5000
+ *
+ * Revision 1.32 2002/06/06 20:40:01 oliverschwartz
+ * Changed default scan area for transparancy unit of SnapScan e50
+ *
+ * Revision 1.31 2002/05/02 18:28:44 oliverschwartz
+ * Added ADF support
+ *
+ * Revision 1.30 2002/04/27 14:41:22 oliverschwartz
+ * Print number of open handles in close_scanner()
+ *
+ * Revision 1.29 2002/04/10 21:46:48 oliverschwartz
+ * Removed illegal character
+ *
+ * Revision 1.28 2002/04/10 21:01:02 oliverschwartz
+ * Disable send_diagnostic() for 1236s
+ *
+ * Revision 1.27 2002/03/24 12:11:20 oliverschwartz
+ * Get name of firmware file in sane_init
+ *
+ * Revision 1.26 2002/01/23 20:42:41 oliverschwartz
+ * Improve recognition of Acer 320U
+ * Add sense_handler code for sense code 0x0b
+ * Fix for spaces in model strings
+ *
+ * Revision 1.25 2001/12/12 19:44:59 oliverschwartz
+ * Clean up CVS log
+ *
+ * Revision 1.24 2001/12/09 23:01:00 oliverschwartz
+ * - use sense handler for USB
+ * - fix scan mode
+ *
+ * Revision 1.23 2001/12/08 11:53:31 oliverschwartz
+ * - Additional logging in sense handler
+ * - Fix wait_scanner_ready() if device reports busy
+ * - Fix scanning mode (preview/normal)
+ *
+ * Revision 1.22 2001/11/27 23:16:17 oliverschwartz
+ * - Fix color alignment for SnapScan 600
+ * - Added documentation in snapscan-sources.c
+ * - Guard against TL_X < BR_X and TL_Y < BR_Y
+ *
+ * Revision 1.21 2001/10/21 08:49:37 oliverschwartz
+ * correct number of scan lines for calibration thanks to Mikko Ty���vi
+ *
+ * Revision 1.20 2001/10/12 20:54:04 oliverschwartz
+ * enable gamma correction for Snapscan 1236, e20 and e50 scanners
+ *
+ * Revision 1.19 2001/10/11 14:02:10 oliverschwartz
+ * Distinguish between e20/e25 and e40/e50
+ *
+ * Revision 1.18 2001/10/09 22:34:23 oliverschwartz
+ * fix compiler warnings
+ *
+ * Revision 1.17 2001/10/08 19:26:01 oliverschwartz
+ * - Disable quality calibration for scanners that do not support it
+ *
+ * Revision 1.16 2001/10/08 18:22:01 oliverschwartz
+ * - Disable quality calibration for Acer Vuego 310F
+ * - Use sanei_scsi_max_request_size as scanner buffer size
+ * for SCSI devices
+ * - Added new devices to snapscan.desc
+ *
+ * Revision 1.15 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.14 2001/09/17 10:01:08 sable
+ * Added model AGFA 1236U
+ *
+ * Revision 1.13 2001/09/09 18:06:32 oliverschwartz
+ * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
+ *
+ * Revision 1.12 2001/04/10 13:00:31 sable
+ * Moving sanei_usb_* to snapscani_usb*
+ *
+ * Revision 1.11 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.10 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.9 2000/11/10 01:01:59 sable
+ * USB (kind of) autodetection
+ *
+ * Revision 1.8 2000/11/01 01:26:43 sable
+ * Support for 1212U
+ *
+ * Revision 1.7 2000/10/30 22:32:20 sable
+ * Support for vuego310s vuego610s and 1236s
+ *
+ * Revision 1.6 2000/10/29 22:44:55 sable
+ * Bug correction for 1236s
+ *
+ * Revision 1.5 2000/10/28 14:16:10 sable
+ * Bug correction for SnapScan310
+ *
+ * Revision 1.4 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.3 2000/10/15 19:52:06 cbagwell
+ * Changed USB support to a 1 line modification instead of multi-file
+ * changes.
+ *
+ * Revision 1.2 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ *
+ * */
diff --git a/backend/snapscan-sources.c b/backend/snapscan-sources.c
new file mode 100644
index 0000000..d91060e
--- /dev/null
+++ b/backend/snapscan-sources.c
@@ -0,0 +1,1385 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998, 2002, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Max Ushakov,
+ Andrew Goodbody, Oliver Schwartz and Kevin Charter
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+
+/* $Id$
+ SnapScan backend data sources (implementation) */
+
+/**************************************************************************************
+If you get confused from all the structs (like I did when I first saw them),
+think of it as "C++ in C". If you're accustomed to OO and UML maybe the
+following diagram helps you to make sense of it:
+
+ ------------------------
+ ! Source !
+ ------------------------
+ !pss: SnapScan_Scanner*!
+ ------------------------ +psub
+ !init() = 0 !-----------------
+ !remaining() = 0 ! !
+ !bytesPerLine() ! !
+ !pixelsPerLine() ! !
+ !get() = 0 ! !{TransformerSource forwards
+ !done() = 0 ! ! function calls to corres-
+ ------------------------ ! ponding functions in psub}
+ ^ !
+ /_\ !
+ ! !
+ -------------------------------------------------- /\
+ ! ! ! ! \/
+------------- ------------- ------------- -------------------
+!SCSISource ! ! FDSource ! !BufSource ! !TransformerSource!
+============= ============= ============= ===================
+!remaining()! !remaining()! !remaining()! !init() !
+!get() ! !get() ! !get() ! !remaining() !
+!done() ! !done() ! !done() ! !bytesPerLine() !
+!init() ! !init() ! !init() ! !pixelsPerLine() !
+------------- ------------- ------------- !get() !
+ !done() !
+ -------------------
+ ^
+ /_\
+ !
+ ------------------------------------
+ ! ! !
+ ---------------- ------------- -------------
+ ! Expander ! ! RGBRouter ! ! Inverter !
+ ================ ============= =============
+ !remaining() ! !remaining()! !remaining()!
+ !bytesPerLine()! !get() ! !get() !
+ !get() ! !done() ! !done() !
+ !done() ! !init() ! !init() !
+ !init() ! ------------- -------------
+ ----------------
+All instances of the descendants of TransformerSource can be chained together. For
+color scanning, a typical source chain would consist of an RGBRouter sitting on top
+of a SCSISource. In the get() method, RGBRouter will then call the get() method of
+the subsource, process the data and return it.
+
+I hope this makes sense to you (and I got the right idea of the original author's
+intention).
+***********************************************************************************/
+
+#ifndef __FUNCTION__
+#define __FUNCTION__ "(undef)"
+#endif
+
+static SANE_Status Source_init (Source *pself,
+ SnapScan_Scanner *pss,
+ SourceRemaining remaining,
+ SourceBytesPerLine bytesPerLine,
+ SourcePixelsPerLine pixelsPerLine,
+ SourceGet get,
+ SourceDone done)
+{
+ pself->pss = pss;
+ pself->remaining = remaining;
+ pself->bytesPerLine = bytesPerLine;
+ pself->pixelsPerLine = pixelsPerLine;
+ pself->get = get;
+ pself->done = done;
+ return SANE_STATUS_GOOD;
+}
+
+/* these are defaults, normally used only by base sources */
+
+static SANE_Int Source_bytesPerLine (Source *pself)
+{
+ return pself->pss->bytes_per_line;
+}
+
+static SANE_Int Source_pixelsPerLine (Source *pself)
+{
+ return pself->pss->pixels_per_line;
+}
+
+/**********************************************************************/
+
+/* the base sources */
+typedef enum
+{
+ SCSI_SRC,
+ FD_SRC,
+ BUF_SRC
+} BaseSourceType;
+
+
+typedef struct
+{
+ SOURCE_GUTS;
+ SANE_Int scsi_buf_pos; /* current position in scsi buffer */
+ SANE_Int scsi_buf_max; /* data limit */
+ SANE_Int absolute_max; /* largest possible data read */
+} SCSISource;
+
+static SANE_Int SCSISource_remaining (Source *pself)
+{
+ SCSISource *ps = (SCSISource *) pself;
+ return ps->pss->bytes_remaining + (ps->scsi_buf_max - ps->scsi_buf_pos);
+}
+
+static SANE_Status SCSISource_get (Source *pself,
+ SANE_Byte *pbuf,
+ SANE_Int *plen)
+{
+ SCSISource *ps = (SCSISource *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+ char* me = "SCSISource_get";
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ while (remaining > 0
+ && pself->remaining(pself) > 0
+ && status == SANE_STATUS_GOOD
+ && !cancelRead)
+ {
+ SANE_Int ndata = ps->scsi_buf_max - ps->scsi_buf_pos;
+ DBG (DL_DATA_TRACE, "%s: ndata %d; remaining %d\n", me, ndata, remaining);
+ if (ndata == 0)
+ {
+ ps->pss->expected_read_bytes = MIN((size_t)ps->absolute_max,
+ ps->pss->bytes_remaining);
+ ps->scsi_buf_pos = 0;
+ ps->scsi_buf_max = 0;
+ status = scsi_read (ps->pss, READ_IMAGE);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ ps->scsi_buf_max = ps->pss->read_bytes;
+ ndata = ps->pss->read_bytes;
+ ps->pss->bytes_remaining -= ps->pss->read_bytes;
+ DBG (DL_DATA_TRACE, "%s: pos: %d; max: %d; expected: %lu; read: %lu\n",
+ me, ps->scsi_buf_pos, ps->scsi_buf_max, (u_long) ps->pss->expected_read_bytes,
+ (u_long) ps->pss->read_bytes);
+ }
+ ndata = MIN(ndata, remaining);
+ memcpy (pbuf, ps->pss->buf + ps->scsi_buf_pos, (size_t)ndata);
+ pbuf += ndata;
+ ps->scsi_buf_pos += ndata;
+ remaining -= ndata;
+ }
+ *plen -= remaining;
+ return status;
+}
+
+static SANE_Status SCSISource_done (Source *pself)
+{
+ DBG(DL_MINOR_INFO, "SCSISource_done\n");
+ UNREFERENCED_PARAMETER(pself);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status SCSISource_init (SCSISource *pself, SnapScan_Scanner *pss)
+{
+ SANE_Status status = Source_init ((Source *) pself, pss,
+ SCSISource_remaining,
+ Source_bytesPerLine,
+ Source_pixelsPerLine,
+ SCSISource_get,
+ SCSISource_done);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->scsi_buf_max = 0;
+ pself->scsi_buf_pos = 0;
+ pself->absolute_max =
+ (pss->phys_buf_sz/pss->bytes_per_line)*pss->bytes_per_line;
+ }
+ return status;
+}
+
+/* File sources */
+
+typedef struct
+{
+ SOURCE_GUTS;
+ int fd;
+ SANE_Int bytes_remaining;
+} FDSource;
+
+static SANE_Int FDSource_remaining (Source *pself)
+{
+ FDSource *ps = (FDSource *) pself;
+ return ps->bytes_remaining;
+}
+
+static SANE_Status FDSource_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ FDSource *ps = (FDSource *) pself;
+ SANE_Int remaining = *plen;
+
+ while (remaining > 0
+ && pself->remaining(pself) > 0
+ && status == SANE_STATUS_GOOD)
+ {
+ SANE_Int bytes_read = read (ps->fd, pbuf, remaining);
+ if (bytes_read == -1)
+ {
+ if (errno == EAGAIN)
+ {
+ /* No data currently available */
+ break;
+ }
+ /* It's an IO error */
+ DBG (DL_MAJOR_ERROR, "%s: read failed: %s\n",
+ __FUNCTION__, strerror(errno));
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (bytes_read == 0)
+ {
+ /* EOF of current reading */
+ DBG(DL_DATA_TRACE, "%s: EOF\n",__FUNCTION__);
+ break;
+ }
+ ps->bytes_remaining -= bytes_read;
+ remaining -= bytes_read;
+ pbuf += bytes_read;
+ }
+ *plen -= remaining;
+ return status;
+}
+
+static SANE_Status FDSource_done (Source *pself)
+{
+ close(((FDSource *) pself)->fd);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status FDSource_init (FDSource *pself,
+ SnapScan_Scanner *pss,
+ int fd)
+{
+ SANE_Status status = Source_init ((Source *) pself,
+ pss,
+ FDSource_remaining,
+ Source_bytesPerLine,
+ Source_pixelsPerLine,
+ FDSource_get,
+ FDSource_done);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->fd = fd;
+ pself->bytes_remaining = pss->bytes_per_line * (pss->lines + pss->chroma);
+ }
+ return status;
+}
+
+
+/* buffer sources simply read from a pre-filled buffer; we have these
+ so that we can include source chain processing overhead in the
+ measure_transfer_rate() function */
+
+typedef struct
+{
+ SOURCE_GUTS;
+ SANE_Byte *buf;
+ SANE_Int buf_size;
+ SANE_Int buf_pos;
+} BufSource;
+
+static SANE_Int BufSource_remaining (Source *pself)
+{
+ BufSource *ps = (BufSource *) pself;
+ return ps->buf_size - ps->buf_pos;
+}
+
+static SANE_Status BufSource_get (Source *pself,
+ SANE_Byte *pbuf,
+ SANE_Int *plen)
+{
+ BufSource *ps = (BufSource *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int to_move = MIN(*plen, pself->remaining(pself));
+ if (to_move == 0)
+ {
+ status = SANE_STATUS_EOF;
+ }
+ else
+ {
+ memcpy (pbuf, ps->buf + ps->buf_pos, to_move);
+ ps->buf_pos += to_move;
+ *plen = to_move;
+ }
+ return status;
+}
+
+static SANE_Status BufSource_done (Source *pself)
+{
+ UNREFERENCED_PARAMETER(pself);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status BufSource_init (BufSource *pself,
+ SnapScan_Scanner *pss,
+ SANE_Byte *buf,
+ SANE_Int buf_size)
+{
+ SANE_Status status = Source_init ((Source *) pself,
+ pss,
+ BufSource_remaining,
+ Source_bytesPerLine,
+ Source_pixelsPerLine,
+ BufSource_get,
+ BufSource_done);
+ DBG(DL_DATA_TRACE, "BufSource_init: buf_size=%d\n", buf_size);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->buf = buf;
+ pself->buf_size = buf_size;
+ pself->buf_pos = 0;
+ }
+ return status;
+}
+
+/* base source creation */
+
+static SANE_Status create_base_source (SnapScan_Scanner *pss,
+ BaseSourceType st,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = NULL;
+ switch (st)
+ {
+ case SCSI_SRC:
+ *pps = (Source *) malloc(sizeof(SCSISource));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "failed to allocate SCSISource");
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = SCSISource_init ((SCSISource *) *pps, pss);
+ }
+ break;
+ case FD_SRC:
+ *pps = (Source *) malloc(sizeof(FDSource));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "failed to allocate FDSource");
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = FDSource_init ((FDSource *) *pps, pss, pss->rpipe[0]);
+ }
+ break;
+ case BUF_SRC:
+ *pps = (Source *) malloc(sizeof(BufSource));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "failed to allocate BufSource");
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = BufSource_init ((BufSource *) *pps,
+ pss,
+ pss->buf,
+ pss->read_bytes);
+ }
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "illegal base source type %d", st);
+ break;
+ }
+ return status;
+}
+
+/**********************************************************************/
+
+/* The transformer sources */
+
+#define TX_SOURCE_GUTS \
+ SOURCE_GUTS;\
+ Source *psub /* sub-source */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+} TxSource;
+
+static SANE_Int TxSource_remaining (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->remaining(ps->psub);
+}
+
+static SANE_Int TxSource_bytesPerLine (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->bytesPerLine(ps->psub);
+}
+
+static SANE_Int TxSource_pixelsPerLine (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->pixelsPerLine(ps->psub);
+}
+
+static SANE_Status TxSource_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->get(ps->psub, pbuf, plen);
+}
+
+static SANE_Status TxSource_done (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ SANE_Status status = ps->psub->done(ps->psub);
+ free(ps->psub);
+ ps->psub = NULL;
+ return status;
+}
+
+static SANE_Status TxSource_init (TxSource *pself,
+ SnapScan_Scanner *pss,
+ SourceRemaining remaining,
+ SourceBytesPerLine bytesPerLine,
+ SourcePixelsPerLine pixelsPerLine,
+ SourceGet get,
+ SourceDone done,
+ Source *psub)
+{
+ SANE_Status status = Source_init((Source *) pself,
+ pss,
+ remaining,
+ bytesPerLine,
+ pixelsPerLine,
+ get,
+ done);
+ if (status == SANE_STATUS_GOOD)
+ pself->psub = psub;
+ return status;
+}
+
+/* The expander makes three-channel, one-bit, raw scanner data into
+ 8-bit data. It is used to support the bilevel colour scanning mode */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+ SANE_Byte *ch_buf; /* channel buffer */
+ SANE_Int ch_size; /* channel buffer size = #bytes in a channel */
+ SANE_Int ch_ndata; /* actual #bytes in channel buffer */
+ SANE_Int ch_pos; /* position in buffer */
+ SANE_Int bit; /* current bit */
+ SANE_Int last_bit; /* current last bit (counting down) */
+ SANE_Int last_last_bit; /* last bit in the last byte of the channel */
+} Expander;
+
+static SANE_Int Expander_remaining (Source *pself)
+{
+ Expander *ps = (Expander *) pself;
+ SANE_Int sub_remaining = TxSource_remaining(pself);
+ SANE_Int sub_bits_per_channel = TxSource_pixelsPerLine(pself);
+ SANE_Int whole_channels = sub_remaining/ps->ch_size;
+ SANE_Int result = whole_channels*sub_bits_per_channel;
+
+ if (ps->ch_pos < ps->ch_size)
+ {
+ SANE_Int bits_covered = MAX((ps->ch_pos - 1)*8, 0) + 7 - ps->bit;
+ result += sub_bits_per_channel - bits_covered;
+ }
+
+ return result;
+}
+
+static SANE_Int Expander_bytesPerLine (Source *pself)
+{
+ return TxSource_pixelsPerLine(pself)*3;
+}
+
+static SANE_Status Expander_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ Expander *ps = (Expander *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+
+ while (remaining > 0
+ &&
+ pself->remaining(pself) > 0 &&
+ !cancelRead)
+ {
+ if (ps->ch_pos == ps->ch_ndata)
+ {
+ /* we need more data; try to get the remainder of the current
+ channel, or else the next channel */
+ SANE_Int ndata = ps->ch_size - ps->ch_ndata;
+ if (ndata == 0)
+ {
+ ps->ch_ndata = 0;
+ ps->ch_pos = 0;
+ ndata = ps->ch_size;
+ }
+ status = TxSource_get(pself, ps->ch_buf + ps->ch_pos, &ndata);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ if (ndata == 0)
+ break;
+ ps->ch_ndata += ndata;
+ if (ps->ch_pos == (ps->ch_size - 1))
+ ps->last_bit = ps->last_last_bit;
+ else
+ ps->last_bit = 0;
+ ps->bit = 7;
+ }
+ *pbuf = ((ps->ch_buf[ps->ch_pos] >> ps->bit) & 0x01) ? 0xFF : 0x00;
+ pbuf++;
+ remaining--;
+
+ if (ps->bit == ps->last_bit)
+ {
+ ps->bit = 7;
+ ps->ch_pos++;
+ if (ps->ch_pos == (ps->ch_size - 1))
+ ps->last_bit = ps->last_last_bit;
+ else
+ ps->last_bit = 0;
+ }
+ else
+ {
+ ps->bit--;
+ }
+ }
+
+ *plen -= remaining;
+ return status;
+}
+
+static SANE_Status Expander_done (Source *pself)
+{
+ Expander *ps = (Expander *) pself;
+ SANE_Status status = TxSource_done(pself);
+ free(ps->ch_buf);
+ ps->ch_buf = NULL;
+ ps->ch_size = 0;
+ ps->ch_pos = 0;
+ return status;
+}
+
+static SANE_Status Expander_init (Expander *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ SANE_Status status = TxSource_init((TxSource *) pself,
+ pss,
+ Expander_remaining,
+ Expander_bytesPerLine,
+ TxSource_pixelsPerLine,
+ Expander_get,
+ Expander_done,
+ psub);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->ch_size = TxSource_bytesPerLine((Source *) pself)/3;
+ pself->ch_buf = (SANE_Byte *) malloc(pself->ch_size);
+ if (pself->ch_buf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: couldn't allocate channel buffer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ pself->ch_ndata = 0;
+ pself->ch_pos = 0;
+ pself->last_last_bit = pself->pixelsPerLine((Source *) pself)%8;
+ if (pself->last_last_bit == 0)
+ pself->last_last_bit = 7;
+ pself->last_last_bit = 7 - pself->last_last_bit;
+ pself->bit = 7;
+ if (pself->ch_size > 1)
+ pself->last_bit = 0;
+ else
+ pself->last_bit = pself->last_last_bit;
+ }
+ }
+ return status;
+}
+
+static SANE_Status create_Expander (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = (Source *) malloc(sizeof(Expander));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: failed to allocate Expander.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = Expander_init ((Expander *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/*
+ This filter implements a fix for scanners that have some columns
+ of pixels offset. Currently it only shifts every other column
+ starting with the first one down ch_offset pixels.
+
+ The Deinterlacer detects if data is in SANE RGB frame format (3 bytes/pixel)
+ or in Grayscale (1 byte/pixel).
+
+ The first ch_offset lines of data in the output are fudged so that even indexed
+ add odd indexed pixels will have the same value. This is necessary because
+ the real pixel values of the columns that are shifted down are not
+ in the data for the first ch_offset lines. A better way to handle this would be to
+ scan in ch_offset extra lines of data, but I haven't figured out how to do this
+ yet.
+
+*/
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+ SANE_Byte *ch_buf; /* channel buffer */
+ SANE_Int ch_size; /* channel buffer size */
+ SANE_Int ch_line_size; /* size of one line */
+ SANE_Int ch_ndata; /* actual #bytes in channel buffer */
+ SANE_Int ch_pos; /* position in buffer */
+ SANE_Int ch_bytes_per_pixel;
+ SANE_Bool ch_lineart;
+ SANE_Int ch_offset; /* The number of lines to be shifted */
+ SANE_Bool ch_past_init; /* flag indicating if we have enough data to shift pixels down */
+ SANE_Bool ch_shift_even; /* flag indicating wether even or odd pixels are shifted */
+} Deinterlacer;
+
+static SANE_Int Deinterlacer_remaining (Source *pself)
+{
+ Deinterlacer *ps = (Deinterlacer *) pself;
+ SANE_Int result = TxSource_remaining(pself);
+ result += ps->ch_ndata - ps->ch_pos;
+ return result;
+}
+
+static SANE_Status Deinterlacer_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ Deinterlacer *ps = (Deinterlacer *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+ SANE_Int org_len = *plen;
+ char *me = "Deinterlacer_get";
+
+ DBG(DL_DATA_TRACE, "%s: remaining=%d, pself->remaining=%d, ch_ndata=%d, ch_pos=%d\n",
+ me, remaining, pself->remaining(pself), ps->ch_ndata, ps->ch_pos);
+
+ while (remaining > 0
+ &&
+ pself->remaining(pself) > 0 &&
+ !cancelRead)
+ {
+ if (ps->ch_pos % (ps->ch_line_size) == ps->ch_ndata % (ps->ch_line_size) )
+ {
+ /* we need more data; try to get the remainder of the current
+ line, or else the next line */
+ SANE_Int ndata = (ps->ch_line_size) - ps->ch_ndata % (ps->ch_line_size);
+ if (ps->ch_pos >= ps->ch_size)
+ {
+ /* wrap to the beginning of the buffer if we need to */
+ ps->ch_ndata = 0;
+ ps->ch_pos = 0;
+ ndata = ps->ch_line_size;
+ }
+ status = TxSource_get(pself, ps->ch_buf + ps->ch_pos, &ndata);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ if (ndata == 0)
+ break;
+ ps->ch_ndata += ndata;
+ }
+ /* Handle special lineart mode: Valid pixels need to be masked */
+ if (ps->ch_lineart)
+ {
+ if (ps->ch_past_init)
+ {
+ if (ps->ch_shift_even)
+ {
+ /* Even columns need to be shifted, i.e. bits 1,3,5,7 -> 0xaa */
+ /* use valid pixels from this line and shifted pixels from ch_size lines back */
+ *pbuf = (ps->ch_buf[ps->ch_pos] & 0x55) |
+ (ps->ch_buf[(ps->ch_pos + (ps->ch_line_size)) % ps->ch_size] & 0xaa);
+ }
+ else
+ {
+ /* Odd columns need to be shifted, i.e. bits 0,2,4,6 -> 0x55 */
+ *pbuf = (ps->ch_buf[ps->ch_pos] & 0xaa) |
+ (ps->ch_buf[(ps->ch_pos + (ps->ch_line_size)) % ps->ch_size] & 0x55);
+ }
+ }
+ else
+ {
+ /* not enough data. duplicate pixel values from previous column */
+ if (ps->ch_shift_even)
+ {
+ /* bits 0,2,4,6 contain valid data -> 0x55 */
+ SANE_Byte valid_pixel = ps->ch_buf[ps->ch_pos] & 0x55;
+ *pbuf = valid_pixel | (valid_pixel >> 1);
+ }
+ else
+ {
+
+ /* bits 1,3,5,7 contain valid data -> 0xaa */
+ SANE_Byte valid_pixel = ps->ch_buf[ps->ch_pos] & 0xaa;
+ *pbuf = valid_pixel | (valid_pixel << 1);
+ }
+ }
+ }
+ else /* colour / grayscale mode */
+ {
+ if ((ps->ch_shift_even && ((ps->ch_pos/ps->ch_bytes_per_pixel) % 2 == 0)) ||
+ (!ps->ch_shift_even && ((ps->ch_pos/ps->ch_bytes_per_pixel) % 2 == 1)))
+ {
+ /* the even indexed pixels need to be shifted down */
+ if (ps->ch_past_init){
+ /* We need to use data 4 lines back */
+ /* So we just go one forward and it will wrap around to 4 back. */
+ *pbuf = ps->ch_buf[(ps->ch_pos + (ps->ch_line_size)) % ps->ch_size];
+ }else{
+ /* Use data from the next pixel for even indexed pixels
+ if we are on the first few lines.
+ TODO: also we will overread the buffer if the buffer read ended
+ on the first pixel. */
+ if (ps->ch_pos % (ps->ch_line_size) == 0 )
+ *pbuf = ps->ch_buf[ps->ch_pos+ps->ch_bytes_per_pixel];
+ else
+ *pbuf = ps->ch_buf[ps->ch_pos-ps->ch_bytes_per_pixel];
+ }
+ }else{
+ /* odd indexed pixels are okay */
+ *pbuf = ps->ch_buf[ps->ch_pos];
+ }
+ }
+ /* set the flag so we know we have enough data to start shifting columns */
+ if (ps->ch_pos >= ps->ch_line_size * ps->ch_offset)
+ ps->ch_past_init = SANE_TRUE;
+
+ pbuf++;
+ remaining--;
+ ps->ch_pos++;
+ }
+
+ *plen -= remaining;
+
+ DBG(DL_DATA_TRACE,
+ "%s: Request=%d, remaining=%d, read=%d, TXSource_rem=%d, bytes_rem=%lu\n",
+ me,
+ org_len,
+ pself->remaining(pself),
+ *plen,
+ TxSource_remaining(pself),
+ (u_long) ps->pss->bytes_remaining);
+ return status;
+}
+
+static SANE_Status Deinterlacer_done (Source *pself)
+{
+ Deinterlacer *ps = (Deinterlacer *) pself;
+ SANE_Status status = TxSource_done(pself);
+ free(ps->ch_buf);
+ ps->ch_buf = NULL;
+ ps->ch_size = 0;
+ ps->ch_line_size = 0;
+ ps->ch_pos = 0;
+ return status;
+}
+
+static SANE_Status Deinterlacer_init (Deinterlacer *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ SANE_Status status = TxSource_init((TxSource *) pself,
+ pss,
+ Deinterlacer_remaining,
+ TxSource_bytesPerLine,
+ TxSource_pixelsPerLine,
+ Deinterlacer_get,
+ Deinterlacer_done,
+ psub);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->ch_shift_even = SANE_TRUE;
+ switch (pss->pdev->model)
+ {
+ case PERFECTION3490:
+ pself->ch_offset = 8;
+ if ((actual_mode(pss) == MD_GREYSCALE) || (actual_mode(pss) == MD_LINEART))
+ pself->ch_shift_even = SANE_FALSE;
+ break;
+ case PERFECTION2480:
+ default:
+ pself->ch_offset = 4;
+ break;
+ }
+ pself->ch_line_size = TxSource_bytesPerLine((Source *) pself);
+ /* We need at least ch_offset+1 lines of buffer in order
+ to shift up ch_offset pixels. */
+ pself->ch_size = pself->ch_line_size * (pself->ch_offset + 1);
+ pself->ch_buf = (SANE_Byte *) malloc(pself->ch_size);
+ if (pself->ch_buf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: couldn't allocate channel buffer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ pself->ch_ndata = 0;
+ pself->ch_pos = 0;
+ pself->ch_past_init = SANE_FALSE;
+ if ((actual_mode(pss) == MD_GREYSCALE) || (actual_mode(pss) == MD_LINEART))
+ pself->ch_bytes_per_pixel = 1;
+ else
+ pself->ch_bytes_per_pixel = 3;
+ if (pss->bpp_scan == 16)
+ pself->ch_bytes_per_pixel *= 2;
+ }
+ pself->ch_lineart = (actual_mode(pss) == MD_LINEART);
+ }
+ return status;
+}
+
+static SANE_Status create_Deinterlacer (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = (Source *) malloc(sizeof(Deinterlacer));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: failed to allocate Deinterlacer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = Deinterlacer_init ((Deinterlacer *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/* ----------------------------------------------------- */
+
+/* the RGB router assumes 8-bit RGB data arranged in contiguous
+ channels, possibly with R-G and R-B offsets, and rearranges the
+ data into SANE RGB frame format */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+ SANE_Byte *cbuf; /* circular line buffer */
+ SANE_Byte *xbuf; /* single line buffer */
+ SANE_Int pos; /* current position in xbuf */
+ SANE_Int cb_size; /* size of the circular buffer */
+ SANE_Int cb_line_size;/* size of a line in the circular buffer */
+ SANE_Int cb_start; /* start of valid data in the circular buffer */
+ SANE_Int cb_finish; /* finish of valid data, for next read */
+ SANE_Int ch_offset[3];/* offset in cbuf */
+ SANE_Int round_req;
+ SANE_Int round_read;
+} RGBRouter;
+
+static void put_int16r (int n, u_char *p)
+{
+ p[0] = (n & 0x00ff);
+ p[1] = (n & 0xff00) >> 8;
+}
+
+
+static SANE_Int RGBRouter_remaining (Source *pself)
+{
+ RGBRouter *ps = (RGBRouter *) pself;
+ SANE_Int remaining;
+ if (ps->round_req == ps->cb_size)
+ remaining = TxSource_remaining(pself) - ps->cb_size + ps->cb_line_size;
+ else
+ remaining = TxSource_remaining(pself) + ps->cb_line_size - ps->pos;
+ return (remaining);
+}
+
+static SANE_Status RGBRouter_get (Source *pself,
+ SANE_Byte *pbuf,
+ SANE_Int *plen)
+{
+ RGBRouter *ps = (RGBRouter *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+ SANE_Byte *s;
+ SANE_Int i, t;
+ SANE_Int r, g, b;
+ SANE_Int run_req;
+ SANE_Int org_len = *plen;
+ char *me = "RGBRouter_get";
+
+ while (remaining > 0 && pself->remaining(pself) > 0 && !cancelRead)
+ {
+ DBG(DL_DATA_TRACE, "%s: remaining=%d, pself->remaining=%d, round_req=%d, cb_size=%d\n",
+ me, remaining, pself->remaining(pself), ps->round_req, ps->cb_size);
+ /* Check if there is no valid data left from previous get */
+ if (ps->pos >= ps->cb_line_size)
+ {
+ /* Try to get more data. either one line or
+ full buffer (first time) */
+ do
+ {
+ run_req = ps->round_req - ps->round_read;
+ status = TxSource_get (pself,
+ ps->cbuf + ps->cb_start + ps->round_read,
+ &run_req);
+ if (status != SANE_STATUS_GOOD || run_req==0)
+ {
+ *plen -= remaining;
+ if ( *plen > 0 )
+ DBG(DL_DATA_TRACE, "%s: request=%d, read=%d\n",
+ me, org_len, *plen);
+ return status;
+ }
+ ps->round_read += run_req;
+ }
+ while ((ps->round_req > ps->round_read) && !cancelRead);
+
+ /* route RGB */
+ ps->cb_start = (ps->cb_start + ps->round_read)%ps->cb_size;
+ s = ps->xbuf;
+ r = (ps->cb_start + ps->ch_offset[0])%ps->cb_size;
+ g = (ps->cb_start + ps->ch_offset[1])%ps->cb_size;
+ b = (ps->cb_start + ps->ch_offset[2])%ps->cb_size;
+ for (i = 0; i < ps->cb_line_size/3; i++)
+ {
+ if (pself->pss->bpp_scan == 8)
+ {
+ *s++ = ps->cbuf[r++];
+ *s++ = ps->cbuf[g++];
+ *s++ = ps->cbuf[b++];
+ }
+ else if (pself->pss->pdev->model == SCANWIT2720S)
+ {
+ t = (((ps->cbuf[r+1] << 8) | ps->cbuf[r]) & 0xfff) << 4;
+ put_int16r (t, s);
+ s += 2;
+ r += 2;
+ t = (((ps->cbuf[g+1] << 8) | ps->cbuf[g]) & 0xfff) << 4;
+ put_int16r (t, s);
+ s += 2;
+ g += 2;
+ t = (((ps->cbuf[b+1] << 8) | ps->cbuf[b]) & 0xfff) << 4;
+ put_int16r (t, s);
+ s += 2;
+ b += 2;
+ i++;
+ }
+ else
+ {
+ *s++ = ps->cbuf[r++];
+ *s++ = ps->cbuf[r++];
+ *s++ = ps->cbuf[g++];
+ *s++ = ps->cbuf[g++];
+ *s++ = ps->cbuf[b++];
+ *s++ = ps->cbuf[b++];
+ i++;
+ }
+ }
+
+ /* end of reading & offsetiing whole line data;
+ reset valid position */
+ ps->pos = 0;
+
+ /* prepare for next round */
+ ps->round_req = ps->cb_line_size;
+ ps->round_read =0;
+ }
+
+ /* Repack the whole scan line and copy to caller's buffer */
+ while (remaining > 0 && ps->pos < ps->cb_line_size)
+ {
+ *pbuf++ = ps->xbuf[ps->pos++];
+ remaining--;
+ }
+ }
+ *plen -= remaining;
+ DBG(DL_DATA_TRACE,
+ "%s: Request=%d, remaining=%d, read=%d, TXSource_rem=%d, bytes_rem=%lu\n",
+ me,
+ org_len,
+ pself->remaining(pself),
+ *plen,
+ TxSource_remaining(pself),
+ (u_long) ps->pss->bytes_remaining);
+ return status;
+}
+
+static SANE_Status RGBRouter_done (Source *pself)
+{
+ RGBRouter *ps = (RGBRouter *) pself;
+ SANE_Status status = TxSource_done(pself);
+
+ free(ps->cbuf);
+ free(ps->xbuf);
+ ps->cbuf = NULL;
+ ps->cb_start = -1;
+ ps->pos = 0;
+ return status;
+}
+
+static SANE_Status RGBRouter_init (RGBRouter *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ SANE_Status status = TxSource_init((TxSource *) pself,
+ pss,
+ RGBRouter_remaining,
+ TxSource_bytesPerLine,
+ TxSource_pixelsPerLine,
+ RGBRouter_get,
+ RGBRouter_done,
+ psub);
+ if (status == SANE_STATUS_GOOD)
+ {
+ SANE_Int lines_in_buffer = 0;
+
+ /* Size the buffer to accomodate the necessary number of scan
+ lines to cater for the offset between R, G and B */
+ lines_in_buffer = pss->chroma + 1;
+ pself->cb_line_size = pself->bytesPerLine((Source *) pself);
+ pself->cb_size = pself->cb_line_size*lines_in_buffer;
+ pself->pos = pself->cb_line_size;
+
+ pself->round_req = pself->cb_size;
+ pself->round_read = 0;
+
+ pself->cbuf = (SANE_Byte *) malloc(pself->cb_size);
+ pself->xbuf = (SANE_Byte *) malloc(pself->cb_line_size);
+ if (pself->cbuf == NULL || pself->xbuf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: failed to allocate circular buffer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ SANE_Int ch;
+
+ pself->cb_start = 0;
+ for (ch = 0; ch < 3; ch++)
+ {
+ pself->ch_offset[ch] =
+ pss->chroma_offset[ch] * pself->cb_line_size
+ + ch * (pself->cb_line_size / 3);
+ }
+ }
+ DBG(DL_MINOR_INFO, "RGBRouter_init: buf_size: %d x %d = %d\n",
+ pself->cb_line_size, lines_in_buffer, pself->cb_size);
+ DBG(DL_MINOR_INFO, "RGBRouter_init: buf offset R:%d G:%d B:%d\n",
+ pself->ch_offset[0], pself->ch_offset[1],pself->ch_offset[2]);
+ }
+ return status;
+}
+
+static SANE_Status create_RGBRouter (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ static char me[] = "create_RGBRouter";
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ *pps = (Source *) malloc(sizeof(RGBRouter));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: failed to allocate RGBRouter.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = RGBRouter_init ((RGBRouter *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/* An Inverter is used to invert the bits in a lineart image */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+} Inverter;
+
+static SANE_Status Inverter_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ SANE_Status status = TxSource_get (pself, pbuf, plen);
+ if (status == SANE_STATUS_GOOD)
+ {
+ int i;
+ for (i = 0; i < *plen; i++)
+ pbuf[i] ^= 0xFF;
+ }
+ return status;
+}
+
+static SANE_Status Inverter_init (Inverter *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ return TxSource_init ((TxSource *) pself,
+ pss,
+ TxSource_remaining,
+ TxSource_bytesPerLine,
+ TxSource_pixelsPerLine,
+ Inverter_get,
+ TxSource_done,
+ psub);
+}
+
+static SANE_Status create_Inverter (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = (Source *) malloc(sizeof(Inverter));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: failed to allocate Inverter.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = Inverter_init ((Inverter *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/* Source chain creation */
+
+static SANE_Status create_source_chain (SnapScan_Scanner *pss,
+ BaseSourceType bst,
+ Source **pps)
+{
+ static char me[] = "create_source_chain";
+ SANE_Status status = create_base_source (pss, bst, pps);
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ if (status == SANE_STATUS_GOOD)
+ {
+ SnapScan_Mode mode = actual_mode(pss);
+ switch (mode)
+ {
+ case MD_COLOUR:
+ status = create_RGBRouter (pss, *pps, pps);
+ /* We only have the interlace probelms on
+ some scanners like the Epson Perfection 2480/2580
+ at 2400 dpi. */
+ if (status == SANE_STATUS_GOOD &&
+ ((pss->pdev->model == PERFECTION2480 && pss->res == 2400) ||
+ (pss->pdev->model == PERFECTION3490 && pss->res == 3200) ||
+ (pss->pdev->model == PRISA5000E && pss->res == 1200)))
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ case MD_BILEVELCOLOUR:
+ status = create_Expander (pss, *pps, pps);
+ if (status == SANE_STATUS_GOOD)
+ status = create_RGBRouter (pss, *pps, pps);
+ if (status == SANE_STATUS_GOOD &&
+ ((pss->pdev->model == PERFECTION2480 && pss->res == 2400) ||
+ (pss->pdev->model == PERFECTION3490 && pss->res == 3200) ||
+ (pss->pdev->model == PRISA5000E && pss->res == 1200)))
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ case MD_GREYSCALE:
+ if ((pss->pdev->model == PERFECTION2480 && pss->res == 2400) ||
+ (pss->pdev->model == PERFECTION3490 && pss->res == 3200) ||
+ (pss->pdev->model == PRISA5000E && pss->res == 1200))
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ case MD_LINEART:
+ /* The SnapScan creates a negative image by
+ default... so for the user interface to make sense,
+ the internal meaning of "negative" is reversed */
+ if (pss->negative == SANE_FALSE)
+ status = create_Inverter (pss, *pps, pps);
+ if (pss->pdev->model == PERFECTION3490 && pss->res == 3200)
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: bad mode value %d (internal error)\n",
+ __FUNCTION__, mode);
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+ }
+ return status;
+}
+
+/*
+ * $Log$
+ * Revision 1.21 2005/12/02 19:12:54 oliver-guest
+ * Another fix for lineart mode for the Epson 3490 @ 3200 DPI
+ *
+ * Revision 1.20 2005/11/28 19:28:29 oliver-guest
+ * Fix for lineart mode of Epson 3490 @ 3200 DPI
+ *
+ * Revision 1.19 2005/11/25 17:24:48 oliver-guest
+ * Fix for Epson 3490 @ 3200 DPI for grayscale and lineart mode
+ *
+ * Revision 1.18 2005/11/17 23:47:11 oliver-guest
+ * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
+ *
+ * Revision 1.17 2005/11/17 23:32:23 oliver-guest
+ * Fixes for Epson 3490 @ 2400 DPI
+ *
+ * Revision 1.16 2005/11/10 19:42:02 oliver-guest
+ * Added deinterlacing for Epson 3490
+ *
+ * Revision 1.15 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.14 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.13 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.12 2004/11/14 19:26:38 oliver-guest
+ * Applied patch from Julien Blache to change ch_past_init from SANE_Int to SANE_Bool
+ *
+ * Revision 1.11 2004/11/09 23:17:38 oliver-guest
+ * First implementation of deinterlacer for Epson scanners at high resolutions (thanks to Brad Johnson)
+ *
+ * Revision 1.10 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.9 2004/04/09 16:18:37 oliver-guest
+ * Fix initialization of FDSource.bytes_remaining
+ *
+ * Revision 1.8 2004/04/09 11:59:02 oliver-guest
+ * Fixes for pthread implementation
+ *
+ * Revision 1.7 2004/04/08 21:53:10 oliver-guest
+ * Use sanei_thread in snapscan backend
+ *
+ * Revision 1.6 2001/12/17 22:51:49 oliverschwartz
+ * Update to snapscan-20011212 (snapscan 1.4.3)
+ *
+ * Revision 1.18 2001/12/12 19:44:59 oliverschwartz
+ * Clean up CVS log
+ *
+ * Revision 1.17 2001/11/27 23:16:17 oliverschwartz
+ * - Fix color alignment for SnapScan 600
+ * - Added documentation in snapscan-sources.c
+ * - Guard against TL_X < BR_X and TL_Y < BR_Y
+ *
+ * Revision 1.16 2001/10/08 18:22:02 oliverschwartz
+ * - Disable quality calibration for Acer Vuego 310F
+ * - Use sanei_scsi_max_request_size as scanner buffer size
+ * for SCSI devices
+ * - Added new devices to snapscan.desc
+ *
+ * Revision 1.15 2001/09/28 15:56:51 oliverschwartz
+ * - fix hanging for SNAPSCAN300 / VUEGO 310
+ *
+ * Revision 1.14 2001/09/28 13:39:16 oliverschwartz
+ * - Added "Snapscan 300" ID string
+ * - cleanup
+ * - more debugging messages in snapscan-sources.c
+ *
+ * Revision 1.13 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.12 2001/09/09 18:06:32 oliverschwartz
+ * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
+ *
+ * Revision 1.11 2001/04/13 13:12:18 oliverschwartz
+ * use absolute_max as expected_read_bytes for PRISA620S
+ *
+ * Revision 1.10 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.9 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.8 2000/11/28 03:55:07 cbagwell
+ * Reverting a fix to RGBRouter_remaining to original fix. This allows
+ * most scanners to scan at 600 dpi by ignoring insufficent data in
+ * the RGB circular buffer and always returning size = 1 in those cases.
+ * This should probably be fixed at a higher level.
+ *
+ * Revision 1.7 2000/11/20 01:02:42 cbagwell
+ * Updates so that USB will continue reading when it receives an EAGAIN error.
+ * Also, changed RGBRouter_remaining to not be able to return a negative
+ * value.
+ *
+ * Revision 1.6 2000/11/04 01:53:58 cbagwell
+ * Commiting some needed USB updates. Added extra test logic to detect
+ * bad bytes_expected values. Just to help debug faster on scanners
+ * that tickle the bug.
+ *
+ * Revision 1.5 2000/10/30 22:32:20 sable
+ * Support for vuego310s vuego610s and 1236s
+ *
+ * Revision 1.4 2000/10/28 14:16:10 sable
+ * Bug correction for SnapScan310
+ *
+ * Revision 1.3 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.2 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ * */
diff --git a/backend/snapscan-sources.h b/backend/snapscan-sources.h
new file mode 100644
index 0000000..53b705b
--- /dev/null
+++ b/backend/snapscan-sources.h
@@ -0,0 +1,109 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Oliver Schwartz
+ and Kevin Charter
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+
+/* $Id$
+ SnapScan backend scan data sources */
+
+#ifndef SNAPSCAN_SOURCES_H
+#define SNAPSCAN_SOURCES_H
+
+typedef struct source Source;
+
+typedef SANE_Int (*SourceRemaining) (Source *ps);
+typedef SANE_Int (*SourceBytesPerLine) (Source *ps);
+typedef SANE_Int (*SourcePixelsPerLine) (Source *ps);
+typedef SANE_Status (*SourceGet) (Source *ps, SANE_Byte *pbuf, SANE_Int *plen);
+typedef SANE_Status (*SourceDone) (Source *ps);
+
+#define SOURCE_GUTS \
+ SnapScan_Scanner *pss;\
+ SourceRemaining remaining;\
+ SourceBytesPerLine bytesPerLine;\
+ SourcePixelsPerLine pixelsPerLine;\
+ SourceGet get;\
+ SourceDone done
+
+struct source
+{
+ SOURCE_GUTS;
+};
+
+static SANE_Status Source_init (Source *pself,
+ SnapScan_Scanner *pss,
+ SourceRemaining remaining,
+ SourceBytesPerLine bytesPerLine,
+ SourcePixelsPerLine pixelsPerLine,
+ SourceGet get,
+ SourceDone done);
+
+/* base sources */
+
+#endif
+
+/*
+ * $Log$
+ * Revision 1.5 2001/12/17 22:51:50 oliverschwartz
+ * Update to snapscan-20011212 (snapscan 1.4.3)
+ *
+ * Revision 1.5 2001/12/12 19:44:59 oliverschwartz
+ * Clean up CVS log
+ *
+ * Revision 1.4 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.3 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.2 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ * */
diff --git a/backend/snapscan-usb.c b/backend/snapscan-usb.c
new file mode 100644
index 0000000..ec6a53e
--- /dev/null
+++ b/backend/snapscan-usb.c
@@ -0,0 +1,656 @@
+/*
+ Snapscan 1212U modifications for the Snapscan SANE backend
+
+ Copyright (C) 2000 Henrik Johansson
+
+ Henrik Johansson (henrikjo@post.urfors.se)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements USB equivalents to the SCSI routines used by the Snapscan
+ backend.
+
+ History
+
+ 0.1 2000-02-01
+
+ First version released
+
+ 0.2 2000-02-12
+
+ The Send Diagnostics SCSI command seems to hang some 1212U scanners.
+ Bypassing this command fixes the problem. This bug was reported by
+ Dmitri (dmitri@advantrix.com).
+
+ 0.3 2000-02-13
+
+ The "Set window" command returns with status "Device busy" when the
+ scanner is busy. One consequence is that some frontends exits with an
+ error message if it's started when the scanner is warming up.
+ A solution was suggested by Dmitri (dmitri@advantrix.com)
+ The idea is that a SCSI command which returns "device busy" is stored
+ in a "TODO" queue. The send command function is modified to first send
+ commands in the queue before the intended command.
+ So far this strategy has worked flawlessly. Thanks Dmitri!
+*/
+
+/* $Id$
+ SnapScan backend scan data sources */
+
+#include "snapscan-usb.h"
+#include "snapscan-mutex.c"
+
+#ifndef SHM_R
+#define SHM_R 0
+#endif
+
+#ifndef SHM_W
+#define SHM_W 0
+#endif
+
+/* Global variables */
+
+static snapscan_mutex_t snapscan_mutex;
+static sense_handler_type usb_sense_handler;
+static void* usb_pss;
+
+struct urb_counters_t {
+ unsigned long read_urbs;
+ unsigned long write_urbs;
+};
+
+static struct urb_counters_t* urb_counters = NULL;
+
+/* Forward declarations */
+static SANE_Status usb_request_sense(SnapScan_Scanner *pss);
+
+static SANE_Status snapscani_usb_cmd(int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ static const char me[] = "snapscani_usb_cmd";
+ int status;
+
+ DBG (DL_CALL_TRACE, "%s(%d,0x%lx,%lu,0x%lx,0x%lx (%lu))\n", me,
+ fd, (u_long) src,(u_long) src_size,(u_long) dst, (u_long) dst_size,(u_long) (dst_size ? *dst_size : 0));
+
+ while(bqhead) {
+ status = atomic_usb_cmd(fd, bqhead->src, bqhead->src_size, NULL, NULL);
+ if(status == SANE_STATUS_DEVICE_BUSY) {
+ if(is_queueable(src)) {
+ enqueue_bq(fd,src,src_size);
+ return SANE_STATUS_GOOD;
+ } else {
+ sleep(1);
+ continue;
+ }
+ }
+ dequeue_bq();
+ }
+
+ status = atomic_usb_cmd(fd,src,src_size,dst,dst_size);
+
+ if ((status == SANE_STATUS_DEVICE_BUSY) && is_queueable(src) ) {
+ enqueue_bq(fd,src,src_size);
+ return SANE_STATUS_GOOD;
+ }
+
+ return status;
+}
+
+static SANE_Status atomic_usb_cmd(int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ static const char me[] = "atomic_usb_cmd";
+
+ int status;
+ sigset_t all,oldset;
+
+ DBG (DL_CALL_TRACE, "%s(%d,0x%lx,%lu,0x%lx,0x%lx (%lu))\n", me,
+ fd, (u_long) src,(u_long) src_size,(u_long) dst, (u_long) dst_size,(u_long) (dst_size ? *dst_size : 0));
+
+ /* Prevent the calling process from being killed */
+ sigfillset(&all);
+ sigprocmask(SIG_BLOCK, &all, &oldset);
+
+ /* Make sure we are alone */
+ snapscani_mutex_lock(&snapscan_mutex);
+
+ status = usb_cmd(fd,src,src_size,dst,dst_size);
+
+ snapscani_mutex_unlock(&snapscan_mutex);
+
+ /* Now it is ok to be killed */
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+
+ return status;
+
+}
+
+static SANE_Status snapscani_usb_open(const char *dev, int *fdp,
+ sense_handler_type sense_handler, void* pss)
+{
+ static const char me[] = "snapscani_usb_open";
+
+ DBG (DL_CALL_TRACE, "%s(%s)\n", me, dev);
+
+ if(!snapscani_mutex_open(&snapscan_mutex, dev)) {
+ DBG (DL_MAJOR_ERROR, "%s: Can't get semaphore\n", me);
+ return SANE_STATUS_INVAL;
+ }
+ usb_sense_handler=sense_handler;
+ usb_pss = pss;
+ urb_counters->read_urbs = 0;
+ urb_counters->write_urbs = 0;
+ return sanei_usb_open(dev, fdp);
+}
+
+
+static void snapscani_usb_close(int fd) {
+ static const char me[] = "snapscani_usb_close";
+ SANE_Word vendor_id, product_id;
+
+ DBG (DL_CALL_TRACE, "%s(%d)\n", me, fd);
+ DBG (DL_DATA_TRACE,"1st read %ld write %ld\n", urb_counters->read_urbs, urb_counters->write_urbs);
+
+ /* Check if URB counting is needed. If yes, ensure the number of sent and
+ received URBs is even.
+ Odd number of URBs only cause problems with libusb and certain
+ scanner models. On other scanner models, sending additional commands
+ seems to cause problems (e.g. 1212u_2).
+ If sanei_usb_get_vendor_product returns an error there's probably no
+ libusb, so everything's fine.
+ */
+ if (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) == SANE_STATUS_GOOD)
+ {
+ /* Exclude 1212u_2 */
+ if (!((vendor_id == USB_VENDOR_AGFA) && (product_id == USB_PRODUCT_1212U2)))
+ {
+ if ((urb_counters->read_urbs & 0x01) && (urb_counters->write_urbs & 0x01))
+ {
+ char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+
+ snapscani_usb_cmd (fd, cmd, sizeof (cmd), NULL, 0);
+ }
+ else if (urb_counters->read_urbs & 0x01)
+ {
+ size_t read_bytes;
+ char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ char cmd2[] = {INQUIRY, 0, 0, 0, 120, 0};
+ char data[120];
+
+ read_bytes = 120;
+ snapscani_usb_cmd (fd, cmd2, sizeof (cmd2), data, &read_bytes);
+ snapscani_usb_cmd (fd, cmd, sizeof (cmd), NULL, 0);
+ }
+ else if (urb_counters->write_urbs & 0x01)
+ {
+ size_t read_bytes;
+ char cmd[] = {INQUIRY, 0, 0, 0, 120, 0};
+ char data[120];
+
+ read_bytes = 120;
+ snapscani_usb_cmd (fd, cmd, sizeof (cmd), data, &read_bytes);
+ }
+ DBG (DL_DATA_TRACE,"2nd read %ld write %ld\n", urb_counters->read_urbs,
+ urb_counters->write_urbs);
+ }
+ }
+ urb_counters->read_urbs = 0;
+ urb_counters->write_urbs = 0;
+ snapscani_mutex_close(&snapscan_mutex);
+ sanei_usb_close(fd);
+}
+
+static int usb_cmdlen(int cmd)
+{
+ switch(cmd) {
+ case TEST_UNIT_READY:
+ case INQUIRY:
+ case SCAN:
+ case REQUEST_SENSE:
+ case RESERVE_UNIT:
+ case RELEASE_UNIT:
+ case SEND_DIAGNOSTIC:
+ return 6;
+ case SEND:
+ case SET_WINDOW:
+ case READ:
+ case GET_DATA_BUFFER_STATUS:
+ return 10;
+ }
+ return 0;
+}
+
+static char *usb_debug_data(char *str,const char *data, int len) {
+ char tmpstr[10];
+ int i;
+
+ str[0]=0;
+ for(i=0; i < (len < 10 ? len : 10); i++) {
+ sprintf(tmpstr," 0x%02x",((int)data[i]) & 0xff);
+ if(i%16 == 0 && i != 0)
+ strcat(str,"\n");
+ strcat(str,tmpstr);
+ }
+ if(i < len)
+ strcat(str," ...");
+ return str;
+}
+
+#define RETURN_ON_FAILURE(x) if((status = x) != SANE_STATUS_GOOD) return status;
+
+static SANE_Status usb_write(int fd, const void *buf, size_t n) {
+ char dbgmsg[16384];
+ SANE_Status status;
+ size_t bytes_written = n;
+
+ static const char me[] = "usb_write";
+ DBG(DL_DATA_TRACE, "%s: writing: %s\n",me,usb_debug_data(dbgmsg,buf,n));
+
+ status = sanei_usb_write_bulk(fd, (const SANE_Byte*)buf, &bytes_written);
+ if(bytes_written != n) {
+ DBG (DL_MAJOR_ERROR, "%s Only %lu bytes written\n",me, (u_long) bytes_written);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ urb_counters->write_urbs += (bytes_written + 7) / 8;
+ DBG (DL_DATA_TRACE, "Written %lu bytes\n", (u_long) bytes_written);
+ return status;
+}
+
+static SANE_Status usb_read(SANE_Int fd, void *buf, size_t n) {
+ char dbgmsg[16384];
+ static const char me[] = "usb_read";
+ SANE_Status status;
+ size_t bytes_read = n;
+
+ status = sanei_usb_read_bulk(fd, (SANE_Byte*)buf, &bytes_read);
+ if (bytes_read != n) {
+ DBG (DL_MAJOR_ERROR, "%s Only %lu bytes read\n",me, (u_long) bytes_read);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ urb_counters->read_urbs += ((63 + bytes_read) / 64);
+ DBG(DL_DATA_TRACE, "%s: reading: %s\n",me,usb_debug_data(dbgmsg,buf,n));
+ DBG(DL_DATA_TRACE, "Read %lu bytes\n", (u_long) bytes_read);
+ return status;
+}
+
+static SANE_Status usb_read_status(int fd, int *scsistatus, int *transaction_status,
+ char command)
+{
+ static const char me[] = "usb_read_status";
+ unsigned char status_buf[8];
+ int scsistat;
+ int status;
+
+ RETURN_ON_FAILURE(usb_read(fd,status_buf,8));
+
+ if(transaction_status)
+ *transaction_status = status_buf[0];
+
+ scsistat = (status_buf[1] & STATUS_MASK) >> 1;
+
+ if(scsistatus)
+ *scsistatus = scsistat;
+
+ switch(scsistat) {
+ case GOOD:
+ return SANE_STATUS_GOOD;
+ case CHECK_CONDITION:
+ if (usb_pss != NULL) {
+ if (command != REQUEST_SENSE) {
+ return usb_request_sense(usb_pss);
+ }
+ else {
+ /* Avoid recursive calls of usb_request_sense */
+ return SANE_STATUS_GOOD;
+ }
+ } else {
+ DBG (DL_MAJOR_ERROR, "%s: scanner structure not set, returning default error\n",
+ me);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ break;
+ case BUSY:
+ return SANE_STATUS_DEVICE_BUSY;
+ default:
+ return SANE_STATUS_IO_ERROR;
+ }
+}
+
+
+static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ static const char me[] = "usb_cmd";
+ int status,tstatus;
+ int cmdlen,datalen;
+ char command;
+
+ DBG (DL_CALL_TRACE, "%s(%d,0x%lx,%lu,0x%lx,0x%lx (%lu))\n", me,
+ fd, (u_long) src,(u_long) src_size,(u_long) dst, (u_long) dst_size,(u_long) (dst_size ? *dst_size : 0));
+
+ /* Since the "Send Diagnostic" command isn't supported by
+ all Snapscan USB-scanners it's disabled .
+ */
+ command = ((const char *)src)[0];
+ if(command == SEND_DIAGNOSTIC)
+ return(SANE_STATUS_GOOD);
+
+ cmdlen = usb_cmdlen(*((const char *)src));
+ datalen = src_size - cmdlen;
+
+ DBG(DL_DATA_TRACE, "%s: cmdlen=%d, datalen=%d\n",me,cmdlen,datalen);
+
+ /* Send command to scanner */
+ RETURN_ON_FAILURE( usb_write(fd,src,cmdlen) );
+
+ /* Read status */
+ RETURN_ON_FAILURE( usb_read_status(fd, NULL, &tstatus, command) );
+
+ /* Send data only if the scanner is expecting it */
+ if(datalen > 0 && (tstatus == TRANSACTION_WRITE)) {
+ /* Send data to scanner */
+ RETURN_ON_FAILURE( usb_write(fd, ((const SANE_Byte *) src) + cmdlen, datalen) );
+
+ /* Read status */
+ RETURN_ON_FAILURE( usb_read_status(fd, NULL, &tstatus, command) );
+ }
+
+ /* Receive data only when new data is waiting */
+ if(dst_size && *dst_size && (tstatus == TRANSACTION_READ)) {
+ RETURN_ON_FAILURE( usb_read(fd,dst,*dst_size) );
+
+ /* Read status */
+ RETURN_ON_FAILURE( usb_read_status(fd, NULL, &tstatus, command) );
+ }
+
+ if(tstatus != TRANSACTION_COMPLETED) {
+ if(tstatus == TRANSACTION_WRITE)
+ DBG(DL_MAJOR_ERROR,
+ "%s: The transaction should now be completed, but the scanner is expecting more data" ,me);
+ else
+ DBG(DL_MAJOR_ERROR,
+ "%s: The transaction should now be completed, but the scanner has more data to send" ,me);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return status;
+}
+
+/* Busy queue data structures and function implementations*/
+
+static int is_queueable(const char *src)
+{
+ switch(src[0]) {
+ case SEND:
+ case SET_WINDOW:
+ case SEND_DIAGNOSTIC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static struct usb_busy_queue *bqhead=NULL,*bqtail=NULL;
+static int bqelements=0;
+
+static int enqueue_bq(int fd,const void *src, size_t src_size)
+{
+ static const char me[] = "enqueue_bq";
+ struct usb_busy_queue *bqe;
+
+ DBG (DL_CALL_TRACE, "%s(%d,%p,%lu)\n", me, fd,src, (u_long) src_size);
+
+ if((bqe = malloc(sizeof(struct usb_busy_queue))) == NULL)
+ return -1;
+
+ if((bqe->src = malloc(src_size)) == NULL)
+ return -1;
+
+ memcpy(bqe->src,src,src_size);
+ bqe->src_size=src_size;
+
+ bqe->next=NULL;
+
+ if(bqtail) {
+ bqtail->next=bqe;
+ bqtail = bqe;
+ } else
+ bqhead = bqtail = bqe;
+
+ bqelements++;
+ DBG(DL_DATA_TRACE, "%s: Busy queue: elements=%d, bqhead=%p, bqtail=%p\n",
+ me,bqelements,(void*)bqhead,(void*)bqtail);
+ return 0;
+}
+
+static void dequeue_bq()
+{
+ static const char me[] = "dequeue_bq";
+ struct usb_busy_queue *tbqe;
+
+ DBG (DL_CALL_TRACE, "%s()\n", me);
+
+ if(!bqhead)
+ return;
+
+ tbqe = bqhead;
+ bqhead = bqhead->next;
+ if(!bqhead)
+ bqtail=NULL;
+
+ if(tbqe->src)
+ free(tbqe->src);
+ free(tbqe);
+
+ bqelements--;
+ DBG(DL_DATA_TRACE, "%s: Busy queue: elements=%d, bqhead=%p, bqtail=%p\n",
+ me,bqelements,(void*)bqhead,(void*)bqtail);
+}
+
+static SANE_Status usb_request_sense(SnapScan_Scanner *pss) {
+ static const char *me = "usb_request_sense";
+ size_t read_bytes = 0;
+ u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, 20, 0};
+ u_char data[20];
+ SANE_Status status;
+
+ read_bytes = 20;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = usb_cmd (pss->fd, cmd, sizeof (cmd), data, &read_bytes);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: usb command error: %s\n",
+ me, sane_strstatus (status));
+ }
+ else
+ {
+ if (usb_sense_handler) {
+ status = usb_sense_handler (pss->fd, data, (void *) pss);
+ } else {
+ DBG (DL_MAJOR_ERROR, "%s: No sense handler for USB\n", me);
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ return status;
+}
+
+#if defined USE_PTHREAD || defined HAVE_OS2_H || defined __BEOS__
+static SANE_Status snapscani_usb_shm_init(void)
+{
+ unsigned int shm_size = sizeof(struct urb_counters_t);
+ urb_counters = (struct urb_counters_t*) malloc(shm_size);
+ if (urb_counters == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(urb_counters, 0, shm_size);
+ return SANE_STATUS_GOOD;
+
+}
+
+static void snapscani_usb_shm_exit(void)
+{
+ if (urb_counters)
+ {
+ free ((void*)urb_counters);
+ urb_counters = NULL;
+ }
+}
+#else
+#include <sys/ipc.h>
+#include <sys/shm.h>
+static SANE_Status snapscani_usb_shm_init(void)
+{
+ unsigned int shm_size = sizeof(struct urb_counters_t);
+ void* shm_area = NULL;
+ int shm_id = shmget (IPC_PRIVATE, shm_size, IPC_CREAT | SHM_R | SHM_W);
+ if (shm_id == -1)
+ {
+ DBG (DL_MAJOR_ERROR, "snapscani_usb_shm_init: cannot create shared memory segment: %s\n",
+ strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shm_area = shmat (shm_id, NULL, 0);
+ if (shm_area == (void *) -1)
+ {
+ DBG (DL_MAJOR_ERROR, "snapscani_usb_shm_init: cannot attach to shared memory segment: %s\n",
+ strerror (errno));
+ shmctl (shm_id, IPC_RMID, NULL);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (shmctl (shm_id, IPC_RMID, NULL) == -1)
+ {
+ DBG (DL_MAJOR_ERROR, "snapscani_usb_shm_init: cannot remove shared memory segment id: %s\n",
+ strerror (errno));
+ shmdt (shm_area);
+ shmctl (shm_id, IPC_RMID, NULL);
+ return SANE_STATUS_NO_MEM;
+ }
+ urb_counters = (struct urb_counters_t*) shm_area;
+ memset(urb_counters, 0, shm_size);
+ return SANE_STATUS_GOOD;
+}
+
+static void snapscani_usb_shm_exit(void)
+{
+ if (urb_counters)
+ {
+ shmdt (urb_counters);
+ urb_counters = NULL;
+ }
+}
+#endif
+/*
+ * $Log$
+ * Revision 1.22 2006/01/26 17:42:30 hmg-guest
+ * Added #defines for SHM_R/W for cygwin (patch from Philip Aston <philipa@mail.com>).
+ *
+ * Revision 1.21 2005/11/02 19:22:06 oliver-guest
+ * Fixes for Benq 5000
+ *
+ * Revision 1.20 2005/07/18 17:37:37 oliver-guest
+ * ZETA changes for SnapScan backend
+ *
+ * Revision 1.19 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.18 2004/06/16 19:52:26 oliver-guest
+ * Don't enforce even number of URB packages on 1212u_2. Fixes bug #300753.
+ *
+ * Revision 1.17 2004/06/06 14:50:36 oliver-guest
+ * Use shared memory functions only when needed
+ *
+ * Revision 1.16 2004/05/26 22:37:01 oliver-guest
+ * Use shared memory for urb counters in snapscan backend
+ *
+ * Revision 1.15 2004/04/09 11:59:02 oliver-guest
+ * Fixes for pthread implementation
+ *
+ * Revision 1.14 2004/04/08 22:48:13 oliver-guest
+ * Use URB counting in snapscan-usb.c (thanks to Jose Alberto Reguero)
+ *
+ * Revision 1.13 2003/11/08 09:50:27 oliver-guest
+ * Fix TPO scanning range for Epson 1670
+ *
+ * Revision 1.12 2003/07/26 17:16:55 oliverschwartz
+ * Changed licence to GPL + SANE exception for snapscan-usb.[ch]
+ *
+ * Revision 1.11 2002/07/12 23:29:06 oliverschwartz
+ * SnapScan backend 1.4.15
+ *
+ * Revision 1.21 2002/07/12 22:52:42 oliverschwartz
+ * use sanei_usb_read_bulk() and sanei_usb_write_bulk()
+ *
+ * Revision 1.20 2002/04/27 14:36:25 oliverschwartz
+ * Pass a char as 'proj' argument for ftok()
+ *
+ * Revision 1.19 2002/04/10 21:00:33 oliverschwartz
+ * Make bqelements static
+ *
+ * Revision 1.18 2002/03/24 12:16:09 oliverschwartz
+ * Better error report in usb_read
+ *
+ * Revision 1.17 2002/02/05 19:32:39 oliverschwartz
+ * Only define union semun if not already defined in <sys/sem.h>. Fixes
+ * compilation bugs on Irix and FreeBSD. Fixed by Henning Meier-Geinitz.
+ *
+ * Revision 1.16 2002/01/14 21:11:56 oliverschwartz
+ * Add workaround for bug semctl() call in libc for PPC
+ *
+ * Revision 1.15 2001/12/09 23:06:44 oliverschwartz
+ * - use sense handler for USB if scanner reports CHECK_CONDITION
+ *
+ * Revision 1.14 2001/11/16 20:23:16 oliverschwartz
+ * Merge with sane-1.0.6
+ * - Check USB vendor IDs to avoid hanging scanners
+ * - fix bug in dither matrix computation
+ *
+ * Revision 1.13 2001/10/09 22:34:23 oliverschwartz
+ * fix compiler warnings
+ *
+ * Revision 1.12 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * */
+
diff --git a/backend/snapscan-usb.h b/backend/snapscan-usb.h
new file mode 100644
index 0000000..c222d3f
--- /dev/null
+++ b/backend/snapscan-usb.h
@@ -0,0 +1,128 @@
+/*
+ Snapscan 1212U modifications for the Snapscan SANE backend
+
+ Copyright (C) 2000 Henrik Johansson
+
+ Henrik Johansson (henrikjo@post.urfors.se)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements USB equivalents to the SCSI routines used by the Snapscan
+ backend.
+*/
+
+/* $Id$
+ SnapScan backend scan data sources */
+
+#ifndef snapscan_usb_h
+#define snapscan_usb_h
+
+typedef SANE_Status (*sense_handler_type)(int fd, u_char *sense_buffer, void *arg);
+
+static SANE_Status snapscani_usb_cmd(int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size);
+static SANE_Status snapscani_usb_open(const char *dev, int *fdp,
+ sense_handler_type, void*);
+static void snapscani_usb_close(int fd);
+
+/*
+ * USB status codes
+ */
+#define GOOD 0x00
+#define CHECK_CONDITION 0x01
+#define CONDITION_GOOD 0x02
+#define BUSY 0x04
+#define INTERMEDIATE_GOOD 0x08
+#define INTERMEDIATE_C_GOOD 0x0a
+#define RESERVATION_CONFLICT 0x0c
+#define COMMAND_TERMINATED 0x11
+#define QUEUE_FULL 0x14
+
+#define STATUS_MASK 0x3e
+
+/*
+ * USB transaction status
+ */
+#define TRANSACTION_COMPLETED 0xfb /* Scanner considers the transaction done */
+#define TRANSACTION_READ 0xf9 /* Scanner has data to deliver */
+#define TRANSACTION_WRITE 0xf8 /* Scanner is expecting more data */
+
+/*
+ * Busy queue data structure and prototypes
+ */
+struct usb_busy_queue {
+ int fd;
+ void *src;
+ size_t src_size;
+ struct usb_busy_queue *next;
+};
+
+static struct usb_busy_queue *bqhead,*bqtail;
+static int enqueue_bq(int fd,const void *src, size_t src_size);
+static void dequeue_bq(void);
+static int is_queueable(const char *src);
+
+static SANE_Status atomic_usb_cmd(int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size);
+static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size);
+
+#endif
+
+/*
+ * $Log$
+ * Revision 1.6 2003/07/26 17:16:55 oliverschwartz
+ * Changed licence to GPL + SANE exception for snapscan-usb.[ch]
+ *
+ * Revision 1.5 2002/04/10 21:45:53 oliverschwartz
+ * Removed illegal character / removed declaration of bqelements
+ *
+ * Revision 1.10 2001/12/09 23:06:45 oliverschwartz
+ * - use sense handler for USB if scanner reports CHECK_CONDITION
+ *
+ * Revision 1.9 2001/11/16 20:23:16 oliverschwartz
+ * Merge with sane-1.0.6
+ * - Check USB vendor IDs to avoid hanging scanners
+ * - fix bug in dither matrix computation
+ *
+ * Revision 1.8 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * */
diff --git a/backend/snapscan.c b/backend/snapscan.c
new file mode 100644
index 0000000..44b757b
--- /dev/null
+++ b/backend/snapscan.c
@@ -0,0 +1,2646 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997-2005, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Simon Munton, Petter Reinholdtsen, Gary Plewa, Sebastien Sable,
+ Mikael Magnusson, Max Ushakov, Andrew Goodbody, Oliver Schwartz
+ and Kevin Charter
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+
+/* $Id$
+ SANE SnapScan backend */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define MINOR_VERSION 4
+#define BUILD 53
+#define BACKEND_NAME snapscan
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/saneopts.h"
+
+#include "snapscan.h"
+
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+#define LIMIT(x,min,max) MIN(MAX(x, min), max)
+
+#ifdef INOPERATIVE
+#define P_200_TO_255(per) SANE_UNFIX(255.0*((per + 100)/200.0))
+#endif
+
+#include "../include/sane/sanei_config.h"
+
+/* debug levels */
+#define DL_INFO 10
+#define DL_MINOR_INFO 15
+#define DL_MAJOR_ERROR 1
+#define DL_MINOR_ERROR 2
+#define DL_DATA_TRACE 50
+#define DL_OPTION_TRACE 70
+#define DL_CALL_TRACE 30
+#define DL_VERBOSE 20
+
+#define CHECK_STATUS(s,caller,cmd) \
+if ((s) != SANE_STATUS_GOOD) { DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", caller, (cmd), sane_strstatus(s)); return s; }
+
+/*----- internal scanner operations -----*/
+
+/* hardware configuration byte masks */
+
+#define HCFG_ADC 0x80 /* AD converter 1 ==> 10bit, 0 ==> 8bit */
+#define HCFG_ADF 0x40 /* automatic document feeder */
+#define HCFG_TPO 0x20 /* transparency option */
+#define HCFG_RB 0x10 /* ring buffer */
+#define HCFG_HT16 0x08 /* 16x16 halftone matrices */
+#define HCFG_HT8 0x04 /* 8x8 halftone matrices */
+#define HCFG_SRA 0x02 /* scanline row average (high-speed colour) */
+#define HCFG_CAL_ALLOWED 0x01 /* 1 ==> calibration allowed */
+
+#define HCFG_HT 0x0C /* support halftone matrices at all */
+
+#define MM_PER_IN 25.4 /* # millimetres per inch */
+#define IN_PER_MM 0.03937 /* # inches per millimetre */
+
+#define GAMMA_8BIT 0
+#define GAMMA_16BIT 1
+#define GAMMA_12_16BIT 2
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/* authorization stuff */
+static SANE_Auth_Callback auth = NULL;
+#if UNUSED
+static SANE_Char username[SANE_MAX_USERNAME_LEN];
+static SANE_Char password[SANE_MAX_PASSWORD_LEN];
+#endif
+
+/* function prototypes */
+
+static void gamma_n (double gamma, int brightness, int contrast,
+ u_char *buf, int length, int gamma_mode);
+static void gamma_to_sane (int length, u_char *in, SANE_Int *out);
+
+static size_t max_string_size(SANE_String_Const strings[]);
+
+/* inline functions */
+static inline SnapScan_Mode actual_mode (SnapScan_Scanner *pss)
+{
+ if (pss->preview == SANE_TRUE)
+ return pss->preview_mode;
+ return pss->mode;
+}
+
+static inline int is_colour_mode (SnapScan_Mode m)
+{
+ return (m == MD_COLOUR) || (m == MD_BILEVELCOLOUR);
+}
+
+static inline int calibration_line_length(SnapScan_Scanner *pss)
+{
+ int pos_factor;
+ int pixel_length;
+
+ switch (pss->pdev->model)
+ {
+ case STYLUS_CX1500:
+ case PRISA5000E:
+ case PRISA5000:
+ case PRISA5150:
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ pos_factor = pss->actual_res / 2;
+ pixel_length = pos_factor * 8.5;
+ break;
+ case SCANWIT2720S:
+ pixel_length = 2550;
+ break;
+ default:
+ pos_factor = pss->actual_res;
+ pixel_length = pos_factor * 8.5;
+ break;
+ }
+
+ if(is_colour_mode(actual_mode(pss))) {
+ return 3 * pixel_length;
+ } else {
+ return pixel_length;
+ }
+}
+
+/*----- global data structures and access utilities -----*/
+
+/* available device list */
+
+static SnapScan_Device *first_device = NULL; /* device list head */
+static SANE_Int n_devices = 0; /* the device count */
+static SANE_Char *default_firmware_filename;
+static SANE_Bool cancelRead;
+
+/* list returned from sane_get_devices() */
+static const SANE_Device **get_devices_list = NULL;
+
+/* external routines */
+#include "snapscan-scsi.c"
+#include "snapscan-sources.c"
+#include "snapscan-usb.c"
+#include "snapscan-options.c"
+
+/* Initialize gamma tables */
+static SANE_Status alloc_gamma_tables(SnapScan_Scanner * ps)
+{
+ static const char me[] = "alloc_gamma_tables";
+
+ ps->gamma_length = 1 << ps->bpp;
+ DBG (DL_MINOR_INFO, "%s: using 4*%d bytes for gamma table\n",
+ me,
+ ps->gamma_length);
+
+ ps->gamma_tables =
+ (SANE_Int *) malloc(4 * ps->gamma_length * sizeof(SANE_Int));
+
+ if (!ps->gamma_tables)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ps->gamma_table_gs = &ps->gamma_tables[0 * ps->gamma_length];
+ ps->gamma_table_r = &ps->gamma_tables[1 * ps->gamma_length];
+ ps->gamma_table_g = &ps->gamma_tables[2 * ps->gamma_length];
+ ps->gamma_table_b = &ps->gamma_tables[3 * ps->gamma_length];
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status init_gamma(SnapScan_Scanner * ps)
+{
+ u_char *gamma;
+
+ gamma = (u_char*) malloc(ps->gamma_length * sizeof(u_char) * 2);
+
+ if (!gamma)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Default tables */
+ gamma_n (SANE_UNFIX(ps->gamma_gs), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_gs);
+
+ gamma_n (SANE_UNFIX(ps->gamma_r), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_r);
+
+ gamma_n (SANE_UNFIX(ps->gamma_g), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_g);
+
+ gamma_n (SANE_UNFIX(ps->gamma_b), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_b);
+
+ free (gamma);
+ return SANE_STATUS_GOOD;
+}
+
+/* Max string size */
+
+static size_t max_string_size (SANE_String_Const strings[])
+{
+ size_t size;
+ size_t max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+/* gamma table computation */
+static void gamma_n (double gamma, int brightness, int contrast,
+ u_char *buf, int bpp, int gamma_mode)
+{
+ int i;
+ double i_gamma = 1.0/gamma;
+ int length = 1 << bpp;
+ int max = length - 1;
+ double mid = max / 2.0;
+
+ for (i = 0; i < length; i++)
+ {
+ int x;
+ double val = (i - mid) * (1.0 + contrast / 100.0)
+ + (1.0 + brightness / 100.0) * mid;
+ val = LIMIT(val, 0, max);
+ switch (gamma_mode)
+ {
+ case GAMMA_16BIT:
+ x = LIMIT(65535*pow ((double) val/max, i_gamma) + 0.5, 0, 65535);
+
+ buf[2*i] = (u_char) x;
+ buf[2*i + 1] = (u_char) (x >> 8);
+ break;
+ case GAMMA_12_16BIT:
+ buf[2*i] = (u_char) i;
+ buf[2*i + 1] = (u_char) (i >> 8);
+ break;
+ case GAMMA_8BIT:
+ buf[i] =
+ (u_char) LIMIT(255*pow ((double) val/max, i_gamma) + 0.5, 0, 255);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void gamma_from_sane (int length, SANE_Int *in, u_char *out, int gamma_mode)
+{
+ int i;
+ for (i = 0; i < length; i++)
+ if (gamma_mode != GAMMA_8BIT)
+ {
+ out[2*i] = (u_char) LIMIT(in[i], 0, 65535);
+ out[2*i + 1] = (u_char) (LIMIT(in[i], 0, 65535) >> 8);
+ }
+ else
+ out[i] = (u_char) LIMIT(in[i] / 256, 0, 255);
+}
+
+static void gamma_to_sane (int length, u_char *in, SANE_Int *out)
+{
+ int i;
+ for (i = 0; i < length; i++)
+ out[i] = in[2*i] + 256 * in[2*i + 1];
+}
+
+/* dispersed-dot dither matrices; this is discussed in Foley, Van Dam,
+ Feiner and Hughes: Computer Graphics: principles and practice,
+ 2nd ed. (Addison-Wesley), pp 570-571.
+
+ The function mfDn computes the nth dispersed-dot dither matrix Dn
+ given D(n/2) and n; n is presumed to be a power of 2. D8 and D16
+ are the matrices of interest to us, since the SnapScan supports
+ only 8x8 and 16x16 dither matrices. */
+
+static u_char D2[] ={0, 2, 3, 1};
+
+static u_char D4[16], D8[64], D16[256];
+
+static void mkDn (u_char *Dn, u_char *Dn_half, unsigned n)
+{
+ unsigned int x, y;
+ for (y = 0; y < n; y++) {
+ for (x = 0; x < n; x++) {
+ /* Dn(x,y) = D2(2*x/n, 2*y/n) +4*Dn_half(x%(n/2), y%(n/2)) */
+ Dn[y*n + x] = D2[((int)(2*y/n))*2 + (int)(2*x/n)]
+ + 4*Dn_half[(y%(n/2))*(n/2) + x%(n/2)];
+ }
+ }
+}
+
+static SANE_Bool device_already_in_list (SnapScan_Device *current,
+ SANE_String_Const name)
+{
+ for ( ; NULL != current; current = current->pnext)
+ {
+ if (0 == strcmp (name, current->dev.name))
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+static SANE_Char* get_driver_name(SnapScan_Model model_num) {
+ SANE_Int i;
+ for (i=0; i<known_drivers; i++) {
+ if (drivers[i].id == model_num) break;
+ }
+ if (i == known_drivers) {
+ DBG(0, "Implementation error: Driver name not found\n");
+ return ("Unknown");
+ }
+ return (drivers[i].driver_name);
+}
+
+static SANE_Status snapscani_check_device(
+ int fd,
+ SnapScan_Bus bus_type,
+ char* vendor,
+ char* model,
+ SnapScan_Model* model_num
+) {
+ static const char me[] = "snapscani_check_device";
+ SANE_Status status = SANE_STATUS_GOOD;
+ int supported_vendor = 0;
+ int i;
+
+ DBG (DL_CALL_TRACE, "%s()\n", me);
+
+ /* check that the device is legitimate */
+ if ((status = mini_inquiry (bus_type, fd, vendor, model)) != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: mini_inquiry failed with %s.\n",
+ me,
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DL_VERBOSE,
+ "%s: Is vendor \"%s\" model \"%s\" a supported scanner?\n",
+ me,
+ vendor,
+ model);
+
+ /* check if this is one of our supported vendors */
+ for (i = 0; i < known_vendors; i++)
+ {
+ if (0 == strcasecmp (vendor, vendors[i]))
+ {
+ supported_vendor = 1;
+ break;
+ }
+ }
+ if (supported_vendor)
+ {
+ /* Known vendor. Check if it is one of our supported models */
+ *model_num = snapscani_get_model_id(model, fd, bus_type);
+ }
+ if (!supported_vendor || UNKNOWN == model_num)
+ {
+ DBG (DL_MINOR_ERROR,
+ "%s: \"%s %s\" is not one of %s\n",
+ me,
+ vendor,
+ model,
+ "AGFA SnapScan 300, 310, 600, 1212, 1236, e10, e20, e25, e26, "
+ "e40, e42, e50, e52 or e60\n"
+ "Acer 300, 310, 610, 610+, "
+ "620, 620+, 640, 1240, 3300, 4300 or 5300\n"
+ "Guillemot MaxiScan A4 Deluxe");
+ status = SANE_STATUS_INVAL;
+ } else {
+ DBG(DL_VERBOSE, "%s: Autodetected driver: %s\n", me, get_driver_name(*model_num));
+ }
+ return status;
+}
+
+static SANE_Status snapscani_init_device_structure(
+ SnapScan_Device **pd,
+ const SnapScan_Bus bus_type,
+ SANE_String_Const name,
+ const char* vendor,
+ const char* model,
+ const SnapScan_Model model_num
+) {
+ static const char me[] = "snapscani_init_device_structure";
+ SANE_Status status = SANE_STATUS_GOOD;;
+
+ DBG (DL_CALL_TRACE, "%s()\n", me);
+
+ (*pd) = (SnapScan_Device *) malloc (sizeof (SnapScan_Device));
+ if (!(*pd))
+ {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating device.", me);
+ return SANE_STATUS_NO_MEM;
+ }
+ (*pd)->dev.name = strdup (name);
+ if (strcmp(vendor, "Color") == 0) {
+ (*pd)->dev.vendor = strdup ("Acer");
+ } else {
+ (*pd)->dev.vendor = strdup (vendor);
+ }
+ (*pd)->dev.model = strdup (model);
+ if (model_num == SCANWIT2720S)
+ {
+ (*pd)->dev.type = strdup (SNAPSCAN_FS_TYPE);
+ }
+ else
+ {
+ (*pd)->dev.type = strdup (SNAPSCAN_TYPE);
+ }
+ (*pd)->bus = bus_type;
+ (*pd)->model = model_num;
+
+ if (!(*pd)->dev.name || !(*pd)->dev.vendor || !(*pd)->dev.model || !(*pd)->dev.type)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory allocating device descriptor strings.\n",
+ me);
+ free (*pd);
+ return SANE_STATUS_NO_MEM;
+ }
+ (*pd)->x_range.min = x_range_fb.min;
+ (*pd)->x_range.quant = x_range_fb.quant;
+ (*pd)->x_range.max = x_range_fb.max;
+ (*pd)->y_range.min = y_range_fb.min;
+ (*pd)->y_range.quant = y_range_fb.quant;
+ (*pd)->y_range.max = y_range_fb.max;
+ (*pd)->firmware_filename = NULL;
+
+ (*pd)->pnext = first_device;
+ first_device = (*pd);
+ n_devices++;
+ return status;
+}
+
+static SANE_Status add_scsi_device (SANE_String_Const full_name)
+{
+ int fd;
+ static const char me[] = "add_scsi_device";
+ SANE_Status status = SANE_STATUS_GOOD;
+ SnapScan_Device *pd;
+ SnapScan_Model model_num = UNKNOWN;
+ SnapScan_Bus bus_type = SCSI;
+ char vendor[8];
+ char model[17];
+ SANE_Char *name = NULL;
+
+ DBG (DL_CALL_TRACE, "%s(%s)\n", me, full_name);
+
+ sanei_config_get_string(full_name, &name);
+ if (!name)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ /* Avoid adding the same device more then once */
+ if (device_already_in_list (first_device, name)) {
+ free(name);
+ name = 0;
+ return SANE_STATUS_GOOD;
+ }
+
+ vendor[0] = model[0] = '\0';
+
+ DBG (DL_VERBOSE, "%s: Detected (kind of) a SCSI device\n", me);
+
+ status = sanei_scsi_open (name, &fd, sense_handler, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error opening device %s: %s\n",
+ me,
+ name,
+ sane_strstatus (status));
+ } else {
+ status = snapscani_check_device(fd, bus_type, vendor, model, &model_num);
+ sanei_scsi_close(fd);
+ }
+ if (status == SANE_STATUS_GOOD) {
+ status = snapscani_init_device_structure(
+ &pd,
+ bus_type,
+ name,
+ vendor,
+ model,
+ model_num
+ );
+ }
+ free(name);
+ name = 0;
+ return status;
+}
+
+static SANE_Status add_usb_device (SANE_String_Const full_name) {
+ static const char me[] = "add_usb_device";
+ int fd;
+ SnapScan_Device *pd;
+ SnapScan_Model model_num = UNKNOWN;
+ SANE_Word vendor_id, product_id;
+ int supported_usb_vendor = 0;
+ char vendor[8];
+ char model[17];
+ SANE_Status status = SANE_STATUS_GOOD;
+ SnapScan_Bus bus_type = USB;
+ int i;
+ SANE_Char *name = NULL;
+
+ DBG (DL_CALL_TRACE, "%s(%s)\n", me, full_name);
+ sanei_config_get_string(full_name, &name);
+ if (!name)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ /* Avoid adding the same device more then once */
+ if (device_already_in_list (first_device, name)) {
+ free(name);
+ name = 0;
+ return SANE_STATUS_GOOD;
+ }
+
+ vendor[0] = model[0] = '\0';
+
+ DBG (DL_VERBOSE, "%s: Detected (kind of) an USB device\n", me);
+ bus_type = USB;
+ status = snapscani_usb_shm_init();
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ status = snapscani_usb_open (name, &fd, sense_handler, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error opening device %s: %s\n",
+ me,
+ name,
+ sane_strstatus (status));
+ } else {
+ if (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) ==
+ SANE_STATUS_GOOD)
+ {
+ /* check for known USB vendors to avoid hanging scanners by
+ inquiry-command.
+ */
+ DBG(DL_INFO, "%s: Checking if 0x%04x is a supported USB vendor ID\n",
+ me, vendor_id);
+ for (i = 0; i < known_usb_vendor_ids; i++) {
+ if (vendor_id == usb_vendor_ids[i]) {
+ supported_usb_vendor = 1;
+ }
+ }
+ if (!supported_usb_vendor) {
+ DBG(DL_MINOR_ERROR,
+ "%s: USB vendor ID 0x%04x is currently NOT supported by the snapscan backend.\n",
+ me, vendor_id);
+ status=SANE_STATUS_INVAL;
+ snapscani_usb_close(fd);
+ }
+ }
+ }
+ if (status == SANE_STATUS_GOOD) {
+ status = snapscani_check_device(fd, bus_type, vendor, model, &model_num);
+ snapscani_usb_close(fd);
+ }
+ /* deinit shared memory, will be initialized again in open_scanner */
+ snapscani_usb_shm_exit();
+ if (status == SANE_STATUS_GOOD) {
+ status = snapscani_init_device_structure(
+ &pd,
+ bus_type,
+ name,
+ vendor,
+ model,
+ model_num
+ );
+ }
+ free(name);
+ name = 0;
+ return status;
+}
+
+/* find_device: find a device in the available list by name
+
+ ARG: the device name
+
+ RET: a pointer to the corresponding device record, or NULL if there
+ is no such device */
+
+static SnapScan_Device *find_device (SANE_String_Const name)
+{
+ static char me[] = "find_device";
+ SnapScan_Device *psd;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ for (psd = first_device; psd; psd = psd->pnext)
+ {
+ if (strcmp (psd->dev.name, name) == 0)
+ return psd;
+ }
+ return NULL;
+}
+
+/*----- functions in the scanner interface -----*/
+
+SANE_Status sane_init (SANE_Int *version_code,
+ SANE_Auth_Callback authorize)
+{
+ static const char me[] = "sane_snapscan_init";
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ DBG (DL_VERBOSE, "%s: Snapscan backend version %d.%d.%d\n",
+ me,
+ SANE_CURRENT_MAJOR, MINOR_VERSION, BUILD);
+
+ if (version_code != NULL)
+ {
+ *version_code =
+ SANE_VERSION_CODE (SANE_CURRENT_MAJOR, MINOR_VERSION, BUILD);
+ }
+
+ auth = authorize;
+ /* Initialize data structures */
+ default_firmware_filename = NULL;
+ first_device = NULL;
+ n_devices = 0;
+
+ sanei_usb_init();
+ sanei_thread_init();
+ /* build a device structure */
+ fp = sanei_config_open (SNAPSCAN_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to DEFAULT_DEVICE instead of insisting on config file */
+ DBG (DL_INFO,
+ "%s: configuration file not found, defaulting to %s.\n",
+ me,
+ DEFAULT_DEVICE);
+ status = add_scsi_device (DEFAULT_DEVICE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MINOR_ERROR,
+ "%s: failed to add device \"%s\"\n",
+ me,
+ dev_name);
+ }
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ len = strlen (dev_name);
+ if (!len)
+ continue; /* ignore empty lines */
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ if (strncasecmp(dev_name, FIRMWARE_KW, strlen(FIRMWARE_KW)) == 0) {
+ if (!default_firmware_filename) {
+ sanei_config_get_string(dev_name + strlen(FIRMWARE_KW), &default_firmware_filename);
+ if (default_firmware_filename == NULL) {
+ DBG (0, "%s: Illegal firmware entry %s.\n", me, dev_name);
+ }
+ }
+ }
+ else if (strncasecmp(dev_name, OPTIONS_KW, strlen(OPTIONS_KW)) == 0)
+ continue; /* ignore options lines */
+
+ else if (strncmp(dev_name, "usb", 3) == 0) {
+ sanei_usb_attach_matching_devices (dev_name, add_usb_device);
+ }
+ else if (strncmp(dev_name, "scsi", 4) == 0) {
+ sanei_config_attach_matching_devices (dev_name, add_scsi_device);
+ }
+ else if (strstr (dev_name, "usb")) {
+ add_usb_device(dev_name);
+ }
+ else {
+ add_scsi_device(dev_name);
+ }
+ }
+ fclose (fp);
+ }
+
+ /* compute the dither matrices */
+
+ mkDn (D4, D2, 4);
+ mkDn (D8, D4, 8);
+ mkDn (D16, D8, 16);
+ /* scale the D8 matrix from 0..63 to 0..255 */
+ {
+ u_char i;
+ for (i = 0; i < 64; i++)
+ D8[i] = (u_char) (4 * D8[i] + 2);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static void free_device_list(SnapScan_Device *psd) {
+ if (psd->pnext != NULL) {
+ free_device_list(psd->pnext);
+ }
+ free(psd);
+}
+
+void sane_exit (void)
+{
+ DBG (DL_CALL_TRACE, "sane_snapscan_exit\n");
+
+ if (get_devices_list)
+ free (get_devices_list);
+ get_devices_list = NULL;
+
+ /* just for safety, reset things to known values */
+ auth = NULL;
+
+ if (first_device) {
+ free_device_list(first_device);
+ first_device = NULL;
+ }
+ n_devices = 0;
+}
+
+
+SANE_Status sane_get_devices (const SANE_Device ***device_list,
+ SANE_Bool local_only)
+{
+ static const char *me = "sane_snapscan_get_devices";
+ DBG (DL_CALL_TRACE,
+ "%s (%p, %ld)\n",
+ me,
+ (const void *) device_list,
+ (long) local_only);
+
+ /* Waste the last list returned from this function */
+ if (NULL != get_devices_list)
+ free (get_devices_list);
+
+ *device_list =
+ (const SANE_Device **) malloc ((n_devices + 1) * sizeof (SANE_Device *));
+
+ if (*device_list)
+ {
+ int i;
+ SnapScan_Device *pdev;
+ for (i = 0, pdev = first_device; pdev; i++, pdev = pdev->pnext)
+ (*device_list)[i] = &(pdev->dev);
+ (*device_list)[i] = 0x0000 /*NULL */;
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory\n", me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ get_devices_list = *device_list;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ static const char *me = "sane_snapscan_open";
+ SnapScan_Device *psd;
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s (%s, %p)\n", me, name, (void *) h);
+
+ /* possible authorization required */
+
+ /* no device name: use first device */
+ if ((strlen(name) == 0) && (first_device != NULL))
+ {
+ name = first_device->dev.name;
+ }
+
+ /* device exists? */
+ psd = find_device (name);
+ if (!psd)
+ {
+ DBG (DL_MINOR_ERROR,
+ "%s: device \"%s\" not in current device list.\n",
+ me,
+ name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* create and initialize the scanner structure */
+
+ *h = (SnapScan_Scanner *) calloc (sizeof (SnapScan_Scanner), 1);
+ if (!*h)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory creating scanner structure.\n",
+ me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ {
+ SnapScan_Scanner *pss = *(SnapScan_Scanner **) h;
+
+ {
+ pss->devname = strdup (name);
+ if (!pss->devname)
+ {
+ free (*h);
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory copying device name.\n",
+ me);
+ return SANE_STATUS_NO_MEM;
+ }
+ pss->pdev = psd;
+ pss->opens = 0;
+ pss->sense_str = NULL;
+ pss->as_str = NULL;
+ pss->phys_buf_sz = DEFAULT_SCANNER_BUF_SZ;
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
+ pss->phys_buf_sz *= 2;
+ if (psd->bus == SCSI) {
+ pss->phys_buf_sz = sanei_scsi_max_request_size;
+ }
+ DBG (DL_DATA_TRACE,
+ "%s: Allocating %lu bytes as scanner buffer.\n",
+ me, (u_long) pss->phys_buf_sz);
+ pss->buf = (u_char *) malloc(pss->phys_buf_sz);
+ if (!pss->buf) {
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory creating scanner buffer.\n",
+ me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DL_VERBOSE,
+ "%s: allocated scanner structure at %p\n",
+ me,
+ (void *) pss);
+ }
+ status = snapscani_usb_shm_init();
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ status = open_scanner (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: open_scanner failed, status: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ DBG (DL_MINOR_INFO, "%s: waiting for scanner to warm up.\n", me);
+ status = wait_scanner_ready (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error waiting for scanner to warm up: %s\n",
+ me,
+ sane_strstatus(status));
+ free (pss);
+ return status;
+ }
+ DBG (DL_MINOR_INFO, "%s: performing scanner self test.\n", me);
+ status = send_diagnostic (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MINOR_INFO, "%s: send_diagnostic reports %s\n",
+ me, sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+ DBG (DL_MINOR_INFO, "%s: self test passed.\n", me);
+
+ /* option initialization depends on getting the hardware configuration
+ byte */
+ status = inquiry (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error in inquiry command: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+
+ if (pss->pdev->bus == USB)
+ {
+ if (sanei_usb_get_vendor_product(pss->fd, &pss->usb_vendor, &pss->usb_product) != SANE_STATUS_GOOD)
+ {
+ pss->usb_vendor = 0;
+ pss->usb_product = 0;
+ }
+ /* Download Firmware for USB scanners */
+ if (pss->hwst & 0x02)
+ {
+ char vendor[8];
+ char model[17];
+
+ status = download_firmware(pss);
+ CHECK_STATUS (status, me, "download_firmware");
+ /* send inquiry command again, wait for scanner to initialize */
+ status = wait_scanner_ready(pss);
+ CHECK_STATUS (status, me, "wait_scanner_ready after firmware upload");
+ status = mini_inquiry (pss->pdev->bus, pss->fd, vendor, model);
+ CHECK_STATUS (status, me, "mini_inquiry after firmware upload");
+ /* The model identifier may change after firmware upload */
+ DBG (DL_INFO,
+ "%s (after firmware upload): Checking if \"%s\" is a supported scanner\n",
+ me,
+ model);
+ /* Check if it is one of our supported models */
+ pss->pdev->model = snapscani_get_model_id(model, pss->fd, pss->pdev->bus);
+
+ if (pss->pdev->model == UNKNOWN) {
+ DBG (DL_MINOR_ERROR,
+ "%s (after firmware upload): \"%s\" is not a supported scanner\n",
+ me,
+ model);
+ }
+ /* run "real" inquiry command once again for option initialization */
+ status = inquiry (pss);
+ CHECK_STATUS (status, me, "inquiry after firmware upload");
+ }
+ }
+ close_scanner(pss);
+
+ status = alloc_gamma_tables (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error in alloc_gamma_tables: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+
+ init_options (pss);
+ status = init_gamma (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error in init_gamma: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+
+ pss->state = ST_IDLE;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void sane_close (SANE_Handle h)
+{
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ DBG (DL_CALL_TRACE, "sane_snapscan_close (%p)\n", (void *) h);
+ switch (pss->state)
+ {
+ case ST_SCAN_INIT:
+ case ST_SCANNING:
+ release_unit (pss);
+ break;
+ default:
+ break;
+ }
+ close_scanner (pss);
+ snapscani_usb_shm_exit();
+ free (pss->gamma_tables);
+ free (pss->buf);
+ free (pss);
+}
+
+
+
+SANE_Status sane_get_parameters (SANE_Handle h,
+ SANE_Parameters *p)
+{
+ static const char *me = "sane_snapscan_get_parameters";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SnapScan_Mode mode = actual_mode(pss);
+
+ DBG (DL_CALL_TRACE, "%s (%p, %p)\n", me, (void *) h, (void *) p);
+
+ p->last_frame = SANE_TRUE; /* we always do only one frame */
+
+ if ((pss->state == ST_SCAN_INIT) || (pss->state == ST_SCANNING))
+ {
+ /* we are in the middle of a scan, so we can use the data
+ that the scanner has reported */
+ if (pss->psrc != NULL)
+ {
+ DBG(DL_DATA_TRACE, "%s: Using source chain data\n", me);
+ /* use what the source chain says */
+ p->pixels_per_line = pss->psrc->pixelsPerLine(pss->psrc);
+ p->bytes_per_line = pss->psrc->bytesPerLine(pss->psrc);
+ /* p->lines = pss->psrc->remaining(pss->psrc)/p->bytes_per_line; */
+ p->lines = pss->lines;
+ }
+ else
+ {
+ DBG(DL_DATA_TRACE, "%s: Using current data\n", me);
+ /* estimate based on current data */
+ p->pixels_per_line = pss->pixels_per_line;
+ p->bytes_per_line = pss->bytes_per_line;
+ p->lines = pss->lines;
+ if (mode == MD_BILEVELCOLOUR)
+ p->bytes_per_line = p->pixels_per_line*3;
+ }
+ }
+ else
+ {
+ /* no scan in progress. The scanner data may not be up to date.
+ we have to calculate an estimate. */
+ double width, height;
+ int dpi;
+ double dots_per_mm;
+
+ DBG(DL_DATA_TRACE, "%s: Using estimated data\n", me);
+ width = SANE_UNFIX (pss->brx - pss->tlx);
+ height = SANE_UNFIX (pss->bry - pss->tly);
+ dpi = pss->res;
+ dots_per_mm = dpi / MM_PER_IN;
+ p->pixels_per_line = width * dots_per_mm;
+ p->lines = height * dots_per_mm;
+ switch (mode)
+ {
+ case MD_COLOUR:
+ case MD_BILEVELCOLOUR:
+ p->bytes_per_line = 3 * p->pixels_per_line * ((pss->bpp_scan+7)/8);
+ break;
+ case MD_LINEART:
+ p->bytes_per_line = (p->pixels_per_line + 7) / 8;
+ break;
+ default:
+ /* greyscale */
+ p->bytes_per_line = p->pixels_per_line * ((pss->bpp_scan+7)/8);
+ break;
+ }
+ }
+ p->format = (is_colour_mode(mode)) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ if (mode == MD_LINEART)
+ p->depth = 1;
+ else if (pss->pdev->model == SCANWIT2720S)
+ p->depth = 16;
+ else if (pss->preview)
+ p->depth = 8;
+ else
+ p->depth = pss->val[OPT_BIT_DEPTH].w;
+
+ DBG (DL_DATA_TRACE, "%s: depth = %ld\n", me, (long) p->depth);
+ DBG (DL_DATA_TRACE, "%s: lines = %ld\n", me, (long) p->lines);
+ DBG (DL_DATA_TRACE,
+ "%s: pixels per line = %ld\n",
+ me,
+ (long) p->pixels_per_line);
+ DBG (DL_DATA_TRACE,
+ "%s: bytes per line = %ld\n",
+ me,
+ (long) p->bytes_per_line);
+
+ return status;
+}
+
+/* scan data reader routine for child process */
+
+#define READER_WRITE_SIZE 4096
+
+static void reader (SnapScan_Scanner *pss)
+{
+ static char me[] = "Child reader process";
+ SANE_Status status;
+ SANE_Byte *wbuf = NULL;
+
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ wbuf = (SANE_Byte*) malloc(READER_WRITE_SIZE);
+ if (wbuf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: failed to allocate write buffer.\n", me);
+ return;
+ }
+
+ while ((pss->preadersrc->remaining(pss->preadersrc) > 0) && !cancelRead)
+ {
+ SANE_Int ndata = READER_WRITE_SIZE;
+ status = pss->preadersrc->get(pss->preadersrc, wbuf, &ndata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: %s on read.\n",
+ me,
+ sane_strstatus (status));
+ return;
+ }
+ {
+ SANE_Byte *buf = wbuf;
+ DBG (DL_DATA_TRACE, "READ %d BYTES (%d)\n", ndata, cancelRead);
+ while (ndata > 0)
+ {
+ int written = write (pss->rpipe[1], buf, ndata);
+ DBG (DL_DATA_TRACE, "WROTE %d BYTES\n", written);
+ if (written == -1)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error writing scan data on parent pipe.\n",
+ me);
+ perror ("pipe error: ");
+ }
+ else
+ {
+ ndata -= written;
+ buf += written;
+ }
+ }
+ }
+ }
+}
+
+/** signal handler to kill the child process
+ */
+static RETSIGTYPE usb_reader_process_sigterm_handler( int signo )
+{
+ DBG( DL_INFO, "(SIG) reader_process: terminated by signal %d\n", signo );
+ cancelRead = SANE_TRUE;
+}
+
+static RETSIGTYPE sigalarm_handler( int signo UNUSEDARG)
+{
+ DBG( DL_INFO, "ALARM!!!\n" );
+}
+
+/** executed as a child process
+ * read the data from the driver and send them to the parent process
+ */
+static int reader_process( void *args )
+{
+ SANE_Status status;
+ struct SIGACTION act;
+ sigset_t ignore_set;
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) args;
+
+ if( sanei_thread_is_forked()) {
+ DBG( DL_MINOR_INFO, "reader_process started (forked)\n" );
+ /* child process - close read side, make stdout the write side of the pipe */
+ close( pss->rpipe[0] );
+ pss->rpipe[0] = -1;
+ } else {
+ DBG( DL_MINOR_INFO, "reader_process started (as thread)\n" );
+ }
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGUSR1 );
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset ( &act, 0, sizeof (act));
+ sigaction( SIGTERM, &act, 0 );
+
+ cancelRead = SANE_FALSE;
+
+ /* install the signal handler */
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = usb_reader_process_sigterm_handler;
+ sigaction( SIGUSR1, &act, 0 );
+
+ status = create_base_source (pss, SCSI_SRC, &(pss->preadersrc));
+ if (status == SANE_STATUS_GOOD)
+ {
+ reader (pss);
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "Reader process: failed to create SCSISource.\n");
+ }
+ pss->preadersrc->done(pss->preadersrc);
+ free(pss->preadersrc);
+ pss->preadersrc = 0;
+ close( pss->rpipe[1] );
+ pss->rpipe[1] = -1;
+ DBG( DL_MINOR_INFO, "reader_process: finished reading data\n" );
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status start_reader (SnapScan_Scanner *pss)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ static char me[] = "start_reader";
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ pss->nonblocking = SANE_FALSE;
+ pss->rpipe[0] = pss->rpipe[1] = -1;
+ pss->child = -1;
+
+ if (pipe (pss->rpipe) != -1)
+ {
+ pss->orig_rpipe_flags = fcntl (pss->rpipe[0], F_GETFL, 0);
+ pss->child = sanei_thread_begin(reader_process, (void *) pss);
+
+ cancelRead = SANE_FALSE;
+
+ if (pss->child == -1)
+ {
+ /* we'll have to read in blocking mode */
+ DBG (DL_MAJOR_ERROR,
+ "%s: Error while calling sanei_thread_begin; must read in blocking mode.\n",
+ me);
+ close (pss->rpipe[0]);
+ close (pss->rpipe[1]);
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+ if (sanei_thread_is_forked())
+ {
+ /* parent; close write side */
+ close (pss->rpipe[1]);
+ pss->rpipe[1] = -1;
+ }
+ pss->nonblocking = SANE_TRUE;
+ }
+ return status;
+}
+
+static SANE_Status send_gamma_table (SnapScan_Scanner *pss, u_char dtc, u_char dtcq)
+{
+ static char me[] = "send_gamma_table";
+ SANE_Status status = SANE_STATUS_GOOD;
+ status = send (pss, dtc, dtcq);
+ CHECK_STATUS (status, me, "send");
+ switch (pss->pdev->model)
+ {
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ /* Some epson scanners need the gamma table twice */
+ status = send (pss, dtc, dtcq);
+ CHECK_STATUS (status, me, "2nd send");
+ break;
+ case PRISA5150:
+ /* 5150 needs the gamma table twice, with dtc = 0x04 for the second one */
+ status = send (pss, DTC_GAMMA2, dtcq);
+ CHECK_STATUS (status, me, "2nd send");
+ break;
+ default:
+ break;
+ }
+ return status;
+}
+
+static SANE_Status download_gamma_tables (SnapScan_Scanner *pss)
+{
+ static char me[] = "download_gamma_tables";
+ SANE_Status status = SANE_STATUS_GOOD;
+ double gamma_gs = SANE_UNFIX (pss->gamma_gs);
+ double gamma_r = SANE_UNFIX (pss->gamma_r);
+ double gamma_g = SANE_UNFIX (pss->gamma_g);
+ double gamma_b = SANE_UNFIX (pss->gamma_b);
+ SnapScan_Mode mode = actual_mode (pss);
+ int dtcq_gamma_gray;
+ int dtcq_gamma_red;
+ int dtcq_gamma_green;
+ int dtcq_gamma_blue;
+ int gamma_mode = GAMMA_8BIT;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ switch (mode)
+ {
+ case MD_COLOUR:
+ break;
+ case MD_BILEVELCOLOUR:
+ if (!pss->halftone)
+ {
+ gamma_r =
+ gamma_g =
+ gamma_b = 1.0;
+ }
+ break;
+ case MD_LINEART:
+ if (!pss->halftone)
+ gamma_gs = 1.0;
+ break;
+ default:
+ /* no further action for greyscale */
+ break;
+ }
+
+ switch (pss->bpp)
+ {
+ case 10:
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY10;
+ dtcq_gamma_red = DTCQ_GAMMA_RED10;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN10;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE10;
+ break;
+ case 12:
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 16bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY12_16BIT;
+ dtcq_gamma_red = DTCQ_GAMMA_RED12_16BIT;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN12_16BIT;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE12_16BIT;
+ gamma_mode = GAMMA_12_16BIT;
+ }
+ else
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY12;
+ dtcq_gamma_red = DTCQ_GAMMA_RED12;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN12;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE12;
+ }
+ break;
+ case 14:
+ if (pss->bpp_scan == 16)
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 16bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY14_16BIT;
+ dtcq_gamma_red = DTCQ_GAMMA_RED14_16BIT;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN14_16BIT;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE14_16BIT;
+ gamma_mode = GAMMA_16BIT;
+ }
+ else
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY14;
+ dtcq_gamma_red = DTCQ_GAMMA_RED14;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN14;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE14;
+ }
+ break;
+ default:
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY8;
+ dtcq_gamma_red = DTCQ_GAMMA_RED8;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN8;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE8;
+ break;
+ }
+
+ if (is_colour_mode(mode))
+ {
+ if (pss->val[OPT_CUSTOM_GAMMA].b)
+ {
+ if (pss->val[OPT_GAMMA_BIND].b)
+ {
+ /* Use greyscale gamma for all rgb channels */
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ else
+ {
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_r,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_g,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_b,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ }
+ else
+ {
+ if (pss->val[OPT_GAMMA_BIND].b)
+ {
+ /* Use greyscale gamma for all rgb channels */
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ else
+ {
+ gamma_n (gamma_r, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_g, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_b, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ }
+ }
+ else
+ {
+ if(pss->val[OPT_CUSTOM_GAMMA].b)
+ {
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_gray);
+ CHECK_STATUS (status, me, "send");
+ }
+ else
+ {
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_gray);
+ CHECK_STATUS (status, me, "send");
+ }
+ }
+ return status;
+}
+
+static SANE_Status download_halftone_matrices (SnapScan_Scanner *pss)
+{
+ static char me[] = "download_halftone_matrices";
+ SANE_Status status = SANE_STATUS_GOOD;
+ if ((pss->halftone) &&
+ ((actual_mode(pss) == MD_LINEART) || (actual_mode(pss) == MD_BILEVELCOLOUR)))
+ {
+ u_char *matrix;
+ size_t matrix_sz;
+ u_char dtcq;
+
+ if (pss->dither_matrix == dm_dd8x8)
+ {
+ matrix = D8;
+ matrix_sz = sizeof (D8);
+ }
+ else
+ {
+ matrix = D16;
+ matrix_sz = sizeof (D16);
+ }
+
+ memcpy (pss->buf + SEND_LENGTH, matrix, matrix_sz);
+
+ if (is_colour_mode(actual_mode(pss)))
+ {
+ if (matrix_sz == sizeof (D8))
+ dtcq = DTCQ_HALFTONE_COLOR8;
+ else
+ dtcq = DTCQ_HALFTONE_COLOR16;
+
+ /* need copies for green and blue bands */
+ memcpy (pss->buf + SEND_LENGTH + matrix_sz,
+ matrix,
+ matrix_sz);
+ memcpy (pss->buf + SEND_LENGTH + 2 * matrix_sz,
+ matrix,
+ matrix_sz);
+ }
+ else
+ {
+ if (matrix_sz == sizeof (D8))
+ dtcq = DTCQ_HALFTONE_BW8;
+ else
+ dtcq = DTCQ_HALFTONE_BW16;
+ }
+
+ status = send (pss, DTC_HALFTONE, dtcq);
+ CHECK_STATUS (status, me, "send");
+ }
+ return status;
+}
+
+static SANE_Status measure_transfer_rate (SnapScan_Scanner *pss)
+{
+ static char me[] = "measure_transfer_rate";
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (pss->hconfig & HCFG_RB)
+ {
+ /* We have a ring buffer. We simulate one round of a read-store
+ cycle on the size of buffer we will be using. For this read only,
+ the buffer size must be rounded to a 128-byte boundary. */
+
+ DBG (DL_VERBOSE, "%s: have ring buffer\n", me);
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
+ {
+ /* Epson 2480: read a multiple of bytes per line, limit to less than 0xfff0 */
+ if (pss->bytes_per_line > 0xfff0)
+ pss->expected_read_bytes = 0xfff0;
+ else
+ pss->expected_read_bytes = (0xfff0 / pss->bytes_per_line) * pss->bytes_per_line;
+ }
+ else
+ pss->expected_read_bytes =
+ (pss->buf_sz%128) ? (pss->buf_sz/128 + 1)*128 : pss->buf_sz;
+
+ status = scsi_read (pss, READ_TRANSTIME);
+ CHECK_STATUS (status, me, "scsi_read");
+ pss->expected_read_bytes = 0;
+ status = scsi_read (pss, READ_TRANSTIME);
+ CHECK_STATUS (status, me, "scsi_read");
+ }
+ else
+ {
+ /* we don't have a ring buffer. The test requires transferring one
+ scan line of data (rounded up to next 128 byte boundary). */
+
+ DBG (DL_VERBOSE, "%s: we don't have a ring buffer.\n", me);
+ pss->expected_read_bytes = pss->bytes_per_line;
+
+ if (pss->expected_read_bytes%128)
+ {
+ pss->expected_read_bytes =
+ (pss->expected_read_bytes/128 + 1)*128;
+ }
+ status = scsi_read (pss, READ_TRANSTIME);
+ CHECK_STATUS (status, me, "scsi_read");
+ DBG (DL_VERBOSE, "%s: read %ld bytes.\n", me, (long) pss->read_bytes);
+ }
+
+ pss->expected_read_bytes = 0;
+ status = scsi_read (pss, READ_TRANSTIME);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: test read failed.\n", me);
+ return status;
+ }
+
+ DBG (DL_VERBOSE, "%s: successfully calibrated transfer rate.\n", me);
+ return status;
+}
+
+
+SANE_Status sane_start (SANE_Handle h)
+{
+ static const char *me = "sane_snapscan_start";
+ SANE_Status status;
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+
+ DBG (DL_CALL_TRACE, "%s (%p)\n", me, (void *) h);
+
+ /* possible authorization required */
+
+ status = open_scanner (pss);
+ CHECK_STATUS (status, me, "open_scanner");
+
+ status = wait_scanner_ready (pss);
+ CHECK_STATUS (status, me, "wait_scanner_ready");
+
+ /* start scanning; reserve the unit first, because a release_unit is
+ necessary to abort a scan in progress */
+
+ pss->state = ST_SCAN_INIT;
+
+ if ((pss->pdev->model == SCANWIT2720S) && (pss->focus_mode == MD_AUTO))
+ {
+ status = get_focus(pss);
+ CHECK_STATUS (status, me, "get_focus");
+ }
+
+ reserve_unit(pss);
+
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ status = set_frame(pss, 0);
+ CHECK_STATUS (status, me, "set_frame");
+ status = set_focus(pss, pss->focus);
+ CHECK_STATUS (status, me, "set_focus");
+ }
+
+ /* set up the window and fetch the resulting scanner parameters */
+ status = set_window(pss);
+ CHECK_STATUS (status, me, "set_window");
+
+ status = inquiry(pss);
+ CHECK_STATUS (status, me, "inquiry");
+
+ /* download the gamma and halftone tables */
+
+ status = download_gamma_tables(pss);
+ CHECK_STATUS (status, me, "download_gamma_tables");
+
+ status = download_halftone_matrices(pss);
+ CHECK_STATUS (status, me, "download_halftone_matrices");
+
+ if (pss->val[OPT_QUALITY_CAL].b && (pss->usb_vendor == USB_VENDOR_EPSON))
+ {
+ status = calibrate(pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: calibration failed.\n", me);
+ release_unit (pss);
+ return status;
+ }
+ }
+
+ /* we must measure the data transfer rate between the host and the
+ scanner, and the method varies depending on whether there is a
+ ring buffer or not. */
+
+ status = measure_transfer_rate(pss);
+ CHECK_STATUS (status, me, "measure_transfer_rate");
+
+ /* now perform an inquiry again to retrieve the scan speed */
+ status = inquiry(pss);
+ CHECK_STATUS (status, me, "inquiry");
+
+ DBG (DL_DATA_TRACE,
+ "%s: after measuring speed:\n\t%lu bytes per scan line\n"
+ "\t%f milliseconds per scan line.\n\t==>%f bytes per millisecond\n",
+ me,
+ (u_long) pss->bytes_per_line,
+ pss->ms_per_line,
+ pss->bytes_per_line/pss->ms_per_line);
+
+
+ if (pss->val[OPT_QUALITY_CAL].b && (pss->usb_vendor != USB_VENDOR_EPSON))
+ {
+ status = calibrate(pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: calibration failed.\n", me);
+ release_unit (pss);
+ return status;
+ }
+ }
+
+ status = scan(pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: scan command failed: %s.\n", me, sane_strstatus(status));
+ release_unit (pss);
+ return status;
+ }
+
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ status = set_frame(pss, pss->frame_no);
+ CHECK_STATUS (status, me, "set_frame");
+ }
+
+ if (pss->source == SRC_ADF)
+ {
+ /* Wait for scanner ready again (e.g. until paper is loaded from an ADF) */
+ /* Maybe replace with get_data_buffer_status()? */
+ status = wait_scanner_ready (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: scan command failed while waiting for scanner: %s.\n", me, sane_strstatus(status));
+ release_unit (pss);
+ return status;
+ }
+ }
+
+ DBG (DL_MINOR_INFO, "%s: starting the reader process.\n", me);
+ status = start_reader(pss);
+ {
+ BaseSourceType st = FD_SRC;
+ if (status != SANE_STATUS_GOOD)
+ st = SCSI_SRC;
+ status = create_source_chain (pss, st, &(pss->psrc));
+ }
+
+ return status;
+}
+
+
+SANE_Status sane_read (SANE_Handle h,
+ SANE_Byte *buf,
+ SANE_Int maxlen,
+ SANE_Int *plen)
+{
+ static const char *me = "sane_snapscan_read";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DL_CALL_TRACE,
+ "%s (%p, %p, %ld, %p)\n",
+ me,
+ (void *) h,
+ (void *) buf,
+ (long) maxlen,
+ (void *) plen);
+
+ *plen = 0;
+
+ if (pss->state == ST_CANCEL_INIT) {
+ pss->state = ST_IDLE;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (pss->psrc == NULL || pss->psrc->remaining(pss->psrc) == 0)
+ {
+ if (pss->child != -1)
+ {
+ sanei_thread_waitpid (pss->child, 0); /* ensure no zombies */
+ pss->child = -1;
+ }
+ release_unit (pss);
+ close_scanner (pss);
+ if (pss->psrc != NULL)
+ {
+ pss->psrc->done(pss->psrc);
+ free(pss->psrc);
+ pss->psrc = NULL;
+ }
+ pss->state = ST_IDLE;
+ return SANE_STATUS_EOF;
+ }
+
+ *plen = maxlen;
+ status = pss->psrc->get(pss->psrc, buf, plen);
+
+ switch (pss->state)
+ {
+ case ST_IDLE:
+ DBG (DL_MAJOR_ERROR,
+ "%s: weird error: scanner state should not be idle on call to "
+ "sane_read.\n",
+ me);
+ break;
+ case ST_SCAN_INIT:
+ /* we've read some data */
+ pss->state = ST_SCANNING;
+ break;
+ case ST_CANCEL_INIT:
+ /* stop scanning */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+void sane_cancel (SANE_Handle h)
+{
+ char *me = "sane_snapscan_cancel";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ struct SIGACTION act;
+ SANE_Pid res;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ switch (pss->state)
+ {
+ case ST_IDLE:
+ break;
+ case ST_SCAN_INIT:
+ case ST_SCANNING:
+ /* signal a cancellation has occurred */
+ pss->state = ST_CANCEL_INIT;
+ /* signal the reader, if any */
+ if (pss->child != -1)
+ {
+ DBG( DL_INFO, ">>>>>>>> killing reader_process <<<<<<<<\n" );
+
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+ sigaction( SIGALRM, &act, 0 );
+
+ if (sanei_thread_is_forked())
+ {
+ /* use SIGUSR1 to set cancelRead in child process */
+ sanei_thread_sendsig( pss->child, SIGUSR1 );
+ }
+ else
+ {
+ cancelRead = SANE_TRUE;
+ }
+
+ /* give'em 10 seconds 'til done...*/
+ alarm(10);
+ res = sanei_thread_waitpid( pss->child, 0 );
+ alarm(0);
+
+ if( res != pss->child ) {
+ DBG( DL_MINOR_ERROR,"sanei_thread_waitpid() failed !\n");
+
+ /* do it the hard way...*/
+#ifdef USE_PTHREAD
+ sanei_thread_kill( pss->child );
+#else
+ sanei_thread_sendsig( pss->child, SIGKILL );
+#endif
+ }
+ pss->child = -1;
+ DBG( DL_INFO,"reader_process killed\n");
+ }
+ release_unit (pss);
+ close_scanner (pss);
+ break;
+ case ST_CANCEL_INIT:
+ DBG (DL_INFO, "%s: cancellation already initiated.\n", me);
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: weird error: invalid scanner state (%ld).\n",
+ me,
+ (long) pss->state);
+ break;
+ }
+}
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ static char me[] = "sane_snapscan_set_io_mode";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ char *op;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ if (pss->state != ST_SCAN_INIT)
+ return SANE_STATUS_INVAL;
+
+ if (m)
+ {
+ if (pss->child == -1)
+ {
+ DBG (DL_MINOR_INFO,
+ "%s: no reader child; must use blocking mode.\n",
+ me);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ op = "ON";
+ fcntl (pss->rpipe[0], F_SETFL, O_NONBLOCK | pss->orig_rpipe_flags);
+ }
+ else
+ {
+ op = "OFF";
+ fcntl (pss->rpipe[0], F_SETFL, pss->orig_rpipe_flags);
+ }
+ DBG (DL_MINOR_INFO, "%s: turning nonblocking mode %s.\n", me, op);
+ pss->nonblocking = m;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ static char me[] = "sane_snapscan_get_select_fd";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ if (pss->state != ST_SCAN_INIT)
+ return SANE_STATUS_INVAL;
+
+ if (pss->child == -1)
+ {
+ DBG (DL_MINOR_INFO,
+ "%s: no reader child; cannot provide select file descriptor.\n",
+ me);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ *fd = pss->rpipe[0];
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * $Log$
+ * Revision 1.73 2008/11/26 21:21:29 kitno-guest
+ * * backend/ *.[ch]: nearly every backend used V_MAJOR
+ * instead of SANE_CURRENT_MAJOR in sane_init()
+ * * backend/snapscan.c: remove EXPECTED_VERSION check
+ * since new SANE standard is forward compatible
+ *
+ * Revision 1.72 2008-05-15 12:50:24 ellert-guest
+ * Fix for bug #306751: sanei-thread with pthreads on 64 bit
+ *
+ * Revision 1.71 2008-01-29 17:48:42 kitno-guest
+ * fix snapscan bug, add LiDE 600F
+ *
+ * Revision 1.70 2007-11-18 10:59:18 ellert-guest
+ * Fix handling of valid "negative" PIDs
+ *
+ * Revision 1.69 2007-11-16 08:04:02 ellert-guest
+ * Correct the test of the return value from sanei_thread_begin
+ *
+ * Revision 1.68 2006-09-03 10:00:11 oliver-guest
+ * Bugfix for firmware download by Paul Smedley
+ *
+ * Revision 1.67 2006/01/10 19:32:16 oliver-guest
+ * Added 12 bit gamma tables for Epson Stylus CX-1500
+ *
+ * Revision 1.66 2006/01/06 20:59:17 oliver-guest
+ * Some fixes for the Epson Stylus CX 1500
+ *
+ * Revision 1.65 2006/01/01 23:02:55 oliver-guest
+ * Added snapscan-data.c to Makefile.in
+ *
+ * Revision 1.64 2005/12/05 20:38:23 oliver-guest
+ * Small bugfix for Benq 5150
+ *
+ * Revision 1.63 2005/12/04 15:03:00 oliver-guest
+ * Some fixes for Benq 5150
+ *
+ * Revision 1.62 2005/12/02 19:15:42 oliver-guest
+ * Change SnapScan version number to 1.4.50
+ *
+ * Revision 1.61 2005/11/15 20:11:19 oliver-guest
+ * Enabled quality calibration for the Epson 3490
+ *
+ * Revision 1.60 2005/11/10 19:42:02 oliver-guest
+ * Added deinterlacing for Epson 3490
+ *
+ * Revision 1.59 2005/11/02 22:12:54 oliver-guest
+ * Correct cut'n'paste error
+ *
+ * Revision 1.58 2005/11/02 19:22:06 oliver-guest
+ * Fixes for Benq 5000
+ *
+ * Revision 1.57 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.56 2005/10/24 19:46:40 oliver-guest
+ * Preview and range fix for Epson 2480/2580
+ *
+ * Revision 1.55 2005/10/23 21:28:58 oliver-guest
+ * Fix for buffer size in high res modes, fixes for delay code
+ *
+ * Revision 1.54 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.53 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.52 2005/09/28 21:33:11 oliver-guest
+ * Added 16 bit option for Epson scanners (untested)
+ *
+ * Revision 1.51 2005/08/15 18:56:55 oliver-guest
+ * Added temporary debug code for 2480/2580 distinction
+ *
+ * Revision 1.50 2005/08/15 18:06:37 oliver-guest
+ * Added support for Epson 3490/3590 (thanks to Matt Judge)
+ *
+ * Revision 1.49 2005/08/07 12:37:29 oliver-guest
+ * Use first known device if no device is specified
+ *
+ * Revision 1.48 2004/12/09 23:21:48 oliver-guest
+ * Added quality calibration for Epson 2480 (by Simon Munton)
+ *
+ * Revision 1.47 2004/12/01 22:49:14 oliver-guest
+ * Fix for allocation of gamma tables by Simon Munton
+ *
+ * Revision 1.46 2004/12/01 22:12:03 oliver-guest
+ * Added support for Epson 1270
+ *
+ * Revision 1.45 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.44 2004/09/02 20:59:12 oliver-guest
+ * Added support for Epson 2480
+ *
+ * Revision 1.43 2004/06/16 19:52:26 oliver-guest
+ * Don't enforce even number of URB packages on 1212u_2. Fixes bug #300753.
+ *
+ * Revision 1.42 2004/06/15 12:17:37 hmg-guest
+ * Only use __attribute__ if gcc is used for compilation. Some other compilers
+ * don't know __attribute__ and therefore can't compile sane-backends without this
+ * fix. See bug #300803.
+ *
+ * Revision 1.41 2004/05/26 22:37:01 oliver-guest
+ * Use shared memory for urb counters in snapscan backend
+ *
+ * Revision 1.40 2004/04/09 11:59:02 oliver-guest
+ * Fixes for pthread implementation
+ *
+ * Revision 1.39 2004/04/08 21:53:10 oliver-guest
+ * Use sanei_thread in snapscan backend
+ *
+ * Revision 1.37 2003/11/27 23:11:32 oliver-guest
+ * Send gamma table twice for Epson Perfection 1670
+ *
+ * Revision 1.36 2003/11/09 21:43:45 oliver-guest
+ * Disabled quality calibration for Epson Perfection 1670
+ *
+ * Revision 1.35 2003/11/07 23:26:49 oliver-guest
+ * Final bugfixes for bascic support of Epson 1670
+ *
+ * Revision 1.34 2003/10/21 20:43:25 oliver-guest
+ * Bugfixes for SnapScan backend
+ *
+ * Revision 1.33 2003/10/07 18:29:20 oliver-guest
+ * Initial support for Epson 1670, minor bugfix
+ *
+ * Revision 1.32 2003/09/24 18:05:39 oliver-guest
+ * Bug #300198: Check second argument of sanei_config_get_string
+ *
+ * Revision 1.31 2003/09/12 16:10:33 hmg-guest
+ * Moved union Option_Value from backend header files to sanei_backend.h. No need
+ * to copy it over and over again. Changed header inclusion order in backend
+ * files to include backend.h after sanei_backend.h. Based on a patch from stef
+ * <stef-listes@wanadoo.fr>.
+ *
+ * Revision 1.30 2003/08/19 21:05:08 oliverschwartz
+ * Scanner ID cleanup
+ *
+ * Revision 1.29 2003/04/30 20:49:40 oliverschwartz
+ * SnapScan backend 1.4.26
+ *
+ * Revision 1.58 2003/04/30 20:43:07 oliverschwartz
+ * Set backend version number to 1.4.26
+ *
+ * Revision 1.57 2003/04/02 21:17:14 oliverschwartz
+ * Fix for 1200 DPI with Acer 5000
+ *
+ * Revision 1.56 2003/02/08 10:45:09 oliverschwartz
+ * Use 600 DPI as optical resolution for Benq 5000
+ *
+ * Revision 1.55 2003/01/08 21:16:17 oliverschwartz
+ * Added support for Acer / Benq 310U
+ *
+ * Revision 1.54 2002/12/10 20:14:12 oliverschwartz
+ * Enable color offset correction for SnapScan300
+ *
+ * Revision 1.53 2002/10/31 19:29:41 oliverschwartz
+ * Set version to 1.4.17
+ *
+ * Revision 1.52 2002/10/12 10:40:48 oliverschwartz
+ * Added support for Snapscan e10
+ *
+ * Revision 1.51 2002/09/26 19:27:44 oliverschwartz
+ * Version 1.4.16
+ *
+ * Revision 1.50 2002/09/24 16:07:44 oliverschwartz
+ * Added support for Benq 5000
+ *
+ * Revision 1.49 2002/07/12 22:53:54 oliverschwartz
+ * Version 1.4.15
+ *
+ * Revision 1.48 2002/07/12 22:53:16 oliverschwartz
+ * call sanei_usb_init() before sanei_usb_attach_matching_devices()
+ *
+ * Revision 1.47 2002/06/06 21:16:23 oliverschwartz
+ * Set backend version to 1.4.14
+ *
+ * Revision 1.46 2002/06/06 20:40:01 oliverschwartz
+ * Changed default scan area for transparancy unit of SnapScan e50
+ *
+ * Revision 1.45 2002/05/02 18:29:34 oliverschwartz
+ * - Added ADF support
+ * - Fixed status handling after cancel
+ *
+ * Revision 1.44 2002/04/27 14:42:30 oliverschwartz
+ * Cleanup of debug logging
+ *
+ * Revision 1.43 2002/04/23 22:40:33 oliverschwartz
+ * Improve config file reading
+ *
+ * Revision 1.42 2002/04/10 21:00:09 oliverschwartz
+ * Check for NULL pointer before deleting device list
+ *
+ * Revision 1.41 2002/03/24 12:12:36 oliverschwartz
+ * - Moved option functions to snapscan-options.c
+ * - Autodetect USB scanners
+ * - Cleanup
+ *
+ * Revision 1.40 2002/02/09 14:55:23 oliverschwartz
+ * Added language translation support (SANE_I18N)
+ *
+ * Revision 1.39 2002/01/23 20:40:54 oliverschwartz
+ * Don't use quantization for scan area parameter
+ * Improve recognition of Acer 320U
+ * Version 1.4.7
+ *
+ * Revision 1.38 2002/01/14 21:11:56 oliverschwartz
+ * Add workaround for bug semctl() call in libc for PPC
+ *
+ * Revision 1.37 2002/01/10 21:33:12 oliverschwartz
+ * Set version number to 1.4.4
+ *
+ * Revision 1.36 2002/01/06 18:34:02 oliverschwartz
+ * Added support for Snapscan e42 thanks to Yari Ad� Petralanda
+ *
+ * Revision 1.35 2001/12/20 23:18:01 oliverschwartz
+ * Remove tmpfname
+ *
+ * Revision 1.34 2001/12/18 18:28:35 oliverschwartz
+ * Removed temporary file
+ *
+ * Revision 1.33 2001/12/12 19:43:30 oliverschwartz
+ * - Set version number to 1.4.3
+ * - Clean up CVS Log
+ *
+ * Revision 1.32 2001/12/09 23:06:45 oliverschwartz
+ * - use sense handler for USB if scanner reports CHECK_CONDITION
+ *
+ * Revision 1.31 2001/12/08 11:50:34 oliverschwartz
+ * Fix dither matrix computation
+ *
+ * Revision 1.30 2001/11/29 22:50:14 oliverschwartz
+ * Add support for SnapScan e52
+ *
+ * Revision 1.29 2001/11/27 23:16:17 oliverschwartz
+ * - Fix color alignment for SnapScan 600
+ * - Added documentation in snapscan-sources.c
+ * - Guard against TL_X < BR_X and TL_Y < BR_Y
+ *
+ * Revision 1.28 2001/11/25 18:51:41 oliverschwartz
+ * added support for SnapScan e52 thanks to Rui Lopes
+ *
+ * Revision 1.27 2001/11/16 20:28:35 oliverschwartz
+ * add support for Snapscan e26
+ *
+ * Revision 1.26 2001/11/16 20:23:16 oliverschwartz
+ * Merge with sane-1.0.6
+ * - Check USB vendor IDs to avoid hanging scanners
+ * - fix bug in dither matrix computation
+ *
+ * Revision 1.25 2001/10/25 11:06:22 oliverschwartz
+ * Change snapscan backend version number to 1.4.0
+ *
+ * Revision 1.24 2001/10/11 14:02:10 oliverschwartz
+ * Distinguish between e20/e25 and e40/e50
+ *
+ * Revision 1.23 2001/10/09 22:34:23 oliverschwartz
+ * fix compiler warnings
+ *
+ * Revision 1.22 2001/10/08 19:26:01 oliverschwartz
+ * - Disable quality calibration for scanners that do not support it
+ *
+ * Revision 1.21 2001/10/08 18:22:02 oliverschwartz
+ * - Disable quality calibration for Acer Vuego 310F
+ * - Use sanei_scsi_max_request_size as scanner buffer size
+ * for SCSI devices
+ * - Added new devices to snapscan.desc
+ *
+ * Revision 1.20 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.19 2001/09/17 10:01:08 sable
+ * Added model AGFA 1236U
+ *
+ * Revision 1.18 2001/09/10 10:16:32 oliverschwartz
+ * better USB / SCSI recognition, correct max scan area for 1236+TPO
+ *
+ * Revision 1.17 2001/09/09 18:06:32 oliverschwartz
+ * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
+ *
+ * Revision 1.16 2001/09/07 09:42:13 oliverschwartz
+ * Sync with Sane-1.0.5
+ *
+ * Revision 1.15 2001/05/15 20:51:14 oliverschwartz
+ * check for pss->devname instead of name in sane_open()
+ *
+ * Revision 1.14 2001/04/10 13:33:06 sable
+ * Transparency adapter bug and xsane crash corrections thanks to Oliver Schwartz
+ *
+ * Revision 1.13 2001/04/10 13:00:31 sable
+ * Moving sanei_usb_* to snapscani_usb*
+ *
+ * Revision 1.12 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.11 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.4 2001/03/04 16:50:53 mikael
+ * Added Scan Mode, Geometry, Enhancement and Advanced groups. Implemented brightness and contrast controls with gamma tables. Added Quality Calibration, Analog Gamma Bind, Custom Gamma and Gamma Vector GS,R,G,B options.
+ *
+ * Revision 1.3 2001/02/16 18:32:28 mikael
+ * impl calibration, signed position, increased buffer size
+ *
+ * Revision 1.2 2001/02/10 18:18:29 mikael
+ * Extended x and y ranges
+ *
+ * Revision 1.1.1.1 2001/02/10 17:09:29 mikael
+ * Imported from snapscan-11282000.tar.gz
+ *
+ * Revision 1.10 2000/11/10 01:01:59 sable
+ * USB (kind of) autodetection
+ *
+ * Revision 1.9 2000/11/01 01:26:43 sable
+ * Support for 1212U
+ *
+ * Revision 1.8 2000/10/30 22:31:13 sable
+ * Auto preview mode
+ *
+ * Revision 1.7 2000/10/28 14:16:10 sable
+ * Bug correction for SnapScan310
+ *
+ * Revision 1.6 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.5 2000/10/15 19:52:06 cbagwell
+ * Changed USB support to a 1 line modification instead of multi-file
+ * changes.
+ *
+ * Revision 1.4 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ *
+ * Revision 1.3 2000/08/12 15:09:35 pere
+ * Merge devel (v1.0.3) into head branch.
+ *
+ * Revision 1.1.1.1.2.5 2000/07/29 16:04:33 hmg
+ * 2000-07-29 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/GUIDE: Added some comments about portability and
+ * documentation.
+ * * backend/abaton.c backend/agfafocus.c backend/apple.c
+ * backend/canon.c backend/coolscan.c backend/dc210.c backend/dc25.c
+ * backend/dll.c backend/dmc.c backend/microtek.c backend/microtek2.c
+ * backend/microtek2.c backend/mustek_pp.c backend/net.c backend/pint.c
+ * backend/pnm.c backend/qcam.c backend/ricoh.c backend/s9036.c
+ * backend/sane_strstatus.c backend/sharp.c backend/snapscan.c
+ * backend/st400.c backend/stubs.c backend/tamarack.c backend/v4l.c:
+ * Changed include statements from #include <sane/...> to
+ * #include "sane...".
+ * * backend/avision.c backend/dc25.c: Use DBG(0, ...) instead of
+ * fprintf (stderr, ...)
+ * * backend/avision.c backend/canon-sane.c backend/coolscan.c
+ * backend/dc25.c backend/microtek.c backend/microtek2.c
+ * backend/st400.c: Use sanei_config_read() instead of fgets().
+ * * backend/coolscan.desc backend/microtek.desc backend/microtek2.desc
+ * backend/st400.desc: Added :interface and :manpage entries.
+ * * backend/nec.desc: Status is beta now (was: new). Fixed typo.
+ * * doc/canon.README: Removed, because the information is included in
+ * the manpage now.
+ * * doc/Makefile.in: Added sane-coolscan to list of mapages to install.
+ * * README: Added Link to coolscan manpage.
+ * * backend/mustek.*: Update to Mustek backend 1.0-94. Fixed the
+ * #include <sane/...> bug.
+ *
+ * Revision 1.1.1.1.2.4 2000/07/25 21:47:43 hmg
+ * 2000-07-25 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/snapscan.c: Use DBG(0, ...) instead of fprintf (stderr, ...).
+ * * backend/abaton.c backend/agfafocus.c backend/apple.c backend/dc210.c
+ * backend/dll.c backend/dmc.c backend/microtek2.c backend/pint.c
+ * backend/qcam.c backend/ricoh.c backend/s9036.c backend/snapscan.c
+ * backend/tamarack.c: Use sanei_config_read instead of fgets.
+ * * backend/dc210.c backend/microtek.c backend/pnm.c: Added
+ * #include "../include/sane/config.h".
+ * * backend/dc25.c backend/m3096.c backend/sp15.c
+ * backend/st400.c: Moved #include "../include/sane/config.h" to the beginning.
+ * * AUTHORS: Changed agfa to agfafocus.
+ *
+ * Revision 1.1.1.1.2.3 2000/07/17 21:37:28 hmg
+ * 2000-07-17 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/snapscan.c backend/snapscan-scsi.c: Replace C++ comment
+ * with C comment.
+ *
+ * Revision 1.1.1.1.2.2 2000/07/13 04:47:46 pere
+ * New snapscan backend version dated 20000514 from Steve Underwood.
+ *
+ * Revision 1.2 2000/05/14 13:30:20 coppice
+ * R, G and B images now merge correctly. Still some outstanding issues,
+ * but a lot more useful than before.
+ *
+ * Revision 1.2 2000/03/05 13:55:20 pere
+ * Merged main branch with current DEVEL_1_9.
+ *
+ * Revision 1.1.1.1.2.1 1999/09/15 18:20:44 charter
+ * Early version 1.0 snapscan.c
+ *
+ * Revision 2.2 1999/09/09 18:22:45 charter
+ * Checkpoint. Now using Sources for scanner data, and have removed
+ * references to the old snapscan-310.c stuff. This stuff must still
+ * be incorporated into the RGBRouter to get trilinear CCD SnapScan
+ * models working.
+ *
+ * Revision 2.1 1999/09/08 03:07:05 charter
+ * Start of branch 2; same as 1.47.
+ *
+ * Revision 1.47 1999/09/08 03:03:53 charter
+ * The actions for the scanner command options now use fprintf for
+ * printing, rather than DGB. I want the output to come out no matter
+ * what the value of the snapscan debug level.
+ *
+ * Revision 1.46 1999/09/07 20:53:41 charter
+ * Changed expected_data_len to bytes_remaining.
+ *
+ * Revision 1.45 1999/09/06 23:32:37 charter
+ * Split up sane_start() into sub-functions to improve readability (again).
+ * Introduced actual_mode() and is_colour_mode() (again).
+ * Fixed problems with cancellation. Works fine with my system now.
+ *
+ * Revision 1.44 1999/09/02 05:28:01 charter
+ * Added Gary Plewa's name to the list of copyrighted contributors.
+ *
+ * Revision 1.43 1999/09/02 05:23:54 charter
+ * Added Gary Plewa's patch for the Acer PRISA 620s.
+ *
+ * Revision 1.42 1999/09/02 02:05:34 charter
+ * Check-in of revision 1.42 (release 0.7 of the backend).
+ * This is part of the recovery from the great disk crash of Sept 1, 1999.
+ *
+ * Revision 1.42 1999/07/09 22:37:55 charter
+ * Potential bugfix for problems with sane_get_parameters() and
+ * the new generic scsi driver (suggested by Francois Desarmeni,
+ * Douglas Gilbert, Abel Deuring).
+ *
+ * Revision 1.41 1999/07/09 20:58:07 charter
+ * Changes to support SnapScan 1236s (Petter Reinholdsten).
+ *
+ * Revision 1.40 1998/12/16 18:43:06 charter
+ * Fixed major version problem precipitated by release of SANE-1.00.
+ *
+ * Revision 1.39 1998/09/07 06:09:26 charter
+ * Formatting (whitespace) changes.
+ *
+ * Revision 1.38 1998/09/07 06:06:01 charter
+ * Merged in Wolfgang Goeller's changes (Vuego 310S, bugfixes).
+ *
+ * Revision 1.37 1998/08/06 06:16:39 charter
+ * Now using sane_config_attach_matching_devices() in sane_snapscan_init().
+ * Change contributed by David Mosberger-Tang.
+ *
+ * Revision 1.36 1998/05/11 17:02:53 charter
+ * Added Mikko's threshold stuff.
+ *
+ * Revision 1.35 1998/03/10 23:43:23 eblot
+ * Bug correction
+ *
+ * Revision 0.72 1998/03/10 23:40:42 eblot
+ * More support for 310/600 models: color preview, large window
+ *
+ * Revision 1.35 1998/03/10 21:32:07 eblot
+ * Debugging
+ *
+ * Revision 1.34 1998/02/15 21:55:53 charter
+ * From Emmanuel Blot:
+ * First routines to support SnapScan 310 scanned data.
+ *
+ * Revision 1.33 1998/02/06 02:30:28 charter
+ * Now using a mode enum (instead of the static string pointers directly).
+ * Now check for the SnapScan 310 and 600 explicitly (start of support
+ * for these models).
+ *
+ * Revision 1.32 1998/02/01 21:56:48 charter
+ * Patches to fix compilation problems on Solaris supplied by
+ * Jim McBeath.
+ *
+ * Revision 1.31 1998/02/01 03:36:40 charter
+ * Now check for BRX < TLX and BRY < TLY and whether the area of the
+ * scanning window is approaching zero in set_window. I'm setting a
+ * minimum window size of 75x75 hardware pixels (0.25 inches a side).
+ * If the area falls to zero, the scanner seems to hang in the middle
+ * of the set_window command.
+ *
+ * Revision 1.30 1998/02/01 00:00:33 charter
+ * TLX, TLY, BRX and BRY are now lengths expressed in mm. The frontends
+ * can now allow changes in the units, and units that are more user-
+ * friendly.
+ *
+ * Revision 1.29 1998/01/31 21:09:19 charter
+ * Fixed another problem with add_device(): if mini_inquiry ends
+ * up indirectly invoking the sense handler, there'll be a segfault
+ * because the sense_handler isn't set. Had to fix sense_handler so
+ * it can handle a NULL pss pointer and then use the sanei_scsi stuff
+ * everywhere. This error is most likely to occur if the scanner is
+ * turned off.
+ *
+ * Revision 1.28 1998/01/31 18:45:22 charter
+ * Last fix botched, produced a compile error. Thought I'd already
+ * compiled successfully.
+ *
+ * Revision 1.27 1998/01/31 18:32:42 charter
+ * Fixed stupid bug in add_device that causes segfault when no snapscan
+ * found: closing a scsi fd opened with open() using sanei_scsi_close().
+ *
+ * Revision 1.26 1998/01/30 21:19:02 charter
+ * sane_snapscan_init() handles failure of add_device() in the same
+ * way when there is no snapscan.conf file available as when there is
+ * one.
+ *
+ * Revision 1.25 1998/01/30 19:41:11 charter
+ * Waiting for child process termination at regular end of scan (not
+ * just on cancellation); before I was getting zombies.
+ *
+ * Revision 1.24 1998/01/30 19:19:27 charter
+ * Changed from strncmp() to strncasecmp() to do vendor and model
+ * comparisons in sane_snapscan_init. There are some snapcsan models
+ * that use lower case.
+ * Now have debug level defines instead of raw numbers, and better debug
+ * information categories.
+ * Don't complain at debug level 0 when a snapscan isn't found on a
+ * requested device.
+ * Changed CHECK_STATUS to take caller parameter instead of always
+ * assuming an available string "me".
+ *
+ * Revision 1.23 1998/01/30 11:03:04 charter
+ * Fixed * vs [] operator precedence screwup in sane_snapscan_get_devices()
+ * that caused a segfault in scanimage -h.
+ * Fixed problem with not closing the scsi fd between certain commands
+ * that caused scanimage to hang; now using open_scanner() and close_scanner().
+ *
+ * Revision 1.22 1998/01/28 09:02:55 charter
+ * Fixed bug: zero allocation length in request sense command buffer
+ * was preventing sense information from being received. The
+ * backend now correctly waits for the scanner to warm up.
+ * Now using the hardware configuration byte to check whether
+ * both 8x8 and 16x16 halftoning should be made available.
+ *
+ * Revision 1.21 1998/01/25 09:57:57 charter
+ * Added more SCSI command buttons (and a group for them).
+ * Made the output of the Inquiry command a bit nicer.
+ *
+ * Revision 1.20 1998/01/25 08:53:14 charter
+ * Have added bi-level colour mode, with halftones too.
+ * Can now select preview mode (but it's an advanced option, since
+ * you usually don't want to do it).
+ *
+ * Revision 1.19 1998/01/25 02:25:02 charter
+ * Fixed bug: preview mode gives blank image at initial startup.
+ * Fixed bug: lineart mode goes weird after a preview or gs image.
+ * More changes to option relationships;
+ * now using test_unit_ready and send_diagnostic in sane_snapscan_open().
+ * Added negative option.
+ *
+ * Revision 1.18 1998/01/24 05:15:32 charter
+ * Now have RGB gamma correction and dispersed-dot dither halftoning
+ * for BW images. Cleaned up some spots in the code and have set up
+ * option interactions a bit better (e.g. halftoning and GS gamma
+ * correction made inactive in colour mode, etc). TL_[XY] and BR_[XY]
+ * now change in ten-pixel increments (I had problems with screwed-up
+ * scan lines when the dimensions were weird at low res... could be the
+ * problem).
+ *
+ * Revision 1.17 1998/01/23 13:03:17 charter
+ * Several changes, all aimed at getting scanning performance working
+ * correctly, and the progress/cancel window functioning. Cleaned up
+ * a few nasty things as well.
+ *
+ * Revision 1.16 1998/01/23 07:40:23 charter
+ * Reindented using GNU convention at David Mosberger-Tang's request.
+ * Also applied David's patch fixing problems on 64-bit architectures.
+ * Now using scanner's reported speed to guage amount of data to request
+ * in a read on the scsi fd---nonblocking mode operates better now.
+ * Fixed stupid bug I introduced in preview mode data transfer.
+ *
+ * Revision 1.15 1998/01/22 06:18:57 charter
+ * Raised the priority of a couple of DBG messages in reserve_unit()
+ * and release_unit(), and got rid of some unecessary ones.
+ *
+ * Revision 1.14 1998/01/22 05:15:35 charter
+ * Have replaced the bit depth option with a mode option; various
+ * changes associated with that.
+ * Also, I again close the STDERR_FILENO in the reader child and
+ * dup the STDOUT file descriptor onto it. This prevents an "X io"
+ * error when the child exits, while still allowing the use of
+ * DBG.
+ *
+ * Revision 1.13 1998/01/21 20:41:22 charter
+ * Added copyright info.
+ * Also now seem to have cancellation working. This requires using a
+ * new scanner state variable and checking in all the right places
+ * in the reader child and the sane_snapscan_read function. I've
+ * tested it using both blocking and nonblocking I/O and it seems
+ * to work both ways.
+ * I've also switched to GTK+-0.99.2 and sane-0.69, and the
+ * mysterious problems with the preview window have disappeared.
+ * Problems with scanimage doing weird things to options have also
+ * gone away and the frontends seem more stable.
+ *
+ * Revision 1.12 1998/01/21 11:05:53 charter
+ * Inoperative code largely #defined out; I had the preview window
+ * working correctly by having the window coordinates properly
+ * constrained, but now the preview window bombs with a floating-
+ * point error each time... I'm not sure yet what happened.
+ * I've also figured out that we need to use reserve_unit and
+ * release_unit in order to cancel scans in progress. This works
+ * under scanimage, but I can't seem to find a way to fit cancellation
+ * into xscanimage properly.
+ *
+ * Revision 1.11 1998/01/20 22:42:08 charter
+ * Applied Franck's patch from Dec 17; preview mode is now grayscale.
+ *
+ * Revision 1.10 1997/12/10 23:33:12 charter
+ * Slight change to some floating-point computations in the brightness
+ * and contrast stuff. The controls don't seem to do anything to the
+ * scanner though (I think these aren't actually supported in the
+ * SnapScan).
+ *
+ * Revision 1.9 1997/11/26 15:40:50 charter
+ * Brightness and contrast added by Michel.
+ *
+ * Revision 1.8 1997/11/12 12:55:40 charter
+ * No longer exec after forking to do nonblocking scanning; found how
+ * to fix the problems with SIGPIPEs from before.
+ * Now support a config file like the other scanner drivers, and
+ * can check whether a given device is an AGFA SnapScan (mini_inquiry()).
+ *
+ * Revision 1.7 1997/11/10 05:52:08 charter
+ * Now have the child reader process and pipe stuff working, and
+ * nonblocking mode. For large scans the nonblocking mode actually
+ * seems to cut down on cpu hogging (though there is still a hit).
+ *
+ * Revision 1.6 1997/11/03 07:45:54 charter
+ * Added the predef_window stuff. I've tried it with 6x4, and it seems
+ * to work; I think something gets inconsistent if a preview is
+ * performed though.
+ *
+ * Revision 1.5 1997/11/03 03:15:27 charter
+ * Global static variables have now become part of the scanner structure;
+ * the inquiry command automatically retrieves window parameters into
+ * scanner structure members. Things are a bit cleaned up.
+ *
+ * Revision 1.4 1997/11/02 23:35:28 charter
+ * After much grief.... I can finally scan reliably. Now it's a matter
+ * of getting the band arrangement sorted out.
+ *
+ * Revision 1.3 1997/10/30 07:36:37 charter
+ * Fixed a stupid bug in the #defines for the inquiry command, pointed out
+ * by Franck.
+ *
+ * Revision 1.2 1997/10/14 06:00:11 charter
+ * Option manipulation and some basic SCSI commands done; the basics
+ * for scanning are written but there are bugs. A full scan always hangs
+ * the SCSI driver, and preview mode scans complete but it isn't clear
+ * whether any meaningful data is received.
+ *
+ * Revision 1.1 1997/10/13 02:25:54 charter
+ * Initial revision
+ * */
diff --git a/backend/snapscan.conf.in b/backend/snapscan.conf.in
new file mode 100644
index 0000000..a55087f
--- /dev/null
+++ b/backend/snapscan.conf.in
@@ -0,0 +1,115 @@
+#------------------------------ General -----------------------------------
+
+# Change to the fully qualified filename of your firmware file, if
+# firmware upload is needed by the scanner
+firmware @DATADIR@/sane/snapscan/your-firmwarefile.bin
+
+# If not automatically found you may manually specify a device name.
+
+# For USB scanners also specify bus=usb, e.g.
+# /dev/usb/scanner0 bus=usb
+
+# For SCSI scanners specify the generic device, e.g. /dev/sg0 on Linux.
+# /dev/sg0
+
+#---------------------------------------------------------------------------
+# No changes should be necessary below this line
+#---------------------------------------------------------------------------
+
+#-------------------------- SCSI scanners ----------------------------------
+# These SCSI devices will be probed automatically
+scsi AGFA * Scanner
+scsi COLOR * Scanner
+scsi Color * Scanner
+scsi ACERPERI * Scanner
+
+#--------------------------- USB scanners -----------------------------------
+# These USB devices will be probed automatically
+# (This will currently work only on Linux)
+
+# Benq/Acer/Vuego 310U
+usb 0x04a5 0x1a20
+usb 0x04a5 0x1a26
+
+# Benq/Acer/Vuego 320U
+usb 0x04a5 0x2022
+
+# Benq/Acer/Vuego 620U / 620UT
+usb 0x04a5 0x1a2a
+usb 0x04a5 0x2040
+
+# Benq/Acer/Vuego 640U
+usb 0x04a5 0x2060
+
+# Benq/Acer/Vuego 640BU
+usb 0x04a5 0x207e
+
+# Benq/Acer/Vuego 640BT
+usb 0x04a5 0x20be
+
+# Benq/Acer/Vuego 1240U
+usb 0x04a5 0x20c0
+
+# Benq/Acer/Vuego 3300 / 4300
+usb 0x04a5 0x20b0
+
+# Benq/Acer/Vuego 4300
+usb 0x04a5 0x20de
+
+# Benq 5000E / 5000U
+usb 0x04a5 0x20f8
+
+# Benq 5000
+usb 0x04a5 0x20fc
+
+# Benq/Acer 5300
+usb 0x04a5 0x20fe
+
+# Benq 5250C
+usb 0x04a5 0x2137
+
+# Agfa 1236U
+usb 0x06bd 0x0002
+
+# Agfa 1212U
+usb 0x06bd 0x0001
+usb 0x06bd 0x2061
+
+# Agfa Snapscan e10
+usb 0x06bd 0x2093
+
+# Agfa Snapscan e20
+usb 0x06bd 0x2091
+
+# Agfa Snapscan e25
+usb 0x06bd 0x2095
+
+# Agfa Snapscan e26
+usb 0x06bd 0x2097
+
+# Agfa Snapscan e40
+usb 0x06bd 0x208d
+
+# Agfa Snapscan e42
+usb 0x06bd 0x20ff
+
+# Agfa Snapscan e50
+usb 0x06bd 0x208f
+
+# Agfa Snapscan e52
+usb 0x06bd 0x20fd
+
+# Epson Perfection 660
+usb 0x04b8 0x0114
+
+# Epson Perfection 1670
+usb 0x04b8 0x011f
+
+# Epson Perfection 2480
+usb 0x04b8 0x0121
+
+# Epson Perfection 3490
+usb 0x04b8 0x0122
+
+# Epson Stylus CX-1500
+usb 0x04b8 0x080c
diff --git a/backend/snapscan.h b/backend/snapscan.h
new file mode 100644
index 0000000..2795d88
--- /dev/null
+++ b/backend/snapscan.h
@@ -0,0 +1,751 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998, 1999, 2001, 2002, 2013 Franck Schnefra,
+ Michel Roelofs, Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang,
+ Wolfgang Goeller, Petter Reinholdtsen, Gary Plewa, Sebastien Sable,
+ Mikael Magnusson, Andrew Goodbody, Oliver Schwartz and Kevin Charter
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+/* $Id$
+ SANE SnapScan backend */
+
+#ifndef snapscan_h
+#define snapscan_h
+
+#include "../include/sane/sane.h"
+
+#define UNREFERENCED_PARAMETER(x) ((void) x)
+
+/* snapscan device field values */
+
+#define DEFAULT_DEVICE "/dev/scanner" /* Check this if config is missing */
+#define SNAPSCAN_TYPE "flatbed scanner"
+#define SNAPSCAN_FS_TYPE "film scanner"
+#define TMP_FILE_PREFIX "/var/tmp/snapscan"
+#define SNAPSCAN_CONFIG_FILE "snapscan.conf"
+#define FIRMWARE_KW "firmware"
+#define OPTIONS_KW "options"
+
+/* Define the colour channel order in arrays */
+#define R_CHAN 0
+#define G_CHAN 1
+#define B_CHAN 2
+
+typedef enum
+{
+ UNKNOWN_BUS,
+ SCSI,
+ USB
+} SnapScan_Bus;
+
+typedef enum
+{
+ UNKNOWN,
+ SNAPSCAN, /* the original SnapScan */
+ SNAPSCAN300, /* the SnapScan 300 */
+ SNAPSCAN310, /* the SnapScan 310 */
+ SNAPSCAN600, /* the SnapScan 600 */
+ SNAPSCAN1236, /* the SnapScan 1236 */
+ SNAPSCAN1212U,
+ SNAPSCANE20, /* SnapScan e20/e25, 600 DPI */
+ SNAPSCANE50, /* SnapScan e40/e50, 1200 DPI */
+ SNAPSCANE52, /* SnapScan e52, 1200 DPI, no quality calibration */
+ ACER300F,
+ PRISA310, /* Acer ScanPrisa 310 - 300 DPI */
+ PRISA610, /* Acer ScanPrisa 610 - 600 DPI */
+ PRISA620, /* Acer ScanPrisa 620 - 600 DPI */
+ PRISA640, /* Acer ScanPrisa 640 - 600 DPI */
+ PRISA1240, /* Acer ScanPrisa 1240 - 1200 DPI */
+ PRISA4300, /* Acer ScanPrisa 3300/4300 - 600 DPI */
+ PRISA4300_2, /* Acer ScanPrisa 3300/4300 - 600 DPI, 42 bit*/
+ PRISA5000, /* Acer ScanPrisa 5000 - 1200 DPI */
+ PRISA5000E, /* Acer ScanPrisa 5000E/5000U - 1200 DPI */
+ PRISA5150, /* Benq 5150 - 1200 DPI */
+ PRISA5300, /* Acer ScanPrisa 5300 - 1200 DPI */
+ PERFECTION660, /* Epson Perfection 660 - 1200 DPI */
+ PERFECTION1270, /* Epson Perfection 1270 - 1600 DPI */
+ PERFECTION1670, /* Epson Perfection 1670 - 1600 DPI */
+ PERFECTION2480, /* Epson Perfection 2480 - 2400 DPI */
+ PERFECTION3490, /* Epson Perfection 3490 - 3200 DPI */
+ STYLUS_CX1500, /* Epson Stylus CX 1500 - 600 DPI */
+ ARCUS1200, /* Agfa Arcus 1200 - 1200 DPI (rebadged Acer?) */
+ SCANWIT2720S /* BenQ ScanWit 2720S film scanner 2700 DPI */
+} SnapScan_Model;
+
+struct SnapScan_Driver_desc {
+ SnapScan_Model id;
+ char *driver_name;
+};
+
+static struct SnapScan_Driver_desc drivers[] =
+{
+ /* enum value -> Driver name */
+ {UNKNOWN, "Unknown"},
+ {SNAPSCAN, "SnapScan"},
+ {SNAPSCAN300, "SnapScan300"},
+ {SNAPSCAN310, "SnapScan310"},
+ {SNAPSCAN600, "SnapScan600"},
+ {SNAPSCAN1236, "SnapScan1236"},
+ {SNAPSCAN1212U, "SnapScan1212"},
+ {SNAPSCANE20, "SnapScanE20"},
+ {SNAPSCANE50, "SnapScanE50"},
+ {SNAPSCANE52, "SnapScanE52"},
+ {ACER300F, "Acer300"},
+ {PRISA310, "Acer310"},
+ {PRISA610, "Acer610"},
+ {PRISA620, "Acer620"},
+ {PRISA640, "Acer640"},
+ {PRISA4300, "Acer4300"},
+ {PRISA4300_2, "Acer4300 (42 bit)"},
+ {PRISA1240, "Acer1240"},
+ {PRISA5000E, "Benq 5000E/5000U"},
+ {PRISA5000, "Benq 5000"},
+ {PRISA5150, "Benq 5150 / 5250"},
+ {PRISA5300, "Benq 5300"},
+ {ARCUS1200, "Arcus1200"},
+ {PERFECTION660, "Perfection 660"},
+ {PERFECTION1270, "Perfection 1270"},
+ {PERFECTION1670, "Perfection 1670"},
+ {PERFECTION2480, "Perfection 2480"},
+ {PERFECTION3490, "Perfection 3490"},
+ {STYLUS_CX1500, "Stylus CX 1500"},
+ {SCANWIT2720S, "BenQ ScanWit 2720S"}
+};
+
+#define known_drivers ((int) (sizeof(drivers)/sizeof(drivers[0])))
+
+struct SnapScan_Model_desc
+{
+ char *scsi_name;
+ SnapScan_Model id;
+};
+
+static struct SnapScan_Model_desc scanners[] =
+{
+ /* SCSI model name -> enum value */
+ {"FlatbedScanner_2", PRISA310},
+ {"FlatbedScanner_4", PRISA610},
+ {"FlatbedScanner_5", PRISA620},
+ {"FlatbedScanner_7", PRISA310},
+ {"FlatbedScanner_9", PRISA620},
+ {"FlatbedScanner13", PRISA620},
+ {"FlatbedScanner16", PRISA620},
+ {"FlatbedScanner17", PRISA620},
+ {"FlatbedScanner18", PRISA620},
+ {"FlatbedScanner19", PRISA1240},
+ {"FlatbedScanner20", PRISA640},
+ {"FlatbedScanner21", PRISA4300},
+ {"FlatbedScanner22", PRISA4300_2},
+ {"FlatbedScanner23", PRISA4300_2},
+ {"FlatbedScanner24", PRISA5300},
+ {"FlatbedScanner25", PRISA5000E},/* 5000E/5000U */
+ {"FlatbedScanner40", PRISA5150}, /* 5150 / 5250 */
+ {"FlatbedScanner42", PRISA5000}, /* 5000 */
+ {"SNAPSCAN 1212U", SNAPSCAN1212U},
+ {"SNAPSCAN 1212U_2", SNAPSCAN1212U},
+ {"SNAPSCAN e10", SNAPSCANE20},
+ {"SNAPSCAN e20", SNAPSCANE20},
+ {"SNAPSCAN e25", SNAPSCANE20},
+ {"SNAPSCAN e26", SNAPSCANE20},
+ {"SNAPSCAN e40", SNAPSCANE50},
+ {"SNAPSCAN e42", SNAPSCANE52},
+ {"SNAPSCAN e50", SNAPSCANE50},
+ {"SNAPSCAN e52", SNAPSCANE52},
+ {"SNAPSCAN 1236", SNAPSCAN1236},
+ {"SNAPSCAN 1236U", SNAPSCAN1236},
+ {"SNAPSCAN 300", SNAPSCAN300},
+ {"SNAPSCAN 310", SNAPSCAN310},
+ {"SNAPSCAN 600", SNAPSCAN600},
+ {"SnapScan", SNAPSCAN},
+ {"ACERSCAN_A4____1", ACER300F},
+ {"Perfection 660", PERFECTION660},
+ {"EPSON Scanner", PERFECTION1670}, /* dummy entry to detect scanner */
+ {"EPSON Scanner1", PERFECTION2480}, /* dummy entry to detect scanner */
+ {"EPSON Scanner2", PERFECTION3490}, /* dummy entry to detect scanner */
+ {"EPSON MFP00", STYLUS_CX1500},
+ {"ARCUS 1200", ARCUS1200},
+ {"FilmScanner____1", SCANWIT2720S}
+};
+
+#define known_scanners ((int) (sizeof(scanners)/sizeof(scanners[0])))
+
+static char *vendors[] =
+{
+ /* SCSI Vendor name */
+ "AGFA",
+ "COLOR",
+ "Color",
+ "ACERPER",
+ "EPSON"
+};
+#define known_vendors ((int) (sizeof(vendors)/sizeof(vendors[0])))
+
+/* Known vendor IDs */
+#define USB_VENDOR_AGFA 0x06bd
+#define USB_VENDOR_ACER 0x04a5
+#define USB_VENDOR_EPSON 0x04b8
+
+/* Agfa product IDs */
+#define USB_PRODUCT_1212U2 0x2061
+/* Acer product IDs */
+#define USB_PRODUCT_PRISA310 0x1a20
+#define USB_PRODUCT_PRISA320 0x2022
+/* Epson product IDs */
+#define USB_PRODUCT_PERFECTION660 0x0114
+#define USB_PRODUCT_PERFECTION1270 0x0120
+#define USB_PRODUCT_PERFECTION1670 0x011f
+#define USB_PRODUCT_PERFECTION2480 0x0121
+#define USB_PRODUCT_PERFECTION3490 0x0122
+
+static SANE_Word usb_vendor_ids[] =
+{
+ /* USB Vendor IDs */
+ USB_VENDOR_AGFA, /* Agfa */
+ USB_VENDOR_ACER, /* Acer */
+ USB_VENDOR_EPSON /* Epson */
+};
+#define known_usb_vendor_ids ((int) (sizeof(usb_vendor_ids)/sizeof(usb_vendor_ids[0])))
+
+struct SnapScan_USB_Model_desc
+{
+ SANE_Word vendor_id;
+ SANE_Word product_id;
+ SnapScan_Model id;
+};
+
+static struct SnapScan_USB_Model_desc usb_scanners[] =
+{
+ {USB_VENDOR_ACER, USB_PRODUCT_PRISA310, PRISA310}, /* Acer 310U */
+ {USB_VENDOR_ACER, USB_PRODUCT_PRISA320, PRISA310}, /* Acer 320U */
+ {USB_VENDOR_EPSON, USB_PRODUCT_PERFECTION660, PERFECTION660}, /* Epson Perfection 660 */
+ {USB_VENDOR_EPSON, USB_PRODUCT_PERFECTION1270, PERFECTION1270}, /* Epson Perfection 1270 */
+ {USB_VENDOR_EPSON, USB_PRODUCT_PERFECTION1670, PERFECTION1670}, /* Epson Perfection 1670 */
+ {USB_VENDOR_EPSON, USB_PRODUCT_PERFECTION2480, PERFECTION2480}, /* Epson Perfection 2480 */
+ {USB_VENDOR_EPSON, USB_PRODUCT_PERFECTION3490, PERFECTION3490} /* Epson Perfection 3490 */
+};
+#define known_usb_scanners ((int) (sizeof(usb_scanners)/sizeof(usb_scanners[0])))
+
+typedef enum
+{
+ OPT_COUNT = 0, /* option count */
+ OPT_MODE_GROUP, /* scan mode group */
+ OPT_SCANRES, /* scan resolution */
+ OPT_PREVIEW, /* preview mode toggle */
+ OPT_MODE, /* scan mode */
+ OPT_PREVIEW_MODE, /* preview mode */
+ OPT_HIGHQUALITY, /* scan quality (fast / high) */
+ OPT_SOURCE, /* scan source (flatbed / TPO) */
+ OPT_FRAME_NO, /* frame number for film scanner */
+ OPT_FOCUS_MODE, /* manual or auto focus for film scanner */
+ OPT_FOCUS_POINT, /* focus point for film scanner */
+ OPT_GEOMETRY_GROUP, /* geometry group */
+ OPT_TLX, /* top left x */
+ OPT_TLY, /* top left y */
+ OPT_BRX, /* bottom right x */
+ OPT_BRY, /* bottom right y */
+ OPT_PREDEF_WINDOW, /* predefined window configuration */
+ OPT_ENHANCEMENT_GROUP, /* enhancement group */
+ OPT_BIT_DEPTH, /* 8/16 bit/pixel for colour scans */
+ OPT_QUALITY_CAL, /* quality calibration */
+ OPT_HALFTONE, /* halftone flag */
+ OPT_HALFTONE_PATTERN, /* halftone matrix */
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables */
+ OPT_GAMMA_BIND, /* use same gamma value for all colors */
+ OPT_GAMMA_GS, /* gamma correction (greyscale) */
+ OPT_GAMMA_R, /* gamma correction (red) */
+ OPT_GAMMA_G, /* gamma correction (green) */
+ OPT_GAMMA_B, /* gamma correction (blue) */
+ OPT_GAMMA_VECTOR_GS, /* gamma correction vector (greyscale) */
+ OPT_GAMMA_VECTOR_R, /* gamma correction vector (red) */
+ OPT_GAMMA_VECTOR_G, /* gamma correction vector (green) */
+ OPT_GAMMA_VECTOR_B, /* gamma correction vector (blue) */
+ OPT_NEGATIVE, /* swap black and white */
+ OPT_THRESHOLD, /* threshold for line art */
+ OPT_BRIGHTNESS, /* brightness */
+ OPT_CONTRAST, /* contrast */
+ OPT_ADVANCED_GROUP, /* advanced group */
+ OPT_RGB_LPR, /* lines per scsi read (RGB) */
+ OPT_GS_LPR, /* lines per scsi read (GS) */
+ NUM_OPTS /* dummy (gives number of options) */
+} SnapScan_Options;
+
+typedef enum
+{
+ MD_COLOUR = 0, /* full colour */
+ MD_BILEVELCOLOUR, /* 1-bit per channel colour */
+ MD_GREYSCALE, /* grey scale */
+ MD_LINEART, /* black and white */
+ MD_NUM_MODES
+} SnapScan_Mode;
+
+typedef enum
+{
+ SRC_FLATBED = 0, /* Flatbed (normal) */
+ SRC_TPO, /* Transparency unit */
+ SRC_ADF
+} SnapScan_Source;
+
+typedef enum
+{
+ ST_IDLE, /* between scans */
+ ST_SCAN_INIT, /* scan initialization */
+ ST_SCANNING, /* actively scanning data */
+ ST_CANCEL_INIT /* cancellation begun */
+} SnapScan_State;
+
+typedef struct snapscan_device
+{
+ SANE_Device dev;
+ SANE_Range x_range; /* x dimension of scan area */
+ SANE_Range y_range; /* y dimension of scan area */
+ SnapScan_Model model; /* type of scanner */
+ SnapScan_Bus bus; /* bus of the device usb/scsi */
+ SANE_Char *firmware_filename; /* The name of the firmware file for USB scanners */
+ struct snapscan_device *pnext;
+}
+SnapScan_Device;
+
+#define MD_AUTO 0
+#define MD_MANUAL 1
+
+#define MAX_SCSI_CMD_LEN 256 /* not that large */
+#define DEFAULT_SCANNER_BUF_SZ 1024*63
+
+typedef struct snapscan_scanner SnapScan_Scanner;
+
+#include <snapscan-sources.h>
+
+struct snapscan_scanner
+{
+ SANE_String devname; /* the scsi device name */
+ SnapScan_Device *pdev; /* the device */
+ int fd; /* scsi file descriptor */
+ int opens; /* open count */
+ int rpipe[2]; /* reader pipe descriptors */
+ int orig_rpipe_flags; /* initial reader pipe flags */
+ SANE_Pid child; /* child reader process pid */
+ SnapScan_Mode mode; /* mode */
+ SnapScan_Mode preview_mode; /* preview mode */
+ SnapScan_Source source; /* scanning source */
+ SnapScan_State state; /* scanner state */
+ u_char cmd[MAX_SCSI_CMD_LEN]; /* scsi command buffer */
+ u_char *buf; /* data buffer */
+ size_t phys_buf_sz; /* physical buffer size */
+ size_t buf_sz; /* effective buffer size */
+ size_t expected_read_bytes; /* expected amount of data in a single read */
+ size_t read_bytes; /* amount of actual data read */
+ size_t bytes_remaining; /* remaining bytes expected from scanner */
+ size_t actual_res; /* actual resolution */
+ size_t lines; /* number of scan lines */
+ size_t bytes_per_line; /* bytes per scan line */
+ size_t pixels_per_line; /* pixels per scan line */
+ u_char hconfig; /* hardware configuration byte */
+ u_char hconfig_epson; /* additional hw configuration for some Epson scanners */
+ u_char hwst; /* hardware status byte */
+ float ms_per_line; /* speed: milliseconds per scan line */
+ SANE_Bool nonblocking; /* wait on reads for data? */
+ char *sense_str; /* sense string */
+ char *as_str; /* additional sense string */
+ u_char asi1; /* first additional sense info byte */
+ u_char asi2; /* second additional sense info byte */
+ SANE_Byte chroma_offset[3]; /* chroma offsets */
+ SANE_Int chroma;
+ Source *psrc; /* data source */
+ Source *preadersrc; /* data source for reader thread */
+ SANE_Option_Descriptor options[NUM_OPTS]; /* the option descriptors */
+ Option_Value val[NUM_OPTS]; /* the options themselves... */
+ SANE_Int res; /* resolution */
+ SANE_Int bpp; /* bit depth supported by scanner*/
+ SANE_Int bpp_scan; /* bit depth used for scanning */
+ SANE_Bool preview; /* preview mode toggle */
+ SANE_Bool highquality; /* high quality mode toggle */
+ SANE_String mode_s; /* scanning mode */
+ SANE_String source_s; /* scanning source */
+ SANE_String preview_mode_s; /* scanning mode for preview */
+ SANE_Fixed tlx; /* window top left x */
+ SANE_Fixed tly; /* window top left y */
+ SANE_Fixed brx; /* window bottom right x */
+ SANE_Fixed bry; /* window bottom right y */
+ int bright; /* brightness */
+ int contrast; /* contrast */
+ SANE_String predef_window; /* predefined window name */
+ SANE_Fixed gamma_gs; /* gamma correction value (greyscale) */
+ SANE_Fixed gamma_r; /* gamma correction value (red) */
+ SANE_Fixed gamma_g; /* gamma correction value (green) */
+ SANE_Fixed gamma_b; /* gamma correction value (blue) */
+ SANE_Int *gamma_tables; /* gamma correction vectors */
+ SANE_Int *gamma_table_gs; /* gamma correction vector (greyscale) */
+ SANE_Int *gamma_table_r; /* gamma correction vector (red) */
+ SANE_Int *gamma_table_g; /* gamma correction vector (green) */
+ SANE_Int *gamma_table_b; /* gamma correction vector (blue) */
+ int gamma_length; /* length of gamma vectors */
+ SANE_Bool halftone; /* halftone toggle */
+ SANE_String dither_matrix; /* the halftone dither matrix */
+ SANE_Bool negative; /* swap black and white */
+ SANE_Int threshold; /* threshold for line art */
+ SANE_Int rgb_lpr; /* lines per scsi read (RGB) */
+ SANE_Int gs_lpr; /* lines per scsi read (greyscale) */
+ SANE_Bool firmware_loaded; /* true if firmware was downloaded */
+ SANE_Word usb_vendor; /* USB vendor id */
+ SANE_Word usb_product; /* USB product id */
+ SANE_Byte frame_no; /* frame number for film scanner */
+ SANE_Int focus_mode; /* focus mode value */
+ SANE_String focus_mode_s; /* focus mode string */
+ SANE_Word focus; /* focus point */
+};
+
+#endif
+
+/*
+ * $Log$
+ * Revision 1.42 2008/05/15 12:50:24 ellert-guest
+ * Fix for bug #306751: sanei-thread with pthreads on 64 bit
+ *
+ * Revision 1.41 2006-02-02 21:28:05 oliver-guest
+ * Corrected USB ID for Benq 310
+ *
+ * Revision 1.40 2006/01/01 22:57:01 oliver-guest
+ * Added calibration data for Benq 5150 / 5250, preliminary support for Epson Stylus CX 1500
+ *
+ * Revision 1.39 2005/12/04 15:03:00 oliver-guest
+ * Some fixes for Benq 5150
+ *
+ * Revision 1.38 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.37 2005/10/24 19:46:40 oliver-guest
+ * Preview and range fix for Epson 2480/2580
+ *
+ * Revision 1.36 2005/09/28 21:33:11 oliver-guest
+ * Added 16 bit option for Epson scanners (untested)
+ *
+ * Revision 1.35 2005/09/03 10:52:11 oliver-guest
+ * Fixed debugging code for epson scanners
+ *
+ * Revision 1.34 2005/08/15 18:06:37 oliver-guest
+ * Added support for Epson 3490/3590 (thanks to Matt Judge)
+ *
+ * Revision 1.33 2005/02/08 22:17:53 oliver-guest
+ * Added IDs for Benq 5250C and 5000S
+ *
+ * Revision 1.32 2005/01/18 20:36:13 oliver-guest
+ * Added ID for Benq 5250C
+ *
+ * Revision 1.31 2004/12/01 22:12:04 oliver-guest
+ * Added support for Epson 1270
+ *
+ * Revision 1.30 2004/09/02 20:59:12 oliver-guest
+ * Added support for Epson 2480
+ *
+ * Revision 1.29 2004/06/16 19:52:27 oliver-guest
+ * Don't enforce even number of URB packages on 1212u_2. Fixes bug #300753.
+ *
+ * Revision 1.28 2004/04/08 21:53:10 oliver-guest
+ * Use sanei_thread in snapscan backend
+ *
+ * Revision 1.27 2004/03/22 00:00:40 oliver-guest
+ * Added detection for Epson 660 by USB ID since new models use new ID strings
+ *
+ * Revision 1.26 2003/10/21 20:43:25 oliver-guest
+ * Bugfixes for SnapScan backend
+ *
+ * Revision 1.25 2003/10/07 19:41:34 oliver-guest
+ * Updates for Epson Perfection 1670
+ *
+ * Revision 1.24 2003/10/07 18:29:20 oliver-guest
+ * Initial support for Epson 1670, minor bugfix
+ *
+ * Revision 1.23 2003/09/12 16:10:33 hmg-guest
+ * Moved union Option_Value from backend header files to sanei_backend.h. No need
+ * to copy it over and over again. Changed header inclusion order in backend
+ * files to include backend.h after sanei_backend.h. Based on a patch from stef
+ * <stef-listes@wanadoo.fr>.
+ *
+ * Revision 1.22 2003/08/19 21:05:08 oliverschwartz
+ * Scanner ID cleanup
+ *
+ * Revision 1.21 2003/04/30 20:49:40 oliverschwartz
+ * SnapScan backend 1.4.26
+ *
+ * Revision 1.38 2003/04/30 20:42:22 oliverschwartz
+ * Added support for Agfa Arcus 1200 (supplied by Valtteri Vuorikoski)
+ *
+ * Revision 1.37 2003/02/05 22:11:11 oliverschwartz
+ * Added Epson Perfection 660
+ *
+ * Revision 1.36 2003/01/08 21:16:36 oliverschwartz
+ * Added support for Acer / Benq 310U
+ *
+ * Revision 1.35 2002/12/10 20:14:12 oliverschwartz
+ * Enable color offset correction for SnapScan300
+ *
+ * Revision 1.34 2002/10/12 10:40:48 oliverschwartz
+ * Added support for Snapscan e10
+ *
+ * Revision 1.33 2002/09/24 16:07:47 oliverschwartz
+ * Added support for Benq 5000
+ *
+ * Revision 1.32 2002/07/12 22:22:47 oliverschwartz
+ * Correct driver description for 4300_2
+ *
+ * Revision 1.31 2002/04/27 14:44:27 oliverschwartz
+ * - Remove SCSI debug options
+ *
+ * Revision 1.30 2002/04/23 22:51:00 oliverschwartz
+ * Cleanup, support for ADF
+ *
+ * Revision 1.29 2002/03/24 12:14:34 oliverschwartz
+ * Add Snapcan_Driver_desc
+ *
+ * Revision 1.28 2002/01/23 20:38:20 oliverschwartz
+ * Fix model ID for e42
+ * Improve recognition of Acer 320U
+ *
+ * Revision 1.27 2002/01/06 18:34:02 oliverschwartz
+ * Added support for Snapscan e42 thanks to Yari Ad� Petralanda
+ *
+ * Revision 1.26 2001/12/20 23:18:01 oliverschwartz
+ * Remove tmpfname
+ *
+ * Revision 1.25 2001/12/18 18:28:35 oliverschwartz
+ * Removed temporary file
+ *
+ * Revision 1.24 2001/12/12 19:44:59 oliverschwartz
+ * Clean up CVS log
+ *
+ * Revision 1.23 2001/11/25 18:51:41 oliverschwartz
+ * added support for SnapScan e52 thanks to Rui Lopes
+ *
+ * Revision 1.22 2001/11/16 20:56:47 oliverschwartz
+ * additional identification string for e26 added
+ *
+ * Revision 1.21 2001/11/16 20:28:35 oliverschwartz
+ * add support for Snapscan e26
+ *
+ * Revision 1.20 2001/11/16 20:23:16 oliverschwartz
+ * Merge with sane-1.0.6
+ * - Check USB vendor IDs to avoid hanging scanners
+ * - fix bug in dither matrix computation
+ *
+ * Revision 1.19 2001/10/11 14:02:10 oliverschwartz
+ * Distinguish between e20/e25 and e40/e50
+ *
+ * Revision 1.18 2001/10/10 10:11:10 oliverschwartz
+ * Add support for Snapscan e25 thanks to Rodolphe Suescun
+ *
+ * Revision 1.17 2001/10/08 18:22:02 oliverschwartz
+ * - Disable quality calibration for Acer Vuego 310F
+ * - Use sanei_scsi_max_request_size as scanner buffer size
+ * for SCSI devices
+ * - Added new devices to snapscan.desc
+ *
+ * Revision 1.16 2001/09/28 13:39:16 oliverschwartz
+ * - Added "Snapscan 300" ID string
+ * - cleanup
+ * - more debugging messages in snapscan-sources.c
+ *
+ * Revision 1.15 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.14 2001/09/17 10:01:08 sable
+ * Added model AGFA 1236U
+ *
+ * Revision 1.13 2001/09/09 20:39:52 oliverschwartz
+ * add identification for 620ST+
+ *
+ * Revision 1.12 2001/09/09 18:06:32 oliverschwartz
+ * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
+ *
+ * Revision 1.11 2001/04/10 12:38:21 sable
+ * Adding e20 support thanks to Steffen Hbner
+ *
+ * Revision 1.10 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.9 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.8 2000/11/10 01:01:59 sable
+ * USB (kind of) autodetection
+ *
+ * Revision 1.7 2000/11/01 01:26:43 sable
+ * Support for 1212U
+ *
+ * Revision 1.6 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.5 2000/10/15 17:54:58 cbagwell
+ * Adding USB files for optional USB compiles.
+ *
+ * Revision 1.4 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ *
+ * Revision 1.3 2000/08/12 15:09:37 pere
+ * Merge devel (v1.0.3) into head branch.
+ *
+ * Revision 1.1.1.1.2.2 2000/07/13 04:47:50 pere
+ * New snapscan backend version dated 20000514 from Steve Underwood.
+ *
+ * Revision 1.2.1 2000/05/14 13:30:20 coppice
+ * Some reformatting a minor tidying.
+ *
+ * Revision 1.2 2000/03/05 13:55:21 pere
+ * Merged main branch with current DEVEL_1_9.
+ *
+ * Revision 1.1.1.1.2.1 1999/09/15 18:20:02 charter
+ * Early version 1.0 snapscan.h
+ *
+ * Revision 2.2 1999/09/09 18:25:02 charter
+ * Checkpoint. Removed references to snapscan-310.c stuff using
+ * "#ifdef OBSOLETE".
+ *
+ * Revision 2.1 1999/09/08 03:05:05 charter
+ * Start of branch 2; same as 1.30.
+ *
+ * Revision 1.30 1999/09/07 20:54:07 charter
+ * Changed expected_data_len to bytes_remaining.
+ *
+ * Revision 1.29 1999/09/02 05:29:46 charter
+ * Fixed the spelling of Petter's name (again).
+ *
+ * Revision 1.28 1999/09/02 05:28:50 charter
+ * Added Gary Plewa's name to the list of contributors.
+ *
+ * Revision 1.27 1999/09/02 04:48:25 charter
+ * Added models and strings for the Acer PRISA 620s (thanks to Gary Plewa).
+ *
+ * Revision 1.26 1999/09/02 02:01:46 charter
+ * Checking in rev 1.26 (for backend version 0.7) again.
+ * This is part of the recovery from the great disk crash of Sept 1, 1999.
+ *
+ * Revision 1.26 1999/07/09 20:54:34 charter
+ * Modifications for SnapScan 1236s (Petter Reinholdsten).
+ *
+ * Revision 1.25 1998/12/16 18:40:53 charter
+ * Commented the INOPERATIVE define to get rid of spurious brightness
+ * and contrast controls accidentally reintroduced previously.
+ *
+ * Revision 1.24 1998/09/07 06:04:58 charter
+ * Merged in Wolfgang Goeller's changes (Vuego 310S, bugfixes).
+ *
+ * Revision 1.23 1998/05/11 17:03:22 charter
+ * Added Mikko's threshold stuff
+ *
+ * Revision 1.22 1998/03/10 23:43:05 eblot
+ * Changing 310/600 models support (structure)
+ *
+ * Revision 1.21 1998/03/08 14:24:43 eblot
+ * Debugging
+ *
+ * Revision 1.20 1998/02/15 21:55:03 charter
+ * From Emmanuel Blot:
+ * Added rgb ring buffer to handle snapscan 310 data specs.
+ *
+ * Revision 1.19 1998/02/06 02:29:52 charter
+ * Added SnapScan_Mode and SnapScan_Model enums.
+ *
+ * Revision 1.18 1998/01/31 23:59:51 charter
+ * Changed window coordinates type to SANE_Fixed (what it should be
+ * for a length).
+ *
+ * Revision 1.17 1998/01/30 19:18:41 charter
+ * Added sense_str and as_str to SnapScan_Scanner; these are intended to
+ * be set by the sense handler.
+ *
+ * Revision 1.16 1998/01/30 11:02:17 charter
+ * Added opens to the SnapScan_Scanner to support open_scanner() and
+ * close_scanner().
+ *
+ * Revision 1.15 1998/01/25 09:57:32 charter
+ * Added more SCSI command options and a group for them.
+ *
+ * Revision 1.14 1998/01/25 08:50:49 charter
+ * Added preview mode option.
+ *
+ * Revision 1.13 1998/01/25 02:24:31 charter
+ * Added OPT_NEGATIVE and the extra sense data bytes.
+ *
+ * Revision 1.12 1998/01/24 05:14:56 charter
+ * Added stuff for RGB gamma correction and for BW mode halftoning.
+ *
+ * Revision 1.11 1998/01/23 13:02:45 charter
+ * Added rgb_lpr and gs_lpr so the user can tune scanning performance.
+ *
+ * Revision 1.10 1998/01/23 07:39:08 charter
+ * Reindented using GNU convention at David Mosberger-Tang's request.
+ * Added ms_per_line to SnapScan_Scanner.
+ *
+ * Revision 1.9 1998/01/22 05:14:23 charter
+ * The bit depth option has been replaced with a mode option. We support
+ * full color, greyscale and lineart modes.
+ *
+ * Revision 1.8 1998/01/21 20:40:13 charter
+ * Added copyright info; added the new SnapScan_State type and
+ * replaced the scanning member of SnapScan_Scanner with a state
+ * member. This is for supporting cancellation.
+ *
+ * Revision 1.7 1998/01/21 11:05:20 charter
+ * Inoperative options now #defined out.
+ *
+ * Revision 1.6 1997/11/26 15:40:24 charter
+ * Brightness and contrast added by Michel.
+ *
+ * Revision 1.5 1997/11/12 12:52:16 charter
+ * Added OPT_INQUIRY for the inquiry button.
+ *
+ * Revision 1.4 1997/11/10 05:51:45 charter
+ * Added stuff for the child reader process and pipe.
+ *
+ * Revision 1.3 1997/11/03 03:16:46 charter
+ * Added buffers and window parameter variables to the scanner structure.
+ *
+ * Revision 1.2 1997/10/14 05:59:53 charter
+ * Basic options and structures added.
+ *
+ * Revision 1.1 1997/10/13 02:25:54 charter
+ * Initial revision
+ * */
diff --git a/backend/sp15c-scsi.h b/backend/sp15c-scsi.h
new file mode 100644
index 0000000..4babc8b
--- /dev/null
+++ b/backend/sp15c-scsi.h
@@ -0,0 +1,566 @@
+#ifndef SP15C_SCSI_H
+#define SP15C_SCSI_H
+
+static const char RCSid_sh[] = "$Header$";
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Fujitsu ScanParner 15c
+ flatbed/ADF scanners. It was derived from the COOLSCAN driver.
+ Written by Randolph Bentson <bentson@holmsjoen.com> */
+
+/* ------------------------------------------------------------------------- */
+/*
+ * $Log$
+ * Revision 1.6 2005/09/19 19:57:48 fzago-guest
+ * Replaced __unused__ with __sane_unused__ to avoid a namespace conflict.
+ *
+ * Revision 1.5 2004/11/13 19:53:04 fzago-guest
+ * Fixes some warnings.
+ *
+ * Revision 1.4 2003/12/27 17:48:38 hmg-guest
+ * Silenced some compilation warnings.
+ *
+ * Revision 1.3 2000/08/12 15:09:37 pere
+ * Merge devel (v1.0.3) into head branch.
+ *
+ * Revision 1.1.2.4 2000/03/14 17:47:12 abel
+ * new version of the Sharp backend added.
+ *
+ * Revision 1.1.2.3 2000/02/14 14:20:19 pere
+ * Make lint_catcher static to avoid link problems with duplicate symbols.
+ *
+ * Revision 1.1.2.2 2000/01/26 03:51:48 pere
+ * Updated backends sp15c (v1.12) and m3096g (v1.11).
+ *
+ * Revision 1.7 2000/01/05 05:27:34 bentson
+ * indent to barfin' GNU style
+ *
+ * Revision 1.6 1999/12/03 20:56:44 bentson
+ * add MEDIA CHECK command
+ *
+ * Revision 1.5 1999/11/24 15:57:50 bentson
+ * add license
+ *
+ * Revision 1.4 1999/11/23 19:04:08 bentson
+ * clean up and enhance inquiry command
+ *
+ * Revision 1.3 1999/11/23 06:42:19 bentson
+ * add 4-bit grayscale support; fix color count field placement
+ *
+ * Revision 1.2 1999/11/22 18:15:43 bentson
+ * more work on color support
+ *
+ * Revision 1.1 1999/11/19 15:09:08 bentson
+ * cribbed from m3096g
+ *
+ */
+
+/****************************************************/
+
+static inline void
+setbitfield (unsigned char *pageaddr, int mask, int shift, int val) \
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void
+resetbitfield (unsigned char *pageaddr, int mask, int shift, int val) \
+{
+ *pageaddr = (*pageaddr & ~(mask << shift)) | (((!val) & mask) << shift);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift) \
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline int
+getnbyte (unsigned char *pnt, int nbytes) \
+{
+ unsigned int result = 0;
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = 0; i < nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void
+putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) \
+{
+ int i;
+
+#ifdef DEBUG
+ assert (nbytes < 5);
+#endif
+ for (i = nbytes - 1; i >= 0; i--)
+ \
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+
+/* ==================================================================== */
+/* SCSI commands */
+
+typedef struct
+{
+ unsigned char *cmd;
+ unsigned int size;
+}
+scsiblk;
+
+/* ==================================================================== */
+
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define INQUIRY 0x12
+#define REQUEST_SENSE 0x03
+#define SEND_DIAGNOSTIC 0x1d
+#define TEST_UNIT_READY 0x00
+#define SET_WINDOW 0x24
+#define SET_SUBWINDOW 0xc0
+#define OBJECT_POSITION 0x31
+#define MEDIA_CHECK 0x08
+#define SEND 0x2a
+#define READ 0x28
+#define MODE_SELECT 0x15
+#define MODE_SENSE 0x1a
+#define SCAN 0x1b
+
+/* ==================================================================== */
+
+static unsigned char reserve_unitC[] =
+{RESERVE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00};
+static scsiblk reserve_unitB =
+{reserve_unitC, sizeof (reserve_unitC)};
+
+/* ==================================================================== */
+
+static unsigned char release_unitC[] =
+{RELEASE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00};
+static scsiblk release_unitB =
+{release_unitC, sizeof (release_unitC)};
+
+/* ==================================================================== */
+
+static unsigned char inquiryC[] =
+{INQUIRY, 0x00, 0x00, 0x00, 0x1f, 0x00};
+static scsiblk inquiryB =
+{inquiryC, sizeof (inquiryC)};
+
+#define set_IN_return_size(icb,val) icb[0x04]=val
+#define set_IN_length(out,n) out[0x04]=n-5
+
+#define get_IN_periph_qual(in) getbitfield(in, 0x07, 5)
+#define IN_periph_qual_lun 0x00
+#define IN_periph_qual_nolun 0x03
+#define get_IN_periph_devtype(in) getbitfield(in, 0x1f, 0)
+#define IN_periph_devtype_scanner 0x06
+#define IN_periph_devtype_unknown 0x1f
+#define get_IN_response_format(in) getbitfield(in + 0x03, 0x0f, 0)
+#define IN_recognized 0x02
+#define get_IN_additional_length(in) in[0x04]
+#define get_IN_vendor(in, buf) strncpy(buf, in + 0x08, 0x08)
+#define get_IN_product(in, buf) strncpy(buf, in + 0x10, 0x010)
+#define get_IN_version(in, buf) strncpy(buf, in + 0x20, 0x04)
+#define get_IN_color_mode(in) getbitfield(in + 0x24, 0xf, 0)
+#define get_IN_color_seq(in) getbitfield(in + 0x24, 0x7, 4)
+#define get_IN_adf(in) getbitfield(in + 0x24, 0x1, 7)
+
+/* ==================================================================== */
+
+static unsigned char request_senseC[] =
+{REQUEST_SENSE, 0x00, 0x00, 0x00, 0x00, 0x00};
+static scsiblk request_senseB =
+{request_senseC, sizeof (request_senseC)};
+
+#define set_RS_allocation_length(sb,val) sb[0x04] = (unsigned char)val
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */
+#define get_RS_additional_length(b) b[0x07] /* always 10 */
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid, always 0 */
+
+#define rs_return_block_size 18 /* Says Nikon */
+
+/* ==================================================================== */
+
+static unsigned char send_diagnosticC[] =
+{SEND_DIAGNOSTIC, 0x04, 0x00, 0x00, 0x00, 0x00};
+static scsiblk send_diagnosticB =
+{send_diagnosticC, sizeof (send_diagnosticC)};
+
+/* ==================================================================== */
+
+static unsigned char test_unit_readyC[] =
+{TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00};
+static scsiblk test_unit_readyB =
+{test_unit_readyC, sizeof (test_unit_readyC)};
+
+/* ==================================================================== */
+
+static unsigned char set_windowC[] =
+{SET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
+ /* opcode, lun, _____4 X reserved____, _transfer length, ctl */
+static scsiblk set_windowB =
+{set_windowC, sizeof (set_windowC)};
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+#define get_SW_xferlen(sb) getnbyte(sb + 0x06, 3)
+
+/* ==================================================================== */
+
+static unsigned char set_subwindowC[] =
+{SET_SUBWINDOW};
+static scsiblk set_subwindowB =
+{set_subwindowC, sizeof (set_subwindowC)};
+
+/* ==================================================================== */
+
+static unsigned char object_positionC[] =
+{OBJECT_POSITION, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* ADF, _____Count_____, ________Reserved______, Ctl */
+static scsiblk object_positionB =
+{object_positionC, sizeof (object_positionC)};
+
+#define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val)
+#define OP_Discharge 0x00
+#define OP_Feed 0x01
+/* ==================================================================== */
+
+static unsigned char media_checkC[] =
+{MEDIA_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* ADF, _Reserved_, Len, Ctl */
+static scsiblk media_checkB =
+{media_checkC, sizeof (media_checkC)};
+
+#define set_MC_return_size(sb,val) sb[0x04]=val
+#define get_MC_adf_status(sb) sb[0x00]
+#define MC_ADF_OK 0x01
+
+/* ==================================================================== */
+static unsigned char media_parameter_data_blockC[] =
+{
+ 0x00
+};
+static scsiblk media_parameter_data_blockB =
+{media_parameter_data_blockC, sizeof (media_parameter_data_blockC)};
+/* ==================================================================== */
+
+static unsigned char sendC[] =
+{SEND, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static scsiblk sendB =
+{sendC, sizeof (sendC)};
+
+#define set_S_datatype_code(sb, val) sb[0x02] = (unsigned char)val
+#define S_datatype_imagedatai 0x00
+#define S_EX_datatype_LUT 0x01 /* Experiment code */
+#define S_EX_datatype_shading_data 0xa0 /* Experiment code */
+#define S_user_reg_gamma 0xc0
+#define S_device_internal_info 0x03
+#define set_S_datatype_qual_upper(sb, val) sb[0x04] = (unsigned char)val
+#define S_DQ_none 0x00
+#define S_DQ_Rcomp 0x06
+#define S_DQ_Gcomp 0x07
+#define S_DQ_Bcomp 0x08
+#define S_DQ_Reg1 0x01
+#define S_DQ_Reg2 0x02
+#define S_DQ_Reg3 0x03
+#define set_S_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+/*
+ static unsigned char gamma_user_LUT_LS1K[512] = { 0x00 };
+ static scsiblk gamma_user_LUT_LS1K_LS1K = {
+ gamma_user_LUT_LS1K, sizeof(gamma_user_LUT_LS1K)
+ };
+ */
+
+/* ==================================================================== */
+
+static unsigned char readC[] =
+{READ, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* Type, rsvd, type qual, __xfer length__, Ctl */
+static scsiblk readB =
+{readC, sizeof (readC)};
+
+#define set_R_datatype_code(sb, val) sb[0x02] = val
+#define R_datatype_imagedata 0x00
+#define R_pixel_size 0x80
+#define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+/* ==================================================================== */
+
+static unsigned char mode_selectC[] =
+{MODE_SELECT, 0x10, 0x00, 0x00, 0x00, 0x00};
+static scsiblk mode_selectB =
+{mode_selectC, sizeof (mode_selectC)};
+
+/* ==================================================================== */
+
+static unsigned char mode_senseC[] =
+{MODE_SENSE, 0x18, 0x03, 0x00, 0x00, 0x00, /* PF set, page type 03 */ };
+static scsiblk mode_senseB =
+{mode_senseC, sizeof (mode_senseC)};
+
+#define set_MS_DBD(b, val) setbitfield(b, 0x01, 3, (val?1:0))
+#define set_MS_len(b, val) putnbyte(b+0x04, val, 1)
+#define get_MS_MUD(b) getnbyte(b+(0x04+((int)*(b+0x3)))+0x4,2)
+
+/* ==================================================================== */
+
+static unsigned char scanC[] =
+{SCAN, 0x00, 0x00, 0x00, 0x00, 0x00};
+static scsiblk scanB =
+{scanC, sizeof (scanC)};
+
+#define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val
+
+/* ==================================================================== */
+
+/* We use the same structure for both SET WINDOW and GET WINDOW. */
+static unsigned char window_parameter_data_blockC[] =
+{
+ 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, /* Window Descriptor Length */
+};
+static scsiblk window_parameter_data_blockB =
+{window_parameter_data_blockC, sizeof (window_parameter_data_blockC)};
+
+#define set_WDPB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2)
+#define get_WDPB_wdblen(sb) getnbyte(sb + 0x06, 2)
+
+#define WDB_size_empty 0x28 /* wdb_len if nothing is set by inquiry */
+#define WDB_size_BW 0x40
+#define WDB_size_Color 0x33
+#define WDB_size_max 0xff
+
+/* ==================================================================== */
+
+static unsigned char window_descriptor_blockC[] =
+{
+ 0x00, /* 0x00 *//* Window Identifier */
+#define set_WD_wid(sb, val) sb[0] = val
+#define WD_wid_all 0x00 /* Only one supported */
+ 0x00, /* 0x01 *//* reserved, AUTO */
+#define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val)
+#define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0)
+ 0x00, 0x00, /* 0x02 *//* X resolution in dpi, 0 => 400 */
+#define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2)
+#define get_WD_Xres(sb) getnbyte(sb + 0x02, 2)
+ 0x00, 0x00, /* 0x04 *//* Y resolution in dpi, 0 => 400 */
+#define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2)
+#define get_WD_Yres(sb) getnbyte(sb + 0x04, 2)
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x06 *//* Upper Left X in inch/1200 */
+#define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4)
+#define get_WD_ULX(sb) getnbyte(sb + 0x06, 4)
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x0a *//* Upper Left Y in inch/1200 */
+#define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4)
+#define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4)
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x0e *//* Width */
+#define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4)
+#define get_WD_width(sb) getnbyte(sb + 0x0e, 4)
+#define WD_width 10200
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x12 *//* Length */
+#define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4)
+#define get_WD_length(sb) getnbyte(sb + 0x12, 4)
+#define WD_length 13200
+ 0x00, /* 0x16 *//* Brightness */
+#define set_WD_brightness(sb, val) sb[0x16] = val
+#define get_WD_brightness(sb) sb[0x16]
+#define WD_brightness 0x80
+ 0x00, /* 0x17 *//* Threshold */
+#define set_WD_threshold(sb, val) sb[0x17] = val
+#define get_WD_threshold(sb) sb[0x17]
+#define WD_threshold 0x80
+ 0x00, /* 0x18 *//* Contrast */
+#define set_WD_contrast(sb, val) sb[0x18] = val
+#define get_WD_contrast(sb) sb[0x18]
+ 0x05, /* 0x19 *//* Image composition */
+#define set_WD_composition(sb, val) sb[0x19] = val
+#define get_WD_composition(sb) sb[0x19]
+/* lineart, halftone, greyscale, binary color, dither color, multi-color */
+#define WD_comp_LA 0
+#define WD_comp_HT 1
+#define WD_comp_GS 2
+#define WD_comp_BC 3
+#define WD_comp_DC 4
+#define WD_comp_MC 5
+#define WD_comp_G4 10
+#define WD_comp_G8 11
+ 0x08, /* 0x1a *//* Bits/Pixel */
+#define set_WD_bitsperpixel(sb, val) sb[0x1a] = val
+#define get_WD_bitsperpixel(sb) sb[0x1a]
+ 0x00, 0x00, /* 0x1b *//* Halftone pattern */
+#define set_WD_halftone(sb, val) putnbyte(sb + 0x1b, val, 2)
+#define get_WD_halftone(sb) getnbyte(sb + 0x1b, 2)
+ 0x00,
+/* 0x1d *//*************** STUFF ***************/
+#define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val)
+#define get_WD_rif(sb) getbitfield(sb + 0x1d, 1, 7)
+#define set_WD_pad(sb, val) setbitfield(sb + 0x1d, 7, 0, val)
+#define get_WD_pad(sb) getbitfield(sb + 0x1d, 7, 0)
+ 0x00, 0x00, /* 0x1e *//* bit ordering */
+#define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2)
+#define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2)
+ 0x00, /* 0x20 *//* compression type */
+#define set_WD_compress_type(sb, val) sb[0x20] = val
+#define get_WD_compress_type(sb) sb[0x20]
+ 0x00, /* 0x21 *//* compression argument */
+#define set_WD_compress_arg(sb, val) sb[0x21] = val
+#define get_WD_compress_arg(sb) sb[0x21]
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x22 *//* reserved */
+ 0x00, /* 0x28 *//* vendor id code */
+#define set_WD_vendor_id_code(sb, val) sb[0x28] = val
+#define get_WD_vendor_id_code(sb) sb[0x28]
+#define WD_bw 0x00
+#define WD_color 0xFF
+ 0x00, /* 0x29 C *//* parm length */
+#define set_WD_parm_length(sb, val) sb[0x29] = val
+#define get_WD_parm_length(sb) sb[0x29]
+ 0x00, /* 0x2a C *//* ADF, source, Color */
+#define set_WD_adf(sb, val) setbitfield(sb + 0x2a, 1, 7, val)
+#define get_WD_adf(sb) getbitfield(sb + 0x2a, 1, 7)
+#define set_WD_source(sb, val) setbitfield(sb + 0x2a, 1, 6, val)
+#define get_WD_source(sb) getbitfield(sb + 0x2a, 1, 6)
+#define set_WD_color(sb, val) setbitfield(sb + 0x2a, 7, 3, val)
+#define get_WD_color(sb) getbitfield(sb + 0x2a, 7, 3)
+#define WD_color_green 0
+#define WD_color_red 1
+#define WD_color_greenx 2
+#define WD_color_blue 3
+#define WD_color_rgb 4
+ 0x00, /* 0x2b C *//* highlight color */
+#define set_WD_highlight_color(sb, val) sb[0x2b] = val
+#define get_WD_highlight_color(sb) sb[0x2b]
+ 0x00, /* 0x2c C *//* shadow value */
+#define set_WD_shadow_value(sb, val) sb[0x2c] = val
+#define get_WD_shadow_value(sb) sb[0x2c]
+ 0x00, 0x00, /* 0x2d C *//* line width */
+#define set_WD_line_width(sb, val) putnbyte(sb + 0x2d, val, 2)
+#define get_WD_line_width(sb) getnbyte(sb + 0x2d, 2)
+ 0x00, 0x00, /* 0x2f C *//* line count */
+#define set_WD_line_count(sb, val) putnbyte(sb + 0x2f, val, 2)
+#define get_WD_line_count(sb) getnbyte(sb + 0x2f, 2)
+ 0x00, /* 0x31 *//* reserved */
+ 0x00, /* 0x32 *//* reserved */
+ 0x00, /* 0x33 *//* reserved */
+ 0x00, /* 0x34 *//* reserved */
+ 0x00, /* 0x35 *//* paper size */
+#define set_WD_paper_size(sb, val) sb[0x35] = val
+#define get_WD_paper_size(sb) sb[0x35]
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x36 BW *//* paper width X */
+#define set_WD_paper_width_X(sb, val) putnbyte(sb + 0x36, val, 4)
+#define get_WD_paper_width_X(sb) getnbyte(sb + 0x36, 4)
+ 0x00, 0x00,
+ 0x00, 0x00, /* 0x3a BW *//* paper length Y */
+#define set_WD_paper_length_Y(sb, val) putnbyte(sb+0x3a, val, 4)
+#define get_WD_paper_length_Y(sb) getnbyte(sb + 0x3a, 4)
+ 0x00, /* 0x3e *//* reserved */
+ 0x00, /* 0x3f *//* reserved */
+ /* 0x40 (last) */
+};
+
+static scsiblk window_descriptor_blockB =
+{window_descriptor_blockC, sizeof (window_descriptor_blockC)};
+
+/* ==================================================================== */
+
+#if 0
+#define set_WDB_length(wdb,len) (wdb.size = (len))
+#define WDB_OFF(b) (b + set_window.size)
+#define WDB_OFF(b, n) (b + set_window.size + \
+ window_parameter_data_block.size + \
+ ( window_descriptor_block.size * (n - 1) ) )
+#define set_WDPB_wdbnum(sb,n) set_WPDB_wdblen(sb,window_descriptor_block.size*n)
+#endif
+
+/* ==================================================================== */
+
+static scsiblk __sane_unused__ *lint_catcher[] =
+{&reserve_unitB,
+ &release_unitB,
+ &inquiryB,
+ &request_senseB,
+ &send_diagnosticB,
+ &test_unit_readyB,
+ &set_windowB,
+ &set_subwindowB,
+ &object_positionB,
+ &media_checkB,
+ &media_parameter_data_blockB,
+ &sendB,
+ &readB,
+ &mode_selectB,
+ &mode_senseB,
+ &scanB,
+ &window_parameter_data_blockB,
+ &window_descriptor_blockB};
+
+#endif /* SP15C_SCSI_H */
diff --git a/backend/sp15c.c b/backend/sp15c.c
new file mode 100644
index 0000000..7a3808e
--- /dev/null
+++ b/backend/sp15c.c
@@ -0,0 +1,2232 @@
+static const char RCSid[] = "$Header$";
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Fujitsu ScanParner 15C
+ flatbed/ADF scanners. It was derived from the COOLSCAN driver.
+ Written by Randolph Bentson <bentson@holmsjoen.com> */
+
+/*
+ * $Log$
+ * Revision 1.17 2008/11/26 21:21:30 kitno-guest
+ * * backend/ *.[ch]: nearly every backend used V_MAJOR
+ * instead of SANE_CURRENT_MAJOR in sane_init()
+ * * backend/snapscan.c: remove EXPECTED_VERSION check
+ * since new SANE standard is forward compatible
+ *
+ * Revision 1.16 2008-07-26 03:53:44 kitno-guest
+ * separate x-resolution from resolution, and update all backends that use
+ * it, to prevent ui change
+ *
+ * Revision 1.15 2007-11-18 10:59:18 ellert-guest
+ * Fix handling of valid "negative" PIDs
+ *
+ * Revision 1.14 2007-10-26 14:56:38 jblache
+ * OPT_NUM_OPTS must be of type SANE_TYPE_INT.
+ *
+ * Revision 1.13 2006/03/29 20:48:50 hmg-guest
+ * Fixed ADF support. Patch from Andreas Degert <ad@papyrus-gmbh.de>.
+ *
+ * Revision 1.12 2005/10/01 17:06:25 hmg-guest
+ * Fixed some warnings (bug #302290).
+ *
+ * Revision 1.11 2005/07/15 18:12:49 hmg-guest
+ * Better 4->8 bit depth expansion algorithm (from Mattias Ellert
+ * <mattias.ellert@tsl.uu.se>).
+ *
+ * Revision 1.10 2004/10/06 15:59:40 hmg-guest
+ * Don't eject medium twice after each page.
+ *
+ * Revision 1.9 2004/06/20 00:34:10 ellert-guest
+ * Missed one...
+ *
+ * Revision 1.8 2004/05/29 10:27:47 hmg-guest
+ * Fixed the fix of the sanei_thread fix (from Mattias Ellert).
+ *
+ * Revision 1.7 2004/05/28 18:52:53 hmg-guest
+ * Fixed sanei_thread fix (bug #300634, by Mattias Ellert).
+ *
+ * Revision 1.6 2004/05/23 17:28:56 hmg-guest
+ * Use sanei_thread instead of fork() in the unmaintained backends.
+ * Patches from Mattias Ellert (bugs: 300635, 300634, 300633, 300629).
+ *
+ * Revision 1.5 2003/12/27 17:48:38 hmg-guest
+ * Silenced some compilation warnings.
+ *
+ * Revision 1.4 2001/05/31 18:01:39 hmg
+ * Fixed config_line[len-1] bug which could generate an access
+ * violation if len==0.
+ * Henning Meier-Geinitz <henning@meier-geinitz.de>
+ *
+ * Revision 1.3 2000/08/12 15:09:38 pere
+ * Merge devel (v1.0.3) into head branch.
+ *
+ * Revision 1.1.2.6 2000/07/30 11:16:06 hmg
+ * 2000-07-30 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/mustek.*: Update to Mustek backend 1.0-95. Changed from
+ * wait() to waitpid() and removed unused code.
+ * * configure configure.in backend/m3096g.c backend/sp15c.c: Reverted
+ * the V_REV patch. V_REV should not be used in backends.
+ *
+ * Revision 1.1.2.5 2000/07/29 21:38:13 hmg
+ * 2000-07-29 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/sp15.c backend/m3096g.c: Replace fgets with
+ * sanei_config_read, return V_REV as part of version_code string
+ * (patch from Randolph Bentson).
+ *
+ * Revision 1.1.2.4 2000/07/25 21:47:46 hmg
+ * 2000-07-25 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/snapscan.c: Use DBG(0, ...) instead of fprintf (stderr, ...).
+ * * backend/abaton.c backend/agfafocus.c backend/apple.c backend/dc210.c
+ * backend/dll.c backend/dmc.c backend/microtek2.c backend/pint.c
+ * backend/qcam.c backend/ricoh.c backend/s9036.c backend/snapscan.c
+ * backend/tamarack.c: Use sanei_config_read instead of fgets.
+ * * backend/dc210.c backend/microtek.c backend/pnm.c: Added
+ * #include "../include/sane/config.h".
+ * * backend/dc25.c backend/m3096.c backend/sp15.c
+ * backend/st400.c: Moved #include "../include/sane/config.h" to the beginning.
+ * * AUTHORS: Changed agfa to agfafocus.
+ *
+ * Revision 1.1.2.3 2000/03/14 17:47:12 abel
+ * new version of the Sharp backend added.
+ *
+ * Revision 1.1.2.2 2000/01/26 03:51:48 pere
+ * Updated backends sp15c (v1.12) and m3096g (v1.11).
+ *
+ * Revision 1.12 2000/01/25 16:23:13 bentson
+ * tab expansion; add one debug message
+ *
+ * Revision 1.11 2000/01/05 05:21:37 bentson
+ * indent to barfable GNU style
+ *
+ * Revision 1.10 1999/12/06 17:36:55 bentson
+ * show default value for scan size at the start
+ *
+ * Revision 1.9 1999/12/04 00:30:35 bentson
+ * fold in 1.8.1.x versions
+ *
+ * Revision 1.8.1.2 1999/12/04 00:19:43 bentson
+ * bunch of changes to complete MEDIA CHECK use
+ *
+ * Revision 1.8.1.1 1999/12/03 20:44:56 bentson
+ * trial changes to use MEDIA CHECK command
+ *
+ * Revision 1.8 1999/12/03 18:30:56 bentson
+ * cosmetic changes
+ *
+ * Revision 1.7 1999/11/24 20:09:25 bentson
+ * fold in 1.6.1.x changes
+ *
+ * Revision 1.6.1.3 1999/11/24 15:56:48 bentson
+ * remove some debugging; final :-) fix to option constraint processing
+ *
+ * Revision 1.6.1.2 1999/11/24 15:37:42 bentson
+ * more constraint debugging
+ *
+ * Revision 1.6.1.1 1999/11/24 14:35:24 bentson
+ * fix some of the constraint handling
+ *
+ * Revision 1.6 1999/11/23 18:48:27 bentson
+ * add constraint checking and enforcement
+ *
+ * Revision 1.5 1999/11/23 08:26:03 bentson
+ * basic color seems to work
+ *
+ * Revision 1.4 1999/11/23 06:41:26 bentson
+ * 4-bit Grayscale works; now working on color
+ *
+ * Revision 1.3 1999/11/22 18:15:07 bentson
+ * more work on color support
+ *
+ * Revision 1.2 1999/11/19 17:30:54 bentson
+ * enhance control (works with xscanimage)
+ *
+ * Revision 1.1 1999/11/19 15:09:08 bentson
+ * cribbed from m3096g
+ *
+ */
+
+/* SANE-FLOW-DIAGRAM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner-device
+ - sane_exit() : terminate use of backend
+ */
+
+/* ------------------------------------------------------------------------- */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#include "sp15c-scsi.h"
+#include "sp15c.h"
+
+/* ------------------------------------------------------------------------- */
+
+static const char negativeStr[] = "Negative";
+static const char positiveStr[] = "Positive";
+static SANE_String_Const type_list[] =
+{positiveStr, negativeStr, 0};
+
+static SANE_String_Const source_list[] =
+{"ADF", "FB", NULL};
+
+static const SANE_Int resolution_list[] =
+{11, 0, 60, 75, 80, 100, 120, 150, 200, 240, 300, 600};
+
+static const SANE_Int x_res_list[] =
+{11, 0, 60, 75, 80, 100, 120, 150, 200, 240, 300, 600};
+
+static const SANE_Int y_res_list[] =
+{11, 0, 60, 75, 80, 100, 120, 150, 200, 240, 300, 600};
+
+static const char lineStr[] = SANE_VALUE_SCAN_MODE_LINEART;
+static const char halfStr[] = SANE_VALUE_SCAN_MODE_HALFTONE;
+static const char gray4Str[] = "4-bit Gray";
+static const char gray8Str[] = "8-bit Gray";
+static const char colorStr[] = SANE_VALUE_SCAN_MODE_COLOR;
+static SANE_String_Const scan_mode_list[] =
+{lineStr, halfStr, gray4Str, gray8Str, colorStr, NULL};
+
+/* how do the following work? */
+static const SANE_Range brightness_range =
+{0, 255, 32};
+static const SANE_Range threshold_range =
+{0, 255, 4};
+static const SANE_Range x_range =
+{0, SANE_FIX (216), 1};
+static const SANE_Range y_range_fb =
+{0, SANE_FIX (295), 1};
+static const SANE_Range y_range_adf =
+{0, SANE_FIX (356), 1};
+
+/* ################# externally visible routines ################{ */
+
+
+SANE_Status /* looks like frontend ignores results */
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ authorize = authorize; /* silence compilation warnings */
+
+ DBG_INIT ();
+ DBG (10, "sane_init\n");
+
+ sanei_thread_init ();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+ fp = sanei_config_open (SP15C_CONFIG_FILE);
+ if (!fp)
+ {
+ attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#')
+ continue;
+ len = strlen (dev_name);
+ if (!len)
+ continue;
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+} /* sane_init */
+
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ struct sp15c *dev;
+ int i;
+
+ local_only = local_only; /* silence compilation warnings */
+
+ DBG (10, "sane_get_devices\n");
+
+ if (devlist)
+ free (devlist);
+ devlist = calloc (num_devices + 1, sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = first_dev, i = 0; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+} /* sane_get_devices */
+
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct sp15c *dev = first_dev;
+
+ name = name; /* silence compilation warnings */
+ /* Strange, name is not used? */
+
+ DBG (10, "sane_open\n");
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ init_options (dev);
+ *handle = dev;
+
+ dev->use_adf = SANE_TRUE;
+
+ dev->x_res = 200;
+ dev->y_res = 200;
+ dev->tl_x = 0;
+ dev->tl_y = 0;
+ dev->br_x = 1200 * 17 / 2;
+ dev->br_y = 1200 * 11;
+ dev->brightness = 128;
+ dev->threshold = 128;
+ dev->contrast = 128;
+ dev->composition = WD_comp_LA;
+ dev->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT
+ | SANE_CAP_SOFT_SELECT;
+ dev->bitsperpixel = 1;
+ dev->halftone = 0;
+ dev->rif = 0;
+ dev->bitorder = 0;
+ dev->compress_type = 0;
+ dev->compress_arg = 0;
+ dev->vendor_id_code = 0;
+ dev->outline = 0;
+ dev->emphasis = 0;
+ dev->auto_sep = 0;
+ dev->mirroring = 0;
+ dev->var_rate_dyn_thresh = 0;
+ dev->white_level_follow = 0;
+ dev->paper_size = 0x87;
+ dev->paper_width_X = 1200 * 17 / 2;
+ dev->paper_length_Y = 1200 * 11;
+ dev->opt[OPT_TL_Y].constraint.range = &y_range_adf;
+ dev->opt[OPT_BR_Y].constraint.range = &y_range_adf;
+ adjust_width (dev, 0);
+
+ return SANE_STATUS_GOOD;
+} /* sane_open */
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ h = h;
+ non_blocking = non_blocking; /* silence compilation warnings */
+
+ DBG (10, "sane_set_io_mode\n");
+ return SANE_STATUS_UNSUPPORTED;
+} /* sane_set_io_mode */
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fdp)
+{
+ h = h;
+ fdp = fdp; /* silence compilation warnings */
+
+ DBG (10, "sane_get_select_fd\n");
+ return SANE_STATUS_UNSUPPORTED;
+} /* sane_get_select_fd */
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct sp15c *scanner = handle;
+
+ DBG (10, "sane_get_option_descriptor: \"%s\"\n",
+ scanner->opt[option].name);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return &scanner->opt[option];
+} /* sane_get_option_descriptor */
+
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info)
+{
+ struct sp15c *scanner = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ if (info)
+ *info = 0;
+
+ if (scanner->scanning == SANE_TRUE)
+ {
+ DBG (5, "sane_control_option: device busy\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = scanner->opt[option].cap;
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (10, "sane_control_option: get value \"%s\"\n",
+ scanner->opt[option].name);
+ DBG (11, "\tcap = %d\n", cap);
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (10, "\tinactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (option)
+ {
+
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if (scanner->use_adf == SANE_TRUE)
+ {
+ strcpy (val, "ADF");
+ }
+ else
+ {
+ strcpy (val, "FB");
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ switch (scanner->composition)
+ {
+ case WD_comp_LA:
+ strcpy (val, lineStr);
+ break;
+ case WD_comp_HT:
+ strcpy (val, halfStr);
+ break;
+ case WD_comp_G4:
+ strcpy (val, gray4Str);
+ break;
+ case WD_comp_G8:
+ strcpy (val, gray8Str);
+ break;
+ case WD_comp_MC:
+ strcpy (val, colorStr);
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_TYPE:
+ return SANE_STATUS_INVAL;
+
+ case OPT_PRESCAN:
+ return SANE_STATUS_INVAL;
+
+ case OPT_X_RES:
+ *(SANE_Word *) val = scanner->x_res;
+ return SANE_STATUS_GOOD;
+
+ case OPT_Y_RES:
+ *(SANE_Word *) val = scanner->y_res;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW_RES:
+ return SANE_STATUS_INVAL;
+
+ case OPT_TL_X:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_x));
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_y));
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_x));
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_y));
+ return SANE_STATUS_GOOD;
+
+ case OPT_AVERAGING:
+ return SANE_STATUS_INVAL;
+
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ *(SANE_Word *) val = scanner->threshold;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW:
+ return SANE_STATUS_INVAL;
+
+ }
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (10, "sane_control_option: set value \"%s\"\n",
+ scanner->opt[option].name);
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (10, "\tinactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (10, "\tnot settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (scanner->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (10, "\tbad value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ case OPT_NUM_OPTS:
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if (strcmp (val, "ADF") == 0)
+ {
+ if (scanner->use_adf == SANE_TRUE)
+ return SANE_STATUS_GOOD;
+ scanner->use_adf = SANE_TRUE;
+ scanner->opt[OPT_TL_Y].constraint.range = &y_range_adf;
+ scanner->opt[OPT_BR_Y].constraint.range = &y_range_adf;
+ apply_constraints (scanner, OPT_TL_Y, &scanner->tl_y, info);
+ apply_constraints (scanner, OPT_BR_Y, &scanner->br_y, info);
+ }
+ else if (strcmp (val, "FB") == 0)
+ {
+ if (scanner->use_adf == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+ scanner->use_adf = SANE_FALSE;
+ scanner->opt[OPT_TL_Y].constraint.range = &y_range_fb;
+ scanner->opt[OPT_BR_Y].constraint.range = &y_range_fb;
+ apply_constraints (scanner, OPT_TL_Y, &scanner->tl_y, info);
+ apply_constraints (scanner, OPT_BR_Y, &scanner->br_y, info);
+ }
+ else
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (strcmp (val, lineStr) == 0)
+ {
+ if (scanner->composition == WD_comp_LA)
+ return SANE_STATUS_GOOD;
+ scanner->composition = WD_comp_LA;
+ scanner->bitsperpixel = 1;
+ scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT
+ | SANE_CAP_SOFT_SELECT;
+ scanner->vendor_id_code = 0;
+ }
+ else if (strcmp (val, halfStr) == 0)
+ {
+ if (scanner->composition == WD_comp_HT)
+ return SANE_STATUS_GOOD;
+ scanner->composition = WD_comp_HT;
+ scanner->bitsperpixel = 1;
+ scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_DETECT
+ | SANE_CAP_SOFT_SELECT;
+ scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE;
+ scanner->vendor_id_code = 0;
+ }
+ else if (strcmp (val, gray4Str) == 0)
+ {
+ if (scanner->composition == WD_comp_G4)
+ return SANE_STATUS_GOOD;
+ scanner->composition = WD_comp_G4;
+ scanner->bitsperpixel = 4;
+ scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE;
+ scanner->vendor_id_code = 0;
+ }
+ else if (strcmp (val, gray8Str) == 0)
+ {
+ if (scanner->composition == WD_comp_G8)
+ return SANE_STATUS_GOOD;
+ scanner->composition = WD_comp_G8;
+ scanner->bitsperpixel = 8;
+ scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE;
+ scanner->vendor_id_code = 0;
+ }
+ else if (strcmp (val, colorStr) == 0)
+ {
+ if (scanner->composition == WD_comp_MC)
+ return SANE_STATUS_GOOD;
+ scanner->composition = WD_comp_MC;
+ scanner->bitsperpixel = 8;
+ scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE;
+ scanner->vendor_id_code = 0xff;
+ }
+ else
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_TYPE:
+ return SANE_STATUS_INVAL;
+
+ case OPT_PRESCAN:
+ return SANE_STATUS_INVAL;
+
+ case OPT_X_RES:
+ scanner->x_res = (*(SANE_Word *) val);
+ adjust_width (scanner, info);
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_Y_RES:
+ scanner->y_res = (*(SANE_Word *) val);
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW_RES:
+ return SANE_STATUS_INVAL;
+
+ case OPT_TL_X:
+ scanner->tl_x = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ adjust_width (scanner, info);
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_x));
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ scanner->tl_y = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_y));
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ scanner->br_x = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ adjust_width (scanner, info);
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_x));
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ scanner->br_y = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_y));
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_AVERAGING:
+ return SANE_STATUS_INVAL;
+
+ case OPT_BRIGHTNESS:
+ scanner->brightness = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ scanner->threshold = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+ return SANE_STATUS_INVAL;
+} /* sane_control_option */
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct sp15c *scanner = handle;
+ int fds[2];
+ int ret;
+
+ DBG (10, "sane_start\n");
+ if (scanner->scanning == SANE_TRUE)
+ {
+ DBG (5, "sane_start: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (scanner->sfd < 0)
+ { /* first call */
+ if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd),
+ sense_handler, 0) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: open of %s failed:\n",
+ scanner->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ scanner->scanning = SANE_TRUE;
+
+
+ if ((ret = sp15c_check_values (scanner)) != 0)
+ { /* Verify values */
+ DBG (1, "sane_start: ERROR: invalid scan-values\n");
+ sanei_scsi_close (scanner->sfd);
+ scanner->scanning = SANE_FALSE;
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ if ((ret = sp15c_grab_scanner (scanner)))
+ {
+ DBG (5, "sane_start: unable to reserve scanner\n");
+ sanei_scsi_close (scanner->sfd);
+ scanner->scanning = SANE_FALSE;
+ scanner->sfd = -1;
+ return ret;
+ }
+
+ if ((ret = sp15c_set_window_param (scanner, 0)))
+ {
+ DBG (5, "sane_start: ERROR: failed to set window\n");
+ sp15c_free_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->scanning = SANE_FALSE;
+ scanner->sfd = -1;
+ return ret;
+ }
+
+ /* Since the SET WINDOW can specify the use of the ADF, and since the
+ ScanPartner 15C automatically pre-loads sheets from the ADF, it
+ is only necessary to see if any paper is available. The OEM
+ Manual offers the OBJECT POSITION command, but it causes the
+ carrier unit into a "homing" cycle. The undocumented MEDIA CHECK
+ command avoids the "homing" cycle. (Note the SET WINDOW command
+ had to use the color scanning vendor unique parameters, regardless
+ of scanning mode, so that it could invoke the ADF.) */
+ if (scanner->use_adf == SANE_TRUE
+ && (ret = sp15c_media_check (scanner)))
+ {
+ DBG (5, "sane_start: WARNING: ADF empty\n");
+ sp15c_free_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->scanning = SANE_FALSE;
+ scanner->sfd = -1;
+ return ret;
+ }
+
+ swap_res (scanner);
+
+ DBG (10, "\tbytes per line = %d\n", bytes_per_line (scanner));
+ DBG (10, "\tpixels_per_line = %d\n", pixels_per_line (scanner));
+ DBG (10, "\tlines = %d\n", lines_per_scan (scanner));
+ DBG (10, "\tbrightness (halftone) = %d\n", scanner->brightness);
+ DBG (10, "\tthreshold (line art) = %d\n", scanner->threshold);
+
+ if (scanner->composition == WD_comp_MC
+ && (ret = sp15c_start_scan (scanner)))
+ {
+ DBG (5, "sane_start: start_scan failed\n");
+ sp15c_free_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->scanning = SANE_FALSE;
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
+ if (pipe (fds) < 0)
+ {
+ DBG (1, "ERROR: could not create pipe\n");
+ swap_res (scanner);
+ scanner->scanning = SANE_FALSE;
+ sp15c_free_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe = fds[0];
+ scanner->reader_pipe = fds[1];
+ scanner->reader_pid = sanei_thread_begin (reader_process, (void *) scanner);
+
+ if (sanei_thread_is_forked ())
+ close (scanner->reader_pipe);
+
+ DBG (10, "sane_start: ok\n");
+ return SANE_STATUS_GOOD;
+} /* sane_start */
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct sp15c *scanner = handle;
+
+ DBG (10, "sane_get_parameters\n");
+ if (scanner->composition == WD_comp_MC)
+ {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ }
+ else if (scanner->composition == WD_comp_LA
+ || scanner->composition == WD_comp_HT)
+ {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 1;
+ }
+ else
+ {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ }
+
+ params->pixels_per_line = pixels_per_line (scanner);
+ params->lines = lines_per_scan (scanner);
+ params->bytes_per_line = bytes_per_line (scanner);
+ params->last_frame = 1;
+
+ DBG (10, "\tdepth %d\n", params->depth);
+ DBG (10, "\tlines %d\n", params->lines);
+ DBG (10, "\tpixels_per_line %d\n", params->pixels_per_line);
+ DBG (10, "\tbytes_per_line %d\n", params->bytes_per_line);
+/*************************/
+ DBG (10, "\tlength %d\n", scanner->br_y - scanner->tl_y);
+ DBG (10, "\t(nom.) width %d\n", scanner->br_x - scanner->tl_x);
+ DBG (10, "\tx res %d\n", scanner->x_res);
+ DBG (10, "\ty res %d\n", scanner->y_res);
+/*************************/
+
+ return SANE_STATUS_GOOD;
+} /* sane_get_parameters */
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ struct sp15c *scanner = handle;
+ ssize_t nread;
+
+ DBG (10, "sane_read\n");
+ *len = 0;
+
+ nread = read (scanner->pipe, buf, max_len);
+ DBG (10, "sane_read: read %ld bytes of %ld\n",
+ (long) nread, (long) max_len);
+
+ if (scanner->scanning == SANE_FALSE)
+ {
+ /* PREDICATE WAS (!(scanner->scanning)) */
+ return do_cancel (scanner);
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel (scanner);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0)
+ return do_eof (scanner); /* close pipe */
+
+ return SANE_STATUS_GOOD;
+} /* sane_read */
+
+
+void
+sane_cancel (SANE_Handle h)
+{
+ DBG (10, "sane_cancel\n");
+ do_cancel ((struct sp15c *) h);
+} /* sane_cancel */
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close\n");
+ if (((struct sp15c *) handle)->scanning == SANE_TRUE)
+ do_cancel (handle);
+} /* sane_close */
+
+
+void
+sane_exit (void)
+{
+ struct sp15c *dev, *next;
+
+ DBG (10, "sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->devicename);
+ free (dev->buffer);
+ free (dev);
+ }
+} /* sane_exit */
+
+/* }################ internal (support) routines ################{ */
+
+static SANE_Status
+attach_scanner (const char *devicename, struct sp15c **devp)
+{
+ struct sp15c *dev;
+ int sfd;
+
+ DBG (15, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (15, "attach_scanner: opening %s\n", devicename);
+ if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0)
+ {
+ DBG (5, "attach_scanner: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (NULL == (dev = malloc (sizeof (*dev))))
+ return SANE_STATUS_NO_MEM;
+
+ dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024))
+ ? sanei_scsi_max_request_size
+ : 64 * 1024;
+
+ if ((dev->buffer = malloc (dev->row_bufsize)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ if (sp15c_identify_scanner (dev) != 0)
+ {
+ DBG (5, "attach_scanner: scanner-identification failed\n");
+ sanei_scsi_close (dev->sfd);
+ free (dev->buffer);
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+#if 0
+ /* Get MUD (via mode_sense), internal info (via get_internal_info), and
+ * initialize values */
+ coolscan_initialize_values (dev);
+#endif
+
+ /* Why? */
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.model = dev->product;
+ dev->sane.type = "flatbed/ADF scanner";
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ DBG (15, "attach_scanner: done\n");
+
+ return SANE_STATUS_GOOD;
+} /* attach_scanner */
+
+static SANE_Status
+attach_one (const char *name)
+{
+ return attach_scanner (name, 0);
+} /* attach_one */
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ scsi_fd = scsi_fd;
+ arg = arg; /* silence compilation warnings */
+
+ return request_sense_parse (result);
+} /* sense_handler */
+
+static int
+request_sense_parse (u_char * sensed_data)
+{
+ unsigned int ret, sense, asc, ascq;
+ sense = get_RS_sense_key (sensed_data);
+ asc = get_RS_ASC (sensed_data);
+ ascq = get_RS_ASCQ (sensed_data);
+
+ ret = SANE_STATUS_IO_ERROR;
+
+ switch (sense)
+ {
+ case 0x0: /* No Sense */
+ DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq);
+ return SANE_STATUS_GOOD;
+
+ case 0x2: /* Not Ready */
+ if ((0x00 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Not Ready \n", sense, asc, ascq);
+ }
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ break;
+
+ case 0x3: /* Medium Error */
+ if ((0x80 == asc) && (0x01 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Jam \n", sense, asc, ascq);
+ ret = SANE_STATUS_JAMMED;
+ }
+ else if ((0x80 == asc) && (0x02 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: ADF cover open \n", sense, asc, ascq);
+ ret = SANE_STATUS_COVER_OPEN;
+ }
+ else if ((0x80 == asc) && (0x03 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: ADF empty \n", sense, asc, ascq);
+ ret = SANE_STATUS_NO_DOCS;
+ }
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ break;
+
+ case 0x4: /* Hardware Error */
+ if ((0x80 == asc) && (0x01 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: FB motor fuse \n", sense, asc, ascq);
+ }
+ else if ((0x80 == asc) && (0x02 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: heater fuse \n", sense, asc, ascq);
+ }
+ else if ((0x80 == asc) && (0x04 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: ADF motor fuse \n", sense, asc, ascq);
+ }
+ else if ((0x80 == asc) && (0x05 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: mechanical alarm \n", sense, asc, ascq);
+ }
+ else if ((0x80 == asc) && (0x06 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: optical alarm \n", sense, asc, ascq);
+ }
+ else if ((0x44 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: abnormal internal target \n", sense, asc, ascq);
+ }
+ else if ((0x47 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: SCSI parity error \n", sense, asc, ascq);
+ }
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ break;
+
+ case 0x5: /* Illegal Request */
+ if ((0x20 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Invalid command \n", sense, asc, ascq);
+ ret = SANE_STATUS_INVAL;
+ }
+ else if ((0x24 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Invalid field in CDB \n", sense, asc, ascq);
+ ret = SANE_STATUS_INVAL;
+ }
+ else if ((0x25 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Unsupported logical unit \n", sense, asc, ascq);
+ ret = SANE_STATUS_UNSUPPORTED;
+ }
+ else if ((0x26 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Invalid field in parm list \n", sense, asc, ascq);
+ ret = SANE_STATUS_INVAL;
+ }
+ else if ((0x2C == asc) && (0x02 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: wrong window combination \n", sense, asc, ascq);
+ ret = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ break;
+
+ case 0x6: /* Unit Attention */
+ if ((0x00 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: UNIT ATTENTION \n", sense, asc, ascq);
+ }
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ break;
+
+ case 0xb: /* Aborted Command */
+ if ((0x43 == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Message error \n", sense, asc, ascq);
+ }
+ else if ((0x80 == asc) && (0x01 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: Image transfer error \n", sense, asc, ascq);
+ }
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ break;
+
+ default:
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ }
+ return ret;
+} /* request_sense_parse */
+
+static SANE_Status
+sp15c_identify_scanner (struct sp15c *s)
+{
+ char vendor[9];
+ char product[0x11];
+ char version[5];
+ char *pp;
+ SANE_Status ret;
+
+ DBG (10, "identify_scanner\n");
+
+ vendor[8] = product[0x10] = version[4] = 0;
+
+ if ((ret = sp15c_do_inquiry (s)))
+ {
+ DBG (5, "identify_scanner: inquiry failed\n");
+ return ret;
+ }
+ if (get_IN_periph_devtype (s->buffer) != IN_periph_devtype_scanner)
+ {
+ DBG (5, "identify_scanner: not a scanner\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ get_IN_vendor ((char *) s->buffer, vendor);
+ get_IN_product ((char *)s->buffer, product);
+ get_IN_version ((char *)s->buffer, version);
+
+ if (strncmp ("FCPA ", vendor, 8))
+ {
+ DBG (5, "identify_scanner: \"%s\" isn't a Fujitsu product\n", vendor);
+ return 1;
+ }
+
+ pp = &vendor[8];
+ vendor[8] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &product[0x10];
+ product[0x10] = ' ';
+ while (*(pp - 1) == ' ')
+ {
+ *pp-- = '\0';
+ } /* leave one blank at the end! */
+
+ pp = &version[4];
+ version[4] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ if (get_IN_adf (s->buffer))
+ {
+ s->autofeeder = 1;
+ }
+ else
+ {
+ s->autofeeder = 0;
+ }
+
+ DBG (10, "Found %s scanner %s version %s on device %s %x/%x/%x\n",
+ vendor, product, version, s->devicename,
+ s->autofeeder, get_IN_color_mode (s->buffer),
+ get_IN_color_seq (s->buffer));
+
+ vendor[8] = '\0';
+ product[16] = '\0';
+ version[4] = '\0';
+
+ strncpy (s->vendor, vendor, 9);
+ strncpy (s->product, product, 17);
+ strncpy (s->version, version, 5);
+
+ return SANE_STATUS_GOOD;
+} /* sp15c_identify_scanner */
+
+static SANE_Status
+sp15c_do_inquiry (struct sp15c *s)
+{
+ static SANE_Status ret;
+
+ DBG (10, "do_inquiry\n");
+
+ memset (s->buffer, '\0', 256); /* clear buffer */
+ set_IN_return_size (inquiryB.cmd, 96);
+
+ ret = do_scsi_cmd (s->sfd, inquiryB.cmd, inquiryB.size, s->buffer, 96);
+ return ret;
+} /* sp15c_do_inquiry */
+
+static SANE_Status
+do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len)
+{
+ SANE_Status ret;
+ size_t ol = out_len;
+
+ hexdump (20, "<cmd<", cmd, cmd_len);
+
+ ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol);
+ if ((out_len != 0) && (out_len != ol))
+ {
+ DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n",
+ (u_long) out_len, (u_long) ol);
+ }
+ if (ret)
+ {
+ DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret);
+ }
+ DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol);
+ if (out != NULL && out_len != 0)
+ hexdump (15, ">rslt>", out, (out_len > 0x60) ? 0x60 : out_len);
+
+ return ret;
+} /* do_scsi_cmd */
+
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+} /* hexdump */
+
+static SANE_Status
+init_options (struct sp15c *scanner)
+{
+ int i;
+
+ DBG (10, "init_options\n");
+
+ memset (scanner->opt, 0, sizeof (scanner->opt));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+/************** "Mode" group: **************/
+ scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ scanner->opt[OPT_MODE_GROUP].desc = "";
+ scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* source */
+
+ scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_SOURCE].size = max_string_size (source_list);
+ scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
+ if (scanner->autofeeder)
+ {
+ scanner->opt[OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT
+ | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* scan mode */
+ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ scanner->opt[OPT_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ /* negative */
+ scanner->opt[OPT_TYPE].name = "type";
+ scanner->opt[OPT_TYPE].title = "Film type";
+ scanner->opt[OPT_TYPE].desc = "positive or negative image";
+ scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_TYPE].size = max_string_size (type_list);
+ scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_TYPE].constraint.string_list = type_list;
+
+ scanner->opt[OPT_PRESCAN].name = "prescan";
+ scanner->opt[OPT_PRESCAN].title = "Prescan";
+ scanner->opt[OPT_PRESCAN].desc = "Perform a prescan during preview";
+ scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE;
+
+ /* resolution */
+ scanner->opt[OPT_X_RES].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RES].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ scanner->opt[OPT_X_RES].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ scanner->opt[OPT_X_RES].type = SANE_TYPE_INT;
+ scanner->opt[OPT_X_RES].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_X_RES].constraint.word_list = x_res_list;
+ scanner->opt[OPT_X_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ scanner->opt[OPT_Y_RES].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RES].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RES].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RES].type = SANE_TYPE_INT;
+ scanner->opt[OPT_Y_RES].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_Y_RES].constraint.word_list = y_res_list;
+ scanner->opt[OPT_Y_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ scanner->opt[OPT_PREVIEW_RES].name = "preview-resolution";
+ scanner->opt[OPT_PREVIEW_RES].title = "Preview resolution";
+ scanner->opt[OPT_PREVIEW_RES].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_PREVIEW_RES].type = SANE_TYPE_INT;
+ scanner->opt[OPT_PREVIEW_RES].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_PREVIEW_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_PREVIEW_RES].constraint.word_list = resolution_list;
+
+/************** "Geometry" group: **************/
+ scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
+ scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_X].constraint.range = &x_range;
+ scanner->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ /* top-left y */
+ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_Y].constraint.range = &y_range_fb;
+ scanner->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ /* bottom-right x */
+ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_X].constraint.range = &x_range;
+ scanner->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+ /* bottom-right y */
+ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_Y].constraint.range = &y_range_fb;
+ scanner->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+
+
+ /* ------------------------------ */
+
+/************** "Enhancement" group: **************/
+ scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ scanner->opt[OPT_AVERAGING].name = "averaging";
+ scanner->opt[OPT_AVERAGING].title = "Averaging";
+ scanner->opt[OPT_AVERAGING].desc = "Averaging";
+ scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE;
+
+ scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_DETECT;
+
+ scanner->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ scanner->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_THRESHOLD].constraint.range = &threshold_range;
+ scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT;
+
+ /* ------------------------------ */
+
+/************** "Advanced" group: **************/
+ scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
+ scanner->opt[OPT_ADVANCED_GROUP].desc = "";
+ scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* preview */
+ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+
+ DBG (10, "init_options:ok\n");
+ return SANE_STATUS_GOOD;
+} /* init_options */
+
+static int
+sp15c_check_values (struct sp15c *s)
+{
+ if (s->use_adf == SANE_TRUE && s->autofeeder == 0)
+ {
+ DBG (1, "sp15c_check_values: %s\n",
+ "ERROR: ADF-MODE NOT SUPPORTED BY SCANNER, ABORTING");
+ return (1);
+ }
+ return (0);
+} /* sp15c_check_values */
+
+/* sp15c_free_scanner should go through the following sequence:
+ * OBJECT POSITION DISCHARGE
+ * GOOD
+ * RELEASE UNIT
+ * GOOD
+ */
+static int
+sp15c_free_scanner (struct sp15c *s)
+{
+ int ret;
+ DBG (10, "sp15c_free_scanner\n");
+#if 0
+ /* hmg: reports from several people show that this code ejects two pages
+ instead of one. So I've commented it out for now. */
+ ret = sp15c_object_discharge (s);
+ if (ret)
+ return ret;
+#endif
+
+ wait_scanner (s);
+
+ ret = do_scsi_cmd (s->sfd, release_unitB.cmd, release_unitB.size, NULL, 0);
+ if (ret)
+ return ret;
+
+ DBG (10, "sp15c_free_scanner: ok\n");
+ return ret;
+} /* sp15c_free_scanner */
+
+/* sp15c_grab_scanner should go through the following command sequence:
+ * TEST UNIT READY
+ * CHECK CONDITION \
+ * REQUEST SENSE > These should be handled automagically by
+ * UNIT ATTENTION / the kernel if they happen (powerup/reset)
+ * TEST UNIT READY
+ * GOOD
+ * RESERVE UNIT
+ * GOOD
+ *
+ * It is then responsible for installing appropriate signal handlers
+ * to call emergency_give_scanner() if user aborts.
+ */
+
+static int
+sp15c_grab_scanner (struct sp15c *s)
+{
+ int ret;
+
+ DBG (10, "sp15c_grab_scanner\n");
+ wait_scanner (s);
+
+ ret = do_scsi_cmd (s->sfd, reserve_unitB.cmd, reserve_unitB.size, NULL, 0);
+ if (ret)
+ return ret;
+
+ DBG (10, "sp15c_grab_scanner: ok\n");
+ return 0;
+} /* sp15c_grab_scanner */
+
+/*
+ * wait_scanner spins until TEST_UNIT_READY returns 0 (GOOD)
+ * returns 0 on success,
+ * returns -1 on error or timeout
+ */
+static int
+wait_scanner (struct sp15c *s)
+{
+ int ret = -1;
+ int cnt = 0;
+
+ DBG (10, "wait_scanner\n");
+
+ while (ret != 0)
+ {
+ ret = do_scsi_cmd (s->sfd, test_unit_readyB.cmd,
+ test_unit_readyB.size, 0, 0);
+ if (ret == SANE_STATUS_DEVICE_BUSY)
+ {
+ usleep (50000); /* wait 0.05 seconds */
+ /* 20 sec. max (prescan takes up to 15 sec.) */
+ if (cnt++ > 400)
+ {
+ DBG (1, "wait_scanner: scanner does NOT get ready\n");
+ return -1;
+ }
+ }
+ else if (ret == SANE_STATUS_GOOD)
+ {
+ DBG (10, "wait_scanner: ok\n");
+ return ret;
+ }
+ else
+ {
+ DBG (1, "wait_scanner: unit ready failed (%s)\n",
+ sane_strstatus (ret));
+ }
+ }
+ DBG (10, "wait_scanner: ok\n");
+ return 0;
+} /* wait_scanner */
+
+/* As noted above, this command has been supplanted by the
+ MEDIA CHECK command. Nevertheless, it's still available
+ here in case some future need is discovered. */
+static int
+sp15c_object_position (struct sp15c *s)
+{
+ int ret;
+ DBG (10, "sp15c_object_position\n");
+ if (s->use_adf != SANE_TRUE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ if (s->autofeeder == 0)
+ {
+ DBG (10, "sp15c_object_position: Autofeeder not present.\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ memcpy (s->buffer, object_positionB.cmd, object_positionB.size);
+ set_OP_autofeed (s->buffer, OP_Feed);
+ ret = do_scsi_cmd (s->sfd, s->buffer,
+ object_positionB.size, NULL, 0);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ return ret;
+ }
+ wait_scanner (s);
+ DBG (10, "sp15c_object_position: ok\n");
+ return ret;
+} /* sp15c_object_position */
+
+static int
+sp15c_media_check (struct sp15c *s)
+{
+ static SANE_Status ret;
+ DBG (10, "sp15c_media_check\n");
+ if (s->use_adf != SANE_TRUE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ if (s->autofeeder == 0)
+ {
+ DBG (10, "sp15c_media_check: Autofeeder not present.\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ memset (s->buffer, '\0', 256); /* clear buffer */
+ set_MC_return_size (media_checkB.cmd, 1);
+
+ ret = do_scsi_cmd (s->sfd, media_checkB.cmd, media_checkB.size, s->buffer, 1);
+
+ if (ret != SANE_STATUS_GOOD)
+ {
+ return ret;
+ }
+ wait_scanner (s);
+
+ if (get_MC_adf_status (s->buffer) == MC_ADF_OK)
+ {
+ DBG (10, "sp15c_media_check: ok\n");
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_NO_DOCS;
+
+} /* sp15c_media_check */
+
+static SANE_Status
+do_cancel (struct sp15c *scanner)
+{
+ DBG (10, "do_cancel\n");
+ swap_res (scanner);
+
+ do_eof (scanner); /* close pipe and reposition scanner */
+
+ if (scanner->reader_pid != -1)
+ {
+ int exit_status;
+ DBG (10, "do_cancel: kill reader_process\n");
+ /* ensure child knows it's time to stop: */
+ sanei_thread_kill (scanner->reader_pid);
+ DBG (50, "wait for scanner to stop\n");
+ sanei_thread_waitpid (scanner->reader_pid, &exit_status);
+ scanner->reader_pid = -1;
+ }
+
+ if (scanner->sfd >= 0)
+ {
+ sp15c_free_scanner (scanner);
+ DBG (10, "do_cancel: close filedescriptor\n");
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+} /* do_cancel */
+
+static void
+swap_res (struct sp15c *s)
+{
+ s = s; /* silence compilation warnings */
+
+ /* for the time being, do nothing */
+} /* swap_res */
+
+static int
+sp15c_object_discharge (struct sp15c *s)
+{
+ int ret;
+
+ DBG (10, "sp15c_object_discharge\n");
+ if (s->use_adf != SANE_TRUE)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy (s->buffer, object_positionB.cmd, object_positionB.size);
+ set_OP_autofeed (s->buffer, OP_Discharge);
+ ret = do_scsi_cmd (s->sfd, s->buffer,
+ object_positionB.size, NULL, 0);
+ wait_scanner (s);
+ DBG (10, "sp15c_object_discharge: ok\n");
+ return ret;
+} /* sp15c_object_discharge */
+
+static int
+sp15c_set_window_param (struct sp15c *s, int prescan)
+{
+ unsigned char buffer_r[WDB_size_max];
+ int ret;
+ int active_buffer_size;
+
+ prescan = prescan; /* silence compilation warnings */
+
+ wait_scanner (s);
+ DBG (10, "set_window_param\n");
+ memset (buffer_r, '\0', WDB_size_max); /* clear buffer */
+ memcpy (buffer_r, window_descriptor_blockB.cmd,
+ window_descriptor_blockB.size); /* copy preset data */
+
+ set_WD_wid (buffer_r, WD_wid_all); /* window identifier */
+
+ set_WD_Xres (buffer_r, s->x_res); /* x resolution in dpi */
+ set_WD_Yres (buffer_r, s->y_res); /* y resolution in dpi */
+
+ set_WD_ULX (buffer_r, s->tl_x); /* top left x */
+ set_WD_ULY (buffer_r, s->tl_y); /* top left y */
+
+ set_WD_width (buffer_r, s->br_x - s->tl_x);
+ set_WD_length (buffer_r, s->br_y - s->tl_y);
+
+ set_WD_brightness (buffer_r, s->brightness);
+ set_WD_threshold (buffer_r, s->threshold);
+ set_WD_contrast (buffer_r, s->contrast);
+ set_WD_bitsperpixel (buffer_r, s->bitsperpixel);
+ set_WD_rif (buffer_r, s->rif);
+ set_WD_pad (buffer_r, 0x3);
+ set_WD_halftone (buffer_r, s->halftone);
+ set_WD_bitorder (buffer_r, s->bitorder);
+ set_WD_compress_type (buffer_r, s->compress_type);
+ set_WD_compress_arg (buffer_r, s->compress_arg);
+ set_WD_composition (buffer_r, s->composition); /* may be overridden below */
+ set_WD_vendor_id_code (buffer_r, 0xff);
+ set_WD_adf (buffer_r, s->use_adf == SANE_TRUE ? 1 : 0);
+ set_WD_source (buffer_r, 1);
+ set_WD_highlight_color (buffer_r, 0xff);
+ set_WD_shadow_value (buffer_r, 0x00);
+
+ switch (s->composition)
+ {
+ case WD_comp_LA:
+ case WD_comp_HT:
+ set_WD_line_width (buffer_r, ((s->br_x - s->tl_x) * s->x_res / 1200) / 8);
+ set_WD_line_count (buffer_r, (s->br_y - s->tl_y) * s->y_res / 1200);
+ goto b_and_w;
+ case WD_comp_G4:
+ set_WD_line_width (buffer_r, ((s->br_x - s->tl_x) * s->x_res / 1200) / 2);
+ set_WD_line_count (buffer_r, (s->br_y - s->tl_y) * s->y_res / 1200);
+ goto grayscale;
+ case WD_comp_G8:
+ set_WD_line_width (buffer_r, (s->br_x - s->tl_x) * s->x_res / 1200);
+ set_WD_line_count (buffer_r, (s->br_y - s->tl_y) * s->y_res / 1200);
+ grayscale:
+ set_WD_composition (buffer_r, WD_comp_GS);
+ b_and_w:
+ set_WD_color (buffer_r, WD_color_greenx);
+ break;
+ case WD_comp_MC:
+ set_WD_color (buffer_r, WD_color_rgb);
+ set_WD_line_width (buffer_r, 3 * (s->br_x - s->tl_x) * s->x_res / 1200);
+ set_WD_line_count (buffer_r, (s->br_y - s->tl_y) * s->y_res / 1200);
+ break;
+ case WD_comp_BC:
+ case WD_comp_DC:
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ /* prepare SCSI-BUFFER */
+ memcpy (s->buffer, set_windowB.cmd, set_windowB.size);
+ memcpy ((s->buffer + set_windowB.size),
+ window_parameter_data_blockB.cmd,
+ window_parameter_data_blockB.size);
+ memcpy ((s->buffer + set_windowB.size + window_parameter_data_blockB.size),
+ buffer_r, window_descriptor_blockB.size);
+
+ set_SW_xferlen (s->buffer,
+ (window_parameter_data_blockB.size
+ + WDB_size_Color));
+ set_WDPB_wdblen ((s->buffer
+ + set_windowB.size),
+ WDB_size_Color);
+ set_WD_parm_length ((s->buffer
+ + set_windowB.size
+ + window_parameter_data_blockB.size),
+ 9);
+
+ DBG (10, "\tx_res=%d, y_res=%d\n",
+ s->x_res, s->y_res);
+ DBG (10, "\tupper left-x=%d, upper left-y=%d\n",
+ s->tl_x, s->tl_y);
+ DBG (10, "\twindow width=%d, length=%d\n",
+ s->br_x - s->tl_x,
+ s->br_y - s->tl_y);
+
+ active_buffer_size = set_windowB.size + get_SW_xferlen (s->buffer);
+
+ hexdump (15, "Window set", s->buffer, active_buffer_size);
+
+ ret = do_scsi_cmd (s->sfd, s->buffer, active_buffer_size, NULL, 0);
+ if (ret)
+ return ret;
+
+ DBG (10, "set_window_param: ok\n");
+ return ret;
+} /* sp15c_set_window_param */
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+} /* max_string_size */
+
+static int
+sp15c_start_scan (struct sp15c *s)
+{
+ int ret;
+ DBG (10, "sp15c_start_scan\n");
+ ret = do_scsi_cmd (s->sfd, scanB.cmd, scanB.size, NULL, 0);
+ if (ret)
+ return ret;
+ DBG (10, "sp15c_start_scan:ok\n");
+ return ret;
+} /* sp15c_start_scan */
+
+static void
+sigterm_handler (int signal)
+{
+ signal = signal; /* silence compilation warnings */
+
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+ _exit (SANE_STATUS_GOOD);
+} /* sigterm_handler */
+
+/* This function is executed as a child process. */
+static int
+reader_process (void *data)
+{
+ struct sp15c *scanner = (struct sp15c *) data;
+ int pipe_fd = scanner->reader_pipe;
+
+ int status;
+ unsigned int data_left;
+ unsigned int data_to_read;
+ FILE *fp;
+ sigset_t ignore_set;
+ sigset_t sigterm_set;
+ struct SIGACTION act;
+ unsigned int i;
+ unsigned char *src, *dst;
+
+ DBG (10, "reader_process started\n");
+
+ if (sanei_thread_is_forked ())
+ close (scanner->pipe);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ fp = fdopen (pipe_fd, "w");
+ if (!fp)
+ {
+ DBG (1, "reader_process: couldn't open pipe!\n");
+ return 1;
+ }
+
+ DBG (10, "reader_process: starting to READ data\n");
+
+ data_left = bytes_per_line (scanner) *
+ lines_per_scan (scanner);
+
+ sp15c_trim_rowbufsize (scanner); /* trim bufsize */
+
+ DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n",
+ data_left, scanner->row_bufsize);
+
+ memset (&act, 0, sizeof (act));
+
+ act.sa_handler = sigterm_handler;
+ sigaction (SIGTERM, &act, 0);
+ /* wait_scanner(scanner); */
+ do
+ {
+ data_to_read = (data_left < scanner->row_bufsize)
+ ? data_left
+ : scanner->row_bufsize;
+
+ if (scanner->composition == WD_comp_G4)
+ {
+ data_to_read /= 2; /* 'cause each byte holds two pixels */
+ }
+ status = sp15c_read_data_block (scanner, data_to_read);
+ if (status == 0)
+ {
+ DBG (1, "reader_process: no data yet\n");
+ fflush (stdout);
+ fflush (stderr);
+ usleep (50000);
+ continue;
+ }
+ if (status == -1)
+ {
+ DBG (1, "reader_process: unable to get image data from scanner!\n");
+ fflush (stdout);
+ fflush (stderr);
+ fclose (fp);
+ return (-1);
+ }
+
+ /* expand 4-bit pixels to 8-bit bytes */
+ if (scanner->composition == WD_comp_G4)
+ {
+ src = &scanner->buffer[data_to_read - 1];
+ dst = &scanner->buffer[data_to_read * 2 - 1];
+ for (i = 0; i < data_to_read; i++)
+ {
+ *dst-- = ((*src << 4) & 0xf0) + ((*src) & 0x0f);
+ *dst-- = ((*src) & 0xf0) + ((*src >> 4) & 0x0f);
+ --src;
+ }
+ data_to_read *= 2;
+ }
+
+ fwrite (scanner->buffer, 1, data_to_read, fp);
+ fflush (fp);
+
+ data_left -= data_to_read;
+ DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n",
+ data_to_read, data_left);
+ fflush (stdout);
+ fflush (stderr);
+ }
+ while (data_left);
+
+ fclose (fp);
+
+ DBG (10, "reader_process: finished\n");
+
+ return 0;
+} /* reader_process */
+
+static SANE_Status
+do_eof (struct sp15c *scanner)
+{
+ DBG (10, "do_eof\n");
+
+ scanner->scanning = SANE_FALSE;
+ if (scanner->pipe >= 0)
+ {
+ close (scanner->pipe);
+ scanner->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+} /* do_eof */
+
+static int
+pixels_per_line (struct sp15c *s)
+{
+ int pixels;
+ pixels = s->x_res * (s->br_x - s->tl_x) / 1200;
+ return pixels;
+} /* pixels_per_line */
+
+static int
+lines_per_scan (struct sp15c *s)
+{
+ int lines;
+ lines = s->y_res * (s->br_y - s->tl_y) / 1200;
+ return lines;
+} /* lines_per_scan */
+
+static int
+bytes_per_line (struct sp15c *s)
+{
+ int bytes;
+ /* SEE PAGE 29 OF SANE SPEC!!! */
+ if (s->bitsperpixel == 1)
+ {
+ bytes = (pixels_per_line (s) + 7) / 8;
+ }
+ else
+ {
+ bytes = pixels_per_line (s);
+ }
+ if (s->composition == WD_comp_MC)
+ {
+ bytes *= 3;
+ }
+ return bytes;
+} /* bytes_per_line */
+
+static void
+sp15c_trim_rowbufsize (struct sp15c *s)
+{
+ unsigned int row_len;
+ row_len = bytes_per_line (s);
+ if (s->row_bufsize >= row_len)
+ {
+ s->row_bufsize = s->row_bufsize - (s->row_bufsize % row_len);
+ DBG (10, "trim_rowbufsize to %d (%d lines)\n",
+ s->row_bufsize, s->row_bufsize / row_len);
+ }
+} /* sp15c_trim_rowbufsize */
+
+static int
+sp15c_read_data_block (struct sp15c *s, unsigned int length)
+{
+ int r;
+
+ DBG (10, "sp15c_read_data_block (length = %d)\n", length);
+ /*wait_scanner(s); */
+
+ set_R_datatype_code (readB.cmd, R_datatype_imagedata);
+ set_R_xfer_length (readB.cmd, length);
+
+ r = do_scsi_cmd (s->sfd, readB.cmd, readB.size, s->buffer, length);
+#if 0
+ return ((r != 0) ? -1 : length);
+#else
+ if (r)
+ {
+ return -1;
+ }
+ else
+ {
+ return length;
+ }
+#endif
+} /* sp15c_read_data_block */
+
+/* Increase initial width by increasing bottom right X
+ * until we have a HAPPY number of bytes in line.
+ * Should be called whenever s->composition, s->x_res,
+ * s->br_x, or s->tl_x are changed */
+static void
+adjust_width (struct sp15c *s, SANE_Int * info)
+{
+ int pixels;
+ int changed = 0;
+ if (s->composition == WD_comp_MC)
+ {
+ while (pixels = s->x_res * (s->br_x - s->tl_x) / 1200,
+ (s->bitsperpixel * pixels) % (8 * 4))
+ {
+ s->br_x--;
+ changed++;
+ }
+ }
+ else
+ {
+ while (pixels = s->x_res * (s->br_x - s->tl_x) / 1200,
+ (s->bitsperpixel * pixels) % 8)
+ {
+ s->br_x--;
+ changed++;
+ }
+ }
+ if (changed && info)
+ *info |= SANE_INFO_INEXACT;
+
+ return;
+} /* adjust_width */
+
+static SANE_Status
+apply_constraints (struct sp15c *scanner, SANE_Int opt,
+ SANE_Int * target, SANE_Word * info)
+{
+ SANE_Status status;
+ status = sanei_constrain_value (scanner->opt + opt, target, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (SANE_CONSTRAINT_RANGE ==
+ scanner->opt[opt].constraint_type)
+ {
+ if (*target < scanner->opt[opt].constraint.range->min)
+ {
+ *target = scanner->opt[opt].constraint.range->min;
+ return SANE_STATUS_GOOD;
+ }
+ else if (scanner->opt[opt].constraint.range->max < *target)
+ {
+ *target = scanner->opt[opt].constraint.range->max;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ return status;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+} /* apply_constraints */
+
+/******************************************************************************
+}#############################################################################
+******************************************************************************/
diff --git a/backend/sp15c.conf.in b/backend/sp15c.conf.in
new file mode 100644
index 0000000..b85e6d2
--- /dev/null
+++ b/backend/sp15c.conf.in
@@ -0,0 +1 @@
+scsi FCPA
diff --git a/backend/sp15c.h b/backend/sp15c.h
new file mode 100644
index 0000000..52535e0
--- /dev/null
+++ b/backend/sp15c.h
@@ -0,0 +1,336 @@
+#ifndef SP15C_H
+#define SP15C_H
+
+static const char RCSid_h[] = "$Header$";
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Fujitsu ScanParner 15c
+ flatbed/ADF scanners. It was derived from the COOLSCAN driver.
+ Written by Randolph Bentson <bentson@holmsjoen.com> */
+
+/* ------------------------------------------------------------------------- */
+/*
+ * $Log$
+ * Revision 1.8 2008/05/15 12:50:24 ellert-guest
+ * Fix for bug #306751: sanei-thread with pthreads on 64 bit
+ *
+ * Revision 1.7 2005-09-19 19:57:48 fzago-guest
+ * Replaced __unused__ with __sane_unused__ to avoid a namespace conflict.
+ *
+ * Revision 1.6 2004/11/13 19:53:04 fzago-guest
+ * Fixes some warnings.
+ *
+ * Revision 1.5 2004/05/23 17:28:56 hmg-guest
+ * Use sanei_thread instead of fork() in the unmaintained backends.
+ * Patches from Mattias Ellert (bugs: 300635, 300634, 300633, 300629).
+ *
+ * Revision 1.4 2003/12/27 17:48:38 hmg-guest
+ * Silenced some compilation warnings.
+ *
+ * Revision 1.3 2000/08/12 15:09:42 pere
+ * Merge devel (v1.0.3) into head branch.
+ *
+ * Revision 1.1.2.3 2000/03/14 17:47:14 abel
+ * new version of the Sharp backend added.
+ *
+ * Revision 1.1.2.2 2000/01/26 03:51:50 pere
+ * Updated backends sp15c (v1.12) and m3096g (v1.11).
+ *
+ * Revision 1.7 2000/01/05 05:22:26 bentson
+ * indent to barfable GNU style
+ *
+ * Revision 1.6 1999/12/03 20:57:13 bentson
+ * add MEDIA CHECK command
+ *
+ * Revision 1.5 1999/11/24 15:55:56 bentson
+ * remove some debug stuff; rename function
+ *
+ * Revision 1.4 1999/11/23 18:54:26 bentson
+ * tidy up function types for constraint checking
+ *
+ * Revision 1.3 1999/11/23 06:41:54 bentson
+ * add debug flag to interface
+ *
+ * Revision 1.2 1999/11/22 18:15:20 bentson
+ * more work on color support
+ *
+ * Revision 1.1 1999/11/19 15:09:08 bentson
+ * cribbed from m3096g
+ *
+ */
+
+static int num_devices;
+static struct sp15c *first_dev;
+
+enum sp15c_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_SOURCE,
+ OPT_MODE,
+ OPT_TYPE,
+ OPT_X_RES,
+ OPT_Y_RES,
+ OPT_PRESCAN,
+ OPT_PREVIEW_RES,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* in mm/2^16 */
+ OPT_TL_Y, /* in mm/2^16 */
+ OPT_BR_X, /* in mm/2^16 */
+ OPT_BR_Y, /* in mm/2^16 */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_AVERAGING,
+ OPT_BRIGHTNESS,
+ OPT_THRESHOLD,
+
+ OPT_ADVANCED_GROUP,
+ OPT_PREVIEW,
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+struct sp15c
+ {
+ struct sp15c *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ SANE_Device sane;
+
+ char vendor[9];
+ char product[17];
+ char version[5];
+
+ char *devicename; /* name of the scanner device */
+ int sfd; /* output file descriptor, scanner device */
+ int pipe;
+ int reader_pipe;
+
+ int scanning; /* "in progress" flag */
+ int autofeeder; /* detected */
+ int use_adf; /* requested */
+ SANE_Pid reader_pid; /* child is running */
+ int prescan; /* ??? */
+
+/***** terms for "set window" command *****/
+ int x_res; /* resolution in */
+ int y_res; /* pixels/inch */
+ int tl_x; /* top left position, */
+ int tl_y; /* in inch/1200 units */
+ int br_x; /* bottom right position, */
+ int br_y; /* in inch/1200 units */
+
+ int brightness;
+ int threshold;
+ int contrast;
+ int composition;
+ int bitsperpixel; /* at the scanner interface */
+ int halftone;
+ int rif;
+ int bitorder;
+ int compress_type;
+ int compress_arg;
+ int vendor_id_code;
+ int outline;
+ int emphasis;
+ int auto_sep;
+ int mirroring;
+ int var_rate_dyn_thresh;
+ int white_level_follow;
+ int subwindow_list;
+ int paper_size;
+ int paper_width_X;
+ int paper_length_Y;
+/***** end of "set window" terms *****/
+
+ /* buffer used for scsi-transfer */
+ unsigned char *buffer;
+ unsigned int row_bufsize;
+
+ };
+
+/* ------------------------------------------------------------------------- */
+
+#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))
+#define mmToIlu(mm) ((mm) / length_quant)
+#define iluToMm(ilu) ((ilu) * length_quant)
+#define SP15C_CONFIG_FILE "sp15c.conf"
+
+/* ------------------------------------------------------------------------- */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle);
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking);
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int * fdp);
+
+const SANE_Option_Descriptor *
+ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option);
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info);
+
+SANE_Status
+sane_start (SANE_Handle handle);
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params);
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len);
+
+void
+ sane_cancel (SANE_Handle h);
+
+void
+ sane_close (SANE_Handle h);
+
+void
+ sane_exit (void);
+
+/* ------------------------------------------------------------------------- */
+
+static SANE_Status
+ attach_scanner (const char *devicename, struct sp15c **devp);
+
+static SANE_Status
+ sense_handler (int scsi_fd, u_char * result, void *arg);
+
+static int
+ request_sense_parse (u_char * sensed_data);
+
+static SANE_Status
+ sp15c_identify_scanner (struct sp15c *s);
+
+static SANE_Status
+ sp15c_do_inquiry (struct sp15c *s);
+
+static SANE_Status
+ do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len);
+
+static void
+ hexdump (int level, char *comment, unsigned char *p, int l);
+
+static SANE_Status
+ init_options (struct sp15c *scanner);
+
+static int
+ sp15c_check_values (struct sp15c *s);
+
+static int
+ sp15c_grab_scanner (struct sp15c *s);
+
+static int
+ sp15c_free_scanner (struct sp15c *s);
+
+static int
+ wait_scanner (struct sp15c *s);
+
+static int __sane_unused__
+ sp15c_object_position (struct sp15c *s);
+
+static SANE_Status
+ do_cancel (struct sp15c *scanner);
+
+static void
+ swap_res (struct sp15c *s);
+
+static int __sane_unused__
+ sp15c_object_discharge (struct sp15c *s);
+
+static int
+ sp15c_set_window_param (struct sp15c *s, int prescan);
+
+static size_t
+ max_string_size (const SANE_String_Const strings[]);
+
+static int
+ sp15c_start_scan (struct sp15c *s);
+
+static int
+ reader_process (void *scanner);
+
+static SANE_Status
+ do_eof (struct sp15c *scanner);
+
+static int
+ pixels_per_line (struct sp15c *s);
+
+static int
+ lines_per_scan (struct sp15c *s);
+
+static int
+ bytes_per_line (struct sp15c *s);
+
+static void
+ sp15c_trim_rowbufsize (struct sp15c *s);
+
+static int
+ sp15c_read_data_block (struct sp15c *s, unsigned int length);
+
+static SANE_Status
+ attach_one (const char *name);
+
+static void
+ adjust_width (struct sp15c *s, SANE_Int * info);
+
+static SANE_Status
+ apply_constraints (struct sp15c *s, SANE_Int opt,
+ SANE_Int * target, SANE_Word * info);
+
+static int
+ sp15c_media_check (struct sp15c *s);
+
+#endif /* SP15C_H */
diff --git a/backend/st400.c b/backend/st400.c
new file mode 100644
index 0000000..b8ee6d1
--- /dev/null
+++ b/backend/st400.c
@@ -0,0 +1,1309 @@
+/*
+vim: ts=4 sw=4 noexpandtab
+ */
+
+/* st400.c - SANE module for Siemens ST400 flatbed scanner
+
+ Copyright (C) 1999-2000 Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ *************************************************************************
+
+ This file implements a SANE backend for the Siemens ST400 flatbed scanner.
+
+ Unfortunately, I have no documentation for this scanner. All I have
+ is an old PC Pascal source and an Amiga C source (derived from the
+ Pascal source). Both are quite primitive, and so is this backend...
+
+ Version numbers of this backend follow SANE version scheme: The first
+ number is SANE's major version (i.e. the version of the SANE API that
+ this backend conforms to), the second is the version of this backend.
+ Thus, version 1.2 is the second release of this backend for SANE v1.
+
+ 1.0 (08 Mar 1999): First public release, for SANE v1.0.0
+ 1.1 (12 Mar 1999): Fixed some stupid bugs (caused crash if accessed via net).
+ 1.2 (23 Apr 1999): Oops, got threshold backwards.
+ Tested with SANE 1.0.1.
+ 1.3 (27 Apr 1999): Seems the separate MODE SELECT to switch the light on
+ is not necessary. Removed debugging output via syslog,
+ it was only used to track down a bug in saned. Some
+ minor cleanups. Removed illegal version check (only
+ frontends should do this). Made "maxread" and "delay"
+ config options instead of compile-time #define's.
+ Added model check via INQUIRY, and related changes.
+ 1.4 (29 Jun 1999): New config options to configure scanner models.
+ See st400.conf for details. These options are only
+ for testing, and will be removed someday.
+ 1.5 (26 Mar 2000): Check for optnum >= 0. Fixed some hardcoded paths in
+ manpage. ST400 locks up with reads >64KB - added
+ maxread entry to model struct. Tested with SANE 1.0.2.
+ 1.6 (08 Apr 2000): Minor cleanups.
+ 1.7 (18 Dec 2001): Security fix from Tim Waugh. Dump inquiry data to
+ "$HOME/st400.dump" instead of "/tmp/st400.dump".
+*/
+
+#include "../include/sane/config.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define BACKEND_NAME st400
+#include "../include/sane/sanei_backend.h"
+
+#include "st400.h"
+
+/* supported scanners */
+static ST400_Model st400_models[] = {
+{ 8, "SIEMENS", 16, "ST 400", 6, 0x200000UL, 65536UL, NULL, "Siemens", "ST400", "flatbed scanner" },
+{ 8, "SIEMENS", 16, "ST 800", 6, 0x200000UL, 65536UL, NULL, "Siemens", "ST800", "flatbed scanner" }, /* to be tested */
+{ 0, "", 0, "", 6, 0x200000UL, 65536UL, NULL, "Unknown", "untested", "flatbed scanner" }, /* matches anything */
+
+ /* must be last */
+ { 0, NULL, 0, NULL, 0, 0, 0, NULL, NULL, NULL, NULL }
+};
+
+
+static ST400_Device *st400_devices = NULL;
+static unsigned int st400_num_devices = 0;
+static const SANE_Device **st400_device_array = NULL;
+/* The array pointer must stay valid between calls to sane_get_devices().
+ * So we cannot modify or deallocate the array when a new device is attached
+ * - until the next call to sane_get_devices(). The array_valid bit indicates
+ * whether the array is still in sync with the device list or not (if new
+ * devices have been attached in the meantime).
+ */
+static struct {
+ unsigned array_valid: 1;
+} st400_status = { 0 };
+static size_t st400_maxread = 0;
+static size_t st400_light_delay = 0;
+static int st400_dump_data = 0;
+
+/* SCSI commands */
+#define CMD_TEST_UNIT_READY 0x00
+#define CMD_INQUIRY 0x12
+#define CMD_MODE_SELECT 0x15
+#define CMD_RESERVE 0x16
+#define CMD_RELEASE 0x17
+#define CMD_START_STOP 0x1b
+#define CMD_SET_WINDOW 0x24
+#define CMD_READ_CAPACITY 0x25 /* get window settings - unused */
+#define CMD_READ10 0x28
+
+/* prototypes */
+static SANE_Status st400_inquiry( int fd, ST400_Model **modelP );
+static SANE_Status st400_sense_handler( int fd, SANE_Byte *result, void *arg );
+static SANE_Status st400_attach( const char *devname, ST400_Device **devP );
+static SANE_Status st400_attach_one( const char *device );
+static void st400_init_options( ST400_Device *dev );
+static SANE_Status st400_set_window( ST400_Device *dev );
+static SANE_Status st400_wait_ready( int fd );
+static SANE_Status st400_cmd6( int fd, SANE_Byte cmd, SANE_Byte ctrl );
+static SANE_Status st400_read10( int fd, SANE_Byte *buf, size_t *lenP );
+static void st400_reset_options( ST400_Device *dev );
+
+#undef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define maxval(bpp) ((1<<(bpp))-1)
+
+/* Debugging levels */
+#define DERR 0 /* errors */
+#define DINFO 1 /* misc information */
+#define DSENSE 2 /* SCSI sense */
+#define DSCSI 3 /* SCSI commands */
+#define DOPT 4 /* option control */
+#define DVAR 5 /* important variables */
+#define DCODE 6 /* code flow */
+
+
+/*********************************************************************
+ * low-level SCSI functions
+ *********************************************************************/
+
+#define set24(m, x) do { \
+ *((SANE_Byte *)(m)+0) = (SANE_Byte)(((x) >> 16) & 0xff); \
+ *((SANE_Byte *)(m)+1) = (SANE_Byte)(((x) >> 8) & 0xff); \
+ *((SANE_Byte *)(m)+2) = (SANE_Byte)(((x) >> 0) & 0xff); \
+} while(0)
+#define set16(m, x) do { \
+ *((SANE_Byte *)(m)+0) = (SANE_Byte)(((x) >> 8) & 0xff); \
+ *((SANE_Byte *)(m)+1) = (SANE_Byte)(((x) >> 0) & 0xff); \
+} while(0)
+
+
+static int str_at_offset(char *str, size_t offset, unsigned char *data)
+{
+ size_t len;
+
+ len = strlen(str);
+ return !strncmp((char *)&data[offset], str, len);
+}
+
+
+static SANE_Status
+st400_inquiry( int fd, ST400_Model **modelP )
+{
+ struct { SANE_Byte cmd, lun, reserved[2], tr_len, ctrl; } scsi_cmd;
+/*
+ struct {
+ SANE_Byte devtype, devqual, version;
+ SANE_Byte reserved1, additionallength;
+ SANE_Byte reserved2[2], flags;
+ SANE_Byte vendor[8], product[16], release[4];
+ SANE_Byte reserved3[60];
+ } inqdata;
+*/
+ struct { SANE_Byte bytes[96]; } inqdata;
+
+ size_t inqlen;
+ SANE_Status status;
+ ST400_Model *model;
+
+ inqlen = sizeof(inqdata);
+ memset(&scsi_cmd, 0, sizeof(scsi_cmd));
+ scsi_cmd.cmd = CMD_INQUIRY;
+ scsi_cmd.tr_len = inqlen;
+
+ DBG(DSCSI, "SCSI: sending INQUIRY (%lu bytes)\n", (u_long)inqlen);
+ status = sanei_scsi_cmd(fd, &scsi_cmd, sizeof(scsi_cmd), &inqdata, &inqlen);
+ DBG(DSCSI, "SCSI: result=%s (%lu bytes)\n", sane_strstatus(status), (u_long)inqlen);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+
+ if( st400_dump_data ) {
+ const char *home = getenv ("HOME");
+ char basename[] = "st400.dump";
+ char *name;
+ FILE *fp;
+
+ if (home) {
+ name = malloc (strlen (home) + sizeof (basename) + 1);
+ sprintf (name, "%s/%s", home, basename);
+ } else name = basename;
+
+ fp = fopen(name, "ab");
+ if( fp != NULL ) {
+ fwrite(inqdata.bytes, 1, inqlen, fp);
+ fclose(fp);
+ }
+
+ if (name != basename)
+ free (name);
+ }
+
+ if( inqlen != sizeof(inqdata) )
+ return SANE_STATUS_IO_ERROR;
+
+ for( model = st400_models; model->inq_vendor; model++ ) {
+ if( str_at_offset(model->inq_vendor, model->inq_voffset, inqdata.bytes) && str_at_offset(model->inq_model, model->inq_moffset, inqdata.bytes) ) {
+ *modelP = model;
+ DBG(DINFO, "found matching scanner model \"%s %s\" in list\n", model->sane_vendor, model->sane_model);
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+static SANE_Status
+st400_cmd6( int fd, SANE_Byte cmd, SANE_Byte ctrl )
+{
+ struct { SANE_Byte cmd, lun, reserved[2], tr_len, ctrl; } scsi_cmd;
+ SANE_Status status;
+
+ memset(&scsi_cmd, 0, sizeof(scsi_cmd));
+ scsi_cmd.cmd = cmd;
+ scsi_cmd.ctrl = ctrl;
+
+ DBG(DSCSI, "SCSI: sending cmd6 0x%02x (ctrl=%d)\n", (int)cmd, (int)ctrl);
+ status = sanei_scsi_cmd(fd, &scsi_cmd, sizeof(scsi_cmd), 0, 0);
+ DBG(DSCSI, "SCSI: result=%s\n", sane_strstatus(status));
+
+ return status;
+}
+
+#define st400_test_ready(fd) st400_cmd6(fd, CMD_TEST_UNIT_READY, 0)
+#define st400_reserve(fd) st400_cmd6(fd, CMD_RESERVE, 0)
+#define st400_release(fd) st400_cmd6(fd, CMD_RELEASE, 0)
+#define st400_start_scan(fd) st400_cmd6(fd, CMD_START_STOP, 0)
+#define st400_light_on(fd) st400_cmd6(fd, CMD_MODE_SELECT, 0x80)
+#define st400_light_off(fd) st400_cmd6(fd, CMD_MODE_SELECT, 0)
+
+
+static SANE_Status
+st400_wait_ready( int fd )
+{
+#define SLEEPTIME 100000L /* 100ms */
+ long max_sleep = 60000000L; /* 60 seconds */
+ SANE_Status status;
+
+ DBG(DCODE, "st400_wait_ready(%d)\n", fd);
+
+ while(1) {
+ status = st400_test_ready(fd);
+ switch( status ) {
+ case SANE_STATUS_DEVICE_BUSY:
+ if( max_sleep > 0 ) {
+ usleep(SLEEPTIME); /* retry after 100ms */
+ max_sleep -= SLEEPTIME;
+ break;
+ }
+ /* else fall through */
+ default:
+ DBG(DERR, "st400_wait_ready: failed, error=%s\n", sane_strstatus(status));
+ /* fall through */
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+static SANE_Status
+st400_set_window( ST400_Device *dev )
+{
+ unsigned short xoff, yoff;
+ SANE_Byte th;
+
+ struct {
+ /* 10byte command */
+ SANE_Byte cmd, lun, reserved1[4], tr_len[3], ctrl;
+
+ /* 40byte window struct */
+ SANE_Byte reserved2[6], wd_len[2], winnr, reserved3;
+ SANE_Byte x_res[2], y_res[2]; /* resolution: 200, 300, 400 */
+ SANE_Byte x_ul[2], y_ul[2]; /* upper left corner */
+ SANE_Byte width[2], height[2];
+ SANE_Byte reserved4, threshold;
+ SANE_Byte reserved5, halftone; /* ht: 0 or 2 */
+ SANE_Byte bitsperpixel, reserved6[13]; /* bpp: 1 or 8 */
+ } scsi_cmd;
+ /* The PC/Amiga source uses reserved5 to indicate A4/A5 paper size
+ * (values 4 and 5), but a comment implies that this is only for the
+ * scanning program and the value is ignored by the scanner.
+ */
+ SANE_Status status;
+
+ memset(&scsi_cmd, 0, sizeof(scsi_cmd));
+ scsi_cmd.cmd = CMD_SET_WINDOW;
+ set24(scsi_cmd.tr_len, 40);
+ set16(scsi_cmd.wd_len, 32);
+
+ /* These offsets seem to be required to avoid damaging the scanner:
+ * If a scan with 0/0 as the top left corner is started, the scanner
+ * seems to try to move the carriage over the bottom end (not a
+ * pretty sound).
+ */
+ xoff = (11L * dev->val[OPT_RESOLUTION]) / 100;
+ yoff = 6;
+ th = (double)maxval(dev->model->bits) * SANE_UNFIX(dev->val[OPT_THRESHOLD]) / 100.0;
+
+ scsi_cmd.winnr = 1;
+ set16(scsi_cmd.x_res, (unsigned short)dev->val[OPT_RESOLUTION]);
+ set16(scsi_cmd.y_res, (unsigned short)dev->val[OPT_RESOLUTION]);
+ set16(scsi_cmd.x_ul, dev->x + xoff);
+ set16(scsi_cmd.y_ul, dev->wy + yoff);
+ set16(scsi_cmd.width, dev->w);
+ set16(scsi_cmd.height, dev->wh);
+ scsi_cmd.threshold = th;
+ scsi_cmd.halftone = (dev->val[OPT_DEPTH] == 1) ? 0 : 2;
+ scsi_cmd.bitsperpixel = dev->val[OPT_DEPTH];
+
+ DBG(DSCSI, "SCSI: sending SET_WINDOW (x=%hu y=%hu w=%hu h=%hu wy=%hu wh=%hu th=%d\n", dev->x, dev->y, dev->w, dev->h, dev->wy, dev->wh, (int)th);
+ status = sanei_scsi_cmd(dev->fd, &scsi_cmd, sizeof(scsi_cmd), 0, 0);
+ DBG(DSCSI, "SCSI: result=%s\n", sane_strstatus(status));
+
+ return status;
+}
+
+static SANE_Status
+st400_read10( int fd, SANE_Byte *buf, size_t *lenP )
+{
+ struct { SANE_Byte cmd, lun, res[4], tr_len[3], ctrl; } scsi_cmd;
+ SANE_Status status;
+
+ memset(&scsi_cmd, 0, sizeof(scsi_cmd));
+ scsi_cmd.cmd = CMD_READ10;
+ set24(scsi_cmd.tr_len, *lenP);
+
+ DBG(DSCSI, "SCSI: sending READ10 (%lu bytes)\n", (u_long)(*lenP));
+ status = sanei_scsi_cmd(fd, &scsi_cmd, sizeof(scsi_cmd), buf, lenP);
+ DBG(DSCSI, "SCSI: result=%s (%lu bytes)\n", sane_strstatus(status), (u_long)(*lenP));
+
+ return status;
+}
+
+static SANE_Status
+st400_fill_scanner_buffer( ST400_Device *dev )
+{
+ SANE_Status status;
+
+ DBG(DCODE, "st400_fill_scanner_buffer(%p)\n", (void *) dev);
+
+ if( dev->lines_to_read == 0 )
+ dev->status.eof = 1;
+ if( dev->status.eof )
+ return SANE_STATUS_EOF;
+
+ dev->wh = dev->model->bufsize / dev->params.bytes_per_line;
+ if( dev->wh > dev->lines_to_read )
+ dev->wh = dev->lines_to_read;
+ DBG(DVAR, "dev->wh = %hu\n", dev->wh);
+
+ status = st400_set_window(dev);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = st400_start_scan(dev->fd);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+
+ dev->wy += dev->wh;
+ dev->lines_to_read -= dev->wh;
+ dev->bytes_in_scanner = dev->wh * dev->params.bytes_per_line;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+st400_fill_backend_buffer( ST400_Device *dev )
+{
+ size_t r;
+ SANE_Status status;
+
+ DBG(DCODE, "st400_fill_backend_buffer(%p)\n", (void *) dev);
+
+ if( dev->bytes_in_scanner == 0 ) {
+ status = st400_fill_scanner_buffer(dev);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ r = min(dev->bufsize, dev->bytes_in_scanner);
+ status = st400_read10(dev->fd, dev->buffer, &r);
+ if( status == SANE_STATUS_GOOD ) {
+ dev->bufp = dev->buffer;
+ dev->bytes_in_buffer = r;
+ dev->bytes_in_scanner -= r;
+
+ if( r == 0 )
+ dev->status.eof = 1;
+ }
+
+ return status;
+}
+
+static SANE_Status
+st400_sense_handler( int fd, SANE_Byte *result, void *arg )
+{
+ /* ST400_Device *dev = arg; */
+ SANE_Status status;
+
+ fd = fd;
+ arg = arg; /* silence compilation warnings */
+
+ switch( result[0] & 0x0f ) {
+ case 0x0:
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x1:
+ DBG(DSENSE, "SCSI: sense RECOVERED_ERROR\n");
+ status = SANE_STATUS_GOOD; /* ?? */
+ break;
+ case 0x2:
+ DBG(DSENSE, "SCSI: sense NOT_READY\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x4:
+ DBG(DSENSE, "SCSI: sense HARDWARE_ERROR\n");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x5:
+ DBG(DSENSE, "SCSI: sense ILLEGAL_REQUEST\n");
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6:
+ DBG(DSENSE, "SCSI: sense UNIT_ATTENTION\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0xb:
+ DBG(DSENSE, "SCSI: sense ABORTED_COMMAND\n");
+ status = SANE_STATUS_CANCELLED; /* ?? */
+ break;
+ default:
+ DBG(DSENSE, "SCSI: sense unknown (%d)\n", result[0] & 0x0f);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ return status;
+}
+
+
+/*********************************************************************
+ * Sane initializing stuff
+ *********************************************************************/
+
+static SANE_Status
+st400_attach( const char *devname, ST400_Device **devP )
+{
+ ST400_Device *dev;
+ ST400_Model *model;
+ SANE_Status status;
+ int fd;
+
+ DBG(DCODE, "st400_attach(%s, %p)\n", devname, (void *) devP);
+ if( devP )
+ *devP = NULL;
+
+ for( dev = st400_devices; dev != NULL; dev = dev->next ) {
+ if( strcmp(dev->sane.name, devname) == 0 ) {
+ if( devP )
+ *devP = dev;
+ DBG(DCODE, "st400_attach: found device in list\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ dev = calloc(1, sizeof(*dev));
+ if( !dev )
+ return SANE_STATUS_NO_MEM;
+ DBG(DCODE, "st400_attach: new device struct at %p\n", (void *) dev);
+
+ status = sanei_scsi_open(devname, &fd, st400_sense_handler, dev);
+ if( status == SANE_STATUS_GOOD ) {
+ status = st400_inquiry(fd, &model);
+ if( status == SANE_STATUS_GOOD )
+ status = st400_test_ready(fd);
+ sanei_scsi_close(fd);
+ }
+ if( status != SANE_STATUS_GOOD ) {
+ free(dev);
+ return status;
+ }
+
+ /* initialize device structure */
+ dev->sane.name = strdup(devname);
+ if( !dev->sane.name ) {
+ free(dev);
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->sane.vendor = model->sane_vendor;
+ dev->sane.model = model->sane_model;
+ dev->sane.type = model->sane_type;
+ dev->status.open = 0;
+ dev->status.scanning = 0;
+ dev->status.eof = 0;
+ dev->fd = -1;
+ dev->buffer = NULL;
+ dev->model = model;
+
+ st400_init_options(dev);
+
+ DBG(DCODE, "st400_attach: everything ok, adding device to list\n");
+
+ dev->next = st400_devices;
+ st400_devices = dev;
+ ++st400_num_devices;
+ st400_status.array_valid = 0;
+
+ if( devP )
+ *devP = dev;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+st400_attach_one( const char *device )
+{
+ DBG(DCODE, "st400_attach_one(%s)\n", device);
+ return st400_attach(device, NULL);
+}
+
+static SANE_Status
+st400_config_get_arg(char **optP, unsigned long *argP, size_t linenum)
+{
+ int n;
+
+ linenum = linenum; /* silence compilation warnings */
+
+ if( sscanf(*optP, "%lu%n", argP, &n) == 1 ) {
+ *optP += n;
+ *optP = (char *)sanei_config_skip_whitespace(*optP);
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_INVAL;
+}
+
+
+static SANE_Status
+st400_config_get_single_arg(char *opt, unsigned long *argP, size_t linenum)
+{
+ int n;
+
+ if( sscanf(opt, "%lu%n", argP, &n) == 1 ) {
+ opt += n;
+ opt = (char *)sanei_config_skip_whitespace(opt);
+ if( *opt == '\0' )
+ return SANE_STATUS_GOOD;
+ else {
+ DBG(DERR, "extraneous arguments at line %lu: %s\n", (u_long)linenum, opt);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ DBG(DERR, "invalid option argument at line %lu: %s\n", (u_long)linenum, opt);
+ return SANE_STATUS_INVAL;
+}
+
+
+static SANE_Status
+st400_config_do_option(char *opt, size_t linenum)
+{
+ unsigned long arg;
+ SANE_Status status;
+ int i;
+
+ status = SANE_STATUS_GOOD;
+
+ opt = (char *)sanei_config_skip_whitespace(opt);
+ if( strncmp(opt, "maxread", 7) == 0 && isspace(opt[7]) ) {
+ opt += 8;
+ status = st400_config_get_single_arg(opt, &arg, linenum);
+ if( status == SANE_STATUS_GOOD ) {
+ if( arg == 0 )
+ st400_maxread = sanei_scsi_max_request_size;
+ else
+ st400_maxread = (size_t)arg;
+ }
+ }
+ else
+ if( strncmp(opt, "delay", 5) == 0 && isspace(opt[5]) ) {
+ opt += 6;
+ status = st400_config_get_single_arg(opt, &arg, linenum);
+ if( status == SANE_STATUS_GOOD )
+ st400_light_delay = (size_t)arg;
+ }
+ else
+ if( strncmp(opt, "scanner_bufsize", 15) == 0 && isspace(opt[15]) ) {
+ opt += 16;
+ status = st400_config_get_single_arg(opt, &arg, linenum);
+ if( status == SANE_STATUS_GOOD )
+ if( st400_devices )
+ st400_devices->model->bufsize = arg; /* FIXME: changes bufsize for all scanners of this model! */
+ }
+ else
+ if( strncmp(opt, "scanner_bits", 12) == 0 && isspace(opt[12]) ) {
+ opt += 13;
+ status = st400_config_get_single_arg(opt, &arg, linenum);
+ if( status == SANE_STATUS_GOOD )
+ if( st400_devices )
+ st400_devices->model->bits = arg; /* FIXME */
+ }
+ else
+ if( strncmp(opt, "scanner_maxread", 15) == 0 && isspace(opt[15]) ) {
+ opt += 16;
+ status = st400_config_get_single_arg(opt, &arg, linenum);
+ if( status == SANE_STATUS_GOOD )
+ if( st400_devices )
+ st400_devices->model->maxread = arg; /* FIXME */
+ }
+ else
+ if( strncmp(opt, "scanner_resolutions", 19) == 0 && isspace(opt[19]) ) {
+ opt += 20;
+ st400_devices->model->dpi_list = malloc(16 * sizeof(SANE_Int));
+ i = 0;
+ do {
+ status = st400_config_get_arg(&opt, &arg, linenum);
+ if( status == SANE_STATUS_GOOD ) {
+ ++i;
+ st400_devices->model->dpi_list[i] = (SANE_Int)arg;
+ }
+ } while( status == SANE_STATUS_GOOD && i < 15 );
+ st400_devices->model->dpi_list[0] = i;
+ DBG(DINFO, "%d entries for resolution\n", i);
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ if( strncmp(opt, "dump_inquiry", 12) == 0 ) {
+ st400_dump_data = 1;
+ }
+ if( st400_devices )
+ st400_reset_options(st400_devices);
+ return status;
+}
+
+SANE_Status
+sane_init( SANE_Int *versionP, SANE_Auth_Callback authorize )
+{
+ FILE *fp;
+ SANE_Status status;
+
+ DBG_INIT();
+ DBG(DCODE, "sane_init: version %s null, authorize %s null\n", (versionP) ? "!=" : "==", (authorize) ? "!=" : "==");
+
+ if( versionP != NULL )
+ *versionP = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ status = SANE_STATUS_GOOD;
+ if( (fp = sanei_config_open(ST400_CONFIG_FILE)) != NULL ) {
+ char line[PATH_MAX], *str;
+ size_t len, linenum;
+
+ linenum = 0;
+ DBG(DCODE, "sane_init: reading config file\n");
+ while( sanei_config_read(line, sizeof(line), fp) ) {
+ ++linenum;
+ str = line;
+ if( str[0] == '#' )
+ continue; /* ignore comments */
+ str = (char *)sanei_config_skip_whitespace(str);
+ len = strlen(str);
+ if( !len )
+ continue; /* ignore empty lines */
+ if( strncmp(str, "option", 6) == 0 && isspace(str[6]) ) {
+ DBG(DCODE, "sane_init: config line <%s>\n", line);
+ status = st400_config_do_option(str+7, linenum);
+ }
+ else {
+ DBG(DCODE, "sane_init: attaching device <%s>\n", line);
+ sanei_config_attach_matching_devices(line, st400_attach_one);
+ }
+ if( status != SANE_STATUS_GOOD )
+ break;
+ }
+ DBG(DCODE, "sane_init: closing config file\n");
+ fclose(fp);
+ }
+
+ if( status == SANE_STATUS_GOOD && st400_devices == NULL ) {
+ DBG(DCODE, "sane_init: attaching default device <%s>\n", ST400_DEFAULT_DEVICE);
+ sanei_config_attach_matching_devices(ST400_DEFAULT_DEVICE, st400_attach_one);
+ }
+
+ return status;
+}
+
+void
+sane_exit( void )
+{
+ ST400_Device *dev;
+
+ DBG(DCODE, "sane_exit()\n");
+
+ while( (dev = st400_devices) != NULL ) {
+ st400_devices = dev->next;
+
+ sane_close(dev);
+ free((char *)(dev->sane.name));
+ free(dev);
+ }
+ st400_num_devices = 0;
+ if( st400_device_array ) {
+ DBG(DCODE, "sane_exit: freeing device array\n");
+ free(st400_device_array);
+ st400_device_array = NULL;
+ st400_status.array_valid = 0;
+ }
+}
+
+SANE_Status
+sane_get_devices( const SANE_Device ***devarrayP, SANE_Bool local_only )
+{
+ ST400_Device *dev;
+ unsigned int i;
+
+ DBG(DCODE, "sane_get_devices(%p, %d)\n", (void *) devarrayP, (int)local_only);
+
+ if( !st400_status.array_valid ) {
+ if( st400_device_array ) {
+ DBG(DCODE, "sane_get_devices: freeing old device array\n");
+ free(st400_device_array);
+ }
+ st400_device_array = malloc((st400_num_devices + 1) * sizeof(*st400_device_array));
+ if( !st400_device_array )
+ return SANE_STATUS_NO_MEM;
+ DBG(DCODE, "sane_get_devices: new device array at %p\n", (void *) st400_device_array);
+
+ dev = st400_devices;
+ for( i = 0; i < st400_num_devices; i++ ) {
+ st400_device_array[i] = &dev->sane;
+ dev = dev->next;
+ }
+ st400_device_array[st400_num_devices] = NULL;
+ st400_status.array_valid = 1;
+ }
+ DBG(DCODE, "sane_get_devices: %u entries in device array\n", st400_num_devices);
+ if( devarrayP )
+ *devarrayP = st400_device_array;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_open( SANE_String_Const devicename, SANE_Handle *handleP )
+{
+ ST400_Device *dev;
+ SANE_Status status;
+
+ DBG(DCODE, "sane_open(%s, %p)\n", devicename, (void *) handleP);
+
+ *handleP = NULL;
+ if( devicename && devicename[0] ) {
+ status = st400_attach(devicename, &dev);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+ }
+ else
+ dev = st400_devices;
+
+ if( !dev )
+ return SANE_STATUS_INVAL;
+
+ if( dev->status.open )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ dev->status.open = 1;
+ st400_reset_options(dev);
+ *handleP = (SANE_Handle)dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close( SANE_Handle handle )
+{
+ ST400_Device *dev = handle;
+
+ DBG(DCODE, "sane_close(%p)\n", handle);
+
+ if( dev->status.open ) {
+ sane_cancel(dev);
+ dev->status.open = 0;
+ }
+}
+
+
+/*
+ * options
+ */
+static void
+st400_reset_options( ST400_Device *dev )
+{
+ DBG(DCODE, "st400_reset_options(%p)\n", (void *) dev);
+
+ dev->val[OPT_NUM_OPTS] = NUM_OPTIONS;
+ dev->val[OPT_RESOLUTION] = dev->opt[OPT_RESOLUTION].constraint.word_list[1];
+ dev->val[OPT_DEPTH] = dev->opt[OPT_DEPTH].constraint.word_list[1];
+ dev->val[OPT_THRESHOLD] = SANE_FIX(50.0);
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ dev->val[OPT_TL_X] = SANE_FIX(0.0);
+ dev->val[OPT_TL_Y] = SANE_FIX(0.0);
+ dev->val[OPT_BR_X] = SANE_FIX(0.0);
+ dev->val[OPT_BR_Y] = SANE_FIX(0.0);
+
+ if( dev->model->dpi_list )
+ dev->opt[OPT_RESOLUTION].constraint.word_list = dev->model->dpi_list;
+}
+
+static void
+st400_init_options( ST400_Device *dev )
+{
+ static const SANE_Int depth_list[] = { 2, 1, 8 };
+ static const SANE_Int dpi_list[] = { 3, 200, 300, 400 };
+ static const SANE_Range thres_range = {
+ SANE_FIX(0.0), SANE_FIX(100.0), SANE_FIX(0.0)
+ };
+ static const SANE_Range x_range = {
+ SANE_FIX(0.0), SANE_FIX(ST400_MAX_X * MM_PER_INCH), SANE_FIX(0.0)
+ };
+ static const SANE_Range y_range = {
+ SANE_FIX(0.0), SANE_FIX(ST400_MAX_Y * MM_PER_INCH), SANE_FIX(0.0)
+ };
+
+ DBG(DCODE, "st400_init_options(%p)\n", (void *)dev);
+
+ dev->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_NUM_OPTS].size = sizeof(SANE_Word);
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ dev->opt[OPT_MODE_GROUP].title= "Scan Mode";
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title= SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].size = sizeof(SANE_Word);
+ dev->opt[OPT_RESOLUTION].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+
+ dev->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ dev->opt[OPT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ dev->opt[OPT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ dev->opt[OPT_DEPTH].type = SANE_TYPE_INT;
+ dev->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
+ dev->opt[OPT_DEPTH].size = sizeof(SANE_Word);
+ dev->opt[OPT_DEPTH].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_DEPTH].constraint.word_list = depth_list;
+
+ dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ dev->opt[OPT_THRESHOLD].size = sizeof(SANE_Word);
+ dev->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_THRESHOLD].constraint.range = &thres_range;
+
+ dev->opt[OPT_GEOMETRY_GROUP].title= "Geometry";
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].size = sizeof(SANE_Word);
+ dev->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &x_range;
+
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].size = sizeof(SANE_Word);
+ dev->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &y_range;
+
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].size = sizeof(SANE_Word);
+ dev->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &x_range;
+
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].size = sizeof(SANE_Word);
+ dev->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &y_range;
+
+ st400_reset_options(dev);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor( SANE_Handle handle, SANE_Int optnum )
+{
+ ST400_Device *dev = handle;
+
+ DBG(DOPT, "sane_get_option_descriptor(%p, %d)\n", handle, (int)optnum);
+
+ if( dev->status.open && optnum >= 0 && optnum < NUM_OPTIONS )
+ return &dev->opt[optnum];
+
+ return NULL;
+}
+
+SANE_Status
+sane_control_option( SANE_Handle handle, SANE_Int optnum,
+ SANE_Action action, void *valP, SANE_Int *infoP)
+{
+ ST400_Device *dev = handle;
+ SANE_Status status;
+
+ DBG(DCODE, "sane_control_option(%p, %d, %d, %p, %p)\n", (void *) handle, (int)optnum, (int)action, valP, (void *) infoP);
+
+ if( infoP )
+ *infoP = 0;
+
+ if( !dev->status.open )
+ return SANE_STATUS_INVAL;
+ if( dev->status.scanning )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if( optnum < 0 || optnum >= NUM_OPTIONS )
+ return SANE_STATUS_INVAL;
+
+ switch( action ) {
+ case SANE_ACTION_GET_VALUE:
+
+ DBG(DOPT, "getting option %d (value=%d)\n", (int)optnum, (int)dev->val[optnum]);
+
+ switch( optnum ) {
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_DEPTH:
+ case OPT_THRESHOLD:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *)valP = dev->val[optnum];
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if( !SANE_OPTION_IS_SETTABLE(dev->opt[optnum].cap) )
+ return SANE_STATUS_INVAL;
+ status = sanei_constrain_value(&dev->opt[optnum], valP, infoP);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+
+ DBG(DOPT, "setting option %d to %d\n", (int)optnum, (int)*(SANE_Word *)valP);
+
+ switch( optnum ) {
+ case OPT_DEPTH:
+ if( *(SANE_Word *)valP != 1 )
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ else
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ if( infoP )
+ *infoP |= SANE_INFO_RELOAD_OPTIONS;
+ /* fall through */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if( infoP )
+ *infoP |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_THRESHOLD:
+ dev->val[optnum] = *(SANE_Word *)valP;
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+
+ DBG(DOPT, "automatic option setting\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters( SANE_Handle handle, SANE_Parameters *paramsP )
+{
+ ST400_Device *dev = handle;
+
+ DBG(DCODE, "sane_get_parameters(%p, %p)\n", handle, (void *) paramsP);
+
+ if( !dev->status.open )
+ return SANE_STATUS_INVAL;
+
+ if( !dev->status.scanning ) {
+ double width, height, dpi;
+
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.last_frame = SANE_TRUE;
+ dev->params.lines = 0;
+ dev->params.depth = dev->val[OPT_DEPTH];
+
+ width = SANE_UNFIX(dev->val[OPT_BR_X] - dev->val[OPT_TL_X]);
+ height = SANE_UNFIX(dev->val[OPT_BR_Y] - dev->val[OPT_TL_Y]);
+ dpi = dev->val[OPT_RESOLUTION];
+
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if( dpi > 0.0 && width > 0.0 && height > 0.0 ) {
+ double dots_per_mm = dpi / MM_PER_INCH;
+
+ dev->params.pixels_per_line = width * dots_per_mm + 0.5;
+ dev->params.lines = height * dots_per_mm + 0.5;
+
+ if( dev->params.depth == 1 ) {
+ /* Pad to an even multiple of 8. This way we can simply
+ * copy the bytes from the scanner to the SANE buffer
+ * (only need to invert them).
+ */
+ dev->params.pixels_per_line += 7;
+ dev->params.pixels_per_line &= ~7;
+
+ /*dev->params.bytes_per_line = (dev->params.pixels_per_line + 7)/8;*/
+ dev->params.bytes_per_line = dev->params.pixels_per_line/8;
+ }
+ else
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+
+ dev->x = SANE_UNFIX(dev->val[OPT_TL_X]) * dots_per_mm + 0.5;
+ dev->y = SANE_UNFIX(dev->val[OPT_TL_Y]) * dots_per_mm + 0.5;
+ dev->w = dev->params.pixels_per_line;
+ dev->h = dev->params.lines;
+
+ DBG(DVAR, "parameters: bpl=%d, x=%hu, y=%hu, w=%hu, h=%hu\n", (int)dev->params.bytes_per_line, dev->x, dev->y, dev->w, dev->h);
+ }
+ }
+
+ if( paramsP )
+ *paramsP = dev->params;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_start( SANE_Handle handle )
+{
+ ST400_Device *dev = handle;
+ SANE_Status status;
+
+ DBG(DCODE, "sane_start(%p)\n", handle);
+
+ if( !dev->status.open )
+ return SANE_STATUS_INVAL;
+ if( dev->status.scanning )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ status = sane_get_parameters(dev, NULL);
+ if( status != SANE_STATUS_GOOD )
+ return status;
+
+ if( !dev->buffer ) {
+ if( st400_maxread > 0 )
+ dev->bufsize = min(st400_maxread, (unsigned int) sanei_scsi_max_request_size);
+ else
+ if( dev->model->maxread > 0 )
+ dev->bufsize = min(dev->model->maxread, (unsigned int) sanei_scsi_max_request_size);
+ else
+ dev->bufsize = sanei_scsi_max_request_size;
+ DBG(DVAR, "allocating %lu bytes buffer\n", (u_long)dev->bufsize);
+ dev->buffer = malloc(dev->bufsize);
+ if( !dev->buffer )
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->bufp = dev->buffer;
+ dev->bytes_in_buffer = 0;
+
+ if( dev->fd < 0 ) {
+ status = sanei_scsi_open(dev->sane.name, &dev->fd, st400_sense_handler, dev);
+ if( status != SANE_STATUS_GOOD )
+ goto return_error;
+ }
+
+ dev->status.eof = 0;
+
+ status = st400_wait_ready(dev->fd);
+ if( status != SANE_STATUS_GOOD )
+ goto close_and_return;
+
+ status = st400_reserve(dev->fd);
+ if( status != SANE_STATUS_GOOD )
+ goto close_and_return;
+
+ if( st400_light_delay > 0 ) {
+ status = st400_light_on(dev->fd);
+ if( status != SANE_STATUS_GOOD )
+ goto release_and_return;
+ usleep(st400_light_delay * 100000); /* 1/10 seconds */
+ }
+
+ dev->wy = dev->y;
+ dev->lines_to_read = dev->h;
+ dev->bytes_in_scanner = 0;
+
+ status = st400_fill_scanner_buffer(dev);
+ if( status != SANE_STATUS_GOOD )
+ goto lightoff_and_return;
+
+ /* everything ok */
+ dev->status.scanning = 1;
+ return SANE_STATUS_GOOD;
+
+lightoff_and_return:
+ if( st400_light_delay )
+ st400_light_off(dev->fd);
+release_and_return:
+ st400_release(dev->fd);
+close_and_return:
+ sanei_scsi_close(dev->fd);
+return_error:
+ dev->fd = -1;
+ return status;
+}
+
+void
+sane_cancel( SANE_Handle handle )
+{
+ ST400_Device *dev = handle;
+
+ DBG(DCODE, "sane_cancel(%p)\n", handle);
+
+ if( dev->status.scanning ) {
+#if 0
+ st400_stop_scan(dev->fd);
+#endif
+ if( st400_light_delay )
+ st400_light_off(dev->fd);
+ st400_release(dev->fd);
+ sanei_scsi_close(dev->fd);
+ dev->status.scanning = 0;
+ dev->fd = -1;
+ }
+ if( dev->buffer ) {
+ free(dev->buffer);
+ dev->buffer = NULL;
+ }
+}
+
+
+SANE_Status
+sane_read( SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenP )
+{
+ ST400_Device *dev = handle;
+ SANE_Status status;
+ size_t r, i;
+ SANE_Byte val;
+
+ DBG(DCODE, "sane_read(%p, %p, %d, %p)\n", handle, buf, (int)maxlen, (void *) lenP);
+
+ *lenP = 0;
+ if( !dev->status.scanning )
+ return SANE_STATUS_INVAL;
+ if( dev->status.eof )
+ return SANE_STATUS_EOF;
+
+ status = SANE_STATUS_GOOD;
+ while( maxlen > 0 ) {
+ if( dev->bytes_in_buffer == 0 ) {
+ status = st400_fill_backend_buffer(dev);
+ if( status == SANE_STATUS_EOF )
+ return SANE_STATUS_GOOD;
+ if( status != SANE_STATUS_GOOD ) {
+ *lenP = 0;
+ return status;
+ }
+ }
+
+ r = min((SANE_Int) dev->bytes_in_buffer, maxlen);
+
+ if( dev->val[OPT_DEPTH] == 1 || dev->model->bits == 8 ) {
+ /* This is simple. We made sure the scanning are is aligned to
+ * 8 pixels (see sane_get_parameters()), so we can simply copy
+ * the stuff - only need to invert it.
+ */
+ for( i = 0; i < r; i++ )
+ *buf++ = ~(*dev->bufp++);
+ }
+ else {
+ SANE_Byte mv;
+
+ /* The scanner sends bytes with 6bit-values (0..63), where 0 means
+ * white. To convert to 8bit, we invert the values (so 0 means
+ * black) and then shift them two bits to the left and replicate
+ * the most- significant bits in the lowest two bits of the
+ * 8bit-value:
+ * bit-pattern x x 5 4 3 2 1 0 becomes 5 4 3 2 1 0 5 4
+ * This is more accurate than simply shifting the values two bits
+ * to the left (e.g. 6bit-white 00111111 gets converted to 8bit-
+ * white 11111111 instead of almost-white 11111100) and is still
+ * reasonably fast.
+ */
+ mv = (SANE_Byte)maxval(dev->model->bits);
+
+ /* Note: this works with any bit depth <= 8 */
+ for( i = 0; i < r; i++ ) {
+ val = mv - *dev->bufp++;
+ val <<= (8 - dev->model->bits);
+ val += (val >> dev->model->bits);
+ *buf++ = val;
+ }
+ }
+ maxlen -= r;
+ dev->bytes_in_buffer -= r;
+ *lenP += r;
+ }
+ return status;
+}
+
+
+/*********************************************************************
+ * Advanced functions (not supported)
+ *********************************************************************/
+
+SANE_Status
+sane_set_io_mode( SANE_Handle handle, SANE_Bool nonblock )
+{
+ DBG(DCODE, "sane_set_io_mode(%p, %d)\n", handle, (int)nonblock);
+
+ if( nonblock == SANE_TRUE )
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd( SANE_Handle handle, SANE_Int *fdP )
+{
+ DBG(DCODE, "sane_get_select_fd(%p, %p)\n", handle, (void *) fdP);
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+/* The End */
diff --git a/backend/st400.conf.in b/backend/st400.conf.in
new file mode 100644
index 0000000..5e29226
--- /dev/null
+++ b/backend/st400.conf.in
@@ -0,0 +1,53 @@
+# the ST400 is fixed to ID 3
+scsi SIEMENS "ST 400" Scanner * * 3 *
+scsi SIEMENS "ST 800" Scanner * * 3 *
+
+# The following options are for testing and bug-hunting. If your scanner
+# needs one of these options to function reliably, please let me know.
+
+# Maximum amount of data to read in a single SCSI command. If not set
+# (or set to 0), the backend will read as much data as allowed by the
+# scanner model or the OS. WARNING: Using this option overrides the
+# hardcoded # maxread limits for all scanner models! With more than
+# 65536 bytes, my ST400 locks up (itself, the SCSI bus, the sg driver,
+# and the machine). Use with caution.
+#option maxread 65536
+
+# Use this to switch the scanner lights on with a separate MODE SELECT call
+# and wait for some time before starting to scan (to allow the lights to go
+# to full intensity). The time is in 1/10 seconds (i.e. 20 means 2 seconds).
+# If not set, scanning starts immediately (works with my ST400).
+#option delay 20
+
+# The following are hacks that affect all scanners of the same model as the
+# last attached device. Used like this (assume ST800's had 8bit depth and
+# 4MB internal buffer):
+# scsi SIEMENS "ST 400" Scanner * * 3 *
+# option scanner_bufsize 2097152
+# option scanner_bits 6
+# scsi SIEMENS "ST 800" Scanner * * 3 *
+# option scanner_bufsize 4194304
+# option scanner_bits 8
+# Currently, the backend has entries for ST400, ST800 and "everything else".
+# To add more scanners, you have to add a line in the st400_models array.
+# Please note that these options are only for testing unknown devices with
+# this backend.
+
+# Internal scanner buffer:
+#option scanner_bufsize 2097152
+
+# Bit depth:
+#option scanner_bits 6
+
+# Maximum bytes to read in a single SCSI command (see also maxread above).
+#option scanner_maxread 65536
+
+# Supported resolutions (upto 15 different values). If you specify an
+# illegal value here, most likely the scanner will not report an error,
+# but only scan a small sub-area of the requested area (at least my ST400
+# does this).
+#option scanner_resolutions 200 300 400
+
+# This option causes the SCSI inquiry response to be written to
+# "$HOME/st400.dump" (as binary data). For debugging purposes.
+#option dump_inquiry
diff --git a/backend/st400.h b/backend/st400.h
new file mode 100644
index 0000000..246ba80
--- /dev/null
+++ b/backend/st400.h
@@ -0,0 +1,79 @@
+#ifndef st400_h
+#define st400_h
+
+#define ST400_CONFIG_FILE "st400.conf"
+#define ST400_DEFAULT_DEVICE "/dev/scanner"
+
+/* maximum scanning area in inch (guessed) */
+#define ST400_MAX_X 8.5
+#define ST400_MAX_Y 12.0
+
+enum ST400_Option {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_RESOLUTION,
+ OPT_DEPTH,
+ OPT_THRESHOLD,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+
+ NUM_OPTIONS /* must be last */
+};
+
+typedef struct {
+ size_t inq_voffset; /* offset in INQUIRY result */
+ char *inq_vendor;
+ size_t inq_moffset; /* offset in INQUIRY result */
+ char *inq_model;
+
+ size_t bits; /* 6 or 8 */
+ unsigned long bufsize; /* internal buffer of scanner */
+ unsigned long maxread; /* max bytes to read in a cmd (0 = no limit) */
+ SANE_Int *dpi_list; /* NULL for default list */
+
+ char *sane_vendor;
+ char *sane_model;
+ char *sane_type;
+} ST400_Model;
+
+
+typedef struct ST400_Device {
+ struct ST400_Device *next;
+
+ SANE_Device sane;
+ SANE_Parameters params;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ SANE_Word val[NUM_OPTIONS];
+
+ struct {
+ unsigned open :1;
+ unsigned scanning :1;
+ unsigned eof :1;
+ } status;
+
+ /* pixel values of entire scan window - for convenience */
+ unsigned short x, y, w, h;
+
+ int fd; /* SCSI filedescriptor */
+
+ /* backend buffer */
+ SANE_Byte *buffer;
+ size_t bufsize;
+ SANE_Byte *bufp; /* next byte to transfer */
+ size_t bytes_in_buffer;
+
+ ST400_Model *model;
+
+ /* scanner buffer */
+ unsigned short wy, wh; /* current subwindow */
+ unsigned long bytes_in_scanner;
+ unsigned short lines_to_read;
+} ST400_Device;
+
+#endif /* st400_h */
+/* The End */
diff --git a/backend/stubs.c b/backend/stubs.c
new file mode 100644
index 0000000..3fb5ce3
--- /dev/null
+++ b/backend/stubs.c
@@ -0,0 +1,85 @@
+#define STUBS
+
+#include "../include/sane/sanei_backend.h"
+
+/* Now define the wrappers (we could use aliases here, but go for
+ robustness for now...: */
+
+SANE_Status
+sane_init (SANE_Int *vc, SANE_Auth_Callback cb)
+{
+ return ENTRY(init) (vc, cb);
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***dl, SANE_Bool local)
+{
+ return ENTRY(get_devices) (dl, local);
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle *h)
+{
+ return ENTRY(open) (name, h);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int opt)
+{
+ return ENTRY(get_option_descriptor) (h, opt);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int opt, SANE_Action act,
+ void *val, SANE_Word *info)
+{
+ return ENTRY(control_option) (h, opt, act, val, info);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters *parms)
+{
+ return ENTRY(get_parameters) (h, parms);
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ return ENTRY(start) (h);
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
+{
+ return ENTRY(read) (h, buf, maxlen, lenp);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ return ENTRY(set_io_mode) (h, non_blocking);
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ return ENTRY(get_select_fd) (h, fdp);
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ ENTRY(cancel) (h);
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ ENTRY(close) (h);
+}
+
+void
+sane_exit (void)
+{
+ ENTRY(exit) ();
+}
diff --git a/backend/stv680.c b/backend/stv680.c
new file mode 100644
index 0000000..15a8472
--- /dev/null
+++ b/backend/stv680.c
@@ -0,0 +1,2180 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2004 - 2006 Gerard Klaver <gerard at gkall dot hobby dot nl>
+
+ The teco2 and gl646 backend (Frank Zago) are used as a template for
+ this backend.
+
+ For the usb commands and bayer decoding parts of the following
+ program are used:
+ The pencam2 program (GNU GPL license 2)
+
+ For the usb commands parts of the following programs are used:
+ The libgphoto2 (camlib stv0680) (GNU GPL license 2)
+ The stv680.c/.h kernel module (GNU GPL license 2)
+
+ For the stv680_add_text routine the add_text routine and font_6x11.h file
+ are taken from the webcam.c file, part of xawtv program,
+ (c) 1998-2002 Gerd Knorr (GNU GPL license 2).
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+ ------------------------------------------------------------------
+*/
+
+/* $Id$
+
+ stv680 vidcam driver Gerard Klaver
+*/
+
+/*SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend, attach vidcams
+ . - sane_get_devices() : query list of vidcam devices
+ . - sane_open() : open a particular vidcam device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get vidcam fd
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times;
+ . . after sane_read returns EOF)
+ . . go back to sane_start() if more frames desired
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened vidcam device
+ - sane_exit() : terminate use of backend
+*/
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 1 /* 2004/09/09 update 20-04-2006 */
+#define BACKEND_NAME stv680
+#define STV680_CONFIG_FILE "stv680.conf"
+
+/* --------------------- SANE INTERNATIONALISATION ------------------------ */
+
+/* must be first include */
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+/* for add-text routine */
+#include <time.h>
+#include "../include/font_6x11.h"
+/*-----------------------*/
+
+#include "stv680.h"
+
+#define TIMEOUT 1000
+
+/*--------------------------------------------------------------------------*/
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list[] = {
+ COLOR_RGB_STR,
+ COLOR_RGB_TEXT_STR,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ COLOR_RAW_STR,
+
+ NULL
+};
+
+/*-----------------------------------------minium, maximum, quantization----*/
+static const SANE_Range brightness_range = { -128, 128, 1 };
+
+static const SANE_Range red_level_range = { -32, 32, 1 };
+
+static const SANE_Range green_level_range = { -32, 32, 1 };
+
+static const SANE_Range blue_level_range = { -32, 32, 1 };
+
+/*--------------------------------------------------------------------------*/
+
+static const struct dpi_color_adjust stv680_dpi_color_adjust[] = {
+
+ /*dpi, y, x, color sequence R G or B */
+ {176, 144, 0, 1, 2}, /* QCIF selected by dev->CIF */
+ {352, 288, 0, 1, 2}, /* CIF ,, */
+ {160, 120, 0, 1, 2}, /* QSIF ,, dev->VGA */
+ {320, 240, 0, 1, 2}, /* QVGA (SIF) ,, */
+ {640, 480, 0, 1, 2}, /* VGA ,, */
+ /* must be the last entry */
+ {0, 0, 0, 0, 0}
+};
+
+static const struct vidcam_hardware vidcams[] = {
+
+ {0x0553, 0x0202, USB_CLASS_VENDOR_SPEC,
+ "AIPTEK", "PENCAM STV0680",
+ stv680_dpi_color_adjust},
+
+ {0x04c8, 0x0722, USB_CLASS_VENDOR_SPEC,
+ "Konica", "e-mini",
+ stv680_dpi_color_adjust},
+
+ {0x1183, 0x0001, USB_CLASS_VENDOR_SPEC,
+ "DigitalDream", "l'espion XS",
+ stv680_dpi_color_adjust},
+
+ {0x041e, 0x4007, USB_CLASS_VENDOR_SPEC,
+ "Creative", "WebCam Go mini",
+ stv680_dpi_color_adjust}
+};
+
+/* List of vidcams attached. */
+static Stv680_Vidcam *first_dev = NULL;
+static int num_devices = 0;
+/* used by sane_get_devices */
+static const SANE_Device **devlist = NULL;
+
+/*----------------------------------------------------------- */
+
+/* Local functions. */
+
+/* Display a buffer in the log. Display by lines of 16 bytes. */
+static void
+hexdump (int level, const char *comment, unsigned char *buf, const int length)
+{
+ int i;
+ char line[128];
+ char *ptr;
+ char asc_buf[17];
+ char *asc_ptr;
+
+ DBG (level, " %s\n", comment);
+
+ i = 0;
+ goto start;
+
+ do
+ {
+ if (i < length)
+ {
+ ptr += sprintf (ptr, " %2.2x", *buf);
+
+ if (*buf >= 32 && *buf <= 127)
+ {
+ asc_ptr += sprintf (asc_ptr, "%c", *buf);
+ }
+ else
+ {
+ asc_ptr += sprintf (asc_ptr, ".");
+ }
+ }
+ else
+ {
+ /* After the length; do nothing. */
+ ptr += sprintf (ptr, " ");
+ }
+
+ i++;
+ buf++;
+
+ if ((i % 16) == 0)
+ {
+ /* It's a new line */
+ DBG (level, " %s %s\n", line, asc_buf);
+
+ start:
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+
+ ptr += sprintf (ptr, " %3.3d:", i);
+ }
+ }
+ while (i < ((length + 15) & ~15));
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+ return max_size;
+}
+
+/* Initialize a vidcam entry. Return an allocated vidcam with some
+ * */
+static Stv680_Vidcam *
+stv680_init (void)
+{
+ Stv680_Vidcam *dev;
+
+ DBG (DBG_proc, "stv680_init: enter\n");
+
+ /* Allocate a new vidcam entry. */
+ dev = malloc (sizeof (Stv680_Vidcam));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+ memset (dev, 0, sizeof (Stv680_Vidcam));
+
+/* Allocate the windoww buffer*/
+ dev->windoww_size = 0x20;
+ dev->windoww = malloc (dev->windoww_size);
+ if (dev->windoww == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+/* Allocate the windowr buffer*/
+ dev->windowr_size = 0x20;
+ dev->windowr = malloc (dev->windowr_size);
+ if (dev->windowr == NULL)
+ {
+ free (dev->windoww);
+ free (dev);
+ return NULL;
+ }
+
+ dev->fd = -1;
+
+ DBG (DBG_proc, "stv680_init: exit\n");
+
+ return (dev);
+}
+
+static SANE_Status
+stv680_init_2 (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "stv680_init_2: enter\n");
+
+ /* Allocate the buffer used to transfer the USB data */
+ /* Check for max. format image size so buffer size can
+ * be adjusted, format from camera is bayer 422 */
+ if (dev->CIF)
+ dev->buffer_size = 356 * 292;
+ if (dev->VGA)
+ dev->buffer_size = 644 * 484;
+ DBG (DBG_proc, "stv680_init_2: dev->bufffer = 0x%lx\n", (unsigned long) (size_t) dev->buffer_size);
+
+ dev->buffer = malloc (dev->buffer_size);
+
+ if (dev->buffer == NULL)
+ {
+ free (dev->windowr);
+ free (dev->windoww);
+ free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Allocate the output buffer used for bayer conversion */
+ dev->output_size = dev->buffer_size * 3;
+
+ dev->output = malloc (dev->output_size);
+ if (dev->output == NULL)
+ {
+ free (dev->windowr);
+ free (dev->windoww);
+ free (dev->buffer);
+ free (dev);
+
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->image_size = dev->buffer_size;
+
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ free (dev->windowr);
+ free (dev->windoww);
+ free (dev->buffer);
+ free (dev->output);
+ free (dev);
+
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_proc, "stv680_init_2: exit\n");
+ status = SANE_STATUS_GOOD;
+ return status;
+}
+
+/* Closes an open vidcams. */
+static void
+stv680_close (Stv680_Vidcam * dev)
+{
+ DBG (DBG_proc, "stv680_close: enter \n");
+
+ if (dev->fd != -1)
+ {
+
+ DBG (DBG_proc, "stv680_close: fd !=-1 \n");
+ sanei_usb_close (dev->fd);
+ dev->fd = -1;
+ }
+
+ DBG (DBG_proc, "stv680_close: exit\n");
+}
+
+/* Frees the memory used by a vidcam. */
+static void
+stv680_free (Stv680_Vidcam * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "stv680_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ stv680_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->output)
+ {
+ free (dev->output);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ if (dev->windoww)
+ {
+ free (dev->windoww);
+ }
+ if (dev->windowr)
+ {
+ free (dev->windowr);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+ if (dev->resolutions_list)
+ free (dev->resolutions_list);
+
+ free (dev);
+
+ DBG (DBG_proc, "stv680_free: exit\n");
+}
+
+static SANE_Status
+stv680_set_config (Stv680_Vidcam * dev, int configuration, int interface,
+ int alternate)
+{
+ SANE_Status status;
+ DBG (DBG_proc, "stv680_set_config: open\n");
+/* seems a problem on some systems (Debian amd64 unstable 19042006)
+ * not calling usb_set_configuration seems to help reason ?
+ status = sanei_usb_set_configuration (dev->fd, configuration);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_set_config: STV680 FAILED to set configuration %d\n",
+ configuration);
+ return status;
+ }
+*/
+ status = sanei_usb_claim_interface (dev->fd, interface);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_set_config: STV0680 FAILED to claim interface\n");
+ return status;
+ }
+
+ status = sanei_usb_set_altinterface (dev->fd, alternate);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_set_config: STV0680 FAILED to set alternate interface %d\n",
+ alternate);
+ return status;
+ }
+ DBG (DBG_proc,
+ "stv680_set_config: configuration=%d, interface=%d, alternate=%d\n",
+ configuration, interface, alternate);
+
+ DBG (DBG_proc, "stv680_set_config: exit\n");
+ return status;
+}
+
+/* Reset vidcam */
+static SANE_Status
+stv680_reset_vidcam (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ size_t sizew; /* significant size of window */
+ size_t sizer;
+
+ DBG (DBG_proc, "stv680_reset_vidcam: enter\n");
+
+ sizew = dev->windoww_size;
+ sizer = dev->windowr_size;
+
+ memset (dev->windoww, 0, sizew);
+ memset (dev->windowr, 0, sizer);
+
+ sizew = 0x00; /* was 0 ? */
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x0a, 0x0000, 0, sizew,
+ dev->windoww);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ DBG (DBG_proc, "stv680_reset_vidcam: CMDID_STOP_VIDEO end\n");
+
+ /* this is a high priority command; it stops all lower order commands */
+
+ sizew = 0x00; /* was 0 */
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x04, 0x0000, 0, sizew,
+ dev->windoww);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ DBG (DBG_proc, "stv680_reset_vidcam: CMDID_CANCEL_TRANSACTION end\n");
+ sizer = 0x02;
+ DBG (DBG_proc, "stv680_reset_vidcam: CMDID_GET_LAST_ERROR begin\n");
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x80, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* Get Last Error; 2 = busy */
+ DBG (DBG_proc,
+ "stv680_reset_vidcam: last error: %i, command = 0x%x\n",
+ dev->windowr[0], dev->windowr[1]);
+ return status;
+ }
+ else
+ {
+ DBG (DBG_proc, "stv680_reset_vidcam: Camera reset to idle mode.\n");
+ }
+ hexdump (DBG_info2, "stv680_reset_vidcam: CMDID_GET_LAST_ERROR",
+ dev->windowr, sizer);
+
+ /* configuration = 1, interface = 0, alternate = 0 */
+ /*
+ status = stv680_set_config (dev, 1, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_reset_vidcam: STV680 FAILED to set configure\n");
+ return status;
+ }
+ */
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_proc, "stv680_reset_vidcam: exit\n");
+
+ return status;
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+stv680_identify_vidcam (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ SANE_Word vendor;
+ SANE_Word product;
+ int i;
+ size_t sizer;
+
+ DBG (DBG_info, "stv680_identify_vidcam: open\n");
+
+ status = sanei_usb_get_vendor_product (dev->fd, &vendor, &product);
+
+ /* Loop through our list to make sure this scanner is supported. */
+ for (i = 0; i < NELEMS (vidcams); i++)
+ {
+ if (vidcams[i].vendor == vendor && vidcams[i].product == product)
+ {
+
+ DBG (DBG_info, "stv680_identify_vidcam: vidcam %x:%x is in list\n",
+ vendor, product);
+
+ dev->hw = &(vidcams[i]);
+
+ sizer = dev->windowr_size;
+ memset (dev->windowr, 0, sizer);
+
+ /* configuration = 1, interface = 0, alternate = 0 */
+ /*
+ status = stv680_set_config (dev, 1, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_vidcam_init: STV680 FAILED to set configure\n");
+ return status;
+ }
+ */
+ sizer = 0x02;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x88, 0x5678, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_identify_vidcam: this is not a STV680 (idVendor = %d, bProduct = %d) writing register failed with %s\n",
+ vendor, product, sane_strstatus (status));
+ return SANE_FALSE;
+ }
+ if ((dev->windowr[0] != 0x56) || (dev->windowr[1] != 0x78))
+ {
+ DBG (DBG_proc,
+ "STV(e): camera ping failed!!, checkvalue !=0x5678\n");
+ sizer = 0x02;
+ hexdump (DBG_info2, "urb12 window", dev->windowr, sizer);
+ return SANE_FALSE;
+ }
+ sizer = 0x02;
+ hexdump (DBG_info2, "urb12 ping data", dev->windowr, sizer);
+
+ sizer = 0x10;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x85, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+
+ hexdump (DBG_info2, "urbxx CMDID_GET_CAMERA_INFO", dev->windowr,
+ sizer);
+
+ dev->SupportedModes = dev->windowr[7];
+ i = dev->SupportedModes;
+ dev->QSIF = 0;
+ dev->CIF = 0;
+ dev->QCIF = 0;
+ dev->VGA = 0;
+ dev->QVGA = 0;
+ if (i & 1)
+ dev->CIF = 1;
+ if (i & 2)
+ dev->VGA = 1;
+ if (i & 8)
+ dev->QVGA = 1;
+ if (i & 4)
+ dev->QCIF = 1;
+ dev->QSIF = dev->QVGA; /* for software subsample */
+ if (dev->SupportedModes == 0)
+ {
+ DBG (DBG_proc,
+ "STV(e): There are NO supported STV680 modes!!\n");
+ i = -1;
+ return SANE_FALSE;
+ }
+ else
+ {
+ if (dev->VGA)
+ DBG (DBG_proc, "STV(i): VGA is supported\n");
+ if (dev->CIF)
+ DBG (DBG_proc, "STV(i): CIF is supported\n");
+ if (dev->QVGA)
+ DBG (DBG_proc, "STV(i): QVGA is supported\n");
+ if (dev->QCIF)
+ DBG (DBG_proc, "STV(i): QCIF is supported\n");
+ }
+
+ /* FW rev, ASIC rev, sensor ID */
+ DBG (DBG_proc, "STV(i): Firmware rev is %i.%i\n", dev->windowr[0],
+ dev->windowr[1]);
+ DBG (DBG_proc, "STV(i): ASIC rev is %i.%i\n", dev->windowr[2],
+ dev->windowr[3]);
+ DBG (DBG_proc, "STV(i): Sensor ID is %i.%i\n", (dev->windowr[4]),
+ (dev->windowr[5]));
+ /* Hardware config */
+ dev->HardwareConfig = dev->windowr[6];
+ i = dev->HardwareConfig;
+ /* Comms link, Flicker freq, Mem size */
+ if (i & 1)
+ DBG (DBG_proc, "STV(i): Comms link is serial\n");
+ else
+ DBG (DBG_proc, "STV(i): Comms link is USB\n");
+ if (i & 2)
+ DBG (DBG_proc, "STV(i): Flicker freq = 60 Hz\n");
+ else
+ DBG (DBG_proc, "STV(i): Flicker freq = 50 Hz\n");
+ if (i & 4)
+ DBG (DBG_proc, "STV(i): Mem size = 16Mbit\n");
+ else
+ DBG (DBG_proc, "STV(i): Mem size = 64Mbit\n");
+ if (i & 8)
+ DBG (DBG_proc, "STV(i): Thumbnails supported\n");
+ else
+ DBG (DBG_proc, "STV(i): Thumbnails N/A\n");
+ if (i & 16)
+ DBG (DBG_proc, "STV(i): Video supported\n");
+ else
+ DBG (DBG_proc, "STV(i): Video N/A\n");
+ if (i & 32)
+ DBG (DBG_proc, "STV(i): Startup Complete\n");
+ else
+ DBG (DBG_proc, "STV(i): Startup Not Complete\n");
+ if (i & 64)
+ DBG (DBG_proc, "STV(i): Monochrome\n");
+ else
+ DBG (DBG_proc, "STV(i): Color\n");
+ if (i & 128)
+ DBG (DBG_proc, "STV(i): Mem fitted\n");
+ else
+ DBG (DBG_proc, "STV(i): Mem not fitted\n");
+
+ DBG (DBG_proc, "urb 25 CMDID_GET_IMAGE_INFO\n");
+ sizer = 0x10;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x86, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return SANE_FALSE;
+ }
+ hexdump (DBG_info2, "urb25 CMDID_GET_IMAGE_INFO", dev->windowr,
+ sizer);
+ DBG (DBG_proc, "STV(i): Current image index %d\n",
+ ((dev->windowr[0] << 8) + (dev->windowr[1])));
+ DBG (DBG_proc,
+ "If images are stored in camera, they will be lost when captering images is started!!!!!\n");
+ DBG (DBG_proc, "STV(i): Max images %d\n",
+ ((dev->windowr[2] << 8) + (dev->windowr[3])));
+ DBG (DBG_proc, "STV(i): Image width (pix) %d\n",
+ ((dev->windowr[4] << 8) + (dev->windowr[5])));
+ DBG (DBG_proc, "STV(i): Image height (pix) %d\n",
+ ((dev->windowr[6] << 8) + (dev->windowr[7])));
+ DBG (DBG_proc, "STV(i): Image size camera %d bytes\n",
+ ((dev->windowr[8] << 24) + (dev->windowr[9] << 16) +
+ (dev->windowr[10] << 8) + (dev->windowr[11])));
+
+ /* configuration = 1, interface = 0, alternate = 1 */
+ status = stv680_set_config (dev, 1, 0, 1);
+ /*
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_vidcam_init: STV680 FAILED to set configure\n");
+ return status;
+ }
+
+ DBG (DBG_info, "stv680_identify_vidcam: exit vidcam supported\n");
+ */
+ return SANE_TRUE;
+ }
+ }
+ DBG (DBG_error, "stv680_identify_vidcam: exit this is not a STV680 exit\n");
+ return SANE_FALSE;
+}
+
+static SANE_Status
+stv680_vidcam_init (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ SANE_Byte i = 0;
+ SANE_Byte val = 0;
+ size_t sizer;
+ size_t sizew;
+ DBG (DBG_proc, "stv680_vidcam_init: open\n");
+
+ sizew = dev->windoww_size;
+ sizer = dev->windowr_size;
+
+ memset (dev->windoww, 0, sizew);
+ memset (dev->windowr, 0, sizer);
+
+ DBG (DBG_proc, "stv680_vidcam_init: urb 13 CMDID_GET_USER_INFO\n");
+ dev->video_status = 0x04; /* dummy value busy */
+
+ while (dev->video_status == 0x04)
+ {
+ sizer = 0x08;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x8d, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ hexdump (DBG_info2, "stv680_vidcam_init: urb13 CMDID_GET_USER_INFO",
+ dev->windowr, sizer);
+
+ dev->video_status = dev->windowr[1];
+ if (dev->video_status == 0x02)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: status = video\n");
+ }
+ else if ((dev->video_status == 0x01) || (dev->video_status == 0x08))
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: status=%d\n",
+ dev->video_status);
+ }
+ else if (dev->video_status != 0x04)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: status = busy\n");
+ /* CMDID_CANCEL_TRANSACTION */
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x04, 0x0000, 0, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info,
+ "stv680_vidcam_init: urb13 CMDID_CANCEL_TRANSACTION NOK\n");
+ return status;
+ }
+ }
+ }
+
+ if (dev->video_status == 0x01 || dev->video_status == 0x08)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: urb 21 CMDID_GET_COLDATA_SIZE\n");
+ sizer = 0x02;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x8a, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ val = dev->windowr[0];
+ hexdump (DBG_info2, "stv680_vidcam_init: urb21 CMDID_GET_COLDATA_SIZE",
+ dev->windowr, sizer);
+ if (dev->windowr[0] &= 0x00)
+ DBG (DBG_info,
+ "stv680_vidcam_init: no camera defaults, must be downloaded?\n");
+
+ sizer = 0x10;
+ for (i = 0; i < val; i += 0x10)
+ {
+ DBG (DBG_proc,
+ "stv680_vidcam_init: urb 22, 23, 24 CMDID_GET_COLDATA i=0x%x, val=0x%x\n",
+ i, val);
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x8b, (i << 8), 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ hexdump (DBG_info2,
+ "stv680_vidcam_init: urb22, 23, 24 CMDID_GET_COLDATA",
+ dev->windowr, sizer);
+ }
+
+ sizer = 0x12;
+ status =
+ sanei_usb_control_msg (dev->fd, 0x80, 0x06, 0x0100, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ /* if (!(i > 0) && (dev->windowr[8] == 0x53) && (dev->windowr[9] == 0x05))
+ {
+ DBG (DBG_proc, "STV(e): Could not get descriptor 0100.");
+ *//* return status; *//*
+ } */
+ sizer = 0x12;
+ hexdump (DBG_info2, "stv680_vidcam_init: CMDID_SET_IMAGE_INDEX",
+ dev->windowr, sizer);
+
+ /* configuration = 1, interface = 0, alternate = 1 */
+ status = stv680_set_config (dev, 1, 0, 1);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_vidcam_init: STV680 FAILED to set configure\n");
+ return status;
+ }
+ }
+ /* Switch to Video mode: 0x0000 = CIF (352x288), 0x0200 = QCIF (176x144) */
+ /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0300 = QVGA (320x240) */
+ sizew = 0x0;
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x09, dev->video_mode, 0, sizew,
+ dev->windoww);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: video_mode = 0x%x\n",
+ dev->video_mode);
+ return status;
+ }
+ DBG (DBG_proc,
+ "stv680_vidcam_init: CMDID_START_VIDEO: video_mode=0x%x\n",
+ dev->video_mode);
+
+ if (dev->x_resolution == 176)
+ {
+ usleep (1000); /* delay time needed */
+ }
+ status = SANE_STATUS_GOOD;
+
+ if (status)
+ {
+ DBG (DBG_error, "stv680_vidcam_init failed : %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_proc, "stv680_vidcam_init: exit\n");
+ return status;
+}
+
+/* Attach a vidcam to this backend. */
+static SANE_Status
+attach_vidcam (SANE_String_Const devicename, Stv680_Vidcam ** devp)
+{
+ Stv680_Vidcam *dev;
+ int fd;
+ SANE_Status status;
+
+ DBG (DBG_proc, "attach_vidcam: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new vidcam entry. */
+ dev = stv680_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "stv680_init ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_vidcam: opening USB device %s\n", devicename);
+
+ if (sanei_usb_open (devicename, &fd) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_vidcam: open failed\n");
+ stv680_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->fd = fd;
+
+ /* Now, check that it is a vidcam we support. */
+
+ if (stv680_identify_vidcam (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error, "ERROR: attach_vidcam: vidcam-identification failed\n");
+ stv680_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Allocate a buffer memory. */
+ status = stv680_init_2 (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "stv680_initi_2, ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ stv680_close (dev);
+
+ DBG (DBG_info, "attach_vidcam: opening USB device %s\n", devicename);
+
+ /* Build list of vidcam supported resolutions. */
+ DBG (DBG_proc, "attach_vidcam: build resolution list\n");
+
+ if (dev->hw->color_adjust[0].resolution_x != 0)
+ {
+ int num_entries;
+ int i;
+ num_entries = 0;
+
+ while (dev->hw->color_adjust[num_entries].resolution_x != 0)
+ num_entries++;
+
+ dev->resolutions_list = malloc (sizeof (SANE_Word) * (num_entries + 1));
+
+ if (dev->resolutions_list == NULL)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_vidcam: vidcam resolution list failed\n");
+ stv680_free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+ /* for CIF or VGA sensor different resolutions */
+ if (dev->CIF)
+ num_entries = 2;
+ if (dev->VGA)
+ num_entries = 3;
+ dev->resolutions_list[0] = num_entries;
+ DBG (DBG_proc, "attach_vidcam: make color resolution table \n");
+ for (i = 0; i < num_entries; i++)
+ {
+ dev->resolutions_list[i + 1 + dev->VGA + dev->QVGA] =
+ dev->hw->color_adjust[i].resolution_x;
+ }
+ }
+ else
+ {
+ dev->resolutions_list = NULL;
+ }
+
+ /* Set the default options for that vidcam. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->hw->vendor_name;
+ dev->sane.model = dev->hw->product_name;
+ dev->sane.type = SANE_I18N ("webcam");
+
+ /* Link the vidcam with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_vidcam: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (DBG_proc, "attach_one: open \n");
+ attach_vidcam (dev, NULL);
+ DBG (DBG_proc, "attach_one: exit \n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that vidcam. */
+static void
+stv680_init_options (Stv680_Vidcam * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "stv680_init_options: open\n");
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ DBG (DBG_proc,
+ "stv680_init_options: done loop opt_num_options=%d, i=%d \n",
+ OPT_NUM_OPTIONS, i);
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Vidcam supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[dev->CIF + dev->QCIF + dev->VGA + dev->QVGA + dev->QSIF]; /* value will be 2 or 3 */
+
+ /* brightness */
+ dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ dev->val[OPT_BRIGHTNESS].w = 0; /* to get middle value */
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* red level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_R].name = SANE_NAME_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].title = SANE_TITLE_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].desc = SANE_DESC_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_R].constraint.range = &red_level_range;
+ dev->val[OPT_WHITE_LEVEL_R].w = 00; /* to get middle value */
+
+ /* green level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_G].name = SANE_NAME_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].title = SANE_TITLE_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].desc = SANE_DESC_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_G].constraint.range = &green_level_range;
+ dev->val[OPT_WHITE_LEVEL_G].w = 00; /* to get middle value */
+
+ /* blue level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_B].name = SANE_NAME_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].title = SANE_TITLE_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].desc = SANE_DESC_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_B].constraint.range = &blue_level_range;
+ dev->val[OPT_WHITE_LEVEL_B].w = 00; /* to get middle value */
+
+ DBG (DBG_proc, "stv680_init_options: after blue level\n");
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scan_mode_list[0], NULL);
+ DBG (DBG_proc, "stv680_init_options: exit\n");
+}
+
+/* Read the image from the vidcam and fill the temporary buffer with it. */
+static SANE_Status
+stv680_fill_image (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ size_t size;
+ size_t bulk_size_read;
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ DBG (DBG_proc, "stv680_fill_image: enter\n");
+
+ DBG (DBG_proc, "stv680_fill_image: real dev bytes left=0x%lx \n",
+ (unsigned long) (size_t) dev->real_bytes_left);
+ bulk_size_read = dev->real_bytes_left;
+
+ while (dev->real_bytes_left)
+ {
+ /* Try to read the maximum number of bytes. */
+ DBG (DBG_proc,
+ "stv680_fill_image: real dev bytes left, while loop=0x%lx \n",
+ (unsigned long) (size_t) dev->real_bytes_left);
+
+ size = dev->real_bytes_left;
+ if (size < bulk_size_read)
+ {
+ size = bulk_size_read; /* it seems size can not be smaller then read by bulk */
+ }
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer. Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ /* Do the transfer */
+
+ DBG (DBG_proc,
+ "stv680_fill_image: dev->real_bytes_left: 0x%lx size: 0x%lx\n",
+ (unsigned long) (size_t) dev->real_bytes_left, (unsigned long) (size_t) size);
+ usleep (3000);
+ /* urb 44 first read bulk */
+
+ status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+
+ DBG (DBG_info,
+ "stv680_fill_image: size (read) = 0x%lx bytes (bpl=0x%lx)\n",
+ (unsigned long) (size_t) size, (unsigned long) (size_t) dev->params.bytes_per_line);
+
+ memcpy (dev->image + dev->image_end, dev->buffer, size);
+
+ dev->image_end += size;
+ bulk_size_read = size;
+ if (dev->real_bytes_left > size)
+ dev->real_bytes_left -= size;
+ else if (dev->real_bytes_left <= size) /* last loop */
+ dev->real_bytes_left = 0;
+ DBG (DBG_info, "stv680_fill_image: real bytes left = 0x%lx\n",
+ (unsigned long) (size_t) dev->real_bytes_left);
+ }
+ /* i = stv_sndctrl (0, dev, 0x80, 0, &window, 0x02); *//* Get Last Error */
+/* DBG (DBG_proc, "STV(i): last error: %i, command = 0x%x", window[0], window[1]);
+ return -1; */
+/*
+ }
+ return 0; */
+
+ DBG (DBG_proc, "stv680_fill_image: exit\n");
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+#define MSG_MAXLEN 45
+#define CHAR_HEIGHT 11
+#define CHAR_WIDTH 6
+#define CHAR_START 4
+
+static SANE_Status
+stv680_add_text (SANE_Byte * image, int width, int height, char *txt)
+{
+ SANE_Status status;
+ time_t t;
+ struct tm *tm;
+ char line[MSG_MAXLEN + 1];
+ SANE_Byte *ptr;
+ int i, x, y, f, len;
+ char fmtstring[25] = " %Y-%m-%d %H:%M:%S";
+ char fmttxt[46];
+
+ DBG (DBG_proc, "stv680_add_text: enter\n");
+ time (&t);
+ tm = localtime (&t);
+ if (strlen (txt) > (MSG_MAXLEN - 23))
+ strncpy (fmttxt, txt, (MSG_MAXLEN - 23));
+ else
+ strcpy (fmttxt, txt);
+ strcat (fmttxt, fmtstring);
+
+ len = strftime (line, MSG_MAXLEN, fmttxt, tm);
+
+ for (y = 0; y < CHAR_HEIGHT; y++)
+ {
+ ptr = image + 3 * width * (height - CHAR_HEIGHT - 2 + y) + 12;
+
+ for (x = 0; x < len; x++)
+ {
+ f = fontdata[line[x] * CHAR_HEIGHT + y];
+ for (i = CHAR_WIDTH - 1; i >= 0; i--)
+ {
+ if (f & (CHAR_START << i))
+ {
+ ptr[0] = 255;
+ ptr[1] = 255;
+ ptr[2] = 255;
+ }
+ ptr += 3;
+ } /* for i */
+ } /* for x */
+ } /* for y */
+
+ DBG (DBG_proc, "stv680_add_text: exit vw=%d, vh=%d\n", width, height);
+ status = (SANE_STATUS_GOOD);
+ return status;
+
+}
+
+/* ************************** Video Decoding ********************* */
+
+static SANE_Status
+stv680_bayer_unshuffle (Stv680_Vidcam * dev, SANE_Byte * buf, size_t * size)
+{
+ SANE_Status status;
+ int x, y;
+ int i = 0;
+ int RED, GREEN, BLUE;
+ int w = dev->cwidth;
+ int vw = dev->x_resolution;
+ int vh = dev->y_resolution;
+ SANE_Byte p = 0;
+ int colour = 0, bayer = 0;
+ int bright_red;
+ int bright_green;
+ int bright_blue;
+ int count;
+
+ RED = dev->red_s;
+ GREEN = dev->green_s;
+ BLUE = dev->blue_s;
+
+ DBG (DBG_proc, "stv680_bayer_unshuffle: enter\n");
+
+#define AD(x, y, w) (((y)*(w)+(x))*3)
+
+ DBG (DBG_proc,
+ "stv680_bayer_unshuffle: color read RED=%d, GREEN=%d, BLUE=%d\n",
+ RED, GREEN, BLUE);
+
+ DBG (DBG_proc, "stv680_bayer_unshuffle: w=%d, vw=%d, vh=%d, len=0x%lx\n",
+ w, vw, vh, (unsigned long) (size_t) size);
+
+ for (y = 0; y < vh; y++)
+ {
+ for (x = 0; x < vw; x++)
+ {
+ if (x & 1)
+ {
+ p = dev->image[y * w + (x >> 1)];
+ }
+ else
+ {
+ p = dev->image[y * w + (x >> 1) + (w >> 1)];
+ }
+ if (y & 1)
+ bayer = 2;
+ else
+ bayer = 0;
+ if (x & 1)
+ bayer++;
+
+ switch (bayer)
+ {
+ case 0:
+ case 3:
+ colour = 1;
+ break;
+ case 1:
+ colour = 0;
+ break;
+ case 2:
+ colour = 2;
+ break;
+ }
+ i = (y * vw + x) * 3;
+ *(dev->output + i + colour) = (SANE_Byte) p;
+ } /* for x */
+
+ } /* for y */
+
+ /****** gamma correction plus hardcoded white balance */
+ /* Correction values red[], green[], blue[], are generated by
+ (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255.
+ White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and
+ converted to unsigned char. Values are in stv680.h */
+ if (dev->scan_mode == STV680_COLOR_RGB
+ || dev->scan_mode == STV680_COLOR_RGB_TEXT)
+ {
+ for (y = 0; y < vh; y++)
+ {
+ for (x = 0; x < vw; x++)
+ {
+ i = (y * vw + x) * 3;
+ *(dev->output + i) = red_g[*(dev->output + i)];
+ *(dev->output + i + 1) = green_g[*(dev->output + i + 1)];
+ *(dev->output + i + 2) = blue_g[*(dev->output + i + 2)];
+ }
+ }
+ }
+ DBG (DBG_proc, "stv680_bayer_unshuffle: gamma correction done\n");
+
+ if (dev->scan_mode != STV680_COLOR_RAW)
+ {
+
+ /****** bayer demosaic ******/
+ for (y = 1; y < (vh - 1); y++)
+ {
+ for (x = 1; x < (vw - 1); x++)
+ { /* work out pixel type */
+ if (y & 1)
+ bayer = 0;
+ else
+ bayer = 2;
+ if (!(x & 1))
+ bayer++;
+ switch (bayer)
+ {
+ case 0: /* green. blue lr, red tb */
+ *(dev->output + AD (x, y, vw) + BLUE) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + BLUE) +
+ (int) *(dev->output + AD (x + 1, y, vw) + BLUE)) >> 1;
+ *(dev->output + AD (x, y, vw) + RED) =
+ ((int) *(dev->output + AD (x, y - 1, vw) + RED) +
+ (int) *(dev->output + AD (x, y + 1, vw) + RED)) >> 1;
+ break;
+
+ case 1: /* blue. green lrtb, red diagonals */
+ *(dev->output + AD (x, y, vw) + GREEN) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x + 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y - 1, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(dev->output + AD (x, y, vw) + RED) =
+ ((int) *(dev->output + AD (x - 1, y - 1, vw) + RED) +
+ (int) *(dev->output + AD (x - 1, y + 1, vw) + RED) +
+ (int) *(dev->output + AD (x + 1, y - 1, vw) + RED) +
+ (int) *(dev->output + AD (x + 1, y + 1, vw) + RED)) >> 2;
+ break;
+
+ case 2: /* red. green lrtb, blue diagonals */
+ *(dev->output + AD (x, y, vw) + GREEN) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x + 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y - 1, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(dev->output + AD (x, y, vw) + BLUE) =
+ ((int) *(dev->output + AD (x - 1, y - 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x + 1, y - 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x - 1, y + 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x + 1, y + 1, vw) +
+ BLUE)) >> 2;
+ break;
+
+ case 3: /* green. red lr, blue tb */
+ *(dev->output + AD (x, y, vw) + RED) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + RED) +
+ (int) *(dev->output + AD (x + 1, y, vw) + RED)) >> 1;
+ *(dev->output + AD (x, y, vw) + BLUE) =
+ ((int) *(dev->output + AD (x, y - 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x, y + 1, vw) + BLUE)) >> 1;
+ break;
+ } /* switch */
+ } /* for x */
+ } /* for y - end demosaic */
+ } /* no bayer demosaic */
+ DBG (DBG_proc, "stv680_bayer_unshuffle: bayer demosaic done\n");
+
+ /* fix top and bottom row, left and right side */
+ i = vw * 3;
+ memcpy (dev->output, (dev->output + i), i);
+
+ memcpy ((dev->output + (vh * i)), (dev->output + ((vh - 1) * i)), i);
+
+
+ for (y = 0; y < vh; y++)
+ {
+ i = y * vw * 3;
+ memcpy ((dev->output + i), (dev->output + i + 3), 3);
+ memcpy ((dev->output + i + (vw * 3)),
+ (dev->output + i + (vw - 1) * 3), 3);
+ }
+
+ /* process all raw data, then trim to size if necessary */
+ if (dev->subsample == 160)
+ {
+ i = 0;
+ for (y = 0; y < vh; y++)
+ {
+ if (!(y & 1))
+ {
+ for (x = 0; x < vw; x++)
+ {
+ p = (y * vw + x) * 3;
+ if (!(x & 1))
+ {
+ *(dev->output + i) = *(dev->output + p);
+ *(dev->output + i + 1) = *(dev->output + p + 1);
+ *(dev->output + i + 2) = *(dev->output + p + 2);
+ i += 3;
+ }
+ } /* for x */
+ }
+ } /* for y */
+
+ DBG (DBG_proc,
+ "stv680_bayer_unshuffle: if needed, trim to size 160 done\n");
+ }
+ /* reset to proper width */
+ if ((dev->subsample == 160))
+ {
+ vw = 160;
+ vh = 120;
+ }
+
+ /* brightness adjustment */
+
+ count = vw * vh * 3;
+
+ bright_red = (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_R].w);
+ bright_green =
+ (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_G].w);
+ bright_blue =
+ (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_B].w);
+
+ for (x = 0; x < count; x++)
+ {
+ y = x + 1;
+ i = x + 2;
+ if ((*(dev->output + x) + bright_red) >= 255)
+ *(buf + x) = 255;
+
+ else if ((*(dev->output + x) + bright_red) <= 0)
+ *(buf + x) = 0;
+ else
+ *(buf + x) = (*(dev->output + x) + bright_red);
+
+ if ((*(dev->output + y) + bright_green) >= 255)
+ *(buf + y) = 255;
+
+ else if ((*(dev->output + y) + bright_green) <= 0)
+ *(buf + y) = 0;
+ else
+ *(buf + y) = (*(dev->output + y) + bright_green);
+
+ if ((*(dev->output + i) + bright_blue) >= 255)
+ *(buf + i) = 255;
+
+ else if ((*(dev->output + i) + bright_blue) <= 0)
+ *(buf + i) = 0;
+ else
+ *(buf + i) = (*(dev->output + i) + bright_blue);
+
+ x += 2;
+ }
+
+ if (dev->scan_mode == STV680_COLOR_RGB_TEXT)
+ {
+ strcpy (dev->picmsg_ps, "STVcam ");
+
+ status = stv680_add_text (buf, vw, vh, dev->picmsg_ps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "stv680_bayer_unshuffle status NOK\n");
+ return (status);
+ }
+ }
+
+ DBG (DBG_proc, "stv680_bayer_unshuffle: exit vw=%d, vh=%d\n", vw, vh);
+ status = (SANE_STATUS_GOOD);
+ return status;
+}
+
+/* Sane entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ FILE *fp;
+ char line[PATH_MAX];
+ size_t len;
+
+ num_devices = 0;
+ devlist = NULL;
+ first_dev = NULL;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ authorize = authorize; /* silence gcc */
+
+ DBG (DBG_error, "This is sane-stv680 version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2004-2006 by Gerard Klaver\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ DBG (DBG_proc, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ sanei_usb_init ();
+
+ fp = sanei_config_open (STV680_CONFIG_FILE);
+ if (!fp)
+ {
+ /* No default vidcam? */
+ DBG (DBG_warning, "configuration file not found (%s)\n",
+ STV680_CONFIG_FILE);
+
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ SANE_Word vendor;
+ SANE_Word product;
+
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+ if (sscanf (line, "usb %i %i", &vendor, &product) == 2)
+ {
+
+ sanei_usb_attach_matching_devices (line, attach_one);
+ }
+ else
+ {
+ /* Garbage. Ignore. */
+ DBG (DBG_warning, "bad configuration line: \"%s\" - ignoring.\n",
+ line);
+ }
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Stv680_Vidcam *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ local_only = local_only; /* silence gcc */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Stv680_Vidcam *dev;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_vidcam (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No vidcam found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ stv680_init_options (dev);
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Stv680_Vidcam *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Stv680_Vidcam *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BRIGHTNESS:
+ case OPT_WHITE_LEVEL_R:
+ case OPT_WHITE_LEVEL_G:
+ case OPT_WHITE_LEVEL_B:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+ case OPT_MODE:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Numeric side-effect options */
+ case OPT_RESOLUTION:
+ case OPT_BRIGHTNESS:
+ case OPT_WHITE_LEVEL_R:
+ case OPT_WHITE_LEVEL_G:
+ case OPT_WHITE_LEVEL_B:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect options */
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, COLOR_RAW_STR) == 0)
+ {
+ dev->scan_mode = STV680_COLOR_RAW;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_RGB_STR) == 0)
+ {
+ dev->scan_mode = STV680_COLOR_RGB;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)
+ == 0)
+ {
+ dev->scan_mode = STV680_COLOR;
+
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_RGB_TEXT_STR) == 0)
+ {
+ dev->scan_mode = STV680_COLOR_RGB_TEXT;
+
+ }
+
+ /* The STV680 supports only a handful of resolution. */
+ /* This the default resolution range for the STV680 */
+
+ dev->depth = 8;
+ if (dev->resolutions_list != NULL)
+ {
+ int i;
+
+ dev->opt[OPT_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list =
+ dev->resolutions_list;
+
+ /* If the resolution isn't in the list, set a default. */
+ for (i = 1; i <= dev->resolutions_list[0]; i++)
+ {
+ if (dev->resolutions_list[i] >= dev->val[OPT_RESOLUTION].w)
+ break;
+ }
+ if (i > dev->resolutions_list[0])
+ {
+ /* Too big. Take lowest. */
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[1];
+ }
+ else
+ {
+ /* Take immediate superioir value. */
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[i];
+ }
+ }
+
+ /* String side-effect options */
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Stv680_Vidcam *dev = handle;
+ int i;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+ dev->x_resolution = dev->val[OPT_RESOLUTION].w;
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.last_frame = SANE_TRUE;
+
+ switch (dev->scan_mode)
+ {
+ case STV680_COLOR_RAW:
+ dev->bytes_pixel = 1; /* raw image is 422 code, 1 byte/pixel */
+ break;
+ case STV680_COLOR_RGB:
+ case STV680_COLOR_RGB_TEXT:
+ case STV680_COLOR:
+ dev->bytes_pixel = 3;
+ break;
+ }
+ dev->params.format = SANE_FRAME_RGB;
+ dev->params.pixels_per_line = dev->x_resolution;
+ dev->params.bytes_per_line =
+ dev->params.pixels_per_line * dev->bytes_pixel;
+ dev->params.depth = 8;
+ if (dev->resolutions_list != NULL)
+ {
+ /* This vidcam has a fixed number of supported
+ * resolutions. Find the color sequence for that
+ * resolution. */
+
+ for (i = 0;
+ dev->hw->color_adjust[i].resolution_x != dev->x_resolution;
+ i++);
+
+ dev->red_s = dev->hw->color_adjust[i].z1_color_0;
+ dev->green_s = dev->hw->color_adjust[i].z1_color_1;
+ dev->blue_s = dev->hw->color_adjust[i].z1_color_2;
+ dev->y_resolution = dev->hw->color_adjust[i].resolution_y;
+ }
+ dev->subsample = 0;
+ switch (dev->val[OPT_RESOLUTION].w)
+ {
+ case 176:
+ dev->video_mode = 0x0200;
+ dev->cwidth = dev->x_resolution + 2;
+ dev->cheight = dev->y_resolution + 2;
+ break;
+ case 160: /* 160x120 subsampled */
+ dev->x_resolution = 320;
+ dev->y_resolution = 240;
+ dev->video_mode = 0x0300;
+ dev->cwidth = dev->x_resolution + 2;
+ dev->cheight = dev->y_resolution + 2;
+ dev->subsample = 160;
+ break;
+ case 320:
+ dev->video_mode = 0x0300;
+ dev->cwidth = dev->x_resolution + 2;
+ dev->cheight = dev->y_resolution + 2;
+ break;
+ case 352:
+ dev->video_mode = 0x0000;
+ dev->cwidth = dev->x_resolution + 4;
+ dev->cheight = dev->y_resolution + 4;
+ break;
+ case 640:
+ dev->video_mode = 0x0100;
+ dev->cwidth = dev->x_resolution + 4;
+ dev->cheight = dev->y_resolution + 4;
+ break;
+ }
+ dev->params.pixels_per_line = dev->x_resolution;
+ dev->params.lines = dev->y_resolution;
+ DBG (DBG_info, "sane_get_parameters: x=%d, y=%d\n", dev->x_resolution,
+ dev->y_resolution);
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Stv680_Vidcam *dev = handle;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+ sane_get_parameters (dev, NULL);
+
+ /* Open again the vidcam */
+ if (sanei_usb_open (dev->devicename, &(dev->fd)) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Initialize the vidcam. */
+ status = stv680_vidcam_init (dev);
+ if (status)
+ {
+ DBG (DBG_error, "ERROR: failed to init the vidcam\n");
+ stv680_close (dev);
+ return status;
+ }
+
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+ /* real_byte_left is bulk read bytes, bytes_left is frontend buffer bytes */
+ dev->real_bytes_left = dev->cwidth * dev->cheight;
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Stv680_Vidcam *dev = handle;
+ size_t size;
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+ if (dev->deliver_eof)
+ {
+ dev->deliver_eof = 0;
+ return SANE_STATUS_EOF;
+ }
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning, stop a scan. */
+ stv680_reset_vidcam (dev);
+ stv680_close (dev);
+ dev->scanning = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = stv680_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "sane_read: stv680_fill_image status NOK\n");
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ size = dev->bytes_left;
+ if (((unsigned int) max_len) < size)
+ {
+ DBG (DBG_error, "sane_read: max_len < size\n");
+ return (SANE_FALSE);
+ }
+ if ((dev->image_end - dev->image_begin) > size)
+ {
+ size = dev->image_end - dev->image_begin;
+ DBG (DBG_proc, "sane_read: size < dev->image_end - dev->image_begin\n");
+ }
+ /* diff between size an dev->bytes_left because of 356/352 and 292/288 */
+ DBG (DBG_info, "sane_read: size =0x%lx bytes, max_len=0x%lx bytes\n",
+ (unsigned long) (size_t) size, (unsigned long) (size_t) max_len);
+
+ *len = dev->bytes_left; /* needed */
+ size = dev->bytes_left;
+ dev->bytes_left = 0; /* needed for frontend or ? */
+
+ if (dev->scan_mode != STV680_COLOR_RAW)
+ {
+ /* do bayer unshuffle after complete frame is read */
+ status = stv680_bayer_unshuffle (dev, buf, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "sane_read: stv680_bayer_unshuffle status NOK\n");
+ return (status);
+ }
+ }
+ else
+ {
+ /* Copy the raw data to the frontend buffer. */
+ memcpy (buf, dev->image, size);
+ DBG (DBG_info, "sane_read: raw mode\n");
+ }
+ DBG (DBG_info, "sane_read: exit\n");
+ status = SANE_STATUS_GOOD;
+ return status;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ handle = handle; /* silence gcc */
+ non_blocking = non_blocking; /* silence gcc */
+
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ handle = handle; /* silence gcc */
+ fd = fd; /* silence gcc */
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Stv680_Vidcam *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ /* Stop a scan. */
+ if (dev->scanning == SANE_TRUE)
+ {
+ /* Reset the vidcam */
+ stv680_reset_vidcam (dev);
+ stv680_close (dev);
+ }
+ dev->scanning = SANE_FALSE;
+ dev->deliver_eof = 0;
+
+ /* return SANE_STATUS_CANCELLED; */
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Stv680_Vidcam *dev = handle;
+ Stv680_Vidcam *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+/* Stop a scan. */
+
+ if (dev->scanning == SANE_TRUE)
+ {
+ stv680_reset_vidcam (dev);
+ stv680_close (dev);
+ }
+ dev->scanning = SANE_FALSE;
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ stv680_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/stv680.conf.in b/backend/stv680.conf.in
new file mode 100755
index 0000000..eb15e7c
--- /dev/null
+++ b/backend/stv680.conf.in
@@ -0,0 +1,10 @@
+#stv680.conf
+#
+# AIPTEK stv680 vidcam
+usb 0x0553 0x0202
+# Konica e-mini
+usb 0x04c8 0x0722
+# DigitalDream l'espion XS
+usb 0x1183 0x0001
+#Creative WebCam Go mini
+usb 0x041e 0x4007
diff --git a/backend/stv680.h b/backend/stv680.h
new file mode 100755
index 0000000..e08976b
--- /dev/null
+++ b/backend/stv680.h
@@ -0,0 +1,302 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2004 -2006 Gerard Klaver (gerard at gkall dot hobby dot nl)
+
+ The teco2 and gl646 backend (Frank Zago) are used as a template for
+ this backend.
+
+ For the usb commands and bayer decoding parts of the following
+ program are used:
+ The pencam2 program (GNU GPL license 2)
+
+ For the usb commands parts of the following programs are used:
+ The libgphoto2 (camlib stv0680) (GNU GPL license 2)
+ The stv680.c/.h kernel module (GNU GPL license 2)
+
+ For the stv680_add_text routine the add_text routine and font_6x11.h file
+ are taken from the webcam.c file, part of xawtv program,
+ (c) 1998-2002 Gerd Knorr (GNU GPL license 2).
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+ ---------------------------------------------------------------------
+*/
+
+/*
+ $Id$
+ update 20-04-2006*/
+
+/* Commands supported by the vidcam. */
+
+/*--------------------------------------------------------------------------*/
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/*--------------------------------------------------------------------------*/
+
+#include <stdio.h>
+
+#define LIBUSB_TIMEOUT 1000 /* ms */
+
+typedef unsigned char byte;
+
+/*--------------------------------------------------------------------------*/
+
+/* Black magic for color adjustment. */
+struct dpi_color_adjust
+{
+ int resolution_x; /* x-resolution */
+ int resolution_y; /* y-resolution */
+
+ int z1_color_0; /* 0, 1 or 2 */
+ int z1_color_1; /* idem */
+ int z1_color_2; /* idem */
+};
+
+/*--------------------------------------------------------------------------*/
+
+enum Stv680_Option
+{
+ /* Must come first */
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* vidcam modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+ OPT_BRIGHTNESS, /* brightness */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_WHITE_LEVEL_R, /*white level red correction */
+ OPT_WHITE_LEVEL_G, /*white level green correction */
+ OPT_WHITE_LEVEL_B, /*white level blue correction */
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Video Camera supported by this backend.
+ */
+struct vidcam_hardware
+{
+ /* USB stuff */
+ SANE_Word vendor;
+ SANE_Word product;
+ SANE_Word class;
+
+ /* Readable names */
+ const char *vendor_name; /* brand on the box */
+ const char *product_name; /* name on the box */
+
+ /* Resolutions supported in color mode. */
+ const struct dpi_color_adjust *color_adjust;
+};
+
+#define COLOR_RAW_STR SANE_I18N("Color RAW")
+#define COLOR_RGB_STR SANE_I18N("Color RGB")
+#define COLOR_RGB_TEXT_STR SANE_I18N("Color RGB TEXT")
+/*--------------------------------------------------------------------------*/
+
+/* Define a vidcam occurence. */
+typedef struct Stv680_Vidcam
+{
+ struct Stv680_Vidcam *next;
+ SANE_Device sane;
+
+ char *devicename;
+ SANE_Int fd; /* device handle */
+
+ /* USB handling */
+ size_t buffer_size; /* size of the buffer */
+ SANE_Byte *buffer; /* for USB transfer. */
+
+ /* Bayer handling */
+ size_t output_size; /* size of the output */
+ SANE_Byte *output; /* for bayer conversion */
+
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+ SANE_Byte *image; /* keep the raw image here */
+
+ /* USB control messages handling */
+ size_t windoww_size; /* size of window write */
+ size_t windowr_size; /* size of window read */
+ SANE_Byte *windoww; /* for window write */
+ SANE_Byte *windowr; /* for window read */
+
+ /* Scanner infos. */
+ const struct vidcam_hardware *hw; /* default options for that vidcam */
+
+ SANE_Word *resolutions_list;
+ SANE_Word *color_sequence_list;
+
+ /* Scanning handling. */
+ SANE_Bool scanning; /* TRUE if a scan is running. */
+ SANE_Bool deliver_eof;
+ int x_resolution; /* X resolution */
+ int y_resolution; /* Y resolution */
+ int depth; /* depth per color */
+ unsigned int colour;
+ int red_s;
+ int green_s;
+ int blue_s;
+
+ SANE_Parameters s_params;
+ enum
+ {
+ STV680_COLOR_RGB,
+ STV680_COLOR_RGB_TEXT,
+ STV680_COLOR,
+ STV680_COLOR_RAW
+ }
+ scan_mode;
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+ size_t real_bytes_left; /* number of bytes left the vidcam will return. */
+ int bytes_pixel;
+
+ const struct dpi_color_adjust *color_adjust;
+
+ SANE_Parameters params;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+
+ unsigned int video_mode; /* 0x0100 = VGA, 0x0000 = CIF,
+ * 0x0300 = QVGA, 0x0200 = QCIF*/
+ unsigned int video_status; /* 0x01=start, 0x02=video, 0x04=busy, 0x08=idle */
+
+ int SupportedModes;
+ int HardwareConfig;
+ int QSIF;
+ int CIF;
+ int VGA;
+ int QVGA;
+ int QCIF;
+ int cwidth; /* camera width */
+ int cheight;
+ int subsample;
+
+ int framecount;
+ char picmsg_ps[50];
+
+}
+Stv680_Vidcam;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------*/
+
+static SANE_Byte red_g[256] = {
+ 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42,
+ 44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69,
+ 71, 71, 73, 75, 77, 78, 80, 81, 82, 84, 85, 87,
+ 88, 89, 90, 91, 93, 94, 95, 97, 98, 98, 99, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134,
+ 134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, 143,
+ 143, 143, 144, 145, 146, 147, 147, 148, 149, 150, 150, 151,
+ 152, 152, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159,
+ 159, 160, 161, 161, 161, 162, 163, 163, 164, 165, 165, 166,
+ 167, 167, 168, 168, 169, 170, 170, 170, 171, 171, 172, 173,
+ 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 179, 179,
+ 180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186,
+ 187, 187, 188, 188, 188, 188, 189, 190, 190, 191, 191, 192,
+ 192, 193, 193, 194, 195, 195, 196, 196, 197, 197, 197, 197,
+ 198, 198, 199, 199, 200, 201, 201, 202, 202, 203, 203, 204,
+ 204, 205, 205, 206, 206, 206, 206, 207, 207, 208, 208, 209,
+ 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215,
+ 215, 215, 215, 216, 216, 217, 217, 218, 218, 218, 219, 219,
+ 220, 220, 221, 221
+};
+
+static SANE_Byte green_g[256] = {
+ 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47,
+ 50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77,
+ 79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97,
+ 98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113,
+ 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149,
+ 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159,
+ 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168,
+ 169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177,
+ 177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185,
+ 186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193,
+ 193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200,
+ 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207,
+ 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214,
+ 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220,
+ 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+ 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
+ 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+ 239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244,
+ 245, 245, 246, 246
+};
+
+static SANE_Byte blue_g[256] = {
+ 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51,
+ 55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84,
+ 86, 88, 90, 92, 94, 95, 97, 100, 101, 103, 104, 106,
+ 107, 110, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124,
+ 125, 126, 127, 128, 129, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163,
+ 165, 166, 166, 167, 168, 169, 170, 171, 171, 172, 173, 174,
+ 176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184,
+ 185, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194,
+ 194, 195, 196, 196, 198, 199, 200, 200, 201, 202, 202, 203,
+ 204, 204, 205, 205, 206, 207, 207, 209, 210, 210, 211, 212,
+ 212, 213, 213, 214, 215, 215, 216, 217, 217, 218, 218, 220,
+ 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+ 228, 228, 229, 229, 231, 231, 232, 233, 233, 234, 234, 235,
+ 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 242, 242,
+ 243, 243, 244, 244, 245, 246, 246, 247, 247, 248, 248, 249,
+ 249, 250, 250, 251, 251, 253, 253, 254, 254, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255
+};
diff --git a/backend/tamarack.c b/backend/tamarack.c
new file mode 100644
index 0000000..9baec51
--- /dev/null
+++ b/backend/tamarack.c
@@ -0,0 +1,1474 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996 David Mosberger-Tang
+ Copyright (C) 1997 R.E.Wolff@BitWizard.nl
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ Note: The exception that is mentioned in the other source files is
+ not here. If a case arises where you need the rights that that
+ exception gives you, Please do contact me, and we'll work something
+ out.
+
+ R.E.Wolff@BitWizard.nl
+ tel: +31-152137555
+ fax: +31-152138217
+
+ This file implements a SANE backend for Tamarack flatbed scanners. */
+
+/*
+ This driver was written initially by changing all occurances of
+ "mustek" to "tamarack". This actuall worked without modification
+ for the manufacturer detection code! :-)
+
+ */
+
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_thread.h"
+#include "../include/sane/sanei_config.h"
+
+/* For timeval... */
+#ifdef DEBUG
+#include <sys/time.h>
+#endif
+
+
+#define BACKEND_NAME tamarack
+#include "../include/sane/sanei_backend.h"
+
+#include "tamarack.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define TAMARACK_CONFIG_FILE "tamarack.conf"
+
+
+static int num_devices;
+static Tamarack_Device *first_dev;
+static Tamarack_Scanner *first_handle;
+
+static const SANE_String_Const mode_list[] =
+ {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+ };
+
+
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+
+
+/* David used " 100 << SANE_FIXED_SCALE_SHIFT ". This assumes that
+ * it is implemented that way. I want to hide the datatype.
+ */
+static const SANE_Range percentage_range =
+ {
+ SANE_FIX(-100), /* minimum */
+ SANE_FIX( 100), /* maximum */
+ SANE_FIX( 1 ) /* quantization */
+ };
+
+/* David used " 100 << SANE_FIXED_SCALE_SHIFT ". This assumes that
+ * it is implemented that way. I want to hide the datatype.
+ */
+static const SANE_Range abs_percentage_range =
+ {
+ SANE_FIX( 0), /* minimum */
+ SANE_FIX( 100), /* maximum */
+ SANE_FIX( 1 ) /* quantization */
+ };
+
+
+#define INQ_LEN 0x60
+static const uint8_t inquiry[] =
+{
+ TAMARACK_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00
+};
+
+static const uint8_t test_unit_ready[] =
+{
+ TAMARACK_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t stop[] =
+{
+ TAMARACK_SCSI_START_STOP, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t get_status[] =
+{
+ TAMARACK_SCSI_GET_DATA_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x00
+};
+
+
+
+static SANE_Status
+wait_ready (int fd)
+{
+ SANE_Status status;
+ int i;
+
+ for (i = 0; i < 1000; ++i)
+ {
+ DBG(3, "wait_ready: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0);
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG(1, "wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ DBG(1, "wait_ready: timed out after %d attempts\n", i);
+ return SANE_STATUS_INVAL;
+}
+
+
+
+static SANE_Status
+sense_handler (int scsi_fd, u_char *result, void *arg)
+{
+ scsi_fd = scsi_fd;
+ arg = arg; /* silence compilation warnings */
+
+ switch (result[0])
+ {
+ case 0x00:
+ break;
+
+ default:
+ DBG(1, "sense_handler: got unknown sense code %02x\n", result[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/* XXX This might leak the memory to a TAMARACK string */
+
+static SANE_Status
+attach (const char *devname, Tamarack_Device **devp)
+{
+ char result[INQ_LEN];
+ int fd;
+ Tamarack_Device *dev;
+ SANE_Status status;
+ size_t size;
+ char *mfg, *model;
+ char *p;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0) {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(3, "attach: opening %s\n", devname);
+ status = sanei_scsi_open (devname, &fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "attach: open failed (%s)\n", sane_strstatus (status));
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(3, "attach: sending INQUIRY\n");
+ size = sizeof (result);
+ status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size);
+ if (status != SANE_STATUS_GOOD || size != INQ_LEN) {
+ DBG(1, "attach: inquiry failed (%s)\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+
+ status = wait_ready (fd);
+ sanei_scsi_close (fd);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ result[33]= '\0';
+ p = strchr(result+16,' ');
+ if (p) *p = '\0';
+ model = strdup (result+16);
+
+ result[16]= '\0';
+ p = strchr(result+8,' ');
+ if (p) *p = '\0';
+ mfg = strdup (result+8);
+
+ DBG(1, "attach: Inquiry gives mfg=%s, model=%s.\n", mfg, model);
+
+ if (strcmp (mfg, "TAMARACK") != 0) {
+ DBG(1, "attach: device doesn't look like a Tamarack scanner "
+ "(result[0]=%#02x)\n", result[0]);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor = "Tamarack";
+ dev->sane.model = model;
+ dev->sane.type = "flatbed scanner";
+
+ dev->x_range.min = 0;
+ dev->y_range.min = 0;
+ dev->x_range.quant = 0;
+ dev->y_range.quant = 0;
+ dev->dpi_range.min = SANE_FIX (1);
+ dev->dpi_range.quant = SANE_FIX (1);
+
+ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
+ dev->y_range.max = SANE_FIX (11.0 * MM_PER_INCH);
+ dev->dpi_range.max = SANE_FIX (600);
+
+ DBG(3, "attach: found Tamarack scanner model %s (%s)\n",
+ dev->sane.model, dev->sane.type);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static SANE_Status
+constrain_value (Tamarack_Scanner *s, SANE_Int option, void *value,
+ SANE_Int *info)
+{
+ return sanei_constrain_value (s->opt + option, value, info);
+}
+
+
+static unsigned char sign_mag (double val)
+{
+ if (val > 100) val = 100;
+ if (val < -100) val = -100;
+ if (val >= 0) return ( val);
+ else return ((unsigned char)(-val)) | 0x80;
+}
+
+
+
+static SANE_Status
+scan_area_and_windows (Tamarack_Scanner *s)
+{
+ struct def_win_par dwp;
+
+ memset (&dwp,'\0',sizeof (dwp));
+ dwp.dwph.opc = TAMARACK_SCSI_AREA_AND_WINDOWS;
+ set_triple (dwp.dwph.len,8 + sizeof (dwp.wdb));
+
+ set_double (dwp.wdh.wpll, sizeof (dwp.wdb));
+
+ dwp.wdb.winid = WINID;
+ set_double (dwp.wdb.xres, (int) SANE_UNFIX (s->val[OPT_RESOLUTION].w));
+ set_double (dwp.wdb.yres, (int) SANE_UNFIX (s->val[OPT_RESOLUTION].w));
+
+ set_quad (dwp.wdb.ulx, (int) (47.2 * SANE_UNFIX (s->val[OPT_TL_X].w)));
+ set_quad (dwp.wdb.uly, (int) (47.2 * SANE_UNFIX (s->val[OPT_TL_Y].w)));
+ set_quad (dwp.wdb.width,
+ (int) (47.2 * SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)));
+ set_quad (dwp.wdb.length,
+ (int) (47.2 * SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)));
+
+ dwp.wdb.brightness = sign_mag (SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
+ dwp.wdb.contrast = sign_mag (SANE_UNFIX (s->val[OPT_CONTRAST].w));
+ dwp.wdb.thresh = 0x80;
+
+
+ switch (s->mode) {
+ case THRESHOLDED:
+ dwp.wdb.bpp = 1;
+ dwp.wdb.image_comp = 0;
+ dwp.wdb.thresh = 1 + 2.55 * (SANE_UNFIX (s->val[OPT_THRESHOLD].w));
+ break;
+ case DITHERED:
+ dwp.wdb.bpp = 1;
+ dwp.wdb.image_comp = 1;
+ break;
+ case GREYSCALE:
+ dwp.wdb.bpp = 8;
+ dwp.wdb.image_comp = 2;
+ break;
+ case TRUECOLOR:
+ dwp.wdb.bpp = 8;
+ dwp.wdb.image_comp = 2;
+ break;
+ default:
+ DBG(1, "Invalid mode. %d\n", s->mode);
+ return SANE_STATUS_INVAL;
+ }
+ DBG(1, "bright, thresh, contrast = %d(%5.1f), %d, %d(%5.1f)\n",
+ dwp.wdb.brightness, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w),
+ dwp.wdb.thresh ,
+ dwp.wdb.contrast , SANE_UNFIX (s->val[OPT_CONTRAST].w));
+
+ set_double (dwp.wdb.halftone, 1); /* XXX What does this do again ? */
+ dwp.wdb.pad_type = 3; /* This is the only usable pad-type. */
+ dwp.wdb.exposure = 0x6f; /* XXX Option? */
+ dwp.wdb.compr_type = 0;
+
+ /* XXX Shouldn't this be sizeof (dwp) */
+ return sanei_scsi_cmd (s->fd, &dwp, (10+8+38), 0, 0);
+}
+
+
+static SANE_Status
+mode_select (Tamarack_Scanner *s)
+{
+ struct {
+ struct command_header cmd;
+ struct page_header hdr;
+ struct tamarack_page page;
+ } c;
+
+ memset (&c, '\0', sizeof (c));
+ c.cmd.opc = TAMARACK_SCSI_MODE_SELECT;
+ c.cmd.pad0[0] = 0x10; /* Suddenly the pad bytes are no long pad... */
+ c.cmd.pad0[1] = 0;
+ c.cmd.len = sizeof (struct page_header) + sizeof (struct tamarack_page);
+ c.hdr.code = 0;
+ c.hdr.length = 6;
+ c.page.gamma = 2;
+ c.page.thresh = 0x80; /* XXX Option? */
+ switch (s->mode) {
+ case THRESHOLDED:
+ case DITHERED:
+ case GREYSCALE:
+ c.page.masks = 0x80;
+ break;
+ case TRUECOLOR:
+ c.page.masks = 0x40 >> s->pass;
+ break;
+ }
+ c.page.delay = 0x10; /* XXX Option? */
+ c.page.features = (s->val[OPT_TRANS].w ? TAM_TRANS_ON:0) | 1;
+ return sanei_scsi_cmd (s->fd, &c, sizeof (c), 0, 0);
+}
+
+
+static SANE_Status
+start_scan (Tamarack_Scanner *s)
+{
+ struct {
+ struct command_header cmd;
+ unsigned char winid[1];
+ } c;
+
+ memset (&c,'\0',sizeof (c));
+ c.cmd.opc = TAMARACK_SCSI_START_STOP;
+ c.cmd.len = sizeof (c.winid);
+ c.winid[0] = WINID;
+ return sanei_scsi_cmd (s->fd, &c, sizeof (c), 0, 0);
+}
+
+
+static SANE_Status
+stop_scan (Tamarack_Scanner *s)
+{
+ /* XXX I don't think a TAMARACK can stop in mid-scan. Just stop
+ sending it requests for data....
+ */
+ return sanei_scsi_cmd (s->fd, stop, sizeof (stop), 0, 0);
+}
+
+
+static SANE_Status
+do_eof (Tamarack_Scanner *s)
+{
+ if (s->pipe >= 0)
+ {
+ close (s->pipe);
+ s->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+
+
+static SANE_Status
+do_cancel (Tamarack_Scanner *s)
+{
+ s->scanning = SANE_FALSE;
+ s->pass = 0;
+
+ do_eof (s);
+
+ if (s->reader_pid != -1)
+ {
+ int exit_status;
+
+ /* ensure child knows it's time to stop: */
+ sanei_thread_kill (s->reader_pid);
+ sanei_thread_waitpid (s->reader_pid, &exit_status);
+ s->reader_pid = -1;
+ }
+
+ if (s->fd >= 0)
+ {
+ stop_scan (s);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+
+static SANE_Status
+get_image_status (Tamarack_Scanner *s)
+{
+ uint8_t result[12];
+ SANE_Status status;
+ size_t len;
+ int busy;
+
+#if 1
+ do
+ {
+ len = sizeof (result);
+ status = sanei_scsi_cmd (s->fd, get_status, sizeof (get_status),
+ result, &len);
+ if ((status != SANE_STATUS_GOOD) && (status != SANE_STATUS_DEVICE_BUSY))
+ return status;
+
+ busy = (result[2] != 8) || (status == SANE_STATUS_DEVICE_BUSY);
+ if (busy)
+ usleep (100000);
+
+ if (!s->scanning)
+ return do_cancel (s);
+ }
+ while (busy);
+#else
+ /* XXX Test if this works one day... */
+ wait_ready (s);
+#endif
+
+ len = sizeof (result);
+ status = sanei_scsi_cmd (s->fd, get_status, sizeof (get_status),
+ result, &len);
+ if ((status != SANE_STATUS_GOOD) && (status != SANE_STATUS_DEVICE_BUSY))
+ return status;
+
+ s->params.bytes_per_line =
+ result[ 8] | (result[ 7] << 8) | (result[6] << 16);
+ s->params.lines =
+ result[11] | (result[10] << 8) | (result[9] << 16);
+
+ switch (s->mode) {
+ case DITHERED:
+ case THRESHOLDED:
+ s->params.pixels_per_line = 8 * s->params.bytes_per_line;
+ break;
+ case GREYSCALE:
+ case TRUECOLOR:
+ s->params.pixels_per_line = s->params.bytes_per_line;
+ break;
+ }
+
+
+ DBG(1, "get_image_status: bytes_per_line=%d, lines=%d\n",
+ s->params.bytes_per_line, s->params.lines);
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+read_data (Tamarack_Scanner *s, SANE_Byte *buf, int lines, int bpl)
+{
+ struct command_header_10 cmd;
+ size_t nbytes;
+ SANE_Status status;
+#ifdef DEBUG
+ int dt;
+ struct timeval tv_start,tv_end;
+#endif
+
+ nbytes = bpl * lines;
+ memset (&cmd,'\0',sizeof (cmd));
+ cmd.opc = 0x28;
+ set_triple (cmd.len,nbytes);
+
+#ifdef DEBUG
+ if (verbose) DBG (1, "Doing read_data... \n");
+ gettimeofday (&tv_start,NULL);
+#endif
+
+ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, &nbytes);
+
+#ifdef DEBUG
+ gettimeofday (&tv_end,NULL);
+ dt = tv_end.tv_usec - tv_start.tv_usec +
+ (tv_end.tv_sec - tv_start.tv_sec) * 1000000;
+ if (verbose) DBG(1, "Read took %d.%06d seconds.",
+ dt/1000000,dt%1000000);
+ dt = 1000000 * nbytes / dt;
+ if (verbose) DBG(1, "which is %d.%03d bytes per second.\n",dt,0);
+#endif
+ return status;
+}
+
+
+
+static SANE_Status
+init_options (Tamarack_Scanner *s)
+{
+ int i;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = "Select the scan mode";
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[OPT_MODE_DEFAULT]);
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w = SANE_FIX (OPT_RESOLUTION_DEFAULT);
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* gray preview */
+ s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = s->hw->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = s->hw->y_range.max;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* transparency adapter. */
+ s->opt[OPT_TRANS].name = "transparency";
+ s->opt[OPT_TRANS].title = "transparency";
+ s->opt[OPT_TRANS].desc = "Turn on the transparency adapter.";
+ s->opt[OPT_TRANS].type = SANE_TYPE_BOOL;
+ s->opt[OPT_TRANS].unit = SANE_UNIT_NONE;
+ s->val[OPT_TRANS].w = SANE_FALSE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
+ " This option is active for lineart/halftone modes only. "
+ "For multibit modes (grey/color) use the gamma-table(s).";
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS].w = SANE_FIX(0);
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST
+ " This option is active for lineart/halftone modes only. "
+ "For multibit modes (grey/color) use the gamma-table(s).";
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST].w = SANE_FIX(0);
+
+ /* Threshold */
+ s->opt[OPT_THRESHOLD].name = "Threshold";
+ s->opt[OPT_THRESHOLD].title = "Threshold";
+ s->opt[OPT_THRESHOLD].desc = "Threshold: below this level is black, above is white"
+ " This option is active for bitmap modes only. ";
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &abs_percentage_range;
+ s->val[OPT_THRESHOLD].w = SANE_FIX(50);
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+#if 0
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+
+/* This function is executed as a child process. The reason this is
+ executed as a subprocess is because some (most?) generic SCSI
+ interfaces block a SCSI request until it has completed. With a
+ subprocess, we can let it block waiting for the request to finish
+ while the main process can go about to do more important things
+ (such as recognizing when the user presses a cancel button).
+
+ WARNING: Since this is executed as a subprocess, it's NOT possible
+ to update any of the variables in the main process (in particular
+ the scanner state cannot be updated). */
+static int
+reader_process (void *scanner)
+{
+ Tamarack_Scanner *s = (Tamarack_Scanner *) scanner;
+ int fd = s->reader_pipe;
+
+ SANE_Byte *data;
+ int lines_per_buffer, bpl;
+ SANE_Status status;
+ sigset_t sigterm_set;
+ sigset_t ignore_set;
+ struct SIGACTION act;
+ FILE *fp;
+
+ if (sanei_thread_is_forked()) close (s->pipe);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ fp = fdopen (fd, "w");
+ if (!fp)
+ return 1;
+
+ bpl = s->params.bytes_per_line;
+
+ lines_per_buffer = sanei_scsi_max_request_size / bpl;
+ if (!lines_per_buffer)
+ return 2; /* resolution is too high */
+
+ /* Limit the size of a single transfer to one inch.
+ XXX Add a stripsize option. */
+ if (lines_per_buffer > SANE_UNFIX (s->val[OPT_RESOLUTION].w))
+ lines_per_buffer = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+
+ DBG(3, "lines_per_buffer=%d, bytes_per_line=%d\n", lines_per_buffer, bpl);
+
+ data = malloc (lines_per_buffer * bpl);
+
+ for (s->line = 0; s->line < s->params.lines; s->line += lines_per_buffer) {
+ if (s->line + lines_per_buffer > s->params.lines)
+ /* do the last few lines: */
+ lines_per_buffer = s->params.lines - s->line;
+
+ sigprocmask (SIG_BLOCK, &sigterm_set, 0);
+ status = read_data (s, data, lines_per_buffer, bpl);
+ sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "reader_process: read_data failed with status=%d\n", status);
+ return 3;
+ }
+ DBG(3, "reader_process: read %d lines\n", lines_per_buffer);
+
+ if ((s->mode == TRUECOLOR) || (s->mode == GREYSCALE)) {
+ fwrite (data, lines_per_buffer, bpl, fp);
+ } else {
+ /* in singlebit mode, the scanner returns 1 for black. ;-( --DM */
+ /* Hah! Same for Tamarack... -- REW */
+ int i;
+
+ for (i = 0; i < lines_per_buffer * bpl; ++i)
+ fputc (~data[i], fp);
+ }
+ }
+ fclose (fp);
+ return 0;
+}
+
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach (dev, 0);
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* silence compilation warnings */
+
+ DBG_INIT();
+
+ sanei_thread_init();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (TAMARACK_CONFIG_FILE);
+ if (!fp) {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp)) {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_exit (void)
+{
+ Tamarack_Device *dev, *next;
+
+ for (dev = first_dev; dev; dev = next) {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Tamarack_Device *dev;
+ int i;
+
+ local_only = local_only; /* silence compilation warnings */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle *handle)
+{
+ Tamarack_Device *dev;
+ SANE_Status status;
+ Tamarack_Scanner *s;
+ int i, j;
+
+ if (devicename[0]) {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev) {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ } else {
+ /* empty devicname -> use first device */
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->fd = -1;
+ s->pipe = -1;
+ s->hw = dev;
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 256; ++j)
+ s->gamma_table[i][j] = j;
+
+ init_options (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Tamarack_Scanner *prev, *s;
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next) {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+
+ if (!s) {
+ DBG(1, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ do_cancel (handle);
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free (handle);
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Tamarack_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return s->opt + option;
+}
+
+
+
+static int make_mode (char *mode)
+{
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ return THRESHOLDED;
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
+ return DITHERED;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ return GREYSCALE;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ return TRUECOLOR;
+
+ return -1;
+}
+
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int *info)
+{
+ Tamarack_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_GET_VALUE) {
+ switch (option) {
+ /* word options: */
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_TRANS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+#if 0
+ case OPT_CUSTOM_GAMMA:
+#endif
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+
+#if 0
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+#endif
+
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ } else if (action == SANE_ACTION_SET_VALUE) {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = constrain_value (s, option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_TRANS:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+#if 0
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* options with side-effects: */
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ if (w) {
+ s->mode = make_mode (s->val[OPT_MODE].s);
+
+ if (s->mode == GREYSCALE) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ } else if (s->mode == TRUECOLOR) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ } else {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+#endif
+
+ case OPT_MODE:
+ {
+
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+
+ s->mode = make_mode (s->val[OPT_MODE].s);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+#if 0
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+#endif
+
+
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ else {
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ }
+#if 0
+ if (!binary)
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+
+ if (s->val[OPT_CUSTOM_GAMMA].w) {
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+#endif
+ return SANE_STATUS_GOOD;
+ }
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
+{
+ Tamarack_Scanner *s = handle;
+
+ if (!s->scanning) {
+ double width, height, dpi;
+
+
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
+ height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
+ dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
+ s->mode = make_mode (s->val[OPT_MODE].s);
+ DBG(1, "got mode '%s' -> %d.\n", s->val[OPT_MODE].s, s->mode);
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ if (dpi > 0.0 && width > 0.0 && height > 0.0) {
+ double dots_per_mm = dpi / MM_PER_INCH;
+
+ s->params.pixels_per_line = width * dots_per_mm;
+ s->params.lines = height * dots_per_mm;
+ }
+ if ((s->mode == THRESHOLDED) || (s->mode == DITHERED)) {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ } else if (s->mode == GREYSCALE) {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ } else {
+ s->params.format = SANE_FRAME_RED + s->pass;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ s->pass = 0;
+ } else {
+ if (s->mode == TRUECOLOR)
+ s->params.format = SANE_FRAME_RED + s->pass;
+ }
+
+ s->params.last_frame = (s->mode != TRUECOLOR) || (s->pass == 2);
+
+ if (params)
+ *params = s->params;
+
+ DBG(1, "Got parameters: format:%d, ppl: %d, bpl:%d, depth:%d, "
+ "last %d pass %d\n",
+ s->params.format, s->params.pixels_per_line,
+ s->params.bytes_per_line, s->params.depth,
+ s->params.last_frame, s->pass);
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Tamarack_Scanner *s = handle;
+ SANE_Status status;
+ int fds[2];
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (s->fd < 0) {
+ /* translate options into s->mode for convenient access: */
+ s->mode = make_mode (s->val[OPT_MODE].s);
+
+ if (s->mode == TRUECOLOR)
+ {
+ if (s->val[OPT_PREVIEW].w && s->val[OPT_GRAY_PREVIEW].w) {
+ /* Force gray-scale mode when previewing. */
+ s->mode = GREYSCALE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.last_frame = SANE_TRUE;
+ }
+ }
+
+ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "open: open of %s failed: %s\n",
+ s->hw->sane.name, sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = wait_ready (s->fd);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "open: wait_ready() failed: %s\n", sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = scan_area_and_windows (s);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "open: set scan area command failed: %s\n",
+ sane_strstatus (status));
+ goto stop_scanner_and_return;
+ }
+
+ status = mode_select (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ s->scanning = SANE_TRUE;
+
+ status = start_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ status = get_image_status (s);
+ if (status != SANE_STATUS_GOOD)
+ goto stop_scanner_and_return;
+
+ s->line = 0;
+
+ if (pipe (fds) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ s->pipe = fds[0];
+ s->reader_pipe = fds[1];
+ s->reader_pid = sanei_thread_begin (reader_process, (void *) s);
+
+ if (sanei_thread_is_forked()) close (s->reader_pipe);
+
+ return SANE_STATUS_GOOD;
+
+stop_scanner_and_return:
+ do_cancel (s);
+ return status;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
+{
+ Tamarack_Scanner *s = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read (s->pipe, buf, max_len);
+ DBG(3, "read %ld bytes\n", (long) nread);
+
+ if (!s->scanning)
+ return do_cancel (s);
+
+ if (nread < 0) {
+ if (errno == EAGAIN) {
+ return SANE_STATUS_GOOD;
+ } else {
+ do_cancel (s);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0) {
+ s->pass++;
+ return do_eof (s);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Tamarack_Scanner *s = handle;
+
+ if (s->reader_pid != -1)
+ sanei_thread_kill (s->reader_pid);
+ s->scanning = SANE_FALSE;
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Tamarack_Scanner *s = handle;
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+ Tamarack_Scanner *s = handle;
+
+ if (!s->scanning)
+ return SANE_STATUS_INVAL;
+
+ *fd = s->pipe;
+ return SANE_STATUS_GOOD;
+}
+
diff --git a/backend/tamarack.conf.in b/backend/tamarack.conf.in
new file mode 100644
index 0000000..4c5ae7d
--- /dev/null
+++ b/backend/tamarack.conf.in
@@ -0,0 +1,3 @@
+scsi TAMARACK
+/dev/scanner
+
diff --git a/backend/tamarack.h b/backend/tamarack.h
new file mode 100644
index 0000000..65697ac
--- /dev/null
+++ b/backend/tamarack.h
@@ -0,0 +1,267 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang
+ Copyright (C) 1997 R.E.Wolff@BitWizard.nl
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ */
+
+#ifndef tamarack_h
+#define tamarack_h
+
+#include <sys/types.h>
+
+#define TAMARACK_FLAG_TA (1 << 3) /* transparency adapter */
+
+
+enum Tamarack_Option
+ {
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+#define OPT_MODE_DEFAULT 2
+ OPT_RESOLUTION,
+#define OPT_RESOLUTION_DEFAULT 100
+#if 0
+ OPT_SPEED,
+ OPT_SOURCE,
+ OPT_BACKTRACK,
+#endif
+ OPT_PREVIEW,
+ OPT_GRAY_PREVIEW,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_TRANS,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+#if 0
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables? */
+ /* The gamma vectors MUST appear in the order gray, red, green,
+ blue. */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_HALFTONE_DIMENSION,
+ OPT_HALFTONE_PATTERN,
+#endif
+
+ /* must come last: */
+ NUM_OPTIONS
+ };
+
+
+
+typedef struct Tamarack_Device
+ {
+ struct Tamarack_Device *next;
+ SANE_Device sane;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ unsigned flags;
+ }
+
+Tamarack_Device;
+
+typedef struct Tamarack_Scanner
+ {
+ /* all the state needed to define a scan request: */
+ struct Tamarack_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Int gamma_table[4][256];
+#if 0
+ SANE_Int halftone_pattern[64];
+#endif
+
+ int scanning;
+ int pass; /* pass number */
+ int line; /* current line number */
+ SANE_Parameters params;
+
+ /* Parsed option values and variables that are valid only during
+ actual scanning: */
+ int mode;
+#if 0
+ int one_pass_color_scan;
+ int resolution_code;
+#endif
+ int fd; /* SCSI filedescriptor */
+ SANE_Pid reader_pid; /* process id of reader */
+ int pipe; /* pipe to reader process */
+ int reader_pipe; /* pipe from reader process */
+
+ /* scanner dependent/low-level state: */
+ Tamarack_Device *hw;
+
+#if 0
+ /* line-distance correction related state: */
+ struct
+ {
+ int max_value;
+ int peak_res;
+ struct
+ {
+ int dist; /* line distance */
+ int Qk;
+ }
+ c[3];
+ /* these are used in the MLD_MFS mode only: */
+ char *red_buf;
+ char *green_buf;
+ }
+ ld;
+#endif
+ }
+Tamarack_Scanner;
+
+
+
+#define TAM_ADF_ON 0x80
+#define TAM_DOUBLE_ON 0x40
+#define TAM_TRANS_ON 0x20
+
+#define TAM_INVERSE_ON 0x20
+
+
+
+
+#define THRESHOLDED 0
+#define DITHERED 1
+#define GREYSCALE 2
+#define TRUECOLOR 3
+
+
+
+/* Some Tamarack driver internal defines */
+#define WINID 0
+
+
+/* SCSI commands that the Tamarack scanners understand: */
+
+#define TAMARACK_SCSI_TEST_UNIT_READY 0x00
+#define TAMARACK_SCSI_INQUIRY 0x12
+#define TAMARACK_SCSI_MODE_SELECT 0x15
+#define TAMARACK_SCSI_START_STOP 0x1b
+#define TAMARACK_SCSI_AREA_AND_WINDOWS 0x24
+#define TAMARACK_SCSI_READ_SCANNED_DATA 0x28
+#define TAMARACK_SCSI_GET_DATA_STATUS 0x34
+
+
+/* The structures that you have to send to the tamarack to get it to
+ do various stuff... */
+
+struct win_desc_header {
+ unsigned char pad0[6];
+ unsigned char wpll[2];
+};
+
+
+struct win_desc_block {
+ unsigned char winid;
+ unsigned char pad0;
+ unsigned char xres[2];
+ unsigned char yres[2];
+ unsigned char ulx[4];
+ unsigned char uly[4];
+ unsigned char width[4];
+ unsigned char length[4];
+ unsigned char brightness;
+ unsigned char thresh;
+ unsigned char contrast;
+ unsigned char image_comp;
+ unsigned char bpp;
+ unsigned char halftone[2];
+ unsigned char pad_type;
+ unsigned char exposure;
+ unsigned char pad3;
+ unsigned char compr_type;
+ unsigned char pad4[5];
+};
+
+
+
+struct command_header {
+ unsigned char opc;
+ unsigned char pad0[3];
+ unsigned char len;
+ unsigned char pad1;
+};
+
+
+struct command_header_10 {
+ unsigned char opc;
+ unsigned char pad0[5];
+ unsigned char len[3];
+ unsigned char pad1;
+};
+
+
+
+
+struct def_win_par {
+ struct command_header_10 dwph;
+ struct win_desc_header wdh;
+ struct win_desc_block wdb;
+};
+
+
+struct page_header{
+ char pad0[4];
+ char code;
+ char length;
+};
+
+
+
+struct tamarack_page {
+ char gamma;
+ unsigned char thresh;
+ unsigned char masks;
+ char delay;
+ char features;
+ char pad0;
+};
+
+
+/* set SCSI highended variables. Declare them as an array of chars */
+/* endianness-safe, int-size safe... */
+#define set_double(var,val) var[0] = ((val) >> 8) & 0xff; \
+ var[1] = ((val) ) & 0xff;
+
+#define set_triple(var,val) var[0] = ((val) >> 16) & 0xff; \
+ var[1] = ((val) >> 8 ) & 0xff; \
+ var[2] = ((val) ) & 0xff;
+
+#define set_quad(var,val) var[0] = ((val) >> 24) & 0xff; \
+ var[1] = ((val) >> 16) & 0xff; \
+ var[2] = ((val) >> 8 ) & 0xff; \
+ var[3] = ((val) ) & 0xff;
+
+#endif /* tamarack_h */
diff --git a/backend/teco1.c b/backend/teco1.c
new file mode 100644
index 0000000..2a13201
--- /dev/null
+++ b/backend/teco1.c
@@ -0,0 +1,2263 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ Some Relisys scanners AVEC and RELI series
+*/
+
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 10 /* 2004/02/08 */
+#define BACKEND_NAME teco1
+#define TECO_CONFIG_FILE "teco1.conf"
+
+/*--------------------------------------------------------------------------*/
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "teco1.h"
+
+#undef sim
+#ifdef sim
+#define sanei_scsi_cmd2(a, b, c, d, e, f, g) SANE_STATUS_GOOD
+#define sanei_scsi_open(a, b, c, d) 0
+#define sanei_scsi_cmd(a, b, c, d, e) SANE_STATUS_GOOD
+#define sanei_scsi_close(a) SANE_STATUS_GOOD
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list[] = {
+ BLACK_WHITE_STR,
+ GRAY_STR,
+ COLOR_STR,
+ NULL
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Minimum and maximum width and length supported. */
+static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 };
+static SANE_Range y_range = { SANE_FIX (0), SANE_FIX (14 * MM_PER_INCH), 0 };
+
+/*--------------------------------------------------------------------------*/
+
+/* Gamma range */
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of dithering options. */
+static SANE_String_Const dither_list[] = {
+ "Line art",
+ "2x2",
+ "3x3",
+ "4x4 bayer",
+ "4x4 smooth",
+ "8x8 bayer",
+ "8x8 smooth",
+ "8x8 horizontal",
+ "8x8 vertical",
+ NULL
+};
+static const int dither_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range threshold_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define the supported scanners and their characteristics. */
+static const struct scanners_supported scanners[] = {
+ {6, "TECO VM3510", /* *fake id*, see teco_identify_scanner */
+ TECO_VM3510,
+ "Dextra", "DF-600P",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y res */
+ 3, /* color 3 pass */
+ 256, /* number of bytes per gamma color */
+ 80 /* number of bytes in a window */
+ },
+
+ {6, "TECO VM353A",
+ TECO_VM353A,
+ "Relisys", "RELI 2412",
+ {1, 1200, 1}, /* resolution */
+ 300, 1200, /* max x and Y resolution */
+ 1, /* color 1 pass */
+ 256, /* number of bytes per gamma color */
+ 99 /* number of bytes in a window */
+ },
+
+ {6, "TECO VM3520",
+ TECO_VM3520,
+ "Relisys", "AVEC Colour Office 2400",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y resolution */
+ 3, /* color 3 pass */
+ 256, /* number of bytes per gamma color */
+ 99 /* number of bytes in a window */
+ },
+
+ {6, "TECO VM352A",
+ TECO_VM3520, /* same as AVEC 2400 */
+ "Relisys", "AVEC Colour 2412",
+ {1, 600, 1},
+ 300, 600,
+ 3,
+ 256,
+ 99
+ },
+
+ {6, "TECO VM4540",
+ TECO_VM4540,
+ "Relisys", "RELI 4816",
+ {1, 1600, 1}, /* resolution */
+ 400, 1600, /* max x and Y resolution */
+ 1, /* color 1 pass */
+ 256, /* number of bytes per gamma color */
+ 99 /* number of bytes in a window */
+ },
+
+ {6, "TECO VM4542",
+ TECO_VM4542,
+ "Relisys", "RELI 4830",
+ {1, 400, 1}, /* resolution */
+ 400, 400, /* max x and Y resolution */
+ 1, /* color 1 pass */
+ 1024, /* number of bytes per gamma color */
+ 99 /* number of bytes in a window */
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of scanner attached. */
+static Teco_Scanner *first_dev = NULL;
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+
+
+/* Local functions. */
+
+/* Display a buffer in the log. */
+static void
+hexdump (int level, const char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+ char asc_buf[17];
+ char *asc_ptr;
+
+ DBG (level, "%s\n", comment);
+
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ DBG (level, "%s %s\n", line, asc_buf);
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ ptr += sprintf (ptr, " %2.2x", *p);
+ if (*p >= 32 && *p <= 127)
+ {
+ asc_ptr += sprintf (asc_ptr, "%c", *p);
+ }
+ else
+ {
+ asc_ptr += sprintf (asc_ptr, ".");
+ }
+ }
+ *ptr = '\0';
+ DBG (level, "%s %s\n", line, asc_buf);
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+get_string_list_index (SANE_String_Const list[], SANE_String_Const name)
+{
+ int index;
+
+ index = 0;
+ while (list[index] != NULL)
+ {
+ if (strcmp (list[index], name) == 0)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "name %s not found in list\n", name);
+
+ assert (0 == 1); /* bug in backend, core dump */
+
+ return (-1);
+}
+
+/* Initialize a scanner entry. Return an allocated scanner with some
+ * preset values. */
+static Teco_Scanner *
+teco_init (void)
+{
+ Teco_Scanner *dev;
+
+ DBG (DBG_proc, "teco_init: enter\n");
+
+ /* Allocate a new scanner entry. */
+ dev = malloc (sizeof (Teco_Scanner));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ memset (dev, 0, sizeof (Teco_Scanner));
+
+ /* Allocate the buffer used to transfer the SCSI data. */
+ dev->buffer_size = 64 * 1024;
+ dev->buffer = malloc (dev->buffer_size);
+ if (dev->buffer == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+ /* Allocate a buffer to store the temporary image. */
+ dev->image_size = 64 * 1024; /* enough for 1 line at max res */
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ free (dev->buffer);
+ free (dev);
+ return NULL;
+ }
+
+ dev->sfd = -1;
+
+ DBG (DBG_proc, "teco_init: exit\n");
+
+ return (dev);
+}
+
+/* Closes an open scanner. */
+static void
+teco_close (Teco_Scanner * dev)
+{
+ DBG (DBG_proc, "teco_close: enter\n");
+
+ if (dev->sfd != -1)
+ {
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+ }
+
+ DBG (DBG_proc, "teco_close: exit\n");
+}
+
+/* Frees the memory used by a scanner. */
+static void
+teco_free (Teco_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "teco_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ teco_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+
+ free (dev);
+
+ DBG (DBG_proc, "teco_free: exit\n");
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+teco_identify_scanner (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "teco_identify_scanner: enter\n");
+
+ size = 5;
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+#ifdef sim
+ {
+#if 1
+ /* vm3510 / Dextra DF-600P */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x24, 0x00, 0x00, 0x10, 0x44, 0x46,
+ 0x2D, 0x36, 0x30, 0x30, 0x4D, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x31, 0x2E, 0x31, 0x37, 0x31, 0x2E, 0x31, 0x37,
+ 0x02
+ };
+#endif
+
+#if 0
+ /* vm4542 */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x30, 0x00, 0x00, 0x10, 0x52, 0x45, 0x4c, 0x49,
+ 0x53, 0x59, 0x53, 0x20, 0x52, 0x45, 0x4c, 0x49, 0x20, 0x34, 0x38,
+ 0x33, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x2e,
+ 0x30, 0x33, 0x31, 0x2e, 0x30, 0x33, 0x02, 0x00, 0x54, 0x45, 0x43,
+ 0x4f, 0x20, 0x56, 0x4d, 0x34, 0x35, 0x34, 0x32
+ };
+#endif
+ memcpy (dev->buffer, table, sizeof (table));
+ }
+#endif
+
+ size = dev->buffer[4] + 5; /* total length of the inquiry data */
+
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ /* Hack to recognize the dextra as a TECO scanner. */
+ if (memcmp (dev->buffer + 0x08, "DF-600M ", 8) == 0)
+ {
+ memcpy (dev->buffer + 0x29, "\0TECO VM3510", 12);
+ dev->buffer[4] = 0x30; /* change length */
+ size = 0x35;
+ }
+
+ if (size < 53)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: not enough data to identify device\n");
+ return (SANE_FALSE);
+ }
+
+ hexdump (DBG_info2, "inquiry", dev->buffer, size);
+
+ dev->scsi_type = dev->buffer[0] & 0x1f;
+ memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08);
+ dev->scsi_vendor[0x08] = 0;
+ memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010);
+ dev->scsi_product[0x10] = 0;
+ memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04);
+ dev->scsi_version[0x04] = 0;
+ memcpy (dev->scsi_teco_name, dev->buffer + 0x2A, 0x0B);
+ dev->scsi_teco_name[0x0B] = 0;
+
+ DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\" \"%s\"\n",
+ dev->scsi_vendor, dev->scsi_product, dev->scsi_version,
+ dev->scsi_teco_name);
+
+ /* Lookup through the supported scanners table to find if this
+ * backend supports that one. */
+ for (i = 0; i < NELEMS (scanners); i++)
+ {
+
+ if (dev->scsi_type == scanners[i].scsi_type &&
+ strcmp (dev->scsi_teco_name, scanners[i].scsi_teco_name) == 0)
+ {
+
+ DBG (DBG_error, "teco_identify_scanner: scanner supported\n");
+
+ dev->def = &(scanners[i]);
+
+ return (SANE_TRUE);
+ }
+ }
+
+ DBG (DBG_proc, "teco_identify_scanner: exit, device not supported\n");
+
+ return (SANE_FALSE);
+}
+
+/* Get the inquiry page 0x82. */
+static int
+teco_get_inquiry_82 (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+
+ DBG (DBG_proc, "teco_get_inquiry_82: enter\n");
+
+ size = 0x4;
+ MKSCSI_INQUIRY (cdb, size);
+ cdb.data[1] = 1; /* evpd */
+ cdb.data[2] = 0x82; /* page code number */
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_get_inquiry_82: inquiry page 0x82 failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ size = dev->buffer[3] + 4;
+ MKSCSI_INQUIRY (cdb, size);
+ cdb.data[1] = 1; /* evpd */
+ cdb.data[2] = 0x82; /* page code number */
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_get_inquiry_82: inquiry page 0x82 failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ hexdump (DBG_info2, "inquiry page 0x82", dev->buffer, size);
+
+ DBG (DBG_proc, "teco_get_inquiry_82: leave\n");
+
+ return (status);
+}
+
+/* SCSI sense handler. Callback for SANE.
+ * These scanners never set asc or ascq. */
+static SANE_Status
+teco_sense_handler (int __sane_unused__ scsi_fd, unsigned char *result, void __sane_unused__ *arg)
+{
+ int sensekey;
+ int len;
+
+ DBG (DBG_proc, "teco_sense_handler: enter\n");
+
+ sensekey = get_RS_sense_key (result);
+ len = 7 + get_RS_additional_length (result);
+
+ hexdump (DBG_info2, "sense", result, len);
+
+ if (get_RS_error_code (result) != 0x70)
+ {
+ DBG (DBG_error,
+ "teco_sense_handler: invalid sense key error code (%d)\n",
+ get_RS_error_code (result));
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (len < 14)
+ {
+ DBG (DBG_error, "teco_sense_handler: sense too short, no ASC/ASCQ\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_sense, "teco_sense_handler: sense=%d\n", sensekey);
+
+ if (sensekey == 0x00)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Send the mode select to the scanner. */
+static int
+teco_mode_select (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ unsigned char select[24] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x06, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00
+ };
+
+ DBG (DBG_proc, "teco_mode_select: enter\n");
+
+ size = 24;
+ MKSCSI_MODE_SELECT (cdb, 1, 0, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ select, size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_mode_select: exit\n");
+
+ return (status);
+}
+
+/* Set a window. */
+static SANE_Status
+teco_set_window (Teco_Scanner * dev)
+{
+ size_t size; /* significant size of window */
+ CDB cdb;
+ unsigned char window[99];
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "teco_set_window: enter\n");
+
+ size = dev->def->window_size;
+
+ MKSCSI_SET_WINDOW (cdb, size);
+
+ memset (window, 0, size);
+
+ /* size of the windows descriptor block */
+ window[7] = size - 8;
+
+ /* X and Y resolution */
+ Ito16 (dev->x_resolution, &window[10]);
+ Ito16 (dev->y_resolution, &window[12]);
+
+ /* Upper Left (X,Y) */
+ Ito32 (dev->x_tl, &window[14]);
+ Ito32 (dev->y_tl, &window[18]);
+
+ /* Width and length */
+ Ito32 (dev->width, &window[22]);
+ Ito32 (dev->length, &window[26]);
+
+ /* Image Composition */
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ window[33] = 0x00;
+ i = get_string_list_index (dither_list, dev->val[OPT_DITHER].s);
+ window[36] = dither_val[i];
+ break;
+ case TECO_GRAYSCALE:
+ window[33] = 0x02;
+ break;
+ case TECO_COLOR:
+ window[33] = 0x05;
+ break;
+ }
+
+ /* Depth */
+ window[34] = dev->depth;
+
+ /* Unknown - invariants */
+ window[31] = 0x80;
+ window[37] = 0x80;
+ window[55] = 0x80;
+ window[57] = 0x80;
+ window[59] = 0x80;
+ window[61] = 0x80;
+ window[65] = 0x80;
+ window[67] = 0x80;
+ window[69] = 0x80;
+ window[71] = 0x80;
+ window[73] = 0x80;
+ window[75] = 0x80;
+ window[77] = 0x80;
+ window[79] = 0x80;
+ window[85] = 0xff;
+ window[89] = 0xff;
+ window[93] = 0xff;
+ window[97] = 0xff;
+
+ hexdump (DBG_info2, "windows", window, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ window, size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_set_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Return the number of byte that can be read. */
+static SANE_Status
+get_filled_data_length (Teco_Scanner * dev, size_t * to_read)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "get_filled_data_length: enter\n");
+
+ *to_read = 0;
+
+ size = 0x12;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (size < 0x10)
+ {
+ DBG (DBG_error,
+ "get_filled_data_length: not enough data returned (%ld)\n",
+ (long) size);
+ }
+
+ hexdump (DBG_info2, "get_filled_data_length return", dev->buffer, size);
+
+ *to_read = B24TOI (&dev->buffer[9]);
+
+ DBG (DBG_info, "%d %d - %d %d\n",
+ dev->params.lines, B16TOI (&dev->buffer[12]),
+ dev->params.bytes_per_line, B16TOI (&dev->buffer[14]));
+
+ if (dev->real_bytes_left == 0)
+ {
+ /* Beginning of a scan. */
+ dev->params.lines = B16TOI (&dev->buffer[12]);
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ dev->params.bytes_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.pixels_per_line = dev->params.bytes_per_line * 8;
+ break;
+
+ case TECO_GRAYSCALE:
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ break;
+
+ case TECO_COLOR:
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ if (dev->def->pass == 3)
+ {
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ }
+ else
+ {
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ }
+ break;
+ }
+ }
+
+ DBG (DBG_info, "get_filled_data_length: to read = %ld\n", (long) *to_read);
+
+ DBG (DBG_proc, "get_filled_data_length: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Start a scan. */
+static SANE_Status
+teco_scan (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "teco_scan: enter\n");
+
+ MKSCSI_SCAN (cdb);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "teco_scan: exit, status=%d\n", status);
+
+ return status;
+}
+
+#if 0
+/* Do some vendor specific stuff. */
+static SANE_Status
+teco_vendor_spec (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+
+ DBG (DBG_proc, "teco_vendor_spec: enter\n");
+
+ size = 0x7800;
+
+ cdb.data[0] = 0x09;
+ cdb.data[1] = 0;
+ cdb.data[2] = 0;
+ cdb.data[3] = (size >> 8) & 0xff;
+ cdb.data[4] = (size >> 0) & 0xff;
+ cdb.data[5] = 0;
+ cdb.len = 6;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ /*hexdump (DBG_info2, "calibration:", dev->buffer, size); */
+
+ cdb.data[0] = 0x0E;
+ cdb.data[1] = 0;
+ cdb.data[2] = 0;
+ cdb.data[3] = 0;
+ cdb.data[4] = 0;
+ cdb.data[5] = 0;
+ cdb.len = 6;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ return status;
+}
+#endif
+
+/* Send the gamma
+ * The order is RGB. The last color is unused.
+ * G is also the gray gamma (if gray scan).
+ *
+ * Some scanner have 4 tables of 256 bytes, and some 4 tables of 1024 bytes.
+ */
+static SANE_Status
+teco_send_gamma (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ struct
+ {
+ unsigned char gamma[4 * MAX_GAMMA_LENGTH];
+ }
+ param;
+ size_t i;
+ size_t size;
+
+ DBG (DBG_proc, "teco_send_gamma: enter\n");
+
+ size = 4 * GAMMA_LENGTH;
+ MKSCSI_SEND_10 (cdb, 0x03, 0x02, size);
+
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* Use the custom gamma. */
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ /* Gray */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma[0 * GAMMA_LENGTH + i] = 0;
+ param.gamma[1 * GAMMA_LENGTH + i] = dev->gamma_GRAY[i];
+ param.gamma[2 * GAMMA_LENGTH + i] = 0;
+ param.gamma[3 * GAMMA_LENGTH + i] = 0;
+ }
+ }
+ else
+ {
+ /* Color */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma[0 * GAMMA_LENGTH + i] = dev->gamma_R[i];
+ param.gamma[1 * GAMMA_LENGTH + i] = dev->gamma_G[i];
+ param.gamma[2 * GAMMA_LENGTH + i] = dev->gamma_B[i];
+ param.gamma[3 * GAMMA_LENGTH + i] = 0;
+ }
+ }
+ }
+ else
+ {
+ if (dev->scan_mode == TECO_BW)
+ {
+ /* Map threshold from a 0..255 scale to a
+ * 0..GAMMA_LENGTH scale. */
+ unsigned int threshold =
+ dev->val[OPT_THRESHOLD].w * (GAMMA_LENGTH / 256);
+
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma[0 * GAMMA_LENGTH + i] = 0;
+ if (i < threshold)
+ param.gamma[1 * GAMMA_LENGTH + i] = 0;
+ else
+ param.gamma[1 * GAMMA_LENGTH + i] = 255;
+ param.gamma[2 * GAMMA_LENGTH + i] = 0;
+ param.gamma[3 * GAMMA_LENGTH + i] = 0;
+ }
+ }
+ else
+ {
+
+ /*
+ * Shift is 1 for GAMMA_LENGTH == 256
+ * and 4 for GAMMA_LENGTH == 1024
+ */
+ int shift = GAMMA_LENGTH >> 8;
+
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma[0 * GAMMA_LENGTH + i] = i / shift;
+ param.gamma[1 * GAMMA_LENGTH + i] = i / shift;
+ param.gamma[2 * GAMMA_LENGTH + i] = i / shift;
+ param.gamma[3 * GAMMA_LENGTH + i] = 0;
+ }
+ }
+ }
+
+ hexdump (DBG_info2, "teco_send_gamma:", cdb.data, cdb.len);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ &param, size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_send_gamma: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Attach a scanner to this backend. */
+static SANE_Status
+attach_scanner (const char *devicename, Teco_Scanner ** devp)
+{
+ Teco_Scanner *dev;
+ int sfd;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new scanner entry. */
+ dev = teco_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+ if (sanei_scsi_open (devicename, &sfd, teco_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_scanner: open failed\n");
+ teco_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Now, check that it is a scanner we support. */
+ if (teco_identify_scanner (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ teco_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Get the page 0x82. It doesn't appear to be usefull yet. */
+ teco_get_inquiry_82 (dev);
+
+ teco_close (dev);
+
+ /* Set the default options for that scanner. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->def->real_vendor;
+ dev->sane.model = dev->def->real_product;
+ dev->sane.type = "flatbed scanner";
+
+ /* Link the scanner with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_scanner: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that scanner. */
+static void
+teco_init_options (Teco_Scanner * dev)
+{
+ int i;
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RESOLUTION].constraint.range = &dev->def->res_range;
+ dev->val[OPT_RESOLUTION].w = 100;
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &x_range;
+ dev->val[OPT_TL_X].w = x_range.min;
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &y_range;
+ dev->val[OPT_TL_Y].w = y_range.min;
+
+ /* Bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &x_range;
+ dev->val[OPT_BR_X].w = x_range.max;
+
+ /* Bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &y_range;
+ dev->val[OPT_BR_Y].w = y_range.max;
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Halftone pattern */
+ dev->opt[OPT_DITHER].name = "dither";
+ dev->opt[OPT_DITHER].title = SANE_I18N ("Dither");
+ dev->opt[OPT_DITHER].desc = SANE_I18N ("Dither");
+ dev->opt[OPT_DITHER].type = SANE_TYPE_STRING;
+ dev->opt[OPT_DITHER].size = max_string_size (dither_list);
+ dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_DITHER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_DITHER].constraint.string_list = dither_list;
+ dev->val[OPT_DITHER].s = strdup (dither_list[0]);
+
+ /* custom-gamma table */
+ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R;
+
+ /* green and gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G;
+
+ /* blue gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B;
+
+ /* grayscale gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_GRAY].wa = dev->gamma_GRAY;
+
+ /* Threshold */
+ dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_THRESHOLD].size = sizeof (SANE_Int);
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_THRESHOLD].constraint.range = &threshold_range;
+ dev->val[OPT_THRESHOLD].w = 128;
+
+ /* preview */
+ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scan_mode_list[0], NULL);
+}
+
+/*
+ * Wait until the scanner is ready.
+ */
+static SANE_Status
+teco_wait_scanner (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ int timeout;
+ CDB cdb;
+
+ DBG (DBG_proc, "teco_wait_scanner: enter\n");
+
+ MKSCSI_TEST_UNIT_READY (cdb);
+
+ /* Set the timeout to 60 seconds. */
+ timeout = 60;
+
+ while (timeout > 0)
+ {
+
+ /* test unit ready */
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, NULL, NULL);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ };
+
+ DBG (DBG_proc, "teco_wait_scanner: scanner not ready\n");
+ return (SANE_STATUS_IO_ERROR);
+}
+
+/* Read the image from the scanner and fill the temporary buffer with it. */
+static SANE_Status
+teco_fill_image (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+ unsigned char *image;
+
+ DBG (DBG_proc, "teco_fill_image: enter\n");
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ while (dev->real_bytes_left)
+ {
+ /*
+ * Try to read the maximum number of bytes.
+ */
+ size = 0;
+ while (size == 0)
+ {
+ status = get_filled_data_length (dev, &size);
+ if (status)
+ return (status);
+ if (size == 0)
+ usleep (100000); /* sleep 1/10th of second */
+ }
+
+ if (size > dev->real_bytes_left)
+ size = dev->real_bytes_left;
+ if (size > dev->image_size - dev->image_end)
+ size = dev->image_size - dev->image_end;
+
+ /* Always read a multiple of a line. */
+ size = size - (size % dev->params.bytes_per_line);
+
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_info, "teco_fill_image: to read = %ld bytes (bpl=%d)\n",
+ (long) size, dev->params.bytes_per_line);
+
+ MKSCSI_READ_10 (cdb, 0, 0, size);
+
+ hexdump (DBG_info2, "teco_fill_image: READ_10 CDB", cdb.data, 10);
+
+ image = dev->image + dev->image_end;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, image, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "teco_fill_image: cannot read from the scanner\n");
+ return status;
+ }
+
+ /* The size this scanner returns is always a multiple of lines. */
+ assert ((size % dev->params.bytes_per_line) == 0);
+
+ DBG (DBG_info, "teco_fill_image: real bytes left = %ld\n",
+ (long) dev->real_bytes_left);
+
+ if (dev->scan_mode == TECO_COLOR)
+ {
+ if (dev->def->pass == 1)
+ {
+
+ /* Reorder the lines. The scanner gives color by color for
+ * each line. */
+ unsigned char *src = image;
+ int nb_lines = size / dev->params.bytes_per_line;
+ int i, j;
+
+ for (i = 0; i < nb_lines; i++)
+ {
+
+ unsigned char *dest = dev->buffer;
+
+ for (j = 0; j < dev->params.pixels_per_line; j++)
+ {
+ *dest = src[j + 0 * dev->params.pixels_per_line];
+ dest++;
+ *dest = src[j + 1 * dev->params.pixels_per_line];
+ dest++;
+ *dest = src[j + 2 * dev->params.pixels_per_line];
+ dest++;
+ }
+
+ /* Copy the line back. */
+ memcpy (src, dev->buffer, dev->params.bytes_per_line);
+
+ src += dev->params.bytes_per_line;
+ }
+ }
+ }
+
+ dev->image_end += size;
+ dev->real_bytes_left -= size;
+ }
+
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+/* Copy from the raw buffer to the buffer given by the backend.
+ *
+ * len in input is the maximum length available in buf, and, in
+ * output, is the length written into buf.
+ */
+static void
+teco_copy_raw_to_frontend (Teco_Scanner * dev, SANE_Byte * buf, size_t * len)
+{
+ size_t size;
+
+ size = dev->image_end - dev->image_begin;
+ if (size > *len)
+ {
+ size = *len;
+ }
+ *len = size;
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ {
+ /* Invert black and white. */
+ unsigned char *src = dev->image + dev->image_begin;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ {
+ *buf = *src ^ 0xff;
+ src++;
+ buf++;
+ }
+ }
+ break;
+
+ case TECO_GRAYSCALE:
+ case TECO_COLOR:
+ memcpy (buf, dev->image + dev->image_begin, size);
+ break;
+ }
+
+ dev->image_begin += size;
+}
+
+/* Stop a scan. */
+static SANE_Status
+do_cancel (Teco_Scanner * dev)
+{
+ DBG (DBG_sane_proc, "do_cancel enter\n");
+
+ if (dev->scanning == SANE_TRUE)
+ {
+
+ /* Reset the scanner */
+ dev->x_resolution = 300;
+ dev->y_resolution = 300;
+ dev->x_tl = 0;
+ dev->y_tl = 0;
+ dev->width = 0;
+ dev->length = 0;
+
+ teco_set_window (dev);
+
+ teco_scan (dev);
+
+ teco_close (dev);
+ }
+
+ dev->scanning = SANE_FALSE;
+
+ DBG (DBG_sane_proc, "do_cancel exit\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Sane entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ char dev_name[PATH_MAX];
+ size_t len;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ DBG (DBG_error, "This is sane-teco1 version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2002 by Frank Zago\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ fp = sanei_config_open (TECO_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach_scanner ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Teco_Scanner *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Teco_Scanner *dev;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No scanner found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ teco_init_options (dev);
+
+ /* Initialize the gamma table. */
+ {
+ /*
+ * Shift is 1 for GAMMA_LENGTH == 256
+ * and 4 for GAMMA_LENGTH == 1024
+ */
+ int shift = GAMMA_LENGTH >> 8;
+ size_t i;
+
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ dev->gamma_R[i] = i / shift;
+ dev->gamma_G[i] = i / shift;
+ dev->gamma_B[i] = i / shift;
+ dev->gamma_GRAY[i] = i / shift;
+ }
+ }
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Teco_Scanner *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ name = dev->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_THRESHOLD:
+ case OPT_PREVIEW:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ case OPT_DITHER:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* Gamma */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (val, dev->val[option].wa, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Numeric side-effect options */
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_RESOLUTION:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Numeric side-effect free options */
+ case OPT_THRESHOLD:
+ case OPT_PREVIEW:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect free options */
+ case OPT_DITHER:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect options */
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, BLACK_WHITE_STR) == 0)
+ {
+ dev->depth = 8;
+ dev->scan_mode = TECO_BW;
+ dev->opt[OPT_DITHER].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, GRAY_STR) == 0)
+ {
+ dev->scan_mode = TECO_GRAYSCALE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ dev->depth = 8;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_STR) == 0)
+ {
+ dev->scan_mode = TECO_COLOR;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ dev->depth = 8;
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (dev->val[option].wa, val, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* use custom_gamma_table */
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Setup the parameters for the scan. These values will be re-used
+ * in the SET WINDOWS command. */
+ if (dev->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ dev->x_resolution = 22;
+ dev->y_resolution = 22;
+ dev->x_tl = 0;
+ dev->y_tl = 0;
+ dev->x_br = mmToIlu (SANE_UNFIX (x_range.max));
+ dev->y_br = mmToIlu (SANE_UNFIX (y_range.max));
+ }
+ else
+ {
+ dev->x_resolution = dev->val[OPT_RESOLUTION].w;
+ dev->y_resolution = dev->val[OPT_RESOLUTION].w;
+ if (dev->x_resolution > dev->def->x_resolution_max)
+ {
+ dev->x_resolution = dev->def->x_resolution_max;
+ }
+
+ dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ }
+
+ /* Check the corners are OK. */
+ if (dev->x_tl > dev->x_br)
+ {
+ int s;
+ s = dev->x_tl;
+ dev->x_tl = dev->x_br;
+ dev->x_br = s;
+ }
+ if (dev->y_tl > dev->y_br)
+ {
+ int s;
+ s = dev->y_tl;
+ dev->y_tl = dev->y_br;
+ dev->y_br = s;
+ }
+
+ dev->width = dev->x_br - dev->x_tl;
+ dev->length = dev->y_br - dev->y_tl;
+
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.last_frame = SANE_TRUE;
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / 300) & ~0x7;
+ dev->params.bytes_per_line = dev->params.pixels_per_line / 8;
+ dev->params.depth = 1;
+ dev->pass = 1;
+ break;
+ case TECO_GRAYSCALE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / 300);
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ dev->params.depth = 8;
+ dev->pass = 1;
+ break;
+ case TECO_COLOR:
+ dev->params.format = SANE_FRAME_RGB;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / 300);
+ dev->pass = dev->def->pass;
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->params.depth = 8;
+ break;
+ }
+
+ dev->params.lines = (dev->length * dev->y_resolution) / 300;
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+ SANE_Status status;
+ size_t size;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Open again the scanner. */
+ if (sanei_scsi_open
+ (dev->devicename, &(dev->sfd), teco_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Set the correct parameters. */
+ sane_get_parameters (dev, NULL);
+
+ /* The scanner must be ready. */
+ status = teco_wait_scanner (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_mode_select (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ if (dev->scan_mode == TECO_COLOR)
+ {
+ dev->pass = dev->def->pass;
+ }
+ else
+ {
+ dev->pass = 1;
+ }
+
+ if (dev->def->tecoref != TECO_VM3510)
+ {
+ status = teco_set_window (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ dev->real_bytes_left = 0;
+ status = get_filled_data_length (dev, &size);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ }
+
+#if 0
+ /* The windows driver does that, but some scanners don't like it. */
+ teco_vendor_spec (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+#endif
+
+ status = teco_send_gamma (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_set_window (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_scan (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ if (dev->def->tecoref == TECO_VM3510)
+ {
+ dev->real_bytes_left = 0;
+ status = get_filled_data_length (dev, &size);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ }
+ }
+ else
+ {
+ /* Scan has already started. */
+ dev->pass--;
+ }
+
+ /* Set the frame parameter. */
+ if (dev->scan_mode == TECO_COLOR && dev->def->pass > 1)
+ {
+ SANE_Frame frames[] = { 0, SANE_FRAME_BLUE,
+ SANE_FRAME_GREEN, SANE_FRAME_RED
+ };
+ dev->params.format = frames[dev->pass];
+ }
+
+ /* Is it the last frame? */
+ if (dev->pass > 1)
+ {
+ dev->params.last_frame = SANE_FALSE;
+ }
+ else
+ {
+ dev->params.last_frame = SANE_TRUE;
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+ dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Teco_Scanner *dev = handle;
+ size_t size;
+ int buf_offset; /* offset into buf */
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning */
+ return do_cancel (dev);
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ buf_offset = 0;
+
+ do
+ {
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = teco_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Copy the data to the frontend buffer. */
+ size = max_len - buf_offset;
+ if (size > dev->bytes_left)
+ {
+ size = dev->bytes_left;
+ }
+ teco_copy_raw_to_frontend (dev, buf + buf_offset, &size);
+
+ buf_offset += size;
+
+ dev->bytes_left -= size;
+ *len += size;
+
+ }
+ while ((buf_offset != max_len) && dev->bytes_left);
+
+ DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n",
+ (long) dev->bytes_left);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ SANE_Status status;
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ if (dev->scanning == SANE_FALSE)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (non_blocking == SANE_FALSE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ *fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ do_cancel (dev);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+ Teco_Scanner *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+ do_cancel (dev);
+ teco_close (dev);
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ teco_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/teco1.conf.in b/backend/teco1.conf.in
new file mode 100644
index 0000000..3f7fd72
--- /dev/null
+++ b/backend/teco1.conf.in
@@ -0,0 +1,17 @@
+# Relisys RELI 2412
+# The scanner respond to all luns. Get only lun 0.
+scsi "RELISYS" "VM3530+" Scanner * * * 0
+
+# Avec Color Office 2400, 2412
+scsi "" "Image Scanner" Scanner
+
+# Relisys RELI 4816
+scsi "RELISYS" "VM4540" Scanner * * * 0
+
+# Relisys RELI 4830
+scsi "RELISYS" "RELI 4830" Scanner
+
+# Dextra DF-600P
+scsi "Dextra DF-600P" Scanner
+
+/dev/scanner
diff --git a/backend/teco1.h b/backend/teco1.h
new file mode 100644
index 0000000..1b5fecb
--- /dev/null
+++ b/backend/teco1.h
@@ -0,0 +1,399 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+*/
+
+/* Commands supported by the scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_VENDOR_09 0x09
+#define SCSI_VENDOR_0E 0x0E
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT 0x15
+#define SCSI_SCAN 0x1b
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_SEND_10 0x2a
+#define SCSI_READ_10 0x28
+#define SCSI_GET_DATA_BUFFER_STATUS 0x34
+
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+}
+CDB;
+
+
+/* Set a specific bit depending on a boolean.
+ * MKSCSI_BIT(TRUE, 3) will generate 0x08. */
+#define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0)
+
+/* Set a value in a range of bits.
+ * MKSCSI_I2B(5, 3, 5) will generate 0x28 */
+#define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1))
+
+/* Store an integer in 2, 3 or 4 byte in an array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+#define MKSCSI_GET_DATA_BUFFER_STATUS(cdb, wait, buflen) \
+ cdb.data[0] = SCSI_GET_DATA_BUFFER_STATUS; \
+ cdb.data[1] = MKSCSI_BIT(wait, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_INQUIRY(cdb, buflen) \
+ cdb.data[0] = SCSI_INQUIRY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_MODE_SELECT(cdb, pf, sp, buflen) \
+ cdb.data[0] = SCSI_MODE_SELECT; \
+ cdb.data[1] = MKSCSI_BIT(pf, 4) | MKSCSI_BIT(sp, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SET_WINDOW(cdb, buflen) \
+ cdb.data[0] = SCSI_SET_WINDOW; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_READ_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_REQUEST_SENSE(cdb, buflen) \
+ cdb.data[0] = SCSI_REQUEST_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (buflen); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SCAN(cdb) \
+ cdb.data[0] = SCSI_SCAN; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SEND_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_SEND_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_TEST_UNIT_READY(cdb) \
+ cdb.data[0] = SCSI_TEST_UNIT_READY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+/*--------------------------------------------------------------------------*/
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7)
+
+/*--------------------------------------------------------------------------*/
+
+#define mmToIlu(mm) (((mm) * dev->def->x_resolution_max) / MM_PER_INCH)
+#define iluToMm(ilu) (((ilu) * MM_PER_INCH) / dev->def->x_resolution_max)
+
+/*--------------------------------------------------------------------------*/
+
+#define MAX_GAMMA_LENGTH 0x400 /* maximum number of value per color */
+#define GAMMA_LENGTH (dev->def->num_gamma_color) /* number of value per color */
+/*--------------------------------------------------------------------------*/
+
+enum Teco_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CUSTOM_GAMMA, /* Use the custom gamma tables */
+ OPT_GAMMA_VECTOR_R, /* Custom Red gamma table */
+ OPT_GAMMA_VECTOR_G, /* Custom Green Gamma table */
+ OPT_GAMMA_VECTOR_B, /* Custom Blue Gamma table */
+ OPT_GAMMA_VECTOR_GRAY, /* Custom Grayscale Gamma table */
+ OPT_DITHER,
+ OPT_THRESHOLD,
+ OPT_PREVIEW,
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Scanner supported by this backend.
+ */
+struct scanners_supported
+{
+ int scsi_type;
+ char scsi_teco_name[12]; /* real name of the scanner */
+ enum
+ {
+ TECO_VM3510,
+ TECO_VM3520,
+ TECO_VM353A,
+ TECO_VM4540,
+ TECO_VM4542
+ }
+ tecoref;
+ char *real_vendor; /* brand on the box */
+ char *real_product; /* name on the box */
+
+ SANE_Range res_range;
+
+ int x_resolution_max; /* maximum X dpi */
+ int y_resolution_max; /* maximum Y dpi */
+
+ int pass; /* number of passes in color mode */
+
+ size_t num_gamma_color; /* number of value per color */
+
+ int window_size; /* size of the SCAN window */
+
+};
+
+/*--------------------------------------------------------------------------*/
+
+#define BLACK_WHITE_STR SANE_VALUE_SCAN_MODE_LINEART
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+/*--------------------------------------------------------------------------*/
+
+/* Define a scanner occurence. */
+typedef struct Teco_Scanner
+{
+ struct Teco_Scanner *next;
+ SANE_Device sane;
+
+ char *devicename;
+ int sfd; /* device handle */
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+ char scsi_version[5];
+ char scsi_teco_name[12]; /* real name of the scanner */
+
+ /* SCSI handling */
+ size_t buffer_size; /* size of the buffer */
+ SANE_Byte *buffer; /* for SCSI transfer. */
+
+ /* Scanner infos. */
+ const struct scanners_supported *def; /* default options for that scanner */
+
+ /* Scanning handling. */
+ int scanning; /* TRUE if a scan is running. */
+ int x_resolution; /* X resolution in DPI */
+ int y_resolution; /* Y resolution in DPI */
+ int x_tl; /* X top left */
+ int y_tl; /* Y top left */
+ int x_br; /* X bottom right */
+ int y_br; /* Y bottom right */
+ int width; /* width of the scan area in mm */
+ int length; /* length of the scan area in mm */
+ int pass; /* current pass number */
+
+ enum
+ {
+ TECO_BW,
+ TECO_GRAYSCALE,
+ TECO_COLOR
+ }
+ scan_mode;
+
+ int depth; /* depth per color */
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+
+ size_t real_bytes_left; /* number of bytes left the scanner will return. */
+
+ SANE_Byte *image; /* keep the raw image here */
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+
+ SANE_Parameters params;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+
+ /* Gamma table. 1 array per colour. */
+ SANE_Word gamma_GRAY[MAX_GAMMA_LENGTH];
+ SANE_Word gamma_R[MAX_GAMMA_LENGTH];
+ SANE_Word gamma_G[MAX_GAMMA_LENGTH];
+ SANE_Word gamma_B[MAX_GAMMA_LENGTH];
+}
+Teco_Scanner;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
+
+#define B24TOI(buf) \
+ ((((unsigned char *)buf)[0] << 16) | \
+ (((unsigned char *)buf)[1] << 8) | \
+ (((unsigned char *)buf)[2] << 0))
+
+#define B16TOI(buf) \
+ ((((unsigned char *)buf)[0] << 8) | \
+ (((unsigned char *)buf)[1] << 0))
diff --git a/backend/teco2.c b/backend/teco2.c
new file mode 100644
index 0000000..9bc8bf4
--- /dev/null
+++ b/backend/teco2.c
@@ -0,0 +1,3455 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002-2003 Frank Zago (sane at zago dot net)
+ Copyright (C) 2003-2008 Gerard Klaver (gerard at gkall dot hobby dot nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ TECO scanner VM3575, VM656A, VM6575, VM6586, VM356A, VM3564
+ update 2003/02/14, Patch for VM356A Gerard Klaver
+ update 2003/03/19, traces, tests VM356A Gerard Klaver, Michael Hoeller
+ update 2003/07/19, white level calibration, color modes VM3564, VM356A, VM3575
+ Gerard Klaver, Michael Hoeller
+ update 2004/01/15, white level , red, green, and blue calibration and
+ leave out highest and lowest value and then divide
+ VM3564, VM356A and VM3575: Gerard Klaver
+ update 2004/08/04, white level, red, green and blue calibration for the VM6575
+ changed default SANE_TECO_CAL_ALGO to 1 for VM3564 and VM6575
+ preview value changed to 75 dpi for VM6575
+ leave out highest and lowest value and then divide
+ VM656A, VM6586
+ update 2004/08/05, use of SANE_VALUE_SCAN_MODE_LINEART, _GRAY, and _COLOR,
+ changed use of %d to %ld (when bytes values are displayed)
+ update 2005/03/04, use of __sane_unused__
+ update 2005/07/29. Removed using teco_request_sense (dev) routine for VM3564
+ update 2008/01/12, Update teco_request_sense routine due to no
+ init value for size.
+*/
+
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 10 /* 2008/01/12 */
+#define BACKEND_NAME teco2
+#define TECO2_CONFIG_FILE "teco2.conf"
+
+/*--------------------------------------------------------------------------*/
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "teco2.h"
+
+/* This is used to debug the backend without having the real hardware. */
+#undef sim
+#ifdef sim
+#define sanei_scsi_cmd2(a, b, c, d, e, f, g) SANE_STATUS_GOOD
+#define sanei_scsi_open(a, b, c, d) 0
+#define sanei_scsi_cmd(a, b, c, d, e) SANE_STATUS_GOOD
+#define sanei_scsi_close(a) SANE_STATUS_GOOD
+#endif
+
+/* For debugging purposes: output a stream straight out from the
+ * scanner without reordering the colors, 0=normal, 1 = raw. */
+static int raw_output = 0;
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of color dropout. */
+static SANE_String_Const filter_color_list[] = {
+ "Red",
+ "Green",
+ "Blue",
+ NULL
+};
+static const int filter_color_val[] = {
+ 0,
+ 1,
+ 2
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of dithering options. */
+static SANE_String_Const dither_list[] = {
+ "Line art",
+ "2x2",
+ "3x3",
+ "4x4 bayer",
+ "4x4 smooth",
+ "8x8 bayer",
+ "8x8 smooth",
+ "8x8 horizontal",
+ "8x8 vertical",
+ NULL
+};
+static const int dither_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range threshold_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range red_level_range = {
+ 0, /* minimum */
+ 64, /* maximum */
+ 1 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range green_level_range = {
+ 0, /* minimum */
+ 64, /* maximum */
+ 1 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range blue_level_range = {
+ 0, /* minimum */
+ 64, /* maximum */
+ 1 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Gamma range */
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* 255 maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const struct dpi_color_adjust vm3564_dpi_color_adjust[] = {
+
+ /*dpi, color sequence R G or B, 0 (-) or 1 (+) color skewing, lines skewing */
+ {25, 1, 0, 2, 0, 0},
+ {50, 1, 2, 0, 0, 1},
+ {75, 1, 0, 2, 1, 1},
+ {150, 1, 0, 2, 1, 2},
+ {225, 1, 0, 2, 1, 3},
+ {300, 1, 0, 2, 1, 4},
+ {375, 1, 0, 2, 1, 5},
+ {450, 1, 0, 2, 1, 6},
+ {525, 1, 0, 2, 1, 7},
+ {600, 1, 0, 2, 1, 8},
+ /* must be the last entry */
+ {0, 0, 0, 0, 0, 0}
+};
+
+/* Used for VM356A only */
+
+static const struct dpi_color_adjust vm356a_dpi_color_adjust[] = {
+
+ /* All values with ydpi > 300 result in a wrong proportion for */
+ /* the scan. The proportion can be adjusted with the following */
+ /* command: convert -geometry (dpi/max_xdpi * 100%)x100% */
+ /* max_xdpi is for the vm356a constant with 300 dpi */
+ /* e.g. 600dpi adjust with: convert -geometry 200%x100% */
+
+ /* All resolutions in increments of 25 are testede, only the shown */
+ /* bring back good results */
+
+ /*dpi, color sequence R G or B, 0 (-) or 1 (+) color skewing, lines skewing */
+ {25, 1, 0, 2, 0, 0},
+ {50, 1, 2, 0, 0, 1},
+ {75, 1, 0, 2, 1, 1},
+ {150, 1, 0, 2, 1, 2},
+ {225, 1, 0, 2, 1, 3},
+ {300, 1, 0, 2, 1, 4},
+ {375, 1, 0, 2, 1, 5},
+ {450, 1, 0, 2, 1, 6},
+ {525, 1, 0, 2, 1, 7},
+ {600, 1, 0, 2, 1, 8},
+
+ /* must be the last entry */
+ {0, 0, 0, 0, 0, 0}
+};
+
+/* Used for the VM3575 only */
+
+static const struct dpi_color_adjust vm3575_dpi_color_adjust[] = {
+
+ /* All values with ydpi > 300 result in a wrong proportion for */
+ /* the scan. The proportion can be adjusted with the following */
+ /* command: convert -geometry (dpi/max_xdpi * 100%)x100% */
+ /* max_xdpi is for the vm3575 constant with 300 dpi */
+ /* e.g. 600dpi adjust with: convert -geometry 200%x100% */
+
+ {50, 2, 0, 1, 1, 1},
+ {60, 2, 1, 0, 0, 2},
+/* {75, 2, 0, 1, 1, 2}, NOK, effects in scan, also with twain driver*/
+ {100, 2, 1, 0, 0, 3},
+ {120, 2, 0, 1, 1, 3},
+ {150, 2, 0, 1, 1, 4},
+/* {180, 2, 1, 0, 0, 4}, NOK, skewing problem*/
+ {225, 2, 0, 1, 1, 6},
+ {300, 2, 0, 1, 1, 8},
+ {375, 2, 0, 1, 1, 10},
+ {450, 2, 0, 1, 1, 12},
+ {525, 2, 0, 1, 1, 14},
+ {600, 2, 0, 1, 1, 16},
+
+ /* must be the last entry */
+ {0, 0, 0, 0, 0, 0}
+};
+
+static const struct dpi_color_adjust vm656a_dpi_color_adjust[1] = {
+ {0, 0, 1, 2, 0, 0}
+};
+
+static const struct dpi_color_adjust vm6575_dpi_color_adjust[] = {
+ {75, 1, 0, 2, 1, 1},
+ {150, 1, 0, 2, 1, 2},
+ {300, 1, 0, 2, 1, 4},
+ {600, 1, 0, 2, 1, 8},
+};
+
+static const struct dpi_color_adjust vm6586_dpi_color_adjust[] = {
+ {25, 1, 0, 2, 1, 0},
+ {40, 1, 0, 2, 1, 0},
+ {50, 1, 0, 2, 1, 0},
+ {75, 1, 2, 0, 0, 1},
+ {150, 1, 0, 2, 1, 1},
+ {160, 1, 0, 2, 1, 1},
+ {175, 1, 0, 2, 1, 1},
+ {180, 1, 0, 2, 1, 1},
+ {300, 1, 0, 2, 1, 2},
+ {320, 1, 0, 2, 1, 2},
+ {325, 1, 0, 2, 1, 2},
+ {450, 1, 0, 2, 1, 3},
+ {460, 1, 0, 2, 1, 3},
+ {600, 1, 0, 2, 1, 4},
+ {620, 1, 0, 2, 1, 6},
+ {625, 1, 0, 2, 1, 6},
+ {750, 1, 0, 2, 1, 12},
+ {760, 1, 0, 2, 1, 12},
+ {900, 1, 0, 2, 1, 12},
+ {1050, 1, 0, 2, 1, 15},
+ {1200, 1, 0, 2, 1, 15},
+
+ /* must be the last entry */
+ {0, 0, 0, 0, 0, 0}
+};
+
+static const struct dpi_color_adjust default_dpi_color_adjust[1] = {
+ {0, 0, 1, 2, 0, 0}
+};
+
+/* For all scanners. Must be reasonable (eg. between 50 and 300) and
+ * appear in the ...._dpi_color_adjust list of all supported scanners. */
+#define DEF_RESOLUTION 150
+
+/*--------------------------------------------------------------------------*/
+
+/* Define the supported scanners and their characteristics. */
+static const struct scanners_supported scanners[] = {
+
+ {6, "TECO VM3564",
+ TECO_VM3564,
+ "Relisys", "AVEC II S3",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y resolution */
+ 2550, 12, 3, 1, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm3564_dpi_color_adjust},
+
+ {6, "TECO VM356A",
+ TECO_VM356A,
+ "Relisys", "APOLLO Express 3",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y resolution */
+ 2550, 12, 3, 1, /* calibration */
+ /* dots/inch * x-length, calibration samples, tot.bytes for 3 colors, default SANE_TECO2_CAL_ALGO value */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm356a_dpi_color_adjust},
+
+ {6, "TECO VM356A",
+ TECO_VM356A,
+ "Primax", "Jewel 4800",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y resolution */
+ 2550, 12, 3, 1, /* calibration */
+ /* dots/inch * x-length, calibration samples, tot.bytes for 3 colors, default SANE_TECO2_CAL_ALGO value */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm356a_dpi_color_adjust},
+
+ {6, "TECO VM3575",
+ TECO_VM3575,
+ "Relisys", "SCORPIO Super 3",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y resolution */
+ 2550, 12, 6, 1, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm3575_dpi_color_adjust},
+
+ {6, "TECO VM3575",
+ TECO_VM3575,
+ "Relisys", "AVEC Super 3",
+ {1, 600, 1}, /* resolution */
+ 300, 600, /* max x and Y resolution */
+ 2550, 12, 6, 1, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm3575_dpi_color_adjust},
+
+ {6, "TECO VM656A",
+ TECO_VM656A,
+ "Relisys", "APOLLO Express 6",
+ {1, 600, 1}, /* resolution */
+ 600, 1200, /* max x and Y resolution */
+ 5100, 8, 6, 0, /* calibration */
+ {SANE_FIX (0), SANE_FIX (210), 0},
+ {SANE_FIX (0), SANE_FIX (297), 0},
+ vm656a_dpi_color_adjust},
+
+ {6, "TECO VM6575",
+ TECO_VM6575,
+ "Relisys", "SCORPIO Pro",
+ {1, 600, 1}, /* resolution */
+ 600, 1200, /* max x and Y resolution */
+ 5100, 8, 6, 1, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm6575_dpi_color_adjust},
+
+ {6, "TECO VM6575",
+ TECO_VM6575,
+ "Primax", "Profi 9600",
+ {1, 600, 1}, /* resolution */
+ 600, 1200, /* max x and Y resolution */
+ 5100, 8, 6, 1, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm6575_dpi_color_adjust},
+
+ {6, "TECO VM6586",
+ TECO_VM6586,
+ "Relisys", "SCORPIO Pro-S",
+ {1, 600, 1}, /* resolution */
+ 600, 1200, /* max x and Y resolution */
+ 5100, 8, 6, 0, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm6586_dpi_color_adjust},
+
+ {6, "TECO VM6586",
+ TECO_VM6586,
+ "Primax", "Profi 19200",
+ {1, 600, 1}, /* resolution */
+ 600, 1200, /* max x and Y resolution */
+ 5100, 8, 6, 0, /* calibration */
+ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0},
+ {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0},
+ vm6586_dpi_color_adjust}
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of scanner attached. */
+static Teco_Scanner *first_dev = NULL;
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+
+
+/* Local functions. */
+
+/* Display a buffer in the log. Display by lines of 16 bytes. */
+static void
+hexdump (int level, const char *comment, unsigned char *buf, const int length)
+{
+ int i;
+ char line[128];
+ char *ptr;
+ char asc_buf[17];
+ char *asc_ptr;
+
+ DBG (level, " %s\n", comment);
+
+ i = 0;
+ goto start;
+
+ do
+ {
+ if (i < length)
+ {
+ ptr += sprintf (ptr, " %2.2x", *buf);
+
+ if (*buf >= 32 && *buf <= 127)
+ {
+ asc_ptr += sprintf (asc_ptr, "%c", *buf);
+ }
+ else
+ {
+ asc_ptr += sprintf (asc_ptr, ".");
+ }
+ }
+ else
+ {
+ /* After the length; do nothing. */
+ ptr += sprintf (ptr, " ");
+ }
+
+ i++;
+ buf++;
+
+ if ((i % 16) == 0)
+ {
+ /* It's a new line */
+ DBG (level, " %s %s\n", line, asc_buf);
+
+ start:
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+
+ ptr += sprintf (ptr, " %3.3d:", i);
+ }
+
+ }
+ while (i < ((length + 15) & ~15));
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+get_string_list_index (SANE_String_Const list[], SANE_String_Const name)
+{
+ int index;
+
+ index = 0;
+ while (list[index] != NULL)
+ {
+ if (strcmp (list[index], name) == 0)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "name %s not found in list\n", name);
+
+ assert (0); /* bug in backend, core dump */
+
+ return (-1);
+}
+
+/* Initialize a scanner entry. Return an allocated scanner with some
+ * preset values. */
+static Teco_Scanner *
+teco_init (void)
+{
+ Teco_Scanner *dev;
+
+ DBG (DBG_proc, "teco_init: enter\n");
+
+ /* Allocate a new scanner entry. */
+ dev = malloc (sizeof (Teco_Scanner));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ memset (dev, 0, sizeof (Teco_Scanner));
+
+ /* Allocate the buffer used to transfer the SCSI data. */
+ dev->buffer_size = 64 * 1024;
+ dev->buffer = malloc (dev->buffer_size);
+ if (dev->buffer == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+ dev->sfd = -1;
+
+ DBG (DBG_proc, "teco_init: exit\n");
+
+ return (dev);
+}
+
+/* Closes an open scanner. */
+static void
+teco_close (Teco_Scanner * dev)
+{
+ DBG (DBG_proc, "teco_close: enter\n");
+
+ if (dev->sfd != -1)
+ {
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+ }
+
+ DBG (DBG_proc, "teco_close: exit\n");
+}
+
+/* Frees the memory used by a scanner. */
+static void
+teco_free (Teco_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "teco_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ teco_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+ if (dev->resolutions_list)
+ free (dev->resolutions_list);
+
+ free (dev);
+
+ DBG (DBG_proc, "teco_free: exit\n");
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+teco_identify_scanner (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "teco_identify_scanner: enter\n");
+
+ size = 5;
+ MKSCSI_INQUIRY (cdb, size);
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ size = dev->buffer[4] + 5; /* total length of the inquiry data */
+
+#ifndef sim
+ if (size < 53)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: not enough data to identify device\n");
+ return (SANE_FALSE);
+ }
+#endif
+#ifdef sim
+ {
+#if 0
+ /* vm3564 */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x10, 0x52, 0x45,
+ 0x4c, 0x49, 0x53, 0x59, 0x53, 0x20, 0x41, 0x56, 0x45, 0x43,
+ 0x20, 0x49, 0x49, 0x20, 0x53, 0x33, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x31, 0x2e, 0x30, 0x37, 0x31, 0x2e, 0x30, 0x37,
+ 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x33,
+ 0x35, 0x36, 0x34, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01,
+ 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08,
+ 0x01, 0x00
+ };
+#endif
+#if 0
+ /* vm356A */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x10, 0x50, 0x72,
+ 0x69, 0x6d, 0x61, 0x78, 0x20, 0x20, 0x4a, 0x65, 0x77, 0x65,
+ 0x6c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x31, 0x2e, 0x30, 0x30, 0x31, 0x2e, 0x30, 0x30,
+ 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x33,
+ 0x35, 0x36, 0x41, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01,
+ 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08,
+ 0x10, 0x00
+ };
+#endif
+#if 1
+ /* vm3575 */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x00, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x6c, 0x61, 0x74,
+ 0x62, 0x65, 0x64, 0x20, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65,
+ 0x72, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x31, 0x2e, 0x30, 0x33,
+ 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x33,
+ 0x35, 0x37, 0x35, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01,
+ 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08,
+ 0x01, 0x00
+ };
+#endif
+#if 0
+ /* vm6586 */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x00, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x6c, 0x61, 0x74,
+ 0x62, 0x65, 0x64, 0x20, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65,
+ 0x72, 0x20, 0x33, 0x2e, 0x30, 0x31, 0x33, 0x2e, 0x30, 0x31,
+ 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x36,
+ 0x35, 0x38, 0x36, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01,
+ 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08,
+ 0x01, 0x00
+ };
+#endif
+#if 0
+ /* vm656A */
+ unsigned char table[] = {
+ 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x00, 0x52, 0x45,
+ 0x4c, 0x49, 0x53, 0x59, 0x53, 0x20, 0x41, 0x50, 0x4f, 0x4c,
+ 0x4c, 0x4f, 0x20, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73,
+ 0x20, 0x36, 0x31, 0x2e, 0x30, 0x33, 0x31, 0x2e, 0x30, 0x33,
+ 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x36,
+ 0x35, 0x36, 0x41, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01, 0x02,
+ 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08, 0x01,
+ 0x00, 0x00
+ };
+#endif
+ size = sizeof (table);
+ memcpy (dev->buffer, table, sizeof (table));
+ }
+#endif
+
+ MKSCSI_INQUIRY (cdb, size);
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ hexdump (DBG_info2, "inquiry", dev->buffer, size);
+
+ dev->scsi_type = dev->buffer[0] & 0x1f;
+ memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08);
+ dev->scsi_vendor[0x08] = 0;
+ memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010);
+ dev->scsi_product[0x10] = 0;
+ memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04);
+ dev->scsi_version[0x04] = 0;
+ memcpy (dev->scsi_teco_name, dev->buffer + 0x2A, 0x0B);
+ dev->scsi_teco_name[0x0B] = 0;
+
+ DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\" \"%s\"\n",
+ dev->scsi_vendor, dev->scsi_product, dev->scsi_version,
+ dev->scsi_teco_name);
+
+ /* Lookup through the supported scanners table to find if this
+ * backend supports that one. */
+ for (i = 0; i < NELEMS (scanners); i++)
+ {
+ if (dev->scsi_type == scanners[i].scsi_type &&
+ strcmp (dev->scsi_teco_name, scanners[i].scsi_teco_name) == 0)
+ {
+
+ DBG (DBG_error, "teco_identify_scanner: scanner supported\n");
+
+ /*patch for VM356A Jewel or Apollo text in frontend-+ others-------------- */
+
+ if (strncmp (dev->scsi_vendor, "RELISYS", 1) == 0 ||
+ strncmp (dev->scsi_vendor, " ", 1) == 0)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: scanner detected with this teco_name and first brand/name entry in table\n");
+
+ dev->def = &(scanners[i]);
+
+ return (SANE_TRUE);
+ }
+ else
+ {
+ i++;
+
+ DBG (DBG_error,
+ "teco_identify_scanner: scanner detected with this teco_name and second brand/name entry in table\n");
+
+ dev->def = &(scanners[i]);
+
+ return (SANE_TRUE);
+ }
+
+ }
+ }
+
+ DBG (DBG_proc, "teco_identify_scanner: exit, device not supported\n");
+
+ return (SANE_FALSE);
+}
+
+/* Set a window. */
+static SANE_Status
+teco_set_window (Teco_Scanner * dev)
+{
+ size_t size; /* significant size of window */
+ CDB cdb;
+ unsigned char window[56];
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "teco_set_window: enter\n");
+
+ /* size of the whole windows block */
+ size = 0; /* keep gcc quiet */
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3575:
+ size = 53;
+ break;
+ case TECO_VM3564:
+ case TECO_VM356A:
+ case TECO_VM656A:
+ case TECO_VM6575:
+ case TECO_VM6586:
+ size = 56;
+ break;
+ default:
+ assert (0);
+ }
+
+ /* Check that window is big enough */
+ assert (size <= sizeof (window));
+
+ MKSCSI_SET_WINDOW (cdb, size);
+
+ memset (window, 0, size);
+
+ /* size of the windows descriptor block */
+ window[7] = size - 8;
+
+ /* X and Y resolution */
+ Ito16 (dev->x_resolution, &window[10]);
+ Ito16 (dev->y_resolution, &window[12]);
+
+ /* Upper Left (X,Y) */
+ Ito32 (dev->x_tl, &window[14]);
+ Ito32 (dev->y_tl, &window[18]);
+
+ /* Width and length */
+ Ito32 (dev->width, &window[22]);
+ Ito32 (dev->length, &window[26]);
+
+ /* Image Composition */
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ window[31] = dev->val[OPT_THRESHOLD].w;
+ window[33] = 0x00;
+ i = get_string_list_index (dither_list, dev->val[OPT_DITHER].s);
+ window[36] = dither_val[i];
+ break;
+ case TECO_GRAYSCALE:
+ window[33] = 0x02;
+ break;
+ case TECO_COLOR:
+ window[33] = 0x05;
+ break;
+ }
+
+ /* Depth */
+ window[34] = dev->depth;
+
+ /* Lamp to use */
+ i = get_string_list_index (filter_color_list, dev->val[OPT_FILTER_COLOR].s);
+ window[48] = filter_color_val[i];
+
+ /* Unknown - invariants */
+ window[31] = 0x80;
+ window[37] = 0x80;
+
+ /* Set the expected size line and number of lines. */
+ if (dev->def->tecoref == TECO_VM656A ||
+ dev->def->tecoref == TECO_VM6575 || dev->def->tecoref == TECO_VM6586)
+ {
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ case TECO_GRAYSCALE:
+ Ito16 (dev->params.bytes_per_line, &window[52]);
+ break;
+ case TECO_COLOR:
+ Ito16 (dev->params.bytes_per_line / 3, &window[52]);
+ break;
+ }
+ Ito16 (dev->params.lines, &window[54]);
+ }
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ hexdump (DBG_info2, "windows", window, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ window, size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_set_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Park the CCD */
+static SANE_Status
+teco_reset_window (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ CDB cdb;
+
+ DBG (DBG_proc, "teco_reset_window: enter\n");
+
+ MKSCSI_OBJECT_POSITION (cdb, 0);
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "teco_reset_window: leave, status=%d\n", status);
+
+ return status;
+}
+
+#ifdef sim
+unsigned char *image_buf;
+int image_buf_begin;
+#endif
+
+/* Read the size of the scan. */
+static SANE_Status
+teco_get_scan_size (Teco_Scanner * dev)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "teco_get_scan_size: enter\n");
+
+ size = 0x12;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ assert (size == 0x12);
+
+ hexdump (DBG_info2, "teco_get_scan_size return", dev->buffer, size);
+
+#ifndef sim
+ dev->params.lines = B16TOI (&dev->buffer[12]);
+ dev->bytes_per_raster = B16TOI (&dev->buffer[14]);
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ dev->params.bytes_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.pixels_per_line = dev->params.bytes_per_line * 8;
+ break;
+ case TECO_GRAYSCALE:
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ break;
+ case TECO_COLOR:
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ break;
+ }
+#else
+ {
+ size_t s1, s2;
+ int fd;
+ char *name;
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3575:
+
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ dev->params.pixels_per_line = 850;
+ dev->params.bytes_per_line = 850;
+ dev->params.lines = 1170;
+ dev->bytes_per_raster = 850;
+
+ name = "/home/fzago/sane/teco2/vm3575/gr100d-2.raw";
+
+ }
+ else
+ {
+ switch (dev->val[OPT_RESOLUTION].w)
+ {
+ case 50:
+ dev->params.pixels_per_line = 425;
+ dev->params.lines = 585;
+ name = "/home/fzago/sane/teco2/vm3575/out12-co50.raw";
+ break;
+ case 60:
+ dev->params.pixels_per_line = 510;
+ dev->params.lines = 702;
+ name = "/home/fzago/sane/teco2/vm3575/out12-co60.raw";
+ break;
+ case 75:
+ dev->params.pixels_per_line = 638;
+ dev->params.lines = 431;
+ name = "/home/fzago/sane/teco2/vm3575/out12-co75.raw";
+ break;
+ case 100:
+ dev->params.pixels_per_line = 402;
+ dev->params.lines = 575;
+ name = "/home/fzago/sane/teco2/vm3575/out12-co100.raw";
+ break;
+ }
+
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->bytes_per_raster = dev->params.pixels_per_line;
+ }
+ break;
+
+ case TECO_VM6586:
+ switch (dev->val[OPT_RESOLUTION].w)
+ {
+ case 150:
+ dev->params.pixels_per_line = 297;
+ dev->params.lines = 296;
+ name = "/home/fzago/sane/teco2/vm6586/150dpi.raw";
+ break;
+ case 600:
+ dev->params.pixels_per_line = 1186;
+ dev->params.lines = 1186;
+ name = "/home/fzago/sane/teco2/vm6586/600dpi.raw";
+ break;
+ }
+
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->bytes_per_raster = dev->params.pixels_per_line;
+ }
+
+
+ s1 = dev->params.bytes_per_line * dev->params.lines;
+ image_buf = malloc (s1);
+ assert (image_buf);
+ image_buf_begin = 0;
+
+ fd = open (name, O_RDONLY);
+ assert (fd >= 0);
+ s2 = read (fd, image_buf, s1);
+ assert (s1 == s2);
+ close (fd);
+
+ }
+#endif
+
+ DBG (DBG_proc, "teco_get_scan_size: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Wait until the scanner is ready to send the data. This is the
+ almost the same code as teco_get_scan_size(). This function has to
+ be called once after the scan has started. */
+static SANE_Status
+teco_wait_for_data (Teco_Scanner * dev)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+ int i;
+
+#ifdef sim
+ return SANE_STATUS_GOOD;
+#endif
+
+ DBG (DBG_proc, "teco_wait_for_data: enter\n");
+
+ for (i = 0; i < 60; i++)
+ {
+
+ size = 0x12;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ assert (size == 0x12);
+
+ hexdump (DBG_info2, "teco_wait_for_data return", dev->buffer, size);
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ if (dev->buffer[11] > 0x01)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ break;
+ /* For VM3575, VM6575, VM656A, VM6586 until otherwise default value is used */
+ default:
+ if (dev->buffer[11] == 0x80)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ break;
+ }
+ }
+
+ DBG (DBG_proc, "teco_wait_for_data: scanner not ready to send data (%d)\n",
+ status);
+
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+/* Do the calibration stuff. Get 12 or 8 lines of data. Each pixel is coded
+ * in 6 bytes (2 per color) or 3 bytes (3564 and 356A). To do the calibration,
+ * allocates an array big enough for one line, read the 12 or 8 lines of calibration,
+ * subtract the highest and lowest value and do a average.
+ * The input line is 1 raster for each color. However
+ * the output line is interlaced (ie RBG for the first pixel, then RGB
+ * for the second, and so on...). The output line are the value to use
+ * to compensate for the white point.
+ * There is two algorithms:
+ *
+ * The range goes from 0 to 0xfff, and the average is 0x800. So if
+ * the average input is 0x700, the output value for that dot must be
+ * 0x1000-0x700=0x900.
+ *
+ * and
+ *
+ * the calibration needs to be a multiplication factor, to
+ * compensate for the too strong or too weak pixel in the sensor.
+ *
+ * See more info in doc/teco/teco2.txt
+ *
+ **/
+static SANE_Status
+teco_do_calibration (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ CDB cdb;
+ size_t size;
+ int i;
+ int j;
+ int colsub0_0, colsub0_1, colsub0_2;
+ int colsub1_0, colsub1_1, colsub1_2;
+ int *tmp_buf, *tmp_min_buf, *tmp_max_buf; /* hold the temporary calibration */
+ size_t tmp_buf_size, tmp_min_buf_size, tmp_max_buf_size;
+ const char *calibration_algo;
+ int cal_algo;
+
+ colsub0_0 = 0;
+ colsub0_1 = 0;
+ colsub0_2 = 0;
+ colsub1_0 = 0;
+ colsub1_1 = 0;
+ colsub1_2 = 0;
+
+ DBG (DBG_proc, "teco_do_calibration: enter\n");
+
+ /* Get default calibration algorithm. */
+ cal_algo = dev->def->cal_algo;
+ if ((calibration_algo = getenv ("SANE_TECO2_CAL_ALGO")) != NULL)
+ {
+ cal_algo = atoi (calibration_algo);
+ }
+ if (cal_algo != 0 && cal_algo != 1)
+ {
+ DBG (DBG_error, "Invalid calibration algorithm (%d)\n", cal_algo);
+ cal_algo = 0;
+ }
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ /* white level red, green and blue calibration correction */
+ /* 0x110 or 272 is middle value */
+ colsub0_1 = 240 + (dev->val[OPT_WHITE_LEVEL_R].w);
+ colsub0_2 = 240 + (dev->val[OPT_WHITE_LEVEL_G].w);
+ colsub0_0 = 240 + (dev->val[OPT_WHITE_LEVEL_B].w);
+ /* 14000 is middle value */
+ colsub1_1 = 12720 + (40 * dev->val[OPT_WHITE_LEVEL_R].w);
+ colsub1_2 = 12720 + (40 * dev->val[OPT_WHITE_LEVEL_G].w);
+ colsub1_0 = 12720 + (40 * dev->val[OPT_WHITE_LEVEL_B].w);
+ break;
+ case TECO_VM3575:
+ case TECO_VM6575:
+ /* 0x1100 or 4352 is middle value */
+ colsub0_1 = 4096 + (8 * dev->val[OPT_WHITE_LEVEL_R].w);
+ colsub0_2 = 4096 + (8 * dev->val[OPT_WHITE_LEVEL_G].w);
+ colsub0_0 = 4096 + (8 * dev->val[OPT_WHITE_LEVEL_B].w);
+ /* 4206639 is middle value */
+ colsub1_1 = 4078639 + (4000 * dev->val[OPT_WHITE_LEVEL_R].w);
+ colsub1_2 = 4078639 + (4000 * dev->val[OPT_WHITE_LEVEL_G].w);
+ colsub1_0 = 4078639 + (4000 * dev->val[OPT_WHITE_LEVEL_B].w);
+ break;
+ /* old default value */
+ case TECO_VM656A:
+ case TECO_VM6586:
+ colsub0_1 = 0x1000;
+ colsub0_2 = 0x1000;
+ colsub0_0 = 0x1000;
+ colsub1_1 = 4206639;
+ colsub1_2 = 4206639;
+ colsub1_0 = 4206639;
+ break;
+ default:
+ colsub0_1 = 0x1000;
+ colsub0_2 = 0x1000;
+ colsub0_0 = 0x1000;
+ colsub1_1 = 4206639;
+ colsub1_2 = 4206639;
+ colsub1_0 = 4206639;
+ break;
+ }
+
+ tmp_buf_size = dev->def->cal_length * 3 * sizeof (int);
+ tmp_min_buf_size = dev->def->cal_length * 3 * sizeof (int);
+ tmp_max_buf_size = dev->def->cal_length * 3 * sizeof (int);
+ tmp_buf = malloc (tmp_buf_size);
+ tmp_min_buf = malloc (tmp_min_buf_size);
+ tmp_max_buf = malloc (tmp_max_buf_size);
+ memset (tmp_buf, 0, tmp_buf_size);
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ memset (tmp_min_buf, 0xff, tmp_min_buf_size);
+ memset (tmp_max_buf, 0x00, tmp_max_buf_size);
+ break;
+ case TECO_VM3575:
+ case TECO_VM656A:
+ case TECO_VM6575:
+ case TECO_VM6586:
+ memset (tmp_min_buf, 0xffff, tmp_min_buf_size);
+ memset (tmp_max_buf, 0x0000, tmp_max_buf_size);
+ break;
+ }
+
+ if ((tmp_buf == NULL) || (tmp_min_buf == NULL) || (tmp_max_buf == NULL))
+ {
+ DBG (DBG_proc, "teco_do_calibration: not enough memory (%ld bytes)\n",
+ (long) tmp_buf_size);
+ return (SANE_STATUS_NO_MEM);
+ }
+
+ for (i = 0; i < dev->def->cal_lines; i++)
+ {
+
+ MKSCSI_VENDOR_SPEC (cdb, SCSI_VENDOR_09, 6);
+
+ /* No idea what that is. */
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ cdb.data[2] = 0x02;
+ break;
+ case TECO_GRAYSCALE:
+ cdb.data[2] = 0x01;
+ break;
+ case TECO_COLOR:
+ cdb.data[2] = 0x00;
+ break;
+ }
+
+ /* Length of the scanner * number of bytes per color */
+ size = dev->def->cal_length * dev->def->cal_col_len;
+ cdb.data[3] = (size >> 8) & 0xff;
+ cdb.data[4] = (size >> 0) & 0xff;
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "teco_do_calibration: cannot read from the scanner\n");
+ free (tmp_buf);
+ return status;
+ }
+
+#ifdef sim
+ for (j = 0; j < dev->def->cal_length; j++)
+ {
+ dev->buffer[6 * j + 0] = 0x10;
+ dev->buffer[6 * j + 1] = 0x12;
+ dev->buffer[6 * j + 2] = 0x14;
+ dev->buffer[6 * j + 3] = 0x16;
+ dev->buffer[6 * j + 4] = 0x20;
+ dev->buffer[6 * j + 5] = 0x25;
+ }
+#endif
+ /*hexdump (DBG_info2, "got calibration line:", dev->buffer, size); */
+
+ for (j = 0; j < dev->def->cal_length; j++)
+ {
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3575:
+ case TECO_VM6575:
+ case TECO_VM656A:
+ case TECO_VM6586:
+ tmp_buf[3 * j + 0] +=
+ (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0];
+ /* get lowest value */
+ if (tmp_min_buf[3 * j + 0] >> ((dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0]))
+ {
+ tmp_min_buf[3 * j + 0] = (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0];
+ }
+ /* get highest value */
+ if (tmp_max_buf[3 * j + 0] < ((dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0]))
+ {
+ tmp_max_buf[3 * j + 0] = (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0];
+ }
+ tmp_buf[3 * j + 1] +=
+ (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2];
+ /* get lowest value */
+ if (tmp_min_buf[3 * j + 1] >> ((dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2]))
+ {
+ tmp_min_buf[3 * j + 1] = (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2];
+ }
+ /* get highest value */
+ if (tmp_max_buf[3 * j + 1] < ((dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2]))
+ {
+ tmp_max_buf[3 * j + 1] = (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2];
+ }
+ tmp_buf[3 * j + 2] +=
+ (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4];
+ /* get lowest value */
+ if (tmp_min_buf[3 * j + 2] >> ((dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4]))
+ {
+ tmp_min_buf[3 * j + 2] = (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4];
+ }
+ /* get highest value */
+ if (tmp_max_buf[3 * j + 2] < ((dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4]))
+ {
+ tmp_max_buf[3 * j + 2] = (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4];
+ }
+ break;
+ case TECO_VM3564:
+ case TECO_VM356A:
+ tmp_buf[3 * j + 0] += dev->buffer[3 * j + 0];
+ /* get lowest value */
+ if (tmp_min_buf[3 * j + 0] >> dev->buffer[3 * j + 0])
+ {
+ tmp_min_buf[3 * j + 0] = dev->buffer[3 * j + 0];
+ }
+ /* get highest value */
+ if (tmp_max_buf[3 * j + 0] < dev->buffer[3 * j + 0])
+ {
+ tmp_max_buf[3 * j + 0] = dev->buffer[3 * j + 0];
+ }
+ tmp_buf[3 * j + 1] += dev->buffer[3 * j + 1];
+ /* get lowest value */
+ if (tmp_min_buf[3 * j + 1] >> dev->buffer[3 * j + 1])
+ {
+ tmp_min_buf[3 * j + 1] = dev->buffer[3 * j + 1];
+ }
+ /* get hightest value */
+ if (tmp_max_buf[3 * j + 1] < dev->buffer[3 * j + 1])
+ {
+ tmp_max_buf[3 * j + 1] = dev->buffer[3 * j + 1];
+ }
+ tmp_buf[3 * j + 2] += dev->buffer[3 * j + 2];
+ /* get lowest value */
+ if (tmp_min_buf[3 * j + 2] >> dev->buffer[3 * j + 2])
+ {
+ tmp_min_buf[3 * j + 2] = dev->buffer[3 * j + 2];
+ }
+ /* get highest value */
+ if (tmp_max_buf[3 * j + 2] < dev->buffer[3 * j + 2])
+ {
+ tmp_max_buf[3 * j + 2] = dev->buffer[3 * j + 2];
+ }
+ break;
+/* default:
+ tmp_buf[3 * j + 0] +=
+ (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0];
+ tmp_buf[3 * j + 1] +=
+ (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2];
+ tmp_buf[3 * j + 2] +=
+ (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4];
+ break; */
+ }
+ }
+ }
+
+ /* hexdump (DBG_info2, "calibration before average:", tmp_buf, tmp_buf_size); */
+ /* hexdump (DBG_info2, "calibration before average min value:", tmp_min_buf, tmp_min_buf_size); */
+ /* hexdump (DBG_info2, "calibration before average max value:", tmp_max_buf, tmp_max_buf_size); */
+
+ /* Do the average. Since we got 12 or 8 lines, divide all values by 10 or 6
+ * and create the final calibration value that compensates for the
+ * white values read. */
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM356A:
+ case TECO_VM3564:
+ case TECO_VM3575:
+ case TECO_VM6575:
+ case TECO_VM656A:
+ case TECO_VM6586:
+ for (j = 0; j < dev->def->cal_length; j++)
+ {
+ /* subtract lowest and highest value */
+ tmp_buf[j] = tmp_buf[j] - (tmp_min_buf[j] + tmp_max_buf[j]);
+ tmp_buf[j + dev->def->cal_length] = tmp_buf[j + dev->def->cal_length]
+ - (tmp_min_buf[j + dev->def->cal_length]
+ + tmp_max_buf[j + dev->def->cal_length]);
+ tmp_buf[j + 2 * dev->def->cal_length] = tmp_buf[j + 2 * dev->def->cal_length]
+ - (tmp_min_buf[j + 2 * dev->def->cal_length]
+ + tmp_max_buf[j + 2 *dev->def->cal_length]);
+ /* sequence colors first color row one then two and last three */
+ if (cal_algo == 1)
+ {
+ tmp_buf[j] = (colsub1_0 * (dev->def->cal_lines - 2)) / tmp_buf[j];
+ tmp_buf[j + dev->def->cal_length] = (colsub1_1 * (dev->def->cal_lines - 2))
+ / tmp_buf[j + dev->def->cal_length];
+ tmp_buf[j + 2 * dev->def->cal_length] = (colsub1_2 * (dev->def->cal_lines - 2))
+ / tmp_buf[j + 2 * dev->def->cal_length];
+ }
+ else
+ {
+ tmp_buf[j] = colsub0_0 - (tmp_buf[j] / (dev->def->cal_lines - 2));
+ tmp_buf[j + dev->def->cal_length] = colsub0_1 - (tmp_buf[j + dev->def->cal_length]
+ / (dev->def->cal_lines - 2));
+ tmp_buf[j + 2 * dev->def->cal_length] = colsub0_2
+ - (tmp_buf[j + 2 * dev->def->cal_length] / (dev->def->cal_lines - 2));
+ }
+ }
+ break;
+/* default:
+ for (j = 0; j < (3 * dev->def->cal_length); j++)
+ {
+ if (cal_algo == 1)
+ tmp_buf[j] = (colsub1_1 * dev->def->cal_lines) / tmp_buf[j];
+ else
+ tmp_buf[j] = colsub0_1 - (tmp_buf[j] / dev->def->cal_lines);
+ }
+ break; */
+ }
+
+ /*hexdump (DBG_info2, "calibration after average:", tmp_buf, tmp_buf_size); */
+
+ /* Build the calibration line to send. */
+ for (j = 0; j < dev->def->cal_length; j++)
+ {
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3575:
+ case TECO_VM6575:
+ case TECO_VM656A:
+ case TECO_VM6586:
+ dev->buffer[6 * j + 0] =
+ (tmp_buf[0 * dev->def->cal_length + j] >> 0) & 0xff;
+ dev->buffer[6 * j + 1] =
+ (tmp_buf[0 * dev->def->cal_length + j] >> 8) & 0xff;
+
+ dev->buffer[6 * j + 2] =
+ (tmp_buf[1 * dev->def->cal_length + j] >> 0) & 0xff;
+ dev->buffer[6 * j + 3] =
+ (tmp_buf[1 * dev->def->cal_length + j] >> 8) & 0xff;
+
+ dev->buffer[6 * j + 4] =
+ (tmp_buf[2 * dev->def->cal_length + j] >> 0) & 0xff;
+ dev->buffer[6 * j + 5] =
+ (tmp_buf[2 * dev->def->cal_length + j] >> 8) & 0xff;
+ break;
+ case TECO_VM3564:
+ case TECO_VM356A:
+ dev->buffer[3 * j + 0] = (tmp_buf[3 * j + 0] >> 0) & 0xff;
+
+ dev->buffer[3 * j + 1] = (tmp_buf[3 * j + 1] >> 0) & 0xff;
+
+ dev->buffer[3 * j + 2] = (tmp_buf[3 * j + 2] >> 0) & 0xff;
+ break;
+ }
+ }
+
+ free (tmp_buf);
+ tmp_buf = NULL;
+
+ /* Send the calibration line. The CDB is the same as the previous
+ * one, except for the command. */
+
+ cdb.data[0] = 0x0E;
+ size = dev->def->cal_length * dev->def->cal_col_len;
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ /*hexdump (DBG_info2, "calibration line sent:", dev->buffer, size); */
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ dev->buffer, size, NULL, NULL);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "teco_do_calibration: calibration line was not sent correctly\n");
+ return status;
+ }
+
+ DBG (DBG_proc, "teco_do_calibration: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*------------request sense command 03----------------*/
+static SANE_Status
+teco_request_sense (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ unsigned char buf[255];
+ CDB cdb;
+ size_t size;
+ /* size = 0; */
+
+ DBG (DBG_proc, "teco_request_sense: enter\n");
+
+ size = sizeof (buf);
+ MKSCSI_REQUEST_SENSE (cdb, size);
+
+ /*size = cdb.data[5];
+
+ hexdump (DBG_info2, "teco_request_sense", cdb.data, cdb.len);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size); */
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, buf, &size);
+
+ hexdump (DBG_info2, "teco_request_sense:", buf, size);
+
+ DBG (DBG_proc, "teco_request_sense: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/*----------------------------------------------------*/
+
+/* Send the gamma */
+static SANE_Status
+teco_send_gamma (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ struct
+ {
+ unsigned char gamma_R[GAMMA_LENGTH];
+ unsigned char gamma_G[GAMMA_LENGTH]; /* also gray */
+ unsigned char gamma_B[GAMMA_LENGTH];
+ }
+ param;
+ size_t i;
+ size_t size;
+
+ DBG (DBG_proc, "teco_send_gamma: enter\n");
+
+ size = sizeof (param);
+ assert (size == 3 * GAMMA_LENGTH);
+ MKSCSI_SEND_10 (cdb, 0x03, 0x04, size);
+
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* Use the custom gamma. */
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ /* Gray */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = dev->gamma_GRAY[i];
+ param.gamma_G[i] = dev->gamma_GRAY[i];
+ param.gamma_B[i] = dev->gamma_GRAY[i];
+ }
+ }
+ else
+ {
+ /* Color */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = dev->gamma_R[i];
+ param.gamma_G[i] = dev->gamma_G[i];
+ param.gamma_B[i] = dev->gamma_B[i];
+ }
+ }
+ }
+ else
+ {
+ /* Defaults to a linear gamma for now. */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = i / 4;
+ param.gamma_G[i] = i / 4;
+ param.gamma_B[i] = i / 4;
+ }
+ }
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ status = 0;
+ break;
+ default:
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ &param, size, NULL, NULL);
+ }
+ DBG (DBG_proc, "teco_send_gamma: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Send a vendor specific command. */
+static SANE_Status
+teco_send_vendor_06 (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ CDB cdb;
+ size_t size;
+
+ DBG (DBG_proc, "teco_send_vendor_06: enter\n");
+
+ MKSCSI_VENDOR_SPEC (cdb, SCSI_VENDOR_06, 6);
+
+ size = 4;
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+
+ MKSCSI_VENDOR_SPEC (cdb, SCSI_VENDOR_1C, 6);
+
+ size = 4;
+ memset (dev->buffer, 0, size);
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ dev->buffer, size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_send_vendor_06: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Start a scan. */
+static SANE_Status
+teco_scan (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "teco_scan: enter\n");
+
+ MKSCSI_SCAN (cdb);
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "teco_scan: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* SCSI sense handler. Callback for SANE. */
+static SANE_Status
+teco_sense_handler (int scsi_fd, unsigned char *result, void __sane_unused__ *arg)
+{
+ int asc, ascq, sensekey;
+ int len;
+
+ DBG (DBG_proc, "teco_sense_handler (scsi_fd = %d)\n", scsi_fd);
+
+ sensekey = get_RS_sense_key (result);
+ len = 7 + get_RS_additional_length (result);
+
+ hexdump (DBG_info2, "sense", result, len);
+
+ if (get_RS_error_code (result) != 0x70)
+ {
+ DBG (DBG_error,
+ "teco_sense_handler: invalid sense key error code (%d)\n",
+ get_RS_error_code (result));
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (get_RS_ILI (result) != 0)
+ {
+ DBG (DBG_sense, "teco_sense_handler: short read\n");
+ }
+
+ if (len < 14)
+ {
+ DBG (DBG_error, "teco_sense_handler: sense too short, no ASC/ASCQ\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ asc = get_RS_ASC (result);
+ ascq = get_RS_ASCQ (result);
+
+ DBG (DBG_sense, "teco_sense_handler: sense=%d, ASC/ASCQ=%02x%02x\n",
+ sensekey, asc, ascq);
+
+ switch (sensekey)
+ {
+ case 0x00: /* no sense */
+ case 0x02: /* not ready */
+ case 0x03: /* medium error */
+ case 0x05:
+ case 0x06:
+ break;
+ }
+
+ DBG (DBG_sense,
+ "teco_sense_handler: unknown error condition. Please report it to the backend maintainer\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Attach a scanner to this backend. */
+static SANE_Status
+attach_scanner (const char *devicename, Teco_Scanner ** devp)
+{
+ Teco_Scanner *dev;
+ int sfd;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new scanner entry. */
+ dev = teco_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+ if (sanei_scsi_open (devicename, &sfd, teco_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_scanner: open failed\n");
+ teco_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+#ifdef sim
+ sfd = -1;
+#endif
+
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Now, check that it is a scanner we support. */
+ if (teco_identify_scanner (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ teco_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ teco_close (dev);
+
+ /* If this scanner only supports a given number of resolutions,
+ * build that list now. */
+ if (dev->def->color_adjust[0].resolution != 0)
+ {
+ int num_entries;
+ int i;
+
+ num_entries = 0;
+ while (dev->def->color_adjust[num_entries].resolution != 0)
+ num_entries++;
+
+ dev->resolutions_list = malloc (sizeof (SANE_Word) * (num_entries + 1));
+
+ if (dev->resolutions_list == NULL)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ teco_free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->resolutions_list[0] = num_entries;
+ for (i = 0; i < num_entries; i++)
+ {
+ dev->resolutions_list[i + 1] = dev->def->color_adjust[i].resolution;
+ }
+ }
+ else
+ {
+ dev->resolutions_list = NULL;
+ }
+
+ /* Set the default options for that scanner. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->def->real_vendor;
+ dev->sane.model = dev->def->real_product;
+ dev->sane.type = SANE_I18N ("flatbed scanner");
+
+ /* Link the scanner with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_scanner: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that scanner. */
+static void
+teco_init_options (Teco_Scanner * dev)
+{
+ int i;
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RESOLUTION].constraint.range = &dev->def->res_range;
+ dev->val[OPT_RESOLUTION].w = DEF_RESOLUTION;
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &dev->def->x_range;
+ dev->val[OPT_TL_X].w = dev->def->x_range.min;
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &dev->def->y_range;
+ dev->val[OPT_TL_Y].w = dev->def->y_range.min;
+
+ /* Bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &dev->def->x_range;
+ dev->val[OPT_BR_X].w = dev->def->x_range.max;
+
+ /* Bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &dev->def->y_range;
+ dev->val[OPT_BR_Y].w = dev->def->y_range.max;
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Halftone pattern */
+ dev->opt[OPT_DITHER].name = "dither";
+ dev->opt[OPT_DITHER].title = SANE_I18N ("Dither");
+ dev->opt[OPT_DITHER].desc = SANE_I18N ("Dither");
+ dev->opt[OPT_DITHER].type = SANE_TYPE_STRING;
+ dev->opt[OPT_DITHER].size = max_string_size (dither_list);
+ dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_DITHER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_DITHER].constraint.string_list = dither_list;
+ dev->val[OPT_DITHER].s = strdup (dither_list[0]);
+
+ /* custom-gamma table */
+ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R;
+
+ /* green and gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G;
+
+ /* blue gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B;
+
+ /* grayscale gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_GRAY].wa = dev->gamma_GRAY;
+
+ /* Color filter */
+ dev->opt[OPT_FILTER_COLOR].name = "color-filter";
+ dev->opt[OPT_FILTER_COLOR].title = "Color dropout";
+ dev->opt[OPT_FILTER_COLOR].desc = "Color dropout";
+ dev->opt[OPT_FILTER_COLOR].type = SANE_TYPE_STRING;
+ dev->opt[OPT_FILTER_COLOR].size = max_string_size (filter_color_list);
+ dev->opt[OPT_FILTER_COLOR].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_FILTER_COLOR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_FILTER_COLOR].constraint.string_list = filter_color_list;
+ dev->val[OPT_FILTER_COLOR].s = (SANE_Char *) strdup (filter_color_list[0]);
+
+ /* Threshold */
+ dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_THRESHOLD].size = sizeof (SANE_Int);
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_THRESHOLD].constraint.range = &threshold_range;
+ dev->val[OPT_THRESHOLD].w = 128;
+
+ /* preview */
+ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* red level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_R].name = SANE_NAME_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].title = SANE_TITLE_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].desc = SANE_DESC_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_R].constraint.range = &red_level_range;
+ dev->val[OPT_WHITE_LEVEL_R].w = 32; /* to get middle value */
+
+ /* green level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_G].name = SANE_NAME_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].title = SANE_TITLE_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].desc = SANE_DESC_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_G].constraint.range = &green_level_range;
+ dev->val[OPT_WHITE_LEVEL_G].w = 32; /* to get middle value */
+
+ /* blue level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_B].name = SANE_NAME_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].title = SANE_TITLE_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].desc = SANE_DESC_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_B].constraint.range = &blue_level_range;
+ dev->val[OPT_WHITE_LEVEL_B].w = 32; /* to get middle value */
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scan_mode_list[0], NULL);
+}
+
+/*
+ * Wait until the scanner is ready.
+ */
+static SANE_Status
+teco_wait_scanner (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ int timeout;
+ CDB cdb;
+
+ DBG (DBG_proc, "teco_wait_scanner: enter\n");
+
+ MKSCSI_TEST_UNIT_READY (cdb);
+
+ /* Set the timeout to 60 seconds. */
+ timeout = 60;
+
+ while (timeout > 0)
+ {
+
+ /* test unit ready */
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd (dev->sfd, cdb.data, cdb.len, NULL, NULL);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ };
+
+ DBG (DBG_proc, "teco_wait_scanner: scanner not ready\n");
+ return (SANE_STATUS_IO_ERROR);
+}
+
+/*
+ * Adjust the rasters. This function is used during a color scan,
+ * because the scanner does not present a format sane can interpret
+ * directly.
+ *
+ * The scanner sends the colors by rasters (B then G then R), whereas
+ * sane is waiting for a group of 3 bytes per color. To make things
+ * funnier, the rasters are shifted. As a result, color planes appear to be shifted be a few pixels.
+ *
+ * The order of the color is dependant on each scanners. Also the same
+ * scanner can change the order depending on the resolution.
+ *
+ * For instance, the VM6586 at 300dpi has a color shift of 2 lines. The rasters sent are:
+ * starts with two blue rasters - BB,
+ * then red in added - BRBR
+ * then green - BRG ... (most of the picture)
+ * then blue is removed - RGRG
+ * and finaly only green stays - GG
+ *
+ * Overall there is the same number of RGB rasters.
+ * The VM3575 is a variant (when factor_x is 0). It does not keep the same order,
+ * but reverses it, eg:
+ * BB RBRB GRB... GRGR GG
+ * (ie it adds the new color in front of the previous one, instead of after).
+ *
+ * So this function reorders all that mess. It gets the input from
+ * dev->buffer and write the output in dev->image. size_in the the
+ * length of the valid data in dev->buffer. */
+
+#define COLOR_0 (color_adjust->z3_color_0)
+#define COLOR_1 (color_adjust->z3_color_1)
+#define COLOR_2 (color_adjust->z3_color_2)
+
+static void
+teco_adjust_raster (Teco_Scanner * dev, size_t size_in)
+{
+ int nb_rasters; /* number of rasters in dev->buffer */
+
+ int raster; /* current raster number in buffer */
+ int line; /* line number for that raster */
+ int color; /* color for that raster */
+ size_t offset;
+ const struct dpi_color_adjust *color_adjust = dev->color_adjust;
+
+ DBG (DBG_proc, "teco_adjust_raster: enter\n");
+
+ color = 0; /* keep gcc quiet */
+
+ assert (dev->scan_mode == TECO_COLOR);
+ assert ((size_in % dev->params.bytes_per_line) == 0);
+
+ if (size_in == 0)
+ {
+ return;
+ }
+
+ /*
+ * The color coding is one line for each color (in the RGB order).
+ * Recombine that stuff to create a RGB value for each pixel.
+ */
+
+ nb_rasters = size_in / dev->raster_size;
+
+ for (raster = 0; raster < nb_rasters; raster++)
+ {
+
+ /*
+ * Find the color and the line which this raster belongs to.
+ */
+ line = 0;
+ if (dev->raster_num < color_adjust->color_shift)
+ {
+ /* Top of the picture. */
+ if (color_adjust->factor_x)
+ {
+ color = COLOR_2;
+ }
+ else
+ {
+ color = COLOR_1;
+ }
+ line = dev->raster_num;
+ }
+ else if (dev->raster_num < (3 * color_adjust->color_shift))
+ {
+ /* Top of the picture. */
+ if ((dev->raster_num - color_adjust->color_shift) % 2)
+ {
+ color = COLOR_0;
+ line = (dev->raster_num - color_adjust->color_shift) / 2;
+ }
+ else
+ {
+ if (color_adjust->factor_x)
+ {
+ color = COLOR_2;
+ }
+ else
+ {
+ color = COLOR_1;
+ }
+ line = (dev->raster_num + color_adjust->color_shift) / 2;
+ }
+ }
+ else if (dev->raster_num >=
+ dev->raster_real - color_adjust->color_shift)
+ {
+ /* Bottom of the picture. */
+ if (color_adjust->factor_x)
+ {
+ color = COLOR_1;
+ }
+ else
+ {
+ color = COLOR_2;
+ }
+ line = dev->line;
+ }
+ else if (dev->raster_num >=
+ dev->raster_real - 3 * color_adjust->color_shift)
+ {
+ /* Bottom of the picture. */
+ if ((dev->raster_real - dev->raster_num -
+ color_adjust->color_shift) % 2)
+ {
+ if (color_adjust->factor_x)
+ {
+ color = COLOR_1;
+ line = dev->line;
+ }
+ else
+ {
+ color = COLOR_0;
+ line = dev->line + color_adjust->color_shift - 1;
+ }
+ }
+ else
+ {
+ if (color_adjust->factor_x)
+ {
+ color = COLOR_0;
+ line = dev->line + color_adjust->color_shift;
+ }
+ else
+ {
+ color = COLOR_2;
+ line = dev->line;
+ }
+ }
+ }
+ else
+ {
+ /* Middle of the picture. */
+ switch ((dev->raster_num) % 3)
+ {
+ case 0:
+ color = COLOR_2;
+ if (color_adjust->factor_x)
+ line = dev->raster_num / 3 + color_adjust->color_shift;
+ else
+ line = dev->raster_num / 3 - color_adjust->color_shift;
+ break;
+ case 1:
+ color = COLOR_0;
+ line = dev->raster_num / 3;
+ break;
+ case 2:
+ color = COLOR_1;
+ if (color_adjust->factor_x)
+ line = dev->raster_num / 3 - color_adjust->color_shift;
+ else
+ line = dev->raster_num / 3 + color_adjust->color_shift;
+ break;
+ }
+ }
+
+ /* Adjust the line number relative to the image. */
+ line -= dev->line;
+
+ offset = dev->image_end + line * dev->params.bytes_per_line;
+
+ assert (offset <= (dev->image_size - dev->params.bytes_per_line));
+
+ /* Copy the raster to the temporary image. */
+ {
+ int i;
+ unsigned char *src = dev->buffer + raster * dev->raster_size;
+ unsigned char *dest = dev->image + offset + color;
+
+ for (i = 0; i < dev->raster_size; i++)
+ {
+ *dest = *src;
+ src++;
+ dest += 3;
+ }
+ }
+
+ DBG (DBG_info, "raster=%d, line=%d, color=%d\n", dev->raster_num,
+ dev->line + line, color);
+
+ if ((color_adjust->factor_x == 0 && color == COLOR_2) ||
+ (color_adjust->factor_x == 1 && color == COLOR_1))
+ {
+ /* This blue raster completes a new line */
+ dev->line++;
+ dev->image_end += dev->params.bytes_per_line;
+ }
+
+ dev->raster_num++;
+ }
+
+ DBG (DBG_proc, "teco_adjust_raster: exit\n");
+}
+
+/* Read the image from the scanner and fill the temporary buffer with it. */
+static SANE_Status
+teco_fill_image (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+
+ DBG (DBG_proc, "teco_fill_image: enter\n");
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ /* Copy the complete lines, plus the incompletes
+ * ones. We don't keep the real end of data used
+ * in image, so we copy the biggest possible.
+ *
+ * This is a no-op for non color images.
+ */
+ memmove (dev->image, dev->image + dev->image_begin, dev->raster_ahead);
+
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ while (dev->real_bytes_left)
+ {
+ /*
+ * Try to read the maximum number of bytes.
+ */
+ size = dev->real_bytes_left;
+ if (size > dev->image_size - dev->raster_ahead - dev->image_end)
+ {
+ size = dev->image_size - dev->raster_ahead - dev->image_end;
+ }
+ if (size > dev->buffer_size)
+ {
+ size = dev->buffer_size;
+ }
+
+ /* Limit to 0x2000 bytes as does the TWAIN driver. */
+ if (size > 0x2000)
+ size = 0x2000;
+
+ /* Round down to a multiple of line size. */
+ size = size - (size % dev->params.bytes_per_line);
+
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_info, "teco_fill_image: to read = %ld bytes (bpl=%d)\n",
+ (long) size, dev->params.bytes_per_line);
+
+ MKSCSI_READ_10 (cdb, 0, 0, size);
+ cdb.data[5] = size / dev->params.bytes_per_line;
+
+ hexdump (DBG_info2, "teco_fill_image: READ_10 CDB", cdb.data, cdb.len);
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+#ifdef sim
+ memcpy (dev->buffer, image_buf + image_buf_begin, size);
+ image_buf_begin += size;
+#endif
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "teco_fill_image: cannot read from the scanner\n");
+ return status;
+ }
+
+ DBG (DBG_info, "teco_fill_image: real bytes left = %ld\n",
+ (long) dev->real_bytes_left);
+
+ if (dev->scan_mode == TECO_COLOR &&
+ dev->def->tecoref != TECO_VM656A && raw_output == 0)
+ {
+ teco_adjust_raster (dev, size);
+ }
+ else
+ {
+ memcpy (dev->image + dev->image_end, dev->buffer, size);
+ dev->image_end += size;
+ }
+ dev->real_bytes_left -= size;
+ }
+
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+/* Copy from the raw buffer to the buffer given by the backend.
+ *
+ * len in input is the maximum length available in buf, and, in
+ * output, is the length written into buf.
+ */
+static void
+teco_copy_raw_to_frontend (Teco_Scanner * dev, SANE_Byte * buf, size_t * len)
+{
+ size_t size;
+
+ size = dev->image_end - dev->image_begin;
+ if (size > *len)
+ {
+ size = *len;
+ }
+ *len = size;
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ {
+ /* Invert black and white. */
+ unsigned char *src = dev->image + dev->image_begin;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ {
+ *buf = *src ^ 0xff;
+ src++;
+ buf++;
+ }
+ }
+ break;
+
+ case TECO_GRAYSCALE:
+ case TECO_COLOR:
+ memcpy (buf, dev->image + dev->image_begin, size);
+ break;
+ }
+
+ dev->image_begin += size;
+}
+
+/* Stop a scan. */
+static SANE_Status
+do_cancel (Teco_Scanner * dev)
+{
+ DBG (DBG_sane_proc, "do_cancel enter\n");
+
+ if (dev->scanning == SANE_TRUE)
+ {
+
+ /* Reset the scanner */
+ teco_reset_window (dev);
+ teco_close (dev);
+ }
+
+ dev->scanning = SANE_FALSE;
+
+ DBG (DBG_sane_proc, "do_cancel exit\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Sane entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ char dev_name[PATH_MAX];
+ size_t len;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ DBG (DBG_error, "This is sane-teco2 version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2002 - 2003 by Frank Zago, update 2003 - 2008 by Gerard Klaver\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ fp = sanei_config_open (TECO2_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach_scanner ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Teco_Scanner *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Teco_Scanner *dev;
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No scanner found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ teco_init_options (dev);
+
+ /* Initialize the gamma table. */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ dev->gamma_R[i] = i / 4;
+ dev->gamma_G[i] = i / 4;
+ dev->gamma_B[i] = i / 4;
+ dev->gamma_GRAY[i] = i / 4;
+ }
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Teco_Scanner *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_PREVIEW:
+ case OPT_THRESHOLD:
+ case OPT_WHITE_LEVEL_R:
+ case OPT_WHITE_LEVEL_G:
+ case OPT_WHITE_LEVEL_B:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ case OPT_DITHER:
+ case OPT_FILTER_COLOR:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* Gamma */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (val, dev->val[option].wa, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Numeric side-effect options */
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_THRESHOLD:
+ case OPT_RESOLUTION:
+ case OPT_WHITE_LEVEL_R:
+ case OPT_WHITE_LEVEL_G:
+ case OPT_WHITE_LEVEL_B:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Numeric side-effect free options */
+ case OPT_PREVIEW:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect free options */
+ case OPT_DITHER:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ return SANE_STATUS_GOOD;
+
+ case OPT_FILTER_COLOR:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect options */
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_FILTER_COLOR].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap |= SANE_CAP_INACTIVE;
+
+
+ /* This the default resolution range, except for the
+ * VM3575, VM3564, VM356A and VM6586 in color mode. */
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RESOLUTION].constraint.range = &dev->def->res_range;
+
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ dev->scan_mode = TECO_BW;
+ dev->depth = 8;
+ dev->opt[OPT_DITHER].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_FILTER_COLOR].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ dev->scan_mode = TECO_GRAYSCALE;
+ dev->depth = 8;
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;
+ break;
+ case TECO_VM3575:
+ case TECO_VM6575:
+ dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &=
+ ~SANE_CAP_INACTIVE;
+ }
+ break;
+ case TECO_VM656A:
+ case TECO_VM6586:
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &=
+ ~SANE_CAP_INACTIVE;
+ }
+ break;
+ }
+ dev->opt[OPT_FILTER_COLOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->scan_mode = TECO_COLOR;
+ dev->depth = 8;
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;
+ break;
+ case TECO_VM3575:
+ case TECO_VM6575:
+ dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ break;
+ case TECO_VM656A:
+ case TECO_VM6586:
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ break;
+ }
+ /* The VM3575, VM3564, VM356A and VM6586 supports only a handful of resolution. Do that here.
+ * Ugly! */
+ if (dev->resolutions_list != NULL)
+ {
+ int i;
+
+ dev->opt[OPT_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list =
+ dev->resolutions_list;
+
+ /* If the resolution isn't in the list, set a default. */
+ for (i = 1; i <= dev->resolutions_list[0]; i++)
+ {
+ if (dev->resolutions_list[i] >=
+ dev->val[OPT_RESOLUTION].w)
+ break;
+ }
+ if (i > dev->resolutions_list[0])
+ {
+ /* Too big. Take default. */
+ dev->val[OPT_RESOLUTION].w = DEF_RESOLUTION;
+ }
+ else
+ {
+ /* Take immediate superioir value. */
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[i];
+ }
+ }
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (dev->val[option].wa, val, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* use custom_gamma_table */
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Setup the parameters for the scan. These values will be re-used
+ * in the SET WINDOWS command. */
+ if (dev->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ /* for VM356A, no good 50 dpi scan possible, now leave as */
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM356A:
+ case TECO_VM6575:
+ dev->x_resolution = 75;
+ dev->y_resolution = 75;
+ break;
+ /* For VM3575, VM656A, VM6586 etc until otherwise default value is used */
+ default:
+ dev->x_resolution = 50;
+ dev->y_resolution = 50;
+ break;
+ }
+ dev->x_tl = 0;
+ dev->y_tl = 0;
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->def->x_range.max));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->def->y_range.max));
+ }
+ else
+ {
+ dev->x_resolution = dev->val[OPT_RESOLUTION].w;
+ dev->y_resolution = dev->val[OPT_RESOLUTION].w;
+
+ dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ }
+
+ if (dev->x_resolution > dev->def->x_resolution_max)
+ {
+ dev->x_resolution = dev->def->x_resolution_max;
+ }
+
+ /* Check the corners are OK. */
+ if (dev->x_tl > dev->x_br)
+ {
+ int s;
+ s = dev->x_tl;
+ dev->x_tl = dev->x_br;
+ dev->x_br = s;
+ }
+ if (dev->y_tl > dev->y_br)
+ {
+ int s;
+ s = dev->y_tl;
+ dev->y_tl = dev->y_br;
+ dev->y_br = s;
+ }
+
+ dev->width = dev->x_br - dev->x_tl;
+ dev->length = dev->y_br - dev->y_tl;
+
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.last_frame = SANE_TRUE;
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) /
+ dev->def->x_resolution_max) & ~0x7;
+ dev->params.bytes_per_line = dev->params.pixels_per_line / 8;
+ dev->params.depth = 1;
+ dev->color_adjust = NULL;
+ break;
+ case TECO_GRAYSCALE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / dev->def->x_resolution_max);
+ if ((dev->def->tecoref == TECO_VM656A
+ || dev->def->tecoref == TECO_VM6586)
+ && ((dev->width * dev->x_resolution) %
+ dev->def->x_resolution_max != 0))
+ {
+ /* Round up */
+ dev->params.pixels_per_line += 1;
+ }
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ dev->params.depth = 8;
+ dev->color_adjust = NULL;
+ break;
+ case TECO_COLOR:
+ dev->params.format = SANE_FRAME_RGB;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / dev->def->x_resolution_max);
+ if ((dev->def->tecoref == TECO_VM656A
+ || dev->def->tecoref == TECO_VM6586)
+ && ((dev->width * dev->x_resolution) %
+ dev->def->x_resolution_max != 0))
+ {
+ /* Round up */
+ dev->params.pixels_per_line += 1;
+ }
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->params.depth = 8;
+
+ if (dev->resolutions_list != NULL)
+ {
+ /* This scanner has a fixed number of supported
+ * resolutions. Find the color shift for that
+ * resolution. */
+
+ int i;
+ for (i = 0;
+ dev->def->color_adjust[i].resolution != dev->y_resolution;
+ i++);
+
+ dev->color_adjust = &dev->def->color_adjust[i];
+ }
+ else
+ {
+ dev->color_adjust = &dev->def->color_adjust[0];
+ }
+ break;
+ }
+
+ dev->params.lines =
+ (dev->length * dev->y_resolution) / dev->def->x_resolution_max;
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ sane_get_parameters (dev, NULL);
+
+ /* Open again the scanner. */
+ if (sanei_scsi_open
+ (dev->devicename, &(dev->sfd), teco_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* The scanner must be ready. */
+ status = teco_wait_scanner (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_set_window (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_get_scan_size (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ /* Compute the length necessary in image. The first part will store
+ * the complete lines, and the rest is used to stored ahead
+ * rasters.
+ */
+ if (dev->color_adjust)
+ {
+ dev->raster_ahead =
+ (2 * dev->color_adjust->color_shift) * dev->params.bytes_per_line;
+ }
+ else
+ {
+ dev->raster_ahead = 0;
+ }
+ dev->image_size = dev->buffer_size + dev->raster_ahead;
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Rasters are meaningfull only in color mode. */
+ dev->raster_size = dev->params.pixels_per_line;
+ dev->raster_real = dev->params.lines * 3;
+ dev->raster_num = 0;
+ dev->line = 0;
+
+#if 1
+ status = teco_do_calibration (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+#endif
+
+ switch (dev->def->tecoref)
+ {
+ /* case TECO_VM3564: not for VM3564 */
+ case TECO_VM356A:
+/*--------------request sense for first time loop---*/
+ status = teco_request_sense (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ break;
+ default:
+ break;
+ }
+
+ status = teco_send_gamma (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_set_window (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3564:
+ case TECO_VM356A:
+ break;
+ default:
+ status = teco_send_vendor_06 (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ }
+ status = teco_scan (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_wait_for_data (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+ dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Teco_Scanner *dev = handle;
+ size_t size;
+ int buf_offset; /* offset into buf */
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning */
+ return do_cancel (dev);
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ buf_offset = 0;
+
+ do
+ {
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = teco_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+#if 1
+ assert (dev->image_begin != dev->image_end);
+#else
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+#endif
+
+ /* Copy the data to the frontend buffer. */
+ size = max_len - buf_offset;
+ if (size > dev->bytes_left)
+ {
+ size = dev->bytes_left;
+ }
+ teco_copy_raw_to_frontend (dev, buf + buf_offset, &size);
+
+ buf_offset += size;
+
+ dev->bytes_left -= size;
+ *len += size;
+
+ }
+ while ((buf_offset != max_len) && dev->bytes_left);
+
+ DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long) dev->bytes_left);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ SANE_Status status;
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ if (dev->scanning == SANE_FALSE)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (non_blocking == SANE_FALSE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ do_cancel (dev);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+ Teco_Scanner *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+ do_cancel (dev);
+ teco_close (dev);
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ teco_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/teco2.conf.in b/backend/teco2.conf.in
new file mode 100644
index 0000000..a012e56
--- /dev/null
+++ b/backend/teco2.conf.in
@@ -0,0 +1,29 @@
+# VM3564 RELISYS AVEC II S3 scanner
+scsi "RELISYS" "AVEC II S3"
+
+# VM356A Primax Jewel 4800 scanner
+scsi "Primax" "Jewel"
+
+# VM356A Relisys APOLLO Express 3
+scsi "RELISYS" "APOLLO Express 3"
+
+# VM3575 Relisys AVEC Super 3
+scsi "RELISYS" "AVEC Super 3"
+
+# VM3575 Relisys SCORPIO Super 3
+# VM3575 Mustek ScanMagic 4830S
+# VM6586 Relisys SCORPIO Pro-S
+scsi "" "Flatbed Scanner"
+
+# VM656A Relisys APOLLO Express 6
+scsi "RELISYS" "APOLLO Express 6"
+
+# VM6575 Relisys SCORPIO Pro
+scsi "RELISYS" "SCORPIO Pro"
+
+# VM6575 Primax Profi 9600
+scsi "Primax" "Profi 9600"
+
+# VM6586 Primax Profi 19200
+scsi "Primax" "Profi 19200"
+/dev/scanner
diff --git a/backend/teco2.h b/backend/teco2.h
new file mode 100644
index 0000000..9975f03
--- /dev/null
+++ b/backend/teco2.h
@@ -0,0 +1,465 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+ Copyright (C) 2003-2005 Gerard Klaver (gerard at gkall dot hobby dot nl)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+*/
+
+/* Commands supported by the scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_VENDOR_06 0x06
+#define SCSI_VENDOR_09 0x09
+#define SCSI_VENDOR_0C 0x0C
+#define SCSI_VENDOR_0E 0x0E
+#define SCSI_INQUIRY 0x12
+#define SCSI_SCAN 0x1b
+#define SCSI_VENDOR_1C 0x1C
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_SEND_10 0x2a
+#define SCSI_READ_10 0x28
+#define SCSI_OBJECT_POSITION 0x31
+#define SCSI_GET_DATA_BUFFER_STATUS 0x34
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+}
+CDB;
+
+
+/* Set a specific bit depending on a boolean.
+ * MKSCSI_BIT(TRUE, 3) will generate 0x08. */
+#define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0)
+
+/* Set a value in a range of bits.
+ * MKSCSI_I2B(5, 3, 5) will generate 0x28 */
+#define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1))
+
+/* Store an integer in 2, 3 or 4 byte in an array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+#define MKSCSI_GET_DATA_BUFFER_STATUS(cdb, wait, buflen) \
+ cdb.data[0] = SCSI_GET_DATA_BUFFER_STATUS; \
+ cdb.data[1] = MKSCSI_BIT(wait, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_INQUIRY(cdb, buflen) \
+ cdb.data[0] = SCSI_INQUIRY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_MODE_SELECT(cdb, pf, sp, buflen) \
+ cdb.data[0] = SCSI_MODE_SELECT; \
+ cdb.data[1] = MKSCSI_BIT(pf, 4) | MKSCSI_BIT(sp, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_OBJECT_POSITION(cdb, position) \
+ cdb.data[0] = SCSI_OBJECT_POSITION; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (((position) >> 16) & 0xff); \
+ cdb.data[3] = (((position) >> 8) & 0xff); \
+ cdb.data[4] = (((position) >> 0) & 0xff); \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = 0; \
+ cdb.data[8] = 0; \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_SET_WINDOW(cdb, buflen) \
+ cdb.data[0] = SCSI_SET_WINDOW; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_READ_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_REQUEST_SENSE(cdb, buflen) \
+ cdb.data[0] = SCSI_REQUEST_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (buflen); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SCAN(cdb) \
+ cdb.data[0] = SCSI_SCAN; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SEND_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_SEND_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_TEST_UNIT_READY(cdb) \
+ cdb.data[0] = SCSI_TEST_UNIT_READY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_VENDOR_SPEC(cdb, command, length) { \
+ assert(length == 6 || length == 10 || length == 12 || length == 16); \
+ memset(cdb.data, 0, length); \
+ cdb.data[0] = command; \
+ cdb.len = length; \
+}
+
+/*--------------------------------------------------------------------------*/
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7)
+
+/*--------------------------------------------------------------------------*/
+
+#define mmToIlu(mm) (((mm) * dev->def->x_resolution_max) / MM_PER_INCH)
+#define iluToMm(ilu) (((ilu) * MM_PER_INCH) / dev->def->x_resolution_max)
+
+/*--------------------------------------------------------------------------*/
+
+#define GAMMA_LENGTH 0x400 /* number of value per color */
+
+/*--------------------------------------------------------------------------*/
+
+/* Black magic for color adjustment. Used only for VM3575. */
+struct dpi_color_adjust
+{
+ int resolution; /* in dpi. 0 means all resolution supported. */
+
+#if 0
+ int z1_color_0; /* 0, 1 or 2 */
+ int z1_color_1; /* idem */
+ int z1_color_2; /* idem */
+#endif
+
+ int z3_color_0; /* 0, 1 or 2 */
+ int z3_color_1; /* idem */
+ int z3_color_2; /* idem */
+
+ int factor_x;
+
+ int color_shift; /* color plane shift in pixel. If a
+ * negative shift seems necessary, set
+ * factor_x to 1 */
+};
+
+/*--------------------------------------------------------------------------*/
+
+enum Teco_Option
+{
+ /* Must come first */
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CUSTOM_GAMMA, /* Use the custom gamma tables */
+ OPT_GAMMA_VECTOR_R, /* Custom Red gamma table */
+ OPT_GAMMA_VECTOR_G, /* Custom Green Gamma table */
+ OPT_GAMMA_VECTOR_B, /* Custom Blue Gamma table */
+ OPT_GAMMA_VECTOR_GRAY, /* Custom Grayscale Gamma table */
+
+ OPT_DITHER,
+ OPT_FILTER_COLOR, /* which color to filter */
+ OPT_THRESHOLD, /* Threshold */
+ OPT_WHITE_LEVEL_R, /* white level correction RED */
+ OPT_WHITE_LEVEL_G, /* white level correction GREEN */
+ OPT_WHITE_LEVEL_B, /* white level correction BLUE */
+
+ OPT_PREVIEW,
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Scanner supported by this backend.
+ */
+struct scanners_supported
+{
+ int scsi_type;
+ char scsi_teco_name[12]; /* real name of the scanner */
+ enum
+ {
+ TECO_VM3564,
+ TECO_VM356A,
+ TECO_VM3575,
+ TECO_VM6575,
+ TECO_VM656A,
+ TECO_VM6586
+ }
+ tecoref;
+ char *real_vendor; /* brand on the box */
+ char *real_product; /* name on the box */
+
+ SANE_Range res_range;
+
+ int x_resolution_max; /* maximum X dpi */
+ int y_resolution_max; /* maximum Y dpi */
+
+ int cal_length; /* size of a calibration line in pixels */
+ int cal_lines; /* number of calibration lines to read */
+ int cal_col_len; /* number of byte to code one color */
+ int cal_algo; /* default algo to use to compute calibration line */
+
+ /* Minimum and maximum width and length supported. */
+ SANE_Range x_range;
+ SANE_Range y_range;
+
+ /* Resolutions supported in color mode. */
+ const struct dpi_color_adjust *color_adjust;
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define a scanner occurence. */
+typedef struct Teco_Scanner
+{
+ struct Teco_Scanner *next;
+ SANE_Device sane;
+
+ char *devicename;
+ int sfd; /* device handle */
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+ char scsi_version[5];
+ char scsi_teco_name[12]; /* real name of the scanner */
+
+ /* SCSI handling */
+ size_t buffer_size; /* size of the buffer */
+ SANE_Byte *buffer; /* for SCSI transfer. */
+
+ /* Scanner infos. */
+ const struct scanners_supported *def; /* default options for that scanner */
+
+ SANE_Word *resolutions_list;
+
+ /* Scanning handling. */
+ int scanning; /* TRUE if a scan is running. */
+ int x_resolution; /* X resolution in DPI */
+ int y_resolution; /* Y resolution in DPI */
+ int x_tl; /* X top left */
+ int y_tl; /* Y top left */
+ int x_br; /* X bottom right */
+ int y_br; /* Y bottom right */
+ int width; /* width of the scan area in mm */
+ int length; /* length of the scan area in mm */
+ int depth; /* depth per color */
+
+ enum
+ {
+ TECO_BW,
+ TECO_GRAYSCALE,
+ TECO_COLOR
+ }
+ scan_mode;
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+
+ size_t real_bytes_left; /* number of bytes left the scanner will return. */
+
+ SANE_Byte *image; /* keep the raw image here */
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+
+ const struct dpi_color_adjust *color_adjust;
+
+ size_t bytes_per_raster; /* bytes per raster. In B&W and Gray,
+ that the same as
+ param.bytes_per_lines. In Color,
+ it's a third.
+ */
+
+ int raster_size; /* size of a raster */
+ int raster_num; /* for color scan, current raster read */
+ int raster_real; /* real number of raster in the
+ * scan. This is necessary since I
+ * don't know how to reliably compute
+ * the number of lines */
+ int raster_ahead; /* max size of the incomplete lines */
+ int line; /* current line of the scan */
+
+ SANE_Parameters params;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+
+ /* Gamma table. 1 array per color. */
+ SANE_Word gamma_GRAY[GAMMA_LENGTH];
+ SANE_Word gamma_R[GAMMA_LENGTH];
+ SANE_Word gamma_G[GAMMA_LENGTH];
+ SANE_Word gamma_B[GAMMA_LENGTH];
+}
+Teco_Scanner;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
+
+#define B16TOI(buf) \
+ ((((unsigned char *)buf)[0] << 8) | \
+ (((unsigned char *)buf)[1] << 0))
diff --git a/backend/teco3.c b/backend/teco3.c
new file mode 100644
index 0000000..e66d95d
--- /dev/null
+++ b/backend/teco3.c
@@ -0,0 +1,2255 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+ VM3552 (and maybe VM4552 and VM6552)
+*/
+
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 1 /* 2002/08/06 */
+#define BACKEND_NAME teco3
+#define TECO_CONFIG_FILE "teco3.conf"
+
+/*--------------------------------------------------------------------------*/
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+#include "teco3.h"
+
+/*--------------------------------------------------------------------------*/
+
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list[] = {
+ BLACK_WHITE_STR,
+ GRAY_STR,
+ COLOR_STR,
+ NULL
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Minimum and maximum width and length supported. */
+static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 };
+static SANE_Range y_range = { SANE_FIX (0), SANE_FIX (14 * MM_PER_INCH), 0 };
+
+/*--------------------------------------------------------------------------*/
+
+/* Gamma range */
+static const SANE_Range gamma_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of dithering options. */
+static SANE_String_Const dither_list[] = {
+ "Line art",
+ "2x2",
+ "3x3",
+ "4x4 bayer",
+ "4x4 smooth",
+ "8x8 bayer",
+ "8x8 smooth",
+ "8x8 horizontal",
+ "8x8 vertical",
+ NULL
+};
+static const int dither_val[] = {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08
+};
+
+/*--------------------------------------------------------------------------*/
+
+static const SANE_Range threshold_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Define the supported scanners and their characteristics. */
+static const struct scanners_supported scanners[] = {
+ {
+ 6, "TECO VM3552",
+ TECO_VM3552,
+ "Relisys", "Scorpio",
+ {1, 1200, 1}, /* resolution range */
+ 300, 1200 /* max x and Y resolution */
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* List of scanner attached. */
+static Teco_Scanner *first_dev = NULL;
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+
+
+/* Local functions. */
+
+/* Display a buffer in the log. */
+static void
+hexdump (int level, const char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+ char asc_buf[17];
+ char *asc_ptr;
+
+ DBG (level, "%s\n", comment);
+
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ DBG (level, "%s %s\n", line, asc_buf);
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ ptr += sprintf (ptr, " %2.2x", *p);
+ if (*p >= 32 && *p <= 127)
+ {
+ asc_ptr += sprintf (asc_ptr, "%c", *p);
+ }
+ else
+ {
+ asc_ptr += sprintf (asc_ptr, ".");
+ }
+ }
+ *ptr = '\0';
+ DBG (level, "%s %s\n", line, asc_buf);
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+/* Lookup a string list from one array and return its index. */
+static int
+get_string_list_index (SANE_String_Const list[], SANE_String_Const name)
+{
+ int index;
+
+ index = 0;
+ while (list[index] != NULL)
+ {
+ if (strcmp (list[index], name) == 0)
+ {
+ return (index);
+ }
+ index++;
+ }
+
+ DBG (DBG_error, "name %s not found in list\n", name);
+
+ assert (0 == 1); /* bug in backend, core dump */
+
+ return (-1);
+}
+
+/* Initialize a scanner entry. Return an allocated scanner with some
+ * preset values. */
+static Teco_Scanner *
+teco_init (void)
+{
+ Teco_Scanner *dev;
+
+ DBG (DBG_proc, "teco_init: enter\n");
+
+ /* Allocate a new scanner entry. */
+ dev = malloc (sizeof (Teco_Scanner));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ memset (dev, 0, sizeof (Teco_Scanner));
+
+ /* Allocate the buffer used to transfer the SCSI data. */
+ dev->buffer_size = 64 * 1024;
+ dev->buffer = malloc (dev->buffer_size);
+ if (dev->buffer == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+ dev->sfd = -1;
+
+ DBG (DBG_proc, "teco_init: exit\n");
+
+ return (dev);
+}
+
+/* Closes an open scanner. */
+static void
+teco_close (Teco_Scanner * dev)
+{
+ DBG (DBG_proc, "teco_close: enter\n");
+
+ if (dev->sfd != -1)
+ {
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+ }
+
+ DBG (DBG_proc, "teco_close: exit\n");
+}
+
+/* Frees the memory used by a scanner. */
+static void
+teco_free (Teco_Scanner * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "teco_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ teco_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+
+ free (dev);
+
+ DBG (DBG_proc, "teco_free: exit\n");
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+teco_identify_scanner (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "teco_identify_scanner: enter\n");
+
+ size = 5;
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ size = dev->buffer[4] + 5; /* total length of the inquiry data */
+
+ if (size < 53)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: not enough data to identify device\n");
+ return (SANE_FALSE);
+ }
+
+ MKSCSI_INQUIRY (cdb, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (status)
+ {
+ DBG (DBG_error,
+ "teco_identify_scanner: inquiry failed with status %s\n",
+ sane_strstatus (status));
+ return (SANE_FALSE);
+ }
+
+ hexdump (DBG_info2, "inquiry", dev->buffer, size);
+
+ dev->scsi_type = dev->buffer[0] & 0x1f;
+ memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08);
+ dev->scsi_vendor[0x08] = 0;
+ memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010);
+ dev->scsi_product[0x10] = 0;
+ memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04);
+ dev->scsi_version[0x04] = 0;
+ memcpy (dev->scsi_teco_name, dev->buffer + 0x2A, 0x0B);
+ dev->scsi_teco_name[0x0B] = 0;
+
+ DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\" \"%s\"\n",
+ dev->scsi_vendor, dev->scsi_product, dev->scsi_version,
+ dev->scsi_teco_name);
+
+ /* Lookup through the supported scanners table to find if this
+ * backend supports that one. */
+ for (i = 0; i < NELEMS (scanners); i++)
+ {
+
+ if (dev->scsi_type == scanners[i].scsi_type &&
+ strcmp (dev->scsi_teco_name, scanners[i].scsi_teco_name) == 0)
+ {
+
+ DBG (DBG_error, "teco_identify_scanner: scanner supported\n");
+
+ dev->def = &(scanners[i]);
+
+ return (SANE_TRUE);
+ }
+ }
+
+ DBG (DBG_proc, "teco_identify_scanner: exit, device not supported\n");
+
+ return (SANE_FALSE);
+}
+
+/* SCSI sense handler. Callback for SANE.
+ * These scanners never set asc or ascq. */
+static SANE_Status
+teco_sense_handler (int __sane_unused__ scsi_fd, unsigned char *result, void __sane_unused__ *arg)
+{
+ int sensekey;
+ int len;
+
+ DBG (DBG_proc, "teco_sense_handler: enter\n");
+
+ sensekey = get_RS_sense_key (result);
+ len = 7 + get_RS_additional_length (result);
+
+ hexdump (DBG_info2, "sense", result, len);
+
+ if (get_RS_error_code (result) != 0x70)
+ {
+ DBG (DBG_error,
+ "teco_sense_handler: invalid sense key error code (%d)\n",
+ get_RS_error_code (result));
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (len < 14)
+ {
+ DBG (DBG_error, "teco_sense_handler: sense too short, no ASC/ASCQ\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (DBG_sense, "teco_sense_handler: sense=%d\n", sensekey);
+
+ if (sensekey == 0x00)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/* Set a window. */
+static SANE_Status
+teco_set_window (Teco_Scanner * dev)
+{
+ size_t window_size;
+ CDB cdb;
+ unsigned char window[255];
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "teco_set_window: enter\n");
+
+ /* size of the whole windows block */
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3552:
+ window_size = 69;
+ break;
+ default:
+ assert(0);
+ }
+
+
+ MKSCSI_SET_WINDOW (cdb, window_size);
+
+ memset (window, 0, window_size);
+
+ /* size of the windows descriptor block */
+ window[7] = window_size - 8;
+
+ /* X and Y resolution */
+ Ito16 (dev->x_resolution, &window[10]);
+ Ito16 (dev->y_resolution, &window[12]);
+
+ /* Upper Left (X,Y) */
+ Ito32 (dev->x_tl, &window[14]);
+ Ito32 (dev->y_tl, &window[18]);
+
+ /* Width and length */
+ Ito32 (dev->width, &window[22]);
+ Ito32 (dev->length, &window[26]);
+
+ /* Image Composition */
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ window[31] = dev->val[OPT_THRESHOLD].w;
+ window[33] = 0x00;
+ i = get_string_list_index (dither_list, dev->val[OPT_DITHER].s);
+ window[36] = dither_val[i];
+ break;
+ case TECO_GRAYSCALE:
+ window[31] = 0x80;
+ window[33] = 0x02;
+ break;
+ case TECO_COLOR:
+ window[31] = 0x80;
+ window[33] = 0x05;
+ break;
+ }
+
+ /* Depth */
+ window[34] = dev->depth;
+
+ /* Unknown - invariants */
+ window[37] = 0x80;
+
+ switch (dev->def->tecoref)
+ {
+ case TECO_VM3552:
+ window[48] = 0x01;
+ window[50] = 0x02;
+ window[53] = 0xff;
+ window[57] = 0xff;
+ window[61] = 0xff;
+ window[65] = 0xff;
+ break;
+ }
+
+ hexdump (DBG_info2, "windows", window, window_size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ window, window_size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_set_window: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Park the CCD */
+static SANE_Status
+teco_reset_window (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ CDB cdb;
+
+ DBG (DBG_proc, "teco_reset_window: enter\n");
+
+ MKSCSI_OBJECT_POSITION (cdb, 0);
+
+ hexdump (DBG_info2, "CDB:", cdb.data, cdb.len);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "teco_reset_window: leave, status=%d\n", status);
+
+ return status;
+}
+
+/* Return the number of byte that can be read. */
+static SANE_Status
+get_filled_data_length (Teco_Scanner * dev, size_t * to_read)
+{
+ size_t size;
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "get_filled_data_length: enter\n");
+
+ *to_read = 0;
+
+ size = 0x12;
+ MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size);
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ if (size < 0x10)
+ {
+ DBG (DBG_error,
+ "get_filled_data_length: not enough data returned (%ld)\n",
+ (long) size);
+ }
+
+ hexdump (DBG_info2, "get_filled_data_length return", dev->buffer, size);
+
+ *to_read = B24TOI (&dev->buffer[9]);
+
+ DBG (DBG_info, "%d %d - %d %d\n",
+ dev->params.lines, B16TOI (&dev->buffer[12]),
+ dev->params.bytes_per_line, B16TOI (&dev->buffer[14]));
+
+ if (dev->real_bytes_left == 0)
+ {
+
+ DBG (DBG_error,
+ "get_filled_data_length: internal scanner buffer size is %d bytes\n",
+ B24TOI (&dev->buffer[6]));
+
+ /* Beginning of a scan. */
+ dev->params.lines = B16TOI (&dev->buffer[12]);
+ dev->bytes_per_raster = B16TOI (&dev->buffer[14]);
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ dev->params.bytes_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.pixels_per_line = dev->params.bytes_per_line * 8;
+ break;
+
+ case TECO_GRAYSCALE:
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ break;
+
+ case TECO_COLOR:
+ dev->params.pixels_per_line = B16TOI (&dev->buffer[14]);
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ if (dev->buffer[17] == 0x07)
+ {
+ /* There is no RAM extension present. The colors will
+ * be shifted and the backend will need to fix that.
+ */
+ dev->does_color_shift = 1;
+ }
+ else
+ {
+ dev->does_color_shift = 0;
+ }
+ break;
+ }
+ }
+
+ DBG (DBG_info, "get_filled_data_length: to read = %ld\n", (long) *to_read);
+
+ DBG (DBG_proc, "get_filled_data_length: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Start a scan. */
+static SANE_Status
+teco_scan (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+
+ DBG (DBG_proc, "teco_scan: enter\n");
+
+ MKSCSI_SCAN (cdb);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ DBG (DBG_proc, "teco_scan: exit, status=%d\n", status);
+
+ return status;
+}
+
+/* Do some vendor specific stuff. */
+static SANE_Status
+teco_vendor_spec (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ size_t size;
+
+ DBG (DBG_proc, "teco_vendor_spec: enter\n");
+
+ size = 0x7800;
+
+ cdb.data[0] = 0x09;
+ cdb.data[1] = 0;
+ cdb.data[2] = 0;
+ cdb.data[3] = (size >> 8) & 0xff;
+ cdb.data[4] = (size >> 0) & 0xff;
+ cdb.data[5] = 0;
+ cdb.len = 6;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, dev->buffer, &size);
+
+ /*hexdump (DBG_info2, "calibration:", dev->buffer, size); */
+
+ cdb.data[0] = 0x0E;
+ cdb.data[1] = 0;
+ cdb.data[2] = 0;
+ cdb.data[3] = 0;
+ cdb.data[4] = 0;
+ cdb.data[5] = 0;
+ cdb.len = 6;
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL);
+
+ return status;
+}
+
+/* Send the gamma */
+static SANE_Status
+teco_send_gamma (Teco_Scanner * dev)
+{
+ CDB cdb;
+ SANE_Status status;
+ struct
+ {
+ unsigned char gamma_R[GAMMA_LENGTH];
+ unsigned char gamma_G[GAMMA_LENGTH]; /* also gray */
+ unsigned char gamma_B[GAMMA_LENGTH];
+ unsigned char gamma_unused[GAMMA_LENGTH];
+ }
+ param;
+ size_t i;
+ size_t size;
+
+ DBG (DBG_proc, "teco_send_gamma: enter\n");
+
+ size = sizeof (param);
+ assert (size == 4 * GAMMA_LENGTH);
+ MKSCSI_SEND_10 (cdb, 0x03, 0x02, size);
+
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* Use the custom gamma. */
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ /* Gray */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = 0;
+ param.gamma_G[i] = dev->gamma_GRAY[i];
+ param.gamma_B[i] = 0;
+ param.gamma_unused[i] = 0;
+ }
+ }
+ else
+ {
+ /* Color */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = dev->gamma_R[i];
+ param.gamma_G[i] = dev->gamma_G[i];
+ param.gamma_B[i] = dev->gamma_B[i];
+ param.gamma_unused[i] = 0;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ param.gamma_R[i] = i / 4;
+ param.gamma_G[i] = i / 4;
+ param.gamma_B[i] = i / 4;
+ param.gamma_unused[i] = 0;
+ }
+ }
+
+ hexdump (DBG_info2, "teco_send_gamma:", cdb.data, cdb.len);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ &param, size, NULL, NULL);
+
+ DBG (DBG_proc, "teco_send_gamma: exit, status=%d\n", status);
+
+ return (status);
+}
+
+/* Attach a scanner to this backend. */
+static SANE_Status
+attach_scanner (const char *devicename, Teco_Scanner ** devp)
+{
+ Teco_Scanner *dev;
+ SANE_Status status;
+ int sfd;
+
+ DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new scanner entry. */
+ dev = teco_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_scanner: opening %s\n", devicename);
+
+ status = sanei_scsi_open (devicename, &sfd, teco_sense_handler, dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "ERROR: attach_scanner: open failed (%s)\n",
+ sane_strstatus (status));
+ teco_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Now, check that it is a scanner we support. */
+ if (teco_identify_scanner (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_scanner: scanner-identification failed\n");
+ teco_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ teco_close (dev);
+
+ /* Set the default options for that scanner. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->def->real_vendor;
+ dev->sane.model = dev->def->real_product;
+ dev->sane.type = "flatbed scanner";
+
+ /* Link the scanner with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_scanner: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, NULL);
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that scanner. */
+static void
+teco_init_options (Teco_Scanner * dev)
+{
+ int i;
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scanner supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RESOLUTION].constraint.range = &dev->def->res_range;
+ dev->val[OPT_RESOLUTION].w = 100;
+
+ /* Geometry group */
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Upper left X */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &x_range;
+ dev->val[OPT_TL_X].w = x_range.min;
+
+ /* Upper left Y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &y_range;
+ dev->val[OPT_TL_Y].w = y_range.min;
+
+ /* Bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &x_range;
+ dev->val[OPT_BR_X].w = x_range.max;
+
+ /* Bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &y_range;
+ dev->val[OPT_BR_Y].w = y_range.max;
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Halftone pattern */
+ dev->opt[OPT_DITHER].name = "dither";
+ dev->opt[OPT_DITHER].title = SANE_I18N ("Dither");
+ dev->opt[OPT_DITHER].desc = SANE_I18N ("Dither");
+ dev->opt[OPT_DITHER].type = SANE_TYPE_STRING;
+ dev->opt[OPT_DITHER].size = max_string_size (dither_list);
+ dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_DITHER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_DITHER].constraint.string_list = dither_list;
+ dev->val[OPT_DITHER].s = strdup (dither_list[0]);
+
+ /* custom-gamma table */
+ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* red gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R;
+
+ /* green and gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G;
+
+ /* blue gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B;
+
+ /* grayscale gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &gamma_range;
+ dev->val[OPT_GAMMA_VECTOR_GRAY].wa = dev->gamma_GRAY;
+
+ /* Threshold */
+ dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_THRESHOLD].size = sizeof (SANE_Int);
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_THRESHOLD].constraint.range = &threshold_range;
+ dev->val[OPT_THRESHOLD].w = 128;
+
+ /* preview */
+ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scan_mode_list[0], NULL);
+}
+
+/*
+ * Wait until the scanner is ready.
+ */
+static SANE_Status
+teco_wait_scanner (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ int timeout;
+ CDB cdb;
+
+ DBG (DBG_proc, "teco_wait_scanner: enter\n");
+
+ MKSCSI_TEST_UNIT_READY (cdb);
+
+ /* Set the timeout to 60 seconds. */
+ timeout = 60;
+
+ while (timeout > 0)
+ {
+
+ /* test unit ready */
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, NULL, NULL);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ sleep (1);
+ };
+
+ DBG (DBG_proc, "teco_wait_scanner: scanner not ready\n");
+ return (SANE_STATUS_IO_ERROR);
+}
+
+/*
+ * Get the sense
+ */
+static SANE_Status
+teco_query_sense (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ unsigned char buf[255];
+ CDB cdb;
+ size_t size;
+
+ DBG (DBG_proc, "teco_wait_scanner: enter\n");
+
+ size = sizeof (buf);
+ MKSCSI_REQUEST_SENSE (cdb, size);
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, buf, &size);
+
+ hexdump (DBG_info2, "sense", buf, size);
+
+ DBG (DBG_error, "teco_query_sense: return (%s)\n", sane_strstatus (status));
+ return (status);
+}
+
+/*
+ * Adjust the rasters. This function is used during a color scan,
+ * because the scanner does not present a format sane can interpret
+ * directly.
+ *
+ * The scanner sends the colors by rasters (B then G then R), whereas
+ * sane is waiting for a group of 3 bytes per color. To make things
+ * funnier, the rasters are shifted. The format of those raster is:
+ * BGR...BGR
+ *
+ * For a proper scan, the first 2 R and 1 G, and the last 1 G and 2 B
+ * must be ignored. (TODO)
+ *
+ * So this function reorders all that mess. It gets the input from
+ * dev->buffer and write the output in dev->image. size_in the the
+ * length of the valid data in dev->buffer. */
+
+/* 0=red, 1=green, 2=blue */
+
+#define COLOR_0 2
+#define COLOR_1 1
+#define COLOR_2 0
+#define line_shift ( - dev->color_shift)
+
+static void
+teco_adjust_raster (Teco_Scanner * dev, size_t size_in)
+{
+ int nb_rasters; /* number of rasters in dev->buffer */
+
+ int raster; /* current raster number in buffer */
+ int line; /* line number for that raster */
+ int color = -1; /* color for that raster */
+ size_t offset;
+
+ DBG (DBG_proc, "teco_adjust_raster: enter\n");
+
+ assert (dev->scan_mode == TECO_COLOR);
+ assert ((size_in % dev->bytes_per_raster) == 0);
+
+ if (size_in == 0)
+ {
+ return;
+ }
+
+ /*
+ * The color coding is one line for each color (in the RGB order).
+ * Recombine that stuff to create a RGB value for each pixel.
+ */
+
+ nb_rasters = size_in / dev->raster_size;
+
+ for (raster = 0; raster < nb_rasters; raster++)
+ {
+
+ /*
+ * Find the color to which this raster belongs to.
+ */
+ line = 0;
+ if (dev->raster_num < dev->color_shift)
+ {
+ color = COLOR_0;
+ line = dev->raster_num;
+ }
+ else if (dev->raster_num < (3 * dev->color_shift))
+ {
+
+ if ((dev->raster_num - line_shift) % 2)
+ {
+ color = COLOR_1;
+ line = (dev->raster_num + line_shift) / 2;
+ }
+ else
+ {
+ color = COLOR_0;
+ line = (dev->raster_num - line_shift) / 2;
+ }
+ }
+ else if (dev->raster_num >= dev->raster_real - dev->color_shift)
+ {
+ color = COLOR_2;
+ line = dev->line;
+ }
+ else if (dev->raster_num >= dev->raster_real - 3 * dev->color_shift)
+ {
+ if ((dev->raster_real - dev->raster_num - line_shift) % 2)
+ {
+ color = COLOR_2;
+ line = dev->line;
+ }
+ else
+ {
+ color = COLOR_1;
+ line = dev->line - line_shift;
+ }
+ }
+ else
+ {
+ switch ((dev->raster_num - 3 * line_shift) % 3)
+ {
+ case 0:
+ color = COLOR_0;
+ line = (dev->raster_num - 3 * line_shift) / 3;
+ break;
+ case 1:
+ color = COLOR_1;
+ line = dev->raster_num / 3;
+ break;
+ case 2:
+ color = COLOR_2;
+ line = (dev->raster_num + 3 * line_shift) / 3;
+ break;
+ }
+ }
+
+ /* Adjust the line number relative to the image. */
+ line -= dev->line;
+
+ offset = dev->image_end + line * dev->params.bytes_per_line;
+
+ assert (offset <= (dev->image_size - dev->params.bytes_per_line));
+
+ /* Copy the raster to the temporary image. */
+ {
+ int i;
+ unsigned char *src = dev->buffer + raster * dev->raster_size;
+ unsigned char *dest = dev->image + offset + color;
+
+ for (i = 0; i < dev->raster_size; i++)
+ {
+ *dest = *src;
+ src++;
+ dest += 3;
+ }
+
+ assert (dest <= (dev->image + dev->image_size + 2));
+
+ }
+
+ DBG (DBG_info, "raster=%d, line=%d, color=%d\n", dev->raster_num,
+ dev->line + line, color);
+
+ if (color == COLOR_2)
+ {
+ /* This raster completes a new line */
+ dev->line++;
+ dev->image_end += dev->params.bytes_per_line;
+ }
+
+ dev->raster_num++;
+ }
+
+ DBG (DBG_proc, "teco_adjust_raster: exit\n");
+}
+
+/* Read the image from the scanner and fill the temporary buffer with it. */
+static SANE_Status
+teco_fill_image (Teco_Scanner * dev)
+{
+ SANE_Status status;
+ size_t size;
+ CDB cdb;
+ unsigned char *image;
+
+ DBG (DBG_proc, "teco_fill_image: enter\n");
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ /* Copy the complete lines, plus the incompletes
+ * ones. We don't keep the real end of data used
+ * in image, so we copy the biggest possible.
+ */
+ if (dev->scan_mode == TECO_COLOR)
+ {
+ memmove (dev->image, dev->image + dev->image_begin, dev->raster_ahead);
+ }
+
+ dev->image_begin = 0;
+ dev->image_end = 0;
+
+ while (dev->real_bytes_left)
+ {
+
+ /* todo: teco2 too */
+ /* Check that we can at least one line. */
+ if (dev->raster_ahead + dev->image_end + dev->params.bytes_per_line >
+ dev->image_size)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ /*
+ * Try to read the maximum number of bytes.
+ */
+ size = 0;
+ while (size == 0)
+ {
+ status = get_filled_data_length (dev, &size);
+ if (status)
+ return (status);
+ if (size == 0)
+ usleep (100000); /* sleep 1/10th of second */
+ }
+
+ if (size > dev->real_bytes_left)
+ size = dev->real_bytes_left;
+ if (size > dev->image_size - dev->raster_ahead - dev->image_end)
+ size = dev->image_size - dev->raster_ahead - dev->image_end;
+ if (size > dev->buffer_size)
+ {
+ size = dev->buffer_size;
+ }
+
+ /* Always read a multiple of a raster. */
+ size = size - (size % dev->bytes_per_raster);
+
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer.
+ * Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_info, "teco_fill_image: to read = %ld bytes (bpl=%d)\n",
+ (long) size, dev->params.bytes_per_line);
+
+ MKSCSI_READ_10 (cdb, 0, 0, size);
+
+ hexdump (DBG_info2, "teco_fill_image: READ_10 CDB", cdb.data, 10);
+ DBG (DBG_info, " image_end=%lu\n", (u_long) dev->image_end);
+
+ if (dev->scan_mode == TECO_COLOR && dev->does_color_shift)
+ {
+ image = dev->buffer;
+ }
+ else
+ {
+ image = dev->image + dev->image_end;
+ }
+
+ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len,
+ NULL, 0, image, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "teco_fill_image: cannot read from the scanner\n");
+ return status;
+ }
+
+ /* The size this scanner returns is always a multiple of a
+ * raster size. */
+ assert ((size % dev->bytes_per_raster) == 0);
+
+ DBG (DBG_info, "teco_fill_image: real bytes left = %ld\n",
+ (long) dev->real_bytes_left);
+
+ if (dev->scan_mode == TECO_COLOR && dev->does_color_shift)
+ {
+ teco_adjust_raster (dev, size);
+ }
+ else
+ {
+ /* Already in dev->image. */
+ dev->image_end += size;
+ }
+
+ dev->real_bytes_left -= size;
+ }
+
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+/* Copy from the raw buffer to the buffer given by the backend.
+ *
+ * len in input is the maximum length available in buf, and, in
+ * output, is the length written into buf.
+ */
+static void
+teco_copy_raw_to_frontend (Teco_Scanner * dev, SANE_Byte * buf, size_t * len)
+{
+ size_t size;
+
+ size = dev->image_end - dev->image_begin;
+ if (size > *len)
+ {
+ size = *len;
+ }
+ *len = size;
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ {
+ /* For Black & White, the bits in every bytes are mirrored.
+ * for instance 11010001 is coded as 10001011 */
+
+ unsigned char *src = dev->image + dev->image_begin;
+ size_t i;
+ unsigned char s;
+ unsigned char d;
+
+ for (i = 0; i < size; i++)
+ {
+ s = *src ^ 0xff;
+ d = 0;
+ if (s & 0x01)
+ d |= 0x80;
+ if (s & 0x02)
+ d |= 0x40;
+ if (s & 0x04)
+ d |= 0x20;
+ if (s & 0x08)
+ d |= 0x10;
+ if (s & 0x10)
+ d |= 0x08;
+ if (s & 0x20)
+ d |= 0x04;
+ if (s & 0x40)
+ d |= 0x02;
+ if (s & 0x80)
+ d |= 0x01;
+ *buf = d;
+ src++;
+ buf++;
+ }
+ }
+ break;
+
+ case TECO_GRAYSCALE:
+ case TECO_COLOR:
+ memcpy (buf, dev->image + dev->image_begin, size);
+ break;
+ }
+
+ dev->image_begin += size;
+}
+
+/* Stop a scan. */
+static SANE_Status
+do_cancel (Teco_Scanner * dev)
+{
+ DBG (DBG_sane_proc, "do_cancel enter\n");
+
+ if (dev->scanning == SANE_TRUE)
+ {
+ teco_reset_window (dev);
+ teco_close (dev);
+ }
+
+ dev->scanning = SANE_FALSE;
+
+ DBG (DBG_sane_proc, "do_cancel exit\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Sane entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ char dev_name[PATH_MAX];
+ size_t len;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ DBG (DBG_error, "This is sane-teco3 version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2002 by Frank Zago\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ fp = sanei_config_open (TECO_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to /dev/scanner instead of insisting on config file */
+ attach_scanner ("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
+{
+ Teco_Scanner *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Teco_Scanner *dev;
+ SANE_Status status;
+ int i;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No scanner found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ teco_init_options (dev);
+
+ /* Initialize the gamma table. */
+ for (i = 0; i < GAMMA_LENGTH; i++)
+ {
+ dev->gamma_R[i] = i / 4;
+ dev->gamma_G[i] = i / 4;
+ dev->gamma_B[i] = i / 4;
+ dev->gamma_GRAY[i] = i / 4;
+ }
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Teco_Scanner *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ name = dev->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_THRESHOLD:
+ case OPT_PREVIEW:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_MODE:
+ case OPT_DITHER:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* Gamma */
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (val, dev->val[option].wa, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Numeric side-effect options */
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_BR_X:
+ case OPT_THRESHOLD:
+ case OPT_RESOLUTION:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* Numeric side-effect free options */
+ case OPT_PREVIEW:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect free options */
+ case OPT_DITHER:
+ free (dev->val[option].s);
+ dev->val[option].s = (SANE_String) strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect options */
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, BLACK_WHITE_STR) == 0)
+ {
+ dev->depth = 8;
+ dev->scan_mode = TECO_BW;
+ dev->opt[OPT_DITHER].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, GRAY_STR) == 0)
+ {
+ dev->scan_mode = TECO_GRAYSCALE;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ dev->depth = 8;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_STR) == 0)
+ {
+ dev->scan_mode = TECO_COLOR;
+ dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ dev->depth = 8;
+ }
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (dev->val[option].wa, val, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (dev->val[OPT_CUSTOM_GAMMA].w)
+ {
+ /* use custom_gamma_table */
+ if (dev->scan_mode == TECO_GRAYSCALE)
+ {
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Setup the parameters for the scan. These values will be re-used
+ * in the SET WINDOWS command. */
+ if (dev->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ dev->x_resolution = 50;
+ dev->y_resolution = 50;
+ dev->x_tl = 0;
+ dev->y_tl = 0;
+ dev->x_br = mmToIlu (SANE_UNFIX (x_range.max));
+ dev->y_br = mmToIlu (SANE_UNFIX (y_range.max));
+ }
+ else
+ {
+ dev->x_resolution = dev->val[OPT_RESOLUTION].w;
+ dev->y_resolution = dev->val[OPT_RESOLUTION].w;
+ if (dev->x_resolution > dev->def->x_resolution_max)
+ {
+ dev->x_resolution = dev->def->x_resolution_max;
+ }
+
+ dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
+ dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
+ dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
+ dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
+ }
+
+ /* Check the corners are OK. */
+ if (dev->x_tl > dev->x_br)
+ {
+ int s;
+ s = dev->x_tl;
+ dev->x_tl = dev->x_br;
+ dev->x_br = s;
+ }
+ if (dev->y_tl > dev->y_br)
+ {
+ int s;
+ s = dev->y_tl;
+ dev->y_tl = dev->y_br;
+ dev->y_br = s;
+ }
+
+ dev->width = dev->x_br - dev->x_tl;
+ dev->length = dev->y_br - dev->y_tl;
+
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.last_frame = SANE_TRUE;
+
+ switch (dev->scan_mode)
+ {
+ case TECO_BW:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / 300) & ~0x7;
+ dev->params.bytes_per_line = dev->params.pixels_per_line / 8;
+ dev->params.depth = 1;
+ dev->color_shift = 0;
+ break;
+ case TECO_GRAYSCALE:
+ dev->params.format = SANE_FRAME_GRAY;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / 300);
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ dev->params.depth = 8;
+ dev->color_shift = 0;
+ break;
+ case TECO_COLOR:
+ dev->params.format = SANE_FRAME_RGB;
+ dev->params.pixels_per_line =
+ ((dev->width * dev->x_resolution) / 300);
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->params.depth = 8;
+
+ /* If the scanner does not have enough memory, it will
+ * send the raw rasters instead of returning a full
+ * interleaved line. Unfortunatly this does not work well,
+ * because I don't know how to compute the color
+ * shifting. So here is the result of some trial and error
+ * process. This is ignored if the scanner has a RAM
+ * module.
+ */
+ dev->color_shift = dev->x_resolution / 75;
+
+ break;
+ }
+
+ dev->params.lines = (dev->length * dev->y_resolution) / 300;
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+ SANE_Status status;
+ size_t size;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+
+ /* Open again the scanner. */
+ if (sanei_scsi_open
+ (dev->devicename, &(dev->sfd), teco_sense_handler, dev) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Set the correct parameters. */
+ sane_get_parameters (dev, NULL);
+
+ /* The scanner must be ready. */
+ status = teco_wait_scanner (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ teco_query_sense (dev);
+ teco_reset_window (dev);
+
+ status = teco_set_window (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ dev->real_bytes_left = 0;
+ status = get_filled_data_length (dev, &size);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ /* Compute the length necessary in image. The first part will store
+ * the complete lines, and the rest is used to stored ahead
+ * rasters.
+ * Align image_size to a multiple of lines. (important)
+ */
+ dev->raster_ahead =
+ (2 * dev->color_shift + 1) * dev->params.bytes_per_line;
+ dev->image_size = dev->buffer_size + dev->raster_ahead;
+ dev->image_size =
+ dev->image_size - (dev->image_size % dev->params.bytes_per_line);
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Rasters are meaningfull only in color mode. */
+ dev->raster_size = dev->params.pixels_per_line;
+ dev->raster_real = dev->params.lines * 3;
+ dev->raster_num = 0;
+ dev->line = 0;
+
+ teco_vendor_spec (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_send_gamma (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_set_window (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+
+ status = teco_scan (dev);
+ if (status)
+ {
+ teco_close (dev);
+ return status;
+ }
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+ dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Teco_Scanner *dev = handle;
+ size_t size;
+ int buf_offset; /* offset into buf */
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning */
+ return do_cancel (dev);
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ buf_offset = 0;
+
+ do
+ {
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = teco_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Copy the data to the frontend buffer. */
+ size = max_len - buf_offset;
+ if (size > dev->bytes_left)
+ {
+ size = dev->bytes_left;
+ }
+ teco_copy_raw_to_frontend (dev, buf + buf_offset, &size);
+
+ buf_offset += size;
+
+ dev->bytes_left -= size;
+ *len += size;
+
+ }
+ while ((buf_offset != max_len) && dev->bytes_left);
+
+ DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n",
+ (long) dev->bytes_left);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ SANE_Status status;
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ if (dev->scanning == SANE_FALSE)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (non_blocking == SANE_FALSE)
+ {
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ do_cancel (dev);
+
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Teco_Scanner *dev = handle;
+ Teco_Scanner *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+ do_cancel (dev);
+ teco_close (dev);
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ teco_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}
diff --git a/backend/teco3.conf.in b/backend/teco3.conf.in
new file mode 100644
index 0000000..8fbe733
--- /dev/null
+++ b/backend/teco3.conf.in
@@ -0,0 +1,11 @@
+# VM3552
+scsi "" "Flat-bed scanner" Scanner
+scsi "RELISYS" "Scorpio"
+
+# Trust Imagery 2400SP
+scsi "Aashima" "IMAGERY 2400SP" Scanner * * * 0
+
+# Trust Imagery 4800 SP +:
+scsi "Aashima" "IMAGERY 4800SP +"
+
+/dev/scanner
diff --git a/backend/teco3.h b/backend/teco3.h
new file mode 100644
index 0000000..64e894d
--- /dev/null
+++ b/backend/teco3.h
@@ -0,0 +1,427 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Frank Zago (sane at zago dot net)
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/*
+ $Id$
+*/
+
+/* Commands supported by the scanner. */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_VENDOR_09 0x09
+#define SCSI_VENDOR_0E 0x0E
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT 0x15
+#define SCSI_SCAN 0x1b
+#define SCSI_SET_WINDOW 0x24
+#define SCSI_SEND_10 0x2a
+#define SCSI_READ_10 0x28
+#define SCSI_OBJECT_POSITION 0x31
+#define SCSI_GET_DATA_BUFFER_STATUS 0x34
+
+
+typedef struct
+{
+ unsigned char data[16];
+ int len;
+}
+CDB;
+
+
+/* Set a specific bit depending on a boolean.
+ * MKSCSI_BIT(TRUE, 3) will generate 0x08. */
+#define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0)
+
+/* Set a value in a range of bits.
+ * MKSCSI_I2B(5, 3, 5) will generate 0x28 */
+#define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1))
+
+/* Store an integer in 2, 3 or 4 byte in an array. */
+#define Ito16(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito24(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \
+}
+
+#define Ito32(val, buf) { \
+ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \
+ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \
+ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \
+ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \
+}
+
+#define MKSCSI_GET_DATA_BUFFER_STATUS(cdb, wait, buflen) \
+ cdb.data[0] = SCSI_GET_DATA_BUFFER_STATUS; \
+ cdb.data[1] = MKSCSI_BIT(wait, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_INQUIRY(cdb, buflen) \
+ cdb.data[0] = SCSI_INQUIRY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_MODE_SELECT(cdb, pf, sp, buflen) \
+ cdb.data[0] = SCSI_MODE_SELECT; \
+ cdb.data[1] = MKSCSI_BIT(pf, 4) | MKSCSI_BIT(sp, 0); \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = buflen; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_OBJECT_POSITION(cdb, position) \
+ cdb.data[0] = SCSI_OBJECT_POSITION; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (((position) >> 16) & 0xff); \
+ cdb.data[3] = (((position) >> 8) & 0xff); \
+ cdb.data[4] = (((position) >> 0) & 0xff); \
+ cdb.data[5] = 0; \
+ cdb.data[6] = 0; \
+ cdb.data[7] = 0; \
+ cdb.data[8] = 0; \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_SET_WINDOW(cdb, buflen) \
+ cdb.data[0] = SCSI_SET_WINDOW; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_READ_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_REQUEST_SENSE(cdb, buflen) \
+ cdb.data[0] = SCSI_REQUEST_SENSE; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (buflen); \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SCAN(cdb) \
+ cdb.data[0] = SCSI_SCAN; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+#define MKSCSI_SEND_10(cdb, dtc, dtq, buflen) \
+ cdb.data[0] = SCSI_SEND_10; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = (dtc); \
+ cdb.data[3] = 0; \
+ cdb.data[4] = (((dtq) >> 8) & 0xff); \
+ cdb.data[5] = (((dtq) >> 0) & 0xff); \
+ cdb.data[6] = (((buflen) >> 16) & 0xff); \
+ cdb.data[7] = (((buflen) >> 8) & 0xff); \
+ cdb.data[8] = (((buflen) >> 0) & 0xff); \
+ cdb.data[9] = 0; \
+ cdb.len = 10;
+
+#define MKSCSI_TEST_UNIT_READY(cdb) \
+ cdb.data[0] = SCSI_TEST_UNIT_READY; \
+ cdb.data[1] = 0; \
+ cdb.data[2] = 0; \
+ cdb.data[3] = 0; \
+ cdb.data[4] = 0; \
+ cdb.data[5] = 0; \
+ cdb.len = 6;
+
+/*--------------------------------------------------------------------------*/
+
+static inline int
+getbitfield (unsigned char *pageaddr, int mask, int shift)
+{
+ return ((*pageaddr >> shift) & mask);
+}
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7)
+
+/*--------------------------------------------------------------------------*/
+
+#define mmToIlu(mm) (((mm) * 300) / MM_PER_INCH)
+#define iluToMm(ilu) (((ilu) * MM_PER_INCH) / 300)
+
+/*--------------------------------------------------------------------------*/
+
+#define GAMMA_LENGTH 0x400 /* number of value per color */
+
+/*--------------------------------------------------------------------------*/
+
+enum Teco_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE, /* scanner modes */
+ OPT_RESOLUTION, /* X and Y resolution */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* upper left X */
+ OPT_TL_Y, /* upper left Y */
+ OPT_BR_X, /* bottom right X */
+ OPT_BR_Y, /* bottom right Y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_CUSTOM_GAMMA, /* Use the custom gamma tables */
+ OPT_GAMMA_VECTOR_R, /* Custom Red gamma table */
+ OPT_GAMMA_VECTOR_G, /* Custom Green Gamma table */
+ OPT_GAMMA_VECTOR_B, /* Custom Blue Gamma table */
+ OPT_GAMMA_VECTOR_GRAY, /* Custom Grayscale Gamma table */
+ OPT_THRESHOLD, /* Threshold */
+ OPT_DITHER,
+ OPT_PREVIEW,
+
+ /* must come last: */
+ OPT_NUM_OPTIONS
+};
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Scanner supported by this backend.
+ */
+struct scanners_supported
+{
+ int scsi_type;
+ char scsi_teco_name[12]; /* real name of the scanner */
+ enum
+ {
+ TECO_VM3552
+ }
+ tecoref;
+ char *real_vendor; /* brand on the box */
+ char *real_product; /* name on the box */
+
+ SANE_Range res_range;
+
+ int x_resolution_max; /* maximum X dpi */
+ int y_resolution_max; /* maximum Y dpi */
+};
+
+/*--------------------------------------------------------------------------*/
+
+#define BLACK_WHITE_STR SANE_VALUE_SCAN_MODE_LINEART
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+/*--------------------------------------------------------------------------*/
+
+/* Define a scanner occurence. */
+typedef struct Teco_Scanner
+{
+ struct Teco_Scanner *next;
+ SANE_Device sane;
+
+ char *devicename;
+ int sfd; /* device handle */
+
+ /* Infos from inquiry. */
+ char scsi_type;
+ char scsi_vendor[9];
+ char scsi_product[17];
+ char scsi_version[5];
+ char scsi_teco_name[12]; /* real name of the scanner */
+
+ /* SCSI handling */
+ size_t buffer_size; /* size of the buffer */
+ SANE_Byte *buffer; /* for SCSI transfer. */
+
+ /* Scanner infos. */
+ const struct scanners_supported *def; /* default options for that scanner */
+
+ /* Scanning handling. */
+ int scanning; /* TRUE if a scan is running. */
+ int x_resolution; /* X resolution in DPI */
+ int y_resolution; /* Y resolution in DPI */
+ int x_tl; /* X top left */
+ int y_tl; /* Y top left */
+ int x_br; /* X bottom right */
+ int y_br; /* Y bottom right */
+ int width; /* width of the scan area in mm */
+ int length; /* length of the scan area in mm */
+
+ enum
+ {
+ TECO_BW,
+ TECO_GRAYSCALE,
+ TECO_COLOR
+ }
+ scan_mode;
+
+ int depth; /* depth per color */
+
+ size_t bytes_left; /* number of bytes left to give to the backend */
+
+ size_t real_bytes_left; /* number of bytes left the scanner will return. */
+ size_t bytes_per_raster; /* bytes per raster. In B&W and Gray,
+ that the same as
+ param.bytes_per_lines. In Color,
+ it's a third.
+ */
+
+ SANE_Byte *image; /* keep the raw image here */
+ size_t image_size; /* allocated size of image */
+ size_t image_begin; /* first significant byte in image */
+ size_t image_end; /* first free byte in image */
+
+ int does_color_shift; /* in color mode only, do we need to
+ * apply the color shifting algorithm?
+ * It is necessary for the VM3552
+ * without a RAM extension. With the
+ * RAM extension, the scanner does
+ * it. */
+
+ int color_shift; /* for color scan: number of lines to
+ * shift the colors. The higher the
+ * resolution, the higher this
+ * number. */
+
+ int raster_size; /* size of a raster */
+ int raster_num; /* for color scan, current raster read */
+ int raster_real; /* real number of raster in the
+ * scan. This is necessary since I
+ * don't know how to reliably compute
+ * the number of lines */
+ int raster_ahead; /* max size of the incomplete lines */
+ int line; /* current line of the scan */
+
+ SANE_Parameters params;
+
+ /* Options */
+ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS];
+ Option_Value val[OPT_NUM_OPTIONS];
+
+ /* Gamma table. 1 array per colour. */
+ SANE_Word gamma_GRAY[GAMMA_LENGTH];
+ SANE_Word gamma_R[GAMMA_LENGTH];
+ SANE_Word gamma_G[GAMMA_LENGTH];
+ SANE_Word gamma_B[GAMMA_LENGTH];
+}
+Teco_Scanner;
+
+/*--------------------------------------------------------------------------*/
+
+/* Debug levels.
+ * Should be common to all backends. */
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+/*--------------------------------------------------------------------------*/
+
+/* 32 bits from an array to an integer (eg ntohl). */
+#define B32TOI(buf) \
+ ((((unsigned char *)buf)[0] << 24) | \
+ (((unsigned char *)buf)[1] << 16) | \
+ (((unsigned char *)buf)[2] << 8) | \
+ (((unsigned char *)buf)[3] << 0))
+
+#define B24TOI(buf) \
+ ((((unsigned char *)buf)[0] << 16) | \
+ (((unsigned char *)buf)[1] << 8) | \
+ (((unsigned char *)buf)[2] << 0))
+
+#define B16TOI(buf) \
+ ((((unsigned char *)buf)[0] << 8) | \
+ (((unsigned char *)buf)[1] << 0))
diff --git a/backend/test-picture.c b/backend/test-picture.c
new file mode 100644
index 0000000..e2d81ce
--- /dev/null
+++ b/backend/test-picture.c
@@ -0,0 +1,849 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2002 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements test picture functions for the test backend.
+*/
+
+#define BUFFER_SIZE (64 * 1024)
+
+static SANE_Bool little_endian (void);
+
+static SANE_Status
+init_picture_buffer (Test_Device * test_device, SANE_Byte ** buffer,
+ size_t * buffer_size)
+{
+ SANE_Word pattern_size = 0, pattern_distance = 0;
+ SANE_Word line_count, b_size;
+ SANE_Word lines = 0;
+ SANE_Word bpl = test_device->bytes_per_line;
+ SANE_Word ppl = test_device->pixels_per_line;
+ SANE_Byte *b;
+ SANE_Bool is_little_endian = little_endian ();
+
+ if (test_device->val[opt_invert_endianess].w)
+ is_little_endian ^= 1;
+
+ DBG (2, "(child) init_picture_buffer test_device=%p, buffer=%p, "
+ "buffer_size=%p\n",(void*)test_device,(void*)buffer,(void*)buffer_size);
+
+ if (strcmp (test_device->val[opt_test_picture].s, "Solid black") == 0
+ || strcmp (test_device->val[opt_test_picture].s, "Solid white") == 0)
+ {
+ SANE_Byte pattern = 0;
+
+ b_size = BUFFER_SIZE;
+ if (buffer_size)
+ *buffer_size = b_size;
+
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (buffer)
+ *buffer = b;
+
+ if (strcmp (test_device->val[opt_test_picture].s, "Solid black") == 0)
+ {
+ DBG (3, "(child) init_picture_buffer: drawing solid black test "
+ "picture %d bytes\n", b_size);
+ if (test_device->params.format == SANE_FRAME_GRAY
+ && test_device->params.depth == 1)
+ pattern = 0xff;
+ else
+ pattern = 0x00;
+ }
+ else
+ {
+ DBG (3, "(child) init_picture_buffer: drawing solid white test "
+ "picture %d bytes\n", b_size);
+ if (test_device->params.format == SANE_FRAME_GRAY
+ && test_device->params.depth == 1)
+ pattern = 0x00;
+ else
+ pattern = 0xff;
+ }
+ memset (b, pattern, b_size);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* Grid */
+ if (strcmp (test_device->val[opt_test_picture].s, "Grid") == 0)
+ {
+ double p_size = (10.0 * SANE_UNFIX (test_device->val[opt_resolution].w)
+ / MM_PER_INCH);
+ SANE_Word increment = 1;
+ if (test_device->params.format == SANE_FRAME_RGB)
+ increment *= 3;
+ if (test_device->params.depth == 16)
+ increment *= 2;
+
+ lines = 2 * p_size + 0.5;
+ b_size = lines * bpl;
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing grid test picture "
+ "%d bytes, %d bpl, %d ppl, %d lines\n", b_size, bpl, ppl, lines);
+
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = 0;
+
+ for (x = 0; x < bpl; x += increment)
+ {
+ SANE_Word x1;
+ SANE_Byte color = 0;
+
+ if (test_device->params.depth == 1)
+ {
+ if (test_device->params.format == SANE_FRAME_GRAY ||
+ (test_device->params.format >= SANE_FRAME_RED &&
+ test_device->params.format <= SANE_FRAME_BLUE))
+ {
+ SANE_Byte value = 0;
+ for (x1 = 0; x1 < 8; x1++)
+ {
+ SANE_Word xfull = x * 8 + (7 - x1);
+
+ if (xfull < ppl)
+ {
+ if ((((SANE_Word) (xfull / p_size)) % 2)
+ ^ !(line_count >
+ (SANE_Word) (p_size + 0.5)))
+ color = 0x0;
+ else
+ color = 0x1;
+ }
+ else
+ color = (rand ()) & 0x01;
+ value |= (color << x1);
+ }
+ b[line_count * bpl + x] = value;
+ }
+ else /* SANE_FRAME_RGB */
+ {
+ SANE_Byte value = 0;
+ for (x1 = 0; x1 < 8; x1++)
+ {
+ SANE_Word xfull = x * 8 / 3 + (7 - x1);
+
+ if (xfull < ppl)
+ {
+ if (((SANE_Word) (xfull / p_size) % 2)
+ ^ (line_count > (SANE_Word) (p_size + 0.5)))
+ color = 0x0;
+ else
+ color = 0x1;
+ }
+ else
+ color = (rand ()) & 0x01;
+ value |= (color << x1);
+ }
+ for (x1 = 0; x1 < increment; x1++)
+ b[line_count * bpl + x + x1] = value;
+ }
+ }
+ else /* depth = 8, 16 */
+ {
+ if (x / increment < ppl)
+ if ((((SANE_Int) (x / increment / p_size)) % 2)
+ ^ (line_count > (SANE_Int) (p_size + 0.5)))
+ color = 0x00;
+ else
+ color = 0xff;
+ else
+ color = (rand ()) & 0xff;
+
+ for (x1 = 0; x1 < increment; x1++)
+ b[line_count * bpl + x + x1] = color;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ /* Color patterns */
+ if (test_device->params.format == SANE_FRAME_GRAY
+ && test_device->params.depth == 1)
+ {
+ /* 1 bit black/white */
+ pattern_size = 16;
+ pattern_distance = 0;
+ lines = 2 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing b/w test picture "
+ "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 255, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = 0;
+
+ if (line_count >= lines / 2)
+ x += (pattern_size + pattern_distance) / 8;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+
+ width = pattern_size / 8;
+ if (x + width >= bpl)
+ width = bpl - x;
+ memset (b + line_count * bpl + x, 0x00, width);
+ x += (pattern_size + pattern_distance) * 2 / 8;
+ }
+ }
+ }
+ else if (test_device->params.format == SANE_FRAME_GRAY
+ && test_device->params.depth == 8)
+ {
+ /* 8 bit gray */
+ pattern_size = 4;
+ pattern_distance = 1;
+ lines = 2 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing 8 bit gray test picture "
+ "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = pattern_distance;
+
+ if (line_count % (pattern_size + pattern_distance)
+ < pattern_distance)
+ continue;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Byte color;
+
+ width = pattern_size;
+ if (x + width >= bpl)
+ width = bpl - x;
+ if (line_count > (pattern_size + pattern_distance))
+ color =
+ 0xff - ((x / (pattern_size + pattern_distance)) & 0xff);
+ else
+ color = (x / (pattern_size + pattern_distance)) & 0xff;
+ memset (b + line_count * bpl + x, color, width);
+ x += (pattern_size + pattern_distance);
+ }
+ }
+
+ }
+ else if (test_device->params.format == SANE_FRAME_GRAY
+ && test_device->params.depth == 16)
+ {
+ /* 16 bit gray */
+ pattern_size = 256;
+ pattern_distance = 4;
+ lines = 1 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing 16 bit gray test picture "
+ "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = pattern_distance * 2;
+
+ if (line_count % (pattern_size + pattern_distance)
+ < pattern_distance)
+ continue;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Word x1;
+ SANE_Byte pattern_lo, pattern_hi;
+
+ width = pattern_size * 2;
+ if (x + width >= bpl)
+ width = bpl - x;
+ pattern_lo =
+ ((line_count - pattern_distance)
+ % (pattern_size + pattern_distance)) & 0xff;
+ for (x1 = 0; x1 < width; x1 += 2)
+ {
+ pattern_hi = (x1 / 2) & 0xff;
+ if (is_little_endian)
+ {
+ b[line_count * bpl + x + x1 + 0] = pattern_lo;
+ b[line_count * bpl + x + x1 + 1] = pattern_hi;
+ }
+ else
+ {
+ b[line_count * bpl + x + x1 + 0] = pattern_hi;
+ b[line_count * bpl + x + x1 + 1] = pattern_lo;
+ }
+ }
+ x += ((pattern_size + pattern_distance) * 2);
+ }
+ }
+
+ }
+ else if (test_device->params.format == SANE_FRAME_RGB
+ && test_device->params.depth == 1)
+ {
+ /* 1 bit color */
+ pattern_size = 16;
+ pattern_distance = 0;
+ lines = 2 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing color lineart test "
+ "picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = 0;
+ SANE_Byte color = 0, color_r = 0, color_g = 0, color_b = 0;
+
+ if (line_count >= lines / 2)
+ color = 7;
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Word x2 = 0;
+
+ width = pattern_size / 8 * 3;
+ if (x + width >= bpl)
+ width = bpl - x;
+
+ color_b = (color & 1) * 0xff;
+ color_g = ((color >> 1) & 1) * 0xff;
+ color_r = ((color >> 2) & 1) * 0xff;
+
+ for (x2 = 0; x2 < width; x2 += 3)
+ {
+ b[line_count * bpl + x + x2 + 0] = color_r;
+ b[line_count * bpl + x + x2 + 1] = color_g;
+ b[line_count * bpl + x + x2 + 2] = color_b;
+ }
+ if (line_count < lines / 2)
+ {
+ ++color;
+ if (color >= 8)
+ color = 0;
+ }
+ else
+ {
+ if (color == 0)
+ color = 8;
+ --color;
+ }
+ x += ((pattern_size + pattern_distance) / 8 * 3);
+ }
+ }
+ }
+ else if ((test_device->params.format == SANE_FRAME_RED
+ || test_device->params.format == SANE_FRAME_GREEN
+ || test_device->params.format == SANE_FRAME_BLUE)
+ && test_device->params.depth == 1)
+ {
+ /* 1 bit color three-pass */
+ pattern_size = 16;
+ pattern_distance = 0;
+ lines = 2 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing color lineart three-pass "
+ "test picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = 0;
+ SANE_Byte color = 0, color_r = 0, color_g = 0, color_b = 0;
+
+ if (line_count >= lines / 2)
+ color = 7;
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Word x2 = 0;
+
+ width = pattern_size / 8;
+ if (x + width >= bpl)
+ width = bpl - x;
+
+ color_b = (color & 1) * 0xff;
+ color_g = ((color >> 1) & 1) * 0xff;
+ color_r = ((color >> 2) & 1) * 0xff;
+
+ for (x2 = 0; x2 < width; x2++)
+ {
+ if (test_device->params.format == SANE_FRAME_RED)
+ b[line_count * bpl + x + x2] = color_r;
+ else if (test_device->params.format == SANE_FRAME_GREEN)
+ b[line_count * bpl + x + x2] = color_g;
+ else
+ b[line_count * bpl + x + x2] = color_b;
+ }
+ if (line_count < lines / 2)
+ {
+ ++color;
+ if (color >= 8)
+ color = 0;
+ }
+ else
+ {
+ if (color == 0)
+ color = 8;
+ --color;
+ }
+ x += (pattern_size + pattern_distance) / 8;
+ }
+ }
+ }
+ else if (test_device->params.format == SANE_FRAME_RGB
+ && test_device->params.depth == 8)
+ {
+ /* 8 bit color */
+ pattern_size = 4;
+ pattern_distance = 1;
+ lines = 6 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing 8 bit color test picture "
+ "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = pattern_distance * 3;
+
+ if (line_count % (pattern_size + pattern_distance)
+ < pattern_distance)
+ continue;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Byte color = 0, color_r = 0, color_g = 0, color_b = 0;
+ SANE_Word x1;
+
+ width = pattern_size * 3;
+ if (x + width >= bpl)
+ width = bpl - x;
+
+ if ((line_count / (pattern_size + pattern_distance)) & 1)
+ color =
+ 0xff - ((x / ((pattern_size + pattern_distance) * 3))
+ & 0xff);
+ else
+ color = (x / ((pattern_size + pattern_distance) * 3)) & 0xff;
+
+ if (line_count / (pattern_size + pattern_distance) < 2)
+ {
+ color_r = color;
+ color_g = 0;
+ color_b = 0;
+ }
+ else if (line_count / (pattern_size + pattern_distance) < 4)
+ {
+ color_r = 0;
+ color_g = color;
+ color_b = 0;
+ }
+ else
+ {
+ color_r = 0;
+ color_g = 0;
+ color_b = color;
+ }
+
+ for (x1 = 0; x1 < width; x1 += 3)
+ {
+ b[line_count * bpl + x + x1 + 0] = color_r;
+ b[line_count * bpl + x + x1 + 1] = color_g;
+ b[line_count * bpl + x + x1 + 2] = color_b;
+ }
+
+ x += ((pattern_size + pattern_distance) * 3);
+ }
+ }
+
+ }
+ else if ((test_device->params.format == SANE_FRAME_RED
+ || test_device->params.format == SANE_FRAME_GREEN
+ || test_device->params.format == SANE_FRAME_BLUE)
+ && test_device->params.depth == 8)
+ {
+ /* 8 bit color three-pass */
+ pattern_size = 4;
+ pattern_distance = 1;
+ lines = 6 * (pattern_size + pattern_distance);
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing 8 bit color three-pass "
+ "test picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = pattern_distance;
+
+ if (line_count % (pattern_size + pattern_distance)
+ < pattern_distance)
+ continue;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Byte color = 0;
+
+ width = pattern_size;
+ if (x + width >= bpl)
+ width = bpl - x;
+
+ if ((line_count / (pattern_size + pattern_distance)) & 1)
+ color =
+ 0xff - (x / ((pattern_size + pattern_distance)) & 0xff);
+ else
+ color = (x / (pattern_size + pattern_distance)) & 0xff;
+
+ if (line_count / (pattern_size + pattern_distance) < 2)
+ {
+ if (test_device->params.format != SANE_FRAME_RED)
+ color = 0x00;
+ }
+ else if (line_count / (pattern_size + pattern_distance) < 4)
+ {
+ if (test_device->params.format != SANE_FRAME_GREEN)
+ color = 0x00;
+ }
+ else
+ {
+ if (test_device->params.format != SANE_FRAME_BLUE)
+ color = 0x00;
+ }
+ memset (b + line_count * bpl + x, color, width);
+
+ x += (pattern_size + pattern_distance);
+ }
+ }
+ }
+ else if (test_device->params.format == SANE_FRAME_RGB
+ && test_device->params.depth == 16)
+ {
+ /* 16 bit color */
+ pattern_size = 256;
+ pattern_distance = 4;
+ lines = pattern_size + pattern_distance;
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3,
+ "(child) init_picture_buffer: drawing 16 bit color test picture "
+ "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = pattern_distance * 2 * 3;
+
+ if (line_count % (pattern_size + pattern_distance)
+ < pattern_distance)
+ continue;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Word x1;
+ SANE_Byte color_hi = 0, color_lo = 0;
+ SANE_Byte color_hi_r = 0, color_lo_r = 0;
+ SANE_Byte color_hi_g = 0, color_lo_g = 0;
+ SANE_Byte color_hi_b = 0, color_lo_b = 0;
+
+ width = pattern_size * 2 * 3;
+ if (x + width >= bpl)
+ width = bpl - x;
+
+
+ for (x1 = 0; x1 < width; x1 += 6)
+ {
+ color_lo =
+ ((line_count + pattern_size)
+ % (pattern_size + pattern_distance)) & 0xff;
+ color_hi = (x1 / 6) & 0xff;
+ if (((x / ((pattern_size + pattern_distance) * 6)) % 3) ==
+ 0)
+ {
+ color_lo_r = color_lo;
+ color_hi_r = color_hi;
+ color_lo_g = 0;
+ color_hi_g = 0;
+ color_lo_b = 0;
+ color_hi_b = 0;
+ }
+ else if (((x / ((pattern_size + pattern_distance) * 6)) % 3)
+ == 1)
+ {
+ color_lo_r = 0;
+ color_hi_r = 0;
+ color_lo_g = color_lo;
+ color_hi_g = color_hi;
+ color_lo_b = 0;
+ color_hi_b = 0;
+ }
+ else
+ {
+ color_lo_r = 0;
+ color_hi_r = 0;
+ color_lo_g = 0;
+ color_hi_g = 0;
+ color_lo_b = color_lo;
+ color_hi_b = color_hi;
+ }
+ if (is_little_endian)
+ {
+ b[line_count * bpl + x + x1 + 0] = color_lo_r;
+ b[line_count * bpl + x + x1 + 1] = color_hi_r;
+ b[line_count * bpl + x + x1 + 2] = color_lo_g;
+ b[line_count * bpl + x + x1 + 3] = color_hi_g;
+ b[line_count * bpl + x + x1 + 4] = color_lo_b;
+ b[line_count * bpl + x + x1 + 5] = color_hi_b;
+ }
+ else
+ {
+ b[line_count * bpl + x + x1 + 0] = color_hi_r;
+ b[line_count * bpl + x + x1 + 1] = color_lo_r;
+ b[line_count * bpl + x + x1 + 2] = color_hi_g;
+ b[line_count * bpl + x + x1 + 3] = color_lo_g;
+ b[line_count * bpl + x + x1 + 4] = color_hi_b;
+ b[line_count * bpl + x + x1 + 5] = color_lo_b;
+ }
+ }
+ x += ((pattern_size + pattern_distance) * 2 * 3);
+ }
+ }
+
+ }
+ else if ((test_device->params.format == SANE_FRAME_RED
+ || test_device->params.format == SANE_FRAME_GREEN
+ || test_device->params.format == SANE_FRAME_BLUE)
+ && test_device->params.depth == 16)
+ {
+ /* 16 bit color three-pass */
+ pattern_size = 256;
+ pattern_distance = 4;
+ lines = pattern_size + pattern_distance;
+ b_size = lines * bpl;
+
+ if (buffer_size)
+ *buffer_size = b_size;
+ b = malloc (b_size);
+ if (!b)
+ {
+ DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ if (buffer)
+ *buffer = b;
+ DBG (3, "(child) init_picture_buffer: drawing 16 bit color three-pass "
+ "test picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines);
+ memset (b, 0x55, b_size);
+ for (line_count = 0; line_count < lines; line_count++)
+ {
+ SANE_Word x = pattern_distance * 2;
+
+ if (line_count % (pattern_size + pattern_distance)
+ < pattern_distance)
+ continue;
+
+ while (x < bpl)
+ {
+ SANE_Word width;
+ SANE_Word x1;
+ SANE_Byte color_hi = 0, color_lo = 0;
+
+ width = pattern_size * 2;
+ if (x + width >= bpl)
+ width = bpl - x;
+
+
+ for (x1 = 0; x1 < width; x1 += 2)
+ {
+ color_lo =
+ ((line_count + pattern_size)
+ % (pattern_size + pattern_distance)) & 0xff;
+ color_hi = (x1 / 2) & 0xff;
+ if (((x / ((pattern_size + pattern_distance) * 2)) % 3) ==
+ 0)
+ {
+ if (test_device->params.format != SANE_FRAME_RED)
+ {
+ color_lo = 0x00;
+ color_hi = 0x00;
+ }
+ }
+ else if (((x / ((pattern_size + pattern_distance) * 2)) % 3)
+ == 1)
+ {
+ if (test_device->params.format != SANE_FRAME_GREEN)
+ {
+ color_lo = 0x00;
+ color_hi = 0x00;
+ }
+ }
+ else
+ {
+ if (test_device->params.format != SANE_FRAME_BLUE)
+ {
+ color_lo = 0x00;
+ color_hi = 0x00;
+ }
+ }
+
+ if (is_little_endian)
+ {
+ b[line_count * bpl + x + x1 + 0] = color_lo;
+ b[line_count * bpl + x + x1 + 1] = color_hi;
+ }
+ else
+ {
+ b[line_count * bpl + x + x1 + 0] = color_hi;
+ b[line_count * bpl + x + x1 + 1] = color_lo;
+ }
+
+ }
+ x += ((pattern_size + pattern_distance) * 2);
+ }
+ }
+
+ }
+ else /* Huh? */
+ {
+ DBG (1, "(child) init_picture_buffer: unknown mode\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/test.c b/backend/test.c
new file mode 100644
index 0000000..8f8851a
--- /dev/null
+++ b/backend/test.c
@@ -0,0 +1,2841 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002-2006 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Changes according to the sanei_thread usage by
+ Gerhard Jaeger <gerhard@gjaeger.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This backend is for testing frontends.
+*/
+
+#define BUILD 28
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "../include/_stdint.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#define BACKEND_NAME test
+#include "../include/sane/sanei_backend.h"
+
+#include "test.h"
+
+#include "test-picture.c"
+
+#define TEST_CONFIG_FILE "test.conf"
+
+static SANE_Bool inited = SANE_FALSE;
+static SANE_Device **sane_device_list = 0;
+static Test_Device *first_test_device = 0;
+
+static SANE_Range geometry_range = {
+ SANE_FIX (0.0),
+ SANE_FIX (200.0),
+ SANE_FIX (1.0)
+};
+
+static SANE_Range resolution_range = {
+ SANE_FIX (1.0),
+ SANE_FIX (1200.0),
+ SANE_FIX (1.0)
+};
+
+static SANE_Range ppl_loss_range = {
+ 0,
+ 128,
+ 1
+};
+
+static SANE_Range read_limit_size_range = {
+ 1,
+ 64 * 1024, /* 64 KB ought to be enough for everyone :-) */
+ 1
+};
+
+static SANE_Range read_delay_duration_range = {
+ 1000,
+ 200 * 1000, /* 200 msec */
+ 1000
+};
+
+static SANE_Range int_constraint_range = {
+ 4,
+ 192,
+ 2
+};
+
+static SANE_Range fixed_constraint_range = {
+ SANE_FIX (-42.17),
+ SANE_FIX (32767.9999),
+ SANE_FIX (2.0)
+};
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static SANE_String_Const order_list[] = {
+ "RGB", "RBG", "GBR", "GRB", "BRG", "BGR",
+ 0
+};
+
+static SANE_String_Const test_picture_list[] = {
+ SANE_I18N ("Solid black"), SANE_I18N ("Solid white"),
+ SANE_I18N ("Color pattern"), SANE_I18N ("Grid"),
+ 0
+};
+
+static SANE_String_Const read_status_code_list[] = {
+ SANE_I18N ("Default"), "SANE_STATUS_UNSUPPORTED", "SANE_STATUS_CANCELLED",
+ "SANE_STATUS_DEVICE_BUSY", "SANE_STATUS_INVAL", "SANE_STATUS_EOF",
+ "SANE_STATUS_JAMMED", "SANE_STATUS_NO_DOCS", "SANE_STATUS_COVER_OPEN",
+ "SANE_STATUS_IO_ERROR", "SANE_STATUS_NO_MEM", "SANE_STATUS_ACCESS_DENIED",
+ 0
+};
+
+static SANE_Int depth_list[] = {
+ 3, 1, 8, 16
+};
+
+static SANE_Int int_constraint_word_list[] = {
+ 9, -42, -8, 0, 17, 42, 256, 65536, 256 * 256 * 256, 256 * 256 * 256 * 64
+};
+
+static SANE_Int fixed_constraint_word_list[] = {
+ 4, SANE_FIX (-32.7), SANE_FIX (12.1), SANE_FIX (42.0), SANE_FIX (129.5)
+};
+
+static SANE_String_Const string_constraint_string_list[] = {
+ SANE_I18N ("First entry"), SANE_I18N ("Second entry"),
+ SANE_I18N
+ ("This is the very long third entry. Maybe the frontend has an idea how to "
+ "display it"),
+ 0
+};
+
+static SANE_String_Const string_constraint_long_string_list[] = {
+ SANE_I18N ("First entry"), SANE_I18N ("Second entry"), "3", "4", "5", "6",
+ "7", "8", "9", "10",
+ "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22",
+ "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34",
+ "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46",
+ 0
+};
+
+static SANE_Int int_array[] = {
+ -17, 0, -5, 42, 91, 256 * 256 * 256 * 64
+};
+
+static SANE_Int int_array_constraint_range[] = {
+ 48, 6, 4, 92, 190, 16
+};
+
+static SANE_Int int_array_constraint_word_list[] = {
+ -42, 0, -8, 17, 42, 42
+};
+
+static SANE_String_Const source_list[] = {
+ SANE_I18N ("Flatbed"), SANE_I18N ("Automatic Document Feeder"),
+ 0
+};
+
+static double random_factor; /* use for fuzzyness of parameters */
+
+/* initial values */
+static SANE_Word init_number_of_devices = 2;
+static SANE_Fixed init_tl_x = SANE_FIX (0.0);
+static SANE_Fixed init_tl_y = SANE_FIX (0.0);
+static SANE_Fixed init_br_x = SANE_FIX (80.0);
+static SANE_Fixed init_br_y = SANE_FIX (100.0);
+static SANE_Word init_resolution = 50;
+static SANE_String init_mode =SANE_VALUE_SCAN_MODE_GRAY;
+static SANE_Word init_depth = 8;
+static SANE_Bool init_hand_scanner = SANE_FALSE;
+static SANE_Bool init_three_pass = SANE_FALSE;
+static SANE_String init_three_pass_order = "RGB";
+static SANE_String init_scan_source = "Flatbed";
+static SANE_String init_test_picture = "Solid black";
+static SANE_Bool init_invert_endianess = SANE_FALSE;
+static SANE_Bool init_read_limit = SANE_FALSE;
+static SANE_Word init_read_limit_size = 1;
+static SANE_Bool init_read_delay = SANE_FALSE;
+static SANE_Word init_read_delay_duration = 1000;
+static SANE_String init_read_status_code = "Default";
+static SANE_Bool init_fuzzy_parameters = SANE_FALSE;
+static SANE_Word init_ppl_loss = 0;
+static SANE_Bool init_non_blocking = SANE_FALSE;
+static SANE_Bool init_select_fd = SANE_FALSE;
+static SANE_Bool init_enable_test_options = SANE_FALSE;
+static SANE_String init_string = "This is the contents of the string option. "
+ "Fill some more words to see how the frontend behaves.";
+static SANE_String init_string_constraint_string_list = "First entry";
+static SANE_String init_string_constraint_long_string_list = "First entry";
+
+/* Test if this machine is little endian (from coolscan.c) */
+static SANE_Bool
+little_endian (void)
+{
+ SANE_Int testvalue = 255;
+ uint8_t *firstbyte = (uint8_t *) & testvalue;
+
+ if (*firstbyte == 255)
+ return SANE_TRUE;
+ return SANE_FALSE;
+}
+
+static void
+swap_double (double *a, double *b)
+{
+ double c;
+
+ c = *a;
+ *a = *b;
+ *b = c;
+
+ return;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Bool
+check_handle (SANE_Handle handle)
+{
+ Test_Device *test_device = first_test_device;
+
+ while (test_device)
+ {
+ if (test_device == (Test_Device *) handle)
+ return SANE_TRUE;
+ test_device = test_device->next;
+ }
+ return SANE_FALSE;
+}
+
+static SANE_Status
+init_options (Test_Device * test_device)
+{
+ SANE_Option_Descriptor *od;
+
+ DBG (2, "init_options: test_device=%p\n", (void *) test_device);
+
+ /* opt_num_opts */
+ od = &test_device->opt[opt_num_opts];
+ od->name = "";
+ od->title = SANE_TITLE_NUM_OPTIONS;
+ od->desc = SANE_DESC_NUM_OPTIONS;
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_num_opts].w = num_options;
+
+ test_device->loaded[opt_num_opts] = 1;
+
+ /* opt_mode_group */
+ od = &test_device->opt[opt_mode_group];
+ od->name = "";
+ od->title = SANE_I18N ("Scan Mode");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_mode_group].w = 0;
+
+ /* opt_mode */
+ od = &test_device->opt[opt_mode];
+ od->name = SANE_NAME_SCAN_MODE;
+ od->title = SANE_TITLE_SCAN_MODE;
+ od->desc = SANE_DESC_SCAN_MODE;
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (mode_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = mode_list;
+ test_device->val[opt_mode].s = malloc (od->size);
+ if (!test_device->val[opt_mode].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_mode].s, init_mode);
+
+ /* opt_depth */
+ od = &test_device->opt[opt_depth];
+ od->name = SANE_NAME_BIT_DEPTH;
+ od->title = SANE_TITLE_BIT_DEPTH;
+ od->desc = SANE_DESC_BIT_DEPTH;
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ od->constraint.word_list = depth_list;
+ test_device->val[opt_depth].w = init_depth;
+
+ /* opt_hand_scanner */
+ od = &test_device->opt[opt_hand_scanner];
+ od->name = "hand-scanner";
+ od->title = SANE_I18N ("Hand-scanner simulation");
+ od->desc = SANE_I18N ("Simulate a hand-scanner. Hand-scanners do not "
+ "know the image height a priori. Instead, they "
+ "return a height of -1. Setting this option allows "
+ "to test whether a frontend can handle this "
+ "correctly. This option also enables a fixed width "
+ "of 11 cm.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_hand_scanner].w = init_hand_scanner;
+
+ /* opt_three_pass */
+ od = &test_device->opt[opt_three_pass];
+ od->name = "three-pass";
+ od->title = SANE_I18N ("Three-pass simulation");
+ od->desc = SANE_I18N ("Simulate a three-pass scanner. In color mode, three "
+ "frames are transmitted.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (strcmp (init_mode, SANE_VALUE_SCAN_MODE_COLOR) != 0)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_three_pass].w = init_three_pass;
+
+ /* opt_three_pass_order */
+ od = &test_device->opt[opt_three_pass_order];
+ od->name = "three-pass-order";
+ od->title = SANE_I18N ("Set the order of frames");
+ od->desc = SANE_I18N ("Set the order of frames in three-pass color mode.");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (order_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (strcmp (init_mode, SANE_VALUE_SCAN_MODE_COLOR) != 0)
+ od->cap |= SANE_CAP_INACTIVE;
+ if (!init_three_pass)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = order_list;
+ test_device->val[opt_three_pass_order].s = malloc (od->size);
+ if (!test_device->val[opt_three_pass_order].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_three_pass_order].s, init_three_pass_order);
+
+ /* opt_resolution */
+ od = &test_device->opt[opt_resolution];
+ od->name = SANE_NAME_SCAN_RESOLUTION;
+ od->title = SANE_TITLE_SCAN_RESOLUTION;
+ od->desc = SANE_DESC_SCAN_RESOLUTION;
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_DPI;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &resolution_range;
+ test_device->val[opt_resolution].w = init_resolution;
+
+ /* opt_scan_source */
+ od = &test_device->opt[opt_scan_source];
+ od->name = SANE_NAME_SCAN_SOURCE;
+ od->title = SANE_TITLE_SCAN_SOURCE;
+ od->desc = SANE_I18N("If Automatic Document Feeder is selected, the feeder will be 'empty' after 10 scans.");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (source_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = source_list;
+ test_device->val[opt_scan_source].s = malloc (od->size);
+ if (!test_device->val[opt_scan_source].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_scan_source].s, init_scan_source);
+
+ /* opt_special_group */
+ od = &test_device->opt[opt_special_group];
+ od->name = "";
+ od->title = SANE_I18N ("Special Options");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_special_group].w = 0;
+
+ /* opt_test_picture */
+ od = &test_device->opt[opt_test_picture];
+ od->name = "test-picture";
+ od->title = SANE_I18N ("Select the test picture");
+ od->desc =
+ SANE_I18N ("Select the kind of test picture. Available options:\n"
+ "Solid black: fills the whole scan with black.\n"
+ "Solid white: fills the whole scan with white.\n"
+ "Color pattern: draws various color test patterns "
+ "depending on the mode.\n"
+ "Grid: draws a black/white grid with a width and "
+ "height of 10 mm per square.");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (test_picture_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = test_picture_list;
+ test_device->val[opt_test_picture].s = malloc (od->size);
+ if (!test_device->val[opt_test_picture].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_test_picture].s, init_test_picture);
+
+ /* opt_invert_endianness */
+ od = &test_device->opt[opt_invert_endianess];
+ od->name = "invert-endianess";
+ od->title = SANE_I18N ("Invert endianness");
+ od->desc = SANE_I18N ("Exchange upper and lower byte of image data in 16 "
+ "bit modes. This option can be used to test the 16 "
+ "bit modes of frontends, e.g. if the frontend uses "
+ "the correct endianness.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_invert_endianess].w = init_invert_endianess;
+
+ /* opt_read_limit */
+ od = &test_device->opt[opt_read_limit];
+ od->name = "read-limit";
+ od->title = SANE_I18N ("Read limit");
+ od->desc = SANE_I18N ("Limit the amount of data transferred with each "
+ "call to sane_read().");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_read_limit].w = init_read_limit;
+
+ /* opt_read_limit_size */
+ od = &test_device->opt[opt_read_limit_size];
+ od->name = "read-limit-size";
+ od->title = SANE_I18N ("Size of read-limit");
+ od->desc = SANE_I18N ("The (maximum) amount of data transferred with each "
+ "call to sane_read().");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (!init_read_limit)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &read_limit_size_range;
+ test_device->val[opt_read_limit_size].w = init_read_limit_size;
+
+ /* opt_read_delay */
+ od = &test_device->opt[opt_read_delay];
+ od->name = "read-delay";
+ od->title = SANE_I18N ("Read delay");
+ od->desc = SANE_I18N ("Delay the transfer of data to the pipe.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_read_delay].w = init_read_delay;
+
+ /* opt_read_delay_duration */
+ od = &test_device->opt[opt_read_delay_duration];
+ od->name = "read-delay-duration";
+ od->title = SANE_I18N ("Duration of read-delay");
+ od->desc = SANE_I18N ("How long to wait after transferring each buffer of "
+ "data through the pipe.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_MICROSECOND;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (!init_read_delay)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &read_delay_duration_range;
+ test_device->val[opt_read_delay_duration].w = init_read_delay_duration;
+
+ /* opt_read_status_code */
+ od = &test_device->opt[opt_read_status_code];
+ od->name = "read-return-value";
+ od->title = SANE_I18N ("Return-value of sane_read");
+ od->desc =
+ SANE_I18N ("Select the return-value of sane_read(). \"Default\" "
+ "is the normal handling for scanning. All other status "
+ "codes are for testing how the frontend handles them.");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (read_status_code_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = read_status_code_list;
+ test_device->val[opt_read_status_code].s = malloc (od->size);
+ if (!test_device->val[opt_read_status_code].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_read_status_code].s, init_read_status_code);
+
+ /* opt_ppl_loss */
+ od = &test_device->opt[opt_ppl_loss];
+ od->name = "ppl-loss";
+ od->title = SANE_I18N ("Loss of pixels per line");
+ od->desc =
+ SANE_I18N ("The number of pixels that are wasted at the end of each "
+ "line.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_PIXEL;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &ppl_loss_range;
+ test_device->val[opt_ppl_loss].w = init_ppl_loss;
+
+ /* opt_fuzzy_parameters */
+ od = &test_device->opt[opt_fuzzy_parameters];
+ od->name = "fuzzy-parameters";
+ od->title = SANE_I18N ("Fuzzy parameters");
+ od->desc = SANE_I18N ("Return fuzzy lines and bytes per line when "
+ "sane_parameters() is called before sane_start().");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_fuzzy_parameters].w = init_fuzzy_parameters;
+
+ /* opt_non_blocking */
+ od = &test_device->opt[opt_non_blocking];
+ od->name = "non-blocking";
+ od->title = SANE_I18N ("Use non-blocking IO");
+ od->desc = SANE_I18N ("Use non-blocking IO for sane_read() if supported "
+ "by the frontend.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_non_blocking].w = init_non_blocking;
+
+ /* opt_select_fd */
+ od = &test_device->opt[opt_select_fd];
+ od->name = "select-fd";
+ od->title = SANE_I18N ("Offer select file descriptor");
+ od->desc = SANE_I18N ("Offer a select filedescriptor for detecting if "
+ "sane_read() will return data.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_select_fd].w = init_select_fd;
+
+ /* opt_enable_test_options */
+ od = &test_device->opt[opt_enable_test_options];
+ od->name = "enable-test-options";
+ od->title = SANE_I18N ("Enable test options");
+ od->desc = SANE_I18N ("Enable various test options. This is for testing "
+ "the ability of frontends to view and modify all the "
+ "different SANE option types.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_enable_test_options].w = init_enable_test_options;
+
+ /* opt_print_options */
+ od = &test_device->opt[opt_print_options];
+ od->name = "print-options";
+ od->title = SANE_I18N ("Print options");
+ od->desc = SANE_I18N ("Print a list of all options.");
+ od->type = SANE_TYPE_BUTTON;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.string_list = 0;
+ test_device->val[opt_print_options].w = 0;
+
+ /* opt_geometry_group */
+ od = &test_device->opt[opt_geometry_group];
+ od->name = "";
+ od->title = SANE_I18N ("Geometry");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_geometry_group].w = 0;
+
+ /* opt_tl_x */
+ od = &test_device->opt[opt_tl_x];
+ od->name = SANE_NAME_SCAN_TL_X;
+ od->title = SANE_TITLE_SCAN_TL_X;
+ od->desc = SANE_DESC_SCAN_TL_X;
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_MM;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &geometry_range;
+ test_device->val[opt_tl_x].w = init_tl_x;
+
+ /* opt_tl_y */
+ od = &test_device->opt[opt_tl_y];
+ od->name = SANE_NAME_SCAN_TL_Y;
+ od->title = SANE_TITLE_SCAN_TL_Y;
+ od->desc = SANE_DESC_SCAN_TL_Y;
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_MM;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &geometry_range;
+ test_device->val[opt_tl_y].w = init_tl_y;
+
+ /* opt_br_x */
+ od = &test_device->opt[opt_br_x];
+ od->name = SANE_NAME_SCAN_BR_X;
+ od->title = SANE_TITLE_SCAN_BR_X;
+ od->desc = SANE_DESC_SCAN_BR_X;
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_MM;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &geometry_range;
+ test_device->val[opt_br_x].w = init_br_x;
+
+ /* opt_br_y */
+ od = &test_device->opt[opt_br_y];
+ od->name = SANE_NAME_SCAN_BR_Y;
+ od->title = SANE_TITLE_SCAN_BR_Y;
+ od->desc = SANE_DESC_SCAN_BR_Y;
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_MM;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &geometry_range;
+ test_device->val[opt_br_y].w = init_br_y;
+
+ /* opt_bool_group */
+ od = &test_device->opt[opt_bool_group];
+ od->name = "";
+ od->title = SANE_I18N ("Bool test options");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = SANE_CAP_ADVANCED;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_group].w = 0;
+
+ /* opt_bool_soft_select_soft_detect */
+ od = &test_device->opt[opt_bool_soft_select_soft_detect];
+ od->name = "bool-soft-select-soft-detect";
+ od->title = SANE_I18N ("(1/6) Bool soft select soft detect");
+ od->desc =
+ SANE_I18N ("(1/6) Bool test option that has soft select and soft "
+ "detect (and advanced) capabilities. That's just a "
+ "normal bool option.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_soft_select_soft_detect].w = SANE_FALSE;
+
+ /* opt_bool_hard_select_soft_detect */
+ od = &test_device->opt[opt_bool_hard_select_soft_detect];
+ od->name = "bool-hard-select-soft-detect";
+ od->title = SANE_I18N ("(2/6) Bool hard select soft detect");
+ od->desc =
+ SANE_I18N ("(2/6) Bool test option that has hard select and soft "
+ "detect (and advanced) capabilities. That means the "
+ "option can't be set by the frontend but by the user "
+ "(e.g. by pressing a button at the device).");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_hard_select_soft_detect].w = SANE_FALSE;
+
+ /* opt_bool_hard_select */
+ od = &test_device->opt[opt_bool_hard_select];
+ od->name = "bool-hard-select";
+ od->title = SANE_I18N ("(3/6) Bool hard select");
+ od->desc = SANE_I18N ("(3/6) Bool test option that has hard select "
+ "(and advanced) capabilities. That means the option "
+ "can't be set by the frontend but by the user "
+ "(e.g. by pressing a button at the device) and can't "
+ "be read by the frontend.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_hard_select].w = SANE_FALSE;
+
+ /* opt_bool_soft_detect */
+ od = &test_device->opt[opt_bool_soft_detect];
+ od->name = "bool-soft-detect";
+ od->title = SANE_I18N ("(4/6) Bool soft detect");
+ od->desc = SANE_I18N ("(4/6) Bool test option that has soft detect "
+ "(and advanced) capabilities. That means the option "
+ "is read-only.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_soft_detect].w = SANE_FALSE;
+
+ /* opt_bool_soft_select_soft_detect_emulated */
+ od = &test_device->opt[opt_bool_soft_select_soft_detect_emulated];
+ od->name = "bool-soft-select-soft-detect-emulated";
+ od->title = SANE_I18N ("(5/6) Bool soft select soft detect emulated");
+ od->desc = SANE_I18N ("(5/6) Bool test option that has soft select, soft "
+ "detect, and emulated (and advanced) capabilities.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED
+ | SANE_CAP_EMULATED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_soft_select_soft_detect_emulated].w = SANE_FALSE;
+
+ /* opt_bool_soft_select_soft_detect_auto */
+ od = &test_device->opt[opt_bool_soft_select_soft_detect_auto];
+ od->name = "bool-soft-select-soft-detect-auto";
+ od->title = SANE_I18N ("(6/6) Bool soft select soft detect auto");
+ od->desc = SANE_I18N ("(6/6) Bool test option that has soft select, soft "
+ "detect, and automatic (and advanced) capabilities. "
+ "This option can be automatically set by the backend.");
+ od->type = SANE_TYPE_BOOL;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED
+ | SANE_CAP_AUTOMATIC;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_bool_soft_select_soft_detect_auto].w = SANE_FALSE;
+
+ /* opt_int_group */
+ od = &test_device->opt[opt_int_group];
+ od->name = "";
+ od->title = SANE_I18N ("Int test options");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = SANE_CAP_ADVANCED;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_int_group].w = 0;
+
+ /* opt_int */
+ od = &test_device->opt[opt_int];
+ od->name = "int";
+ od->title = SANE_I18N ("(1/6) Int");
+ od->desc = SANE_I18N ("(1/6) Int test option with no unit and no "
+ "constraint set.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_int].w = 42;
+
+ /* opt_int_constraint_range */
+ od = &test_device->opt[opt_int_constraint_range];
+ od->name = "int-constraint-range";
+ od->title = SANE_I18N ("(2/6) Int constraint range");
+ od->desc = SANE_I18N ("(2/6) Int test option with unit pixel and "
+ "constraint range set. Minimum is 4, maximum 192, and "
+ "quant is 2.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_PIXEL;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &int_constraint_range;
+ test_device->val[opt_int_constraint_range].w = 26;
+
+ /* opt_int_constraint_word_list */
+ od = &test_device->opt[opt_int_constraint_word_list];
+ od->name = "int-constraint-word-list";
+ od->title = SANE_I18N ("(3/6) Int constraint word list");
+ od->desc = SANE_I18N ("(3/6) Int test option with unit bits and "
+ "constraint word list set.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_BIT;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ od->constraint.word_list = int_constraint_word_list;
+ test_device->val[opt_int_constraint_word_list].w = 42;
+
+ /* opt_int_array */
+ od = &test_device->opt[opt_int_array];
+ od->name = "int-constraint-array";
+ od->title = SANE_I18N ("(4/6) Int array");
+ od->desc = SANE_I18N ("(4/6) Int test option with unit mm and using "
+ "an array without constraints.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_MM;
+ od->size = 6 * sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_int_array].wa = &int_array[0];
+
+ /* opt_int_array_constraint_range */
+ od = &test_device->opt[opt_int_array_constraint_range];
+ od->name = "int-constraint-array-constraint-range";
+ od->title = SANE_I18N ("(5/6) Int array constraint range");
+ od->desc = SANE_I18N ("(5/6) Int test option with unit dpi and using "
+ "an array with a range constraint. Minimum is 4, "
+ "maximum 192, and quant is 2.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_DPI;
+ od->size = 6 * sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &int_constraint_range;
+ test_device->val[opt_int_array_constraint_range].wa =
+ &int_array_constraint_range[0];
+
+ /* opt_int_array_constraint_word_list */
+ od = &test_device->opt[opt_int_array_constraint_word_list];
+ od->name = "int-constraint-array-constraint-word-list";
+ od->title = SANE_I18N ("(6/6) Int array constraint word list");
+ od->desc = SANE_I18N ("(6/6) Int test option with unit percent and using "
+ "an array with a word list constraint.");
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_PERCENT;
+ od->size = 6 * sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ od->constraint.word_list = int_constraint_word_list;
+ test_device->val[opt_int_array_constraint_word_list].wa =
+ &int_array_constraint_word_list[0];
+
+ /* opt_fixed_group */
+ od = &test_device->opt[opt_fixed_group];
+ od->name = "";
+ od->title = SANE_I18N ("Fixed test options");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = SANE_CAP_ADVANCED;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_fixed_group].w = 0;
+
+ /* opt_fixed */
+ od = &test_device->opt[opt_fixed];
+ od->name = "fixed";
+ od->title = SANE_I18N ("(1/3) Fixed");
+ od->desc = SANE_I18N ("(1/3) Fixed test option with no unit and no "
+ "constraint set.");
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_fixed].w = SANE_FIX (42.0);
+
+ /* opt_fixed_constraint_range */
+ od = &test_device->opt[opt_fixed_constraint_range];
+ od->name = "fixed-constraint-range";
+ od->title = SANE_I18N ("(2/3) Fixed constraint range");
+ od->desc = SANE_I18N ("(2/3) Fixed test option with unit microsecond and "
+ "constraint range set. Minimum is -42.17, maximum "
+ "32767.9999, and quant is 2.0.");
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_MICROSECOND;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &fixed_constraint_range;
+ test_device->val[opt_fixed_constraint_range].w = SANE_FIX (41.83);
+
+ /* opt_fixed_constraint_word_list */
+ od = &test_device->opt[opt_fixed_constraint_word_list];
+ od->name = "fixed-constraint-word-list";
+ od->title = SANE_I18N ("(3/3) Fixed constraint word list");
+ od->desc = SANE_I18N ("(3/3) Fixed test option with no unit and "
+ "constraint word list set.");
+ od->type = SANE_TYPE_FIXED;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ od->constraint.word_list = fixed_constraint_word_list;
+ test_device->val[opt_fixed_constraint_word_list].w = SANE_FIX (42.0);
+
+ /* opt_string_group */
+ od = &test_device->opt[opt_string_group];
+ od->name = "";
+ od->title = SANE_I18N ("String test options");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_string_group].w = 0;
+
+ /* opt_string */
+ od = &test_device->opt[opt_string];
+ od->name = "string";
+ od->title = SANE_I18N ("(1/3) String");
+ od->desc = SANE_I18N ("(1/3) String test option without constraint.");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = strlen (init_string) + 1;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.string_list = 0;
+ test_device->val[opt_string].s = malloc (od->size);
+ if (!test_device->val[opt_string].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_string].s, init_string);
+
+ /* opt_string_constraint_string_list */
+ od = &test_device->opt[opt_string_constraint_string_list];
+ od->name = "string-constraint-string-list";
+ od->title = SANE_I18N ("(2/3) String constraint string list");
+ od->desc = SANE_I18N ("(2/3) String test option with string list "
+ "constraint.");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (string_constraint_string_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = string_constraint_string_list;
+ test_device->val[opt_string_constraint_string_list].s = malloc (od->size);
+ if (!test_device->val[opt_string_constraint_string_list].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_string_constraint_string_list].s,
+ init_string_constraint_string_list);
+
+ /* opt_string_constraint_long_string_list */
+ od = &test_device->opt[opt_string_constraint_long_string_list];
+ od->name = "string-constraint-long-string-list";
+ od->title = SANE_I18N ("(3/3) String constraint long string list");
+ od->desc = SANE_I18N ("(3/3) String test option with string list "
+ "constraint. Contains some more entries...");
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = max_string_size (string_constraint_long_string_list);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = string_constraint_long_string_list;
+ test_device->val[opt_string_constraint_long_string_list].s =
+ malloc (od->size);
+ if (!test_device->val[opt_string_constraint_long_string_list].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (test_device->val[opt_string_constraint_long_string_list].s,
+ init_string_constraint_long_string_list);
+
+ /* opt_button_group */
+ od = &test_device->opt[opt_button_group];
+ od->name = "";
+ od->title = SANE_I18N ("Button test options");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ test_device->val[opt_button_group].w = 0;
+
+ /* opt_button */
+ od = &test_device->opt[opt_button];
+ od->name = "button";
+ od->title = SANE_I18N ("(1/1) Button");
+ od->desc = SANE_I18N ("(1/1) Button test option. Prints some text...");
+ od->type = SANE_TYPE_BUTTON;
+ od->unit = SANE_UNIT_NONE;
+ od->size = 0;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ if (init_enable_test_options == SANE_FALSE)
+ od->cap |= SANE_CAP_INACTIVE;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.string_list = 0;
+ test_device->val[opt_button].w = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+read_option (SANE_String line, SANE_String option_string,
+ parameter_type p_type, void *value)
+{
+ SANE_String_Const cp;
+ SANE_Char *word, *end;
+
+ word = 0;
+
+ cp = sanei_config_get_string (line, &word);
+
+ if (!word)
+ return SANE_STATUS_INVAL;
+
+ if (strcmp (word, option_string) != 0)
+ return SANE_STATUS_INVAL;
+
+ free (word);
+ word = 0;
+
+ switch (p_type)
+ {
+ case param_none:
+ return SANE_STATUS_GOOD;
+ case param_bool:
+ {
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ return SANE_STATUS_INVAL;
+ if (strlen (word) == 0)
+ {
+ DBG (3, "read_option: option `%s' requires parameter\n",
+ option_string);
+ return SANE_STATUS_INVAL;
+ }
+ if (strcmp (word, "true") != 0 && strcmp (word, "false") != 0)
+ {
+ DBG (3, "read_option: option `%s' requires parameter "
+ "`true' or `false'\n", option_string);
+ return SANE_STATUS_INVAL;
+ }
+ else if (strcmp (word, "true") == 0)
+ *(SANE_Bool *) value = SANE_TRUE;
+ else
+ *(SANE_Bool *) value = SANE_FALSE;
+ DBG (3, "read_option: set option `%s' to %s\n", option_string,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ }
+ case param_int:
+ {
+ SANE_Int int_value;
+
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ return SANE_STATUS_INVAL;
+ errno = 0;
+ int_value = (SANE_Int) strtol (word, &end, 0);
+ if (end == word)
+ {
+ DBG (3, "read_option: option `%s' requires parameter\n",
+ option_string);
+ return SANE_STATUS_INVAL;
+ }
+ else if (errno)
+ {
+ DBG (3, "read_option: option `%s': can't parse parameter `%s' "
+ "(%s)\n", option_string, word, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ DBG (3, "read_option: set option `%s' to %d\n", option_string,
+ int_value);
+ *(SANE_Int *) value = int_value;
+ }
+ break;
+ }
+ case param_fixed:
+ {
+ double double_value;
+ SANE_Fixed fixed_value;
+
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ return SANE_STATUS_INVAL;
+ errno = 0;
+ double_value = strtod (word, &end);
+ if (end == word)
+ {
+ DBG (3, "read_option: option `%s' requires parameter\n",
+ option_string);
+ return SANE_STATUS_INVAL;
+ }
+ else if (errno)
+ {
+ DBG (3, "read_option: option `%s': can't parse parameter `%s' "
+ "(%s)\n", option_string, word, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ DBG (3, "read_option: set option `%s' to %.0f\n", option_string,
+ double_value);
+ fixed_value = SANE_FIX (double_value);
+ *(SANE_Fixed *) value = fixed_value;
+ }
+ break;
+ }
+ case param_string:
+ {
+ cp = sanei_config_get_string (cp, &word);
+ if (!word)
+ return SANE_STATUS_INVAL;
+ if (strlen (word) == 0)
+ {
+ DBG (3, "read_option: option `%s' requires parameter\n",
+ option_string);
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ DBG (3, "read_option: set option `%s' to `%s'\n", option_string,
+ word);
+ *(SANE_String *) value = strdup (word);
+ }
+ break;
+ }
+ default:
+ DBG (1, "read_option: unknown param_type %d\n", p_type);
+ return SANE_STATUS_INVAL;
+ } /* switch */
+
+ if (word)
+ free (word);
+ word = 0;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+reader_process (Test_Device * test_device, SANE_Int fd)
+{
+ SANE_Status status;
+ SANE_Word byte_count = 0, bytes_total;
+ SANE_Byte *buffer = 0;
+ ssize_t bytes_written = 0;
+ size_t buffer_size = 0, write_count = 0;
+
+ DBG (2, "(child) reader_process: test_device=%p, fd=%d\n",
+ (void *) test_device, fd);
+
+ bytes_total = test_device->lines * test_device->bytes_per_line;
+ status = init_picture_buffer (test_device, &buffer, &buffer_size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG (2, "(child) reader_process: buffer=%p, buffersize=%lu\n",
+ buffer, (u_long) buffer_size);
+
+ while (byte_count < bytes_total)
+ {
+ if (write_count == 0)
+ {
+ write_count = buffer_size;
+ if (byte_count + (SANE_Word) write_count > bytes_total)
+ write_count = bytes_total - byte_count;
+
+ if (test_device->val[opt_read_delay].w == SANE_TRUE)
+ usleep (test_device->val[opt_read_delay_duration].w);
+ }
+ bytes_written = write (fd, buffer, write_count);
+ if (bytes_written < 0)
+ {
+ DBG (1, "(child) reader_process: write returned %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ byte_count += bytes_written;
+ DBG (4, "(child) reader_process: wrote %ld bytes of %lu (%d total)\n",
+ (long) bytes_written, (u_long) write_count, byte_count);
+ write_count -= bytes_written;
+ }
+
+ free (buffer);
+
+ if (sanei_thread_is_forked ())
+ {
+ DBG (4, "(child) reader_process: finished, wrote %d bytes, expected %d "
+ "bytes, now waiting\n", byte_count, bytes_total);
+ while (SANE_TRUE)
+ sleep (10);
+ DBG (4, "(child) reader_process: this should have never happened...");
+ close (fd);
+ }
+ else
+ {
+ DBG (4, "(child) reader_process: finished, wrote %d bytes, expected %d "
+ "bytes\n", byte_count, bytes_total);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * this code either runs in child or thread context...
+ */
+static int
+reader_task (void *data)
+{
+ SANE_Status status;
+ struct SIGACTION act;
+ struct Test_Device *test_device = (struct Test_Device *) data;
+
+ DBG (2, "reader_task started\n");
+ if (sanei_thread_is_forked ())
+ {
+ DBG (3, "reader_task started (forked)\n");
+ close (test_device->pipe);
+ test_device->pipe = -1;
+
+ }
+ else
+ {
+ DBG (3, "reader_task started (as thread)\n");
+ }
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ status = reader_process (test_device, test_device->reader_fds);
+ DBG (2, "(child) reader_task: reader_process finished (%s)\n",
+ sane_strstatus (status));
+ return (int) status;
+}
+
+static SANE_Status
+finish_pass (Test_Device * test_device)
+{
+ SANE_Status return_status = SANE_STATUS_GOOD;
+
+ DBG (2, "finish_pass: test_device=%p\n", (void *) test_device);
+ test_device->scanning = SANE_FALSE;
+ if (test_device->pipe >= 0)
+ {
+ DBG (2, "finish_pass: closing pipe\n");
+ close (test_device->pipe);
+ DBG (2, "finish_pass: pipe closed\n");
+ test_device->pipe = -1;
+ }
+ if (test_device->reader_pid != -1)
+ {
+ int status;
+ SANE_Pid pid;
+
+ DBG (2, "finish_pass: terminating reader process %ld\n",
+ (long) test_device->reader_pid);
+ sanei_thread_kill (test_device->reader_pid);
+ pid = sanei_thread_waitpid (test_device->reader_pid, &status);
+ if (pid == -1)
+ {
+ DBG (1,
+ "finish_pass: sanei_thread_waitpid failed, already terminated? (%s)\n",
+ strerror (errno));
+ }
+ else
+ {
+ DBG (2, "finish_pass: reader process terminated with status: %s\n",
+ sane_strstatus (status));
+ }
+ test_device->reader_pid = -1;
+ }
+ /* this happens when running in thread context... */
+ if (test_device->reader_fds >= 0)
+ {
+ DBG (2, "finish_pass: closing reader pipe\n");
+ close (test_device->reader_fds);
+ DBG (2, "finish_pass: reader pipe closed\n");
+ test_device->reader_fds = -1;
+ }
+ return return_status;
+}
+
+static void
+print_options (Test_Device * test_device)
+{
+ SANE_Option_Descriptor *od;
+ SANE_Word option_number;
+ SANE_Char caps[1024];
+
+ for (option_number = 0; option_number < num_options; option_number++)
+ {
+ od = &test_device->opt[option_number];
+ DBG (0, "-----> number: %d\n", option_number);
+ DBG (0, " name: `%s'\n", od->name);
+ DBG (0, " title: `%s'\n", od->title);
+ DBG (0, " description: `%s'\n", od->desc);
+ DBG (0, " type: %s\n",
+ od->type == SANE_TYPE_BOOL ? "SANE_TYPE_BOOL" :
+ od->type == SANE_TYPE_INT ? "SANE_TYPE_INT" :
+ od->type == SANE_TYPE_FIXED ? "SANE_TYPE_FIXED" :
+ od->type == SANE_TYPE_STRING ? "SANE_TYPE_STRING" :
+ od->type == SANE_TYPE_BUTTON ? "SANE_TYPE_BUTTON" :
+ od->type == SANE_TYPE_GROUP ? "SANE_TYPE_GROUP" : "unknown");
+ DBG (0, " unit: %s\n",
+ od->unit == SANE_UNIT_NONE ? "SANE_UNIT_NONE" :
+ od->unit == SANE_UNIT_PIXEL ? "SANE_UNIT_PIXEL" :
+ od->unit == SANE_UNIT_BIT ? "SANE_UNIT_BIT" :
+ od->unit == SANE_UNIT_MM ? "SANE_UNIT_MM" :
+ od->unit == SANE_UNIT_DPI ? "SANE_UNIT_DPI" :
+ od->unit == SANE_UNIT_PERCENT ? "SANE_UNIT_PERCENT" :
+ od->unit == SANE_UNIT_MICROSECOND ? "SANE_UNIT_MICROSECOND" :
+ "unknown");
+ DBG (0, " size: %d\n", od->size);
+ caps[0] = '\0';
+ if (od->cap & SANE_CAP_SOFT_SELECT)
+ strcat (caps, "SANE_CAP_SOFT_SELECT ");
+ if (od->cap & SANE_CAP_HARD_SELECT)
+ strcat (caps, "SANE_CAP_HARD_SELECT ");
+ if (od->cap & SANE_CAP_SOFT_DETECT)
+ strcat (caps, "SANE_CAP_SOFT_DETECT ");
+ if (od->cap & SANE_CAP_EMULATED)
+ strcat (caps, "SANE_CAP_EMULATED ");
+ if (od->cap & SANE_CAP_AUTOMATIC)
+ strcat (caps, "SANE_CAP_AUTOMATIC ");
+ if (od->cap & SANE_CAP_INACTIVE)
+ strcat (caps, "SANE_CAP_INACTIVE ");
+ if (od->cap & SANE_CAP_ADVANCED)
+ strcat (caps, "SANE_CAP_ADVANCED ");
+ DBG (0, " capabilities: %s\n", caps);
+ DBG (0, "constraint type: %s\n",
+ od->constraint_type == SANE_CONSTRAINT_NONE ?
+ "SANE_CONSTRAINT_NONE" :
+ od->constraint_type == SANE_CONSTRAINT_RANGE ?
+ "SANE_CONSTRAINT_RANGE" :
+ od->constraint_type == SANE_CONSTRAINT_WORD_LIST ?
+ "SANE_CONSTRAINT_WORD_LIST" :
+ od->constraint_type == SANE_CONSTRAINT_STRING_LIST ?
+ "SANE_CONSTRAINT_STRING_LIST" : "unknown");
+ }
+}
+
+/***************************** SANE API ****************************/
+
+
+SANE_Status
+sane_init (SANE_Int * __sane_unused__ version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+ SANE_Int linenumber;
+ SANE_Char line[PATH_MAX], *word;
+ SANE_String_Const cp;
+ SANE_Device *sane_device;
+ Test_Device *test_device, *previous_device;
+ SANE_Int num;
+
+ DBG_INIT ();
+ sanei_thread_init ();
+
+ test_device = 0;
+ previous_device = 0;
+
+ DBG (1, "sane_init: SANE test backend version %d.%d.%d from %s\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ if (inited)
+ DBG (3, "sane_init: warning: already inited\n");
+
+ fp = sanei_config_open (TEST_CONFIG_FILE);
+ if (fp)
+ {
+ linenumber = 0;
+ DBG (4, "sane_init: reading config file `%s'\n", TEST_CONFIG_FILE);
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ word = 0;
+ linenumber++;
+
+ cp = sanei_config_get_string (line, &word);
+ if (!word || cp == line)
+ {
+ DBG (5,
+ "sane_init: config file line %3d: ignoring empty line\n",
+ linenumber);
+ if (word)
+ free (word);
+ continue;
+ }
+ if (word[0] == '#')
+ {
+ DBG (5,
+ "sane_init: config file line %3d: ignoring comment line\n",
+ linenumber);
+ free (word);
+ continue;
+ }
+
+ DBG (5, "sane_init: config file line %3d: `%s'\n",
+ linenumber, line);
+ if (read_option (line, "number_of_devices", param_int,
+ &init_number_of_devices) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "mode", param_string,
+ &init_mode) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "hand-scanner", param_bool,
+ &init_hand_scanner) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "three-pass", param_bool,
+ &init_three_pass) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "three-pass-order", param_string,
+ &init_three_pass_order) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "resolution_min", param_fixed,
+ &resolution_range.min) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "resolution_max", param_fixed,
+ &resolution_range.max) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "resolution_quant", param_fixed,
+ &resolution_range.quant) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "resolution", param_fixed,
+ &init_resolution) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "depth", param_int,
+ &init_depth) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "scan-source", param_string,
+ &init_scan_source) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "test-picture", param_string,
+ &init_test_picture) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "invert-endianess", param_bool,
+ &init_invert_endianess) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "read-limit", param_bool,
+ &init_read_limit) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "read-limit-size", param_int,
+ &init_read_limit_size) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "read-delay", param_bool,
+ &init_read_delay) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "read-delay-duration", param_int,
+ &init_read_delay_duration) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "read-status-code", param_string,
+ &init_read_status_code) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "ppl-loss", param_int,
+ &init_ppl_loss) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "fuzzy-parameters", param_bool,
+ &init_fuzzy_parameters) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "non-blocking", param_bool,
+ &init_non_blocking) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "select-fd", param_bool,
+ &init_select_fd) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "enable-test-options", param_bool,
+ &init_enable_test_options) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "geometry_min", param_fixed,
+ &geometry_range.min) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "geometry_max", param_fixed,
+ &geometry_range.max) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "geometry_quant", param_fixed,
+ &geometry_range.quant) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "tl_x", param_fixed,
+ &init_tl_x) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "tl_y", param_fixed,
+ &init_tl_y) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "br_x", param_fixed,
+ &init_br_x) == SANE_STATUS_GOOD)
+ continue;
+ if (read_option (line, "br_y", param_fixed,
+ &init_br_y) == SANE_STATUS_GOOD)
+ continue;
+
+ DBG (3, "sane-init: I don't know how to handle option `%s'\n",
+ word);
+ } /* while */
+ fclose (fp);
+ } /* if */
+ else
+ {
+ DBG (3, "sane_init: couldn't find config file (%s), using default "
+ "settings\n", TEST_CONFIG_FILE);
+ }
+
+ /* create devices */
+ sane_device_list =
+ malloc ((init_number_of_devices + 1) * sizeof (sane_device));
+ if (!sane_device_list)
+ return SANE_STATUS_NO_MEM;
+ for (num = 0; num < init_number_of_devices; num++)
+ {
+ SANE_Char number_string[PATH_MAX];
+
+ test_device = malloc (sizeof (*test_device));
+ if (!test_device)
+ return SANE_STATUS_NO_MEM;
+ test_device->sane.vendor = "Noname";
+ test_device->sane.type = "virtual device";
+ test_device->sane.model = "frontend-tester";
+ snprintf (number_string, sizeof (number_string), "%d", num);
+ number_string[sizeof (number_string) - 1] = '\0';
+ test_device->name = strdup (number_string);
+ if (!test_device->name)
+ return SANE_STATUS_NO_MEM;
+ test_device->sane.name = test_device->name;
+ if (previous_device)
+ previous_device->next = test_device;
+ previous_device = test_device;
+ if (num == 0)
+ first_test_device = test_device;
+ sane_device_list[num] = &test_device->sane;
+ test_device->open = SANE_FALSE;
+ test_device->eof = SANE_FALSE;
+ test_device->scanning = SANE_FALSE;
+ test_device->cancelled = SANE_FALSE;
+ test_device->reader_pid = -1;
+ test_device->pipe = -1;
+ DBG (4, "sane_init: new device: `%s' is a %s %s %s\n",
+ test_device->sane.name, test_device->sane.vendor,
+ test_device->sane.model, test_device->sane.type);
+ }
+ test_device->next = 0;
+ sane_device_list[num] = 0;
+ srand (time (NULL));
+ random_factor = ((double) rand ()) / RAND_MAX + 0.5;
+ inited = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Test_Device *test_device, *previous_device;
+
+ DBG (2, "sane_exit\n");
+ if (!inited)
+ {
+ DBG (1, "sane_exit: not inited, call sane_init() first\n");
+ return;
+ }
+
+ test_device = first_test_device;
+ while (test_device)
+ {
+ DBG (4, "sane_exit: freeing device %s\n", test_device->name);
+ previous_device = test_device;
+ test_device = test_device->next;
+ if (previous_device->name)
+ free (previous_device->name);
+ free (previous_device);
+ }
+ DBG (4, "sane_exit: freeing device list\n");
+ if (sane_device_list)
+ free (sane_device_list);
+ sane_device_list = NULL;
+ first_test_device = NULL;
+ inited = SANE_FALSE;
+ return;
+}
+
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+
+ DBG (2, "sane_get_devices: device_list=%p, local_only=%d\n",
+ (void *) device_list, local_only);
+ if (!inited)
+ {
+ DBG (1, "sane_get_devices: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!device_list)
+ {
+ DBG (1, "sane_get_devices: device_list == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ *device_list = (const SANE_Device **) sane_device_list;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Test_Device *test_device = first_test_device;
+ SANE_Status status;
+
+ DBG (2, "sane_open: devicename = \"%s\", handle=%p\n",
+ devicename, (void *) handle);
+ if (!inited)
+ {
+ DBG (1, "sane_open: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!handle)
+ {
+ DBG (1, "sane_open: handle == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!devicename || strlen (devicename) == 0)
+ {
+ DBG (2, "sane_open: device name NULL or empty\n");
+ }
+ else
+ {
+ for (test_device = first_test_device; test_device;
+ test_device = test_device->next)
+ {
+ if (strcmp (devicename, test_device->name) == 0)
+ break;
+ }
+ }
+ if (!test_device)
+ {
+ DBG (1, "sane_open: device `%s' not found\n", devicename);
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->open)
+ {
+ DBG (1, "sane_open: device `%s' already open\n", devicename);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ DBG (2, "sane_open: opening device `%s', handle = %p\n", test_device->name,
+ (void *) test_device);
+ test_device->open = SANE_TRUE;
+ *handle = test_device;
+
+ status = init_options (test_device);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ test_device->open = SANE_TRUE;
+ test_device->scanning = SANE_FALSE;
+ test_device->cancelled = SANE_FALSE;
+ test_device->eof = SANE_FALSE;
+ test_device->bytes_total = 0;
+ test_device->pass = 0;
+ test_device->number_of_scans = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Test_Device *test_device = handle;
+
+ DBG (2, "sane_close: handle=%p\n", (void *) handle);
+ if (!inited)
+ {
+ DBG (1, "sane_close: not inited, call sane_init() first\n");
+ return;
+ }
+
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_close: handle %p unknown\n", (void *) handle);
+ return;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_close: handle %p not open\n", (void *) handle);
+ return;
+ }
+ test_device->open = SANE_FALSE;
+ return;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Test_Device *test_device = handle;
+
+ DBG (4, "sane_get_option_descriptor: handle=%p, option = %d\n",
+ (void *) handle, option);
+ if (!inited)
+ {
+ DBG (1, "sane_get_option_descriptor: not inited, call sane_init() "
+ "first\n");
+ return 0;
+ }
+
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_get_option_descriptor: handle %p unknown\n",
+ (void *) handle);
+ return 0;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_get_option_descriptor: not open\n");
+ return 0;
+ }
+ if (option < 0 || option >= num_options)
+ {
+ DBG (3, "sane_get_option_descriptor: option < 0 || "
+ "option > num_options\n");
+ return 0;
+ }
+
+ test_device->loaded[option] = 1;
+
+ return &test_device->opt[option];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *value, SANE_Int * info)
+{
+ Test_Device *test_device = handle;
+ SANE_Int myinfo = 0;
+ SANE_Status status;
+
+ DBG (4, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ (void *) handle, option, action, (void *) value, (void *) info);
+ if (!inited)
+ {
+ DBG (1, "sane_control_option: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_control_option: handle %p unknown\n", (void *) handle);
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_control_option: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->scanning)
+ {
+ DBG (1, "sane_control_option: is scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (option < 0 || option >= num_options)
+ {
+ DBG (1, "sane_control_option: option < 0 || option > num_options\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!test_device->loaded[option])
+ {
+ DBG (1, "sane_control_option: option not loaded\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (test_device->opt[option].cap))
+ {
+ DBG (1, "sane_control_option: option is inactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (test_device->opt[option].type == SANE_TYPE_GROUP)
+ {
+ DBG (1, "sane_control_option: option is a group\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ if (!SANE_OPTION_IS_SETTABLE (test_device->opt[option].cap))
+ {
+ DBG (1, "sane_control_option: option is not setable\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!(test_device->opt[option].cap & SANE_CAP_AUTOMATIC))
+ {
+ DBG (1, "sane_control_option: option is not automatically "
+ "setable\n");
+ return SANE_STATUS_INVAL;
+ }
+ switch (option)
+ {
+ case opt_bool_soft_select_soft_detect_auto:
+ test_device->val[option].w = SANE_TRUE;
+ DBG (4, "sane_control_option: set option %d (%s) automatically "
+ "to %s\n", option, test_device->opt[option].name,
+ test_device->val[option].w == SANE_TRUE ? "true" : "false");
+ break;
+
+ default:
+ DBG (1, "sane_control_option: trying to automatically set "
+ "unexpected option\n");
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (test_device->opt[option].cap))
+ {
+ DBG (1, "sane_control_option: option is not setable\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = sanei_constrain_value (&test_device->opt[option],
+ value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ switch (option)
+ {
+ case opt_tl_x: /* Fixed with parameter reloading */
+ case opt_tl_y:
+ case opt_br_x:
+ case opt_br_y:
+ case opt_resolution:
+ if (test_device->val[option].w == *(SANE_Fixed *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Fixed *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ DBG (4, "sane_control_option: set option %d (%s) to %.0f %s\n",
+ option, test_device->opt[option].name,
+ SANE_UNFIX (*(SANE_Fixed *) value),
+ test_device->opt[option].unit == SANE_UNIT_MM ? "mm" : "dpi");
+ break;
+ case opt_fixed: /* Fixed */
+ case opt_fixed_constraint_range:
+ if (test_device->val[option].w == *(SANE_Fixed *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Fixed *) value;
+ DBG (4, "sane_control_option: set option %d (%s) to %.0f\n",
+ option, test_device->opt[option].name,
+ SANE_UNFIX (*(SANE_Fixed *) value));
+ break;
+ case opt_read_limit_size: /* Int */
+ case opt_ppl_loss:
+ case opt_read_delay_duration:
+ case opt_int:
+ case opt_int_constraint_range:
+ if (test_device->val[option].w == *(SANE_Int *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Int *) value;
+ DBG (4, "sane_control_option: set option %d (%s) to %d\n",
+ option, test_device->opt[option].name, *(SANE_Int *) value);
+ break;
+ case opt_fuzzy_parameters: /* Bool with parameter reloading */
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_invert_endianess: /* Bool */
+ case opt_non_blocking:
+ case opt_select_fd:
+ case opt_bool_soft_select_soft_detect:
+ case opt_bool_soft_select_soft_detect_auto:
+ case opt_bool_soft_select_soft_detect_emulated:
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_depth: /* Word list with parameter and options reloading */
+ if (test_device->val[option].w == *(SANE_Int *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Int *) value;
+ if (test_device->val[option].w == 16)
+ test_device->opt[opt_invert_endianess].cap &= ~SANE_CAP_INACTIVE;
+ else
+ test_device->opt[opt_invert_endianess].cap |= SANE_CAP_INACTIVE;
+
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ DBG (4, "sane_control_option: set option %d (%s) to %d\n",
+ option, test_device->opt[option].name, *(SANE_Int *) value);
+ break;
+ case opt_three_pass_order: /* String list with parameter reload */
+ if (strcmp (test_device->val[option].s, value) == 0)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ strcpy (test_device->val[option].s, (SANE_String) value);
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name, (SANE_String) value);
+ break;
+ case opt_int_constraint_word_list: /* Word list */
+ case opt_fixed_constraint_word_list:
+ if (test_device->val[option].w == *(SANE_Int *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Int *) value;
+ DBG (4, "sane_control_option: set option %d (%s) to %d\n",
+ option, test_device->opt[option].name, *(SANE_Int *) value);
+ break;
+ case opt_read_status_code: /* String (list) */
+ case opt_test_picture:
+ case opt_string:
+ case opt_string_constraint_string_list:
+ case opt_string_constraint_long_string_list:
+ case opt_scan_source:
+ if (strcmp (test_device->val[option].s, value) == 0)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ strcpy (test_device->val[option].s, (SANE_String) value);
+ DBG (4, "sane_control_option: set option %d (%s) to `%s'\n",
+ option, test_device->opt[option].name, (SANE_String) value);
+ break;
+ case opt_int_array: /* Word array */
+ case opt_int_array_constraint_range:
+ case opt_int_array_constraint_word_list:
+ memcpy (test_device->val[option].wa, value,
+ test_device->opt[option].size);
+ DBG (4, "sane_control_option: set option %d (%s) to %p\n",
+ option, test_device->opt[option].name, (void *) value);
+ break;
+ /* options with side-effects */
+ case opt_print_options:
+ DBG (4, "sane_control_option: set option %d (%s)\n",
+ option, test_device->opt[option].name);
+ print_options (test_device);
+ break;
+ case opt_button:
+ DBG (0, "Yes! You pressed me!\n");
+ DBG (4, "sane_control_option: set option %d (%s)\n",
+ option, test_device->opt[option].name);
+ break;
+ case opt_mode:
+ if (strcmp (test_device->val[option].s, value) == 0)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ strcpy (test_device->val[option].s, (SANE_String) value);
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ if (strcmp (test_device->val[option].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ test_device->opt[opt_three_pass].cap &= ~SANE_CAP_INACTIVE;
+ if (test_device->val[opt_three_pass].w == SANE_TRUE)
+ test_device->opt[opt_three_pass_order].cap
+ &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ test_device->opt[opt_three_pass].cap |= SANE_CAP_INACTIVE;
+ test_device->opt[opt_three_pass_order].cap |= SANE_CAP_INACTIVE;
+ }
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name, (SANE_String) value);
+ break;
+ case opt_three_pass:
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ if (test_device->val[option].w == SANE_TRUE)
+ test_device->opt[opt_three_pass_order].cap &= ~SANE_CAP_INACTIVE;
+ else
+ test_device->opt[opt_three_pass_order].cap |= SANE_CAP_INACTIVE;
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_hand_scanner:
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ if (test_device->val[option].w == SANE_TRUE)
+ {
+ test_device->opt[opt_tl_x].cap |= SANE_CAP_INACTIVE;
+ test_device->opt[opt_tl_y].cap |= SANE_CAP_INACTIVE;
+ test_device->opt[opt_br_x].cap |= SANE_CAP_INACTIVE;
+ test_device->opt[opt_br_y].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ test_device->opt[opt_tl_x].cap &= ~SANE_CAP_INACTIVE;
+ test_device->opt[opt_tl_y].cap &= ~SANE_CAP_INACTIVE;
+ test_device->opt[opt_br_x].cap &= ~SANE_CAP_INACTIVE;
+ test_device->opt[opt_br_y].cap &= ~SANE_CAP_INACTIVE;
+ }
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_read_limit:
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ if (test_device->val[option].w == SANE_TRUE)
+ test_device->opt[opt_read_limit_size].cap &= ~SANE_CAP_INACTIVE;
+ else
+ test_device->opt[opt_read_limit_size].cap |= SANE_CAP_INACTIVE;
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_read_delay:
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ if (test_device->val[option].w == SANE_TRUE)
+ test_device->opt[opt_read_delay_duration].cap
+ &= ~SANE_CAP_INACTIVE;
+ else
+ test_device->opt[opt_read_delay_duration].cap |=
+ SANE_CAP_INACTIVE;
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n", option,
+ test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_enable_test_options:
+ {
+ int option_number;
+ if (test_device->val[option].w == *(SANE_Bool *) value)
+ {
+ DBG (4, "sane_control_option: option %d (%s) not changed\n",
+ option, test_device->opt[option].name);
+ break;
+ }
+ test_device->val[option].w = *(SANE_Bool *) value;
+ myinfo |= SANE_INFO_RELOAD_OPTIONS;
+ for (option_number = opt_bool_soft_select_soft_detect;
+ option_number < num_options; option_number++)
+ {
+ if (test_device->opt[option_number].type == SANE_TYPE_GROUP)
+ continue;
+ if (test_device->val[option].w == SANE_TRUE)
+ test_device->opt[option_number].cap &= ~SANE_CAP_INACTIVE;
+ else
+ test_device->opt[option_number].cap |= SANE_CAP_INACTIVE;
+ }
+ DBG (4, "sane_control_option: set option %d (%s) to %s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ }
+ default:
+ DBG (1, "sane_control_option: trying to set unexpected option\n");
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ case opt_num_opts:
+ *(SANE_Word *) value = num_options;
+ DBG (4, "sane_control_option: get option 0, value = %d\n",
+ num_options);
+ break;
+ case opt_tl_x: /* Fixed options */
+ case opt_tl_y:
+ case opt_br_x:
+ case opt_br_y:
+ case opt_resolution:
+ case opt_fixed:
+ case opt_fixed_constraint_range:
+ case opt_fixed_constraint_word_list:
+ {
+ *(SANE_Fixed *) value = test_device->val[option].w;
+ DBG (4,
+ "sane_control_option: get option %d (%s), value=%.1f %s\n",
+ option, test_device->opt[option].name,
+ SANE_UNFIX (*(SANE_Fixed *) value),
+ test_device->opt[option].unit ==
+ SANE_UNIT_MM ? "mm" : SANE_UNIT_DPI ? "dpi" : "");
+ break;
+ }
+ case opt_hand_scanner: /* Bool options */
+ case opt_three_pass:
+ case opt_invert_endianess:
+ case opt_read_limit:
+ case opt_read_delay:
+ case opt_fuzzy_parameters:
+ case opt_non_blocking:
+ case opt_select_fd:
+ case opt_bool_soft_select_soft_detect:
+ case opt_bool_hard_select_soft_detect:
+ case opt_bool_soft_detect:
+ case opt_enable_test_options:
+ case opt_bool_soft_select_soft_detect_emulated:
+ case opt_bool_soft_select_soft_detect_auto:
+ *(SANE_Bool *) value = test_device->val[option].w;
+ DBG (4,
+ "sane_control_option: get option %d (%s), value=%s\n",
+ option, test_device->opt[option].name,
+ *(SANE_Bool *) value == SANE_TRUE ? "true" : "false");
+ break;
+ case opt_mode: /* String (list) options */
+ case opt_three_pass_order:
+ case opt_read_status_code:
+ case opt_test_picture:
+ case opt_string:
+ case opt_string_constraint_string_list:
+ case opt_string_constraint_long_string_list:
+ case opt_scan_source:
+ strcpy (value, test_device->val[option].s);
+ DBG (4, "sane_control_option: get option %d (%s), value=`%s'\n",
+ option, test_device->opt[option].name, (SANE_String) value);
+ break;
+ case opt_depth: /* Int + word list options */
+ case opt_read_limit_size:
+ case opt_ppl_loss:
+ case opt_read_delay_duration:
+ case opt_int:
+ case opt_int_constraint_range:
+ case opt_int_constraint_word_list:
+ *(SANE_Int *) value = test_device->val[option].w;
+ DBG (4, "sane_control_option: get option %d (%s), value=%d\n",
+ option, test_device->opt[option].name, *(SANE_Int *) value);
+ break;
+ case opt_int_array: /* Int array */
+ case opt_int_array_constraint_range:
+ case opt_int_array_constraint_word_list:
+ memcpy (value, test_device->val[option].wa,
+ test_device->opt[option].size);
+ DBG (4, "sane_control_option: get option %d (%s), value=%p\n",
+ option, test_device->opt[option].name, (void *) value);
+ break;
+ default:
+ DBG (1, "sane_control_option: trying to get unexpected option\n");
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ default:
+ DBG (1, "sane_control_option: trying unexpected action %d\n", action);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (info)
+ *info = myinfo;
+
+ if(myinfo & SANE_INFO_RELOAD_OPTIONS){
+ SANE_Int i = 0;
+ for(i=1;i<num_options;i++){
+ test_device->loaded[i] = 0;
+ }
+ }
+
+ DBG (4, "sane_control_option: finished, info=%s %s %s \n",
+ myinfo & SANE_INFO_INEXACT ? "inexact" : "",
+ myinfo & SANE_INFO_RELOAD_PARAMS ? "reload_parameters" : "",
+ myinfo & SANE_INFO_RELOAD_OPTIONS ? "reload_options" : "");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Test_Device *test_device = handle;
+ SANE_Parameters *p;
+ double res, tl_x = 0, tl_y = 0, br_x = 0, br_y = 0;
+ SANE_String text_format, mode;
+ SANE_Int channels = 1;
+
+ DBG (2, "sane_get_parameters: handle=%p, params=%p\n",
+ (void *) handle, (void *) params);
+ if (!inited)
+ {
+ DBG (1, "sane_get_parameters: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_get_parameters: handle %p unknown\n", (void *) handle);
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_get_parameters: handle %p not open\n", (void *) handle);
+ return SANE_STATUS_INVAL;
+ }
+
+ res = SANE_UNFIX (test_device->val[opt_resolution].w);
+ mode = test_device->val[opt_mode].s;
+ p = &test_device->params;
+ p->depth = test_device->val[opt_depth].w;
+
+ if (test_device->val[opt_hand_scanner].w == SANE_TRUE)
+ {
+ tl_x = 0.0;
+ br_x = 110.0;
+ tl_y = 0.0;
+ br_y = 170.0;
+ p->lines = -1;
+ test_device->lines = (SANE_Word) (res * (br_y - tl_y) / MM_PER_INCH);
+ }
+ else
+ {
+ tl_x = SANE_UNFIX (test_device->val[opt_tl_x].w);
+ tl_y = SANE_UNFIX (test_device->val[opt_tl_y].w);
+ br_x = SANE_UNFIX (test_device->val[opt_br_x].w);
+ br_y = SANE_UNFIX (test_device->val[opt_br_y].w);
+ if (tl_x > br_x)
+ swap_double (&tl_x, &br_x);
+ if (tl_y > br_y)
+ swap_double (&tl_y, &br_y);
+ test_device->lines = (SANE_Word) (res * (br_y - tl_y) / MM_PER_INCH);
+ if (test_device->lines < 1)
+ test_device->lines = 1;
+ p->lines = test_device->lines;
+ if (test_device->val[opt_fuzzy_parameters].w == SANE_TRUE
+ && test_device->scanning == SANE_FALSE)
+ p->lines *= random_factor;
+ }
+
+ if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ {
+ p->format = SANE_FRAME_GRAY;
+ p->last_frame = SANE_TRUE;
+ }
+ else /* Color */
+ {
+ if (test_device->val[opt_three_pass].w == SANE_TRUE)
+ {
+ if (test_device->val[opt_three_pass_order].s[test_device->pass]
+ == 'R')
+ p->format = SANE_FRAME_RED;
+ else if (test_device->val[opt_three_pass_order].s[test_device->pass]
+ == 'G')
+ p->format = SANE_FRAME_GREEN;
+ else
+ p->format = SANE_FRAME_BLUE;
+ if (test_device->pass > 1)
+ p->last_frame = SANE_TRUE;
+ else
+ p->last_frame = SANE_FALSE;
+ }
+ else
+ {
+ p->format = SANE_FRAME_RGB;
+ p->last_frame = SANE_TRUE;
+ }
+ }
+
+ p->pixels_per_line = (SANE_Int) (res * (br_x - tl_x) / MM_PER_INCH);
+ if (test_device->val[opt_fuzzy_parameters].w == SANE_TRUE
+ && test_device->scanning == SANE_FALSE)
+ p->pixels_per_line *= random_factor;
+ if (p->pixels_per_line < 1)
+ p->pixels_per_line = 1;
+
+ if (p->format == SANE_FRAME_RGB)
+ channels = 3;
+
+ if (p->depth == 1)
+ p->bytes_per_line = channels * (int) ((p->pixels_per_line + 7) / 8);
+ else /* depth == 8 || depth == 16 */
+ p->bytes_per_line = channels * p->pixels_per_line * ((p->depth + 7) / 8);
+
+ test_device->bytes_per_line = p->bytes_per_line;
+
+ p->pixels_per_line -= test_device->val[opt_ppl_loss].w;
+ if (p->pixels_per_line < 1)
+ p->pixels_per_line = 1;
+ test_device->pixels_per_line = p->pixels_per_line;
+
+ switch (p->format)
+ {
+ case SANE_FRAME_GRAY:
+ text_format = "gray";
+ break;
+ case SANE_FRAME_RGB:
+ text_format = "rgb";
+ break;
+ case SANE_FRAME_RED:
+ text_format = "red";
+ break;
+ case SANE_FRAME_GREEN:
+ text_format = "green";
+ break;
+ case SANE_FRAME_BLUE:
+ text_format = "blue";
+ break;
+ default:
+ text_format = "unknown";
+ break;
+ }
+
+ DBG (3, "sane_get_parameters: format=%s\n", text_format);
+ DBG (3, "sane_get_parameters: last_frame=%s\n",
+ p->last_frame ? "true" : "false");
+ DBG (3, "sane_get_parameters: lines=%d\n", p->lines);
+ DBG (3, "sane_get_parameters: depth=%d\n", p->depth);
+ DBG (3, "sane_get_parameters: pixels_per_line=%d\n", p->pixels_per_line);
+ DBG (3, "sane_get_parameters: bytes_per_line=%d\n", p->bytes_per_line);
+
+ if (params)
+ *params = *p;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Test_Device *test_device = handle;
+ int pipe_descriptor[2];
+
+ DBG (2, "sane_start: handle=%p\n", handle);
+ if (!inited)
+ {
+ DBG (1, "sane_start: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_start: handle %p unknown\n", handle);
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_start: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->scanning
+ && (test_device->val[opt_three_pass].w == SANE_FALSE
+ && strcmp (test_device->val[opt_mode].s, SANE_VALUE_SCAN_MODE_COLOR) == 0))
+ {
+ DBG (1, "sane_start: already scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (strcmp (test_device->val[opt_mode].s, SANE_VALUE_SCAN_MODE_COLOR) == 0
+ && test_device->val[opt_three_pass].w == SANE_TRUE
+ && test_device->pass > 2)
+ {
+ DBG (1, "sane_start: already in last pass of three\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (test_device->pass == 0)
+ {
+ test_device->number_of_scans++;
+ DBG (3, "sane_start: scanning page %d\n", test_device->number_of_scans);
+
+ if ((strcmp (test_device->val[opt_scan_source].s, "Automatic Document Feeder") == 0) &&
+ (((test_device->number_of_scans) % 11) == 0))
+ {
+ DBG (1, "sane_start: Document feeder is out of documents!\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ test_device->scanning = SANE_TRUE;
+ test_device->cancelled = SANE_FALSE;
+ test_device->eof = SANE_FALSE;
+ test_device->bytes_total = 0;
+
+ sane_get_parameters (handle, 0);
+
+ if (test_device->params.lines == 0)
+ {
+ DBG (1, "sane_start: lines == 0\n");
+ test_device->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->params.pixels_per_line == 0)
+ {
+ DBG (1, "sane_start: pixels_per_line == 0\n");
+ test_device->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->params.bytes_per_line == 0)
+ {
+ DBG (1, "sane_start: bytes_per_line == 0\n");
+ test_device->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (pipe (pipe_descriptor) < 0)
+ {
+ DBG (1, "sane_start: pipe failed (%s)\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* create reader routine as new process or thread */
+ test_device->pipe = pipe_descriptor[0];
+ test_device->reader_fds = pipe_descriptor[1];
+ test_device->reader_pid =
+ sanei_thread_begin (reader_task, (void *) test_device);
+
+ if (test_device->reader_pid == -1)
+ {
+ DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ close (test_device->reader_fds);
+ test_device->reader_fds = -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Test_Device *test_device = handle;
+ SANE_Int max_scan_length;
+ ssize_t bytes_read;
+ size_t read_count;
+ SANE_Int bytes_total = test_device->lines * test_device->bytes_per_line;
+
+
+ DBG (4, "sane_read: handle=%p, data=%p, max_length = %d, length=%p\n",
+ handle, data, max_length, (void *) length);
+ if (!inited)
+ {
+ DBG (1, "sane_read: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_read: handle %p unknown\n", handle);
+ return SANE_STATUS_INVAL;
+ }
+ if (!length)
+ {
+ DBG (1, "sane_read: length == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (strcmp (test_device->val[opt_read_status_code].s, "Default") != 0)
+ {
+ SANE_String_Const sc = test_device->val[opt_read_status_code].s;
+ DBG (3, "sane_read: setting return status to %s\n", sc);
+ if (strcmp (sc, "SANE_STATUS_UNSUPPORTED") == 0)
+ return SANE_STATUS_UNSUPPORTED;
+ if (strcmp (sc, "SANE_STATUS_CANCELLED") == 0)
+ return SANE_STATUS_CANCELLED;
+ if (strcmp (sc, "SANE_STATUS_DEVICE_BUSY") == 0)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (strcmp (sc, "SANE_STATUS_INVAL") == 0)
+ return SANE_STATUS_INVAL;
+ if (strcmp (sc, "SANE_STATUS_EOF") == 0)
+ return SANE_STATUS_EOF;
+ if (strcmp (sc, "SANE_STATUS_JAMMED") == 0)
+ return SANE_STATUS_JAMMED;
+ if (strcmp (sc, "SANE_STATUS_NO_DOCS") == 0)
+ return SANE_STATUS_NO_DOCS;
+ if (strcmp (sc, "SANE_STATUS_COVER_OPEN") == 0)
+ return SANE_STATUS_COVER_OPEN;
+ if (strcmp (sc, "SANE_STATUS_IO_ERROR") == 0)
+ return SANE_STATUS_IO_ERROR;
+ if (strcmp (sc, "SANE_STATUS_NO_MEM") == 0)
+ return SANE_STATUS_NO_MEM;
+ if (strcmp (sc, "SANE_STATUS_ACCESS_DENIED") == 0)
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ max_scan_length = max_length;
+ if (test_device->val[opt_read_limit].w == SANE_TRUE
+ && test_device->val[opt_read_limit_size].w < max_scan_length)
+ {
+ max_scan_length = test_device->val[opt_read_limit_size].w;
+ DBG (3, "sane_read: limiting max_scan_length to %d bytes\n",
+ max_scan_length);
+ }
+
+ *length = 0;
+
+ if (!data)
+ {
+ DBG (1, "sane_read: data == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_read: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->cancelled)
+ {
+ DBG (1, "sane_read: scan was cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ if (test_device->eof)
+ {
+ DBG (2, "sane_read: No more data available, sending EOF\n");
+ return SANE_STATUS_EOF;
+ }
+ if (!test_device->scanning)
+ {
+ DBG (1, "sane_read: not scanning (call sane_start first)\n");
+ return SANE_STATUS_INVAL;
+ }
+ read_count = max_scan_length;
+
+ bytes_read = read (test_device->pipe, data, read_count);
+ if (bytes_read == 0
+ || (bytes_read + test_device->bytes_total >= bytes_total))
+ {
+ SANE_Status status;
+ DBG (2, "sane_read: EOF reached\n");
+ status = finish_pass (test_device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_read: finish_pass returned `%s'\n",
+ sane_strstatus (status));
+ return status;
+ }
+ test_device->eof = SANE_TRUE;
+ if (strcmp (test_device->val[opt_mode].s, SANE_VALUE_SCAN_MODE_COLOR) == 0
+ && test_device->val[opt_three_pass].w == SANE_TRUE)
+ {
+ test_device->pass++;
+ if (test_device->pass > 2)
+ test_device->pass = 0;
+ }
+ if (bytes_read == 0)
+ return SANE_STATUS_EOF;
+ }
+ else if (bytes_read < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ DBG (2, "sane_read: no data available, try again\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG (1, "sane_read: read returned error: %s\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ *length = bytes_read;
+ test_device->bytes_total += bytes_read;
+
+ DBG (2, "sane_read: read %ld bytes of %d, total %d\n", (long) bytes_read,
+ max_scan_length, test_device->bytes_total);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Test_Device *test_device = handle;
+
+ DBG (2, "sane_cancel: handle = %p\n", handle);
+ if (!inited)
+ {
+ DBG (1, "sane_cancel: not inited, call sane_init() first\n");
+ return;
+ }
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_cancel: handle %p unknown\n", handle);
+ return;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_cancel: not open\n");
+ return;
+ }
+ if (test_device->cancelled)
+ {
+ DBG (1, "sane_cancel: scan already cancelled\n");
+ return;
+ }
+ if (!test_device->scanning)
+ {
+ DBG (2, "sane_cancel: scan is already finished\n");
+ return;
+ }
+ finish_pass (test_device);
+ test_device->cancelled = SANE_TRUE;
+ test_device->scanning = SANE_FALSE;
+ test_device->eof = SANE_FALSE;
+ test_device->pass = 0;
+ return;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Test_Device *test_device = handle;
+
+ DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
+ non_blocking);
+ if (!inited)
+ {
+ DBG (1, "sane_set_io_mode: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_set_io_mode: handle %p unknown\n", handle);
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_set_io_mode: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->scanning)
+ {
+ DBG (1, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->val[opt_non_blocking].w == SANE_TRUE)
+ {
+ if (fcntl (test_device->pipe,
+ F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ DBG (1, "sane_set_io_mode: can't set io mode");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ {
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Test_Device *test_device = handle;
+
+ DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
+ fd ? "!=" : "=");
+ if (!inited)
+ {
+ DBG (1, "sane_get_select_fd: not inited, call sane_init() first\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!check_handle (handle))
+ {
+ DBG (1, "sane_get_select_fd: handle %p unknown\n", handle);
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->open)
+ {
+ DBG (1, "sane_get_select_fd: not open\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (!test_device->scanning)
+ {
+ DBG (1, "sane_get_select_fd: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (test_device->val[opt_select_fd].w == SANE_TRUE)
+ {
+ *fd = test_device->pipe;
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/test.conf.in b/backend/test.conf.in
new file mode 100644
index 0000000..458342e
--- /dev/null
+++ b/backend/test.conf.in
@@ -0,0 +1,87 @@
+# test backend configuration file
+########################################
+
+# number of test devices
+number_of_devices 2
+
+# Initial option values and ranges
+########################################
+
+# Scan mode (Color, Gray)
+mode Gray
+
+# Bit depth (1, 8, 16)
+depth 8
+
+# Hand-scanner mode (true, false)
+hand-scanner false
+
+# Three-pass mode (true, false)
+three-pass false
+
+# Three-pass order (RGB, RBG, GBR, GRB, BGR, BRG)
+three-pass-order RGB
+
+# Resolution (dpi)
+resolution_min 1.0
+resolution_max 1200.0
+resolution_quant 1.0
+resolution 50.0
+
+# Draw test picture ("Solid black", "Solid white", "Color pattern", "Grid")
+test-picture "Solid black"
+
+# Invert upper and lower byte in 16 bit modes
+invert-endianess false
+
+# Read-limit (true, false)
+read-limit false
+
+# Read-limit size (1 - 65536 bytes)
+read-limit-size 1
+
+# Read-delay (true, false)
+read-delay false
+
+# Read-delay duration (1000 - 200,000 microseconds)
+read-delay-duration 1000
+
+# Status code (return-value) of sane_read() ("Default",
+# "SANE_STATUS_UNSUPPORTED",
+# "SANE_STATUS_CANCELLED", "SANE_STATUS_DEVICE_BUSY", "SANE_STATUS_INVAL",
+# "SANE_STATUS_EOF", "SANE_STATUS_JAMMED", "SANE_STATUS_NO_DOCS",
+# "SANE_STATUS_COVER_OPEN", "SANE_STATUS_IO_ERROR", "SANE_STATUS_NO_MEM",
+# "SANE_STATUS_ACCESS_DENIED"
+read-status-code "Default"
+
+# Fuzzy parameters (true, false)
+fuzzy-parameters false
+
+# Loss of pixels per line (pixels)
+ppl-loss 0
+
+# Non-blocking io (true, false)
+non-blocking false
+
+# Support select fd (true, false)
+select-fd false
+
+# Enable test options (true, false)
+enable-test-options false
+
+# Geometry (mm)
+geometry_min 0.0
+geometry_max 200.0
+geometry_quant 1.0
+
+# Top-left x position (mm)
+tl_x 0.0
+
+# Top-left y position (mm)
+tl_y 0.0
+
+# Bottom-right x position (mm)
+br_x 80.0
+
+# Bottom-right y position (mm)
+br_y 100.0
diff --git a/backend/test.h b/backend/test.h
new file mode 100644
index 0000000..dcd54b6
--- /dev/null
+++ b/backend/test.h
@@ -0,0 +1,146 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002-2006 Henning Meier-Geinitz <henning@meier-geinitz.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This backend is for testing frontends.
+*/
+
+#ifndef test_h
+#define test_h
+
+
+typedef enum
+{
+ param_none = 0,
+ param_bool,
+ param_int,
+ param_fixed,
+ param_string
+}
+parameter_type;
+
+typedef enum
+{
+ opt_num_opts = 0,
+ opt_mode_group,
+ opt_mode,
+ opt_depth,
+ opt_hand_scanner,
+ opt_three_pass, /* 5 */
+ opt_three_pass_order,
+ opt_resolution,
+ opt_scan_source,
+ opt_special_group,
+ opt_test_picture,
+ opt_invert_endianess,
+ opt_read_limit,
+ opt_read_limit_size,
+ opt_read_delay,
+ opt_read_delay_duration,
+ opt_read_status_code,
+ opt_ppl_loss,
+ opt_fuzzy_parameters,
+ opt_non_blocking,
+ opt_select_fd,
+ opt_enable_test_options,
+ opt_print_options,
+ opt_geometry_group,
+ opt_tl_x,
+ opt_tl_y,
+ opt_br_x,
+ opt_br_y,
+ opt_bool_group,
+ opt_bool_soft_select_soft_detect,
+ opt_bool_hard_select_soft_detect,
+ opt_bool_hard_select,
+ opt_bool_soft_detect,
+ opt_bool_soft_select_soft_detect_emulated,
+ opt_bool_soft_select_soft_detect_auto,
+ opt_int_group,
+ opt_int,
+ opt_int_constraint_range,
+ opt_int_constraint_word_list,
+ opt_int_array,
+ opt_int_array_constraint_range,
+ opt_int_array_constraint_word_list,
+ opt_fixed_group,
+ opt_fixed,
+ opt_fixed_constraint_range,
+ opt_fixed_constraint_word_list,
+ opt_string_group,
+ opt_string,
+ opt_string_constraint_string_list,
+ opt_string_constraint_long_string_list,
+ opt_button_group,
+ opt_button,
+ /* must come last: */
+ num_options
+}
+test_opts;
+
+
+typedef struct Test_Device
+{
+ struct Test_Device *next;
+ SANE_Device sane;
+ SANE_Option_Descriptor opt[num_options];
+ Option_Value val[num_options];
+ SANE_Bool loaded[num_options];
+ SANE_Parameters params;
+ SANE_String name;
+ SANE_Pid reader_pid;
+ SANE_Int reader_fds;
+ SANE_Int pipe;
+ FILE *pipe_handle;
+ SANE_Word pass;
+ SANE_Word bytes_per_line;
+ SANE_Word pixels_per_line;
+ SANE_Word lines;
+ SANE_Int bytes_total;
+ SANE_Bool open;
+ SANE_Bool scanning;
+ SANE_Bool cancelled;
+ SANE_Bool eof;
+ SANE_Int number_of_scans;
+}
+Test_Device;
+
+#endif /* test_h */
diff --git a/backend/u12-ccd.c b/backend/u12-ccd.c
new file mode 100644
index 0000000..3f1ad77
--- /dev/null
+++ b/backend/u12-ccd.c
@@ -0,0 +1,1121 @@
+/* @file u12-ccd.c
+ * @brief here we have the whole code to intialize the CCD and DAC stuff
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/*************************** some definitions ********************************/
+
+#define _REFLECTION 0
+#define _TRANSPARENCY 1
+#define _NEGATIVE 2
+
+#define _NUM_OF_CCDREGS_W8143 25
+#define _NUM_OF_DACREGS_W8143 10
+
+#define _NUM_OF_CCDREGS_S1224 29
+#define _NUM_OF_DACREGS_S1224 7
+
+#define _NUM_OF_CCDREGS_S8531 29
+#define _NUM_OF_DACREGS_S8531 9
+
+/*************************** some local vars *********************************/
+
+static pFnVoid u12ccd_InitFunc = NULL;
+
+static RegDef W3797CCDParams[4][_NUM_OF_CCDREGS_W8143] = {
+ {
+ {0x2c, 0x02}, {0x39, 0x2a}, {0x3a, 0x0a}, {0x3b, 0x37},
+ {0x3c, 0x16}, {0x41, 0x0e}, {0x42, 0x90}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x03},
+ {0x48, 0x27}, {0x49, 0x2f}, {0x4a, 0x09}, {0x4b, 0x03},
+ {0x4c, 0x07}, {0x4d, 0x06}, {0x4e, 0x06}, {0x67, 0x00},
+ {0x50, 0x08}, {0x51, 0x0e}, {0x52, 0x0c}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x3d}, {0x3a, 0x04}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x1f}, {0x42, 0x8c}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x00},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x01}, {0x51, 0x06}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x04}, {0x42, 0x1e}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xf9}, {0x4b, 0x04},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x03}, {0x67, 0x00},
+ {0x50, 0x06}, {0x51, 0x03}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x18}, {0x3a, 0x04}, {0x3b, 0x1d},
+ {0x3c, 0x03}, {0x41, 0x0c}, {0x42, 0x84}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x04},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x00}, {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03},
+ {0xf0, 0x00}
+ }
+};
+
+static RegDef W3799CCDParams[4][_NUM_OF_CCDREGS_W8143] = {
+ {
+ {0x2c, 0x02}, {0x39, 0x2c}, {0x3a, 0x05}, {0x3b, 0x3c},
+ {0x3c, 0x0e}, {0x41, 0x0e}, {0x42, 0x90}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x02},
+ {0x48, 0x27}, {0x49, 0x2f}, {0x4a, 0x09}, {0x4b, 0x05},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x06}, {0x67, 0x00},
+ {0x50, 0x08}, {0x51, 0x0d}, {0x52, 0x0c}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x3d}, {0x3a, 0x04}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x1f}, {0x42, 0x8c}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x00},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x01}, {0x51, 0x06}, {0x52, 0x12}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x02}, {0x3b, 0x1a},
+ {0x3c, 0x05}, {0x41, 0x04}, {0x42, 0x1e}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xf9}, {0x4b, 0x04},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x03}, {0x67, 0x00},
+ {0x50, 0x06}, {0x51, 0x03}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x18}, {0x3a, 0x04}, {0x3b, 0x1d},
+ {0x3c, 0x03}, {0x41, 0x0c}, {0x42, 0x84}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x03},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x00}, {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03},
+ {0xf0, 0x00}
+ }
+};
+
+/* Genius ColorPage Vivid III */
+static RegDef W548CCDParams[4][_NUM_OF_CCDREGS_W8143] = {
+ {
+ {0x2c, 0x02}, {0x39, 0x2c}, {0x3a, 0x05}, {0x3b, 0x3c},
+ {0x3c, 0x0e}, {0x41, 0x0e}, {0x42, 0x90}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x02},
+ {0x48, 0x27}, {0x49, 0x2f}, {0x4a, 0x09}, {0x4b, 0x05},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x06}, {0x67, 0x00},
+ {0x50, 0x08}, {0x51, 0x0d}, {0x52, 0x0c}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x3d}, {0x3a, 0x04}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x1f}, {0x42, 0x8c}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x00},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x01}, {0x51, 0x06}, {0x52, 0x12}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x02}, {0x3b, 0x1a},
+ {0x3c, 0x05}, {0x41, 0x04}, {0x42, 0x1e}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xf9}, {0x4b, 0x04},
+ {0x4c, 0x07}, {0x4d, 0x05}, {0x4e, 0x03}, {0x67, 0x00},
+ {0x50, 0x06}, {0x51, 0x03}, {0x52, 0x09}, {0x53, 0x0b},
+ {0xf0, 0x00}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x18}, {0x3a, 0x04}, {0x3b, 0x1d},
+ {0x3c, 0x03}, {0x41, 0x0c}, {0x42, 0x84}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x03},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x67, 0x00},
+ {0x50, 0x00}, {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03},
+ {0xf0, 0x00}
+ }
+};
+
+static RegDef S3797CCDParams[4][_NUM_OF_CCDREGS_S1224] = {
+ {
+ {0x2c, 0x00}, {0x39, 0x2a}, {0x3a, 0x0a}, {0x3b, 0x37},
+ {0x3c, 0x16}, {0x41, 0x2c}, {0x42, 0x9f}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x03},
+ {0x48, 0x27}, {0x49, 0x27}, {0x4a, 0x09}, {0x4b, 0x0b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x13},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x20}, {0x70, 0x06}, {0x60, 0x07}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x3d}, {0x3a, 0x06}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x3d}, {0x42, 0x92}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x1b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x23},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x30}, {0x70, 0x06}, {0x60, 0x17}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x09},
+ {0x4c, 0x13}, {0x4d, 0x14}, {0x4e, 0x09}, {0x50, 0x09},
+ {0x51, 0x14}, {0x52, 0x13}, {0x53, 0x01}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x02},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x09},
+ {0x4c, 0x13}, {0x4d, 0x14}, {0x4e, 0x09}, {0x50, 0x09},
+ {0x51, 0x14}, {0x52, 0x13}, {0x53, 0x01}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }
+};
+
+static RegDef S3799CCDParams[4][_NUM_OF_CCDREGS_S1224] = {
+ {
+ {0x2c, 0x00}, {0x39, 0x2a}, {0x3a, 0x0a}, {0x3b, 0x37},
+ {0x3c, 0x16}, {0x41, 0x2c}, {0x42, 0x8f}, {0x43, 0x01},
+ {0x44, 0x27}, {0x45, 0x27}, {0x46, 0x01}, {0x47, 0x01},
+ {0x48, 0x27}, {0x49, 0x27}, {0x4a, 0x09}, {0x4b, 0x0b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x13},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x20}, {0x70, 0x06}, {0x60, 0x07}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x3d}, {0x3a, 0x06}, {0x3b, 0x46},
+ {0x3c, 0x06}, {0x41, 0x3d}, {0x42, 0x92}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf2}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x1b},
+ {0x4c, 0x06}, {0x4d, 0x06}, {0x4e, 0x0b}, {0x50, 0x23},
+ {0x51, 0x06}, {0x52, 0x06}, {0x53, 0x0b}, {0x67, 0x00},
+ {0x6f, 0x30}, {0x70, 0x06}, {0x60, 0x17}, {0x61, 0x9f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x02}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x01},
+ {0x44, 0x13}, {0x45, 0x13}, {0x46, 0xf1}, {0x47, 0x01},
+ {0x48, 0x13}, {0x49, 0x13}, {0x4a, 0xfa}, {0x4b, 0x09},
+ {0x4c, 0x13}, {0x4d, 0x14}, {0x4e, 0x09}, {0x50, 0x09},
+ {0x51, 0x14}, {0x52, 0x13}, {0x53, 0x01}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }, {
+ {0x2c, 0x00}, {0x39, 0x16}, {0x3a, 0x03}, {0x3b, 0x1f},
+ {0x3c, 0x07}, {0x41, 0x1c}, {0x42, 0x99}, {0x43, 0x03},
+ {0x44, 0x0a}, {0x45, 0x08}, {0x46, 0xfa}, {0x47, 0x03},
+ {0x48, 0x0a}, {0x49, 0x08}, {0x4a, 0xf2}, {0x4b, 0x02},
+ {0x4c, 0x03}, {0x4d, 0x02}, {0x4e, 0x0e}, {0x50, 0x00},
+ {0x51, 0x09}, {0x52, 0x03}, {0x53, 0x03}, {0x67, 0x00},
+ {0x6f, 0xff}, {0x70, 0x7f}, {0x60, 0x04}, {0x61, 0x8f},
+ {0x65, 0x01}
+ }
+};
+
+static RegDef WolfsonDAC8143[_NUM_OF_DACREGS_W8143] = {
+ {0x01, 0x01}, {0x02, 0x04}, {0x03, 0x42}, {0x05, 0x10}, {0x20, 0xd0},
+ {0x21, 0xd0}, {0x22, 0xd0}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}
+};
+
+static RegDef SamsungDAC8531[_NUM_OF_DACREGS_S8531] = {
+ {0x00, 0x51}, {0x02, 0x01}, {0x01, 0x80},
+ {0x00, 0x55}, {0x02, 0x01}, {0x01, 0x80},
+ {0x00, 0x59}, {0x02, 0x01}, {0x01, 0x80}
+};
+
+static RegDef SamsungDAC1224[_NUM_OF_DACREGS_S1224] ={
+ {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
+ {0x03, 0x80}, {0x04, 0x06}, {0x05, 0x06}, {0x06, 0x06}
+};
+
+static ShadingVarDef ShadingVar3797[3] = {
+ {
+ {{99, 100, 94}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x04, 0x00, 0x00}}, {{0xcc, 0xcc, 0xcc}}, 0
+ }, {
+ {{100, 90, 100}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0xcc, 0xcc, 0xcc}}, 0
+ }, {
+ {{90, 90, 90}}, {{0x30, 0x30, 0x30}}, {{0x20, 0x20, 0x20}},
+ {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+static ShadingVarDef ShadingVar3799[3] = {
+ {
+ {{100, 97, 92}}, {{0x90, 0xe0, 0x80}},
+ {{0x70, 0xc0, 0x60}}, {{0x90, 0x34, 0x3c}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{75, 75, 75}}, {{0x30, 0x30, 0x30}},
+ {{0x10, 0x10, 0x10}}, {{0x20, 0x20, 0x20}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{80, 75, 64}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+/* Genius ColorPage Vivid III */
+static ShadingVarDef ShadingVar548[3] = {
+ {
+ {{100, 97, 92}}, {{0x90, 0xe0, 0x80}},
+ {{0x70, 0xc0, 0x60}}, {{0x90, 0x34, 0x3c}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{75, 75, 75}}, {{0x30, 0x30, 0x30}},
+ {{0x10, 0x10, 0x10}}, {{0x20, 0x20, 0x20}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{80, 75, 64}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+static ShadingVarDef ShadingVar3777[3] = {
+ {
+ {{100, 100, 100}}, {{0x90, 0xe0, 0x80}},
+ {{0x70, 0xc0, 0x60}}, {{0x90, 0x34, 0x3c}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{75, 75, 75}}, {{0x30, 0x30, 0x30}},
+ {{0x10, 0x10, 0x10}}, {{0x20, 0x20, 0x20}}, {{0x80, 0x80, 0x80}}, 0
+ }, {
+ {{80, 75, 64}}, {{0x30, 0x30, 0x30}},
+ {{0x20, 0x20, 0x20}}, {{0x10, 0x10, 0x10}}, {{0x80, 0x80, 0x80}}, 0
+ }
+};
+
+/*************************** local functions *********************************/
+
+/**
+ */
+static void fnCCDInitWolfson3797( U12_Device *dev )
+{
+ if( dev->shade.intermediate & _ScanMode_Mono )
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xcc;
+ else
+ if( dev->shade.intermediate & _ScanMode_AverageOut)
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x68;
+ else
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xa0;
+
+ if((dev->shade.intermediate & _ScanMode_AverageOut) ||
+ (dev->DataInf.dwScanFlag & _SCANDEF_Negative)) {
+ WolfsonDAC8143[3].val = 0x12;
+ } else {
+ WolfsonDAC8143[3].val = 0x10;
+ }
+}
+
+/**
+ */
+static void fnCCDInitSamsung3797( U12_Device *dev )
+{
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA)) {
+
+ if (!(dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ if( dev->PCBID == _OPTICWORKS2000 ) {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 102;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 102;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 97;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x40;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x40;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x40;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x40;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x40;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x38;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x40;
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 99;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 101;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 94;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x40;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x40;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x40;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x04;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x00;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x00;
+ }
+ } else {
+ if( dev->PCBID == _OPTICWORKS2000 ) {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 100;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 100;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 96;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x48;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x48;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x38;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x38;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x48;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x48;
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 100; /* 98 */
+ dev->shade.pCcdDac->GainResize.Colors.Green = 103; /* 106 */
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 96; /* 96 */
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x10;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x10;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x110;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x1f0;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x190;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x100;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x1e0;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x180;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x10;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x20;
+ }
+ }
+ }
+}
+
+/**
+ */
+static void fnCCDInitWolfson3799( U12_Device *dev )
+{
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_Negative)) {
+
+ if (!(dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ dev->shade.pCcdDac->GainResize.Colors.Red = 103;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 102;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 99;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0xc8;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xc8;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0xc8;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x40;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x18;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x2c;
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 100;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 98;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 95;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x0;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x0;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x0;
+ }
+ } else {
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = -0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = -0x108;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = -0x1c8;
+ }
+}
+
+/**
+ */
+static void fnCCDInitWolfson548( U12_Device *dev )
+{
+ if (!(dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ dev->shade.pCcdDac->GainResize.Colors.Red = 103;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 102;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 99;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0xc8;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xc8;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0xc8;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x40;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x18;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x2c;
+
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 100;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 98;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 95;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x0;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x0;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x0;
+ }
+}
+
+/**
+ */
+static void fnCCDInitSamsung3777( U12_Device *dev )
+{
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_Negative)) {
+
+ if (!(dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ dev->shade.pCcdDac->GainResize.Colors.Red = 109;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 108;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 105;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x4a;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x4a;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x4a;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x3c;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x38;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x38;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x2c;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x3C;
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 108;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 107;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 104;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x50;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x50;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x50;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x40;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x40;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x40;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x20;
+ }
+ } else {
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = -0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = -0x108;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = -0x1c8;
+ }
+}
+
+/**
+ */
+static void fnCCDInitSamsung3799( U12_Device *dev )
+{
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA)) {
+
+ if (!(dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ if( dev->PCBID == _SCANNER2Button ) {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 109;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 109;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 105;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x68;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x68;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x68;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x24; /* 0 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x20; /* 0 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x1c; /* 0 */
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 98;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 97;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 92;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x90;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x90;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x90;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0xc0; /* 0x90 */
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0xc0; /* 0xe0 */
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0xc0; /* 0x80 */
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0xb0; /* 0x70 */
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0xb0; /* 0xc0 */
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0xb0; /* 0x60 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x24; /* 0x90 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x00; /* 0x34 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x0c; /* 0x3c */
+ }
+ } else {
+ if( dev->PCBID == _SCANNER2Button ) {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 107;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 106;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 103;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x48;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x48;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x30;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x28; /* 0 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x18; /* 0 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x20; /* 0 */
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 104;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 107;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 99;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x30; /* 0x80 */
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x30;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x30; /* 0x0a0 */
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x150; /* 0x170 */
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x130; /* 0x90 */
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x110; /* 0x130 */
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x140; /* 0x150 */
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x120; /* 0x70 */
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x100; /* 0x120 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0xF0; /* 0x90 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0xD4; /* 0x50 */
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0xCC; /* 0x60 */
+ }
+ }
+ }
+}
+
+/**
+ */
+static void fnCCDInitESIC3799( U12_Device *dev )
+{
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_Negative)) {
+
+ if (!(dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ dev->shade.pCcdDac->GainResize.Colors.Red = 100;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 99;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 94;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0xc8;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xc8;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0xc8;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x58;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x38;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x48;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x48;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x58;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0x48;
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red = 100;
+ dev->shade.pCcdDac->GainResize.Colors.Green = 98;
+ dev->shade.pCcdDac->GainResize.Colors.Blue = 93;
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0xd0;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0xd0;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0xd0;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x108;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0xf8;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0xc8;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x100;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0xf0;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0xc0;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = 0x108;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = 0xf8;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = 0xc8;
+ }
+ } else {
+ dev->shade.pCcdDac->DarkDAC.Colors.Red = 0x80;
+ dev->shade.pCcdDac->DarkDAC.Colors.Green = 0x80;
+ dev->shade.pCcdDac->DarkDAC.Colors.Blue = 0x80;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Red = 0x28;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Green = 0x28;
+ dev->shade.pCcdDac->DarkCmpHi.Colors.Blue = 0x28;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Red = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Green = 0x20;
+ dev->shade.pCcdDac->DarkCmpLo.Colors.Blue = 0x20;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Red = -0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Green = -0x38;
+ dev->shade.pCcdDac->DarkOffSub.Colors.Blue = -0x38;
+ }
+}
+
+/**
+ */
+static void
+fnDarkOffsetWolfson3797( U12_Device *dev, ShadingVarDef *sTbl, u_long dwCh )
+{
+ if(( dev->shade.DarkOffset.wColors[dwCh] -=
+ sTbl->DarkOffSub.wColors[dwCh]) > 0xfff ) {
+ dev->shade.DarkOffset.wColors[dwCh] = 0;
+ }
+}
+
+/**
+ */
+static void
+fnDarkOffsetSamsung3777( U12_Device *dev, ShadingVarDef *sTbl, u_long dwCh )
+{
+ dev->shade.DarkOffset.wColors[dwCh] += sTbl->DarkOffSub.wColors [dwCh];
+}
+
+/**
+ */
+static void
+fnDarkOffsetSamsung3797( U12_Device *dev, ShadingVarDef *sTbl, u_long dwCh )
+{
+ if( dev->shade.DarkOffset.wColors[dwCh] > sTbl->DarkOffSub.wColors[dwCh] )
+ dev->shade.DarkOffset.wColors[dwCh] -= sTbl->DarkOffSub.wColors[dwCh];
+ else
+ dev->shade.DarkOffset.wColors[dwCh] = 0;
+}
+
+/**
+ */
+static void
+fnDarkOffsetSamsung3799( U12_Device *dev, ShadingVarDef *sTbl, u_long dwCh )
+{
+ if( dev->shade.DarkOffset.wColors[dwCh] > sTbl->DarkOffSub.wColors[dwCh])
+ dev->shade.DarkOffset.wColors[dwCh] -= sTbl->DarkOffSub.wColors[dwCh];
+ else
+ dev->shade.DarkOffset.wColors[dwCh] = 0;
+}
+
+/**
+ */
+static void fnDACDarkWolfson( U12_Device *dev, ShadingVarDef *sTbl,
+ u_long dwCh, u_short wDarkest )
+{
+ u_short w = dev->shade.DarkDAC.bColors[dwCh];
+
+ if( wDarkest > sTbl->DarkCmpHi.wColors[dwCh] ) {
+
+ wDarkest -= sTbl->DarkCmpHi.wColors[dwCh];
+ if( wDarkest > dev->shade.wDarkLevels)
+ w += (u_short)wDarkest / dev->shade.wDarkLevels;
+ else
+ w++;
+
+ if (w > 0xff)
+ w = 0xff;
+
+ if(w != (u_short)dev->shade.DarkDAC.bColors[dwCh] ) {
+ dev->shade.DarkDAC.bColors[dwCh] = (SANE_Byte)w;
+ dev->shade.fStop = SANE_FALSE;
+ }
+ } else {
+ if((wDarkest < sTbl->DarkCmpLo.wColors[dwCh]) &&
+ dev->shade.DarkDAC.bColors[dwCh]) {
+ if( wDarkest )
+ w = (u_short)dev->shade.DarkDAC.bColors[dwCh] - 2U;
+ else
+ w = (u_short)dev->shade.DarkDAC.bColors[dwCh] - dev->shade.wDarkLevels;
+
+ if ((short) w < 0)
+ w = 0;
+
+ if( w != (u_short)dev->shade.DarkDAC.bColors[dwCh] ) {
+ dev->shade.DarkDAC.bColors [dwCh] = (SANE_Byte)w;
+ dev->shade.fStop = SANE_FALSE;
+ }
+ }
+ }
+}
+
+/**
+ */
+static void fnDACDarkSamsung( U12_Device *dev, ShadingVarDef *sTbl,
+ u_long dwCh, u_short wDarkest )
+{
+ u_short w;
+
+ if( wDarkest > sTbl->DarkCmpHi.wColors[dwCh] ) {
+
+ wDarkest -= sTbl->DarkCmpHi.wColors[dwCh];
+ if( wDarkest > dev->shade.wDarkLevels )
+ w = (u_short)dev->shade.DarkDAC.bColors[dwCh] -
+ wDarkest / dev->shade.wDarkLevels;
+ else
+ w = (u_short)dev->shade.DarkDAC.bColors[dwCh] - 1U;
+
+ if((short) w < 0)
+ w = 0;
+
+ if(w != (u_short)dev->shade.DarkDAC.bColors[dwCh]) {
+ dev->shade.DarkDAC.bColors [dwCh] = (SANE_Byte)w;
+ dev->shade.fStop = SANE_FALSE;
+ }
+ } else {
+ if((wDarkest < sTbl->DarkCmpLo.wColors[dwCh]) &&
+ dev->shade.DarkDAC.bColors[dwCh]) {
+ if( wDarkest )
+ w = (u_short)dev->shade.DarkDAC.bColors[dwCh] + 2U;
+ else
+ w = dev->shade.wDarkLevels + (u_short)dev->shade.DarkDAC.bColors[dwCh];
+
+ if (w > 0xff)
+ w = 0xff;
+
+ if(w != (u_short)dev->shade.DarkDAC.bColors[dwCh]) {
+ dev->shade.DarkDAC.bColors[dwCh] = (SANE_Byte)w;
+ dev->shade.fStop = SANE_FALSE;
+ }
+ }
+ }
+}
+
+/************************ exported functions *********************************/
+
+/** according to detected CCD and DAC, we set their correct init values
+ * and functions
+ */
+static void u12ccd_InitCCDandDAC( U12_Device *dev, SANE_Bool shading )
+{
+ u_short w;
+ ShadingVarDef *pDAC_CCD;
+
+ DBG( _DBG_INFO, "CCD & DAC init\n" );
+
+ /* some presets */
+ dev->f0_8_16 = SANE_FALSE;
+
+ switch( dev->DACType ) {
+
+ case _DA_WOLFSON8143:
+
+ DBG( _DBG_INFO, "* DAC: WOLFSON 8143\n" );
+ switch( dev->CCDID ) {
+
+ case _CCD_3797:
+ DBG( _DBG_INFO, "* CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ u12ccd_InitFunc = fnCCDInitWolfson3797;
+ dev->fnDarkOffset = fnDarkOffsetWolfson3797;
+ dev->fnDACDark = fnDACDarkWolfson;
+ dev->CCDRegs = (RegDef*)W3797CCDParams;
+ break;
+
+ case _CCD_548:
+ DBG( _DBG_INFO, "* CCD-548\n" );
+ pDAC_CCD = ShadingVar548;
+ u12ccd_InitFunc = fnCCDInitWolfson548;
+ dev->fnDarkOffset = fnDarkOffsetWolfson3797;
+ dev->fnDACDark = fnDACDarkWolfson;
+ dev->CCDRegs = (RegDef*)W548CCDParams;
+ break;
+
+
+ default:
+ DBG( _DBG_INFO, "* CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ u12ccd_InitFunc = fnCCDInitWolfson3799;
+/* CHECK: org was to fnDarkOffsetWolfson3797 */
+ dev->fnDarkOffset = fnDarkOffsetWolfson3797;
+ dev->fnDACDark = fnDACDarkWolfson;
+ dev->CCDRegs = (RegDef*)W3799CCDParams;
+ }
+
+ dev->numCCDRegs = _NUM_OF_CCDREGS_W8143;
+ dev->numDACRegs = _NUM_OF_DACREGS_W8143;
+ dev->DACRegs = WolfsonDAC8143;
+ dev->RegDACOffset.Red = 0x20;
+ dev->RegDACOffset.Green = 0x21;
+ dev->RegDACOffset.Blue = 0x22;
+ dev->RegDACGain.Red = 0x28;
+ dev->RegDACGain.Green = 0x29;
+ dev->RegDACGain.Blue = 0x2a;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut ) {
+ dev->shade.bUniGain = 1;
+ dev->shade.bGainDouble = 1;
+ } else {
+ dev->shade.bUniGain = 2;
+ dev->shade.bGainDouble = 4;
+ }
+ dev->shade.bMinGain = 1;
+ dev->shade.bMaxGain = 0x1f;
+ dev->shade.wDarkLevels = 10;
+
+ if( dev->shade.intermediate == _ScanMode_Color )
+ WolfsonDAC8143[2].val = 0x52;
+ else
+ WolfsonDAC8143[2].val = 0x42;
+
+ if (dev->shade.intermediate == _ScanMode_Mono )
+ WolfsonDAC8143[0].val = 7;
+ else
+ WolfsonDAC8143[0].val = 3;
+ break;
+
+ case _DA_SAMSUNG1224:
+ DBG( _DBG_INFO, "* DAC: Samsung 1224\n" );
+
+ switch( dev->CCDID ) {
+
+ case _CCD_3797:
+ DBG( _DBG_INFO, "* CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ u12ccd_InitFunc = fnCCDInitSamsung3797;
+ dev->fnDarkOffset = fnDarkOffsetSamsung3797;
+ dev->fnDACDark = fnDACDarkSamsung;
+ dev->CCDRegs = (RegDef*)S3797CCDParams;
+ break;
+
+ default:
+ DBG( _DBG_INFO, "* CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ u12ccd_InitFunc = fnCCDInitSamsung3799;
+ dev->fnDarkOffset = fnDarkOffsetSamsung3799;
+ dev->fnDACDark = fnDACDarkSamsung;
+ dev->CCDRegs = (RegDef*)S3799CCDParams;
+ }
+ dev->numCCDRegs = _NUM_OF_CCDREGS_S1224;
+ dev->numDACRegs = _NUM_OF_DACREGS_S1224;
+ dev->DACRegs = SamsungDAC1224;
+ dev->RegDACOffset.Red = 1;
+ dev->RegDACOffset.Green = 2;
+ dev->RegDACOffset.Blue = 3;
+ dev->RegDACGain.Red = 4;
+ dev->RegDACGain.Green = 5;
+ dev->RegDACGain.Blue = 6;
+ dev->shade.bGainDouble = 6;
+ dev->shade.bUniGain = 7;
+ dev->shade.bMinGain = 0;
+ dev->shade.bMaxGain = 0x1f;
+ dev->shade.wDarkLevels = 10;
+
+ if( dev->shade.intermediate & _ScanMode_Mono )
+ SamsungDAC1224[0].val = 0x57;
+ else
+ SamsungDAC1224[0].val = 0x51;
+ break;
+
+ case _DA_ESIC:
+ DBG( _DBG_INFO, "* DAC: ESIC\n" );
+
+ switch( dev->CCDID ) {
+
+ case _CCD_3797:
+ DBG( _DBG_INFO, "* CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ u12ccd_InitFunc = fnCCDInitWolfson3797;
+ dev->fnDarkOffset = fnDarkOffsetWolfson3797;
+ dev->fnDACDark = fnDACDarkWolfson;
+ dev->CCDRegs = (RegDef*)W3797CCDParams;
+ break;
+
+ default:
+ DBG( _DBG_INFO, "* CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ u12ccd_InitFunc = fnCCDInitESIC3799;
+ dev->fnDarkOffset = fnDarkOffsetWolfson3797;
+ dev->fnDACDark = fnDACDarkWolfson;
+ dev->CCDRegs = (RegDef*)W3799CCDParams;
+ }
+
+ dev->numCCDRegs = _NUM_OF_CCDREGS_W8143;
+ dev->numDACRegs = _NUM_OF_DACREGS_W8143;
+ dev->DACRegs = WolfsonDAC8143;
+ dev->RegDACOffset.Red = 0x20;
+ dev->RegDACOffset.Green = 0x21;
+ dev->RegDACOffset.Blue = 0x22;
+ dev->RegDACGain.Red = 0x28;
+ dev->RegDACGain.Green = 0x29;
+ dev->RegDACGain.Blue = 0x2a;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut ) {
+ dev->shade.bUniGain = 1;
+ dev->shade.bGainDouble = 1;
+ } else {
+ dev->shade.bUniGain = 2;
+ dev->shade.bGainDouble = 4;
+ }
+ dev->shade.bMinGain = 1;
+ dev->shade.bMaxGain = 0x1f;
+ dev->shade.wDarkLevels = 10;
+
+ if( dev->shade.intermediate == _ScanMode_Color )
+ WolfsonDAC8143[2].val = 0x52;
+ else
+ WolfsonDAC8143[2].val = 0x42;
+
+ if(dev->shade.intermediate == _ScanMode_Mono )
+ WolfsonDAC8143[0].val = 7;
+ else
+ WolfsonDAC8143[0].val = 3;
+ break;
+
+ default:
+
+ DBG( _DBG_INFO, "* DAC: SAMSUNG 8531\n" );
+ switch( dev->CCDID ) {
+
+ case _CCD_3797:
+ DBG( _DBG_INFO, "* CCD-3797\n" );
+ pDAC_CCD = ShadingVar3797;
+ u12ccd_InitFunc = fnCCDInitSamsung3797;
+ dev->fnDarkOffset = fnDarkOffsetSamsung3797;
+ dev->fnDACDark = fnDACDarkSamsung;
+ dev->CCDRegs = (RegDef*)S3797CCDParams;
+ break;
+
+ case _CCD_3777:
+ DBG( _DBG_INFO, "* CCD-3777\n" );
+ pDAC_CCD = ShadingVar3777;
+ u12ccd_InitFunc = fnCCDInitSamsung3777;
+ dev->fnDarkOffset = fnDarkOffsetSamsung3777;
+ dev->fnDACDark = fnDACDarkSamsung;
+ dev->CCDRegs = (RegDef*)S3797CCDParams;
+ dev->f0_8_16 = SANE_TRUE;
+ break;
+
+ default:
+ DBG( _DBG_INFO, "* CCD-3799\n" );
+ pDAC_CCD = ShadingVar3799;
+ u12ccd_InitFunc = fnCCDInitSamsung3799;
+ dev->fnDarkOffset = fnDarkOffsetSamsung3799;
+ dev->fnDACDark = fnDACDarkSamsung;
+ dev->CCDRegs = (RegDef*)S3799CCDParams;
+ }
+
+ dev->numCCDRegs = _NUM_OF_CCDREGS_S8531;
+ dev->numDACRegs = _NUM_OF_DACREGS_S8531;
+ dev->DACRegs = SamsungDAC8531;
+ dev->RegDACOffset.Red = 1;
+ dev->RegDACOffset.Green = 1;
+ dev->RegDACOffset.Blue = 1;
+ dev->RegDACGain.Red = 2;
+ dev->RegDACGain.Green = 2;
+ dev->RegDACGain.Blue = 2;
+ dev->shade.bGainDouble = 6;
+ dev->shade.bMinGain = 1;
+ dev->shade.bMaxGain = 0x1f;
+ if( dev->DataInf.dwScanFlag & _SCANDEF_TPA )
+ dev->shade.bUniGain = 2;
+ else
+ dev->shade.bUniGain = 7;
+
+ dev->shade.wDarkLevels = 10;
+
+ if( dev->shade.intermediate & _ScanMode_Mono ) {
+ SamsungDAC8531[0].val = 0x57;
+ SamsungDAC8531[3].val = 0x57;
+ SamsungDAC8531[6].val = 0x57;
+ } else {
+ SamsungDAC8531[0].val = 0x51;
+ SamsungDAC8531[3].val = 0x55;
+ SamsungDAC8531[6].val = 0x59;
+ }
+ }
+
+ if( shading ) {
+
+ if( !(dev->DataInf.dwScanFlag & _SCANDEF_TPA))
+ dev->shade.pCcdDac = &pDAC_CCD[_REFLECTION];
+ else {
+ if( dev->DataInf.dwScanFlag & _SCANDEF_Transparency )
+ dev->shade.pCcdDac = &pDAC_CCD[_TRANSPARENCY];
+ else
+ dev->shade.pCcdDac = &pDAC_CCD[_NEGATIVE];
+ }
+ } else {
+ dev->shade.pCcdDac = &pDAC_CCD[_REFLECTION];
+ }
+
+ /* as we now have the correct init function, call it */
+ u12ccd_InitFunc( dev );
+
+ DBG( _DBG_INFO, "* Programming DAC (%u regs)\n", dev->numDACRegs );
+
+ for( w = 0; w < dev->numDACRegs; w++ ) {
+
+ DBG( _DBG_INFO, "* [0x%02x] = 0x%02x\n", dev->DACRegs[w].reg,
+ dev->DACRegs[w].val );
+#if 0
+ u12io_DataRegisterToDAC( dev, dev->DACRegs[w].reg,
+ dev->DACRegs[w].val );
+#else
+ u12io_DataToRegister( dev, REG_ADCADDR, dev->DACRegs[w].reg );
+ u12io_DataToRegister( dev, REG_ADCDATA, dev->DACRegs[w].val );
+ u12io_DataToRegister( dev, REG_ADCSERIALOUT, dev->DACRegs[w].val );
+#endif
+ }
+ DBG( _DBG_INFO, "CCD & DAC init done.\n" );
+}
+
+/* END U12-CCD.C ............................................................*/
diff --git a/backend/u12-hw.c b/backend/u12-hw.c
new file mode 100644
index 0000000..89f37be
--- /dev/null
+++ b/backend/u12-hw.c
@@ -0,0 +1,978 @@
+/** @file u12-hw.c
+ * @brief The HW-access functions to the U12 backend stuff.
+ *
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - cleanup
+ * - added lampTimer stuff
+ * - fixed issue for Genius device - reported by
+ * Jose Alberto Reguero <jareguero@telefonica.net>
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#define _TEST_SIZE 1000
+
+/*************************** some local vars *********************************/
+
+static RegDef u12CcdStop[] = {
+/* this was the original sequence from parport backend*/
+#if 0
+#define _STOP_LEN 13
+
+ {0x41, 0xff}, {0x42, 0xff}, {0x60, 0xff}, {0x61, 0xff},
+ {0x4b, 0xff}, {0x4c, 0xff}, {0x4d, 0xff}, {0x4e, 0xff},
+ {0x2a, 0x01}, {0x2b, 0x00}, {0x2d, 0x00}, {0x1b, 0x19}, {0x15, 0x00}
+#else
+#define _STOP_LEN 29
+/* this is what we see from usb-snoop... */
+ {0x60, 0xff}, {0x61, 0xff}, {0x1b, 0x19}, {0x15, 0x00}, {0x20, 0x14},
+ {0x2c, 0x02}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00}, {0x3c, 0x00},
+ {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, {0x44, 0x00}, {0x45, 0x01},
+ {0x46, 0x00}, {0x47, 0x00}, {0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x09},
+ {0x4b, 0x00}, {0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x01}, {0x50, 0x00},
+ {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x01}, {0x67, 0x00}
+#endif
+};
+
+/**
+ */
+static void u12hw_SelectLampSource( U12_Device *dev )
+{
+ dev->regs.RD_ScanControl &= (~_SCAN_LAMPS_ON);
+
+ if( dev->DataInf.dwScanFlag & (_SCANDEF_TPA)) {
+ dev->regs.RD_ScanControl |= _SCAN_TPALAMP_ON;
+ } else {
+ dev->regs.RD_ScanControl |= _SCAN_NORMALLAMP_ON;
+ }
+}
+
+/** as the function name says
+ */
+static void u12hw_PutToIdleMode( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "CCD-Stop\n" );
+ u12io_DataToRegs( dev, (SANE_Byte*)u12CcdStop, _STOP_LEN );
+}
+
+/** program the CCD relevant stuff
+ */
+static void u12hw_ProgramCCD( U12_Device *dev )
+{
+ SANE_Byte *reg_val;
+
+ DBG( _DBG_INFO, "u12hw_ProgramCCD: 0x%08lx[%lu]\n",
+ (u_long)dev->CCDRegs,
+ ((u_long)dev->numCCDRegs * dev->shade.intermediate));
+
+ DBG( _DBG_INFO, " * %u regs * %u (intermediate)\n",
+ dev->numCCDRegs, dev->shade.intermediate );
+
+ reg_val = (SANE_Byte*)(dev->CCDRegs +
+ (u_long)dev->numCCDRegs * dev->shade.intermediate);
+
+ u12io_DataToRegs( dev, reg_val, dev->numCCDRegs );
+}
+
+/** init the stuff according to the buttons
+ */
+static void u12hw_ButtonSetup( U12_Device *dev, SANE_Byte numButtons )
+{
+ dev->Buttons = numButtons;
+
+ dev->regs.RD_MotorDriverType |= _BUTTON_DISABLE;
+ dev->MotorPower |= _BUTTON_DISABLE;
+}
+
+/** According to what we have detected, set the other stuff
+ */
+static void u12hw_InitiateComponentModel( U12_Device *dev )
+{
+ /* preset some stuff and do the differences later */
+ dev->Buttons = 0;
+ dev->ModelOriginY = 64;
+ dev->Tpa = SANE_FALSE;
+ dev->ModelCtrl = (_LED_ACTIVITY | _LED_CONTROL);
+
+ switch( dev->PCBID ) {
+
+ /* typical for Plustek OpticPro U12 and 1212U */
+ case _PLUSTEK_SCANNER:
+ DBG( _DBG_INFO, "We have a Plustek Scanner ;-)\n" );
+ break;
+
+ case _SCANNER_WITH_TPA:
+ DBG( _DBG_INFO, "Scanner has TPA\n" );
+ dev->Tpa = SANE_TRUE;
+ break;
+
+ case _SCANNER4Button:
+ DBG( _DBG_INFO, "Scanner has 4 Buttons\n" );
+ u12hw_ButtonSetup( dev, 4 );
+ break;
+
+ /* typical for Plustek OpticPro UT12 */
+ case _SCANNER4ButtonTPA:
+ DBG( _DBG_INFO, "Scanner has 4 Buttons & TPA\n" );
+ dev->Tpa = SANE_TRUE;
+ u12hw_ButtonSetup( dev, 4 );
+ break;
+
+ case _SCANNER5Button:
+ DBG( _DBG_INFO, "Scanner has 5 Buttons\n" );
+ dev->ModelOriginY += 20;
+ u12hw_ButtonSetup( dev, 5 );
+ break;
+
+ /* typical for Genius Colorpage HR6 */
+ case _SCANNER5ButtonTPA:
+ DBG( _DBG_INFO, "Scanner has 5 Buttons & TPA\n" );
+ dev->ModelOriginY += 20;
+ dev->Tpa = SANE_TRUE;
+ u12hw_ButtonSetup( dev, 5 );
+ break;
+
+ case _SCANNER1Button:
+ DBG( _DBG_INFO, "Scanner has 1 Button\n" );
+ u12hw_ButtonSetup( dev, 1 );
+ break;
+
+ case _SCANNER1ButtonTPA:
+ DBG( _DBG_INFO, "Scanner has 1 Button & TPA\n" );
+ dev->Tpa = SANE_TRUE;
+ u12hw_ButtonSetup( dev, 1 );
+ break;
+
+ case _AGFA_SCANNER:
+ DBG( _DBG_INFO, "Agfa Scanner\n" );
+ dev->ModelOriginY = 24; /* 1200 dpi */
+ break;
+
+ case _SCANNER2Button:
+ DBG( _DBG_INFO, "Scanner has 2 Buttons\n" );
+ dev->ModelOriginY -= 33;
+ u12hw_ButtonSetup( dev, 2 );
+ break;
+
+ default:
+ DBG( _DBG_INFO, "Default Model: U12\n" );
+ break;
+ }
+
+#if 0
+ if( _MOTOR0_2003 == dev->MotorID ) {
+ dev->f2003 = SANE_TRUE;
+ dev->XStepMono = 10;
+ dev->XStepColor = 6;
+ dev->XStepBack = 5;
+ dev->regs.RD_MotorDriverType |= _MOTORR_STRONG;
+ } else {
+#endif
+ dev->f2003 = SANE_FALSE;
+ dev->XStepMono = 8;
+ dev->XStepColor = 4;
+ dev->XStepBack = 5;
+ dev->regs.RD_MotorDriverType |= _MOTORR_WEAK;
+/* } */
+}
+
+/**
+ */
+static SANE_Status u12hw_InitAsic( U12_Device *dev, SANE_Bool shading )
+{
+ SANE_Byte rb[6];
+ int c;
+
+ DBG( _DBG_INFO, "u12hw_InitAsic(%d)\n", shading );
+
+ /* get DAC and motor stuff */
+ dev->DACType = u12io_DataFromRegister( dev, REG_RESETCONFIG );
+ dev->MotorID = (SANE_Byte)(dev->DACType & _MOTOR0_MASK);
+
+ dev->regs.RD_MotorDriverType =
+ (SANE_Byte)((dev->DACType & _MOTOR0_MASK) >> 3);
+ dev->regs.RD_MotorDriverType |=
+ (SANE_Byte)((dev->DACType & _MOTOR1_MASK) >> 1);
+ dev->DACType &= _ADC_MASK;
+
+ dev->MotorPower = dev->regs.RD_MotorDriverType | _MOTORR_STRONG;
+
+ /*get CCD and PCB ID */
+ dev->PCBID = u12io_DataFromRegister( dev, REG_CONFIG );
+ dev->CCDID = dev->PCBID & 0x07;
+ dev->PCBID &= 0xf0;
+
+ if( _AGFA_SCANNER == dev->PCBID )
+ dev->DACType = _DA_WOLFSON8141;
+
+ DBG( _DBG_INFO, "* PCB-ID=0x%02x, CCD-ID=0x%02x, DAC-TYPE=0x%02x\n",
+ dev->PCBID, dev->CCDID, dev->DACType );
+
+ u12hw_InitiateComponentModel( dev );
+ u12ccd_InitCCDandDAC( dev, shading );
+
+ dev->regs.RD_Model1Control = _CCD_SHIFT_GATE;
+ if( dev->Buttons != 0 )
+ dev->regs.RD_Model1Control += _BUTTON_MODE;
+
+ if( dev->shade.intermediate & _ScanMode_Mono )
+ dev->regs.RD_Model1Control += _SCAN_GRAYTYPE;
+
+ DBG( _DBG_INFO, "* MotorDrvType = 0x%02x\n", dev->regs.RD_MotorDriverType);
+ DBG( _DBG_INFO, "* Model1Cntrl = 0x%02x\n", dev->regs.RD_Model1Control );
+
+#if 0
+ u12io_DataToRegister( dev, REG_MOTORDRVTYPE, dev->regs.RD_MotorDriverType);
+ u12io_DataToRegister( dev, REG_WAITSTATEINSERT, 4 );
+ u12io_DataToRegister( dev, REG_MODEL1CONTROL, dev->regs.RD_Model1Control );
+#else
+ c = 0;
+ _SET_REG( rb, c, REG_MOTORDRVTYPE, dev->regs.RD_MotorDriverType);
+ _SET_REG( rb, c, REG_WAITSTATEINSERT, 4 );
+ _SET_REG( rb, c, REG_MODEL1CONTROL, dev->regs.RD_Model1Control );
+ u12io_DataToRegs( dev, rb, c );
+#endif
+
+ u12hw_ProgramCCD( dev );
+ DBG( _DBG_INFO, "u12hw_InitAsic done.\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static void u12hw_ControlLampOnOff( U12_Device *dev )
+{
+ SANE_Byte lampStatus;
+
+ dev->warmupNeeded = SANE_TRUE;
+
+ lampStatus = dev->regs.RD_ScanControl & _SCAN_LAMPS_ON;
+
+ if ( dev->lastLampStatus != lampStatus ) {
+
+ DBG( _DBG_INFO, "* Using OTHER Lamp --> warmup needed\n" );
+ dev->lastLampStatus = lampStatus;
+
+ u12io_DataToRegister( dev, REG_SCANCONTROL, dev->regs.RD_ScanControl );
+ return;
+ }
+
+ dev->warmupNeeded = SANE_FALSE;
+ DBG( _DBG_INFO, "* Using SAME Lamp --> no warmup needed\n" );
+}
+
+/** set all necessary register contents
+ */
+static void u12hw_SetGeneralRegister( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "u12hw_SetGeneralRegister()\n" );
+
+ dev->scan.motorBackward = SANE_FALSE;
+ dev->scan.refreshState = SANE_FALSE;
+
+ if( COLOR_BW == dev->DataInf.wPhyDataType )
+ dev->regs.RD_ScanControl = _SCAN_BITMODE;
+ else {
+ if( dev->DataInf.wPhyDataType <= COLOR_TRUE24 )
+ dev->regs.RD_ScanControl = _SCAN_BYTEMODE;
+ else
+ dev->regs.RD_ScanControl = _SCAN_12BITMODE;
+ }
+
+ u12hw_SelectLampSource( dev );
+
+ dev->regs.RD_ModelControl = (_LED_CONTROL | _LED_ACTIVITY);
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ dev->regs.RD_ModelControl |= _MODEL_DPI300;
+ else
+ dev->regs.RD_ModelControl |= _MODEL_DPI600;
+
+ dev->regs.RD_Motor0Control = _MotorOn | _MotorHQuarterStep | _MotorPowerEnable;
+ dev->regs.RD_ScanControl1 = _SCANSTOPONBUFFULL | _MFRC_BY_XSTEP;
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE;
+}
+
+/**
+ */
+static void u12hw_SetupPreviewCondition( U12_Device *dev )
+{
+ int i, c;
+ u_long channel;
+ SANE_Byte rb[100];
+
+ DBG( _DBG_INFO, "u12_SetupPreviewCondition()\n" );
+
+ u12hw_SetGeneralRegister( dev );
+
+ u12io_RegisterToScanner( dev, REG_RESETMTSC );
+ _DODELAY(250);
+
+ /* ------- Set the max. read fifo to Asic ------- */
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+ if( dev->DataInf.xyAppDpi.x >= 38 ) { /* 38 <= x <= 75 */
+
+ for(i = 0; i < _SCANSTATE_BYTES; i++ )
+ dev->scanStates[i] = 0xad;
+
+ } else if( dev->DataInf.xyAppDpi.x >= 19 ) { /* 19 <= x <= 30(37) */
+
+ u_short *pState = (u_short*)dev->scanStates;
+
+ for( i = 0; i < (_SCANSTATE_BYTES / 2); i++ )
+ pState[i] = 0x89ac;
+
+ } else { /* 16 <= x <= 18 */
+
+ u_long *pState = (u_long*)dev->scanStates;
+
+ for(i = 0; i < (_SCANSTATE_BYTES / 4); i++)
+ pState[i] = 0x888889ac;
+ }
+
+ dev->regs.RD_BufFullSize = dev->DataInf.dwAppPixelsPerLine *
+ ((dev->DataInf.xyPhyDpi.y * dev->max_y + 299) / 300) + 1;
+ if( dev->regs.RD_BufFullSize > _SIZE_BLUEFIFO )
+ dev->regs.RD_BufFullSize = _SIZE_BLUEFIFO -
+ dev->DataInf.dwAppPixelsPerLine - 1;
+
+ dev->scan.dwMaxReadFifo =
+ dev->scan.dwMinReadFifo = dev->DataInf.dwAppPixelsPerLine *2 ;
+
+ if( dev->scan.dwMinReadFifo < 1024)
+ dev->scan.dwMinReadFifo = dev->scan.dwMaxReadFifo = 1024;
+
+ dev->scan.dwMaxReadFifo += (dev->DataInf.dwAsicBytesPerPlane / 2);
+
+ if( dev->DataInf.wPhyDataType > COLOR_256GRAY )
+ dev->scan.bFifoSelect = REG_BFIFOOFFSET;
+ else
+ dev->scan.bFifoSelect = REG_GFIFOOFFSET;
+
+ channel = _BLUE_FULLSIZE << 16;
+ dev->regs.RD_BufFullSize = _SIZE_BLUEFIFO;
+
+ dev->regs.RD_LineControl = _LOBYTE(dev->shade.wExposure);
+ dev->regs.RD_ExtLineControl = _HIBYTE(dev->shade.wExposure);
+ dev->regs.RD_XStepTime = _LOBYTE(dev->shade.wXStep);
+ dev->regs.RD_ExtXStepTime = _HIBYTE(dev->shade.wXStep);
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE | _MOTOR_FREERUN;
+ dev->regs.RD_ModeControl = _ModeScan;
+
+ if( dev->DataInf.wPhyDataType == COLOR_BW ) {
+ dev->regs.RD_ScanControl = _SCAN_BITMODE;
+ } else if( dev->DataInf.wPhyDataType <= COLOR_TRUE24 )
+ dev->regs.RD_ScanControl = _SCAN_BYTEMODE;
+ else {
+ dev->regs.RD_ScanControl = _SCAN_12BITMODE;
+ }
+
+ dev->regs.RD_ScanControl |= _SCAN_1ST_AVERAGE;
+ u12hw_SelectLampSource( dev );
+
+ dev->regs.RD_MotorTotalSteps = (dev->DataInf.crImage.cy * 4) +
+ (dev->f0_8_16 ? 32 : 16) +
+ (dev->scan.bDiscardAll ? 32 : 0);
+ DBG( _DBG_INFO, "* RD_MotorTotalSteps = 0x%04x\n",
+ dev->regs.RD_MotorTotalSteps);
+
+ dev->regs.RD_ScanControl1 = (_MTSC_ENABLE | _SCANSTOPONBUFFULL |
+ _MFRC_RUNSCANSTATE | _MFRC_BY_XSTEP);
+ DBG( _DBG_INFO, "* RD_ScanControl1 = 0x%02x\n", dev->regs.RD_ScanControl1);
+
+ dev->regs.RD_Dpi = dev->DataInf.xyPhyDpi.x;
+
+ dev->regs.RD_Origin = (u_short)(dev->adj.leftNormal*2+_DATA_ORIGIN_X);
+ dev->regs.RD_Origin += dev->DataInf.crImage.x;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ dev->regs.RD_Origin >>= 1;
+
+ if( dev->DataInf.wPhyDataType == COLOR_BW )
+ dev->regs.RD_Pixels = dev->DataInf.dwAsicBytesPerPlane;
+ else
+ dev->regs.RD_Pixels = dev->DataInf.dwAppPixelsPerLine;
+
+ /* ------- Wait for scan state stop ------- */
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeIdle );
+
+ u12io_DownloadScanStates( dev );
+
+ c = 0;
+ _SET_REG( rb, c, REG_LINECONTROL, dev->regs.RD_LineControl );
+ _SET_REG( rb, c, REG_EXTENDEDLINECONTROL,
+ dev->regs.RD_ExtLineControl);
+ _SET_REG( rb, c, REG_XSTEPTIME, dev->regs.RD_XStepTime );
+ _SET_REG( rb, c, REG_EXTENDEDXSTEP, dev->regs.RD_ExtXStepTime );
+ _SET_REG( rb, c, REG_MOTORDRVTYPE,
+ dev->regs.RD_MotorDriverType );
+ _SET_REG( rb, c, REG_STEPCONTROL, dev->regs.RD_StepControl );
+ _SET_REG( rb, c, REG_MOTOR0CONTROL, dev->regs.RD_Motor0Control );
+ _SET_REG( rb, c, REG_MODELCONTROL, dev->regs.RD_ModelControl );
+ _SET_REG( rb, c, REG_DPILO, (_LOBYTE(dev->regs.RD_Dpi)));
+ _SET_REG( rb, c, REG_DPIHI, (_HIBYTE(dev->regs.RD_Dpi)));
+ _SET_REG( rb, c, REG_SCANPOSLO, (_LOBYTE(dev->regs.RD_Origin)));
+ _SET_REG( rb, c, REG_SCANPOSHI,(_HIBYTE(dev->regs.RD_Origin)));
+ _SET_REG( rb, c, REG_WIDTHPIXELLO,(_LOBYTE(dev->regs.RD_Pixels)));
+ _SET_REG( rb, c, REG_WIDTHPIXELHI,(_HIBYTE(dev->regs.RD_Pixels)));
+ _SET_REG( rb, c, REG_THRESHOLDLO,
+ (_LOBYTE(dev->regs.RD_ThresholdControl)));
+ _SET_REG( rb, c, REG_THRESHOLDHI,
+ (_HIBYTE(dev->regs.RD_ThresholdControl)));
+ _SET_REG( rb, c, REG_MOTORTOTALSTEP0,
+ (_LOBYTE(dev->regs.RD_MotorTotalSteps)));
+ _SET_REG( rb, c, REG_MOTORTOTALSTEP1,
+ (_HIBYTE(dev->regs.RD_MotorTotalSteps)));
+ _SET_REG( rb, c, REG_SCANCONTROL, dev->regs.RD_ScanControl);
+ u12io_DataToRegs( dev, rb, c );
+ _DODELAY(100);
+
+ u12io_RegisterToScanner( dev, REG_INITDATAFIFO );
+}
+
+/**
+ */
+static void u12hw_SetupScanningCondition( U12_Device *dev )
+{
+ TimerDef timer;
+ u_long channel;
+ int c;
+ SANE_Byte state;
+ SANE_Byte rb[100];
+ SANE_Byte *pState;
+
+ DBG( _DBG_INFO, "u12_SetupScanningCondition()\n" );
+
+ u12hw_SetGeneralRegister( dev );
+
+ u12io_RegisterToScanner( dev, REG_RESETMTSC );
+ _DODELAY(250);
+
+ /* ------- Setup MinRead/MaxRead Fifo size ------- */
+ if( dev->DataInf.wPhyDataType <= COLOR_TRUE24 ) {
+ dev->scan.dwMaxReadFifo =
+ dev->scan.dwMinReadFifo = dev->DataInf.dwAsicBytesPerPlane * 2;
+ } else {
+ dev->scan.dwMaxReadFifo =
+ dev->scan.dwMinReadFifo = dev->DataInf.dwAppPixelsPerLine << 1;
+ }
+
+ if( dev->scan.dwMinReadFifo < 1024)
+ dev->scan.dwMinReadFifo = dev->scan.dwMaxReadFifo = 1024;
+
+ dev->scan.dwMaxReadFifo += (dev->DataInf.dwAsicBytesPerPlane / 2);
+
+ DBG( _DBG_INFO, "* MinReadFifo=%lu, MaxReadFifo=%lu\n",
+ dev->scan.dwMinReadFifo, dev->scan.dwMaxReadFifo );
+
+ /* ------- Set the max. read fifo to asic ------- */
+ if( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ dev->scan.bFifoSelect = REG_BFIFOOFFSET;
+
+ if( !dev->scan.p48BitBuf.pb ) {
+
+ long lRed, lGreen;
+
+ lRed = (_SIZE_REDFIFO - _SIZE_BLUEFIFO) /
+ dev->DataInf.dwAsicBytesPerPlane - dev->scan.bd_rk.wRedKeep;
+
+ lGreen = (_SIZE_GREENFIFO - _SIZE_BLUEFIFO) /
+ dev->DataInf.dwAsicBytesPerPlane - dev->scan.gd_gk.wGreenKeep;
+
+ if((lRed < 0) || (lGreen < 0)) {
+
+ if( lRed < lGreen ) {
+ channel = _RED_FULLSIZE << 16;
+ dev->regs.RD_BufFullSize = _SIZE_REDFIFO;
+ lGreen = lRed;
+ } else {
+ channel = _GREEN_FULLSIZE << 16;
+ dev->regs.RD_BufFullSize = _SIZE_GREENFIFO;
+ }
+
+ lGreen = (u_long)(-lGreen * dev->DataInf.dwAsicBytesPerPlane);
+
+ if( dev->DataInf.wPhyDataType > COLOR_TRUE24 )
+ lGreen >>= 1;
+
+ dev->scan.dwMinReadFifo += (u_long)lGreen;
+ dev->scan.dwMaxReadFifo += (u_long)lGreen;
+
+ } else {
+ channel = _BLUE_FULLSIZE << 16;
+ dev->regs.RD_BufFullSize = _SIZE_BLUEFIFO;
+ }
+ } else {
+ channel = _BLUE_FULLSIZE << 16;
+ dev->regs.RD_BufFullSize = _SIZE_BLUEFIFO;
+ }
+ } else {
+ dev->scan.bFifoSelect = REG_GFIFOOFFSET;
+ channel = _GREEN_FULLSIZE << 16;
+ dev->regs.RD_BufFullSize = _SIZE_GRAYFIFO;
+ }
+
+ dev->regs.RD_BufFullSize -= (dev->DataInf.dwAsicBytesPerPlane << 1);
+
+ if( dev->DataInf.wPhyDataType > COLOR_TRUE24 )
+ dev->regs.RD_BufFullSize >>= 1;
+
+ dev->regs.RD_BufFullSize |= channel;
+
+ dev->scan.bRefresh = (SANE_Byte)(dev->scan.dwInterval << 1);
+ dev->regs.RD_LineControl = _LOBYTE(dev->shade.wExposure);
+ dev->regs.RD_ExtLineControl = _HIBYTE(dev->shade.wExposure);
+ dev->regs.RD_XStepTime = _LOBYTE(dev->shade.wXStep);
+ dev->regs.RD_ExtXStepTime = _HIBYTE(dev->shade.wXStep);
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE;
+ dev->regs.RD_ModeControl = _ModeScan/*(_ModeScan | _ModeFifoGSel)*/;
+
+ DBG( _DBG_INFO, "* bRefresh = %i\n", dev->scan.bRefresh );
+
+ if( dev->DataInf.wPhyDataType == COLOR_BW ) {
+ dev->regs.RD_ScanControl = _SCAN_BITMODE;
+ } else if( dev->DataInf.wPhyDataType <= COLOR_TRUE24 )
+ dev->regs.RD_ScanControl = _SCAN_BYTEMODE;
+ else {
+ dev->regs.RD_ScanControl = _SCAN_12BITMODE;
+ }
+
+ dev->regs.RD_ScanControl |= _SCAN_1ST_AVERAGE;
+ u12hw_SelectLampSource( dev );
+
+ DBG( _DBG_INFO, "* RD_ScanControl = 0x%02x\n", dev->regs.RD_ScanControl );
+
+ DBG( _DBG_INFO, "* ImageInfo: x=%u,y=%u,dx=%u,dy=%u\n",
+ dev->DataInf.crImage.x, dev->DataInf.crImage.y,
+ dev->DataInf.crImage.cx, dev->DataInf.crImage.cy );
+
+ dev->regs.RD_MotorTotalSteps = (dev->DataInf.crImage.cy * 4) +
+ (dev->f0_8_16 ? 32 : 16) +
+ (dev->scan.bDiscardAll ? 32 : 0);
+ DBG( _DBG_INFO, "* RD_MotorTotalSteps = 0x%04x\n",
+ dev->regs.RD_MotorTotalSteps);
+
+ dev->regs.RD_ScanControl1 = (_MTSC_ENABLE | _SCANSTOPONBUFFULL |
+ _MFRC_RUNSCANSTATE | _MFRC_BY_XSTEP);
+ DBG( _DBG_INFO, "* RD_ScanControl1 = 0x%02x\n", dev->regs.RD_ScanControl1);
+
+ dev->regs.RD_Dpi = dev->DataInf.xyPhyDpi.x;
+
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA )) {
+ dev->regs.RD_Origin = (u_short)(dev->adj.leftNormal*2+_DATA_ORIGIN_X);
+
+ } else if( dev->DataInf.dwScanFlag & _SCANDEF_Transparency ) {
+ dev->regs.RD_Origin = (u_short)dev->scan.posBegin;
+ } else {
+ dev->regs.RD_Origin = (u_short)dev->scan.negBegin;
+ }
+ dev->regs.RD_Origin += dev->DataInf.crImage.x;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ dev->regs.RD_Origin >>= 1;
+
+ if( dev->DataInf.wPhyDataType == COLOR_BW )
+ dev->regs.RD_Pixels = (u_short)dev->DataInf.dwAsicBytesPerPlane;
+ else
+ dev->regs.RD_Pixels = (u_short)dev->DataInf.dwAppPixelsPerLine;
+
+ DBG( _DBG_INFO, "* RD_Origin = %u, RD_Pixels = %u\n",
+ dev->regs.RD_Origin, dev->regs.RD_Pixels );
+
+ /* ------- Prepare scan states ------- */
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+ memset( dev->bufs.b1.pReadBuf, 0, _NUMBER_OF_SCANSTEPS );
+
+ if( dev->DataInf.wPhyDataType <= COLOR_256GRAY )
+ state = (_SS_MONO | _SS_STEP);
+ else
+ state = (_SS_COLOR | _SS_STEP);
+
+ for( channel = _NUMBER_OF_SCANSTEPS, pState = dev->bufs.b1.pReadBuf;
+ channel; channel -= dev->scan.dwInterval ) {
+ *pState = state;
+ pState += dev->scan.dwInterval;
+ }
+ for( channel = 0, pState = dev->bufs.b1.pReadBuf;
+ channel < _SCANSTATE_BYTES; channel++) {
+ dev->scanStates[channel] = pState[0] | (pState[1] << 4);
+ pState += 2;
+ }
+
+ /* ------- Wait for scan state stop ------- */
+ u12io_StartTimer( &timer, _SECOND * 2 );
+
+ u12io_ResetFifoLen();
+ while(!(u12io_GetScanState( dev ) & _SCANSTATE_STOP) &&
+ !u12io_CheckTimer(&timer));
+ u12io_DownloadScanStates( dev );
+
+ c = 0;
+ _SET_REG( rb, c, REG_LINECONTROL, dev->regs.RD_LineControl );
+ _SET_REG( rb, c, REG_EXTENDEDLINECONTROL,
+ dev->regs.RD_ExtLineControl);
+ _SET_REG( rb, c, REG_XSTEPTIME, dev->regs.RD_XStepTime );
+ _SET_REG( rb, c, REG_EXTENDEDXSTEP, dev->regs.RD_ExtXStepTime );
+ _SET_REG( rb, c, REG_MOTORDRVTYPE,
+ dev->regs.RD_MotorDriverType );
+ _SET_REG( rb, c, REG_STEPCONTROL, dev->regs.RD_StepControl );
+ _SET_REG( rb, c, REG_MOTOR0CONTROL, dev->regs.RD_Motor0Control );
+ _SET_REG( rb, c, REG_MODELCONTROL, dev->regs.RD_ModelControl );
+ _SET_REG( rb, c, REG_DPILO, (_LOBYTE(dev->regs.RD_Dpi)));
+ _SET_REG( rb, c, REG_DPIHI, (_HIBYTE(dev->regs.RD_Dpi)));
+ _SET_REG( rb, c, REG_SCANPOSLO, (_LOBYTE(dev->regs.RD_Origin)));
+ _SET_REG( rb, c, REG_SCANPOSHI,(_HIBYTE(dev->regs.RD_Origin)));
+ _SET_REG( rb, c, REG_WIDTHPIXELLO,(_LOBYTE(dev->regs.RD_Pixels)));
+ _SET_REG( rb, c, REG_WIDTHPIXELHI,(_HIBYTE(dev->regs.RD_Pixels)));
+ _SET_REG( rb, c, REG_THRESHOLDLO,
+ (_LOBYTE(dev->regs.RD_ThresholdControl)));
+ _SET_REG( rb, c, REG_THRESHOLDHI,
+ (_HIBYTE(dev->regs.RD_ThresholdControl)));
+ _SET_REG( rb, c, REG_MOTORTOTALSTEP0,
+ (_LOBYTE(dev->regs.RD_MotorTotalSteps)));
+ _SET_REG( rb, c, REG_MOTORTOTALSTEP1,
+ (_HIBYTE(dev->regs.RD_MotorTotalSteps)));
+ _SET_REG( rb, c, REG_SCANCONTROL, dev->regs.RD_ScanControl);
+ u12io_DataToRegs( dev, rb, c );
+ _DODELAY(100);
+
+ u12io_RegisterToScanner( dev, REG_INITDATAFIFO );
+}
+
+/**
+ */
+static SANE_Status u12hw_Memtest( U12_Device *dev )
+{
+ SANE_Byte tmp;
+ SANE_Byte buf[_TEST_SIZE];
+ int i;
+
+ DBG( _DBG_INFO, "u12hw_Memtest()\n" );
+
+ /* prepare buffer */
+ for( i = 0; i < _TEST_SIZE; i++ ) {
+ buf[i] = (SANE_Byte)((i * 3) & 0xff);
+ }
+
+ /* avoid switching to Lamp0, when previously scanned in transp./neg mode */
+ tmp = dev->lastLampStatus + _SCAN_BYTEMODE;
+ u12io_DataToRegister( dev, REG_SCANCONTROL, tmp );
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeMappingMem );
+ u12io_DataToRegister( dev, REG_MEMORYLO, 0 );
+ u12io_DataToRegister( dev, REG_MEMORYHI, 0 );
+
+ /* fill to buffer */
+ u12io_MoveDataToScanner( dev, buf, _TEST_SIZE );
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeMappingMem );
+ u12io_DataToRegister( dev, REG_MEMORYLO, 0 );
+ u12io_DataToRegister( dev, REG_MEMORYHI, 0 );
+
+ u12io_DataToRegister( dev, REG_WIDTHPIXELLO, 0 );
+ u12io_DataToRegister( dev, REG_WIDTHPIXELHI, 5 );
+
+ memset( buf, 0, _TEST_SIZE );
+
+ dev->regs.RD_ModeControl = _ModeReadMappingMem;
+ u12io_ReadData( dev, buf, _TEST_SIZE );
+
+ for( i = 0; i < _TEST_SIZE; i++ ) {
+ if((SANE_Byte)((i * 3) & 0xff) != buf[i] ) {
+ DBG( _DBG_ERROR, "* Memtest failed at pos %u: %u != %u\n",
+ i+1, buf[i], (SANE_Byte)((i * 3) & 0xff) );
+ return SANE_STATUS_INVAL;
+ }
+ }
+ DBG( _DBG_INFO, "* Memtest passed.\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** check if ASIC can be accessed, if the version is supported and the memory
+ * is also accessible...
+ */
+static SANE_Status u12hw_CheckDevice( U12_Device *dev )
+{
+#ifndef _FAKE_DEVICE
+ SANE_Byte tmp;
+ SANE_Byte rb[8];
+ int c;
+
+#if 1
+ if( !u12io_IsConnected( dev )) {
+
+ if( !u12io_OpenScanPath( dev ))
+ return SANE_STATUS_IO_ERROR;
+ }
+#else
+/* u12io_IsConnected( dev ); */
+ if( !u12io_OpenScanPath( dev ))
+ return SANE_STATUS_IO_ERROR;
+#endif
+
+ /* some setup stuff... */
+ tmp = u12io_GetExtendedStatus( dev );
+ DBG( _DBG_INFO, "* REG_STATUS2 = 0x%02x\n", tmp );
+ if( tmp & _REFLECTIONLAMP_ON ) {
+ DBG( _DBG_INFO, "* Normal lamp is ON\n" );
+ dev->lastLampStatus = _SCAN_NORMALLAMP_ON;
+ } else if( tmp & _TPALAMP_ON ) {
+ dev->lastLampStatus = _SCAN_TPALAMP_ON;
+ DBG( _DBG_INFO, "* TPA lamp is ON\n" );
+ }
+
+ c = 0;
+ _SET_REG( rb, c, REG_PLLPREDIV, 1 );
+ _SET_REG( rb, c, REG_PLLMAINDIV, 0x20 );
+ _SET_REG( rb, c, REG_PLLPOSTDIV, 2 );
+ _SET_REG( rb, c, REG_CLOCKSELECTOR, 2 );
+ u12io_DataToRegs( dev, rb, c );
+
+#if 0
+ return u12hw_Memtest( dev );
+#else
+ if( !dev->initialized )
+ return u12hw_Memtest( dev );
+ else
+ return SANE_STATUS_GOOD;
+#endif
+#else
+ _VAR_NOT_USED( dev );
+ return SANE_STATUS_GOOD;
+#endif
+}
+
+/* prototypes... */
+static void u12motor_PositionModuleToHome( U12_Device *);
+static void u12motor_ToHomePosition( U12_Device *, SANE_Bool );
+
+/**
+ */
+static void u12hw_CancelSequence( U12_Device *dev )
+{
+ int c = 0;
+ SANE_Byte rb[6];
+
+ DBG( _DBG_INFO, "u12hw_CancelSequence()\n" );
+
+ u12motor_PositionModuleToHome( dev );
+
+ u12motor_ToHomePosition( dev, SANE_TRUE );
+
+ u12io_DataToRegister( dev, REG_MOTOR0CONTROL, 0 );
+ u12io_DataToRegister( dev, REG_MODELCONTROL, 0x1a );
+
+ u12hw_PutToIdleMode( dev );
+
+ if( strcmp( dev->usbId, "0x0458-0x2004" ))
+ u12io_SoftwareReset( dev );
+
+ u12motor_PositionModuleToHome( dev );
+
+ u12io_DataToRegister( dev, REG_SCANCONTROL, 0x05 );
+ u12io_DataToRegister( dev, REG_MODELCONTROL, 0x1f );
+
+ u12hw_PutToIdleMode( dev );
+
+ u12io_DataToRegister( dev, REG_MODELCONTROL, 0x00 );
+
+ u12io_DataToRegister( dev, REG_ADCADDR, 0x01 );
+ u12io_DataToRegister( dev, REG_ADCDATA, 0x00 );
+ u12io_DataToRegister( dev, REG_ADCSERIALOUT, 0x00 );
+
+ _SET_REG( rb, c, REG_MODECONTROL, 0x19 );
+ _SET_REG( rb, c, REG_STEPCONTROL, 0xff );
+ _SET_REG( rb, c, REG_MOTOR0CONTROL, 0 );
+ u12io_DataToRegs( dev, rb, c );
+
+ u12io_CloseScanPath( dev );
+}
+
+/**
+ */
+static SANE_Status u12hw_WarmupLamp( U12_Device *dev )
+{
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "u12hw_WarmupLamp()\n" );
+
+ if( dev->warmupNeeded ) {
+ DBG( _DBG_INFO, "* warming up...\n" );
+ u12io_StartTimer( &timer, _SECOND * dev->adj.warmup );
+ while( !u12io_CheckTimer( &timer )) {
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+ }
+ } else {
+ DBG( _DBG_INFO, "* skipped\n" );
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* FIXME: replace!!! */
+static U12_Device *dev_xxx = NULL;
+
+/** ISR to switch lamp off after time has elapsed
+ */
+static void usb_LampTimerIrq( int sig )
+{
+ SANE_Byte tmp;
+ int handle = -1;
+
+ if( NULL == dev_xxx )
+ return;
+
+ _VAR_NOT_USED( sig );
+ DBG( _DBG_INFO, "*** LAMP OFF!!! ***\n" );
+
+ if( -1 == dev_xxx->fd ) {
+
+ if( SANE_STATUS_GOOD == sanei_usb_open(dev_xxx->sane.name, &handle)) {
+ dev_xxx->fd = handle;
+ }
+ }
+
+ if( -1 != dev_xxx->fd ) {
+
+ if( !u12io_IsConnected( dev_xxx )) {
+
+ if( u12io_OpenScanPath( dev_xxx )) {
+
+ /* some setup stuff... */
+ tmp = u12io_GetExtendedStatus( dev_xxx );
+ if( tmp & _REFLECTIONLAMP_ON ) {
+ DBG( _DBG_INFO, "* Normal lamp is ON\n" );
+ } else if( tmp & _TPALAMP_ON ) {
+ DBG( _DBG_INFO, "* TPA lamp is ON\n" );
+ }
+
+ u12io_DataToRegister( dev_xxx, REG_SCANCONTROL, 0 );
+ u12io_CloseScanPath( dev_xxx );
+ }
+ }
+ }
+
+ if( -1 != handle ) {
+ dev_xxx->fd = -1;
+ sanei_usb_close( handle );
+ }
+}
+
+/**
+ */
+static void u12hw_StartLampTimer( U12_Device *dev )
+{
+ sigset_t block, pause_mask;
+ struct sigaction s;
+#ifdef HAVE_SETITIMER
+ struct itimerval interval;
+#endif
+
+ /* block SIGALRM */
+ sigemptyset( &block );
+ sigaddset ( &block, SIGALRM );
+ sigprocmask( SIG_BLOCK, &block, &pause_mask );
+
+ /* setup handler */
+ sigemptyset( &s.sa_mask );
+ sigaddset ( &s.sa_mask, SIGALRM );
+ s.sa_flags = 0;
+ s.sa_handler = usb_LampTimerIrq;
+
+ if( sigaction( SIGALRM, &s, NULL ) < 0 )
+ DBG( _DBG_ERROR, "Can't setup timer-irq handler\n" );
+
+ sigprocmask( SIG_UNBLOCK, &block, &pause_mask );
+
+#ifdef HAVE_SETITIMER
+ /*
+ * define a one-shot timer
+ */
+ interval.it_value.tv_usec = 0;
+ interval.it_value.tv_sec = dev->adj.lampOff;
+ interval.it_interval.tv_usec = 0;
+ interval.it_interval.tv_sec = 0;
+
+ if( 0 != dev->adj.lampOff ) {
+ dev_xxx = dev;
+ setitimer( ITIMER_REAL, &interval, &dev->saveSettings );
+ DBG( _DBG_INFO, "Lamp-Timer started (using ITIMER)\n" );
+ }
+#else
+ if( 0 != dev->adj.lampOff ) {
+ dev_xxx = dev;
+ alarm( dev->adj.lampOff );
+ DBG( _DBG_INFO, "Lamp-Timer started (using ALARM)\n" );
+ }
+#endif
+}
+
+/**
+ */
+static void u12hw_StopLampTimer( U12_Device *dev )
+{
+ sigset_t block, pause_mask;
+
+ /* block SIGALRM */
+ sigemptyset( &block );
+ sigaddset ( &block, SIGALRM );
+ sigprocmask( SIG_BLOCK, &block, &pause_mask );
+
+ dev_xxx = NULL;
+
+#ifdef HAVE_SETITIMER
+ if( 0 != dev->adj.lampOff )
+ setitimer( ITIMER_REAL, &dev->saveSettings, NULL );
+#else
+ _VAR_NOT_USED( dev );
+ alarm( 0 );
+#endif
+ DBG( _DBG_INFO, "Lamp-Timer stopped\n" );
+}
+
+/* END U12-HW.C .............................................................*/
diff --git a/backend/u12-hwdef.h b/backend/u12-hwdef.h
new file mode 100644
index 0000000..ac86cf5
--- /dev/null
+++ b/backend/u12-hwdef.h
@@ -0,0 +1,473 @@
+/** @file u12-hwdef.h
+ * @brief Definitions for the ASIC.
+ *
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - change _DEF_BW_THRESHOLD
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __U12_HWDEF_H__
+#define __U12_HWDEF_H__
+
+#define _LOWORD(x) ((u_short)(x & 0xffff))
+#define _HIWORD(x) ((u_short)(x >> 16))
+#define _LOBYTE(x) ((SANE_Byte)((x) & 0xFF))
+#define _HIBYTE(x) ((SANE_Byte)((x) >> 8))
+
+#define _SET_REG(b,c,r,v) {b[c*2]=r; b[(c*2)+1]=v;c++;}
+
+/** some magics for the ASIC */
+#define _ID_TO_PRINTER 0x00
+#define _ID1ST 0x69
+#define _ID2ND 0x96
+#define _ID3RD 0xa5
+#define _ID4TH 0x5a
+
+/** some more for reset...
+ */
+#define _RESET1ST 0x69
+#define _RESET2ND 0x96
+#define _RESET3RD 0xaa
+#define _RESET4TH 0x55
+
+/** Printer Control Port: Definitions
+ */
+#define _CTRL_STROBE 0x01
+#define _CTRL_AUTOLF 0x02
+#define _CTRL_NOT_INIT 0x04
+#define _CTRL_SELECT_IN 0x08
+#define _CTRL_ENABLE_IRQ 0x10
+#define _CTRL_DIRECTION 0x20
+#define _CTRL_RESERVED 0xc0
+
+/** for Asic I/O signal control
+ */
+#define _CTRL_GENSIGNAL (_CTRL_RESERVED + _CTRL_NOT_INIT) /* 0xc4 */
+#define _CTRL_SIGNAL_REGWRITE (_CTRL_GENSIGNAL + _CTRL_SELECT_IN) /* 0xcc */
+#define _CTRL_END_REGWRITE (_CTRL_GENSIGNAL) /* 0xc4 */
+#define _CTRL_SIGNAL_DATAWRITE (_CTRL_GENSIGNAL + _CTRL_AUTOLF) /* 0xc6 */
+#define _CTRL_END_DATAWRITE (_CTRL_GENSIGNAL) /* 0xc4 */
+
+#define ASIC_ID 0x83
+
+/** the Register set
+ */
+#define REG_SWITCHBUS 0x00
+#define REG_EPPENABLE 0x01
+
+#define REG_READDATAMODE 0x03
+#define REG_WRITEDATAMODE 0x04
+#define REG_INITDATAFIFO 0x05
+#define REG_FORCESTEP 0x06
+
+#define REG_REFRESHSCANSTATE 0x08
+
+#define REG_WAITSTATEINSERT 0x0a
+#define REG_RFIFOOFFSET 0x0a
+#define REG_GFIFOOFFSET 0x0b
+#define REG_BFIFOOFFSET 0x0c
+
+#define REG_STEPCONTROL 0x14
+#define REG_MOTOR0CONTROL 0x15
+#define REG_XSTEPTIME 0x16
+
+#define REG_GETSCANSTATE 0x17
+#define REG_ASICID 0x18
+#define REG_MEMORYLO 0x19
+#define REG_MEMORYHI 0x1a
+#define REG_MODECONTROL 0x1b
+#define REG_LINECONTROL 0x1c
+#define REG_SCANCONTROL 0x1d
+#define REG_CONFIG 0x1e
+#define REG_MODELCONTROL 0x1f
+#define REG_MODEL1CONTROL 0x20
+#define REG_DPILO 0x21
+#define REG_DPIHI 0x22
+#define REG_SCANPOSLO 0x23
+#define REG_SCANPOSHI 0x24
+#define REG_WIDTHPIXELLO 0x25
+#define REG_WIDTHPIXELHI 0x26
+#define REG_THRESHOLDLO 0x27
+#define REG_THRESHOLDHI 0x28
+
+#define REG_ADCADDR 0x2a
+#define REG_ADCDATA 0x2b
+#define REG_ADCSERIALOUT 0x2d
+
+#define REG_RESETCONFIG 0x2e
+
+#define REG_STATUS 0x30
+#define REG_SCANSTATECONTROL 0x31
+
+#define REG_REDCHDARKOFFSETLO 0x33
+#define REG_REDCHDARKOFFSETHI 0x34
+#define REG_GREENCHDARKOFFSETLO 0x35
+#define REG_GREENCHDARKOFFSETHI 0x36
+#define REG_BLUECHDARKOFFSETLO 0x37
+#define REG_BLUECHDARKOFFSETHI 0x38
+
+#define REG_FIFOFULLEN0 0x54
+#define REG_FIFOFULLEN1 0x55
+#define REG_FIFOFULLEN2 0x56
+#define REG_MOTORTOTALSTEP0 0x57
+#define REG_MOTORTOTALSTEP1 0x58
+#define REG_MOTORFREERUNCOUNT0 0x59
+#define REG_MOTORFREERUNCOUNT1 0x5a
+#define REG_SCANCONTROL1 0x5b
+#define REG_MOTORFREERUNTRIGGER 0x5c
+#define REG_RESETMTSC 0x5d
+
+#define REG_MOTORDRVTYPE 0x64
+
+#define REG_STATUS2 0x66
+
+#define REG_EXTENDEDLINECONTROL 0x6d
+#define REG_EXTENDEDXSTEP 0x6e
+
+#define REG_PLLPREDIV 0x71
+#define REG_PLLMAINDIV 0x72
+#define REG_PLLPOSTDIV 0x73
+#define REG_CLOCKSELECTOR 0x74
+
+#define REG_TESTMODE 0xf0
+
+/* Register RegStepControl (Addr: 0x14) */
+#define _MOTOR0_ONESTEP 0x01
+#define _MOTOR0_SCANSTATE 0x02
+#define _MOTOR_FREERUN 0x40
+#define _MOTOR_NOFREERUN 0x00
+
+/* Register RegGetScanState (Addr: 0x17)*/
+#define _SCANSTATE_MASK 0x3f /* bit 0-5 */
+#define _SCANSTATE_STOP 0x80
+
+/* Register RegMemoryLow/High (Addr: 0x19/0x1a)*/
+#define _MAP_ADDR_RED 0x00
+#define _MAP_ADDR_GREEN 0x40
+#define _MAP_ADDR_BLUE 0x80
+#define _MAP_ADDR_SIZE 0x40
+
+/* Register RegModeControl (Addr: 0x1b)*/
+#define _ModeScan 0x00
+#define _ModeIdle 0x01
+#define _ModeShadingMem 0x02
+#define _ModeMappingMem 0x03
+#define _ModeReadMappingMem 0x07
+#define _ModeFifoRSel 0x00
+#define _ModeFifoGSel 0x08
+#define _ModeFifoBSel 0x10
+#define _ModeFifoClose 0x18
+
+/* Register RegLineControl (Addr: 0x1c) */
+#define _LINE_SCANTIME_MASK 0x3f /* bit 0-6 */
+#define _LINE_CDSINVERSE 0x80 /* Color Drive Signal */
+
+/* Register RegScanControl (Addr: 0x1d) */
+#define _SCAN_BITMODE 0x00
+#define _SCAN_BYTEMODE 0x01 /* Gray/Color mode */
+#define _SCAN_12BITMODE 0x02
+#define _SCAN_1ST_AVERAGE 0x04 /* first pixel is averaged pixel */
+#define _SCAN_BITDIRR2L 0x08 /* bit shift from right to left */
+#define _SCAN_NORMALLAMP_ON 0x10 /* normal Lamp */
+#define _SCAN_TPALAMP_ON 0x20
+#define _SCAN_DATA_INVERT 0x40
+#define _BITALIGN_LEFT 0x80
+
+#define _SCAN_LAMPS_ON (_SCAN_NORMALLAMP_ON | _SCAN_TPALAMP_ON)
+#define _SCAN_LAMP_MASK _SCAN_LAMPS_ON
+
+/* Register RegMotor0Control (Addr: 0x15) */
+#define _MotorDirForward 0x01
+#define _MotorDirBackward 0x00
+#define _MotorOn 0x02
+#define _MotorHFullStepH 0x00
+#define _MotorHHalfStep 0x04
+#define _MotorHQuarterStep 0x08
+#define _MotorPowerEnable 0x40
+#define _MotorHHomeStop 0x80
+
+#define _DIR_FW 1
+#define _DIR_BW 2
+#define _DIR_NONE 0
+
+#define _FORWARD_MOTOR (_MotorDirForward + _MotorOn + \
+ _MotorHQuarterStep + _MotorPowerEnable)
+#define _BACKWARD_MOTOR (_MotorDirBackward + _MotorOn + _MotorHHomeStop + \
+ _MotorHQuarterStep + _MotorPowerEnable)
+
+/* Register RegConfiguration (Addr: 0x1e) */
+#define _P98_CCD_TYPE_ID 0x07
+#define _P98_NEC_MACHINE 0x08
+#define _P98_PCBID 0xF0
+
+#define _DEF_BW_THRESHOLD 128 /* default B/W mode threshold value */
+#define _NUMBER_OF_SCANSTEPS 64 /* Asic spec.: up to 64 scan steps */
+#define _SCANSTATE_BYTES (_NUMBER_OF_SCANSTEPS/2)
+
+#define _CCD_3797 0
+#define _CCD_3799 1
+#define _CCD_535 2
+#define _CCD_2556 3
+#define _CCD_518 4
+#define _CCD_539 5
+#define _CCD_3777 6
+#define _CCD_548 7
+
+/* PCB-IDs (from parport driver)... */
+#define _OPTICWORKS2000 0x00
+#define _PLUSTEK_SCANNER 0x10
+#define _SCANNER_WITH_TPA 0x20
+#define _SCANNER4Button 0x30
+#define _SCANNER4ButtonTPA 0x40
+#define _SCANNER5Button 0x50
+#define _SCANNER5ButtonTPA 0x60
+#define _SCANNER1Button 0x70
+#define _SCANNER1ButtonTPA 0x80
+#define _SCANNER2Button 0x90
+#define _AGFA_SCANNER 0xf0
+#define _AGFA_PCB 0x1f
+
+/* Register RegModelControl (Addr: 0x1f) */
+#define _HOME_SENSOR_POLARITY 0x01
+#define _LED_CONTROL 0x02
+#define _LED_ACTIVITY 0x04
+#define _MODEL_DPI200 0x00
+#define _MODEL_DPI300 0x08
+#define _MODEL_DPI400 0x10
+#define _MODEL_DPI600 0x18
+#define _MODEL_DPI800 0x20
+#define _MODEL_DPI1200 0x28
+#define _DUMMY_12BIT 0x40
+
+/* Register RegModel1Control (Addr: 0x20) */
+#define _SCAN_GRAYTYPE 0x01
+#define _CCD_SHIFT_GATE 0x02
+#define _CCD_SHIFT_PULSE 0x04
+#define _BUTTON_MODE 0x08
+#define _MOTOR_2003 0x00
+#define _MOTOR_2916 0x10
+#define _MOTOR_7042 0x20
+
+/* Register RegThresholdGapControl (Addr: 0x29) */
+#define _THRESHOLDGAP_MASK 0x0f
+
+/* Register RegResetConfig (Addr: 0x2e) */
+#define _ADC_MASK 0x07
+#define _DA_WOLFSON8143 0x00
+#define _DA_ESIC 0x04
+#define _DA_SAMSUNG8531 0x05
+#define _DA_WOLFSON8141 0x06
+#define _DA_SAMSUNG1224 0x07
+#define _MOTOR0_MASK 0x18
+#define _MOTOR0_2003 0x00
+#define _MOTOR0_2916 0x08
+#define _MOTOR0_7042 0x10
+#define _MOTOR1_MASK 0x60
+#define _MOTOR1_2003 0x00
+#define _MOTOR1_2916 0x20
+#define _MOTOR1_7042 0x40
+
+/* Status Register (Addr: 0x30) */
+#define _FLAG_PAPER 0x01
+#define _FLAG_KEY 0x80
+
+/* Register RegFifoFullLength (Addr: 0x54) */
+#define _RED_FULLSIZE 0x00
+#define _GREEN_FULLSIZE 0x08
+#define _BLUE_FULLSIZE 0x10
+
+/* Register RegScanControl1 (Addr: 0x5b) */
+#define _MTSC_ENABLE 0x01
+#define _SCANSTOPONBUFFULL 0x02
+#define _MFRC_RUNSCANSTATE 0x04
+#define _MFRC_BY_XSTEP 0x08
+
+/* Register RegMotorDriverType (Addr: 0x64) */
+#define _MOTORS_MASK 0x33
+#define _MOTORR_MASK 0xf3
+#define _MOTORR_WEAK 0x04
+#define _MOTORR_MEDIUM 0x08
+#define _MOTORR_STRONG 0x0c
+#define _MOTORT_WEAK 0x40
+#define _MOTORT_MEDIUM 0x80
+#define _MOTORT_STRONG 0xc0
+#define _BUTTON_SELECT1 0x40
+#define _BUTTON_SELECT2 0x80
+#define _BUTTON_DISABLE 0xc0
+
+/** Register RegStatus2 (Addr: 0x66) */
+#define _REFLECTIONLAMP_ON 0x01
+#define _TPALAMP_ON 0x02
+#define _STILL_FREE_RUNNING 0x04
+#define _BUFFER_IS_FULL 0x08
+
+/** Register RegTestMode (Addr: 0xf0) */
+#define _SW_TESTMODE 0x20
+
+/** buffer sizes */
+#define _BYTES_PER_CHANNEL 5500UL
+#define _SIZE_DATA_BUF (u_long)(_BYTES_PER_CHANNEL * 3 * 2)
+#define _SIZE_TPA_DATA_BUF (u_long)(_BYTES_PER_CHANNEL * 3 * 2)
+#define _SIZE_SHADING_SUM_BUF (u_long)(_BYTES_PER_CHANNEL * 3 * 4)
+#define _SIZE_TOTAL_BUF (u_long)(_SIZE_DATA_BUF + _SIZE_SHADING_SUM_BUF)
+#define _SIZE_TOTAL_BUF_TPA (u_long)(_SIZE_TOTAL_BUF + _SIZE_TPA_DATA_BUF)
+
+/** internal FIFO buffers */
+#define _SIZE_REDFIFO 196608UL /* 192k */
+#define _SIZE_GREENFIFO 147456UL /* 144k */
+#define _SIZE_BLUEFIFO 114688UL /* 112k */
+
+#define _SIZE_GRAYFIFO (_SIZE_REDFIFO + _SIZE_GREENFIFO + _SIZE_BLUEFIFO)
+
+/* Scan State Definitions */
+#define _SS_STEP 0x08
+#define _SS_RED 0x04
+#define _SS_GREEN 0x02
+#define _SS_BLUE 0x01
+
+#define _SS_MONO _SS_GREEN
+#define _SS_COLOR (_SS_RED | _SS_GREEN | _SS_BLUE)
+
+/** some positioning stuff
+ */
+#define _RFT_SCANNING_ORG 380U
+#define _POS_SCANNING_ORG 2840U
+#define _NEG_SCANNING_ORG 3000U
+#define _TPA_SHADINGORG 2172U
+
+#define _DATA_ORIGIN_X 72
+#define _YOFFSET 300
+
+#define _POS_PAGEWIDTH 450U
+#define _POS_ORG_OFFSETX 0x41C
+
+#define _NEG_PAGEWIDTH 464U
+#define _NEG_PAGEWIDTH600 992U
+#define _NEG_ORG_OFFSETX 0x430
+#define _NEG_EDGE_VALUE 0x800
+#define _NEG_SHADING_OFFS 1500U
+
+#define _SHADING_BEGINX 4U
+
+
+#define _DEFAULT_LINESCANTIME 96
+
+#define _LINE_TIMEOUT (_SECOND * 5 )
+
+
+/** for mirroring parts of the 98003 ASIC register set
+ */
+typedef struct {
+ SANE_Byte RD_Motor1Control; /* 0x0b */
+ SANE_Byte RD_StepControl; /* 0x14 */
+ SANE_Byte RD_Motor0Control; /* 0x15 */
+ SANE_Byte RD_XStepTime; /* 0x16 */
+ SANE_Byte RD_ModeControl; /* 0x1b */
+ SANE_Byte RD_LineControl; /* 0x1c */
+ SANE_Byte RD_ScanControl; /* 0x1d, init = 5 */
+ SANE_Byte RD_ModelControl; /* 0x1f */
+ SANE_Byte RD_Model1Control; /* 0x20 */
+ u_short RD_Dpi; /* 0x21 */
+ u_short RD_Origin; /* 0x23 */
+ u_short RD_Pixels; /* 0x25 */
+ u_short RD_ThresholdControl; /* 0x27 */
+ SANE_Byte RD_ThresholdGapCtrl; /* 0x29 */
+ u_short RD_RedDarkOff; /* 0x33 */
+ u_short RD_GreenDarkOff; /* 0x35 */
+ u_short RD_BlueDarkOff; /* 0x37 */
+
+ u_long RD_BufFullSize; /* 0x54 */
+ u_short RD_MotorTotalSteps; /* 0x57 */
+
+ SANE_Byte RD_ScanControl1; /* 0x5b */
+ SANE_Byte RD_MotorDriverType; /* 0x64 */
+ SANE_Byte RD_ExtLineControl; /* 0x6d */
+ SANE_Byte RD_ExtXStepTime; /* 0x6e */
+
+} ShadowRegs;
+
+/** to hold all the shading stuff for calibrating a scanner
+ */
+typedef struct svd
+{
+ ColorWord GainResize;
+ ColorWord DarkCmpHi;
+ ColorWord DarkCmpLo;
+ ColorWord DarkOffSub;
+ ColorByte DarkDAC;
+ SANE_Byte Reserved;
+} ShadingVarDef;
+
+typedef struct
+{
+ ShadingVarDef *pCcdDac;
+ ColorByte DarkDAC;
+ ColorWord DarkOffset;
+ u_short wDarkLevels;
+ SANE_Byte intermediate;
+
+ u_long dwDiv;
+ SANE_Byte skipShadow;
+
+ SANE_Byte skipHilight;
+ ColorByte Hilight;
+ RGBUShortDef *pHilight;
+ ColorByte Gain;
+ SANE_Byte bGainDouble;
+ SANE_Byte bUniGain;
+ SANE_Byte bMinGain;
+ SANE_Byte bMaxGain;
+ SANE_Byte bGainHigh;
+ SANE_Byte bGainLow;
+
+ SANE_Bool fStop;
+
+ u_short wExposure;
+ u_short wXStep;
+
+} ShadingDef;
+
+#endif /* guard __U12_HWDEF_H__ */
+
+/* END U12-HWDEF.H ..........................................................*/
diff --git a/backend/u12-if.c b/backend/u12-if.c
new file mode 100644
index 0000000..be213b4
--- /dev/null
+++ b/backend/u12-if.c
@@ -0,0 +1,602 @@
+/** @file u12-if.c
+ * @brief The interface functions to the U12 backend stuff.
+ *
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - added model tweaking
+ * - fixed autodetection bug
+ * - added line-scaling stuff
+ * - removed u12if_setScanEnv()
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#define _DEF_BRIGHTEST_SKIP 3
+#define _DEF_DARKEST_SKIP 5
+
+/** useful for description tables
+ */
+typedef struct {
+ int id;
+ char *desc;
+} TabDef;
+
+typedef struct {
+ char *vp;
+ char *name;
+} DevDesc;
+
+#define _PLUSTEK_VENID 0x07B3
+#define _KYE_VENID 0x0458
+
+/** to allow different vendors...
+ */
+static TabDef u12Vendors[] = {
+
+ { _PLUSTEK_VENID, "Plustek" },
+ { _KYE_VENID, "KYE/Genius" },
+ { 0xFFFF, NULL }
+};
+
+/** list of supported devices
+ */
+static DevDesc u12Devices[] = {
+ { "0x07B3-0x0001", "1212U/U12" },
+ { "0x0458-0x2004", "Colorpage HR6" },
+ { NULL, NULL }
+};
+
+/** for autodetection */
+static SANE_Char USB_devname[1024];
+
+/********************** the USB scanner interface ****************************/
+
+/**
+ */
+static SANE_Status u12_initDev( U12_Device *dev, int handle, int vendor )
+{
+ int i;
+ SANE_Status res;
+ TimerDef timer;
+
+ /* well now we patch the vendor string...
+ * if not found, the default vendor will be Plustek
+ */
+ for( i = 0; u12Vendors[i].desc != NULL; i++ ) {
+
+ if( u12Vendors[i].id == vendor ) {
+ dev->sane.vendor = u12Vendors[i].desc;
+ DBG( _DBG_INFO, "Vendor adjusted to: >%s<\n", dev->sane.vendor );
+ break;
+ }
+ }
+ dev->fd = handle;
+
+ dev->adj.upNormal = 0;
+ dev->adj.upNegative = 20;
+ dev->adj.upPositive = -30;
+ dev->adj.leftNormal = 51;
+
+ res = SANE_STATUS_IO_ERROR;
+ if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {
+
+ u12motor_PositionModuleToHome( dev );
+
+ u12io_StartTimer( &timer, _SECOND * 20);
+ do {
+ if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER) {
+ res = SANE_STATUS_GOOD;
+ break;
+ }
+ } while( !u12io_CheckTimer( &timer ));
+ } else {
+ res = u12hw_InitAsic( dev, SANE_FALSE );
+ }
+
+ if( res == SANE_STATUS_GOOD )
+ u12hw_PutToIdleMode( dev );
+ return res;
+}
+
+/** will be called upon sane_exit
+ */
+static void u12if_shutdown( U12_Device *dev )
+{
+ SANE_Int handle;
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "Shutdown called (dev->fd=%d, %s)\n",
+ dev->fd, dev->sane.name );
+ if( SANE_STATUS_GOOD == sanei_usb_open( dev->sane.name, &handle )) {
+
+ dev->fd = handle;
+ u12io_OpenScanPath( dev );
+
+ u12hw_PutToIdleMode( dev );
+
+ if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {
+
+ u12motor_PositionModuleToHome( dev );
+
+ u12io_StartTimer( &timer, _SECOND * 20);
+ do {
+ if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER) {
+ break;
+ }
+ } while( !u12io_CheckTimer( &timer ));
+ }
+ DBG( _DBG_INFO, "* Home position reached.\n" );
+
+ if( 0 != dev->adj.lampOffOnEnd ) {
+
+ DBG( _DBG_INFO, "* Switching lamp off...\n" );
+ dev->regs.RD_ScanControl &= ~_SCAN_LAMPS_ON;
+ u12io_DataToRegister(dev,REG_SCANCONTROL, dev->regs.RD_ScanControl );
+ }
+
+ u12io_CloseScanPath( dev );
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ }
+
+#if 0
+ usb_StopLampTimer( dev );
+#endif
+ DBG( _DBG_INFO, "Shutdown done.\n" );
+}
+
+/** This function checks wether a device, described by a given
+ * string(vendor and product ID), is supported by this backend or not
+ *
+ * @param usbIdStr - sting consisting out of product and vendor ID
+ * format: "0xVVVV-0xPPPP" VVVV = Vendor ID, PPP = Product ID
+ * @returns; SANE_TRUE if supported, SANE_FALSE if not
+ */
+static SANE_Bool u12if_IsDeviceSupported( U12_Device *dev )
+{
+ int i;
+
+ for( i = 0; NULL != u12Devices[i].name; i++ ) {
+
+ if( !strcmp( dev->usbId, u12Devices[i].vp )) {
+ dev->sane.model = u12Devices[i].name;
+ return SANE_TRUE;
+ }
+ }
+
+ return SANE_FALSE;
+}
+
+/**
+ */
+static SANE_Status u12if_usbattach( SANE_String_Const dev_name )
+{
+ if( USB_devname[0] == '\0' ) {
+ DBG( _DBG_INFO, "Found device at >%s<\n", dev_name );
+ strncpy( USB_devname, dev_name, 1023 );
+ USB_devname[1023] = '\0';
+ } else {
+ DBG( _DBG_INFO, "Device >%s< ignoring\n", dev_name );
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#ifndef _FAKE_DEVICE
+/** here we roam through our list of supported devices and
+ * cross check with the ones the system reports...
+ * @param vendor - pointer to receive vendor ID
+ * @param product - pointer to receive product ID
+ * @return SANE_TRUE if a matching device has been found or
+ * SANE_FALSE if nothing supported found...
+ */
+static SANE_Bool usbDev_autodetect( SANE_Word *vendor, SANE_Word *product )
+{
+ int i;
+ SANE_Word p, v;
+
+ DBG( _DBG_INFO, "Autodetection...\n" );
+
+ for( i = 0; NULL != u12Devices[i].name; i++ ) {
+
+ v = strtol( &(u12Devices[i].vp)[0], 0, 0 );
+ p = strtol( &(u12Devices[i].vp)[7], 0, 0 );
+ DBG( _DBG_INFO, "* checking for 0x%04x-0x%04x\n", v, p );
+
+ sanei_usb_find_devices( v, p, u12if_usbattach );
+
+ if( USB_devname[0] != '\0' ) {
+
+ *vendor = v;
+ *product = p;
+ DBG( _DBG_INFO, "* using device >%s<\n", USB_devname );
+ return SANE_TRUE;
+ }
+ }
+
+ return SANE_FALSE;
+}
+#endif
+
+/**
+ */
+static int u12if_open( U12_Device *dev )
+{
+ char devStr[50];
+ int result;
+ SANE_Int handle;
+ SANE_Word vendor, product;
+ SANE_Bool was_empty;
+
+ DBG( _DBG_INFO, "u12if_open(%s,%s)\n", dev->name, dev->usbId );
+
+ USB_devname[0] = '\0';
+
+#ifdef _FAKE_DEVICE
+ dev->name = strdup( "auto" );
+ dev->sane.name = dev->name;
+ was_empty = SANE_FALSE;
+
+ result = SANE_STATUS_UNSUPPORTED;
+#else
+ if( !strcmp( dev->name, "auto" )) {
+
+ if( dev->usbId[0] == '\0' ) {
+
+ if( !usbDev_autodetect( &vendor, &product )) {
+ DBG( _DBG_ERROR, "No supported device found!\n" );
+ return -1;
+ }
+
+ } else {
+
+ vendor = strtol( &dev->usbId[0], 0, 0 );
+ product = strtol( &dev->usbId[7], 0, 0 );
+
+ sanei_usb_find_devices( vendor, product, u12if_usbattach );
+
+ if( USB_devname[0] == '\0' ) {
+ DBG( _DBG_ERROR, "No matching device found!\n" );
+ return -1;
+ }
+ }
+
+ if( SANE_STATUS_GOOD != sanei_usb_open( USB_devname, &handle )) {
+ return -1;
+ }
+
+ /* replace the old devname, so we are able to have multiple
+ * auto-detected devices
+ */
+ free( dev->name );
+ dev->name = strdup( USB_devname );
+ dev->sane.name = dev->name;
+
+ } else {
+
+ if( SANE_STATUS_GOOD != sanei_usb_open( dev->name, &handle ))
+ return -1;
+ }
+ was_empty = SANE_FALSE;
+
+ result = sanei_usb_get_vendor_product( handle, &vendor, &product );
+#endif
+
+ if( SANE_STATUS_GOOD == result ) {
+
+ sprintf( devStr, "0x%04X-0x%04X", vendor, product );
+
+ DBG(_DBG_INFO,"Vendor ID=0x%04X, Product ID=0x%04X\n",vendor,product);
+
+ if( dev->usbId[0] != '\0' ) {
+
+ if( 0 != strcmp( dev->usbId, devStr )) {
+ DBG( _DBG_ERROR, "Specified Vendor and Product ID "
+ "doesn't match with the ones\n"
+ "in the config file\n" );
+ sanei_usb_close( handle );
+ return -1;
+ }
+ } else {
+ sprintf( dev->usbId, "0x%04X-0x%04X", vendor, product );
+ was_empty = SANE_TRUE;
+ }
+
+ } else {
+
+ DBG( _DBG_INFO, "Can't get vendor & product ID from driver...\n" );
+
+ /* if the ioctl stuff is not supported by the kernel and we have
+ * nothing specified, we have to give up...
+ */
+ if( dev->usbId[0] == '\0' ) {
+ DBG( _DBG_ERROR, "Cannot autodetect Vendor an Product ID, "
+ "please specify in config file.\n" );
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ vendor = strtol( &dev->usbId[0], 0, 0 );
+ product = strtol( &dev->usbId[7], 0, 0 );
+ DBG( _DBG_INFO, "... using the specified: "
+ "0x%04X-0x%04X\n", vendor, product );
+ }
+
+ /* before accessing the scanner, check if supported!
+ */
+ if( !u12if_IsDeviceSupported( dev )) {
+ DBG( _DBG_ERROR, "Device >%s<, is not supported!\n", dev->usbId );
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ dev->mode = _PP_MODE_SPP;
+ dev->fd = handle;
+
+ /* is it accessible ? */
+ if( SANE_STATUS_GOOD != u12hw_CheckDevice( dev )) {
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ DBG( _DBG_INFO, "Detected vendor & product ID: "
+ "0x%04X-0x%04X\n", vendor, product );
+
+ if( was_empty )
+ dev->usbId[0] = '\0';
+
+ /* now initialize the device */
+ if( SANE_STATUS_GOOD != u12_initDev( dev, handle, vendor )) {
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ if( _PLUSTEK_VENID == vendor ) {
+ if( dev->Tpa )
+ dev->sane.model = "UT12";
+ }
+
+ dev->initialized = SANE_TRUE;
+ return handle;
+}
+
+/**
+ */
+static int u12if_close( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "u12if_close()\n" );
+ u12io_CloseScanPath( dev );
+ sanei_usb_close( dev->fd );
+ dev->fd = -1;
+ return 0;
+}
+
+/**
+ */
+static SANE_Status u12if_getCaps( U12_Device *dev )
+{
+ int cntr;
+ int res_x = 600 ; /*dev->caps.OpticDpi.x */
+ DBG( _DBG_INFO, "u12if_getCaps()\n" );
+
+/* FIXME: set dpi_range.max, max_x & max_y on a per model base */
+ dev->dpi_max_x = 600;
+ dev->dpi_max_y = 1200;
+
+ /* A4 devices */
+ dev->max_x = 8.5 * (double)_MM_PER_INCH;
+ dev->max_y = 11.6934 * (double)_MM_PER_INCH;
+
+ /* limit the range */
+ dev->dpi_range.min = _DEF_DPI;
+ dev->dpi_range.max = dev->dpi_max_y;
+ dev->dpi_range.quant = 0;
+ dev->x_range.min = 0;
+ dev->x_range.max = SANE_FIX(dev->max_x);
+ dev->x_range.quant = 0;
+ dev->y_range.min = 0;
+ dev->y_range.max = SANE_FIX(dev->max_y);
+ dev->y_range.quant = 0;
+
+ /* calculate the size of the resolution list +
+ * one more to avoid a buffer overflow, then allocate it...
+ */
+ dev->res_list = (SANE_Int *)
+ calloc((((res_x * 16)-_DEF_DPI)/25+1),
+ sizeof (SANE_Int));
+
+ if (NULL == dev->res_list) {
+ DBG( _DBG_ERROR, "alloc fail, resolution problem\n" );
+ u12if_close(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* build up the resolution table */
+ dev->res_list_size = 0;
+ for( cntr = _DEF_DPI; cntr <= (res_x*16); cntr += 25 ) {
+ dev->res_list_size++;
+ dev->res_list[dev->res_list_size - 1] = (SANE_Int)cntr;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status u12if_startScan( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "u12if_startScan()\n" );
+ u12hw_StopLampTimer( dev );
+ u12hw_SetGeneralRegister( dev );
+ u12hw_ControlLampOnOff( dev );
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status u12if_stopScan( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "u12if_stopScan()\n" );
+
+#if 0
+ u12motor_ToHomePosition( dev, SANE_FALSE );
+#else
+#if 0
+ u12motor_ToHomePosition( dev, SANE_TRUE );
+ u12io_SoftwareReset( dev );
+#endif
+ u12hw_CancelSequence( dev );
+#endif
+ u12hw_StartLampTimer( dev );
+ dev->DataInf.dwAppLinesPerArea = 0;
+ dev->DataInf.dwScanFlag &= ~_SCANDEF_SCANNING;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status u12if_prepare( U12_Device *dev )
+{
+ SANE_Status res;
+
+ DBG( _DBG_INFO, "u12if_prepare()\n" );
+
+ u12motor_ToHomePosition( dev, SANE_TRUE );
+
+ res = u12hw_WarmupLamp( dev );
+ if( res != SANE_STATUS_GOOD )
+ return res;
+
+ res = u12shading_DoCalibration( dev );
+ if( res != SANE_STATUS_GOOD )
+ return res;
+
+ u12image_PrepareScaling( dev );
+
+ u12motor_ForceToLeaveHomePos( dev );
+ if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW )
+ u12hw_SetupPreviewCondition( dev );
+ else
+ u12hw_SetupScanningCondition( dev );
+
+ res = u12motor_WaitForPositionY( dev );
+
+ _DODELAY(100);
+ u12io_ResetFifoLen();
+ u12io_GetFifoLength(dev);
+
+ dev->scan.bModuleState = _MotorAdvancing;
+ dev->scan.oldScanState = u12io_GetScanState( dev );
+ dev->scan.oldScanState &= _SCANSTATE_MASK;
+ dev->DataInf.dwScanFlag |= _SCANDEF_SCANNING;
+ DBG( _DBG_INFO, "* oldScanState = %u\n", dev->scan.oldScanState );
+ DBG( _DBG_INFO, "u12if_prepare() done.\n" );
+ return res;
+}
+
+/**
+ */
+static SANE_Status u12if_readLine( U12_Device *dev, SANE_Byte *line_buffer )
+{
+ SANE_Status res;
+
+ DBG( _DBG_READ, "u12if_readLine()\n" );
+
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "u12if_readLine() - cancel detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if( dev->scaleBuf != NULL ) {
+ res = u12image_ReadOneImageLine( dev, dev->scaleBuf );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ u12image_ScaleX( dev, dev->scaleBuf, line_buffer );
+
+ } else {
+ res = u12image_ReadOneImageLine( dev, line_buffer );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status u12if_SetupBuffer( U12_Device *dev )
+{
+ void *buffer;
+
+ DBG( _DBG_INFO, "u12if_SetupBuffer()\n" );
+ buffer = malloc(_SIZE_TOTAL_BUF_TPA);
+ if( buffer == NULL )
+ return SANE_STATUS_NO_MEM;
+
+ dev->shade.pHilight = NULL;
+ dev->bufs.b1.pReadBuf = buffer;
+ dev->bufs.b2.pSumBuf = dev->bufs.b1.pReadBuf + _SIZE_DATA_BUF;
+ dev->bufs.TpaBuf.pb = &((SANE_Byte*)dev->bufs.b2.pSumBuf)[_SIZE_SHADING_SUM_BUF];
+
+/* CHECK: We might should play around with these values... */
+ dev->shade.skipHilight = _DEF_BRIGHTEST_SKIP;
+ dev->shade.skipShadow = _DEF_DARKEST_SKIP;
+
+ if( dev->shade.skipHilight && dev->shade.skipShadow ) {
+
+ u_long skipSize;
+
+ skipSize = (u_long)((dev->shade.skipHilight + dev->shade.skipShadow)
+ * _SIZE_DATA_BUF * 3);
+ dev->shade.pHilight = (RGBUShortDef*)malloc( skipSize );
+ if( NULL != dev->shade.pHilight ) {
+ dev->shade.dwDiv = (u_long)(32UL - dev->shade.skipHilight -
+ dev->shade.skipShadow);
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* END U12-IF.C .............................................................*/
diff --git a/backend/u12-image.c b/backend/u12-image.c
new file mode 100644
index 0000000..1d9acda
--- /dev/null
+++ b/backend/u12-image.c
@@ -0,0 +1,908 @@
+/* @file u12_image.c
+ * @brief functions to convert scanner data into image data
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - fixed fnColor42() to return 16bit values instead of
+ * only 12bit (this is the maximum the scanner can)
+ * - added scaling function u12image_ScaleX()
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/*************************** local vars **************************************/
+
+static u_short wPreviewScanned = 0;
+
+static ExpXStepDef negScan[5] = {
+ {128, 8}, {96, 12}, {96, 24}, {96, 48}, {96, 96}
+};
+
+static ExpXStepDef posScan[5] = {
+ {128, 8}, {96, 12}, {96, 24}, {96, 48}, {96, 96}
+};
+
+static ExpXStepDef nmlScan[5] = {
+ {160, 10}, {96, 12}, {96, 24}, {96, 48}, {96, 96},
+};
+
+/*************************** local functions *********************************/
+
+/**
+ */
+static SANE_Bool fnReadToDriver( U12_Device *dev )
+{
+ dev->regs.RD_ModeControl = _ModeFifoBSel;
+ u12io_ReadMonoData( dev, dev->scan.BufPut.blue.bp,
+ dev->DataInf.dwAsicBytesPerPlane );
+
+ dev->regs.RD_ModeControl = _ModeFifoGSel;
+ u12io_ReadMonoData( dev, dev->scan.BufPut.green.bp,
+ dev->DataInf.dwAsicBytesPerPlane );
+
+ if( dev->scan.gd_gk.wGreenKeep )
+ dev->scan.gd_gk.wGreenKeep--;
+ else {
+ dev->scan.BufPut.green.bp += dev->DataInf.dwAsicBytesPerPlane;
+
+ if( dev->scan.BufPut.green.bp >= dev->scan.BufEnd.green.bp )
+ dev->scan.BufPut.green.bp = dev->scan.BufBegin.green.bp;
+ }
+
+ dev->regs.RD_ModeControl = _ModeFifoRSel;
+ u12io_ReadMonoData( dev, dev->scan.BufPut.red.bp,
+ dev->DataInf.dwAsicBytesPerPlane );
+
+ dev->scan.BufPut.red.bp += dev->DataInf.dwAsicBytesPerPlane;
+ if( dev->scan.BufPut.red.bp >= dev->scan.BufEnd.red.bp )
+ dev->scan.BufPut.red.bp = dev->scan.BufBegin.red.bp;
+
+ if( dev->scan.bd_rk.wRedKeep ) {
+ dev->scan.bd_rk.wRedKeep--;
+ return SANE_FALSE;
+
+ } else {
+
+ dev->scan.BufData.green.bp = dev->scan.BufGet.green.bp;
+ dev->scan.BufData.red.bp = dev->scan.BufGet.red.bp;
+ dev->scan.BufData.blue.bp = dev->scan.BufGet.blue.bp;
+
+ dev->scan.BufGet.red.bp += dev->DataInf.dwAsicBytesPerPlane;
+ dev->scan.BufGet.green.bp += dev->DataInf.dwAsicBytesPerPlane;
+
+ if( dev->scan.BufGet.red.bp >= dev->scan.BufEnd.red.bp )
+ dev->scan.BufGet.red.bp = dev->scan.BufBegin.red.bp;
+
+ if( dev->scan.BufGet.green.bp >= dev->scan.BufEnd.green.bp )
+ dev->scan.BufGet.green.bp = dev->scan.BufBegin.green.bp;
+
+ return SANE_TRUE;
+ }
+}
+
+/**
+ */
+static SANE_Bool fnReadOutScanner( U12_Device *dev )
+{
+ if( dev->scan.bd_rk.wBlueDiscard ) {
+
+ dev->scan.bd_rk.wBlueDiscard--;
+ dev->regs.RD_ModeControl = _ModeFifoBSel;
+
+ u12io_ReadMonoData( dev, dev->bufs.b1.pReadBuf,
+ dev->DataInf.dwAsicBytesPerPlane );
+
+ if( dev->scan.gd_gk.wGreenDiscard ) {
+ dev->scan.gd_gk.wGreenDiscard--;
+
+ dev->regs.RD_ModeControl = _ModeFifoGSel;
+ u12io_ReadMonoData( dev, dev->bufs.b1.pReadBuf,
+ dev->DataInf.dwAsicBytesPerPlane );
+ }
+ return SANE_FALSE;
+
+ } else {
+ u12io_ReadColorData( dev, dev->bufs.b1.pReadBuf,
+ dev->DataInf.dwAsicBytesPerPlane );
+ return SANE_TRUE;
+ }
+}
+
+/** some sampling functions
+ */
+static SANE_Bool fnEveryLine( U12_Device *dev )
+{
+ _VAR_NOT_USED( dev );
+ return SANE_TRUE;
+}
+
+static SANE_Bool fnSampleLines( U12_Device *dev )
+{
+ dev->DataInf.wYSum += dev->DataInf.xyAppDpi.y;
+
+ if( dev->DataInf.wYSum >= dev->DataInf.xyPhyDpi.y ) {
+ dev->DataInf.wYSum -= dev->DataInf.xyPhyDpi.y;
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+static SANE_Bool fnSamplePreview( U12_Device *dev )
+{
+ dev->DataInf.wYSum += wPreviewScanned;
+ if( dev->DataInf.wYSum >= 150 ) {
+
+ dev->DataInf.wYSum -= 150;
+ return SANE_TRUE;
+ }
+
+ return SANE_FALSE;
+}
+
+/** this function is used when
+ * - the data type is B/W or GrayScale.
+ * - the required horizontal resolution doesn't exceed the optic spec.
+ * - the required vertical resolution exceeds the optic spec.
+ */
+static void fnDataDirect( U12_Device *dev, void *src, void *dest, u_long len )
+{
+ _VAR_NOT_USED( dev );
+ memcpy( dest, src, len );
+}
+
+/** merges the color planes to pixels style without enlarge operation.
+ */
+static void fnColorDirect( U12_Device *dev, void *pb, void *img, u_long len )
+{
+ SANE_Byte *src;
+ RGBByteDef *dest;
+
+ src = (SANE_Byte*)img;
+ dest = (RGBByteDef*)pb;
+
+ for ( len = dev->DataInf.dwAsicPixelsPerPlane; len; len--, src++, dest++) {
+
+ dest->Red = *src;
+ dest->Green = src[dev->DataInf.dwAsicPixelsPerPlane];
+ dest->Blue = src[dev->DataInf.dwAsicPixelsPerPlane*2];
+ }
+}
+
+/** merges the color planes to pixels style without enlarge operation.
+ * The scanner returns the pixel data in Motorola-Format, so we have to swap
+ * (at least on x86)
+ */
+static void fnColor42( U12_Device *dev, void *pb, void *img, u_long len )
+{
+ u_short *src;
+ RGBUShortDef *dest;
+
+ register u_long i;
+
+ _VAR_NOT_USED( len );
+ src = (u_short*)img;
+ dest = (RGBUShortDef*)pb;
+
+ for ( i = dev->DataInf.dwAsicPixelsPerPlane; i; i--, src++, dest++) {
+
+ dest->Red = (*src) << 4;
+ dest->Green = (src[dev->DataInf.dwAsicPixelsPerPlane]) << 4;
+ dest->Blue = (src[dev->DataInf.dwAsicPixelsPerPlane * 2]) << 4;
+ }
+}
+
+/**
+ */
+static void u12image_SetupScanStateVariables( U12_Device *dev, u_long index )
+{
+ DataType var;
+
+ DBG( _DBG_INFO, "u12image_SetupScanStateVariables(%lu)\n", index );
+ dev->scan.dpiIdx = index;
+
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA)) {
+
+ dev->shade.wExposure = nmlScan[index].exposureTime;
+ dev->shade.wXStep = nmlScan[index].xStepTime;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut ) {
+ dev->shade.wExposure >>= 1;
+ dev->shade.wXStep >>= 1;
+ }
+ } else {
+ if( dev->DataInf.dwScanFlag & _SCANDEF_Transparency ) {
+ dev->shade.wExposure = posScan[index].exposureTime;
+ dev->shade.wXStep = posScan[index].xStepTime;
+ } else {
+ dev->shade.wExposure = dev->scan.negScan[index].exposureTime;
+ dev->shade.wXStep = dev->scan.negScan[index].xStepTime;
+ }
+ }
+ dev->scan.dwInterval = 1;
+
+ if( dev->DataInf.wPhyDataType == COLOR_BW )
+ var.dwValue = 0;
+ else {
+ if( dev->DataInf.wPhyDataType == COLOR_256GRAY )
+ var.dwValue = 2500;
+ else
+ var.dwValue = 3200;
+ }
+
+ /* for small size/descreen */
+ if((dev->DataInf.xyAppDpi.y >= 300) && var.dwValue &&
+ (dev->DataInf.dwAsicBytesPerPlane <= var.dwValue)) {
+ dev->scan.dwInterval <<= 1;
+ }
+
+ if( var.dwValue && dev->DataInf.dwAsicBytesPerPlane > var.dwValue ) {
+ if((var.dwValue << 1) > dev->DataInf.dwAsicBytesPerPlane)
+ dev->scan.dwInterval <<= 1;
+ else
+ if((var.dwValue << 2) > dev->DataInf.dwAsicBytesPerPlane)
+ dev->scan.dwInterval <<= 2;
+ else
+ dev->scan.dwInterval <<= 3;
+ }
+
+ if( dev->DataInf.wPhyDataType >= COLOR_TRUE24 ) {
+
+ if( dev->DataInf.xyPhyDpi.y > 75U ) {
+ if( dev->f0_8_16 ) {
+ dev->scan.gd_gk.wGreenDiscard = dev->DataInf.xyPhyDpi.y / 75U;
+ } else {
+ dev->scan.gd_gk.wGreenDiscard = dev->DataInf.xyPhyDpi.y / 150U;
+ }
+ } else {
+ dev->scan.gd_gk.wGreenDiscard = 1;
+ }
+
+ dev->scan.bd_rk.wBlueDiscard = dev->scan.gd_gk.wGreenDiscard << 1;
+ } else {
+ dev->scan.bd_rk.wBlueDiscard = dev->scan.gd_gk.wGreenDiscard = 0;
+ }
+}
+
+/** limit the resolution
+ */
+static u_short
+u12image_GetPhysDPI( U12_Device *dev, ImgDef *img, SANE_Bool fDpiX )
+{
+ if( fDpiX ) {
+
+ if( img->xyDpi.x > dev->dpi_max_x )
+ return dev->dpi_max_x;
+ else
+ return img->xyDpi.x;
+
+ } else {
+
+ if( img->xyDpi.y > dev->dpi_max_y )
+ return dev->dpi_max_y;
+ else
+ return img->xyDpi.y;
+ }
+}
+
+/** calculate the image properties according to the scanmode
+ * set all internally needed information
+ */
+static void u12image_GetImageInfo( U12_Device *dev, ImgDef *image )
+{
+ DBG( _DBG_INFO, "u12image_GetImageInfo()\n" );
+
+ dev->DataInf.xyPhyDpi.x = u12image_GetPhysDPI(dev, image, SANE_TRUE );
+ dev->DataInf.xyPhyDpi.y = u12image_GetPhysDPI(dev, image, SANE_FALSE);
+
+ DBG( _DBG_INFO, "* xyPhyDpi.x = %u, xyPhyDpi.y = %u\n",
+ dev->DataInf.xyPhyDpi.x, dev->DataInf.xyPhyDpi.y );
+
+ DBG( _DBG_INFO, "* crArea.x = %u, crArea.y = %u\n",
+ image->crArea.x, image->crArea.y );
+
+ DBG( _DBG_INFO, "* crArea.cx = %u, crArea.cy = %u\n",
+ image->crArea.cx, image->crArea.cy );
+
+ dev->DataInf.xyRatio = (double)dev->DataInf.xyPhyDpi.y/
+ (double)dev->DataInf.xyPhyDpi.x;
+
+ dev->DataInf.dwAppLinesPerArea = (u_long)image->crArea.cy *
+ image->xyDpi.y / _MEASURE_BASE;
+
+ dev->DataInf.dwAppPixelsPerLine = (u_long)image->crArea.cx *
+ image->xyDpi.x / _MEASURE_BASE;
+
+ dev->DataInf.dwPhysBytesPerLine = (u_long)image->crArea.cx *
+ dev->DataInf.xyPhyDpi.x / _MEASURE_BASE;
+
+ if( image->wDataType <= COLOR_BW ) {
+ dev->DataInf.dwAsicPixelsPerPlane =
+ (dev->DataInf.dwAppPixelsPerLine+7UL) & 0xfffffff8UL;
+ dev->DataInf.dwAppPhyBytesPerLine =
+ dev->DataInf.dwAppBytesPerLine =
+ dev->DataInf.dwAsicBytesPerLine =
+ dev->DataInf.dwAsicBytesPerPlane = dev->DataInf.dwAsicPixelsPerPlane>>3;
+ } else {
+ dev->DataInf.dwAsicBytesPerPlane =
+ dev->DataInf.dwAsicPixelsPerPlane = dev->DataInf.dwAppPixelsPerLine;
+ }
+
+ if( COLOR_TRUE42 == image->wDataType ) {
+ dev->DataInf.dwAsicBytesPerPlane *= 2;
+ }
+
+ switch( image->wDataType ) {
+
+ case COLOR_BW:
+ dev->scan.DataProcess = fnDataDirect;
+ dev->DataInf.wPhyDataType = COLOR_BW;
+ dev->shade.intermediate = _ScanMode_Mono;
+ break;
+
+ case COLOR_256GRAY:
+ dev->scan.DataProcess = fnDataDirect;
+ dev->DataInf.dwAsicBytesPerLine =
+ dev->DataInf.dwAppPhyBytesPerLine = dev->DataInf.dwAppPixelsPerLine;
+ dev->DataInf.wPhyDataType = COLOR_256GRAY;
+ dev->shade.intermediate = _ScanMode_Mono;
+ break;
+
+ case COLOR_TRUE24:
+ dev->scan.DataProcess = fnColorDirect;
+ dev->DataInf.dwAsicBytesPerLine =
+ dev->DataInf.dwAppPhyBytesPerLine = dev->DataInf.dwAppPixelsPerLine * 3;
+ dev->DataInf.wPhyDataType = COLOR_TRUE24;
+ dev->shade.intermediate = _ScanMode_Color;
+ break;
+
+ case COLOR_TRUE42:
+ dev->scan.DataProcess = fnColor42;
+ dev->DataInf.dwAsicBytesPerLine =
+ dev->DataInf.dwAppPhyBytesPerLine = dev->DataInf.dwAppPixelsPerLine * 6;
+ dev->DataInf.wPhyDataType = COLOR_TRUE42;
+ dev->shade.intermediate = _ScanMode_Color;
+ break;
+ }
+
+ /* raus mit einem von beiden!!!!*/
+ dev->DataInf.dwAppBytesPerLine = dev->DataInf.dwAppPhyBytesPerLine;
+
+ DBG( _DBG_INFO, "AppLinesPerArea = %lu\n", dev->DataInf.dwAppLinesPerArea );
+ DBG( _DBG_INFO, "AppPixelsPerLine = %lu\n", dev->DataInf.dwAppPixelsPerLine );
+ DBG( _DBG_INFO, "AppPhyBytesPerLine = %lu\n", dev->DataInf.dwAppPhyBytesPerLine );
+ DBG( _DBG_INFO, "AppBytesPerLine = %lu\n", dev->DataInf.dwAppBytesPerLine );
+ DBG( _DBG_INFO, "AsicPixelsPerPlane = %lu\n", dev->DataInf.dwAsicPixelsPerPlane );
+ DBG( _DBG_INFO, "AsicBytesPerPlane = %lu\n", dev->DataInf.dwAsicBytesPerPlane );
+ DBG( _DBG_INFO, "AsicBytesPerLine = %lu\n", dev->DataInf.dwAsicBytesPerLine );
+ DBG( _DBG_INFO, "Physical Bytes = %lu\n", dev->DataInf.dwPhysBytesPerLine );
+}
+
+/**
+ */
+static int imageSetupScanSettings( U12_Device *dev, ImgDef *img )
+{
+ u_short brightness;
+
+ DBG( _DBG_INFO, "imageSetupScanSettings()\n" );
+
+ dev->DataInf.dwScanFlag = img->dwFlag;
+ dev->DataInf.crImage = img->crArea;
+
+ DBG( _DBG_INFO,"* DataInf.dwScanFlag = 0x%08lx\n",dev->DataInf.dwScanFlag);
+
+ dev->DataInf.crImage.x <<= 1;
+
+ dev->DataInf.xyAppDpi = img->xyDpi;
+ dev->DataInf.wAppDataType = img->wDataType;
+
+ u12image_GetImageInfo( dev, img );
+
+ dev->scan.lBufferAdjust = (long)dev->DataInf.dwAppBytesPerLine;
+
+ DBG( _DBG_INFO, "* Scan settings:\n" );
+ DBG( _DBG_INFO, "* ImageInfo: (x=%u,y=%u,dx=%u,dy=%u)\n",
+ dev->DataInf.crImage.x, dev->DataInf.crImage.y,
+ dev->DataInf.crImage.cx, dev->DataInf.crImage.cy );
+
+ /*
+ * 0 _DEF_BW_THRESHOLD 255
+ * +-------------------------+--------------------------------+
+ * |<------- Black --------->|<----------- White ------------>|
+ * So, if user wish to make image darker, the threshold value should be
+ * higher than _defBwThreshold, otherwise it should lower than the
+ * _DefBwThreshold.
+ * Darker = _DEF_BW_THRESHOLD + White * Input / 127;
+ * Input < 0, and White = 255 - _DEF_BW_THRESHOLD, so
+ * = _DEF_BW_THRESHOLD - (255 - _DEF_BW_THRESHOLD) * Input / 127;
+ * The brighter is the same idea.
+ */
+
+/* CHECK: We have now two methods for setting the brightness...
+*/
+ DBG( _DBG_INFO, "* brightness = %i\n", dev->DataInf.siBrightness );
+ if ( dev->DataInf.siBrightness < 0) {
+ brightness = (u_short)(_DEF_BW_THRESHOLD -
+ (255 - _DEF_BW_THRESHOLD) * dev->DataInf.siBrightness /127);
+ } else {
+ brightness = (u_short)(_DEF_BW_THRESHOLD -
+ _DEF_BW_THRESHOLD * dev->DataInf.siBrightness /127);
+ }
+
+ dev->regs.RD_ThresholdControl = brightness;
+ DBG( _DBG_INFO, "* RD_ThresholdControl = %i\n", brightness );
+ return 0;
+}
+
+/** PrepareScanningVariables() !!!
+ */
+static SANE_Status u12image_SetupScanSettings( U12_Device *dev, ImgDef *img )
+{
+ DBG( _DBG_INFO, "u12image_SetupScanSettings()\n" );
+
+ wPreviewScanned = 0;
+ dev->scan.dpiIdx = 0;
+ dev->scan.negScan = negScan;
+
+ imageSetupScanSettings( dev, img );
+
+ if( !(dev->DataInf.dwScanFlag & _SCANDEF_TPA )) {
+
+ dev->scan.dwScanOrigin = dev->adj.upNormal * 4 + _RFT_SCANNING_ORG;
+
+ } else if( dev->DataInf.dwScanFlag & _SCANDEF_Transparency) {
+
+ dev->scan.dwScanOrigin = dev->adj.upPositive * 4 + _POS_SCANNING_ORG;
+ } else {
+ dev->scan.dwScanOrigin = dev->adj.upNegative * 4 + _NEG_SCANNING_ORG;
+ }
+ dev->scan.dwScanOrigin += 64 /*dev->dwModelOriginY*/;
+
+ if( dev->DataInf.xyAppDpi.y <= 75 ) {
+
+ if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW ) {
+
+ dev->scan.bDiscardAll = 0;
+ dev->DataInf.xyPhyDpi.y = 150;
+ dev->shade.intermediate |= _ScanMode_AverageOut;
+ u12image_SetupScanStateVariables( dev, 1 );
+ dev->scan.gd_gk.wGreenDiscard = 0;
+
+ if( dev->DataInf.xyAppDpi.y >= 38 )
+ dev->scan.bd_rk.wBlueDiscard = 1;
+ else
+ dev->scan.bd_rk.wBlueDiscard = 0;
+
+ if( dev->DataInf.wPhyDataType >= COLOR_256GRAY ) {
+ dev->shade.wXStep = 6;
+ dev->shade.wExposure = 8 * dev->shade.wXStep;
+ }
+ } else {
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA) &&
+ (dev->DataInf.xyAppDpi.y <= 50) &&
+ (dev->DataInf.wPhyDataType >= COLOR_TRUE24)) {
+ dev->shade.intermediate |= _ScanMode_AverageOut;
+ }
+
+ if((dev->DataInf.wPhyDataType<COLOR_TRUE24) || dev->f0_8_16 ||
+ (dev->shade.intermediate & _ScanMode_AverageOut)) {
+
+ dev->scan.bDiscardAll = 1;
+ dev->DataInf.xyPhyDpi.y = 75;
+ u12image_SetupScanStateVariables( dev, 0 );
+ } else {
+ dev->scan.bDiscardAll = 2;
+ dev->DataInf.xyPhyDpi.y = 150;
+ u12image_SetupScanStateVariables( dev, 1 );
+ }
+ }
+ } else {
+ if( dev->DataInf.xyAppDpi.y <= 150 ) {
+
+ dev->scan.bDiscardAll = 2;
+ dev->DataInf.xyPhyDpi.y = 150;
+ u12image_SetupScanStateVariables( dev, 1 );
+
+ } else if( dev->DataInf.xyAppDpi.y <= 300 ) {
+
+ dev->scan.bDiscardAll = 4;
+ dev->DataInf.xyPhyDpi.y = 300;
+ u12image_SetupScanStateVariables( dev, 2 );
+
+ } else if( dev->DataInf.xyAppDpi.y <= 600 ) {
+
+ dev->scan.bDiscardAll = 8;
+ dev->DataInf.xyPhyDpi.y = 600;
+ u12image_SetupScanStateVariables( dev, 3 );
+
+ } else {
+
+ dev->scan.bDiscardAll = 16;
+ dev->DataInf.xyPhyDpi.y = 1200;
+ u12image_SetupScanStateVariables( dev, 4 );
+ }
+ }
+
+ /* ------- lines to sample or not? ------- */
+ if( dev->DataInf.xyAppDpi.y == dev->DataInf.xyPhyDpi.y ) {
+ DBG( _DBG_INFO, "* Sample every line\n" );
+ dev->scan.DoSample = fnEveryLine;
+ } else {
+ if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW ) {
+
+ DBG( _DBG_INFO, "* Sample preview\n" );
+ dev->scan.DoSample = fnSamplePreview;
+ dev->DataInf.wYSum = 150;
+
+ if( dev->DataInf.xyAppDpi.y >= 38 )
+ wPreviewScanned = dev->DataInf.xyAppDpi.y * 2;
+ else if( dev->DataInf.xyAppDpi.y >= 19 )
+ wPreviewScanned = dev->DataInf.xyAppDpi.y * 4;
+ else
+ wPreviewScanned = dev->DataInf.xyAppDpi.y * 8;
+ } else {
+
+ DBG( _DBG_INFO, "* Sample lines (%u - %u)...\n",
+ dev->DataInf.xyPhyDpi.y, dev->DataInf.xyAppDpi.y );
+ dev->scan.DoSample = fnSampleLines;
+ dev->DataInf.wYSum = dev->DataInf.xyPhyDpi.y - dev->DataInf.xyAppDpi.y;
+ }
+ }
+
+ /* now assign the buffer pointers for image aquisition
+ */
+ dev->scan.p48BitBuf.pb = NULL;
+
+ if( dev->DataInf.wPhyDataType >= COLOR_TRUE24 ) {
+
+ u_long r, g, b;
+
+ r = (u_long)_SIZE_REDFIFO /
+ dev->DataInf.dwAsicBytesPerPlane - dev->scan.bd_rk.wRedKeep;
+ g = (u_long)_SIZE_GREENFIFO /
+ dev->DataInf.dwAsicBytesPerPlane - dev->scan.gd_gk.wGreenKeep;
+
+ if((int)r < 16 || (int)g < 16) {
+
+ b = (u_long)(dev->scan.bd_rk.wRedKeep +
+ dev->scan.gd_gk.wGreenKeep + 2U) *
+ dev->DataInf.dwAsicBytesPerPlane;
+
+ DBG( _DBG_INFO, "48Bit buffer request: "
+ "len=%lu bytes, available=%lu\n", b, _SIZE_TOTAL_BUF_TPA );
+
+ if( b > _SIZE_TOTAL_BUF_TPA ) {
+ DBG( _DBG_ERROR, "Not that much FIFO memory available!\n" );
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->scan.p48BitBuf.pb = dev->bufs.b1.pReadBuf;
+ }
+ }
+
+ if( dev->scan.p48BitBuf.pb ){
+ dev->scan.DataRead = fnReadToDriver;
+ dev->scan.BufGet.red.bp =
+ dev->scan.BufPut.red.bp =
+ dev->scan.BufBegin.red.bp = dev->scan.p48BitBuf.pb;
+ dev->scan.BufEnd.red.bp =
+ dev->scan.BufBegin.green.bp =
+ dev->scan.BufGet.green.bp =
+ dev->scan.BufPut.green.bp = dev->scan.p48BitBuf.pb +
+ dev->DataInf.dwAsicBytesPerLine *
+ (dev->scan.bd_rk.wRedKeep + 1U);
+
+ dev->scan.BufEnd.green.bp = dev->scan.BufBegin.green.bp +
+ dev->DataInf.dwAsicBytesPerLine *
+ (dev->scan.gd_gk.wGreenKeep + 1U);
+ dev->scan.BufPut.blue.bp =
+ dev->scan.BufGet.blue.bp = dev->bufs.b1.pReadBuf +
+ dev->DataInf.dwAsicBytesPerLine * 2;
+ } else {
+ dev->scan.DataRead = fnReadOutScanner;
+ dev->scan.BufPut.red.bp = dev->bufs.b1.pReadBuf;
+ dev->scan.BufData.green.bp =
+ dev->scan.BufPut.green.bp = dev->scan.BufPut.red.bp +
+ dev->DataInf.dwAsicBytesPerLine;
+ dev->scan.BufPut.blue.bp = dev->scan.BufPut.green.bp +
+ dev->DataInf.dwAsicBytesPerLine;
+
+ dev->scan.BufData.red.bp = dev->scan.BufPut.red.bp;
+ dev->scan.BufData.blue.bp = dev->scan.BufPut.blue.bp;
+ }
+
+/* CHECK: maybe remove this stuff */
+#if 0
+ if( ps->DataInf.dwScanFlag & _SCANDEF_Transparency) {
+ posScan[1].exposureTime = 96;
+ posScan[1].xStepTime = 12;
+ posScan[2].exposureTime = 96;
+ posScan[2].xStepTime = 24;
+ posScan[3].exposureTime = 96;
+ posScan[3].xStepTime = 48;
+ posScan[4].exposureTime = 96;
+ posScan[4].xStepTime = 96;
+
+ /* Reset shading Exposure Time & xStep Time */
+ ps->Shade.wExposure = posScan[ps->Scan.dpiIdx].exposureTime;
+ ps->Shade.wXStep = posScan[ps->Scan.dpiIdx].xStepTime;
+ }
+ else if( ps->DataInf.dwScanFlag & _SCANDEF_Negative) {
+ ps->Scan.negScan[1].exposureTime = 96;
+ ps->Scan.negScan[1].xStepTime = 12;
+ ps->Scan.negScan[2].exposureTime = 96;
+ ps->Scan.negScan[2].xStepTime = 24;
+ ps->Scan.negScan[3].exposureTime = 96;
+ ps->Scan.negScan[3].xStepTime = 48;
+ ps->Scan.negScan[4].exposureTime = 96;
+ ps->Scan.negScan[4].xStepTime = 96;
+
+ /* Reset shading Exposure Time & xStep Time */
+ ps->Shade.wExposure = ps->Scan.negScan[ps->Scan.dpiIdx].exposureTime;
+ ps->Shade.wXStep = ps->Scan.negScan[ps->Scan.dpiIdx].xStepTime;
+ }
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Bool u12image_DataIsReady( U12_Device *dev, void* buf )
+{
+ DBG( _DBG_READ, "* DataIsReady()\n" );
+
+ if( dev->scan.bDiscardAll ) {
+ dev->scan.bDiscardAll--;
+
+ if( dev->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+ dev->regs.RD_ModeControl = _ModeFifoGSel;
+ u12io_ReadMonoData( dev, dev->bufs.b1.pReadBuf,
+ dev->DataInf.dwAsicBytesPerPlane );
+ } else {
+ u12io_ReadColorData( dev, dev->bufs.b1.pReadBuf,
+ dev->DataInf.dwAsicBytesPerPlane );
+ }
+ return SANE_FALSE;
+ }
+
+ if( dev->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+
+ dev->regs.RD_ModeControl = _ModeFifoGSel;
+ u12io_ReadMonoData( dev, buf, dev->DataInf.dwAsicBytesPerPlane );
+
+ } else {
+
+ if( !dev->scan.DataRead( dev )) {
+ return SANE_FALSE;
+ }
+ }
+
+ if( dev->scan.DoSample( dev )) {
+
+ /* direct is done here without copying...*/
+ if( fnDataDirect != dev->scan.DataProcess ) {
+ (*dev->scan.DataProcess)(dev, buf, (void*)(dev->scan.BufPut.red.bp),
+ dev->DataInf.dwAppPhyBytesPerLine);
+ }
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+/**
+ */
+static SANE_Status u12image_ReadOneImageLine( U12_Device *dev, void* buf )
+{
+ SANE_Byte b, state;
+ TimerDef timer, t2;
+
+ DBG( _DBG_READ, "u12image_ReadOneImageLine()\n" );
+
+ u12io_StartTimer( &timer, _LINE_TIMEOUT );
+ u12io_StartTimer( &t2, _SECOND*2 );
+ do {
+
+ state = u12io_GetScanState( dev );
+ dev->scan.bNowScanState = (state & _SCANSTATE_MASK);
+
+ if( state & _SCANSTATE_STOP ) {
+
+ DBG( _DBG_READ, "* SCANSTATE_STOP\n" );
+ u12motor_ModuleForwardBackward( dev );
+
+ if( u12io_GetFifoLength( dev ) >= dev->scan.dwMinReadFifo )
+ if( u12image_DataIsReady( dev, buf ))
+ return SANE_STATUS_GOOD;
+
+ } else {
+
+ dev->scan.bModuleState = _MotorInNormalState;
+ b = dev->scan.bNowScanState - dev->scan.oldScanState;
+
+ if((char) b < 0)
+ b += _NUMBER_OF_SCANSTEPS;
+
+ if( b >= dev->scan.bRefresh ) {
+
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+ dev->scan.oldScanState = u12io_GetScanState( dev );
+ dev->scan.oldScanState &= _SCANSTATE_MASK;
+ }
+
+ if( u12io_GetFifoLength( dev ) >= dev->scan.dwMaxReadFifo ) {
+
+ if( u12image_DataIsReady( dev, buf ))
+ return SANE_STATUS_GOOD;
+ }
+ else {
+
+ b = dev->scan.bNowScanState - dev->scan.oldScanState;
+
+ if((char) b < 0)
+ b += _NUMBER_OF_SCANSTEPS;
+
+ if( b >= dev->scan.bRefresh ) {
+
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+ dev->scan.oldScanState = u12io_GetScanState( dev );
+ dev->scan.oldScanState &= _SCANSTATE_MASK;
+ }
+
+ if( u12io_GetFifoLength( dev ) >= dev->scan.dwMinReadFifo ) {
+ if( u12image_DataIsReady( dev, buf ))
+ return SANE_STATUS_GOOD;
+ }
+ }
+ }
+
+ } while( !u12io_CheckTimer( &timer ));
+
+ DBG( _DBG_ERROR, "Timeout - Scanner malfunction !!\n" );
+ u12motor_ToHomePosition( dev, SANE_TRUE );
+
+ /* timed out, scanner malfunction */
+ return SANE_STATUS_IO_ERROR;
+}
+
+/**
+ */
+static void u12image_PrepareScaling( U12_Device *dev )
+{
+ int step;
+ double ratio;
+
+ dev->scaleBuf = NULL;
+ DBG( _DBG_INFO, "APP-DPIX=%u, MAX-DPIX=%u\n",
+ dev->DataInf.xyAppDpi.x, dev->dpi_max_x );
+
+ if( dev->DataInf.xyAppDpi.x > dev->dpi_max_x ) {
+
+ dev->scaleBuf = malloc( dev->DataInf.dwAppBytesPerLine );
+
+ ratio = (double)dev->DataInf.xyAppDpi.x/(double)dev->dpi_max_x;
+ dev->scaleIzoom = (int)(1.0/ratio * 1000);
+
+ switch( dev->DataInf.wAppDataType ) {
+
+ case COLOR_BW : step = 0; break;
+ case COLOR_256GRAY : step = 1; break;
+ case COLOR_TRUE24 : step = 3; break;
+ case COLOR_TRUE42 : step = 6; break;
+ default : step = 99; break;
+ }
+ dev->scaleStep = step;
+
+ DBG( _DBG_INFO, "u12image_PrepareScaling: izoom=%i, step=%u\n",
+ dev->scaleIzoom, step );
+ } else {
+
+ DBG( _DBG_INFO, "u12image_PrepareScaling: DISABLED\n" );
+ }
+}
+
+/** scaling picture data in x-direction, using a DDA algorithm
+ * (digital differential analyzer).
+ */
+static void u12image_ScaleX( U12_Device *dev, SANE_Byte *ib, SANE_Byte *ob )
+{
+ SANE_Byte tmp;
+ int ddax;
+ u_long i, j, x;
+
+ /* when not supported, only copy the data */
+ if( 99 == dev->scaleStep ) {
+ memcpy( ob, ib, dev->DataInf.dwAppBytesPerLine );
+ return;
+ }
+
+ /* now scale... */
+ if( 0 == dev->scaleStep ) {
+
+ /* binary scaling */
+ ddax = 0;
+ x = 0;
+ memset( ob, 0, dev->DataInf.dwAppBytesPerLine );
+
+ for( i = 0; i < dev->DataInf.dwPhysBytesPerLine*8; i++ ) {
+
+ ddax -= 1000;
+
+ while( ddax < 0 ) {
+
+ tmp = ib[(i>>3)];
+
+ if((x>>3) < dev->DataInf.dwAppBytesPerLine ) {
+ if( 0 != (tmp &= (1 << ((~(i & 0x7))&0x7))))
+ ob[x>>3] |= (1 << ((~(x & 0x7))&0x7));
+ }
+ x++;
+ ddax += dev->scaleIzoom;
+ }
+ }
+
+ } else {
+
+ /* color and gray scaling */
+ ddax = 0;
+ x = 0;
+ for( i = 0; i < dev->DataInf.dwPhysBytesPerLine*dev->scaleStep;
+ i+=dev->scaleStep ) {
+
+ ddax -= 1000;
+
+ while( ddax < 0 ) {
+
+ for( j = 0; j < (u_long)dev->scaleStep; j++ ) {
+
+ if((x+j) < dev->DataInf.dwAppBytesPerLine ) {
+ ob[x+j] = ib[i+j];
+ }
+ }
+ x += dev->scaleStep;
+ ddax += dev->scaleIzoom;
+ }
+ }
+ }
+}
+
+/* END U12_IMAGE.C ..........................................................*/
diff --git a/backend/u12-io.c b/backend/u12-io.c
new file mode 100644
index 0000000..cd65b72
--- /dev/null
+++ b/backend/u12-io.c
@@ -0,0 +1,851 @@
+/** @file u12-io.c
+ * @brief The I/O functions to the U12 backend stuff.
+ *
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ * GeneSys Logic I/O stuff derived from canon630u-common.c which has
+ * been written by Nathan Rutman <nathan@gordian.com>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - changed u12io_GetFifoLength() behaviour
+ * - added delays to reset function
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/** Format:
+ * cacheLen[0] = ASIC-ID
+ * cacheLen[1] = SCANSTATE ?
+ * cacheLen[2] = REG-STATUS ?
+ * cacheLen[3] = ??
+ * cacheLen[4] = FIFO-LEN (RED) HiByte LW
+ * cacheLen[5] = FIFO-LEN (RED) LoByte LW
+ * cacheLen[6] = FIFO-LEN (RED) LoByte HW
+ * cacheLen[7] = FIFO-LEN (GREEN) HiByte LW
+ * cacheLen[8] = FIFO-LEN (GREEN) LoByte LW
+ * cacheLen[9] = FIFO-LEN (GREEN) LoByte HW
+ * cacheLen[10] = FIFO-LEN (BLUE) HiByte LW
+ * cacheLen[11] = FIFO-LEN (BLUE) LoByte LW
+ * cacheLen[12] = FIFO-LEN (BLUE) LoByte HW
+ */
+static SANE_Byte cacheLen[13];
+
+/** This function is used to detect a cancel condition,
+ * our ESC key is the SIGUSR1 signal. It is sent by the backend when the
+ * cancel button has been pressed
+ *
+ * @param - none
+ * @return the function returns SANE_TRUE if a cancel condition has been
+ * detected, if not, it returns SANE_FALSE
+ */
+static SANE_Bool u12io_IsEscPressed( void )
+{
+ sigset_t sigs;
+
+ sigpending( &sigs );
+ if( sigismember( &sigs, SIGUSR1 )) {
+ DBG( _DBG_INFO, "SIGUSR1 is pending --> Cancel detected\n" );
+ return SANE_TRUE;
+ }
+
+ return SANE_FALSE;
+}
+
+/** fall asleep for some micro-seconds...
+ */
+static void u12io_udelay( unsigned long usec )
+{
+ struct timeval now, deadline;
+
+ if( usec == 0 )
+ return;
+
+ gettimeofday( &deadline, NULL );
+ deadline.tv_usec += usec;
+ deadline.tv_sec += deadline.tv_usec / 1000000;
+ deadline.tv_usec %= 1000000;
+
+ do {
+ gettimeofday( &now, NULL );
+ } while ((now.tv_sec < deadline.tv_sec) ||
+ (now.tv_sec == deadline.tv_sec && now.tv_usec < deadline.tv_usec));
+}
+
+/** Initializes a timer.
+ * @param timer - pointer to the timer to start
+ * @param us - timeout value in micro-seconds
+ */
+static void u12io_StartTimer( TimerDef *timer , unsigned long us )
+{
+ struct timeval start_time;
+
+ gettimeofday( &start_time, NULL );
+ *timer = start_time.tv_sec * 1e6 + start_time.tv_usec + us;
+}
+
+/** Checks if a timer has been expired or not.
+ * @param timer - pointer to the timer to check
+ * @return Function returns SANE_TRUE when the timer has been expired,
+ * otherwise SANE_FALSE
+ */
+static SANE_Bool u12io_CheckTimer( TimerDef *timer )
+{
+ struct timeval current_time;
+
+ gettimeofday(&current_time, NULL);
+
+ if((current_time.tv_sec * 1e6 + current_time.tv_usec) > *timer )
+ return SANE_TRUE;
+
+ return SANE_FALSE;
+}
+
+/* GL640 communication functions for Genesys Logic GL640USB
+ * USB-IEEE1284 parallel port bridge
+ */
+
+/* Assign status and verify a good return code */
+#define CHK(A) {if( (status = A) != SANE_STATUS_GOOD ) { \
+ DBG( _DBG_ERROR, "Failure on line of %s: %d\n", __FILE__, \
+ __LINE__ ); return A; }}
+
+/** Register codes for the bridge. These are NOT the registers for the ASIC
+ * on the other side of the bridge.
+ */
+typedef enum
+{
+ GL640_BULK_SETUP = 0x82,
+ GL640_EPP_ADDR = 0x83,
+ GL640_EPP_DATA_READ = 0x84,
+ GL640_EPP_DATA_WRITE = 0x85,
+ GL640_SPP_STATUS = 0x86,
+ GL640_SPP_CONTROL = 0x87,
+ GL640_SPP_DATA = 0x88,
+ GL640_GPIO_OE = 0x89,
+ GL640_GPIO_READ = 0x8a,
+ GL640_GPIO_WRITE = 0x8b
+} GL640_Request;
+
+/** for setting up bulk transfers */
+static SANE_Byte bulk_setup_data[] = { 0, 0x11, 0, 0, 0, 0, 0, 0 };
+
+/** Write to the usb-parallel port bridge.
+ */
+static SANE_Status
+gl640WriteControl(int fd, GL640_Request req, u_char * data, unsigned int size)
+{
+ SANE_Status status;
+
+ status = sanei_usb_control_msg( fd,
+ /* rqttype */ USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE | USB_DIR_OUT /*0x40 */ ,
+ /* rqt */ (size > 1) ? 0x04 : 0x0C,
+ /* val */ (SANE_Int) req,
+ /* ind */ 0,
+ /* len */ size,
+ /* dat */ data);
+
+ if( status != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, "gl640WriteControl error\n");
+ }
+ return status;
+}
+
+/** Read from the usb-parallel port bridge.
+ */
+static SANE_Status
+gl640ReadControl( int fd, GL640_Request req, u_char *data, unsigned int size )
+{
+ SANE_Status status;
+
+ status = sanei_usb_control_msg( fd,
+ /* rqttype */ USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE | USB_DIR_IN /*0xc0 */ ,
+ /* rqt */ (size > 1) ? 0x04 : 0x0C,
+ /* val */ (SANE_Int) req,
+ /* ind */ 0,
+ /* len */ size,
+ /* dat */ data);
+
+ if( status != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, "gl640ReadControl error\n");
+ }
+ return status;
+}
+
+/** Wrappers to write a single byte to the bridge */
+static inline SANE_Status
+gl640WriteReq( int fd, GL640_Request req, u_char data )
+{
+ return gl640WriteControl( fd, req, &data, 1);
+}
+
+/** Wrappers to read a single byte from the bridge */
+static inline SANE_Status
+gl640ReadReq( int fd, GL640_Request req, u_char *data )
+{
+ return gl640ReadControl( fd, req, data, 1 );
+}
+
+/** Write USB bulk data
+ * setup is an apparently scanner-specific sequence:
+ * {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00}
+ * setup[1] = 0x11 --> data to register
+ * setup[1] = 0x01 --> data to scanner memory
+ */
+static SANE_Status
+gl640WriteBulk( int fd, u_char *setup, u_char *data, size_t size )
+{
+ SANE_Status status;
+
+ setup[0] = 1;
+ setup[4] = (size) & 0xFF;
+ setup[5] = (size >> 8) & 0xFF;
+ setup[6] = 0;
+
+ CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8));
+
+ status = sanei_usb_write_bulk (fd, data, &size);
+ if( status != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, "gl640WriteBulk error\n");
+ }
+ return status;
+}
+
+/** Read USB bulk data
+ * setup is an apparently scanner-specific sequence:
+ * {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00}
+ * setup[1] = 0x00 --> data from scanner memory
+ * setup[1] = 0x0c --> data from scanner fifo?
+ */
+static SANE_Status
+gl640ReadBulk( int fd, u_char *setup, u_char *data, size_t size, int mod )
+{
+ SANE_Byte *len_info;
+ size_t complete, current, toget;
+ SANE_Status status;
+
+ setup[0] = 0;
+ setup[4] = (size) & 0xFF;
+ setup[5] = (size >> 8) & 0xFF;
+ setup[6] = mod;
+
+ CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8));
+
+ len_info = NULL;
+ toget = size;
+ if( mod ) {
+ toget *= mod;
+ len_info = data + toget;
+ toget += 13;
+ }
+
+ for( complete = 0; complete < toget; ) {
+
+ current = toget - complete;
+ status = sanei_usb_read_bulk( fd, data, &current );
+ if( status != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, "gl640ReadBulk error\n");
+ break;
+ }
+ data += current;
+ complete += current;
+ }
+ if( len_info ) {
+ memcpy( cacheLen, len_info, 13 );
+ }
+ return status;
+}
+
+/* now the functions to access PP registers */
+
+/** read the contents of the status register */
+static SANE_Byte
+inb_status( int fd )
+{
+ u_char data = 0xff;
+
+ gl640ReadReq( fd, GL640_SPP_STATUS, &data );
+ return data;
+}
+
+/** write a byte to the SPP data port */
+static SANE_Status
+outb_data( int fd, u_char data )
+{
+ return gl640WriteReq( fd, GL640_SPP_DATA, data);
+}
+
+/** write to the parport control register */
+static SANE_Status
+outb_ctrl( int fd, u_char data )
+{
+ return gl640WriteReq( fd, GL640_SPP_CONTROL, data);
+}
+
+/************************* ASIC access stuff *********************************/
+
+/** write a register number to the ASIC
+ */
+static void u12io_RegisterToScanner( U12_Device *dev, SANE_Byte reg )
+{
+ if( dev->mode == _PP_MODE_EPP ) {
+
+ gl640WriteReq( dev->fd, GL640_EPP_ADDR, reg );
+
+ } else {
+
+ /* write register number to read from to SPP data-port
+ */
+ outb_data( dev->fd, reg );
+
+ /* signal that to the ASIC */
+ outb_ctrl( dev->fd, _CTRL_SIGNAL_REGWRITE );
+ _DODELAY(20);
+ outb_ctrl( dev->fd, _CTRL_END_REGWRITE );
+ }
+}
+
+/** as the name says, we switch to SPP mode
+ */
+static void u12io_SwitchToSPPMode( U12_Device *dev )
+{
+ dev->mode = _PP_MODE_SPP;
+ outb_ctrl( dev->fd, _CTRL_GENSIGNAL );
+}
+
+/** as the name says, we switch to SPP mode
+ */
+static void u12io_SwitchToEPPMode( U12_Device *dev )
+{
+ u12io_RegisterToScanner( dev, REG_EPPENABLE );
+ dev->mode = _PP_MODE_EPP;
+}
+
+/** read data from SPP status port
+ */
+static SANE_Byte u12io_DataFromSPP( U12_Device *dev )
+{
+ SANE_Byte data, tmp;
+
+ /* read low nibble */
+ tmp = inb_status( dev->fd );
+
+ outb_ctrl( dev->fd, (_CTRL_GENSIGNAL + _CTRL_STROBE));
+
+ /* read high nibble */
+ data = inb_status( dev->fd );
+ data &= 0xf0;
+
+ /* combine with low nibble */
+ data |= (tmp >> 4);
+ return data;
+}
+
+/** Read the content of specific ASIC register
+ */
+static SANE_Byte u12io_DataFromRegister( U12_Device *dev, SANE_Byte reg )
+{
+ SANE_Byte val;
+
+ if( dev->mode == _PP_MODE_EPP ) {
+ gl640WriteReq( dev->fd, GL640_EPP_ADDR, reg );
+ gl640ReadReq ( dev->fd, GL640_EPP_DATA_READ, &val );
+ } else {
+
+ u12io_RegisterToScanner( dev, reg );
+ val = u12io_DataFromSPP( dev );
+ }
+ return val;
+}
+
+/**
+ */
+static void u12io_CloseScanPath( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "u12io_CloseScanPath()\n" );
+/* FIXME: Probaly not needed */
+#if 0
+ u12io_RegisterToScanner( dev, 0xff );
+#endif
+ u12io_RegisterToScanner( dev, REG_SWITCHBUS );
+
+ dev->mode = _PP_MODE_SPP;
+}
+
+/** try to connect to scanner
+ */
+static SANE_Bool u12io_OpenScanPath( U12_Device *dev )
+{
+ u_char tmp;
+
+ DBG( _DBG_INFO, "u12io_OpenScanPath()\n" );
+
+ u12io_SwitchToSPPMode( dev );
+
+ outb_data( dev->fd, _ID_TO_PRINTER );
+ _DODELAY(20);
+
+ outb_data( dev->fd, _ID1ST );
+ _DODELAY(5);
+
+ outb_data( dev->fd, _ID2ND );
+ _DODELAY(5);
+
+ outb_data( dev->fd, _ID3RD );
+ _DODELAY(5);
+
+ outb_data( dev->fd, _ID4TH );
+ _DODELAY(5);
+
+ tmp = u12io_DataFromRegister( dev, REG_ASICID );
+ if( ASIC_ID == tmp ) {
+ u12io_SwitchToEPPMode( dev );
+ return SANE_TRUE;
+ }
+
+ DBG( _DBG_ERROR, "u12io_OpenScanPath() failed!\n" );
+ return SANE_FALSE;
+}
+
+/** Write data to asic (SPP mode only)
+ */
+static void u12io_DataToScanner( U12_Device *dev , SANE_Byte bValue )
+{
+ if( dev->mode != _PP_MODE_SPP ) {
+ DBG( _DBG_ERROR, "u12io_DataToScanner() in wrong mode!\n" );
+ return;
+ }
+
+ /* output data */
+ outb_data( dev->fd, bValue );
+
+ /* notify asic there is data */
+ outb_ctrl( dev->fd, _CTRL_SIGNAL_DATAWRITE );
+
+ /* end write cycle */
+ outb_ctrl( dev->fd, _CTRL_END_DATAWRITE );
+}
+
+/** Write data to specific ASIC's register
+ */
+static SANE_Status u12io_DataToRegister( U12_Device *dev,
+ SANE_Byte reg, SANE_Byte data )
+{
+ SANE_Status status;
+ SANE_Byte buf[2];
+
+ if( dev->mode == _PP_MODE_EPP ) {
+
+ buf[0] = reg;
+ buf[1] = data;
+
+ bulk_setup_data[1] = 0x11;
+ CHK( gl640WriteBulk ( dev->fd, bulk_setup_data, buf, 2 ));
+
+ } else {
+
+ u12io_RegisterToScanner( dev, reg );
+ u12io_DataToScanner( dev, data );
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** Write data-buffer to specific ASIC's register
+ * The format in the buffer is
+ * reg(0),val(0),reg(1),val(1),..., reg(len-1),val(len-1)
+ */
+static SANE_Status u12io_DataToRegs( U12_Device *dev, SANE_Byte *buf, int len )
+{
+ SANE_Status status;
+
+ if( dev->mode != _PP_MODE_EPP ) {
+ DBG( _DBG_ERROR, "u12io_DataToRegs() in wrong mode!\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ bulk_setup_data[1] = 0x11;
+ CHK( gl640WriteBulk ( dev->fd, bulk_setup_data, buf, len*2 ));
+ return SANE_STATUS_GOOD;
+}
+
+/** write data to the DAC
+ */
+static void
+u12io_DataRegisterToDAC( U12_Device *dev, SANE_Byte reg, SANE_Byte val )
+{
+ SANE_Byte buf[6];
+
+ buf[0] = REG_ADCADDR;
+ buf[1] = reg;
+ buf[2] = REG_ADCDATA;
+ buf[3] = val;
+ buf[4] = REG_ADCSERIALOUT;
+ buf[5] = val;
+
+ u12io_DataToRegs( dev, buf, 3 );
+}
+
+/** write data block to scanner
+ */
+static SANE_Status u12io_MoveDataToScanner( U12_Device *dev,
+ SANE_Byte *buf, int len )
+{
+ SANE_Status status;
+
+/* u12io_RegisterToScanner( dev, REG_INITDATAFIFO ); */
+ u12io_RegisterToScanner( dev, REG_WRITEDATAMODE );
+
+ bulk_setup_data[1] = 0x01;
+ CHK( gl640WriteBulk( dev->fd, bulk_setup_data, buf, len ));
+ bulk_setup_data[1] = 0x11;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status u12io_ReadData( U12_Device *dev, SANE_Byte *buf, int len )
+{
+ SANE_Status status;
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, dev->regs.RD_ModeControl );
+/* u12io_RegisterToScanner( dev, REG_INITDATAFIFO ); */
+ u12io_RegisterToScanner( dev, REG_READDATAMODE );
+
+ bulk_setup_data[1] = 0x00;
+ CHK (gl640ReadBulk( dev->fd, bulk_setup_data, buf, len, 0 ));
+ bulk_setup_data[1] = 0x11;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** perform a SW reset of ASIC P98003
+ */
+static void u12io_SoftwareReset( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "Device reset (%i)!!!\n", dev->fd );
+
+ u12io_DataToRegister( dev, REG_TESTMODE, _SW_TESTMODE );
+
+ outb_data( dev->fd, _ID_TO_PRINTER );
+ _DODELAY(20);
+
+ outb_data( dev->fd, _RESET1ST );
+ _DODELAY(5);
+ outb_data( dev->fd, _RESET2ND );
+ _DODELAY(5);
+ outb_data( dev->fd, _RESET3RD );
+ _DODELAY(5);
+ outb_data( dev->fd, _RESET4TH );
+ _DODELAY(250);
+}
+
+/**
+ */
+static SANE_Bool u12io_IsConnected( U12_Device *dev )
+{
+ int c, mode;
+ SANE_Byte tmp, rb[6];
+
+ DBG( _DBG_INFO, "u12io_IsConnected()\n" );
+ tmp = inb_status( dev->fd );
+ DBG( _DBG_INFO, "* tmp1 = 0x%02x\n", tmp );
+
+ gl640WriteReq( dev->fd, GL640_EPP_ADDR, REG_ASICID );
+ gl640ReadReq ( dev->fd, GL640_EPP_DATA_READ, &tmp );
+ DBG( _DBG_INFO, "* REG_ASICID = 0x%02x\n", tmp );
+
+ if( tmp != ASIC_ID ) {
+
+ DBG( _DBG_INFO, "* Scanner is NOT connected!\n" );
+
+ tmp = inb_status( dev->fd );
+ DBG( _DBG_INFO, "* tmp2 = 0x%02x\n", tmp );
+
+ gl640WriteReq( dev->fd, GL640_EPP_ADDR, REG_ASICID );
+ gl640ReadReq ( dev->fd, GL640_EPP_DATA_READ, &tmp );
+ DBG( _DBG_INFO, "* REG_ASICID = 0x%02x\n", tmp );
+
+ if( tmp == 0x02 ) {
+
+ mode = dev->mode;
+ dev->mode = _PP_MODE_EPP;
+ u12io_DataToRegister( dev, REG_ADCADDR, 0x01 );
+ u12io_DataToRegister( dev, REG_ADCDATA, 0x00 );
+ u12io_DataToRegister( dev, REG_ADCSERIALOUT, 0x00 );
+
+ c = 0;
+ _SET_REG( rb, c, REG_MODECONTROL, 0x19 );
+ _SET_REG( rb, c, REG_STEPCONTROL, 0xff );
+ _SET_REG( rb, c, REG_MOTOR0CONTROL, 0 );
+ u12io_DataToRegs( dev, rb, c );
+ dev->mode = mode ;
+ }
+ return SANE_FALSE;
+ }
+
+ u12io_SwitchToEPPMode( dev );
+ DBG( _DBG_INFO, "* Scanner is connected!\n" );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static SANE_Byte u12io_GetExtendedStatus( U12_Device *dev )
+{
+ SANE_Byte b;
+
+ b = u12io_DataFromRegister( dev, REG_STATUS2 );
+ if( b == 0xff )
+ return 0;
+ return b;
+}
+
+/**
+ */
+static SANE_Status u12io_ReadMonoData( U12_Device *dev, SANE_Byte *buf, u_long len )
+{
+ SANE_Status status;
+
+ bulk_setup_data[1] = 0x0c;
+ bulk_setup_data[2] = ((dev->regs.RD_ModeControl >> 3) + 1);
+
+ CHK (gl640ReadBulk( dev->fd, bulk_setup_data, buf, len, 1 ));
+ bulk_setup_data[1] = 0x11;
+ bulk_setup_data[2] = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status
+u12io_ReadColorData( U12_Device *dev, SANE_Byte *buf, u_long len )
+{
+ SANE_Status status;
+
+ bulk_setup_data[1] = 0x0c;
+
+ CHK (gl640ReadBulk( dev->fd, bulk_setup_data, buf, len, 3 ));
+ bulk_setup_data[1] = 0x11;
+ return SANE_STATUS_GOOD;
+}
+
+/** read the recent state count
+ */
+static SANE_Byte u12io_GetScanState( U12_Device *dev )
+{
+ if( cacheLen[0] == 0x83 ) {
+ DBG( _DBG_READ, "u12io_GetScanState(cached) = 0x%02x\n", cacheLen[1] );
+ return cacheLen[1];
+ }
+ return u12io_DataFromRegister( dev, REG_GETSCANSTATE );
+}
+
+/** download a scanstate-table
+ */
+static SANE_Status u12io_DownloadScanStates( U12_Device *dev )
+{
+ SANE_Status status;
+ TimerDef timer;
+
+ u12io_RegisterToScanner( dev, REG_SCANSTATECONTROL );
+
+ bulk_setup_data[1] = 0x01;
+ CHK( gl640WriteBulk( dev->fd, bulk_setup_data,
+ dev->scanStates, _SCANSTATE_BYTES ));
+ bulk_setup_data[1] = 0x11;
+
+/* FIXME: refreshState probably always FALSE */
+ if( dev->scan.refreshState ) {
+
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+
+ u12io_StartTimer( &timer, (_SECOND/2));
+ do {
+
+ if (!( u12io_GetScanState( dev ) & _SCANSTATE_STOP))
+ break;
+ }
+ while( !u12io_CheckTimer(&timer));
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** - initializes the scan states
+ * - sets all necessary registers
+ * FIXME: first copy to buffer, then use u12io_DataToRegs()
+ */
+static void u12io_PutOnAllRegisters( U12_Device *dev )
+{
+ SANE_Byte *val, reg;
+ SANE_Byte *rb, buf[100];
+ int c;
+
+ /* setup scan states */
+ u12io_DownloadScanStates( dev );
+
+ c = 0;
+ rb = buf;
+
+ *(rb++) = REG_MODECONTROL;
+ *(rb++) = dev->regs.RD_ModeControl;
+ c++;
+ *(rb++) = REG_STEPCONTROL;
+ *(rb++) = dev->regs.RD_StepControl;
+ c++;
+ *(rb++) = REG_MOTOR0CONTROL;
+ *(rb++) = dev->regs.RD_Motor0Control;
+ c++;
+ *(rb++) = REG_LINECONTROL;
+ *(rb++) = dev->regs.RD_LineControl;
+ c++;
+ *(rb++) = REG_XSTEPTIME;
+ *(rb++) = dev->regs.RD_XStepTime;
+ c++;
+ *(rb++) = REG_MODELCONTROL;
+ *(rb++) = dev->regs.RD_ModelControl;
+ c++;
+ /* the 1st register to write */
+ val = (SANE_Byte*)&dev->regs.RD_Dpi;
+
+ /* 0x21 - 0x28 */
+ for( reg = REG_DPILO; reg <= REG_THRESHOLDHI; reg++, val++ ) {
+ *(rb++) = reg;
+ *(rb++) = *val;
+ c++;
+ }
+
+ u12io_DataToRegs( dev, buf, c );
+
+ u12io_RegisterToScanner( dev, REG_INITDATAFIFO );
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeScan );
+}
+
+/**
+ */
+static void u12io_ResetFifoLen( void )
+{
+ memset( cacheLen, 0, 13 );
+}
+
+/**
+ */
+static u_long u12io_GetFifoLength( U12_Device *dev )
+{
+ SANE_Status status;
+ size_t toget;
+ SANE_Byte data[64];
+ u_long len, len_r, len_g, len_b;
+
+ if( cacheLen[0] == 0x83 ) {
+
+ DBG( _DBG_READ, "Using cached FIFO len\n" );
+ memcpy( data, cacheLen, 13 );
+ u12io_ResetFifoLen();
+
+ } else {
+
+ memset( bulk_setup_data, 0, 8 );
+ bulk_setup_data[1] = 0x0c;
+
+ CHK (gl640WriteControl(dev->fd, GL640_BULK_SETUP, bulk_setup_data, 8));
+
+ toget = 13;
+ status = sanei_usb_read_bulk( dev->fd, data, &toget );
+ if( status != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, "ReadBulk error\n");
+ return SANE_FALSE;
+ }
+ bulk_setup_data[1] = 0x11;
+
+ memcpy( cacheLen, data, 13 );
+ }
+ len_r = (u_long)data[5] * 256 + (u_long)data[4];
+ len_g = (u_long)data[8] * 256 + (u_long)data[7];
+ len_b = (u_long)data[11] * 256 + (u_long)data[10];
+
+ if( dev->DataInf.wPhyDataType < COLOR_TRUE24 ) {
+ len = len_g;
+ } else {
+
+ len = len_r;
+ if( len_g < len )
+ len = len_g;
+ if( len_b < len )
+ len = len_b;
+ }
+
+ DBG( _DBG_READ, "FIFO-LEN: %lu %lu %lu = %lu\n", len_r, len_g, len_b, len );
+ return len;
+}
+
+/**
+ */
+static SANE_Bool
+u12io_ReadOneShadingLine( U12_Device *dev, SANE_Byte *buf, u_long len )
+{
+ TimerDef timer;
+ SANE_Status status;
+
+ DBG( _DBG_READ, "u12io_ReadOneShadingLine()\n" );
+ u12io_StartTimer( &timer, _SECOND );
+
+ dev->scan.bFifoSelect = REG_GFIFOOFFSET;
+
+ do {
+ u12io_ResetFifoLen();
+ if( u12io_GetFifoLength( dev ) >= dev->regs.RD_Pixels ) {
+
+ status = u12io_ReadColorData( dev, buf, len );
+ if( status != SANE_STATUS_GOOD ) {
+ DBG( _DBG_ERROR, "ReadColorData error\n");
+ return SANE_FALSE;
+ }
+ DBG( _DBG_READ, "* done\n" );
+ return SANE_TRUE;
+ }
+ } while( !u12io_CheckTimer( &timer ));
+
+ DBG( _DBG_ERROR, "u12io_ReadOneShadingLine() failed!\n" );
+ return SANE_FALSE;
+}
+
+/* END U12-IO.C .............................................................*/
diff --git a/backend/u12-map.c b/backend/u12-map.c
new file mode 100644
index 0000000..7d2f84a
--- /dev/null
+++ b/backend/u12-map.c
@@ -0,0 +1,192 @@
+/* @file u12_map.c
+ * @brief functions to create and manipulate gamma lookup tables.
+ *
+ * Copyright (C) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - inverting map when scanning binary images
+ * - changed u12map_Adjust()
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/**
+ * @param s - pointer to the scanner specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static SANE_Status u12map_InitGammaSettings( U12_Device *dev )
+{
+ int i, j, val;
+ double gamma;
+
+ dev->gamma_length = 4096;
+ dev->gamma_range.min = 0;
+ dev->gamma_range.max = 255;
+ dev->gamma_range.quant = 0;
+
+ DBG( _DBG_INFO, "Presetting Gamma tables (len=%u)\n", dev->gamma_length );
+ DBG( _DBG_INFO, "----------------------------------\n" );
+
+ /* preset the gamma maps */
+ for( i = 0; i < 4; i++ ) {
+
+ switch( i ) {
+ case 1: gamma = dev->adj.rgamma; break;
+ case 2: gamma = dev->adj.ggamma; break;
+ case 3: gamma = dev->adj.bgamma; break;
+ default: gamma = dev->adj.graygamma; break;
+ }
+
+ for( j = 0; j < dev->gamma_length; j++ ) {
+
+ val = (dev->gamma_range.max *
+ pow((double) j / ((double)dev->gamma_length - 1.0),
+ 1.0 / gamma ));
+
+ if( val > dev->gamma_range.max )
+ val = dev->gamma_range.max;
+
+ dev->gamma_table[i][j] = val;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** Check the gamma vectors we got back and limit if necessary
+ * @param s - pointer to the scanner specific structure
+ * @return nothing
+ */
+static void u12map_CheckGammaSettings( U12_Device *dev )
+{
+ int i, j;
+
+ for( i = 0; i < 4 ; i++ ) {
+ for( j = 0; j < dev->gamma_length; j++ ) {
+ if( dev->gamma_table[i][j] > dev->gamma_range.max ) {
+ dev->gamma_table[i][j] = dev->gamma_range.max;
+ }
+ }
+ }
+}
+
+/** adjust acording to brightness and contrast
+ */
+static void u12map_Adjust( U12_Device *dev, int which, SANE_Byte *buf )
+{
+ int i;
+ u_long *pdw;
+ double b, c, tmp;
+
+ DBG( _DBG_INFO, "u12map_Adjust(%u)\n", which );
+
+ /* adjust brightness (b) and contrast (c) using the function:
+ *
+ * s'(x,y) = (s(x,y) + b) * c
+ * b = [-127, 127]
+ * c = [0,2]
+ */
+
+ /* scale brightness and contrast...
+ */
+ b = ((double)dev->DataInf.siBrightness * 192.0)/100.0;
+ c = ((double)dev->DataInf.siContrast + 100.0)/100.0;
+
+ DBG( _DBG_INFO, "* brightness = %i -> %i\n",
+ dev->DataInf.siBrightness, (SANE_Byte)b);
+ DBG( _DBG_INFO, "* contrast*100 = %i -> %i\n",
+ dev->DataInf.siContrast, (int)(c*100));
+
+ for( i = 0; i < dev->gamma_length; i++ ) {
+
+ if((_MAP_MASTER == which) || (_MAP_RED == which)) {
+ tmp = ((double)(dev->gamma_table[0][i] + b)) * c;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ buf[i] = (SANE_Byte)tmp;
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_GREEN == which)) {
+ tmp = ((double)(dev->gamma_table[1][i] + b)) * c;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ buf[4096+i] = (SANE_Byte)tmp;
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_BLUE == which)) {
+ tmp = ((double)(dev->gamma_table[2][i] + b)) * c;
+ if( tmp < 0 ) tmp = 0;
+ if( tmp > 255 ) tmp = 255;
+ buf[8192+i] = (SANE_Byte)tmp;
+ }
+ }
+
+ if((dev->DataInf.dwScanFlag & _SCANDEF_Negative) ||
+ (dev->DataInf.wPhyDataType == COLOR_BW)) {
+ DBG( _DBG_INFO, "inverting...\n" );
+
+ if((_MAP_MASTER == which) || (_MAP_RED == which)) {
+
+ DBG( _DBG_INFO, "inverting RED map\n" );
+ pdw = (u_long*)&buf[0];
+ for( i = dev->gamma_length / 4; i; i--, pdw++ )
+ *pdw = ~(*pdw);
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_GREEN == which)) {
+
+ DBG( _DBG_INFO, "inverting GREEN map\n" );
+ pdw = (u_long*)&buf[4096];
+ for( i = dev->gamma_length / 4; i; i--, pdw++ )
+ *pdw = ~(*pdw);
+ }
+
+ if((_MAP_MASTER == which) || (_MAP_BLUE == which)) {
+
+ DBG( _DBG_INFO, "inverting BLUE map\n" );
+ pdw = (u_long*)&buf[8192];
+ for( i = dev->gamma_length / 4; i; i--, pdw++ )
+ *pdw = ~(*pdw);
+ }
+ }
+}
+
+/* END U12-MAP.C ............................................................*/
diff --git a/backend/u12-motor.c b/backend/u12-motor.c
new file mode 100644
index 0000000..c3d5120
--- /dev/null
+++ b/backend/u12-motor.c
@@ -0,0 +1,519 @@
+/* @file u12-motor.c
+ * @brief all functions for motor control
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - no changes
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/*************************** some definitons *********************************/
+
+#define _BACKSTEPS 120
+#define _FORWARDSTEPS 120
+
+/**************************** local vars *************************************/
+
+static TimerDef u12motor_Timer;
+
+
+/*************************** local functions *********************************/
+
+/**
+ */
+static void u12motor_DownloadNullScanStates( U12_Device *dev )
+{
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+ u12io_DownloadScanStates( dev );
+}
+
+/**
+ */
+static void u12motor_Force16Steps( U12_Device *dev, int dir )
+{
+ u_long dw;
+
+ if( dir == _DIR_FW )
+ u12io_DataToRegister( dev, REG_MOTOR0CONTROL, _FORWARD_MOTOR );
+ else if( dir == _DIR_BW )
+ u12io_DataToRegister( dev, REG_MOTOR0CONTROL, _BACKWARD_MOTOR );
+
+ for( dw = 16; dw; dw-- ) {
+ u12io_RegisterToScanner( dev, REG_FORCESTEP );
+ _DODELAY( 10 );
+ }
+}
+
+/**
+ */
+static void u12motor_ModuleFreeRun( U12_Device *dev, u_long steps )
+{
+ SANE_Byte rb[6];
+
+ rb[0] = REG_MOTORFREERUNCOUNT1; rb[1] = _HIBYTE(steps);
+ rb[2] = REG_MOTORFREERUNCOUNT0; rb[3] = _LOBYTE(steps);
+ rb[4] = REG_MOTORFREERUNTRIGGER; rb[5] = 0;
+
+ u12io_DataToRegs( dev, rb, 3 );
+}
+
+/**
+ */
+static SANE_Status u12motor_PositionYProc( U12_Device *dev, u_long steps )
+{
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "u12motor_PositionYProc()\n" );
+ u12io_StartTimer( &timer, _SECOND * 5 );
+
+ u12io_ResetFifoLen();
+ while(!(u12io_GetScanState( dev ) & _SCANSTATE_STOP) &&
+ (!u12io_CheckTimer( &timer )));
+ _DODELAY( 12 );
+ u12motor_ModuleFreeRun( dev, steps );
+ _DODELAY( 15 );
+
+ u12io_StartTimer( &timer, _SECOND * 30 );
+ do {
+ if( !(u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING)) {
+ break;
+ }
+
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ } while( !u12io_CheckTimer( &timer ));
+ DBG( _DBG_INFO, "u12motor_PositionYProc() - done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** initialize this module and setup the correct function pointer according
+ * to the ASIC
+ */
+static void u12motor_PositionModuleToHome( U12_Device *dev )
+{
+ SANE_Byte rb[50];
+ SANE_Byte save, saveModel;
+ int c = 0;
+
+ DBG( _DBG_INFO, "u12motor_PositionModuleToHome()\n" );
+ saveModel = dev->regs.RD_ModelControl;
+
+ dev->scan.refreshState = SANE_FALSE;
+ u12motor_DownloadNullScanStates( dev );
+
+ _DODELAY( 125 );
+ save = dev->shade.intermediate;
+
+ dev->shade.intermediate = _ScanMode_AverageOut;
+ u12hw_InitAsic( dev, SANE_FALSE );
+ dev->shade.intermediate = save;
+
+ _SET_REG( rb, c, REG_MODECONTROL, _ModeScan );
+ _SET_REG( rb, c, REG_RESETMTSC, 0 );
+ _SET_REG( rb, c, REG_SCANCONTROL1, 0 );
+ _SET_REG( rb, c, REG_MODELCONTROL, (dev->ModelCtrl | _MODEL_DPI300));
+ _SET_REG( rb, c, REG_LINECONTROL, 80 );
+ _SET_REG( rb, c, REG_XSTEPTIME, dev->XStepBack );
+ _SET_REG( rb, c, REG_MOTORDRVTYPE, dev->MotorPower );
+ _SET_REG( rb, c, REG_MOTOR0CONTROL, (_MotorHHomeStop | _MotorOn |
+ _MotorHQuarterStep | _MotorPowerEnable));
+ _SET_REG( rb, c, REG_STEPCONTROL, (_MOTOR0_SCANSTATE | _MOTOR_FREERUN));
+ u12io_DataToRegs( dev, rb, c );
+
+ memset( dev->scanStates, 0x88, _SCANSTATE_BYTES );
+ u12io_DownloadScanStates( dev );
+
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+ dev->regs.RD_ModelControl = saveModel;
+}
+
+/** function to bring the sensor back home
+ */
+static void u12motor_ToHomePosition( U12_Device *dev, SANE_Bool wait )
+{
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "Waiting for Sensor to be back in position\n" );
+ if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {
+
+ u12motor_PositionModuleToHome( dev );
+
+ if( wait ) {
+ u12io_StartTimer( &timer, _SECOND * 20);
+ do {
+ if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)
+ break;
+ } while( !u12io_CheckTimer( &timer ));
+ }
+ }
+ DBG( _DBG_INFO, "- done !\n" );
+}
+
+/**
+ */
+static SANE_Status u12motor_BackToHomeSensor( U12_Device *dev )
+{
+ SANE_Byte rb[20];
+ int c;
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "u12Motor_BackToHomeSensor()\n" );
+
+ c = 0;
+ _SET_REG( rb, c, REG_STEPCONTROL, _MOTOR0_SCANSTATE );
+ _SET_REG( rb, c, REG_MODECONTROL, _ModeScan );
+
+ u12io_DataToRegs( dev, rb, c );
+
+ u12motor_Force16Steps( dev, _DIR_NONE );
+
+ /* stepping every state */
+ memset( dev->scanStates, 0x88, _SCANSTATE_BYTES );
+ u12io_DownloadScanStates( dev );
+ _DODELAY(50);
+
+ u12io_StartTimer( &timer, _SECOND * 2 );
+
+ u12io_ResetFifoLen();
+ while(!(u12io_GetScanState( dev ) & _SCANSTATE_STOP) &&
+ !u12io_CheckTimer( &timer )) {
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+ }
+
+ u12motor_Force16Steps( dev, _DIR_BW );
+ dev->regs.RD_ModeControl = _ModeScan;
+
+ c = 0;
+
+ if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA)) {
+ _SET_REG( rb, c, REG_LINECONTROL, _LOBYTE(dev->shade.wExposure));
+ _SET_REG( rb, c, REG_XSTEPTIME, _LOBYTE(dev->shade.wXStep));
+ } else {
+ _SET_REG( rb, c, REG_LINECONTROL, _DEFAULT_LINESCANTIME );
+ _SET_REG( rb, c, REG_XSTEPTIME, 6 );
+ }
+
+ _SET_REG( rb, c, REG_STEPCONTROL, (_MOTOR_FREERUN | _MOTOR0_SCANSTATE));
+ _SET_REG( rb, c, REG_MOTOR0CONTROL,
+ (_MotorHQuarterStep | _MotorOn | _MotorDirBackward |
+ _MotorPowerEnable | _MotorHHomeStop));
+ _SET_REG( rb, c, REG_REFRESHSCANSTATE, 0);
+ u12io_DataToRegs( dev, rb, c );
+
+ u12io_StartTimer( &timer, _SECOND * 5 );
+ do {
+ if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER )
+ break;
+
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ _DODELAY( 55 );
+
+ } while( !u12io_CheckTimer( &timer ));
+
+ c = 0;
+ _SET_REG( rb, c, REG_LINECONTROL, dev->regs.RD_LineControl);
+ _SET_REG( rb, c, REG_XSTEPTIME, dev->regs.RD_XStepTime);
+ u12io_DataToRegs( dev, rb, c );
+
+ DBG( _DBG_INFO, "* LineCtrl=0x%02x, XStepTime=0x%02x\n",
+ dev->regs.RD_LineControl, dev->regs.RD_XStepTime );
+
+ u12motor_DownloadNullScanStates( dev );
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status u12motor_ModuleToHome( U12_Device *dev )
+{
+ SANE_Status res;
+
+ DBG( _DBG_INFO, "u12motor_ModuleToHome()\n" );
+ if(!(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {
+
+ u12io_DataToRegister( dev, REG_MOTOR0CONTROL,
+ (SANE_Byte)(dev->regs.RD_Motor0Control|_MotorDirForward));
+
+ res = u12motor_PositionYProc( dev, 40 );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ res = u12motor_BackToHomeSensor( dev );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ _DODELAY( 250 );
+ }
+ DBG( _DBG_INFO, "* done.\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static SANE_Status u12motor_WaitForPositionY( U12_Device *dev )
+{
+ SANE_Byte rb[20];
+ SANE_Status res;
+ SANE_Byte bXStep;
+ int c;
+ u_long dwBeginY;
+
+ c = 0;
+ dwBeginY = (u_long)dev->DataInf.crImage.y * 4 + dev->scan.dwScanOrigin;
+
+ if( dev->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+ if( dev->f0_8_16 )
+ dwBeginY += 16;
+ else
+ dwBeginY += 8;
+ }
+
+ bXStep = (SANE_Byte)((dev->DataInf.wPhyDataType <= COLOR_256GRAY) ?
+ dev->XStepMono : dev->XStepColor);
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ bXStep = 8;
+
+ u12motor_Force16Steps( dev, _DIR_NONE );
+ dwBeginY -= 16;
+
+ if( dwBeginY > (_RFT_SCANNING_ORG + _YOFFSET) &&
+ bXStep < dev->regs.RD_XStepTime ) {
+
+ u12io_DataToRegister( dev, REG_MOTORDRVTYPE, dev->MotorPower );
+ _DODELAY( 12 );
+ u12io_DataToRegister( dev, REG_XSTEPTIME, bXStep);
+ u12io_DataToRegister( dev, REG_EXTENDEDXSTEP, 0 );
+ u12io_DataToRegister( dev, REG_SCANCONTROL1,
+ (SANE_Byte)(dev->regs.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
+ res = u12motor_PositionYProc( dev, dwBeginY - 64 );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+ dwBeginY = 64;
+ } else {
+ _SET_REG(rb, c, REG_SCANCONTROL1, dev->regs.RD_ScanControl1 );
+ }
+
+ _SET_REG( rb, c, REG_FIFOFULLEN0, _LOBYTE(dev->regs.RD_BufFullSize));
+ _SET_REG( rb, c, REG_FIFOFULLEN1, _HIBYTE(dev->regs.RD_BufFullSize));
+ _SET_REG( rb, c, REG_FIFOFULLEN2, _LOBYTE(_HIWORD(dev->regs.RD_BufFullSize)));
+ u12io_DataToRegs( dev, rb, c );
+
+ u12io_DataToRegister( dev, REG_MOTORDRVTYPE, dev->regs.RD_MotorDriverType);
+ _DODELAY( 12 );
+
+ if(!dev->f2003 || (dev->shade.intermediate & _ScanMode_AverageOut) ||
+ ( dev->DataInf.xyAppDpi.y <= 75 &&
+ dev->DataInf.wPhyDataType <= COLOR_256GRAY)) {
+ u12io_DataToRegister( dev, REG_MOTORDRVTYPE,
+ (SANE_Byte)(dev->MotorPower & (_MOTORR_MASK | _MOTORR_STRONG)));
+ } else {
+ u12io_DataToRegister( dev, REG_MOTORDRVTYPE,
+ dev->regs.RD_MotorDriverType );
+ }
+
+ c = 0;
+ _SET_REG( rb, c, REG_XSTEPTIME, dev->regs.RD_XStepTime );
+ _SET_REG( rb, c, REG_EXTENDEDXSTEP, dev->regs.RD_ExtXStepTime );
+ _SET_REG( rb, c, REG_SCANCONTROL1,
+ (SANE_Byte)(dev->regs.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
+ u12io_DataToRegs( dev, rb, c );
+
+ if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW ) {
+
+ TimerDef timer;
+
+ u12motor_ModuleFreeRun( dev, dwBeginY );
+ _DODELAY( 15 );
+
+ u12io_StartTimer( &timer, (_SECOND * 20));
+
+ while(( u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING) &&
+ !u12io_CheckTimer(&timer));
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeScan );
+ } else {
+ u12motor_PositionYProc( dev, dwBeginY );
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static void u12motor_ForceToLeaveHomePos( U12_Device *dev )
+{
+ SANE_Byte rb[4];
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "u12motor_ForceToLeaveHomePos()\n" );
+ rb[0] = REG_STEPCONTROL;
+ rb[1] = _MOTOR0_ONESTEP;
+ rb[2] = REG_MOTOR0CONTROL;
+ rb[3] = _FORWARD_MOTOR;
+ u12io_DataToRegs( dev, rb, 2 );
+
+ u12io_StartTimer( &timer, _SECOND );
+
+ do {
+ if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER))
+ break;
+
+ u12io_RegisterToScanner( dev, REG_FORCESTEP );
+ _DODELAY( 10 );
+
+ } while( !u12io_CheckTimer( &timer ));
+
+ u12io_DataToRegister( dev, REG_STEPCONTROL, _MOTOR0_SCANSTATE );
+}
+
+/** move the sensor to the appropriate shading position
+ */
+static SANE_Status u12motor_GotoShadingPosition( U12_Device *dev )
+{
+ SANE_Byte rb[20];
+ SANE_Status res;
+ int c;
+
+ DBG( _DBG_INFO, "u12motor_GotoShadingPosition()\n" );
+ res = u12motor_ModuleToHome( dev );
+ if( SANE_STATUS_GOOD == res )
+ return res;
+
+ /* position to somewhere under the transparency adapter */
+ if( dev->DataInf.dwScanFlag & _SCANDEF_TPA ) {
+
+ u12motor_ForceToLeaveHomePos( dev );
+ u12motor_DownloadNullScanStates( dev );
+
+ c = 0;
+ _SET_REG( rb, c, REG_STEPCONTROL, _MOTOR0_SCANSTATE );
+ _SET_REG( rb, c, REG_MODECONTROL, _ModeScan);
+ _SET_REG( rb, c, REG_MOTOR0CONTROL, _FORWARD_MOTOR );
+ _SET_REG( rb, c, REG_XSTEPTIME, 6);
+ _SET_REG( rb, c, REG_EXTENDEDXSTEP, 0);
+ _SET_REG( rb, c, REG_SCANCONTROL1, _MFRC_BY_XSTEP);
+ u12io_DataToRegs( dev, rb, c );
+
+ res = u12motor_PositionYProc( dev, _TPA_SHADINGORG );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+ }
+ DBG( _DBG_INFO, "* Position reached\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static void u12motor_ModuleForwardBackward( U12_Device *dev )
+{
+ DBG( _DBG_INFO, "u12motor_ModuleForwardBackward()\n" );
+
+ switch( dev->scan.bModuleState ) {
+
+ case _MotorInNormalState:
+ DBG( _DBG_INFO, "* _MotorInNormalState\n" );
+ dev->scan.bModuleState = _MotorGoBackward;
+ u12io_DataToRegister( dev, REG_SCANCONTROL1,
+ (SANE_Byte)(dev->regs.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
+ u12io_DataToRegister( dev, REG_MOTOR0CONTROL,
+ (SANE_Byte)(dev->regs.RD_Motor0Control & ~_MotorDirForward));
+
+ u12motor_ModuleFreeRun( dev, _BACKSTEPS );
+ u12io_StartTimer( &u12motor_Timer, (15 * _MSECOND));
+ break;
+
+ case _MotorGoBackward:
+ DBG( _DBG_INFO, "* _MotorGoBackward\n" );
+ if( u12io_CheckTimer( &u12motor_Timer)) {
+ if(!(u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING )) {
+ dev->scan.bModuleState = _MotorInStopState;
+ u12io_StartTimer( &u12motor_Timer, (50 *_MSECOND));
+ }
+ }
+ break;
+
+ case _MotorInStopState:
+ DBG( _DBG_INFO, "* _MotorInStopState\n" );
+ if( u12io_CheckTimer( &u12motor_Timer )) {
+
+ if( u12io_GetFifoLength( dev ) < dev->scan.dwMaxReadFifo ) {
+ dev->scan.bModuleState = _MotorAdvancing;
+ u12io_DataToRegister( dev, REG_SCANCONTROL1,
+ dev->regs.RD_ScanControl1);
+ u12io_DataToRegister( dev, REG_MOTOR0CONTROL,
+ dev->regs.RD_Motor0Control);
+ u12motor_ModuleFreeRun( dev, _FORWARDSTEPS );
+ u12io_StartTimer( &u12motor_Timer, (15 * _MSECOND));
+ }
+ }
+ break;
+
+ case _MotorAdvancing:
+ DBG( _DBG_INFO, "* _MotorAdvancing\n" );
+ if( u12io_CheckTimer( &u12motor_Timer)) {
+ if( !(u12io_GetScanState( dev ) & _SCANSTATE_STOP))
+ dev->scan.bModuleState = _MotorInNormalState;
+ else {
+ if (!(u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING )) {
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+ dev->scan.bModuleState = _MotorInNormalState;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/* END U12-MOTOR.C ..........................................................*/
diff --git a/backend/u12-scanner.h b/backend/u12-scanner.h
new file mode 100644
index 0000000..be0c200
--- /dev/null
+++ b/backend/u12-scanner.h
@@ -0,0 +1,306 @@
+/** @file u12-scanner.h
+ * @brief Definitions for the devices.
+ *
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - removed useless stuff
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __U12_SCANNER_H__
+#define __U12_SCANNER_H__
+
+/* definitions for the timer functions
+ */
+typedef double TimerDef;
+#define _MSECOND 1000 /* based on 1 us */
+#define _SECOND (1000*_MSECOND)
+
+#if 1
+#define _DO_UDELAY(usecs) u12io_udelay(usecs)
+#define _DODELAY(msecs) u12io_udelay(1000*msecs)
+/*{ int i; for( i = msecs; i--; ) _DO_UDELAY(1000); }*/
+#else
+#define _DODELAY(msecs)
+#endif
+
+/* ModuleStates */
+#define _MotorInNormalState 0
+#define _MotorGoBackward 1
+#define _MotorInStopState 2
+#define _MotorAdvancing 3
+#define _MotorAdvanced 4
+
+
+/** some function types
+ */
+typedef struct u12d *pU12_Device;
+typedef struct svd *pShadingVarDef;
+typedef void (*pFnVoid)(pU12_Device);
+typedef void (*pFnDACOffs)(pU12_Device, pShadingVarDef, u_long);
+typedef void (*pFnDACDark)(pU12_Device, pShadingVarDef, u_long, u_short);
+typedef void (*pFnDataProcess)(pU12_Device, void*, void*, u_long);
+typedef SANE_Bool (*pFnBool)(pU12_Device);
+
+
+/* useful for RGB-values */
+typedef struct {
+ SANE_Byte Red;
+ SANE_Byte Green;
+ SANE_Byte Blue;
+} RGBByteDef;
+
+typedef struct {
+ u_short Red;
+ u_short Green;
+ u_short Blue;
+} RGBUShortDef;
+
+typedef struct {
+ u_long Red;
+ u_long Green;
+ u_long Blue;
+} RGBULongDef;
+
+typedef union {
+ RGBByteDef Colors;
+ SANE_Byte bColors[3];
+} ColorByte;
+
+typedef union {
+ RGBUShortDef Colors;
+ u_short wColors[3];
+} ColorWord;
+
+typedef union {
+ SANE_Byte *pb;
+ u_short *pw;
+ u_long *pdw;
+ RGBUShortDef *pusrgb;
+ RGBULongDef *pulrgb;
+ RGBByteDef *pbrgb;
+} DataPointer;
+
+typedef struct
+{
+ union {
+ SANE_Byte *bp;
+ u_short *usp;
+ u_long *ulp;
+ } red;
+ union {
+ SANE_Byte *bp;
+ u_short *usp;
+ u_long *ulp;
+ } green;
+ union {
+ SANE_Byte *bp;
+ u_short *usp;
+ u_long *ulp;
+ } blue;
+
+} RBGPtrDef;
+
+typedef struct {
+ SANE_Byte b1st;
+ SANE_Byte b2nd;
+} WordVal;
+
+typedef struct {
+ WordVal w1st;
+ WordVal w2nd;
+} DWordVal;
+
+
+typedef union {
+ WordVal wOverlap;
+ DWordVal dwOverlap;
+ u_long dwValue;
+ u_short wValue;
+ SANE_Byte bValue;
+} DataType;
+
+typedef struct {
+ u_short exposureTime;
+ u_short xStepTime;
+} ExpXStepDef;
+
+typedef struct {
+ SANE_Byte reg;
+ SANE_Byte val;
+} RegDef;
+
+/** for defining a point
+ */
+typedef struct {
+ u_short x;
+ u_short y;
+} XY;
+
+/** for defining a crop area, all is 300DPI based
+ */
+typedef struct {
+ u_short x; /**< x-pos of top-left corner */
+ u_short y; /**< y-pos of top-left corner */
+ u_short cx; /**< width */
+ u_short cy; /**< height */
+} CropRect;
+
+/** for defining an image
+ */
+typedef struct {
+ u_long dwFlag; /**< i.e. image source */
+ CropRect crArea; /**< the image size and position */
+ XY xyDpi; /**< the resolution */
+ u_short wDataType; /**< and the data type */
+} ImgDef;
+
+/**
+ */
+typedef struct {
+ u_long dwPixelsPerLine;
+ u_long dwBytesPerLine;
+ u_long dwLinesPerArea;
+ ImgDef image;
+} CropInfo;
+
+/** all we need for a scan
+ */
+typedef struct
+{
+ u_long dwScanFlag;
+ double xyRatio; /**< for scaling */
+ u_short wYSum;
+
+ XY xyPhyDpi; /**< physical resolution of a scan */
+ u_long dwPhysBytesPerLine;
+ u_long wPhyDataType; /**< how the scanner should scan */
+
+ u_long dwAsicPixelsPerPlane;
+ u_long dwAsicBytesPerPlane;
+ u_long dwAsicBytesPerLine;
+
+ XY xyAppDpi;
+ u_long dwAppLinesPerArea;
+ u_long dwAppPixelsPerLine;
+ u_long dwAppPhyBytesPerLine;
+ u_long dwAppBytesPerLine;
+ u_short wAppDataType;
+
+ short siBrightness;
+ short siContrast;
+
+ CropRect crImage;
+} DataInfo;
+
+/** this will be our global "overkill" structure
+ */
+typedef struct
+{
+ pFnDataProcess DataProcess; /* to convert RGB buffers to RGB pixels */
+ pFnBool DoSample;
+ pFnBool DataRead; /* function to get data from scanner */
+
+ long lBufferAdjust;
+ u_long dwScanOrigin; /* where to start the scan */
+ u_long negBegin; /* used while scanning in TPA modes */
+ u_long posBegin;
+ SANE_Byte bDiscardAll;
+
+ union {
+ u_short wGreenDiscard;
+ u_short wGreenKeep;
+ } gd_gk;
+ union {
+ u_short wBlueDiscard;
+ u_short wRedKeep;
+ } bd_rk;
+
+ u_long dpiIdx; /* index to get/set values in the table */
+ ExpXStepDef *negScan; /* reference to exposure/xtep table */
+
+ DataPointer p48BitBuf; /* for handling 48-bit data */
+ RBGPtrDef BufBegin; /* for reading/writing the scan-data */
+ RBGPtrDef BufEnd;
+ RBGPtrDef BufGet;
+ RBGPtrDef BufData;
+ RBGPtrDef BufPut;
+
+ /* motor movement stuff */
+ u_long dwInterval;
+ SANE_Bool refreshState;
+ SANE_Bool motorBackward;
+ SANE_Byte oldScanState;
+ SANE_Byte bRefresh;
+ SANE_Byte bModuleState;
+ SANE_Byte bNowScanState;
+
+ /* internal FIFO management */
+ u_long dwMinReadFifo;
+ u_long dwMaxReadFifo;
+ SANE_Byte bFifoSelect; /* defines which FIFO to use */
+
+} ScanInfo;
+
+/** structure for accessing one buffer in various ways...
+ */
+typedef struct
+{
+ union {
+ SANE_Byte *pReadBuf;
+ SANE_Byte *pShadingMap;
+ u_short *pShadingRam;
+ DataPointer Buf;
+ } b1;
+
+ union {
+ SANE_Byte *pSumBuf;
+ RGBUShortDef *pSumRGB;
+ } b2;
+
+ DataPointer TpaBuf;
+} BufferDef;
+
+#endif /* guard __U12_SCANNER_H__ */
+
+/* END U12-SCANNER.H ........................................................*/
diff --git a/backend/u12-shading.c b/backend/u12-shading.c
new file mode 100644
index 0000000..e31c7ee
--- /dev/null
+++ b/backend/u12-shading.c
@@ -0,0 +1,877 @@
+/* @file u12-shading.c -
+ * @brief all the shading functions
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 -
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#define _GAIN_HIGH 240 /* Volt. max. value */
+#define _GAIN_LOW 220 /* Volt. min. value */
+
+#define _CHANNEL_RED 0
+#define _CHANNEL_GREEN 1
+#define _CHANNEL_BLUE 2
+
+/* for DAC programming */
+#define _VALUE_CONFIG 0x51
+#define _DAC_RED (SANE_Byte)(_VALUE_CONFIG | 0x00)
+#define _DAC_GREENCOLOR (SANE_Byte)(_VALUE_CONFIG | 0x04)
+#define _DAC_GREENMONO (SANE_Byte)(_VALUE_CONFIG | 0x06)
+#define _DAC_BLUE (SANE_Byte)(_VALUE_CONFIG | 0x08)
+
+
+/* forward declarations ... */
+static void u12tpa_Reshading( U12_Device * );
+static void u12tpa_FindCenterPointer( U12_Device * );
+
+/**
+ */
+static void
+u12shading_DownloadShadingTable( U12_Device *dev, SANE_Byte *buf, u_long len )
+{
+ SANE_Byte *val, *rb;
+ SANE_Byte reg, regs[20];
+ int c;
+
+ DBG( _DBG_INFO, "u12shading_DownloadShadingTable()\n" );
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeShadingMem );
+ u12io_DataToRegister( dev, REG_MEMORYLO, 0 );
+ u12io_DataToRegister( dev, REG_MEMORYHI, 0 );
+
+ /* set 12 bits output color */
+ u12io_DataToRegister( dev, REG_SCANCONTROL,
+ (SANE_Byte)(dev->regs.RD_ScanControl | _SCAN_12BITMODE));
+
+ u12io_MoveDataToScanner( dev, buf, len );
+
+ regs[0] = REG_MODECONTROL;
+ regs[1] = _ModeScan;
+
+ /* FillShadingDarkToShadingRegister() */
+ dev->regs.RD_RedDarkOff = dev->shade.DarkOffset.Colors.Red;
+ dev->regs.RD_GreenDarkOff = dev->shade.DarkOffset.Colors.Green;
+ dev->regs.RD_BlueDarkOff = dev->shade.DarkOffset.Colors.Blue;
+
+ val = (SANE_Byte*)&dev->regs.RD_RedDarkOff;
+ rb = &regs[2];
+ c = 1;
+ for( reg = REG_REDCHDARKOFFSETLO;
+ reg <= REG_BLUECHDARKOFFSETHI; reg++, val++) {
+
+ *(rb++) = reg;
+ *(rb++) = *val;
+ c++;
+ }
+ u12io_DataToRegs( dev, regs, c );
+}
+
+/**
+ */
+static SANE_Status u12shadingAdjustShadingWaveform( U12_Device *dev )
+{
+ SANE_Byte b;
+ u_short count, wR, wG, wB, tmp;
+ DataType var;
+ DataPointer pvar, psum;
+ RBGPtrDef cp;
+ RGBUShortDef *pRGB, *pwsum;
+ u_long shadingBytes;
+
+ DBG( _DBG_INFO, "u12shading_AdjustShadingWaveForm()\n" );
+
+ memset( &cp, 0, sizeof(RBGPtrDef));
+ memset( dev->bufs.b2.pSumBuf, 0, (5400 * 3 * 2));
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeIdle );
+
+ dev->regs.RD_LineControl = _LOBYTE(dev->shade.wExposure);
+ dev->regs.RD_ExtLineControl = _HIBYTE(dev->shade.wExposure);
+ u12io_DataToRegister( dev, REG_EXTENDEDLINECONTROL,
+ dev->regs.RD_ExtLineControl );
+ u12io_DataToRegister( dev, REG_LINECONTROL, dev->regs.RD_LineControl );
+
+ dev->regs.RD_XStepTime = _LOBYTE(dev->shade.wExposure);
+ dev->regs.RD_ExtXStepTime = _HIBYTE(dev->shade.wExposure);
+ u12io_DataToRegister( dev, REG_EXTENDEDXSTEP, dev->regs.RD_ExtXStepTime );
+ u12io_DataToRegister( dev, REG_XSTEPTIME, dev->regs.RD_XStepTime );
+
+ dev->regs.RD_ModeControl = _ModeScan;
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE;
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut ) {
+
+ dev->regs.RD_Dpi = 300;
+ dev->regs.RD_Pixels = 2700;
+ shadingBytes = 2700 * 2;
+ } else {
+ dev->regs.RD_Dpi = 600;
+ dev->regs.RD_Pixels = 5400;
+ shadingBytes = 5400 * 2;
+ }
+ dev->regs.RD_Origin = _SHADING_BEGINX;
+
+ for( pvar.pdw = (u_long*)dev->scanStates,
+ var.dwValue = _SCANSTATE_BYTES >> 2; var.dwValue--; pvar.pdw++) {
+ *pvar.pdw = 0x00f00080;
+ }
+
+ dev->scan.refreshState = SANE_FALSE;
+ u12io_PutOnAllRegisters( dev );
+/* _DODELAY( 100 ); */
+
+ if( dev->shade.pHilight ) {
+
+ memset( dev->shade.pHilight, 0,
+ shadingBytes * dev->shade.skipHilight * 3 );
+
+ memset((SANE_Byte*)dev->shade.pHilight +
+ shadingBytes * dev->shade.skipHilight * 3, 0xff,
+ shadingBytes * dev->shade.skipShadow * 3 );
+ }
+
+ for( count = 32; count--; ) {
+
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ u12io_ReadOneShadingLine( dev, ((SANE_Byte*)dev->bufs.b1.pShadingRam)+
+ _SHADING_BEGINX, shadingBytes );
+
+ if( dev->shade.pHilight ) {
+
+ if ( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ cp.red.usp = dev->bufs.b1.pShadingRam + _SHADING_BEGINX;
+ cp.green.usp = cp.red.usp + dev->regs.RD_Pixels;
+ cp.blue.usp = cp.green.usp + dev->regs.RD_Pixels;
+ pvar.pusrgb = (RGBUShortDef*)dev->shade.pHilight +
+ _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ pRGB = pvar.pusrgb++;
+ wR = *cp.red.usp;
+ wG = *cp.green.usp;
+ wB = *cp.blue.usp;
+
+ for( b = dev->shade.skipHilight; b--;
+ pRGB += dev->regs.RD_Pixels ) {
+ if( wR > pRGB->Red ) {
+ tmp = wR;
+ wR = pRGB->Red;
+ pRGB->Red = tmp;
+ }
+ if( wG > pRGB->Green ) {
+ tmp = wG;
+ wG = pRGB->Green;
+ pRGB->Green = tmp;
+ }
+ if( wB > pRGB->Blue ) {
+ tmp = wB;
+ wB = pRGB->Blue;
+ pRGB->Blue = tmp;
+ }
+ }
+
+ wR = *cp.red.usp++;
+ wG = *cp.green.usp++;
+ wB = *cp.blue.usp++;
+
+ for( b = dev->shade.skipShadow; b--;
+ pRGB += dev->regs.RD_Pixels ) {
+ if( wR < pRGB->Red ) {
+ tmp = wR;
+ wR = pRGB->Red;
+ pRGB->Red = tmp;
+ }
+ if( wG < pRGB->Green ) {
+ tmp = wG;
+ wG = pRGB->Green;
+ pRGB->Green = tmp;
+ }
+ if( wB < pRGB->Blue ) {
+ tmp = wB;
+ wB = pRGB->Blue;
+ pRGB->Blue = tmp;
+ }
+ }
+ }
+ } else {
+
+ cp.green.usp = dev->bufs.b1.pShadingRam +
+ dev->regs.RD_Pixels + _SHADING_BEGINX;
+ cp.blue.usp = (u_short*)dev->shade.pHilight + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ cp.red.usp = cp.blue.usp++;
+ wG = *cp.green.usp;
+ for( b = dev->shade.skipHilight; b--;
+ cp.red.usp += dev->regs.RD_Pixels) {
+ if( wG > *cp.red.usp ) {
+ tmp = wG;
+ wG = *cp.red.usp;
+ *cp.red.usp = tmp;
+ }
+ }
+ wG = *cp.green.usp++;
+ for( b = dev->shade.skipShadow; b--;
+ cp.red.usp += dev->regs.RD_Pixels ) {
+ if( wG < *cp.red.usp ) {
+ tmp = wG;
+ wG = *cp.red.usp;
+ *cp.red.usp = tmp;
+ }
+ }
+ }
+ }
+ }
+
+ /* AddToSumBuffer() */
+ if( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ cp.red.usp = dev->bufs.b1.pShadingRam + _SHADING_BEGINX;
+ cp.green.usp = cp.red.usp + dev->regs.RD_Pixels;
+ cp.blue.usp = cp.green.usp + dev->regs.RD_Pixels;
+
+ pvar.pulrgb = (RGBULongDef*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = (u_long)dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;
+ pvar.pulrgb++, cp.red.usp++, cp.green.usp++, cp.blue.usp++) {
+ pvar.pulrgb->Red += (u_long)*cp.red.usp;
+ pvar.pulrgb->Green += (u_long)*cp.green.usp;
+ pvar.pulrgb->Blue += (u_long)*cp.blue.usp;
+ }
+
+ } else {
+
+ cp.green.usp = dev->bufs.b1.pShadingRam +
+ dev->regs.RD_Pixels + _SHADING_BEGINX;
+ pvar.pdw = (u_long*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ for( var.dwValue = (u_long)dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--; pvar.pdw++, cp.green.usp++) {
+ *pvar.pdw += (u_long)*cp.green.usp;
+ }
+ }
+
+ u12io_ResetFifoLen();
+ if( u12io_GetFifoLength( dev ) < dev->regs.RD_Pixels )
+ u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
+ }
+
+ /* AverageAfterSubHilightShadow() */
+ if( dev->shade.pHilight ) {
+ if( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ psum.pulrgb = (RGBULongDef*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pwsum = (RGBUShortDef*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pvar.pusrgb = (RGBUShortDef*)dev->shade.pHilight + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ pRGB = pvar.pusrgb++;
+
+ for( b = dev->shade.skipHilight + dev->shade.skipShadow;
+ b--; pRGB += dev->regs.RD_Pixels ) {
+
+ psum.pulrgb->Red -= (u_long)pRGB->Red;
+ psum.pulrgb->Green -= (u_long)pRGB->Green;
+ psum.pulrgb->Blue -= (u_long)pRGB->Blue;
+ }
+
+ pwsum->Red = (u_short)(psum.pulrgb->Red / dev->shade.dwDiv);
+ pwsum->Green = (u_short)(psum.pulrgb->Green / dev->shade.dwDiv);
+ pwsum->Blue = (u_short)(psum.pulrgb->Blue / dev->shade.dwDiv);
+ psum.pulrgb++;
+ pwsum++;
+ }
+ } else {
+ cp.green.ulp = (u_long*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ cp.blue.usp = (u_short*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pvar.pw = (u_short*)dev->shade.pHilight + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ cp.red.usp = pvar.pw++;
+
+ for( b = dev->shade.skipHilight + dev->shade.skipShadow;
+ b--; cp.red.usp += dev->regs.RD_Pixels )
+ *cp.green.ulp -= *cp.red.usp;
+
+ *cp.blue.usp = (u_short)(*cp.green.ulp / dev->shade.dwDiv);
+ cp.blue.usp++;
+ cp.green.ulp++;
+ }
+ }
+ } else {
+
+ if( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ psum.pulrgb = (RGBULongDef*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ pwsum = (RGBUShortDef*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ pwsum->Red = (u_short)(psum.pulrgb->Red >> 5);
+ pwsum->Green = (u_short)(psum.pulrgb->Green >> 5);
+ pwsum->Blue = (u_short)(psum.pulrgb->Blue >> 5);
+ psum.pulrgb++;
+ pwsum++;
+ }
+ } else {
+ cp.green.ulp = (u_long*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+ cp.blue.usp = (u_short*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+ *cp.blue.usp = (u_short)(*cp.green.ulp >> 5);
+ cp.blue.usp++;
+ cp.green.ulp++;
+ }
+ }
+ }
+
+ /* Process negative & transparency here */
+ if( dev->DataInf.dwScanFlag & _SCANDEF_TPA )
+ u12tpa_FindCenterPointer( dev );
+
+ if( dev->DataInf.dwScanFlag & _SCANDEF_Negative )
+ u12tpa_Reshading( dev );
+
+ pRGB = (RGBUShortDef*)&dev->shade.pCcdDac->GainResize;
+
+ if ( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ pwsum = (RGBUShortDef*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+
+ if ((short)(pwsum->Red -= dev->shade.DarkOffset.Colors.Red) > 0) {
+ pwsum->Red = pwsum->Red * pRGB->Red / 100U;
+ if( pwsum->Red > 0xfff )
+ pwsum->Red = 0xfff;
+ } else
+ pwsum->Red = 0;
+
+ if((short)(pwsum->Green -= dev->shade.DarkOffset.Colors.Green) > 0) {
+ pwsum->Green = pwsum->Green * pRGB->Green / 100U;
+ if( pwsum->Green > 0xfff )
+ pwsum->Green = 0xfff;
+ } else
+ pwsum->Green = 0;
+
+ if ((short)(pwsum->Blue -= dev->shade.DarkOffset.Colors.Blue) > 0) {
+ pwsum->Blue = pwsum->Blue * pRGB->Blue / 100U;
+ if( pwsum->Blue > 0xfff )
+ pwsum->Blue = 0xfff;
+ } else
+ pwsum->Blue = 0;
+
+ wR = (u_short)(pwsum->Red >> 4);
+ pwsum->Red <<= 12;
+ pwsum->Red |= wR;
+ wR = (u_short)(pwsum->Green >> 4);
+ pwsum->Green <<= 12;
+ pwsum->Green |= wR;
+ wR = (u_short)(pwsum->Blue>> 4);
+ pwsum->Blue <<= 12;
+ pwsum->Blue |= wR;
+ pwsum++;
+ }
+ } else {
+
+ cp.green.usp = (u_short*)dev->bufs.b2.pSumBuf + _SHADING_BEGINX;
+
+ for( var.dwValue = dev->regs.RD_Pixels - _SHADING_BEGINX;
+ var.dwValue--;) {
+
+ if((short)(*cp.green.usp -= dev->shade.DarkOffset.Colors.Green) > 0) {
+
+ *cp.green.usp = *cp.green.usp * pRGB->Green / 100U;
+ if( *cp.green.usp > 0xfff )
+ *cp.green.usp = 0xfff;
+ } else
+ *cp.green.usp = 0;
+
+ wR = (u_short)(*cp.green.usp >> 4);
+ *cp.green.usp <<= 12;
+ *cp.green.usp |= wR;
+
+ cp.green.usp++;
+ }
+ }
+
+ u12shading_DownloadShadingTable(dev, dev->bufs.b2.pSumBuf, (5400 * 3 * 2));
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static void u12shading_GainOffsetToDAC( U12_Device *dev, SANE_Byte ch,
+ SANE_Byte reg, SANE_Byte d )
+{
+ if( dev->DACType == _DA_SAMSUNG8531 ) {
+ u12io_DataRegisterToDAC( dev, 0, ch );
+ }
+ u12io_DataRegisterToDAC( dev, reg, d );
+}
+
+/**
+ */
+static void u12shading_FillToDAC( U12_Device *dev,
+ RGBByteDef *regs, ColorByte *data )
+{
+ if( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ u12shading_GainOffsetToDAC(dev, _DAC_RED, regs->Red, data->Colors.Red);
+ u12shading_GainOffsetToDAC(dev, _DAC_GREENCOLOR,
+ regs->Green, data->Colors.Green);
+ u12shading_GainOffsetToDAC(dev, _DAC_BLUE,
+ regs->Blue, data->Colors.Blue);
+ } else {
+ u12shading_GainOffsetToDAC(dev, _DAC_GREENMONO, regs->Green,
+ data->Colors.Green);
+ }
+}
+
+/**
+ */
+static SANE_Byte u12shading_SumGains( SANE_Byte *pb, u_long pixelsLine )
+{
+ SANE_Byte hilight, tmp;
+ u_long dwPixels, dwAve;
+ u_short sum;
+
+ hilight = 0;
+ for( dwPixels = pixelsLine >> 4; dwPixels--; ) {
+
+ for( sum = 0, dwAve = 16; dwAve--; pb++ )
+ sum += (u_short)*pb;
+
+ sum >>= 4;
+ tmp = (SANE_Byte)sum;
+
+ if( tmp > hilight )
+ hilight = tmp;
+ }
+ return hilight;
+}
+
+/**
+ */
+static void
+u12shading_AdjustGain( U12_Device *dev, u_long color, SANE_Byte hilight )
+{
+ if( hilight < dev->shade.bGainLow ) {
+
+ if( dev->shade.Hilight.bColors[color] < dev->shade.bGainHigh ) {
+
+ dev->shade.fStop = SANE_FALSE;
+ dev->shade.Hilight.bColors[color] = hilight;
+
+ if( hilight <= (SANE_Byte)(dev->shade.bGainLow - hilight))
+ dev->shade.Gain.bColors[color] += dev->shade.bGainDouble;
+ else
+ dev->shade.Gain.bColors[color]++;
+ }
+ } else {
+ if( hilight > dev->shade.bGainHigh ) {
+ dev->shade.fStop = SANE_FALSE;
+ dev->shade.Hilight.bColors[color] = hilight;
+ dev->shade.Gain.bColors[color]--;
+ } else {
+ dev->shade.Hilight.bColors[color] = hilight;
+ }
+ }
+
+ if( dev->shade.Gain.bColors[color] > dev->shade.bMaxGain ) {
+ dev->shade.Gain.bColors[color] = dev->shade.bMaxGain;
+ }
+}
+
+/**
+ */
+static SANE_Status u12shading_AdjustRGBGain( U12_Device *dev )
+{
+ int i;
+ SANE_Byte hi[3];
+
+ DBG( _DBG_INFO, "u12shading_AdjustRGBGain()\n" );
+
+ dev->shade.Gain.Colors.Red =
+ dev->shade.Gain.Colors.Green =
+ dev->shade.Gain.Colors.Blue = dev->shade.bUniGain;
+
+ dev->shade.Hilight.Colors.Red =
+ dev->shade.Hilight.Colors.Green =
+ dev->shade.Hilight.Colors.Blue = 0;
+
+ dev->shade.bGainHigh = _GAIN_HIGH;
+ dev->shade.bGainLow = _GAIN_LOW;
+
+ dev->shade.fStop = SANE_FALSE;
+
+ for( i = 10; i-- && !dev->shade.fStop; ) {
+
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ dev->shade.fStop = SANE_TRUE;
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeIdle );
+
+ dev->regs.RD_ScanControl = _SCAN_BYTEMODE;
+ u12hw_SelectLampSource( dev );
+ u12io_DataToRegister( dev, REG_SCANCONTROL, dev->regs.RD_ScanControl );
+
+ u12shading_FillToDAC( dev, &dev->RegDACGain, &dev->shade.Gain );
+
+ dev->regs.RD_ModeControl = _ModeScan;
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE;
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ dev->regs.RD_Origin = (u_short)_DATA_ORIGIN_X >> 1;
+ else
+ dev->regs.RD_Origin = (u_short)_DATA_ORIGIN_X;
+
+ dev->regs.RD_Dpi = 300;
+ dev->regs.RD_Pixels = 2560;
+
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+ dev->scanStates[1] = 0x77;
+
+ u12io_PutOnAllRegisters( dev );
+/* _DODELAY( 100 ); */
+
+ /* read one shading line and work on it */
+ if( u12io_ReadOneShadingLine( dev,
+ (SANE_Byte*)dev->bufs.b1.pShadingRam, 2560)) {
+
+ if( dev->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+
+ hi[1] = u12shading_SumGains(
+ (SANE_Byte*)dev->bufs.b1.pShadingRam + 2560, 2560);
+ if( hi[1] ) {
+ u12shading_AdjustGain( dev, _CHANNEL_GREEN, hi[1] );
+ } else {
+ dev->shade.fStop = SANE_FALSE;
+ }
+ } else {
+ hi[0] = u12shading_SumGains(
+ (SANE_Byte*)dev->bufs.b1.pShadingRam, 2560);
+ hi[1] = u12shading_SumGains(
+ (SANE_Byte*)dev->bufs.b1.pShadingRam + 2560, 2560);
+ hi[2] = u12shading_SumGains(
+ (SANE_Byte*)dev->bufs.b1.pShadingRam + 5120, 2560);
+
+ if (!hi[0] || !hi[1] || !hi[2] ) {
+ dev->shade.fStop = SANE_FALSE;
+ } else {
+ u12shading_AdjustGain( dev, _CHANNEL_RED, hi[0] );
+ u12shading_AdjustGain( dev, _CHANNEL_GREEN, hi[1] );
+ u12shading_AdjustGain( dev, _CHANNEL_BLUE, hi[2] );
+ }
+ }
+ } else
+ dev->shade.fStop = SANE_FALSE;
+ }
+
+ if( !dev->shade.fStop )
+ DBG( _DBG_INFO, "u12shading_AdjustRGBGain() - all loops done!!!\n" );
+
+ u12shading_FillToDAC( dev, &dev->RegDACGain, &dev->shade.Gain );
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static u_short u12shading_SumDarks( U12_Device *dev, u_short *data )
+{
+ u_short i, loop;
+
+ if( dev->CCDID == _CCD_3799 ) {
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ data += 0x18;
+ else
+ data += 0x30;
+ } else {
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ data += 0x18;
+ else
+ data += 0x20;
+ }
+
+ for( i = 0, loop = 16; loop--; data++ )
+ i += *data;
+ i >>= 4;
+
+ return i;
+}
+
+/**
+ */
+static SANE_Status u12shadingAdjustDark( U12_Device *dev )
+{
+ u_long i;
+ u_short wDarks[3];
+
+ DBG( _DBG_INFO, "u12shadingAdjustDark()\n" );
+ dev->shade.DarkDAC.Colors = dev->shade.pCcdDac->DarkDAC.Colors;
+ dev->shade.fStop = SANE_FALSE;
+
+ for( i = 16; i-- && !dev->shade.fStop;) {
+
+ if( u12io_IsEscPressed()) {
+ DBG( _DBG_INFO, "* CANCEL detected!\n" );
+ return SANE_STATUS_CANCELLED;
+ }
+
+ dev->shade.fStop = SANE_TRUE;
+
+ u12shading_FillToDAC( dev, &dev->RegDACOffset, &dev->shade.DarkDAC );
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeIdle );
+
+ dev->regs.RD_ScanControl = (_SCAN_12BITMODE + _SCAN_1ST_AVERAGE);
+ u12hw_SelectLampSource( dev );
+ u12io_DataToRegister( dev, REG_SCANCONTROL, dev->regs.RD_ScanControl );
+
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE;
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+
+ dev->regs.RD_Origin = _SHADING_BEGINX;
+ dev->regs.RD_Pixels = 512;
+
+ if( dev->shade.intermediate & _ScanMode_AverageOut )
+ dev->regs.RD_Dpi = 300;
+ else
+ dev->regs.RD_Dpi = 600;
+
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+ dev->scanStates[1] = 0x77;
+
+ u12io_PutOnAllRegisters( dev );
+/* _DODELAY( 100 ); */
+
+ /* read one shading line and work on it */
+ if( u12io_ReadOneShadingLine(dev,
+ (SANE_Byte*)dev->bufs.b1.pShadingRam, 512*2)) {
+
+ if ( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+
+ wDarks[0] = u12shading_SumDarks(dev, dev->bufs.b1.pShadingRam);
+ wDarks[1] = u12shading_SumDarks(dev, dev->bufs.b1.pShadingRam +
+ dev->regs.RD_Pixels );
+ wDarks[2] = u12shading_SumDarks(dev, dev->bufs.b1.pShadingRam +
+ dev->regs.RD_Pixels * 2UL);
+
+ if( !wDarks[0] || !wDarks[1] || !wDarks[2] ) {
+ dev->shade.fStop = SANE_FALSE;
+ } else {
+ dev->shade.DarkOffset.wColors[0] = wDarks[0];
+ dev->shade.DarkOffset.wColors[1] = wDarks[1];
+ dev->shade.DarkOffset.wColors[2] = wDarks[2];
+ (*dev->fnDACDark)( dev,dev->shade.pCcdDac,
+ _CHANNEL_RED, wDarks[0] );
+ (*dev->fnDACDark)( dev, dev->shade.pCcdDac,
+ _CHANNEL_GREEN, wDarks[1] );
+ (*dev->fnDACDark)( dev, dev->shade.pCcdDac,
+ _CHANNEL_BLUE, wDarks[2] );
+ }
+ } else {
+ wDarks[1] = u12shading_SumDarks(dev, dev->bufs.b1.pShadingRam +
+ dev->regs.RD_Pixels );
+ if(!wDarks[1] ) {
+ dev->shade.fStop = SANE_FALSE;
+ } else {
+ dev->shade.DarkOffset.wColors[1] = wDarks[1];
+ (*dev->fnDACDark)( dev, dev->shade.pCcdDac,
+ _CHANNEL_GREEN, wDarks[1] );
+ }
+ }
+ } else {
+ dev->shade.fStop = SANE_FALSE;
+ }
+ }
+
+ /* CalculateDarkDependOnCCD() */
+ if ( dev->DataInf.wPhyDataType > COLOR_256GRAY ) {
+ (*dev->fnDarkOffset)( dev, dev->shade.pCcdDac, _CHANNEL_RED );
+ (*dev->fnDarkOffset)( dev, dev->shade.pCcdDac, _CHANNEL_GREEN );
+ (*dev->fnDarkOffset)( dev, dev->shade.pCcdDac, _CHANNEL_BLUE );
+ } else {
+ (*dev->fnDarkOffset)( dev, dev->shade.pCcdDac, _CHANNEL_GREEN );
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/** here we download the current mapping table
+ */
+static void u12shading_DownloadMapTable( U12_Device *dev, SANE_Byte *buf )
+{
+ SANE_Byte addr, regs[6];
+ int i;
+
+ u12io_DataToRegister( dev, REG_SCANCONTROL,
+ (SANE_Byte)((dev->regs.RD_ScanControl & 0xfc) | _SCAN_BYTEMODE));
+
+ /* prepare register settings... */
+ regs[0] = REG_MODECONTROL;
+ regs[1] = _ModeMappingMem;
+ regs[2] = REG_MEMORYLO;
+ regs[3] = 0;
+ regs[4] = REG_MEMORYHI;
+
+ for( i = 3, addr = _MAP_ADDR_RED; i--; addr += _MAP_ADDR_SIZE ) {
+
+ regs[5] = addr;
+ u12io_DataToRegs( dev, regs, 3 );
+
+ u12io_MoveDataToScanner( dev, buf, 4096 );
+ buf += 4096;
+ }
+
+ u12io_DataToRegister( dev, REG_SCANCONTROL, dev->regs.RD_ScanControl );
+}
+
+/**
+ */
+static SANE_Status u12shading_DoCalibration( U12_Device *dev )
+{
+ SANE_Byte tb[4096*3];
+ u_long i, tmp;
+ SANE_Byte bScanControl, rb[20];
+ SANE_Status res;
+ int c;
+
+ DBG( _DBG_INFO, "u12shading_DoCalibration()\n" );
+
+ /** before getting the shading data, (re)init the ASIC
+ */
+ u12hw_InitAsic( dev, SANE_TRUE );
+
+ dev->shade.DarkOffset.Colors.Red = 0;
+ dev->shade.DarkOffset.Colors.Green = 0;
+ dev->shade.DarkOffset.Colors.Blue = 0;
+
+ c = 0;
+ _SET_REG( rb, c, REG_RESETMTSC, 0 );
+ _SET_REG( rb, c, REG_MODELCONTROL, dev->regs.RD_ModelControl);
+ _SET_REG( rb, c, REG_MOTORDRVTYPE, dev->regs.RD_MotorDriverType );
+ _SET_REG( rb, c, REG_SCANCONTROL1, (_SCANSTOPONBUFFULL| _MFRC_BY_XSTEP));
+
+ u12io_DataToRegs( dev, rb, c );
+
+ res = u12motor_GotoShadingPosition( dev );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ bScanControl = dev->regs.RD_ScanControl;
+
+ /* SetShadingMapForGainDark */
+ memset( dev->bufs.b2.pSumBuf, 0xff, (5400 * 3 * 2));
+ u12shading_DownloadShadingTable( dev, dev->bufs.b2.pSumBuf, (5400*3*2));
+
+ for( i = 0, tmp = 0; i < 1024; tmp += 0x01010101, i += 4 ) {
+ dev->bufs.b1.Buf.pdw[i] =
+ dev->bufs.b1.Buf.pdw[i+1] =
+ dev->bufs.b1.Buf.pdw[i+2] =
+ dev->bufs.b1.Buf.pdw[i+3] = tmp;
+ }
+
+ memcpy( dev->bufs.b1.pShadingMap + 4096, dev->bufs.b1.pShadingMap, 4096 );
+ memcpy( dev->bufs.b1.pShadingMap + 8192, dev->bufs.b1.pShadingMap, 4096 );
+ u12shading_DownloadMapTable( dev, dev->bufs.b1.pShadingMap );
+
+ DBG( _DBG_INFO, "* wExposure = %u\n", dev->shade.wExposure);
+ DBG( _DBG_INFO, "* wXStep = %u\n", dev->shade.wXStep);
+
+ dev->regs.RD_LineControl = (_LOBYTE(dev->shade.wExposure));
+ dev->regs.RD_ExtLineControl = (_HIBYTE(dev->shade.wExposure));
+ u12io_DataToRegister( dev, REG_EXTENDEDLINECONTROL,
+ dev->regs.RD_ExtLineControl );
+ u12io_DataToRegister( dev, REG_LINECONTROL, dev->regs.RD_LineControl );
+
+ res = u12shading_AdjustRGBGain( dev );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ res = u12shadingAdjustDark( dev );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ res = u12shadingAdjustShadingWaveform( dev );
+ if( SANE_STATUS_GOOD != res )
+ return res;
+
+ dev->regs.RD_ScanControl = bScanControl;
+
+ /* here we have to prepare and download the table in any case...*/
+ if( dev->DataInf.wPhyDataType <= COLOR_256GRAY ) {
+ u12map_Adjust( dev, _MAP_MASTER, tb );
+ } else {
+ u12map_Adjust( dev, _MAP_RED, tb );
+ u12map_Adjust( dev, _MAP_GREEN, tb );
+ u12map_Adjust( dev, _MAP_BLUE, tb );
+ }
+
+ u12shading_DownloadMapTable( dev, tb );
+
+ u12motor_BackToHomeSensor( dev );
+ DBG( _DBG_INFO, "u12shading_DoCalibration() - done.\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/* END U12-SHADING ..........................................................*/
diff --git a/backend/u12-tpa.c b/backend/u12-tpa.c
new file mode 100644
index 0000000..5a8e164
--- /dev/null
+++ b/backend/u12-tpa.c
@@ -0,0 +1,441 @@
+/* @file u12-pp_tpa.c
+ * @brief Here we find some adjustments according to the scan source.
+ *
+ * based on sources acquired from Plustek Inc.
+ * Copyright (C) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - cleanup
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+/** this function does some reshading, when scanning negatives on an ASIC 98003
+ * based scanner
+ */
+static void u12tpa_Reshading( U12_Device *dev )
+{
+ SANE_Byte bHi[3], bHiLeft[3], bHiRight[3];
+ u_long i, dwR, dwG, dwB, dwSum;
+ u_long dwIndex, dwIndexRight, dwIndexLeft;
+ DataPointer RedPtr, GreenPtr, BluePtr;
+ TimerDef timer;
+
+ DBG( _DBG_INFO, "u12tpa_Reshading()\n" );
+
+ bHi[0] = bHi[1] = bHi[2] = 0;
+
+ dev->scan.negScan[1].exposureTime = 144;
+ dev->scan.negScan[1].xStepTime = 18;
+ dev->scan.negScan[2].exposureTime = 144;
+ dev->scan.negScan[2].xStepTime = 36;
+ dev->scan.negScan[3].exposureTime = 144;
+ dev->scan.negScan[3].xStepTime = 72;
+ dev->scan.negScan[4].exposureTime = 144;
+ dev->scan.negScan[4].xStepTime = 144;
+
+ dev->shade.wExposure = dev->scan.negScan[dev->scan.dpiIdx].exposureTime;
+ dev->shade.wXStep = dev->scan.negScan[dev->scan.dpiIdx].xStepTime;
+
+ u12io_StartTimer( &timer, _SECOND );
+
+ u12io_ResetFifoLen();
+ while(!(u12io_GetScanState( dev ) & _SCANSTATE_STOP) &&
+ (!u12io_CheckTimer(&timer)));
+ u12io_DataToRegister( dev, REG_XSTEPTIME,
+ (SANE_Byte)(dev->regs.RD_LineControl >> 4));
+ _DODELAY( 12 );
+ u12motor_PositionYProc( dev, _NEG_SHADING_OFFS );
+
+ u12io_DataToRegister( dev, REG_XSTEPTIME, dev->regs.RD_XStepTime );
+
+ dev->regs.RD_ScanControl = _SCAN_BYTEMODE;
+ u12hw_SelectLampSource( dev );
+
+ u12io_DataToRegister( dev, REG_LINECONTROL, _LOBYTE(dev->shade.wExposure));
+ u12io_DataToRegister( dev, REG_XSTEPTIME, _LOBYTE(dev->shade.wXStep));
+
+ dev->regs.RD_LineControl = _LOBYTE(dev->shade.wExposure);
+ dev->regs.RD_ExtLineControl = _HIBYTE(dev->shade.wExposure);
+ dev->regs.RD_XStepTime = (SANE_Byte)(dev->shade.wExposure);
+ dev->regs.RD_ModeControl = _ModeScan;
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+
+ dev->regs.RD_Origin = (u_short)dev->scan.negBegin;
+ dev->regs.RD_Pixels = _NEG_PAGEWIDTH600;
+
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+
+ /* put 9 scan states to make sure there are 8 lines available at least */
+ for( i = 0; i <= 12; i++)
+ dev->scanStates[i] = 0x8f;
+
+ u12io_PutOnAllRegisters( dev );
+ _DODELAY( 70 );
+
+ /* prepare the buffers... */
+ memset( dev->bufs.TpaBuf.pb, 0, _SIZE_TPA_DATA_BUF );
+
+ RedPtr.pb = dev->bufs.b1.pShadingMap;
+ GreenPtr.pb = RedPtr.pb + _NEG_PAGEWIDTH600;
+ BluePtr.pb = GreenPtr.pb + _NEG_PAGEWIDTH600;
+
+ for( dwSum = 8; dwSum--; ) {
+
+ u12io_ReadOneShadingLine( dev, dev->bufs.b1.pShadingMap, _NEG_PAGEWIDTH600 );
+
+ for( i = 0; i < _NEG_PAGEWIDTH600; i++) {
+
+ dev->bufs.TpaBuf.pusrgb[i].Red += RedPtr.pb[i];
+ dev->bufs.TpaBuf.pusrgb[i].Green += GreenPtr.pb[i];
+ dev->bufs.TpaBuf.pusrgb[i].Blue += BluePtr.pb[i];
+ }
+ }
+
+ for( i = 0; i < (_NEG_PAGEWIDTH600 * 3UL); i++ )
+ dev->bufs.TpaBuf.pb[i] = dev->bufs.TpaBuf.pw[i] >> 3;
+
+ RedPtr.pb = dev->bufs.TpaBuf.pb;
+
+ /* Convert RGB to gray scale (Brightness), and average 16 pixels */
+ for( bHiRight[1] = 0, i = dwIndexRight = 0;
+ i < _NEG_PAGEWIDTH600 / 2; i += 16 ) {
+ bHiRight [0] =
+ (SANE_Byte)(((((u_long) RedPtr.pbrgb [i].Red +
+ (u_long) RedPtr.pbrgb[i + 1].Red +
+ (u_long) RedPtr.pbrgb[i + 2].Red +
+ (u_long) RedPtr.pbrgb[i + 3].Red +
+ (u_long) RedPtr.pbrgb[i + 4].Red +
+ (u_long) RedPtr.pbrgb[i + 5].Red +
+ (u_long) RedPtr.pbrgb[i + 6].Red +
+ (u_long) RedPtr.pbrgb[i + 7].Red +
+ (u_long) RedPtr.pbrgb[i + 8].Red +
+ (u_long) RedPtr.pbrgb[i + 9].Red +
+ (u_long) RedPtr.pbrgb[i + 10].Red +
+ (u_long) RedPtr.pbrgb[i + 11].Red +
+ (u_long) RedPtr.pbrgb[i + 12].Red +
+ (u_long) RedPtr.pbrgb[i + 13].Red +
+ (u_long) RedPtr.pbrgb[i + 14].Red +
+ (u_long) RedPtr.pbrgb[i + 15].Red) >> 4) * 30UL +
+ (((u_long) RedPtr.pbrgb[i].Green +
+ (u_long) RedPtr.pbrgb[i + 1].Green +
+ (u_long) RedPtr.pbrgb[i + 2].Green +
+ (u_long) RedPtr.pbrgb[i + 3].Green +
+ (u_long) RedPtr.pbrgb[i + 4].Green +
+ (u_long) RedPtr.pbrgb[i + 5].Green +
+ (u_long) RedPtr.pbrgb[i + 6].Green +
+ (u_long) RedPtr.pbrgb[i + 7].Green +
+ (u_long) RedPtr.pbrgb[i + 8].Green +
+ (u_long) RedPtr.pbrgb[i + 9].Green +
+ (u_long) RedPtr.pbrgb[i + 10].Green +
+ (u_long) RedPtr.pbrgb[i + 11].Green +
+ (u_long) RedPtr.pbrgb[i + 12].Green +
+ (u_long) RedPtr.pbrgb[i + 13].Green +
+ (u_long) RedPtr.pbrgb[i + 14].Green +
+ (u_long) RedPtr.pbrgb[i + 15].Green) >> 4) * 59UL +
+ (((u_long) RedPtr.pbrgb[i].Blue +
+ (u_long) RedPtr.pbrgb[i + 1].Blue +
+ (u_long) RedPtr.pbrgb[i + 2].Blue +
+ (u_long) RedPtr.pbrgb[i + 3].Blue +
+ (u_long) RedPtr.pbrgb[i + 4].Blue +
+ (u_long) RedPtr.pbrgb[i + 5].Blue +
+ (u_long) RedPtr.pbrgb[i + 6].Blue +
+ (u_long) RedPtr.pbrgb[i + 7].Blue +
+ (u_long) RedPtr.pbrgb[i + 8].Blue +
+ (u_long) RedPtr.pbrgb[i + 9].Blue +
+ (u_long) RedPtr.pbrgb[i + 10].Blue +
+ (u_long) RedPtr.pbrgb[i + 11].Blue +
+ (u_long) RedPtr.pbrgb[i + 12].Blue +
+ (u_long) RedPtr.pbrgb[i + 13].Blue +
+ (u_long) RedPtr.pbrgb[i + 14].Blue +
+ (u_long) RedPtr.pbrgb[i + 15].Blue) >> 4) * 11UL) / 100UL);
+
+ if( bHiRight[1] < bHiRight[0] ) {
+ bHiRight[1] = bHiRight[0];
+ dwIndexRight = i;
+ }
+ }
+
+ /* Convert RGB to gray scale (Brightness), and average 16 pixels */
+ for( bHiLeft[1] = 0, i = dwIndexLeft = _NEG_PAGEWIDTH / 2;
+ i < _NEG_PAGEWIDTH600; i += 16 ) {
+ bHiLeft [0] =
+ (SANE_Byte)(((((u_long) RedPtr.pbrgb[i].Red +
+ (u_long) RedPtr.pbrgb[i + 1].Red +
+ (u_long) RedPtr.pbrgb[i + 2].Red +
+ (u_long) RedPtr.pbrgb[i + 3].Red +
+ (u_long) RedPtr.pbrgb[i + 4].Red +
+ (u_long) RedPtr.pbrgb[i + 5].Red +
+ (u_long) RedPtr.pbrgb[i + 6].Red +
+ (u_long) RedPtr.pbrgb[i + 7].Red +
+ (u_long) RedPtr.pbrgb[i + 8].Red +
+ (u_long) RedPtr.pbrgb[i + 9].Red +
+ (u_long) RedPtr.pbrgb[i + 10].Red +
+ (u_long) RedPtr.pbrgb[i + 11].Red +
+ (u_long) RedPtr.pbrgb[i + 12].Red +
+ (u_long) RedPtr.pbrgb[i + 13].Red +
+ (u_long) RedPtr.pbrgb[i + 14].Red +
+ (u_long) RedPtr.pbrgb[i + 15].Red) >> 4) * 30UL +
+ (((u_long) RedPtr.pbrgb[i].Green +
+ (u_long) RedPtr.pbrgb[i + 1].Green +
+ (u_long) RedPtr.pbrgb[i + 2].Green +
+ (u_long) RedPtr.pbrgb[i + 3].Green +
+ (u_long) RedPtr.pbrgb[i + 4].Green +
+ (u_long) RedPtr.pbrgb[i + 5].Green +
+ (u_long) RedPtr.pbrgb[i + 6].Green +
+ (u_long) RedPtr.pbrgb[i + 7].Green +
+ (u_long) RedPtr.pbrgb[i + 8].Green +
+ (u_long) RedPtr.pbrgb[i + 9].Green +
+ (u_long) RedPtr.pbrgb[i + 10].Green +
+ (u_long) RedPtr.pbrgb[i + 11].Green +
+ (u_long) RedPtr.pbrgb[i + 12].Green +
+ (u_long) RedPtr.pbrgb[i + 13].Green +
+ (u_long) RedPtr.pbrgb[i + 14].Green +
+ (u_long) RedPtr.pbrgb[i + 15].Green) >> 4) * 59UL +
+ (((u_long) RedPtr.pbrgb[i].Blue +
+ (u_long) RedPtr.pbrgb[i + 1].Blue +
+ (u_long) RedPtr.pbrgb[i + 2].Blue +
+ (u_long) RedPtr.pbrgb[i + 3].Blue +
+ (u_long) RedPtr.pbrgb[i + 4].Blue +
+ (u_long) RedPtr.pbrgb[i + 5].Blue +
+ (u_long) RedPtr.pbrgb[i + 6].Blue +
+ (u_long) RedPtr.pbrgb[i + 7].Blue +
+ (u_long) RedPtr.pbrgb[i + 8].Blue +
+ (u_long) RedPtr.pbrgb[i + 9].Blue +
+ (u_long) RedPtr.pbrgb[i + 10].Blue +
+ (u_long) RedPtr.pbrgb[i + 11].Blue +
+ (u_long) RedPtr.pbrgb[i + 12].Blue +
+ (u_long) RedPtr.pbrgb[i + 13].Blue +
+ (u_long) RedPtr.pbrgb[i + 14].Blue +
+ (u_long) RedPtr.pbrgb[i + 15].Blue) >> 4) * 11UL) / 100UL);
+
+ if( bHiLeft[1] < bHiLeft[0] ) {
+ bHiLeft[1] = bHiLeft[0];
+ dwIndexLeft = i;
+ }
+ }
+
+ if((bHiLeft[1] < 200) && (bHiRight[1] < 200)) {
+
+ if( bHiLeft[1] < bHiRight[1] )
+ dwIndex = dwIndexRight;
+ else
+ dwIndex = dwIndexLeft;
+ } else {
+ if( bHiLeft[1] > 200 )
+ dwIndex = dwIndexRight;
+ else
+ dwIndex = dwIndexLeft;
+ }
+
+ /* Get the hilight */
+ RedPtr.pusrgb = dev->bufs.b2.pSumRGB + dwIndex +
+ dev->regs.RD_Origin + _SHADING_BEGINX;
+
+ for( dwR = dwG = dwB = 0, i = 16; i--; RedPtr.pusrgb++ ) {
+ dwR += RedPtr.pusrgb->Red;
+ dwG += RedPtr.pusrgb->Green;
+ dwB += RedPtr.pusrgb->Blue;
+ }
+
+ dwR >>= 8;
+ dwG >>= 8;
+ dwB >>= 8;
+
+ if( dwR > dwG && dwR > dwB )
+ dev->shade.bGainHigh = (SANE_Byte)dwR; /* >> 4 for average, >> 4 to 8-bit */
+ else {
+ if( dwG > dwR && dwG > dwB )
+ dev->shade.bGainHigh = (SANE_Byte)dwG;
+ else
+ dev->shade.bGainHigh = (SANE_Byte)dwB;
+ }
+
+ dev->shade.bGainHigh = (SANE_Byte)(dev->shade.bGainHigh - 0x18);
+ dev->shade.bGainLow = (SANE_Byte)(dev->shade.bGainHigh - 0x10);
+
+ /* Reshading to get the new gain */
+ dev->shade.Hilight.Colors.Red = 0;
+ dev->shade.Hilight.Colors.Green = 0;
+ dev->shade.Hilight.Colors.Blue = 0;
+ dev->shade.Gain.Colors.Red++;
+ dev->shade.Gain.Colors.Green++;
+ dev->shade.Gain.Colors.Blue++;
+ dev->shade.fStop = SANE_FALSE;
+
+ RedPtr.pb = dev->bufs.b1.pShadingMap + dwIndex;
+ GreenPtr.pb = RedPtr.pb + _NEG_PAGEWIDTH600;
+ BluePtr.pb = GreenPtr.pb + _NEG_PAGEWIDTH600;
+
+ for( i = 16; i-- && !dev->shade.fStop;) {
+
+ dev->shade.fStop = SANE_TRUE;
+
+ u12shading_FillToDAC( dev, &dev->RegDACGain, &dev->shade.Gain );
+
+ u12io_DataToRegister( dev, REG_MODECONTROL, _ModeIdle );
+
+ dev->regs.RD_ScanControl = _SCAN_BYTEMODE;
+ u12hw_SelectLampSource( dev );
+
+ dev->regs.RD_ModeControl = _ModeScan;
+ dev->regs.RD_StepControl = _MOTOR0_SCANSTATE;
+ dev->regs.RD_Motor0Control = _FORWARD_MOTOR;
+
+ memset( dev->scanStates, 0, _SCANSTATE_BYTES );
+ dev->scanStates[1] = 0x77;
+
+ u12io_PutOnAllRegisters( dev );
+ _DODELAY( 50 );
+
+ if(u12io_ReadOneShadingLine( dev,
+ dev->bufs.b1.pShadingMap,_NEG_PAGEWIDTH600)) {
+
+ bHi[0] = u12shading_SumGains( RedPtr.pb, 32 );
+ bHi[1] = u12shading_SumGains( GreenPtr.pb, 32 );
+ bHi[2] = u12shading_SumGains( BluePtr.pb, 32 );
+
+ if( !bHi[0] || !bHi[1] || !bHi[2]) {
+ dev->shade.fStop = SANE_FALSE;
+ } else {
+
+ u12shading_AdjustGain( dev, _CHANNEL_RED, bHi[0] );
+ u12shading_AdjustGain( dev, _CHANNEL_GREEN, bHi[1] );
+ u12shading_AdjustGain( dev, _CHANNEL_BLUE, bHi[2] );
+ }
+ } else {
+ dev->shade.fStop = SANE_FALSE;
+ }
+ }
+
+ u12shading_FillToDAC( dev, &dev->RegDACGain, &dev->shade.Gain );
+
+ /* Set RGB Gain */
+ if( dwR && dwG && dwB ) {
+
+ if(dev->CCDID == _CCD_3797 || dev->DACType == _DA_ESIC ) {
+ dev->shade.pCcdDac->GainResize.Colors.Red =
+ (u_short)((u_long)bHi[0] * 100UL / dwR);
+ dev->shade.pCcdDac->GainResize.Colors.Green =
+ (u_short)((u_long)bHi[1] * 100UL / dwG);
+ dev->shade.pCcdDac->GainResize.Colors.Blue =
+ (u_short)((u_long)bHi[2] * 100UL / dwB);
+ } else {
+ dev->shade.pCcdDac->GainResize.Colors.Red =
+ (u_short)((u_long)bHi[0] * 90UL / dwR);
+ dev->shade.pCcdDac->GainResize.Colors.Green =
+ (u_short)((u_long)bHi[1] * 77UL / dwG);
+ dev->shade.pCcdDac->GainResize.Colors.Blue =
+ (u_short)((u_long)bHi[2] * 73UL / dwB);
+ }
+
+ dev->shade.DarkOffset.Colors.Red +=
+ (u_short)((dwR > bHi[0]) ? dwR - bHi[0] : 0);
+ dev->shade.DarkOffset.Colors.Green +=
+ (u_short)((dwG > bHi[1]) ? dwG - bHi[1] : 0);
+ dev->shade.DarkOffset.Colors.Blue +=
+ (u_short)((dwB > bHi[2]) ? dwB - bHi[2] : 0);
+
+ if( dev->DACType != _DA_ESIC && dev->CCDID != _CCD_3799 ) {
+ dev->shade.DarkOffset.Colors.Red =
+ (u_short)(dev->shade.DarkOffset.Colors.Red *
+ dev->shade.pCcdDac->GainResize.Colors.Red / 100UL);
+ dev->shade.DarkOffset.Colors.Green =
+ (u_short)(dev->shade.DarkOffset.Colors.Green *
+ dev->shade.pCcdDac->GainResize.Colors.Green / 100UL);
+ dev->shade.DarkOffset.Colors.Blue =
+ (u_short)(dev->shade.DarkOffset.Colors.Blue *
+ dev->shade.pCcdDac->GainResize.Colors.Blue / 100UL);
+ }
+ }
+
+ /* AdjustDark () */
+ dev->regs.RD_Origin = _SHADING_BEGINX;
+ dev->regs.RD_Pixels = 5400;
+}
+
+/** perform some adjustments according to the source (normal, transparency etc)
+ */
+static void u12tpa_FindCenterPointer( U12_Device *dev )
+{
+ u_long i;
+ u_long width;
+ u_long left;
+ u_long right;
+ RGBUShortDef *pwSum = dev->bufs.b2.pSumRGB;
+
+ if( dev->DataInf.dwScanFlag & _SCANDEF_Negative )
+ width = _NEG_PAGEWIDTH600;
+ else
+ width = _NEG_PAGEWIDTH600 - 94;
+
+ /* 2.54 cm tolerance */
+ left = _DATA_ORIGIN_X + _NEG_ORG_OFFSETX * 2 - 600;
+ right = _DATA_ORIGIN_X + _NEG_ORG_OFFSETX * 2 +
+ _NEG_PAGEWIDTH600 + 600;
+
+ for( i = 5400UL - left, pwSum = dev->bufs.b2.pSumRGB; i--; left++)
+ if( pwSum[left].Red > _NEG_EDGE_VALUE &&
+ pwSum[left].Green > _NEG_EDGE_VALUE &&
+ pwSum[left].Blue > _NEG_EDGE_VALUE)
+ break;
+
+ for( i = 5400UL - left, pwSum = dev->bufs.b2.pSumRGB; i--; right--)
+ if( pwSum[right].Red > _NEG_EDGE_VALUE &&
+ pwSum[right].Green > _NEG_EDGE_VALUE &&
+ pwSum[right].Blue > _NEG_EDGE_VALUE)
+ break;
+
+ if((right <= left) || ((right - left) < width)) {
+ if( dev->DataInf.dwScanFlag & _SCANDEF_Negative )
+ dev->scan.negBegin = _DATA_ORIGIN_X + _NEG_ORG_OFFSETX * 2;
+ else
+ dev->scan.posBegin = _DATA_ORIGIN_X + _POS_ORG_OFFSETX * 2;
+ } else {
+ if( dev->DataInf.dwScanFlag & _SCANDEF_Negative )
+ dev->scan.negBegin = (right + left) / 2UL - _NEG_PAGEWIDTH;
+ else
+ dev->scan.posBegin = (right + left) / 2UL - _POS_PAGEWIDTH;
+ }
+}
+
+/* END U12_TPA.C ............................................................*/
diff --git a/backend/u12.c b/backend/u12.c
new file mode 100644
index 0000000..8401a9f
--- /dev/null
+++ b/backend/u12.c
@@ -0,0 +1,1869 @@
+/** @file u12.c
+ * @brief SANE backend for USB scanner, based on Plusteks' ASIC P98003 and
+ * the GeneSys Logic GL640 parallel-port to USB bridge.
+ *
+ * Based on source aquired from Plustek<br>
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de><br>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - enabled other scan-modes
+ * - increased default gamma to 1.5
+ *.
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_VERSION "0.02-11"
+#define BACKEND_NAME u12
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+#include "../include/sane/sanei_usb.h"
+
+#define ALL_MODES
+
+#include "u12-scanner.h"
+#include "u12-hwdef.h"
+#include "u12.h"
+
+/*********************** the debug levels ************************************/
+
+#define _DBG_FATAL 0
+#define _DBG_ERROR 1
+#define _DBG_WARNING 3
+#define _DBG_INFO 5
+#define _DBG_PROC 7
+#define _DBG_SANE_INIT 10
+#define _DBG_IO 128
+#define _DBG_READ 255
+
+/* uncomment this for testing... */
+/*#define _FAKE_DEVICE
+ */
+/*****************************************************************************/
+
+#define _SECTION "[usb]"
+#define _DEFAULT_DEVICE "auto"
+
+/* including the "worker" code... */
+#include "u12-io.c"
+#include "u12-ccd.c"
+#include "u12-hw.c"
+#include "u12-motor.c"
+#include "u12-image.c"
+#include "u12-map.c"
+#include "u12-shading.c"
+#include "u12-tpa.c"
+#include "u12-if.c"
+
+/************************** global vars **************************************/
+
+static int num_devices;
+static U12_Device *first_dev;
+static U12_Scanner *first_handle;
+static const SANE_Device **devlist = 0;
+static unsigned long tsecs = 0;
+static SANE_Bool cancelRead;
+
+#ifdef ALL_MODES
+static ModeParam mode_params[] =
+{
+ {0, 1, COLOR_BW},
+ {0, 8, COLOR_256GRAY},
+ {1, 8, COLOR_TRUE24},
+ {1, 16, COLOR_TRUE42}
+};
+
+static const SANE_String_Const mode_list[] =
+{
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_I18N("Color 36"),
+ NULL
+};
+
+static const SANE_String_Const src_list[] =
+{
+ SANE_I18N("Normal"),
+ SANE_I18N("Transparency"),
+ SANE_I18N("Negative"),
+ NULL
+};
+#endif
+
+static const SANE_Range percentage_range =
+{
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+/* authorization stuff */
+static SANE_Auth_Callback auth = NULL;
+
+/****************************** the backend... *******************************/
+
+#define _YN(x) (x?"yes":"no")
+
+/**
+ * function to display the configuration options for the current device
+ * @param cnf - pointer to the configuration structure whose content should be
+ * displayed
+ */
+static void show_cnf( pCnfDef cnf )
+{
+ DBG( _DBG_SANE_INIT,"Device configuration:\n" );
+ DBG( _DBG_SANE_INIT,"device name : >%s<\n",cnf->devName );
+ DBG( _DBG_SANE_INIT,"USB-ID : >%s<\n",cnf->usbId );
+ DBG( _DBG_SANE_INIT,"warmup : %ds\n", cnf->adj.warmup );
+ DBG( _DBG_SANE_INIT,"lampOff : %d\n", cnf->adj.lampOff );
+ DBG( _DBG_SANE_INIT,"lampOffOnEnd : %s\n", _YN(cnf->adj.lampOffOnEnd ));
+ DBG( _DBG_SANE_INIT,"red Gamma : %.2f\n",cnf->adj.rgamma );
+ DBG( _DBG_SANE_INIT,"green Gamma : %.2f\n",cnf->adj.ggamma );
+ DBG( _DBG_SANE_INIT,"blue Gamma : %.2f\n",cnf->adj.bgamma );
+ DBG( _DBG_SANE_INIT,"gray Gamma : %.2f\n",cnf->adj.graygamma );
+ DBG( _DBG_SANE_INIT,"---------------------\n" );
+}
+
+/** Calls the device specific stop and close functions.
+ * @param dev - pointer to the device specific structure
+ * @return The function always returns SANE_STATUS_GOOD
+ */
+static SANE_Status drvClose( U12_Device *dev )
+{
+ if( dev->fd >= 0 ) {
+
+ DBG( _DBG_INFO, "drvClose()\n" );
+
+ if( 0 != tsecs ) {
+ DBG( _DBG_INFO, "TIME END 1: %lus\n", time(NULL)-tsecs);
+ }
+
+ /* don't check the return values, simply do it */
+ u12if_stopScan( dev );
+ u12if_close ( dev );
+ }
+ dev->fd = -1;
+ return SANE_STATUS_GOOD;
+}
+
+/** as the name says, close our pipes
+ * @param scanner -
+ * @return
+ */
+static SANE_Status drvClosePipes( U12_Scanner *scanner )
+{
+ if( scanner->r_pipe >= 0 ) {
+
+ DBG( _DBG_PROC, "drvClosePipes(r_pipe)\n" );
+ close( scanner->r_pipe );
+ scanner->r_pipe = -1;
+ }
+ if( scanner->w_pipe >= 0 ) {
+
+ DBG( _DBG_PROC, "drvClosePipes(w_pipe)\n" );
+ close( scanner->w_pipe );
+ scanner->w_pipe = -1;
+ }
+
+ return SANE_STATUS_EOF;
+}
+
+#ifdef ALL_MODES
+/** according to the mode and source we return the corresponding mode list
+ */
+static pModeParam getModeList( U12_Scanner *scanner )
+{
+ pModeParam mp = mode_params;
+
+ /* the transparency/negative mode supports only gray and color
+ */
+ if( 0 != scanner->val[OPT_EXT_MODE].w ) {
+ mp = &mp[_TPAModeSupportMin];
+ }
+
+ return mp;
+}
+#endif
+
+/** goes through a string list and returns the start-address of the string
+ * that has been found, or NULL on error
+ */
+static const SANE_String_Const
+*search_string_list( const SANE_String_Const *list, SANE_String value )
+{
+ while( *list != NULL && strcmp(value, *list) != 0 )
+ ++list;
+
+ if( *list == NULL )
+ return NULL;
+
+ return list;
+}
+
+/**
+ */
+static void sig_chldhandler( int signo )
+{
+ DBG( _DBG_PROC, "(SIG) Child is down (signal=%d)\n", signo );
+}
+
+/** signal handler to kill the child process
+ */
+static RETSIGTYPE reader_process_sigterm_handler( int signo )
+{
+ DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo );
+ _exit( SANE_STATUS_GOOD );
+}
+
+static RETSIGTYPE usb_reader_process_sigterm_handler( int signo )
+{
+ DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo );
+ cancelRead = SANE_TRUE;
+}
+
+static RETSIGTYPE sigalarm_handler( int signo )
+{
+ _VAR_NOT_USED( signo );
+ DBG( _DBG_PROC, "ALARM!!!\n" );
+}
+
+/** executed as a child process
+ * read the data from the driver and send them to the parent process
+ */
+static int reader_process( void *args )
+{
+ int line;
+ unsigned char *buf;
+ unsigned long data_length;
+ struct SIGACTION act;
+ sigset_t ignore_set;
+ SANE_Status status;
+
+ U12_Scanner *scanner = (U12_Scanner *)args;
+
+ if( sanei_thread_is_forked()) {
+ DBG( _DBG_PROC, "reader_process started (forked)\n" );
+ close( scanner->r_pipe );
+ scanner->r_pipe = -1;
+ } else {
+ DBG( _DBG_PROC, "reader_process started (as thread)\n" );
+ }
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGTERM );
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset ( &ignore_set, SIGUSR2 );
+#endif
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ cancelRead = SANE_FALSE;
+
+ /* install the signal handler */
+ memset( &act, 0, sizeof (act));
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = reader_process_sigterm_handler;
+ sigaction( SIGTERM, &act, 0 );
+
+ act.sa_handler = usb_reader_process_sigterm_handler;
+ sigaction( SIGUSR1, &act, 0 );
+
+ data_length = scanner->params.lines * scanner->params.bytes_per_line;
+
+ DBG( _DBG_PROC, "reader_process:"
+ "starting to READ data (%lu bytes)\n", data_length );
+ DBG( _DBG_PROC, "buf = 0x%08lx\n", (unsigned long)scanner->buf );
+
+ if( NULL == scanner->buf ) {
+ DBG( _DBG_FATAL, "NULL Pointer !!!!\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* here we read all data from the scanner... */
+ buf = scanner->buf;
+ status = u12if_prepare( scanner->hw );
+
+ if( SANE_STATUS_GOOD == status ) {
+
+ for( line = 0; line < scanner->params.lines; line++ ) {
+
+ status = u12if_readLine( scanner->hw, buf );
+ if( SANE_STATUS_GOOD != status ) {
+ break;
+ }
+
+ write( scanner->w_pipe, buf, scanner->params.bytes_per_line );
+ buf += scanner->params.bytes_per_line;
+ }
+ }
+
+ close( scanner->w_pipe );
+ scanner->w_pipe = -1;
+
+ /* on error, there's no need to clean up, as this is done by the parent */
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "read failed, status = %i\n", (int)status );
+ return status;
+ }
+
+ DBG( _DBG_PROC, "reader_process: finished reading data\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** stop the current scan process
+ */
+static SANE_Status do_cancel( U12_Scanner *scanner, SANE_Bool closepipe )
+{
+ struct SIGACTION act;
+ SANE_Pid res;
+
+ DBG( _DBG_PROC,"do_cancel\n" );
+
+ scanner->scanning = SANE_FALSE;
+
+ if( scanner->reader_pid != -1 ) {
+
+ DBG( _DBG_PROC, ">>>>>>>> killing reader_process <<<<<<<<\n" );
+
+ cancelRead = SANE_TRUE;
+
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+ sigaction( SIGALRM, &act, 0 );
+
+ /* kill our child process and wait until done */
+ sanei_thread_sendsig( scanner->reader_pid, SIGUSR1 );
+
+ /* give'em 10 seconds 'til done...*/
+ alarm(10);
+ res = sanei_thread_waitpid( scanner->reader_pid, 0 );
+ alarm(0);
+
+ if( res != scanner->reader_pid ) {
+ DBG( _DBG_PROC,"sanei_thread_waitpid() failed !\n");
+
+ /* do it the hard way...*/
+#ifdef USE_PTHREAD
+ sanei_thread_kill( scanner->reader_pid );
+#else
+ sanei_thread_sendsig( scanner->reader_pid, SIGKILL );
+#endif
+ }
+ scanner->reader_pid = -1;
+ DBG( _DBG_PROC, "reader_process killed\n");
+
+ if( scanner->hw->fd >= 0 ) {
+ u12hw_CancelSequence( scanner->hw );
+ }
+#ifndef HAVE_SETITIMER
+ u12hw_StartLampTimer( scanner->hw );
+#endif
+ }
+
+ if( SANE_TRUE == closepipe ) {
+ drvClosePipes( scanner );
+ }
+
+ drvClose( scanner->hw );
+
+ if( tsecs != 0 ) {
+ DBG( _DBG_INFO, "TIME END 2: %lus\n", time(NULL)-tsecs);
+ tsecs = 0;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/** initialize the options for the backend according to the device we have
+ */
+static SANE_Status init_options( U12_Scanner *s )
+{
+ int i;
+
+ memset( s->opt, 0, sizeof(s->opt));
+
+ for( i = 0; i < NUM_OPTIONS; ++i ) {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+ s->opt[OPT_MODE_GROUP].name = "scanmode-group";
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+#ifdef ALL_MODES
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = 32;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = COLOR_TRUE24;
+
+ /* scan source */
+ s->opt[OPT_EXT_MODE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_EXT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_EXT_MODE].size = 32;
+ s->opt[OPT_EXT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_EXT_MODE].constraint.string_list = src_list;
+ s->val[OPT_EXT_MODE].w = 0; /* Normal */
+#endif
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = 0;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = "geometry-group";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ u12map_InitGammaSettings( s->hw );
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR].wa = &(s->hw->gamma_table[0][0]);
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &(s->hw->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR].size = s->hw->gamma_length * sizeof(SANE_Word);
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &(s->hw->gamma_table[1][0]);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->hw->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_R].size = s->hw->gamma_length * sizeof(SANE_Word);
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &(s->hw->gamma_table[2][0]);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->hw->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_G].size = s->hw->gamma_length * sizeof(SANE_Word);
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &(s->hw->gamma_table[3][0]);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->hw->gamma_range);
+ s->opt[OPT_GAMMA_VECTOR_B].size = s->hw->gamma_length * sizeof(SANE_Word);
+
+ /* GAMMA stuff is disabled per default */
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+#ifdef ALL_MODES
+ /* disable extended mode list for devices without TPA */
+ if( SANE_FALSE == s->hw->Tpa ) {
+ s->opt[OPT_EXT_MODE].cap |= SANE_CAP_INACTIVE;
+ }
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+/** Function to retrieve the vendor and product id from a given string
+ * @param src - string, that should be investigated
+ * @param dest - pointer to a string to receive the USB ID
+ */
+static void decodeUsbIDs( char *src, char **dest )
+{
+ const char *name;
+ char *tmp = *dest;
+ int len = strlen(_SECTION);
+
+ if( isspace(src[len])) {
+ strncpy( tmp, &src[len+1], (strlen(src)-(len+1)));
+ tmp[(strlen(src)-(len+1))] = '\0';
+ }
+
+ name = tmp;
+ name = sanei_config_skip_whitespace( name );
+
+ if( '\0' == name[0] ) {
+ DBG( _DBG_SANE_INIT, "next device uses autodetection\n" );
+ } else {
+
+ u_short pi = 0, vi = 0;
+
+ if( *name ) {
+
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+ vi = strtol( tmp, 0, 0 );
+ free( tmp );
+ }
+ }
+
+ name = sanei_config_skip_whitespace( name );
+ if( *name ) {
+
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+ pi = strtol( tmp, 0, 0 );
+ free( tmp );
+ }
+ }
+
+ /* create what we need to go through our device list...*/
+ sprintf( *dest, "0x%04X-0x%04X", vi, pi );
+ DBG( _DBG_SANE_INIT, "next device is a USB device (%s)\n", *dest );
+ }
+}
+
+#define _INT 0
+#define _FLOAT 1
+
+/** function to decode an value and give it back to the caller.
+ * @param src - pointer to the source string to check
+ * @param opt - string that keeps the option name to check src for
+ * @param what - _FLOAT or _INT
+ * @param result - pointer to the var that should receive our result
+ * @param def - default value that result should be in case of any error
+ * @return The function returns SANE_TRUE if the option has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool decodeVal( char *src, char *opt,
+ int what, void *result, void *def )
+{
+ char *tmp, *tmp2;
+ const char *name;
+
+ /* skip the option string */
+ name = (const char*)&src[strlen("option")];
+
+ /* get the name of the option */
+ name = sanei_config_get_string( name, &tmp );
+
+ if( tmp ) {
+
+ /* on success, compare wiht the given one */
+ if( 0 == strcmp( tmp, opt )) {
+
+ DBG( _DBG_SANE_INIT, "Decoding option >%s<\n", opt );
+
+ if( _INT == what ) {
+
+ /* assign the default value for this option... */
+ *((int*)result) = *((int*)def);
+
+ if( *name ) {
+
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string( name, &tmp2 );
+
+ if( tmp2 ) {
+ *((int*)result) = strtol( tmp2, 0, 0 );
+ free( tmp2 );
+ }
+ }
+ free( tmp );
+ return SANE_TRUE;
+
+ } else if( _FLOAT == what ) {
+
+ /* assign the default value for this option... */
+ *((double*)result) = *((double*)def);
+
+ if( *name ) {
+
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string( name, &tmp2 );
+
+ if( tmp2 ) {
+ *((double*)result) = strtod( tmp2, 0 );
+ free( tmp2 );
+ }
+ }
+ free( tmp );
+ return SANE_TRUE;
+ }
+ }
+ free( tmp );
+ }
+
+ return SANE_FALSE;
+}
+
+/** function to retrive the device name of a given string
+ * @param src - string that keeps the option name to check src for
+ * @param dest - pointer to the string, that should receive the detected
+ * devicename
+ * @return The function returns SANE_TRUE if the devicename has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool decodeDevName( char *src, char *dest )
+{
+ char *tmp;
+ const char *name;
+
+ if( 0 == strncmp( "device", src, 6 )) {
+
+ name = (const char*)&src[strlen("device")];
+ name = sanei_config_skip_whitespace( name );
+
+ DBG( _DBG_SANE_INIT, "Decoding device name >%s<\n", name );
+
+ if( *name ) {
+ name = sanei_config_get_string( name, &tmp );
+ if( tmp ) {
+
+ strcpy( dest, tmp );
+ free( tmp );
+ return SANE_TRUE;
+ }
+ }
+ }
+
+ return SANE_FALSE;
+}
+
+/** attach a device to the backend
+ */
+static SANE_Status attach( const char *dev_name,
+ pCnfDef cnf, U12_Device **devp )
+{
+ int result;
+ int handle;
+ U12_Device *dev;
+
+ DBG( _DBG_SANE_INIT, "attach (%s, %p, %p)\n",
+ dev_name, (void *)cnf, (void *)devp);
+
+ /* already attached ?*/
+ for( dev = first_dev; dev; dev = dev->next ) {
+
+ if( 0 == strcmp( dev->sane.name, dev_name )) {
+ if( devp )
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* allocate some memory for the device */
+ dev = malloc( sizeof (*dev));
+ if( NULL == dev )
+ return SANE_STATUS_NO_MEM;
+
+ /* assign all the stuff we need fo this device... */
+ memset(dev, 0, sizeof (*dev));
+
+ dev->fd = -1;
+ dev->name = strdup(dev_name); /* hold it double to avoid */
+ dev->sane.name = dev->name; /* compiler warnings */
+ dev->sane.vendor = "Plustek";
+ dev->sane.model = "U12/1212U";
+ dev->sane.type = SANE_I18N ("flatbed scanner");
+ dev->initialized = SANE_FALSE;
+
+ memcpy( &dev->adj, &cnf->adj, sizeof(AdjDef));
+ show_cnf( cnf );
+
+ strncpy( dev->usbId, cnf->usbId, _MAX_ID_LEN );
+
+ /* go ahead and open the scanner device */
+ handle = u12if_open( dev );
+ if( handle < 0 ) {
+ DBG( _DBG_ERROR,"open failed: %d\n", handle );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* okay, so assign the handle... */
+ dev->fd = handle;
+
+ /* now check what we have */
+ result = u12if_getCaps( dev );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "u12if_getCaps() failed(%d)\n", result);
+ u12if_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* save the info we got from the driver */
+ DBG( _DBG_INFO, "Scanner information:\n" );
+ DBG( _DBG_INFO, "Vendor : %s\n", dev->sane.vendor );
+ DBG( _DBG_INFO, "Model : %s\n", dev->sane.model );
+ DBG( _DBG_INFO, "Flags : 0x%08lx\n", dev->caps.flag );
+
+ if( SANE_STATUS_GOOD != u12if_SetupBuffer( dev )) {
+ DBG( _DBG_ERROR, "u12if_SetupBuffer() failed\n" );
+ u12if_close( dev );
+ return SANE_STATUS_NO_MEM;
+ }
+
+ drvClose( dev );
+ DBG( _DBG_SANE_INIT, "attach: model = >%s<\n", dev->sane.model );
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if( devp )
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** function to preset a configuration structure
+ * @param cnf - pointer to the structure that should be initialized
+ */
+static void init_config_struct( pCnfDef cnf )
+{
+ memset( cnf, 0, sizeof(CnfDef));
+
+ cnf->adj.warmup = -1;
+ cnf->adj.lampOff = -1;
+ cnf->adj.lampOffOnEnd = -1;
+
+ cnf->adj.graygamma = 1.0;
+ cnf->adj.rgamma = 1.0;
+ cnf->adj.ggamma = 1.0;
+ cnf->adj.bgamma = 1.0;
+}
+
+/** intialize the backend
+ */
+SANE_Status sane_init( SANE_Int *version_code, SANE_Auth_Callback authorize )
+{
+ char str[PATH_MAX] = _DEFAULT_DEVICE;
+ CnfDef config;
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT();
+
+ sanei_usb_init();
+ sanei_thread_init();
+
+ DBG( _DBG_INFO, "U12 backend V"
+ BACKEND_VERSION", part of "PACKAGE " " VERSION "\n");
+
+ /* do some presettings... */
+ auth = authorize;
+ first_dev = NULL;
+ first_handle = NULL;
+ num_devices = 0;
+
+ /* initialize the configuration structure */
+ init_config_struct( &config );
+
+ if( version_code != NULL )
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open( U12_CONFIG_FILE );
+
+ /* default to _DEFAULT_DEVICE instead of insisting on config file */
+ if( NULL == fp ) {
+ return attach( _DEFAULT_DEVICE, &config, 0 );
+ }
+
+ while( sanei_config_read( str, sizeof(str), fp)) {
+
+ DBG( _DBG_SANE_INIT, ">%s<\n", str );
+ if( str[0] == '#') /* ignore line comments */
+ continue;
+
+ len = strlen(str);
+ if( 0 == len )
+ continue; /* ignore empty lines */
+
+ /* check for options */
+ if( 0 == strncmp(str, "option", 6)) {
+
+ int ival;
+ double dval;
+
+ ival = -1;
+ decodeVal( str, "warmup", _INT, &config.adj.warmup, &ival);
+ decodeVal( str, "lampOff", _INT, &config.adj.lampOff, &ival);
+ decodeVal( str, "lOffOnEnd", _INT, &config.adj.lampOffOnEnd,&ival);
+
+ ival = 0;
+
+ dval = 1.5;
+ decodeVal( str, "grayGamma", _FLOAT, &config.adj.graygamma,&dval);
+ decodeVal( str, "redGamma", _FLOAT, &config.adj.rgamma, &dval );
+ decodeVal( str, "greenGamma", _FLOAT, &config.adj.ggamma, &dval );
+ decodeVal( str, "blueGamma", _FLOAT, &config.adj.bgamma, &dval );
+ continue;
+
+ /* check for sections: */
+ } else if( 0 == strncmp( str, _SECTION, strlen(_SECTION))) {
+
+ char *tmp;
+
+ /* new section, try and attach previous device */
+ if( config.devName[0] != '\0' ) {
+ attach( config.devName, &config, 0 );
+ } else {
+ if( first_dev != NULL ) {
+ DBG( _DBG_WARNING, "section contains no device name,"
+ " ignored!\n" );
+ }
+ }
+
+ /* re-initialize the configuration structure */
+ init_config_struct( &config );
+
+ tmp = config.usbId;
+ decodeUsbIDs( str, &tmp );
+
+ DBG( _DBG_SANE_INIT, "... next device\n" );
+ continue;
+
+ } else if( SANE_TRUE == decodeDevName( str, config.devName )) {
+ continue;
+ }
+
+ /* ignore other stuff... */
+ DBG( _DBG_SANE_INIT, "ignoring >%s<\n", str );
+ }
+ fclose( fp );
+
+ /* try to attach the last device in the config file... */
+ if( config.devName[0] != '\0' )
+ attach( config.devName, &config, 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+/** cleanup the backend...
+ */
+void sane_exit( void )
+{
+ U12_Device *dev, *next;
+
+ DBG( _DBG_SANE_INIT, "sane_exit\n" );
+
+ for( dev = first_dev; dev; ) {
+
+ next = dev->next;
+
+ u12if_shutdown( dev );
+
+ /*
+ * we're doin' this to avoid compiler warnings as dev->sane.name
+ * is defined as const char*
+ */
+ if( dev->sane.name )
+ free( dev->name );
+
+ if( dev->res_list )
+ free( dev->res_list );
+
+ free( dev );
+ dev = next;
+ }
+
+ if( devlist )
+ free( devlist );
+
+ devlist = NULL;
+ auth = NULL;
+ first_dev = NULL;
+ first_handle = NULL;
+}
+
+/** return a list of all devices
+ */
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only )
+{
+ int i;
+ U12_Device *dev;
+
+ DBG(_DBG_SANE_INIT, "sane_get_devices (%p, %ld)\n",
+ (void *)device_list, (long) local_only);
+
+ /* already called, so cleanup */
+ if( devlist )
+ free( devlist );
+
+ devlist = malloc((num_devices + 1) * sizeof (devlist[0]));
+ if ( NULL == devlist )
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for( dev = first_dev; i < num_devices; dev = dev->next )
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+/** open the sane device
+ */
+SANE_Status sane_open( SANE_String_Const devicename, SANE_Handle* handle )
+{
+ SANE_Status status;
+ U12_Device *dev;
+ U12_Scanner *s;
+ CnfDef config;
+
+ DBG( _DBG_SANE_INIT, "sane_open - %s\n", devicename );
+
+ if( devicename[0] ) {
+ for( dev = first_dev; dev; dev = dev->next ) {
+ if( strcmp( dev->sane.name, devicename ) == 0 )
+ break;
+ }
+
+ if( !dev ) {
+
+ memset( &config, 0, sizeof(CnfDef));
+
+ status = attach( devicename, &config, &dev );
+ if( SANE_STATUS_GOOD != status )
+ return status;
+ }
+ } else {
+ /* empty devicename -> use first device */
+ dev = first_dev;
+ }
+
+ if( !dev )
+ return SANE_STATUS_INVAL;
+
+ s = malloc (sizeof (*s));
+ if( NULL == s )
+ return SANE_STATUS_NO_MEM;
+
+ memset(s, 0, sizeof (*s));
+ s->r_pipe = -1;
+ s->w_pipe = -1;
+ s->hw = dev;
+ s->scanning = SANE_FALSE;
+
+ init_options( s );
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+void sane_close( SANE_Handle handle )
+{
+ U12_Scanner *prev, *s;
+
+ DBG( _DBG_SANE_INIT, "sane_close\n" );
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+
+ for( s = first_handle; s; s = s->next ) {
+ if( s == handle )
+ break;
+ prev = s;
+ }
+
+ if( !s ) {
+ DBG( _DBG_ERROR, "close: invalid handle %p\n", handle);
+ return;
+ }
+
+ drvClosePipes( s );
+
+ if( NULL != s->buf )
+ free(s->buf);
+
+ if( NULL != s->hw->bufs.b1.pReadBuf )
+ free( s->hw->bufs.b1.pReadBuf );
+
+ if( NULL != s->hw->shade.pHilight )
+ free( s->hw->shade.pHilight );
+
+ if( NULL != s->hw->scaleBuf )
+ free( s->hw->scaleBuf );
+
+ drvClose( s->hw );
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ free(s);
+}
+
+/** return or set the parameter values, also do some checks
+ */
+SANE_Status sane_control_option( SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value,
+ SANE_Int * info)
+{
+ U12_Scanner *s = (U12_Scanner *)handle;
+ SANE_Status status;
+ const SANE_String_Const *optval;
+#ifdef ALL_MODES
+ pModeParam mp;
+ int idx;
+#endif
+ int scanmode;
+
+ if ( s->scanning )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if((option < 0) || (option >= NUM_OPTIONS))
+ return SANE_STATUS_INVAL;
+
+ if( NULL != info )
+ *info = 0;
+
+ switch( action ) {
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option) {
+ case OPT_PREVIEW:
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_CUSTOM_GAMMA:
+ *(SANE_Word *)value = s->val[option].w;
+ break;
+
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *)value =
+ (s->val[option].w << SANE_FIXED_SCALE_SHIFT);
+ break;
+
+#ifdef ALL_MODES
+ case OPT_MODE:
+ case OPT_EXT_MODE:
+ strcpy ((char *) value,
+ s->opt[option].constraint.string_list[s->val[option].w]);
+ break;
+#endif
+
+ /* word array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy( value, s->val[option].wa, s->opt[option].size );
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ status = sanei_constrain_value( s->opt + option, value, info );
+ if( SANE_STATUS_GOOD != status )
+ return status;
+
+ optval = NULL;
+ if( SANE_CONSTRAINT_STRING_LIST == s->opt[option].constraint_type ) {
+
+ optval = search_string_list( s->opt[option].constraint.string_list,
+ (char *) value);
+ if( NULL == optval )
+ return SANE_STATUS_INVAL;
+ }
+
+ switch (option) {
+
+ case OPT_RESOLUTION: {
+ int n;
+ int min_d = s->hw->res_list[s->hw->res_list_size - 1];
+ int v = *(SANE_Word *)value;
+ int best = v;
+
+ for( n = 0; n < s->hw->res_list_size; n++ ) {
+ int d = abs(v - s->hw->res_list[n]);
+
+ if( d < min_d ) {
+ min_d = d;
+ best = s->hw->res_list[n];
+ }
+ }
+
+ s->val[option].w = (SANE_Word)best;
+
+ if( v != best )
+ *(SANE_Word *)value = best;
+
+ if( NULL != info ) {
+ if( v != best )
+ *info |= SANE_INFO_INEXACT;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+ }
+
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *)value;
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_CUSTOM_GAMMA:
+ s->val[option].w = *(SANE_Word *)value;
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+#ifdef ALL_MODES
+ mp = getModeList( s );
+ scanmode = mp[s->val[OPT_MODE].w].scanmode;
+#else
+ scanmode = COLOR_TRUE24;
+#endif
+
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ if( SANE_TRUE == s->val[option].w ) {
+
+ if( scanmode == COLOR_256GRAY ) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ } else {
+
+ u12map_InitGammaSettings( s->hw );
+
+ if( scanmode == COLOR_256GRAY ) {
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ break;
+
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ s->val[option].w =
+ ((*(SANE_Word *)value) >> SANE_FIXED_SCALE_SHIFT);
+ break;
+
+#ifdef ALL_MODES
+ case OPT_MODE:
+ idx = (optval - mode_list);
+ mp = getModeList( s );
+
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+
+ if( mp[idx].scanmode == COLOR_BW ) {
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ if( s->val[OPT_CUSTOM_GAMMA].w &&
+ !(s->opt[OPT_CUSTOM_GAMMA].cap & SANE_CAP_INACTIVE)) {
+
+ if( mp[idx].scanmode == COLOR_256GRAY ) {
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ s->val[option].w = optval - s->opt[option].constraint.string_list;
+ break;
+
+ case OPT_EXT_MODE: {
+ s->val[option].w = optval - s->opt[option].constraint.string_list;
+
+ /*
+ * change the area and mode_list when changing the source
+ */
+ if( s->val[option].w == 0 ) {
+
+ s->hw->dpi_range.min = _DEF_DPI;
+
+ s->hw->x_range.max = SANE_FIX(s->hw->max_x);
+ s->hw->y_range.max = SANE_FIX(s->hw->max_y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
+
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = COLOR_TRUE24;
+
+ } else {
+
+ s->hw->dpi_range.min = _TPAMinDpi;
+
+ if( s->val[option].w == 1 ) {
+ s->hw->x_range.max = SANE_FIX(_TP_X);
+ s->hw->y_range.max = SANE_FIX(_TP_Y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TP_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TP_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_TP_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_TP_BRY);
+
+ } else {
+ s->hw->x_range.max = SANE_FIX(_NEG_X);
+ s->hw->y_range.max = SANE_FIX(_NEG_Y);
+ s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_NEG_TLX);
+ s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_NEG_TLY);
+ s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_NEG_BRX);
+ s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_NEG_BRY);
+ }
+ s->opt[OPT_MODE].constraint.string_list =
+ &mode_list[_TPAModeSupportMin];
+ s->val[OPT_MODE].w = 0; /* COLOR_24 is the default */
+ }
+
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+#endif
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy( s->val[option].wa, value, s->opt[option].size );
+ u12map_CheckGammaSettings(s->hw);
+ if( NULL != info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** according to the option number, return a pointer to a descriptor
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor( SANE_Handle handle, SANE_Int option )
+{
+ U12_Scanner *s = (U12_Scanner *)handle;
+
+ if((option < 0) || (option >= NUM_OPTIONS))
+ return NULL;
+
+ return &(s->opt[option]);
+}
+
+/** return the current parameter settings
+ */
+SANE_Status sane_get_parameters( SANE_Handle handle, SANE_Parameters *params )
+{
+ int ndpi;
+#ifdef ALL_MODES
+ pModeParam mp;
+#endif
+ U12_Scanner *s = (U12_Scanner *)handle;
+
+ /* if we're called from within, calc best guess
+ * do the same, if sane_get_parameters() is called
+ * by a frontend before sane_start() is called
+ */
+ if((NULL == params) || (s->scanning != SANE_TRUE)) {
+
+#ifdef ALL_MODES
+ mp = getModeList( s );
+#endif
+ memset( &s->params, 0, sizeof (SANE_Parameters));
+
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ s->params.pixels_per_line = SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / _MM_PER_INCH * ndpi;
+
+ s->params.lines = SANE_UNFIX( s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / _MM_PER_INCH * ndpi;
+
+ /* pixels_per_line seems to be 8 * n. */
+ /* s->params.pixels_per_line = s->params.pixels_per_line & ~7; debug only */
+
+ s->params.last_frame = SANE_TRUE;
+#ifdef ALL_MODES
+ s->params.depth = mp[s->val[OPT_MODE].w].depth;
+#else
+ s->params.depth = 8;
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+#endif
+
+#ifdef ALL_MODES
+ if( mp[s->val[OPT_MODE].w].color ) {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ } else {
+ s->params.format = SANE_FRAME_GRAY;
+ if (s->params.depth == 1)
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ else
+ s->params.bytes_per_line = s->params.pixels_per_line *
+ s->params.depth / 8;
+ }
+#endif
+
+ /* if sane_get_parameters() was called before sane_start() */
+ /* pass new values to the caller */
+ if ((NULL != params) && (s->scanning != SANE_TRUE))
+ *params = s->params;
+ } else
+ *params = s->params;
+
+ return SANE_STATUS_GOOD;
+}
+
+/** initiate the scan process
+ */
+SANE_Status sane_start( SANE_Handle handle )
+{
+ U12_Scanner *s = (U12_Scanner *)handle;
+ U12_Device *dev;
+#ifdef ALL_MODES
+ ModeParam *mp;
+#endif
+ int result;
+ int ndpi;
+ int left, top;
+ int width, height;
+ int scanmode;
+ int fds[2];
+ double dpi_x, dpi_y;
+ ImgDef image;
+ SANE_Status status;
+ SANE_Word tmp;
+
+ DBG( _DBG_SANE_INIT, "sane_start\n" );
+ if( s->scanning ) {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = sane_get_parameters (handle, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DBG( _DBG_ERROR, "sane_get_parameters failed\n" );
+ return status;
+ }
+
+ dev = s->hw;
+
+ /* open the driver and get some information about the scanner
+ */
+ dev->fd = u12if_open( dev );
+ if( dev->fd < 0 ) {
+ DBG( _DBG_ERROR,"sane_start: open failed: %d\n", errno );
+
+ if( errno == EBUSY )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ tsecs = 0;
+
+ result = u12if_getCaps( dev );
+ if( result < 0 ) {
+ DBG( _DBG_ERROR, "u12if_getCaps() failed(%d)\n", result);
+ u12if_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* All ready to go. Set image def and see what the scanner
+ * says for crop info.
+ */
+ ndpi = s->val[OPT_RESOLUTION].w;
+
+ /* exchange the values as we can't deal with negative heights and so on...*/
+ tmp = s->val[OPT_TL_X].w;
+ if( tmp > s->val[OPT_BR_X].w ) {
+ DBG( _DBG_INFO, "exchanging BR-X - TL-X\n" );
+ s->val[OPT_TL_X].w = s->val[OPT_BR_X].w;
+ s->val[OPT_BR_X].w = tmp;
+ }
+
+ tmp = s->val[OPT_TL_Y].w;
+ if( tmp > s->val[OPT_BR_Y].w ) {
+ DBG( _DBG_INFO, "exchanging BR-Y - TL-Y\n" );
+ s->val[OPT_TL_Y].w = s->val[OPT_BR_Y].w;
+ s->val[OPT_BR_Y].w = tmp;
+ }
+
+ /* position and extent are always relative to 300 dpi */
+ dpi_x = (double)dev->dpi_max_x;
+ dpi_y = (double)dev->dpi_max_y;
+
+ left = (int)(SANE_UNFIX(s->val[OPT_TL_X].w)* dpi_x/
+ (_MM_PER_INCH*(dpi_x/_MEASURE_BASE)));
+ top = (int)(SANE_UNFIX(s->val[OPT_TL_Y].w)*dpi_y/
+ (_MM_PER_INCH*(dpi_y/_MEASURE_BASE)));
+ width = (int)(SANE_UNFIX(s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) *
+ dpi_x / (_MM_PER_INCH *(dpi_x/_MEASURE_BASE)));
+ height = (int)(SANE_UNFIX(s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) *
+ dpi_y / (_MM_PER_INCH *(dpi_y/_MEASURE_BASE)));
+
+ if((width == 0) || (height == 0)) {
+ DBG( _DBG_ERROR, "invalid width or height!\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ /* adjust mode list according to the model we use and the source we have
+ */
+#ifdef ALL_MODES
+ mp = getModeList( s );
+ scanmode = mp[s->val[OPT_MODE].w].scanmode;
+#else
+ scanmode = COLOR_TRUE24;
+#endif
+ DBG( _DBG_INFO, "scanmode = %u\n", scanmode );
+
+ /* clear it out just in case */
+ memset (&image, 0, sizeof(ImgDef));
+
+ /* this is what we want */
+ image.xyDpi.x = ndpi;
+ image.xyDpi.y = ndpi;
+ image.crArea.x = left; /* offset from left edge to area you want to scan */
+ image.crArea.y = top; /* offset from top edge to area you want to scan */
+ image.crArea.cx = width; /* always relative to 300 dpi */
+ image.crArea.cy = height;
+ image.wDataType = scanmode;
+
+#ifdef ALL_MODES
+ switch( s->val[OPT_EXT_MODE].w ) {
+ case 1: image.dwFlag |= _SCANDEF_Transparency; break;
+ case 2: image.dwFlag |= _SCANDEF_Negative; break;
+ default: break;
+ }
+#endif
+
+#if 0
+ if( s->val[OPT_PREVIEW].w )
+ image.dwFlag |= _SCANDEF_PREVIEW;
+#endif
+ /* set adjustments for brightness and contrast */
+ dev->DataInf.siBrightness = s->val[OPT_BRIGHTNESS].w;
+ dev->DataInf.siContrast = s->val[OPT_CONTRAST].w;
+
+ DBG( _DBG_SANE_INIT, "brightness %i, contrast %i\n",
+ dev->DataInf.siBrightness, dev->DataInf.siContrast );
+
+ result = u12image_SetupScanSettings( dev, &image );
+ if( SANE_STATUS_GOOD != result ) {
+ DBG( _DBG_ERROR, "u12image_SetupScanSettings() failed(%d)\n", result );
+ u12if_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->params.pixels_per_line = dev->DataInf.dwAppPixelsPerLine;
+ s->params.bytes_per_line = dev->DataInf.dwAppBytesPerLine;
+ s->params.lines = dev->DataInf.dwAppLinesPerArea;
+
+ DBG( _DBG_INFO, "* PixelPerLine = %u\n", s->params.pixels_per_line );
+ DBG( _DBG_INFO, "* BytesPerLine = %u\n", s->params.bytes_per_line );
+ DBG( _DBG_INFO, "* Lines = %u\n", s->params.lines );
+
+ /* reset our timer...*/
+ tsecs = 0;
+
+ s->buf = realloc( s->buf, (s->params.lines) * s->params.bytes_per_line );
+ if( NULL == s->buf ) {
+ DBG( _DBG_ERROR, "realloc failed\n" );
+ u12if_close( dev );
+ return SANE_STATUS_NO_MEM;
+ }
+
+ result = u12if_startScan( dev );
+ if( SANE_STATUS_GOOD != result ) {
+ DBG( _DBG_ERROR, "u12if_startScan() failed(%d)\n", result );
+ u12if_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ s->scanning = SANE_TRUE;
+
+ tsecs = (unsigned long)time(NULL);
+ DBG( _DBG_INFO, "TIME START\n" );
+
+ /*
+ * everything prepared, so start the child process and a pipe to communicate
+ * pipe --> fds[0]=read-fd, fds[1]=write-fd
+ */
+ if( pipe(fds) < 0 ) {
+ DBG( _DBG_ERROR, "ERROR: could not create pipe\n" );
+ s->scanning = SANE_FALSE;
+ u12if_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* create reader routine as new process */
+ s->bytes_read = 0;
+ s->r_pipe = fds[0];
+ s->w_pipe = fds[1];
+ s->reader_pid = sanei_thread_begin( reader_process, s );
+
+ cancelRead = SANE_FALSE;
+
+ if( s->reader_pid == -1 ) {
+ DBG( _DBG_ERROR, "ERROR: could not start reader task\n" );
+ s->scanning = SANE_FALSE;
+ u12if_close( dev );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ signal( SIGCHLD, sig_chldhandler );
+ if( sanei_thread_is_forked()) {
+ close( s->w_pipe );
+ s->w_pipe = -1;
+ }
+
+ DBG( _DBG_SANE_INIT, "sane_start done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** function to read the data from our child process
+ */
+SANE_Status sane_read( SANE_Handle handle, SANE_Byte *data,
+ SANE_Int max_length, SANE_Int *length )
+{
+ U12_Scanner *s = (U12_Scanner*)handle;
+ ssize_t nread;
+
+ *length = 0;
+
+ /* here we read all data from the driver... */
+ nread = read( s->r_pipe, data, max_length );
+ DBG( _DBG_READ, "sane_read - read %ld bytes\n", (long)nread );
+ if (!(s->scanning)) {
+ return do_cancel( s, SANE_TRUE );
+ }
+
+ if( nread < 0 ) {
+
+ if( EAGAIN == errno ) {
+
+ /* if we already had red the picture, so it's okay and stop */
+ if( s->bytes_read ==
+ (unsigned long)(s->params.lines * s->params.bytes_per_line)) {
+ sanei_thread_waitpid( s->reader_pid, 0 );
+ s->reader_pid = -1;
+ drvClose( s->hw );
+ return drvClosePipes(s);
+ }
+
+ /* else force the frontend to try again*/
+ return SANE_STATUS_GOOD;
+
+ } else {
+ DBG( _DBG_ERROR, "ERROR: errno=%d\n", errno );
+ do_cancel( s, SANE_TRUE );
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *length = nread;
+ s->bytes_read += nread;
+
+ /* nothing red means that we're finished OR we had a problem...*/
+ if( 0 == nread ) {
+
+ drvClose( s->hw );
+ s->exit_code = sanei_thread_get_status( s->reader_pid );
+
+ if( SANE_STATUS_GOOD != s->exit_code ) {
+ drvClosePipes(s);
+ return s->exit_code;
+ }
+ s->reader_pid = -1;
+ return drvClosePipes(s);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/** cancel the scanning process
+ */
+void sane_cancel( SANE_Handle handle )
+{
+ U12_Scanner *s = (U12_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_cancel\n" );
+
+ if( s->scanning )
+ do_cancel( s, SANE_FALSE );
+}
+
+/** set the pipe to blocking/non blocking mode
+ */
+SANE_Status sane_set_io_mode( SANE_Handle handle, SANE_Bool non_blocking )
+{
+ U12_Scanner *s = (U12_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_set_io_mode: non_blocking=%d\n", non_blocking );
+
+ if ( !s->scanning ) {
+ DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ if( -1 == s->r_pipe ) {
+ DBG( _DBG_ERROR, "ERROR: not supported !\n" );
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if( fcntl (s->r_pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) {
+ DBG( _DBG_ERROR, "ERROR: can´t set to non-blocking mode !\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG( _DBG_SANE_INIT, "sane_set_io_mode done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/** return the descriptor if available
+ */
+SANE_Status sane_get_select_fd( SANE_Handle handle, SANE_Int * fd )
+{
+ U12_Scanner *s = (U12_Scanner *)handle;
+
+ DBG( _DBG_SANE_INIT, "sane_get_select_fd\n" );
+
+ if( !s->scanning ) {
+ DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->r_pipe;
+
+ DBG( _DBG_SANE_INIT, "sane_get_select_fd done\n" );
+ return SANE_STATUS_GOOD;
+}
+
+/* END U12.C ................................................................*/
diff --git a/backend/u12.conf.in b/backend/u12.conf.in
new file mode 100644
index 0000000..c5457cb
--- /dev/null
+++ b/backend/u12.conf.in
@@ -0,0 +1,67 @@
+# U12-SANE Backend configuration file
+#
+
+# each device needs at least two lines:
+# - [usb] vendor-ID and product-ID
+# - device devicename
+# i.e. for Plustek (0x07B3) U1212 (0x0001)
+# [usb] 0x07B3 0x0001
+# device /dev/usbscanner
+# or
+# device libusb:bbb:ddd
+# where bbb is the busnumber and ddd the device number
+# make sure that your user has access to /proc/bus/usb/bbb/ddd
+#
+# additionally you can specify some options
+# warmup, lOffOnEnd, lampOff
+#
+# For autodetection use
+# [usb]
+# device /dev/usbscanner
+#
+# or simply
+# [usb]
+#
+# or if you want a specific device but you have no idea about the
+# device node or you use libusb, simply set vendor- and product-ID
+# [usb] 0x07B3 0x0001
+# device auto
+#
+# NOTE: autodetection is safe, as it uses the info it got
+# from the USB subsystem. If you're not using the
+# autodetection, you MUST have attached that device
+# at your USB-port, that you have specified...
+#
+
+[usb]
+
+#
+# options for the previous USB entry
+#
+# switch lamp off after xxx secs, 0 disables the feature
+option lampOff 300
+
+# warmup period in seconds, 0 means no warmup
+option warmup 15
+
+# 0 means leave lamp-status untouched, not 0 means switch off
+# on sane_close
+option lOffOnEnd 1
+
+#
+# for adjusting the default gamma values
+#
+#option redGamma 1.5
+#option greenGamma 1.5
+#option blueGamma 1.5
+#option grayGamma 1.5
+
+#
+# and of course the device-name
+#
+device auto
+
+#
+# to define a new device, start with a new section:
+# [usb]
+#
diff --git a/backend/u12.h b/backend/u12.h
new file mode 100644
index 0000000..5d18c96
--- /dev/null
+++ b/backend/u12.h
@@ -0,0 +1,336 @@
+/** @file u12.h
+ * @brief Definitions for the backend.
+ *
+ * Copyright (c) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.01 - initial version
+ * - 0.02 - added scaling variables to struct u12d
+ * .
+ * <hr>
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ * <hr>
+ */
+#ifndef __U12_H__
+#define __U12_H__
+
+#ifndef SANE_OPTION
+/* for compatibility with older versions */
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+} Option_Value;
+#endif
+
+/************************ some definitions ***********************************/
+
+#define U12_CONFIG_FILE "u12.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define _MM_PER_INCH 25.4
+#define _MEASURE_BASE 300UL
+#define _DEF_DPI 50
+
+/** the default image
+ */
+#define _DEFAULT_TLX 0
+#define _DEFAULT_TLY 0
+#define _DEFAULT_BRX 126
+#define _DEFAULT_BRY 76
+
+#define _DEFAULT_TP_TLX 3.5
+#define _DEFAULT_TP_TLY 10.5
+#define _DEFAULT_TP_BRX 38.5
+#define _DEFAULT_TP_BRY 33.5
+
+#define _DEFAULT_NEG_TLX 1.5
+#define _DEFAULT_NEG_TLY 1.5
+#define _DEFAULT_NEG_BRX 37.5
+#define _DEFAULT_NEG_BRY 25.5
+
+/** image sizes for normal, transparent and negative modes
+ */
+#define _TPAPageWidth 500U
+#define _TPAPageHeight 510U
+#define _TPAMinDpi 150
+#define _TPAModeSupportMin COLOR_TRUE24
+
+#define _NegativePageWidth 460UL
+#define _NegativePageHeight 350UL
+
+#define _TP_X ((double)_TPAPageWidth/300.0 * _MM_PER_INCH)
+#define _TP_Y ((double)_TPAPageHeight/300.0 * _MM_PER_INCH)
+#define _NEG_X ((double)_NegativePageWidth/300.0 * _MM_PER_INCH)
+#define _NEG_Y ((double)_NegativePageHeight/300.0 * _MM_PER_INCH)
+
+/** scan modes
+ */
+#define COLOR_BW 0
+#define COLOR_256GRAY 1
+#define COLOR_TRUE24 2
+#define COLOR_TRUE42 3
+
+#define _VAR_NOT_USED(x) ((x)=(x))
+
+
+/** usb id buffer
+ */
+#define _MAX_ID_LEN 20
+
+/** Scanmodes
+ */
+#define _ScanMode_Color 0
+#define _ScanMode_AverageOut 1 /* CCD averaged 2 pixels value for output*/
+#define _ScanMode_Mono 2 /* not color mode */
+
+/** Scansource + additional flags
+ */
+#define _SCANDEF_PREVIEW 0x00000001
+#define _SCANDEF_Transparency 0x00000100
+#define _SCANDEF_Negative 0x00000200
+#define _SCANDEF_TPA (_SCANDEF_Transparency | _SCANDEF_Negative)
+#define _SCANDEF_SCANNING 0x8000000
+
+/** for Gamma tables
+ */
+#define _MAP_RED 0
+#define _MAP_GREEN 1
+#define _MAP_BLUE 2
+#define _MAP_MASTER 3
+
+/** the ASIC modes */
+#define _PP_MODE_SPP 0
+#define _PP_MODE_EPP 1
+
+/************************ some structures ************************************/
+
+enum {
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+#ifdef ALL_MODES
+ OPT_MODE,
+ OPT_EXT_MODE,
+#endif
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_CUSTOM_GAMMA,
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ NUM_OPTIONS
+};
+
+/** for adjusting the scanner settings
+ */
+typedef struct {
+ int lampOff;
+ int lampOffOnEnd;
+ int warmup;
+
+ /* for adjusting the default gamma settings */
+ double rgamma;
+ double ggamma;
+ double bgamma;
+ double graygamma;
+
+ /* for adjusting scan-area */
+ long upNormal;
+ long upPositive;
+ long upNegative;
+ long leftNormal;
+
+} AdjDef, *pAdjDef;
+
+/** for holding basic capabilities
+ */
+typedef struct {
+ unsigned short scanAreaX;
+ unsigned short scanAreaY;
+ unsigned long flag;
+#if 0
+ RANGE rDataType; /* available scan modes */
+ unsigned short wMaxExtentX; /* scanarea width */
+ unsigned short wMaxExtentY; /* scanarea height */
+#endif
+} ScannerCaps, *pScannerCaps;
+
+
+/** for defining the scanmodes
+ */
+typedef const struct mode_param
+{
+ int color;
+ int depth;
+ int scanmode;
+} ModeParam, *pModeParam;
+
+/** Here we hold all device specific data
+ */
+typedef struct u12d
+{
+ SANE_Bool initialized; /* device already initialized? */
+ struct u12d *next; /* pointer to next dev in list */
+ int fd; /* device handle */
+ int mode;
+ char *name; /* (to avoid compiler warnings!)*/
+ SANE_Device sane; /* info struct */
+
+ /* scan-area settings */
+ SANE_Int max_x; /* max XY-extension of the scan-*/
+ SANE_Int max_y; /* area */
+ SANE_Range x_range; /* x-range of the scan-area */
+ SANE_Range y_range; /* y-range of the scan-area */
+
+ /* resolution settings */
+ SANE_Int dpi_max_x; /* */
+ SANE_Int dpi_max_y; /* */
+ SANE_Range dpi_range; /* resolution range */
+
+ SANE_Int *res_list; /* to hold the available phys. */
+ SANE_Int res_list_size; /* resolution values */
+ ScannerCaps caps; /* caps reported by the driver */
+ AdjDef adj; /* for driver adjustment */
+
+ char usbId[_MAX_ID_LEN];/* to keep Vendor and product */
+ /* ID string (from conf) file */
+ /* our gamma tables */
+ SANE_Word gamma_table[4][4096];
+ SANE_Range gamma_range;
+ int gamma_length;
+
+ /* the shading section */
+ pFnDACOffs fnDarkOffset; /**< ... */
+ ShadingDef shade; /**< shading parameters */
+
+ /* */
+ SANE_Byte PCBID; /**< which version of the PCB */
+
+ /* motor control section */
+ SANE_Byte MotorID; /**< the type of the motor drivers */
+ SANE_Byte MotorPower; /**< how to drive the motor */
+ SANE_Bool f2003;
+ SANE_Byte XStepMono;
+ SANE_Byte XStepColor;
+ SANE_Byte XStepBack;
+ SANE_Bool f0_8_16;
+ SANE_Byte scanStates[_SCANSTATE_BYTES];
+
+ /* CCD section */
+ SANE_Byte CCDID; /**< what CCD do we have */
+ RegDef *CCDRegs; /**< pointer to the register descr */
+ u_short numCCDRegs; /**< number of values to write */
+
+ /* DAC section */
+ SANE_Byte DACType; /**< what DAC do we have */
+ RegDef *DACRegs; /**< pointer to DAC reg descr. */
+ u_short numDACRegs; /**< number of values to write */
+ pFnDACDark fnDACDark; /**< */
+ RGBByteDef RegDACOffset;
+ RGBByteDef RegDACGain;
+
+ ShadowRegs regs; /**< for holding ASIC register values */
+ DataInfo DataInf; /**< all static info about the current scan */
+ ScanInfo scan; /**< buffer and motor management during scan */
+ BufferDef bufs;
+ void *scaleBuf; /**< buffer for line scaling */
+ int scaleStep; /**< step size for line scaling */
+ int scaleIzoom; /**< factor for line scaling */
+
+ u_long ModelOriginY;
+ SANE_Byte ModelCtrl;
+
+ SANE_Bool Tpa; /**< do we have a TPA */
+ SANE_Byte Buttons; /**< number of buttons */
+
+ /* lamp control section */
+ SANE_Bool warmupNeeded;
+ SANE_Byte lastLampStatus; /**< for keeping the lamp status */
+
+#ifdef HAVE_SETITIMER
+ struct itimerval saveSettings; /**< for lamp timer */
+#endif
+} U12_Device;
+
+typedef struct u12s
+{
+ struct u12s *next;
+ SANE_Pid reader_pid; /* process id of reader */
+ SANE_Status exit_code; /* status of the reader process */
+ int r_pipe; /* pipe to reader process */
+ int w_pipe; /* pipe from reader process */
+ unsigned long bytes_read; /* number of bytes currently read*/
+ U12_Device *hw; /* pointer to current device */
+ Option_Value val[NUM_OPTIONS];
+ SANE_Byte *buf; /* the image buffer */
+ SANE_Bool scanning; /* TRUE during scan-process */
+ SANE_Parameters params; /* for keeping the parameter */
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+
+} U12_Scanner;
+
+/** for collecting configuration info...
+ */
+typedef struct {
+
+ char devName[PATH_MAX];
+ char usbId[_MAX_ID_LEN];
+
+ /* contains the stuff to adjust... */
+ AdjDef adj;
+
+} CnfDef, *pCnfDef;
+
+#endif /* guard __U12_H__ */
+
+/* END U12.H ................................................................*/
diff --git a/backend/umax-scanner.c b/backend/umax-scanner.c
new file mode 100644
index 0000000..21f24ce
--- /dev/null
+++ b/backend/umax-scanner.c
@@ -0,0 +1,136 @@
+/* -------------------------------------------------------------------- */
+
+/* umax-scanner.c: scanner-definiton file for UMAX scanner driver.
+
+ (C) 1997-2004 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* -------------------------------------------------------------------- */
+
+#include "umax-scanner.h"
+
+/* ==================================================================== */
+
+/* scanners that are supported because the driver knows missing */
+/* inquiry-data */
+
+/* these umax-*.c files are included and not compiled separately */
+/* because this way the symbols are not exported */
+
+#include "umax-uc630.c"
+#include "umax-uc840.c"
+#include "umax-ug630.c"
+#include "umax-ug80.c"
+#include "umax-uc1200s.c"
+#include "umax-uc1200se.c"
+#include "umax-uc1260.c"
+
+static inquiry_blk *inquiry_table[] =
+{
+ &inquiry_uc630,
+ &inquiry_uc840,
+ &inquiry_ug630,
+ &inquiry_ug80,
+ &inquiry_uc1200s,
+ &inquiry_uc1200se,
+ &inquiry_uc1260
+};
+
+#define known_inquiry 7
+
+/* ==================================================================== */
+
+/* names of scanners that are supported because */
+/* the inquiry_return_block is ok and driver is tested */
+
+static char *scanner_str[] =
+{
+ "UMAX ", "Vista-T630 ",
+ "UMAX ", "Vista-S6 ",
+ "UMAX ", "Vista-S6E ",
+ "UMAX ", "UMAX S-6E ",
+ "UMAX ", "UMAX S-6EG ",
+ "UMAX ", "Vista-S8 ",
+ "UMAX ", "UMAX S-12 ",
+ "UMAX ", "UMAX S-12G ",
+ "UMAX ", "SuperVista S-12 ",
+ "UMAX ", "PSD ",
+ "UMAX ", "Astra 600S ",
+ "UMAX ", "Astra 610S ",
+ "UMAX ", "Astra 1200S ",
+ "UMAX ", "Astra 1220S ",
+ "UMAX ", "Astra 2100S ",
+ "UMAX ", "Astra 2200 ",
+ "UMAX ", "Astra 2400S ",
+/* "UMAX ", "Astra 6400 ", */ /* this is a firewire scanner */
+/* "UMAX ", "Astra 6450 ", */ /* this is a firewire scanner */
+ "UMAX ", "Mirage D-16L ",
+ "UMAX ", "Mirage II ",
+ "UMAX ", "Mirage IIse ",
+ "UMAX ", "PL-II ",
+ "UMAX ", "Power Look 2000 ",
+ "UMAX ", "PowerLook 2100XL",
+ "UMAX ", "PowerLook III ",
+ "UMAX ", "PowerLook 3000 ",
+ "UMAX ", "Gemini D-16 ",
+ "UMAX ", "PS-2400X ", /* same as LinoHell SAPHIR */
+ "LinoHell", "JADE ", /* is a Supervista S-12 */
+ "LinoHell", "Office ", /* is a Supervista S-12 */
+ "LinoHell", "Office2 ",
+ "LinoHell", "SAPHIR ", /* same as UMAX PS-2400X */
+ "LinoHell", "SAPHIR2 ",
+ "LinoHell", "SAPHIR3 ", /* 1000x2000 dpi */
+/* "LinoHell", "SAPHIR4 ", */
+ "Linotype", "SAPHIR4 ", /* Linotype-Hell Saphir Ultra II */
+/* "LinoHell", "OPAL ", */
+ "LinoHell", "OPAL2 ", /* looks like a UMAX Mirage II */
+ "HDM ", "LS4H1S ", /* Linoscan 1400 */
+ "Nikon ", "AX-110 ", /* is a Vista S6E */
+ "Nikon ", "AX-210 ", /* is a Supervista S12 */
+ "KYE ", "ColorPage-HR5 ",
+ "EPSON ", "Perfection600 ",
+ "ESCORT ", "Galleria 600S ", /* is an Astra 600S */
+ "EDGE ", "KTX-9600US ", /* may be an Astra 1220S */
+ "TriGem ", "PowerScanII ", /* is a Supervista S12 */
+ "END_OF_LIST"
+};
+
+/* ==================================================================== */
+
diff --git a/backend/umax-scanner.h b/backend/umax-scanner.h
new file mode 100644
index 0000000..6c9a93a
--- /dev/null
+++ b/backend/umax-scanner.h
@@ -0,0 +1,61 @@
+/* -------------------------------------------------------------------- */
+
+/* umax-scanner.h: scanner-definiton header-file for UMAX scanner driver.
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* -------------------------------------------------------------------- */
+
+#ifndef UMAX_SCANNER_H
+#define UMAX_SCANNER_H
+
+/* -------------------------------------------------------------------- */
+
+
+typedef struct
+{
+ char *scanner;
+ unsigned char *inquiry;
+ int inquiry_len;
+} inquiry_blk;
+
+#endif
+
diff --git a/backend/umax-scsidef.h b/backend/umax-scsidef.h
new file mode 100644
index 0000000..f192e2c
--- /dev/null
+++ b/backend/umax-scsidef.h
@@ -0,0 +1,1091 @@
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* umax-scsidef.h: scsi-definiton header file for UMAX scanner driver.
+
+ Copyright (C) 1996-1997 Michael K. Johnson
+ Copyright (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#ifndef UMAX_SCSIDEF_H
+#define UMAX_SCSIDEF_H
+
+#include "umax.h"
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* I'm using functions derived from Eric Youngdale's scsiinfo
+ * program here for dealing with parts of SCSI commands.
+ */
+
+static inline void setbitfield(unsigned char * pageaddr, int mask, int shift, int val) \
+{ *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift); }
+
+static inline void resetbitfield(unsigned char * pageaddr, int mask, int shift, int val) \
+{ *pageaddr = (*pageaddr & ~(mask << shift)) | (((!val) & mask) << shift); }
+
+static inline int getbitfield(unsigned char * pageaddr, int mask, int shift) \
+{ return ((*pageaddr >> shift) & mask); }
+
+/* ------------------------------------------------------------------------- */
+
+static inline int getnbyte(unsigned char * pnt, int nbytes) \
+{
+ unsigned int result = 0;
+ int i;
+
+ for(i=0; i<nbytes; i++)
+ result = (result << 8) | (pnt[i] & 0xff);
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void putnbyte(unsigned char * pnt, unsigned int value, unsigned int nbytes) \
+{
+ int i;
+
+ for(i=nbytes-1; i>= 0; i--) \
+ {
+ pnt[i] = value & 0xff;
+ value = value >> 8;
+ }
+}
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* Not all of these are defined in scsi.h, so we'll make sure
+ * we agree about them here...
+ */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define SCAN 0x1B
+#define SET_WINDOW 0x24
+#define READ 0x28
+#define SEND 0x2A
+#define OBJECT_POSITION 0x31
+#define GET_DATA_BUFFER_STATUS 0x34
+#undef WRITE_BUFFER /* correct write_buffer for scanner */
+#define WRITE_BUFFER 0x3B
+#define GET_LAMP_STATUS 0x5E
+#define SET_LAMP_STATUS 0x5F
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define STD_WDB_LEN 0x28 /* wdb_len if nothing is set by inquiry */
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* SCSI commands */
+
+typedef struct
+{
+ unsigned char *cmd;
+ size_t size;
+} scsiblk;
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define set_inquiry_return_size(icb,val) icb[0x04]=val
+static unsigned char inquiryC[] = { INQUIRY, 0x00, 0x02, 0x00, 0xff, 0x00 };
+static scsiblk inquiry = { inquiryC, sizeof(inquiryC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define get_inquiry_periph_qual(in) getbitfield(in, 0x07, 5)
+# define IN_periph_qual_lun 0x00
+# define IN_periph_qual_nolun 0x03
+#define get_inquiry_periph_devtype(in) getbitfield(in, 0x1f, 0)
+# define IN_periph_devtype_scanner 0x06
+# define IN_periph_devtype_unknown 0x1f
+
+#define get_inquiry_rmb(in) getbitfield(in + 0x01, 0x01, 7)
+#define get_inquiry_0x01_bit6(in) getbitfield(in + 0x01, 0x01, 6)
+#define get_inquiry_0x01_bit5(in) getbitfield(in + 0x01, 0x01, 5)
+#define get_inquiry_CBHS(in) getbitfield(in + 0x01, 0x03, 3)
+#define set_inquiry_CBHS(in, val) setbitfield(in + 0x01, 0x03, 3, val)
+# define IN_CBHS_50 0x00
+# define IN_CBHS_255 0x01
+# define IN_CBHS_auto 0x02
+#define get_inquiry_translamp(in) getbitfield(in + 0x01, 0x01, 2)
+#define get_inquiry_transavail(in) getbitfield(in + 0x01, 0x01, 1)
+#define get_inquiry_scanmode(in) getbitfield(in + 0x01, 0x01, 0)
+# define IN_flatbed 0x00
+# define IN_adf 0x01
+
+#define get_inquiry_iso_version(in) getbitfield(in + 0x02, 0x03, 6)
+#define get_inquiry_ecma_version(in) getbitfield(in + 0x02, 0x07, 3)
+#define get_inquiry_ansi_version(in) getbitfield(in + 0x02, 0x07, 0)
+
+#define get_inquiry_aenc(in) getbitfield(in + 0x03, 0x01, 7)
+#define get_inquiry_tmiop(in) getbitfield(in + 0x03, 0x01, 6)
+#define get_inquiry_0x03_bit5(in) getbitfield(in + 0x03, 0x01, 5)
+#define get_inquiry_0x03_bit4(in) getbitfield(in + 0x03, 0x01, 4)
+#define get_inquiry_response_format(in) getbitfield(in + 0x03, 0x0f, 0)
+# define IN_recognized 0x02
+
+#define get_inquiry_additional_length(in) in[0x04]
+#define set_inquiry_length(out,n) out[0x04]=n-5
+
+#define get_inquiry_0x05(in) in[0x05]
+#define get_inquiry_0x06(in) in[0x06]
+
+#define get_inquiry_scsi_byte(in) in[0x07]
+#define get_inquiry_scsi_reladr(in) getbitfield(in + 0x07, 0x01, 7)
+#define get_inquiry_scsi_wbus32(in) getbitfield(in + 0x07, 0x01, 6)
+#define get_inquiry_scsi_wbus16(in) getbitfield(in + 0x07, 0x01, 5)
+#define get_inquiry_scsi_sync(in) getbitfield(in + 0x07, 0x01, 4)
+#define get_inquiry_scsi_linked(in) getbitfield(in + 0x07, 0x01, 3)
+#define get_inquiry_scsi_R(in) getbitfield(in + 0x07, 0x01, 2)
+#define get_inquiry_scsi_cmdqueue(in) getbitfield(in + 0x07, 0x01, 1)
+#define get_inquiry_scsi_sftre(in) getbitfield(in + 0x07, 0x01, 0)
+
+#define get_inquiry_vendor(in, buf) strncpy(buf, in + 0x08, 0x08)
+#define get_inquiry_product(in, buf) strncpy(buf, in + 0x10, 0x010)
+#define get_inquiry_version(in, buf) strncpy(buf, in + 0x20, 0x04)
+
+#define set_inquiry_fw_quality(in, val) setbitfield(in + 0x24, 1, 0, val)
+#define get_inquiry_fw_quality(in) getbitfield(in + 0x24, 1, 0)
+#define get_inquiry_fw_fast_preview(in) getbitfield(in + 0x24, 1, 1)
+#define get_inquiry_fw_shadow_comp(in) getbitfield(in + 0x24, 1, 2)
+#define get_inquiry_fw_reselection(in) getbitfield(in + 0x24, 1, 3)
+#define get_inquiry_fw_lamp_int_cont(in) getbitfield(in + 0x24, 1, 4)
+#define get_inquiry_fw_batch_scan(in) getbitfield(in + 0x24, 1, 5)
+#define get_inquiry_fw_calibration(in) getbitfield(in + 0x24, 1, 6)
+#define get_inquiry_fw_adjust_exposure_tf(in) getbitfield(in + 0x24, 1, 7)
+
+#define get_inquiry_exposure_time_step_unit(in) in[0x25]
+#define get_inquiry_exposure_time_max(in) getnbyte(in + 0x26, 2)
+#define get_inquiry_exposure_time_lhg_min(in) in[0x2a]
+#define get_inquiry_exposure_time_color_min(in) in[0x2b]
+#define get_inquiry_exposure_time_lh_def_fb(in) in[0x2c]
+#define get_inquiry_exposure_time_lh_def_uta(in) in[0x2d]
+#define get_inquiry_exposure_time_gray_def_fb(in) in[0x2e]
+#define get_inquiry_exposure_time_gray_def_uta(in) in[0x2f]
+#define get_inquiry_exposure_time_def_r_fb(in) in[0x30]
+#define get_inquiry_exposure_time_def_g_fb(in) in[0x31]
+#define get_inquiry_exposure_time_def_b_fb(in) in[0x32]
+#define get_inquiry_exposure_time_def_r_uta(in) in[0x33]
+#define get_inquiry_exposure_time_def_g_uta(in) in[0x34]
+#define get_inquiry_exposure_time_def_b_uta(in) in[0x35]
+
+#define get_inquiry_0x36(in) in[0x36]
+#define get_inquiry_0x37(in) in[0x37]
+
+/* bytes 0x38 - 0x5f reserved by SCSI */
+
+#define get_inquiry_sc_feature_byte0(in) in[0x60]
+#define get_inquiry_sc_three_pass_color(in) getbitfield(in + 0x60, 1, 0)
+#define get_inquiry_sc_one_pass_color(in) getbitfield(in + 0x60, 1, 1)
+#define get_inquiry_sc_lineart(in) getbitfield(in + 0x60, 1, 2)
+#define get_inquiry_sc_halftone(in) getbitfield(in + 0x60, 1, 3)
+#define get_inquiry_sc_gray(in) getbitfield(in + 0x60, 1, 4)
+#define get_inquiry_sc_color(in) getbitfield(in + 0x60, 1, 5)
+#define get_inquiry_sc_uta(in) getbitfield(in + 0x60, 1, 6)
+#define get_inquiry_sc_adf(in) getbitfield(in + 0x60, 1, 7)
+
+#define set_inquiry_sc_three_pass_color(in,val) setbitfield(in + 0x60, 1, 0, val)
+#define set_inquiry_sc_one_pass_color(in,val) setbitfield(in + 0x60, 1, 1, val)
+#define set_inquiry_sc_lineart(in,val) setbitfield(in + 0x60, 1, 2, val)
+#define set_inquiry_sc_halftone(in,val) setbitfield(in + 0x60, 1, 3, val)
+#define set_inquiry_sc_gray(in,val) setbitfield(in + 0x60, 1, 4, val)
+#define set_inquiry_sc_color(in,val) setbitfield(in + 0x60, 1, 5, val)
+#define set_inquiry_sc_uta(in,val) setbitfield(in + 0x60, 1, 6, val)
+#define set_inquiry_sc_adf(in,val) setbitfield(in + 0x60, 1, 7, val)
+
+#define get_inquiry_sc_feature_byte1(in) in[0x61]
+#define get_inquiry_sc_double_res(in) getbitfield(in + 0x61, 1, 0)
+#define get_inquiry_sc_high_byte_first(in) getbitfield(in + 0x61, 1, 1)
+#define get_inquiry_sc_bi_image_reverse(in) getbitfield(in + 0x61, 1, 2)
+#define get_inquiry_sc_multi_image_reverse(in) getbitfield(in + 0x61, 1, 3)
+#define get_inquiry_sc_no_shadow(in) getbitfield(in + 0x61, 1, 4)
+#define get_inquiry_sc_no_highlight(in) getbitfield(in + 0x61, 1, 5)
+#define get_inquiry_sc_downloadable_fw(in) getbitfield(in + 0x61, 1, 6)
+#define get_inquiry_sc_paper_length_14(in) getbitfield(in + 0x61, 1, 7)
+
+#define get_inquiry_sc_feature_byte2(in) in[0x62]
+#define get_inquiry_sc_uploadable_shade(in) getbitfield(in + 0x62, 1, 0)
+#define get_inquiry_fw_support_color(in) getbitfield(in + 0x62, 1, 1)
+#define get_inquiry_analog_gamma(in) getbitfield(in + 0x62, 1, 2)
+#define get_inquiry_xy_coordinate_base(in) getbitfield(in + 0x62, 1, 3)
+#define get_inquiry_lineart_order(in) getbitfield(in + 0x62, 1, 4)
+#define get_inquiry_start_density(in) getbitfield(in + 0x62, 1, 5)
+#define get_inquiry_hw_x_scaling(in) getbitfield(in + 0x62, 1, 6)
+#define get_inquiry_hw_y_scaling(in) getbitfield(in + 0x62, 1, 7)
+
+#define get_inquiry_sc_feature_byte3(in) in[0x63]
+#define get_inquiry_ADF_no_paper(in) getbitfield(in + 0x63, 1, 0)
+#define get_inquiry_ADF_cover_open(in) getbitfield(in + 0x63, 1, 1)
+#define get_inquiry_ADF_paper_jam(in) getbitfield(in + 0x63, 1, 2)
+#define get_inquiry_0x63_bit3(in) getbitfield(in + 0x63, 1, 3)
+#define get_inquiry_0x63_bit4(in) getbitfield(in + 0x63, 1, 4)
+#define get_inquiry_lens_cal_in_doc_pos(in) getbitfield(in + 0x63, 1, 5)
+#define get_inquiry_manual_focus(in) getbitfield(in + 0x63, 1, 6)
+#define get_inquiry_sel_uta_lens_cal_pos(in) getbitfield(in + 0x63, 1, 7)
+
+#define get_inquiry_gamma_download_available(in) getbitfield(in + 0x64, 1, 7)
+#define get_inquiry_0x64_bit6(in) getbitfield(in + 0x64, 1, 6)
+#define get_inquiry_gamma_type_2(in) getbitfield(in + 0x64, 1, 5)
+#define get_inquiry_0x64_bit4(in) getbitfield(in + 0x64, 1, 4)
+#define get_inquiry_0x64_bit3(in) getbitfield(in + 0x64, 1, 3)
+#define get_inquiry_0x64_bit2(in) getbitfield(in + 0x64, 1, 2)
+#define get_inquiry_gamma_lines(in) getbitfield(in + 0x64, 0x03, 0)
+# define IN_3_pass 0x01
+# define IN_3_line 0x03
+
+#define get_inquiry_0x65(in) in[0x65]
+
+#define get_inquiry_gib(in) in[0x66]
+#define get_inquiry_gib_8bpp(in) getbitfield(in + 0x66, 1, 0)
+#define get_inquiry_gib_9bpp(in) getbitfield(in + 0x66, 1, 1)
+#define get_inquiry_gib_10bpp(in) getbitfield(in + 0x66, 1, 2)
+#define get_inquiry_gib_12bpp(in) getbitfield(in + 0x66, 1, 3)
+#define get_inquiry_gib_14bpp(in) getbitfield(in + 0x66, 1, 4)
+#define get_inquiry_gib_16bpp(in) getbitfield(in + 0x66, 1, 5)
+#define get_inquiry_0x66_bit6(in) getbitfield(in + 0x66, 1, 6)
+#define get_inquiry_0x66_bit7(in) getbitfield(in + 0x66, 1, 7)
+# define IN_gib_8bpp 0x01
+# define IN_gib_10bpp 0x04
+
+#define get_inquiry_0x67(in) in[0x67]
+
+#define get_inquiry_gob(in) in[0x68]
+#define get_inquiry_gob_8bpp(in) getbitfield(in + 0x68, 1, 0)
+#define get_inquiry_gob_9bpp(in) getbitfield(in + 0x68, 1, 1)
+#define get_inquiry_gob_10bpp(in) getbitfield(in + 0x68, 1, 2)
+#define get_inquiry_gob_12bpp(in) getbitfield(in + 0x68, 1, 3)
+#define get_inquiry_gob_14bpp(in) getbitfield(in + 0x68, 1, 4)
+#define get_inquiry_gob_16bpp(in) getbitfield(in + 0x68, 1, 5)
+#define get_inquiry_0x68_bit6(in) getbitfield(in + 0x68, 1, 6)
+#define get_inquiry_0x68_bit7(in) getbitfield(in + 0x68, 1, 7)
+# define IN_gob_8bpp 0x01
+# define IN_gob_10bpp 0x04
+
+#define get_inquiry_hda(in) getbitfield(in + 0x69, 0x01, 7)
+#define get_inquiry_max_halftone_matrix(in) getbitfield(in + 0x69, 0x7f, 0)
+#define get_inquiry_halftones_supported(in) in[0x6a]
+#define get_inquiry_halftones_2x2(in) getbitfield(in + 0x6a, 1, 0)
+#define get_inquiry_halftones_4x4(in) getbitfield(in + 0x6a, 1, 1)
+#define get_inquiry_halftones_6x6(in) getbitfield(in + 0x6a, 1, 2)
+#define get_inquiry_halftones_8x8(in) getbitfield(in + 0x6a, 1, 3)
+#define get_inquiry_halftones_12x12(in) getbitfield(in + 0x6a, 1, 5)
+# define IN_halftone_2x2 0x01
+# define IN_halftone_4x4 0x02
+# define IN_halftone_6x6 0x04
+# define IN_halftone_8x8 0x08
+# define IN_halftone_12x12 0x20
+
+#define get_inquiry_0x6b(in) in[0x6b]
+#define get_inquiry_0x6c(in) in[0x6c]
+
+#define get_inquiry_colorseq(in) getbitfield(in + 0x6d, 0x07, 5)
+# define IN_color_sequence_RGB 0x00
+# define IN_color_sequence_RBG 0x01
+# define IN_color_sequence_GBR 0x02
+# define IN_color_sequence_GRB 0x03
+# define IN_color_sequence_BRG 0x04
+# define IN_color_sequence_BGR 0x05
+# define IN_color_sequence_all 0x07
+#define get_inquiry_color_order(in) getbitfield(in + 0x6d, 0x1f, 0)
+#define set_inquiry_color_order(in,val) setbitfield(in + 0x6d, 0x1f, 0, val)
+#define get_inquiry_color_order_pixel(in) getbitfield(in + 0x6d, 1, 0)
+#define get_inquiry_color_order_line_no_ccd(in) getbitfield(in + 0x6d, 1, 1)
+#define get_inquiry_color_order_plane(in) getbitfield(in + 0x6d, 1, 2)
+#define get_inquiry_color_order_line_w_ccd(in) getbitfield(in + 0x6d, 1, 3)
+#define get_inquiry_color_order_reserved(in) getbitfield(in + 0x6d, 1, 4)
+# define IN_color_ordering_pixel 0x01
+# define IN_color_ordering_line_no_ccd 0x02
+# define IN_color_ordering_plane 0x04
+# define IN_color_ordering_line_w_ccd 0x08
+
+#define get_inquiry_max_vidmem(in) getnbyte(in + 0x6e, 4)
+
+#define get_inquiry_0x72(in) in[0x72]
+#define get_inquiry_max_opt_res(in) in[0x73]
+#define get_inquiry_max_x_res(in) in[0x74]
+#define get_inquiry_max_y_res(in) in[0x75]
+#define get_inquiry_fb_max_scan_width(in) getnbyte(in + 0x76, 2)
+#define get_inquiry_fb_max_scan_length(in) getnbyte(in + 0x78, 2)
+#define get_inquiry_uta_x_original_point(in) getnbyte(in + 0x7a, 2)
+#define get_inquiry_uta_y_original_point(in) getnbyte(in + 0x7c, 2)
+#define get_inquiry_uta_max_scan_width(in) getnbyte(in + 0x7e, 2)
+#define get_inquiry_uta_max_scan_length(in) getnbyte(in + 0x80, 2)
+
+#define get_inquiry_0x82(in) in[0x82]
+
+#define get_inquiry_dor_max_opt_res(in) in[0x83]
+#define get_inquiry_dor_max_x_res(in) in[0x84]
+#define get_inquiry_dor_max_y_res(in) in[0x85]
+
+#define get_inquiry_dor_x_original_point(in) getnbyte(in + 0x86, 2)
+#define get_inquiry_dor_y_original_point(in) getnbyte(in + 0x88, 2)
+#define get_inquiry_dor_max_scan_width(in) getnbyte(in + 0x8a, 2)
+#define get_inquiry_dor_max_scan_length(in) getnbyte(in + 0x8c, 2)
+
+#define get_inquiry_0x8e(in) in[0x8e]
+
+#define get_inquiry_last_calibration_lamp_density(in) in[0x8f]
+# define IN_min_lamp_density 0x01
+# define IN_max_lamp_density 0xff
+
+#define get_inquiry_0x90(in) in[0x90]
+
+#define get_inquiry_lamp_warmup_maximum_time(in) in[0x91]
+#define get_inquiry_wdb_length(in) getnbyte(in + 0x92, 2)
+#define get_inquiry_optical_resolution_residue(in) in[0x94]
+#define get_inquiry_x_resolution_residue(in) in[0x95]
+#define get_inquiry_y_resolution_residue(in) in[0x96]
+#define get_inquiry_analog_gamma_table(in) in[0x97]
+
+#define get_inquiry_0x98(in) in[0x98]
+#define get_inquiry_0x99(in) in[0x99]
+
+#define get_inquiry_max_calibration_data_lines(in) in[0x9a]
+#define get_inquiry_fb_uta_line_arrangement_mode(in) in[0x9b]
+#define get_inquiry_adf_line_arrangement_mode(in) in[0x9c]
+#define get_inquiry_CCD_line_distance(in) in[0x9d]
+
+#define set_inquiry_max_calibration_data_lines(out,val) out[0x9a]=val
+#define set_inquiry_fb_uta_line_arrangement_mode(out,val) out[0x9b]=val
+#define set_inquiry_adf_line_arrangement_mode(out,val) out[0x9c]=val
+#define set_inquiry_CCD_line_distance(out,val) out[0x9d]=val
+
+#define get_inquiry_0x9e(in) in[0x9e]
+
+#define get_inquiry_dor_optical_resolution_residue(in) in[0xa0]
+#define get_inquiry_dor_x_resolution_residue(in) in[0xa1]
+#define get_inquiry_dor_y_resolution_residue(in) in[0xa2]
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* although the command is defined with 6 bytes length in the doc, 10 bytes seems to be the correct length */
+static unsigned char get_lamp_statusC[] = { GET_LAMP_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk get_lamp_status = { get_lamp_statusC,sizeof(get_lamp_statusC) };
+#define get_lamp_status_lamp_on(in) getbitfield(in, 1, 0)
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* although the command is defined with 6 bytes length in the doc, 10 bytes seems to be the correct length */
+static unsigned char set_lamp_statusC[] = { SET_LAMP_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk set_lamp_status = { set_lamp_statusC,sizeof(set_lamp_statusC) };
+#define set_lamp_status_lamp_on(in,val) setbitfield(in + 0x03, 1, 7, val)
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char test_unit_readyC[] = { TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk test_unit_ready = { test_unit_readyC,sizeof(test_unit_readyC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char reserve_unitC[] = { RESERVE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk reserve_unit = { reserve_unitC, sizeof(reserve_unitC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char release_unitC[] = { RELEASE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk release_unit = { release_unitC, sizeof(release_unitC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char set_windowC[] =
+{
+ SET_WINDOW, 0x00, /* opcode, lun */
+ 0x00, 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00, 0x00, /* transfer length; needs to be set */
+ 0x00 /* control byte */
+};
+
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
+static scsiblk set_window = { set_windowC, sizeof(set_windowC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char window_parameter_data_blockC[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reserved */
+ 0x00, 0x00 /* Window Descriptor Length, value must be set by SHORT_WDB define */
+};
+
+#define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2)
+
+
+static scsiblk window_parameter_data_block =
+{ window_parameter_data_blockC, sizeof(window_parameter_data_blockC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char window_descriptor_blockC[] =
+{
+#define max_WDB_size 0xff
+#define used_WDB_size 0x53
+
+/* 0x00 */ 0x00, /* Window Identifier */
+#define set_WD_wid(sb, val) sb[0] = val
+# define WD_wid_bw 0x00
+# define WD_wid_red 0x01
+# define WD_wid_green 0x02
+# define WD_wid_blue 0x03
+# define WD_wid_all 0x00
+/* 0x01 */ 0x00, /* reserved, AUTO */
+#define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val)
+
+/* 0x02 */ 0x00, 0x00, /* X Resolution in dpi */
+#define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2)
+
+/* 0x04 */ 0x00, 0x00, /* Y Resolution in dpi */
+#define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2)
+
+/* 0x06 */ 0x00, 0x00, 0x00, 0x00, /* Upper Left X in 1200pt/inch */
+#define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4)
+
+/* 0x0a */ 0x00, 0x00, 0x00, 0x00, /* Upper Left Y in 1200pt/inch */
+#define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4)
+
+/* 0x0e */ 0x00, 0x00, 0x00, 0x00, /* Width 1200pt/inch */
+#define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4)
+
+/* 0x12 */ 0x00, 0x00, 0x00, 0x00, /* Length 1200pt/inch */
+#define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4)
+
+/* 0x16 */ 0xff, /* Brightness */
+#define set_WD_brightness(sb, val) sb[0x16] = val
+
+/* 0x17 */ 0x80, /* Threshold */
+#define set_WD_threshold(sb, val) sb[0x17] = val
+
+/* 0x18 */ 0x80, /* Contrast */
+#define set_WD_contrast(sb, val) sb[0x18] = val
+
+/* 0x19 */ 0x05, /* Image Composition */
+#define set_WD_composition(sb, val) sb[0x19] = val
+# define WD_comp_lineart 0x00
+# define WD_comp_dithered 0x01
+# define WD_comp_gray 0x02
+# define WD_comp_gray 0x02
+# define WD_comp_rgb_bilevel 0x03
+# define WD_comp_rgb_dithered 0x04
+# define WD_comp_rgb_full 0x05
+
+/* 0x1a */ 0x08, /* Bits/Pixel */
+#define set_WD_bitsperpixel(sb, val) sb[0x1a] = val
+# define WD_bits_1 0x01
+# define WD_bits_8 0x08
+# define WD_bits_10 0x0a
+
+/* 0x1b */ 0x00, 0x03, /* Halftone pattern */
+#define set_WD_halftone(sb, val) putnbyte(sb + 0x1b, val, 2)
+# define WD_halftone_download 0x00
+# define WD_halftone_12x12_1 0x01
+# define WD_halftone_12x12_2 0x02
+# define WD_halftone_8x8_1 0x03
+# define WD_halftone_8x8_2 0x04
+# define WD_halftone_8x8_3 0x05
+# define WD_halftone_8x8_4 0x06
+# define WD_halftone_8x8_5 0x07
+# define WD_halftone_6x6_1 0x08
+# define WD_halftone_6x6_2 0x09
+# define WD_halftone_6x6_3 0x0a
+# define WD_halftone_4x4_1 0x0b
+# define WD_halftone_4x4_2 0x0c
+# define WD_halftone_4x4_3 0x0d
+# define WD_halftone_2x2_1 0x0e
+# define WD_halftone_2x2_2 0x0f
+
+/* 0x1d */ 0x03, /* RIF, reserved, padding type */
+#define set_WD_RIF(sb, val) setbitfield(sb + 0x1d, 1, 7, val)
+#define set_WD_padding_type(sb,val) setbitfield(sb + 0x1d, 0x07, 0, val)
+# define WD_padding_byte 0x03
+# define WD_padding_word 0x07
+
+/* 0x1e */ 0x00, 0x00, /* Bit Ordering (0 = left to right;) */
+/* 0x20 */ 0x00, /* Compression Type (reserved) */
+/* 0x21 */ 0x00, /* Compr. Argument (reserved) */
+/* 0x22 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Reserved */
+/* 0x28 */ 0x01, /* Speed */
+#define set_WD_speed(sb, val) sb[0x28] = val
+# define WD_speed_fastsmear 0x81
+# define WD_speed_slowsmear 0x82
+# define WD_speed_fastclear 0x01
+# define WD_speed_slowclear 0x02
+# define WD_speed_speedbit 0x03
+# define WD_speed_smearbit 0x80
+# define WD_speed_fast 0x01
+# define WD_speed_slow 0x02
+# define WD_speed_smear 0x80
+
+/* 0x29 */ 0x00, /* Select Color */
+#define set_WD_select_color(sb,val) setbitfield(sb+0x29, 0x07, 5, val)
+# define WD_color_gray 0x00
+# define WD_color_red 0x04
+# define WD_color_green 0x02
+# define WD_color_blue 0x01
+
+/* 0x2a */ 0xff, /* Highlight */
+#define set_WD_highlight(sb, val) sb[0x2a] = val
+
+/* 0x2b */ 0x00, /* Shadow */
+#define set_WD_shadow(sb, val) sb[0x2b] = val
+
+/* 0x2c */ 0x00, 0x00, /* Paper Length */
+#define set_WD_paperlength(sb, val) putnbyte(sb + 0x2c, val, 2)
+
+/* 0x2e */ 0x0f, /* Gamma Function */
+#define set_WD_gamma(sb, val) sb[0x2e] = val
+# define WD_gamma_download 0x00
+# define WD_gamma_builtin1 0x01
+# define WD_gamma_builtin2 0x02
+# define WD_gamma_builtin3 0x03
+# define WD_gamma_builtin4 0x04
+# define WD_gamma_normal 0x0f
+
+/* 0x2f */ 0x11, /* Scan Module */
+#define set_WD_module(sb, val) sb[0x2f] = val
+# define WD_module_flatbed 0x11
+# define WD_module_transparency 0xff
+
+/* 0x30 */ 0x01, /* HBT, DOR, reserved, RMIF, CBHS Type */
+ /* CBHS = Contrast, Brightness, Highlight, Shadow */
+#define set_WD_CBHS(sb, val) setbitfield(sb + 0x30, 1, 0, val)
+# define WD_CBHS_50 0
+# define WD_CBHS_255 1
+#define set_WD_FF(sb, val) setbitfield(sb + 0x30, 1, 1, val) /* FF = Fix Focus position */
+#define set_WD_RMIF(sb, val) setbitfield(sb + 0x30, 1, 2, val) /* Reverse Multil Image Frmt */
+#define set_WD_FDC(sb, val) setbitfield(sb + 0x30, 1, 3, val) /* document calibration */
+#define set_WD_PF(sb, val) setbitfield(sb + 0x30, 1, 4, val) /* PF pre focus */
+#define set_WD_LCL(sb, val) setbitfield(sb + 0x30, 1, 5, val) /* LCL (focus position) */
+#define set_WD_DOR(sb, val) setbitfield(sb + 0x30, 1, 6, val) /* Double Optical Resolution */
+#define set_WD_HBT(sb, val) setbitfield(sb + 0x30, 1, 7, val) /* High Byte Transfer */
+# define WD_HBT_HBF 0x00
+# define WD_HBT_LBF 0x01
+
+/* 0x31 */ 0x00, 0x00, /* scan exposure level */
+#define set_WD_scan_exposure_level(sb, val) putnbyte(sb+0x31, val, 2)
+
+/* 0x33 */ 0x00, 0x00, /* calibration exposure level */
+#define set_WD_calibration_exposure_level(sb, val) putnbyte(sb+0x33, val, 2)
+
+/* 0x35 */ 0x00, /* reserved */
+/* 0x36 */ 0x00, /* reserved */
+/* 0x37 */ 0x00, /* reserved */
+/* 0x38 */ 0x00, /* reserved (For Autoexposure) */
+
+/* 0x39 */ 0x00, /* BS, reserved, Calibration Mode */
+#define set_WD_batch(sb, val) setbitfield(sb + 0x39, 1, 7, val)
+#define set_WD_MF(sb, val) setbitfield(sb + 0x39, 1, 6, val) /* manual focus */
+#define set_WD_line_arrangement(sb, val) setbitfield(sb + 0x39, 1, 5, val)
+# define WD_line_arrengement_by_driver 0x01
+# define WD_line_arrengement_by_fw 0x00
+#define set_WD_warmup(sb, val) setbitfield(sb + 0x39, 1, 4, val)
+#define set_WD_calibration(sb, val) setbitfield(sb + 0x39, 0x0f, 0, val)
+# define WD_calibration_image 0x00
+# define WD_calibration_lineart 0x0f
+# define WD_calibration_dither 0x0e
+# define WD_calibration_gray 0x0d
+# define WD_calibration_rgb 0x0a
+# define WD_calibration_ignore 0x09
+
+/* 0x3a */ 0x01, /* Color Sequence, Color Ordering Support */
+#define set_WD_color_sequence(sb,val) setbitfield(sb+0x3a,0x07,5, val)
+# define WD_color_sequence_RGB 0x00
+# define WD_color_sequence_RBG 0x01
+# define WD_color_sequence_GBR 0x02
+# define WD_color_sequence_GRB 0x03
+# define WD_color_sequence_BRG 0x04
+# define WD_color_sequence_BGR 0x05
+
+#define set_WD_color_ordering(sb,val) setbitfield(sb+0x3a,0x1f,0, val)
+# define WD_color_ordering_pixel 0x01
+# define WD_color_ordering_line_no_ccd 0x02
+# define WD_color_ordering_plane 0x04
+# define WD_color_ordering_line_w_ccd 0x08
+
+/* 0x3b */ 0x00, /* analog gamma code (set to 1.0) */
+#define set_WD_analog_gamma(sb, gamma) sb[0x3b] = gamma /* see analog_gamma_table */
+
+/* 0x3c */ 0x00, /* Reserved */
+
+/* 0x3d */ 0x00, /* Next Calibration Lamp Density */
+#define set_WD_lamp_c_density(sb, val) sb[0x3d] = val
+# define WD_lamp_c_density_auto 0x00
+# define WD_lamp_c_density_min 0x01
+# define WD_lamp_c_density_max 0xff
+
+/* 0x3e */ 0x00, /* Reserved */
+
+/* 0x3f */ 0x00, /* Next Scan Lamp Density */
+#define set_WD_lamp_s_density(sb, val) sb[0x3f] = val
+# define WD_lamp_s_density_auto 0x00
+# define WD_lamp_s_density_min 0x01
+# define WD_lamp_s_density_max 0xff
+
+/* 0x40 */ 0x00, 0x00, 0x00, 0x00, /* Next Upper Left Y in 1200 dpt/inch */
+#define set_WD_next_upper_left(sb, val) putnbyte(sb + 0x40, val, 4)
+
+/* 0x44 */ 0x00, 0x00, 0x00, 0x00, /* Pixel Count */
+#define set_WD_pixel_count(sb, val) putnbyte(sb + 0x44, val, 4)
+
+/* 0x48 */ 0x00, 0x00, 0x00, 0x00, /* Line Count */
+#define set_WD_line_count(sb, val) putnbyte(sb + 0x48, val, 4)
+
+/* 0x4c */ 4, 176, /* x coordiante base 1200 (pts/inch) */
+#define set_WD_x_coordinate_base(sb, val) putnbyte(sb + 0x4c, val, 2)
+
+/* 0x4e */ 4, 176, /* y coordinate base 1200 (pts/inch) */
+#define set_WD_y_coordinate_base(sb, val) putnbyte(sb + 0x4e, val, 2)
+
+/* 0x50 */ 0x00, /* reserved */
+
+/* 0x51 */ 0x00, /* driver calibration need image data lines */
+#define set_WD_calibration_data_lines(sb, val) sb[0x51] = val
+
+/* 0x52 */ 0x00 /* start density */
+#define set_WD_start_density(sb, val) sb[0x52] = val
+
+/* if somone adds here anything, please change used_WDB_size in this file !! */
+};
+
+
+static scsiblk window_descriptor_block = { window_descriptor_blockC, sizeof(window_descriptor_blockC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#define set_WDB_length(length) (window_descriptor_block.size = (length))
+
+#define WPDB_OFF(b) (b + set_window.size)
+
+#define WDB_OFF(b, n) (b + set_window.size + \
+ window_parameter_data_block.size + \
+ ( window_descriptor_block.size * (n - 1) ) )
+
+#define set_WPDB_wdbnum(sb,n) set_WPDB_wdblen(sb,window_descriptor_block.size*n)
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char scanC[] = { SCAN, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00 };
+
+static scsiblk scan = { scanC, sizeof(scanC) - 3 };
+#define set_SC_xfer_length(sb, val) sb[0x04] = val
+#define set_SC_quality(sb, val) setbitfield(sb + 0x05, 1, 5, val)
+#define set_SC_adf(sb, val) setbitfield(sb + 0x05, 1, 6, val)
+#define set_SC_preview(sb, val) setbitfield(sb + 0x05, 1, 7, val)
+#define set_SC_wid(sb, n, val) sb[0x05 + n] = val
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* sread instead of read because read is a libc primitive */
+static unsigned char sreadC[] = { READ, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk sread = { sreadC, sizeof(sreadC) };
+
+#define set_R_datatype_code(sb, val) sb[0x02] = val
+# define R_datatype_imagedata 0x00
+# define R_datatype_vendorspecific 0x01
+# define R_datatype_halftone 0x02
+# define R_datatype_gamma 0x03
+# define R_datatype_shading 0x80
+# define R_datatype_gain 0x81
+
+#define set_R_datatype_qual_winid(sb, val) sb[0x05] = val
+#define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char sendC[] = { SEND, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk send = { sendC, sizeof(sendC) };
+
+#define set_S_datatype_code(sb, val) sb[0x02] = val
+# define S_datatype_imagedata 0x00
+# define S_datatype_vendorspecific 0x01
+# define S_datatype_halftone 0x02
+# define S_datatype_gamma 0x03
+# define S_datatype_shading 0x80
+# define S_datatype_gain 0x81
+
+#define set_S_datatype_qual_winid(sb, val) sb[0x05] = val
+
+#define set_S_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3)
+# define S_xfer_length_gamma(l) ((l * (1024 + 1)) + 1)
+# define S_xfer_length_1line 0x0402
+# define S_xfer_length_2line 0x0803
+# define S_xfer_length_3line 0x0c04
+
+/* FIXME: add Type 0 download curve format */
+/* FIXME: add Type 1 download curve format */
+
+
+static unsigned char gamma_DCF0C[] = { 0x00 };
+static scsiblk gamma_DCF0 = { gamma_DCF0C, sizeof(gamma_DCF0C) };
+#define set_DCF0_gamma_color(dc,col,val) dc[1 + (1025 * col)] = val
+# define DCF0_gamma_color_gray 0
+# define DCF0_gamma_color_red 1
+# define DCF0_gamma_color_green 2
+# define DCF0_gamma_color_blue 3
+
+#define set_DCF0_gamma_lines(dc,val) setbitfield(dc + 0x00, 3, 0, val)
+# define DCF0_gamma_one_line 1
+# define DCF0_gamma_three_lines 3
+
+
+static unsigned char gamma_DCF1C[] = { 0x00, 0x00 };
+static scsiblk gamma_DCF1 = { gamma_DCF1C, sizeof(gamma_DCF1C) };
+#define set_DCF1_gamma_color(dc,val) dc[1] = val
+# define DCF1_gamma_color_gray 0
+# define DCF1_gamma_color_red 1
+# define DCF1_gamma_color_green 2
+# define DCF1_gamma_color_blue 3
+
+
+static unsigned char gamma_DCF2C[] = { 0x21, 0x00, 0x01, 0x00, 0x01, 0x00 };
+static scsiblk gamma_DCF2 = { gamma_DCF2C, sizeof(gamma_DCF2C) };
+
+#define set_DCF2_gamma_color(dc,val) setbitfield(dc + 0x00, 3, 2, val)
+# define DCF2_gamma_color_gray 0
+# define DCF2_gamma_color_red 1
+# define DCF2_gamma_color_green 2
+# define DCF2_gamma_color_blue 3
+
+#define set_DCF2_gamma_lines(dc,val) setbitfield(dc + 0x00, 3, 0, val)
+# define DCF2_gamma_one_line 1
+# define DCF2_gamma_three_lines 3
+
+#define set_DCF2_gamma_input_bits(dc,val) dc[0x02]=val
+#define set_DCF2_gamma_output_bits(dc,val) dc[0x04]=val
+# define DCF2_gamma_8_bits 1
+# define DCF2_gamma_10_bits 4
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char object_positionC[] =
+{ OBJECT_POSITION, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static scsiblk object_position = { object_positionC, sizeof(object_positionC) };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static unsigned char get_data_buffer_statusC[] =
+{
+ GET_DATA_BUFFER_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00 };
+
+#define set_GDBS_wait(sb, val) setbitfield(sb + 0x01, 1, 0, val)
+#define set_GDBS_len(sb, nwins) putnbyte(sb + 0x07, (4 + (nwins * 8)), 2)
+
+static scsiblk get_data_buffer_status = { get_data_buffer_statusC, sizeof(get_data_buffer_statusC) };
+
+ /* DBS is Data Buffer Status, the header that is returned */
+#define get_DBS_data_buffer_status_length(sb) getnbyte(sb, 3)
+#define get_DBSD_block_bit(sb) getbitfield(sb + 3, 1, 0)
+
+ /* DBSD are the Data Buffer Status Descriptors, the records that
+ * are returned following the DBS header. */
+ /* order is the number of the DBSD, from 1 to (length/8) */
+#define get_DBSD_winid(sb, order) sb[4 + ((order - 1) * 8)]
+#define get_DBSD_available_data_buffer(sb,order) getnbyte(sb+4+((order-1)*8)+2,3)
+#define get_DBSD_filled_data_buffer(sb,order) getnbyte(sb+4+((order-1)*8)+5,3)
+
+#define gdbs_return_block_size 12
+#endif
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+ static unsigned char write_bufferC[] =
+ { WRITE_BUFFER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static scsiblk write_buffer = { write_bufferC, sizeof(write_bufferC) };
+#endif
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static double analog_gamma_table[16]={1.0000, 1.0434, 1.0887, 1.1361, \
+ 1.1859, 1.2382, 1.2933, 1.3516, \
+ 1.4134, 1.4792, 1.5495, 1.6251, \
+ 1.7067, 1.7954, 1.8926, 2.0000 };
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static unsigned char request_senseC[] = { REQUEST_SENSE, 0x00, 0x00, 0x00, 0x00, 0x00 };
+#define set_RS_allocation_length(sb,val) sb[0x04]=val
+#define set_RS_LUN(sb,val) setbitfield(sb + 0x01, 7, 5) /* ??? */
+
+static scsiblk request_sense = { request_senseC, sizeof(request_senseC) };
+
+/* defines for request sense return block */
+#define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7)
+#define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0)
+#define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7)
+#define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6)
+#define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5)
+#define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0)
+#define get_RS_information(b) getnbyte(b+0x03, 4)
+#define get_RS_additional_length(b) b[0x07]
+#define get_RS_ASC(b) b[0x0c]
+#define get_RS_ASCQ(b) b[0x0d]
+#define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid */
+#define get_RS_CD(b) getbitfield(b+0x0f,1,6) /* 1=CDB */
+#define get_RS_field_pointer(b) getnbyte(b+0x10, 2)
+
+#define get_RS_additional_sense(b) getnbyte(b+0x12, 2)
+#define get_RS_asb_dim_light(b) getbitfield(b+0x12,1,7)
+#define get_RS_asb_no_light(b) getbitfield(b+0x12,1,6)
+#define get_RS_asb_sensor_motor(b) getbitfield(b+0x12,1,5)
+#define get_RS_asb_too_light(b) getbitfield(b+0x12,1,4)
+#define get_RS_asb_calibration(b) getbitfield(b+0x12,1,3)
+#define get_RS_asb_rom(b) getbitfield(b+0x12,1,2)
+#define get_RS_asb_ram(b) getbitfield(b+0x12,1,1)
+#define get_RS_asb_cpu(b) getbitfield(b+0x12,1,0)
+#define get_RS_asb_scsi(b) getbitfield(b+0x13,1,7)
+#define get_RS_asb_timer(b) getbitfield(b+0x13,1,6)
+#define get_RS_asb_filter_motor(b) getbitfield(b+0x13,1,5)
+#define get_RS_asb_dc_adjust(b) getbitfield(b+0x13,1,1)
+#define get_RS_asb_uta_sensor(b) getbitfield(b+0x13,1,0)
+
+#define get_RS_scanner_error_code(b) b[0x15]
+#define get_RS_SCC_condition_code(b) b[0x17]
+#define get_RS_SCC_calibration_bytesperline(b) getnbyte(b+0x18, 4)
+#define get_RS_SCC_calibration_lines(b) getnbyte(b+0x1c, 2)
+#define get_RS_SCC_calibration_bytespp(b) b[0x1e]
+
+#define rs_return_block_size 0x1f
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static char *cbhs_str[] = { "0-50","0-255", "0-255 autoexposure", "reserved" };
+
+static char *scanmode_str[] = { "flatbed (FB)", "automatic document feeder (ADF)" };
+
+static char *gamma_lines_str[]= { "reserved",
+ "one line (gray), three pass (color) download",
+ "reserved",
+ "one line (gray), three lines (color) download" };
+
+static char *color_sequence_str[]= { "R->G->B","R->B->G",
+ "G->B->R","G->R->B",
+ "B->R->G","B->G->R",
+ "reserved","all supported"};
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static char *sense_str[] = {"NO SENSE",
+ "RECOVERED ERROR",
+ "NOT READY",
+ "MEDIUM ERROR",
+ "HARDWARE ERROR",
+ "ILLEGAL REQUEST",
+ "UNIT ATTENTION",
+ "DATA PROTECT",
+ "BLANK CHECK",
+ "VENDOR SPECIFIC",
+ "COPY ABORTED",
+ "ABORTED COMMAND",
+ "EQUAL",
+ "VOLUME OVERFLOW",
+ "MISCOMPARE",
+ "??? - SENSE 0FH" };
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+static char *scanner_error_str[] =
+{"no error", /* 0 */
+ "CPU error",
+ "err 2",
+ "err 3",
+ "ROM error",
+ "err 5",
+ "err 6",
+ "err 7",
+ "err 8",
+ "err 9",
+ "buffer error", /* 10 */
+ "system buffer error",
+ "shading buffer error",
+ "video buffer error",
+ "stack buffer error",
+ "control buffer error",
+ "gamma buffer error",
+ "err 17",
+ "err 18",
+ "err 19",
+ "lamp error", /* 20 */
+ "dark error",
+ "dim error",
+ "light error",
+ "lamp adjust control error (by darken)",
+ "err 25",
+ "err 26",
+ "err 27",
+ "err 28",
+ "err 29",
+ "calibration error", /* 30 */
+ "dc offset error",
+ "gain error",
+ "auto focus error",
+ "err 34",
+ "err 35",
+ "err 36",
+ "err 37",
+ "err 38",
+ "err 39",
+ "scsi error", /* 40 */
+ "err 41",
+ "asic error",
+ "timer error",
+ "ccd error",
+ "err 45",
+ "err 46",
+ "err 47",
+ "err 48",
+ "err 49",
+ "uta error", /* 50 */
+ "uta home or motor sensor error",
+ "err 52",
+ "err 53",
+ "err 54",
+ "err 55",
+ "err 56",
+ "err 57",
+ "err 58",
+ "err 59",
+ "adf error", /* 60 */
+ "adf paper jam",
+ "adf no paper",
+ "adf cover open",
+ "err 64",
+ "err 65",
+ "err 66",
+ "err 67",
+ "err 68",
+ "err 69",
+ "fb sensor error", /* 70 */
+ "fb home or motor sensor error",
+ "fb filter or motor sensor error",
+ "fb lens or motor sensor error"
+ "first line position error (LER error, vertical)",
+ "first pixel position error (SER error, horizontal)",
+ "first pixel position error for lens 2 (SER2 error, horizontal)",
+ "err 77",
+ "err 78",
+ "err 79",
+ "err", /* 80 */
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err", /* 90 */
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err",
+ "err" /* 99 */
+};
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#endif
diff --git a/backend/umax-uc1200s.c b/backend/umax-uc1200s.c
new file mode 100644
index 0000000..97e5416
--- /dev/null
+++ b/backend/umax-uc1200s.c
@@ -0,0 +1,204 @@
+
+/* ------------------------------------------------------------------------- */
+
+/* umax-uc1200s.c: inquiry for UMAX scanner uc1200s
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UC1200S_INQUIRY[] =
+{
+#define UC1200S_INQUIRY_LEN 0x9d
+/* 24 F/W support function */
+ 0x03,
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability*/
+ 0x2f,
+ 0x0c,
+ 0x07,
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0xa3,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x01,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x0c, 0x2f,
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0xe8, /* 0xe9 or 0xe1 ? */
+
+/* 6e - 71 video memory */
+ 0x00, 0x04, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x06,
+
+/* 74 max x_res in 100 dpi */
+ 0x0c,
+
+/* 75 max y_res in 100 dpi */
+ 0x0c,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x20,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x04, 0x9c,
+
+/* 7a-7b uta x original point, may be 0x05, 0x89 */
+ 0x00, 0x76,
+
+/* 7c-7d uta y original point, may be 0x06, 0x75 */
+ 0x00, 0x89,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x4e,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x03, 0x65,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x30,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00,
+
+/* 9b fb/uta colour-sequnce-mode */
+ 0x01,
+
+/* 9c adf colour-sequnce-mode */
+ 0x01,
+
+/* 9d line-distance of ccd */
+ 0x08
+};
+
+static inquiry_blk inquiry_uc1200s =
+{
+ "UC1200S ",UC1200S_INQUIRY, UC1200S_INQUIRY_LEN
+};
diff --git a/backend/umax-uc1200se.c b/backend/umax-uc1200se.c
new file mode 100644
index 0000000..defe65a
--- /dev/null
+++ b/backend/umax-uc1200se.c
@@ -0,0 +1,204 @@
+
+/* ------------------------------------------------------------------------- */
+
+/* umax-uc1200se.c: inquiry for UMAX scanner uc1200se
+
+ (C) 1998-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UC1200SE_INQUIRY[] =
+{
+#define UC1200SE_INQUIRY_LEN 0x9d
+/* 24 F/W support function */
+ 0x03,
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability*/
+ 0x3e,
+ 0x0c,
+ 0x03,
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0x83,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x04,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x00, 0x2f,
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0xe1, /* 0xe8, 0xe9 or 0xe1 ? */
+
+/* 6e - 71 video memory */
+ 0x00, 0x20, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x06,
+
+/* 74 max x_res in 100 dpi */
+ 0x06,
+
+/* 75 max y_res in 100 dpi */
+ 0x0c,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x20,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x04, 0x4c,
+
+/* 7a-7b uta x original point, may be 0x05, 0x89 */
+ 0x00, 0x76,
+
+/* 7c-7d uta y original point, may be 0x06, 0x75 */
+ 0x00, 0x89,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x4e,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x03, 0x65,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x31,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00,
+
+/* 9b fb/uta colour-sequnce-mode */
+ 0x01,
+
+/* 9c adf colour-sequnce-mode */
+ 0x01,
+
+/* 9d line-distance of ccd */
+ 0x08
+};
+
+static inquiry_blk inquiry_uc1200se =
+{
+ "UC1200SE ",UC1200SE_INQUIRY, UC1200SE_INQUIRY_LEN
+};
diff --git a/backend/umax-uc1260.c b/backend/umax-uc1260.c
new file mode 100644
index 0000000..7743cd4
--- /dev/null
+++ b/backend/umax-uc1260.c
@@ -0,0 +1,204 @@
+/* ------------------------------------------------------------------------- */
+
+/* umax-uc1260.c: inquiry for UMAX scanner uc1260
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UC1260_INQUIRY[] =
+{
+#define UC1260_INQUIRY_LEN 0x9d
+/* 24 F/W support function */
+ 0x03,
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability*/
+ 0x31,
+ 0x0c,
+ 0x07,
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0xa3,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x01,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x0c, 0x2f,
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0x08,
+
+/* 6e - 71 video memory */
+ 0x00, 0x20, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x06,
+
+/* 74 max x_res in 100 dpi */
+ 0x06,
+
+/* 75 max y_res in 100 dpi */
+ 0x0c,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x52,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x05, 0x78,
+
+/* UTA parameters empirically determined on Peter Missel's unit */
+/* 7a-7b uta x original point */
+ 0x00, 0x77,
+
+/* 7c-7d uta y original point */
+ 0x00, 0x8f,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x63,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x03, 0x61,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x30,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00,
+
+/* 9b fb/uta colour-sequnce-mode */
+ 0x01,
+
+/* 9c adf colour-sequnce-mode */
+ 0x01,
+
+/* 9d line-distance of ccd */
+ 0x08
+};
+
+static inquiry_blk inquiry_uc1260 =
+{
+ "UC1260 ",UC1260_INQUIRY, UC1260_INQUIRY_LEN
+};
diff --git a/backend/umax-uc630.c b/backend/umax-uc630.c
new file mode 100644
index 0000000..26c6831
--- /dev/null
+++ b/backend/umax-uc630.c
@@ -0,0 +1,195 @@
+/* ------------------------------------------------------------------------- */
+
+/* umax-uc630.c: inquiry for UMAX scanner uc630
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UC630_INQUIRY[] =
+{
+#define UC630_INQUIRY_LEN 0x94
+/* 24 F/W support function */
+ 0x00, /* ? */
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability */
+ 0xfd,
+ 0x8c, /* 0xbc ? */
+ 0x03,
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0xa1,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x01,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x08, 0x2f,
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0xe5,
+
+/* 6e - 71 video memory */
+ 0x00, 0x04, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x04,
+
+/* 74 max x_res in 100 dpi */
+ 0x04,
+
+/* 75 max y_res in 100 dpi */
+ 0x04,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x52,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x04, 0x9c,
+
+/* 7a-7b uta x original point, may be 0x05, 0x89 */
+ 0x00, 0x76,
+
+/* 7c-7d uta y original point, may be 0x06, 0x75 */
+ 0x00, 0x89,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x4e,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x03, 0x65,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x30,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00
+};
+
+static inquiry_blk inquiry_uc630 =
+{
+ "UC630 ", UC630_INQUIRY, UC630_INQUIRY_LEN
+};
+
diff --git a/backend/umax-uc840.c b/backend/umax-uc840.c
new file mode 100644
index 0000000..8f430fe
--- /dev/null
+++ b/backend/umax-uc840.c
@@ -0,0 +1,195 @@
+/* ------------------------------------------------------------------------- */
+
+/* umax-uc840.c: inquiry for UMAX scanner uc840
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UC840_INQUIRY[] =
+{
+#define UC840_INQUIRY_LEN 0x94
+/* 24 F/W support function */
+ 0x00, /* ? */
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability*/
+ 0xfd,
+ 0x8c, /* 0xbc ? */
+ 0x03,
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0xa1,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x01,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x88, 0x2f,
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0xe5,
+
+/* 6e - 71 video memory */
+ 0x00, 0x20, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x04,
+
+/* 74 max x_res in 100 dpi */
+ 0x04,
+
+/* 75 max y_res in 100 dpi */
+ 0x08,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x52,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x05, 0x78,
+
+/* 7a-7b uta x original point, may be 0x05, 0x89 */
+ 0x00, 0x76,
+
+/* 7c-7d uta y original point, may be 0x06, 0x75 */
+ 0x00, 0x89,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x4e,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x03, 0x65,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x30,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00
+};
+
+static inquiry_blk inquiry_uc840 =
+{
+ "UC840 ", UC840_INQUIRY, UC840_INQUIRY_LEN
+};
+
diff --git a/backend/umax-ug630.c b/backend/umax-ug630.c
new file mode 100644
index 0000000..0a584b2
--- /dev/null
+++ b/backend/umax-ug630.c
@@ -0,0 +1,195 @@
+/* ------------------------------------------------------------------------- */
+
+/* umax-ug630.c: inquiry for UMAX scanner ug630
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UG630_INQUIRY[] =
+{
+#define UG630_INQUIRY_LEN 0x94
+/* 24 F/W support function */
+ 0x00,
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability */
+ 0xdc,
+ 0xbc, /* 8e ? */
+ 0x03, /* ? */
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0xa1,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x01,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x88, 0x21, /* ? */
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0x05,
+
+/* 6e - 71 video memory */
+ 0x00, 0x04, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x04,
+
+/* 74 max x_res in 100 dpi */
+ 0x04,
+
+/* 75 max y_res in 100 dpi */
+ 0x04,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x52,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x04, 0x92,
+
+/* 7a-7b uta x original point */
+ 0x00, 0x76,
+
+/* 7c-7d uta y original point */
+ 0x00, 0x89,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x4e,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x03, 0x65,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x30,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00
+};
+
+static inquiry_blk inquiry_ug630 =
+{
+ "UG630 ", UG630_INQUIRY, UG630_INQUIRY_LEN
+};
+
diff --git a/backend/umax-ug80.c b/backend/umax-ug80.c
new file mode 100644
index 0000000..b9d1d42
--- /dev/null
+++ b/backend/umax-ug80.c
@@ -0,0 +1,198 @@
+/* ------------------------------------------------------------------------- */
+
+/* umax-ug80.c: inquiry for UMAX scanner ug80
+
+ (C) 1998-2002 Oliver Rauch
+
+ Thanks to Andreas Hofmeister <hofmeist@informatik.uni-freiburg.de>
+ for his help!
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* ------------------------------------------------------------------------- */
+#include "umax-scanner.h"
+/* ------------------------------------------------------------------------- */
+
+static unsigned char UG80_INQUIRY[] =
+{
+#define UG80_INQUIRY_LEN 0x94
+/* 24 F/W support function */
+ 0x00,
+
+/* 25 -27 exposure-times */
+ 0x00, 0x00, 0x00,
+
+/* 28 - 29 reserved */
+ 0x00, 0x00,
+
+/* 2a - 35 exposure times */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+/* 36 - 37 reserved */
+ 0x00, 0x00,
+
+/* 38 - 5f scsi reserved */
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00,
+
+/* 60 -62 scanner capability */
+ 0xdc,
+ 0xb4,
+ 0x03, /* ? */
+
+/* 63 reserved */
+ 0x00,
+
+/* 64 gamma */
+ 0xa1,
+
+/* 65 reserved */
+ 0x00,
+
+/* 66 GIB */
+ 0x01,
+
+/* 67 reserved */
+ 0x00,
+
+/* 68 GOB */
+ 0x01,
+
+/* 69 - 6a halftone */
+ 0x88, 0x21, /* ? */
+
+/* 6b - 6c reserved */
+ 0x00, 0x00,
+
+/* 6d color sequence */
+ 0x05,
+
+/* 6e - 71 video memory */
+ 0x00, 0x01, 0x00, 0x00,
+
+/* 72 reserved */
+ 0x00,
+
+/* 73 max optical res in 100 dpi */
+ 0x03,
+
+/* 74 max x_res in 100 dpi */
+ 0x03,
+
+/* 75 max y_res in 100 dpi */
+ 0x03,
+
+/* 76-77 fb max scan width in 0.01 inch */
+ 0x03, 0x52,
+
+/* 78-79 fb max scan length in 0.01 inch */
+ 0x05, 0x78,
+
+/* 7a-7b uta x original point */
+ 0x00, 0x76,
+
+/* 7c-7d uta y original point */
+ 0x00, 0x89,
+
+/* 7e-7f uta max scan width in 0.01 inch */
+ 0x02, 0x4e,
+
+/* 80-81 uta max scan length in 0.01 inch */
+ 0x05, 0x78,
+
+/* 82-85 reserved */
+ 00, 00, 00, 00,
+
+/* 86-87 dor x original point */
+ 0x00, 0x00,
+
+/* 88-89 dor x original point */
+ 0x00, 0x00,
+
+/* 8a-8b dor max scan width in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8c-8d dor max scan length in 0.01 inch */
+ 0x00, 0x00,
+
+/* 8e reserved */
+ 0x00,
+
+/* 8f last calibration lamp density */
+ 0x00,
+
+/* 90 reserved */
+ 0x00,
+
+/* 91 lamp warmup max time */
+ 0x00,
+
+/* 92-93 window descriptor block length */
+ 0x00, 0x30,
+
+/* 94 optical resolution residue (1dpi) */
+ 0x00,
+
+/* 95 x_resolution residue (1dpi) */
+ 0x00,
+
+/* 96 y_resolution residue (1dpi) */
+ 0x00,
+
+/* 97 analog gamma table */
+ 0x00,
+
+/* 98-99 reserved */
+ 0x00, 0x00,
+
+/* 9a max calibration data lines */
+ 0x00
+};
+
+static inquiry_blk inquiry_ug80 =
+{
+ "UG80 ",UG80_INQUIRY,UG80_INQUIRY_LEN,
+};
+
diff --git a/backend/umax-usb.c b/backend/umax-usb.c
new file mode 100644
index 0000000..c26aeae
--- /dev/null
+++ b/backend/umax-usb.c
@@ -0,0 +1,324 @@
+/* ---------------------------------------------------------------------- */
+
+/* sane - Scanner Access Now Easy.
+
+ umax-usb.c
+
+ (C) 2001-2002 Frank Zago
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for UMAX USB flatbed scanners. */
+
+
+/* ---------------------------------------------------------------------- */
+
+#include "../include/sane/sanei_usb.h"
+
+#include "../include/sane/sanei_pv8630.h"
+
+/* USB specific parts */
+
+/* Apparently this will recover from some errors. */
+static void pv8630_mini_init_scanner(int fd)
+{
+ DBG(DBG_info, "mini_init_scanner\n");
+
+ /* (re-)init the device (?) */
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04 );
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02 );
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02 );
+
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+}
+
+/* Length of the CDB given the SCSI command. The last two are not
+ correct (vendor reserved). */
+static u_char cdb_sizes[8] = {
+ 6, 10, 10, 6, 16, 12, 0, 0
+};
+#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)]
+
+/* Sends a CDB to the scanner. Also sends the parameters and receives
+ * the data, if necessary. When this function returns with a
+ * SANE_STATUS_GOOD, the SCSI command has been completed.
+ *
+ * Note: I don't know about deferred commands.
+ */
+static SANE_Status sanei_umaxusb_cmd(int fd, const void *src, size_t src_size, void *dst, size_t * dst_size)
+{
+ unsigned char result;
+ size_t cmd_size = CDB_SIZE (*(const char *) src);
+ size_t param_size = src_size - cmd_size;
+ const char * param_ptr = ((const char *) src) + cmd_size;
+ size_t tmp_len;
+
+ DBG(DBG_info, "Sending SCSI cmd 0x%02x cdb len %ld, param len %ld, result len %ld\n", ((const unsigned char *)src)[0], (long)cmd_size, (long)param_size, dst_size? (long)*dst_size:(long)0);
+
+ /* This looks like some kind of pre-initialization. */
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+
+ /* Send the CDB and check it's been received OK. */
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x16);
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkwrite(fd, cmd_size);
+
+ tmp_len = cmd_size;
+ sanei_pv8630_bulkwrite(fd, src, &tmp_len);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, 1);
+
+ result = 0xA5; /* to be sure */
+ tmp_len = 1;
+ sanei_pv8630_bulkread(fd, &result, &tmp_len);
+ if (result != 0) {
+ DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
+ if (result == 8) {
+ pv8630_mini_init_scanner(fd);
+ }
+ return(SANE_STATUS_IO_ERROR);
+ }
+
+ /* Send the parameters and check they've been received OK. */
+ if (param_size) {
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkwrite(fd, param_size);
+
+ tmp_len = param_size;
+ sanei_pv8630_bulkwrite(fd, param_ptr, &tmp_len);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, 1);
+
+ result = 0xA5; /* to be sure */
+ tmp_len = 1;
+ sanei_pv8630_bulkread(fd, &result, &tmp_len);
+ if (result != 0) {
+ DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
+ if (result == 8) {
+ pv8630_mini_init_scanner(fd);
+ }
+ return(SANE_STATUS_IO_ERROR);
+ }
+ }
+
+ /* If the SCSI command expect a return, get it. */
+ if (dst_size != NULL && *dst_size != 0 && dst != NULL) {
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, *dst_size);
+ sanei_pv8630_bulkread(fd, dst, dst_size);
+
+ DBG(DBG_info, " SCSI cmd returned %lu bytes\n", (u_long) *dst_size);
+
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, 1);
+
+ result = 0x5A; /* just to be sure */
+ tmp_len = 1;
+ sanei_pv8630_bulkread(fd, &result, &tmp_len);
+ if (result != 0) {
+ DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
+ if (result == 8) {
+ pv8630_mini_init_scanner(fd);
+ }
+ return(SANE_STATUS_IO_ERROR);
+ }
+ }
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+
+ DBG(DBG_info, " SCSI command successfully executed\n");
+
+ return(SANE_STATUS_GOOD);
+}
+
+/* Initialize the PowerVision 8630. */
+static SANE_Status pv8630_init_umaxusb_scanner(int fd)
+{
+ DBG(DBG_info, "Initializing the PV8630\n");
+
+ /* Init the device */
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x16);
+
+ DBG(DBG_info, "PV8630 initialized\n");
+
+ return(SANE_STATUS_GOOD);
+}
+
+
+/*
+ * SCSI functions for the emulation.
+ *
+ * The following functions emulate their sanei_scsi_* counterpart.
+ *
+ */
+
+
+/*
+ * sanei_umaxusb_req_wait() and sanei_umaxusb_req_enter()
+ *
+ * I don't know if it is possible to queue the reads to the
+ * scanner. So The queing is disabled. The performance does not seems
+ * to be bad anyway.
+ */
+
+static void *umaxusb_req_buffer; /* keep the buffer ptr as an ID */
+
+static SANE_Status sanei_umaxusb_req_enter (int fd,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+{
+ umaxusb_req_buffer = *idp = dst;
+ return(sanei_umaxusb_cmd(fd, src, src_size, dst, dst_size));
+}
+
+static SANE_Status
+sanei_umaxusb_req_wait (void *id)
+{
+ if (id != umaxusb_req_buffer) {
+ DBG(DBG_info, "sanei_umaxusb_req_wait: AIE, invalid id\n");
+ return(SANE_STATUS_IO_ERROR);
+ }
+ return(SANE_STATUS_GOOD);
+}
+
+/* Open the device.
+ */
+static SANE_Status
+sanei_umaxusb_open (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ SANE_Status status;
+
+ handler = handler; /* silence gcc */
+ handler_arg = handler_arg; /* silence gcc */
+
+ status = sanei_usb_open (dev, fdp);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sanei_umaxusb_open: open of `%s' failed: %s\n",
+ dev, sane_strstatus(status));
+ return status;
+ } else {
+ SANE_Word vendor;
+ SANE_Word product;
+
+ /* We have openned the device. Check that it is a USB scanner. */
+ if (sanei_usb_get_vendor_product (*fdp, &vendor, &product) != SANE_STATUS_GOOD) {
+ /* This is not a USB scanner, or SANE or the OS doesn't support it. */
+ sanei_usb_close(*fdp);
+ *fdp = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* So it's a scanner. Does this backend support it?
+ * Only the UMAX 2200 USB is currently supported. */
+ if ((vendor != 0x1606) || (product != 0x0230)) {
+ sanei_usb_close(*fdp);
+ *fdp = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* It's a good scanner. Initialize it.
+ *
+ * Note: pv8630_init_umaxusb_scanner() is for the UMAX
+ * 2200. Other UMAX scanner might need a different
+ * initialization routine. */
+
+ pv8630_init_umaxusb_scanner(*fdp);
+ }
+
+ return(SANE_STATUS_GOOD);
+}
+
+/* sanei_umaxusb_open_extended() is just a passthrough for sanei_umaxusb_open(). */
+static SANE_Status
+sanei_umaxusb_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg, int *buffersize)
+{
+ buffersize = buffersize;
+ return(sanei_umaxusb_open(dev, fdp, handler, handler_arg));
+}
+
+/* Close the scanner. */
+static void
+sanei_umaxusb_close (int fd)
+{
+ sanei_usb_close(fd);
+}
+
+
+
+
diff --git a/backend/umax.c b/backend/umax.c
new file mode 100644
index 0000000..b2ceb00
--- /dev/null
+++ b/backend/umax.c
@@ -0,0 +1,8131 @@
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* sane - Scanner Access Now Easy.
+
+ umax.c
+
+ (C) 1997-2007 Oliver Rauch
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for UMAX flatbed scanners. */
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#define BUILD 45
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* SANE-FLOW-DIAGRAMM
+
+ - sane_init() : initialize backend, attach scanners(devicename,0)
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev)
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+in ADF mode this is done often:
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+
+ . . - sane_cancel() : cancel operation, kill reader_process
+
+ . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
+ - sane_exit() : terminate use of backend, free devicename and device-struture
+*/
+
+
+/* ------------------------------------------------------------ DBG OUTPUT LEVELS -------------------------- */
+
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+
+/* ------------------------------------------------------------ SANE DEFINES ------------------------------- */
+
+#define BACKEND_NAME umax
+#define UMAX_CONFIG_FILE "umax.conf"
+
+/* ------------------------------------------------------------ SANE INTERNATIONALISATION ------------------ */
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/* ------------------------------------------------------------ INCLUDES ----------------------------------- */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifdef UMAX_ENABLE_USB
+# include "sane/sanei_usb.h"
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "umax-scsidef.h"
+#include "umax-scanner.c"
+
+#ifdef UMAX_ENABLE_USB
+# include "umax-usb.c"
+#endif
+
+#include "umax.h"
+
+/* ------------------------------------------------------------ SANE DEFINES ------------------------------- */
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------ OPTION DEFINITIONS ------------------------- */
+
+#define SANE_NAME_BATCH_SCAN_START "batch-scan-start"
+#define SANE_TITLE_BATCH_SCAN_START "Batch scan start"
+#define SANE_DESC_BATCH_SCAN_START "set for first scan of batch"
+
+#define SANE_NAME_BATCH_SCAN_LOOP "batch-scan-loop"
+#define SANE_TITLE_BATCH_SCAN_LOOP "Batch scan loop"
+#define SANE_DESC_BATCH_SCAN_LOOP "set for middle scans of batch"
+
+#define SANE_NAME_BATCH_SCAN_END "batch-scan-end"
+#define SANE_TITLE_BATCH_SCAN_END "Batch scan end"
+#define SANE_DESC_BATCH_SCAN_END "set for last scan of batch"
+
+#define SANE_NAME_BATCH_NEXT_TL_Y "batch-scan-next-tl-y"
+#define SANE_TITLE_BATCH_NEXT_TL_Y "Batch scan next top left Y"
+#define SANE_DESC_BATCH_NEXT_TL_Y "Set top left Y position for next scan"
+
+#define SANE_UMAX_NAME_SELECT_CALIBRATON_EXPOSURE_TIME "select-calibration-exposure-time"
+#define SANE_UMAX_TITLE_SELECT_CALIBRATION_EXPOSURE_TIME "Set calibration exposure time"
+#define SANE_UMAX_DESC_SELECT_CALIBRATION_EXPOSURE_TIME "Allow different settings for calibration and scan exposure times"
+
+/* ------------------------------------------------------------ STRING DEFINITIONS ------------------------- */
+
+#define FLB_STR SANE_I18N("Flatbed")
+#define UTA_STR SANE_I18N("Transparency Adapter")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+#define LINEART_STR SANE_VALUE_SCAN_MODE_LINEART
+#define HALFTONE_STR SANE_VALUE_SCAN_MODE_HALFTONE
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_LINEART_STR SANE_VALUE_SCAN_MODE_COLOR_LINEART
+#define COLOR_HALFTONE_STR SANE_VALUE_SCAN_MODE_COLOR_HALFTONE
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+/* ------------------------------------------------------------ DEFINITIONS -------------------------------- */
+
+#define P_200_TO_255(per) ( (SANE_UNFIX(per) + 100) * 255/200 )
+#define P_100_TO_255(per) SANE_UNFIX(per * 255/100 )
+#define P_100_TO_254(per) 1+SANE_UNFIX(per * 254/100 )
+
+/* ------------------------------------------------------------ GLOBAL VARIABLES --------------------------- */
+
+
+static SANE_String scan_mode_list[7];
+static SANE_String_Const source_list[4];
+static SANE_Int bit_depth_list[9];
+static SANE_Auth_Callback frontend_authorize_callback;
+
+static int umax_scsi_buffer_size_min = 32768; /* default: minimum scsi buffer size: 32 KB */
+static int umax_scsi_buffer_size_max = 131072; /* default: maximum scsi buffer size: 128 KB */
+
+/* number of lines that shall be scanned in one buffer for preview if possible */
+/* this value should not be too large because it defines the step size in which */
+/* the scanned parts are displayed while preview scan is in progress */
+static int umax_preview_lines = 10; /* default: 10 preview lines */
+/* number of lines that shall be scanned in one buffer for scan if possible */
+static int umax_scan_lines = 40; /* default: 40 scan lines */
+static int umax_scsi_maxqueue = 2; /* use command queueing depth 2 as default */
+static int umax_handle_bad_sense_error = 0; /* default: handle bad sense error code as device busy */
+static int umax_execute_request_sense = 0; /* default: do not use request sense in do_calibration */
+static int umax_force_preview_bit_rgb = 0; /* default: do not force preview bit in real color scan */
+static int umax_slow = -1; /* don`t use slow scanning speed */
+static int umax_smear = -1; /* don`t care about image smearing problem */
+static int umax_calibration_area = -1; /* -1=auto, 0=calibration on image, 1=calibration for full ccd */
+static int umax_calibration_width_offset = -99999; /* -99999=auto */
+static int umax_calibration_width_offset_batch = -99999; /* -99999=auto */
+static int umax_calibration_bytespp = -1; /* -1=auto */
+static int umax_exposure_time_rgb_bind = -1; /* -1=auto */
+static int umax_invert_shading_data = -1; /* -1=auto */
+static int umax_lamp_control_available = 0; /* 0=disabled */
+static int umax_gamma_lsb_padded = -1; /* -1=auto */
+static int umax_connection_type = 1; /* 1=scsi, 2=usb */
+
+/* ------------------------------------------------------------ CALIBRATION MODE --------------------------- */
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+
+#define CALIB_MODE_0000 SANE_I18N("Use Image Composition")
+#define CALIB_MODE_1111 SANE_I18N("Bi-level black and white (lineart mode)")
+#define CALIB_MODE_1110 SANE_I18N("Dithered/halftone black & white (halftone mode)")
+#define CALIB_MODE_1101 SANE_I18N("Multi-level black & white (grayscale mode)")
+#define CALIB_MODE_1010 SANE_I18N("Multi-level RGB color (one pass color)")
+#define CALIB_MODE_1001 SANE_I18N("Ignore calibration")
+
+static SANE_String_Const calibration_list[] =
+{
+ CALIB_MODE_0000,
+ CALIB_MODE_1111,
+ CALIB_MODE_1110,
+ CALIB_MODE_1101,
+ CALIB_MODE_1010,
+ CALIB_MODE_1001,
+ 0
+};
+
+#endif
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+enum
+{
+ UMAX_CALIBRATION_AREA_IMAGE = 0,
+ UMAX_CALIBRATION_AREA_CCD
+};
+
+static const SANE_Int pattern_dim_list[] =
+{
+ 5, /* # of elements */
+ 2, 4, 6, 8, 12
+};
+
+static const SANE_Range u8_range =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range percentage_range =
+{
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static const SANE_Range percentage_range_100 =
+{
+ 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 0 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+static Umax_Device *first_dev = NULL;
+static Umax_Scanner *first_handle = NULL;
+
+
+/* ------------------------------------------------------------ MATH-HELPERS ------------------------------- */
+
+
+#define min(a, b) (((a)<(b))?(a):(b))
+#define max(a, b) (((a)>(b))?(a):(b))
+#define inrange(minimum, val, maximum) (min(maximum, max(val, minimum)))
+
+
+/* ------------------------------------------------------------ umax_test_little_endian -------------------- */
+
+static SANE_Bool umax_test_little_endian(void)
+{
+ SANE_Int testvalue = 255;
+ unsigned char *firstbyte = (unsigned char *) &testvalue;
+
+ if (*firstbyte == 255)
+ {
+ return SANE_TRUE;
+ }
+
+ return SANE_FALSE;
+}
+
+/* ------------------------------------------------------------ DBG_inq_nz --------------------------------- */
+
+
+static void DBG_inq_nz(char *text, int flag)
+{
+ if (flag != 0) { DBG(DBG_inquiry,"%s", text); }
+}
+
+
+/*------------------------------------------------------------- DBG_sense_nz ------------------------------- */
+
+
+static void DBG_sense_nz(char *text, int flag)
+{
+ if (flag != 0) { DBG(DBG_sense,"%s", text); }
+}
+
+
+/* ------------------------------------------------------------ UMAX PRINT INQUIRY ------------------------- */
+
+
+static void umax_print_inquiry(Umax_Device *dev)
+{
+ unsigned char *inquiry_block;
+ int i;
+
+ inquiry_block=dev->buffer[0];
+
+ DBG(DBG_inquiry,"INQUIRY:\n");
+ DBG(DBG_inquiry,"========\n");
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"vendor........................: '%s'\n", dev->vendor);
+ DBG(DBG_inquiry,"product.......................: '%s'\n", dev->product);
+ DBG(DBG_inquiry,"version.......................: '%s'\n", dev->version);
+
+ DBG(DBG_inquiry,"peripheral qualifier..........: %d\n", get_inquiry_periph_qual(inquiry_block));
+ DBG(DBG_inquiry,"peripheral device type........: %d\n", get_inquiry_periph_devtype(inquiry_block));
+
+ DBG_inq_nz("RMB bit set (reserved)\n", get_inquiry_rmb(inquiry_block));
+ DBG_inq_nz("0x01 bit 6\n", get_inquiry_0x01_bit6(inquiry_block));
+ DBG_inq_nz("0x01 bit 5\n", get_inquiry_0x01_bit5(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"CBHS value range..............: %s\n", cbhs_str[dev->inquiry_cbhs]);
+ DBG(DBG_inquiry,"scanmode......................: %s\n", scanmode_str[get_inquiry_scanmode(inquiry_block)]);
+ if (dev->inquiry_transavail)
+ {
+ DBG(DBG_inquiry,"UTA (transparency)............: available\n");
+
+ if (get_inquiry_translamp(inquiry_block) == 0)
+ { DBG(DBG_inquiry,"UTA lamp status ..............: off\n"); }
+ else
+ { DBG(DBG_inquiry,"UTA lamp status ..............: on\n"); }
+
+ DBG(DBG_inquiry,"\n");
+ }
+
+ DBG(DBG_inquiry,"inquiry block length..........: %d bytes\n", dev->inquiry_len);
+ if (dev->inquiry_len<=0x8e)
+ {
+ DBG(DBG_inquiry, "Inquiry block is unexpected short, should be at least 147 bytes\n");
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"ISO Version (reserved).......: %d\n", get_inquiry_iso_version(inquiry_block));
+ DBG(DBG_inquiry,"ECMA Version (reserved).......: %d\n", get_inquiry_ecma_version(inquiry_block));
+ DBG(DBG_inquiry,"ANSI Version .................: %d\n", get_inquiry_ansi_version(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ DBG_inq_nz("AENC bit set (reserved)\n", get_inquiry_aenc(inquiry_block));
+ DBG_inq_nz("TmIOP bit set (reserved)\n", get_inquiry_tmiop(inquiry_block));
+ DBG_inq_nz("0x03 bit 5\n", get_inquiry_0x03_bit5(inquiry_block));
+ DBG_inq_nz("0x03 bit 4\n", get_inquiry_0x03_bit4(inquiry_block));
+
+ DBG(DBG_inquiry,"reserved byte 0x05 = %d\n", get_inquiry_0x05(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x06 = %d\n", get_inquiry_0x06(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"scsi features (%02x):\n", get_inquiry_scsi_byte(inquiry_block));
+ DBG(DBG_inquiry,"-------------------\n");
+ DBG_inq_nz(" - relative address\n", get_inquiry_scsi_reladr(inquiry_block));
+ DBG_inq_nz(" - wide bus 32 bit\n", get_inquiry_scsi_wbus32(inquiry_block));
+ DBG_inq_nz(" - wide bus 16 bit\n", get_inquiry_scsi_wbus16(inquiry_block));
+ DBG_inq_nz(" - syncronous neg.\n", get_inquiry_scsi_sync(inquiry_block));
+ DBG_inq_nz(" - linked commands\n", get_inquiry_scsi_linked(inquiry_block));
+ DBG_inq_nz(" - (reserved)\n", get_inquiry_scsi_R(inquiry_block));
+ DBG_inq_nz(" - command queueing\n", get_inquiry_scsi_cmdqueue(inquiry_block));
+ DBG_inq_nz(" - sftre\n", get_inquiry_scsi_sftre(inquiry_block));
+
+ /* 0x24 */
+ if (dev->inquiry_len<=0x24)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"f/w support function:\n");
+ DBG(DBG_inquiry,"---------------------\n");
+ DBG_inq_nz(" - quality calibration\n", dev->inquiry_quality_ctrl);
+ DBG_inq_nz(" - fast preview function\n", dev->inquiry_preview);
+ DBG_inq_nz(" - shadow compensation by f/w\n", get_inquiry_fw_shadow_comp(inquiry_block));
+ DBG_inq_nz(" - reselection phase\n", get_inquiry_fw_reselection(inquiry_block));
+ DBG_inq_nz(" - lamp intensity control\n", dev->inquiry_lamp_ctrl);
+ DBG_inq_nz(" - batch scan function\n", get_inquiry_fw_batch_scan(inquiry_block));
+ DBG_inq_nz(" - calibration mode control by driver\n", get_inquiry_fw_calibration(inquiry_block));
+
+ /* 0x36, 0x37 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x36 = %d\n", get_inquiry_0x36(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x37 = %d\n", get_inquiry_0x37(inquiry_block));
+
+ if (get_inquiry_fw_adjust_exposure_tf(inquiry_block))
+ {
+ int unit;
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"adjust exposure time function\n");
+ unit=get_inquiry_exposure_time_step_unit(inquiry_block);
+ DBG(DBG_inquiry,"exposure time step units......: %d micro-sec\n", unit);
+ DBG(DBG_inquiry,"exposure time maximum.........: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_max(inquiry_block));
+ DBG(DBG_inquiry,"exposure time minimum (LHG)...: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_lhg_min(inquiry_block));
+ DBG(DBG_inquiry,"exposure time minimum color...: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_color_min(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB (LH).: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_lh_def_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA (LH): %d micro-sec\n",
+ unit*get_inquiry_exposure_time_lh_def_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB gray.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_gray_def_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA gray: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_gray_def_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB red..: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_r_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB grn..: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_g_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB blue.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_b_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA red.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_r_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA grn.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_g_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA blue: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_b_uta(inquiry_block));
+ }
+
+
+ /* 0x60 */
+ if (dev->inquiry_len<=0x60)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"scan modes (%02x):\n", get_inquiry_sc_feature_byte0(inquiry_block));
+ DBG(DBG_inquiry,"----------------\n");
+ DBG_inq_nz(" - three passes color mode\n", get_inquiry_sc_three_pass_color(inquiry_block));
+ DBG_inq_nz(" - single pass color mode\n", get_inquiry_sc_one_pass_color(inquiry_block));
+ DBG_inq_nz(" - lineart mode\n", dev->inquiry_lineart);
+ DBG_inq_nz(" - halftone mode\n", dev->inquiry_halftone);
+ DBG_inq_nz(" - gray mode\n", dev->inquiry_gray);
+ DBG_inq_nz(" - color mode\n", dev->inquiry_color);
+ DBG_inq_nz(" - transparency (UTA)\n", dev->inquiry_uta);
+ DBG_inq_nz(" - automatic document feeder (ADF)\n", dev->inquiry_adf);
+
+ /* 0x61 */
+ if (dev->inquiry_len<=0x61)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"scanner capability (%02x, %02x, %02x):\n",
+ get_inquiry_sc_feature_byte1(inquiry_block),
+ get_inquiry_sc_feature_byte2(inquiry_block),
+ get_inquiry_sc_feature_byte3(inquiry_block));
+ DBG(DBG_inquiry,"--------------------------------\n");
+ DBG_inq_nz(" - double resolution\n", dev->inquiry_dor);
+ DBG_inq_nz(" - send high byte first\n", get_inquiry_sc_high_byte_first(inquiry_block));
+ DBG_inq_nz(" - bi-level image reverse\n", dev->inquiry_reverse);
+ DBG_inq_nz(" - multi-level image reverse\n", dev->inquiry_reverse_multi);
+ DBG_inq_nz(" - support shadow function\n", dev->inquiry_shadow);
+ DBG_inq_nz(" - support highlight function\n", dev->inquiry_highlight);
+ DBG_inq_nz(" - f/w downloadable\n", get_inquiry_sc_downloadable_fw(inquiry_block));
+ DBG_inq_nz(" - paper length can reach to 14 inch\n", get_inquiry_sc_paper_length_14(inquiry_block));
+
+ /* 0x62 */
+ if (dev->inquiry_len<=0x62)
+ {
+ return;
+ }
+
+ DBG_inq_nz(" - shading data/gain uploadable\n", get_inquiry_sc_uploadable_shade(inquiry_block));
+ DBG_inq_nz(" - analog gamma correction\n", dev->inquiry_analog_gamma);
+ DBG_inq_nz(" - x, y coordinate base\n", get_inquiry_xy_coordinate_base(inquiry_block));
+ DBG_inq_nz(" - lineart starts with LSB\n", get_inquiry_lineart_order(inquiry_block));
+ DBG_inq_nz(" - start density \n", get_inquiry_start_density(inquiry_block));
+ DBG_inq_nz(" - hardware x scaling\n", get_inquiry_hw_x_scaling(inquiry_block));
+ DBG_inq_nz(" - hardware y scaling\n", get_inquiry_hw_y_scaling(inquiry_block));
+
+ /* 0x63 */
+ if (dev->inquiry_len<=0x63)
+ {
+ return;
+ }
+
+ DBG_inq_nz(" + ADF: no paper\n", get_inquiry_ADF_no_paper(inquiry_block));
+ DBG_inq_nz(" + ADF: cover open\n", get_inquiry_ADF_cover_open(inquiry_block));
+ DBG_inq_nz(" + ADF: paper jam\n", get_inquiry_ADF_paper_jam(inquiry_block));
+ DBG_inq_nz(" - unknwon flag; 0x63 bit 3\n", get_inquiry_0x63_bit3(inquiry_block));
+ DBG_inq_nz(" - unknown lfag: 0x63 bit 4\n", get_inquiry_0x63_bit4(inquiry_block));
+ DBG_inq_nz(" - lens calib in doc pos\n", get_inquiry_lens_cal_in_doc_pos(inquiry_block));
+ DBG_inq_nz(" - manual focus\n", get_inquiry_manual_focus(inquiry_block));
+ DBG_inq_nz(" - UTA lens calib pos selectable\n", get_inquiry_sel_uta_lens_cal_pos(inquiry_block));
+
+ /* 0x64 - 0x68*/
+ if (dev->inquiry_len<=0x68)
+ {
+ return;
+ }
+
+ if (dev->inquiry_gamma_dwload)
+ {
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"gamma download available\n");
+ DBG_inq_nz("gamma download type 2\n", get_inquiry_gamma_type_2(inquiry_block));
+ DBG(DBG_inquiry,"lines of gamma curve: %s\n", gamma_lines_str[get_inquiry_gamma_lines(inquiry_block)]);
+
+ /* 0x66 */
+ DBG_inq_nz("gamma input 8 bits/pixel support\n", get_inquiry_gib_8bpp(inquiry_block));
+ DBG_inq_nz("gamma input 9 bits/pixel support\n", get_inquiry_gib_9bpp(inquiry_block));
+ DBG_inq_nz("gamma input 10 bits/pixel support\n", get_inquiry_gib_10bpp(inquiry_block));
+ DBG_inq_nz("gamma input 12 bits/pixel support\n", get_inquiry_gib_12bpp(inquiry_block));
+ DBG_inq_nz("gamma input 14 bits/pixel support\n", get_inquiry_gib_14bpp(inquiry_block));
+ DBG_inq_nz("gamma input 16 bits/pixel support\n", get_inquiry_gib_16bpp(inquiry_block));
+ DBG_inq_nz("0x66 bit 6\n", get_inquiry_0x66_bit6(inquiry_block));
+ DBG_inq_nz("0x66 bit 7\n", get_inquiry_0x66_bit7(inquiry_block));
+
+ /* 0x68 */
+ DBG_inq_nz("gamma output 8 bits/pixel support\n", get_inquiry_gob_8bpp(inquiry_block));
+ DBG_inq_nz("gamma output 9 bits/pixel support\n", get_inquiry_gob_9bpp(inquiry_block));
+ DBG_inq_nz("gamma output 10 bits/pixel support\n", get_inquiry_gob_10bpp(inquiry_block));
+ DBG_inq_nz("gamma output 12 bits/pixel support\n", get_inquiry_gob_12bpp(inquiry_block));
+ DBG_inq_nz("gamma output 14 bits/pixel support\n", get_inquiry_gob_14bpp(inquiry_block));
+ DBG_inq_nz("gamma output 16 bits/pixel support\n", get_inquiry_gob_16bpp(inquiry_block));
+ DBG_inq_nz("0x68 bit 6\n", get_inquiry_0x68_bit6(inquiry_block));
+ DBG_inq_nz("0x68 bit 7\n", get_inquiry_0x68_bit7(inquiry_block));
+ }
+
+ /* 0x64 - 0x68 reserved bits */
+ DBG_inq_nz("0x64 bit 2\n", get_inquiry_0x64_bit2(inquiry_block));
+ DBG_inq_nz("0x64 bit 3\n", get_inquiry_0x64_bit3(inquiry_block));
+ DBG_inq_nz("0x64 bit 4\n", get_inquiry_0x64_bit4(inquiry_block));
+ DBG_inq_nz("0x64 bit 6\n", get_inquiry_0x64_bit6(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x65 = %d\n", get_inquiry_0x65(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x67 = %d\n", get_inquiry_0x67(inquiry_block));
+
+
+ /* 0x69 */
+ if (dev->inquiry_len<=0x69)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ if (get_inquiry_hda(inquiry_block))
+ {
+ DBG(DBG_inquiry,"halftone download available\n");
+ DBG(DBG_inquiry,"halftone pattern download max matrix %dx%d\n",
+ get_inquiry_max_halftone_matrix(inquiry_block),
+ get_inquiry_max_halftone_matrix(inquiry_block));
+ }
+
+ /* 0x6a */
+ if (dev->inquiry_len<=0x6a)
+ {
+ return;
+ }
+
+ DBG_inq_nz("built-in halftone patterns:\n", get_inquiry_halftones_supported(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 2x2\n", get_inquiry_halftones_2x2(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 4x4\n", get_inquiry_halftones_4x4(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 6x6\n", get_inquiry_halftones_6x6(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 8x8\n", get_inquiry_halftones_8x8(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 12x12\n", get_inquiry_halftones_12x12(inquiry_block));
+
+ /* 0x6b, 0x6c */
+ DBG(DBG_inquiry,"reserved byte 0x6b = %d\n", get_inquiry_0x6b(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x6c = %d\n", get_inquiry_0x6c(inquiry_block));
+
+
+ /* 0x6d */
+ if (dev->inquiry_len<=0x6d)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"color sequence............................: %s\n",
+ color_sequence_str[get_inquiry_colorseq(inquiry_block)]);
+ DBG_inq_nz("color ordering support....................: pixel\n",
+ get_inquiry_color_order_pixel(inquiry_block));
+ DBG_inq_nz("color ordering support....................: line without CCD distance\n",
+ get_inquiry_color_order_line_no_ccd(inquiry_block));
+ DBG_inq_nz("color ordering support....................: plane\n",
+ get_inquiry_color_order_plane(inquiry_block));
+ DBG_inq_nz("color ordering support....................: line with CCD distance\n",
+ get_inquiry_color_order_line_w_ccd(inquiry_block));
+ DBG_inq_nz("color ordering support....................: (reserved)\n",
+ get_inquiry_color_order_reserved(inquiry_block));
+
+ /* 0x6e */
+ if (dev->inquiry_len<=0x71)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"maximum video memory......................: %d KB\n", dev->inquiry_vidmem/1024);
+
+ /* 0x72 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x72 = %d\n", get_inquiry_0x72(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* 0x73/0x94 - 0x75/0x96 */
+ if (dev->inquiry_len<=0x75)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"optical resolution........................: %d dpi\n", dev->inquiry_optical_res);
+ DBG(DBG_inquiry,"maximum x-resolution......................: %d dpi\n", dev->inquiry_x_res);
+ DBG(DBG_inquiry,"maximum y-resolution......................: %d dpi\n", dev->inquiry_y_res);
+
+ /* ---------- */
+
+ /* 0x76 0x77 */
+ if (dev->inquiry_len<=0x77)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"FB (flatbed-mode):\n");
+ DBG(DBG_inquiry,"FB maximum scan width.....................: %2.2f inch\n", dev->inquiry_fb_width);
+ DBG(DBG_inquiry,"FB maximum scan length....................: %2.2f inch\n", dev->inquiry_fb_length);
+
+ /* ---------- */
+
+ /* 0x7a - 0x81 */
+ if (dev->inquiry_len<=0x81)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"UTA (transparency-mode):\n");
+ DBG(DBG_inquiry,"UTA x-original point......................: %2.2f inch\n", dev->inquiry_uta_x_off);
+ DBG(DBG_inquiry,"UTA y-original point......................: %2.2f inch\n", dev->inquiry_uta_y_off);
+ DBG(DBG_inquiry,"UTA maximum scan width....................: %2.2f inch\n", dev->inquiry_uta_width);
+ DBG(DBG_inquiry,"UTA maximum scan length...................: %2.2f inch\n", dev->inquiry_uta_length);
+
+ /* ---------- */
+
+ /* 0x82-0x85 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x82 = %d\n", get_inquiry_0x82(inquiry_block));
+
+ /* ---------- */
+
+ /* 0x83/0xa0 - 0x85/0xa2 */
+ if (dev->inquiry_len<=0x85)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"DOR (double optical resolution-mode):\n");
+ DBG(DBG_inquiry,"DOR optical resolution....................: %d dpi\n", dev->inquiry_dor_optical_res);
+ DBG(DBG_inquiry,"DOR maximum x-resolution..................: %d dpi\n", dev->inquiry_dor_x_res);
+ DBG(DBG_inquiry,"DOR maximum y-resolution..................: %d dpi\n", dev->inquiry_dor_y_res);
+
+ /* 0x86 - 0x8d */
+ if (dev->inquiry_len<=0x8d)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"DOR x-original point......................: %2.2f inch\n", dev->inquiry_dor_x_off);
+ DBG(DBG_inquiry,"DOR y-original point......................: %2.2f inch\n", dev->inquiry_dor_y_off);
+ DBG(DBG_inquiry,"DOR maximum scan width....................: %2.2f inch\n", dev->inquiry_dor_width);
+ DBG(DBG_inquiry,"DOR maximum scan length...................: %2.2f inch\n", dev->inquiry_dor_length);
+ DBG(DBG_inquiry,"\n");
+
+ /* ---------- */
+
+ /* 0x8e */
+ DBG(DBG_inquiry,"reserved byte 0x8e = %d\n", get_inquiry_0x8e(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* ---------- */
+
+ /* 0x8f */
+ if (dev->inquiry_len<=0x8f)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"last calibration lamp density.............: %d\n",
+ get_inquiry_last_calibration_lamp_density(inquiry_block));
+
+ /* 0x90 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x90 = %d\n", get_inquiry_0x90(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* 0x91 */
+ if (dev->inquiry_len<=0x91)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"lamp warmup maximum time..................: %d sec\n", dev->inquiry_max_warmup_time);
+
+ /* 0x92 0x93 */
+ if (dev->inquiry_len<=0x93)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"window descriptor block length............: %d bytes\n", get_inquiry_wdb_length(inquiry_block));
+
+ /* ----------------- */
+
+ /* 0x97 */
+ if (dev->inquiry_len<=0x97)
+ {
+ return;
+ }
+
+ if (get_inquiry_analog_gamma_table(inquiry_block) == 0)
+ {
+ DBG(DBG_inquiry,"no analog gamma function\n");
+ }
+ else
+ {
+ DBG(DBG_inquiry,"mp 8832 analog gamma table\n");
+ }
+
+ /* 0x98, 0x99 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x98 = %d\n", get_inquiry_0x98(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x99 = %d\n", get_inquiry_0x99(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* 0x9a */
+ if (dev->inquiry_len<=0x9a)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"maximum calibration data lines for shading: %d\n",
+ get_inquiry_max_calibration_data_lines(inquiry_block));
+
+ /* 0x9b */
+ if (dev->inquiry_len<=0x9b)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"fb/uta: color line arrangement mode.......: %d\n",
+ get_inquiry_fb_uta_line_arrangement_mode(inquiry_block));
+
+ /* 0x9c */
+ if (dev->inquiry_len<=0x9c)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"adf: color line arrangement mode.......: %d\n",
+ get_inquiry_adf_line_arrangement_mode(inquiry_block));
+
+ /* 0x9d */
+ if (dev->inquiry_len<=0x9d)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"CCD line distance.........................: %d\n",
+ get_inquiry_CCD_line_distance(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x9e = %d\n", get_inquiry_0x9e(inquiry_block));
+
+ /* 0xa2 following */
+ if (dev->inquiry_len<=0xa2)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ for(i=0xa3; i<dev->inquiry_len; i++)
+ {
+ DBG(DBG_inquiry,"unknown reserved byte 0x%x = %d\n", i, inquiry_block[i]);
+ }
+}
+
+
+/* ------------------------------------------------------------ CBHS_CORRECT ------------------------------- */
+
+
+static int umax_cbhs_correct(int minimum, int cbhs, int maximum)
+{
+ int range = maximum - minimum + 1;
+
+ if (range == 256)
+ {
+ return cbhs;
+ }
+
+ return (int)( (cbhs/256.0)*range + minimum );
+}
+
+
+/* ------------------------------------------------------------ SENSE_HANDLER ------------------------------ */
+
+
+static SANE_Status sense_handler(int scsi_fd, unsigned char *result, void *arg) /* is called by sanei_scsi */
+{
+ unsigned char asc, ascq, sensekey;
+ int asc_ascq, len;
+ Umax_Device *dev = arg;
+
+ DBG(DBG_proc, "check condition sense handler (scsi_fd = %d)\n", scsi_fd);
+
+ sensekey = get_RS_sense_key(result);
+ asc = get_RS_ASC(result);
+ ascq = get_RS_ASCQ(result);
+ asc_ascq = (int)(256 * asc + ascq);
+ len = 7 + get_RS_additional_length(result);
+
+ if ( get_RS_error_code(result) != 0x70 )
+ {
+ DBG(DBG_error, "invalid sense key error code (%d)\n", get_RS_error_code(result));
+
+ switch (dev->handle_bad_sense_error)
+ {
+ default:
+ case 0:
+ DBG(DBG_error, "=> handled as DEVICE BUSY!\n");
+ return SANE_STATUS_DEVICE_BUSY;
+
+ case 1:
+ DBG(DBG_error, "=> handled as ok!\n");
+ return SANE_STATUS_GOOD;
+
+ case 2:
+ DBG(DBG_error, "=> handled as i/o error!\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 3:
+ DBG(DBG_error, "=> ignored, sense handler does continue\n");
+ }
+ }
+
+ DBG(DBG_sense, "check condition sense: %s\n", sense_str[sensekey]);
+
+ /* when we reach here then we have no valid data in buffer[0] */
+ /* but it may be helpful to have the result data in buffer[0] */
+ memset(dev->buffer[0], 0, rs_return_block_size); /* clear sense data buffer */
+ memcpy(dev->buffer[0], result, len+1); /* copy sense data to buffer */
+
+ if (len > 0x15)
+ {
+ int scanner_error = get_RS_scanner_error_code(result);
+
+ if (scanner_error < 100)
+ {
+ DBG(DBG_sense, "-> %s (#%d)\n", scanner_error_str[scanner_error], scanner_error);
+ }
+ else
+ {
+ DBG(DBG_sense, "-> error %d\n", scanner_error);
+ }
+ }
+
+ if (get_RS_ILI(result) != 0)
+ {
+ DBG(DBG_sense, "-> ILI-ERROR: requested data length is larger than actual length\n");
+ }
+
+ switch(sensekey)
+ {
+ case 0x00: /* no sense */
+ return SANE_STATUS_GOOD;
+ break;
+
+
+ case 0x03: /* medium error */
+ if (asc_ascq == 0x1400)
+ {
+ DBG(DBG_sense, "-> misfeed, paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ else if (asc_ascq == 0x1401)
+ {
+ DBG(DBG_sense, "-> adf not ready\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ else
+ {
+ DBG(DBG_sense, "-> unknown medium error: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ break;
+
+
+ case 0x04: /* hardware error */
+ if (asc_ascq == 0x4000)
+ {
+ DBG(DBG_sense, "-> diagnostic error:\n");
+ if (len >= 0x13)
+ {
+ DBG_sense_nz(" dim light\n", get_RS_asb_dim_light(result));
+ DBG_sense_nz(" no light\n", get_RS_asb_no_light(result));
+ DBG_sense_nz(" sensor or motor error\n", get_RS_asb_sensor_motor(result));
+ DBG_sense_nz(" too light\n", get_RS_asb_too_light(result));
+ DBG_sense_nz(" calibration error\n", get_RS_asb_calibration(result));
+ DBG_sense_nz(" rom error\n", get_RS_asb_rom(result));
+ DBG_sense_nz(" ram error\n", get_RS_asb_ram(result));
+ DBG_sense_nz(" cpu error\n", get_RS_asb_cpu(result));
+ DBG_sense_nz(" scsi error\n", get_RS_asb_scsi(result));
+ DBG_sense_nz(" timer error\n", get_RS_asb_timer(result));
+ DBG_sense_nz(" filter motor error\n", get_RS_asb_filter_motor(result));
+ DBG_sense_nz(" dc adjust error\n", get_RS_asb_dc_adjust(result));
+ DBG_sense_nz(" uta home sensor or motor error\n", get_RS_asb_uta_sensor(result));
+ }
+ }
+ else
+ {
+ DBG(DBG_sense, "-> unknown hardware error: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+
+ case 0x05: /* illegal request */
+ if (asc_ascq == 0x2000)
+ {
+ DBG(DBG_sense, "-> invalid command operation code\n");
+ }
+ else if (asc_ascq == 0x2400)
+ {
+ DBG(DBG_sense, "-> illegal field in CDB\n");
+ }
+ else if (asc_ascq == 0x2500)
+ {
+ DBG(DBG_sense, "-> logical unit not supported\n");
+ }
+ else if (asc_ascq == 0x2600)
+ {
+ DBG(DBG_sense, "-> invalid field in parameter list\n");
+ }
+ else if (asc_ascq == 0x2c01)
+ {
+ DBG(DBG_sense, "-> too many windows specified\n");
+ }
+ else if (asc_ascq == 0x2c02)
+ {
+ DBG(DBG_sense, "-> invalid combination of windows specified\n");
+ }
+ else
+ {
+ DBG(DBG_sense, "-> illegal request: asc=%d, ascq=%d\n", asc, ascq);
+ }
+
+ if (len >= 0x11)
+ {
+ if (get_RS_SKSV(result) != 0)
+ {
+ if (get_RS_CD(result) == 0)
+ {
+ DBG(DBG_sense, "-> illegal parameter in CDB\n");
+ }
+ else
+ {
+ DBG(DBG_sense, "-> illegal parameter is in the data parameters sent during data out phase\n");
+ }
+
+ DBG(DBG_sense, "-> error detected in byte %d\n", get_RS_field_pointer(result));
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+
+ case 0x06: /* unit attention */
+ if (asc_ascq == 0x2900)
+ {
+ DBG(DBG_sense, "-> power on, reset or bus device reset\n");
+ }
+ else if (asc_ascq == 0x3f01)
+ {
+ DBG(DBG_sense, "-> microcode has been changed\n");
+ }
+ else
+ {
+ DBG(DBG_sense, "-> unit attention: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ break;
+
+
+ case 0x09: /* vendor specific */
+
+ if (asc == 0x00)
+ {
+ DBG(DBG_sense, "-> button protocoll\n");
+ if (ascq & 1)
+ {
+ dev->button0_pressed = 1;
+ DBG(DBG_sense, "-> button 0 pressed\n");
+ }
+
+ if (ascq & 2)
+ {
+ dev->button1_pressed = 1;
+ DBG(DBG_sense, "-> button 1 pressed\n");
+ }
+
+ if (ascq & 4)
+ {
+ dev->button2_pressed = 1;
+ DBG(DBG_sense, "-> button 2 pressed\n");
+ }
+ return SANE_STATUS_GOOD;
+ }
+ else if (asc_ascq == 0x8001)
+ {
+ DBG(DBG_sense, "-> lamp warmup\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (asc_ascq == 0x8002)
+ {
+ DBG(DBG_sense, "-> calibration by driver\n");
+ if (dev)
+ {
+ dev->do_calibration = 1; /* set flag for calibration by driver */
+ }
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(DBG_sense, "-> vendor specific sense-code: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ break;
+
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ UMAX SET RGB BIND -------------------------- */
+
+static void umax_set_rgb_bind(Umax_Scanner *scanner)
+{
+ if ( (scanner->val[OPT_RGB_BIND].w == SANE_FALSE) &&
+ (strcmp(scanner->val[OPT_MODE].s, COLOR_STR) == 0) ) /* enable rgb options */
+ {
+ if (scanner->device->inquiry_analog_gamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else /* only show gray options */
+ {
+ if (scanner->device->inquiry_analog_gamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+
+ if ( (scanner->device->inquiry_exposure_adj) && (scanner->val[OPT_SELECT_EXPOSURE_TIME].w) )
+ {
+ if ( (scanner->val[OPT_RGB_BIND].w == SANE_FALSE) &&
+ (!scanner->device->exposure_time_rgb_bind) &&
+ (strcmp(scanner->val[OPT_MODE].s, COLOR_STR) == 0) ) /* enable rgb exposure time options */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* exposure time setting for calibration enabled */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* no separate settings for calibration exposure times */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* enable gray exposure time options */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* exposure time setting for calibration enabled */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* no separate settings for calibration exposure times */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+}
+
+/* ------------------------------------------------------------ UMAX CALCULATE PIXELS ---------------------- */
+
+static int umax_calculate_pixels(int scansize_pixel, int resolution, int resolution_base, int coordinate_base)
+/* scansize_pixel = size in pixels at 1200 dpi */
+/* resolution = scan resolution */
+/* resolution_base = this is the optical resolution * 1 or * 2 */
+/* coordinate_base = this is 1200 dpi */
+{
+ unsigned int intsize_inch, intsize_pixel, diffsize_pixel, missing_pixels, del_pixel_1, pix;
+ int toomuch;
+
+ intsize_inch = scansize_pixel / coordinate_base; /* full inches */
+ intsize_pixel = intsize_inch * resolution; /* pixels in full inches */
+
+ diffsize_pixel = scansize_pixel % coordinate_base; /* missing pixels in last inch at 1200 dpi */
+ missing_pixels = diffsize_pixel * resolution_base / coordinate_base; /* missing pixels at resolution_base dpi */
+ del_pixel_1 = resolution_base - resolution; /* pixels to erase in one inch */
+ toomuch = 0; /* number of pixels that must be deleted in last inch */
+
+ if (del_pixel_1 != 0) /* search the number of pixels that must deleted */
+ {
+ pix = 0;
+ while (pix <= missing_pixels)
+ {
+ toomuch++;
+ pix = toomuch * resolution_base/del_pixel_1;
+ }
+
+ if (pix > missing_pixels)
+ {
+ toomuch--;
+ }
+ }
+
+ return (intsize_pixel + missing_pixels - toomuch);
+}
+
+/* ------------------------------------------------------------ UMAX FORGET LINE --------------------------- */
+
+
+static int umax_forget_line(Umax_Device *dev, int color)
+/* tests if line related to optical resolution has to be skipped for selected resolution */
+/* returns 0 if line is ok, -1 if line has to be skipped */
+{
+ unsigned int opt_res = dev->relevant_optical_res * dev->scale_y;
+ unsigned int forget;
+
+ dev->pixelline_opt_res++; /* increment number of lines in optical res */
+
+ if (opt_res != dev->y_resolution) /* are there any lines to skip ? */
+ {
+
+ forget = (dev->pixelline_del[color] * opt_res)/(opt_res - dev->y_resolution);
+
+ if (dev->pixelline_optic[color]++ == forget)
+ {
+ dev->pixelline_del[color]++; /* inc pointer to next line to skip */
+ return(-1); /* skip line */
+ }
+ }
+
+ return(0); /* ok, order this line */
+}
+
+
+/* ------------------------------------------------------------ UMAX ORDER LINE TO PIXEL ------------------- */
+
+
+static void umax_order_line_to_pixel(Umax_Device *dev, unsigned char *source, int color)
+/* reads a one-color line and writes it into a pixel-ordered-buffer if line */
+/* is not skipped */
+/* color = 0:red, 1:green, 2:blue */
+{
+ unsigned int i;
+ unsigned int line = dev->pixelline_next[color]; /* bufferlinenumber */
+ unsigned char *dest = dev->pixelbuffer;
+
+ if (dest != NULL)
+ {
+ if (dev->bits_per_pixel_code == 1) /* 24 bpp */
+ {
+ dest += line * dev->width_in_pixels * 3 + color;
+
+ for (i=0; i<dev->width_in_pixels; i++) /* cp each pixel into pixelbuffer */
+ {
+ *dest++ = *source++;
+ dest++;
+ dest++;
+ }
+ }
+ else /* > 24 bpp */
+ {
+ dest += line * dev->width_in_pixels * 6 + color * 2;
+
+ for(i=0; i<dev->width_in_pixels; i++) /* cp each pixel into pixelbuffer */
+ {
+ *dest++ = *source++; /* byte order correct ? , don't know ! */
+ *dest++ = *source++;
+
+ dest++; dest++;
+ dest++; dest++;
+ }
+ }
+
+ line++;
+ if (line >= dev->pixelline_max)
+ {
+ line = 0;
+ }
+
+ dev->pixelline_next[color] = line; /* next line of this color */
+ dev->pixelline_ready[color]++; /* number of ready lines for color */
+
+ DBG(DBG_read, "merged line as color %d to line %d\n", color, dev->pixelline_ready[color]);
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX ORDER LINE ---------------------------- */
+
+
+static void umax_order_line(Umax_Device *dev, unsigned char *source)
+{
+ unsigned int CCD_distance = dev->CCD_distance * dev->scale_y;
+ unsigned int length = (dev->scanlength * dev->scale_y * dev->relevant_optical_res) / dev->y_coordinate_base;
+ unsigned int color;
+
+ do /* search next valid line */
+ {
+ if (dev->pixelline_opt_res < CCD_distance)
+ {
+ color = dev->CCD_color[0]; /* color 0 */
+ }
+ else if (dev->pixelline_opt_res < CCD_distance * 3)
+ {
+ color = dev->CCD_color[1 + ((dev->pixelline_opt_res - CCD_distance) % 2)]; /* color 1,2 */
+ }
+ else if (dev->pixelline_opt_res < length * 3 - CCD_distance * 3)
+ {
+ color = dev->CCD_color[3 + (dev->pixelline_opt_res % 3)]; /* color 3,4,5 */
+ }
+ else if (dev->pixelline_opt_res < length * 3 - CCD_distance)
+ {
+ color = dev->CCD_color[6 + ((dev->pixelline_opt_res - length*3 + CCD_distance*3) % 2)]; /* color 6,7 */
+ }
+ else
+ {
+ color = dev->CCD_color[8]; /* color 8 */
+ }
+ } while(umax_forget_line(dev, color) != 0); /* until found correct line */
+
+ umax_order_line_to_pixel(dev, source, color);
+}
+
+
+/* ------------------------------------------------------------ UMAX GET PIXEL LINE ------------------------ */
+
+
+static unsigned char * umax_get_pixel_line(Umax_Device *dev)
+{
+ unsigned char *source = NULL;
+
+ if (dev->pixelbuffer != NULL)
+ {
+ if ( (dev->pixelline_ready[0] > dev->pixelline_written) &&
+ (dev->pixelline_ready[1] > dev->pixelline_written) &&
+ (dev->pixelline_ready[2] > dev->pixelline_written) )
+ {
+ source = dev->pixelbuffer + dev->pixelline_read * dev->width_in_pixels * 3;
+
+ dev->pixelline_written++;
+ dev->pixelline_read++;
+
+ if (dev->pixelline_read >= dev->pixelline_max)
+ {
+ dev->pixelline_read = 0;
+ }
+ }
+ }
+
+ return source;
+}
+
+
+/* ============================================================ Switches between the SCSI and USB commands = */
+
+/* ------------------------------------------------------------ UMAX SCSI CMD ------------------------------ */
+
+static SANE_Status umax_scsi_cmd(Umax_Device *dev, const void *src, size_t src_size, void *dst, size_t * dst_size)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_cmd(dev->sfd, src, src_size, dst, dst_size);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_cmd(dev->sfd, src, src_size, dst, dst_size);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI OPEN EXTENDED -------------------- */
+
+static SANE_Status umax_scsi_open_extended(const char *devicename, Umax_Device *dev,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg, int *buffersize)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_open_extended(devicename, &dev->sfd, handler, handler_arg, buffersize);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_open_extended(devicename, &dev->sfd, handler, handler_arg, buffersize);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI OPEN ----------------------------- */
+
+static SANE_Status umax_scsi_open(const char *devicename, Umax_Device *dev,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_open(devicename, &dev->sfd, handler, handler_arg);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_open(devicename, &dev->sfd, handler, handler_arg);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI CLOSE ---------------------------- */
+
+static void umax_scsi_close(Umax_Device *dev)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ sanei_scsi_close(dev->sfd);
+ dev->sfd=-1;
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ sanei_umaxusb_close(dev->sfd);
+ dev->sfd=-1;
+ break;
+#endif
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI REQ ENTER ------------------------ */
+
+static SANE_Status umax_scsi_req_enter(Umax_Device *dev, const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_req_enter (dev->sfd, src, src_size, dst, dst_size, idp);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_req_enter (dev->sfd, src, src_size, dst, dst_size, idp);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI REQ WAIT ------------------------- */
+
+static SANE_Status umax_scsi_req_wait(Umax_Device *dev, void *id)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_req_wait(id);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_req_wait(id);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI GET LAMP STATUS ------------------ */
+
+
+static SANE_Status umax_scsi_get_lamp_status(Umax_Device *dev, int *lamp_on)
+{
+ SANE_Status status;
+ size_t size = 1;
+
+ DBG(DBG_proc, "umax_scsi_get_lamp_status\n");
+
+ status = umax_scsi_cmd(dev, get_lamp_status.cmd, get_lamp_status.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_scsi_get_lamp_status: command returned status %s\n", sane_strstatus(status));
+ return status;
+ }
+
+ *lamp_on = get_lamp_status_lamp_on(dev->buffer[0]);
+
+ DBG(DBG_info, "lamp_status = %d\n", *lamp_on);
+
+ return status;
+}
+
+/* ------------------------------------------------------------ UMAX SCSI SET LAMP STATUS ------------------ */
+
+
+static SANE_Status umax_scsi_set_lamp_status(Umax_Device *dev, int lamp_on)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_scsi_set_lamp_status\n");
+ DBG(DBG_info, "lamp_status=%d\n", lamp_on);
+
+ set_lamp_status_lamp_on(set_lamp_status.cmd, lamp_on);
+ status = umax_scsi_cmd(dev, set_lamp_status.cmd, set_lamp_status.size, NULL, NULL);
+
+ if (status)
+ {
+ DBG(DBG_error, "umax_scsi_set_lamp_status: command returned status %s\n", sane_strstatus(status));
+ }
+
+ return status;
+}
+
+/* ------------------------------------------------------------ UMAX SET LAMP STATUS ----------------------- */
+
+static SANE_Status umax_set_lamp_status(SANE_Handle handle, int lamp_on)
+{
+ Umax_Scanner *scanner = handle;
+ int lamp_status;
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_set_lamp_status\n");
+
+ if (umax_scsi_open(scanner->device->sane.name, scanner->device, sense_handler,
+ scanner->device) != SANE_STATUS_GOOD )
+ {
+ DBG(DBG_error, "ERROR: umax_set_lamp_status: open of %s failed:\n", scanner->device->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = umax_scsi_get_lamp_status(scanner->device, &lamp_status);
+
+ if (!status)
+ {
+ status = umax_scsi_set_lamp_status(scanner->device, lamp_on);
+ }
+
+ umax_scsi_close(scanner->device);
+
+ return status;
+}
+
+/* ------------------------------------------------------------ UMAX GET DATA BUFFER STATUS ---------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED /* NOT USED */
+static SANE_Status umax_get_data_buffer_status(Umax_Device *dev)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "get_data_buffer_status\n");
+ set_GDBS_wait(get_data_buffer_status.cmd,1); /* wait for scanned data */
+ status = umax_scsi_cmd(dev, get_data_buffer_status.cmd, get_data_buffer_status.size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_get_data_buffer_status: command returned status %s\n", sane_strstatus(status));
+ }
+
+ return status;
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX DO REQUEST SENSE ---------------------- */
+
+
+static void umax_do_request_sense(Umax_Device *dev)
+{
+ size_t size = rs_return_block_size;
+ SANE_Status status;
+
+ DBG(DBG_proc, "do_request_sense\n");
+ set_RS_allocation_length(request_sense.cmd, rs_return_block_size);
+ status = umax_scsi_cmd(dev, request_sense.cmd, request_sense.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_request_sense: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX WAIT SCANNER -------------------------- */
+
+
+static SANE_Status umax_wait_scanner(Umax_Device *dev)
+{
+ SANE_Status status;
+ int cnt = 0;
+
+ DBG(DBG_proc, "wait_scanner\n");
+
+ do
+ {
+ if (cnt > 100) /* maximal 100 * 0.5 sec = 50 sec */
+ {
+ DBG(DBG_warning, "scanner does not get ready\n");
+ return -1;
+ }
+ /* test unit ready */
+ status = umax_scsi_cmd(dev, test_unit_ready.cmd, test_unit_ready.size, NULL, NULL);
+ cnt++;
+
+ if (status)
+ {
+ if (cnt == 1)
+ {
+ DBG(DBG_info2, "scanner reports %s, waiting ...\n", sane_strstatus(status));
+ }
+
+ usleep(500000); /* wait 0.5 seconds */
+ }
+ } while (status != SANE_STATUS_GOOD );
+
+ DBG(DBG_info, "scanner ready\n");
+
+ return status;
+}
+
+#define WAIT_SCANNER { int status = umax_wait_scanner(dev); if (status) return status; }
+
+
+/* ------------------------------------------------------------ UMAX GRAB SCANNER -------------------------- */
+
+
+static int umax_grab_scanner(Umax_Device *dev)
+{
+ int status;
+
+ DBG(DBG_proc, "grab_scanner\n");
+
+ WAIT_SCANNER; /* wait for scanner ready */
+ status = umax_scsi_cmd(dev, reserve_unit.cmd, reserve_unit.size, NULL, NULL);
+
+ if (status)
+ {
+ DBG(DBG_error, "umax_grab_scanner: command returned status %s\n", sane_strstatus(status));
+ }
+ else
+ {
+ DBG(DBG_info, "scanner reserved\n");
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX REPOSITION SCANNER -------------------- */
+
+
+static int umax_reposition_scanner(Umax_Device *dev)
+{
+ int status;
+ int pause;
+
+ pause = dev->pause_after_reposition + dev->pause_for_moving * (dev->upper_left_y + dev->scanlength) /
+ ( (dev->inquiry_fb_length * dev->y_coordinate_base) );
+
+ DBG(DBG_info2, "trying to reposition scanner ...\n");
+ status = umax_scsi_cmd(dev, object_position.cmd, object_position.size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_reposition_scanner: command returned status %s\n", sane_strstatus(status));
+ return status;
+ }
+
+ if (pause > 0) /* predefined time to wait (Astra 2400S) */
+ {
+ DBG(DBG_info2, "pause for repositioning %d msec ...\n", pause);
+ usleep(((long) pause) * 1000);
+ DBG(DBG_info, "repositioning pause done\n");
+ }
+ else if (pause == 0) /* use TEST UNIT READY */
+ {
+ WAIT_SCANNER;
+ DBG(DBG_info, "scanner repositioned\n");
+ }
+ else /* pause < 0 : return without any pause */
+ {
+ DBG(DBG_info, "not waiting for finishing reposition scanner\n");
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ UMAX GIVE SCANNER -------------------------- */
+
+
+static int umax_give_scanner(Umax_Device *dev)
+{
+ int status;
+
+ DBG(DBG_info2, "trying to release scanner ...\n");
+ status = umax_scsi_cmd(dev, release_unit.cmd, release_unit.size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_give_scanner: command returned status %s\n", sane_strstatus(status));
+ }
+ else
+ {
+ DBG(DBG_info, "scanner released\n");
+ }
+
+ if (!dev->batch_scan || dev->batch_end)
+ {
+ umax_reposition_scanner(dev);
+ }
+ else
+ {
+ usleep(200000); /* 200 ms pause to make sure program does not exit before scanner is ready */
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND GAMMA DATA ----------------------- */
+
+
+static void umax_send_gamma_data(Umax_Device *dev, void *gamma_data, int color)
+{
+ unsigned char *data = gamma_data;
+ unsigned char *dest;
+ int length;
+ SANE_Status status;
+
+ DBG(DBG_proc, "send_gamma_data\n");
+
+ if (dev->inquiry_gamma_dwload == 0)
+ {
+ DBG(DBG_error, "ERROR: gamma download not available\n");
+ return;
+ }
+
+ memcpy(dev->buffer[0], send.cmd, send.size); /* send */
+ set_S_datatype_code(dev->buffer[0], S_datatype_gamma); /* gamma curve */
+
+ dest = dev->buffer[0] + send.size;
+
+ if (dev->inquiry_gamma_DCF == 0) /* gamma format type 0 */
+ {
+ DBG(DBG_info, "using gamma download curve format type 0\n");
+
+ memcpy(dest, gamma_DCF0.cmd, gamma_DCF0.size);
+
+ if (color == 1) /* one color */
+ {
+ set_DCF0_gamma_lines(dest, DCF0_gamma_one_line);
+
+ set_DCF0_gamma_color(dest, 0, DCF0_gamma_color_gray); /* grayscale */
+ if ( (dev->colormode == RGB) && (dev->three_pass != 0) ) /* 3 pass color */
+ {
+ set_DCF0_gamma_color(dest, 0, dev->three_pass_color); /* set color */
+ }
+
+ dest = dest + 2;
+ memcpy(dest, data, 1024); /* copy data */
+
+ set_S_xfer_length(dev->buffer[0], 1026); /* set length */
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + 1026, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=0, one color): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ else /* three colors */
+ {
+ set_DCF0_gamma_lines(dest, DCF0_gamma_three_lines);
+
+ set_DCF0_gamma_color(dest, 0, DCF0_gamma_color_red); /* red */
+ set_DCF0_gamma_color(dest, 1, DCF0_gamma_color_green); /* green */
+ set_DCF0_gamma_color(dest, 2, DCF0_gamma_color_blue); /* blue */
+
+ dest = dest + 2;
+ memcpy(dest, data, 1024); /* copy red data */
+
+ dest = dest + 1025;
+ data = data + 1024;
+ memcpy(dest, data, 1024); /* copy green data */
+
+ dest = dest + 1025;
+ data = data + 1024;
+ memcpy(dest, data, 1024); /* copy blue data */
+
+ set_S_xfer_length(dev->buffer[0], 3076); /* set length */
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + 3076, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=0, RGB): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ }
+ else if (dev->inquiry_gamma_DCF == 1) /* gamma format type 1 */
+ {
+ DBG(DBG_info, "using gamma download curve format type 1\n");
+
+ memcpy(dest, gamma_DCF1.cmd, gamma_DCF1.size);
+
+ set_DCF1_gamma_color(dest, DCF1_gamma_color_gray); /* grayscale */
+ if ( (dev->colormode == RGB) && (dev->three_pass != 0) ) /* 3 pass color */
+ {
+ set_DCF1_gamma_color(dest, dev->three_pass_color); /* set color */
+ }
+
+ dest = dest + 2;
+ memcpy(dest, data, 256); /* copy data */
+
+ set_S_xfer_length(dev->buffer[0], 258); /* set length */
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + 258, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=1): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ else if (dev->inquiry_gamma_DCF == 2) /* gamma format type 2 */
+ {
+ DBG(DBG_info, "using gamma download curve format type 2\n");
+
+ memcpy(dest, gamma_DCF2.cmd, gamma_DCF2.size);
+
+ set_DCF2_gamma_color(dest, DCF2_gamma_color_gray); /* grayscale */
+ if ( (dev->colormode == RGB) && (dev->three_pass != 0) ) /* 3 pass color */
+ { set_DCF2_gamma_color(dest, dev->three_pass_color); } /* set color */
+
+ if (color == 1)
+ {
+ set_DCF2_gamma_lines(dest, DCF2_gamma_one_line);
+ }
+ else
+ {
+ set_DCF2_gamma_lines(dest, DCF2_gamma_three_lines);
+ }
+
+ set_DCF2_gamma_input_bits(dest, dev->gamma_input_bits_code);
+ set_DCF2_gamma_output_bits(dest, dev->bits_per_pixel_code);
+
+ dest = dev->buffer[0] + send.size + gamma_DCF2.size; /* write to dest */
+
+ if (dev->gamma_input_bits_code & 32)
+ {
+ length = 65536; /* 16 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 16)
+ {
+ length = 16384; /* 14 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 8)
+ {
+ length = 4096; /* 12 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 4)
+ {
+ length = 1024; /* 10 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 2)
+ {
+ length = 512; /* 9 input bits */
+ }
+ else
+ {
+ length = 256; /* 8 input bits */
+ }
+
+ if (dev->bits_per_pixel_code != 1) /* more than 8 output bits per pixel */
+ {
+ length = length * 2; /* = 2 output bytes */
+ }
+
+ if (dev->bufsize >= color*length+gamma_DCF2.size)
+ {
+ set_S_xfer_length(dev->buffer[0], color*length+gamma_DCF2.size); /* set length */
+ memcpy(dest, data, color*length); /* copy data */
+
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size+gamma_DCF2.size + length * color, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=2): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ else
+ {
+ DBG(DBG_error, "ERROR: too small scsi buffer (%d bytes) to send gamma data\n", dev->bufsize);
+ }
+ }
+ else
+ {
+ DBG(DBG_error, "ERROR: unknown gamma download curve type for this scanner\n");
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND DATA ---------------------------- */
+
+
+static void umax_send_data(Umax_Device *dev, void *data, int size, int datatype)
+{
+ unsigned char *dest;
+ SANE_Status status;
+
+ memcpy(dev->buffer[0], send.cmd, send.size); /* send */
+ set_S_datatype_code(dev->buffer[0], datatype); /* set datatype */
+ set_S_xfer_length(dev->buffer[0], size); /* bytes */
+
+ dest=dev->buffer[0] + send.size;
+ memcpy(dest, data, size); /* copy data */
+
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_data: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND HALFTONE PATTERN ----------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static void umax_send_halftone_pattern(Umax_Device *dev, void *data, int size)
+{
+ DBG(DBG_proc,"send_halftone_pattern\n");
+ umax_send_data(dev, data, size*size, S_datatype_halftone);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX SEND SHADING DATA -------------------- */
+
+
+static void umax_send_shading_data(Umax_Device *dev, void *data, int size)
+{
+ DBG(DBG_proc,"send_shading_data\n");
+ umax_send_data(dev, data, size, S_datatype_shading);
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND GAIN DATA ----------------------- */
+
+#ifndef UMAX_HIDE_UNUSED
+static void umax_send_gain_data(Umax_Device *dev, void *data, int size)
+{
+ DBG(DBG_proc,"send_gain_data\n");
+ umax_send_data(dev, data, size, S_datatype_gain);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX QUEUE READ IMAGE DATA REQ ------------- */
+
+static SANE_Status umax_queue_read_image_data_req(Umax_Device *dev, unsigned int length, int bufnr)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_queue_read_image_data_req for buffer[%d], length = %d\n", bufnr, length);
+
+ set_R_xfer_length(sread.cmd, length); /* set length */
+ set_R_datatype_code(sread.cmd, R_datatype_imagedata); /* set datatype */
+
+ dev->length_queued[bufnr] = length; /* set length request */
+ dev->length_read[bufnr] = length; /* set length request, can be changed asyncronous by umax_scsi_req_enter */
+
+ status = umax_scsi_req_enter(dev, sread.cmd, sread.size, dev->buffer[bufnr], &(dev->length_read[bufnr]), &(dev->queue_id[bufnr]));
+ if (status)
+ {
+ DBG(DBG_error, "umax_queue_read_image_data_req: command returned status %s\n", sane_strstatus(status));
+ return -1;
+ }
+ else
+ {
+ DBG(DBG_info2, "umax_queue_read_image_data_req: id for buffer[%d] is %p\n", bufnr, dev->queue_id[bufnr]);
+ }
+
+ return length;
+}
+
+/* ------------------------------------------------------------ UMAX WAIT QUEUED IMAGE DATA ---------------- */
+
+
+static int umax_wait_queued_image_data(Umax_Device *dev, int bufnr)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_wait_queued_image_data for buffer[%d] (id=%p)\n", bufnr, dev->queue_id[bufnr]);
+
+ status = umax_scsi_req_wait(dev, dev->queue_id[bufnr]);
+ if (status)
+ {
+ DBG(DBG_error, "umax_wait_queued_image_data: wait returned status %s\n", sane_strstatus(status));
+ return -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ UMAX READ DATA ----------------------------- */
+
+
+static int umax_read_data(Umax_Device *dev, size_t length, int datatype)
+{
+ SANE_Status status;
+
+ set_R_xfer_length(sread.cmd, length); /* set length */
+ set_R_datatype_code(sread.cmd, datatype); /* set datatype */
+
+ status = umax_scsi_cmd(dev, sread.cmd, sread.size, dev->buffer[0], &length);
+ if (status)
+ {
+ DBG(DBG_error, "umax_read_data: command returned status %s\n", sane_strstatus(status));
+ return -1;
+ }
+
+ return length;
+}
+
+
+/* ------------------------------------------------------------ UMAX READ SHADING DATA -------------------- */
+
+
+static int umax_read_shading_data(Umax_Device *dev, unsigned int length)
+{
+ DBG(DBG_proc,"read_shading_data\n");
+ return umax_read_data(dev, length, R_datatype_shading);
+}
+
+
+/* ------------------------------------------------------------ UMAX READ GAIN DATA ----------------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static int umax_read_gain_data(Umax_Device *dev, unsigned int length)
+{
+ DBG(DBG_proc,"read_gain_data\n");
+ return umax_read_data(dev, length, R_datatype_gain);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX READ IMAGE DATA ---------------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static int umax_read_image_data(Umax_Device *dev, unsigned int length)
+{
+ DBG(DBG_proc,"read_image_data\n");
+ WAIT_SCANNER;
+ return umax_read_data(dev, length, R_datatype_imagedata);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX CORRECT LIGHT ------------------------- */
+
+
+static int umax_correct_light(int light, int analog_gamma_byte) /* correct highlight/shadow if analog gamma is set */
+{
+ double analog_gamma;
+ analog_gamma=analog_gamma_table[analog_gamma_byte];
+ return( (int) 255 * pow( ((double) light)/255.0 , (1.0/analog_gamma) )+.5 );
+}
+
+
+/* ------------------------------------------------------------ UMAX SET WINDOW PARAM ---------------------- */
+
+
+/* set_window_param sets all the window parameters. This means building a */
+/* fairly complicated SCSI command before sending it... */
+
+static SANE_Status umax_set_window_param(Umax_Device *dev)
+{
+ SANE_Status status;
+ int num_dblocks = 1; /* number of window descriptor blocks, usually 1 or 3 */
+ unsigned char buffer_r[max_WDB_size], buffer_g[max_WDB_size], buffer_b[max_WDB_size];
+
+ DBG(DBG_proc, "set_window_param\n");
+ memset(buffer_r, '\0', max_WDB_size); /* clear buffer */
+ set_WDB_length(dev->wdb_len); /* length of win descriptor block */
+ memcpy(buffer_r, window_descriptor_block.cmd, window_descriptor_block.size); /* copy preset data */
+
+ set_WD_wid(buffer_r, 0); /* window identifier */
+ set_WD_auto(buffer_r, dev->set_auto); /* 0 or 1: don't know what it is */
+
+ /* geometry */
+ set_WD_Xres(buffer_r, dev->x_resolution); /* x resolution in dpi */
+ set_WD_Yres(buffer_r, dev->y_resolution); /* y resolution in dpi */
+ set_WD_ULX(buffer_r, dev->upper_left_x); /* left_edge x */
+ set_WD_ULY(buffer_r, dev->upper_left_y); /* upper_edge y */
+ set_WD_width(buffer_r, dev->scanwidth); /* width */
+ set_WD_length(buffer_r, dev->scanlength); /* length */
+
+ /* BTC */
+ set_WD_brightness(buffer_r, dev->brightness); /* brightness, only halftone */
+ set_WD_threshold(buffer_r, dev->threshold); /* threshold, only lineart */
+ set_WD_contrast(buffer_r, dev->contrast); /* contrast, only halftone */
+
+ /* scanmode, preset to LINEART */
+ set_WD_composition(buffer_r, WD_comp_lineart); /* image composition */
+ /* = (scan-mode) */
+ set_WD_bitsperpixel(buffer_r, WD_bits_1); /* bits/pixel (1,8,9,10,12,14,16) */
+ set_WD_halftone(buffer_r, dev->halftone); /* select halftone-pattern */
+ set_WD_RIF(buffer_r, dev->reverse); /* reverse, invert black and white */
+ set_WD_speed(buffer_r, dev->WD_speed); /* set speed */
+ set_WD_select_color(buffer_r, WD_color_gray); /* color for window-block */
+
+ /* set highlight and shadow in dependence of analog gamma */
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_r, dev->analog_gamma_r));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_r, dev->analog_gamma_r));
+
+ /* scan options */
+ set_WD_gamma(buffer_r, dev->digital_gamma_r); /* set digital gamma */
+ set_WD_module(buffer_r, dev->module); /* flatbed or transparency */
+ set_WD_CBHS(buffer_r, dev->cbhs_range); /* 50 or 255 */
+ set_WD_FF(buffer_r, dev->fix_focus_position); /* fix focus position */
+ set_WD_RMIF(buffer_r, dev->reverse_multi); /* reverse color-values */
+ set_WD_FDC(buffer_r, dev->lens_cal_in_doc_pos); /* lens calibration in document position */
+ set_WD_PF(buffer_r, dev->disable_pre_focus); /* disable pre focus */
+ set_WD_LCL(buffer_r, dev->holder_focus_pos_0mm); /* 0.6mm <-> 0.0mm holder focus position */
+ set_WD_HBT(buffer_r, dev->low_byte_first); /* set byte order for 16 bit scanners */
+ set_WD_DOR(buffer_r, dev->dor); /* double-resolution-mode */
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_r); /* scan exposure time */
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_r);/* calibration exposure time */
+
+ set_WD_batch(buffer_r, dev->batch_scan); /* batch or normal scan */
+ set_WD_MF(buffer_r, dev->manual_focus); /* automatic <-> manual focus */
+ set_WD_line_arrangement(buffer_r, WD_line_arrengement_by_fw); /* line arrangement by scanner */
+ set_WD_warmup(buffer_r, dev->warmup); /* warmup */
+
+ set_WD_calibration(buffer_r, dev->calibration); /* image calibration */
+
+ set_WD_color_sequence(buffer_r, WD_color_sequence_RGB); /* sequence RGB */
+ set_WD_color_ordering(buffer_r, WD_color_ordering_pixel); /* set to pixel for pbm, pgm, pnm-file */
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_r ); /* analog gamma */
+ set_WD_lamp_c_density(buffer_r, dev->c_density); /* calibrat. lamp density */
+ set_WD_lamp_s_density(buffer_r, dev->s_density); /* scan lamp density */
+ set_WD_next_upper_left(buffer_r, dev->batch_next_tl_y); /* batch scan next top left y position */
+ set_WD_pixel_count(buffer_r, dev->width_in_pixels); /* pixel count */
+ set_WD_line_count(buffer_r, dev->length_in_pixels); /* line count */
+ set_WD_x_coordinate_base(buffer_r, dev->x_coordinate_base); /* dpi (1200) */
+ set_WD_y_coordinate_base(buffer_r, dev->y_coordinate_base); /* dpi (1200) */
+ set_WD_calibration_data_lines(buffer_r, dev->calib_lines); /* required lines for calibration by driver */
+
+
+ switch(dev->colormode)
+ {
+ case LINEART: /* LINEART */
+ set_WD_composition(buffer_r, WD_comp_lineart);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+
+ set_WD_select_color(buffer_r, WD_color_gray);
+ break;
+
+ case HALFTONE: /* HALFTONE */
+ set_WD_composition(buffer_r, WD_comp_dithered);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+
+ set_WD_select_color(buffer_r, WD_color_gray);
+ break;
+
+ case GRAYSCALE: /* GRAYSCALE */
+ set_WD_composition(buffer_r, WD_comp_gray);
+ set_WD_bitsperpixel(buffer_r, dev->bits_per_pixel);
+
+ set_WD_select_color(buffer_r, WD_color_gray);
+ break;
+
+ case RGB_LINEART: /* COLOR MODES */
+ case RGB_HALFTONE:
+ case RGB:
+ if (dev->colormode == RGB_LINEART )
+ {
+ set_WD_composition(buffer_r, WD_comp_rgb_bilevel);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+ }
+ else if (dev->colormode == RGB_HALFTONE )
+ {
+ set_WD_composition(buffer_r, WD_comp_rgb_dithered);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+ }
+ else /* RGB */
+ {
+ set_WD_composition(buffer_r, WD_comp_rgb_full);
+ set_WD_bitsperpixel(buffer_r, dev->bits_per_pixel);
+ }
+
+ if (dev->three_pass == 0)
+ { /* singlepass */
+ num_dblocks = 3;
+
+ if (dev->do_color_ordering != 0)
+ {
+ set_WD_line_arrangement(buffer_r, WD_line_arrengement_by_driver);
+
+ if (dev->CCD_distance == 0)
+ {
+ set_WD_color_ordering(buffer_r, WD_color_ordering_line_no_ccd);
+ }
+ else
+ {
+ set_WD_color_ordering(buffer_r, WD_color_ordering_line_w_ccd);
+ }
+ }
+
+ memcpy(buffer_g, buffer_r, max_WDB_size); /* copy WDB for green */
+ memcpy(buffer_b, buffer_r, max_WDB_size); /* copy WDB for blue */
+
+ set_WD_wid(buffer_r, WD_wid_red); /* window identifier red */
+ set_WD_wid(buffer_g, WD_wid_green); /* window identifier green */
+ set_WD_wid(buffer_b, WD_wid_blue); /* window identifier blue */
+
+ set_WD_select_color(buffer_r, WD_color_red); /* select red for this window */
+ set_WD_select_color(buffer_g, WD_color_green); /* select green for this window */
+ set_WD_select_color(buffer_b, WD_color_blue); /* select blue for this window */
+
+ set_WD_gamma(buffer_r, dev->digital_gamma_r); /* digital gamma */
+ set_WD_gamma(buffer_g, dev->digital_gamma_g);
+ set_WD_gamma(buffer_b, dev->digital_gamma_b);
+
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_r); /* analog gamma */
+ set_WD_analog_gamma(buffer_g, dev->analog_gamma_g);
+ set_WD_analog_gamma(buffer_b, dev->analog_gamma_b);
+
+ /* set highlight in dependence of analog gamma */
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_r, dev->analog_gamma_r));
+ set_WD_highlight(buffer_g, umax_correct_light(dev->highlight_g, dev->analog_gamma_g));
+ set_WD_highlight(buffer_b, umax_correct_light(dev->highlight_b, dev->analog_gamma_b));
+
+ /* set shadow in dependence of analog gamma */
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_r, dev->analog_gamma_r));
+ set_WD_shadow(buffer_g, umax_correct_light(dev->shadow_g, dev->analog_gamma_g));
+ set_WD_shadow(buffer_b, umax_correct_light(dev->shadow_b, dev->analog_gamma_b));
+
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_r); /* set scan exposure times */
+ set_WD_scan_exposure_level(buffer_g, dev->exposure_time_scan_g);
+ set_WD_scan_exposure_level(buffer_b, dev->exposure_time_scan_b);
+
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_r);/* set calib exp times */
+ set_WD_calibration_exposure_level(buffer_g, dev->exposure_time_calibration_g);
+ set_WD_calibration_exposure_level(buffer_b, dev->exposure_time_calibration_b);
+ }
+ else
+ { /* threepass */
+ set_WD_wid(buffer_r, 0); /* window identifier */
+ set_WD_color_ordering(buffer_r, WD_color_ordering_plane); /* ???? */
+
+ if (dev->colormode == RGB_LINEART )
+ {
+ set_WD_composition(buffer_r, WD_comp_lineart); /* color-lineart-mode */
+ }
+ else if (dev->colormode == RGB_HALFTONE )
+ {
+ set_WD_composition(buffer_r, WD_comp_dithered); /* color-halftone-mode */
+ }
+ else /* RGB */
+ {
+ set_WD_composition(buffer_r, WD_comp_gray); /* color-mode */
+ }
+
+ switch (dev->three_pass_color)
+ {
+ case WD_wid_red:
+ set_WD_select_color(buffer_r, WD_color_red); /* color red */
+ set_WD_gamma(buffer_r, dev->digital_gamma_r);
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_r);
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_r, dev->analog_gamma_r));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_r, dev->analog_gamma_r));
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_r);
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_r);
+ break;
+
+ case WD_wid_green:
+ set_WD_select_color(buffer_r, WD_color_green); /* color green */
+ set_WD_gamma(buffer_r, dev->digital_gamma_g);
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_g);
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_g, dev->analog_gamma_g));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_g, dev->analog_gamma_g));
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_g);
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_g);
+ break;
+
+ case WD_wid_blue:
+ set_WD_select_color(buffer_r, WD_color_blue); /* color blue */
+ set_WD_gamma(buffer_r, dev->digital_gamma_b);
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_b);
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_b, dev->analog_gamma_b));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_b, dev->analog_gamma_b));
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_b);
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_b);
+ break;
+
+ } /* switch dev->three_pass_color */
+
+ } /* if (single_pass) else (three_pass) */
+ break;
+ } /* switch dev->colormode, case RGB */
+
+ /* prepare SCSI-BUFFER */
+ memcpy(dev->buffer[0], set_window.cmd, set_window.size); /* SET-WINDOW cmd */
+ memcpy(WPDB_OFF(dev->buffer[0]), window_parameter_data_block.cmd, window_parameter_data_block.size); /* WPDB */
+ set_WPDB_wdbnum(WPDB_OFF(dev->buffer[0]), num_dblocks); /* set WD_len */
+ memcpy(WDB_OFF(dev->buffer[0],1), buffer_r, window_descriptor_block.size); /* add WD_block */
+
+ if ( num_dblocks == 3) /* if singelpass RGB */
+ {
+ memcpy(WDB_OFF(dev->buffer[0],2), buffer_g, window_descriptor_block.size); /* add green */
+ memcpy(WDB_OFF(dev->buffer[0],3), buffer_b, window_descriptor_block.size); /* add blue */
+ }
+
+
+ DBG(DBG_info2, "window descriptor block created with %d bytes\n", dev->wdb_len);
+
+ set_SW_xferlen(dev->buffer[0], (window_parameter_data_block.size + (window_descriptor_block.size * num_dblocks)));
+
+ status = umax_scsi_cmd(dev, dev->buffer[0], set_window.size + window_parameter_data_block.size +
+ (window_descriptor_block.size * num_dblocks), NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_set_window_param: command returned status %s\n", sane_strstatus(status));
+ }
+ else
+ {
+ DBG(DBG_info, "window(s) set\n");
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX DO INQUIRY ---------------------------- */
+
+
+static void umax_do_inquiry(Umax_Device *dev)
+{
+ size_t size;
+ SANE_Status status;
+
+ DBG(DBG_proc,"do_inquiry\n");
+ memset(dev->buffer[0], '\0', 256); /* clear buffer */
+
+ size = 5;
+
+ set_inquiry_return_size(inquiry.cmd, size); /* first get only 5 bytes to get size of inquiry_return_block */
+ status = umax_scsi_cmd(dev, inquiry.cmd, inquiry.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_inquiry: command returned status %s\n", sane_strstatus(status));
+ }
+
+ size = get_inquiry_additional_length(dev->buffer[0]) + 5;
+
+ set_inquiry_return_size(inquiry.cmd, size); /* then get inquiry with actual size */
+ status = umax_scsi_cmd(dev, inquiry.cmd, inquiry.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_inquiry: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX START SCAN ---------------------------- */
+
+
+static SANE_Status umax_start_scan(Umax_Device *dev)
+{
+ int size = 1;
+ SANE_Status status;
+
+ DBG(DBG_proc,"start_scan\n");
+
+ if (dev->adf) /* ADF selected: test for ADF errors */
+ {
+ umax_do_inquiry(dev); /* get inquiry */
+
+ if (get_inquiry_ADF_paper_jam(dev->buffer[0])) /* test for ADF paper jam */
+ {
+ DBG(DBG_error,"ERROR: umax_start_scan: ADF paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ else if (get_inquiry_ADF_cover_open(dev->buffer[0])) /* test for ADF cover open */
+ {
+ DBG(DBG_error,"ERROR: umax_start_scan: ADF cover open\n");
+ return SANE_STATUS_COVER_OPEN;
+ }
+ else if (get_inquiry_ADF_no_paper(dev->buffer[0])) /* test for ADF no paper */
+ {
+ DBG(DBG_error,"ERROR: umax_start_scan: ADF no paper\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ set_SC_quality(scan.cmd, dev->quality); /* 1=qual, 0=fast */
+ set_SC_adf( scan.cmd, dev->adf); /* ADF, 0=off, 1=use */
+ set_SC_preview(scan.cmd, dev->preview); /* 1=preview */
+
+ set_SC_wid(scan.cmd, 1, 0); /* Window-Identifier */
+
+ set_SC_xfer_length(scan.cmd, size); /* following Bytes */
+
+ DBG(DBG_info,"starting scan\n");
+
+ status = umax_scsi_cmd(dev, scan.cmd, scan.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_start_scan: command returned status %s\n", sane_strstatus(status));
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX DO CALIBRATION ------------------------ */
+
+
+static SANE_Status umax_do_calibration(Umax_Device *dev)
+{
+ SANE_Status status;
+ unsigned int width = 0;
+ unsigned int lines = 0;
+ unsigned int bytespp = 0;
+
+ DBG(DBG_proc,"do_calibration\n");
+
+ status = umax_wait_scanner(dev);
+
+ if ((status == SANE_STATUS_GOOD) && (dev->do_calibration != 0)) /* calibration by driver */
+ {
+ unsigned char *shading_data = 0;
+ unsigned int i, j;
+ long *average;
+
+
+ DBG(DBG_info,"driver is doing calibration\n");
+
+
+ if (umax_execute_request_sense)
+ {
+ DBG(DBG_info,"request sense call is enabled\n");
+ memset(dev->buffer[0], 0, rs_return_block_size); /* clear sense data buffer */
+ umax_do_request_sense(dev); /* new request-sense call to get all data */
+ }
+ else
+ {
+ DBG(DBG_info,"request sense call is disabled\n");
+ }
+
+ if (get_RS_SCC_condition_code(dev->buffer[0]) != 1)
+ {
+ DBG(DBG_warning,"WARNING: missing information about shading-data\n");
+ DBG(DBG_warning," driver tries to guess missing values!\n");
+
+ if ((dev->calibration_area != UMAX_CALIBRATION_AREA_CCD) && (!dev->batch_scan))
+ /* calibration is done with image geometry and depth */
+ {
+ DBG(DBG_warning," Calibration is done with selected image geometry and depth!\n");
+
+ width = dev->scanwidth * dev->relevant_optical_res / dev->x_coordinate_base;
+
+ if (dev->calibration_width_offset > -99999) /* driver or user (umax.conf) define an offset */
+ {
+ width = width + dev->calibration_width_offset;
+ DBG(DBG_warning," Using calibration width offset of %d\n", dev->calibration_width_offset);
+ }
+
+ if (dev->colormode == RGB)
+ {
+ width = width * 3;
+ }
+
+ lines = dev->calib_lines;
+
+ if (dev->gamma_input_bits_code <= 1)
+ {
+ bytespp = 1; /* 8 bit mode */
+ }
+ else
+ {
+ bytespp = 2; /* 16 bit mode */
+ }
+ }
+ else /* calibration is done with full scanarea and full depth */
+ {
+ DBG(DBG_warning," Calibration is done for each CCD pixel with full depth!\n");
+
+ width = (int)(dev->inquiry_fb_width * dev->inquiry_optical_res);
+
+ if (dev->batch_scan)
+ {
+ if (dev->calibration_width_offset_batch > -99999) /* driver or user (umax.conf) define an offset for batch scanning */
+ {
+ width = width + dev->calibration_width_offset_batch;
+ DBG(DBG_warning," Using calibration width offset for batch scanning of %d\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else /* normal scan */
+ {
+ if (dev->calibration_width_offset > -99999) /* driver or user (umax.conf) define an offset */
+ {
+ width = width + dev->calibration_width_offset;
+ DBG(DBG_warning," Using calibration width offset of %d\n", dev->calibration_width_offset);
+ }
+ }
+
+ if (dev->colormode == RGB)
+ {
+ width = width * 3;
+ }
+
+ lines = dev->calib_lines;
+
+ if (dev->gamma_input_bits_code <= 1)
+ {
+ bytespp = 1; /* 8 bit mode */
+ }
+ else
+ {
+ bytespp = 2; /* 16 bit mode */
+ }
+ }
+ }
+ else
+ {
+ lines = get_RS_SCC_calibration_lines(dev->buffer[0]);
+ bytespp = get_RS_SCC_calibration_bytespp(dev->buffer[0]);
+ width = get_RS_SCC_calibration_bytesperline(dev->buffer[0]) / bytespp;
+ }
+
+ if (dev->calibration_bytespp > 0) /* correct bytespp if necessary and driver knows about it or user did select it */
+ {
+ bytespp = dev->calibration_bytespp;
+ }
+
+ DBG(DBG_info,"scanner sends %d lines with %d pixels and %d bytes/pixel\n", lines, width, bytespp);
+
+ if (width * bytespp > dev->bufsize)
+ {
+ DBG(DBG_error,"ERROR: scsi buffer is to small for one shading line, calibration aborted\n");
+ DBG(DBG_error,"=> change umax.conf options scsi-buffer-size-min and scsi-buffer-size-max\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* UMAX S12 sends a kind of uncalibrated image data, bright -> 255, dark -> 0 */
+ /* (although 0 is not black) my scanner sends values around 220 */
+ /* for some scanners the data is simply sent back, other scanners want 255-value as awnswer */
+
+ average = calloc(width, sizeof(long));
+ if (average == 0)
+ {
+ DBG(DBG_error,"ERROR: could not allocate memory for averaging shading data: calibration aborted\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shading_data = calloc(width, bytespp);
+ if (shading_data == 0)
+ {
+ DBG(DBG_error,"ERROR: could not allocate memory for shading data: calibration aborted\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (bytespp == 1) /* 1 byte per pixel */
+ {
+ DBG(DBG_info,"calculating average value for 8 bit shading data!\n");
+
+ for (i=0; i<lines; i++)
+ {
+ umax_read_shading_data(dev, width * bytespp);
+
+ for (j=0; j<width; j++)
+ {
+ average[j] += (long) dev->buffer[0][j];
+ }
+
+ DBG(DBG_read,"8 bit shading-line %d read\n", i+1);
+ }
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[j] = (unsigned char) (average[j] / lines);
+ }
+ }
+ else if (dev->low_byte_first) /* 2 bytes per pixel with low byte first */
+ {
+ DBG(DBG_info,"calculating average value for 16 bit shading data (low byte first)!\n");
+ for (i=0; i<lines; i++)
+ {
+ umax_read_shading_data(dev, width * bytespp);
+
+ for (j=0; j<width; j++)
+ {
+ average[j] += (long) 256 * dev->buffer[0][2*j+1] + dev->buffer[0][2*j] ;
+ }
+
+ DBG(DBG_read,"16 bit shading-line %d read\n", i+1);
+ }
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[2*j+1] = (unsigned char) (average[j] / (256 * lines));
+ shading_data[2*j] = (unsigned char) (average[j] / lines);
+ }
+ }
+ else /* 2 bytes per pixel with highbyte first */
+ {
+ DBG(DBG_info,"calculating average value for 16 bit shading data (high byte first)!\n");
+ for (i=0; i<lines; i++)
+ {
+ umax_read_shading_data(dev, width * bytespp);
+
+ for (j=0; j<width; j++)
+ {
+ average[j] += (long) 256 * dev->buffer[0][2*j] + dev->buffer[0][2*j + 1] ;
+ }
+
+ DBG(DBG_read,"16 bit shading-line %d read\n", i+1);
+ }
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[2*j] = (unsigned char) (average[j] / (256 * lines));
+ shading_data[2*j+1] = (unsigned char) (average[j] / lines);
+ }
+ }
+
+ free(average);
+
+ if ( (dev->invert_shading_data) ) /* invert data */
+ {
+ if (bytespp == 1)
+ {
+ DBG(DBG_info,"inverting 8 bit shading data\n");
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[j] = 255 - shading_data[j];
+ }
+ }
+ else
+ {
+ unsigned int value;
+
+ DBG(DBG_info,"inverting 16 bit shading data\n");
+
+ for (j=0; j<width; j++)
+ {
+ value = shading_data[2*j] + shading_data[2*j+1] * 256;
+ value = 65535 - value;
+ shading_data[2*j] = (unsigned char) value/256;
+ shading_data[2*j+1] = (unsigned char) value & 255;
+ }
+ }
+ }
+
+ umax_send_shading_data(dev, shading_data, width * bytespp);
+ DBG(DBG_info,"shading-data sent\n");
+ free(shading_data);
+
+ status = umax_start_scan(dev); /* now start real scan */
+
+ dev->do_calibration = 0;
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX DO NEW INQUIRY ------------------------ */
+
+
+static void umax_do_new_inquiry(Umax_Device *dev, size_t size) /* call inquiry again if wrong length */
+{
+ SANE_Status status;
+
+ DBG(DBG_proc,"do_new_inquiry\n");
+ memset(dev->buffer[0], '\0', 256); /* clear buffer */
+
+ set_inquiry_return_size(inquiry.cmd, size);
+ status = umax_scsi_cmd(dev, inquiry.cmd, inquiry.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_new_inquiry: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX CORRECT INQUIRY ----------------------- */
+
+
+static void umax_correct_inquiry(Umax_Device *dev, char *vendor, char *product, char *version)
+{
+ DBG(DBG_info, "umax_correct_inquiry(\"%s %s %s\")\n", vendor, product, version);
+
+ if (!strncmp(vendor, "UMAX ", 5))
+ {
+ if (!strncmp(product, "Astra 600S ", 11))
+ {
+ int add_len = get_inquiry_additional_length(dev->buffer[0]);
+
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (add_len == 0x8f)
+ {
+ DBG(DBG_warning," - correcting wrong inquiry data\n");
+ umax_do_new_inquiry(dev, 0x9b); /* get inquiry with correct length */
+ set_inquiry_length(dev->buffer[0], 0x9e); /* correct inquiry len */
+ /* correct color-ordering from pixel to line_with_ccd_distance */
+ set_inquiry_color_order(dev->buffer[0], IN_color_ordering_line_w_ccd);
+ set_inquiry_fb_uta_line_arrangement_mode(dev->buffer[0], 32);
+ set_inquiry_CCD_line_distance(dev->buffer[0], 8);
+ /* we should reset ADF-bit here too */
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf */
+ {
+ DBG(DBG_warning," - activating inversion of shading data\n");
+ dev->invert_shading_data = 1;
+ }
+ }
+ }
+ else if (!strncmp(product, "Astra 610S ", 11))
+ {
+ int add_len = get_inquiry_additional_length(dev->buffer[0]);
+
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (add_len == 0x8f)
+ {
+ DBG(DBG_warning," - correcting wrong inquiry data\n");
+ umax_do_new_inquiry(dev, 0x9b); /* get inquiry with correct length */
+ set_inquiry_length(dev->buffer[0], 0x9e); /* correct inquiry len */
+ /* correct color-ordering from pixel to line_with_ccd_distance */
+ set_inquiry_color_order(dev->buffer[0], IN_color_ordering_line_w_ccd);
+ set_inquiry_fb_uta_line_arrangement_mode(dev->buffer[0], 33);
+ set_inquiry_CCD_line_distance(dev->buffer[0], 8);
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf */
+ {
+ DBG(DBG_warning," - activating inversion of shading data\n");
+ dev->invert_shading_data = 1;
+ }
+ }
+ }
+ else if ( (!strncmp(product, "Astra 1200S ", 12)) ||
+ (!strncmp(product, "Perfection600 ", 14)) )
+ {
+ DBG(DBG_warning,"using standard options for %s\n", product);
+ }
+ else if (!strncmp(product, "Astra 1220S ", 12))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->gamma_lsb_padded == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ DBG(DBG_warning," - 16 bit gamma table is created lsb padded\n");
+ dev->gamma_lsb_padded = 1;
+ }
+
+ if (!strncmp(version, "V1.5 ", 4))
+ {
+ DBG(DBG_warning," - lamp control enabled for version %s\n", version);
+ dev->lamp_control_available = 1;
+ }
+ }
+ else if (!strncmp(product, "Astra 2100S ", 12))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - lamp control enabled\n");
+ dev->lamp_control_available = 1;
+
+ if (dev->calibration_bytespp == -1) /* no calibration-bytespp defined in umax.conf */
+ {
+ DBG(DBG_warning," - setting calibration_bytespp = 1\n");
+ dev->calibration_bytespp = 1; /* scanner says 2 bytespp for calibration but 1 bytepp is correct */
+ }
+ }
+ else if (!strncmp(product, "Astra 2200 ", 11))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - lamp control enabled\n");
+ dev->lamp_control_available = 1;
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+
+ if (dev->calibration_bytespp == -1) /* no calibration-bytespp defined in umax.conf */
+ {
+ DBG(DBG_warning," - setting calibration_bytespp = 2\n");
+ dev->calibration_bytespp = 2;
+ }
+
+ DBG(DBG_warning," - common x and y resolution\n");
+ dev->common_xy_resolutions = 1;
+
+ if (dev->connection_type == SANE_UMAX_USB)
+ {
+ DBG(DBG_warning," - disabling quality calibration for USB connection\n");
+ set_inquiry_fw_quality(dev->buffer[0], 0);
+ }
+ }
+ else if (!strncmp(product, "Astra 2400S ", 12))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - defining pauses\n");
+ dev->pause_for_color_calibration = 7000; /* pause between start_scan and do_calibration in ms */
+ dev->pause_for_gray_calibration = 4000; /* pause between start_scan and do_calibration in ms */
+ dev->pause_after_calibration = 0000; /* pause between do_calibration and read data in ms */
+ dev->pause_after_reposition = 3000; /* pause after repostion scanner in ms */
+ dev->pause_for_moving = 3000; /* pause for moving scanhead over full area */
+
+ DBG(DBG_warning," - correcting ADF bit in inquiry\n");
+ set_inquiry_sc_adf(dev->buffer[0], 1); /* set second bit that indicates ADF is supported */
+ }
+ else if (!strncmp(product, "Vista-T630 ", 11))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->slow == -1) /* option is not predefined in umax.conf */
+ {
+ DBG(DBG_warning," - activating slow option\n");
+ dev->slow = 1;
+ }
+ }
+ else if (!strncmp(product, "UC630 ", 6))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - reposition_scanner waits until move of scan head has finished\n");
+ dev->pause_after_reposition = 0; /* call wait_scanner */
+ }
+ else if (!strncmp(product, "UC840 ", 6))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - reposition_scanner waits until move of scan head has finished\n");
+ dev->pause_after_reposition = 0; /* call wait_scanner */
+ }
+ else if (!strncmp(product, "UC1260 ", 7))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 1\n");
+ dev->inquiry_gamma_DCF = 1; /* define gamma download curve format */
+ DBG(DBG_warning," - reposition_scanner waits until move of scan head has finished\n");
+ dev->pause_after_reposition = 0; /* call wait_scanner */
+ }
+ else if (!strncmp(product, "UC1200S ", 8))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 1\n");
+ dev->inquiry_gamma_DCF = 1; /* define gamma download curve format */
+ }
+ else if (!strncmp(product, "UC1200SE ", 9))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 0\n");
+ dev->inquiry_gamma_DCF = 0; /* define gamma download curve format */
+ }
+ else if (!strncmp(product, "ARCUS PLUS ", 11))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 0\n");
+ dev->inquiry_gamma_DCF = 0; /* define gamma download curve format */
+ }
+ else if ( (!strncmp(product, "UMAX S-12G ", 11)) ||
+ (!strncmp(product, "UMAX S-12 ", 10)) ||
+ (!strncmp(product, "SuperVista S-12 ", 16)) )
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ DBG(DBG_warning," - setting maximum calibration data lines to 66\n");
+ set_inquiry_max_calibration_data_lines(dev->buffer[0], 66);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = -1;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+ }
+ else if (!strncmp(product, "Mirage D-16L ", 13))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 308;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ }
+ else if (!strncmp(product, "PowerLook III ", 14))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 28;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 828;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else if (!strncmp(product, "Power Look 2000", 15))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 22;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 24;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else if (!strncmp(product, "PowerLook 2100XL", 16))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 52;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 1052;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+
+ dev->force_quality_calibration = 1;
+ DBG(DBG_warning," - always set quality calibration\n");
+
+ /* the scanner uses the same exposure times for red, green and blue exposure_time_rgb_bind = 1 */
+ }
+ else if (!strncmp(product, "PowerLook 3000 ", 15))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 52;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ /* not tested */
+ dev->calibration_width_offset_batch = 1052;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else
+ {
+ DBG(DBG_warning,"using standard options for %s\n", product);
+ }
+ }
+ else if (!strncmp(vendor, "LinoHell ", 9))
+ {
+ if ( (!strncmp(product, "Office ", 7)) || (!strncmp(product, "JADE ", 5)) ) /* is a Supervista S-12 */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ DBG(DBG_warning," - setting maximum calibration data lines to 66\n");
+ set_inquiry_max_calibration_data_lines(dev->buffer[0], 66);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = -1;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+ }
+ else if (!strncmp(product, "OPAL2 ", 6)) /* looks like a Mirage II */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->gamma_lsb_padded == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ DBG(DBG_warning," - 16 bit gamma table is created lsb padded\n");
+ dev->gamma_lsb_padded = 1;
+ }
+ }
+ }
+ else if (!strncmp(vendor, "Linotype ", 9))
+ {
+ if (!strncmp(product, "SAPHIR4 ", 8)) /* is a Powerlook III */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 28;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 828;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ }
+ else if (!strncmp(vendor, "HDM ", 4))
+ {
+ if (!strncmp(product, "LS4H1S ", 7)) /* is a Powerlook III */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 28;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 828;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ }
+ else if (!strncmp(vendor, "ESCORT ", 7))
+ {
+ if (!strncmp(product, "Galleria 600S ", 14)) /* this is an Astra 600S */
+ {
+ int add_len = get_inquiry_additional_length(dev->buffer[0]);
+
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (add_len == 0x8f)
+ {
+ DBG(DBG_warning," - correcting wrong inquiry data\n");
+ umax_do_new_inquiry(dev, 0x9b); /* get inquiry with correct length */
+ set_inquiry_length(dev->buffer[0], 0x9e); /* correct inquiry len */
+ /* correct color-ordering from pixel to line_with_ccd_distance */
+ set_inquiry_color_order(dev->buffer[0], IN_color_ordering_line_w_ccd);
+ set_inquiry_fb_uta_line_arrangement_mode(dev->buffer[0], 32);
+ set_inquiry_CCD_line_distance(dev->buffer[0], 8);
+ /* we should reset ADF-bit here too */
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf */
+ {
+ DBG(DBG_warning," - activating inversion of shading data\n");
+ dev->invert_shading_data = 1;
+ }
+ }
+ }
+ }
+ else if (!strncmp(vendor, "TriGem ", 7))
+ {
+ if (!strncmp(product, "PowerScanII ", 12)) /* is a Supervista S-12 */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ DBG(DBG_warning," - setting maximum calibration data lines to 66\n");
+ set_inquiry_max_calibration_data_lines(dev->buffer[0], 66);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = -1;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX IDENTIFY SCANNER ---------------------- */
+
+
+static int umax_identify_scanner(Umax_Device *dev)
+{
+ char vendor[10];
+ char product[0x12];
+ char version[6];
+ char *pp;
+
+ DBG(DBG_proc,"identify_scanner\n");
+ umax_do_inquiry(dev); /* get inquiry */
+ if (get_inquiry_periph_devtype(dev->buffer[0]) != IN_periph_devtype_scanner) { return 1; } /* no scanner */
+
+ get_inquiry_vendor( (char *)dev->buffer[0], vendor); vendor[8] = ' '; vendor[9] = '\0';
+ get_inquiry_product((char *)dev->buffer[0], product); product[16] = ' '; product[17] = '\0';
+ get_inquiry_version((char *)dev->buffer[0], version); version[4] = ' '; version[5] = '\0';
+
+ pp = &vendor[8];
+ while (*(pp-1) == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &product[0x10];
+ while (*(pp-1) == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &version[4];
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ DBG(DBG_info, "Found %s scanner %sversion %s on device %s\n", vendor, product, version, dev->devicename);
+
+ /* look for scanners that do not give all inquiry-information */
+ /* and if possible use driver-known inquiry-data */
+
+ if (get_inquiry_additional_length(dev->buffer[0])>=0x8f)
+ {
+ int i = 0;
+ while (strncmp("END_OF_LIST", scanner_str[2*i], 11) != 0) /* Now identify full supported scanners */
+ {
+ if (!strncmp(vendor, scanner_str[2*i], strlen(scanner_str[2*i])) )
+ {
+ if (!strncmp(product, scanner_str[2*i+1], strlen(scanner_str[2*i+1])) )
+ {
+ umax_correct_inquiry(dev, vendor, product, version);
+ return 0;
+ }
+ }
+ i++;
+ }
+
+ if (strncmp(vendor, "UMAX ", 5)) { return 1; } /* not UMAX then abort */
+
+ DBG(DBG_error0, "WARNING: %s scanner %s version %s on device %s\n"
+ "is currently an unrecognized device for this backend version.\n"
+ "Please make sure you use the most recent version of the umax backend.\n"
+ "You can download new umax-backend versions from:\n"
+ "http://www.rauch-domain.de/sane-umax\n",
+ vendor, product, version, dev->devicename);
+
+ DBG(DBG_error0,
+ "Inquiry seems to be ok.\n"
+ "******************************************************************\n"
+ "*** !!!! CONTINUE AT YOUR OWN RISK !!!! ***\n"
+ "******************************************************************\n"
+ "If you already use the most recent umax-backend version\n"
+ "then please contact me: Oliver.Rauch@rauch-domain.de\n");
+
+ return 0;
+ }
+ else /* inquiry-data not complete */
+ if (!strncmp(vendor, "UMAX ", 5)) /* test UMAX-scanners with short inquiry */
+ {
+ inquiry_blk inq_data;
+ int i;
+
+ for(i=0; i < known_inquiry; i++)
+ {
+ inq_data = *inquiry_table[i];
+ if (!strncmp(product, inq_data.scanner, strlen(inq_data.scanner)))
+ {
+ DBG(DBG_warning, "inquiry-block-length: %d\n", get_inquiry_additional_length(dev->buffer[0])+5);
+ DBG(DBG_warning, "using driver-internal inquiry-data for this scanner!\n");
+
+ /* copy driver-defined inquiry-data into inquiry-block */
+ memcpy(dev->buffer[0]+0x24, inq_data.inquiry, inq_data.inquiry_len-0x24);
+
+ /* correct variables */
+ set_inquiry_sc_uta(dev->buffer[0], get_inquiry_transavail(dev->buffer[0])); /* transparancy available ? */
+ set_inquiry_sc_adf(dev->buffer[0], get_inquiry_scanmode(dev->buffer[0])); /* automatic document feeder available ? */
+
+ set_inquiry_length(dev->buffer[0], inq_data.inquiry_len);
+ umax_correct_inquiry(dev, vendor, product, version);
+
+ return 0; /* ok */
+ }
+ }
+ DBG(DBG_error0, "ERROR: %s scanner %s version %s on device %s\n"
+ "is currently an unrecognized device, and inquiry is too short,\n"
+ "so we are not able to continue!\n"
+ "Please make sure you use the most recent version of the umax backend.\n"
+ "You can download new umax-backend versions from:\n"
+ "http://www.rauch-domain.de/sane-umax\n"
+ "You already use the most recent umax-backend version:\n"
+ "Please contact me: Oliver.Rauch@rauch-domain.de\n",
+ vendor, product, version, dev->devicename);
+ }
+
+ return 1; /* NO SUPPORTED SCANNER: short inquiry-block and unknown scanner */
+}
+
+
+/* ------------------------------------------------------------ UMAX TRIM BUFSIZE -------------------------- */
+
+
+static void umax_trim_rowbufsize(Umax_Device *dev)
+{
+ unsigned int lines=0;
+
+ if (dev->row_bufsize > dev->row_len)
+ {
+ lines = dev->row_bufsize / dev->row_len;
+
+ if (lines > dev->lines_max) /* reduce number of lines to scan if set up in config file */
+ {
+ lines = dev->lines_max;
+ }
+
+ dev->row_bufsize = lines * dev->row_len;
+ }
+
+ DBG(DBG_proc,"trim_rowbufsize: row_bufsize = %d bytes = %d lines\n", dev->row_bufsize, lines);
+}
+
+
+/* ------------------------------------------------------------ UMAX CALCULATE EXPOSURE TIME --------------- */
+
+
+static void umax_calculate_exposure_time(Umax_Device *dev, int def, int *value)
+{
+ int level;
+
+ DBG(DBG_proc,"calculate_exposure_time\n");
+ if ( (*value))
+ {
+ if ( (*value) == -1 ) { (*value) = def; }
+ else
+ {
+ level = (*value) / dev->inquiry_exposure_time_step_unit;
+ (*value) = inrange(dev->use_exposure_time_min, level, dev->inquiry_exposure_time_max);
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX CHECK VALUES -------------------------- */
+
+
+static int umax_check_values(Umax_Device *dev)
+{
+ double inquiry_x_orig;
+ double inquiry_y_orig;
+ double inquiry_width;
+ double inquiry_length;
+ unsigned int maxwidth;
+ unsigned int maxlength;
+
+ DBG(DBG_proc,"check_values\n");
+
+ /* ------------------------------- flatbed ------------------------------- */
+
+ dev->module = WD_module_flatbed; /* reset scanmode to flatbed first */
+
+ /* --------------------------------- uta --------------------------------- */
+
+ if (dev->uta != 0)
+ {
+ dev->module = WD_module_transparency;
+ if ( (dev->inquiry_uta == 0) || (dev->inquiry_transavail == 0) )
+ {
+ DBG(DBG_error, "ERROR: transparency mode not supported by scanner\n");
+ return(1);
+ }
+ }
+
+ /* --------------------------------- adf --------------------------------- */
+
+ if (dev->adf != 0)
+ {
+ if (dev->inquiry_adf == 0)
+ {
+ DBG(DBG_error,"ERROR: adf mode not supported by scanner\n");
+ return(1);
+ }
+ }
+
+ /* --------------------------------- dor --------------------------------- */
+
+ if (dev->dor != 0)
+ {
+ if (dev->inquiry_dor == 0)
+ {
+ DBG(DBG_error, "ERROR: double optical resolution not supported by scanner\n");
+ return(1);
+ }
+ }
+
+ /* ------------------------------- resolution ------------------------ */
+
+ if (dev->dor == 0) /* standard (FB) */
+ {
+ dev->relevant_optical_res = dev->inquiry_optical_res;
+ dev->relevant_max_x_res = dev->inquiry_x_res;
+ dev->relevant_max_y_res = dev->inquiry_y_res;
+ }
+ else /* DOR mode */
+ {
+ dev->relevant_optical_res = dev->inquiry_dor_optical_res;
+ dev->relevant_max_x_res = dev->inquiry_dor_x_res;
+ dev->relevant_max_y_res = dev->inquiry_dor_y_res;
+ }
+
+ if (dev->x_resolution <= 0)
+ {
+ DBG(DBG_error,"ERROR: no x-resolution given\n");
+ return(1);
+ }
+
+ if (dev->x_resolution > dev->relevant_max_x_res)
+ {
+ dev->x_resolution = dev->relevant_max_x_res;
+ }
+
+ if (dev->x_resolution > dev->relevant_optical_res)
+ {
+ dev->scale_x = 2;
+ }
+ else
+ {
+ dev->scale_x = 1;
+ }
+
+ if (dev->y_resolution <= 0)
+ {
+ DBG(DBG_error,"ERROR: no y-resolution given\n");
+ return(1);
+ }
+
+ if (dev->y_resolution > dev->relevant_max_y_res)
+ {
+ dev->y_resolution = dev->relevant_max_y_res;
+ }
+
+ if (dev->y_resolution > dev->relevant_optical_res)
+ {
+ dev->scale_y = 2;
+ }
+ else if (dev->y_resolution > dev->relevant_optical_res/2)
+ {
+ dev->scale_y = 1;
+ }
+ else
+ {
+ /* astra 600S and 610S need this in umax_forget_line */
+ dev->scale_y = 0.5;
+ }
+
+
+ /* ------------------------------- scanarea ------------------------ */
+
+ if (dev->module == WD_module_flatbed) /* flatbed mode */
+ {
+ inquiry_x_orig = 0; /* flatbed origin */
+ inquiry_y_orig = 0;
+ inquiry_width = dev->inquiry_fb_width; /* flatbed width */
+ inquiry_length = dev->inquiry_fb_length;
+ }
+ else /* transparency mode */
+ {
+ inquiry_x_orig = dev->inquiry_uta_x_off; /* uta origin */
+ inquiry_y_orig = dev->inquiry_uta_y_off;
+ inquiry_width = dev->inquiry_uta_x_off + dev->inquiry_uta_width; /* uta width */
+ inquiry_length = dev->inquiry_uta_y_off + dev->inquiry_uta_length;
+ }
+
+ if (dev->dor != 0)
+ {
+ inquiry_x_orig = dev->inquiry_dor_x_off; /* dor origin */
+ inquiry_y_orig = dev->inquiry_dor_y_off;
+ inquiry_width = dev->inquiry_dor_x_off + dev->inquiry_dor_width; /* dor width */
+ inquiry_length = dev->inquiry_dor_y_off + dev->inquiry_dor_length;
+ }
+
+ /* limit the size to what the scanner can scan. */
+ /* this is particularly important because the scanners don't have */
+ /* built-in checks and will happily grind their gears if this is exceeded. */
+
+
+ maxwidth = inquiry_width * dev->x_coordinate_base - dev->upper_left_x - 1;
+
+ if ( (dev->scanwidth <= 0) || (dev->scanwidth > maxwidth) )
+ {
+ dev->scanwidth = maxwidth;
+ }
+
+ if (dev->upper_left_x < inquiry_x_orig)
+ {
+ dev->upper_left_x = inquiry_x_orig;
+ }
+
+
+ maxlength = inquiry_length * dev->y_coordinate_base - dev->upper_left_y - 1;
+
+ if ( (dev->scanlength <= 0) || (dev->scanlength > maxlength) )
+ {
+ dev->scanlength = maxlength;
+ }
+
+ if (dev->upper_left_y < inquiry_y_orig)
+ {
+ dev->upper_left_y = inquiry_y_orig;
+ }
+
+
+ /* Now calculate width and length in pixels */
+ dev->width_in_pixels = umax_calculate_pixels(dev->scanwidth, dev->x_resolution,
+ dev->relevant_optical_res * dev->scale_x, dev->x_coordinate_base);
+
+ dev->length_in_pixels = umax_calculate_pixels(dev->scanlength, dev->y_resolution,
+ dev->relevant_optical_res * dev->scale_y, dev->y_coordinate_base);
+
+ if ((dev->scanwidth <= 0) || (dev->scanlength <= 0))
+ {
+ DBG(DBG_error,"ERROR: scanwidth or scanlength not given\n");
+ return(1);
+ }
+
+ if (dev->bits_per_pixel_code == 1)
+ {
+ dev->bytes_per_color = 1;
+ }
+ else
+ {
+ dev->bytes_per_color = 2;
+ }
+
+ switch(dev->colormode)
+ {
+ case LINEART:
+ dev->width_in_pixels -= dev->width_in_pixels % 8;
+ dev->row_len = (dev->width_in_pixels / 8);
+ break;
+
+ case HALFTONE:
+ dev->width_in_pixels -= dev->width_in_pixels % 8;
+ dev->row_len = (dev->width_in_pixels / 8);
+ break;
+
+ case GRAYSCALE:
+ dev->row_len = dev->width_in_pixels * dev->bytes_per_color;
+ break;
+
+ case RGB_LINEART:
+ case RGB_HALFTONE:
+ if (dev->three_pass)
+ {
+ dev->row_len = dev->width_in_pixels / 8 ;
+ }
+ else
+ {
+ dev->row_len = (dev->width_in_pixels / 8 ) * 3;
+ }
+ break;
+
+ case RGB:
+ if (dev->three_pass) /* three (24bpp) or six (30bpp) bytes per pixel */
+ {
+ dev->row_len = dev->width_in_pixels * dev->bytes_per_color;
+ }
+ else
+ {
+ dev->row_len = dev->width_in_pixels * 3 * dev->bytes_per_color;
+ }
+ break;
+ }
+
+
+ /* ------------------------------- wdb length ------------------------ */
+
+ if (dev->wdb_len <= 0)
+ {
+ dev->wdb_len = dev->inquiry_wdb_len;
+ if (dev->wdb_len <= 0)
+ {
+ DBG(DBG_error,"ERROR: wdb-length not given\n");
+ return(1);
+ }
+ }
+
+ if (dev->wdb_len > used_WDB_size)
+ {
+ DBG(DBG_warning,"WARNING:window descriptor block too long, will be shortned!\n");
+ dev->wdb_len = used_WDB_size;
+ }
+
+ /* ----------------------------- cbhs-range ----------------------------- */
+
+ dev->threshold = umax_cbhs_correct(dev->inquiry_threshold_min, dev->threshold , dev->inquiry_threshold_max);
+ dev->contrast = umax_cbhs_correct(dev->inquiry_contrast_min, dev->contrast , dev->inquiry_contrast_max);
+ dev->brightness = umax_cbhs_correct(dev->inquiry_brightness_min, dev->brightness, dev->inquiry_brightness_max);
+
+ dev->highlight_r = umax_cbhs_correct(dev->inquiry_highlight_min, dev->highlight_r, dev->inquiry_highlight_max);
+ dev->highlight_g = umax_cbhs_correct(dev->inquiry_highlight_min, dev->highlight_g, dev->inquiry_highlight_max);
+ dev->highlight_b = umax_cbhs_correct(dev->inquiry_highlight_min, dev->highlight_b, dev->inquiry_highlight_max);
+
+ dev->shadow_r = umax_cbhs_correct(dev->inquiry_shadow_min, dev->shadow_r, dev->inquiry_shadow_max-1);
+ dev->shadow_g = umax_cbhs_correct(dev->inquiry_shadow_min, dev->shadow_g, dev->inquiry_shadow_max-1);
+ dev->shadow_b = umax_cbhs_correct(dev->inquiry_shadow_min, dev->shadow_b, dev->inquiry_shadow_max-1);
+
+ if (dev->shadow_r >= dev->highlight_r)
+ {
+ dev->shadow_r = dev->highlight_r-1;
+ }
+ if (dev->shadow_g >= dev->highlight_g)
+ {
+ dev->shadow_g = dev->highlight_g-1;
+ }
+ if (dev->shadow_b >= dev->highlight_b)
+ {
+ dev->shadow_b = dev->highlight_b-1;
+ }
+
+ /* ----------------------- quality calibration and preview -------------- */
+
+ if (dev->inquiry_preview == 0)
+ {
+ if (dev->preview)
+ {
+ DBG(DBG_warning, "WARNING: fast preview function not supported by scanner\n");
+ dev->preview = 0;
+ }
+ }
+
+ /* always set calibration lines because we also need this value if the scanner
+ requeires calibration by driver */
+ dev->calib_lines = dev->inquiry_max_calib_lines;
+
+ if (dev->force_quality_calibration)
+ {
+ dev->quality = 1; /* always use quality calibration */
+ }
+ else if (dev->inquiry_quality_ctrl == 0)
+ {
+ if (dev->quality)
+ {
+ DBG(DBG_warning, "WARNING: quality calibration not supported by scanner\n");
+ dev->quality = 0;
+ }
+ }
+ else
+ {
+ if (dev->preview != 0)
+ {
+ DBG(DBG_info, "quality calibration disabled in preview mode\n");
+ dev->quality = 0; /* do not use quality calibration in preview mode */
+ }
+ }
+
+ /* --------------------------- lamp intensity control ------------------- */
+
+ if (dev->inquiry_lamp_ctrl == 0)
+ {
+ if (dev->c_density || dev->s_density)
+ {
+ DBG(DBG_warning, "WARNING: scanner doesn't support lamp intensity control\n");
+ }
+ dev->c_density = dev->s_density = 0;
+ }
+
+
+ /* --------------------------- reverse (negative) ----------------------- */
+
+ if (dev->reverse != 0)
+ {
+ if ( (dev->colormode == LINEART) || (dev->colormode == HALFTONE) ||
+ (dev->colormode == RGB_LINEART) || (dev->colormode == RGB_HALFTONE) )
+ {
+ if (dev->inquiry_reverse == 0)
+ {
+ DBG(DBG_error, "ERROR: reverse for bi-level-image not supported\n");
+ return(1);
+ }
+ }
+ else
+ { dev->reverse = 0; }
+ }
+
+ if (dev->reverse_multi != 0)
+ {
+ if ((dev->colormode == RGB) || (dev->colormode == GRAYSCALE) )
+ {
+ if (dev->inquiry_reverse_multi == 0)
+ {
+ DBG(DBG_error, "ERROR: reverse for multi-level-image not supported\n");
+ return(1);
+ }
+ }
+ else
+ {
+ dev->reverse_multi = 0;
+ }
+ }
+
+ /* ----------------------------- analog gamma ---------------------------- */
+
+ if (dev->inquiry_analog_gamma == 0)
+ {
+ if (dev->analog_gamma_r + dev->analog_gamma_g + dev->analog_gamma_b != 0)
+ {
+ DBG(DBG_warning,"WARNING: analog gamma correction not supported by scanner!\n");
+ }
+ dev->analog_gamma_r = dev->analog_gamma_g = dev->analog_gamma_b = 0;
+ }
+
+ /* ---------------------------- digital gamma ---------------------------- */
+
+ if ( (dev->digital_gamma_r == 0) || (dev->digital_gamma_g == 0) ||
+ (dev->digital_gamma_b == 0) )
+ {
+ if (dev->inquiry_gamma_dwload == 0)
+ {
+ DBG(DBG_warning, "WARNING: gamma download not available\n");
+ dev->digital_gamma_r = dev->digital_gamma_g = dev->digital_gamma_b = 15;
+ }
+ }
+
+ /* ---------------------------- speed and smear ------------------------- */
+
+ if (dev->slow == 1)
+ {
+ dev->WD_speed = WD_speed_slow;
+ }
+ else
+ {
+ dev->WD_speed = WD_speed_fast;
+ }
+
+ if (dev->smear == 1)
+ {
+ dev->WD_speed += WD_speed_smear;
+ }
+
+ /* ---------------------- test bits per pixel --------------------------- */
+
+ if ( ( (dev->inquiry_GIB | 1) & dev->gamma_input_bits_code) == 0 )
+ {
+ DBG(DBG_warning,"WARNING: selected gamma input bits not supported, gamma ignored\n");
+ dev->gamma_input_bits_code = 1;
+ dev->digital_gamma_r = dev->digital_gamma_g = dev->digital_gamma_b = 15;
+ }
+
+ if ( ( (dev->inquiry_GOB | 1) & dev->bits_per_pixel_code) == 0 )
+ {
+ DBG(DBG_error,"ERROR: selected bits per pixel not supported\n");
+ return(1);
+ }
+
+ /* ----------------------- scan mode dependencies ------------------------ */
+
+ switch(dev->colormode)
+ {
+ case LINEART: /* ------------ LINEART ------------- */
+ case RGB_LINEART: /* ---------- RGB_LINEART ----------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_l_min;
+
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_l_fb_def;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_l_uta_def;
+ }
+
+ if (dev->inquiry_lineart == 0)
+ {
+ DBG(DBG_error,"ERROR: lineart mode not supported by scanner\n");
+ return(1);
+ }
+ break;
+
+ case HALFTONE: /* ----------- HALFTONE------------ */
+ case RGB_HALFTONE: /* --------- RGB_HALFTONE---------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_h_min;
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_h_fb_def;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_h_uta_def;
+ }
+
+ if (dev->inquiry_halftone == 0)
+ {
+ DBG(DBG_error,"ERROR: halftone mode not supported by scanner\n");
+ return(1);
+ }
+ break;
+
+ case GRAYSCALE: /* ---------- GRAYSCALE ------------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_g_min;
+
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_g_fb_def;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_g_uta_def;
+ }
+
+ if (dev->inquiry_gray == 0)
+ {
+ DBG(DBG_error, "ERROR: grayscale mode not supported by scanner\n");
+ return(1);
+ }
+ break;
+
+ case RGB: /* ----------------- COLOR ---------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_c_min;
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_c_fb_def_r;
+ dev->use_exposure_time_def_g = dev->inquiry_exposure_time_c_fb_def_g;
+ dev->use_exposure_time_def_b = dev->inquiry_exposure_time_c_fb_def_b;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_c_uta_def_r;
+ dev->use_exposure_time_def_g = dev->inquiry_exposure_time_c_uta_def_g;
+ dev->use_exposure_time_def_b = dev->inquiry_exposure_time_c_uta_def_b;
+ }
+
+ if (dev->inquiry_color == 0)
+ {
+ DBG(DBG_error,"ERROR: color mode not supported by scanner\n");
+ return(1);
+ }
+
+ if (dev->inquiry_one_pass_color)
+ {
+ DBG(DBG_info,"using one pass scanning mode\n");
+
+ if (dev->inquiry_color_order & IN_color_ordering_pixel)
+ {
+ DBG(DBG_info,"scanner uses color-pixel-ordering\n");
+ }
+ else if (dev->inquiry_color_order & IN_color_ordering_line_no_ccd)
+ {
+ dev->CCD_distance = 0;
+ dev->do_color_ordering = 1;
+ DBG(DBG_info,"scanner uses color-line-ordering without CCD-distance\n");
+ }
+ else if (dev->inquiry_color_order & IN_color_ordering_line_w_ccd)
+ {
+ dev->CCD_distance = dev->inquiry_CCD_line_distance;
+ dev->do_color_ordering = 1;
+ switch (dev->inquiry_fb_uta_color_arrangement) /* define color order for line ordering */
+ {
+ case 1:
+ dev->CCD_color[0] = CCD_color_green;
+
+ dev->CCD_color[1] = CCD_color_blue;
+ dev->CCD_color[2] = CCD_color_green;
+
+ dev->CCD_color[3] = CCD_color_blue;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_green;
+
+ dev->CCD_color[6] = CCD_color_blue;
+ dev->CCD_color[7] = CCD_color_red;
+
+ dev->CCD_color[8] = CCD_color_red;
+ break;
+
+ case 2:
+ dev->CCD_color[0] = CCD_color_blue;
+
+ dev->CCD_color[1] = CCD_color_green;
+ dev->CCD_color[2] = CCD_color_blue;
+
+ dev->CCD_color[3] = CCD_color_green;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_green;
+ dev->CCD_color[7] = CCD_color_red;
+
+ dev->CCD_color[8] = CCD_color_red;
+ break;
+
+ case 3:
+ dev->CCD_color[0] = CCD_color_red;
+
+ dev->CCD_color[1] = CCD_color_blue;
+ dev->CCD_color[2] = CCD_color_red;
+
+ dev->CCD_color[3] = CCD_color_blue;
+ dev->CCD_color[4] = CCD_color_green;
+ dev->CCD_color[5] = CCD_color_red;
+
+ dev->CCD_color[6] = CCD_color_blue;
+ dev->CCD_color[7] = CCD_color_green;
+
+ dev->CCD_color[8] = CCD_color_green;
+ break;
+
+ case 4: /* may be wrong !!! */
+ dev->CCD_color[0] = CCD_color_red;
+
+ dev->CCD_color[1] = CCD_color_green;
+ dev->CCD_color[2] = CCD_color_red;
+
+ dev->CCD_color[3] = CCD_color_green;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_green;
+ dev->CCD_color[7] = CCD_color_blue;
+
+ dev->CCD_color[8] = CCD_color_blue;
+ break;
+
+ case 32: /* not defined from UMAX, for Astra 600S */
+ dev->CCD_color[0] = CCD_color_green;
+
+ dev->CCD_color[1] = CCD_color_green;
+ dev->CCD_color[2] = CCD_color_blue;
+
+ dev->CCD_color[3] = CCD_color_green;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_red;
+ dev->CCD_color[7] = CCD_color_blue;
+
+ dev->CCD_color[8] = CCD_color_red;
+ break;
+
+ case 33: /* not defined from UMAX, for Astra 610S */
+ dev->CCD_color[0] = CCD_color_red;
+
+ dev->CCD_color[1] = CCD_color_red;
+ dev->CCD_color[2] = CCD_color_blue;
+
+ dev->CCD_color[3] = CCD_color_red;
+ dev->CCD_color[4] = CCD_color_green;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_green;
+ dev->CCD_color[7] = CCD_color_blue;
+
+ dev->CCD_color[8] = CCD_color_green;
+ break;
+
+ default:
+ dev->CCD_color[0] = CCD_color_green;
+
+ dev->CCD_color[1] = CCD_color_blue;
+ dev->CCD_color[2] = CCD_color_green;
+
+ dev->CCD_color[3] = CCD_color_blue;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_green;
+
+ dev->CCD_color[6] = CCD_color_blue;
+ dev->CCD_color[7] = CCD_color_red;
+
+ dev->CCD_color[8] = CCD_color_red;
+ }
+ DBG(DBG_info,"scanner uses color-line-ordering with CCD-distance of %d lines\n", dev->CCD_distance);
+ }
+ else
+ {
+ DBG(DBG_error,"ERROR: color-ordering-type not supported \n");
+ return(1);
+ }
+ }
+ else
+ {
+ DBG(DBG_info,"using three pass scanning mode\n");
+ dev->three_pass=1;
+ }
+ break;
+ } /* switch */
+
+ /* ----------------------------- color ordering ------------------------ */
+
+ if (dev->do_color_ordering != 0)
+ {
+ if ( (dev->colormode != RGB) || (dev->three_pass != 0) )
+ {
+ dev->do_color_ordering = 0; /* color ordering not necessery */
+ }
+ }
+
+ return(0);
+}
+
+
+/* ------------------------------------------------------------ UMAX GET INQUIRY VALUES -------------------- */
+
+
+static void umax_get_inquiry_values(Umax_Device *dev)
+{
+ unsigned char * inquiry_block;
+
+ DBG(DBG_proc,"get_inquiry_values\n");
+
+ inquiry_block = dev->buffer[0];
+ dev->inquiry_len = get_inquiry_additional_length(dev->buffer[0])+5;
+ dev->cbhs_range = dev->inquiry_cbhs = get_inquiry_CBHS(inquiry_block);
+
+ if (dev->cbhs_range > IN_CBHS_255)
+ {
+ dev->cbhs_range = IN_CBHS_255;
+ }
+
+ if (dev->cbhs_range == IN_CBHS_50)
+ {
+ dev->inquiry_contrast_min = 103; /* minimum value for c */
+ dev->inquiry_contrast_max = 153; /* maximum value for c */
+ dev->inquiry_brightness_min = 78; /* minimum value for b */
+ dev->inquiry_brightness_max = 178; /* maximum value for b */
+ dev->inquiry_threshold_min = 78; /* minimum value for t */
+ dev->inquiry_threshold_max = 178; /* maximum value for t */
+ dev->inquiry_highlight_min = 1; /* minimum value for h */
+ dev->inquiry_highlight_max = 50; /* maximum value for h */
+ dev->inquiry_shadow_min = 0; /* minimum value for s */
+ dev->inquiry_shadow_max = 49; /* maximum value for s */
+ }
+
+ get_inquiry_vendor( (char *)inquiry_block, dev->vendor); dev->vendor[8] ='\0';
+ get_inquiry_product((char *)inquiry_block, dev->product); dev->product[16]='\0';
+ get_inquiry_version((char *)inquiry_block, dev->version); dev->version[4] ='\0';
+
+ dev->inquiry_batch_scan = get_inquiry_fw_batch_scan(inquiry_block);
+ dev->inquiry_quality_ctrl = get_inquiry_fw_quality(inquiry_block);
+ dev->inquiry_preview = get_inquiry_fw_fast_preview(inquiry_block);
+ dev->inquiry_lamp_ctrl = get_inquiry_fw_lamp_int_cont(inquiry_block);
+ dev->inquiry_calibration = get_inquiry_fw_calibration(inquiry_block);
+ dev->inquiry_transavail = get_inquiry_transavail(inquiry_block);
+ dev->inquiry_adfmode = get_inquiry_scanmode(inquiry_block);
+
+ if (dev->inquiry_len<=0x8f)
+ {
+ DBG(DBG_warning, "WARNING: inquiry return block is unexpected short.\n");
+ }
+
+ dev->inquiry_uta = get_inquiry_sc_uta(inquiry_block);
+ dev->inquiry_adf = get_inquiry_sc_adf(inquiry_block);
+
+ dev->inquiry_one_pass_color = get_inquiry_sc_one_pass_color(inquiry_block);
+ dev->inquiry_three_pass_color = get_inquiry_sc_three_pass_color(inquiry_block);
+ dev->inquiry_color = get_inquiry_sc_color(inquiry_block);
+ dev->inquiry_gray = get_inquiry_sc_gray(inquiry_block);
+ dev->inquiry_halftone = get_inquiry_sc_halftone(inquiry_block);
+ dev->inquiry_lineart = get_inquiry_sc_lineart(inquiry_block);
+
+ dev->inquiry_exposure_adj = get_inquiry_fw_adjust_exposure_tf(inquiry_block);
+ dev->inquiry_exposure_time_step_unit = get_inquiry_exposure_time_step_unit(inquiry_block);
+ dev->inquiry_exposure_time_max = get_inquiry_exposure_time_max(inquiry_block);
+
+ /* --- lineart --- */
+ dev->inquiry_exposure_time_l_min = get_inquiry_exposure_time_lhg_min(inquiry_block);
+ dev->inquiry_exposure_time_l_fb_def = get_inquiry_exposure_time_lh_def_fb(inquiry_block);
+ dev->inquiry_exposure_time_l_uta_def = get_inquiry_exposure_time_lh_def_uta(inquiry_block);
+
+ /* --- halftone --- */
+ dev->inquiry_exposure_time_h_min = get_inquiry_exposure_time_lhg_min(inquiry_block);
+ dev->inquiry_exposure_time_h_fb_def = get_inquiry_exposure_time_lh_def_fb(inquiry_block);
+ dev->inquiry_exposure_time_h_uta_def = get_inquiry_exposure_time_lh_def_uta(inquiry_block);
+
+ /* --- grayscale --- */
+ dev->inquiry_exposure_time_g_min = get_inquiry_exposure_time_lhg_min(inquiry_block);
+ dev->inquiry_exposure_time_g_fb_def = get_inquiry_exposure_time_gray_def_fb(inquiry_block);
+ dev->inquiry_exposure_time_g_uta_def = get_inquiry_exposure_time_gray_def_uta(inquiry_block);
+
+ /* --- color --- */
+ dev->inquiry_exposure_time_c_min = get_inquiry_exposure_time_color_min(inquiry_block);
+ dev->inquiry_exposure_time_c_fb_def_r = get_inquiry_exposure_time_def_r_fb(inquiry_block);
+ dev->inquiry_exposure_time_c_fb_def_g = get_inquiry_exposure_time_def_g_fb(inquiry_block);
+ dev->inquiry_exposure_time_c_fb_def_b = get_inquiry_exposure_time_def_g_fb(inquiry_block);
+ dev->inquiry_exposure_time_c_uta_def_r = get_inquiry_exposure_time_def_r_uta(inquiry_block);
+ dev->inquiry_exposure_time_c_uta_def_g = get_inquiry_exposure_time_def_g_uta(inquiry_block);
+ dev->inquiry_exposure_time_c_uta_def_b = get_inquiry_exposure_time_def_b_uta(inquiry_block);
+
+
+ dev->inquiry_dor = get_inquiry_sc_double_res(inquiry_block);
+ dev->inquiry_reverse = get_inquiry_sc_bi_image_reverse(inquiry_block);
+ dev->inquiry_reverse_multi = get_inquiry_sc_multi_image_reverse(inquiry_block);
+ dev->inquiry_shadow = 1 - get_inquiry_sc_no_shadow(inquiry_block);
+ dev->inquiry_highlight = 1 - get_inquiry_sc_no_highlight(inquiry_block);
+ dev->inquiry_analog_gamma = get_inquiry_analog_gamma(inquiry_block);
+ dev->inquiry_lineart_order = get_inquiry_lineart_order(inquiry_block);
+
+ dev->inquiry_lens_cal_in_doc_pos = get_inquiry_manual_focus(inquiry_block);
+ dev->inquiry_manual_focus = get_inquiry_manual_focus(inquiry_block);
+ dev->inquiry_sel_uta_lens_cal_pos = get_inquiry_manual_focus(inquiry_block);
+
+ dev->inquiry_gamma_dwload = get_inquiry_gamma_download_available(inquiry_block);
+
+ if (get_inquiry_gamma_type_2(inquiry_block) != 0)
+ {
+ dev->inquiry_gamma_DCF = 2;
+ }
+
+ dev->inquiry_GIB = get_inquiry_gib(inquiry_block);
+ dev->inquiry_GOB = get_inquiry_gob(inquiry_block);
+ dev->inquiry_color_order = get_inquiry_color_order(inquiry_block);
+ dev->inquiry_vidmem = get_inquiry_max_vidmem(inquiry_block);
+
+ /* optical resolution = [0x73] * 100 + [0x94] , 0x94 is not always defined */
+ dev->inquiry_optical_res = 100 * get_inquiry_max_opt_res(inquiry_block);
+ if (dev->inquiry_len > 0x94)
+ {
+ dev->inquiry_optical_res += get_inquiry_optical_resolution_residue(inquiry_block);
+ }
+
+ /* x resolution = [0x74] * 100 + [0x95] , 0x95 is not always defined */
+ dev->inquiry_x_res = 100 * get_inquiry_max_x_res(inquiry_block);
+ if (dev->inquiry_len > 0x95)
+ {
+ dev->inquiry_x_res+= get_inquiry_x_resolution_residue(inquiry_block);
+ };
+
+ /* y resolution = [0x75] * 100 + [0x96] , 0x96 is not always defined */
+ dev->inquiry_y_res = 100 * get_inquiry_max_y_res(inquiry_block);
+ if (dev->inquiry_len > 0x96)
+ {
+ dev->inquiry_y_res+= get_inquiry_y_resolution_residue(inquiry_block);
+ }
+
+
+ /* optical resolution = [0x83] * 100 + [0xa0] , 0xa0 is not always defined */
+ dev->inquiry_dor_optical_res = 100 * get_inquiry_dor_max_opt_res(inquiry_block);
+ if (dev->inquiry_len > 0xa0)
+ {
+ dev->inquiry_dor_optical_res += get_inquiry_dor_optical_resolution_residue(inquiry_block);
+ }
+
+ /* x resolution = [0x84] * 100 + [0xa1] , 0xa1 is not always defined */
+ dev->inquiry_dor_x_res = 100 * get_inquiry_dor_max_x_res(inquiry_block);
+ if (dev->inquiry_len > 0xa1)
+ {
+ dev->inquiry_dor_x_res+= get_inquiry_dor_x_resolution_residue(inquiry_block);
+ }
+
+ /* y resolution = [0x85] * 100 + [0xa2] , 0xa2 is not always defined */
+ dev->inquiry_dor_y_res = 100 * get_inquiry_dor_max_y_res(inquiry_block);
+ if (dev->inquiry_len > 0xa2)
+ {
+ dev->inquiry_dor_y_res+= get_inquiry_dor_y_resolution_residue(inquiry_block);
+ }
+
+ if (dev->inquiry_dor) /* DOR mode available ? */
+ {
+ /* if DOR resolutions are not defined, use double of standard resolution */
+
+ if (dev->inquiry_dor_optical_res == 0)
+ {
+ dev->inquiry_dor_optical_res = dev->inquiry_optical_res * 2;
+ }
+
+ if (dev->inquiry_dor_x_res == 0)
+ {
+ dev->inquiry_dor_x_res = dev->inquiry_x_res * 2;
+ }
+
+ if (dev->inquiry_dor_y_res == 0)
+ {
+ dev->inquiry_dor_y_res = dev->inquiry_y_res * 2;
+ }
+ }
+
+ dev->inquiry_fb_width = (double)get_inquiry_fb_max_scan_width(inquiry_block) * 0.01;
+ dev->inquiry_fb_length = (double)get_inquiry_fb_max_scan_length(inquiry_block) * 0.01;
+
+ dev->inquiry_uta_width = (double)get_inquiry_uta_max_scan_width(inquiry_block) * 0.01;
+ dev->inquiry_uta_length = (double)get_inquiry_uta_max_scan_length(inquiry_block) * 0.01;
+ dev->inquiry_uta_x_off = (double)get_inquiry_uta_x_original_point(inquiry_block) * 0.01;
+ dev->inquiry_uta_y_off = (double)get_inquiry_uta_y_original_point(inquiry_block) * 0.01;
+
+ dev->inquiry_dor_width = (double)get_inquiry_dor_max_scan_width(inquiry_block) * 0.01;
+ dev->inquiry_dor_length = (double)get_inquiry_dor_max_scan_length(inquiry_block) * 0.01;
+ dev->inquiry_dor_x_off = (double)get_inquiry_dor_x_original_point(inquiry_block) * 0.01;
+ dev->inquiry_dor_y_off = (double)get_inquiry_dor_y_original_point(inquiry_block) * 0.01;
+
+ dev->inquiry_max_warmup_time = get_inquiry_lamp_warmup_maximum_time(inquiry_block) * 2;
+
+ dev->inquiry_wdb_len = get_inquiry_wdb_length(inquiry_block);
+
+ /* it is not guaranteed that the following values are in the inquiry return block */
+
+ /* 0x9a */
+ if (dev->inquiry_len<=0x9a)
+ {
+ return;
+ }
+ dev->inquiry_max_calib_lines = get_inquiry_max_calibration_data_lines(inquiry_block);
+
+ /* 0x9b */
+ if (dev->inquiry_len<=0x9b)
+ {
+ return;
+ }
+ dev->inquiry_fb_uta_color_arrangement = get_inquiry_fb_uta_line_arrangement_mode(inquiry_block);
+
+ /* 0x9c */
+ if (dev->inquiry_len<=0x9c)
+ {
+ return;
+ }
+ dev->inquiry_adf_color_arrangement = get_inquiry_adf_line_arrangement_mode(inquiry_block);
+
+ /* 0x9d */
+ if (dev->inquiry_len<=0x9d)
+ {
+ return;
+ }
+ dev->inquiry_CCD_line_distance = get_inquiry_CCD_line_distance(inquiry_block);
+
+ return;
+}
+
+
+/* ------------------------------------------------------------ UMAX CALCULATE ANALOG GAMMA ---------------- */
+
+
+static int umax_calculate_analog_gamma(double value)
+{
+ int gamma;
+
+ if (value < 1.0)
+ { value=1.0; }
+
+ if (value > 2.0)
+ { value=2.0; }
+
+ gamma=0; /* select gamma_value from analog_gamma_table */
+ while (value>analog_gamma_table[gamma])
+ {
+ gamma++;
+ }
+
+ if (gamma)
+ {
+ if ((analog_gamma_table[gamma-1] + analog_gamma_table[gamma]) /2 > value)
+ {
+ gamma--;
+ }
+ }
+
+ return(gamma);
+}
+
+/* ------------------------------------------------------------ UMAX OUTPUT IMAGE DATA -------------------- */
+
+static void umax_output_image_data(Umax_Device *dev, FILE *fp, unsigned int data_to_read, int bufnr)
+{
+ if (dev->do_color_ordering == 0) /* pixel ordering */
+ {
+ if ((dev->inquiry_lineart_order) && (dev->colormode == LINEART)) /* lineart with LSB first */
+ {
+ unsigned int i, j;
+ int new, old;
+
+ for (i=0; i<data_to_read; i++)
+ {
+ old = dev->buffer[bufnr][i];
+ new = 0;
+ for (j=0; j<8; j++) /* reverse bit order of 1 byte */
+ {
+ new = (new << 1) + (old & 1);
+ old = old >> 1;
+ }
+ dev->buffer[bufnr][i]=new;
+ }
+ }
+ fwrite(dev->buffer[bufnr], 1, data_to_read, fp);
+ }
+ else /* line ordering */
+ {
+ unsigned char *linesource = dev->buffer[bufnr];
+ unsigned char *pixelsource;
+ int bytes = 1;
+ int lines;
+ int i;
+
+ if (dev->bits_per_pixel_code != 1) /* >24 bpp */
+ {
+ bytes = 2;
+ }
+
+ lines = data_to_read / (dev->width_in_pixels * bytes);
+
+ for(i=0; i<lines; i++)
+ {
+ umax_order_line(dev, linesource);
+ linesource += dev->width_in_pixels * bytes;
+
+ pixelsource = umax_get_pixel_line(dev);
+ if (pixelsource != NULL)
+ {
+ fwrite(pixelsource, bytes, dev->width_in_pixels * 3, fp);
+ }
+ }
+ }
+}
+
+/* ------------------------------------------------------------ UMAX READER PROCESS ------------------------ */
+
+
+static int umax_reader_process(Umax_Device *dev, FILE *fp, unsigned int image_size)
+{
+ int status;
+ int bytes = 1;
+ int queue_filled = 0;
+ unsigned int bufnr_queue = 0;
+ unsigned int bufnr_read = 0;
+ unsigned int data_left_to_read = image_size;
+ unsigned int data_left_to_queue = image_size;
+ unsigned int data_to_read;
+ unsigned int data_to_queue;
+
+ dev->row_bufsize = dev->bufsize;
+ umax_trim_rowbufsize(dev); /* trim bufsize */
+
+ if (dev->bits_per_pixel_code != 1) /* >24 bpp */
+ {
+ bytes = 2;
+ }
+
+ DBG(DBG_read,"reading %u bytes in blocks of %u bytes\n", image_size, dev->row_bufsize);
+
+ if (dev->pixelbuffer != NULL) /* buffer exists? */
+ {
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+ }
+
+ if (dev->do_color_ordering != 0)
+ {
+ DBG(DBG_info,"ordering from line-order to pixel-order\n");
+
+ dev->pixelline_max = 3 * dev->CCD_distance * dev->scale_y + 2;
+
+ dev->pixelbuffer = malloc(dev->width_in_pixels * dev->pixelline_max * bytes * 3);
+
+ if (dev->pixelbuffer == NULL) /* NO MEMORY */
+ {
+ return -1;
+ }
+ }
+
+ WAIT_SCANNER;
+
+ do
+ {
+ if (data_left_to_queue)
+ {
+ data_to_queue = (data_left_to_queue < dev->row_bufsize) ? data_left_to_queue : dev->row_bufsize;
+
+ /* umax_get_data_buffer_status(dev); */
+
+ status = umax_queue_read_image_data_req(dev, data_to_queue, bufnr_queue);
+
+ if (status == 0) /* no error but nothing queued */
+ {
+ continue;
+ }
+
+ if (status == -1) /* error */
+ {
+ DBG(DBG_error,"ERROR: umax_reader_process: unable to queue read image data request!\n");
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+ return(-1);
+ }
+
+ data_left_to_queue -= data_to_queue;
+ DBG(DBG_read, "umax_reader_process: read image data queued for buffer[%d] \n", bufnr_queue);
+
+ bufnr_queue++;
+ if (bufnr_queue >= dev->scsi_maxqueue)
+ {
+ bufnr_queue = 0;
+ queue_filled = 1; /* ok, we can start to read the queued buffers - if not already started */
+ }
+
+ if (!data_left_to_queue)
+ {
+ queue_filled = 1; /* ok, we can start to read the queued buffer(s) - all read requests are send */
+ }
+ }
+
+ if (queue_filled) /* queue filled, ok we can read data */
+ {
+ status = umax_wait_queued_image_data(dev, bufnr_read);
+
+ if (status == -1)
+ {
+ DBG(DBG_error,"ERROR: umax_reader_process: unable to get image data from scanner!\n");
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+ return(-1);
+ }
+
+ data_to_read = dev->length_read[bufnr_read]; /* number of bytes in buffer */
+ umax_output_image_data(dev, fp, data_to_read, bufnr_read);
+
+ data_left_to_read -= data_to_read;
+ DBG(DBG_read, "umax_reader_process: buffer of %d bytes read; %d bytes to go\n", data_to_read, data_left_to_read);
+
+ /* if we did not get all requested data increase data_left_to_queue so that we get all needed data */
+ if (dev->length_read[bufnr_read] != dev->length_queued[bufnr_read])
+ {
+ data_left_to_queue += dev->length_queued[bufnr_read] - dev->length_read[bufnr_read];
+ }
+
+ bufnr_read++;
+ if (bufnr_read >= dev->scsi_maxqueue)
+ {
+ bufnr_read = 0;
+ }
+ }
+ } while (data_left_to_read);
+
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------ UMAX INITIALIZE VALUES --------------------- */
+
+
+static void umax_initialize_values(Umax_Device *dev) /* called each time before setting scan-values */
+{ /* Initialize dev structure */
+ DBG(DBG_proc,"initialize_values\n");
+
+ dev->three_pass = 0; /* 1 if threepas_mode only */
+ dev->row_len = -1;
+ dev->max_value = 255; /* maximum value */
+
+ dev->wdb_len = 0;
+ dev->width_in_pixels = 0; /* scan width in pixels */
+ dev->length_in_pixels = 0; /* scan length in pixels */
+ dev->scanwidth = 0; /* width in inch at x_coordinate_base dpi */
+ dev->scanlength = 0; /* length in inch at y_coordinate_base dpi */
+ dev->x_resolution = 0;
+ dev->y_resolution = 0;
+ dev->upper_left_x = 0; /* at 1200pt/inch */
+ dev->upper_left_y = 0; /* at 1200pt/inch */
+ dev->bytes_per_color = 0; /* bytes for each color */
+
+ dev->bits_per_pixel = 8; /* number of bits per pixel */
+ dev->bits_per_pixel_code = 1; /* 1 = 8/24 bpp, 2 = 9/27 bpp, 4 = 10/30 bpp */
+ dev->gamma_input_bits_code = 1; /* 8 = 12/36 bpp, 16 = 14/42 bpp, 32 = 16/48 bpp */
+ dev->set_auto = 0; /* 0 or 1 */
+ dev->preview = 0; /* 1 for preview */
+ dev->quality = 0; /* quality calibration */
+ dev->warmup = 0; /* warmup-bit */
+ dev->fix_focus_position = 0; /* fix focus position */
+ dev->lens_cal_in_doc_pos = 0; /* lens calibration in document position */
+ dev->disable_pre_focus = 0; /* disable pre focus */
+ dev->holder_focus_pos_0mm = 0; /* 0.6mm <-> 0.0mm holder focus position */
+ dev->manual_focus = 0; /* automatic <-> manual focus */
+ dev->colormode = 0; /* LINEART, HALFTONE, GRAYSCALE or RGB */
+ dev->adf = 0; /* 1 if adf shall be used */
+ dev->uta = 0; /* 1 if uta shall be used */
+ dev->module = WD_module_flatbed;
+ dev->cbhs_range = WD_CBHS_255;
+ dev->dor = 0;
+ dev->halftone = WD_halftone_8x8_1;
+ dev->reverse = 0;
+ dev->reverse_multi = 0;
+ dev->calibration = 0;
+
+ dev->exposure_time_calibration_r = 0; /* use this for calibration */
+ dev->exposure_time_calibration_g = 0; /* use this for calibration */
+ dev->exposure_time_calibration_b = 0; /* use this for calibration */
+ dev->exposure_time_scan_r = 0; /* use this for scan */
+ dev->exposure_time_scan_g = 0; /* use this for scan */
+ dev->exposure_time_scan_b = 0; /* use this for scan */
+
+ dev->c_density = WD_lamp_c_density_auto; /* calibration lamp density */
+ dev->s_density = WD_lamp_s_density_auto; /* next scan lamp density */
+
+ dev->threshold = 128; /* threshold for lineart mode */
+ dev->brightness = 128; /* brightness for halftone mode */
+ dev->contrast = 128; /* contrast for halftone mode */
+ dev->highlight_r = 255; /* highlight gray/red */
+ dev->highlight_g = 255; /* highlight green */
+ dev->highlight_b = 255; /* highlight blue */
+ dev->shadow_r = 0; /* shadow gray/red */
+ dev->shadow_g = 0; /* shadow green */
+ dev->shadow_b = 0; /* shadow blue */
+
+ dev->digital_gamma_r = WD_gamma_normal;
+ dev->digital_gamma_g = WD_gamma_normal;
+ dev->digital_gamma_b = WD_gamma_normal;
+
+ dev->analog_gamma_r = 0; /* analog gamma for red and gray to 1.0 */
+ dev->analog_gamma_g = 0; /* analog gamma for green to 1.0 */
+ dev->analog_gamma_b = 0; /* analog gamma for blue to 1.0 */
+
+
+ dev->pixelline_ready[0] = 0; /* reset all values for color ordering */
+ dev->pixelline_ready[1] = 0;
+ dev->pixelline_ready[2] = 0;
+ dev->pixelline_next[0] = 0;
+ dev->pixelline_next[1] = 0;
+ dev->pixelline_next[2] = 0;
+ dev->pixelline_del[0] = 1;
+ dev->pixelline_del[1] = 1;
+ dev->pixelline_del[2] = 1;
+ dev->pixelline_optic[0] = 1;
+ dev->pixelline_optic[1] = 1;
+ dev->pixelline_optic[2] = 1;
+ dev->pixelline_max = 0;
+ dev->pixelline_opt_res = 0;
+ dev->pixelline_read = 0;
+ dev->pixelline_written = 0;
+ dev->CCD_distance = 0;
+
+ dev->calib_lines = 0; /* request calibration lines */
+ dev->do_calibration = 0; /* no calibration by driver */
+ dev->do_color_ordering = 0; /* no line- to pixel-mode ordering */
+
+ dev->button0_pressed = 0; /* reset button 0 pressed flag */
+ dev->button1_pressed = 0; /* reset button 1 pressed flag */
+ dev->button2_pressed = 0; /* reset button 2 pressed flag */
+}
+
+
+/* ------------------------------------------------------------ UMAX INIT ---------------------------------- */
+
+
+static void umax_init(Umax_Device *dev) /* umax_init is called once while driver-initialization */
+{
+ DBG(DBG_proc,"init\n");
+
+ dev->devicename = NULL;
+ dev->pixelbuffer = NULL;
+
+ /* config file or predefined settings */
+ if (dev->connection_type == SANE_UMAX_SCSI)
+ {
+ dev->request_scsi_maxqueue = umax_scsi_maxqueue;
+ }
+ else /* SANE_UMAX_USB, USB does not support command queueing */
+ {
+ DBG(DBG_info2, "setting request_scsi_maxqueue = 1 for USB connection\n");
+ dev->request_scsi_maxqueue = 1;
+ }
+
+ dev->request_preview_lines = umax_preview_lines;
+ dev->request_scan_lines = umax_scan_lines;
+ dev->handle_bad_sense_error = umax_handle_bad_sense_error;
+ dev->execute_request_sense = umax_execute_request_sense;
+ dev->scsi_buffer_size_min = umax_scsi_buffer_size_min;
+ dev->scsi_buffer_size_max = umax_scsi_buffer_size_max;
+ dev->force_preview_bit_rgb = umax_force_preview_bit_rgb;
+ dev->slow = umax_slow;
+ dev->smear = umax_smear;
+ dev->calibration_area = umax_calibration_area;
+ dev->calibration_width_offset = umax_calibration_width_offset;
+ dev->calibration_width_offset_batch = umax_calibration_width_offset_batch;
+ dev->calibration_bytespp = umax_calibration_bytespp;
+ dev->exposure_time_rgb_bind = umax_exposure_time_rgb_bind;
+ dev->invert_shading_data = umax_invert_shading_data;
+ dev->lamp_control_available = umax_lamp_control_available;
+ dev->gamma_lsb_padded = umax_gamma_lsb_padded;
+
+ DBG(DBG_info, "request_scsi_maxqueue = %d\n", dev->request_scsi_maxqueue);
+ DBG(DBG_info, "request_preview_lines = %d\n", dev->request_preview_lines);
+ DBG(DBG_info, "request_scan_lines = %d\n", dev->request_scan_lines);
+ DBG(DBG_info, "handle_bad_sense_error = %d\n", dev->handle_bad_sense_error);
+ DBG(DBG_info, "execute_request_sense = %d\n", dev->execute_request_sense);
+ DBG(DBG_info, "scsi_buffer_size_min = %d\n", dev->scsi_buffer_size_min);
+ DBG(DBG_info, "scsi_buffer_size_max = %d\n", dev->scsi_buffer_size_max);
+ DBG(DBG_info, "force_preview_bit_rgb = %d\n", dev->force_preview_bit_rgb);
+ DBG(DBG_info, "slow = %d\n", dev->slow);
+ DBG(DBG_info, "smear = %d\n", dev->smear);
+ DBG(DBG_info, "calibration_area = %d\n", dev->calibration_area);
+ DBG(DBG_info, "calibration_width_offset = %d\n", dev->calibration_width_offset);
+ DBG(DBG_info, "calibration_width_offset_batch = %d\n", dev->calibration_width_offset_batch);
+ DBG(DBG_info, "calibration_bytespp = %d\n", dev->calibration_bytespp);
+ DBG(DBG_info, "exposure_time_rgb_bind = %d\n", dev->exposure_time_rgb_bind);
+ DBG(DBG_info, "invert_shading_data = %d\n", dev->invert_shading_data);
+ DBG(DBG_info, "lamp_control_available = %d\n", dev->lamp_control_available);
+
+
+ dev->inquiry_len = 0;
+ dev->inquiry_wdb_len = -1;
+ dev->inquiry_optical_res = -1;
+ dev->inquiry_x_res = -1;
+ dev->inquiry_y_res = -1;
+ dev->inquiry_fb_width = -1;
+ dev->inquiry_fb_length = -1;
+ dev->inquiry_uta_width = -1;
+ dev->inquiry_uta_length = -1;
+ dev->inquiry_dor_width = -1;
+ dev->inquiry_dor_length = -1;
+ dev->inquiry_exposure_adj = 0;
+ dev->inquiry_exposure_time_step_unit = -1; /* exposure time unit in micro sec */
+ dev->inquiry_exposure_time_max = -1; /* exposure time maximum */
+ dev->inquiry_exposure_time_l_min = -1; /* exposure time minimum for lineart */
+ dev->inquiry_exposure_time_l_fb_def = -1; /* exposure time default for lineart flatbed */
+ dev->inquiry_exposure_time_l_uta_def = -1; /* exposure time default for lineart uta */
+ dev->inquiry_exposure_time_h_min = -1; /* exposure time minimum for halftone */
+ dev->inquiry_exposure_time_h_fb_def = -1; /* exposure time default for halftone flatbed */
+ dev->inquiry_exposure_time_h_uta_def = -1; /* exposure time default for halftone uta */
+ dev->inquiry_exposure_time_g_min = -1; /* exposure time minimum for grayscale */
+ dev->inquiry_exposure_time_g_fb_def = -1; /* exposure time default for grayscale flatbed */
+ dev->inquiry_exposure_time_g_uta_def = -1; /* exposure time default for grayscale uta */
+ dev->inquiry_exposure_time_c_min = -1; /* exposure time minimum for color */
+ dev->inquiry_exposure_time_c_fb_def_r = -1; /* exposure time default for color flatbed red */
+ dev->inquiry_exposure_time_c_fb_def_g = -1; /* exposure time default for color flatbed green */
+ dev->inquiry_exposure_time_c_fb_def_b = -1; /* exposure time default for color flatbed blue */
+ dev->inquiry_exposure_time_c_uta_def_r = -1; /* exposure time default for color uta red */
+ dev->inquiry_exposure_time_c_uta_def_g = -1; /* exposure time default for color uta green */
+ dev->inquiry_exposure_time_c_uta_def_b = -1; /* exposure time default for color uta blue */
+ dev->inquiry_max_warmup_time = 0; /* maximum warmup time */
+ dev->inquiry_cbhs = WD_CBHS_255;
+ dev->inquiry_contrast_min = 1; /* minimum value for c */
+ dev->inquiry_contrast_max = 255; /* maximum value for c */
+ dev->inquiry_brightness_min = 1; /* minimum value for b */
+ dev->inquiry_brightness_max = 255; /* maximum value for b */
+ dev->inquiry_threshold_min = 1; /* minimum value for t */
+ dev->inquiry_threshold_max = 255; /* maximum value for t */
+ dev->inquiry_highlight_min = 1; /* minimum value for h */
+ dev->inquiry_highlight_max = 255; /* maximum value for h */
+ dev->inquiry_shadow_min = 0; /* minimum value for s */
+ dev->inquiry_shadow_max = 254; /* maximum value for s */
+ dev->inquiry_quality_ctrl = 0;
+ dev->inquiry_preview = 0;
+ dev->inquiry_lamp_ctrl = 0;
+ dev->inquiry_transavail = 0;
+ dev->inquiry_uta = 0;
+ dev->inquiry_adfmode = 0;
+ dev->inquiry_adf = 0;
+ dev->inquiry_dor = 0;
+ dev->inquiry_reverse = 0;
+ dev->inquiry_reverse_multi = 0;
+ dev->inquiry_analog_gamma = 0;
+ dev->inquiry_gamma_dwload = 0;
+ dev->inquiry_one_pass_color = 0;
+ dev->inquiry_three_pass_color = 0;
+ dev->inquiry_color = 0;
+ dev->inquiry_gray = 0;
+ dev->inquiry_halftone = 0;
+ dev->inquiry_lineart = 0;
+ dev->inquiry_calibration = 1;
+ dev->inquiry_shadow = 0;
+ dev->inquiry_highlight = 0;
+ dev->inquiry_gamma_DCF = -1;
+ dev->inquiry_max_calib_lines = 66; /* most scanners use 66 lines, so lets define it as default */
+
+ dev->common_xy_resolutions = 0;
+
+ dev->x_coordinate_base = 1200; /* these are the 1200pt/inch */
+ dev->y_coordinate_base = 1200; /* these are the 1200pt/inch */
+
+ dev->button0_pressed = 0; /* reset button 0 pressed flag */
+ dev->button1_pressed = 0; /* reset button 1 pressed flag */
+ dev->button2_pressed = 0; /* reset button 2 pressed flag */
+
+ dev->pause_for_color_calibration = 0; /* pause between start_scan and do_calibration in ms */
+ dev->pause_for_gray_calibration = 0; /* pause between start_scan and do_calibration in ms */
+ dev->pause_after_calibration = 0; /* pause between do_calibration and read data in ms */
+ dev->pause_after_reposition = -1; /* pause after repostion scanner in ms, -1 = do not wait */
+ dev->pause_for_moving = 0; /* pause for moving scanhead over full area */
+
+ if (umax_test_little_endian() == SANE_TRUE)
+ {
+ dev->low_byte_first = 1; /* in 2 byte mode send lowbyte first */
+ DBG(DBG_info, "backend runs on little endian machine\n");
+ }
+ else
+ {
+ dev->low_byte_first = 0; /* in 2 byte mode send highbyte first */
+ DBG(DBG_info, "backend runs on big endian machine\n");
+ }
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ DBG(DBG_info,"variable scsi buffer size (usage of sanei_scsi_open_extended)\n");
+#else
+ DBG(DBG_info,"fixed scsi buffer size = %d bytes\n", sanei_scsi_max_request_size);
+#endif
+}
+
+
+/* ------------------------------------------------------------ MAX STRING SIZE ---------------------------- */
+
+
+static size_t max_string_size(SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+
+/* ------------------------------------------------------------ DO CANCEL ---------------------------------- */
+
+
+static SANE_Status do_cancel(Umax_Scanner *scanner)
+{
+ SANE_Pid pid;
+ int status;
+
+ DBG(DBG_sane_proc,"do_cancel\n");
+
+ scanner->scanning = SANE_FALSE;
+
+ if (scanner->reader_pid != -1)
+ {
+ DBG(DBG_sane_info,"killing reader_process\n");
+
+ sanei_thread_kill(scanner->reader_pid);
+ pid = sanei_thread_waitpid(scanner->reader_pid, &status);
+
+ if (pid == -1)
+ {
+ DBG(DBG_sane_info, "do_cancel: sanei_thread_waitpid failed, already terminated ? (%s)\n", strerror(errno));
+ }
+ else
+ {
+ DBG(DBG_sane_info, "do_cancel: reader_process terminated with status: %s\n", sane_strstatus(status));
+ }
+
+ scanner->reader_pid = -1;
+
+ if (scanner->device->pixelbuffer != NULL) /* pixelbuffer exists? */
+ {
+ free(scanner->device->pixelbuffer); /* free pixelbuffer */
+ scanner->device->pixelbuffer = NULL;
+ }
+ }
+
+ sanei_scsi_req_flush_all(); /* flush SCSI queue, when we do not do this then sanei_scsi crashes next time */
+
+ if (scanner->device->sfd != -1) /* make sure we have a working filedescriptor */
+ {
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ DBG(DBG_sane_info,"closing scannerdevice filedescriptor\n");
+ umax_scsi_close(scanner->device);
+ }
+
+ scanner->device->three_pass_color = 1; /* reset color in color scanning */
+
+ return SANE_STATUS_CANCELLED;
+}
+
+
+/* ------------------------------------------------------------ ATTACH SCANNER ----------------------------- */
+
+
+static SANE_Status attach_scanner(const char *devicename, Umax_Device **devp, int connection_type)
+{
+ Umax_Device *dev;
+ int i;
+
+ DBG(DBG_sane_proc,"attach_scanner: %s, connection_type %d\n", devicename, connection_type);
+
+ for (dev = first_dev; dev; dev = dev->next) /* search is scanner already is listed in devicelist */
+ {
+ if (strcmp(dev->sane.name, devicename) == 0) /* scanner is already listed */
+ {
+ if (devp)
+ {
+ *devp = dev; /* return pointer to device */
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* scanner has not been attached yet */
+
+ dev = malloc( sizeof(*dev) );
+ if (!dev)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(dev, '\0', sizeof(Umax_Device)); /* clear structure */
+
+ /* If connection type is not known (==0) then try to open the device as an USB device. */
+ /* If it fails, try the SCSI method. */
+
+#ifdef UMAX_ENABLE_USB
+ dev->connection_type = connection_type; /* 0 = unknown, 1=scsi, 2=usb */
+
+ if (dev->connection_type != SANE_UMAX_SCSI)
+ {
+ dev->bufsize = 16384; /* 16KB */
+ DBG(DBG_info, "attach_scanner: opening usb device %s\n", devicename);
+
+ if (sanei_umaxusb_open(devicename, &dev->sfd, sense_handler, dev) == SANE_STATUS_GOOD)
+ {
+ dev->connection_type = SANE_UMAX_USB;
+ }
+ else /* opening usb device failed */
+ {
+ if (dev->connection_type == SANE_UMAX_USB) /* we know it is not a scsi device: error */
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: opening usb device %s failed\n", devicename);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(DBG_info, "attach_scanner: failed to open %s as usb device\n", devicename);
+ }
+ }
+#else
+ dev->connection_type = SANE_UMAX_SCSI;
+#endif
+
+ if (dev->connection_type != SANE_UMAX_USB) /* not an USB device, then try as SCSI */
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ dev->bufsize = 16384; /* 16KB */
+ DBG(DBG_info, "attach_scanner: opening scsi device %s\n", devicename);
+
+ if (sanei_scsi_open_extended(devicename, &dev->sfd, sense_handler, dev, (int *) &dev->bufsize) != 0)
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: opening scsi device %s failed\n", devicename);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->bufsize < 4096) /* < 4KB */
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: sanei_scsi_open_extended returned too small scsi buffer\n");
+ umax_scsi_close(dev);
+ free(dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(DBG_info, "attach_scanner: sanei_scsi_open_extended returned scsi buffer size = %d\n", dev->bufsize);
+#else
+ dev->bufsize = sanei_scsi_max_request_size;
+
+ if (sanei_scsi_open(devicename, dev, sense_handler, dev) != 0)
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: opening scsi device %s failed\n", devicename);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ dev->connection_type = SANE_UMAX_SCSI; /* set connection type (may have been unknown == 0) */
+ }
+
+ DBG(DBG_info, "attach_scanner: allocating SCSI buffer[0]\n");
+ dev->buffer[0] = malloc(dev->bufsize); /* allocate buffer */
+
+ for (i=1; i<SANE_UMAX_SCSI_MAXQUEUE; i++)
+ {
+ dev->buffer[i] = NULL;
+ }
+
+ if (!dev->buffer[0]) /* malloc failed */
+ {
+ DBG(DBG_error, "ERROR: attach scanner: could not allocate buffer[0]\n");
+ umax_scsi_close(dev);
+ free(dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->scsi_maxqueue = 1; /* only one buffer outside the reader process */
+
+ umax_init(dev); /* preset values in structure dev */
+ umax_initialize_values(dev); /* reset values */
+
+ dev->devicename = strdup(devicename);
+
+ if (umax_identify_scanner(dev) != 0)
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: scanner-identification failed\n");
+ umax_scsi_close(dev);
+ free(dev->buffer[0]);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->slow == -1) /* option is not predefined in umax.conf and not by backend */
+ {
+ dev->slow = 0;
+ }
+
+ if (dev->smear == -1) /* option is not predefined in umax.conf and not by backend */
+ {
+ dev->smear = 0;
+ }
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ dev->invert_shading_data = 0;
+ }
+
+ if (dev->gamma_lsb_padded == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ dev->gamma_lsb_padded = 0;
+ }
+
+ umax_get_inquiry_values(dev);
+ umax_print_inquiry(dev);
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"==================== end of inquiry ====================\n");
+ DBG(DBG_inquiry,"\n");
+
+ umax_scsi_close(dev);
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.model = dev->product;
+ dev->sane.type = "flatbed scanner";
+
+ if (strcmp(dev->sane.model,"PSD ") == 0)
+ {
+ dev->sane.type = "page scanner";
+ }
+
+ dev->x_range.min = SANE_FIX(0);
+ dev->x_range.quant = SANE_FIX(0);
+ dev->x_range.max = SANE_FIX(dev->inquiry_fb_width * MM_PER_INCH);
+
+ dev->y_range.min = SANE_FIX(0);
+ dev->y_range.quant = SANE_FIX(0);
+ dev->y_range.max = SANE_FIX(dev->inquiry_fb_length * MM_PER_INCH);
+
+#if UMAX_RESOLUTION_PERCENT_STEP
+ dev->x_dpi_range.min = SANE_FIX(dev->inquiry_optical_res/100);
+ dev->x_dpi_range.quant = SANE_FIX(dev->inquiry_optical_res/100);
+#else
+ dev->x_dpi_range.min = SANE_FIX(5);
+ dev->x_dpi_range.quant = SANE_FIX(5);
+#endif
+ dev->x_dpi_range.max = SANE_FIX(dev->inquiry_x_res);
+
+#if UMAX_RESOLUTION_PERCENT_STEP
+ dev->y_dpi_range.min = SANE_FIX(dev->inquiry_optical_res/100);
+ dev->y_dpi_range.quant = SANE_FIX(dev->inquiry_optical_res/100);
+#else
+ dev->y_dpi_range.min = SANE_FIX(5);
+ dev->y_dpi_range.quant = SANE_FIX(5);
+#endif
+ dev->y_dpi_range.max = SANE_FIX(dev->inquiry_y_res);
+
+ dev->analog_gamma_range.min = SANE_FIX(1.0);
+ dev->analog_gamma_range.quant = SANE_FIX(0.01);
+ dev->analog_gamma_range.max = SANE_FIX(2.0);
+
+ DBG(DBG_info,"x_range.max = %f\n", SANE_UNFIX(dev->x_range.max));
+ DBG(DBG_info,"y_range.max = %f\n", SANE_UNFIX(dev->y_range.max));
+ DBG(DBG_info,"x_dpi_range.max = %f\n", SANE_UNFIX(dev->x_dpi_range.max));
+ DBG(DBG_info,"y_dpi_range.max = %f\n", SANE_UNFIX(dev->y_dpi_range.max));
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ READER PROCESS SIGTERM HANDLER ------------ */
+
+
+static RETSIGTYPE reader_process_sigterm_handler(int signal)
+{
+ DBG(DBG_sane_info,"reader_process: terminated by signal %d\n", signal);
+
+ sanei_scsi_req_flush_all(); /* flush SCSI queue */
+
+ _exit (SANE_STATUS_GOOD);
+}
+
+
+/* ------------------------------------------------------------ READER PROCESS ----------------------------- */
+
+
+static int reader_process(void *data) /* executed as a child process or as thread */
+{
+ Umax_Scanner *scanner = (Umax_Scanner *)data;
+ FILE *fp;
+ int status;
+ unsigned int data_length;
+ struct SIGACTION act;
+ unsigned int i;
+
+ if (sanei_thread_is_forked())
+ {
+ DBG(DBG_sane_proc,"reader_process started (forked)\n");
+ close(scanner->pipe_read_fd);
+ scanner->pipe_read_fd = -1;
+
+ /* sanei_scsi crashes when the scsi commands are not flushed, done in reader_process_sigterm_handler */
+ memset(&act, 0, sizeof (act)); /* define SIGTERM-handler */
+ act.sa_handler = reader_process_sigterm_handler;
+ sigaction(SIGTERM, &act, 0);
+ }
+ else
+ {
+ DBG(DBG_sane_proc,"reader_process started (as thread)\n");
+ }
+
+
+ scanner->device->scsi_maxqueue = scanner->device->request_scsi_maxqueue;
+
+ if (scanner->device->request_scsi_maxqueue > 1)
+ {
+ for (i = 1; i<SANE_UMAX_SCSI_MAXQUEUE; i++)
+ {
+ if (scanner->device->buffer[i])
+ {
+ DBG(DBG_info, "reader_process: freeing SCSI buffer[%d]\n", i);
+ free(scanner->device->buffer[i]); /* free buffer */
+ scanner->device->buffer[i] = NULL;
+ }
+ }
+
+ for (i = 1; i<scanner->device->request_scsi_maxqueue; i++)
+ {
+ DBG(DBG_info, "reader_process: allocating SCSI buffer[%d]\n", i);
+ scanner->device->buffer[i] = malloc(scanner->device->bufsize); /* allocate buffer */
+
+ if (!scanner->device->buffer[i]) /* malloc failed */
+ {
+ DBG(DBG_warning, "WARNING: reader_process: only allocated %d/%d scsi buffers\n", i, scanner->device->request_scsi_maxqueue);
+ scanner->device->scsi_maxqueue = i;
+ break; /* leave for loop */
+ }
+ }
+ }
+
+ data_length = scanner->params.lines * scanner->params.bytes_per_line;
+
+ fp = fdopen(scanner->pipe_write_fd, "w");
+ if (!fp)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(DBG_sane_info,"reader_process: starting to READ data\n");
+
+ status = umax_reader_process(scanner->device, fp, data_length);
+ fclose(fp); /* close write end of pipe */
+
+ for (i = 1; i<scanner->device->request_scsi_maxqueue; i++)
+ {
+ if (scanner->device->buffer[i])
+ {
+ DBG(DBG_info, "reader_process: freeing SCSI buffer[%d]\n", i);
+ free(scanner->device->buffer[i]); /* free buffer */
+ scanner->device->buffer[i] = NULL;
+ }
+ }
+ DBG(DBG_sane_info,"reader_process: finished reading data\n");
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ INIT OPTIONS ------------------------------- */
+
+
+static SANE_Status init_options(Umax_Scanner *scanner)
+{
+ int i;
+ int scan_modes;
+ int bit_depths;
+
+ DBG(DBG_sane_proc,"init_options\n");
+
+ memset(scanner->opt, 0, sizeof (scanner->opt));
+ memset(scanner->val, 0, sizeof (scanner->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ scanner->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; /* empty string */
+ scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ scanner->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ scanner->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ scanner->opt[OPT_MODE_GROUP].desc = "";
+ scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_MODE_GROUP].cap = 0;
+ scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ scan_modes = -1;
+
+ if (scanner->device->inquiry_lineart)
+ {
+ scan_mode_list[++scan_modes] = LINEART_STR;
+ }
+
+ if (scanner->device->inquiry_halftone)
+ {
+ scan_mode_list[++scan_modes]= HALFTONE_STR;
+ }
+
+ if (scanner->device->inquiry_gray)
+ {
+ scan_mode_list[++scan_modes]= GRAY_STR;
+ }
+
+ if (scanner->device->inquiry_color)
+ {
+/*
+ if (scanner->device->inquiry_lineart)
+ { scan_mode_list[++scan_modes]= COLOR_LINEART_STR; }
+
+ if (scanner->device->inquiry_halftone)
+ { scan_mode_list[++scan_modes]= COLOR_HALFTONE_STR; }
+*/
+ scan_mode_list[++scan_modes]= COLOR_STR;
+ }
+
+ scan_mode_list[scan_modes + 1] = 0;
+
+ {
+ int i=0;
+ source_list[i++]= FLB_STR;
+
+ if (scanner->device->inquiry_adfmode)
+ {
+ source_list[i++] = ADF_STR;
+ }
+
+ if (scanner->device->inquiry_transavail)
+ {
+ source_list[i++] = UTA_STR;
+ }
+
+ source_list[i] = 0;
+ }
+
+ /* scan mode */
+ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_MODE].size = max_string_size((SANE_String_Const *) scan_mode_list);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = (SANE_String_Const *) scan_mode_list;
+ scanner->val[OPT_MODE].s = (SANE_Char*)strdup(scan_mode_list[0]);
+
+ /* source */
+ scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_SOURCE].size = max_string_size(source_list);
+ scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
+ scanner->val[OPT_SOURCE].s = (SANE_Char*)strdup(source_list[0]);
+
+ /* x-resolution */
+ scanner->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_X_RESOLUTION].constraint.range = &scanner->device->x_dpi_range;
+ scanner->val[OPT_X_RESOLUTION].w = 100 << SANE_FIXED_SCALE_SHIFT;
+
+ /* y-resolution */
+ scanner->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_Y_RESOLUTION].constraint.range = &scanner->device->y_dpi_range;
+ scanner->val[OPT_Y_RESOLUTION].w = 100 << SANE_FIXED_SCALE_SHIFT;
+ scanner->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+
+ /* bind resolution */
+ scanner->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ scanner->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ scanner->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ scanner->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
+ if (scanner->device->common_xy_resolutions) /* disable bind if x and y res have to be the same */
+ {
+ scanner->opt[OPT_RESOLUTION_BIND].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* negative */
+ scanner->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ scanner->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ scanner->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ scanner->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_reverse_multi == 0)
+ {
+ scanner->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* "Geometry" group: */
+ scanner->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
+ scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_X].constraint.range = &(scanner->device->x_range);
+ scanner->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_X].constraint.range = &(scanner->device->x_range);
+ scanner->val[OPT_BR_X].w = scanner->device->x_range.max;
+
+ /* bottom-right y */
+ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_BR_Y].w = scanner->device->y_range.max;
+
+ /* ------------------------------ */
+
+
+ /* "Enhancement" group: */
+ scanner->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ /* bit depth */
+ bit_depths = 0;
+
+ if (scanner->device->inquiry_GOB & 1)
+ {
+ bit_depth_list[++bit_depths] = 8;
+ }
+
+ if (scanner->device->inquiry_GOB & 2)
+ {
+ bit_depth_list[++bit_depths] = 9;
+ }
+
+ if (scanner->device->inquiry_GOB & 4)
+ {
+ bit_depth_list[++bit_depths] = 10;
+ }
+
+ if (scanner->device->inquiry_GOB & 8)
+ {
+ bit_depth_list[++bit_depths] = 12;
+ }
+
+ if (scanner->device->inquiry_GOB & 16)
+ {
+ bit_depth_list[++bit_depths] = 14;
+ }
+
+ if (scanner->device->inquiry_GOB & 32)
+ {
+ bit_depth_list[++bit_depths] = 16;
+ }
+
+ bit_depth_list[0] = bit_depths;
+
+ scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
+ scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
+ scanner->val[OPT_BIT_DEPTH].w = bit_depth_list[1];
+
+
+ /* quality-calibration */
+ scanner->opt[OPT_QUALITY].name = SANE_NAME_QUALITY_CAL;
+ scanner->opt[OPT_QUALITY].title = SANE_TITLE_QUALITY_CAL;
+ scanner->opt[OPT_QUALITY].desc = SANE_DESC_QUALITY_CAL;
+ scanner->opt[OPT_QUALITY].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_QUALITY].w = SANE_FALSE;
+
+ if ((scanner->device->inquiry_quality_ctrl == 0) || (scanner->device->force_quality_calibration) )
+ {
+ scanner->opt[OPT_QUALITY].cap |= SANE_CAP_INACTIVE;
+ }
+ else /* enable quality calibration when available */
+ {
+ scanner->val[OPT_QUALITY].w = SANE_TRUE;
+ }
+
+
+ /* double optical resolution */
+ scanner->opt[OPT_DOR].name = SANE_NAME_DOR;
+ scanner->opt[OPT_DOR].title = SANE_TITLE_DOR;
+ scanner->opt[OPT_DOR].desc = SANE_DESC_DOR;
+ scanner->opt[OPT_DOR].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_DOR].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_dor == 0)
+ {
+ scanner->opt[OPT_DOR].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* warmup */
+ scanner->opt[OPT_WARMUP].name = SANE_NAME_WARMUP;
+ scanner->opt[OPT_WARMUP].title = SANE_TITLE_WARMUP;
+ scanner->opt[OPT_WARMUP].desc = SANE_DESC_WARMUP;
+ scanner->opt[OPT_WARMUP].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_WARMUP].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_max_warmup_time == 0)
+ {
+ scanner->opt[OPT_WARMUP].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_RGB_BIND].name = SANE_NAME_RGB_BIND;
+ scanner->opt[OPT_RGB_BIND].title = SANE_TITLE_RGB_BIND;
+ scanner->opt[OPT_RGB_BIND].desc = SANE_DESC_RGB_BIND;
+ scanner->opt[OPT_RGB_BIND].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_RGB_BIND].w = SANE_FALSE;
+
+ /* brightness */
+ scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ scanner->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ scanner->val[OPT_CONTRAST].w = 0;
+
+
+ /* threshold */
+ scanner->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_THRESHOLD].constraint.range = &percentage_range_100;
+ scanner->val[OPT_THRESHOLD].w = SANE_FIX(50);
+
+
+ /* ------------------------------ */
+
+
+ /* highlight, white level */
+ scanner->opt[OPT_HIGHLIGHT].name = SANE_NAME_HIGHLIGHT;
+ scanner->opt[OPT_HIGHLIGHT].title = SANE_TITLE_HIGHLIGHT;
+ scanner->opt[OPT_HIGHLIGHT].desc = SANE_DESC_HIGHLIGHT;
+ scanner->opt[OPT_HIGHLIGHT].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT].w = SANE_FIX(100);
+
+ scanner->opt[OPT_HIGHLIGHT_R].name = SANE_NAME_HIGHLIGHT_R;
+ scanner->opt[OPT_HIGHLIGHT_R].title = SANE_TITLE_HIGHLIGHT_R;
+ scanner->opt[OPT_HIGHLIGHT_R].desc = SANE_DESC_HIGHLIGHT_R;
+ scanner->opt[OPT_HIGHLIGHT_R].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT_R].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT_R].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT_R].w = SANE_FIX(100);
+
+ scanner->opt[OPT_HIGHLIGHT_G].name = SANE_NAME_HIGHLIGHT_G;
+ scanner->opt[OPT_HIGHLIGHT_G].title = SANE_TITLE_HIGHLIGHT_G;
+ scanner->opt[OPT_HIGHLIGHT_G].desc = SANE_DESC_HIGHLIGHT_G;
+ scanner->opt[OPT_HIGHLIGHT_G].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT_G].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT_G].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT_G].w = SANE_FIX(100);
+
+ scanner->opt[OPT_HIGHLIGHT_B].name = SANE_NAME_HIGHLIGHT_B;
+ scanner->opt[OPT_HIGHLIGHT_B].title = SANE_TITLE_HIGHLIGHT_B;
+ scanner->opt[OPT_HIGHLIGHT_B].desc = SANE_DESC_HIGHLIGHT_B;
+ scanner->opt[OPT_HIGHLIGHT_B].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT_B].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT_B].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT_B].w = SANE_FIX(100);
+
+
+ /* shadow, black level */
+ scanner->opt[OPT_SHADOW].name = SANE_NAME_SHADOW;
+ scanner->opt[OPT_SHADOW].title = SANE_TITLE_SHADOW;
+ scanner->opt[OPT_SHADOW].desc = SANE_DESC_SHADOW;
+ scanner->opt[OPT_SHADOW].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW].w = 0;
+
+ scanner->opt[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R;
+ scanner->opt[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R;
+ scanner->opt[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R;
+ scanner->opt[OPT_SHADOW_R].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW_R].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW_R].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW_R].w = 0;
+
+ scanner->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G;
+ scanner->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G;
+ scanner->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G;
+ scanner->opt[OPT_SHADOW_G].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW_G].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW_G].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW_G].w = 0;
+
+ scanner->opt[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B;
+ scanner->opt[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B;
+ scanner->opt[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B;
+ scanner->opt[OPT_SHADOW_B].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW_B].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW_B].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW_B].w = 0;
+
+
+
+ /* analog-gamma */
+ scanner->opt[OPT_ANALOG_GAMMA].name = SANE_NAME_ANALOG_GAMMA;
+ scanner->opt[OPT_ANALOG_GAMMA].title = SANE_TITLE_ANALOG_GAMMA;
+ scanner->opt[OPT_ANALOG_GAMMA].desc = SANE_DESC_ANALOG_GAMMA;
+ scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ scanner->opt[OPT_ANALOG_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ scanner->opt[OPT_ANALOG_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ scanner->opt[OPT_ANALOG_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ scanner->opt[OPT_ANALOG_GAMMA_R].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA_R].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA_R].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ scanner->opt[OPT_ANALOG_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ scanner->opt[OPT_ANALOG_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ scanner->opt[OPT_ANALOG_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ scanner->opt[OPT_ANALOG_GAMMA_G].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA_G].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA_G].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ scanner->opt[OPT_ANALOG_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ scanner->opt[OPT_ANALOG_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ scanner->opt[OPT_ANALOG_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ scanner->opt[OPT_ANALOG_GAMMA_B].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA_B].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA_B].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+
+ /* custom-gamma table */
+ scanner->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ scanner->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ scanner->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ scanner->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_CUSTOM_GAMMA].w = SANE_TRUE;
+
+ /* grayscale gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR].wa = scanner->gamma_table[0];
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &scanner->output_range;
+ scanner->opt[OPT_GAMMA_VECTOR].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ scanner->output_range.min = 0;
+
+ if (bit_depth_list[1] == 8)
+ {
+ scanner->output_range.max = 255; /* 8 bits/pixel */
+ }
+ else
+ {
+ scanner->output_range.max = 65535; /* 9-16 bits/pixel */
+ }
+
+ scanner->output_range.quant = 0;
+
+ /* red gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_R].wa = scanner->gamma_table[1];
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ /* green gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_G].wa = scanner->gamma_table[2];
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ /* blue gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_B].wa = scanner->gamma_table[3];
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ /* halftone dimension */
+ scanner->opt[OPT_HALFTONE_DIMENSION].name = SANE_NAME_HALFTONE_DIMENSION;
+ scanner->opt[OPT_HALFTONE_DIMENSION].title = SANE_TITLE_HALFTONE_DIMENSION;
+ scanner->opt[OPT_HALFTONE_DIMENSION].desc = SANE_DESC_HALFTONE_DIMENSION;
+ scanner->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_INT;
+ scanner->opt[OPT_HALFTONE_DIMENSION].unit = SANE_UNIT_PIXEL;
+ scanner->opt[OPT_HALFTONE_DIMENSION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_HALFTONE_DIMENSION].constraint.word_list = pattern_dim_list;
+ scanner->val[OPT_HALFTONE_DIMENSION].w = pattern_dim_list[1];
+
+ /* halftone pattern */
+ scanner->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT;
+ scanner->opt[OPT_HALFTONE_PATTERN].size = 0;
+ scanner->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HALFTONE_PATTERN].constraint.range = &u8_range;
+ scanner->val[OPT_HALFTONE_PATTERN].wa = scanner->halftone_pattern;
+
+
+ /* ------------------------------ */
+
+
+ /* "Advanced" group: */
+ scanner->opt[OPT_ADVANCED_GROUP].title = SANE_I18N("Advanced");
+ scanner->opt[OPT_ADVANCED_GROUP].desc = "";
+ scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ /* ------------------------------ */
+
+
+ /* select exposure time */
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].name = SANE_NAME_SELECT_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].title = SANE_TITLE_SELECT_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].desc = SANE_DESC_SELECT_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_SELECT_EXPOSURE_TIME].w = SANE_FALSE;
+
+ /* select calibration exposure time */
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].name = SANE_UMAX_NAME_SELECT_CALIBRATON_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].title = SANE_UMAX_TITLE_SELECT_CALIBRATION_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].desc = SANE_UMAX_DESC_SELECT_CALIBRATION_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w = SANE_FALSE;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time */
+ scanner->opt[OPT_CAL_EXPOS_TIME].name = SANE_NAME_CAL_EXPOS_TIME;
+ scanner->opt[OPT_CAL_EXPOS_TIME].title = SANE_TITLE_CAL_EXPOS_TIME;
+ scanner->opt[OPT_CAL_EXPOS_TIME].desc = SANE_DESC_CAL_EXPOS_TIME;
+ scanner->opt[OPT_CAL_EXPOS_TIME].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME].w = scanner->device->inquiry_exposure_time_g_fb_def *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time red */
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].name = SANE_NAME_CAL_EXPOS_TIME_R;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].title = SANE_TITLE_CAL_EXPOS_TIME_R;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].desc = SANE_DESC_CAL_EXPOS_TIME_R;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME_R].w = scanner->device->inquiry_exposure_time_c_fb_def_r *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time green */
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].name = SANE_NAME_CAL_EXPOS_TIME_G;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].title = SANE_TITLE_CAL_EXPOS_TIME_G;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].desc = SANE_DESC_CAL_EXPOS_TIME_G;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME_G].w = scanner->device->inquiry_exposure_time_c_fb_def_g *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time blue */
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].name = SANE_NAME_CAL_EXPOS_TIME_B;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].title = SANE_TITLE_CAL_EXPOS_TIME_B;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].desc = SANE_DESC_CAL_EXPOS_TIME_B;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME_B].w = scanner->device->inquiry_exposure_time_c_fb_def_b *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time */
+ scanner->opt[OPT_SCAN_EXPOS_TIME].name = SANE_NAME_SCAN_EXPOS_TIME;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].title = SANE_TITLE_SCAN_EXPOS_TIME;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].desc = SANE_DESC_SCAN_EXPOS_TIME;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME].w = scanner->device->inquiry_exposure_time_g_fb_def *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time red */
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].name = SANE_NAME_SCAN_EXPOS_TIME_R;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].title = SANE_TITLE_SCAN_EXPOS_TIME_R;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].desc = SANE_DESC_SCAN_EXPOS_TIME_R;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME_R].w = scanner->device->inquiry_exposure_time_c_fb_def_r *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time green */
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].name = SANE_NAME_SCAN_EXPOS_TIME_G;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].title = SANE_TITLE_SCAN_EXPOS_TIME_G;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].desc = SANE_DESC_SCAN_EXPOS_TIME_G;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME_G].w = scanner->device->inquiry_exposure_time_c_fb_def_g *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time blue */
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].name = SANE_NAME_SCAN_EXPOS_TIME_B;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].title = SANE_TITLE_SCAN_EXPOS_TIME_B;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].desc = SANE_DESC_SCAN_EXPOS_TIME_B;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME_B].w = scanner->device->inquiry_exposure_time_c_fb_def_b *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_exposure_adj == 0)
+ {
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* ------------------------------ */
+
+
+ /* select calibration lamp density */
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].name = SANE_NAME_SELECT_LAMP_DENSITY;
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].title = SANE_TITLE_SELECT_LAMP_DENSITY;
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].desc = SANE_DESC_SELECT_LAMP_DENSITY;
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_SELECT_LAMP_DENSITY].w = SANE_FALSE;
+
+ /* calibration lamp density */
+ scanner->opt[OPT_CAL_LAMP_DEN].name = SANE_NAME_CAL_LAMP_DEN;
+ scanner->opt[OPT_CAL_LAMP_DEN].title = SANE_TITLE_CAL_LAMP_DEN;
+ scanner->opt[OPT_CAL_LAMP_DEN].desc = SANE_DESC_CAL_LAMP_DEN;
+ scanner->opt[OPT_CAL_LAMP_DEN].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_CAL_LAMP_DEN].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_CAL_LAMP_DEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_LAMP_DEN].constraint.range = &percentage_range_100;
+ scanner->val[OPT_CAL_LAMP_DEN].w = SANE_FIX(50);
+ scanner->opt[OPT_CAL_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+
+ /* scan lamp density */
+ scanner->opt[OPT_SCAN_LAMP_DEN].name = SANE_NAME_SCAN_LAMP_DEN;
+ scanner->opt[OPT_SCAN_LAMP_DEN].title = SANE_TITLE_SCAN_LAMP_DEN;
+ scanner->opt[OPT_SCAN_LAMP_DEN].desc = SANE_DESC_SCAN_LAMP_DEN;
+ scanner->opt[OPT_SCAN_LAMP_DEN].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SCAN_LAMP_DEN].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SCAN_LAMP_DEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_LAMP_DEN].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SCAN_LAMP_DEN].w = SANE_FIX(50);
+ scanner->opt[OPT_SCAN_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_lamp_ctrl == 0)
+ {
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* disable pre focus */
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].name = "disable-pre-focus";
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].title = SANE_I18N("Disable pre focus");
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].desc = SANE_I18N("Do not calibrate focus");
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_DISABLE_PRE_FOCUS].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_manual_focus == 0)
+ {
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* manual pre focus */
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].name = "manual-pre-focus";
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].title = SANE_I18N("Manual pre focus");
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].desc = "";
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_MANUAL_PRE_FOCUS].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_manual_focus == 0)
+ {
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* fix focus position */
+ scanner->opt[OPT_FIX_FOCUS_POSITION].name = "fix-focus-position";
+ scanner->opt[OPT_FIX_FOCUS_POSITION].title = SANE_I18N("Fix focus position");
+ scanner->opt[OPT_FIX_FOCUS_POSITION].desc = "";
+ scanner->opt[OPT_FIX_FOCUS_POSITION].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_FIX_FOCUS_POSITION].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_manual_focus == 0)
+ {
+ scanner->opt[OPT_FIX_FOCUS_POSITION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* lens calibration in doc position */
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].name = "lens-calibration-in-doc-position";
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].title = SANE_I18N("Lens calibration in doc position");
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].desc = SANE_I18N("Calibrate lens focus in document position");
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_LENS_CALIBRATION_DOC_POS].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_lens_cal_in_doc_pos == 0)
+ {
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* 0mm holder focus position */
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].name = "holder-focus-position-0mm";
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].title = SANE_I18N("Holder focus position 0mm");
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].desc = SANE_I18N("Use 0mm holder focus position instead of 0.6mm");
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_HOLDER_FOCUS_POS_0MM].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_sel_uta_lens_cal_pos == 0)
+ {
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* lamp on */
+ scanner->opt[OPT_LAMP_ON].name = "lamp-on";
+ scanner->opt[OPT_LAMP_ON].title = SANE_I18N("Lamp on");
+ scanner->opt[OPT_LAMP_ON].desc = SANE_I18N("Turn on scanner lamp");
+ scanner->opt[OPT_LAMP_ON].type = SANE_TYPE_BUTTON;
+ scanner->opt[OPT_LAMP_ON].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_LAMP_ON].size = 0;
+ scanner->opt[OPT_LAMP_ON].constraint_type = SANE_CONSTRAINT_NONE;
+ scanner->val[OPT_LAMP_ON].w = 0;
+
+ if (!scanner->device->lamp_control_available) /* lamp state can not be controlled by driver */
+ {
+ scanner->opt[OPT_LAMP_ON].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* lamp off */
+ scanner->opt[OPT_LAMP_OFF].name = "lamp-off";
+ scanner->opt[OPT_LAMP_OFF].title = SANE_I18N("Lamp off");
+ scanner->opt[OPT_LAMP_OFF].desc = SANE_I18N("Turn off scanner lamp");
+ scanner->opt[OPT_LAMP_OFF].type = SANE_TYPE_BUTTON;
+ scanner->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_LAMP_OFF].size = 0;
+ scanner->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
+ scanner->val[OPT_LAMP_OFF].w = 0;
+
+ if (!scanner->device->lamp_control_available) /* lamp state can not be controlled by driver */
+ {
+ scanner->opt[OPT_LAMP_OFF].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* lamp off at exit */
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].name = "lamp-off-at-exit";
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].title = SANE_I18N("Lamp off at exit");
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].desc = SANE_I18N("Turn off lamp when program exits");
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_LAMP_OFF_AT_EXIT].w = SANE_FALSE;
+
+ if (!scanner->device->lamp_control_available) /* lamp state can not be controlled by driver */
+ {
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* batch-scan-start */
+ scanner->opt[OPT_BATCH_SCAN_START].name = SANE_NAME_BATCH_SCAN_START;
+ scanner->opt[OPT_BATCH_SCAN_START].title = SANE_TITLE_BATCH_SCAN_START;
+ scanner->opt[OPT_BATCH_SCAN_START].desc = SANE_DESC_BATCH_SCAN_START;
+ scanner->opt[OPT_BATCH_SCAN_START].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_BATCH_SCAN_START].w = SANE_FALSE;
+
+ /* batch-scan-loop */
+ scanner->opt[OPT_BATCH_SCAN_LOOP].name = SANE_NAME_BATCH_SCAN_LOOP;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].title = SANE_TITLE_BATCH_SCAN_LOOP;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].desc = SANE_DESC_BATCH_SCAN_LOOP;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_BATCH_SCAN_LOOP].w = SANE_FALSE;
+
+ /* batch-scan-end */
+ scanner->opt[OPT_BATCH_SCAN_END].name = SANE_NAME_BATCH_SCAN_END;
+ scanner->opt[OPT_BATCH_SCAN_END].title = SANE_TITLE_BATCH_SCAN_END;
+ scanner->opt[OPT_BATCH_SCAN_END].desc = SANE_DESC_BATCH_SCAN_END;
+ scanner->opt[OPT_BATCH_SCAN_END].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_BATCH_SCAN_END].w = SANE_FALSE;
+
+ /* batch-scan-next-y */
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].name = SANE_NAME_BATCH_NEXT_TL_Y;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].title = SANE_TITLE_BATCH_NEXT_TL_Y;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].desc = SANE_DESC_BATCH_NEXT_TL_Y;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_BATCH_NEXT_TL_Y].w = 0xFFFF; /* mark value as not touched */
+
+ if (scanner->device->inquiry_batch_scan == 0)
+ {
+ scanner->opt[OPT_BATCH_SCAN_START].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BATCH_SCAN_END].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ /* calibration mode */
+ scanner->opt[OPT_CALIB_MODE].name = "calibrationmode";
+ scanner->opt[OPT_CALIB_MODE].title = SANE_I18N("Calibration mode");
+ scanner->opt[OPT_CALIB_MODE].desc = SANE_I18N("Define calibration mode");
+ scanner->opt[OPT_CALIB_MODE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_CALIB_MODE].size = max_string_size(calibration_list);
+ scanner->opt[OPT_CALIB_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_CALIB_MODE].constraint.string_list = calibration_list;
+ scanner->val[OPT_CALIB_MODE].s = (SANE_Char*)strdup(calibration_list[0]);
+
+ if (scanner->device->inquiry_calibration == 0)
+ {
+ scanner->opt[OPT_CALIB_MODE].cap |= SANE_CAP_INACTIVE;
+ }
+#endif
+
+ /* preview */
+ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ scanner->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ sane_control_option(scanner, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String *) scan_mode_list[scan_modes], NULL );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ ATTACH ONE SCSI ----------------------------- */
+
+/* callback function for sanei_config_attach_matching_devices(dev_name, attach_one_scsi) */
+static SANE_Status attach_one_scsi(const char *name)
+{
+ attach_scanner(name, 0, SANE_UMAX_SCSI);
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ ATTACH ONE USB ------------------------------ */
+
+/* callback function for sanei_usb_attach_matching_devices(dev_name, attach_one_usb) */
+static SANE_Status attach_one_usb(const char *name)
+{
+ attach_scanner(name, 0, SANE_UMAX_USB);
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ UMAX TEST CONFIGURE OPTION ------------------ */
+
+static SANE_Status umax_test_configure_option(const char *option_str, char *test_name, int *test_value, int test_min, int test_max)
+/* returns with 1 if option was found, 0 if option was not found */
+{
+ const char *value_str;
+ char *end_ptr;
+ int value;
+
+ if (strncmp(option_str, test_name, strlen(test_name)) == 0)
+ {
+ value_str = sanei_config_skip_whitespace(option_str+strlen(test_name));
+
+ errno = 0;
+ value = strtol(value_str, &end_ptr, 10);
+ if (end_ptr == value_str || errno)
+ {
+ DBG(DBG_error, "ERROR: invalid value \"%s\" for option %s in %s\n", value_str, test_name, UMAX_CONFIG_FILE);
+ }
+ else
+ {
+ if (value < test_min)
+ {
+ DBG(DBG_error, "ERROR: value \"%d\" is too small for option %s in %s\n", value, test_name, UMAX_CONFIG_FILE);
+ value = test_min;
+ }
+ else if (value > test_max)
+ {
+ DBG(DBG_error, "ERROR: value \"%d\" is too large for option %s in %s\n", value, test_name, UMAX_CONFIG_FILE);
+ value = test_max;
+ }
+
+ *test_value = value;
+
+ DBG(DBG_info, "option %s = %d\n", test_name, *test_value);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------ SANE INIT ---------------------------------- */
+
+
+SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char config_line[PATH_MAX];
+ const char *option_str;
+ size_t len;
+ FILE *fp;
+
+ /* we have to initialize these global variables here because sane_init can be called several times */
+ num_devices = 0;
+ devlist = NULL;
+ first_dev = NULL;
+ first_handle = NULL;
+
+ DBG_INIT();
+
+ DBG(DBG_sane_init,"sane_init\n");
+ DBG(DBG_error,"This is sane-umax version %d.%d build %d\n", SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+#ifdef UMAX_ENABLE_USB
+ DBG(DBG_error,"compiled with USB support for Astra 2200\n");
+#else
+ DBG(DBG_error,"no USB support for Astra 2200\n");
+#endif
+ DBG(DBG_error,"(C) 1997-2002 by Oliver Rauch\n");
+ DBG(DBG_error,"EMAIL: Oliver.Rauch@rauch-domain.de\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ frontend_authorize_callback = authorize; /* store frontend authorize callback */
+
+ sanei_thread_init(); /* must be called before any other sanei_thread call */
+
+#ifdef UMAX_ENABLE_USB
+ sanei_usb_init();
+ sanei_pv8630_init();
+#endif
+
+ fp = sanei_config_open(UMAX_CONFIG_FILE);
+ if (!fp)
+ {
+ /* no config-file: try /dev/scanner and /dev/usbscanner. */
+ attach_scanner("/dev/scanner", 0, SANE_UMAX_SCSI);
+#ifdef UMAX_ENABLE_USB
+ attach_scanner("/dev/usbscanner", 0, SANE_UMAX_USB);
+#endif
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(DBG_info, "reading configure file %s\n", UMAX_CONFIG_FILE);
+
+ while(sanei_config_read(config_line, sizeof(config_line), fp))
+ {
+ if (config_line[0] == '#')
+ {
+ continue; /* ignore line comments */
+ }
+
+ if (strncmp(config_line, "option", 6) == 0)
+ {
+ option_str = sanei_config_skip_whitespace(config_line+6);
+
+ if (umax_test_configure_option(option_str, "scsi-maxqueue", &umax_scsi_maxqueue, 1, SANE_UMAX_SCSI_MAXQUEUE));
+ else if (umax_test_configure_option(option_str, "scsi-buffer-size-min", &umax_scsi_buffer_size_min, 4096, 1048576));
+ else if (umax_test_configure_option(option_str, "scsi-buffer-size-max", &umax_scsi_buffer_size_max, 4096, 1048576));
+ else if (umax_test_configure_option(option_str, "preview-lines", &umax_preview_lines, 1, 65535));
+ else if (umax_test_configure_option(option_str, "scan-lines", &umax_scan_lines, 1, 65535));
+ else if (umax_test_configure_option(option_str, "handle-bad-sense-error", &umax_handle_bad_sense_error, 0, 3));
+ else if (umax_test_configure_option(option_str, "execute-request-sense", &umax_execute_request_sense, 0, 1));
+ else if (umax_test_configure_option(option_str, "force-preview-bit-rgb", &umax_force_preview_bit_rgb, 0, 1));
+ else if (umax_test_configure_option(option_str, "slow-speed", &umax_slow, -1, 1));
+ else if (umax_test_configure_option(option_str, "care-about-smearing", &umax_smear, -1, 1));
+ else if (umax_test_configure_option(option_str, "calibration-full-ccd", &umax_calibration_area, -1, 1));
+ else if (umax_test_configure_option(option_str, "calibration-width-offset-batch", &umax_calibration_width_offset_batch, -99999, 65535));
+ else if (umax_test_configure_option(option_str, "calibration-width-offset", &umax_calibration_width_offset, -99999, 65535));
+ else if (umax_test_configure_option(option_str, "calibration-bytes-pixel", &umax_calibration_bytespp, -1, 2));
+ else if (umax_test_configure_option(option_str, "exposure-time-rgb-bind", &umax_exposure_time_rgb_bind, -1, 1));
+ else if (umax_test_configure_option(option_str, "invert-shading-data", &umax_invert_shading_data, -1, 1));
+ else if (umax_test_configure_option(option_str, "lamp-control-available", &umax_lamp_control_available, 0, 1));
+ else if (umax_test_configure_option(option_str, "gamma-lsb-padded", &umax_gamma_lsb_padded, -1, 1));
+ else if (umax_test_configure_option(option_str, "connection-type", &umax_connection_type, 1, 2));
+ else
+ {
+ DBG(DBG_error,"ERROR: unknown option \"%s\" in %s\n", option_str, UMAX_CONFIG_FILE);
+ }
+ continue;
+ }
+ else if (strncmp(config_line, "scsi", 4) == 0)
+ {
+ DBG(DBG_info,"sanei_config_attach_matching_devices(%s)\n", config_line);
+ sanei_config_attach_matching_devices(config_line, attach_one_scsi);
+ continue;
+ }
+ else if (strncmp(config_line, "usb", 3) == 0)
+ {
+#ifdef UMAX_ENABLE_USB
+ DBG(DBG_info,"sanei_usb_attach_matching_devices(%s)\n", config_line);
+ sanei_usb_attach_matching_devices(config_line, attach_one_usb);
+#else
+ DBG(DBG_info,"USB not supported, ignoring config line: %s\n", config_line);
+#endif
+ continue;
+ }
+
+ len = strlen(config_line);
+
+ if (!len) /* ignore empty lines */
+ {
+ continue;
+ }
+
+ /* umax_connection_type is set by umax.conf: 1=scsi, 2=usb */
+ attach_scanner(config_line, 0, umax_connection_type); /* try to open as devicename */
+ }
+
+ DBG(DBG_info, "finished reading configure file\n");
+
+ fclose(fp);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE EXIT ---------------------------------- */
+
+
+void sane_exit(void)
+{
+ Umax_Device *dev, *next;
+
+ DBG(DBG_sane_init,"sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free(dev->devicename);
+ free(dev);
+ }
+
+ if (devlist)
+ {
+ free(devlist);
+ }
+}
+
+
+/* ------------------------------------------------------------ SANE GET DEVICES --------------------------- */
+
+
+SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ Umax_Device *dev;
+ int i;
+
+ DBG(DBG_sane_init,"sane_get_devices(local_only = %d)\n", local_only);
+
+ if (devlist)
+ {
+ free(devlist);
+ }
+
+ devlist = malloc((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ {
+ devlist[i++] = &dev->sane;
+ }
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE OPEN ---------------------------------- */
+
+
+SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle *handle)
+{
+ Umax_Device *dev;
+ SANE_Status status;
+ Umax_Scanner *scanner;
+ unsigned int i, j;
+
+ DBG(DBG_sane_init,"sane_open\n");
+
+ if (devicename[0]) /* search for devicename */
+ {
+ DBG(DBG_sane_info,"sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp(dev->sane.name, devicename) == 0)
+ {
+ break; /* device found, exit for loop */
+ }
+ }
+
+ if (!dev) /* no device found */
+ {
+ status = attach_scanner(devicename, &dev, 0 /* connection-type not known */); /* try to open devicename and set dev */
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG(DBG_sane_info,"sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev) /* no device found */
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ scanner = malloc(sizeof (*scanner));
+ if (!scanner)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(scanner, 0, sizeof (*scanner));
+
+ scanner->device = dev;
+
+ if (scanner->device->inquiry_GIB & 32)
+ {
+ scanner->gamma_length = 65536; /* 16 bits input */
+ DBG(DBG_sane_info, "Using 16 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 16)
+ {
+ scanner->gamma_length = 16384; /* 14 bits input */
+ DBG(DBG_sane_info, "Using 14 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 8)
+ {
+ scanner->gamma_length = 4096; /* 12 bits input */
+ DBG(DBG_sane_info, "Using 12 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 4)
+ {
+ scanner->gamma_length = 1024; /* 10 bits input */
+ DBG(DBG_sane_info, "Using 10 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 2)
+ {
+ scanner->gamma_length = 512; /* 9 bits input */
+ DBG(DBG_sane_info, "Using 9 bits for gamma input\n");
+ }
+ else
+ {
+ scanner->gamma_length = 256; /* 8 bits input */
+ DBG(DBG_sane_info, "Using 8 bits for gamma input\n");
+ }
+
+ scanner->output_bytes = 1; /* 8 bits output */
+
+ scanner->gamma_range.min = 0;
+ scanner->gamma_range.max = scanner->gamma_length-1;
+ scanner->gamma_range.quant = 0;
+
+ scanner->gamma_table[0] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+ scanner->gamma_table[1] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+ scanner->gamma_table[2] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+ scanner->gamma_table[3] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+
+ for (j = 0; j < scanner->gamma_length; ++j) /* gamma_table[0] : converts GIB to GOB */
+ {
+ scanner->gamma_table[0][j] = j * scanner->device->max_value / scanner->gamma_length;
+ }
+
+ for (i = 1; i < 4; ++i) /* gamma_table[1,2,3] : doesn't convert anything (GIB->GIB) */
+ {
+ for (j = 0; j < scanner->gamma_length; ++j)
+ {
+ scanner->gamma_table[i][j] = j;
+ }
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_c_min *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->exposure_time_range.quant = scanner->device->inquiry_exposure_time_step_unit;
+ scanner->exposure_time_range.max = scanner->device->inquiry_exposure_time_max *
+ scanner->device->inquiry_exposure_time_step_unit;
+
+ init_options(scanner);
+
+ scanner->next = first_handle; /* insert newly opened handle into list of open handles: */
+ first_handle = scanner;
+
+ *handle = scanner;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE CLOSE --------------------------------- */
+
+
+void sane_close(SANE_Handle handle)
+{
+ Umax_Scanner *prev, *scanner;
+
+ DBG(DBG_sane_init,"sane_close\n");
+
+ if (!first_handle)
+ {
+ DBG(DBG_error, "ERROR: sane_close: no handles opened\n");
+ return;
+ }
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+
+ for (scanner = first_handle; scanner; scanner = scanner->next)
+ {
+ if (scanner == handle)
+ {
+ break;
+ }
+
+ prev = scanner;
+ }
+
+ if (!scanner)
+ {
+ DBG(DBG_error, "ERROR: sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (scanner->scanning) /* stop scan if still scanning */
+ {
+ do_cancel(handle);
+ }
+
+ if (scanner->device->lamp_control_available) /* lamp state can be controlled by driver */
+ {
+ if (scanner->val[OPT_LAMP_OFF_AT_EXIT].w) /* turn off scanner lamp on sane_close */
+ {
+ umax_set_lamp_status(handle, 0 /* lamp off */);
+ }
+ }
+
+ if (prev)
+ {
+ prev->next = scanner->next;
+ }
+ else
+ {
+ first_handle = scanner->next;
+ }
+
+ free(scanner->gamma_table[0]); /* free custom gamma tables */
+ free(scanner->gamma_table[1]);
+ free(scanner->gamma_table[2]);
+ free(scanner->gamma_table[3]);
+
+ free(scanner->device->buffer[0]); /* free buffer allocated by umax_initialize_values */
+ scanner->device->buffer[0] = NULL;
+ scanner->device->bufsize = 0;
+
+ free(scanner); /* free scanner */
+}
+
+
+/* ------------------------------------------------------------ SANE GET OPTION DESCRIPTOR ----------------- */
+
+
+const SANE_Option_Descriptor *sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_option,"sane_get_option_descriptor %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ return 0;
+ }
+
+ return scanner->opt + option;
+}
+
+/* ------------------------------------------------------------ UMAX SET MAX GEOMETRY ---------------------- */
+
+static void umax_set_max_geometry(Umax_Scanner *scanner)
+{
+
+ if (scanner->val[OPT_DOR].w)
+ {
+ scanner->device->x_range.min = SANE_FIX(scanner->device->inquiry_dor_x_off * MM_PER_INCH);
+ scanner->device->x_range.max = SANE_FIX( (scanner->device->inquiry_dor_x_off + scanner->device->inquiry_dor_width) * MM_PER_INCH);
+ scanner->device->y_range.min = SANE_FIX(scanner->device->inquiry_dor_y_off * MM_PER_INCH);
+ scanner->device->y_range.max = SANE_FIX( (scanner->device->inquiry_dor_y_off + scanner->device->inquiry_dor_length) * MM_PER_INCH);
+
+ scanner->device->x_dpi_range.max = SANE_FIX(scanner->device->inquiry_dor_x_res);
+ scanner->device->y_dpi_range.max = SANE_FIX(scanner->device->inquiry_dor_y_res);
+ }
+ else if ( (strcmp(scanner->val[OPT_SOURCE].s, FLB_STR) == 0) || (strcmp(scanner->val[OPT_SOURCE].s, ADF_STR) == 0) )
+ {
+ scanner->device->x_range.min = 0;
+ scanner->device->x_range.max = SANE_FIX(scanner->device->inquiry_fb_width * MM_PER_INCH);
+ scanner->device->y_range.min = 0;
+ scanner->device->y_range.max = SANE_FIX(scanner->device->inquiry_fb_length * MM_PER_INCH);
+
+ scanner->device->x_dpi_range.max = SANE_FIX(scanner->device->inquiry_x_res);
+ scanner->device->y_dpi_range.max = SANE_FIX(scanner->device->inquiry_y_res);
+ }
+ else if (strcmp(scanner->val[OPT_SOURCE].s, UTA_STR) == 0)
+ {
+ scanner->device->x_range.min = SANE_FIX(scanner->device->inquiry_uta_x_off * MM_PER_INCH);
+ scanner->device->x_range.max = SANE_FIX( (scanner->device->inquiry_uta_x_off + scanner->device->inquiry_uta_width) * MM_PER_INCH);
+ scanner->device->y_range.min = SANE_FIX(scanner->device->inquiry_uta_y_off * MM_PER_INCH);
+ scanner->device->y_range.max = SANE_FIX( ( scanner->device->inquiry_uta_y_off + scanner->device->inquiry_uta_length) * MM_PER_INCH);
+
+ scanner->device->x_dpi_range.max = SANE_FIX(scanner->device->inquiry_x_res);
+ scanner->device->y_dpi_range.max = SANE_FIX(scanner->device->inquiry_y_res);
+ }
+
+ DBG(DBG_info,"x_range = [%f .. %f]\n", SANE_UNFIX(scanner->device->x_range.min), SANE_UNFIX(scanner->device->x_range.max));
+ DBG(DBG_info,"y_range = [%f .. %f]\n", SANE_UNFIX(scanner->device->y_range.min), SANE_UNFIX(scanner->device->y_range.max));
+ DBG(DBG_info,"x_dpi_range = [1 .. %f]\n", SANE_UNFIX(scanner->device->x_dpi_range.max));
+ DBG(DBG_info,"y_dpi_range = [1 .. %f]\n", SANE_UNFIX(scanner->device->y_dpi_range.max));
+
+ /* make sure geometry selection is in bounds */
+
+ if ( scanner->val[OPT_TL_X].w < scanner->device->x_range.min)
+ {
+ scanner->val[OPT_TL_X].w = scanner->device->x_range.min;
+ }
+
+ if (scanner->val[OPT_TL_Y].w < scanner->device->y_range.min)
+ {
+ scanner->val[OPT_TL_Y].w = scanner->device->y_range.min;
+ }
+
+ if ( scanner->val[OPT_BR_X].w > scanner->device->x_range.max)
+ {
+ scanner->val[OPT_BR_X].w = scanner->device->x_range.max;
+ }
+
+ if (scanner->val[OPT_BR_Y].w > scanner->device->y_range.max)
+ {
+ scanner->val[OPT_BR_Y].w = scanner->device->y_range.max;
+ }
+}
+
+/* ------------------------------------------------------------ SANE CONTROL OPTION ------------------------ */
+
+
+SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *val, SANE_Int *info)
+{
+ Umax_Scanner *scanner = handle;
+ SANE_Status status;
+ SANE_Word w, cap;
+ SANE_String_Const name;
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (scanner->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = scanner->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ name = scanner->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG(DBG_sane_option,"get %s [#%d]\n", name, option);
+
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION_BIND:
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_BIT_DEPTH:
+ case OPT_NEGATIVE:
+ case OPT_BATCH_SCAN_START:
+ case OPT_BATCH_SCAN_LOOP:
+ case OPT_BATCH_SCAN_END:
+ case OPT_BATCH_NEXT_TL_Y:
+ case OPT_QUALITY:
+ case OPT_DOR:
+ case OPT_WARMUP:
+ case OPT_RGB_BIND:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_HALFTONE_DIMENSION:
+ case OPT_SELECT_EXPOSURE_TIME:
+ case OPT_SELECT_CAL_EXPOSURE_TIME:
+ case OPT_CAL_EXPOS_TIME:
+ case OPT_CAL_EXPOS_TIME_R:
+ case OPT_CAL_EXPOS_TIME_G:
+ case OPT_CAL_EXPOS_TIME_B:
+ case OPT_SCAN_EXPOS_TIME:
+ case OPT_SCAN_EXPOS_TIME_R:
+ case OPT_SCAN_EXPOS_TIME_G:
+ case OPT_SCAN_EXPOS_TIME_B:
+ case OPT_CAL_LAMP_DEN:
+ case OPT_SCAN_LAMP_DEN:
+ case OPT_DISABLE_PRE_FOCUS:
+ case OPT_MANUAL_PRE_FOCUS:
+ case OPT_FIX_FOCUS_POSITION:
+ case OPT_LENS_CALIBRATION_DOC_POS:
+ case OPT_HOLDER_FOCUS_POS_0MM:
+ case OPT_LAMP_OFF_AT_EXIT:
+ case OPT_SELECT_LAMP_DENSITY:
+ *(SANE_Word *) val = scanner->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_HALFTONE_PATTERN:
+ memcpy (val, scanner->val[option].wa, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_SOURCE:
+ /* fall through */
+ case OPT_MODE:
+ /* fall through */
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ case OPT_CALIB_MODE:
+ /* fall through */
+#endif
+ strcpy (val, scanner->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ switch (scanner->opt[option].type)
+ {
+ case SANE_TYPE_INT:
+ DBG(DBG_sane_option, "set %s [#%d] to %d\n", name, option, *(SANE_Word *) val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ DBG(DBG_sane_option, "set %s [#%d] to %f\n", name, option, SANE_UNFIX(*(SANE_Word *) val));
+ break;
+
+ case SANE_TYPE_STRING:
+ DBG(DBG_sane_option, "set %s [#%d] to %s\n", name, option, (char *) val);
+ break;
+
+ case SANE_TYPE_BOOL:
+ DBG(DBG_sane_option, "set %s [#%d] to %d\n", name, option, *(SANE_Word *) val);
+ break;
+
+ default:
+ DBG(DBG_sane_option, "set %s [#%d]\n", name, option);
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE(cap))
+ {
+ DBG(DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value(scanner->opt+option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_NEGATIVE:
+ case OPT_BATCH_SCAN_START:
+ case OPT_BATCH_SCAN_LOOP:
+ case OPT_BATCH_SCAN_END:
+ case OPT_BATCH_NEXT_TL_Y:
+ case OPT_QUALITY:
+ case OPT_WARMUP:
+ case OPT_PREVIEW:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ case OPT_CAL_EXPOS_TIME:
+ case OPT_CAL_EXPOS_TIME_R:
+ case OPT_CAL_EXPOS_TIME_G:
+ case OPT_CAL_EXPOS_TIME_B:
+ case OPT_SCAN_EXPOS_TIME:
+ case OPT_SCAN_EXPOS_TIME_R:
+ case OPT_SCAN_EXPOS_TIME_G:
+ case OPT_SCAN_EXPOS_TIME_B:
+ case OPT_CAL_LAMP_DEN:
+ case OPT_SCAN_LAMP_DEN:
+ case OPT_DISABLE_PRE_FOCUS:
+ case OPT_MANUAL_PRE_FOCUS:
+ case OPT_FIX_FOCUS_POSITION:
+ case OPT_LENS_CALIBRATION_DOC_POS:
+ case OPT_HOLDER_FOCUS_POS_0MM:
+ case OPT_LAMP_OFF_AT_EXIT:
+ scanner->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DOR:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val; /* update valure for umax_set_max_geometry */
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ DBG(DBG_info,"sane_control_option: set DOR = %d\n", scanner->val[option].w);
+ umax_set_max_geometry(scanner);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_BIT_DEPTH:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ scanner->output_range.min = 0;
+ scanner->output_range.quant = 0;
+
+ if (scanner->val[option].w == 8) /* 8 bit mode */
+ {
+ scanner->output_bytes = 1; /* 1 bytes output */
+ scanner->output_range.max = 255;
+ }
+ else /* > 8 bit mode */
+ {
+ scanner->output_bytes = 2; /* 2 bytes output */
+
+ if (scanner->device->gamma_lsb_padded) /* e.g. astra 1220s need lsb padded data */
+ {
+ scanner->output_range.max = (int) pow(2, scanner->val[option].w) - 1;
+ }
+ else
+ {
+ scanner->output_range.max = 65535; /* define maxval for msb padded data */
+ }
+ }
+
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_RGB_BIND:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ umax_set_rgb_bind(scanner);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_RESOLUTION_BIND:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if (scanner->val[option].w == SANE_FALSE)
+ { /* don't bind */
+ scanner->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ }
+ else
+ { /* bind */
+ scanner->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SELECT_EXPOSURE_TIME:
+ case OPT_SELECT_CAL_EXPOSURE_TIME:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_FALSE)
+ {
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else /* exposure time selection active */
+ {
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].cap &= ~SANE_CAP_INACTIVE;
+
+ if ( (strcmp(scanner->val[OPT_MODE].s, COLOR_STR) != 0) ||
+ (scanner->val[OPT_RGB_BIND].w == SANE_TRUE) ||
+ (scanner->device->exposure_time_rgb_bind) ) /* RGB bind */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* no RGB bind */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SELECT_LAMP_DENSITY:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if (scanner->val[option].w == SANE_FALSE)
+ {
+ scanner->opt[OPT_CAL_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->opt[OPT_CAL_LAMP_DEN].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_LAMP_DEN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ /* side-effect-free word-array options: */
+ case OPT_HALFTONE_PATTERN:
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (scanner->val[option].wa, val, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* single string-option with side-effect: */
+ case OPT_SOURCE:
+ {
+ if (scanner->val[option].s)
+ {
+ free(scanner->val[option].s);
+ }
+ scanner->val[option].s = (SANE_Char *) strdup(val); /* update string for umax_set_max_geometry */
+
+ DBG(DBG_info,"sane_control_option: set SOURCE = %s\n", (SANE_Char *) val);
+ umax_set_max_geometry(scanner);
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ break;
+
+ /* side-effect-free single-string options: */
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ case OPT_CALIB_MODE:
+ /* fall through */
+#endif
+ {
+ if (scanner->val[option].s)
+ {
+ free (scanner->val[option].s);
+ }
+ scanner->val[option].s = (SANE_Char*)strdup(val);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* options with side-effects: */
+
+ case OPT_CUSTOM_GAMMA:
+ {
+ w = *(SANE_Word *) val;
+ if (w == scanner->val[OPT_CUSTOM_GAMMA].w) { return SANE_STATUS_GOOD; }
+
+ scanner->val[OPT_CUSTOM_GAMMA].w = w;
+ if (w) /* use custom_gamma_table */
+ {
+ const char *mode = scanner->val[OPT_MODE].s;
+
+ if ( (strcmp(mode, LINEART_STR) == 0) ||
+ (strcmp(mode, HALFTONE_STR) == 0) ||
+ (strcmp(mode, GRAY_STR) == 0) )
+ { scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; }
+ else if (strcmp(mode, COLOR_STR) == 0)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else /* don't use custom_gamma_table */
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_MODE:
+ {
+ int halftoning;
+
+ if (scanner->val[option].s)
+ {
+ free (scanner->val[option].s);
+ }
+
+ scanner->val[option].s = (SANE_Char*)strdup(val);
+
+ if (info)
+ {
+ *info |=SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ scanner->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_RGB_BIND].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+
+ halftoning = (strcmp(val, HALFTONE_STR) == 0 || strcmp(val, COLOR_HALFTONE_STR) == 0);
+
+ if (halftoning || strcmp(val, LINEART_STR) == 0 || strcmp(val, COLOR_LINEART_STR) == 0)
+ { /* one bit modes */
+ if (scanner->device->inquiry_reverse)
+ {
+ scanner->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (halftoning)
+ { /* halftoning modes */
+ scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+/* disable halftone pattern download options */
+#if 0
+ scanner->opt[OPT_HALFTONE_DIMENSION].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->val[OPT_HALFTONE_DIMENSION].w)
+ {
+ scanner->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+#endif
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_h_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ else
+ { /* lineart modes */
+ scanner->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_l_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ }
+ else
+ { /* multi-bit modes(gray or color) */
+ scanner->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_reverse_multi)
+ {
+ scanner->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_gamma_dwload)
+ {
+ scanner->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+ }
+
+ if (scanner->device->inquiry_analog_gamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (strcmp(val, COLOR_STR) == 0)
+ {
+ if ( (scanner->device->inquiry_analog_gamma) ||
+ (scanner->device->inquiry_highlight) ||
+ (scanner->device->inquiry_shadow) ||
+ (scanner->device->inquiry_exposure_adj) )
+ {
+ scanner->opt[OPT_RGB_BIND].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_c_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ else /* grayscale */
+ {
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_g_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ }
+
+ umax_set_rgb_bind(scanner);
+
+ if (scanner->val[OPT_CUSTOM_GAMMA].w)
+ {
+ if (strcmp(val, GRAY_STR) == 0)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp(val, COLOR_STR) == 0)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_HALFTONE_DIMENSION: /* halftone pattern dimension affects halftone pattern option: */
+ {
+ unsigned dim = *(SANE_Word *) val;
+
+ scanner->val[option].w = dim;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ if (dim > 0)
+ {
+ scanner->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HALFTONE_PATTERN].size = dim * sizeof (SANE_Word);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_LAMP_ON:
+ {
+ if (!umax_set_lamp_status(handle, 1 /* lamp on */))
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ case OPT_LAMP_OFF:
+ {
+ if (!umax_set_lamp_status(handle, 0 /* lamp off */))
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+ } /* else */
+ return SANE_STATUS_INVAL;
+}
+
+
+/* ------------------------------------------------------------ SANE GET PARAMETERS ------------------------ */
+
+
+SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ Umax_Scanner *scanner = handle;
+ const char *mode;
+
+ DBG(DBG_sane_info,"sane_get_parameters\n");
+
+ if (!scanner->scanning)
+ { /* not scanning, so lets use recent values */
+ double width, length, x_dpi, y_dpi;
+
+ memset(&scanner->params, 0, sizeof (scanner->params));
+
+ width = SANE_UNFIX(scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w);
+ length = SANE_UNFIX(scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w);
+ x_dpi = SANE_UNFIX(scanner->val[OPT_X_RESOLUTION].w);
+ y_dpi = SANE_UNFIX(scanner->val[OPT_Y_RESOLUTION].w);
+
+ if ( (scanner->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) || (scanner->val[OPT_PREVIEW].w == SANE_TRUE) )
+ {
+ y_dpi = x_dpi;
+ }
+
+ if (x_dpi > 0.0 && y_dpi > 0.0 && width > 0.0 && length > 0.0)
+ {
+ double x_dots_per_mm = x_dpi / MM_PER_INCH;
+ double y_dots_per_mm = y_dpi / MM_PER_INCH;
+
+ scanner->params.pixels_per_line = width * x_dots_per_mm;
+ scanner->params.lines = length * y_dots_per_mm;
+ }
+ }
+
+ mode = scanner->val[OPT_MODE].s;
+
+ if (strcmp(mode, LINEART_STR) == 0 || strcmp(mode, HALFTONE_STR) == 0)
+ {
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.bytes_per_line = (scanner->params.pixels_per_line + 7) / 8;
+ scanner->params.depth = 1;
+ }
+ else if (strcmp(mode, GRAY_STR) == 0)
+ {
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line * scanner->output_bytes;
+ scanner->params.depth = 8 * scanner->output_bytes;
+ }
+ else if (strcmp(mode, COLOR_LINEART_STR) == 0 || strcmp(mode, COLOR_HALFTONE_STR) == 0 )
+ {
+ if (scanner->device->inquiry_one_pass_color)
+ {
+ scanner->device->three_pass = 0;
+ scanner->params.format = SANE_FRAME_RGB;
+ scanner->params.bytes_per_line = 3 * scanner->params.pixels_per_line;
+ scanner->params.depth = 8;
+ }
+ else /* three pass color */
+ {
+ scanner->device->three_pass = 1;
+ scanner->params.format = SANE_FRAME_RED + scanner->device->three_pass_color - 1;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line;
+ scanner->params.depth = 8;
+ }
+ }
+ else /* RGB */
+ {
+ if (scanner->device->inquiry_one_pass_color)
+ {
+ scanner->device->three_pass = 0;
+ scanner->params.format = SANE_FRAME_RGB;
+ scanner->params.bytes_per_line = 3 * scanner->params.pixels_per_line * scanner->output_bytes;
+ scanner->params.depth = 8 * scanner->output_bytes;
+ }
+ else /* three pass color */
+ {
+ scanner->device->three_pass = 1;
+ scanner->params.format = SANE_FRAME_RED + scanner->device->three_pass_color - 1;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line * scanner->output_bytes;
+ scanner->params.depth = 8 * scanner->output_bytes;
+ }
+ }
+
+ scanner->params.last_frame = (scanner->params.format != SANE_FRAME_RED && scanner->params.format != SANE_FRAME_GREEN);
+
+ if (params)
+ {
+ *params = scanner->params;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE START --------------------------------- */
+
+
+SANE_Status sane_start(SANE_Handle handle)
+{
+ Umax_Scanner *scanner = handle;
+ const char *mode;
+ double xbasedots, ybasedots;
+ const char *scan_source;
+ int pause;
+ int status;
+ int fds[2];
+
+ DBG(DBG_sane_init,"sane_start\n");
+
+ /* Initialize reader_pid to invalid so a subsequent error and following call
+ * to do_cancel() won't trip over it. */
+ scanner->reader_pid = -1;
+
+ mode = scanner->val[OPT_MODE].s;
+
+ if (scanner->device->sfd == -1) /* first call, don`t run this routine again on multi frame or multi image scan */
+ {
+ umax_initialize_values(scanner->device); /* reset values */
+
+ scanner->device->three_pass_color = 1;
+
+ /* test for adf and uta */
+ scan_source = scanner->val[OPT_SOURCE].s;
+
+ if (strcmp(scan_source, UTA_STR) == 0)
+ {
+ if ( (scanner->device->inquiry_uta != 0) && (scanner->device->inquiry_transavail != 0) )
+ {
+ scanner->device->uta = 1;
+ }
+ else
+ {
+ DBG(DBG_error,"ERROR: Transparency Adapter not available\n");
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else /* Test if ADF is selected */
+ {
+ scanner->device->uta = 0;
+
+ if (strcmp(scan_source, ADF_STR) == 0)
+ {
+ if ( (scanner->device->inquiry_adf) && (scanner->device->inquiry_adfmode) )
+ {
+ scanner->device->adf = 1;
+ }
+ else
+ {
+ DBG(DBG_error,"ERROR: Automatic Document Feeder not available\n");
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ {
+ scanner->device->adf = 0;
+ }
+ }
+
+ if (scanner->device->inquiry_GIB & 32) /* 16 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 32;
+ DBG(DBG_sane_info, "Using 16 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 16) /* 14 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 16;
+ DBG(DBG_sane_info, "Using 14 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 8) /* 12 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 8;
+ DBG(DBG_sane_info, "Using 12 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 4) /* 10 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 4;
+ DBG(DBG_sane_info, "Using 10 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 2) /* 9 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 2;
+ DBG(DBG_sane_info, "Using 9 bits for gamma input\n");
+ }
+ else /* 8 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 1;
+ DBG(DBG_sane_info, "Using 8 bits for gamma input\n");
+ }
+
+ if (scanner->val[OPT_BIT_DEPTH].w == 16) /* 16 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 16;
+ scanner->device->bits_per_pixel_code = 32;
+ scanner->device->max_value = 65535;
+ DBG(DBG_sane_info,"Using 16 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 14) /* 14 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 14;
+ scanner->device->bits_per_pixel_code = 16;
+ scanner->device->max_value = 16383;
+ DBG(DBG_sane_info,"Using 14 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 12) /* 12 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 12;
+ scanner->device->bits_per_pixel_code = 8;
+ scanner->device->max_value = 4095;
+ DBG(DBG_sane_info,"Using 12 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 10) /* 10 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 10;
+ scanner->device->bits_per_pixel_code = 4;
+ scanner->device->max_value = 1023;
+ DBG(DBG_sane_info,"Using 10 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 9) /* 9 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 9;
+ scanner->device->bits_per_pixel_code = 2;
+ scanner->device->max_value = 511;
+ DBG(DBG_sane_info,"Using 9 bits for output\n");
+ }
+ else /* 8 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 8;
+ scanner->device->bits_per_pixel_code = 1;
+ scanner->device->max_value = 255;
+ DBG(DBG_sane_info,"Using 8 bits for output\n");
+ }
+
+ scanner->device->reverse = scanner->device->reverse_multi = scanner->val[OPT_NEGATIVE].w;
+
+ scanner->device->threshold = P_100_TO_255(scanner->val[OPT_THRESHOLD].w);
+ scanner->device->brightness = P_200_TO_255(scanner->val[OPT_BRIGHTNESS].w);
+ scanner->device->contrast = P_200_TO_255(scanner->val[OPT_CONTRAST].w);
+
+ scanner->device->batch_scan = ( scanner->val[OPT_BATCH_SCAN_START].w ||
+ scanner->val[OPT_BATCH_SCAN_LOOP].w ||
+ scanner->val[OPT_BATCH_SCAN_END].w );
+ scanner->device->batch_end = scanner->val[OPT_BATCH_SCAN_END].w;
+ scanner->device->batch_next_tl_y = SANE_UNFIX(scanner->val[OPT_BATCH_NEXT_TL_Y].w) * scanner->device->y_coordinate_base / MM_PER_INCH;
+
+ if (scanner->val[OPT_BATCH_NEXT_TL_Y].w == 0xFFFF) /* option not set: use br_y => scanhead stops at end of batch area */
+ {
+ scanner->device->batch_next_tl_y = SANE_UNFIX(scanner->val[OPT_BR_Y].w) * scanner->device->y_coordinate_base / MM_PER_INCH;
+ }
+
+ if ((scanner->device->batch_scan) && !scanner->val[OPT_BATCH_SCAN_START].w)
+ {
+ scanner->device->calibration = 9; /* no calibration - otherwise the scanhead will go into calibration position */
+ }
+ else
+ {
+ scanner->device->calibration = 0; /* calibration defined by image type */
+ }
+
+ scanner->device->quality = scanner->val[OPT_QUALITY].w;
+ scanner->device->dor = scanner->val[OPT_DOR].w;
+ scanner->device->preview = scanner->val[OPT_PREVIEW].w;
+ scanner->device->warmup = scanner->val[OPT_WARMUP].w;
+
+ scanner->device->fix_focus_position = scanner->val[OPT_FIX_FOCUS_POSITION].w;
+ scanner->device->lens_cal_in_doc_pos = scanner->val[OPT_LENS_CALIBRATION_DOC_POS].w;
+ scanner->device->disable_pre_focus = scanner->val[OPT_DISABLE_PRE_FOCUS].w;
+ scanner->device->holder_focus_pos_0mm = scanner->val[OPT_HOLDER_FOCUS_POS_0MM].w;
+ scanner->device->manual_focus = scanner->val[OPT_MANUAL_PRE_FOCUS].w;
+
+ scanner->device->analog_gamma_r =
+ scanner->device->analog_gamma_g =
+ scanner->device->analog_gamma_b = umax_calculate_analog_gamma(SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA].w));
+
+ scanner->device->highlight_r =
+ scanner->device->highlight_g =
+ scanner->device->highlight_b = P_100_TO_255(scanner->val[OPT_HIGHLIGHT].w);
+
+ scanner->device->shadow_r =
+ scanner->device->shadow_g =
+ scanner->device->shadow_b = P_100_TO_255(scanner->val[OPT_SHADOW].w);
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* separate calibration exposure time */
+ {
+ scanner->device->exposure_time_calibration_r =
+ scanner->device->exposure_time_calibration_g =
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_CAL_EXPOS_TIME].w;
+ }
+ else /* same exposure times for calibration as for scanning */
+ {
+ scanner->device->exposure_time_calibration_r =
+ scanner->device->exposure_time_calibration_g =
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_SCAN_EXPOS_TIME].w;
+ }
+
+ scanner->device->exposure_time_scan_r =
+ scanner->device->exposure_time_scan_g =
+ scanner->device->exposure_time_scan_b = scanner->val[OPT_SCAN_EXPOS_TIME].w;
+ }
+
+ if (scanner->val[OPT_SELECT_LAMP_DENSITY].w == SANE_TRUE)
+ {
+ scanner->device->c_density = P_100_TO_254(scanner->val[OPT_CAL_LAMP_DEN].w);
+ scanner->device->s_density = P_100_TO_254(scanner->val[OPT_SCAN_LAMP_DEN].w);
+ }
+
+ if (strcmp(mode, LINEART_STR) == 0)
+ {
+ scanner->device->colormode = LINEART;
+ }
+ else if (strcmp(mode, HALFTONE_STR) == 0)
+ {
+ scanner->device->colormode = HALFTONE;
+ }
+ else if (strcmp(mode, GRAY_STR) == 0)
+ {
+ scanner->device->colormode = GRAYSCALE;
+ }
+ else if (strcmp(mode, COLOR_LINEART_STR) == 0)
+ {
+ scanner->device->colormode = RGB_LINEART;
+ }
+ else if (strcmp(mode, COLOR_HALFTONE_STR) == 0)
+ {
+ scanner->device->colormode = RGB_HALFTONE;
+ }
+ else if (strcmp(mode, COLOR_STR) == 0)
+ {
+ scanner->device->colormode = RGB;
+ if (scanner->val[OPT_RGB_BIND].w == SANE_FALSE)
+ {
+ scanner->device->analog_gamma_r =
+ umax_calculate_analog_gamma( SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA_R].w) );
+ scanner->device->analog_gamma_g =
+ umax_calculate_analog_gamma( SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA_G].w) );
+ scanner->device->analog_gamma_b =
+ umax_calculate_analog_gamma( SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA_B].w) );
+
+ scanner->device->highlight_r = P_100_TO_255(scanner->val[OPT_HIGHLIGHT_R].w);
+ scanner->device->highlight_g = P_100_TO_255(scanner->val[OPT_HIGHLIGHT_G].w);
+ scanner->device->highlight_b = P_100_TO_255(scanner->val[OPT_HIGHLIGHT_B].w);
+
+ scanner->device->shadow_r = P_100_TO_255(scanner->val[OPT_SHADOW_R].w);
+ scanner->device->shadow_g = P_100_TO_255(scanner->val[OPT_SHADOW_G].w);
+ scanner->device->shadow_b = P_100_TO_255(scanner->val[OPT_SHADOW_B].w);
+
+ if ((scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE) && (!scanner->device->exposure_time_rgb_bind))
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* separate calibration exposure time */
+ {
+ scanner->device->exposure_time_calibration_r = scanner->val[OPT_CAL_EXPOS_TIME_R].w;
+ scanner->device->exposure_time_calibration_g = scanner->val[OPT_CAL_EXPOS_TIME_G].w;
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_CAL_EXPOS_TIME_B].w;
+ }
+ else /* same exposure times for calibration as for scanning */
+ {
+ scanner->device->exposure_time_calibration_r = scanner->val[OPT_SCAN_EXPOS_TIME_R].w;
+ scanner->device->exposure_time_calibration_g = scanner->val[OPT_SCAN_EXPOS_TIME_G].w;
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_SCAN_EXPOS_TIME_B].w;
+ }
+
+ scanner->device->exposure_time_scan_r = scanner->val[OPT_SCAN_EXPOS_TIME_R].w;
+ scanner->device->exposure_time_scan_g = scanner->val[OPT_SCAN_EXPOS_TIME_G].w;
+ scanner->device->exposure_time_scan_b = scanner->val[OPT_SCAN_EXPOS_TIME_B].w;
+ }
+ }
+ }
+
+ if (scanner->device->force_preview_bit_rgb != 0) /* in RGB-mode set preview bit, eg. for UMAX S6E */
+ {
+ if (scanner->device->colormode == RGB)
+ {
+ DBG(DBG_sane_info,"setting preview bit = 1 (option force-preview-bit-rgb)\n");
+ scanner->device->preview = SANE_TRUE;
+ }
+ }
+
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_0000) == 0)
+ {
+ scanner->device->calibration = 0;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1111) == 0)
+ {
+ scanner->device->calibration = 15;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1110) == 0)
+ {
+ scanner->device->calibration = 14;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1101) == 0)
+ {
+ scanner->device->calibration = 13;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1010) == 0)
+ {
+ scanner->device->calibration = 10;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1001) == 0)
+ {
+ scanner->device->calibration = 9;
+ }
+#endif
+
+ /* get and set geometric values for scanning */
+ scanner->device->x_resolution = SANE_UNFIX(scanner->val[OPT_X_RESOLUTION].w);
+ scanner->device->y_resolution = SANE_UNFIX(scanner->val[OPT_Y_RESOLUTION].w);
+
+ if ( (scanner->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) || (scanner->val[OPT_PREVIEW].w == SANE_TRUE) )
+ {
+ scanner->device->y_resolution = scanner->device->x_resolution;
+ }
+
+ xbasedots = scanner->device->x_coordinate_base / MM_PER_INCH;
+ ybasedots = scanner->device->y_coordinate_base / MM_PER_INCH;
+
+#if 0
+ scanner->device->upper_left_x = ((int) (SANE_UNFIX(scanner->val[OPT_TL_X].w) * xbasedots)) & 65534;
+ scanner->device->upper_left_y = ((int) (SANE_UNFIX(scanner->val[OPT_TL_Y].w) * ybasedots)) & 65534;
+
+ scanner->device->scanwidth = ((int)((SANE_UNFIX(scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w)) * xbasedots)) & 65534;
+ scanner->device->scanlength = ((int)((SANE_UNFIX(scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w)) * ybasedots)) & 65534;
+#endif
+
+ scanner->device->upper_left_x = (int) (SANE_UNFIX(scanner->val[OPT_TL_X].w) * xbasedots);
+ scanner->device->upper_left_y = (int) (SANE_UNFIX(scanner->val[OPT_TL_Y].w) * ybasedots);
+
+ scanner->device->scanwidth = (int)((SANE_UNFIX(scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w)) * xbasedots);
+ scanner->device->scanlength = (int)((SANE_UNFIX(scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w)) * ybasedots);
+
+
+ if (umax_check_values(scanner->device) != 0)
+ {
+ DBG(DBG_error,"ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ /* The scanner defines a x-origin-offset for DOR mode, this offset is used for the */
+ /* x range in this backend, so the frontend/user knows the correct positions related to */
+ /* scanner's surface. But the scanner wants x values from origin 0 instead */
+ /* of the x-origin defined by the scanner`s inquiry */
+ if (scanner->device->dor != 0) /* dor mode active */
+ {
+ DBG(DBG_info,"substracting DOR x-origin-offset from upper left x\n");
+ scanner->device->upper_left_x -= scanner->device->inquiry_dor_x_off * scanner->device->x_coordinate_base; /* correct DOR x-origin */
+
+ if (scanner->device->upper_left_x < 0) /* rounding errors may create a negative value */
+ {
+ scanner->device->upper_left_x = 0; /* but negative values are not allowed */
+ }
+ }
+
+ scanner->params.bytes_per_line = scanner->device->row_len;
+ scanner->params.pixels_per_line = scanner->device->width_in_pixels;
+ scanner->params.lines = scanner->device->length_in_pixels;
+
+
+ /* set exposure times */
+ if ( scanner->device->inquiry_exposure_adj )
+ {
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_r, &scanner->device->exposure_time_calibration_r);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_g, &scanner->device->exposure_time_calibration_g);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_b, &scanner->device->exposure_time_calibration_b);
+
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_r, &scanner->device->exposure_time_scan_r);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_g, &scanner->device->exposure_time_scan_g);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_b, &scanner->device->exposure_time_scan_b);
+ }
+ else
+ {
+ scanner->device->exposure_time_calibration_r = scanner->device->exposure_time_calibration_g = scanner->device->exposure_time_calibration_b =
+ scanner->device->exposure_time_scan_r = scanner->device->exposure_time_scan_g = scanner->device->exposure_time_scan_b = 0;
+ }
+
+
+ scanner->scanning = SANE_TRUE;
+ sane_get_parameters(scanner, 0);
+
+ DBG(DBG_sane_info,"x_resolution (dpi) = %u\n", scanner->device->x_resolution);
+ DBG(DBG_sane_info,"y_resolution (dpi) = %u\n", scanner->device->y_resolution);
+ DBG(DBG_sane_info,"x_coordinate_base (dpi) = %u\n", scanner->device->x_coordinate_base);
+ DBG(DBG_sane_info,"y_coordinate_base (dpi) = %u\n", scanner->device->y_coordinate_base);
+ DBG(DBG_sane_info,"upper_left_x (xbase) = %d\n", scanner->device->upper_left_x);
+ DBG(DBG_sane_info,"upper_left_y (ybase) = %d\n", scanner->device->upper_left_y);
+ DBG(DBG_sane_info,"scanwidth (xbase) = %u\n", scanner->device->scanwidth);
+ DBG(DBG_sane_info,"scanlength (ybase) = %u\n", scanner->device->scanlength);
+ DBG(DBG_sane_info,"width in pixels = %u\n", scanner->device->width_in_pixels);
+ DBG(DBG_sane_info,"length in pixels = %u\n", scanner->device->length_in_pixels);
+ DBG(DBG_sane_info,"bits per pixel/color = %u\n", scanner->device->bits_per_pixel);
+ DBG(DBG_sane_info,"bytes per line = %d\n", scanner->params.bytes_per_line);
+ DBG(DBG_sane_info,"pixels_per_line = %d\n", scanner->params.pixels_per_line);
+ DBG(DBG_sane_info,"lines = %d\n", scanner->params.lines);
+ DBG(DBG_sane_info,"negative = %d\n", scanner->device->reverse);
+ DBG(DBG_sane_info,"threshold (lineart) = %d\n", scanner->device->threshold);
+ DBG(DBG_sane_info,"brightness (halftone) = %d\n", scanner->device->brightness);
+ DBG(DBG_sane_info,"contrast (halftone) = %d\n", scanner->device->contrast);
+
+ DBG(DBG_sane_info,"analog_gamma = %d %d %d\n",
+ scanner->device->analog_gamma_r,
+ scanner->device->analog_gamma_g,
+ scanner->device->analog_gamma_b);
+ DBG(DBG_sane_info,"highlight = %d %d %d\n",
+ scanner->device->highlight_r,
+ scanner->device->highlight_g,
+ scanner->device->highlight_b);
+ DBG(DBG_sane_info,"shadow = %d %d %d\n",
+ scanner->device->shadow_r,
+ scanner->device->shadow_g,
+ scanner->device->shadow_b);
+ DBG(DBG_sane_info,"calibrat. exposure time = %d %d %d\n",
+ scanner->device->exposure_time_calibration_r,
+ scanner->device->exposure_time_calibration_g,
+ scanner->device->exposure_time_calibration_b);
+ DBG(DBG_sane_info,"scan exposure time = %d %d %d\n",
+ scanner->device->exposure_time_scan_r,
+ scanner->device->exposure_time_scan_g,
+ scanner->device->exposure_time_scan_b);
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ DBG(DBG_sane_info,"calibration = %s\n", scanner->val[OPT_CALIB_MODE].s);
+#endif
+ DBG(DBG_sane_info,"calibration mode number = %d\n", scanner->device->calibration);
+
+ DBG(DBG_sane_info,"batch scan = %d\n", scanner->device->batch_scan);
+ DBG(DBG_sane_info,"batch end = %d\n", scanner->device->batch_end);
+ DBG(DBG_sane_info,"batch next top left y = %d\n", scanner->device->batch_next_tl_y);
+ DBG(DBG_sane_info,"quality calibration = %d\n", scanner->device->quality);
+ DBG(DBG_sane_info,"warm up = %d\n", scanner->device->warmup);
+ DBG(DBG_sane_info,"fast preview function = %d\n", scanner->device->preview);
+ DBG(DBG_sane_info,"DOR = %d\n", scanner->device->dor);
+ DBG(DBG_sane_info,"ADF = %d\n", scanner->device->adf);
+ DBG(DBG_sane_info,"manual focus = %d\n", scanner->device->manual_focus);
+ DBG(DBG_sane_info,"fix focus position = %d\n", scanner->device->fix_focus_position);
+ DBG(DBG_sane_info,"disable pre focus = %d\n", scanner->device->disable_pre_focus);
+ DBG(DBG_sane_info,"lens cal in doc pos = %d\n", scanner->device->lens_cal_in_doc_pos);
+ DBG(DBG_sane_info,"holder focus pos 0mm = %d\n", scanner->device->holder_focus_pos_0mm);
+
+ if (scanner->val[OPT_PREVIEW].w) /* preview mode */
+ {
+ scanner->device->lines_max = scanner->device->request_preview_lines;
+ }
+ else /* scan mode */
+ {
+ scanner->device->lines_max = scanner->device->request_scan_lines;
+ }
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ {
+ unsigned int scsi_bufsize = 0;
+
+ scsi_bufsize = scanner->device->width_in_pixels * scanner->device->lines_max;
+
+ if (scsi_bufsize == 0) /* no scsi buffer size, take scanner buffer size */
+ {
+ scsi_bufsize = scanner->device->inquiry_vidmem;
+ }
+
+ if (scsi_bufsize < scanner->device->scsi_buffer_size_min) /* make sure buffer has at least minimum size */
+ {
+ scsi_bufsize = scanner->device->scsi_buffer_size_min;
+ }
+ else if (scsi_bufsize > scanner->device->scsi_buffer_size_max) /* make sure buffer does not exceed maximum size */
+ {
+ scsi_bufsize = scanner->device->scsi_buffer_size_max;
+ }
+
+ if (umax_scsi_open_extended(scanner->device->sane.name, scanner->device, sense_handler,
+ scanner->device, (int *) &scsi_bufsize) != 0)
+ {
+ DBG(DBG_error, "ERROR: sane_start: open failed\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scsi_bufsize < scanner->device->scsi_buffer_size_min) /* minimum size must be available */
+ {
+ DBG(DBG_error, "ERROR: sane_start: umax_scsi_open_extended returned too small scsi buffer\n");
+ umax_scsi_close((scanner->device));
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG(DBG_info, "sane_start: umax_scsi_open_extended returned scsi buffer size = %d\n", scsi_bufsize);
+
+ if (scsi_bufsize < scanner->device->width_in_pixels) /* print warning when buffer is smaller than one scanline */
+ {
+ DBG(DBG_warning, "WARNING: sane_start: scsi buffer is smaller than one scanline\n");
+ }
+
+ if (scsi_bufsize != scanner->device->bufsize)
+ {
+ DBG(DBG_info, "sane_start: buffer size has changed, reallocating buffer\n");
+
+ if (scanner->device->buffer[0])
+ {
+ DBG(DBG_info, "sane_start: freeing SCSI buffer[0]\n");
+ free(scanner->device->buffer[0]); /* free buffer */
+ }
+
+ scanner->device->bufsize = scsi_bufsize;
+
+ DBG(DBG_info, "sane_start: allocating SCSI buffer[0]\n");
+ scanner->device->buffer[0] = malloc(scanner->device->bufsize); /* allocate buffer */
+
+ if (!scanner->device->buffer[0]) /* malloc failed */
+ {
+ DBG(DBG_error, "ERROR: sane_start: could not allocate buffer[0]\n");
+ umax_scsi_close(scanner->device);
+ scanner->device->bufsize = 0;
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+#else
+ if ( umax_scsi_open(scanner->device->sane.name, scanner->device, sense_handler,
+ scanner->device) != SANE_STATUS_GOOD )
+ {
+ scanner->scanning = SANE_FALSE;
+ DBG(DBG_error, "ERROR: sane_start: open of %s failed:\n", scanner->device->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* there is no need to reallocate the buffer because the size is fixed */
+#endif
+
+ /* grab scanner */
+ if (umax_grab_scanner(scanner->device))
+ {
+ umax_scsi_close(scanner->device);
+ scanner->scanning = SANE_FALSE;
+ DBG(DBG_warning,"WARNING: unable to reserve scanner: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+/* halftone pattern download is not ready in this version */
+#if 0
+ /* send halftonepattern */
+ if ( (strcmp(mode, HALFTONE_STR) == 0) || (strcmp(mode, COLOR_HALFTONE_STR) == 0) )
+ {
+ umax_send_halftone_pattern(scanner->device, (char *) &(scanner->halftone_pattern[0]),
+ scanner->val[OPT_HALFTONE_DIMENSION].w );
+ scanner->device->halftone = WD_halftone_download;
+ } /* end of send halftonepattern */
+#endif
+
+ } /* ------------ end of first call -------------- */
+
+
+ /* send gammacurves */
+ if (scanner->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ {
+ if (strcmp(mode, COLOR_STR) == 0)
+ {
+ if (scanner->device->three_pass == 0) /* one pass color scan */
+ {
+ unsigned int i, dest, color, value;
+ char *gamma;
+
+ gamma = malloc( (size_t) (3 * scanner->gamma_length * scanner->output_bytes) );
+ if (gamma == NULL)
+ {
+ DBG(DBG_warning,"WARNING: not able to allocate memory for gamma table, gamma ignored !!!\n");
+ }
+ else
+ {
+ dest=0;
+ for(color=1; color <= 3; color++)
+ {
+ for(i=0; i < scanner->gamma_length; i++)
+ {
+ value = scanner->gamma_table[color][i];
+ if (scanner->output_bytes == 2)
+ {
+ gamma[dest++] = scanner->gamma_table[0][value] / 256;
+ }
+ gamma[dest++] = (scanner->gamma_table[0][value] & 255);
+ }
+ }
+
+ DBG(DBG_sane_info,"sending 3 * %d bytes of gamma data for RGB\n",
+ scanner->gamma_length * scanner->output_bytes);
+
+ umax_send_gamma_data(scanner->device, &gamma[0], 3);
+ scanner->device->digital_gamma_r =
+ scanner->device->digital_gamma_g =
+ scanner->device->digital_gamma_b = WD_gamma_download;
+ free(gamma);
+ }
+ }
+ else /* three pass color scan */
+ {
+ unsigned int i, dest, color, value;
+ char *gamma;
+
+ gamma = malloc( (size_t) (scanner->gamma_length * scanner->output_bytes) );
+ if (gamma == NULL)
+ {
+ DBG(DBG_warning,"not able to allocate memory for gamma table, gamma ignored !!!\n");
+ }
+ else
+ {
+ dest = 0;
+ color = scanner->device->three_pass_color;
+
+ for(i = 0; i < scanner->gamma_length; i++)
+ {
+ value = scanner->gamma_table[color][i];
+
+ if (scanner->output_bytes == 2)
+ {
+ gamma[dest++] = scanner->gamma_table[0][value] / 256;
+ }
+ gamma[dest++] = (scanner->gamma_table[0][value] & 255);
+ }
+
+ DBG(DBG_sane_info,"sending %d bytes of gamma data for color %d\n",
+ scanner->gamma_length * scanner->output_bytes, color);
+
+ umax_send_gamma_data(scanner->device, &gamma[0], 1);
+ scanner->device->digital_gamma_r =
+ scanner->device->digital_gamma_g =
+ scanner->device->digital_gamma_b = WD_gamma_download;
+ free(gamma);
+ }
+ }
+ }
+ else if (strcmp(mode, GRAY_STR) == 0) /* grayscale scan */
+ {
+ unsigned int i, dest;
+ char *gamma;
+
+ gamma = malloc( (size_t) (scanner->gamma_length * scanner->output_bytes) );
+ if (gamma == NULL)
+ {
+ DBG(DBG_warning,"WARNING: not able to allocate memory for gamma table, gamma ignored !!!\n");
+ }
+ else
+ {
+ dest=0;
+ for(i=0; i < scanner->gamma_length; i++)
+ {
+ if (scanner->output_bytes == 2)
+ {
+ gamma[dest++] = scanner->gamma_table[0][i] / 256;
+ }
+ gamma[dest++] = (scanner->gamma_table[0][i] & 255);
+ }
+
+ DBG(DBG_sane_info,"sending %d bytes of gamma data for gray\n",
+ scanner->gamma_length * scanner->output_bytes);
+
+ umax_send_gamma_data(scanner->device, &gamma[0], 1);
+ scanner->device->digital_gamma_r = WD_gamma_download;
+ free(gamma);
+ }
+ }
+ } /* end of send gammacurves */
+
+ if ( scanner->device->three_pass_color > WD_wid_red) /* three pass scan, not first pass */
+ {
+ umax_reposition_scanner(scanner->device);
+ }
+
+ umax_set_window_param(scanner->device);
+ status = umax_start_scan(scanner->device);
+ if (status) /* errror */
+ {
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ return status;
+ }
+
+ pause = scanner->device->pause_for_color_calibration;
+
+ if (scanner->device->colormode != RGB)
+ {
+ pause = scanner->device->pause_for_gray_calibration;
+ }
+
+ if (pause) /* Astra 2400S needs this pause (7sec in color, 4sec in gray mode) */
+ {
+ DBG(DBG_info2,"pause for calibration %d msec ...\n", pause);
+ usleep(((long) pause) * 1000); /* time in ms */
+ DBG(DBG_info2,"pause done\n");
+ }
+
+ status = umax_do_calibration(scanner->device);
+ if (status) /* errror */
+ {
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ return status;
+ }
+
+ if (scanner->device->pause_after_calibration) /* may be usefull */
+ {
+ DBG(DBG_info2,"pause after calibration %d msec ...\n", scanner->device->pause_after_calibration);
+ usleep(((long) scanner->device->pause_after_calibration) * 1000); /* time in ms */
+ DBG(DBG_info2,"pause done\n");
+ }
+
+
+ if (pipe(fds) < 0)
+ {
+ DBG(DBG_error,"ERROR: could not create pipe\n");
+ scanner->scanning = SANE_FALSE;
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe_read_fd = fds[0];
+ scanner->pipe_write_fd = fds[1];
+
+ /* start reader_process, deponds on OS if fork() or threads are used */
+ scanner->reader_pid = sanei_thread_begin(reader_process, (void *) scanner);
+
+ if (scanner->reader_pid == -1)
+ {
+ DBG(DBG_error, "ERROR: sanei_thread_begin failed (%s)\n", strerror(errno));
+ scanner->scanning = SANE_FALSE;
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_NO_MEM; /* any other reason than no memory possible ? */
+ }
+
+ if (sanei_thread_is_forked())
+ {
+ close(scanner->pipe_write_fd);
+ scanner->pipe_write_fd = -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE READ ---------------------------------- */
+
+
+SANE_Status sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
+{
+ Umax_Scanner *scanner = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read(scanner->pipe_read_fd, buf, max_len);
+
+ DBG(DBG_sane_info, "sane_read: read %ld bytes\n", (long) nread);
+
+ if (!(scanner->scanning)) /* OOPS, not scanning */
+ {
+ return do_cancel(scanner);
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ DBG(DBG_sane_info, "sane_read: EAGAIN\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel(scanner); /* we had an error, stop scanner */
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0) /* EOF */
+ {
+ if ( (scanner->device->three_pass == 0) ||
+ (scanner->device->colormode <= RGB_LINEART) ||
+ (++(scanner->device->three_pass_color) > 3) )
+ {
+ do_cancel(scanner);
+ }
+
+ DBG(DBG_sane_proc,"closing read end of pipe\n");
+
+ if (scanner->pipe_read_fd >= 0)
+ {
+ close(scanner->pipe_read_fd);
+ scanner->pipe_read_fd = -1;
+ }
+
+ return SANE_STATUS_EOF;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE CANCEL -------------------------------- */
+
+
+void sane_cancel(SANE_Handle handle)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_init,"sane_cancel\n");
+
+ if (scanner->scanning)
+ {
+ do_cancel(scanner);
+ }
+}
+
+
+/* ------------------------------------------------------------ SANE SET IO MODE --------------------------- */
+
+
+SANE_Status sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_init,"sane_set_io_mode: non_blocking=%d\n", non_blocking);
+
+ if (!scanner->scanning) { return SANE_STATUS_INVAL; }
+
+ if (fcntl(scanner->pipe_read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE GET SELECT FD ------------------------- */
+
+
+SANE_Status sane_get_select_fd(SANE_Handle handle, SANE_Int *fd)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_init,"sane_get_select_fd\n");
+
+ if (!scanner->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = scanner->pipe_read_fd;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ EOF ---------------------------------------- */
diff --git a/backend/umax.conf.in b/backend/umax.conf.in
new file mode 100644
index 0000000..a57cf53
--- /dev/null
+++ b/backend/umax.conf.in
@@ -0,0 +1,117 @@
+#
+# Options for the umax backend
+#
+
+# define scsi queueing depth
+#option scsi-maxqueue 2
+
+# define scsi buffer size in bytes
+#option scsi-buffer-size-min 65536
+#option scsi-buffer-size-max 262144
+
+# define scan lines that shall be read in one block
+#option scan-lines 100
+#option preview-lines 20
+
+# define how to handle bad sense codes
+# 0 = handle as device busy
+# 1 = handle as ok
+# 2 = handle as i/o error
+# 3 = ignore bad error code - continue sense handler,
+#option handle-bad-sense-error 0
+
+# define if a request sense command shall be executed
+#option execute-request-sense 0
+
+# define if the preview bit shall be set when scanning in rgb mode
+#option force-preview-bit-rgb 0
+
+# define if slow speed flag shall be set
+# BE CAREFUL WITH THIS OPTION, IT MAY DESTROY YOUR SCANNER WHEN SET FALSE
+# -1 = automatically set by driver - if known
+# 0 = disabled
+# 1 = enabled
+#option slow-speed 0
+
+# define if care-about-smeraring flag shall be set
+# BE CAREFUL WITH THIS OPTION, IT MAY DESTROY YOUR SCANNER WHEN SET FALSE
+# -1 = automatically set by driver - if known
+# 0 = disabled
+# 1 = enabled
+#option care-about-smearing 0
+
+# define if the calibration shall be done for selected scanarea or for each ccd pixel
+# -1 = automatically set by driver - if known
+# 0 = disabled
+# 1 = enabled
+#option calibration-full-ccd 1
+
+# define if an offset of the calculate calibration with has to be used
+# -99999 = auto
+#option calibration-width-offset -99999
+
+# define the number of pixels that is used for calibration
+# -1 = disabled
+# 0 = not set
+# 1 = 1 byte/pixel,
+# 2 = 2 bytes/pixel
+#option calibration-bytes-pixel -1
+
+# define if scanner uses the same exposure times for red, green and blue
+# -1 = automatically set by driver - if known
+# 0 = disabled (own selection for red, green and blue)
+# 1 = enabled (same values for red, green and blue)
+#options exposure-time-rgb-bind 0
+
+# define if shading data shall be inverted befor sending it back to the scanner
+# -1 = automatically set by driver - if known
+# 0 = disabled
+# 1 = enabled
+#option invert-shading-data 0
+
+# define if the scanner supports lamp control commands
+# 0 = automatically set by driver - if known
+# 1 = enabled
+#option lamp-control-available 0
+
+# define how 16 bit gamma data is padded
+# -1 = automatically set by driver - if known
+# 0 = gamma data is msb padded
+# 1 = gamma data is lsb padded
+#option gamma-lsb-padded 0
+
+# define connection type of following devices
+# 1 = scsi
+# 2 = usb
+#option connection-type 1
+
+# linux device identification:
+#scsi vendor model type bus channel id lun
+scsi UMAX * Scanner
+scsi LinoHell JADE
+scsi LinoHell Office
+scsi LinoHell Office2
+scsi LinoHell SAPHIR2
+scsi LinoHell SAPHIR3
+scsi Linotype SAPHIR4
+scsi LinoHell OPAL2
+scsi HDM LS4H1S
+scsi Nikon AX-110
+scsi Nikon AX-210
+scsi KYE ColorPage-HR5
+scsi EPSON Perfection600
+scsi ESCORT "Galleria 600S"
+scsi TriGem PowerScanII
+
+# Umax Astra 2200 via USB:
+# usb vendor product
+usb 0x1606 0x0230
+
+# scsi device list
+option connection-type 1
+/dev/scanner
+
+# usb device list
+option connection-type 2
+/dev/usbscanner
+
diff --git a/backend/umax.h b/backend/umax.h
new file mode 100644
index 0000000..682e58b
--- /dev/null
+++ b/backend/umax.h
@@ -0,0 +1,499 @@
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* umax.h - headerfile for SANE-backend for umax scanners
+
+ (C) 1997-2002 Oliver Rauch
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ */
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#ifndef umax_h
+#define umax_h
+
+#include "sys/types.h"
+
+/* --------------------------------------------------------------------------------------------------------- */
+/* COMPILER OPTIONS: */
+
+#define UMAX_ENABLE_USB
+#define UMAX_HIDE_UNUSED
+
+/* #define SANE_UMAX_DEBUG_S12 */
+/* #define UMAX_CALIBRATION_MODE_SELECTABLE */
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#define SANE_UMAX_SCSI_MAXQUEUE 8
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#define SANE_UMAX_FIX_ROUND(val) ((SANE_Word) ((val) * (1 << SANE_FIXED_SCALE_SHIFT) + 1.0 / (1 << (SANE_FIXED_SCALE_SHIFT+1))))
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+enum Umax_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ /* ------------------------------------------- */
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_SOURCE,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+
+ OPT_RESOLUTION_BIND, /* bind x and y resolution */
+ OPT_NEGATIVE,
+
+ /* ------------------------------------------- */
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ /* ------------------------------------------- */
+
+ OPT_ENHANCEMENT_GROUP,
+
+ OPT_BIT_DEPTH, /* output bit depth */
+ OPT_QUALITY, /* quality calibration */
+ OPT_DOR, /* double optical resolution */
+ OPT_WARMUP,
+ OPT_RGB_BIND, /* use same rgb-values for each color in color-mode */
+
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_THRESHOLD,
+
+ OPT_HIGHLIGHT, /* highlight */
+ OPT_HIGHLIGHT_R,
+ OPT_HIGHLIGHT_G,
+ OPT_HIGHLIGHT_B,
+
+ OPT_SHADOW, /* shadow */
+ OPT_SHADOW_R,
+ OPT_SHADOW_G,
+ OPT_SHADOW_B,
+
+ OPT_ANALOG_GAMMA, /* analog gamma */
+ OPT_ANALOG_GAMMA_R,
+ OPT_ANALOG_GAMMA_G,
+ OPT_ANALOG_GAMMA_B,
+
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables */
+ /* The gamma vectors MUST appear in the order gray, red, green, blue. */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_HALFTONE_DIMENSION,
+ OPT_HALFTONE_PATTERN,
+
+ /* ------------------------------------------- */
+
+ OPT_ADVANCED_GROUP,
+
+ OPT_CAL_EXPOS_TIME, /* exposure time for calibration */
+ OPT_CAL_EXPOS_TIME_R,
+ OPT_CAL_EXPOS_TIME_G,
+ OPT_CAL_EXPOS_TIME_B,
+ OPT_SCAN_EXPOS_TIME, /* exposure time for scan */
+ OPT_SCAN_EXPOS_TIME_R,
+ OPT_SCAN_EXPOS_TIME_G,
+ OPT_SCAN_EXPOS_TIME_B,
+
+ OPT_DISABLE_PRE_FOCUS,
+ OPT_MANUAL_PRE_FOCUS,
+ OPT_FIX_FOCUS_POSITION,
+ OPT_LENS_CALIBRATION_DOC_POS,
+ OPT_HOLDER_FOCUS_POS_0MM,
+
+ OPT_CAL_LAMP_DEN,
+ OPT_SCAN_LAMP_DEN,
+
+ OPT_SELECT_EXPOSURE_TIME,
+ OPT_SELECT_CAL_EXPOSURE_TIME,
+ OPT_SELECT_LAMP_DENSITY,
+
+ OPT_LAMP_ON,
+ OPT_LAMP_OFF,
+ OPT_LAMP_OFF_AT_EXIT,
+
+ OPT_BATCH_SCAN_START, /* start batch scan function */
+ OPT_BATCH_SCAN_LOOP, /* loop batch scan function */
+ OPT_BATCH_SCAN_END, /* end batch scan function */
+ OPT_BATCH_NEXT_TL_Y, /* batch scan function next y position */
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ OPT_CALIB_MODE,
+#endif
+
+ OPT_PREVIEW, /* preview, sets preview-bit and bind x/y-resolution */
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+
+/* -------------------------------------------------------------------------------------------------------- */
+
+
+/* LIST OF AVAILABLE SCANNERS, THE VALUES LISTED HERE ARE THE SAME FOR DIFFERENT APPLICATIONS
+ THAT USE THE SAME DEVICE */
+
+/* Umax_Device contains values relevant for the device that are not intersting for the sane interface */
+
+typedef struct Umax_Device
+{
+ struct Umax_Device *next;
+ SANE_Device sane;
+
+ int connection_type;
+#define SANE_UMAX_UNKNOWN 0
+#define SANE_UMAX_SCSI 1
+#define SANE_UMAX_USB 2
+
+ SANE_Range x_dpi_range;
+ SANE_Range y_dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Range analog_gamma_range;
+ unsigned flags;
+
+ unsigned char *buffer[SANE_UMAX_SCSI_MAXQUEUE]; /* buffer used for scsi-transfer */
+ void *queue_id[SANE_UMAX_SCSI_MAXQUEUE]; /* scsi queue id */
+ size_t length_queued[SANE_UMAX_SCSI_MAXQUEUE]; /* length of queued data */
+ size_t length_read[SANE_UMAX_SCSI_MAXQUEUE]; /* length of returned data */
+ unsigned int bufsize;
+ unsigned int row_bufsize;
+ unsigned int request_scsi_maxqueue;
+ unsigned int request_preview_lines;
+ unsigned int request_scan_lines;
+ unsigned int handle_bad_sense_error;
+ unsigned int execute_request_sense;
+ unsigned int force_preview_bit_rgb;
+ unsigned int scsi_buffer_size_min;
+ unsigned int scsi_buffer_size_max;
+ unsigned int scsi_maxqueue;
+
+ unsigned char *pixelbuffer; /* buffer used for pixel ordering */
+ unsigned int pixelline_max; /* number of lines that fit into pixelbuffer */
+ unsigned int pixelline_ready[3]; /* finished absolute line for each color */
+ unsigned int pixelline_next[3]; /* next line to write for each color */
+ unsigned int pixelline_del[3]; /* next line to delete in opt_res for col */
+ unsigned int pixelline_optic[3]; /* scanned line in opt_res for each color */
+ unsigned int pixelline_opt_res; /* number of scanned line in optical res */
+ unsigned int pixelline_read; /* number of read pixel-lines */
+ unsigned int pixelline_written; /* number of written pixel-lines */
+ unsigned int CCD_distance; /* color line distance in optical resolution */
+ unsigned int CCD_color[9]; /* color order */
+ /* 0 / 1 2 / 3 4 5 / 6 7 / 8 */
+# define CCD_color_red 0
+# define CCD_color_green 1
+# define CCD_color_blue 2
+
+ char *devicename; /* name of the scanner device */
+ int sfd; /* output file descriptor, scanner device */
+
+ char vendor[9]; /* will be UMAX */
+ char product[17]; /* e.g. "SuperVista_S12" or so */
+ char version[5]; /* e.g. V1.3 */
+
+ int three_pass; /* used in RGB-mode if 3-pass => 1 */
+ int three_pass_color; /* select color for scanning in 3pass mode */
+ unsigned int row_len; /* len of one scan-line in bytes */
+ unsigned int lines_max; /* maximum number of lines to scan */
+ unsigned int max_value; /* used for pnm-file */
+
+ /* data defined by inquiry */
+ int inquiry_len; /* length of inquiry return block */
+ int inquiry_wdb_len; /* length of window descriptor block */
+ int inquiry_vidmem; /* size of video memory */
+ int inquiry_optical_res; /* optical resolution */
+ int inquiry_x_res; /* maximum x-resolution */
+ int inquiry_y_res; /* maximum y-resolution */
+ int inquiry_dor_optical_res; /* optical resolution for dor mode */
+ int inquiry_dor_x_res; /* maximum x-resolution for dor mode */
+ int inquiry_dor_y_res; /* maximum y-resolution for dor mode */
+ double inquiry_fb_width; /* flatbed width in inches */
+ double inquiry_fb_length; /* flatbed length in inches */
+ double inquiry_uta_width; /* transparency width in inches */
+ double inquiry_uta_length; /* transparency length in inches */
+ double inquiry_uta_x_off; /* transparency x offset in inches */
+ double inquiry_uta_y_off; /* transparency y offset in inches */
+ double inquiry_dor_width; /* double resolution width in inches */
+ double inquiry_dor_length; /* double resolution length in inches */
+ double inquiry_dor_x_off; /* double resolution x offset in inches */
+ double inquiry_dor_y_off; /* double resolution y offset in inches */
+
+ int inquiry_exposure_adj; /* 1 if exposure adjust is supported */
+ int inquiry_exposure_time_step_unit; /* exposure time unit in micro sec */
+ int inquiry_exposure_time_max; /* exposure time maximum */
+ int inquiry_exposure_time_l_min; /* exposure tine minimum for lineart */
+ int inquiry_exposure_time_l_fb_def; /* exposure time default for lineart/flatbed */
+ int inquiry_exposure_time_l_uta_def; /* exposure time default for lineart/uta */
+ int inquiry_exposure_time_h_min; /* exposure tine minimum for halftone */
+ int inquiry_exposure_time_h_fb_def; /* exposure time default for halftone/flatbed */
+ int inquiry_exposure_time_h_uta_def; /* exposure time default for halftone/uta */
+ int inquiry_exposure_time_g_min; /* exposure tine minimum for grayscale */
+ int inquiry_exposure_time_g_fb_def; /* exposure time default for grayscale/flatbed */
+ int inquiry_exposure_time_g_uta_def; /* exposure time default for grayscale/uta */
+ int inquiry_exposure_time_c_min; /* exposure tine minimum for color */
+ int inquiry_exposure_time_c_fb_def_r; /* exposure time default for color red/flatbed */
+ int inquiry_exposure_time_c_fb_def_g; /* exposure time default for color green/flatbed */
+ int inquiry_exposure_time_c_fb_def_b; /* exposure time default for color blue/flatbed */
+ int inquiry_exposure_time_c_uta_def_r; /* exposure time default for color red/uta */
+ int inquiry_exposure_time_c_uta_def_g; /* exposure time default for color green/uta */
+ int inquiry_exposure_time_c_uta_def_b; /* exposure time default for color blue/uta */
+
+ int inquiry_max_warmup_time; /* maximum lamp warmup time in sec */
+ int inquiry_cbhs; /* 50, 255, 255+autoexp. */
+ int inquiry_cbhs_min; /* minimum value for cbhs */
+ int inquiry_cbhs_max; /* maximum value for cbhs */
+ int inquiry_contrast_min; /* minimum value for c */
+ int inquiry_contrast_max; /* maximum value for c */
+ int inquiry_brightness_min; /* minimum value for b */
+ int inquiry_brightness_max; /* maximum value for b */
+ int inquiry_threshold_min; /* minimum value for t */
+ int inquiry_threshold_max; /* maximum value for t */
+ int inquiry_highlight_min; /* minimum value for h */
+ int inquiry_highlight_max; /* maximum value for h */
+ int inquiry_shadow_min; /* minimum value for s */
+ int inquiry_shadow_max; /* maximum value for s */
+
+ int inquiry_quality_ctrl; /* 1 = supported */
+ int inquiry_batch_scan; /* 1 = supported */
+ int inquiry_preview; /* 1 = supported */
+ int inquiry_lamp_ctrl; /* 1 = supported */
+ int inquiry_transavail; /* 1 = uta available */
+ int inquiry_adfmode; /* 1 = adf available */
+ int inquiry_uta; /* 1 = uta supported */
+ int inquiry_adf; /* 1 = adf supported */
+ int inquiry_dor; /* 1 = dor supported */
+ int inquiry_reverse; /* 1 = 1 bit reverse supported */
+ int inquiry_reverse_multi; /* 1 = multi bit reverse supported */
+ int inquiry_analog_gamma; /* 1 = analog gamma supported */
+ int inquiry_lineart_order; /* 1 = LSB first, 0 = MSB first */
+
+ int inquiry_lens_cal_in_doc_pos; /* 1 = lens calibration in doc pos supported */
+ int inquiry_manual_focus; /* 1 = manual focus supported */
+ int inquiry_sel_uta_lens_cal_pos; /* 1 = selection of lens calib pos for uta supported */
+
+ int inquiry_gamma_dwload; /* 1 = gamma download supported */
+ int inquiry_gamma_DCF; /* gamma download curve format */
+
+ int inquiry_one_pass_color; /* 1 = 1 pass supported */
+ int inquiry_three_pass_color; /* 1 = 3 pass supported */
+ int inquiry_color; /* 1 = color mode supported */
+ int inquiry_gray; /* 1 = grayscale mode supported */
+ int inquiry_halftone; /* 1 = halftone mode supported */
+ int inquiry_lineart; /* 1 = lineart mode supported */
+
+ int inquiry_calibration; /* 1 = calibration mode control supported */
+ int inquiry_highlight; /* 1 = highlight supported */
+ int inquiry_shadow; /* 1 = shadow supported */
+ int inquiry_GIB; /* gamma input bits */
+ int inquiry_GOB; /* gamma output bits */
+ int inquiry_max_calib_lines; /* maximum calibration lines */
+ int inquiry_color_order; /* color ordering support */
+ int inquiry_CCD_line_distance; /* color line distance */
+ int inquiry_fb_uta_color_arrangement; /* line arrangement for fb & uta */
+ int inquiry_adf_color_arrangement; /* line arrangement for adf */
+
+ unsigned int relevant_optical_res; /* optical resolution */
+ unsigned int relevant_max_x_res; /* maximum x-resolution */
+ unsigned int relevant_max_y_res; /* maximum y-resolution */
+
+ /* selected data */
+
+ int use_exposure_time_min; /* exposure tine minimum */
+ int use_exposure_time_def_r; /* exposure time default */
+ int use_exposure_time_def_g; /* exposure time default */
+ int use_exposure_time_def_b; /* exposure time default */
+
+ int wdb_len; /* use this length of WDB */
+ unsigned int width_in_pixels; /* thats the wanted width in pixels */
+ unsigned int length_in_pixels; /* thats the wanted length in pixels */
+ unsigned int scanwidth; /* thats the width in pixels at x_coordinate_base dpi */
+ unsigned int scanlength; /* thats the length in pixels at y_coordinate_base dpi */
+ unsigned int bytes_per_color; /* bytes per each color */
+
+ unsigned int x_resolution; /* scan-resolution for x in dpi */
+ unsigned int y_resolution; /* scan-resolution for y in dpi */
+ double scale_x; /* x-scaling of optical resolution */
+ double scale_y; /* y-scaling of optical resolution */
+ int upper_left_x; /* thats the left edge in points at 1200pt/inch */
+ int upper_left_y; /* thats the top edge in points at 1200pt/inch */
+
+ unsigned int x_coordinate_base; /* x base in pixels/inch, normaly 1200 */
+ unsigned int y_coordinate_base; /* y base in pixels/inch, normaly 1200 */
+
+ unsigned int bits_per_pixel; /* number of bits per pixel */
+ int bits_per_pixel_code; /* 1 = 24bpp, 4 = 30 bpp, 8 = 36 bpp */
+ int gamma_input_bits_code; /* 1 = 24bpp, 4 = 30 bpp, 8 = 36 bpp */
+ int set_auto; /* 0 or 1, don't know what it is */
+ int preview; /* 1 if preview */
+ int batch_scan; /* 1 = batch scan, 0 = normal scan */
+ int batch_end; /* 1 = reposition scanhead */
+ int batch_next_tl_y; /* top left y position for next batch scan */
+ int quality; /* 1 = quality_calibration, 0 = fast */
+ int reverse; /* 1: exchange black and white */
+ int reverse_multi; /* 1: invert color values */
+ int WD_speed; /* is a combination of slow and smear */
+ int slow; /* 1: slow scan speed */
+ int smear; /* 1: don't care about image smearing problem */
+ int dor; /* double resolution */
+ int cbhs_range; /* 50,255 or 255+autoexposure */
+ int fix_focus_position; /* fix focus position */
+ int lens_cal_in_doc_pos; /* lens calibration in document position */
+ int disable_pre_focus; /* disable pre focus */
+ int holder_focus_pos_0mm; /* 0.6mm <-> 0.0mm holder focus position */
+ int manual_focus; /* automatic <-> manual focus */
+ int warmup; /* 1=set warmup-bit */
+ int module; /* flatbed or transparency */
+ int adf; /* 1 if ADF turned on */
+ int uta; /* 1 if UTA turned on */
+ int calibration; /* calibration :0=ignore, 1=driver, 2=by image */
+ int low_byte_first; /* 10 bit mode: 0=high byte frist, 1=low byte frist */
+ int colormode; /* LINEART, HALFTONE, GRAYSCALE or RGB */
+# define LINEART 1
+# define HALFTONE 2
+# define GRAYSCALE 3
+# define RGB_LINEART 4
+# define RGB_HALFTONE 5
+# define RGB 6
+
+ int exposure_time_calibration_r; /* red exposure time for calibration */
+ int exposure_time_calibration_g; /* green exposure time for calibration */
+ int exposure_time_calibration_b; /* blue exposure time for calibration */
+ int exposure_time_scan_r; /* red exposure time for scan */
+ int exposure_time_scan_g; /* green exposure time for scan */
+ int exposure_time_scan_b; /* bue exposure time for scan */
+
+ int c_density; /* next calibration lamp density */
+ int s_density; /* next scan lamp density */
+
+ int threshold; /* (128) 0-255, lineart mode */
+ int brightness; /* (128) cbhs_range 0-255, halftone mode */
+ int contrast; /* (128) cbhs_range 0-255, halftone-mode */
+ int highlight_r; /* (255) cbhs_range 1-255, each mode */
+ int highlight_g; /* (255) cbhs_range 1-255, each mode */
+ int highlight_b; /* (255) cbhs_range 1-255, each mode */
+ int shadow_r; /* (0) cbhs_range 0-254, each mode */
+ int shadow_g; /* (0) cbhs_range 0-254, each mode */
+ int shadow_b; /* (0) cbhs_range 0-254, each mode */
+ int halftone; /* halftone pattern select */
+
+ int digital_gamma_r; /* gamma-select for red and gray */
+ int digital_gamma_g; /* gamma-select value for green */
+ int digital_gamma_b; /* gamma-select value for blue */
+
+ int analog_gamma_r; /* analog gamma red and gray */
+ int analog_gamma_g; /* analog gamma green */
+ int analog_gamma_b; /* analog gamma blue */
+
+ int calib_lines; /* request calibration lines */
+
+ int do_calibration; /* 1: do calibration by driver */
+ int do_color_ordering; /* 1: order line-mode to pixel-mode */
+
+ int button0_pressed; /* scan-button 0 on scanner is pressed => 1 */
+ int button1_pressed; /* scan-button 1 on scanner is pressed => 1 */
+ int button2_pressed; /* scan-button 2 on scanner is pressed => 1 */
+
+ int calibration_area; /* define calibration area if no area is given */
+ int calibration_width_offset; /* some scanners do calibrate with some additional pixels */
+ int calibration_width_offset_batch; /* the same for batch scanning */
+ int calibration_bytespp; /* correction of bytespp if driver knows about it */
+ int exposure_time_rgb_bind; /* exposure time can not be defined for each color */
+ int invert_shading_data; /* invert shading data before sending it to the scanner */
+ int common_xy_resolutions; /* do not allow different x and y resolution */
+ int pause_for_color_calibration; /* pause between start_scan and do_calibration in ms */
+ int pause_for_gray_calibration; /* pause between start_scan and do_calibration in ms */
+ int pause_after_calibration; /* pause between do_calibration and read data in ms */
+ int pause_after_reposition; /* pause for repositioning in ms */
+ int pause_for_moving; /* pause for moving scanhead over full scanarea in ms */
+ int lamp_control_available; /* is set when scanner supportes lamp control */
+ int gamma_lsb_padded; /* 16 bit gamma data is padded to lsb */
+ int force_quality_calibration; /* always set quality calibration */
+} Umax_Device;
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* LIST OF OPEND DEVICES, A DEVICE MAY BE OPEND TWICE, ALL VALUES LISTED HERE MAY BE
+ DIFFERENT FOR DIFFERENT APPLICATIONS */
+
+/* Umax_Scanner contains values relevant for the sane interface */
+
+typedef struct Umax_Scanner
+{
+ struct Umax_Scanner *next;
+ Umax_Device *device;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Int *gamma_table[4];
+ SANE_Int halftone_pattern[64];
+ SANE_Range gamma_range;
+ unsigned int gamma_length;
+ SANE_Range output_range;
+ unsigned int output_bytes;
+ SANE_Range exposure_time_range;
+
+ int scanning;
+ SANE_Parameters params;
+
+ SANE_Pid reader_pid;
+ int pipe_read_fd;
+ int pipe_write_fd;
+} Umax_Scanner;
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+#endif /* umax-sane_h */
diff --git a/backend/umax1220u-common.c b/backend/umax1220u-common.c
new file mode 100644
index 0000000..43a4a6f
--- /dev/null
+++ b/backend/umax1220u-common.c
@@ -0,0 +1,2386 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1999 Paul Mackerras
+ Copyright (C) 2000 Adrian Perez Jorge
+ Copyright (C) 2001 Frank Zago
+ Copyright (C) 2001 Marcio Teixeira
+ Parts copyright (C) 2006 Patrick Lessard
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Defines a driver and API for accessing the UMAX Astra 1220U
+ USB scanner. Based on the original command line tool by Paul
+ Mackerras.
+
+ The UMAX Astra 1220U scanner uses the PowerVision PV8630
+ Parallel Port to USB bridge. This chip is also used
+ by the HP4200C flatbed scanner. Adrian Perez Jorge wrote
+ a nice interface file for that chip and Frank Zago adapted
+ it to use the sanei_usb interface. Thanks, guys, for making
+ my life easier! :)
+
+ */
+
+
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <math.h>
+
+/*
+ * The backend performs test scans in order to calibrate
+ * the CCD and to find the zero location. If you would like
+ * to look at those scans, define DEBUG_CALIBRATION to have
+ * the backend save "find_zero.pgm" and "calibration.pgm" to
+ * disk.
+ */
+/* #define DEBUG_CALIBRATION */
+
+/*
+ * Define DEBUG_BOUNDS to insert paranoid array bounds
+ * overrun detection into the code.
+ */
+/* #define DEBUG_BOUNDS */
+
+/* These values are empirically determined and are given
+ * in 1/600 inch units. If UMAX_MAX_HEIGHT is too large,
+ * the scanner may grind its gears. I assume there is a
+ * physical limit to UMAX_MAX_WIDTH as well (based on the
+ * sensor size) but I do not know what it is. The current
+ * value can be increased beyond what it is now, but you
+ * gain nothing in usuable scan area (you only scan more
+ * of the underside of the scanner's plastic lid).
+ */
+
+
+#define UMAX_MAX_WIDTH 5400
+#define UMAX_MAX_HEIGHT 7040
+
+/* Buffer size. Specifies the size of the buffer that is
+ * used to copy data from the scanner. The old command
+ * line driver had this set at 0x80000 which is likely
+ * the largest possible chunck of data that can be.
+ * at once. This is probably most efficient, but using
+ * a lower value for the SANE driver makes the driver
+ * more responsive to interaction.
+ */
+#define BUFFER_SIZE 0x80000
+
+/* Constants that can be used with set_lamp_state to
+ * control the state of the scanner's lamp
+ */
+typedef enum
+{
+ UMAX_LAMP_OFF = 0,
+ UMAX_LAMP_ON = 1
+}
+UMAX_Lamp_State;
+
+/* Constants that can be used with move to control
+ * the rate of scanner head movement
+ */
+typedef enum
+{
+ UMAX_NOT_FINE = 0,
+ UMAX_FINE = 1
+}
+UMAX_Speed;
+
+/* If anyone knows some descriptive names for these,
+ * please update
+ */
+typedef enum
+{
+ CMD_0 = 0x00,
+ CMD_1 = 0x01,
+ CMD_2 = 0x02,
+ CMD_4 = 0x04,
+ CMD_8 = 0x08,
+ CMD_40 = 0x40,
+ CMD_WRITE = 0x80,
+ CMD_READ = 0xc0
+}
+UMAX_Cmd;
+
+/* Product IDs for Astra scanners
+ */
+typedef enum
+{
+ ASTRA_1220U = 0x0010,
+ ASTRA_2000U = 0x0030,
+ ASTRA_2100U = 0x0130
+}
+UMAX_Model;
+
+/* The bytes UMAX_SYNC1 and UMAX_SYNC2 serve as a
+ * synchronization signal. Unintentional sync bytes
+ * in the data stream are escaped with UMAX_ESCAPE
+ * character
+ */
+
+#define UMAX_SYNC1 0x55
+#define UMAX_SYNC2 0xaa
+#define UMAX_ESCAPE 0x1b
+
+/* Status bits. These bits are active low.
+ * In umax_pp, UMAX_REVERSE_BIT is called
+ * MOTOR_BIT.
+ */
+
+#define UMAX_FORWARD_BIT 0x40
+#define UMAX_ERROR_BIT 0x20
+#define UMAX_MOTOR_OFF_BIT 0x08
+
+#define UMAX_OK 0x48 /* Used to be 0xC8 */
+#define UMAX_OK_WITH_MOTOR 0x40 /* Used to be 0xD0 */
+
+#define UMAX_STATUS_MASK 0x68
+
+/* This byte is used as a placeholder for bytes that are parameterized
+ * in the opcode strings */
+
+#define XXXX 0x00
+
+/* This macro is used to check the return code of
+ * functions
+ */
+#define CHK(A) {if( (res = A) != SANE_STATUS_GOOD ) { \
+ DBG( 1, "Failure on line of %s: %d\n", __FILE__, \
+ __LINE__ ); return A; }}
+
+/* Macros that are used for array overrun detection
+ * (when DEBUG_BOUNDS is defined)
+ */
+#ifdef DEBUG_BOUNDS
+#define PAD 10
+#define PAD_ARRAY( A, len ) {int i; \
+ for( i = 0; i < PAD; i++ ) {A[len+i]=0x55;}}
+
+#define CHK_ARRAY( A, len ) {int i;for( i = 0; i < PAD; i++ ) {\
+ if(A[len+i]!=0x55) { \
+ DBG( 1, "Array overrun detected on line %d\n", __LINE__ ); \
+ }}}
+#else
+#define PAD 0
+#define PAD_ARRAY( A, len )
+#define CHK_ARRAY( A, len )
+#endif
+
+
+/* This data structure contains data related
+ * to the scanning process.
+ */
+typedef struct
+{
+ /* Constant data */
+
+ int color;
+ int w;
+ int h;
+ int xo;
+ int yo;
+ int xdpi; /* Physical x dpi */
+ int ydpi; /* Physical y dpi */
+ int xsamp;
+ int ysamp;
+
+ int xskip;
+ int yskip;
+
+ int fd; /* Device file handle */
+ UMAX_Model model;
+
+ /* Raw scan data buffer */
+
+ unsigned char *p;
+ int bh; /* Size of buffer in lines */
+ int hexp; /* Scan lines yet to be read */
+
+ /* Decoding logic */
+
+ int x, y, maxh;
+ int done; /* Boolean, all lines decoded */
+
+ /* Calibration data */
+
+ unsigned char caldata[16070 + PAD];
+
+ /* Scan head position */
+
+ int scanner_ypos;
+ int scanner_yorg;
+}
+UMAX_Handle;
+
+typedef unsigned char UMAX_Status_Byte;
+
+
+#if 0
+static void
+unused_operations ()
+{
+ /* These operations are unused anywhere in the driver */
+
+ unsigned char opb8[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0x18, 0x10, 0x03,
+ 0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x13, 0x1a
+ };
+
+ unsigned char opb9[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0x41, 0x20, 0x24,
+ 0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x13, 0x1a
+ };
+
+ unsigned char opb10[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0x41, 0x60, 0x4f,
+ 0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x93, 0x1a
+ };
+
+ unsigned char opc5[16] = {
+ 0x05, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x00,
+ 0x00, 0x30, 0x0c, 0xc3, 0xa4, 0x00
+ };
+
+ unsigned char opc6[16] = {
+ 0x01, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x00,
+ 0x88, 0x48, 0x0c, 0x83, 0xa4, 0x00
+ };
+
+ unsigned char opc7[16] = {
+ 0x05, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x00,
+ 0xec, 0x4e, 0x0c, 0xc3, 0xa4, 0x00
+ };
+
+ unsigned char opd2[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, 0x00, 0x30
+ };
+}
+#endif
+
+#if 0
+static SANE_Status
+calib (UMAX_Handle * scan)
+{
+ unsigned char buf[65536];
+ opc5[11] = 0x30;
+ opd2[7] = 0x30;
+ CHK (get_pixels (scan, opc5, opb8, opd2, ope, 24, 0, buff));
+
+ opc5[11] = 0x40;
+ opd2[7] = 0x40;
+ CHK (get_pixels (scan, opc5, opb8, opd2, ope, 24, 0, buff));
+
+ opd2[6] = 8;
+ opd2[7] = 0x30;
+ CHK (get_pixels (scan, opc6, opb9, opd2, ope, 0x200, 1, buff));
+
+ opc7[10] = 0xec;
+ opd2[6] = 0xc;
+ opd2[7] = 0x40;
+ CHK (get_pixels (scan, opc7, opb10, opd2, ope, 5300, 1, buff));
+
+ opc7[10] = 0xed;
+ opd2[6] = 0xd;
+ CHK (get_pixels (scan, opc7, opb10, opd2, ope, 5300, 0, buff));
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+
+/* This seems to configure the pv8630 chip somehow. I wish
+ * all the magic numbers were defined as self-descriptive
+ * constants somewhere. I made some guesses based on what
+ * I found in "pv8630.c", but alas there wasn't enough in
+ * there. If you know what this does, please let me know!
+ */
+static SANE_Status
+xxxops (UMAX_Handle * scan)
+{
+ SANE_Status res;
+
+ DBG (9, "doing xxxops\n");
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_RMODE, 0x02));
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x0E));
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_RDATA, 0x40));
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x06));
+ CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_RSTATUS, 0x38, 0xFF));
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x07));
+ CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_RSTATUS, 0x38, 0xFF));
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x04));
+ CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_RSTATUS, 0xF8, 0xFF));
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x05));
+ CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_UNKNOWN, 0x05, 0xFF));
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x04));
+
+ CHK (sanei_pv8630_write_byte (scan->fd, PV8630_RMODE, 0x1E));
+
+ return res;
+}
+
+/*
+Apparently sends the two syncronization characters followed
+by the command length, followed by the command number
+*/
+static SANE_Status
+usync (UMAX_Handle * scan, UMAX_Cmd cmd, int len)
+{
+ UMAX_Status_Byte s0, s4;
+ SANE_Status res;
+ unsigned char buf[4];
+ size_t nb;
+
+ DBG (80, "usync: len = %d, cmd = %d\n", len, cmd);
+
+ buf[0] = UMAX_SYNC1;
+ buf[1] = UMAX_SYNC2;
+
+ nb = 2;
+ CHK (sanei_pv8630_flush_buffer (scan->fd));
+ CHK (sanei_pv8630_prep_bulkwrite (scan->fd, nb));
+ CHK (sanei_pv8630_bulkwrite (scan->fd, buf, &nb));
+
+ CHK (sanei_pv8630_wait_byte
+ (scan->fd, PV8630_RSTATUS, UMAX_OK, UMAX_STATUS_MASK, 20));
+
+ buf[0] = len >> 16;
+ buf[1] = len >> 8;
+ buf[2] = len;
+ buf[3] = cmd;
+
+ nb = 4;
+ CHK (sanei_pv8630_flush_buffer (scan->fd));
+ CHK (sanei_pv8630_prep_bulkwrite (scan->fd, nb));
+ CHK (sanei_pv8630_bulkwrite (scan->fd, buf, &nb));
+
+ CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RDATA, &s0));
+ CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RSTATUS, &s4));
+
+ DBG (90, "usync: s0 = %#x s4 = %#x\n", s0, s4);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This function escapes any syncronization sequence that may be
+in data, storing the result in buf. In the worst case where
+every character gets escaped buf must be at least twice as
+large as dlen.
+*/
+static int
+bescape (const unsigned char *data, int dlen, unsigned char *buf, int blen)
+{
+ const unsigned char *p;
+ unsigned char *q;
+ int i, c;
+ i = blen; /* Eliminate compiler warning about unused param */
+
+ p = data;
+ q = buf;
+ for (i = 0; i < dlen; ++i)
+ {
+ c = *p++;
+ if (c == UMAX_ESCAPE
+ || (c == UMAX_SYNC2 && i > 0 && p[-2] == UMAX_SYNC1))
+ *q++ = UMAX_ESCAPE;
+ *q++ = c;
+ }
+ return q - buf;
+}
+
+
+
+/* Write */
+
+static SANE_Status
+cwrite (UMAX_Handle * scan, UMAX_Cmd cmd, size_t len, const unsigned char *data,
+ UMAX_Status_Byte * s)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s0, s4;
+
+ static unsigned char *escaped = NULL;
+ static size_t escaped_size = 0;
+
+ DBG (80, "cwrite: cmd = %d, len = %lu\n", cmd, (u_long) len);
+
+ CHK (usync (scan, cmd | CMD_WRITE, len));
+
+ if (len <= 0)
+ return SANE_STATUS_GOOD;
+
+ if (escaped_size < len * 2)
+ {
+ escaped_size = len * 2;
+ if (escaped)
+ free (escaped);
+ escaped = malloc (escaped_size);
+ if (escaped == NULL)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ len = bescape (data, len, escaped, len * 2);
+
+ CHK (sanei_pv8630_wait_byte
+ (scan->fd, PV8630_RSTATUS, UMAX_OK, UMAX_STATUS_MASK, 20));
+
+ CHK (sanei_pv8630_flush_buffer (scan->fd));
+ CHK (sanei_pv8630_prep_bulkwrite (scan->fd, len));
+ CHK (sanei_pv8630_bulkwrite (scan->fd, escaped, &len));
+
+ CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RSTATUS, &s4));
+ CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RDATA, &s0));
+
+ DBG (90, "cwrite: s0 = %#x s4 = %#x\n", s0, s4);
+
+ if (s)
+ *s = s0;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Read */
+
+static SANE_Status
+cread (UMAX_Handle * scan, UMAX_Cmd cmd, size_t len, unsigned char *data,
+ UMAX_Status_Byte * s)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s0, s4;
+
+ DBG (80, "cread: cmd = %d, len = %lu\n", cmd, (u_long) len);
+
+ CHK (usync (scan, cmd | CMD_READ, len));
+
+ if (len > 0)
+ {
+ CHK (sanei_pv8630_wait_byte
+ (scan->fd, PV8630_RSTATUS, UMAX_OK_WITH_MOTOR, UMAX_STATUS_MASK,
+ 2000));
+
+ while (len > 0)
+ {
+ size_t req, n;
+
+ req = n = (len > 0xf000) ? 0xf000 : len;
+ CHK (sanei_pv8630_prep_bulkread (scan->fd, n));
+ CHK (sanei_pv8630_bulkread (scan->fd, data, &n));
+ if (n < req)
+ {
+ DBG (1, "qread: Expecting to read %lu, only got %lu\n", (u_long) req, (u_long) n);
+ return SANE_STATUS_IO_ERROR;
+ }
+ data += n;
+ len -= n;
+ }
+ }
+
+ CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RSTATUS, &s4));
+ CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RDATA, &s0));
+
+ DBG (90, "cwrite: s0 = %#x s4 = %#x\n", s0, s4);
+
+ if (s)
+ *s = s0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* Seems to be like cwrite, with a verification option */
+
+static SANE_Status
+cwritev (UMAX_Handle * scan, UMAX_Cmd cmd, size_t len, const unsigned char *data,
+ UMAX_Status_Byte * s)
+{
+ SANE_Status res;
+ unsigned char buf[16384];
+
+ /* Write out the opcode */
+
+ CHK (cwrite (scan, cmd, len, data, s));
+ if (len <= 0)
+ return SANE_STATUS_GOOD;
+
+ /* Read the opcode back */
+
+ CHK (cread (scan, cmd, len, buf, NULL));
+ if (bcmp (buf, data, len))
+ {
+ DBG (1, "cwritev: verification failed\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Send command */
+
+static SANE_Status
+csend (UMAX_Handle * scan, UMAX_Cmd cmd)
+{
+ DBG (80, "csend: cmd = %d\n", cmd);
+
+ return usync (scan, cmd, 0);
+}
+
+/* Lamp control */
+
+static SANE_Status
+cwritev_opc1_lamp_ctrl (UMAX_Handle * scan, UMAX_Lamp_State state)
+{
+ unsigned char opc1[16] = {
+ 0x01, 0x00, 0x01, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x13, 0x05,
+ 0x00, 0x00, 0x00, 0x80, 0xf0, 0x00
+ };
+
+ DBG (9, "cwritev_opc1: set lamp state = %s\n",
+ (state == UMAX_LAMP_OFF) ? "off" : "on");
+ opc1[14] = (state == UMAX_LAMP_OFF) ? 0x90 : 0xf0;
+ return cwritev (scan, CMD_2, 16, opc1, NULL);
+}
+
+
+/* Restore Head 1220U */
+
+static SANE_Status
+cwritev_opb3_restore (UMAX_Handle * scan)
+{
+ unsigned char opb3[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
+ 0xc1, 0x80, 0x00, 0x00, 0x04, 0x00, 0x16, 0x80, 0x15, 0x78,
+ 0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x1b, 0x1a,
+ };
+
+ SANE_Status res;
+
+ DBG (9, "cwritev_opb3_restore:\n");
+ CHK (cwritev (scan, CMD_8, 35, opb3, NULL));
+ CHK (csend (scan, CMD_40));
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Restore Head 2100U */
+
+static SANE_Status
+cwritev_opb3_restore_2100U (UMAX_Handle * scan)
+{
+ unsigned char opb3[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
+ 0xc1, 0x80, 0x00, 0x00, 0x04, 0x00, 0x16, 0x80, 0x15, 0x78,
+ 0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x0b, 0x1a, 0x00
+ };
+
+ SANE_Status res;
+
+ DBG (9, "cwritev_opb3_restore:\n");
+ CHK (cwritev (scan, CMD_8, 36, opb3, NULL));
+ CHK (csend (scan, CMD_40));
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Initialize and turn lamp on 1220U */
+
+/*
+This function seems to perform various things. First, it loads a default
+gamma information (which is used for the calibration scan), returns the
+head to the park position, and turns the lamp on. This function used to
+be split up into two parts, umaxinit and umaxinit2.
+*/
+
+static SANE_Status
+umaxinit (UMAX_Handle * scan)
+{
+ unsigned char opb[34] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
+ 0xc1, 0x80, 0x60, 0x20, 0x00, 0x00, 0x16, 0x41, 0xe0, 0xac,
+ 0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xf0
+ };
+ unsigned char opb1[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
+ 0xc1, 0x80, 0x00, 0x20, 0x02, 0x00, 0x16, 0x41, 0xe0, 0xac,
+ 0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x1a
+ };
+ unsigned char opb2[35] = {
+ 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
+ 0xc1, 0x80, 0x00, 0x20, 0x02, 0x00, 0x16, 0x41, 0xe0, 0xac,
+ 0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x1a
+ };
+ unsigned char opb4[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
+ 0xc1, 0x80, 0x60, 0x20, 0x00, 0x00, 0x16, 0x41, 0xe0, 0xac,
+ 0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0xf3, 0x1b
+ };
+ unsigned char opbx[35];
+ unsigned char opc[16] = {
+ 0x02, 0x80, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x07,
+ 0x00, 0x00, 0x00, 0x80, 0xf0, 0x00
+ };
+ unsigned char opcx[16];
+ unsigned char opd[8] = {
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa
+ };
+
+ SANE_Status res;
+ UMAX_Status_Byte s;
+ unsigned char ramp[800];
+ int i;
+ unsigned char *p;
+
+ DBG (3, "umaxinit called\n");
+
+ CHK (csend (scan, CMD_0));
+ CHK (xxxops (scan));
+
+ CHK (cwritev (scan, CMD_8, 34, opb, &s));
+ CHK (cread (scan, CMD_8, 35, opbx, &s));
+
+ CHK (cwritev (scan, CMD_8, 35, opb1, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "umaxinit: checkpoint 2:\n");
+
+ /* The following code appears to send three 256 entry, 8-bit gamma tables
+ * to the scanner
+ */
+ p = ramp;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ for (i = 0; i < 256; ++i)
+ *p++ = i;
+ for (i = 0; i < 256; ++i)
+ *p++ = i;
+ for (i = 0; i < 256; ++i)
+ *p++ = i;
+ *p++ = 0xaa;
+ *p++ = 0xaa;
+
+ res = cwritev (scan, CMD_4, p - ramp, ramp, &s);
+ if (res != SANE_STATUS_GOOD)
+ {
+ DBG (4, "umaxinit: Writing ramp 1 failed (is this a 2000U?)\n");
+ }
+ CHK (cwritev (scan, CMD_8, 35, opb1, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "umaxinit: checkpoint 3:\n");
+
+ /* The following code appears to send a 256 entry, 16-bit gamma table
+ * to the scanner
+ */
+ p = ramp;
+ for (i = 0; i < 256; ++i)
+ {
+ *p++ = i;
+ *p++ = 0;
+ }
+
+ res = cwrite (scan, CMD_4, p - ramp, ramp, &s);
+ if (res != SANE_STATUS_GOOD)
+ {
+ DBG (4, "umaxinit: Writing ramp 2 failed (is this a 2000U?)\n");
+ }
+ CHK (cwritev (scan, CMD_8, 35, opb2, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "umaxinit: checkpoint 4:\n");
+
+ /* The following code appears to send a 256 entry, 16-bit gamma table
+ * to the scanner.
+ */
+ p = ramp;
+ for (i = 0; i < 256; ++i)
+ {
+ *p++ = i;
+ *p++ = 4;
+ }
+
+ res = cwritev (scan, CMD_4, p - ramp, ramp, &s);
+ if (res != SANE_STATUS_GOOD)
+ {
+ DBG (4, "umaxinit: Writing ramp 3 failed (is this a 2000U?)\n");
+ }
+ CHK (cwritev (scan, CMD_8, 35, opb1, &s));
+
+ CHK (cwritev (scan, CMD_2, 16, opc, NULL));
+ CHK (cwritev (scan, CMD_1, 8, opd, NULL));
+ CHK (csend (scan, CMD_0));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "umaxinit: checkpoint 5: s = %#x\n", s);
+
+ if ((s & 0x40) == 0)
+ {
+ DBG (4, "umaxinit: turning on lamp and restoring\n");
+ CHK (cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON));
+ CHK (cwritev_opb3_restore (scan));
+
+ for (i = 0; i < 60; ++i)
+ {
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (4, "umaxinit: s = %#x\n", s);
+ if ((s & 0x40) != 0)
+ break;
+ DBG (4, "umaxinit: sleeping\n");
+ usleep (500000);
+ }
+ }
+
+ DBG (4, "umaxinit: checkpoint 6\n");
+
+ CHK (csend (scan, CMD_0));
+
+ /* The following stuff used to be in umaxinit2() */
+
+ DBG (4, "umaxinit: checkpoint 7\n");
+
+ CHK (xxxops (scan));
+ CHK (csend (scan, CMD_0));
+ CHK (xxxops (scan));
+
+ CHK (cwritev (scan, CMD_8, 34, opb4, &s));
+ CHK (cread (scan, CMD_8, 35, opbx, &s));
+ CHK (cread (scan, CMD_2, 16, opcx, &s));
+
+ CHK (cwritev (scan, CMD_2, 16, opc, NULL));
+ CHK (cwritev (scan, CMD_1, 8, opd, NULL));
+ CHK (csend (scan, CMD_0));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "umaxinit: checkpoint 8: s = %d\n", s);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Initialize and turn lamp on 2100U */
+
+static SANE_Status
+umaxinit_2100U (UMAX_Handle * scan)
+{
+
+ unsigned char opx[36];
+ unsigned char opy[16];
+
+
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ DBG (3, "umaxinit called\n");
+
+ CHK (xxxops (scan));
+ CHK (csend (scan, CMD_0));
+
+ /* Turn lamp on */
+
+ cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON);
+
+ CHK (cread (scan, CMD_8, 36, opx, &s));
+ CHK (cread (scan, CMD_2, 16, opy, &s));
+ CHK (csend (scan, CMD_0));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ CHK (csend (scan, CMD_0));
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Move head 1220U */
+
+static SANE_Status
+move (UMAX_Handle * scan, int distance, UMAX_Speed fine)
+{
+ unsigned char opc4[16] = {
+ 0x01, XXXX, XXXX, XXXX, 0x00, 0x00, 0x60, 0x2f, XXXX, XXXX,
+ 0x00, 0x00, 0x00, 0x80, XXXX, 0x00
+ };
+ unsigned char opb5[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xf6, 0x79, 0xbf,
+ 0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x13, 0x1a
+ };
+ unsigned char opb7[35] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xf6, 0x79, 0xbf,
+ 0x01, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x13, 0x1a
+ };
+
+ unsigned char ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+
+ unsigned char ope2[3] = {
+ 0x00, 0xff, 0x8f
+ };
+ unsigned char buf[512 + PAD];
+
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ SANE_Bool rev = distance < 0;
+ int skip = (rev ? -distance : distance) - 1;
+
+ DBG (9, "move: distance = %d, scanner_ypos = %d\n", distance,
+ scan->scanner_ypos);
+
+ PAD_ARRAY (buf, 512);
+
+ if (distance == 0)
+ return SANE_STATUS_GOOD;
+
+ opc4[1] = skip << 6;
+ opc4[2] = skip >> 2;
+ opc4[3] = (rev ? 0x20 : 0x70) + ((skip >> 10) & 0xf);
+ opc4[9] = rev ? 0x01 : 0x05;
+
+ if (fine == UMAX_FINE)
+ {
+ opc4[8] = 0x2f;
+ opc4[14] = 0xa4;
+ }
+ else
+ {
+ opc4[8] = 0x17;
+ opc4[14] = 0xac;
+ }
+
+ scan->scanner_ypos +=
+ (fine == UMAX_FINE ? distance : 2 * distance + (rev ? -1 : 1));
+ scan->scanner_ypos = (scan->scanner_ypos + (rev ? 0 : 3)) & ~3;
+
+ CHK (cwrite (scan, CMD_2, 16, opc4, &s));
+ CHK (cwrite (scan, CMD_8, 35, rev ? opb7 : opb5, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (10, "move: checkpoint 1: s = %d\n", s);
+
+ CHK (csend (scan, CMD_0));
+ if (rev)
+ CHK (cwrite (scan, CMD_4, 3, ope2, &s))
+ else
+ CHK (cwrite (scan, CMD_4, 8, ope, &s));
+
+
+ CHK (csend (scan, CMD_40));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (10, "move: checkpoint 2: s = %d\n", s);
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (10, "move: checkpoint 3: s = %d\n", s);
+
+ CHK (cread (scan, CMD_4, 512, buf, &s));
+
+ CHK_ARRAY (buf, 512);
+
+ return res;
+}
+
+
+
+/* Move head 2100U */
+
+static SANE_Status
+move_2100U (UMAX_Handle * scan, int distance, UMAX_Speed fine)
+{
+
+
+ unsigned char opc4[16] = {
+ 0x01, XXXX, XXXX, XXXX, 0x00, 0x00, 0x60, 0x2f, XXXX, XXXX,
+ 0x00, 0x00, 0x00, 0x80, XXXX, 0x00
+ };
+ unsigned char opb5[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xf6, 0x79, 0xbf,
+ 0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x03, 0x1a, 0x00
+ };
+ unsigned char opb7[36] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xf6, 0x79, 0xbf,
+ 0x01, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x03, 0x1a, 0x00
+ };
+ unsigned char ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+ unsigned char ope2[3] = {
+ 0x00, 0xff, 0xff
+ };
+ unsigned char buf[512];
+
+
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ SANE_Bool rev = distance < 0;
+ int skip = (rev ? -distance : distance) - 1;
+
+ DBG (9, "move: distance = %d, scanner_ypos = %d\n", distance,
+ scan->scanner_ypos);
+
+ PAD_ARRAY (buf, 512);
+
+ if (distance == 0)
+ return SANE_STATUS_GOOD;
+
+ opc4[1] = skip << 6;
+ opc4[2] = skip >> 2;
+ opc4[3] = (rev ? 0x20 : 0x70) + ((skip >> 10) & 0x0f);
+ opc4[9] = rev ? 0x01 : 0x05;
+
+ if (fine == UMAX_FINE)
+ {
+ opc4[8] = 0x2b;
+ opc4[14] = 0xa4;
+ }
+ else
+ {
+ opc4[8] = 0x15;
+ opc4[14] = 0xac;
+ }
+
+ scan->scanner_ypos +=
+ (fine == UMAX_FINE ? distance : 2 * distance + (rev ? -1 : 1));
+ scan->scanner_ypos = (scan->scanner_ypos + (rev ? 0 : 3)) & ~3;
+
+ CHK (cwrite (scan, CMD_2, 16, opc4, &s));
+ CHK (cwrite (scan, CMD_8, 36, rev ? opb7 : opb5, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (10, "move: checkpoint 1: s = %d\n", s);
+
+ CHK (csend (scan, CMD_0));
+
+ if (rev)
+ CHK (cwrite (scan, CMD_4, 3, ope2, &s))
+ else
+ CHK (cwrite (scan, CMD_4, 8, ope, &s));
+
+ CHK (csend (scan, CMD_40));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (10, "move: checkpoint 2: s = %d\n", s);
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (10, "move: checkpoint 3: s = %d\n", s);
+
+ CHK (cread (scan, CMD_4, 512, buf, &s));
+
+ CHK_ARRAY (buf, 512);
+
+ return res;
+}
+
+
+/* Get pixel image 1220U */
+
+static SANE_Status
+get_pixels (UMAX_Handle * scan, unsigned char *op2, unsigned char *op8,
+ unsigned char *op1, unsigned char *op4, int len, int zpos,
+ unsigned char *buf)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ DBG (9, "get_pixels: len = %d, zpos = %d\n", len, zpos);
+
+ if (zpos == 0)
+ CHK (csend (scan, CMD_0));
+
+ CHK (cwrite (scan, CMD_2, 16, op2, &s));
+ CHK (cwrite (scan, CMD_8, 35, op8, &s));
+ CHK (cwrite (scan, CMD_1, 8, op1, &s));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ if (zpos == 1)
+ CHK (csend (scan, CMD_0));
+
+ CHK (cwrite (scan, CMD_4, 8, op4, &s));
+ CHK (csend (scan, CMD_40));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ CHK (cread (scan, CMD_4, len, buf, &s));
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Get pixel image 2100U */
+
+static SANE_Status
+get_pixels_2100U (UMAX_Handle * scan, unsigned char *op2, unsigned char *op8,
+ unsigned char *op1, unsigned char *op4, int len, int zpos,
+ unsigned char *buf)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ DBG (9, "get_pixels: len = %d, zpos = %d\n", len, zpos);
+
+ CHK (cwrite (scan, CMD_2, 16, op2, &s));
+ CHK (cwrite (scan, CMD_8, 36, op8, &s));
+
+ if (zpos == 1)
+ CHK (cwritev (scan, CMD_1, 8, op1, &s))
+ else
+ CHK (cwrite (scan, CMD_1, 8, op1, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ if (zpos == 1)
+ CHK (csend (scan, CMD_0));
+
+ CHK (cwrite (scan, CMD_4, 8, op4, &s));
+ CHK (csend (scan, CMD_40));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ CHK (cread (scan, CMD_4, len, buf, &s));
+ return SANE_STATUS_GOOD;
+}
+
+
+/* This function locates the black stripe under scanner lid */
+
+static int
+locate_black_stripe (unsigned char *img, int w, int h)
+{
+ int epos, ecnt, x, y;
+ unsigned char *p;
+
+ epos = 0;
+ ecnt = 0;
+ p = img;
+ for (x = 0; x < w; ++x, ++p)
+ {
+ int d, dmax = 0, dpos = 0;
+ unsigned char *q = img + x;
+ for (y = 1; y < h; ++y, q += w)
+ {
+ d = q[0] - q[w];
+ if (d > dmax)
+ {
+ dmax = d;
+ dpos = y;
+ }
+ }
+ if (dmax > 0)
+ {
+ epos += dpos;
+ ++ecnt;
+ }
+ }
+ if (ecnt == 0)
+ epos = 70;
+ else
+ epos = (epos + ecnt / 2) / ecnt;
+ return epos;
+}
+
+
+/* To find the lowest head position 1220U */
+
+static SANE_Status
+find_zero (UMAX_Handle * scan)
+{
+ unsigned char opc3[16] = {
+ 0xb4, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x05,
+ 0x00, 0x00, 0x00, 0x80, 0xa4, 0x00
+ };
+ unsigned char ope1[8] = {
+ 0x00, 0x00, 0x00, 0xaa, 0xcc, 0xee, 0x80, 0xff
+ };
+ unsigned char opb6[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xfb, 0xc4, 0xe5,
+ 0x06, 0x00, 0x00, 0x60, 0x4d, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x13, 0x1a
+ };
+ unsigned char opd1[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, 0x08, 0x00
+ };
+
+ SANE_Status res;
+ int s;
+ unsigned char *img;
+
+ DBG (9, "find_zero:\n");
+
+ img = malloc (54000);
+ if (img == 0)
+ {
+ DBG (1, "out of memory (need 54000)\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ CHK (csend (scan, CMD_0));
+ CHK (get_pixels (scan, opc3, opb6, opd1, ope1, 54000, 1, img));
+
+#ifdef DEBUG_CALIBRATION
+ {
+ int w = 300, h = 180;
+ FILE *f2 = fopen ("find_zero.pgm", "wb");
+ fprintf (f2, "P5\n%d %d\n255\n", w, h);
+ fwrite (img, 1, w * h, f2);
+ fclose (f2);
+ }
+#endif
+
+ s = locate_black_stripe (img, 300, 180);
+ scan->scanner_yorg = scan->scanner_ypos + s + 64;
+ scan->scanner_ypos += 180 + 3;
+ scan->scanner_ypos &= ~3;
+
+ free (img);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* To find the lowest head position 2100U */
+
+static SANE_Status
+find_zero_2100U (UMAX_Handle * scan)
+{
+ unsigned char opc3[16] = {
+ 0xb4, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2b, 0x05,
+ 0x00, 0x00, 0x00, 0x80, 0xa4, 0x00
+ };
+ unsigned char ope1[8] = {
+ 0x00, 0x00, 0x00, 0xaa, 0xcc, 0xee, 0x80, 0xff
+ };
+ unsigned char opb6[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xfb, 0xc4, 0xe5,
+ 0x06, 0x00, 0x00, 0x60, 0x4d, 0xa0, 0x00, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x03, 0x1a, 0x00
+ };
+ unsigned char opd1[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x1b, 0x00, 0x08, 0x00
+ };
+
+ SANE_Status res;
+ int s;
+ unsigned char *img;
+
+ DBG (9, "find_zero:\n");
+
+ img = malloc (54000);
+ if (img == 0)
+ {
+ DBG (1, "out of memory (need 54000)\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ CHK (csend (scan, CMD_0));
+ CHK (get_pixels_2100U (scan, opc3, opb6, opd1, ope1, 54000, 1, img));
+
+#ifdef DEBUG_CALIBRATION
+ {
+ int w = 300, h = 180;
+ FILE *f2 = fopen ("find_zero.pgm", "wb");
+ fprintf (f2, "P5\n%d %d\n255\n", w, h);
+ fwrite (img, 1, w * h, f2);
+ fclose (f2);
+ }
+#endif
+
+ s = locate_black_stripe (img, 300, 180);
+ scan->scanner_yorg = scan->scanner_ypos + s + 64;
+ scan->scanner_ypos += 180 + 3;
+ scan->scanner_ypos &= ~3;
+
+ free (img);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Calibration 1220U */
+
+/*
+Format of caldata:
+
+ 5100 bytes of CCD calibration values
+ 5100 bytes of CCD calibration values
+ 5100 bytes of CCD calibration values
+ 256 bytes of gamma data for blue
+ 256 bytes of gamma data for green
+ 256 bytes of gamma data for red
+ 2 bytes of extra information
+
+*/
+static SANE_Status
+get_caldata (UMAX_Handle * scan, int color)
+{
+ unsigned char opc9[16] = {
+ XXXX, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x17, 0x05,
+ 0xec, 0x4e, 0x0c, XXXX, 0xac
+ };
+ unsigned char opb11[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xad, 0xa0, 0x49,
+ 0x06, 0x00, 0x00, XXXX, XXXX, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, 0x93, 0x1b
+ };
+
+ unsigned char ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+
+ unsigned char opd4[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, XXXX, XXXX
+ };
+ SANE_Status res;
+
+ unsigned char *p;
+ int h = 66;
+ int w = color ? 3 * 5100 : 5100;
+ int x0 = color ? 0 : 5100;
+ int l = w * h;
+ int i, x, y;
+
+ PAD_ARRAY (scan->caldata, 16070);
+
+ DBG (9, "get_caldata: color = %d\n", color);
+
+ p = malloc (l);
+ if (p == 0)
+ {
+ DBG (1, "out of memory (need %d)\n", l);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (scan->caldata, 0, 3 * 5100);
+
+ CHK (csend (scan, CMD_0));
+ opc9[0] = h + 4;
+ if (color)
+ {
+ opc9[13] = 0x03;
+ opb11[23] = 0xc4;
+ opb11[24] = 0x5c;
+ opd4[6] = 0x08;
+ opd4[7] = 0x00;
+ }
+ else
+ {
+ opc9[13] = 0xc3;
+ opb11[23] = 0xec;
+ opb11[24] = 0x54;
+ opd4[6] = 0x0c;
+ opd4[7] = 0x40;
+ }
+
+ /* Do a test scan of the calibration strip (which is located
+ * under the scanner's lid */
+
+ CHK (get_pixels (scan, opc9, opb11, opd4, ope, l, 0, p));
+
+#ifdef DEBUG_CALIBRATION
+ {
+ FILE *f2 = fopen ("calibration.pgm", "wb");
+ fprintf (f2, "P5\n%d %d\n255\n", w, h);
+ fwrite (p, 1, w * h, f2);
+ fclose (f2);
+ }
+#endif
+
+ scan->scanner_ypos += (h + 4) * 2 + 3;
+ scan->scanner_ypos &= ~3;
+
+ /* The following loop computes the gain for each of the CCD pixel
+ * elements.
+ */
+ for (x = 0; x < w; ++x)
+ {
+ int t = 0, gn;
+ double av, gain;
+
+ for (y = 0; y < h; ++y)
+ t += p[x + y * w];
+ av = (double) t / h;
+ gain = 250 / av;
+ gn = (int) ((gain - 0.984) * 102.547 + 0.5);
+ if (gn < 0)
+ gn = 0;
+ else if (gn > 255)
+ gn = 255;
+ scan->caldata[x + x0] = gn;
+ }
+
+ /* Gamma table for blue */
+
+ for (i = 0; i < 256; ++i)
+ scan->caldata[i + 3 * 5100 + 0] = i;
+
+ /* Gamma table for green */
+
+ for (i = 0; i < 256; ++i)
+ scan->caldata[i + 3 * 5100 + 256] = i;
+
+ /* Gamma table for red */
+
+ for (i = 0; i < 256; ++i)
+ scan->caldata[i + 3 * 5100 + 512] = i;
+
+ free (p);
+
+ CHK_ARRAY (scan->caldata, 16070);
+ return SANE_STATUS_GOOD;
+}
+
+/* Calibration 2100U */
+
+/*
+Format of caldata:
+
+ 5100 bytes of CCD calibration values
+ 5100 bytes of CCD calibration values
+ 5100 bytes of CCD calibration values
+ 256 bytes of gamma data for blue
+ 256 bytes of gamma data for green
+ 256 bytes of gamma data for red
+ 2 bytes of extra information
+
+*/
+static SANE_Status
+get_caldata_2100U (UMAX_Handle * scan, int color)
+{
+ unsigned char opc9[16] = {
+ XXXX, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x15, 0x05,
+ XXXX, XXXX, XXXX, XXXX, 0xac, 0x00
+ };
+ unsigned char opb11[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, XXXX, XXXX, 0x46,
+ 0x06, 0x00, 0x00, XXXX, XXXX, 0xa0, 0x00, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x83, XXXX, 0x00
+ };
+ unsigned char opd4[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x1b, 0x00, XXXX, XXXX
+ };
+ unsigned char ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+
+
+/* default gamma translation table */
+ unsigned char ggamma[256] = {
+ 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x12, 0x14, 0x17, 0x19, 0x1B, 0x1D,
+ 0x1F, 0x21, 0x23, 0x24, 0x26, 0x28, 0x2A, 0x2B, 0x2D, 0x2E, 0x30,
+ 0x31, 0x33, 0x34, 0x36, 0x37, 0x39, 0x3A, 0x3B, 0x3D, 0x3E, 0x40,
+ 0x41, 0x42, 0x43, 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4F, 0x50, 0x51, 0x52, 0x53, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
+ 0x5B, 0x5C, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
+ 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86,
+ 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x90,
+ 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A,
+ 0x9B, 0x9C, 0x9D, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA2, 0xA3,
+ 0xA4, 0xA5, 0xA6, 0xA7, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAC,
+ 0xAD, 0xAE, 0xAF, 0xB0, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBC, 0xBD, 0xBE,
+ 0xBF, 0xBF, 0xC0, 0xC1, 0xC2, 0xC2, 0xC3, 0xC4, 0xC5, 0xC5, 0xC6,
+ 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCB, 0xCC, 0xCD, 0xCE, 0xCE,
+ 0xCF, 0xD0, 0xD1, 0xD1, 0xD2, 0xD3, 0xD4, 0xD4, 0xD5, 0xD6, 0xD6,
+ 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC, 0xDC, 0xDD, 0xDE, 0xDE,
+ 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE3, 0xE4, 0xE5, 0xE6, 0xE6,
+ 0xE7, 0xE8, 0xE8, 0xE9, 0xEA, 0xEA, 0xEB, 0xEC, 0xEC, 0xED, 0xEE,
+ 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3, 0xF4, 0xF5, 0xF5,
+ 0xF6, 0xF7, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFE, 0xFF
+ };
+
+
+ SANE_Status res;
+
+ unsigned char *p;
+ int h = 66;
+ int w = color ? 3 * 5100 : 5100;
+ int x0 = color ? 0 : 5100;
+ int l = w * h;
+ int i, x, y;
+ int t, gn;
+ double av, pct;
+
+ PAD_ARRAY (scan->caldata, 16070);
+
+ DBG (9, "get_caldata: color = %d\n", color);
+
+ p = malloc (l);
+ if (p == 0)
+ {
+ DBG (1, "out of memory (need %d)\n", l);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (scan->caldata, 0, 3 * 5100);
+
+ CHK (csend (scan, CMD_0));
+ CHK (csend (scan, CMD_0));
+
+ opc9[0] = h + 4;
+
+ if (color)
+ {
+ opc9[10] = 0xb6;
+ opc9[11] = 0x3b;
+ opc9[12] = 0x0c;
+ opc9[13] = 0x03;
+ opb11[17] = 0x7e;
+ opb11[18] = 0xb0;
+ opb11[23] = 0xc4;
+ opb11[24] = 0x5c;
+ opb11[34] = 0x1b;
+ opd4[6] = 0x0f;
+ opd4[7] = 0x40;
+ }
+ else
+ {
+ opc9[10] = 0xa6;
+ opc9[11] = 0x2a;
+ opc9[12] = 0x08;
+ opc9[13] = 0xc2;
+ opb11[17] = 0x7f;
+ opb11[18] = 0xc0;
+ opb11[23] = 0xec;
+ opb11[24] = 0x54;
+ opb11[34] = 0x1a;
+ opd4[6] = 0x06;
+ opd4[7] = 0x20;
+ }
+
+ /* Do a test scan of the calibration strip (which is located
+ * under the scanner's lid */
+ CHK (get_pixels_2100U (scan, opc9, opb11, opd4, ope, l, 0, p));
+
+#ifdef DEBUG_CALIBRATION
+ {
+ FILE *f2 = fopen ("calibration.pgm", "wb");
+ fprintf (f2, "P5\n%d %d\n255\n", w, h);
+ fwrite (p, 1, w * h, f2);
+ fclose (f2);
+ }
+#endif
+
+ scan->scanner_ypos += (h + 4) * 2 + 3;
+ scan->scanner_ypos &= ~3;
+
+ /* The following loop computes the gain for each of the CCD pixel
+ * elements.
+ */
+ for (x = 0; x < w; x++)
+ {
+ t = 0;
+ for (y = 0; y < h; y++)
+ t += p[x + y * w];
+ av = (double) t / h;
+ pct = 100.0 - (av * 100.0) / 250;
+ gn = (int) (pct / 0.57);
+
+ pct = gn;
+ av = exp((-pct)/50)*2.5+0.9;
+ gn = gn * av;
+
+
+ if (gn < 0)
+ gn = 0;
+ else if (gn > 127)
+ gn = 127;
+ scan->caldata[x + x0] = gn;
+ }
+
+ /* Gamma table for blue */
+
+ for (i = 0; i < 256; i++)
+ scan->caldata[i + 3 * 5100 + 0] = ggamma[i];
+
+ /* Gamma table for green */
+
+ for (i = 0; i < 256; i++)
+ scan->caldata[i + 3 * 5100 + 256] = ggamma[i];
+
+ /* Gamma table for red */
+
+ for (i = 0; i < 256; i++)
+ scan->caldata[i + 3 * 5100 + 512] = ggamma[i];
+
+ free (p);
+
+ CHK_ARRAY (scan->caldata, 16070);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* Sends scan user parameters from frontend 1220U */
+
+static SANE_Status
+send_scan_parameters (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ /* Appears to correspond to opscan in umax_pp_low.c */
+ unsigned char opbgo[35] = {
+ 0x00, 0x00, 0xb0, 0x4f, 0xd8, 0xe7, 0xfa, 0x10, 0xef, 0xc4,
+ 0x3c, 0x71, 0x0f, 0x00, 0x04, 0x00, 0x6e, XXXX, XXXX, XXXX,
+ 0xc4, 0x7e, 0x00, XXXX, XXXX, 0xa0, 0x0a, 0x8b, 0x49, 0x4a,
+ 0xd0, 0x68, 0xdf, XXXX, 0x1a
+ };
+
+ /* Appears to correspond to opsc53 in umax_pp_low.c */
+ unsigned char opcgo[16] = {
+ XXXX, XXXX, XXXX, XXXX, 0xec, 0x03, XXXX, XXXX, XXXX, XXXX,
+ 0xec, 0x4e, XXXX, XXXX, XXXX
+ };
+
+ /* Appears to correspond to opsc04 in umax_pp_low.c */
+ unsigned char opdgo[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, XXXX, XXXX
+ };
+
+ unsigned char subsamp[9] = {
+ 0xff, 0xff, 0xaa, 0xaa, 0x88, 0x88, 0x88, 0x88, 0x80
+ };
+
+ const int xend =
+ scan->xskip + scan->w * scan->xsamp + (scan->xsamp + 1) / 2;
+ const int ytot = scan->hexp * scan->ysamp + 12;
+
+ opbgo[17] = scan->xskip % 256;
+ opbgo[18] = ((scan->xskip >> 8) & 0xf) + (xend << 4);
+ opbgo[19] = xend >> 4;
+ opbgo[33] = 0x33 + ((xend & 0x1000) >> 5) + ((scan->xskip & 0x1000) >> 6);
+
+ /* bytes per line */
+
+ opbgo[23] = scan->color ? 0xc6 : 0x77;
+ opbgo[24] = scan->color ? 0x5b : 0x4a;
+
+ /* Scan height */
+
+ opcgo[0] = ytot;
+ opcgo[1] = ((ytot >> 8) & 0x3f) + (scan->yskip << 6);
+ opcgo[2] = scan->yskip >> 2;
+ opcgo[3] = 0x50 + ((scan->yskip >> 10) & 0xf);
+
+ /* This is what used to be here:
+
+ opcgo[6] = bh == h? 0: 0x60; // a guess
+
+ I replaced it with what umax_pp_low.c uses, since it
+ made more sense
+ */
+ opcgo[6] = (scan->ydpi <= 300) ? 0x00 : 0x60;
+ opcgo[8] = (scan->ydpi <= 300) ? 0x17 : 0x2F;
+ opcgo[9] = (scan->ydpi >= 300) ? 0x05 : 0x07;
+ opcgo[14] = (scan->ydpi == 600) ? 0xa4 : 0xac;
+
+ opcgo[7] = scan->color ? 0x2F : 0x40;
+ opcgo[12] = scan->color ? 0x10 : 0x0C;
+ opcgo[13] = scan->color ? 0x04 : 0xc3;
+
+ opdgo[6] = scan->color ? 0x88 : 0x8c;
+ opdgo[7] = scan->color ? 0x00 : 0x40;
+
+ DBG (3, "send_scan_parameters: xskip = %d, yskip = %d\n", scan->xskip,
+ scan->yskip);
+
+ CHK (csend (scan, CMD_0));
+ CHK (csend (scan, CMD_0));
+ CHK (cwritev (scan, CMD_2, 16, opcgo, &s));
+ CHK (cwritev (scan, CMD_8, 35, opbgo, &s));
+ CHK (cwritev (scan, CMD_1, 8, opdgo, &s));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (4, "send_scan_parameters: checkpoint 1: s = %d\n", s);
+
+ /* Loads the new calibration data (that was computed by get_caldata) into the
+ scanner */
+
+ scan->caldata[16068] = subsamp[scan->xsamp];
+ scan->caldata[16069] = subsamp[scan->ysamp];
+ CHK (cwrite (scan, CMD_4, 16070, scan->caldata, &s));
+
+ CHK (csend (scan, CMD_40));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "send_scan_parameters: checkpoint 2: s = %d\n", s);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Sends scan user parameters from frontend 2100U */
+
+static SANE_Status
+send_scan_parameters_2100U (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+ int bpl;
+
+ /* Appears to correspond to opscan in umax_pp_low.c */
+ unsigned char opbgo[36] = {
+ 0x00, 0x00, 0xb0, 0x4f, 0xd8, 0xe7, 0xfa, 0x10, 0xef, 0xc4,
+ 0x3c, 0x71, 0x0f, 0x00, 0x04, 0x00, 0x6e, XXXX, XXXX, XXXX,
+ 0xc4, 0x7e, 0x00, XXXX, XXXX, 0xa0, 0x0a, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, XXXX, 0x1a, 0x00
+ };
+
+ /* Appears to correspond to opsc53 in umax_pp_low.c */
+ unsigned char opcgo[16] = {
+ XXXX, XXXX, XXXX, XXXX, 0xec, 0x03, 0x60, XXXX, XXXX, XXXX,
+ XXXX, XXXX, XXXX, XXXX, XXXX, 0x00
+ };
+
+ /* Appears to correspond to opsc04 in umax_pp_low.c */
+ unsigned char opdgo[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x1b, 0x00, XXXX, XXXX
+ };
+
+ unsigned char subsamp[9] = {
+ 0xff, 0xff, 0xaa, 0xaa, 0x88, 0x88, 0x88, 0x88, 0x80
+ };
+
+ const int xend =
+ scan->xskip + scan->w * scan->xsamp + (scan->xsamp + 1) / 2;
+ const int ytot = scan->hexp * scan->ysamp + 12;
+
+ opbgo[17] = scan->xskip % 256;
+ opbgo[18] = ((scan->xskip >> 8) & 0x0f) + (xend << 4);
+ opbgo[19] = xend >> 4;
+ opbgo[33] = 0x23 + ((xend & 0x1000) >> 5) + ((scan->xskip & 0x1000) >> 6);
+
+ /* bytes per line */
+
+ bpl = (scan->color ? 3 : 1) * scan->w * scan->xdpi;
+ opbgo[23] = bpl % 256;
+ opbgo[24] = 0x41 + ((bpl / 256) & 0x1f);
+
+ /* Scan height */
+
+ opcgo[0] = ytot;
+ opcgo[1] = ((ytot >> 8) & 0x3f) + (scan->yskip << 6);
+ opcgo[2] = (scan->yskip >> 2);
+ opcgo[3] = 0x50 + ((scan->yskip >> 10) & 0x0f);
+
+
+ opcgo[6] = (scan->ydpi <= 300) ? 0x00 : 0x60;
+ opcgo[8] = (scan->ydpi <= 300) ? 0x17 : 0x2F;
+ opcgo[9] = (scan->ydpi >= 300) ? 0x05 : 0x07;
+ opcgo[14] = (scan->ydpi == 600) ? 0xa4 : 0xac;
+
+
+ opcgo[7] = scan->color ? 0x2f : 0x40;
+ opcgo[10] = scan->color ? 0xb6 : 0xa6;
+ opcgo[11] = scan->color ? 0x3b : 0x2a;
+ opcgo[12] = scan->color ? 0x0c : 0x08;
+ opcgo[13] = scan->color ? 0x03 : 0xc2;
+
+ opdgo[6] = scan->color ? 0x8f : 0x86;
+ opdgo[7] = scan->color ? 0x40 : 0x20;
+
+ DBG (3, "send_scan_parameters: xskip = %d, yskip = %d\n", scan->xskip,
+ scan->yskip);
+
+ CHK (csend (scan, CMD_0));
+ CHK (csend (scan, CMD_0));
+ CHK (cwritev (scan, CMD_2, 16, opcgo, &s));
+ CHK (cwritev (scan, CMD_8, 36, opbgo, &s));
+ CHK (cwritev (scan, CMD_1, 8, opdgo, &s));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (4, "send_scan_parameters: checkpoint 1: s = %d\n", s);
+
+ /* Loads the new calibration data (that was computed by get_caldata) into the
+ scanner */
+
+ scan->caldata[16068] = subsamp[scan->xsamp];
+ scan->caldata[16069] = subsamp[scan->ysamp];
+ CHK (cwrite (scan, CMD_4, 16070, scan->caldata, &s));
+
+ CHK (csend (scan, CMD_40));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+
+ DBG (4, "send_scan_parameters: checkpoint 2: s = %d\n", s);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Read raw data */
+
+static SANE_Status
+read_raw_data (UMAX_Handle * scan, unsigned char *data, int len)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ CHK (cread (scan, CMD_4, len, data, &s));
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Read raw strip color */
+
+static SANE_Status
+read_raw_strip_color (UMAX_Handle * scan)
+{
+ /**
+ yres = 75 => ydpi = 150 => ysamp = 2 => yoff_scale = 8
+ yres = 150 => ydpi = 150 => ysamp = 1 => yoff_scale = 4
+ yres = 300 => ydpi = 300 => ysamp = 1 => yoff_scale = 2
+ yres = 600 => ydpi = 600 => ysamp = 1 => yoff_scale = 1
+ */
+
+ const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
+ const int linelen = 3 * scan->w;
+
+ /*
+ yoff_scale = 8 => roff = 5 * w, goff = 1 * w, boff = 0 * w, hextra = 1
+ yoff_scale = 4 => roff = 8 * w, goff = 4 * w, boff = 0 * w, hextra = 2
+ yoff_scale = 2 => roff = 14 * w, goff = 7 * w, boff = 0 * w, hextra = 4
+ yoff_scale = 1 => roff = 26 * w, goff = 13 * w, boff = 0 * w, hextra = 8
+ */
+
+ const int hextra = 8 / yoff_scale;
+
+ SANE_Status res;
+ int lines_to_read = scan->hexp;
+
+ DBG (9, "read_raw_strip_color: hexp = %d, bh = %d\n", scan->hexp, scan->bh);
+
+ if (scan->maxh == -1)
+ {
+ DBG (10, "read_raw_strip_color: filling buffer for the first time\n");
+ if (lines_to_read > scan->bh)
+ lines_to_read = scan->bh;
+
+ CHK (read_raw_data (scan, scan->p, lines_to_read * linelen));
+ scan->maxh = lines_to_read - hextra;
+ }
+ else
+ {
+ DBG (10, "read_raw_strip_color: reading new rows into buffer\n");
+ memmove (scan->p, scan->p + (scan->bh - hextra) * linelen,
+ hextra * linelen);
+
+ if (lines_to_read > (scan->bh - hextra))
+ lines_to_read = scan->bh - hextra;
+
+ CHK (read_raw_data
+ (scan, scan->p + hextra * linelen, lines_to_read * linelen));
+ scan->maxh = lines_to_read;
+ }
+
+ scan->hexp -= lines_to_read;
+ scan->x = 0;
+ scan->y = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Read raw strip grey */
+
+static SANE_Status
+read_raw_strip_gray (UMAX_Handle * scan)
+{
+ const int linelen = scan->w;
+
+ SANE_Status res;
+
+ int lines_to_read = scan->bh;
+
+ DBG (9, "read_raw_strip_gray: hexp = %d\n", scan->hexp);
+
+ if (lines_to_read > scan->hexp)
+ lines_to_read = scan->hexp;
+ scan->hexp -= lines_to_read;
+
+ CHK (read_raw_data (scan, scan->p, lines_to_read * linelen));
+
+ scan->maxh = lines_to_read;
+ scan->x = 0;
+ scan->y = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Read raw strip */
+
+static SANE_Status
+read_raw_strip (UMAX_Handle * scan)
+{
+ if (scan->color)
+ return read_raw_strip_color (scan);
+ else
+ return read_raw_strip_gray (scan);
+}
+
+/* Set scan user pamaters Frontend */
+
+static SANE_Status
+UMAX_set_scan_parameters (UMAX_Handle * scan,
+ const int color,
+ const int xo,
+ const int yo,
+ const int w,
+ const int h, const int xres, const int yres)
+{
+
+ /* Validate the input parameters */
+
+ int left = xo;
+ int top = yo;
+ int right = xo + w * 600 / xres;
+ int bottom = yo + h * 600 / yres;
+
+ DBG (2, "UMAX_set_scan_parameters:\n");
+ DBG (2, "color = %d \n", color);
+ DBG (2, "xo = %d, yo = %d\n", xo, yo);
+ DBG (2, "w = %d, h = %d\n", w, h);
+ DBG (2, "xres = %d, yres = %d\n", xres, yres);
+ DBG (2, "left = %d, top = %d\n", left, top);
+ DBG (2, "right = %d, bottom = %d\n", right, bottom);
+
+ if ((left < 0) || (right > UMAX_MAX_WIDTH))
+ return SANE_STATUS_INVAL;
+
+ if ((top < 0) || (bottom > UMAX_MAX_HEIGHT))
+ return SANE_STATUS_INVAL;
+
+ if (((right - left) < 10) || ((bottom - top) < 10))
+ return SANE_STATUS_INVAL;
+
+ if ((xres != 75) && (xres != 150) && (xres != 300) && (xres != 600))
+ return SANE_STATUS_INVAL;
+
+ if ((yres != 75) && (yres != 150) && (yres != 300) && (yres != 600))
+ return SANE_STATUS_INVAL;
+
+ /* If we get this far, begin initializing the data
+ structure
+ */
+
+ scan->color = color;
+ scan->w = w;
+ scan->h = h;
+ scan->xo = xo;
+ scan->yo = yo;
+
+ /*
+ The scanner has a fixed X resolution of 600 dpi, but
+ supports three choices for the Y resolution. We must
+ choose an appropriate physical resolution and the
+ corresponding sampling value.
+
+ It is not clear to me why the choice depends on
+ whether we are scanning in color or not, but the
+ original code did this and I didn't want to mess
+ with it.
+
+ Physical X resolution choice:
+ xres = 75 => xdpi = 600 (xsamp = 8)
+ xres = 150 => xdpi = 600 (xsamp = 4)
+ xres = 300 => xdpi = 600 (xsamp = 2)
+ xres = 600 => xdpi = 600 (xsamp = 1)
+
+ Physical Y resolution choice (if color):
+ yres = 75 => ydpi = 150 (ysamp = 2)
+ yres = 150 => ydpi = 150 (ysamp = 1)
+ yres = 300 => ydpi = 300 (ysamp = 1)
+ yres = 600 => ydpi = 600 (ysamp = 1)
+
+ Physical Y resolution choice (if not color):
+ yres = 75 => ydpi = 300 (ysamp = 4)
+ yres = 150 => ydpi = 300 (ysamp = 2)
+ yres = 300 => ydpi = 300 (ysamp = 1)
+ yres = 600 => ydpi = 600 (ysamp = 1)
+ */
+
+ scan->xdpi = 600;
+ if (yres <= 150 && color)
+ scan->ydpi = 150;
+ else if (yres > 300)
+ scan->ydpi = 600;
+ else
+ scan->ydpi = 300;
+
+ scan->xsamp = scan->xdpi / xres;
+ scan->ysamp = scan->ydpi / yres;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Start actual scan 1220U */
+
+static SANE_Status
+UMAX_start_scan (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ int linelen;
+ int yd;
+
+ DBG (3, "UMAX_start_scan called\n");
+
+ if (scan->color)
+ {
+ const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
+ const int hextra = 8 / yoff_scale;
+
+ linelen = 3 * scan->w;
+ scan->hexp = scan->h + hextra;
+ }
+ else
+ {
+ linelen = scan->w;
+ scan->hexp = scan->h;
+ }
+
+ scan->bh = BUFFER_SIZE / linelen;
+
+ scan->p = malloc (scan->bh * linelen);
+ if (scan->p == 0)
+ return SANE_STATUS_NO_MEM;
+
+ DBG (4, "UMAX_start_scan: bh = %d, linelen = %d\n", scan->bh, linelen);
+
+ scan->maxh = -1;
+ scan->done = 0;
+
+ /* Initialize the scanner and position the scan head */
+
+ CHK (umaxinit (scan));
+
+ /* This scans in the black and white calibration strip that
+ * is located under the scanner's lid. The scan of that strip
+ * is used to pick correct values for the CCD calibration
+ * values
+ */
+
+ scan->scanner_ypos = 0;
+ CHK (move (scan, 196, UMAX_NOT_FINE));
+ CHK (find_zero (scan));
+ CHK (move (scan, scan->scanner_yorg - 232 - scan->scanner_ypos, UMAX_FINE));
+ CHK (get_caldata (scan, scan->color));
+
+ /* This moves the head back to the starting position */
+
+ yd = scan->scanner_yorg + scan->yo - scan->scanner_ypos;
+ if (yd < 0)
+ CHK (move (scan, yd, UMAX_FINE));
+ if (yd > 300)
+ CHK (move (scan, (yd - 20) / 2, UMAX_NOT_FINE));
+ yd = scan->scanner_yorg + scan->yo - scan->scanner_ypos;
+
+ scan->yskip = yd / (600 / scan->ydpi);
+ scan->xskip = scan->xo / (600 / scan->xdpi);
+
+ /* Read in the first chunk of raw data */
+
+ CHK (send_scan_parameters (scan));
+ CHK (read_raw_strip (scan));
+
+ DBG (4, "UMAX_start_scan successful\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Start actual scan 2100U */
+
+static SANE_Status
+UMAX_start_scan_2100U (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ int linelen;
+ int yd;
+
+ DBG (3, "UMAX_start_scan called\n");
+
+ if (scan->color)
+ {
+ const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
+ const int hextra = 8 / yoff_scale;
+
+ linelen = 3 * scan->w;
+ scan->hexp = scan->h + hextra;
+ }
+ else
+ {
+ linelen = scan->w;
+ scan->hexp = scan->h;
+ }
+
+ scan->bh = BUFFER_SIZE / linelen;
+
+ scan->p = malloc (scan->bh * linelen);
+ if (scan->p == 0)
+ return SANE_STATUS_NO_MEM;
+
+ DBG (4, "UMAX_start_scan: bh = %d, linelen = %d\n", scan->bh, linelen);
+
+ scan->maxh = -1;
+ scan->done = 0;
+
+ /* Initialize the scanner and position the scan head */
+
+ CHK (umaxinit_2100U (scan));
+
+ /* This scans in the black and white calibration strip that
+ * is located under the scanner's lid. The scan of that strip
+ * is used to pick correct values for the CCD calibration
+ * values
+ */
+
+ scan->scanner_ypos = 0;
+ CHK (move_2100U (scan, 196, UMAX_NOT_FINE));
+ CHK (find_zero_2100U (scan));
+ CHK (move_2100U (scan, scan->scanner_yorg - 232 - scan->scanner_ypos, UMAX_FINE));
+ CHK (get_caldata_2100U (scan, scan->color));
+
+ /* This moves the head back to the starting position */
+
+ yd = scan->scanner_yorg + scan->yo - scan->scanner_ypos;
+ if (yd < 0)
+ CHK (move_2100U (scan, yd, UMAX_FINE));
+ if (yd > 300)
+ CHK (move_2100U (scan, (yd - 20) / 2, UMAX_NOT_FINE));
+ yd = scan->scanner_yorg + scan->yo - scan->scanner_ypos;
+
+ scan->yskip = yd / (600 / scan->ydpi);
+ scan->xskip = scan->xo / (600 / scan->xdpi);
+
+ /* Read in the first chunk of raw data */
+
+ CHK (send_scan_parameters_2100U (scan));
+ CHK (read_raw_strip (scan));
+
+ DBG (4, "UMAX_start_scan successful\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Set lamp state */
+
+static SANE_Status
+UMAX_set_lamp_state (UMAX_Handle * scan, UMAX_Lamp_State state)
+{
+ SANE_Status res;
+
+ DBG (3, "UMAX_set_lamp_state: state = %d\n", (int) state);
+
+ CHK (csend (scan, CMD_0));
+ CHK (cwritev_opc1_lamp_ctrl (scan, state));
+ return SANE_STATUS_GOOD;
+}
+
+/* Park head 1220U */
+
+static SANE_Status
+UMAX_park_head (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+ int i;
+
+ DBG (3, "UMAX_park_head called\n");
+
+ CHK (csend (scan, CMD_0));
+ /* WARNING: Must call cwritev_opc1_lamp_ctrl before cwritev_opb3_restore,
+ * otherwise the head moves the wrong way and makes ugly grinding noises. */
+
+ CHK (cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON));
+ CHK (cwritev_opb3_restore (scan));
+
+ for (i = 0; i < 60; ++i)
+ {
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (4, "UMAX_park_head: s = %#x\n", s);
+ if ((s & 0x40) != 0)
+ break;
+ DBG (4, "UMAX_park_head: sleeping\n");
+ usleep (500000);
+ }
+
+ scan->scanner_ypos = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Park head 2100U */
+
+static SANE_Status
+UMAX_park_head_2100U (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+ int i;
+
+ DBG (3, "UMAX_park_head called\n");
+
+ CHK (csend (scan, CMD_0));
+ /* WARNING: Must call cwritev_opc1_lamp_ctrl before cwritev_opb3_restore,
+ * otherwise the head moves the wrong way and makes ugly grinding noises. */
+
+ CHK (cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON));
+ CHK (cwritev_opb3_restore_2100U (scan));
+
+ for (i = 0; i < 60; ++i)
+ {
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ DBG (4, "UMAX_park_head: s = %#x\n", s);
+ if ((s & 0x40) != 0)
+ break;
+ DBG (4, "UMAX_park_head: sleeping\n");
+ usleep (500000);
+ }
+
+ /* CHK (csend (scan, CMD_0));
+ CHK (csend (scan, CMD_0)); */
+
+ scan->scanner_ypos = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Finish scan */
+
+static SANE_Status
+UMAX_finish_scan (UMAX_Handle * scan)
+{
+ DBG (3, "UMAX_finish_scan:\n");
+
+ if (scan->p)
+ free (scan->p);
+ scan->p = NULL;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* RGB decoding for a color scan */
+
+static SANE_Status
+UMAX_get_rgb (UMAX_Handle * scan, unsigned char *rgb)
+{
+
+ if (scan->color)
+ {
+ const int linelen = 3 * scan->w;
+ const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
+ const int roff = (8 / yoff_scale * 3 + 2) * scan->w;
+ const int goff = (4 / yoff_scale * 3 + 1) * scan->w;
+ const int boff = 0;
+
+ unsigned char *base = scan->p + (scan->y * linelen) + scan->x;
+
+ rgb[0] = base[roff];
+ rgb[1] = base[goff];
+ rgb[2] = base[boff];
+ }
+ else
+ {
+ const int linelen = scan->w;
+ unsigned char *base = scan->p + (scan->y * linelen) + (scan->x);
+
+ rgb[0] = base[0];
+ rgb[1] = base[0];
+ rgb[2] = base[0];
+ }
+
+ if (!(((scan->x + 1) == scan->w) && ((scan->y + 1) == scan->maxh)))
+ {
+ ++scan->x;
+ if (scan->x == scan->w)
+ {
+ ++scan->y;
+ scan->x = 0;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ if (scan->hexp <= 0)
+ {
+ DBG (4, "UMAX_get_rgb: setting done flag\n");
+ scan->done = 1;
+ return SANE_STATUS_GOOD;
+ }
+
+ return read_raw_strip (scan);
+}
+
+/* Close device */
+
+static SANE_Status
+UMAX_close_device (UMAX_Handle * scan)
+{
+ DBG (3, "UMAX_close_device:\n");
+ sanei_usb_close (scan->fd);
+ return SANE_STATUS_GOOD;
+}
+
+/* Open device */
+
+static SANE_Status
+UMAX_open_device (UMAX_Handle * scan, const char *dev)
+{
+ SANE_Word vendor;
+ SANE_Word product;
+ SANE_Status res;
+
+ DBG (3, "UMAX_open_device: `%s'\n", dev);
+
+ res = sanei_usb_open (dev, &scan->fd);
+ if (res != SANE_STATUS_GOOD)
+ {
+ DBG (1, "UMAX_open_device: couldn't open device `%s': %s\n", dev,
+ sane_strstatus (res));
+ return res;
+ }
+
+#ifndef NO_AUTODETECT
+ /* We have opened the device. Check that it is a USB scanner. */
+ if (sanei_usb_get_vendor_product (scan->fd, &vendor, &product) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (1, "UMAX_open_device: sanei_usb_get_vendor_product failed\n");
+ /* This is not a USB scanner, or SANE or the OS doesn't support it. */
+ sanei_usb_close (scan->fd);
+ scan->fd = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* Make sure we have a UMAX scanner */
+ if (vendor != 0x1606)
+ {
+ DBG (1, "UMAX_open_device: incorrect vendor\n");
+ sanei_usb_close (scan->fd);
+ scan->fd = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* Now check whether it is a scanner we know about */
+ switch (product)
+ {
+ case ASTRA_2000U:
+ /* The UMAX Astra 2000U is only partially supported by
+ this driver. Expect severe color problems! :)
+ */
+ DBG (1,
+ "UMAX_open_device: Scanner is a 2000U. Expect color problems :)\n");
+ scan->model = ASTRA_2000U;
+ break;
+ case ASTRA_2100U:
+ scan->model = ASTRA_2100U;
+ break;
+ case ASTRA_1220U:
+ scan->model = ASTRA_1220U;
+ break;
+ default:
+ DBG (1, "UMAX_open_device: unknown product number\n");
+ sanei_usb_close (scan->fd);
+ scan->fd = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif
+
+ res = csend (scan, CMD_0);
+ if (res != SANE_STATUS_GOOD)
+ UMAX_close_device (scan);
+ CHK (res);
+
+ res = xxxops (scan);
+ if (res != SANE_STATUS_GOOD)
+ UMAX_close_device (scan);
+ CHK (res);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Get scanner model name */
+
+static const char *
+UMAX_get_device_name (UMAX_Handle * scan)
+{
+ switch (scan->model)
+ {
+ case ASTRA_1220U:
+ return "Astra 1220U";
+ case ASTRA_2000U:
+ return "Astra 2000U";
+ case ASTRA_2100U:
+ return "Astra 2100U";
+ }
+ return "Unknown";
+}
+
+/* End */
diff --git a/backend/umax1220u.c b/backend/umax1220u.c
new file mode 100644
index 0000000..e04d908
--- /dev/null
+++ b/backend/umax1220u.c
@@ -0,0 +1,960 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2001, Marcio Luis Teixeira
+
+ Parts copyright (C) 1996, 1997 Andreas Beck
+ Parts copyright (C) 2000, 2001 Michael Herder <crapsite@gmx.net>
+ Parts copyright (C) 2001 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Parts copyright (C) 2006 Patrick Lessard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#define BUILD 2
+#define MM_IN_INCH 25.4
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_pv8630.h"
+
+#define BACKEND_NAME umax1220u
+#define UMAX_CONFIG_FILE "umax1220u.conf"
+
+#include "../include/sane/sanei_backend.h"
+
+#include "umax1220u-common.c"
+
+typedef struct Umax_Device
+{
+ struct Umax_Device *next;
+ SANE_String name;
+ SANE_Device sane;
+}
+Umax_Device;
+
+typedef struct Umax_Scanner
+{
+ struct Umax_Scanner *next;
+ Umax_Device *device;
+ UMAX_Handle scan;
+}
+Umax_Scanner;
+
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+static Umax_Device *first_dev = NULL;
+static Umax_Scanner *first_handle = NULL;
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 0,
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8 /* Number of bits per sample. */
+};
+
+struct _SANE_Option
+{
+ SANE_Option_Descriptor *descriptor;
+ SANE_Status (*callback) (struct _SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value,
+ SANE_Int * info);
+};
+
+typedef struct _SANE_Option SANE_Option;
+
+static SANE_Word getNumberOfOptions (void); /* Forward declaration */
+
+/*
+This read-only option returns the number of options available for
+the device. It should be the first option in the options array
+declared below.
+*/
+
+static SANE_Option_Descriptor optionNumOptionsDescriptor = {
+ SANE_NAME_NUM_OPTIONS,
+ SANE_TITLE_NUM_OPTIONS,
+ SANE_DESC_NUM_OPTIONS,
+ SANE_TYPE_INT,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+static SANE_Status
+optionNumOptionsCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ option = option;
+ handle = handle;
+ info = info; /* Eliminate warning about unused parameters */
+
+ if (action != SANE_ACTION_GET_VALUE)
+ return SANE_STATUS_INVAL;
+ *(SANE_Word *) value = getNumberOfOptions ();
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This option lets the user select the scan resolution. The UMAX
+scanner only supports the following resolutions: 75, 150, 300 and
+600
+*/
+
+static const SANE_Word optionResolutionList[] = {
+ 4, /* Number of elements */
+ 75, 150, 300, 600 /* Resolution list */
+};
+
+static SANE_Option_Descriptor optionResolutionDescriptor = {
+ SANE_NAME_SCAN_RESOLUTION,
+ SANE_TITLE_SCAN_RESOLUTION,
+ SANE_DESC_SCAN_RESOLUTION,
+ SANE_TYPE_INT,
+ SANE_UNIT_DPI,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC,
+ SANE_CONSTRAINT_WORD_LIST,
+ {(const SANE_String_Const *) optionResolutionList}
+};
+
+static SANE_Word optionResolutionValue = 75;
+
+static SANE_Status
+optionResolutionCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Status status;
+ SANE_Word autoValue = 75;
+
+ handle = handle; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ status =
+ sanei_constrain_value (option->descriptor, (void *) &autoValue, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ optionResolutionValue = autoValue;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ optionResolutionValue = *(SANE_Word *) value;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Word *) value = optionResolutionValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This option lets the user select a gray scale scan
+*/
+static SANE_Word optionGrayscaleValue = SANE_FALSE;
+
+static SANE_Option_Descriptor optionGrayscaleDescriptor = {
+ "gray",
+ SANE_I18N ("Grayscale scan"),
+ SANE_I18N ("Do a grayscale rather than color scan"),
+ SANE_TYPE_BOOL,
+ SANE_UNIT_NONE,
+ sizeof (SANE_Word),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+static SANE_Status
+optionGrayscaleCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ handle = handle;
+ option = option; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ optionGrayscaleValue = *(SANE_Bool *) value;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Word *) value = optionGrayscaleValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This option is a button that allows the user to turn off the
+lamp in the UMAX scanner
+*/
+
+static SANE_Option_Descriptor optionLampOffDescriptor = {
+ "lamp-off",
+ SANE_I18N ("Lamp off"),
+ SANE_I18N ("Turn off scanner lamp"),
+ SANE_TYPE_BUTTON,
+ SANE_UNIT_NONE,
+ 0,
+ SANE_CAP_SOFT_SELECT,
+ SANE_CONSTRAINT_NONE,
+ {NULL}
+};
+
+static SANE_Status
+optionLampOffCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ Umax_Scanner *scanner = handle;
+ SANE_Status res = SANE_STATUS_GOOD;
+
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+ info = info;
+ value = value;
+
+ if (action != SANE_ACTION_SET_VALUE)
+ return SANE_STATUS_INVAL;
+
+ res = UMAX_set_lamp_state (&scanner->scan, UMAX_LAMP_OFF);
+
+ return res;
+}
+
+static const SANE_Range widthRange = {
+ 0, /* minimum */
+ SANE_FIX (UMAX_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range heightRange = {
+ 0, /* minimum */
+ SANE_FIX (UMAX_MAX_HEIGHT * MM_IN_INCH / 600), /* maximum */
+ 0 /* quantization */
+};
+
+/*
+This option controls the top-left-x corner of the scan
+*/
+
+static SANE_Fixed optionTopLeftXValue = 0;
+
+static SANE_Option_Descriptor optionTopLeftXDescriptor = {
+ SANE_NAME_SCAN_TL_X,
+ SANE_TITLE_SCAN_TL_X,
+ SANE_DESC_SCAN_TL_X,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) & widthRange}
+};
+
+static SANE_Status
+optionTopLeftXCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ option = option;
+ handle = handle;
+ value = value; /* Eliminate warning about unused parameters */
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionTopLeftXValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionTopLeftXValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This option controls the top-left-y corner of the scan
+*/
+
+static SANE_Fixed optionTopLeftYValue = 0;
+
+static SANE_Option_Descriptor optionTopLeftYDescriptor = {
+ SANE_NAME_SCAN_TL_Y,
+ SANE_TITLE_SCAN_TL_Y,
+ SANE_DESC_SCAN_TL_Y,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) & heightRange}
+};
+
+static SANE_Status
+optionTopLeftYCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionTopLeftYValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionTopLeftYValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This option controls the bot-right-x corner of the scan
+*/
+
+static SANE_Fixed optionBotRightXValue
+ = SANE_FIX (UMAX_MAX_WIDTH * MM_IN_INCH / 600);
+
+static SANE_Option_Descriptor optionBotRightXDescriptor = {
+ SANE_NAME_SCAN_BR_X,
+ SANE_TITLE_SCAN_BR_X,
+ SANE_DESC_SCAN_BR_X,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) & widthRange}
+};
+
+static SANE_Status
+optionBotRightXCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionBotRightXValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionBotRightXValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+This option controls the bot-right-y corner of the scan
+*/
+
+static SANE_Fixed optionBotRightYValue
+ = SANE_FIX (UMAX_MAX_HEIGHT * MM_IN_INCH / 600);
+
+static SANE_Option_Descriptor optionBotRightYDescriptor = {
+ SANE_NAME_SCAN_BR_Y,
+ SANE_TITLE_SCAN_BR_Y,
+ SANE_DESC_SCAN_BR_Y,
+ SANE_TYPE_FIXED,
+ SANE_UNIT_MM,
+ sizeof (SANE_Fixed),
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
+ SANE_CONSTRAINT_RANGE,
+ {(const SANE_String_Const *) & heightRange}
+};
+
+static SANE_Status
+optionBotRightYCallback (SANE_Option * option, SANE_Handle handle,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ /* Eliminate warnings about unused parameters */
+ option = option;
+ handle = handle;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_INVAL;
+ break;
+ case SANE_ACTION_SET_VALUE:
+ optionBotRightYValue = *(SANE_Fixed *) value;
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case SANE_ACTION_GET_VALUE:
+ *(SANE_Fixed *) value = optionBotRightYValue;
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+The following array binds the option descriptors to
+their respective callback routines
+*/
+
+static SANE_Option so[] = {
+ {&optionNumOptionsDescriptor, optionNumOptionsCallback},
+ {&optionResolutionDescriptor, optionResolutionCallback},
+ {&optionGrayscaleDescriptor, optionGrayscaleCallback},
+ {&optionTopLeftXDescriptor, optionTopLeftXCallback},
+ {&optionTopLeftYDescriptor, optionTopLeftYCallback},
+ {&optionBotRightXDescriptor, optionBotRightXCallback},
+ {&optionBotRightYDescriptor, optionBotRightYCallback},
+ {&optionLampOffDescriptor, optionLampOffCallback}
+};
+
+static SANE_Word
+getNumberOfOptions (void)
+{
+ return NELEMS (so);
+}
+
+/*
+This routine dispatches the control message to the appropriate
+callback routine, it outght to be called by sane_control_option
+after any driver specific validation.
+*/
+static SANE_Status
+dispatch_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ SANE_Option *op = so + option;
+ SANE_Int myinfo = 0;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (option < 0 || option >= NELEMS (so))
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ if ((action == SANE_ACTION_SET_VALUE) &&
+ ((op->descriptor->cap & SANE_CAP_SOFT_SELECT) == 0))
+ return SANE_STATUS_INVAL;
+
+ if ((action == SANE_ACTION_GET_VALUE) &&
+ ((op->descriptor->cap & SANE_CAP_SOFT_DETECT) == 0))
+ return SANE_STATUS_INVAL;
+
+ if ((action == SANE_ACTION_SET_AUTO) &&
+ ((op->descriptor->cap & SANE_CAP_AUTOMATIC) == 0))
+ return SANE_STATUS_INVAL;
+
+ if (action == SANE_ACTION_SET_VALUE)
+ {
+ status = sanei_constrain_value (op->descriptor, value, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ status = (op->callback) (op, handle, action, value, &myinfo);
+
+ if (info)
+ *info = myinfo;
+
+ return status;
+}
+
+static SANE_Status
+attach_scanner (const char *devicename, Umax_Device ** devp)
+{
+ UMAX_Handle scan;
+ Umax_Device *dev;
+ SANE_Status status;
+
+ DBG (3, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, '\0', sizeof (Umax_Device)); /* clear structure */
+
+ DBG (4, "attach_scanner: opening %s\n", devicename);
+
+ status = UMAX_open_device (&scan, devicename);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename);
+ free (dev);
+ return status;
+ }
+ dev->name = strdup (devicename);
+ dev->sane.name = dev->name;
+ dev->sane.vendor = "UMAX";
+ dev->sane.model = UMAX_get_device_name (&scan);
+ dev->sane.type = "flatbed scanner";
+ UMAX_close_device (&scan);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+/* callback function for sanei_usb_attach_matching_devices
+*/
+static SANE_Status
+attach_one (const char *name)
+{
+ attach_scanner (name, 0);
+ return SANE_STATUS_GOOD;
+}
+
+/* This file implements a SANE backend for the UMAX Astra 1220U scanner.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char config_line[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ DBG_INIT ();
+
+ DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+ version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+ DBG (1, "sane_init: SANE umax1220u backend version %d.%d.%d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ sanei_usb_init ();
+ sanei_pv8630_init ();
+
+ fp = sanei_config_open (UMAX_CONFIG_FILE);
+ if (!fp)
+ {
+ /* no config-file: try /dev/scanner and /dev/usbscanner. */
+ attach_scanner ("/dev/scanner", 0);
+ attach_scanner ("/dev/usbscanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "reading configure file %s\n", UMAX_CONFIG_FILE);
+
+ while (sanei_config_read (config_line, sizeof (config_line), fp))
+ {
+ if (config_line[0] == '#')
+ continue; /* ignore line comments */
+
+ len = strlen (config_line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ DBG (4, "attach_matching_devices(%s)\n", config_line);
+ sanei_usb_attach_matching_devices (config_line, attach_one);
+ }
+
+ DBG (4, "finished reading configure file\n");
+
+ fclose (fp);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Umax_Device *dev, *next;
+
+ DBG (3, "sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->name);
+ free (dev);
+ }
+
+ if (devlist)
+ free (devlist);
+ return;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Umax_Device *dev;
+ int i;
+
+ DBG (3, "sane_get_devices(local_only = %d)\n", local_only);
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Umax_Device *dev;
+ SANE_Status status;
+ Umax_Scanner *scanner;
+
+ DBG (3, "sane_open\n");
+
+ if (devicename[0]) /* search for devicename */
+ {
+ DBG (4, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+ }
+ else
+ {
+ DBG (2, "sane_open: no devicename, opening first device\n");
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ scanner = malloc (sizeof (*scanner));
+ if (!scanner)
+ return SANE_STATUS_NO_MEM;
+
+ memset (scanner, 0, sizeof (*scanner));
+ scanner->device = dev;
+
+ status = UMAX_open_device (&scanner->scan, dev->sane.name);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (scanner);
+ return status;
+ }
+
+ *handle = scanner;
+
+ /* insert newly opened handle into list of open handles: */
+ scanner->next = first_handle;
+
+ first_handle = scanner;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Umax_Scanner *prev, *scanner;
+ SANE_Status res;
+
+ DBG (3, "sane_close\n");
+
+ if (!first_handle)
+ {
+ DBG (1, "ERROR: sane_close: no handles opened\n");
+ return;
+ }
+
+ /* remove handle from list of open handles: */
+
+ prev = NULL;
+
+ for (scanner = first_handle; scanner; scanner = scanner->next)
+ {
+ if (scanner == handle)
+ break;
+
+ prev = scanner;
+ }
+
+ if (!scanner)
+ {
+ DBG (1, "ERROR: sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (prev)
+ prev->next = scanner->next;
+ else
+ first_handle = scanner->next;
+
+ res = UMAX_close_device (&scanner->scan);
+
+ free (scanner);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ handle = handle; /* Eliminate compiler warning */
+
+ DBG (3, "sane_get_option_descriptor: option = %d\n", option);
+ if (option < 0 || option >= NELEMS (so))
+ return NULL;
+ return so[option].descriptor;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ handle = handle; /* Eliminate compiler warning */
+
+ DBG (3,
+ "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ handle, option, action, value, (void*) info);
+
+ return dispatch_control_option (handle, option, action, value, info);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int rc = SANE_STATUS_GOOD;
+ int w =
+ SANE_UNFIX (optionBotRightXValue -
+ optionTopLeftXValue) / MM_IN_INCH * optionResolutionValue;
+ int h =
+ SANE_UNFIX (optionBotRightYValue -
+ optionTopLeftYValue) / MM_IN_INCH * optionResolutionValue;
+
+ handle = handle; /* Eliminate compiler warning */
+
+ DBG (3, "sane_get_parameters\n");
+ parms.depth = 8;
+ parms.last_frame = SANE_TRUE;
+ parms.pixels_per_line = w;
+ parms.lines = h;
+
+ if (optionGrayscaleValue == SANE_TRUE)
+ {
+ parms.format = SANE_FRAME_GRAY;
+ parms.bytes_per_line = w;
+ }
+ else
+ {
+ parms.format = SANE_FRAME_RGB;
+ parms.bytes_per_line = w * 3;
+ }
+ *params = parms;
+ return rc;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Umax_Scanner *scanner = handle;
+ SANE_Status res;
+
+ DBG (3, "sane_start\n");
+
+ res = UMAX_set_scan_parameters (&scanner->scan,
+ optionGrayscaleValue == SANE_FALSE,
+ SANE_UNFIX (optionTopLeftXValue) /
+ MM_IN_INCH * 600,
+ SANE_UNFIX (optionTopLeftYValue) /
+ MM_IN_INCH * 600,
+ SANE_UNFIX (optionBotRightXValue -
+ optionTopLeftXValue) /
+ MM_IN_INCH * optionResolutionValue,
+ SANE_UNFIX (optionBotRightYValue -
+ optionTopLeftYValue) /
+ MM_IN_INCH * optionResolutionValue,
+ optionResolutionValue,
+ optionResolutionValue);
+
+ if (res != SANE_STATUS_GOOD)
+ return res;
+
+ if (scanner->scan.model == ASTRA_1220U)
+ return UMAX_start_scan (&scanner->scan);
+ else
+ return UMAX_start_scan_2100U (&scanner->scan);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Umax_Scanner *scanner = handle;
+ SANE_Status res;
+ int len;
+ unsigned char rgb[3];
+
+ len = *length = 0;
+
+ if (!data || !length)
+ return SANE_STATUS_INVAL;
+
+ if (scanner->scan.done)
+ {
+ res = UMAX_finish_scan (&scanner->scan);
+
+ if (scanner->scan.model == ASTRA_1220U)
+ res = UMAX_park_head (&scanner->scan);
+ else
+ res = UMAX_park_head_2100U (&scanner->scan);
+
+ return SANE_STATUS_EOF;
+ }
+
+ DBG (3, "sane_read: max_length = %d\n", max_length);
+
+ if (optionGrayscaleValue == SANE_FALSE)
+ {
+ while (!scanner->scan.done && (max_length >= 3))
+ {
+ res = UMAX_get_rgb (&scanner->scan, rgb);
+ if (res != SANE_STATUS_GOOD)
+ {
+ *length = 0;
+ return res;
+ }
+ *data++ = rgb[0];
+ *data++ = rgb[1];
+ *data++ = rgb[2];
+ max_length -= 3;
+ len += 3;
+ }
+ }
+ else
+ {
+ while (!scanner->scan.done && max_length)
+ {
+ res = UMAX_get_rgb (&scanner->scan, rgb);
+ if (res != SANE_STATUS_GOOD)
+ {
+ *length = 0;
+ return res;
+ }
+ *data++ = rgb[0];
+ max_length--;
+ len++;
+ }
+ }
+
+ *length = len;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (3, "sane_cancel: handle = %p\n", handle);
+ DBG (3, "sane_cancel: canceling is unsupported in this backend\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
+ non_blocking);
+ if (non_blocking != SANE_FALSE)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (3, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
+ fd ? "!=" : "=");
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/umax1220u.conf.in b/backend/umax1220u.conf.in
new file mode 100644
index 0000000..2c1ed70
--- /dev/null
+++ b/backend/umax1220u.conf.in
@@ -0,0 +1,14 @@
+# Options for the umax1220u backend
+
+# Autodetect the UMAX Astra 1220U
+usb 0x1606 0x0010
+
+# The following line enables autodetection for the
+# Astra 2000U and Astra 2100U. However, this driver
+# isn't entirely compatible, so expect color problems :)
+usb 0x1606 0x0030
+usb 0x1606 0x0130
+
+# device list for non-linux-systems (enable if autodetect fails):
+#/dev/scanner
+#/dev/usb/scanner0
diff --git a/backend/umax_pp.c b/backend/umax_pp.c
new file mode 100644
index 0000000..90cd486
--- /dev/null
+++ b/backend/umax_pp.c
@@ -0,0 +1,2404 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Umax PP flatbed scanners. */
+
+/* CREDITS:
+ Started by being a mere copy of mustek_pp
+ by Jochen Eisinger <jochen.eisinger@gmx.net>
+ then evolved in its own thing
+
+ support for the 610P has been made possible thank to an hardware donation
+ from William Stuart
+ */
+
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <math.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#define DEBUG_NOT_STATIC
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#define BACKEND_NAME umax_pp
+#include "../include/sane/sanei_backend.h"
+
+#include "umax_pp_mid.h"
+#include "umax_pp.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define UMAX_PP_CONFIG_FILE "umax_pp.conf"
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+
+/* DEBUG
+ * for debug output, set SANE_DEBUG_UMAX_PP to
+ * 0 for nothing
+ * 1 for errors
+ * 2 for warnings
+ * 3 for additional information
+ * 4 for debug information
+ * 5 for code flow protocol (there isn't any)
+ * 129 if you want to know which parameters are unused
+ */
+
+/* history:
+ * see Changelog
+ */
+
+#define UMAX_PP_BUILD 2301
+#define UMAX_PP_STATE "release"
+
+static int num_devices = 0;
+static Umax_PP_Descriptor *devlist = NULL;
+static const SANE_Device **devarray = NULL;
+
+static Umax_PP_Device *first_dev = NULL;
+
+
+/* 2 Meg scan buffer */
+static SANE_Word buf_size = 2048 * 1024;
+
+static SANE_Word red_gain = 0;
+static SANE_Word green_gain = 0;
+static SANE_Word blue_gain = 0;
+
+static SANE_Word red_offset = 0;
+static SANE_Word green_offset = 0;
+static SANE_Word blue_offset = 0;
+static SANE_Char scanner_vendor[128]="";
+static SANE_Char scanner_name[128]="";
+static SANE_Char scanner_model[128]="";
+static SANE_Char astra[128];
+
+
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static const SANE_Range u4_range = {
+ 0, /* minimum */
+ 15, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+/* range for int value in [0-15] */
+static const SANE_Range value16_range = {
+ 0, /* minimum */
+ 15, /* maximum */
+ 1 /* quantization */
+};
+
+/* range for buffer size */
+static const SANE_Range buffer_range = {
+ 2048, /* minimum */
+ 4096 * 4096, /* maximum */
+ 1 /* quantization */
+};
+
+/* list of astra models */
+static const SANE_String_Const astra_models[] =
+ { "610", "1220", "1600", "2000", NULL };
+
+
+#define UMAX_PP_CHANNEL_RED 0
+#define UMAX_PP_CHANNEL_GREEN 1
+#define UMAX_PP_CHANNEL_BLUE 2
+#define UMAX_PP_CHANNEL_GRAY 1
+
+#define UMAX_PP_STATE_SCANNING 2
+#define UMAX_PP_STATE_CANCELLED 1
+#define UMAX_PP_STATE_IDLE 0
+
+#define UMAX_PP_MODE_LINEART 0
+#define UMAX_PP_MODE_GRAYSCALE 1
+#define UMAX_PP_MODE_COLOR 2
+
+#define MM_TO_PIXEL(mm, res) (SANE_UNFIX(mm) * (float )res / MM_PER_INCH)
+#define PIXEL_TO_MM(px, res) (SANE_FIX((float )(px * MM_PER_INCH / (res / 10)) / 10.0))
+
+#define UMAX_PP_DEFAULT_PORT "/dev/parport0"
+
+#define UMAX_PP_RESERVE 259200
+
+/*
+ * devname may be either an hardware address for direct I/O (0x378 for instance)
+ * or the device name used by ppdev on linux systems (/dev/parport0 )
+ */
+
+
+static SANE_Status
+umax_pp_attach (SANEI_Config * config, const char *devname)
+{
+ Umax_PP_Descriptor *dev;
+ int i;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int ret, prt = 0, mdl;
+ char model[32];
+ char name[64];
+ char *val;
+
+ memset (name, 0, 64);
+
+ if ((strlen (devname) < 3))
+ return SANE_STATUS_INVAL;
+
+ sanei_umax_pp_setastra (atoi((SANE_Char *) config->values[CFG_ASTRA]));
+
+ /* if the name begins with a slash, it's a device, else it's an addr */
+ if (devname != NULL)
+ {
+ if ((devname[0] == '/'))
+ {
+ strncpy (name, devname, 64);
+ }
+ else
+ {
+ if ((devname[0] == '0')
+ && ((devname[1] == 'x') || (devname[1] == 'X')))
+ prt = strtol (devname + 2, NULL, 16);
+ else
+ prt = atoi (devname);
+ }
+ }
+
+
+ for (i = 0; i < num_devices; i++)
+ {
+ if (devname[0] == '/')
+ {
+ if (strcmp (devlist[i].ppdevice, devname) == 0)
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ if (strcmp (devlist[i].port, devname) == 0)
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ ret = sanei_umax_pp_attach (prt, name);
+ switch (ret)
+ {
+ case UMAX1220P_OK:
+ status = SANE_STATUS_GOOD;
+ break;
+ case UMAX1220P_BUSY:
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case UMAX1220P_TRANSPORT_FAILED:
+ DBG (1, "umax_pp_attach: failed to init transport layer on %s\n",
+ devname);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case UMAX1220P_PROBE_FAILED:
+ DBG (1, "umax_pp_attach: failed to probe scanner on %s\n", devname);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "umax_pp_attach: couldn't attach to `%s' (%s)\n", devname,
+ sane_strstatus (status));
+ DEBUG ();
+ return status;
+ }
+
+
+ /* now look for the model */
+ do
+ {
+ ret = sanei_umax_pp_model (prt, &mdl);
+ if (ret != UMAX1220P_OK)
+ {
+ DBG (1, "umax_pp_attach: waiting for busy scanner on %s\n",
+ devname);
+ }
+ }
+ while (ret == UMAX1220P_BUSY);
+
+ if (ret != UMAX1220P_OK)
+ {
+ DBG (1, "umax_pp_attach: failed to recognize scanner model on %s\n",
+ devname);
+ return SANE_STATUS_IO_ERROR;
+ }
+ sprintf (model, "Astra %dP", mdl);
+
+
+ dev = malloc (sizeof (Umax_PP_Descriptor) * (num_devices + 1));
+
+ if (dev == NULL)
+ {
+ DBG (2, "umax_pp_attach: not enough memory for device descriptor\n");
+ DEBUG ();
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (dev, 0, sizeof (Umax_PP_Descriptor) * (num_devices + 1));
+
+ if (num_devices > 0)
+ {
+ memcpy (dev + 1, devlist, sizeof (Umax_PP_Descriptor) * (num_devices));
+ free (devlist);
+ }
+
+ devlist = dev;
+ num_devices++;
+
+ /* if there are user provided values, use them */
+ val=(SANE_Char *) config->values[CFG_NAME];
+ if(strlen(val)==0)
+ dev->sane.name = strdup (devname);
+ else
+ dev->sane.name = strdup (val);
+ val=(SANE_Char *) config->values[CFG_VENDOR];
+ if(strlen(val)==0)
+ dev->sane.vendor = strdup ("UMAX");
+ else
+ dev->sane.vendor = strdup (val);
+ dev->sane.type = "flatbed scanner";
+
+ if (devname[0] == '/')
+ dev->ppdevice = strdup (devname);
+ else
+ dev->port = strdup (devname);
+ dev->buf_size = buf_size;
+
+ if (mdl > 610)
+ { /* Astra 1220, 1600 and 2000 */
+ dev->max_res = 1200;
+ dev->ccd_res = 600;
+ dev->max_h_size = 5100;
+ dev->max_v_size = 7000 - 8; /* -8: workaround 'y overflow bug at 600 dpi' */
+ }
+ else
+ { /* Astra 610 */
+ dev->max_res = 600;
+ dev->ccd_res = 300;
+ dev->max_h_size = 2550;
+ dev->max_v_size = 3500;
+ }
+ val=(SANE_Char *) config->values[CFG_MODEL];
+ if(strlen(val)==0)
+ dev->sane.model = strdup (model);
+ else
+ dev->sane.model = strdup (val);
+
+
+ DBG (3, "umax_pp_attach: device %s attached\n", devname);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * walk a port list and try to attach to them
+ *
+ */
+static SANE_Int
+umax_pp_try_ports (SANEI_Config * config, char **ports)
+{
+ int i;
+ int rc = SANE_STATUS_INVAL;
+
+ if (ports != NULL)
+ {
+ i = 0;
+ rc = SANE_STATUS_INVAL;
+ while (ports[i] != NULL)
+ {
+ if (rc != SANE_STATUS_GOOD)
+ {
+ DBG (3, "umax_pp_try_ports: trying port `%s'\n", ports[i]);
+ rc = umax_pp_attach (config, ports[i]);
+ if (rc != SANE_STATUS_GOOD)
+ DBG (3, "umax_pp_try_ports: couldn't attach to port `%s'\n",
+ ports[i]);
+ else
+ DBG (3,
+ "umax_pp_try_ports: attach to port `%s' successfull\n",
+ ports[i]);
+ }
+ free (ports[i]);
+ i++;
+ }
+ free (ports);
+ }
+ return rc;
+}
+
+/*
+ * attempt to auto detect right parallel port
+ * if safe set to SANE_TRUE, no direct hardware access
+ * is tried
+ */
+static SANE_Int
+umax_pp_auto_attach (SANEI_Config * config, SANE_Int safe)
+{
+ char **ports;
+ int rc = SANE_STATUS_INVAL;
+
+ /* safe tests: user parallel port devices */
+ ports = sanei_parport_find_device ();
+ if (ports != NULL)
+ rc = umax_pp_try_ports (config, ports);
+
+ /* try for direct hardware access */
+ if ((safe != SANE_TRUE) && (rc != SANE_STATUS_GOOD))
+ {
+ ports = sanei_parport_find_port ();
+ if (ports != NULL)
+ rc = umax_pp_try_ports (config, ports);
+ }
+ return rc;
+}
+
+/** callback use by sanei_configure_attach, it is called with the
+ * device name to use for attach try.
+ */
+static SANE_Status
+umax_pp_configure_attach (SANEI_Config * config, const char *devname)
+{
+ const char *lp;
+ SANE_Char *token;
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ /* check for mandatory 'port' token */
+ lp = sanei_config_get_string (devname, &token);
+ if (strncmp (token, "port", 4) != 0)
+ {
+ DBG (3, "umax_pp_configure_attach: invalid port line `%s'\n", devname);
+ free (token);
+ return SANE_STATUS_INVAL;
+ }
+ free (token);
+
+ /* get argument */
+ lp = sanei_config_get_string (lp, &token);
+
+ /* if "safe-auto" or "auto" devname, use umax_pp_attach_auto */
+ if (strncmp (token, "safe-auto", 9) == 0)
+ {
+ status = umax_pp_auto_attach (config, SANE_TRUE);
+ }
+ else if (strncmp (token, "auto", 4) == 0)
+ {
+ status = umax_pp_auto_attach (config, SANE_FALSE);
+ }
+ else
+ {
+ status = umax_pp_attach (config, token);
+ }
+ free (token);
+ return status;
+}
+
+static SANE_Int
+umax_pp_get_sync (SANE_Int dpi)
+{
+ /* delta between color frames */
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ switch (dpi)
+ {
+ case 1200:
+ return 8;
+ case 600:
+ return 4;
+ case 300:
+ return 2;
+ case 150:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ else
+ {
+ switch (dpi)
+ {
+ case 600:
+ return 16;
+ case 300:
+ return 8; /* 8 double-checked */
+ case 150:
+ /* wrong: 2, 3, 5
+ * double-checked : 4
+ */
+ return 4;
+ default:
+ return 2; /* 2 double-checked */
+ }
+ }
+}
+
+
+static SANE_Status
+init_options (Umax_PP_Device * dev)
+{
+ int i;
+
+ /* sets initial option value to zero */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ dev->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+
+ dev->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE_GROUP].name = "";
+ dev->opt[OPT_MODE_GROUP].desc = "";
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].size = 10;
+ dev->opt[OPT_MODE].constraint.string_list = mode_list;
+ dev->val[OPT_MODE].s = strdup (mode_list[1]);
+
+ /* resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RESOLUTION].constraint.range = &dev->dpi_range;
+ dev->val[OPT_RESOLUTION].w = dev->dpi_range.min;
+
+
+ /* preview */
+ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_PREVIEW].size = sizeof (SANE_Word);
+ dev->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* gray preview */
+ dev->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
+ dev->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
+ dev->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
+ dev->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_GRAY_PREVIEW].size = sizeof (SANE_Word);
+ dev->opt[OPT_GRAY_PREVIEW].unit = SANE_UNIT_NONE;
+ dev->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+
+ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ dev->opt[OPT_GEOMETRY_GROUP].desc = "";
+ dev->opt[OPT_GEOMETRY_GROUP].name = "";
+ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GEOMETRY_GROUP].size = 0;
+ dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ dev->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_X].constraint.range = &dev->x_range;
+ dev->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ dev->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_TL_Y].constraint.range = &dev->y_range;
+ dev->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ dev->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_X].constraint.range = &dev->x_range;
+ dev->val[OPT_BR_X].w = dev->x_range.max;
+
+ /* bottom-right y */
+ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ dev->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BR_Y].constraint.range = &dev->y_range;
+ dev->val[OPT_BR_Y].w = dev->y_range.max;
+
+ /* "Enhancement" group: */
+
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ dev->opt[OPT_ENHANCEMENT_GROUP].name = "";
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* lamp control */
+ dev->opt[OPT_LAMP_CONTROL].name = "lamp-control";
+ dev->opt[OPT_LAMP_CONTROL].title = SANE_I18N ("Lamp on");
+ dev->opt[OPT_LAMP_CONTROL].desc = SANE_I18N ("Sets lamp on/off");
+ dev->opt[OPT_LAMP_CONTROL].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_LAMP_CONTROL].size = sizeof (SANE_Word);
+ dev->opt[OPT_LAMP_CONTROL].unit = SANE_UNIT_NONE;
+ dev->val[OPT_LAMP_CONTROL].w = SANE_TRUE;
+ dev->opt[OPT_LAMP_CONTROL].cap |= SANE_CAP_ADVANCED;
+
+ /* UTA control */
+ dev->opt[OPT_UTA_CONTROL].name = "UTA-control";
+ dev->opt[OPT_UTA_CONTROL].title = SANE_I18N ("UTA on");
+ dev->opt[OPT_UTA_CONTROL].desc = SANE_I18N ("Sets UTA on/off");
+ dev->opt[OPT_UTA_CONTROL].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_UTA_CONTROL].size = sizeof (SANE_Word);
+ dev->opt[OPT_UTA_CONTROL].unit = SANE_UNIT_NONE;
+ dev->val[OPT_UTA_CONTROL].w = SANE_TRUE;
+ dev->opt[OPT_UTA_CONTROL].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
+
+ /* custom-gamma table */
+ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED;
+ dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ dev->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ dev->val[OPT_GAMMA_VECTOR].wa = &dev->gamma_table[0][0];
+
+ /* red gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ dev->val[OPT_GAMMA_VECTOR_R].wa = &dev->gamma_table[1][0];
+
+ /* green gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ dev->val[OPT_GAMMA_VECTOR_G].wa = &dev->gamma_table[2][0];
+
+ /* blue gamma vector */
+ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ dev->val[OPT_GAMMA_VECTOR_B].wa = &dev->gamma_table[3][0];
+
+ /* gain group */
+ dev->opt[OPT_MANUAL_GAIN].name = "manual-channel-gain";
+ dev->opt[OPT_MANUAL_GAIN].title = SANE_I18N ("Gain");
+ dev->opt[OPT_MANUAL_GAIN].desc = SANE_I18N ("Color channels gain settings");
+ dev->opt[OPT_MANUAL_GAIN].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_MANUAL_GAIN].cap |= SANE_CAP_ADVANCED;
+ dev->val[OPT_MANUAL_GAIN].w = SANE_FALSE;
+
+ /* gray gain */
+ dev->opt[OPT_GRAY_GAIN].name = "gray-gain";
+ dev->opt[OPT_GRAY_GAIN].title = SANE_I18N ("Gray gain");
+ dev->opt[OPT_GRAY_GAIN].desc = SANE_I18N ("Sets gray channel gain");
+ dev->opt[OPT_GRAY_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_GRAY_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GRAY_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_GRAY_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GRAY_GAIN].constraint.range = &u4_range;
+ dev->val[OPT_GRAY_GAIN].w = dev->gray_gain;
+
+ /* red gain */
+ dev->opt[OPT_RED_GAIN].name = "red-gain";
+ dev->opt[OPT_RED_GAIN].title = SANE_I18N ("Red gain");
+ dev->opt[OPT_RED_GAIN].desc = SANE_I18N ("Sets red channel gain");
+ dev->opt[OPT_RED_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_RED_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_RED_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_RED_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RED_GAIN].constraint.range = &u4_range;
+ dev->val[OPT_RED_GAIN].w = dev->red_gain;
+
+ /* green gain */
+ dev->opt[OPT_GREEN_GAIN].name = "green-gain";
+ dev->opt[OPT_GREEN_GAIN].title = SANE_I18N ("Green gain");
+ dev->opt[OPT_GREEN_GAIN].desc = SANE_I18N ("Sets green channel gain");
+ dev->opt[OPT_GREEN_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_GREEN_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GREEN_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_GREEN_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GREEN_GAIN].constraint.range = &u4_range;
+ dev->val[OPT_GREEN_GAIN].w = dev->green_gain;
+
+ /* blue gain */
+ dev->opt[OPT_BLUE_GAIN].name = "blue-gain";
+ dev->opt[OPT_BLUE_GAIN].title = SANE_I18N ("Blue gain");
+ dev->opt[OPT_BLUE_GAIN].desc = SANE_I18N ("Sets blue channel gain");
+ dev->opt[OPT_BLUE_GAIN].type = SANE_TYPE_INT;
+ dev->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_BLUE_GAIN].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BLUE_GAIN].size = sizeof (SANE_Int);
+ dev->opt[OPT_BLUE_GAIN].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BLUE_GAIN].constraint.range = &u4_range;
+ dev->val[OPT_BLUE_GAIN].w = dev->blue_gain;
+
+ /* offset group */
+ dev->opt[OPT_MANUAL_OFFSET].name = "manual-offset";
+ dev->opt[OPT_MANUAL_OFFSET].title = SANE_I18N ("Offset");
+ dev->opt[OPT_MANUAL_OFFSET].desc =
+ SANE_I18N ("Color channels offset settings");
+ dev->opt[OPT_MANUAL_OFFSET].type = SANE_TYPE_BOOL;
+ dev->opt[OPT_MANUAL_OFFSET].cap |= SANE_CAP_ADVANCED;
+ dev->val[OPT_MANUAL_OFFSET].w = SANE_FALSE;
+
+ /* gray offset */
+ dev->opt[OPT_GRAY_OFFSET].name = "gray-offset";
+ dev->opt[OPT_GRAY_OFFSET].title = SANE_I18N ("Gray offset");
+ dev->opt[OPT_GRAY_OFFSET].desc = SANE_I18N ("Sets gray channel offset");
+ dev->opt[OPT_GRAY_OFFSET].type = SANE_TYPE_INT;
+ dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_GRAY_OFFSET].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GRAY_OFFSET].size = sizeof (SANE_Int);
+ dev->opt[OPT_GRAY_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GRAY_OFFSET].constraint.range = &u4_range;
+ dev->val[OPT_GRAY_OFFSET].w = dev->gray_offset;
+
+ /* red offset */
+ dev->opt[OPT_RED_OFFSET].name = "red-offset";
+ dev->opt[OPT_RED_OFFSET].title = SANE_I18N ("Red offset");
+ dev->opt[OPT_RED_OFFSET].desc = SANE_I18N ("Sets red channel offset");
+ dev->opt[OPT_RED_OFFSET].type = SANE_TYPE_INT;
+ dev->opt[OPT_RED_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_RED_OFFSET].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_RED_OFFSET].size = sizeof (SANE_Int);
+ dev->opt[OPT_RED_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_RED_OFFSET].constraint.range = &u4_range;
+ dev->val[OPT_RED_OFFSET].w = dev->red_offset;
+
+ /* green offset */
+ dev->opt[OPT_GREEN_OFFSET].name = "green-offset";
+ dev->opt[OPT_GREEN_OFFSET].title = SANE_I18N ("Green offset");
+ dev->opt[OPT_GREEN_OFFSET].desc = SANE_I18N ("Sets green channel offset");
+ dev->opt[OPT_GREEN_OFFSET].type = SANE_TYPE_INT;
+ dev->opt[OPT_GREEN_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_GREEN_OFFSET].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_GREEN_OFFSET].size = sizeof (SANE_Int);
+ dev->opt[OPT_GREEN_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_GREEN_OFFSET].constraint.range = &u4_range;
+ dev->val[OPT_GREEN_OFFSET].w = dev->green_offset;
+
+ /* blue offset */
+ dev->opt[OPT_BLUE_OFFSET].name = "blue-offset";
+ dev->opt[OPT_BLUE_OFFSET].title = SANE_I18N ("Blue offset");
+ dev->opt[OPT_BLUE_OFFSET].desc = SANE_I18N ("Sets blue channel offset");
+ dev->opt[OPT_BLUE_OFFSET].type = SANE_TYPE_INT;
+ dev->opt[OPT_BLUE_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
+ dev->opt[OPT_BLUE_OFFSET].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BLUE_OFFSET].size = sizeof (SANE_Int);
+ dev->opt[OPT_BLUE_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BLUE_OFFSET].constraint.range = &u4_range;
+ dev->val[OPT_BLUE_OFFSET].w = dev->blue_offset;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Status status;
+ SANEI_Config config;
+ SANE_Option_Descriptor *options[NUM_CFG_OPTIONS];
+ void *values[NUM_CFG_OPTIONS];
+ int i = 0;
+
+ DBG_INIT ();
+
+ if (authorize != NULL)
+ {
+ DBG (2, "init: SANE_Auth_Callback not supported ...\n");
+ }
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, UMAX_PP_BUILD);
+
+ DBG (3, "init: SANE v%s, backend v%d.%d.%d-%s\n", VERSION, SANE_CURRENT_MAJOR, V_MINOR,
+ UMAX_PP_BUILD, UMAX_PP_STATE);
+
+ /* set up configuration options to parse */
+ options[CFG_BUFFER] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_BUFFER]->name = "buffer";
+ options[CFG_BUFFER]->type = SANE_TYPE_INT;
+ options[CFG_BUFFER]->unit = SANE_UNIT_NONE;
+ options[CFG_BUFFER]->size = sizeof (SANE_Word);
+ options[CFG_BUFFER]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_BUFFER]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_BUFFER]->constraint.range = &buffer_range;
+ values[CFG_BUFFER] = &buf_size;
+
+ options[CFG_RED_GAIN] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_RED_GAIN]->name = "red-gain";
+ options[CFG_RED_GAIN]->type = SANE_TYPE_INT;
+ options[CFG_RED_GAIN]->unit = SANE_UNIT_NONE;
+ options[CFG_RED_GAIN]->size = sizeof (SANE_Word);
+ options[CFG_RED_GAIN]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_RED_GAIN]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_RED_GAIN]->constraint.range = &value16_range;
+ values[CFG_RED_GAIN] = &red_gain;
+
+ options[CFG_GREEN_GAIN] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_GREEN_GAIN]->name = "green-gain";
+ options[CFG_GREEN_GAIN]->type = SANE_TYPE_INT;
+ options[CFG_GREEN_GAIN]->unit = SANE_UNIT_NONE;
+ options[CFG_GREEN_GAIN]->size = sizeof (SANE_Word);
+ options[CFG_GREEN_GAIN]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_GREEN_GAIN]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_GREEN_GAIN]->constraint.range = &value16_range;
+ values[CFG_GREEN_GAIN] = &green_gain;
+
+ options[CFG_BLUE_GAIN] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_BLUE_GAIN]->name = "blue-gain";
+ options[CFG_BLUE_GAIN]->type = SANE_TYPE_INT;
+ options[CFG_BLUE_GAIN]->unit = SANE_UNIT_NONE;
+ options[CFG_BLUE_GAIN]->size = sizeof (SANE_Word);
+ options[CFG_BLUE_GAIN]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_BLUE_GAIN]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_BLUE_GAIN]->constraint.range = &value16_range;
+ values[CFG_BLUE_GAIN] = &blue_gain;
+
+ options[CFG_RED_OFFSET] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_RED_OFFSET]->name = "red-offset";
+ options[CFG_RED_OFFSET]->type = SANE_TYPE_INT;
+ options[CFG_RED_OFFSET]->unit = SANE_UNIT_NONE;
+ options[CFG_RED_OFFSET]->size = sizeof (SANE_Word);
+ options[CFG_RED_OFFSET]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_RED_OFFSET]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_RED_OFFSET]->constraint.range = &value16_range;
+ values[CFG_RED_OFFSET] = &red_offset;
+
+ options[CFG_GREEN_OFFSET] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_GREEN_OFFSET]->name = "green-offset";
+ options[CFG_GREEN_OFFSET]->type = SANE_TYPE_INT;
+ options[CFG_GREEN_OFFSET]->unit = SANE_UNIT_NONE;
+ options[CFG_GREEN_OFFSET]->size = sizeof (SANE_Word);
+ options[CFG_GREEN_OFFSET]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_GREEN_OFFSET]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_GREEN_OFFSET]->constraint.range = &value16_range;
+ values[CFG_GREEN_OFFSET] = &green_offset;
+
+ options[CFG_BLUE_OFFSET] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_BLUE_OFFSET]->name = "blue-offset";
+ options[CFG_BLUE_OFFSET]->type = SANE_TYPE_INT;
+ options[CFG_BLUE_OFFSET]->unit = SANE_UNIT_NONE;
+ options[CFG_BLUE_OFFSET]->size = sizeof (SANE_Word);
+ options[CFG_BLUE_OFFSET]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_BLUE_OFFSET]->constraint_type = SANE_CONSTRAINT_RANGE;
+ options[CFG_BLUE_OFFSET]->constraint.range = &value16_range;
+ values[CFG_BLUE_OFFSET] = &blue_offset;
+
+ options[CFG_VENDOR] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_VENDOR]->name = "vendor";
+ options[CFG_VENDOR]->type = SANE_TYPE_STRING;
+ options[CFG_VENDOR]->unit = SANE_UNIT_NONE;
+ options[CFG_VENDOR]->size = 128;
+ options[CFG_VENDOR]->cap = SANE_CAP_SOFT_SELECT;
+ values[CFG_VENDOR] = scanner_vendor;
+
+ options[CFG_NAME] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_NAME]->name = "name";
+ options[CFG_NAME]->type = SANE_TYPE_STRING;
+ options[CFG_NAME]->unit = SANE_UNIT_NONE;
+ options[CFG_NAME]->size = 128;
+ options[CFG_NAME]->cap = SANE_CAP_SOFT_SELECT;
+ values[CFG_NAME] = scanner_name;
+
+ options[CFG_MODEL] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_MODEL]->name = "model";
+ options[CFG_MODEL]->type = SANE_TYPE_STRING;
+ options[CFG_MODEL]->unit = SANE_UNIT_NONE;
+ options[CFG_MODEL]->size = 128;
+ options[CFG_MODEL]->cap = SANE_CAP_SOFT_SELECT;
+ values[CFG_MODEL] = scanner_model;
+
+ options[CFG_ASTRA] =
+ (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
+ options[CFG_ASTRA]->name = "astra";
+ options[CFG_ASTRA]->type = SANE_TYPE_STRING;
+ options[CFG_ASTRA]->unit = SANE_UNIT_NONE;
+ options[CFG_ASTRA]->size = 128;
+ options[CFG_ASTRA]->cap = SANE_CAP_SOFT_SELECT;
+ options[CFG_ASTRA]->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ options[CFG_ASTRA]->constraint.string_list = astra_models;
+ values[CFG_ASTRA] = astra;
+
+ config.descriptors = options;
+ config.values = values;
+ config.count = NUM_CFG_OPTIONS;
+
+ /* generic configure and attach function */
+ status = sanei_configure_attach (UMAX_PP_CONFIG_FILE, &config,
+ umax_pp_configure_attach);
+
+ /* free option descriptors */
+ for (i = 0; i < NUM_CFG_OPTIONS; i++)
+ {
+ free (options[i]);
+ }
+
+ return status;
+}
+
+void
+sane_exit (void)
+{
+ int i;
+ Umax_PP_Device *dev;
+
+ DBG (3, "sane_exit: (...)\n");
+ if (first_dev)
+ DBG (3, "exit: closing open devices\n");
+
+ while (first_dev)
+ {
+ dev = first_dev;
+ sane_close (dev);
+ }
+
+ for (i = 0; i < num_devices; i++)
+ {
+ free (devlist[i].port);
+ free (devlist[i].sane.name);
+ free (devlist[i].sane.model);
+ free (devlist[i].sane.vendor);
+ }
+
+ if (devlist != NULL)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ if (devarray != NULL)
+ {
+ free (devarray);
+ devarray = NULL;
+ }
+
+ /* reset values */
+ num_devices = 0;
+ first_dev = NULL;
+
+ red_gain = 0;
+ green_gain = 0;
+ blue_gain = 0;
+
+ red_offset = 0;
+ green_offset = 0;
+ blue_offset = 0;
+
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ int i;
+
+ DBG (3, "get_devices\n");
+ DBG (129, "unused arg: local_only = %d\n", (int) local_only);
+
+ if (devarray != NULL)
+ {
+ free (devarray);
+ devarray = NULL;
+ }
+
+ devarray = malloc ((num_devices + 1) * sizeof (devarray[0]));
+
+ if (devarray == NULL)
+ {
+ DBG (2, "get_devices: not enough memory for device list\n");
+ DEBUG ();
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < num_devices; i++)
+ devarray[i] = &devlist[i].sane;
+
+ devarray[num_devices] = NULL;
+ *device_list = devarray;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Umax_PP_Device *dev;
+ Umax_PP_Descriptor *desc;
+ int i, j;
+ int rc, prt = 0;
+ char *name = NULL;
+
+ DBG (3, "open: device `%s'\n", devicename);
+
+ /* if no device given or 'umax_pp' default value given */
+ if (devicename == NULL || devicename[0] == 0
+ || strncmp (devicename, "umax_pp", 7) == 0)
+ {
+
+ if (num_devices == 0)
+ {
+ DBG (1, "open: no devices present\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (3, "open: trying default device %s, port=%s,ppdev=%s\n",
+ devlist[0].sane.name, devlist[0].port, devlist[0].ppdevice);
+ if (devlist[0].port != NULL)
+ {
+ if ((devlist[0].port[0] == '0')
+ && ((devlist[0].port[1] == 'x') || (devlist[0].port[1] == 'X')))
+ prt = strtol (devlist[0].port + 2, NULL, 16);
+ else
+ prt = atoi (devlist[0].port);
+ rc = sanei_umax_pp_open (prt, NULL);
+ }
+ else
+ {
+ rc = sanei_umax_pp_open (0, devlist[0].ppdevice);
+ }
+ desc = &devlist[0];
+ }
+ else /* specific value */
+ {
+ for (i = 0; i < num_devices; i++)
+ if (strcmp (devlist[i].sane.name, devicename) == 0)
+ break;
+
+ if (i >= num_devices)
+ for (i = 0; i < num_devices; i++)
+ if (strcmp (devlist[i].port, devicename) == 0)
+ break;
+
+ if (i >= num_devices)
+ {
+ DBG (2, "open: device doesn't exist\n");
+ DEBUG ();
+ return SANE_STATUS_INVAL;
+ }
+
+ desc = &devlist[i];
+
+ if (devlist[i].ppdevice != NULL)
+ {
+ if (devlist[i].ppdevice[0] == '/')
+ {
+ name = devlist[i].ppdevice;
+ }
+ }
+ else
+ {
+ if ((devlist[i].port[0] == '0')
+ && ((devlist[i].port[1] == 'x') || (devlist[i].port[1] == 'X')))
+ prt = strtol (devlist[i].port + 2, NULL, 16);
+ else
+ prt = atoi (devlist[i].port);
+ DBG (64, "open: devlist[i].port='%s' -> port=0x%X\n",
+ devlist[i].port, prt);
+ }
+ rc = sanei_umax_pp_open (prt, name);
+ }
+
+ /* treat return code from open */
+ switch (rc)
+ {
+ case UMAX1220P_TRANSPORT_FAILED:
+ if (name == NULL)
+ {
+ DBG (1, "failed to init transport layer on port 0x%03X\n", prt);
+ }
+ else
+ {
+ DBG (1, "failed to init transport layer on device %s\n", name);
+ }
+ return SANE_STATUS_IO_ERROR;
+
+ case UMAX1220P_SCANNER_FAILED:
+ if (name == NULL)
+ {
+ DBG (1, "failed to initialize scanner on port 0x%03X\n", prt);
+ }
+ else
+ {
+ DBG (1, "failed to initialize scanner on device %s\n", name);
+ }
+ return SANE_STATUS_IO_ERROR;
+ case UMAX1220P_BUSY:
+ if (name == NULL)
+ {
+ DBG (1, "busy scanner on port 0x%03X\n", prt);
+ }
+ else
+ {
+ DBG (1, "busy scanner on device %s\n", name);
+ }
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+
+ dev = (Umax_PP_Device *) malloc (sizeof (*dev));
+
+ if (dev == NULL)
+ {
+ DBG (2, "open: not enough memory for device descriptor\n");
+ DEBUG ();
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->desc = desc;
+
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 256; ++j)
+ dev->gamma_table[i][j] = j;
+
+ /* the extra amount of UMAX_PP_RESERVE bytes is to handle */
+ /* the data needed to resync the color frames */
+ dev->buf = malloc (dev->desc->buf_size + UMAX_PP_RESERVE);
+ dev->bufsize = dev->desc->buf_size;
+
+ dev->dpi_range.min = SANE_FIX (75);
+ dev->dpi_range.max = SANE_FIX (dev->desc->max_res);
+ dev->dpi_range.quant = 0;
+
+ dev->x_range.min = 0;
+ dev->x_range.max = dev->desc->max_h_size;
+ dev->x_range.quant = 0;
+
+ dev->y_range.min = 0;
+ dev->y_range.max = dev->desc->max_v_size;
+ dev->y_range.quant = 0;
+
+ dev->gray_gain = 0;
+
+ /* use pre defined settings read from umax_pp.conf */
+ dev->red_gain = red_gain;
+ dev->green_gain = green_gain;
+ dev->blue_gain = blue_gain;
+ dev->red_offset = red_offset;
+ dev->green_offset = green_offset;
+ dev->blue_offset = blue_offset;
+
+
+ if (dev->buf == NULL)
+ {
+ DBG (2, "open: not enough memory for scan buffer (%lu bytes)\n",
+ (long int) dev->desc->buf_size);
+ DEBUG ();
+ free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ init_options (dev);
+
+ dev->next = first_dev;
+ first_dev = dev;
+
+
+ if (sanei_umax_pp_UTA () == 1)
+ dev->opt[OPT_UTA_CONTROL].cap &= ~SANE_CAP_INACTIVE;
+
+ *handle = dev;
+
+ DBG (3, "open: success\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Umax_PP_Device *prev, *dev;
+ int rc;
+
+ DBG (3, "sane_close: ...\n");
+ /* remove handle from list of open handles: */
+ prev = NULL;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (dev == handle)
+ break;
+ prev = dev;
+ }
+
+ if (dev == NULL)
+ {
+ DBG (2, "close: unknown device\n");
+ DEBUG ();
+ return; /* oops, not a handle we know about */
+ }
+
+ if (dev->state == UMAX_PP_STATE_SCANNING)
+ sane_cancel (handle); /* remember: sane_cancel is a macro and
+ expands to sane_umax_pp_cancel ()... */
+
+
+ /* if the scanner is parking head, we wait it to finish */
+ while (dev->state == UMAX_PP_STATE_CANCELLED)
+ {
+ DBG (2, "close: waiting scanner to park head\n");
+ rc = sanei_umax_pp_status ();
+
+ /* check if scanner busy parking */
+ if (rc != UMAX1220P_BUSY)
+ {
+ DBG (2, "close: scanner head parked\n");
+ dev->state = UMAX_PP_STATE_IDLE;
+ }
+ }
+
+ /* then we switch off gain if needed */
+ if (dev->val[OPT_LAMP_CONTROL].w == SANE_TRUE)
+ {
+ rc = sanei_umax_pp_lamp (0);
+ if (rc == UMAX1220P_TRANSPORT_FAILED)
+ {
+ DBG (1, "close: switch off gain failed (ignored....)\n");
+ }
+ }
+
+ sanei_umax_pp_close ();
+
+
+
+ if (prev != NULL)
+ prev->next = dev->next;
+ else
+ first_dev = dev->next;
+
+ free (dev->buf);
+ DBG (3, "close: device closed\n");
+
+ free (handle);
+
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Umax_PP_Device *dev = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ DBG (2, "get_option_descriptor: option %d doesn't exist\n", option);
+ DEBUG ();
+ return NULL;
+ }
+
+ DBG (6, "get_option_descriptor: requested option %d (%s)\n",
+ option, dev->opt[option].name);
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Umax_PP_Device *dev = handle;
+ SANE_Status status;
+ SANE_Word w, cap, tmpw;
+ int dpi, rc;
+
+ DBG (6, "control_option: option %d, action %d\n", option, action);
+
+ if (info)
+ *info = 0;
+
+ if (dev->state == UMAX_PP_STATE_SCANNING)
+ {
+ DBG (2, "control_option: device is scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((unsigned int) option >= NUM_OPTIONS)
+ {
+ DBG (2, "control_option: option doesn't exist\n");
+ return SANE_STATUS_INVAL;
+ }
+
+
+ cap = dev->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (2, "control_option: option isn't active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (6, "control_option: option <%s>, action ... %d\n",
+ dev->opt[option].name, action);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (6, " get value\n");
+ switch (option)
+ {
+ /* word options: */
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_LAMP_CONTROL:
+ case OPT_UTA_CONTROL:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_MANUAL_GAIN:
+ case OPT_GRAY_GAIN:
+ case OPT_GREEN_GAIN:
+ case OPT_RED_GAIN:
+ case OPT_BLUE_GAIN:
+ case OPT_MANUAL_OFFSET:
+ case OPT_GRAY_OFFSET:
+ case OPT_GREEN_OFFSET:
+ case OPT_RED_OFFSET:
+ case OPT_BLUE_OFFSET:
+
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, dev->val[option].wa, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_MODE:
+
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (6, " set value\n");
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (2, "control_option: option can't be set\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "control_option: constrain_value failed (%s)\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (option == OPT_RESOLUTION)
+ {
+ DBG (16, "control_option: setting resolution to %d\n",
+ *(SANE_Int *) val);
+ }
+ if (option == OPT_PREVIEW)
+ {
+ DBG (16, "control_option: setting preview to %d\n",
+ *(SANE_Word *) val);
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ case OPT_TL_Y:
+ case OPT_BR_Y:
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ case OPT_GRAY_GAIN:
+ case OPT_GREEN_GAIN:
+ case OPT_RED_GAIN:
+ case OPT_BLUE_GAIN:
+ case OPT_GRAY_OFFSET:
+ case OPT_GREEN_OFFSET:
+ case OPT_RED_OFFSET:
+ case OPT_BLUE_OFFSET:
+
+ dev->val[option].w = *(SANE_Word *) val;
+ /* sanity check */
+ if (dev->val[OPT_BR_Y].w < dev->val[OPT_TL_Y].w)
+ {
+ tmpw = dev->val[OPT_BR_Y].w;
+ dev->val[OPT_BR_Y].w = dev->val[OPT_TL_Y].w;
+ dev->val[OPT_TL_Y].w = tmpw;
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ DBG (16, "control_option: swapping Y coordinates\n");
+ }
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w));
+ if (dev->val[OPT_TL_Y].w < 2 * umax_pp_get_sync (dpi))
+ {
+ DBG (16, "control_option: correcting TL_Y coordinates\n");
+ dev->val[OPT_TL_Y].w = 2 * umax_pp_get_sync (dpi);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+
+ memcpy (dev->val[option].wa, val, dev->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+
+ /* options with side-effects: */
+ case OPT_UTA_CONTROL:
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_LAMP_CONTROL:
+ if (dev->state != UMAX_PP_STATE_IDLE)
+ {
+ rc = sanei_umax_pp_status ();
+
+ /* check if scanner busy parking */
+ if (rc == UMAX1220P_BUSY)
+ {
+ DBG (2, "control_option: scanner busy\n");
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ dev->state = UMAX_PP_STATE_IDLE;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ if (dev->val[option].w == SANE_TRUE)
+ rc = sanei_umax_pp_lamp (1);
+ else
+ rc = sanei_umax_pp_lamp (0);
+ if (rc == UMAX1220P_TRANSPORT_FAILED)
+ return SANE_STATUS_IO_ERROR;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ case OPT_BR_X:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w));
+ dev->val[option].w = *(SANE_Word *) val;
+ /* coords rounded to allow 32 bit IO/transfer */
+ /* at high resolution */
+ if (dpi >= 600)
+ {
+ if (dev->val[option].w & 0x03)
+ {
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ dev->val[option].w = dev->val[option].w & 0xFFFC;
+ *(SANE_Word *) val = dev->val[option].w;
+ DBG (16, "control_option: rounding X to %d\n",
+ *(SANE_Word *) val);
+ }
+ }
+ /* sanity check */
+ if (dev->val[OPT_BR_X].w < dev->val[OPT_TL_X].w)
+ {
+ tmpw = dev->val[OPT_BR_X].w;
+ dev->val[OPT_BR_X].w = dev->val[OPT_TL_X].w;
+ dev->val[OPT_TL_X].w = tmpw;
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ DBG (16, "control_option: swapping X coordinates\n");
+ }
+ return SANE_STATUS_GOOD;
+
+
+
+ case OPT_RESOLUTION:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* resolution : only have 75, 150, 300, 600 and 1200 */
+ dpi = (int) (SANE_UNFIX (*(SANE_Word *) val));
+ if ((dpi != 75)
+ && (dpi != 150)
+ && (dpi != 300) && (dpi != 600) && (dpi != 1200))
+ {
+ if (dpi <= 75)
+ dpi = 75;
+ else if (dpi <= 150)
+ dpi = 150;
+ else if (dpi <= 300)
+ dpi = 300;
+ else if (dpi <= 600)
+ dpi = 600;
+ else
+ dpi = 1200;
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ *(SANE_Word *) val = SANE_FIX ((SANE_Word) dpi);
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+
+ /* correct top x and bottom x if needed */
+ if (dpi >= 600)
+ {
+ dev->val[OPT_TL_X].w = dev->val[OPT_TL_X].w & 0xFFFC;
+ dev->val[OPT_BR_X].w = dev->val[OPT_BR_X].w & 0xFFFC;
+ }
+ /* corrects top y for offset */
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ if (dev->val[OPT_TL_Y].w < 2 * umax_pp_get_sync (dpi))
+ {
+ DBG (16, "control_option: correcting TL_Y coordinates\n");
+ dev->val[OPT_TL_Y].w = 2 * umax_pp_get_sync (dpi);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MANUAL_OFFSET:
+ w = *(SANE_Word *) val;
+
+ if (w == dev->val[OPT_MANUAL_OFFSET].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ dev->val[OPT_MANUAL_OFFSET].w = w;
+
+ if (w == SANE_TRUE)
+ {
+ const char *mode = dev->val[OPT_MODE].s;
+
+ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ dev->opt[OPT_GRAY_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_RED_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_RED_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_OFFSET].cap |= SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+
+
+
+ case OPT_MANUAL_GAIN:
+ w = *(SANE_Word *) val;
+
+ if (w == dev->val[OPT_MANUAL_GAIN].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ dev->val[OPT_MANUAL_GAIN].w = w;
+
+ if (w == SANE_TRUE)
+ {
+ const char *mode = dev->val[OPT_MODE].s;
+
+ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ dev->opt[OPT_GRAY_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_RED_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+
+
+
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == dev->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ dev->val[OPT_CUSTOM_GAMMA].w = w;
+
+ if (w == SANE_TRUE)
+ {
+ const char *mode = dev->val[OPT_MODE].s;
+
+ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ {
+ dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ sanei_umax_pp_gamma (NULL, dev->val[OPT_GAMMA_VECTOR].wa,
+ NULL);
+ }
+ else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ sanei_umax_pp_gamma (dev->val[OPT_GAMMA_VECTOR_R].wa,
+ dev->val[OPT_GAMMA_VECTOR_G].wa,
+ dev->val[OPT_GAMMA_VECTOR_B].wa);
+ }
+ }
+ else
+ {
+ dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ sanei_umax_pp_gamma (NULL, NULL, NULL);
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ {
+ char *old_val = dev->val[option].s;
+
+ if (old_val)
+ {
+ if (strcmp (old_val, val) == 0)
+ return SANE_STATUS_GOOD; /* no change */
+
+ free (old_val);
+ }
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+
+ dev->val[option].s = strdup (val);
+
+ /* corrects top y for offset */
+ if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w));
+ if (dev->val[OPT_TL_Y].w < 2 * umax_pp_get_sync (dpi))
+ {
+ dev->val[OPT_TL_Y].w = 2 * umax_pp_get_sync (dpi);
+ DBG (16, "control_option: correcting TL_Y coordinates\n");
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+
+ dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ sanei_umax_pp_gamma (NULL, NULL, NULL);
+
+
+ if (dev->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ {
+ if ((strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ {
+ dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ sanei_umax_pp_gamma (NULL, dev->val[OPT_GAMMA_VECTOR].wa,
+ NULL);
+ }
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ sanei_umax_pp_gamma (dev->val[OPT_GAMMA_VECTOR_R].wa,
+ dev->val[OPT_GAMMA_VECTOR_G].wa,
+ dev->val[OPT_GAMMA_VECTOR_B].wa);
+ }
+ }
+
+ /* rebuild OPT OFFSET */
+ dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_RED_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_OFFSET].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_OFFSET].cap |= SANE_CAP_INACTIVE;
+
+
+ if (dev->val[OPT_MANUAL_OFFSET].w == SANE_TRUE)
+ {
+ if ((strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ dev->opt[OPT_GRAY_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->opt[OPT_RED_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_OFFSET].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* rebuild OPT GAIN */
+ dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE;
+
+
+ if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE)
+ {
+ if ((strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ dev->opt[OPT_GRAY_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ {
+ dev->opt[OPT_RED_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_GREEN_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_BLUE_GAIN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+ }
+ }
+ }
+
+
+ DBG (2, "control_option: unknown action %d \n", action);
+ return SANE_STATUS_INVAL;
+}
+
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Umax_PP_Device *dev = handle;
+ int dpi, remain;
+
+ memset (&(dev->params), 0, sizeof (dev->params));
+ DBG (64, "sane_get_parameters\n");
+
+ /* color/gray */
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) != 0)
+ {
+ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) != 0)
+ dev->color = UMAX_PP_MODE_LINEART;
+ else
+ dev->color = UMAX_PP_MODE_GRAYSCALE;
+ }
+ else
+ dev->color = UMAX_PP_MODE_COLOR;
+
+ /* offset control */
+ if (dev->val[OPT_MANUAL_OFFSET].w == SANE_TRUE)
+ {
+ if (dev->color != UMAX_PP_MODE_COLOR)
+ {
+ dev->red_offset = 0;
+ dev->green_offset = (int) (dev->val[OPT_GRAY_OFFSET].w);
+ dev->blue_offset = 0;
+ }
+ else
+ {
+ dev->red_offset = (int) (dev->val[OPT_RED_OFFSET].w);
+ dev->green_offset = (int) (dev->val[OPT_GREEN_OFFSET].w);
+ dev->blue_offset = (int) (dev->val[OPT_BLUE_OFFSET].w);
+ }
+ }
+ else
+ {
+ dev->red_offset = 6;
+ dev->green_offset = 6;
+ dev->blue_offset = 6;
+ }
+
+ /* gain control */
+ if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE)
+ {
+ if (dev->color != UMAX_PP_MODE_COLOR)
+ {
+ dev->red_gain = 0;
+ dev->green_gain = (int) (dev->val[OPT_GRAY_GAIN].w);
+ dev->blue_gain = 0;
+ }
+ else
+ {
+ dev->red_gain = (int) (dev->val[OPT_RED_GAIN].w);
+ dev->green_gain = (int) (dev->val[OPT_GREEN_GAIN].w);
+ dev->blue_gain = (int) (dev->val[OPT_BLUE_GAIN].w);
+ }
+ }
+ else
+ {
+ dev->red_gain = red_gain;
+ dev->green_gain = green_gain;
+ dev->blue_gain = blue_gain;
+ }
+
+ /* geometry */
+ dev->TopX = dev->val[OPT_TL_X].w;
+ dev->TopY = dev->val[OPT_TL_Y].w;
+ dev->BottomX = dev->val[OPT_BR_X].w;
+ dev->BottomY = dev->val[OPT_BR_Y].w;
+
+ /* resolution : only have 75, 150, 300, 600 and 1200 */
+ dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w));
+ if (dpi <= 75)
+ dpi = 75;
+ else if (dpi <= 150)
+ dpi = 150;
+ else if (dpi <= 300)
+ dpi = 300;
+ else if (dpi <= 600)
+ dpi = 600;
+ else
+ dpi = 1200;
+ dev->dpi = dpi;
+
+ DBG (16, "sane_get_parameters: dpi set to %d\n", dpi);
+
+ /* for highest resolutions , width must be aligned on 32 bit word */
+ if (dpi >= 600)
+ {
+ remain = (dev->BottomX - dev->TopX) & 0x03;
+ if (remain)
+ {
+ DBG (64, "sane_get_parameters: %d-%d -> remain is %d\n",
+ dev->BottomX, dev->TopX, remain);
+ if (dev->BottomX + remain < dev->desc->max_h_size)
+ dev->BottomX += remain;
+ else
+ {
+ remain -= (dev->desc->max_h_size - dev->BottomX);
+ dev->BottomX = dev->desc->max_h_size;
+ dev->TopX -= remain;
+ }
+ }
+ }
+
+ if (dev->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+
+ if (dev->val[OPT_GRAY_PREVIEW].w == SANE_TRUE)
+ {
+ DBG (16, "sane_get_parameters: gray preview\n");
+ dev->color = UMAX_PP_MODE_GRAYSCALE;
+ dev->params.format = SANE_FRAME_GRAY;
+ }
+ else
+ {
+ DBG (16, "sane_get_parameters: color preview\n");
+ dev->color = UMAX_PP_MODE_COLOR;
+ dev->params.format = SANE_FRAME_RGB;
+ }
+
+ dev->dpi = 75;
+ dev->TopX = 0;
+ dev->TopY = 0;
+ dev->BottomX = dev->desc->max_h_size;
+ dev->BottomY = dev->desc->max_v_size;
+ }
+
+
+ /* fill params */
+ dev->params.last_frame = SANE_TRUE;
+ dev->params.lines =
+ ((dev->BottomY - dev->TopY) * dev->dpi) / dev->desc->ccd_res;
+ if (dev->dpi >= dev->desc->ccd_res)
+ dpi = dev->desc->ccd_res;
+ else
+ dpi = dev->dpi;
+ dev->params.pixels_per_line =
+ ((dev->BottomX - dev->TopX) * dpi) / dev->desc->ccd_res;
+ if (dev->color == UMAX_PP_MODE_COLOR)
+ {
+ dev->params.bytes_per_line = dev->params.pixels_per_line * 3;
+ dev->params.format = SANE_FRAME_RGB;
+ }
+ else
+ {
+ dev->params.bytes_per_line = dev->params.pixels_per_line;
+ dev->params.format = SANE_FRAME_GRAY;
+ }
+ dev->params.depth = 8;
+
+ /* success */
+ if (params != NULL)
+ memcpy (params, &(dev->params), sizeof (dev->params));
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Umax_PP_Device *dev = handle;
+ int rc, autoset;
+ int delta = 0, points;
+
+ /* sanity check */
+ if (dev->state == UMAX_PP_STATE_SCANNING)
+ {
+ DBG (2, "sane_start: device is already scanning\n");
+ DEBUG ();
+
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* if cancelled, check if head is back home */
+ if (dev->state == UMAX_PP_STATE_CANCELLED)
+ {
+ DBG (2, "sane_start: checking if scanner is parking head .... \n");
+
+ rc = sanei_umax_pp_status ();
+ points = 0;
+
+ /* check if scanner busy parking */
+ /* if so, wait parking completion */
+ DBG (2, "sane_start: scanner busy\n");
+ while ((rc == UMAX1220P_BUSY) && (points < 30))
+ {
+ sleep (1);
+ rc = sanei_umax_pp_status ();
+ points++;
+ }
+ /* timeout waiting for scanner */
+ if (rc == UMAX1220P_BUSY)
+ {
+ DBG (2, "sane_start: scanner still busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ dev->state = UMAX_PP_STATE_IDLE;
+ }
+
+
+ /* get values from options */
+ sane_get_parameters (handle, NULL);
+
+ /* sets lamp flag to TRUE */
+ dev->val[OPT_LAMP_CONTROL].w = SANE_TRUE;
+
+ /* tests if we do auto setting */
+ if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE)
+ autoset = 0;
+ else
+ autoset = 1;
+
+
+ /* call start scan */
+ if (dev->color == UMAX_PP_MODE_COLOR)
+ {
+ delta = umax_pp_get_sync (dev->dpi);
+ points = 2 * delta;
+ /* first lines are 'garbage' for 610P */
+ if (sanei_umax_pp_getastra () < 1210)
+ points *= 2;
+ DBG (64, "sane_start:umax_pp_start(%d,%d,%d,%d,%d,1,%X,%X)\n",
+ dev->TopX,
+ dev->TopY - points,
+ dev->BottomX - dev->TopX,
+ dev->BottomY - dev->TopY + points,
+ dev->dpi,
+ (dev->red_gain << 8) + (dev->green_gain << 4) +
+ dev->blue_gain,
+ (dev->red_offset << 8) + (dev->green_offset << 4) +
+ dev->blue_offset);
+
+ rc = sanei_umax_pp_start (dev->TopX,
+ dev->TopY - points,
+ dev->BottomX - dev->TopX,
+ dev->BottomY - dev->TopY + points,
+ dev->dpi,
+ 2,
+ autoset,
+ (dev->red_gain << 8) |
+ (dev->green_gain << 4) |
+ dev->blue_gain,
+ (dev->red_offset << 8) |
+ (dev->green_offset << 4) |
+ dev->blue_offset, &(dev->bpp), &(dev->tw),
+ &(dev->th));
+ /* we enlarged the scanning zone */
+ /* to allow reordering, we must */
+ /* substract it from real scanning */
+ /* zone */
+ dev->th -= points;
+ DBG (64, "sane_start: bpp=%d,tw=%d,th=%d\n", dev->bpp, dev->tw,
+ dev->th);
+ }
+ else
+ {
+ DBG (64, "sane_start:umax_pp_start(%d,%d,%d,%d,%d,0,%X,%X)\n",
+ dev->TopX,
+ dev->TopY,
+ dev->BottomX - dev->TopX,
+ dev->BottomY - dev->TopY, dev->dpi, dev->gray_gain << 4,
+ dev->gray_offset << 4);
+ rc = sanei_umax_pp_start (dev->TopX,
+ dev->TopY,
+ dev->BottomX - dev->TopX,
+ dev->BottomY - dev->TopY,
+ dev->dpi,
+ 1,
+ autoset,
+ dev->gray_gain << 4,
+ dev->gray_offset << 4, &(dev->bpp),
+ &(dev->tw), &(dev->th));
+ DBG (64, "sane_start: bpp=%d,tw=%d,th=%d\n", dev->bpp, dev->tw,
+ dev->th);
+ }
+
+ if (rc != UMAX1220P_OK)
+ {
+ DBG (2, "sane_start: failure\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* scan started, no bytes read */
+ dev->state = UMAX_PP_STATE_SCANNING;
+ dev->buflen = 0;
+ dev->bufread = 0;
+ dev->read = 0;
+
+ /* leading lines for 610P aren't complete in color mode */
+ /* and should be discarded */
+ if ((sanei_umax_pp_getastra () < 1210)
+ && (dev->color == UMAX_PP_MODE_COLOR))
+ {
+ rc =
+ sanei_umax_pp_read (2 * delta * dev->tw * dev->bpp, dev->tw, dev->dpi,
+ 0,
+ dev->buf + UMAX_PP_RESERVE -
+ 2 * delta * dev->tw * dev->bpp);
+ if (rc != UMAX1220P_OK)
+ {
+ DBG (2, "sane_start: first lines discarding failed\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* in case of color, we have to preload blue and green */
+ /* data to allow reordering while later read */
+ if ((dev->color == UMAX_PP_MODE_COLOR) && (delta > 0))
+ {
+ rc =
+ sanei_umax_pp_read (2 * delta * dev->tw * dev->bpp, dev->tw, dev->dpi,
+ 0,
+ dev->buf + UMAX_PP_RESERVE -
+ 2 * delta * dev->tw * dev->bpp);
+ if (rc != UMAX1220P_OK)
+ {
+ DBG (2, "sane_start: preload buffer failed\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* OK .... */
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ Umax_PP_Device *dev = handle;
+ long int length;
+ int last, rc;
+ int x, y, nl, ll;
+ SANE_Byte *lbuf;
+ int max = 0;
+ int min = 255;
+ int delta = 0;
+
+
+ /* no data until further notice */
+ *len = 0;
+ DBG (64, "sane_read(max_len=%d)\n", max_len);
+ ll = dev->tw * dev->bpp;
+
+ /* sanity check */
+ if (dev->state == UMAX_PP_STATE_CANCELLED)
+ {
+ DBG (2, "sane_read: scan cancelled\n");
+ DEBUG ();
+
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* eof test */
+ if (dev->read >= dev->th * ll)
+ {
+ DBG (2, "sane_read: end of scan reached\n");
+ return SANE_STATUS_EOF;
+ }
+
+ /* read data from scanner if needed */
+ if ((dev->buflen == 0) || (dev->bufread >= dev->buflen))
+ {
+ DBG (64, "sane_read: reading data from scanner\n");
+ /* absolute number of bytes needed */
+ length = ll * dev->th - dev->read;
+
+ /* does all fit in a single last read ? */
+ if (length <= dev->bufsize)
+ {
+ last = 1;
+ }
+ else
+ {
+ last = 0;
+ /* round number of scan lines */
+ length = (dev->bufsize / ll) * ll;
+ }
+
+
+ if (dev->color == UMAX_PP_MODE_COLOR)
+ {
+ delta = umax_pp_get_sync (dev->dpi);
+ rc =
+ sanei_umax_pp_read (length, dev->tw, dev->dpi, last,
+ dev->buf + UMAX_PP_RESERVE);
+ }
+ else
+ rc = sanei_umax_pp_read (length, dev->tw, dev->dpi, last, dev->buf);
+ if (rc != UMAX1220P_OK)
+ return SANE_STATUS_IO_ERROR;
+ dev->buflen = length;
+ DBG (64, "sane_read: got %ld bytes of data from scanner\n", length);
+
+ /* we transform data for software lineart */
+ if (dev->color == UMAX_PP_MODE_LINEART)
+ {
+ DBG (64, "sane_read: software lineart\n");
+
+ for (y = 0; y < length; y++)
+ {
+ if (dev->buf[y] > max)
+ max = dev->buf[y];
+ if (dev->buf[y] < min)
+ min = dev->buf[y];
+ }
+ max = (min + max) / 2;
+ for (y = 0; y < length; y++)
+ {
+ if (dev->buf[y] > max)
+ dev->buf[y] = 255;
+ else
+ dev->buf[y] = 0;
+ }
+ }
+ else if (dev->color == UMAX_PP_MODE_COLOR)
+ {
+ /* number of lines */
+ nl = dev->buflen / ll;
+ DBG (64, "sane_read: reordering %ld bytes of data (lines=%d)\n",
+ length, nl);
+ lbuf = (SANE_Byte *) malloc (dev->bufsize + UMAX_PP_RESERVE);
+ if (lbuf == NULL)
+ {
+ DBG (1, "sane_read: couldn't allocate %ld bytes\n",
+ dev->bufsize + UMAX_PP_RESERVE);
+ return SANE_STATUS_NO_MEM;
+ }
+ /* reorder data in R,G,B values */
+ for (y = 0; y < nl; y++)
+ {
+ for (x = 0; x < dev->tw; x++)
+ {
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ /* green value: sync'ed */
+ lbuf[x * dev->bpp + y * ll + 1 + UMAX_PP_RESERVE] =
+ dev->buf[x + y * ll + 2 * dev->tw + UMAX_PP_RESERVE];
+
+ /* blue value, +delta line ahead of sync */
+ lbuf[x * dev->bpp + y * ll + 2 + UMAX_PP_RESERVE] =
+ dev->buf[x + (y - delta) * ll + dev->tw +
+ UMAX_PP_RESERVE];
+
+ /* red value, +2*delta line ahead of sync */
+ lbuf[x * dev->bpp + y * ll + UMAX_PP_RESERVE] =
+ dev->buf[x + (y - 2 * delta) * ll + UMAX_PP_RESERVE];
+
+ break;
+ default:
+ /* red value: sync'ed */
+ lbuf[x * dev->bpp + y * ll + UMAX_PP_RESERVE] =
+ dev->buf[x + y * ll + 2 * dev->tw + UMAX_PP_RESERVE];
+
+ /* green value, +delta line ahead of sync */
+ lbuf[x * dev->bpp + y * ll + 1 + UMAX_PP_RESERVE] =
+ dev->buf[x + (y - delta) * ll + dev->tw +
+ UMAX_PP_RESERVE];
+
+ /* blue value, +2*delta line ahead of sync */
+ lbuf[x * dev->bpp + y * ll + 2 + UMAX_PP_RESERVE] =
+ dev->buf[x + (y - 2 * delta) * ll + UMAX_PP_RESERVE];
+ }
+ }
+ }
+ /* store last data lines for next reordering */
+ if (!last)
+ memcpy (lbuf + UMAX_PP_RESERVE - 2 * delta * ll,
+ dev->buf + UMAX_PP_RESERVE + dev->buflen - 2 * delta * ll,
+ 2 * delta * ll);
+ free (dev->buf);
+ dev->buf = lbuf;
+ }
+ dev->bufread = 0;
+ }
+
+ /* how much get data we can get from memory buffer */
+ length = dev->buflen - dev->bufread;
+ DBG (64, "sane_read: %ld bytes of data available\n", length);
+ if (length > max_len)
+ length = max_len;
+
+
+
+ if (dev->color == UMAX_PP_MODE_COLOR)
+ memcpy (buf, dev->buf + dev->bufread + UMAX_PP_RESERVE, length);
+ else
+ memcpy (buf, dev->buf + dev->bufread, length);
+ *len = length;
+ dev->bufread += length;
+ dev->read += length;
+ DBG (64, "sane_read: %ld bytes read\n", length);
+
+ return SANE_STATUS_GOOD;
+
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Umax_PP_Device *dev = handle;
+ int rc;
+
+ DBG (64, "sane_cancel\n");
+ if (dev->state == UMAX_PP_STATE_IDLE)
+ {
+ DBG (3, "cancel: cancelling idle \n");
+ return;
+ }
+ if (dev->state == UMAX_PP_STATE_SCANNING)
+ {
+ DBG (3, "cancel: stopping current scan\n");
+
+ dev->buflen = 0;
+
+ dev->state = UMAX_PP_STATE_CANCELLED;
+ sanei_umax_pp_cancel ();
+ }
+ else
+ {
+ /* STATE_CANCELLED */
+ DBG (2, "cancel: checking if scanner is still parking head .... \n");
+
+ rc = sanei_umax_pp_status ();
+
+ /* check if scanner busy parking */
+ if (rc == UMAX1220P_BUSY)
+ {
+ DBG (2, "cancel: scanner busy\n");
+ return;
+ }
+ dev->state = UMAX_PP_STATE_IDLE;
+ }
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (129, "unused arg: handle = %p, non_blocking = %d\n",
+ handle, (int) non_blocking);
+
+ DBG (2, "set_io_mode: not supported\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+
+ DBG (129, "unused arg: handle = %p, fd = %p\n", handle, (void *) fd);
+
+ DBG (2, "get_select_fd: not supported\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/umax_pp.conf.in b/backend/umax_pp.conf.in
new file mode 100644
index 0000000..e7e9fd4
--- /dev/null
+++ b/backend/umax_pp.conf.in
@@ -0,0 +1,64 @@
+# For documentation see sane-umax_pp(5)
+
+# GLOBAL #
+
+# size (in bytes) of scan buffer (default: 2 megabyte)
+option buffer 2097152
+
+
+# the following options are local to this scanner
+# gain for red channel, if not given, will be automatically computed
+# must be between 0 and 15
+#option red-gain 8
+
+# gain for green channel, if not given, will be automatically computed
+# must be between 0 and 15
+#option green-gain 4
+
+# gain for blue channel, if not given, will be automatically computed
+# must be between 0 and 15
+#option blue-gain 8
+
+# offset for red channel, if not given, will default to 0
+# must be between 0 and 15
+#option red-offset 2
+
+# offset for green channel, if not given, will default to 0
+# must be between 0 and 15
+#option green-offset 1
+
+# offset for blue channel, if not given, will default to 0
+# must be between 0 and 15
+#option blue-offset 1
+
+
+#
+#
+# model number
+#
+# valid values are 610, 1220, 1600 and 2000
+#
+# by default, no model, we rely on autodetection
+# in case you have black or 'inverted' scans,
+# you may override detection by providing the
+# model number
+#option astra 1220
+
+# DEVICES #
+
+# specify the port your scanner is connected to.
+#
+# the value 'auto' will make the backend find the correct value
+# by itself, it will scan ppdev, ppi device, then hardware address
+# 'safe-auto' will do the same but won't do direct hardware access
+# on linux systems, you may provide the device name of the ppdev character
+# device : /dev/parport0, /dev/parport1, ......
+#
+# on *BSD, you may provide the device name of the ppi device: /dev/ppi0,
+# /dev/ppi1, ...
+#
+# Possible hardware addresses are 0x378 (lp0)
+# 0x278 (lp2) and 0x3c8 (lp1)
+#
+
+port safe-auto
diff --git a/backend/umax_pp.h b/backend/umax_pp.h
new file mode 100644
index 0000000..27ee95e
--- /dev/null
+++ b/backend/umax_pp.h
@@ -0,0 +1,207 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Umax PP flatbed scanners. */
+
+#ifndef umax_pp_h
+#define umax_pp_h
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <../include/sane/sanei_debug.h>
+
+
+enum Umax_PP_Option
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_GRAY_PREVIEW,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+
+ OPT_LAMP_CONTROL,
+ OPT_UTA_CONTROL,
+
+ OPT_CUSTOM_GAMMA, /* use custom gamma tables? */
+ /* The gamma vectors MUST appear in the order gray, red, green,
+ blue. */
+ OPT_GAMMA_VECTOR,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+
+ OPT_MANUAL_GAIN,
+ OPT_GRAY_GAIN,
+ OPT_RED_GAIN,
+ OPT_GREEN_GAIN,
+ OPT_BLUE_GAIN,
+
+ OPT_MANUAL_OFFSET,
+ OPT_GRAY_OFFSET,
+ OPT_RED_OFFSET,
+ OPT_GREEN_OFFSET,
+ OPT_BLUE_OFFSET,
+
+ /* must come last: */
+ NUM_OPTIONS
+};
+
+
+typedef struct Umax_PP_Descriptor
+{
+ SANE_Device sane;
+
+ SANE_String port;
+ SANE_String ppdevice;
+
+ SANE_Int max_res;
+ SANE_Int ccd_res;
+ SANE_Int max_h_size;
+ SANE_Int max_v_size;
+ long int buf_size;
+ u_char revision;
+
+ /* default values */
+ SANE_Int gray_gain;
+ SANE_Int red_gain;
+ SANE_Int blue_gain;
+ SANE_Int green_gain;
+ SANE_Int gray_offset;
+ SANE_Int red_offset;
+ SANE_Int blue_offset;
+ SANE_Int green_offset;
+}
+Umax_PP_Descriptor;
+
+typedef struct Umax_PP_Device
+{
+ struct Umax_PP_Device *next;
+ Umax_PP_Descriptor *desc;
+
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+
+ SANE_Int gamma_table[4][256];
+
+ int state;
+ int mode;
+
+ int TopX;
+ int TopY;
+ int BottomX;
+ int BottomY;
+
+ int dpi;
+ int gain;
+ int color;
+ int bpp; /* bytes per pixel */
+ int tw; /* target width in pixels */
+ int th; /* target height in pixels */
+
+
+
+ SANE_Byte *calibration;
+
+ SANE_Byte *buf;
+ long int bufsize; /* size of read buffer */
+ long int buflen; /* size of data length in buffer */
+ long int bufread; /* number of bytes read in the buffer */
+ long int read; /* bytes read from previous start scan */
+
+ SANE_Parameters params;
+ SANE_Range dpi_range;
+ SANE_Range x_range;
+ SANE_Range y_range;
+
+ SANE_Int gray_gain;
+ SANE_Int red_gain;
+ SANE_Int blue_gain;
+ SANE_Int green_gain;
+
+ SANE_Int gray_offset;
+ SANE_Int red_offset;
+ SANE_Int blue_offset;
+ SANE_Int green_offset;
+}
+Umax_PP_Device;
+
+
+/**
+ * enumeration of configuration options
+ */
+enum Umax_PP_Configure_Option
+{
+ CFG_BUFFER = 0,
+ CFG_RED_GAIN,
+ CFG_GREEN_GAIN,
+ CFG_BLUE_GAIN,
+ CFG_RED_OFFSET,
+ CFG_GREEN_OFFSET,
+ CFG_BLUE_OFFSET,
+ CFG_VENDOR,
+ CFG_NAME,
+ CFG_MODEL,
+ CFG_ASTRA,
+ NUM_CFG_OPTIONS
+};
+
+#if (!defined __GNUC__ || __GNUC__ < 2 || \
+ __GNUC_MINOR__ < (defined __cplusplus ? 6 : 4))
+
+#define __PRETTY_FUNCTION__ "umax_pp"
+
+#endif
+
+#define DEBUG() DBG(4, "%s(v%d.%d.%d-%s): line %d: debug exception\n", \
+ __PRETTY_FUNCTION__, SANE_CURRENT_MAJOR, V_MINOR, \
+ UMAX_PP_BUILD, UMAX_PP_STATE, __LINE__)
+
+#endif /* umax_pp_h */
diff --git a/backend/umax_pp_low.c b/backend/umax_pp_low.c
new file mode 100644
index 0000000..ef10bd7
--- /dev/null
+++ b/backend/umax_pp_low.c
@@ -0,0 +1,13195 @@
+/**
+ Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Umax PP flatbed scanners. */
+
+#undef BACKEND_NAME
+#define BACKEND_NAME umax_pp_low
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_IO_H
+#include <sys/io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "../include/sane/sanei_debug.h"
+#include <errno.h>
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+#include <dev/ppbus/ppi.h>
+#include <dev/ppbus/ppbconf.h>
+#endif
+
+#ifdef HAVE_MACHINE_CPUFUNC_H
+#include <machine/cpufunc.h>
+#endif
+
+#ifdef HAVE_I386_SET_IOPERM
+#include <machine/sysarch.h>
+#endif
+
+#ifdef HAVE_LINUX_PPDEV_H
+#include <sys/ioctl.h>
+#include <linux/parport.h>
+#include <linux/ppdev.h>
+#endif
+
+/*************************************************/
+/* here we define sanei_inb/sanei_outb based on */
+/* OS dependant inb/outb definitions */
+/* SANE_INB is defined whenever a valid inb/outb */
+/* definition has been found */
+/* once all these work, it might be moved to */
+/* sanei_pio.c */
+/*************************************************/
+
+#ifdef ENABLE_PARPORT_DIRECTIO
+
+#if (! defined SANE_INB ) && ( defined HAVE_SYS_HW_H ) /* OS/2 EMX case */
+#define SANE_INB 1
+static int
+sanei_ioperm (int start, int length, int enable)
+{
+ if (enable)
+ return _portaccess (port, port + length - 1);
+ return 0;
+}
+
+static unsigned char
+sanei_inb (unsigned int port)
+{
+ return _inp8 (port) & 0xFF;
+}
+
+static void
+sanei_outb (unsigned int port, unsigned char value)
+{
+ _outp8 (port, value);
+}
+
+static void
+sanei_insb (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ _inps8 (port, (unsigned char *) addr, count);
+}
+
+static void
+sanei_insl (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ _inps32 (port, (unsigned long *) addr, count);
+}
+
+static void
+sanei_outsb (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ _outps8 (port, (unsigned char *) addr, count);
+}
+
+static void
+sanei_outsl (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ _outps32 (port, (unsigned long *) addr, count);
+}
+#endif /* OS/2 EMX case */
+
+
+
+#if (! defined SANE_INB ) && ( defined HAVE_MACHINE_CPUFUNC_H ) /* FreeBSD case */
+#define SANE_INB 2
+static int
+sanei_ioperm (int start, int length, int enable)
+{
+#ifdef HAVE_I386_SET_IOPERM
+ return i386_set_ioperm (start, length, enable);
+#else
+ int fd = 0;
+
+ /* makes compilers happy */
+ start = length + enable;
+ fd = open ("/dev/io", O_RDONLY);
+ if (fd > 0)
+ return 0;
+ return -1;
+#endif
+}
+
+static unsigned char
+sanei_inb (unsigned int port)
+{
+ return inb (port);
+}
+
+static void
+sanei_outb (unsigned int port, unsigned char value)
+{
+ outb (port, value);
+}
+
+static void
+sanei_insb (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ insb (port, addr, count);
+}
+
+static void
+sanei_insl (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ insl (port, addr, count);
+}
+
+static void
+sanei_outsb (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ outsb (port, addr, count);
+}
+
+static void
+sanei_outsl (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ outsl (port, addr, count);
+}
+#endif /* FreeBSD case */
+
+
+/* linux GCC on i386 */
+#if ( ! defined SANE_INB ) && ( defined HAVE_SYS_IO_H ) && ( defined __GNUC__ ) && ( defined __i386__ )
+#define SANE_INB 3
+
+static int
+sanei_ioperm (int start, int length, int enable)
+{
+#ifdef HAVE_IOPERM
+ return ioperm (start, length, enable);
+#else
+ /* linux without ioperm ? hum ... */
+ /* makes compilers happy */
+ start = length + enable;
+ return 0;
+#endif
+}
+
+static unsigned char
+sanei_inb (unsigned int port)
+{
+ return inb (port);
+}
+
+static void
+sanei_outb (unsigned int port, unsigned char value)
+{
+ outb (value, port);
+}
+
+static void
+sanei_insb (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ insb (port, addr, count);
+}
+
+static void
+sanei_insl (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ insl (port, addr, count);
+}
+
+static void
+sanei_outsb (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ outsb (port, addr, count);
+}
+
+static void
+sanei_outsl (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ /* oddly, 32 bit I/O are done with outsw instead of the expected outsl */
+ outsw (port, addr, count);
+}
+#endif /* linux GCC on i386 */
+
+
+/* linux GCC non i386 */
+#if ( ! defined SANE_INB ) && ( defined HAVE_SYS_IO_H ) && ( defined __GNUC__ ) && ( ! defined __i386__ )
+#define SANE_INB 4
+static int
+sanei_ioperm (int start, int length, int enable)
+{
+#ifdef HAVE_IOPERM
+ return ioperm (start, length, enable);
+#else
+ /* linux without ioperm ? hum ... */
+ /* makes compilers happy */
+ start = length + enable;
+ return 0;
+#endif
+}
+
+static unsigned char
+sanei_inb (unsigned int port)
+{
+ return inb (port);
+}
+
+static void
+sanei_outb (unsigned int port, unsigned char value)
+{
+ outb (value, port);
+}
+
+static void
+sanei_insb (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ addr[i] = sanei_inb (port);
+}
+
+static void
+sanei_insl (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ int i;
+
+ for (i = 0; i < count * 4; i++)
+ addr[i] = sanei_inb (port);
+}
+
+static void
+sanei_outsb (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ sanei_outb (port, addr[i]);
+}
+
+static void
+sanei_outsl (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ int i;
+
+ for (i = 0; i < count * 4; i++)
+ sanei_outb (port, addr[i]);
+}
+#endif /* linux GCC non i386 */
+
+
+/* ICC on i386 */
+#if ( ! defined SANE_INB ) && ( defined __INTEL_COMPILER ) && ( defined __i386__ )
+#define SANE_INB 5
+static int
+sanei_ioperm (int start, int length, int enable)
+{
+#ifdef HAVE_IOPERM
+ return ioperm (start, length, enable);
+#else
+ /* ICC without ioperm() ... */
+ /* makes compilers happy */
+ start = length + enable;
+ return 0;
+#endif
+}
+static unsigned char
+sanei_inb (unsigned int port)
+{
+ unsigned char ret;
+
+ __asm__ __volatile__ ("inb %%dx,%%al":"=a" (ret):"d" ((u_int) port));
+ return ret;
+}
+
+static void
+sanei_outb (unsigned int port, unsigned char value)
+{
+ __asm__ __volatile__ ("outb %%al,%%dx"::"a" (value), "d" ((u_int) port));
+}
+
+static void
+sanei_insb (unsigned int port, void *addr, unsigned long count)
+{
+ __asm__ __volatile__ ("rep ; insb":"=D" (addr), "=c" (count):"d" (port),
+ "0" (addr), "1" (count));
+}
+
+static void
+sanei_insl (unsigned int port, void *addr, unsigned long count)
+{
+ __asm__ __volatile__ ("rep ; insl":"=D" (addr), "=c" (count):"d" (port),
+ "0" (addr), "1" (count));
+}
+
+static void
+sanei_outsb (unsigned int port, const void *addr, unsigned long count)
+{
+ __asm__ __volatile__ ("rep ; outsb":"=S" (addr), "=c" (count):"d" (port),
+ "0" (addr), "1" (count));
+}
+
+static void
+sanei_outsl (unsigned int port, const void *addr, unsigned long count)
+{
+ __asm__ __volatile__ ("rep ; outsl":"=S" (addr), "=c" (count):"d" (port),
+ "0" (addr), "1" (count));
+}
+
+#endif /* ICC on i386 */
+
+/* direct io requested, but no valid inb/oub */
+#if ( ! defined SANE_INB) && ( defined ENABLE_PARPORT_DIRECTIO )
+#warning "ENABLE_PARPORT_DIRECTIO cannot be used du to lack of inb/out definition"
+#undef ENABLE_PARPORT_DIRECTIO
+#endif
+
+#endif /* ENABLE_PARPORT_DIRECTIO */
+/*
+ * no inb/outb without --enable-parport-directio *
+ */
+#ifndef ENABLE_PARPORT_DIRECTIO
+#define SANE_INB 0
+static int
+sanei_ioperm (int start, int length, int enable)
+{
+ /* make compilers happy */
+ enable = start + length;
+
+ /* returns failure */
+ return -1;
+}
+
+static unsigned char
+sanei_inb (unsigned int port)
+{
+ /* makes compilers happy */
+ port = 0;
+ return 255;
+}
+
+static void
+sanei_outb (unsigned int port, unsigned char value)
+{
+ /* makes compilers happy */
+ port = 0;
+ value = 0;
+}
+
+static void
+sanei_insb (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ /* makes compilers happy */
+ if (addr)
+ {
+ port = 0;
+ count = 0;
+ }
+}
+
+static void
+sanei_insl (unsigned int port, unsigned char *addr, unsigned long count)
+{
+ /* makes compilers happy */
+ if (addr)
+ {
+ port = 0;
+ count = 0;
+ }
+}
+
+static void
+sanei_outsb (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ /* makes compilers happy */
+ if (addr)
+ {
+ port = 0;
+ count = 0;
+ }
+}
+
+static void
+sanei_outsl (unsigned int port, const unsigned char *addr,
+ unsigned long count)
+{
+ /* makes compilers happy */
+ if (addr)
+ {
+ port = 0;
+ count = 0;
+ }
+}
+#endif /* ENABLE_PARPORT_DIRECTIO is not defined */
+
+/* we need either direct io or ppdev */
+#if ! defined ENABLE_PARPORT_DIRECTIO && ! defined HAVE_LINUX_PPDEV_H && ! defined HAVE_DEV_PPBUS_PPI_H
+#define IO_SUPPORT_MISSING
+#endif
+
+
+#include "umax_pp_low.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#ifndef __IO__
+#define __IO__
+
+#define DATA gPort+0x00
+#define STATUS gPort+0x01
+#define CONTROL gPort+0x02
+#define EPPADDR gPort+0x03
+#define EPPDATA gPort+0x04
+
+#define ECPDATA gPort+0x400
+#define ECR gPort+0x402
+
+#define FIFO_WAIT 1000
+#endif
+
+static int fonc001 (void);
+static int foncSendWord (int *cmd);
+
+static void setEPPMode (int mode);
+static int getEPPMode (void);
+static void setModel (int model);
+static int getModel (void);
+static int ringScanner (int count, unsigned long delay);
+static int testVersion (int no);
+
+static int probePS2 (unsigned char *dest);
+static int probeEPP (unsigned char *dest);
+static int probeECP (unsigned char *dest);
+
+static int sendCommand (int cmd);
+static void SPPResetLPT (void);
+static int sendWord (int *cmd);
+static int sendData (int *cmd, int len);
+static int receiveData (int *cmd, int len);
+static int sendLength (int *cmd, int len);
+
+static int waitAck (void);
+static void init001 (void);
+static int init002 (int arg);
+static int init005 (int arg);
+
+/* 610p comm functions */
+static int putByte610p (int data);
+static int EPPputByte610p (int data);
+static int sendLength610p (int *cmd);
+static int sendData610p (int *cmd, int len);
+static int receiveData610p (int *cmd, int len);
+static int connect610p (void);
+static int sync610p (void);
+static int cmdSync610p (int cmd);
+static int EPPcmdSync610p (int cmd);
+static int getStatus610p (void);
+static int EPPgetStatus610p (void);
+static int disconnect610p (void);
+static int EPPsendWord610p (int *cmd);
+static int SPPsendWord610p (int *cmd);
+static int cmdSet610p (int cmd, int len, int *buffer);
+static int cmdGet610p (int cmd, int len, int *buffer);
+static int EPPcmdSet610p (int cmd, int len, int *buffer);
+static int EPPcmdGet610p (int cmd, int len, int *buffer);
+static int initScanner610p (int recover);
+static int cmdGetBuffer610p (int cmd, int len, unsigned char *buffer);
+
+
+/* parport mode setting */
+static void compatMode (void);
+static void byteMode (void);
+static void ECPFifoMode (void);
+
+/* block transfer init */
+static void ECPSetBuffer (int size);
+
+/* mode dependant operations */
+static int PS2Something (int reg);
+static void PS2bufferRead (int size, unsigned char *dest);
+static void PS2bufferWrite (int size, unsigned char *source);
+static int PS2registerRead (int reg);
+static void PS2registerWrite (int reg, int value);
+
+static int EPPconnect (void);
+static int EPPregisterRead (int reg);
+static void EPPregisterWrite (int reg, int value);
+static void EPPbufferRead (int size, unsigned char *dest);
+static void EPPbufferWrite (int size, unsigned char *source);
+static void EPPRead32Buffer (int size, unsigned char *dest);
+static void EPPWrite32Buffer (int size, unsigned char *source);
+
+static int ECPconnect (void);
+static void ECPdisconnect (void);
+static int ECPregisterRead (int reg);
+static void ECPregisterWrite (int reg, int value);
+static int ECPbufferRead (int size, unsigned char *dest);
+static void ECPbufferWrite (int size, unsigned char *source);
+static int waitFifoEmpty (void);
+static int waitFifoNotEmpty (void);
+static int waitFifoFull (void);
+
+/* generic operations */
+static int connect (void);
+static void disconnect (void);
+static void bufferRead (int size, unsigned char *dest);
+static void bufferWrite (int size, unsigned char *source);
+static int pausedBufferRead (int size, unsigned char *dest);
+
+
+static void ClearRegister (int reg);
+
+static int connect_epat (int r08);
+static int prologue (int r08);
+static int epilogue (void);
+
+static int cmdSet (int cmd, int len, int *buffer);
+static int cmdGet (int cmd, int len, int *buffer);
+static int cmdSetGet (int cmd, int len, int *buffer);
+
+
+static int cmdGetBuffer (int cmd, int len, unsigned char *buffer);
+static int cmdGetBuffer32 (int cmd, int len, unsigned char *buffer);
+static int cmdGetBlockBuffer (int cmd, int len, int window,
+ unsigned char *buffer);
+
+static void bloc2Decode (int *op);
+static void bloc8Decode (int *op);
+/*
+ * high level operations
+ */
+static int loadDefaultTables (void);
+static int inquire (void);
+static int offsetCalibration (int color, int *offRed, int *offGreen,
+ int *offBlue);
+static int coarseGainCalibration (int color, int dcRed, int dcGreen,
+ int dcBlue, int *vgaRed, int *vgaGreen,
+ int *vgaBlue);
+static int
+shadingCalibration (int color, int dcRed, int dcGreen, int dcBlue,
+ int vgaRed, int vgaGreen, int vgaBlue, int *calibration);
+static int leftShadingCalibration610p (int color, int dcRed, int dcGreen,
+ int dcBlue, int vgaRed, int vgaGreen,
+ int vgaBlue, int *calibration);
+
+#define WRITESLOW(x,y) \
+ PS2registerWrite((x),(y)); \
+ DBG(16,"PS2registerWrite(0x%X,0x%X) passed... (%s:%d)\n",(x),(y),__FILE__,__LINE__);
+#define SLOWNIBBLEREGISTERREAD(x,y) \
+ tmp=PS2registerRead(x);\
+ if(tmp!=y)\
+ {\
+ DBG(0,"PS2registerRead: found 0x%X expected 0x%X (%s:%d)\n",tmp,y,__FILE__,__LINE__);\
+ /*return 0;*/ \
+ }\
+ DBG(16,"PS2registerRead(0x%X)=0x%X passed... (%s:%d)\n",x,y,__FILE__,__LINE__);
+#define REGISTERWRITE(x,y) \
+ registerWrite((x),(y)); \
+ DBG(16,"registerWrite(0x%X,0x%X) passed... (%s:%d)\n",(x),(y),__FILE__,__LINE__);
+#define REGISTERREAD(x,y) \
+ tmp=registerRead(x);\
+ if(tmp!=y)\
+ {\
+ DBG(0,"registerRead, found 0x%X expected 0x%X (%s:%d)\n",tmp,y,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ DBG(16,"registerRead(0x%X)=0x%X passed... (%s:%d)\n",x,y,__FILE__,__LINE__);
+#define TRACE(level,msg) DBG(level, msg" (%s:%d)\n",__FILE__,__LINE__);
+#define CMDSYNC(x) if(sanei_umax_pp_cmdSync(x)!=1)\
+ {\
+ DBG(0,"cmdSync(0x%02X) failed (%s:%d)\n",x,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ DBG(16,"cmdSync(0x%02X)=%02X passed ... (%s:%d)\n",x,sanei_umax_pp_scannerStatus(),__FILE__,__LINE__)
+#define CMDSETGET(cmd,len,sent) if(cmdSetGet(cmd,len,sent)!=1)\
+ {\
+ DBG(0,"cmdSetGet(0x%02X,%d,sent) failed (%s:%d)\n",cmd,len,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ TRACE(16,"cmdSetGet() passed ...")
+#define YOFFSET 40
+#define YOFFSET1220P 40
+#define YOFFSET2000P 40
+#define COMPLETIONWAIT if(completionWait()==0)\
+ {\
+ DBG(0,"completionWait() failed (%s:%d)\n",__FILE__,__LINE__);\
+ return 0;\
+ }\
+ TRACE(16,"completionWait() passed ...")
+#define MOVE(x,y,t) if(move(x,y,t)==0)\
+ {\
+ DBG(0,"move(%d,%d,buffer) failed (%s:%d)\n",x,y,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ TRACE(16,"move() passed ...")
+#define CMDGETBUF(cmd,len,sent) if(cmdGetBuffer(cmd,len,sent)!=1)\
+ {\
+ DBG(0,"cmdGetBuffer(0x%02X,%ld,buffer) failed (%s:%d)\n",cmd,(long)len,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ DBG(16,"cmdGetBuffer(%ld) passed ... (%s:%d)\n",(long)len,__FILE__,__LINE__);
+#define CMDGETBUF32(cmd,len,sent) if(cmdGetBuffer32(cmd,len,sent)!=1)\
+ {\
+ DBG(0,"cmdGetBuffer32(0x%02X,%ld,buffer) failed (%s:%d)\n",cmd,(long)len,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ TRACE(16,"cmdGetBuffer32() passed ...")
+#define CMDSET(cmd,len,sent) if(cmdSet(cmd,len,sent)!=1)\
+ {\
+ DBG(0,"cmdSet(0x%02X,%d,sent) failed (%s:%d)\n",cmd,len,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ TRACE(16,"cmdSet() passed ...")
+#define CMDGET(cmd,len,sent) if(cmdGet(cmd,len,sent)!=1)\
+ {\
+ DBG(0,"cmdGet(0x%02X,%d,read) failed (%s:%d)\n",cmd,len,__FILE__,__LINE__);\
+ return 0;\
+ }\
+ TRACE(16,"cmdGet() passed ...")
+static int gPort = 0x378;
+static float targetCode = 250.0;
+
+/* global control vars */
+static int gControl = 0;
+static int gData = 0;
+static int g674 = 0; /* semble indiquer qu'on utilise les IRQ */
+static int g67D = 0;
+static int g67E = 0;
+static int gEPAT = 0; /* signals fast mode ? */
+static int g6FE = 0;
+static int gECP = 0;
+
+static int gLeft = 144; /* default value for 1220P */
+
+/* default gamma translation table */
+static int ggamma[256] =
+ { 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x12, 0x14, 0x17, 0x19, 0x1B, 0x1D,
+ 0x1F,
+ 0x21, 0x23, 0x24, 0x26, 0x28, 0x2A, 0x2B, 0x2D, 0x2E, 0x30, 0x31, 0x33,
+ 0x34, 0x36, 0x37, 0x39, 0x3A, 0x3B, 0x3D, 0x3E, 0x40, 0x41, 0x42, 0x43,
+ 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4F, 0x50, 0x51, 0x52,
+ 0x53, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5E, 0x5F, 0x60,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C,
+ 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84,
+ 0x85, 0x86, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99,
+ 0x9A, 0x9B, 0x9C, 0x9D, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA2, 0xA3,
+ 0xA4, 0xA5, 0xA6, 0xA7, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAC, 0xAD,
+ 0xAE, 0xAF, 0xB0, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xC0,
+ 0xC1, 0xC2, 0xC2, 0xC3, 0xC4, 0xC5, 0xC5, 0xC6, 0xC7, 0xC8, 0xC8, 0xC9,
+ 0xCA, 0xCB, 0xCB, 0xCC, 0xCD, 0xCE, 0xCE, 0xCF, 0xD0, 0xD1, 0xD1, 0xD2,
+ 0xD3, 0xD4, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB,
+ 0xDC, 0xDC, 0xDD, 0xDE, 0xDE, 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE3,
+ 0xE4, 0xE5, 0xE6, 0xE6, 0xE7, 0xE8, 0xE8, 0xE9, 0xEA, 0xEA, 0xEB, 0xEC,
+ 0xEC, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3, 0xF4,
+ 0xF5, 0xF5, 0xF6, 0xF7, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC,
+ 0xFD, 0xFE, 0xFE, 0xFF
+};
+
+
+/* default gamma translation table */
+static int *ggGreen = ggamma;
+static int *ggBlue = ggamma;
+static int *ggRed = ggamma;
+static int gParport = 0;
+static int gCancel = 0;
+static int gAutoSettings = 1;
+
+
+/*****************************************************************************/
+/* output one byte on given port */
+/*****************************************************************************/
+static void Outb (int port, int value);
+
+/*****************************************************************************/
+/* ouput 'size' bytes stored in 'source' on given port */
+/*****************************************************************************/
+static void Outsb (int port, unsigned char *source, int size);
+
+/*****************************************************************************/
+/* ouput 'size' 32 bits words stored in 'source' on given port */
+/*****************************************************************************/
+static void Outsw (int port, unsigned char *source, int size);
+
+
+/*****************************************************************************/
+/* input one byte from given port */
+/*****************************************************************************/
+static int Inb (int port);
+
+/*****************************************************************************/
+/* input 'size' bytes from given port ans store them in 'dest' */
+/*****************************************************************************/
+static void Insb (int port, unsigned char *dest, int size);
+
+/*****************************************************************************/
+/* input 'size' 32 bits word from given port ans store them in 'dest' */
+/*****************************************************************************/
+static void Insw (int port, unsigned char *dest, int size);
+
+
+
+char **
+sanei_parport_find_port (void)
+{
+ char **ports = NULL;
+#ifdef ENABLE_PARPORT_DIRECTIO
+ int i, addr, ecpaddr;
+ int found = 0;
+ char name[80], buffer[80];
+ FILE *fic = NULL;
+
+ /* direct I/O detection */
+ /* linux 2.4 + 2.6 with proc support */
+ for (i = 0; i < 4; i++)
+ {
+ /* try to ensure loading of lp module */
+ sprintf (name, "/dev/lp%d", i);
+ fic = fopen (name, "wb");
+ if (fic != NULL)
+ fclose (fic);
+ sprintf (name, "/proc/sys/dev/parport/parport%d/base-addr", i);
+ fic = fopen (name, "rb");
+ if (fic != NULL)
+ {
+ fread (buffer, 64, 1, fic);
+ fclose (fic);
+ if (sscanf (buffer, "%d %d", &addr, &ecpaddr) > 0)
+ {
+ DBG (16, "parport at 0x%X\n", addr);
+ ports =
+ (char **) realloc (ports, (found + 2) * sizeof (char *));
+ ports[found] = (char *) malloc (19);
+ sprintf (ports[found], "0x%X", addr);
+ found++;
+ ports[found] = NULL;
+ }
+ }
+ }
+#endif
+ return ports;
+}
+
+
+char **
+sanei_parport_find_device (void)
+{
+ char *devices[] = {
+ /* FreeBSD */
+ "/dev/ppi0",
+ "/dev/ppi1",
+ "/dev/ppi2",
+ "/dev/ppi3",
+ /* linux ppdev with devfs */
+ "/dev/parports/0",
+ "/dev/parports/1",
+ "/dev/parports/2",
+ "/dev/parports/3",
+ /* linux ppdev */
+ "/dev/parport0",
+ "/dev/parport1",
+ "/dev/parport2",
+ "/dev/parport3",
+ NULL
+ };
+ int i, file;
+ int rc = 0;
+ int found = 0;
+ char **ports = NULL;
+
+
+ /* device finding */
+ i = 0;
+ while (devices[i] != NULL)
+ {
+ DBG (16, "Controling %s: ", devices[i]);
+ file = open (devices[i], O_RDWR);
+ if (file < 0)
+ {
+ switch (errno)
+ {
+ case ENOENT:
+#ifdef ENIO
+ case ENXIO:
+#endif
+#ifdef ENODEV
+ case ENODEV:
+#endif
+ DBG (16, "no %s device ...\n", devices[i]);
+ break;
+ case EACCES:
+ DBG (16, "current user cannot use existing %s device ...\n",
+ devices[i]);
+ break;
+ default:
+ perror (devices[i]);
+ }
+ }
+ else
+ {
+#ifdef HAVE_LINUX_PPDEV_H
+ /* on kernel < 2.4.23, you have to CLAIM the device
+ * to check it really exists
+ * we may hang if another program already claimed it
+ */
+ rc = ioctl (file, PPCLAIM);
+ if (rc)
+ {
+ switch (errno)
+ {
+ case ENOENT:
+#ifdef ENXIO
+ case ENXIO:
+#endif
+#ifdef ENODEV
+ case ENODEV:
+#endif
+ DBG (16, "no %s device ...\n", devices[i]);
+ break;
+ case EACCES:
+ DBG (16, "current user cannot use existing %s device ...\n",
+ devices[i]);
+ break;
+ default:
+ DBG (16, "errno=%d\n", errno);
+ perror (devices[i]);
+ }
+ }
+ else
+ {
+ rc = ioctl (file, PPRELEASE);
+ }
+#endif /* HAVE_LINUX_PPDEV_H */
+ close (file);
+ if (!rc)
+ {
+ DBG (16, "adding %s to valid devices ...\n", devices[i]);
+ ports =
+ (char **) realloc (ports, (found + 2) * sizeof (char *));
+ ports[found] = strdup (devices[i]);
+ found++;
+ ports[found] = NULL;
+ }
+ }
+
+ /* suite */
+ i++;
+ }
+ return ports;
+}
+
+
+
+/*
+ * gain direct acces to IO port, and set parport to the 'right' mode
+ * returns 1 on success, 0 an failure
+ */
+
+
+int
+sanei_umax_pp_initPort (int port, char *name)
+{
+ int fd, ectr;
+ int found = 0, ecp = 1;
+#if ((defined HAVE_IOPERM)||(defined HAVE_MACHINE_CPUFUNC_H)||(defined HAVE_LINUX_PPDEV_H))
+ int mode, modes, rc;
+#endif
+#ifdef HAVE_LINUX_PPDEV_H
+ char strmodes[160];
+#endif
+
+ /* since this function must be called before */
+ /* any other, we put debug init here */
+ DBG_INIT ();
+ DBG (1, "SANE_INB level %d\n", SANE_INB);
+
+ /* sets global vars */
+ ggGreen = ggamma;
+ ggBlue = ggamma;
+ ggRed = ggamma;
+ gParport = 0;
+ gCancel = 0;
+ gAutoSettings = 1;
+ gControl = 0;
+ gData = 0;
+ g674 = 0;
+ g67D = 0;
+ g67E = 0;
+ gEPAT = 0;
+ g6FE = 0;
+ sanei_umax_pp_setparport (0);
+
+
+ DBG (1, "sanei_umax_pp_InitPort(0x%X,%s)\n", port, name);
+#ifndef ENABLE_PARPORT_DIRECTIO
+ if ((name == NULL) || ((name != NULL) && (strlen (name) < 4)))
+ {
+ DBG (0, "sanei_umax_pp_InitPort cannot use direct hardware access\n");
+ DBG (0, "if not compiled with --enable-parport-directio\n");
+ return 0;
+ }
+#endif
+
+
+ /* init global var holding port value */
+ gPort = port;
+
+#ifdef IO_SUPPORT_MISSING
+ DBG (1, "*** Direct I/O or ppdev unavailable, giving up ***\n");
+ return 0;
+#else
+
+
+#ifdef HAVE_LINUX_PPDEV_H
+ if (name != NULL)
+ {
+ if (strlen (name) > 3)
+ {
+ /* ppdev opening and configuration */
+ found = 0;
+ fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0)
+ {
+ switch (errno)
+ {
+ case ENOENT:
+ DBG (1, "umax_pp: '%s' does not exist \n", name);
+ break;
+ case EACCES:
+ DBG (1,
+ "umax_pp: current user has not R/W permissions on '%s' \n",
+ name);
+ break;
+ }
+ return 0;
+
+ }
+ /* claim port */
+ if (ioctl (fd, PPCLAIM))
+ {
+ DBG (1, "umax_pp: cannot claim port '%s'\n", name);
+ }
+ else
+ {
+ /* we check if parport does EPP or ECP */
+#ifdef PPGETMODES
+ if (ioctl (fd, PPGETMODES, &modes))
+ {
+ DBG (16,
+ "umax_pp: ppdev couldn't gave modes for port '%s'\n",
+ name);
+ }
+ else
+ {
+ sprintf (strmodes, "\n");
+ if (modes & PARPORT_MODE_PCSPP)
+ sprintf (strmodes, "%s\t\tPARPORT_MODE_PCSPP\n",
+ strmodes);
+ if (modes & PARPORT_MODE_TRISTATE)
+ sprintf (strmodes, "%s\t\tPARPORT_MODE_TRISTATE\n",
+ strmodes);
+ if (modes & PARPORT_MODE_EPP)
+ sprintf (strmodes, "%s\t\tPARPORT_MODE_EPP\n", strmodes);
+ if (modes & PARPORT_MODE_ECP)
+ {
+ sprintf (strmodes, "%s\t\tPARPORT_MODE_ECP\n",
+ strmodes);
+ gECP = 1;
+ }
+ if (modes & PARPORT_MODE_COMPAT)
+ sprintf (strmodes, "%s\t\tPARPORT_MODE_COMPAT\n",
+ strmodes);
+ if (modes & PARPORT_MODE_DMA)
+ sprintf (strmodes, "%s\t\tPARPORT_MODE_DMA\n", strmodes);
+ DBG (32, "parport modes: %X\n", modes);
+ DBG (32, "parport modes: %s\n", strmodes);
+ if (!(modes & PARPORT_MODE_EPP)
+ && !(modes & PARPORT_MODE_ECP))
+ {
+ DBG (1,
+ "port 0x%X does not have EPP or ECP, giving up ...\n",
+ port);
+ mode = IEEE1284_MODE_COMPAT;
+ ioctl (fd, PPSETMODE, &mode);
+ ioctl (fd, PPRELEASE);
+ close (fd);
+ return 0;
+ }
+ }
+
+#else
+ DBG (16,
+ "umax_pp: ppdev used to build SANE doesn't have PPGETMODES.\n");
+ /* faking result */
+ modes = 0xFFFFFFFF;
+#endif
+ mode = 0;
+
+ /* prefered mode is EPP */
+ if (modes & PARPORT_MODE_EPP)
+ {
+ mode = IEEE1284_MODE_EPP;
+
+ /* negot allways fail here ... */
+ rc = ioctl (fd, PPNEGOT, &mode);
+ if (rc)
+ {
+ DBG (16,
+ "umax_pp: ppdev couldn't negociate mode IEEE1284_MODE_EPP for '%s' (ignored)\n",
+ name);
+ }
+ if (ioctl (fd, PPSETMODE, &mode))
+ {
+ DBG (16,
+ "umax_pp: ppdev couldn't set mode to IEEE1284_MODE_EPP for '%s'\n",
+ name);
+ /* signal failure for ECP test */
+ mode = 0;
+ }
+ else
+ {
+ DBG (16,
+ "umax_pp: mode set to PARPORT_MODE_EPP for '%s'\n",
+ name);
+ }
+ }
+
+ if ((modes & PARPORT_MODE_ECP) && (mode == 0))
+ {
+ mode = IEEE1284_MODE_ECP;
+ rc = ioctl (fd, PPNEGOT, &mode);
+ if (rc)
+ {
+ DBG (16,
+ "umax_pp: ppdev couldn't negociate mode IEEE1284_MODE_ECP for '%s' (ignored)\n",
+ name);
+ }
+ if (ioctl (fd, PPSETMODE, &mode))
+ {
+ DBG (16,
+ "umax_pp: ppdev couldn't set mode to IEEE1284_MODE_ECP for '%s'\n",
+ name);
+ DBG (1,
+ "port 0x%X can't be set to EPP or ECP, giving up ...\n",
+ port);
+
+ mode = IEEE1284_MODE_COMPAT;
+ ioctl (fd, PPSETMODE, &mode);
+ ioctl (fd, PPRELEASE);
+ close (fd);
+ return 0;
+ }
+ else
+ {
+ gECP = 1;
+ DBG (16,
+ "umax_pp: mode set to PARPORT_MODE_ECP for '%s'\n",
+ name);
+ }
+ }
+
+
+ /* allways start in compat mode (for probe) */
+ mode = IEEE1284_MODE_COMPAT;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ mode = 1; /* FW IDLE */
+ rc = ioctl (fd, PPSETPHASE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ found = 1;
+
+ }
+
+ if (!found)
+ {
+ DBG (1, "device %s does not fit ...\n", name);
+ }
+ else
+ {
+ DBG (1, "Using %s ...\n", name);
+ sanei_umax_pp_setparport (fd);
+ return 1;
+ }
+ }
+ }
+#endif /* HAVE_LINUX_PPDEV_H */
+
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+/* the ppi device let user access to parallel port on freebsd */
+ if (name != NULL)
+ {
+ if (strlen (name) > 3)
+ {
+ /* ppi opening and configuration */
+ found = 0;
+ fd = open (name, O_RDONLY);
+ if (fd < 0)
+ {
+ switch (errno)
+ {
+ case ENOENT:
+ DBG (1, "umax_pp: '%s' does not exist \n", name);
+ break;
+ case EACCES:
+ DBG (1,
+ "umax_pp: current user has not read permissions on '%s' \n",
+ name);
+ break;
+ }
+ return 0;
+ }
+ else
+ {
+ DBG (1, "Using %s ...\n", name);
+ sanei_umax_pp_setparport (fd);
+ return 1;
+ }
+ }
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+
+ if (port < 0x400)
+ {
+ if (sanei_ioperm (port, 8, 1) != 0)
+ {
+ DBG (1, "sanei_ioperm() could not gain access to 0x%X\n", port);
+ return 0;
+ }
+ DBG (1, "sanei_ioperm(0x%X, 8, 1) OK ...\n", port);
+ }
+
+#ifdef HAVE_IOPERM
+ /* ECP i/o range */
+ if (iopl (3) != 0)
+ {
+ DBG (1, "iopl could not raise IO permission to level 3\n");
+ DBG (1, "*NO* ECP support\n");
+ ecp = 0;
+
+ }
+ else
+ {
+ /* any ECP out there ? */
+ ectr = Inb (ECR);
+ if (ectr != 0xFF)
+ {
+ gECP = 1;
+
+ }
+ }
+#else
+ ecp = 0;
+#endif
+
+
+
+#endif /* IO_SUPPORT_MISSING */
+ return 1;
+}
+
+
+
+
+
+
+static void
+Outb (int port, int value)
+{
+#ifndef IO_SUPPORT_MISSING
+
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, rc, mode, exmode;
+ unsigned char val;
+
+
+ fd = sanei_umax_pp_getparport ();
+ val = (unsigned char) value;
+ if (fd > 0)
+ {
+ /* there should be ECR that doesn't go through ppdev */
+ /* it will leave when all the I/O will be done with ppdev */
+ switch (port - gPort)
+ {
+ case 0:
+ rc = ioctl (fd, PPWDATA, &val);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#ifdef IOLOG
+ DBG (0, "outb DATA,%02X\n", value);
+#endif
+ return;
+ case 2:
+ mode = val & 0x20;
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ val = val & 0xDF;
+ rc = ioctl (fd, PPWCONTROL, &val);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+#ifdef IOLOG
+ DBG (0, "outb CONTROL,%02X\n", value);
+#endif
+ return;
+ case 4:
+ rc = ioctl (fd, PPGETMODE, &exmode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &val, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short write (%s:%d)\n", __FILE__, __LINE__);
+ rc = ioctl (fd, PPSETMODE, &exmode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#ifdef IOLOG
+ DBG (0, "outb EPPDATA,%02X\n", value);
+#endif
+ return;
+ case 3:
+ rc = ioctl (fd, PPGETMODE, &exmode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &val, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short write (%s:%d)\n", __FILE__, __LINE__);
+ rc = ioctl (fd, PPSETMODE, &exmode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ case 0x400:
+ case 0x402:
+ break;
+ default:
+ DBG (16, "Outb(0x%03X,0x%02X) escaped ppdev\n", port, value);
+ return;
+ }
+ }
+#endif /* HAVE_LINUX_PPDEV_H */
+
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+ int fd, rc;
+ unsigned char val;
+
+
+ fd = sanei_umax_pp_getparport ();
+ val = (unsigned char) value;
+ if (fd > 0)
+ {
+ switch (port - gPort)
+ {
+ case 0:
+ rc = ioctl (fd, PPISDATA, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ case 1:
+ rc = ioctl (fd, PPISSTATUS, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ case 2:
+ rc = ioctl (fd, PPISCTRL, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ case 3:
+ rc = ioctl (fd, PPISEPPA, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ case 4:
+ rc = ioctl (fd, PPISEPPD, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ case 0x402:
+ rc = ioctl (fd, PPISECR, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return;
+ default:
+ DBG (16, "Outb(0x%03X,0x%02X) escaped ppi\n", port, value);
+ return;
+ }
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+
+ sanei_outb (port, value);
+
+#endif /* IO_SUPPORT_MISSING */
+}
+
+
+
+
+
+static int
+Inb (int port)
+{
+ int res = 0xFF;
+#ifndef IO_SUPPORT_MISSING
+
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, rc, mode;
+ unsigned char val;
+
+
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ /* there should be ECR that doesn't go through ppdev */
+ /* it will leave when all the I/O will be done with ppdev */
+ switch (port - gPort)
+ {
+ case 0:
+ rc = ioctl (fd, PPRDATA, &val);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+#ifdef IOLOG
+ DBG (0, "inb DATA,%02X\n", res);
+#endif
+ return res;
+
+ case 1:
+ rc = ioctl (fd, PPRSTATUS, &val);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+#ifdef IOLOG
+ DBG (0, "inb STATUS,%02X\n", res);
+#endif
+ return res;
+
+ case 2:
+ rc = ioctl (fd, PPRCONTROL, &val);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+#ifdef IOLOG
+ DBG (0, "inb CONTROL,%02X\n", res);
+#endif
+ return res;
+
+ case 4:
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = read (fd, &val, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short read (%s:%d)\n", __FILE__, __LINE__);
+ mode = 0; /* data_forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+#ifdef IOLOG
+ DBG (0, "inb EPPDATA,%02X\n", res);
+#endif
+ return res;
+ case 0x400:
+ case 0x402:
+ default:
+ DBG (16, "Inb(0x%03X) escaped ppdev\n", port);
+ return 0xFF;
+ }
+ }
+#endif /* HAVE_LINUX_PPDEV_H */
+
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+ int fd, rc;
+ unsigned char val;
+
+
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ switch (port - gPort)
+ {
+ case 0:
+ rc = ioctl (fd, PPIGDATA, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+ return res;
+ case 1:
+ rc = ioctl (fd, PPIGSTATUS, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+ return res;
+ case 2:
+ rc = ioctl (fd, PPIGCTRL, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+ return res;
+ case 3:
+ rc = ioctl (fd, PPIGEPPA, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+ return res;
+ case 4:
+ rc = ioctl (fd, PPIGEPPD, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+ return res;
+ case 0x402:
+ rc = ioctl (fd, PPIGECR, &val);
+ if (rc)
+ DBG (0, "ppi ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ res = val;
+ return res;
+ default:
+ DBG (16, "Inb(0x%03X) escaped ppi\n", port);
+ return 0xFF;
+ }
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+
+ res = sanei_inb (port);
+
+#endif /* IO_SUPPORT_MISSING */
+ return res;
+}
+
+
+static void
+Insb (int port, unsigned char *dest, int size)
+{
+#ifndef IO_SUPPORT_MISSING
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+ int i;
+ if (sanei_umax_pp_getparport () > 0)
+ {
+ for (i = 0; i < size; i++)
+ dest[i] = Inb (port);
+ return;
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+ sanei_insb (port, dest, size);
+
+#endif
+}
+
+static void
+Outsb (int port, unsigned char *source, int size)
+{
+#ifndef IO_SUPPORT_MISSING
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+ int i;
+
+ if (sanei_umax_pp_getparport () > 0)
+ {
+ for (i = 0; i < size; i++)
+ Outb (port, source[i]);
+ return;
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+
+ sanei_outsb (port, source, size);
+
+#endif
+}
+
+
+
+/* size = nb words */
+static void
+Insw (int port, unsigned char *dest, int size)
+{
+#ifndef IO_SUPPORT_MISSING
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+ int i;
+
+ if (sanei_umax_pp_getparport () > 0)
+ {
+ for (i = 0; i < size * 4; i++)
+ dest[i] = Inb (port);
+ return;
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+
+ sanei_insl (port, dest, size);
+
+#endif
+}
+
+static void
+Outsw (int port, unsigned char *source, int size)
+{
+#ifndef IO_SUPPORT_MISSING
+
+#ifdef HAVE_DEV_PPBUS_PPI_H
+ int i;
+
+ if (sanei_umax_pp_getparport () > 0)
+ {
+ for (i = 0; i < size * 4; i++)
+ Outb (port, source[i]);
+ return;
+ }
+#endif /* HAVE_DEV_PPBUS_PPI_H */
+
+ sanei_outsl (port, source, size);
+
+#endif
+}
+
+
+/* we're trying to gather information on the scanner here, */
+/* and published it through an easy interface */
+/* will turn it into a struct when 610P code will be done */
+static int scannerStatus = 0;
+static time_t gTime = 0;
+static time_t gDelay = 0;
+static int epp32 = 1;
+static int gMode = 0;
+static int gprobed = 0;
+static int model = 0x15;
+static int astra = 0;
+static int hasUTA = 0;
+
+/* signals that scan width matches full ccd width */
+static int fullCCDWidth = 0;
+
+int
+sanei_umax_pp_getfull (void)
+{
+ return fullCCDWidth;
+}
+
+void
+sanei_umax_pp_setfull (int mod)
+{
+ fullCCDWidth = mod;
+}
+
+
+int
+sanei_umax_pp_UTA (void)
+{
+ return hasUTA;
+}
+
+int
+sanei_umax_pp_scannerStatus (void)
+{
+ struct timeval tv;
+
+ /* the 610P ASIC needs some time to settle down after probe */
+ if ((gTime > 0) && (gDelay > 0))
+ {
+ gettimeofday (&tv, NULL);
+ /* delay elapsed ? */
+ if (tv.tv_sec - gTime < gDelay)
+ /* still waiting */
+ return ASIC_BIT;
+ /* wait finished */
+ gDelay = 0;
+ gTime = 0;
+ }
+
+ /* 0x07 variant returns status with bit 0 or 1 allways set to 1 */
+ /* so we mask it out */
+ return scannerStatus & 0xFC;
+}
+
+static int
+getEPPMode (void)
+{
+ if (epp32)
+ return 32;
+ return 8;
+}
+
+static void
+setEPPMode (int mode)
+{
+ if (mode == 8)
+ epp32 = 0;
+ else
+ epp32 = 1;
+}
+
+static int
+getModel (void)
+{
+ return model;
+}
+
+static void
+setModel (int mod)
+{
+ model = mod;
+}
+
+
+int
+sanei_umax_pp_getparport (void)
+{
+ return gParport;
+}
+
+void
+sanei_umax_pp_setparport (int fd)
+{
+ gParport = fd;
+}
+
+int
+sanei_umax_pp_getport (void)
+{
+ return gPort;
+}
+
+void
+sanei_umax_pp_setport (int port)
+{
+ gPort = port;
+}
+
+int
+sanei_umax_pp_getastra (void)
+{
+ return astra;
+}
+
+void
+sanei_umax_pp_setastra (int mod)
+{
+ astra = mod;
+}
+
+int
+sanei_umax_pp_getLeft (void)
+{
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ return 92;
+ default:
+ return 144;
+ }
+}
+
+void
+sanei_umax_pp_setLeft (int mod)
+{
+ gLeft = mod;
+}
+
+int
+sanei_umax_pp_getauto (void)
+{
+ return gAutoSettings;
+}
+
+void
+sanei_umax_pp_setauto (int autoset)
+{
+ gAutoSettings = autoset;
+}
+
+#ifdef HAVE_LINUX_PPDEV_H
+/* set to the parallel port needed using ppdev
+ * returns 1 if ok, 0 else
+ */
+static int
+ppdev_set_mode (int mode)
+{
+ int fd, rc;
+
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ {
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/* set parallel port mode to 'compatible'*/
+static void
+compatMode (void)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ if (ppdev_set_mode (IEEE1284_MODE_COMPAT))
+ return;
+#endif
+ if (!gECP)
+ return;
+ Outb (ECR, 0x15);
+}
+
+/* set parallel port mode to 'bidirectionel'*/
+static void
+byteMode (void)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ if (ppdev_set_mode (IEEE1284_MODE_BYTE))
+ return;
+#endif
+ if (!gECP)
+ return;
+ Outb (ECR, 0x35); /* or 0x34 */
+}
+
+/* set parallel port mode to 'fifo'*/
+static void
+ECPFifoMode (void)
+{
+ /* bits 7:5 :
+ 000 Standard Mode
+ 001 Byte Mode
+ 010 Parallel Port FIFO Mode
+ 011 ECP FIFO Mode
+ 100 EPP Mode
+ 101 Reserved
+ 110 FIFO Test Mode
+ 111 Configuration Mode
+ */
+ /* logged as 74/75 */
+#ifdef HAVE_LINUX_PPDEV_H
+ if (ppdev_set_mode (IEEE1284_MODE_ECP))
+ return;
+#endif
+ if (!gECP)
+ return;
+ Outb (ECR, 0x75);
+}
+
+/* wait for ack bit */
+/* return 1 on success, 0 on error */
+static int
+waitAck ()
+{
+ unsigned char breg = 0;
+ int i = 0;
+
+ Outb (CONTROL, 0x0C); /* select printer + initialize printer */
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ breg = Inb (STATUS) & 0xF8;
+ while ((i < 1024) && ((breg & 0x04) == 0))
+ {
+ Outb (CONTROL, 0x0E); /* autolinefeed ?.. */
+ Outb (CONTROL, 0x0E);
+ Outb (CONTROL, 0x0E);
+ breg = Inb (STATUS) & 0xF8;
+ i++;
+ usleep (1000);
+ }
+ if (i == 1024)
+ {
+ DBG (1, "waitAck failed, time-out waiting for Ack (%s:%d)\n",
+ __FILE__, __LINE__);
+ /* return 0; seems to be non-blocking ... */
+ }
+ Outb (CONTROL, 0x04); /* printer reset */
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ return 1;
+}
+
+static int
+waitFifoEmpty (void)
+{
+ int i;
+ unsigned char breg;
+
+ breg = Inb (ECR);
+ i = 0;
+ while ((i < FIFO_WAIT) && ((breg & 0x01) == 0))
+ {
+ breg = Inb (ECR);
+ i++;
+ }
+ if (i == FIFO_WAIT)
+ {
+ DBG (0, "waitFifoEmpty failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+waitFifoNotEmpty (void)
+{
+ int i;
+ unsigned char breg;
+
+ breg = Inb (ECR);
+ i = 0;
+ while ((i < FIFO_WAIT) && ((breg & 0x01) != 0))
+ {
+ breg = Inb (ECR);
+ i++;
+ /* usleep (2000); */
+ }
+ if (i == FIFO_WAIT)
+ {
+ DBG (0, "waitFifoNotEmpty failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+
+static int
+waitFifoFull (void)
+{
+ int i;
+ unsigned char breg;
+
+ breg = Inb (ECR);
+
+ /* two wait loop */
+ /* the first apply to fast data transfer (ie when no scaaning is undergoing) */
+ /* and we fallback to the second in case the scanner is answering slowly */
+ i = 0;
+ while ((i < FIFO_WAIT) && ((breg & 0x02) == 0))
+ {
+ breg = Inb (ECR);
+ i++;
+ }
+ /* don't need to wait any longer */
+ if (i < FIFO_WAIT)
+ return 1;
+ i = 0;
+ while ((i < FIFO_WAIT) && ((breg & 0x02) == 0))
+ {
+ breg = Inb (ECR);
+ i++;
+ usleep (500);
+ }
+ if (i == FIFO_WAIT)
+ {
+ DBG (0, "waitFifoFull failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * surely some register reading in PS2 mode
+ * only one nibble is accessed, may be
+ * PS2RegisterLowNibbleRead(reg)
+ */
+static int
+PS2Something (int reg)
+{
+ unsigned char breg, low, high = 0;
+
+ Outb (CONTROL, 0x04);
+ Outb (DATA, reg); /* register number ? */
+ Outb (CONTROL, 0x06);
+ Outb (CONTROL, 0x06);
+ Outb (CONTROL, 0x06);
+ breg = Inb (STATUS) & 0xF8;
+ low = breg;
+ breg = breg & 0x08;
+ /* surely means register(0x10)=0x0B */
+ /* since reg & 0x08 != 0, high and low nibble
+ * differ, but we don't care, since we surely expect it
+ * to be 0
+ */
+ if (breg != 0x08)
+ {
+ DBG (0, "PS2Something failed, expecting 0x08, got 0x%02X (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ }
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ if (breg != 0x08)
+ high = Inb (STATUS) & 0xF0;
+ return high + ((low & 0xF0) >> 4);
+}
+
+static int
+PS2Read (void)
+{
+ int res;
+ int tmp;
+
+ res = Inb (STATUS);
+ res = Inb (STATUS);
+ res = res & 0xF0;
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+
+ tmp = Inb (STATUS);
+ tmp = Inb (STATUS);
+ tmp = (tmp & 0xF0) >> 4;
+ res = res | tmp;
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 5);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+
+ return res;
+}
+
+
+/******************************************************************************/
+/* PS2registerWrite: write value in register, slow method */
+/******************************************************************************/
+static void
+PS2registerWrite (int reg, int value)
+{
+ /* select register */
+ Outb (DATA, reg | 0x60);
+ Outb (DATA, reg | 0x60);
+ Outb (CONTROL, 0x01);
+ Outb (CONTROL, 0x01);
+ Outb (CONTROL, 0x01);
+
+ /* send value */
+ Outb (DATA, value);
+ Outb (DATA, value);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+}
+
+
+
+/*****************************************************************************/
+/* Send command returns 0 on failure, 1 on success */
+/*****************************************************************************/
+static int
+sendCommand (int cmd)
+{
+ int control;
+ int tmp;
+ int val;
+ int i;
+ int gbufferRead[256]; /* read buffer for command 0x10 */
+
+
+ if (g674 != 0)
+ {
+ DBG (0, "No scanner attached, sendCommand(0x%X) failed\n", cmd);
+ return 0;
+ }
+
+ control = Inb (CONTROL) & 0x3F;
+ tmp = cmd & 0xF8;
+
+
+ if ((g67D != 1) && (tmp != 0xE0) && (tmp != 0x20) && (tmp != 0x40)
+ && (tmp != 0xD0) && (tmp != 0x08) && (tmp != 0x48))
+ {
+ Outb (CONTROL, 4); /* reset printer */
+ }
+ else
+ {
+ val = control & 0x1F;
+ if (g67D != 1)
+ val = val & 0xF; /* no IRQ */
+ val = val | 0x4;
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ }
+
+ /* send sequence */
+ Outb (DATA, 0x22);
+ Outb (DATA, 0x22);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x78);
+ Outb (DATA, 0x78);
+ Outb (DATA, cmd);
+ Outb (DATA, cmd);
+
+ cmd = cmd & 0xF8;
+
+ if ((g67D == 1) && (cmd == 0xE0))
+ {
+ val = Inb (CONTROL);
+ val = (val & 0xC) | 0x01;
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ val = val & 0xC;
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ goto sendCommandEnd;
+ }
+
+ if ((cmd != 8) && (cmd != 0x48))
+ {
+ tmp = Inb (CONTROL);
+ tmp = Inb (CONTROL);
+ tmp = tmp & 0x1E;
+ if (g67D != 1)
+ tmp = tmp & 0xE;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+ }
+
+ if (cmd == 0x10)
+ {
+ tmp = PS2Read ();
+ tmp = tmp * 256 + PS2Read ();
+ goto sendCommandEnd;
+ }
+
+ if (cmd == 8)
+ {
+ if (g67D == 1)
+ {
+ i = 0;
+ while (i < g67E)
+ {
+ tmp = Inb (CONTROL);
+ tmp = Inb (CONTROL);
+ tmp = (tmp & 0x1E) | 0x1;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+ gbufferRead[i] = Inb (STATUS);
+ tmp = tmp & 0x1E;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+
+ /* next read */
+ i++;
+ if (i < g67E)
+ {
+ Outb (DATA, i | 8);
+ Outb (DATA, i | 8);
+ }
+ }
+ goto sendCommandEnd;
+ }
+ else
+ {
+ DBG (0, "UNEXPLORED BRANCH %s:%d\n", __FILE__, __LINE__);
+ return 0;
+ }
+ }
+
+ /* */
+ if (cmd == 0)
+ {
+ i = 0;
+ do
+ {
+ tmp = Inb (CONTROL);
+ tmp = (tmp & 0xE) | 0x1;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+ tmp = tmp & 0xE;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+
+ i++;
+ if (i < g67E)
+ {
+ Outb (DATA, i);
+ Outb (DATA, i);
+ }
+ }
+ while (i < g67E);
+ goto sendCommandEnd;
+ }
+
+ if (cmd == 0x48)
+ {
+ /* this case is unneeded, should fit with the rest */
+ tmp = Inb (CONTROL) & 0x1E;
+ if (g67D != 1)
+ tmp = tmp & 0xE;
+ Outb (CONTROL, tmp | 0x1);
+ Outb (CONTROL, tmp | 0x1);
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+ goto sendCommandEnd;
+ }
+
+ /* */
+ tmp = Inb (CONTROL) & 0x1E;
+ if (g67D != 1)
+ tmp = tmp & 0xE;
+ Outb (CONTROL, tmp | 0x1);
+ Outb (CONTROL, tmp | 0x1);
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+
+ /* success */
+sendCommandEnd:
+
+ if (cmd == 0x48)
+ Outb (CONTROL, (control & 0xF) | 0x4);
+ if (cmd == 0x30)
+ Outb (CONTROL, (gControl & 0xF) | 0x4);
+
+ /* end signature */
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+
+ if (cmd == 8)
+ {
+ Outb (CONTROL, control);
+ return 1;
+ }
+
+ if (cmd == 0x30)
+ {
+ Outb (CONTROL, gControl);
+ return 1;
+ }
+
+ if (cmd != 0xE0)
+ Outb (CONTROL, control);
+
+ return 1;
+}
+
+
+static void
+ClearRegister (int reg)
+{
+
+ /* choose register */
+ Outb (DATA, reg);
+ Outb (DATA, reg);
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 1);
+ if ((g6FE == 0) || (g674 != 0))
+ {
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 1);
+ }
+
+ /* clears it by not sending new value */
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+}
+
+static void
+SPPResetLPT (void)
+{
+ Outb (CONTROL, 0x04);
+}
+
+
+static int
+PS2registerRead (int reg)
+{
+ int low, high;
+
+
+ /* send register number */
+ Outb (DATA, reg);
+ Outb (DATA, reg);
+
+ /* get low nibble */
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 3);
+ Outb (CONTROL, 3);
+ Outb (CONTROL, 3);
+ Outb (CONTROL, 3);
+ low = Inb (STATUS);
+ low = Inb (STATUS);
+
+ /* get high nibble */
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+ high = Inb (STATUS);
+ high = Inb (STATUS);
+
+ /* merge nibbles and return */
+ high = (high & 0xF0) | ((low & 0xF0) >> 4);
+ return high;
+}
+
+
+static void
+PS2bufferRead (int size, unsigned char *dest)
+{
+ int high;
+ int low;
+ int i;
+ int count;
+ int bytel, byteh;
+
+ /* init transfer */
+ Outb (DATA, 7);
+ Outb (DATA, 7);
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 1);
+ Outb (CONTROL, 3);
+ Outb (CONTROL, 3);
+ Outb (CONTROL, 3);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ count = (size - 2) / 2;
+ i = 0;
+ bytel = 0x06; /* signals low byte of word */
+ byteh = 0x07; /* signals high byte of word */
+
+ /* read loop */
+ while (count > 0)
+ {
+ /* low nibble byte 0 */
+ Outb (CONTROL, bytel);
+ Outb (CONTROL, bytel);
+ Outb (CONTROL, bytel);
+ low = Inb (STATUS);
+ if ((low & 0x08) == 0)
+ {
+ /* high nibble <> low nibble */
+ Outb (CONTROL, bytel & 0x05);
+ Outb (CONTROL, bytel & 0x05);
+ Outb (CONTROL, bytel & 0x05);
+ high = Inb (STATUS);
+ }
+ else
+ {
+ high = low;
+ }
+ low = low & 0xF0;
+ high = high & 0xF0;
+ dest[i] = (unsigned char) (high | (low >> 4));
+ i++;
+
+ /* low nibble byte 1 */
+ Outb (CONTROL, byteh);
+ Outb (CONTROL, byteh);
+ Outb (CONTROL, byteh);
+ low = Inb (STATUS);
+ if ((low & 0x08) == 0)
+ {
+ /* high nibble <> low nibble */
+ Outb (CONTROL, byteh & 0x05);
+ Outb (CONTROL, byteh & 0x05);
+ Outb (CONTROL, byteh & 0x05);
+ high = Inb (STATUS);
+ }
+ else
+ {
+ high = low;
+ }
+ low = low & 0xF0;
+ high = high & 0xF0;
+ dest[i] = (unsigned char) (high | (low >> 4));
+ i++;
+
+ /* next read */
+ count--;
+ }
+
+ /* final read */
+ /* low nibble byte 0 */
+ Outb (CONTROL, bytel);
+ Outb (CONTROL, bytel);
+ Outb (CONTROL, bytel);
+ low = Inb (STATUS);
+ if ((low & 0x08) == 0)
+ {
+ /* high nibble <> low nibble */
+ Outb (CONTROL, bytel & 0x05);
+ Outb (CONTROL, bytel & 0x05);
+ Outb (CONTROL, bytel & 0x05);
+ high = Inb (STATUS);
+ }
+ else
+ {
+ high = low;
+ }
+ low = low & 0xF0;
+ high = high & 0xF0;
+ dest[i] = (unsigned char) (high | (low >> 4));
+ i++;
+
+ /* uneven size need an extra read */
+ if ((size & 0x01) == 1)
+ {
+ /* low nibble byte 1 */
+ Outb (CONTROL, byteh);
+ Outb (CONTROL, byteh);
+ Outb (CONTROL, byteh);
+ low = Inb (STATUS);
+ if ((low & 0x08) == 0)
+ {
+ /* high nibble <> low nibble */
+ Outb (CONTROL, byteh & 0x05);
+ Outb (CONTROL, byteh & 0x05);
+ Outb (CONTROL, byteh & 0x05);
+ high = Inb (STATUS);
+ }
+ else
+ {
+ high = low;
+ }
+ low = low & 0xF0;
+ high = high & 0xF0;
+ dest[i] = (unsigned char) (high | (low >> 4));
+ i++;
+
+ /* swap high/low word */
+ count = bytel;
+ bytel = byteh;
+ byteh = count;
+ }
+
+ /* final byte ... */
+ Outb (DATA, 0xFD);
+ Outb (DATA, 0xFD);
+ Outb (DATA, 0xFD);
+
+ /* low nibble */
+ Outb (CONTROL, byteh);
+ Outb (CONTROL, byteh);
+ Outb (CONTROL, byteh);
+ low = Inb (STATUS);
+ if ((low & 0x08) == 0)
+ {
+ /* high nibble <> low nibble */
+ Outb (CONTROL, byteh & 0x05);
+ Outb (CONTROL, byteh & 0x05);
+ Outb (CONTROL, byteh & 0x05);
+ high = Inb (STATUS);
+ }
+ else
+ {
+ high = low;
+ }
+ low = low & 0xF0;
+ high = high & 0xF0;
+ dest[i] = (unsigned char) (high | (low >> 4));
+ i++;
+
+ /* reset port */
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (CONTROL, 0x04);
+}
+
+static void
+PS2bufferWrite (int size, unsigned char *source)
+{
+ int i;
+ int count;
+ int val;
+
+ /* init buffer write */
+ i = 0;
+ count = size / 2;
+ Outb (DATA, 0x67);
+ Outb (CONTROL, 0x01);
+ Outb (CONTROL, 0x01);
+ Outb (CONTROL, 0x05);
+
+ /* write loop */
+ while (count > 0)
+ {
+ /* low byte of word */
+ val = source[i];
+ i++;
+ Outb (DATA, val);
+ Outb (DATA, val);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+
+ /* high byte of word */
+ val = source[i];
+ i++;
+ Outb (DATA, val);
+ Outb (DATA, val);
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x05);
+
+ /* next write */
+ count--;
+ }
+
+ /* termination sequence */
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x05);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+}
+
+
+
+
+static void
+init001 (void)
+{
+ int i;
+ int val;
+ int status;
+
+ ClearRegister (0);
+ Outb (CONTROL, 0x0C);
+ if (g674 != 0)
+ {
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ }
+ Outb (DATA, 0x40);
+ if (g674 != 0)
+ {
+ Outb (DATA, 0x40);
+ Outb (DATA, 0x40);
+ Outb (DATA, 0x40);
+ }
+ Outb (CONTROL, 0x06);
+ Outb (CONTROL, 0x06);
+ Outb (CONTROL, 0x06);
+ if (g674 != 0)
+ {
+ Outb (CONTROL, 0x06);
+ Outb (CONTROL, 0x06);
+ Outb (CONTROL, 0x06);
+ }
+
+ /* sync loop */
+ i = 256;
+ do
+ {
+ status = Inb (STATUS);
+ i--;
+ }
+ while ((i > 0) && ((status & 0x40)));
+ val = 0x0C;
+ if (i > 0)
+ {
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ if (g674 != 0)
+ {
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ Outb (CONTROL, 0x07);
+ }
+ val = 0x04;
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ if (g674 != 0)
+ {
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ }
+ }
+ val = val | 0x0C;
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ if (g674 != 0)
+ {
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ }
+ val = val & 0xF7;
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ if (g674 != 0)
+ {
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ Outb (CONTROL, val);
+ }
+}
+
+/* SPP register reading */
+static int
+init002 (int arg)
+{
+ int data;
+
+ if (arg == 1)
+ return 0;
+ Outb (DATA, 0x0B);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x24);
+ Outb (CONTROL, 0x24);
+ Outb (CONTROL, 0x26);
+ Outb (CONTROL, 0x26);
+
+ data = Inb (DATA);
+ Outb (CONTROL, 0x04);
+ if (data == gEPAT)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * connecct to the EPAT chip, and
+ * prepare command sending
+ */
+static int
+ECPconnect (void)
+{
+ int ret, control, data;
+
+ /* these 3 lines set to 'inital mode' */
+ byteMode (); /*Outb (ECR, 0x20); */
+ Outb (DATA, 0x04); /* gData */
+ Outb (CONTROL, 0x0C); /* gControl */
+
+ Inb (ECR); /* 0x35 */
+ byteMode (); /*Outb (ECR, 0x20); */
+ byteMode (); /*Outb (ECR, 0x20); */
+
+ gData = Inb (DATA);
+ gControl = Inb (CONTROL);
+
+ data = Inb (DATA);
+ control = Inb (CONTROL);
+ Outb (CONTROL, control & 0x1F);
+ control = Inb (CONTROL);
+ Outb (CONTROL, control & 0x1F);
+ sendCommand (0xE0);
+
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ ClearRegister (0);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x04);
+ ClearRegister (0);
+ ret = PS2Something (0x10);
+ if (ret != 0x0B)
+ {
+ DBG (16,
+ "PS2Something returned 0x%02X, 0x0B expected (%s:%d)\n", ret,
+ __FILE__, __LINE__);
+ }
+ return 1;
+}
+
+static void
+EPPdisconnect (void)
+{
+ if (getModel () != 0x07)
+ sendCommand (40);
+ sendCommand (30);
+ Outb (DATA, gData);
+ Outb (CONTROL, gControl);
+}
+
+static void
+ECPdisconnect (void)
+{
+ int control;
+
+ if (getModel () != 0x07) /* guessed */
+ sendCommand (40); /* guessed */
+ sendCommand (0x30);
+ control = Inb (CONTROL) | 0x01;
+ Outb (CONTROL, control);
+ Outb (CONTROL, control);
+ control = control & 0x04;
+ Outb (CONTROL, control);
+ Outb (CONTROL, control);
+ control = control | 0x08;
+ Outb (CONTROL, control);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (CONTROL, control);
+}
+
+static int
+ECPregisterRead (int reg)
+{
+ unsigned char breg, value;
+
+#ifdef HAVE_LINUX_PPDEV_H
+ int rc, mode, fd;
+ unsigned char bval;
+
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ Outb (CONTROL, 0x04);
+ ECPFifoMode ();
+ Outb (DATA, reg);
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = read (fd, &bval, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short read (%s:%d)\n", __FILE__, __LINE__);
+ value = bval;
+ breg = (Inb (CONTROL) & 0x3F);
+ if (breg != 0x20)
+ {
+ DBG (0,
+ "ECPregisterRead failed, expecting 0x20, got 0x%02X (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ }
+
+ /* restore port state */
+ mode = 0; /* data_forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ return value;
+ }
+#endif
+
+ Outb (CONTROL, 0x4);
+
+ /* ECP FIFO mode, interrupt bit, dma disabled,
+ service bit, fifo full=0, fifo empty=0 */
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPregisterRead failed, FIFO time-out (%s:%d)\n",
+ __FILE__, __LINE__);
+ }
+ breg = Inb (ECR);
+
+ Outb (DATA, reg);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPregisterRead failed, FIFO time-out (%s:%d)\n",
+ __FILE__, __LINE__);
+ }
+ breg = Inb (ECR);
+
+ /* byte mode, interrupt bit, dma disabled,
+ service bit, fifo full=0, fifo empty=0 */
+ byteMode (); /*Outb (ECR, 0x20); */
+ Outb (CONTROL, 0x20); /* data reverse */
+
+ /* ECP FIFO mode, interrupt bit, dma disabled,
+ service bit, fifo full=0, fifo empty=0 */
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+ if (waitFifoNotEmpty () == 0)
+ {
+ DBG (0, "ECPregisterRead failed, FIFO time-out (%s:%d)\n",
+ __FILE__, __LINE__);
+ }
+ breg = Inb (ECR);
+ value = Inb (ECPDATA);
+
+ /* according to the spec bit 7 and 6 are unused */
+ breg = (Inb (CONTROL) & 0x3F);
+ if (breg != 0x20)
+ {
+ DBG (0, "ECPregisterRead failed, expecting 0x20, got 0x%02X (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ }
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ return value;
+}
+
+static int
+EPPregisterRead (int reg)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc;
+ unsigned char breg, bval;
+#endif
+ int control;
+ int value;
+
+
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ breg = (unsigned char) (reg);
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &breg, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short write (%s:%d)\n", __FILE__, __LINE__);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = read (fd, &bval, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short read (%s:%d)\n", __FILE__, __LINE__);
+ value = bval;
+
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+
+ return value;
+ }
+ /* if not, direct hardware access */
+#endif
+
+ Outb (EPPADDR, reg);
+ control = Inb (CONTROL);
+ control = (control & 0x1F) | 0x20;
+ Outb (CONTROL, control);
+ value = Inb (EPPDATA);
+ control = Inb (CONTROL);
+ control = control & 0x1F;
+ Outb (CONTROL, control);
+ return 0xFF;
+ /* return value; */
+}
+
+static int
+registerRead (int reg)
+{
+ switch (gMode)
+ {
+ case UMAX_PP_PARPORT_ECP:
+ return ECPregisterRead (reg);
+ case UMAX_PP_PARPORT_BYTE:
+ DBG (0, "STEF: gMode BYTE in registerRead !!\n");
+ return 0xFF;
+ case UMAX_PP_PARPORT_EPP:
+ return EPPregisterRead (reg);
+ case UMAX_PP_PARPORT_PS2:
+ DBG (0, "STEF: gMode PS2 in registerRead !!\n");
+ return PS2registerRead (reg);
+ default:
+ DBG (0, "STEF: gMode unset in registerRead !!\n");
+ return 0xFF;
+ }
+}
+
+
+static void
+ECPregisterWrite (int reg, int value)
+{
+ unsigned char breg;
+
+#ifdef HAVE_LINUX_PPDEV_H
+ int rc, fd;
+
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ ECPFifoMode ();
+ Outb (DATA, reg);
+ breg = value;
+ rc = write (fd, &breg, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short write (%s:%d)\n", __FILE__, __LINE__);
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ return;
+ }
+#endif
+
+ /* standard mode, interrupt bit, dma disabled,
+ service bit, fifo full=0, fifo empty=0 */
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+
+ /* ECP FIFO mode, interrupt bit, dma disabled,
+ service bit, fifo full=0, fifo empty=0 */
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPregisterWrite failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (DATA, reg);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPregisterWrite failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (ECPDATA, value);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPregisterWrite failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ return;
+}
+
+static void
+EPPregisterWrite (int reg, int value)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc;
+ unsigned char breg, bval;
+#endif
+
+ reg = reg | 0x40;
+
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ breg = (unsigned char) (reg);
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &breg, 1);
+ if (rc != 1)
+ DBG (0, "ppdev short write (%s:%d)\n", __FILE__, __LINE__);
+
+ bval = (unsigned char) (value);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ return;
+ }
+ /* if not, direct hardware access */
+#endif
+ Outb (EPPADDR, reg);
+ Outb (EPPDATA, value);
+}
+
+static void
+registerWrite (int reg, int value)
+{
+ switch (gMode)
+ {
+ case UMAX_PP_PARPORT_PS2:
+ PS2registerWrite (reg, value);
+ DBG (0, "STEF: gMode PS2 in registerWrite !!\n");
+ break;
+ case UMAX_PP_PARPORT_ECP:
+ ECPregisterWrite (reg, value);
+ break;
+ case UMAX_PP_PARPORT_EPP:
+ EPPregisterWrite (reg, value);
+ break;
+ case UMAX_PP_PARPORT_BYTE:
+ DBG (0, "STEF: gMode BYTE in registerWrite !!\n");
+ break;
+ default:
+ DBG (0, "STEF: gMode unset in registerWrite !!\n");
+ break;
+ }
+}
+
+static void
+EPPBlockMode (int flag)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc;
+ unsigned char bval;
+
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ bval = (unsigned char) (flag);
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+ return;
+ }
+#endif
+ Outb (EPPADDR, flag);
+}
+
+static void
+EPPbufferRead (int size, unsigned char *dest)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc, nb;
+ unsigned char bval;
+#endif
+ int control;
+
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+
+ bval = 0x80;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ nb = 0;
+ while (nb < size - 1)
+ {
+ rc = read (fd, dest + nb, size - 1 - nb);
+ nb += rc;
+ }
+
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ bval = 0xA0;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = read (fd, dest + size - 1, 1);
+
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+
+ return;
+ }
+ /* if not, direct hardware access */
+#endif
+
+ EPPBlockMode (0x80);
+ control = Inb (CONTROL);
+ Outb (CONTROL, (control & 0x1F) | 0x20); /* reverse */
+ Insb (EPPDATA, dest, size - 1);
+ control = Inb (CONTROL);
+
+ Outb (CONTROL, (control & 0x1F)); /* forward */
+ EPPBlockMode (0xA0);
+ control = Inb (CONTROL);
+
+ Outb (CONTROL, (control & 0x1F) | 0x20); /* reverse */
+ Insb (EPPDATA, (unsigned char *) (dest + size - 1), 1);
+
+ control = Inb (CONTROL);
+ Outb (CONTROL, (control & 0x1F)); /* forward */
+}
+
+
+/* block transfer init */
+static void
+ECPSetBuffer (int size)
+{
+ static int last = 0;
+ unsigned char breg;
+
+ /* routine XX */
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+
+ /* we set size only if it has changed */
+ /* from last time */
+ if (size == last)
+ return;
+ last = size;
+
+ /* mode and size setting */
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (DATA, 0x0E);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (ECPDATA, 0x0B); /* R0E=0x0B */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (DATA, 0x0F); /* R0F=size MSB */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (ECPDATA, size / 256);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (DATA, 0x0B); /* R0B=size LSB */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outb (ECPDATA, size % 256);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPSetBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+ DBG (16, "ECPSetBuffer(%d) passed ...\n", size);
+}
+
+
+
+static int
+ECPbufferRead (int size, unsigned char *dest)
+{
+ int breg, n, idx, remain;
+
+ idx = 0;
+ n = size / 16;
+ remain = size - 16 * n;
+
+ /* block transfer */
+ breg = Inb (ECR); /* 0x15,0x75 expected: fifo empty */
+
+ byteMode (); /*Outb (ECR, 0x20); byte mode */
+ Outb (CONTROL, 0x04);
+
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPbufferRead failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return idx;
+ }
+ breg = Inb (ECR);
+
+ Outb (DATA, 0x80);
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPbufferRead failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return idx;
+ }
+ breg = Inb (ECR); /* 0x75 expected */
+
+ byteMode (); /*Outb (ECR, 0x20); byte mode */
+ Outb (CONTROL, 0x20); /* data reverse */
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+
+ while (n > 0)
+ {
+ if (waitFifoFull () == 0)
+ {
+ DBG (0,
+ "ECPbufferRead failed, time-out waiting for FIFO idx=%d (%s:%d)\n",
+ idx, __FILE__, __LINE__);
+ return idx;
+ }
+ Insb (ECPDATA, dest + idx, 16);
+ idx += 16;
+ n--;
+ }
+
+ /* reading trailing bytes */
+ while (remain > 0)
+ {
+ if (waitFifoNotEmpty () == 0)
+ {
+ DBG (0, "ECPbufferRead failed, FIFO time-out (%s:%d)\n",
+ __FILE__, __LINE__);
+ }
+ dest[idx] = Inb (ECPDATA);
+ idx++;
+ remain--;
+ }
+
+ return idx;
+}
+
+static void
+EPPbufferWrite (int size, unsigned char *source)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc;
+ unsigned char bval;
+#endif
+
+
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ bval = 0xC0;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, source, size);
+ return;
+ }
+ /* if not, direct hardware access */
+#endif
+ EPPBlockMode (0xC0);
+ Outsb (EPPDATA, source, size);
+}
+
+static void
+ECPbufferWrite (int size, unsigned char *source)
+{
+ unsigned char breg;
+ int n, idx;
+
+ /* until we know to handle that case, fail */
+ if (size % 16 != 0)
+ {
+ DBG (0, "ECPbufferWrite failed, size %%16 !=0 (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+
+ /* prepare actual transfer */
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+ breg = Inb (CONTROL);
+ Outb (CONTROL, 0x04); /* data forward */
+ ECPFifoMode (); /*Outb (ECR, 0x60); */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPWriteBuffer failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+ breg = (Inb (STATUS)) & 0xF8;
+ n = 0;
+ while ((n < 1024) && (breg != 0xF8))
+ {
+ breg = (Inb (STATUS)) & 0xF8;
+ n++;
+ }
+ if (breg != 0xF8)
+ {
+ DBG (0,
+ "ECPbufferWrite failed, expected status=0xF8, got 0x%02X (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ return;
+ }
+
+ /* wait for FIFO empty (bit 0) */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPbufferWrite failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ /* block transfer direction
+ * 0x80 means from scanner to PC, 0xC0 means PC to scanner
+ */
+ Outb (DATA, 0xC0);
+
+ n = size / 16;
+ idx = 0;
+ while (n > 0)
+ {
+ /* wait for FIFO empty */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0,
+ "ECPbufferWrite failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+
+ Outsb (ECPDATA, source + idx * 16, 16);
+ idx++;
+ n--;
+ }
+
+
+ /* final FIFO check and go to Byte mode */
+ if (waitFifoEmpty () == 0)
+ {
+ DBG (0, "ECPbufferWrite failed, time-out waiting for FIFO (%s:%d)\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ breg = Inb (ECR);
+ Outb (CONTROL, 0x04);
+ byteMode ();
+}
+
+static void
+bufferWrite (int size, unsigned char *source)
+{
+ switch (gMode)
+ {
+ case UMAX_PP_PARPORT_PS2:
+ PS2bufferWrite (size, source);
+ DBG (0, "STEF: gMode PS2 in bufferWrite !!\n");
+ break;
+ case UMAX_PP_PARPORT_ECP:
+ ECPbufferWrite (size, source);
+ break;
+ case UMAX_PP_PARPORT_EPP:
+ switch (getEPPMode ())
+ {
+ case 32:
+ EPPWrite32Buffer (size, source);
+ break;
+ default:
+ EPPbufferWrite (size, source);
+ break;
+ }
+ break;
+ default:
+ DBG (0, "STEF: gMode PS2 in bufferWrite !!\n");
+ break;
+ }
+ return;
+}
+
+static void
+bufferRead (int size, unsigned char *dest)
+{
+ switch (gMode)
+ {
+ case UMAX_PP_PARPORT_PS2:
+ PS2bufferRead (size, dest);
+ DBG (0, "STEF: gMode PS2 in bufferRead !!\n");
+ break;
+ case UMAX_PP_PARPORT_ECP:
+ ECPbufferRead (size, dest);
+ break;
+ case UMAX_PP_PARPORT_EPP:
+ switch (getEPPMode ())
+ {
+ case 32:
+ EPPRead32Buffer (size, dest);
+ break;
+ default:
+ EPPbufferRead (size, dest);
+ break;
+ }
+ break;
+ default:
+ DBG (0, "STEF: gMode unset in bufferRead !!\n");
+ break;
+ }
+ return;
+}
+
+static int
+connect (void)
+{
+ if (sanei_umax_pp_getastra () == 610)
+ return connect610p ();
+
+ switch (gMode)
+ {
+ case UMAX_PP_PARPORT_PS2:
+ DBG (0, "STEF: unimplemented gMode PS2 in connect() !!\n");
+ return 0;
+ break;
+ case UMAX_PP_PARPORT_ECP:
+ return ECPconnect ();
+ break;
+ case UMAX_PP_PARPORT_BYTE:
+ DBG (0, "STEF: unimplemented gMode BYTE in connect() !!\n");
+ return 0;
+ break;
+ case UMAX_PP_PARPORT_EPP:
+ return EPPconnect ();
+ default:
+ DBG (0, "STEF: gMode unset in connect() !!\n");
+ break;
+ }
+ return 0;
+}
+
+
+static void
+disconnect (void)
+{
+ if (sanei_umax_pp_getastra () == 610)
+ disconnect610p ();
+ switch (gMode)
+ {
+ case UMAX_PP_PARPORT_PS2:
+ DBG (0, "STEF: unimplemented gMode PS2 in disconnect() !!\n");
+ break;
+ case UMAX_PP_PARPORT_ECP:
+ ECPdisconnect ();
+ break;
+ case UMAX_PP_PARPORT_BYTE:
+ DBG (0, "STEF: unimplemented gMode BYTE in disconnect() !!\n");
+ break;
+ case UMAX_PP_PARPORT_EPP:
+ EPPdisconnect ();
+ break;
+ default:
+ DBG (0, "STEF: gMode unset in disconnect() !!\n");
+ break;
+ }
+}
+
+
+/* returns 0 if mode OK, else -1 */
+static int
+checkEPAT (void)
+{
+ int version;
+
+ version = registerRead (0x0B);
+ if (version == 0xC7)
+ return 0;
+ DBG (0, "checkEPAT: expected EPAT version 0xC7, got 0x%X! (%s:%d)\n",
+ version, __FILE__, __LINE__);
+ return -1;
+
+}
+
+static int
+init005 (int arg)
+{
+ int count = 5;
+ int res;
+
+ while (count > 0)
+ {
+ registerWrite (0x0A, arg);
+ Outb (DATA, 0xFF);
+ res = registerRead (0x0A);
+
+ /* failed ? */
+ if (res != arg)
+ return 1;
+
+ /* ror arg */
+ res = arg & 0x01;
+ arg = arg / 2;
+ if (res == 1)
+ arg = arg | 0x80;
+
+ /* next loop */
+ count--;
+ }
+ return 0;
+}
+
+/* write 1 byte in EPP mode, returning scnner's status */
+static int
+EPPputByte610p (int data)
+{
+ int status, control;
+
+ status = Inb (STATUS) & 0xF8;
+ if ((status != 0xC8) && (status != 0xC0) && (status != 0xD0))
+ {
+ DBG (0,
+ "EPPputByte610p failed, expected 0xC8, 0xD0 or 0xC0 got 0x%02X ! (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ control = (Inb (CONTROL) & 0x44) | 0x44; /* data forward, bit 5 cleared (!!) */
+ Outb (CONTROL, control);
+ Outb (EPPDATA, data);
+ return status;
+}
+
+static int
+putByte610p (int data)
+{
+ int status, control, j;
+
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ return EPPputByte610p (data);
+ j = 0;
+ do
+ {
+ status = Inb (STATUS) & 0xF8;
+ j++;
+ }
+ while ((j < 20) && (status & 0x08));
+
+ if ((status != 0xC8) && (status != 0xC0))
+ {
+ DBG (0,
+ "putByte610p failed, expected 0xC8 or 0xC0 got 0x%02X ! (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ control = Inb (CONTROL) & 0x1F; /* data forward */
+ Outb (CONTROL, control);
+
+ Outb (DATA, data);
+ Outb (CONTROL, 0x07);
+ status = Inb (STATUS) & 0xF8;
+ if ((status != 0x48) && (status != 0x40))
+ {
+ DBG (0,
+ "putByte610p failed, expected 0x48 or 0x40 got 0x%02X ! (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+
+
+ Outb (CONTROL, 0x05);
+ status = Inb (STATUS) & 0xF8;
+ Outb (CONTROL, control);
+ return status;
+}
+
+
+/* 1 OK, 0 failure */
+static int
+sync610p (void)
+{
+ int status;
+
+ Outb (DATA, 0x40);
+ Outb (CONTROL, 0x06);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0x38)
+ {
+ DBG (0, "sync610p failed (got 0x%02X expected 0x38)! (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x07);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0x38)
+ {
+ DBG (0, "sync610p failed (got 0x%02X expected 0x38)! (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x04);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0xF8)
+ {
+ DBG (0, "sync610p failed (got 0x%02X expected 0xF8)! (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x05);
+ Inb (CONTROL); /* 0x05 expected */
+ Outb (CONTROL, 0x04);
+ return 1;
+}
+
+static int
+EPPcmdSync610p (int cmd)
+{
+ int word[5];
+ int status;
+ int i;
+
+ word[0] = 0;
+ word[1] = 0;
+ word[2] = 0;
+ word[3] = cmd;
+
+ connect610p ();
+ sync610p ();
+
+ /* sends magic seal 55 AA */
+ status = EPPputByte610p (0x55);
+ if ((status != 0xC8) && (status != 0xC0) && (status != 0xD0))
+ {
+ DBG (1,
+ "EPPcmdSync610p: Found 0x%X expected 0xC8, 0xC0 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ status = EPPputByte610p (0xAA);
+ if ((status != 0xC8) && (status != 0xC0) && (status != 0xD0))
+ {
+ DBG (1,
+ "EPPcmdSync610p: Found 0x%X expected 0xC8, 0xC0 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ status = EPPgetStatus610p ();
+ if (status == 0xC0)
+ for (i = 0; i < 10; i++)
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0xC8)
+ {
+ DBG (0, "EPPcmdSync610p: Found 0x%X expected 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ /*return 0; */
+ }
+
+ /* sends 4 bytes of data */
+ for (i = 0; i < 4; i++)
+ {
+ status = EPPputByte610p (word[i]);
+ }
+ if (status != 0xC8)
+ {
+ DBG (0, "EPPcmdSync610p: Found 0x%X expected 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ /*return 0; */
+ }
+
+ /* tests status */
+ Outb (DATA, 0xFF);
+ if (cmd == 0xC2)
+ {
+ status = EPPgetStatus610p ();
+ if (status != 0xC0)
+ {
+ DBG (0, "EPPcmdSync610p: Found 0x%X expected 0xC0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ /*return 0; */
+ }
+ }
+ status = EPPgetStatus610p ();
+ if (status != 0xC0)
+ {
+ DBG (0, "EPPcmdSync610p: Found 0x%X expected 0xC0 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ /*return 0; */
+ }
+ disconnect610p ();
+ return 1;
+}
+
+static int
+cmdSync610p (int cmd)
+{
+ int word[5];
+ int status;
+
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ return EPPcmdSync610p (cmd);
+
+ word[0] = 0;
+ word[1] = 0;
+ word[2] = 0;
+ word[3] = cmd;
+
+ connect610p ();
+ sync610p ();
+ if (sendLength610p (word) == 0)
+ {
+ DBG (0, "sendLength610p() failed... (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ if (cmd == 0xC2)
+ {
+ status = getStatus610p ();
+ if (status != 0xC0)
+ {
+ DBG (1, "Found 0x%X expected 0xC0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ }
+ status = getStatus610p ();
+ if (status != 0xC0)
+ {
+ DBG (1, "Found 0x%X expected 0xC0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ disconnect610p ();
+ return 1;
+}
+
+static int
+EPPgetStatus610p (void)
+{
+ int data, status, control, i;
+
+ control = Inb (CONTROL) & 0xA4;
+ control = control | 0xE0;
+ Outb (CONTROL, control);
+ status = Inb (STATUS) & 0xF8;
+ if (status & 0x08)
+ {
+ for (i = 1; i < 10; i++)
+ status = Inb (STATUS) & 0xF8;
+ }
+ else
+ {
+ data = Inb (EPPDATA);
+ scannerStatus = data;
+ }
+ return status;
+}
+
+static int
+getStatus610p (void)
+{
+ int data, status;
+
+ byteMode ();
+ status = Inb (STATUS) & 0xF8;
+ Outb (CONTROL, 0x26); /* data reverse */
+ data = Inb (DATA);
+ scannerStatus = data;
+ Outb (CONTROL, 0x24);
+ return status;
+}
+
+
+int
+sendLength610p (int *cmd)
+{
+ int ret, i, wait;
+/* 55,AA,x,y,z,t */
+
+ byteMode ();
+ wait = putByte610p (0x55);
+ if ((wait != 0xC8) && (wait != 0xC0))
+ {
+ DBG (0,
+ "sendLength610p failed, expected 0xC8 or 0xC0 got 0x%02X ! (%s:%d)\n",
+ wait, __FILE__, __LINE__);
+ return 0;
+ }
+ wait = putByte610p (0xAA);
+ if ((wait != 0xC8) && (wait != 0xC0))
+ {
+ DBG (0,
+ "sendLength610p failed, expected 0xC8 or 0xC0 got 0x%02X ! (%s:%d)\n",
+ wait, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* if wait=C0, we have to ... wait */
+ if (wait == 0xC0)
+ {
+ byteMode ();
+ wait = Inb (STATUS); /* C0 expected */
+ Outb (CONTROL, 0x26);
+ ret = Inb (DATA); /* 88 expected */
+ Outb (CONTROL, 0x24);
+ for (i = 0; i < 10; i++)
+ wait = Inb (STATUS); /* C8 expected */
+ byteMode ();
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ ret = putByte610p (cmd[i]);
+ if (ret != 0xC8)
+ {
+ DBG (0,
+ "sendLength610p failed, expected 0xC8 got 0x%02X ! (%s:%d)\n",
+ ret, __FILE__, __LINE__);
+ return 0;
+ }
+ }
+ ret = putByte610p (cmd[3]);
+ if ((ret != 0xC0) && (ret != 0xD0))
+ {
+ DBG (0,
+ "sendLength610p failed, expected 0xC0 or 0xD0 got 0x%02X ! (%s:%d)\n",
+ ret, __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+/* 1 OK, 0 failure */
+static int
+disconnect610p (void)
+{
+ int control, i;
+
+ Outb (CONTROL, 0x04);
+ for (i = 0; i < 41; i++)
+ {
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x04)
+ {
+ DBG (0, "disconnect610p failed (idx %d=%02X)! (%s:%d)\n",
+ i, control, __FILE__, __LINE__);
+ return 0;
+ }
+ }
+ Outb (CONTROL, 0x0C);
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x0C)
+ {
+ DBG (0, "disconnect610p failed expected 0x0C got %02X (%s:%d)\n",
+ control, __FILE__, __LINE__);
+ return 0;
+ }
+ /* XXX STEF XXX Outb (DATA, gData); */
+ Outb (DATA, 0xFF);
+ return 1;
+}
+
+/* 1 OK, 0 failure */
+/* 0: short connect, 1 long connect */
+static int
+connect610p (void)
+{
+ int control;
+
+ gData = Inb (DATA); /* to gDATA ? */
+
+ Outb (DATA, 0xAA);
+ Outb (CONTROL, 0x0E);
+ control = Inb (CONTROL); /* 0x0E expected */
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x0E)
+ {
+ DBG (0, "connect610p control=%02X, expected 0x0E (%s:%d)\n", control,
+ __FILE__, __LINE__);
+ }
+
+ Outb (DATA, 0x00);
+ Outb (CONTROL, 0x0C);
+ control = Inb (CONTROL); /* 0x0C expected */
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x0C)
+ {
+ DBG (0, "connect610p control=%02X, expected 0x0C (%s:%d)\n", control,
+ __FILE__, __LINE__);
+ }
+
+ Outb (DATA, 0x55);
+ Outb (CONTROL, 0x0E);
+ control = Inb (CONTROL); /* 0x0E expected */
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x0E)
+ {
+ DBG (0, "connect610p control=%02X, expected 0x0E (%s:%d)\n", control,
+ __FILE__, __LINE__);
+ }
+
+ Outb (DATA, 0xFF);
+ Outb (CONTROL, 0x0C);
+ control = Inb (CONTROL); /* 0x0C expected */
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x0C)
+ {
+ DBG (0, "connect610p control=%02X, expected 0x0C (%s:%d)\n", control,
+ __FILE__, __LINE__);
+ }
+
+ Outb (CONTROL, 0x04);
+ control = Inb (CONTROL); /* 0x04 expected */
+ control = Inb (CONTROL) & 0x3F;
+ if (control != 0x04)
+ {
+ DBG (0, "connect610p control=%02X, expected 0x04 (%s:%d)\n", control,
+ __FILE__, __LINE__);
+ }
+ return 1;
+}
+
+/* 1 OK, 0 failure */
+static int
+EPPconnect (void)
+{
+ int control;
+ int data;
+
+ /* initial values, don't hardcode */
+ Outb (DATA, 0x04);
+ Outb (CONTROL, 0x0C);
+
+ data = Inb (DATA);
+ control = Inb (CONTROL);
+ Outb (CONTROL, control & 0x1F);
+ control = Inb (CONTROL);
+ Outb (CONTROL, control & 0x1F);
+
+ if (sendCommand (0xE0) != 1)
+ {
+ DBG (0, "EPPconnect: sendCommand(0xE0) failed! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ ClearRegister (0);
+ init001 ();
+ return 1;
+}
+
+
+
+static void
+EPPRead32Buffer (int size, unsigned char *dest)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc, nb;
+ unsigned char bval;
+#endif
+ int control;
+
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+
+ bval = 0x80;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ nb = 0;
+ while (nb < size - 4)
+ {
+ rc = read (fd, dest + nb, size - 4 - nb);
+ nb += rc;
+ }
+
+ rc = read (fd, dest + size - 4, 3);
+
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ bval = 0xA0;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = read (fd, dest + size - 1, 1);
+
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+
+ return;
+ }
+ /* if not, direct hardware access */
+#endif
+
+ EPPBlockMode (0x80);
+
+ control = Inb (CONTROL);
+ Outb (CONTROL, (control & 0x1F) | 0x20);
+ Insw (EPPDATA, dest, size / 4 - 1);
+
+ Insb (EPPDATA, (unsigned char *) (dest + size - 4), 3);
+ control = Inb (CONTROL);
+ Outb (CONTROL, (control & 0x1F));
+
+ EPPBlockMode (0xA0);
+ control = Inb (CONTROL);
+ Outb (CONTROL, (control & 0x1F) | 0x20);
+
+ Insb (EPPDATA, (unsigned char *) (dest + size - 1), 1);
+ control = Inb (CONTROL);
+ Outb (CONTROL, (control & 0x1F));
+}
+
+static void
+EPPWrite32Buffer (int size, unsigned char *source)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc;
+ unsigned char bval;
+#endif
+
+ if ((size % 4) != 0)
+ {
+ DBG (0, "EPPWrite32Buffer: size %% 4 != 0!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+
+ bval = 0xC0;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+#ifdef PPSETFLAGS
+ mode = PP_FASTWRITE;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, source, size);
+
+ return;
+ }
+ /* if not, direct hardware access */
+#endif
+ EPPBlockMode (0xC0);
+ Outsw (EPPDATA, source, size / 4);
+}
+
+
+
+
+
+
+/* returns 0 if ERROR cleared in STATUS within 1024 inb, else 1 */
+static int
+WaitOnError (void)
+{
+ int c = 0;
+ int count = 1024;
+ int status;
+
+ do
+ {
+ do
+ {
+ status = Inb (STATUS) & 0x08;
+ if (status != 0)
+ {
+ count--;
+ if (count == 0)
+ c = 1;
+ }
+ }
+ while ((count > 0) && (status != 0));
+ if (status == 0)
+ {
+ status = Inb (STATUS) & 0x08;
+ if (status == 0)
+ c = 0;
+ }
+ }
+ while ((status != 0) && (c == 0));
+ return c;
+}
+
+
+
+#ifdef HAVE_LINUX_PPDEV_H
+/* read up to size bytes, returns bytes read */
+static int
+ParportpausedBufferRead (int size, unsigned char *dest)
+{
+ unsigned char status, bval;
+ int error;
+ int word;
+ int bread;
+ int c;
+ int fd, rc, mode;
+
+ /* WIP check */
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ DBG (0, "ECP access not implemented yet (WIP) ! (%s:%d)\n",
+ __FILE__, __LINE__);
+ }
+
+ /* init */
+ bread = 0;
+ error = 0;
+ fd = sanei_umax_pp_getparport ();
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+
+ if ((size & 0x03) != 0)
+ {
+ while ((!error) && ((size & 0x03) != 0))
+ {
+ rc = read (fd, dest, 1);
+ size--;
+ dest++;
+ bread++;
+ rc = ioctl (fd, PPRSTATUS, &status);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ error = status & 0x08;
+ }
+ if (error)
+ {
+ DBG (0, "Read error (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ }
+
+ /* from here, we read 1 byte, then size/4-1 32 bits words, and then
+ 3 bytes, pausing on ERROR bit of STATUS */
+ size -= 4;
+
+ /* sanity test, seems to be wrongly handled ... */
+ if (size == 0)
+ {
+ DBG (0, "case not handled! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ word = 0;
+ error = 0;
+ bread += size;
+ do
+ {
+ do
+ {
+ rc = read (fd, dest, 1);
+ size--;
+ dest++;
+ readstatus:
+ if (size > 0)
+ {
+ rc = ioctl (fd, PPRSTATUS, &status);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ word = status & 0x10;
+ error = status & 0x08;
+ }
+ }
+ while ((size > 0) && (!error) && (!word));
+ }
+ while ((size < 4) && (!error) && (size > 0));
+
+ /* here size=0 or error=8 or word=0x10 */
+ if ((word) && (!error) && (size))
+ {
+ rc = read (fd, dest, 4);
+ dest += 4;
+ size -= 4;
+ if (size != 0)
+ error = 0x08;
+ }
+ if (!error)
+ {
+ c = 0;
+ rc = ioctl (fd, PPRSTATUS, &status);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ error = status & 0x08;
+ if (error)
+ c = WaitOnError ();
+ }
+ else
+ { /* 8282 */
+ c = WaitOnError ();
+ if (c == 0)
+ goto readstatus;
+ }
+ if (c == 1)
+ {
+ bread -= size;
+ }
+ else
+ {
+ bread += 3;
+ size = 3;
+ do
+ {
+ do
+ {
+ rc = read (fd, dest, 1);
+ dest++;
+ size--;
+ if (size)
+ {
+ rc = ioctl (fd, PPRSTATUS, &status);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ error = status & 0x08;
+ if (!error)
+ {
+ rc = ioctl (fd, PPRSTATUS, &status);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ error = status & 0x08;
+ }
+ }
+ }
+ while ((size > 0) && (!error));
+ c = 0;
+ if (error)
+ c = WaitOnError ();
+ }
+ while ((size > 0) && (c == 0));
+ }
+
+ /* end reading */
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ bval = 0xA0;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ rc = read (fd, dest, 1);
+ bread++;
+
+ mode = 0; /* forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ return bread;
+}
+#endif
+
+/* read up to size bytes, returns bytes read */
+static int
+DirectpausedBufferRead (int size, unsigned char *dest)
+{
+ int control;
+ int status;
+ int error;
+ int word;
+ int read;
+ int c;
+
+ /* init */
+ read = 0;
+ error = 0;
+ control = Inb (CONTROL) & 0x1F;
+ Outb (CONTROL, control | 0x20);
+ if ((size & 0x03) != 0)
+ {
+ /* 8174 */
+ while ((!error) && ((size & 0x03) != 0))
+ {
+ Insb (EPPDATA, dest, 1);
+ size--;
+ dest++;
+ read++;
+ status = Inb (STATUS) & 0x1F;
+ error = status & 0x08;
+ }
+ if (error)
+ {
+ DBG (0, "Read error (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ }
+
+ /* from here, we read 1 byte, then size/4-1 32 bits words, and then
+ 3 bytes, pausing on ERROR bit of STATUS */
+ size -= 4;
+
+ /* sanity test, seems to be wrongly handled ... */
+ if (size == 0)
+ {
+ DBG (0, "case not handled! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ word = 0;
+ error = 0;
+ read += size;
+ do
+ {
+ do
+ {
+ Insb (EPPDATA, dest, 1);
+ size--;
+ dest++;
+ readstatus:
+ if (size > 0)
+ {
+ status = Inb (STATUS) & 0x1F;
+ word = status & 0x10;
+ error = status & 0x08;
+ }
+ }
+ while ((size > 0) && (!error) && (!word));
+ }
+ while ((size < 4) && (!error) && (size > 0));
+
+ /* here size=0 or error=8 or word=0x10 */
+ if ((word) && (!error) && (size))
+ {
+ if (epp32)
+ Insw (EPPDATA, dest, 1);
+ else
+ Insb (EPPDATA, dest, 4);
+ dest += 4;
+ size -= 4;
+ if (size != 0)
+ error = 0x08;
+ }
+ if (!error)
+ {
+ c = 0;
+ error = Inb (STATUS) & 0x08;
+ if (error)
+ c = WaitOnError ();
+ }
+ else
+ { /* 8282 */
+ c = WaitOnError ();
+ if (c == 0)
+ goto readstatus;
+ }
+ if (c == 1)
+ {
+ read -= size;
+ }
+ else
+ {
+ read += 3;
+ size = 3;
+ do
+ {
+ do
+ {
+ Insb (EPPDATA, dest, 1);
+ dest++;
+ size--;
+ if (size)
+ {
+ error = Inb (STATUS) & 0x08;
+ if (!error)
+ error = Inb (STATUS) & 0x08;
+ }
+ }
+ while ((size > 0) && (!error));
+ c = 0;
+ if (error)
+ c = WaitOnError ();
+ }
+ while ((size > 0) && (c == 0));
+ }
+
+ /* end reading */
+ control = Inb (CONTROL) & 0x1F;
+ Outb (CONTROL, control);
+ EPPBlockMode (0xA0);
+ control = Inb (CONTROL) & 0x1F;
+ Outb (CONTROL, control | 0x20);
+ Insb (EPPDATA, dest, 1);
+ read++;
+ control = Inb (CONTROL) & 0x1F;
+ Outb (CONTROL, control);
+ return read;
+}
+
+
+int
+pausedBufferRead (int size, unsigned char *dest)
+{
+ EPPBlockMode (0x80);
+#ifdef HAVE_LINUX_PPDEV_H
+ if (sanei_umax_pp_getparport () > 0)
+ return ParportpausedBufferRead (size, dest);
+#endif
+ /* only EPP hardware access for now */
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ return DirectpausedBufferRead (size, dest);
+ return 0;
+}
+
+
+
+
+/* returns 1 on success, 0 otherwise */
+static int
+sendWord1220P (int *cmd)
+{
+ int i;
+ int reg;
+ int try = 0;
+
+ /* send header */
+ reg = registerRead (0x19) & 0xF8;
+retry:
+ registerWrite (0x1C, 0x55);
+ reg = registerRead (0x19) & 0xF8;
+
+ registerWrite (0x1C, 0xAA);
+ reg = registerRead (0x19) & 0xF8;
+
+ /* sync when needed */
+ if ((reg & 0x08) == 0x00)
+ {
+ reg = registerRead (0x1C);
+ DBG (16, "UTA: reg1C=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+ if (((reg & 0x10) != 0x10) && (reg != 0x6B) && (reg != 0xAB)
+ && (reg != 0x23))
+ {
+ DBG (0, "sendWord failed (reg1C=0x%02X) (%s:%d)\n", reg, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ for (i = 0; i < 10; i++)
+ {
+ usleep (1000);
+ reg = registerRead (0x19) & 0xF8;
+ if (reg != 0xC8)
+ {
+ DBG (0, "Unexpected reg19=0x%2X (%s:%d)\n", reg, __FILE__,
+ __LINE__);
+ }
+ }
+ do
+ {
+ if ((reg != 0xC0) && (reg != 0xC8))
+ {
+ DBG (0, "Unexpected reg19=0x%2X (%s:%d)\n", reg, __FILE__,
+ __LINE__);
+ }
+ /* 0xF0 certainly means error */
+ if ((reg == 0xC0) || (reg == 0xD0))
+ {
+ try++;
+ goto retry;
+ }
+ reg = registerRead (0x19) & 0xF8;
+ }
+ while (reg != 0xC8);
+ }
+
+ /* send word */
+ i = 0;
+ while ((reg == 0xC8) && (cmd[i] != -1))
+ {
+ registerWrite (0x1C, cmd[i]);
+ i++;
+ reg = registerRead (0x19) & 0xF8;
+ }
+ TRACE (16, "sendWord() passed ");
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0, "sendWord failed got 0x%02X instead of 0xC0 or 0xD0 (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Blindly going on .....\n");
+ }
+ if (((reg == 0xC0) || (reg == 0xD0)) && (cmd[i] != -1))
+ {
+ DBG (0, "sendWord failed: short send (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ reg = registerRead (0x1C);
+ DBG (16, "sendWord, reg1C=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+ /* model 0x07 has always the last bit set to 1, and even bit 1 */
+ /* when UTA is present, we get 0x6B there */
+ scannerStatus = reg & 0xFC;
+ if (scannerStatus == 0x68)
+ hasUTA = 1;
+
+ reg = reg & 0x10;
+ if ((reg != 0x10) && (scannerStatus != 0x68) && (scannerStatus != 0xA8))
+ {
+ DBG (0, "sendWord failed: acknowledge not received (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ if (try)
+ {
+ DBG (0, "sendWord retry success (retry %d time%s) ... (%s:%d)\n", try,
+ (try > 1) ? "s" : "", __FILE__, __LINE__);
+ }
+ return 1;
+}
+
+
+/* returns 1 on success, 0 otherwise */
+static int
+SPPsendWord610p (int *cmd)
+{
+ int i, j;
+ int tmp, status;
+
+#ifdef HAVE_LINUX_PPDEV_H
+ int exmode, mode, rc, fd;
+#endif
+ connect610p ();
+
+#ifdef HAVE_LINUX_PPDEV_H
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ rc = ioctl (fd, PPGETMODE, &exmode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ mode = IEEE1284_MODE_COMPAT;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ }
+#endif
+
+ Outb (DATA, 0x55);
+ Outb (CONTROL, 0x05);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0x88)
+ {
+ DBG (0, "SPPsendWord610p found 0x%02X expected 0x88 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x04);
+
+ Outb (DATA, 0xAA);
+ Outb (CONTROL, 0x05);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0x88)
+ {
+ DBG (0, "SPPsendWord610p found 0x%02X expected 0x88 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x04);
+
+ for (i = 0; i < 4; i++)
+ {
+ Outb (DATA, cmd[i]);
+ Outb (CONTROL, 0x05);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0x88)
+ {
+ DBG (0, "SPPsendWord610p found 0x%02X expected 0x88 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x04);
+ }
+
+ Outb (CONTROL, 0x07);
+ Outb (DATA, 0xFF);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (0, "SPPsendWord610p found 0x%X expected 0xFF (%s:%d)\n", tmp,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ status = Inb (STATUS) & 0xF8;
+ j = 0;
+ while ((j < 256) && (status & 0x08))
+ {
+ j++;
+ status = Inb (STATUS) & 0xF8;
+ }
+ if ((status != 0x80) && (status != 0xA0))
+ {
+ DBG (0, "SPPsendWord610p found 0x%X expected 0x80 or 0xA0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (DATA, 0x7F);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0xC0)
+ {
+ DBG (0, "SPPsendWord610p found 0x%X expected 0xC0 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (DATA, 0xFF);
+ if (cmd[3] == 0xC2)
+ {
+ Outb (CONTROL, 0x07);
+ Outb (DATA, 0xFF);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (0, "SPPsendWord610p found 0x%X expected 0xFF (%s:%d)\n", tmp,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ status = Inb (STATUS) & 0xF8;
+ if ((status != 0x80) && (status != 0xA0))
+ {
+ DBG (0,
+ "SPPsendWord610p found 0x%X expected 0x80 or 0xA0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (DATA, 0x7F);
+ status = Inb (STATUS) & 0xF8;
+ if (status != 0xC0)
+ {
+ DBG (0, "SPPsendWord610p found 0x%X expected 0xC0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (DATA, 0xFF);
+ }
+
+#ifdef HAVE_LINUX_PPDEV_H
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ rc = ioctl (fd, PPSETMODE, &exmode);
+ if (rc)
+ DBG (0, "ppdev ioctl returned <%s> (%s:%d)\n", strerror (errno),
+ __FILE__, __LINE__);
+ }
+#endif
+ disconnect610p ();
+
+ return 1;
+}
+
+/* returns 1 on success, 0 otherwise */
+static int
+EPPsendWord610p (int *cmd)
+{
+ int i;
+ int tmp, control;
+
+ /* send magic tag */
+ tmp = Inb (STATUS) & 0xF8;
+ if (tmp != 0xC8)
+ {
+ DBG (0,
+ "EPPsendWord610p failed, expected tmp=0xC8 , found 0x%02X (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* sets to EPP, and get sure that data direction is forward */
+ tmp = (Inb (CONTROL) & 0x44) | 0x44; /* !! */
+ Outb (CONTROL, tmp);
+ Outb (EPPDATA, 0x55);
+
+ /* bit0 is timeout bit in EPP mode, should we take care of it ? */
+ tmp = Inb (STATUS) & 0xF8;
+ if (tmp != 0xC8)
+ {
+ DBG (0,
+ "EPPsendWord610p failed, expected tmp=0xC8 , found 0x%02X (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ return 0;
+ }
+ tmp = (Inb (CONTROL) & 0x44) | 0x44;
+ Outb (CONTROL, tmp);
+ Outb (EPPDATA, 0xAA);
+
+ control = (Inb (CONTROL) & 0xE0) | 0xE4;
+ Outb (CONTROL, control); /* bit 7 + data reverse + reset */
+ for (i = 0; i < 10; i++)
+ {
+ tmp = Inb (STATUS) & 0xF8;
+ if (tmp != 0xC8)
+ {
+ DBG (0,
+ "EPPsendWord610p failed, expected tmp=0xC8 , found 0x%02X (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ return 0;
+ }
+ }
+
+ i = 0;
+ while ((tmp == 0xC8) && (cmd[i] != -1))
+ {
+ tmp = Inb (STATUS) & 0xF8;
+ control = (Inb (CONTROL) & 0x44) | 0x44; /* !! */
+ Outb (CONTROL, control);
+ Outb (EPPDATA, cmd[i]);
+ i++;
+ }
+
+ /* end */
+ Outb (DATA, 0xFF);
+ control = (Inb (CONTROL) & 0x44) | 0xE4;
+ Outb (CONTROL, control); /* data reverse + ????? */
+ tmp = Inb (STATUS) & 0xF8;
+ if (tmp == 0xC8)
+ {
+ for (i = 0; i < 9; i++)
+ tmp = Inb (STATUS) & 0xF8;
+ scannerStatus = tmp;
+ }
+ else
+ {
+ scannerStatus = Inb (EPPDATA);
+ }
+ if ((tmp != 0xC0) && (tmp != 0xD0))
+ {
+ DBG (0,
+ "EPPsendWord610p failed got 0x%02X instead of 0xC0 or 0xD0 (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * 0: failure
+ * 1: success
+ */
+static int
+sendWord (int *cmd)
+{
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ return sendLength610p (cmd);
+ case 1220:
+ case 1600:
+ case 2000:
+ default:
+ return sendWord1220P (cmd);
+ }
+}
+
+
+
+/******************************************************************************/
+/* ringScanner: returns 1 if scanner present, else 0 */
+/******************************************************************************/
+/*
+ * in fact this function is really close to CPP macro in
+ * /usr/src/linux/drivers/block/paride/epat.c .....
+ * we have almost CPP(8)
+ */
+
+static int
+ringScanner (int count, unsigned long delay)
+{
+ int status;
+ int data;
+ int control;
+ int ret = 1;
+
+ /* save state */
+ data = Inb (DATA);
+ control = Inb (CONTROL) & 0x1F;
+
+ /* send -irq,+reset */
+ Outb (CONTROL, (control & 0xF) | 0x4);
+
+ /* unhandled case */
+ if (g674 == 1)
+ {
+ DBG (1, "OUCH! %s:%d\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* send ring string */
+ Outb (DATA, 0x22);
+ usleep (delay);
+ Outb (DATA, 0x22);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0x22);
+ usleep (delay);
+ Outb (DATA, 0x22);
+ usleep (delay);
+ Outb (DATA, 0x22);
+ usleep (delay);
+ }
+ Outb (DATA, 0xAA);
+ usleep (delay);
+ Outb (DATA, 0xAA);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0xAA);
+ usleep (delay);
+ Outb (DATA, 0xAA);
+ usleep (delay);
+ Outb (DATA, 0xAA);
+ usleep (delay);
+ }
+ Outb (DATA, 0x55);
+ usleep (delay);
+ Outb (DATA, 0x55);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0x55);
+ usleep (delay);
+ Outb (DATA, 0x55);
+ usleep (delay);
+ Outb (DATA, 0x55);
+ usleep (delay);
+ }
+ Outb (DATA, 0x00);
+ usleep (delay);
+ Outb (DATA, 0x00);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0x00);
+ usleep (delay);
+ Outb (DATA, 0x00);
+ usleep (delay);
+ Outb (DATA, 0x00);
+ usleep (delay);
+ }
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ }
+
+ /* OK ? */
+ status = Inb (STATUS) & 0xF8;
+ usleep (delay);
+ if ((status & 0xB8) != 0xB8)
+ {
+ DBG (1, "status %d doesn't match! %s:%d\n", status, __FILE__, __LINE__);
+ ret = 0;
+ }
+
+ /* if OK send 0x87 */
+ if (ret)
+ {
+ Outb (DATA, 0x87);
+ usleep (delay);
+ Outb (DATA, 0x87);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0x87);
+ usleep (delay);
+ Outb (DATA, 0x87);
+ usleep (delay);
+ Outb (DATA, 0x87);
+ usleep (delay);
+ }
+ status = Inb (STATUS);
+ /* status = 126 when scanner not connected .... */
+ if ((status & 0xB8) != 0x18)
+ {
+ DBG (1, "status %d doesn't match! %s:%d\n", status, __FILE__,
+ __LINE__);
+ ret = 0;
+ }
+ }
+
+ /* if OK send 0x78 */
+ if (ret)
+ {
+ Outb (DATA, 0x78);
+ usleep (delay);
+ Outb (DATA, 0x78);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0x78);
+ usleep (delay);
+ Outb (DATA, 0x78);
+ usleep (delay);
+ Outb (DATA, 0x78);
+ usleep (delay);
+ }
+ status = Inb (STATUS);
+ if ((status & 0x30) != 0x30)
+ {
+ DBG (1, "status %d doesn't match! %s:%d\n", status, __FILE__,
+ __LINE__);
+ ret = 0;
+ }
+ }
+
+ /* ring OK, send termination */
+ if (ret)
+ {
+ Outb (DATA, 0x08);
+ usleep (delay);
+ Outb (DATA, 0x08);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0x08);
+ usleep (delay);
+ Outb (DATA, 0x08);
+ usleep (delay);
+ Outb (DATA, 0x08);
+ usleep (delay);
+ }
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ if (count == 5)
+ {
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ Outb (DATA, 0xFF);
+ usleep (delay);
+ }
+ }
+
+ /* restore state */
+ Outb (CONTROL, control);
+ Outb (DATA, data);
+ return ret;
+}
+
+/*****************************************************************************/
+/* test some version : returns 1 on success, 0 otherwise */
+/*****************************************************************************/
+
+
+static int
+testVersion (int no)
+{
+ int data;
+ int status;
+ int control;
+ int count;
+ int tmp;
+
+ data = Inb (DATA);
+ control = Inb (CONTROL) & 0x3F;
+ Outb (CONTROL, (control & 0x1F) | 0x04);
+
+ /* send magic sequence */
+ Outb (DATA, 0x22);
+ Outb (DATA, 0x22);
+ Outb (DATA, 0x22);
+ Outb (DATA, 0x22);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0xAA);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x55);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0x00);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x87);
+ Outb (DATA, 0x78);
+ Outb (DATA, 0x78);
+ Outb (DATA, 0x78);
+ Outb (DATA, 0x78);
+ Outb (DATA, 0x78);
+ Outb (DATA, 0x78);
+ tmp = no | 0x88;
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+
+ /* test status */
+ status = Inb (STATUS);
+ status = Inb (STATUS);
+ if ((status & 0xB8) != 0)
+ {
+ /* 1600P fails here */
+ DBG (64, "status %d doesn't match! %s:%d\n", status, __FILE__,
+ __LINE__);
+ Outb (CONTROL, control);
+ Outb (DATA, data);
+ return 0;
+ }
+
+ count = 0xF0;
+ do
+ {
+ tmp = no | 0x80;
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ tmp = no | 0x88;
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+ Outb (DATA, tmp);
+
+ /* command received ? */
+ status = Inb (STATUS);
+ status = ((status << 1) & 0x70) | (status & 0x80);
+ if (status != count)
+ {
+ /* since failure is expected, we dont't alaways print */
+ /* this message ... */
+ DBG (2, "status %d doesn't match count 0x%X! %s:%d\n", status,
+ count, __FILE__, __LINE__);
+ Outb (CONTROL, control);
+ Outb (DATA, data);
+ return 0;
+ }
+
+ /* next */
+ count -= 0x10;
+ }
+ while (count > 0);
+
+ /* restore port , successful exit */
+ Outb (CONTROL, control);
+ Outb (DATA, data);
+ return 1;
+}
+
+
+/* sends len bytes to scanner */
+/* needs data channel to be set up */
+/* returns 1 on success, 0 otherwise */
+static int
+sendLength (int *cmd, int len)
+{
+ int i;
+ int reg, wait;
+ int try = 0;
+
+ /* send header */
+retry:
+ wait = registerRead (0x19) & 0xF8;
+
+ registerWrite (0x1C, 0x55);
+ reg = registerRead (0x19) & 0xF8;
+
+ registerWrite (0x1C, 0xAA);
+ reg = registerRead (0x19) & 0xF8;
+
+ /* sync when needed */
+ if ((wait & 0x08) == 0x00)
+ {
+ reg = registerRead (0x1C);
+ while (((reg & 0x10) != 0x10) && (reg != 0x6B) && (reg != 0xAB)
+ && (reg != 0x23))
+ {
+ DBG (0,
+ "sendLength failed, expected reg & 0x10=0x10 , found 0x%02X (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ if (try > 10)
+ {
+ DBG (0, "Aborting...\n");
+ return 0;
+ }
+ else
+ {
+ DBG (0, "Retrying ...\n");
+ }
+ /* resend */
+ epilogue ();
+ prologue (0x10);
+ try++;
+ goto retry;
+ }
+ for (i = 0; i < 10; i++)
+ {
+ reg = registerRead (0x19) & 0xF8;
+ if (reg != 0xC8)
+ {
+ DBG (0, "Unexpected reg19=0x%2X (%s:%d)\n", reg, __FILE__,
+ __LINE__);
+ /* 0xF0 certainly means error */
+ if ((reg == 0xC0) || (reg == 0xD0) || (reg == 0x80))
+ {
+ /* resend */
+ try++;
+ if (try > 20)
+ {
+ DBG (0, "sendLength retry failed (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+
+ epilogue ();
+ sendCommand (0x00);
+ sendCommand (0xE0);
+ Outb (DATA, 0x00);
+ Outb (CONTROL, 0x01);
+ Outb (CONTROL, 0x04);
+ sendCommand (0x30);
+
+ prologue (0x10);
+ goto retry;
+ }
+ }
+ }
+ do
+ {
+ if ((reg != 0xC0) && (reg != 0xD0) && (reg != 0xC8))
+ {
+ /* status has changed while waiting */
+ /* but it's too early */
+ DBG (0, "Unexpected reg19=0x%2X (%s:%d)\n", reg, __FILE__,
+ __LINE__);
+ }
+ /* 0xF0 certainly means error */
+ if ((reg == 0xC0) || (reg == 0xD0) || (reg == 0x80))
+ {
+ /* resend */
+ try++;
+ epilogue ();
+
+ sendCommand (0x00);
+ sendCommand (0xE0);
+ Outb (DATA, 0x00);
+ Outb (CONTROL, 0x01);
+ Outb (CONTROL, 0x04);
+ sendCommand (0x30);
+
+ prologue (0x10);
+ goto retry;
+ }
+ reg = registerRead (0x19) & 0xF8;
+ }
+ while (reg != 0xC8);
+ }
+
+ /* send bytes */
+ i = 0;
+ while ((reg == 0xC8) && (i < len))
+ {
+ /* write byte */
+ registerWrite (0x1C, cmd[i]);
+ reg = registerRead (0x19) & 0xF8;
+
+ /* 1B handling: escape it to confirm value */
+ if (cmd[i] == 0x1B)
+ {
+ registerWrite (0x1C, cmd[i]);
+ reg = registerRead (0x19) & 0xF8;
+ }
+ i++;
+ }
+ DBG (16, "sendLength, reg19=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0,
+ "sendLength failed got 0x%02X instead of 0xC0 or 0xD0 (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Blindly going on .....\n");
+ }
+
+ /* check if 'finished status' received too early */
+ if (((reg == 0xC0) || (reg == 0xD0)) && (i != len))
+ {
+ DBG (0, "sendLength failed: sent only %d bytes out of %d (%s:%d)\n", i,
+ len, __FILE__, __LINE__);
+ return 0;
+ }
+
+
+ reg = registerRead (0x1C);
+ DBG (16, "sendLength, reg1C=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+
+ /* model 0x07 has always the last bit set to 1 */
+ scannerStatus = reg & 0xFC;
+ reg = reg & 0x10;
+ if ((reg != 0x10) && (scannerStatus != 0x68) && (scannerStatus != 0xA8))
+ {
+ DBG (0, "sendLength failed: acknowledge not received (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ if (try)
+ {
+ DBG (0, "sendLength retry success (retry %d time%s) ... (%s:%d)\n", try,
+ (try > 1) ? "s" : "", __FILE__, __LINE__);
+ }
+ return 1;
+}
+
+
+/* sends data bytes to scanner */
+/* needs data channel to be set up */
+/* returns 1 on success, 0 otherwise */
+static int
+sendData610p (int *cmd, int len)
+{
+ int i, status, j;
+
+ i = 0;
+ status = 0xC8;
+ /* while ((i < len) && ((status & 0x08) == 0x08)) XXX STEF XXX */
+ while (i < len)
+ {
+ /* escape special values */
+ if (cmd[i] == 0x1B)
+ status = putByte610p (0x1B);
+ if (i > 0)
+ {
+ if ((cmd[i] == 0xAA) && (cmd[i - 1] == 0x55))
+ status = putByte610p (0x1B);
+ }
+ /* regular values */
+ status = putByte610p (cmd[i]);
+ i++;
+ }
+ j = 0;
+ while ((status & 0x08) && (j < 256))
+ {
+ status = getStatus610p ();
+ j++;
+ }
+ if ((status != 0xC0) && (status != 0xD0))
+ {
+ DBG (0,
+ "sendData610p() failed, status=0x%02X, expected 0xC0 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* check if 'finished status' received too early */
+ if (i < len)
+ {
+ DBG (0, "sendData610p failed: sent only %d bytes out of %d (%s:%d)\n",
+ i, len, __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* sends data bytes to scanner */
+/* needs data channel to be set up */
+/* returns 1 on success, 0 otherwise */
+static int
+sendData (int *cmd, int len)
+{
+ int i;
+ int reg;
+
+ if (sanei_umax_pp_getastra () == 610)
+ return sendData610p (cmd, len);
+
+ /* send header */
+ reg = registerRead (0x19) & 0xF8;
+
+ /* send bytes */
+ i = 0;
+ while ((reg == 0xC8) && (i < len))
+ {
+ /* write byte */
+ registerWrite (0x1C, cmd[i]);
+ reg = registerRead (0x19) & 0xF8;
+
+ /* 1B handling: escape it to confirm value */
+ if (cmd[i] == 0x1B)
+ {
+ registerWrite (0x1C, 0x1B);
+ reg = registerRead (0x19) & 0xF8;
+ }
+
+ /* escape 55 AA pattern by adding 1B */
+ if ((i < len - 1) && (cmd[i] == 0x55) && (cmd[i + 1] == 0xAA))
+ {
+ registerWrite (0x1C, 0x1B);
+ reg = registerRead (0x19) & 0xF8;
+ }
+
+ /* next value */
+ i++;
+ }
+ DBG (16, "sendData, reg19=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0, "sendData failed got 0x%02X instead of 0xC0 or 0xD0 (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Blindly going on .....\n");
+ }
+
+ /* check if 'finished status' received too early */
+ if (((reg == 0xC0) || (reg == 0xD0)) && (i < len))
+ {
+ DBG (0, "sendData failed: sent only %d bytes out of %d (%s:%d)\n", i,
+ len, __FILE__, __LINE__);
+ return 0;
+ }
+
+
+ reg = registerRead (0x1C);
+ DBG (16, "sendData, reg1C=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+
+ /* model 0x07 has always the last bit set to 1 */
+ scannerStatus = reg & 0xFC;
+ reg = reg & 0x10;
+ if ((reg != 0x10) && (scannerStatus != 0x68) && (scannerStatus != 0xA8)
+ && (scannerStatus != 0x20))
+ {
+ DBG (0, "sendData failed: acknowledge not received (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* receive data bytes from scanner */
+/* needs data channel to be set up */
+/* returns 1 on success, 0 otherwise */
+/* uses pausedBufferRead */
+static int
+pausedReadData (int size, unsigned char *dest)
+{
+ int reg;
+ int tmp;
+ int read;
+
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+ reg = registerRead (0x19) & 0xF8;
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0, "Unexpected reg19: 0x%02X instead of 0xC0 or 0xD0 (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ return 0;
+ }
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x1A, 0x44);
+ }
+ REGISTERREAD (0x0C, 0x04);
+ REGISTERWRITE (0x0C, 0x44); /* sets data direction ? */
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+ ECPSetBuffer (size);
+ read = ECPbufferRead (size, dest);
+ DBG (16, "ECPbufferRead(%d,dest) passed (%s:%d)\n", size, __FILE__,
+ __LINE__);
+ REGISTERWRITE (0x1A, 0x84);
+ }
+ else
+ {
+ read = pausedBufferRead (size, dest);
+ }
+ if (read < size)
+ {
+ DBG (16,
+ "pausedBufferRead(%d,dest) failed, only got %d bytes (%s:%d)\n",
+ size, read, __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "pausedBufferRead(%d,dest) passed (%s:%d)\n", size, __FILE__,
+ __LINE__);
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+ return 1;
+}
+
+
+
+/* receive data bytes from scanner */
+/* needs data channel to be set up */
+/* returns 1 on success, 0 otherwise */
+static int
+receiveData610p (int *cmd, int len)
+{
+ int i;
+ int status;
+
+ i = 0;
+ status = 0xD0;
+ byteMode ();
+ while (i < len)
+ {
+ status = Inb (STATUS) & 0xF8;
+ Outb (CONTROL, 0x26); /* data reverse+ 'reg' */
+ cmd[i] = Inb (DATA);
+ Outb (CONTROL, 0x24); /* data reverse+ 'reg' */
+ i++;
+ }
+ if (status != 0xC0)
+ {
+ DBG (0, "receiveData610p failed got 0x%02X instead of 0xC0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ DBG (0, "Blindly going on .....\n");
+ }
+
+ /* check if 'finished status' received to early */
+ if ((status == 0xC0) && (i != len))
+ {
+ DBG (0,
+ "receiveData610p failed: received only %d bytes out of %d (%s:%d)\n",
+ i, len, __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+/* receive data bytes from scanner */
+/* needs data channel to be set up */
+/* returns 1 on success, 0 otherwise */
+static int
+receiveData (int *cmd, int len)
+{
+ int i;
+ int reg;
+
+ /* send header */
+ reg = registerRead (0x19) & 0xF8;
+
+ /* send bytes */
+ i = 0;
+ while (((reg == 0xD0) || (reg == 0xC0)) && (i < len))
+ {
+ /* write byte */
+ cmd[i] = registerRead (0x1C);
+ reg = registerRead (0x19) & 0xF8;
+ i++;
+ }
+ DBG (16, "receiveData, reg19=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0, "sendData failed got 0x%02X instead of 0xC0 or 0xD0 (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Blindly going on .....\n");
+ }
+
+ /* check if 'finished status' received to early */
+ if (((reg == 0xC0) || (reg == 0xD0)) && (i != len))
+ {
+ DBG (0,
+ "receiveData failed: received only %d bytes out of %d (%s:%d)\n",
+ i, len, __FILE__, __LINE__);
+ return 0;
+ }
+
+
+ reg = registerRead (0x1C);
+ DBG (16, "receiveData, reg1C=0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+
+ /* model 0x07 has always the last bit set to 1 */
+ scannerStatus = reg & 0xF8;
+ reg = reg & 0x10;
+ if ((reg != 0x10) && (scannerStatus != 0x68) && (scannerStatus != 0xA8))
+ {
+ DBG (0, "receiveData failed: acknowledge not received (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* 1=success, 0 failed */
+static int
+fonc001 (void)
+{
+ int i;
+ int res;
+ int reg;
+
+ res = 1;
+ while (res == 1)
+ {
+ registerWrite (0x1A, 0x0C);
+ registerWrite (0x18, 0x40);
+
+ /* send 0x06 */
+ registerWrite (0x1A, 0x06);
+ for (i = 0; i < 10; i++)
+ {
+ reg = registerRead (0x19) & 0xF8;
+ if ((reg & 0x78) == 0x38)
+ {
+ res = 0;
+ break;
+ }
+ }
+ if (res == 1)
+ {
+ registerWrite (0x1A, 0x00);
+ registerWrite (0x1A, 0x0C);
+ }
+ }
+
+ /* send 0x07 */
+ registerWrite (0x1A, 0x07);
+ res = 1;
+ for (i = 0; i < 10; i++)
+ {
+ reg = registerRead (0x19) & 0xF8;
+ if ((reg & 0x78) == 0x38)
+ {
+ res = 0;
+ break;
+ }
+ }
+ if (res != 0)
+ return 0;
+
+ /* send 0x04 */
+ registerWrite (0x1A, 0x04);
+ res = 1;
+ for (i = 0; i < 10; i++)
+ {
+ reg = registerRead (0x19) & 0xF8;
+ if ((reg & 0xF8) == 0xF8)
+ {
+ res = 0;
+ break;
+ }
+ }
+ if (res != 0)
+ return 0;
+
+ /* send 0x05 */
+ registerWrite (0x1A, 0x05);
+ res = 1;
+ for (i = 0; i < 10; i++)
+ {
+ reg = registerRead (0x1A);
+ if (reg == 0x05)
+ {
+ res = 0;
+ break;
+ }
+ }
+ if (res != 0)
+ return 0;
+
+ /* end */
+ registerWrite (0x1A, 0x84);
+ return 1;
+}
+
+
+
+
+
+
+
+/* 1 OK, 0 failed */
+static int
+foncSendWord (int *cmd)
+{
+ prologue (0x10);
+ if (sendWord (cmd) == 0)
+ {
+ DBG (0, "sendWord(cmd) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ epilogue ();
+
+ return 1;
+}
+
+
+static int
+cmdSetDataBuffer (int *data)
+{
+ int cmd1[] = { 0x00, 0x00, 0x22, 0x88, -1 }; /* 34 bytes write on channel 8 */
+ int cmd2[] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x03, 0xC1, 0x80,
+ 0x00, 0x20, 0x02, 0x00, 0x16, 0x41, 0xE0, 0xAC, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -1
+ };
+ int cmd3[] = { 0x00, 0x08, 0x00, 0x84, -1 }; /* 2048 bytes size write on channel 4 (data) */
+ int cmd4[] = { 0x00, 0x08, 0x00, 0xC4, -1 }; /* 2048 bytes size read on channel 4 (data) */
+ int i;
+ unsigned char dest[2048];
+
+ /* cmdSet(8,34,cmd2), but without prologue/epilogue */
+ /* set block length to 34 bytes on 'channel 8' */
+ sendWord (cmd1);
+ DBG (16, "sendWord(cmd1) passed (%s:%d) \n", __FILE__, __LINE__);
+
+ /* sendData */
+ sendData (cmd2, 0x22);
+ DBG (16, "sendData(cmd2) passed (%s:%d) \n", __FILE__, __LINE__);
+
+ if (DBG_LEVEL >= 128)
+ {
+ bloc8Decode (cmd2);
+ }
+
+ /* set block length to 2048, write on 'channel 4' */
+ sendWord (cmd3);
+ DBG (16, "sendWord(cmd3) passed (%s:%d) \n", __FILE__, __LINE__);
+
+ if (sendData (data, 2048) == 0)
+ {
+ DBG (0, "sendData(data,%d) failed (%s:%d)\n", 2048, __FILE__, __LINE__);
+ return 0;
+ }
+ TRACE (16, "sendData(data,2048) passed ...");
+
+ /* read back all data sent to 'channel 4' */
+ sendWord (cmd4);
+ DBG (16, "sendWord(cmd4) passed (%s:%d) \n", __FILE__, __LINE__);
+
+ if (pausedReadData (2048, dest) == 0)
+ {
+ DBG (16, "pausedReadData(2048,dest) failed (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16, "pausedReadData(2048,dest) passed (%s:%d)\n", __FILE__, __LINE__);
+
+ /* dest should hold the same data than donnees */
+ for (i = 0; i < 2047; i++)
+ {
+ if (data[i] != (int) (dest[i]))
+ {
+ DBG
+ (0,
+ "Warning data read back differs: expected %02X found dest[%d]=%02X ! (%s:%d)\n",
+ data[i], i, dest[i], __FILE__, __LINE__);
+ }
+ }
+ return 1;
+}
+
+
+/* 1: OK
+ 0: end session failed */
+
+int
+sanei_umax_pp_endSession (void)
+{
+ int zero[5] = { 0, 0, 0, 0, -1 };
+
+ if (sanei_umax_pp_getastra () != 610)
+ {
+ prologue (0x00);
+ sendWord (zero);
+ epilogue ();
+ sanei_umax_pp_cmdSync (0xC2);
+ sanei_umax_pp_cmdSync (0x00); /* cancels any pending operation */
+ sanei_umax_pp_cmdSync (0x00); /* cancels any pending operation */
+ }
+ else
+ {
+ CMDSYNC (0x00);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSYNC (0x00);
+ }
+ compatMode ();
+
+ /* restore port state */
+ Outb (DATA, gData);
+ Outb (CONTROL, gControl);
+
+ /* OUF */
+ DBG (1, "End session done ...\n");
+ return 1;
+}
+
+
+/* initialize scanner with default values
+ * and do head re-homing if needed */
+int
+initScanner610p (int recover)
+{
+ int first, rc, x;
+ int cmd55AA[9] = { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, -1 };
+ int cmd02[17] = { 0x02, 0x80, 0x00, 0x40, 0x30, 0x00, 0xC0, 0x2F,
+ 0x2F, 0x07, 0x00, 0x00, 0x00, 0x80, 0xF0, 0x00, -1
+ };
+ int op01[17] =
+ { 0x01, 0x00, 0x32, 0x70, 0x00, 0x00, 0xC0, 0x2F, 0x17, 0x05, 0x00, 0x00,
+ 0x00, 0x80, 0xA4, 0x00, -1
+ };
+ int op11[17] =
+ { 0x01, 0x80, 0x0C, 0x70, 0x00, 0x00, 0xC0, 0x2F, 0x17, 0x01, 0x00, 0x00,
+ 0x00, 0x80, 0xA4, 0x00, -1
+ };
+ int op21[17] =
+ { 0x01, 0x00, 0x01, 0x40, 0x30, 0x00, 0xC0, 0x2F, 0x17, 0x05, 0x00, 0x00,
+ 0x00, 0x80, 0xF4, 0x00, -1
+ };
+ int op31[17] =
+ { 0x01, 0x00, 0x39, 0x73, 0x00, 0x00, 0xC0, 0x2F, 0x17, 0x05, 0x00, 0x00,
+ 0x00, 0x80, 0xB4, 0x00, -1
+ };
+
+ int op02[35] = { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x04, 0x40, 0x01, 0x00, 0x20, 0x02, 0x00,
+ 0x76, 0x00, 0x75, 0xEF, 0x06, 0x00, 0x00, 0xF6,
+ 0x4D, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68,
+ 0xDF, 0x1B, -1
+ };
+ int op22[35] = { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x03, 0xC1, 0x80, 0x00, 0x20, 0x02, 0x00,
+ 0x16, 0x80, 0x15, 0x78, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68,
+ 0xDF, 0x1B, -1
+ };
+
+ int op03[9] = { 0x00, 0x00, 0x00, 0xAA, 0xCC, 0xEE, 0xFF, 0xFF, -1 };
+ struct timeval tv;
+
+ byteMode (); /* just to get sure */
+ first = 0;
+ rc = inquire ();
+
+ /* get time to handle settle time delay */
+ gettimeofday (&tv, NULL);
+ gTime = tv.tv_sec;
+ /* default delay */
+ gDelay = 5;
+
+ if (rc == 0)
+ {
+ DBG (0, "inquire() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ if (rc == 2)
+ {
+ /* same value used by windows driver */
+ gDelay = 45;
+ DBG (1, "inquire() signals re-homing needed ... (%s:%d) \n",
+ __FILE__, __LINE__);
+ first = 1;
+ }
+ DBG (1, "inquire() passed ... (%s:%d) \n", __FILE__, __LINE__);
+
+ rc = loadDefaultTables ();
+ if (rc == 0)
+ {
+ DBG (0, "loadDefaultTables() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (1, "loadDefaultTables() passed ... (%s:%d) \n", __FILE__, __LINE__);
+ if (recover)
+ first = 1;
+
+ CMDSETGET (2, 0x10, cmd02);
+ CMDSETGET (1, 0x08, cmd55AA);
+
+ if (!first)
+ {
+ CMDSYNC (0x00);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ DBG (1, "initScanner610p done ...\n");
+ return 1;
+ }
+
+ /* here we do re-homing
+ * since it is first probe or recover */
+ /* move forward */
+ CMDSYNC (0xC2);
+ if (!recover)
+ {
+ CMDSETGET (2, 0x10, op01);
+ CMDSETGET (8, 0x22, op02);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, op03);
+ CMDSYNC (0x40);
+ CMDSYNC (0xC2);
+ sleep (2);
+ }
+
+ /* move backward */
+ CMDSETGET (2, 0x10, op11);
+ CMDSETGET (8, 0x22, op02);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, op03);
+ CMDSYNC (0x40);
+ CMDSYNC (0xC2);
+ sleep (2);
+
+ /* means 'CONTINUE MOVE' */
+ CMDSYNC (0x00);
+ while ((scannerStatus & MOTOR_BIT) == 0)
+ {
+ CMDSYNC (0xC2);
+ CMDSETGET (2, 0x10, op21);
+ CMDSETGET (8, 0x22, op22);
+ CMDSYNC (0x40);
+ usleep (20000);
+ }
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+
+ /* send head away */
+ if (!recover)
+ {
+ CMDSETGET (2, 0x10, op31);
+ CMDSETGET (8, 0x22, op02);
+ if (DBG_LEVEL > 8)
+ {
+ bloc2Decode (op31);
+ bloc8Decode (op02);
+ }
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, op03);
+ CMDSYNC (0x40);
+ CMDSYNC (0xC2);
+ sleep (9);
+ }
+
+ CMDSYNC (0x00);
+
+ /* this code has been added, without corresponding logs/
+ * it seem I just can't found 'real' parking command ...
+ */
+ /* send park command */
+ if (sanei_umax_pp_park () == 0)
+ {
+ TRACE (0, "sanei_umax_pp_park failed! ");
+ return 0;
+ }
+ /* and wait it to succeed */
+ if (sanei_umax_pp_parkWait () == 0)
+ {
+ TRACE (0, "sanei_umax_pp_parkWait failed! ");
+ return 0;
+ }
+
+ /* override gamma table with 610P defaults */
+ for (x = 0; x < 256; x++)
+ {
+ ggRed[x] = x;
+ ggGreen[x] = x;
+ ggBlue[x] = x;
+ }
+
+ DBG (1, "initScanner610p done ...\n");
+ return 1;
+}
+
+/* 1: OK
+ 2: homing happened
+ 3: scanner busy
+ 0: init failed
+
+ init transport layer
+ init scanner
+*/
+
+int
+sanei_umax_pp_initScanner (int recover)
+{
+ int i;
+ int status;
+ int readcmd[64];
+ /* in umax1220u, this buffer is opc[16] */
+ int sentcmd[17] =
+ { 0x02, 0x80, 0x00, 0x70, 0x00, 0x00, 0x00, 0x2F, 0x2F, 0x07, 0x00,
+ 0x00, 0x00, 0x80, 0xF0, 0x00, -1
+ };
+ int cmdA7[9] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, -1 };
+
+ if (sanei_umax_pp_getastra () == 610)
+ return initScanner610p (recover);
+
+ if (getModel () == 0x07)
+ sentcmd[15] = 0x00;
+ else
+ sentcmd[15] = 0x18;
+
+ /* fails here if there is an unfinished previous scan */
+ CMDSETGET (0x02, 16, sentcmd);
+
+ /* needs some init */
+ if (sentcmd[15] == 0x18)
+ {
+ sentcmd[15] = 0x00; /* was 0x18 */
+ CMDSETGET (0x02, 16, sentcmd);
+
+ /* in umax1220u, this buffer does not exist */
+ CMDSETGET (0x01, 8, cmdA7);
+ }
+
+
+ /* ~ opb3: inquire status */
+ CMDGET (0x08, 36, readcmd);
+ if (DBG_LEVEL >= 32)
+ {
+ bloc8Decode (readcmd);
+ }
+ DBG (16, "cmdGet(0x08,36,readcmd) passed (%s:%d)\n", __FILE__, __LINE__);
+
+ /* is the scanner busy parking ? */
+ status = sanei_umax_pp_scannerStatus ();
+ DBG (8, "INQUIRE SCANNER STATUS IS 0x%02X (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ if ((!recover) && (status & MOTOR_BIT) == 0x00)
+ {
+ DBG (1, "Warning: scanner motor on, giving up ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 3;
+ }
+
+ /* head homing needed ? */
+ if ((readcmd[34] != 0x1A) || (recover == 1))
+ { /* homing needed, readcmd[34] should be 0x48 */
+ int op01[17] =
+ { 0x01, 0x00, 0x32, 0x70, 0x00, 0x00, 0x60, 0x2F, 0x17, 0x05, 0x00,
+ 0x00, 0x00, 0x80, 0xE4, 0x00, -1
+ };
+ int op05[17] =
+ { 0x01, 0x00, 0x01, 0x70, 0x00, 0x00, 0x60, 0x2F, 0x13, 0x05, 0x00,
+ 0x00, 0x00, 0x80, 0xF0, 0x00, -1
+ };
+ int op02[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x40,
+ 0x01, 0x00, 0x20, 0x02, 0x00, 0x16, 0x00, 0x70, 0x9F, 0x06, 0x00,
+ 0x00, 0xF6, 0x4D, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF,
+ 0x0B, 0x1A, 0x00, -1
+ };
+ int op04[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x03, 0xC1,
+ 0x80, 0x00, 0x20, 0x02, 0x00, 0x16, 0x80, 0x15, 0x78, 0x03, 0x03,
+ 0x00, 0x00, 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF,
+ 0x0B, 0x1A, 0x00, -1
+ };
+ int op03[9] = { 0x00, 0x00, 0x00, 0xAA, 0xCC, 0xEE, 0xFF, 0xFF, -1 };
+
+ CMDSYNC (0xC2);
+ CMDSETGET (0x02, 16, op01);
+ CMDSETGET (0x08, 36, op02);
+
+ if (!recover)
+ {
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (0x04, 8, op03);
+ CMDSYNC (0x40);
+ do
+ {
+ sleep (1);
+ CMDSYNC (0xC2);
+ }
+ while ((sanei_umax_pp_scannerStatus () & 0x90) != 0x90);
+
+ op01[2] = 0x1E;
+ op01[9] = 0x01;
+ CMDSETGET (0x02, 16, op01);
+ CMDSETGET (0x08, 36, op02);
+ CMDSYNC (0x00);
+ CMDSYNC (0x00);
+ CMDSETGET (0x04, 8, op03);
+
+ CMDSYNC (0x40);
+ do
+ {
+ sleep (1);
+ CMDSYNC (0xC2);
+ }
+ while ((sanei_umax_pp_scannerStatus () & 0x90) != 0x90);
+ CMDSYNC (0x00);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+
+ do
+ {
+ usleep (500000);
+ CMDSYNC (0xC2);
+ status = sanei_umax_pp_scannerStatus ();
+ status = status & 0x10;
+ }
+ while (status != 0x10); /* was 0x90 */
+ CMDSETGET (0x02, 16, op05);
+ CMDSETGET (0x08, 36, op04);
+ CMDSYNC (0x40);
+ status = sanei_umax_pp_scannerStatus ();
+ DBG (16, "loop %d passed, status=0x%02X (%s:%d)\n", i, status,
+ __FILE__, __LINE__);
+ }
+
+
+
+ /* get head back home ... */
+ do
+ {
+ i++;
+ do
+ {
+ usleep (500000);
+ CMDSYNC (0xC2);
+ status = sanei_umax_pp_scannerStatus ();
+ status = status & 0x10;
+ }
+ while (status != 0x10); /* was 0x90 */
+ CMDSETGET (0x02, 16, op05);
+ CMDSETGET (0x08, 36, op04);
+ CMDSYNC (0x40);
+ status = sanei_umax_pp_scannerStatus ();
+ DBG (16, "loop %d passed, status=0x%02X (%s:%d)\n", i, status,
+ __FILE__, __LINE__);
+ }
+ while ((status & MOTOR_BIT) == 0x00); /* 0xD0 when head is back home */
+
+ do
+ {
+ usleep (500000);
+ CMDSYNC (0xC2);
+ }
+ while ((sanei_umax_pp_scannerStatus () & 0x90) != 0x90);
+
+
+ /* don't do automatic home sequence on recovery */
+ if (!recover)
+ {
+ CMDSYNC (0x00);
+ op01[2] = 0x1A;
+ op01[3] = 0x74; /* was 0x70 */
+ op01[9] = 0x05; /* initial value */
+ op01[14] = 0xF4; /* was 0xE4 */
+ CMDSETGET (0x02, 16, op01);
+ CMDSETGET (0x08, 36, op02);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (0x04, 8, op03);
+ CMDSYNC (0x40);
+
+ /* wait for automatic homing sequence */
+ /* to complete, thus avoiding */
+ /* scanning too early */
+ do
+ {
+ /* the sleep is here to prevent */
+ /* excessive CPU usage, can be */
+ /* removed, if we don't care */
+ sleep (3);
+ CMDSYNC (0xC2);
+ DBG (16, "PARKING polling status is 0x%02X (%s:%d)\n",
+ sanei_umax_pp_scannerStatus (), __FILE__, __LINE__);
+ }
+ while (sanei_umax_pp_scannerStatus () == 0x90);
+ }
+
+ /* signal homing */
+ return 2;
+ }
+
+
+ /* end ... */
+ DBG (1, "Scanner init done ...\n");
+ return 1;
+}
+
+
+/*
+ 1: OK
+ 2: failed, try again
+ 0: init failed
+
+ initialize the transport layer
+
+ */
+
+static int
+initTransport610p (void)
+{
+ int tmp, i;
+ int zero[5] = { 0, 0, 0, 0, -1 };
+
+ /* test EPP availability */
+ connect610p ();
+ if (sync610p () == 0)
+ {
+ DBG (0,
+ "sync610p failed! Scanner not present or powered off ... (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ if (EPPsendWord610p (zero) == 0)
+ {
+ DBG (1, "No EPP mode detected\n");
+ gMode = UMAX_PP_PARPORT_BYTE;
+ }
+ else
+ {
+ DBG (1, "EPP mode detected\n");
+ gMode = UMAX_PP_PARPORT_EPP;
+ }
+ disconnect610p ();
+
+ /* set up to bidirectionnal */
+ /* in fact we could add support for EPP */
+ /* but let's make 610 work first */
+ if (gMode == UMAX_PP_PARPORT_BYTE)
+ {
+ byteMode ();
+
+ /* reset after failure */
+ /* set to data reverse */
+ Outb (CONTROL, 0x2C);
+ Inb (CONTROL);
+ for (i = 0; i < 10; i++)
+ Outb (DATA, 0xAA);
+ tmp = Inb (DATA);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (1, "Found 0x%X expected 0xFF (%s:%d)\n", tmp, __FILE__,
+ __LINE__);
+ }
+ for (i = 0; i < 4; i++)
+ {
+ Outb (DATA, 0x00);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (1, "Found 0x%X expected 0xFF (%s:%d)\n", tmp, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ Outb (DATA, 0xFF);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (1, "Found 0x%X expected 0xFF (%s:%d)\n", tmp, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ }
+ TRACE (16, "RESET done... ");
+ byteMode ();
+
+ if (SPPsendWord610p (zero) == 0)
+ {
+ DBG (0, "SPPsendWord610p(zero) failed! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ TRACE (16, "SPPsendWord610p(zero) passed... ");
+ }
+
+ /* OK ! */
+ TRACE (1, "initTransport610p done... ");
+ return 1;
+}
+
+/*
+ 1: OK
+ 2: failed, try again
+ 0: init failed
+
+ initialize the transport layer
+
+ */
+
+static int
+initTransport1220P (int recover) /* ECP OK !! */
+{
+ int i, j;
+ int reg, tmp;
+ unsigned char *dest = NULL;
+ int zero[5] = { 0, 0, 0, 0, -1 };
+ int model, nb;
+
+ connect ();
+ DBG (16, "connect() passed... (%s:%d)\n", __FILE__, __LINE__);
+ gEPAT = 0xC7;
+ reg = registerRead (0x0B);
+ if (reg != gEPAT)
+ {
+ DBG (16, "Error! expected reg0B=0x%02X, found 0x%02X! (%s:%d) \n",
+ gEPAT, reg, __FILE__, __LINE__);
+ DBG (16, "Scanner needs probing ... \n");
+ if (sanei_umax_pp_probeScanner (recover) != 1)
+ {
+ return 0;
+ }
+ else
+ {
+ return 2; /* signals retry initTransport() */
+ }
+ }
+
+ reg = registerRead (0x0D);
+ reg = (reg & 0xE8) | 0x43;
+ registerWrite (0x0D, reg);
+ REGISTERWRITE (0x0C, 0x04);
+ reg = registerRead (0x0A);
+ if (reg != 0x00)
+ {
+ if (reg != 0x1C)
+ {
+ DBG (0, "Warning! expected reg0A=0x00, found 0x%02X! (%s:%d) \n",
+ reg, __FILE__, __LINE__);
+ }
+ else
+ {
+ DBG (16, "Scanner in idle state .... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ }
+
+ /* model detection: redone since we might not be probing each time ... */
+ /* write addr in 0x0E, read value at 0x0F */
+ REGISTERWRITE (0x0E, 0x01);
+ model = registerRead (0x0F);
+ setModel (model);
+
+ REGISTERWRITE (0x0A, 0x1C);
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x08, 0x10);
+ }
+ else
+ {
+ REGISTERWRITE (0x08, 0x21);
+ }
+ REGISTERWRITE (0x0E, 0x0F);
+ REGISTERWRITE (0x0F, 0x0C);
+
+ REGISTERWRITE (0x0A, 0x1C);
+ REGISTERWRITE (0x0E, 0x10);
+ REGISTERWRITE (0x0F, 0x1C);
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x0F, 0x00);
+ }
+ REGISTERWRITE (0x0A, 0x11);
+
+ dest = (unsigned char *) (malloc (65536));
+ if (dest == NULL)
+ {
+ DBG (0, "Failed to allocate 64 Ko !\n");
+ return 0;
+ }
+ for (i = 0; i < 256; i++)
+ {
+ dest[i * 2] = i;
+ dest[i * 2 + 1] = 0xFF - i;
+ dest[512 + i * 2] = i;
+ dest[512 + i * 2 + 1] = 0xFF - i;
+ }
+ nb = 150;
+ for (i = 0; i < nb; i++)
+ {
+ bufferWrite (0x400, dest);
+ DBG (16,
+ "Loop %d: bufferWrite(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+
+ REGISTERWRITE (0x0A, 0x18);
+ REGISTERWRITE (0x0A, 0x11);
+
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ ECPSetBuffer (0x400);
+ }
+ for (i = 0; i < nb; i++)
+ {
+ /* XXX Compat/Byte ??? XXX */
+ bufferRead (0x400, dest);
+ for (j = 0; j < 256; j++)
+ {
+ if (dest[j * 2] != j)
+ {
+ DBG (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ j * 2, j, dest[j * 2]);
+ return 0;
+ }
+ if (dest[j * 2 + 1] != 0xFF - j)
+ {
+ DBG
+ (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ j * 2 + 1, 0xFF - j, dest[j * 2 + 1]);
+ return 0;
+ }
+ if (dest[512 + j * 2] != j)
+ {
+ DBG (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ 512 + j * 2, j, dest[512 + j * 2]);
+ return 0;
+ }
+ if (dest[512 + j * 2 + 1] != 0xFF - j)
+ {
+ DBG
+ (0,
+ "Altered buffer value at %03X, expected 0x%02X, found 0x%02X\n",
+ 512 + j * 2 + 1, 0xFF - j, dest[512 + j * 2 + 1]);
+ return 0;
+ }
+ }
+ DBG (16, "Loop %d: bufferRead(0x400,dest) passed... (%s:%d)\n",
+ i, __FILE__, __LINE__);
+ }
+ REGISTERWRITE (0x0A, 0x18);
+ /* ECP: "HEAVY" reconnect here */
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ epilogue ();
+ /* 3 line: set to initial parport state ? */
+ byteMode (); /*Outb (ECR, 0x20); */
+ Outb (DATA, 0x04);
+ Outb (CONTROL, 0x0C);
+
+ /* the following is a variant of connect(); */
+ Inb (ECR);
+ Inb (ECR);
+ byteMode (); /*Outb (ECR, 0x20); */
+ byteMode (); /*Outb (ECR, 0x20); */
+ Inb (CONTROL);
+ Outb (CONTROL, 0x0C);
+ Inb (DATA);
+ sendCommand (0xE0);
+ Outb (DATA, 0XFF);
+ Outb (DATA, 0XFF);
+ ClearRegister (0);
+ WRITESLOW (0x0E, 0x0A);
+ SLOWNIBBLEREGISTERREAD (0x0F, 0x08);
+ /* resend value OR'ed 0x08 ? */
+ WRITESLOW (0x0F, 0x08);
+ WRITESLOW (0x08, 0x10);
+ disconnect ();
+ prologue (0x10);
+ }
+
+ if (fonc001 () != 1)
+ {
+ DBG (0, "fonc001() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "fonc001() passed ... (%s:%d) \n", __FILE__, __LINE__);
+
+ /* sync */
+ if (sendWord (zero) == 0)
+ {
+ DBG (0, "sendWord(zero) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "sendWord(zero) passed (%s:%d)\n", __FILE__, __LINE__);
+ epilogue ();
+
+ /* OK ! */
+ free (dest);
+ DBG (1, "initTransport1220P done ...\n");
+ return 1;
+}
+
+/*
+ 1: OK
+ 2: failed, try again
+ 0: init failed
+
+ initialize the transport layer
+
+ */
+
+int
+sanei_umax_pp_initTransport (int recover)
+{
+ TRACE (16, "sanei_umax_pp_initTransport");
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ return initTransport610p ();
+ case 1220:
+ case 1600:
+ case 2000:
+ default:
+ return initTransport1220P (recover);
+ }
+}
+
+
+/* 1: OK
+ 0: probe failed */
+
+static int
+probe610p (int recover)
+{
+ if (initTransport610p () == 0)
+ {
+ DBG (0, "initTransport610p() failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* make sure we won't try 1220/200P later
+ * since we got here, we have a 610, and in any case
+ * NOT a 1220P/2000P, since no EPAT present */
+ sanei_umax_pp_setastra (610);
+
+ if (initScanner610p (recover) == 0)
+ {
+ DBG (0, "initScanner610p() failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ /* successfull end ... */
+ DBG (1, "UMAX Astra 610p detected\n");
+ DBG (1, "probe610p done ...\n");
+ return 1;
+}
+
+
+ /*
+ * try PS2 mode
+ * returns 1 on success, 0 on failure
+ */
+int
+probePS2 (unsigned char *dest)
+{
+ int i, tmp;
+
+ /* write/read full buffer */
+ for (i = 0; i < 256; i++)
+ {
+ WRITESLOW (0x0A, i);
+ SLOWNIBBLEREGISTERREAD (0x0A, i);
+ WRITESLOW (0x0A, 0xFF - i);
+ SLOWNIBBLEREGISTERREAD (0x0A, 0xFF - i);
+ }
+
+ /* end test for nibble byte/byte mode */
+
+ /* now we try nibble buffered mode */
+ WRITESLOW (0x13, 0x01);
+ WRITESLOW (0x13, 0x00); /*reset something */
+ WRITESLOW (0x0A, 0x11);
+ for (i = 0; i < 10; i++) /* 10 ~ 11 ? */
+ {
+ PS2bufferRead (0x400, dest);
+ DBG (16, "Loop %d: PS2bufferRead passed ... (%s:%d)\n", i, __FILE__,
+ __LINE__);
+ }
+
+ /* write buffer */
+ for (i = 0; i < 10; i++)
+ {
+ PS2bufferWrite (0x400, dest);
+ DBG (16, "Loop %d: PS2bufferWrite passed ... (%s:%d)\n", i, __FILE__,
+ __LINE__);
+ }
+
+ SLOWNIBBLEREGISTERREAD (0x0C, 0x04);
+ WRITESLOW (0x13, 0x01);
+ WRITESLOW (0x13, 0x00);
+ WRITESLOW (0x0A, 0x18);
+
+ return 1;
+}
+
+ /*
+ * try EPP 8 then 32 bits
+ * returns 1 on success, 0 on failure
+ */
+int
+probeEPP (unsigned char *dest)
+{
+ int tmp, i, j;
+ int reg;
+
+ /* test EPP MODE */
+ setEPPMode (8);
+ gMode = UMAX_PP_PARPORT_EPP;
+ ClearRegister (0);
+ DBG (16, "ClearRegister(0) passed... (%s:%d)\n", __FILE__, __LINE__);
+ WRITESLOW (0x08, 0x22);
+ init001 ();
+ DBG (16, "init001() passed... (%s:%d)\n", __FILE__, __LINE__);
+ gEPAT = 0xC7;
+ init002 (0);
+ DBG (16, "init002(0) passed... (%s:%d)\n", __FILE__, __LINE__);
+
+ REGISTERWRITE (0x0A, 0);
+
+ /* catch any failure to read back data in EPP mode */
+ reg = registerRead (0x0A);
+ if (reg != 0)
+ {
+ DBG (0, "registerRead, found 0x%X expected 0x00 (%s:%d)\n", reg,
+ __FILE__, __LINE__);
+ if (reg == 0xFF)
+ {
+ DBG (0,
+ "*** It appears that EPP data transfer doesn't work ***\n");
+ DBG (0,
+ "*** Please read SETTING EPP section in sane-umax_pp.5 ***\n");
+ }
+ return 0;
+ }
+ else
+ {
+ DBG (16, "registerRead(0x0A)=0x00 passed... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ registerWrite (0x0A, 0xFF);
+ DBG (16, "registerWrite(0x%X,0x%X) passed... (%s:%d)\n", 0x0A, 0xFF,
+ __FILE__, __LINE__);
+ REGISTERREAD (0x0A, 0xFF);
+ for (i = 1; i < 256; i++)
+ {
+ REGISTERWRITE (0x0A, i);
+ REGISTERREAD (0x0A, i);
+ REGISTERWRITE (0x0A, 0xFF - i);
+ REGISTERREAD (0x0A, 0xFF - i);
+ }
+
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x11);
+
+ for (i = 0; i < 10; i++)
+ {
+ bufferRead (0x400, dest);
+ for (j = 0; j < 512; j++)
+ {
+ if (dest[2 * j] != (j % 256))
+ {
+ DBG (0, "Loop %d, char %d bufferRead failed! (%s:%d)\n", i,
+ j * 2, __FILE__, __LINE__);
+ return 0;
+ }
+ if (dest[2 * j + 1] != (0xFF - (j % 256)))
+ {
+ DBG (0, "Loop %d, char %d bufferRead failed! (%s:%d)\n", i,
+ j * 2 + 1, __FILE__, __LINE__);
+ return 0;
+ }
+ }
+ DBG (16, "Loop %d: bufferRead(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ bufferWrite (0x400, dest);
+ DBG (16, "Loop %d: bufferWrite(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+
+
+ REGISTERREAD (0x0C, 4);
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x18);
+
+ Outb (DATA, 0x0);
+ ClearRegister (0);
+ init001 ();
+
+ if (checkEPAT () != 0)
+ return 0;
+ DBG (16, "checkEPAT() passed... (%s:%d)\n", __FILE__, __LINE__);
+
+ tmp = Inb (CONTROL) & 0x1F;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+
+ WRITESLOW (0x08, 0x21);
+ init001 ();
+ DBG (16, "init001() passed... (%s:%d)\n", __FILE__, __LINE__);
+ WRITESLOW (0x08, 0x21);
+ init001 ();
+ DBG (16, "init001() passed... (%s:%d)\n", __FILE__, __LINE__);
+ SPPResetLPT ();
+
+
+ if (init005 (0x80))
+ {
+ DBG (0, "init005(0x80) failed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ DBG (16, "init005(0x80) passed... (%s:%d)\n", __FILE__, __LINE__);
+ if (init005 (0xEC))
+ {
+ DBG (0, "init005(0xEC) failed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ DBG (16, "init005(0xEC) passed... (%s:%d)\n", __FILE__, __LINE__);
+
+
+ /* write/read buffer loop */
+ for (i = 0; i < 256; i++)
+ {
+ REGISTERWRITE (0x0A, i);
+ REGISTERREAD (0x0A, i);
+ REGISTERWRITE (0x0A, 0xFF - i);
+ REGISTERREAD (0x0A, 0xFF - i);
+ }
+ DBG (16, "EPP write/read buffer loop passed... (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x11);
+
+ /* test EPP32 mode */
+ /* we set 32 bits I/O mode first, then step back to */
+ /* 8bits if tests fail */
+ setEPPMode (32);
+ for (i = 0; (i < 10) && (getEPPMode () == 32); i++)
+ {
+ bufferRead (0x400, dest);
+ /* if 32 bit I/O work, we should have a buffer */
+ /* filled by 00 FF 01 FE 02 FD 03 FC ..... */
+ for (j = 0; j < 0x200; j++)
+ {
+ if ((dest[j * 2] != j % 256)
+ || (dest[j * 2 + 1] != 0xFF - (j % 256)))
+ {
+ DBG (1, "Setting EPP I/O to 8 bits ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ setEPPMode (8);
+ /* leave out current loop since an error was detected */
+ break;
+ }
+ }
+ DBG (16, "Loop %d: bufferRead(0x400) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+ DBG (1, "%d bits EPP data transfer\n", getEPPMode ());
+
+
+ for (i = 0; i < 10; i++)
+ {
+ bufferWrite (0x400, dest);
+ DBG (16, "Loop %d: bufferWrite(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+
+
+
+ REGISTERREAD (0x0C, 0x04);
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x18);
+
+ WRITESLOW (0x08, 0x21);
+ init001 ();
+ DBG (16, "init001() passed... (%s:%d)\n", __FILE__, __LINE__);
+ SPPResetLPT ();
+
+ if (init005 (0x80))
+ {
+ DBG (0, "init005(0x80) failed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ DBG (16, "init005(0x80) passed... (%s:%d)\n", __FILE__, __LINE__);
+ if (init005 (0xEC))
+ {
+ DBG (0, "init005(0xEC) failed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ DBG (16, "init005(0xEC) passed... (%s:%d)\n", __FILE__, __LINE__);
+
+
+ /* write/read buffer loop */
+ for (i = 0; i < 256; i++)
+ {
+ REGISTERWRITE (0x0A, i);
+ REGISTERREAD (0x0A, i);
+ REGISTERWRITE (0x0A, 0xFF - i);
+ REGISTERREAD (0x0A, 0xFF - i);
+ }
+ DBG (16, "EPP write/read buffer loop passed... (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x11);
+
+
+ for (i = 0; i < 10; i++)
+ {
+ bufferRead (0x400, dest);
+ DBG (16, "Loop %d: bufferRead(0x400) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ bufferWrite (0x400, dest);
+ DBG (16, "Loop %d: bufferWrite(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+ REGISTERREAD (0x0C, 0x04);
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x18);
+ gMode = UMAX_PP_PARPORT_EPP;
+ return 1;
+}
+
+ /*
+ * try ECP mode
+ * returns 1 on success, 0 on failure
+ */
+int
+probeECP (unsigned char *dest)
+{
+ int i, j, tmp;
+ unsigned char breg;
+
+ /* if ECP not available, fail */
+ if (gECP != 1)
+ {
+ DBG (1, "Hardware can't do ECP, giving up (%s:%d) ...\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ gMode = UMAX_PP_PARPORT_ECP;
+
+/* clean from EPP failure */
+ breg = Inb (CONTROL);
+ Outb (CONTROL, breg & 0x04);
+
+/* reset sequence */
+ byteMode (); /*Outb (ECR, 0x20); byte mode */
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x0C);
+ for (i = 0; i < 256; i++)
+ {
+ breg = (Inb (STATUS)) & 0xF8;
+ if (breg != 0x48)
+ {
+ DBG (0,
+ "probeECP() failed at sync step %d, status=0x%02X, expected 0x48 (%s:%d)\n",
+ i, breg, __FILE__, __LINE__);
+ return 0;
+ }
+ }
+ Outb (CONTROL, 0x0E);
+ Outb (CONTROL, 0x0E);
+ Outb (CONTROL, 0x0E);
+ breg = (Inb (STATUS)) & 0xF8;
+ if (breg != 0x48)
+ {
+ DBG (0, "probeECP() failed, status=0x%02X, expected 0x48 (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ return 0;
+ }
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ breg = Inb (STATUS) & 0xF8;
+ breg = (Inb (STATUS)) & 0xF8;
+ if (breg != 0xC8)
+ {
+ DBG (0, "probeECP() failed, status=0x%02X, expected 0xC8 (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ return 0;
+ }
+/* end of reset sequence */
+
+ Outb (DATA, 0x00);
+ ClearRegister (0);
+
+/* utile ? semble tester le registre de configuration
+ * inb ECR,35
+ * inb 77B,FF
+ * inb ECR,35
+ */
+/* routine A */
+ breg = Inb (CONTROL); /* 0x04 évidemment! */
+ breg = Inb (ECR);
+ breg = Inb (ECR);
+ breg = Inb (ECR);
+ breg = Inb (ECR);
+ breg = Inb (CONTROL);
+ byteMode (); /*Outb (ECR, 0x20); byte mode */
+ /*byteMode (); Outb (ECR, 0x20); */
+ breg = Inb (CONTROL);
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ breg = Inb (ECR); /* 35 expected */
+ breg = Inb (ECR); /* 35 expected */
+ breg = Inb (ECR); /* 35 expected */
+ breg = Inb (ECR); /* 35 expected */
+ Outb (CONTROL, 0x04);
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ byteMode ();
+
+ ClearRegister (0);
+
+/* routine C */
+ PS2registerWrite (0x08, 0x01);
+
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x04);
+
+ ClearRegister (0);
+
+ breg = PS2Something (0x10);
+ if (breg != 0x0B)
+ {
+ DBG (0, "probeECP() failed, reg10=0x%02X, expected 0x0B (%s:%d)\n",
+ breg, __FILE__, __LINE__);
+ /* return 0; */
+ }
+
+
+ for (i = 0; i < 256; i++)
+ {
+ ECPregisterWrite (0x0A, i);
+ breg = ECPregisterRead (0x0A);
+ if (breg != i)
+ {
+ DBG (0, "probeECP(), loop %d failed (%s:%d)\n", i, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ ECPregisterWrite (0x0A, 0xFF - i);
+ breg = ECPregisterRead (0x0A);
+ if (breg != 0xFF - i)
+ {
+ DBG (0, "probeECP(), loop %d failed (%s:%d)\n", i, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ }
+ DBG (16, "probeECP(), loop passed (%s:%d)\n", __FILE__, __LINE__);
+
+ ECPregisterWrite (0x13, 0x01);
+ ECPregisterWrite (0x13, 0x00);
+ ECPregisterWrite (0x0A, 0x11);
+
+ /* there is one buffer transfer size set up */
+ /* subsequent reads are done in a row */
+ ECPSetBuffer (0x400);
+ for (i = 0; i < 10; i++)
+ {
+ /* if (i > 0) */
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+
+ ECPbufferRead (1024, dest);
+ /* check content of the returned buffer */
+ for (j = 0; j < 256; j++)
+ {
+ if (dest[j * 2] != j)
+ {
+ DBG (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ j * 2, j, dest[j * 2]);
+ return 0;
+ }
+ if (dest[j * 2 + 1] != 0xFF - j)
+ {
+ DBG
+ (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ j * 2 + 1, 0xFF - j, dest[j * 2 + 1]);
+ return 0;
+ }
+ if (dest[512 + j * 2] != j)
+ {
+ DBG (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ 512 + j * 2, j, dest[512 + j * 2]);
+ return 0;
+ }
+ if (dest[512 + j * 2 + 1] != 0xFF - j)
+ {
+ DBG
+ (0,
+ "Altered buffer value at %03X, expected 0x%02X, found 0x%02X\n",
+ 512 + j * 2 + 1, 0xFF - j, dest[512 + j * 2 + 1]);
+ return 0;
+ }
+ }
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ }
+
+ for (i = 0; i < 10; i++)
+ ECPbufferWrite (1024, dest);
+
+ breg = ECPregisterRead (0x0C);
+ if (breg != 0x04)
+ {
+ DBG (0, "Warning! expected reg0C=0x04, found 0x%02X! (%s:%d) \n", breg,
+ __FILE__, __LINE__);
+ }
+
+ ECPregisterWrite (0x13, 0x01);
+ ECPregisterWrite (0x13, 0x00);
+ ECPregisterWrite (0x0A, 0x18);
+
+ /* reset printer ? */
+ Outb (DATA, 0x00);
+ Outb (CONTROL, 0x00);
+ Outb (CONTROL, 0x04);
+
+ for (i = 0; i < 3; i++)
+ { /* will go in a function */
+ ClearRegister (0);
+ if (waitAck () != 1)
+ {
+ DBG (0, "probeECP failed because of waitAck() (%s:%d) \n", __FILE__,
+ __LINE__);
+ /* return 0; may fail without harm ... ??? */
+ }
+ /* are these 2 out really needed ? */
+ PS2registerWrite (0x08, 0x01);
+ Outb (CONTROL, 0x0C); /* select + reset */
+ Outb (CONTROL, 0x04); /* reset */
+ }
+
+ /* prologue of the 'rotate test' */
+ ClearRegister (0);
+ breg = PS2Something (0x10);
+ if (breg != 0x0B)
+ {
+ DBG (0,
+ "PS2Something returned 0x%02X, 0x0B expected (%s:%d)\n", breg,
+ __FILE__, __LINE__);
+ }
+ Outb (CONTROL, 0x04); /* reset */
+
+ if (init005 (0x80))
+ {
+ DBG (0, "init005(0x80) failed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ DBG (16, "init005(0x80) passed... (%s:%d)\n", __FILE__, __LINE__);
+ if (init005 (0xEC))
+ {
+ DBG (0, "init005(0xEC) failed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ DBG (16, "init005(0xEC) passed... (%s:%d)\n", __FILE__, __LINE__);
+
+ for (i = 0; i < 256; i++)
+ {
+ REGISTERWRITE (0x0A, i);
+ REGISTERREAD (0x0A, i);
+ REGISTERWRITE (0x0A, 0xFF - i);
+ REGISTERREAD (0x0A, 0xFF - i);
+ }
+ DBG (16, "ECPprobe(), write/read buffer loop passed (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x11);
+
+ /* should be a function */
+ /* in probeEPP(), we begin 32 bit mode test here */
+ for (i = 0; i < 10; i++)
+ {
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+
+ ECPbufferRead (0x400, dest);
+ /* check content of the returned buffer */
+ for (j = 0; j < 256; j++)
+ {
+ if (dest[j * 2] != j)
+ {
+ DBG (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ j * 2, j, dest[j * 2]);
+ return 0;
+ }
+ if (dest[j * 2 + 1] != 0xFF - j)
+ {
+ DBG
+ (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ j * 2 + 1, 0xFF - j, dest[j * 2 + 1]);
+ return 0;
+ }
+ if (dest[512 + j * 2] != j)
+ {
+ DBG (0,
+ "Altered buffer value at %03X, expected %02X, found %02X\n",
+ 512 + j * 2, j, dest[512 + j * 2]);
+ return 0;
+ }
+ if (dest[512 + j * 2 + 1] != 0xFF - j)
+ {
+ DBG
+ (0,
+ "Altered buffer value at %03X, expected 0x%02X, found 0x%02X\n",
+ 512 + j * 2 + 1, 0xFF - j, dest[512 + j * 2 + 1]);
+ return 0;
+ }
+ }
+ Outb (CONTROL, 0x04);
+ byteMode ();
+ }
+
+ for (i = 0; i < 10; i++)
+ ECPbufferWrite (1024, dest);
+
+ REGISTERREAD (0x0C, 0x04);
+ REGISTERWRITE (0x13, 0x01);
+ REGISTERWRITE (0x13, 0x00);
+ REGISTERWRITE (0x0A, 0x18);
+ waitAck ();
+
+ return 1;
+}
+
+
+
+/* 1: OK
+ 0: probe failed */
+
+int
+sanei_umax_pp_probeScanner (int recover)
+{
+ int tmp, i, j;
+ int reg, nb;
+ unsigned char *dest = NULL;
+ int initbuf[2049];
+ int voidbuf[2049];
+ int val;
+ int zero[5] = { 0, 0, 0, 0, -1 };
+ int model;
+
+ /* saves port state */
+ gData = Inb (DATA);
+ gControl = Inb (CONTROL);
+
+ if (sanei_umax_pp_getastra () == 610)
+ return probe610p (recover);
+
+ /* save and set CONTROL */
+ tmp = (Inb (CONTROL)) & 0x1F;
+ tmp = (Inb (CONTROL)) & 0x1F;
+ Outb (CONTROL, tmp);
+ Outb (CONTROL, tmp);
+
+ tmp = Inb (DATA);
+ tmp = Inb (CONTROL) & 0x3F;
+ Outb (CONTROL, tmp);
+
+ tmp = Inb (CONTROL) & 0x3F;
+ tmp = Inb (DATA);
+ tmp = Inb (CONTROL) & 0x3F;
+ tmp = Inb (DATA);
+
+ /* any scanner ? */
+ /* fast detect */
+ tmp = ringScanner (2, 0);
+ if (!tmp)
+ {
+ DBG (1, "No scanner detected by 'ringScanner(2,0)'...\n");
+ tmp = ringScanner (5, 0);
+ if (!tmp)
+ {
+ DBG (1, "No scanner detected by 'ringScanner(5,0)'...\n");
+ tmp = ringScanner (5, 10000);
+ if (!tmp)
+ {
+ DBG (1, "No scanner detected by 'ringScanner(5,10000)'...\n");
+ tmp = ringScanner (5, 10000);
+ if (!tmp)
+ {
+ DBG (1,
+ "No scanner detected by 'ringScanner(5,10000)'...\n");
+ }
+ }
+ }
+ }
+ if (!tmp)
+ {
+ DBG (1, "No 1220P/2000P scanner detected by 'ringScanner()'...\n");
+ }
+ DBG (16, "ringScanner passed...\n");
+
+ gControl = Inb (CONTROL) & 0x3F;
+ g67D = 1;
+ if (sendCommand (0x30) == 0)
+ {
+ DBG (0, "sendCommand(0x30) (%s:%d) failed ...\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "sendCommand(0x30) passed ... (%s:%d)\n", __FILE__, __LINE__);
+ g67E = 4; /* bytes to read */
+ if (sendCommand (0x00) == 0)
+ {
+ DBG (0, "sendCommand(0x00) (%s:%d) failed ...\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "sendCommand(0x00) passed... (%s:%d)\n", __FILE__, __LINE__);
+ g67E = 0; /* bytes to read */
+ if (testVersion (0) == 0)
+ {
+ DBG (16, "testVersion(0) (%s:%d) failed ...\n", __FILE__, __LINE__);
+ }
+ DBG (16, "testVersion(0) passed...\n");
+ /* must fail for 1220P and 2000P */
+ if (testVersion (1) == 0) /* software doesn't do it for model 0x07 */
+ { /* but it works .. */
+ DBG (16, "testVersion(1) failed (expected) ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ else
+ {
+ DBG (16, "Unexpected success on testVersion(1) ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ if (testVersion (0) == 0)
+ {
+ DBG (16, "testVersion(0) (%s:%d) failed ...\n", __FILE__, __LINE__);
+ }
+ DBG (16, "testVersion(0) passed...\n");
+ /* must fail */
+ if (testVersion (1) == 0)
+ {
+ DBG (16, "testVersion(1) failed (expected) ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ else
+ {
+ DBG (16, "Unexpected success on testVersion(1) ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+
+ Outb (DATA, 0x04);
+ Outb (CONTROL, 0x0C);
+ Outb (DATA, 0x04);
+ Outb (CONTROL, 0x0C);
+
+ gControl = Inb (CONTROL) & 0x3F;
+ Outb (CONTROL, gControl & 0xEF);
+
+
+
+ if (sendCommand (0x40) == 0)
+ {
+ DBG (0, "sendCommand(0x40) (%s:%d) failed ...\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "sendCommand(0x40) passed...\n");
+ if (sendCommand (0xE0) == 0)
+ {
+ DBG (0, "sendCommand(0xE0) (%s:%d) failed ...\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "sendCommand(0xE0) passed...\n");
+
+ ClearRegister (0);
+ DBG (16, "ClearRegister(0) passed...\n");
+
+ SPPResetLPT ();
+ DBG (16, "SPPResetLPT() passed...\n");
+
+ Outb (CONTROL, 4);
+ Outb (CONTROL, 4);
+
+
+ /* test PS2 mode */
+
+ tmp = PS2registerRead (0x0B);
+ if (tmp == 0xC7)
+ {
+ /* epat C7 detected */
+ DBG (16, "PS2registerRead(0x0B)=0x%X passed...\n", tmp);
+
+ PS2registerWrite (8, 0);
+ DBG (16, "PS2registerWrite(8,0) passed... (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ tmp = PS2registerRead (0x0A);
+ if (tmp != 0x00)
+ {
+ if (tmp == 0x1C)
+ {
+ DBG (16, "Previous probe detected ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ else
+ {
+ DBG (0, "Found 0x%X expected 0x00 (%s:%d)\n", tmp, __FILE__,
+ __LINE__);
+ }
+ }
+ DBG (16, "PS2registerRead(0x0A)=0x%X passed ...(%s:%d)\n", tmp,
+ __FILE__, __LINE__);
+
+ }
+ else
+ {
+ DBG (4, "Found 0x%X expected 0xC7 (%s:%d)\n", tmp, __FILE__, __LINE__);
+ if ((tmp == 0xFF) && (sanei_umax_pp_getparport () < 1))
+ {
+ DBG (0,
+ "It is likely that the hardware address (0x%X) you specified is wrong\n",
+ gPort);
+ return 0;
+ }
+ /* probe for a 610p, since we can't detect an EPAT */
+ DBG (1, "Trying 610p (%s:%d)\n", __FILE__, __LINE__);
+ return probe610p (recover);
+ }
+
+ /* clear register 3 */
+ ClearRegister (3);
+ DBG (16, "ClearRegister(3) passed...\n");
+
+ /* wait ? */
+ i = 65535;
+ while (i > 0)
+ {
+ tmp = Inb (DATA);
+ tmp = Inb (DATA);
+ i--;
+ }
+ DBG (16, "FFFF in loop passed...\n");
+
+ ClearRegister (0);
+ DBG (16, "ClearRegister(0) passed... (%s:%d)\n", __FILE__, __LINE__);
+ fflush (stdout);
+
+ /* 1220/2000P branch */
+ WRITESLOW (0x0E, 1);
+
+ /* register 0x0F used only once: model number ? Or ASIC revision ? */
+ /* comm mode ? */
+ model = PS2registerRead (0x0F);
+ DBG (1, "UMAX Astra 1220/1600/2000 P ASIC detected (mode=%d)\n", model);
+ setModel (model);
+ DBG (16, "PS2registerRead(0x0F) passed... (%s:%d)\n", __FILE__, __LINE__);
+
+ /* scanner powered off */
+ if (model == 0x1B)
+ {
+ DBG (0, "Register 0x0F=0x1B, scanner powered off ! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+
+
+ if ((model != 0x1F) && (model != 0x07))
+ {
+ DBG
+ (0,
+ "Unexpected value for for register 0x0F, expected 0x07 or 0x1F, got 0x%02X ! (%s:%d)\n",
+ model, __FILE__, __LINE__);
+ DBG (0, "There is a new scanner revision in town, or a bug ....\n");
+ }
+
+ WRITESLOW (0x08, 0x02);
+ WRITESLOW (0x0E, 0x0F);
+ WRITESLOW (0x0F, 0x0C);
+ WRITESLOW (0x0C, 0x04);
+ tmp = PS2registerRead (0x0D);
+ if ((tmp != 0x00) && (tmp != 0x40))
+ {
+ DBG
+ (0,
+ "Unexpected value for for register 0x0D, expected 0x00 or 0x40, got 0x%02X ! (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ }
+ WRITESLOW (0x0D, 0x1B);
+ switch (model)
+ {
+ case 0x1F:
+ WRITESLOW (0x12, 0x14);
+ SLOWNIBBLEREGISTERREAD (0x12, 0x10);
+ break;
+ case 0x07:
+ WRITESLOW (0x12, 0x00);
+ SLOWNIBBLEREGISTERREAD (0x12, 0x00);
+ /* we may get 0x20, in this case some color aberration may occur */
+ /* must depend on the parport */
+ /* model 0x07 + 0x00=>0x20=2000P */
+ break;
+ default:
+ WRITESLOW (0x12, 0x00);
+ SLOWNIBBLEREGISTERREAD (0x12, 0x20);
+ break;
+ }
+ SLOWNIBBLEREGISTERREAD (0x0D, 0x18);
+ SLOWNIBBLEREGISTERREAD (0x0C, 0x04);
+ SLOWNIBBLEREGISTERREAD (0x0A, 0x00);
+ WRITESLOW (0x0E, 0x0A);
+ WRITESLOW (0x0F, 0x00);
+ WRITESLOW (0x0E, 0x0D);
+ WRITESLOW (0x0F, 0x00);
+ dest = (unsigned char *) malloc (65536);
+ if (dest == NULL)
+ {
+ DBG (0, "Failed to allocate 64K (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ gMode = UMAX_PP_PARPORT_PS2;
+ if (probePS2 (dest))
+ { /* PS2 mode works */
+ DBG (16, "probePS2 passed ... (%s:%d)\n", __FILE__, __LINE__);
+ gprobed = UMAX_PP_PARPORT_PS2;
+ }
+
+ Outb (CONTROL, 4);
+ SLOWNIBBLEREGISTERREAD (0x0A, 0x18);
+ WRITESLOW (0x08, 0x40);
+ WRITESLOW (0x08, 0x60);
+ WRITESLOW (0x08, 0x22);
+
+ gMode = UMAX_PP_PARPORT_EPP;
+ if (probeEPP (dest))
+ { /* EPP mode works */
+ gprobed = UMAX_PP_PARPORT_EPP;
+ gMode = UMAX_PP_PARPORT_EPP;
+ DBG (16, "probeEPP passed ... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ else
+ { /* EPP fails, try ECP */
+ DBG (16, "probeEPP failed ... (%s:%d)\n", __FILE__, __LINE__);
+ gMode = UMAX_PP_PARPORT_ECP;
+ if (probeECP (dest))
+ { /* ECP mode works */
+ DBG (16, "probeECP passed ... (%s:%d)\n", __FILE__, __LINE__);
+ gprobed = UMAX_PP_PARPORT_ECP;
+ }
+ else
+ { /* ECP and EPP fail, give up */
+ /* PS2 could be used */
+ DBG (16, "probeECP failed ... (%s:%d)\n", __FILE__, __LINE__);
+ DBG (1, "No EPP or ECP mode working, giving up ... (%s:%d)\n",
+ __FILE__, __LINE__);
+ free (dest);
+ return 0;
+ }
+ }
+
+ /* some operations here may have to go into probeEPP/probeECP */
+ g6FE = 1;
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ WRITESLOW (0x08, 0x01);
+ Outb (CONTROL, 0x0C);
+ Outb (CONTROL, 0x04);
+ ClearRegister (0);
+ tmp = PS2Something (0x10);
+ if (tmp != 0x0B)
+ {
+ DBG (0,
+ "PS2Something returned 0x%02X, 0x0B expected (%s:%d)\n", tmp,
+ __FILE__, __LINE__);
+ }
+ }
+ else
+ {
+ WRITESLOW (0x08, 0x21);
+ init001 ();
+ DBG (16, "init001() passed... (%s:%d)\n", __FILE__, __LINE__);
+ }
+
+
+ reg = registerRead (0x0D);
+ reg = (reg & 0xE8) | 0x43;
+ registerWrite (0x0D, reg);
+
+ REGISTERWRITE (0x0A, 0x18);
+ REGISTERWRITE (0x0E, 0x0F);
+ REGISTERWRITE (0x0F, 0x0C);
+
+ REGISTERWRITE (0x0A, 0x1C);
+ REGISTERWRITE (0x0E, 0x10);
+ REGISTERWRITE (0x0F, 0x1C);
+
+
+ reg = registerRead (0x0D); /* 0x48 expected */
+ reg = registerRead (0x0D);
+ reg = registerRead (0x0D);
+ reg = (reg & 0xB7) | 0x03;
+ registerWrite (0x0D, reg);
+ DBG (16, "(%s:%d) passed \n", __FILE__, __LINE__);
+
+ reg = registerRead (0x12); /* 0x10 for model 0x0F, 0x20 for model 0x07 */
+ /* 0x00 when in ECP mode ... */
+ reg = reg & 0xEF;
+ registerWrite (0x12, reg);
+ DBG (16, "(%s:%d) passed \n", __FILE__, __LINE__);
+
+ reg = registerRead (0x0A);
+ if (reg != 0x1C)
+ {
+ DBG (0, "Warning! expected reg0A=0x1C, found 0x%02X! (%s:%d) \n", reg,
+ __FILE__, __LINE__);
+ }
+ DBG (16, "(%s:%d) passed \n", __FILE__, __LINE__);
+
+ /*Inb(CONTROL); ECP 0x04 expected */
+ disconnect ();
+ DBG (16, "disconnect() passed... (%s:%d)\n", __FILE__, __LINE__);
+ connect ();
+ DBG (16, "connect() passed... (%s:%d)\n", __FILE__, __LINE__);
+
+
+ /* some sort of countdown, some warming-up ? */
+ /* maybe some data sent to the stepper motor */
+ /* if (model == 0x07) */
+ {
+ /* REGISTERWRITE (0x0A, 0x00);
+ reg = registerRead (0x0D);
+ reg = (reg & 0xE8);
+ registerWrite (0x0D, reg);
+ DBG (16, "(%s:%d) passed \n", __FILE__, __LINE__); */
+ epilogue ();
+ prologue (0x10);
+
+ reg = registerRead (0x13);
+ if (reg != 0x00)
+ {
+ DBG (0, "Warning! expected reg13=0x00, found 0x%02X! (%s:%d) \n",
+ reg, __FILE__, __LINE__);
+ }
+ REGISTERWRITE (0x13, 0x81);
+ usleep (10000);
+ REGISTERWRITE (0x13, 0x80);
+ /* could it be step-motor values ? */
+ REGISTERWRITE (0x0E, 0x04); /* FF->R04 */
+ REGISTERWRITE (0x0F, 0xFF);
+ REGISTERWRITE (0x0E, 0x05); /* 03->R05 */
+ REGISTERWRITE (0x0F, 0x03);
+ REGISTERWRITE (0x10, 0x66);
+ usleep (10000);
+
+ REGISTERWRITE (0x0E, 0x04); /* FF->R04 */
+ REGISTERWRITE (0x0F, 0xFF);
+ REGISTERWRITE (0x0E, 0x05); /* 01 ->R05 */
+ REGISTERWRITE (0x0F, 0x01);
+ REGISTERWRITE (0x10, 0x55);
+ usleep (10000);
+
+ REGISTERWRITE (0x0E, 0x04); /* FF -> R04 */
+ REGISTERWRITE (0x0F, 0xFF);
+ REGISTERWRITE (0x0E, 0x05); /* 00 -> R05 */
+ REGISTERWRITE (0x0F, 0x00);
+ REGISTERWRITE (0x10, 0x44);
+ usleep (10000);
+
+ REGISTERWRITE (0x0E, 0x04); /* 7F -> R04 */
+ REGISTERWRITE (0x0F, 0x7F);
+ REGISTERWRITE (0x0E, 0x05); /* 00 -> R05 */
+ REGISTERWRITE (0x0F, 0x00);
+ REGISTERWRITE (0x10, 0x33);
+ usleep (10000);
+
+ REGISTERWRITE (0x0E, 0x04); /* 3F -> R04 */
+ REGISTERWRITE (0x0F, 0x3F);
+ REGISTERWRITE (0x0E, 0x05);
+ REGISTERWRITE (0x0F, 0x00); /* 00 -> R05 */
+ REGISTERWRITE (0x10, 0x22);
+ usleep (10000);
+
+ REGISTERWRITE (0x0E, 0x04);
+ REGISTERWRITE (0x0F, 0x00);
+ REGISTERWRITE (0x0E, 0x05);
+ REGISTERWRITE (0x0F, 0x00);
+ REGISTERWRITE (0x10, 0x11);
+ usleep (10000);
+
+ REGISTERWRITE (0x13, 0x81);
+ usleep (10000);
+ REGISTERWRITE (0x13, 0x80);
+
+ REGISTERWRITE (0x0E, 0x04);
+ REGISTERWRITE (0x0F, 0x00);
+ REGISTERWRITE (0x0E, 0x05);
+ REGISTERWRITE (0x0F, 0x00);
+ usleep (10000);
+
+ reg = registerRead (0x10);
+ DBG (1, "Count-down value is 0x%02X (%s:%d)\n", reg, __FILE__, __LINE__);
+ /* 2 reports of CF, was FF first (typo ?) */
+ /* CF seems a valid value */
+ /* in case of CF, we may have timeout ... */
+ /*if (reg != 0x00)
+ {
+ DBG (0, "Warning! expected reg10=0x00, found 0x%02X! (%s:%d) \n",
+ reg, __FILE__, __LINE__);
+ } */
+ REGISTERWRITE (0x13, 0x00);
+ }
+/* end of countdown */
+ DBG (16, "(%s:%d) passed \n", __FILE__, __LINE__);
+ /* *NOT* epilogue(); (when EPP) */
+ /*REGISTERWRITE (0x0A, 0x00);
+ REGISTERREAD (0x0D, 0x40);
+ REGISTERWRITE (0x0D, 0x00); */
+ epilogue ();
+ prologue (0x10);
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+ REGISTERWRITE (0x0A, 0x1C);
+
+ /* real start of high level protocol ? */
+ if (fonc001 () != 1)
+ {
+ DBG (0, "fonc001() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "fonc001() passed (%s:%d) \n", __FILE__, __LINE__);
+ reg = registerRead (0x19) & 0xC8;
+ /* if reg=E8 or D8 , we have a 'messed' scanner */
+
+ /* 4 tranform buffers + 'void' are sent: 1 B&W, and 3 RGB ? */
+ memset (initbuf, 0x00, 2048 * sizeof (int));
+ memset (voidbuf, 0x00, 2048 * sizeof (int));
+
+ initbuf[512] = 0xFF;
+ initbuf[513] = 0xAA;
+ initbuf[514] = 0x55;
+
+ for (j = 0; j < 4; j++)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ voidbuf[512 * j + 2 * i] = i;
+ voidbuf[512 * j + 2 * i] = 0xFF - i;
+ }
+ }
+
+ /* one pass (B&W ?) */
+ if (cmdSetDataBuffer (initbuf) != 1)
+ {
+ DBG (0, "cmdSetDataBuffer(initbuf) failed ! (%s:%d) \n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16, "cmdSetDataBuffer(initbuf) passed... (%s:%d)\n", __FILE__,
+ __LINE__);
+ if (cmdSetDataBuffer (voidbuf) != 1)
+ {
+ DBG (0, "cmdSetDataBuffer(voidbuf) failed ! (%s:%d) \n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16, "cmdSetDataBuffer(voidbuf) passed... (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ /* everything above the FF 55 AA tag is 'void' */
+ /* it seems that the buffer is reused and only the beginning is initalized */
+ for (i = 515; i < 2048; i++)
+ initbuf[i] = voidbuf[i];
+
+ /* three pass (RGB ?) */
+ for (i = 0; i < 3; i++)
+ {
+ if (cmdSetDataBuffer (initbuf) != 1)
+ {
+ DBG (0, "cmdSetDataBuffer(initbuf) failed ! (%s:%d) \n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16, "Loop %d: cmdSetDataBuffer(initbuf) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ if (cmdSetDataBuffer (voidbuf) != 1)
+ {
+ DBG (0, "Loop %d: cmdSetDataBuffer(voidbuf) failed ! (%s:%d) \n", i,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ }
+
+
+ /* memory size testing ? */
+ /* load 150 Ko in scanner */
+ REGISTERWRITE (0x1A, 0x00);
+ REGISTERWRITE (0x1A, 0x0C);
+ REGISTERWRITE (0x1A, 0x00);
+ REGISTERWRITE (0x1A, 0x0C);
+
+
+ REGISTERWRITE (0x0A, 0x11); /* start */
+ nb = 150;
+ for (i = 0; i < nb; i++) /* 300 for ECP ??? */
+ {
+ bufferWrite (0x400, dest);
+ DBG (16, "Loop %d: bufferWrite(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+ REGISTERWRITE (0x0A, 0x18); /* end */
+
+ /* read them back */
+ REGISTERWRITE (0x0A, 0x11); /*start transfert */
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ ECPSetBuffer (0x400);
+ }
+
+ for (i = 0; i < nb; i++) /* 300 for ECP ??? */
+ {
+ bufferRead (0x400, dest);
+ DBG (16, "Loop %d: bufferRead(0x400,dest) passed... (%s:%d)\n", i,
+ __FILE__, __LINE__);
+ }
+ REGISTERWRITE (0x0A, 0x18); /*end transfer */
+
+ /* fully disconnect, then reconnect */
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ epilogue ();
+ sendCommand (0xE0);
+ Outb (DATA, 0xFF);
+ Outb (DATA, 0xFF);
+ ClearRegister (0);
+ WRITESLOW (0x0E, 0x0A);
+ SLOWNIBBLEREGISTERREAD (0x0F, 0x00);
+ WRITESLOW (0x0F, 0x08);
+ WRITESLOW (0x08, 0x10); /* 0x10 ?? */
+ prologue (0x10);
+ }
+
+
+ /* almost cmdSync(0x00) which halts any pending operation */
+ if (fonc001 () != 1)
+ {
+ DBG (0, "fonc001() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "Fct001() passed (%s:%d) \n", __FILE__, __LINE__);
+ if (sendWord (zero) == 0)
+ {
+ DBG (0, "sendWord(zero) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ epilogue ();
+ DBG (16, "sendWord(zero) passed (%s:%d)\n", __FILE__, __LINE__);
+
+
+ /* end transport init */
+ /* now high level (connected) protocol begins */
+ val = sanei_umax_pp_initScanner (recover);
+ if (val == 0)
+ {
+ DBG (0, "initScanner() failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+
+ /* if no homing .... */
+ if (val == 1)
+ {
+ CMDSYNC (0);
+ CMDSYNC (0xC2);
+ CMDSYNC (0);
+ }
+
+ /* set port to its initial state */
+ Outb (DATA, gData);
+ Outb (CONTROL, gControl);
+
+ free (dest);
+ DBG (1, "probe done ...\n");
+ return 1;
+}
+
+
+static int
+disconnect_epat (void)
+{
+ REGISTERWRITE (0x0A, 0x00);
+ registerRead (0x0D);
+ REGISTERWRITE (0x0D, 0x00);
+ disconnect ();
+ return 1;
+}
+
+
+static int
+connect_epat (int r08)
+{
+ int reg;
+
+ if (connect () != 1)
+ {
+ DBG (0, "connect_epat: connect() failed! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+
+ reg = registerRead (0x0B);
+ if (reg != gEPAT)
+ {
+ /* ASIC version is not */
+ /* the one expected (epat c7) */
+ DBG (0, "Error! expected reg0B=0x%02X, found 0x%02X! (%s:%d) \n", gEPAT,
+ reg, __FILE__, __LINE__);
+ /* we try to clean all */
+ disconnect ();
+ return 0;
+ }
+ reg = registerRead (0x0D);
+ reg = (reg | 0x43) & 0xEB;
+ REGISTERWRITE (0x0D, reg);
+ REGISTERWRITE (0x0C, 0x04);
+ reg = registerRead (0x0A);
+ if (reg != 0x00)
+ {
+ /* a previous unfinished command */
+ /* has left an uncleared value */
+ DBG (0, "Warning! expected reg0A=0x00, found 0x%02X! (%s:%d) \n", reg,
+ __FILE__, __LINE__);
+ }
+ REGISTERWRITE (0x0A, 0x1C);
+ if (r08 != 0)
+ {
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x08, r08); /* 0x01 or 0x10 ??? */
+ }
+ else
+ {
+ REGISTERWRITE (0x08, 0x21);
+ }
+ }
+ REGISTERWRITE (0x0E, 0x0F);
+ REGISTERWRITE (0x0F, 0x0C);
+ REGISTERWRITE (0x0A, 0x1C);
+ REGISTERWRITE (0x0E, 0x10);
+ REGISTERWRITE (0x0F, 0x1C);
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x0F, 0x00);
+ }
+ return 1;
+}
+
+
+static int
+prologue (int r08)
+{
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ connect610p ();
+ return sync610p ();
+ case 1220:
+ case 1600:
+ case 2000:
+ default:
+ return connect_epat (r08);
+ }
+}
+
+
+
+static int
+epilogue (void)
+{
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ return disconnect610p ();
+ case 1220:
+ case 1600:
+ case 2000:
+ default:
+ return disconnect_epat ();
+ }
+}
+
+
+static int
+EPPcmdGet610p (int cmd, int len, int *val)
+{
+ int word[4];
+ int i, status, control;
+
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80 | 0x40; /* 0x40 means 'read' */
+
+ connect610p ();
+ sync610p ();
+
+ /* sends magic seal 55 AA */
+ status = EPPputByte610p (0x55);
+ if (status != 0xC8)
+ {
+ DBG (1, "EPPcmdGet610p: Found 0x%X expected 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ status = EPPputByte610p (0xAA);
+ if (status != 0xC8)
+ {
+ DBG (1, "EPPcmdGet610p: Found 0x%02X expected 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* tests status */
+ /* scannerStatus=0x58 */
+ status = EPPgetStatus610p ();
+ if (status != 0xC8)
+ {
+ DBG (1,
+ "EPPcmdGet610p: Found 0x%X expected 0xC8, status=0x%02X (%s:%d)\n",
+ status, scannerStatus, __FILE__, __LINE__);
+ return 0;
+ }
+
+
+ /* sends length of data */
+ for (i = 0; (i < 4) && (status == 0xC8); i++)
+ {
+ status = EPPputByte610p (word[i]);
+ }
+ if (status != 0xC8)
+ {
+ DBG (1, "EPPcmdGet610p: loop %d, found 0x%02X expected 0xC8 (%s:%d)\n",
+ i, status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ Outb (DATA, 0xFF);
+
+ /* tests status */
+ /* scannerStatus=0x58 */
+ status = EPPgetStatus610p ();
+ if (status != 0xD0)
+ {
+ DBG (1,
+ "EPPcmdGet610p: Found 0x%X expected 0xD0, status=0x%02X (%s:%d)\n",
+ status, scannerStatus, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* data reverse */
+ control = Inb (CONTROL) & 0xF4;
+ control = control | 0xA0;
+
+ /* receive data */
+ i = 0;
+ while (i < len)
+ {
+ status = Inb (STATUS) & 0xF8;
+ if (status & 0x08)
+ {
+ DBG (1,
+ "EPPcmdGet610p: loop %d, found 0x%X expected 0xD0 or 0xC0 (%s:%d)\n",
+ i, status, __FILE__, __LINE__);
+ return 0;
+ }
+ val[i] = Inb (EPPDATA);
+ i++;
+ }
+
+ if (DBG_LEVEL >= 8)
+ {
+ char *str = NULL;
+
+ str = malloc (3 * len + 1);
+ if (str != NULL)
+ {
+ for (i = 0; i < len; i++)
+ {
+ sprintf (str + 3 * i, "%02X ", val[i]);
+ }
+ str[3 * i] = 0x00;
+ DBG (8, "String received for %02X: %s\n", cmd, str);
+ free (str);
+ }
+ else
+ {
+ TRACE (8, "not enough memory for debugging ...");
+ }
+ }
+
+ /* scannerStatus=0x58 */
+ status = EPPgetStatus610p ();
+ scannerStatus = status;
+ if (status != 0xC0)
+ {
+ DBG (0, "EPPcmdGet610p: Found 0x%02X expected 0xC0 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ disconnect610p ();
+ return 1;
+}
+
+
+
+static int
+cmdGet610p (int cmd, int len, int *val)
+{
+ int word[5];
+ int i, j, status;
+
+ if ((cmd == 8) && (len > 0x23))
+ len = 0x23;
+
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ return EPPcmdGet610p (cmd, len, val);
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80 | 0x40; /* 0x40 means 'read' */
+ word[4] = -1;
+
+ connect610p ();
+ sync610p ();
+ if (sendLength610p (word) == 0)
+ {
+ DBG (0, "sendLength610p(word) failed... (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ status = getStatus610p ();
+ scannerStatus = status;
+ if ((status != 0xC0) && (status != 0xD0))
+ {
+ DBG (0, "Found 0x%02X expected 0xC0 or 0xD0 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ if (receiveData610p (val, len) == 0)
+ {
+ DBG (0, "sendData610p(val,%d) failed (%s:%d)\n", len, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ status = getStatus610p ();
+ scannerStatus = status;
+ j = 0;
+ while ((j < 256) && (status & 0x08))
+ {
+ status = getStatus610p ();
+ j++;
+ }
+ if (status != 0xC0)
+ {
+ DBG (0, "Found 0x%02X expected 0xC0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ disconnect610p ();
+
+ if (DBG_LEVEL >= 8)
+ {
+ char *str = NULL;
+
+ str = malloc (3 * len + 1);
+ if (str != NULL)
+ {
+ for (i = 0; i < len; i++)
+ {
+ sprintf (str + 3 * i, "%02X ", val[i]);
+ }
+ str[3 * i] = 0x00;
+ DBG (8, "String received for %02X: %s\n", cmd, str);
+ free (str);
+ }
+ else
+ {
+ TRACE (8, "not enough memory for debugging ...");
+ }
+ }
+ return 1;
+}
+
+
+static int
+EPPcmdSet610p (int cmd, int len, int *val)
+{
+ int word[5];
+ int i, status;
+
+ if ((cmd == 8) && (len > 0x23))
+ {
+ /* blank useless extra bytes */
+ for (i = 0x22; i < len; i++)
+ val[i] = 0x00;
+ }
+
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80;
+
+ connect610p ();
+ sync610p ();
+
+ /* sends magic seal 55 AA */
+ status = EPPputByte610p (0x55);
+ if ((status != 0xC8) && (status!=0xC0))
+ {
+ DBG (0, "EPPcmdSet610p: Found 0x%X expected 0xC0 or 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+ status = EPPputByte610p (0xAA);
+ if ((status != 0xC8) && (status!=0xC0))
+ {
+ DBG (0, "EPPcmdSet610p: Found 0x%X expected 0xC0 or 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* tests status */
+ status = EPPgetStatus610p ();
+ if ((status != 0xC8) && (status!=0xC0))
+ {
+ DBG (0, "EPPcmdSet610p: Found 0x%02X expected 0xC0 or 0xC8 (%s:%d)\n", status,
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* sends length of data */
+ for (i = 0; i < 4; i++)
+ {
+ status = EPPputByte610p (word[i]);
+ }
+ if ((status != 0xC8) && (status!=0xC0))
+ {
+ DBG (0, "EPPcmdSet610p: loop %d, found 0x%02X expected 0xC0 or 0xC8 (%s:%d)\n",
+ i, status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ Outb (DATA, 0xFF);
+
+ /* tests status */
+ status = EPPgetStatus610p ();
+ if (status != 0xC0)
+ {
+ DBG (0, "Found 0x%X expected 0xC0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+
+ /* sends data */
+ status = 0xC8;
+ for (i = 0; (i < len) && (status == 0xC8); i++)
+ {
+ /* escape special values with ESC (0x1B) */
+ if (val[i] == 0x1B)
+ status = EPPputByte610p (0x1B);
+ if (i > 0)
+ {
+ if ((val[i] == 0xAA) && (val[i - 1] == 0x55))
+ status = EPPputByte610p (0x1B);
+ }
+ /* now send data */
+ status = EPPputByte610p (val[i]);
+ }
+
+ if (status != 0xC8)
+ {
+ DBG (0, "EPPcmdSet610p: loop %d, found 0x%02X expected 0xC8 (%s:%d)\n",
+ i, status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ Outb (DATA, 0xFF);
+ status = EPPgetStatus610p ();
+ if (status != 0xC0)
+ {
+ DBG (0, "Found 0x%X expected 0xC0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+
+ disconnect610p ();
+ return 1;
+}
+
+
+static int
+cmdSet610p (int cmd, int len, int *val)
+{
+ int word[5];
+ int i, j, status;
+
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ return EPPcmdSet610p (cmd, len, val);
+
+ if ((cmd == 8) && (len > 0x23))
+ {
+ /* blank useless extra bytes */
+ for (i = 0x22; i < len; i++)
+ val[i] = 0x00;
+ }
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80;
+ word[4] = -1;
+
+ connect610p ();
+ sync610p ();
+ if (sendLength610p (word) == 0)
+ {
+ DBG (0, "sendLength610p(word) failed... (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ status = getStatus610p ();
+ scannerStatus = status;
+ if ((status != 0xC0) && (status != 0xD0))
+ {
+ DBG (1, "Found 0x%X expected 0xC0 or 0xD0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ if (sendData610p (val, len) == 0)
+ {
+ DBG (1, "sendData610p(val,%d) failed (%s:%d)\n", len, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ status = getStatus610p ();
+ scannerStatus = status;
+ j = 0;
+ while ((j < 256) && (status & 0x08))
+ {
+ status = getStatus610p ();
+ j++;
+ }
+ if (status != 0xC0)
+ {
+ DBG (1, "Found 0x%X expected 0xC0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ /* return 0; */
+ }
+ disconnect610p ();
+ return 1;
+}
+
+static int
+cmdSet (int cmd, int len, int *val)
+{
+ int word[5];
+ int i;
+
+ if (DBG_LEVEL >= 8)
+ {
+ char *str = NULL;
+
+ str = malloc (3 * len + 1);
+ if (str != NULL)
+ {
+ for (i = 0; i < len; i++)
+ {
+ sprintf (str + 3 * i, "%02X ", val[i]);
+ }
+ str[3 * i] = 0x00;
+ DBG (8, "String sent for %02X: %s\n", cmd, str);
+ free (str);
+ }
+ else
+ {
+ TRACE (8, "not enough memory for debugging ...");
+ }
+ }
+
+ if (sanei_umax_pp_getastra () == 610)
+ return cmdSet610p (cmd, len, val);
+
+ /* cmd 08 as 2 length depending upon model */
+ if ((cmd == 8) && (getModel () == 0x07))
+ {
+ len = 35;
+ }
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80;
+
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdSet: prologue failed ! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* send data */
+ if (sendLength (word, 4) == 0)
+ {
+ DBG (0, "sendLength(word,4) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ TRACE (16, "sendLength(word,4) passed ...");
+
+ /* head end */
+ epilogue ();
+
+ if (len > 0)
+ {
+ /* send body */
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdSet: prologue failed ! (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+
+ /* send data */
+ if (sendData (val, len) == 0)
+ {
+ DBG (0, "sendData(word,%d) failed (%s:%d)\n", len, __FILE__,
+ __LINE__);
+ epilogue ();
+ return 0;
+ }
+ TRACE (16, "sendData(val,len) passed ...");
+ /* body end */
+ epilogue ();
+ }
+ return 1;
+}
+
+static int
+cmdGet (int cmd, int len, int *val)
+{
+ int word[5];
+ int i;
+
+ if (sanei_umax_pp_getastra () == 610)
+ return cmdGet610p (cmd, len, val);
+
+ /* cmd 08 as 2 length depending upon model */
+ if ((cmd == 8) && (getModel () == 0x07))
+ {
+ len = 35;
+ }
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80 | 0x40; /* 0x40 means 'read' */
+ word[4] = -1;
+
+ /* send header */
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdGet: prologue failed ! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* send data */
+ if (sendLength (word, 4) == 0)
+ {
+ DBG (0, "sendLength(word,4) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ TRACE (16, "sendLength(word,4) passed ...");
+
+ /* head end */
+ epilogue ();
+
+
+ /* send header */
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdGet: prologue failed ! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* get actual data */
+ if (receiveData (val, len) == 0)
+ {
+ DBG (0, "receiveData(val,len) failed (%s:%d)\n", __FILE__, __LINE__);
+ epilogue ();
+ return 0;
+ }
+ if (DBG_LEVEL >= 8)
+ {
+ char *str = NULL;
+
+ str = malloc (3 * len + 1);
+ if (str != NULL)
+ {
+ for (i = 0; i < len; i++)
+ {
+ sprintf (str + 3 * i, "%02X ", val[i]);
+ }
+ str[3 * i] = 0x00;
+ DBG (8, "String received for %02X: %s\n", cmd, str);
+ free (str);
+ }
+ else
+ {
+ TRACE (8, "not enough memory for debugging ...");
+ }
+ }
+ epilogue ();
+ return 1;
+}
+
+
+
+static int
+cmdSetGet (int cmd, int len, int *val)
+{
+ int *tampon;
+ int i;
+
+ /* model revision 0x07 uses 35 bytes buffers */
+ /* cmd 08 as 2 length depending upon model */
+ if ((cmd == 8) && (getModel () == 0x07))
+ {
+ len = 0x23;
+ }
+
+ /* first we send */
+ if (cmdSet (cmd, len, val) == 0)
+ {
+ DBG (0, "cmdSetGet failed ! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ tampon = (int *) malloc (len * sizeof (int));
+ memset (tampon, 0x00, len * sizeof (int));
+ if (tampon == NULL)
+ {
+ DBG (0, "Failed to allocate room for %d int ! (%s:%d)\n", len, __FILE__,
+ __LINE__);
+ epilogue ();
+ return 0;
+ }
+
+ /* then we receive */
+ if (cmdGet (cmd, len, tampon) == 0)
+ {
+ DBG (0, "cmdSetGet failed ! (%s:%d)\n", __FILE__, __LINE__);
+ free (tampon);
+ epilogue ();
+ return 0;
+ }
+
+ /* check and copy */
+ for (i = 0; (i < len) && (val[i] >= 0); i++)
+ {
+ if (tampon[i] != val[i])
+ {
+ DBG
+ (0,
+ "Warning data read back differs: expected %02X found tampon[%d]=%02X ! (%s:%d)\n",
+ val[i], i, tampon[i], __FILE__, __LINE__);
+ }
+ val[i] = tampon[i];
+ }
+
+
+ /* OK */
+ free (tampon);
+ return 1;
+}
+
+
+/* 1 OK, 0 failed */
+static int
+EPPcmdGetBuffer610p (int cmd, int len, unsigned char *buffer)
+{
+ int status, i, tmp, control;
+ int word[5];
+ int count, needed, max;
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode, rc;
+#endif
+ int loop, wait, remain;
+
+ /* first we set length and channel */
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = (len / 256) % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x0F) | 0xC0;
+ word[4] = -1;
+
+ connect610p (); /* start of EPPsendLength610p */
+ sync610p ();
+
+ /* sends magic seal 55 AA */
+ status = EPPputByte610p (0x55);
+ if ((status != 0xD0) && (status != 0xC8))
+ {
+ DBG (0, "EPPcmdGetBuffer610p: Found 0x%X expected 0xC8 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ status = EPPputByte610p (0xAA);
+ if ((status != 0xD0) && (status != 0xC8))
+ {
+ DBG (0, "EPPcmdGetBuffer610p: Found 0x%02X expected 0xC8 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* tests status */
+ status = EPPgetStatus610p ();
+ if ((status != 0xD0) && (status != 0xC8))
+ {
+ DBG (0, "EPPcmdGetBuffer610p: Found 0x%X expected 0xC8 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* sends length of data */
+ for (i = 0; i < 4; i++)
+ {
+ status = EPPputByte610p (word[i]);
+ }
+ if ((status != 0xC0) && (status != 0xC8))
+ {
+ DBG (0,
+ "EPPcmdGetBuffer610p: loop %d, found 0x%02X expected 0xC0 or 0xC8 (%s:%d)\n",
+ i, status, __FILE__, __LINE__);
+ return 0;
+ }
+
+ Outb (DATA, 0xFF);
+
+ /* test status */
+ status = EPPgetStatus610p ();
+ if ((status != 0xC0) && (status != 0xD0))
+ {
+ DBG (0,
+ "EPPcmdGetBuffer610p: Found 0x%X expected 0xC0 or 0xD0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ /*return 0; */
+ }
+ disconnect610p (); /* end of EPPsendLength610p */
+
+ /* max data read in one go */
+ if (sanei_umax_pp_getfull () == 1)
+ max = 2550 / 3;
+ else
+ max = 32768;
+
+ /* loop until enough data is read */
+ count = 0;
+ while (count < len)
+ {
+ if (len - count > max)
+ needed = max;
+ else
+ needed = len - count;
+ if (needed % 4)
+ remain = needed % 4;
+ else
+ remain = 4;
+ loop = (needed - remain) / 2;
+ wait = 0;
+ DBG (32, "EPPcmdGetBuffer610p: %d loops to do \n", loop);
+
+ status = 0x20;
+
+ /* wait for data ready */
+ while ((status & 0x80) != 0x80)
+ {
+ /* this is SPPgetStatus */
+ connect610p ();
+ Outb (CONTROL, 0x07);
+ Outb (DATA, 0xFF);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (0,
+ "EPPcmdGetBuffer610p found 0x%02X expected 0xFF (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ return 0;
+ }
+ status = Inb (STATUS) & 0xF8;
+ if ((status & 0x80) != 0x80)
+ {
+ disconnect610p ();
+ usleep(1000);
+ }
+ else
+ {
+ Outb (CONTROL, 0x04);
+ sync610p ();
+ Outb (DATA, 0xFF);
+ control = (Inb (CONTROL) & 0x44) | 0xE4;
+ Outb (CONTROL, control);
+ }
+ }
+
+ /* EPP block read */
+ /* there is one form for full CCD width reads, and another for other
+ reads */
+#ifdef HAVE_LINUX_PPDEV_H
+ /* check we have ppdev working */
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (rc)
+ DBG (0,
+ "EPPcmdGetBuffer610p: ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+ if (rc)
+ DBG (0,
+ "EPPcmdGetBuffer610p: ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ if (rc)
+ {
+ DBG (0,
+ "EPPcmdGetBuffer610p: ppdev ioctl returned <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ return 0;
+ }
+ if (sanei_umax_pp_getfull () == 1)
+ {
+ do
+ {
+ rc = read (fd, buffer + count, needed);
+ }
+ while (rc == EAGAIN);
+ if (rc < 0)
+ {
+ DBG (0,
+ "EPPcmdGetBuffer610p: ppdev read failed <%s> (%s:%d)\n",
+ strerror (errno), __FILE__, __LINE__);
+ return 0;
+ }
+#ifdef IOLOG
+ DBG (0, "insb *%d\n", rc);
+#endif
+ needed = rc;
+ }
+ else
+ {
+ for (loop = 0; (loop < needed) && (wait==0); loop++)
+ {
+ status = Inb (STATUS) & 0xF8;
+ if ((status != 0xD0) && (status != 0xC0)
+ && (status != 0xC8))
+ {
+ DBG (0,
+ "EPPcmdGetBuffer610p found 0x%02X expected 0xD0 or 0xC0 (%s:%d)\n",
+ status, __FILE__, __LINE__);
+ return 0;
+ }
+ if (status == 0xC8)
+ {
+ wait = 1;
+ needed=loop;
+ }
+ else
+ {
+ tmp = Inb (EPPDATA);
+ buffer[count + loop] = tmp;
+ }
+ }
+ }
+ }
+ else
+#endif /* HAVE_LINUX_PPDEV_H */
+ {
+ Insb (EPPDATA, buffer + count, needed);
+ }
+ count += needed;
+ disconnect610p ();
+ }
+ usleep (10000);
+ /* ??? CMDSYNC (0x00); */
+ /* everything went fine */
+ return 1;
+}
+
+/* 1 OK, 0 failed */
+static int
+cmdGetBuffer610p (int cmd, int len, unsigned char *buffer)
+{
+ int status, i, tmp;
+ int word[5];
+ int read, needed, max;
+
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ return EPPcmdGetBuffer610p (cmd, len, buffer);
+
+ /* first we set length and channel */
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = (len / 256) % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x0F) | 0xC0;
+ word[4] = -1;
+
+ connect610p ();
+ sync610p ();
+ if (sendLength610p (word) == 0)
+ {
+ DBG (0, "sendLength610p(word) failed... (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ status = getStatus610p ();
+ scannerStatus = status;
+ if ((status != 0xC0) && (status != 0xD0))
+ {
+ DBG (1, "Found 0x%X expected 0xC0 or 0xD0 (%s:%d)\n", status, __FILE__,
+ __LINE__);
+ return 0;
+ }
+ disconnect610p ();
+
+ if (sanei_umax_pp_getfull () == 1)
+ max = 2550 / 3;
+ else
+ max = 32768;
+ read = 0;
+ while (read < len)
+ {
+ if (len - read > max)
+ needed = max;
+ else
+ needed = len - read;
+
+ if (sanei_umax_pp_getfull () == 0)
+ status = getStatus610p ();
+ else
+ status = 0x20;
+
+ /* wait for data ready */
+ while ((status & 0x80) == 0x00)
+ {
+ connect610p ();
+ Outb (CONTROL, 0x07);
+ Outb (DATA, 0xFF);
+ tmp = Inb (DATA);
+ if (tmp != 0xFF)
+ {
+ DBG (0,
+ "cmdGetBuffer610p found 0x%02X expected 0xFF (%s:%d)\n",
+ tmp, __FILE__, __LINE__);
+ return 0;
+ }
+ status = Inb (STATUS) & 0xF8;
+ if ((status & 0x80) == 0x00)
+ disconnect610p ();
+ else
+ {
+ Outb (CONTROL, 0x04);
+ sync610p ();
+ byteMode ();
+ }
+ }
+
+ i = 0;
+ while (i < needed)
+ {
+ if (sanei_umax_pp_getfull () == 0)
+ {
+ status = Inb (STATUS) & 0xF8;
+ if (status == 0xC8)
+ {
+ for (tmp = 0; tmp < 18; tmp++)
+ status = Inb (STATUS) & 0xF8;
+ break;
+ }
+ }
+ Outb (CONTROL, 0x26); /* data reverse+ 'reg' */
+ buffer[read + i] = Inb (DATA);
+ Outb (CONTROL, 0x24); /* data reverse+ 'reg' */
+ i++;
+ }
+ byteMode ();
+ disconnect610p ();
+ read += i;
+ }
+
+ return 1;
+}
+
+
+/* 1 OK, 0 failed */
+static int
+cmdGetBuffer (int cmd, int len, unsigned char *buffer)
+{
+ int reg, tmp, i;
+ int word[5], read;
+ int needed;
+
+ if (sanei_umax_pp_getastra () == 610)
+ return cmdGetBuffer610p (cmd, len, buffer);
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x0F) | 0xC0;
+ word[4] = -1;
+
+ /* send word: len+addr(?) */
+ if (foncSendWord (word) == 0)
+ {
+ DBG (0, "foncSendWord(word) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "(%s:%d) passed \n", __FILE__, __LINE__);
+
+ prologue (0x10);
+
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+
+ i = 0;
+ reg = registerRead (0x19) & 0xF8;
+
+ /* wait if busy */
+ while ((reg & 0x08) == 0x08)
+ reg = registerRead (0x19) & 0xF8;
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0, "cmdGetBuffer failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x1A, 0x44);
+ }
+
+ read = 0;
+ reg = registerRead (0x0C);
+ if (reg != 0x04)
+ {
+ DBG (0, "cmdGetBuffer failed: unexpected status 0x%02X ...(%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ return 0;
+ }
+ REGISTERWRITE (0x0C, reg | 0x40);
+
+ /* actual data */
+ read = 0;
+ while (read < len)
+ {
+ needed = len - read;
+ if (needed > 32768)
+ needed = 32768;
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ compatMode ();
+ Outb (CONTROL, 0x04); /* reset ? */
+ ECPSetBuffer (needed);
+ tmp = ECPbufferRead (needed, buffer + read);
+ DBG (16, "ECPbufferRead(%d,buffer+read) passed (%s:%d)\n", needed,
+ __FILE__, __LINE__);
+ REGISTERWRITE (0x1A, 0x84);
+ }
+ else
+ {
+ tmp = pausedBufferRead (needed, buffer + read);
+ }
+ if (tmp < needed)
+ {
+ DBG (64, "cmdGetBuffer only got %d bytes out of %d ...(%s:%d)\n",
+ tmp, needed, __FILE__, __LINE__);
+ }
+ else
+ {
+ DBG (64,
+ "cmdGetBuffer got all %d bytes out of %d , read=%d...(%s:%d)\n",
+ tmp, 32768, read, __FILE__, __LINE__);
+ }
+ read += tmp;
+ DBG (16, "Read %d bytes out of %d (last block is %d bytes) (%s:%d)\n",
+ read, len, tmp, __FILE__, __LINE__);
+ if (read < len)
+ {
+ /* wait for scanner to be ready */
+ reg = registerRead (0x19) & 0xF8;
+ DBG (64, "Status after block read is 0x%02X (%s:%d)\n", reg,
+ __FILE__, __LINE__);
+ if ((reg & 0x08) == 0x08)
+ {
+ int pass = 0;
+
+ do
+ {
+ reg = registerRead (0x19) & 0xF8;
+ usleep (100);
+ pass++;
+ }
+ while ((pass < 32768) && ((reg & 0x08) == 0x08));
+ DBG (64, "Status after waiting is 0x%02X (pass=%d) (%s:%d)\n",
+ reg, pass, __FILE__, __LINE__);
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0,
+ "Unexpected status 0x%02X, expected 0xC0 or 0xD0 ! (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Going on...\n");
+ }
+ }
+
+ /* signal we want next data chunk */
+ if (gMode == UMAX_PP_PARPORT_ECP)
+ {
+ REGISTERWRITE (0x1A, 0x44);
+ }
+ reg = registerRead (0x0C);
+ registerWrite (0x0C, reg | 0x40);
+ }
+ }
+
+ /* OK ! */
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+
+ /* epilogue */
+ epilogue ();
+ return 1;
+}
+
+/* 1 OK, 0 failed */
+static int
+cmdGetBuffer32 (int cmd, int len, unsigned char *buffer)
+{
+ int reg, tmp, i;
+ int word[5], read;
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80 | 0x40;
+
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdSet: prologue failed ! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* send data */
+ if (sendLength (word, 4) == 0)
+ {
+ DBG (0, "sendLength(word,4) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ TRACE (16, "sendLength(word,4) passed ...");
+
+ /* head end */
+ epilogue ();
+
+ prologue (0x10);
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+
+ i = 0;
+ reg = registerRead (0x19) & 0xF8;
+
+ /* wait if busy */
+ while ((reg & 0x08) == 0x08)
+ reg = registerRead (0x19) & 0xF8;
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0, "cmdGetBuffer32 failed: unexpected status 0x%02X ...(%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ return 0;
+ }
+ reg = registerRead (0x0C);
+ if (reg != 0x04)
+ {
+ DBG (0, "cmdGetBuffer32 failed: unexpected status 0x%02X ...(%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ return 0;
+ }
+ REGISTERWRITE (0x0C, reg | 0x40);
+
+ read = 0;
+ while (read < len)
+ {
+ if (read + 1700 < len)
+ {
+ tmp = 1700;
+ bufferRead (tmp, buffer + read);
+ reg = registerRead (0x19) & 0xF8;
+ if ((read + tmp < len) && (reg & 0x08) == 0x08)
+ {
+ do
+ {
+ reg = registerRead (0x19) & 0xF8;
+ }
+ while ((reg & 0x08) == 0x08);
+ if ((reg != 0xC0) && (reg != 0xD0))
+ {
+ DBG (0,
+ "Unexpected status 0x%02X, expected 0xC0 or 0xD0 ! (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Going on...\n");
+ }
+ }
+ reg = registerRead (0x0C);
+ registerWrite (0x0C, reg | 0x40);
+ read += tmp;
+ }
+ else
+ {
+ tmp = len - read;
+ bufferRead (tmp, buffer + read);
+ read += tmp;
+ if ((read < len))
+ {
+ reg = registerRead (0x19) & 0xF8;
+ while ((reg & 0x08) == 0x08)
+ {
+ reg = registerRead (0x19) & 0xF8;
+ }
+ }
+ }
+ }
+
+ /* OK ! */
+ epilogue ();
+ return 1;
+}
+
+int
+sanei_umax_pp_cmdSync (int cmd)
+{
+ int word[5];
+
+
+ if (sanei_umax_pp_getastra () == 610)
+ return cmdSync610p (cmd);
+
+ /* compute word */
+ word[0] = 0x00;
+ word[1] = 0x00;
+ word[2] = 0x00;
+ word[3] = cmd;
+
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdSync: prologue failed ! (%s:%d)\n", __FILE__, __LINE__);
+ }
+
+ /* send data */
+ if (sendLength (word, 4) == 0)
+ {
+ DBG (0, "sendLength(word,4) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ TRACE (16, "sendLength(word,4) passed ...");
+
+ /* end OK */
+ epilogue ();
+
+ return 1;
+}
+
+
+/* numbers of bytes read, else 0 (failed) */
+/* read data by chunk EXACTLY the width of the scan area in the given */
+/* resolution . If a valid file descriptor is given, we write data */
+/* in it according to the color mode, before polling the scanner */
+/* len should not be bigger than 2 Megs */
+
+int
+cmdGetBlockBuffer (int cmd, int len, int window, unsigned char *buffer)
+{
+#ifdef HAVE_SYS_TIME_H
+ struct timeval td, tf;
+ float elapsed;
+#endif
+ int reg, i;
+ int word[5], read;
+
+ /* compute word */
+ word[0] = len / 65536;
+ word[1] = len / 256 % 256;
+ word[2] = len % 256;
+ word[3] = (cmd & 0x3F) | 0x80 | 0x40;
+
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdGetBlockBuffer: prologue failed ! (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+
+ /* send data */
+ if (sendLength (word, 4) == 0)
+ {
+ DBG (0, "sendLength(word,4) failed (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ TRACE (16, "sendLength(word,4) passed ...");
+ /* head end */
+ epilogue ();
+
+
+
+ if (!prologue (0x10))
+ {
+ DBG (0, "cmdGetBlockBuffer: prologue failed ! (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+
+
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+
+ i = 0;
+
+ /* init counter */
+ read = 0;
+
+ /* read scanner state */
+ reg = registerRead (0x19) & 0xF8;
+
+
+ /* read loop */
+ while (read < len)
+ {
+ /* wait for the data to be ready */
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday (&td, NULL);
+#endif
+ while ((reg & 0x08) == 0x08)
+ {
+ reg = registerRead (0x19) & 0xF8;
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday (&tf, NULL);
+ elapsed =
+ ((tf.tv_sec * 1000000 + tf.tv_usec) -
+ (td.tv_sec * 1000000 + td.tv_usec)) / 1000000;
+ if (elapsed > 3)
+ {
+ DBG
+ (0,
+ "Time-out (%.2f s) waiting for scanner ... giving up on status 0x%02X ! (%s:%d)\n",
+ elapsed, reg, __FILE__, __LINE__);
+ epilogue ();
+ return read;
+ }
+#endif
+ }
+ if ((reg != 0xC0) && (reg != 0xD0) && (reg != 0x00))
+ {
+ DBG (0,
+ "Unexpected status 0x%02X, expected 0xC0 or 0xD0 ! (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Going on...\n");
+ }
+
+ /* signals next chunk */
+ reg = registerRead (0x0C);
+ if (reg != 0x04)
+ {
+ DBG (0,
+ "cmdGetBlockBuffer failed: unexpected value reg0C=0x%02X ...(%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ return 0;
+ }
+ REGISTERWRITE (0x0C, reg | 0x40);
+
+
+ /* there is always a full block ready when scanner is ready */
+ /* 32 bits I/O read , window must match the width of scan */
+ bufferRead (window, buffer + read);
+
+ /* add bytes read */
+ read += window;
+
+
+ DBG (16, "Read %d bytes out of %d (last block is %d bytes) (%s:%d)\n",
+ read, len, window, __FILE__, __LINE__);
+
+ /* test status after read */
+ reg = registerRead (0x19) & 0xF8;
+ }
+
+
+ /* wait for the data to be ready */
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday (&td, NULL);
+#endif
+ while ((reg & 0x08) == 0x08)
+ {
+ reg = registerRead (0x19) & 0xF8;
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday (&tf, NULL);
+ elapsed =
+ ((tf.tv_sec * 1000000 + tf.tv_usec) -
+ (td.tv_sec * 1000000 + td.tv_usec)) / 1000000;
+ if (elapsed > 3)
+ {
+ DBG
+ (0,
+ "Time-out (%.2f s) waiting for scanner ... giving up on status 0x%02X ! (%s:%d)\n",
+ elapsed, reg, __FILE__, __LINE__);
+ epilogue ();
+ return read;
+ }
+#endif
+ }
+ if ((reg != 0xC0) && (reg != 0xD0) && (reg != 0x00))
+ {
+ DBG (0, "Unexpected status 0x%02X, expected 0xC0 or 0xD0 ! (%s:%d)\n",
+ reg, __FILE__, __LINE__);
+ DBG (0, "Going on...\n");
+ }
+
+ REGISTERWRITE (0x0E, 0x0D);
+ REGISTERWRITE (0x0F, 0x00);
+
+
+ /* OK ! */
+ epilogue ();
+ return read;
+}
+
+/*
+ * encodes DC offsets: must be in [0..0x0F] range
+ */
+static void
+encodeDC (int dcRed, int dcGreen, int dcBlue, int *motor)
+{
+ motor[11] = (motor[11] & 0x0F) | dcRed << 4;
+ motor[12] = (motor[12] & 0xC3) | dcGreen << 2;
+ motor[13] = (motor[13] & 0xF0) | dcBlue;
+}
+
+static void
+decodeDC (int *motor)
+{
+ DBG (0, "DC (R,G,B)=(%d,%d,%d)\n",
+ (motor[11] & 0xF0) >> 4, (motor[12] & 0x3C) >> 2, motor[13] & 0x0F);
+}
+
+
+/*
+ * encodes VGA : must be in [0..0x0F] range
+ */
+static void
+encodeVGA (int vgaRed, int vgaGreen, int vgaBlue, int *motor)
+{
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ motor[10] = (vgaRed << 4) | vgaGreen;
+ motor[11] = (motor[11] & 0xF0) | vgaBlue;
+ }
+ else
+ {
+ motor[10] = (vgaGreen << 4) | vgaBlue;
+ motor[11] = (motor[11] & 0xF0) | vgaRed;
+ /* ancien
+ F00: vert
+ 0F0: bleu
+ 00F: rouge
+ motor[10] = (vgaRed << 4) | vgaGreen;
+ motor[11] = (motor[11] & 0xF0) | vgaBlue; */
+ }
+}
+
+static void
+decodeVGA (int *motor)
+{
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ DBG (0, "VGA (R,G,B)=(%d,%d,%d)\n",
+ (motor[10] & 0xF0) >> 4, (motor[10] & 0x0F), (motor[11] & 0x0F));
+ }
+ else
+ {
+ DBG (0, "VGA (R,G,B)=(%d,%d,%d)\n",
+ (motor[11] & 0x0F), (motor[10] & 0xF0) >> 4, (motor[10] & 0x0F));
+ }
+}
+
+/*
+ * this function encodes total head movement which includes
+ * y movement and scan area height
+ * height is scan area height
+ * ypos is head movement before scan
+ * total move will be ypos+height */
+static void
+encodeHY (int height, int ypos, int *motor)
+{
+ motor[0] = height % 256;
+ motor[1] = (height / 256) & 0x3F;
+ motor[1] = motor[1] | (ypos & 0x03) << 6;
+ motor[2] = (ypos >> 2) % 256;
+ motor[3] = (motor[3] & 0xF0) | ((ypos >> 10) & 0x0F);
+}
+
+/*
+ * this function encodes x start and x end on the CCD
+ * w is width of scanning area
+ * x is start of scanning area
+ * dpi is x resolution
+ * color is non zero if scanning in color
+ * bytes is on aoverride for bpl, since it sin't clear today when
+ * the formula has to be applied
+ */
+static void
+encodeWX (int width, int xstart, int dpi, int color, int *ccd, int bytes)
+{
+ int xend;
+ int bpl;
+ int x;
+
+ xend = xstart + width;
+ x = xstart - 1;
+
+ /* x start encoding */
+ ccd[17] = x % 256;
+ ccd[18] = (ccd[18] & 0xF0) | ((x / 256) & 0x0F);
+ /* models >=1220P have a 600 dpi CCD: x is bigger */
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ if (x > 0x1000)
+ ccd[33] |= 0x40;
+ else
+ ccd[33] &= 0xBF;
+ }
+
+ /* x end encoding */
+ ccd[18] = (ccd[18] & 0x0F) | ((xend % 16) << 4);
+ ccd[19] = (xend / 16) % 256;
+ /* models >=1220P have a 600 dpi CCD: x is bigger */
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ if (xend > 0x1000)
+ ccd[33] |= 0x80;
+ else
+ ccd[33] &= 0x7F;
+ }
+
+ /* now bytes per line */
+ /* bpl = (op[24] - 0x41) * 256 + 8192 * (op[34] & 0x01) + op[23]; */
+ bpl = (color == 0 ? 1 : 3) * width * dpi;
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ bpl /= 600;
+ if (bpl >= 8192)
+ ccd[34] |= 0x01;
+ else
+ ccd[34] &= 0xFE;
+ }
+ else
+ {
+ bpl /= 300;
+ }
+ if (bytes > 0)
+ bpl = bytes;
+ ccd[23] = bpl % 256;
+ ccd[24] = 0x41 + ((bpl / 256) & 0x1F);
+}
+
+ /* cropping coefficient: last 2 bytes gives the coefficient applied */
+ /* to data scanned to get the actual image resolution */
+static void
+encodeCoefficient (int color, int dpi, int *calibration)
+{
+ int w, idx = 0;
+ int *coeff;
+
+ /* 75, 150, 300, 600 and 1200 dpi */
+ int color610p[4][2] =
+ { {0x88, 0x88}, {0xAA, 0xAA}, {0xFF, 0xFF}, {0xFF, 0xFF} };
+ int gray610p[4][2] =
+ { {0x88, 0x01}, {0xAA, 0x11}, {0xFF, 0xAA}, {0xFF, 0xFF} };
+
+ /* FF means coeff=1
+ * AA coeff=1/2
+ * 88 coeff=1/4
+ * 80 coeff=1/8
+ * first coeff for CCD (x)
+ * second coeff for motor steps (y)
+ */
+ int color1220p[5][2] =
+ { {0x80, 0xAA}, {0x88, 0xFF}, {0xAA, 0xFF}, {0xFF, 0xFF}, {0xFF, 0xFF} };
+ int gray1220p[5][2] =
+ { {0x80, 0x88}, {0x88, 0xAA}, {0xAA, 0xFF}, {0xFF, 0xFF}, {0xFF, 0xFF} };
+
+ switch (dpi)
+ {
+ case 1200:
+ idx = 4;
+ break;
+ case 600:
+ idx = 3;
+ break;
+ case 300:
+ idx = 2;
+ break;
+ case 150:
+ idx = 1;
+ break;
+ case 75:
+ idx = 0;
+ break;
+ }
+
+ if (sanei_umax_pp_getastra () < 1210)
+ {
+ w = 2550;
+ if (color >= RGB_MODE)
+ coeff = color610p[idx];
+ else
+ coeff = gray610p[idx];
+ }
+ else
+ {
+ w = 5100;
+ if (color >= RGB_MODE)
+ coeff = color1220p[idx];
+ else
+ coeff = gray1220p[idx];
+ }
+
+ /* x coefficient */
+ calibration[3 * w + 768] = coeff[0];
+
+ /* y coefficient */
+ calibration[3 * w + 769] = coeff[1];
+}
+
+
+static void
+bloc2Decode (int *op)
+{
+ int i;
+ int scanh;
+ int skiph;
+ int dpi = 0;
+ int dir = 0;
+ int color = 0;
+ char str[64];
+
+ for (i = 0; i < 16; i++)
+ sprintf (str + 3 * i, "%02X ", (unsigned char) op[i]);
+ str[48] = 0x00;
+ DBG (0, "Command bloc 2: %s\n", str);
+
+
+ scanh = op[0] + (op[1] & 0x3F) * 256;
+ skiph = ((op[1] & 0xC0) >> 6) + (op[2] << 2) + ((op[3] & 0x0F) << 10);
+
+ if (op[3] & 0x10)
+ dir = 1;
+ else
+ dir = 0;
+
+ /* XXX STEF XXX seems to conflict with DC definitions */
+ if (op[13] & 0x40)
+ color = 1;
+ else
+ color = 0;
+
+ /* op[6]=0x60 at 600 and 1200 dpi */
+ if ((op[8] == 0x17) && (op[9] != 0x05))
+ dpi = 150;
+ if ((op[8] == 0x17) && (op[9] == 0x05))
+ dpi = 300;
+ if ((op[9] == 0x05) && (op[14] & 0x08))
+ dpi = 1200;
+ if ((dpi == 0) && ((op[14] & 0x08) == 0))
+ dpi = 600;
+
+
+
+ DBG (0, "\t->scan height =0x%04X (%d)\n", scanh, scanh);
+ DBG (0, "\t->skip height =0x%04X (%d)\n", skiph, skiph);
+ DBG (0, "\t->y dpi =0x%04X (%d)\n", dpi, dpi);
+ decodeVGA (op);
+ decodeDC (op);
+ if (dir)
+ DBG (0, "\t->forward direction\n");
+ else
+ DBG (0, "\t->reverse direction\n");
+ if (color)
+ DBG (0, "\t->color scan \n");
+ else
+ DBG (0, "\t->no color scan \n");
+
+ /* byte 14 */
+ if (op[14] & 0x20)
+ {
+ DBG (0, "\t->lamp on \n");
+ }
+ else
+ {
+ DBG (0, "\t->lamp off \n");
+ }
+ if (op[14] & 0x04)
+ {
+ DBG (0, "\t->normal scan (head stops at each row) \n");
+ }
+ else
+ {
+ DBG (0, "\t->move and scan (head doesn't stop at each row) \n");
+ }
+ DBG (0, "\n");
+}
+
+
+static void
+bloc8Decode (int *op)
+{
+ int i, bpl;
+ int xskip;
+ int xend, len;
+ char str[128];
+
+ if (sanei_umax_pp_getastra () < 1220)
+ len = 34;
+ else
+ len = 36;
+ for (i = 0; i < len; i++)
+ sprintf (str + 3 * i, "%02X ", (unsigned char) op[i]);
+ str[3 * i] = 0x00;
+ DBG (0, "Command bloc 8: %s\n", str);
+
+ xskip = op[17] + 256 * (op[18] & 0x0F);
+ if (op[33] & 0x40)
+ xskip += 0x1000;
+ xend = (op[18] & 0xF0) / 16 + 16 * op[19];
+ if (op[33] & 0x80)
+ xend += 0x1000;
+ if (len > 34)
+ bpl = (op[24] - 0x41) * 256 + 8192 * (op[34] & 0x01) + op[23];
+ else
+ bpl = (op[24] - 0x41) * 256 + op[23];
+
+ DBG (0, "\t->xskip =0x%X (%d)\n", xskip, xskip);
+ DBG (0, "\t->xend =0x%X (%d)\n", xend, xend);
+ DBG (0, "\t->scan width=0x%X (%d)\n", xend - xskip - 1, xend - xskip - 1);
+ DBG (0, "\t->bytes/line=0x%X (%d)\n", bpl, bpl);
+ DBG (0, "\t->raw =0x%X (%d)\n", op[24] * 256 + op[23],
+ op[24] * 256 + op[23]);
+ DBG (0, "\n");
+}
+static int
+completionWait (void)
+{
+ /* for 610P, wait and sync is done while
+ * reading data from the scanner */
+ CMDSYNC (0x40);
+ usleep (100000);
+ CMDSYNC (0xC2);
+ if ((sanei_umax_pp_getastra () == 610)
+ || ((sanei_umax_pp_scannerStatus () & 0x90) == 0x90))
+ return 1;
+ do
+ {
+ usleep (100000);
+ CMDSYNC (0xC2);
+ }
+ while ((sanei_umax_pp_scannerStatus () & 0x90) != 0x90);
+ CMDSYNC (0xC2);
+ return 1;
+}
+
+int
+sanei_umax_pp_setLamp (int on)
+{
+ int buffer[17];
+ int state;
+
+ /* reset? */
+ sanei_umax_pp_cmdSync (0x00);
+ sanei_umax_pp_cmdSync (0xC2);
+ sanei_umax_pp_cmdSync (0x00);
+
+ /* get status */
+ cmdGet (0x02, 16, buffer);
+ state = buffer[14] & LAMP_STATE;
+ buffer[16] = -1;
+ if ((state == 0) && (on == 0))
+ {
+ DBG (0, "Lamp already off ... (%s:%d)\n", __FILE__, __LINE__);
+ return 1;
+ }
+ if ((state) && (on))
+ {
+ DBG (2, "Lamp already on ... (%s:%d)\n", __FILE__, __LINE__);
+ return 1;
+ }
+
+ /* set lamp state */
+ if (on)
+ buffer[14] = buffer[14] | LAMP_STATE;
+ else
+ buffer[14] = buffer[14] & ~LAMP_STATE;
+ CMDSETGET (0x02, 16, buffer);
+ TRACE (16, "setLamp passed ...");
+ return 1;
+}
+
+static int num = 0;
+static void
+Dump (int len, unsigned char *data, char *name)
+{
+ FILE *fic;
+ char titre[80];
+
+ if (name == NULL)
+ {
+ sprintf (titre, "dump%04d.bin", num);
+ num++;
+ }
+ else
+ {
+ sprintf (titre, "%s", name);
+ }
+ fic = fopen (titre, "wb");
+ if (fic == NULL)
+ {
+ DBG (0, "could not open %s for writing\n", titre);
+ return;
+ }
+ fwrite (data, 1, len, fic);
+ fclose (fic);
+}
+
+
+static void
+DumpNB (int width, int height, unsigned char *data, char *name)
+{
+ FILE *fic;
+ char titre[80];
+
+ if (name == NULL)
+ {
+ sprintf (titre, "dump%04d.pnm", num);
+ num++;
+ }
+ else
+ {
+ sprintf (titre, "%s", name);
+ }
+ fic = fopen (titre, "wb");
+ if (fic == NULL)
+ {
+ DBG (0, "could not open %s for writing\n", titre);
+ return;
+ }
+ fprintf (fic, "P5\n%d %d\n255\n", width, height);
+ fwrite (data, width, height, fic);
+ fclose (fic);
+}
+
+
+/* dump data has received from scanner (red line/green line/blue line)
+ to a color pnm file */
+static void
+DumpRVB (int width, int height, unsigned char *data, char *name)
+{
+ FILE *fic;
+ char titre[80];
+ int y, x;
+
+ if (name == NULL)
+ {
+ sprintf (titre, "dump%04d.pnm", num);
+ num++;
+ }
+ else
+ {
+ sprintf (titre, "%s", name);
+ }
+ fic = fopen (titre, "wb");
+ if (fic == NULL)
+ {
+ DBG (0, "could not open %s for writing\n", titre);
+ return;
+ }
+ fprintf (fic, "P6\n%d %d\n255\n", width, height);
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ fputc (data[3 * y * width + 2 * width + x], fic);
+ fputc (data[3 * y * width + width + x], fic);
+ fputc (data[3 * y * width + x], fic);
+ }
+ }
+ fclose (fic);
+}
+
+/* dump a color buffer in a color PNM */
+static void
+DumpRGB (int width, int height, unsigned char *data, char *name)
+{
+ FILE *fic;
+ char titre[80];
+ int y, x;
+
+ if (name == NULL)
+ {
+ sprintf (titre, "dump%04d.pnm", num);
+ num++;
+ }
+ else
+ {
+ sprintf (titre, "%s", name);
+ }
+ fic = fopen (titre, "wb");
+ fprintf (fic, "P6\n%d %d\n255\n", width, height);
+ if (fic == NULL)
+ {
+ DBG (0, "could not open %s for writing\n", titre);
+ return;
+ }
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ fputc (data[3 * y * width + x * 3], fic);
+ fputc (data[3 * y * width + x * 3 + 1], fic);
+ fputc (data[3 * y * width + x * 3 + 2], fic);
+ }
+ }
+ fclose (fic);
+}
+
+static int
+evalGain (int sum, int count)
+{
+ int gn;
+ float pct;
+ float avg;
+ float area=50;
+ float coeff=2.5;
+ float cnst=0.9;
+
+
+ /* after ~ 60 * 10 scans , it looks like 1 step is a 0.57% increase */
+ /* so we take the value and compute the percent increase to reach 250 */
+ /* (target code) not 255, because we want some room for inaccuracy */
+ /* pct=100-(value*100)/250 */
+ /* then correction is pct/0.57 */
+ avg = (float) (sum) / (float) (count);
+ pct = 100.0 - (avg * 100.0) / targetCode;
+ gn = (int) (pct / 0.57);
+
+ /* give gain for dark areas a boost */
+#ifdef UMAX_PP_DANGEROUS_EXPERIMENT
+ if(getenv("AREA")!=NULL)
+ cnst=atol(getenv("AREA"));
+ if(getenv("COEFF")!=NULL)
+ cnst=atol(getenv("COEFF"));
+ if(getenv("CNST")!=NULL)
+ cnst=atol(getenv("CNST"));
+#endif
+
+ pct = gn;
+ avg = exp((-pct)/area)*coeff+cnst;
+ gn = gn * avg;
+
+ /* bound checking : there are sightings of >127 values being negative */
+ if (gn < 0)
+ gn = 0;
+ else if (gn > 127)
+ gn = 127;
+ return gn;
+}
+
+static void
+computeCalibrationData (int color, int width, unsigned char *source,
+ int *data)
+{
+ int p, i, l;
+ int sum;
+
+
+ memset (data, 0, (3 * 5100 + 768 + 3) * sizeof (int));
+
+
+ /* 0 -> 5099 */
+ for (i = 0; i < width; i++)
+ { /* red calibration data */
+ if (color >= RGB_MODE)
+ {
+ /* compute average */
+ sum = 0;
+ for (l = 0; l < 66; l++)
+ sum += source[i + l * 5100 * 3];
+ data[i] = evalGain (sum, l);
+ }
+ else
+ data[i] = 0x00;
+ }
+
+
+ /* 5100 -> 10199: green data */
+ p = 5100;
+ for (i = 0; i < width; i++)
+ {
+ /* compute average */
+ sum = 0;
+ for (l = 0; l < 66; l++)
+ sum += source[i + l * 5100 * 3 + 5100];
+ data[p + i] = evalGain (sum, l);
+ }
+
+
+ /* 10200 -> 15299: blue */
+ p = 10200;
+ for (i = 0; i < width; i++)
+ {
+ if (color >= RGB_MODE)
+ {
+ /* compute average */
+ sum = 0;
+ for (l = 0; l < 66; l++)
+ sum += source[i + l * 5100 * 3 + 5100 * 2];
+ data[p + i] = evalGain (sum, l);
+ }
+ else
+ data[p + i] = 0x00;
+ }
+
+
+ /* gamma tables */
+ for (i = 0; i < 256; i++)
+ data[15300 + i] = ggRed[i];
+ for (i = 0; i < 256; i++)
+ data[15300 + 256 + i] = ggGreen[i];
+ for (i = 0; i < 256; i++)
+ data[15300 + 512 + i] = ggBlue[i];
+ data[16070] = -1;
+}
+
+
+
+/* move head by the distance given using precision or not */
+/* 0: failed
+ 1: success */
+static int
+move (int distance, int precision, unsigned char *buffer)
+{
+ int header[17] = {
+ 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x2F,
+ 0x2F, 0x01, 0x00, 0x00, 0x00, 0x80, 0xA4, 0x00,
+ -1
+ };
+ int body[37] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x6E, 0xF6, 0x79, 0xBF, 0x01, 0x00, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+ int end[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, -1 };
+ int steps, len, cmdlen;
+ unsigned char tmp[0x200];
+ unsigned char *ptr;
+
+ if (distance == 0)
+ return 0;
+
+ if (buffer == NULL)
+ ptr = tmp;
+ else
+ ptr = buffer;
+
+ /* build commands */
+ if (distance < 0)
+ {
+ /* header */
+ steps = -distance - 1;
+ header[3] = 0x20;
+ header[9] = 0x01;
+
+ /* reverse direction body by default */
+
+ /* end */
+ end[1] = 0xFF;
+ end[2] = 0xFF;
+ end[3] = -1;
+ len = 3;
+ }
+ else
+ {
+ /* header */
+ steps = distance - 1;
+ header[3] = 0x70;
+ header[9] = 0x05;
+
+ /* body */
+ body[2] = 0x04;
+ body[4] = 0x02;
+ body[7] = 0x0C;
+ body[9] = 0x04;
+ body[10] = 0x40;
+ body[11] = 0x01;
+ /* end */
+ len = 8;
+ }
+ if (steps > 0)
+ {
+ encodeHY (1, steps, header);
+ }
+
+
+ if (sanei_umax_pp_getastra () < 1220)
+ {
+ header[6] = 0xC0;
+
+ body[16] = 0x76; /* encodeWX */
+ body[17] = 0x00;
+ body[18] = 0x15;
+ body[19] = 0x70;
+ body[20] = 0x01;
+ body[21] = 0x00;
+
+ body[28] = 0x4D;
+ body[29] = 0x4B;
+ body[30] = 0xD0;
+
+ cmdlen = 0x22;
+ }
+ else
+ cmdlen = 0x24;
+
+ /* precision: default header set to precision on */
+ if (precision == PRECISION_OFF)
+ {
+ if (sanei_umax_pp_getastra () == 1600)
+ header[8] = 0x15;
+ else
+ header[8] = 0x17;
+ if (sanei_umax_pp_getastra () > 610)
+ header[14] = 0xAC;
+ body[20] = 0x06;
+ }
+ CMDSETGET (0x02, 16, header);
+ CMDSETGET (0x08, cmdlen, body);
+ if (DBG_LEVEL >= 128)
+ {
+ bloc2Decode (header);
+ bloc8Decode (body);
+ }
+ CMDSYNC (0xC2);
+ if ((sanei_umax_pp_scannerStatus () & 0x80)
+ || (sanei_umax_pp_getastra () < 1220))
+ {
+ CMDSYNC (0x00);
+ }
+ CMDSETGET (4, len, end);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x200, ptr);
+ if (DBG_LEVEL >= 128)
+ {
+ Dump (0x200, ptr, NULL);
+ }
+ DBG (16, "MOVE STATUS IS 0x%02X (%s:%d)\n", sanei_umax_pp_scannerStatus (),
+ __FILE__, __LINE__);
+ CMDSYNC (0x00);
+ return 1;
+}
+
+
+
+/* for each column, finds the row where white/black transition occurs
+ then returns the average */
+static float
+edgePosition (int width, int height, unsigned char *data)
+{
+ int ecnt, x, y;
+ float epos;
+ int d, dmax, dpos, i;
+ unsigned char *dbuffer = NULL;
+
+ if (DBG_LEVEL > 128)
+ {
+ dbuffer = (unsigned char *) malloc (3 * width * height);
+ memset (dbuffer, 0x00, 3 * width * height);
+ }
+ epos = 0;
+ ecnt = 0;
+ for (x = 0; x < width; x++)
+ {
+ /* edge: white->black drop */
+ /* loop stops on black area */
+ dmax = 0;
+ dpos = 0;
+ d = 0;
+ i = 0;
+ for (y = 10; (y < height) && (data[i] > 10); y++)
+ {
+ i = x + y * width;
+ d = data[i - width] - data[i];
+ if (d > dmax)
+ {
+ dmax = d;
+ dpos = y;
+ }
+ if ((DBG_LEVEL > 128) && (dbuffer != NULL))
+ {
+ dbuffer[i * 3] = data[i];
+ dbuffer[i * 3 + 1] = data[i];
+ dbuffer[i * 3 + 2] = data[i];
+ }
+ }
+ epos += dpos;
+ ecnt++;
+ if ((DBG_LEVEL > 128) && (dbuffer != NULL))
+ {
+ dbuffer[(x + dpos * width) * 3] = 0xFF;
+ dbuffer[(x + dpos * width) * 3 + 1] = 0x00;
+ dbuffer[(x + dpos * width) * 3 + 2] = 0x00;
+ }
+ }
+ if (ecnt == 0)
+ epos = 70;
+ else
+ epos = epos / ecnt;
+ if ((DBG_LEVEL > 128) && (dbuffer != NULL))
+ {
+ i = ((int) epos) * width;
+ for (x = 0; x < width; x++)
+ {
+ dbuffer[(x + i) * 3] = 0x00;
+ dbuffer[(x + i) * 3 + 1] = 0xFF;
+ dbuffer[(x + i) * 3 + 2] = 0xFF;
+ }
+ for (y = 0; y < height; y++)
+ {
+ dbuffer[(width / 2 + y * width) * 3] = 0x00;
+ dbuffer[(width / 2 + y * width) * 3 + 1] = 0xFF;
+ dbuffer[(width / 2 + y * width) * 3 + 2] = 0x00;
+ }
+ DumpRGB (width, height, dbuffer, NULL);
+ free (dbuffer);
+ }
+ return epos;
+}
+
+
+
+static int
+moveToOrigin (void)
+{
+ unsigned char buffer[54000];
+ float edge;
+ int val, delta = 188;
+ int header[17] = {
+ 0xB4, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2F,
+ 0x2F, 0x05, 0x00, 0x00, 0x00, 0x80, 0xA4, 0x00, -1
+ };
+
+ int body[37] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x04, 0x40, 0x01, 0x00, 0x00, 0x04, 0x00,
+ 0x6E, 0xFB, 0xC4, 0xE5, 0x06, 0x00, 0x00, 0x60,
+ 0x4D, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00, -1
+ };
+
+ int end[9] = {
+ 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x08, 0x00,
+ -1
+ };
+ int opsc03[9] = {
+ 0x00, 0x00, 0x00, 0xAA, 0xCC, 0xEE, 0x80, 0xFF,
+ -1
+ };
+ int w = 300, h = 180, len = 36;
+
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 1600:
+ header[8] = 0x2B;
+
+ body[29] = 0x1A;
+ body[30] = 0xEE;
+
+ end[0] = 0x19;
+ end[1] = 0xD5;
+ end[4] = 0x1B;
+
+ case 1220:
+ case 2000:
+ w = 300;
+ h = 180;
+ len = 36;
+ delta = -188;
+ CMDSYNC (0x00);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ MOVE (196, PRECISION_OFF, NULL);
+ break;
+
+ case 610:
+
+ w = 512;
+ h = 90;
+ len = 34;
+ delta = -81;
+
+ opsc03[6] = 0xFF; /* instead of 0x80 */
+
+ encodeHY (h, 60, header);
+
+ /* will add encodeDpi(dpi,cmd) */
+ header[6] = 0xC0;
+ header[8] = 0x17;
+
+ body[13] = 0x20;
+ body[14] = 0x02;
+
+ body[16] = 0x76;
+
+ encodeWX (0x200, 0x501, 300, 0, body, 0x500);
+
+ /* fixed values for all 610p commands */
+ body[28] = 0x4D;
+ body[29] = 0x4B;
+ body[30] = 0xD0;
+
+ /* LM9811 command block ? */
+ end[0] = 0x88;
+ end[1] = 0xE6;
+ end[2] = 0xFD;
+ end[3] = 0x8E;
+ end[4] = 0x30;
+
+ break;
+ }
+
+
+ /* scan an area where is a white and a black regions, which */
+ /* can be detected and gives this offset to the origin of the */
+ /* scanning windows */
+ CMDSETGET (2, 0x10, header);
+ CMDSETGET (8, len, body);
+ CMDSETGET (1, 0x08, end);
+
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ /* signals black & white ? */
+ CMDSETGET (4, 8, opsc03);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w * h, buffer); /* get find position data */
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, h, buffer, NULL);
+ }
+
+ /* detection of 1600P is a by product of origin finding */
+ edge = 0.0;
+ for (val = 0; val < w * h; val++)
+ if (buffer[val] > edge)
+ edge = buffer[val];
+ DBG (32, "MAX VALUE=%f (%s:%d)\n", edge, __FILE__, __LINE__);
+ if ((edge <= 30) && (sanei_umax_pp_getastra () != 1600))
+ {
+ DBG (2, "moveToOrigin() detected a 1600P");
+ sanei_umax_pp_setastra (1600);
+ }
+ edge = edgePosition (w, h, buffer);
+ /* rounded to lowest integer, since upping origin might lead */
+ /* to bump in the other side if doing a full size preview */
+ val = (int) (edge);
+
+ delta += val;
+ DBG (64, "Edge=%f, val=%d, delta=%d\n", edge, val, delta);
+
+ /* move back to y-coordinate origin */
+ if (sanei_umax_pp_getastra () < 1220)
+ {
+ MOVE (delta, PRECISION_OFF, NULL);
+ }
+ else
+ {
+ MOVE (delta, PRECISION_ON, NULL);
+ }
+
+ /* head successfully set to the origin */
+ return 1;
+}
+
+
+/* park head: returns 1 on success, 0 otherwise */
+int
+sanei_umax_pp_park (void)
+{
+ int header610[17] = {
+ 0x01, 0x00, 0x01, 0x40, 0x30, 0x00, 0xC0, 0x2F, 0x17, 0x05, 0x00, 0x00,
+ 0x00, 0x80, 0xF4, 0x00, -1
+ };
+ int body610[35] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x03, 0xC1, 0x80,
+ 0x00, 0x20, 0x02, 0x00, 0x16, 0x80, 0x15, 0x78, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68, 0xDF, 0x1B, -1
+ };
+
+ int header[17] =
+ { 0x01, 0x00, 0x01, 0x70, 0x00, 0x00, 0x60, 0x2F, 0x13, 0x05, 0x00, 0x00,
+ 0x00, 0x80, 0xF0, 0x00, -1
+ };
+ int body[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x03, 0xC1, 0x80,
+ 0x00, 0x00, 0x04, 0x00, 0x16, 0x80, 0x15, 0x78, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x1B, 0x1A, 0x00,
+ -1
+ };
+
+ int status = 0x90;
+
+ CMDSYNC (0x00);
+
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ CMDSETGET (0x02, 0x10, header);
+ CMDSETGET (0x08, 0x24, body);
+ }
+ else
+ {
+ CMDSETGET (0x02, 0x10, header610);
+ CMDSETGET (0x08, 0x22, body610);
+ }
+ CMDSYNC (0x40);
+
+
+ status = sanei_umax_pp_scannerStatus ();
+ DBG (16, "PARKING STATUS is 0x%02X (%s:%d)\n", status, __FILE__, __LINE__);
+ DBG (1, "Park command issued ...\n");
+ return 1;
+}
+
+
+/* calibrates CCD: returns 1 on success, 0 on failure */
+static int
+shadingCalibration1220p (int color,
+ int dcRed, int dcGreen, int dcBlue,
+ int vgaRed, int vgaGreen, int vgaBlue,
+ int *calibration)
+{
+ int opsc32[17] =
+ { 0x4A, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x17, 0x05, 0xA5, 0x08,
+ 0x00, 0x00, 0xAC, 0x00, -1
+ };
+ int opsc41[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x40, 0x01,
+ 0x00, 0x00, 0x04, 0x00, 0x6E, 0x90, 0xD0, 0x47, 0x06, 0x00, 0x00, 0xC4,
+ 0x5C, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x93, 0x1B, 0x00,
+ -1
+ };
+ int opscnb[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x40, 0x01,
+ 0x00, 0x00, 0x04, 0x00, 0x6E, 0x90, 0xD0, 0x47, 0x06, 0x00, 0x00, 0xEC,
+ 0x54, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x93, 0x1A, 0x00,
+ -1
+ };
+ int opsc04[9] = { 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x00, 0x00, -1 };
+ int commit[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, -1 };
+ int size;
+ int width = 5100; /* full usable CCD width */
+ unsigned char buffer[0x105798];
+
+ /* 1600P have a different CCD command block */
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ opsc04[0] = 0x19;
+ opsc04[1] = 0xD5;
+ opsc04[4] = 0x1B;
+
+ opsc41[29] = 0x1A;
+ opsc41[30] = 0xEE;
+ }
+
+ /* step back by 67 ticks: */
+ /* since we're going to scan 66 lines of data */
+ /* which are going to be used as calibration */
+ /* data */
+ /* we are on the white area just before */
+ /* the user scan area */
+ MOVE (-67, PRECISION_ON, NULL);
+
+
+ CMDSYNC (0x00);
+
+ /* get calibration data */
+ /*
+ if (sanei_umax_pp_getauto ())
+ { auto settings doesn't use offset
+ offset = 0x000;
+ }
+ else
+ { manual settings
+ gain = 0x777;
+ offset = 0x000;
+ }
+ */
+ encodeDC (dcRed, dcGreen, dcBlue, opsc32);
+ encodeVGA (vgaRed, vgaGreen, vgaBlue, opsc32);
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ opsc32[13] = 0x03;
+ }
+
+ /* 1220P/2000P shading calibration */
+ if (color < RGB_MODE)
+ {
+ opsc32[0] -= 4;
+ opsc32[13] = 0xC0;
+ }
+ CMDSETGET (2, 0x10, opsc32);
+ if (DBG_LEVEL >= 64)
+ {
+ bloc2Decode (opsc32);
+ }
+ if (color < RGB_MODE)
+ {
+ CMDSETGET (8, 0x24, opscnb);
+ if (DBG_LEVEL >= 64)
+ {
+ bloc8Decode (opscnb);
+ }
+ opsc04[6] = 0x85;
+ }
+ else
+ {
+ CMDSETGET (8, 0x24, opsc41);
+ if (DBG_LEVEL >= 64)
+ {
+ bloc8Decode (opsc41);
+ }
+ opsc04[6] = 0x0F;
+ if (sanei_umax_pp_getastra () == 1600)
+ opsc04[7] = 0xC0;
+ else
+ opsc04[7] = 0x70;
+ }
+
+ /* BUG BW noisy here */
+ CMDSETGET (1, 0x08, opsc04);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit); /* opsc03 hangs it */
+ COMPLETIONWAIT;
+
+ opsc04[0] = 0x06;
+ if (color >= RGB_MODE)
+ size = 3 * width * 70;
+ else
+ size = width * 66;
+ if (getEPPMode () == 32)
+ {
+ cmdGetBuffer32 (4, size, buffer);
+ }
+ else
+ {
+ CMDGETBUF (4, size, buffer);
+ }
+ if (DBG_LEVEL >= 128)
+ {
+ Dump (size, buffer, NULL);
+ if (color >= RGB_MODE)
+ {
+ DumpRVB (width, 66, buffer, NULL);
+ }
+ else
+ {
+ DumpNB (width, 66, buffer, NULL);
+ }
+ }
+ computeCalibrationData (color, width, buffer, calibration);
+
+ DBG (1, "shadingCalibration1220p() done ...\n");
+ return 1;
+}
+
+
+/* returns number of bytes read or 0 on failure */
+int
+sanei_umax_pp_readBlock (long len, int window, int dpi, int last,
+ unsigned char *buffer)
+{
+ DBG (8, "readBlock(%ld,%d,%d,%d)\n", len, window, dpi, last);
+ /* EPP block reading is available only when dpi >=600 */
+ if ((dpi >= 600)
+ && (gMode != UMAX_PP_PARPORT_ECP) && (sanei_umax_pp_getastra () > 610))
+ {
+ DBG (8, "cmdGetBlockBuffer(4,%ld,%d);\n", len, window);
+ len = cmdGetBlockBuffer (4, len, window, buffer);
+ if (len == 0)
+ {
+ DBG (0, "cmdGetBlockBuffer(4,%ld,%d) failed (%s:%d)\n", len, window,
+ __FILE__, __LINE__);
+ gCancel = 1;
+ }
+ }
+ else
+ {
+ if ((sanei_umax_pp_getastra () < 1210) && (len > 0xFDCE))
+ {
+ len = 0xFDCE;
+ last = 0;
+ }
+ DBG (8, "cmdGetBuffer(4,%ld);\n", len);
+ if (cmdGetBuffer (4, len, buffer) != 1)
+ {
+ DBG (0, "cmdGetBuffer(4,%ld) failed (%s:%d)\n", len, __FILE__,
+ __LINE__);
+ gCancel = 1;
+ }
+ }
+ if (!last)
+ {
+ /* sync with scanner */
+ if (sanei_umax_pp_cmdSync (0xC2) == 0)
+ {
+ DBG (0, "Warning cmdSync(0xC2) failed! (%s:%d)\n", __FILE__,
+ __LINE__);
+ DBG (0, "Trying again ...\n");
+ if (sanei_umax_pp_cmdSync (0xC2) == 0)
+ {
+ DBG (0, " failed again! (%s:%d)\n", __FILE__, __LINE__);
+ DBG (0, "Aborting ...\n");
+ gCancel = 1;
+ }
+ else
+ DBG (0, " success ...\n");
+ }
+ }
+ return len;
+}
+
+int
+sanei_umax_pp_scan (int x, int y, int width, int height, int dpi, int color,
+ int gain, int offset)
+{
+#ifdef HAVE_SYS_TIME_H
+ struct timeval td, tf;
+ float elapsed;
+#endif
+ unsigned char *buffer;
+ long int somme, len, read, blocksize;
+ FILE *fout = NULL;
+ int *dest = NULL;
+ int bpl, hp;
+ int th, tw, bpp;
+ int nb;
+ int bx, by, delta;
+ int reserve, rc, remain, dataoffset;
+
+ if (gain != 0 || offset != 0)
+ sanei_umax_pp_setauto (0);
+
+
+ /* colors don't come in sync, so we must increase y */
+ /* to have extra lines to reorder datas */
+ if (sanei_umax_pp_getastra () > 610)
+ {
+ switch (dpi)
+ {
+ case 1200:
+ delta = 8;
+ break;
+ case 600:
+ delta = 4;
+ break;
+ case 300:
+ delta = 2;
+ break;
+ case 150:
+ delta = 1;
+ break;
+ default:
+ delta = 0;
+ break;
+ }
+ }
+ else
+ {
+ if (color >= RGB_MODE)
+ {
+ switch (dpi)
+ {
+ case 600:
+ delta = 16;
+ break;
+ case 300:
+ delta = 8;
+ break;
+ case 150:
+ delta = 4;
+ break;
+ default:
+ delta = 2;
+ break;
+ }
+ }
+ else
+ delta = 0;
+ }
+
+ /* in color mode, we need extra lines to reorder data */
+ if (color >= RGB_MODE)
+ {
+ if (sanei_umax_pp_getastra () <= 610)
+ dataoffset = 4 * delta;
+ else
+ dataoffset = 2 * delta;
+ }
+ else
+ dataoffset = 0;
+
+ rc = sanei_umax_pp_startScan
+ (x, y - dataoffset, width, height + dataoffset, dpi, color, gain,
+ offset, &bpp, &tw, &th);
+ if (rc == 1)
+ {
+ /* blocksize must be multiple of the number of bytes per line */
+ /* max is 2096100 */
+ /* so blocksize will hold a round number of lines, easing the */
+ /* write data to file operation */
+ bpl = bpp * tw;
+ hp = 2096100 / bpl;
+ blocksize = hp * bpl;
+ nb = 0;
+ somme = bpl * th;
+ DBG (8, "Getting buffer %d*%d*%d=%ld=0x%lX (%s:%d) \n", bpp, tw, th,
+ somme, somme, __FILE__, __LINE__);
+
+ /* correct th to be usable scan height */
+ th -= dataoffset;
+
+ /* we need a 2 * delta lines reserve to reorder data */
+ if (color >= RGB_MODE)
+ {
+ reserve = 2 * delta * bpl;
+ if (sanei_umax_pp_getastra () < 1210)
+ dataoffset = reserve;
+ else
+ dataoffset = 0;
+ }
+ else
+ {
+ reserve = 0;
+ dataoffset = 0;
+ }
+
+ /* get scanned data */
+
+ /* allocate memory */
+ buffer = (unsigned char *) malloc (blocksize + reserve);
+ if (buffer == NULL)
+ {
+ DBG (0, "Failed to allocate %ld bytes, giving up....\n",
+ blocksize + reserve);
+ DBG (0, "Try to scan at lower resolution, or a smaller area.\n");
+ gCancel = 1;
+ }
+
+ /* open output file */
+ fout = fopen ("out.pnm", "wb");
+ if (fout == NULL)
+ {
+ DBG (0, "Failed to open 'out.pnm', giving up....\n");
+ gCancel = 1;
+ }
+ else
+ {
+ /* write pnm header */
+ if (color >= RGB_MODE)
+ fprintf (fout, "P6\n%d %d\n255\n", tw, th - 2 * delta);
+ else
+ fprintf (fout, "P5\n%d %d\n255\n", tw, th);
+ }
+
+ /* read some line first until we got clean data */
+ read = 0;
+ remain = 0;
+ while (read < dataoffset)
+ {
+ if (read == 0)
+ len = dataoffset;
+ else
+ len = dataoffset - read;
+ len = sanei_umax_pp_readBlock (len, tw, dpi, 0, buffer + read);
+ if (len == 0)
+ {
+ DBG (0,
+ "sanei_umax_pp_readBlock failed, cancelling scan ...\n");
+ gCancel = 1;
+ }
+ read += len;
+ }
+
+ /* in color mode we have to fill the 'reserve' area
+ * so that we can reorder data lines */
+ while ((read - dataoffset < reserve) && (!gCancel))
+ {
+ len = reserve - read + dataoffset;
+ len =
+ sanei_umax_pp_readBlock (len, tw, dpi, 0,
+ buffer + read - dataoffset);
+ if (len == 0)
+ {
+ DBG (0,
+ "sanei_umax_pp_readBlock failed, cancelling scan ...\n");
+ gCancel = 1;
+ }
+ read += len;
+ }
+
+ /* data reading loop */
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday (&td, NULL);
+#endif
+ while ((read < somme) && (!gCancel))
+ {
+ /* 2096100 max */
+ if (somme - read > blocksize - remain)
+ len = blocksize - remain;
+ else
+ len = somme - read;
+ len =
+ sanei_umax_pp_readBlock (len, tw, dpi, (len < blocksize),
+ buffer + reserve + remain);
+ if (len == 0)
+ {
+ DBG (0,
+ "sanei_umax_pp_readBlock failed, cancelling scan ...\n");
+ gCancel = 1;
+ }
+
+ read += len;
+ nb++;
+ DBG (8, "Read %ld bytes out of %ld ...\n", read, somme);
+ DBG (8, "Read %d blocks ... \n", nb);
+
+ /* write partial buffer to file */
+ if (len)
+ {
+ if (color >= RGB_MODE)
+ {
+ /* using an image format that doesn't need */
+ /* reordering would speed up write operation */
+
+ /* don't forget remaining bytes from previous block */
+ hp = (len + remain) / bpl;
+ remain = (len + remain) - hp * bpl;
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ /* first comes RED
+ * then BLUE
+ * and finally GREEN */
+ for (by = 0; by < hp; by++)
+ {
+ for (bx = 0; bx < tw; bx++)
+ {
+ /* scanner data: red line, blue line then green line */
+ /* red */
+ fputc (buffer
+ [3 * (by - 2 * delta) * tw + bx +
+ reserve], fout);
+ /* green */
+ fputc (buffer
+ [3 * by * tw + 2 * tw +
+ bx + reserve], fout);
+ /* blue */
+ fputc (buffer
+ [3 * (by - delta) * tw + tw + bx +
+ reserve], fout);
+ }
+ }
+ /* copy tail lines for next block */
+ /* memcpy (buffer,
+ * (buffer + reserve) + (hp * bpl - reserve),
+ * reserve + remain); */
+ memcpy (buffer, buffer + hp * bpl, reserve + remain);
+ break;
+ case 1600:
+ for (by = 0; by < hp; by++)
+ {
+ for (bx = 0; bx < tw; bx++)
+ {
+ fputc (buffer[3 * by * tw + 2 * tw + bx], fout);
+ fputc (buffer[3 * by * tw + bx], fout);
+ fputc (buffer[3 * by * tw + tw + bx], fout);
+ }
+ }
+ break;
+ default:
+ for (by = 0; by < hp; by++)
+ {
+ for (bx = 0; bx < tw; bx++)
+ {
+ fputc (buffer[3 * by * tw + 2 * tw + bx], fout);
+ fputc (buffer[3 * by * tw + tw + bx], fout);
+ fputc (buffer[3 * by * tw + bx], fout);
+ }
+ }
+ /* put remaining partial lines at start of buffer */
+ memcpy (buffer, buffer + hp * bpl, remain);
+ break;
+ }
+ }
+ else
+ fwrite (buffer, len, 1, fout);
+ }
+ }
+
+#ifdef HAVE_SYS_TIME_H
+ gettimeofday (&tf, NULL);
+
+ /* scan time are high enough to forget about usec */
+ elapsed = tf.tv_sec - td.tv_sec;
+ DBG (8, "%ld bytes transfered in %f seconds ( %.2f Kb/s)\n", somme,
+ elapsed, (somme / elapsed) / 1024.0);
+#endif
+
+ /* release ressources */
+ if (fout != NULL)
+ fclose (fout);
+ free (dest);
+ free (buffer);
+ } /* if start scan OK */
+ else
+ {
+ DBG (0, "startScan failed..... \n");
+ }
+
+ /* terminate any pending command */
+ if (sanei_umax_pp_cmdSync (0x00) == 0)
+ {
+ DBG (0, "Warning cmdSync(0x00) failed! (%s:%d)\n", __FILE__, __LINE__);
+ DBG (0, "Trying again ... ");
+ if (sanei_umax_pp_cmdSync (0x00) == 0)
+ {
+ DBG (0, " failed again! (%s:%d)\n", __FILE__, __LINE__);
+ DBG (0, "Blindly going on ...\n");
+ }
+ else
+ DBG (0, " success ...\n");
+
+ }
+
+ /* parking */
+ if (sanei_umax_pp_park () == 0)
+ DBG (0, "Park failed !!! (%s:%d)\n", __FILE__, __LINE__);
+
+
+ /* end ... */
+ DBG (16, "Scan done ...\n");
+ return 1;
+}
+
+
+
+
+
+/*
+ * returns 0 on error, 1 on success: ie head parked
+ */
+int
+sanei_umax_pp_parkWait (void)
+{
+ int status;
+
+ /* check if head is at home */
+ DBG (16, "entering parkWait ...\n");
+ do
+ {
+ usleep (1000);
+ CMDSYNC (0x40);
+ status = sanei_umax_pp_scannerStatus ();
+ }
+ while ((status & MOTOR_BIT) == 0x00);
+ DBG (16, "parkWait done ...\n");
+ return 1;
+}
+
+
+
+
+
+
+/* starts scan: return 1 on success */
+int
+sanei_umax_pp_startScan (int x, int y, int width, int height, int dpi,
+ int color, int gain, int offset, int *rbpp,
+ int *rtw, int *rth)
+{
+ unsigned char *buffer;
+ int *dest = NULL;
+ int rc = 0;
+ int calibration[3 * 5100 + 768 + 2 + 1];
+ int xdpi, ydpi, h;
+ int th, tw, bpp;
+ int hwdpi = 600; /* CCD hardware dpi */
+ /* DC offsets */
+ int dcRed, dcGreen, dcBlue;
+ int vgaRed, vgaGreen, vgaBlue;
+ int len, delta;
+
+ int lm9811[9] = {
+ 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x00, 0x00,
+ -1
+ };
+
+ int motor[17] = {
+ 0xA4, 0x80, 0x07, 0x50, 0xEC, 0x03, 0x00, 0x2F,
+ 0x17, 0x07, 0x84, 0x08, 0x90, 0x00, 0xAC, 0x00,
+ -1
+ };
+
+ int ccd[37] =
+ { 0x00, 0x00, 0xB0, 0x4F, 0xD8, 0xE7, 0xFA, 0x10, 0xEF, 0xC4, 0x3C, 0x71,
+ 0x0F, 0x00, 0x04, 0x00, 0x6E, 0x61, 0xA1, 0x24, 0xC4, 0x7E, 0x00, 0xAE,
+ 0x41, 0xA0, 0x0A, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x33, 0x1A, 0x00,
+ -1
+ };
+
+
+#ifdef UMAX_PP_DANGEROUS_EXPERIMENT
+ FILE *f = NULL;
+ char line[1024], *ptr;
+ int *base = NULL;
+ int channel;
+ int max = 0;
+#endif
+
+
+ if (sanei_umax_pp_getastra () == 610)
+ {
+ hwdpi = 300;
+ len = 0x22;
+
+ lm9811[0] = 0x88;
+ lm9811[1] = 0xE6;
+ lm9811[2] = 0xFD;
+ lm9811[3] = 0x8E;
+ lm9811[4] = 0x30;
+ lm9811[5] = 0x00;
+ lm9811[6] = 0x8F;
+ lm9811[7] = 0x80;
+
+ motor[3] = 0x30;
+ motor[4] = 0x0E;
+ motor[5] = 0x02;
+ motor[12] = 0xAA;
+
+ ccd[0] = 0x00;
+ ccd[1] = 0x00;
+ ccd[2] = 0xD8;
+ ccd[3] = 0x27;
+ ccd[4] = 0xEC;
+ ccd[5] = 0x53;
+ ccd[6] = 0x7D;
+ ccd[7] = 0x8A;
+ ccd[8] = 0x77;
+ ccd[9] = 0xE2;
+ ccd[10] = 0x9E;
+ ccd[11] = 0xF8;
+ ccd[12] = 0x07;
+ ccd[13] = 0x20;
+ ccd[14] = 0x02;
+ ccd[15] = 0x00;
+ ccd[16] = 0x76;
+ ccd[17] = 0x5D;
+ ccd[18] = 0xE0;
+ ccd[19] = 0x13;
+ ccd[20] = 0xE2;
+ ccd[21] = 0x20;
+ ccd[22] = 0x00;
+ ccd[23] = 0xA8;
+ ccd[24] = 0x41;
+ ccd[25] = 0xA0;
+ ccd[26] = 0x0A;
+ ccd[27] = 0x8B;
+ ccd[28] = 0x4D;
+ ccd[29] = 0x4B;
+ ccd[30] = 0xD0;
+ ccd[31] = 0x68;
+ ccd[32] = 0xDF;
+ ccd[33] = 0x13;
+ }
+ else
+ {
+ len = 0x24;
+ hwdpi = 600;
+ }
+ DBG (8, "startScan(%d,%d,%d,%d,%d,%d,%X);\n", x, y, width, height, dpi,
+ color, gain);
+ buffer = (unsigned char *) malloc (2096100);
+ if (buffer == NULL)
+ {
+ DBG (0, "Failed to allocate 2096100 bytes... (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+
+ /* 1600P have a different CCD command block */
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ lm9811[0] = 0x19;
+ lm9811[1] = 0xD5;
+ lm9811[4] = 0x1B;
+ lm9811[7] = 0x70;
+
+ ccd[29] = 0x1A;
+ ccd[30] = 0xEE;
+
+ motor[13] = 0x03; /* may be blur filter */
+ }
+
+ /* matches intTransport610P */
+ /* get scanner status */
+ rc = inquire ();
+ if (rc == 0)
+ {
+ DBG (0, "inquire() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "inquire() passed ... (%s:%d)\n", __FILE__, __LINE__);
+
+ dest = (int *) malloc (65536 * sizeof (int));
+
+ rc = loadDefaultTables ();
+ if (rc == 0)
+ {
+ DBG (0, "loadDefaultTables() failed ! (%s:%d) \n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "loadDefaultTables() passed ... (%s:%d)\n", __FILE__, __LINE__);
+
+ /* find and move to zero */
+ if (moveToOrigin () == 0)
+ {
+ DBG (0, "moveToOrigin() failed ... (%s:%d)\n", __FILE__, __LINE__);
+ }
+ else
+ {
+ DBG (16, "moveToOrigin() passed ... (%s:%d)\n", __FILE__, __LINE__);
+ }
+
+ /* 1600P have a different CCD command block */
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ lm9811[0] = 0x19;
+ lm9811[1] = 0xD5;
+ lm9811[4] = 0x1B;
+ lm9811[7] = 0x70;
+
+ ccd[29] = 0x1A;
+ ccd[30] = 0xEE;
+
+ motor[13] = 0x03; /* may be blur filter */
+ }
+
+ /* XXX STEF XXX : done even is manual settings, some day skip it
+ * and move head the right amount */
+ if (offsetCalibration (color, &dcRed, &dcGreen, &dcBlue) == 0)
+ {
+ DBG (0, "offsetCalibration failed !!! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16, "offsetCalibration(%d=>%d,%d,%d) passed ... (%s:%d)\n",
+ color, dcRed, dcGreen, dcBlue, __FILE__, __LINE__);
+
+ if (coarseGainCalibration
+ (color, dcRed, dcGreen, dcBlue, &vgaRed, &vgaGreen, &vgaBlue) == 0)
+ {
+ DBG (0, "coarseGainCalibration failed !!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16,
+ "coarseGainCalibration(%d,%d,%d,%d=>%d,%d,%d) passed ... (%s:%d)\n",
+ color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue,
+ __FILE__, __LINE__);
+
+ /* manual setting overrides evaluated values */
+ if (!sanei_umax_pp_getauto ())
+ {
+ dcRed = (offset & 0xF00) >> 8;
+ dcGreen = (offset & 0x0F0) >> 4;
+ dcBlue = offset & 0x00F;
+ vgaRed = (gain & 0xF00) >> 8;
+ vgaGreen = (gain & 0x0F0) >> 4;
+ vgaBlue = gain & 0x00F;
+ }
+
+ /* ccd calibration is allways done */
+ /* with final dc and vga */
+ if (shadingCalibration
+ (color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue,
+ calibration) == 0)
+ {
+ DBG (0, "shadingCalibration failed !!! (%s:%d)\n", __FILE__, __LINE__);
+ return 0;
+ }
+ DBG (16,
+ "shadingCalibration(%d,%d,%d,%d,%d,%d,%d) passed ... (%s:%d)\n",
+ color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue, __FILE__,
+ __LINE__);
+
+ /* gamma or left shading calibration ? */
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ if (leftShadingCalibration610p
+ (color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue,
+ calibration) == 0)
+ {
+ DBG (0, "leftShadingCalibration610p failed !!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16,
+ "leftShadingCalibration610p(%d,%d,%d,%d,%d,%d,%d) passed ... (%s:%d)\n",
+ color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue, __FILE__,
+ __LINE__);
+ }
+
+ /* 1220P: x dpi is from 75 to 600 max, any modes */
+ /* 610P: x dpi is from 75 to 300 max, any modes */
+ if (dpi > hwdpi)
+ xdpi = hwdpi;
+ else
+ xdpi = dpi;
+
+
+ /* EPPRead32Buffer does not work */
+ /* with length not multiple of four bytes, so we enlarge */
+ /* width to meet this criteria ... */
+ if ((getEPPMode () == 32) && (xdpi >= 600) && (width & 0x03)
+ && (sanei_umax_pp_getastra () > 610))
+ {
+ width += (4 - (width & 0x03));
+ /* in case we go too far on the right */
+ if (x + width > 5100)
+ {
+ x = 5100 - width;
+ }
+ }
+
+ /* compute target size */
+ th = (height * dpi) / hwdpi;
+ tw = (width * xdpi) / hwdpi;
+
+ /* corrects y to match exact scan area start
+ * and lets room for a leading zone so that
+ * we can reorder data */
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ if (color >= RGB_MODE)
+ switch (dpi)
+ {
+ case 600:
+ y += 64;
+ break;
+ case 300:
+ y += 32;
+ break;
+ case 150:
+ y += 16;
+ break;
+ case 75:
+ y += 8;
+ break;
+ }
+ else
+ y += 80;
+ default:
+ y += 8;
+ break;
+ }
+
+ /* for 1220P/2000P move fast to scan target if possible */
+ /* it is faster to move at low resolution, then scan */
+ /* than scan & move at high resolution */
+ if ((sanei_umax_pp_getastra () > 610 && (dpi > 300)) && (y > 100))
+ {
+ /* move at 150 dpi resolution */
+ move (y / 2, PRECISION_OFF, NULL);
+
+ /* keep the remainder for scan */
+ y = y % 4;
+ }
+
+ /* build final scan command */
+
+ /* round width and height */
+ width = (tw * hwdpi) / xdpi;
+ height = (th * hwdpi) / dpi;
+
+ ydpi = dpi;
+ if (ydpi < 300)
+ {
+ if ((color >= RGB_MODE) && (sanei_umax_pp_getastra () > 610))
+ {
+ if (dpi < 150)
+ ydpi = 150;
+ }
+ else
+ ydpi = 300;
+ }
+ if ((color < RGB_MODE) && (sanei_umax_pp_getastra () <= 610))
+ ydpi = 600;
+
+ /* at maximum resolution */
+ if (color >= RGB_MODE)
+ {
+ h = ((height * ydpi) / hwdpi) + 8;
+ bpp = 3;
+ }
+ else
+ {
+ h = ((height * ydpi) / hwdpi) + 4;
+ if (sanei_umax_pp_getastra () <= 610)
+ h += 16;
+ bpp = 1;
+ }
+
+ /* sets y resolution */
+ switch (ydpi)
+ {
+ case 1200:
+ motor[6] = 0x60;
+ motor[8] = 0x5E; /* *WORKING* value */
+ motor[8] = 0x5F; /* 5F gives wrong colors ? */
+ motor[8] = 0x58;
+ motor[9] = 0x05;
+ /* XXX test value XXX motor[14] = motor[14] & 0xF0; ~ 0x08 -> scan AND move */
+ /* XXX test value XXX motor[14] = (motor[14] & 0xF0) | 0x04; -> 600 dpi ? */
+ /* XXX test value XXX motor[14] = (motor[14] & 0xF0) | 0x0C; */
+ motor[14] = motor[14] & 0xF0; /* *WORKING* 1200 dpi */
+ break;
+
+ case 600:
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ motor[6] = 0xC0;
+ motor[7] = 0x2F;
+ motor[14] = motor[14] & 0xF0;
+ /* if (color >= RGB_MODE)
+ motor[14] |= 0x04; XXX STEF XXX */
+ }
+ else
+ {
+ motor[6] = 0x60;
+ motor[14] = (motor[14] & 0xF0) | 0x04;
+ }
+ motor[8] = 0x2F;
+ motor[9] = 0x05;
+ break;
+
+ case 300:
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ motor[6] = 0xC0;
+ motor[14] = motor[14] & 0xF0;
+ if (color >= RGB_MODE)
+ motor[14] |= 0x04;
+ }
+ else
+ {
+ motor[6] = 0x00;
+ motor[14] = (motor[14] & 0xF0) | 0x0C;
+ }
+ motor[8] = 0x17;
+ motor[9] = 0x05;
+
+ /* si | 0C h=2*w, si | 04 h=w ? */
+
+ break;
+
+ case 150:
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ motor[6] = 0xC0;
+ motor[9] = 0x05;
+ motor[14] = motor[14] & 0xF0;
+ if (color >= RGB_MODE)
+ motor[14] |= 0x04;
+ }
+ else
+ {
+ motor[6] = 0x00;
+ motor[9] = 0x07;
+ motor[14] = (motor[14] & 0xF0) | 0x0C;
+ }
+ motor[8] = 0x17;
+ break;
+ }
+
+ /* different values for 610P in B&W */
+ if ((sanei_umax_pp_getastra () <= 610) && (color < RGB_MODE))
+ {
+ motor[7] = 0xC8;
+ motor[8] = 0x2F;
+ motor[9] = 0x05;
+ }
+
+ /* y start -1 */
+ y = (y * ydpi) / hwdpi;
+
+ if (color >= RGB_MODE)
+ {
+ /* 00 seems to give better results ? */
+ /* 80 some more gain, lamp power level ? */
+ /* 8x does not make much difference */
+ lm9811[6] = 0x8F;
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ lm9811[7] = 0x80;
+ motor[13] = 0x20;
+ break;
+ case 1600:
+ lm9811[7] = 0x70;
+ motor[13] = 0x03;
+ break;
+ default:
+ lm9811[7] = 0xF0;
+ motor[13] = 0x09;
+ }
+ }
+ else
+ {
+ motor[13] = 0xC0;
+ lm9811[6] = 0x80 | vgaGreen;
+ switch (sanei_umax_pp_getastra ())
+ {
+ case 610:
+ lm9811[7] = 0xA0;
+ lm9811[6] = lm9811[6] | 0x40;
+ motor[13] = 0x6F;
+ break;
+ case 1600:
+ lm9811[7] = 0x20;
+ motor[13] = 0xC3;
+ break;
+ default:
+ lm9811[7] = 0xA0;
+ motor[13] = 0xC9;
+ }
+ }
+
+ encodeCoefficient (color, dpi, calibration);
+ encodeWX (width, x, dpi, color, ccd, 0);
+ encodeHY (h, y, motor);
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (vgaRed, vgaGreen, vgaBlue, motor);
+
+#ifdef UMAX_PP_DANGEROUS_EXPERIMENT
+ /*motor[13] = 0x80; B&W bit */
+ /*motor[13] = 0x40; green bit */
+ /*motor[13] = 0x20; red bit */
+ /*motor[13] = 0x10; blue bit */
+ /* with cmd 01, may be use to do 3 pass scanning ? */
+ /* bits 0 to 3 seem related to sharpness */
+ f = fopen ("/tmp/dangerous.params", "rb");
+ if (f != NULL)
+ {
+ fgets (line, 1024, f);
+ while (!feof (f))
+ {
+ channel = 0;
+ if (sscanf (line, "CMD%1d", &channel) != 1)
+ channel = 0;
+ switch (channel)
+ {
+ case 0:
+ break;
+ case 1:
+ base = lm9811;
+ max = 8;
+ break;
+ case 2:
+ base = motor;
+ max = 16;
+ break;
+ case 8:
+ base = ccd;
+ max = 36;
+ break;
+ default:
+ channel = 0;
+ }
+ printf ("CMD%d BEFORE: ", channel);
+ for (i = 0; i < max; i++)
+ printf ("%02X ", base[i]);
+ printf ("\n");
+ if (channel > 0)
+ {
+ ptr = line + 6;
+ for (i = 0; (i < max) && ((ptr - line) < strlen (line)); i++)
+ {
+ if (ptr[0] != '-')
+ {
+ sscanf (ptr, "%X", base + i);
+ }
+ ptr += 3;
+ }
+ }
+ printf ("CMD%d AFTER : ", channel);
+ for (i = 0; i < max; i++)
+ printf ("%02X ", base[i]);
+ printf ("\n");
+ fgets (line, 1024, f);
+ }
+ fclose (f);
+ }
+#endif
+
+ if (DBG_LEVEL >= 64)
+ {
+ bloc2Decode (motor);
+ bloc8Decode (ccd);
+ }
+
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+
+ /* 3 ccd lines + 3 gamma tables + end tag */
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ /* XXX STEF XXX : there is a 4 pixels shift to the right
+ * the first shading correction value applies to the forth
+ * pixel of scan (at 300 dpi), we allready shift to the left
+ * when doing shadingCalibration, but now we have to move coeffs
+ * to match x coordinate */
+ delta = x - sanei_umax_pp_getLeft ();
+ if (delta)
+ {
+ memcpy (calibration,
+ calibration + delta, (7650 - delta) * sizeof (int));
+ }
+ CMDSET (4, 0x20E4, calibration);
+ }
+ else
+ {
+ CMDSET (4, 0x3EC6, calibration);
+ }
+
+ COMPLETIONWAIT;
+
+ *rbpp = bpp;
+ *rtw = tw;
+ *rth = th;
+
+ free (buffer);
+ return 1;
+}
+
+/*
+ * check the scanner model. Return 1220 for
+ * a 1220P, or 2000 for a 2000P.
+ * and 610 for a 610p
+ * values less than 610 are errors
+ */
+int
+sanei_umax_pp_checkModel (void)
+{
+ int *dest = NULL;
+ int state[16];
+ int err = 0;
+ int i;
+
+ int opsc35[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x03, 0xC1, 0x80,
+ 0x00, 0x00, 0x04, 0x00, 0x16, 0x41, 0xE0, 0xAC, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+
+ /* if we have already detected a scanner different from */
+ /* default type, no need to check again */
+ if (sanei_umax_pp_getastra ())
+ return sanei_umax_pp_getastra ();
+
+ /* get scanner status */
+ CMDGET (0x02, 16, state);
+ CMDSETGET (0x08, 36, opsc35);
+ CMDSYNC (0xC2);
+
+ dest = (int *) malloc (65536 * sizeof (int));
+ if (dest == NULL)
+ {
+ DBG (0, "%s:%d failed to allocate 256 Ko !\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ /* init some buffer : default calibration data ? */
+ dest[0] = 0x00;
+ dest[1] = 0x00;
+ dest[2] = 0x00;
+ for (i = 0; i < 768; i++)
+ dest[i + 3] = i % 256;
+ dest[768 + 3] = 0xAA;
+ dest[768 + 4] = 0xAA;
+ dest[768 + 5] = -1;
+ CMDSETGET (0x04, 768 + 5, dest);
+
+
+ /* check buffer returned */
+ for (i = 0; i < 768; i++)
+ {
+ if (dest[i + 3] != (i % 256))
+ {
+ DBG
+ (0,
+ "Error data altered: byte %d=0x%02X, should be 0x%02X ! (%s:%d)\n",
+ i, dest[i + 3], i % 256, __FILE__, __LINE__);
+ err = 1;
+ }
+ }
+ if (err)
+ return 0;
+
+
+ /* new part of buffer ... */
+ for (i = 0; i < 256; i++)
+ {
+ dest[i * 2] = i;
+ dest[i * 2 + 1] = 0x00;
+ }
+ CMDSETGET (0x08, 36, opsc35);
+ CMDSYNC (0xC2);
+ CMDSET (0x04, 512, dest);
+
+ /* another new part ... */
+ for (i = 0; i < 256; i++)
+ {
+ dest[i * 2] = i;
+ dest[i * 2 + 1] = 0x04; /* instead of 0x00 */
+ }
+ opsc35[2] = 0x06; /* instead of 0x04, write flag ? */
+ CMDSETGET (0x08, 36, opsc35);
+ CMDSYNC (0xC2);
+ CMDSET (0x04, 512, dest);
+
+ opsc35[2] = 0x04; /* return to initial value, read flag? */
+ CMDSETGET (0x08, 36, opsc35);
+ CMDGET (0x04, 512, dest);
+
+ /* check buffer returned */
+ /* if 0x4 are still 0x04, we got a 1220P, else it is a 2000P */
+ for (i = 0; i < 256; i++)
+ {
+ if ((dest[2 * i] != i)
+ || ((dest[2 * i + 1] != 0x04) && (dest[2 * i + 1] != 0x00)))
+ {
+ DBG
+ (0,
+ "Error data altered: expected %d=(0x%02X,0x04), found (0x%02X,0x%02X) ! (%s:%d)\n",
+ i, i, dest[i * 2], dest[i * 2 + 1], __FILE__, __LINE__);
+ err = 0;
+ }
+ }
+
+ /* if buffer unchanged, we have a 1600P, or a 1220P */
+ /* if data has turned into 0, we have a 2000P */
+ if (dest[1] == 0x00)
+ {
+ sanei_umax_pp_setastra (2000);
+ err = 2000;
+ }
+ else
+ {
+ /* detects 1600 by finding black scans */
+ /* we defaults to 1220 */
+ sanei_umax_pp_setastra (1220);
+ moveToOrigin ();
+ err = sanei_umax_pp_getastra ();
+
+ /* parking */
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ if (sanei_umax_pp_park () == 0)
+ DBG (0, "Park failed !!! (%s:%d)\n", __FILE__, __LINE__);
+
+ /* poll parking */
+ do
+ {
+ sleep (1);
+ CMDSYNC (0x40);
+ }
+ while ((sanei_umax_pp_scannerStatus () & MOTOR_BIT) == 0x00);
+ }
+
+ /* return guessed model number */
+ CMDSYNC (0x00);
+ return err;
+}
+
+
+
+/* sets, resets gamma tables */
+
+void
+sanei_umax_pp_gamma (int *red, int *green, int *blue)
+{
+ if (red != NULL)
+ {
+ ggRed = red;
+ }
+ else
+ {
+ ggRed = ggamma;
+ }
+
+ if (green != NULL)
+ {
+ ggGreen = green;
+ }
+ else
+ {
+ ggGreen = ggamma;
+ }
+
+ if (blue != NULL)
+ {
+ ggBlue = blue;
+ }
+ else
+ {
+ ggBlue = ggamma;
+ }
+}
+
+/* initialize scanner by loading default transformation table */
+/* O: failure
+ * 1: OK
+ */
+int
+loadDefaultTables (void)
+{
+ int cmd01[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x03, 0xC1, 0x80, 0x60, 0x20, 0x00, 0x00,
+ 0x16, 0x41, 0xE0, 0xAC, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xF0, 0x13, -1
+ };
+ int opsc35[37] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x03, 0xC1, 0x80, 0x00, 0x00, 0x04, 0x00,
+ 0x16, 0x41, 0xE0, 0xAC, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+ int i, len, *cmd, err;
+ int buffer[774];
+ int rc = 1;
+
+
+ /* 1600P have a different CCD command block */
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ opsc35[29] = 0x1A;
+ opsc35[30] = 0xEE;
+ }
+
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ len = 0x22;
+ cmd = cmd01;
+ /* XXX STEF XXX if we send F0, we get 0x10 back */
+ cmd[0x21] = 0x10;
+ }
+ else
+ {
+ len = 0x24;
+ cmd = opsc35;
+ }
+
+ /* set and reread first table */
+ /* since 1660P seems to have another type of CCD
+ * this table is not sent/needed
+ */
+ err = 0;
+ if (sanei_umax_pp_getastra () != 1600)
+ {
+ CMDSETGET (8, len, cmd);
+ CMDSYNC (0xC2);
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ for (i = 0; i < 768; i++)
+ buffer[i + 3] = i % 256;
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ buffer[768 + 3] = 0xFF;
+ buffer[768 + 4] = 0xFF;
+ }
+ else
+ {
+ buffer[768 + 3] = 0xAA;
+ buffer[768 + 4] = 0xAA;
+ }
+ buffer[768 + 5] = -1;
+ CMDSETGET (4, 0x305, buffer);
+
+
+ /* check buffer returned */
+ for (i = 0; i < 768; i++)
+ {
+ if (buffer[i + 3] != (i % 256))
+ {
+ DBG
+ (0,
+ "Error data altered: byte %d=0x%02X, should be 0x%02X ! (%s:%d)\n",
+ i, buffer[i + 3], i % 256, __FILE__, __LINE__);
+ err = 1;
+ }
+ }
+ if (err)
+ return 0;
+ }
+
+ /* second table ... */
+ for (i = 0; i < 256; i++)
+ {
+ buffer[i * 2] = i;
+ buffer[i * 2 + 1] = 0;
+ }
+ CMDSETGET (8, len, cmd);
+ CMDSYNC (0xC2);
+ CMDSET (4, 0x200, buffer);
+
+ /* third table ... */
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ buffer[i * 2] = i;
+ buffer[i * 2 + 1] = 0x01; /* instead of 0x00 */
+ }
+ }
+ else
+ {
+ for (i = 0; i < 256; i++)
+ {
+ buffer[i * 2] = i;
+ buffer[i * 2 + 1] = 0x04; /* instead of 0x00 */
+ }
+ }
+ opsc35[2] = 0x06;
+ cmd01[1] = 0x80;
+ CMDSETGET (8, len, cmd);
+ CMDSYNC (0xC2);
+ CMDSET (4, 0x200, buffer);
+
+ opsc35[2] = 0x04;
+ cmd01[1] = 0x00;
+ CMDSETGET (8, len, cmd);
+ CMDGET (4, 0x200, buffer);
+ /* check buffer returned */
+ /* if 0x4 are still 0x0 (hum..), we got a 1220P, else it is a 2000P */
+ for (i = 0; i < 256; i++)
+ {
+ if ((buffer[2 * i] != i)
+ || ((buffer[2 * i + 1] != 0x04) && (buffer[2 * i + 1] != 0x01)
+ && (buffer[2 * i + 1] != 0x00)))
+ {
+ DBG
+ (0,
+ "Error data altered: expected %d=(0x%02X,0x04), found (0x%02X,0x%02X) ! (%s:%d)\n",
+ i, i, buffer[i * 2], buffer[i * 2 + 1], __FILE__, __LINE__);
+ err = 1;
+ }
+ }
+ if (err)
+ return 0;
+
+ return rc;
+}
+
+/* inquire scanner status
+ * O: failure
+ * 1: OK
+ * 2: first scanner init, needs re-homing
+ */
+int
+inquire (void)
+{
+ int cmd01[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x03, 0xC1, 0x80, 0x60, 0x20, 0x00, 0x00,
+ 0x16, 0x41, 0xE0, 0xAC, 0x03, 0x03, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xF0, 0x1B, -1
+ };
+ int buffer[37];
+ int rc = 1, first, i;
+ char str[106];
+
+
+ /* inquiry: ask for RAM, CCD, ... */
+ CMDSET (8, 0x23, cmd01);
+ CMDGET (8, 0x23, buffer);
+
+ if (DBG_LEVEL > 8)
+ {
+ for (i = 0; i < 35; i++)
+ sprintf (str + 3 * i, "%02X ", buffer[i]);
+ str[105] = 0x00;
+ DBG (8, "SCANNER INFORMATION=%s\n", str);
+ }
+
+ /* get state */
+ CMDGET (2, 0x10, buffer);
+ first = 1;
+ for (i = 0; i < 14; i++)
+ {
+ if (buffer[i] != 0)
+ first = 0;
+ }
+ if (buffer[15] != 0)
+ first = 0;
+ if (first)
+ rc = 2;
+
+ if (DBG_LEVEL > 8)
+ {
+ for (i = 0; i < 16; i++)
+ sprintf (str + 3 * i, "%02X ", buffer[i]);
+ str[48] = 0x00;
+ DBG (8, "SCANNER STATE=%s\n", str);
+ }
+
+ return rc;
+}
+
+/*
+ * computes DC offset to have black pixel really black out of
+ * CCD ie black gives 0
+ * 1220P implements the method described in LM9811 datasheet
+ * returns 1 and DC offsets in the corresponding vars on success .
+ * On failure, returns 0.
+ */
+static int
+offsetCalibration1220p (int color, int *offRed, int *offGreen, int *offBlue)
+{
+ unsigned char buffer[5300];
+ int i, val;
+ int commit[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, -1 };
+ int opsc04[9] = { 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x00, 0x00, -1 };
+ int opsc10[9] = { 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x08, 0x00, -1 };
+ int opsc38[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x40, 0x01,
+ 0x00, 0x00, 0x04, 0x00, 0x6E, 0x18, 0x10, 0x03, 0x06, 0x00, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+ int opsc48[17] =
+ { 0x09, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2F, 0x2F, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0xA4, 0x00, -1
+ };
+ float low, high;
+ DBG (16, "entering offsetCalibration1220p() ... (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ /* really dirty hack: somethig is buggy in BW mode */
+ /* we override mode with color until the bug is found */
+ /* color = RGB_MODE; */
+
+ /* 1600P have a different CCD command block */
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ opsc04[0] = 0x19;
+ opsc04[1] = 0xD5;
+ opsc04[4] = 0x1B;
+ opsc04[7] = 0x20;
+
+ opsc10[0] = 0x19;
+ opsc10[1] = 0xD5;
+ opsc10[4] = 0x1B;
+ opsc10[7] = 0x20;
+
+ opsc48[8] = 0x2B;
+ opsc48[11] = 0x20;
+ opsc48[12] = 0x08;
+ opsc48[13] = 0x42;
+ }
+
+ /* offset calibration, scan 24 bytes of black in each color */
+ /* component see section 5.1 of LM9811 datasheet */
+ if (color >= RGB_MODE)
+ {
+ CMDSETGET (2, 0x10, opsc48);
+ CMDSETGET (8, 0x24, opsc38);
+ CMDSETGET (1, 0x08, opsc04); /* scan std, no 'enhancing' */
+ CMDSYNC (0xC2);
+ if (sanei_umax_pp_scannerStatus () & 0x80)
+ {
+ CMDSYNC (0x00);
+ }
+ CMDSETGET (4, 0x08, commit); /* commit ? */
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x18, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x18, buffer, NULL);
+ val = 0;
+ for (i = 0; i < 24; i++)
+ val += buffer[i];
+ low = (float) val / i; /* Vadc1 */
+
+
+ CMDSYNC (0x00);
+ opsc04[7] = opsc04[7] | 0x10; /* enhanced ? */
+ CMDSETGET (1, 0x08, opsc04);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x18, buffer);
+ val = 0;
+ for (i = 0; i < 24; i++)
+ val += buffer[i];
+ high = (float) val / i; /* Vadc2 */
+ if (DBG_LEVEL >= 128)
+ Dump (0x18, buffer, NULL);
+ *offRed = 15.0 - ((high - low) * 2);
+
+ /* block that repeats */
+ /* must be monochrome since hscan=1 */
+ opsc48[0] = 0x01;
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ opsc48[12] = 0x0C;
+ opsc48[13] = 0x82;
+ }
+ else
+ {
+ opsc48[12] = 0x04;
+ opsc48[13] = 0x80;
+ }
+ CMDSETGET (2, 0x10, opsc48);
+ CMDSETGET (8, 0x24, opsc38);
+ opsc04[7] = opsc04[7] & 0x20;
+ CMDSETGET (1, 0x08, opsc04);
+ CMDSYNC (0xC2);
+ if (sanei_umax_pp_scannerStatus () & 0x80)
+ {
+ CMDSYNC (0x00);
+ }
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x18, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x18, buffer, NULL);
+ val = 0;
+ for (i = 0; i < 24; i++)
+ val += buffer[i];
+ low = (float) val / i;
+
+ CMDSYNC (0x00);
+ opsc04[7] = opsc04[7] | 0x10; /* gain ? */
+ CMDSETGET (1, 0x08, opsc04);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x18, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x18, buffer, NULL);
+ val = 0;
+ for (i = 0; i < 24; i++)
+ val += buffer[i];
+ high = (float) val / i;
+
+ *offBlue = 15.0 - ((high - low) * 2);
+ }
+
+ /* block that repeats */
+ if (color < RGB_MODE)
+ {
+ opsc48[0] = 0x05; /* B&H height is 5 */
+ opsc48[13] = 0xC0; /* B&W mode */
+ }
+ else
+ {
+ opsc48[0] = 0x05; /* color height is 5 (+4 ?) */
+ opsc48[13] = 0xC1; /* some strange mode ? */
+ }
+ if (sanei_umax_pp_getastra () == 1600)
+ opsc48[13] = opsc48[13] | 0x02;
+ CMDSETGET (2, 0x10, opsc48);
+ CMDSETGET (8, 0x24, opsc38);
+ opsc04[7] = opsc04[7] & 0x20;
+ CMDSETGET (1, 0x08, opsc04);
+ CMDSYNC (0xC2);
+ if (sanei_umax_pp_scannerStatus () & 0x80)
+ {
+ CMDSYNC (0x00);
+ }
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x18, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x18, buffer, NULL);
+ val = 0;
+ for (i = 0; i < 24; i++)
+ val += buffer[i];
+ low = (float) val / i;
+
+ CMDSYNC (0x00);
+ opsc04[7] = opsc04[7] | 0x10;
+ CMDSETGET (1, 0x08, opsc04);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x18, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x18, buffer, NULL);
+ val = 0;
+ for (i = 0; i < 24; i++)
+ val += buffer[i];
+ high = (float) val / i;
+
+ *offGreen = 15.0 - ((high - low) * 2);
+
+ /*DBG (1, "STEF: offsets(RED,GREEN,BLUE=(%d,%d,%d)\n", *offRed, *offGreen,
+ *offBlue);*/
+ DBG (16, "offsetCalibration1220p() done ...\n");
+ return 1;
+}
+
+/*
+ * computes DC offset to have black pixel really black out of
+ * CCD ie black gives 0
+ * 610P doesn't implement method described in LM9811 datasheet
+ * but scan a black line with decreasing offsets until the
+ * scanned data reach a 'good black level'.
+ * returns 1 and DC offsets in the corresponding vars on success .
+ * On failure, returns 0.
+ */
+static int
+offsetCalibration610p (int color, int *offRed, int *offGreen, int *offBlue)
+{
+ int motor[17] = {
+ 0x11, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC0, 0x2F,
+ 0x17, 0x00, 0x00, 0xF0, 0x7D, 0x5F, 0xA4, 0x00,
+ -1
+ };
+
+ int ccd[37] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x04, 0x40, 0x01, 0x00, 0x20, 0x02, 0x00,
+ 0x76, 0x12, 0xB0, 0x03, 0x06, 0x00, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+
+ int lm9811[9] = {
+ 0x88, 0xE6, 0xFD, 0x8E, 0x30, 0x00, 0x40, 0xF0,
+ -1
+ };
+
+ int commit[9] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ -1
+ };
+
+
+ int offset;
+ int level;
+ unsigned char data[40];
+ int i;
+ int len;
+ int w;
+
+ DBG (16, "entering offsetCalibration610P() ... (%s:%d)\n", __FILE__,
+ __LINE__);
+
+ if (sanei_umax_pp_getastra () < 1220)
+ {
+ len = 0x22;
+ w = 40;
+ }
+ else
+ {
+ len = 0x24;
+ w = 40;
+ }
+
+ *offRed = 0;
+ *offGreen = 0;
+ *offBlue = 0;
+
+ /* first color channel: used both in color and b&w modes */
+ /* offset to the max */
+ /* supposed to be green componant */
+ offset = 0x10;
+ do
+ {
+ offset--;
+
+ /* 7D: 0111 1101 */
+ /* sets for the current offset */
+ motor[12] = (offset << 2) | 0x40 | 0x01;
+ lm9811[7] = offset << 4;
+
+ /* sends commands */
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+ level = 0;
+ for (i = 0; i < w; i++)
+ level += data[i];
+ }
+ /* loop while average >0.5 */
+ while ((offset > 0) && ((level * 2) / w > 1));
+ *offGreen = offset;
+
+ /* offset calibration for the two other channels when in color */
+ if (color >= RGB_MODE)
+ {
+ motor[0] = 0x01;
+
+ offset = 0x10;
+ do
+ {
+ offset--;
+
+ /* sets for the current offset */
+ motor[13] = 0x90 | offset;
+ lm9811[7] = offset << 4;
+
+ /* sends commands */
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+ level = 0;
+ for (i = 0; i < w; i++)
+ level += data[i];
+ }
+ /* loop while average >0.5 */
+ while ((offset > 0) && ((level * 2) / w > 1));
+ *offBlue = offset;
+
+ /* last color component */
+ motor[0] = 0x09;
+ ccd[13] = 0xD0 | (ccd[13] & 0x0F);
+
+ offset = 0x10;
+ do
+ {
+ offset--;
+
+ /* sets for the current offset */
+ motor[11] = offset << 4;
+ lm9811[7] = offset << 4;
+
+ /* sends commands */
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ {
+ CMDSYNC (0x00);
+ }
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+ level = 0;
+ for (i = 0; i < w; i++)
+ level += data[i];
+ }
+ /* loop while average >0.5 */
+ while ((offset > 0) && ((level * 2) / w > 1));
+ *offRed = offset;
+ }
+ else
+ {
+ *offRed = 0x0F;
+ *offBlue = 0x0F;
+ }
+
+ return 1;
+}
+
+/*
+ * generic offset calibration function
+ */
+static int
+offsetCalibration (int color, int *dcRed, int *dcGreen, int *dcBlue)
+{
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ if (offsetCalibration610p (color, dcRed, dcGreen, dcBlue) == 0)
+ {
+ DBG (0, "offsetCalibration610p failed !!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16, "offsetCalibration610p(%d=>%d,%d,%d) passed ... (%s:%d)\n",
+ color, *dcRed, *dcGreen, *dcBlue, __FILE__, __LINE__);
+ }
+ else
+ {
+ if (offsetCalibration1220p (color, dcRed, dcGreen, dcBlue) == 0)
+ {
+ DBG (0, "offsetCalibration1220p failed !!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16, "offsetCalibration1220p(%d=>%d,%d,%d) passed ... (%s:%d)\n",
+ color, *dcRed, *dcGreen, *dcBlue, __FILE__, __LINE__);
+ }
+ return 1;
+}
+
+/*
+ * computes Video Gain Amplifier : LM9811 can corrects up to 3dB of
+ * light variation. So we must adjust VGA so that dynamic range is
+ * within 3db. It is achieved when min white pixel >= max white pixel / 2.8
+ * So we scan a white area and decrease gain until this condition is met.
+ * returns 1 and VGA values in the corresponding vars on success .
+ * On failure, returns 0.
+ */
+static int
+coarseGainCalibration610p (int color, int dcRed, int dcGreen, int dcBlue,
+ int *vgaRed, int *vgaGreen, int *vgaBlue)
+{
+ int motor[17] = {
+ 0x11, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC0, 0x2F,
+ 0x17, 0x00, 0xFF, 0xAF, 0xAA, 0x6A, 0xA4, 0x00,
+ -1
+ };
+
+ int ccd[37] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x04, 0x40, 0x01, 0x00, 0x20, 0x02, 0x00,
+ 0x76, 0x41, 0xE0, 0xAC, 0x06, 0x00, 0x00, 0x9C,
+ 0x4A, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+
+ /*
+ * lm9811[7]= VGA << 4
+ * lm9811[6]= 0x40 | DC offset
+ */
+ int lm9811[9] = {
+ 0x88, 0xE6, 0xFD, 0x8E, 0x30, 0x00, 0x40, 0xF0,
+ -1
+ };
+
+ int commit[9] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ -1
+ };
+
+ unsigned char data[5400];
+ int i, len;
+ int w, xstart, xend;
+ int min, max;
+
+ TRACE (16, "entering coarseGainCalibration610p ...\n");
+ if (sanei_umax_pp_getastra () < 1220)
+ {
+ w = 2700;
+ len = 0x22;
+ }
+ else
+ {
+ w = 5400;
+ len = 0x24;
+ }
+
+ /* move back to desired area */
+ MOVE (-69, PRECISION_OFF, NULL);
+
+ /* first scan : taking a reference full width scan to
+ * find usable full width of the CCD
+ */
+ *vgaRed = 0x08;
+ *vgaGreen = 0x00;
+ *vgaBlue = 0x00;
+
+ /* scanning red component -> h=1 */
+ motor[0] = 0x01;
+ motor[13] = 0xAA; /* will be 6A below */
+
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (*vgaRed, *vgaGreen, *vgaBlue, motor);
+
+ lm9811[7] = dcRed << 4;
+ lm9811[6] = 0x40 | *vgaRed;
+
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSETGET (4, 0x08, commit);
+
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ {
+ CMDSYNC (0x00);
+ }
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+
+ /* find usable CCD area */
+ i = 0;
+ while ((i < w) && (data[i] <= (targetCode * 2) / 5))
+ i++;
+ xstart = i;
+ i = w - 1;
+ while ((i > 0) && (data[i] <= (targetCode * 2) / 5))
+ i--;
+ xend = i;
+ DBG (32, "coarseGainCalibration610p: xstart=%d, xend=%d ->left=%d\n",
+ xstart, xend, ((xend + xstart - w) / 2));
+ /* choose best 'left' position */
+ sanei_umax_pp_setLeft ((xend + xstart - w) / 2);
+
+ /* now do VGA calibration for green (=B&W channel) */
+ motor[0] = 0x11;
+ motor[13] = 0x60 | (motor[13] & 0x0F);
+
+ *vgaRed = 0x0F;
+ *vgaGreen = 0x0F;
+ *vgaBlue = 0x0F;
+
+ for (*vgaGreen = 0x0F; *vgaGreen > 0; (*vgaGreen)--)
+ {
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (*vgaRed, *vgaGreen, *vgaBlue, motor);
+
+ lm9811[7] = dcGreen << 4;
+ lm9811[6] = 0x40 | *vgaGreen;
+
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ {
+ CMDSYNC (0x00);
+ }
+
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+
+ min = 0xFF;
+ max = 0x00;
+ for (i = xstart; i <= xend; i++)
+ {
+ if (data[i] < min)
+ min = data[i];
+ if (data[i] > max)
+ max = data[i];
+ }
+ if ((max <= targetCode) && (min > (((float) targetCode) / 2.8)))
+ break;
+ DBG (32, "coarseGainCalibration610p, target/2.8=%f\n",
+ (((float) targetCode) / 2.8));
+ DBG (32, "coarseGainCalibration610p, green: min=%d, max=%d\n", min,
+ max);
+ }
+
+ if (color >= RGB_MODE)
+ {
+ motor[0] = 0x01;
+ motor[13] = 0xA0 | (motor[13] & 0x0F);
+
+ for (*vgaBlue = 0x0F; *vgaBlue > 0; (*vgaBlue)--)
+ {
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (*vgaRed, *vgaGreen, *vgaBlue, motor);
+
+ lm9811[7] = dcBlue << 4;
+ lm9811[6] = 0x40 | *vgaBlue;
+
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ {
+ CMDSYNC (0x00);
+ }
+
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+
+ min = 0xFF;
+ max = 0x00;
+ for (i = xstart; i <= xend; i++)
+ {
+ if (data[i] < min)
+ min = data[i];
+ if (data[i] > max)
+ max = data[i];
+ }
+ if ((max <= targetCode) && (min > (((float) targetCode) / 2.8)))
+ break;
+ DBG (32, "coarseGainCalibration610p, blue: min=%d, max=%d\n", min,
+ max);
+ }
+
+ motor[0] = 0x09;
+ motor[13] = 0xE0 | (motor[13] & 0x0F);
+
+ for (*vgaRed = 0x0F; *vgaRed > 0; (*vgaRed)--)
+ {
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (*vgaRed, *vgaGreen, *vgaBlue, motor);
+
+ lm9811[7] = dcRed << 4;
+ lm9811[6] = 0x40 | *vgaRed;
+
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+
+ COMPLETIONWAIT;
+ CMDGETBUF (4, w, data);
+ if (gMode == UMAX_PP_PARPORT_EPP)
+ {
+ CMDSYNC (0x00);
+ }
+
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w, 1, data, NULL);
+ }
+
+ min = 0xFF;
+ max = 0x00;
+ for (i = xstart; i <= xend; i++)
+ {
+ if (data[i] < min)
+ min = data[i];
+ if (data[i] > max)
+ max = data[i];
+ }
+ if ((max <= targetCode) && (min > (((float) targetCode) / 2.8)))
+ break;
+ DBG (32, "coarseGainCalibration610p, red: min=%d, max=%d\n", min,
+ max);
+ }
+ }
+ else
+ {
+ *vgaRed = 0x0F;
+ *vgaBlue = 0x0F;
+ }
+
+ TRACE (16, "coarseGainCalibration610p end ...\n");
+ return 1;
+}
+
+/* same as above, but for 1220P/1600P/200P */
+static int
+coarseGainCalibration1220p (int color, int dcRed, int dcGreen,
+ int dcBlue, int *vgaRed, int *vgaGreen,
+ int *vgaBlue)
+{
+ unsigned char buffer[5300];
+ int i;
+ double sum;
+ int xstart = 540;
+ int xend = 5100;
+ int commit[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, -1 };
+ int opsc04[9] = { 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x00, 0x00, -1 };
+ int opsc10[9] = { 0x06, 0xF4, 0xFF, 0x81, 0x1B, 0x00, 0x08, 0x00, -1 };
+ int opsc18[17] =
+ { 0x01, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2F, 0x2F, 0x00, 0x88, 0x08,
+ 0x00, 0x80, 0xA4, 0x00, -1
+ };
+ int opsc39[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x40, 0x01,
+ 0x00, 0x00, 0x04, 0x00, 0x6E, 0x41, 0x20, 0x24, 0x06, 0x00, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+ int opsc40[37] =
+ { 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x04, 0x40, 0x01,
+ 0x00, 0x00, 0x04, 0x00, 0x6E, 0x41, 0x60, 0x4F, 0x06, 0x00, 0x00, 0x00,
+ 0x46, 0xA0, 0x00, 0x8B, 0x49, 0x2A, 0xE9, 0x68, 0xDF, 0x93, 0x1A, 0x00,
+ -1
+ };
+ int motor[17] =
+ { 0x09, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2F, 0x2F, 0x00, 0xA5, 0x09,
+ 0x00, 0x40, 0xA4, 0x00, -1
+ };
+
+ DBG (16, "entering coarseGainCalibration1220p() ... (%s:%d) \n", __FILE__,
+ __LINE__);
+
+ /* temporay workaround */
+ color = RGB_MODE;
+
+ /* initialize VGA components */
+ *vgaGreen = 0;
+ *vgaRed = 2;
+ *vgaBlue = 2;
+
+ CMDSETGET (2, 0x10, opsc18);
+ CMDSETGET (8, 0x24, opsc39);
+ opsc04[7] = opsc04[7] & 0x20;
+ opsc04[6] = 0x06; /* one channel gain value */
+ CMDSETGET (1, 0x08, opsc10); /* was opsc04, extraneaous string */
+ /* that prevents using move .... */
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x200, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x200, buffer, NULL);
+
+
+ /* auto correction of gain levels */
+ /* first color component X */
+ if (color >= RGB_MODE)
+ {
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ motor[11] |= 0x20;
+ motor[12] = 0x08;
+ motor[13] |= 0x02;
+
+ opsc04[7] |= 0x20;
+ }
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (*vgaRed, 0, 0, motor);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, 0x24, opsc40);
+ if (DBG_LEVEL >= 128)
+ {
+ bloc2Decode (motor);
+ bloc8Decode (opsc40);
+ }
+ opsc04[6] = *vgaRed;
+ CMDSETGET (1, 0x08, opsc04);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x14B4, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x14B4, buffer, NULL);
+ sum = 0;
+ for (i = xstart; i < xend; i++)
+ sum += buffer[i];
+ sum = sum / (xend - xstart);
+ while ((opsc04[6] < 0x0F) && (sum < 140))
+ {
+ CMDSYNC (0x00);
+ opsc04[6]++;
+ CMDSETGET (1, 0x000008, opsc04);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x0014B4, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x14B4, buffer, NULL);
+ sum = 0;
+ for (i = xstart; i < xend; i++)
+ sum += buffer[i];
+ sum = sum / (xend - xstart);
+ }
+ *vgaRed = opsc04[6];
+
+ /* blue */
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (0, 0, *vgaBlue, motor);
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ motor[11] |= 0x20;
+ motor[12] = 0x08;
+ motor[13] |= 0x02;
+
+ opsc04[7] |= 0x20;
+ }
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, 0x24, opsc40);
+ opsc04[6] = *vgaBlue;
+ CMDSETGET (1, 0x08, opsc04);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x14B4, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x14B4, buffer, NULL);
+ sum = 0;
+ for (i = xstart; i < xend; i++)
+ sum += buffer[i];
+ sum = sum / (xend - xstart);
+ while ((opsc04[6] < 0x0F) && (sum < 140))
+ {
+ CMDSYNC (0x00);
+ opsc04[6]++;
+ CMDSETGET (1, 0x08, opsc04);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x14B4, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x14B4, buffer, NULL);
+ sum = 0;
+ for (i = xstart; i < xend; i++)
+ sum += buffer[i];
+ sum = sum / (xend - xstart);
+ }
+ *vgaBlue = opsc04[6];
+ }
+
+
+ /* component Z: B&W component (green ...) */
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (0, *vgaGreen, 0, motor);
+ if (color < RGB_MODE)
+ motor[0] = 0x01; /* in BW, scan zone doesn't have an extra 4 points */
+ else
+ motor[0] = 0x05; /* extra 4 points */
+ motor[13] = 0xC0; /* B&W */
+ if (sanei_umax_pp_getastra () == 1600)
+ {
+ motor[11] |= 0x20;
+ motor[12] = 0x08;
+ motor[13] |= 0x02;
+
+ opsc04[7] |= 0x20;
+ }
+ CMDSETGET (2, 0x10, motor);
+ if (DBG_LEVEL >= 128)
+ {
+ bloc2Decode (motor);
+ }
+ CMDSETGET (8, 0x24, opsc40);
+ opsc04[6] = *vgaGreen;
+ CMDSETGET (1, 0x08, opsc04);
+ CMDSYNC (0xC2);
+ CMDSYNC (0x00);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+ /* B&W hangs here XXX STEF XXX */
+ CMDGETBUF (4, 0x14B4, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x14B4, buffer, NULL);
+ sum = 0;
+ for (i = xstart; i < xend; i++)
+ sum += buffer[i];
+ sum = sum / (xend - xstart);
+ while ((opsc04[6] < 0x07) && (sum < 110))
+ {
+ CMDSYNC (0x00);
+ opsc04[6]++;
+ CMDSETGET (1, 0x08, opsc04);
+ COMPLETIONWAIT;
+ CMDGETBUF (4, 0x0014B4, buffer);
+ if (DBG_LEVEL >= 128)
+ Dump (0x14B4, buffer, NULL);
+ sum = 0;
+ for (i = xstart; i < xend; i++)
+ sum += buffer[i];
+ sum = sum / (xend - xstart);
+ }
+ *vgaGreen = opsc04[6];
+ DBG (1, "coarseGainCalibration1220p()=%d,%d,%d done ...\n", *vgaRed,
+ *vgaGreen, *vgaBlue);
+ return 1;
+}
+
+/*
+ * generic function
+ */
+static int
+coarseGainCalibration (int color, int dcRed, int dcGreen, int dcBlue,
+ int *vgaRed, int *vgaGreen, int *vgaBlue)
+{
+ if (sanei_umax_pp_getastra () <= 610)
+ {
+ if (coarseGainCalibration610p
+ (color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue) == 0)
+ {
+ DBG (0, "coarseGainCalibration610p failed !!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16,
+ "coarseGainCalibration610p passed ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ else
+ {
+ if (coarseGainCalibration1220p
+ (color, dcRed, dcGreen, dcBlue, vgaRed, vgaGreen, vgaBlue) == 0)
+ {
+ DBG (0, "coarseGainCalibration1220p failed !!! (%s:%d)\n", __FILE__,
+ __LINE__);
+ return 0;
+ }
+ DBG (16,
+ "coarseGainCalibration1220p passed ... (%s:%d)\n", __FILE__,
+ __LINE__);
+ }
+ return 1;
+}
+
+/*
+ * computes PGA offset for each pixel of the ccd.
+ * We scan a white area with PGA=0 and computes the
+ * offset to push the result in the correctable range
+ * returns 1 and PGA values in 'calibration' var on success .
+ * On failure, returns 0.
+ */
+static int
+shadingCalibration610p (int color, int dcRed, int dcGreen, int dcBlue,
+ int vgaRed, int vgaGreen, int vgaBlue,
+ int *calibration)
+{
+ int motor[17] = {
+ 0x5A, 0x80, 0x02, 0x70, 0x00, 0x00, 0xC0, 0x00,
+ 0x17, 0x05, 0x6C, 0xAB, 0xAA, 0x2A, 0xA4, 0x00,
+ -1
+ };
+
+ int ccd[37] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0C,
+ 0x00, 0x04, 0x40, 0x01, 0x00, 0x20, 0x02, 0x00,
+ 0x76, 0x5D, 0x40, 0xA5, 0x06, 0x00, 0x00, 0xE2,
+ 0x5E, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+
+ /*
+ * lm9811[7]= VGA << 4
+ * lm9811[6]= 0x40 | DC offset
+ */
+ int lm9811[9] = {
+ 0x88, 0xE6, 0xFD, 0x8E, 0x30, 0x00, 0x0F, 0x80,
+ -1
+ };
+
+ int commit[9] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ -1
+ };
+
+ int len, dpi, size;
+ int bpp = 3; /* defaults to color scan value */
+ int w, h, x, y;
+ int sum, i;
+ float avg, pct, coeff = 0;
+ unsigned char *data = NULL;
+ int top, bottom;
+
+ TRACE (16, "entering shadingCalibration610p ...\n");
+ len = 0x22;
+ w = 2550;
+ y = 10;
+ dpi = 300;
+ h = 90;
+ top = 8;
+ bottom = 8;
+
+ /* move back first */
+ MOVE (-31, PRECISION_OFF, NULL);
+
+ /* gray scanning handling */
+ if (color < RGB_MODE)
+ {
+ lm9811[7] = dcGreen << 4;
+ lm9811[6] = 0x40 | vgaGreen;
+ bpp = 1;
+
+ motor[13] = 0x6F;
+ }
+
+ data = (unsigned char *) malloc (w * h * bpp);
+ if (data == NULL)
+ {
+ DBG (0, "shadingCalibration610p: failed to allocate memory (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+ memset (data, 0x00, w * h * bpp);
+
+ /* prepare scan command */
+ x = sanei_umax_pp_getLeft ();
+ encodeWX (w, x, dpi, color, ccd, bpp * w);
+ encodeHY (h, y, motor);
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (vgaRed, vgaGreen, vgaBlue, motor);
+ if (DBG_LEVEL > 128)
+ {
+ bloc2Decode (motor);
+ bloc8Decode (ccd);
+ }
+
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSETGET (4, 0x08, commit);
+ COMPLETIONWAIT;
+
+ /* picture height is scan area height minus y */
+ /* then we substract 14 or 6 lines that aren't scanned */
+ if (color < RGB_MODE)
+ h = h - y - 14;
+ else
+ h = h - y - 6;
+ size = w * bpp * h;
+
+ DBG (128,
+ "shadingCalibration610p: trying to read 0x%06X bytes ... (%s:%d)\n",
+ size, __FILE__, __LINE__);
+ /* since we know that each scan line matches CCD width, we signals
+ * that data reading doens't need to sync on each byte, but at each
+ * row end
+ */
+ sanei_umax_pp_setfull (1);
+ CMDGETBUF (4, size, data);
+ sanei_umax_pp_setfull (0);
+
+ /* computes correction here ... */
+ /* debug image files */
+ /* data is in R B G order */
+ if (DBG_LEVEL > 128)
+ DumpNB (w * bpp, h, data, NULL);
+
+ /* zeroes all shading coefficients first */
+ memset (calibration, 0x00, 3 * w * sizeof (int));
+
+ /* in gray scans, we have only green component (i=3) */
+ if (color < RGB_MODE)
+ {
+
+ /* build green only coefficients */
+ for (x = 4; x < w; x++)
+ {
+ sum = 0;
+ for (y = top; y < h - bottom; y++)
+ sum += data[(y * bpp) * w + x];
+ avg = ((float) (sum)) / ((float) (h - (top + bottom)));
+ /* XXX ICI XXX avg=128==>2 */
+ /*coeff = (256.0 * (targetCode / avg - 1.0)) / 2.00;*/
+ coeff = (256.0 * (targetCode / avg - 1.0)) / ((avg*3.5)/targetCode);
+ if (coeff < 0)
+ coeff = 0;
+ if (coeff > 255)
+ coeff = 255;
+ calibration[x + 2 * w - 4] = (int) (coeff + 0.5);
+ }
+ }
+ else
+ {
+ for (i = 0; i < 3; i++)
+ {
+ for (x = 4; x < w; x++)
+ {
+ sum = 0;
+ for (y = top; y < h - bottom; y++)
+ sum += data[(y * bpp + i) * w + x];
+ avg = ((float) (sum)) / ((float) (h - (top + bottom)));
+ /* one step increase means a 0.71% increase of the final
+ pixel */
+ pct = 100.0 - (avg * 100.0) / targetCode;
+ switch (i)
+ {
+ case 0: /* RED 1.80 */
+ case 1: /* BLUE : 2.10 */
+ coeff = (int) (pct / 0.57 + 0.5);
+ break;
+ case 2: /* GREEN 1.50 */
+ coeff = (int) (pct / 0.45 + 0.5);
+ break;
+ }
+ if (coeff < 0)
+ coeff = 0;
+ if (coeff > 255)
+ coeff = 255;
+ calibration[x + i * w - 4] = (int) (coeff + 0.5);
+ }
+ /* 100 in coeffs -> +104 on picture */
+ }
+ }
+
+ /* use default color tables */
+ for (x = 0; x < 256; x++)
+ {
+ calibration[3 * w + x] = ggRed[x];
+ calibration[3 * w + x + 256] = ggGreen[x];
+ calibration[3 * w + x + 512] = ggBlue[x];
+ }
+
+ if (DBG_LEVEL > 128)
+ {
+ DumpNB (w * bpp, h, data, NULL);
+ DumpNB (w, h * bpp, data, NULL);
+ }
+
+ free (data);
+ TRACE (16, "shadingCalibration610p end ...\n");
+ return 1;
+}
+
+/*
+ * build CCD correction: a white area below the top is scanned without
+ * correction, and the data are used to compute the coefficents needed
+ * to correct the light/CCD variations
+ */
+static int
+shadingCalibration (int color, int dcRed, int dcGreen, int dcBlue,
+ int vgaRed, int vgaGreen, int vgaBlue, int *calibration)
+{
+ if (sanei_umax_pp_getastra () < 1220)
+ return shadingCalibration610p (color, dcRed, dcGreen, dcBlue, vgaRed,
+ vgaGreen, vgaBlue, calibration);
+ return shadingCalibration1220p (color, dcRed, dcGreen, dcBlue, vgaRed,
+ vgaGreen, vgaBlue, calibration);
+}
+
+
+/*
+ * this is certainly gamma calibration
+ * We scan a white area with PGA=0 and computes the
+ * offset to push the result in the correctable range
+ * returns 1 and PGA values in 'calibration' var on success .
+ * On failure, returns 0.
+ */
+static int
+leftShadingCalibration610p (int color, int dcRed, int dcGreen, int dcBlue,
+ int vgaRed, int vgaGreen, int vgaBlue,
+ int *calibration)
+{
+ int motor[17] = {
+ 0x14, 0x80, 0x02, 0x60, 0xDE, 0x01, 0xC0, 0x2F,
+ 0x17, 0x00, 0x6C, 0xAB, 0xAA, 0x2A, 0xA4, 0x00,
+ -1
+ };
+
+ int ccd[37] = {
+ 0x00, 0x00, 0xD8, 0x27, 0xEC, 0x53, 0x7D, 0x8A,
+ 0x77, 0xE3, 0x1D, 0x79, 0x07, 0x20, 0x02, 0x00,
+ 0x76, 0x41, 0x80, 0xA3, 0xE5, 0x1D, 0x00, 0xF2,
+ 0x5D, 0xA0, 0x00, 0x8B, 0x4D, 0x4B, 0xD0, 0x68,
+ 0xDF, 0x13, 0x1A, 0x00,
+ -1
+ };
+
+ /*
+ * lm9811[7]= VGA << 4
+ * lm9811[6]= 0x40 | DC offset
+ * lm9811[6].bit7 = use shading data
+ */
+ int lm9811[9] = {
+ 0x88, 0xE6, 0xFD, 0x8E, 0x30, 0x00, 0x8F, 0x80,
+ -1
+ };
+
+ int *commit = NULL;
+
+ int len, dpi, size;
+ int w, h, x, y;
+ int ofst;
+ unsigned char *data = NULL;
+
+ TRACE (16, "entering leftShadingCalibration610p ...\n");
+ if (sanei_umax_pp_getastra () < 1220)
+ {
+ len = 0x22;
+ ofst = 28;
+ w = 2550;
+ x = 94 - ofst; /* left shift compared to shading calibration */
+ y = 10;
+ dpi = 75;
+ h = 20;
+ }
+ else
+ {
+ len = 0x24;
+ ofst = 28;
+ w = 5100;
+ x = 180;
+ y = 10;
+ dpi = 600;
+ h = 67;
+ }
+
+ commit = (int *) malloc ((w * 3 + 5) * sizeof (int));
+ if (commit == NULL)
+ {
+ DBG (0,
+ "leftShadingCalibration610p: failed to allocate memory (%s:%d)\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ data = (unsigned char *) malloc (w * h * 3);
+ if (data == NULL)
+ {
+ DBG (0,
+ "leftShadingCalibration610p: failed to allocate memory (%s:%d)\n",
+ __FILE__, __LINE__);
+ free (commit);
+ return 0;
+ }
+
+ /* prepare scan command */
+ encodeWX (w, x, dpi, color, ccd, 7410);
+ encodeHY (h, y, motor);
+ encodeDC (dcRed, dcGreen, dcBlue, motor);
+ encodeVGA (vgaRed, vgaGreen, vgaBlue, motor);
+ if (DBG_LEVEL > 128)
+ {
+ bloc2Decode (motor);
+ bloc8Decode (ccd);
+ }
+
+ /* build shading calibration data */
+ memset (commit, 0x00, (3 * w + 5) * sizeof (int));
+ for (x = ofst; x < w; x++)
+ {
+ commit[x] = calibration[x - ofst];
+ commit[x + w] = calibration[x - ofst + w];
+ commit[x + 2 * w] = calibration[x - ofst + 2 * w];
+ }
+ /* image data cropping coefficient */
+ commit[3 * w + 3] = 0xFF;
+ commit[3 * w + 4] = 0xFF;
+
+ CMDSYNC (0x00);
+ CMDSETGET (2, 0x10, motor);
+ CMDSETGET (8, len, ccd);
+ CMDSETGET (1, 0x08, lm9811);
+ CMDSYNC (0xC2);
+ CMDSETGET (4, 3 * w + 5, commit);
+ free (commit);
+ COMPLETIONWAIT;
+
+ if (color >= RGB_MODE)
+ {
+ /* picture height is scan area height minus y */
+ h = h - y;
+ size = w * 3 * h;
+ }
+ else
+ {
+ h = h - y - 1;
+ size = w * h;
+ }
+ DBG (128,
+ "leftShadingCalibration610p: trying to read 0x%06X bytes ... (%s:%d)\n",
+ size, __FILE__, __LINE__);
+ CMDGETBUF (4, size, data);
+ if (DBG_LEVEL > 128)
+ DumpNB (3 * w, h, data, NULL);
+
+ /* XXX STEF XXX */
+ /* build coefficients for the 25 left pixels */
+ /* and compute gamma correction ? */
+
+ free (data);
+ TRACE (16, "leftShadingCalibration610p end ...\n");
+ return 1;
+}
diff --git a/backend/umax_pp_low.h b/backend/umax_pp_low.h
new file mode 100644
index 0000000..7cef9dd
--- /dev/null
+++ b/backend/umax_pp_low.h
@@ -0,0 +1,123 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Umax PP flatbed scanners. */
+
+#include <stdio.h>
+#include "../include/sane/config.h"
+
+/*****************************************************************************/
+/* set port to 'idle state' and get iopl */
+/*****************************************************************************/
+extern int sanei_umax_pp_initPort (int port, char *name);
+extern int sanei_umax_pp_initScanner (int recover);
+extern int sanei_umax_pp_initTransport (int recover);
+extern int sanei_umax_pp_endSession (void);
+extern int sanei_umax_pp_initCancel (void);
+extern int sanei_umax_pp_cancel (void);
+extern int sanei_umax_pp_checkModel (void);
+extern int sanei_umax_pp_getauto (void);
+extern int sanei_umax_pp_UTA (void);
+extern void sanei_umax_pp_setauto (int mode);
+
+#ifndef __GLOBALES__
+
+#define RGB_MODE 0x10
+#define RGB12_MODE 0x11
+#define BW_MODE 0x08
+#define BW12_MODE 0x09
+#define BW2_MODE 0x04
+
+
+
+#define __GLOBALES__
+#endif /* __GLOBALES__ */
+
+
+
+#ifndef PRECISION_ON
+#define PRECISION_ON 1
+#define PRECISION_OFF 0
+
+#define LAMP_STATE 0x20
+#define MOTOR_BIT 0x40
+#define ASIC_BIT 0x100
+
+#define UMAX_PP_PARPORT_PS2 0x01
+#define UMAX_PP_PARPORT_BYTE 0x02
+#define UMAX_PP_PARPORT_EPP 0x04
+#define UMAX_PP_PARPORT_ECP 0x08
+
+#endif
+
+extern int sanei_umax_pp_scan (int x, int y, int width, int height, int dpi,
+ int color, int gain, int offset);
+extern int sanei_umax_pp_move (int distance, int precision,
+ unsigned char *buffer);
+extern int sanei_umax_pp_setLamp (int on);
+extern int sanei_umax_pp_completionWait (void);
+extern int sanei_umax_pp_commitScan (void);
+extern int sanei_umax_pp_park (void);
+extern int sanei_umax_pp_parkWait (void);
+extern int sanei_umax_pp_readBlock (long len, int window, int dpi, int last,
+ unsigned char *buffer);
+extern int sanei_umax_pp_startScan (int x, int y, int width, int height,
+ int dpi, int color, int gain,
+ int offset, int *rbpp, int *rtw,
+ int *rth);
+
+extern void sanei_umax_pp_setport (int port);
+extern int sanei_umax_pp_getport (void);
+extern void sanei_umax_pp_setparport (int fd);
+extern int sanei_umax_pp_getparport (void);
+extern void sanei_umax_pp_setastra (int mod);
+extern int sanei_umax_pp_getastra (void);
+extern void sanei_umax_pp_setLeft (int mod);
+extern int sanei_umax_pp_getLeft (void);
+extern void sanei_umax_pp_setfull (int mod);
+extern int sanei_umax_pp_getfull (void);
+extern int sanei_umax_pp_scannerStatus (void);
+extern int sanei_umax_pp_probeScanner (int recover);
+
+extern char **sanei_parport_find_port (void);
+extern char **sanei_parport_find_device (void);
+
+extern int sanei_umax_pp_cmdSync (int cmd);
+extern void sanei_umax_pp_gamma (int *red, int *green, int *blue);
diff --git a/backend/umax_pp_mid.c b/backend/umax_pp_mid.c
new file mode 100644
index 0000000..ee8d98d
--- /dev/null
+++ b/backend/umax_pp_mid.c
@@ -0,0 +1,475 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Umax PP flatbed scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#undef BACKEND_NAME
+#define BACKEND_NAME umax_pp
+
+#include "../include/sane/config.h"
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include "../include/sane/sanei_debug.h"
+
+#define __MAIN__
+
+
+#include "umax_pp_mid.h"
+
+/* this function locks the parallel port so that other devices */
+/* won't interfere. Returns UMAX1220P_BUSY is port cannot be */
+/* lock or UMAX1220P_OK if it is locked */
+static int locked = 0;
+#ifdef HAVE_LINUX_PPDEV_H
+static int exmode = IEEE1284_MODE_COMPAT;
+static int exflags = 0;
+#endif
+
+static int
+lock_parport (void)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int mode, fd;
+#endif
+
+ DBG_INIT ();
+ DBG (3, "lock_parport\n");
+
+#ifdef HAVE_LINUX_PPDEV_H
+ fd = sanei_umax_pp_getparport ();
+ if ((fd > 0) && (!locked))
+ {
+ if (ioctl (sanei_umax_pp_getparport (), PPCLAIM))
+ {
+ return UMAX1220P_BUSY;
+ }
+#ifdef PPGETMODE
+ if (ioctl (fd, PPGETMODE, &exmode))
+ exmode = IEEE1284_MODE_COMPAT;
+ if (ioctl (fd, PPGETFLAGS, &exflags))
+ exflags = 0;
+#endif
+ mode = IEEE1284_MODE_EPP;
+ ioctl (fd, PPNEGOT, &mode);
+ ioctl (fd, PPSETMODE, &mode);
+ locked = 1;
+ }
+#else
+ locked = 1;
+#endif
+ return UMAX1220P_OK;
+}
+
+
+/* this function release parport */
+static int
+unlock_parport (void)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd, mode;
+
+ fd = sanei_umax_pp_getparport ();
+ if ((fd > 0) && (locked))
+ {
+ mode = IEEE1284_MODE_COMPAT;
+ ioctl (fd, PPNEGOT, &mode);
+ ioctl (fd, PPSETMODE, &exmode);
+#ifdef PPSETFLAGS
+ ioctl (fd, PPSETFLAGS, &exflags);
+#endif
+ ioctl (fd, PPRELEASE);
+ locked = 1;
+ }
+#endif
+ DBG (3, "unlock_parport\n");
+ locked = 0;
+ return UMAX1220P_OK;
+}
+
+
+
+
+/*
+ *
+ * This function recognize the scanner model by sending an image
+ * filter command. 1220P will use it as is, but 2000P will return
+ * it back modified.
+ *
+ */
+int
+sanei_umax_pp_model (int port, int *model)
+{
+ int recover = 0, rc;
+
+ /* set up port */
+ DBG (3, "sanei_umax_pp_model\n");
+ sanei_umax_pp_setport (port);
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+
+ /* init transport layer */
+ /* 0: failed
+ 1: success
+ 2: retry
+ 3: busy
+ */
+ do
+ {
+ rc = sanei_umax_pp_initTransport (recover);
+ }
+ while (rc == 2);
+
+ if (rc == 3)
+ {
+ unlock_parport ();
+ return UMAX1220P_BUSY;
+ }
+ if (rc != 1)
+ {
+ DBG (0, "sanei_umax_pp_initTransport() failed (%s:%d)\n", __FILE__,
+ __LINE__);
+ unlock_parport ();
+ return UMAX1220P_TRANSPORT_FAILED;
+ }
+
+ /* check model only, and if only none given in conf file */
+ if (!sanei_umax_pp_getastra ())
+ {
+ rc = sanei_umax_pp_checkModel ();
+ }
+ else
+ {
+ rc = sanei_umax_pp_getastra ();
+ }
+ sanei_umax_pp_endSession ();
+ unlock_parport ();
+ if (rc < 600)
+ {
+ DBG (0, "sanei_umax_pp_CheckModel() failed (%s:%d)\n", __FILE__,
+ __LINE__);
+ return UMAX1220P_PROBE_FAILED;
+ }
+ *model = rc;
+
+
+ /* OK */
+ return UMAX1220P_OK;
+}
+
+int
+sanei_umax_pp_attach (int port, char *name)
+{
+ int recover = 0;
+
+ /* set up port */
+ if (name == NULL)
+ {
+ DBG (3, "sanei_umax_pp_attach(%d,NULL)\n", port);
+ }
+ else
+ {
+ DBG (3, "sanei_umax_pp_attach(%d,%s)\n", port, name);
+ }
+
+ sanei_umax_pp_setport (port);
+ if (sanei_umax_pp_initPort (port, name) != 1)
+ return UMAX1220P_PROBE_FAILED;
+
+ /* init port locks the port, so we flag that */
+ locked = 1;
+
+ if (sanei_umax_pp_probeScanner (recover) != 1)
+ {
+ if (recover)
+ {
+ sanei_umax_pp_initTransport (recover);
+ sanei_umax_pp_endSession ();
+ if (sanei_umax_pp_probeScanner (recover) != 1)
+ {
+ DBG (0, "Recover failed ....\n");
+ unlock_parport ();
+ return UMAX1220P_PROBE_FAILED;
+ }
+ }
+ else
+ {
+ unlock_parport ();
+ return UMAX1220P_PROBE_FAILED;
+ }
+ }
+ sanei_umax_pp_endSession ();
+ unlock_parport ();
+
+
+ /* OK */
+ return UMAX1220P_OK;
+}
+
+
+int
+sanei_umax_pp_open (int port, char *name)
+{
+ int rc;
+ int recover = 0;
+
+ /* set up port */
+ DBG (3, "sanei_umax_pp_open\n");
+
+ if (name == NULL)
+ sanei_umax_pp_setport (port);
+
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+
+ /* init transport layer */
+ /* 0: failed
+ 1: success
+ 2: retry
+ 3: scanner busy
+ */
+ do
+ {
+ rc = sanei_umax_pp_initTransport (recover);
+ }
+ while (rc == 2);
+
+ if (rc == 3)
+ {
+ unlock_parport ();
+ return UMAX1220P_BUSY;
+ }
+
+ if (rc != 1)
+ {
+
+ DBG (0, "sanei_umax_pp_initTransport() failed (%s:%d)\n", __FILE__,
+ __LINE__);
+ unlock_parport ();
+ return UMAX1220P_TRANSPORT_FAILED;
+ }
+ /* init scanner */
+ if (sanei_umax_pp_initScanner (recover) == 0)
+ {
+ DBG (0, "sanei_umax_pp_initScanner() failed (%s:%d)\n", __FILE__,
+ __LINE__);
+ sanei_umax_pp_endSession ();
+ unlock_parport ();
+ return UMAX1220P_SCANNER_FAILED;
+ }
+
+ /* OK */
+ unlock_parport ();
+ return UMAX1220P_OK;
+}
+
+
+int
+sanei_umax_pp_cancel (void)
+{
+ DBG (3, "sanei_umax_pp_cancel\n");
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+
+ /* maybe EPAT reset here if exists */
+ sanei_umax_pp_cmdSync (0xC2);
+ sanei_umax_pp_cmdSync (0x00);
+ sanei_umax_pp_cmdSync (0x00);
+ if (sanei_umax_pp_park () == 0)
+ {
+ DBG (0, "sanei_umax_pp_park failed !!! (%s:%d)\n", __FILE__, __LINE__);
+ unlock_parport ();
+ return UMAX1220P_PARK_FAILED;
+ }
+ /* endSession() cancels any pending command */
+ /* such as parking ...., so we only return */
+ unlock_parport ();
+ return UMAX1220P_OK;
+}
+
+
+
+int
+sanei_umax_pp_start (int x, int y, int width, int height, int dpi, int color,
+ int autoset,
+ int gain, int offset, int *rbpp, int *rtw,
+ int *rth)
+{
+ int col = BW_MODE;
+
+ DBG (3, "sanei_umax_pp_start\n");
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+ /* end session isn't done by cancel any more */
+ sanei_umax_pp_endSession ();
+
+ if (autoset)
+ sanei_umax_pp_setauto (1);
+ else
+ sanei_umax_pp_setauto (0);
+
+ switch (color)
+ {
+ case 0:
+ col = BW2_MODE;
+ break;
+ case 1:
+ col = BW_MODE;
+ break;
+ case 2:
+ col = RGB_MODE;
+ break;
+ }
+
+ if (sanei_umax_pp_startScan
+ (x + sanei_umax_pp_getLeft (), y, width, height, dpi, col, gain,
+ offset, rbpp, rtw, rth) != 1)
+ {
+ sanei_umax_pp_endSession ();
+ unlock_parport ();
+ return UMAX1220P_START_FAILED;
+ }
+ unlock_parport ();
+ return UMAX1220P_OK;
+}
+
+int
+sanei_umax_pp_read (long len, int window, int dpi, int last,
+ unsigned char *buffer)
+{
+ int read = 0;
+ int bytes;
+
+ DBG (3, "sanei_umax_pp_read\n");
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+
+ /* since 610P may override len and last to meet its */
+ /* hardware requirements, we have to loop until we */
+ /* have all the data */
+ while (read < len)
+ {
+ bytes =
+ sanei_umax_pp_readBlock (len - read, window, dpi, last,
+ buffer + read);
+ if (bytes == 0)
+ {
+ sanei_umax_pp_endSession ();
+ return UMAX1220P_READ_FAILED;
+ }
+ read += bytes;
+ }
+ unlock_parport ();
+ return UMAX1220P_OK;
+}
+
+
+
+int
+sanei_umax_pp_lamp (int on)
+{
+ /* init transport layer */
+ DBG (3, "sanei_umax_pp_lamp\n");
+
+ /* no lamp support for 610P ... */
+ if (sanei_umax_pp_getastra () < 1210)
+ return UMAX1220P_OK;
+
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+
+ if (sanei_umax_pp_setLamp (on) == 0)
+ {
+ DBG (0, "Setting lamp state failed!\n");
+ }
+
+ unlock_parport ();
+ return UMAX1220P_OK;
+}
+
+
+
+
+int
+sanei_umax_pp_status (void)
+{
+ int status;
+
+ DBG (3, "sanei_umax_pp_status\n");
+ if (lock_parport () == UMAX1220P_BUSY)
+ return UMAX1220P_BUSY;
+ /* check if head is at home */
+ sanei_umax_pp_cmdSync (0x40);
+ status = sanei_umax_pp_scannerStatus ();
+ unlock_parport ();
+ DBG (8, "sanei_umax_pp_status=0x%02X\n", status);
+ if (((status & ASIC_BIT) != 0x00)||((status & MOTOR_BIT) == 0x00))
+ return UMAX1220P_BUSY;
+
+ return UMAX1220P_OK;
+}
+
+int
+sanei_umax_pp_close ()
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int fd;
+#endif
+
+ DBG (3, "sanei_umax_pp_close\n");
+
+ lock_parport ();
+ sanei_umax_pp_endSession ();
+ unlock_parport ();
+
+#ifdef HAVE_LINUX_PPDEV_H
+ fd = sanei_umax_pp_getparport ();
+ if (fd > 0)
+ {
+ close (fd);
+ sanei_umax_pp_setparport (0);
+ }
+#endif
+ return UMAX1220P_OK;
+}
diff --git a/backend/umax_pp_mid.h b/backend/umax_pp_mid.h
new file mode 100644
index 0000000..32245c6
--- /dev/null
+++ b/backend/umax_pp_mid.h
@@ -0,0 +1,191 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for Umax PP flatbed scanners. */
+
+#ifdef HAVE_LINUX_PPDEV_H
+#include <linux/parport.h>
+#include <linux/ppdev.h>
+#include <sys/ioctl.h>
+#endif
+
+#include "umax_pp_low.h"
+
+#ifndef _UMAX1220P_
+#define _UMAX1220P_
+
+#define UMAX1220P_OK 0
+#define UMAX1220P_NOSCANNER 1
+#define UMAX1220P_TRANSPORT_FAILED 2
+#define UMAX1220P_PROBE_FAILED 3
+#define UMAX1220P_SCANNER_FAILED 4
+#define UMAX1220P_PARK_FAILED 5
+#define UMAX1220P_START_FAILED 6
+#define UMAX1220P_READ_FAILED 7
+#define UMAX1220P_BUSY 8
+
+/*
+ probes the port for an umax1220p scanner
+ initialize transport layer
+ probe scanner
+
+ if name is null, direct I/O is attempted to address given in port
+ else ppdev is tried using the given name as device
+
+ on success returns UMAX1220P_OK,
+ else one of the error above.
+
+*/
+
+extern int sanei_umax_pp_attach (int port, char *name);
+
+/*
+ recognizes 1220P from 2000P
+
+ on success returns UMAX1220P_OK,
+ else one of the error above.
+
+*/
+
+extern int sanei_umax_pp_model (int port, int *model);
+
+
+/*
+if on=1 -> lights scanner lamp
+if on=0 -> lights off scanner lamp
+
+ on success returns UMAX1220P_OK,
+ else one of the error above.
+*/
+
+extern int sanei_umax_pp_lamp (int on);
+
+
+
+/*
+ probes the port for an umax1220p scanner
+ initialize transport layer
+ initialize scanner
+
+ on succes returns UMAX1220P_OK,
+ else one of the error above.
+
+ port: addr when doing direc I/O
+ name: ppdev character device name
+
+*/
+
+extern int sanei_umax_pp_open (int port, char *name);
+
+
+
+/*
+ release any ressource acquired during open
+ since there may be only one scanner, no port parameter
+*/
+extern int sanei_umax_pp_close (void);
+
+
+
+
+/*
+ stops any pending action, then parks the head
+*/
+extern int sanei_umax_pp_cancel (void);
+
+/* starts scanning:
+ - find scanner origin
+ - does channel gain calibration if needed
+ - does calibration
+ - initialize scan operation
+
+ x, y, width and height are expressed in 600 dpi unit
+
+ dpi must be 75, 150, 300, 600 or 1200
+
+ color is true for color scan, false for gray-levels
+
+ gain value is 256*red_gain+16*green_gain+blue_gain
+ if gain is given (ie <> 0), auto gain will not be performed
+
+
+
+ returns UMAX1220P_OK on success, or one of the error above
+ if successful, rbpp holds bytes/pixel, rth the height and rtw
+ the width of scanned area expressed in pixels
+*/
+extern int sanei_umax_pp_start (int x, int y, int width, int height, int dpi,
+ int color, int autoset, int gain,
+ int offset, int *rbpp, int *rtw, int *rth);
+
+
+/* reads one block of data from scanner
+ returns UMAX1220P_OK on success, or UMAX1220P_READ_FAILED on error
+ it also sets internal cancel flag on error
+
+ len if the length of the block needed
+ window if the width in pixels of the scanned area
+ dpi is the resolution, it is used to choose the best read method
+ last is true if it is the last block of the scan
+ buffer will hold the data read
+*/
+extern int sanei_umax_pp_read (long len, int window, int dpi, int last,
+ unsigned char *buffer);
+
+
+
+/* get ASIC status from scanner
+ returns UMAX1220P_OK if scanner idle, or UMAX1220P_BUSY if
+ scanner's motor is on
+*/
+extern int sanei_umax_pp_status (void);
+
+/* set auto calibration 0: no, else yes */
+extern void sanei_umax_pp_setauto (int mode);
+
+/* set umax astra model number */
+extern void sanei_umax_pp_setastra (int val);
+
+/* set gamma tables */
+extern void sanei_umax_pp_gamma (int *red, int *green, int *blue);
+
+/* sets coordinate of first usable left pixel */
+extern int sanei_umax_pp_getLeft (void);
+#endif
diff --git a/backend/v4l-frequencies.h b/backend/v4l-frequencies.h
new file mode 100644
index 0000000..646120e
--- /dev/null
+++ b/backend/v4l-frequencies.h
@@ -0,0 +1,113 @@
+/*
+ * Worldwide channel/frequency list
+ *
+ * Nathan Laredo (laredo@broked.net)
+ *
+ * Frequencies are given in kHz
+ */
+#define NTSC_AUDIO_CARRIER 4500
+#define PAL_AUDIO_CARRIER_I 6000
+#define PAL_AUDIO_CARRIER_BGHN 5500
+#define PAL_AUDIO_CARRIER_MN 4500
+#define PAL_AUDIO_CARRIER_D 6500
+#define SEACAM_AUDIO_DKK1L 6500
+#define SEACAM_AUDIO_BG 5500
+/* NICAM 728 32-kHz, 14-bit digital stereo audio is transmitted in 1ms frames
+ containing 8 bits frame sync, 5 bits control, 11 bits additional data, and
+ 704 bits audio data. The bit rate is reduced by transmitting only 10 bits
+ plus parity of each 14 bit sample, the largest sample in a frame determines
+ which 10 bits are transmitted. The parity bits for audio samples also
+ specify the scaling factor used for that channel during that frame. The
+ companeded audio data is interleaved to reduce the influence of dropouts
+ and the whole frame except for sync bits is scrambled for spectrum shaping.
+ Data is modulated using QPSK, at below following subcarrier freqs */
+#define NICAM728_PAL_BGH 5850
+#define NICAM728_PAL_I 6552
+
+/* COMPREHENSIVE LIST OF FORMAT BY COUNTRY
+ (M) NTSC used in:
+ Antigua, Aruba, Bahamas, Barbados, Belize, Bermuda, Bolivia, Burma,
+ Canada, Chile, Colombia, Costa Rica, Cuba, Curacao, Dominican Republic,
+ Ecuador, El Salvador, Guam Guatemala, Honduras, Jamaica, Japan,
+ South Korea, Mexico, Montserrat, Myanmar, Nicaragua, Panama, Peru,
+ Philippines, Puerto Rico, St Christopher and Nevis, Samoa, Suriname,
+ Taiwan, Trinidad/Tobago, United States, Venezuela, Virgin Islands
+ (B) PAL used in:
+ Albania, Algeria, Australia, Austria, Bahrain, Bangladesh, Belgium,
+ Bosnia-Herzegovinia, Brunei Darussalam, Cambodia, Cameroon, Croatia,
+ Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea, Finland, Germany,
+ Ghana, Gibraltar, Greenland, Iceland, India, Indonesia, Israel, Italy,
+ Jordan, Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysa, Maldives,
+ Malta, Nepal, Netherlands, New Zeland, Nigeria, Norway, Oman, Pakistan,
+ Papua New Guinea, Portugal, Qatar, Sao Tome and Principe, Saudi Arabia,
+ Seychelles, Sierra Leone, Singapore, Slovenia, Somali, Spain,
+ Sri Lanka, Sudan, Swaziland, Sweden, Switzeland, Syria, Thailand,
+ Tunisia, Turkey, Uganda, United Arab Emirates, Yemen
+ (N) PAL used in: (Combination N = 4.5MHz audio carrier, 3.58MHz burst)
+ Argentina (Combination N), Paraguay, Uruguay
+ (M) PAL (525/60, 3.57MHz burst) used in:
+ Brazil
+ (G) PAL used in:
+ Albania, Algeria, Austria, Bahrain, Bosnia/Herzegovinia, Cambodia,
+ Cameroon, Croatia, Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea,
+ Finland, Germany, Gibraltar, Greenland, Iceland, Israel, Italy, Jordan,
+ Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysia, Monaco,
+ Mozambique, Netherlands, New Zealand, Norway, Oman, Pakistan,
+ Papa New Guinea, Portugal, Qatar, Romania, Sierra Leone, Singapore,
+ Slovenia, Somalia, Spain, Sri Lanka, Sudan, Swaziland, Sweeden,
+ Switzerland, Syria, Thailand, Tunisia, Turkey, United Arab Emirates,
+ Yemen, Zambia, Zimbabwe
+ (D) PAL used in:
+ China, North Korea, Romania, Czech Republic
+ (H) PAL used in:
+ Belgium
+ (I) PAL used in:
+ Angola, Botswana, Gambia, Guinea-Bissau, Hong Kong, Ireland, Lesotho,
+ Malawi, Nambia, Nigeria, South Africa, Tanzania, United Kingdom,
+ Zanzibar
+ (B) SECAM used in:
+ Djibouti, Greece, Iran, Iraq, Lebanon, Mali, Mauritania, Mauritus,
+ Morocco
+ (D) SECAM used in:
+ Afghanistan, Armenia, Azerbaijan, Belarus, Bulgaria,
+ Estonia, Georgia, Hungary, Zazakhstan, Lithuania, Mongolia, Moldova,
+ Poland, Russia, Slovak Republic, Ukraine, Vietnam
+ (G) SECAM used in:
+ Greecem Iran, Iraq, Mali, Mauritus, Morocco, Saudi Arabia
+ (K) SECAM used in:
+ Armenia, Azerbaijan, Bulgaria, Estonia, Georgia,
+ Hungary, Kazakhstan, Lithuania, Madagascar, Moldova, Poland, Russia,
+ Slovak Republic, Ukraine, Vietnam
+ (K1) SECAM used in:
+ Benin, Burkina Faso, Burundi, Chad, Cape Verde, Central African
+ Republic, Comoros, Congo, Gabon, Madagascar, Niger, Rwanda, Senegal,
+ Togo, Zaire
+ (L) SECAM used in:
+ France
+*/
+
+/* --------------------------------------------------------------------- */
+
+struct CHANLIST
+{
+ char *name;
+ int freq;
+};
+
+struct CHANLISTS
+{
+ char *name;
+ struct CHANLIST *list;
+ int count;
+};
+
+#define CHAN_COUNT(x) (sizeof(x)/sizeof(struct CHANLIST))
+
+/* --------------------------------------------------------------------- */
+
+extern struct CHANLISTS chanlists[];
+extern struct STRTAB chanlist_names[];
+
+extern int chantab;
+extern struct CHANLIST *chanlist;
+extern int chancount;
diff --git a/backend/v4l.c b/backend/v4l.c
new file mode 100644
index 0000000..38595ed
--- /dev/null
+++ b/backend/v4l.c
@@ -0,0 +1,1137 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1999 Juergen G. Schimmer
+ Updates and bugfixes (C) 2002 - 2004 Henning Meier-Geinitz
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for v4l-Devices.
+*/
+
+#define BUILD 5
+
+#include "../include/sane/config.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#include <sys/ioctl.h>
+#include <asm/types.h> /* XXX glibc */
+
+#define BACKEND_NAME v4l
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "../include/sane/sanei_config.h"
+#define V4L_CONFIG_FILE "v4l.conf"
+
+#include <libv4l1.h>
+#include "v4l.h"
+
+static const SANE_Device **devlist = NULL;
+static int num_devices;
+static V4L_Device *first_dev;
+static V4L_Scanner *first_handle;
+static char *buffer;
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static const SANE_Range u8_range = {
+ /* min, max, quantization */
+ 0, 255, 0
+};
+
+static SANE_Range x_range = { 0, 338, 2 };
+
+static SANE_Range odd_x_range = { 1, 339, 2 };
+
+static SANE_Range y_range = { 0, 249, 1 };
+
+static SANE_Range odd_y_range = { 1, 250, 1 };
+
+
+static SANE_Parameters parms = {
+ SANE_FRAME_RGB,
+ 1, /* 1 = Last Frame , 0 = More Frames to come */
+ 0, /* Number of bytes returned per scan line: */
+ 0, /* Number of pixels per scan line. */
+ 0, /* Number of lines for the current scan. */
+ 8, /* Number of bits per sample. */
+};
+
+static SANE_Status
+attach (const char *devname, V4L_Device ** devp)
+{
+ V4L_Device *dev;
+ static int v4lfd;
+ static struct video_capability capability;
+
+ errno = 0;
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ DBG (5, "attach: device %s is already known\n", devname);
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "attach: trying to open %s\n", devname);
+ v4lfd = v4l1_open (devname, O_RDWR);
+ if (v4lfd != -1)
+ {
+ if (v4l1_ioctl (v4lfd, VIDIOCGCAP, &capability) == -1)
+ {
+ DBG (1,
+ "attach: ioctl (%d, VIDIOCGCAP,..) failed on `%s': %s\n",
+ v4lfd, devname, strerror (errno));
+ v4l1_close (v4lfd);
+ return SANE_STATUS_INVAL;
+ }
+ if (!(VID_TYPE_CAPTURE & capability.type))
+ {
+ DBG (1, "attach: device %s can't capture to memory -- exiting\n",
+ devname);
+ v4l1_close (v4lfd);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ DBG (2, "attach: found videodev `%s' on `%s'\n", capability.name,
+ devname);
+ v4l1_close (v4lfd);
+ }
+ else
+ {
+ DBG (1, "attach: failed to open device `%s': %s\n", devname,
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devname);
+ if (!dev->sane.name)
+ return SANE_STATUS_NO_MEM;
+ dev->sane.vendor = "Noname";
+ dev->sane.model = strdup (capability.name);
+ if (!dev->sane.model)
+ return SANE_STATUS_NO_MEM;
+ dev->sane.type = "virtual device";
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+}
+
+static void
+update_parameters (V4L_Scanner * s)
+{
+ /* ??? should be per-device */
+ x_range.min = 0;
+ x_range.max = s->capability.maxwidth - s->capability.minwidth;
+ x_range.quant = 1;
+
+ y_range.min = 0;
+ y_range.max = s->capability.maxheight - s->capability.minheight;
+ y_range.quant = 1;
+
+ odd_x_range.min = s->capability.minwidth;
+ odd_x_range.max = s->capability.maxwidth;
+ if (odd_x_range.max > 767)
+ {
+ odd_x_range.max = 767;
+ x_range.max = 767 - s->capability.minwidth;
+ };
+ odd_x_range.quant = 1;
+
+ odd_y_range.min = s->capability.minheight;
+ odd_y_range.max = s->capability.maxheight;
+ if (odd_y_range.max > 511)
+ {
+ odd_y_range.max = 511;
+ y_range.max = 511 - s->capability.minheight;
+ };
+ odd_y_range.quant = 1;
+
+ parms.lines = s->window.height;
+ parms.pixels_per_line = s->window.width;
+
+ switch (s->pict.palette)
+ {
+ case VIDEO_PALETTE_GREY: /* Linear greyscale */
+ {
+ parms.format = SANE_FRAME_GRAY;
+ parms.depth = 8;
+ parms.bytes_per_line = s->window.width;
+ break;
+ }
+ case VIDEO_PALETTE_RGB24: /* 24bit RGB */
+ {
+ parms.format = SANE_FRAME_RGB;
+ parms.depth = 8;
+ parms.bytes_per_line = s->window.width * 3;
+ break;
+ }
+ default:
+ {
+ parms.format = SANE_FRAME_GRAY;
+ parms.bytes_per_line = s->window.width;
+ break;
+ }
+ }
+}
+
+static SANE_Status
+init_options (V4L_Scanner * s)
+{
+ int i;
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT);
+ }
+
+ /* Number of options */
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[0]);
+ if (!s->val[OPT_MODE].s)
+ return SANE_STATUS_NO_MEM;
+
+ /* channel */
+ s->opt[OPT_CHANNEL].name = "channel";
+ s->opt[OPT_CHANNEL].title = "Channel";
+ s->opt[OPT_CHANNEL].desc =
+ "Selects the channel of the v4l device (e.g. television " "or video-in.";
+ s->opt[OPT_CHANNEL].type = SANE_TYPE_STRING;
+ s->opt[OPT_CHANNEL].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CHANNEL].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_CHANNEL].constraint.string_list = s->channel;
+ s->val[OPT_CHANNEL].s = strdup (s->channel[0]);
+ if (!s->val[OPT_CHANNEL].s)
+ return SANE_STATUS_NO_MEM;
+ if (s->channel[0] == 0 || s->channel[1] == 0)
+ s->opt[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+/* top-left x *//* ??? first check if window is settable at all */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_X].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_Y].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_X].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &odd_x_range;
+ s->val[OPT_BR_X].w = s->capability.maxwidth;
+ if (s->val[OPT_BR_X].w > 767)
+ s->val[OPT_BR_X].w = 767;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_Y].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &odd_y_range;
+ s->val[OPT_BR_Y].w = s->capability.maxheight;
+ if (s->val[OPT_BR_Y].w > 511)
+ s->val[OPT_BR_Y].w = 511;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range;
+ s->val[OPT_BRIGHTNESS].w = s->pict.brightness / 256;
+
+ /* hue */
+ s->opt[OPT_HUE].name = SANE_NAME_HUE;
+ s->opt[OPT_HUE].title = SANE_TITLE_HUE;
+ s->opt[OPT_HUE].desc = SANE_DESC_HUE;
+ s->opt[OPT_HUE].type = SANE_TYPE_INT;
+ s->opt[OPT_HUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_HUE].constraint.range = &u8_range;
+ s->val[OPT_HUE].w = s->pict.hue / 256;
+
+ /* colour */
+ s->opt[OPT_COLOR].name = "color";
+ s->opt[OPT_COLOR].title = "Picture color";
+ s->opt[OPT_COLOR].desc = "Sets the picture's color.";
+ s->opt[OPT_COLOR].type = SANE_TYPE_INT;
+ s->opt[OPT_COLOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_COLOR].constraint.range = &u8_range;
+ s->val[OPT_COLOR].w = s->pict.colour / 256;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &u8_range;
+ s->val[OPT_CONTRAST].w = s->pict.contrast / 256;
+
+ /* whiteness */
+ s->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
+ s->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
+ s->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL;
+ s->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_WHITE_LEVEL].constraint.range = &u8_range;
+ s->val[OPT_WHITE_LEVEL].w = s->pict.whiteness / 256;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX], *str;
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize; /* stop gcc from complaining */
+ DBG_INIT ();
+
+ DBG (2, "SANE v4l backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ fp = sanei_config_open (V4L_CONFIG_FILE);
+ if (!fp)
+ {
+ DBG (2,
+ "sane_init: file `%s' not accessible (%s), trying /dev/video0\n",
+ V4L_CONFIG_FILE, strerror (errno));
+
+ return attach ("/dev/video0", 0);
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ /* Remove trailing space and trailing comments */
+ for (str = dev_name; *str && !isspace (*str) && *str != '#'; ++str);
+ attach (dev_name, 0);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ V4L_Device *dev, *next;
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+
+ if (NULL != devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+ DBG (5, "sane_exit: all devices freed\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ V4L_Device *dev;
+ int i;
+
+ DBG (5, "sane_get_devices\n");
+ local_only = SANE_TRUE; /* Avoid compile warning */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devname, SANE_Handle * handle)
+{
+ V4L_Device *dev;
+ V4L_Scanner *s;
+ static int v4lfd;
+ int i;
+ struct video_channel channel;
+ SANE_Status status;
+ int max_channels = MAX_CHANNELS;
+
+ if (!devname)
+ {
+ DBG (1, "sane_open: devname == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ DBG (5, "sane_open: device %s found in devlist\n", devname);
+ break;
+ }
+ if (!devname[0])
+ dev = first_dev;
+ if (!dev)
+ {
+ DBG (1, "sane_open: device %s doesn't seem to be a v4l "
+ "device\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ v4lfd = v4l1_open (devname, O_RDWR);
+ if (v4lfd == -1)
+ {
+ DBG (1, "sane_open: can't open %s (%s)\n", devname, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->user_corner = 0; /* ??? */
+ s->devicename = devname;
+ s->fd = v4lfd;
+
+ if (v4l1_ioctl (s->fd, VIDIOCGCAP, &s->capability) == -1)
+ {
+ DBG (1, "sane_open: ioctl (%d, VIDIOCGCAP,..) failed on `%s': %s\n",
+ s->fd, devname, strerror (errno));
+ v4l1_close (s->fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sane_open: %d channels, %d audio devices\n",
+ s->capability.channels, s->capability.audios);
+ DBG (5, "sane_open: minwidth=%d, minheight=%d, maxwidth=%d, "
+ "maxheight=%d\n", s->capability.minwidth, s->capability.minheight,
+ s->capability.maxwidth, s->capability.maxheight);
+ if (VID_TYPE_CAPTURE & s->capability.type)
+ DBG (5, "sane_open: V4L device can capture to memory\n");
+ if (VID_TYPE_TUNER & s->capability.type)
+ DBG (5, "sane_open: V4L device has a tuner of some form\n");
+ if (VID_TYPE_TELETEXT & s->capability.type)
+ DBG (5, "sane_open: V4L device supports teletext\n");
+ if (VID_TYPE_OVERLAY & s->capability.type)
+ DBG (5, "sane_open: V4L device can overlay its image onto the frame "
+ "buffer\n");
+ if (VID_TYPE_CHROMAKEY & s->capability.type)
+ DBG (5, "sane_open: V4L device uses chromakey on overlay\n");
+ if (VID_TYPE_CLIPPING & s->capability.type)
+ DBG (5, "sane_open: V4L device supports overlay clipping\n");
+ if (VID_TYPE_FRAMERAM & s->capability.type)
+ DBG (5, "sane_open: V4L device overwrites frame buffer memory\n");
+ if (VID_TYPE_SCALES & s->capability.type)
+ DBG (5, "sane_open: V4L device supports hardware scaling\n");
+ if (VID_TYPE_MONOCHROME & s->capability.type)
+ DBG (5, "sane_open: V4L device is grey scale only\n");
+ if (VID_TYPE_SUBCAPTURE & s->capability.type)
+ DBG (5, "sane_open: V4L device can capture parts of the image\n");
+
+ if (s->capability.channels < max_channels)
+ max_channels = s->capability.channels;
+ for (i = 0; i < max_channels; i++)
+ {
+ channel.channel = i;
+ if (-1 == v4l1_ioctl (v4lfd, VIDIOCGCHAN, &channel))
+ {
+ DBG (1, "sane_open: can't ioctl VIDIOCGCHAN %s: %s\n", devname,
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "sane_open: channel %d (%s), tuners=%d, flags=0x%x, "
+ "type=%d, norm=%d\n", channel.channel, channel.name,
+ channel.tuners, channel.flags, channel.type, channel.norm);
+ if (VIDEO_VC_TUNER & channel.flags)
+ DBG (5, "sane_open: channel has tuner(s)\n");
+ if (VIDEO_VC_AUDIO & channel.flags)
+ DBG (5, "sane_open: channel has audio\n");
+ if (VIDEO_TYPE_TV == channel.type)
+ DBG (5, "sane_open: input is TV input\n");
+ if (VIDEO_TYPE_CAMERA == channel.type)
+ DBG (5, "sane_open: input is camera input\n");
+ s->channel[i] = strdup (channel.name);
+ if (!s->channel[i])
+ return SANE_STATUS_NO_MEM;
+ }
+ s->channel[i] = 0;
+ if (-1 == v4l1_ioctl (v4lfd, VIDIOCGPICT, &s->pict))
+ {
+ DBG (1, "sane_open: can't ioctl VIDIOCGPICT %s: %s\n", devname,
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "sane_open: brightness=%d, hue=%d, colour=%d, contrast=%d\n",
+ s->pict.brightness, s->pict.hue, s->pict.colour, s->pict.contrast);
+ DBG (5, "sane_open: whiteness=%d, depth=%d, palette=%d\n",
+ s->pict.whiteness, s->pict.depth, s->pict.palette);
+
+ /* ??? */
+ s->pict.palette = VIDEO_PALETTE_GREY;
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCSPICT, &s->pict))
+ {
+ DBG (1, "sane_open: ioctl VIDIOCSPICT failed (%s)\n", strerror (errno));
+ }
+
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCGWIN, &s->window))
+ {
+ DBG (1, "sane_open: can't ioctl VIDIOCGWIN %s: %s\n", devname,
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "sane_open: x=%d, y=%d, width=%d, height=%d\n",
+ s->window.x, s->window.y, s->window.width, s->window.height);
+
+ /* already done in sane_start
+ if (-1 == v4l1_ioctl (v4lfd, VIDIOCGMBUF, &mbuf))
+ DBG (1, "sane_open: can't ioctl VIDIOCGMBUF (no Fbuffer?)\n");
+ */
+
+ status = init_options (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ update_parameters (s);
+
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ V4L_Scanner *prev, *s;
+
+ DBG (2, "sane_close: trying to close handle %p\n", (void *) handle);
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_close: bad handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ if (s->scanning)
+ sane_cancel (handle);
+ v4l1_close (s->fd);
+ free (s);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ V4L_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS || option < 0)
+ return 0;
+ DBG (4, "sane_get_option_descriptor: option %d (%s)\n", option,
+ s->opt[option].name ? s->opt[option].name : s->opt[option].title);
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ V4L_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ if (info)
+ *info = 0;
+
+ if (option >= NUM_OPTIONS || option < 0)
+ return SANE_STATUS_INVAL;
+
+ DBG (4, "sane_control_option: %s option %d (%s)\n",
+ action == SANE_ACTION_GET_VALUE ? "get" :
+ action == SANE_ACTION_SET_VALUE ? "set" :
+ action == SANE_ACTION_SET_AUTO ? "auto set" :
+ "(unknow action with)", option,
+ s->opt[option].name ? s->opt[option].name : s->opt[option].title);
+
+ cap = s->opt[option].cap;
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (1, "sane_control option: option is inactive\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_BRIGHTNESS:
+ case OPT_HUE:
+ case OPT_COLOR:
+ case OPT_CONTRAST:
+ case OPT_WHITE_LEVEL:
+ *(SANE_Word *) val = s->val[option].w;
+ return SANE_STATUS_GOOD;
+ case OPT_CHANNEL: /* string list options */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ return SANE_STATUS_GOOD;
+ default:
+ DBG (1, "sane_control_option: option %d unknown\n", option);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (1, "sane_control_option: option is not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_control_option: sanei_constarin_value failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (option >= OPT_TL_X && option <= OPT_BR_Y)
+ {
+ s->user_corner |= 1 << (option - OPT_TL_X);
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCGWIN, &s->window))
+ {
+ DBG (1, "sane_control_option: ioctl VIDIOCGWIN failed "
+ "(can not get window geometry)\n");
+ return SANE_STATUS_INVAL;
+ }
+ s->window.clipcount = 0;
+ s->window.clips = 0;
+ s->window.height = parms.lines;
+ s->window.width = parms.pixels_per_line;
+ }
+
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_TL_X:
+ break;
+ case OPT_TL_Y:
+ break;
+ case OPT_BR_X:
+ s->window.width = *(SANE_Word *) val;
+ parms.pixels_per_line = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_BR_Y:
+ s->window.height = *(SANE_Word *) val;
+ parms.lines = *(SANE_Word *) val;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_MODE:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ s->pict.palette = VIDEO_PALETTE_GREY;
+ else
+ s->pict.palette = VIDEO_PALETTE_RGB24;
+ update_parameters (s);
+ break;
+ case OPT_BRIGHTNESS:
+ s->pict.brightness = *(SANE_Word *) val *256;
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_HUE:
+ s->pict.hue = *(SANE_Word *) val *256;
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_COLOR:
+ s->pict.colour = *(SANE_Word *) val *256;
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_CONTRAST:
+ s->pict.contrast = *(SANE_Word *) val *256;
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_WHITE_LEVEL:
+ s->pict.whiteness = *(SANE_Word *) val *256;
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ case OPT_CHANNEL:
+ {
+ int i;
+ struct video_channel channel;
+
+ s->val[option].s = strdup (val);
+ if (!s->val[option].s)
+ return SANE_STATUS_NO_MEM;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ if (strcmp (s->channel[i], val) == 0)
+ {
+ channel.channel = i;
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCGCHAN, &channel))
+ {
+ DBG (1, "sane_open: can't ioctl VIDIOCGCHAN %s: %s\n",
+ s->devicename, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCSCHAN, &channel))
+ {
+ DBG (1, "sane_open: can't ioctl VIDIOCSCHAN %s: %s\n",
+ s->devicename, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ break;
+ }
+ default:
+ DBG (1, "sane_control_option: option %d unknown\n", option);
+ return SANE_STATUS_INVAL;
+ }
+ if (option >= OPT_TL_X && option <= OPT_BR_Y)
+ {
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCSWIN, &s->window))
+ {
+ DBG (1, "sane_control_option: ioctl VIDIOCSWIN failed (%s)\n",
+ strerror (errno));
+ /* return SANE_STATUS_INVAL; */
+ }
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCGWIN, &s->window))
+ {
+ DBG (1, "sane_control_option: ioctl VIDIOCGWIN failed (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ }
+ if (option >= OPT_BRIGHTNESS && option <= OPT_WHITE_LEVEL)
+ {
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCSPICT, &s->pict))
+ {
+ DBG (1, "sane_control_option: ioctl VIDIOCSPICT failed (%s)\n",
+ strerror (errno));
+ /* return SANE_STATUS_INVAL; */
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+ else if (action == SANE_ACTION_SET_AUTO)
+ {
+ if (!(cap & SANE_CAP_AUTOMATIC))
+ {
+ DBG (1, "sane_control_option: option can't be set automatically\n");
+ return SANE_STATUS_INVAL;
+ }
+ switch (option)
+ {
+ case OPT_BRIGHTNESS:
+ /* not implemented yet */
+ return SANE_STATUS_GOOD;
+
+ default:
+ break;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ V4L_Scanner *s = handle;
+
+ DBG (4, "sane_get_parameters\n");
+ update_parameters (s);
+ if (params == 0)
+ {
+ DBG (1, "sane_get_parameters: params == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCGWIN, &s->window))
+ {
+ DBG (1, "sane_control_option: ioctl VIDIOCGWIN failed "
+ "(can not get window geometry)\n");
+ return SANE_STATUS_INVAL;
+ }
+ parms.pixels_per_line = s->window.width;
+ parms.bytes_per_line = s->window.width;
+ if (parms.format == SANE_FRAME_RGB)
+ parms.bytes_per_line = s->window.width * 3;
+ parms.lines = s->window.height;
+ *params = parms;
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int len, loop;
+ V4L_Scanner *s;
+ char data;
+
+ DBG (2, "sane_start\n");
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ }
+ if (!s)
+ {
+ DBG (1, "sane_start: bad handle %p\n", handle);
+ return SANE_STATUS_INVAL; /* oops, not a handle we know about */
+ }
+ len = v4l1_ioctl (s->fd, VIDIOCGCAP, &s->capability);
+ if (-1 == len)
+ {
+ DBG (1, "sane_start: can not get capabilities\n");
+ return SANE_STATUS_INVAL;
+ }
+ s->buffercount = 0;
+ if (-1 == v4l1_ioctl (s->fd, VIDIOCGMBUF, &s->mbuf))
+ {
+ s->is_mmap = SANE_FALSE;
+ buffer =
+ malloc (s->capability.maxwidth * s->capability.maxheight *
+ s->pict.depth);
+ if (0 == buffer)
+ return SANE_STATUS_NO_MEM;
+ DBG (3, "sane_start: V4L trying to read frame\n");
+ len = v4l1_read (s->fd, buffer, parms.bytes_per_line * parms.lines);
+ DBG (3, "sane_start: %d bytes read\n", len);
+ }
+ else
+ {
+ s->is_mmap = SANE_TRUE;
+ DBG (3,
+ "sane_start: mmap frame, buffersize: %d bytes, buffers: %d, offset 0 %d\n",
+ s->mbuf.size, s->mbuf.frames, s->mbuf.offsets[0]);
+ buffer =
+ v4l1_mmap (0, s->mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0);
+ if (buffer == (void *)-1)
+ {
+ DBG (1, "sane_start: mmap failed: %s\n", strerror (errno));
+ buffer = NULL;
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (3, "sane_start: mmapped frame, capture 1 pict into %p\n", buffer);
+ s->mmap.frame = 0;
+ s->mmap.width = s->window.width;
+ /* s->mmap.width = parms.pixels_per_line; ??? huh? */
+ s->mmap.height = s->window.height;
+ /* s->mmap.height = parms.lines; ??? huh? */
+ s->mmap.format = s->pict.palette;
+ DBG (2, "sane_start: mmapped frame %d x %d with palette %d\n",
+ s->mmap.width, s->mmap.height, s->mmap.format);
+
+ /* We need to loop here to empty the read buffers, so we don't
+ get a stale image */
+ for (loop = 0; loop <= s->mbuf.frames; loop++)
+ {
+ len = v4l1_ioctl (s->fd, VIDIOCMCAPTURE, &s->mmap);
+ if (len == -1)
+ {
+ DBG (1, "sane_start: ioctl VIDIOCMCAPTURE failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ DBG (3, "sane_start: waiting for frame %x, loop %d\n", s->mmap.frame, loop);
+ len = v4l1_ioctl (s->fd, VIDIOCSYNC, &(s->mmap.frame));
+ if (-1 == len)
+ {
+ DBG (1, "sane_start: call to ioctl(%d, VIDIOCSYNC, ..) failed\n",
+ s->fd);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ DBG (3, "sane_start: frame %x done\n", s->mmap.frame);
+ }
+
+ /* v4l1 actually returns BGR when we ask for RGB, so convert it */
+ if (s->pict.palette == VIDEO_PALETTE_RGB24)
+ {
+ DBG (3, "sane_start: converting from BGR to RGB\n");
+ for (loop = 0; loop < (s->window.width * s->window.height * 3); loop += 3)
+ {
+ data = *(buffer + loop);
+ *(buffer + loop) = *(buffer + loop + 2);
+ *(buffer + loop + 2) = data;
+ }
+ }
+
+ DBG (3, "sane_start: done\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * lenp)
+{
+ int i, min;
+ V4L_Scanner *s = handle;
+
+ DBG (4, "sane_read: max_len = %d\n", max_len);
+ if (!lenp)
+ {
+ DBG (1, "sane_read: lenp == 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ((s->buffercount + 1) > (parms.lines * parms.bytes_per_line))
+ {
+ *lenp = 0;
+ return SANE_STATUS_EOF;
+ };
+ min = parms.lines * parms.bytes_per_line;
+ if (min > (max_len + s->buffercount))
+ min = (max_len + s->buffercount);
+ if (s->is_mmap == SANE_FALSE)
+ {
+ for (i = s->buffercount; i < (min + 0); i++)
+ {
+ *(buf + i - s->buffercount) = *(buffer + i);
+ };
+ *lenp = (parms.lines * parms.bytes_per_line - s->buffercount);
+ if (max_len < *lenp)
+ *lenp = max_len;
+ DBG (3, "sane_read: transferred %d bytes (from %d to %d)\n", *lenp,
+ s->buffercount, i);
+ s->buffercount = i;
+ }
+ else
+ {
+ for (i = s->buffercount; i < (min + 0); i++)
+ {
+ *(buf + i - s->buffercount) = *(buffer + i);
+ };
+ *lenp = (parms.lines * parms.bytes_per_line - s->buffercount);
+ if ((i - s->buffercount) < *lenp)
+ *lenp = (i - s->buffercount);
+ DBG (3, "sane_read: transferred %d bytes (from %d to %d)\n", *lenp,
+ s->buffercount, i);
+ s->buffercount = i;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ V4L_Scanner *s = handle;
+
+ DBG (2, "sane_cancel\n");
+
+ /* ??? buffer isn't checked in sane_read? */
+ if (buffer)
+ {
+ if (s->is_mmap)
+ v4l1_munmap(buffer, s->mbuf.size);
+ else
+ free (buffer);
+
+ buffer = NULL;
+ }
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ /* Avoid compile warning */
+ handle = 0;
+
+ if (non_blocking == SANE_FALSE)
+ return SANE_STATUS_GOOD;
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ /* Avoid compile warning */
+ handle = 0;
+ fd = 0;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
diff --git a/backend/v4l.conf.in b/backend/v4l.conf.in
new file mode 100644
index 0000000..bb55f1f
--- /dev/null
+++ b/backend/v4l.conf.in
@@ -0,0 +1,10 @@
+#
+# In order to use the v4linux backend you have to give the device
+# You can enable multiple lines if
+# you really have multible v4l devices.
+#
+/dev/bttv0
+/dev/video0
+/dev/video1
+/dev/video2
+/dev/video3
diff --git a/backend/v4l.h b/backend/v4l.h
new file mode 100644
index 0000000..6aee586
--- /dev/null
+++ b/backend/v4l.h
@@ -0,0 +1,242 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ Updates and bugfixes (C) 2002. 2003 Henning Meier-Geinitz
+
+ This file is part of the SANE package.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef v4l_h
+#define v4l_h
+
+#ifndef __LINUX_VIDEODEV_H
+/* Kernel interface */
+/* Only the stuff we need. For more features, more defines are needed */
+
+#define VID_TYPE_CAPTURE 1 /* Can capture */
+#define VID_TYPE_TUNER 2 /* Can tune */
+#define VID_TYPE_TELETEXT 4 /* Does teletext */
+#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */
+#define VID_TYPE_CLIPPING 32 /* Can clip */
+#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */
+#define VID_TYPE_SCALES 128 /* Scalable */
+#define VID_TYPE_MONOCHROME 256 /* Monochrome only */
+#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */
+#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */
+#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */
+#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */
+#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */
+
+struct video_capability
+{
+ char name[32];
+ int type;
+ int channels; /* Num channels */
+ int audios; /* Num audio devices */
+ int maxwidth; /* Supported width */
+ int maxheight; /* And height */
+ int minwidth; /* Supported width */
+ int minheight; /* And height */
+};
+
+struct video_picture
+{
+ __u16 brightness;
+ __u16 hue;
+ __u16 colour;
+ __u16 contrast;
+ __u16 whiteness; /* Black and white only */
+ __u16 depth; /* Capture depth */
+ __u16 palette; /* Palette in use */
+#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */
+#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */
+#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */
+#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */
+#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */
+#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */
+#define VIDEO_PALETTE_YUYV 8
+#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */
+#define VIDEO_PALETTE_YUV420 10
+#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */
+#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */
+#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */
+#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */
+};
+
+struct video_window
+{
+ __u32 x,y; /* Position of window */
+ __u32 width,height; /* Its size */
+ __u32 chromakey;
+ __u32 flags;
+ struct video_clip *clips; /* Set only */
+ int clipcount;
+#define VIDEO_WINDOW_INTERLACE 1
+#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */
+#define VIDEO_CLIP_BITMAP -1
+/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
+#define VIDEO_CLIPMAP_SIZE (128 * 625)
+};
+
+#define VIDEO_MAX_FRAME 32
+
+struct video_mbuf
+{
+ int size; /* Total memory to map */
+ int frames; /* Frames */
+ int offsets[VIDEO_MAX_FRAME];
+};
+
+struct video_mmap
+{
+ unsigned int frame; /* Frame (0 - n) for double buffer */
+ int height,width;
+ unsigned int format; /* should be VIDEO_PALETTE_* */
+};
+
+struct video_channel
+{
+ int channel;
+ char name[32];
+ int tuners;
+ __u32 flags;
+#define VIDEO_VC_TUNER 1 /* Channel has a tuner */
+#define VIDEO_VC_AUDIO 2 /* Channel has audio */
+ __u16 type;
+#define VIDEO_TYPE_TV 1
+#define VIDEO_TYPE_CAMERA 2
+ __u16 norm; /* Norm set by channel */
+};
+
+#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */
+#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */
+#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */
+#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */
+#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */
+#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */
+#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */
+#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */
+#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */
+#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
+#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */
+#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */
+#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
+#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */
+#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */
+#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */
+#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */
+#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */
+#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */
+#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */
+#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */
+#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */
+#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */
+#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */
+#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */
+#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */
+#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */
+#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */
+#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */
+
+
+/* end of kernel interface */
+#endif /* !__LINUX_VIDEODEV_H */
+
+#include <../include/sane/sane.h>
+
+#define MAX_CHANNELS 32
+
+typedef enum
+{
+ V4L_RES_LOW = 0,
+ V4L_RES_HIGH
+}
+V4L_Resolution;
+
+typedef enum
+{
+ OPT_NUM_OPTS = 0,
+
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_CHANNEL,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X, /* top-left x */
+ OPT_TL_Y, /* top-left y */
+ OPT_BR_X, /* bottom-right x */
+ OPT_BR_Y, /* bottom-right y */
+
+ OPT_ENHANCEMENT_GROUP,
+ OPT_BRIGHTNESS,
+ OPT_HUE,
+ OPT_COLOR,
+ OPT_CONTRAST,
+ OPT_WHITE_LEVEL,
+
+ /* must come last: */
+ NUM_OPTIONS
+}
+V4L_Option;
+
+typedef struct V4L_Device
+{
+ struct V4L_Device *next;
+ SANE_Device sane;
+}
+V4L_Device;
+
+typedef struct V4L_Scanner
+{
+ struct V4L_Scanner *next;
+
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ V4L_Resolution resolution;
+ SANE_Parameters params;
+ SANE_String_Const devicename; /* Name of the Device */
+ int fd; /* Filedescriptor */
+ SANE_Int user_corner; /* bitmask of user-selected coordinates */
+ SANE_Bool scanning;
+ SANE_Bool deliver_eof;
+ SANE_Bool is_mmap; /* Do we use mmap ? */
+ /* state for reading a frame: */
+ size_t num_bytes; /* # of bytes read so far */
+ size_t bytes_per_frame; /* total number of bytes in frame */
+ struct video_capability capability;
+ struct video_picture pict;
+ struct video_window window;
+ struct video_mbuf mbuf;
+ struct video_mmap mmap;
+ SANE_String_Const channel[MAX_CHANNELS];
+ SANE_Int buffercount;
+}
+V4L_Scanner;
+
+#endif /* v4l_h */
diff --git a/backend/xerox_mfp-tcp.c b/backend/xerox_mfp-tcp.c
new file mode 100644
index 0000000..2addb0d
--- /dev/null
+++ b/backend/xerox_mfp-tcp.c
@@ -0,0 +1,182 @@
+/*
+ * SANE backend for
+ * Samsung SCX-4500W
+ *
+ * Network Scanners Support
+ * Copyright 2010 Alexander Kuznetsov <acca(at)cpan.org>
+ *
+ * This program is licensed under GPL + SANE exception.
+ * More info at http://www.sane-project.org/license.html
+ *
+ */
+
+#undef BACKEND_NAME
+#define BACKEND_NAME xerox_mfp
+#define DEBUG_DECLARE_ONLY
+#define DEBUG_NOT_STATIC
+
+#include "sane/config.h"
+
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "sane/saneopts.h"
+#include "sane/sanei_scsi.h"
+#include "sane/sanei_usb.h"
+#include "sane/sanei_pio.h"
+#include "sane/sanei_tcp.h"
+#include "sane/sanei_udp.h"
+#include "sane/sanei_backend.h"
+#include "sane/sanei_config.h"
+
+#include "xerox_mfp.h"
+
+
+#define RECV_TIMEOUT 1 /* seconds */
+extern int sanei_debug_xerox_mfp;
+
+int tcp_dev_request (struct device *dev,
+ SANE_Byte *cmd, size_t cmdlen,
+ SANE_Byte *resp, size_t *resplen)
+{
+ size_t bytes_recv = 0;
+ ssize_t rc = 1;
+ size_t len;
+
+
+ /* Send request, if any */
+ if (cmd && cmdlen) {
+ len = (size_t)sanei_tcp_write(dev->dn, cmd, cmdlen);
+ if (len != cmdlen) {
+ DBG (1, "%s: sent only %lu bytes of %lu\n",
+ __FUNCTION__, (u_long)len, (u_long)cmdlen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* Receive response, if expected */
+ if (resp && resplen) {
+ DBG (3, "%s: wait for %i bytes\n", __FUNCTION__, (int)*resplen);
+
+ while (bytes_recv < *resplen && rc > 0) {
+ rc = recv(dev->dn, resp+bytes_recv, *resplen-bytes_recv, 0);
+
+ if (rc > 0) bytes_recv += rc;
+ else {
+ DBG(1, "%s: error %s, bytes requested: %i, bytes read: %i\n",
+ __FUNCTION__, strerror(errno), (int)*resplen, (int)bytes_recv);
+ *resplen = bytes_recv;
+/*
+ TODO:
+ do something smarter than that!
+*/
+ return SANE_STATUS_GOOD;
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ *resplen = bytes_recv;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status tcp_dev_open (struct device *dev)
+{
+ SANE_Status status;
+ char* strhost;
+ char* strport;
+ int port;
+ struct servent *sp;
+ struct timeval tv;
+ SANE_String_Const devname;
+
+
+ devname = dev->sane.name;
+ DBG (3, "%s: open %s\n", __FUNCTION__, devname);
+
+ if (strncmp (devname, "tcp", 3) != 0) return SANE_STATUS_INVAL;
+ devname += 3;
+ devname = sanei_config_skip_whitespace (devname);
+ if (!*devname) return SANE_STATUS_INVAL;
+
+ devname = sanei_config_get_string (devname, &strhost);
+ devname = sanei_config_skip_whitespace (devname);
+
+ if (*devname)
+ devname = sanei_config_get_string (devname, &strport);
+ else
+ strport = "9400";
+
+
+ if (isdigit(*strport)) {
+ port = atoi(strport);
+ } else {
+ if ((sp = getservbyname(strport, "tcp"))) {
+ port = ntohs(sp->s_port);
+ } else {
+ DBG (1, "%s: unknown TCP service %s\n", __FUNCTION__, strport);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ status = sanei_tcp_open(strhost, port, &dev->dn);
+ if (status == SANE_STATUS_GOOD) {
+ tv.tv_sec = RECV_TIMEOUT;
+ tv.tv_usec = 0;
+ if (setsockopt (dev->dn, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv) < 0) {
+ DBG(1, "%s: setsockopts %s", __FUNCTION__, strerror(errno));
+ }
+ }
+
+ return status;
+}
+
+void
+tcp_dev_close (struct device *dev)
+{
+ if (!dev) return;
+
+ DBG (3, "%s: closing dev %p\n", __FUNCTION__, (void *)dev);
+
+ /* finish all operations */
+ if (dev->scanning) {
+ dev->cancel = 1;
+ /* flush READ_IMAGE data */
+ if (dev->reading) sane_read(dev, NULL, 1, NULL);
+ /* send cancel if not sent before */
+ if (dev->state != SANE_STATUS_CANCELLED)
+ ret_cancel(dev, 0);
+ }
+
+ sanei_tcp_close(dev->dn);
+ dev->dn = -1;
+}
+
+
+SANE_Status
+tcp_configure_device (const char *devname, SANE_Status (*list_one)(SANE_String_Const devname))
+{
+/*
+ TODO: LAN scanners multicast discovery.
+ devname would contain "tcp auto"
+
+ We find new devnames and feed them to
+ `list_one_device' one by one
+*/
+ return list_one(devname);
+}
+
+/* xerox_mfp-tcp.c */
diff --git a/backend/xerox_mfp-usb.c b/backend/xerox_mfp-usb.c
new file mode 100644
index 0000000..b9b56d8
--- /dev/null
+++ b/backend/xerox_mfp-usb.c
@@ -0,0 +1,112 @@
+/*
+ * SANE backend for Xerox Phaser 3200MFP
+ * Copyright 2008 ABC <abc@telekom.ru>
+ *
+ * This program is licensed under GPL + SANE exception.
+ * More info at http://www.sane-project.org/license.html
+ */
+
+#undef BACKEND_NAME
+#define BACKEND_NAME xerox_mfp
+#define DEBUG_DECLARE_ONLY
+#define DEBUG_NOT_STATIC
+#include "sane/config.h"
+#include "sane/saneopts.h"
+#include "sane/sanei_config.h"
+#include "sane/sanei_backend.h"
+#include "sane/sanei_debug.h"
+#include "sane/sanei_usb.h"
+#include "xerox_mfp.h"
+
+
+extern int sanei_debug_xerox_mfp;
+
+int
+usb_dev_request (struct device *dev,
+ SANE_Byte *cmd, size_t cmdlen,
+ SANE_Byte *resp, size_t *resplen)
+{
+ SANE_Status status;
+ size_t len = cmdlen;
+
+ if (cmd && cmdlen) {
+ status = sanei_usb_write_bulk (dev->dn, cmd, &cmdlen);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: sanei_usb_write_bulk: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (cmdlen != len) {
+ DBG (1, "%s: sanei_usb_write_bulk: wanted %lu bytes, wrote %lu bytes\n",
+ __FUNCTION__, (size_t)len, (size_t)cmdlen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (resp && resplen) {
+ status = sanei_usb_read_bulk (dev->dn, resp, resplen);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: sanei_usb_read_bulk: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usb_dev_open (struct device *dev)
+{
+ SANE_Status status;
+
+ DBG (3, "%s: open %p\n", __FUNCTION__, (void *)dev);
+ status = sanei_usb_open (dev->sane.name, &dev->dn);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: sanei_usb_open(%s): %s\n", __FUNCTION__,
+ dev->sane.name, sane_strstatus (status));
+ dev->dn = -1;
+ return status;
+ }
+ sanei_usb_clear_halt (dev->dn);
+ return SANE_STATUS_GOOD;
+}
+
+void
+usb_dev_close (struct device *dev)
+{
+ if (!dev)
+ return;
+ DBG (3, "%s: closing dev %p\n", __FUNCTION__, (void *)dev);
+
+ /* finish all operations */
+ if (dev->scanning) {
+ dev->cancel = 1;
+ /* flush READ_IMAGE data */
+ if (dev->reading)
+ sane_read(dev, NULL, 1, NULL);
+ /* send cancel if not sent before */
+ if (dev->state != SANE_STATUS_CANCELLED)
+ ret_cancel(dev, 0);
+ }
+
+ sanei_usb_clear_halt (dev->dn); /* unstall for next users */
+ sanei_usb_close (dev->dn);
+ dev->dn = -1;
+}
+
+
+/* SANE API ignores return code of this callback */
+SANE_Status
+usb_configure_device (const char *devname, SANE_Status (*attach) (const char *dev))
+{
+ sanei_usb_set_timeout (1000);
+ sanei_usb_attach_matching_devices (devname, attach);
+ sanei_usb_set_timeout (30000);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* xerox_mfp-usb.c */
diff --git a/backend/xerox_mfp.c b/backend/xerox_mfp.c
new file mode 100644
index 0000000..2ef7078
--- /dev/null
+++ b/backend/xerox_mfp.c
@@ -0,0 +1,1343 @@
+/*
+ * SANE backend for Xerox Phaser 3200MFP
+ * Copyright 2008 ABC <abc@telekom.ru>
+ *
+ * Network Scanners Support
+ * Copyright 2010 Alexander Kuznetsov <acca(at)cpan.org>
+ *
+ * This program is licensed under GPL + SANE exception.
+ * More info at http://www.sane-project.org/license.html
+ */
+
+#define DEBUG_NOT_STATIC
+#define BACKEND_NAME xerox_mfp
+
+#include "../include/sane/config.h"
+#include "../include/lassert.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_thread.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_backend.h"
+#include "xerox_mfp.h"
+
+#define BACKEND_BUILD 13
+#define XEROX_CONFIG_FILE "xerox_mfp.conf"
+
+static const SANE_Device **devlist = NULL; /* sane_get_devices array */
+static struct device *devices_head = NULL; /* sane_get_devices list */
+
+enum { TRANSPORT_USB, TRANSPORT_TCP, TRANSPORTS_MAX };
+transport available_transports[TRANSPORTS_MAX] = {
+ { "usb", usb_dev_request, usb_dev_open, usb_dev_close, usb_configure_device },
+ { "tcp", tcp_dev_request, tcp_dev_open, tcp_dev_close, tcp_configure_device },
+};
+
+static int resolv_state(int state)
+{
+ if (state & STATE_DOCUMENT_JAM)
+ return SANE_STATUS_JAMMED;
+ if (state & STATE_NO_DOCUMENT)
+ return SANE_STATUS_NO_DOCS;
+ if (state & STATE_COVER_OPEN)
+ return SANE_STATUS_COVER_OPEN;
+ if (state & STATE_INVALID_AREA)
+ return SANE_STATUS_INVAL; /* sane_start: implies SANE_INFO_RELOAD_OPTIONS */
+ if (state & STATE_WARMING)
+#ifdef SANE_STATUS_WARMING_UP
+ return SANE_STATUS_WARMING_UP;
+#else
+ return SANE_STATUS_DEVICE_BUSY;
+#endif
+ if (state & STATE_LOCKING)
+#ifdef SANE_STATUS_HW_LOCKED
+ return SANE_STATUS_HW_LOCKED;
+#else
+ return SANE_STATUS_JAMMED;
+#endif
+ if (state & ~STATE_NO_ERROR)
+ return SANE_STATUS_DEVICE_BUSY;
+ return 0;
+}
+
+static char *str_cmd(int cmd)
+{
+ switch (cmd) {
+ case CMD_ABORT: return "ABORT";
+ case CMD_INQUIRY: return "INQUIRY";
+ case CMD_RESERVE_UNIT: return "RESERVE_UNIT";
+ case CMD_RELEASE_UNIT: return "RELEASE_UNIT";
+ case CMD_SET_WINDOW: return "SET_WINDOW";
+ case CMD_READ: return "READ";
+ case CMD_READ_IMAGE: return "READ_IMAGE";
+ case CMD_OBJECT_POSITION: return "OBJECT_POSITION";
+ }
+ return "unknown";
+}
+
+#define MAX_DUMP 70
+static void dbg_dump(struct device *dev)
+{
+ int i;
+ char dbuf[MAX_DUMP * 3 + 1], *dptr = dbuf;
+ int nzlen = dev->reslen;
+ int dlen = MIN(dev->reslen, MAX_DUMP);
+
+ for (i = dev->reslen - 1; i >= 0; i--, nzlen--)
+ if (dev->res[i] != 0)
+ break;
+
+ dlen = MIN(dlen, nzlen + 1);
+
+ for (i = 0; i < dlen; i++, dptr += 3)
+ sprintf(dptr, " %02x", dev->res[i]);
+
+ DBG (5, "[%lu]%s%s\n", (u_long)dev->reslen, dbuf,
+ (dlen < (int)dev->reslen)? "..." : "");
+}
+
+/* one command to device */
+/* return 0: on error, 1: success */
+static int dev_command (struct device *dev, SANE_Byte * cmd, size_t reqlen)
+{
+ SANE_Status status;
+ size_t sendlen = cmd[3] + 4;
+ SANE_Byte *res = dev->res;
+
+
+ assert (reqlen <= sizeof (dev->res)); /* requested len */
+ dev->reslen = sizeof (dev->res); /* doing full buffer to flush stalled commands */
+
+ if (cmd[2] == CMD_SET_WINDOW) {
+ /* Set Window have wrong packet length, huh. */
+ sendlen = 25;
+ }
+
+ if (cmd[2] == CMD_READ_IMAGE) {
+ /* Read Image is raw data, don't need to read response */
+ res = NULL;
+ }
+
+ dev->state = 0;
+ DBG (4, ":: dev_command(%s[%#x], %lu)\n", str_cmd(cmd[2]), cmd[2],
+ (u_long)reqlen);
+ status = dev->io->dev_request(dev, cmd, sendlen, res, &dev->reslen);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: dev_request: %s\n", __FUNCTION__, sane_strstatus (status));
+ dev->state = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+
+ if (!res) {
+ /* if not need response just return success */
+ return 1;
+ }
+
+ /* normal command reply, some sanity checking */
+ if (dev->reslen < reqlen) {
+ DBG (1, "%s: illegal response len %lu, need %lu\n",
+ __FUNCTION__, (u_long)dev->reslen, (u_long)reqlen);
+ dev->state = SANE_STATUS_IO_ERROR;
+ return 0;
+ } else {
+ size_t pktlen; /* len specified in packet */
+
+ if (DBG_LEVEL > 3)
+ dbg_dump(dev);
+
+ if (dev->res[0] != RES_CODE) {
+ DBG (2, "%s: illegal data header %02x\n", __FUNCTION__, dev->res[0]);
+ dev->state = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+ pktlen = dev->res[2] + 3;
+ if (dev->reslen != pktlen) {
+ DBG (2, "%s: illegal response len %lu, should be %lu\n",
+ __FUNCTION__, (u_long)pktlen, (u_long)dev->reslen);
+ dev->state = SANE_STATUS_IO_ERROR;
+ return 0;
+ }
+ if (dev->reslen > reqlen)
+ DBG (2, "%s: too big packet len %lu, need %lu\n",
+ __FUNCTION__, (u_long)dev->reslen, (u_long)reqlen);
+ }
+
+ dev->state = 0;
+ if (cmd[2] == CMD_SET_WINDOW ||
+ cmd[2] == CMD_OBJECT_POSITION ||
+ cmd[2] == CMD_READ ||
+ cmd[2] == CMD_RESERVE_UNIT) {
+ if (dev->res[1] == STATUS_BUSY)
+ dev->state = SANE_STATUS_DEVICE_BUSY;
+ else if (dev->res[1] == STATUS_CANCEL)
+ dev->state = SANE_STATUS_CANCELLED;
+ else if (dev->res[1] == STATUS_CHECK)
+ dev->state = resolv_state((cmd[2] == CMD_READ)?
+ (dev->res[12] << 8 | dev->res[13]) :
+ (dev->res[4] << 8 | dev->res[5]));
+
+ if (dev->state)
+ DBG (3, "%s(%s[%#x]): => %d: %s\n",
+ __FUNCTION__, str_cmd(cmd[2]), cmd[2],
+ dev->state, sane_strstatus(dev->state));
+ }
+
+ return 1;
+}
+
+/* one short command to device */
+static int dev_cmd (struct device *dev, SANE_Byte command)
+{
+ SANE_Byte cmd[4] = { REQ_CODE_A, REQ_CODE_B };
+ cmd[2] = command;
+ return dev_command (dev, cmd, (command == CMD_INQUIRY)? 70 : 32);
+}
+
+/* stop scanning operation. return previous status */
+static SANE_Status dev_stop(struct device *dev)
+{
+ int state = dev->state;
+
+ DBG (3, "%s: %p, scanning %d, reserved %d\n", __FUNCTION__,
+ (void *)dev, dev->scanning, dev->reserved);
+ dev->scanning = 0;
+
+ /* release */
+ if (!dev->reserved)
+ return state;
+ dev->reserved = 0;
+ dev_cmd(dev, CMD_RELEASE_UNIT);
+ DBG (3, "total image %d*%d size %d (win %d*%d), %d*%d %d data: %d, out %d bytes\n",
+ dev->para.pixels_per_line, dev->para.lines,
+ dev->total_img_size,
+ dev->win_width, dev->win_len,
+ dev->pixels_per_line, dev->ulines, dev->blocks,
+ dev->total_data_size, dev->total_out_size);
+ dev->state = state;
+ return state;
+}
+
+SANE_Status ret_cancel(struct device *dev, SANE_Status ret)
+{
+ dev_cmd(dev, CMD_ABORT);
+ if (dev->scanning) {
+ dev_stop(dev);
+ dev->state = SANE_STATUS_CANCELLED;
+ }
+ return ret;
+}
+
+static int cancelled(struct device *dev)
+{
+ if (dev->cancel)
+ return ret_cancel(dev, 1);
+ return 0;
+}
+
+/* issue command and wait until scanner is not busy */
+/* return 0 on error/blocking, 1 is ok and ready */
+static int dev_cmd_wait(struct device *dev, int cmd)
+{
+ int sleeptime = 10;
+
+ do {
+ if (cancelled(dev))
+ return 0;
+ if (!dev_cmd(dev, cmd)) {
+ dev->state = SANE_STATUS_IO_ERROR;
+ return 0;
+ } else if (dev->state) {
+ if (dev->state != SANE_STATUS_DEVICE_BUSY)
+ return 0;
+ else {
+ if (dev->non_blocking) {
+ dev->state = SANE_STATUS_GOOD;
+ return 0;
+ } else {
+ if (sleeptime > 1000)
+ sleeptime = 1000;
+ DBG (4, "(%s) sleeping(%d ms).. [%x %x]\n",
+ str_cmd(cmd), sleeptime, dev->res[4], dev->res[5]);
+ usleep(sleeptime * 1000);
+ if (sleeptime < 1000)
+ sleeptime *= (sleeptime < 100)? 10 : 2;
+ }
+ } /* BUSY */
+ }
+ } while (dev->state == SANE_STATUS_DEVICE_BUSY);
+
+ return 1;
+}
+
+static int inq_dpi_bits[] = {
+ 75, 150, 0, 0,
+ 200, 300, 0, 0,
+ 600, 0, 0, 1200,
+ 100, 0, 0, 2400,
+ 0, 4800, 0, 9600
+};
+
+static int res_dpi_codes[] = {
+ 75, 0, 150, 0,
+ 0, 300, 0, 600,
+ 1200, 200, 100, 2400,
+ 4800, 9600
+};
+
+static int SANE_Word_sort(const void * a, const void * b)
+{
+ return *(const SANE_Word *)a - *(const SANE_Word *)b;
+}
+
+/* resolve inquired dpi list to dpi_list array */
+static void resolv_inq_dpi(struct device *dev)
+{
+ unsigned int i;
+ int res = dev->resolutions;
+
+ assert(sizeof(inq_dpi_bits) < sizeof(dev->dpi_list));
+ for (i = 0; i < sizeof(inq_dpi_bits) / sizeof(int); i++)
+ if (inq_dpi_bits[i] && (res & (1 << i)))
+ dev->dpi_list[++dev->dpi_list[0]] = inq_dpi_bits[i];
+ qsort(&dev->dpi_list[1], dev->dpi_list[0], sizeof(SANE_Word), SANE_Word_sort);
+}
+
+static unsigned int dpi_to_code(int dpi)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(res_dpi_codes) / sizeof(int); i++) {
+ if (dpi == res_dpi_codes[i])
+ return i;
+ }
+ return 0;
+}
+
+static int string_match_index(const SANE_String_Const s[], SANE_String m)
+{
+ int i;
+
+ for (i = 0; *s; i++) {
+ SANE_String_Const x = *s++;
+ if (strcasecmp(x, m) == 0)
+ return i;
+ }
+ return 0;
+}
+
+static SANE_String string_match(const SANE_String_Const s[], SANE_String m)
+{
+ return UNCONST(s[string_match_index(s, m)]);
+}
+
+static size_t max_string_size (SANE_String_Const s[])
+{
+ size_t max = 0;
+
+ while (*s) {
+ size_t size = strlen(*s++) + 1;
+ if (size > max)
+ max = size;
+ }
+ return max;
+}
+
+static SANE_String_Const doc_sources[] = {
+ "Flatbed", "ADF", "Auto", NULL
+};
+
+static int doc_source_to_code[] = {
+ 0x40, 0x20, 0x80
+};
+
+static SANE_String_Const scan_modes[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_HALFTONE,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static int scan_mode_to_code[] = {
+ 0x00, 0x01, 0x03, 0x05
+};
+
+static SANE_Range threshold = {
+ SANE_FIX(30), SANE_FIX(70), SANE_FIX(10)
+};
+
+static void reset_options(struct device *dev)
+{
+ dev->val[OPT_RESOLUTION].w = 150;
+ dev->val[OPT_MODE].s = string_match(scan_modes, SANE_VALUE_SCAN_MODE_COLOR);
+
+ /* if docs loaded in adf use it as default source, flatbed oterwise */
+ dev->val[OPT_SOURCE].s = UNCONST(doc_sources[(dev->doc_loaded)? 1 : 0]);
+
+ dev->val[OPT_THRESHOLD].w = SANE_FIX(50);
+
+ /* this is reported maximum window size, will be fixed later */
+ dev->win_x_range.min = SANE_FIX(0);
+ dev->win_x_range.max = SANE_FIX((double)dev->max_win_width / PNT_PER_MM);
+ dev->win_x_range.quant = SANE_FIX(1);
+ dev->win_y_range.min = SANE_FIX(0);
+ dev->win_y_range.max = SANE_FIX((double)dev->max_win_len / PNT_PER_MM);
+ dev->win_y_range.quant = SANE_FIX(1);
+ dev->val[OPT_SCAN_TL_X].w = dev->win_x_range.min;
+ dev->val[OPT_SCAN_TL_Y].w = dev->win_y_range.min;
+ dev->val[OPT_SCAN_BR_X].w = dev->win_x_range.max;
+ dev->val[OPT_SCAN_BR_Y].w = dev->win_y_range.max;
+}
+
+static void init_options(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < NUM_OPTIONS; i++) {
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->opt[i].size = sizeof(SANE_Word);
+ dev->opt[i].type = SANE_TYPE_FIXED;
+ dev->val[i].s = NULL;
+ }
+
+ dev->opt[OPT_NUMOPTIONS].name = SANE_NAME_NUM_OPTIONS;
+ dev->opt[OPT_NUMOPTIONS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUMOPTIONS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUMOPTIONS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUMOPTIONS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUMOPTIONS].w = NUM_OPTIONS;
+
+ dev->opt[OPT_GROUP_STD].name = SANE_NAME_STANDARD;
+ dev->opt[OPT_GROUP_STD].title = SANE_TITLE_STANDARD;
+ dev->opt[OPT_GROUP_STD].desc = SANE_DESC_STANDARD;
+ dev->opt[OPT_GROUP_STD].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GROUP_STD].cap = 0;
+
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list = dev->dpi_list;
+
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size(scan_modes);
+ dev->opt[OPT_MODE].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_modes;
+
+ dev->opt[OPT_THRESHOLD].name = SANE_NAME_HIGHLIGHT;
+ dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ dev->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_THRESHOLD].constraint.range = &threshold;
+
+ dev->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ dev->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ dev->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ dev->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_SOURCE].size = max_string_size(doc_sources);
+ dev->opt[OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
+ dev->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_SOURCE].constraint.string_list = doc_sources;
+
+ dev->opt[OPT_GROUP_GEO].name = SANE_NAME_GEOMETRY;
+ dev->opt[OPT_GROUP_GEO].title = SANE_TITLE_GEOMETRY;
+ dev->opt[OPT_GROUP_GEO].desc = SANE_DESC_GEOMETRY;
+ dev->opt[OPT_GROUP_GEO].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_GROUP_GEO].cap = 0;
+
+ dev->opt[OPT_SCAN_TL_X].name = SANE_NAME_SCAN_TL_X;
+ dev->opt[OPT_SCAN_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ dev->opt[OPT_SCAN_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ dev->opt[OPT_SCAN_TL_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_SCAN_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_SCAN_TL_X].constraint.range = &dev->win_x_range;
+
+ dev->opt[OPT_SCAN_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ dev->opt[OPT_SCAN_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ dev->opt[OPT_SCAN_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ dev->opt[OPT_SCAN_TL_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_SCAN_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_SCAN_TL_Y].constraint.range = &dev->win_y_range;
+
+ dev->opt[OPT_SCAN_BR_X].name = SANE_NAME_SCAN_BR_X;
+ dev->opt[OPT_SCAN_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ dev->opt[OPT_SCAN_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ dev->opt[OPT_SCAN_BR_X].unit = SANE_UNIT_MM;
+ dev->opt[OPT_SCAN_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_SCAN_BR_X].constraint.range = &dev->win_x_range;
+
+ dev->opt[OPT_SCAN_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ dev->opt[OPT_SCAN_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ dev->opt[OPT_SCAN_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ dev->opt[OPT_SCAN_BR_Y].unit = SANE_UNIT_MM;
+ dev->opt[OPT_SCAN_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_SCAN_BR_Y].constraint.range = &dev->win_y_range;
+}
+
+/* fill parameters from options */
+static void set_parameters(struct device *dev)
+{
+ double px_to_len;
+
+ dev->para.last_frame = SANE_TRUE;
+ dev->para.lines = -1;
+ px_to_len = 1200.0 / dev->val[OPT_RESOLUTION].w;
+#define BETTER_BASEDPI 1
+ /* tests prove that 1200dpi base is very inexact
+ * so I calculated better values for each axis */
+#if BETTER_BASEDPI
+ px_to_len = 1180.0 / dev->val[OPT_RESOLUTION].w;
+#endif
+ dev->para.pixels_per_line = dev->win_width / px_to_len;
+ dev->para.bytes_per_line = dev->para.pixels_per_line;
+#if BETTER_BASEDPI
+ px_to_len = 1213.9 / dev->val[OPT_RESOLUTION].w;
+#endif
+ dev->para.lines = dev->win_len / px_to_len;
+ if (dev->composition == MODE_LINEART ||
+ dev->composition == MODE_HALFTONE) {
+ dev->para.format = SANE_FRAME_GRAY;
+ dev->para.depth = 1;
+ dev->para.bytes_per_line = (dev->para.pixels_per_line + 7) / 8;
+ } else if (dev->composition == MODE_GRAY8) {
+ dev->para.format = SANE_FRAME_GRAY;
+ dev->para.depth = 8;
+ dev->para.bytes_per_line = dev->para.pixels_per_line;
+ } else if (dev->composition == MODE_RGB24) {
+ dev->para.format = SANE_FRAME_RGB;
+ dev->para.depth = 8;
+ dev->para.bytes_per_line *= 3;
+ } else {
+ /* this will never happen */
+ DBG (1, "%s: impossible image composition %d\n",
+ __FUNCTION__, dev->composition);
+ dev->para.format = SANE_FRAME_GRAY;
+ dev->para.depth = 8;
+ }
+}
+
+/* resolve all options related to scan window */
+/* called after option changed and in set_window */
+static int fix_window(struct device *dev)
+{
+ double win_width_mm, win_len_mm;
+ int i;
+ int threshold = SANE_UNFIX(dev->val[OPT_THRESHOLD].w);
+
+ dev->resolution = dpi_to_code(dev->val[OPT_RESOLUTION].w);
+ dev->composition = scan_mode_to_code[string_match_index(scan_modes, dev->val[OPT_MODE].s)];
+
+ if (dev->composition == MODE_LINEART ||
+ dev->composition == MODE_HALFTONE) {
+ dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ } else {
+ dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+ if (threshold < 30) {
+ dev->val[OPT_THRESHOLD].w = SANE_FIX(30);
+ } else if (threshold > 70) {
+ dev->val[OPT_THRESHOLD].w = SANE_FIX(70);
+ }
+ threshold = SANE_UNFIX(dev->val[OPT_THRESHOLD].w);
+ dev->threshold = (threshold - 30) / 10;
+ dev->val[OPT_THRESHOLD].w = SANE_FIX(dev->threshold * 10 + 30);
+
+ dev->doc_source = doc_source_to_code[string_match_index(doc_sources, dev->val[OPT_SOURCE].s)];
+
+ /* max window len is dependent of document source */
+ if (dev->doc_source == DOC_FLATBED ||
+ (dev->doc_source == DOC_AUTO && !dev->doc_loaded))
+ dev->max_len = dev->max_len_fb;
+ else
+ dev->max_len = dev->max_len_adf;
+
+ /* parameters */
+ dev->win_y_range.max = SANE_FIX((double)dev->max_len / PNT_PER_MM);
+
+ /* window sanity checking */
+ for (i = OPT_SCAN_TL_X; i <= OPT_SCAN_BR_Y; i++) {
+ if (dev->val[i].w < dev->opt[i].constraint.range->min)
+ dev->val[i].w = dev->opt[i].constraint.range->min;
+ if (dev->val[i].w > dev->opt[i].constraint.range->max)
+ dev->val[i].w = dev->opt[i].constraint.range->max;
+ }
+
+ if (dev->val[OPT_SCAN_TL_X].w > dev->val[OPT_SCAN_BR_X].w)
+ SWAP_Word(dev->val[OPT_SCAN_TL_X].w, dev->val[OPT_SCAN_BR_X].w);
+ if (dev->val[OPT_SCAN_TL_Y].w > dev->val[OPT_SCAN_BR_Y].w)
+ SWAP_Word(dev->val[OPT_SCAN_TL_Y].w, dev->val[OPT_SCAN_BR_Y].w);
+
+ /* recalculate millimeters to inches */
+ dev->win_off_x = SANE_UNFIX(dev->val[OPT_SCAN_TL_X].w) / MM_PER_INCH;
+ dev->win_off_y = SANE_UNFIX(dev->val[OPT_SCAN_TL_Y].w) / MM_PER_INCH;
+
+ /* calc win size in mm */
+ win_width_mm = SANE_UNFIX(dev->val[OPT_SCAN_BR_X].w) -
+ SANE_UNFIX(dev->val[OPT_SCAN_TL_X].w);
+ win_len_mm = SANE_UNFIX(dev->val[OPT_SCAN_BR_Y].w) -
+ SANE_UNFIX(dev->val[OPT_SCAN_TL_Y].w);
+ /* convert mm to 1200 dpi points */
+ dev->win_width = (int)(win_width_mm * PNT_PER_MM);
+ dev->win_len = (int)(win_len_mm * PNT_PER_MM);
+
+ /* don't scan if window is zero size */
+ if (!dev->win_width || !dev->win_len) {
+ /* "The scan cannot be started with the current set of options." */
+ dev->state = SANE_STATUS_INVAL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int dev_set_window (struct device *dev)
+{
+ SANE_Byte cmd[0x19] = {
+ REQ_CODE_A, REQ_CODE_B, CMD_SET_WINDOW, 0x13, MSG_SCANNING_PARAM
+ };
+
+ if (!fix_window(dev))
+ return 0;
+
+ cmd[0x05] = dev->win_width >> 24;
+ cmd[0x06] = dev->win_width >> 16;
+ cmd[0x07] = dev->win_width >> 8;
+ cmd[0x08] = dev->win_width;
+ cmd[0x09] = dev->win_len >> 24;
+ cmd[0x0a] = dev->win_len >> 16;
+ cmd[0x0b] = dev->win_len >> 8;
+ cmd[0x0c] = dev->win_len;
+ cmd[0x0d] = dev->resolution; /* x */
+ cmd[0x0e] = dev->resolution; /* y */
+ cmd[0x0f] = (SANE_Byte)floor(dev->win_off_x);
+ cmd[0x10] = (SANE_Byte)((dev->win_off_x - floor(dev->win_off_x)) * 100);
+ cmd[0x11] = (SANE_Byte)floor(dev->win_off_y);
+ cmd[0x12] = (SANE_Byte)((dev->win_off_y - floor(dev->win_off_y)) * 100);
+ cmd[0x13] = dev->composition;
+ cmd[0x16] = dev->threshold;
+ cmd[0x17] = dev->doc_source;
+
+ DBG (5, "OFF xi: %02x%02x yi: %02x%02x,"
+ " WIN xp: %02x%02x%02x%02x yp %02x%02x%02x%02x,"
+ " MAX %08x %08x\n",
+ cmd[0x0f], cmd[0x10], cmd[0x11], cmd[0x12],
+ cmd[0x05], cmd[0x06], cmd[0x07], cmd[0x08],
+ cmd[0x09], cmd[0x0a], cmd[0x0b], cmd[0x0c],
+ dev->max_win_width, dev->max_win_len);
+
+ return dev_command (dev, cmd, 32);
+}
+
+static SANE_Status
+dev_inquiry (struct device *dev)
+{
+ SANE_Byte *ptr;
+ SANE_Char *optr, *xptr;
+
+ if (!dev_cmd (dev, CMD_INQUIRY))
+ return SANE_STATUS_IO_ERROR;
+ ptr = dev->res;
+ if (ptr[3] != MSG_PRODUCT_INFO) {
+ DBG (1, "%s: illegal INQUIRY response %02x\n", __FUNCTION__, ptr[3]);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* parse reported manufacturer/product names */
+ dev->sane.vendor = optr = (SANE_Char *) malloc (33);
+ for (ptr += 4; ptr < &dev->res[0x24] && *ptr && *ptr != ' ';)
+ *optr++ = *ptr++;
+ *optr++ = 0;
+
+ for (; ptr < &dev->res[0x24] && (!*ptr || *ptr == ' '); ptr++)
+ /* skip spaces */;
+
+ dev->sane.model = optr = (SANE_Char *) malloc (33);
+ xptr = optr; /* is last non space character + 1 */
+ for (; ptr < &dev->res[0x24] && *ptr;) {
+ if (*ptr != ' ')
+ xptr = optr + 1;
+ *optr++ = *ptr++;
+ }
+ *optr++ = 0;
+ *xptr = 0;
+
+ DBG (1, "%s: found %s/%s\n", __FUNCTION__, dev->sane.vendor, dev->sane.model);
+ dev->sane.type = strdup ("multi-function peripheral");
+
+ dev->resolutions = dev->res[0x37] << 16 |
+ dev->res[0x24] << 8 |
+ dev->res[0x25];
+ dev->compositions = dev->res[0x27];
+ dev->max_win_width = dev->res[0x28] << 24 |
+ dev->res[0x29] << 16 |
+ dev->res[0x2a] << 8 |
+ dev->res[0x2b];
+ dev->max_win_len = dev->res[0x2c] << 24 |
+ dev->res[0x2d] << 16 |
+ dev->res[0x2e] << 8 |
+ dev->res[0x2f];
+ dev->max_len_adf = dev->res[0x38] << 24 |
+ dev->res[0x39] << 16 |
+ dev->res[0x3a] << 8 |
+ dev->res[0x3b];
+ dev->max_len_fb = dev->res[0x3c] << 24 |
+ dev->res[0x3d] << 16 |
+ dev->res[0x3e] << 8 |
+ dev->res[0x3f];
+ dev->line_order = dev->res[0x31];
+ dev->doc_loaded = (dev->res[0x35] == 0x02) &&
+ (dev->res[0x26] & 0x03);
+
+ init_options(dev);
+ reset_options(dev);
+ fix_window(dev);
+ set_parameters(dev);
+ resolv_inq_dpi(dev);
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int opt)
+{
+ struct device *dev = h;
+
+ DBG (3, "%s: %p, %d\n", __FUNCTION__, h, opt);
+ if (opt >= NUM_OPTIONS || opt < 0)
+ return NULL;
+ return &dev->opt[opt];
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int opt, SANE_Action act,
+ void *val, SANE_Word * info)
+{
+ struct device *dev = h;
+
+ DBG (3, "%s: %p, %d, <%d>, %p, %p\n", __FUNCTION__, h, opt, act, val, (void *)info);
+ if (!dev || opt >= NUM_OPTIONS || opt < 0)
+ return SANE_STATUS_INVAL;
+
+ if (info)
+ *info = 0;
+
+ if (act == SANE_ACTION_GET_VALUE) { /* GET */
+ if (dev->opt[opt].type == SANE_TYPE_STRING)
+ strcpy(val, dev->val[opt].s);
+ else
+ *(SANE_Word *)val = dev->val[opt].w;
+ } else if (act == SANE_ACTION_SET_VALUE) { /* SET */
+ SANE_Parameters xpara = dev->para;
+ SANE_Option_Descriptor xopt[NUM_OPTIONS];
+ Option_Value xval[NUM_OPTIONS];
+ int i;
+
+ if (dev->opt[opt].constraint_type == SANE_CONSTRAINT_STRING_LIST) {
+ dev->val[opt].s = string_match(dev->opt[opt].constraint.string_list, val);
+ if (info && strcasecmp(dev->val[opt].s, val))
+ *info |= SANE_INFO_INEXACT;
+ } else if (opt == OPT_RESOLUTION)
+ dev->val[opt].w = res_dpi_codes[dpi_to_code(*(SANE_Word *)val)];
+ else
+ dev->val[opt].w = *(SANE_Word *)val;
+
+ memcpy(&xopt, &dev->opt, sizeof(xopt));
+ memcpy(&xval, &dev->val, sizeof(xval));
+ fix_window(dev);
+ set_parameters(dev);
+
+ /* check for side effects */
+ if (info) {
+ if (memcmp(&xpara, &dev->para, sizeof(xpara)))
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ if (memcmp(&xopt, &dev->opt, sizeof(xopt)))
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ for (i = 0; i < NUM_OPTIONS; i++)
+ if (xval[i].w != dev->val[i].w) {
+ if (i == opt)
+ *info |= SANE_INFO_INEXACT;
+ else
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ }
+ }
+
+ DBG (4, "%s: %d, <%d> => %08x, %x\n", __FUNCTION__, opt, act,
+ val? *(SANE_Word *)val : 0, info? *info : 0);
+ return SANE_STATUS_GOOD;
+}
+
+static void
+dev_free (struct device *dev)
+{
+ if (!dev)
+ return;
+
+ if (dev->sane.name)
+ free (UNCONST(dev->sane.name));
+ if (dev->sane.vendor)
+ free (UNCONST(dev->sane.vendor));
+ if (dev->sane.model)
+ free (UNCONST(dev->sane.model));
+ if (dev->sane.type)
+ free (UNCONST(dev->sane.type));
+ if (dev->data)
+ free(dev->data);
+ memset (dev, 0, sizeof (*dev));
+ free (dev);
+}
+
+static void
+free_devices (void)
+{
+ int i;
+ struct device *next;
+ struct device *dev;
+
+ if (devlist) {
+ free (devlist);
+ devlist = NULL;
+ }
+ for (i = 0, dev = devices_head; dev; dev = next) {
+ next = dev->next;
+ dev_free (dev);
+ }
+ devices_head = NULL;
+}
+
+static transport *tr_from_devname(SANE_String_Const devname)
+{
+ if (strncmp("tcp", devname, 3) == 0)
+ return &available_transports[TRANSPORT_TCP];
+ return &available_transports[TRANSPORT_USB];
+}
+
+static SANE_Status
+list_one_device (SANE_String_Const devname)
+{
+ struct device *dev;
+ SANE_Status status;
+ transport *tr;
+
+ DBG (4, "%s: %s\n", __FUNCTION__, devname);
+
+ for (dev = devices_head; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, devname) == 0)
+ return SANE_STATUS_GOOD;
+ }
+
+ tr = tr_from_devname(devname);
+
+ dev = calloc (1, sizeof (struct device));
+ if (dev == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->sane.name = strdup (devname);
+ dev->io = tr;
+ status = tr->dev_open (dev);
+ if (status != SANE_STATUS_GOOD) {
+ dev_free (dev);
+ return status;
+ }
+
+/* status = dev_cmd (dev, CMD_ABORT);*/
+ status = dev_inquiry (dev);
+ tr->dev_close (dev);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "%s: dev_inquiry(%s): %s\n", __FUNCTION__,
+ dev->sane.name, sane_strstatus (status));
+ dev_free (dev);
+ return status;
+ }
+
+ /* good device, add it to list */
+ dev->next = devices_head;
+ devices_head = dev;
+ return SANE_STATUS_GOOD;
+}
+
+/* SANE API ignores return code of this callback */
+static SANE_Status
+list_conf_devices (UNUSED (SANEI_Config * config), const char *devname)
+{
+ return tr_from_devname(devname)->configure_device(devname, list_one_device);
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback cb)
+{
+ DBG_INIT ();
+ DBG (2, "sane_init: Xerox backend (build %d), version %s null, authorize %s null\n", BACKEND_BUILD,
+ (version_code) ? "!=" : "==", (cb) ? "!=" : "==");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BACKEND_BUILD);
+
+ sanei_usb_init ();
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ struct device *dev;
+
+ for (dev = devices_head; dev; dev = dev->next)
+ if (dev->dn != -1)
+ sane_close(dev); /* implies flush */
+
+ free_devices ();
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local)
+{
+ SANEI_Config config;
+ struct device *dev;
+ int dev_count;
+ int i;
+
+ DBG (3, "%s: %p, %d\n", __FUNCTION__, (const void *)device_list, local);
+
+ if (devlist) {
+ if (device_list)
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+ }
+
+ free_devices ();
+
+ config.count = 0;
+ config.descriptors = NULL;
+ config.values = NULL;
+ sanei_configure_attach (XEROX_CONFIG_FILE, &config, list_conf_devices);
+
+ for (dev_count = 0, dev = devices_head; dev; dev = dev->next)
+ dev_count++;
+
+ devlist = malloc ((dev_count + 1) * sizeof (*devlist));
+ if (!devlist)
+ {
+ DBG (1, "%s: malloc: no memory\n", __FUNCTION__);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0, dev = devices_head; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = NULL;
+
+ if (device_list)
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ struct device *dev = h;
+
+ if (!dev)
+ return;
+
+ DBG (3, "%s: %p (%s)\n", __FUNCTION__, (void *)dev, dev->sane.name);
+ dev->io->dev_close(dev);
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ struct device *dev;
+
+ DBG (3, "%s: '%s'\n", __FUNCTION__, name);
+
+ if (!devlist)
+ sane_get_devices (NULL, SANE_TRUE);
+
+ if (!name || !*name) {
+ /* special case of empty name: open first available device */
+ for (dev = devices_head; dev; dev = dev->next) {
+ if (dev->dn != -1) {
+ if (sane_open (dev->sane.name, h) == SANE_STATUS_GOOD)
+ return SANE_STATUS_GOOD;
+ }
+ }
+ } else {
+ for (dev = devices_head; dev; dev = dev->next) {
+ if (strcmp(name, dev->sane.name) == 0) {
+ *h = dev;
+ return dev->io->dev_open(dev);
+ }
+ }
+ }
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * para)
+{
+ struct device *dev = h;
+
+ DBG (3, "%s: %p, %p\n", __FUNCTION__, h, (void *)para);
+ if (!para)
+ return SANE_STATUS_INVAL;
+
+ *para = dev->para;
+ return SANE_STATUS_GOOD;
+}
+
+/* check if image data is ready, and wait if not */
+/* 1: image is acquired, 0: error or non_blocking mode */
+static int dev_acquire(struct device *dev)
+{
+ if (!dev_cmd_wait(dev, CMD_READ))
+ return dev->state;
+
+ dev->state = SANE_STATUS_GOOD;
+ dev->vertical = dev->res[0x08] << 8 | dev->res[0x09];
+ dev->horizontal = dev->res[0x0a] << 8 | dev->res[0x0b];
+ dev->blocklen = dev->res[4] << 24 |
+ dev->res[5] << 16 |
+ dev->res[6] << 8 |
+ dev->res[7];
+ dev->final_block = (dev->res[3] == MSG_END_BLOCK)? 1 : 0;
+
+ dev->pixels_per_line = dev->horizontal;
+ dev->bytes_per_line = dev->horizontal;
+
+ if (dev->composition == MODE_RGB24)
+ dev->bytes_per_line *= 3;
+ else if (dev->composition == MODE_LINEART ||
+ dev->composition == MODE_HALFTONE)
+ dev->pixels_per_line *= 8;
+
+ DBG (4, "acquiring, size per band v: %d, h: %d, %sblock: %d, slack: %d\n",
+ dev->vertical, dev->horizontal, dev->final_block? "last " : "",
+ dev->blocklen, dev->blocklen - (dev->vertical * dev->bytes_per_line));
+
+ if (dev->bytes_per_line > DATASIZE) {
+ DBG (1, "%s: unsupported line size: %d bytes > %d\n",
+ __FUNCTION__, dev->bytes_per_line, DATASIZE);
+ return ret_cancel(dev, SANE_STATUS_NO_MEM);
+ }
+
+ dev->reading = 0; /* need to issue READ_IMAGE */
+
+ dev->dataindex = 0;
+ dev->datalen = 0;
+ dev->dataoff = 0;
+
+ return 1;
+}
+
+static int fill_slack(struct device *dev, SANE_Byte *buf, int maxlen)
+{
+ const int slack = dev->total_img_size - dev->total_out_size;
+ const int havelen = MIN(slack, maxlen);
+ int j;
+
+ if (havelen <= 0)
+ return 0;
+ for (j = 0; j < havelen; j++)
+ buf[j] = 255;
+ return havelen;
+}
+
+static int copy_plain_trim(struct device *dev, SANE_Byte *buf, int maxlen, int *olenp)
+{
+ int j;
+ const int linesize = dev->bytes_per_line;
+ int k = dev->dataindex;
+ *olenp = 0;
+ for (j = 0; j < dev->datalen && *olenp < maxlen; j++, k++) {
+ const int x = k % linesize;
+ const int y = k / linesize;
+ if (y >= dev->vertical)
+ break; /* slack */
+ if (x < dev->para.bytes_per_line &&
+ (y + dev->y_off) < dev->para.lines) {
+ *buf++ = dev->data[(dev->dataoff + j) & DATAMASK];
+ (*olenp)++;
+ }
+ }
+ dev->dataindex = k;
+ return j;
+}
+
+/* return: how much data could be freed from cyclic buffer */
+/* convert from RRGGBB to RGBRGB */
+static int copy_mix_bands_trim(struct device *dev, SANE_Byte *buf, int maxlen, int *olenp) {
+ int j;
+
+ const int linesize = dev->bytes_per_line; /* caching real line size */
+
+ /* line number of the head of input buffer,
+ * input buffer is always aligned to whole line */
+ const int y_off = dev->dataindex / linesize;
+
+ int k = dev->dataindex; /* caching current index of input buffer */
+
+ /* can only copy as much as full lines we have */
+ int havelen = dev->datalen / linesize * linesize - k % linesize;
+
+ const int bands = 3;
+ *olenp = 0;
+
+ /* while we have data && they can receive */
+ for (j = 0; j < havelen && *olenp < maxlen; j++, k++) {
+ const int band = (k % bands) * dev->horizontal;
+ const int x = k % linesize / bands;
+ const int y = k / linesize - y_off; /* y relative to buffer head */
+ const int y_rly = y + y_off + dev->y_off; /* global y */
+
+ if (x < dev->para.pixels_per_line &&
+ y_rly < dev->para.lines) {
+ *buf++ = dev->data[(dev->dataoff + band + x + y * linesize) & DATAMASK];
+ (*olenp)++;
+ }
+ }
+ dev->dataindex = k;
+
+ /* how much full lines are finished */
+ return (k / linesize - y_off) * linesize;
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * lenp)
+{
+ SANE_Status status;
+ struct device *dev = h;
+
+ DBG (3, "%s: %p, %p, %d, %p\n", __FUNCTION__, h, buf, maxlen, (void *)lenp);
+
+ if (lenp)
+ *lenp = 0;
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ if (!dev->scanning)
+ return SANE_STATUS_EOF;
+
+ /* if there is no data to read or output from buffer */
+ if (!dev->blocklen && dev->datalen <= PADDING_SIZE) {
+
+ /* and we don't need to acquire next block */
+ if (dev->final_block) {
+ int slack = dev->total_img_size - dev->total_out_size;
+
+ /* but we may need to fill slack */
+ if (buf && lenp && slack > 0) {
+ *lenp = fill_slack(dev, buf, maxlen);
+ dev->total_out_size += *lenp;
+ DBG (9, "<> slack: %d, filled: %d, maxlen %d\n",
+ slack, *lenp, maxlen);
+ return SANE_STATUS_GOOD;
+ } else if (slack < 0) {
+ /* this will never happen */
+ DBG(1, "image overflow %d bytes\n", dev->total_img_size - dev->total_out_size);
+ }
+
+ /* that's all */
+ dev_stop(dev);
+ return SANE_STATUS_EOF;
+ }
+
+ /* queue next image block */
+ if (!dev_acquire(dev))
+ return dev->state;
+ }
+
+ if (!dev->reading) {
+ if (cancelled(dev))
+ return dev->state;
+ DBG (5, "READ_IMAGE\n");
+ if (!dev_cmd(dev, CMD_READ_IMAGE))
+ return SANE_STATUS_IO_ERROR;
+ dev->reading++;
+ dev->ulines += dev->vertical;
+ dev->y_off = dev->ulines - dev->vertical;
+ dev->total_data_size += dev->blocklen;
+ dev->blocks++;
+ }
+
+ do {
+ size_t datalen;
+ int clrlen; /* cleared lines len */
+ int olen; /* output len */
+
+ /* read as much data into the buffer */
+ datalen = DATAROOM(dev) & USB_BLOCK_MASK;
+ while (datalen && dev->blocklen) {
+ SANE_Byte *rbuf = dev->data + DATATAIL(dev);
+
+ DBG (9, "<> request len: %lu, [%d, %d; %d]\n",
+ (u_long)datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
+ if ((status = dev->io->dev_request(dev, NULL, 0, rbuf, &datalen)) !=
+ SANE_STATUS_GOOD)
+ return status;
+ dev->datalen += datalen;
+ dev->blocklen -= datalen;
+ DBG (9, "<> got %lu, [%d, %d; %d]\n",
+ (u_long)datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
+ if (dev->blocklen < 0)
+ return ret_cancel(dev, SANE_STATUS_IO_ERROR);
+
+ datalen = DATAROOM(dev) & USB_BLOCK_MASK;
+ }
+
+ if (buf && lenp) { /* read mode */
+ /* copy will do minimal of valid data */
+ if (dev->para.format == SANE_FRAME_RGB && dev->line_order)
+ clrlen = copy_mix_bands_trim(dev, buf, maxlen, &olen);
+ else
+ clrlen = copy_plain_trim(dev, buf, maxlen, &olen);
+
+ dev->datalen -= clrlen;
+ dev->dataoff = (dev->dataoff + clrlen) & DATAMASK;
+ buf += olen;
+ maxlen -= olen;
+ *lenp += olen;
+ dev->total_out_size += olen;
+
+ DBG (9, "<> olen: %d, clrlen: %d, blocklen: %d/%d, maxlen %d (%d %d %d)\n",
+ olen, clrlen, dev->blocklen, dev->datalen, maxlen,
+ dev->dataindex / dev->bytes_per_line + dev->y_off,
+ dev->y_off, dev->para.lines);
+
+ /* slack beyond last line */
+ if (dev->dataindex / dev->bytes_per_line + dev->y_off >= dev->para.lines) {
+ dev->datalen = 0;
+ dev->dataoff = 0;
+ }
+
+ if (!clrlen || maxlen <= 0)
+ break;
+ } else { /* flush mode */
+ dev->datalen = 0;
+ dev->dataoff = 0;
+ }
+
+ } while (dev->blocklen);
+
+ if (lenp)
+ DBG (9, " ==> %d\n", *lenp);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ struct device *dev = h;
+
+ DBG (3, "%s: %p\n", __FUNCTION__, h);
+
+ dev->cancel = 0;
+ dev->scanning = 0;
+ dev->total_img_size = 0;
+ dev->total_out_size = 0;
+ dev->total_data_size = 0;
+ dev->blocks = 0;
+
+ if (!dev->reserved) {
+ if (!dev_cmd_wait(dev, CMD_RESERVE_UNIT))
+ return dev->state;
+ dev->reserved++;
+ }
+
+ if (!dev_set_window(dev) ||
+ (dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
+ return dev_stop(dev);
+
+ if (!dev_cmd_wait(dev, CMD_OBJECT_POSITION))
+ return dev_stop(dev);
+
+ if (!dev_cmd(dev, CMD_READ) ||
+ (dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
+ return dev_stop(dev);
+
+ dev->scanning = 1;
+ dev->final_block = 0;
+ dev->blocklen = 0;
+ dev->pixels_per_line = 0;
+ dev->bytes_per_line = 0;
+ dev->ulines = 0;
+
+ set_parameters(dev);
+
+ if (!dev->data && !(dev->data = malloc(DATASIZE)))
+ return ret_cancel(dev, SANE_STATUS_NO_MEM);
+
+ if (!dev_acquire(dev))
+ return dev->state;
+
+ /* make sure to have dev->para <= of real size */
+ if (dev->para.pixels_per_line > dev->pixels_per_line) {
+ dev->para.pixels_per_line = dev->pixels_per_line;
+ dev->para.bytes_per_line = dev->pixels_per_line;
+ }
+
+ if (dev->composition == MODE_RGB24)
+ dev->para.bytes_per_line = dev->para.pixels_per_line * 3;
+ else if (dev->composition == MODE_LINEART ||
+ dev->composition == MODE_HALFTONE) {
+ dev->para.bytes_per_line = (dev->para.pixels_per_line + 7) / 8;
+ dev->para.pixels_per_line = dev->para.bytes_per_line * 8;
+ } else {
+ dev->para.bytes_per_line = dev->para.pixels_per_line;
+ }
+
+ dev->total_img_size = dev->para.bytes_per_line * dev->para.lines;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ struct device *dev = h;
+
+ DBG (3, "%s: %p, %d\n", __FUNCTION__, h, non_blocking);
+
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+
+ dev->non_blocking = non_blocking;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp)
+{
+ DBG (3, "%s: %p, %p\n", __FUNCTION__, h, (void *)fdp);
+ /* supporting of this will require thread creation */
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void sane_cancel (SANE_Handle h)
+{
+ struct device *dev = h;
+
+ DBG (3, "%s: %p\n", __FUNCTION__, h);
+ dev->cancel = 1;
+}
+
+/* xerox_mfp.c */
diff --git a/backend/xerox_mfp.conf.in b/backend/xerox_mfp.conf.in
new file mode 100644
index 0000000..e0f78ea
--- /dev/null
+++ b/backend/xerox_mfp.conf.in
@@ -0,0 +1,212 @@
+#xerox_mfp.conf
+# all entries are sorted in USB IDs numerical order
+
+######################
+### Samsung Models ###
+######################
+
+#Samsung SCX-4x16 Series
+usb 0x04e8 0x3409
+
+#Samsung SCX-6x20 Series
+usb 0x04e8 0x340d
+
+#Samsung MFP-560 Series
+usb 0x04e8 0x340e
+
+#Samsung MFP-750 Series
+usb 0x04e8 0x340f
+
+#Samsung SCX-4x20 Series
+usb 0x04e8 0x3412
+
+#Samsung SCX-4100 Series
+usb 0x04e8 0x3413
+
+#Samsung SCX-4x21 Series
+usb 0x04e8 0x3419
+
+#Samsung SCX-5x30 Series
+usb 0x04e8 0x341a
+
+#Samsung SCX-4200 Series
+usb 0x04e8 0x341b
+
+#Samsung CLX-3160 Series
+usb 0x04e8 0x341c
+
+#Samsung SCX-6x22 Series
+usb 0x04e8 0x341d
+
+#Samsung SCX-4725 Series, Samsung SCX4725-FN
+usb 0x04e8 0x341f
+
+#Samsung SCX-6x45 Series
+usb 0x04e8 0x3420
+
+#Samsung CLX-8380 Series
+usb 0x04e8 0x3421
+
+#Samsung CLX-216x Series
+usb 0x04e8 0x3425
+
+#Samsung SCX-4500 Series
+usb 0x04e8 0x3426
+
+#Samsung CLX-6200 Series
+usb 0x04e8 0x3427
+
+#Samsung CLX-6240 Series
+usb 0x04e8 0x3428
+
+#Samsung SCX-6x55 Series
+usb 0x04e8 0x3429
+
+#Samsung CLX-3170 Series: Samsung CLX-3170fn & CLX-3175FW
+usb 0x04e8 0x342a
+
+#Samsung SCX-4500W Series
+usb 0x04e8 0x342b
+
+# Samsung SCX-4500W, network mode
+# tcp HOST_ADDR PORT
+# Uncomment and configure:
+#tcp scx4500 9400
+
+#Samsung SCX-4x24 Series, Samsung SCX-4824
+usb 0x04e8 0x342c
+
+#Samsung SCX-4x28 Series, Samsung SCX-4828FN
+usb 0x04e8 0x342d
+
+#Samsung SCX-4300 Series
+usb 0x04e8 0x342e
+
+#Samsung SCX-5835_5935 Series
+usb 0x04e8 0x342f
+
+#Samsung SCX-5635 Series
+usb 0x04e8 0x3430
+
+#Samsung SCX-4x26 Series
+usb 0x04e8 0x3432
+
+#Samsung SCX-4600 Series
+usb 0x04e8 0x3433
+
+#Samsung SCX-4623 Series
+usb 0x04e8 0x3434
+
+#Samsung MFP-65x Series
+usb 0x04e8 0x3435
+
+#Samsung SCX-6545 Series
+usb 0x04e8 0x3437
+
+#Samsung CLX-8385 Series
+usb 0x04e8 0x3439
+
+#Samsung CLX-6220 Series
+usb 0x04e8 0x343a
+
+#Samsung CLX-6250 Series
+usb 0x04e8 0x343b
+
+#Samsung SCX-4x25 Series, Samsung SCX-4825FN
+usb 0x04e8 0x343c
+
+#Samsung CLX-3180 Series: Samsung CLX 3185
+usb 0x04e8 0x343d
+
+#Samsung CLX-8540 Series
+usb 0x04e8 0x343f
+
+#Samsung SCX-4623FW Series
+usb 0x04e8 0x3440
+
+#Samsung SCX-3200 Series, Samsung SCX-3205W
+usb 0x04e8 0x3441
+
+#Samsung SCX-6545X Series
+usb 0x04e8 0x3442
+
+#Samsung SCX-6x55X Series
+usb 0x04e8 0x3443
+
+#Samsung CLX-8385X Series
+usb 0x04e8 0x3444
+
+#Samsung SCX-5835_5935X Series
+usb 0x04e8 0x3446
+
+#Samsung SCX-483x 5x3x Series, Samsung SCX-4833FD
+usb 0x04e8 0x344b
+
+#Samsung SCX-3400 Series
+usb 0x04e8 0x344f
+
+#Samsung SF-760 Series
+usb 0x04e8 0x3450
+
+#Samsung SCX-472x Series, Samsung SCX-4729FD
+usb 0x04e8 0x3453
+
+#Samsung CLX-6260 Series
+usb 0x04e8 0x3455
+
+# Samsung CLX-3300 Series
+usb 0x04e8 0x3456
+
+#Samsung SCX-470x Series
+usb 0x04e8 0x3457
+
+#Samsung CLX-4190 Series
+usb 0x04e8 0x345a
+
+#Samsung SCX-4650 4x21S Series
+usb 0x04e8 0x345b
+
+#Samsung M337x 387x 407x Series
+usb 0x04e8 0x3460
+
+#Samsung M267x 287x Series
+usb 0x04e8 0x3461
+
+#Samsung SCX-681x Series
+usb 0x04e8 0x3466
+
+#Samsung C460 Series
+usb 0x04e8 0x3468
+
+####################
+### Xerox Models ###
+####################
+
+#Xerox Phaser 6110MFP
+usb 0x0924 0x3d5d
+
+#Xerox Phaser 3200MFP
+usb 0x0924 0x3da4
+
+#Xerox WorkCentre 4118 Series
+usb 0x0924 0x420c
+
+#Xerox WorkCentre 3119 Series
+usb 0x0924 0x4265
+
+#Xerox WorkCentre 3210
+usb 0x0924 0x4293
+
+#Xerox WorkCentre 3220
+usb 0x0924 0x4294
+
+###################
+### Dell Models ###
+###################
+
+#Dell MFP Laser Printer 1815dn
+usb 0x413c 0x5124
+
+#Dell 1235cn (clone of Samsung CLX-3175)
+usb 0x413c 0x5310
+
diff --git a/backend/xerox_mfp.h b/backend/xerox_mfp.h
new file mode 100644
index 0000000..500dd26
--- /dev/null
+++ b/backend/xerox_mfp.h
@@ -0,0 +1,216 @@
+/*
+ * SANE backend for Xerox Phaser 3200MFP
+ * Copyright 2008 ABC <abc@telekom.ru>
+ *
+ * Network scanners support
+ * Copyright 2010 Alexander Kuznetsov <acca(at)cpan.org>
+ *
+ * This program is licensed under GPL + SANE exception.
+ * More info at http://www.sane-project.org/license.html
+ */
+
+#ifndef xerox_mfp_h
+#define xerox_mfp_h
+
+#ifdef __GNUC__
+#define UNUSED(x) x __attribute__((unused))
+#else
+#define UNUSED(x) x
+#endif
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+#define UNCONST(ptr) ((void *)(long)(ptr))
+
+#define PNT_PER_MM (1200. / MM_PER_INCH)
+
+#define PADDING_SIZE 16
+
+#define SWAP_Word(x, y) { SANE_Word z = x; x = y; y = z; }
+
+enum options {
+ OPT_NUMOPTIONS,
+ OPT_GROUP_STD,
+ OPT_RESOLUTION, /* dpi*/
+ OPT_MODE, /* color */
+ OPT_THRESHOLD, /* brightness */
+ OPT_SOURCE, /* affects max window size */
+ OPT_GROUP_GEO,
+ OPT_SCAN_TL_X, /* for (OPT_SCAN_TL_X to OPT_SCAN_BR_Y) */
+ OPT_SCAN_TL_Y,
+ OPT_SCAN_BR_X,
+ OPT_SCAN_BR_Y,
+ NUM_OPTIONS
+};
+
+typedef struct transport transport;
+
+struct device {
+ struct device *next;
+ SANE_Device sane;
+ int dn; /* usb file descriptor */
+ SANE_Byte res[1024]; /* buffer for responses */
+ size_t reslen; /* response len */
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ SANE_Parameters para;
+ SANE_Bool non_blocking;
+ int scanning; /* scanning is started */
+ int cancel; /* cancel flag */
+ int state; /* current state */
+ int reserved; /* CMD_RESERVE_UNIT */
+ int reading; /* READ_IMAGE is sent */
+
+ SANE_Byte *data; /* postprocessing cyclic buffer 64k */
+ int datalen; /* how data in buffer */
+ int dataoff; /* offset of data */
+ int dataindex; /* sequental number */
+#define DATAMASK 0xffff /* mask of data buffer */
+#define DATASIZE (DATAMASK + 1) /* size of data buffer */
+ /* 64K will be enough to hold whole line of 2400 dpi of 23cm */
+#define DATATAIL(dev) ((dev->dataoff + dev->datalen) & DATAMASK)
+#define DATAROOM(dev) dataroom(dev)
+
+ /* data from CMD_INQUIRY: */
+ int resolutions; /* supported resolution bitmask */
+ int compositions; /* supported image compositions bitmask */
+ int max_len; /* effective max len for current doc source */
+ int max_win_width;
+ int max_win_len;
+ int max_len_adf;
+ int max_len_fb;
+ int line_order; /* if need post processing */
+ SANE_Word dpi_list[30]; /* allowed resolutions */
+ int doc_loaded;
+
+ SANE_Range win_x_range;
+ SANE_Range win_y_range;
+
+ /* CMD_SET_WINDOW parameters we set: */
+ int win_width; /* in 1200dpi points */
+ int win_len;
+ double win_off_x; /* in inches (byte.byte) */
+ double win_off_y;
+ int resolution; /* dpi indexed values */
+ int composition; /* MODE_ */
+ int doc_source; /* document source */
+ int threshold; /* brightness */
+
+ /* CMD_READ data. It is per block only, image could be in many blocks */
+ int blocklen; /* image data block len (padding incl.) */
+ int vertical; /* lines in block (padded) */
+ int horizontal; /* b/w: bytes, gray/color: pixels (padded) */
+ int final_block;
+ int pixels_per_line;
+ int bytes_per_line;
+ int ulines; /* up to this block including */
+ int y_off; /* up to this block excluding*/
+ int blocks;
+
+ /* stat */
+ int total_img_size; /* predicted image size */
+ int total_out_size; /* total we sent to user */
+ int total_data_size; /* total of what scanner sent us */
+
+ /* transport to use */
+ transport *io;
+};
+
+
+/* Transport abstract layer */
+struct transport {
+ char* ttype;
+
+ int (*dev_request) (struct device *dev,
+ SANE_Byte *cmd, size_t cmdlen,
+ SANE_Byte *resp, size_t *resplen);
+ SANE_Status (*dev_open) (struct device *dev);
+ void (*dev_close) (struct device *dev);
+ SANE_Status (*configure_device) (const char *devname, SANE_Status (*cb)(SANE_String_Const devname));
+};
+
+/* USB transport */
+int usb_dev_request (struct device *dev, SANE_Byte *cmd, size_t cmdlen, SANE_Byte *resp, size_t *resplen);
+SANE_Status usb_dev_open (struct device *dev);
+void usb_dev_close (struct device *dev);
+SANE_Status usb_configure_device (const char *devname, SANE_Status (*cb)(SANE_String_Const devname));
+
+/* TCP unicast */
+int tcp_dev_request (struct device *dev, SANE_Byte *cmd, size_t cmdlen, SANE_Byte *resp, size_t *resplen);
+SANE_Status tcp_dev_open (struct device *dev);
+void tcp_dev_close (struct device *dev);
+SANE_Status tcp_configure_device (const char *devname, SANE_Status (*cb)(SANE_String_Const devname));
+
+/* device wants transfer buffer to be multiple of 512 */
+#define USB_BLOCK_SIZE 512
+#define USB_BLOCK_MASK ~(USB_BLOCK_SIZE - 1)
+
+static inline int dataroom(struct device *dev) {
+ int tail = DATATAIL(dev);
+ if (tail < dev->dataoff)
+ return dev->dataoff - tail;
+ else if (dev->datalen == DATASIZE) {
+ return 0;
+ } else
+ return DATASIZE - tail;
+}
+
+/* Functions from original xerox_mfp.c, used in -usb.c and -tcp.c */
+SANE_Status ret_cancel(struct device *dev, SANE_Status ret);
+
+/* a la SCSI commands. */ /* request len, response len, exception */
+#define CMD_ABORT 0x06 /* 4, 32 */
+#define CMD_INQUIRY 0x12 /* 4, 70 */
+#define CMD_RESERVE_UNIT 0x16 /* 4, 32 */
+#define CMD_RELEASE_UNIT 0x17 /* 4, 32 */
+#define CMD_SET_WINDOW 0x24 /* 25, 32, specified req len is 22 */
+#define CMD_READ 0x28 /* 4, 32 */
+#define CMD_READ_IMAGE 0x29 /* 4, var + padding[16] */
+#define CMD_OBJECT_POSITION 0x31 /* 4, 32 */
+
+/* Packet Headers */
+#define REQ_CODE_A 0x1b
+#define REQ_CODE_B 0xa8
+#define RES_CODE 0xa8
+
+/* Status Codes, going into dev->state */
+#define STATUS_GOOD 0x00
+#define STATUS_CHECK 0x02 /* MSG_SCANNER_STATE */
+#define STATUS_CANCEL 0x04
+#define STATUS_BUSY 0x08
+
+/* Message Code */
+#define MSG_NO_MESSAGE 0x00
+#define MSG_PRODUCT_INFO 0x10 /* CMD_INQUIRY */
+#define MSG_SCANNER_STATE 0x20 /* CMD_RESERVE_UNIT, and
+ CMD_READ, CMD_SET_WINDOW, CMD_OBJECT_POSITION */
+#define MSG_SCANNING_PARAM 0x30 /* CMD_SET_WINDOW */
+#define MSG_PREVIEW_PARAM 0x31 /* CMD_SET_WINDOW */
+#define MSG_LINK_BLOCK 0x80 /* CMD_READ */
+#define MSG_END_BLOCK 0x81 /* CMD_READ */
+
+/* Scanner State Bits (if MSG_SCANNER_STATE if STATUS_CHECK) */
+#define STATE_NO_ERROR 0x001
+#define STATE_COMMAND_ERROR 0x002
+#define STATE_UNSUPPORTED 0x004
+#define STATE_RESET 0x008
+#define STATE_NO_DOCUMENT 0x010
+#define STATE_DOCUMENT_JAM 0x020
+#define STATE_COVER_OPEN 0x040
+#define STATE_WARMING 0x080
+#define STATE_LOCKING 0x100
+#define STATE_INVALID_AREA 0x200
+#define STATE_RESOURCE_BUSY 0x400
+
+/* Image Composition */
+#define MODE_LINEART 0x00
+#define MODE_HALFTONE 0x01
+#define MODE_GRAY8 0x03
+#define MODE_RGB24 0x05
+
+/* Document Source */
+#define DOC_ADF 0x20
+#define DOC_FLATBED 0x40
+#define DOC_AUTO 0x80
+
+#endif /* xerox_mfp_h */